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 super::raw::Raw;
|
||||||
|
use anyhow::Result;
|
||||||
|
use std::str;
|
||||||
|
|
||||||
pub trait Decoder: Sized {
|
pub trait Decoder: Sized {
|
||||||
fn decode(raw: &Raw, offset: usize, size: usize) -> Result<Self>;
|
fn decode(raw: &Raw, offset: usize, size: usize) -> Result<Self>;
|
||||||
|
|
@ -13,14 +13,18 @@ impl Decoder for String {
|
||||||
let str = &raw.mem[offset..];
|
let str = &raw.mem[offset..];
|
||||||
match str.iter().position(|&c| c == 0) {
|
match str.iter().position(|&c| c == 0) {
|
||||||
Some(pos) => Ok(str::from_utf8(&str[..pos])?.to_string()),
|
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> {
|
fn encode(&self) -> Result<Raw> {
|
||||||
let mut str = self.as_bytes().to_vec();
|
let mut str = self.as_bytes().to_vec();
|
||||||
str.push(0);
|
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 {
|
fn get_enc_size(&self) -> usize {
|
||||||
|
|
|
||||||
|
|
@ -1,39 +1,52 @@
|
||||||
use super::decoder::Decoder;
|
use super::decoder::Decoder;
|
||||||
use super::raw::Raw;
|
use super::raw::Raw;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use std::io::Cursor;
|
|
||||||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||||
use encoding_rs::WINDOWS_1251;
|
use encoding_rs::WINDOWS_1251;
|
||||||
|
use std::io::Cursor;
|
||||||
|
|
||||||
// FString - Fallout
|
// FString - Fallout
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum FStringEncoding {
|
pub enum FStringEncoding {
|
||||||
ANSI,
|
ANSI,
|
||||||
WCS2
|
WCS2,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct FString {
|
pub struct FString {
|
||||||
pub encoding: FStringEncoding,
|
pub encoding: FStringEncoding,
|
||||||
pub enc_len: usize,
|
pub enc_len: usize,
|
||||||
pub str: String
|
pub str: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Decoder for FString {
|
impl Decoder for FString {
|
||||||
fn decode(raw: &Raw, offset: usize, _: usize) -> Result<Self> {
|
fn decode(raw: &Raw, offset: usize, _: usize) -> Result<Self> {
|
||||||
let mut rdr = Cursor::new(&raw.mem[offset..]);
|
let mut rdr = Cursor::new(&raw.mem[offset..]);
|
||||||
let flen = rdr.read_u32::<LittleEndian>()? as usize;
|
let flen = rdr.read_u32::<LittleEndian>()? as usize;
|
||||||
let len = flen & !(1<<31);
|
let len = flen & !(1 << 31);
|
||||||
let start = offset + 4;
|
let start = offset + 4;
|
||||||
if flen & (1<<31) == 0 { // ANSI
|
if flen & (1 << 31) == 0 {
|
||||||
let (str, _, _) = WINDOWS_1251.decode(&raw.mem[start..start+len]);
|
// ANSI
|
||||||
Ok(FString { encoding: FStringEncoding::ANSI, enc_len: len, str: str.to_string() })
|
let (str, _, _) = WINDOWS_1251.decode(&raw.mem[start..start + len]);
|
||||||
} else { // WCS2
|
Ok(FString {
|
||||||
let chars: Vec<u8> = raw.mem[start..start+len*2]
|
encoding: FStringEncoding::ANSI,
|
||||||
.iter().step_by(2).copied().collect();
|
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);
|
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 mut wdr = Cursor::new(&mut buf[..]);
|
||||||
let (chars, _, _) = WINDOWS_1251.encode(self.str.as_str());
|
let (chars, _, _) = WINDOWS_1251.encode(self.str.as_str());
|
||||||
if self.encoding == FStringEncoding::ANSI {
|
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());
|
buf.extend(chars.iter());
|
||||||
} else { // WCS2
|
} else {
|
||||||
wdr.write_u32::<LittleEndian>(chars.len() as u32 | (1<<31))?;
|
// WCS2
|
||||||
|
wdr.write_u32::<LittleEndian>(chars.len() as u32 | (1 << 31))?;
|
||||||
for &c in chars.iter() {
|
for &c in chars.iter() {
|
||||||
buf.push(c);
|
buf.push(c);
|
||||||
buf.push(0);
|
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 {
|
fn get_enc_size(&self) -> usize {
|
||||||
4 + match self.encoding {
|
4 + match self.encoding {
|
||||||
FStringEncoding::ANSI => self.enc_len,
|
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 anyhow::Result;
|
||||||
use std::io::Write;
|
use memmem::{Searcher, TwoWaySearcher};
|
||||||
use std::str;
|
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::fs::OpenOptions;
|
use std::fs::OpenOptions;
|
||||||
|
use std::io::BufWriter;
|
||||||
|
use std::io::Write;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use memmem::{Searcher, TwoWaySearcher};
|
use std::str;
|
||||||
use anyhow::Result;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Raw {
|
pub struct Raw {
|
||||||
pub offset: usize,
|
pub offset: usize,
|
||||||
pub size: usize,
|
pub size: usize,
|
||||||
pub mem: Vec<u8>
|
pub mem: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Raw {
|
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();
|
let mut mem: Vec<u8> = Vec::new();
|
||||||
for raw in raws.iter_mut() {
|
for raw in raws.iter_mut() {
|
||||||
mem.append(&mut raw.mem);
|
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> {
|
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> {
|
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) {
|
match self.find_str(str, i) {
|
||||||
Some(offset) => return Some(i+offset),
|
Some(offset) => return Some(i + offset),
|
||||||
None => continue
|
None => continue,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -43,13 +47,21 @@ impl Raw {
|
||||||
pub fn load_file(path: &Path) -> Result<Raw> {
|
pub fn load_file(path: &Path) -> Result<Raw> {
|
||||||
let mem = fs::read(path)?;
|
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<()> {
|
pub fn assemble_file(&self, path: &Path, blocks: Vec<Raw>) -> Result<()> {
|
||||||
let mut file = BufWriter::new(OpenOptions::new()
|
let mut file = BufWriter::new(
|
||||||
.create(true).truncate(true).write(true).open(path)?);
|
OpenOptions::new()
|
||||||
|
.create(true)
|
||||||
|
.truncate(true)
|
||||||
|
.write(true)
|
||||||
|
.open(path)?,
|
||||||
|
);
|
||||||
|
|
||||||
let mut sorted = blocks;
|
let mut sorted = blocks;
|
||||||
sorted.sort_by(|a, b| a.offset.cmp(&b.offset));
|
sorted.sort_by(|a, b| a.offset.cmp(&b.offset));
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,16 @@
|
||||||
use std::str;
|
use super::decoder::Decoder;
|
||||||
use std::path::Path;
|
use super::raw::Raw;
|
||||||
|
use super::world::World;
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use byteorder::{ByteOrder, LittleEndian};
|
use byteorder::{ByteOrder, LittleEndian};
|
||||||
use super::raw::Raw;
|
use std::path::Path;
|
||||||
use super::world::World;
|
use std::str;
|
||||||
use super::decoder::Decoder;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Save {
|
pub struct Save {
|
||||||
pub raw: Raw,
|
pub raw: Raw,
|
||||||
pub world: World
|
pub world: World,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Save {
|
impl Save {
|
||||||
|
|
@ -21,21 +21,21 @@ impl Save {
|
||||||
let raw = Raw::load_file(path)?;
|
let raw = Raw::load_file(path)?;
|
||||||
let world_offset = match raw.find_str_backwards(Self::WORLD_TAG) {
|
let world_offset = match raw.find_str_backwards(Self::WORLD_TAG) {
|
||||||
Some(offset) => offset,
|
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 mut world_size: usize = 0;
|
||||||
{
|
{
|
||||||
let campaign = match raw.find_str(Self::CAMPAIGN_TAG, world_offset) {
|
let campaign = match raw.find_str(Self::CAMPAIGN_TAG, world_offset) {
|
||||||
Some(campaign) => world_offset + campaign,
|
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() {
|
for i in (campaign - 256..campaign).rev() {
|
||||||
let fsize = LittleEndian::read_u32(&raw.mem[i..i+4]);
|
let fsize = LittleEndian::read_u32(&raw.mem[i..i + 4]);
|
||||||
if fsize & (1<<31) != 0 {
|
if fsize & (1 << 31) != 0 {
|
||||||
let size = fsize ^ (1<<31);
|
let size = fsize ^ (1 << 31);
|
||||||
if size as usize <= campaign-i {
|
if size as usize <= campaign - i {
|
||||||
world_size = i - world_offset;
|
world_size = i - world_offset;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -51,9 +51,7 @@ impl Save {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn save(&self, path: &Path) -> Result<()> {
|
pub fn save(&self, path: &Path) -> Result<()> {
|
||||||
self.raw.assemble_file(path, vec![
|
self.raw.assemble_file(path, vec![self.world.encode()?])?;
|
||||||
self.world.encode()?
|
|
||||||
])?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,26 @@
|
||||||
use anyhow::Result;
|
|
||||||
use super::raw::Raw;
|
|
||||||
use super::decoder::Decoder;
|
use super::decoder::Decoder;
|
||||||
|
use super::raw::Raw;
|
||||||
|
use anyhow::Result;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Tag {
|
pub struct Tag {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub version: String
|
pub version: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Decoder for Tag {
|
impl Decoder for Tag {
|
||||||
fn decode(raw: &Raw, offset: usize, size: usize) -> Result<Self> {
|
fn decode(raw: &Raw, offset: usize, size: usize) -> Result<Self> {
|
||||||
let name = String::decode(raw, offset, size)?;
|
let name = String::decode(raw, offset, size)?;
|
||||||
let version = String::decode(raw, offset + name.len()+1, 0)?;
|
let version = String::decode(raw, offset + name.len() + 1, 0)?;
|
||||||
Ok(Tag {name, version})
|
Ok(Tag { name, version })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn encode(&self) -> Result<Raw> {
|
fn encode(&self) -> Result<Raw> {
|
||||||
Ok(Raw::join(0, self.get_enc_size(), &mut [
|
Ok(Raw::join(
|
||||||
self.name.encode()?,
|
0,
|
||||||
self.version.encode()?
|
self.get_enc_size(),
|
||||||
]))
|
&mut [self.name.encode()?, self.version.encode()?],
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_enc_size(&self) -> usize {
|
fn get_enc_size(&self) -> usize {
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,20 @@
|
||||||
use crate::fot::fstring::FStringEncoding;
|
|
||||||
|
|
||||||
use super::decoder::Decoder;
|
use super::decoder::Decoder;
|
||||||
|
use super::fstring::FString;
|
||||||
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 byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||||
use inflate::inflate_bytes_zlib;
|
|
||||||
use deflate::deflate_bytes_zlib;
|
use deflate::deflate_bytes_zlib;
|
||||||
|
use inflate::inflate_bytes_zlib;
|
||||||
|
use std::io::Cursor;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct World {
|
pub struct World {
|
||||||
pub tag: Tag,
|
pub tag: Tag,
|
||||||
pub uncompressed_size: u32,
|
pub uncompressed_size: u32,
|
||||||
|
|
||||||
pub data: Raw
|
pub data: Raw,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl World {
|
impl World {
|
||||||
|
|
@ -40,7 +38,7 @@ impl World {
|
||||||
cur += name.get_enc_size();
|
cur += name.get_enc_size();
|
||||||
names.push(name);
|
names.push(name);
|
||||||
}
|
}
|
||||||
//dbg!(names);
|
dbg!(names);
|
||||||
|
|
||||||
let unk1 = rdr.read_u32::<LittleEndian>()?;
|
let unk1 = rdr.read_u32::<LittleEndian>()?;
|
||||||
cur += 4;
|
cur += 4;
|
||||||
|
|
@ -66,13 +64,21 @@ impl Decoder for World {
|
||||||
fn decode(raw: &Raw, offset: usize, size: usize) -> Result<Self> {
|
fn decode(raw: &Raw, offset: usize, size: usize) -> Result<Self> {
|
||||||
let tag = Tag::decode(raw, offset, Self::WORLD_TAG_LEN)?;
|
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 uncompressed_size = rdr.read_u32::<LittleEndian>()?;
|
||||||
|
|
||||||
let data_start = offset + Self::WORLD_HDR_LEN;
|
let data_start = offset + Self::WORLD_HDR_LEN;
|
||||||
let data = inflate_bytes_zlib(&raw.mem[data_start..data_start+size])
|
let data =
|
||||||
.map_err(|e| anyhow!(e))?;
|
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 } })
|
Ok(World {
|
||||||
|
tag,
|
||||||
|
uncompressed_size,
|
||||||
|
data: Raw {
|
||||||
|
offset,
|
||||||
|
size,
|
||||||
|
mem: data,
|
||||||
|
},
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn encode(&self) -> Result<Raw> {
|
fn encode(&self) -> Result<Raw> {
|
||||||
|
|
@ -84,11 +90,23 @@ impl Decoder for World {
|
||||||
}
|
}
|
||||||
let data = deflate_bytes_zlib(&self.data.mem);
|
let data = deflate_bytes_zlib(&self.data.mem);
|
||||||
|
|
||||||
Ok(Raw::join(self.data.offset, self.data.size, &mut [
|
Ok(Raw::join(
|
||||||
|
self.data.offset,
|
||||||
|
self.data.size,
|
||||||
|
&mut [
|
||||||
self.tag.encode()?,
|
self.tag.encode()?,
|
||||||
Raw { offset: Self::WORLD_TAG_LEN, size: 8, mem: hdr.to_vec()},
|
Raw {
|
||||||
Raw { offset: Self::WORLD_HDR_LEN, size: data.len(), mem: data}
|
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 {
|
fn get_enc_size(&self) -> usize {
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@ fn main() {
|
||||||
None => "out.bin"
|
None => "out.bin"
|
||||||
};*/
|
};*/
|
||||||
|
|
||||||
|
|
||||||
let save = Save::load(Path::new(save_path)).expect("load save");
|
let save = Save::load(Path::new(save_path)).expect("load save");
|
||||||
save.world.test().expect("test");
|
save.world.test().expect("test");
|
||||||
//save.save(Path::new("out.sav")).expect("failed to save");
|
//save.save(Path::new("out.sav")).expect("failed to save");
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue