diff --git a/docs/re/BACKLOG.md b/docs/re/BACKLOG.md index 00cab093..0af92849 100644 --- a/docs/re/BACKLOG.md +++ b/docs/re/BACKLOG.md @@ -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. diff --git a/docs/re/challenge-mission-gate.md b/docs/re/challenge-mission-gate.md index c072b382..e1bfa0e1 100644 --- a/docs/re/challenge-mission-gate.md +++ b/docs/re/challenge-mission-gate.md @@ -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.