wow_m2/
common.rs

1use crate::error::{M2Error, Result};
2use crate::io_ext::{ReadExt, WriteExt};
3use std::io::{Read, Seek, SeekFrom, Write};
4
5/// Trait for parsing and writing types to and from the M2 binary file format.
6///
7/// Types implementing `M2Parse` can be deserialized from a binary reader and serialized to a binary writer.
8/// This trait is used throughout the M2 parsing code to provide a generic interface for reading and writing
9/// primitive types, vectors, and complex structures in a version-agnostic way.
10pub trait M2Parse {
11    /// Parse an instance of the type from the given reader.
12    ///
13    /// # Arguments
14    /// * `reader` - A mutable reference to a type implementing `Read + Seek`.
15    ///
16    /// # Returns
17    /// * `Result<Self>` - The parsed instance or an error.
18    fn parse<R: Read + Seek>(reader: &mut R) -> Result<Self>
19    where
20        Self: Sized;
21
22    /// Write this instance to the given writer.
23    ///
24    /// # Arguments
25    /// * `writer` - A mutable reference to a type implementing `Write`.
26    ///
27    /// # Returns
28    /// * `Result<()>` - Ok if successful, or an error.
29    fn write<W: Write>(&self, writer: &mut W) -> Result<()>;
30}
31
32impl M2Parse for f32 {
33    fn parse<R: Read + Seek>(reader: &mut R) -> Result<Self> {
34        Ok(reader.read_f32_le()?)
35    }
36
37    fn write<W: Write>(&self, writer: &mut W) -> Result<()> {
38        writer.write_f32_le(*self)?;
39        Ok(())
40    }
41}
42
43impl M2Parse for u16 {
44    fn parse<R: Read + Seek>(reader: &mut R) -> Result<Self> {
45        Ok(reader.read_u16_le()?)
46    }
47
48    fn write<W: Write>(&self, writer: &mut W) -> Result<()> {
49        writer.write_u16_le(*self)?;
50        Ok(())
51    }
52}
53
54impl M2Parse for u32 {
55    fn parse<R: Read + Seek>(reader: &mut R) -> Result<Self> {
56        Ok(reader.read_u32_le()?)
57    }
58
59    fn write<W: Write>(&self, writer: &mut W) -> Result<()> {
60        writer.write_u32_le(*self)?;
61        Ok(())
62    }
63}
64
65/// Represents a vector of elements in the M2 file format, along with its array reference.
66///
67/// `M2Vec` stores both the `M2Array` reference (count and offset in the file) and the actual data elements.
68/// This is used for fields in M2 structs that point to arrays of data elsewhere in the file.
69///
70/// The `data` field is populated when parsing, but only the `array` field is written when serializing,
71/// as the actual data is written separately at the correct offset.
72#[derive(Debug, Clone, Default)]
73pub struct M2Vec<T: M2Parse> {
74    /// Reference to the array in the file (count and offset)
75    pub array: M2Array<T>,
76    /// The actual data elements (populated when parsing)
77    pub data: Vec<T>,
78}
79
80impl<T: M2Parse> M2Vec<T> {
81    /// Create a new, empty `M2Vec` with no data and a zeroed array reference.
82    pub fn new() -> Self {
83        Self {
84            array: M2Array::new(0, 0),
85            data: Vec::new(),
86        }
87    }
88}
89
90impl<T: M2Parse> M2Parse for M2Vec<T> {
91    fn parse<R: Read + Seek>(reader: &mut R) -> Result<Self> {
92        let array = M2Array::<T>::parse(reader)?;
93        let pos = reader.stream_position()?;
94        let data = read_array(reader, &array, |r| T::parse(r))?;
95        reader.seek(SeekFrom::Start(pos))?;
96        Ok(Self { array, data })
97    }
98
99    fn write<W: Write>(&self, writer: &mut W) -> Result<()> {
100        self.array.write(writer)?;
101        Ok(())
102    }
103}
104
105/// A reference to an array in the M2 file format
106#[derive(Debug, Clone, Copy, Default, PartialEq)]
107pub struct M2Array<T> {
108    /// Number of elements in the array
109    pub count: u32,
110    /// Offset from the start of the file to the array
111    pub offset: u32,
112    /// Phantom data to associate with the type T
113    _phantom: std::marker::PhantomData<T>,
114}
115
116impl<T> M2Array<T> {
117    /// Create a new array reference
118    pub fn new(count: u32, offset: u32) -> Self {
119        Self {
120            count,
121            offset,
122            _phantom: std::marker::PhantomData,
123        }
124    }
125
126    /// Parse an array reference from a reader
127    pub fn parse<R: Read>(reader: &mut R) -> Result<Self> {
128        let count = reader.read_u32_le()?;
129        let offset = reader.read_u32_le()?;
130
131        Ok(Self {
132            count,
133            offset,
134            _phantom: std::marker::PhantomData,
135        })
136    }
137
138    /// Write an array reference to a writer
139    pub fn write<W: Write>(&self, writer: &mut W) -> Result<()> {
140        writer.write_u32_le(self.count)?;
141        writer.write_u32_le(self.offset)?;
142
143        Ok(())
144    }
145
146    /// Check if the array is empty
147    pub fn is_empty(&self) -> bool {
148        self.count == 0
149    }
150
151    /// Convert this reference to a reference of another type
152    pub fn convert<U>(&self) -> M2Array<U> {
153        M2Array {
154            count: self.count,
155            offset: self.offset,
156            _phantom: std::marker::PhantomData,
157        }
158    }
159}
160
161/// Reads data at an array reference location
162pub fn read_array<T, R, F>(reader: &mut R, array: &M2Array<T>, parse_fn: F) -> Result<Vec<T>>
163where
164    R: Read + Seek,
165    F: Fn(&mut R) -> Result<T>,
166{
167    if array.is_empty() {
168        return Ok(Vec::new());
169    }
170
171    // Seek to the array data
172    reader
173        .seek(std::io::SeekFrom::Start(array.offset as u64))
174        .map_err(M2Error::Io)?;
175
176    // Read each element
177    let mut result = Vec::with_capacity(array.count as usize);
178    for _ in 0..array.count {
179        result.push(parse_fn(reader)?);
180    }
181
182    Ok(result)
183}
184
185/// Reads raw bytes from an M2Array reference, preserving data for sections we don't fully parse
186pub fn read_raw_bytes<R: Read + Seek>(
187    reader: &mut R,
188    array: &M2Array<u32>,
189    element_size: usize,
190) -> Result<Vec<u8>> {
191    if array.is_empty() {
192        return Ok(Vec::new());
193    }
194
195    // Seek to the array data
196    reader
197        .seek(std::io::SeekFrom::Start(array.offset as u64))
198        .map_err(M2Error::Io)?;
199
200    // Read raw bytes
201    let total_bytes = array.count as usize * element_size;
202    let mut data = vec![0u8; total_bytes];
203    reader.read_exact(&mut data).map_err(M2Error::Io)?;
204
205    Ok(data)
206}
207
208/// A vector in 3D space
209#[derive(Debug, Clone, Copy, PartialEq, Default)]
210pub struct C3Vector {
211    pub x: f32,
212    pub y: f32,
213    pub z: f32,
214}
215
216impl M2Parse for C3Vector {
217    fn parse<R: Read + Seek>(reader: &mut R) -> Result<Self> {
218        C3Vector::parse(reader)
219    }
220
221    fn write<W: Write>(&self, writer: &mut W) -> Result<()> {
222        self.write(writer)
223    }
224}
225
226impl C3Vector {
227    /// Parse a C3Vector from a reader
228    pub fn parse<R: Read>(reader: &mut R) -> Result<Self> {
229        let x = reader.read_f32_le()?;
230        let y = reader.read_f32_le()?;
231        let z = reader.read_f32_le()?;
232
233        Ok(Self { x, y, z })
234    }
235
236    /// Write a C3Vector to a writer
237    pub fn write<W: Write>(&self, writer: &mut W) -> Result<()> {
238        writer.write_f32_le(self.x)?;
239        writer.write_f32_le(self.y)?;
240        writer.write_f32_le(self.z)?;
241
242        Ok(())
243    }
244
245    /// Convert to a glam vector for easier math operations
246    pub fn to_glam(&self) -> glam::Vec3 {
247        glam::Vec3::new(self.x, self.y, self.z)
248    }
249
250    /// Create from a glam vector
251    pub fn from_glam(v: glam::Vec3) -> Self {
252        Self {
253            x: v.x,
254            y: v.y,
255            z: v.z,
256        }
257    }
258}
259
260/// A vector in 2D space
261#[derive(Debug, Clone, Copy, PartialEq, Default)]
262pub struct C2Vector {
263    pub x: f32,
264    pub y: f32,
265}
266
267impl M2Parse for C2Vector {
268    fn parse<R: Read + Seek>(reader: &mut R) -> Result<Self> {
269        C2Vector::parse(reader)
270    }
271
272    fn write<W: Write>(&self, writer: &mut W) -> Result<()> {
273        self.write(writer)
274    }
275}
276
277impl C2Vector {
278    /// Parse a C2Vector from a reader
279    pub fn parse<R: Read>(reader: &mut R) -> Result<Self> {
280        let x = reader.read_f32_le()?;
281        let y = reader.read_f32_le()?;
282
283        Ok(Self { x, y })
284    }
285
286    /// Write a C2Vector to a writer
287    pub fn write<W: Write>(&self, writer: &mut W) -> Result<()> {
288        writer.write_f32_le(self.x)?;
289        writer.write_f32_le(self.y)?;
290
291        Ok(())
292    }
293
294    /// Convert to a glam vector for easier math operations
295    pub fn to_glam(&self) -> glam::Vec2 {
296        glam::Vec2::new(self.x, self.y)
297    }
298
299    /// Create from a glam vector
300    pub fn from_glam(v: glam::Vec2) -> Self {
301        Self { x: v.x, y: v.y }
302    }
303}
304
305/// A fixed-width string with a specified maximum length
306#[derive(Debug, Clone, PartialEq, Default)]
307pub struct FixedString {
308    pub data: Vec<u8>,
309}
310
311impl FixedString {
312    pub fn len(&self) -> usize {
313        self.data.len()
314    }
315
316    /// Check if the string is empty
317    pub fn is_empty(&self) -> bool {
318        self.data.is_empty()
319    }
320
321    /// Parse a fixed-width string from a reader
322    pub fn parse<R: Read + Seek>(reader: &mut R, len: usize) -> Result<Self> {
323        let mut data = vec![0u8; len];
324        reader.read_exact(&mut data)?;
325
326        // Find null terminator
327        let null_pos = data.iter().position(|&b| b == 0).unwrap_or(len);
328        data.truncate(null_pos);
329
330        Ok(Self { data })
331    }
332
333    /// Write a fixed-width string to a writer
334    pub fn write<W: Write>(&self, writer: &mut W, len: usize) -> Result<()> {
335        let mut data = self.data.clone();
336        data.resize(len, 0);
337        writer.write_all(&data)?;
338
339        Ok(())
340    }
341
342    /// Convert to a string, lossy UTF-8 conversion
343    pub fn to_string_lossy(&self) -> String {
344        String::from_utf8_lossy(&self.data).to_string()
345    }
346}
347
348#[derive(Debug, Clone, PartialEq, Default)]
349pub struct M2ArrayString {
350    pub string: FixedString,
351    pub array: M2Array<u8>,
352}
353
354impl M2ArrayString {
355    pub fn parse<R: Read + Seek>(reader: &mut R) -> Result<Self> {
356        let array = M2Array::<u8>::parse(reader)?;
357        let current_pos = reader.stream_position()?;
358        reader.seek(SeekFrom::Start(array.offset as u64))?;
359        let string = FixedString::parse(reader, array.count as usize)?;
360        reader.seek(SeekFrom::Start(current_pos))?;
361        Ok(Self { string, array })
362    }
363
364    pub fn is_empty(&self) -> bool {
365        self.array.is_empty()
366    }
367
368    /// Write a reference to our array to a writer
369    pub fn write<W: Write>(&self, writer: &mut W) -> Result<()> {
370        writer.write_u32_le(self.array.count)?;
371        writer.write_u32_le(self.array.offset)?;
372
373        Ok(())
374    }
375}
376
377/// A quaternion for rotations
378#[derive(Debug, Clone, Copy, PartialEq)]
379pub struct Quaternion {
380    pub x: f32,
381    pub y: f32,
382    pub z: f32,
383    pub w: f32,
384}
385
386impl Quaternion {
387    /// Parse a quaternion from a reader
388    pub fn parse<R: Read>(reader: &mut R) -> Result<Self> {
389        let x = reader.read_f32_le()?;
390        let y = reader.read_f32_le()?;
391        let z = reader.read_f32_le()?;
392        let w = reader.read_f32_le()?;
393
394        Ok(Self { x, y, z, w })
395    }
396
397    /// Write a quaternion to a writer
398    pub fn write<W: Write>(&self, writer: &mut W) -> Result<()> {
399        writer.write_f32_le(self.x)?;
400        writer.write_f32_le(self.y)?;
401        writer.write_f32_le(self.z)?;
402        writer.write_f32_le(self.w)?;
403
404        Ok(())
405    }
406
407    /// Convert to a glam quaternion for easier math operations
408    pub fn to_glam(&self) -> glam::Quat {
409        glam::Quat::from_xyzw(self.x, self.y, self.z, self.w)
410    }
411
412    /// Create from a glam quaternion
413    pub fn from_glam(q: glam::Quat) -> Self {
414        Self {
415            x: q.x,
416            y: q.y,
417            z: q.z,
418            w: q.w,
419        }
420    }
421}
422
423#[cfg(test)]
424mod tests {
425    use super::*;
426    use std::io::Cursor;
427
428    #[test]
429    fn test_m2array_parse() {
430        let data = [
431            0x05, 0x00, 0x00, 0x00, // count = 5
432            0x20, 0x30, 0x00, 0x00, // offset = 0x3020
433        ];
434
435        let mut cursor = Cursor::new(data);
436        let array = M2Array::<u32>::parse(&mut cursor).unwrap();
437
438        assert_eq!(array.count, 5);
439        assert_eq!(array.offset, 0x3020);
440    }
441
442    #[test]
443    fn test_m2array_write() {
444        let array = M2Array::<u32>::new(5, 0x3020);
445        let mut cursor = Cursor::new(Vec::new());
446
447        array.write(&mut cursor).unwrap();
448
449        let data = cursor.into_inner();
450        assert_eq!(
451            data,
452            [
453                0x05, 0x00, 0x00, 0x00, // count = 5
454                0x20, 0x30, 0x00, 0x00, // offset = 0x3020
455            ]
456        );
457    }
458
459    #[test]
460    fn test_c3vector_parse() {
461        let data = [
462            0x00, 0x00, 0x80, 0x3F, // x = 1.0
463            0x00, 0x00, 0x00, 0x40, // y = 2.0
464            0x00, 0x00, 0x40, 0x40, // z = 3.0
465        ];
466
467        let mut cursor = Cursor::new(data);
468        let vector = C3Vector::parse(&mut cursor).unwrap();
469
470        assert_eq!(vector.x, 1.0);
471        assert_eq!(vector.y, 2.0);
472        assert_eq!(vector.z, 3.0);
473    }
474
475    #[test]
476    fn test_c2vector_parse() {
477        let data = [
478            0x00, 0x00, 0x80, 0x3F, // x = 1.0
479            0x00, 0x00, 0x00, 0x40, // y = 2.0
480        ];
481
482        let mut cursor = Cursor::new(data);
483        let vector = C2Vector::parse(&mut cursor).unwrap();
484
485        assert_eq!(vector.x, 1.0);
486        assert_eq!(vector.y, 2.0);
487    }
488
489    #[test]
490    fn test_fixed_string_parse() {
491        let data = [b'T', b'e', b's', b't', 0, 0, 0, 0];
492
493        let mut cursor = Cursor::new(data);
494        let string = FixedString::parse(&mut cursor, 8).unwrap();
495
496        assert_eq!(string.data, b"Test");
497        assert_eq!(string.to_string_lossy(), "Test");
498    }
499}