Trophy fix (#4774)

* trophies cleanup and fixes

* even more trophy fixes for stephen
This commit is contained in:
georgemoralis
2026-07-30 19:40:12 +03:00
committed by GitHub
parent 24e68bafde
commit e7eb1da596
4 changed files with 99 additions and 31 deletions
+26 -16
View File
@@ -1,6 +1,8 @@
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <system_error>
#include "common/aes.h"
#include "common/key_manager.h"
#include "common/logging/log.h"
@@ -35,13 +37,6 @@ static void removePadding(std::vector<u8>& vec) {
}
}
static void hexToBytes(const char* hex, unsigned char* dst) {
for (size_t i = 0; hex[i] != 0; i++) {
const unsigned char value = (hex[i] < 0x3A) ? (hex[i] - 0x30) : (hex[i] - 0x37);
dst[i / 2] |= ((i % 2) == 0) ? (value << 4) : (value);
}
}
bool TRP::Extract(const std::filesystem::path& trophyPath, std::string npCommId,
const std::filesystem::path& outputPath) {
// Retrieve trophy key
@@ -58,6 +53,10 @@ bool TRP::Extract(const std::filesystem::path& trophyPath, std::string npCommId,
s32 trpFileIndex = 0;
bool success = true;
const auto cleanupOnFailure = [&outputPath] {
std::error_code ec;
std::filesystem::remove_all(outputPath, ec);
};
try {
if (trophyPath.extension() != ".trp") {
return false;
@@ -75,15 +74,22 @@ bool TRP::Extract(const std::filesystem::path& trophyPath, std::string npCommId,
}
if (header.magic != TRP_MAGIC) {
LOG_ERROR(Common_Filesystem, "Wrong trophy magic number in {}", trophyPath.string());
LOG_ERROR(Common_Filesystem,
"Wrong trophy magic number in {}: got {:#010x}, expected {:#010x}",
trophyPath.string(), static_cast<u32>(header.magic), TRP_MAGIC);
return false;
}
s64 seekPos = sizeof(TrpHeader);
// Create output directories
if (!std::filesystem::create_directories(outputPath / "Icons") ||
!std::filesystem::create_directories(outputPath / "Xml")) {
LOG_ERROR(Common_Filesystem, "Failed to create output directories for {}", npCommId);
std::error_code dir_ec;
std::filesystem::create_directories(outputPath / "Icons", dir_ec);
if (!dir_ec) {
std::filesystem::create_directories(outputPath / "Xml", dir_ec);
}
if (dir_ec) {
LOG_ERROR(Common_Filesystem, "Failed to create output directories for {}: {}", npCommId,
dir_ec.message());
cleanupOnFailure();
return false;
}
@@ -132,18 +138,22 @@ bool TRP::Extract(const std::filesystem::path& trophyPath, std::string npCommId,
} catch (const std::filesystem::filesystem_error& e) {
LOG_CRITICAL(Common_Filesystem, "Filesystem error during trophy extraction: {}", e.what());
cleanupOnFailure();
return false;
} catch (const std::exception& e) {
LOG_CRITICAL(Common_Filesystem, "Error during trophy extraction: {}", e.what());
cleanupOnFailure();
return false;
}
if (success) {
LOG_INFO(Common_Filesystem, "Successfully extracted {} trophy files for {}", trpFileIndex,
npCommId);
if (!success) {
cleanupOnFailure();
return false;
}
return success;
LOG_INFO(Common_Filesystem, "Successfully extracted {} trophy files for {}", trpFileIndex,
npCommId);
return true;
}
bool TRP::ProcessPngEntry(Common::FS::IOFile& file, const TrpEntry& entry,
-3
View File
@@ -45,7 +45,4 @@ private:
bool ProcessEncryptedXmlEntry(Common::FS::IOFile& file, const TrpEntry& entry,
const std::filesystem::path& outputPath, std::string_view name,
const std::array<u8, 16>& user_key, const std::string& npCommId);
std::array<u8, 16> esfmIv{};
static constexpr int iv_len = 16;
};
+44 -4
View File
@@ -14,6 +14,28 @@
namespace Core::FileSys {
// Iterates a pre-collected entry list produced by merging the backend stack.
class MergedDirectory final : public IDirectory {
public:
explicit MergedDirectory(std::vector<DirEntry> entries) : entries{std::move(entries)} {}
bool Next(DirEntry& out) override {
if (index >= entries.size()) {
return false;
}
out = entries[index++];
return true;
}
void Rewind() override {
index = 0;
}
private:
std::vector<DirEntry> entries;
size_t index{0};
};
bool MntPoints::ignore_game_patches = false;
std::string RemoveTrailingSlashes(const std::string& path) {
@@ -467,13 +489,31 @@ std::unique_ptr<IDirectory> MntPoints::OpenDir(std::string_view guest_path) {
}
const auto rel = RelativeToMount(*corrected, *mount);
const std::string rel_copy{rel};
std::vector<DirEntry> entries;
std::unordered_set<std::string> seen;
bool found = false;
for (const auto& backend : mount->backends) {
if (backend->Exists(rel) && backend->IsDirectory(rel)) {
return backend->OpenDir(rel_copy);
if (!backend->Exists(rel) || !backend->IsDirectory(rel)) {
continue;
}
found = true;
auto dir = backend->OpenDir(rel_copy);
if (!dir) {
continue;
}
DirEntry entry;
while (dir->Next(entry)) {
const auto key = NeedsCaseInsensitiveSearch ? Common::ToLower(entry.name) : entry.name;
if (!seen.insert(key).second) {
continue;
}
entries.push_back(entry);
}
}
return nullptr;
if (!found) {
return nullptr;
}
return std::make_unique<MergedDirectory>(std::move(entries));
}
std::optional<std::vector<u8>> MntPoints::ReadFile(std::string_view guest_path) {
+29 -8
View File
@@ -12,6 +12,7 @@
#include "common/debug.h"
#include "common/logging/log.h"
#include "common/string_util.h"
#include "common/thread.h"
#include "core/emulator_settings.h"
#include "core/ipc/ipc.h"
@@ -157,12 +158,13 @@ std::map<s32, std::string> ExtractTrophies(std::string_view npbind_guest,
if (entry.is_directory) {
continue;
}
// Extension check: entry names carry the raw leaf, no path.
if (entry.name.size() < 4 || entry.name.compare(entry.name.size() - 4, 4, ".trp") != 0) {
// Extension check: match TROPHY00.TRP as well as trophy00.trp.
const std::string name_lower = Common::ToLower(entry.name);
if (!name_lower.ends_with(".trp")) {
continue;
}
const std::string stem = entry.name.substr(0, entry.name.size() - 4);
if (stem.find(pattern) != 0) {
const std::string stem = name_lower.substr(0, name_lower.size() - 4);
if (!stem.starts_with(pattern)) {
continue;
}
@@ -212,7 +214,18 @@ std::map<s32, std::string> ExtractTrophies(std::string_view npbind_guest,
continue;
}
TRP trp;
const bool ok = trp.Extract(trp_source, np_comm_id, trophy_output_dir);
bool ok = trp.Extract(trp_source, np_comm_id, trophy_output_dir);
if (!ok) {
// if it's an update and doesn't contain trophies fallback to base
const auto base_source = mnt->GetHostPath(
entry_guest, nullptr, Core::FileSys::MntPoints::HostPathType::Base);
if (!base_source.empty() && base_source != trp_source &&
std::filesystem::is_regular_file(base_source)) {
LOG_WARNING(Loader, "Retrying trophy extraction with base game file {}",
base_source.string());
ok = trp.Extract(base_source, np_comm_id, trophy_output_dir);
}
}
if (!temp_extract.empty()) {
std::error_code ec;
std::filesystem::remove(temp_extract, ec);
@@ -231,13 +244,21 @@ std::map<s32, std::string> ExtractTrophies(std::string_view npbind_guest,
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);
std::error_code ec;
const auto tropconf = trophy_output_dir / "Xml" / "TROPCONF.XML";
std::filesystem::copy_file(tropconf, user_trophy_file, ec);
if (ec) {
LOG_ERROR(Loader, "Failed to copy {} to {}: {}", tropconf.string(),
user_trophy_file.string(), ec.message());
}
}
}
}
if (trophy_index_map.empty()) {
LOG_WARNING(Common_Filesystem, "No usable trophy files found in {}", trophy_dir_guest);
}
return trophy_index_map;
}