Item (a): mine the screen population found last iteration. Resolving every one's key list via the string-xref join over its own body gives 298 distinct keys and a three-way split: 95 = 9 corpus-known + 48 new with a readable key list + 38 bare, where "bare" means BASE_INFO and nothing else. The bare 38 are the honest limit of the instrument - a screen whose only catalogued string is BASE_INFO cannot be named this way. The control turned the finding into a law. Only 9 of the 19 loaders the corpus documents appear among the 95, and which nine is not arbitrary. The nine that reference BASE_INFO all read a SCREEN's own configuration: debriefing, career, save, medals, evaluation list, gamma, POINT_RANKING, CLEAR_TIME_RANKING, hangar. The ten that do not all read a DATA TABLE: unit definitions, weapon datasheet, AIParams, stage settings, chatter, PlayerParams, ControlTweak, the PG* HUD roster, material slots, and the leaderboard. 9/9 and 10/10, both off-diagonals empty. The leaderboard falling on the table side is a check rather than an exception - its own doc records that all 18 of its names are tables.pak names, so it is a table reader that happens to paint a screen. That exclusion is what last commit's "universal screen marker" claim was missing. 48 new screens are named by their most distinctive keys, including the title screen (sub_821C4EB0: TITLE_SCREEN/TITLE_MENU/LOADING), the ready room (sub_821D6D28: SCRIPT_ID/SCRIPT_PATH, confirmed independently by its own error string silph::GamePart_ReadyRoom::Impl::PrepareScript), movie subtitles, the three menu modes STANDARD/EXTRA/TUTORIAL, the speaker window, loadout and gallery views, mission select, and an equip screen carrying the game's own typo EQUIIP_LIMITATION. One caution worth recording: a high key count is not a rich screen. The two 27-key entries sub_82297550 and sub_822A2F00 resolve to pure coordinate pairs (1080,163 / 734,502 / 1164,403) - a layout table, not configuration. Read the values before trusting the rank. Still open: the 38 bare screens, and whether the EX_ in EX_MENU_DISABLE_SKIP and EX_LETTERS is the same EXTRA mode word as +184. All seventeen artefacts byte-identical.
355 lines
18 KiB
Markdown
355 lines
18 KiB
Markdown
# ✅ The debriefing and pilot-record screens — `STAGE_RESULT`, `OVERVIEW`, `EX_OVERVIEW`
|
|
|
|
- **Where:** `tables.pak` (the menu config pak), records `STAGE_RESULT`, `OVERVIEW`,
|
|
`EX_OVERVIEW`; key lists compiled into the executable at `sub_822814D8` and
|
|
`sub_8227A3A0` (found by the base-solver, [[player-tuning-tables]]).
|
|
- **Related:** [[mission-scoring]] owns the *rules* (`Score_*` in the stage
|
|
settings); this is the *readout*. [[savegame-format]] documents the same
|
|
compiled-key-list shape for the LOAD/SAVE screen.
|
|
|
|
## The shape was already known — for other screens
|
|
|
|
[[savegame-format]] establishes it: *"the LOAD/SAVE screen's config key list is
|
|
compiled into the executable, as a pointer array of key strings — the same shape
|
|
as the Arsenal's (whose keys sit a few hundred bytes earlier and match the pak
|
|
record exactly)"*, run starting `0x820A0074`. **`sub_82286BC8`'s 18-name block is
|
|
exactly that list** — 17 of its 18 are a `tables.pak` field or record name, so it
|
|
is the documented save-screen list and nothing new. Its lone residual is
|
|
**`PLAYER_AMMO_LESS_10`**, which is not a `tables.pak` name at all.
|
|
|
|
## ✅ Two more screens, and the key lists match their records exactly
|
|
|
|
| block | names | all present in `tables.pak`? |
|
|
|---|---:|---|
|
|
| `sub_822814D8` | 24 | **24 / 24** |
|
|
| `sub_8227A3A0` | 21 | **21 / 21** |
|
|
|
|
And the arithmetic closes against the records themselves:
|
|
|
|
* **`sub_822814D8` = the debriefing screen.** 24 names = 2 screen ids
|
|
(`STAGE_RESULT`, `EX_STAGE_RESULT`) + **21 `stage_*` fields** + one sound cue
|
|
(`SE_BOSS_CORE_CHARGE`). The `tables.pak` record `STAGE_RESULT` has **exactly
|
|
21 fields** (2 objects, both 21).
|
|
* **`sub_8227A3A0` = the pilot record / career screen.** 21 names = 5 screen ids
|
|
(`LAST_RESULT`, `EX_BASE`, `EX_MAIN`, `EX_OVERVIEW`, `OVERVIEW`) + **7
|
|
`ex_overview_*`** + **9 `overview_*`**. The records `EX_OVERVIEW` and
|
|
`OVERVIEW` have **exactly 7 and 9 fields** (2 objects each).
|
|
|
|
`2+21+1 = 24` and `5+7+9 = 21`, with 21/7/9 measured independently off the pak.
|
|
|
|
### The debriefing readout, in full
|
|
|
|
```
|
|
stage_num_shoot_down_aircrafts stage_points_shoot_down_aircrafts
|
|
stage_num_shoot_down_ships stage_points_shoot_down_ships
|
|
stage_points_shoot_down_others
|
|
stage_weight_shoot_down
|
|
stage_num_main_objectives stage_points_main_objectives
|
|
stage_num_sub_objectives stage_points_sub_objectives
|
|
stage_clear_time stage_points_clear_time
|
|
stage_shoot_down_ratio stage_points_shoot_down_ratio
|
|
stage_damages_friendly_ships stage_points_damages_friendly_ships
|
|
stage_damages_wingman stage_points_damages_wingman
|
|
stage_points_friendly_fire
|
|
stage_total_points stage_rank
|
|
```
|
|
|
|
🔑 **Nine `num`/`points` pairs plus three points-only lines** — the screen shows
|
|
a raw count *and* its score contribution for kills by class, objectives, time,
|
|
ratio and both damage categories; `friendly_fire`, `shoot_down_others` and
|
|
`weight` have no counter column. That is the same partition
|
|
[[mission-scoring]] measures on the settings side.
|
|
|
|
### The career readout
|
|
|
|
`overview_points`, `_total_play_time`, `_clear_stages`, `_retry_times`,
|
|
`_shoot_down_aircrafts`, `_shoot_down_ships`, `_shoot_down_weight` — seven, and
|
|
`OVERVIEW` adds **`overview_rank`** and **`overview_medals`**. The `EX_` twin has
|
|
the seven and neither of the two, which is what makes `EX_` the reduced variant
|
|
rather than a different screen.
|
|
|
|
🟡 Not settled: what `EX_` means (a second profile? the online/leaderboard
|
|
variant?) — **but see
|
|
[stage-numbering-and-player-craft](stage-numbering-and-player-craft.md):
|
|
`_EXn` on an asset name is measured to mean "the n-th challenge mission", which
|
|
makes "`EX_` = the challenge-mission debriefing" a consistent reading of why
|
|
this twin drops `overview_rank` and `overview_medals`. Not adopted — the
|
|
selection has not been shown** — `EX_BASE`, `EX_MAIN`, `EX_STAGE_RESULT` and `EX_FONT` are keys with
|
|
**no record of that name**, so the prefix is a screen-id convention, not a table.
|
|
And `PLAYER_AMMO_LESS_10` is unexplained.
|
|
|
|
**Reproduce:** parse `tables.pak` with `tools/re-capture/unitgroup.py` and count
|
|
the fields of the three records; take the key lists from the `sub_822814D8` /
|
|
`sub_8227A3A0` / `sub_82286BC8` sections of [`../data/name-block-bases.txt`](../data/name-block-bases.txt).
|
|
|
|
## ✅ The `EX_` selection, shown at last — a mode word tested against 3
|
|
|
|
The section above left this open: "**the selection has not been shown**". It is
|
|
shown now. Both screens that reference `EX_` names carry the *same five-instruction
|
|
idiom* immediately before the choice — `sub_822814D8` at `0x822815b4` and
|
|
`sub_8227A3A0` at `0x8227a408`:
|
|
|
|
```asm
|
|
lwz r11, 4(rX) ; the owning object
|
|
lwz r11, 184(r11) ; a mode word at +184
|
|
addi r11, r11, -3
|
|
cntlzw r11, r11
|
|
rlwinm r11, r11, 27, 31, 31 ; r11 = 1 iff [+184] == 3
|
|
cmpli cr6, 0, r11, 0x0
|
|
bc 12, 4*cr6+eq, <plain> ; not 3 -> STAGE_RESULT / OVERVIEW
|
|
; else -> EX_STAGE_RESULT / EX_OVERVIEW
|
|
```
|
|
|
|
`cntlzw` of zero is 32; rotating left 27 lifts bit 5 into bit 31, so the sequence
|
|
is a branchless **"equals 3"** test. The equal-to-zero branch takes the *plain*
|
|
name, so **`EX_` is chosen exactly when the word is 3**.
|
|
|
|
A small partition over the three key-list loaders the corpus already names:
|
|
|
|
| | loads `+184` | does not |
|
|
|---|---|---|
|
|
| **references `EX_` names** | **2** | 0 |
|
|
| **does not** | 0 | **1** |
|
|
|
|
`sub_822814D8` and `sub_8227A3A0` both load it; `sub_82286BC8`, which references
|
|
no `EX_` name, has no `+184` access at all. Both off-diagonal cells are empty.
|
|
|
|
### 🔑 3 is the game's own `EXTRA` — and that is *not* `CHALLENGE`
|
|
|
|
[[challenge-mission-gate]] already established the vocabulary for this constant
|
|
at a three-way switch: `== 3` → **`EXTRA`**, `== 5 or 6` → **`CHALLENGE`**,
|
|
otherwise `FILE`. So the prefix reads literally: **`EX_` is the `EXTRA` variant**.
|
|
|
|
⚠️ **This refines the tentative reading above rather than confirming it.** That
|
|
section proposed "`EX_` = the challenge-mission debriefing" by analogy with
|
|
`_EXn` on asset names. The measured constant is `EXTRA`, which in the game's own
|
|
three-way vocabulary is a *different* kind from `CHALLENGE` (5/6). `EX_` tracks
|
|
`EXTRA` specifically.
|
|
|
|
⚠️ **The boundary of what was measured:** the `EXTRA = 3` constant is established
|
|
at **`+144`** of the stage-loader class, while the word tested here is at
|
|
**`+184`** of `[object + 4]`. Same constant, same `EX`/`EXTRA` naming, two
|
|
different offsets in what may be two different objects — **that the two fields
|
|
are the same field is not shown**, only that both compare a mode word against 3.
|
|
|
|
## 🔴 Who writes `+184` — blocked, by two independent instrument failures
|
|
|
|
The next question after the selector is what *supplies* the mode. Two routes
|
|
were tried and both are measured to have no power here.
|
|
|
|
**The offset route has no power.** `stw …, 184(rN)` occurs **301 times** in the
|
|
image, `lwz` from `+184` **351 times**, and **115 functions touch both `+144`
|
|
and `+184`**. `+184` is an ordinary small offset shared by many unrelated
|
|
classes — the same shape that the corpus already recorded as failing three
|
|
times. Nothing here narrows to a writer.
|
|
|
|
**❌ The `xrefs` `ind_call` edges are a cross product — do not use them as
|
|
callers.** Asking for the callers of the two screens returns **633 sources for
|
|
each, and the two lists are identical**, which cannot be right. Measuring the
|
|
relation itself:
|
|
|
|
| | |
|
|
|---|---|
|
|
| `ind_call` rows | **1 827 297** |
|
|
| distinct targets | 1 710 |
|
|
| distinct sources | 6 992 |
|
|
| **targets with *exactly* 633 sources** | **236** |
|
|
|
|
236 different functions sharing the identical source count is the signature of
|
|
an unresolved-indirect-call cross product, not of a call graph. ⚠️ **Any reading
|
|
that treats an `ind_call` edge as "X calls Y" is void**, here and elsewhere in
|
|
the corpus.
|
|
|
|
⚠️ The control shows the *other* kinds are fine: `sub_82286BC8` has exactly
|
|
**one** caller, kind `call`. And both `EX_` screens have **zero** non-`ind_call`
|
|
edges — they are reached only through function pointers, which is why the direct
|
|
graph is empty for them.
|
|
|
|
### ✅ What did come out: both `EX_` screens are slot 1 of their own class
|
|
|
|
Scanning the 1 150 catalogued vtables' entries in the flat `.pe`:
|
|
|
|
| function | vtable | slot |
|
|
|---|---|---|
|
|
| `sub_822814D8` (debriefing) | `ANON_Class_271D5F25` | **1** |
|
|
| `sub_8227A3A0` (career) | `ANON_Class_CAA8AD62` | **1** |
|
|
| `sub_82286BC8` (save) | *in no catalogued vtable* | — |
|
|
|
|
So each `EX_`-selecting screen is a class whose **slot-1 virtual** is its
|
|
key-list builder, and the third — which selects nothing — is not a vtable method
|
|
at all. That is consistent with the 2 / 0 / 0 / 1 partition above arriving from a
|
|
structural difference rather than a coincidence.
|
|
|
|
🔴 **Still not settled: what writes `+184`.** Both the offset sweep and the call
|
|
graph are exhausted for it. A route with actual power would be the RTTI class
|
|
behind `ANON_Class_271D5F25` / `_CAA8AD62`, or a runtime watch on the field —
|
|
not another static offset search.
|
|
|
|
### ✅ How far the `ind_call` damage reaches — bounded, and the corpus is clean
|
|
|
|
The refutation above is corpus-wide in principle, so the next question is which
|
|
existing claims it voids. Answer: **none of the ones the corpus makes.**
|
|
|
|
**Every caller-count claim in the corpus checks out against `call` edges, with
|
|
zero `ind_call` contamination:**
|
|
|
|
| doc | claim | `call` | `ind_call` |
|
|
|---|---|---|---|
|
|
| `title-crash-stl-tree` | `sub_82457780` "one caller" | **1** | 0 |
|
|
| `isl-trigger-queue` | `sub_8226EAB8` "16 callers" | **16** | 0 |
|
|
| `isl-coroutine-spawner` | `sub_822737C8` "seven callers" | **7** | 0 |
|
|
| `challenge-mission-gate` | `0x82175110` "22 callers" | **22** | 0 |
|
|
|
|
4 of 4 exact, and none of the four has a single `ind_call` edge.
|
|
|
|
**And there is a structural reason, not luck.** Partitioning the two target
|
|
populations:
|
|
|
|
| | count |
|
|
|---|---|
|
|
| targets with `ind_call` edges | 1 710 |
|
|
| targets with `call` edges | 12 170 |
|
|
| **both** | 103 |
|
|
| `ind_call` only | 1 607 |
|
|
| `call` only | 12 067 |
|
|
|
|
🔑 **All 1 710 `ind_call` targets are vtable entries — 1 710 of 1 710, zero
|
|
partials.** (Against a control: only 273 of the 12 170 `call` targets are.) So
|
|
`ind_call` fires *only* on virtual methods, and **1 607 of them have no direct
|
|
caller at all** — which is exactly why the two `EX_` screens came back empty on
|
|
`call` and flooded on `ind_call`.
|
|
|
|
⚠️ **The residual risk, named precisely:** the **103** targets carrying *both*
|
|
kinds. There a caller count taken without a `kind` filter would be inflated by
|
|
~633. Everywhere else the two populations are disjoint, so a `call`-based count
|
|
is safe and an `ind_call`-based one is meaningless.
|
|
|
|
**Rule for future work:** always filter `kind='call'`; and when a function has no
|
|
`call` edges, that is a *fact about it* — it is a virtual method — not a gap to
|
|
be filled from `ind_call`.
|
|
|
|
### ❌ The RTTI route is dead — and ✅ what the vtable scan gave instead
|
|
|
|
Last iteration proposed "the RTTI behind those two anonymous classes" as the next
|
|
route with power. **Measured, it does not exist.**
|
|
|
|
| over all 1 150 catalogued vtables | |
|
|
|---|---|
|
|
| `class_name` starting `ANON_` | **1 150 of 1 150** |
|
|
| `rtti_present` true | **0** |
|
|
| non-empty `base_classes_json` | **0** |
|
|
|
|
There is not one named class, not one RTTI flag, and not one base-class edge in
|
|
the whole table. ⚠️ **Withdrawn: my own "next route" from last turn was
|
|
unrunnable.** The `ANON_Class_XXXXXXXX` labels are synthesised, not recovered
|
|
names.
|
|
|
|
✅ **Re-verified, though**: reading the two vtables at their true addresses
|
|
(`0x820A9760`, `0x820A9350`) confirms `sub_822814D8` and `sub_8227A3A0` are each
|
|
**slot 1, uniquely**, and `sub_82286BC8` is in no vtable.
|
|
|
|
❌ **The sibling methods cannot name the class either.** Slots 0, 2, 3 and 4 of
|
|
both classes reference **zero** strings. Every naming datum in both classes sits
|
|
in slot 1 — the method the corpus already had.
|
|
|
|
#### ✅ But the shape found eight more screens
|
|
|
|
Both classes are 5-slot with an exactly-88-byte slot 0. Filtering the whole
|
|
table on that shape: 83 vtables have 5 slots, **16** also have the 88-byte slot
|
|
0, 13 of those have a catalogued slot 1, and **10 of the 13 build a key list**.
|
|
Ranked by key count, the two known screens are at the top and the rest name
|
|
themselves:
|
|
|
|
| class | slot 1 | its key vocabulary |
|
|
|---|---|---|
|
|
| `ANON_Class_271D5F25` | `sub_822814D8` | `EX_STAGE_RESULT` / `STAGE_RESULT` — **the debriefing** |
|
|
| `ANON_Class_CAA8AD62` | `sub_8227A3A0` | `EX_OVERVIEW` / `OVERVIEW` / `LAST_RESULT` — **the career screen** |
|
|
| `ANON_Class_5FFD2641` | `sub_82278960` | `MEDALS`, `ACHIEVED_MEDALS`, `NEW_MEDAL`, `DETAIL_NEW` |
|
|
| `ANON_Class_8501FBF7` | `sub_82275F80` | `EVALUATION_LIST`, `eval_%02d_eval`, `eval_%02d_points` |
|
|
| `ANON_Class_B09C08C7` | `sub_822B4838` | `GAMMA_RGB`, `GAMMA_WB` |
|
|
| `ANON_Class_9795AB66` | `sub_8227F6E0` | **`POINT_RANKING`** |
|
|
| `ANON_Class_A7CB2C37` | `sub_82282C28` | **`CLEAR_TIME_RANKING`** |
|
|
| `ANON_Class_7EE7DC6F` / `431702E3` / `22F09B71` | `sub_822B6F60` / `sub_822B84A8` / `sub_822B9B78` | `BASE_INFO` only |
|
|
|
|
🔑 **`POINT_RANKING` and `CLEAR_TIME_RANKING` are two separate screens** — a
|
|
direct lead for the open item "which challenge stage is which leaderboard mode":
|
|
the leaderboard is ranked two ways, and each way has its own class.
|
|
|
|
⚠️ Note `eval_%02d_eval` — a **printf numeric wildcard**, the code-side analogue
|
|
of the datasheet schema's `???` suffix wildcard.
|
|
|
|
#### ❌ …but `BASE_INFO` is NOT this family's signature
|
|
|
|
The obvious next claim — "`BASE_INFO` marks the family" — is false, and the
|
|
control says so plainly. `BASE_INFO` is a single string (`0x820A20EC`) and
|
|
**95 functions reference it: 10 inside the family, 85 outside** (`95 = 10 + 85`).
|
|
Among the outsiders are `sub_82286BC8` (the save screen, in no vtable at all) and
|
|
`sub_822AE628` (the hangar loader).
|
|
|
|
So the direction inverts: **`BASE_INFO` is the universal first key of every
|
|
screen key list — a 95-function population** — while the 5-slot/88-byte shape is
|
|
only a **weak** filter that happens to catch 10 of them (and 6 non-screens). Use
|
|
`BASE_INFO` to enumerate screens; do not use the vtable shape.
|
|
|
|
### ✅ The 95 `BASE_INFO` functions, partitioned — and what `BASE_INFO` actually discriminates
|
|
|
|
Mining the population found last iteration. Resolving every one's key list (the
|
|
string-xref join over the function's own body) gives **298 distinct keys** and a
|
|
three-way split:
|
|
|
|
| | |
|
|
|---|---:|
|
|
| already documented in the corpus | **9** |
|
|
| **new**, with a readable key list | **48** |
|
|
| **bare** — `BASE_INFO` and nothing else | **38** |
|
|
|
|
`9 + 48 + 38 = 95`. The bare 38 are the honest limit of this instrument: a screen
|
|
whose only catalogued string is `BASE_INFO` cannot be named from its key list.
|
|
|
|
#### 🔑 The control turned it into a law
|
|
|
|
Only **9 of the 19** loaders the corpus documents appear among the 95. Reading
|
|
which nine, against which ten, the split is not arbitrary:
|
|
|
|
| references `BASE_INFO` (9 of 9) | does **not** (10 of 10) |
|
|
|---|---|
|
|
| debriefing, career, save, medals, evaluation list, gamma, `POINT_RANKING`, `CLEAR_TIME_RANKING`, hangar | unit definitions, weapon datasheet, `AIParams`, stage settings, chatter, `PlayerParams`, `ControlTweak`, `PG*` HUD roster, material slots, **leaderboard** |
|
|
|
|
✅ **`BASE_INFO` separates a screen's own config from a data-table read — 9/9 and
|
|
10/10, both off-diagonals empty.** Every function in the left column reads the
|
|
configuration of a *screen*; every one in the right reads a *table*
|
|
(`tables.pak`, a datasheet). The leaderboard sits on the right, correctly — its
|
|
own doc records that all 18 of its names are `tables.pak` names, so it is a table
|
|
reader that happens to paint a screen.
|
|
|
|
That is what last iteration's "universal screen marker" claim needed: a
|
|
population it *excludes*.
|
|
|
|
#### What the 48 new screens are
|
|
|
|
Named by their most distinctive keys:
|
|
|
|
| function | keys | reading |
|
|
|---|---|---|
|
|
| `sub_821C4EB0` | `TITLE_SCREEN`, `TITLE_MENU`, `LOADING`, `LOADING2` | the title screen |
|
|
| `sub_821D6D28` | `SCRIPT_ID`, `SCRIPT_PATH` + its own error string `silph::GamePart_ReadyRoom::Impl::PrepareScript` | **the ready room** |
|
|
| `sub_8219F6D0` | `LIVE_BOARD`, `LOCAL_BOARD`, `LIVE_FOCUS`, `LOCAL_FOCUS`, `DEBRIEFING`, `DETAIL` | the board/focus selector |
|
|
| `sub_821B26F8`, `sub_821B1448` | `NO_RECORD`, `RECORD_DIFFICULTY`, `%02d:%02d.%02d` | record display, with a time format |
|
|
| `sub_821BDF88`, `sub_821BDBB0` | `STANDARD_MENU`, `EXTRA_MENU`, `TUTORIAL_MENU` | the three menu modes |
|
|
| `sub_821AE188`, `sub_821AED00` | `SPEAKER_FONT`, `SPEAKER_HEIGHT`, `IN_DEMO`, `WINDOW` | the speaker/dialogue window |
|
|
| `sub_821B55D8` | `SUBTITLE_FONT`, `SUBTITLE_Y` + `silph::SoundManager::Impl::SetMovieM…` | movie subtitles |
|
|
| `sub_8229F018` | `PlayerLOD`, `PlayerMotion`, `PlayerPackage`, `MODEL_PATH` | the player-model viewer |
|
|
| `sub_8229E878`, `sub_822A8EF0` | `PlayerUnit`, `WEAPON`, `WEAPONS`, `UnitID` | loadout views |
|
|
| `sub_82299448`, `sub_822A46B8` | `WEAPON_CATEGORIES`, `OBJ_GALLERY`, `OBJ_MENU_DELTA_SABER`, `MAP_PYLON` | the gallery / arsenal menu |
|
|
| `sub_821849E8` | `CHALLENGE`, `NEW_ITEM`, **`EQUIIP_LIMITATION`** | an equip screen — note the game's own typo, a join key |
|
|
| `sub_8218AE58` | `MISSIONS`, `MISSION_ID`, `STRINGS` | mission select |
|
|
| `sub_82198120`, `sub_82198808` | `MENU_ENABLE_SKIP`, `MENU_DISABLE_SKIP`, `EX_MENU_DISABLE_SKIP` | skip gating, with its own `EX_` variant |
|
|
|
|
⚠️ **Two of the 27-key entries are not key lists at all.** `sub_82297550` and
|
|
`sub_822A2F00` resolve to pure coordinate pairs — `1080,163`, `734,502`,
|
|
`1164,403` — i.e. a **layout table**, not configuration. A high key count does
|
|
not mean a rich screen; read the values before believing the rank.
|
|
|
|
🟡 Not settled: the 38 bare screens, and whether the `EX_` variant seen in
|
|
`EX_MENU_DISABLE_SKIP` / `EX_LETTERS` is the same `EXTRA` mode word as `+184`.
|