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 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 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 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 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, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x40, ];
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, 0x00, 0x00, 0x00, 0x40, ];
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, 0x20, 0x30, 0x00, 0x00, ];
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, 0x20, 0x30, 0x00, 0x00, ]
991 );
992 }
993
994 #[test]
995 fn test_wowarray_to_vec() {
996 let data = [
997 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x40, 0x40, ];
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 0x16, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x40, 0x00, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x80, 0x3F, 0x23, 0x01, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x80, 0x3F, ];
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 0x16, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x40, 0x00, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x80, 0x3F, ];
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 0x16, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x40, 0x00, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x80, 0x3F, ];
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 0x16, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x40, 0x00, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x80, 0x3F, ];
1418
1419 assert_eq!(*converted_writer.get_ref(), example_converted_data_bin);
1420 }
1421}