diff --git a/recompiler/src/config_loader.cpp b/recompiler/src/config_loader.cpp index 386b1c64..699c842f 100644 --- a/recompiler/src/config_loader.cpp +++ b/recompiler/src/config_loader.cpp @@ -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(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(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; } diff --git a/recompiler/src/config_loader.h b/recompiler/src/config_loader.h index 23cbe8ee..74f90fa7 100644 --- a/recompiler/src/config_loader.h +++ b/recompiler/src/config_loader.h @@ -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.