mod companion; mod glyphs; mod murals; mod symbol; mod test; use core::fmt; use std::io::{Read, Write}; use binrw::{until_eof, BinRead, BinReaderExt, BinWriterExt}; use chrono::{DateTime, NaiveDateTime, Utc}; use symbol::Symbol; use crate::companion::{CompanionSymbols, CompanionWithId, Companions}; use crate::glyphs::Glyphs; use crate::murals::Murals; pub const LEVEL_NAMES: [&str; 12] = [ "Chapter Select", "Broken Bridge", "Pink Desert", "Sunken City", "Underground", "Tower", "Snow", "Paradise", "Credits", "Level Bryan", "Level Matt", "Level Chris", ]; 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), } #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum RobeColor { Red, White, } impl fmt::Display for RobeColor { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Red => write!(f, "Red"), Self::White => write!(f, "White"), } } } #[binrw::binrw] #[derive(Debug, Clone)] #[brw(little)] pub struct Savefile { #[br(count = 8)] _unknown0: Vec, robe: u32, pub symbol: Symbol, pub scarf_length: u32, #[br(count = 4)] _unknown1: Vec, #[br(assert(current_level <= 12))] pub current_level: u64, 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_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 } }) } pub fn current_level_name(&self) -> &'static str { LEVEL_NAMES[self.current_level as usize] } pub fn robe_color(&self) -> RobeColor { if self.robe > 3 { RobeColor::White } else { RobeColor::Red } } pub fn set_robe_color(&mut self, color: RobeColor) { self.robe = match (self.robe_color(), color) { (RobeColor::Red, RobeColor::White) => self.robe + 4, (RobeColor::White, RobeColor::Red) => self.robe - 4, _ => return, } } pub fn robe_tier(&self) -> u32 { match self.robe_color() { RobeColor::Red => self.robe + 1, RobeColor::White => self.robe - 2, } } pub fn set_robe_tier(&mut self, tier: u32) { if tier < 1 || tier > 4 { return; } self.robe = match self.robe_color() { RobeColor::Red => tier - 1, // There can't be a tier 1 white robe, setting it to the lowers possible tier RobeColor::White if tier == 1 => 4, RobeColor::White => tier + 2, } } } #[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(()) }