cargo fmt
This commit is contained in:
parent
b43efe1abd
commit
584d361095
7 changed files with 142 additions and 92 deletions
|
|
@ -1,6 +1,6 @@
|
|||
use std::str;
|
||||
use anyhow::Result;
|
||||
use super::raw::Raw;
|
||||
use anyhow::Result;
|
||||
use std::str;
|
||||
|
||||
pub trait Decoder: Sized {
|
||||
fn decode(raw: &Raw, offset: usize, size: usize) -> Result<Self>;
|
||||
|
|
@ -13,17 +13,21 @@ impl Decoder for String {
|
|||
let str = &raw.mem[offset..];
|
||||
match str.iter().position(|&c| c == 0) {
|
||||
Some(pos) => Ok(str::from_utf8(&str[..pos])?.to_string()),
|
||||
None => Ok(str::from_utf8(&raw.mem[offset..offset+size])?.to_string())
|
||||
None => Ok(str::from_utf8(&raw.mem[offset..offset + size])?.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
fn encode(&self) -> Result<Raw> {
|
||||
let mut str = self.as_bytes().to_vec();
|
||||
str.push(0);
|
||||
Ok(Raw { offset: 0, size: str.len(), mem: str})
|
||||
Ok(Raw {
|
||||
offset: 0,
|
||||
size: str.len(),
|
||||
mem: str,
|
||||
})
|
||||
}
|
||||
|
||||
fn get_enc_size(&self) -> usize {
|
||||
self.len() + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,39 +1,52 @@
|
|||
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::io::Cursor;
|
||||
|
||||
// FString - Fallout
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum FStringEncoding {
|
||||
ANSI,
|
||||
WCS2
|
||||
WCS2,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct FString {
|
||||
pub encoding: FStringEncoding,
|
||||
pub enc_len: usize,
|
||||
pub str: String
|
||||
pub 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 len = flen & !(1 << 31);
|
||||
let start = offset + 4;
|
||||
if flen & (1<<31) == 0 { // ANSI
|
||||
let (str, _, _) = WINDOWS_1251.decode(&raw.mem[start..start+len]);
|
||||
Ok(FString { encoding: FStringEncoding::ANSI, enc_len: len, str: str.to_string() })
|
||||
} else { // WCS2
|
||||
let chars: Vec<u8> = raw.mem[start..start+len*2]
|
||||
.iter().step_by(2).copied().collect();
|
||||
if flen & (1 << 31) == 0 {
|
||||
// ANSI
|
||||
let (str, _, _) = WINDOWS_1251.decode(&raw.mem[start..start + len]);
|
||||
Ok(FString {
|
||||
encoding: FStringEncoding::ANSI,
|
||||
enc_len: len,
|
||||
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, enc_len: len, str: str.to_string() })
|
||||
Ok(FString {
|
||||
encoding: FStringEncoding::WCS2,
|
||||
enc_len: len,
|
||||
str: str.to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -42,22 +55,27 @@ impl Decoder for FString {
|
|||
let mut wdr = Cursor::new(&mut buf[..]);
|
||||
let (chars, _, _) = WINDOWS_1251.encode(self.str.as_str());
|
||||
if self.encoding == FStringEncoding::ANSI {
|
||||
wdr.write_u32::<LittleEndian>(chars.len() as u32 & !(1<<31))?;
|
||||
wdr.write_u32::<LittleEndian>(chars.len() as u32 & !(1 << 31))?;
|
||||
buf.extend(chars.iter());
|
||||
} else { // WCS2
|
||||
wdr.write_u32::<LittleEndian>(chars.len() as u32 | (1<<31))?;
|
||||
} else {
|
||||
// WCS2
|
||||
wdr.write_u32::<LittleEndian>(chars.len() as u32 | (1 << 31))?;
|
||||
for &c in chars.iter() {
|
||||
buf.push(c);
|
||||
buf.push(0);
|
||||
}
|
||||
};
|
||||
Ok(Raw { offset: 0, size: buf.len(), mem: buf })
|
||||
Ok(Raw {
|
||||
offset: 0,
|
||||
size: buf.len(),
|
||||
mem: buf,
|
||||
})
|
||||
}
|
||||
|
||||
fn get_enc_size(&self) -> usize {
|
||||
4 + match self.encoding {
|
||||
FStringEncoding::ANSI => self.enc_len,
|
||||
FStringEncoding::WCS2 => self.enc_len * 2
|
||||
FStringEncoding::WCS2 => self.enc_len * 2,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,27 +1,31 @@
|
|||
use std::io::BufWriter;
|
||||
use std::io::Write;
|
||||
use std::str;
|
||||
use anyhow::Result;
|
||||
use memmem::{Searcher, TwoWaySearcher};
|
||||
use std::fs;
|
||||
use std::fs::OpenOptions;
|
||||
use std::io::BufWriter;
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
use memmem::{Searcher, TwoWaySearcher};
|
||||
use anyhow::Result;
|
||||
use std::str;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Raw {
|
||||
pub offset: usize,
|
||||
pub size: usize,
|
||||
pub mem: Vec<u8>
|
||||
pub mem: Vec<u8>,
|
||||
}
|
||||
|
||||
impl Raw {
|
||||
pub fn join(offset: usize, size: usize, raws: &mut [Raw], ) -> Raw {
|
||||
pub fn join(offset: usize, size: usize, raws: &mut [Raw]) -> Raw {
|
||||
let mut mem: Vec<u8> = Vec::new();
|
||||
for raw in raws.iter_mut() {
|
||||
mem.append(&mut raw.mem);
|
||||
}
|
||||
|
||||
Raw { offset: offset, size: size, mem: mem }
|
||||
Raw {
|
||||
offset: offset,
|
||||
size: size,
|
||||
mem: mem,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn find_str(&self, str: &str, offset: usize) -> Option<usize> {
|
||||
|
|
@ -30,10 +34,10 @@ impl Raw {
|
|||
}
|
||||
|
||||
pub fn find_str_backwards(&self, str: &str) -> Option<usize> {
|
||||
for i in (0..self.mem.len()-str.len()).step_by(1024).rev() {
|
||||
for i in (0..self.mem.len() - str.len()).step_by(1024).rev() {
|
||||
match self.find_str(str, i) {
|
||||
Some(offset) => return Some(i+offset),
|
||||
None => continue
|
||||
Some(offset) => return Some(i + offset),
|
||||
None => continue,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -42,15 +46,23 @@ impl Raw {
|
|||
|
||||
pub fn load_file(path: &Path) -> Result<Raw> {
|
||||
let mem = fs::read(path)?;
|
||||
|
||||
Ok(Self { offset: 0, size: mem.len(), mem })
|
||||
|
||||
Ok(Self {
|
||||
offset: 0,
|
||||
size: mem.len(),
|
||||
mem,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn assemble_file(&self, path: &Path, blocks: Vec<Raw>) -> Result<()> {
|
||||
let mut file = BufWriter::new(OpenOptions::new()
|
||||
.create(true).truncate(true).write(true).open(path)?);
|
||||
|
||||
|
||||
let mut file = BufWriter::new(
|
||||
OpenOptions::new()
|
||||
.create(true)
|
||||
.truncate(true)
|
||||
.write(true)
|
||||
.open(path)?,
|
||||
);
|
||||
|
||||
let mut sorted = blocks;
|
||||
sorted.sort_by(|a, b| a.offset.cmp(&b.offset));
|
||||
|
||||
|
|
@ -63,7 +75,7 @@ impl Raw {
|
|||
file.write(&block.mem)?;
|
||||
// padding
|
||||
if block.size > block.mem.len() {
|
||||
for _ in 0..block.size - block.mem.len() {
|
||||
for _ in 0..block.size - block.mem.len() {
|
||||
file.write(&[0])?;
|
||||
}
|
||||
}
|
||||
|
|
@ -76,4 +88,4 @@ impl Raw {
|
|||
file.flush()?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
use std::str;
|
||||
use std::path::Path;
|
||||
use super::decoder::Decoder;
|
||||
use super::raw::Raw;
|
||||
use super::world::World;
|
||||
use anyhow::anyhow;
|
||||
use anyhow::Result;
|
||||
use byteorder::{ByteOrder, LittleEndian};
|
||||
use super::raw::Raw;
|
||||
use super::world::World;
|
||||
use super::decoder::Decoder;
|
||||
use std::path::Path;
|
||||
use std::str;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Save {
|
||||
pub raw: Raw,
|
||||
pub world: World
|
||||
pub world: World,
|
||||
}
|
||||
|
||||
impl Save {
|
||||
|
|
@ -21,21 +21,21 @@ impl Save {
|
|||
let raw = Raw::load_file(path)?;
|
||||
let world_offset = match raw.find_str_backwards(Self::WORLD_TAG) {
|
||||
Some(offset) => offset,
|
||||
None => return Err(anyhow!("no world found in file"))
|
||||
None => return Err(anyhow!("no world found in file")),
|
||||
};
|
||||
|
||||
|
||||
let mut world_size: usize = 0;
|
||||
{
|
||||
let campaign = match raw.find_str(Self::CAMPAIGN_TAG, world_offset) {
|
||||
Some(campaign) => world_offset + campaign,
|
||||
None => return Err(anyhow!("no campaign found after world"))
|
||||
None => return Err(anyhow!("no campaign found after world")),
|
||||
};
|
||||
|
||||
for i in (campaign-256..campaign).rev() {
|
||||
let fsize = LittleEndian::read_u32(&raw.mem[i..i+4]);
|
||||
if fsize & (1<<31) != 0 {
|
||||
let size = fsize ^ (1<<31);
|
||||
if size as usize <= campaign-i {
|
||||
for i in (campaign - 256..campaign).rev() {
|
||||
let fsize = LittleEndian::read_u32(&raw.mem[i..i + 4]);
|
||||
if fsize & (1 << 31) != 0 {
|
||||
let size = fsize ^ (1 << 31);
|
||||
if size as usize <= campaign - i {
|
||||
world_size = i - world_offset;
|
||||
break;
|
||||
}
|
||||
|
|
@ -51,10 +51,8 @@ impl Save {
|
|||
}
|
||||
|
||||
pub fn save(&self, path: &Path) -> Result<()> {
|
||||
self.raw.assemble_file(path, vec![
|
||||
self.world.encode()?
|
||||
])?;
|
||||
self.raw.assemble_file(path, vec![self.world.encode()?])?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,28 +1,29 @@
|
|||
use anyhow::Result;
|
||||
use super::raw::Raw;
|
||||
use super::decoder::Decoder;
|
||||
use super::raw::Raw;
|
||||
use anyhow::Result;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Tag {
|
||||
pub name: String,
|
||||
pub version: String
|
||||
pub version: String,
|
||||
}
|
||||
|
||||
impl Decoder for Tag {
|
||||
fn decode(raw: &Raw, offset: usize, size: usize) -> Result<Self> {
|
||||
let name = String::decode(raw, offset, size)?;
|
||||
let version = String::decode(raw, offset + name.len()+1, 0)?;
|
||||
Ok(Tag {name, version})
|
||||
let version = String::decode(raw, offset + name.len() + 1, 0)?;
|
||||
Ok(Tag { name, version })
|
||||
}
|
||||
|
||||
fn encode(&self) -> Result<Raw> {
|
||||
Ok(Raw::join(0, self.get_enc_size(), &mut [
|
||||
self.name.encode()?,
|
||||
self.version.encode()?
|
||||
]))
|
||||
Ok(Raw::join(
|
||||
0,
|
||||
self.get_enc_size(),
|
||||
&mut [self.name.encode()?, self.version.encode()?],
|
||||
))
|
||||
}
|
||||
|
||||
fn get_enc_size(&self) -> usize {
|
||||
self.name.get_enc_size() + self.version.get_enc_size()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,22 +1,20 @@
|
|||
use crate::fot::fstring::FStringEncoding;
|
||||
|
||||
use super::decoder::Decoder;
|
||||
use super::fstring::FString;
|
||||
use super::raw::Raw;
|
||||
use super::tag::Tag;
|
||||
use super::fstring::FString;
|
||||
use anyhow::anyhow;
|
||||
use anyhow::Result;
|
||||
use std::io::Cursor;
|
||||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||
use inflate::inflate_bytes_zlib;
|
||||
use deflate::deflate_bytes_zlib;
|
||||
use inflate::inflate_bytes_zlib;
|
||||
use std::io::Cursor;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct World {
|
||||
pub tag: Tag,
|
||||
pub uncompressed_size: u32,
|
||||
|
||||
pub data: Raw
|
||||
pub data: Raw,
|
||||
}
|
||||
|
||||
impl World {
|
||||
|
|
@ -28,11 +26,11 @@ impl World {
|
|||
let mut cur = sgd_start;
|
||||
cur += Tag::decode(&self.data, sgd_start, 0)?.get_enc_size();
|
||||
cur += 0x48;
|
||||
|
||||
|
||||
let mut rdr = Cursor::new(&self.data.mem[..]);
|
||||
rdr.set_position(cur as u64);
|
||||
let N = rdr.read_u32::<LittleEndian>()?;
|
||||
|
||||
|
||||
let mut names: Vec<FString> = Vec::new();
|
||||
cur += 4;
|
||||
for _ in 0..N {
|
||||
|
|
@ -40,7 +38,7 @@ impl World {
|
|||
cur += name.get_enc_size();
|
||||
names.push(name);
|
||||
}
|
||||
//dbg!(names);
|
||||
dbg!(names);
|
||||
|
||||
let unk1 = rdr.read_u32::<LittleEndian>()?;
|
||||
cur += 4;
|
||||
|
|
@ -65,14 +63,22 @@ impl World {
|
|||
impl Decoder for World {
|
||||
fn decode(raw: &Raw, offset: usize, size: usize) -> Result<Self> {
|
||||
let tag = Tag::decode(raw, offset, Self::WORLD_TAG_LEN)?;
|
||||
|
||||
let mut rdr = Cursor::new(&raw.mem[offset+Self::WORLD_TAG_LEN..]);
|
||||
|
||||
let mut rdr = Cursor::new(&raw.mem[offset + Self::WORLD_TAG_LEN..]);
|
||||
let uncompressed_size = rdr.read_u32::<LittleEndian>()?;
|
||||
|
||||
let data_start = offset + Self::WORLD_HDR_LEN;
|
||||
let data = inflate_bytes_zlib(&raw.mem[data_start..data_start+size])
|
||||
.map_err(|e| anyhow!(e))?;
|
||||
Ok(World { tag, uncompressed_size, data: Raw { offset, size, mem: data } })
|
||||
let data =
|
||||
inflate_bytes_zlib(&raw.mem[data_start..data_start + size]).map_err(|e| anyhow!(e))?;
|
||||
Ok(World {
|
||||
tag,
|
||||
uncompressed_size,
|
||||
data: Raw {
|
||||
offset,
|
||||
size,
|
||||
mem: data,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
fn encode(&self) -> Result<Raw> {
|
||||
|
|
@ -84,14 +90,26 @@ impl Decoder for World {
|
|||
}
|
||||
let data = deflate_bytes_zlib(&self.data.mem);
|
||||
|
||||
Ok(Raw::join(self.data.offset, self.data.size, &mut [
|
||||
self.tag.encode()?,
|
||||
Raw { offset: Self::WORLD_TAG_LEN, size: 8, mem: hdr.to_vec()},
|
||||
Raw { offset: Self::WORLD_HDR_LEN, size: data.len(), mem: data}
|
||||
]))
|
||||
Ok(Raw::join(
|
||||
self.data.offset,
|
||||
self.data.size,
|
||||
&mut [
|
||||
self.tag.encode()?,
|
||||
Raw {
|
||||
offset: Self::WORLD_TAG_LEN,
|
||||
size: 8,
|
||||
mem: hdr.to_vec(),
|
||||
},
|
||||
Raw {
|
||||
offset: Self::WORLD_HDR_LEN,
|
||||
size: data.len(),
|
||||
mem: data,
|
||||
},
|
||||
],
|
||||
))
|
||||
}
|
||||
|
||||
fn get_enc_size(&self) -> usize {
|
||||
Self::WORLD_HDR_LEN + self.data.mem.len()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,9 +11,8 @@ fn main() {
|
|||
Some(path) => path,
|
||||
None => "out.bin"
|
||||
};*/
|
||||
|
||||
|
||||
|
||||
let save = Save::load(Path::new(save_path)).expect("load save");
|
||||
save.world.test().expect("test");
|
||||
//save.save(Path::new("out.sav")).expect("failed to save");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue