xc3_lib/
mxmd.rs

1//! Model data in `.wimdo` files.
2//!
3//! [Mxmd] files contain the main model data like the mesh hierarchy and materials
4//! as well as information on the streaming data in the optional `.wismt` file.
5//!
6//! # File Paths
7//! | Game | Version | File Patterns |
8//! | --- | --- | --- |
9//! | Xenoblade 1 DE | 10112 | `chr/{en,np,obj,pc,wp}/*.wimdo`, `monolib/shader/*.wimdo` |
10//! | Xenoblade 2 | 10111, 10112 | `model/{bl,en,np,oj,pc,we,wp}/*.wimdo`, `monolib/shader/*.wimdo` |
11//! | Xenoblade 3 | 10112 | `chr/{bt,ch,en,oj,wp}/*.wimdo`, `map/*.wimdo`, `monolib/shader/*.wimdo` |
12//! | Xenoblade X DE | 10040 | `chr/{dl,en,fc,mb,np,oj,pc,un,wd,wdb,we,ws}/*.wimdo` |
13use crate::{
14    msrd::Streaming,
15    parse_count32_offset32, parse_offset32_count32, parse_opt_ptr32, parse_ptr32,
16    parse_string_opt_ptr32, parse_string_ptr32,
17    spch::Spch,
18    vertex::{DataType, VertexData},
19    xc3_write_binwrite_impl, StringOffset32,
20};
21use bilge::prelude::*;
22use binrw::{args, binread, BinRead, BinWrite};
23use legacy2::MxmdV40;
24use xc3_write::{Xc3Write, Xc3WriteOffsets};
25
26pub mod legacy;
27pub mod legacy2;
28
29#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
30#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
31#[br(magic(b"DMXM"))]
32#[xc3(magic(b"DMXM"))]
33pub struct Mxmd {
34    // TODO: Calculate version when writing.
35    pub version: u32,
36
37    #[br(args_raw(version))]
38    pub inner: MxmdInner,
39}
40
41#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
42#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
43#[br(import_raw(version: u32))]
44pub enum MxmdInner {
45    #[br(pre_assert(version == 10040))]
46    V40(MxmdV40),
47
48    #[br(pre_assert(version == 10111))]
49    V111(MxmdV111),
50
51    #[br(pre_assert(version == 10112))]
52    V112(MxmdV112),
53}
54
55// TODO: Test this against xc2 files.
56#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
57#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
58pub struct MxmdV111 {
59    /// A collection of [ModelV111] and associated data.
60    #[br(parse_with = parse_ptr32)]
61    #[xc3(offset(u32))]
62    pub models: ModelsV111,
63
64    /// A collection of [Material] and associated data.
65    #[br(parse_with = parse_ptr32)]
66    #[xc3(offset(u32))]
67    pub materials: Materials,
68
69    #[br(parse_with = parse_opt_ptr32)]
70    #[xc3(offset(u32))]
71    pub unk1: Option<Unk1>,
72
73    // TODO: This type is slightly different than for mxmd 112
74    /// Embedded vertex data for .wimdo only models with no .wismt.
75    #[br(parse_with = parse_opt_ptr32)]
76    #[xc3(offset(u32))]
77    pub vertex_data: Option<VertexData>,
78
79    /// Embedded shader data for .wimdo only models with no .wismt.
80    #[br(parse_with = parse_opt_ptr32)]
81    #[xc3(offset(u32))]
82    pub spch: Option<Spch>,
83
84    /// Textures included within this file.
85    #[br(parse_with = parse_opt_ptr32)]
86    #[xc3(offset(u32))]
87    pub packed_textures: Option<PackedTextures>,
88
89    pub unk5: u32,
90
91    /// Streaming information for the .wismt file or [None] if no .wismt file.
92    /// Identical to the same field in the corresponding [Msrd](crate::msrd::Msrd).
93    #[br(parse_with = parse_opt_ptr32)]
94    #[xc3(offset(u32), align(4))]
95    pub streaming: Option<Streaming>,
96
97    pub unk6: u32,
98    pub unk7: u32,
99
100    #[br(parse_with = parse_opt_ptr32)]
101    #[xc3(offset(u32), align(16))]
102    pub unk8: Option<Unk8>,
103
104    // TODO: padding?
105    pub unk: [u32; 6],
106}
107
108#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
109#[derive(Debug, BinRead, Xc3Write, PartialEq, Clone)]
110pub struct MxmdV112 {
111    /// A collection of [ModelV112] and associated data.
112    #[br(parse_with = parse_ptr32)]
113    #[xc3(offset(u32), align(16))]
114    pub models: ModelsV112,
115
116    /// A collection of [Material] and associated data.
117    #[br(parse_with = parse_ptr32)]
118    #[xc3(offset(u32), align(16))]
119    pub materials: Materials,
120
121    #[br(parse_with = parse_opt_ptr32)]
122    #[xc3(offset(u32), align(16))]
123    pub unk1: Option<Unk1>,
124
125    /// Embedded vertex data for .wimdo only models with no .wismt.
126    #[br(parse_with = parse_opt_ptr32)]
127    #[xc3(offset(u32))]
128    pub vertex_data: Option<VertexData>,
129
130    /// Embedded shader data for .wimdo only models with no .wismt.
131    #[br(parse_with = parse_opt_ptr32)]
132    #[xc3(offset(u32))]
133    pub spch: Option<Spch>,
134
135    /// Textures included within this file.
136    #[br(parse_with = parse_opt_ptr32)]
137    #[xc3(offset(u32))]
138    pub packed_textures: Option<PackedTextures>,
139
140    pub unk5: u32,
141
142    /// Streaming information for the .wismt file or [None] if no .wismt file.
143    /// Identical to the same field in the corresponding [Msrd](crate::msrd::Msrd).
144    #[br(parse_with = parse_opt_ptr32)]
145    #[xc3(offset(u32), align(4))]
146    pub streaming: Option<Streaming>,
147
148    pub unk6: u32,
149    pub unk7: u32,
150
151    #[br(parse_with = parse_opt_ptr32)]
152    #[xc3(offset(u32), align(16))]
153    pub unk8: Option<Unk8>,
154
155    // TODO: padding?
156    pub unk: [u32; 6],
157}
158
159// TODO: more strict alignment for xc3?
160// TODO: 108 bytes for xc2 and 112 bytes for xc3?
161/// A collection of [Material], [Sampler], and material parameters.
162/// `ml::MdsMatTopHeader` in the Xenoblade 2 binary.
163#[binread]
164#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
165#[derive(Debug, Xc3Write, PartialEq, Clone)]
166#[br(stream = r)]
167#[xc3(base_offset)]
168pub struct Materials {
169    #[br(temp, try_calc = r.stream_position())]
170    base_offset: u64,
171
172    #[br(temp, restore_position)]
173    material_offset: u32,
174
175    // TODO: Sometimes 108 and sometimes 112?
176    #[br(parse_with = parse_offset32_count32, args { offset: base_offset, inner: base_offset })]
177    #[xc3(offset_count(u32, u32), align(4))]
178    pub materials: Vec<Material>,
179
180    // offset?
181    pub unk1: u32,
182    pub unk2: u32,
183
184    // TODO: Materials have offsets into these arrays for parameter values?
185    // material body has a uniform at shader offset 64 but offset 48 in this floats buffer
186    #[br(parse_with = parse_offset32_count32, offset = base_offset)]
187    #[xc3(offset_count(u32, u32), align(16))]
188    pub work_values: Vec<f32>,
189
190    // TODO: final number counts up from 0?
191    // TODO: Some sort of index or offset?
192    #[br(parse_with = parse_offset32_count32, offset = base_offset)]
193    #[xc3(offset_count(u32, u32))]
194    pub shader_vars: Vec<(u16, u16)>, // shader vars (u8, u8, u16)?
195
196    #[br(parse_with = parse_opt_ptr32)]
197    #[br(args { offset: base_offset, inner: base_offset })]
198    #[xc3(offset(u32))]
199    pub callbacks: Option<MaterialCallbacks>,
200
201    // TODO: is this ever not 0?
202    pub unk4: u32,
203
204    /// Info for each of the shaders in the associated [Spch](crate::spch::Spch).
205    #[br(parse_with = parse_offset32_count32)]
206    #[br(args { offset: base_offset, inner: base_offset })]
207    #[xc3(offset_count(u32, u32))]
208    pub techniques: Vec<Technique>,
209
210    pub unks1: u32,
211
212    #[br(parse_with = parse_opt_ptr32)]
213    #[br(args { offset: base_offset, inner: base_offset })]
214    #[xc3(offset(u32))]
215    pub unk6: Option<MaterialUnk6>,
216
217    #[br(parse_with = parse_count32_offset32, offset = base_offset)]
218    #[xc3(count_offset(u32, u32))]
219    pub alpha_test_textures: Vec<AlphaTestTexture>,
220
221    // TODO: extra fields that go before samplers?
222    pub unks3: [u32; 3],
223
224    #[br(parse_with = parse_opt_ptr32)]
225    #[br(args { offset: base_offset, inner: base_offset })]
226    #[xc3(offset(u32))]
227    pub material_unk2: Option<MaterialUnk2>,
228
229    #[br(parse_with = parse_opt_ptr32)]
230    #[br(args { offset: base_offset, inner: args! { base_offset, count: materials.len() } })]
231    #[xc3(offset(u32))]
232    pub fur_shells: Option<FurShells>,
233
234    pub unks3_1: [u32; 2],
235
236    #[br(parse_with = parse_opt_ptr32, offset = base_offset)]
237    #[xc3(offset(u32))]
238    pub samplers: Option<Samplers>,
239
240    // TODO: padding?
241    pub unks4: [u32; 3],
242
243    #[br(if(material_offset >= 112))]
244    #[br(args_raw(base_offset))]
245    pub unk5: Option<MaterialUnk5>,
246}
247
248#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
249#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
250pub struct AlphaTestTexture {
251    /// Index into [textures](struct.Material.html#structfield.textures) for the texture
252    /// to compare with [alpha_test_ref](struct.Material.html#structfield.alpha_test_ref).
253    pub texture_index: u16,
254    /// Index into [samplers](struct.Materials.html#structfield.samplers).
255    pub sampler_index: u16,
256
257    // TODO: Test this in game.
258    pub unk2: u16, // 1, 3, 4, 5
259    pub unk3: u16, // 0, 1
260}
261
262/// `ml::MdsMatTechnique` in the Xenoblade 2 binary.
263#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
264#[derive(Debug, BinRead, Xc3Write, PartialEq, Clone)]
265#[br(import_raw(base_offset: u64))]
266pub struct Technique {
267    /// The input attributes for the vertex shader.
268    /// The order defined here should also be used for the vertex buffer attributes to work properly in game.
269    #[br(parse_with = parse_offset32_count32, offset = base_offset)]
270    #[xc3(offset_count(u32, u32))]
271    pub attributes: Vec<VertexAttribute>,
272
273    pub unk3: u32, // 0
274    pub unk4: u32, // 0
275
276    // work values?
277    // TODO: matches up with uniform parameters for U_Mate?
278    #[br(parse_with = parse_offset32_count32, offset = base_offset)]
279    #[xc3(offset_count(u32, u32))]
280    pub parameters: Vec<MaterialParameter>,
281
282    #[br(parse_with = parse_offset32_count32, offset = base_offset)]
283    #[xc3(offset_count(u32, u32))]
284    pub textures: Vec<u16>,
285
286    // ssbos and then uniform buffers ordered by handle?
287    #[br(parse_with = parse_offset32_count32, offset = base_offset)]
288    #[xc3(offset_count(u32, u32))]
289    pub uniform_blocks: Vec<UniformBlock>, // uniform blocks?
290
291    pub material_texture_count: u32,
292
293    // first texture param index?
294    pub unk12: u16, // counts up from 0?
295    // first global param index?
296    pub unk13: u16, // unk11 + unk12?
297
298    pub unk14: u32,
299
300    // TODO: type?
301    #[br(parse_with = parse_offset32_count32, offset = base_offset)]
302    #[xc3(offset_count(u32, u32))]
303    pub unk15: Vec<[u32; 5]>,
304
305    // TODO: padding?
306    pub padding: [u32; 2],
307}
308
309#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
310#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
311pub struct UniformBlock {
312    pub unk1: u16,
313    pub unk2: u8,
314    pub unk3: u8,
315}
316
317#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
318#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
319pub struct VertexAttribute {
320    pub data_type: DataType,
321    pub relative_offset: u16,
322    pub buffer_index: u16,
323    pub unk4: u16, // always 0?
324}
325
326/// `ml::MdsMatVariableTbl` in the Xenoblade 2 binary.
327#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
328#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
329pub struct MaterialParameter {
330    pub param_type: ParamType,
331    pub work_value_index: u16, // added to work value start index?
332    pub unk: u16,
333    /// The number of elements for an array or `1` for a single element
334    /// or `0` for values initialized without work values.
335    pub count: u16, // actual number of bytes depends on type?
336}
337
338#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
339#[derive(Debug, BinRead, BinWrite, Clone, Copy, PartialEq, Eq, Hash)]
340#[brw(repr(u16))]
341pub enum ParamType {
342    /// `gDpRat` uniform in the [Spch].
343    DpRat = 0,
344    /// `gTexMat` uniform in the [Spch] and
345    /// `ml::DrMdoSetup::unimate_texMatrix` in the Xenoblade 2 binary.
346    TexMatrix = 1,
347    /// `gWrkFl4[0]` uniform in the [Spch] and
348    /// `ml::DrMdoSetup::unimate_workFloat4` in the Xenoblade 2 binary.
349    WorkFloat4 = 2,
350    /// `gWrkCol` uniform in the [Spch] and
351    /// `ml::DrMdoSetup::unimate_workColor` in the Xenoblade 2 binary.
352    WorkColor = 3,
353    /// `gProjTexMat` uniform in the [Spch].
354    ProjectionTexMatrix = 4,
355    /// `gAlInf` uniform in the [Spch] and
356    /// `ml::DrMdoSetup::unimate_alphaInfo` in the Xenoblade 2 binary.
357    AlphaInfo = 5,
358    /// `gMatCol` uniform in the [Spch].
359    /// Takes the value of [color](struct.Material.html#structfield.color).
360    MaterialColor = 6,
361    Unk7 = 7,
362    /// `gToonHeadMat` uniform in the [Spch].
363    ToonHeadMatrix = 10,
364}
365
366#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
367#[derive(Debug, BinRead, Xc3Write, PartialEq, Clone)]
368#[br(import_raw(base_offset: u64))]
369pub struct MaterialCallbacks {
370    // TODO: affects material parameter assignment?
371    #[br(parse_with = parse_offset32_count32, offset = base_offset)]
372    #[xc3(offset_count(u32, u32))]
373    pub work_callbacks: Vec<WorkCallback>,
374
375    // 0 ... material_count - 1
376    #[br(parse_with = parse_offset32_count32, offset = base_offset)]
377    #[xc3(offset_count(u32, u32))]
378    pub material_indices: Vec<u16>,
379
380    // TODO: [index, ???]
381    #[br(parse_with = parse_offset32_count32, offset = base_offset)]
382    #[xc3(offset_count(u32, u32))]
383    pub unk1: Vec<[u32; 2]>,
384
385    // TODO: padding?
386    pub unk: [u32; 6],
387}
388
389#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
390#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
391pub struct WorkCallback {
392    // TODO: enum 0, 12, 22, 25, 26, 27, 28, 35, 36, 38, 40, 41, 42, 43, 45, 46, 47, 58, 50
393    // 25 outline width
394    // 26 next value / 255.0
395    pub unk1: u16,
396    // TODO: index?
397    pub unk2: u16,
398}
399
400#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
401#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
402#[br(import_raw(base_offset: u64))]
403pub struct MaterialUnk2 {
404    #[br(parse_with = parse_count32_offset32, offset = base_offset)]
405    #[xc3(count_offset(u32, u32))]
406    pub unk1: Vec<[u32; 3]>,
407
408    // TODO: padding?
409    pub unk: [u32; 4],
410}
411
412#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
413#[derive(Debug, BinRead, Xc3Write, PartialEq, Clone)]
414#[br(import { base_offset: u64, count: usize })]
415pub struct FurShells {
416    /// Index into [params](#structfield.params) for each of the elements in
417    /// [materials](struct.Materials.html#structfield.materials).
418    #[br(parse_with = parse_ptr32)]
419    #[br(args { offset: base_offset, inner: args! { count }})]
420    #[xc3(offset(u32))]
421    pub material_param_indices: Vec<u16>,
422
423    #[br(parse_with = parse_offset32_count32, offset = base_offset)]
424    #[xc3(offset_count(u32, u32))]
425    pub params: Vec<FurShellParams>,
426
427    // TODO: padding?
428    pub unk: [u32; 4],
429}
430
431#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
432#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
433pub struct FurShellParams {
434    /// The number of fur shells to render.
435    pub instance_count: u32,
436    /// The distance at which shell count starts to lower.
437    pub view_distance: f32,
438    /// The width applied increasingly to each fur shell.
439    pub shell_width: f32,
440    /// The vertical offset applied increasingly to each fur shell.
441    pub y_offset: f32,
442    /// The alpha transparency applied increasingly to each fur shell.
443    // TODO: alpha of 0.0 is not fully transparent?
444    pub alpha: f32,
445}
446
447/// A collection of [Sampler].
448#[binread]
449#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
450#[derive(Debug, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
451#[br(stream = r)]
452#[xc3(base_offset)]
453pub struct Samplers {
454    #[br(temp, try_calc = r.stream_position())]
455    base_offset: u64,
456
457    #[br(parse_with = parse_count32_offset32, offset = base_offset)]
458    #[xc3(count_offset(u32, u32))]
459    pub samplers: Vec<Sampler>,
460
461    // TODO: padding?
462    pub unk: [u32; 2],
463}
464
465/// State for controlling how textures are sampled.
466#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
467#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
468pub struct Sampler {
469    pub flags: SamplerFlags,
470    pub unk2: u16,
471
472    // Is this actually a float?
473    pub unk3: f32,
474}
475
476/// Texture sampler settings for addressing and filtering.
477#[bitsize(16)]
478#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
479#[derive(DebugBits, FromBits, BinRead, BinWrite, PartialEq, Clone, Copy)]
480#[br(map = u16::into)]
481#[bw(map = |&x| u16::from(x))]
482pub struct SamplerFlags {
483    /// Sets wrap U to repeat when `true`.
484    pub repeat_u: bool,
485    /// Sets wrap V to repeat when `true`.
486    pub repeat_v: bool,
487    /// Sets wrap U to mirrored repeat when `true` regardless of repeat U.
488    pub mirror_u: bool,
489    /// Sets wrap V to mirrored repeat when `true` regardless of repeat V.
490    pub mirror_v: bool,
491    /// Sets min and mag filter to nearest when `true`.
492    /// Disables 4x anisotropic filtering when `true`
493    /// The min filter also depends on disable_mipmap_filter.
494    pub nearest: bool,
495    /// Sets all wrap modes to clamp and min and mag filter to linear.
496    /// Ignores the values of previous flags.
497    pub force_clamp: bool,
498    /// Removes the mipmap nearest from the min filter when `true`.
499    /// Disables 4x anisotropic filtering when `true`
500    pub disable_mipmap_filter: bool,
501    pub unk1: bool,
502    pub unk3: bool,
503    pub unk: u7,
504}
505
506/// A single material assignable to a [MeshV112].
507/// `ml::MdsMatInfoHeader` in the Xenoblade 2 binary.
508#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
509#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
510#[br(import_raw(base_offset: u64))]
511pub struct Material {
512    #[br(parse_with = parse_string_ptr32, offset = base_offset)]
513    #[xc3(offset(u32))]
514    pub name: String,
515
516    pub flags: MaterialFlags,
517
518    pub render_flags: MaterialRenderFlags,
519
520    /// Color multiplier value assigned to the `gMatCol` shader uniform.
521    pub color: [f32; 4],
522
523    // TODO: remapped from range [0.0, 1.0] to [0.01, 0.99] for uniform buffer?
524    pub alpha_test_ref: f32,
525
526    // TODO: materials with zero textures?
527    /// Defines the shader's sampler bindings in order for s0, s1, s2, ...
528    #[br(parse_with = parse_offset32_count32, offset = base_offset)]
529    #[xc3(offset_count(u32, u32))]
530    pub textures: Vec<Texture>,
531
532    // TODO: rename to pipeline state?
533    pub state_flags: StateFlags,
534
535    // TODO: group indices for animations?
536    pub m_unks1_1: u32,
537    pub m_unks1_2: u32,
538    pub m_unks1_3: u32,
539    pub m_unks1_4: u32,
540
541    // TODO: each material has its own unique range of values?
542    /// Index into [work_values](struct.Materials.html#structfield.work_values).
543    pub work_value_start_index: u32,
544
545    // TODO: each material has its own unique range of values?
546    /// Index into [shader_vars](struct.Materials.html#structfield.shader_vars).
547    pub shader_var_start_index: u32,
548    pub shader_var_count: u32,
549
550    // TODO: always count 1?
551    #[br(parse_with = parse_offset32_count32, offset = base_offset)]
552    #[xc3(offset_count(u32, u32))]
553    pub techniques: Vec<MaterialTechnique>,
554
555    pub unk5: u32, // 0
556
557    /// Index into [work_callbacks](struct.MaterialCallbacks.html#structfield.work_callbacks).
558    pub callback_start_index: u16,
559    pub callback_count: u16,
560
561    // TODO: alt textures offset for non opaque rendering?
562    pub m_unks2: [u16; 3],
563
564    /// Index into [alpha_test_textures](struct.Materials.html#structfield.alpha_test_textures).
565    pub alpha_test_texture_index: u16,
566
567    pub m_unk3: u16,
568
569    // TODO: does this determine the layout of the gbuffer outputs?
570    pub gbuffer_flags: u16, // 0, 1, 2, 3, 4, 5, 6, ...
571
572    pub m_unk4: [u16; 6],
573}
574
575#[bitsize(32)]
576#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
577#[derive(DebugBits, FromBits, BinRead, BinWrite, PartialEq, Clone, Copy)]
578#[br(map = u32::into)]
579#[bw(map = |&x| u32::from(x))]
580pub struct MaterialFlags {
581    pub unk1: bool,
582    pub unk2: bool,
583    /// Enables alpha testing from a texture in a prepass when `true`.
584    pub alpha_mask: bool,
585    /// Samples `texture.x` from a dedicated mask texture when `true`.
586    /// Otherwise, the alpha channel is used.
587    pub separate_mask: bool,
588    pub unk5: bool,
589    pub unk6: bool,
590    pub unk7: bool,
591    pub unk8: bool,
592    pub unk9: bool,
593    // TODO: Extra draw calls for fur rendering?
594    pub fur: bool,
595    pub unk11: u17,
596    // TODO: Actually draw the instanced shells?
597    pub fur_shells: bool,
598    pub unk: u4,
599}
600
601#[bitsize(32)]
602#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
603#[derive(DebugBits, FromBits, BinRead, BinWrite, PartialEq, Clone, Copy)]
604#[br(map = u32::into)]
605#[bw(map = |&x| u32::from(x))]
606pub struct MaterialRenderFlags {
607    pub unk1: bool,
608    pub unk2: bool,
609    pub unk3: bool,
610    pub unk4: bool,
611    pub unk5: bool,
612    pub unk6: bool,
613    /// Render in a depth only z-prepass.
614    /// Used exlusively for speff_zpre materials for Xenoblade 3.
615    pub speff_zpre: bool, // TODO: start of gbuffer flags?
616    pub unk8: bool,
617    pub unk9: bool,
618    pub unk10: bool, // TODO: fur shading temp tex for xc2?
619    pub unk11: bool,
620    // TODO: Is this an enum?
621    pub specular: bool, // TODO: specular for out_attr5?
622    pub unk13: bool,    // TODO: true for core crystals?
623    pub unk14: bool,    // TODO: true for core crystals?
624    pub unk15: bool,    // TODO: true for _speff_trans?
625    pub unk16: bool,    // false for characters
626    pub unk17: bool,
627    pub unk18: bool,
628    pub unk19: bool,
629    pub unk20: bool,
630    /// Used exclusively for speff_ope materials for Xenoblade 3.
631    // TODO: what does this toggle?
632    pub speff_ope: bool,
633    pub unk22: bool,
634    pub unk23: bool,
635    pub unk24: bool,
636    pub unk25: bool,
637    pub unk26: bool,
638    pub unk: u6,
639}
640
641/// Flags controlling pipeline state for rasterizer and fragment state.
642#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
643#[derive(Debug, BinRead, BinWrite, Clone, Copy, PartialEq, Eq, Hash)]
644pub struct StateFlags {
645    pub depth_write_mode: u8, // TODO: 0, 1, 2, 7
646    pub blend_mode: BlendMode,
647    pub cull_mode: CullMode,
648    pub unk4: u8, // unused?
649    pub stencil_value: StencilValue,
650    pub stencil_mode: StencilMode,
651    pub depth_func: DepthFunc,
652    pub color_write_mode: ColorWriteMode,
653}
654
655// TODO: 0, 10 write to all outputs and 1,11 write to just color?
656#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
657#[derive(Debug, BinRead, BinWrite, Clone, Copy, PartialEq, Eq, Hash)]
658#[brw(repr(u8))]
659pub enum ColorWriteMode {
660    Unk0 = 0,   // TODO: all outputs?
661    Unk1 = 1,   // TODO: single output?
662    Unk2 = 2,   // TODO: xcx only?
663    Unk3 = 3,   // TODO: xcx only?
664    Unk5 = 5,   // TODO: xc2 efb0 only?
665    Unk6 = 6,   // TODO: xcx only?
666    Unk9 = 9,   // TODO: xcx only?
667    Unk10 = 10, // TODO: all outputs but blends with previous color output texture?
668    Unk11 = 11, // TODO: single output?
669    Unk12 = 12, // TODO: xcx only?
670}
671
672/// | Value | Col Src | Col Dst | Col Op | Alpha Src | Alpha Dst | Alpha Op |
673/// | --- | --- | --- | --- | --- | --- | --- |
674/// | 0 |  |  |  |  |  |  |
675/// | 1 | Src Alpha | 1 - Src Alpha | Add | Src Alpha | 1 - Src Alpha | Add |
676/// | 2 | Src Alpha | One | Add | Src Alpha | One | Add |
677/// | 3 | Zero | Src Col | Add | Zero | Src Col | Add |
678/// | 4 | 1 - Dst Col | Zero | Add | 1 - Dst Col | Zero | Add |
679/// | 5 | One | One | Add | One | One | Add |
680/// | 6 |  |  |  |  |  |  |
681#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
682#[derive(Debug, BinRead, BinWrite, Clone, Copy, PartialEq, Eq, Hash)]
683#[brw(repr(u8))]
684pub enum BlendMode {
685    Disabled = 0,
686    Blend = 1,
687    Unk2 = 2,
688    Multiply = 3,
689    MultiplyInverted = 4,
690    Add = 5,
691    Disabled2 = 6,
692}
693
694// TODO: manually test stencil values in renderdoc.
695#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
696#[derive(Debug, BinRead, BinWrite, Clone, Copy, PartialEq, Eq, Hash)]
697#[brw(repr(u8))]
698pub enum StencilValue {
699    /// 10 (0xA)
700    Unk0 = 0,
701    Unk1 = 1,
702    /// 14 (0xE)
703    Unk4 = 4,
704    Unk5 = 5,
705    Unk8 = 8,
706    Unk9 = 9,
707    Unk12 = 12,
708    /// 74 (0x4A)
709    Unk16 = 16,
710    Unk20 = 20,
711    // TODO: test Xenoblade X values in RenderDoc
712    Unk33 = 33,
713    Unk37 = 37,
714    Unk41 = 41,
715    Unk49 = 49,
716    Unk97 = 97,
717    Unk105 = 105,
718    Unk128 = 128, // TODO: xcx de only?
719}
720
721// TODO: Does this flag actually disable stencil?
722#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
723#[derive(Debug, BinRead, BinWrite, Clone, Copy, PartialEq, Eq, Hash)]
724#[brw(repr(u8))]
725pub enum StencilMode {
726    // func, write mask, comp mask, ref value
727    Unk0 = 0,   // completely disabled?
728    Unk1 = 1,   // always, ff, ff, 0a
729    Unk2 = 2,   // equals, 0a, 0a, 0a
730    Unk6 = 6,   // equals, 4b, 04, 0a
731    Unk7 = 7,   // always, 0e, 04, 0a
732    Unk8 = 8,   // nequal, 02, 02, 02
733    Unk9 = 9,   // TODO: xcx de only?
734    Unk12 = 12, // TODO: xcx de only?
735    Unk13 = 13, // TODO: xcx de only?
736}
737
738#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
739#[derive(Debug, BinRead, BinWrite, Clone, Copy, PartialEq, Eq, Hash)]
740#[brw(repr(u8))]
741pub enum DepthFunc {
742    Disabled = 0,
743    LessEqual = 1,
744    Equal = 3,
745}
746
747#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
748#[derive(Debug, BinRead, BinWrite, Clone, Copy, PartialEq, Eq, Hash)]
749#[brw(repr(u8))]
750pub enum CullMode {
751    Back = 0,
752    Front = 1,
753    Disabled = 2,
754    Unk3 = 3, // front + ???
755}
756
757/// `ml::MdsMatMaterialTechnique` in the Xenoblade 2 binary.
758#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
759#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
760pub struct MaterialTechnique {
761    /// Index into [techniques](struct.Materials.html#structfield.techniques).
762    /// This can also be assumed to be the index into the [Spch] programs.
763    pub technique_index: u32,
764    pub pass_type: RenderPassType,
765    pub material_buffer_index: u16,
766    pub flags: u32, // always 1?
767}
768
769// TODO: Use in combination with mesh render flags?
770// Each "pass" has different render targets?
771// _trans = 1,
772// _ope = 0,1,7
773// _zpre = 0
774// _outline = 0
775#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
776#[derive(Debug, BinRead, BinWrite, PartialEq, Eq, Clone, Copy, Hash)]
777#[brw(repr(u16))]
778pub enum RenderPassType {
779    Unk0 = 0, // main opaque + some transparent?
780    Unk1 = 1, // transparent pass with color output
781    Unk6 = 6, // used for maps?
782    Unk7 = 7, // transparent pass but writes to all outputs
783    Unk9 = 9, // used for maps?
784}
785
786#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
787#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
788pub struct Texture {
789    /// Index into the textures in [streaming](struct.Mxmd.html#structfield.streaming)
790    /// or [packed_textures](struct.Mxmd.html#structfield.packed_textures).
791    pub texture_index: u16,
792    /// Index into the samplers in [samplers](struct.Materials.html#structfield.samplers).
793    pub sampler_index: u16,
794    /// Index into the samplers in [samplers](struct.Materials.html#structfield.samplers).
795    // TODO: This sampler is the same as above but with a float value of 0.0?
796    pub sampler_index2: u16,
797    pub unk3: u16, // 0
798}
799
800#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
801#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
802#[br(import_raw(base_offset: u64))]
803pub struct MaterialUnk5 {
804    #[br(parse_with = parse_opt_ptr32, offset = base_offset)]
805    #[xc3(offset(u32))]
806    pub unk1: Option<MaterialUnk5Inner>,
807}
808
809#[binread]
810#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
811#[derive(Debug, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
812#[br(stream = r)]
813#[xc3(base_offset)]
814pub struct MaterialUnk5Inner {
815    #[br(temp, try_calc = r.stream_position())]
816    base_offset: u64,
817
818    pub unk1: u32,
819
820    // TODO: item type?
821    #[br(parse_with = parse_count32_offset32, offset = base_offset)]
822    #[xc3(count_offset(u32, u32))]
823    pub unk2: Vec<[f32; 6]>,
824}
825
826#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
827#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
828#[br(import_raw(base_offset: u64))]
829pub struct MaterialUnk6 {
830    #[br(parse_with = parse_count32_offset32)]
831    #[br(args { offset: base_offset, inner: base_offset })]
832    #[xc3(count_offset(u32, u32))]
833    pub items: Vec<MaterialUnk6Item>,
834}
835
836#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
837#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
838#[br(import_raw(base_offset: u64))]
839pub struct MaterialUnk6Item {
840    pub unk1: u32,
841    pub material_index: u32,
842    pub unk3: u32,
843
844    #[br(parse_with = parse_count32_offset32)]
845    #[br(args { offset: base_offset, inner: base_offset })]
846    #[xc3(count_offset(u32, u32))]
847    pub unk4: Vec<MaterialUnk6ItemUnk4>,
848}
849
850#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
851#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
852#[br(import_raw(base_offset: u64))]
853pub struct MaterialUnk6ItemUnk4 {
854    pub unk1: u32,
855
856    #[br(parse_with = parse_count32_offset32, offset = base_offset)]
857    #[xc3(count_offset(u32, u32))]
858    pub unk2: Vec<[f32; 3]>,
859}
860
861// xc1: 160, 164, 168 bytes
862// xc2: 160 bytes
863// xc3: 160, 164, 168, 200, 204 bytes
864/// A collection of [ModelV112] as well as skinning and animation information.
865#[binread]
866#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
867#[derive(Debug, Xc3Write, PartialEq, Clone)]
868#[br(stream = r)]
869#[xc3(base_offset)]
870pub struct ModelsV112 {
871    #[br(temp, try_calc = r.stream_position())]
872    base_offset: u64,
873
874    pub models_flags: ModelsFlags,
875
876    /// The maximum of all the [max_xyz](struct.Model.html#structfield.max_xyz) in [models](#structfield.models).
877    pub max_xyz: [f32; 3],
878    /// The minimum of all the [min_xyz](struct.Model.html#structfield.min_xyz) in [models](#structfield.models).
879    pub min_xyz: [f32; 3],
880
881    #[br(temp, restore_position)]
882    models_offset: u32,
883
884    #[br(parse_with = parse_offset32_count32, args { offset: base_offset, inner: base_offset })]
885    #[xc3(offset_count(u32, u32))]
886    pub models: Vec<ModelV112>,
887
888    pub unk2: u32,
889
890    #[br(parse_with = parse_opt_ptr32, offset = base_offset)]
891    #[xc3(offset(u32))]
892    pub skinning: Option<Skinning>,
893
894    #[br(parse_with = parse_opt_ptr32, offset = base_offset)]
895    #[xc3(offset(u32))]
896    pub model_unk11: Option<ModelUnk11>,
897
898    pub unks3_1: [u32; 13],
899
900    // offset 100
901    #[br(parse_with = parse_offset32_count32, args { offset: base_offset, inner: base_offset })]
902    #[xc3(offset_count(u32, u32), align(16))]
903    pub ext_meshes: Vec<ExtMesh>,
904
905    // TODO: always 0?
906    // TODO: offset for 10111?
907    pub unks3_2: [u32; 2],
908
909    #[br(parse_with = parse_opt_ptr32)]
910    #[br(args { offset: base_offset, inner: base_offset })]
911    #[xc3(offset(u32))]
912    pub model_unk8: Option<ModelUnk8>,
913
914    pub unk3_3: u32,
915
916    #[br(parse_with = parse_opt_ptr32, offset = base_offset)]
917    #[xc3(offset(u32))]
918    pub model_unk7: Option<ModelUnk7>,
919
920    // offset 128
921    #[br(parse_with = parse_opt_ptr32, offset = base_offset)]
922    #[xc3(offset(u32), align(16))]
923    pub morph_controllers: Option<MorphControllers>,
924
925    // TODO: Also morph related but for animations?
926    #[br(parse_with = parse_opt_ptr32, offset = base_offset)]
927    #[xc3(offset(u32), align(16))]
928    pub model_unk1: Option<ModelUnk1>,
929
930    #[br(parse_with = parse_opt_ptr32, offset = base_offset)]
931    #[xc3(offset(u32))]
932    pub model_unk3: Option<ModelUnk3>,
933
934    // TODO: not always aligned to 16?
935    #[br(parse_with = parse_opt_ptr32, offset = base_offset)]
936    #[xc3(offset(u32), align(8))]
937    pub lod_data: Option<LodData>,
938
939    // TODO: not always aligned to 16?
940    // TODO: Only null for stage models?
941    #[br(parse_with = parse_opt_ptr32, offset = base_offset)]
942    #[xc3(offset(u32), align(4))]
943    pub alpha_table: Option<AlphaTable>,
944
945    pub unk_field2: u32,
946
947    #[br(parse_with = parse_opt_ptr32)]
948    #[br(args { offset: base_offset, inner: base_offset})]
949    #[xc3(offset(u32))]
950    pub model_unk9: Option<ModelUnk9>,
951
952    // TODO: Completely different type for version 10111?
953    #[br(parse_with = parse_opt_ptr32, offset = base_offset)]
954    #[xc3(offset(u32))]
955    pub model_unk12: Option<ModelUnk12>,
956
957    // offset 160
958    // TODO: What controls the up to 44 optional bytes?
959    // TODO: How to estimate models offset from these fields?
960    // TODO: Investigate extra data for legacy mxmd files.
961    #[br(args { size: models_offset, base_offset})]
962    pub extra: ModelsExtraData,
963}
964
965#[binread]
966#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
967#[derive(Debug, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
968#[br(stream = r)]
969#[xc3(base_offset)]
970pub struct ModelsV111 {
971    #[br(temp, try_calc = r.stream_position())]
972    base_offset: u64,
973
974    /// The maximum of all the [max_xyz](struct.Model.html#structfield.max_xyz) in [models](#structfield.models).
975    pub max_xyz: [f32; 3],
976    /// The minimum of all the [min_xyz](struct.Model.html#structfield.min_xyz) in [models](#structfield.models).
977    pub min_xyz: [f32; 3],
978
979    #[br(parse_with = parse_offset32_count32, args { offset: base_offset, inner: base_offset })]
980    #[xc3(offset_count(u32, u32))]
981    pub models: Vec<ModelV111>,
982
983    pub unk2: u32,
984
985    pub model_unk16: u32,
986
987    pub model_unk11: u32,
988
989    #[br(parse_with = parse_opt_ptr32, offset = base_offset)]
990    #[xc3(offset(u32))]
991    pub skinning: Option<Skinning>,
992
993    pub unks3_1: [u32; 12],
994
995    // offset 100
996    pub ext_meshes: [u32; 2],
997
998    // TODO: always 0?
999    // TODO: offset for 10111?
1000    pub unks3_2: [u32; 2],
1001
1002    pub model_unk8: u32,
1003
1004    pub unk3_3: u32,
1005
1006    #[br(parse_with = parse_opt_ptr32, offset = base_offset)]
1007    #[xc3(offset(u32))]
1008    pub model_unk7: Option<ModelUnk7>,
1009
1010    // offset 128
1011    pub morph_controllers: u32,
1012
1013    pub model_unk1: u32,
1014
1015    pub model_unk12: u32,
1016
1017    pub lod_data: u32,
1018
1019    pub alpha_table: u32,
1020
1021    pub unk_field2: u32,
1022
1023    pub model_unk9: u32,
1024
1025    // TODO: Completely different type than 10112?
1026    #[br(parse_with = parse_opt_ptr32, offset = base_offset)]
1027    #[xc3(offset(u32))]
1028    pub model_unk3: Option<ModelUnk3>,
1029
1030    // offset 160
1031    pub model_unk13: u32,
1032    pub model_unk14: u32,
1033    pub model_unk15: u32,
1034}
1035
1036// Use an enum since even the largest size can have all offsets as null.
1037// i.e. the nullability of the offsets does not determine the size.
1038#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1039#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
1040#[br(import { size: u32, base_offset: u64 })]
1041pub enum ModelsExtraData {
1042    #[br(pre_assert(size == 160))]
1043    Unk1,
1044
1045    #[br(pre_assert(size == 164))]
1046    Unk2(#[br(args_raw(base_offset))] ModelsExtraDataUnk2),
1047
1048    #[br(pre_assert(size == 168))]
1049    Unk3(#[br(args_raw(base_offset))] ModelsExtraDataUnk3),
1050
1051    #[br(pre_assert(size == 200))]
1052    Unk4(#[br(args_raw(base_offset))] ModelsExtraDataUnk4),
1053
1054    #[br(pre_assert(size == 204))]
1055    Unk5(#[br(args_raw(base_offset))] ModelsExtraDataUnk5),
1056}
1057
1058// TODO: add asserts to all padding fields?
1059// 164 total bytes
1060#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1061#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
1062#[br(import_raw(base_offset: u64))]
1063pub struct ModelsExtraDataUnk2 {
1064    #[br(parse_with = parse_opt_ptr32)]
1065    #[br(args { offset: base_offset, inner: base_offset })]
1066    #[xc3(offset(u32))]
1067    pub model_unk10: Option<ModelUnk10>,
1068}
1069
1070// 168 total bytes
1071#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1072#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
1073#[br(import_raw(base_offset: u64))]
1074pub struct ModelsExtraDataUnk3 {
1075    #[br(parse_with = parse_opt_ptr32)]
1076    #[br(args { offset: base_offset, inner: base_offset })]
1077    #[xc3(offset(u32))]
1078    pub model_unk10: Option<ModelUnk10>,
1079
1080    #[br(parse_with = parse_opt_ptr32, offset = base_offset)]
1081    #[xc3(offset(u32))]
1082    pub model_unk5: Option<ModelUnk5>,
1083}
1084
1085// 200 total bytes
1086#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1087#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
1088#[br(import_raw(base_offset: u64))]
1089pub struct ModelsExtraDataUnk4 {
1090    #[br(parse_with = parse_opt_ptr32)]
1091    #[br(args { offset: base_offset, inner: base_offset })]
1092    #[xc3(offset(u32))]
1093    pub model_unk10: Option<ModelUnk10>,
1094
1095    #[br(parse_with = parse_opt_ptr32, offset = base_offset)]
1096    #[xc3(offset(u32))]
1097    pub model_unk5: Option<ModelUnk5>,
1098
1099    #[br(parse_with = parse_opt_ptr32, offset = base_offset)]
1100    #[xc3(offset(u32))]
1101    pub model_unk6: Option<ModelUnk6>,
1102
1103    // TODO: padding?
1104    pub unk: Option<[u32; 7]>,
1105}
1106
1107// 204 total bytes
1108#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1109#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
1110#[br(import_raw(base_offset: u64))]
1111pub struct ModelsExtraDataUnk5 {
1112    #[br(parse_with = parse_opt_ptr32)]
1113    #[br(args { offset: base_offset, inner: base_offset })]
1114    #[xc3(offset(u32))]
1115    pub model_unk10: Option<ModelUnk10>,
1116
1117    #[br(parse_with = parse_opt_ptr32, offset = base_offset)]
1118    #[xc3(offset(u32))]
1119    pub model_unk5: Option<ModelUnk5>,
1120
1121    #[br(parse_with = parse_opt_ptr32, offset = base_offset)]
1122    #[xc3(offset(u32))]
1123    pub model_unk6: Option<ModelUnk6>,
1124
1125    // TODO: padding?
1126    pub unk: Option<[u32; 8]>,
1127}
1128
1129/// A collection of meshes where each [MeshV111] represents one draw call.
1130///
1131/// Each [ModelV111] has an associated [VertexData] containing vertex and index buffers.
1132#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1133#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
1134#[br(import_raw(base_offset: u64))]
1135pub struct ModelV111 {
1136    #[br(parse_with = parse_offset32_count32, offset = base_offset)]
1137    #[xc3(offset_count(u32, u32))]
1138    pub meshes: Vec<MeshV111>,
1139
1140    // TODO: flags?
1141    pub unk1: u32, // 0, 64, 320
1142
1143    // TODO: Slightly larger than a volume containing all vertex buffers?
1144    /// The minimum XYZ coordinates of the bounding volume.
1145    pub max_xyz: [f32; 3],
1146    /// The maximum XYZ coordinates of the bounding volume.
1147    pub min_xyz: [f32; 3],
1148    // TODO: how to calculate this?
1149    pub bounding_radius: f32,
1150    pub unks1: [u32; 3],  // always 0?
1151    pub unk2: (u16, u16), // TODO: rendering related?
1152    // TODO: padding?
1153    pub unks: [u32; 3],
1154}
1155
1156/// A collection of meshes where each [MeshV112] represents one draw call.
1157///
1158/// Each [ModelV112] has an associated [VertexData] containing vertex and index buffers.
1159#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1160#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
1161#[br(import_raw(base_offset: u64))]
1162pub struct ModelV112 {
1163    #[br(parse_with = parse_offset32_count32, offset = base_offset)]
1164    #[xc3(offset_count(u32, u32))]
1165    pub meshes: Vec<MeshV112>,
1166
1167    // TODO: flags?
1168    pub unk1: u32, // 0, 64, 320
1169
1170    // TODO: Slightly larger than a volume containing all vertex buffers?
1171    /// The minimum XYZ coordinates of the bounding volume.
1172    pub max_xyz: [f32; 3],
1173    /// The maximum XYZ coordinates of the bounding volume.
1174    pub min_xyz: [f32; 3],
1175    // TODO: how to calculate this?
1176    pub bounding_radius: f32,
1177    pub unks1: [u32; 3],  // always 0?
1178    pub unk2: (u16, u16), // TODO: rendering related?
1179    // TODO: padding?
1180    pub unks: [u32; 3],
1181}
1182
1183// TODO: Figure out remaining indices.
1184/// Flags and resources associated with a single draw call.
1185#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1186#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
1187pub struct MeshV111 {
1188    pub flags1: u32,
1189    pub flags2: MeshRenderFlags2,
1190    /// Index into [vertex_buffers](../vertex/struct.VertexData.html#structfield.vertex_buffers)
1191    /// for the associated [VertexData].
1192    pub vertex_buffer_index: u16,
1193    /// Index into [index_buffers](../vertex/struct.VertexData.html#structfield.index_buffers)
1194    /// for the associated [VertexData].
1195    pub index_buffer_index: u16,
1196    /// Index into [index_buffers](../vertex/struct.VertexData.html#structfield.index_buffers)
1197    /// for the associated [VertexData] for the depth only draw call used for shadow rendering.
1198    /// Custom models can use the same value as [index_buffer_index](#structfield.index_buffer_index).
1199    pub index_buffer_index2: u16,
1200    /// Index into [materials](struct.Materials.html#structfield.materials).
1201    pub material_index: u16,
1202    pub unk2: u32, // 0
1203    pub unk3: u16, // 0
1204    /// Index into [ext_meshes](struct.Models.html#structfield.ext_meshes).
1205    // TODO: enabled via a flag?
1206    pub ext_mesh_index: u16,
1207    pub unk4: u32, // 0
1208    pub unk5: u16, // TODO: used mostly for outline meshes?
1209    /// 1-based index into [items](struct.LodData.html#structfield.items).
1210    pub lod_item_index: u8,
1211    pub unk_mesh_index2: u8, // 1 to 20?
1212    /// Index into [items](struct.AlphaTable.html#structfield.items).
1213    pub alpha_table_index: u16,
1214    pub unk6: u16, // TODO: used mostly for outline meshes?
1215    // TODO: -1 for xc3 for "base" meshes and always 0 for xc1 and xc2
1216    // TODO: index for parent or base mesh for speff materials?
1217    pub base_mesh_index: i32,
1218    pub unk8: u32, // 0, 1
1219    pub unk9: u32, // 0
1220    pub unk10: [u32; 4],
1221}
1222
1223/// Flags and resources associated with a single draw call.
1224#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1225#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
1226pub struct MeshV112 {
1227    pub flags1: u32, // TODO: possible bits that are set, check outline, speff, etc
1228    pub flags2: MeshRenderFlags2,
1229    /// Index into [vertex_buffers](../vertex/struct.VertexData.html#structfield.vertex_buffers)
1230    /// for the associated [VertexData].
1231    pub vertex_buffer_index: u16,
1232    /// Index into [index_buffers](../vertex/struct.VertexData.html#structfield.index_buffers)
1233    /// for the associated [VertexData].
1234    pub index_buffer_index: u16,
1235    /// Index into [index_buffers](../vertex/struct.VertexData.html#structfield.index_buffers)
1236    /// for the associated [VertexData] for the depth only draw call used for shadow rendering.
1237    /// Custom models can use the same value as [index_buffer_index](#structfield.index_buffer_index).
1238    pub index_buffer_index2: u16,
1239    /// Index into [materials](struct.Materials.html#structfield.materials).
1240    pub material_index: u16,
1241    pub unk2: u32, // 0
1242    pub unk3: u16, // 0
1243    /// Index into [ext_meshes](struct.Models.html#structfield.ext_meshes).
1244    // TODO: enabled via a flag?
1245    pub ext_mesh_index: u16,
1246    pub unk4: u32, // 0
1247    pub unk5: u16, // TODO: used mostly for outline meshes?
1248    /// 1-based index into [items](struct.LodData.html#structfield.items).
1249    pub lod_item_index: u8,
1250    pub unk_mesh_index2: u8, // 1 to 20?
1251    /// Index into [items](struct.AlphaTable.html#structfield.items).
1252    pub alpha_table_index: u16,
1253    pub unk6: u16, // TODO: used mostly for outline meshes?
1254    // TODO: -1 for xc3 for "base" meshes and always 0 for xc1 and xc2
1255    // TODO: index for parent or base mesh for speff materials?
1256    pub base_mesh_index: i32,
1257    pub unk8: u32, // 0, 1
1258    pub unk9: u32, // 0
1259}
1260
1261// TODO: remaining bits affect skinning?
1262/// Flags to determine how to draw a [MeshV112].
1263#[bitsize(32)]
1264#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1265#[derive(DebugBits, TryFromBits, BinRead, BinWrite, PartialEq, Clone, Copy)]
1266#[br(try_map = |x: u32| x.try_into().map_err(|e| format!("{e:?}")))]
1267#[bw(map = |&x| u32::from(x))]
1268pub struct MeshRenderFlags2 {
1269    /// The render pass for this draw call.
1270    pub render_pass: MeshRenderPass,
1271    pub unk5: u28,
1272}
1273
1274// TODO: 16 also draws in the first pass but earlier?
1275// TODO: Also depends on technique type?
1276/// The render pass for this draw call.
1277#[bitsize(4)]
1278#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1279#[derive(Debug, TryFromBits, PartialEq, Clone, Copy)]
1280pub enum MeshRenderPass {
1281    /// The first opaque pass with depth writes.
1282    Unk0 = 0,
1283    /// The first opaque pass with depth writes but earlier in the pass.
1284    Unk1 = 1,
1285    /// The alpha pass after the deferred pass without depth writes.
1286    Unk2 = 2,
1287    Unk4 = 4, // TODO: xc1 maps?
1288    /// The alpha pass immediately after [MeshRenderPass::Unk0] without depth writes.
1289    Unk8 = 8,
1290}
1291
1292/// Flags to determine what data is present in [ModelsV112].
1293#[bitsize(32)]
1294#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1295#[derive(DebugBits, FromBits, BinRead, BinWrite, PartialEq, Clone, Copy)]
1296#[br(map = u32::into)]
1297#[bw(map = |&x| u32::from(x))]
1298pub struct ModelsFlags {
1299    pub unk1: bool,
1300    pub has_model_unk8: bool,
1301    pub unk3: bool,
1302    pub unk4: bool,
1303    pub unk5: bool,
1304    pub unk6: bool,
1305    pub has_model_unk7: bool,
1306    pub unk8: bool,
1307    pub unk9: bool,
1308    pub unk10: bool,
1309    pub has_morph_controllers: bool,
1310    pub has_model_unk1: bool,
1311    pub has_model_unk3: bool,
1312    pub unk14: bool,
1313    pub unk15: bool,
1314    pub has_skinning: bool,
1315    pub unk17: bool,
1316    pub has_lod_data: bool,
1317    pub has_alpha_table: bool,
1318    pub unk20: bool,
1319    pub unk21: bool,
1320    pub unk22: bool,
1321    pub unk23: bool,
1322    pub unk24: bool,
1323    pub unk25: bool,
1324    pub unk26: bool,
1325    pub unk27: bool,
1326    pub unk28: bool,
1327    pub unk29: bool,
1328    pub unk30: bool,
1329    pub unk31: bool,
1330    pub unk32: bool,
1331}
1332
1333/// `ExtMesh` in the Xenoblade 2 binary.
1334#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1335#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
1336#[br(import_raw(base_offset: u64))]
1337pub struct ExtMesh {
1338    #[br(parse_with = parse_string_ptr32, offset = base_offset)]
1339    #[xc3(offset(u32))]
1340    pub name1: String,
1341
1342    // TODO: Always an empty string?
1343    #[br(parse_with = parse_string_ptr32, offset = base_offset)]
1344    #[xc3(offset(u32))]
1345    pub name2: String,
1346
1347    pub flags: ExtMeshFlags,
1348    pub unk2: u16,
1349    pub unk3: u32,
1350}
1351
1352#[bitsize(16)]
1353#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1354#[derive(DebugBits, FromBits, BinRead, BinWrite, PartialEq, Clone, Copy)]
1355#[br(map = u16::into)]
1356#[bw(map = |&x| u16::from(x))]
1357pub struct ExtMeshFlags {
1358    pub unk1: bool, // true
1359    pub unk2: bool, // false
1360    pub unk3: bool, // false
1361    /// Whether to initially skip rendering assigned meshes.
1362    pub start_hidden: bool,
1363    pub unk5: bool,
1364    pub unk6: bool, // 0, 1 (xc3 only)
1365    pub unk: u10,   // 0
1366}
1367
1368#[binread]
1369#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1370#[derive(Debug, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
1371#[br(stream = r)]
1372#[xc3(base_offset)]
1373pub struct MorphControllers {
1374    #[br(temp, try_calc = r.stream_position())]
1375    base_offset: u64,
1376
1377    // TODO: same count as morph targets per descriptor in vertex data?
1378    #[br(parse_with = parse_offset32_count32, args { offset: base_offset, inner: base_offset })]
1379    #[xc3(offset_count(u32, u32))]
1380    pub controllers: Vec<MorphController>,
1381
1382    pub unk1: u32,
1383
1384    // TODO: padding?
1385    pub unk: [u32; 3],
1386}
1387
1388#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1389#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
1390#[br(import_raw(base_offset: u64))]
1391pub struct MorphController {
1392    #[br(parse_with = parse_string_ptr32, offset = base_offset)]
1393    #[xc3(offset(u32))]
1394    pub name1: String,
1395
1396    // TODO: Is one of these names for the ModelUnk1Item1?
1397    #[br(parse_with = parse_string_opt_ptr32, offset = base_offset)]
1398    #[xc3(offset(u32))]
1399    pub name2: Option<String>,
1400
1401    pub unk1: u16, // 7?
1402    pub unk2: u16, // TODO: index into ModelUnk1Item1 used for animation tracks?
1403    pub unk3: u16, // 0?
1404    pub unk4: u16, // 3?
1405
1406    // TODO: padding?
1407    pub unk: [u32; 3],
1408}
1409
1410#[binread]
1411#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1412#[derive(Debug, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
1413#[br(stream = r)]
1414#[xc3(base_offset)]
1415pub struct ModelUnk3 {
1416    #[br(temp, try_calc = r.stream_position())]
1417    base_offset: u64,
1418
1419    #[br(parse_with = parse_count32_offset32, args { offset: base_offset, inner: base_offset })]
1420    #[xc3(count_offset(u32, u32))]
1421    pub items: Vec<ModelUnk3Item>,
1422
1423    // TODO: padding?
1424    pub unk: [u32; 4],
1425}
1426
1427#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1428#[derive(Debug, BinRead, Xc3Write, PartialEq, Clone)]
1429#[br(import_raw(base_offset: u64))]
1430pub struct ModelUnk3Item {
1431    // DECL_GBL_CALC
1432    #[br(parse_with = parse_string_ptr32, offset = base_offset)]
1433    #[xc3(offset(u32))]
1434    pub name: String,
1435    pub unk1: u32, // 0?
1436    pub unk2: u32,
1437
1438    #[br(parse_with = parse_offset32_count32, offset = base_offset)]
1439    #[xc3(offset_count(u32, u32))]
1440    pub unk3: Vec<u16>,
1441}
1442
1443#[binread]
1444#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1445#[derive(Debug, Xc3Write, PartialEq, Clone)]
1446#[br(stream = r)]
1447#[xc3(base_offset)]
1448pub struct Unk8 {
1449    #[br(temp, try_calc = r.stream_position())]
1450    base_offset: u64,
1451
1452    pub unk1: u32,
1453
1454    #[br(parse_with = parse_count32_offset32)]
1455    #[br(args { offset: base_offset, inner: base_offset })]
1456    #[xc3(count_offset(u32, u32))]
1457    pub unk2: Vec<Unk8Item>,
1458
1459    #[br(parse_with = parse_ptr32)]
1460    #[br(args { offset: base_offset, inner: args! { count: unk2.len() } })]
1461    #[xc3(offset(u32), align(16))]
1462    pub unk3: Vec<[[f32; 4]; 4]>,
1463
1464    // TODO: padding?
1465    pub unk: [u32; 4],
1466}
1467
1468#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1469#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
1470#[br(import_raw(base_offset: u64))]
1471pub struct Unk8Item {
1472    #[br(parse_with = parse_string_ptr32, offset = base_offset)]
1473    #[xc3(offset(u32))]
1474    pub name: String,
1475    pub unk1: u32,
1476    pub unk2: [[f32; 4]; 4],
1477    pub unk3: u32,
1478}
1479
1480/// A table for mapping [ExtMesh] to [LodItem].
1481#[binread]
1482#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1483#[derive(Debug, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
1484#[br(stream = r)]
1485#[xc3(base_offset)]
1486pub struct AlphaTable {
1487    #[br(temp, try_calc = r.stream_position())]
1488    base_offset: u64,
1489
1490    // TODO: used to assign ext mesh and lod alpha to a mesh?
1491    // TODO: assigned to meshes in order based on their ext mesh and lod?
1492    /// A mapping table for `(ext_mesh_index + 1, lod_item1_index + 1)`
1493    #[br(parse_with = parse_offset32_count32, offset = base_offset)]
1494    #[xc3(offset_count(u32, u32))]
1495    pub items: Vec<(u16, u16)>,
1496
1497    // TODO: padding?
1498    pub unks: [u32; 4],
1499}
1500
1501#[binread]
1502#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1503#[derive(Debug, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
1504#[br(stream = r)]
1505#[xc3(base_offset)]
1506pub struct ModelUnk5 {
1507    #[br(temp, try_calc = r.stream_position())]
1508    base_offset: u64,
1509
1510    // TODO: DS_ names?
1511    #[br(parse_with = parse_count32_offset32)]
1512    #[br(args { offset: base_offset, inner: base_offset })]
1513    #[xc3(count_offset(u32, u32))]
1514    pub items: Vec<StringOffset32>,
1515
1516    // TODO: padding?
1517    pub unks: [u32; 4],
1518}
1519
1520#[binread]
1521#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1522#[derive(Debug, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
1523#[br(stream = r)]
1524#[xc3(base_offset)]
1525pub struct ModelUnk6 {
1526    #[br(temp, try_calc = r.stream_position())]
1527    base_offset: u64,
1528
1529    // TODO: What type is this?
1530    #[br(parse_with = parse_count32_offset32, offset = base_offset)]
1531    #[xc3(count_offset(u32, u32))]
1532    pub items: Vec<[u32; 2]>,
1533
1534    // TODO: padding?
1535    pub unks: [u32; 4],
1536}
1537
1538#[binread]
1539#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1540#[derive(Debug, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
1541#[br(stream = r)]
1542#[xc3(base_offset)]
1543pub struct ModelUnk7 {
1544    #[br(temp, try_calc = r.stream_position())]
1545    base_offset: u64,
1546
1547    // TODO: What type is this?
1548    #[br(parse_with = parse_count32_offset32, offset = base_offset)]
1549    #[xc3(count_offset(u32, u32))]
1550    pub items: Vec<[f32; 9]>,
1551
1552    // TODO: padding?
1553    pub unks: [u32; 4],
1554}
1555
1556#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1557#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
1558#[br(import_raw(base_offset: u64))]
1559pub struct ModelUnk8 {
1560    // TODO: What type is this?
1561    #[br(parse_with = parse_offset32_count32, offset = base_offset)]
1562    #[xc3(offset_count(u32, u32))]
1563    pub unk1: Vec<[u32; 2]>,
1564
1565    // TODO: What type is this?
1566    #[br(parse_with = parse_offset32_count32, offset = base_offset)]
1567    #[xc3(offset_count(u32, u32))]
1568    pub unk2: Vec<[f32; 4]>,
1569
1570    // TODO: padding?
1571    pub unks: [u32; 2],
1572}
1573
1574#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1575#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
1576#[br(import_raw(base_offset: u64))]
1577pub struct ModelUnk9 {
1578    // TODO: flags?
1579    // xc1: 1, 2, 3, 4, 5
1580    // xc3: 10000
1581    pub unk1: u32,
1582
1583    #[br(args { unk1, base_offset})]
1584    pub inner: ModelUnk9Inner,
1585}
1586
1587#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1588#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
1589#[br(import { unk1: u32, base_offset: u64 })]
1590pub enum ModelUnk9Inner {
1591    // TODO: Safe to assume that this covers other cases?
1592    #[br(pre_assert(unk1 != 10000))]
1593    Unk0(ModelUnk9InnerUnk0),
1594
1595    #[br(pre_assert(unk1 == 10000))]
1596    Unk1(#[br(args_raw(base_offset))] ModelUnk9InnerUnk1),
1597}
1598
1599#[binread]
1600#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1601#[derive(Debug, Xc3Write, PartialEq, Clone)]
1602#[br(stream = r)]
1603#[xc3(base_offset)]
1604pub struct ModelUnk9InnerUnk0 {
1605    // Subtract the unk1 size.
1606    #[br(temp, try_calc = r.stream_position().map(|p| p - 4))]
1607    base_offset: u64,
1608
1609    #[br(parse_with = parse_offset32_count32, offset = base_offset)]
1610    #[xc3(offset_count(u32, u32))]
1611    pub items1: Vec<(u16, u16)>,
1612
1613    #[br(parse_with = parse_offset32_count32, offset = base_offset)]
1614    #[xc3(offset_count(u32, u32))]
1615    pub items2: Vec<(u16, u16)>,
1616
1617    // TODO: padding?
1618    pub unk: [u32; 4],
1619}
1620
1621#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1622#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
1623#[br(import_raw(base_offset: u64))]
1624pub struct ModelUnk9InnerUnk1 {
1625    // TODO: These offsets are relative to the start of the struct for xc1?
1626    #[br(parse_with = parse_offset32_count32, offset = base_offset)]
1627    #[xc3(offset_count(u32, u32))]
1628    pub items: Vec<ModelUnk9Buffer>,
1629
1630    #[br(parse_with = parse_ptr32)]
1631    #[br(args { offset: base_offset, inner: args! { count: model_unk9_buffer_length(&items) } })]
1632    #[xc3(offset(u32))]
1633    pub buffer: Vec<u8>,
1634
1635    // TODO: Some sort of optional count?
1636    pub unk2: u32,
1637
1638    // TODO: padding?
1639    pub unk: [u32; 3],
1640}
1641
1642#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1643#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
1644pub struct ModelUnk9Buffer {
1645    // TODO: items are 48 byte structs of f32 and i16 in buffer?
1646    pub offset: u32,
1647    pub count: u32,
1648}
1649
1650#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1651#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
1652#[br(import_raw(base_offset: u64))]
1653pub struct ModelUnk10 {
1654    #[br(parse_with = parse_offset32_count32, offset = base_offset)]
1655    #[xc3(offset_count(u32, u32))]
1656    pub unk1: Vec<u32>,
1657}
1658
1659#[binread]
1660#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1661#[derive(Debug, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
1662#[br(stream = r)]
1663#[xc3(base_offset)]
1664pub struct ModelUnk11 {
1665    #[br(temp, try_calc = r.stream_position())]
1666    base_offset: u64,
1667
1668    #[br(parse_with = parse_count32_offset32, offset = base_offset)]
1669    #[xc3(count_offset(u32, u32))]
1670    pub unk1: Vec<[u32; 6]>,
1671
1672    #[br(parse_with = parse_count32_offset32, offset = base_offset)]
1673    #[xc3(count_offset(u32, u32))]
1674    pub unk2: Vec<[u32; 2]>,
1675
1676    // TODO: padding?
1677    pub unks: [u32; 4],
1678}
1679
1680#[binread]
1681#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1682#[derive(Debug, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
1683#[br(stream = r)]
1684#[xc3(base_offset)]
1685pub struct ModelUnk12 {
1686    #[br(temp, try_calc = r.stream_position())]
1687    base_offset: u64,
1688
1689    #[br(parse_with = parse_offset32_count32, offset = base_offset)]
1690    #[xc3(offset_count(u32, u32))]
1691    pub items: Vec<ModelUnk12Item>,
1692
1693    #[br(parse_with = parse_offset32_count32, offset = base_offset)]
1694    #[xc3(offset_count(u32, u32))]
1695    pub indices: Vec<u32>,
1696
1697    pub unk2: u32,
1698
1699    // TODO: array of 10 u16?
1700    pub unk: [u16; 22],
1701}
1702
1703#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1704#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
1705pub struct ModelUnk12Item {
1706    pub unk1: [f32; 4],
1707    pub unk2: [f32; 4],
1708    pub unk3: [f32; 4],
1709    pub unk4: [f32; 4],
1710}
1711
1712// TODO: Some sort of float animation for eyes, morphs, etc?
1713#[binread]
1714#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1715#[derive(Debug, Xc3Write, PartialEq, Clone)]
1716#[br(stream = r)]
1717#[xc3(base_offset)]
1718pub struct ModelUnk1 {
1719    #[br(temp, try_calc = r.stream_position())]
1720    base_offset: u64,
1721
1722    // TODO: Related to ext meshes?
1723    // TODO: same count as track indices for xc2 extra animation for morph targets?
1724    #[br(parse_with = parse_offset32_count32)]
1725    #[br(args { offset: base_offset, inner: base_offset })]
1726    #[xc3(offset_count(u32, u32), align(4))]
1727    pub items1: Vec<ModelUnk1Item1>,
1728
1729    #[br(parse_with = parse_offset32_count32, offset = base_offset)]
1730    #[xc3(offset_count(u32, u32))]
1731    pub items2: Vec<ModelUnk1Item2>,
1732
1733    // TODO: Default values for items1?
1734    // TODO: same count as track indices for xc2 extra animation for morph targets?
1735    #[br(parse_with = parse_ptr32)]
1736    #[br(args { offset: base_offset, inner: args! { count: items1.len() }})]
1737    #[xc3(offset(u32))]
1738    pub items3: Vec<f32>,
1739
1740    pub unk1: u32, // 0 or 1?
1741
1742    #[br(parse_with = parse_offset32_count32, offset = base_offset)]
1743    #[xc3(offset_count(u32, u32))]
1744    pub items4: Vec<[u16; 10]>,
1745
1746    // flags?
1747    pub unk4: u32,
1748    pub unk5: u32,
1749    // TODO: not present for xc2?
1750    // TODO: Is this the correct check?
1751    #[br(if(unk4 != 0 || unk5 != 0))]
1752    #[br(args_raw(base_offset))]
1753    pub extra: Option<ModelUnk1Extra>,
1754}
1755
1756#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1757#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
1758#[br(import_raw(base_offset: u64))]
1759pub struct ModelUnk1Extra {
1760    #[br(parse_with = parse_opt_ptr32, offset = base_offset)]
1761    #[xc3(offset(u32))]
1762    pub unk_inner: Option<ModelUnk1Inner>,
1763
1764    // TODO: only 12 bytes for chr/ch/ch01022012.wimdo?
1765    pub unk: [u32; 4],
1766}
1767
1768// TODO: Another table like the alpha table?
1769#[binread]
1770#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1771#[derive(Debug, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
1772#[br(stream = r)]
1773#[xc3(base_offset)]
1774pub struct ModelUnk1Inner {
1775    #[br(temp, try_calc = r.stream_position())]
1776    base_offset: u64,
1777
1778    // TODO: A mapping table for `(model_unk1_item1_index + 1, ???)`
1779    // TODO: What indexes into this table?
1780    #[br(parse_with = parse_offset32_count32, offset = base_offset)]
1781    #[xc3(offset_count(u32, u32))]
1782    pub items1: Vec<(u16, u16)>,
1783
1784    // TODO: 0..N-1 arranged in a different order?
1785    #[br(parse_with = parse_ptr32)]
1786    #[br(args {
1787        offset: base_offset,
1788        inner: args! { count: items1.iter().map(|(i, j)| (*i + *j) as usize).max().unwrap_or_default() }
1789    })]
1790    #[xc3(offset(u32))]
1791    pub items2: Vec<u16>,
1792
1793    // TODO: padding?
1794    pub unks: [u32; 5],
1795}
1796
1797#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1798#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
1799#[br(import_raw(base_offset: u64))]
1800pub struct ModelUnk1Item1 {
1801    #[br(parse_with = parse_string_ptr32, offset = base_offset)]
1802    #[xc3(offset(u32))]
1803    pub name: String,
1804    // TODO: padding?
1805    pub unk: [u32; 3],
1806}
1807
1808#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1809#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
1810pub struct ModelUnk1Item2 {
1811    pub unk1: u16,
1812    pub unk2: u16,
1813    pub unk3: u32,
1814    pub unk4: u32,
1815    pub unk5: u32,
1816    pub unk6: u32,
1817}
1818
1819#[binread]
1820#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1821#[derive(Debug, Xc3Write, PartialEq, Clone)]
1822#[br(stream = r)]
1823#[xc3(base_offset)]
1824pub struct LodData {
1825    #[br(temp, try_calc = r.stream_position())]
1826    base_offset: u64,
1827
1828    pub unk1: u32, // 0?
1829
1830    // TODO: Count related to number of mesh lod values?
1831    #[br(parse_with = parse_offset32_count32, offset = base_offset)]
1832    #[xc3(offset_count(u32, u32), align(16))]
1833    pub items: Vec<LodItem>,
1834
1835    #[br(parse_with = parse_offset32_count32, offset = base_offset)]
1836    #[xc3(offset_count(u32, u32))]
1837    pub groups: Vec<LodGroup>,
1838
1839    pub unks: [u32; 4],
1840}
1841
1842// TODO: is lod: 0 in the mxmd special?
1843#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1844#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
1845pub struct LodItem {
1846    pub unk1: [u32; 4], // [0, 0, 0, 0]
1847    pub unk2: f32,      // distance or radius?
1848    pub unk3: u8,       // 0
1849    pub index: u8,      // index within lod group?
1850    pub unk5: u8,       // 1, 2
1851    pub unk6: u8,       // 0
1852    pub unk7: [u32; 2], // [0, 0]
1853}
1854
1855#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1856#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
1857pub struct LodGroup {
1858    /// Index into [items](struct.LodData.html#structfield.items) for the highest level of detail.
1859    pub base_lod_index: u16,
1860    /// The number of LOD levels in this group.
1861    pub lod_count: u16,
1862}
1863
1864/// A collection of [Mibl](crate::mibl::Mibl) textures embedded in the current file.
1865#[binread]
1866#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1867#[derive(Debug, Xc3Write, PartialEq, Clone)]
1868#[br(stream = r)]
1869#[xc3(base_offset)]
1870pub struct PackedTextures {
1871    #[br(temp, try_calc = r.stream_position())]
1872    base_offset: u64,
1873
1874    #[br(parse_with = parse_count32_offset32, args { offset: base_offset, inner: base_offset })]
1875    #[xc3(count_offset(u32, u32))]
1876    pub textures: Vec<PackedTexture>,
1877
1878    pub unk2: u32,
1879
1880    #[xc3(shared_offset)]
1881    pub strings_offset: u32,
1882}
1883
1884/// A single [Mibl](crate::mibl::Mibl) texture.
1885#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1886#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
1887#[br(import_raw(base_offset: u64))]
1888pub struct PackedTexture {
1889    pub usage: TextureUsage,
1890
1891    #[br(parse_with = parse_count32_offset32, offset = base_offset)]
1892    #[xc3(count_offset(u32, u32), align(4096))]
1893    pub mibl_data: Vec<u8>,
1894
1895    #[br(parse_with = parse_string_ptr32, offset = base_offset)]
1896    #[xc3(offset(u32))]
1897    pub name: String,
1898}
1899
1900/// References to [Mibl](crate::mibl::Mibl) textures in a separate file.
1901#[binread]
1902#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1903#[derive(Debug, Xc3Write, PartialEq, Clone)]
1904#[br(stream = r)]
1905#[xc3(base_offset)]
1906pub struct PackedExternalTextures<U>
1907where
1908    U: Xc3Write + 'static,
1909    for<'a> U: BinRead<Args<'a> = ()>,
1910    for<'a> U::Offsets<'a>: Xc3WriteOffsets<Args = ()>,
1911{
1912    #[br(temp, try_calc = r.stream_position())]
1913    base_offset: u64,
1914
1915    // TODO: Always identical to low textures in msrd?
1916    #[br(parse_with = parse_count32_offset32, args { offset: base_offset, inner: base_offset })]
1917    #[xc3(count_offset(u32, u32), align(2))]
1918    pub textures: Vec<PackedExternalTexture<U>>,
1919
1920    pub unk2: u32, // 0
1921
1922    #[xc3(shared_offset)]
1923    pub strings_offset: u32,
1924}
1925
1926#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1927#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
1928#[br(import_raw(base_offset: u64))]
1929pub struct PackedExternalTexture<U>
1930where
1931    U: Xc3Write + 'static,
1932    for<'a> U: BinRead<Args<'a> = ()>,
1933    for<'a> U::Offsets<'a>: Xc3WriteOffsets<Args = ()>,
1934{
1935    pub usage: U,
1936
1937    /// The size of the texture file in bytes.
1938    pub length: u32,
1939    /// The offset of the texture file in bytes.
1940    pub offset: u32,
1941
1942    #[br(parse_with = parse_string_ptr32, offset = base_offset)]
1943    #[xc3(offset(u32))]
1944    pub name: String,
1945}
1946
1947// TODO: These are big endian?
1948// TODO: Are these some sort of flags?
1949// TODO: Use these for default assignments without database?
1950// TODO: Possible to guess temp texture channels?
1951/// Hints on how the texture is used.
1952/// Actual usage is determined by the shader.
1953#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1954#[derive(Debug, BinRead, BinWrite, Clone, Copy, PartialEq, Eq, Hash)]
1955#[brw(repr(u32))]
1956pub enum TextureUsage {
1957    Unk0 = 0,
1958    /// MTL, AMB, GLO, SHY, MASK, SPC, DPT, VEL, temp0001, ...
1959    Temp = 1048576,
1960    Unk6 = 1074790400,
1961    Nrm = 1179648,
1962    Unk13 = 131072,
1963    WavePlus = 136314882,
1964    Col = 2097152,
1965    Unk8 = 2162689,
1966    Alp = 2228224,
1967    Unk = 268435456,
1968    Unk21 = 269615104,
1969    Alp2 = 269484032,
1970    Col2 = 270532608,
1971    Unk11 = 270663680,
1972    Unk9 = 272629760,
1973    Alp3 = 273678336,
1974    Nrm2 = 273809408,
1975    Col3 = 274726912,
1976    Unk3 = 274857984,
1977    Unk2 = 275775488,
1978    Unk20 = 287309824,
1979    Unk17 = 3276800,
1980    F01 = 403701762, // 3D?
1981    Unk4 = 4194304,
1982    Unk7 = 536870912,
1983    Unk15 = 537001984,
1984    /// AO, OCL2, temp0000, temp0001, ...
1985    Temp2 = 537919488,
1986    Unk14 = 538050560,
1987    Col4 = 538968064,
1988    Alp4 = 539099136,
1989    Unk12 = 540147712,
1990    Unk18 = 65537,
1991    Unk19 = 805306368,
1992    Unk5 = 807403520,
1993    Unk10 = 807534592,
1994    VolTex = 811597824,
1995    Unk16 = 811728896,
1996
1997    // TODO: separate enum for XCX DE only?
1998    Unk22 = 6291456,
1999    Unk23 = 5242880,
2000    Unk24 = 69206016,
2001    Unk25 = 7340032,
2002    Unk26 = 3145728,
2003    Unk27 = 73400320,
2004    Unk28 = 35651584,
2005    Unk29 = 34734080,
2006    Unk30 = 34603008,
2007}
2008
2009// xc1: 40 bytes
2010// xc2: 32, 36, 40 bytes
2011// xc3: 52, 60 bytes
2012/// Information for the skinned bones used by this model.
2013#[binread]
2014#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
2015#[derive(Debug, Xc3Write, PartialEq, Clone)]
2016#[br(stream = r)]
2017#[xc3(base_offset)]
2018pub struct Skinning {
2019    #[br(temp, try_calc = r.stream_position())]
2020    base_offset: u64,
2021
2022    pub render_bone_count: u32,
2023    pub bone_count: u32,
2024
2025    // Estimate the struct size based on its first offset.
2026    #[br(temp, restore_position)]
2027    bones_offset: u32,
2028
2029    /// The bone list for the [BoneIndices](crate::vertex::DataType::BoneIndices) in the weights buffer.
2030    // TODO: Find a simpler way of writing this?
2031    // TODO: helper for separate count.
2032    #[br(parse_with = parse_ptr32)]
2033    #[br(args {
2034        offset: base_offset,
2035        inner: args! { count: bone_count as usize, inner: base_offset }
2036    })]
2037    #[xc3(offset(u32))]
2038    pub bones: Vec<Bone>,
2039
2040    /// Column-major inverse of the world transform for each bone in [bones](#structfield.bones).
2041    #[br(parse_with = parse_ptr32)]
2042    #[br(args { offset: base_offset, inner: args! { count: bone_count as usize } })]
2043    #[xc3(offset(u32), align(16))]
2044    pub inverse_bind_transforms: Vec<[[f32; 4]; 4]>,
2045
2046    // TODO: do these contain data for both types of constraints?
2047    #[br(parse_with = parse_opt_ptr32)]
2048    #[br(args { offset: base_offset, inner: args! { count: count_constraints(&bones) } })]
2049    #[xc3(offset(u32))]
2050    pub constraints: Option<Vec<BoneConstraint>>,
2051
2052    #[br(parse_with = parse_opt_ptr32)]
2053    #[br(args { offset: base_offset, inner: args! { count: count_bounds(&bones) } })]
2054    #[xc3(offset(u32))]
2055    pub bounds: Option<Vec<BoneBounds>>,
2056
2057    // TODO: 0..count-1?
2058    #[br(parse_with = parse_count32_offset32, offset = base_offset)]
2059    #[xc3(count_offset(u32, u32))]
2060    pub bone_indices: Vec<u16>,
2061
2062    // offset 32
2063    // Use nested options to skip fields entirely if not present.
2064    #[br(if(constraints.is_some()))]
2065    #[br(args_raw(base_offset))]
2066    pub unk_offset4: Option<SkinningUnkBones>,
2067
2068    #[br(if(bounds.is_some()))]
2069    #[br(args_raw(base_offset))]
2070    pub unk_offset5: Option<SkinningUnk5>,
2071
2072    // TODO: not present in xc2?
2073    // TODO: procedural bones?
2074    #[br(if(!bone_indices.is_empty()))]
2075    #[br(args_raw(base_offset))]
2076    pub as_bone_data: Option<SkinningAsBoneData>,
2077
2078    // TODO: Optional padding for xc3?
2079    // TODO: This doesn't always have correct padding?
2080    #[br(if(bones_offset == 52))]
2081    pub unk1: Option<[u32; 2]>,
2082
2083    #[br(if(bones_offset == 60))]
2084    pub unk2: Option<[u32; 4]>,
2085}
2086
2087#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
2088#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
2089#[br(import_raw(base_offset: u64))]
2090pub struct SkinningUnkBones {
2091    #[br(parse_with = parse_opt_ptr32)]
2092    #[br(args { offset: base_offset, inner: base_offset })]
2093    #[xc3(offset(u32))]
2094    pub unk_offset4: Option<UnkBones>,
2095}
2096
2097#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
2098#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
2099#[br(import_raw(base_offset: u64))]
2100pub struct SkinningUnk5 {
2101    #[br(parse_with = parse_opt_ptr32, offset = base_offset)]
2102    #[xc3(offset(u32))]
2103    pub unk_offset5: Option<SkeletonUnk5>,
2104}
2105
2106#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
2107#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
2108#[br(import_raw(base_offset: u64))]
2109pub struct SkinningAsBoneData {
2110    // TODO: procedural bones?
2111    #[br(parse_with = parse_opt_ptr32, args { offset: base_offset, inner: base_offset })]
2112    #[xc3(offset(u32))]
2113    pub as_bone_data: Option<AsBoneData>,
2114}
2115
2116#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
2117#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
2118pub struct BoneConstraint {
2119    pub fixed_offset: [f32; 3],
2120    pub max_distance: f32,
2121}
2122
2123#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
2124#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
2125pub struct BoneBounds {
2126    pub center: [f32; 4],
2127    pub size: [f32; 4],
2128}
2129
2130#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
2131#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
2132#[br(import_raw(base_offset: u64))]
2133pub struct Bone {
2134    #[br(parse_with = parse_string_ptr32, offset = base_offset)]
2135    #[xc3(offset(u32))]
2136    pub name: String,
2137    pub bounds_radius: f32,
2138    pub flags: BoneFlags,
2139    /// Index into [constraints](struct.Skinning.html#structfield.constraints)
2140    /// if [flags](#structfield.flags) enables any constraints and 0 otherwise.
2141    pub constraint_index: u8,
2142    /// Index into [bones](struct.Skinning.html#structfield.bones) of the parent bone
2143    /// if [flags](#structfield.flags) enables any constraints and 0 otherwise.
2144    pub parent_index: u8,
2145    /// Index into [bounds](struct.Skinning.html#structfield.bounds)
2146    /// if [flags](#structfield.flags) enables bounds and 0 otherwise.
2147    pub bounds_index: u32,
2148    // TODO: padding?
2149    pub unk: [u32; 2],
2150}
2151
2152#[bitsize(16)]
2153#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
2154#[derive(DebugBits, FromBits, BinRead, BinWrite, PartialEq, Clone, Copy)]
2155#[br(map = u16::into)]
2156#[bw(map = |&x| u16::from(x))]
2157pub struct BoneFlags {
2158    pub fixed_offset_constraint: bool,
2159    pub bounds_offset: bool,
2160    pub distance_constraint: bool,
2161    pub no_camera_overlap: bool,
2162    pub unk: u12,
2163}
2164
2165#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
2166#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
2167#[br(import_raw(base_offset: u64))]
2168pub struct UnkBones {
2169    #[br(parse_with = parse_offset32_count32, offset = base_offset)]
2170    #[xc3(offset_count(u32, u32))]
2171    pub bones: Vec<UnkBone>,
2172
2173    #[br(parse_with = parse_ptr32)]
2174    #[br(args { offset: base_offset, inner: args! { count: bones.len() }})]
2175    #[xc3(offset(u32), align(16))]
2176    pub unk_offset: Vec<[[f32; 4]; 4]>,
2177    // TODO: no padding?
2178}
2179
2180#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
2181#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
2182pub struct UnkBone {
2183    pub unk1: u32,
2184    /// Index in [bones](struct.Skeleton.html#structfield.bones).
2185    pub bone_index: u16,
2186    /// Index in [bones](struct.Skeleton.html#structfield.bones) of the parent bone.
2187    pub parent_index: u16,
2188    // TODO: padding?
2189    pub unks: [u32; 7],
2190}
2191
2192#[binread]
2193#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
2194#[derive(Debug, Xc3Write, PartialEq, Clone)]
2195#[br(stream = r)]
2196#[xc3(base_offset)]
2197pub struct SkeletonUnk5 {
2198    #[br(temp, try_calc = r.stream_position())]
2199    base_offset: u64,
2200
2201    #[br(parse_with = parse_count32_offset32)]
2202    #[br(args { offset: base_offset, inner: base_offset })]
2203    #[xc3(count_offset(u32, u32))]
2204    pub unk1: Vec<SkeletonUnk5Unk1>,
2205
2206    // TODO: count?
2207    #[br(parse_with = parse_opt_ptr32, offset = base_offset)]
2208    #[xc3(offset(u32))]
2209    pub unk_offset: Option<[f32; 12]>,
2210
2211    // TODO: padding?
2212    pub unk: [u32; 5],
2213}
2214
2215#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
2216#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
2217#[br(import_raw(base_offset: u64))]
2218pub struct SkeletonUnk5Unk1 {
2219    pub unk1: [[f32; 4]; 4],
2220    pub unk2: u32,
2221
2222    // TODO: all unk3 and then all unk4?
2223    #[br(parse_with = parse_count32_offset32, offset = base_offset)]
2224    #[xc3(count_offset(u32, u32))]
2225    pub unk3: Vec<SkeletonUnk5Unk1Unk3>,
2226
2227    #[br(parse_with = parse_count32_offset32, offset = base_offset)]
2228    #[xc3(count_offset(u32, u32))]
2229    pub unk4: Vec<u32>,
2230
2231    pub unk7: [f32; 15],
2232}
2233
2234#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
2235#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
2236pub struct SkeletonUnk5Unk1Unk3 {
2237    pub unk1: f32,
2238    pub unk2: u16, // bone index?
2239    pub unk3: u16, // bone index?
2240}
2241
2242// TODO: Data for AS_ bones?
2243#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
2244#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
2245#[br(import_raw(base_offset: u64))]
2246pub struct AsBoneData {
2247    #[br(parse_with = parse_offset32_count32, offset = base_offset)]
2248    #[xc3(offset_count(u32, u32))]
2249    pub bones: Vec<AsBone>,
2250
2251    #[br(parse_with = parse_offset32_count32, offset = base_offset)]
2252    #[xc3(offset_count(u32, u32))]
2253    pub values: Vec<AsBoneValue>,
2254
2255    // TODO: Not bone count for ch01022012.wimdo?
2256    #[br(parse_with = parse_ptr32)]
2257    #[br(args { offset: base_offset, inner: args! { count: bones.len() }})]
2258    #[xc3(offset(u32), align(16))]
2259    pub transforms: Vec<AsBoneTransform>,
2260
2261    pub unk3: u32,
2262
2263    // TODO: padding?
2264    // TODO: only 4 bytes for ch01022012.wimdo?
2265    pub unk: [u32; 2],
2266}
2267
2268#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
2269#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
2270pub struct AsBone {
2271    /// Index into [bones](struct.Skeleton.html#structfield.bones) for this bone.
2272    pub bone_index: u16,
2273    /// Index into [bones](struct.Skeleton.html#structfield.bones) of the parent bone.
2274    pub parent_index: u16,
2275    pub unk_end_index: u16,   // bones?
2276    pub unk_start_index: u16, // bones?
2277    pub unk1: u16,
2278    pub unk2: u16,
2279    pub value_count: u16,
2280    /// Index into [values](struct.AsBoneData.html#structfield.values).
2281    pub value_start_index: u16,
2282    /// The translation of this bone relative to its parent.
2283    pub translation: [f32; 3],
2284    pub unk5: [f32; 6], // ???
2285    /// The rotation of this bone relative to its parent.
2286    pub rotation_quaternion: [f32; 4],
2287    pub unk6: [f32; 3],
2288}
2289
2290// TODO: Some of these aren't floats?
2291#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
2292#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
2293pub struct AsBoneValue {
2294    unk1: [f32; 4],
2295    unk2: [f32; 4],
2296    unk3: [f32; 4],
2297    unk4: [f32; 2],
2298}
2299
2300#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
2301#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
2302pub struct AsBoneTransform {
2303    pub unk1: [[f32; 4]; 4],
2304    pub unk2: [[f32; 4]; 4],
2305    pub unk3: [[f32; 4]; 4],
2306}
2307
2308// TODO: pointer to decl_gbl_cac in ch001011011.wimdo?
2309#[binread]
2310#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
2311#[derive(Debug, Xc3Write, PartialEq, Clone)]
2312#[br(stream = r)]
2313#[xc3(base_offset)]
2314pub struct Unk1 {
2315    #[br(temp, try_calc = r.stream_position())]
2316    base_offset: u64,
2317
2318    #[br(parse_with = parse_count32_offset32, offset = base_offset)]
2319    #[xc3(count_offset(u32, u32))]
2320    pub unk1: Vec<Unk1Unk1>,
2321
2322    #[br(parse_with = parse_count32_offset32, offset = base_offset)]
2323    #[xc3(count_offset(u32, u32))]
2324    pub unk2: Vec<Unk1Unk2>,
2325
2326    #[br(parse_with = parse_count32_offset32, offset = base_offset)]
2327    #[xc3(count_offset(u32, u32))]
2328    pub unk3: Vec<Unk1Unk3>,
2329
2330    // angle values?
2331    #[br(parse_with = parse_count32_offset32, offset = base_offset)]
2332    #[xc3(count_offset(u32, u32))]
2333    pub unk4: Vec<Unk1Unk4>,
2334
2335    // TODO: padding?
2336    pub unk: [u32; 4],
2337}
2338
2339#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
2340#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
2341pub struct Unk1Unk1 {
2342    pub index: u16,
2343    pub unk2: u16, // 1
2344}
2345
2346#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
2347#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
2348pub struct Unk1Unk2 {
2349    pub unk1: u16, // 0
2350    pub index: u16,
2351    pub unk3: u16,
2352    pub unk4: u16,
2353    pub unk5: u32, // 0
2354}
2355
2356#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
2357#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
2358pub struct Unk1Unk3 {
2359    pub unk1: u16,
2360    pub unk2: u16,
2361    pub unk3: u32,
2362    pub unk4: u16,
2363    pub unk5: u16,
2364    pub unk6: u16,
2365    pub unk7: u16,
2366}
2367
2368#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
2369#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
2370pub struct Unk1Unk4 {
2371    pub unk1: f32,
2372    pub unk2: f32,
2373    pub unk3: f32,
2374    pub unk4: u32,
2375}
2376
2377xc3_write_binwrite_impl!(
2378    ParamType,
2379    RenderPassType,
2380    StateFlags,
2381    ModelsFlags,
2382    SamplerFlags,
2383    TextureUsage,
2384    ExtMeshFlags,
2385    MeshRenderFlags2,
2386    MaterialFlags,
2387    MaterialRenderFlags,
2388    BoneFlags
2389);
2390
2391impl Xc3WriteOffsets for SkinningOffsets<'_> {
2392    type Args = ();
2393
2394    fn write_offsets<W: std::io::Write + std::io::Seek>(
2395        &self,
2396        writer: &mut W,
2397        _base_offset: u64,
2398        data_ptr: &mut u64,
2399        endian: xc3_write::Endian,
2400        _args: Self::Args,
2401    ) -> xc3_write::Xc3Result<()> {
2402        let base_offset = self.base_offset;
2403
2404        let bones = self.bones.write(writer, base_offset, data_ptr, endian)?;
2405
2406        if !self.bone_indices.data.is_empty() {
2407            self.bone_indices
2408                .write_full(writer, base_offset, data_ptr, endian, ())?;
2409        }
2410
2411        self.inverse_bind_transforms
2412            .write_full(writer, base_offset, data_ptr, endian, ())?;
2413
2414        self.constraints
2415            .write_full(writer, base_offset, data_ptr, endian, ())?;
2416        self.bounds
2417            .write_full(writer, base_offset, data_ptr, endian, ())?;
2418
2419        self.unk_offset4
2420            .write_offsets(writer, base_offset, data_ptr, endian, ())?;
2421        self.as_bone_data
2422            .write_offsets(writer, base_offset, data_ptr, endian, ())?;
2423        self.unk_offset5
2424            .write_offsets(writer, base_offset, data_ptr, endian, ())?;
2425
2426        for bone in bones.0 {
2427            bone.name
2428                .write_full(writer, base_offset, data_ptr, endian, ())?;
2429        }
2430        Ok(())
2431    }
2432}
2433
2434impl Xc3WriteOffsets for ModelUnk1Offsets<'_> {
2435    type Args = ();
2436
2437    fn write_offsets<W: std::io::Write + std::io::Seek>(
2438        &self,
2439        writer: &mut W,
2440        _base_offset: u64,
2441        data_ptr: &mut u64,
2442        endian: xc3_write::Endian,
2443        _args: Self::Args,
2444    ) -> xc3_write::Xc3Result<()> {
2445        let base_offset = self.base_offset;
2446
2447        let items1 = self.items1.write(writer, base_offset, data_ptr, endian)?;
2448
2449        self.items3
2450            .write_full(writer, base_offset, data_ptr, endian, ())?;
2451
2452        if !self.items2.data.is_empty() {
2453            self.items2
2454                .write_full(writer, base_offset, data_ptr, endian, ())?;
2455        }
2456
2457        // TODO: Set alignment at type level for Xc3Write?
2458        if !self.items4.data.is_empty() {
2459            self.items4
2460                .write_full(writer, base_offset, data_ptr, endian, ())?;
2461        }
2462
2463        for item in items1.0 {
2464            item.name
2465                .write_full(writer, base_offset, data_ptr, endian, ())?;
2466        }
2467
2468        self.extra
2469            .write_offsets(writer, base_offset, data_ptr, endian, ())?;
2470
2471        Ok(())
2472    }
2473}
2474
2475impl Xc3WriteOffsets for LodDataOffsets<'_> {
2476    type Args = ();
2477
2478    fn write_offsets<W: std::io::Write + std::io::Seek>(
2479        &self,
2480        writer: &mut W,
2481        _base_offset: u64,
2482        data_ptr: &mut u64,
2483        endian: xc3_write::Endian,
2484        _args: Self::Args,
2485    ) -> xc3_write::Xc3Result<()> {
2486        let base_offset = self.base_offset;
2487        // Different order than field order.
2488        self.groups
2489            .write_full(writer, base_offset, data_ptr, endian, ())?;
2490        self.items
2491            .write_full(writer, base_offset, data_ptr, endian, ())?;
2492        Ok(())
2493    }
2494}
2495
2496// TODO: Add derive attribute for skipping empty vecs?
2497impl Xc3WriteOffsets for ModelsV112Offsets<'_> {
2498    type Args = ();
2499
2500    fn write_offsets<W: std::io::Write + std::io::Seek>(
2501        &self,
2502        writer: &mut W,
2503        _base_offset: u64,
2504        data_ptr: &mut u64,
2505        endian: xc3_write::Endian,
2506        _args: Self::Args,
2507    ) -> xc3_write::Xc3Result<()> {
2508        let base_offset = self.base_offset;
2509
2510        self.models
2511            .write_full(writer, base_offset, data_ptr, endian, ())?;
2512        self.skinning
2513            .write_full(writer, base_offset, data_ptr, endian, ())?;
2514        if !self.ext_meshes.data.is_empty() {
2515            self.ext_meshes
2516                .write_full(writer, base_offset, data_ptr, endian, ())?;
2517        }
2518
2519        self.model_unk8
2520            .write_full(writer, base_offset, data_ptr, endian, ())?;
2521
2522        // TODO: Padding before this?
2523        self.morph_controllers
2524            .write_full(writer, base_offset, data_ptr, endian, ())?;
2525
2526        // Different order than field order.
2527        self.lod_data
2528            .write_full(writer, base_offset, data_ptr, endian, ())?;
2529        self.model_unk7
2530            .write_full(writer, base_offset, data_ptr, endian, ())?;
2531        self.model_unk11
2532            .write_full(writer, base_offset, data_ptr, endian, ())?;
2533        self.model_unk1
2534            .write_full(writer, base_offset, data_ptr, endian, ())?;
2535        self.model_unk12
2536            .write_full(writer, base_offset, data_ptr, endian, ())?;
2537        self.alpha_table
2538            .write_full(writer, base_offset, data_ptr, endian, ())?;
2539        self.model_unk3
2540            .write_full(writer, base_offset, data_ptr, endian, ())?;
2541        self.model_unk9
2542            .write_full(writer, base_offset, data_ptr, endian, ())?;
2543        self.extra
2544            .write_offsets(writer, base_offset, data_ptr, endian, ())?;
2545
2546        Ok(())
2547    }
2548}
2549
2550impl Xc3WriteOffsets for TechniqueOffsets<'_> {
2551    type Args = ();
2552
2553    fn write_offsets<W: std::io::Write + std::io::Seek>(
2554        &self,
2555        writer: &mut W,
2556        base_offset: u64,
2557        data_ptr: &mut u64,
2558        endian: xc3_write::Endian,
2559        _args: Self::Args,
2560    ) -> xc3_write::Xc3Result<()> {
2561        // Different order than field order.
2562        self.attributes
2563            .write_full(writer, base_offset, data_ptr, endian, ())?;
2564        if !self.textures.data.is_empty() {
2565            // TODO: Always skip offset for empty vec?
2566            self.textures
2567                .write_full(writer, base_offset, data_ptr, endian, ())?;
2568        }
2569        self.uniform_blocks
2570            .write_full(writer, base_offset, data_ptr, endian, ())?;
2571
2572        self.parameters
2573            .write_full(writer, base_offset, data_ptr, endian, ())?;
2574
2575        self.unk15
2576            .write_full(writer, base_offset, data_ptr, endian, ())?;
2577
2578        // TODO: Why is there a variable amount of padding?
2579        // TODO: This isn't always accurate?
2580        *data_ptr += self.parameters.data.len() as u64 * 16;
2581
2582        Ok(())
2583    }
2584}
2585
2586// TODO: Add derive attribute for skipping empty vecs?
2587impl Xc3WriteOffsets for MaterialsOffsets<'_> {
2588    type Args = ();
2589
2590    fn write_offsets<W: std::io::Write + std::io::Seek>(
2591        &self,
2592        writer: &mut W,
2593        _base_offset: u64,
2594        data_ptr: &mut u64,
2595        endian: xc3_write::Endian,
2596        _args: Self::Args,
2597    ) -> xc3_write::Xc3Result<()> {
2598        let base_offset = self.base_offset;
2599
2600        // Material fields get split up and written in a different order.
2601        let materials = self
2602            .materials
2603            .write(writer, base_offset, data_ptr, endian)?;
2604
2605        self.work_values
2606            .write_full(writer, base_offset, data_ptr, endian, ())?;
2607        self.shader_vars
2608            .write_full(writer, base_offset, data_ptr, endian, ())?;
2609
2610        for material in &materials.0 {
2611            material
2612                .techniques
2613                .write_full(writer, base_offset, data_ptr, endian, ())?;
2614        }
2615
2616        for material in &materials.0 {
2617            material
2618                .textures
2619                .write_full(writer, base_offset, data_ptr, endian, ())?;
2620        }
2621
2622        // Different order than field order.
2623        if !self.alpha_test_textures.data.is_empty() {
2624            self.alpha_test_textures
2625                .write_full(writer, base_offset, data_ptr, endian, ())?;
2626        }
2627        self.callbacks
2628            .write_full(writer, base_offset, data_ptr, endian, ())?;
2629        self.material_unk2
2630            .write_full(writer, base_offset, data_ptr, endian, ())?;
2631        self.fur_shells
2632            .write_full(writer, base_offset, data_ptr, endian, ())?;
2633        self.samplers
2634            .write_full(writer, base_offset, data_ptr, endian, ())?;
2635        self.unk6
2636            .write_full(writer, base_offset, data_ptr, endian, ())?;
2637        self.unk5
2638            .write_offsets(writer, base_offset, data_ptr, endian, ())?;
2639
2640        self.techniques
2641            .write_full(writer, base_offset, data_ptr, endian, ())?;
2642
2643        // TODO: Offset not large enough?
2644        for material in &materials.0 {
2645            material
2646                .name
2647                .write_full(writer, base_offset, data_ptr, endian, ())?;
2648        }
2649
2650        Ok(())
2651    }
2652}
2653
2654impl Xc3WriteOffsets for MxmdV112Offsets<'_> {
2655    type Args = ();
2656
2657    fn write_offsets<W: std::io::Write + std::io::Seek>(
2658        &self,
2659        writer: &mut W,
2660        base_offset: u64,
2661        data_ptr: &mut u64,
2662        endian: xc3_write::Endian,
2663        _args: Self::Args,
2664    ) -> xc3_write::Xc3Result<()> {
2665        self.models
2666            .write_full(writer, base_offset, data_ptr, endian, ())?;
2667        self.unk8
2668            .write_full(writer, base_offset, data_ptr, endian, ())?;
2669        self.materials
2670            .write_full(writer, base_offset, data_ptr, endian, ())?;
2671
2672        // Different order than field order.
2673        self.streaming
2674            .write_full(writer, base_offset, data_ptr, endian, ())?;
2675
2676        // Apply padding even if this is the end of the file.
2677        vec![0u8; (data_ptr.next_multiple_of(16) - *data_ptr) as usize]
2678            .xc3_write(writer, endian)?;
2679        *data_ptr = (*data_ptr).max(writer.stream_position()?);
2680
2681        // TODO: Some files have 16 more bytes of padding?
2682        self.unk1
2683            .write_full(writer, base_offset, data_ptr, endian, ())?;
2684
2685        self.vertex_data
2686            .write_full(writer, base_offset, data_ptr, endian, ())?;
2687        self.spch
2688            .write_full(writer, base_offset, data_ptr, endian, ())?;
2689        self.packed_textures
2690            .write_full(writer, base_offset, data_ptr, endian, ())?;
2691
2692        // TODO: Align the file size itself for xc1?
2693
2694        Ok(())
2695    }
2696}
2697
2698// TODO: Add derive attribute for skipping empty vecs?
2699impl Xc3WriteOffsets for Unk1Offsets<'_> {
2700    type Args = ();
2701
2702    fn write_offsets<W: std::io::Write + std::io::Seek>(
2703        &self,
2704        writer: &mut W,
2705        _base_offset: u64,
2706        data_ptr: &mut u64,
2707        endian: xc3_write::Endian,
2708        _args: Self::Args,
2709    ) -> xc3_write::Xc3Result<()> {
2710        let base_offset = self.base_offset;
2711        self.unk1
2712            .write_full(writer, base_offset, data_ptr, endian, ())?;
2713        self.unk2
2714            .write_full(writer, base_offset, data_ptr, endian, ())?;
2715        self.unk3
2716            .write_full(writer, base_offset, data_ptr, endian, ())?;
2717        if !self.unk4.data.is_empty() {
2718            self.unk4
2719                .write_full(writer, base_offset, data_ptr, endian, ())?;
2720        }
2721        Ok(())
2722    }
2723}
2724
2725impl Xc3WriteOffsets for ModelUnk3ItemOffsets<'_> {
2726    type Args = ();
2727
2728    fn write_offsets<W: std::io::prelude::Write + std::io::prelude::Seek>(
2729        &self,
2730        writer: &mut W,
2731        base_offset: u64,
2732        data_ptr: &mut u64,
2733        endian: xc3_write::Endian,
2734        _args: Self::Args,
2735    ) -> xc3_write::Xc3Result<()> {
2736        // Different order than field order.
2737        self.unk3
2738            .write_full(writer, base_offset, data_ptr, endian, ())?;
2739        self.name
2740            .write_full(writer, base_offset, data_ptr, endian, ())?;
2741        Ok(())
2742    }
2743}
2744
2745impl Xc3WriteOffsets for FurShellsOffsets<'_> {
2746    type Args = ();
2747
2748    fn write_offsets<W: std::io::prelude::Write + std::io::prelude::Seek>(
2749        &self,
2750        writer: &mut W,
2751        base_offset: u64,
2752        data_ptr: &mut u64,
2753        endian: xc3_write::Endian,
2754        _args: Self::Args,
2755    ) -> xc3_write::Xc3Result<()> {
2756        // Different order than field order.
2757        self.params
2758            .write_full(writer, base_offset, data_ptr, endian, ())?;
2759        self.material_param_indices
2760            .write_full(writer, base_offset, data_ptr, endian, ())?;
2761        Ok(())
2762    }
2763}
2764
2765impl Xc3WriteOffsets for PackedTexturesOffsets<'_> {
2766    type Args = ();
2767
2768    fn write_offsets<W: std::io::prelude::Write + std::io::prelude::Seek>(
2769        &self,
2770        writer: &mut W,
2771        _base_offset: u64,
2772        data_ptr: &mut u64,
2773        endian: xc3_write::Endian,
2774        _args: Self::Args,
2775    ) -> xc3_write::Xc3Result<()> {
2776        let base_offset = self.base_offset;
2777
2778        // Names and data need to be written at the end.
2779        let textures = self.textures.write(writer, base_offset, data_ptr, endian)?;
2780
2781        self.strings_offset
2782            .write_full(writer, base_offset, data_ptr, endian, ())?;
2783        for texture in &textures.0 {
2784            texture
2785                .name
2786                .write_full(writer, base_offset, data_ptr, endian, ())?;
2787        }
2788        for texture in &textures.0 {
2789            texture
2790                .mibl_data
2791                .write_full(writer, base_offset, data_ptr, endian, ())?;
2792        }
2793        Ok(())
2794    }
2795}
2796
2797impl<U> Xc3WriteOffsets for PackedExternalTexturesOffsets<'_, U>
2798where
2799    U: Xc3Write + 'static,
2800    for<'b> U: BinRead<Args<'b> = ()>,
2801    for<'b> U::Offsets<'b>: Xc3WriteOffsets<Args = ()>,
2802{
2803    type Args = ();
2804
2805    fn write_offsets<W: std::io::prelude::Write + std::io::prelude::Seek>(
2806        &self,
2807        writer: &mut W,
2808        _base_offset: u64,
2809        data_ptr: &mut u64,
2810        endian: xc3_write::Endian,
2811        _args: Self::Args,
2812    ) -> xc3_write::Xc3Result<()> {
2813        let base_offset = self.base_offset;
2814
2815        // Names need to be written at the end.
2816        let textures = self.textures.write(writer, base_offset, data_ptr, endian)?;
2817
2818        self.strings_offset
2819            .write_full(writer, base_offset, data_ptr, endian, ())?;
2820        for texture in &textures.0 {
2821            texture
2822                .name
2823                .write_full(writer, base_offset, data_ptr, endian, ())?;
2824        }
2825        Ok(())
2826    }
2827}
2828
2829impl Xc3WriteOffsets for SkeletonUnk5Offsets<'_> {
2830    type Args = ();
2831
2832    fn write_offsets<W: std::io::Write + std::io::Seek>(
2833        &self,
2834        writer: &mut W,
2835        _base_offset: u64,
2836        data_ptr: &mut u64,
2837        endian: xc3_write::Endian,
2838        _args: Self::Args,
2839    ) -> xc3_write::Xc3Result<()> {
2840        let base_offset = self.base_offset;
2841
2842        let unk1 = self.unk1.write(writer, base_offset, data_ptr, endian)?;
2843        for u in &unk1.0 {
2844            u.unk3
2845                .write_full(writer, base_offset, data_ptr, endian, ())?;
2846        }
2847        for u in &unk1.0 {
2848            u.unk4
2849                .write_full(writer, base_offset, data_ptr, endian, ())?;
2850        }
2851        self.unk_offset
2852            .write_full(writer, base_offset, data_ptr, endian, ())?;
2853
2854        Ok(())
2855    }
2856}
2857
2858impl Xc3WriteOffsets for Unk8Offsets<'_> {
2859    type Args = ();
2860    fn write_offsets<W: std::io::prelude::Write + std::io::prelude::Seek>(
2861        &self,
2862        writer: &mut W,
2863        _base_offset: u64,
2864        data_ptr: &mut u64,
2865        endian: xc3_write::Endian,
2866        _args: Self::Args,
2867    ) -> xc3_write::Xc3Result<()> {
2868        let base_offset = self.base_offset;
2869        let unk2 = self.unk2.write(writer, base_offset, data_ptr, endian)?;
2870        self.unk3
2871            .write_full(writer, base_offset, data_ptr, endian, ())?;
2872        // Strings go at the end.
2873        for u in unk2.0 {
2874            u.name
2875                .write_full(writer, base_offset, data_ptr, endian, ())?;
2876        }
2877        Ok(())
2878    }
2879}
2880
2881impl Xc3WriteOffsets for ModelUnk9InnerUnk0Offsets<'_> {
2882    type Args = ();
2883
2884    fn write_offsets<W: std::io::Write + std::io::Seek>(
2885        &self,
2886        writer: &mut W,
2887        _base_offset: u64,
2888        data_ptr: &mut u64,
2889        endian: xc3_write::Endian,
2890        args: Self::Args,
2891    ) -> xc3_write::Xc3Result<()> {
2892        // Subtract the unk1 size.
2893        let base_offset = self.base_offset.saturating_sub(4);
2894        if !self.items1.data.is_empty() {
2895            self.items1
2896                .write_full(writer, base_offset, data_ptr, endian, args)?;
2897        }
2898        if !self.items2.data.is_empty() {
2899            self.items2
2900                .write_full(writer, base_offset, data_ptr, endian, args)?;
2901        }
2902        Ok(())
2903    }
2904}
2905
2906impl Xc3WriteOffsets for MaterialCallbacksOffsets<'_> {
2907    type Args = ();
2908    fn write_offsets<W: std::io::prelude::Write + std::io::prelude::Seek>(
2909        &self,
2910        writer: &mut W,
2911        base_offset: u64,
2912        data_ptr: &mut u64,
2913        endian: xc3_write::Endian,
2914        _args: Self::Args,
2915    ) -> xc3_write::Xc3Result<()> {
2916        // Different order than field order.
2917        self.work_callbacks
2918            .write_full(writer, base_offset, data_ptr, endian, ())?;
2919        self.unk1
2920            .write_full(writer, base_offset, data_ptr, endian, ())?;
2921        self.material_indices
2922            .write_full(writer, base_offset, data_ptr, endian, ())?;
2923        Ok(())
2924    }
2925}
2926
2927fn count_constraints(bones: &[Bone]) -> usize {
2928    // Assume all constraints are used.
2929    bones
2930        .iter()
2931        .map(|b| {
2932            if b.flags.distance_constraint() || b.flags.fixed_offset_constraint() {
2933                b.constraint_index as usize + 1
2934            } else {
2935                0
2936            }
2937        })
2938        .max()
2939        .unwrap_or_default()
2940}
2941
2942fn count_bounds(bones: &[Bone]) -> usize {
2943    // Assume all bounds are used.
2944    bones
2945        .iter()
2946        .map(|b| {
2947            if b.flags.bounds_offset() {
2948                b.bounds_index as usize + 1
2949            } else {
2950                0
2951            }
2952        })
2953        .max()
2954        .unwrap_or_default()
2955}
2956
2957fn model_unk9_buffer_length(buffers: &[ModelUnk9Buffer]) -> usize {
2958    // TODO: Is it safe to assume the item size?
2959    buffers
2960        .iter()
2961        .max_by_key(|b| b.offset)
2962        .map(|b| b.offset as usize + b.count as usize * 48)
2963        .unwrap_or_default()
2964}