use chrono::{naive::NaiveDate, Datelike}; use std::io::{Read, Write}; use crate::techblox::{EntityHeader, BlockGroupEntity, parse_i64, parse_u32, Parsable, SerializedEntityDescriptor}; use crate::techblox::blocks::lookup_hashname; /// A collection of cubes and other data from a GameSave.techblox file //#[derive(Clone)] pub struct GameSave { /// Game version that this save was created by. /// This may affect how the rest of the save file was parsed. pub version: NaiveDate, /// Unused magic value in file header. pub magic1: i64, /// Amount of cubes present in the save data, as claimed by the file header. pub cube_len: u32, /// Unused magic value in file header. pub magic2: u32, /// Amount of block groups, as claimed by the file header. pub group_len: u32, /// Entity group descriptors for block group entities. pub group_headers: Vec, /// Block group entities. pub cube_groups: Vec, /// Entity group descriptors for block entities. pub cube_headers: Vec, /// Blocks pub cube_entities: Vec> } impl Parsable for GameSave { /// Process a Techblox save file from raw bytes. fn parse(data: &mut dyn Read) -> std::io::Result { // parse version let year = parse_u32(data)?; // parsed as i32 in-game for some reason let month = parse_u32(data)?; let day = parse_u32(data)?; let date = NaiveDate::from_ymd(year as i32, month, day); let magic_val1 = parse_i64(data)?; // unused let cube_count = parse_u32(data)?; // parsed as i32 in-game for some reason let magic_val2 = parse_u32(data)?; // unused let group_count = parse_u32(data)?; // parsed as i32 in-game for some reason // parse block groups let mut groups_h = Vec::::with_capacity(group_count as usize); let mut groups_e = Vec::::with_capacity(group_count as usize); for _i in 0..group_count { groups_h.push(EntityHeader::parse(data)?); groups_e.push(BlockGroupEntity::parse(data)?); } // parse cube data let mut cubes_h = Vec::::with_capacity(cube_count as usize); let mut cubes_e = Vec::>::with_capacity(cube_count as usize); for _i in 0..cube_count { let header = EntityHeader::parse(data)?; let hash = header.hash; cubes_h.push(header); cubes_e.push(lookup_hashname(hash, data)?); } // TODO Ok(Self { version: date, magic1: magic_val1, cube_len: cube_count, magic2: magic_val2, group_len: group_count, group_headers: groups_h, cube_groups: groups_e, cube_headers: cubes_h, cube_entities: cubes_e, }) } fn dump(&self, writer: &mut dyn Write) -> std::io::Result { let mut write_count: usize = 0; // version write_count += self.version.year().dump(writer)?; write_count += self.version.month().dump(writer)?; write_count += self.version.day().dump(writer)?; // magic separator write_count += self.magic1.dump(writer)?; write_count += self.cube_len.dump(writer)?; // magic separator write_count += self.magic2.dump(writer)?; write_count += self.group_len.dump(writer)?; // dump block groups for i in 0..self.group_len as usize { write_count += self.group_headers[i].dump(writer)?; write_count += self.cube_groups[i].dump(writer)?; } // dump cube data for i in 0..self.cube_len as usize { write_count += self.cube_headers[i].dump(writer)?; write_count += self.cube_entities[i].dump(writer)?; } // TODO Ok(write_count) } } impl std::string::ToString for GameSave { fn to_string(&self) -> String { format!("{}g {}c (v{})", self.group_len, self.cube_len, self.version) } }