Fix the pad-hotkey range check rejecting every chord

[hotkeys] rewind_pad / save_state_menu_pad accept the recomp-ui pad encoding,
where 1000 + a button bitmask is a CHORD. The parse guarded on `n < 256`,
which admits single buttons and rejects every chord there is — including the
two values this same file writes back out (1272 = select+r3, 2040 =
select+r1). A saved chord was silently dropped on load and the defaults
quietly reinstated, so the pad hotkeys were never actually configurable.

The bound predates the chord encoding; the runtime's own
normalize_hotkey_pad_binding has handled combos correctly all along.

Replaced with pad_bind_value_ok(), sharing one documented bound with the
encoding it validates. Verified against a live boot with rewind_pad = 1017
(select+cross): parsed has=0 value=1272 before, has=1 value=1017 after.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Alex Vanderveen
2026-08-17 16:52:18 -04:00
parent b423486563
commit 165fae0aa1
2 changed files with 16 additions and 2 deletions
+2 -2
View File
@@ -2356,14 +2356,14 @@ UserSettings load_user_settings(const fs::path& path) {
const toml::value& h = toml::find(doc, "hotkeys");
if (h.contains("rewind_pad")) try_get([&]{
const auto n = toml::find<int64_t>(h, "rewind_pad");
if (n >= 0 && n < 256) {
if (pad_bind_value_ok(n)) {
s.hotkey_pad_rewind = (int)n;
s.has_hotkey_pad_rewind = true;
}
});
if (h.contains("save_state_menu_pad")) try_get([&]{
const auto n = toml::find<int64_t>(h, "save_state_menu_pad");
if (n >= 0 && n < 256) {
if (pad_bind_value_ok(n)) {
s.hotkey_pad_save_state_menu = (int)n;
s.has_hotkey_pad_save_state_menu = true;
}
+14
View File
@@ -55,6 +55,20 @@ inline constexpr int VIDEO_RENDERER_OPENGL = 1;
inline constexpr int VIDEO_RENDERER_VULKAN = 2;
inline constexpr int DEFAULT_VIDEO_RENDERER = VIDEO_RENDERER_OPENGL;
// Controller-hotkey bind encoding, mirroring recomp-ui's RECOMP_LAUNCHER_PAD_*
// (recomp_launcher.h): 0 = unbound, 1..99 = button (1 + SDL button code),
// 100..999 = axis, 1000+ = a CHORD, encoded as 1000 + a bitmask of SDL button
// codes. Two buttons held together is the normal case (select+r3), so a real
// value here is routinely five digits.
//
// The bound matters: a naive `< 256` check silently rejects every chord — which
// is exactly what shipped, so a saved rewind_pad = 1272 was dropped on load and
// the pad hotkeys were only ever configurable to single buttons. SDL defines
// ~21 gamepad buttons, so cap the mask at 32 bits' worth and let the runtime
// ignore bits no controller can produce.
inline constexpr int PAD_BIND_MAX = 1000 + (1 << 21);
inline bool pad_bind_value_ok(long long n) { return n >= 0 && n < PAD_BIND_MAX; }
// FMV present reconstruction, ordered least → most smoothing. See
// RuntimeConfig::video_fmv_filter for what each one does and why bicubic is
// the default. Shared by game.toml/settings parsing and runtime startup.