At a glance

What it is
Extraction PROT 1221 / 1222, each exactly 0x28000 bytes of raw BGR555 with no TIM header
Shape
One 320 × 256 image, uploaded as four 320 × 64 bands to VRAM (384, 0)
Who reads it
FUN_801F6B24 in the PROT 0978 field_back_read overlay, at slot-B base 0x801F69D8
Which entry
Chosen at load time from the party leader's live HP - below
Parser
legaia_asset::ringside_still

The behavioural side - what the stills are for, which module owns them and what else is in that bundle - is on the minigame-muscle-dome.md.

Layout: four bands, no remainder

A band is 320 × 64 × 2 = 0xA000 bytes, which is also 20 sectors exactly. Those are two independent statements of the same size and they agree, which is most of the reason to believe the shape at all. Four bands tile the entry with nothing left over, and pixels run row-major inside a band, so the entry is one continuous 320 × 256 picture cut into four horizontal strips.

entry file (0x28000 B) +0x00000 band 0 +0x0A000 band 1 +0x14000 band 2 +0x1E000 band 3 LoadImage, 20 sectors each VRAM, x = 384 y = 0 y = 64 y = 128 y = 192
Only rect.y changes between the four uploads; x, w and h are written once.
FieldValueWhere it comes from
width320 (0x140)rect.w immediate
height256 = 4 × 64four uploads of rect.h = 0x40
VRAM x384 (0x180)rect.x immediate
VRAM y0, 64, 128, 192the per-band rect.y store
pixelBGR555, STP clearthe residue classifier's bgr555 test

What names the rectangle

The rect lives at 0x801F735C and is built by one arm of FUN_801F6B24. Three of its four halfwords are written once and never touched again:

801f6be4  li   v0,0x180         ; rect.x = 384
801f6bec  sh   v0,0x735c(at)
801f6bf0  li   v0,0x140         ; rect.w = 320
801f6bf8  sh   v0,0x7360(at)
801f6bfc  li   v0,0x40          ; rect.h = 64
801f6c04  sh   v0,0x7362(at)
801f6c20  sh   zero,0x735e(at)  ; rect.y = 0, then 0x40 / 0x80 / 0xC0

The read is FUN_8003E964(sector, 0) seeking 0 / 0x14 / 0x28 / 0x3C, then FUN_8003E800(dst, 0x14, 1) for 20 sectors; the upload is FUN_800583C8, which is LoadImage - it hands the literal at 0x800156D4 to the debug hook before the libgpu vtable call.

Those arms are one of two jump tables in the same routine. A beqz on the special-battle word _DAT_8007BAC0 at 0x801F6BA8 picks between them: non-zero takes the 12-arm panel-still table at 0x801F6AA8 (these stills), zero takes the 19-arm field-restore table at 0x801F6AD8, which uploads the field party's texture pages instead. So an ordinary battle teardown never reaches this rectangle - it reads a different table.

Which entry is which

The PROT index is computed, not a literal, which is why a sweep for literal references finds no loader for either entry:

801f6b90  lhu  v0,0x4824(v0)    ; party slot 0 hp_max_record  (record +0x11C)
801f6b98  lhu  v1,0x480e(v1)    ; party slot 0 hp_curr_live   (record +0x106)
801f6ba4  srl  v0,v0,0x1
801f6bac  sltu s0,v1,v0         ; s0 = current HP < max / 2
801f6c3c  addiu a0,s0,0x4c7     ; raw TOC 0x4C7 + s0
801f6c40  jal  0x8003e8a8       ; the LBA resolver

Raw TOC 0x4C7 / 0x4C8 are extraction 1221 / 1222 under the +2 correction, and the same s0 picks between the two dev-path strings on the dev-file branch - which is what ties each index to a filename. 1221 is int.tim, 1222 is int2.tim, and int2.tim is the below-half-HP variant. The record offsets are the live ones from the per-character save record.

Two details there are the kind a backward-only or literal-only scan loses: the index is formed by addiu in the jal's delay slot, and the comparison reads the character record through lhu at a gp-free absolute pair rather than through any table.

What draws it, and when

The still is loaded at a battle's end and drawn a screen later, by the Muscle Dome's contest hub: two textured quads that address it by texture page (0x106 and 0x109, VRAM x 384 and 576) rather than by pixel column, one 320x240 image split at x = 192. The hub draws them only when it is re-entered after a finished leg, as the backdrop of the INTERVAL score tally and the ROUND card that follows - the first visit draws a different backdrop. Its brightness is the hub's backdrop fade level, which climbs to full with the INTERVAL heading, dims to half while the tally rolls, returns to full, and fades out with the ROUND card. Checkpoints from a dome contest played through three hub visits show both packets in the frame's primitive pool on the second and third visits, at the level the first of those arms had reached.

In the port both play hosts draw it the same way, from one set of engine kernels: the pick at the leg's end, the fade envelope, the two quads, and the sheet assembled band by band at the loader's own rectangles. The standalone minigames page does not.

Why it has no magic

Nothing in the entry identifies it. Length is the only structural statement it makes, and 0x28000 is not distinctive on its own, so legaia_asset::ringside_still::has_still_shape is a length test and the selection is by PROT index. That is not a shortcut around a detector - there is nothing to detect. A still and any other raw 16bpp VRAM region are the same bytes, and the only thing that distinguishes them is which code uploads them where.

See also