From 1579b5d82c33e2d0934cb5e815f5e6740e5d2af3 Mon Sep 17 00:00:00 2001 From: D4VID Date: Sun, 17 Aug 2025 22:17:27 +0200 Subject: [PATCH] Generating subassemblies --- .gitignore | 3 +- binary_serialize_derive/Cargo.lock | 47 +++++ binary_serialize_derive/Cargo.toml | 12 ++ binary_serialize_derive/src/lib.rs | 36 ++++ logicworld_blotter_file_format_v7.ksy | 181 ++++++++++++++++++++ Cargo.lock => verilog2logicworld/Cargo.lock | 71 ++++++++ Cargo.toml => verilog2logicworld/Cargo.toml | 3 + counter.v => verilog2logicworld/counter.v | 0 gates.v => verilog2logicworld/gates.v | 0 verilog2logicworld/src/lw.rs | 149 ++++++++++++++++ {src => verilog2logicworld/src}/main.rs | 41 ++++- verilog2logicworld/src/subassembly.rs | 102 +++++++++++ 12 files changed, 641 insertions(+), 4 deletions(-) create mode 100644 binary_serialize_derive/Cargo.lock create mode 100644 binary_serialize_derive/Cargo.toml create mode 100644 binary_serialize_derive/src/lib.rs create mode 100644 logicworld_blotter_file_format_v7.ksy rename Cargo.lock => verilog2logicworld/Cargo.lock (82%) rename Cargo.toml => verilog2logicworld/Cargo.toml (54%) rename counter.v => verilog2logicworld/counter.v (100%) rename gates.v => verilog2logicworld/gates.v (100%) create mode 100644 verilog2logicworld/src/lw.rs rename {src => verilog2logicworld/src}/main.rs (51%) create mode 100644 verilog2logicworld/src/subassembly.rs diff --git a/.gitignore b/.gitignore index 9593387..5db7991 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ -/target +target/ .nvim +output/ diff --git a/binary_serialize_derive/Cargo.lock b/binary_serialize_derive/Cargo.lock new file mode 100644 index 0000000..682123d --- /dev/null +++ b/binary_serialize_derive/Cargo.lock @@ -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" diff --git a/binary_serialize_derive/Cargo.toml b/binary_serialize_derive/Cargo.toml new file mode 100644 index 0000000..80d024e --- /dev/null +++ b/binary_serialize_derive/Cargo.toml @@ -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"] } diff --git a/binary_serialize_derive/src/lib.rs b/binary_serialize_derive/src/lib.rs new file mode 100644 index 0000000..57aeadf --- /dev/null +++ b/binary_serialize_derive/src/lib.rs @@ -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(&self, writer: &mut W) -> std::io::Result<()> { + #(#field_writes)* + return Ok(()); + } + } + }; + + TokenStream::from(expanded) +} + diff --git a/logicworld_blotter_file_format_v7.ksy b/logicworld_blotter_file_format_v7.ksy new file mode 100644 index 0000000..32c8d3e --- /dev/null +++ b/logicworld_blotter_file_format_v7.ksy @@ -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 \ No newline at end of file diff --git a/Cargo.lock b/verilog2logicworld/Cargo.lock similarity index 82% rename from Cargo.lock rename to verilog2logicworld/Cargo.lock index 43c58cd..ddeb0db 100644 --- a/Cargo.lock +++ b/verilog2logicworld/Cargo.lock @@ -2,6 +2,15 @@ # 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 = "bitflags" version = "2.9.1" @@ -66,6 +75,39 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +[[package]] +name = "piston-float" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad78bf43dcf80e8f950c92b84f938a0fc7590b7f6866fbcbeca781609c115590" + +[[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 = "quaternion" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a23cd933813dea6a8e24391c80634deba6a70ca5a14b2b944b8cb6d871e071" +dependencies = [ + "vecmath", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + [[package]] name = "r-efi" version = "5.3.0" @@ -85,6 +127,17 @@ dependencies = [ "windows-sys 0.60.2", ] +[[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 = "tempfile" version = "3.20.0" @@ -98,12 +151,30 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "vecmath" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "956ae1e0d85bca567dee1dcf87fb1ca2e792792f66f87dced8381f99cd91156a" +dependencies = [ + "piston-float", +] + [[package]] name = "verilog2logicworld" version = "0.1.0" dependencies = [ + "binary_serialize_derive", "json", + "quaternion", "tempfile", + "vecmath", ] [[package]] diff --git a/Cargo.toml b/verilog2logicworld/Cargo.toml similarity index 54% rename from Cargo.toml rename to verilog2logicworld/Cargo.toml index 3c06e1b..448f44f 100644 --- a/Cargo.toml +++ b/verilog2logicworld/Cargo.toml @@ -4,5 +4,8 @@ version = "0.1.0" edition = "2021" [dependencies] +binary_serialize_derive = { path = "../binary_serialize_derive" } json = "0.12.4" +quaternion = "2.0.0" tempfile = "3.20.0" +vecmath = "1.0.0" diff --git a/counter.v b/verilog2logicworld/counter.v similarity index 100% rename from counter.v rename to verilog2logicworld/counter.v diff --git a/gates.v b/verilog2logicworld/gates.v similarity index 100% rename from gates.v rename to verilog2logicworld/gates.v diff --git a/verilog2logicworld/src/lw.rs b/verilog2logicworld/src/lw.rs new file mode 100644 index 0000000..4cec668 --- /dev/null +++ b/verilog2logicworld/src/lw.rs @@ -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(&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, + pub rotation: Quaternion, + pub inputs: Vec, + pub outputs: Vec, + pub custom_data: Vec, +} + +#[derive(BinarySerializable)] +pub struct Wire { + first_point: PegAddress, + second_point: PegAddress, + circuit_state_id: int, + wire_rotation: float, +} + +impl BinarySerializable for Vec { + fn write_to(&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 { + fn write_to(&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(&self, writer: &mut W) -> io::Result<()> { + writer.write_all(&[*self as u8]) + } +} + +impl BinarySerializable for String { + fn write_to(&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 { + fn write_to(&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 { + fn write_to(&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(&self, writer: &mut W) -> std::io::Result<()> { + writer.write_all(&self.to_le_bytes()) + } + })* + }; +} + +impl_binary_num!(i16, u16, i32, u32, f32); diff --git a/src/main.rs b/verilog2logicworld/src/main.rs similarity index 51% rename from src/main.rs rename to verilog2logicworld/src/main.rs index 9bce755..0e75cc4 100644 --- a/src/main.rs +++ b/verilog2logicworld/src/main.rs @@ -4,7 +4,12 @@ use std::{ process::{Command, Stdio}, }; +use subassembly::{ + write_meta_file, write_placements_file, write_placing_info_file, write_subassembly_file, +}; use tempfile::tempdir; +mod lw; +mod subassembly; #[derive(Debug)] enum Error { @@ -61,10 +66,40 @@ fn run_yosys(yosys_command: &str, module: Option<&str>, files: &[&str]) -> Resul return Ok(()); } +fn write_subassembly() -> Result<(), Error> { + let mut file = File::create("output/data.partialworld")?; + let components = vec![lw::Component { + address: 1, + parent: 0, + numeric_id: 15, + position: [0, 0, 0], + rotation: quaternion::id(), + inputs: vec![], + outputs: vec![], + custom_data: vec![0x80, 0x00, 0x00, 5, 0, 0, 0, 10, 0, 0, 0], + }]; + let wires = vec![]; + write_subassembly_file(&mut file, &components, &wires)?; + + let mut placing_info_file = File::create("output/LastMainPlacingInfo.jecs")?; + write_placing_info_file(&mut placing_info_file)?; + + let mut placements_file = File::create("output/placements.jecs")?; + write_placements_file(&mut placements_file)?; + + let mut meta_file = File::create("output/meta.jecs")?; + write_meta_file(&mut meta_file, &"output")?; + + return Ok(()); +} + fn main() { - let result = run_yosys("proc;", None, &["gates.v"]); - println!("{:?}", result); + // let result = run_yosys("proc;", None, &["gates.v"]); + // println!("{:?}", result); + + // let result = run_yosys("proc; flatten; wreduce; opt; fsm; opt; memory -nomap -nordff; opt; muxpack; peepopt; async2sync; wreduce; opt -mux_bool", Some("$abstract\\gates"), &["gates.v"]); + // println!("{:?}", result); - let result = run_yosys("proc; flatten; wreduce; opt; fsm; opt; memory -nomap -nordff; opt; muxpack; peepopt; async2sync; wreduce; opt -mux_bool", Some("$abstract\\gates"), &["gates.v"]); + let result = write_subassembly(); println!("{:?}", result); } diff --git a/verilog2logicworld/src/subassembly.rs b/verilog2logicworld/src/subassembly.rs new file mode 100644 index 0000000..080e1e8 --- /dev/null +++ b/verilog2logicworld/src/subassembly.rs @@ -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(), + ) +}