Skip to content

Commit

Permalink
System: Add 'Fast Forward Memory Card Access' option
Browse files Browse the repository at this point in the history
Does what it says on the tin.
  • Loading branch information
stenzek committed Feb 15, 2025
1 parent 13d58d8 commit e9bfca8
Show file tree
Hide file tree
Showing 8 changed files with 50 additions and 5 deletions.
6 changes: 6 additions & 0 deletions src/core/fullscreen_ui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4016,6 +4016,10 @@ void FullscreenUI::DrawConsoleSettingsPage()
"may vary between games."),
"BIOS", "FastForwardBoot", false,
GetEffectiveBoolSetting(bsi, "BIOS", "PatchFastBoot", Settings::DEFAULT_FAST_BOOT_VALUE));
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_SD_CARD, "Fast Forward Memory Card Access"),
FSUI_CSTR("Fast forwards through memory card access, both loading and saving. Can reduce waiting "
"times in games that frequently access memory cards."),
"MemoryCards", "FastForwardAccess", false);
DrawToggleSetting(
bsi, FSUI_ICONSTR(ICON_FA_MEMORY, "Enable 8MB RAM"),
FSUI_CSTR("Enables an additional 6MB of RAM to obtain a total of 2+6 = 8MB, usually present on dev consoles."),
Expand Down Expand Up @@ -8980,8 +8984,10 @@ TRANSLATE_NOOP("FullscreenUI", "Failed to load shader {}. It may be invalid.\nEr
TRANSLATE_NOOP("FullscreenUI", "Failed to save controller preset '{}'.");
TRANSLATE_NOOP("FullscreenUI", "Fast Boot");
TRANSLATE_NOOP("FullscreenUI", "Fast Forward Boot");
TRANSLATE_NOOP("FullscreenUI", "Fast Forward Memory Card Access");
TRANSLATE_NOOP("FullscreenUI", "Fast Forward Speed");
TRANSLATE_NOOP("FullscreenUI", "Fast Forward Volume");
TRANSLATE_NOOP("FullscreenUI", "Fast forwards through memory card access, both loading and saving. Can reduce waiting times in games that frequently access memory cards.");
TRANSLATE_NOOP("FullscreenUI", "Fast forwards through the early loading process when fast booting, saving time. Results may vary between games.");
TRANSLATE_NOOP("FullscreenUI", "File Size");
TRANSLATE_NOOP("FullscreenUI", "File Size: %u MB (%u MB on disk)");
Expand Down
6 changes: 4 additions & 2 deletions src/core/memory_card.cpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
// SPDX-FileCopyrightText: 2019-2025 Connor McLaughlin <stenzek@gmail.com>
// SPDX-License-Identifier: CC-BY-NC-ND-4.0

#include "memory_card.h"
#include "host.h"
#include "system.h"
#include "system_private.h"

#include "util/imgui_manager.h"
#include "util/state_wrapper.h"
Expand Down Expand Up @@ -143,6 +143,7 @@ bool MemoryCard::Transfer(const u8 data_in, u8* data_out)
{
DEV_LOG("Reading memory card sector {}", m_address);
m_checksum = Truncate8(m_address >> 8) ^ Truncate8(m_address) ^ bits;
System::OnMemoryCardAccessed();
}
else
{
Expand Down Expand Up @@ -178,6 +179,7 @@ bool MemoryCard::Transfer(const u8 data_in, u8* data_out)
INFO_LOG("Writing memory card sector {}", m_address);
m_checksum = Truncate8(m_address >> 8) ^ Truncate8(m_address) ^ data_in;
m_FLAG.no_write_yet = false;
System::OnMemoryCardAccessed();
}
else
{
Expand Down
2 changes: 2 additions & 0 deletions src/core/settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,7 @@ void Settings::Load(const SettingsInterface& si, const SettingsInterface& contro
memory_card_paths[0] = si.GetStringValue("MemoryCards", "Card1Path", "");
memory_card_paths[1] = si.GetStringValue("MemoryCards", "Card2Path", "");
memory_card_use_playlist_title = si.GetBoolValue("MemoryCards", "UsePlaylistTitle", true);
memory_card_fast_forward_access = si.GetBoolValue("MemoryCards", "FastForwardAccess", false);

achievements_enabled = si.GetBoolValue("Cheevos", "Enabled", false);
achievements_hardcore_mode = si.GetBoolValue("Cheevos", "ChallengeMode", false);
Expand Down Expand Up @@ -698,6 +699,7 @@ void Settings::Save(SettingsInterface& si, bool ignore_base) const
si.DeleteValue("MemoryCards", "Card2Path");

si.SetBoolValue("MemoryCards", "UsePlaylistTitle", memory_card_use_playlist_title);
si.SetBoolValue("MemoryCards", "FastForwardAccess", memory_card_fast_forward_access);

si.SetStringValue("ControllerPorts", "MultitapMode", GetMultitapModeName(multitap_mode));

Expand Down
1 change: 1 addition & 0 deletions src/core/settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,7 @@ struct Settings : public GPUSettings
bool bios_fast_forward_boot : 1 = false;
bool enable_8mb_ram : 1 = false;
bool memory_card_use_playlist_title : 1 = true;
bool memory_card_fast_forward_access : 1 = false;
bool pio_switch_active : 1 = true;
bool pio_flash_write_enable : 1 = false;
bool pcdrv_enable_writes : 1 = false;
Expand Down
21 changes: 20 additions & 1 deletion src/core/system.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ static constexpr float PRE_FRAME_SLEEP_UPDATE_INTERVAL = 1.0f;
static constexpr const char FALLBACK_EXE_NAME[] = "PSX.EXE";
static constexpr u32 MAX_SKIPPED_DUPLICATE_FRAME_COUNT = 2; // 20fps minimum
static constexpr u32 MAX_SKIPPED_TIMEOUT_FRAME_COUNT = 1; // 30fps minimum
static constexpr u8 MEMORY_CARD_FAST_FORWARD_FRAMES = 30;

namespace {

Expand Down Expand Up @@ -263,6 +264,7 @@ struct ALIGN_TO_CACHE_LINE StateVars
bool turbo_enabled = false;

bool runahead_replay_pending = false;
u8 memory_card_fast_forward_frames = 0;

u32 skipped_frame_count = 0;
u32 last_presented_internal_frame_number = 0;
Expand Down Expand Up @@ -2140,6 +2142,14 @@ void System::FrameDone()
Timer::Value current_time = Timer::GetCurrentValue();
GTE::UpdateFreecam(current_time);

// memory card fast forward
if (s_state.memory_card_fast_forward_frames > 0)
{
s_state.memory_card_fast_forward_frames--;
if (s_state.memory_card_fast_forward_frames == 0)
UpdateSpeedLimiterState();
}

// Frame step after runahead, otherwise the pause takes precedence and the replay never happens.
if (s_state.frame_step_request)
{
Expand Down Expand Up @@ -3523,7 +3533,7 @@ void System::UpdateSpeedLimiterState()
{
DebugAssert(IsValid());

s_state.target_speed = IsFastForwardingBoot() ?
s_state.target_speed = (IsFastForwardingBoot() || s_state.memory_card_fast_forward_frames > 0) ?
0.0f :
(s_state.turbo_enabled ? g_settings.turbo_speed :
(s_state.fast_forward_enabled ? g_settings.fast_forward_speed :
Expand Down Expand Up @@ -3931,6 +3941,15 @@ bool System::IsSavingMemoryCards()
return false;
}

void System::OnMemoryCardAccessed()
{
if (!g_settings.memory_card_fast_forward_access)
return;

if (std::exchange(s_state.memory_card_fast_forward_frames, MEMORY_CARD_FAST_FORWARD_FRAMES) == 0)
UpdateSpeedLimiterState();
}

void System::SwapMemoryCards()
{
if (!IsValid())
Expand Down
3 changes: 3 additions & 0 deletions src/core/system_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ void DisplayWindowResized();
/// Updates the internal GTE aspect ratio. Use with "match display" aspect ratio setting.
void UpdateGTEAspectRatio();

/// Called on card read/write, handles fast forwarding.
void OnMemoryCardAccessed();

/// Immediately terminates the virtual machine, no state is saved.
void AbnormalShutdown(const std::string_view reason);

Expand Down
5 changes: 5 additions & 0 deletions src/duckstation-qt/consolesettingswidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ ConsoleSettingsWidget::ConsoleSettingsWidget(SettingsWindow* dialog, QWidget* pa
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.fastBoot, "BIOS", "PatchFastBoot", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.fastForwardBoot, "BIOS", "FastForwardBoot", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enable8MBRAM, "Console", "Enable8MBRAM", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.fastForwardMemoryCardAccess, "MemoryCards",
+"FastForwardAccess", false);
connect(m_ui.fastBoot, &QCheckBox::checkStateChanged, this, &ConsoleSettingsWidget::onFastBootChanged);
onFastBootChanged();

Expand Down Expand Up @@ -92,6 +94,9 @@ ConsoleSettingsWidget::ConsoleSettingsWidget(SettingsWindow* dialog, QWidget* pa
"to use a larger heap size for "
"this additional RAM to be usable. Titles which rely on memory mirrors may break, so it should only be used "
"with compatible mods."));
m_dialog->registerWidgetHelp(m_ui.fastForwardMemoryCardAccess, tr("Fast Forward Memory Card Access"), tr("Unchecked"),
tr("Fast forwards through memory card access, both loading and saving. Can reduce "
"waiting times in games that frequently access memory cards."));

dialog->registerWidgetHelp(m_ui.cpuExecutionMode, tr("Execution Mode"), tr("Recompiler (Fastest)"),
tr("Determines how the emulated CPU executes instructions."));
Expand Down
11 changes: 9 additions & 2 deletions src/duckstation-qt/consolesettingswidget.ui
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,21 @@
</property>
</widget>
</item>
<item row="0" column="1">
<item row="1" column="0">
<widget class="QCheckBox" name="fastForwardBoot">
<property name="text">
<string>Fast Forward Boot</string>
</property>
</widget>
</item>
<item row="1" column="0">
<item row="0" column="1">
<widget class="QCheckBox" name="fastForwardMemoryCardAccess">
<property name="text">
<string>Fast Forward Memory Card Access</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="enable8MBRAM">
<property name="text">
<string>Enable 8MB RAM (Dev Console)</string>
Expand Down

0 comments on commit e9bfca8

Please sign in to comment.