Implement savefile writing

This commit is contained in:
Patrick Auernig 2023-08-07 20:01:33 +02:00
parent 8003278dcd
commit 65656f2e09
4 changed files with 103 additions and 37 deletions

View File

@ -1,9 +1,9 @@
use std::io::SeekFrom; use std::io::SeekFrom;
use binrw::{BinRead, NullString}; use binrw::{BinRead, BinWrite, BinWriterExt, NullString};
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct Companions(Vec<CompanionWithId>); pub struct Companions(Vec<CompanionWithId>);
impl Companions { impl Companions {
@ -49,11 +49,32 @@ impl BinRead for Companions {
} }
} }
impl BinWrite for Companions {
type Args<'a> = ();
#[derive(Debug)] fn write_options<W: std::io::Write + std::io::Seek>(
&self,
writer: &mut W,
endian: binrw::Endian,
_args: Self::Args<'_>,
) -> binrw::BinResult<()> {
for companion in &self.0 {
writer.write_type(companion, endian)?;
writer.write_all(&[0x01, 0x00, 0x10, 0x01])?;
}
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct CompanionSymbols(Vec<CompanionWithSymbol>); pub struct CompanionSymbols(Vec<CompanionWithSymbol>);
impl CompanionSymbols { impl CompanionSymbols {
const ENTRY_SIZE: i64 = 60;
const SECTION_SIZE: i64 = 960;
pub fn iter(&self) -> std::slice::Iter<CompanionWithSymbol> { pub fn iter(&self) -> std::slice::Iter<CompanionWithSymbol> {
self.0.iter() self.0.iter()
} }
@ -80,27 +101,48 @@ impl BinRead for CompanionSymbols {
let companion: CompanionWithSymbol = <_>::read_options(reader, endian, ())?; let companion: CompanionWithSymbol = <_>::read_options(reader, endian, ())?;
if companion.name.is_empty() { if companion.name.is_empty() {
reader.seek(SeekFrom::Current(-60))?; reader.seek(SeekFrom::Current(-Self::ENTRY_SIZE))?;
break; break;
} }
companions.push(companion); companions.push(companion);
} }
let padding = 960 - (companions.len() * 60); let padding = Self::SECTION_SIZE - (companions.len() as i64 * Self::ENTRY_SIZE);
reader.seek(SeekFrom::Current(padding as i64))?; reader.seek(SeekFrom::Current(padding))?;
Ok(Self(companions)) Ok(Self(companions))
} }
} }
impl BinWrite for CompanionSymbols {
type Args<'a> = ();
#[derive(Debug, BinRead)] fn write_options<W: std::io::Write + std::io::Seek>(
&self,
writer: &mut W,
endian: binrw::Endian,
_args: Self::Args<'_>,
) -> binrw::BinResult<()> {
for companion in &self.0 {
writer.write_type(companion, endian)?;
}
let padding = Self::SECTION_SIZE - (self.0.len() as i64 * Self::ENTRY_SIZE);
writer.write_all(&vec![0u8; padding as usize])?;
Ok(())
}
}
#[derive(Debug, Clone, BinRead, BinWrite)]
pub struct CompanionWithId { pub struct CompanionWithId {
#[brw(pad_size_to = 24, map = |raw: NullString| raw.to_string())] #[br(pad_size_to = 24, map = |raw: NullString| raw.to_string())]
#[bw(pad_size_to = 24, map = |s| NullString::from(s.as_ref()))]
pub name: String, pub name: String,
#[brw(assert(steam_id != 0))] #[br(assert(steam_id != 0))]
pub steam_id: u32, pub steam_id: u32,
} }
@ -117,14 +159,15 @@ impl CompanionWithId {
} }
#[derive(Debug, BinRead)] #[derive(Debug, Clone, BinRead, BinWrite)]
pub struct CompanionWithSymbol { pub struct CompanionWithSymbol {
#[brw(pad_size_to = 52, map = |raw: NullString| raw.to_string())] #[br(pad_size_to = 52, map = |raw: NullString| raw.to_string())]
#[bw(pad_size_to = 52, map = |s| NullString::from(s.as_ref()))]
pub name: String, pub name: String,
#[brw(count = 4)] #[br(count = 4)]
_unknown1: Vec<u8>, _unknown1: Vec<u8>,
#[brw(assert((0..=21).contains(&symbol)))] #[br(assert((0..=21).contains(&symbol)))]
pub symbol: u32, pub symbol: u32,
} }

View File

@ -1,8 +1,8 @@
use binrw::BinRead; use binrw::{BinRead, BinWrite};
#[derive(Debug, BinRead)] #[derive(Debug, Clone, BinRead, BinWrite)]
pub struct Glyphs(#[brw(count = 6)] Vec<LevelGlyphs>); pub struct Glyphs(#[br(count = 6)] Vec<LevelGlyphs>);
impl Glyphs { impl Glyphs {
const COUNT: [usize; 6] = [3, 3, 4, 3, 4, 4]; const COUNT: [usize; 6] = [3, 3, 4, 3, 4, 4];
@ -32,11 +32,11 @@ impl Glyphs {
} }
#[derive(Debug, BinRead)] #[derive(Debug, Clone, BinRead, BinWrite)]
pub struct LevelGlyphs { pub struct LevelGlyphs {
status_flags: u8, status_flags: u8,
#[brw(count = 343)] #[br(count = 343)]
_unused: Vec<u8>, _unused: Vec<u8>,
} }

View File

@ -5,9 +5,9 @@ mod test;
use core::fmt; use core::fmt;
use std::io::Read; use std::io::{Read, Write};
use binrw::{BinRead, BinReaderExt}; use binrw::{until_eof, BinRead, BinReaderExt, BinWriterExt};
use chrono::{DateTime, NaiveDateTime, Utc}; use chrono::{DateTime, NaiveDateTime, Utc};
use crate::companion::{CompanionSymbols, CompanionWithId, Companions}; use crate::companion::{CompanionSymbols, CompanionWithId, Companions};
@ -31,7 +31,7 @@ pub const LEVEL_NAMES: [&str; 12] = [
]; ];
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RobeColor { pub enum RobeColor {
Red, Red,
White, White,
@ -47,11 +47,11 @@ impl fmt::Display for RobeColor {
} }
#[binrw::binread] #[binrw::binrw]
#[derive(Debug)] #[derive(Debug, Clone)]
#[brw(little)] #[brw(little)]
pub struct Savefile { pub struct Savefile {
#[brw(count = 8)] #[br(count = 8)]
_unknown0: Vec<u8>, _unknown0: Vec<u8>,
pub robe: u32, pub robe: u32,
@ -60,57 +60,70 @@ pub struct Savefile {
pub scarf_length: u32, pub scarf_length: u32,
#[brw(count = 4)] #[br(count = 4)]
_unknown1: Vec<u8>, _unknown1: Vec<u8>,
#[brw(assert(current_level <= 12))] #[br(assert(current_level <= 12))]
pub current_level: u64, pub current_level: u64,
pub total_collected_symbols: u32, pub total_collected_symbols: u32,
#[brw(assert(collected_symbols <= 21))] #[br(assert(collected_symbols <= 21))]
pub collected_symbols: u32, pub collected_symbols: u32,
pub murals: Murals, pub murals: Murals,
#[brw(count = 22)] #[br(count = 22)]
_unknown2: Vec<u8>, _unknown2: Vec<u8>,
#[brw(parse_with = parse_last_played)] #[br(parse_with = parse_last_played)]
#[bw(write_with = write_last_played)]
pub last_played: DateTime<Utc>, pub last_played: DateTime<Utc>,
#[brw(count = 4)] #[br(count = 4)]
_unknown3: Vec<u8>, _unknown3: Vec<u8>,
pub journey_count: u64, pub journey_count: u64,
pub glyphs: Glyphs, pub glyphs: Glyphs,
#[brw(count = 2404)] #[br(count = 2404)]
_unknown4: Vec<u8>, _unknown4: Vec<u8>,
pub companion_symbols: CompanionSymbols, pub companion_symbols: CompanionSymbols,
pub companions_met: u32, pub companions_met: u32,
#[brw(count = 1024)] #[br(count = 1024)]
_unknown6: Vec<u8>, _unknown6: Vec<u8>,
pub total_companions_met: u32, pub total_companions_met: u32,
#[brw(count = 24)] #[br(count = 24)]
_unknown7: Vec<u8>, _unknown7: Vec<u8>,
pub companions: Companions, pub companions: Companions,
#[br(parse_with = until_eof)]
_unknown8: Vec<u8>,
} }
impl Savefile { impl Savefile {
pub fn from_reader<R>(mut reader: R) -> std::io::Result<Self> pub fn from_reader<R>(mut reader: R) -> binrw::BinResult<Self>
where where
R: Read + BinReaderExt, R: Read + BinReaderExt,
{ {
// TODO: implement error type // TODO: implement error type
Ok(reader.read_le().unwrap()) Ok(reader.read_le()?)
}
pub fn write<W>(&self, mut writer: W) -> binrw::BinResult<()>
where
W: Write + BinWriterExt,
{
writer.write_le(self)?;
Ok(())
} }
pub fn current_companions<'a>(&'a self) -> impl Iterator<Item = &'a CompanionWithId> { pub fn current_companions<'a>(&'a self) -> impl Iterator<Item = &'a CompanionWithId> {
@ -169,3 +182,13 @@ fn parse_last_played() -> binrw::BinResult<DateTime<Utc>> {
Ok(datetime) Ok(datetime)
} }
#[binrw::writer(writer, endian)]
fn write_last_played(datetime: &DateTime<Utc>) -> binrw::BinResult<()> {
let timestamp = datetime.timestamp_millis();
let timestamp = (timestamp + 11_644_473_600_000) * 10_000;
writer.write_type(&timestamp, endian)?;
Ok(())
}

View File

@ -1,7 +1,7 @@
use binrw::BinRead; use binrw::{BinRead, BinWrite};
#[derive(Debug, BinRead)] #[derive(Debug, Clone, Copy, BinRead, BinWrite)]
pub struct Murals { pub struct Murals {
status_flags: u16, status_flags: u16,
} }