From 0e23d468caeaa9b0da778fa81a6e7602cf8e2826 Mon Sep 17 00:00:00 2001 From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com> Date: Mon, 1 Jun 2026 09:00:15 -0400 Subject: [PATCH] Clean up trophy extraction logic (#4498) This code has been an absolute mess since the trophy rework. This eliminates several redundant loops through game trophy files, and separates the logic into it's own function. --- src/common/elf_info.h | 11 +-- src/core/file_format/trp.cpp | 51 +++--------- src/core/file_format/trp.h | 2 +- src/emulator.cpp | 146 +++++++++++++++++++---------------- 4 files changed, 94 insertions(+), 116 deletions(-) diff --git a/src/common/elf_info.h b/src/common/elf_info.h index a700152fd..7a00fa486 100644 --- a/src/common/elf_info.h +++ b/src/common/elf_info.h @@ -75,8 +75,7 @@ class ElfInfo { std::filesystem::path splash_path{}; std::filesystem::path game_folder{}; - std::vector npCommIds{}; - std::map trophyIndexMap{}; + std::map trophy_index_map{}; public: static constexpr u32 FW_100 = 0x1000000; @@ -145,12 +144,8 @@ public: return game_folder; } - [[nodiscard]] const std::vector GetNpCommIds() const { - return npCommIds; - } - - [[nodiscard]] const std::map& GetTrophyIndexMap() const { - return trophyIndexMap; + [[nodiscard]] const std::map& GetTrophyIndexMap() const { + return trophy_index_map; } }; diff --git a/src/core/file_format/trp.cpp b/src/core/file_format/trp.cpp index 4545d415c..8879e5599 100644 --- a/src/core/file_format/trp.cpp +++ b/src/core/file_format/trp.cpp @@ -42,34 +42,9 @@ static void hexToBytes(const char* hex, unsigned char* dst) { } } -bool TRP::Extract(const std::filesystem::path& trophyPath, int index, std::string npCommId, +bool TRP::Extract(const std::filesystem::path& trophyPath, std::string npCommId, const std::filesystem::path& outputPath) { - if (!std::filesystem::exists(trophyPath)) { - LOG_WARNING(Common_Filesystem, "Trophy directory doesn't exist: {}", trophyPath.string()); - return false; - } - - // Collect all .trp files in the directory - std::vector trpFiles; - for (const auto& entry : std::filesystem::directory_iterator(trophyPath)) { - if (entry.is_regular_file() && entry.path().extension() == ".trp") { - trpFiles.push_back(entry.path()); - } - } - - // Sort files to ensure consistent ordering - std::sort(trpFiles.begin(), trpFiles.end()); - - if (index >= trpFiles.size()) { - LOG_WARNING(Common_Filesystem, "Trophy index {} out of range (only {} .trp files found)", - index, trpFiles.size()); - return false; - } - - // Select the file at the given index - std::filesystem::path gameSysDir = trpFiles[index]; - LOG_INFO(Common_Filesystem, "Using trophy file: {}", gameSysDir.filename().string()); - + // Retrieve trophy key const auto& user_key_vec = KeyManager::GetInstance()->GetAllKeys().TrophyKeySet.ReleaseTrophyKey; @@ -81,28 +56,26 @@ bool TRP::Extract(const std::filesystem::path& trophyPath, int index, std::strin std::array user_key{}; std::copy(user_key_vec.begin(), user_key_vec.end(), user_key.begin()); + s32 trpFileIndex = 0; bool success = true; - int trpFileIndex = 0; - try { - const auto& it = gameSysDir; - if (it.extension() != ".trp") { + if (trophyPath.extension() != ".trp") { return false; } - Common::FS::IOFile file(it, Common::FS::FileAccessMode::Read); + Common::FS::IOFile file(trophyPath, Common::FS::FileAccessMode::Read); if (!file.IsOpen()) { - LOG_ERROR(Common_Filesystem, "Unable to open trophy file: {}", it.string()); + LOG_ERROR(Common_Filesystem, "Unable to open trophy file: {}", trophyPath.string()); return false; } TrpHeader header; if (!file.Read(header)) { - LOG_ERROR(Common_Filesystem, "Failed to read TRP header from {}", it.string()); + LOG_ERROR(Common_Filesystem, "Failed to read TRP header from {}", trophyPath.string()); return false; } if (header.magic != TRP_MAGIC) { - LOG_ERROR(Common_Filesystem, "Wrong trophy magic number in {}", it.string()); + LOG_ERROR(Common_Filesystem, "Wrong trophy magic number in {}", trophyPath.string()); return false; } @@ -115,7 +88,7 @@ bool TRP::Extract(const std::filesystem::path& trophyPath, int index, std::strin } // Process each entry in the TRP file - for (int i = 0; i < header.entry_num; i++) { + for (u32 i = 0; i < header.entry_num; i++) { if (!file.Seek(seekPos)) { LOG_ERROR(Common_Filesystem, "Failed to seek to TRP entry offset"); success = false; @@ -152,7 +125,7 @@ bool TRP::Extract(const std::filesystem::path& trophyPath, int index, std::strin } } else { LOG_DEBUG(Common_Filesystem, "Unknown entry flag: {} for {}", - static_cast(entry.flag), name); + static_cast(entry.flag), name); } trpFileIndex++; } @@ -233,7 +206,7 @@ bool TRP::ProcessEncryptedXmlEntry(Common::FS::IOFile& file, const TrpEntry& ent return false; } - // Decrypt the data - FIX: Don't check return value since DecryptEFSM returns void + // Decrypt the data std::span key_span(user_key); // Convert npCommId string to span (pad or truncate to 16 bytes) @@ -247,7 +220,7 @@ bool TRP::ProcessEncryptedXmlEntry(Common::FS::IOFile& file, const TrpEntry& ent // Remove padding removePadding(XML); - // Create output filename (replace ESFM with XML) + // Create output filename std::string xml_name(entry.entry_name); size_t pos = xml_name.find("ESFM"); if (pos != std::string::npos) { diff --git a/src/core/file_format/trp.h b/src/core/file_format/trp.h index df0ea6eaf..30c184829 100644 --- a/src/core/file_format/trp.h +++ b/src/core/file_format/trp.h @@ -36,7 +36,7 @@ class TRP { public: TRP(); ~TRP(); - bool Extract(const std::filesystem::path& trophyPath, int index, std::string npCommId, + bool Extract(const std::filesystem::path& trophyPath, std::string npCommId, const std::filesystem::path& outputPath); private: diff --git a/src/emulator.cpp b/src/emulator.cpp index 35f8ebe3b..8cb5bcd2a 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -91,6 +91,78 @@ s32 ReadCompiledSdkVersion(const std::filesystem::path& file) { return 0; } +std::map ExtractTrophies(const std::filesystem::path& npbind_path, + const std::filesystem::path& trophy_dir) { + std::map trophy_index_map{}; + + NPBindFile npbind; + if (!npbind.Load(npbind_path.string())) { + LOG_WARNING(Common_Filesystem, "Failed to load npbind.dat file"); + return trophy_index_map; + } + + auto np_comm_ids = npbind.GetNpCommIds(); + if (!std::filesystem::exists(trophy_dir) || np_comm_ids.empty()) { + LOG_WARNING(Common_Filesystem, "Cannot extract game trophies"); + return trophy_index_map; + } + + std::string pattern = "trophy"; + for (const auto& entry : std::filesystem::directory_iterator(trophy_dir)) { + if (entry.is_regular_file() && entry.path().extension() == ".trp") { + std::string filename = entry.path().stem().string(); // "trophy00", "trophy01", etc. + + // Check if filename starts with "trophy" + if (filename.find(pattern) != 0) { + continue; + } + + // Extract the number part + std::string num_str = filename.substr(pattern.length()); + s32 trophy_index = std::stoi(num_str); + + if (np_comm_ids.size() <= trophy_index) { + // This logic currently assumes each NPCommID corresponds to a trophy index. + LOG_WARNING(Common_Filesystem, + "Trophy index {} does not have a corresponding NPCommId", trophy_index); + continue; + } + + // Extract the actual trophies if they're no extracted yet + std::string np_comm_id = np_comm_ids[trophy_index]; + const auto& trophy_output_dir = + Common::FS::GetUserPath(Common::FS::PathType::UserDir) / "trophy" / np_comm_id; + if (!std::filesystem::exists(trophy_output_dir)) { + TRP trp; + if (!trp.Extract(entry, np_comm_id, trophy_output_dir)) { + LOG_ERROR(Loader, "Couldn't extract trophy file {}", filename); + continue; + } + } + + // Move extracted trophy contents into each user's folder + for (User user : UserSettings.GetUserManager().GetValidUsers()) { + auto const user_trophy_file = EmulatorSettings.GetHomeDir() / + std::to_string(user.user_id) / "trophy" / + (np_comm_id + ".xml"); + if (!std::filesystem::exists(user_trophy_file)) { + auto temp = user_trophy_file.parent_path(); + std::filesystem::create_directories(temp); + std::error_code discard; + std::filesystem::copy_file(trophy_output_dir / "Xml" / "TROPCONF.XML", + user_trophy_file, discard); + } + } + + // Add the relevant trophies to our trophy index map. + // This currently assumes the order of NPCommIDs matches the order of trophies. + trophy_index_map[trophy_index] = np_comm_id; + LOG_DEBUG(Loader, "Mapped trophy index {} to NPCommID: {}", trophy_index, np_comm_id); + } + } + return trophy_index_map; +} + void Emulator::Run(std::filesystem::path file, std::vector args, std::optional p_game_folder) { Common::SetCurrentThreadName("shadPS4:Main"); @@ -199,48 +271,6 @@ void Emulator::Run(std::filesystem::path file, std::vector args, } game_info.game_folder = game_folder; - std::filesystem::path npbindPath = mnt->GetHostPath("/app0/sce_sys/npbind.dat"); - std::filesystem::path trophyDir = mnt->GetHostPath("/app0/sce_sys/trophy"); - NPBindFile npbind; - if (!npbind.Load(npbindPath.string())) { - LOG_WARNING(Common_Filesystem, "Failed to load npbind.dat file"); - } else { - auto npCommIds = npbind.GetNpCommIds(); - if (!std::filesystem::exists(trophyDir) || npCommIds.empty()) { - LOG_WARNING(Common_Filesystem, "Cannot extract game trophies"); - } else { - std::vector> trophyFiles; // (trophy_index, filename) - std::string pattern = "trophy"; - for (const auto& entry : std::filesystem::directory_iterator(trophyDir)) { - if (entry.is_regular_file() && entry.path().extension() == ".trp") { - std::string filename = - entry.path().stem().string(); // "trophy00", "trophy01", etc. - - // Check if filename starts with "trophy" - if (filename.find(pattern) == 0) { - // Extract the number part - std::string numStr = filename.substr(pattern.length()); - int trophy_index = std::stoi(numStr); - trophyFiles.emplace_back(trophy_index, filename); - } - } - } - // Sort by trophy index - std::sort(trophyFiles.begin(), trophyFiles.end()); - std::map trophyIndexMap{}; - // Map trophy indices to npCommIds (assuming npCommIds are in the same order as sorted - // trophy files) - for (size_t i = 0; i < trophyFiles.size() && i < npCommIds.size(); i++) { - int trophy_index = trophyFiles[i].first; - const std::string& npCommId = npCommIds[i]; - trophyIndexMap[trophy_index] = npCommId; - LOG_DEBUG(Loader, "Mapped trophy index {} to npCommId: {}", trophy_index, npCommId); - } - game_info.trophyIndexMap = std::move(trophyIndexMap); - game_info.npCommIds = std::move(npCommIds); - } - } - EmulatorSettings.Load(id); Common::Log::Shutdown(); @@ -342,36 +372,16 @@ void Emulator::Run(std::filesystem::path file, std::vector args, // Load renderdoc module VideoCore::LoadRenderDoc(); - // Initialize patcher and trophies + // Initialize patcher if (!id.empty()) { MemoryPatcher::g_game_serial = id; - - int index = 0; - for (std::string npCommId : game_info.npCommIds) { - const auto trophyOutputDir = - Common::FS::GetUserPath(Common::FS::PathType::UserDir) / "trophy" / npCommId; - if (!std::filesystem::exists(trophyOutputDir)) { - TRP trp; - if (!trp.Extract(trophyDir, index, npCommId, trophyOutputDir)) { - LOG_ERROR(Loader, "Couldn't extract trophies"); - } - } - for (User user : UserSettings.GetUserManager().GetValidUsers()) { - auto const user_trophy_file = EmulatorSettings.GetHomeDir() / - std::to_string(user.user_id) / "trophy" / - (npCommId + ".xml"); - if (!std::filesystem::exists(user_trophy_file)) { - auto temp = user_trophy_file.parent_path(); - std::filesystem::create_directories(temp); - std::error_code discard; - std::filesystem::copy_file(trophyOutputDir / "Xml" / "TROPCONF.XML", - user_trophy_file, discard); - } - } - index++; - } } + // Extract and load trophies + std::filesystem::path npbind_path = mnt->GetHostPath("/app0/sce_sys/npbind.dat"); + std::filesystem::path trophy_dir = mnt->GetHostPath("/app0/sce_sys/trophy"); + game_info.trophy_index_map = ExtractTrophies(npbind_path, trophy_dir); + std::string game_title = fmt::format("{} - {} <{}>", id, title, app_version); std::string window_title = ""; std::string remote_url(Common::g_scm_remote_url);