diff --git a/Cargo.lock b/Cargo.lock index b267b9e..5a60f05 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20,6 +20,12 @@ version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + [[package]] name = "deflate" version = "1.0.0" @@ -29,6 +35,15 @@ dependencies = [ "adler32", ] +[[package]] +name = "encoding_rs" +version = "0.8.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +dependencies = [ + "cfg-if", +] + [[package]] name = "fot-save-edit" version = "0.1.0" @@ -36,6 +51,7 @@ dependencies = [ "anyhow", "byteorder", "deflate", + "encoding_rs", "inflate", "memmem", ] diff --git a/Cargo.toml b/Cargo.toml index 5cfa5b4..e0f19f8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,5 +9,6 @@ edition = "2021" anyhow = "1.0.75" byteorder = "1.4.3" deflate = "1.0.0" +encoding_rs = "0.8.33" inflate = "0.4.5" memmem = "0.1.1" diff --git a/src/fot/fstring.rs b/src/fot/fstring.rs new file mode 100644 index 0000000..28ecd17 --- /dev/null +++ b/src/fot/fstring.rs @@ -0,0 +1,63 @@ +use super::decoder::Decoder; +use super::raw::Raw; +use anyhow::Result; +use std::io::Cursor; +use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; +use encoding_rs::WINDOWS_1251; +use std::str; + +// FString - Fallout String + +#[derive(Debug, PartialEq)] +pub enum FStringEncoding { + ANSI, + WCS2 +} + +#[derive(Debug)] +pub struct FString { + encoding: FStringEncoding, + str: String +} + +impl Decoder for FString { + fn decode(raw: &Raw, offset: usize, _: usize) -> Result { + let mut rdr = Cursor::new(&raw.mem[offset..]); + let flen = rdr.read_u32::()? as usize; + let len = flen ^ (1<<31); + let start = offset + 4; + if flen & (1<<31) == 0 { // ANSI + let str = str::from_utf8(&raw.mem[start..start+len])?; + Ok(FString { encoding: FStringEncoding::ANSI, str: str.to_string() }) + } else { // WCS2 + let chars: Vec = raw.mem[start..start+len*2] + .iter().step_by(2).copied().collect(); + let (str, _, _) = WINDOWS_1251.decode(&chars); + Ok(FString { encoding: FStringEncoding::WCS2, str: str.to_string() }) + } + } + + fn encode(&self) -> Raw { + let mut buf = vec![0u8, 4]; + let mut wdr = Cursor::new(&mut buf[..]); + if self.encoding == FStringEncoding::ANSI { + let _ = wdr.write_u32::(self.str.len() as u32 ^ (1<<31)); + buf.append(&mut self.str.clone().into_bytes()); + } else { // WCS2 + let _ = wdr.write_u32::(self.str.len() as u32 | (1<<31)); + let (chars, _, _) = WINDOWS_1251.encode(self.str.as_str()); + for &c in chars.iter() { + buf.push(c); + buf.push(0); + } + }; + Raw { offset: 0, size: buf.len(), mem: buf } + } + + fn get_enc_size(&self) -> usize { + match self.encoding { + FStringEncoding::ANSI => 4 + self.str.len(), + FStringEncoding::WCS2 => 4 + self.str.len() * 2 + } + } +} \ No newline at end of file diff --git a/src/fot/world.rs b/src/fot/world.rs index c89d0ae..1824477 100644 --- a/src/fot/world.rs +++ b/src/fot/world.rs @@ -1,6 +1,7 @@ use super::decoder::Decoder; use super::raw::Raw; use super::tag::Tag; +use super::fstring::FString; use anyhow::anyhow; use anyhow::Result; use std::io::Cursor; @@ -19,6 +20,13 @@ pub struct World { impl World { const WORLD_TAG_LEN: usize = 11; const WORLD_HDR_LEN: usize = 0x13; + + pub fn test(&self) -> Result<()> { + let a = FString::decode(&self.data, 0xF6, 0)?; + dbg!(&a); + + Ok(()) + } } impl Decoder for World { diff --git a/src/main.rs b/src/main.rs index bf30a2d..7572c50 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,5 +14,6 @@ fn main() { let save = Save::load(Path::new(save_path)).expect("load save"); - save.save(Path::new("out.sav")).expect("failed to save"); + save.world.test().expect("test"); + //save.save(Path::new("out.sav")).expect("failed to save"); } \ No newline at end of file