1#![allow(unused_variables)]
2
3use crate::atlas::AtlasData;
4use crate::enums::*;
5use crate::mipblock::MipblockData;
6use crate::pack::TexturePackerError;
7use crate::WoaVersion;
8use binrw::helpers::until_eof;
9use binrw::{binread, binrw, BinRead, BinResult, BinWrite, BinWriterExt, Endian};
10use serde::{Deserialize, Serialize};
11use std::fs::File;
12use std::io::{BufReader, BufWriter, Cursor, Seek, Write};
13use std::path::Path;
14use std::{fs, io};
15
16const MAX_MIP_LEVELS: usize = 0xE;
18
19#[derive(Debug, thiserror::Error)]
20pub enum TextureMapError {
21 #[error("Io error")]
22 IoError(#[from] io::Error),
23
24 #[error("Parsing error")]
25 ParsingError(#[from] binrw::Error),
26
27 #[error("Failed on {0}")]
28 UnknownError(String),
29}
30
31pub(crate) struct DynamicTextureMapArgs {
33 pub(crate) data_size: u32,
34
35 pub(crate) atlas_data_size: u32,
36
37 pub(crate) text_scale: u8,
38 pub(crate) text_mip_levels: u8,
39}
40
41pub(crate) trait TextureMapHeaderImpl {
43 fn text_scale(&self) -> usize;
45 fn size() -> usize;
47 fn text_data_size(&self) -> usize;
49 fn has_atlas(&self) -> bool;
51 fn texd_mip_levels(&self) -> usize;
53}
54
55#[binrw]
57#[derive(Serialize, Deserialize, Clone, Debug)]
58#[br(assert(
59 num_textures == 1 && num_textures != 6, "Looks like you tried to export a cubemap texture, those are not supported yet"
60))]
61#[bw(import(args: DynamicTextureMapArgs))]
62pub(crate) struct TextureMapHeaderV1 {
63 #[br(temp)]
64 #[bw(calc(1))]
65 num_textures: u16,
66
67 pub(crate) type_: TextureType,
68
69 pub(crate) texd_identifier: u32,
70
71 #[br(temp)]
72 #[bw(calc(args.data_size - 8))]
73 data_size: u32,
74 pub(crate) flags: TextureFlagsInner,
75 pub(crate) width: u16,
76 pub(crate) height: u16,
77 pub(crate) format: RenderFormat,
78 pub(crate) num_mip_levels: u8,
79 pub(crate) default_mip_level: u8,
80 pub(crate) interpret_as: InterpretAs,
81 pub(crate) dimensions: Dimensions,
82 #[br(temp)]
83 #[bw(calc(0))]
84 mips_interpolation_deprecated: u16,
85
86 pub(crate) mip_sizes: [u32; MAX_MIP_LEVELS],
87 #[br(temp)]
88 #[bw(calc(args.atlas_data_size))]
89 atlas_data_size: u32,
90 #[br(temp)]
91 #[bw(calc(0x54))]
92 atlas_data_offset: u32,
93
94 #[br(calc = atlas_data_size > 0)]
96 #[bw(ignore)]
97 pub(crate) has_atlas: bool,
98}
99
100impl TextureMapHeaderImpl for TextureMapHeaderV1 {
101 fn text_scale(&self) -> usize {
102 let texd_mips = self.num_mip_levels as usize;
103
104 if texd_mips == 1 {
105 return 0;
106 }
107
108 if self.interpret_as == InterpretAs::Billboard {
109 return 0;
110 }
111
112 let area = self.width as usize * self.height as usize;
113 ((area as f32).log2() * 0.5 - 6.5).floor() as usize
114 }
115
116 fn size() -> usize {
117 92
118 }
119
120 fn text_data_size(&self) -> usize {
121 let text_mip_levels = self.num_mip_levels as usize - self.text_scale();
122 let blocks_to_skip = self.num_mip_levels as usize - text_mip_levels;
123 let last_mip_size = self.mip_sizes[(self.num_mip_levels - 1) as usize] as usize;
124 if blocks_to_skip == 0 {
125 return last_mip_size;
126 }
127 let texd_mip_size = self.mip_sizes.get(blocks_to_skip - 1).unwrap_or(&0);
128 last_mip_size - *texd_mip_size as usize
129 }
130
131 fn has_atlas(&self) -> bool {
132 self.has_atlas
133 }
134
135 fn texd_mip_levels(&self) -> usize {
136 self.num_mip_levels as usize
137 }
138}
139
140#[binrw]
141#[derive(Serialize, Deserialize, Clone, Debug)]
142#[br(assert(mip_sizes == compressed_mip_sizes))]
143#[br(assert(num_textures == 1))]
144#[bw(import(args: DynamicTextureMapArgs))]
145pub(crate) struct TextureMapHeaderV2 {
146 #[br(temp)]
147 #[bw(calc(1))]
148 num_textures: u16,
149
150 pub(crate) type_: TextureType,
151
152 #[br(temp)]
153 #[bw(calc(args.data_size))]
154 data_size: u32,
155 pub(crate) flags: TextureFlagsInner,
156 pub(crate) width: u16,
157 pub(crate) height: u16,
158 pub(crate) format: RenderFormat,
159 pub(crate) num_mip_levels: u8,
160 pub(crate) default_mip_level: u8,
161 pub(crate) texd_identifier: u32,
162 pub(crate) mip_sizes: [u32; MAX_MIP_LEVELS],
163 pub(crate) compressed_mip_sizes: [u32; MAX_MIP_LEVELS],
164 #[br(temp)]
165 #[bw(calc(args.atlas_data_size))]
166 atlas_data_size: u32,
167 #[br(temp)]
168 #[bw(calc(0x90))]
169 atlas_data_offset: u32,
170
171 #[br(calc = atlas_data_size > 0)]
173 #[bw(ignore)]
174 pub(crate) has_atlas: bool,
175}
176
177impl TextureMapHeaderImpl for TextureMapHeaderV2 {
178 fn text_scale(&self) -> usize {
179 let texd_mips = self.num_mip_levels as usize;
180 if texd_mips == 1 {
181 return 0;
182 }
183
184 if self.type_ == TextureType::Billboard {
185 return 0;
186 }
187
188 if self.format == RenderFormat::BC1 && (self.width as usize * self.height as usize) == 16 {
189 return 1;
190 }
191
192 if self.texd_identifier != 16384 {
193 return 0;
194 }
195
196 let area = self.width as usize * self.height as usize;
197 ((area as f32).log2() * 0.5 - 6.5).floor() as usize
198 }
199
200 fn size() -> usize {
201 144
202 }
203
204 fn text_data_size(&self) -> usize {
205 let text_mip_levels = self.num_mip_levels as usize - self.text_scale();
206 let blocks_to_skip = self.num_mip_levels as usize - text_mip_levels;
207 let last_mip_size = self.compressed_mip_sizes[(self.num_mip_levels - 1) as usize] as usize;
208 if blocks_to_skip == 0 {
209 return last_mip_size;
210 }
211 let texd_mip_size = self
212 .compressed_mip_sizes
213 .get(blocks_to_skip - 1)
214 .unwrap_or(&0);
215 last_mip_size - *texd_mip_size as usize
216 }
217
218 fn has_atlas(&self) -> bool {
219 self.has_atlas
220 }
221
222 fn texd_mip_levels(&self) -> usize {
223 self.num_mip_levels as usize
224 }
225}
226
227#[binrw]
228#[derive(Serialize, Deserialize, Clone, Debug)]
229#[br(assert(text_scaling_width == num_mip_levels - text_mip_levels))]
230#[br(assert(text_scaling_height == num_mip_levels - text_mip_levels))]
231#[br(assert(num_textures == 1))]
232#[bw(import(args: DynamicTextureMapArgs))]
233pub(crate) struct TextureMapHeaderV3 {
234 #[br(temp)]
235 #[bw(calc(1))]
236 num_textures: u16,
237
238 pub(crate) type_: TextureType,
239
240 #[br(temp)]
241 #[bw(calc(args.data_size))]
242 data_size: u32,
243 pub(crate) flags: TextureFlagsInner,
244 pub(crate) width: u16,
245 pub(crate) height: u16,
246 pub(crate) format: RenderFormat,
247 pub(crate) num_mip_levels: u8,
248 pub(crate) default_mip_level: u8,
249 pub(crate) interpret_as: InterpretAs,
250 pub(crate) dimensions: Dimensions,
251
252 #[br(temp)]
253 #[bw(calc(0))]
254 mips_interpolation_deprecated: u16,
255 pub(crate) mip_sizes: [u32; MAX_MIP_LEVELS],
256 pub(crate) compressed_mip_sizes: [u32; MAX_MIP_LEVELS],
257 #[br(temp)]
258 #[bw(calc(args.atlas_data_size))]
259 atlas_data_size: u32,
260 #[br(temp)]
261 #[bw(calc(0x98))]
262 atlas_data_offset: u32,
263 #[br(temp)]
264 #[bw(calc(0xFF))]
265 text_scaling_data1: u8,
266 #[br(temp)]
267 #[bw(calc(args.text_scale))]
268 text_scaling_width: u8,
269 #[br(temp)]
270 #[bw(calc(args.text_scale))]
271 text_scaling_height: u8,
272
273 #[br(temp)]
274 #[bw(calc(args.text_mip_levels))]
275 #[brw(pad_after = 0x4)]
276 text_mip_levels: u8,
277
278 #[br(calc = atlas_data_size > 0)]
280 #[bw(ignore)]
281 pub(crate) has_atlas: bool,
282}
283
284impl TextureMapHeaderImpl for TextureMapHeaderV3 {
285 fn text_scale(&self) -> usize {
286 let texd_mips = self.num_mip_levels as usize;
287 if texd_mips == 1 {
288 return 0;
289 }
290
291 if self.type_ == TextureType::Billboard || self.interpret_as == InterpretAs::Volume {
292 return 0;
293 }
294
295 if self.type_ == TextureType::UNKNOWN512 {
296 return 0;
297 }
298
299 if self.format == RenderFormat::BC1 && (self.width as usize * self.height as usize) == 16 {
300 return 1;
301 }
302
303 let area = self.width as usize * self.height as usize;
304 ((area as f32).log2() * 0.5 - 6.5).floor() as usize
305 }
306
307 fn size() -> usize {
308 152
309 }
310
311 fn text_data_size(&self) -> usize {
312 let text_mip_levels = self.num_mip_levels as usize - self.text_scale();
313 let blocks_to_skip = self.num_mip_levels as usize - text_mip_levels;
314 let last_mip_size = self.compressed_mip_sizes[(self.num_mip_levels - 1) as usize] as usize;
315 if blocks_to_skip == 0 {
316 return last_mip_size;
317 }
318 let texd_mip_size = self
319 .compressed_mip_sizes
320 .get(blocks_to_skip - 1)
321 .unwrap_or(&0);
322 last_mip_size - *texd_mip_size as usize
323 }
324
325 fn has_atlas(&self) -> bool {
326 self.has_atlas
327 }
328
329 fn texd_mip_levels(&self) -> usize {
330 self.num_mip_levels as usize
331 }
332}
333
334#[binrw]
335#[derive(Serialize, Deserialize, Clone, Debug)]
336#[br(import(woa_version: WoaVersion))]
337pub struct TextureMap {
338 #[br(args(woa_version))]
339 pub(crate) inner: TextureMapVersion,
340}
341
342#[binrw]
343#[derive(Serialize, Deserialize, Clone, Debug)]
344#[br(import(woa_version: WoaVersion))]
345pub(crate) enum TextureMapVersion {
346 #[br(pre_assert(woa_version == WoaVersion::HM2016))]
347 V1(TextureMapInner<TextureMapHeaderV1>),
348
349 #[br(pre_assert(woa_version == WoaVersion::HM2))]
350 V2(TextureMapInner<TextureMapHeaderV2>),
351
352 #[br(pre_assert(woa_version == WoaVersion::HM3))]
353 V3(TextureMapInner<TextureMapHeaderV3>),
354}
355
356impl From<TextureMapInner<TextureMapHeaderV1>> for TextureMap {
357 fn from(inner: TextureMapInner<TextureMapHeaderV1>) -> Self {
358 Self {
359 inner: TextureMapVersion::V1(inner),
360 }
361 }
362}
363
364impl From<TextureMapInner<TextureMapHeaderV2>> for TextureMap {
365 fn from(inner: TextureMapInner<TextureMapHeaderV2>) -> Self {
366 Self {
367 inner: TextureMapVersion::V2(inner),
368 }
369 }
370}
371
372impl From<TextureMapInner<TextureMapHeaderV3>> for TextureMap {
373 fn from(inner: TextureMapInner<TextureMapHeaderV3>) -> Self {
374 Self {
375 inner: TextureMapVersion::V3(inner),
376 }
377 }
378}
379
380#[derive(Serialize, Deserialize, Clone, Debug)]
382pub enum TextureData {
383 Tex(Vec<u8>),
385 Mipblock1(MipblockData),
387}
388
389impl BinWrite for TextureData {
390 type Args<'a> = (usize,);
391
392 fn write_options<W: Write + Seek>(
393 &self,
394 writer: &mut W,
395 endian: Endian,
396 args: Self::Args<'_>,
397 ) -> BinResult<()> {
398 match self {
399 TextureData::Tex(data) => writer.write_type(data, endian),
400 TextureData::Mipblock1(mipblock) => {
401 let data = &mipblock.data;
402 let cut_data = &data
403 .clone()
404 .into_iter()
405 .skip(data.len() - args.0)
406 .collect::<Vec<_>>();
407 writer.write_type(cut_data, endian)
408 }
409 }
410 }
411}
412
413impl TextureData {
414 fn size(&self) -> usize {
415 match self {
416 TextureData::Tex(d) => d.len(),
417 TextureData::Mipblock1(d) => d.data.len(),
418 }
419 }
420}
421
422#[binread]
423#[derive(Serialize, Deserialize, Clone, Debug)]
424pub(crate) struct TextureMapInner<A>
425where
426 A: for<'a> BinRead<Args<'a> = ()>,
427 A: TextureMapHeaderImpl,
428{
429 pub header: A,
430
431 #[br(if (header.has_atlas()))]
433 pub atlas_data: Option<AtlasData>,
434
435 #[br(parse_with = until_eof, map = TextureData::Tex)]
436 #[serde(skip_serializing)]
437 pub data: TextureData,
438}
439
440impl<A> BinWrite for TextureMapInner<A>
441where
442 A: for<'a> BinWrite<Args<'a> = (DynamicTextureMapArgs,)>
443 + Clone
444 + for<'a> binrw::BinRead<Args<'a> = ()>,
445 A: TextureMapHeaderImpl,
446{
447 type Args<'a> = ();
448
449 fn write_options<W: Write + Seek>(
450 &self,
451 writer: &mut W,
452 endian: Endian,
453 _: Self::Args<'_>,
454 ) -> BinResult<()> {
455 let atlas_size = self.atlas_data_size();
456 let total_size = self.data.size() + A::size() + atlas_size;
457
458 let args = DynamicTextureMapArgs {
459 data_size: total_size as u32,
460 atlas_data_size: atlas_size as u32,
461 text_scale: self.header.text_scale() as u8,
462 text_mip_levels: self.header.texd_mip_levels() as u8 - self.header.text_scale() as u8,
463 };
464 self.header.write_options(writer, endian, (args,))?;
465
466 if let Some(atlas_data) = &self.atlas_data {
468 atlas_data.write_options(writer, endian, ())?;
469 }
470
471 let text_data_size = self.header.text_data_size();
473 self.data.write_options(writer, endian, (text_data_size,))?;
474
475 Ok(())
476 }
477}
478
479impl<A> TextureMapInner<A>
480where
481 A: for<'a> BinRead<Args<'a> = ()>,
482 A: Clone,
483 A: for<'a> binrw::BinWrite<Args<'a> = (DynamicTextureMapArgs,)>,
484 A: TextureMapHeaderImpl,
485{
486 pub fn data(&self) -> &Vec<u8> {
487 match &self.data {
488 TextureData::Tex(d) => d,
489 TextureData::Mipblock1(d) => &d.data,
490 }
491 }
492
493 pub fn atlas_data(&self) -> &Option<AtlasData> {
494 &self.atlas_data
495 }
496
497 fn atlas_data_size(&self) -> usize {
498 self.atlas_data
499 .as_ref()
500 .map(|atlas| atlas.size())
501 .unwrap_or(0)
502 }
503
504 pub fn has_mipblock_data(&self) -> bool {
505 match &self.data {
506 TextureData::Tex(_) => false,
507 TextureData::Mipblock1(_) => true,
508 }
509 }
510}
511
512#[derive(Debug, Serialize, Deserialize)]
513pub struct MipLevel {
514 pub format: RenderFormat,
515 pub width: usize,
516 pub height: usize,
517 pub data: Vec<u8>,
518}
519
520impl TextureMap {
521 pub fn default_mip_level(&self) -> u8 {
522 match &self.inner {
523 TextureMapVersion::V1(tex) => tex.header.default_mip_level,
524 TextureMapVersion::V2(tex) => tex.header.default_mip_level,
525 TextureMapVersion::V3(tex) => tex.header.default_mip_level,
526 }
527 }
528
529 pub fn version(&self) -> WoaVersion {
530 match &self.inner {
531 TextureMapVersion::V1(_) => WoaVersion::HM2016,
532 TextureMapVersion::V2(_) => WoaVersion::HM2,
533 TextureMapVersion::V3(_) => WoaVersion::HM3,
534 }
535 }
536
537 pub(crate) fn data(&self) -> &Vec<u8> {
538 match &self.inner {
539 TextureMapVersion::V1(t) => t.data(),
540 TextureMapVersion::V2(t) => t.data(),
541 TextureMapVersion::V3(t) => t.data(),
542 }
543 }
544
545 pub fn atlas(&self) -> &Option<AtlasData> {
546 match &self.inner {
547 TextureMapVersion::V1(t) => t.atlas_data(),
548 TextureMapVersion::V2(t) => t.atlas_data(),
549 TextureMapVersion::V3(t) => t.atlas_data(),
550 }
551 }
552
553 fn set_data(&mut self, data: TextureData) {
554 match &mut self.inner {
555 TextureMapVersion::V1(t) => t.data = data,
556 TextureMapVersion::V2(t) => t.data = data,
557 TextureMapVersion::V3(t) => t.data = data,
558 }
559 }
560
561 fn text_mip_levels(&self) -> usize {
562 self.texd_mip_levels() - self.text_scale()
563 }
564
565 fn texd_mip_levels(&self) -> usize {
566 match &self.inner {
567 TextureMapVersion::V1(inner) => inner.header.num_mip_levels as usize,
568 TextureMapVersion::V2(inner) => inner.header.num_mip_levels as usize,
569 TextureMapVersion::V3(inner) => inner.header.num_mip_levels as usize,
570 }
571 }
572
573 pub fn num_mip_levels(&self) -> usize {
574 if self.has_mipblock1() {
575 self.texd_mip_levels()
576 } else {
577 self.text_mip_levels()
578 }
579 }
580
581 fn text_scale(&self) -> usize {
582 match &self.inner {
583 TextureMapVersion::V1(tex) => tex.header.text_scale(),
584 TextureMapVersion::V2(tex) => tex.header.text_scale(),
585 TextureMapVersion::V3(tex) => tex.header.text_scale(),
586 }
587 }
588
589 fn mip_sizes(&self) -> Vec<u32> {
590 match &self.inner {
591 TextureMapVersion::V1(tex) => tex
592 .header
593 .mip_sizes
594 .iter()
595 .copied()
596 .filter(|mip| *mip != 0)
597 .collect(),
598 TextureMapVersion::V2(tex) => tex
599 .header
600 .mip_sizes
601 .iter()
602 .copied()
603 .filter(|mip| *mip != 0)
604 .collect(),
605 TextureMapVersion::V3(tex) => tex
606 .header
607 .mip_sizes
608 .iter()
609 .copied()
610 .filter(|mip| *mip != 0)
611 .collect(),
612 }
613 }
614
615 fn compressed_mip_sizes(&self) -> Vec<u32> {
616 match &self.inner {
617 TextureMapVersion::V1(tex) => tex
618 .header
619 .mip_sizes
620 .iter()
621 .copied()
622 .filter(|mip| *mip != 0)
623 .collect(),
624 TextureMapVersion::V2(tex) => tex
625 .header
626 .compressed_mip_sizes
627 .iter()
628 .copied()
629 .filter(|mip| *mip != 0)
630 .collect(),
631 TextureMapVersion::V3(tex) => tex
632 .header
633 .compressed_mip_sizes
634 .iter()
635 .copied()
636 .filter(|mip| *mip != 0)
637 .collect(),
638 }
639 }
640
641 pub fn video_memory_requirement(&self) -> usize {
642 match self.version() {
643 WoaVersion::HM2016 | WoaVersion::HM2 => {
644 self.mip_sizes()
645 .get(self.text_scale())
646 .cloned()
647 .unwrap_or(0) as usize }
649 WoaVersion::HM3 => {
650 if self.has_mipblock1() {
651 (self.mip_sizes().first().cloned().unwrap_or(0)
653 + self.mip_sizes().get(1).cloned().unwrap_or(0))
654 as usize } else {
656 0
657 }
658 }
659 }
660 }
661
662 pub fn mipblock1(&self) -> Option<MipblockData> {
663 self.has_mipblock1()
664 .then(|| {
665 self.texd_header().ok().map(|header| MipblockData {
666 video_memory_requirement: self.mip_sizes().first().copied().unwrap_or(0x0)
667 as usize,
668 header,
669 data: self.data().clone(),
670 })
671 })
672 .flatten()
673 }
674
675 fn texd_size(&self) -> (usize, usize) {
676 match &self.inner {
677 TextureMapVersion::V1(tex) => (tex.header.width as usize, tex.header.height as usize),
678 TextureMapVersion::V2(tex) => (tex.header.width as usize, tex.header.height as usize),
679 TextureMapVersion::V3(tex) => (tex.header.width as usize, tex.header.height as usize),
680 }
681 }
682
683 fn text_size(&self) -> (usize, usize) {
684 let (width, height) = self.texd_size();
685 let scale_factor = 1 << self.text_scale();
686 (width / scale_factor, height / scale_factor)
687 }
688
689 pub fn width(&self) -> usize {
690 if self.has_mipblock1() {
691 self.texd_size().0
692 } else {
693 self.text_size().0
694 }
695 }
696
697 pub fn height(&self) -> usize {
698 if self.has_mipblock1() {
699 self.texd_size().1
700 } else {
701 self.text_size().1
702 }
703 }
704
705 pub fn format(&self) -> RenderFormat {
706 match &self.inner {
707 TextureMapVersion::V1(tex) => tex.header.format,
708 TextureMapVersion::V2(tex) => tex.header.format,
709 TextureMapVersion::V3(tex) => tex.header.format,
710 }
711 }
712
713 pub fn flags(&self) -> TextureFlags {
714 match &self.inner {
715 TextureMapVersion::V1(tex) => TextureFlags {
716 inner: tex.header.flags,
717 },
718 TextureMapVersion::V2(tex) => TextureFlags {
719 inner: tex.header.flags,
720 },
721 TextureMapVersion::V3(tex) => TextureFlags {
722 inner: tex.header.flags,
723 },
724 }
725 }
726
727 pub fn texture_type(&self) -> TextureType {
728 match &self.inner {
729 TextureMapVersion::V1(tex) => tex.header.type_,
730 TextureMapVersion::V2(tex) => tex.header.type_,
731 TextureMapVersion::V3(tex) => tex.header.type_,
732 }
733 }
734
735 pub fn interpret_as(&self) -> Option<InterpretAs> {
736 match &self.inner {
737 TextureMapVersion::V1(tex) => Some(tex.header.interpret_as),
738 TextureMapVersion::V2(_) => None,
739 TextureMapVersion::V3(tex) => Some(tex.header.interpret_as),
740 }
741 }
742
743 pub fn dimensions(&self) -> Dimensions {
744 match &self.inner {
745 TextureMapVersion::V1(tex) => tex.header.dimensions,
746 TextureMapVersion::V2(_) => Dimensions::_2D,
747 TextureMapVersion::V3(tex) => tex.header.dimensions,
748 }
749 }
750
751 pub fn has_mipblock1(&self) -> bool {
752 match &self.inner {
753 TextureMapVersion::V1(t) => t.has_mipblock_data(),
754 TextureMapVersion::V2(t) => t.has_mipblock_data(),
755 TextureMapVersion::V3(t) => t.has_mipblock_data(),
756 }
757 }
758
759 pub fn from_file<P: AsRef<Path>>(
760 path: P,
761 woa_version: WoaVersion,
762 ) -> Result<Self, TextureMapError> {
763 let file = File::open(path).map_err(TextureMapError::IoError)?;
764 let mut reader = BufReader::new(file);
765 TextureMap::read_le_args(&mut reader, (woa_version,)).map_err(TextureMapError::ParsingError)
766 }
767
768 pub fn from_memory(data: &[u8], woa_version: WoaVersion) -> Result<Self, TextureMapError> {
769 let mut reader = Cursor::new(data);
770 TextureMap::read_le_args(&mut reader, (woa_version,)).map_err(TextureMapError::ParsingError)
771 }
772
773 pub fn default_mipmap(&self) -> Result<MipLevel, TextureMapError> {
774 self.mipmap(self.default_mip_level() as usize)
775 }
776
777 pub fn mipmaps(&self) -> impl Iterator<Item = Result<MipLevel, TextureMapError>> + '_ {
778 (0..self.num_mip_levels()).map(move |level| self.mipmap(level))
779 }
780
781 pub fn mipmap(&self, level: usize) -> Result<MipLevel, TextureMapError> {
782 let removed_mip_count = self.texd_mip_levels() - self.text_mip_levels();
783
784 let mut mips_sizes: Vec<u32> = self.mip_sizes();
785 let mut block_sizes: Vec<u32> = self.compressed_mip_sizes();
786
787 if !self.has_mipblock1() {
788 let removed_mip = mips_sizes
789 .drain(0..removed_mip_count)
790 .collect::<Vec<u32>>()
791 .pop()
792 .unwrap_or(0);
793 mips_sizes.iter_mut().for_each(|x| {
794 if *x > 0 {
795 *x -= removed_mip
796 }
797 });
798
799 let removed_block = block_sizes
800 .drain(0..removed_mip_count)
801 .collect::<Vec<u32>>()
802 .pop()
803 .unwrap_or(0);
804 block_sizes.iter_mut().for_each(|x| {
805 if *x > 0 {
806 *x -= removed_block
807 }
808 });
809 }
810
811 if level > self.texd_mip_levels() {
812 return Err(TextureMapError::UnknownError(
813 "mip level is out of bounds".parse().unwrap(),
814 ));
815 }
816
817 let mip_start = if level > 0 { mips_sizes[level - 1] } else { 0 };
818 let mip_size = mips_sizes.get(level).ok_or(TextureMapError::UnknownError(
819 "mip level is out of bounds".parse().unwrap(),
820 ))? - mip_start;
821
822 let block_start = if level > 0 { block_sizes[level - 1] } else { 0 };
823 let block_size = block_sizes.get(level).ok_or(TextureMapError::UnknownError(
824 "mip level is out of bounds".parse().unwrap(),
825 ))? - block_start;
826
827 let is_compressed = mip_size != block_size;
828 let block = self
829 .data()
830 .clone()
831 .into_iter()
832 .skip(block_start as usize)
833 .take(block_size as usize)
834 .collect::<Vec<u8>>();
835 let data = if is_compressed {
836 let mut dst = vec![0u8; mip_size as usize];
837 match lz4::block::decompress_to_buffer(
838 block.as_slice(),
839 Some(mip_size as i32),
840 &mut dst,
841 ) {
842 Ok(_) => {}
843 Err(e) => {
844 return Err(TextureMapError::UnknownError(format!(
845 "Failed to decompress texture data {e}"
846 )));
847 }
848 };
849 dst
850 } else {
851 block
852 };
853
854 Ok(MipLevel {
855 format: self.format(),
856 width: self.width() >> level,
857 height: self.height() >> level,
858 data,
859 })
860 }
861
862 pub fn has_atlas(&self) -> bool {
863 self.atlas().is_some()
864 }
865
866 pub fn set_mipblock1(&mut self, mipblock: MipblockData) {
867 self.set_data(TextureData::Mipblock1(mipblock))
868 }
869
870 fn texd_header(&self) -> Result<Vec<u8>, TextureMapError> {
871 let mut writer = Cursor::new(Vec::new());
872
873 let data = match &self.inner {
874 TextureMapVersion::V1(d) => &d.data,
875 TextureMapVersion::V2(d) => &d.data,
876 TextureMapVersion::V3(d) => &d.data,
877 };
878
879 let atlas_size = self.atlas().as_ref().map(|atlas| atlas.size()).unwrap_or(0);
880 let total_size = data.size()
881 + match &self.inner {
882 TextureMapVersion::V1(_) => TextureMapHeaderV1::size(),
883 TextureMapVersion::V2(_) => TextureMapHeaderV2::size(),
884 TextureMapVersion::V3(_) => TextureMapHeaderV3::size(),
885 }
886 + atlas_size;
887
888 let args = DynamicTextureMapArgs {
889 data_size: total_size as u32,
890 atlas_data_size: atlas_size as u32,
891
892 text_scale: 0,
894 text_mip_levels: 0,
895 };
896
897 match &self.inner {
898 TextureMapVersion::V1(tex) => {
899 tex.header
900 .write_options(&mut writer, Endian::Little, (args,))?
901 }
902 TextureMapVersion::V2(tex) => {
903 tex.header
904 .write_options(&mut writer, Endian::Little, (args,))?
905 }
906 TextureMapVersion::V3(tex) => {
907 tex.header
908 .write_options(&mut writer, Endian::Little, (args,))?
909 }
910 }
911
912 if let Some(atlas_data) = &self.atlas() {
914 atlas_data.write_options(&mut writer, Endian::Little, ())?;
915 }
916
917 Ok(writer.into_inner())
918 }
919
920 pub fn pack_to_vec(&self) -> Result<Vec<u8>, TexturePackerError> {
921 let mut writer = Cursor::new(Vec::new());
922 self.pack_internal(&mut writer)?;
923 Ok(writer.into_inner())
924 }
925
926 pub fn pack_to_file<P: AsRef<Path>>(&self, path: P) -> Result<(), TexturePackerError> {
927 let file = fs::File::create(path).map_err(TexturePackerError::IoError)?;
928 let mut writer = BufWriter::new(file);
929 self.pack_internal(&mut writer)?;
930 Ok(())
931 }
932
933 fn pack_internal<W: Write + Seek>(&self, writer: &mut W) -> Result<(), TexturePackerError> {
934 self.write_le_args(writer, ())
935 .map_err(TexturePackerError::SerializationError)?;
936 Ok(())
937 }
938}