Skip to main content

banished_formats/
types.rs

1//! Decoders for individual resource classes.
2
3use crate::crc::type_id;
4use crate::crs::{self, Reader};
5use crate::error::{format_err, Error, Result};
6
7// ---------------------------------------------------------------- ImageBuffer
8
9/// `Video::ImageBuffer::Format`, mapped to DXGI by the VideoDX11 texture-creation table.
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum ImageFormat {
12    /// BC1 (DXT1) blocks.
13    Bc1,
14    /// BC3 (DXT5) blocks.
15    Bc3,
16    /// One byte of alpha per pixel.
17    A8,
18    /// One byte of red per pixel.
19    R8,
20    /// Four bytes per pixel: blue, green, red, alpha.
21    Bgra8,
22    /// One 32-bit float per pixel.
23    R32F,
24    /// 24-bit depth and 8-bit stencil per pixel (*inferred*).
25    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    /// Byte size of one level.
43    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/// A texture: its size, format and levels (mip maps, and the six faces of a cube map).
56#[derive(Debug, Clone)]
57pub struct ImageBuffer {
58    /// The width in pixels.
59    pub width: u32,
60    /// The height in pixels.
61    pub height: u32,
62    /// 0 = 2D; otherwise a cube map (levels are 6 faces x mips).
63    pub image_type: u32,
64    /// How the engine uses the texture.
65    pub usage: u32,
66    /// The pixel format.
67    pub format: ImageFormat,
68    /// Whether the engine makes the mip levels itself.
69    pub generate_mips: bool,
70    /// The address (wrap) mode on each axis.
71    pub address: [u32; 3],
72    /// The filter mode.
73    pub filter: u32,
74    /// The original image file the texture was made from.
75    pub source: String,
76    /// The data of each level: faces × mips, face by face.
77    pub levels: Vec<Vec<u8>>,
78}
79
80impl ImageBuffer {
81    /// The resource class name.
82    pub const CLASS: &'static str = "ImageBuffer";
83
84    /// `Video::ImageBuffer::Serialize` + `PlatformSerialize`.
85    ///
86    /// Original: Runtime `0x1800f4f40`, Runtime `0x1800f5980`.
87    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    /// The number of faces: 6 for a cube map, else 1.
121    pub fn faces(&self) -> usize {
122        if self.image_type == 0 {
123            1
124        } else {
125            6
126        }
127    }
128
129    /// The number of mip levels of each face.
130    pub fn mip_count(&self) -> usize {
131        self.levels.len() / self.faces()
132    }
133}
134
135// ---------------------------------------------------------------- GraphicsMesh
136
137/// A range of a mesh part that is drawn with one material.
138#[derive(Debug, Clone)]
139pub struct DrawRange {
140    /// Base vertex added to each range-local u16 index.
141    pub start_vertex: i32,
142    /// The number of vertices.
143    pub vertex_count: i32,
144    /// The first index.
145    pub start_index: i32,
146    /// The number of indices.
147    pub index_count: i32,
148    /// The material of the range, an index into the mesh's material set.
149    pub material_index: i32,
150    /// Rigid node transform, or -1 for weighted skinning.
151    pub bone: i32,
152}
153
154/// A part of a mesh, with its level of detail, its draw ranges and its buffers.
155#[derive(Debug, Clone)]
156pub struct MeshPart {
157    /// The distance up to which this level of detail is used.
158    pub lod_distance: f32,
159    /// The part's name.
160    pub name: String,
161    /// The ambient-occlusion texture.
162    pub ao_texture: Option<String>,
163    /// The draw ranges.
164    pub ranges: Vec<DrawRange>,
165    /// Both streams can be present in one detail level (for example, the trader boat).
166    pub static_indices: Vec<u8>,
167    /// The vertex buffer of the rigid geometry.
168    pub static_vertices: Vec<u8>,
169    /// The index buffer of the skinned geometry.
170    pub skinned_indices: Vec<u8>,
171    /// The vertex buffer of the skinned geometry.
172    pub skinned_vertices: Vec<u8>,
173}
174
175impl MeshPart {
176    /// Select the index and vertex stream used by a triangle list.
177    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/// A bone of a skeleton.
191#[derive(Debug, Clone)]
192pub struct Bone {
193    /// The bone's name.
194    pub name: String,
195    /// The parent bone's index (-1 for the root).
196    pub parent: i32,
197    /// The rotation relative to the parent (a quaternion, stored conjugated).
198    pub rotation: [f32; 4],
199    /// The translation relative to the parent.
200    pub translation: [f32; 3],
201    /// The rotation of the inverse bind transform.
202    pub inv_rotation: [f32; 4],
203    /// The translation of the inverse bind transform.
204    pub inv_translation: [f32; 3],
205}
206
207/// A mesh: its parts, its material sets, its skeleton and its bounds.
208#[derive(Debug, Clone)]
209pub struct GraphicsMesh {
210    /// The original model file.
211    pub source: String,
212    /// The parts.
213    pub parts: Vec<MeshPart>,
214    /// Material instance references, one list per material set.
215    pub material_instances: Vec<Vec<Option<String>>>,
216    /// The skeleton's bones (empty for a rigid mesh).
217    pub bones: Vec<Bone>,
218    /// The smallest corner of the bounding box.
219    pub bounds_min: [f32; 3],
220    /// The largest corner of the bounding box.
221    pub bounds_max: [f32; 3],
222}
223
224/// An animation clip: the bones' transforms for each frame, and its events.
225#[derive(Debug, Clone)]
226pub struct AnimationClip {
227    /// The clip's name, for example `Walk`.
228    pub name: String,
229    /// The mesh it was made for.
230    pub mesh_name: String,
231    /// The original animation file.
232    pub source_name: String,
233    /// Whether the clip loops.
234    pub looping: bool,
235    /// The frame rate.
236    pub sample_rate: f32,
237    // +0x30/+0x34
238    /// Animation: source frame range for loop-point conversion.
239    pub start_frame: i32,
240    /// The last frame of the source range.
241    pub end_frame: i32,
242    // +0x38 / +0x48
243    /// Declared rate and stored sample interval.
244    pub declared_rate: f32,
245    /// The time between stored samples.
246    pub sample_interval: f32,
247    // +0x40
248    /// Source frame at which a looping animation resumes.
249    pub loop_start_frame: i32,
250    // +0x28 / +0x2c
251    /// Shared animation-instance configuration.
252    pub instance_count: i32,
253    /// Whether all entities share one instance of the clip.
254    pub instanced: bool,
255    /// The frame at which a looping clip starts again.
256    pub loop_start: usize,
257    /// The number of bones in each frame.
258    pub bone_count: usize,
259    /// Frame-major quaternion (xyzw) + translation (xyz) samples.
260    pub frames: Vec<Vec<crate::skeleton::Transform>>,
261    // +0x58: a serialised System::Time (microseconds, Runtime Timer::Startup 0x1800cd030).
262    /// The clip's length, in microseconds.
263    pub duration_ticks: i64,
264    /// The events of the clip.
265    pub events: Vec<AnimationEvent>,
266}
267
268/// An event in a clip (for example a footstep sound), at a time.
269#[derive(Debug, Clone)]
270pub struct AnimationEvent {
271    /// The frame at which it happens.
272    pub frame: i32,
273    /// The resource it plays (for example a sound effect).
274    pub resource: Option<String>,
275}
276
277/// How the game blends from one clip to another.
278#[derive(Debug, Clone)]
279pub struct AnimationBlend {
280    /// The clip that ends.
281    pub source: String,
282    /// The clip that begins.
283    pub target: String,
284    /// The blend time.
285    pub seconds: f32,
286}
287
288/// A group of animation clips for one skeleton.
289#[derive(Debug, Clone)]
290pub struct AnimationGroup {
291    /// The clips.
292    pub clips: Vec<AnimationClip>,
293    // +0x30; +0x38/+0x40
294    /// The blend time between clips that have no blend of their own, in seconds.
295    pub default_blend: f32,
296    /// The blends between named clips.
297    pub blends: Vec<AnimationBlend>,
298    // +0x48/+0x50
299    /// Precompiled [new clip][previous clip] transition matrix.
300    pub blend_matrix: Vec<f32>,
301}
302
303impl AnimationGroup {
304    /// The resource class name.
305    pub const CLASS: &'static str = "AnimationGroup";
306
307    /// `Graphics::AnimationGroup::Serialize` and `Graphics::Animation::Serialize` from the Ghidra
308    /// Runtime export. Animation samples are 4 quaternion floats and 3 translation floats;
309    /// UpdateNodes interpolates adjacent samples per mesh node.
310    ///
311    /// Original: Runtime `0x1800fdf30`, Runtime `0x1800fd910`.
312    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                            // Engine quaternions use the inverse convention to our column-vector
355                            // transform math, as do the serialized mesh bind rotations.
356                            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
399/// `ModelLayout`: sint16x4 position, sint16x4 texcoords, unorm8x4 normal, 4 bytes padding.
400pub const STATIC_STRIDE: usize = 24;
401/// `ModelSkinLayout`: as static, plus unorm8x4 weights at 24 and uint8x4 bone indices at 28.
402pub const SKINNED_STRIDE: usize = 32;
403/// Positions are stored as int16 and scaled by this in the vertex shader.
404pub const POSITION_SCALE: f32 = 1.0 / 512.0;
405
406impl GraphicsMesh {
407    /// The resource class name.
408    pub const CLASS: &'static str = "GraphicsMesh";
409
410    /// Read a mesh. Block version 0 (meshes from older toolkits, in mods) keeps each part's
411    /// geometry with the part (the DX9 block, then the DX11 block); later versions keep all of it
412    /// in the two platform blocks at the end, the DX11 one led by an integer.
413    ///
414    /// Original: `Graphics::GraphicsMesh::Serialize` and `PlatformSerialize`; the version 0 part
415    /// geometry, Runtime `0x1800a55c0`.
416    ///
417    /// Original: Runtime `0x180101c40`, Runtime `0x1800a5830`.
418    pub fn read(r: &mut Reader) -> Result<Self> {
419        /// One part's draw ranges and buffers (DX11).
420        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>()?; // unused bounds, always (1,1,1)/(-1,-1,-1)
447            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()?; // DX9 geometry
463                    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()?; // DX9 geometry
487                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// ---------------------------------------------------------------- MaterialInstance
508
509/// A material instance: a base material, and its named textures and values.
510#[derive(Debug, Clone)]
511pub struct MaterialInstance {
512    /// The base material, which selects the shader.
513    pub material: Option<String>,
514    /// The textures by slot name, for example `diffuse`.
515    pub textures: Vec<(String, Option<String>)>,
516    /// The named values (four floats each).
517    pub vectors: Vec<(String, [f32; 4])>,
518}
519
520impl MaterialInstance {
521    /// The resource class name.
522    pub const CLASS: &'static str = "MaterialInstance";
523
524    /// Original: `Graphics::MaterialInstance::Serialize`, Runtime `0x180106300`.
525    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    /// The texture in a named slot, for example `diffuse`.
543    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// ---------------------------------------------------------------- VertexLayout
552
553/// One attribute of a vertex layout.
554#[derive(Debug, Clone)]
555pub struct VertexElement {
556    /// The semantic, for example `POSITION`.
557    pub semantic: String,
558    /// The vertex stream.
559    pub stream: i32,
560    /// The offset in the vertex, in bytes.
561    pub offset: i32,
562    /// The usage.
563    pub usage: i32,
564    /// The usage index.
565    pub usage_index: i32,
566    /// 0 float4, 1 float3, 2 float2, 3 float1, 4 sint16x4, 5 sint16x2,
567    /// 6 bgra8unorm, 7 uint8x4, 8 unorm8x4 (VideoDX11 DXGI table).
568    pub format: i32,
569}
570
571/// A vertex layout: its attributes, and the Direct3D 11 input signature.
572#[derive(Debug, Clone)]
573pub struct VertexLayout {
574    /// The attributes.
575    pub elements: Vec<VertexElement>,
576    /// The Direct3D 11 input signature.
577    pub dxbc_signature: Vec<u8>,
578}
579
580impl VertexLayout {
581    /// The resource class name.
582    pub const CLASS: &'static str = "VertexLayout";
583
584    /// Original: `Video::VertexLayout::Serialize`, Runtime `0x1800f86c0`.
585    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
608// ---------------------------------------------------------------- dispatch
609
610/// Decode a resource whose class is known statically; checks the type id.
611pub 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// ---------------------------------------------------------------- SoundBuffer
640
641/// `WAVEFORMATEX` header carried by every sound. All shipped audio is xWMA (`tag == 0x161`).
642#[derive(Debug, Clone, Copy)]
643pub struct WaveFormat {
644    /// The format tag: `0x161`, WMA v2, for every sound of the game.
645    pub tag: u16,
646    /// The number of channels.
647    pub channels: u16,
648    /// The sample rate.
649    pub sample_rate: u32,
650    /// The average data rate, in bytes per second.
651    pub avg_bytes_per_sec: u32,
652    /// The packet size in bytes.
653    pub block_align: u16,
654    /// The bits per sample.
655    pub bits_per_sample: u16,
656}
657
658/// A sound: its format, its loop flag, its length, and its data in xWMA (or the entry of its
659/// stream).
660#[derive(Debug, Clone)]
661pub struct SoundBuffer {
662    /// Whether the sound loops.
663    pub looping: bool,
664    /// Streamed sounds keep their packets in a raw `.str` package entry named by `stream_entry`.
665    pub streamed: bool,
666    /// Original source file, e.g. `Audio\Animal\Chicken0.wav`.
667    pub source: String,
668    /// The package entry of a streamed sound's packets.
669    pub stream_entry: String,
670    /// The length in seconds.
671    pub length_seconds: f32,
672    /// The format header.
673    pub format: WaveFormat,
674    /// Raw `WAVEFORMATEX(TENSIBLE)` bytes (40).
675    pub format_bytes: Vec<u8>,
676    /// xWMA `dpds` table: cumulative decoded bytes per packet, u32 LE.
677    pub packet_table: Vec<u8>,
678    /// xWMA packets (empty when streamed).
679    pub data: Vec<u8>,
680}
681
682impl SoundBuffer {
683    /// The resource class name.
684    pub const CLASS: &'static str = "SoundBuffer";
685
686    /// `Audio::SoundBuffer::Serialize` + `PlatformSerialize`.
687    ///
688    /// Original: Runtime `0x1800e2cd0`, Runtime `0x1800e2e10`.
689    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    /// Wrap as a standard xWMA RIFF file (`RIFF/XWMA` with `fmt `, `dpds`, `data`), which FFmpeg
726    /// and XAudio2 tooling read. `stream` supplies the packets for streamed sounds.
727    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);