Skip to main content

rpfm_lib/files/fastbin_lists/
mod.rs

1//---------------------------------------------------------------------------//
2// Copyright (c) 2017-2026 Ismael Gutiérrez González. All rights reserved.
3//
4// This file is part of the Rusted PackFile Manager (RPFM) project,
5// which can be found here: https://github.com/Frodo45127/rpfm.
6//
7// This file is licensed under the MIT license, which can be found here:
8// https://github.com/Frodo45127/rpfm/blob/master/LICENSE.
9//---------------------------------------------------------------------------//
10
11//! Lists of a battle map saved as their own FASTBIN0 files, as Attila's tiles have them.
12//!
13//! | File                     | Type                       | Contents                           |
14//! |--------------------------|----------------------------|------------------------------------|
15//! | `*.tree_list.bin`        | [`TreeListFile`]           | Trees, by model                    |
16//! | `*.grass_list.bin`       | [`GrassListFile`]          | Grass, by type                     |
17//! | `civilian_deployment.bin`| [`CivilianDeploymentFile`] | Areas where civilians are deployed |
18//! | `civilian_shelter.bin`   | [`CivilianShelterFile`]    | Places where civilians shelter     |
19//!
20//! Each file is the FASTBIN0 signature, the version of the list, and the list.
21
22use getset::*;
23use serde_derive::{Serialize, Deserialize};
24
25use crate::binary::{ReadBytes, WriteBytes};
26use crate::error::{Result, RLibError};
27use crate::files::{DecodeableExtraData, Decodeable, EncodeableExtraData, Encodeable};
28use crate::files::bmd::civilian_deployment_list::CivilianDeploymentList;
29use crate::files::bmd::civilian_shelter_list::CivilianShelterList;
30use crate::files::bmd_vegetation::grass_list::GrassList;
31use crate::files::bmd_vegetation::tree_list::TreeList;
32use crate::utils::check_size_mismatch;
33
34/// End of the paths of tree list files.
35pub const TREE_LIST_EXTENSION: &str = ".tree_list.bin";
36
37/// End of the paths of grass list files.
38pub const GRASS_LIST_EXTENSION: &str = ".grass_list.bin";
39
40/// Name of civilian deployment files.
41pub const CIVILIAN_DEPLOYMENT_FILE_NAME: &str = "civilian_deployment.bin";
42
43/// Name of civilian shelter files.
44pub const CIVILIAN_SHELTER_FILE_NAME: &str = "civilian_shelter.bin";
45
46/// FASTBIN0 file signature.
47pub const SIGNATURE: &[u8; 8] = b"FASTBIN0";
48
49/// Version of the civilian list files. No other is known.
50const CIVILIAN_LIST_VERSION: u16 = 1;
51
52#[cfg(test)] mod fastbin_lists_test;
53
54//---------------------------------------------------------------------------//
55//                              Enum & Structs
56//---------------------------------------------------------------------------//
57
58/// A tree list file. The list has its version.
59#[derive(Default, PartialEq, Clone, Debug, Getters, MutGetters, Setters, Serialize, Deserialize)]
60#[getset(get = "pub", get_mut = "pub", set = "pub")]
61pub struct TreeListFile {
62    tree_list: TreeList,
63}
64
65/// A grass list file. The list has its version.
66#[derive(Default, PartialEq, Clone, Debug, Getters, MutGetters, Setters, Serialize, Deserialize)]
67#[getset(get = "pub", get_mut = "pub", set = "pub")]
68pub struct GrassListFile {
69    grass_list: GrassList,
70}
71
72/// A civilian deployment file.
73#[derive(Default, PartialEq, Clone, Debug, Getters, MutGetters, Setters, Serialize, Deserialize)]
74#[getset(get = "pub", get_mut = "pub", set = "pub")]
75pub struct CivilianDeploymentFile {
76    serialise_version: u16,
77    civilian_deployment_list: CivilianDeploymentList,
78}
79
80/// A civilian shelter file.
81#[derive(Default, PartialEq, Clone, Debug, Getters, MutGetters, Setters, Serialize, Deserialize)]
82#[getset(get = "pub", get_mut = "pub", set = "pub")]
83pub struct CivilianShelterFile {
84    serialise_version: u16,
85    civilian_shelter_list: CivilianShelterList,
86}
87
88//---------------------------------------------------------------------------//
89//                              Implementations
90//---------------------------------------------------------------------------//
91
92impl Decodeable for TreeListFile {
93
94    fn decode<R: ReadBytes>(data: &mut R, extra_data: &Option<DecodeableExtraData>) -> Result<Self> {
95        read_signature(data)?;
96        let tree_list = TreeList::decode(data, extra_data)?;
97        check_size_mismatch(data.stream_position()? as usize, data.len()? as usize)?;
98
99        Ok(Self { tree_list })
100    }
101}
102
103impl Encodeable for TreeListFile {
104
105    fn encode<W: WriteBytes>(&mut self, buffer: &mut W, extra_data: &Option<EncodeableExtraData>) -> Result<()> {
106        buffer.write_all(SIGNATURE)?;
107        self.tree_list.encode(buffer, extra_data)
108    }
109}
110
111impl Decodeable for GrassListFile {
112
113    fn decode<R: ReadBytes>(data: &mut R, extra_data: &Option<DecodeableExtraData>) -> Result<Self> {
114        read_signature(data)?;
115        let grass_list = GrassList::decode(data, extra_data)?;
116        check_size_mismatch(data.stream_position()? as usize, data.len()? as usize)?;
117
118        Ok(Self { grass_list })
119    }
120}
121
122impl Encodeable for GrassListFile {
123
124    fn encode<W: WriteBytes>(&mut self, buffer: &mut W, extra_data: &Option<EncodeableExtraData>) -> Result<()> {
125        buffer.write_all(SIGNATURE)?;
126        self.grass_list.encode(buffer, extra_data)
127    }
128}
129
130impl Decodeable for CivilianDeploymentFile {
131
132    fn decode<R: ReadBytes>(data: &mut R, extra_data: &Option<DecodeableExtraData>) -> Result<Self> {
133        read_signature(data)?;
134        let serialise_version = read_civilian_list_version(data, "CivilianDeploymentFile")?;
135        let civilian_deployment_list = CivilianDeploymentList::decode(data, extra_data)?;
136        check_size_mismatch(data.stream_position()? as usize, data.len()? as usize)?;
137
138        Ok(Self { serialise_version, civilian_deployment_list })
139    }
140}
141
142impl Encodeable for CivilianDeploymentFile {
143
144    fn encode<W: WriteBytes>(&mut self, buffer: &mut W, extra_data: &Option<EncodeableExtraData>) -> Result<()> {
145        buffer.write_all(SIGNATURE)?;
146        buffer.write_u16(self.serialise_version)?;
147        self.civilian_deployment_list.encode(buffer, extra_data)
148    }
149}
150
151impl Decodeable for CivilianShelterFile {
152
153    fn decode<R: ReadBytes>(data: &mut R, extra_data: &Option<DecodeableExtraData>) -> Result<Self> {
154        read_signature(data)?;
155        let serialise_version = read_civilian_list_version(data, "CivilianShelterFile")?;
156        let civilian_shelter_list = CivilianShelterList::decode(data, extra_data)?;
157        check_size_mismatch(data.stream_position()? as usize, data.len()? as usize)?;
158
159        Ok(Self { serialise_version, civilian_shelter_list })
160    }
161}
162
163impl Encodeable for CivilianShelterFile {
164
165    fn encode<W: WriteBytes>(&mut self, buffer: &mut W, extra_data: &Option<EncodeableExtraData>) -> Result<()> {
166        buffer.write_all(SIGNATURE)?;
167        buffer.write_u16(self.serialise_version)?;
168        self.civilian_shelter_list.encode(buffer, extra_data)
169    }
170}
171
172/// Reads the FASTBIN0 signature, failing if it's not there.
173fn read_signature<R: ReadBytes>(data: &mut R) -> Result<()> {
174    let signature = data.read_slice(8, false)?;
175    if signature.as_slice() != SIGNATURE {
176        return Err(RLibError::DecodingFastBinUnsupportedSignature(signature));
177    }
178
179    Ok(())
180}
181
182/// Reads the version of a civilian list file, failing if it's not the known one.
183fn read_civilian_list_version<R: ReadBytes>(data: &mut R, file_type: &str) -> Result<u16> {
184    let version = data.read_u16()?;
185    if version != CIVILIAN_LIST_VERSION {
186        return Err(RLibError::DecodingFastBinUnsupportedVersion(file_type.to_owned(), version));
187    }
188
189    Ok(version)
190}