1use bitflags::bitflags;
85use getset::*;
86use serde_derive::{Serialize, Deserialize};
87
88use std::collections::{BTreeMap, HashMap};
89use std::io::Cursor;
90
91use crate::binary::{ReadBytes, WriteBytes};
92use crate::error::{RLibError, Result};
93use crate::files::{DecodeableExtraData, Decodeable, EncodeableExtraData, Encodeable, table::{DecodedData, local::TableInMemory, Table}};
94use crate::games::supported_games::{KEY_PHARAOH, KEY_PHARAOH_DYNASTIES, KEY_THREE_KINGDOMS, KEY_TROY, KEY_WARHAMMER_2};
95use crate::schema::*;
96use crate::utils::check_size_mismatch;
97
98pub const BASE_PATH: &str = "animations/";
100
101pub const MID_PATH: &str = "/battle/";
103
104pub const EXTENSION_NEW: &str = ".bin";
106
107pub const EXTENSION_OLD: &str = ".frg";
109
110mod versions;
111
112#[cfg(test)] mod anim_fragment_battle_test;
113
114#[derive(PartialEq, Clone, Debug, Default, Getters, MutGetters, Setters, Serialize, Deserialize)]
129#[getset(get = "pub", get_mut = "pub", set = "pub")]
130pub struct AnimFragmentBattle {
131 version: u32,
135
136 entries: Vec<Entry>,
138
139 skeleton_name: String,
141
142 subversion: u32,
146
147 table_name: String,
151
152 mount_table_name: String,
154
155 unmount_table_name: String,
157
158 locomotion_graph: String,
160
161 is_simple_flight: bool,
163
164 is_new_cavalry_tech: bool,
166
167 min_id: u32,
171
172 max_id: u32,
174}
175
176#[derive(PartialEq, Clone, Debug, Default, Getters, MutGetters, Setters, Serialize, Deserialize)]
186#[getset(get = "pub", get_mut = "pub", set = "pub")]
187pub struct Entry {
188 animation_id: u32,
192
193 blend_in_time: f32,
195
196 selection_weight: f32,
198
199 weapon_bone: WeaponBone,
201
202 anim_refs: Vec<AnimRef>,
208
209 slot_id: u32,
213
214 filename: String,
216
217 metadata: String,
219
220 metadata_sound: String,
222
223 skeleton_type: String,
225
226 uk_3: u32,
228
229 uk_4: String,
231
232 single_frame_variant: bool,
236}
237
238#[derive(PartialEq, Clone, Debug, Default, Getters, MutGetters, Setters, Serialize, Deserialize)]
245#[getset(get = "pub", get_mut = "pub", set = "pub")]
246pub struct AnimRef {
247 file_path: String,
249
250 meta_file_path: String,
252
253 snd_file_path: String,
255}
256
257bitflags! {
258 #[derive(PartialEq, Clone, Copy, Debug, Default, Serialize, Deserialize)]
276 pub struct WeaponBone: u32 {
277 const WEAPON_BONE_1 = 0b0000_0000_0000_0001;
279
280 const WEAPON_BONE_2 = 0b0000_0000_0000_0010;
282
283 const WEAPON_BONE_3 = 0b0000_0000_0000_0100;
285
286 const WEAPON_BONE_4 = 0b0000_0000_0000_1000;
288
289 const WEAPON_BONE_5 = 0b0000_0000_0001_0000;
291
292 const WEAPON_BONE_6 = 0b0000_0000_0010_0000;
294 }
295}
296
297impl AnimFragmentBattle {
302
303 pub fn definitions() -> (Definition, Definition) {
318 let mut anim_refs_definition = Definition::default();
319 anim_refs_definition.fields_mut().push(Field::new("file_path".to_string(), FieldType::StringU8, false, None, false, None, None, None, String::new(), -1, 0, BTreeMap::new(), None));
320 anim_refs_definition.fields_mut().push(Field::new("meta_file_path".to_string(), FieldType::StringU8, false, None, false, None, None, None, String::new(), -1, 0, BTreeMap::new(), None));
321 anim_refs_definition.fields_mut().push(Field::new("snd_file_path".to_string(), FieldType::StringU8, false, None, false, None, None, None, String::new(), -1, 0, BTreeMap::new(), None));
322
323 let mut definition = Definition::default();
324 definition.fields_mut().push(Field::new("animation_id".to_string(), FieldType::I32, true, None, false, None, None, None, String::new(), -1, 0, BTreeMap::new(), None));
325 definition.fields_mut().push(Field::new("blend_in_time".to_string(), FieldType::F32, false, None, false, None, None, None, String::new(), -1, 0, BTreeMap::new(), None));
326 definition.fields_mut().push(Field::new("selection_weight".to_string(), FieldType::F32, false, None, false, None, None, None, String::new(), -1, 0, BTreeMap::new(), None));
327 definition.fields_mut().push(Field::new("weapon_bone".to_string(), FieldType::I32, false, None, false, None, None, None, String::new(), -1, 6, BTreeMap::new(), None));
328 definition.fields_mut().push(Field::new("anim_refs".to_string(), FieldType::SequenceU32(Box::new(anim_refs_definition.clone())), false, None, false, None, None, None, String::new(), -1, 0, BTreeMap::new(), None));
329 definition.fields_mut().push(Field::new("slot_id".to_string(), FieldType::I32, true, None, false, None, None, None, String::new(), -1, 0, BTreeMap::new(), None));
330 definition.fields_mut().push(Field::new("filename".to_string(), FieldType::StringU8, false, None, false, None, None, None, String::new(), -1, 0, BTreeMap::new(), None));
331 definition.fields_mut().push(Field::new("metadata".to_string(), FieldType::StringU8, false, None, false, None, None, None, String::new(), -1, 0, BTreeMap::new(), None));
332 definition.fields_mut().push(Field::new("metadata_sound".to_string(), FieldType::StringU8, false, None, false, None, None, None, String::new(), -1, 0, BTreeMap::new(), None));
333 definition.fields_mut().push(Field::new("skeleton_type".to_string(), FieldType::StringU8, false, None, false, None, None, None, String::new(), -1, 0, BTreeMap::new(), None));
334 definition.fields_mut().push(Field::new("uk_3".to_string(), FieldType::I32, false, None, false, None, None, None, String::new(), -1, 0, BTreeMap::new(), None));
335 definition.fields_mut().push(Field::new("uk_4".to_string(), FieldType::StringU8, false, None, false, None, None, None, String::new(), -1, 0, BTreeMap::new(), None));
336 definition.fields_mut().push(Field::new("single_frame_variant".to_string(), FieldType::Boolean, false, None, false, None, None, None, String::new(), -1, 0, BTreeMap::new(), None));
337
338 (definition, anim_refs_definition)
339 }
340
341 pub fn from_table(table: &TableInMemory) -> Result<Vec<Entry>> {
361 let mut entries = vec![];
362
363 let definition = table.definition();
364 let fields_processed = definition.fields_processed();
365
366 for row in table.data().iter() {
367 let mut entry = Entry::default();
368
369 if let DecodedData::I32(data) = row[0] {
370 entry.set_animation_id(data as u32);
371 }
372
373 if let DecodedData::F32(data) = row[1] {
374 entry.set_blend_in_time(data);
375 }
376
377 if let DecodedData::F32(data) = row[2] {
378 entry.set_selection_weight(data);
379 }
380
381 if let DecodedData::Boolean(data_1) = row[3] {
382 if let DecodedData::Boolean(data_2) = row[4] {
383 if let DecodedData::Boolean(data_3) = row[5] {
384 if let DecodedData::Boolean(data_4) = row[6] {
385 if let DecodedData::Boolean(data_5) = row[7] {
386 if let DecodedData::Boolean(data_6) = row[8] {
387 let mut bits = WeaponBone::empty();
388
389 if data_1 { bits |= WeaponBone::WEAPON_BONE_1; }
390 if data_2 { bits |= WeaponBone::WEAPON_BONE_2; }
391 if data_3 { bits |= WeaponBone::WEAPON_BONE_3; }
392 if data_4 { bits |= WeaponBone::WEAPON_BONE_4; }
393 if data_5 { bits |= WeaponBone::WEAPON_BONE_5; }
394 if data_6 { bits |= WeaponBone::WEAPON_BONE_6; }
395
396 entry.set_weapon_bone(bits);
397 }
398 }
399 }
400 }
401 }
402 }
403
404 if let DecodedData::SequenceU32(ref data) = row[9] {
405 if let FieldType::SequenceU32(ref definition) = fields_processed[9].field_type() {
406 let mut data = Cursor::new(data);
407 let data = TableInMemory::decode(&mut data, definition, &HashMap::new(), None, false, fields_processed[9].name())?;
408 let mut entries = vec![];
409
410 for row in data.data().iter() {
411 let mut entry = AnimRef::default();
412
413 if let DecodedData::StringU8(ref data) = row[0] {
414 entry.set_file_path(data.to_string());
415 }
416
417 if let DecodedData::StringU8(ref data) = row[1] {
418 entry.set_meta_file_path(data.to_string());
419 }
420
421 if let DecodedData::StringU8(ref data) = row[2] {
422 entry.set_snd_file_path(data.to_string());
423 }
424
425 entries.push(entry);
426 }
427
428 entry.set_anim_refs(entries);
429 }
430 }
431
432 if let DecodedData::I32(data) = row[10] {
433 entry.set_slot_id(data as u32);
434 }
435
436 if let DecodedData::StringU8(ref data) = row[11] {
437 entry.set_filename(data.to_string());
438 }
439
440 if let DecodedData::StringU8(ref data) = row[12] {
441 entry.set_metadata(data.to_string());
442 }
443
444 if let DecodedData::StringU8(ref data) = row[13] {
445 entry.set_metadata_sound(data.to_string());
446 }
447
448 if let DecodedData::StringU8(ref data) = row[14] {
449 entry.set_skeleton_type(data.to_string());
450 }
451
452 if let DecodedData::I32(data) = row[15] {
453 entry.set_uk_3(data as u32);
454 }
455
456 if let DecodedData::StringU8(ref data) = row[16] {
457 entry.set_uk_4(data.to_string());
458 }
459
460 if let DecodedData::Boolean(data) = row[17] {
461 entry.set_single_frame_variant(data);
462 }
463
464 entries.push(entry);
465 }
466
467 Ok(entries)
468 }
469
470 pub fn to_table(&self) -> Result<TableInMemory> {
485 let (definition, anim_refs_definition) = Self::definitions();
486 let mut table = TableInMemory::new(&definition, None, "");
487
488 let data = self.entries()
489 .iter()
490 .map(|entry| {
491 let mut row = Vec::with_capacity(19);
492 row.push(DecodedData::I32(*entry.animation_id() as i32));
493 row.push(DecodedData::F32(*entry.blend_in_time()));
494 row.push(DecodedData::F32(*entry.selection_weight()));
495 row.push(DecodedData::Boolean(entry.weapon_bone().contains(WeaponBone::WEAPON_BONE_1)));
496 row.push(DecodedData::Boolean(entry.weapon_bone().contains(WeaponBone::WEAPON_BONE_2)));
497 row.push(DecodedData::Boolean(entry.weapon_bone().contains(WeaponBone::WEAPON_BONE_3)));
498 row.push(DecodedData::Boolean(entry.weapon_bone().contains(WeaponBone::WEAPON_BONE_4)));
499 row.push(DecodedData::Boolean(entry.weapon_bone().contains(WeaponBone::WEAPON_BONE_5)));
500 row.push(DecodedData::Boolean(entry.weapon_bone().contains(WeaponBone::WEAPON_BONE_6)));
501
502 let mut anim_refs_subtable = TableInMemory::new(&anim_refs_definition, None, "anim_refs");
503 let mut anim_ref_rows = Vec::with_capacity(entry.anim_refs().len());
504 for anim_ref in entry.anim_refs() {
505 let anim_ref_row = vec![
506 DecodedData::StringU8(anim_ref.file_path().to_string()),
507 DecodedData::StringU8(anim_ref.meta_file_path().to_string()),
508 DecodedData::StringU8(anim_ref.snd_file_path().to_string()),
509 ];
510 anim_ref_rows.push(anim_ref_row)
511 }
512 anim_refs_subtable.set_data(&anim_ref_rows).unwrap();
513 let mut writer = vec![];
514 writer.write_u32(anim_ref_rows.len() as u32).unwrap();
515 let _ = anim_refs_subtable.encode(&mut writer);
516
517 row.push(DecodedData::SequenceU32(writer));
518
519 row.push(DecodedData::I32(*entry.slot_id() as i32));
520 row.push(DecodedData::StringU8(entry.filename().to_string()));
521 row.push(DecodedData::StringU8(entry.metadata().to_string()));
522 row.push(DecodedData::StringU8(entry.metadata_sound().to_string()));
523 row.push(DecodedData::StringU8(entry.skeleton_type().to_string()));
524 row.push(DecodedData::I32(*entry.uk_3() as i32));
525 row.push(DecodedData::StringU8(entry.uk_4().to_string()));
526 row.push(DecodedData::Boolean(*entry.single_frame_variant()));
527
528 row
529 }).collect::<Vec<_>>();
530
531 table.set_data(&data)?;
532 Ok(table)
533 }
534}
535
536impl Decodeable for AnimFragmentBattle {
537
538 fn decode<R: ReadBytes>(data: &mut R, extra_data: &Option<DecodeableExtraData>) -> Result<Self> {
539 let extra_data = extra_data.as_ref().ok_or(RLibError::DecodingMissingExtraData)?;
540 let game_info = extra_data.game_info.ok_or_else(|| RLibError::DecodingMissingExtraDataField("game_info".to_owned()))?;
541
542 let version = data.read_u32()?;
543
544 let mut fragment = Self::default();
545 fragment.version = version;
546
547 match version {
548 2 => match game_info.key() {
549 KEY_WARHAMMER_2 | KEY_TROY | KEY_PHARAOH | KEY_PHARAOH_DYNASTIES => fragment.read_v2_wh2(data)?,
550 KEY_THREE_KINGDOMS => fragment.read_v2_3k(data)?,
551 _ => Err(RLibError::DecodingMatchedCombatUnsupportedVersion(fragment.version as usize))?,
552 },
553 4 => fragment.read_v4(data)?,
554 _ => Err(RLibError::DecodingAnimFragmentUnsupportedVersion(version as usize))?,
555 }
556
557 check_size_mismatch(data.stream_position()? as usize, data.len()? as usize)?;
559
560 Ok(fragment)
561 }
562}
563
564impl Encodeable for AnimFragmentBattle {
565
566 fn encode<W: WriteBytes>(&mut self, buffer: &mut W, extra_data: &Option<EncodeableExtraData>) -> Result<()> {
567 let extra_data = extra_data.as_ref().ok_or(RLibError::DecodingMissingExtraData)?;
568 let game_info = extra_data.game_info.ok_or_else(|| RLibError::DecodingMissingExtraDataField("game_info".to_owned()))?;
569
570 buffer.write_u32(self.version)?;
571
572 match self.version {
573 2 => match game_info.key() {
574 KEY_WARHAMMER_2 | KEY_TROY | KEY_PHARAOH | KEY_PHARAOH_DYNASTIES => self.write_v2_wh2(buffer)?,
575 KEY_THREE_KINGDOMS => self.write_v2_3k(buffer)?,
576 _ => Err(RLibError::DecodingMatchedCombatUnsupportedVersion(self.version as usize))?,
577 },
578 4 => self.write_v4(buffer)?,
579 _ => Err(RLibError::DecodingAnimFragmentUnsupportedVersion(self.version as usize))?,
580 };
581
582 Ok(())
583 }
584}