implement FString for ANSI and WCS2 (UTF-16 1251) encodings
This commit is contained in:
parent
1b05779355
commit
650cbcaaca
5 changed files with 90 additions and 1 deletions
16
Cargo.lock
generated
16
Cargo.lock
generated
|
|
@ -20,6 +20,12 @@ version = "1.4.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "deflate"
|
name = "deflate"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
|
|
@ -29,6 +35,15 @@ dependencies = [
|
||||||
"adler32",
|
"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]]
|
[[package]]
|
||||||
name = "fot-save-edit"
|
name = "fot-save-edit"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
@ -36,6 +51,7 @@ dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"deflate",
|
"deflate",
|
||||||
|
"encoding_rs",
|
||||||
"inflate",
|
"inflate",
|
||||||
"memmem",
|
"memmem",
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -9,5 +9,6 @@ edition = "2021"
|
||||||
anyhow = "1.0.75"
|
anyhow = "1.0.75"
|
||||||
byteorder = "1.4.3"
|
byteorder = "1.4.3"
|
||||||
deflate = "1.0.0"
|
deflate = "1.0.0"
|
||||||
|
encoding_rs = "0.8.33"
|
||||||
inflate = "0.4.5"
|
inflate = "0.4.5"
|
||||||
memmem = "0.1.1"
|
memmem = "0.1.1"
|
||||||
|
|
|
||||||
63
src/fot/fstring.rs
Normal file
63
src/fot/fstring.rs
Normal file
|
|
@ -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<Self> {
|
||||||
|
let mut rdr = Cursor::new(&raw.mem[offset..]);
|
||||||
|
let flen = rdr.read_u32::<LittleEndian>()? 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<u8> = 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::<LittleEndian>(self.str.len() as u32 ^ (1<<31));
|
||||||
|
buf.append(&mut self.str.clone().into_bytes());
|
||||||
|
} else { // WCS2
|
||||||
|
let _ = wdr.write_u32::<LittleEndian>(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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
use super::decoder::Decoder;
|
use super::decoder::Decoder;
|
||||||
use super::raw::Raw;
|
use super::raw::Raw;
|
||||||
use super::tag::Tag;
|
use super::tag::Tag;
|
||||||
|
use super::fstring::FString;
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use std::io::Cursor;
|
use std::io::Cursor;
|
||||||
|
|
@ -19,6 +20,13 @@ pub struct World {
|
||||||
impl World {
|
impl World {
|
||||||
const WORLD_TAG_LEN: usize = 11;
|
const WORLD_TAG_LEN: usize = 11;
|
||||||
const WORLD_HDR_LEN: usize = 0x13;
|
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 {
|
impl Decoder for World {
|
||||||
|
|
|
||||||
|
|
@ -14,5 +14,6 @@ fn main() {
|
||||||
|
|
||||||
|
|
||||||
let save = Save::load(Path::new(save_path)).expect("load save");
|
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");
|
||||||
}
|
}
|
||||||
Loading…
Add table
Reference in a new issue