1use crate::crc::type_id;
4use crate::crs::{self, Reader};
5use crate::error::{format_err, Error, Result};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum ImageFormat {
12 Bc1,
14 Bc3,
16 A8,
18 R8,
20 Bgra8,
22 R32F,
24 R24G8,
26}
27
28impl ImageFormat {
29 fn from_raw(v: u32) -> Result<Self> {
30 Ok(match v {
31 0 => Self::Bc1,
32 1 | 2 => Self::Bc3,
33 3 => Self::A8,
34 4 => Self::R8,
35 5 => Self::Bgra8,
36 6 => Self::R32F,
37 7 => Self::R24G8,
38 _ => return Err(Error::Unsupported(format!("image format {v}"))),
39 })
40 }
41
42 pub fn level_size(self, w: u32, h: u32) -> usize {
44 let (w, h) = (w.max(1) as usize, h.max(1) as usize);
45 let blocks = w.div_ceil(4) * h.div_ceil(4);
46 match self {
47 Self::Bc1 => blocks * 8,
48 Self::Bc3 => blocks * 16,
49 Self::A8 | Self::R8 => w * h,
50 Self::Bgra8 | Self::R32F | Self::R24G8 => w * h * 4,
51 }
52 }
53}
54
55#[derive(Debug, Clone)]
57pub struct ImageBuffer {
58 pub width: u32,
60 pub height: u32,
62 pub image_type: u32,
64 pub usage: u32,
66 pub format: ImageFormat,
68 pub generate_mips: bool,
70 pub address: [u32; 3],
72 pub filter: u32,
74 pub source: String,
76 pub levels: Vec<Vec<u8>>,
78}
79
80impl ImageBuffer {
81 pub const CLASS: &'static str = "ImageBuffer";
83
84 pub fn read(r: &mut Reader) -> Result<Self> {
88 r.block(|r, _| {
89 let width = r.u32()?;
90 let height = r.u32()?;
91 let image_type = r.u32()?;
92 let usage = r.u32()?;
93 let format = ImageFormat::from_raw(r.u32()?)?;
94 let generate_mips = r.u32()? != 0;
95 let address = [r.u32()?, r.u32()?, r.u32()?];
96 let filter = r.u32()?;
97 let source = r.string()?;
98 let mut levels = Vec::new();
99 if !matches!(usage, 4 | 5) {
100 for _ in 0..r.count()? {
101 let n = r.count()?;
102 levels.push(r.bytes(n)?.to_vec());
103 }
104 }
105 Ok(ImageBuffer {
106 width,
107 height,
108 image_type,
109 usage,
110 format,
111 generate_mips,
112 address,
113 filter,
114 source,
115 levels,
116 })
117 })
118 }
119
120 pub fn faces(&self) -> usize {
122 if self.image_type == 0 {
123 1
124 } else {
125 6
126 }
127 }
128
129 pub fn mip_count(&self) -> usize {
131 self.levels.len() / self.faces()
132 }
133}
134
135#[derive(Debug, Clone)]
139pub struct DrawRange {
140 pub start_vertex: i32,
142 pub vertex_count: i32,
144 pub start_index: i32,
146 pub index_count: i32,
148 pub material_index: i32,
150 pub bone: i32,
152}
153
154#[derive(Debug, Clone)]
156pub struct MeshPart {
157 pub lod_distance: f32,
159 pub name: String,
161 pub ao_texture: Option<String>,
163 pub ranges: Vec<DrawRange>,
165 pub static_indices: Vec<u8>,
167 pub static_vertices: Vec<u8>,
169 pub skinned_indices: Vec<u8>,
171 pub skinned_vertices: Vec<u8>,
173}
174
175impl MeshPart {
176 pub fn geometry(&self, range: &DrawRange) -> (&[u8], &[u8], usize) {
178 if range.bone < 0 {
179 (
180 &self.skinned_indices,
181 &self.skinned_vertices,
182 SKINNED_STRIDE,
183 )
184 } else {
185 (&self.static_indices, &self.static_vertices, STATIC_STRIDE)
186 }
187 }
188}
189
190#[derive(Debug, Clone)]
192pub struct Bone {
193 pub name: String,
195 pub parent: i32,
197 pub rotation: [f32; 4],
199 pub translation: [f32; 3],
201 pub inv_rotation: [f32; 4],
203 pub inv_translation: [f32; 3],
205}
206
207#[derive(Debug, Clone)]
209pub struct GraphicsMesh {
210 pub source: String,
212 pub parts: Vec<MeshPart>,
214 pub material_instances: Vec<Vec<Option<String>>>,
216 pub bones: Vec<Bone>,
218 pub bounds_min: [f32; 3],
220 pub bounds_max: [f32; 3],
222}
223
224#[derive(Debug, Clone)]
226pub struct AnimationClip {
227 pub name: String,
229 pub mesh_name: String,
231 pub source_name: String,
233 pub looping: bool,
235 pub sample_rate: f32,
237 pub start_frame: i32,
240 pub end_frame: i32,
242 pub declared_rate: f32,
245 pub sample_interval: f32,
247 pub loop_start_frame: i32,
250 pub instance_count: i32,
253 pub instanced: bool,
255 pub loop_start: usize,
257 pub bone_count: usize,
259 pub frames: Vec<Vec<crate::skeleton::Transform>>,
261 pub duration_ticks: i64,
264 pub events: Vec<AnimationEvent>,
266}
267
268#[derive(Debug, Clone)]
270pub struct AnimationEvent {
271 pub frame: i32,
273 pub resource: Option<String>,
275}
276
277#[derive(Debug, Clone)]
279pub struct AnimationBlend {
280 pub source: String,
282 pub target: String,
284 pub seconds: f32,
286}
287
288#[derive(Debug, Clone)]
290pub struct AnimationGroup {
291 pub clips: Vec<AnimationClip>,
293 pub default_blend: f32,
296 pub blends: Vec<AnimationBlend>,
298 pub blend_matrix: Vec<f32>,
301}
302
303impl AnimationGroup {
304 pub const CLASS: &'static str = "AnimationGroup";
306
307 pub fn read(r: &mut Reader) -> Result<Self> {
313 r.block(|r, _| {
314 let clip_count = r.count()?;
315 let mut clips = Vec::with_capacity(clip_count);
316 for _ in 0..clip_count {
317 let name = r.string()?;
318 let mesh_name = r.string()?;
319 let source_name = r.string()?;
320 let start_frame = r.i32()?;
321 let end_frame = r.i32()?;
322 let declared_rate = r.f32()?;
323 let looping = r.bool()?;
324 let loop_start_frame = r.i32()?;
325 let instance_count = r.i32()?;
326 let instanced = r.bool()?;
327 let event_count = r.count()?;
328 let events = (0..event_count).map(|_| Ok(AnimationEvent {
329 frame: r.i32()?, resource: r.external_ref()?,
330 })).collect::<Result<Vec<_>>>()?;
331 let frame_count = r.count()?;
332 let sample_rate = r.f32()?;
333 let sample_interval = r.f32()?;
334 let bone_count = r.count()?;
335 let sample_count = r.count()?;
336 if sample_count != frame_count.saturating_mul(bone_count) {
337 return Err(crate::error::Error::Format(format!(
338 "animation {name}: {sample_count} samples != {frame_count} frames x {bone_count} bones"
339 )));
340 }
341 let loop_start = if loop_start_frame == 0 || end_frame == start_frame {
342 0
343 } else {
344 ((((loop_start_frame - start_frame) as f32
345 / (end_frame - start_frame) as f32)
346 * frame_count as f32) as usize)
347 .min(frame_count.saturating_sub(1))
348 };
349 let mut frames = Vec::with_capacity(frame_count);
350 for _ in 0..frame_count {
351 let mut frame = Vec::with_capacity(bone_count);
352 for _ in 0..bone_count {
353 frame.push(crate::skeleton::Transform {
354 rotation: {
357 let mut q = r.f32s()?;
358 q[..3].iter_mut().for_each(|v| *v = -*v);
359 q
360 },
361 translation: r.f32s()?,
362 });
363 }
364 frames.push(frame);
365 }
366 let duration_ticks = r.i64()?;
367 clips.push(AnimationClip {
368 name,
369 mesh_name,
370 source_name,
371 looping,
372 sample_rate,
373 start_frame,
374 end_frame,
375 declared_rate,
376 sample_interval,
377 loop_start_frame,
378 instance_count,
379 instanced,
380 loop_start,
381 bone_count,
382 frames,
383 duration_ticks,
384 events,
385 });
386 }
387 let blend_count = r.count()?;
388 let blends = (0..blend_count).map(|_| Ok(AnimationBlend {
389 source: r.string()?, target: r.string()?, seconds: r.f32()?,
390 })).collect::<Result<Vec<_>>>()?;
391 let default_blend = r.f32()?;
392 let blend_matrix_count = r.count()?;
393 let blend_matrix = (0..blend_matrix_count).map(|_| r.f32()).collect::<Result<Vec<_>>>()?;
394 Ok(AnimationGroup { clips, default_blend, blends, blend_matrix })
395 })
396 }
397}
398
399pub const STATIC_STRIDE: usize = 24;
401pub const SKINNED_STRIDE: usize = 32;
403pub const POSITION_SCALE: f32 = 1.0 / 512.0;
405
406impl GraphicsMesh {
407 pub const CLASS: &'static str = "GraphicsMesh";
409
410 pub fn read(r: &mut Reader) -> Result<Self> {
419 fn geometry(r: &mut Reader, part: &mut MeshPart) -> Result<()> {
421 for _ in 0..r.count()? {
422 let v = [r.i32()?, r.i32()?, r.i32()?, r.i32()?, r.i32()?, r.i32()?];
423 part.ranges.push(DrawRange {
424 start_vertex: v[0],
425 vertex_count: v[1],
426 start_index: v[2],
427 index_count: v[3],
428 material_index: v[4],
429 bone: v[5],
430 });
431 }
432 let mut buffer = || -> Result<Vec<u8>> {
433 r.u8()?;
434 let n = r.u32()? as usize;
435 Ok(r.bytes(n)?.to_vec())
436 };
437 part.static_indices = buffer()?;
438 part.static_vertices = buffer()?;
439 part.skinned_indices = buffer()?;
440 part.skinned_vertices = buffer()?;
441 r.i32()?;
442 Ok(())
443 }
444 r.block(|r, version| {
445 let source = r.string()?;
446 r.f32s::<6>()?; r.i32()?;
448 r.f32s::<2>()?;
449 let mut parts = Vec::new();
450 for _ in 0..r.count()? {
451 let mut part = MeshPart {
452 lod_distance: r.f32()?,
453 name: r.string()?,
454 ao_texture: r.external_ref()?,
455 ranges: Vec::new(),
456 static_indices: Vec::new(),
457 static_vertices: Vec::new(),
458 skinned_indices: Vec::new(),
459 skinned_vertices: Vec::new(),
460 };
461 if version == 0 {
462 r.skip_block()?; r.block(|r, _| geometry(r, &mut part))?;
464 }
465 parts.push(part);
466 }
467 let mut material_instances = Vec::new();
468 for _ in 0..r.count()? {
469 let n = r.count()?;
470 material_instances.push((0..n).map(|_| r.external_ref()).collect::<Result<_>>()?);
471 }
472 let mut bones = Vec::new();
473 for _ in 0..r.count()? {
474 bones.push(Bone {
475 name: r.string()?,
476 parent: r.i32()?,
477 rotation: r.f32s()?,
478 translation: r.f32s()?,
479 inv_rotation: r.f32s()?,
480 inv_translation: r.f32s()?,
481 });
482 }
483 let bounds_min = r.f32s()?;
484 let bounds_max = r.f32s()?;
485 if version > 0 {
486 r.skip_block()?; r.block(|r, _| {
488 r.i32()?;
489 for part in &mut parts {
490 geometry(r, part)?;
491 }
492 Ok(())
493 })?;
494 }
495 Ok(GraphicsMesh {
496 source,
497 parts,
498 material_instances,
499 bones,
500 bounds_min,
501 bounds_max,
502 })
503 })
504 }
505}
506
507#[derive(Debug, Clone)]
511pub struct MaterialInstance {
512 pub material: Option<String>,
514 pub textures: Vec<(String, Option<String>)>,
516 pub vectors: Vec<(String, [f32; 4])>,
518}
519
520impl MaterialInstance {
521 pub const CLASS: &'static str = "MaterialInstance";
523
524 pub fn read(r: &mut Reader) -> Result<Self> {
526 r.block(|r, _| {
527 let material = r.external_ref()?;
528 let textures = (0..r.count()?)
529 .map(|_| Ok((r.string()?, r.external_ref()?)))
530 .collect::<Result<_>>()?;
531 let vectors = (0..r.count()?)
532 .map(|_| Ok((r.string()?, r.f32s()?)))
533 .collect::<Result<_>>()?;
534 Ok(MaterialInstance {
535 material,
536 textures,
537 vectors,
538 })
539 })
540 }
541
542 pub fn texture(&self, slot: &str) -> Option<&str> {
544 self.textures
545 .iter()
546 .find(|(s, _)| s == slot)
547 .and_then(|(_, t)| t.as_deref())
548 }
549}
550
551#[derive(Debug, Clone)]
555pub struct VertexElement {
556 pub semantic: String,
558 pub stream: i32,
560 pub offset: i32,
562 pub usage: i32,
564 pub usage_index: i32,
566 pub format: i32,
569}
570
571#[derive(Debug, Clone)]
573pub struct VertexLayout {
574 pub elements: Vec<VertexElement>,
576 pub dxbc_signature: Vec<u8>,
578}
579
580impl VertexLayout {
581 pub const CLASS: &'static str = "VertexLayout";
583
584 pub fn read(r: &mut Reader) -> Result<Self> {
586 r.block(|r, _| {
587 let elements = (0..r.count()?)
588 .map(|_| {
589 Ok(VertexElement {
590 semantic: r.string()?,
591 stream: r.i32()?,
592 offset: r.i32()?,
593 usage: r.i32()?,
594 usage_index: r.i32()?,
595 format: r.i32()?,
596 })
597 })
598 .collect::<Result<_>>()?;
599 let n = r.count()?;
600 Ok(VertexLayout {
601 elements,
602 dxbc_signature: r.bytes(n)?.to_vec(),
603 })
604 })
605 }
606}
607
608pub fn decode<T>(data: &[u8], class: &str, read: fn(&mut Reader) -> Result<T>) -> Result<T> {
612 let (_, value) = crs::parse(data, |r, h| {
613 if h.type_id != type_id(class) {
614 return format_err(format!(
615 "expected {class}, found type id {:#010x}",
616 h.type_id
617 ));
618 }
619 read(r)
620 })?;
621 Ok(value)
622}
623
624macro_rules! decode_fn {
625 ($name:ident, $ty:ty) => {
626 #[doc = concat!("Read a [`", stringify!($ty), "`] from the bytes of its resource.")]
627 pub fn $name(data: &[u8]) -> Result<$ty> {
628 decode(data, <$ty>::CLASS, <$ty>::read)
629 }
630 };
631}
632
633decode_fn!(image_buffer, ImageBuffer);
634decode_fn!(graphics_mesh, GraphicsMesh);
635decode_fn!(animation_group, AnimationGroup);
636decode_fn!(material_instance, MaterialInstance);
637decode_fn!(vertex_layout, VertexLayout);
638
639#[derive(Debug, Clone, Copy)]
643pub struct WaveFormat {
644 pub tag: u16,
646 pub channels: u16,
648 pub sample_rate: u32,
650 pub avg_bytes_per_sec: u32,
652 pub block_align: u16,
654 pub bits_per_sample: u16,
656}
657
658#[derive(Debug, Clone)]
661pub struct SoundBuffer {
662 pub looping: bool,
664 pub streamed: bool,
666 pub source: String,
668 pub stream_entry: String,
670 pub length_seconds: f32,
672 pub format: WaveFormat,
674 pub format_bytes: Vec<u8>,
676 pub packet_table: Vec<u8>,
678 pub data: Vec<u8>,
680}
681
682impl SoundBuffer {
683 pub const CLASS: &'static str = "SoundBuffer";
685
686 pub fn read(r: &mut Reader) -> Result<Self> {
690 r.block(|r, _| {
691 let looping = r.bool()?;
692 let streamed = r.bool()?;
693 let source = r.string()?;
694 let stream_entry = r.string()?;
695 let length_seconds = r.f32()?;
696 let data_len = r.count()?;
697 let table_len = r.count()?;
698 let packet_table = r.bytes(table_len)?.to_vec();
699 let format_bytes = r.bytes(0x28)?.to_vec();
700 let data = r.bytes(data_len)?.to_vec();
701 let u16_at = |o: usize| u16::from_le_bytes([format_bytes[o], format_bytes[o + 1]]);
702 let u32_at = |o: usize| u32::from_le_bytes(format_bytes[o..o + 4].try_into().unwrap());
703 let format = WaveFormat {
704 tag: u16_at(0),
705 channels: u16_at(2),
706 sample_rate: u32_at(4),
707 avg_bytes_per_sec: u32_at(8),
708 block_align: u16_at(12),
709 bits_per_sample: u16_at(14),
710 };
711 Ok(SoundBuffer {
712 looping,
713 streamed,
714 source,
715 stream_entry,
716 length_seconds,
717 format,
718 format_bytes,
719 packet_table,
720 data,
721 })
722 })
723 }
724
725 pub fn to_xwma(&self, stream: Option<&[u8]>) -> Vec<u8> {
728 fn chunk(out: &mut Vec<u8>, tag: &[u8; 4], body: &[u8]) {
729 out.extend_from_slice(tag);
730 out.extend_from_slice(&(body.len() as u32).to_le_bytes());
731 out.extend_from_slice(body);
732 if body.len() % 2 == 1 {
733 out.push(0);
734 }
735 }
736 let data = stream.unwrap_or(&self.data);
737 let mut body = b"XWMA".to_vec();
738 chunk(&mut body, b"fmt ", &self.format_bytes[..18]);
739 chunk(&mut body, b"dpds", &self.packet_table);
740 chunk(&mut body, b"data", data);
741 let mut out = b"RIFF".to_vec();
742 out.extend_from_slice(&(body.len() as u32).to_le_bytes());
743 out.extend_from_slice(&body);
744 out
745 }
746}
747
748decode_fn!(sound_buffer, SoundBuffer);