Save-slot portrait sheet Confirmed
The small 16×16 faces the save screen draws, and the icon a PlayStation memory-card browser shows for each Legaia save, come from one image: a single horizontal strip of sixteen tiles in the menu overlay. Each tile has its own 16-colour palette, and the tile a save gets is simply its card slot number.
At a glance
- Where
- PROT entry 899 (the menu overlay), TIM header at offset
0x1F908; overlay address0x801EE120 - Image
- 4bpp, 256 × 16 texels, uploaded to VRAM
(960, 224) - Palettes
- One 256-entry CLUT row at VRAM
(0, 1)= sixteen 16-colour palettes - Tiles
- Sixteen 16×16 tiles; fifteen portraits and one blank
- Parser
legaia_ui_formats::save_icon(finds the sheet by scanning for its two rects, not by offset)- Modding
legaia-patcher save-icon-list/save-icon-export/save-icon-replace- Used by
- save screen, memory-card block writer
The image's flag word sets a reserved bit, so a strict TIM reader rejects it; a lenient reader plus the rect fingerprint is the parse path.
The strip
Two consumers, two indices
The tile space is a character space - fifteen different faces - and its two consumers index it by different quantities.
| Consumer | Index | How |
|---|---|---|
Memory-card block icon (FUN_801E1934) | card slot | grabs the tile and its palette back out of VRAM into the save block; no modulo, no clamp - a card holds 15 blocks |
Save-screen info panel (FUN_801E08D8) | party id (0 Vahn, 1 Noa, 2 Gala) | one portrait per party member in the slot summary |
So the save the game shows as number N carries tile N - 1, checked against real cards.
Why a byte search finds the palette and not the pixels
A save block stores its icon as one contiguous 16×16 tile. The strip stores the same tile as sixteen 8-byte runs spaced 128 bytes apart. Retail never converts in software - it reads a rectangle back out of VRAM, and a rectangular read is contiguous by construction. So searching the disc for a real save's icon finds its palette verbatim in the overlay and its pixels nowhere. That asymmetry is the layout difference, not a missing asset.
All four header bytes of a save block are part of the icon's contract. A writer that stamps only the two magic bytes leaves the icon-frame descriptor as found - zero in a previously free block - and the BIOS card browser reads the block as malformed. The port stamps the whole header.
The second copy
Tiles 0, 1 and 2 also ship as three standalone 16×16 images in the boot-resident system-UI TIM-pack, already contiguous because each is its own image. Their bytes match strip tiles 0 to 2 exactly, which makes them an independent check on the de-interleave. They live in the head region of the archive that the extraction index space skips, which is why no extracted file contains them - the same +2 numbering skew the CDNAME page documents.
Tile 15 and modding
Tile 15 is blank - one flat pixel index and an all-zero palette. It is width padding that rounds the strip to a whole number of VRAM words, and no code path selects it: both index rules are bounded below 15. The patcher therefore exposes 15 slots, not 16.
Replacing one portrait is 17 same-size in-place writes - the tile's palette plus its sixteen pixel runs - so every other portrait stays byte-identical.
See also
Source of record: docs/formats/save-icon.md.