ironworks/file/
pbd.rs

1//! Structs and utilities for parsing .pbd files.
2
3use std::{
4	borrow::Cow,
5	collections::HashMap,
6	fmt,
7	io::{Cursor, Read, Seek, SeekFrom},
8};
9
10use binrw::{binread, BinRead, BinResult, NullString, ReadOptions};
11
12use crate::error::Result;
13
14use super::file::File;
15
16/// Collection of bone deformations for transforming between character skeletons.
17#[binread]
18#[br(little)]
19#[derive(Debug)]
20pub struct PreBoneDeformer {
21	#[br(temp)]
22	data_count: u32,
23
24	#[br(count = data_count)]
25	deformers: Vec<DeformerData>,
26
27	#[br(count = data_count)]
28	nodes: Vec<NodeData>,
29}
30
31impl PreBoneDeformer {
32	/// Get an iterator over the deformers in this file.
33	pub fn deformers(&self) -> impl Iterator<Item = Deformer> {
34		self.deformers.iter().map(|deformer| Deformer {
35			pbd: self,
36			deformer,
37		})
38	}
39
40	/// Get the root of the node tree.
41	pub fn root_node(&self) -> Option<Node> {
42		self.nodes
43			.iter()
44			.find(|node| node.parent_index == u16::MAX)
45			.map(|node| Node { pbd: self, node })
46	}
47}
48
49impl File for PreBoneDeformer {
50	fn read<'a>(data: impl Into<Cow<'a, [u8]>>) -> Result<Self> {
51		Ok(<Self as BinRead>::read(&mut Cursor::new(data.into()))?)
52	}
53}
54
55/// A node within the deformer tree.
56pub struct Node<'a> {
57	pbd: &'a PreBoneDeformer,
58	node: &'a NodeData,
59}
60
61impl Node<'_> {
62	/// Get this node's corresponding deformer.
63	pub fn deformer(&self) -> Deformer {
64		Deformer {
65			pbd: self.pbd,
66			deformer: &self.pbd.deformers[usize::from(self.node.deformer_index)],
67		}
68	}
69
70	/// Get the parent node within the tree.
71	pub fn parent(&self) -> Option<Node> {
72		self.get_relation(self.node.parent_index)
73	}
74
75	/// Get the first child node, if this node has any children.
76	pub fn first_child(&self) -> Option<Node> {
77		self.get_relation(self.node.first_child_index)
78	}
79
80	/// Get the next sibling node.
81	pub fn next(&self) -> Option<Node> {
82		self.get_relation(self.node.next_index)
83	}
84
85	fn get_relation(&self, index: u16) -> Option<Node> {
86		match index {
87			u16::MAX => None,
88			index => Some(Node {
89				pbd: self.pbd,
90				node: &self.pbd.nodes[usize::from(index)],
91			}),
92		}
93	}
94}
95
96impl fmt::Debug for Node<'_> {
97	fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
98		self.node.fmt(formatter)
99	}
100}
101
102/// Deformer information for a character ID.
103pub struct Deformer<'a> {
104	pbd: &'a PreBoneDeformer,
105	deformer: &'a DeformerData,
106}
107
108impl Deformer<'_> {
109	/// Get this deformer's corresponding node in the tree.
110	pub fn node(&self) -> Node {
111		Node {
112			pbd: self.pbd,
113			node: &self.pbd.nodes[usize::from(self.deformer.node_index)],
114		}
115	}
116
117	/// Get the character ID this deformer represents.
118	pub fn id(&self) -> u16 {
119		self.deformer.id
120	}
121
122	/// Get the bone matrices for this deformer, if any exist.
123	pub fn bone_matrices(&self) -> Option<&HashMap<String, BoneMatrix>> {
124		self.deformer
125			.bone_matrices
126			.as_ref()
127			.map(|s| &s.bone_matrices)
128	}
129}
130
131impl fmt::Debug for Deformer<'_> {
132	fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
133		self.deformer.fmt(formatter)
134	}
135}
136
137#[binread]
138#[br(little)]
139#[derive(Debug)]
140struct DeformerData {
141	id: u16,
142	node_index: u16,
143
144	#[br(temp)]
145	data_offset: i32,
146
147	#[br(
148		if(data_offset > 0),
149		seek_before = SeekFrom::Start(data_offset.try_into().unwrap()),
150		restore_position,
151	)]
152	bone_matrices: Option<BoneMatrices>,
153
154	// TODO: apparently 2.x pbds don't include this?
155	_unknown: f32,
156}
157
158type BoneMatrix = [[f32; 4]; 3];
159
160#[binread]
161#[br(little)]
162struct BoneMatrices {
163	#[br(temp, parse_with = current_position)]
164	base_offset: u64,
165
166	#[br(temp)]
167	bone_count: u32,
168
169	#[br(temp, args {
170		count: bone_count.try_into().unwrap(),
171		inner: (base_offset,)
172	})]
173	bone_names: Vec<BoneName>,
174
175	#[br(temp, align_before = 4, count = bone_count)]
176	matrices: Vec<BoneMatrix>,
177
178	#[br(calc = (0..bone_count)
179		.map(|index| {
180			let i = usize::try_from(index).unwrap();
181			(bone_names[i].bone_name.to_string(), matrices[i])
182		})
183		.collect()
184	)]
185	bone_matrices: HashMap<String, BoneMatrix>,
186}
187
188impl fmt::Debug for BoneMatrices {
189	fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
190		self.bone_matrices.fmt(formatter)
191	}
192}
193
194#[binread]
195#[br(little, import(base_offset: u64))]
196#[derive(Debug)]
197struct BoneName {
198	#[br(temp)]
199	offset: i16,
200
201	#[br(
202		seek_before = SeekFrom::Start(base_offset + u64::try_from(offset).unwrap()),
203		restore_position,
204	)]
205	bone_name: NullString,
206}
207
208#[binread]
209#[br(little)]
210#[derive(Debug)]
211struct NodeData {
212	parent_index: u16,
213	first_child_index: u16,
214	next_index: u16,
215	deformer_index: u16,
216}
217
218fn current_position<R: Read + Seek>(reader: &mut R, _: &ReadOptions, _: ()) -> BinResult<u64> {
219	Ok(reader.stream_position()?)
220}