diff --git a/src/core/system.cpp b/src/core/system.cpp index 5313fea87..bbdfa9ea2 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -4634,6 +4634,103 @@ void System::DeleteSaveStates(const char* serial, bool resume) } } +std::string System::GetGameMemoryCardPath(std::string_view serial, std::string_view path, u32 slot) +{ + const char* section = "MemoryCards"; + const TinyString type_key = TinyString::from_format("Card{}Type", slot + 1); + const MemoryCardType default_type = + (slot == 0) ? Settings::DEFAULT_MEMORY_CARD_1_TYPE : Settings::DEFAULT_MEMORY_CARD_2_TYPE; + const MemoryCardType global_type = + Settings::ParseMemoryCardTypeName( + Host::GetBaseTinyStringSettingValue(section, type_key, Settings::GetMemoryCardTypeName(default_type))) + .value_or(default_type); + + MemoryCardType type = global_type; + std::unique_ptr ini; + if (!serial.empty()) + { + std::string game_settings_path = GetGameSettingsPath(serial); + if (FileSystem::FileExists(game_settings_path.c_str())) + { + ini = std::make_unique(std::move(game_settings_path)); + if (!ini->Load()) + { + ini.reset(); + } + else if (ini->ContainsValue(section, type_key)) + { + type = Settings::ParseMemoryCardTypeName( + ini->GetTinyStringValue(section, type_key, Settings::GetMemoryCardTypeName(global_type))) + .value_or(global_type); + } + } + } + else if (type == MemoryCardType::PerGame) + { + // always shared without serial + type = MemoryCardType::Shared; + } + + std::string ret; + switch (type) + { + case MemoryCardType::None: + break; + + case MemoryCardType::Shared: + { + const TinyString path_key = TinyString::from_format("Card{}Path", slot + 1); + std::string global_path = + Host::GetBaseStringSettingValue(section, path_key, Settings::GetDefaultSharedMemoryCardName(slot + 1).c_str()); + if (ini && ini->ContainsValue(section, path_key)) + ret = ini->GetStringValue(section, path_key, global_path.c_str()); + else + ret = std::move(global_path); + + if (!Path::IsAbsolute(ret)) + ret = Path::Combine(EmuFolders::MemoryCards, ret); + } + break; + + case MemoryCardType::PerGame: + ret = g_settings.GetGameMemoryCardPath(serial, slot); + break; + + case MemoryCardType::PerGameTitle: + { + const GameDatabase::Entry* entry = GameDatabase::GetEntryForSerial(serial); + if (entry) + { + ret = g_settings.GetGameMemoryCardPath(Path::SanitizeFileName(entry->title), slot); + + // Use disc set name if there isn't a per-disc card present. + const bool global_use_playlist_title = Host::GetBaseBoolSettingValue(section, "UsePlaylistTitle", true); + const bool use_playlist_title = + ini ? ini->GetBoolValue(section, "UsePlaylistTitle", global_use_playlist_title) : global_use_playlist_title; + if (entry->disc_set_name.empty() && use_playlist_title && !FileSystem::FileExists(ret.c_str())) + ret = g_settings.GetGameMemoryCardPath(Path::SanitizeFileName(entry->disc_set_name), slot); + } + else + { + ret = g_settings.GetGameMemoryCardPath( + Path::SanitizeFileName(Path::GetFileTitle(FileSystem::GetDisplayNameFromPath(path))), slot); + } + } + break; + + case MemoryCardType::PerGameFileTitle: + { + ret = g_settings.GetGameMemoryCardPath( + Path::SanitizeFileName(Path::GetFileTitle(FileSystem::GetDisplayNameFromPath(path))), slot); + } + break; + default: + break; + } + + return ret; +} + std::string System::GetMostRecentResumeSaveStatePath() { std::vector files; diff --git a/src/core/system.h b/src/core/system.h index b0dbd324f..b6f4fcbf8 100644 --- a/src/core/system.h +++ b/src/core/system.h @@ -407,6 +407,9 @@ std::optional GetExtendedSaveStateInfo(const char* path); /// Deletes save states for the specified game code. If resume is set, the resume state is deleted too. void DeleteSaveStates(const char* serial, bool resume); +/// Returns the path to the memory card for the specified game, considering game settings. +std::string GetGameMemoryCardPath(std::string_view serial, std::string_view path, u32 slot); + /// Returns intended output volume considering fast forwarding. s32 GetAudioOutputVolume(); void UpdateVolume(); diff --git a/src/duckstation-qt/mainwindow.cpp b/src/duckstation-qt/mainwindow.cpp index 9fdb2b9f3..24d6c3b30 100644 --- a/src/duckstation-qt/mainwindow.cpp +++ b/src/duckstation-qt/mainwindow.cpp @@ -809,52 +809,7 @@ void MainWindow::populateGameListContextMenu(const GameList::Entry* entry, QWidg connect(open_memory_cards_action, &QAction::triggered, [entry]() { QString paths[2]; for (u32 i = 0; i < 2; i++) - { - MemoryCardType type = g_settings.memory_card_types[i]; - if (entry->serial.empty() && type == MemoryCardType::PerGame) - type = MemoryCardType::Shared; - - switch (type) - { - case MemoryCardType::None: - continue; - case MemoryCardType::Shared: - if (g_settings.memory_card_paths[i].empty()) - { - paths[i] = QString::fromStdString(g_settings.GetSharedMemoryCardPath(i)); - } - else - { - QFileInfo path(QString::fromStdString(g_settings.memory_card_paths[i])); - path.makeAbsolute(); - paths[i] = QDir::toNativeSeparators(path.canonicalFilePath()); - } - break; - case MemoryCardType::PerGame: - paths[i] = QString::fromStdString(g_settings.GetGameMemoryCardPath(entry->serial, i)); - break; - case MemoryCardType::PerGameTitle: - { - paths[i] = QString::fromStdString(g_settings.GetGameMemoryCardPath(Path::SanitizeFileName(entry->title), i)); - if (!entry->disc_set_name.empty() && g_settings.memory_card_use_playlist_title && !QFile::exists(paths[i])) - { - paths[i] = - QString::fromStdString(g_settings.GetGameMemoryCardPath(Path::SanitizeFileName(entry->disc_set_name), i)); - } - } - break; - - case MemoryCardType::PerGameFileTitle: - { - const std::string display_name(FileSystem::GetDisplayNameFromPath(entry->path)); - paths[i] = QString::fromStdString( - g_settings.GetGameMemoryCardPath(Path::SanitizeFileName(Path::GetFileTitle(display_name)), i)); - } - break; - default: - break; - } - } + paths[i] = QString::fromStdString(System::GetGameMemoryCardPath(entry->serial, entry->path, i)); g_main_window->openMemoryCardEditor(paths[0], paths[1]); });