From c2bbef066dd8478c602e00c842e675b4993ff05d Mon Sep 17 00:00:00 2001 From: Stenzek Date: Mon, 22 Sep 2025 20:37:43 +1000 Subject: [PATCH] System: Simplify memory card opening Now cards no longer reopen when changing discs in a multi-disc game. --- src/core/memory_card.cpp | 4 +- src/core/memory_card.h | 2 +- src/core/settings.cpp | 2 +- src/core/system.cpp | 313 +++++++++++++++++++-------------------- src/core/system.h | 2 +- 5 files changed, 161 insertions(+), 162 deletions(-) diff --git a/src/core/memory_card.cpp b/src/core/memory_card.cpp index 424f244d7..f69f267d1 100644 --- a/src/core/memory_card.cpp +++ b/src/core/memory_card.cpp @@ -295,10 +295,10 @@ std::unique_ptr MemoryCard::Create() return mc; } -std::unique_ptr MemoryCard::Open(std::string_view path) +std::unique_ptr MemoryCard::Open(std::string path) { std::unique_ptr mc = std::make_unique(); - mc->m_path = path; + mc->m_path = std::move(path); Error error; if (!FileSystem::FileExists(mc->m_path.c_str())) [[unlikely]] diff --git a/src/core/memory_card.h b/src/core/memory_card.h index 93a446040..bf1ff0b8a 100644 --- a/src/core/memory_card.h +++ b/src/core/memory_card.h @@ -23,7 +23,7 @@ public: static constexpr u32 STATE_SIZE = 1 + 1 + 2 + 1 + 1 + 1 + MemoryCardImage::DATA_SIZE + 1; static std::unique_ptr Create(); - static std::unique_ptr Open(std::string_view path); + static std::unique_ptr Open(std::string path); const MemoryCardImage::DataArray& GetData() const { return m_data; } MemoryCardImage::DataArray& GetData() { return m_data; } diff --git a/src/core/settings.cpp b/src/core/settings.cpp index ec78f8c14..075edb714 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -2591,7 +2591,7 @@ void EmuFolders::Update() System::ReloadGameSettings(false); if (System::IsValid() && old_memorycards != EmuFolders::MemoryCards) - System::UpdateMemoryCardTypes(); + System::UpdateMemoryCards(); } void EmuFolders::EnsureFolderExists(const std::string& path) diff --git a/src/core/system.cpp b/src/core/system.cpp index cd9adf3c9..53ca2623d 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -203,8 +203,7 @@ static std::string_view GetCurrentGameSaveTitle(); static void UpdateControllers(); static void ResetControllers(); -static void ReloadMemoryCardsFromGameChange(); -static std::unique_ptr GetMemoryCardForSlot(u32 slot, MemoryCardType type); +static std::string GetMemoryCardPathForSlot(u32 slot, MemoryCardType type); static void UpdateMultitaps(); static std::string GetMediaPathFromSaveState(const char* path); @@ -1859,7 +1858,7 @@ bool System::BootSystem(SystemBootParameters parameters, Error* error) s_state.exe_override = std::move(exe_override); UpdateControllers(); - UpdateMemoryCardTypes(); + UpdateMemoryCards(); UpdateMultitaps(); InternalReset(); @@ -3787,170 +3786,40 @@ void System::ResetControllers() } } -std::unique_ptr System::GetMemoryCardForSlot(u32 slot, MemoryCardType type) -{ - // Disable memory cards when running PSFs. - const bool is_running_psf = !s_state.running_game_path.empty() && IsPsfPath(s_state.running_game_path.c_str()); - if (is_running_psf) - return nullptr; - - std::string message_key = fmt::format("MemoryCard{}SharedWarning", slot); - - switch (type) - { - case MemoryCardType::PerGame: - { - if (s_state.running_game_serial.empty()) - { - Host::AddIconOSDMessage( - std::move(message_key), ICON_PF_MEMORY_CARD, - fmt::format(TRANSLATE_FS("System", "Per-game memory card cannot be used for slot {} as the running " - "game has no code. Using shared card instead."), - slot + 1u), - Host::OSD_INFO_DURATION); - return MemoryCard::Open(g_settings.GetSharedMemoryCardPath(slot)); - } - else - { - Host::RemoveKeyedOSDMessage(std::move(message_key)); - return MemoryCard::Open(g_settings.GetGameMemoryCardPath(s_state.running_game_serial, slot)); - } - } - - case MemoryCardType::PerGameTitle: - { - if (s_state.running_game_title.empty()) - { - Host::AddIconOSDMessage( - std::move(message_key), ICON_PF_MEMORY_CARD, - fmt::format(TRANSLATE_FS("System", "Per-game memory card cannot be used for slot {} as the running " - "game has no title. Using shared card instead."), - slot + 1u), - Host::OSD_INFO_DURATION); - return MemoryCard::Open(g_settings.GetSharedMemoryCardPath(slot)); - } - else - { - const std::string_view game_title = (s_state.running_game_custom_title || !s_state.running_game_entry) ? - std::string_view(s_state.running_game_title) : - s_state.running_game_entry->GetSaveTitle(); - std::string card_path; - - // Multi-disc game - use disc set name. - if (s_state.running_game_entry && s_state.running_game_entry->disc_set) - { - card_path = g_settings.GetGameMemoryCardPath( - Path::SanitizeFileName(s_state.running_game_entry->disc_set->GetSaveTitle()), slot); - } - - // But prefer a disc-specific card if one already exists. - std::string disc_card_path = g_settings.GetGameMemoryCardPath(Path::SanitizeFileName(game_title), slot); - if (disc_card_path != card_path) - { - if (card_path.empty() || !g_settings.memory_card_use_playlist_title || - FileSystem::FileExists(disc_card_path.c_str())) - { - if (g_settings.memory_card_use_playlist_title && !card_path.empty()) - { - Host::AddIconOSDMessage( - fmt::format("DiscSpecificMC{}", slot), ICON_PF_MEMORY_CARD, - fmt::format(TRANSLATE_FS("System", "Using disc-specific memory card '{}' instead of per-game card."), - Path::GetFileName(disc_card_path)), - Host::OSD_INFO_DURATION); - } - - card_path = std::move(disc_card_path); - } - } - - Host::RemoveKeyedOSDMessage(std::move(message_key)); - return MemoryCard::Open(card_path.c_str()); - } - } - - case MemoryCardType::PerGameFileTitle: - { - const std::string display_name(FileSystem::GetDisplayNameFromPath(s_state.running_game_path)); - const std::string_view file_title(Path::GetFileTitle(display_name)); - if (file_title.empty()) - { - Host::AddIconOSDMessage( - std::move(message_key), ICON_PF_MEMORY_CARD, - fmt::format(TRANSLATE_FS("System", "Per-game memory card cannot be used for slot {} as the running " - "game has no path. Using shared card instead."), - slot + 1u)); - return MemoryCard::Open(g_settings.GetSharedMemoryCardPath(slot)); - } - else - { - Host::RemoveKeyedOSDMessage(std::move(message_key)); - return MemoryCard::Open(g_settings.GetGameMemoryCardPath(Path::SanitizeFileName(file_title).c_str(), slot)); - } - } - - case MemoryCardType::Shared: - { - Host::RemoveKeyedOSDMessage(std::move(message_key)); - return MemoryCard::Open(g_settings.GetSharedMemoryCardPath(slot)); - } - - case MemoryCardType::NonPersistent: - { - Host::RemoveKeyedOSDMessage(std::move(message_key)); - return MemoryCard::Create(); - } - - case MemoryCardType::None: - default: - { - Host::RemoveKeyedOSDMessage(std::move(message_key)); - return nullptr; - } - } -} - -void System::UpdateMemoryCardTypes() +void System::UpdateMemoryCards() { for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++) { - Pad::SetMemoryCard(i, nullptr); - const MemoryCardType type = g_settings.memory_card_types[i]; - std::unique_ptr card = GetMemoryCardForSlot(i, type); - if (card) + if (type == MemoryCardType::None) { - if (const std::string& path = card->GetPath(); !path.empty()) - INFO_LOG("Memory Card Slot {}: {}", i + 1, path); - - Pad::SetMemoryCard(i, std::move(card)); + Pad::SetMemoryCard(i, {}); + continue; } - } -} -void System::ReloadMemoryCardsFromGameChange() -{ - if (!g_settings.HasAnyPerGameMemoryCards()) - return; + std::string path = GetMemoryCardPathForSlot(i, type); - Host::AddIconOSDMessage("ReloadMemoryCardsFromGameChange", ICON_PF_MEMORY_CARD, - TRANSLATE_STR("System", "Game changed, reloading memory cards."), Host::OSD_INFO_DURATION); - - for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++) - { - const MemoryCardType type = g_settings.memory_card_types[i]; - if (!Settings::IsPerGameMemoryCardType(type)) + const MemoryCard* current_card = Pad::GetMemoryCard(i); + const bool path_changed = (!current_card || current_card->GetPath() != path); + INFO_LOG("Memory Card {}: {}{}", i + 1, path, path_changed ? "" : " [UNCHANGED]"); + if (!path_changed) continue; - Pad::SetMemoryCard(i, nullptr); - - std::unique_ptr card = GetMemoryCardForSlot(i, type); - if (card) + if (current_card) { - if (const std::string& path = card->GetPath(); !path.empty()) - INFO_LOG("Memory Card Slot {}: {}", i + 1, path); - - Pad::SetMemoryCard(i, std::move(card)); + Host::AddIconOSDMessage(fmt::format("MemoryCardChange{}", i), ICON_PF_MEMORY_CARD, + fmt::format(TRANSLATE_FS("OSDMessage", "Memory card in slot {} changed to '{}'."), i + 1, + Path::GetFileName(path)), + Host::OSD_INFO_DURATION); } + + std::unique_ptr card; + if (!path.empty()) + card = MemoryCard::Open(std::move(path)); + else + card = MemoryCard::Create(); + + Pad::SetMemoryCard(i, std::move(card)); } } @@ -4232,7 +4101,8 @@ void System::UpdateRunningGame(const std::string& path, CDImage* image, bool boo if (had_setting_overrides) ApplySettings(true); - ReloadMemoryCardsFromGameChange(); + if (g_settings.HasAnyPerGameMemoryCards()) + UpdateMemoryCards(); } } @@ -4710,7 +4580,7 @@ void System::CheckForSettingsChanges(const Settings& old_settings) g_settings.memory_card_paths != old_settings.memory_card_paths || (g_settings.memory_card_use_playlist_title != old_settings.memory_card_use_playlist_title)) { - UpdateMemoryCardTypes(); + UpdateMemoryCards(); } if (g_settings.rewind_enable != old_settings.rewind_enable || @@ -5814,6 +5684,135 @@ std::string System::GetGameMemoryCardPath(std::string_view serial, std::string_v return ret; } +std::string System::GetMemoryCardPathForSlot(u32 slot, MemoryCardType type) +{ + std::string ret; + + // Disable memory cards when running PSFs/GPU dumps. + if ((!s_state.running_game_path.empty() && IsPsfPath(s_state.running_game_path.c_str())) || IsReplayingGPUDump()) + return ret; + + std::string message_key = fmt::format("MemoryCard{}SharedWarning", slot); + + switch (type) + { + case MemoryCardType::PerGame: + { + if (s_state.running_game_serial.empty()) + { + Host::AddIconOSDMessage( + std::move(message_key), ICON_PF_MEMORY_CARD, + fmt::format(TRANSLATE_FS("System", "Per-game memory card cannot be used for slot {} as the running " + "game has no code. Using shared card instead."), + slot + 1u), + Host::OSD_INFO_DURATION); + ret = g_settings.GetSharedMemoryCardPath(slot); + } + else + { + Host::RemoveKeyedOSDMessage(std::move(message_key)); + ret = g_settings.GetGameMemoryCardPath(s_state.running_game_serial, slot); + } + } + break; + + case MemoryCardType::PerGameTitle: + { + if (s_state.running_game_title.empty()) + { + Host::AddIconOSDMessage( + std::move(message_key), ICON_PF_MEMORY_CARD, + fmt::format(TRANSLATE_FS("System", "Per-game memory card cannot be used for slot {} as the running " + "game has no title. Using shared card instead."), + slot + 1u), + Host::OSD_INFO_DURATION); + ret = g_settings.GetSharedMemoryCardPath(slot); + } + else + { + const std::string_view game_title = (s_state.running_game_custom_title || !s_state.running_game_entry) ? + std::string_view(s_state.running_game_title) : + s_state.running_game_entry->GetSaveTitle(); + std::string card_path; + + // Multi-disc game - use disc set name. + if (s_state.running_game_entry && s_state.running_game_entry->disc_set) + { + card_path = g_settings.GetGameMemoryCardPath( + Path::SanitizeFileName(s_state.running_game_entry->disc_set->GetSaveTitle()), slot); + } + + // But prefer a disc-specific card if one already exists. + std::string disc_card_path = g_settings.GetGameMemoryCardPath(Path::SanitizeFileName(game_title), slot); + if (disc_card_path != card_path) + { + if (card_path.empty() || !g_settings.memory_card_use_playlist_title || + FileSystem::FileExists(disc_card_path.c_str())) + { + if (g_settings.memory_card_use_playlist_title && !card_path.empty()) + { + Host::AddIconOSDMessage( + fmt::format("DiscSpecificMC{}", slot), ICON_PF_MEMORY_CARD, + fmt::format(TRANSLATE_FS("System", "Using disc-specific memory card '{}' instead of per-game card."), + Path::GetFileName(disc_card_path)), + Host::OSD_INFO_DURATION); + } + + card_path = std::move(disc_card_path); + } + } + + Host::RemoveKeyedOSDMessage(std::move(message_key)); + ret = card_path; + } + } + break; + + case MemoryCardType::PerGameFileTitle: + { + const std::string display_name(FileSystem::GetDisplayNameFromPath(s_state.running_game_path)); + const std::string_view file_title(Path::GetFileTitle(display_name)); + if (file_title.empty()) + { + Host::AddIconOSDMessage( + std::move(message_key), ICON_PF_MEMORY_CARD, + fmt::format(TRANSLATE_FS("System", "Per-game memory card cannot be used for slot {} as the running " + "game has no path. Using shared card instead."), + slot + 1u)); + ret = g_settings.GetSharedMemoryCardPath(slot); + } + else + { + Host::RemoveKeyedOSDMessage(std::move(message_key)); + ret = g_settings.GetGameMemoryCardPath(Path::SanitizeFileName(file_title).c_str(), slot); + } + } + break; + + case MemoryCardType::Shared: + { + Host::RemoveKeyedOSDMessage(std::move(message_key)); + ret = g_settings.GetSharedMemoryCardPath(slot); + } + break; + + case MemoryCardType::NonPersistent: + { + Host::RemoveKeyedOSDMessage(std::move(message_key)); + } + break; + + case MemoryCardType::None: + default: + { + Host::RemoveKeyedOSDMessage(std::move(message_key)); + } + break; + } + + return ret; +} + std::string System::GetMostRecentResumeSaveStatePath() { FlushSaveStates(); diff --git a/src/core/system.h b/src/core/system.h index 7257b4a6a..57005167c 100644 --- a/src/core/system.h +++ b/src/core/system.h @@ -296,7 +296,7 @@ void SetVideoFrameRate(float frequency); // Access controllers for simulating input. Controller* GetController(u32 slot); -void UpdateMemoryCardTypes(); +void UpdateMemoryCards(); bool HasMemoryCard(u32 slot); bool IsSavingMemoryCards();