#![allow(unused)]
#![allow(clippy::needless_late_init)]
#![allow(clippy::upper_case_acronyms)]
use binrw::helpers::until_eof;
use binrw::{BinRead, binread};
use std::io::{Cursor, SeekFrom};
use crate::common::Platform;
use crate::havok::{HavokAnimationContainer, HavokBinaryTagFileReader};
use crate::race::{Gender, Race, Tribe, get_race_id};
use crate::{ByteSpan, ReadableFile};
#[binread]
struct SklbV1 {
unk_offset: u16,
havok_offset: u16,
body_id: u32,
mapper_body_id1: u32,
mapper_body_id2: u32,
mapper_body_id3: u32,
}
#[binread]
struct SklbV2 {
unk_offset: u32,
havok_offset: u32,
unk: u32,
body_id: u32,
mapper_body_id1: u32,
mapper_body_id2: u32,
mapper_body_id3: u32,
}
#[binread]
#[br(magic = 0x736B6C62i32)]
struct SKLB {
version: u32,
#[br(if(version == 0x3132_3030u32))]
sklb_v1: Option<SklbV1>,
#[br(if(version == 0x3133_3030u32 || version == 0x3133_3031u32))]
sklb_v2: Option<SklbV2>,
#[br(seek_before(SeekFrom::Start(if (version == 0x3132_3030u32) { sklb_v1.as_ref().unwrap().havok_offset as u64 } else { sklb_v2.as_ref().unwrap().havok_offset as u64 })))]
#[br(parse_with = until_eof)]
raw_data: Vec<u8>,
}
#[derive(Debug)]
pub struct Bone {
pub name: String,
pub parent_index: i32,
pub position: [f32; 3],
pub rotation: [f32; 4],
pub scale: [f32; 3],
}
#[derive(Debug)]
pub struct Skeleton {
pub bones: Vec<Bone>,
}
impl ReadableFile for Skeleton {
fn from_existing(platform: Platform, buffer: ByteSpan) -> Option<Skeleton> {
let mut cursor = Cursor::new(buffer);
let sklb = SKLB::read_options(&mut cursor, platform.endianness(), ()).ok()?;
let root = HavokBinaryTagFileReader::read(&sklb.raw_data);
let raw_animation_container = root.find_object_by_type("hkaAnimationContainer");
let animation_container = HavokAnimationContainer::new(raw_animation_container);
let havok_skeleton = &animation_container.skeletons[0];
let mut skeleton = Skeleton { bones: vec![] };
for (index, bone) in havok_skeleton.bone_names.iter().enumerate() {
skeleton.bones.push(Bone {
name: bone.clone(),
parent_index: havok_skeleton.parent_indices[index] as i32,
position: [
havok_skeleton.reference_pose[index].translation[0],
havok_skeleton.reference_pose[index].translation[1],
havok_skeleton.reference_pose[index].translation[2],
],
rotation: havok_skeleton.reference_pose[index].rotation,
scale: [
havok_skeleton.reference_pose[index].scale[0],
havok_skeleton.reference_pose[index].scale[1],
havok_skeleton.reference_pose[index].scale[2],
],
});
}
Some(skeleton)
}
}
impl Skeleton {
pub fn path(race: Race, tribe: Tribe, gender: Gender) -> String {
let race_id = get_race_id(race, tribe, gender).unwrap();
format!("chara/human/c{race_id:04}/skeleton/base/b0001/skl_c{race_id:04}b0001.sklb")
}
}
#[cfg(test)]
mod tests {
use crate::pass_random_invalid;
use super::*;
#[test]
fn test_invalid() {
pass_random_invalid::<Skeleton>();
}
}