1use crate::{align, parse_offset64_count32, parse_opt_ptr64, parse_ptr64, parse_string_ptr64};
2use binrw::{binread, BinRead};
3use xc3_write::{
4 strings::{StringSectionUniqueSorted, WriteOptions},
5 Xc3Write, Xc3WriteOffsets,
6};
7
8use super::{BcList, StringOffset, Transform};
9
10#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
11#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
12#[br(magic(b"SKEL"))]
13#[xc3(magic(b"SKEL"))]
14pub struct Skel {
15 #[br(parse_with = parse_ptr64)]
16 #[xc3(offset(u64))]
17 pub skeleton: Skeleton,
18}
19
20#[binread]
24#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
25#[derive(Debug, Xc3Write, PartialEq, Clone)]
26#[br(stream = r)]
27pub struct Skeleton {
28 #[br(temp, try_calc = r.stream_position())]
32 base_offset: u64,
33
34 pub unk1: BcList<u8>,
35 pub unk2: u64, #[br(parse_with = parse_string_ptr64)]
38 #[xc3(offset(u64))]
39 pub root_bone_name: String,
40
41 pub parent_indices: BcList<i16>,
42
43 pub names: BcList<BoneName>,
44
45 #[br(temp, restore_position)]
47 transforms_offset: u32,
48
49 #[br(parse_with = parse_offset64_count32)]
50 #[xc3(offset_count(u64, u32), align(16, 0xff))]
51 pub transforms: Vec<Transform>,
52 pub unk3: i32, #[br(parse_with = parse_offset64_count32)]
55 #[xc3(offset_count(u64, u32), align(8, 0xff))]
56 pub extra_track_slots: Vec<SkeletonExtraTrackSlot>,
57 pub unk4: i32, #[br(parse_with = parse_offset64_count32)]
61 #[xc3(offset_count(u64, u32), align(8, 0xff))]
62 pub mt_parent_indices: Vec<i16>,
63 pub unk5: i32, #[br(parse_with = parse_offset64_count32)]
66 #[xc3(offset_count(u64, u32), align(8, 0xff))]
67 pub mt_names: Vec<StringOffset>,
68 pub unk6: i32, #[br(parse_with = parse_offset64_count32)]
71 #[xc3(offset_count(u64, u32), align(16, 0xff))]
72 pub mt_transforms: Vec<Transform>,
73 pub unk7: i32, pub labels: BcList<SkeletonLabel>,
76
77 #[br(args_raw(transforms_offset as u64 - base_offset))]
78 pub extra: SkeletonExtra,
79}
80
81#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
85#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
86#[br(import_raw(size: u64))]
87pub enum SkeletonExtra {
88 #[br(pre_assert(size == 160))]
89 Unk0,
90
91 #[br(pre_assert(size == 192))]
92 Unk1(SkeletonExtraUnk1),
93
94 #[br(pre_assert(size == 224))]
95 Unk2(SkeletonExtraUnk2),
96
97 #[br(pre_assert(size == 240))]
98 Unk3(SkeletonExtraUnk3),
99
100 Unk,
102}
103
104#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
106pub struct SkeletonExtraUnk1 {
107 #[br(parse_with = parse_opt_ptr64)]
108 #[xc3(offset(u64), align(16, 0xff))]
109 pub unk6: Option<SkeletonUnk6Unk1>,
110
111 #[br(parse_with = parse_opt_ptr64)]
112 #[xc3(offset(u64), align(16, 0xff))]
113 pub unk7: Option<SkeletonUnk7>,
114
115 #[br(parse_with = parse_opt_ptr64)]
116 #[xc3(offset(u64), align(16, 0xff))]
117 pub unk8: Option<SkeletonUnk8>,
118}
119
120#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
121#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
122pub struct SkeletonExtraUnk2 {
123 #[br(parse_with = parse_opt_ptr64)]
124 #[xc3(offset(u64), align(16, 0xff))]
125 pub unk6: Option<SkeletonUnk6>,
126
127 #[br(parse_with = parse_opt_ptr64)]
128 #[xc3(offset(u64), align(16, 0xff))]
129 pub unk7: Option<SkeletonUnk7>,
130
131 #[br(parse_with = parse_opt_ptr64)]
132 #[xc3(offset(u64), align(16, 0xff))]
133 pub unk8: Option<SkeletonUnk8>,
134
135 #[br(parse_with = parse_opt_ptr64)]
136 #[xc3(offset(u64), align(16, 0xff))]
137 pub unk9: Option<SkeletonUnk9>,
138
139 #[br(parse_with = parse_opt_ptr64)]
140 #[xc3(offset(u64), align(16, 0xff))]
141 pub unk10: Option<SkeletonUnk10>,
142
143 #[br(parse_with = parse_opt_ptr64)]
144 #[xc3(offset(u64), align(16, 0xff))]
145 pub unk11: Option<SkeletonUnk11>,
146
147 pub unk2: u64,
148 pub unk3: i64,
149}
150
151#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
152#[derive(Debug, BinRead, Xc3Write, PartialEq, Clone)]
153pub struct SkeletonExtraUnk3 {
154 #[br(parse_with = parse_opt_ptr64)]
155 #[xc3(offset(u64), align(16, 0xff))]
156 pub unk6: Option<SkeletonUnk6>,
157
158 #[br(parse_with = parse_opt_ptr64)]
159 #[xc3(offset(u64), align(16, 0xff))]
160 pub unk7: Option<SkeletonUnk7>,
161
162 #[br(parse_with = parse_opt_ptr64)]
163 #[xc3(offset(u64), align(16, 0xff))]
164 pub unk8: Option<SkeletonUnk8>,
165
166 #[br(parse_with = parse_opt_ptr64)]
167 #[xc3(offset(u64), align(16, 0xff))]
168 pub unk9: Option<SkeletonUnk9>,
169
170 #[br(parse_with = parse_opt_ptr64)]
171 #[xc3(offset(u64), align(16, 0xff))]
172 pub unk10: Option<SkeletonUnk10>,
173
174 #[br(parse_with = parse_opt_ptr64)]
175 #[xc3(offset(u64), align(16, 0xff))]
176 pub unk11: Option<SkeletonUnk11>,
177
178 #[br(parse_with = parse_opt_ptr64)]
179 #[xc3(offset(u64), align(8, 0xff))]
180 pub unk12: Option<SkeletonUnk12>,
181
182 #[br(parse_with = parse_opt_ptr64)]
183 #[xc3(offset(u64), align(8, 0xff))]
184 pub unk13: Option<SkeletonUnk13>,
185
186 pub unk2: u64,
187 pub unk3: i64,
188}
189
190#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
191#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
192pub struct SkeletonLabel {
193 pub bone_type: u32, pub index: u16, pub bone_index: u16,
196}
197
198#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
199#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
200pub struct BoneName {
201 #[br(parse_with = parse_string_ptr64)]
202 #[xc3(offset(u64))]
203 pub name: String,
204
205 pub unk: [u32; 2],
207}
208
209#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
210#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
211pub struct SkeletonExtraTrackSlot {
212 #[br(parse_with = parse_string_ptr64)]
213 #[xc3(offset(u64))]
214 pub unk1: String,
215
216 pub unk2: BcList<StringOffset>,
217
218 pub unk3: BcList<f32>,
219
220 #[br(parse_with = parse_offset64_count32)]
221 #[xc3(offset_count(u64, u32), align(8, 0xff))]
222 pub unk4: Vec<[f32; 2]>,
223 pub unk1_1: i32, }
225
226#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
227#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
228pub struct SkeletonUnk6 {
229 pub unk1: BcList<u8>,
230
231 #[br(parse_with = parse_offset64_count32)]
232 #[xc3(offset_count(u64, u32), align(4, 0xff))]
233 pub unk2: Vec<u16>,
234 pub unk2_1: i32, #[br(parse_with = parse_offset64_count32)]
237 #[xc3(offset_count(u64, u32), align(8, 0xff))]
238 pub unk3: Vec<u32>,
239}
240
241#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
242#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
243pub struct SkeletonUnk6Unk1 {
244 pub unk1: BcList<u8>,
245
246 #[br(parse_with = parse_offset64_count32)]
247 #[xc3(offset_count(u64, u32), align(4, 0xff))]
248 pub unk2: Vec<u16>,
249 pub unk2_1: i32, }
251
252#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
253#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
254pub struct SkeletonUnk7 {
255 pub unk1: BcList<u8>,
256
257 #[br(parse_with = parse_offset64_count32)]
258 #[xc3(offset_count(u64, u32), align(4, 0xff))]
259 pub unk2: Vec<u16>,
260 pub unk2_1: i32, #[br(parse_with = parse_offset64_count32)]
264 #[xc3(offset_count(u64, u32), align(8, 0xff))]
265 pub unk3: Vec<u32>,
266}
267
268#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
269#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
270pub struct SkeletonUnk8 {
271 #[br(parse_with = parse_offset64_count32)]
272 #[xc3(offset_count(u64, u32))]
273 pub unk1: Vec<u32>,
274}
275
276#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
277#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
278pub struct SkeletonUnk9 {
279 pub unk1: BcList<[u32; 13]>,
281
282 #[br(parse_with = parse_offset64_count32)]
284 #[xc3(offset_count(u64, u32))]
285 pub unk2: Vec<u64>,
286}
287
288#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
289#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
290pub struct SkeletonUnk10 {
291 pub unk1: [u32; 8],
293}
294
295#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
296#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
297pub struct SkeletonUnk11 {
298 #[br(parse_with = parse_offset64_count32)]
299 #[xc3(offset_count(u64, u32))]
300 pub unk1: Vec<u8>,
301}
302
303#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
304#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
305pub struct SkeletonUnk12 {
306 #[br(parse_with = parse_offset64_count32)]
307 #[xc3(offset_count(u64, u32))]
308 pub unk1: Vec<u16>,
309}
310
311#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
312#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
313pub struct SkeletonUnk13 {
314 pub unk1: BcList<[f32; 4]>,
315 pub unk2: BcList<i16>,
316}
317
318impl Xc3WriteOffsets for SkeletonOffsets<'_> {
319 type Args = ();
320
321 fn write_offsets<W: std::io::prelude::Write + std::io::prelude::Seek>(
322 &self,
323 writer: &mut W,
324 base_offset: u64,
325 data_ptr: &mut u64,
326 endian: xc3_write::Endian,
327 _args: Self::Args,
328 ) -> xc3_write::Xc3Result<()> {
329 let mut string_section = StringSectionUniqueSorted::default();
331 string_section.insert_offset64(&self.root_bone_name);
332
333 if !self.unk1.0.data.is_empty() {
335 self.unk1
336 .write_offsets(writer, base_offset, data_ptr, endian, ())?;
337 }
338 self.transforms
339 .write_full(writer, base_offset, data_ptr, endian, ())?;
340
341 let names = self.names.0.write(writer, base_offset, data_ptr, endian)?;
342 for name in names.0 {
343 string_section.insert_offset64(&name.name);
344 }
345
346 self.parent_indices
347 .write_offsets(writer, base_offset, data_ptr, endian, ())?;
348
349 if !self.extra_track_slots.data.is_empty() {
350 let slots = self
351 .extra_track_slots
352 .write(writer, base_offset, data_ptr, endian)?;
353 for slot in slots.0 {
354 string_section.insert_offset64(&slot.unk1);
355
356 if !slot.unk2.0.data.is_empty() {
357 let names = slot.unk2.0.write(writer, base_offset, data_ptr, endian)?;
358 for name in names.0 {
359 string_section.insert_offset64(&name.name);
360 }
361 }
362
363 if !slot.unk3.0.data.is_empty() {
364 slot.unk3
365 .write_offsets(writer, base_offset, data_ptr, endian, ())?;
366 }
367 if !slot.unk4.data.is_empty() {
368 slot.unk4
369 .write_full(writer, base_offset, data_ptr, endian, ())?;
370 }
371 }
372 }
373
374 if !self.mt_parent_indices.data.is_empty() {
375 self.mt_parent_indices
376 .write_full(writer, base_offset, data_ptr, endian, ())?;
377 }
378 if !self.mt_names.data.is_empty() {
379 let names = self.mt_names.write(writer, base_offset, data_ptr, endian)?;
380 for name in names.0 {
381 string_section.insert_offset64(&name.name);
382 }
383 }
384 if !self.mt_transforms.data.is_empty() {
385 self.mt_transforms
386 .write_full(writer, base_offset, data_ptr, endian, ())?;
387 }
388
389 if self.mt_parent_indices.data.is_empty() {
391 weird_skel_alignment(writer, data_ptr, endian)?;
392 }
393
394 if !self.labels.0.data.is_empty() {
395 self.labels
396 .write_offsets(writer, base_offset, data_ptr, endian, ())?;
397 }
398
399 self.extra
400 .write_offsets(writer, base_offset, data_ptr, endian, ())?;
401
402 let start_alignment = match self.extra {
404 SkeletonExtraOffsets::Unk0 => 4,
405 SkeletonExtraOffsets::Unk1(_) => 8,
406 SkeletonExtraOffsets::Unk2(_) => 8,
407 SkeletonExtraOffsets::Unk3(_) => 8,
408 SkeletonExtraOffsets::Unk => todo!(),
409 };
410 string_section.write(
411 writer,
412 data_ptr,
413 &WriteOptions {
414 start_alignment,
415 start_padding_byte: 0xff,
416 string_alignment: 1,
417 string_padding_byte: 0,
418 },
419 endian,
420 )?;
421
422 Ok(())
423 }
424}
425
426fn weird_skel_alignment<W: std::io::Write + std::io::Seek>(
427 writer: &mut W,
428 data_ptr: &mut u64,
429 endian: xc3_write::Endian,
430) -> xc3_write::Xc3Result<()> {
431 let pos = writer.stream_position()?;
435 align(writer, pos, 8, 0xff)?;
436
437 [0u8; 2].xc3_write(writer, endian)?;
440 *data_ptr = (*data_ptr).max(writer.stream_position()?);
441
442 let pos = writer.stream_position()?;
443 align(writer, pos, 16, 0xff)?;
444
445 [0u8; 4].xc3_write(writer, endian)?;
447 *data_ptr = (*data_ptr).max(writer.stream_position()?);
448 Ok(())
449}
450
451impl Xc3WriteOffsets for SkeletonExtraUnk3Offsets<'_> {
452 type Args = ();
453
454 fn write_offsets<W: std::io::prelude::Write + std::io::prelude::Seek>(
455 &self,
456 writer: &mut W,
457 base_offset: u64,
458 data_ptr: &mut u64,
459 endian: xc3_write::Endian,
460 _args: Self::Args,
461 ) -> xc3_write::Xc3Result<()> {
462 self.unk6
464 .write_full(writer, base_offset, data_ptr, endian, ())?;
465 self.unk7
466 .write_full(writer, base_offset, data_ptr, endian, ())?;
467 self.unk12
468 .write_full(writer, base_offset, data_ptr, endian, ())?;
469 self.unk9
470 .write_full(writer, base_offset, data_ptr, endian, ())?;
471 self.unk8
472 .write_full(writer, base_offset, data_ptr, endian, ())?;
473 self.unk10
474 .write_full(writer, base_offset, data_ptr, endian, ())?;
475 self.unk11
476 .write_full(writer, base_offset, data_ptr, endian, ())?;
477 self.unk13
478 .write_full(writer, base_offset, data_ptr, endian, ())?;
479 Ok(())
480 }
481}