z_mirror/fmm

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

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

rust-ice
2.9 KiB101 linesraw
1use anyhow::Result;
2use byteorder::{LittleEndian, ReadBytesExt};
3use compress::zlib;
4use semver::Version;
5use std::fs::File;
6use std::io::prelude::*;
7use std::io::Cursor;
8use std::io::Read;
9use std::io::SeekFrom;
10use std::path::PathBuf;
11use thiserror::Error;
12use zip::ZipArchive;
13
14use crate::read;
15use crate::read::PropertyTree;
16use crate::types::ModIdent;
17
18const READ_SIZE: usize = 1_048_576;
19
20pub struct SaveFile {
21    pub map_version: Version,
22    pub mods: Vec<ModIdent>,
23    pub path: PathBuf,
24    pub startup_settings: PropertyTree,
25}
26
27impl SaveFile {
28    pub fn from(path: PathBuf) -> Result<Self> {
29        println!("Reading save file...");
30        let mut archive = ZipArchive::new(File::open(&path)?)?;
31        let mut compressed = true;
32        let filenames: Vec<&str> = archive.file_names().collect();
33        let filename = filenames
34            .iter()
35            .find(|filename| filename.contains("level.dat0"))
36            .or_else(|| {
37                compressed = false;
38                filenames
39                    .iter()
40                    .find(|filename| filename.contains("level.dat"))
41            })
42            .map(ToString::to_string)
43            .ok_or(SaveFileErr::NoLevelDat)?;
44        let file = archive.by_name(&filename)?;
45
46        let decompressed = if compressed {
47            let mut bytes = Vec::with_capacity(READ_SIZE);
48            zlib::Decoder::new(file).read_to_end(&mut bytes)?;
49            bytes
50        } else {
51            file.bytes()
52                .take(READ_SIZE)
53                .filter_map(|byte| byte.ok())
54                .collect()
55        };
56
57        let mut cursor = Cursor::new(decompressed);
58        let version_major = cursor.read_u16::<LittleEndian>()?;
59        let version_minor = cursor.read_u16::<LittleEndian>()?;
60        let version_patch = cursor.read_u16::<LittleEndian>()?;
61        let _version_build = cursor.read_u16::<LittleEndian>()?;
62
63        // What are these for?
64        cursor.seek(SeekFrom::Current(2))?;
65
66        let _scenario_name = read::read_string(&mut cursor)?;
67        let _scenario_mod_name = read::read_string(&mut cursor)?;
68
69        // TODO: Handle campaigns
70        cursor.seek(SeekFrom::Current(14))?;
71
72        let num_mods = cursor.read_u8()?;
73
74        let mut mods = Vec::with_capacity(num_mods as usize);
75        for _ in 0..num_mods {
76            mods.push(read::read_mod(&mut cursor)?);
77        }
78
79        // What are these for?
80        cursor.seek(SeekFrom::Current(4))?;
81
82        let startup_settings = PropertyTree::load(&mut cursor)?;
83
84        Ok(Self {
85            mods,
86            map_version: Version::new(
87                version_major as u64,
88                version_minor as u64,
89                version_patch as u64,
90            ),
91            path,
92            startup_settings,
93        })
94    }
95}
96
97#[derive(Debug, Error)]
98pub enum SaveFileErr {
99    #[error("No level.dat was found in the save file")]
100    NoLevelDat,
101}