Browse Source

Hatch work

Kevin Lee 1 năm trước cách đây
mục cha
commit
95288f698f

+ 18 - 18
Cargo.lock

@@ -226,24 +226,6 @@ dependencies = [
  "windows-sys 0.52.0",
 ]
 
-[[package]]
-name = "ezcad_patcher"
-version = "1.0.0"
-dependencies = [
- "binrw",
- "clap",
- "clap-verbosity-flag",
- "diff-struct",
- "env_logger",
- "log",
- "modular-bitfield",
- "num",
- "rand",
- "serde",
- "serde_yaml",
- "strum",
-]
-
 [[package]]
 name = "getrandom"
 version = "0.2.11"
@@ -330,6 +312,24 @@ version = "2.6.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
 
+[[package]]
+name = "minilase"
+version = "1.0.0"
+dependencies = [
+ "binrw",
+ "clap",
+ "clap-verbosity-flag",
+ "diff-struct",
+ "env_logger",
+ "log",
+ "modular-bitfield",
+ "num",
+ "rand",
+ "serde",
+ "serde_yaml",
+ "strum",
+]
+
 [[package]]
 name = "modular-bitfield"
 version = "0.11.2"

+ 2 - 2
Cargo.toml

@@ -1,5 +1,5 @@
 [package]
-name = "ezcad_patcher"
+name = "minilase"
 version = "1.0.0"
 edition = "2021"
 
@@ -8,7 +8,7 @@ name ="ezcad"
 path = "src/lib.rs"
 
 [[bin]]
-name = "ezcad_patcher"
+name = "minilase"
 path = "src/main.rs"
 
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

+ 1 - 1
README.md

@@ -6,7 +6,7 @@ The EZCAD format as implemented in this crate was reverse engineered from sample
 ## Usage
 
 ``` text
-Usage: ezcad_patcher.exe [OPTIONS] --input <INPUT>
+Usage: minilase.exe [OPTIONS] --input <INPUT>
 
 Options:
   -i, --input <INPUT>    Input file to parse

+ 93 - 48
imhex.txt

@@ -1,5 +1,5 @@
 #pragma endian little
-#pragma pattern_limit 256000
+#pragma pattern_limit 512000
 
 #include <std/sys.pat>
 #include <std/mem.pat>
@@ -36,7 +36,9 @@ struct FieldOf<T> {
     u32 length;
     T value;
     
-    std::assert(length == sizeof(value), "Size mismatch");
+    if (length != sizeof(value)) {
+        std::error(std::format("Field size mismatch at 0x{:02X}", $));
+    }
 } [[sealed, format("format_field_of")]];
 
 fn format_field_of(FieldOf<u8> f) {
@@ -120,31 +122,36 @@ bitfield ObjectFlags {
     padding : 14;
 };
 
-enum ObjectType: u32 {
+enum ObjectType: u16 {
     Curve = 1,
     Point = 2,
     Rectangle = 3,
     Circle = 4,
     Ellipse = 5,
     Polygon = 6,
+    HatchLine = 16,
     Hatch = 32,
 };
 
 struct ObjCommon {
     u32 num_fields; // Number of fields in this struct (17)
     Field pen;
-    Field type; // Seems to always be same value as object type
+    //Field type; // Seems to always be same value as object type
+    FieldOf<ObjectType>;
     FieldOf<ObjectFlags>;
     String name;
     Field count;
-    Field unknown_2[1];
+    Field unknown_2;
     Field io_control_enable_mask;
     Field io_control_disable_mask;
     Field unknown_3[5];
     FieldOf<Point> origin;
     Field z;
     Field a;
-    Field unknown_4[1];
+    Field index; // Increments for each hatch line, -1 for certain hatch patterns
+    if (index.value != 0) {
+        std::warning(std::format("Unexpected unknown value {} at 0x{:02X}", index.value, $));
+    }
 };
 
 enum LineType : u16 {
@@ -237,47 +244,79 @@ struct HatchLine {
 using Object;
 
 bitfield HatchFlags {
-    padding : 1;
+    all_calc : 1; // Will always be set when background_pattern is set
     follow_edge_once : 1;
-    padding : 4;
+    continuous_pattern : 1; // Hatch type 4 when combined with bidirectional_pattern
+    bidirectional_pattern : 1; // Hatch type 2
+    ring_pattern : 1; // Hatch type 3
+    padding : 1;
     auto_rotate_hatch_angle : 1;
     average_distribut_line : 1;
-    padding : 2;
+    padding : 1;
+    gong_pattern : 1; // Hatch type 5 when combined with bidirectional_pattern
     cross_hatch : 1;
-    padding : 21;
+    background_pattern: 1; // Hatch type 6 (TODO, missing parsing)
+    fill_pattern : 1; // Hatch type 7 when combined with continuous_pattern and bidirectional_pattern
+    zigzag_pattern : 1; // Hatch type 8
+    padding : 18;
 };
 
+// TODO: hatch types 1/2 will set last field of ObjCommon in HatchLines to 1
+// TODO: hatch types 3/4/5/7/8 will set last field of ObjCommon in HatchLines to -1 (0xFFFFFFFF)
+
 struct HatchSettings {
     u32 num_fields; // 46
-    Field flags; // 1 = mark contour
-    Field unknown_1[1];
-    Field pen;
-    FieldOf<HatchFlags> flags_2;
-    Field edge_offset;
-    Field line_spacing;
-    Field start_offset;
-    Field end_offset;
-    Field angle;
-    Field unknown_4[9];
-    Field auto_rotate_angle;
-    Field unknown_7[10];
-    Field line_reduction;
-    Field unknown_3[2];
-    Field num_loops;
-    Field unknown_5[2];
-    Field loop_distance;
-    Field unknown_6[5];
-    Field contour_first;
-    Field count;
-    Field unknown_2[3];
+    Field mark_contour;
+    Field hatch_1_enable;
+    Field hatch_1_pen;
+    FieldOf<HatchFlags> hatch_1_flags;
+    Field hatch_1_edge_offset;
+    Field hatch_1_line_spacing;
+    Field hatch_1_start_offset;
+    Field hatch_1_end_offset;
+    Field hatch_1_angle;
+    Field hatch_2_enable;
+    Field hatch_2_pen;
+    FieldOf<HatchFlags> hatch_2_flags;
+    Field hatch_2_edge_offset;
+    Field hatch_2_line_space;
+    Field hatch_2_start_offset;
+    Field hatch_2_end_offset;
+    Field hatch_2_angle;
+    Field any_hatch_enabled; 
+    Field hatch_1_auto_rotate_angle;
+    Field hatch_2_auto_rotate_angle;
+    Field hatch_3_enable;
+    Field hatch_3_pen;
+    FieldOf<HatchFlags> hatch_3_flags;
+    Field hatch_3_edge_offset;
+    Field hatch_3_line_space;
+    Field hatch_3_start_offset;
+    Field hatch_3_end_offset;
+    Field hatch_3_angle;
+    Field hatch_3_auto_rotate_angle;
+    Field hatch_1_line_reduction;
+    Field hatch_2_line_reduction;
+    Field hatch_3_line_reduction;
+    Field hatch_1_num_loops;
+    Field hatch_2_num_loops;
+    Field hatch_3_num_loops;
+    Field hatch_1_loop_distance;
+    Field hatch_2_loop_distance;
+    Field hatch_3_loop_distance;
+    Field unknown_1[3];
+    Field contour_priority;
+    Field hatch_1_count;
+    Field hatch_2_count;
+    Field hatch_3_count;
 };
 
 struct HatchSettings2 {
     u32 num_fields; // 15
     Field count;
-    Field unknown_1[1];
+    Field enabled;
     Field pen;
-    FieldOf<HatchFlags> flags_2; // Same as HatchSettings.flags_2
+    FieldOf<HatchFlags> flags;
     Field edge_offset;
     Field line_spacing;
     Field start_offset;
@@ -287,29 +326,35 @@ struct HatchSettings2 {
     Field line_reduction;
     Field loop_distance;
     Field num_loops;
-    Field unknown_5[2];
+    Field unknown_1; // Mirror unknown_1 in settings
+    Field unknown_2;
+};
+
+struct HatchLines {
+    u32; // 16 (tag?)
+    ObjCommon;
+    u32 num_lines;
+    HatchLine lines2[num_lines];
 };
 
 struct Hatch : ObjCommon {
-    u32; // 1
-    Object outline;
+    u32 num_outlines;
+    Object outline[num_outlines];
     HatchSettings settings;
-    HatchSettings2 settings_2;
-    u32 count_5; //17
-    Field unknown_5[count_5];
-    u32; // 1
-    u32; // 16
-    u32; // 17
-    Field pen;
-    Field unknown_6[2];
-    String hatch_name;
-    Field unknown_7[13];
-    u32 num_lines;
-    HatchLine lines[num_lines];
+    Field num_settings;
+    HatchSettings2 settings_2[num_settings.value];
+    
+    // Following fields are not defined if hatch is disabled
+    if (settings.any_hatch_enabled.value == 1) {
+        ObjCommon;
+        u32 num_hatches;
+        HatchLines hatches[num_hatches];
+    }
 };
 
 struct Object {
     ObjectType type;
+    u16; // zero
     
     match (type) {
         (ObjectType::Curve): LineSet;

BIN
samples/HatchCircle2.mlp


BIN
samples/HatchRectangle.mlp


BIN
samples/HatchRectangle2.mlp


BIN
samples/HatchRectangle3.mlp


BIN
samples/HatchRectangle4.mlp


BIN
samples/HatchRectangle5.mlp


BIN
samples/HatchRectangle6.mlp


BIN
samples/HatchRectangle7.mlp


BIN
samples/HatchRectangle8.mlp


BIN
samples/HatchRectangleDisabled.mlp


BIN
samples/HatchRectangleMulti.mlp


BIN
samples/HatchRectangleMulti2.mlp


BIN
samples/HatchRectangleMulti3.mlp


BIN
samples/HatchRectangleMultiOutline.mlp


BIN
samples/HatchRectangleMultiTest.mlp


config.yml → samples/config.yml


+ 146 - 86
src/ezcad/objects/hatch.rs

@@ -1,16 +1,16 @@
 use std::fmt::Debug;
 
-use binrw::{BinRead, BinWrite};
+use binrw::{binrw, BinRead, BinWrite};
 use diff::Diff;
 use modular_bitfield::{
     bitfield,
-    specifiers::{B1, B2, B21, B4},
+    specifiers::{B1, B18},
 };
 
 use crate::{
     array_of::ArrayOf,
     field_of::FieldOf,
-    types::{Field, WString, F64, U32},
+    types::{Field, F64, U32},
 };
 
 use super::{line::Lines, Object, ObjectCore};
@@ -23,26 +23,24 @@ use super::{line::Lines, Object, ObjectCore};
 #[br(map = Self::from_bytes)]
 #[bw(map = |&x| Self::into_bytes(x))]
 pub struct ObjectFlags {
-    #[skip]
-    __: B1,
+    pub all_calc: B1,
     pub follow_edge_once: B1,
+    pub continuous_pattern: B1, // Hatch type 4 when combined with bidirectional_pattern
+    pub bidirectional_pattern: B1, // Hatch type 2
+    pub ring_pattern: B1,       // Hatch type 3
     #[skip]
-    __: B4,
+    __: B1,
     pub auto_rotate_hatch_angle: B1,
     pub average_distribute_line: B1,
     #[skip]
-    __: B2,
-    pub cross_patch: B1,
+    __: B1,
+    pub gong_pattern: B1, // Hatch type 5 when combined with bidirectional_pattern
+    pub cross_hatch: B1,
+    pub background_pattern: B1, // Hatch type 6, must set all_calc as well
+    pub fill_pattern: B1, // Hatch type 7 when combined with continuous_pattern and bidirectional_pattern
+    pub zigzag_pattern: B1, // Hatch type 8
     #[skip]
-    __: B21,
-}
-
-#[derive(BinRead, BinWrite, Debug, Diff, PartialEq)]
-#[diff(attr(
-    #[derive(Debug, PartialEq)]
-))]
-pub struct HatchLine {
-    lines: ArrayOf<Lines>,
+    __: B18,
 }
 
 #[cfg_attr(feature = "default-debug", derive(Debug))]
@@ -51,49 +49,99 @@ pub struct HatchLine {
     #[derive(Debug, PartialEq)]
 ))]
 #[brw(magic(46u32))] // Number of fields in this struct
-pub struct HatchSettings1 {
+pub struct LegacyHatchSetting {
     pub mark_contour: U32,
-    _unknown_1: Field,
-    pub pen: U32,
-    pub flags: FieldOf<ObjectFlags>,
-    pub edge_offset: F64,
-    pub line_spacing: F64,
-    pub start_offset: F64,
-    pub end_offset: F64,
-    pub angle: F64,
-    _unknown_2: [Field; 9],
-    pub auto_rotate_angle: F64,
-    _unknown_3: [Field; 10],
-    pub line_reduction: F64,
-    _unknown_4: [Field; 2],
-    pub loop_count: U32,
-    _unknown_5: [Field; 2],
-    pub loop_distance: F64,
-    _unknown_6: [Field; 5],
-    pub contour_first: U32,
-    pub count: U32,
-    _unknown_7: [Field; 3],
+    pub hatch_1_enable: U32,
+    pub hatch_1_pen: U32,
+    pub hatch_1_flags: FieldOf<ObjectFlags>,
+    pub hatch_1_edge_offset: F64,
+    pub hatch_1_line_spacing: F64,
+    pub hatch_1_start_offset: F64,
+    pub hatch_1_end_offset: F64,
+    pub hatch_1_angle: F64,
+    pub hatch_2_enable: U32,
+    pub hatch_2_pen: U32,
+    pub hatch_2_flags: FieldOf<ObjectFlags>,
+    pub hatch_2_edge_offset: F64,
+    pub hatch_2_line_spacing: F64,
+    pub hatch_2_start_offset: F64,
+    pub hatch_2_end_offset: F64,
+    pub hatch_2_angle: F64,
+    pub any_hatch_enabled: U32,
+    pub hatch_1_auto_rotate_angle: F64,
+    pub hatch_2_auto_rotate_angle: F64,
+    pub hatch_3_enable: U32,
+    pub hatch_3_pen: U32,
+    pub hatch_3_flags: FieldOf<ObjectFlags>,
+    pub hatch_3_edge_offset: F64,
+    pub hatch_3_line_spacing: F64,
+    pub hatch_3_start_offset: F64,
+    pub hatch_3_end_offset: F64,
+    pub hatch_3_angle: F64,
+    pub hatch_3_auto_rotate_angle: F64,
+    pub hatch_1_line_reduction: F64,
+    pub hatch_2_line_reduction: F64,
+    pub hatch_3_line_reduction: F64,
+    pub hatch_1_loop_count: U32,
+    pub hatch_2_loop_count: U32,
+    pub hatch_3_loop_count: U32,
+    pub hatch_1_loop_distance: F64,
+    pub hatch_2_loop_distance: F64,
+    pub hatch_3_loop_distance: F64,
+    _unknown_1: [Field; 3],
+    pub contour_priority: U32,
+    pub hatch_1_count: U32,
+    pub hatch_2_count: U32,
+    pub hatch_3_count: U32,
 }
 
 // Custom Debug implementation to only print known fields
 #[cfg(not(feature = "default-debug"))]
-impl Debug for HatchSettings1 {
+impl Debug for LegacyHatchSetting {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        f.debug_struct("HatchSettings1")
+        f.debug_struct("LegacyHatchSetting")
             .field("mark_contour", &self.mark_contour)
-            .field("pen", &self.pen)
-            .field("flags", &self.flags)
-            .field("edge_offset", &self.edge_offset)
-            .field("line_spacing", &self.line_spacing)
-            .field("start_offset", &self.start_offset)
-            .field("end_offset", &self.end_offset)
-            .field("angle", &self.angle)
-            .field("auto_rotate_angle", &self.auto_rotate_angle)
-            .field("line_reduction", &self.line_reduction)
-            .field("loop_count", &self.loop_count)
-            .field("loop_distance", &self.loop_distance)
-            .field("contour_first", &self.contour_first)
-            .field("count", &self.count)
+            .field("hatch_1_enable", &self.hatch_1_enable)
+            .field("hatch_1_pen", &self.hatch_1_pen)
+            .field("hatch_1_flags", &self.hatch_1_flags)
+            .field("hatch_1_edge_offset", &self.hatch_1_edge_offset)
+            .field("hatch_1_line_spacing", &self.hatch_1_line_spacing)
+            .field("hatch_1_start_offset", &self.hatch_1_start_offset)
+            .field("hatch_1_end_offset", &self.hatch_1_end_offset)
+            .field("hatch_1_angle", &self.hatch_1_angle)
+            .field("hatch_2_enable", &self.hatch_2_enable)
+            .field("hatch_2_pen", &self.hatch_2_pen)
+            .field("hatch_2_flags", &self.hatch_2_flags)
+            .field("hatch_2_edge_offset", &self.hatch_2_edge_offset)
+            .field("hatch_2_line_spacing", &self.hatch_2_line_spacing)
+            .field("hatch_2_start_offset", &self.hatch_2_start_offset)
+            .field("hatch_2_end_offset", &self.hatch_2_end_offset)
+            .field("hatch_2_angle", &self.hatch_2_angle)
+            .field("any_hatch_enabled", &self.any_hatch_enabled)
+            .field("hatch_1_auto_rotate_angle", &self.hatch_1_auto_rotate_angle)
+            .field("hatch_2_auto_rotate_angle", &self.hatch_2_auto_rotate_angle)
+            .field("hatch_3_enable", &self.hatch_3_enable)
+            .field("hatch_3_pen", &self.hatch_3_pen)
+            .field("hatch_3_flags", &self.hatch_3_flags)
+            .field("hatch_3_edge_offset", &self.hatch_3_edge_offset)
+            .field("hatch_3_line_spacing", &self.hatch_3_line_spacing)
+            .field("hatch_3_start_offset", &self.hatch_3_start_offset)
+            .field("hatch_3_end_offset", &self.hatch_3_end_offset)
+            .field("hatch_3_angle", &self.hatch_3_angle)
+            .field("hatch_3_auto_rotate_angle", &self.hatch_3_auto_rotate_angle)
+            .field("hatch_1_line_reduction", &self.hatch_1_line_reduction)
+            .field("hatch_2_line_reduction", &self.hatch_2_line_reduction)
+            .field("hatch_3_line_reduction", &self.hatch_3_line_reduction)
+            .field("hatch_1_loop_count", &self.hatch_1_loop_count)
+            .field("hatch_2_loop_count", &self.hatch_2_loop_count)
+            .field("hatch_3_loop_count", &self.hatch_3_loop_count)
+            .field("hatch_1_loop_distance", &self.hatch_1_loop_distance)
+            .field("hatch_2_loop_distance", &self.hatch_2_loop_distance)
+            .field("hatch_3_loop_distance", &self.hatch_3_loop_distance)
+            .field("contour_priority", &self.contour_priority)
+            .field("hatch_1_count", &self.hatch_1_count)
+            .field("hatch_2_count", &self.hatch_2_count)
+            .field("hatch_3_count", &self.hatch_3_count)
             .finish()
     }
 }
@@ -104,9 +152,9 @@ impl Debug for HatchSettings1 {
     #[derive(Debug, PartialEq)]
 ))]
 #[brw(magic(15u32))] // Number of fields in this struct
-pub struct HatchSettings2 {
+pub struct HatchSetting {
     pub count: U32,
-    _unknown_1: Field,
+    pub enabled: U32,
     pub pen: U32,
     pub flags: FieldOf<ObjectFlags>,
     pub edge_offset: F64,
@@ -118,15 +166,16 @@ pub struct HatchSettings2 {
     pub line_reduction: F64,
     pub loop_distance: F64,
     pub loop_count: U32,
-    _unknown_2: [Field; 2],
+    _unknown_1: [Field; 2],
 }
 
 // Custom Debug implementation to only print known fields
 #[cfg(not(feature = "default-debug"))]
-impl Debug for HatchSettings2 {
+impl Debug for HatchSetting {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        f.debug_struct("HatchSettings2")
+        f.debug_struct("HatchSettings")
             .field("count", &self.count)
+            .field("enabled", &self.enabled)
             .field("pen", &self.pen)
             .field("flags", &self.flags)
             .field("edge_offset", &self.edge_offset)
@@ -142,39 +191,50 @@ impl Debug for HatchSettings2 {
     }
 }
 
-#[cfg_attr(feature = "default-debug", derive(Debug))]
-#[derive(BinRead, BinWrite, Diff, PartialEq)]
+#[derive(BinRead, BinWrite, Debug, Diff, PartialEq)]
+#[diff(attr(
+    #[derive(Debug, PartialEq)]
+))]
+pub struct HatchLine {
+    pub lines: ArrayOf<Lines>,
+}
+
+#[derive(BinRead, BinWrite, Debug, Diff, PartialEq)]
+#[diff(attr(
+    #[derive(Debug, PartialEq)]
+))]
+#[brw(magic(16_u32))] // ObjectType::HatchLine
+pub struct HatchLines {
+    pub core: ObjectCore,
+    pub hatches: ArrayOf<HatchLine>,
+}
+
+#[derive(BinRead, BinWrite, Debug, Diff, PartialEq)]
+#[diff(attr(
+    #[derive(Debug, PartialEq)]
+))]
+pub struct Hatches {
+    pub core: ObjectCore,
+    pub hatches: ArrayOf<HatchLines>,
+}
+
+#[binrw]
+#[derive(Debug, Diff, PartialEq)]
 #[diff(attr(
     #[derive(Debug, PartialEq)]
 ))]
 pub struct Hatch {
     pub core: ObjectCore,
     pub outline: ArrayOf<Object>,
-    pub settings_1: HatchSettings1,
-    pub settings_2: HatchSettings2,
-    _unknown_1: ArrayOf<Field>,
-    _unknown_2: u32,
-    _unknown_3: u32,
-    _unknown_4: u32,
-    pub pen: U32,
-    _unknown_5: [Field; 2],
-    pub name: WString,
-    _unknown_6: [Field; 13],
-    pub lines: ArrayOf<HatchLine>,
-}
+    pub legacy_setting: LegacyHatchSetting,
 
-// Custom Debug implementation to only print known fields
-#[cfg(not(feature = "default-debug"))]
-impl Debug for Hatch {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        f.debug_struct("Hatch")
-            .field("core", &self.core)
-            .field("outline", &self.outline)
-            .field("settings_1", &self.settings_1)
-            .field("settings_2", &self.settings_2)
-            .field("pen", &self.pen)
-            .field("name", &self.name)
-            .field("lines", &self.lines)
-            .finish()
-    }
+    #[br(temp)]
+    #[bw(try_calc(u32::try_from(hatch_settings.len()).unwrap().try_into()))]
+    pub num_settings: U32,
+
+    #[br(count = num_settings.value)]
+    pub hatch_settings: Vec<HatchSetting>,
+
+    #[brw(if(legacy_setting.any_hatch_enabled.value == 1))]
+    pub hatches: Option<Hatches>,
 }

+ 30 - 4
src/ezcad/objects/line.rs

@@ -1,11 +1,14 @@
-use binrw::{BinRead, BinWrite};
+use std::fmt::Debug;
+
+use binrw::{binrw, BinRead, BinWrite};
 use diff::Diff;
 
 use crate::{array_of::ArrayOf, types::Point};
 
 use super::ObjectCore;
 
-#[derive(BinRead, BinWrite, Debug, Diff, PartialEq)]
+#[cfg_attr(feature = "default-debug", derive(Debug))]
+#[derive(BinRead, BinWrite, Diff, PartialEq)]
 #[diff(attr(
     #[derive(Debug, PartialEq)]
 ))]
@@ -18,11 +21,34 @@ pub enum LineType {
     Bezier { _zero: u32, points: ArrayOf<Point> },
 }
 
-#[derive(BinRead, BinWrite, Debug, Diff, PartialEq)]
+// Custom Debug implementation to only print known fields
+#[cfg(not(feature = "default-debug"))]
+impl Debug for LineType {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            Self::Point { _zero, point } => f.debug_struct("Point").field("point", point).finish(),
+            Self::Line { _zero, points } => f.debug_struct("Line").field("points", points).finish(),
+            Self::Bezier { _zero, points } => {
+                f.debug_struct("Bezier").field("points", points).finish()
+            }
+        }
+    }
+}
+
+#[binrw]
+#[derive(Debug, Diff, PartialEq)]
 #[diff(attr(
     #[derive(Debug, PartialEq)]
 ))]
 pub struct Lines {
     pub core: ObjectCore,
-    pub lines: ArrayOf<LineType, u64>,
+
+    #[br(temp)]
+    #[bw(try_calc(u32::try_from(lines.len())))]
+    pub num_lines: u32,
+
+    _unknown_1: u32,
+
+    #[br(count = num_lines)]
+    pub lines: Vec<LineType>,
 }

+ 3 - 2
src/ezcad/objects/mod.rs

@@ -58,7 +58,7 @@ pub struct ObjectCore {
     pub origin: FieldOf<Point>,
     pub z: F64,
     pub a: F64,
-    _unknown_3: Field,
+    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
@@ -76,6 +76,7 @@ impl Debug for ObjectCore {
             .field("origin", &self.origin)
             .field("z", &self.z)
             .field("a", &self.a)
+            .field("index", &self.index)
             .finish()
     }
 }
@@ -105,7 +106,7 @@ impl Default for ObjectCore {
             origin: Point { x: 0.0, y: 0.0 }.into(),
             z: 0.0.into(),
             a: 0.0.into(),
-            _unknown_3: vec![0, 0, 0, 0].into(), // 0_u32
+            index: 0.into(),
         }
     }
 }

+ 1 - 0
src/ezcad/types.rs

@@ -170,6 +170,7 @@ pub enum ObjectType {
     Circle = 4,
     Ellipse = 5,
     Polygon = 6,
+    HatchLine = 16,
     Hatch = 32,
 }
 impl Default for ObjectType {