use std::{ fmt::Debug, fs::File, io::{Cursor, Read, Write}, path::PathBuf, }; use binrw::{BinRead, BinWrite, BinWriterExt}; use diff::Diff; use modular_bitfield::{ bitfield, specifiers::{B1, B14}, }; use crate::{ field_of::FieldOf, types::{Coordinate, Field, ObjectType, WString, F64, U16, U32}, }; use self::{ circle::Circle, ellipse::Ellipse, hatch::Hatch, line::Lines, polygon::Polygon, rectangle::Rectangle, }; pub mod circle; pub mod ellipse; pub mod hatch; pub mod line; pub mod polygon; pub mod rectangle; pub trait Translate { /// Move origin of object to point fn move_absolute(&mut self, origin: Option, z: Option); fn move_relative(&mut self, delta: Option, z: Option); } #[bitfield(bits = 16)] #[derive(BinRead, BinWrite, Copy, Clone, Debug, Default, Diff, PartialEq)] #[diff(attr( #[derive(Debug, PartialEq)] ))] #[br(map = Self::from_bytes)] #[bw(map = |&x| Self::into_bytes(x))] pub struct ObjectFlags { pub disabled: B1, pub aspect_ratio_unlocked: B1, #[skip] __: B14, } /// Core properties defined for all object types #[cfg_attr(feature = "default-debug", derive(Debug))] #[derive(BinRead, BinWrite, Clone, Diff, PartialEq)] #[diff(attr( #[derive(Debug, PartialEq)] ))] #[brw(magic(17u32))] pub struct ObjectCore { pub pen: U32, pub obj_type: FieldOf, pub flags: FieldOf, pub name: WString, pub count: U32, pub _unknown_1: Field, pub io_control_enable_mask: U16, pub io_control_disable_mask: U16, pub _unknown_2: [Field; 5], pub origin: FieldOf, pub z: F64, pub a: F64, pub index: U32, // Increments for each hatch fill line (types 1/2) or 0xFFFF_FFFF (types 3/4/5/7/8) } // Custom Debug implementation to only print known fields #[cfg(not(feature = "default-debug"))] impl Debug for ObjectCore { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("ObjectCore") .field("pen", &self.pen) .field("obj_type", &self.obj_type) .field("flags", &self.flags) .field("name", &self.name) .field("count", &self.count) .field("io_control_enable_mask", &self.io_control_enable_mask) .field("io_control_disable_mask", &self.io_control_disable_mask) .field("origin", &self.origin) .field("z", &self.z) .field("a", &self.a) .field("index", &self.index) .finish() } } impl Translate for ObjectCore { fn move_absolute(&mut self, origin: Option, z: Option) { origin.map(|origin| *self.origin = origin); z.map(|z| self.z = z.into()); } fn move_relative(&mut self, delta: Option, z: Option) { delta.map(|delta| *self.origin += delta); z.map(|z| *self.z += z); } } impl ObjectCore { pub fn default(obj_type: ObjectType) -> Self { Self { pen: 0.into(), obj_type: obj_type.into(), flags: ObjectFlags::new() .with_disabled(0) .with_aspect_ratio_unlocked(0) .into(), name: "\0".to_string().into(), count: 1.into(), _unknown_1: vec![0, 0].into(), // 0_u16 io_control_enable_mask: Default::default(), io_control_disable_mask: Default::default(), _unknown_2: [ vec![0, 0].into(), // 0_u16 vec![1, 0, 0, 0].into(), // 1_u32 vec![1, 0, 0, 0].into(), // 1_u32 vec![0, 0, 0, 0, 0, 0, 36, 64].into(), // 10.0_f64 vec![0, 0, 0, 0, 0, 0, 36, 64].into(), // 10.0_f64 ] .into(), origin: Coordinate { x: 0.0, y: 0.0 }.into(), z: 0.0.into(), a: 0.0.into(), index: 0.into(), } } } #[derive(BinRead, BinWrite, Clone, Debug, Diff, PartialEq, strum::Display)] #[diff(attr( #[derive(Debug, PartialEq)] ))] pub enum Object { #[brw(magic = 1u32)] Curve(Lines), #[brw(magic = 2u32)] Point(Lines), #[brw(magic = 3u32)] Rectangle(Rectangle), #[brw(magic = 4u32)] Circle(Circle), #[brw(magic = 5u32)] Ellipse(Ellipse), #[brw(magic = 6u32)] Polygon(Polygon), #[brw(magic = 32u32)] Hatch(Hatch), } impl Translate for Object { fn move_absolute(&mut self, origin: Option, z: Option) { match self { Object::Curve(x) => x.move_absolute(origin, z), Object::Point(x) => x.move_absolute(origin, z), Object::Rectangle(x) => x.move_absolute(origin, z), Object::Circle(x) => x.move_absolute(origin, z), Object::Ellipse(x) => x.move_absolute(origin, z), Object::Polygon(x) => x.move_absolute(origin, z), Object::Hatch(x) => x.move_absolute(origin, z), } } fn move_relative(&mut self, delta: Option, z: Option) { match self { Object::Curve(x) => x.move_relative(delta, z), Object::Point(x) => x.move_relative(delta, z), Object::Rectangle(x) => x.move_relative(delta, z), Object::Circle(x) => x.move_relative(delta, z), Object::Ellipse(x) => x.move_relative(delta, z), Object::Polygon(x) => x.move_relative(delta, z), Object::Hatch(x) => x.move_relative(delta, z), } } } impl Object { pub fn read_from_file(path: &PathBuf) -> Self { let mut input: File = File::open(path).expect("Failed to open input file"); let mut buffer: Cursor> = Cursor::new(vec![]); input .read_to_end(buffer.get_mut()) .expect("Failed to read input file"); Object::read_le(&mut buffer).expect("Failed to deserialize input as object") } pub fn write_to_file(&self, path: &PathBuf) { let mut buffer: Cursor> = Cursor::new(vec![]); buffer.write_le(self).expect("Failed to serialize object"); let mut output: File = File::create(path).expect("Failed to open output file"); output .write_all(buffer.into_inner().as_slice()) .expect("Failed to write to output file"); } pub fn set_pen(&mut self, pen: u32) { match self { Object::Curve(x) => x.core.pen = pen.into(), Object::Point(x) => x.core.pen = pen.into(), Object::Rectangle(x) => x.core.pen = pen.into(), Object::Circle(x) => x.core.pen = pen.into(), Object::Ellipse(x) => x.core.pen = pen.into(), Object::Polygon(x) => x.core.pen = pen.into(), Object::Hatch(x) => { x.core.pen = pen.into(); x.hatch_settings.iter_mut().for_each(|y| y.pen = pen.into()); x.legacy_setting.hatch_0_pen = pen.into(); x.legacy_setting.hatch_1_pen = pen.into(); x.legacy_setting.hatch_2_pen = pen.into(); } } } pub fn core(&mut self) -> &mut ObjectCore { match self { Object::Curve(x) => &mut x.core, Object::Point(x) => &mut x.core, Object::Rectangle(x) => &mut x.core, Object::Circle(x) => &mut x.core, Object::Ellipse(x) => &mut x.core, Object::Polygon(x) => &mut x.core, Object::Hatch(x) => &mut x.core, } } } #[derive(BinRead, BinWrite, Clone, Debug, Diff, PartialEq)] #[diff(attr( #[derive(Debug, PartialEq)] ))] pub struct ObjectModified { pub default: U32, // 0 if values are default, 255 otherwise pub changes: FieldOf, } impl Default for ObjectModified { fn default() -> Self { Self { default: 0.into(), changes: ScaleValues::default().into(), } } } #[derive(BinRead, BinWrite, Clone, Diff, PartialEq)] #[diff(attr( #[derive(Debug, PartialEq)] ))] pub struct ScaleValues { pub width_scale: f64, #[br(assert(_unknown_1 == 0.0))] _unknown_1: f64, #[br(assert(_unknown_2 == 0.0))] _unknown_2: f64, #[br(assert(_unknown_3 == 0.0))] _unknown_3: f64, pub height_scale: f64, #[br(assert(_unknown_4 == 0.0))] _unknown_4: f64, pub origin_x_delta: f64, pub origin_y_delta: f64, #[br(assert(_unknown_5 == 1.0))] _unknown_5: f64, } impl Default for ScaleValues { fn default() -> Self { Self { width_scale: 1.0.into(), _unknown_1: 0.0.into(), _unknown_2: 0.0.into(), _unknown_3: 0.0.into(), height_scale: 1.0.into(), _unknown_4: 0.0.into(), origin_x_delta: 0.0.into(), origin_y_delta: 0.0.into(), _unknown_5: 1.0.into(), } } } // Custom Debug implementation to only print known fields impl Debug for ScaleValues { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("ScaleValues") .field("width_scale", &self.width_scale) .field("height_scale", &self.height_scale) .field("origin_x_delta", &self.origin_x_delta) .field("origin_y_delta", &self.origin_y_delta) .finish() } }