shadPS4/src/core/libraries/np/np_trophy.cpp
2026-03-27 17:58:54 +02:00

1520 lines
56 KiB
C++

// SPDX-FileCopyrightText: Copyright 2025-2026 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <unordered_map>
#include <pugixml.hpp>
#include "common/elf_info.h"
#include "common/logging/log.h"
#include "common/path_util.h"
#include "common/slot_vector.h"
#include "core/emulator_settings.h"
#include "core/libraries/libs.h"
#include "core/libraries/np/np_error.h"
#include "core/libraries/np/np_trophy.h"
#include "core/libraries/np/np_trophy_error.h"
#include "core/libraries/np/trophy_ui.h"
#include "core/libraries/system/userservice.h"
#include "core/memory.h"
namespace Libraries::Np::NpTrophy {
// PS4 system language IDs map directly to TROP00.XML .. TROP30.XML.
// Index = OrbisSystemServiceParamId language value reported by the system.
// clang-format off
static constexpr std::array<std::string_view, 31> s_language_xml_names = {
"TROP_00.XML", // 00 Japanese
"TROP_01.XML", // 01 English (US)
"TROP_02.XML", // 02 French
"TROP_03.XML", // 03 Spanish (ES)
"TROP_04.XML", // 04 German
"TROP_05.XML", // 05 Italian
"TROP_06.XML", // 06 Dutch
"TROP_07.XML", // 07 Portuguese (PT)
"TROP_08.XML", // 08 Russian
"TROP_09.XML", // 09 Korean
"TROP_10.XML", // 10 Traditional Chinese
"TROP_11.XML", // 11 Simplified Chinese
"TROP_12.XML", // 12 Finnish
"TROP_13.XML", // 13 Swedish
"TROP_14.XML", // 14 Danish
"TROP_15.XML", // 15 Norwegian
"TROP_16.XML", // 16 Polish
"TROP_17.XML", // 17 Portuguese (BR)
"TROP_18.XML", // 18 English (GB)
"TROP_19.XML", // 19 Turkish
"TROP_20.XML", // 20 Spanish (LA)
"TROP_21.XML", // 21 Arabic
"TROP_22.XML", // 22 French (CA)
"TROP_23.XML", // 23 Czech
"TROP_24.XML", // 24 Hungarian
"TROP_25.XML", // 25 Greek
"TROP_26.XML", // 26 Romanian
"TROP_27.XML", // 27 Thai
"TROP_28.XML", // 28 Vietnamese
"TROP_29.XML", // 29 Indonesian
"TROP_30.XML", // 30 Unkrainian
};
// clang-format on
// Returns the best available trophy XML path for the current system language.
// Resolution order:
// 1. TROP_XX.XML for the active system language (e.g. TROP01.XML for English)
// 2. TROP.XML (master / language-neutral fallback)
static std::filesystem::path GetTrophyXmlPath(const std::filesystem::path& xml_dir,
int system_language) {
// Try the exact language file first.
if (system_language >= 0 && system_language < static_cast<int>(s_language_xml_names.size())) {
auto lang_path = xml_dir / s_language_xml_names[system_language];
if (std::filesystem::exists(lang_path)) {
return lang_path;
}
}
// Final fallback: master TROP.XML (always present).
return xml_dir / "TROP.XML";
}
static void ApplyUnlockToXmlFile(const std::filesystem::path& xml_path, OrbisNpTrophyId trophyId,
u64 trophyTimestamp, bool unlock_platinum,
OrbisNpTrophyId platinumId, u64 platinumTimestamp) {
pugi::xml_document doc;
if (!doc.load_file(xml_path.native().c_str())) {
LOG_WARNING(Lib_NpTrophy, "ApplyUnlock: failed to load {}", xml_path.string());
return;
}
auto trophyconf = doc.child("trophyconf");
for (pugi::xml_node& node : trophyconf.children()) {
if (std::string_view(node.name()) != "trophy") {
continue;
}
int id = node.attribute("id").as_int(ORBIS_NP_TROPHY_INVALID_TROPHY_ID);
auto set_unlock = [&](u64 ts) {
if (node.attribute("unlockstate").empty()) {
node.append_attribute("unlockstate") = "true";
} else {
node.attribute("unlockstate").set_value("true");
}
const auto ts_str = std::to_string(ts);
if (node.attribute("timestamp").empty()) {
node.append_attribute("timestamp") = ts_str.c_str();
} else {
node.attribute("timestamp").set_value(ts_str.c_str());
}
};
if (id == trophyId) {
set_unlock(trophyTimestamp);
} else if (unlock_platinum && id == platinumId) {
set_unlock(platinumTimestamp);
}
}
doc.save_file(xml_path.native().c_str());
}
static constexpr auto MaxTrophyHandles = 4u;
static constexpr auto MaxTrophyContexts = 8u;
using ContextKey = std::pair<u32, u32>; // <user_id, service label>
struct ContextKeyHash {
size_t operator()(const ContextKey& key) const {
return key.first + (u64(key.second) << 32u);
}
};
struct TrophyContext {
u32 context_id;
bool registered = false;
std::filesystem::path trophy_xml_path; // resolved once at CreateContext
std::filesystem::path xml_dir; // .../Xml/
std::filesystem::path xml_save_file; // The actual file for tracking progress per-user.
std::filesystem::path icons_dir; // .../Icons/
};
static Common::SlotVector<OrbisNpTrophyHandle> trophy_handles{};
static Common::SlotVector<ContextKey> trophy_contexts{};
static std::unordered_map<ContextKey, TrophyContext, ContextKeyHash> contexts_internal{};
void ORBIS_NP_TROPHY_FLAG_ZERO(OrbisNpTrophyFlagArray* p) {
for (int i = 0; i < ORBIS_NP_TROPHY_NUM_MAX; i++) {
uint32_t array_index = i / 32;
uint32_t bit_position = i % 32;
p->flag_bits[array_index] &= ~(1U << bit_position);
}
}
void ORBIS_NP_TROPHY_FLAG_SET(int32_t trophyId, OrbisNpTrophyFlagArray* p) {
uint32_t array_index = trophyId / 32;
uint32_t bit_position = trophyId % 32;
p->flag_bits[array_index] |= (1U << bit_position);
}
void ORBIS_NP_TROPHY_FLAG_SET_ALL(OrbisNpTrophyFlagArray* p) {
for (int i = 0; i < ORBIS_NP_TROPHY_NUM_MAX; i++) {
uint32_t array_index = i / 32;
uint32_t bit_position = i % 32;
p->flag_bits[array_index] |= (1U << bit_position);
}
}
void ORBIS_NP_TROPHY_FLAG_CLR(int32_t trophyId, OrbisNpTrophyFlagArray* p) {
uint32_t array_index = trophyId / 32;
uint32_t bit_position = trophyId % 32;
p->flag_bits[array_index] &= ~(1U << bit_position);
}
bool ORBIS_NP_TROPHY_FLAG_ISSET(int32_t trophyId, OrbisNpTrophyFlagArray* p) {
uint32_t array_index = trophyId / 32;
uint32_t bit_position = trophyId % 32;
return (p->flag_bits[array_index] & (1U << bit_position)) ? 1 : 0;
}
OrbisNpTrophyGrade GetTrophyGradeFromChar(char trophyType) {
switch (trophyType) {
default:
return ORBIS_NP_TROPHY_GRADE_UNKNOWN;
break;
case 'B':
return ORBIS_NP_TROPHY_GRADE_BRONZE;
break;
case 'S':
return ORBIS_NP_TROPHY_GRADE_SILVER;
break;
case 'G':
return ORBIS_NP_TROPHY_GRADE_GOLD;
break;
case 'P':
return ORBIS_NP_TROPHY_GRADE_PLATINUM;
break;
}
}
s32 PS4_SYSV_ABI sceNpTrophyCreateContext(OrbisNpTrophyContext* context,
Libraries::UserService::OrbisUserServiceUserId user_id,
uint32_t service_label, u64 options) {
if (!context || options != 0ull) {
return ORBIS_NP_TROPHY_ERROR_INVALID_ARGUMENT;
}
if (trophy_contexts.size() >= MaxTrophyContexts) {
return ORBIS_NP_TROPHY_ERROR_CONTEXT_EXCEEDS_MAX;
}
const auto& key = ContextKey{user_id, service_label};
if (contexts_internal.contains(key)) {
return ORBIS_NP_TROPHY_ERROR_CONTEXT_ALREADY_EXISTS;
}
const auto ctx_id = trophy_contexts.insert(user_id, service_label);
*context = ctx_id.index + 1;
auto& ctx = contexts_internal[key];
ctx.context_id = *context;
// Resolve and cache all paths once so callers never recompute them.
const std::string np_comm_id = Common::ElfInfo::Instance().GetNpCommIds()[service_label];
const auto trophy_base =
Common::FS::GetUserPath(Common::FS::PathType::UserDir) / "trophy" / np_comm_id;
ctx.xml_save_file = Common::FS::GetUserPath(Common::FS::PathType::HomeDir) /
std::to_string(user_id) / "trophy" / (np_comm_id + ".xml");
ctx.xml_dir = trophy_base / "Xml";
ctx.icons_dir = trophy_base / "Icons";
ctx.trophy_xml_path = GetTrophyXmlPath(ctx.xml_dir, EmulatorSettings.GetConsoleLanguage());
LOG_INFO(Lib_NpTrophy, "New context = {}, user_id = {} service label = {}", *context, user_id,
service_label);
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceNpTrophyCreateHandle(OrbisNpTrophyHandle* handle) {
if (!handle) {
return ORBIS_NP_TROPHY_ERROR_INVALID_ARGUMENT;
}
if (trophy_handles.size() >= MaxTrophyHandles) {
return ORBIS_NP_TROPHY_ERROR_HANDLE_EXCEEDS_MAX;
}
const auto handle_id = trophy_handles.insert();
*handle = handle_id.index + 1;
LOG_INFO(Lib_NpTrophy, "New handle = {}", *handle);
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophyDestroyContext(OrbisNpTrophyContext context) {
LOG_INFO(Lib_NpTrophy, "Destroyed Context {}", context);
if (context == ORBIS_NP_TROPHY_INVALID_CONTEXT) {
return ORBIS_NP_TROPHY_ERROR_INVALID_CONTEXT;
}
Common::SlotId contextId;
contextId.index = context - 1;
if (contextId.index >= trophy_contexts.size()) {
return ORBIS_NP_TROPHY_ERROR_INVALID_CONTEXT;
}
if (!trophy_contexts.is_allocated(contextId)) {
return ORBIS_NP_TROPHY_ERROR_INVALID_CONTEXT;
}
ContextKey contextkey = trophy_contexts[contextId];
trophy_contexts.erase(contextId);
contexts_internal.erase(contextkey);
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceNpTrophyDestroyHandle(OrbisNpTrophyHandle handle) {
if (handle == ORBIS_NP_TROPHY_INVALID_HANDLE)
return ORBIS_NP_TROPHY_ERROR_INVALID_HANDLE;
s32 handle_index = handle - 1;
if (handle_index >= trophy_handles.size()) {
LOG_ERROR(Lib_NpTrophy, "Invalid handle {}", handle);
return ORBIS_NP_TROPHY_ERROR_INVALID_HANDLE;
}
if (!trophy_handles.is_allocated({static_cast<u32>(handle_index)})) {
return ORBIS_NP_TROPHY_ERROR_INVALID_HANDLE;
}
trophy_handles.erase({static_cast<u32>(handle_index)});
LOG_INFO(Lib_NpTrophy, "Handle {} destroyed", handle);
return ORBIS_OK;
}
u64 ReadFile(Common::FS::IOFile& file, void* buf, u64 nbytes) {
const auto* memory = Core::Memory::Instance();
// Invalidate up to the actual number of bytes that could be read.
const auto remaining = file.GetSize() - file.Tell();
memory->InvalidateMemory(reinterpret_cast<VAddr>(buf), std::min<u64>(nbytes, remaining));
return file.ReadRaw<u8>(buf, nbytes);
}
int PS4_SYSV_ABI sceNpTrophyGetGameIcon(OrbisNpTrophyContext context, OrbisNpTrophyHandle handle,
void* buffer, u64* size) {
ASSERT(size != nullptr);
Common::SlotId contextId;
contextId.index = context - 1;
if (contextId.index >= trophy_contexts.size()) {
return ORBIS_NP_TROPHY_ERROR_INVALID_CONTEXT;
}
ContextKey contextkey = trophy_contexts[contextId];
const auto& ctx = contexts_internal[contextkey];
auto icon_file = ctx.icons_dir / "ICON0.PNG";
Common::FS::IOFile icon(icon_file, Common::FS::FileAccessMode::Read);
if (!icon.IsOpen()) {
LOG_ERROR(Lib_NpTrophy, "Failed to open trophy icon file: {}", icon_file.string());
return ORBIS_NP_TROPHY_ERROR_ICON_FILE_NOT_FOUND;
}
u64 icon_size = icon.GetSize();
if (buffer != nullptr) {
ReadFile(icon, buffer, *size);
} else {
*size = icon_size;
}
return ORBIS_OK;
}
struct GameTrophyInfo {
uint32_t num_groups;
uint32_t num_trophies;
uint32_t num_trophies_by_rarity[5];
uint32_t unlocked_trophies;
uint32_t unlocked_trophies_by_rarity[5];
};
int PS4_SYSV_ABI sceNpTrophyGetGameInfo(OrbisNpTrophyContext context, OrbisNpTrophyHandle handle,
OrbisNpTrophyGameDetails* details,
OrbisNpTrophyGameData* data) {
LOG_INFO(Lib_NpTrophy, "Getting Game Trophy");
if (context == ORBIS_NP_TROPHY_INVALID_CONTEXT)
return ORBIS_NP_TROPHY_ERROR_INVALID_CONTEXT;
if (handle == ORBIS_NP_TROPHY_INVALID_HANDLE)
return ORBIS_NP_TROPHY_ERROR_INVALID_HANDLE;
if (details == nullptr || data == nullptr)
return ORBIS_NP_TROPHY_ERROR_INVALID_ARGUMENT;
if (details->size != 0x4A0 || data->size != 0x20)
return ORBIS_NP_TROPHY_ERROR_INVALID_ARGUMENT;
Common::SlotId contextId;
contextId.index = context - 1;
if (contextId.index >= trophy_contexts.size()) {
return ORBIS_NP_TROPHY_ERROR_INVALID_CONTEXT;
}
ContextKey contextkey = trophy_contexts[contextId];
const auto& ctx = contexts_internal[contextkey];
if (!ctx.registered)
return ORBIS_NP_TROPHY_ERROR_NOT_REGISTERED;
const auto& trophy_file = ctx.trophy_xml_path;
const auto& trophy_save_file = ctx.xml_save_file;
pugi::xml_document doc;
pugi::xml_parse_result result = doc.load_file(trophy_file.native().c_str());
if (!result) {
LOG_ERROR(Lib_NpTrophy, "Failed to parse trophy xml : {}", result.description());
return ORBIS_OK;
}
GameTrophyInfo game_info{};
auto trophyconf = doc.child("trophyconf");
for (const pugi::xml_node& node : trophyconf.children()) {
std::string_view node_name = node.name();
if (node_name == "title-name") {
strncpy(details->title, node.text().as_string(), ORBIS_NP_TROPHY_GAME_TITLE_MAX_SIZE);
}
if (node_name == "title-detail") {
strncpy(details->description, node.text().as_string(),
ORBIS_NP_TROPHY_GAME_DESCR_MAX_SIZE);
}
if (node_name == "group")
game_info.num_groups++;
}
pugi::xml_document save_doc;
pugi::xml_parse_result save_result = save_doc.load_file(ctx.xml_save_file.native().c_str());
if (!save_result) {
LOG_ERROR(Lib_NpTrophy, "Failed to parse user trophy xml : {}", result.description());
return ORBIS_OK;
}
auto save_trophyconf = save_doc.child("trophyconf");
for (const pugi::xml_node& node : save_trophyconf.children()) {
std::string_view node_name = node.name();
if (node_name == "trophy") {
bool current_trophy_unlockstate = node.attribute("unlockstate").as_bool();
std::string_view current_trophy_grade = node.attribute("ttype").value();
if (current_trophy_grade.empty()) {
continue;
}
game_info.num_trophies++;
int trophy_grade = GetTrophyGradeFromChar(current_trophy_grade.at(0));
game_info.num_trophies_by_rarity[trophy_grade]++;
if (current_trophy_unlockstate) {
game_info.unlocked_trophies++;
game_info.unlocked_trophies_by_rarity[trophy_grade]++;
}
}
}
details->num_groups = game_info.num_groups;
details->num_trophies = game_info.num_trophies;
details->num_platinum = game_info.num_trophies_by_rarity[ORBIS_NP_TROPHY_GRADE_PLATINUM];
details->num_gold = game_info.num_trophies_by_rarity[ORBIS_NP_TROPHY_GRADE_GOLD];
details->num_silver = game_info.num_trophies_by_rarity[ORBIS_NP_TROPHY_GRADE_SILVER];
details->num_bronze = game_info.num_trophies_by_rarity[ORBIS_NP_TROPHY_GRADE_BRONZE];
data->unlocked_trophies = game_info.unlocked_trophies;
data->unlocked_platinum = game_info.unlocked_trophies_by_rarity[ORBIS_NP_TROPHY_GRADE_PLATINUM];
data->unlocked_gold = game_info.unlocked_trophies_by_rarity[ORBIS_NP_TROPHY_GRADE_GOLD];
data->unlocked_silver = game_info.unlocked_trophies_by_rarity[ORBIS_NP_TROPHY_GRADE_SILVER];
data->unlocked_bronze = game_info.unlocked_trophies_by_rarity[ORBIS_NP_TROPHY_GRADE_BRONZE];
data->progress_percentage = (game_info.num_trophies > 0)
? (game_info.unlocked_trophies * 100u) / game_info.num_trophies
: 0;
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophyGetGroupIcon(OrbisNpTrophyContext context, OrbisNpTrophyHandle handle,
OrbisNpTrophyGroupId groupId, void* buffer, u64* size) {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
struct GroupTrophyInfo {
uint32_t num_trophies;
uint32_t num_trophies_by_rarity[5];
uint32_t unlocked_trophies;
uint32_t unlocked_trophies_by_rarity[5];
};
int PS4_SYSV_ABI sceNpTrophyGetGroupInfo(OrbisNpTrophyContext context, OrbisNpTrophyHandle handle,
OrbisNpTrophyGroupId groupId,
OrbisNpTrophyGroupDetails* details,
OrbisNpTrophyGroupData* data) {
LOG_INFO(Lib_NpTrophy, "Getting Trophy Group Info for id {}", groupId);
if (context == ORBIS_NP_TROPHY_INVALID_CONTEXT)
return ORBIS_NP_TROPHY_ERROR_INVALID_CONTEXT;
if (handle == ORBIS_NP_TROPHY_INVALID_HANDLE)
return ORBIS_NP_TROPHY_ERROR_INVALID_HANDLE;
if (details == nullptr || data == nullptr)
return ORBIS_NP_TROPHY_ERROR_INVALID_ARGUMENT;
if (details->size != 0x4A0 || data->size != 0x28)
return ORBIS_NP_TROPHY_ERROR_INVALID_ARGUMENT;
Common::SlotId contextId;
contextId.index = context - 1;
if (contextId.index >= trophy_contexts.size()) {
return ORBIS_NP_TROPHY_ERROR_INVALID_CONTEXT;
}
ContextKey contextkey = trophy_contexts[contextId];
const auto& ctx = contexts_internal[contextkey];
if (!ctx.registered)
return ORBIS_NP_TROPHY_ERROR_NOT_REGISTERED;
const auto& trophy_file = ctx.trophy_xml_path;
pugi::xml_document doc;
pugi::xml_parse_result result = doc.load_file(trophy_file.native().c_str());
if (!result) {
LOG_ERROR(Lib_NpTrophy, "Failed to open trophy xml : {}", result.description());
return ORBIS_OK;
}
GroupTrophyInfo group_info{};
auto trophyconf = doc.child("trophyconf");
for (const pugi::xml_node& node : trophyconf.children()) {
std::string_view node_name = node.name();
if (node_name == "group") {
int current_group_id = node.attribute("id").as_int(ORBIS_NP_TROPHY_INVALID_GROUP_ID);
if (current_group_id != ORBIS_NP_TROPHY_INVALID_GROUP_ID) {
if (current_group_id == groupId) {
std::string_view current_group_name = node.child("name").text().as_string();
std::string_view current_group_description =
node.child("detail").text().as_string();
strncpy(details->title, current_group_name.data(),
ORBIS_NP_TROPHY_GROUP_TITLE_MAX_SIZE);
strncpy(details->description, current_group_description.data(),
ORBIS_NP_TROPHY_GAME_DESCR_MAX_SIZE);
}
}
}
details->group_id = groupId;
data->group_id = groupId;
}
pugi::xml_document save_doc;
pugi::xml_parse_result save_result = save_doc.load_file(ctx.xml_save_file.native().c_str());
if (!save_result) {
LOG_ERROR(Lib_NpTrophy, "Failed to parse user trophy xml : {}", result.description());
return ORBIS_OK;
}
auto save_trophyconf = save_doc.child("trophyconf");
for (const pugi::xml_node& node : save_trophyconf.children()) {
std::string_view node_name = node.name();
if (node_name == "trophy") {
bool current_trophy_unlockstate = node.attribute("unlockstate").as_bool();
std::string_view current_trophy_grade = node.attribute("ttype").value();
int current_trophy_group_id = node.attribute("gid").as_int(-1);
if (current_trophy_grade.empty()) {
continue;
}
if (current_trophy_group_id == groupId) {
group_info.num_trophies++;
int trophyGrade = GetTrophyGradeFromChar(current_trophy_grade.at(0));
group_info.num_trophies_by_rarity[trophyGrade]++;
if (current_trophy_unlockstate) {
group_info.unlocked_trophies++;
group_info.unlocked_trophies_by_rarity[trophyGrade]++;
}
}
}
}
details->num_trophies = group_info.num_trophies;
details->num_platinum = group_info.num_trophies_by_rarity[ORBIS_NP_TROPHY_GRADE_PLATINUM];
details->num_gold = group_info.num_trophies_by_rarity[ORBIS_NP_TROPHY_GRADE_GOLD];
details->num_silver = group_info.num_trophies_by_rarity[ORBIS_NP_TROPHY_GRADE_SILVER];
details->num_bronze = group_info.num_trophies_by_rarity[ORBIS_NP_TROPHY_GRADE_BRONZE];
data->unlocked_trophies = group_info.unlocked_trophies;
data->unlocked_platinum =
group_info.unlocked_trophies_by_rarity[ORBIS_NP_TROPHY_GRADE_PLATINUM];
data->unlocked_gold = group_info.unlocked_trophies_by_rarity[ORBIS_NP_TROPHY_GRADE_GOLD];
data->unlocked_silver = group_info.unlocked_trophies_by_rarity[ORBIS_NP_TROPHY_GRADE_SILVER];
data->unlocked_bronze = group_info.unlocked_trophies_by_rarity[ORBIS_NP_TROPHY_GRADE_BRONZE];
data->progress_percentage =
(group_info.num_trophies > 0)
? (group_info.unlocked_trophies * 100u) / group_info.num_trophies
: 0;
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophyGetTrophyIcon(OrbisNpTrophyContext context, OrbisNpTrophyHandle handle,
OrbisNpTrophyId trophyId, void* buffer, u64* size) {
if (size == nullptr)
return ORBIS_NP_TROPHY_ERROR_INVALID_ARGUMENT;
if (trophyId < 0 || trophyId >= ORBIS_NP_TROPHY_NUM_MAX)
return ORBIS_NP_TROPHY_ERROR_INVALID_TROPHY_ID;
if (context == ORBIS_NP_TROPHY_INVALID_CONTEXT)
return ORBIS_NP_TROPHY_ERROR_INVALID_CONTEXT;
if (handle == ORBIS_NP_TROPHY_INVALID_HANDLE)
return ORBIS_NP_TROPHY_ERROR_INVALID_HANDLE;
Common::SlotId contextId;
contextId.index = context - 1;
if (contextId.index >= trophy_contexts.size() || !trophy_contexts.is_allocated(contextId)) {
return ORBIS_NP_TROPHY_ERROR_INVALID_CONTEXT;
}
s32 handle_index = handle - 1;
if (handle_index >= trophy_handles.size() ||
!trophy_handles.is_allocated({static_cast<u32>(handle_index)})) {
return ORBIS_NP_TROPHY_ERROR_INVALID_HANDLE;
}
ContextKey contextkey = trophy_contexts[contextId];
const auto& ctx = contexts_internal[contextkey];
if (!ctx.registered)
return ORBIS_NP_TROPHY_ERROR_NOT_REGISTERED;
// Check that the trophy is unlocked and icons are only available for earned trophies.
pugi::xml_document doc;
if (!doc.load_file(ctx.xml_save_file.native().c_str())) {
LOG_ERROR(Lib_NpTrophy, "Failed to open trophy XML: {}", ctx.xml_save_file.string());
return ORBIS_NP_TROPHY_ERROR_ICON_FILE_NOT_FOUND;
}
bool unlocked = false;
bool found = false;
for (const pugi::xml_node& node : doc.child("trophyconf").children()) {
if (std::string_view(node.name()) != "trophy")
continue;
if (node.attribute("id").as_int(ORBIS_NP_TROPHY_INVALID_TROPHY_ID) == trophyId) {
found = true;
unlocked = node.attribute("unlockstate").as_bool();
break;
}
}
if (!found)
return ORBIS_NP_TROPHY_ERROR_INVALID_TROPHY_ID;
if (!unlocked)
return ORBIS_NP_TROPHY_ERROR_TROPHY_NOT_UNLOCKED;
const std::string icon_name = fmt::format("TROP{:03d}.PNG", trophyId);
const auto icon_path = ctx.icons_dir / icon_name;
Common::FS::IOFile icon(icon_path, Common::FS::FileAccessMode::Read);
if (!icon.IsOpen()) {
LOG_ERROR(Lib_NpTrophy, "Failed to open trophy icon: {}", icon_path.string());
return ORBIS_NP_TROPHY_ERROR_ICON_FILE_NOT_FOUND;
}
if (buffer != nullptr) {
ReadFile(icon, buffer, *size);
} else {
*size = icon.GetSize();
}
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophyGetTrophyInfo(OrbisNpTrophyContext context, OrbisNpTrophyHandle handle,
OrbisNpTrophyId trophyId, OrbisNpTrophyDetails* details,
OrbisNpTrophyData* data) {
LOG_INFO(Lib_NpTrophy, "Getting trophy info for id {}", trophyId);
if (context == ORBIS_NP_TROPHY_INVALID_CONTEXT)
return ORBIS_NP_TROPHY_ERROR_INVALID_CONTEXT;
if (handle == ORBIS_NP_TROPHY_INVALID_HANDLE)
return ORBIS_NP_TROPHY_ERROR_INVALID_HANDLE;
if (trophyId >= ORBIS_NP_TROPHY_NUM_MAX)
return ORBIS_NP_TROPHY_ERROR_INVALID_TROPHY_ID;
if (details == nullptr || data == nullptr)
return ORBIS_NP_TROPHY_ERROR_INVALID_ARGUMENT;
if (details->size != 0x498 || data->size != 0x18)
return ORBIS_NP_TROPHY_ERROR_INVALID_ARGUMENT;
Common::SlotId contextId;
contextId.index = context - 1;
if (contextId.index >= trophy_contexts.size()) {
return ORBIS_NP_TROPHY_ERROR_INVALID_CONTEXT;
}
ContextKey contextkey = trophy_contexts[contextId];
const auto& ctx = contexts_internal[contextkey];
if (!ctx.registered)
return ORBIS_NP_TROPHY_ERROR_NOT_REGISTERED;
const auto& trophy_file = ctx.trophy_xml_path;
pugi::xml_document doc;
pugi::xml_parse_result result = doc.load_file(trophy_file.native().c_str());
if (!result) {
LOG_ERROR(Lib_NpTrophy, "Failed to open trophy xml : {}", result.description());
return ORBIS_OK;
}
auto trophyconf = doc.child("trophyconf");
for (const pugi::xml_node& node : trophyconf.children()) {
std::string_view node_name = node.name();
if (node_name == "trophy") {
int current_trophy_id = node.attribute("id").as_int(ORBIS_NP_TROPHY_INVALID_TROPHY_ID);
if (current_trophy_id == trophyId) {
std::string_view current_trophy_name = node.child("name").text().as_string();
std::string_view current_trophy_description =
node.child("detail").text().as_string();
strncpy(details->name, current_trophy_name.data(), ORBIS_NP_TROPHY_NAME_MAX_SIZE);
strncpy(details->description, current_trophy_description.data(),
ORBIS_NP_TROPHY_DESCR_MAX_SIZE);
}
}
}
pugi::xml_document save_doc;
pugi::xml_parse_result save_result = save_doc.load_file(ctx.xml_save_file.native().c_str());
if (!save_result) {
LOG_ERROR(Lib_NpTrophy, "Failed to parse user trophy xml : {}", result.description());
return ORBIS_OK;
}
auto save_trophyconf = save_doc.child("trophyconf");
for (const pugi::xml_node& node : save_trophyconf.children()) {
std::string_view node_name = node.name();
if (node_name == "trophy") {
int current_trophy_id = node.attribute("id").as_int(ORBIS_NP_TROPHY_INVALID_TROPHY_ID);
if (current_trophy_id == trophyId) {
bool current_trophy_unlockstate = node.attribute("unlockstate").as_bool();
std::string_view current_trophy_grade = node.attribute("ttype").value();
uint64_t current_trophy_timestamp = node.attribute("timestamp").as_ullong();
int current_trophy_groupid = node.attribute("gid").as_int(-1);
bool current_trophy_hidden = node.attribute("hidden").as_bool();
details->trophy_id = trophyId;
details->trophy_grade = GetTrophyGradeFromChar(current_trophy_grade.at(0));
details->group_id = current_trophy_groupid;
details->hidden = current_trophy_hidden;
data->trophy_id = trophyId;
data->unlocked = current_trophy_unlockstate;
data->timestamp.tick = current_trophy_timestamp;
}
}
}
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceNpTrophyGetTrophyUnlockState(OrbisNpTrophyContext context,
OrbisNpTrophyHandle handle,
OrbisNpTrophyFlagArray* flags, u32* count) {
LOG_INFO(Lib_NpTrophy, "called");
if (flags == nullptr || count == nullptr)
return ORBIS_NP_TROPHY_ERROR_INVALID_ARGUMENT;
if (context == ORBIS_NP_TROPHY_INVALID_CONTEXT)
return ORBIS_NP_TROPHY_ERROR_INVALID_CONTEXT;
if (handle == ORBIS_NP_TROPHY_INVALID_HANDLE)
return ORBIS_NP_TROPHY_ERROR_INVALID_HANDLE;
Common::SlotId contextId;
contextId.index = context - 1;
if (contextId.index >= trophy_contexts.size() || !trophy_contexts.is_allocated(contextId)) {
return ORBIS_NP_TROPHY_ERROR_INVALID_CONTEXT;
}
s32 handle_index = handle - 1;
if (handle_index >= trophy_handles.size() ||
!trophy_handles.is_allocated({static_cast<u32>(handle_index)})) {
return ORBIS_NP_TROPHY_ERROR_INVALID_HANDLE;
}
ContextKey contextkey = trophy_contexts[contextId];
const auto& ctx = contexts_internal[contextkey];
if (!ctx.registered)
return ORBIS_NP_TROPHY_ERROR_NOT_REGISTERED;
const auto& trophy_file = ctx.xml_save_file;
ORBIS_NP_TROPHY_FLAG_ZERO(flags);
pugi::xml_document doc;
pugi::xml_parse_result result = doc.load_file(trophy_file.native().c_str());
if (!result) {
LOG_ERROR(Lib_NpTrophy, "Failed to open trophy XML: {}", result.description());
*count = 0;
return ORBIS_OK;
}
int num_trophies = 0;
auto trophyconf = doc.child("trophyconf");
for (const pugi::xml_node& node : trophyconf.children()) {
std::string_view node_name = node.name();
int current_trophy_id = node.attribute("id").as_int(ORBIS_NP_TROPHY_INVALID_TROPHY_ID);
bool current_trophy_unlockstate = node.attribute("unlockstate").as_bool();
if (node_name == "trophy") {
num_trophies++;
if (current_trophy_unlockstate) {
ORBIS_NP_TROPHY_FLAG_SET(current_trophy_id, flags);
}
}
}
*count = num_trophies;
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophyRegisterContext(OrbisNpTrophyContext context,
OrbisNpTrophyHandle handle, uint64_t options) {
if (options != 0ull)
return ORBIS_NP_TROPHY_ERROR_INVALID_ARGUMENT;
if (context == ORBIS_NP_TROPHY_INVALID_CONTEXT)
return ORBIS_NP_TROPHY_ERROR_INVALID_CONTEXT;
if (handle == ORBIS_NP_TROPHY_INVALID_HANDLE)
return ORBIS_NP_TROPHY_ERROR_INVALID_HANDLE;
Common::SlotId contextId;
contextId.index = context - 1;
if (contextId.index >= trophy_contexts.size() || !trophy_contexts.is_allocated(contextId)) {
return ORBIS_NP_TROPHY_ERROR_INVALID_CONTEXT;
}
s32 handle_index = handle - 1;
if (handle_index >= trophy_handles.size() ||
!trophy_handles.is_allocated({static_cast<u32>(handle_index)})) {
return ORBIS_NP_TROPHY_ERROR_INVALID_HANDLE;
}
ContextKey contextkey = trophy_contexts[contextId];
auto& ctx = contexts_internal[contextkey];
if (ctx.registered)
return ORBIS_NP_TROPHY_ERROR_ALREADY_REGISTERED;
if (!std::filesystem::exists(ctx.trophy_xml_path))
return ORBIS_NP_TROPHY_ERROR_TITLE_CONF_NOT_INSTALLED;
ctx.registered = true;
LOG_INFO(Lib_NpTrophy, "Context {} registered", context);
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophyUnlockTrophy(OrbisNpTrophyContext context, OrbisNpTrophyHandle handle,
OrbisNpTrophyId trophyId, OrbisNpTrophyId* platinumId) {
LOG_INFO(Lib_NpTrophy, "Unlocking trophy id {}", trophyId);
if (context == ORBIS_NP_TROPHY_INVALID_CONTEXT)
return ORBIS_NP_TROPHY_ERROR_INVALID_CONTEXT;
if (handle == ORBIS_NP_TROPHY_INVALID_HANDLE)
return ORBIS_NP_TROPHY_ERROR_INVALID_HANDLE;
if (trophyId >= ORBIS_NP_TROPHY_NUM_MAX)
return ORBIS_NP_TROPHY_ERROR_INVALID_TROPHY_ID;
if (platinumId == nullptr)
return ORBIS_NP_TROPHY_ERROR_INVALID_ARGUMENT;
Common::SlotId contextId;
contextId.index = context - 1;
if (contextId.index >= trophy_contexts.size() || !trophy_contexts.is_allocated(contextId)) {
return ORBIS_NP_TROPHY_ERROR_INVALID_CONTEXT;
}
s32 handle_index = handle - 1;
if (handle_index >= trophy_handles.size() ||
!trophy_handles.is_allocated({static_cast<u32>(handle_index)})) {
return ORBIS_NP_TROPHY_ERROR_INVALID_HANDLE;
}
ContextKey contextkey = trophy_contexts[contextId];
const auto& ctx = contexts_internal[contextkey];
if (!ctx.registered)
return ORBIS_NP_TROPHY_ERROR_NOT_REGISTERED;
const auto& xml_dir = ctx.xml_dir;
const auto& trophy_file = ctx.trophy_xml_path;
pugi::xml_document save_doc;
pugi::xml_parse_result save_result = save_doc.load_file(ctx.xml_save_file.native().c_str());
if (!save_result) {
LOG_ERROR(Lib_NpTrophy, "Failed to parse user trophy xml : {}", save_result.description());
return ORBIS_OK;
}
auto save_trophyconf = save_doc.child("trophyconf");
for (const pugi::xml_node& node : save_trophyconf.children()) {
std::string_view node_name = node.name();
if (std::string_view(node.name()) != "trophy")
continue;
int current_trophy_id = node.attribute("id").as_int(ORBIS_NP_TROPHY_INVALID_TROPHY_ID);
bool current_trophy_unlockstate = node.attribute("unlockstate").as_bool();
if (current_trophy_id == trophyId) {
if (current_trophy_unlockstate) {
LOG_INFO(Lib_NpTrophy, "Trophy already unlocked");
return ORBIS_NP_TROPHY_ERROR_TROPHY_ALREADY_UNLOCKED;
}
}
}
pugi::xml_document doc;
pugi::xml_parse_result result = doc.load_file(trophy_file.native().c_str());
if (!result) {
LOG_ERROR(Lib_NpTrophy, "Failed to parse trophy xml : {}", result.description());
return ORBIS_NP_TROPHY_ERROR_TITLE_NOT_FOUND;
}
*platinumId = ORBIS_NP_TROPHY_INVALID_TROPHY_ID;
int num_trophies = 0;
int num_trophies_unlocked = 0;
pugi::xml_node platinum_node;
// Outputs filled during the scan.
bool trophy_found = false;
const char* trophy_name = "";
std::string_view trophy_type;
std::filesystem::path trophy_icon_path;
auto trophyconf = doc.child("trophyconf");
for (pugi::xml_node& node : trophyconf.children()) {
if (std::string_view(node.name()) != "trophy")
continue;
int current_trophy_id = node.attribute("id").as_int(ORBIS_NP_TROPHY_INVALID_TROPHY_ID);
bool current_trophy_unlockstate = node.attribute("unlockstate").as_bool();
std::string_view current_trophy_type = node.attribute("ttype").value();
if (current_trophy_type == "P") {
platinum_node = node;
if (trophyId == current_trophy_id) {
return ORBIS_NP_TROPHY_ERROR_PLATINUM_CANNOT_UNLOCK;
}
}
if (node.attribute("pid").as_int(-1) != ORBIS_NP_TROPHY_INVALID_TROPHY_ID) {
num_trophies++;
if (current_trophy_unlockstate) {
num_trophies_unlocked++;
}
}
if (current_trophy_id == trophyId) {
trophy_found = true;
trophy_name = node.child("name").text().as_string();
trophy_type = current_trophy_type;
const std::string icon_file = fmt::format("TROP{:03d}.PNG", current_trophy_id);
trophy_icon_path = ctx.icons_dir / icon_file;
}
}
if (!trophy_found)
return ORBIS_NP_TROPHY_ERROR_INVALID_TROPHY_ID;
// Capture timestamps once so every file gets the exact same value.
const auto now_secs = std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::system_clock::now().time_since_epoch())
.count();
const u64 trophy_timestamp = static_cast<u64>(now_secs);
// Decide platinum.
bool unlock_platinum = false;
OrbisNpTrophyId platinum_id = ORBIS_NP_TROPHY_INVALID_TROPHY_ID;
u64 platinum_timestamp = 0;
const char* platinum_name = "";
std::filesystem::path platinum_icon_path;
if (!platinum_node.attribute("unlockstate").as_bool()) {
if ((num_trophies - 1) == num_trophies_unlocked) {
unlock_platinum = true;
platinum_id = platinum_node.attribute("id").as_int(ORBIS_NP_TROPHY_INVALID_TROPHY_ID);
platinum_timestamp = trophy_timestamp; // same second is fine
platinum_name = platinum_node.child("name").text().as_string();
const std::string plat_icon_file = fmt::format("TROP{:03d}.PNG", platinum_id);
platinum_icon_path = ctx.icons_dir / plat_icon_file;
*platinumId = platinum_id;
}
}
// Queue UI notifications (only once, using the primary XML's strings).
AddTrophyToQueue(trophy_icon_path, trophy_name, trophy_type);
if (unlock_platinum) {
AddTrophyToQueue(platinum_icon_path, platinum_name, "P");
}
ApplyUnlockToXmlFile(ctx.xml_save_file, trophyId, trophy_timestamp, unlock_platinum,
platinum_id, platinum_timestamp);
LOG_INFO(Lib_NpTrophy, "Trophy {} successfully saved.", trophyId);
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophyGroupArrayGetNum() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophyIntAbortHandle() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophyIntCheckNetSyncTitles() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophyIntCreateHandle() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophyIntDestroyHandle() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophyIntGetLocalTrophySummary() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophyIntGetProgress() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophyIntGetRunningTitle() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophyIntGetRunningTitles() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophyIntGetTrpIconByUri() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophyIntNetSyncTitle() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophyIntNetSyncTitles() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophyNumInfoGetTotal() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophySetInfoGetTrophyFlagArray() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophySetInfoGetTrophyNum() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophyShowTrophyList(OrbisNpTrophyContext context,
OrbisNpTrophyHandle handle) {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophySystemAbortHandle() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophySystemBuildGroupIconUri() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophySystemBuildNetTrophyIconUri() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophySystemBuildTitleIconUri() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophySystemBuildTrophyIconUri() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophySystemCheckNetSyncTitles() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophySystemCheckRecoveryRequired() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophySystemCloseStorage() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophySystemCreateContext() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophySystemCreateHandle() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophySystemDbgCtl() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophySystemDebugLockTrophy() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophySystemDebugUnlockTrophy() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophySystemDestroyContext() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophySystemDestroyHandle() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophySystemDestroyTrophyConfig() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophySystemGetDbgParam() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophySystemGetDbgParamInt() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophySystemGetGroupIcon() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophySystemGetLocalTrophySummary() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophySystemGetNextTitleFileEntryStatus() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophySystemGetProgress() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophySystemGetTitleFileStatus() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophySystemGetTitleIcon() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophySystemGetTitleSyncStatus() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophySystemGetTrophyConfig() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophySystemGetTrophyData() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophySystemGetTrophyGroupData() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophySystemGetTrophyIcon() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophySystemGetTrophyTitleData() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophySystemGetTrophyTitleIds() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophySystemGetUserFileInfo() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophySystemGetUserFileStatus() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophySystemIsServerAvailable() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophySystemNetSyncTitle() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophySystemNetSyncTitles() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophySystemOpenStorage() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophySystemPerformRecovery() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophySystemRemoveAll() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophySystemRemoveTitleData() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophySystemRemoveUserData() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophySystemSetDbgParam() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophySystemSetDbgParamInt() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophyAbortHandle(OrbisNpTrophyHandle handle) {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophyCaptureScreenshot() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophyConfigGetTrophyDetails() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophyConfigGetTrophyFlagArray() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophyConfigGetTrophyGroupArray() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophyConfigGetTrophyGroupDetails() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophyConfigGetTrophySetInfo() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophyConfigGetTrophySetInfoInGroup() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophyConfigGetTrophySetVersion() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophyConfigGetTrophyTitleDetails() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophyConfigHasGroupFeature() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI Func_149656DA81D41C59() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI Func_9F80071876FFA5F6() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI Func_F8EF6F5350A91990() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI Func_FA7A2DD770447552() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
void RegisterLib(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("aTnHs7W-9Uk", "libSceNpTrophy", 1, "libSceNpTrophy", sceNpTrophyAbortHandle);
LIB_FUNCTION("cqGkYAN-gRw", "libSceNpTrophy", 1, "libSceNpTrophy",
sceNpTrophyCaptureScreenshot);
LIB_FUNCTION("lhE4XS9OJXs", "libSceNpTrophy", 1, "libSceNpTrophy",
sceNpTrophyConfigGetTrophyDetails);
LIB_FUNCTION("qJ3IvrOoXg0", "libSceNpTrophy", 1, "libSceNpTrophy",
sceNpTrophyConfigGetTrophyFlagArray);
LIB_FUNCTION("zDjF2G+6tI0", "libSceNpTrophy", 1, "libSceNpTrophy",
sceNpTrophyConfigGetTrophyGroupArray);
LIB_FUNCTION("7Kh86vJqtxw", "libSceNpTrophy", 1, "libSceNpTrophy",
sceNpTrophyConfigGetTrophyGroupDetails);
LIB_FUNCTION("ndLeNWExeZE", "libSceNpTrophy", 1, "libSceNpTrophy",
sceNpTrophyConfigGetTrophySetInfo);
LIB_FUNCTION("6EOfS5SDgoo", "libSceNpTrophy", 1, "libSceNpTrophy",
sceNpTrophyConfigGetTrophySetInfoInGroup);
LIB_FUNCTION("MW5ygoZqEBs", "libSceNpTrophy", 1, "libSceNpTrophy",
sceNpTrophyConfigGetTrophySetVersion);
LIB_FUNCTION("3tWKpNKn5+I", "libSceNpTrophy", 1, "libSceNpTrophy",
sceNpTrophyConfigGetTrophyTitleDetails);
LIB_FUNCTION("iqYfxC12sak", "libSceNpTrophy", 1, "libSceNpTrophy",
sceNpTrophyConfigHasGroupFeature);
LIB_FUNCTION("XbkjbobZlCY", "libSceNpTrophy", 1, "libSceNpTrophy", sceNpTrophyCreateContext);
LIB_FUNCTION("q7U6tEAQf7c", "libSceNpTrophy", 1, "libSceNpTrophy", sceNpTrophyCreateHandle);
LIB_FUNCTION("E1Wrwd07Lr8", "libSceNpTrophy", 1, "libSceNpTrophy", sceNpTrophyDestroyContext);
LIB_FUNCTION("GNcF4oidY0Y", "libSceNpTrophy", 1, "libSceNpTrophy", sceNpTrophyDestroyHandle);
LIB_FUNCTION("HLwz1fRIycA", "libSceNpTrophy", 1, "libSceNpTrophy", sceNpTrophyGetGameIcon);
LIB_FUNCTION("YYP3f2W09og", "libSceNpTrophy", 1, "libSceNpTrophy", sceNpTrophyGetGameInfo);
LIB_FUNCTION("w4uMPmErD4I", "libSceNpTrophy", 1, "libSceNpTrophy", sceNpTrophyGetGroupIcon);
LIB_FUNCTION("wTUwGfspKic", "libSceNpTrophy", 1, "libSceNpTrophy", sceNpTrophyGetGroupInfo);
LIB_FUNCTION("eBL+l6HG9xk", "libSceNpTrophy", 1, "libSceNpTrophy", sceNpTrophyGetTrophyIcon);
LIB_FUNCTION("qqUVGDgQBm0", "libSceNpTrophy", 1, "libSceNpTrophy", sceNpTrophyGetTrophyInfo);
LIB_FUNCTION("LHuSmO3SLd8", "libSceNpTrophy", 1, "libSceNpTrophy",
sceNpTrophyGetTrophyUnlockState);
LIB_FUNCTION("Ht6MNTl-je4", "libSceNpTrophy", 1, "libSceNpTrophy", sceNpTrophyGroupArrayGetNum);
LIB_FUNCTION("u9plkqa2e0k", "libSceNpTrophy", 1, "libSceNpTrophy", sceNpTrophyIntAbortHandle);
LIB_FUNCTION("pE5yhroy9m0", "libSceNpTrophy", 1, "libSceNpTrophy",
sceNpTrophyIntCheckNetSyncTitles);
LIB_FUNCTION("edPIOFpEAvU", "libSceNpTrophy", 1, "libSceNpTrophy", sceNpTrophyIntCreateHandle);
LIB_FUNCTION("DSh3EXpqAQ4", "libSceNpTrophy", 1, "libSceNpTrophy", sceNpTrophyIntDestroyHandle);
LIB_FUNCTION("sng98qULzPA", "libSceNpTrophy", 1, "libSceNpTrophy",
sceNpTrophyIntGetLocalTrophySummary);
LIB_FUNCTION("t3CQzag7-zs", "libSceNpTrophy", 1, "libSceNpTrophy", sceNpTrophyIntGetProgress);
LIB_FUNCTION("jF-mCgGuvbQ", "libSceNpTrophy", 1, "libSceNpTrophy",
sceNpTrophyIntGetRunningTitle);
LIB_FUNCTION("PeAyBjC5kp8", "libSceNpTrophy", 1, "libSceNpTrophy",
sceNpTrophyIntGetRunningTitles);
LIB_FUNCTION("PEo09Dkqv0o", "libSceNpTrophy", 1, "libSceNpTrophy",
sceNpTrophyIntGetTrpIconByUri);
LIB_FUNCTION("kF9zjnlAzIA", "libSceNpTrophy", 1, "libSceNpTrophy", sceNpTrophyIntNetSyncTitle);
LIB_FUNCTION("UXiyfabxFNQ", "libSceNpTrophy", 1, "libSceNpTrophy", sceNpTrophyIntNetSyncTitles);
LIB_FUNCTION("hvdThnVvwdY", "libSceNpTrophy", 1, "libSceNpTrophy", sceNpTrophyNumInfoGetTotal);
LIB_FUNCTION("TJCAxto9SEU", "libSceNpTrophy", 1, "libSceNpTrophy", sceNpTrophyRegisterContext);
LIB_FUNCTION("ITUmvpBPaG0", "libSceNpTrophy", 1, "libSceNpTrophy",
sceNpTrophySetInfoGetTrophyFlagArray);
LIB_FUNCTION("BSoSgiMVHnY", "libSceNpTrophy", 1, "libSceNpTrophy",
sceNpTrophySetInfoGetTrophyNum);
LIB_FUNCTION("d9jpdPz5f-8", "libSceNpTrophy", 1, "libSceNpTrophy", sceNpTrophyShowTrophyList);
LIB_FUNCTION("JzJdh-JLtu0", "libSceNpTrophy", 1, "libSceNpTrophy",
sceNpTrophySystemAbortHandle);
LIB_FUNCTION("z8RCP536GOM", "libSceNpTrophy", 1, "libSceNpTrophy",
sceNpTrophySystemBuildGroupIconUri);
LIB_FUNCTION("Rd2FBOQE094", "libSceNpTrophy", 1, "libSceNpTrophy",
sceNpTrophySystemBuildNetTrophyIconUri);
LIB_FUNCTION("Q182x0rT75I", "libSceNpTrophy", 1, "libSceNpTrophy",
sceNpTrophySystemBuildTitleIconUri);
LIB_FUNCTION("lGnm5Kg-zpA", "libSceNpTrophy", 1, "libSceNpTrophy",
sceNpTrophySystemBuildTrophyIconUri);
LIB_FUNCTION("20wAMbXP-u0", "libSceNpTrophy", 1, "libSceNpTrophy",
sceNpTrophySystemCheckNetSyncTitles);
LIB_FUNCTION("sKGFFY59ksY", "libSceNpTrophy", 1, "libSceNpTrophy",
sceNpTrophySystemCheckRecoveryRequired);
LIB_FUNCTION("JMSapEtDH9Q", "libSceNpTrophy", 1, "libSceNpTrophy",
sceNpTrophySystemCloseStorage);
LIB_FUNCTION("dk27olS4CEE", "libSceNpTrophy", 1, "libSceNpTrophy",
sceNpTrophySystemCreateContext);
LIB_FUNCTION("cBzXEdzVzvs", "libSceNpTrophy", 1, "libSceNpTrophy",
sceNpTrophySystemCreateHandle);
LIB_FUNCTION("8aLlLHKP+No", "libSceNpTrophy", 1, "libSceNpTrophy", sceNpTrophySystemDbgCtl);
LIB_FUNCTION("NobVwD8qcQY", "libSceNpTrophy", 1, "libSceNpTrophy",
sceNpTrophySystemDebugLockTrophy);
LIB_FUNCTION("yXJlgXljItk", "libSceNpTrophy", 1, "libSceNpTrophy",
sceNpTrophySystemDebugUnlockTrophy);
LIB_FUNCTION("U0TOSinfuvw", "libSceNpTrophy", 1, "libSceNpTrophy",
sceNpTrophySystemDestroyContext);
LIB_FUNCTION("-LC9hudmD+Y", "libSceNpTrophy", 1, "libSceNpTrophy",
sceNpTrophySystemDestroyHandle);
LIB_FUNCTION("q6eAMucXIEM", "libSceNpTrophy", 1, "libSceNpTrophy",
sceNpTrophySystemDestroyTrophyConfig);
LIB_FUNCTION("WdCUUJLQodM", "libSceNpTrophy", 1, "libSceNpTrophy",
sceNpTrophySystemGetDbgParam);
LIB_FUNCTION("4QYFwC7tn4U", "libSceNpTrophy", 1, "libSceNpTrophy",
sceNpTrophySystemGetDbgParamInt);
LIB_FUNCTION("OcllHFFcQkI", "libSceNpTrophy", 1, "libSceNpTrophy",
sceNpTrophySystemGetGroupIcon);
LIB_FUNCTION("tQ3tXfVZreU", "libSceNpTrophy", 1, "libSceNpTrophy",
sceNpTrophySystemGetLocalTrophySummary);
LIB_FUNCTION("g0dxBNTspC0", "libSceNpTrophy", 1, "libSceNpTrophy",
sceNpTrophySystemGetNextTitleFileEntryStatus);
LIB_FUNCTION("sJSDnJRJHhI", "libSceNpTrophy", 1, "libSceNpTrophy",
sceNpTrophySystemGetProgress);
LIB_FUNCTION("X47s4AamPGg", "libSceNpTrophy", 1, "libSceNpTrophy",
sceNpTrophySystemGetTitleFileStatus);
LIB_FUNCTION("7WPj4KCF3D8", "libSceNpTrophy", 1, "libSceNpTrophy",
sceNpTrophySystemGetTitleIcon);
LIB_FUNCTION("pzL+aAk0tQA", "libSceNpTrophy", 1, "libSceNpTrophy",
sceNpTrophySystemGetTitleSyncStatus);
LIB_FUNCTION("Ro4sI9xgYl4", "libSceNpTrophy", 1, "libSceNpTrophy",
sceNpTrophySystemGetTrophyConfig);
LIB_FUNCTION("7+OR1TU5QOA", "libSceNpTrophy", 1, "libSceNpTrophy",
sceNpTrophySystemGetTrophyData);
LIB_FUNCTION("aXhvf2OmbiE", "libSceNpTrophy", 1, "libSceNpTrophy",
sceNpTrophySystemGetTrophyGroupData);
LIB_FUNCTION("Rkt0bVyaa4Y", "libSceNpTrophy", 1, "libSceNpTrophy",
sceNpTrophySystemGetTrophyIcon);
LIB_FUNCTION("nXr5Rho8Bqk", "libSceNpTrophy", 1, "libSceNpTrophy",
sceNpTrophySystemGetTrophyTitleData);
LIB_FUNCTION("eV1rtLr+eys", "libSceNpTrophy", 1, "libSceNpTrophy",
sceNpTrophySystemGetTrophyTitleIds);
LIB_FUNCTION("SsGLKTfWfm0", "libSceNpTrophy", 1, "libSceNpTrophy",
sceNpTrophySystemGetUserFileInfo);
LIB_FUNCTION("XqLLsvl48kA", "libSceNpTrophy", 1, "libSceNpTrophy",
sceNpTrophySystemGetUserFileStatus);
LIB_FUNCTION("-qjm2fFE64M", "libSceNpTrophy", 1, "libSceNpTrophy",
sceNpTrophySystemIsServerAvailable);
LIB_FUNCTION("50BvYYzPTsY", "libSceNpTrophy", 1, "libSceNpTrophy",
sceNpTrophySystemNetSyncTitle);
LIB_FUNCTION("yDJ-r-8f4S4", "libSceNpTrophy", 1, "libSceNpTrophy",
sceNpTrophySystemNetSyncTitles);
LIB_FUNCTION("mWtsnHY8JZg", "libSceNpTrophy", 1, "libSceNpTrophy",
sceNpTrophySystemOpenStorage);
LIB_FUNCTION("tAxnXpzDgFw", "libSceNpTrophy", 1, "libSceNpTrophy",
sceNpTrophySystemPerformRecovery);
LIB_FUNCTION("tV18n8OcheI", "libSceNpTrophy", 1, "libSceNpTrophy", sceNpTrophySystemRemoveAll);
LIB_FUNCTION("kV4DP0OTMNo", "libSceNpTrophy", 1, "libSceNpTrophy",
sceNpTrophySystemRemoveTitleData);
LIB_FUNCTION("lZSZoN8BstI", "libSceNpTrophy", 1, "libSceNpTrophy",
sceNpTrophySystemRemoveUserData);
LIB_FUNCTION("nytN-3-pdvI", "libSceNpTrophy", 1, "libSceNpTrophy",
sceNpTrophySystemSetDbgParam);
LIB_FUNCTION("JsRnDKRzvRw", "libSceNpTrophy", 1, "libSceNpTrophy",
sceNpTrophySystemSetDbgParamInt);
LIB_FUNCTION("28xmRUFao68", "libSceNpTrophy", 1, "libSceNpTrophy", sceNpTrophyUnlockTrophy);
LIB_FUNCTION("FJZW2oHUHFk", "libSceNpTrophy", 1, "libSceNpTrophy", Func_149656DA81D41C59);
LIB_FUNCTION("n4AHGHb-pfY", "libSceNpTrophy", 1, "libSceNpTrophy", Func_9F80071876FFA5F6);
LIB_FUNCTION("+O9vU1CpGZA", "libSceNpTrophy", 1, "libSceNpTrophy", Func_F8EF6F5350A91990);
LIB_FUNCTION("+not13BEdVI", "libSceNpTrophy", 1, "libSceNpTrophy", Func_FA7A2DD770447552);
};
} // namespace Libraries::Np::NpTrophy