re(challenge): the part id is never persisted -- differential search says stack only
No literal 26 exists anywhere, so the GamePart id is computed. That does not stop it being found: the id is KNOWN at each screen from the GamePart table (EXTRAS = 5, MISSION SELECT = 7), so snapshot both and intersect. New tool diff_words.py does the classic differential search over the sparse guest image, and find_partslot.sh drives the two screens and runs it. Result: 171 MB scanned, exactly 4 addresses read 5 then 7 -- 0x708FFBEC, 0x708FFCBC, 0x708FFDAC, 0x708FFE20 -- and all four are guest STACK (the same run's log puts thread stacks at 0x709...). So the requested part id exists only as a stack argument in flight; there is no persistent field, which is consistent with finding no literal store, and means there is nothing stable to poke. That closes the last memory-and-menu route to the challenge missions. Reaching them needs either the genuine in-game unlock (an in-mission attainment, per AVSCRIPT_COMMAND_ATTAINMENT_CHALLENGE_MISSION_CARGO_SCORE) or an emulator-side hook that forces the transition -- a code change, not a poke. diff_words.py is worth keeping well beyond this question: it locates any field whose address is unknown but whose value is known at two moments.
This commit is contained in:
@@ -385,6 +385,30 @@ against the profile route. `+80` at least is handled as a small struct by addres
|
||||
(`addi r4, obj, 80` → copy helper `0x82175110`, written back via `0x8216FF70`), which
|
||||
is what a serialised value object looks like.
|
||||
|
||||
### 5.8 The part id is never persisted — differential search ✅ (negative)
|
||||
|
||||
`sub_821749C0` creates a part from `slot+12`, and no literal 26 exists anywhere, so
|
||||
the id is computed. It can still be found by **differential search**, because the id
|
||||
is *known* at each screen from §3: `GP_EXTRAS` = 5, `GP_MISSION_SELECT` = 7. Snapshot
|
||||
both screens and intersect (`tools/re-capture/diff_words.py`):
|
||||
|
||||
```
|
||||
scanned 171 MB of allocated guest memory
|
||||
addresses reading 5 on EXTRAS and 7 on MISSION SELECT: 4
|
||||
0x708FFBEC 0x708FFCBC 0x708FFDAC 0x708FFE20
|
||||
```
|
||||
|
||||
**All four are guest stack** (thread stacks sit at `0x709…` in the same run's log).
|
||||
So the requested part id exists only as a **stack argument in flight** — there is no
|
||||
persistent field holding it, which is consistent with finding no literal store and
|
||||
means **there is nothing stable to poke**. Forcing a transition to GamePart 26 needs
|
||||
the caller's context, i.e. an emulator-side hook rather than a memory write.
|
||||
|
||||
**Every memory-and-menu route to the challenge missions is now closed** (§5.4, §5.6,
|
||||
§5.7, this section). What remains is the genuine in-game unlock — an in-mission
|
||||
attainment, per `AVSCRIPT_COMMAND_ATTAINMENT_CHALLENGE_MISSION_CARGO_SCORE` — or a
|
||||
Canary patch that forces the transition in emulator code.
|
||||
|
||||
## 6. The unlock condition, from the strings ❔
|
||||
|
||||
Three strings say challenge missions are *announced*, not menu-browsed:
|
||||
|
||||
Reference in New Issue
Block a user