wow_alchemy_data/
types.rs

1use custom_debug::Debug;
2use wow_alchemy_data_derive::{WowHeaderR, WowHeaderW};
3
4use crate::{
5    error::{Result, WowDataError},
6    v_read_chunk_items,
7};
8pub use std::io::{Read, Seek, SeekFrom, Write};
9
10mod wow_alchemy_data {
11    pub use crate::*;
12}
13
14pub type MagicStr = [u8; 4];
15
16pub trait WowHeaderR: Sized {
17    fn wow_read<R: Read + Seek>(reader: &mut R) -> Result<Self>;
18}
19
20pub trait WowReaderForHeader<T>
21where
22    Self: Read + Seek + Sized,
23    T: WowHeaderR,
24{
25    fn wow_read(&mut self) -> Result<T> {
26        T::wow_read(self)
27    }
28}
29impl<T, R> WowReaderForHeader<T> for R
30where
31    T: WowHeaderR,
32    R: Read + Seek,
33{
34}
35
36pub trait DataVersion: Copy + PartialEq + Eq + PartialOrd + Ord {}
37
38pub trait VWowHeaderR<V: DataVersion>: Sized {
39    fn wow_read<R: Read + Seek>(reader: &mut R, version: V) -> Result<Self>;
40}
41
42pub trait VWowReaderForHeader<V, T>
43where
44    Self: Read + Seek + Sized,
45    V: DataVersion,
46    T: VWowHeaderR<V>,
47{
48    fn wow_read_versioned(&mut self, version: V) -> Result<T> {
49        T::wow_read(self, version)
50    }
51}
52impl<V, T, R> VWowReaderForHeader<V, T> for R
53where
54    V: DataVersion,
55    T: VWowHeaderR<V>,
56    R: Read + Seek,
57{
58}
59
60pub trait WowHeaderW {
61    fn wow_write<W: Write>(&self, writer: &mut W) -> Result<()>;
62    fn wow_size(&self) -> usize;
63}
64
65pub trait WowWriterForHeader<T>
66where
67    Self: Write + Sized,
68    T: WowHeaderW,
69{
70    fn wow_write(&mut self, value: &T) -> Result<()> {
71        value.wow_write(self)?;
72        Ok(())
73    }
74}
75impl<T, W> WowWriterForHeader<T> for W
76where
77    T: WowHeaderW,
78    W: Write,
79{
80}
81
82pub trait WowHeaderConversible<V>
83where
84    Self: WowHeaderW + Sized,
85    V: DataVersion,
86{
87    fn wow_write_version<W: Write>(&self, writer: &mut W, version: V) -> Result<()> {
88        let converted = self.wow_convert(version)?;
89        converted.wow_write(writer)
90    }
91    fn wow_convert(&self, to_version: V) -> Result<Self>;
92}
93
94pub trait VWowWriterForHeader<V, T>
95where
96    Self: Write + Sized,
97    V: DataVersion,
98    T: WowHeaderConversible<V>,
99{
100    fn wow_write_versioned(&mut self, value: &T, version: V) -> Result<()> {
101        value.wow_write_version(self, version)?;
102        Ok(())
103    }
104}
105impl<V, T, W> VWowWriterForHeader<V, T> for W
106where
107    V: DataVersion,
108    T: WowHeaderConversible<V>,
109    W: Write,
110{
111}
112
113pub trait WowDataR<T: WowHeaderR>: Sized {
114    fn new_from_header<R: Read + Seek>(reader: &mut R, header: &T) -> Result<Self>;
115}
116
117pub trait WowReaderForData<H, T>
118where
119    Self: Read + Seek + Sized,
120    H: WowHeaderR,
121    T: WowDataR<H>,
122{
123    fn new_from_header(&mut self, header: &H) -> Result<T> {
124        T::new_from_header(self, header)
125    }
126}
127impl<H, T, R> WowReaderForData<H, T> for R
128where
129    H: WowHeaderR,
130    T: WowDataR<H>,
131    R: Read + Seek,
132{
133}
134
135pub trait VWowDataR<V, T>
136where
137    Self: Sized,
138    V: DataVersion,
139    T: VWowHeaderR<V>,
140{
141    fn new_from_header<R: Read + Seek>(reader: &mut R, header: &T) -> Result<Self>;
142}
143
144pub trait VWowReaderForData<V, H, T>
145where
146    Self: Read + Seek + Sized,
147    V: DataVersion,
148    H: VWowHeaderR<V>,
149    T: VWowDataR<V, H>,
150{
151    fn v_new_from_header(&mut self, header: &H) -> Result<T> {
152        T::new_from_header(self, header)
153    }
154}
155impl<V, H, T, R> VWowReaderForData<V, H, T> for R
156where
157    V: DataVersion,
158    H: VWowHeaderR<V>,
159    T: VWowDataR<V, H>,
160    R: Read + Seek,
161{
162}
163
164pub trait WowStructR: Sized {
165    fn wow_read<R: Read + Seek>(reader: &mut R) -> Result<Self>;
166}
167
168pub trait VWowStructR<V: DataVersion>: Sized {
169    fn wow_read<R: Read + Seek>(reader: &mut R, version: V) -> Result<Self>;
170}
171
172pub trait WowStructW {
173    fn wow_write<W: Write + Seek>(&self, writer: &mut W) -> Result<()>;
174}
175
176pub trait WowChunkR: Sized {
177    fn wow_read_from_chunk<R: Read + Seek>(
178        reader: &mut R,
179        chunk_header: &ChunkHeader,
180    ) -> Result<Vec<Self>>;
181}
182
183impl<W> WowChunkR for W
184where
185    W: WowHeaderR + WowHeaderW,
186{
187    fn wow_read_from_chunk<R: Read + Seek>(
188        reader: &mut R,
189        chunk_header: &ChunkHeader,
190    ) -> Result<Vec<Self>> {
191        let first: W = reader.wow_read()?;
192        let item_size = first.wow_size();
193        let items = chunk_header.bytes as usize / item_size;
194
195        let rest = chunk_header.bytes as usize % item_size;
196        if rest > 0 {
197            dbg!(format!(
198                "chunk items size mismatch: chunk={} item_size={}, items={}, rest={}",
199                String::from_utf8_lossy(&chunk_header.magic),
200                item_size,
201                items,
202                rest
203            ));
204        }
205
206        let mut vec = Vec::<W>::with_capacity(items);
207        vec.push(first);
208
209        for _ in 1..items {
210            vec.push(reader.wow_read()?);
211        }
212
213        reader.seek_relative(rest as i64)?;
214
215        Ok(vec)
216    }
217}
218
219pub trait WowReaderForChunk<T>
220where
221    Self: Read + Seek + Sized,
222    T: WowChunkR,
223{
224    fn wow_read_from_chunk(&mut self, chunk_header: &ChunkHeader) -> Result<Vec<T>> {
225        T::wow_read_from_chunk(self, chunk_header)
226    }
227}
228impl<T, R> WowReaderForChunk<T> for R
229where
230    T: WowChunkR,
231    R: Read + Seek,
232{
233}
234
235#[derive(Debug, Clone)]
236pub struct VersionedChunk<V: DataVersion, T> {
237    pub version: V,
238    pub items: Vec<T>,
239}
240
241pub trait VWowChunkR<V: DataVersion>: Sized {
242    fn wow_read_from_chunk<R: Read + Seek>(
243        reader: &mut R,
244        chunk_header: &ChunkHeader,
245    ) -> Result<VersionedChunk<V, Self>>;
246}
247
248impl<V, W> VWowChunkR<V> for W
249where
250    V: DataVersion + TryFrom<MagicStr, Error = WowDataError>,
251    W: VWowHeaderR<V> + WowHeaderW,
252{
253    fn wow_read_from_chunk<R: Read + Seek>(
254        reader: &mut R,
255        chunk_header: &ChunkHeader,
256    ) -> Result<VersionedChunk<V, Self>> {
257        let version: V = chunk_header.magic.try_into()?;
258
259        Ok(VersionedChunk {
260            version,
261            items: v_read_chunk_items!(reader, version, chunk_header, Self),
262        })
263    }
264}
265
266pub trait VWowReaderForChunk<V, T>
267where
268    Self: Read + Seek + Sized,
269    V: DataVersion,
270    T: VWowChunkR<V>,
271{
272    fn v_wow_read_from_chunk(
273        &mut self,
274        chunk_header: &ChunkHeader,
275    ) -> Result<VersionedChunk<V, T>> {
276        T::wow_read_from_chunk(self, chunk_header)
277    }
278}
279impl<V, T, R> VWowReaderForChunk<V, T> for R
280where
281    V: DataVersion,
282    T: VWowChunkR<V>,
283    R: Read + Seek,
284{
285}
286
287pub struct WowArrayIter<'a, T, R>
288where
289    T: WowHeaderR + WowHeaderW,
290    R: Read + Seek,
291{
292    reader: &'a mut R,
293    initial_reader_pos: u64,
294    current: u32,
295    array: WowArray<T>,
296    item_size: usize,
297}
298
299impl<'a, T, R> WowArrayIter<'a, T, R>
300where
301    T: WowHeaderR + WowHeaderW,
302    R: Read + Seek,
303{
304    pub fn new(reader: &'a mut R, array: WowArray<T>) -> Result<Self> {
305        Ok(Self {
306            reader,
307            initial_reader_pos: array.offset as u64,
308            current: 0,
309            array,
310            item_size: 0,
311        })
312    }
313
314    /// Returns `Ok(true)` if there are items remaining or `Ok(false)` if not.
315    /// This iterator needs at least one item to get the `item_size`, so it will
316    /// read the first item and call `f` with `Some(item)` the first time and `None`
317    /// from then on. It's the user's responsibility to read the subsequent items.
318    /// The reader will always be at the correct offset for reading an item at the
319    /// closure execution start
320    /// When an Err is returned, it's no longer safe to call this function again
321    pub fn next<F>(&mut self, mut f: F) -> Result<bool>
322    where
323        F: FnMut(&mut R, Option<T>) -> Result<()>,
324    {
325        if self.current >= self.array.count {
326            return Ok(false);
327        }
328
329        let current = self.current;
330        self.current += 1;
331
332        let seek_pos = self.initial_reader_pos + (current as usize * self.item_size) as u64;
333        self.reader.seek(SeekFrom::Start(seek_pos))?;
334
335        let item = if self.item_size == 0 {
336            let item: T = self.reader.wow_read()?;
337            self.item_size = item.wow_size();
338            // rewind just in case the user tries to read the item again
339            self.reader.seek(SeekFrom::Start(seek_pos))?;
340            Some(item)
341        } else {
342            None
343        };
344
345        match f(self.reader, item) {
346            Ok(_) => Ok(true),
347            Err(err) => Err(err),
348        }
349    }
350}
351
352#[derive(Debug, Default, PartialEq, WowHeaderR, WowHeaderW)]
353pub struct WowArray<T>
354where
355    T: WowHeaderR + WowHeaderW,
356{
357    pub count: u32,
358    pub offset: u32,
359    #[wow_data(override_read = std::marker::PhantomData)]
360    _phantom: std::marker::PhantomData<T>,
361}
362
363impl<T> Clone for WowArray<T>
364where
365    T: WowHeaderR + WowHeaderW,
366{
367    fn clone(&self) -> Self {
368        Self {
369            count: self.count,
370            offset: self.offset,
371            _phantom: std::marker::PhantomData,
372        }
373    }
374}
375
376impl<T> WowArray<T>
377where
378    T: WowHeaderR + WowHeaderW,
379{
380    pub fn new(count: u32, offset: u32) -> Self {
381        Self {
382            count,
383            offset,
384            _phantom: std::marker::PhantomData,
385        }
386    }
387
388    pub fn is_empty(&self) -> bool {
389        self.count == 0
390    }
391
392    pub fn add_offset(&mut self, offset: usize) {
393        self.offset += offset as u32;
394    }
395
396    pub fn new_iterator<'a, R: Read + Seek>(
397        &self,
398        reader: &'a mut R,
399    ) -> Result<WowArrayIter<'a, T, R>> {
400        WowArrayIter::new(reader, self.clone())
401    }
402}
403
404impl<T> WowArray<T>
405where
406    T: WowHeaderR + WowHeaderW,
407{
408    pub fn wow_read_to_vec<R: Read + Seek>(&self, reader: &mut R) -> Result<Vec<T>> {
409        if self.is_empty() {
410            return Ok(Vec::new());
411        }
412
413        reader
414            .seek(SeekFrom::Start(self.offset as u64))
415            .map_err(WowDataError::Io)?;
416
417        let mut result = Vec::with_capacity(self.count as usize);
418        for _ in 0..self.count {
419            result.push(T::wow_read(reader)?);
420        }
421
422        Ok(result)
423    }
424}
425
426impl<T> WowArray<WowArray<T>>
427where
428    T: WowHeaderR + WowHeaderW,
429{
430    pub fn wow_read_to_vec_r<R: Read + Seek>(&self, reader: &mut R) -> Result<Vec<Vec<T>>> {
431        if self.is_empty() {
432            return Ok(Vec::new());
433        }
434
435        reader
436            .seek(SeekFrom::Start(self.offset as u64))
437            .map_err(WowDataError::Io)?;
438
439        let mut result = Vec::with_capacity(self.count as usize);
440        for _ in 0..self.count {
441            let single: WowArray<T> = reader.wow_read()?;
442            let item_end_position = reader.stream_position()?;
443            result.push(single.wow_read_to_vec(reader)?);
444            reader.seek(SeekFrom::Start(item_end_position))?;
445        }
446
447        Ok(result)
448    }
449}
450
451pub type WowCharArray = WowArray<u8>;
452
453pub trait WowString {
454    fn from_wow_char_array<R: Read + Seek>(
455        reader: &mut R,
456        wow_char_array: WowCharArray,
457    ) -> Result<String>;
458    fn write_wow_char_array<W: Write + Seek>(&self, writer: &mut W) -> Result<WowCharArray>;
459}
460
461#[derive(Debug, PartialEq, WowHeaderW)]
462pub struct WowArrayV<V, T>
463where
464    V: DataVersion,
465    T: VWowHeaderR<V> + WowHeaderW,
466{
467    pub count: u32,
468    pub offset: u32,
469
470    #[wow_data(override_read = std::marker::PhantomData)]
471    _phantom: std::marker::PhantomData<T>,
472    #[wow_data(override_read = std::marker::PhantomData)]
473    _version: std::marker::PhantomData<V>,
474}
475
476impl<V, T> Clone for WowArrayV<V, T>
477where
478    V: DataVersion,
479    T: VWowHeaderR<V> + WowHeaderW,
480{
481    fn clone(&self) -> Self {
482        Self {
483            count: self.count,
484            offset: self.offset,
485            _phantom: std::marker::PhantomData,
486            _version: std::marker::PhantomData,
487        }
488    }
489}
490
491impl<V, T> VWowHeaderR<V> for WowArrayV<V, T>
492where
493    V: DataVersion,
494    T: VWowHeaderR<V> + WowHeaderW,
495{
496    fn wow_read<R: Read + Seek>(reader: &mut R, _version: V) -> Result<Self> {
497        Ok(Self {
498            count: reader.wow_read()?,
499            offset: reader.wow_read()?,
500            _phantom: std::marker::PhantomData,
501            _version: std::marker::PhantomData,
502        })
503    }
504}
505
506impl<V, T> Default for WowArrayV<V, T>
507where
508    V: DataVersion,
509    T: VWowHeaderR<V> + WowHeaderW,
510{
511    fn default() -> Self {
512        Self {
513            count: 0,
514            offset: 0,
515            _phantom: std::marker::PhantomData,
516            _version: std::marker::PhantomData,
517        }
518    }
519}
520
521pub struct WowArrayVIter<'a, V, T, R>
522where
523    V: DataVersion,
524    T: VWowHeaderR<V> + WowHeaderW,
525    R: Read + Seek,
526{
527    reader: &'a mut R,
528    version: V,
529    initial_reader_pos: u64,
530    current: u32,
531    array: WowArrayV<V, T>,
532    item_size: usize,
533}
534
535impl<'a, V, T, R> WowArrayVIter<'a, V, T, R>
536where
537    V: DataVersion,
538    T: VWowHeaderR<V> + WowHeaderW,
539    R: Read + Seek,
540{
541    pub fn new(reader: &'a mut R, version: V, array: WowArrayV<V, T>) -> Result<Self> {
542        Ok(Self {
543            reader,
544            version,
545            initial_reader_pos: array.offset as u64,
546            current: 0,
547            array,
548            item_size: 0,
549        })
550    }
551
552    /// Returns `Ok(true)` if there are items remaining or `Ok(false)` if not.
553    /// This iterator needs at least one item to get the `item_size`, so it will
554    /// read the first item and call `f` with `Some(item)` the first time and `None`
555    /// from then on. It's the user's responsibility to read the subsequent items.
556    /// The reader will always be at the correct offset for reading an item at the
557    /// closure execution start
558    /// When an Err is returned, it's no longer safe to call this function again
559    pub fn next<F>(&mut self, mut f: F) -> Result<bool>
560    where
561        F: FnMut(&mut R, Option<T>) -> Result<()>,
562    {
563        if self.current >= self.array.count {
564            return Ok(false);
565        }
566
567        let current = self.current;
568        self.current += 1;
569
570        let seek_pos = self.initial_reader_pos + (current as usize * self.item_size) as u64;
571        self.reader.seek(SeekFrom::Start(seek_pos))?;
572
573        let item = if self.item_size == 0 {
574            let item: T = self.reader.wow_read_versioned(self.version)?;
575            self.item_size = item.wow_size();
576            // rewind just in case the user tries to read the item again
577            self.reader.seek(SeekFrom::Start(seek_pos))?;
578            Some(item)
579        } else {
580            None
581        };
582
583        match f(self.reader, item) {
584            Ok(_) => Ok(true),
585            Err(err) => Err(err),
586        }
587    }
588}
589
590impl<V, T> WowArrayV<V, T>
591where
592    V: DataVersion,
593    T: VWowHeaderR<V> + WowHeaderW,
594{
595    pub fn is_empty(&self) -> bool {
596        self.count == 0
597    }
598
599    pub fn add_offset(&mut self, offset: usize) {
600        self.offset += offset as u32;
601    }
602
603    pub fn wow_read_to_vec<R: Read + Seek>(&self, reader: &mut R, version: V) -> Result<Vec<T>> {
604        if self.is_empty() {
605            return Ok(Vec::new());
606        }
607
608        reader
609            .seek(SeekFrom::Start(self.offset as u64))
610            .map_err(WowDataError::Io)?;
611
612        let mut result = Vec::with_capacity(self.count as usize);
613        for _ in 0..self.count {
614            result.push(T::wow_read(reader, version)?);
615        }
616
617        Ok(result)
618    }
619
620    pub fn new_iterator<'a, R: Read + Seek>(
621        &self,
622        reader: &'a mut R,
623        version: V,
624    ) -> Result<WowArrayVIter<'a, V, T, R>> {
625        WowArrayVIter::new(reader, version, self.clone())
626    }
627}
628
629pub trait WowVec<T: WowHeaderR + WowHeaderW> {
630    fn wow_write<W: Write + Seek>(&self, writer: &mut W) -> Result<WowArray<T>>;
631}
632
633#[derive(Debug, Clone, Copy, PartialEq, Default, WowHeaderR, WowHeaderW)]
634pub struct C4Vector {
635    pub x: f32,
636    pub y: f32,
637    pub z: f32,
638    pub w: f32,
639}
640
641impl C4Vector {
642    pub fn new(x: f32, y: f32, z: f32, w: f32) -> Self {
643        Self { x, y, z, w }
644    }
645
646    pub fn to_glam(&self) -> glam::Vec4 {
647        glam::Vec4::new(self.x, self.y, self.z, self.w)
648    }
649
650    pub fn from_glam(v: glam::Vec4) -> Self {
651        Self {
652            x: v.x,
653            y: v.y,
654            z: v.z,
655            w: v.w,
656        }
657    }
658
659    pub fn origin() -> Self {
660        Self {
661            x: 0.,
662            y: 0.,
663            z: 0.,
664            w: 0.,
665        }
666    }
667}
668
669#[derive(Debug, Clone, Copy, PartialEq, Default, WowHeaderR, WowHeaderW)]
670pub struct C3Vector {
671    pub x: f32,
672    pub y: f32,
673    pub z: f32,
674}
675
676impl C3Vector {
677    pub fn new(x: f32, y: f32, z: f32) -> Self {
678        Self { x, y, z }
679    }
680
681    pub fn to_glam(&self) -> glam::Vec3 {
682        glam::Vec3::new(self.x, self.y, self.z)
683    }
684
685    pub fn from_glam(v: glam::Vec3) -> Self {
686        Self {
687            x: v.x,
688            y: v.y,
689            z: v.z,
690        }
691    }
692
693    pub fn origin() -> Self {
694        Self {
695            x: 0.,
696            y: 0.,
697            z: 0.,
698        }
699    }
700}
701
702#[derive(Debug, Clone, Copy, PartialEq, Default, WowHeaderR, WowHeaderW)]
703pub struct C2Vector {
704    pub x: f32,
705    pub y: f32,
706}
707
708impl C2Vector {
709    pub fn new(x: f32, y: f32) -> Self {
710        Self { x, y }
711    }
712
713    pub fn to_glam(&self) -> glam::Vec2 {
714        glam::Vec2::new(self.x, self.y)
715    }
716
717    pub fn from_glam(v: glam::Vec2) -> Self {
718        Self { x: v.x, y: v.y }
719    }
720}
721
722#[derive(Debug, Clone, Default, PartialEq, WowHeaderR, WowHeaderW)]
723pub struct BoundingBox {
724    pub min: C3Vector,
725    pub max: C3Vector,
726}
727
728impl BoundingBox {
729    pub fn new(min: C3Vector, max: C3Vector) -> Self {
730        Self { min, max }
731    }
732
733    pub fn zero() -> Self {
734        Self::new(C3Vector::origin(), C3Vector::origin())
735    }
736}
737
738#[derive(Debug, Clone, Copy, PartialEq, WowHeaderR, WowHeaderW)]
739pub struct Quaternion {
740    pub x: f32,
741    pub y: f32,
742    pub z: f32,
743    pub w: f32,
744}
745
746impl Quaternion {
747    pub fn to_glam(&self) -> glam::Quat {
748        glam::Quat::from_xyzw(self.x, self.y, self.z, self.w)
749    }
750
751    pub fn from_glam(q: glam::Quat) -> Self {
752        Self {
753            x: q.x,
754            y: q.y,
755            z: q.z,
756            w: q.w,
757        }
758    }
759}
760
761#[inline]
762pub fn i16_to_f32(value: i16) -> f32 {
763    (value as f32 / i16::MAX as f32) - 1.0
764}
765
766impl From<Quaternion16> for Quaternion {
767    fn from(value: Quaternion16) -> Self {
768        Self {
769            x: i16_to_f32(value.x),
770            y: i16_to_f32(value.y),
771            z: i16_to_f32(value.z),
772            w: i16_to_f32(value.w),
773        }
774    }
775}
776
777#[derive(Debug, Clone, Copy, PartialEq, WowHeaderR, WowHeaderW)]
778pub struct Quaternion16 {
779    pub x: i16,
780    pub y: i16,
781    pub z: i16,
782    pub w: i16,
783}
784
785#[derive(Debug, Clone, Copy, PartialEq, WowHeaderR, WowHeaderW)]
786pub struct Color {
787    pub r: f32,
788    pub g: f32,
789    pub b: f32,
790}
791
792#[derive(Debug, Clone, Copy, PartialEq, WowHeaderR, WowHeaderW)]
793pub struct ColorA {
794    pub r: f32,
795    pub g: f32,
796    pub b: f32,
797    pub a: f32,
798}
799
800impl ColorA {
801    pub fn new(r: f32, g: f32, b: f32, a: f32) -> Self {
802        Self { r, g, b, a }
803    }
804
805    pub fn white() -> Self {
806        Self::new(1.0, 1.0, 1.0, 1.0)
807    }
808
809    pub fn black() -> Self {
810        Self::new(0.0, 0.0, 0.0, 1.0)
811    }
812
813    pub fn transparent() -> Self {
814        Self::new(0.0, 0.0, 0.0, 0.0)
815    }
816}
817
818impl WowHeaderR for [ColorA; 3] {
819    fn wow_read<R: Read + Seek>(reader: &mut R) -> Result<Self> {
820        Ok([reader.wow_read()?, reader.wow_read()?, reader.wow_read()?])
821    }
822}
823impl WowHeaderW for [ColorA; 3] {
824    fn wow_write<W: Write>(&self, writer: &mut W) -> Result<()> {
825        writer.wow_write(&self[0])?;
826        writer.wow_write(&self[1])?;
827        writer.wow_write(&self[2])?;
828        Ok(())
829    }
830
831    fn wow_size(&self) -> usize {
832        0_f32.wow_size() * 4 * 3
833    }
834}
835
836#[derive(Debug, Clone, WowHeaderR, WowHeaderW)]
837pub struct VectorFp6_9 {
838    pub x: u16,
839    pub y: u16,
840}
841
842impl WowHeaderR for [VectorFp6_9; 2] {
843    fn wow_read<R: Read + Seek>(reader: &mut R) -> Result<Self> {
844        Ok([reader.wow_read()?, reader.wow_read()?])
845    }
846}
847impl WowHeaderW for [VectorFp6_9; 2] {
848    fn wow_write<W: Write>(&self, writer: &mut W) -> Result<()> {
849        writer.wow_write(&self[0])?;
850        writer.wow_write(&self[1])?;
851        Ok(())
852    }
853
854    fn wow_size(&self) -> usize {
855        0_u16.wow_size() * 2 * 2
856    }
857}
858
859#[derive(Debug, Clone, Default)]
860pub struct Mat3x4 {
861    pub items: [C4Vector; 3],
862}
863
864impl WowHeaderR for Mat3x4 {
865    fn wow_read<R: Read + Seek>(reader: &mut R) -> Result<Self> {
866        Ok(Self {
867            items: [
868                C4Vector::wow_read(reader)?,
869                C4Vector::wow_read(reader)?,
870                C4Vector::wow_read(reader)?,
871            ],
872        })
873    }
874}
875
876impl WowHeaderW for Mat3x4 {
877    fn wow_write<W: Write>(&self, writer: &mut W) -> Result<()> {
878        writer.wow_write(&self.items[0])?;
879        writer.wow_write(&self.items[1])?;
880        writer.wow_write(&self.items[2])?;
881        Ok(())
882    }
883
884    fn wow_size(&self) -> usize {
885        self.items[0].wow_size() * 3
886    }
887}
888
889#[derive(Debug, Clone, Default, WowHeaderR, WowHeaderW)]
890pub struct ChunkHeader {
891    pub magic: MagicStr,
892    pub bytes: u32,
893}
894
895#[cfg(test)]
896mod tests {
897    use wow_alchemy_data_derive::{WowEnumFrom, WowHeaderR, WowHeaderW};
898
899    use super::*;
900    use std::io::Cursor;
901
902    #[test]
903    fn test_u32_read_write() {
904        let data = [0x05, 0x20, 0x00, 0x00];
905        let mut cursor = Cursor::new(data);
906
907        let u32_val: u32 = cursor.wow_read().unwrap();
908        assert_eq!(u32_val, 0x00002005);
909
910        let mut data = Vec::new();
911        let mut writer = Cursor::new(&mut data);
912        writer.wow_write(&0x30002100_u32).unwrap();
913        assert_eq!(data, [0x00, 0x21, 0x00, 0x30]);
914    }
915
916    #[test]
917    fn test_u16_read_write() {
918        let data = [0x13, 0x43, 0x00, 0x00];
919        let mut cursor = Cursor::new(data);
920
921        let u16val: u16 = cursor.wow_read().unwrap();
922        assert_eq!(u16val, 0x4313);
923
924        let u16val: u16 = cursor.wow_read().unwrap();
925        assert_eq!(u16val, 0x0000);
926
927        let mut data = Vec::new();
928        let mut writer = Cursor::new(&mut data);
929        writer.wow_write(&0x0330_u16).unwrap();
930        assert_eq!(data, [0x30, 0x03]);
931    }
932
933    #[test]
934    fn test_c3vector_parse() {
935        let data = [
936            0x00, 0x00, 0x80, 0x3F, // x = 1.0
937            0x00, 0x00, 0x00, 0x40, // y = 2.0
938            0x00, 0x00, 0x40, 0x40, // z = 3.0
939        ];
940
941        let mut cursor = Cursor::new(data);
942        let vector: C3Vector = cursor.wow_read().unwrap();
943
944        assert_eq!(vector.x, 1.0);
945        assert_eq!(vector.y, 2.0);
946        assert_eq!(vector.z, 3.0);
947    }
948
949    #[test]
950    fn test_c2vector_parse() {
951        let data = [
952            0x00, 0x00, 0x80, 0x3F, // x = 1.0
953            0x00, 0x00, 0x00, 0x40, // y = 2.0
954        ];
955
956        let mut cursor = Cursor::new(data);
957        let vector: C2Vector = cursor.wow_read().unwrap();
958
959        assert_eq!(vector.x, 1.0);
960        assert_eq!(vector.y, 2.0);
961    }
962
963    #[test]
964    fn test_wowarray_parse() {
965        let data = [
966            0x05, 0x00, 0x00, 0x00, // count = 5
967            0x20, 0x30, 0x00, 0x00, // offset = 0x3020
968        ];
969
970        let mut cursor = Cursor::new(data);
971        let array: WowArray<u32> = cursor.wow_read().unwrap();
972
973        assert_eq!(array.count, 5);
974        assert_eq!(array.offset, 0x3020);
975    }
976
977    #[test]
978    fn test_wowarray_write() {
979        let array = WowArray::<u32>::new(5, 0x3020);
980        let mut cursor = Cursor::new(Vec::new());
981
982        cursor.wow_write(&array).unwrap();
983
984        let data = cursor.into_inner();
985        assert_eq!(
986            data,
987            [
988                0x05, 0x00, 0x00, 0x00, // count = 5
989                0x20, 0x30, 0x00, 0x00, // offset = 0x3020
990            ]
991        );
992    }
993
994    #[test]
995    fn test_wowarray_to_vec() {
996        let data = [
997            0x00, 0x00, 0x80, 0x3F, // x = 1.0
998            0x00, 0x00, 0x00, 0x40, // y = 2.0
999            0x00, 0x00, 0x00, 0x40, // x = 2.0
1000            0x00, 0x00, 0x80, 0x3F, // y = 1.0
1001            0x00, 0x00, 0x80, 0x3F, // x = 1.0
1002            0x00, 0x00, 0x40, 0x40, // y = 3.0
1003        ];
1004
1005        let array = WowArray::<C2Vector>::new(3, 0);
1006
1007        let mut cursor = Cursor::new(data);
1008        let vec = array.wow_read_to_vec(&mut cursor).unwrap();
1009
1010        assert_eq!(
1011            vec,
1012            vec![
1013                C2Vector::new(1., 2.),
1014                C2Vector::new(2., 1.),
1015                C2Vector::new(1., 3.)
1016            ]
1017        );
1018    }
1019
1020    #[test]
1021    fn test_vec_to_wowarray() {
1022        let mut cursor = Cursor::new(Vec::new());
1023
1024        cursor.wow_write(&0xB00B5_u32).unwrap();
1025        cursor.wow_write(&0xDEAD_u16).unwrap();
1026
1027        let vec = vec![
1028            C2Vector::new(1., 2.),
1029            C2Vector::new(2., 1.),
1030            C2Vector::new(1., 3.),
1031        ];
1032
1033        let mut wowarr = vec.wow_write(&mut cursor).unwrap();
1034        wowarr.add_offset(3);
1035
1036        assert_eq!(wowarr, WowArray::<C2Vector>::new(3, 9));
1037    }
1038
1039    #[allow(clippy::upper_case_acronyms)]
1040    #[derive(
1041        super::Debug,
1042        Clone,
1043        Copy,
1044        PartialEq,
1045        Eq,
1046        PartialOrd,
1047        Ord,
1048        Hash,
1049        WowEnumFrom,
1050        WowHeaderR,
1051        WowHeaderW,
1052    )]
1053    #[wow_data(from_type=u32)]
1054    pub enum M2Version {
1055        #[wow_data(expr = 1)]
1056        Classic,
1057        #[wow_data(expr = 2)]
1058        TBC,
1059        #[wow_data(expr = 3)]
1060        WotLK,
1061        #[wow_data(expr = 4)]
1062        Cataclysm,
1063        #[wow_data(expr = 5)]
1064        MoP,
1065        #[wow_data(expr = 6)]
1066        WoD,
1067        #[wow_data(expr = 7)]
1068        Legion,
1069        #[wow_data(expr = 8)]
1070        BfA,
1071    }
1072
1073    impl DataVersion for M2Version {}
1074
1075    #[derive(super::Debug, Clone, Copy, WowHeaderR, WowHeaderW)]
1076    #[wow_data(version = M2Version)]
1077    enum ExampleVersioned {
1078        #[wow_data(read_if = version <= M2Version::TBC)]
1079        UpToTBC(i16, f32),
1080        Others(u16),
1081    }
1082
1083    #[derive(super::Debug, Clone, Copy, WowHeaderR, WowHeaderW)]
1084    #[wow_data(version = M2Version)]
1085    enum OptionUpToMoP<T: WowHeaderR + WowHeaderW> {
1086        #[wow_data(read_if = version <= M2Version::MoP)]
1087        Some(T),
1088        None,
1089    }
1090
1091    #[derive(super::Debug, Clone, Copy, WowHeaderR, WowHeaderW)]
1092    #[wow_data(version = M2Version)]
1093    enum OptionAfterMoP<T: WowHeaderR + WowHeaderW> {
1094        #[wow_data(read_if = version > M2Version::MoP)]
1095        Some(T),
1096        None,
1097    }
1098
1099    #[derive(super::Debug, Clone, WowHeaderR, WowHeaderW)]
1100    #[wow_data(version = M2Version)]
1101    struct ExampleHeader {
1102        #[wow_data(override_read = M2Version::Classic)]
1103        _version: M2Version,
1104        crc: u32,
1105        vectors: WowArray<C2Vector>,
1106        #[wow_data(versioned)]
1107        versioned_data: ExampleVersioned,
1108        bounding_box: BoundingBox,
1109        #[wow_data(versioned)]
1110        up_to_mop: OptionUpToMoP<i16>,
1111        #[wow_data(versioned)]
1112        after_mop: OptionAfterMoP<f32>,
1113    }
1114
1115    struct ExampleData {
1116        header: ExampleHeader,
1117        vectors: Vec<C2Vector>,
1118    }
1119
1120    impl ExampleData {
1121        fn read<R: Read + Seek>(reader: &mut R, version: u32) -> Result<Self> {
1122            let version = version.try_into()?;
1123            let header: ExampleHeader = reader.wow_read_versioned(version)?;
1124            let vectors = header.vectors.wow_read_to_vec(reader)?;
1125
1126            Ok(Self { header, vectors })
1127        }
1128
1129        fn write<W: Write>(&self, writer: &mut W) -> Result<()> {
1130            let header_size = self.header.wow_size();
1131
1132            let mut data_section = Vec::new();
1133            let mut data_section_writer = Cursor::new(&mut data_section);
1134
1135            let mut vectors = self.vectors.wow_write(&mut data_section_writer)?;
1136            vectors.add_offset(header_size);
1137
1138            let mut new_header = self.header.clone();
1139            new_header.vectors = vectors;
1140
1141            writer.wow_write(&new_header)?;
1142            writer.write_all(&data_section)?;
1143
1144            Ok(())
1145        }
1146    }
1147
1148    #[test]
1149    fn test_simple_struct_read_write() {
1150        let example_data_bin = [
1151            // header
1152            0x16, 0x00, 0x00, 0x00, // crc
1153            0x02, 0x00, 0x00, 0x00, // vectors count
1154            0x28, 0x00, 0x00, 0x00, // vectors offset
1155            0x42, 0x00, // ExampleVersioned::Others(66)
1156            0x00, 0x00, 0x80, 0x3F, // bounding_box.min.x
1157            0x00, 0x00, 0x00, 0x40, // bounding_box.min.y
1158            0x00, 0x00, 0x40, 0x40, // bounding_box.min.z
1159            0x00, 0x00, 0x40, 0x40, // bounding_box.max.x
1160            0x00, 0x00, 0x00, 0x40, // bounding_box.max.y
1161            0x00, 0x00, 0x80, 0x3F, // bounding_box.max.z
1162            0x23, 0x01, // up_to_mop
1163            // data section
1164            0x00, 0x00, 0x80, 0x3F, // vectors.0.x
1165            0x00, 0x00, 0x00, 0x40, // vectors.0.y
1166            0x00, 0x00, 0x00, 0x40, // vectors.1.x
1167            0x00, 0x00, 0x80, 0x3F, // vectors.1.y
1168        ];
1169
1170        let example_data = ExampleData {
1171            header: ExampleHeader {
1172                _version: M2Version::WotLK,
1173                crc: 22,
1174                vectors: WowArray::default(),
1175                versioned_data: ExampleVersioned::Others(66),
1176                bounding_box: BoundingBox::new(
1177                    C3Vector::new(1., 2., 3.),
1178                    C3Vector::new(3., 2., 1.),
1179                ),
1180                up_to_mop: OptionUpToMoP::Some(0x123),
1181                after_mop: OptionAfterMoP::None,
1182            },
1183            vectors: vec![C2Vector::new(1., 2.), C2Vector::new(2., 1.)],
1184        };
1185
1186        let mut writer = Cursor::new(Vec::new());
1187        example_data.write(&mut writer).unwrap();
1188
1189        assert_eq!(*writer.get_ref(), example_data_bin);
1190
1191        let mut cursor = Cursor::new(&example_data_bin);
1192        let decoded = ExampleData::read(&mut cursor, example_data.header._version.into()).unwrap();
1193        let mut dec_writer = Cursor::new(Vec::new());
1194        decoded.write(&mut dec_writer).unwrap();
1195
1196        assert_eq!(*dec_writer.get_ref(), example_data_bin);
1197    }
1198
1199    struct ExampleDataNoHeader {
1200        _version: M2Version,
1201        crc: u32,
1202        vectors: Vec<C2Vector>,
1203        versioned_data: ExampleVersioned,
1204        bounding_box: BoundingBox,
1205        up_to_mop: OptionUpToMoP<i16>,
1206        after_mop: OptionAfterMoP<f32>,
1207    }
1208
1209    impl ExampleDataNoHeader {
1210        fn read<R: Read + Seek>(reader: &mut R, version: u32) -> Result<Self> {
1211            let version = version.try_into()?;
1212            let header: ExampleHeader = reader.wow_read_versioned(version)?;
1213            let vectors = header.vectors.wow_read_to_vec(reader)?;
1214
1215            Ok(Self {
1216                _version: version,
1217                crc: header.crc,
1218                vectors,
1219                versioned_data: header.versioned_data,
1220                bounding_box: header.bounding_box,
1221                up_to_mop: header.up_to_mop,
1222                after_mop: header.after_mop,
1223            })
1224        }
1225    }
1226
1227    impl WowHeaderW for ExampleDataNoHeader {
1228        fn wow_write<W: Write>(&self, writer: &mut W) -> Result<()> {
1229            let mut new_header = ExampleHeader {
1230                _version: self._version,
1231                crc: self.crc,
1232                vectors: WowArray::default(),
1233                versioned_data: self.versioned_data,
1234                bounding_box: self.bounding_box.clone(),
1235                up_to_mop: self.up_to_mop,
1236                after_mop: self.after_mop,
1237            };
1238
1239            let header_size = new_header.wow_size();
1240
1241            let mut data_section = Vec::new();
1242            let mut data_section_writer = Cursor::new(&mut data_section);
1243
1244            new_header.vectors = self.vectors.wow_write(&mut data_section_writer)?;
1245            new_header.vectors.add_offset(header_size);
1246
1247            writer.wow_write(&new_header)?;
1248            writer.write_all(&data_section)?;
1249
1250            Ok(())
1251        }
1252
1253        fn wow_size(&self) -> usize {
1254            todo!()
1255        }
1256    }
1257
1258    #[test]
1259    fn test_simple_struct_no_header_read_write() {
1260        let example_data_bin = [
1261            // header
1262            0x16, 0x00, 0x00, 0x00, // crc
1263            0x02, 0x00, 0x00, 0x00, // vectors count
1264            0x2A, 0x00, 0x00, 0x00, // vectors offset
1265            0x42, 0x00, // ExampleVersioned::Others(66)
1266            0x00, 0x00, 0x80, 0x3F, // bounding_box.min.x
1267            0x00, 0x00, 0x00, 0x40, // bounding_box.min.y
1268            0x00, 0x00, 0x40, 0x40, // bounding_box.min.z
1269            0x00, 0x00, 0x40, 0x40, // bounding_box.max.x
1270            0x00, 0x00, 0x00, 0x40, // bounding_box.max.y
1271            0x00, 0x00, 0x80, 0x3F, // bounding_box.max.z
1272            0x00, 0x00, 0x80, 0x3F, // after_mop
1273            // data section
1274            0x00, 0x00, 0x80, 0x3F, // vectors.0.x
1275            0x00, 0x00, 0x00, 0x40, // vectors.0.y
1276            0x00, 0x00, 0x00, 0x40, // vectors.1.x
1277            0x00, 0x00, 0x80, 0x3F, // vectors.1.y
1278        ];
1279
1280        let example_data = ExampleDataNoHeader {
1281            _version: M2Version::WoD,
1282            crc: 22,
1283            versioned_data: ExampleVersioned::Others(66),
1284            bounding_box: BoundingBox::new(C3Vector::new(1., 2., 3.), C3Vector::new(3., 2., 1.)),
1285            up_to_mop: OptionUpToMoP::None,
1286            after_mop: OptionAfterMoP::Some(1.0),
1287            vectors: vec![C2Vector::new(1., 2.), C2Vector::new(2., 1.)],
1288        };
1289
1290        let mut writer = Cursor::new(Vec::new());
1291        writer.wow_write(&example_data).unwrap();
1292
1293        assert_eq!(*writer.get_ref(), example_data_bin);
1294
1295        let mut cursor = Cursor::new(&example_data_bin);
1296        let decoded = ExampleDataNoHeader::read(&mut cursor, example_data._version.into()).unwrap();
1297        let mut dec_writer = Cursor::new(Vec::new());
1298        dec_writer.wow_write(&decoded).unwrap();
1299
1300        assert_eq!(*dec_writer.get_ref(), example_data_bin);
1301    }
1302
1303    impl WowHeaderConversible<M2Version> for ExampleDataNoHeader {
1304        fn wow_convert(&self, to_version: M2Version) -> Result<Self> {
1305            match to_version {
1306                M2Version::Classic | M2Version::TBC => Ok(Self {
1307                    _version: to_version,
1308                    crc: self.crc,
1309                    versioned_data: if self._version <= M2Version::TBC {
1310                        self.versioned_data
1311                    } else {
1312                        ExampleVersioned::UpToTBC(0, 2.0)
1313                    },
1314                    bounding_box: self.bounding_box.clone(),
1315                    up_to_mop: if self._version <= M2Version::MoP {
1316                        self.up_to_mop
1317                    } else {
1318                        OptionUpToMoP::Some(0)
1319                    },
1320                    after_mop: OptionAfterMoP::None,
1321                    vectors: self.vectors.clone(),
1322                }),
1323                _ => {
1324                    if to_version <= M2Version::WoD {
1325                        Ok(Self {
1326                            _version: to_version,
1327                            crc: self.crc,
1328                            versioned_data: if self._version <= M2Version::TBC {
1329                                ExampleVersioned::Others(0x49)
1330                            } else {
1331                                self.versioned_data
1332                            },
1333                            bounding_box: self.bounding_box.clone(),
1334                            up_to_mop: if to_version > M2Version::MoP {
1335                                OptionUpToMoP::None
1336                            } else if self._version <= M2Version::MoP {
1337                                self.up_to_mop
1338                            } else {
1339                                OptionUpToMoP::Some(0)
1340                            },
1341                            after_mop: if to_version > M2Version::MoP {
1342                                if self._version > M2Version::MoP {
1343                                    self.after_mop
1344                                } else {
1345                                    OptionAfterMoP::Some(0.0)
1346                                }
1347                            } else {
1348                                OptionAfterMoP::None
1349                            },
1350                            vectors: self.vectors.clone(),
1351                        })
1352                    } else {
1353                        Err(WowDataError::ConversionError {
1354                            from: self._version.into(),
1355                            to: to_version.into(),
1356                            reason: "not supported".into(),
1357                        })
1358                    }
1359                }
1360            }
1361        }
1362    }
1363
1364    #[test]
1365    fn test_simple_conversion() {
1366        let example_data_bin = [
1367            // header
1368            0x16, 0x00, 0x00, 0x00, // crc
1369            0x02, 0x00, 0x00, 0x00, // vectors count
1370            0x2A, 0x00, 0x00, 0x00, // vectors offset
1371            0x42, 0x00, // ExampleVersioned::Others(66)
1372            0x00, 0x00, 0x80, 0x3F, // bounding_box.min.x
1373            0x00, 0x00, 0x00, 0x40, // bounding_box.min.y
1374            0x00, 0x00, 0x40, 0x40, // bounding_box.min.z
1375            0x00, 0x00, 0x40, 0x40, // bounding_box.max.x
1376            0x00, 0x00, 0x00, 0x40, // bounding_box.max.y
1377            0x00, 0x00, 0x80, 0x3F, // bounding_box.max.z
1378            0x00, 0x00, 0x80, 0x3F, // after_mop
1379            // data section
1380            0x00, 0x00, 0x80, 0x3F, // vectors.0.x
1381            0x00, 0x00, 0x00, 0x40, // vectors.0.y
1382            0x00, 0x00, 0x00, 0x40, // vectors.1.x
1383            0x00, 0x00, 0x80, 0x3F, // vectors.1.y
1384        ];
1385
1386        let mut cursor = Cursor::new(&example_data_bin);
1387        let decoded = ExampleDataNoHeader::read(&mut cursor, M2Version::WoD.into()).unwrap();
1388        let mut dec_writer = Cursor::new(Vec::new());
1389        dec_writer.wow_write(&decoded).unwrap();
1390
1391        assert_eq!(*dec_writer.get_ref(), example_data_bin);
1392
1393        let mut converted_writer = Cursor::new(Vec::new());
1394        converted_writer
1395            .wow_write_versioned(&decoded, M2Version::Classic)
1396            .unwrap();
1397
1398        let example_converted_data_bin = [
1399            // header
1400            0x16, 0x00, 0x00, 0x00, // crc
1401            0x02, 0x00, 0x00, 0x00, // vectors count
1402            0x2C, 0x00, 0x00, 0x00, // vectors offset
1403            0x00, 0x00, // ExampleVersioned::UpToTBC.0 = 0
1404            0x00, 0x00, 0x00, 0x40, // ExampleVersioned::UpToTBC.1 = 2.0
1405            0x00, 0x00, 0x80, 0x3F, // bounding_box.min.x
1406            0x00, 0x00, 0x00, 0x40, // bounding_box.min.y
1407            0x00, 0x00, 0x40, 0x40, // bounding_box.min.z
1408            0x00, 0x00, 0x40, 0x40, // bounding_box.max.x
1409            0x00, 0x00, 0x00, 0x40, // bounding_box.max.y
1410            0x00, 0x00, 0x80, 0x3F, // bounding_box.max.z
1411            0x00, 0x00, // up_to_mop
1412            // data section
1413            0x00, 0x00, 0x80, 0x3F, // vectors.0.x
1414            0x00, 0x00, 0x00, 0x40, // vectors.0.y
1415            0x00, 0x00, 0x00, 0x40, // vectors.1.x
1416            0x00, 0x00, 0x80, 0x3F, // vectors.1.y
1417        ];
1418
1419        assert_eq!(*converted_writer.get_ref(), example_converted_data_bin);
1420    }
1421}