Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -716,7 +716,12 @@ acceleration is **opt-in**, per game, so the accurate path is never compromised:
- **Turbo** — a hold-to-fast-forward key that compresses loads on demand
(keyboard `[KeyMap] Turbo`, default Tab; controller `[hotkeys]
fast_forward_pad`, default Select+L1, rebindable under the launcher's
Controller → Host Shortcuts alongside Rewind and Save states).
Controller → Host Shortcuts alongside Rewind and Save states). A
press-to-latch twin, **Turbo toggle** (`[KeyMap] TurboToggle`, default F9;
`[hotkeys] fast_forward_toggle_pad`, unbound by default), locks the same
speed until pressed again. Controller host shortcuts bound to a single
button or trigger are chords with Select — the launcher shows them as
`select + …`; only a two-button capture replaces the implicit Select.
- **The "Fast Loading (host pacing)" and "CD Speed" mods** — automatic
acceleration during load waits, shipped with every title and **off by
default**, with `turbo_audio_sink` keeping the SPU timeline coherent through
Expand Down
12 changes: 11 additions & 1 deletion recompiler/src/config_loader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2436,6 +2436,13 @@ UserSettings load_user_settings(const fs::path& path) {
s.has_hotkey_pad_fast_forward = true;
}
});
if (h.contains("fast_forward_toggle_pad")) try_get([&]{
const auto n = toml::find<int64_t>(h, "fast_forward_toggle_pad");
if (pad_bind_value_ok(n)) {
s.hotkey_pad_fast_forward_toggle = (int)n;
s.has_hotkey_pad_fast_forward_toggle = true;
}
});
}
if (doc.contains("launcher")) {
const toml::value& l = toml::find(doc, "launcher");
Expand Down Expand Up @@ -2680,7 +2687,7 @@ bool save_user_settings(const fs::path& path, const UserSettings& s) {
if (s.has_spu_hq)
f << "spu_hq = " << (s.spu_hq ? "true" : "false") << "\n";
if (s.has_hotkey_pad_rewind || s.has_hotkey_pad_save_state_menu ||
s.has_hotkey_pad_fast_forward) {
s.has_hotkey_pad_fast_forward || s.has_hotkey_pad_fast_forward_toggle) {
f << "\n[hotkeys]\n";
if (s.has_hotkey_pad_rewind)
f << "rewind_pad = " << s.hotkey_pad_rewind << "\n";
Expand All @@ -2689,6 +2696,9 @@ bool save_user_settings(const fs::path& path, const UserSettings& s) {
<< s.hotkey_pad_save_state_menu << "\n";
if (s.has_hotkey_pad_fast_forward)
f << "fast_forward_pad = " << s.hotkey_pad_fast_forward << "\n";
if (s.has_hotkey_pad_fast_forward_toggle)
f << "fast_forward_toggle_pad = "
<< s.hotkey_pad_fast_forward_toggle << "\n";
}
if (s.has_skip_launcher)
f << "\n[launcher]\nskip_launcher = " << (s.skip_launcher ? "true" : "false") << "\n";
Expand Down
1 change: 1 addition & 0 deletions recompiler/src/config_loader.h
Original file line number Diff line number Diff line change
Expand Up @@ -1265,6 +1265,7 @@ struct UserSettings {
// fast_forward_pad: hold-to-fast-forward, the controller twin of the
// keyboard [KeyMap] Turbo (Tab). 0 = unbound.
bool has_hotkey_pad_fast_forward = false; int hotkey_pad_fast_forward = 1528; /* select+l1 */
bool has_hotkey_pad_fast_forward_toggle = false; int hotkey_pad_fast_forward_toggle = 0; /* unbound */
// [audio]
bool has_spu_hq = false; bool spu_hq = false;
bool has_audio_freq = false; int audio_freq = 44100;
Expand Down
1 change: 1 addition & 0 deletions runtime/include/host_keymap.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ typedef enum HostKeymapAction {
HOST_KEYMAP_REWIND, /* default F8 */
HOST_KEYMAP_SAVE_STATE_MENU, /* default F7 */
HOST_KEYMAP_SCANLINES, /* default F6 */
HOST_KEYMAP_TURBO_TOGGLE, /* default F9; latches Turbo until pressed again */
HOST_KEYMAP_ACTION_COUNT
} HostKeymapAction;

Expand Down
3 changes: 3 additions & 0 deletions runtime/src/host_keymap.c
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ static void apply_defaults(void) {
add_bind(HOST_KEYMAP_SAVE_STATE_MENU, (int)SDLK_F7, (int)SDL_SCANCODE_F7, 0);
if (s_actions[HOST_KEYMAP_SCANLINES].count == 0)
add_bind(HOST_KEYMAP_SCANLINES, (int)SDLK_F6, (int)SDL_SCANCODE_F6, 0);
if (s_actions[HOST_KEYMAP_TURBO_TOGGLE].count == 0)
add_bind(HOST_KEYMAP_TURBO_TOGGLE, (int)SDLK_F9, (int)SDL_SCANCODE_F9, 0);
}

/* Parse one "Ctrl+Alt+PageUp" token into key+mods. */
Expand Down Expand Up @@ -157,6 +159,7 @@ static HostKeymapAction action_for_key(const char *name) {
if (ieq(name, "Rewind")) return HOST_KEYMAP_REWIND;
if (ieq(name, "SaveStateMenu")) return HOST_KEYMAP_SAVE_STATE_MENU;
if (ieq(name, "Scanlines")) return HOST_KEYMAP_SCANLINES;
if (ieq(name, "TurboToggle")) return HOST_KEYMAP_TURBO_TOGGLE;
return HOST_KEYMAP_ACTION_COUNT;
}

Expand Down
68 changes: 65 additions & 3 deletions runtime/src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1218,6 +1218,7 @@ static int g_rewind_interval = 15; /* frames between snaps (1/4/8/12/1
static int g_hotkey_pad_rewind = 1272; /* select + r3 */
static int g_hotkey_pad_save_state_menu = 2040;/* select + r1 */
static int g_hotkey_pad_fast_forward = 1528; /* select + l1 (hold) */
static int g_hotkey_pad_fast_forward_toggle = 0; /* unbound: latch fast-forward */
static uint32_t g_savestate_input_guard_min_until = 0;
static uint32_t g_savestate_input_guard_max_until = 0;
static int g_headless = 0; /* debug/CI frontend: no SDL window/audio */
Expand Down Expand Up @@ -5970,6 +5971,7 @@ enum {
PSX_ASSIST_BIND_REWIND = 0,
PSX_ASSIST_BIND_SAVE_STATE_MENU,
PSX_ASSIST_BIND_FAST_FORWARD, /* hold-to-fast-forward; pad twin of [KeyMap] Turbo */
PSX_ASSIST_BIND_FAST_FORWARD_TOGGLE, /* press-to-latch; pad twin of [KeyMap] TurboToggle */
PSX_ASSIST_BIND_COUNT
};

Expand Down Expand Up @@ -6241,6 +6243,35 @@ static void savestate_menu_poll_toggle_buttons(void) {
was_down = down;
}

/* Latched fast-forward: [KeyMap] TurboToggle (default F9) or the [hotkeys]
* fast_forward_toggle_pad shortcut flips this; while set, the manual
* fast-forward block runs exactly as if Turbo were held. Cleared by the next
* press, so a hold-to-run Turbo release never cancels a latched run. */
static int g_manual_turbo_latched = 0;

static void fast_forward_toggle_flip(void) {
char msg[40];
g_manual_turbo_latched = !g_manual_turbo_latched;
if (!g_manual_turbo_latched) {
host_osd_push("Fast forward: off", 900);
return;
}
const int mult = manual_fast_forward_multiplier();
if (mult < 0)
snprintf(msg, sizeof(msg), "Fast forward: max (locked)");
else
snprintf(msg, sizeof(msg), "Fast forward: %dx (locked)", mult);
host_osd_push(msg, 900);
}

static void fast_forward_toggle_poll_buttons(void) {
static int was_down;
int down = hotkey_pad_binding_down(g_hotkey_pad_fast_forward_toggle);
if (down && !was_down)
fast_forward_toggle_flip();
was_down = down;
}

static void rewind_poll_nav(uint32_t now_ms) {
const Uint8 *keys = SDL_GetKeyboardState(NULL);
int left = keys[SDL_SCANCODE_LEFT] ? 1 : 0;
Expand Down Expand Up @@ -6609,6 +6640,12 @@ static NetplayVblankEpilogue sdl_vblank_present_body(void) {
debug_force_cd_reinsert();
host_osd_push("CD reinsert", 1500);
}
else if (!key_repeat &&
host_keymap_match_event(HOST_KEYMAP_TURBO_TOGGLE,
(int)key, (int)scancode,
(int)mod)) {
fast_forward_toggle_flip();
}
else if (!key_repeat &&
host_keymap_match_event(HOST_KEYMAP_DISPLAY_PERF,
(int)key, (int)scancode,
Expand Down Expand Up @@ -6668,6 +6705,7 @@ static NetplayVblankEpilogue sdl_vblank_present_body(void) {
}
savestate_menu_poll_toggle_buttons();
rewind_poll_toggle_buttons();
fast_forward_toggle_poll_buttons();
psx_rewind_note_frame();
psx_rewind_present_tick((uint32_t)SDL_GetTicks());
if (savestate_menu_open)
Expand Down Expand Up @@ -6867,14 +6905,17 @@ static NetplayVblankEpilogue sdl_vblank_present_body(void) {
/* Keyboard ([KeyMap] Turbo, default Tab) or the controller host
* shortcut ([hotkeys] fast_forward_pad, default select+L1). Both are
* hold-to-run; the pad chord goes through the same combo matcher as
* Rewind / Save states so the launcher's binding editor covers it. */
* Rewind / Save states so the launcher's binding editor covers it.
* g_manual_turbo_latched is the press-to-lock twin (TurboToggle /
* fast_forward_toggle_pad) and drives the same path. */
const bool kb_turbo = host_hotkey_input_focused() &&
host_keymap_down(HOST_KEYMAP_TURBO, keys, (int)SDL_GetModState());
if (kb_turbo || hotkey_pad_binding_down(g_hotkey_pad_fast_forward)) {
if (kb_turbo || g_manual_turbo_latched ||
hotkey_pad_binding_down(g_hotkey_pad_fast_forward)) {
const int mult = manual_fast_forward_multiplier();
const int present_every = (mult < 0) ? 4 : (mult <= 4 ? 2 : 4);
manual_turbo_active = true;
if (!turbo_was_down) {
if (!turbo_was_down && !g_manual_turbo_latched) {
char msg[40];
if (mult < 0)
snprintf(msg, sizeof(msg), "Fast forward: max");
Expand Down Expand Up @@ -10861,6 +10902,7 @@ namespace {
"Rewind",
"Save states",
"Fast-forward",
"Fast-forward toggle",
};

void ae_rui_set_sidecar_paths(const char* argv0) {
Expand Down Expand Up @@ -11793,6 +11835,9 @@ int main(int argc, char** argv) {
g_hotkey_pad_fast_forward = normalize_hotkey_pad_binding(
us.hotkey_pad_fast_forward,
PSX_HOTKEY_PAD_SELECT_L1);
if (us.has_hotkey_pad_fast_forward_toggle)
g_hotkey_pad_fast_forward_toggle = normalize_hotkey_pad_binding(
us.hotkey_pad_fast_forward_toggle, 0);
if (us.has_bios_path && !bios_from_cli && !us.bios_path.empty()) {
settings_bios_storage = us.bios_path.string();
bios_path = settings_bios_storage.c_str();
Expand Down Expand Up @@ -12309,6 +12354,8 @@ int main(int argc, char** argv) {
seed.has_hotkey_pad_save_state_menu = true;
seed.hotkey_pad_fast_forward = g_hotkey_pad_fast_forward;
seed.has_hotkey_pad_fast_forward = true;
seed.hotkey_pad_fast_forward_toggle = g_hotkey_pad_fast_forward_toggle;
seed.has_hotkey_pad_fast_forward_toggle = true;
seed.skip_launcher = skip_launcher_setting; seed.has_skip_launcher = true;
if (has_netplay_player_name) {
seed.netplay_player_name = netplay_player_name;
Expand Down Expand Up @@ -12496,6 +12543,8 @@ int main(int argc, char** argv) {
ls.assist_pad_bind[PSX_ASSIST_BIND_FAST_FORWARD] =
normalize_hotkey_pad_binding(seed.hotkey_pad_fast_forward,
PSX_HOTKEY_PAD_SELECT_L1);
ls.assist_pad_bind[PSX_ASSIST_BIND_FAST_FORWARD_TOGGLE] =
normalize_hotkey_pad_binding(seed.hotkey_pad_fast_forward_toggle, 0);
ls.auto_skip_fmv = seed.auto_skip_fmv ? 1 : 0;
ls.turbo_loads = seed.turbo_loads ? 1 : 0;
/* Localization: index of resolved_language within lang_menu_options
Expand Down Expand Up @@ -12798,6 +12847,9 @@ int main(int argc, char** argv) {
ls.assist_pad_bind[PSX_ASSIST_BIND_FAST_FORWARD],
PSX_HOTKEY_PAD_SELECT_L1);
seed.has_hotkey_pad_fast_forward = true;
seed.hotkey_pad_fast_forward_toggle = normalize_hotkey_pad_binding(
ls.assist_pad_bind[PSX_ASSIST_BIND_FAST_FORWARD_TOGGLE], 0);
seed.has_hotkey_pad_fast_forward_toggle = true;
seed.auto_skip_fmv = ls.auto_skip_fmv != 0;
seed.has_auto_skip_fmv = skip_fmv_offered;
seed.turbo_loads = ls.turbo_loads != 0;
Expand Down Expand Up @@ -13018,6 +13070,9 @@ int main(int argc, char** argv) {
g_hotkey_pad_fast_forward = seed.has_hotkey_pad_fast_forward
? seed.hotkey_pad_fast_forward
: PSX_HOTKEY_PAD_SELECT_L1;
g_hotkey_pad_fast_forward_toggle = seed.has_hotkey_pad_fast_forward_toggle
? seed.hotkey_pad_fast_forward_toggle
: 0;
skip_launcher_setting = seed.skip_launcher;
if (seed.has_bios_path) {
settings_bios_storage = seed.bios_path.string();
Expand Down Expand Up @@ -14508,6 +14563,8 @@ int main(int argc, char** argv) {
normalize_hotkey_pad_binding(
g_hotkey_pad_fast_forward,
PSX_HOTKEY_PAD_SELECT_L1);
ls.assist_pad_bind[PSX_ASSIST_BIND_FAST_FORWARD_TOGGLE] =
normalize_hotkey_pad_binding(g_hotkey_pad_fast_forward_toggle, 0);
ls.aspect_index = (g_video_aspect_num * 9 == g_video_aspect_den * 21) ? 2
: (g_video_aspect_num == 16 && g_video_aspect_den == 9) ? 1 : 0;
ls.language_index = 0;
Expand Down Expand Up @@ -14811,6 +14868,9 @@ int main(int argc, char** argv) {
ls.assist_pad_bind[PSX_ASSIST_BIND_FAST_FORWARD],
PSX_HOTKEY_PAD_SELECT_L1);
us.has_hotkey_pad_fast_forward = true;
us.hotkey_pad_fast_forward_toggle = normalize_hotkey_pad_binding(
ls.assist_pad_bind[PSX_ASSIST_BIND_FAST_FORWARD_TOGGLE], 0);
us.has_hotkey_pad_fast_forward_toggle = true;
us.auto_skip_fmv = ls.auto_skip_fmv != 0;
us.has_auto_skip_fmv = skip_fmv_offered;
us.turbo_loads = ls.turbo_loads != 0;
Expand Down Expand Up @@ -14894,6 +14954,8 @@ int main(int argc, char** argv) {
g_hotkey_pad_fast_forward = normalize_hotkey_pad_binding(
ls.assist_pad_bind[PSX_ASSIST_BIND_FAST_FORWARD],
PSX_HOTKEY_PAD_SELECT_L1);
g_hotkey_pad_fast_forward_toggle = normalize_hotkey_pad_binding(
ls.assist_pad_bind[PSX_ASSIST_BIND_FAST_FORWARD_TOGGLE], 0);
switch (ls.aspect_index) {
case 2: g_video_aspect_num = 21; g_video_aspect_den = 9; break;
case 1: g_video_aspect_num = 16; g_video_aspect_den = 9; break;
Expand Down
23 changes: 22 additions & 1 deletion runtime/tests/test_rewind_toggle_combo.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,33 @@
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 "if (kb_turbo || hotkey_pad_binding_down(g_hotkey_pad_fast_forward)) {" in MAIN
assert " hotkey_pad_binding_down(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

print("host shortcut guard passed")