mod companion; mod glyphs; mod level; mod murals; mod robe; mod scarf; mod symbol; mod test; use std::fs::File; use std::io::{self, Read, Write}; use std::path::{Path, PathBuf}; use binrw::{until_eof, BinRead, BinReaderExt, BinWriterExt}; use chrono::{DateTime, NaiveDateTime, Utc}; use level::Level; use robe::Robe; use scarf::Scarf; use symbol::Symbol; use crate::companion::{CompanionSymbols, CompanionWithId, Companions}; use crate::glyphs::Glyphs; pub use crate::level::NAMES as LEVEL_NAMES; use crate::murals::Murals; pub use crate::robe::Color as RobeColor; pub type Result = std::result::Result; #[derive(Debug, thiserror::Error)] pub enum Error { #[error("Failed to deserialize savefile")] DeserializationFailed(binrw::Error), #[error("Failed to serialize savefile")] SerializationFailed(binrw::Error), #[error("Level id is out of range")] LevelIdOutOfRange, #[error("Level name was not found")] LevelNameNotFound, #[error("Scarf already at maximum length")] ScarfMaxLength, #[error("Scarf already at minimum length")] ScarfMinLength, #[error("Scarf can be at most 30 long")] ScarfTooLong, #[error("Symbol id is out of range")] SymbolIdOutOfRange, #[error(transparent)] RobeChange(robe::Error), #[error("Failed to read file")] FileReadingFailed(io::Error), } #[binrw::binrw] #[derive(Debug, Clone)] #[brw(little)] pub struct Savefile { #[brw(ignore)] pub path: PathBuf, #[br(count = 8)] _unknown0: Vec, pub robe: Robe, pub symbol: Symbol, pub scarf_length: Scarf, #[br(count = 4)] _unknown1: Vec, pub current_level: Level, pub total_collected_symbols: u32, #[br(assert(collected_symbols <= 21))] pub collected_symbols: u32, pub murals: Murals, #[br(count = 22)] _unknown2: Vec, #[br(parse_with = parse_last_played)] #[bw(write_with = write_last_played)] pub last_played: DateTime, #[br(count = 4)] _unknown3: Vec, pub journey_count: u64, pub glyphs: Glyphs, #[br(count = 2404)] _unknown4: Vec, pub companion_symbols: CompanionSymbols, pub companions_met: u32, #[br(count = 1024)] _unknown6: Vec, pub total_companions_met: u32, #[br(count = 24)] _unknown7: Vec, pub companions: Companions, #[br(parse_with = until_eof)] _unknown8: Vec, } impl Savefile { pub fn from_path

(path: P) -> Result where P: AsRef, { let file = File::open(&path).map_err(Error::FileReadingFailed)?; let savefile = Self { path: path.as_ref().to_owned(), ..Self::from_reader(file)? }; Ok(savefile) } pub fn from_reader(mut reader: R) -> Result where R: Read + BinReaderExt, { Ok(reader.read_le().map_err(Error::DeserializationFailed)?) } pub fn write(&self, mut writer: W) -> Result<()> where W: Write + BinWriterExt, { writer.write_le(self).map_err(Error::SerializationFailed)?; Ok(()) } pub fn current_companions<'a>(&'a self) -> impl Iterator { self.companions .iter() .enumerate() .filter_map(|(idx, item)| { if idx < self.companions_met as usize { Some(item) } else { None } }) } pub fn past_companions<'a>(&'a self) -> impl Iterator { self.companions .iter() .enumerate() .filter_map(|(idx, item)| { if idx >= self.companions_met as usize { Some(item) } else { None } }) } } #[binrw::parser(reader, endian)] fn parse_last_played() -> binrw::BinResult> { let timestamp: i64 = <_>::read_options(reader, endian, ())?; let timestamp = (timestamp / 10_000) - 11_644_473_600_000; let datetime = NaiveDateTime::from_timestamp_millis(timestamp).unwrap(); let datetime = DateTime::from_utc(datetime, Utc); Ok(datetime) } #[binrw::writer(writer, endian)] fn write_last_played(datetime: &DateTime) -> binrw::BinResult<()> { let timestamp = datetime.timestamp_millis(); let timestamp = (timestamp + 11_644_473_600_000) * 10_000; writer.write_type(×tamp, endian)?; Ok(()) }