parent
9cadbd8990
commit
1579b5d82c
@ -1,2 +1,3 @@
|
||||
/target
|
||||
target/
|
||||
.nvim
|
||||
output/
|
||||
|
@ -0,0 +1,47 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "binary_serialize_derive"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.101"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "binary_serialize_derive"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
proc-macro2 = "1.0.101"
|
||||
quote = "1.0.40"
|
||||
syn = { version = "2.0.106", features = ["full"] }
|
@ -0,0 +1,36 @@
|
||||
extern crate proc_macro;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::{parse_macro_input, DeriveInput};
|
||||
|
||||
#[proc_macro_derive(BinarySerializable)]
|
||||
pub fn derive_binary_serializable(input: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
|
||||
let name = input.ident;
|
||||
|
||||
let fields = match input.data {
|
||||
syn::Data::Struct(s) => s.fields,
|
||||
_ => panic!("#[derive(BinarySerializable)] only works on structs"),
|
||||
};
|
||||
|
||||
let field_writes = fields.iter().map(|f| {
|
||||
let ident = f.ident.as_ref().unwrap();
|
||||
quote! {
|
||||
BinarySerializable::write_to(&self.#ident, writer)?;
|
||||
}
|
||||
});
|
||||
|
||||
let expanded = quote! {
|
||||
impl BinarySerializable for #name {
|
||||
fn write_to<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
|
||||
#(#field_writes)*
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TokenStream::from(expanded)
|
||||
}
|
||||
|
@ -0,0 +1,181 @@
|
||||
meta:
|
||||
id: logicworld_blotter_file_format_v7
|
||||
file-extension: partialworld
|
||||
title: Blotter File Format - v7
|
||||
endian: le
|
||||
|
||||
seq:
|
||||
- id: magic
|
||||
contents: 'Logic World save'
|
||||
- id: save_format_version
|
||||
type: u1
|
||||
- id: game_version
|
||||
type: version
|
||||
- id: save_type
|
||||
type: u1
|
||||
enum: save_type
|
||||
- id: number_of_components
|
||||
type: s4
|
||||
- id: number_of_wires
|
||||
type: s4
|
||||
- id: number_of_mods
|
||||
type: s4
|
||||
- id: mods
|
||||
type: mod_version
|
||||
repeat: expr
|
||||
repeat-expr: number_of_mods
|
||||
- id: number_of_component_ids
|
||||
type: s4
|
||||
- id: component_id_map
|
||||
type: component_id_map
|
||||
repeat: expr
|
||||
repeat-expr: number_of_component_ids
|
||||
- id: components
|
||||
type: component
|
||||
repeat: expr
|
||||
repeat-expr: number_of_components
|
||||
- id: wires
|
||||
type: wire
|
||||
repeat: expr
|
||||
repeat-expr: number_of_wires
|
||||
- id: circuit_states
|
||||
type:
|
||||
switch-on: save_type
|
||||
cases:
|
||||
'save_type::world': world_circuit_states
|
||||
'save_type::subassembly': subassembly_circuit_states
|
||||
- id: trailer
|
||||
contents: 'redstone sux lol'
|
||||
|
||||
types:
|
||||
version:
|
||||
seq:
|
||||
- id: major
|
||||
type: s4
|
||||
- id: minor
|
||||
type: s4
|
||||
- id: build
|
||||
type: s4
|
||||
- id: revision
|
||||
type: s4
|
||||
string:
|
||||
seq:
|
||||
- id: length
|
||||
type: s4
|
||||
- id: text
|
||||
type: str
|
||||
size: length
|
||||
encoding: UTF-8
|
||||
peg_address:
|
||||
seq:
|
||||
- id: type
|
||||
type: u1
|
||||
enum: peg_type
|
||||
- id: component_address
|
||||
type: u4
|
||||
- id: index
|
||||
type: s4
|
||||
mod_version:
|
||||
seq:
|
||||
- id: mod_id
|
||||
type: string
|
||||
- id: mod_version
|
||||
type: version
|
||||
position:
|
||||
seq:
|
||||
- id: x
|
||||
type: s4
|
||||
- id: y
|
||||
type: s4
|
||||
- id: z
|
||||
type: s4
|
||||
rotation:
|
||||
seq:
|
||||
- id: x
|
||||
type: f4
|
||||
- id: y
|
||||
type: f4
|
||||
- id: z
|
||||
type: f4
|
||||
- id: w
|
||||
type: f4
|
||||
input:
|
||||
seq:
|
||||
- id: circuit_state_id
|
||||
type: s4
|
||||
output:
|
||||
seq:
|
||||
- id: circuit_state_id
|
||||
type: s4
|
||||
custom_data:
|
||||
seq:
|
||||
- id: length
|
||||
type: s4
|
||||
- id: data
|
||||
size: (length == -1 ? 0 : length)
|
||||
component_id_map:
|
||||
seq:
|
||||
- id: numeric_id
|
||||
type: u2
|
||||
- id: text_id
|
||||
type: string
|
||||
component:
|
||||
seq:
|
||||
- id: address
|
||||
type: u4
|
||||
- id: parent
|
||||
type: u4
|
||||
- id: numeric_id
|
||||
type: u2
|
||||
- id: position
|
||||
type: position
|
||||
- id: rotation
|
||||
type: rotation
|
||||
- id: number_of_inputs
|
||||
type: s4
|
||||
- id: inputs
|
||||
type: input
|
||||
repeat: expr
|
||||
repeat-expr: number_of_inputs
|
||||
- id: number_of_outputs
|
||||
type: s4
|
||||
- id: outputs
|
||||
type: output
|
||||
repeat: expr
|
||||
repeat-expr: number_of_outputs
|
||||
- id: custom_data
|
||||
type: custom_data
|
||||
wire:
|
||||
seq:
|
||||
- id: first_point
|
||||
type: peg_address
|
||||
- id: second_point
|
||||
type: peg_address
|
||||
- id: circuit_state_id
|
||||
type: s4
|
||||
- id: wire_rotation
|
||||
type: f4
|
||||
world_circuit_states:
|
||||
seq:
|
||||
- id: number_of_bytes
|
||||
type: s4
|
||||
- id: states
|
||||
size: number_of_bytes
|
||||
subassembly_circuit_states:
|
||||
seq:
|
||||
- id: number_of_states
|
||||
type: s4
|
||||
- id: states
|
||||
type: s4
|
||||
repeat: expr
|
||||
repeat-expr: number_of_states
|
||||
|
||||
enums:
|
||||
peg_type:
|
||||
0: undefined
|
||||
1: input_peg
|
||||
2: putput_peg
|
||||
save_type:
|
||||
0: unknown
|
||||
1: world
|
||||
2: subassembly
|
@ -0,0 +1,149 @@
|
||||
use std::io::{self, Write};
|
||||
|
||||
use binary_serialize_derive::BinarySerializable;
|
||||
use quaternion::Quaternion;
|
||||
use vecmath::Vector3;
|
||||
|
||||
pub trait BinarySerializable {
|
||||
fn write_to<W: Write>(&self, writer: &mut W) -> io::Result<()>;
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Copy, Clone)]
|
||||
enum PegType {
|
||||
Input = 1,
|
||||
Output = 2,
|
||||
}
|
||||
|
||||
pub type int = i32;
|
||||
pub type float = f32;
|
||||
pub type ComponentAddress = u32;
|
||||
|
||||
#[derive(BinarySerializable)]
|
||||
pub struct Version {
|
||||
pub major: int,
|
||||
pub minor: int,
|
||||
pub build: int,
|
||||
pub revision: int,
|
||||
}
|
||||
impl Version {
|
||||
pub fn new(major: int, minor: int, build: int, revision: int) -> Self {
|
||||
Self {
|
||||
major,
|
||||
minor,
|
||||
build,
|
||||
revision,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(BinarySerializable)]
|
||||
pub struct ModVersion {
|
||||
pub id: String,
|
||||
pub version: Version,
|
||||
}
|
||||
|
||||
#[derive(BinarySerializable)]
|
||||
pub struct Input {
|
||||
circuit_state_id: int,
|
||||
}
|
||||
#[derive(BinarySerializable)]
|
||||
pub struct Output {
|
||||
circuit_state_id: int,
|
||||
}
|
||||
|
||||
#[derive(BinarySerializable)]
|
||||
pub struct ComponentIdMap {
|
||||
pub numeric_id: i16,
|
||||
pub text_id: String,
|
||||
}
|
||||
|
||||
#[derive(BinarySerializable)]
|
||||
pub struct PegAddress {
|
||||
peg_type: PegType,
|
||||
component_address: ComponentAddress,
|
||||
index: int,
|
||||
}
|
||||
|
||||
#[derive(BinarySerializable)]
|
||||
pub struct Component {
|
||||
pub address: ComponentAddress,
|
||||
pub parent: ComponentAddress,
|
||||
pub numeric_id: u16,
|
||||
pub position: Vector3<int>,
|
||||
pub rotation: Quaternion<float>,
|
||||
pub inputs: Vec<Input>,
|
||||
pub outputs: Vec<Output>,
|
||||
pub custom_data: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(BinarySerializable)]
|
||||
pub struct Wire {
|
||||
first_point: PegAddress,
|
||||
second_point: PegAddress,
|
||||
circuit_state_id: int,
|
||||
wire_rotation: float,
|
||||
}
|
||||
|
||||
impl<T: BinarySerializable> BinarySerializable for Vec<T> {
|
||||
fn write_to<W: Write>(&self, writer: &mut W) -> io::Result<()> {
|
||||
writer.write_all(&(self.len() as int).to_le_bytes())?;
|
||||
for value in self {
|
||||
value.write_to(writer)?;
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
impl BinarySerializable for Vec<u8> {
|
||||
fn write_to<W: Write>(&self, writer: &mut W) -> io::Result<()> {
|
||||
writer.write_all(&(self.len() as int).to_le_bytes())?;
|
||||
writer.write_all(&self)?;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
impl BinarySerializable for PegType {
|
||||
fn write_to<W: Write>(&self, writer: &mut W) -> io::Result<()> {
|
||||
writer.write_all(&[*self as u8])
|
||||
}
|
||||
}
|
||||
|
||||
impl BinarySerializable for String {
|
||||
fn write_to<W: Write>(&self, writer: &mut W) -> io::Result<()> {
|
||||
writer.write_all(&(self.len() as int).to_le_bytes())?;
|
||||
writer.write_all(self.as_bytes())?;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
impl BinarySerializable for Vector3<int> {
|
||||
fn write_to<W: Write>(&self, writer: &mut W) -> io::Result<()> {
|
||||
writer.write_all(&self[0].to_le_bytes())?; // x
|
||||
writer.write_all(&self[1].to_le_bytes())?; // y
|
||||
writer.write_all(&self[2].to_le_bytes())?; // z
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
impl BinarySerializable for Quaternion<float> {
|
||||
fn write_to<W: Write>(&self, writer: &mut W) -> io::Result<()> {
|
||||
writer.write_all(&self.1[0].to_le_bytes())?; // x
|
||||
writer.write_all(&self.1[1].to_le_bytes())?; // y
|
||||
writer.write_all(&self.1[2].to_le_bytes())?; // z
|
||||
writer.write_all(&self.0.to_le_bytes())?; // w
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_binary_num {
|
||||
($($t:ty),*) => {
|
||||
$(impl BinarySerializable for $t {
|
||||
fn write_to<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
|
||||
writer.write_all(&self.to_le_bytes())
|
||||
}
|
||||
})*
|
||||
};
|
||||
}
|
||||
|
||||
impl_binary_num!(i16, u16, i32, u32, f32);
|
@ -0,0 +1,102 @@
|
||||
use std::{
|
||||
i32,
|
||||
io::{self, Write},
|
||||
};
|
||||
|
||||
use crate::lw::{self, BinarySerializable, ComponentIdMap, ModVersion, Version};
|
||||
|
||||
enum SaveType {
|
||||
World = 1,
|
||||
Subassembly = 2,
|
||||
}
|
||||
|
||||
pub fn write_subassembly_file(
|
||||
file: &mut impl Write,
|
||||
components: &[lw::Component],
|
||||
wires: &[lw::Wire],
|
||||
) -> io::Result<()> {
|
||||
// Header:
|
||||
file.write_all(b"Logic World save")?; // Magic bytes
|
||||
file.write_all(&[7 as u8])?; // Version 7
|
||||
Version::new(0, 92, 0, 455).write_to(file)?; // Game Version
|
||||
file.write_all(&[SaveType::Subassembly as u8])?;
|
||||
|
||||
(components.len() as i32).write_to(file)?;
|
||||
(wires.len() as i32).write_to(file)?;
|
||||
|
||||
// Body:
|
||||
let mods = vec![ModVersion {
|
||||
id: "MHG".to_owned(),
|
||||
version: Version::new(0, 91, 4, -1),
|
||||
}];
|
||||
mods.write_to(file)?;
|
||||
|
||||
let component_id_map = vec![ComponentIdMap {
|
||||
numeric_id: 15,
|
||||
text_id: "MHG.CircuitBoard".to_owned(),
|
||||
}];
|
||||
component_id_map.write_to(file)?;
|
||||
|
||||
for component in components {
|
||||
component.write_to(file)?;
|
||||
}
|
||||
|
||||
for wire in wires {
|
||||
wire.write_to(file)?;
|
||||
}
|
||||
|
||||
0i32.write_to(file)?; // We don't have any turned on nets
|
||||
|
||||
file.write_all(b"redstone sux lol")?; // Footer
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
pub fn write_placing_info_file(file: &mut impl Write) -> io::Result<()> {
|
||||
file.write_all(
|
||||
b"PlacingInfo:
|
||||
RaiseOnChangedEvents: true
|
||||
Flipped: false
|
||||
RotationAboutUpVector: 180
|
||||
Offset:
|
||||
x: 0
|
||||
y: 0
|
||||
BoardIsFlat: true
|
||||
BoardRotationState: 0
|
||||
",
|
||||
)
|
||||
}
|
||||
pub fn write_placements_file(file: &mut impl Write) -> io::Result<()> {
|
||||
file.write_all(
|
||||
b"Placements:
|
||||
-
|
||||
CornerMidpointPositionBeforeFlipping:
|
||||
x: 0
|
||||
y: 0
|
||||
z: 0
|
||||
RotationBeforeFlipping:
|
||||
x: 0
|
||||
y: 0
|
||||
z: 0
|
||||
w: 1
|
||||
PlacementType: OnTerrain
|
||||
Flipped: false
|
||||
BoardRotationState: 0
|
||||
Type: Standard
|
||||
data_format_version: 1
|
||||
",
|
||||
)
|
||||
}
|
||||
|
||||
pub fn write_meta_file(file: &mut impl Write, title: &str) -> io::Result<()> {
|
||||
file.write_all(
|
||||
format!(
|
||||
"Title: {}
|
||||
Column: null
|
||||
Category: null
|
||||
",
|
||||
title
|
||||
)
|
||||
.as_bytes(),
|
||||
)
|
||||
}
|
Loading…
Reference in new issue