Find the magical missing bytes at the end of Persistent.sav
Turns out there can be multiple compressed blocks in a single .sav file, so now we read all of them.
This commit is contained in:
parent
153c8610c9
commit
be82857aba
2 changed files with 52 additions and 23 deletions
|
@ -11,22 +11,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let mut data = Cursor::new(std::fs::read(&args[1])?);
|
let mut data = Cursor::new(std::fs::read(&args[1])?);
|
||||||
|
|
||||||
let compressed = CompressedSaveFile::read_le(&mut data)?;
|
let compressed = CompressedSaveFile::read_le(&mut data)?;
|
||||||
|
println!("{:#?}", compressed);
|
||||||
let mut output = vec![];
|
|
||||||
|
|
||||||
let mut uncompressed = vec![0; compressed.a_uncompresed_size as usize];
|
|
||||||
|
|
||||||
let decompress = Decompress::new(true);
|
|
||||||
|
|
||||||
let mut d = ZlibDecoder::new_with_decompress(&*compressed.compressed_data, decompress);
|
|
||||||
let size = d.read(&mut uncompressed)?;
|
|
||||||
output.extend_from_slice(&uncompressed[..size]);
|
|
||||||
std::fs::write("output.bin", &output)?;
|
|
||||||
|
|
||||||
let mut cursor = Cursor::new(&uncompressed);
|
|
||||||
|
|
||||||
let save_data = TaggedSerialization::read_le(&mut cursor);
|
|
||||||
println!("{:#?}", save_data);
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
58
src/lib.rs
58
src/lib.rs
|
@ -15,18 +15,22 @@ pub mod str_property;
|
||||||
pub mod struct_property;
|
pub mod struct_property;
|
||||||
mod structs;
|
mod structs;
|
||||||
|
|
||||||
|
use std::fs::write;
|
||||||
|
use std::io::{Cursor, Read};
|
||||||
use binrw::helpers::until_eof;
|
use binrw::helpers::until_eof;
|
||||||
|
|
||||||
use crate::array_property::ArrayProperty;
|
use crate::array_property::{ArrayEntry, ArrayKeyData, ArrayProperty};
|
||||||
use crate::bool_property::BoolProperty;
|
use crate::bool_property::BoolProperty;
|
||||||
use crate::common::{read_string_with_length, write_string_with_length};
|
use crate::common::{read_string_with_length, write_string_with_length};
|
||||||
use crate::float_property::FloatProperty;
|
use crate::float_property::FloatProperty;
|
||||||
use crate::int_property::IntProperty;
|
use crate::int_property::IntProperty;
|
||||||
use crate::map_property::MapProperty;
|
use crate::map_property::{KeyType, MapProperty};
|
||||||
use crate::set_property::SetProperty;
|
use crate::set_property::SetProperty;
|
||||||
use crate::str_property::StrProperty;
|
use crate::str_property::StrProperty;
|
||||||
use crate::struct_property::StructProperty;
|
use crate::struct_property::StructProperty;
|
||||||
use binrw::{BinRead, BinResult, binrw};
|
use binrw::{BinRead, BinResult, binrw};
|
||||||
|
use flate2::bufread::ZlibDecoder;
|
||||||
|
use flate2::Decompress;
|
||||||
|
|
||||||
// Used in ArrayProperty exclusively, but could be used instead of magic above
|
// Used in ArrayProperty exclusively, but could be used instead of magic above
|
||||||
#[binrw]
|
#[binrw]
|
||||||
|
@ -113,16 +117,56 @@ pub struct TaggedSerialization {
|
||||||
pub objs: Vec<TaggedObject>,
|
pub objs: Vec<TaggedObject>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[binrw::parser(reader, endian)]
|
||||||
|
fn read_compressed_data(
|
||||||
|
compressed_size: u64,
|
||||||
|
uncompressed_size: u64,
|
||||||
|
) -> BinResult<Vec<u8>> {
|
||||||
|
let mut compressed_data = vec![0; compressed_size as usize];
|
||||||
|
reader.read_exact(&mut compressed_data)?;
|
||||||
|
|
||||||
|
let mut uncompressed = vec![0; uncompressed_size as usize];
|
||||||
|
|
||||||
|
let mut d = ZlibDecoder::new(&*compressed_data);
|
||||||
|
let size = d.read(&mut uncompressed)?;
|
||||||
|
|
||||||
|
Ok(uncompressed[..size].to_vec())
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: there's no point in using a parser, we should just use map()
|
||||||
|
#[binrw::parser(reader, endian)]
|
||||||
|
fn read_tagged_data(
|
||||||
|
data_blocks: &Vec<CompressedBlock>
|
||||||
|
) -> BinResult<TaggedSerialization> {
|
||||||
|
let data_vecs: Vec<Vec<u8>> = data_blocks.iter().map(|x| x.data.clone()).collect();
|
||||||
|
let combined_data = data_vecs.concat();
|
||||||
|
write("output.bin", &combined_data);
|
||||||
|
|
||||||
|
let mut cursor = Cursor::new(&combined_data);
|
||||||
|
|
||||||
|
TaggedSerialization::read_le(&mut cursor)
|
||||||
|
}
|
||||||
|
|
||||||
#[binrw]
|
#[binrw]
|
||||||
#[brw(magic = 0x9e2a83c1u32)]
|
#[brw(magic = 0x9e2a83c1u32)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct CompressedSaveFile {
|
pub struct CompressedBlock {
|
||||||
#[br(pad_before = 4)]
|
#[br(pad_before = 6)]
|
||||||
pub compressed_size: u64,
|
#[br(pad_after = 5)]
|
||||||
|
pub unk: u8, // always 2
|
||||||
pub a_compresed_size: u64,
|
pub a_compresed_size: u64,
|
||||||
pub a_uncompresed_size: u64,
|
pub a_uncompresed_size: u64,
|
||||||
pub b_compresed_size: u64,
|
pub b_compresed_size: u64,
|
||||||
pub b_uncompresed_size: u64,
|
pub b_uncompresed_size: u64,
|
||||||
#[br(count = a_compresed_size)]
|
#[br(parse_with = read_compressed_data, args(a_compresed_size, a_uncompresed_size))]
|
||||||
pub compressed_data: Vec<u8>,
|
pub data: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[binrw]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct CompressedSaveFile {
|
||||||
|
#[br(parse_with = until_eof)]
|
||||||
|
pub data: Vec<CompressedBlock>,
|
||||||
|
#[br(parse_with = read_tagged_data, args(&data))]
|
||||||
|
pub value: TaggedSerialization,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue