From 21700e6e6aa1c7b1b6eb8fa74e40d95b37ed729d Mon Sep 17 00:00:00 2001 From: Connor Slade Date: Tue, 18 Jun 2024 21:41:47 -0400 Subject: [PATCH] Refactor goo format --- Cargo.lock | 189 +++++++++++++++ common/src/serde.rs | 70 ------ common/src/serde/deserializer.rs | 79 +++++++ common/src/serde/mod.rs | 7 + common/src/serde/serializer.rs | 113 +++++++++ common/src/serde/types.rs | 42 ++++ goo_format/Cargo.toml | 4 + goo_format/src/default.rs | 94 ++++++++ goo_format/src/encoded_layer.rs | 185 +++++++++++++++ goo_format/src/file.rs | 44 ++++ goo_format/src/header_info.rs | 340 ++++++++++++++++++++++++++ goo_format/src/layer_content.rs | 109 +++++++++ goo_format/src/lib.rs | 393 +------------------------------ goo_format/src/main.rs | 65 +++++ goo_format/src/preview_image.rs | 47 ++++ 15 files changed, 1329 insertions(+), 452 deletions(-) delete mode 100644 common/src/serde.rs create mode 100644 common/src/serde/deserializer.rs create mode 100644 common/src/serde/mod.rs create mode 100644 common/src/serde/serializer.rs create mode 100644 common/src/serde/types.rs create mode 100644 goo_format/src/default.rs create mode 100644 goo_format/src/encoded_layer.rs create mode 100644 goo_format/src/file.rs create mode 100644 goo_format/src/header_info.rs create mode 100644 goo_format/src/layer_content.rs create mode 100644 goo_format/src/main.rs create mode 100644 goo_format/src/preview_image.rs diff --git a/Cargo.lock b/Cargo.lock index de67ca1..f3672af 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -30,6 +30,55 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4aa90d7ce82d4be67b64039a3d588d38dbcc6736577de4a847025ce5b0c468d1" +[[package]] +name = "anstream" +version = "0.6.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" + +[[package]] +name = "anstyle-parse" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" +dependencies = [ + "anstyle", + "windows-sys", +] + [[package]] name = "anyhow" version = "1.0.86" @@ -172,12 +221,58 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "clap" +version = "4.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5db83dced34638ad474f39f250d7fea9598bdd239eaced1bdf45d597da0f433f" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7e204572485eb3fbf28f871612191521df159bc3e15a9f5064c66dba3a8c05f" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c780290ccf4fb26629baa7a1081e68ced113f1d3ec302fa5948f1c381ebf06c6" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "clap_lex" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" + [[package]] name = "color_quant" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" +[[package]] +name = "colorchoice" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" + [[package]] name = "common" version = "0.1.0" @@ -314,7 +409,10 @@ dependencies = [ name = "goo_format" version = "0.1.0" dependencies = [ + "anyhow", + "clap", "common", + "image", ] [[package]] @@ -417,6 +515,12 @@ dependencies = [ "syn 2.0.66", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" + [[package]] name = "itertools" version = "0.12.1" @@ -1014,6 +1118,12 @@ dependencies = [ "float-cmp", ] +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "syn" version = "1.0.109" @@ -1138,6 +1248,12 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + [[package]] name = "v_frame" version = "0.3.8" @@ -1231,6 +1347,79 @@ dependencies = [ "safe_arch", ] +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" + [[package]] name = "winnow" version = "0.6.13" diff --git a/common/src/serde.rs b/common/src/serde.rs deleted file mode 100644 index 5faeae5..0000000 --- a/common/src/serde.rs +++ /dev/null @@ -1,70 +0,0 @@ -pub struct Serializer<'a> { - buffer: &'a mut [u8], - offset: usize, -} - -pub struct SizedString { - data: [u8; SIZE], -} - -impl<'a> Serializer<'a> { - pub fn new(buffer: &'a mut [u8]) -> Self { - Self { buffer, offset: 0 } - } - - pub fn write_bool(&mut self, data: bool) { - self.write_u8(data as u8); - } - - pub fn write_u8(&mut self, data: u8) { - self.buffer[self.offset] = data; - self.offset += 1; - } - - pub fn write_u16(&mut self, data: u16) { - self.buffer[self.offset..self.offset + 2].copy_from_slice(&data.to_be_bytes()); - self.offset += 2; - } - - pub fn write_u32(&mut self, data: u32) { - self.buffer[self.offset..self.offset + 4].copy_from_slice(&data.to_be_bytes()); - self.offset += 4; - } - - pub fn write_u64(&mut self, data: u64) { - self.buffer[self.offset..self.offset + 8].copy_from_slice(&data.to_be_bytes()); - self.offset += 8; - } - - pub fn write_f32(&mut self, data: f32) { - self.buffer[self.offset..self.offset + 4].copy_from_slice(&data.to_be_bytes()); - self.offset += 4; - } - - pub fn write_bytes(&mut self, data: &[u8]) { - self.buffer[self.offset..self.offset + data.len()].copy_from_slice(data); - self.offset += data.len(); - } - - pub fn write_sized_string(&mut self, data: &SizedString) { - let len = data.data.len(); - self.buffer[self.offset..self.offset + len].copy_from_slice(&data.data); - self.offset += len; - } -} - -impl SizedString { - pub const fn new(data: &[u8]) -> Self { - debug_assert!(data.len() <= SIZE); - - // kinda crazy this works in a const fn - let mut arr = [0; SIZE]; - let mut i = 0; - while i < SIZE && i < data.len() { - arr[i] = data[i]; - i += 1; - } - - Self { data: arr } - } -} diff --git a/common/src/serde/deserializer.rs b/common/src/serde/deserializer.rs new file mode 100644 index 0000000..3afd8f2 --- /dev/null +++ b/common/src/serde/deserializer.rs @@ -0,0 +1,79 @@ +use super::SizedString; + +pub struct Deserializer<'a> { + buffer: &'a [u8], + offset: usize, +} + +#[allow(dead_code)] +impl<'a> Deserializer<'a> { + pub fn new(data: &'a [u8]) -> Self { + Self { + buffer: data, + offset: 0, + } + } + + pub fn read_bool(&mut self) -> bool { + self.read_u8() != 0 + } + + pub fn read_u8(&mut self) -> u8 { + let value = self.buffer[self.offset]; + self.offset += 1; + value + } + + pub fn read_u16(&mut self) -> u16 { + let value = u16::from_be_bytes([self.buffer[self.offset], self.buffer[self.offset + 1]]); + self.offset += 2; + value + } + + pub fn read_u32(&mut self) -> u32 { + let value = u32::from_be_bytes([ + self.buffer[self.offset], + self.buffer[self.offset + 1], + self.buffer[self.offset + 2], + self.buffer[self.offset + 3], + ]); + self.offset += 4; + value + } + + pub fn read_u64(&mut self) -> u64 { + let value = u64::from_be_bytes([ + self.buffer[self.offset], + self.buffer[self.offset + 1], + self.buffer[self.offset + 2], + self.buffer[self.offset + 3], + self.buffer[self.offset + 4], + self.buffer[self.offset + 5], + self.buffer[self.offset + 6], + self.buffer[self.offset + 7], + ]); + self.offset += 8; + value + } + + pub fn read_f32(&mut self) -> f32 { + let value = f32::from_be_bytes([ + self.buffer[self.offset], + self.buffer[self.offset + 1], + self.buffer[self.offset + 2], + self.buffer[self.offset + 3], + ]); + self.offset += 4; + value + } + + pub fn read_bytes(&mut self, length: usize) -> &'a [u8] { + let value = &self.buffer[self.offset..self.offset + length]; + self.offset += length; + value + } + + pub fn read_sized_string(&mut self) -> SizedString { + SizedString::new(self.read_bytes(SIZE)) + } +} diff --git a/common/src/serde/mod.rs b/common/src/serde/mod.rs new file mode 100644 index 0000000..cbfad25 --- /dev/null +++ b/common/src/serde/mod.rs @@ -0,0 +1,7 @@ +mod deserializer; +mod serializer; +mod types; + +pub use deserializer::Deserializer; +pub use serializer::Serializer; +pub use types::SizedString; diff --git a/common/src/serde/serializer.rs b/common/src/serde/serializer.rs new file mode 100644 index 0000000..f33a7e5 --- /dev/null +++ b/common/src/serde/serializer.rs @@ -0,0 +1,113 @@ +use super::SizedString; + +pub trait Serializer { + fn write_bool(&mut self, data: bool); + fn write_u8(&mut self, data: u8); + fn write_u16(&mut self, data: u16); + fn write_u32(&mut self, data: u32); + fn write_u64(&mut self, data: u64); + fn write_f32(&mut self, data: f32); + fn write_bytes(&mut self, data: &[u8]); + fn write_sized_string(&mut self, data: &SizedString); +} + +pub struct SizedSerializer<'a> { + buffer: &'a mut [u8], + offset: usize, +} + +pub struct DynamicSerializer { + buffer: Vec, +} + +impl<'a> SizedSerializer<'a> { + pub fn new(buffer: &'a mut [u8]) -> Self { + Self { buffer, offset: 0 } + } +} + +impl DynamicSerializer { + pub fn new() -> Self { + Self { buffer: Vec::new() } + } + + pub fn into_inner(self) -> Vec { + self.buffer + } +} + +impl Serializer for SizedSerializer<'_> { + fn write_bool(&mut self, data: bool) { + self.write_u8(data as u8); + } + + fn write_u8(&mut self, data: u8) { + self.buffer[self.offset] = data; + self.offset += 1; + } + + fn write_u16(&mut self, data: u16) { + self.buffer[self.offset..self.offset + 2].copy_from_slice(&data.to_be_bytes()); + self.offset += 2; + } + + fn write_u32(&mut self, data: u32) { + self.buffer[self.offset..self.offset + 4].copy_from_slice(&data.to_be_bytes()); + self.offset += 4; + } + + fn write_u64(&mut self, data: u64) { + self.buffer[self.offset..self.offset + 8].copy_from_slice(&data.to_be_bytes()); + self.offset += 8; + } + + fn write_f32(&mut self, data: f32) { + self.buffer[self.offset..self.offset + 4].copy_from_slice(&data.to_be_bytes()); + self.offset += 4; + } + + fn write_bytes(&mut self, data: &[u8]) { + self.buffer[self.offset..self.offset + data.len()].copy_from_slice(data); + self.offset += data.len(); + } + + fn write_sized_string(&mut self, data: &SizedString) { + let len = data.data.len(); + self.buffer[self.offset..self.offset + len].copy_from_slice(&data.data); + self.offset += len; + } +} + +impl Serializer for DynamicSerializer { + fn write_bool(&mut self, data: bool) { + self.write_u8(data as u8); + } + + fn write_u8(&mut self, data: u8) { + self.buffer.push(data); + } + + fn write_u16(&mut self, data: u16) { + self.buffer.extend_from_slice(&data.to_be_bytes()); + } + + fn write_u32(&mut self, data: u32) { + self.buffer.extend_from_slice(&data.to_be_bytes()); + } + + fn write_u64(&mut self, data: u64) { + self.buffer.extend_from_slice(&data.to_be_bytes()); + } + + fn write_f32(&mut self, data: f32) { + self.buffer.extend_from_slice(&data.to_be_bytes()); + } + + fn write_bytes(&mut self, data: &[u8]) { + self.buffer.extend_from_slice(data); + } + + fn write_sized_string(&mut self, data: &SizedString) { + self.buffer.extend_from_slice(&data.data); + } +} diff --git a/common/src/serde/types.rs b/common/src/serde/types.rs new file mode 100644 index 0000000..7481d43 --- /dev/null +++ b/common/src/serde/types.rs @@ -0,0 +1,42 @@ +use std::fmt::{self, Debug, Display}; + +pub struct SizedString { + pub(crate) data: [u8; SIZE], +} + +impl SizedString { + pub const fn new_full(data: [u8; SIZE]) -> Self { + Self { data } + } + + pub const fn new(data: &[u8]) -> Self { + debug_assert!(data.len() <= SIZE); + + // kinda crazy this works in a const fn + let mut arr = [0; SIZE]; + let mut i = 0; + while i < SIZE && i < data.len() { + arr[i] = data[i]; + i += 1; + } + + Self { data: arr } + } +} + +impl Display for SizedString<32> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let null = self.data.iter().position(|&x| x == 0).unwrap_or(32); + f.write_str(&String::from_utf8_lossy(&self.data[..null])) + } +} + +impl Debug for SizedString { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let null = self.data.iter().position(|&x| x == 0).unwrap_or(SIZE); + f.write_fmt(format_args!( + "\"{}\"", + String::from_utf8_lossy(&self.data[..null]) + )) + } +} diff --git a/goo_format/Cargo.toml b/goo_format/Cargo.toml index 75b19a7..a16e2b6 100644 --- a/goo_format/Cargo.toml +++ b/goo_format/Cargo.toml @@ -6,4 +6,8 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +anyhow = "1.0.86" +clap = { version = "4.5.7", features = ["derive"] } + common = { path = "../common" } +image = "0.25.1" diff --git a/goo_format/src/default.rs b/goo_format/src/default.rs new file mode 100644 index 0000000..deea769 --- /dev/null +++ b/goo_format/src/default.rs @@ -0,0 +1,94 @@ +use common::serde::SizedString; + +use crate::{HeaderInfo, LayerContent, PreviewImage}; + +impl Default for HeaderInfo { + fn default() -> Self { + Self { + version: SizedString::new(b"V3.0"), + software_info: SizedString::new(b"sla_slicer by Connor Slade"), + software_version: SizedString::new(b"0.1.0"), + file_time: SizedString::new(b"2024-06-14 08:10:14"), + printer_name: SizedString::new(b"standard"), + printer_type: SizedString::new(b"Default"), + profile_name: SizedString::new(b"New Script"), + anti_aliasing_level: 8, + grey_level: 0, + blur_level: 0, + small_preview: PreviewImage::empty(), + big_preview: PreviewImage::empty(), + layer_count: 171, + x_resolution: 11520, + y_resolution: 5102, + x_mirror: false, + y_mirror: false, + x_size: 218.88, + y_size: 122.88, + z_size: 260.0, + layer_thickness: 0.05, + exposure_time: 3.0, + exposure_delay_mode: true, + turn_off_time: 0.0, + bottom_before_lift_time: 0.0, + bottom_after_lift_time: 0.0, + bottom_after_retract_time: 0.0, + before_lift_time: 0.0, + after_lift_time: 0.0, + after_retract_time: 0.0, + bottom_exposure_time: 50.0, + bottom_layers: 8, + bottom_lift_distance: 5.0, + bottom_lift_speed: 65.0, + lift_distance: 5.0, + lift_speed: 65.0, + bottom_retract_distance: 5.0, + bottom_retract_speed: 150.0, + retract_distance: 5.0, + retract_speed: 0.0, + bottom_second_lift_distance: 0.0, + bottom_second_lift_speed: 0.0, + second_lift_distance: 0.0, + second_lift_speed: 0.0, + bottom_second_retract_distance: 0.0, + bottom_second_retract_speed: 0.0, + second_retract_distance: 0.0, + second_retract_speed: 0.0, + bottom_light_pwm: 255, + light_pwm: 255, + advance_mode: false, + printing_time: 2659, + total_volume: 526.507, + total_weight: 0.684, + total_price: 0.0, + price_unit: SizedString::new(b"$"), + grey_scale_level: true, + transition_layers: 10, + } + } +} + +impl Default for LayerContent { + fn default() -> Self { + Self { + pause_flag: 0, + pause_position_z: 200.0, + layer_position_z: 0.05, + layer_exposure_time: 50.0, + layer_off_time: 0.0, + before_lift_time: 0.0, + after_lift_time: 0.0, + after_retract_time: 0.0, + lift_distance: 5.0, + lift_speed: 65.0, + second_lift_distance: 0.0, + second_lift_speed: 0.0, + retract_distance: 5.0, + retract_speed: 150.0, + second_retract_distance: 0.0, + second_retract_speed: 0.0, + light_pwm: 255, + data: Vec::new(), + checksum: 0, + } + } +} diff --git a/goo_format/src/encoded_layer.rs b/goo_format/src/encoded_layer.rs new file mode 100644 index 0000000..6da2ad3 --- /dev/null +++ b/goo_format/src/encoded_layer.rs @@ -0,0 +1,185 @@ +use crate::layer_content::calculate_checksum; + +pub struct LayerEncoder { + data: Vec, + last_value: u8, +} + +pub struct LayerDecoder<'a> { + data: &'a [u8], + color: u8, + offset: usize, +} + +pub struct Run { + pub length: u64, + pub value: u8, +} + +impl LayerEncoder { + pub fn new() -> Self { + Self { + data: Vec::new(), + last_value: 0, + } + } + + pub fn add_run(&mut self, length: u64, value: u8) { + // byte 0: aabbcccc + // a => 0: full black, 1: full white, 2: small diff, 3: large diff + // b => 0: 4 bit length, 1: 12 bit value, 2: 20 bit value, 3: 28 bit value + // c => the first 4 bits of the value + // byte 1-3: optional, the rest of the value + + let diff = value as i16 - self.last_value as i16; + let byte_0: u8 = match value { + // Full black and full white are always encoded as is + 0x00 => 0b00, + 0xFF => 0b11, + _ if !self.data.is_empty() && diff.abs() <= 15 => { + // 0babcccc + // a => 0: add diff, 1: sub diff + // b => 0: length of 1, 1: length is next byte + // c => the diff + + if length > 255 { + self.add_run(255, value); + self.add_run(length - 255, value); + return; + } + + let byte_0 = (0b10 << 6) + | (((diff > 0) as u8) << 5) + | (((length != 1) as u8) << 4) + | (diff.abs() as u8); + self.data.push(byte_0); + + if length != 1 { + self.data.push(length as u8); + } + + self.last_value = value; + return; + } + _ => 0b01, + } << 6; + + let chunk_length_size = match length { + 0x0000000..=0x000000F => 0b00, + 0x0000010..=0x0000FFF => 0b01, + 0x0001000..=0x00FFFFF => 0b10, + 0x0100000..=0xFFFFFFF => 0b11, + _ => { + self.add_run(0xFFFFFFF, value); + self.add_run(length - 0xFFFFFFF, value); + return; + } + }; + + self.data + .push(byte_0 | (chunk_length_size << 4) | (length as u8 & 0x0F)); + match chunk_length_size { + 1 => self.data.extend_from_slice(&[(length >> 4) as u8]), + 2 => self + .data + .extend_from_slice(&[(length >> 12) as u8, (length >> 4) as u8]), + 3 => self.data.extend_from_slice(&[ + (length >> 20) as u8, + (length >> 12) as u8, + (length >> 4) as u8, + ]), + _ => {} + } + + self.last_value = value; + } +} + +impl<'a> LayerDecoder<'a> { + pub fn new(data: &'a [u8]) -> Self { + Self { + data, + color: 0, + offset: 0, + } + } + + pub fn checksum(&self) -> u8 { + calculate_checksum(&self.data) + } +} + +impl Iterator for LayerDecoder<'_> { + type Item = Run; + + fn next(&mut self) -> Option { + if self.offset >= self.data.len() { + return None; + } + + let mut length = 0; + let head = self.data[self.offset]; + + // 0b00 -> All 0x00 pixels + // 0b01 -> Gray between 0x01 to 0xFE (byte 1) + // 0b10 -> Diff value from the previous pixel + // 0b11 -> All 0xFF pixels + let chunk_type = head >> 6; + let chunk_length_size = head >> 4 & 0x03; + match chunk_type { + 0b00 => self.color = 0, + 0b01 => { + self.offset += 1; + self.color = self.data[self.offset] + } + 0b10 => { + let diff_type = head >> 4 & 0x03; + let diff_value = head & 0x0F; + if diff_type & 0b01 == 0 { + length = 1; + } else { + self.offset += 1; + length = self.data[self.offset] as u64; + } + if diff_type & 0b10 == 0 { + self.color += diff_value; + } else { + self.color -= diff_value; + } + } + 0b11 => self.color = 0xFF, + _ => unreachable!(), + }; + + if chunk_type != 0b10 { + let base = (head & 0x0F) as u64; + match chunk_length_size { + 0b00 => length = base, + 0b01 => { + length = base + ((self.data[self.offset + 1] as u64) << 4); + self.offset += 1; + } + 0b10 => { + length = base + + ((self.data[self.offset + 1] as u64) << 12) + + ((self.data[self.offset + 2] as u64) << 4); + self.offset += 2; + } + 0b11 => { + length = base + + ((self.data[self.offset + 1] as u64) << 20) + + ((self.data[self.offset + 2] as u64) << 12) + + ((self.data[self.offset + 3] as u64) << 4); + self.offset += 3; + } + _ => unreachable!(), + }; + } + self.offset += 1; + + Some(Run { + length, + value: self.color, + }) + } +} diff --git a/goo_format/src/file.rs b/goo_format/src/file.rs new file mode 100644 index 0000000..c4fbbf1 --- /dev/null +++ b/goo_format/src/file.rs @@ -0,0 +1,44 @@ +use anyhow::{ensure, Result}; + +use common::serde::{Deserializer, Serializer}; + +use crate::{HeaderInfo, LayerContent}; + +const ENDING_STRING: &[u8] = &[ + 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x44, 0x4C, 0x50, 0x00, +]; + +pub struct File { + pub header: HeaderInfo, + pub layers: Vec, +} + +impl File { + pub fn new(header: HeaderInfo, layers: Vec) -> Self { + Self { header, layers } + } +} + +impl File { + pub fn serialize(&self, ser: &mut T) { + self.header.serialize(ser); + for layer in &self.layers { + layer.serialize(ser); + } + ser.write_bytes(ENDING_STRING); + } + + pub fn deserialize(buf: &[u8]) -> Result { + let mut des = Deserializer::new(buf); + + let header = HeaderInfo::deserialize(&buf[0..HeaderInfo::SIZE])?; + let mut layers = Vec::with_capacity(header.layer_count as usize); + + for _ in 0..header.layer_count { + layers.push(LayerContent::deserialize(&buf[HeaderInfo::SIZE..])?); + } + + ensure!(des.read_bytes(ENDING_STRING.len()) == ENDING_STRING); + Ok(Self { header, layers }) + } +} diff --git a/goo_format/src/header_info.rs b/goo_format/src/header_info.rs new file mode 100644 index 0000000..b6a29f2 --- /dev/null +++ b/goo_format/src/header_info.rs @@ -0,0 +1,340 @@ +use std::fmt::{self, Debug}; + +use anyhow::{ensure, Result}; + +use common::serde::{Deserializer, Serializer, SizedString}; + +use crate::PreviewImage; + +pub struct HeaderInfo { + pub version: SizedString<4>, + pub software_info: SizedString<32>, + pub software_version: SizedString<24>, + pub file_time: SizedString<24>, + pub printer_name: SizedString<32>, + pub printer_type: SizedString<32>, + pub profile_name: SizedString<32>, + pub anti_aliasing_level: u16, + pub grey_level: u16, + pub blur_level: u16, + pub small_preview: PreviewImage<116, 116>, + pub big_preview: PreviewImage<290, 290>, + pub layer_count: u32, + pub x_resolution: u16, + pub y_resolution: u16, + pub x_mirror: bool, + pub y_mirror: bool, + pub x_size: f32, + pub y_size: f32, + pub z_size: f32, + pub layer_thickness: f32, + pub exposure_time: f32, + pub exposure_delay_mode: bool, + pub turn_off_time: f32, + pub bottom_before_lift_time: f32, + pub bottom_after_lift_time: f32, + pub bottom_after_retract_time: f32, + pub before_lift_time: f32, + pub after_lift_time: f32, + pub after_retract_time: f32, + pub bottom_exposure_time: f32, + pub bottom_layers: u32, + pub bottom_lift_distance: f32, + pub bottom_lift_speed: f32, + pub lift_distance: f32, + pub lift_speed: f32, + pub bottom_retract_distance: f32, + pub bottom_retract_speed: f32, + pub retract_distance: f32, + pub retract_speed: f32, + pub bottom_second_lift_distance: f32, + pub bottom_second_lift_speed: f32, + pub second_lift_distance: f32, + pub second_lift_speed: f32, + pub bottom_second_retract_distance: f32, + pub bottom_second_retract_speed: f32, + pub second_retract_distance: f32, + pub second_retract_speed: f32, + pub bottom_light_pwm: u16, + pub light_pwm: u16, + pub advance_mode: bool, + pub printing_time: u32, + pub total_volume: f32, + pub total_weight: f32, + pub total_price: f32, + pub price_unit: SizedString<8>, + pub grey_scale_level: bool, + pub transition_layers: u16, +} + +impl HeaderInfo { + pub const SIZE: usize = 0x2FB95; +} + +// this is fine +impl HeaderInfo { + pub fn serialize(&self, ser: &mut T) { + ser.write_sized_string(&self.version); + ser.write_bytes(&[0x07, 0x00, 0x00, 0x00, 0x44, 0x4C, 0x50, 0x00]); + ser.write_sized_string(&self.software_info); + ser.write_sized_string(&self.software_version); + ser.write_sized_string(&self.file_time); + ser.write_sized_string(&self.printer_name); + ser.write_sized_string(&self.printer_type); + ser.write_sized_string(&self.profile_name); + ser.write_u16(self.anti_aliasing_level); + ser.write_u16(self.grey_level); + ser.write_u16(self.blur_level); + self.small_preview.serializes(ser); + ser.write_bytes(&[0xd, 0xa]); + self.big_preview.serializes(ser); + ser.write_bytes(&[0xd, 0xa]); + ser.write_u32(self.layer_count); + ser.write_u16(self.x_resolution); + ser.write_u16(self.y_resolution); + ser.write_bool(self.x_mirror); + ser.write_bool(self.y_mirror); + ser.write_f32(self.x_size); + ser.write_f32(self.y_size); + ser.write_f32(self.z_size); + ser.write_f32(self.layer_thickness); + ser.write_f32(self.exposure_time); + ser.write_bool(self.exposure_delay_mode); + ser.write_f32(self.turn_off_time); + ser.write_f32(self.bottom_before_lift_time); + ser.write_f32(self.bottom_after_lift_time); + ser.write_f32(self.bottom_after_retract_time); + ser.write_f32(self.before_lift_time); + ser.write_f32(self.after_lift_time); + ser.write_f32(self.after_retract_time); + ser.write_f32(self.bottom_exposure_time); + ser.write_u32(self.bottom_layers); + ser.write_f32(self.bottom_lift_distance); + ser.write_f32(self.bottom_lift_speed); + ser.write_f32(self.lift_distance); + ser.write_f32(self.lift_speed); + ser.write_f32(self.bottom_retract_distance); + ser.write_f32(self.bottom_retract_speed); + ser.write_f32(self.retract_distance); + ser.write_f32(self.retract_speed); + ser.write_f32(self.bottom_second_lift_distance); + ser.write_f32(self.bottom_second_lift_speed); + ser.write_f32(self.second_lift_distance); + ser.write_f32(self.second_lift_speed); + ser.write_f32(self.bottom_second_retract_distance); + ser.write_f32(self.bottom_second_retract_speed); + ser.write_f32(self.second_retract_distance); + ser.write_f32(self.second_retract_speed); + ser.write_u16(self.bottom_light_pwm); + ser.write_u16(self.light_pwm); + ser.write_bool(self.advance_mode); + ser.write_u32(self.printing_time); + ser.write_f32(self.total_volume); + ser.write_f32(self.total_weight); + ser.write_f32(self.total_price); + ser.write_sized_string(&self.price_unit); + ser.write_u32(Self::SIZE as u32); + ser.write_bool(self.grey_scale_level); + ser.write_u16(self.transition_layers); + } + + pub fn deserialize(buf: &[u8]) -> Result { + let mut des = Deserializer::new(buf); + + let version = des.read_sized_string(); + ensure!(des.read_bytes(8) == [0x07, 0x00, 0x00, 0x00, 0x44, 0x4C, 0x50, 0x00]); + let software_info = des.read_sized_string(); + let software_version = des.read_sized_string(); + let file_time = des.read_sized_string(); + let printer_name = des.read_sized_string(); + let printer_type = des.read_sized_string(); + let profile_name = des.read_sized_string(); + let anti_aliasing_level = des.read_u16(); + let grey_level = des.read_u16(); + let blur_level = des.read_u16(); + let small_preview = PreviewImage::deserializes(&mut des); + ensure!(des.read_bytes(2) == [0xd, 0xa]); + let big_preview = PreviewImage::deserializes(&mut des); + ensure!(des.read_bytes(2) == [0xd, 0xa]); + let layer_count = des.read_u32(); + let x_resolution = des.read_u16(); + let y_resolution = des.read_u16(); + let x_mirror = des.read_bool(); + let y_mirror = des.read_bool(); + let x_size = des.read_f32(); + let y_size = des.read_f32(); + let z_size = des.read_f32(); + let layer_thickness = des.read_f32(); + let exposure_time = des.read_f32(); + let exposure_delay_mode = des.read_bool(); + let turn_off_time = des.read_f32(); + let bottom_before_lift_time = des.read_f32(); + let bottom_after_lift_time = des.read_f32(); + let bottom_after_retract_time = des.read_f32(); + let before_lift_time = des.read_f32(); + let after_lift_time = des.read_f32(); + let after_retract_time = des.read_f32(); + let bottom_exposure_time = des.read_f32(); + let bottom_layers = des.read_u32(); + let bottom_lift_distance = des.read_f32(); + let bottom_lift_speed = des.read_f32(); + let lift_distance = des.read_f32(); + let lift_speed = des.read_f32(); + let bottom_retract_distance = des.read_f32(); + let bottom_retract_speed = des.read_f32(); + let retract_distance = des.read_f32(); + let retract_speed = des.read_f32(); + let bottom_second_lift_distance = des.read_f32(); + let bottom_second_lift_speed = des.read_f32(); + let second_lift_distance = des.read_f32(); + let second_lift_speed = des.read_f32(); + let bottom_second_retract_distance = des.read_f32(); + let bottom_second_retract_speed = des.read_f32(); + let second_retract_distance = des.read_f32(); + let second_retract_speed = des.read_f32(); + let bottom_light_pwm = des.read_u16(); + let light_pwm = des.read_u16(); + let advance_mode = des.read_bool(); + let printing_time = des.read_u32(); + let total_volume = des.read_f32(); + let total_weight = des.read_f32(); + let total_price = des.read_f32(); + let price_unit = des.read_sized_string(); + ensure!(des.read_u32() == Self::SIZE as u32); + let grey_scale_level = des.read_bool(); + let transition_layers = des.read_u16(); + + Ok(Self { + version, + software_info, + software_version, + file_time, + printer_name, + printer_type, + profile_name, + anti_aliasing_level, + grey_level, + blur_level, + small_preview, + big_preview, + layer_count, + x_resolution, + y_resolution, + x_mirror, + y_mirror, + x_size, + y_size, + z_size, + layer_thickness, + exposure_time, + exposure_delay_mode, + turn_off_time, + bottom_before_lift_time, + bottom_after_lift_time, + bottom_after_retract_time, + before_lift_time, + after_lift_time, + after_retract_time, + bottom_exposure_time, + bottom_layers, + bottom_lift_distance, + bottom_lift_speed, + lift_distance, + lift_speed, + bottom_retract_distance, + bottom_retract_speed, + retract_distance, + retract_speed, + bottom_second_lift_distance, + bottom_second_lift_speed, + second_lift_distance, + second_lift_speed, + bottom_second_retract_distance, + bottom_second_retract_speed, + second_retract_distance, + second_retract_speed, + bottom_light_pwm, + light_pwm, + advance_mode, + printing_time, + total_volume, + total_weight, + total_price, + price_unit, + grey_scale_level, + transition_layers, + }) + } +} + +impl Debug for HeaderInfo { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("HeaderInfo") + .field("version", &self.version) + .field("software_info", &self.software_info) + .field("software_version", &self.software_version) + .field("file_time", &self.file_time) + .field("printer_name", &self.printer_name) + .field("printer_type", &self.printer_type) + .field("profile_name", &self.profile_name) + .field("anti_aliasing_level", &self.anti_aliasing_level) + .field("grey_level", &self.grey_level) + .field("blur_level", &self.blur_level) + .field("layer_count", &self.layer_count) + .field("x_resolution", &self.x_resolution) + .field("y_resolution", &self.y_resolution) + .field("x_mirror", &self.x_mirror) + .field("y_mirror", &self.y_mirror) + .field("x_size", &self.x_size) + .field("y_size", &self.y_size) + .field("z_size", &self.z_size) + .field("layer_thickness", &self.layer_thickness) + .field("exposure_time", &self.exposure_time) + .field("exposure_delay_mode", &self.exposure_delay_mode) + .field("turn_off_time", &self.turn_off_time) + .field("bottom_before_lift_time", &self.bottom_before_lift_time) + .field("bottom_after_lift_time", &self.bottom_after_lift_time) + .field("bottom_after_retract_time", &self.bottom_after_retract_time) + .field("before_lift_time", &self.before_lift_time) + .field("after_lift_time", &self.after_lift_time) + .field("after_retract_time", &self.after_retract_time) + .field("bottom_exposure_time", &self.bottom_exposure_time) + .field("bottom_layers", &self.bottom_layers) + .field("bottom_lift_distance", &self.bottom_lift_distance) + .field("bottom_lift_speed", &self.bottom_lift_speed) + .field("lift_distance", &self.lift_distance) + .field("lift_speed", &self.lift_speed) + .field("bottom_retract_distance", &self.bottom_retract_distance) + .field("bottom_retract_speed", &self.bottom_retract_speed) + .field("retract_distance", &self.retract_distance) + .field("retract_speed", &self.retract_speed) + .field( + "bottom_second_lift_distance", + &self.bottom_second_lift_distance, + ) + .field("bottom_second_lift_speed", &self.bottom_second_lift_speed) + .field("second_lift_distance", &self.second_lift_distance) + .field("second_lift_speed", &self.second_lift_speed) + .field( + "bottom_second_retract_distance", + &self.bottom_second_retract_distance, + ) + .field( + "bottom_second_retract_speed", + &self.bottom_second_retract_speed, + ) + .field("second_retract_distance", &self.second_retract_distance) + .field("second_retract_speed", &self.second_retract_speed) + .field("bottom_light_pwm", &self.bottom_light_pwm) + .field("light_pwm", &self.light_pwm) + .field("advance_mode", &self.advance_mode) + .field("printing_time", &self.printing_time) + .field("total_volume", &self.total_volume) + .field("total_weight", &self.total_weight) + .field("total_price", &self.total_price) + .field("price_unit", &self.price_unit) + .field("grey_scale_level", &self.grey_scale_level) + .field("transition_layers", &self.transition_layers) + .finish() + } +} diff --git a/goo_format/src/layer_content.rs b/goo_format/src/layer_content.rs new file mode 100644 index 0000000..440b0a0 --- /dev/null +++ b/goo_format/src/layer_content.rs @@ -0,0 +1,109 @@ +use anyhow::{bail, ensure, Result}; + +use common::serde::{Deserializer, Serializer}; + +pub struct LayerContent { + pub pause_flag: u16, + pub pause_position_z: f32, + pub layer_position_z: f32, + pub layer_exposure_time: f32, + pub layer_off_time: f32, + pub before_lift_time: f32, + pub after_lift_time: f32, + pub after_retract_time: f32, + pub lift_distance: f32, + pub lift_speed: f32, + pub second_lift_distance: f32, + pub second_lift_speed: f32, + pub retract_distance: f32, + pub retract_speed: f32, + pub second_retract_distance: f32, + pub second_retract_speed: f32, + pub light_pwm: u16, + pub data: Vec, + pub checksum: u8, +} + +impl LayerContent { + pub fn serialize(&self, ser: &mut T) { + ser.write_u16(self.pause_flag); + ser.write_f32(self.pause_position_z); + ser.write_f32(self.layer_position_z); + ser.write_f32(self.layer_exposure_time); + ser.write_f32(self.layer_off_time); + ser.write_f32(self.before_lift_time); + ser.write_f32(self.after_lift_time); + ser.write_f32(self.after_retract_time); + ser.write_f32(self.lift_distance); + ser.write_f32(self.lift_speed); + ser.write_f32(self.second_lift_distance); + ser.write_f32(self.second_lift_speed); + ser.write_f32(self.retract_distance); + ser.write_f32(self.retract_speed); + ser.write_f32(self.second_retract_distance); + ser.write_f32(self.second_retract_speed); + ser.write_u16(self.light_pwm); + ser.write_u32(self.data.len() as u32 + 2); + ser.write_bytes(&[0x55]); + ser.write_bytes(&self.data); + ser.write_u8(calculate_checksum(&self.data)); + ser.write_bytes(&[0xd, 0xa]); + } + + pub fn deserialize(buf: &[u8]) -> Result { + let mut des = Deserializer::new(buf); + + let pause_flag = des.read_u16(); + let pause_position_z = des.read_f32(); + let layer_position_z = des.read_f32(); + let layer_exposure_time = des.read_f32(); + let layer_off_time = des.read_f32(); + let before_lift_time = des.read_f32(); + let after_lift_time = des.read_f32(); + let after_retract_time = des.read_f32(); + let lift_distance = des.read_f32(); + let lift_speed = des.read_f32(); + let second_lift_distance = des.read_f32(); + let second_lift_speed = des.read_f32(); + let retract_distance = des.read_f32(); + let retract_speed = des.read_f32(); + let second_retract_distance = des.read_f32(); + let second_retract_speed = des.read_f32(); + let light_pwm = des.read_u16(); + let data_len = des.read_u32() as usize - 2; + ensure!(des.read_u8() == 0x55); + let data = des.read_bytes(data_len); + let checksum = des.read_u8(); + ensure!(des.read_bytes(2) == [0xd, 0xa]); + + Ok(Self { + pause_flag, + pause_position_z, + layer_position_z, + layer_exposure_time, + layer_off_time, + before_lift_time, + after_lift_time, + after_retract_time, + lift_distance, + lift_speed, + second_lift_distance, + second_lift_speed, + retract_distance, + retract_speed, + second_retract_distance, + second_retract_speed, + light_pwm, + data: data.to_vec(), // ehhh its fiiiine (its not fine) + checksum, + }) + } +} + +pub fn calculate_checksum(data: &[u8]) -> u8 { + let mut out = 0u8; + for &byte in data { + out = out.wrapping_add(byte); + } + !out +} diff --git a/goo_format/src/lib.rs b/goo_format/src/lib.rs index babb025..a0a745c 100644 --- a/goo_format/src/lib.rs +++ b/goo_format/src/lib.rs @@ -1,383 +1,12 @@ -use common::serde::{Serializer, SizedString}; +mod default; +mod encoded_layer; +mod file; +mod header_info; +mod layer_content; +mod preview_image; -pub struct HeaderInfo { - pub version: SizedString<4>, - pub software_info: SizedString<32>, - pub software_version: SizedString<24>, - pub file_time: SizedString<24>, - pub printer_name: SizedString<32>, - pub printer_type: SizedString<32>, - pub profile_name: SizedString<32>, - pub anti_aliasing_level: u16, - pub grey_level: u16, - pub blur_level: u16, - pub small_preview: PreviewImage<116, 116>, - pub big_preview: PreviewImage<290, 290>, - pub layer_count: u32, - pub x_resolution: u16, - pub y_resolution: u16, - pub x_mirror: bool, - pub y_mirror: bool, - pub x_size: f32, - pub y_size: f32, - pub z_size: f32, - pub layer_thickness: f32, - pub exposure_time: f32, - pub exposure_delay_mode: bool, - pub turn_off_time: f32, - pub bottom_before_lift_time: f32, - pub bottom_after_lift_time: f32, - pub bottom_after_retract_time: f32, - pub before_lift_time: f32, - pub after_lift_time: f32, - pub after_retract_time: f32, - pub bottom_exposure_time: f32, - pub bottom_layers: u32, - pub bottom_lift_distance: f32, - pub bottom_lift_speed: f32, - pub lift_distance: f32, - pub lift_speed: f32, - pub bottom_retract_distance: f32, - pub bottom_retract_speed: f32, - pub retract_distance: f32, - pub retract_speed: f32, - pub bottom_second_lift_distance: f32, - pub bottom_second_lift_speed: f32, - pub second_lift_distance: f32, - pub second_lift_speed: f32, - pub bottom_second_retract_distance: f32, - pub bottom_second_retract_speed: f32, - pub second_retract_distance: f32, - pub second_retract_speed: f32, - pub bottom_light_pwm: u16, - pub light_pwm: u16, - pub advance_mode: bool, - pub printing_time: u32, - pub total_volume: f32, - pub total_weight: f32, - pub total_price: f32, - pub price_unit: SizedString<8>, - pub grey_scale_level: bool, - pub transition_layers: u16, -} - -pub struct LayerContent { - pub pause_flag: u16, - pub pause_position_z: f32, - pub layer_position_z: f32, - pub layer_exposure_time: f32, - pub layer_off_time: f32, - pub before_lift_time: f32, - pub after_lift_time: f32, - pub after_retract_time: f32, - pub lift_distance: f32, - pub lift_speed: f32, - pub second_lift_distance: f32, - pub second_lift_speed: f32, - pub retract_distance: f32, - pub retract_speed: f32, - pub second_retract_distance: f32, - pub second_retract_speed: f32, - pub light_pwm: u16, - pub data: Vec, -} - -pub struct PreviewImage { - data: [[u16; WIDTH]; HEIGHT], -} - -pub struct EncodedLayer { - data: Vec, - last_value: u8, -} - -impl PreviewImage { - pub const fn empty() -> Self { - Self { - data: [[0; WIDTH]; HEIGHT], - } - } - - pub fn serializes(&self, serializer: &mut Serializer) { - for row in self.data.iter() { - for pixel in row.iter() { - serializer.write_u16(*pixel); - } - } - } -} - -impl EncodedLayer { - pub fn new() -> Self { - Self { - data: Vec::new(), - last_value: 0, - } - } - - pub fn add_run(&mut self, length: u64, value: u8) { - // byte 0: aabbcccc - // a => 0: full black, 1: full white, 2: small diff, 3: large diff - // b => 0: 4 bit length, 1: 12 bit value, 2: 20 bit value, 3: 28 bit value - // c => the first 4 bits of the value - // byte 1-3: optional, the rest of the value - - let diff = value as i16 - self.last_value as i16; - let byte_0: u8 = match value { - // Full black and full white are always encoded as is - 0x00 => 0b00, - 0xFF => 0b11, - _ if !self.data.is_empty() && diff.abs() <= 15 => { - // 0babcccc - // a => 0: add diff, 1: sub diff - // b => 0: length of 1, 1: length is next byte - // c => the diff - - if length > 255 { - self.add_run(255, value); - self.add_run(length - 255, value); - return; - } - - let byte_0 = (0b10 << 6) - | (((diff > 0) as u8) << 5) - | (((length != 1) as u8) << 4) - | (diff.abs() as u8); - self.data.push(byte_0); - - if length != 1 { - self.data.push(length as u8); - } - - self.last_value = value; - return; - } - _ => 0b01, - } << 6; - - let chunk_length_size = match length { - 0x0000000..=0x000000F => 0b00, - 0x0000010..=0x0000FFF => 0b01, - 0x0001000..=0x00FFFFF => 0b10, - 0x0100000..=0xFFFFFFF => 0b11, - _ => { - self.add_run(0xFFFFFFF, value); - self.add_run(length - 0xFFFFFFF, value); - return; - } - }; - - self.data - .push(byte_0 | (chunk_length_size << 4) | (length as u8 & 0x0F)); - match chunk_length_size { - 1 => self.data.extend_from_slice(&[(length >> 4) as u8]), - 2 => self - .data - .extend_from_slice(&[(length >> 12) as u8, (length >> 4) as u8]), - 3 => self.data.extend_from_slice(&[ - (length >> 20) as u8, - (length >> 12) as u8, - (length >> 4) as u8, - ]), - _ => {} - } - - self.last_value = value; - } -} - -impl HeaderInfo { - pub const SIZE: usize = 0x2FB95; - - pub fn serialize(&self, buf: &mut [u8]) { - let mut ser = Serializer::new(buf); - ser.write_sized_string(&self.version); - ser.write_bytes(&[0x07, 0x00, 0x00, 0x00, 0x44, 0x4C, 0x50, 0x00]); - ser.write_sized_string(&self.software_info); - ser.write_sized_string(&self.software_version); - ser.write_sized_string(&self.file_time); - ser.write_sized_string(&self.printer_name); - ser.write_sized_string(&self.printer_type); - ser.write_sized_string(&self.profile_name); - ser.write_u16(self.anti_aliasing_level); - ser.write_u16(self.grey_level); - ser.write_u16(self.blur_level); - self.small_preview.serializes(&mut ser); - ser.write_bytes(&[0xd, 0xa]); - self.big_preview.serializes(&mut ser); - ser.write_bytes(&[0xd, 0xa]); - ser.write_u32(self.layer_count); - ser.write_u16(self.x_resolution); - ser.write_u16(self.y_resolution); - ser.write_bool(self.x_mirror); - ser.write_bool(self.y_mirror); - ser.write_f32(self.x_size); - ser.write_f32(self.y_size); - ser.write_f32(self.z_size); - ser.write_f32(self.layer_thickness); - ser.write_f32(self.exposure_time); - ser.write_bool(self.exposure_delay_mode); - ser.write_f32(self.turn_off_time); - ser.write_f32(self.bottom_before_lift_time); - ser.write_f32(self.bottom_after_lift_time); - ser.write_f32(self.bottom_after_retract_time); - ser.write_f32(self.before_lift_time); - ser.write_f32(self.after_lift_time); - ser.write_f32(self.after_retract_time); - ser.write_f32(self.bottom_exposure_time); - ser.write_u32(self.bottom_layers); - ser.write_f32(self.bottom_lift_distance); - ser.write_f32(self.bottom_lift_speed); - ser.write_f32(self.lift_distance); - ser.write_f32(self.lift_speed); - ser.write_f32(self.bottom_retract_distance); - ser.write_f32(self.bottom_retract_speed); - ser.write_f32(self.retract_distance); - ser.write_f32(self.retract_speed); - ser.write_f32(self.bottom_second_lift_distance); - ser.write_f32(self.bottom_second_lift_speed); - ser.write_f32(self.second_lift_distance); - ser.write_f32(self.second_lift_speed); - ser.write_f32(self.bottom_second_retract_distance); - ser.write_f32(self.bottom_second_retract_speed); - ser.write_f32(self.second_retract_distance); - ser.write_f32(self.second_retract_speed); - ser.write_u16(self.bottom_light_pwm); - ser.write_u16(self.light_pwm); - ser.write_bool(self.advance_mode); - ser.write_u32(self.printing_time); - ser.write_f32(self.total_volume); - ser.write_f32(self.total_weight); - ser.write_f32(self.total_price); - ser.write_sized_string(&self.price_unit); - ser.write_u32(Self::SIZE as u32); - ser.write_bool(self.grey_scale_level); - ser.write_u16(self.transition_layers); - } -} - -impl LayerContent { - pub fn serialize(&self, buf: &mut [u8]) { - let mut ser = Serializer::new(buf); - ser.write_u16(self.pause_flag); - ser.write_f32(self.pause_position_z); - ser.write_f32(self.layer_position_z); - ser.write_f32(self.layer_exposure_time); - ser.write_f32(self.layer_off_time); - ser.write_f32(self.before_lift_time); - ser.write_f32(self.after_lift_time); - ser.write_f32(self.after_retract_time); - ser.write_f32(self.lift_distance); - ser.write_f32(self.lift_speed); - ser.write_f32(self.second_lift_distance); - ser.write_f32(self.second_lift_speed); - ser.write_f32(self.retract_distance); - ser.write_f32(self.retract_speed); - ser.write_f32(self.second_retract_distance); - ser.write_f32(self.second_retract_speed); - ser.write_u16(self.light_pwm); - ser.write_u32(self.data.len() as u32 + 2); - ser.write_bytes(&[0x55]); - ser.write_bytes(&self.data); - ser.write_u8(self.calculate_checksum()); - ser.write_bytes(&[0xd, 0xa]); - } - - fn calculate_checksum(&self) -> u8 { - let mut out = 0u8; - for &byte in self.data.iter() { - out = out.wrapping_add(byte); - } - !out - } -} - -impl Default for HeaderInfo { - fn default() -> Self { - Self { - version: SizedString::new(b"V3.0"), - software_info: SizedString::new(b"sla_slicer by Connor Slade"), - software_version: SizedString::new(b"0.1.0"), - file_time: SizedString::new(b"2024-06-14 08:10:14"), - printer_name: SizedString::new(b"standard"), - printer_type: SizedString::new(b"Default"), - profile_name: SizedString::new(b"New Script"), - anti_aliasing_level: 8, - grey_level: 0, - blur_level: 0, - small_preview: PreviewImage::empty(), - big_preview: PreviewImage::empty(), - layer_count: 171, - x_resolution: 11520, - y_resolution: 5102, - x_mirror: false, - y_mirror: false, - x_size: 218.88, - y_size: 122.88, - z_size: 260.0, - layer_thickness: 0.05, - exposure_time: 3.0, - exposure_delay_mode: true, - turn_off_time: 0.0, - bottom_before_lift_time: 0.0, - bottom_after_lift_time: 0.0, - bottom_after_retract_time: 0.0, - before_lift_time: 0.0, - after_lift_time: 0.0, - after_retract_time: 0.0, - bottom_exposure_time: 50.0, - bottom_layers: 8, - bottom_lift_distance: 5.0, - bottom_lift_speed: 65.0, - lift_distance: 5.0, - lift_speed: 65.0, - bottom_retract_distance: 5.0, - bottom_retract_speed: 150.0, - retract_distance: 5.0, - retract_speed: 0.0, - bottom_second_lift_distance: 0.0, - bottom_second_lift_speed: 0.0, - second_lift_distance: 0.0, - second_lift_speed: 0.0, - bottom_second_retract_distance: 0.0, - bottom_second_retract_speed: 0.0, - second_retract_distance: 0.0, - second_retract_speed: 0.0, - bottom_light_pwm: 255, - light_pwm: 255, - advance_mode: false, - printing_time: 2659, - total_volume: 526.507, - total_weight: 0.684, - total_price: 0.0, - price_unit: SizedString::new(b"$"), - grey_scale_level: true, - transition_layers: 10, - } - } -} - -impl Default for LayerContent { - fn default() -> Self { - Self { - pause_flag: 0, - pause_position_z: 200.0, - layer_position_z: 0.05, - layer_exposure_time: 50.0, - layer_off_time: 0.0, - before_lift_time: 0.0, - after_lift_time: 0.0, - after_retract_time: 0.0, - lift_distance: 5.0, - lift_speed: 65.0, - second_lift_distance: 0.0, - second_lift_speed: 0.0, - retract_distance: 5.0, - retract_speed: 150.0, - second_retract_distance: 0.0, - second_retract_speed: 0.0, - light_pwm: 255, - data: Vec::new(), - } - } -} +pub use encoded_layer::{LayerDecoder, LayerEncoder, Run}; +pub use file::File; +pub use header_info::HeaderInfo; +pub use layer_content::LayerContent; +pub use preview_image::PreviewImage; diff --git a/goo_format/src/main.rs b/goo_format/src/main.rs new file mode 100644 index 0000000..9a6f02a --- /dev/null +++ b/goo_format/src/main.rs @@ -0,0 +1,65 @@ +use std::{fs, path::PathBuf}; + +use anyhow::Result; +use clap::Parser; +use goo_format::{File, LayerDecoder, Run}; +use image::RgbImage; + +#[derive(Parser)] +struct Args { + /// Path to the .goo file + input_file: PathBuf, + + /// Path to output each layer as an image + #[clap(short, long)] + layers: Option, + + /// Do not print the header information + #[clap(short, long)] + no_header: bool, +} + +fn main() -> Result<()> { + let args = Args::parse(); + + let raw_goo = fs::read(&args.input_file)?; + let goo = File::deserialize(&raw_goo)?; + + if !args.no_header { + println!("{:#?}", goo.header); + } + + if let Some(layers) = args.layers { + fs::create_dir_all(&layers)?; + + for (i, layer) in goo.layers.iter().enumerate() { + let decoder = LayerDecoder::new(&layer.data); + let mut pixel = 0; + + if layer.checksum != decoder.checksum() { + eprintln!("WARN: Checksum mismatch for layer {}", i); + } + + let path = layers.join(format!("layer_{:03}.png", i)); + let mut image = RgbImage::new( + goo.header.x_resolution as u32, + goo.header.y_resolution as u32, + ); + + for Run { length, value } in decoder { + for _ in 0..length { + let x = pixel % goo.header.x_resolution as u32; + let y = pixel / goo.header.x_resolution as u32; + + image.put_pixel(x, y, image::Rgb([value, value, value])); + + pixel += 1; + } + } + + image.save(path)?; + } + } + + Ok(()) +} diff --git a/goo_format/src/preview_image.rs b/goo_format/src/preview_image.rs new file mode 100644 index 0000000..5d36e3a --- /dev/null +++ b/goo_format/src/preview_image.rs @@ -0,0 +1,47 @@ +use common::serde::{Deserializer, Serializer}; + +pub struct PreviewImage { + // 0brrrrrggggggbbbbb + data: [[u16; WIDTH]; HEIGHT], +} + +impl PreviewImage { + pub const fn empty() -> Self { + Self { + data: [[0; WIDTH]; HEIGHT], + } + } + + pub fn serializes(&self, serializer: &mut T) { + for row in self.data.iter() { + for pixel in row.iter() { + serializer.write_u16(*pixel); + } + } + } + + pub fn deserializes(deserializer: &mut Deserializer) -> Self { + let mut data = [[0; WIDTH]; HEIGHT]; + for row in data.iter_mut() { + for pixel in row.iter_mut() { + *pixel = deserializer.read_u16(); + } + } + Self { data } + } + + pub fn set_pixel(&mut self, x: usize, y: usize, color: (f32, f32, f32)) { + let red = (color.0 * 31.0).round() as u16; + let green = (color.1 * 63.0).round() as u16; + let blue = (color.2 * 31.0).round() as u16; + self.data[y][x] = (red << 11) | (green << 5) | blue; + } + + pub fn get_pixel(&self, x: usize, y: usize) -> (f32, f32, f32) { + let pixel = self.data[y][x]; + let red = ((pixel >> 11) & 0x1F) as f32 / 31.0; + let green = ((pixel >> 5) & 0x3F) as f32 / 63.0; + let blue = (pixel & 0x1F) as f32 / 31.0; + (red, green, blue) + } +}