z_mirror/fmm

git clone https://git.raiguard.me/z_mirror/fmm

raiguardRead mod-settings.dat (causing Rust ICE)216c7c7

rust-ice
3.4 KiB113 linesraw
1use anyhow::anyhow;
2use anyhow::Result;
3use byteorder::{LittleEndian, ReadBytesExt};
4use semver::Version;
5use semver::VersionReq;
6use std::collections::HashMap;
7use std::io::prelude::*;
8use std::io::Cursor;
9use std::io::Read;
10use std::io::SeekFrom;
11
12use crate::types::ModIdent;
13
14pub type DatReader = Cursor<Vec<u8>>;
15
16#[derive(Debug)]
17pub enum PropertyTree {
18    None,
19    Boolean(bool),
20    Number(f64),
21    String(Option<String>),
22    List(Vec<PropertyTree>),
23    Dictionary(HashMap<String, PropertyTree>),
24}
25
26impl PropertyTree {
27    pub fn load(reader: &mut DatReader) -> Result<Self> {
28        let pt_type = reader.read_u8()?;
29        // Internal flag that we don't have to care about
30        reader.seek(SeekFrom::Current(1))?;
31        match pt_type {
32            0 => Ok(Self::None),
33            1 => Ok(Self::Boolean(read_bool(reader)?)),
34            2 => Ok(Self::Number(reader.read_f64::<LittleEndian>()?)),
35            3 => Ok(Self::String(read_pt_string(reader)?)),
36            4 => {
37                let length = reader.read_u32::<LittleEndian>()?;
38                let mut list = Vec::with_capacity(length as usize);
39                for _ in 0..length {
40                    read_pt_string(reader)?;
41                    list.push(PropertyTree::load(reader)?);
42                }
43                Ok(Self::List(list))
44            }
45            5 => {
46                let length = reader.read_u32::<LittleEndian>()?;
47                let mut dict = HashMap::with_capacity(length as usize);
48                for _ in 0..length {
49                    dict.insert(
50                        read_pt_string(reader)?
51                            .ok_or_else(|| anyhow!("Missing key in PropertyTree Dictionary"))?,
52                        PropertyTree::load(reader)?,
53                    );
54                }
55                Ok(Self::Dictionary(dict))
56            }
57            _ => Err(anyhow!("Invalid data type in PropertyTree: {}", pt_type)),
58        }
59    }
60}
61
62pub fn read_bool(reader: &mut DatReader) -> Result<bool> {
63    match reader.read_u8()? {
64        0 => Ok(false),
65        1 => Ok(true),
66        _ => Err(anyhow!("Invalid boolean representation in PropertyTree.")),
67    }
68}
69
70pub fn read_string(reader: &mut DatReader) -> Result<String> {
71    let scenario_len = reader.read_u8()?;
72    let mut scenario_name = vec![0; scenario_len as usize];
73    reader.read_exact(&mut scenario_name)?;
74
75    Ok(String::from_utf8_lossy(&scenario_name).to_string())
76}
77
78// Strings in PropertyTrees have an extra byte to inform us if they are empty
79pub fn read_pt_string(reader: &mut DatReader) -> Result<Option<String>> {
80    if read_bool(reader)? {
81        Ok(None)
82    } else {
83        read_string(reader).map(Some)
84    }
85}
86
87pub fn read_u16_optimized(reader: &mut DatReader) -> Result<u16> {
88    let mut version: u16 = reader.read_u8()?.into();
89    if version == 255 {
90        version = reader.read_u16::<LittleEndian>()?;
91    }
92    Ok(version)
93}
94
95pub fn read_mod(reader: &mut DatReader) -> Result<ModIdent> {
96    let mod_name = read_string(reader)?;
97
98    let version_major = read_u16_optimized(reader)?;
99    let version_minor = read_u16_optimized(reader)?;
100    let version_patch = read_u16_optimized(reader)?;
101
102    // We don't care about the CRC
103    let _crc = reader.read_u32::<LittleEndian>()?;
104
105    Ok(ModIdent {
106        name: mod_name,
107        version_req: Some(VersionReq::exact(&Version::new(
108            version_major as u64,
109            version_minor as u64,
110            version_patch as u64,
111        ))),
112    })
113}