Files
Syplheed-Reborn/docs/re/live-unit-definitions.md
Claude (auto-RE) a836f8e9ea re: live unit definitions from a running Stage 02, and the recipe that gets there
The runtime track is unblocked. Two environment facts cost most of the effort:
audio must be --audio --apu=sdl WITH SDL_AUDIODRIVER=dummy (muting with --apu=nop
fills the log with CreateDriver failures and the guest never passes the movie),
and everything must happen inside ONE blocking foreground call because anything
outliving its call is reaped. Boot is 25s to the flight HUD once caches are warm,
which is what makes launch-and-dump fit in one call. numpy/PIL are absent so the
analysis scripts do not run; gmem.py is stdlib and does.

Result: 14 live definition objects dumped (vtable 0x820af844, 96 words each).
The +0x54 column reads as HP across three orders of magnitude and +0x30..0x38 as
Size_X/Y/Z, corroborating the earlier single-snapshot layout, and five of the
fourteen have Size_Y == Size_X exactly, supporting the inheritance default rule.
Open: no name/id field in the first 96 words, so objects are not yet matched to
their disc records.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-12 20:18:32 +00:00

3.7 KiB

Live unit definitions from a running Stage 02 (2026-08-12)

The Route-B question — what value does a field that the disc leaves defaulted actually take at runtime — needs the game running. This is the first snapshot of the parsed definition objects taken straight out of guest RAM, plus the recipe that works in the box today, because getting there was most of the work.

The recipe that works

Everything must happen inside one blocking foreground call. A process that outlives the call that started it is reaped — nohup, a harness background job and a wait-holding wrapper were all tried and all died within a minute of the turn ending (see the session-lifetime notes).

tools/re-capture/launch_mission.sh fly     # title → LOAD → slot 01 → READY ROOM → TAKE OFF
python3 tools/re-capture/gmem.py find hex:820af844 400   # live definition objects
python3 tools/re-capture/gmem.py words <va> 96           # dump each one

Two environment facts that cost an hour each:

  • Audio must be --audio --apu=sdl with SDL_AUDIODRIVER=dummy. There is no PulseAudio server in the box, so muting (--apu=nop, run-canary's default) looks like the safe choice. It is not: the log then fills with AudioSystem::RegisterClient: CreateDriver failed for index=0, the guest never gets past the movie, and the window stays black for 8+ minutes. nav_probe.sh and launch_mission.sh already pass the right pair — do not "fix" them.
  • Boot is fast once the caches are warm. The first boot took minutes; with the shader/code cache populated, launch_mission.sh reached the flight HUD in 25 s, which is what makes a launch-and-dump fit in a single call.

entities2.py, flight_probe.py and the other analysis scripts do not run here — no numpy (and no PIL for image work). gmem.py is pure stdlib and works, so the pattern is: dump raw words to disc inside the call, analyse afterwards.

What came out

14 live definition objects (vtable 0x820af844), 96 words each: captures/stage02-live-unit-definitions.txt, with the flight screenshot they were taken from.

object +0x30 +0x34 +0x38 +0x54
0xbd3b6a00 2500 3100 2600 10000
0xbd3d2d80 80 80 350 4000
0xbd3da100 10 7 29 1500
0xbd3db980 350 70 200 10000
0xbd3dbd00 10 5 20 300
0xbd3dea80 20 9 22 100
0xbd3dfc00 10 7 29 1000
0xbd3e0680 300 300 2800 3000
0xbd3e1f00 400 400 1400 25000
0xbd3e6f80 300 300 2100 10000
0xbd3ee300 200 200 2000 10000
0xbd3faa80 100 40 50 500
0xbd3fd800 600 600 3800 30000
0xbd40e200 700 700 3800 30000

This corroborates the layout solved earlier from a single in-mission snapshot (+0x054 = HP, +0x30…0x38 = Size_X/Y/Z) rather than re-proving it: the +0x54 column reads as hull values across three orders of magnitude (100 for something small, 30 000 twice for capital ships), and the sizes scale with them. It also gives independent support to the Size_Y inherits Size_X default rule — five of the fourteen have +0x30 == +0x34 exactly (300/300, 400/400, 200/200, 600/600, 700/700) while the rest differ.

Not yet resolved: which unit each object is. Nothing in the first 96 words is an obvious name pointer, so these cannot be matched to their disc records yet — and that match is what turns the snapshot into defaulted-field values. The next step is to dump further into the object (and the entity that points at it) looking for the id or name field, which is one more single-call run.