Skip to main content

banished_formats/
menu_scene.rs

1//! `MenuScene` (`Game/MenuScene/Scene0..9.rsc`): the pre-built 3D scenes behind the main menu, and the
2//! MapComponent block they embed (the same serializer saves use).
3//!
4//! The layout is described in docs/MENU-SCENE.md.
5//!
6//! Original: `FUN_1400a8b00` (the menu scene, version 2), `FUN_1400b59a0` (the map component,
7//! version 6) and `FUN_1400b4670` (version 4), `FUN_14009dd90` (the camera), `FUN_14001c550` (the
8//! placements), `FUN_140020900` and `FUN_140020320` (the decals).
9
10use crate::crc::type_id;
11use crate::crs::{self, Reader, Writer};
12use crate::error::{format_err, Result};
13
14/// A tiled decal (roads, fields, building footprints) drawn over the terrain.
15#[derive(Debug, Clone)]
16pub struct Decal {
17    /// The material.
18    pub material: Option<String>,
19    /// Not identified yet.
20    pub ints: [i32; 5],
21    /// Not identified yet.
22    pub byte: u8,
23    /// Not identified yet.
24    pub int6: i32,
25    /// Not identified yet.
26    pub flag7: bool,
27    /// Not identified yet.
28    pub floats: [f32; 5],
29    /// Not identified yet.
30    pub flag13: bool,
31    /// Not identified yet (from version 2).
32    pub flag_v2: bool,
33    /// The three flags' bytes as written (saves hold some uninitialised non-0/1 bytes).
34    pub flag_bytes: [u8; 3],
35    /// 20-byte vertices: `i16 x4, i16 x4, u8 x4`.
36    pub vertices: Vec<[u8; 20]>,
37    /// The triangle indices.
38    pub indices: Vec<u16>,
39}
40
41/// The serialized terrain and water of a map (`MapComponent`).
42#[derive(Debug, Clone)]
43pub struct MapComponent {
44    /// The block's version (6 in current builds, 4 in 2015 saves).
45    pub version: u32,
46    // +0xc0..+0xd4
47    /// Size (2n), n, tile cells (32), tile vertices (33), width (2n+1), 33².
48    pub dims: [i32; 6],
49    /// The ground heights.
50    pub heights: Vec<i16>,
51    /// The terrain material.
52    pub terrain_material: Option<String>,
53    /// `size²` packed texels (see the terrain surface image).
54    ///
55    /// Original: `FUN_1400d4910`.
56    pub surface: Vec<u8>,
57    /// River and lake mesh: 16-byte vertices (`i16 x, y, z, w; i16 u, v; u8 x4`).
58    pub water_vertices: Vec<[u8; 16]>,
59    /// The water mesh's triangle indices.
60    pub water_indices: Vec<u16>,
61    /// The river material.
62    pub river_material: Option<String>,
63    /// The lake material.
64    pub lake_material: Option<String>,
65    /// Draw ranges `(first index, first vertex, vertex count, index count)` per river and lake.
66    pub river_ranges: Vec<[i32; 4]>,
67    /// The draw ranges of each lake.
68    pub lake_ranges: Vec<[i32; 4]>,
69    /// River centre lines.
70    pub river_paths: Vec<Vec<[f32; 2]>>,
71    /// The tiled decals.
72    pub decals: Vec<Decal>,
73    /// The first decal list of the second decal block.
74    pub extra_decals: Vec<Decal>,
75    /// Not identified yet (from version 4).
76    pub ints: Vec<i32>,
77    /// The decal materials (from version 6).
78    pub decal_materials: Vec<Option<String>>,
79}
80
81/// One placed mesh: position, rotation and scale, then mesh and material table indices.
82#[derive(Debug, Clone, Copy)]
83pub struct Placement {
84    /// The position, the scale (x, y, z each) and the yaw in degrees.
85    pub floats: [f32; 7],
86    /// The mesh, an index into the scene's meshes.
87    pub mesh: i16,
88    /// The material set.
89    pub material: i16,
90}
91
92/// The camera of a menu scene (also saved in game saves).
93#[derive(Debug, Clone, Copy)]
94pub struct SceneCamera {
95    /// The rotation, a quaternion (x, y, z, w).
96    pub rotation: [f32; 4],
97    /// The position.
98    pub position: [f32; 3],
99    /// The point it looks at.
100    pub target: [f32; 3],
101    /// The yaw and pitch in degrees, and the distance.
102    pub floats: [f32; 3],
103}
104
105/// A particle system placed in a menu scene.
106#[derive(Debug, Clone, Copy)]
107pub struct ParticlePlacement {
108    /// The position.
109    pub position: [f32; 3],
110    /// The yaw, in degrees.
111    pub yaw: f32,
112    /// The particle system, an index into the scene's systems.
113    pub system: i32,
114}
115
116/// A main-menu scene: its map, its placed models and particles, and its camera.
117#[derive(Debug, Clone)]
118pub struct MenuScene {
119    /// The source `.msc` path.
120    pub name: String,
121    /// The map.
122    pub map: MapComponent,
123    /// The camera.
124    pub camera: SceneCamera,
125    /// The placed models.
126    pub placements: Vec<Placement>,
127    /// The meshes.
128    pub meshes: Vec<Option<String>>,
129    /// The sky mesh.
130    pub sky: Option<String>,
131    /// The season values: snow, autumn colour, leaf loss and rain.
132    pub vector: [f32; 4],
133    /// Sun, ambient and fog colours (`0xAARRGGBB`), as in `EnvironmentMonth`.
134    pub colors: [u32; 3],
135    /// Fog start, fog end, fog maximum, sun yaw, sun pitch (inferred from the draw code).
136    pub floats: [f32; 5],
137    /// The weather effect (from version 2).
138    pub effect: Option<String>,
139    /// The weather effect's intensity.
140    pub effect_value: f32,
141    /// The placed particle systems.
142    pub particles: Vec<ParticlePlacement>,
143    /// The particle systems (chimney and iron smoke).
144    pub particle_systems: Vec<Option<String>>,
145}
146
147fn f32s<const N: usize>(r: &mut Reader) -> Result<[f32; N]> {
148    let mut out = [0.0; N];
149    for v in &mut out {
150        *v = r.f32()?;
151    }
152    Ok(out)
153}
154
155fn i16(r: &mut Reader) -> Result<i16> {
156    Ok(i16::from_le_bytes(r.bytes(2)?.try_into().unwrap()))
157}
158
159fn u16s(r: &mut Reader) -> Result<Vec<u16>> {
160    let n = r.count()?;
161    Ok(r.bytes(n * 2)?
162        .chunks_exact(2)
163        .map(|c| u16::from_le_bytes([c[0], c[1]]))
164        .collect())
165}
166
167fn refs(r: &mut Reader) -> Result<Vec<Option<String>>> {
168    (0..r.count()?).map(|_| r.external_ref()).collect()
169}
170
171fn ranges(r: &mut Reader) -> Result<Vec<[i32; 4]>> {
172    (0..r.count()?)
173        .map(|_| Ok([r.i32()?, r.i32()?, r.i32()?, r.i32()?]))
174        .collect()
175}
176
177/// `FUN_140020900`, with the enclosing serializer's version.
178fn decals(r: &mut Reader, version: u32) -> Result<Vec<Decal>> {
179    (0..r.count()?)
180        .map(|_| {
181            let material = r.external_ref()?;
182            let ints = [r.i32()?, r.i32()?, r.i32()?, r.i32()?, r.i32()?];
183            let byte = r.u8()?;
184            let int6 = r.i32()?;
185            let mut flag_bytes = [0; 3];
186            flag_bytes[0] = r.u8()?;
187            let floats = f32s::<5>(r)?;
188            flag_bytes[1] = r.u8()?;
189            if version > 1 {
190                flag_bytes[2] = r.u8()?;
191            }
192            let [flag7, flag13, flag_v2] = flag_bytes.map(|b| b != 0);
193            let (vertices, indices) = if version > 0 {
194                let n = r.count()?;
195                let vertices = (0..n)
196                    .map(|_| Ok(r.bytes(20)?.try_into().unwrap()))
197                    .collect::<Result<_>>()?;
198                (vertices, u16s(r)?)
199            } else {
200                (Vec::new(), Vec::new())
201            };
202            Ok(Decal {
203                material,
204                ints,
205                byte,
206                int6,
207                flag7,
208                floats,
209                flag13,
210                flag_v2,
211                flag_bytes,
212                vertices,
213                indices,
214            })
215        })
216        .collect()
217}
218
219/// The MapComponent block.
220///
221/// Original: `FUN_1400b59a0`.
222pub fn read_map_component(r: &mut Reader) -> Result<MapComponent> {
223    r.block(|r, version| {
224        let mut dims = [0; 6];
225        for d in &mut dims {
226            *d = r.i32()?;
227        }
228        let n = r.count()?;
229        let heights = r
230            .bytes(n * 2)?
231            .chunks_exact(2)
232            .map(|c| i16::from_le_bytes([c[0], c[1]]))
233            .collect();
234        let terrain_material = r.external_ref()?;
235        let size = dims[0].max(0) as usize;
236        let surface = r.bytes(size * size * 4)?.to_vec();
237        let (water_vertices, water_indices) = if version < 3 {
238            r.bool()?;
239            let vb = r.count()?;
240            let vertices = r
241                .bytes(vb)?
242                .chunks_exact(16)
243                .map(|c| c.try_into().unwrap())
244                .collect();
245            r.bool()?;
246            let ib = r.count()?;
247            let indices = r
248                .bytes(ib)?
249                .chunks_exact(2)
250                .map(|c| u16::from_le_bytes([c[0], c[1]]))
251                .collect();
252            (vertices, indices)
253        } else {
254            let n = r.count()?;
255            let vertices = (0..n)
256                .map(|_| Ok(r.bytes(16)?.try_into().unwrap()))
257                .collect::<Result<_>>()?;
258            (vertices, u16s(r)?)
259        };
260        let river_material = r.external_ref()?;
261        let lake_material = r.external_ref()?;
262        let river_ranges = ranges(r)?;
263        let lake_ranges = ranges(r)?;
264        let river_paths = (0..r.count()?)
265            .map(|_| (0..r.count()?).map(|_| f32s::<2>(r)).collect())
266            .collect::<Result<_>>()?;
267        let decals = decals(r, version)?;
268        let ints = if version > 3 {
269            (0..r.count()?).map(|_| r.i32()).collect::<Result<_>>()?
270        } else {
271            Vec::new()
272        };
273        let decal_materials = if version > 5 { refs(r)? } else { Vec::new() };
274        Ok(MapComponent {
275            version,
276            dims,
277            heights,
278            terrain_material,
279            surface,
280            water_vertices,
281            water_indices,
282            river_material,
283            lake_material,
284            river_ranges,
285            lake_ranges,
286            river_paths,
287            decals,
288            extra_decals: Vec::new(),
289            ints,
290            decal_materials,
291        })
292    })
293}
294
295fn write_decals(w: &mut Writer, decals: &[Decal], version: u32) -> Result<()> {
296    w.count(decals.len());
297    for d in decals {
298        // The bytes as read, unless the flags were changed since.
299        let flag = |k: usize, v: bool| {
300            if (d.flag_bytes[k] != 0) == v {
301                d.flag_bytes[k]
302            } else {
303                v as u8
304            }
305        };
306        w.external_ref(d.material.as_deref())?;
307        d.ints.iter().for_each(|&v| w.i32(v));
308        w.u8(d.byte);
309        w.i32(d.int6);
310        w.u8(flag(0, d.flag7));
311        d.floats.iter().for_each(|&v| w.f32(v));
312        w.u8(flag(1, d.flag13));
313        if version > 1 {
314            w.u8(flag(2, d.flag_v2));
315        }
316        if version > 0 {
317            w.count(d.vertices.len());
318            d.vertices.iter().for_each(|v| w.bytes(v));
319            w.count(d.indices.len());
320            d.indices.iter().for_each(|&i| w.u16(i));
321        }
322    }
323    Ok(())
324}
325
326/// The writing side of [`read_map_component`], at the version it was read at (3 or later).
327pub fn write_map_component(w: &mut Writer, map: &MapComponent) -> Result<()> {
328    let version = map.version;
329    if version < 3 {
330        return format_err("writing a MapComponent below version 3 is not supported");
331    }
332    w.block(version, |w| {
333        map.dims.iter().for_each(|&d| w.i32(d));
334        w.count(map.heights.len());
335        map.heights.iter().for_each(|&h| w.i16(h));
336        w.external_ref(map.terrain_material.as_deref())?;
337        w.bytes(&map.surface);
338        w.count(map.water_vertices.len());
339        map.water_vertices.iter().for_each(|v| w.bytes(v));
340        w.count(map.water_indices.len());
341        map.water_indices.iter().for_each(|&i| w.u16(i));
342        w.external_ref(map.river_material.as_deref())?;
343        w.external_ref(map.lake_material.as_deref())?;
344        for ranges in [&map.river_ranges, &map.lake_ranges] {
345            w.count(ranges.len());
346            ranges.iter().flatten().for_each(|&v| w.i32(v));
347        }
348        w.count(map.river_paths.len());
349        for path in &map.river_paths {
350            w.count(path.len());
351            path.iter().flatten().for_each(|&v| w.f32(v));
352        }
353        write_decals(w, &map.decals, version)?;
354        if version > 3 {
355            w.count(map.ints.len());
356            map.ints.iter().for_each(|&v| w.i32(v));
357        }
358        if version > 5 {
359            w.count(map.decal_materials.len());
360            for m in &map.decal_materials {
361                w.external_ref(m.as_deref())?;
362            }
363        }
364        Ok(())
365    })
366}
367
368/// The writing side of [`read_camera`].
369pub fn write_camera(w: &mut Writer, c: &SceneCamera) -> Result<()> {
370    w.block(0, |w| {
371        c.rotation
372            .iter()
373            .chain(&c.position)
374            .chain(&c.target)
375            .chain(&c.floats)
376            .for_each(|&v| w.f32(v));
377        Ok(())
378    })
379}
380
381/// `FUN_1400b4670`: the second decal block (replaces the decal lists).
382fn read_map_decals(r: &mut Reader, map: &mut MapComponent) -> Result<()> {
383    r.block(|r, version| {
384        map.extra_decals = decals(r, version)?;
385        map.decals = decals(r, version)?;
386        if version > 3 {
387            map.decal_materials = refs(r)?;
388        }
389        Ok(())
390    })
391}
392
393/// The camera block (also in saves).
394///
395/// Original: `FUN_14009dd90`.
396pub fn read_camera(r: &mut Reader) -> Result<SceneCamera> {
397    r.block(|r, _| {
398        Ok(SceneCamera {
399            rotation: f32s::<4>(r)?,
400            position: f32s::<3>(r)?,
401            target: f32s::<3>(r)?,
402            floats: f32s::<3>(r)?,
403        })
404    })
405}
406
407/// Read a [`MenuScene`] from the bytes of its resource.
408pub fn menu_scene(data: &[u8]) -> Result<MenuScene> {
409    let (_, value) = crs::parse(data, |r: &mut Reader, h| {
410        if h.type_id != type_id("MenuScene") {
411            return format_err(format!("expected MenuScene, found {:#010x}", h.type_id));
412        }
413        r.block(|r, version| {
414            let name = r.string()?;
415            let mut map = read_map_component(r)?;
416            if version > 0 {
417                read_map_decals(r, &mut map)?;
418            }
419            let camera = read_camera(r)?;
420            let placements = (0..r.count()?)
421                .map(|_| {
422                    Ok(Placement {
423                        floats: f32s::<7>(r)?,
424                        mesh: i16(r)?,
425                        material: i16(r)?,
426                    })
427                })
428                .collect::<Result<_>>()?;
429            let meshes = refs(r)?;
430            let sky = r.external_ref()?;
431            let vector = f32s::<4>(r)?;
432            let colors = [r.u32()?, r.u32()?, r.u32()?];
433            let floats = f32s::<5>(r)?;
434            let (mut effect, mut effect_value, mut particles, mut particle_systems) =
435                (None, 0.0, Vec::new(), Vec::new());
436            if version > 1 {
437                effect = r.external_ref()?;
438                effect_value = r.f32()?;
439                particles = (0..r.count()?)
440                    .map(|_| {
441                        Ok(ParticlePlacement {
442                            position: f32s::<3>(r)?,
443                            yaw: r.f32()?,
444                            system: r.i32()?,
445                        })
446                    })
447                    .collect::<Result<_>>()?;
448                particle_systems = refs(r)?;
449            }
450            Ok(MenuScene {
451                name,
452                map,
453                camera,
454                placements,
455                meshes,
456                sky,
457                vector,
458                colors,
459                floats,
460                effect,
461                effect_value,
462                particles,
463                particle_systems,
464            })
465        })
466    })?;
467    Ok(value)
468}