Refactor internal I/O helpers into traits

This commit is contained in:
Adam Gausmann 2022-10-30 00:29:14 -05:00
parent e509969033
commit 9ca398af11
5 changed files with 496 additions and 494 deletions

View file

@ -27,9 +27,9 @@ impl ComponentData for CircuitBoard {
const TYPE_STRING: &'static str = "MHG.CircuitBoard";
fn read<R: Read>(reader: &mut R) -> Result<Self, Error> {
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<W: 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(())
}
}

178
src/io.rs
View file

@ -1,104 +1,112 @@
use crate::error::Error;
use std::io::{Read, Write};
use std::{
io::{Read, Write},
iter::repeat_with,
};
pub fn read_u8<R: Read>(reader: &mut R) -> Result<u8, Error> {
let mut bytes = [0u8; 1];
pub trait ReadFrom: Sized {
fn read_from<R: Read>(reader: &mut R) -> Result<Self, Error>;
}
pub trait ReadFromSeed<Seed>: Sized {
fn read_from_seed<R: Read>(reader: &mut R, seed: Seed) -> Result<Self, Error>;
}
impl<T: ReadFrom> ReadFromSeed<()> for T {
fn read_from_seed<R: Read>(reader: &mut R, seed: ()) -> Result<Self, Error> {
let _ = seed;
Self::read_from(reader)
}
}
pub trait WriteTo {
fn write_to<W: Write>(&self, writer: &mut W) -> Result<(), Error>;
}
macro_rules! primitive_io {
($($t:ty: $len:literal,)*) => {$(
impl ReadFrom for $t {
fn read_from<R: Read>(reader: &mut R) -> Result<Self, Error> {
let mut bytes = [0u8; $len];
reader.read_exact(&mut bytes)?;
Ok(u8::from_le_bytes(bytes))
Ok(<$t>::from_le_bytes(bytes))
}
}
pub fn write_u8<W: Write>(v: u8, writer: &mut W) -> Result<(), Error> {
writer.write_all(&v.to_le_bytes())?;
impl WriteTo for $t {
fn write_to<W: Write>(&self, writer: &mut W) -> Result<(), Error> {
writer.write_all(&self.to_le_bytes())?;
Ok(())
}
pub fn read_u16<R: Read>(reader: &mut R) -> Result<u16, Error> {
let mut bytes = [0u8; 2];
reader.read_exact(&mut bytes)?;
Ok(u16::from_le_bytes(bytes))
}
)*};
}
pub fn write_u16<W: Write>(v: u16, writer: &mut W) -> Result<(), Error> {
writer.write_all(&v.to_le_bytes())?;
Ok(())
primitive_io! {
u8: 1,
u16: 2,
i32: 4,
u32: 4,
f32: 4,
}
pub fn read_i32<R: Read>(reader: &mut R) -> Result<i32, Error> {
let mut bytes = [0u8; 4];
reader.read_exact(&mut bytes)?;
Ok(i32::from_le_bytes(bytes))
impl ReadFrom for usize {
fn read_from<R: Read>(reader: &mut R) -> Result<Self, Error> {
i32::read_from(reader).and_then(|x| Self::try_from(x).map_err(|_| Error::InvalidSave))
}
}
pub fn write_i32<W: Write>(v: i32, writer: &mut W) -> Result<(), Error> {
writer.write_all(&v.to_le_bytes())?;
Ok(())
impl WriteTo for usize {
fn write_to<W: Write>(&self, writer: &mut W) -> Result<(), Error> {
i32::try_from(*self)
.map_err(|_| Error::InvalidSave)
.and_then(|x| x.write_to(writer))
}
}
pub fn read_u32<R: Read>(reader: &mut R) -> Result<u32, Error> {
let mut bytes = [0u8; 4];
reader.read_exact(&mut bytes)?;
Ok(u32::from_le_bytes(bytes))
pub struct Length(pub usize);
impl<T: ReadFrom> ReadFromSeed<Length> for Vec<T> {
fn read_from_seed<R: Read>(reader: &mut R, seed: Length) -> Result<Self, Error> {
repeat_with(|| T::read_from(reader)).take(seed.0).collect()
}
}
pub fn write_u32<W: Write>(v: u32, writer: &mut W) -> Result<(), Error> {
writer.write_all(&v.to_le_bytes())?;
Ok(())
}
pub fn read_f32<R: Read>(reader: &mut R) -> Result<f32, Error> {
let mut bytes = [0u8; 4];
reader.read_exact(&mut bytes)?;
Ok(f32::from_le_bytes(bytes))
}
pub fn write_f32<W: Write>(v: f32, writer: &mut W) -> Result<(), Error> {
writer.write_all(&v.to_le_bytes())?;
Ok(())
}
pub fn read_usize<R: Read>(reader: &mut R) -> Result<usize, Error> {
read_i32(reader)?.try_into().map_err(|_| Error::InvalidSave)
}
pub fn write_usize<W: Write>(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<R: Read>(reader: &mut R) -> Result<String, Error> {
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<W: Write>(s: &str, writer: &mut W) -> Result<(), Error> {
write_usize(s.len(), writer)?;
writer.write_all(s.as_bytes())?;
Ok(())
}
pub fn read_vec<F, T>(len: usize, mut reader: F) -> Result<Vec<T>, Error>
where
F: FnMut() -> Result<T, Error>,
{
let mut vec = Vec::with_capacity(len);
for _ in 0..len {
vec.push(reader()?);
}
Ok(vec)
}
pub fn read_array<F, T, const LEN: usize>(mut reader: F) -> Result<[T; LEN], Error>
where
F: FnMut() -> Result<T, Error>,
T: Default + Copy,
{
let mut arr = [Default::default(); LEN];
impl<T: ReadFrom + Default + Copy, const N: usize> ReadFrom for [T; N] {
fn read_from<R: Read>(reader: &mut R) -> Result<Self, Error> {
let mut arr = [Default::default(); N];
for slot in &mut arr {
*slot = reader()?;
*slot = T::read_from(reader)?;
}
Ok(arr)
}
}
impl<T: WriteTo> WriteTo for [T] {
fn write_to<W: Write>(&self, writer: &mut W) -> Result<(), Error> {
for x in self {
x.write_to(writer)?;
}
Ok(())
}
}
impl ReadFrom for String {
fn read_from<R: Read>(reader: &mut R) -> Result<Self, Error> {
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<W: Write>(&self, writer: &mut W) -> Result<(), Error> {
self.len().write_to(writer)?;
writer.write_all(self.as_bytes())?;
Ok(())
}
}
pub fn read_magic<R: Read>(reader: &mut R, magic_bytes: &[u8]) -> Result<(), Error> {
let mut header = [0u8; 16];
@ -108,13 +116,3 @@ pub fn read_magic<R: Read>(reader: &mut R, magic_bytes: &[u8]) -> Result<(), Err
}
Ok(())
}
pub fn read_version<R: Read>(reader: &mut R) -> Result<[i32; 4], Error> {
read_array(|| read_i32(reader))
}
pub fn read_bytevec<R: Read>(reader: &mut R, len: usize) -> Result<Vec<u8>, Error> {
let mut vec = vec![0u8; len];
reader.read_exact(&mut vec)?;
Ok(vec)
}

View file

@ -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<R: Read>(reader: &mut R) -> Result<Self, Error> {
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),

485
src/v5.rs
View file

@ -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<R: Read>(reader: &mut R) -> Result<Self, Error> {
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<W: Write>(&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<R: Read>(reader: &mut R) -> Result<Self, Error> {
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<W: Write>(&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<R: Read>(reader: &mut R) -> Result<Self, Error> {
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<W: Write>(&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<R: Read>(reader: &mut R) -> Result<Self, Error> {
let circuit_state_id = ReadFrom::read_from(reader)?;
Ok(Self { circuit_state_id })
}
}
impl WriteTo for Input {
fn write_to<W: Write>(&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<R: Read>(reader: &mut R) -> Result<Self, Error> {
let circuit_state_id = ReadFrom::read_from(reader)?;
Ok(Self { circuit_state_id })
}
}
impl WriteTo for Output {
fn write_to<W: Write>(&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<Vec<u8>>,
}
impl ReadFrom for Component {
fn read_from<R: Read>(reader: &mut R) -> Result<Self, Error> {
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<W: Write>(&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<R: Read>(reader: &mut R) -> Result<Self, Error> {
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<W: Write>(&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<R: Read>(reader: &mut R) -> Result<Self, Error> {
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<W: Write>(&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<R: Read>(reader: &mut R) -> Result<Self, Error> {
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<W: Write>(&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<u8> },
SubassemblyFormat { on_states: Vec<i32> },
}
impl ReadFromSeed<SaveType> for CircuitStates {
fn read_from_seed<R: Read>(reader: &mut R, seed: SaveType) -> Result<Self, Error> {
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<W: Write>(&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<R: Read>(reader: &mut R) -> Result<Self, Error> {
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<R: Read>(reader: &mut R) -> Result<Self, Error> {
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<W: 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<R: Read>(reader: &mut R) -> Result<PegAddress, Error> {
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<W: Write>(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(())
}

286
src/v6.rs
View file

@ -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<Vec<u8>>,
}
impl ReadFrom for Component {
fn read_from<R: Read>(reader: &mut R) -> Result<Self, Error> {
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<W: Write>(&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<R: Read>(reader: &mut R) -> Result<Self, Error> {
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<R: Read>(reader: &mut R) -> Result<Self, Error> {
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<W: 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<R: Read>(reader: &mut R) -> Result<PegAddress, Error> {
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<W: Write>(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(())
}