re: the (16,32] gate is != 16 && <= 32, a validity check on a load parameter

Corrects this corpus's own notation: there is no lower bound, values
below 16 pass.

sub_8225EC78(X, kind, ...) -- the function carrying "script load
cancel" -- stores its second argument into X+12 and applies the same
two tests to it immediately, bailing out of the load.  So the gate is
the object's invariant, re-checked at every later touch, not a
selector.  42 sites image-wide have the shape, 34 of them in one code
region plus 3 in sub_8225EC78.

Object chain: X = [GamePart+104] is the current script instance
(installed at 0x821A78EC, previous one torn down via sub_8225EB60);
X+4 -> Y; Y+4 -> the ScriptPhase; Y+72 and [ScriptPhase+10152] get
the answer.

Refutes my own lead in the same pass: "X+12 is a GamePart id and 16 is
the unregistered one" fails its control -- 1, 2 and 18 are also absent
from the RegisterToFactory list, so 16 is one of four gaps.

Docs only; artefacts byte-identical.
This commit is contained in:
Sylpheed RE agent
2026-08-27 09:48:36 +00:00
parent 237dbce155
commit 2c5c8b11d9
2 changed files with 71 additions and 0 deletions

View File

@@ -450,6 +450,34 @@ unknown, what evidence exists, and what the first step would be. Move an item in
System 15, MainGame 17, PauseMenu 19, StageClear 20, MissionLog 21, GameOver 22,
Debriefing 23, Dialog 24, Tutorial 25, ChallengeMission 26, Leaderboard 27.
* ✅🔴 **(2026-08-27) THE `(16,32]` GATE IS READ — it is `!= 16 && <= 32`, a
VALIDITY CHECK on a load-time parameter, and my own GamePart-id lead is killed
by its control. [structures/isl-condition-builtins](structures/isl-condition-builtins.md).**
🔴 **The corpus's own notation was wrong.** `(16, 32]` implies a lower bound;
there is none — everything **below 16 passes**. The test is
`kind != 16 && kind <= 32`.
**`X+12` is a constructor argument, not engine state.** `sub_8225EC78(X,
kind, …)` — the function carrying `"script load cancel"` — does
`stw r4, 12(r30)` and then applies **those same two tests to `r4`**, bailing
out of the load. Every later site re-tests the field it just stored, so the
gate is the object's **invariant**, not a selector between behaviours. Control:
**42 sites image-wide** have this exact `cmpi 16``cmpi 32` shape, **34 in one
region** (`sub_821A9DC8`, `sub_821AA1B0`, `sub_821AB570`, `sub_821AB650`) plus 3
in `sub_8225EC78` — one object, guarded everywhere it is touched.
**The object chain is read.** `X` is installed at `0x821A78EC`
(`stw r25, 104(r30)`), the previous instance torn down first via
`sub_8225EB60` — so **`X = [GamePart+104]` is the game part's CURRENT SCRIPT
INSTANCE**. `X+4``Y`; `Y+4` → the **ScriptPhase**; `Y+72` = the answer, and
`[ScriptPhase+10152]` gets the same value. `X+12` arrives as `[r21+8]`, `r21`
being `sub_821A6CF0`'s second argument.
🔴 **REFUTED, my own lead, in the same pass.** GamePart ids run 0…27 and **16 is
not among them**, which made "`X+12` is a GamePart id, 16 being the unregistered
one" very inviting. Its control kills it: **1, 2 and 18 are also missing** from
the `RegisterToFactory` list, so 16 is one of *four* gaps, not a unique one.
Not adopted.
🟡 Not settled: what the kind **means**. The gate is read; the value's domain is
not, and chasing it needs another hop up (`sub_821A6CF0`'s caller).
## ✅✅ SOLVED — the mission freeze was a modal sign-in dialog (2026-08-26)
`XamShowSigninUI` opens a modal dialog and `xeXamDispatchDialog` blocks the

View File

@@ -134,6 +134,49 @@ left unnamed rather than named off an unverified premise.
and exactly one reference in the image — a tail `b` from `0x821AC064` — so the
string recipe finds nothing there.
## 🔴✅ (2026-08-27) The gate is `!= 16 && <= 32` — and it is a VALIDITY CHECK, not a selector
🔴 **First, a correction to this page's own notation.** Written as `(16, 32]` it
implies a lower bound. There is none:
```
8225EC90 cmpi cr6, 0, r4, 16
8225EC94 stw r4, 12(r30) ; <- the field IS the argument
8225EC98 bc 12, eq, <bail> ; == 16 -> bail
8225EC9C cmpi cr6, 0, r4, 32
8225ECA0 bc 12, gt, <bail> ; > 32 -> bail
```
Everything **below** 16 passes. The condition is `kind != 16 && kind <= 32`.
**`X+12` is a constructor parameter, not engine state.** `sub_8225EC78(X,
kind, …)` — the function carrying the string `"script load cancel\n"` — stores
its second argument into `+12` and applies the identical pair of tests to it
immediately, bailing out of the load. Every later occurrence re-tests the field
it just stored. So the gate is that object's **invariant**, re-checked wherever
it is touched, not a switch selecting between behaviours.
**Where it is checked**: 42 sites image-wide with this exact `cmpi 16`
`cmpi 32` shape, **34 of them in one code region**`sub_821A9DC8`,
`sub_821AA1B0`, `sub_821AB570`, `sub_821AB650` — plus 3 in `sub_8225EC78`. One
object, guarded everywhere.
**And the object chain is now read.** `X` is installed at `0x821A78EC`
(`stw r25, 104(r30)`), the previous one torn down first through `sub_8225EB60`;
so `X = [GamePart+104]` is **the game part's current script instance**. From
there `X+4``Y`, and `Y+4` → the **ScriptPhase**, `Y+72` = the answer, with
`[ScriptPhase+10152]` receiving the same value. `X+12`'s value arrives as
`[r21+8]`, `r21` being `sub_821A6CF0`'s own second argument.
🔴 **A tempting reading, killed by its control.** GamePart ids (from the
`RegisterToFactory<N,…>` strings) run 0…27, and **16 is not among them** — which
makes "`X+12` is a GamePart id and 16 is the unregistered one" very inviting.
It fails: **1, 2 and 18 are also absent** from that list, so 16 is one of *four*
gaps, not a unique one. Nothing distinguishes it. The reading is not adopted.
🟡 So what the kind **means** is still open — the gate is now read, but the
value's domain is not.
🟡 Its neighbours belong to the same cluster: `builtin103` reads
`[phase+10156]` and `[phase+10152]` (9 and 7 writers), and a sibling vtable stub
clears `[phase+10152]`. The shape is an engine→script status trio, but that is a