re: progress word B's writer is sub_8219F2E8 - it sets bit (stage - 24)
Section 5.5 narrowed the search to 21 functions but could not name one, because the scan assumed the copy destination was an r1-relative stack local. It is not. Reading the known word-A writer instead of guessing its shape shows a frame register: bl singleton, addi r3,r31,96 (dest), addi r4,r11,80 (src = obj+80), bl copier, stw r10,96(r31). That one wrong assumption made the first scan return 0 for all 21, the known-good included - the third filter this week killed by its own control. With the real shape - addi r3,rF,K before the copier, then stores at K+d on rF - word B sits at d = 1956-80 = 1876, and exactly one of the 21 hits: sub_8219F2E8, copy dest r31+560, storing 2436(r31), and 560+1876 = 2436. Control: the word-A writer 0x821C1630 stores at relative +0, i.e. word A. The proof is the bias. Word B's writer computes addi r11,r11,-24 then 1 << (index-24) before or-ing into the copy; word A's writer shifts by the raw stage number with no bias. That reproduces section 5.1's read-side rule - n >= 24 means bit (n-24) of word B - on the write side, so word B's bits 0-5 are challenge stages 24-29, the same six that 5.4's MISSION_ID names. The two writers are exact complements. It has exactly one real caller, 0x821A0B1C inside 0x821A0AD0, whose strings are BASE_INFO and DEBRIEFING - so clearing a challenge mission is recorded from the debriefing screen. A second bitfield falls out of the same function: it reads the copy at +1880 (2440(r31) = obj+1960, the word after B), re-reads obj+1960 after the setter, xors old against new, and loops the 32 result bits calling 0x82175C20 with bit + 64. So obj+1960 is a second 32-bit progress bitfield with ids 64-95, whose newly-set bits are announced one at a time - an unlock-notification loop. Against 5.3's own prediction: neither sub_8219F2E8 nor its caller references Time or Points, 0 for both. 5.3 expected word B's writer to also store the stage's Time/Points record. It does not - this is the bit-setter only, and the record store is still unlocated. All seventeen artefacts byte-identical.
This commit is contained in:
@@ -7090,3 +7090,33 @@ stack copy at `localbase + 1876`, invisible to an offset scan.
|
||||
no `Time`. **Two `Points` vocabularies.**
|
||||
|
||||
🔑 **Next candidate:** `0x8218EFE0` — the only one of the 21 carrying `BASE_EXTRA`.
|
||||
|
||||
---
|
||||
|
||||
## ✅ 2026-08-28 — progress word B's writer: `sub_8219F2E8`, sets bit (stage − 24)
|
||||
|
||||
§5.5's scan failed because it assumed the copy destination was an `r1`-relative
|
||||
stack local. Reading the known word-A writer shows it is a **frame register**:
|
||||
`addi r3, r31, 96` / `addi r4, r11, 80` / copier / `stw r10, 96(r31)`.
|
||||
⚠️ **That one wrong assumption returned 0 for all 21, the known-good included —
|
||||
the third filter this week killed by its own control.**
|
||||
|
||||
✅ With the real shape, word B (`d = 1956−80 = 1876`) hits **exactly one** of the
|
||||
21: **`sub_8219F2E8`**, copy dest `r31+560`, storing `2436(r31)` (`560+1876`).
|
||||
Control: `0x821C1630` stores at relative `+0` = word A.
|
||||
|
||||
🔑 **The proof is the bias.** Word B's writer computes `addi r11, r11, -24` then
|
||||
`1 << (index−24)`; word A's shifts by the raw stage number with no bias. That
|
||||
reproduces §5.1's read-side rule (`n >= 24` → bit `n−24` of word B) **on the write
|
||||
side**, so word B's bits 0–5 are challenge stages 24–29 — the same six §5.4 names.
|
||||
|
||||
**Called from exactly one place:** `0x821A0B1C` in `0x821A0AD0` (`BASE_INFO` +
|
||||
`DEBRIEFING`) — clearing a challenge mission is recorded from the debriefing.
|
||||
|
||||
✅ **A second bitfield falls out:** the same function diffs `obj+1960` (the word
|
||||
after B) before/after the setter and loops its 32 bits calling `0x82175C20` with
|
||||
`bit + 64` — a second progress bitfield with ids **64–95**, announced per bit.
|
||||
|
||||
⚠️ **Against §5.3's prediction:** neither the writer nor its caller references
|
||||
`Time` or `Points` (0 both). It is the **bit-setter only**; the Time/Points record
|
||||
store is still unlocated.
|
||||
|
||||
@@ -563,3 +563,66 @@ are `Dependency`, `MissionObjective`, `Points`, `WEAPON`, `WEAPONS`, i.e. the
|
||||
🔑 **Next candidate, not yet checked:** `0x8218EFE0` (1212 B) is the one progress
|
||||
writer whose strings are `BASE_EXTRA` + `DIFFICULTY` — the only member of the 21
|
||||
carrying the **EXTRA** vocabulary.
|
||||
|
||||
### 5.6 ✅ CLOSED — word B's writer is `sub_8219F2E8`, and it sets bit (stage − 24)
|
||||
|
||||
§5.5 narrowed the search to 21 functions but could not name one, because the scan
|
||||
assumed the copy destination was a `r1`-relative stack local. **It is not.**
|
||||
Reading the known word-A writer instead of guessing its shape:
|
||||
|
||||
```asm
|
||||
bl 0x821707C0 ; singleton
|
||||
addi r3, r31, 96 ; DEST = r31+96 <- a FRAME register, not r1
|
||||
addi r4, r11, 80 ; SRC = obj+80
|
||||
bl 0x82175110 ; copy
|
||||
stw r10, 96(r31) ; write copy word 0 == word A
|
||||
```
|
||||
|
||||
⚠️ **The first scan returned 0 for all 21 — including the known-good — because of
|
||||
that one wrong assumption.** Third filter this week killed by its own control.
|
||||
|
||||
With the real shape (`addi r3, rF, K` before the copier, then stores at `K + d`
|
||||
on `rF`), word B is at `d = 1956 − 80 = 1876`, and **exactly one of the 21 hits**:
|
||||
|
||||
| function | copy dest | store at copy `+1876` |
|
||||
|---|---|---|
|
||||
| **`sub_8219F2E8`** (288 B) | `r31+560` | **yes — `2436(r31)`** |
|
||||
| `0x821C1630` (control) | `r31+96` | no — stores at `+0`, i.e. **word A** |
|
||||
| the other 19 | various | no |
|
||||
|
||||
`560 + 1876 = 2436` ✅.
|
||||
|
||||
#### 🔑 The `−24` is the proof
|
||||
|
||||
```asm
|
||||
lwz r11, 60(r11)
|
||||
addi r11, r11, -24 ; <-- index - 24
|
||||
lwz r10, 2436(r31) ; read copy word B
|
||||
slw r11, r27, r11 ; 1 << (index - 24)
|
||||
or r11, r11, r10
|
||||
stw r11, 2436(r31) ; write copy word B
|
||||
bl 0x8216FF70 ; setter (dest = r31+560)
|
||||
```
|
||||
|
||||
That is **§5.1's documented rule reproduced on the write side**: "`n >= 24` → bit
|
||||
`(n−24)` of word B". Word A's writer shifts by the raw stage number
|
||||
(`slw r10, r26, r10`, no bias); word B's shifts by `stage − 24`. **The two writers
|
||||
are exact complements**, so word B's bits 0–5 are challenge stages 24–29 — the
|
||||
same six the leaderboard's `MISSION_ID` names in §5.4.
|
||||
|
||||
**Where it is called from:** exactly one real caller, `0x821A0B1C` inside
|
||||
`0x821A0AD0` (1108 B), whose strings are **`BASE_INFO` + `DEBRIEFING`** — a
|
||||
debriefing screen. Clearing a challenge mission is recorded from the debriefing.
|
||||
|
||||
#### A second bitfield falls out
|
||||
|
||||
The same function also reads the copy at `+1880` (`2440(r31)` = **obj+1960**,
|
||||
the word after B), re-reads `obj+1960` after the setter, `xor`s old against new,
|
||||
and loops the 32 result bits calling `0x82175C20` with `bit + 64`. So `+1960` is
|
||||
a **second 32-bit progress bitfield with ids 64–95**, and newly-set bits are
|
||||
announced one at a time — an unlock-notification loop.
|
||||
|
||||
⚠️ **Not shown, against §5.3's own prediction:** neither `sub_8219F2E8` nor its
|
||||
caller references **`Time`** or **`Points`** (0 for both). §5.3 expected word B's
|
||||
writer to also store the stage's Time/Points record. **It does not — this is the
|
||||
bit-setter only.** The record store is still unlocated.
|
||||
|
||||
Reference in New Issue
Block a user