From 92821142fd4a3a1f31f29f1e9d74ddf734329a9d Mon Sep 17 00:00:00 2001 From: raiguard Date: Tue, 1 Mar 2022 22:01:45 -0700 Subject: [PATCH] Write startup settings --- src/directory.rs | 22 +++++---- src/main.rs | 4 +- src/mod_settings.rs | 30 +++++++----- src/read.rs | 111 ++++++++++++++++++++++++++++++++++++++++++++ src/sync.rs | 2 - 5 files changed, 145 insertions(+), 24 deletions(-) diff --git a/src/directory.rs b/src/directory.rs index bd844ea..5eaf083 100644 --- a/src/directory.rs +++ b/src/directory.rs @@ -1,4 +1,4 @@ -use anyhow::Result; +use anyhow::{anyhow, Result}; use console::style; use std::collections::HashMap; use std::ffi::OsString; @@ -248,21 +248,25 @@ impl Directory { } } - // TODO: Use Result - pub fn sync_settings(&mut self, save_settings: &PropertyTree) -> Option<()> { + pub fn sync_settings(&mut self, save_settings: &PropertyTree) -> Result<()> { let startup_settings = self .mod_settings .settings - .get_mut("settings-startup")? - .as_dictionary_mut()?; - - for (setting_name, setting_value) in save_settings.as_dictionary()? { + .get_mut("startup") + .ok_or_else(|| anyhow!("No startup settings in mod-settings.dat"))? + .as_dictionary_mut() + .ok_or_else(|| anyhow!("Could not read PropertyTree dictionary."))?; + + for (setting_name, setting_value) in save_settings + .as_dictionary() + .ok_or_else(|| anyhow!("Could not read PropertyTree dictionary"))? + { startup_settings.insert(setting_name.clone(), setting_value.clone()); } - self.mod_settings.write().ok()?; + self.mod_settings.write()?; - Some(()) + Ok(()) } } diff --git a/src/main.rs b/src/main.rs index 1af0489..2536228 100644 --- a/src/main.rs +++ b/src/main.rs @@ -117,7 +117,9 @@ fn main() -> Result<()> { combined_enable.append(&mut mods); - directory.sync_settings(&save_file.startup_settings); + directory.sync_settings(&save_file.startup_settings)?; + + println!("Synced startup settings"); } // Manually enable combined_enable.append(&mut app.enable.to_vec()); diff --git a/src/mod_settings.rs b/src/mod_settings.rs index 792e00b..377bb33 100644 --- a/src/mod_settings.rs +++ b/src/mod_settings.rs @@ -1,11 +1,12 @@ use anyhow::Result; -use byteorder::{LittleEndian, ReadBytesExt}; +use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use std::fs; use std::io::{Cursor, Seek, SeekFrom}; use std::path::PathBuf; use crate::read::PropertyTree; +#[derive(Debug)] pub struct ModSettings { pub settings: PropertyTree, @@ -42,16 +43,21 @@ impl ModSettings { } pub fn write(&self) -> Result<()> { - let contents = vec![ - self.version_major, - self.version_minor, - self.version_patch, - self.version_build, - 0, - ]; - // contents.append(self.settings.write()); - // let mut file = fs::write(self.path, &contents); - - unimplemented!() + let mut output = vec![]; + + // Factorio version number + output.write_u16::(self.version_major)?; + output.write_u16::(self.version_minor)?; + output.write_u16::(self.version_patch)?; + output.write_u16::(self.version_build)?; + // Internal flag - always false + output.push(false as u8); + + // Settings PropertyTree + self.settings.write(&mut output)?; + + fs::write(&self.path, output)?; + + Ok(()) } } diff --git a/src/read.rs b/src/read.rs index 534a587..45f24e9 100644 --- a/src/read.rs +++ b/src/read.rs @@ -1,5 +1,6 @@ use anyhow::anyhow; use anyhow::Result; +use byteorder::WriteBytesExt; use byteorder::{LittleEndian, ReadBytesExt}; use semver::Version; use semver::VersionReq; @@ -61,21 +62,25 @@ impl PropertyTree { } /// Index into a PropertyTree list or dictionary. + #[allow(unused)] pub fn get(&self, key: T) -> Option<&Self> { key.index_into(self) } /// Mutably index into a PropertyTree list or dictionary. + #[allow(unused)] pub fn get_mut(&mut self, key: T) -> Option<&mut Self> { key.index_into_mut(self) } /// Returns `true` if the `PropertyTree` is a list. + #[allow(unused)] pub fn is_list(&self) -> bool { matches!(self, Self::List(_)) } /// If the `PropertyTree` is a list, returns the associated vector. Otherwise returns `None`. + #[allow(unused)] pub fn as_list(&self) -> Option<&Vec> { match self { Self::List(list) => Some(list), @@ -84,6 +89,7 @@ impl PropertyTree { } /// If the `PropertyTree` is a list, returns the associated mutable vector. Otherwise returns `None`. + #[allow(unused)] pub fn as_list_mut(&mut self) -> Option<&mut Vec> { match self { Self::List(list) => Some(list), @@ -92,6 +98,7 @@ impl PropertyTree { } /// Returns `true` if the `PropertyTree` is a list. + #[allow(unused)] pub fn is_dictionary(&self) -> bool { matches!(self, Self::Dictionary(_)) } @@ -111,6 +118,77 @@ impl PropertyTree { _ => None, } } + + /// Serializes the `PropertyTree` into the given bytestream. + pub fn write(&self, output: &mut Vec) -> Result<()> { + // Each PropertyTree type has a flag as the second byte that doesn't matter, so we just ignore it + match self { + PropertyTree::None => { + // PropertyTree type + output.write_u8(0)?; + // Internal flag + output.write_u8(0)?; + } + PropertyTree::Boolean(bool) => { + // PropertyTree type + output.write_u8(1)?; + // Internal flag + output.write_u8(0)?; + // Data + output.write_u8(if *bool { 1 } else { 0 })?; + } + PropertyTree::Number(num) => { + // PropertyTree type + output.write_u8(2)?; + // Internal flag + output.write_u8(0)?; + // Data + output.write_f64::(*num)?; + } + PropertyTree::String(str) => { + // PropertyTree type + output.write_u8(3)?; + // Internal flag + output.write_u8(0)?; + // Data + output.write_pt_string(str)?; + } + PropertyTree::List(list) => { + // PropertyTree type + output.write_u8(4)?; + // Internal flag + output.write_u8(0)?; + + // Length of the list + output.write_u32::(list.len() as u32)?; + + // List contents + for item in list { + // List keys are empty strings + output.write_pt_string(&None)?; + item.write(output)?; + } + } + PropertyTree::Dictionary(dict) => { + // PropertyTree type + output.write_u8(5)?; + // Internal flag + output.write_u8(0)?; + + // Length of the list + output.write_u32::(dict.len() as u32)?; + + // Dictionary contents + for (key, value) in dict { + // Dictionary keys always exist + output.write_pt_string(&Some(key.to_string()))?; + value.write(output)?; + } + } + }; + + Ok(()) + } } pub trait PropertyTreeKey { @@ -216,3 +294,36 @@ pub trait ReadFactorioDat: io::Read { /// All types that implement `Read` get methods defined in `ReadFactorioDat` for free. impl ReadFactorioDat for R {} + +pub trait WriteFactorioDat: io::Write { + fn write_u32_optimized(&mut self, num: u32) -> io::Result<()> { + if num < 255 { + self.write_u8(num as u8)?; + } else { + // Represented as the first byte being 255 + self.write_u8(255)?; + self.write_u32::(num)?; + } + + Ok(()) + } + + fn write_pt_string(&mut self, str: &Option) -> io::Result<()> { + if let Some(str) = str { + // Not-empty flag + self.write_u8(0)?; + // String length + self.write_u32_optimized(str.len() as u32)?; + // String contents + self.write_all(str.as_bytes())?; + } else { + // Empty flag + self.write_u8(1)?; + } + + Ok(()) + } +} + +/// All types that implement `Write` get methods defined in `WriteFactorioDat` for free. +impl WriteFactorioDat for R {} diff --git a/src/sync.rs b/src/sync.rs index 048e8ac..8ef3362 100644 --- a/src/sync.rs +++ b/src/sync.rs @@ -81,8 +81,6 @@ impl SaveFile { let startup_settings = PropertyTree::load(&mut cursor)?; - println!("FILE SETTINGS: {:#?}", startup_settings); - Ok(Self { mods, map_version: Version::new(