Expand description
MAR archive reader + writer (classic Mac, Hampa Hug’s mar tool).
A .mar archive stores a single Mac item — a file or a folder tree —
with both forks, type/creator, Finder flags, and 1904-epoch dates. It is the
native format of the mar MPW/Mac tool. Layout (all fields big-endian):
[128-byte record header] [data block] [rsrc block] [comment block] ...Each record header is 128 bytes: 0 u32 magic ‘MAR\x80’ (0x4d415280) 4 u32 id: ‘file’ (file) | ‘fold’ (folder start) | ‘––’ (folder end) 8 pstr name (length byte + up to 63 Mac-Roman bytes, 64 bytes total) 72 u32 data fork size 76 u32 rsrc fork size 80 u32 comment size 84 u32 file flags (bit0 = locked) 88 u32 create date (Mac 1904) 92 u32 mod date 96 16 FInfo/DInfo: type@96 creator@100 finderFlags@104 … 124 u32 header CRC (over bytes 0..124)
A file header is followed by its data, resource, and comment blocks (each
present only when its size > 0). A block is [payload][u32 CRC][zero pad],
padded so payload + 4 + pad is a multiple of 128. A fold header is
followed by its children (more records) terminated by a ---- header.
The whole byte stream may be wrapped in LZW (Unix compress, magic 1f 9d)
or an LZ4 frame (magic 04 22 4d 18); an unwrapped archive starts directly
with the first record’s MAR\x80. We read/write the stored (unwrapped)
form; a compressed archive is detected and reported (not silently mis-read).
Both CRCs use a non-reflected CRC-32 with the Castagnoli polynomial
0x1edc6f41 (init 0, no xorout) — mar’s own checksum, distinct from the
StuffIt/Compact-Pro fork checksums.
Clean-room Rust from the format as described by the GPL-2.0 mar-0.2.0
sources (Hampa Hug); no code is copied. Forks are decoded eagerly into a
buffer and exposed through the shared StuffItArchive model (stored forks,
offsets into the returned buffer) so the existing extract / browse / CLI path
handles MAR with no special case — exactly like the loose-BinHex synthesis in
super::extract.
Functions§
- build_
archive - Build a stored MAR archive. A MAR holds a single root item: with one input
node the node becomes the root; with several, they’re wrapped in a folder
named
root_name. Forks are stored uncompressed (this tool never writes the optional LZW/LZ4 wrapper). The result round-trips throughparseand is read by themartool. - is_mar
- Detect a stored (unwrapped) MAR archive by its first record header.
Compressed wrappers are intentionally not reported here —
parsesurfaces them with a clear “not yet supported” message rather than a detection false-positive on the generic LZW/LZ4 magics. - parse
- Parse a MAR archive. Returns the decoded byte buffer (fork offsets in the
entries are absolute within it) plus the parsed directory — the same
(Vec<u8>, StuffItArchive)shape every other Mac-archive parser returns, sosuper::extracthandles it unchanged. Forks are modelled as stored (method 0) over the returned buffer; each fork’s MAR CRC-32 is verified here.