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.
This commit is contained in:
Stephen Miller 2026-06-01 09:00:15 -04:00 committed by GitHub
parent 48283da2a6
commit 0e23d468ca
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 94 additions and 116 deletions

View File

@ -75,8 +75,7 @@ class ElfInfo {
std::filesystem::path splash_path{};
std::filesystem::path game_folder{};
std::vector<std::string> npCommIds{};
std::map<int, std::string> trophyIndexMap{};
std::map<s32, std::string> trophy_index_map{};
public:
static constexpr u32 FW_100 = 0x1000000;
@ -145,12 +144,8 @@ public:
return game_folder;
}
[[nodiscard]] const std::vector<std::string> GetNpCommIds() const {
return npCommIds;
}
[[nodiscard]] const std::map<int, std::string>& GetTrophyIndexMap() const {
return trophyIndexMap;
[[nodiscard]] const std::map<s32, std::string>& GetTrophyIndexMap() const {
return trophy_index_map;
}
};

View File

@ -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<std::filesystem::path> 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<u8, 16> 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<unsigned int>(entry.flag), name);
static_cast<u32>(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<const u8, 16> 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) {

View File

@ -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:

View File

@ -91,6 +91,78 @@ s32 ReadCompiledSdkVersion(const std::filesystem::path& file) {
return 0;
}
std::map<s32, std::string> ExtractTrophies(const std::filesystem::path& npbind_path,
const std::filesystem::path& trophy_dir) {
std::map<s32, std::string> 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<std::string> args,
std::optional<std::filesystem::path> p_game_folder) {
Common::SetCurrentThreadName("shadPS4:Main");
@ -199,48 +271,6 @@ void Emulator::Run(std::filesystem::path file, std::vector<std::string> 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<std::pair<int, std::string>> 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<int, std::string> 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<std::string> 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);