From 4ac417b74f31ae34a158ce0da7bda8b6d437194b Mon Sep 17 00:00:00 2001 From: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com> Date: Mon, 1 Jun 2026 15:13:56 +0200 Subject: [PATCH] Implement trophy migration (#4501) * Implement trophy migration * Respect user choice from save migration * yet another nested subfolder iterator * refactoring and remove original hardcoded forced migration code --- src/core/user_manager.cpp | 228 ++++++++++++++++++++++++++------------ 1 file changed, 155 insertions(+), 73 deletions(-) diff --git a/src/core/user_manager.cpp b/src/core/user_manager.cpp index 88d61d6e3..adc576110 100644 --- a/src/core/user_manager.cpp +++ b/src/core/user_manager.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include "emulator_settings.h" #include "libraries/system/userservice.h" #include "user_manager.h" @@ -89,6 +90,156 @@ const std::vector& UserManager::GetAllUsers() const { return m_users.user; } +enum class TransferOption : s32 { + Copy = 0, + Move, + MoveAndLinkBack, + Nothing, + SdlCancelled = -1, +}; +TransferOption AskMigrationOption() { + TransferOption user_choice = TransferOption::Nothing; +#ifndef _WIN32 + SDL_MessageBoxButtonData btns[4] +#else + SDL_MessageBoxButtonData btns[3] +#endif + { + {0, 0, "Copy"}, + {0, 1, "Move"}, +#ifndef _WIN32 + {0, 2, "Move and link back"}, +#endif + {0, 3, "Do nothing"}, + }; + SDL_MessageBoxData msg_box{ + 0, + nullptr, + "Save Migration", + "The shadPS4 save and trophy locations have been updated, and save/trophy " + "files have been detected in the old location.\n" + "Do you wish to copy them over, move them over, " +#ifndef _WIN32 + "move and link back to the original the original location, " +#endif + "or continue without doing anything?", + +#ifndef _WIN32 + 4, +#else + 3, +#endif + btns, + nullptr, + }; + SDL_ShowMessageBox(&msg_box, reinterpret_cast(&user_choice)); + return user_choice; +} + +static void MoveFolder(fs::path const& _from, fs::path const& _to) { + try { + fs::rename(_from, _to); + } catch (...) { + fs::copy(_from, _to, fs::copy_options::recursive); + fs::remove_all(_from); + } +} + +static void CheckAndMigrateSaves(TransferOption option) { + auto const new_save_dir = EmulatorSettings.GetHomeDir() / "1000" / "savedata"; + auto const old_save_dir = + Common::FS::GetUserPath(Common::FS::PathType::UserDir) / "savedata" / "1"; + if (fs::exists(old_save_dir) && !fs::is_empty(old_save_dir)) { + try { + switch (option) { + case TransferOption::Copy: + fs::copy(old_save_dir, new_save_dir, fs::copy_options::recursive); + break; + case TransferOption::Move: + MoveFolder(old_save_dir, new_save_dir); + break; + case TransferOption::MoveAndLinkBack: + MoveFolder(old_save_dir, new_save_dir); + fs::create_directory_symlink(new_save_dir, old_save_dir); + break; + case TransferOption::SdlCancelled: + case TransferOption::Nothing: + break; + default: + UNREACHABLE(); + } + } catch (std::exception const& e) { + UNREACHABLE_MSG("Error while migrating saves: {}", e.what()); + } + } +} + +static void CheckAndMigrateTrophies(TransferOption option) { + auto const user_dir = EmulatorSettings.GetHomeDir() / "1000"; + auto const old_trophy_base_dir = + Common::FS::GetUserPath(Common::FS::PathType::UserDir) / "game_data"; + auto const new_trophy_global_dir = + Common::FS::GetUserPath(Common::FS::PathType::UserDir) / "trophy"; + try { + for (auto const& entry : fs::directory_iterator(old_trophy_base_dir)) { + if (!entry.is_directory()) { + continue; + } + if (!fs::exists(entry.path() / "TrophyFiles")) { + continue; + } + for (auto const& subentry : fs::directory_iterator(entry.path() / "TrophyFiles")) { + if (!subentry.is_directory()) { + continue; + } + auto const old_trophy_dir = subentry.path(); + if (fs::exists(old_trophy_dir / "Xml")) { + pugi::xml_document doc; + pugi::xml_parse_result result = + doc.load_file((old_trophy_dir / "Xml" / "TROP.XML").native().c_str()); + if (!result) { + continue; + } + std::string npcommid = + doc.child("trophyconf").child("npcommid").text().as_string(); + if (npcommid.empty()) { + continue; + } + if (fs::exists(user_dir / "trophy" / (npcommid + ".xml"))) { + continue; + } + if (!fs::exists(new_trophy_global_dir / npcommid)) { + fs::create_directories(new_trophy_global_dir / npcommid); + fs::copy(old_trophy_dir, new_trophy_global_dir / npcommid, + fs::copy_options::recursive); + } + auto const old_trophy_file = old_trophy_dir / "Xml" / "TROP.XML"; + auto const new_trophy_file = user_dir / "trophy" / (npcommid + ".xml"); + switch (option) { + case TransferOption::Copy: + fs::copy_file(old_trophy_file, new_trophy_file); + break; + case TransferOption::Move: + MoveFolder(old_trophy_file, new_trophy_file); + break; + case TransferOption::MoveAndLinkBack: + MoveFolder(old_trophy_file, new_trophy_file); + fs::create_symlink(new_trophy_file, old_trophy_file); + break; + case TransferOption::Nothing: + case TransferOption::SdlCancelled: + break; + default: + UNREACHABLE(); + } + } + } + } + } catch (std::exception const& e) { + UNREACHABLE_MSG("Error while migrating trophies: {}", e.what()); + } +} + Users UserManager::CreateDefaultUsers() { Users default_users; default_users.user = { @@ -146,79 +297,10 @@ Users UserManager::CreateDefaultUsers() { std::filesystem::create_directory(user_dir / "savedata"); std::filesystem::create_directory(user_dir / "trophy"); std::filesystem::create_directory(user_dir / "inputs"); - auto const old_save_dir = - Common::FS::GetUserPath(Common::FS::PathType::UserDir) / "savedata" / "1"; - if (u.user_id == 1000 && std::filesystem::exists(old_save_dir) && - !std::filesystem::is_empty(old_save_dir)) { - auto const new_save_dir = user_dir / "savedata"; -#ifndef _WIN32 - SDL_MessageBoxButtonData btns[4] -#else - SDL_MessageBoxButtonData btns[3] -#endif - { - {0, 0, "Copy"}, - {0, 1, "Move"}, -#ifndef _WIN32 - {0, 2, "Move and link back"}, -#endif - {0, 3, "Do nothing"}, - }; - SDL_MessageBoxData msg_box{ - 0, - nullptr, - "Save Migration", - "The shadPS4 save location has been updated, and save files have been detected " - "in the old location.\nDo you wish to copy them over, move them over, " -#ifndef _WIN32 - "move and link back to the original the original location, " -#endif - "or continue without doing anything?", - -#ifndef _WIN32 - 4, -#else - 3, -#endif - btns, - nullptr, - }; - int result = 3; - SDL_ShowMessageBox(&msg_box, &result); - try { - switch (result) { - case 0: - std::filesystem::copy(old_save_dir, new_save_dir, - std::filesystem::copy_options::recursive); - break; - case 1: - try { - std::filesystem::rename(old_save_dir, new_save_dir); - } catch (...) { - std::filesystem::copy(old_save_dir, new_save_dir, - std::filesystem::copy_options::recursive); - std::filesystem::remove_all(old_save_dir); - } - break; - case 2: - try { - std::filesystem::rename(old_save_dir, new_save_dir); - } catch (...) { - std::filesystem::copy(old_save_dir, new_save_dir, - std::filesystem::copy_options::recursive); - std::filesystem::remove_all(old_save_dir); - } - std::filesystem::create_directory_symlink(new_save_dir, old_save_dir); - break; - case -1: - case 3: - break; - default: - UNREACHABLE(); - } - } catch (std::exception const& e) { - UNREACHABLE_MSG("Error while migrating saves: {}", e.what()); - } + if (u.user_id == 1000) { + TransferOption user_choice = AskMigrationOption(); + CheckAndMigrateSaves(user_choice); + CheckAndMigrateTrophies(user_choice); } } }