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 binrw::{BinRead, NullString};
use binrw::{BinRead, BinWrite, BinWriterExt, NullString};
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct Companions(Vec<CompanionWithId>);
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>);
impl CompanionSymbols {
const ENTRY_SIZE: i64 = 60;
const SECTION_SIZE: i64 = 960;
pub fn iter(&self) -> std::slice::Iter<CompanionWithSymbol> {
self.0.iter()
}
@ -80,27 +101,48 @@ impl BinRead for CompanionSymbols {
let companion: CompanionWithSymbol = <_>::read_options(reader, endian, ())?;
if companion.name.is_empty() {
reader.seek(SeekFrom::Current(-60))?;
reader.seek(SeekFrom::Current(-Self::ENTRY_SIZE))?;
break;
}
companions.push(companion);
}
let padding = 960 - (companions.len() * 60);
reader.seek(SeekFrom::Current(padding as i64))?;
let padding = Self::SECTION_SIZE - (companions.len() as i64 * Self::ENTRY_SIZE);
reader.seek(SeekFrom::Current(padding))?;
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 {
#[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,
#[brw(assert(steam_id != 0))]
#[br(assert(steam_id != 0))]
pub steam_id: u32,
}
@ -117,14 +159,15 @@ impl CompanionWithId {
}
#[derive(Debug, BinRead)]
#[derive(Debug, Clone, BinRead, BinWrite)]
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,
#[brw(count = 4)]
#[br(count = 4)]
_unknown1: Vec<u8>,
#[brw(assert((0..=21).contains(&symbol)))]
#[br(assert((0..=21).contains(&symbol)))]
pub symbol: u32,
}

View File

@ -1,8 +1,8 @@
use binrw::BinRead;
use binrw::{BinRead, BinWrite};
#[derive(Debug, BinRead)]
pub struct Glyphs(#[brw(count = 6)] Vec<LevelGlyphs>);
#[derive(Debug, Clone, BinRead, BinWrite)]
pub struct Glyphs(#[br(count = 6)] Vec<LevelGlyphs>);
impl Glyphs {
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 {
status_flags: u8,
#[brw(count = 343)]
#[br(count = 343)]
_unused: Vec<u8>,
}

View File

@ -5,9 +5,9 @@ mod test;
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 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 {
Red,
White,
@ -47,11 +47,11 @@ impl fmt::Display for RobeColor {
}
#[binrw::binread]
#[derive(Debug)]
#[binrw::binrw]
#[derive(Debug, Clone)]
#[brw(little)]
pub struct Savefile {
#[brw(count = 8)]
#[br(count = 8)]
_unknown0: Vec<u8>,
pub robe: u32,
@ -60,57 +60,70 @@ pub struct Savefile {
pub scarf_length: u32,
#[brw(count = 4)]
#[br(count = 4)]
_unknown1: Vec<u8>,
#[brw(assert(current_level <= 12))]
#[br(assert(current_level <= 12))]
pub current_level: u64,
pub total_collected_symbols: u32,
#[brw(assert(collected_symbols <= 21))]
#[br(assert(collected_symbols <= 21))]
pub collected_symbols: u32,
pub murals: Murals,
#[brw(count = 22)]
#[br(count = 22)]
_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>,
#[brw(count = 4)]
#[br(count = 4)]
_unknown3: Vec<u8>,
pub journey_count: u64,
pub glyphs: Glyphs,
#[brw(count = 2404)]
#[br(count = 2404)]
_unknown4: Vec<u8>,
pub companion_symbols: CompanionSymbols,
pub companions_met: u32,
#[brw(count = 1024)]
#[br(count = 1024)]
_unknown6: Vec<u8>,
pub total_companions_met: u32,
#[brw(count = 24)]
#[br(count = 24)]
_unknown7: Vec<u8>,
pub companions: Companions,
#[br(parse_with = until_eof)]
_unknown8: Vec<u8>,
}
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
R: Read + BinReaderExt,
{
// 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> {
@ -169,3 +182,13 @@ fn parse_last_played() -> binrw::BinResult<DateTime<Utc>> {
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 {
status_flags: u16,
}