use crate::darksiders1::gfc;
use failure::Error;
use std::io::{Read, Seek, Write};
pub fn read(stream: impl Read + Seek) -> Result<gfc::Object, Error> {
gfc::DSSaveGameManager::read_save(stream)
}
pub fn write(stream: impl Write + Seek, data: &gfc::Object) -> Result<(), Error> {
gfc::DSSaveGameManager::write_save(stream, data)
}
#[cfg(test)]
mod tests {
use crate::{darksiders1::gfc, dsav};
use failure::Error;
use flate2::read::ZlibDecoder;
use std::{
convert::{TryFrom, TryInto},
fs,
io,
ops::Range,
};
const INITIAL_BUFFER: usize = 16384;
parameterized_test!(round_trip_data, [
new_game => (&read_fixture("new-game.dsav")?),
hundo => (&read_fixture("100%.dsav")?),
]);
parameterized_test!(round_trip_json, [
new_game => (&read_fixture("new-game.dsav")?),
hundo => (&read_fixture("100%.dsav")?),
]);
parameterized_test!(round_trip_game_info, [
new_game => (&read_fixture("new-game.dsav")?),
hundo => (&read_fixture("100%.dsav")?),
]);
parameterized_test!(round_trip_player_save_data, [
new_game => (&read_fixture("new-game.dsav")?),
hundo => (&read_fixture("100%.dsav")?),
]);
parameterized_test!(round_trip_world_data, [
new_game => (&read_fixture("new-game.dsav")?),
hundo => (&read_fixture("100%.dsav")?),
]);
fn round_trip_data(dsav1: &[u8]) -> Result<(), Error> {
let object1 = dsav::read(io::Cursor::new(dsav1))?;
let mut dsav2 = Vec::with_capacity(INITIAL_BUFFER);
dsav::write(io::Cursor::new(&mut dsav2), &object1)?;
let object2 = dsav::read(io::Cursor::new(&mut dsav2))?;
assert_eq!(format!("{:#?}", object1), format!("{:#?}", object2));
Ok(())
}
fn round_trip_json(dsav: &[u8]) -> Result<(), Error> {
let object1 = dsav::read(io::Cursor::new(dsav))?;
let json = serde_json::to_string(&object1)?;
let object2: gfc::Object = serde_json::from_str(&json)?;
assert_eq!(format!("{:#?}", object1), format!("{:#?}", object2));
Ok(())
}
fn round_trip_game_info(dsav1: &[u8]) -> Result<(), Error> {
let object = dsav::read(io::Cursor::new(dsav1))?;
let mut dsav2 = Vec::with_capacity(INITIAL_BUFFER);
dsav::write(io::Cursor::new(&mut dsav2), &object)?;
let range1 = ..0x9;
let range2 = 0xd..0x39;
assert_eq!(&dsav1[range1], &dsav2[range1]);
assert_eq!(&dsav1[range2.clone()], &dsav2[range2]);
Ok(())
}
fn round_trip_player_save_data(dsav1: &[u8]) -> Result<(), Error> {
let object = dsav::read(io::Cursor::new(dsav1))?;
let mut dsav2 = Vec::with_capacity(INITIAL_BUFFER);
dsav::write(io::Cursor::new(&mut dsav2), &object)?;
let offset = 0x39;
let mut bod1 = Vec::with_capacity(INITIAL_BUFFER);
let mut bod2 = Vec::with_capacity(INITIAL_BUFFER);
io::copy(&mut ZlibDecoder::new(&dsav1[offset..]), &mut bod1)?;
io::copy(&mut ZlibDecoder::new(&dsav2[offset..]), &mut bod2)?;
assert_eq!(bod1, bod2);
Ok(())
}
fn round_trip_world_data(dsav1: &[u8]) -> Result<(), Error> {
let object = dsav::read(io::Cursor::new(dsav1))?;
let mut dsav2 = Vec::with_capacity(INITIAL_BUFFER);
dsav::write(io::Cursor::new(&mut dsav2), &object)?;
let data1 = extract_world_data(&dsav1)?;
let data2 = extract_world_data(&dsav2)?;
assert_eq!(data1, data2);
Ok(())
}
fn extract_world_data(dsav: &[u8]) -> Result<Vec<u8>, Error> {
let meta_offset: Range<usize> = 0x9..0xd;
let offset = i32::from_le_bytes(dsav[meta_offset].try_into()?);
let offset = usize::try_from(offset)? + 4;
let mut buffer = Vec::with_capacity(INITIAL_BUFFER);
io::copy(&mut ZlibDecoder::new(&dsav[offset..]), &mut buffer)?;
Ok(buffer)
}
fn read_fixture(name: &str) -> io::Result<Vec<u8>> {
let root = env!("CARGO_MANIFEST_DIR");
let path = format!("{}/src/dsav/fixtures/{}", root, name);
fs::read(&path)
}
}