Rewriting with zero dependencies

This commit is contained in:
Adam Gausmann 2021-10-23 21:54:15 -05:00
parent 54e0e1ba11
commit 0940cca801
4 changed files with 347 additions and 105 deletions

1
.gitignore vendored
View file

@ -1,2 +1,3 @@
/target /target
Cargo.lock Cargo.lock
.vscode

View file

@ -1,9 +1,8 @@
[package] [package]
name = "blotter" name = "blotter"
version = "0.1.0" version = "0.1.0"
edition = "2018" edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
declio = "0.2.0"

16
examples/load.rs Normal file
View file

@ -0,0 +1,16 @@
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)?;
println!("{:#?}", blotter_file);
Ok(())
}
fn usage<T>() -> T {
eprintln!("missing argument: input file name");
exit(1);
}

View file

@ -1,131 +1,357 @@
use declio::ctx::Len; #[derive(Debug, Clone, Copy, PartialEq)]
use declio::util::{magic_bytes, zero_one, LittleEndian}; #[non_exhaustive]
use declio::{Decode, Encode}; pub enum SaveType {
use std::convert::TryInto;
type ComponentId = LittleEndian<u16>;
type ComponentAddress = LittleEndian<u32>;
type Int = LittleEndian<i32>;
type Float = LittleEndian<f32>;
type Version = [Int; 4];
#[derive(Debug, Clone, Copy, PartialEq, Encode, Decode)]
#[declio(id_type = "u8")]
enum SaveType {
#[declio(id = "1")]
World, World,
#[declio(id = "2")]
Subassembly, Subassembly,
} }
#[derive(Debug, Encode, Decode)] #[derive(Debug)]
pub struct BlotterFile { struct ModInfo {
header: Header, mod_id: String,
save_version: u8, mod_version: [i32; 4],
game_version: Version,
save_type: SaveType,
components_len: Int,
wires_len: Int,
mods_len: Int,
#[declio(ctx = "Len(mods_len.0.try_into()?)")]
mods: Vec<Mod>,
component_ids_len: Int,
#[declio(ctx = "Len(component_ids_len.0.try_into()?)")]
component_ids: Vec<ComponentIdMapping>,
#[declio(ctx = "Len(components_len.0.try_into()?)")]
components: Vec<Component>,
#[declio(ctx = "Len(wires_len.0.try_into()?)")]
wires: Vec<Wire>,
#[declio(ctx = "*save_type")]
circuit_states: CircuitStates,
footer: Footer,
} }
magic_bytes! { #[derive(Debug)]
#[derive(Debug)] struct ComponentType {
Header(b"Logic World save"); numeric_id: u16,
text_id: String,
#[derive(Debug)]
Footer(b"redstone sux lol");
} }
#[derive(Debug, Encode, Decode)] #[derive(Debug)]
struct Text {
len: Int,
#[declio(with = "declio::util::utf8", ctx = "Len(len.0.try_into()?)")]
value: String,
}
#[derive(Debug, Encode, Decode)]
struct Mod {
mod_id: Text,
version: Version,
}
#[derive(Debug, Encode, Decode)]
struct ComponentIdMapping {
numeric_id: ComponentId,
text_id: Text,
}
#[derive(Debug, Encode, Decode)]
struct Input { struct Input {
circuit_state_id: Int, circuit_state_id: i32,
} }
#[derive(Debug, Encode, Decode)] #[derive(Debug)]
struct Output { struct Output {
circuit_state_id: Int, circuit_state_id: i32,
} }
#[derive(Debug, Encode, Decode)] #[derive(Debug)]
struct Component { struct Component {
address: ComponentAddress, address: u32,
parent: ComponentAddress, parent: u32,
type_id: ComponentId, type_id: u16,
position: [Float; 3], position: [f32; 3],
rotation: [Float; 4], rotation: [f32; 4],
inputs_len: Int,
#[declio(ctx = "Len(inputs_len.0.try_into()?)")]
inputs: Vec<Input>, inputs: Vec<Input>,
outputs_len: Int,
#[declio(ctx = "Len(outputs_len.0.try_into()?)")]
outputs: Vec<Output>, outputs: Vec<Output>,
custom_data_len: Int,
#[declio(skip_if = "custom_data_len.0 == -1")]
#[declio(ctx = "Len(custom_data_len.0.try_into()?)")]
custom_data: Option<Vec<u8>>, custom_data: Option<Vec<u8>>,
} }
#[derive(Debug, Encode, Decode)] #[derive(Debug)]
struct PegAddress { struct PegAddress {
#[declio(with = "zero_one")]
is_input: bool, is_input: bool,
component: ComponentAddress, component_address: u32,
index: Int, peg_index: i32,
} }
#[derive(Debug, Encode, Decode)] #[derive(Debug)]
struct Wire { struct Wire {
peg_1: PegAddress, start_peg: PegAddress,
peg_2: PegAddress, end_peg: PegAddress,
circuit_state_id: Int, circuit_state_id: i32,
rotation: Float, rotation: f32,
} }
#[derive(Debug, Encode, Decode)] #[derive(Debug)]
#[declio(ctx = "save_type: SaveType", id_expr = "save_type")]
enum CircuitStates { enum CircuitStates {
#[declio(id = "SaveType::World")] WorldFormat { circuit_states: Vec<u8> },
WorldFormat { SubassemblyFormat { on_states: Vec<i32> },
len: Int, }
#[declio(ctx = "Len(len.0.try_into()?)")]
circuit_states: Vec<u8>, #[derive(Debug)]
}, pub struct BlotterFile {
#[declio(id = "SaveType::Subassembly")] game_version: [i32; 4],
SubassemblyFormat { save_type: SaveType,
len: Int, mods: Vec<ModInfo>,
#[declio(ctx = "Len(len.0.try_into()?)")] component_types: Vec<ComponentType>,
on_states: Vec<Int>, components: Vec<Component>,
}, wires: Vec<Wire>,
circuit_states: CircuitStates,
}
impl BlotterFile {
pub fn new(game_version: [i32; 4]) -> Self {
Self {
game_version,
save_type: SaveType::World,
mods: Vec::new(),
component_types: Vec::new(),
components: Vec::new(),
wires: Vec::new(),
circuit_states: CircuitStates::WorldFormat {
circuit_states: Vec::new(),
},
}
}
pub fn read<R>(reader: &mut R) -> Result<Self, Error>
where
R: std::io::Read,
{
read_magic(reader, b"Logic World save")?;
let save_version = read_u8(reader)?;
if save_version != 5 {
return Err(Error::IncompatibleVersion(save_version));
}
let game_version = read_version(reader)?;
let save_type_id = read_u8(reader)?;
let save_type = match save_type_id {
1 => SaveType::World,
2 => SaveType::Subassembly,
_ => {
return Err(Error::UnknownSaveType(save_type_id));
}
};
let num_components = read_usize(reader)?;
let num_wires = read_usize(reader)?;
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 = 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 = 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 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")?;
Ok(Self {
game_version,
save_type,
mods,
component_types,
components,
wires,
circuit_states,
})
}
}
#[derive(Debug)]
#[non_exhaustive]
pub enum Error {
IoError(std::io::Error),
InvalidSave,
IncompatibleVersion(u8),
UnknownSaveType(u8),
}
impl From<std::io::Error> for Error {
fn from(error: std::io::Error) -> Self {
Self::IoError(error)
}
}
fn read_u8<R>(reader: &mut R) -> Result<u8, Error>
where
R: std::io::Read,
{
let mut bytes = [0u8; 1];
reader.read_exact(&mut bytes)?;
Ok(u8::from_le_bytes(bytes))
}
fn read_u16<R>(reader: &mut R) -> Result<u16, Error>
where
R: std::io::Read,
{
let mut bytes = [0u8; 2];
reader.read_exact(&mut bytes)?;
Ok(u16::from_le_bytes(bytes))
}
fn read_i32<R>(reader: &mut R) -> Result<i32, Error>
where
R: std::io::Read,
{
let mut bytes = [0u8; 4];
reader.read_exact(&mut bytes)?;
Ok(i32::from_le_bytes(bytes))
}
fn read_u32<R>(reader: &mut R) -> Result<u32, Error>
where
R: std::io::Read,
{
let mut bytes = [0u8; 4];
reader.read_exact(&mut bytes)?;
Ok(u32::from_le_bytes(bytes))
}
fn read_f32<R>(reader: &mut R) -> Result<f32, Error>
where
R: std::io::Read,
{
let mut bytes = [0u8; 4];
reader.read_exact(&mut bytes)?;
Ok(f32::from_le_bytes(bytes))
}
fn read_usize<R>(reader: &mut R) -> Result<usize, Error>
where
R: std::io::Read,
{
read_i32(reader)?.try_into().map_err(|_| Error::InvalidSave)
}
fn read_string<R>(reader: &mut R) -> Result<String, Error>
where
R: std::io::Read,
{
let string_len = read_usize(reader)?;
let bytes = read_bytevec(reader, string_len)?;
String::from_utf8(bytes).map_err(|_| Error::InvalidSave)
}
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)
}
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];
for slot in &mut arr {
*slot = reader()?;
}
Ok(arr)
}
fn read_magic<R>(reader: &mut R, magic_bytes: &[u8]) -> Result<(), Error>
where
R: std::io::Read,
{
let mut header = [0u8; 16];
reader.read_exact(&mut header)?;
if header != *magic_bytes {
return Err(Error::InvalidSave);
}
Ok(())
}
fn read_version<R>(reader: &mut R) -> Result<[i32; 4], Error>
where
R: std::io::Read,
{
read_array(|| read_i32(reader))
}
fn read_bytevec<R>(reader: &mut R, len: usize) -> Result<Vec<u8>, Error>
where
R: std::io::Read,
{
let mut vec = vec![0u8; len];
reader.read_exact(&mut vec)?;
Ok(vec)
}
fn read_peg_address<R>(reader: &mut R) -> Result<PegAddress, Error>
where
R: std::io::Read,
{
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,
})
} }