← Commit history

Delete src/altium_schlib_write.rs

Colby Knox ·841465ffd5 ·1mo ago ·parent 6490a27
1 file changed −128
src/altium_schlib_write.rsdeleted−128
@@ -1,128 +0,0 @@-//! Native Altium `.SchLib` WRITER — pure Rust, no Altium. The inverse of the-//! reader: emits an OLE2/CFB container whose streams and records are-//! byte-structurally identical to what real Altium produces (verified by-//! re-reading with this crate's parser and by byte-diffing pin records against-//! manufacturer `.SchLib` files).-//!-//! Container: `/FileHeader` (component list) · `/Storage` (icon placeholder) ·-//! `/\x01Sh33tJ5` (the constant "7262" marker) · `/<part>` storage · `/<part>/Data`-//! (the record stream). Every stream is framed `<u16 len><u16 flags><payload>`.--use anyhow::Result;-use std::io::{Cursor, Write};--/// A schematic pin to emit, in Altium SCH internal units (1 unit = 2.54/2560 mm).-/// `x`/`y` are the pin's connection-end location; Altium Y is up. `rotation` is-/// 0/90/180/270 degrees.-pub struct WritePin {-    pub name: String,-    pub designator: String,-    pub x: i32,-    pub y: i32,-    pub length: i32,-    pub rotation: i32,-}--/// The fixed 26-byte prefix of an Altium pin record (record type `0x02`),-/// captured byte-for-byte from a real manufacturer `.SchLib`. Bytes [14] (pin-/// conglomerate / orientation), [15..17] (length), [17..19] (X) and [19..21] (Y)-/// are overwritten per pin; the rest (owner-part id, electrical defaults, the-/// trailing sentinels) carry through unchanged — which is why generated pins are-/// byte-identical to Altium's own.-const PIN_PREFIX: [u8; 26] = [-    0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,-    0x01, 0x04, 0x3a, 0x14, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,-];--fn put_rec(out: &mut Vec<u8>, flags: u16, payload: &[u8]) {-    out.extend_from_slice(&(payload.len() as u16).to_le_bytes());-    out.extend_from_slice(&flags.to_le_bytes());-    out.extend_from_slice(payload);-}--/// Altium length-prefixed (1-byte) string.-fn put_pstr(out: &mut Vec<u8>, s: &str) {-    let b = s.as_bytes();-    let n = b.len().min(255);-    out.push(n as u8);-    out.extend_from_slice(&b[..n]);-}--/// A text record (`|KEY=VAL|…`) — payload is the text plus a trailing NUL, the-/// way Altium frames its FileHeader and component header records.-fn put_text(out: &mut Vec<u8>, flags: u16, body: &str) {-    let mut p = body.as_bytes().to_vec();-    p.push(0);-    put_rec(out, flags, &p);-}--/// Deterministic 8-uppercase-letter UniqueID from an index (Altium wants a-/// stable A–Z token per primitive; exact value is cosmetic).-fn uid(i: usize) -> String {-    let mut s = String::new();-    let mut n = i + 1;-    for _ in 0..8 {-        s.push((b'A' + (n % 26) as u8) as char);-        n = n / 26 + 1;-    }-    s-}--/// Emit a native `.SchLib` for one component. `body` is the symbol body-/// rectangle (x1,y1,x2,y2) in Altium SCH units. `params` become hidden-/// `RECORD=41` parameters (Manufacturer / MPN / "LCSC Part #" / …) — exactly the-/// fields Altium surfaces in the BOM.-pub fn to_schlib_bytes(-    part: &str,-    description: &str,-    body: (i32, i32, i32, i32),-    pins: &[WritePin],-    params: &[(String, String)],-) -> Result<Vec<u8>> {-    // ----- component Data stream ------    let mut d = Vec::new();-    put_text(&mut d, 0, &format!(-        "|RECORD=1|LibReference={part}|ComponentDescription={description}|PartCount=1|DisplayModeCount=1|IndexInSheet=-1|OwnerPartId=-1|CurrentPartId=1|LibraryPath=*|SourceLibraryName=*|SheetPartFileName=*|TargetFileName=*|UniqueID={}|AreaColor=11599871|Color=128|PartIDLocked=T|AllPinCount={}",-        uid(0), pins.len()));-    for (k, (name, val)) in params.iter().enumerate() {-        put_text(&mut d, 0, &format!(-            "|RECORD=41|IndexInSheet={k}|OwnerPartId=-1|Color=8388608|FontID=1|IsHidden=T|Text={val}|Name={name}|UniqueID={}",-            uid(100 + k)));-    }-    let (lx, ly, cx, cy) = body;-    put_text(&mut d, 0, &format!(-        "|RECORD=14|IsNotAccesible=T|OwnerPartId=1|Location.X={lx}|Location.Y={ly}|Corner.X={cx}|Corner.Y={cy}|LineWidth=1|Color=128|AreaColor=11599871|IsSolid=T|UniqueID={}",-        uid(50)));-    for p in pins {-        let mut h = PIN_PREFIX.to_vec();-        h[14] = (h[14] & !0x03) | (((p.rotation.rem_euclid(360) / 90) & 0x03) as u8);-        h[15..17].copy_from_slice(&(p.length as i16).to_le_bytes());-        h[17..19].copy_from_slice(&(p.x as i16).to_le_bytes());-        h[19..21].copy_from_slice(&(p.y as i16).to_le_bytes());-        let mut pl = h;-        put_pstr(&mut pl, &p.name);-        put_pstr(&mut pl, &p.designator);-        put_pstr(&mut pl, "0");-        put_pstr(&mut pl, "0");-        put_pstr(&mut pl, "");-        put_rec(&mut d, 0x0100, &pl);-    }--    // ----- FileHeader (the component list) ------    let mut fh = Vec::new();-    put_text(&mut fh, 0, &format!(-        "|HEADER=Protel for Windows - Schematic Library Editor Binary File Version 5.0|Weight=80|MinorVersion=3|UniqueID=ADOMLBRX|FontIdCount=2|Size1=10|FontName1=Times New Roman|Size2=10|Underline2=T|FontName2=Times New Roman|UseMBCS=T|IsBOC=T|SheetStyle=9|BorderOn=T|SheetNumberSpaceSize=12|AreaColor=16317695|SnapGridOn=T|SnapGridSize=10|VisibleGridOn=T|VisibleGridSize=10|CustomX=18000|CustomY=18000|UseCustomSheet=T|ReferenceZonesOn=T|Display_Unit=0|CompCount=1|LibRef0={part}|CompDescr0={description}|PartCount0=1|AlwaysShowCD=T"));--    let mut storage = Vec::new();-    put_text(&mut storage, 0, "|HEADER=Icon storage");--    // ----- assemble the OLE2/CFB container ------    let mut comp = cfb::CompoundFile::create(Cursor::new(Vec::new()))?;-    comp.create_stream("/FileHeader")?.write_all(&fh)?;-    comp.create_stream("/Storage")?.write_all(&storage)?;-    comp.create_stream("/\u{1}Sh33tJ5")?.write_all(b"7262")?;-    comp.create_storage(&format!("/{part}"))?;-    comp.create_stream(&format!("/{part}/Data"))?.write_all(&d)?;-    comp.flush()?;-    Ok(comp.into_inner().into_inner())-}