re: r0 is not a base register - the last shortlisted row withdrawn
sub_825F2CF0 / sub_825F2F88 (r0, 30 names each, 97% disc names) are not name-block readers at all: they are the compiler's VMX save/restore helper pair. 145 instructions each, 72 differ, every difference stvx<->lvx over v14..v127. Root cause: in `addi rD, rA, N` the rA slot reads as literal zero when it names r0, so `addi r11, r0, -N` is `li r11, -N` - vector spill offsets, not displacements. The solver excluded r1 only; the bogus base landed inside the stage-settings name block, dense enough to "resolve" 30 real field names. name_block_bases.py now skips r0 as well. Rows 277 -> 261, functions 190 -> 176, non-64K bases 170 -> 154, data-table rows 53 -> 50; 154+107=261 and 277-16=261. Artefact diff -100/+3, every removed row line carries r0, control sub_82341A20 r30 = 0x82088F94 217/226 untouched. Nothing lost: all 30 names are a strict subset of sub_8230D1F8 r29 (129 names), the stage-settings loader already in the corpus. String-xref join agrees - FinalPassBG, FogMinDistance, ScreenColorR, ExposureKey_BeginValue each exist once with exactly one xref, from sub_8230D1F8. Fourteen other artefacts byte-identical; name-block-bases.txt byte-identical on a second run.
This commit is contained in:
@@ -151,7 +151,8 @@ 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.
|
||||
`sub_822AE628` (81/108) — and finds **261** such functions image-wide
|
||||
(**277** before the `r0` correction at the end of this file).
|
||||
|
||||
✅ **The answer: `sub_821A6CF0`, `r29 = 0x820A1630`, 22 of 24 displacements.** In
|
||||
code order it names
|
||||
@@ -204,7 +205,7 @@ HP 42 000 with levels at 25 000 / 10 000 and a lock-on release cycle
|
||||
One record, `Generic`, one field — the **name** `eff_n0071` with an empty value.
|
||||
An effect id and nothing else. Not identified.
|
||||
|
||||
## ✅ Classifying all 277 rows objectively — 53 are data-table schemas (2026-08-27)
|
||||
## ✅ Classifying all rows objectively — 50 are data-table schemas (2026-08-27)
|
||||
|
||||
The earlier pass classified by eye and by function. This one is **by row** and
|
||||
uses an objective test: **are the row's names IDXD record/field names on the
|
||||
@@ -212,8 +213,11 @@ disc?** (13 450 of those disc-wide.) `name_block_bases.py` now prints it.
|
||||
|
||||
| | rows |
|
||||
|---|---:|
|
||||
| **≥50 % disc names, ≥8 names — a data-table schema** | **53** |
|
||||
| everything else (engine/XDK vocabulary, key lists, noise) | 224 |
|
||||
| **≥50 % disc names, ≥8 names — a data-table schema** | **50** |
|
||||
| everything else (engine/XDK vocabulary, key lists, noise) | 211 |
|
||||
|
||||
⚠️ Written as 53 / 224 of 277; the `r0` correction below withdrew 16 rows, three
|
||||
of them from the 53. The two axes below are quoted at their original values.
|
||||
|
||||
Split against the base-confidence axis: `solved` bases (non-zero low half) split
|
||||
34 / 136, `round` bases 16 / 91 — so **a round base is not the same question as a
|
||||
@@ -229,7 +233,7 @@ no `docs/re/` file):
|
||||
| `sub_823BDAA8` r10 | 25 | `Motion_stand`, `Motion_stand_b1`, `Motion_attackA_start`… — **motion names**, the `EnumMotions` family `DefTables` declares |
|
||||
| `sub_82315AE8` r11 | 20 | `InitRotation`, `MaxRotationSpeed`, `RotationAccel`, `MaxVerticalSpeed` — the **`Guardian` record's own fields**, i.e. the S16 boss loader |
|
||||
| `sub_8219E560` r11 | 18 | `Detail_Rank`, `MISSIONS`, `DETAIL_TITLE`, `Detail_Board_Permanent` — the **leaderboard screen** keys |
|
||||
| `sub_825F2CF0` / `sub_825F2F88` r0 | 30 each | `FinalPassBG`, `FinalPassToneRatio`, `FogMin/MaxDistance` — **post-processing**, two functions with the same base |
|
||||
| ~~`sub_825F2CF0` / `sub_825F2F88` r0~~ | ~~30 each~~ | 🔴 **WITHDRAWN — not a name block at all.** See the `r0` correction below |
|
||||
|
||||
⚠️ Four rows that *look* new are not: `sub_82297550`, `sub_822A2F00`,
|
||||
`sub_822A9C18`, `sub_82288028` mix arsenal fields the corpus owns
|
||||
@@ -238,10 +242,10 @@ no `docs/re/` file):
|
||||
coordinates as strings** — `757,228`, `903,343`, `1092,457` — which is why their
|
||||
disc-overlap sits at 53–70 % rather than ~100 %.
|
||||
|
||||
## ✅ Mining the 277: what the base-solver's index actually contains (2026-08-27)
|
||||
## ✅ Mining the index: what the base-solver's index actually contains (2026-08-27)
|
||||
|
||||
277 rows over **190 distinct functions** (a function can read more than one
|
||||
block). Classified:
|
||||
277 rows over **190 distinct functions** — a function can read more than one
|
||||
block — or **261 over 176** after the `r0` correction below. Classified:
|
||||
|
||||
**🔴 The tool's false-positive mode, now named.** 107 rows solve to a base on a
|
||||
**64K boundary** — a bare `addis rX, r0, 0xHHHH` with no `addi` of its own, so any
|
||||
@@ -256,9 +260,9 @@ loader `sub_82341A20` (217), stage settings `sub_8230D1F8` (129), `PlayerParams`
|
||||
(`sub_82320B48` → `ORDER_WINGMAN_*`), missile guidance (`sub_8236B608`,
|
||||
`sub_8237BB78` → `st1_up_aperture` etc., [[weapon-datasheet-static]]), shell
|
||||
movement (`sub_82261F70` → `Spiral_BeginTime`, [[weapon-struct-runtime]]),
|
||||
substructures (`sub_823479B8` → `ParentStructureID`), and six camera/fog readers
|
||||
(`sub_825F2CF0`, `sub_825F2F88`, `sub_8247DFC0`, `sub_823B2620`, `sub_82222E70`,
|
||||
`sub_822C7480`).
|
||||
substructures (`sub_823479B8` → `ParentStructureID`), and ~~six~~ **three** camera/fog readers
|
||||
(`sub_8247DFC0` r31, `sub_823B2620` r31, `sub_822C7480` r31 — all three bases in
|
||||
the same `0x8209E6xx` region; the other three were `r0` artefacts, below).
|
||||
|
||||
**🔑 The find: `sub_8233C368` is the `AIParams` loader.** `r28`, base
|
||||
`0x8208583C`, 20 names — `Enumerate_AIs`, `FiringLength`, `GuardLength`,
|
||||
@@ -289,3 +293,70 @@ positions and look-at tags, with `menu2` having sub-shots `2_0`–`2_4b`.
|
||||
|
||||
🟡 None of those five were opened; the index says what each names, not what each
|
||||
means.
|
||||
|
||||
## 🔴 CORRECTION (2026-08-28) — `r0` is not a base register; 16 rows withdrawn
|
||||
|
||||
The last shortlisted row was `sub_825F2CF0` + `sub_825F2F88`, **`r0`**, 30 names
|
||||
each on the same base `0x8209EB30`, 97 % of them disc names. The question I set
|
||||
was "a pair, or a clone?" **Both answers are wrong: neither function reads a name
|
||||
block.**
|
||||
|
||||
Diffing the two, instruction for instruction, settles it in one look — 145
|
||||
instructions each, **72 differ and every difference is the same substitution**:
|
||||
|
||||
```
|
||||
825F2CF0 addi r11, r0, -288 825F2F88 addi r11, r0, -288
|
||||
825F2CF4 stvx v14, r11, r12 825F2F8C lvx v14, r11, r12
|
||||
825F2CF8 addi r11, r0, -272 825F2F90 addi r11, r0, -272
|
||||
825F2CFC stvx v15, r11, r12 825F2F94 lvx v15, r11, r12
|
||||
… stvx / stvx128, v14 … v127 … lvx / lvx128, v14 … v127
|
||||
```
|
||||
|
||||
They are the compiler's **VMX register save / restore helper pair** — a spill of
|
||||
v14–v127 and its matching reload. So they *are* a pair rather than a clone, but
|
||||
the pairing is `stvx`/`lvx`, not two readers of one table.
|
||||
|
||||
🔑 **The root cause, and it is a class of false positive, not one row.** In
|
||||
PowerPC `addi rD, rA, N` the `rA` slot **reads as literal zero when it names r0** —
|
||||
the form is `li rD, N`. The 72 `addi r11, r0, -N` are the vector spill offsets,
|
||||
plain immediates. The solver had excluded `r1` (the stack pointer) and nothing
|
||||
else, so it took those offsets as displacements, voted a base, and landed
|
||||
`0x8209EB30` — **inside the stage-settings name block**, where a scatter of
|
||||
16-byte-spaced negative offsets is dense enough to "resolve" 30 real field names.
|
||||
|
||||
**A 97 %-disc-names row can still be an artefact.** The disc-name test measures
|
||||
what the *base region* contains, not that the function reads it.
|
||||
|
||||
✅ **Nothing is lost.** All 30 of the withdrawn names are a **strict subset of the
|
||||
real reader's block**: `sub_8230D1F8 r29`, base `0x8209FD28`, 129 names —
|
||||
the stage-settings loader the corpus already owns ([[stage-settings-table]]).
|
||||
The string-xref join confirms it independently: `FinalPassBG`, `FogMinDistance`,
|
||||
`ScreenColorR`, `ExposureKey_BeginValue` each exist **once** in the image and
|
||||
each has **exactly one** xref, from `sub_8230D1F8`. `NoseCameraFOV` and
|
||||
`GlareType` exist **twice** — the second copy is read by `sub_822AE628`, the
|
||||
hangar's game-wide defaults, which is the trap the corpus already records.
|
||||
|
||||
**Fix and its effect.** `name_block_bases.py` now skips `reg in ('r0', 'r1')`.
|
||||
Every number moves by exactly the 16 `r0` rows and nothing else:
|
||||
|
||||
| | before | after |
|
||||
|---|---:|---:|
|
||||
| rows | 277 | **261** |
|
||||
| distinct functions | 190 | **176** |
|
||||
| bases with a non-zero low half | 170 | **154** |
|
||||
| bases on a 64K boundary (low confidence) | 107 | 107 |
|
||||
| data-table schemas (≥50 % disc, ≥8 names) | 53 | **50** |
|
||||
|
||||
Both ways: `154 + 107 = 261`, and `277 − 16 = 261`. The diff against the previous
|
||||
artefact removes 100 lines and adds 3; **every removed row line carries `r0`**,
|
||||
the three added lines are those counts, and the control is untouched
|
||||
(`sub_82341A20 r30 = 0x82088F94`, 217/226). Two of the 16 functions keep a row
|
||||
through another register, which is why 190 − 176 = 14, not 16.
|
||||
|
||||
The three withdrawn data-table rows are `sub_825F2CF0`, `sub_825F2F88` and
|
||||
`sub_82222E70` (15 names, 100 % disc — the same `0x8209E6D8` stage-settings
|
||||
region, and by the same mechanism).
|
||||
|
||||
🟡 What this does **not** settle: the 🟡 stalled `SpaceSize` / `GlareType` /
|
||||
`Nebura_*` numerics are no closer — their reader was already known to be
|
||||
`sub_8230D1F8`, and the r0 row never was a second one.
|
||||
|
||||
Reference in New Issue
Block a user