wow_m2/
anim.rs

1use crate::io_ext::{ReadExt, WriteExt};
2use std::fs::File;
3use std::io::{Read, Seek, SeekFrom, Write};
4use std::path::Path;
5
6use crate::common::{C3Vector, Quaternion};
7use crate::error::{M2Error, Result};
8use crate::version::M2Version;
9
10/// Magic signature for Anim files ("MAOF")
11pub const ANIM_MAGIC: [u8; 4] = *b"MAOF";
12
13/// ANIM file header
14#[derive(Debug, Clone)]
15pub struct AnimHeader {
16    /// Magic signature ("MAOF")
17    pub magic: [u8; 4],
18    /// Anim version
19    pub version: u32,
20    /// The number of AFID IDs in this file
21    pub id_count: u32,
22    /// Unknown
23    pub unknown: u32,
24    /// Offset to animation entries
25    pub anim_entry_offset: u32,
26}
27
28impl AnimHeader {
29    /// Parse an ANIM header from a reader
30    pub fn parse<R: Read + Seek>(reader: &mut R) -> Result<Self> {
31        // Read and check magic
32        let mut magic = [0u8; 4];
33        reader.read_exact(&mut magic)?;
34
35        if magic != ANIM_MAGIC {
36            return Err(M2Error::InvalidMagic {
37                expected: String::from_utf8_lossy(&ANIM_MAGIC).to_string(),
38                actual: String::from_utf8_lossy(&magic).to_string(),
39            });
40        }
41
42        // Read other header fields
43        let version = reader.read_u32_le()?;
44        let id_count = reader.read_u32_le()?;
45        let unknown = reader.read_u32_le()?;
46        let anim_entry_offset = reader.read_u32_le()?;
47
48        Ok(Self {
49            magic,
50            version,
51            id_count,
52            unknown,
53            anim_entry_offset,
54        })
55    }
56
57    /// Write an ANIM header to a writer
58    pub fn write<W: Write>(&self, writer: &mut W) -> Result<()> {
59        writer.write_all(&self.magic)?;
60        writer.write_u32_le(self.version)?;
61        writer.write_u32_le(self.id_count)?;
62        writer.write_u32_le(self.unknown)?;
63        writer.write_u32_le(self.anim_entry_offset)?;
64
65        Ok(())
66    }
67}
68
69/// Animation entry header
70#[derive(Debug, Clone)]
71pub struct AnimEntry {
72    /// Animation ID "AFID"
73    pub id: u32,
74    /// Start offset of the animation section
75    pub offset: u32,
76    /// Size of the animation section
77    pub size: u32,
78}
79
80impl AnimEntry {
81    /// Parse an animation entry from a reader
82    pub fn parse<R: Read>(reader: &mut R) -> Result<Self> {
83        let id = reader.read_u32_le()?;
84        let offset = reader.read_u32_le()?;
85        let size = reader.read_u32_le()?;
86
87        Ok(Self { id, offset, size })
88    }
89
90    /// Write an animation entry to a writer
91    pub fn write<W: Write>(&self, writer: &mut W) -> Result<()> {
92        writer.write_u32_le(self.id)?;
93        writer.write_u32_le(self.offset)?;
94        writer.write_u32_le(self.size)?;
95
96        Ok(())
97    }
98}
99
100/// Animation section header "AFID"
101#[derive(Debug, Clone)]
102pub struct AnimSectionHeader {
103    /// "AFID" magic
104    pub magic: [u8; 4],
105    /// Animation ID
106    pub id: u32,
107    /// Start frames for this section
108    pub start: u32,
109    /// End frames for this section
110    pub end: u32,
111}
112
113impl AnimSectionHeader {
114    /// Parse an animation section header from a reader
115    pub fn parse<R: Read>(reader: &mut R) -> Result<Self> {
116        let mut magic = [0u8; 4];
117        reader.read_exact(&mut magic)?;
118
119        if magic != *b"AFID" {
120            return Err(M2Error::InvalidMagic {
121                expected: "AFID".to_string(),
122                actual: String::from_utf8_lossy(&magic).to_string(),
123            });
124        }
125
126        let id = reader.read_u32_le()?;
127        let start = reader.read_u32_le()?;
128        let end = reader.read_u32_le()?;
129
130        Ok(Self {
131            magic,
132            id,
133            start,
134            end,
135        })
136    }
137
138    /// Write an animation section header to a writer
139    pub fn write<W: Write>(&self, writer: &mut W) -> Result<()> {
140        writer.write_all(&self.magic)?;
141        writer.write_u32_le(self.id)?;
142        writer.write_u32_le(self.start)?;
143        writer.write_u32_le(self.end)?;
144
145        Ok(())
146    }
147}
148
149/// Translation data for an animation
150#[derive(Debug, Clone)]
151pub struct AnimTranslation {
152    /// Animation timelines
153    pub timestamps: Vec<u32>,
154    /// Translation vectors
155    pub translations: Vec<C3Vector>,
156}
157
158/// Rotation data for an animation
159#[derive(Debug, Clone)]
160pub struct AnimRotation {
161    /// Animation timelines
162    pub timestamps: Vec<u32>,
163    /// Rotation quaternions
164    pub rotations: Vec<Quaternion>,
165}
166
167/// Scaling data for an animation
168#[derive(Debug, Clone)]
169pub struct AnimScaling {
170    /// Animation timelines
171    pub timestamps: Vec<u32>,
172    /// Scaling vectors
173    pub scalings: Vec<C3Vector>,
174}
175
176/// Animation data for a single bone
177#[derive(Debug, Clone)]
178pub struct AnimBoneAnimation {
179    /// Bone ID
180    pub bone_id: u32,
181    /// Translation animation
182    pub translation: Option<AnimTranslation>,
183    /// Rotation animation
184    pub rotation: Option<AnimRotation>,
185    /// Scaling animation
186    pub scaling: Option<AnimScaling>,
187}
188
189/// Animation data for a section
190#[derive(Debug, Clone)]
191pub struct AnimSection {
192    /// Section header
193    pub header: AnimSectionHeader,
194    /// Animations for each bone
195    pub bone_animations: Vec<AnimBoneAnimation>,
196}
197
198impl AnimSection {
199    /// Parse an animation section from a reader
200    pub fn parse<R: Read>(reader: &mut R, size: u32) -> Result<Self> {
201        let header = AnimSectionHeader::parse(reader)?;
202
203        // Determine the bone count
204        let header_size = 16; // "AFID" + id + start + end
205        let remaining_size = size - header_size;
206        let bone_count = remaining_size / 4; // Each bone animation reference is 4 bytes
207
208        // Read bone animation offsets
209        let mut bone_offsets = Vec::with_capacity(bone_count as usize);
210        for _ in 0..bone_count {
211            bone_offsets.push(reader.read_u32_le()?);
212        }
213
214        // Read bone animations
215        let mut bone_animations = Vec::with_capacity(bone_count as usize);
216
217        for &offset in &bone_offsets {
218            if offset > 0 {
219                // Bone has animation data
220                let bone_id = reader.read_u32_le()?;
221
222                // Read flags
223                let flags = reader.read_u32_le()?;
224
225                // Read translation data if present
226                let translation = if (flags & 0x1) != 0 {
227                    let timestamp_count = reader.read_u32_le()?;
228
229                    let mut timestamps = Vec::with_capacity(timestamp_count as usize);
230                    for _ in 0..timestamp_count {
231                        timestamps.push(reader.read_u32_le()?);
232                    }
233
234                    let mut translations = Vec::with_capacity(timestamp_count as usize);
235                    for _ in 0..timestamp_count {
236                        translations.push(C3Vector::parse(reader)?);
237                    }
238
239                    Some(AnimTranslation {
240                        timestamps,
241                        translations,
242                    })
243                } else {
244                    None
245                };
246
247                // Read rotation data if present
248                let rotation = if (flags & 0x2) != 0 {
249                    let timestamp_count = reader.read_u32_le()?;
250
251                    let mut timestamps = Vec::with_capacity(timestamp_count as usize);
252                    for _ in 0..timestamp_count {
253                        timestamps.push(reader.read_u32_le()?);
254                    }
255
256                    let mut rotations = Vec::with_capacity(timestamp_count as usize);
257                    for _ in 0..timestamp_count {
258                        rotations.push(Quaternion::parse(reader)?);
259                    }
260
261                    Some(AnimRotation {
262                        timestamps,
263                        rotations,
264                    })
265                } else {
266                    None
267                };
268
269                // Read scaling data if present
270                let scaling = if (flags & 0x4) != 0 {
271                    let timestamp_count = reader.read_u32_le()?;
272
273                    let mut timestamps = Vec::with_capacity(timestamp_count as usize);
274                    for _ in 0..timestamp_count {
275                        timestamps.push(reader.read_u32_le()?);
276                    }
277
278                    let mut scalings = Vec::with_capacity(timestamp_count as usize);
279                    for _ in 0..timestamp_count {
280                        scalings.push(C3Vector::parse(reader)?);
281                    }
282
283                    Some(AnimScaling {
284                        timestamps,
285                        scalings,
286                    })
287                } else {
288                    None
289                };
290
291                bone_animations.push(AnimBoneAnimation {
292                    bone_id,
293                    translation,
294                    rotation,
295                    scaling,
296                });
297            } else {
298                // No animation data for this bone
299                bone_animations.push(AnimBoneAnimation {
300                    bone_id: 0,
301                    translation: None,
302                    rotation: None,
303                    scaling: None,
304                });
305            }
306        }
307
308        Ok(Self {
309            header,
310            bone_animations,
311        })
312    }
313
314    /// Write an animation section to a writer
315    pub fn write<W: Write + Seek>(&self, writer: &mut W) -> Result<()> {
316        // Write section header
317        self.header.write(writer)?;
318
319        // Write bone animation offsets (placeholders for now)
320        let bone_offsets_pos = writer.stream_position()?;
321
322        for _ in 0..self.bone_animations.len() {
323            writer.write_u32_le(0)?; // Placeholder
324        }
325
326        // Write bone animations and update offsets
327        let mut bone_offsets = Vec::with_capacity(self.bone_animations.len());
328
329        for bone_animation in &self.bone_animations {
330            if bone_animation.translation.is_some()
331                || bone_animation.rotation.is_some()
332                || bone_animation.scaling.is_some()
333            {
334                // Bone has animation data
335                let offset = writer.stream_position()? as u32;
336                bone_offsets.push(offset);
337
338                // Write bone ID
339                writer.write_u32_le(bone_animation.bone_id)?;
340
341                // Determine flags
342                let mut flags = 0u32;
343                if bone_animation.translation.is_some() {
344                    flags |= 0x1;
345                }
346                if bone_animation.rotation.is_some() {
347                    flags |= 0x2;
348                }
349                if bone_animation.scaling.is_some() {
350                    flags |= 0x4;
351                }
352
353                // Write flags
354                writer.write_u32_le(flags)?;
355
356                // Write translation data if present
357                if let Some(ref translation) = bone_animation.translation {
358                    writer.write_u32_le(translation.timestamps.len() as u32)?;
359
360                    for &timestamp in &translation.timestamps {
361                        writer.write_u32_le(timestamp)?;
362                    }
363
364                    for translation in &translation.translations {
365                        translation.write(writer)?;
366                    }
367                }
368
369                // Write rotation data if present
370                if let Some(ref rotation) = bone_animation.rotation {
371                    writer.write_u32_le(rotation.timestamps.len() as u32)?;
372
373                    for &timestamp in &rotation.timestamps {
374                        writer.write_u32_le(timestamp)?;
375                    }
376
377                    for rotation in &rotation.rotations {
378                        rotation.write(writer)?;
379                    }
380                }
381
382                // Write scaling data if present
383                if let Some(ref scaling) = bone_animation.scaling {
384                    writer.write_u32_le(scaling.timestamps.len() as u32)?;
385
386                    for &timestamp in &scaling.timestamps {
387                        writer.write_u32_le(timestamp)?;
388                    }
389
390                    for scaling in &scaling.scalings {
391                        scaling.write(writer)?;
392                    }
393                }
394            } else {
395                // No animation data for this bone
396                bone_offsets.push(0);
397            }
398        }
399
400        // Update bone offsets
401        let current_pos = writer.stream_position()?;
402        writer.seek(SeekFrom::Start(bone_offsets_pos))?;
403
404        for &offset in &bone_offsets {
405            writer.write_u32_le(offset)?;
406        }
407
408        // Restore position
409        writer.seek(SeekFrom::Start(current_pos))?;
410
411        Ok(())
412    }
413}
414
415/// Represents an ANIM file containing animation data
416#[derive(Debug, Clone)]
417pub struct AnimFile {
418    /// ANIM file header
419    pub header: AnimHeader,
420    /// Animation entries
421    pub entries: Vec<AnimEntry>,
422    /// Animation sections
423    pub sections: Vec<AnimSection>,
424}
425
426impl AnimFile {
427    /// Parse an ANIM file from a reader
428    pub fn parse<R: Read + Seek>(reader: &mut R) -> Result<Self> {
429        // Parse header
430        let header = AnimHeader::parse(reader)?;
431
432        // Parse animation entries
433        reader.seek(SeekFrom::Start(header.anim_entry_offset as u64))?;
434
435        let mut entries = Vec::with_capacity(header.id_count as usize);
436        for _ in 0..header.id_count {
437            entries.push(AnimEntry::parse(reader)?);
438        }
439
440        // Parse animation sections
441        let mut sections = Vec::with_capacity(entries.len());
442
443        for entry in &entries {
444            reader.seek(SeekFrom::Start(entry.offset as u64))?;
445            sections.push(AnimSection::parse(reader, entry.size)?);
446        }
447
448        Ok(Self {
449            header,
450            entries,
451            sections,
452        })
453    }
454
455    /// Load an ANIM file from a file
456    pub fn load<P: AsRef<Path>>(path: P) -> Result<Self> {
457        let mut file = File::open(path)?;
458        Self::parse(&mut file)
459    }
460
461    /// Save an ANIM file to a file
462    pub fn save<P: AsRef<Path>>(&self, path: P) -> Result<()> {
463        let mut file = File::create(path)?;
464        self.write(&mut file)
465    }
466
467    /// Write an ANIM file to a writer
468    pub fn write<W: Write + Seek>(&self, writer: &mut W) -> Result<()> {
469        // Calculate offsets
470        let header_size = 20; // Magic + version + id count + unknown + entry offset
471        let entry_size = 12; // ID + offset + size
472
473        let entry_offset = header_size;
474        let _section_offset = entry_offset + self.entries.len() as u32 * entry_size;
475
476        // Write header
477        let mut header = self.header.clone();
478        header.anim_entry_offset = entry_offset;
479        header.write(writer)?;
480
481        // Write entry placeholders
482        let mut entries = Vec::with_capacity(self.entries.len());
483
484        for i in 0..self.entries.len() {
485            let entry = AnimEntry {
486                id: self.entries[i].id,
487                offset: 0, // Placeholder
488                size: 0,   // Placeholder
489            };
490
491            entry.write(writer)?;
492            entries.push(entry);
493        }
494
495        // Write sections and update entries
496        for (i, section) in self.sections.iter().enumerate() {
497            let section_start = writer.stream_position()? as u32;
498            section.write(writer)?;
499            let section_end = writer.stream_position()? as u32;
500
501            entries[i].offset = section_start;
502            entries[i].size = section_end - section_start;
503        }
504
505        // Update entries
506        writer.seek(SeekFrom::Start(entry_offset as u64))?;
507
508        for entry in &entries {
509            entry.write(writer)?;
510        }
511
512        Ok(())
513    }
514
515    /// Convert this ANIM file to a different version (no version differences for ANIM files yet)
516    pub fn convert(&self, _target_version: M2Version) -> Self {
517        self.clone()
518    }
519}
520
521#[cfg(test)]
522mod tests {
523    use super::*;
524    use std::io::Cursor;
525
526    #[test]
527    fn test_anim_header_parse_write() {
528        let header = AnimHeader {
529            magic: ANIM_MAGIC,
530            version: 1,
531            id_count: 2,
532            unknown: 0,
533            anim_entry_offset: 20,
534        };
535
536        let mut data = Vec::new();
537        header.write(&mut data).unwrap();
538
539        let mut cursor = Cursor::new(data);
540        let parsed_header = AnimHeader::parse(&mut cursor).unwrap();
541
542        assert_eq!(parsed_header.magic, ANIM_MAGIC);
543        assert_eq!(parsed_header.version, 1);
544        assert_eq!(parsed_header.id_count, 2);
545        assert_eq!(parsed_header.unknown, 0);
546        assert_eq!(parsed_header.anim_entry_offset, 20);
547    }
548
549    #[test]
550    fn test_anim_entry_parse_write() {
551        let entry = AnimEntry {
552            id: 1,
553            offset: 100,
554            size: 200,
555        };
556
557        let mut data = Vec::new();
558        entry.write(&mut data).unwrap();
559
560        let mut cursor = Cursor::new(data);
561        let parsed_entry = AnimEntry::parse(&mut cursor).unwrap();
562
563        assert_eq!(parsed_entry.id, 1);
564        assert_eq!(parsed_entry.offset, 100);
565        assert_eq!(parsed_entry.size, 200);
566    }
567}