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
10pub const ANIM_MAGIC: [u8; 4] = *b"MAOF";
12
13#[derive(Debug, Clone)]
15pub struct AnimHeader {
16 pub magic: [u8; 4],
18 pub version: u32,
20 pub id_count: u32,
22 pub unknown: u32,
24 pub anim_entry_offset: u32,
26}
27
28impl AnimHeader {
29 pub fn parse<R: Read + Seek>(reader: &mut R) -> Result<Self> {
31 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 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 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#[derive(Debug, Clone)]
71pub struct AnimEntry {
72 pub id: u32,
74 pub offset: u32,
76 pub size: u32,
78}
79
80impl AnimEntry {
81 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 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#[derive(Debug, Clone)]
102pub struct AnimSectionHeader {
103 pub magic: [u8; 4],
105 pub id: u32,
107 pub start: u32,
109 pub end: u32,
111}
112
113impl AnimSectionHeader {
114 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 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#[derive(Debug, Clone)]
151pub struct AnimTranslation {
152 pub timestamps: Vec<u32>,
154 pub translations: Vec<C3Vector>,
156}
157
158#[derive(Debug, Clone)]
160pub struct AnimRotation {
161 pub timestamps: Vec<u32>,
163 pub rotations: Vec<Quaternion>,
165}
166
167#[derive(Debug, Clone)]
169pub struct AnimScaling {
170 pub timestamps: Vec<u32>,
172 pub scalings: Vec<C3Vector>,
174}
175
176#[derive(Debug, Clone)]
178pub struct AnimBoneAnimation {
179 pub bone_id: u32,
181 pub translation: Option<AnimTranslation>,
183 pub rotation: Option<AnimRotation>,
185 pub scaling: Option<AnimScaling>,
187}
188
189#[derive(Debug, Clone)]
191pub struct AnimSection {
192 pub header: AnimSectionHeader,
194 pub bone_animations: Vec<AnimBoneAnimation>,
196}
197
198impl AnimSection {
199 pub fn parse<R: Read>(reader: &mut R, size: u32) -> Result<Self> {
201 let header = AnimSectionHeader::parse(reader)?;
202
203 let header_size = 16; let remaining_size = size - header_size;
206 let bone_count = remaining_size / 4; 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 let mut bone_animations = Vec::with_capacity(bone_count as usize);
216
217 for &offset in &bone_offsets {
218 if offset > 0 {
219 let bone_id = reader.read_u32_le()?;
221
222 let flags = reader.read_u32_le()?;
224
225 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 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 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 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 pub fn write<W: Write + Seek>(&self, writer: &mut W) -> Result<()> {
316 self.header.write(writer)?;
318
319 let bone_offsets_pos = writer.stream_position()?;
321
322 for _ in 0..self.bone_animations.len() {
323 writer.write_u32_le(0)?; }
325
326 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 let offset = writer.stream_position()? as u32;
336 bone_offsets.push(offset);
337
338 writer.write_u32_le(bone_animation.bone_id)?;
340
341 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 writer.write_u32_le(flags)?;
355
356 if let Some(ref translation) = bone_animation.translation {
358 writer.write_u32_le(translation.timestamps.len() as u32)?;
359
360 for ×tamp 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 if let Some(ref rotation) = bone_animation.rotation {
371 writer.write_u32_le(rotation.timestamps.len() as u32)?;
372
373 for ×tamp 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 if let Some(ref scaling) = bone_animation.scaling {
384 writer.write_u32_le(scaling.timestamps.len() as u32)?;
385
386 for ×tamp 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 bone_offsets.push(0);
397 }
398 }
399
400 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 writer.seek(SeekFrom::Start(current_pos))?;
410
411 Ok(())
412 }
413}
414
415#[derive(Debug, Clone)]
417pub struct AnimFile {
418 pub header: AnimHeader,
420 pub entries: Vec<AnimEntry>,
422 pub sections: Vec<AnimSection>,
424}
425
426impl AnimFile {
427 pub fn parse<R: Read + Seek>(reader: &mut R) -> Result<Self> {
429 let header = AnimHeader::parse(reader)?;
431
432 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 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 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 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 pub fn write<W: Write + Seek>(&self, writer: &mut W) -> Result<()> {
469 let header_size = 20; let entry_size = 12; let entry_offset = header_size;
474 let _section_offset = entry_offset + self.entries.len() as u32 * entry_size;
475
476 let mut header = self.header.clone();
478 header.anim_entry_offset = entry_offset;
479 header.write(writer)?;
480
481 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, size: 0, };
490
491 entry.write(writer)?;
492 entries.push(entry);
493 }
494
495 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 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 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}