Add zero-dependency writer

This commit is contained in:
Adam Gausmann 2021-10-23 22:16:40 -05:00
parent 0940cca801
commit 0e8edad446
2 changed files with 193 additions and 12 deletions

22
examples/roundtrip.rs Normal file
View file

@ -0,0 +1,22 @@
use std::{env::args_os, fs::File, io::BufReader, process::exit};
use blotter::BlotterFile;
fn main() -> Result<(), blotter::Error> {
let infile = args_os().nth(1).unwrap_or_else(usage);
let mut reader = BufReader::new(File::open(infile)?);
let blotter_file = BlotterFile::read(&mut reader)?;
let mut buffer = Vec::new();
blotter_file.write(&mut buffer)?;
let mut reader = buffer.as_slice();
let second_blotter_file = BlotterFile::read(&mut reader)?;
eprintln!("{:#?}", second_blotter_file);
Ok(())
}
fn usage<T>() -> T {
eprintln!("missing argument: input file name");
exit(1);
}

View file

@ -1,3 +1,5 @@
use std::io::{Read, Write};
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
#[non_exhaustive] #[non_exhaustive]
pub enum SaveType { pub enum SaveType {
@ -62,6 +64,7 @@ enum CircuitStates {
#[derive(Debug)] #[derive(Debug)]
pub struct BlotterFile { pub struct BlotterFile {
save_version: u8,
game_version: [i32; 4], game_version: [i32; 4],
save_type: SaveType, save_type: SaveType,
mods: Vec<ModInfo>, mods: Vec<ModInfo>,
@ -74,6 +77,7 @@ pub struct BlotterFile {
impl BlotterFile { impl BlotterFile {
pub fn new(game_version: [i32; 4]) -> Self { pub fn new(game_version: [i32; 4]) -> Self {
Self { Self {
save_version: 5,
game_version, game_version,
save_type: SaveType::World, save_type: SaveType::World,
mods: Vec::new(), mods: Vec::new(),
@ -88,7 +92,7 @@ impl BlotterFile {
pub fn read<R>(reader: &mut R) -> Result<Self, Error> pub fn read<R>(reader: &mut R) -> Result<Self, Error>
where where
R: std::io::Read, R: Read,
{ {
read_magic(reader, b"Logic World save")?; read_magic(reader, b"Logic World save")?;
@ -199,6 +203,7 @@ impl BlotterFile {
read_magic(reader, b"redstone sux lol")?; read_magic(reader, b"redstone sux lol")?;
Ok(Self { Ok(Self {
save_version,
game_version, game_version,
save_type, save_type,
mods, mods,
@ -208,6 +213,89 @@ impl BlotterFile {
circuit_states, circuit_states,
}) })
} }
pub fn write<W>(&self, writer: &mut W) -> Result<(), Error>
where
W: Write,
{
writer.write_all(b"Logic World save")?;
write_u8(self.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)?;
write_usize(self.components.len(), writer)?;
write_usize(self.wires.len(), 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)?;
}
}
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)?;
}
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)?;
}
match &self.circuit_states {
CircuitStates::WorldFormat { circuit_states } => {
write_usize(circuit_states.len(), writer)?;
writer.write_all(&circuit_states)?;
}
CircuitStates::SubassemblyFormat { on_states } => {
write_usize(on_states.len(), writer)?;
for &v in on_states {
write_i32(v, writer)?;
}
}
}
writer.write_all(b"redstone sux lol")?;
Ok(())
}
} }
#[derive(Debug)] #[derive(Debug)]
@ -227,7 +315,7 @@ impl From<std::io::Error> for Error {
fn read_u8<R>(reader: &mut R) -> Result<u8, Error> fn read_u8<R>(reader: &mut R) -> Result<u8, Error>
where where
R: std::io::Read, R: Read,
{ {
let mut bytes = [0u8; 1]; let mut bytes = [0u8; 1];
reader.read_exact(&mut bytes)?; reader.read_exact(&mut bytes)?;
@ -236,7 +324,7 @@ where
fn read_u16<R>(reader: &mut R) -> Result<u16, Error> fn read_u16<R>(reader: &mut R) -> Result<u16, Error>
where where
R: std::io::Read, R: Read,
{ {
let mut bytes = [0u8; 2]; let mut bytes = [0u8; 2];
reader.read_exact(&mut bytes)?; reader.read_exact(&mut bytes)?;
@ -245,7 +333,7 @@ where
fn read_i32<R>(reader: &mut R) -> Result<i32, Error> fn read_i32<R>(reader: &mut R) -> Result<i32, Error>
where where
R: std::io::Read, R: Read,
{ {
let mut bytes = [0u8; 4]; let mut bytes = [0u8; 4];
reader.read_exact(&mut bytes)?; reader.read_exact(&mut bytes)?;
@ -254,7 +342,7 @@ where
fn read_u32<R>(reader: &mut R) -> Result<u32, Error> fn read_u32<R>(reader: &mut R) -> Result<u32, Error>
where where
R: std::io::Read, R: Read,
{ {
let mut bytes = [0u8; 4]; let mut bytes = [0u8; 4];
reader.read_exact(&mut bytes)?; reader.read_exact(&mut bytes)?;
@ -263,7 +351,7 @@ where
fn read_f32<R>(reader: &mut R) -> Result<f32, Error> fn read_f32<R>(reader: &mut R) -> Result<f32, Error>
where where
R: std::io::Read, R: Read,
{ {
let mut bytes = [0u8; 4]; let mut bytes = [0u8; 4];
reader.read_exact(&mut bytes)?; reader.read_exact(&mut bytes)?;
@ -272,14 +360,14 @@ where
fn read_usize<R>(reader: &mut R) -> Result<usize, Error> fn read_usize<R>(reader: &mut R) -> Result<usize, Error>
where where
R: std::io::Read, R: Read,
{ {
read_i32(reader)?.try_into().map_err(|_| Error::InvalidSave) read_i32(reader)?.try_into().map_err(|_| Error::InvalidSave)
} }
fn read_string<R>(reader: &mut R) -> Result<String, Error> fn read_string<R>(reader: &mut R) -> Result<String, Error>
where where
R: std::io::Read, R: Read,
{ {
let string_len = read_usize(reader)?; let string_len = read_usize(reader)?;
let bytes = read_bytevec(reader, string_len)?; let bytes = read_bytevec(reader, string_len)?;
@ -311,7 +399,7 @@ where
fn read_magic<R>(reader: &mut R, magic_bytes: &[u8]) -> Result<(), Error> fn read_magic<R>(reader: &mut R, magic_bytes: &[u8]) -> Result<(), Error>
where where
R: std::io::Read, R: Read,
{ {
let mut header = [0u8; 16]; let mut header = [0u8; 16];
reader.read_exact(&mut header)?; reader.read_exact(&mut header)?;
@ -323,14 +411,14 @@ where
fn read_version<R>(reader: &mut R) -> Result<[i32; 4], Error> fn read_version<R>(reader: &mut R) -> Result<[i32; 4], Error>
where where
R: std::io::Read, R: Read,
{ {
read_array(|| read_i32(reader)) read_array(|| read_i32(reader))
} }
fn read_bytevec<R>(reader: &mut R, len: usize) -> Result<Vec<u8>, Error> fn read_bytevec<R>(reader: &mut R, len: usize) -> Result<Vec<u8>, Error>
where where
R: std::io::Read, R: Read,
{ {
let mut vec = vec![0u8; len]; let mut vec = vec![0u8; len];
reader.read_exact(&mut vec)?; reader.read_exact(&mut vec)?;
@ -339,7 +427,7 @@ where
fn read_peg_address<R>(reader: &mut R) -> Result<PegAddress, Error> fn read_peg_address<R>(reader: &mut R) -> Result<PegAddress, Error>
where where
R: std::io::Read, R: Read,
{ {
let peg_kind = read_u8(reader)?; let peg_kind = read_u8(reader)?;
let is_input = match peg_kind { let is_input = match peg_kind {
@ -355,3 +443,74 @@ where
peg_index, peg_index,
}) })
} }
fn write_u8<W>(v: u8, writer: &mut W) -> Result<(), Error>
where
W: Write,
{
writer.write_all(&v.to_le_bytes())?;
Ok(())
}
fn write_u16<W>(v: u16, writer: &mut W) -> Result<(), Error>
where
W: Write,
{
writer.write_all(&v.to_le_bytes())?;
Ok(())
}
fn write_i32<W>(v: i32, writer: &mut W) -> Result<(), Error>
where
W: Write,
{
writer.write_all(&v.to_le_bytes())?;
Ok(())
}
fn write_u32<W>(v: u32, writer: &mut W) -> Result<(), Error>
where
W: Write,
{
writer.write_all(&v.to_le_bytes())?;
Ok(())
}
fn write_f32<W>(v: f32, writer: &mut W) -> Result<(), Error>
where
W: Write,
{
writer.write_all(&v.to_le_bytes())?;
Ok(())
}
fn write_usize<W>(v: usize, writer: &mut W) -> Result<(), Error>
where
W: Write,
{
let v: i32 = v.try_into().map_err(|_| Error::InvalidSave)?;
write_i32(v, writer)
}
fn write_str<W>(s: &str, writer: &mut W) -> Result<(), Error>
where
W: Write,
{
write_usize(s.len(), writer)?;
writer.write_all(s.as_bytes())?;
Ok(())
}
fn write_peg_address<W>(address: &PegAddress, writer: &mut W) -> Result<(), Error>
where
W: Write,
{
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(())
}