re: STAGENN_UNIT_MAX holds two per-stage caps, PLANE and VESSEL

Reading sub_82261F70 past the table lookup finishes the mechanism the
stage index feeds: the section name is built from the 0-based stage
index, looked up, and two ints land in the phase --

  [phase+356] = PLANE   (default 200)
  [phase+360] = VESSEL  (default 20)

with the defaults taken when a stage has no entry.  Key strings are
0x820A8B14 "PLANE" and 0x820A8B1C "VESSEL"; section and both keys are
the game's own names.  Surrounding reads (GP_SCRIPT, SCRIPTS,
Resource2D, TABLE) plus the pak census's 40 <?xm entries make this an
XML config reader.

Register discipline mattered twice: in sub_82261F70 r21 is the phase
and r31 is the STACK frame, and 0x82273400 lwz r11, 360(r11) in the
built-in switch is a vtable slot 90 call, not a phase read.

Not settled: the values (a crude zlib scan found no UNIT_MAX, but it
did not use the corpus's pak reader, so that is not evidence of
absence), and no consumer of the two fields was identified.

Docs only; artefacts byte-identical.
This commit is contained in:
Sylpheed RE agent
2026-08-27 11:14:17 +00:00
parent 17d1d2c999
commit ad5270cfdd
2 changed files with 71 additions and 0 deletions

View File

@@ -601,6 +601,32 @@ unknown, what evidence exists, and what the first step would be. Move an item in
`sub_824480D0` / `sub_82448AA0`), and whether `Stage30``Stage33` exist on the
disc at all — the table has 33 slots but only 28 scripts were extracted.
***(2026-08-27) `STAGENN_UNIT_MAX` IS TWO PER-STAGE UNIT CAPS — `PLANE` and
`VESSEL`. [structures/isl-condition-builtins](structures/isl-condition-builtins.md).**
Reading `sub_82261F70` past the table lookup finishes the mechanism the stage
index feeds. The section name is built from the 0-based stage index, the section
is looked up, and two ints land in the phase:
**`[phase+356] = PLANE`** (default **200**) and **`[phase+360] = VESSEL`**
(default **20**), the defaults taken when a stage has no entry
(`stw r11(=20), 360(r21)` / `stw r10(=200), 356(r21)`). Key strings are
`0x820A8B14 = "PLANE"` and `0x820A8B1C = "VESSEL"`. All three names — the
section and both keys — are the game's own. So a stage declares how many
fighters and how many capital ships it may hold.
✅ Surrounding reads name the config: `GP_SCRIPT`, `SCRIPTS`, `Resource2D`,
`TABLE`, and the pak census counts **40 `<?xm` entries** — an XML config reader
(`sub_824480D0` open, `sub_82448AA0` find-section, `sub_824482D0` read-int).
⚠️ **Register discipline mattered twice here.** In `sub_82261F70` `r21` is the
phase and **`r31` is the stack frame** — `lwz r4, 356(r31)` at `0x822624DC` is a
stack read, not a phase field. And `0x82273400 lwz r11, 360(r11)` in the
built-in switch is a **vtable slot 90 call** (built-in 123), not a read of
`[phase+360]`.
🟡 Not settled: the **values**. A crude `Z1`+zlib scan of `dat/*.pak` found no
`UNIT_MAX`, but it did not use the corpus's own pak reader (`unitgroup.py`), so
that is **not** evidence of absence. 🟡 No consumer of `[phase+356]`/`[phase+360]`
identified — the offsets recur and the plausible hit was the vtable call above.
🟡 Whether `Stage30``Stage33` exist on disc (33 table slots, 28 scripts) is
still open.
## ✅✅ SOLVED — the mission freeze was a modal sign-in dialog (2026-08-26)
`XamShowSigninUI` opens a modal dialog and `xeXamDispatchDialog` blocks the

View File

@@ -290,6 +290,51 @@ the built-in switch looks like a read of this field. It is not: `r11` had just
been loaded from `0(r31)`, so it is the **vptr** — that instruction is a virtual
call to **slot 38**. Offset 152 recurs; the base register decides.
## ✅ (2026-08-27) What `STAGENN_UNIT_MAX` is: two per-stage unit caps, `PLANE` and `VESSEL`
The lookup in `sub_82261F70` continues straight past the table read. `r21` is the
phase there; `r31` is the **stack frame** (`addi r4, r31, 452`, `lwz r11, 376(r31)`
— easy to misread as phase fields).
```
82262308 bl 0x824480D0 ; open the config node -> [r31+84]
8226230C lwz r11, 152(r21) ; the 0-based stage index
8226231C lwzx r4, r11, r10 ; "STAGE(N+1)_UNIT_MAX"
82262320 bl 0x82448AA0 ; find that section
82262328 bc 4, eq, 0x82262340 ; found -> read two keys
; NOT found — defaults:
82262334 stw r11(=20), 360(r21)
82262338 stw r10(=200), 356(r21)
; found:
82262348 addi r4, r11, -29932 ; 0x820A8B14 = "PLANE"
82262360 stw r11, 356(r21)
8226235C addi r4, r10, -29924 ; 0x820A8B1C = "VESSEL"
82262368 stw r3, 360(r21)
```
| phase field | config key | default |
|---|---|---|
| `[phase+356]` | **`PLANE`** | **200** |
| `[phase+360]` | **`VESSEL`** | **20** |
So the section is a per-stage pair of **unit caps** — planes (fighters) and
vessels (capital ships) — with the section name built from the stage index and
the whole thing falling back to 200/20 when a stage has no entry. All three names
are the game's own.
The surrounding reads name the config itself: `GP_SCRIPT` (`0x820A8244`),
`SCRIPTS`, `Resource2D`, `TABLE`. The pak census counts **40 `<?xm` entries**, so
this is an XML config reader (`sub_824480D0` open, `sub_82448AA0` find-section,
`sub_824482D0` read-int, `sub_82448C50` / `sub_824490E0` others).
🟡 **The values themselves are not confirmed from the data side.** A crude
`Z1`+zlib scan over `dat/*.pak` found no `UNIT_MAX` — but that scan did not use
the corpus's own pak reader (`unitgroup.py`), so it is **not** evidence of
absence. 🟡 No reader of `[phase+356]` / `[phase+360]` was identified: the
offsets recur, and the one ISL-region hit that looked promising
(`0x82273400 lwz r11, 360(r11)`) is a **vtable slot 90 call**`r11` is the
vptr — which is built-in 123, consistent with the known mapping.
🟡 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