Variant of RESO that
stores named TOC entries instead of type tags. Each entry is 48 bytes β
that's what distinguishes RESC v3.11 from RESO v1.21 (which uses 16-byte
entries for variant 0). The format is fully reverse-engineered
and round-trip verified as of step-294 (2026-05-20); the
canonical spec for the writer lives at
notes/format_resc_v311.md in the translation repo.
Where RESC v3.11 appears
data/recipe/recipedata.lzsβ the cooking recipe database. 34 entries spanning 10 category labels, 11 recipe names, and 13 "Mysterious Soup" placeholder slots. This is the canonical RESC v3.11 surface and the one the v2.4 recipe-menu translation work targeted.data/mailorder.lzs,data/goodslist.lzsβ RESC v3.11 graphics containers (per step-305 UI container audit). No translatable text; they hold tile / palette / cell data for the mail-order and goods-list UIs.- Other one-off RESC v3.11 containers may appear in the asset tree; the writer in
src/archive/_resc_v311_writer.pyhandles them with the same round-trip-safe path used forrecipedata.lzs.
Outer wrapper (recipedata.lzs only)
On disk, recipedata.lzs is the SZL outer wrapper
(1.00 magic + 9 zero bytes) followed by a 4-byte file tag
and an LZ11 stream. The file tag at SZL +0x0C..+0x10 is
file-specific for recipedata.lzs:
b'\x00ser' (NOT the default b'sfo' that
lzs.compress() emits). The writer preserves the original
4-byte wrapper word byte-for-byte. Other RESC v3.11 containers carry
their own file-specific tags.
RESC v3.11 header (16 bytes)
| Offset | Size | Type | Field | Value |
|---|---|---|---|---|
0x00 | 4 | char[4] | magic | "RESC" |
0x04 | 4 | u32 LE | file_count | Number of TOC entries (recipedata = 2) |
0x08 | 4 | char[4] | version | "3.11" |
0x0C | 4 | u32 LE | variant | recipedata = 0 ("type-tagged"). Variant 1 ("simple") is theoretically possible by analogy with RESO but not observed in this ROM. |
TOC entry (48 bytes, variant 0)
Stride is 0x30 (48 bytes) β twice the entry size of
RESO variant 0. Begins at file offset 0x10.
| Offset | Size | Field | Notes |
|---|---|---|---|
+0x00 | 16 | type_name | UTF-8 name, byte-reversed within each 8-byte word. recipedata entry 0 = "recipelist", entry 1 = "recipepos". |
+0x10 | 4 | unk | Observed zero in all entries. |
+0x14 | 4 | subtype_tag | 4-byte ASCII reversed (same encoding as RESO type_tag). Entry 0 stores b"tlcr" (reads as "rclt"); entry 1 stores b"\x00tad" (reads as "dat\x00"). |
+0x18 | 4 | length | Payload bytes. |
+0x1C | 4 | data_offset | Absolute offset within the decompressed file. |
+0x20 | 12 | (padding) | Zero. |
+0x2C | 4 | fingerprint | 0x7C952298 in both recipedata entries β looks like a build-tool watermark, not load-bearing. The writer preserves it verbatim. |
type_name byte-reversal convention
The 16-byte type_name field stores the human-readable name
in 8-byte words, byte-reversed within each word. For "recipelist": word
0 holds "recipeli" stored as
69 6c 65 70 69 63 65 72; word 1 holds "st"
plus zero padding, stored as 00 00 00 00 00 00 74 73.
Reverse each word and you get the human-readable name. The writer is
robust to this only at decode time β it doesn't need to write new
type_name values because they're per-entry-type and unchanged across
patches.
recipedata.lzs payload layout
Two entries: entry 0 "recipelist" is the recipe-menu UI strings (890 bytes), and entry 1 "recipepos" is binary recipe metadata (1,260 bytes β item IDs, quantities, slot positions; no translatable text).
Entry 0 "recipelist" β string table
+0x00 u32 record_count = 34
+0x04 N records of stride 12:
+0x00 u32 text_offset (relative to start of entry-0 payload)
+0x04 u8 recipe_id
+0x05 u8 sub_id
+0x06 u8 category_code
+0x07 u8 subtype_code
+0x08 u8 flag
+0x09 u8 unk (always 0xff)
+0x0a u16 pad (always 0)
+0x04 + N*12 ... UTF-16-LE string blob, each string NUL-terminated
34 records. Strings are heavily deduplicated: records
21..33 (recipe_ids 0x64..0x70 β the 13 hidden Mysterious Soup
placeholder slots) all share one string offset at
0x36a = "γ΅γγγͺγΉγΌγ". The writer preserves
deduplication: when two output strings are byte-identical, only one copy
is emitted in the blob and both records point at it.
Entry 1 "recipepos" β binary metadata
1,260 bytes of pure binary recipe metadata β item_id refs, quantities, slot positions, ingredient combinations. No translatable strings. The writer copies this entry's bytes verbatim.
Alignment / padding
Entry-0 payload ends at entry0_off + entry0_len. Entry-1
payload starts at the next 0x10-byte-aligned offset. After entry-1's
payload ends, the decompressed file is padded to the next 0x10-byte
boundary with zeros. For recipedata.lzs: entry-0 ends at
0x3EA, entry-1 starts at 0x3F0 (6 zero bytes
of pad); entry-1 ends at 0x8DC, file ends at
0x8E0 (4 bytes of trailing pad). Both alignments are
enforced by the writer.
Round-trip safety
Calling the writer with an empty substitution map produces a
decompressed output that is byte-identical to the input β verified in
the writer's self-test (step-294). When substitutions are made, the
string blob is rebuilt with deduplication preserved, each entry-0
record's text_offset is recomputed, entry-1's bytes pass
through unchanged, and the final file is re-padded to 0x10 alignment.
Source
notes/format_resc_v311.mdβ canonical writer spec (step-294, 2026-05-20).src/archive/_resc_v311_writer.pyβ round-trip-safe writer.notes/_recipes_format_notes.mdβ earlier partial reverse-engineering notes (kept for historical context).src/archive/lzs.pyβ the SZL + LZ11 wrapper handler.