Resource Forks — the .res container
Resource forks — the .res container
Section titled “Resource forks — the .res container”Every .res file in all five games is a classic Macintosh resource fork,
stored as a flat file. Melbourne House and Synergistic both developed on the
Mac and ported outward, and the resource system came along for the ride —
including to platforms that had no such concept.
The container structure is identical on every platform. The byte order is not, and neither is the data section. Amiga files are big-endian with a u32 length prefix on each resource; DOS and Apple IIGS files are little-endian with no prefix at all, and their type codes are the Amiga codes byte-reversed. Writing one parser for both is correct — but see the reference-list correction below for the field-position trap that costs you if you assume “byte order” means only “multi-byte integers”.
Layout
Section titled “Layout”┌─────────────────────────────────┐ offset 0│ File header (16 bytes) │├─────────────────────────────────┤ offset 0x10│ Data section ││ (sequence of resource blobs) │├─────────────────────────────────┤ offset = mapOffset│ Resource map ││ (type list + reference list ││ + name list) │└─────────────────────────────────┘ EOFFile header (16 bytes)
Section titled “File header (16 bytes)”+0x00 u32 dataOffset always 0x10+0x04 u32 mapOffset offset from file start to the resource map+0x08 u32 dataLength byte length of the data section+0x0C u32 mapLength byte length of the resource mapTwo invariants hold on every file checked, and are worth asserting in a
parser: dataOffset + dataLength == mapOffset, and
mapOffset + mapLength == fileSize.
Data section
Section titled “Data section”Amiga (big-endian) entries are length-prefixed — a u32 length followed by that many bytes. The reference list points at the length prefix.
DOS and Apple IIGS (little-endian) entries have no prefix; the reference list points straight at the data, and each resource’s size is the gap to the next reference entry.
Resource map
Section titled “Resource map”+0x00 16 copy of the 16-byte file header+0x10 u32 nextMapHandle always 0+0x14 u16 fileRefNum always 0+0x16 u16 fileAttributes typically 0x0003+0x18 u16 typeListOffset from map start (typically 0x1C)+0x1A u16 nameListOffset from map startType list — a u16 holding numTypes − 1, then one 8-byte entry per type:
| Offset | Size | Field | Notes |
|---|---|---|---|
+0x00 |
4 | typeCode |
FourCC — see the table below |
+0x04 |
2 | numResources − 1 |
Count, minus one |
+0x06 |
2 | refListOffset |
From the type list start, not the map start |
Reference list — 12 bytes per resource, on every platform:
| Offset | Size | Field | Notes |
|---|---|---|---|
+0x00 |
2 | resourceId |
Unique within its type |
+0x02 |
2 | nameOffset |
Into the name list; 0xFFFF = unnamed |
+0x04 |
1 | attributes |
Big-endian files only — see the correction |
+0x05 |
3 | dataOffset |
u24, from the data section start |
+0x08 |
4 | reserved | Runtime handle; always 0 on disk |
Name list — Pascal strings (u8 length, then that many characters).
Resources whose nameOffset is 0xFFFF have no name.
Type codes
Section titled “Type codes”DOS and IIGS codes are the Amiga codes byte-reversed — the native byte order
of the same 4-character ASCII constant stored in a u32.
| Amiga (BE) | DOS / IIGS (LE) | Meaning |
|---|---|---|
IMAG |
GAMI |
Bitmap image |
FRML |
LMRF |
Sprite animation frames |
MMAP |
PAMM |
Strategic map terrain data |
CSTR |
RTSC |
Null-terminated text string |
CHAR |
RAHC |
Terrain tileset |
FONT |
TNOF |
Font data |
SCEN |
NECS |
Scene / room definition |
SMUS |
— | Music score (IFF FORM SMUS) |
The little-endian reference-list correction
Section titled “The little-endian reference-list correction”The reference-entry table above — attributes at +0x04, dataOffset at
+0x05–+0x07 — is confirmed correct for big-endian (Amiga) files only.
On every little-endian file checked (Conan DOS SCENES.RES, Warriors of
Legend furn.res and sfx.res, WIME DOS ASCENE.RES) the real layout is
dataOffset as a u24 LE at +0x04–+0x06, followed by attributes at
+0x07. The whole 4-byte {attributes, dataOffset} word is byte-reversed as
a unit — not just the 3-byte offset inside it.
The parser read the big-endian field positions unconditionally, which produced per-entry offsets tens of thousands of bytes past the real data section on every LE file. That was masked by a sequential-scan fallback that ignores per-entry offsets and just walks the data section blob by blob — so most resources still came out right, and the bug survived. It only surfaced through the minority of entries whose bogus offset happened to land in bounds and look plausible, which paired the right resource id with the wrong bytes.
Two consequences worth carrying:
- Several previously-recorded “confirmed” findings on DOS-platform data were
pointing at the wrong bytes. The ids were right; the content behind them
wasn’t. The corrected findings are in
docs/legend/plan.md§ LMRF and § XFSD. - A wider audit of DOS-platform findings built on the old mis-pairing has not been done. Anything internally inconsistent in DOS Legend, WIME or Conan data should be re-verified against the corrected parser before it is trusted.
A fallback that hides a bug this cleanly is worth a second look in any parser: it turned a loud failure into a quiet, partial one.
Where the full evidence lives
Section titled “Where the full evidence lives”docs/formats/resource-fork.md in the main repository (not part of this
site) carries the byte-level spec, the correction’s full write-up, and the
parseResourceFork() implementation notes.