This repository has been archived on 2026-09-16. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
Syplheed-Reborn/docs/re/structures/ui-screen-runtime.md
Sylpheed RE agent 4134c21766 docs/re: the UI screen object, found and verified in live guest memory
The item class's vtable (0x820b30b4) is a fixed 4-byte value at offset 0 of every
instance, so gmem.py finds the objects in the running game with no debugger and
no emulator change. Seven are live on the title screen.

Their first {ptr,count,capacity} triplet identifies them outright: counts of 24,
7 and 1 — exactly GP_TITLE's title build, loading overlay and PRESS (A) BUTTON
bundle. The two-bundle composition of the title screen, which the draw capture
had inferred, is visible directly in memory.

The element array at +0x08 is 48-byte entries in declaration order, and it was
checked against the file at five positions with no misses: elements 0, 1, 6, 9
and 21 carry pivots (451,50), (449,46), (320,160), (320,180) and (309,10) as
floats, matching build 4 exactly, and elements 0/1 carry keyframe count 8, which
is their count in the file.

Stated as the reason this was done and NOT achieved: the array is in declaration
order, which the capture already proves is not the paint order, so the renderer
walks something else or sorts. Three further triplets in the object are
unidentified; the 24-entry one at +0x14 is already ruled out as an index list
(its entries are pointers, not indices). The gain is that the question is now a
data question on a structure that dumps in seconds.
2026-08-19 01:35:13 +00:00

3.8 KiB

The UI screen object at runtime — found in live guest memory

Status: CONFIRMED for the object's identity and its element array (five independent field matches against the file, on a running game). 🟡 the remaining arrays are unidentified. nothing here settles the paint order — see the "what this does not answer" section, which is the reason the search happened.

How it was found, in one step

The item class from the emulator-era splash work has vtable 0x820b30b4, and sub_823C2990 allocates its objects at 244 bytes each (ui-quad-class-foothold.md). A vtable pointer is a fixed 4-byte value at offset 0 of every instance, so the objects are findable in the live guest image without a debugger:

tools/re-capture/gmem.py find hex:820b30b4      # while the title screen is up

Seven live objects, at 0xBCD24D88, 0xBCD25188, 0xBCD25388, 0xBCD25488, 0xBCD25588, 0xBCD25688, 0xBCD26488 — spaced 0x100 apart, consistent with a 244-byte object plus allocator overhead.

What they are

Each object begins with two vtable pointers (0x820b30b4, 0x820b31a4 — multiple inheritance) and then a run of {pointer, count, capacity} triplets. The counts identify the objects immediately:

object first triplet's count which GP_TITLE build
0xBCD25188 7 builds 0/1 — the loading overlay (7 elements)
0xBCD25388 24 build 4 — the title (24 elements)
0xBCD24D88 1 builds 2/3 — the PRESS Ⓐ BUTTON bundle (1 element)

So the three bundles this project decoded statically are all resident, with the element counts the files declare — including the two-bundle composition of the title screen that the draw capture inferred.

The element array

+0x08 of the screen object is {ptr, count, capacity} pointing at an array of 48-byte entries, one per element, in declaration order. Verified field-by-file at five positions on the title build, with no misses:

index file says (build 4) live memory at +0x10/+0x14
0 ptlogo1.t32 pivot (451,50) 451.0, 50.0
1 ptlogo2.t32 pivot (449,46) 449.0, 46.0
6 pteff01.t32 pivot (320,160) 320.0, 160.0
9 ptbase2.t32 pivot (320,180) 320.0, 180.0
21 ptcopyright.t32 pivot (309,10) 309.0, 10.0

Entry layout as far as it is read:

+0x00  u32   pointer (per-element data)
+0x04  u32   0
+0x08  u32   flags        (0x10081021 on element 0)
+0x0C  u32   ?
+0x10  f32   pivot X      ← the declaration entry's pivot, as a FLOAT
+0x14  f32   pivot Y
+0x18  u32   0
+0x1C  u32   ?
+0x20  u32   pointer  }
+0x24  u32   count    }   the element's KEYFRAMES — 8/8 on elements 0 and 1,
+0x28  u32   capacity }   which is exactly their keyframe count in the file
+0x2C  u32   ?

So the pivot and the keyframe group, which this project reads out of the bundle, are present at runtime in the same order and with the same values.

What this does not answer

The paint order. The array is in declaration order, and the capture proves the screen is not painted in that order, so the renderer either walks something else or sorts. Three more {ptr,count,capacity} triplets are present in the screen object and unidentified — +0x14 (24 entries of 12 bytes, each {ptr,1,1}, pointing into a densely packed region), +0x24 (2 entries) and +0x30 (24). The +0x14 one is not an index list (its entries are pointers, not small integers), so the obvious "draw order table" reading is already out.

What is better than before: the question is now a data question on a live structure that can be dumped in seconds, rather than a code-reading exercise. The next probe is to dump +0x24 and +0x30 on the title build and see whether either is 24 entries in a non-declaration order.