Files
psxrecomp/runtime/tests/test_rewind_toggle_combo.py
T
Shokunin 812465df58 Let a title block local Rewind (psx_mod_set_rewind_blocked)
A title calls psx_mod_set_rewind_blocked(1) while it is in a mode that must
not be rewound, such as local split-screen multiplayer, and 0 when it ends.
While blocked, opening Rewind is refused with an OSD note, no history is
captured (the ring keeps what it has; a capture already due is dropped and an
open panel closes unloaded), and a title-allowed direct Rewind shortcut is not
in force: its button is not claimed and reaches the guest pad / title
transform as if Rewind were off. Netplay already refuses Rewind on its own.
The block clears at every mod/session start, so a title that never calls it
is unchanged.

The direct-allowance decision (allowed, and for Rewind: enabled and not
blocked) is now one pure helper, psx_hotkey_direct_active, used for both the
claim and the shortcut press, so an unclaimed Rewind button is never a
one-button shortcut (with Rewind off it falls back to Select + button rather
than popping the "Rewind is off" note on every press).

Tests: psx_hotkey_pad_test covers the allowance and a block taking effect
mid-press; rewind_toggle_combo_test guards the capture/open gates and reset.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 23:44:18 -07:00

98 lines
5.2 KiB
Python

#!/usr/bin/env python3
"""Guard PSX host controller shortcuts against single-button defaults."""
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
MAIN = (ROOT / "runtime" / "src" / "main.cpp").read_text(encoding="utf-8")
assert "static int g_hotkey_pad_rewind = 1272;" in MAIN
assert "static int g_hotkey_pad_save_state_menu = 2040;" in MAIN
assert "PSX_HOTKEY_PAD_IS_BUTTON_COMBO" in MAIN
assert "PSX_HOTKEY_PAD_SELECT_R3" in MAIN
assert "static int hotkey_shortcut_down(int shortcut, int binding)" in MAIN
assert "psx_hotkey_pad_down(binding, direct, SDL_CONTROLLER_BUTTON_BACK," in MAIN
assert "return hotkey_shortcut_down(PSX_ASSIST_BIND_REWIND, g_hotkey_pad_rewind);" in MAIN
assert "(btn & PAD_SELECT) == 0 && (btn & PAD_L3) == 0" not in MAIN
assert (
"SDL_GameControllerGetButton(h, SDL_CONTROLLER_BUTTON_BACK) &&\n"
" SDL_GameControllerGetButton(h, SDL_CONTROLLER_BUTTON_LEFTSTICK)"
) not in MAIN
assert (
"SDL_GameControllerGetButton(h, SDL_CONTROLLER_BUTTON_BACK) ||\n"
" SDL_GameControllerGetButton(h, SDL_CONTROLLER_BUTTON_RIGHTSTICK)"
) not in MAIN
assert "HOST_KEYMAP_SAVE_STATE_MENU" in MAIN
assert "key >= SDLK_F1 && key <= SDLK_F12" not in MAIN
assert "savestate_slot_from_function_key" not in MAIN
assert "savestate_submit_slot(f_slot" not in MAIN
assert "savestate_input_guard_arm();" in MAIN
assert "savestate_input_guard_active()" in MAIN
assert "out->buttons = 0xFFFFu;" in MAIN
assert "pad_from_keyboard(1) != 0xFFFFu" in MAIN
assert "savestate_menu_ignore_toggle_release" in MAIN
assert "savestate_menu_open_key" in MAIN
assert "savestate_menu_toggle(key)" in MAIN
assert "savestate_menu_toggle(0)" in MAIN
assert 'std::getenv("PSX_FAST_FORWARD_SPEED")' in MAIN
assert "bool manual_turbo_active = false;" in MAIN
assert "Fast forward: %dx" in MAIN
assert "g_frame_period_ms / (double)mult" in MAIN
# Fast-forward has a controller host shortcut alongside the keyboard Turbo key:
# a select+L1 chord by default, routed through the same combo matcher, offered
# to the launcher as the third host-shortcut row, and persisted as
# [hotkeys] fast_forward_pad.
assert "static int g_hotkey_pad_fast_forward = 1528;" in MAIN
assert "PSX_HOTKEY_PAD_SELECT_L1" in MAIN
assert "PSX_ASSIST_BIND_FAST_FORWARD" in MAIN
assert "hotkey_shortcut_down(PSX_ASSIST_BIND_FAST_FORWARD, g_hotkey_pad_fast_forward)) {" in MAIN
assert MAIN.count("ls.assist_pad_bind[PSX_ASSIST_BIND_FAST_FORWARD]") == \
MAIN.count("ls.assist_pad_bind[PSX_ASSIST_BIND_SAVE_STATE_MENU]")
assert '"Fast-forward",' in MAIN
CFG = (ROOT / "recompiler" / "src" / "config_loader.cpp").read_text(encoding="utf-8")
assert 'h.contains("fast_forward_pad")' in CFG # settings.toml read
assert 'f << "fast_forward_pad = "' in CFG # settings.toml write
# Fast-forward toggle: a press-to-latch twin of the hold shortcut. Keyboard
# [KeyMap] TurboToggle (default F9) and pad [hotkeys] fast_forward_toggle_pad
# (unbound by default) flip one latch that feeds the same fast-forward block.
assert "static int g_hotkey_pad_fast_forward_toggle = 0;" in MAIN
assert "PSX_ASSIST_BIND_FAST_FORWARD_TOGGLE" in MAIN
assert "static int g_manual_turbo_latched = 0;" in MAIN
assert "static void fast_forward_toggle_flip(void)" in MAIN
assert "fast_forward_toggle_poll_buttons();" in MAIN
assert "host_keymap_match_event(HOST_KEYMAP_TURBO_TOGGLE," in MAIN
assert "if (kb_turbo || g_manual_turbo_latched ||" in MAIN
assert MAIN.count("ls.assist_pad_bind[PSX_ASSIST_BIND_FAST_FORWARD_TOGGLE]") == \
MAIN.count("ls.assist_pad_bind[PSX_ASSIST_BIND_FAST_FORWARD]")
assert '"Fast-forward toggle",' in MAIN
assert 'h.contains("fast_forward_toggle_pad")' in CFG
assert 'f << "fast_forward_toggle_pad = "' in CFG
KEYMAP_H = (ROOT / "runtime" / "include" / "host_keymap.h").read_text(encoding="utf-8")
KEYMAP_C = (ROOT / "runtime" / "src" / "host_keymap.c").read_text(encoding="utf-8")
assert "HOST_KEYMAP_TURBO_TOGGLE," in KEYMAP_H
assert 'ieq(name, "TurboToggle")' in KEYMAP_C
assert "add_bind(HOST_KEYMAP_TURBO_TOGGLE, (int)SDLK_F9" in KEYMAP_C
# A present-but-empty / "None" [KeyMap] line is an explicit unbind: no
# built-in default is re-applied for that action (launcher Backspace-to-clear).
assert "int explicit_unbound;" in KEYMAP_C
assert "static int want_default(HostKeymapAction action)" in KEYMAP_C
assert "s_actions[HOST_KEYMAP_TURBO].count == 0" not in KEYMAP_C
assert "if (want_default(HOST_KEYMAP_TURBO))" in KEYMAP_C
print("host shortcut guard passed")
# Title Rewind block (psx_mod_set_rewind_blocked): refuses opening, stops
# capture, drops the direct Rewind claim, and clears at every mod session.
REW = (ROOT / "runtime" / "src" / "psx_rewind.c").read_text(encoding="utf-8")
assert 'extern "C" void psx_mod_set_rewind_blocked(int blocked) {' in MAIN
assert " psx_rewind_set_title_blocked(0);\n" in MAIN
assert "if (!direct_shortcut_active(i)) continue;" in MAIN
assert "const int direct = direct_shortcut_active(shortcut);" in MAIN
assert "psx_rewind_title_blocked());" in MAIN
assert " if (!psx_rewind_enabled() || s_open || psx_netplay_active() ||\n s_title_blocked)\n return;" in REW
assert "!psx_netplay_active() && !s_title_blocked)\n (void)do_capture" in REW
assert 'host_osd_push("Rewind is off in this mode", 1500);' in REW