From 9ca398af112e23e04768b118e2ef1a5667e8de10 Mon Sep 17 00:00:00 2001 From: Adam Gausmann Date: Sun, 30 Oct 2022 00:29:14 -0500 Subject: [PATCH] Refactor internal I/O helpers into traits --- src/custom_data.rs | 14 +- src/io.rs | 198 +++++++++--------- src/lib.rs | 7 +- src/v5.rs | 485 +++++++++++++++++++++++++++------------------ src/v6.rs | 286 +++++++++----------------- 5 files changed, 496 insertions(+), 494 deletions(-) diff --git a/src/custom_data.rs b/src/custom_data.rs index 87b2162..26a5bf7 100644 --- a/src/custom_data.rs +++ b/src/custom_data.rs @@ -27,9 +27,9 @@ impl ComponentData for CircuitBoard { const TYPE_STRING: &'static str = "MHG.CircuitBoard"; fn read(reader: &mut R) -> Result { - let color = read_array(|| read_u8(reader))?; - let size_x = read_u32(reader)?; - let size_z = read_u32(reader)?; + let color = ReadFrom::read_from(reader)?; + let size_x = ReadFrom::read_from(reader)?; + let size_z = ReadFrom::read_from(reader)?; Ok(Self { color, size_x, @@ -38,11 +38,9 @@ impl ComponentData for CircuitBoard { } fn write(&self, writer: &mut W) -> Result<(), Error> { - for x in self.color { - write_u8(x, writer)?; - } - write_u32(self.size_x, writer)?; - write_u32(self.size_z, writer)?; + self.color.write_to(writer)?; + self.size_x.write_to(writer)?; + self.size_z.write_to(writer)?; Ok(()) } } diff --git a/src/io.rs b/src/io.rs index 08a85c9..e9ef8ac 100644 --- a/src/io.rs +++ b/src/io.rs @@ -1,103 +1,111 @@ use crate::error::Error; -use std::io::{Read, Write}; +use std::{ + io::{Read, Write}, + iter::repeat_with, +}; -pub fn read_u8(reader: &mut R) -> Result { - let mut bytes = [0u8; 1]; - reader.read_exact(&mut bytes)?; - Ok(u8::from_le_bytes(bytes)) +pub trait ReadFrom: Sized { + fn read_from(reader: &mut R) -> Result; } -pub fn write_u8(v: u8, writer: &mut W) -> Result<(), Error> { - writer.write_all(&v.to_le_bytes())?; - Ok(()) +pub trait ReadFromSeed: Sized { + fn read_from_seed(reader: &mut R, seed: Seed) -> Result; } -pub fn read_u16(reader: &mut R) -> Result { - let mut bytes = [0u8; 2]; - reader.read_exact(&mut bytes)?; - Ok(u16::from_le_bytes(bytes)) -} - -pub fn write_u16(v: u16, writer: &mut W) -> Result<(), Error> { - writer.write_all(&v.to_le_bytes())?; - Ok(()) -} - -pub fn read_i32(reader: &mut R) -> Result { - let mut bytes = [0u8; 4]; - reader.read_exact(&mut bytes)?; - Ok(i32::from_le_bytes(bytes)) -} - -pub fn write_i32(v: i32, writer: &mut W) -> Result<(), Error> { - writer.write_all(&v.to_le_bytes())?; - Ok(()) -} - -pub fn read_u32(reader: &mut R) -> Result { - let mut bytes = [0u8; 4]; - reader.read_exact(&mut bytes)?; - Ok(u32::from_le_bytes(bytes)) -} - -pub fn write_u32(v: u32, writer: &mut W) -> Result<(), Error> { - writer.write_all(&v.to_le_bytes())?; - Ok(()) -} - -pub fn read_f32(reader: &mut R) -> Result { - let mut bytes = [0u8; 4]; - reader.read_exact(&mut bytes)?; - Ok(f32::from_le_bytes(bytes)) -} - -pub fn write_f32(v: f32, writer: &mut W) -> Result<(), Error> { - writer.write_all(&v.to_le_bytes())?; - Ok(()) -} - -pub fn read_usize(reader: &mut R) -> Result { - read_i32(reader)?.try_into().map_err(|_| Error::InvalidSave) -} - -pub fn write_usize(v: usize, writer: &mut W) -> Result<(), Error> { - let v: i32 = v.try_into().map_err(|_| Error::InvalidSave)?; - write_i32(v, writer) -} - -pub fn read_string(reader: &mut R) -> Result { - let string_len = read_usize(reader)?; - let bytes = read_bytevec(reader, string_len)?; - String::from_utf8(bytes).map_err(|_| Error::InvalidSave) -} - -pub fn write_str(s: &str, writer: &mut W) -> Result<(), Error> { - write_usize(s.len(), writer)?; - writer.write_all(s.as_bytes())?; - Ok(()) -} - -pub fn read_vec(len: usize, mut reader: F) -> Result, Error> -where - F: FnMut() -> Result, -{ - let mut vec = Vec::with_capacity(len); - for _ in 0..len { - vec.push(reader()?); +impl ReadFromSeed<()> for T { + fn read_from_seed(reader: &mut R, seed: ()) -> Result { + let _ = seed; + Self::read_from(reader) } - Ok(vec) } -pub fn read_array(mut reader: F) -> Result<[T; LEN], Error> -where - F: FnMut() -> Result, - T: Default + Copy, -{ - let mut arr = [Default::default(); LEN]; - for slot in &mut arr { - *slot = reader()?; +pub trait WriteTo { + fn write_to(&self, writer: &mut W) -> Result<(), Error>; +} + +macro_rules! primitive_io { + ($($t:ty: $len:literal,)*) => {$( + impl ReadFrom for $t { + fn read_from(reader: &mut R) -> Result { + let mut bytes = [0u8; $len]; + reader.read_exact(&mut bytes)?; + Ok(<$t>::from_le_bytes(bytes)) + } + } + + impl WriteTo for $t { + fn write_to(&self, writer: &mut W) -> Result<(), Error> { + writer.write_all(&self.to_le_bytes())?; + Ok(()) + } + } + )*}; +} + +primitive_io! { + u8: 1, + u16: 2, + i32: 4, + u32: 4, + f32: 4, +} + +impl ReadFrom for usize { + fn read_from(reader: &mut R) -> Result { + i32::read_from(reader).and_then(|x| Self::try_from(x).map_err(|_| Error::InvalidSave)) + } +} + +impl WriteTo for usize { + fn write_to(&self, writer: &mut W) -> Result<(), Error> { + i32::try_from(*self) + .map_err(|_| Error::InvalidSave) + .and_then(|x| x.write_to(writer)) + } +} + +pub struct Length(pub usize); + +impl ReadFromSeed for Vec { + fn read_from_seed(reader: &mut R, seed: Length) -> Result { + repeat_with(|| T::read_from(reader)).take(seed.0).collect() + } +} + +impl ReadFrom for [T; N] { + fn read_from(reader: &mut R) -> Result { + let mut arr = [Default::default(); N]; + for slot in &mut arr { + *slot = T::read_from(reader)?; + } + Ok(arr) + } +} + +impl WriteTo for [T] { + fn write_to(&self, writer: &mut W) -> Result<(), Error> { + for x in self { + x.write_to(writer)?; + } + Ok(()) + } +} + +impl ReadFrom for String { + fn read_from(reader: &mut R) -> Result { + let string_len = usize::read_from(reader)?; + let mut bytes = vec![0u8; string_len]; + reader.read_exact(&mut bytes)?; + String::from_utf8(bytes).map_err(|_| Error::InvalidSave) + } +} + +impl WriteTo for str { + fn write_to(&self, writer: &mut W) -> Result<(), Error> { + self.len().write_to(writer)?; + writer.write_all(self.as_bytes())?; + Ok(()) } - Ok(arr) } pub fn read_magic(reader: &mut R, magic_bytes: &[u8]) -> Result<(), Error> { @@ -108,13 +116,3 @@ pub fn read_magic(reader: &mut R, magic_bytes: &[u8]) -> Result<(), Err } Ok(()) } - -pub fn read_version(reader: &mut R) -> Result<[i32; 4], Error> { - read_array(|| read_i32(reader)) -} - -pub fn read_bytevec(reader: &mut R, len: usize) -> Result, Error> { - let mut vec = vec![0u8; len]; - reader.read_exact(&mut vec)?; - Ok(vec) -} diff --git a/src/lib.rs b/src/lib.rs index 07e8371..418d3c4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,11 +5,10 @@ pub(crate) mod io; pub mod v5; pub mod v6; -use io::read_u8; use std::io::{Read, Write}; use crate::error::Error; -use crate::io::read_magic; +use crate::io::{read_magic, ReadFrom}; pub use v6 as latest; @@ -21,8 +20,8 @@ pub enum BlotterFile { impl BlotterFile { pub fn read(reader: &mut R) -> Result { - read_magic(reader, b"Logic World save")?; - let save_version = read_u8(reader)?; + read_magic(reader, latest::SAVE_HEADER)?; + let save_version = u8::read_from(reader)?; match save_version { v5::SAVE_VERSION => v5::BlotterFile::read_after_save_version(reader).map(Self::V5), v6::SAVE_VERSION => v6::BlotterFile::read_after_save_version(reader).map(Self::V6), diff --git a/src/v5.rs b/src/v5.rs index 5f1a2f0..979745b 100644 --- a/src/v5.rs +++ b/src/v5.rs @@ -3,6 +3,8 @@ use crate::io::*; use std::io::{Read, Write}; pub const SAVE_VERSION: u8 = 5; +pub const SAVE_HEADER: &[u8; 16] = b"Logic World save"; +pub const SAVE_FOOTER: &[u8; 16] = b"redstone sux lol"; #[derive(Debug, Clone, Copy, PartialEq)] pub enum SaveType { @@ -10,28 +12,116 @@ pub enum SaveType { Subassembly, } +impl ReadFrom for SaveType { + fn read_from(reader: &mut R) -> Result { + let id = u8::read_from(reader)?; + match id { + 1 => Ok(Self::World), + 2 => Ok(Self::Subassembly), + _ => Err(Error::InvalidSave), + } + } +} + +impl WriteTo for SaveType { + fn write_to(&self, writer: &mut W) -> Result<(), Error> { + let id: u8 = match self { + Self::World => 1, + Self::Subassembly => 2, + }; + id.write_to(writer)?; + Ok(()) + } +} + #[derive(Debug)] pub struct ModInfo { pub mod_id: String, pub mod_version: [i32; 4], } +impl ReadFrom for ModInfo { + fn read_from(reader: &mut R) -> Result { + let mod_id = ReadFrom::read_from(reader)?; + let mod_version = ReadFrom::read_from(reader)?; + Ok(Self { + mod_id, + mod_version, + }) + } +} + +impl WriteTo for ModInfo { + fn write_to(&self, writer: &mut W) -> Result<(), Error> { + self.mod_id.write_to(writer)?; + self.mod_version.write_to(writer)?; + Ok(()) + } +} + #[derive(Debug)] pub struct ComponentType { pub numeric_id: u16, pub text_id: String, } +impl ReadFrom for ComponentType { + fn read_from(reader: &mut R) -> Result { + let numeric_id = ReadFrom::read_from(reader)?; + let text_id = ReadFrom::read_from(reader)?; + Ok(Self { + numeric_id, + text_id, + }) + } +} + +impl WriteTo for ComponentType { + fn write_to(&self, writer: &mut W) -> Result<(), Error> { + self.numeric_id.write_to(writer)?; + self.text_id.write_to(writer)?; + Ok(()) + } +} + #[derive(Debug)] pub struct Input { pub circuit_state_id: i32, } +impl ReadFrom for Input { + fn read_from(reader: &mut R) -> Result { + let circuit_state_id = ReadFrom::read_from(reader)?; + Ok(Self { circuit_state_id }) + } +} + +impl WriteTo for Input { + fn write_to(&self, writer: &mut W) -> Result<(), Error> { + self.circuit_state_id.write_to(writer)?; + Ok(()) + } +} + #[derive(Debug)] pub struct Output { pub circuit_state_id: i32, } +impl ReadFrom for Output { + fn read_from(reader: &mut R) -> Result { + let circuit_state_id = ReadFrom::read_from(reader)?; + Ok(Self { circuit_state_id }) + } +} + +impl WriteTo for Output { + fn write_to(&self, writer: &mut W) -> Result<(), Error> { + self.circuit_state_id.write_to(writer)?; + Ok(()) + } +} + #[derive(Debug)] pub struct Component { pub address: u32, @@ -44,13 +134,127 @@ pub struct Component { pub custom_data: Option>, } +impl ReadFrom for Component { + fn read_from(reader: &mut R) -> Result { + let address = ReadFrom::read_from(reader)?; + let parent = ReadFrom::read_from(reader)?; + let type_id = ReadFrom::read_from(reader)?; + let position = ReadFrom::read_from(reader)?; + let rotation = ReadFrom::read_from(reader)?; + + let num_inputs = usize::read_from(reader)?; + let inputs = ReadFromSeed::read_from_seed(reader, Length(num_inputs))?; + + let num_outputs = usize::read_from(reader)?; + let outputs = ReadFromSeed::read_from_seed(reader, Length(num_outputs))?; + + let custom_data_len = i32::read_from(reader)?; + let custom_data = if custom_data_len < 0 { + None + } else { + let mut data = vec![0u8; custom_data_len as usize]; + reader.read_exact(&mut data)?; + Some(data) + }; + + Ok(Self { + address, + parent, + type_id, + position, + rotation, + inputs, + outputs, + custom_data, + }) + } +} + +impl WriteTo for Component { + fn write_to(&self, writer: &mut W) -> Result<(), Error> { + self.address.write_to(writer)?; + self.parent.write_to(writer)?; + self.type_id.write_to(writer)?; + self.position.write_to(writer)?; + self.rotation.write_to(writer)?; + + self.inputs.len().write_to(writer)?; + self.inputs.write_to(writer)?; + + self.outputs.len().write_to(writer)?; + self.outputs.write_to(writer)?; + + match &self.custom_data { + None => { + (-1_i32).write_to(writer)?; + } + Some(data) => { + data.len().write_to(writer)?; + writer.write_all(&data)?; + } + } + Ok(()) + } +} + +#[derive(Debug)] +pub enum PegType { + Output, + Input, +} + +impl ReadFrom for PegType { + fn read_from(reader: &mut R) -> Result { + let id = u8::read_from(reader)?; + match id { + 0 => Ok(Self::Output), + 1 => Ok(Self::Input), + _ => Err(Error::InvalidSave), + } + } +} + +impl WriteTo for PegType { + fn write_to(&self, writer: &mut W) -> Result<(), Error> { + let id: u8 = match self { + Self::Output => 0, + Self::Input => 1, + }; + id.write_to(writer)?; + Ok(()) + } +} + #[derive(Debug)] pub struct PegAddress { - pub is_input: bool, + pub peg_type: PegType, pub component_address: u32, pub peg_index: i32, } +impl ReadFrom for PegAddress { + fn read_from(reader: &mut R) -> Result { + let peg_type = ReadFrom::read_from(reader)?; + let component_address = ReadFrom::read_from(reader)?; + let peg_index = ReadFrom::read_from(reader)?; + + Ok(Self { + peg_type, + component_address, + peg_index, + }) + } +} + +impl WriteTo for PegAddress { + fn write_to(&self, writer: &mut W) -> Result<(), Error> { + self.peg_type.write_to(writer)?; + self.component_address.write_to(writer)?; + self.peg_index.write_to(writer)?; + Ok(()) + } +} + #[derive(Debug)] pub struct Wire { pub start_peg: PegAddress, @@ -59,12 +263,71 @@ pub struct Wire { pub rotation: f32, } +impl ReadFrom for Wire { + fn read_from(reader: &mut R) -> Result { + let start_peg = ReadFrom::read_from(reader)?; + let end_peg = ReadFrom::read_from(reader)?; + let circuit_state_id = ReadFrom::read_from(reader)?; + let rotation = ReadFrom::read_from(reader)?; + Ok(Self { + start_peg, + end_peg, + circuit_state_id, + rotation, + }) + } +} + +impl WriteTo for Wire { + fn write_to(&self, writer: &mut W) -> Result<(), Error> { + self.start_peg.write_to(writer)?; + self.end_peg.write_to(writer)?; + self.circuit_state_id.write_to(writer)?; + self.rotation.write_to(writer)?; + Ok(()) + } +} + #[derive(Debug)] pub enum CircuitStates { WorldFormat { circuit_states: Vec }, SubassemblyFormat { on_states: Vec }, } +impl ReadFromSeed for CircuitStates { + fn read_from_seed(reader: &mut R, seed: SaveType) -> Result { + match seed { + SaveType::World => { + let num_bytes = usize::read_from(reader)?; + let mut circuit_states = vec![0u8; num_bytes]; + reader.read_exact(&mut circuit_states)?; + Ok(Self::WorldFormat { circuit_states }) + } + SaveType::Subassembly => { + let num_on_states = usize::read_from(reader)?; + let on_states = Vec::read_from_seed(reader, Length(num_on_states))?; + Ok(Self::SubassemblyFormat { on_states }) + } + } + } +} + +impl WriteTo for CircuitStates { + fn write_to(&self, writer: &mut W) -> Result<(), Error> { + match self { + Self::WorldFormat { circuit_states } => { + circuit_states.len().write_to(writer)?; + writer.write_all(&circuit_states)?; + } + Self::SubassemblyFormat { on_states } => { + on_states.len().write_to(writer)?; + on_states.write_to(writer)?; + } + } + Ok(()) + } +} + #[derive(Debug)] pub struct BlotterFile { pub game_version: [i32; 4], @@ -92,9 +355,9 @@ impl BlotterFile { } pub fn read(reader: &mut R) -> Result { - read_magic(reader, b"Logic World save")?; + read_magic(reader, SAVE_HEADER)?; - let save_version = read_u8(reader)?; + let save_version = u8::read_from(reader)?; if save_version != SAVE_VERSION { return Err(Error::IncompatibleVersion(save_version)); } @@ -102,106 +365,24 @@ impl BlotterFile { } pub(crate) fn read_after_save_version(reader: &mut R) -> Result { - let game_version = read_version(reader)?; + let game_version = ReadFrom::read_from(reader)?; - let save_type_id = read_u8(reader)?; - let save_type = match save_type_id { - 1 => SaveType::World, - 2 => SaveType::Subassembly, - _ => { - return Err(Error::InvalidSave); - } - }; + let save_type = SaveType::read_from(reader)?; + let num_components = usize::read_from(reader)?; + let num_wires = usize::read_from(reader)?; - let num_components = read_usize(reader)?; - let num_wires = read_usize(reader)?; + let num_mods = usize::read_from(reader)?; + let mods = Vec::read_from_seed(reader, Length(num_mods))?; - let num_mods = read_usize(reader)?; - let mods = read_vec(num_mods, || { - let mod_id = read_string(reader)?; - let mod_version = read_version(reader)?; - Ok(ModInfo { - mod_id, - mod_version, - }) - })?; + let num_component_types: usize = usize::read_from(reader)?; + let component_types = Vec::read_from_seed(reader, Length(num_component_types))?; - let num_component_types: usize = read_usize(reader)?; - let component_types = read_vec(num_component_types, || { - let numeric_id = read_u16(reader)?; - let text_id = read_string(reader)?; - Ok(ComponentType { - numeric_id, - text_id, - }) - })?; + let components = Vec::read_from_seed(reader, Length(num_components))?; + let wires = Vec::read_from_seed(reader, Length(num_wires))?; - let components = read_vec(num_components, || { - let address = read_u32(reader)?; - let parent = read_u32(reader)?; - let type_id = read_u16(reader)?; - let position = read_array(|| read_f32(reader))?; - let rotation = read_array(|| read_f32(reader))?; + let circuit_states = CircuitStates::read_from_seed(reader, save_type)?; - let num_inputs = read_usize(reader)?; - let inputs = read_vec(num_inputs, || { - let circuit_state_id = read_i32(reader)?; - Ok(Input { circuit_state_id }) - })?; - - let num_outputs = read_usize(reader)?; - let outputs = read_vec(num_outputs, || { - let circuit_state_id = read_i32(reader)?; - Ok(Output { circuit_state_id }) - })?; - - let custom_data_len = read_i32(reader)?; - let custom_data = if custom_data_len < 0 { - None - } else { - Some(read_bytevec(reader, custom_data_len as usize)?) - }; - - Ok(Component { - address, - parent, - type_id, - position, - rotation, - inputs, - outputs, - custom_data, - }) - })?; - - let wires = read_vec(num_wires, || { - let start_peg = read_peg_address(reader)?; - let end_peg = read_peg_address(reader)?; - let circuit_state_id = read_i32(reader)?; - let rotation = read_f32(reader)?; - - Ok(Wire { - start_peg, - end_peg, - circuit_state_id, - rotation, - }) - })?; - - let circuit_states = match save_type { - SaveType::World => { - let num_bytes = read_usize(reader)?; - let circuit_states = read_bytevec(reader, num_bytes)?; - CircuitStates::WorldFormat { circuit_states } - } - SaveType::Subassembly => { - let num_on_states = read_usize(reader)?; - let on_states = read_vec(num_on_states, || read_i32(reader))?; - CircuitStates::SubassemblyFormat { on_states } - } - }; - - read_magic(reader, b"redstone sux lol")?; + read_magic(reader, SAVE_FOOTER)?; Ok(Self { game_version, @@ -215,112 +396,34 @@ impl BlotterFile { } pub fn write(&self, writer: &mut W) -> Result<(), Error> { - writer.write_all(b"Logic World save")?; + writer.write_all(SAVE_HEADER)?; - write_u8(SAVE_VERSION, writer)?; - for &v in &self.game_version { - write_i32(v, writer)?; - } - let save_type_id = match self.save_type { - SaveType::World => 1, - SaveType::Subassembly => 2, - }; - write_u8(save_type_id, writer)?; + SAVE_VERSION.write_to(writer)?; + self.game_version.write_to(writer)?; + self.save_type.write_to(writer)?; - write_usize(self.components.len(), writer)?; - write_usize(self.wires.len(), writer)?; + self.components.len().write_to(writer)?; + self.wires.len().write_to(writer)?; - write_usize(self.mods.len(), writer)?; - for mod_info in &self.mods { - write_str(&mod_info.mod_id, writer)?; - for &v in &mod_info.mod_version { - write_i32(v, writer)?; - } - } + self.mods.len().write_to(writer)?; + self.mods.write_to(writer)?; - write_usize(self.component_types.len(), writer)?; - for component_type in &self.component_types { - write_u16(component_type.numeric_id, writer)?; - write_str(&component_type.text_id, writer)?; - } + self.component_types.len().write_to(writer)?; + self.component_types.write_to(writer)?; - for component in &self.components { - write_u32(component.address, writer)?; - write_u32(component.parent, writer)?; - write_u16(component.type_id, writer)?; - for &v in &component.position { - write_f32(v, writer)?; - } - for &v in &component.rotation { - write_f32(v, writer)?; - } - write_usize(component.inputs.len(), writer)?; - for input in &component.inputs { - write_i32(input.circuit_state_id, writer)?; - } - write_usize(component.outputs.len(), writer)?; - for output in &component.outputs { - write_i32(output.circuit_state_id, writer)?; - } - if let Some(bytes) = &component.custom_data { - write_usize(bytes.len(), writer)?; - writer.write_all(&bytes)?; - } else { - write_i32(-1, writer)?; - } - } - - for wire in &self.wires { - write_peg_address(&wire.start_peg, writer)?; - write_peg_address(&wire.end_peg, writer)?; - write_i32(wire.circuit_state_id, writer)?; - write_f32(wire.rotation, writer)?; - } + self.components.write_to(writer)?; + self.wires.write_to(writer)?; match (self.save_type, &self.circuit_states) { - (SaveType::World, CircuitStates::WorldFormat { circuit_states }) => { - write_usize(circuit_states.len(), writer)?; - writer.write_all(&circuit_states)?; - } - (SaveType::Subassembly, CircuitStates::SubassemblyFormat { on_states }) => { - write_usize(on_states.len(), writer)?; - for &v in on_states { - write_i32(v, writer)?; - } - } + (SaveType::World, CircuitStates::WorldFormat { .. }) => {} + (SaveType::Subassembly, CircuitStates::SubassemblyFormat { .. }) => {} _ => { return Err(Error::InvalidSave); } } + self.circuit_states.write_to(writer)?; - writer.write_all(b"redstone sux lol")?; + writer.write_all(SAVE_FOOTER)?; Ok(()) } } - -fn read_peg_address(reader: &mut R) -> Result { - let peg_kind = read_u8(reader)?; - let is_input = match peg_kind { - 0 => false, - 1 => true, - _ => return Err(Error::InvalidSave), - }; - let component_address = read_u32(reader)?; - let peg_index = read_i32(reader)?; - Ok(PegAddress { - is_input, - component_address, - peg_index, - }) -} - -fn write_peg_address(address: &PegAddress, writer: &mut W) -> Result<(), Error> { - let peg_type_id = match address.is_input { - false => 0, - true => 1, - }; - write_u8(peg_type_id, writer)?; - write_u32(address.component_address, writer)?; - write_i32(address.peg_index, writer)?; - Ok(()) -} diff --git a/src/v6.rs b/src/v6.rs index b332793..5078e74 100644 --- a/src/v6.rs +++ b/src/v6.rs @@ -6,7 +6,8 @@ pub const SAVE_VERSION: u8 = 6; // Unchanged from previous version: pub use crate::v5::{ - CircuitStates, ComponentType, Input, ModInfo, Output, PegAddress, SaveType, Wire, + CircuitStates, ComponentType, Input, ModInfo, Output, PegAddress, SaveType, Wire, SAVE_FOOTER, + SAVE_HEADER, }; #[derive(Debug)] @@ -21,6 +22,69 @@ pub struct Component { pub custom_data: Option>, } +impl ReadFrom for Component { + fn read_from(reader: &mut R) -> Result { + let address = ReadFrom::read_from(reader)?; + let parent = ReadFrom::read_from(reader)?; + let type_id = ReadFrom::read_from(reader)?; + let position = ReadFrom::read_from(reader)?; + let rotation = ReadFrom::read_from(reader)?; + + let num_inputs = usize::read_from(reader)?; + let inputs = ReadFromSeed::read_from_seed(reader, Length(num_inputs))?; + + let num_outputs = usize::read_from(reader)?; + let outputs = ReadFromSeed::read_from_seed(reader, Length(num_outputs))?; + + let custom_data_len = i32::read_from(reader)?; + let custom_data = if custom_data_len < 0 { + None + } else { + let mut data = vec![0u8; custom_data_len as usize]; + reader.read_exact(&mut data)?; + Some(data) + }; + + Ok(Self { + address, + parent, + type_id, + position, + rotation, + inputs, + outputs, + custom_data, + }) + } +} + +impl WriteTo for Component { + fn write_to(&self, writer: &mut W) -> Result<(), Error> { + self.address.write_to(writer)?; + self.parent.write_to(writer)?; + self.type_id.write_to(writer)?; + self.position.write_to(writer)?; + self.rotation.write_to(writer)?; + + self.inputs.len().write_to(writer)?; + self.inputs.write_to(writer)?; + + self.outputs.len().write_to(writer)?; + self.outputs.write_to(writer)?; + + match &self.custom_data { + None => { + (-1_i32).write_to(writer)?; + } + Some(data) => { + data.len().write_to(writer)?; + writer.write_all(&data)?; + } + } + Ok(()) + } +} + #[derive(Debug)] pub struct BlotterFile { pub game_version: [i32; 4], @@ -48,9 +112,9 @@ impl BlotterFile { } pub fn read(reader: &mut R) -> Result { - read_magic(reader, b"Logic World save")?; + read_magic(reader, SAVE_HEADER)?; - let save_version = read_u8(reader)?; + let save_version = u8::read_from(reader)?; if save_version != SAVE_VERSION { return Err(Error::IncompatibleVersion(save_version)); } @@ -58,106 +122,24 @@ impl BlotterFile { } pub(crate) fn read_after_save_version(reader: &mut R) -> Result { - let game_version = read_version(reader)?; + let game_version = ReadFrom::read_from(reader)?; - let save_type_id = read_u8(reader)?; - let save_type = match save_type_id { - 1 => SaveType::World, - 2 => SaveType::Subassembly, - _ => { - return Err(Error::InvalidSave); - } - }; + let save_type = SaveType::read_from(reader)?; + let num_components = usize::read_from(reader)?; + let num_wires = usize::read_from(reader)?; - let num_components = read_usize(reader)?; - let num_wires = read_usize(reader)?; + let num_mods = usize::read_from(reader)?; + let mods = Vec::read_from_seed(reader, Length(num_mods))?; - let num_mods = read_usize(reader)?; - let mods = read_vec(num_mods, || { - let mod_id = read_string(reader)?; - let mod_version = read_version(reader)?; - Ok(ModInfo { - mod_id, - mod_version, - }) - })?; + let num_component_types: usize = usize::read_from(reader)?; + let component_types = Vec::read_from_seed(reader, Length(num_component_types))?; - let num_component_types: usize = read_usize(reader)?; - let component_types = read_vec(num_component_types, || { - let numeric_id = read_u16(reader)?; - let text_id = read_string(reader)?; - Ok(ComponentType { - numeric_id, - text_id, - }) - })?; + let components = Vec::read_from_seed(reader, Length(num_components))?; + let wires = Vec::read_from_seed(reader, Length(num_wires))?; - let components = read_vec(num_components, || { - let address = read_u32(reader)?; - let parent = read_u32(reader)?; - let type_id = read_u16(reader)?; - let position = read_array(|| read_i32(reader))?; - let rotation = read_array(|| read_f32(reader))?; + let circuit_states = CircuitStates::read_from_seed(reader, save_type)?; - let num_inputs = read_usize(reader)?; - let inputs = read_vec(num_inputs, || { - let circuit_state_id = read_i32(reader)?; - Ok(Input { circuit_state_id }) - })?; - - let num_outputs = read_usize(reader)?; - let outputs = read_vec(num_outputs, || { - let circuit_state_id = read_i32(reader)?; - Ok(Output { circuit_state_id }) - })?; - - let custom_data_len = read_i32(reader)?; - let custom_data = if custom_data_len < 0 { - None - } else { - Some(read_bytevec(reader, custom_data_len as usize)?) - }; - - Ok(Component { - address, - parent, - type_id, - position, - rotation, - inputs, - outputs, - custom_data, - }) - })?; - - let wires = read_vec(num_wires, || { - let start_peg = read_peg_address(reader)?; - let end_peg = read_peg_address(reader)?; - let circuit_state_id = read_i32(reader)?; - let rotation = read_f32(reader)?; - - Ok(Wire { - start_peg, - end_peg, - circuit_state_id, - rotation, - }) - })?; - - let circuit_states = match save_type { - SaveType::World => { - let num_bytes = read_usize(reader)?; - let circuit_states = read_bytevec(reader, num_bytes)?; - CircuitStates::WorldFormat { circuit_states } - } - SaveType::Subassembly => { - let num_on_states = read_usize(reader)?; - let on_states = read_vec(num_on_states, || read_i32(reader))?; - CircuitStates::SubassemblyFormat { on_states } - } - }; - - read_magic(reader, b"redstone sux lol")?; + read_magic(reader, SAVE_FOOTER)?; Ok(Self { game_version, @@ -171,112 +153,34 @@ impl BlotterFile { } pub fn write(&self, writer: &mut W) -> Result<(), Error> { - writer.write_all(b"Logic World save")?; + writer.write_all(SAVE_HEADER)?; - write_u8(SAVE_VERSION, writer)?; - for &v in &self.game_version { - write_i32(v, writer)?; - } - let save_type_id = match self.save_type { - SaveType::World => 1, - SaveType::Subassembly => 2, - }; - write_u8(save_type_id, writer)?; + SAVE_VERSION.write_to(writer)?; + self.game_version.write_to(writer)?; + self.save_type.write_to(writer)?; - write_usize(self.components.len(), writer)?; - write_usize(self.wires.len(), writer)?; + self.components.len().write_to(writer)?; + self.wires.len().write_to(writer)?; - write_usize(self.mods.len(), writer)?; - for mod_info in &self.mods { - write_str(&mod_info.mod_id, writer)?; - for &v in &mod_info.mod_version { - write_i32(v, writer)?; - } - } + self.mods.len().write_to(writer)?; + self.mods.write_to(writer)?; - write_usize(self.component_types.len(), writer)?; - for component_type in &self.component_types { - write_u16(component_type.numeric_id, writer)?; - write_str(&component_type.text_id, writer)?; - } + self.component_types.len().write_to(writer)?; + self.component_types.write_to(writer)?; - for component in &self.components { - write_u32(component.address, writer)?; - write_u32(component.parent, writer)?; - write_u16(component.type_id, writer)?; - for &v in &component.position { - write_i32(v, writer)?; - } - for &v in &component.rotation { - write_f32(v, writer)?; - } - write_usize(component.inputs.len(), writer)?; - for input in &component.inputs { - write_i32(input.circuit_state_id, writer)?; - } - write_usize(component.outputs.len(), writer)?; - for output in &component.outputs { - write_i32(output.circuit_state_id, writer)?; - } - if let Some(bytes) = &component.custom_data { - write_usize(bytes.len(), writer)?; - writer.write_all(&bytes)?; - } else { - write_i32(-1, writer)?; - } - } - - for wire in &self.wires { - write_peg_address(&wire.start_peg, writer)?; - write_peg_address(&wire.end_peg, writer)?; - write_i32(wire.circuit_state_id, writer)?; - write_f32(wire.rotation, writer)?; - } + self.components.write_to(writer)?; + self.wires.write_to(writer)?; match (self.save_type, &self.circuit_states) { - (SaveType::World, CircuitStates::WorldFormat { circuit_states }) => { - write_usize(circuit_states.len(), writer)?; - writer.write_all(&circuit_states)?; - } - (SaveType::Subassembly, CircuitStates::SubassemblyFormat { on_states }) => { - write_usize(on_states.len(), writer)?; - for &v in on_states { - write_i32(v, writer)?; - } - } + (SaveType::World, CircuitStates::WorldFormat { .. }) => {} + (SaveType::Subassembly, CircuitStates::SubassemblyFormat { .. }) => {} _ => { return Err(Error::InvalidSave); } } + self.circuit_states.write_to(writer)?; - writer.write_all(b"redstone sux lol")?; + writer.write_all(SAVE_FOOTER)?; Ok(()) } } - -fn read_peg_address(reader: &mut R) -> Result { - let peg_kind = read_u8(reader)?; - let is_input = match peg_kind { - 0 => false, - 1 => true, - _ => return Err(Error::InvalidSave), - }; - let component_address = read_u32(reader)?; - let peg_index = read_i32(reader)?; - Ok(PegAddress { - is_input, - component_address, - peg_index, - }) -} - -fn write_peg_address(address: &PegAddress, writer: &mut W) -> Result<(), Error> { - let peg_type_id = match address.is_input { - false => 0, - true => 1, - }; - write_u8(peg_type_id, writer)?; - write_u32(address.component_address, writer)?; - write_i32(address.peg_index, writer)?; - Ok(()) -}