From 8e3a65c152a353618a2d0ccec93cf5fff561a86b Mon Sep 17 00:00:00 2001 From: Stenzek Date: Mon, 1 Dec 2025 02:05:46 +1000 Subject: [PATCH] MemoryCard: Show game icon when saving --- src/core/game_list.cpp | 21 +++++++++++++-------- src/core/game_list.h | 2 ++ src/core/memory_card.cpp | 6 +++++- src/core/system.cpp | 14 +++++++++++--- src/core/system.h | 7 +++++-- src/duckstation-qt/mainwindow.cpp | 5 +---- src/util/imgui_manager.cpp | 6 ++++++ src/util/imgui_manager.h | 1 + 8 files changed, 44 insertions(+), 18 deletions(-) diff --git a/src/core/game_list.cpp b/src/core/game_list.cpp index 20d476f46..5977fa534 100644 --- a/src/core/game_list.cpp +++ b/src/core/game_list.cpp @@ -2027,28 +2027,33 @@ void GameList::ReloadMemcardTimestampCache() } std::string GameList::GetGameIconPath(const GameList::Entry* entry) +{ + return GetGameIconPath(entry->title, entry->serial, entry->path, entry->achievements_game_id); +} + +std::string GameList::GetGameIconPath(std::string_view custom_title, std::string_view serial, std::string_view path, + u32 achievements_game_id) { std::string ret; std::string fallback_path; - if (entry->achievements_game_id != 0) + if (achievements_game_id != 0) { - fallback_path = GetAchievementGameBadgePath(entry->achievements_game_id); + fallback_path = GetAchievementGameBadgePath(achievements_game_id); if (!fallback_path.empty() && PreferAchievementGameBadgesForIcons()) return (ret = std::move(fallback_path)); } - if (entry->serial.empty()) + if (serial.empty()) return (ret = std::move(fallback_path)); // might exist already, or the user used a custom icon - ret = Path::Combine(EmuFolders::GameIcons, TinyString::from_format("{}.png", entry->serial)); + ret = Path::Combine(EmuFolders::GameIcons, TinyString::from_format("{}.png", serial)); if (FileSystem::FileExists(ret.c_str())) return ret; MemoryCardType type; - std::string memcard_path = - System::GetGameMemoryCardPath(entry->title, entry->has_custom_title, entry->serial, entry->path, 0, &type); + std::string memcard_path = System::GetGameMemoryCardPath(custom_title, serial, path, 0, &type); FILESYSTEM_STAT_DATA memcard_sd; if (memcard_path.empty() || type == MemoryCardType::Shared || !FileSystem::StatFile(memcard_path.c_str(), &memcard_sd)) @@ -2058,8 +2063,8 @@ std::string GameList::GetGameIconPath(const GameList::Entry* entry) const s64 timestamp = memcard_sd.ModificationTime; TinyString index_serial; - index_serial.assign(entry->serial.substr( - 0, std::min(entry->serial.length(), MemcardTimestampCacheEntry::MAX_SERIAL_LENGTH - 1))); + index_serial.assign( + serial.substr(0, std::min(serial.length(), MemcardTimestampCacheEntry::MAX_SERIAL_LENGTH - 1))); MemcardTimestampCacheEntry* cache_entry = nullptr; for (MemcardTimestampCacheEntry& it : s_state.memcard_timestamp_cache_entries) diff --git a/src/core/game_list.h b/src/core/game_list.h index 8ff0c0b97..7dc6fb0da 100644 --- a/src/core/game_list.h +++ b/src/core/game_list.h @@ -171,6 +171,8 @@ std::optional GetCustomRegionForPath(const std::string_view path); /// The purpose of this cache is to stop us trying to constantly extract memory card icons, when we know a game /// doesn't have any saves yet. It caches the serial:memcard_timestamp pair, and only tries extraction when the /// timestamp of the memory card has changed. +std::string GetGameIconPath(std::string_view title, std::string_view serial, std::string_view path, + u32 achievements_game_id); std::string GetGameIconPath(const GameList::Entry* entry); void ReloadMemcardTimestampCache(); diff --git a/src/core/memory_card.cpp b/src/core/memory_card.cpp index 901c5a6d3..632587896 100644 --- a/src/core/memory_card.cpp +++ b/src/core/memory_card.cpp @@ -380,8 +380,12 @@ bool MemoryCard::SaveIfChanged(bool display_osd_message) if (display_osd_message) { + std::string icon_path = System::GetGameIconPath(); + if (icon_path.empty()) + icon_path = ICON_PF_MEMORY_CARD; + Host::AddIconOSDMessage( - OSDMessageType::Quick, GetOSDMessageKey(m_index), ICON_PF_MEMORY_CARD, + OSDMessageType::Quick, GetOSDMessageKey(m_index), std::move(icon_path), fmt::format(TRANSLATE_FS("MemoryCard", "Memory Card Slot {}"), m_index + 1), fmt::format(TRANSLATE_FS("MemoryCard", "Saved card to '{}'."), Path::GetFileName(display_name))); } diff --git a/src/core/system.cpp b/src/core/system.cpp index 2f88ecfb7..d3cdbb1d0 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -806,6 +806,14 @@ System::BootMode System::GetBootMode() return s_state.boot_mode; } +std::string System::GetGameIconPath() +{ + return GameList::GetGameIconPath((s_state.running_game_custom_title || !s_state.running_game_entry) ? + std::string_view(s_state.running_game_title) : + std::string_view(), + s_state.running_game_serial, s_state.running_game_path, Achievements::GetGameID()); +} + bool System::IsUsingKnownPS1BIOS() { return (s_state.bios_image_info && s_state.bios_image_info->fastboot_patch == BIOS::ImageInfo::FastBootPatch::Type1); @@ -5761,8 +5769,8 @@ void System::DeleteSaveStates(std::string_view serial, bool resume) } } -std::string System::GetGameMemoryCardPath(std::string_view title, bool is_custom_title, std::string_view serial, - std::string_view path, u32 slot, MemoryCardType* out_type /* = nullptr */) +std::string System::GetGameMemoryCardPath(std::string_view custom_title, std::string_view serial, std::string_view path, + u32 slot, MemoryCardType* out_type /* = nullptr */) { const char* section = "MemoryCards"; const TinyString type_key = TinyString::from_format("Card{}Type", slot + 1); @@ -5822,7 +5830,7 @@ std::string System::GetGameMemoryCardPath(std::string_view title, bool is_custom case MemoryCardType::PerGameTitle: { const GameDatabase::Entry* entry = GameDatabase::GetEntryForSerial(serial); - const std::string_view game_title = (is_custom_title || !entry) ? title : entry->GetSaveTitle(); + const std::string_view game_title = (!custom_title.empty() || !entry) ? custom_title : entry->GetSaveTitle(); // Multi-disc game - use disc set name. if (entry && entry->disc_set) diff --git a/src/core/system.h b/src/core/system.h index 7a1cab7a3..49edea4a2 100644 --- a/src/core/system.h +++ b/src/core/system.h @@ -237,6 +237,9 @@ bool IsRunningUnknownGame(); bool IsUsingKnownPS1BIOS(); BootMode GetBootMode(); +/// Returns a path to the game icon, if any. +std::string GetGameIconPath(); + /// Returns the time elapsed in the current play session. u64 GetSessionPlayedTime(); @@ -404,8 +407,8 @@ std::optional GetExtendedSaveStateInfo(const char* path); void DeleteSaveStates(std::string_view serial, bool resume); /// Returns the path to the memory card for the specified game, considering game settings. -std::string GetGameMemoryCardPath(std::string_view title, bool is_custom_title, std::string_view serial, - std::string_view path, u32 slot, MemoryCardType* out_type = nullptr); +std::string GetGameMemoryCardPath(std::string_view save_title, std::string_view serial, std::string_view path, u32 slot, + MemoryCardType* out_type = nullptr); /// Returns intended output volume considering fast forwarding. u8 GetAudioOutputVolume(); diff --git a/src/duckstation-qt/mainwindow.cpp b/src/duckstation-qt/mainwindow.cpp index 641f5ce41..eb0cee8ea 100644 --- a/src/duckstation-qt/mainwindow.cpp +++ b/src/duckstation-qt/mainwindow.cpp @@ -910,10 +910,7 @@ void MainWindow::populateGameListContextMenu(const GameList::Entry* entry, QWidg QString paths[2]; for (u32 i = 0; i < 2; i++) - { - paths[i] = QString::fromStdString( - System::GetGameMemoryCardPath(entry->title, entry->has_custom_title, entry->serial, entry->path, i)); - } + paths[i] = QString::fromStdString(System::GetGameMemoryCardPath(entry->title, entry->serial, entry->path, i)); g_main_window->openMemoryCardEditor(paths[0], paths[1]); }); diff --git a/src/util/imgui_manager.cpp b/src/util/imgui_manager.cpp index eb86c2b00..210992da3 100644 --- a/src/util/imgui_manager.cpp +++ b/src/util/imgui_manager.cpp @@ -1172,6 +1172,12 @@ void Host::AddIconOSDMessage(OSDMessageType type, std::string key, const char* i ImGuiManager::AddOSDMessage(type, std::move(key), std::string(icon), std::move(title), std::move(message)); } +void Host::AddIconOSDMessage(OSDMessageType type, std::string key, std::string icon, std::string title, + std::string message) +{ + ImGuiManager::AddOSDMessage(type, std::move(key), std::move(icon), std::move(title), std::move(message)); +} + void Host::RemoveKeyedOSDMessage(std::string key) { ImGuiManager::RemoveKeyedOSDMessage(std::move(key)); diff --git a/src/util/imgui_manager.h b/src/util/imgui_manager.h index 98642c5cc..1d0025bfb 100644 --- a/src/util/imgui_manager.h +++ b/src/util/imgui_manager.h @@ -238,6 +238,7 @@ void AddOSDMessage(OSDMessageType type, std::string message); void AddKeyedOSDMessage(OSDMessageType type, std::string key, std::string message); void AddIconOSDMessage(OSDMessageType type, std::string key, const char* icon, std::string message); void AddIconOSDMessage(OSDMessageType type, std::string key, const char* icon, std::string title, std::string message); +void AddIconOSDMessage(OSDMessageType type, std::string key, std::string icon, std::string title, std::string message); void RemoveKeyedOSDMessage(std::string key); void ClearOSDMessages();