Strategy A: NSCR-driven BG screen
Inputs: NCGR tilebank (8Γ8 tiles) + NCLR palette + NSCR screen map. Output: 256Γ192 BG image (32Γ24 cells).
ββββββββββββ
NCLR β palette ββββββββ
ββββββββββββ β
ββββββββββββ βΌ
NCGR β tilebank ββββ ββββββββββββββββββββββ
ββββββββββββ βββΊβ compose_nscr_screenββββΊ 256Γ192 RGBA
ββββββββββββ βββΊβ for each cell: β
NSCR β screen ββββ β tile_idx (10b) β
β (32Γ24) β β palette_bank(4b) β
ββββββββββββ β hflip / vflip β
ββββββββββββββββββββββ # src/translator/_sprite_decode.py::compose_nscr_screen
for cell_y in range(24):
for cell_x in range(32):
word = nscr.cells[cell_y * 32 + cell_x]
tile = word & 0x3FF
hflip = (word >> 10) & 1
vflip = (word >> 11) & 1
bank = (word >> 12) & 0xF
tile_pixels = ncgr.tile(tile) # 8Γ8 indices
if hflip: tile_pixels = flip_x(tile_pixels)
if vflip: tile_pixels = flip_y(tile_pixels)
for py in range(8):
for px in range(8):
idx = tile_pixels[py][px]
color = nclr.bank(bank)[idx]
out[cell_y*8 + py][cell_x*8 + px] = color
Used by 1,099 NSCR-bearing containers. Examples:
buyitem.lzs shop chrome, title_pack.ofs castle
art, every map-screen overlay.
Strategy B: NCER-driven OBJ sprite
Inputs: NCGR tilebank + NCLR palette + NCER cell definition. Output: one or more sprite cells, each composed of multiple OAM "chips."
ββββββββββββ
NCLR β palette ββββββββββββ
ββββββββββββ β
ββββββββββββ βΌ
NCGR β tilebank ββββ βββββββββββββββββββββββ
ββββββββββββ ββΊβ compose_ncer_cell ββββΊ per-cell PNG
β for each OBJ: β
ββββββββββββ β (x,y,w,h,tile, β
NCER β cells + ββββΊβ palette,flip) β
β OAM list β β blit into canvasβ
ββββββββββββ βββββββββββββββββββββββ
Each cell holds obj_count OAM entries; per OBJ the
(shape, size) pair selects pixel dimensions via the
standard NDS lookup:
| size β | 0 | 1 | 2 | 3 |
|---|---|---|---|---|
| shape 00 (square) | 8Γ8 | 16Γ16 | 32Γ32 | 64Γ64 |
| shape 01 (wide) | 16Γ8 | 32Γ8 | 32Γ16 | 64Γ32 |
| shape 10 (tall) | 8Γ16 | 8Γ32 | 16Γ32 | 32Γ64 |
Used by 926 NCER-bearing containers.
Strategy C: tad widget table
Inputs: NCER cell (selected by cell_id) +
NCLR + the tad/widget-table record
(palette, sub, flags, x, y).
Output: composed UI screen at the abs_x/abs_y of each record.
The table can sit in two places:
- A
tadRESO entry inside the asset container (e.g.buyitem.lzs'stadentry). - Inside an overlay's
.rodata(e.g. ov070's table at0x0215A538for the buyitem screen).
Either way the engine walks the table, calls a sprite-constructor for each entry, and the constructor resolves the asset bytes from the content cache. See sprite pipeline for the runtime side.
50 containers ship with a static recipe: 37 with a rnan-only
table, 8 with tad, 5 with nib.
Strategy D: code-driven (no static recipe)
123 NCER-bearing containers ship without any static placement table. The composition logic is hard-coded in the controller/body overlay pair. Examples:
FUN_02174604:place(slot, x=20, y=0, w=216, h=64, type=2)FUN_02174760:place(slot, x=13, y=40, w=230, h=112, type=2)FUN_021747A8:place(slot, x=13, y=60, w=230, h=94, type=2)FUN_02177FE4: two placements at(8,64)and(8,120), w=240 h=28FUN_0217B9D8:place(slot, x=13, y=60, w=230, h=94, type=2)FUN_02181D24: looks up(x, y)from a 6-entry table at0x0218CBE6FUN_02174B80: triple-nested loop, 144 sprites in a 3Γ4Γ12 grid (buyitem catalog)
Runtime shadow-OAM
The engine never writes OAM RAM (0x07000000 / 0x07000400)
directly during scene composition. It builds a shadow OAM
in main RAM via STR/STRH at sprite-slot addresses
computed from *0x021907BC + i*0x40 + field_offset chains.
At VBlank, FUN_02185BF8 (ov137) and FUN_0207001C
(arm9) DMA the shadow buffer into hardware OAM.
OAM/VRAM transfer wrappers (all four resolve to FUN_02017D9C = DMA0):
| Address | Role | Destination |
|---|---|---|
0x020769C0 | OAM main | 0x07000000 + dst_off |
0x02076A00 | OAM sub | 0x07000400 + dst_off |
0x02076A40 | OBJ VRAM main | 0x06400000 + dst_off |
0x02076A7C | OBJ VRAM sub | 0x06600000 + dst_off |
Source
src/translator/_sprite_decode.pyβcompose_nscr_screen,compose_ncer_cell,OBJ_SIZE_LUTnotes/phaseA_ghidra/DISCOVERIES.mdβ verified placement recipesnotes/phaseA_ghidra/BUYITEM_RECIPE_CRACKED.mdβ Strategy C worked example