diff --git a/src/core/fullscreen_ui.cpp b/src/core/fullscreen_ui.cpp index a22ae789c..e505c1af0 100644 --- a/src/core/fullscreen_ui.cpp +++ b/src/core/fullscreen_ui.cpp @@ -3583,8 +3583,8 @@ void FullscreenUI::DrawMemoryCardSettingsPage() SetSettingsChanged(bsi); } - DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_SEARCH, "Use Single Card For Sub-Images"), - FSUI_CSTR("When using a multi-disc image (m3u/pbp) and per-game (title) memory cards, " + DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_SEARCH, "Use Single Card For Multi-Disc Games"), + FSUI_CSTR("When playing a multi-disc game and using per-game (title) memory cards, " "use a single memory card for all discs."), "MemoryCards", "UsePlaylistTitle", true); diff --git a/src/core/pad.cpp b/src/core/pad.cpp index 9502d4ed0..ad324b0e9 100644 --- a/src/core/pad.cpp +++ b/src/core/pad.cpp @@ -192,17 +192,14 @@ bool Pad::DoStateController(StateWrapper& sw, u32 i) if (g_settings.load_devices_from_save_states) { Host::AddFormattedOSDMessage( - 10.0f, - TRANSLATE("OSDMessage", - "Save state contains controller type %s in port %u, but %s is used. Switching."), + 10.0f, TRANSLATE("OSDMessage", "Save state contains controller type %s in port %u, but %s is used. Switching."), Settings::GetControllerTypeName(state_controller_type), i + 1u, Settings::GetControllerTypeName(controller_type)); } else { - Host::AddFormattedOSDMessage( - 10.0f, TRANSLATE("OSDMessage", "Ignoring mismatched controller type %s in port %u."), - Settings::GetControllerTypeName(state_controller_type), i + 1u); + Host::AddFormattedOSDMessage(10.0f, TRANSLATE("OSDMessage", "Ignoring mismatched controller type %s in port %u."), + Settings::GetControllerTypeName(state_controller_type), i + 1u); } // dev-friendly untranslated console log. @@ -256,8 +253,7 @@ bool Pad::DoStateMemcard(StateWrapper& sw, u32 i, bool is_memory_state) { Host::AddFormattedOSDMessage( 20.0f, - TRANSLATE("OSDMessage", - "Memory card %u present in save state but not in system. Creating temporary card."), + TRANSLATE("OSDMessage", "Memory card %u present in save state but not in system. Creating temporary card."), i + 1u); s_memory_cards[i] = MemoryCard::Create(); } @@ -296,7 +292,7 @@ bool Pad::DoStateMemcard(StateWrapper& sw, u32 i, bool is_memory_state) Host::AddFormattedOSDMessage( 20.0f, TRANSLATE("OSDMessage", - "Memory card %u from save state does match current card data. Simulating replugging."), + "Memory card %u from save state does match current card data. Simulating replugging."), i + 1u); // this is a potentially serious issue - some games cache info from memcards and jumping around @@ -312,8 +308,7 @@ bool Pad::DoStateMemcard(StateWrapper& sw, u32 i, bool is_memory_state) else { Host::AddFormattedOSDMessage( - 20.0f, - TRANSLATE("OSDMessage", "Memory card %u present in save state but not in system. Ignoring card."), + 20.0f, TRANSLATE("OSDMessage", "Memory card %u present in save state but not in system. Ignoring card."), i + 1u); } @@ -325,16 +320,14 @@ bool Pad::DoStateMemcard(StateWrapper& sw, u32 i, bool is_memory_state) if (g_settings.load_devices_from_save_states) { Host::AddFormattedOSDMessage( - 20.0f, - TRANSLATE("OSDMessage", "Memory card %u present in system but not in save state. Removing card."), + 20.0f, TRANSLATE("OSDMessage", "Memory card %u present in system but not in save state. Removing card."), i + 1u); s_memory_cards[i].reset(); } else { Host::AddFormattedOSDMessage( - 20.0f, - TRANSLATE("OSDMessage", "Memory card %u present in system but not in save state. Replugging card."), + 20.0f, TRANSLATE("OSDMessage", "Memory card %u present in system but not in save state. Replugging card."), i + 1u); s_memory_cards[i]->Reset(); } @@ -545,6 +538,10 @@ MemoryCard* Pad::GetMemoryCard(u32 slot) void Pad::SetMemoryCard(u32 slot, std::unique_ptr dev) { + Log_InfoPrintf("Memory card slot %u: %s", slot, + dev ? (dev->GetFilename().empty() ? "" : dev->GetFilename().c_str()) : + ""); + s_memory_cards[slot] = std::move(dev); } @@ -798,7 +795,8 @@ void Pad::DoTransfer(TickCount ticks_late) const u32 frame_number = System::GetFrameNumber(); // consider u32 overflow case - if (ShouldAvoidSavingToState() && (frame_number - s_last_memory_card_transfer_frame) > GetMaximumRollbackFrames()) + if (ShouldAvoidSavingToState() && + (frame_number - s_last_memory_card_transfer_frame) > GetMaximumRollbackFrames()) BackupMemoryCardState(); s_last_memory_card_transfer_frame = frame_number; diff --git a/src/core/settings.cpp b/src/core/settings.cpp index aa3493198..abd1d4724 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -1271,7 +1271,7 @@ std::string Settings::GetSharedMemoryCardPath(u32 slot) const return ret; } -std::string Settings::GetGameMemoryCardPath(const char* serial, u32 slot) +std::string Settings::GetGameMemoryCardPath(const std::string_view& serial, u32 slot) { return Path::Combine(EmuFolders::MemoryCards, fmt::format("{}_{}.mcd", serial, slot + 1)); } diff --git a/src/core/settings.h b/src/core/settings.h index 96317f6b1..95239c9fb 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -312,7 +312,7 @@ struct Settings std::string GetSharedMemoryCardPath(u32 slot) const; /// Returns the default path to a memory card for a specific game. - static std::string GetGameMemoryCardPath(const char* serial, u32 slot); + static std::string GetGameMemoryCardPath(const std::string_view& serial, u32 slot); static void CPUOverclockPercentToFraction(u32 percent, u32* numerator, u32* denominator); static u32 CPUOverclockFractionToPercent(u32 numerator, u32 denominator); diff --git a/src/core/system.cpp b/src/core/system.cpp index 4251b19ed..f8e6a9137 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -2304,8 +2304,8 @@ bool System::InternalSaveState(ByteStream* state, u32 screenshot_size /* = 256 * u32 screenshot_stride; GPUTexture::Format screenshot_format; if (g_gpu_device->RenderScreenshot(screenshot_width, screenshot_height, - Common::Rectangle::FromExtents(0, 0, screenshot_width, screenshot_height), - &screenshot_buffer, &screenshot_stride, &screenshot_format) && + Common::Rectangle::FromExtents(0, 0, screenshot_width, screenshot_height), + &screenshot_buffer, &screenshot_stride, &screenshot_format) && GPUTexture::ConvertTextureDataToRGBA8(screenshot_width, screenshot_height, screenshot_buffer, screenshot_stride, screenshot_format)) { @@ -2893,8 +2893,43 @@ std::unique_ptr System::GetMemoryCardForSlot(u32 slot, MemoryCardTyp } else { - return MemoryCard::Open(g_settings.GetGameMemoryCardPath( - MemoryCard::SanitizeGameTitleForFileName(s_running_game_title).c_str(), slot)); + std::string card_path; + + // Playlist - use title if different. + if (HasMediaSubImages() && s_running_game_entry && s_running_game_title != s_running_game_entry->title) + { + card_path = g_settings.GetGameMemoryCardPath( + MemoryCard::SanitizeGameTitleForFileName(s_running_game_entry->title), slot); + } + // Multi-disc game - use disc set name. + else if (s_running_game_entry && !s_running_game_entry->disc_set_name.empty()) + { + card_path = g_settings.GetGameMemoryCardPath( + MemoryCard::SanitizeGameTitleForFileName(s_running_game_entry->disc_set_name), slot); + } + + // But prefer a disc-specific card if one already exists. + std::string disc_card_path = + g_settings.GetGameMemoryCardPath(MemoryCard::SanitizeGameTitleForFileName(s_running_game_entry->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_FA_SD_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); + } + } + + return MemoryCard::Open(card_path.c_str()); } } @@ -3145,9 +3180,9 @@ void System::UpdateRunningGame(const char* path, CDImage* image, bool booting) s_running_game_title = Path::GetFileTitle(FileSystem::GetDisplayNameFromPath(path)); } - if (image->HasSubImages() && g_settings.memory_card_use_playlist_title) + if (image->HasSubImages()) { - std::string image_title(image->GetMetadata("title")); + std::string image_title = image->GetMetadata("title"); if (!image_title.empty()) s_running_game_title = std::move(image_title); } @@ -3456,8 +3491,7 @@ void System::CheckForSettingsChanges(const Settings& old_settings) if (g_settings.memory_card_types != old_settings.memory_card_types || g_settings.memory_card_paths != old_settings.memory_card_paths || - (g_settings.memory_card_use_playlist_title != old_settings.memory_card_use_playlist_title && - HasMediaSubImages())) + (g_settings.memory_card_use_playlist_title != old_settings.memory_card_use_playlist_title)) { UpdateMemoryCardTypes(); } @@ -4448,7 +4482,7 @@ void System::UpdateSoftwareCursor() if (image && image->IsValid()) { g_gpu_device->SetSoftwareCursor(image->GetPixels(), image->GetWidth(), image->GetHeight(), image->GetPitch(), - image_scale); + image_scale); } else { diff --git a/src/duckstation-qt/memorycardsettingswidget.cpp b/src/duckstation-qt/memorycardsettingswidget.cpp index e6d3fb7e3..b90381ff4 100644 --- a/src/duckstation-qt/memorycardsettingswidget.cpp +++ b/src/duckstation-qt/memorycardsettingswidget.cpp @@ -58,13 +58,13 @@ void MemoryCardSettingsWidget::createUi(SettingsDialog* dialog) box_layout->addLayout(hbox); } - QCheckBox* playlist_title_as_game_title = new QCheckBox(tr("Use Single Card For Sub-Images"), box); + QCheckBox* playlist_title_as_game_title = new QCheckBox(tr("Use Single Card For Multi-Disc Games"), box); SettingWidgetBinder::BindWidgetToBoolSetting(m_dialog->getSettingsInterface(), playlist_title_as_game_title, "MemoryCards", "UsePlaylistTitle", true); box_layout->addWidget(playlist_title_as_game_title); dialog->registerWidgetHelp( - playlist_title_as_game_title, tr("Use Single Card For Sub-Images"), tr("Checked"), - tr("When using a multi-disc format (m3u/pbp) and per-game (title) memory cards, a single memory card " + playlist_title_as_game_title, tr("Use Single Card For Multi-Disc Games"), tr("Checked"), + tr("When playing a multi-disc game and using per-game (title) memory cards, a single memory card " "will be used for all discs. If unchecked, a separate card will be used for each disc.")); box_layout->addWidget(QtUtils::CreateHorizontalLine(box));