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:
@@ -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