Add zero-dependency writer
This commit is contained in:
parent
0940cca801
commit
0e8edad446
2 changed files with 193 additions and 12 deletions
22
examples/roundtrip.rs
Normal file
22
examples/roundtrip.rs
Normal 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);
|
||||||
|
}
|
||||||
183
src/lib.rs
183
src/lib.rs
|
|
@ -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(())
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue