re: the base-solver -- 277 name-block loaders indexed, and the analog block is SOLVED
New reusable tool, tools/re-capture/name_block_bases.py -> docs/re/data/name-block-bases.txt (2880 lines, ~65 s, byte-identical across two runs). A loader that reads a table by field name keeps one base pointer and emits "addi rX, rBASE, -N" per name, so no static xref sees the strings. Solve the base from the DISPLACEMENT SET alone: every (string address, displacement) pair implies a candidate base, and the true base collects a vote from every name it explains, so it wins outright. My first cut took candidates from ONE displacement and scored the unit loader at 52/226 against the right answer's 217/226 -- vote over the whole set, not a probe. Control passes with no prior knowledge: the tool recovers sub_82341A20 -> r30 = 0x82088F94 at 217/226, and independently recovers sub_8230D1F8 (129/132), sub_822F9498 (90/91) and sub_822AE628 (81/108). 277 name-block-reading functions image-wide, with the schema each names. The analog block is SOLVED: sub_821A6CF0, r29 = 0x820A1630, 22/24. In code order it names ControlTweakName, YawMagForNormal, the 12 Tweak fields, the 8 AnalogRevice_* curves and GP_MAIN_GAME -- the whole schema in the object's own order plus its pak. r29 is built at 0x821A6D34 as addi r29, r11, 5680 = 0x820A0000 + 5680, matching the solved base exactly. It is the same function that reads PlayerParams. Two of my own verdicts withdrawn: "referenced by nothing" and the softened "not found by these routes". The measurements behind them were right; the conclusions were wrong. The base was solvable from the data the whole time. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PMRJjbxLqZtsb5Vb7KunPE
This commit is contained in:
@@ -5039,3 +5039,46 @@ in `player-tuning-tables.md`; runtime watch still the cheap way in.
|
||||
|
||||
🟡 Not settled: which table governs the measured player speeds (unchanged); a
|
||||
base-tracking sweep strong enough to follow register-passed bases was not built.
|
||||
|
||||
---
|
||||
|
||||
## ✅✅ 2026-08-27 — the base-solver: 277 name-block loaders, and the analog block is SOLVED
|
||||
|
||||
Item (b). New regenerator `tools/re-capture/name_block_bases.py` →
|
||||
`docs/re/data/name-block-bases.txt` (2 880 lines, ~65 s, byte-identical across
|
||||
two runs). **This is a reusable tool, not a one-off.**
|
||||
|
||||
🔑 **The method.** A loader that reads a table by field name keeps one base
|
||||
pointer and emits `addi rX, rBASE, -N` per name, so no static xref sees the
|
||||
strings. Solve the base from the **displacement set alone**: every (string
|
||||
address, displacement) pair implies a candidate base, and the true base collects
|
||||
a vote from **every name it explains**, so it wins outright.
|
||||
⚠️ My first cut took candidates from ONE displacement and scored the unit loader
|
||||
at **52/226** against the right answer's **217/226** — a plausible wrong base.
|
||||
**Vote over the whole set, not a probe.**
|
||||
|
||||
✅ **Control passes with no prior knowledge:** the tool recovers
|
||||
`sub_82341A20 → r30 = 0x82088F94`, **217/226**. It also independently recovers
|
||||
`sub_8230D1F8` (129/132), `sub_822F9498` (90/91) and `sub_822AE628` (81/108),
|
||||
and reports **277** name-block-reading functions image-wide with the schema each
|
||||
names — an index the corpus did not have.
|
||||
|
||||
✅✅ **The analog block is SOLVED: `sub_821A6CF0`, `r29 = 0x820A1630`, 22/24.**
|
||||
In code order it names `ControlTweakName`, `YawMagForNormal`, the 12 `Tweak`
|
||||
fields, the 8 `AnalogRevice_*` curves and `GP_MAIN_GAME` — the whole schema in
|
||||
the object's own order, plus its pak. `r29` is built at `0x821A6D34` as
|
||||
`addi r29, r11, 5680` = `0x820A0000 + 5680`, matching the solved base exactly.
|
||||
|
||||
🔑 **It is the same function that reads `PlayerParams`** (it references that
|
||||
literal and calls `sub_822F9498`): one function loads the player parameter object
|
||||
*and* the control-tweak/analog table.
|
||||
|
||||
🔴 **Two of my own verdicts withdrawn.** "Referenced by nothing" (🔴) and the
|
||||
softened "not found by these routes" (🟡) were both wrong; the measurements
|
||||
behind them — 0/28 xrefs, 0 hits from naive base tracking — were right. **The
|
||||
base was solvable from the data the whole time.** ⚠️ `r1` groups are the tool's
|
||||
noise floor (stack frames) and are excluded.
|
||||
|
||||
🟡 Not settled: how the 11 curve samples are *applied* (the loader names the
|
||||
records; the interpolation is downstream); the remaining 255 of the 277 solved
|
||||
functions are listed but unexamined; what selects `Booster` (unchanged).
|
||||
|
||||
@@ -103,7 +103,7 @@ files, which is how the same ground got covered twice.
|
||||
| [`structures/stage-settings-table.md`](structures/stage-settings-table.md) | The per-stage settings object — Camera rigs, Player limits, Difficulty_* damage record | ✅ CONFIRMED, 24 settings objects vs 29 resource objects both using Phase_1/2/3; Camera is 13/14 fields constant; GravityFactor non-zero in 4 stages; IsBoss16Enable identifies one object; the Phase block is 94 fields of which 68 never vary between phases; all 24 objects named StageParameter_S<NN> |
|
||||
| [`structures/isl-mission-timer.md`](structures/isl-mission-timer.md) | The mission timer — two sequential countdowns, not a limit and a warning | ✅ CONFIRMED — read from `sub_822639B8`; the threshold reading is |
|
||||
| [`structures/archive-naming.md`](structures/archive-naming.md) | Which archives the disc can name — 100% for menu paks, **0.0%** for the six 2D paks and `GP_READY_ROOM` | ✅ CONFIRMED — 6027 names × 16 prefixes, bimodal; unnamed entries split by content — three UI paks have ZERO unnamed IDXD (artwork only), DefTables is the only real data gap (1295 LOD/motion tables); **route 2 = the `Enumerate` declaration tables** closes DefTables to 1413/1425 IDXD (99.2 %), 12 left; XPR2 manifests refuted as a naming source |
|
||||
| [`structures/player-tuning-tables.md`](structures/player-tuning-tables.md) | **`PlayerParams`** — the player parameter table: `Booster` flight model, special attacks, 8 analog curves, the S16 boss | ✅✅ `sub_822F9498` is `PlayerParams`'s loader, NOT the unit loader `sub_82341A20` (90/90 strings); all 5 player craft fly identically, `Booster` differs on 10 fields; 🟡 nothing selects it, and the analog block has no xref — but 0-xref does not mean no reader |
|
||||
| [`structures/player-tuning-tables.md`](structures/player-tuning-tables.md) | **`PlayerParams`** — the player parameter table: `Booster` flight model, special attacks, 8 analog curves, the S16 boss | ✅✅ `sub_822F9498` is `PlayerParams`'s loader, not the unit loader; **the analog block's reader SOLVED — `sub_821A6CF0`, base `0x820A1630`**, via the new base-solver (277 name-block loaders image-wide, control 217/226 on `sub_82341A20`); 🟡 nothing selects `Booster` |
|
||||
| [`structures/hud-config.md`](structures/hud-config.md) | The in-game HUD configuration — 16 records, 419 asset paths, and a per-stage `ResourceTable` | ✅ CONFIRMED — 0 of 419 config paths resolve as pak entries |
|
||||
| [`structures/mission-script-manifest.md`](structures/mission-script-manifest.md) | `Stage\script.tbl`'s 11 non-`MISSION` fields, and the mission dialogue table they name | ✅ CONFIRMED — 1 of 7 values resolves (`message\MissionDialogMessage.tbl`, 200 records), controls in the same sweep |
|
||||
| [`structures/stage-numbering-and-player-craft.md`](structures/stage-numbering-and-player-craft.md) | The disc's stage numbering (S01–S16, S18–S29) and the player's craft per stage | ✅✅ CONFIRMED — two independent files close on 6 + 15 + 1 + 6 = 28 |
|
||||
|
||||
2880
docs/re/data/name-block-bases.txt
Normal file
2880
docs/re/data/name-block-bases.txt
Normal file
File diff suppressed because it is too large
Load Diff
@@ -135,35 +135,50 @@ unit `Maneuver` 100 / 350 / 1 200 at a flat ~1.25 and misses `Booster`'s
|
||||
runtime one:** watch which of the two constant sets reaches the live flight
|
||||
struct, and re-measure with the afterburner held.
|
||||
|
||||
## 🟡 The analog-curve block is referenced by nothing *that I can see* — verdict softened
|
||||
## ✅✅ SOLVED — `sub_821A6CF0` reads the analog block, found by solving the base
|
||||
|
||||
The `AnalogRevice_*` / `Tweak` schema is a second contiguous block at
|
||||
**`0x820A119C–0x820A1378`**, in the object's own order:
|
||||
Two iterations called this block unreachable. It is not: it is read through a
|
||||
**base register**, and the base can be *solved from the displacements alone*.
|
||||
|
||||
🔑 **The method** (`tools/re-capture/name_block_bases.py` →
|
||||
[`../data/name-block-bases.txt`](../data/name-block-bases.txt)): for each group
|
||||
of `addi rX, rBASE, -N` sharing a source register, every (string address,
|
||||
displacement) pair implies one candidate base; the true base collects a vote from
|
||||
**every name it explains**, so it wins outright. ⚠️ Taking candidates from one
|
||||
displacement instead misses it — that first cut scored the unit loader at 52/226
|
||||
against the right answer's 217/226.
|
||||
|
||||
✅ **Control, with no prior knowledge: the tool recovers `r30 = 0x82088F94` for
|
||||
the unit loader `sub_82341A20`, resolving 217 of 226 displacements.** It also
|
||||
independently recovers `sub_8230D1F8` (129/132), `sub_822F9498` (90/91) and
|
||||
`sub_822AE628` (81/108) — and finds **277** such functions image-wide.
|
||||
|
||||
✅ **The answer: `sub_821A6CF0`, `r29 = 0x820A1630`, 22 of 24 displacements.** In
|
||||
code order it names
|
||||
|
||||
```
|
||||
ControlTweakName
|
||||
ControlTweakName YawMagForNormal
|
||||
mov_stick_play mov_trigger_play eye_stick_play receipt_after_b
|
||||
receipt_reverse_s receipt_tgt_near receipt_tgt_atk receipt_side_s
|
||||
minimum_side_s receipt_match_spd order_cancel_time YawMagForNormal
|
||||
AnalogRevice_adv_roll … AnalogRevice_throttle (8 curve names)
|
||||
minimum_side_s receipt_match_spd order_cancel_time
|
||||
AnalogRevice_adv_roll … AnalogRevice_throttle (8 curve names)
|
||||
GP_MAIN_GAME
|
||||
```
|
||||
|
||||
**0 of its 28 strings has a code xref**, no instruction names a `0x820A1xxx`
|
||||
operand, and a base-plus-displacement sweep (2 283 functions that form the
|
||||
`0x820A` high half) reaches it **0** times.
|
||||
— the whole `Tweak` + `AnalogRevice_*` schema, in the object's own order, plus
|
||||
the **pak** it comes from. `r29` is built at `0x821A6D34` as `addi r29, r11, 5680`
|
||||
= `0x820A0000 + 5680` = `0x820A1630`, confirming the solved base exactly.
|
||||
|
||||
🟡 **But "no xref" does not mean "no reader", and the corpus proves it.** The
|
||||
*unit*-definition name block at `0x82085B38` — which includes `Maneuver` — also
|
||||
has **0 xrefs**, and it is read by `sub_82341A20`, which builds every key as
|
||||
`addi rX, r30, -N` from a base **passed in a register** (`Maneuver` sits at
|
||||
`0x82088F94 − 13404`). No static xref can see that, and my base-tracking sweep
|
||||
cannot either: run against that known-read block as a **control**, it recovers
|
||||
only **5** references and misses `sub_82341A20` itself.
|
||||
🔑 **And it is the same function that reads `PlayerParams`** — `sub_821A6CF0`
|
||||
references that literal and calls `sub_822F9498`. So one function loads the
|
||||
player's parameter object *and* the control-tweak/analog table. 🟡 It reads the
|
||||
curves as `AnalogRevice_*` record names; how the 11 samples are then *applied*
|
||||
is still unread.
|
||||
|
||||
⇒ The measurement stands; last iteration's 🔴 "the reader is unreachable" was too
|
||||
strong. The honest verdict is **🟡 not found by xref or by naive base tracking,
|
||||
both of which have a demonstrated blind spot** for register-passed bases. A
|
||||
runtime watch is still the cheap way in.
|
||||
> Withdrawn: the earlier 🔴 "referenced by nothing" and the softened 🟡
|
||||
> "not found by these routes". Both measurements were right — 0/28 xrefs, 0 hits
|
||||
> from naive base tracking — and both conclusions were wrong. **The base was
|
||||
> solvable from the data the whole time.**
|
||||
|
||||
⚠️ **The twin-string-block trap, in the flesh.** `mov_stick_play` and
|
||||
`eye_stick_play` each exist **twice**; only the *second* copy (`0x820AA630`,
|
||||
|
||||
Reference in New Issue
Block a user