feat(menu): a Recomp Options button in the pause menu that opens the Insert menu #290

Open
prob wants to merge 2 commits from prob/Longshot:menu/recomp-options into main
First-time contributor

feat(menu): a Recomp Options button in the pause menu that opens the Insert menu

Why

The Insert menu's settings can only be reached with a key (Insert, F1) or a pad chord, which a controller player may never find. This adds a sixth button, "Recomp Options", to the pause menu's Menu page, below Title Screen, drawn and moved to like the game's own; A on it opens the Insert menu.

What changed

  • layout: nx2/pause_layout.py (new) adds Pa_Button_05 to every PaSystemMenu_00 layout in Common.blarc at build time, a copy of Pa_Button_04 below it. The page draws only the panes its layout has (a copy made at run time is never drawn), so the layout itself gets the sixth pane. What grows is appended after the end of the layout and of the archive: no other offset moves, and the prompts' runs over Common.blarc stay valid.
  • prompts: nx2/prompt_assets.py writes the edited archive to prompts/Common.blarc, raises its entry in the resource size table, and lists pause_layout.py among prompt_key()'s sources. host/prompts.c: the served layouts' key (layout_runs) also takes the file's size and time, so a build that adds the button makes the served file again.
  • lifted code: nx2/oot_pause_menu.py (new), applied by nx2/oot_patches.py and by nx2/chunks.py edit_c (all its edits or none):
    • f_80f99400 (the page made): the sixth pane gets a button object like the other five, and its text ("Recomp Options", localized for pt/es/fr/it/de) on its text boxes (host/oot_pause_menu.c hos_pausebtn_build);
    • f_80ad5310 (a button's state): the sixth button's control noted (hos_pausebtn_state);
    • f_80aa2ad0 (the base of the page's destructor): the button forgotten, so its control is never read after it is freed (hos_pausebtn_gone).
  • input: host/nn_hid.c passes the buttons through hos_pausebtn_buttons: A while the cursor is on the button opens the Insert menu (F1 through the menu's key queue) and is not passed to the game. The page's script has no end for a sixth button's decision, and passing it left the pause menu impossible to close.
  • Linux: the Insert menu's key queue (host/rx_backend.c) exists on every platform, and host/posix/display_sdl.c keys_update notes each key on its way down, as host/display.c's WM_KEYDOWN does on Windows.
  • host/nn_applet.c: hos_game_language() for the button's text.
  • tests: tests/test_oot_pause_menu.py (new: the edits all or none, also through edit_c; unknown layouts and archives left as they are; the teardown clears the control); tests/test_linux_menu_keys.py (new: Linux keys reach the queue once on their way down, none while the window is not in front); tests/test_prompt_skip.py (pause_layout covered); tests/test_input_background.py (a pass-through stub for the new call).

No game data is in the change: the layout is edited on the player's PC from their own files, and the original romfs is not modified.

Flags and settings

None.

Measurements

main: fbd42024 (2026-11-13)
this PR: menu/recomp-options @ 46d640e5
Hardware: NVIDIA GeForce RTX 2070 SUPER 8 GB, driver 621.01; AMD Ryzen 7 5800X; 32 GB; Windows 11 Pro 26220
Scene: Kakariko Village at night, standing still; 1920x1080 output, quality preset 6, DLSS on, frame generation off, frame rate cap Uncapped, vsync off; windowed 1280x720, the window not in front
Method: each build in a game folder of its own (nx2/make_game.py), the same longshot_options.ini and save on both; one warm-up run per side first (shaders built, not counted), then main, this PR, main, this PR; 120 s measured per run from HOS_PRESENT_TRACE (45 s after loading the save, the last 5 s left out); with two runs the median is their mean
How to reproduce: load a save in Kakariko Village, stand still, the settings above, HOS_PRESENT_TRACE=<csv>; the fps lines of longshot_log.txt agree

main @ fbd42024 this PR @ 46d640e5
fps (run 1 / run 2) 39.9 / 39.7 39.9 / 39.9
fps, median of the runs 39.8 39.9
median frame time (run 1 / run 2) 24.83 / 24.98 ms 24.90 / 24.97 ms
p99 frame time (run 1 / run 2) 32.15 / 30.80 ms 29.52 / 29.48 ms
1% low, the slowest 1% of frames (run 1 / run 2) 27.2 / 25.4 fps 30.5 / 30.6 fps

The button only acts while the pause menu's Menu page is open, so no cost in play is expected, and none shows: the averages and medians match. The p99 and 1% low differences are run-to-run spread (main's two runs differ by 1.8 fps there), not something this change does in play.

main-1:  [hos Presentation Thread] vi: 14162 frames presented (40.4 fps over the last 5.0 s, interval 1)
pr-1:    [hos Presentation Thread] vi: 13656 frames presented (40.0 fps over the last 5.0 s, interval 1)
main-2:  [hos Presentation Thread] vi: 14831 frames presented (39.9 fps over the last 5.0 s, interval 1)
pr-2:    [hos Presentation Thread] vi: 14544 frames presented (38.8 fps over the last 5.0 s, interval 1)

Testing

Tested:

  • Longshot (this branch @ 46d640e5, built from source with nx2/make_game.py), Windows 11, RTX 2070 SUPER: the button shows below Title Screen with its text, the cursor reaches it, A opens the Insert menu (pause menu: Recomp options: the Insert menu in longshot_log.txt), and B then closes the pause menu.
  • Windows 11, NVIDIA GeForce RTX 2070 SUPER: the button shows below Title Screen with its text, the cursor reaches it, A opens the Insert menu, B then closes the pause menu, and A in play afterwards is the game's. With the pause menu closed while the cursor was on the button, A in play is the game's; back to the title screen the page is destroyed and the button forgotten.
  • Linux: Manjaro on the same PC, same settings: the button shows and opens the Insert menu.

Not tested: other GPUs and the Steam Deck: the change has no renderer or GPU code (a layout edit, the lifted code's pause-menu calls and the pad's A), so it doesn't depend on the GPU; the one platform-specific part, Linux's key queue (host/posix/display_sdl.c), was run on Linux.

Needs other testers: None

Screenshots

image
image

Risk and rollback

The pause menu's Menu page could misbehave if a game update moves the edited functions; the edits are all or none, so a lift that does not find them leaves the button out with a warning (oot_patches: WARNING: the pause menu's places were found ...). Reverting the commit is enough.

Reviewer notes

Start with host/oot_pause_menu.c (the button, its state, its teardown) and nx2/pause_layout.py (the layout edit). The A press is taken in host/nn_hid.c rather than letting the game decide, because the page's script has no end for a sixth button's decision.

Issues

Checklist

For everyone

  • One topic.
  • The title is a Conventional Commit (type(scope): description).
  • Rebased on current main.
  • python -I -B tests/run_all.py run and passing (any failure that also fails on main is named above).
  • Tested in the game, or what wasn't is named under Testing.
  • Testing filled in: what was tested, what wasn't, and what needs other testers (with test requests).
  • Measured: main and this PR at named commits, on the same PC, scene and settings, at least 2 interleaved runs each, the median reported.
  • No game files, dumps, build output or generated files.
  • third_party/ untouched (a new folder with a PROVENANCE.md instead).
  • The license is OK: GPL-3.0-or-later, nothing incompatible and nothing decompiled.

If applicable

  • Screenshots for a change that shows on screen.
  • A row in docs/tested-configurations.md for each measured run.
  • A game-free test added for the fix, failing on main and passing here.
  • A check under tests/ that reads changed source updated in this PR, not weakened.
  • Host code builds on both Windows (llvm-mingw) and Linux, with UTF-8 paths.
feat(menu): a Recomp Options button in the pause menu that opens the Insert menu ## Why The Insert menu's settings can only be reached with a key (Insert, F1) or a pad chord, which a controller player may never find. This adds a sixth button, "Recomp Options", to the pause menu's Menu page, below Title Screen, drawn and moved to like the game's own; A on it opens the Insert menu. ## What changed - layout: `nx2/pause_layout.py` (new) adds `Pa_Button_05` to every `PaSystemMenu_00` layout in `Common.blarc` at build time, a copy of `Pa_Button_04` below it. The page draws only the panes its layout has (a copy made at run time is never drawn), so the layout itself gets the sixth pane. What grows is appended after the end of the layout and of the archive: no other offset moves, and the prompts' runs over `Common.blarc` stay valid. - prompts: `nx2/prompt_assets.py` writes the edited archive to `prompts/Common.blarc`, raises its entry in the resource size table, and lists `pause_layout.py` among `prompt_key()`'s sources. `host/prompts.c`: the served layouts' key (`layout_runs`) also takes the file's size and time, so a build that adds the button makes the served file again. - lifted code: `nx2/oot_pause_menu.py` (new), applied by `nx2/oot_patches.py` and by `nx2/chunks.py` `edit_c` (all its edits or none): - `f_80f99400` (the page made): the sixth pane gets a button object like the other five, and its text ("Recomp Options", localized for pt/es/fr/it/de) on its text boxes (`host/oot_pause_menu.c` `hos_pausebtn_build`); - `f_80ad5310` (a button's state): the sixth button's control noted (`hos_pausebtn_state`); - `f_80aa2ad0` (the base of the page's destructor): the button forgotten, so its control is never read after it is freed (`hos_pausebtn_gone`). - input: `host/nn_hid.c` passes the buttons through `hos_pausebtn_buttons`: A while the cursor is on the button opens the Insert menu (F1 through the menu's key queue) and is not passed to the game. The page's script has no end for a sixth button's decision, and passing it left the pause menu impossible to close. - Linux: the Insert menu's key queue (`host/rx_backend.c`) exists on every platform, and `host/posix/display_sdl.c` `keys_update` notes each key on its way down, as `host/display.c`'s `WM_KEYDOWN` does on Windows. - `host/nn_applet.c`: `hos_game_language()` for the button's text. - tests: `tests/test_oot_pause_menu.py` (new: the edits all or none, also through `edit_c`; unknown layouts and archives left as they are; the teardown clears the control); `tests/test_linux_menu_keys.py` (new: Linux keys reach the queue once on their way down, none while the window is not in front); `tests/test_prompt_skip.py` (`pause_layout` covered); `tests/test_input_background.py` (a pass-through stub for the new call). No game data is in the change: the layout is edited on the player's PC from their own files, and the original romfs is not modified. ## Flags and settings None. ## Measurements main: fbd42024 (2026-11-13) this PR: menu/recomp-options @ 46d640e5 Hardware: NVIDIA GeForce RTX 2070 SUPER 8 GB, driver 621.01; AMD Ryzen 7 5800X; 32 GB; Windows 11 Pro 26220 Scene: Kakariko Village at night, standing still; 1920x1080 output, quality preset 6, DLSS on, frame generation off, frame rate cap Uncapped, vsync off; windowed 1280x720, the window not in front Method: each build in a game folder of its own (`nx2/make_game.py`), the same `longshot_options.ini` and save on both; one warm-up run per side first (shaders built, not counted), then main, this PR, main, this PR; 120 s measured per run from `HOS_PRESENT_TRACE` (45 s after loading the save, the last 5 s left out); with two runs the median is their mean How to reproduce: load a save in Kakariko Village, stand still, the settings above, `HOS_PRESENT_TRACE=<csv>`; the fps lines of longshot_log.txt agree | | main @ fbd42024 | this PR @ 46d640e5 | |---|---|---| | fps (run 1 / run 2) | 39.9 / 39.7 | 39.9 / 39.9 | | fps, median of the runs | 39.8 | 39.9 | | median frame time (run 1 / run 2) | 24.83 / 24.98 ms | 24.90 / 24.97 ms | | p99 frame time (run 1 / run 2) | 32.15 / 30.80 ms | 29.52 / 29.48 ms | | 1% low, the slowest 1% of frames (run 1 / run 2) | 27.2 / 25.4 fps | 30.5 / 30.6 fps | The button only acts while the pause menu's Menu page is open, so no cost in play is expected, and none shows: the averages and medians match. The p99 and 1% low differences are run-to-run spread (main's two runs differ by 1.8 fps there), not something this change does in play. ``` main-1: [hos Presentation Thread] vi: 14162 frames presented (40.4 fps over the last 5.0 s, interval 1) pr-1: [hos Presentation Thread] vi: 13656 frames presented (40.0 fps over the last 5.0 s, interval 1) main-2: [hos Presentation Thread] vi: 14831 frames presented (39.9 fps over the last 5.0 s, interval 1) pr-2: [hos Presentation Thread] vi: 14544 frames presented (38.8 fps over the last 5.0 s, interval 1) ``` ## Testing **Tested:** - Longshot (this branch @ 46d640e5, built from source with `nx2/make_game.py`), Windows 11, RTX 2070 SUPER: the button shows below Title Screen with its text, the cursor reaches it, A opens the Insert menu (`pause menu: Recomp options: the Insert menu` in longshot_log.txt), and B then closes the pause menu. - Windows 11, NVIDIA GeForce RTX 2070 SUPER: the button shows below Title Screen with its text, the cursor reaches it, A opens the Insert menu, B then closes the pause menu, and A in play afterwards is the game's. With the pause menu closed while the cursor was on the button, A in play is the game's; back to the title screen the page is destroyed and the button forgotten. - Linux: Manjaro on the same PC, same settings: the button shows and opens the Insert menu. **Not tested:** other GPUs and the Steam Deck: the change has no renderer or GPU code (a layout edit, the lifted code's pause-menu calls and the pad's A), so it doesn't depend on the GPU; the one platform-specific part, Linux's key queue (`host/posix/display_sdl.c`), was run on Linux. **Needs other testers:** None ## Screenshots ![image](/attachments/c81e938b-9094-4804-b904-49d0fdf95beb) ![image](/attachments/51b4c67c-be22-4c89-bf34-b1e283906806) ## Risk and rollback The pause menu's Menu page could misbehave if a game update moves the edited functions; the edits are all or none, so a lift that does not find them leaves the button out with a warning (`oot_patches: WARNING: the pause menu's places were found ...`). Reverting the commit is enough. ## Reviewer notes Start with `host/oot_pause_menu.c` (the button, its state, its teardown) and `nx2/pause_layout.py` (the layout edit). The A press is taken in `host/nn_hid.c` rather than letting the game decide, because the page's script has no end for a sixth button's decision. ## Issues ## Checklist ### For everyone - [x] One topic. - [x] The title is a Conventional Commit (`type(scope): description`). - [x] Rebased on current `main`. - [x] `python -I -B tests/run_all.py` run and passing (any failure that also fails on `main` is named above). - [x] Tested in the game, or what wasn't is named under Testing. - [x] Testing filled in: what was tested, what wasn't, and what needs other testers (with test requests). - [x] Measured: main and this PR at named commits, on the same PC, scene and settings, at least 2 interleaved runs each, the median reported. - [x] No game files, dumps, build output or generated files. - [x] `third_party/` untouched (a new folder with a `PROVENANCE.md` instead). - [x] The license is OK: GPL-3.0-or-later, nothing incompatible and nothing decompiled. ### If applicable - [x] Screenshots for a change that shows on screen. - [x] A row in `docs/tested-configurations.md` for each measured run. - [x] A game-free test added for the fix, failing on `main` and passing here. - [x] A check under `tests/` that reads changed source updated in this PR, not weakened. - [x] Host code builds on both Windows (llvm-mingw) and Linux, with UTF-8 paths.
image.png
591 KiB
image.png
2.4 MiB
prob force-pushed menu/recomp-options from 92e3c1ac0c
Some checks failed
PR description / Description follows the template (pull_request_target) Failing after 4s
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
Third-party parts / The same as upstream (pull_request) Has been cancelled
to f909218ebf
Some checks failed
Website / Website on Cloudflare (pull_request) Blocked by required conditions
PR description / Description follows the template (pull_request_target) Successful in 3s
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
Third-party parts / The same as upstream (pull_request) Has been cancelled
2026-11-13 19:37:54 +00:00
Compare
prob force-pushed menu/recomp-options from f909218ebf
Some checks failed
Website / Website on Cloudflare (pull_request) Blocked by required conditions
PR description / Description follows the template (pull_request_target) Successful in 3s
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
Third-party parts / The same as upstream (pull_request) Has been cancelled
to c1ae898fe8
Some checks failed
PR description / Description follows the template (pull_request_target) Successful in 3s
Website / Website on Cloudflare (pull_request_target) Has been skipped
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
Third-party parts / The same as upstream (pull_request) Has been cancelled
2026-11-14 01:09:56 +00:00
Compare
prob force-pushed menu/recomp-options from c1ae898fe8
Some checks failed
PR description / Description follows the template (pull_request_target) Successful in 3s
Website / Website on Cloudflare (pull_request_target) Has been skipped
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
Third-party parts / The same as upstream (pull_request) Has been cancelled
to 68702277b9
Some checks failed
PR description / Description follows the template (pull_request_target) Successful in 6s
Website / Website on Cloudflare (pull_request_target) Has been skipped
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
Third-party parts / The same as upstream (pull_request) Has been cancelled
Website / Website on Cloudflare (pull_request) Has been cancelled
2026-11-14 15:58:08 +00:00
Compare
prob force-pushed menu/recomp-options from 68702277b9
Some checks failed
PR description / Description follows the template (pull_request_target) Successful in 6s
Website / Website on Cloudflare (pull_request_target) Has been skipped
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
Third-party parts / The same as upstream (pull_request) Has been cancelled
Website / Website on Cloudflare (pull_request) Has been cancelled
to 1575902fd0
Some checks failed
PR description / Description follows the template (pull_request_target) Failing after 6s
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
Third-party parts / The same as upstream (pull_request) Has been cancelled
Website / Website on Cloudflare (pull_request) Has been cancelled
2026-11-14 23:53:34 +00:00
Compare
prob force-pushed menu/recomp-options from 1575902fd0
Some checks failed
PR description / Description follows the template (pull_request_target) Failing after 6s
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
Third-party parts / The same as upstream (pull_request) Has been cancelled
Website / Website on Cloudflare (pull_request) Has been cancelled
to 5af898b007
Some checks failed
CI / Conventional Commit title (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Blocked by required conditions
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Blocked by required conditions
CI / Workflow lint (actionlint) (pull_request) Blocked by required conditions
Guide screenshots / Guide images and local UI source review (pull_request) Blocked by required conditions
Third-party parts / As their notes say (pull_request) Blocked by required conditions
Third-party parts / The same as upstream (pull_request) Blocked by required conditions
Website / Website on Cloudflare (pull_request) Blocked by required conditions
PR description / Description follows the template (pull_request_target) Failing after 16s
2026-11-15 17:22:36 +00:00
Compare
Some checks failed
CI / Conventional Commit title (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Blocked by required conditions
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Blocked by required conditions
CI / Workflow lint (actionlint) (pull_request) Blocked by required conditions
Guide screenshots / Guide images and local UI source review (pull_request) Blocked by required conditions
Third-party parts / As their notes say (pull_request) Blocked by required conditions
Third-party parts / The same as upstream (pull_request) Blocked by required conditions
Website / Website on Cloudflare (pull_request) Blocked by required conditions
PR description / Description follows the template (pull_request_target) Failing after 16s
This pull request doesn't have enough approvals yet. 0 of 1 approvals granted.
Some workflows are waiting to be reviewed.
This branch is out-of-date with the base branch
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u 'menu/recomp-options:prob-menu/recomp-options'
git switch 'prob-menu/recomp-options'
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
MagicBeanDev/Longshot!290
No description provided.