# Where does the DIFFICULTY screen live? ✅ DECODED 2026-08-31. # It is a DIALOG -- `DLG_SELECT_DIFFICULTY`, GP_DIALOG.pak entries 2/3. # # CLOSES A NEGATIVE OF MINE. gp-title-holds-three-button-screens.txt recorded # "not an 8-record btn-named build anywhere on the disc", with the failed # assumption named as mine: I assumed DIFFICULTY's four items pair with `f` focus # variants in an archive of its own, the way GP_TITLE's screens do. Both halves # of that were wrong -- it has its own button prefix and it is not a GamePart # screen at all. # ################################################################################ # ROUTE 1 -- THE IMAGE. /image/sylpheed.pe, 3 occurrences of "DIFFICULTY": # 0x820A2548 STAGE | DIFFICULTY | TITLE | FADE | BASE_EXTRA ... # (a GamePartTask::RegisterToFactory name list) # 0x820A3377 RECORD_DIFFICULTY (a results/record field) # 0x820A41BB DLG_LEADERBOARD_MENU_NEXT | DLG_SYSTEM_PAUSE | # **DLG_SELECT_DIFFICULTY** | DLG_MISSION_OBJECTIVE | # DLG_MESSAGE_BOX | DLG_MESSAGE_BOX_YES_NO | ... # # ✅ The third is the answer: DIFFICULTY is one of the game's DLG_* dialogs. # ⚠️ "GP_DIFFICULTY" appears 0 times in the image, which is consistent. # ################################################################################ # ROUTE 2 -- THE DISC. GP_DIALOG.pak entries 2 and 3 are the only builds in that # archive carrying `pcbtn00`..`pcbtn03` -- FOUR buttons, matching # EASY / NORMAL / HARD / BACK. Design rows and spacing: # # entry 2 y 259 / 329 / 399 / 469 spacing 70, 70, 70 # entry 3 identical (⚠️ called "the EN/JP pair" here and # WITHDRAWN 2026-08-31 -- see the section # at the end; adjacent GP_DIALOG entries # are generally unrelated dialogs) # # ✅ CONTROL: the same reader on GP_TITLE entry 5 returns 162/242/322/401/482, # spacing 80 -- the rows that archive is independently known to place. # ################################################################################ # ROUTE 3 -- THE ORACLE. My own capture of the running DIFFICULTY screen # (captures/menu-nav/live-difficulty-opens-normal.png), with the disc-grounded # calibration capture_y = 64.82 + 0.9919 * design_y: # # design row predicted measured in the capture residual # 259 321.7 323.5 +1.8 # 329 391.2 394.0 +2.8 # 399 460.6 463.5 +2.9 # 469 530.0 533.5 +3.5 # measured spacing 70.5 / 69.5 / 70.0 against the disc's 70 / 70 / 70 # # Four rows, all under 4 px, with the spacing agreeing exactly. The residual is a # uniform ~+2.8 px offset, which is the calibration's own systematic and not a # mismatch. # # => THREE INDEPENDENT ROUTES: the executable names it a dialog, the disc has a # four-button dialog build, and the running game draws its rows where that # build says they are. # # 📌 WHY IT MATTERS BEYOND THE LOCATION: DIFFICULTY being a DIALOG explains why # NEW GAME's destination is not a screen in GP_TITLE, and it means the "four menu # items load an external archive" reading of the event numbers # (boot-config-and-gamepart-registry.md's count-match) is looser than it looked -- # NEW GAME opens a dialog from GP_DIALOG.pak, which is an external archive, but a # dialog is not the same kind of thing as OPTIONS or TUTORIAL opening a GamePart. # The count still matches; the categories are not uniform. # # ⚠️ REACH: entries 2/3 are identified by button count and geometry, not by a # name binding DLG_SELECT_DIFFICULTY to a pak entry. No such binding was found -- # the DLG_* names live in a string list, and what maps a name to an archive entry # is not decoded. Another 4-button dialog with the same rows would be # indistinguishable by this evidence. ################################################################################ # ✅ DECODED 2026-08-31 (later): THE DIALOG TABLE. `DLG_SELECT_DIFFICULTY` = id 2000. # # Chasing the reach above -- "no binding from the DLG_ name to anything" -- found # one. Every DLG_ string in the image is pointed at by exactly one aligned word, # at a regular 12-byte stride. The table is: # # struct { u32 id; u32 name_ptr; u32 handler; } // 12 bytes, big-endian # # 🔴 CORRECTED 2026-08-31: this first read as `{handler, id, name_ptr}`, which is # the same three fields SHIFTED BY ONE WORD, so every record was credited with the # PREVIOUS record's handler. Caught by a control dump: under the old alignment # record 0 (DLG_LET_SIGNIN) had a "handler" of 0x10000000, which is not a code # address. ids and names are unaffected -- they are the same fields either way, # and DLG_SELECT_DIFFICULTY is still id 2000. Only the handler attribution moved. # Under the corrected alignment its handler is 0x821D0808, and the histogram over # all 70 records is 0x821D0808 x43, 0x821D05D8 x24, 0x821CFD80 x3. # # spanning 0x820A0A2C .. 0x820A0D68, three distinct handler values # (0x821CFD80, 0x821D05D8, 0x821D0808). # # ✅ COMPLETE: 70 DLG_ names in the image, 70 records, ZERO names without one. # ids are banded and monotonic, with a single gap at 24: # 0..23 sign-in, storage, save/load/replay, GO_TITLE, GO_NEXT # 25..43 autosave, training, weapon develop, custom keys, menu jumps # 98, 99 DLG_MESSAGE_BOX_YES_NO, DLG_MESSAGE_BOX # 1000 DLG_MISSION_OBJECTIVE # 2000 **DLG_SELECT_DIFFICULTY** # 2001..3 SYSTEM_PAUSE, LEADERBOARD_MENU_JUMP, LEADERBOARD_MENU_NEXT # 2100..15 DLG_STAGE_TITLE01..16 # 8000 DLG_RATING_ESRB # 9000..2 trial dialogs # # Read from /image/sylpheed.pe directly -- these are the bytes the console ran, # not a database row. # ################################################################################ # ✅ REFUTATION ATTEMPT ON THE SHARED REACH -- IT SURVIVES, AND IS NOW BOUNDED. # # Both agents recorded: "another four-button dialog with the same rows would be # indistinguishable by this evidence". Tested by scanning EVERY build in EVERY pak # for four buttons within 6 px of rows 259/329/399/469 # (examples/four_button_row_rivals.rs): # # INCUMBENT GP_DIALOG.pak entry 2 rows [259, 329, 399, 469] # INCUMBENT GP_DIALOG.pak entry 3 rows [259, 329, 399, 469] # control: 2 incumbents found (want 2) -- PASSED # RIVALS ELSEWHERE ON THE DISC: **0** # # So the hypothetical rival does not exist here. The geometric identification is # UNIQUE DISC-WIDE, which is a stronger statement than the one either of us # recorded, and it was cheap to get. # # ⚠️ WHAT IS STILL NOT BOUND: id 2000 -> a pak entry. The table gives name -> id # and the disc gives a unique four-button build; nothing found so far connects the # two. The tie is uniqueness of geometry plus the oracle capture, not a pointer. ################################################################################ # ❌ REFUTED: the id -> pak-entry join is NOT positional. 2026-08-31. # # GP_DIALOG.pak has exactly **140 entries** and the dialog table has exactly **70 # records** — a 2:1 ratio that looks like one EN/JP pair per dialog, which would # make the unbound join an ordering question rather than a search. Tested, and it # does not hold: # # ADJACENT pairing (2k, 2k+1) identical element-name sets: 2 of 65 # HALVES pairing (i, i+70) identical element-name sets: 0 of 65 # (5 pairs unreadable — entries that do not parse as builds) # # GP_TITLE's language pairs share their element sets exactly (except 4/7, the # title art), so identical sets are the signature of a pair there. In GP_DIALOG # almost nothing matches, so **the 2:1 ratio is not established as a language # pairing** and no positional rule connects id 2000 to entries 2/3. # # 📌 RESIDUAL OBSERVATION, recorded because it is odd rather than because it is # understood: the only two adjacent pairs with identical element sets are # **entries 0/1 and 2/3** — and 2/3 is the DIFFICULTY build. Whatever makes those # two structurally uniform in an archive where 63 of 65 pairs are not, this does # not explain. # # ⚠️ A plausible reading I am NOT asserting: dialog text may be baked into # language-specific sprites, which would make EN/JP entries differ in element # names by construction. That is consistent with the 63, and it is one # measurement away from being tested — but it is not tested here, and the 2 that # DO match would then need their own explanation. # # => The join stays unbound. Table gives name -> id; disc gives a unique build; # the tie is uniqueness plus the oracle capture. Positional ordering is now # ruled out as the missing pointer, which narrows where to look next. ################################################################################ # 🔴 MY UNTESTED READING IS REFUTED, and it inverts the puzzle rather than # solving it. 2026-08-31, sylpheed-port, re-derived here with my own reader. # # I offered: dialog text may be baked into language-specific sprites, so EN/JP # entries differ in element names by construction -- which would explain the 63 # differing pairs and leave the 2 matching ones needing their own account. # # ❌ **26 of 65 adjacent pairs differ in BUTTON COUNT.** Two languages of one # dialog cannot: a locale changes the glyphs on a button, not how many there are. # Reproduced independently (examples/dialog_pair_button_counts.rs): 26 differing, # 39 equal, 5 unreadable -- their count exactly. # # entries 6/7 2 vs 3 entries 44/45 2 vs 0 # entries 8/9 2 vs 0 entries 46/47 2 vs 0 # # And the names say it once read rather than counted: 6/7 is ranking_NEXT against # ranking_JUMP, 8/9 is py_ranking_* against pzeff*, 10/11 is pzstg10 against # pzstg02 -- different subsystems, different stages. # # 📌 SO THE PUZZLE DISSOLVES INSTEAD OF DEEPENING. Adjacent GP_DIALOG entries are # simply UNRELATED DIALOGS. The 63 never needed the language reading, so the 2 # that match need no special explanation either, and the 140:70 ratio is a # **coincidence of counting, not a pairing** -- which is the same fact my own # halves-pairing result of 0 was already showing from the other side. # # ⚠️ AND THE CAREFUL PART, which is theirs and which I am preserving: this does # NOT establish that 0/1 and 2/3 ARE EN/JP pairs. Identical element sets is the # signature in GP_TITLE; here it is equally consistent with a DUPLICATE. And 37 # of the 63 differ WITHOUT a button-count mismatch -- for those the language # reading is UNSUPPORTED, not refuted. What is refuted is it as an explanation of # the 63, which is what I offered it as. # # 🔴 CONSEQUENCE FOR A DELIVERED CLAIM: I described entries 2/3 as "an EN/JP pair" # in HANDOFF.md and above. **Withdrawn.** The DIFFICULTY identification does not # rest on the pairing -- it rests on the unique four-button geometry (0 rivals # disc-wide) plus the oracle capture -- but the pairing was stated as fact and was # not one. ################################################################################ # ❌ AND THE 37 ARE NOW REFUTED TOO, not merely unsupported. 2026-08-31. # # Both agents recorded that the language reading survived for the 37 pairs that # differ WITHOUT a button-count mismatch, and both noted that nothing rewarded # closing it. It cost one scan. # # STEP 1: all 39 equal-button-count pairs share their button NAMES AND ROWS # exactly (examples/dialog_pair_37.rs; control: entries 2/3 counted as matching). # ⚠️ That does NOT settle it -- two different dialogs sharing a button template, # like two yes/no boxes, look identical by that test. # # STEP 2: look at what actually differs (examples/dialog_pair_diffs.rs): # # entries 10/11 pzstg10_01..20 + _eff vs pzstg02_01..16 + _eff # entries 12/13 pzstg11_* vs pzstg03_* # entries 14/15 pzstg12_* vs pzstg13_* # entries 16/17 pzstg04_* vs pzstg14_* # entries 18/19 pzstg05_* vs pzstg15_* # entries 20/21 pzstg06_* vs pzstg16_* # # ✅ THESE ARE DIFFERENT STAGES. They are the sixteen DLG_STAGE_TITLE01..16 # dialogs from the table above, and an adjacent pair carries TWO DIFFERENT STAGES, # not two languages of one. That settles it unaided. # # 🔴 A SECOND ARGUMENT I OFFERED HERE IS WITHDRAWN. I wrote: "the sprite COUNTS # differ too (stg10 has 20 title sprites, stg02 has 16), which is a different # amount of text, not a translation of the same text." **It does not hold # uniformly.** sylpheed-port re-ran it and 12/13 comes out EQUAL; my own output # above shows it -- pzstg11_01..13 against pzstg03_01..13, thirteen each. I # generalised from the 10/11 example I had looked at. # ⚠️ Their absolute figures also differ from mine (42 vs 34 where I said 20 vs 16) # because we counted different things: they count every `.t32` element in the # entry, I counted only the differing stage-title lines. Both are legitimate; I # did not say which I meant. # 📌 The conclusion is untouched -- the stage numbers carry it alone. What this # is worth is the shape: a conclusion resting on two legs, one of which does not # reproduce. **The leg carrying no weight is the one that went unchecked**, which # is the `EN/JP pair` failure one step out, committed while writing up that very # failure. # # => The language reading is refuted for the 37 as well. The whole 63 is now # explained by one fact: **adjacent GP_DIALOG entries are unrelated dialogs.** # No residue, no special case. # # 📌 THE 2 REMAINING PAIRS (0/1 and 2/3) are still the only ones with identical # element sets, and still unexplained -- but they are no longer anomalous against # a hypothesis, because the hypothesis is gone. Whether they are a language pair, # a duplicate, or two dialogs sharing every element is untested. # # ⚠️ WORTH RECORDING ABOUT THE PROCESS, not the disc: this question was left open # by both agents with the explicit observation that "nothing rewards closing it". # It cost two scans and went AGAINST the reading I had offered. A bound nobody is # incentivised to test is exactly where a convenient claim survives -- and the # next reader cannot tell whether a bound was respected or merely never revisited. ################################################################################ # ❌ THE id -> PAK-ENTRY BINDING IS NOT IN THE IMAGE BY THIS ROUTE. 2026-08-31. # # Positional ordering was already ruled out. The next route was the dialog # table's own handlers: if anything joins a dialog id to an archive entry, the # code that builds a dialog should touch it. # # All three handlers were disassembled from the image. All three begin by loading # the SAME global -- `lis r11,0x828E` then `lwz r3,0x2B14(r11)` -> 0x828E2B14 -- # and two take addresses at 0x828E45E0 / 0x828E4640 / 0x828E467C. # # ❌ EVERY ONE OF THOSE IS BSS. They sit inside a single contiguous ZERO RUN of # 364 601 bytes (0x828A28C7 .. 0x828FB900) -- uninitialised data, zero in the # image, populated only at runtime. # # ✅ CONTROL, because an all-zero read is also what a bad address gives: the # dialog table itself at 0x820A0A2C reads non-zero through the same arithmetic # (10000000 00000000 820a4598 821cfd80). The addressing is right; the data is # genuinely absent. # # => The handlers operate on RUNTIME state. If the id -> entry join exists there, # it is visible only in a running game, not in the image. That is a route # closed rather than a question answered, and it says where the next attempt # has to look: guest memory at 0x828E2B14 with the game up. # # ⚠️ REACH: one route (the table's own handlers). Not looked at: the archive # loader, the GamePart that opens GP_DIALOG, or any table keyed by id elsewhere # in the image. "Not in the image" is NOT established -- what is established is # "not reachable from the dialog handlers, because they read BSS".