← Commit history

Altium model checksum: the position-weighted sum Altium validates (wiki #22)

Colby Knox ·1fbf62eb73 ·1mo ago ·parent 5a16035
2 files changed +38−5
crates/altium-codec/src/lib.rs+19
@@ -47,6 +47,25 @@ mod tests {         }     } +    #[test]+    fn model_checksum_matches_altium_and_the_vendor_fixture() {+        // Tiny hand-computed case: [73, 2, 3] -> 73*1 + 2*1 + 3*2 = 81.+        assert_eq!(pcblib::altium_model_checksum(&[73, 2, 3]), 81);+        // The vendor fixture is a third independent ground truth: its stored+        // model checksum is 3227565272 (signed -1067402024) over the+        // decompressed STEP in Library/Models/0.+        let fixture = concat!(env!("CARGO_MANIFEST_DIR"), "/../altium-pcblib/tests/fixtures/ADS1220IRVAT.PcbLib");+        if let Ok(bytes) = std::fs::read(fixture) {+            use std::io::Read;+            let mut comp = cfb::CompoundFile::open(std::io::Cursor::new(bytes)).unwrap();+            let mut raw = Vec::new();+            comp.open_stream("/Library/Models/0").unwrap().read_to_end(&mut raw).unwrap();+            let mut dec = Vec::new();+            flate2::read::ZlibDecoder::new(&raw[..]).read_to_end(&mut dec).unwrap();+            assert_eq!(pcblib::altium_model_checksum(&dec), 3227565272u32);+        }+    }+     #[test]     fn overall_height_comes_from_the_step_z_extent() {         // 1.2 mm of z-extent must render as Altium writes it: 47.2441mil
crates/altium-codec/src/pcblib.rs+19−5
@@ -206,11 +206,7 @@ pub fn encode_pcblib_many_with_models(fps: &[Footprint], models: &[Option<Embedd     for m in models {         match m {             Some(em) => {-                let checksum = {-                    let mut h = flate2::Crc::new();-                    h.update(&em.step);-                    h.sum()-                };+                let checksum = altium_model_checksum(&em.step);                 let guid = model_guid(&em.name, &em.step);                 let height_mil = if em.overall_height_mil > 0.0 {                     em.overall_height_mil@@ -607,6 +603,24 @@ fn harvest_pad_data_block(tpl: &BTreeMap<String, Vec<u8>>) -> Result<Vec<u8>> { }  /// Create any missing parent storages, then write `path` as a stream.+/// Altium's model checksum over the DECOMPRESSED STEP bytes: byte 0 weighted+/// by 1, every later byte weighted by its offset, summed mod 2^32. Cracked by+/// feeding Altium's own model factory four controlled single-byte variants+/// and reading back the checksums it computed (adom-lbr#22); the deltas were+/// linear and position-weighted. Verified exact against three independent+/// ground-truth files (two oracle captures plus the ADS1220IRVAT vendor+/// fixture). Altium validates this on load and CULLS the mesh on mismatch,+/// so a wrong value renders bare pads. The store record prints it signed,+/// the body record unsigned; both are this one u32. (Byte 0's weight of 1 is+/// indistinguishable from a +73 constant, since every STEP starts with "I".)+pub fn altium_model_checksum(step: &[u8]) -> u32 {+    let mut sum: u32 = step.first().map(|b| *b as u32).unwrap_or(0);+    for (i, b) in step.iter().enumerate().skip(1) {+        sum = sum.wrapping_add((i as u32).wrapping_mul(*b as u32));+    }+    sum+}+ /// Overall height of a STEP body in mils, from the z-extent of its /// CARTESIAN_POINT entities (wiki part models are millimetre-unit STEP). /// Altium CULLS a body whose OVERALLHEIGHT is 0, so this is what keeps an