cnmo_parse/lparse/
mod.rs

1use std::collections::HashMap;
2
3/// Enable the "Level Data" feature to parse level data
4#[cfg(any(feature = "level_data", doc))]
5pub mod level_data;
6
7use super::Rect;
8
9/// Error when loading/saving lparse files
10#[derive(thiserror::Error, Debug)]
11pub enum Error {
12    /// Couldn't open the file for reading and writing
13    #[error("Can't open the file!")]
14    CantOpenFile {
15        /// The actuall [`std::io::Error`] that came from reading and writing
16        source: std::io::Error
17    },
18    /// The file being opened is not an CNM Lparse file.
19    #[error("The file isn't a CNM LParse file.")]
20    NotLParseFile,
21    /// The version specifed can not be loaded by the parser
22    #[error("Unknown LParse version ({0}).")]
23    UnknownVersion(u32),
24    /// The lparse file is corrupted due to ...
25    #[error("LParse file corrupted possibly for reason {0}.")]
26    Corrupted(String),
27    /// Entry ID {1} (name: {0}) is corrupted
28    #[error("Entry {0} (id {1}) is corrupted.")]
29    EntryCorrupted(String, usize),
30    /// Entry ID {1} (name: {0}) has an unknown type id of {2}
31    #[error("Entry {0} (id {1}) has a unknown type id of {2}!")]
32    UnknownEntryType(String, usize, u32),
33    /// The CNM LParse struct has too many entries
34    #[error("Can't save file! Version number {0} only allows up to {1} entries, but you're trying to save {2} entries!")]
35    EntryOverflow(u32, usize, usize),
36    /// When loading a level file, the 2 files .cnmb and .cnms have mismatching version ids.
37    #[error("The CNMB file's version id {0} is mismatched with the CNMS file's version id {1}.")]
38    MismatchedVersions(u32, u32),
39    /// When looking for an entry, it couldn't find it
40    #[error("Can't find the entry {0}!")]
41    CannotFindEntry(String),
42    /// When getting a entry you expected data type X but got data type Y
43    #[error("Unexpected entry data type! Expected type {0} but got type {1} instead!")]
44    UnexpectedEntryType(u32, u32),
45}
46
47macro_rules! generate_entry_data_func {
48    ($func_name:ident, $variant:ident, $return:ident) => {
49        /// Returns a refrence to a slice of the type specified or and error of UnexpectedEntryType if it's not the same as the type.
50        pub fn $func_name(&self) -> Result<&[$return], Error> {
51            match self {
52                &Self::$variant(ref vec) => Ok(vec),
53                _ => Err(Error::UnexpectedEntryType(Self::$variant(Vec::new()).get_typeid(), self.get_typeid())),
54            }
55        }
56    };
57}
58
59macro_rules! generate_entry_data_func_mut {
60    ($func_name:ident, $variant:ident, $return:ident) => {
61        /// Returns a refrence to a mut slice of the type specified or and error of UnexpectedEntryType if it's not the same as the type.
62        pub fn $func_name(&mut self) -> Result<&mut Vec<$return>, Error> {
63            match self {
64                &mut Self::$variant(ref mut vec) => Ok(vec),
65                _ => Err(Error::UnexpectedEntryType(Self::$variant(Vec::new()).get_typeid(), self.get_typeid())),
66            }
67        }
68    };
69}
70
71/// A CNM LParse entry
72#[derive(Debug)]
73pub enum EntryData {
74    /// Null
75    Null,
76    /// Dummy, different null means unused, dummy is used but has no data field
77    Dummy,
78    /// Array of i32s
79    I32(Vec<i32>),
80    /// Array of u32s
81    U32(Vec<u32>),
82    /// Array of u8s
83    U8(Vec<u8>),
84    /// Array of u16s
85    U16(Vec<u16>),
86    /// Array of f32s
87    F32(Vec<f32>),
88    /// Array of Rects
89    Rect(Vec<Rect>),
90}
91
92impl EntryData {
93    fn from_lparse(
94        typeid: u32,
95        len: usize,
96        name: &str,
97        entry_id: usize,
98        buffer: &mut bytebuffer::ByteBuffer,
99    ) -> Result<Self, Error> {
100        match typeid {
101            0 => Ok(Self::Null),
102            1 => Ok(Self::Dummy),
103            2 => Ok(Self::I32(
104                (0..len)
105                    .map(|_| buffer.read_i32().unwrap_or_default())
106                    .collect(),
107            )),
108            3 => Ok(Self::U32(
109                (0..len)
110                    .map(|_| buffer.read_u32().unwrap_or_default())
111                    .collect(),
112            )),
113            4 => Ok(Self::U8(
114                (0..len)
115                    .map(|_| buffer.read_u8().unwrap_or_default())
116                    .collect(),
117            )),
118            5 => Ok(Self::U16(
119                (0..len)
120                    .map(|_| buffer.read_u16().unwrap_or_default())
121                    .collect(),
122            )),
123            6 => Ok(Self::F32(
124                (0..len)
125                    .map(|_| buffer.read_f32().unwrap_or_default())
126                    .collect(),
127            )),
128            7 => Ok(Self::Rect(
129                (0..len)
130                    .map(|_| {
131                        let x = buffer.read_i32().unwrap_or_default();
132                        let y = buffer.read_i32().unwrap_or_default();
133                        let w = buffer.read_i32().unwrap_or_default();
134                        let h = buffer.read_i32().unwrap_or_default();
135                        Rect { x, y, w, h }
136                    })
137                    .collect(),
138            )),
139            id => Err(Error::UnknownEntryType(name.to_string(), entry_id, id)),
140        }
141    }
142
143    fn save(&self, buffer: &mut bytebuffer::ByteBuffer) {
144        match &self {
145            &Self::I32(vec) => vec.iter().for_each(|i| buffer.write_i32(*i)),
146            &Self::U32(vec) => vec.iter().for_each(|i| buffer.write_u32(*i)),
147            &Self::U8(vec) => vec.iter().for_each(|i| buffer.write_u8(*i)),
148            &Self::U16(vec) => vec.iter().for_each(|i| buffer.write_u16(*i)),
149            &Self::F32(vec) => vec.iter().for_each(|i| buffer.write_f32(*i)),
150            &Self::Rect(vec) => vec.iter().for_each(|Rect { x, y, w, h }| {
151                buffer.write_i32(*x);
152                buffer.write_i32(*y);
153                buffer.write_i32(*w);
154                buffer.write_i32(*h);
155            }),
156            _ => {}
157        }
158    }
159
160    /// Gets the length of the entry
161    pub fn get_entry_len(&self) -> usize {
162        match &self {
163            &Self::Null | &Self::Dummy => 0,
164            &Self::I32(vec) => vec.len(),
165            &Self::U32(vec) => vec.len(),
166            &Self::U8(vec) => vec.len(),
167            &Self::U16(vec) => vec.len(),
168            &Self::F32(vec) => vec.len(),
169            &Self::Rect(vec) => vec.len(),
170        }
171    }
172
173    fn get_saved_data_size(&self) -> usize {
174        match &self {
175            &Self::Null | &Self::Dummy => 0,
176            &Self::I32(vec) => vec.len() * std::mem::size_of::<i32>(),
177            &Self::U32(vec) => vec.len() * std::mem::size_of::<u32>(),
178            &Self::U8(vec) => vec.len() * std::mem::size_of::<u8>(),
179            &Self::U16(vec) => vec.len() * std::mem::size_of::<u16>(),
180            &Self::F32(vec) => vec.len() * std::mem::size_of::<f32>(),
181            &Self::Rect(vec) => vec.len() * std::mem::size_of::<Rect>(),
182        }
183    }
184
185    fn get_typeid(&self) -> u32 {
186        match &self {
187            &Self::Null => 0,
188            &Self::Dummy => 1,
189            &Self::I32(_) => 2,
190            &Self::U32(_) => 3,
191            &Self::U8(_) => 4,
192            &Self::U16(_) => 5,
193            &Self::F32(_) => 6,
194            &Self::Rect(_) => 7,
195        }
196    }
197
198    generate_entry_data_func!(try_get_i32, I32, i32);
199    generate_entry_data_func!(try_get_u32, U32, u32);
200    generate_entry_data_func!(try_get_u8, U8, u8);
201    generate_entry_data_func!(try_get_u16, U16, u16);
202    generate_entry_data_func!(try_get_f32, F32, f32);
203    generate_entry_data_func!(try_get_rect, Rect, Rect);
204    generate_entry_data_func_mut!(try_get_i32_mut, I32, i32);
205    generate_entry_data_func_mut!(try_get_u32_mut, U32, u32);
206    generate_entry_data_func_mut!(try_get_u8_mut, U8, u8);
207    generate_entry_data_func_mut!(try_get_u16_mut, U16, u16);
208    generate_entry_data_func_mut!(try_get_f32_mut, F32, f32);
209    generate_entry_data_func_mut!(try_get_rect_mut, Rect, Rect);
210}
211
212/// Specs about the lparse struct version.
213/// 
214/// Gives info on:
215/// - How long an entry name can be
216/// - How many entries there can be max
217/// - More
218#[derive(Debug)]
219pub struct VersionSpecs {
220    version: u32,
221    num_entries: usize,
222    entry_name_size: usize,
223    header_size: usize,
224    entry_header_size: usize,
225}
226
227impl VersionSpecs {
228    /// Create version specs from a specific version
229    /// 
230    /// Currently only verison id 1 is supported
231    pub fn from_version(version: u32) -> Result<Self, Error> {
232        match version {
233            1 => Ok(Self {
234                version,
235                num_entries: 128,
236                entry_name_size: 16,
237                header_size: 4 + std::mem::size_of::<u32>(),
238                entry_header_size: std::mem::size_of::<u32>() * 3 + 16,
239            }),
240            version_id => Err(Error::UnknownVersion(version_id)),
241        }
242    }
243
244    /// Version ID
245    pub fn get_version_id(&self) -> u32 {
246        self.version
247    }
248
249    /// Maximum number of entries this version supports
250    pub fn get_num_entries(&self) -> usize {
251        self.num_entries
252    }
253
254    /// The size of entry names in this version
255    pub fn get_entry_name_size(&self) -> usize {
256        self.entry_name_size
257    }
258
259    /// The size of the lparse header in bytes
260    pub fn get_header_size(&self) -> usize {
261        self.header_size
262    }
263
264    /// The size of an entry header (meta data and the entry name)
265    pub fn get_entry_header_size(&self) -> usize {
266        self.entry_header_size
267    }
268}
269
270/// A LParse file in memory
271#[derive(Debug)]
272pub struct LParse {
273    version: VersionSpecs,
274    /// The entries present in the lparse file
275    pub entries: HashMap<String, EntryData>,
276}
277
278impl LParse {
279    /// Create an lparse file from the version id 
280    pub fn new(version: u32) -> Result<Self, Error> {
281        Ok(Self {
282            version: VersionSpecs::from_version(version)?,
283            entries: HashMap::new(),
284        })
285    }
286
287    /// Get version specs
288    pub fn get_version(&self) -> &VersionSpecs {
289        &self.version
290    }
291
292    /// Try to get an entry
293    pub fn try_get_entry(&self, name: &str) -> Result<&EntryData, Error> {
294        match self.entries.get(name) {
295            Some(entry_data) => Ok(entry_data),
296            None => Err(Error::CannotFindEntry(name.to_string())),
297        }
298    }
299
300    /// Load an lparse file from the path
301    pub fn from_file<P: AsRef<std::path::Path>>(path: P) -> Result<Self, Error> {
302        let buffer = match std::fs::read(path) {
303            Ok(f) => f,
304            Err(e) => return Err(Error::CantOpenFile { source: e }),
305        };
306
307        Self::from_memory(buffer)
308    }
309
310    /// Load an lparse file from memory
311    pub fn from_memory(buffer: Vec<u8>) -> Result<Self, Error> {
312        let mut buffer = bytebuffer::ByteBuffer::from_vec(buffer);
313        buffer.set_endian(bytebuffer::Endian::LittleEndian);
314
315        match buffer.read_bytes(4) {
316            Ok(bytes) => match String::from_utf8(bytes) {
317                Ok(s) if s == "CNML" => (),
318                _ => return Err(Error::NotLParseFile),
319            },
320            Err(_) => return Err(Error::NotLParseFile),
321        };
322
323        let version = VersionSpecs::from_version(match buffer.read_u32() {
324            Ok(x) => x,
325            Err(_) => return Err(Error::NotLParseFile),
326        })?;
327        let mut entries = HashMap::new();
328
329        for entry_id in 0..version.num_entries {
330            let name = match buffer.read_bytes(version.entry_name_size) {
331                Ok(b) => match String::from_utf8(b.clone()) {
332                    Ok(s) => s.trim_end_matches('\0').to_string(),
333                    Err(_) => {
334                        return Err(Error::EntryCorrupted(
335                            String::from_utf8_lossy(&b).into_owned(),
336                            entry_id,
337                        ))
338                    }
339                },
340                Err(_) => {
341                    return Err(Error::EntryCorrupted(
342                        "<Entry name corrupted>".to_string(),
343                        entry_id,
344                    ))
345                }
346            };
347
348            let (typeid, len, offset) =
349                match (buffer.read_u32(), buffer.read_u32(), buffer.read_u32()) {
350                    (Ok(typeid), Ok(len), Ok(offset)) => (typeid, len, offset),
351                    _ => return Err(Error::EntryCorrupted(name.clone(), entry_id)),
352                };
353
354            // Add the new entry
355            let mut data = EntryData::Null;
356            if name.len() != 0
357                && typeid != EntryData::Null.get_typeid()
358                && typeid != EntryData::Dummy.get_typeid()
359            {
360                let loc = buffer.get_rpos();
361                buffer.set_rpos(offset as usize);
362                data = EntryData::from_lparse(typeid, len as usize, &name, entry_id, &mut buffer)?;
363                buffer.set_rpos(loc);
364            }
365
366            if name.len() != 0 {
367                entries.insert(name, data);
368            }
369        }
370
371        Ok(Self { version, entries })
372    }
373
374    /// Save to a file path. Creates it if it isn't there and overwrites it if it is.
375    pub fn save_to_file<P: AsRef<std::path::Path>>(&self, path: P) -> Result<(), Error> {
376        if self.entries.len() > self.version.num_entries {
377            return Err(Error::EntryOverflow(self.version.version, self.version.num_entries, self.entries.len()))
378        }
379
380        let mut buffer = bytebuffer::ByteBuffer::new();
381        buffer.set_endian(bytebuffer::Endian::LittleEndian);
382
383        buffer.write_bytes(&"CNML".as_bytes()[0..4]);
384        buffer.write_u32(self.version.version);
385
386        let mut data_offset =
387            self.version.header_size + self.version.entry_header_size * self.version.num_entries;
388        let null_entry = (&"".to_string(), &EntryData::Null);
389        for (name, data) in self
390            .entries
391            .iter()
392            .chain((0..self.version.num_entries - self.entries.len()).map(|_| null_entry))
393        {
394            let mut name_padded = name.clone();
395            name_padded.extend((0..self.version.entry_name_size).map(|_| '\0'));
396            buffer.write_bytes(&name_padded.as_bytes()[0..self.version.entry_name_size]);
397            buffer.write_u32(data.get_typeid());
398            buffer.write_u32(data.get_entry_len() as u32);
399            buffer.write_u32(data_offset as u32);
400            data_offset += data.get_saved_data_size();
401        }
402        for (_, data) in self.entries.iter() {
403            data.save(&mut buffer);
404        }
405
406        match std::fs::write(path, buffer.as_bytes()) {
407            Ok(_) => Ok(()),
408            Err(e) => Err(Error::CantOpenFile { source: e }),
409        }
410    }
411}