UTTORI ASM · IN DEVELOPMENT
SNES struct map
Describe SNES RAM and ROM records in a checked, reusable struct map.
The optional type property makes field encoding explicit in schema version 1:
{
"id": "Stats",
"size": 5,
"fields": [
{ "name": "hp", "offset": 0, "size": 2, "type": "dw" },
{ "name": "mp", "offset": 2, "size": 1, "type": "db" },
{ "name": "unknown", "offset": 3, "size": 2, "type": "db[2]" }
]
}
type must match size exactly. Metadata uses a literal decimal array length,
such as dw[3], rather than an assembler expression. Invalid encodings are rejected before
a generation is accepted. When type is omitted, sizes 1–4 use scalar types (db, dw, dl, dd). Larger fields become
db[size]. Add explicit types when a small field is actually an array. Synthesized gaps
always become byte arrays, even if they contain only one or two bytes.
Export generates typed zero-base layouts and addressed bindings in structs.asm. Bindings
carry count and firstindex into endstruct. Complete ROM records become dataStruct.
Neighboring records from the same binding become dataStructs rows. Every literal is read
from the immutable original ROM, including all unknown bytes and generated __gap_* fields.
ROM bindings can add sparse entryComments keyed by decimal logical index:
"entryComments": { "0": "Candy", "5": "Cup of Wishes" }
These comments appear beside their dataStruct or dataStructs rows without splitting
the table. Keys must lie within firstIndex through firstIndex + count - 1; unknown
entries can be omitted. Values must be non-empty strings, with whitespace normalized
onto one comment line. RAM bindings do not emit data rows and cannot use this property.
If a ROM record is fragmented, its entry comment appears once before the first field.
Interior labels, comments, bank cuts and region boundaries prevent record grouping. The
exporter retains the existing assert pc() == field plus raw directive path for those
records. A label or comment at a record start closes and restarts a table. These fallbacks
are required for exact placement and source navigation. Do not remove them merely to obtain
more initializer blocks. Pure rendering covers the same ROM intervals whether a record is grouped
or fragmented. Code/header conflicts reject the candidate. Pure generations must rebuild the
original ROM and preserve accepted code unless explicit withdrawals are declared.
Edit struct-map.json, declare it in the generation recipes and prepare/preview a new candidate.
Commit a reviewed checkpoint and integrate it with the native Git merge. structs.asm and bank
sources are ordinary editable inputs. Keep original-identity record descriptions in the binding's
entryComments and research files; the renderer preserves record-entry comments beside accepted
includeAsset replacements as well as dataStruct rows. It does not rewrite evidence to enable grouping.