cadmpeg-asm 0.4.0

Parse and write Autodesk Shape Manager record streams.
Documentation

cadmpeg-asm

cadmpeg-asm parses Autodesk Shape Manager binary model streams. It reads BinaryFile4 and BinaryFile8 headers, frames SAB token streams, locates the boundary between solved records and construction history, and reports byte locations for payload fields. Format codecs import its asm_header and sab modules directly.

Install

cargo add cadmpeg-asm

Parse ASM headers

asm_header::has_asm_magic recognizes the 15-byte ASM BinaryFile4 and ASM BinaryFile8 prefixes. asm_header::parse returns an AsmHeader with width, save_format_version, record_count, entity_count, flags, product strings, and tolerance fields that the input provides. A missing magic returns None; a recognized but incomplete header returns an AsmHeader with unavailable fields set to None.

BinaryFile4 stores the save-format version, record count, entity count, and flags as little-endian u32 words at offsets 15, 19, 23, and 27. Its string region begins at byte 31. BinaryFile8 stores the save-format version at byte 15, the entity count as a little-endian u64 at byte 31, and flags as a little-endian u64 at byte 39. Its string region begins at byte 47. Both layouts carry three 0x07-tagged strings followed by three 0x06-tagged little-endian f64 values. The values populate product_family, product_version, save_date, scale, linear (resabs), and angular (resnor).

AsmHeader::save_format_major and save_format_minor split the encoded save-format version, where 100 * major + minor is the stored value. AsmHeader::has_history_partition reads HISTORY_PARTITION_FLAG from flags. FORMAT_REVISION_FLAGS identifies bits 1 through 7; AsmHeader::format_revision reads them. AsmHeader::unassigned_flags preserves the remaining flag bits.

asm_header::record_stream_start returns the byte after the fixed words, the three strings, and the three tolerance values. asm_header::solved_record_limit attempts to locate the history partition's first record when the header declares HISTORY_PARTITION_FLAG. It recognizes the Begin-of-ASM-History-Data preamble and the earlier delta_state boundary. asm_header::stream_ref_width returns the declared integer/reference width in bytes and uses 8 when the header is unreadable.

Frame SAB records

sab::frame(bytes, start, limit, ref_width) returns a Vec<Record> for the requested byte range. ref_width is the stream's integer and reference width, usually 4 or 8. A 0x11 tag terminates a record at subtype depth zero. frame stops at the supplied limit, at the end of bytes, or at the delta_state history boundary. sab::frame_history accepts a final history record that ends at the supplied limit without a 0x11 terminator.

An unknown tag or truncated payload returns FrameError, which stores the byte offset and a reason string. Framing preserves recognized payload tokens, record names, and record extents. A Record contains the zero-based index, the hyphen-joined name, its leading head, the retained tokens in an Arc<[Token]>, the starting offset, and the byte len including the record terminator when present.

Record names use 0x0e sub-identifiers followed by a 0x0d identifier. Those tags become Token::SubIdent and Token::Ident when they occur in a payload. Token::is_payload_ident identifies those payload identifiers. Record::chunks, Record::chunk, and Record::chunk_len index value tokens and skip them. Record::ref_at returns a non-null Token::Ref value at a chunk index; the -1 null reference produces None.

The framer maps SAB tags to Token variants as follows:

Tags Variants
0x02, 0x03, 0x04, 0x05, 0x06 Char(u8), Short(i16), Long(i64), Float(f32), Double(f64)
0x07, 0x08, 0x09, 0x12 Str(String) with one-byte, two-byte, or ref_width length prefixes
0x0a, 0x0b, 0x0c True, False, Ref(i64)
0x0f, 0x10 SubtypeOpen, SubtypeClose
0x13, 0x14, 0x16 Position([f64; 3]), Vector3([f64; 3]), Vector2([f64; 2])
0x15, 0x17 Enum(i64), Int64(i64)
payload 0x0d, 0x0e Ident(String), SubIdent(String)

Long, Ref, and Enum use ref_width. Int64 always uses eight bytes. The 0x11 record terminator is a framing control tag and has no Token variant.

Locate payload bytes

sab::payload_token_offset returns the absolute byte offset for a value token at a record's chunk index. sab::payload_token_offsets returns all absolute offsets for a selected payload tag and reports a FrameError when the record cannot be lexed. Both helpers use the same value-token indexing as Record::chunk.

sab::payload_subtype_range returns the absolute byte range inside the subtype at a chunk index when its following identifier matches the requested name. The range starts after that identifier and ends before the matching Token::SubtypeClose; nested subtype scopes remain in the range.

Frame a synthetic SAB record:

use cadmpeg_asm::sab::{frame, Token};

fn main() {
    let mut bytes = vec![0x0d, 4];
    bytes.extend_from_slice(b"body");
    bytes.push(0x0c);
    bytes.extend_from_slice(&1_i64.to_le_bytes());
    bytes.push(0x11);

    let records = frame(&bytes, 0, bytes.len(), 8).expect("valid SAB record");
    let record = &records[0];

    assert_eq!(record.name, "body");
    assert!(matches!(record.chunk(0), Some(Token::Ref(1))));
    assert_eq!(record.ref_at(0), Some(1));
}

Documentation

Requires Rust 1.88 or later. Licensed under Apache-2.0.