The brief's rule is to commit reference data beside the finding so the port can be built without a disc. Nothing had ever checked that the docs' cited artifacts actually exist. doc_link_check.py walks every markdown file under docs/, resolves each relative link, and reports targets that are missing -- and separately targets that resolve to a ZERO-BYTE file, which looks fine in any listing. links resolving 1038 -> 1049 missing targets 16 -> 5 empty targets 0 -> 0 +11 resolving and -11 missing against 11 edits: the counts pair, which is the confirmation the pass did what it claimed and touched nothing else. Two of the sixteen were the evidence for the UI layout decode itself. structures/ui-rat-layout.md is what the port is built on, and its two figures -- backing "the tutorial PAUSE menu rebuilds pixel-accurately from its sprites" and "the same method reproduces the main menu" -- were written as captures/ui-layout/... from a file in structures/, one directory too shallow. The headline evidence for the decode could not be opened from its own document. Eleven links had the wrong relative depth with the target present. Each was rewritten only where exactly one candidate path resolved, so nothing was guessed; the first pass left three alone because equivalent spellings (captures/../captures/x) failed to collapse, and a second pass normalised them. Five remain genuinely absent and are left rather than invented: two point at MEMORY.md outside the repo, one at a header in the separate xenia-canary-native tree, and two name documents that were never written (weapon-datasheet-runtime.md, canary-build-verified-env-confound.md). None is port-relevant. A missing document is a different problem from a bad path and is not something a link fix should paper over.
87 lines
4.1 KiB
Markdown
87 lines
4.1 KiB
Markdown
# ✅ Entity objects ARE spawned — the vtables were never stale, the ROUTE was wrong
|
||
|
||
**Confidence: ✅ CONFIRMED.** Measured 2026-08-26 in the sylph-re container,
|
||
Canary on the retail disc, save slot 01, Stage 01 "Glasner Training Area".
|
||
|
||
## The result
|
||
|
||
`gworld.py`'s two constants resolve exactly as written:
|
||
|
||
| vtable | address | hits |
|
||
|---|---|---|
|
||
| `DEF_VTABLE` (parsed `.tbl` definition, one per unit type) | `0x820AF844` | **13** |
|
||
| `INST_VTABLE` (spawned entity instance, one per thing in the scene) | `0x820AF030` | **42** |
|
||
|
||
`name_of()` resolves every one of them:
|
||
|
||
```
|
||
# 13 definitions # 42 live instances
|
||
UN_f001_TCAF_DeltaSaber_T_Player 20 UN_e007_ADAN_Turret
|
||
UN_f001_TCAF_DeltaSaber_T 6 UN_f001_TCAF_DeltaSaber_T
|
||
UN_e007_ADAN_Turret 5 UN_e010_ADAN_Attacker_S
|
||
UN_e106_ADAN_Destroyer 2 UN_e106_ADAN_Destroyer
|
||
UN_e010_ADAN_Attacker_S 8 UN_S01_Asteroid_cmesh_0{1..4}{a,b}
|
||
UN_S01_Asteroid_cmesh_0{1..4}{a,b} 1 UN_f001_TCAF_DeltaSaber_T_Player
|
||
```
|
||
|
||
`entities2.py` reports **52 moving triples**, so these are live objects with
|
||
changing positions, not inert table images.
|
||
|
||
## Why this is not a false positive
|
||
|
||
A bit pattern can occur by chance; a *coherent roster* cannot. The evidence is
|
||
the content, not the count:
|
||
|
||
* The asteroid definitions are `UN_S01_…` and the stage actually loaded was
|
||
**S01** — the scan recovered the right stage's props without being told which
|
||
stage was running.
|
||
* The composition is internally sensible: **2** `e106` destroyers and **20**
|
||
`e007` turrets, i.e. ~10 turrets per destroyer, which matches the e106 part
|
||
breakdown in [ship placement](../../MEMORY.md) (8 hull parts + 2 turrets per
|
||
side). Six `DeltaSaber_T` = the player plus a wing.
|
||
* Exactly one object is the `_Player` variant, and exactly one definition is.
|
||
|
||
❌ Control still owed, stated rather than glossed: the title-screen arm of this
|
||
comparison was measured in *earlier processes* (0 hits at every menu state), not
|
||
in this one. The stage-specific roster is strong evidence, but a same-process
|
||
title control has not been run.
|
||
|
||
## What was actually wrong — the route, not the constants
|
||
|
||
Every run in the preceding dozen iterations navigated through **MISSION SELECT**
|
||
(which needs the cleared-stage-mask poke to offer a stage at all). Both scans
|
||
returned 0/0 there, and the constants were marked 🔴 stale.
|
||
|
||
They were not stale. [`structures/unit-struct-runtime.md`](structures/unit-struct-runtime.md)
|
||
records its own provenance — captures came from *"all six tutorials and Stage 02
|
||
loaded from **save slot 01**"* — and `launch_mission.sh` already encoded that
|
||
route: **title → LOAD GAME → slot 01 → YES → READY ROOM → TAKE OFF**.
|
||
|
||
`LOAD GAME` is the menu's **second** item. The previous iteration pressed the
|
||
**first** (NEW GAME) and read the 0/0 that followed as evidence about the
|
||
constants. One `step down` separated a dozen iterations of "stale vtable" from
|
||
this roster.
|
||
|
||
🔑 **Method note.** Two iterations in a row were spent re-deriving something the
|
||
corpus already recorded — first the provenance line, then the working script.
|
||
Read `tools/re-capture/` and the doc's own provenance *before* deriving.
|
||
|
||
## Reproduction
|
||
|
||
`launch_mission.sh fly` does the whole thing. On a slow lavapipe boot the
|
||
attract movie can outlast `SKIP_INTRO_TIMEOUT`; the emulator is `nohup`ed and
|
||
survives, so re-run the post-boot half against the live process.
|
||
|
||
⚠️ `wait_screen.sh readyroom` reports **failure on a successful run**: the cyan
|
||
mission-briefing map classifies as `menu` (see
|
||
[screen-id-briefing-misclass](screen-id-briefing-misclass.md)), so the route
|
||
looks stuck on the main menu while it is in fact three screens further on.
|
||
|
||
## Still open
|
||
|
||
* The **world-unit measurement** ([collisionset](structures/collisionset.md)) — positions
|
||
in world units against the HUD's own distance readout. This run reached the
|
||
mission but the flight HUD was not yet up (green 0.03 %, vs 1.3–1.5 % in
|
||
flight), so no distance readout was available to compare against.
|
||
* A same-process title-screen control for the scan above.
|