1use std::io::{Cursor, SeekFrom};
5
6use crate::ByteSpan;
7use crate::common_file_operations::strings_parser;
8use binrw::BinRead;
9use binrw::binread;
10
11#[binread]
12#[derive(Debug)]
13#[br(import { data_offset: i32 })]
14#[brw(little)]
15#[allow(unused)]
16struct RacialDeformer {
17 bone_count: i32,
18
19 #[br(count = bone_count)]
20 bone_name_offsets: Vec<u16>,
21
22 #[br(args(data_offset as u64, &bone_name_offsets), parse_with = strings_parser)]
23 #[br(restore_position)]
24 bone_names: Vec<String>,
25
26 #[br(if((bone_count & 1) != 0))]
27 #[br(temp)]
28 _padding: u16,
29
30 #[br(count = bone_count)]
32 #[br(err_context("offset = {} bone count = {}", data_offset, bone_count))]
33 transform: Vec<[f32; 12]>,
34}
35
36#[binread]
37#[derive(Debug)]
38#[brw(little)]
39struct PreBoneDeformerItem {
40 body_id: u16, link_index: i16,
42 #[br(pad_after = 4)]
43 #[br(temp)]
44 data_offset: i32,
45
46 #[br(args { data_offset: data_offset })]
47 #[br(seek_before = SeekFrom::Start(data_offset as u64))]
48 #[br(restore_position)]
49 deformer: RacialDeformer,
50}
51
52#[binread]
53#[derive(Debug)]
54#[brw(little)]
55#[allow(dead_code)]
56struct PreBoneDeformerLink {
57 parent_index: i16,
58 first_child_index: i16,
59 next_sibling_index: i16,
60 deformer_index: u16,
61}
62
63#[binread]
64#[derive(Debug)]
65#[brw(little)]
66#[allow(dead_code)]
67struct PreBoneDeformerHeader {
68 count: i32,
69
70 #[br(count = count)]
71 items: Vec<PreBoneDeformerItem>,
72
73 #[br(count = count)]
74 links: Vec<PreBoneDeformerLink>,
75}
76
77pub struct PreBoneDeformer {
78 header: PreBoneDeformerHeader,
79}
80
81#[derive(Debug)]
82pub struct PreBoneDeformBone {
83 pub name: String,
85 pub deform: [f32; 12],
87}
88
89#[derive(Debug)]
90pub struct PreBoneDeformMatrices {
91 pub bones: Vec<PreBoneDeformBone>,
93}
94
95impl PreBoneDeformer {
96 pub fn from_existing(buffer: ByteSpan) -> Option<PreBoneDeformer> {
98 let mut cursor = Cursor::new(buffer);
99 let header = PreBoneDeformerHeader::read(&mut cursor).ok()?;
100
101 Some(PreBoneDeformer { header })
102 }
103
104 pub fn get_deform_matrices(
106 &self,
107 from_body_id: u16,
108 to_body_id: u16,
109 ) -> Option<PreBoneDeformMatrices> {
110 if from_body_id == to_body_id {
111 return None;
112 }
113
114 let mut item = self
115 .header
116 .items
117 .iter()
118 .find(|x| x.body_id == from_body_id)?;
119 let mut next = &self.header.links[item.link_index as usize];
120
121 if next.next_sibling_index == -1 {
122 return None;
123 }
124
125 let mut bones = vec![];
126
127 loop {
128 for i in 0..item.deformer.bone_count {
129 bones.push(PreBoneDeformBone {
130 name: item.deformer.bone_names[i as usize].clone(),
131 deform: item.deformer.transform[i as usize],
132 })
133 }
134
135 if next.parent_index == -1 {
136 break;
137 }
138
139 next = &self.header.links[next.parent_index as usize];
140 item = &self.header.items[next.deformer_index as usize];
141
142 if item.body_id == to_body_id {
143 break;
144 }
145 }
146
147 Some(PreBoneDeformMatrices { bones })
148 }
149}
150
151#[cfg(test)]
152mod tests {
153 use std::fs::read;
154 use std::path::PathBuf;
155
156 use super::*;
157
158 #[test]
159 fn test_invalid() {
160 let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
161 d.push("resources/tests");
162 d.push("random");
163
164 PreBoneDeformer::from_existing(&read(d).unwrap());
166 }
167}