Refactor goo format

This commit is contained in:
Connor Slade
2024-06-18 21:41:47 -04:00
parent c12299baa4
commit 21700e6e6a
15 changed files with 1329 additions and 452 deletions

189
Cargo.lock generated
View File

@@ -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"

View File

@@ -1,70 +0,0 @@
pub struct Serializer<'a> {
buffer: &'a mut [u8],
offset: usize,
}
pub struct SizedString<const SIZE: usize> {
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<const SIZE: usize>(&mut self, data: &SizedString<SIZE>) {
let len = data.data.len();
self.buffer[self.offset..self.offset + len].copy_from_slice(&data.data);
self.offset += len;
}
}
impl<const SIZE: usize> SizedString<SIZE> {
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 }
}
}

View File

@@ -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<const SIZE: usize>(&mut self) -> SizedString<SIZE> {
SizedString::new(self.read_bytes(SIZE))
}
}

7
common/src/serde/mod.rs Normal file
View File

@@ -0,0 +1,7 @@
mod deserializer;
mod serializer;
mod types;
pub use deserializer::Deserializer;
pub use serializer::Serializer;
pub use types::SizedString;

View File

@@ -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<const SIZE: usize>(&mut self, data: &SizedString<SIZE>);
}
pub struct SizedSerializer<'a> {
buffer: &'a mut [u8],
offset: usize,
}
pub struct DynamicSerializer {
buffer: Vec<u8>,
}
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<u8> {
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<const SIZE: usize>(&mut self, data: &SizedString<SIZE>) {
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<const SIZE: usize>(&mut self, data: &SizedString<SIZE>) {
self.buffer.extend_from_slice(&data.data);
}
}

42
common/src/serde/types.rs Normal file
View File

@@ -0,0 +1,42 @@
use std::fmt::{self, Debug, Display};
pub struct SizedString<const SIZE: usize> {
pub(crate) data: [u8; SIZE],
}
impl<const SIZE: usize> SizedString<SIZE> {
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<const SIZE: usize> Debug for SizedString<SIZE> {
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])
))
}
}

View File

@@ -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"

94
goo_format/src/default.rs Normal file
View File

@@ -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,
}
}
}

View File

@@ -0,0 +1,185 @@
use crate::layer_content::calculate_checksum;
pub struct LayerEncoder {
data: Vec<u8>,
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<Self::Item> {
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,
})
}
}

44
goo_format/src/file.rs Normal file
View File

@@ -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<LayerContent>,
}
impl File {
pub fn new(header: HeaderInfo, layers: Vec<LayerContent>) -> Self {
Self { header, layers }
}
}
impl File {
pub fn serialize<T: Serializer>(&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<Self> {
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 })
}
}

View File

@@ -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<T: Serializer>(&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<Self> {
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()
}
}

View File

@@ -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<u8>,
pub checksum: u8,
}
impl LayerContent {
pub fn serialize<T: Serializer>(&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<Self> {
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
}

View File

@@ -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<u8>,
}
pub struct PreviewImage<const WIDTH: usize, const HEIGHT: usize> {
data: [[u16; WIDTH]; HEIGHT],
}
pub struct EncodedLayer {
data: Vec<u8>,
last_value: u8,
}
impl<const WIDTH: usize, const HEIGHT: usize> PreviewImage<WIDTH, HEIGHT> {
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;

65
goo_format/src/main.rs Normal file
View File

@@ -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<PathBuf>,
/// 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(())
}

View File

@@ -0,0 +1,47 @@
use common::serde::{Deserializer, Serializer};
pub struct PreviewImage<const WIDTH: usize, const HEIGHT: usize> {
// 0brrrrrggggggbbbbb
data: [[u16; WIDTH]; HEIGHT],
}
impl<const WIDTH: usize, const HEIGHT: usize> PreviewImage<WIDTH, HEIGHT> {
pub const fn empty() -> Self {
Self {
data: [[0; WIDTH]; HEIGHT],
}
}
pub fn serializes<T: Serializer>(&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)
}
}