1#![allow(unused)]
5#![allow(clippy::needless_late_init)]
6#![allow(clippy::upper_case_acronyms)]
7
8use binrw::helpers::until_eof;
9use binrw::{BinRead, binread};
10use std::io::{Cursor, SeekFrom};
11
12use crate::ByteSpan;
13use crate::havok::{HavokAnimationContainer, HavokBinaryTagFileReader};
14
15#[binread]
16#[br(little)]
17struct SklbV1 {
18 unk_offset: u16,
19 havok_offset: u16,
20 body_id: u32,
21 mapper_body_id1: u32,
22 mapper_body_id2: u32,
23 mapper_body_id3: u32,
24}
25
26#[binread]
27#[br(little)]
28struct SklbV2 {
29 unk_offset: u32,
30 havok_offset: u32,
31 unk: u32,
32 body_id: u32,
33 mapper_body_id1: u32,
34 mapper_body_id2: u32,
35 mapper_body_id3: u32,
36}
37
38#[binread]
39#[br(magic = 0x736B6C62i32)]
40#[br(little)]
41struct SKLB {
42 version: u32,
43
44 #[br(if(version == 0x3132_3030u32))]
45 sklb_v1: Option<SklbV1>,
46
47 #[br(if(version == 0x3133_3030u32 || version == 0x3133_3031u32))]
48 sklb_v2: Option<SklbV2>,
49
50 #[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 })))]
51 #[br(parse_with = until_eof)]
52 raw_data: Vec<u8>,
53}
54
55#[derive(Debug)]
56pub struct Bone {
57 pub name: String,
59 pub parent_index: i32,
61
62 pub position: [f32; 3],
64 pub rotation: [f32; 4],
66 pub scale: [f32; 3],
68}
69
70#[derive(Debug)]
71pub struct Skeleton {
72 pub bones: Vec<Bone>,
74}
75
76impl Skeleton {
77 pub fn from_existing(buffer: ByteSpan) -> Option<Skeleton> {
79 let mut cursor = Cursor::new(buffer);
80
81 let sklb = SKLB::read(&mut cursor).ok()?;
82
83 let root = HavokBinaryTagFileReader::read(&sklb.raw_data);
84 let raw_animation_container = root.find_object_by_type("hkaAnimationContainer");
85 let animation_container = HavokAnimationContainer::new(raw_animation_container);
86
87 let havok_skeleton = &animation_container.skeletons[0];
88
89 let mut skeleton = Skeleton { bones: vec![] };
90
91 for (index, bone) in havok_skeleton.bone_names.iter().enumerate() {
92 skeleton.bones.push(Bone {
93 name: bone.clone(),
94 parent_index: havok_skeleton.parent_indices[index] as i32,
95 position: [
96 havok_skeleton.reference_pose[index].translation[0],
97 havok_skeleton.reference_pose[index].translation[1],
98 havok_skeleton.reference_pose[index].translation[2],
99 ],
100 rotation: havok_skeleton.reference_pose[index].rotation,
101 scale: [
102 havok_skeleton.reference_pose[index].scale[0],
103 havok_skeleton.reference_pose[index].scale[1],
104 havok_skeleton.reference_pose[index].scale[2],
105 ],
106 });
107 }
108
109 Some(skeleton)
110 }
111}
112
113#[cfg(test)]
114mod tests {
115 use std::fs::read;
116 use std::path::PathBuf;
117
118 use super::*;
119
120 #[test]
121 fn test_invalid() {
122 let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
123 d.push("resources/tests");
124 d.push("random");
125
126 Skeleton::from_existing(&read(d).unwrap());
128 }
129}