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:
Sylpheed RE agent
2026-08-28 00:21:26 +00:00
parent aeec0dab8f
commit bb6fb9024b
5 changed files with 149 additions and 113 deletions

View File

@@ -5497,3 +5497,59 @@ field**; counting "24 `Detail_*` things" would merge two unrelated tables.
`Extra01…04` map onto the four challenge missions the corpus knows. And the last
shortlisted row (`sub_825F2CF0`/`sub_825F2F88` post-processing, two functions on
one base) is still unopened.
---
## 🔴✅ 2026-08-28 — the last shortlisted row is an artefact: `r0` is not a base register
Item (a), the final unopened row: `sub_825F2CF0` + `sub_825F2F88`, **`r0`**, 30
names each on the same base `0x8209EB30`, 97 % disc names. The framing was "a
pair, or a clone?" — **both wrong. Neither function reads a name block.**
🔑 **Diffing the two settles it in one look.** 145 instructions each, **72
differ, and every difference is the same substitution**: `stvx`/`stvx128` on one
side, `lvx`/`lvx128` on the other, over v14…v127. They are the compiler's **VMX
register save / restore helper pair**. A pair, then — but the pairing is
spill/reload, not two readers of one table.
🔴 **The root cause is a whole class of false positive.** 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 vector spill offsets, plain
immediates. The solver excluded `r1` (stack pointer) and nothing else, so it took
them 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. That is a
correction to the 53-of-277 classification's own premise.
**Nothing is lost, and the real reader was already ours.** All 30 withdrawn
names are a **strict subset** of `sub_8230D1F8 r29` (base `0x8209FD28`, 129
names) — the stage-settings loader. The string-xref join agrees independently:
`FinalPassBG`, `FogMinDistance`, `ScreenColorR` and `ExposureKey_BeginValue` each
exist **once** in the image with **exactly one** xref, from `sub_8230D1F8`.
`NoseCameraFOV` and `GlareType` exist **twice**, the second copy read by
`sub_822AE628` — the hangar's game-wide defaults, the double-string-block trap
the corpus already records.
**Fix:** `name_block_bases.py` now skips `reg in ('r0', 'r1')`. Every number moves
by exactly the 16 `r0` rows: rows **277 → 261**, distinct functions **190 → 176**
(14, not 16 — two of those functions keep a row through another register),
non-64K bases **170 → 154**, 64K bases 107 unchanged, data-table schemas
**53 → 50**. Both ways: `154 + 107 = 261` and `277 16 = 261`. The artefact diff
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). The three withdrawn data-table rows are `sub_825F2CF0`,
`sub_825F2F88` and `sub_82222E70` (15 names, 100 % disc, same region, same
mechanism); the "six camera/fog readers" are really **three** (`sub_8247DFC0`,
`sub_823B2620`, `sub_822C7480`, all `r31`, all based in `0x8209E6xx`).
Written up in `docs/re/structures/player-tuning-tables.md`; `INDEX.md` updated.
🟡 Not settled: the 🟡 stalled `SpaceSize`/`GlareType`/`Nebura_*` numerics are no
closer — their reader was already known to be `sub_8230D1F8`, and this row never
was a second one. **The shortlist is now empty.** Still open: whether a `stvx`-
style helper elsewhere fooled a *non*-`r0` row (the 64K-boundary cluster is the
remaining known false-positive mode, unaddressed); `Detail_Window_Known/_Unknown`;
whether `Extra01…04` map onto the challenge missions.

View File

@@ -106,7 +106,7 @@ files, which is how the same ground got covered twice.
| [`structures/stage16-boss.md`](structures/stage16-boss.md) | The Stage 16 boss end to end — object, loader `sub_82315AE8`, collision | ✅ CONFIRMED — 19/19 of the loader's names are `Guardian`/`Core` fields; 5 disc fields have no string in the image (two same-length controls do) |
| [`structures/leaderboard-screen.md`](structures/leaderboard-screen.md) | The leaderboard screen — `sub_8219E560`, 12 panel elements, 4 board scopes, 6 modes | ✅ CONFIRMED — 18 names = 13 records + 5 fields; all 12 `Detail_*` records named, zero residual; ⚠️ 12 `Detail*` records and 12 `Detail*` fields are DISJOINT |
| [`structures/result-screens.md`](structures/result-screens.md) | The debriefing and pilot-record screens — `STAGE_RESULT` 21 fields, `OVERVIEW` 9, `EX_OVERVIEW` 7 | ✅ CONFIRMED — the compiled key lists `sub_822814D8` (24/24) and `sub_8227A3A0` (21/21) match the `tables.pak` records exactly; `2+21+1=24`, `5+7+9=21` |
| [`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/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 (261 name-block loaders image-wide after the `r0` correction, 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 (S01S16, S18S29) and the player's craft per stage | ✅✅ CONFIRMED — two independent files close on 6 + 15 + 1 + 6 = 28 |

View File

@@ -4,7 +4,7 @@
strings in the image: 7140 addi instructions: 282736
## 277 functions read a name block through a base register
## 261 functions read a name block through a base register
function reg base resolved / displacements
sub_82341A20 r30 0x82088F94 217 / 226
sub_822215D0 r11 0x820A0000 205 / 206
@@ -81,14 +81,11 @@
sub_8284B398 r11 0x820B0000 40 / 42
sub_822F9498 r31 0x82026F24 39 / 83
sub_826FBC38 r11 0x82050000 38 / 60
sub_826AE390 r0 0x82026F74 37 / 306
sub_82338EE0 r31 0x82026F18 36 / 78
sub_823AE908 r31 0x82026F24 33 / 71
sub_823BDAA8 r11 0x820B0000 33 / 35
sub_826AB050 r11 0x82040000 31 / 33
sub_82561C70 r11 0x820A90D8 30 / 93
sub_825F2CF0 r0 0x8209EB30 30 / 64
sub_825F2F88 r0 0x8209EB30 30 / 64
sub_822E3EC8 r11 0x820B0000 29 / 32
sub_82627B70 r11 0x82030000 29 / 29
sub_82297550 r11 0x820B0000 27 / 41
@@ -174,8 +171,6 @@
sub_8257E270 r5 0x820AE7AC 21 / 31
sub_8257E6C0 r5 0x820AE7AC 21 / 31
sub_82658790 r11 0x82030000 21 / 26
sub_82780C10 r0 0x820A9EAD 21 / 51
sub_827D9DD0 r0 0x82074503 21 / 149
sub_821A8578 r11 0x82023F00 20 / 25
sub_8233C368 r28 0x8208583C 20 / 21
sub_8236B608 r11 0x820B0000 20 / 20
@@ -190,7 +185,6 @@
sub_8232BAA0 r31 0x82034220 17 / 25
sub_823B63E0 r31 0x820AE72C 17 / 30
sub_82440878 r31 0x82026FF0 17 / 33
sub_824B4908 r0 0x82073D58 17 / 122
sub_8265A260 r11 0x82030000 17 / 18
sub_827A5058 r11 0x82050000 17 / 18
sub_8219BB30 r11 0x820244AC 16 / 26
@@ -201,10 +195,8 @@
sub_82324178 r31 0x82026F14 16 / 28
sub_82473448 r31 0x8202EBFC 16 / 29
sub_82562BB8 r3 0x820A918C 16 / 20
sub_825C2980 r0 0x82052A40 16 / 22
sub_826A0040 r11 0x820A01AC 16 / 32
sub_82804AD0 r11 0x820A792C 16 / 43
sub_82222E70 r0 0x8209E6D8 15 / 27
sub_8227F8E0 r31 0x820A0E9C 15 / 20
sub_82282DB0 r31 0x820A0E8C 15 / 20
sub_822E3EC8 r10 0x820B0000 15 / 15
@@ -216,9 +208,7 @@
sub_823C0C58 r31 0x82034270 15 / 18
sub_82565C80 r11 0x820A5574 15 / 42
sub_825DA790 r26 0x820A3B4C 15 / 20
sub_8265DFB8 r0 0x82074ECA 15 / 98
sub_82664700 r11 0x82030000 15 / 16
sub_82787E38 r0 0x82074B66 15 / 76
sub_8285F2C8 r5 0x820AE7AC 15 / 15
sub_82860290 r3 0x820AE7AC 15 / 15
sub_82860670 r3 0x820AE7AC 15 / 15
@@ -226,7 +216,6 @@
sub_8220ED30 r31 0x820A0E9C 14 / 21
sub_8221F700 r11 0x820A0000 14 / 18
sub_82288E70 r11 0x820B0000 14 / 23
sub_822CC130 r0 0x82026F74 14 / 31
sub_822F2E58 r31 0x820A97E4 14 / 27
sub_82308AB8 r31 0x82034200 14 / 27
sub_8230BB58 r11 0x820B0000 14 / 15
@@ -241,7 +230,6 @@
sub_823B2620 r31 0x8209E710 14 / 24
sub_823D3BE0 r31 0x820B47D4 14 / 24
sub_82529708 r25 0x820AAA6C 14 / 20
sub_8252C4E8 r0 0x82074617 14 / 70
sub_825635C8 r31 0x8209E230 14 / 22
sub_8261F2C0 r11 0x8208AB54 14 / 21
sub_82659DF0 r11 0x82030000 14 / 14
@@ -254,8 +242,6 @@
sub_823800A8 r31 0x82001C4C 13 / 21
sub_823A3C30 r31 0x82034270 13 / 18
sub_823BF6D8 r31 0x82034270 13 / 17
sub_82435448 r0 0x82027040 13 / 52
sub_824B42E8 r0 0x820743A3 13 / 66
sub_824E76D0 r10 0x8209FC64 13 / 22
sub_8257DD20 r4 0x820342D0 13 / 15
sub_8257DD20 r5 0x820342D0 13 / 15
@@ -266,7 +252,6 @@
sub_82621400 r11 0x82030000 13 / 14
sub_82622448 r11 0x8202B13C 13 / 18
sub_8263D478 r11 0x82030000 13 / 16
sub_826A7BF0 r0 0x82074DCE 13 / 85
sub_827030F8 r11 0x82050000 13 / 13
sub_827B0098 r11 0x82050000 13 / 15
sub_82193648 r11 0x820A8278 12 / 22
@@ -280,12 +265,11 @@
sub_82625890 r11 0x82030000 12 / 14
sub_826ACC98 r11 0x82040000 12 / 18
sub_826FA758 r11 0x820A81F0 12 / 26
sub_82766DB0 r0 0x8207454A 12 / 71
sub_82766DB0 r11 0x82050000 12 / 29
sub_827D7C00 r11 0x82060000 12 / 14
## Confidence split
bases with a non-zero low half (a real `addis`+`addi` pair): 170
bases with a non-zero low half (a real `addis`+`addi` pair): 154
bases on a 64K boundary (LOW CONFIDENCE, see below) : 107
0x820B0000 x82 rows
0x82030000 x10 rows
@@ -297,7 +281,7 @@
## Is the row a DATA-TABLE schema? (names that are IDXD record/field names on the disc)
distinct IDXD record+field names disc-wide: 13450
rows that are >=50% disc names and >=8 names: 53 / 277
rows that are >=50% disc names and >=8 names: 50 / 261
sub_82341A20 r30 0x82088F94 217 names 89% disc
sub_822215D0 r11 0x820A0000 205 names 98% disc
sub_8230D1F8 r29 0x8209FD28 129 names 97% disc
@@ -306,8 +290,6 @@
sub_822AE628 r11 0x820B0000 81 names 67% disc
sub_823BDAA8 r11 0x820B0000 33 names 88% disc
sub_82561C70 r11 0x820A90D8 30 names 50% disc
sub_825F2CF0 r0 0x8209EB30 30 names 97% disc
sub_825F2F88 r0 0x8209EB30 30 names 97% disc
sub_82297550 r11 0x820B0000 27 names 67% disc
sub_822A2F00 r11 0x820B0000 27 names 70% disc
sub_823BDAA8 r10 0x820B0000 25 names 100% disc
@@ -328,7 +310,6 @@
sub_82286BC8 r11 0x820B0000 18 names 100% disc
sub_821AA1B0 r31 0x820A97CC 16 names 94% disc
sub_82804AD0 r11 0x820A792C 16 names 88% disc
sub_82222E70 r0 0x8209E6D8 15 names 100% disc
sub_8227F8E0 r31 0x820A0E9C 15 names 100% disc
sub_82282DB0 r31 0x820A0E8C 15 names 100% disc
sub_823479B8 r28 0x820864F4 15 names 100% disc
@@ -1118,12 +1099,6 @@
assembler.prd_stack->Size() == 1 , ExpAdjust=%d MiniFetch=FALSE, PrefetchCount=%d
MiniFetch=TRUE
sub_826AE390 (r0, base 0x82026F74) names 37:
vs_3_sw vs_3_0 vs_2_x vs_2_sw vs_2_a vs_1_1 vs_1_0 vs.3.xps vs.3.swx vs.3.sw
vs.3.0 vs.2.x vs.2.sw vs.2.a vs.1.1 vs.1.0 tx_1_0 ps_3_sw ps_3_0 ps_2_x ps_2_sw
ps_2_b ps_2_alpha2 ps_2_a ps_1_4 ps_1_3 ps_1_2 ps_1_1 ps_1_0 ps.3.sw ps.3.0
ps.2.x ps.2.sw ps.2.b ps.2.a ps.1.4 ps.1.3
sub_82338EE0 (r31, base 0x82026F18) names 36:
ps.1.3 vs_3_swx vs_3_0 ps_3_sw vs_2_x vs_2_sw ps_3_0 vs_2_a ps.2.b vs_1_1 ps_2_x
vs_1_0 vs.3.xps ps_2_sw ps_2_b vs.3.sw vs.3.0 ps_2_alpha2 vs.2.x vs.2.sw vs.2.a
@@ -1252,22 +1227,6 @@ float4 FontPixelShader( VS_OUT In ) : COLOR0
C_PAD_DECODER \x8F\x89\x8A\xFA\x89\xBB
Pattern
sub_825F2CF0 (r0, base 0x8209EB30) names 30:
FinalPassBG FinalPassToneRatio FogMinDistance FogMaxDistance FogMinAmount FogMaxAmount
FirstAdaptedLuminance ExposureKey_BeginValue NoseCameraFOV NoseCameraPosX
NoseCameraPosY NoseCameraPosZ NearCameraFOV NearCameraPosX NearCameraPosY
NearCameraPosZ FarCameraFOV FarCameraPosX FarCameraPosY FarCameraPosZ SplinterCell
BGRotateScale_%1d GlareType Contrast ColorLayerA ScreenColorR ScreenColorG
ScreenColorB FinalPassType FinalPassDR
sub_825F2F88 (r0, base 0x8209EB30) names 30:
FinalPassBG FinalPassToneRatio FogMinDistance FogMaxDistance FogMinAmount FogMaxAmount
FirstAdaptedLuminance ExposureKey_BeginValue NoseCameraFOV NoseCameraPosX
NoseCameraPosY NoseCameraPosZ NearCameraFOV NearCameraPosX NearCameraPosY
NearCameraPosZ FarCameraFOV FarCameraPosX FarCameraPosY FarCameraPosZ SplinterCell
BGRotateScale_%1d GlareType Contrast ColorLayerA ScreenColorR ScreenColorG
ScreenColorB FinalPassType FinalPassDR
sub_822E3EC8 (r11, base 0x820B0000) names 29:
g_mWorldViewProjection g_mWorldViewProjectionLast g_mObjectToView g_mObjectToViewLast
g_mWorld g_Params g_UVParams g_LayerParams g_MapEnable g_MapLayerEnable ColorMap
@@ -2145,16 +2104,6 @@ float4 FontPixelShader( VS_OUT In ) : COLOR0
Constant is both a struct member and an array element. defconst: %s
Register type not found. Multi-dimensional arrays are not supported. Bad variable name.
sub_82780C10 (r0, base 0x820A9EAD) names 21:
757,228 rou_e009 rou_e008 rou_e006 rou_e005 rou_e003 rou_e004 rou_f202 rou_f201
rou_f105 rou_f101 rou_f003 rou_f001 rou_e201 ConditionToDevelop rou_e108 rou_e106
rou_e105 rou_e015 rou_e010 rou_e007
sub_827D9DD0 (r0, base 0x82074503) names 21:
hyphensuperior Gravesmall Asmall Msmall colonmonetary notequal sixinferior
Dotaccentsmall Ringsmall seveneighths onethird centinferior Aacutesmall 001.002
Semibold greaterequal Acircumflex Uacute Ugrave eacute edieresis
sub_821A8578 (r11, base 0x82023F00) names 20:
texdp3 vs_3_sw vs_3_0 vs_2_x vs_2_sw vs_2_a vs_1_1 vs_1_0 vs.3.xps vs.3.sw vs.3.0
vs.2.x vs.2.sw vs.2.a vs.1.1 vs.1.0 ps_3_sw ps_3_0 ps_2_x ps_2_sw
@@ -2274,10 +2223,6 @@ float4 FontPixelShader( VS_OUT In ) : COLOR0
ps_2_a ps.2.x ps_1_4 ps_1_2 ps.1.0 ps.1.2 ps_1_1 ps.2.b ps_1_0 ps.1.3 ps.3.sw
ps.2.a ps.3.0 ps.1.1 ps_1_3 ps.1.4 ps.2.sw
sub_824B4908 (r0, base 0x82073D58) names 17:
Ntilde Lslash acircumflex onesuperior logicalnot fouroldstyle Uacute adieresis
atilde ccedilla eacute Ugrave Ydieresis Zcaron notequal product bracketleft
sub_8265A260 (r11, base 0x82030000) names 17:
More than one buffer type specified.
Buffer type "%s" does not use a size value.
@@ -2347,11 +2292,6 @@ float4 FontPixelShader( VS_OUT In ) : COLOR0
mob_n040 rou_e202 rob_e001 mob_n500 mob_n043 mob_n042 mob_n041 rob_f001 rou_f104
rou_f102 rou_e107 1092,457 FlightTime 1066,491 ClearTimes rob_f002
sub_825C2980 (r0, base 0x82052A40) names 16:
FOGEND BORDERCOLORA FOGCOLORA BORDERCOLORG BORDERCOLORB YUVCONSTANTSC33
YUVCONSTANTSC31 YUVCONSTANTSC22 YUVCONSTANTSC23 YUVCONSTANTSC32 YUVCONSTANTSC03
YUVCONSTANTSC13 YUVCONSTANTSC21 YUVCONSTANTSC11 YUVCONSTANTSC12 FOGCOLORG
sub_826A0040 (r11, base 0x820A01AC) names 16:
PGHUD_RANGE_BAR STAGE_ID_7 PGHUD_RANGE_BAR_EM AsteroidDefinition STAGE_ID_8 STAGE_ID_9
STAGE_ID_10 STAGE_ID_11 StageResource STAGE_ID_12 STAGE_ID_13 STAGE_ID_14 STAGE_ID_15
@@ -2362,11 +2302,6 @@ float4 FontPixelShader( VS_OUT In ) : COLOR0
PGMANUVA_NUM3EFF PGHUD_ARMS_NUM%d EnumerateUnit SilhouetteModel PGMANUVA_NUM3
PGHUD_TYPE_CN WEAPONS SE_main_arm1_attach 903,343 PGTARGET_SP_READY_DLH
sub_82222E70 (r0, base 0x8209E6D8) names 15:
Generic NearCameraPosY Nebura_Size GravityFactor FarCameraPosX FarCameraFOV
YawAdjustment EnumerateAIParams RollAdjustment Player NoseCameraFOV FogMinAmount
FinalPassToneRatio NearCameraPosZ FarCameraPosZ
sub_8227F8E0 (r31, base 0x820A0E9C) names 15:
ForceBootPlayerCharacterID_11 ForceBootPlayerCharacterID_15 ForceBootPlayerCharacterID_12
ForceBootPlayerCharacterID_14 ForceBootPlayerCharacterID_13 ForceBootPlayerCharacterID_3
@@ -2429,10 +2364,6 @@ float4 FontPixelShader( VS_OUT In ) : COLOR0
Camera TYPE_END_EVENT A/START MISSION11 MISSION12 MISSION7 MISSION8 MISSION9
MISSION10 MISSION15 MISSION16 MISSION17 MISSION18 MISSION13 MISSION14
sub_8265DFB8 (r0, base 0x82074ECA) names 15:
Asmall Bsmall Csmall Dsmall Esmall Fsmall Gsmall Hsmall Ismall Jsmall Ksmall
Lsmall Msmall Nsmall Osmall
sub_82664700 (r11, base 0x82030000) names 15:
p->xUR1 = __vspltisw(1);
%s = __vmsum3fp(%s, %s);
@@ -2448,10 +2379,6 @@ float4 FontPixelShader( VS_OUT In ) : COLOR0
%s = __vsel(%s, %s, %s);
sub_82787E38 (r0, base 0x82074B66) names 15:
Ucircumflex bsuperior Zcaron osuperior ssuperior atilde nineoldstyle icircumflex
Yacute Udieresis edieresis Psmall zerosuperior aacute dsuperior
sub_8285F2C8 (r5, base 0x820AE7AC) names 15:
%4d / %4d
%4d / %4d
@@ -2558,10 +2485,6 @@ float4 FontPixelShader( VS_OUT In ) : COLOR0
199,333 360,333 BrokenData 260,367 441,367 AUTO_SAVED CLEARED 260,333 441,333
%03d:%02d:%02d 513,367 FlightTime 703,367 RatioOfClear
sub_822CC130 (r0, base 0x82026F74) names 14:
vs_3_sw vs.3.sw vs_1_0 ps_2_alpha2 vs.1.0 vs.2.a vs_2_x vs_1_1 vs.3.xps ps.1.1
vs_2_sw vs_3_0 vs.2.x ps.1.3
sub_822F2E58 (r31, base 0x820A97E4) names 14:
STAGE14 STATE_GAME_CLEAR STAGE04 STAGE06 MISSION_SELECT LOAD_BASES STATE_STAND_BY
STAGE13 STAGE11 STAGE07 STAGE10 STAGE09 STAGE12 STAGE08
@@ -2692,10 +2615,6 @@ float4 FontPixelShader( VS_OUT In ) : COLOR0
LuminanceFX_BrightB LuminanceFX_TonedRatio LuminanceFX_Vignetting
LuminanceFX_VignetSharpness
sub_8252C4E8 (r0, base 0x82074617) names 14:
scedilla cacute parenright semicolon quoteright .notdef percent parenleft hyphen
Scedilla dcroat Ccaron asciicircum exclam
sub_825635C8 (r31, base 0x8209E230) names 14:
DOF_FarFocal FogLinear FogMinDistance Nebura_Size Nebura_MiddleNear FogMaxAmount
Nebura_SpeedScale UnsharpMask_R Nebura_NearAmount FinalPassDB FinalPassBR FinalPassBG
@@ -2773,14 +2692,6 @@ float4 FontPixelShader( VS_OUT In ) : COLOR0
__vspltw(%s, 0) __vspltw(%s, 1) __vsel(%s, %s, %s) __vcfux(%s, %d) __vspltisw(0)
__vspltw(%s, 2) p->xSR2 __lvx(%s, 0)
sub_82435448 (r0, base 0x82027040) names 13:
ps_1_1 ps_1_4 ps_2_a ps_1_3 ps_1_2 ps.2.x ps_1_0 ps.3.sw ps.3.0 ps.2.sw ps.2.b
ps.1.4 ps.2.a
sub_824B42E8 (r0, base 0x820743A3) names 13:
Dieresissmall figuredash dollarinferior Medium Adieresissmall Atildesmall Aringsmall
Agravesmall Aacutesmall Ethsmall threequartersemdash agrave partialdiff
sub_824E76D0 (r10, base 0x8209FC64) names 13:
AccPitchFactor AV_AxisMode_Min DecPitchFactor AV_AxisMode_Max GaugeMaximum
ChargeMinimum ChargeMaximum AttackTime_Minimum StageResource BackGroundID
@@ -2842,10 +2753,6 @@ float4 FontPixelShader( VS_OUT In ) : COLOR0
Internal error 0x%08X while generating code. Please send this shader to xboxds@xbox.com.
Microcode comparison failed. Please send your HLSL code to xboxds@xbox.com.
sub_826A7BF0 (r0, base 0x82074DCE) names 13:
periodsuperior Ksmall Jsmall Lsmall Bsmall Csmall Dsmall Esmall Fsmall Gsmall
Hsmall Ismall Msmall
sub_827030F8 (r11, base 0x82050000) names 13:
Assertion failed: %s (%s:%u)
d:\xenon\jun06\core\private\xtl\graphics\xgraphics\ucode\ssm\statecompiler\ssmstatecompiler.cpp
@@ -2925,10 +2832,6 @@ float4 FontPixelShader( VS_OUT In ) : COLOR0
LANGUAGE PGMARKTGT PGMARKLOCKON PGMANUVA_EFF3 PGMARK_GAUGE_SUB_BASE FrameAnimationType
SYSTEM PGMARK_SHIELD_SUB_BASE PGMARK_GAUGE_BAR PGMARK4 PGMARK3 PGMARK2
sub_82766DB0 (r0, base 0x8207454A) names 12:
notequal lessequal product \x82\x42[`.null Scedilla cacute scedilla B[`.null Cacute
Oslashsmall Uacutesmall Udieresissmall
sub_82766DB0 (r11, base 0x82050000) names 12:
instruction options arrays are not supported in inline microcode predication
alloc option setGradient instructions muls_prev2 instruction kill instructions

View File

@@ -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 5370 % 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
v14v127 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.

View File

@@ -57,7 +57,13 @@ def main():
for name in sorted(byf):
for reg in sorted(byf[name]):
D = sorted(set(byf[name][reg]))
if len(D) < MIN_GROUP or reg == 'r1': # r1 is the stack pointer
# r1 is the stack pointer. r0 is NOT a base register at all: in
# `addi rD, r0, N` the RA slot reads as literal zero, so the form is
# `li rD, N` and the displacements are plain immediates. Left in, the
# VMX save/restore helper pair sub_825F2CF0 / sub_825F2F88 (72 x
# `addi r11, r0, -N`, the vector spill offsets) solved a base and
# "named" 30 strings, 97% of them disc names -- a pure artefact.
if len(D) < MIN_GROUP or reg in ('r0', 'r1'):
continue
# Vote: every (string, displacement) pair implies one candidate base.
# The true base collects a vote from each name it explains, so it wins