trophy icon + platinum fixes (#1093)

* trophy icon + platinum fixes

cleaned up some parts too

* format

* implement turtles review

* use fs native where possible, clang format

* implement vinicius suggestions

* format

* final reviews

* mutex for trophy queue, remove unneeded field

* format
This commit is contained in:
CrazyBloo 2024-10-01 16:39:43 -04:00 committed by GitHub
parent f93b8c1e8d
commit 65f72372f0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 356 additions and 419 deletions

View file

@ -14,8 +14,6 @@
namespace Libraries::NpTrophy { namespace Libraries::NpTrophy {
static TrophyUI g_trophy_ui;
std::string game_serial; std::string game_serial;
static constexpr auto MaxTrophyHandles = 4u; static constexpr auto MaxTrophyHandles = 4u;
@ -223,6 +221,14 @@ int PS4_SYSV_ABI sceNpTrophyGetGameIcon(OrbisNpTrophyContext context, OrbisNpTro
return ORBIS_OK; 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, int PS4_SYSV_ABI sceNpTrophyGetGameInfo(OrbisNpTrophyContext context, OrbisNpTrophyHandle handle,
OrbisNpTrophyGameDetails* details, OrbisNpTrophyGameDetails* details,
OrbisNpTrophyGameData* data) { OrbisNpTrophyGameData* data) {
@ -240,79 +246,66 @@ int PS4_SYSV_ABI sceNpTrophyGetGameInfo(OrbisNpTrophyContext context, OrbisNpTro
if (details->size != 0x4A0 || data->size != 0x20) if (details->size != 0x4A0 || data->size != 0x20)
return ORBIS_NP_TROPHY_ERROR_INVALID_ARGUMENT; return ORBIS_NP_TROPHY_ERROR_INVALID_ARGUMENT;
const auto trophyDir = const auto trophy_dir =
Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) / game_serial / "TrophyFiles"; Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) / game_serial / "TrophyFiles";
auto trophy_file = trophy_dir / "trophy00" / "Xml" / "TROP.XML";
pugi::xml_document doc; pugi::xml_document doc;
pugi::xml_parse_result result = pugi::xml_parse_result result = doc.load_file(trophy_file.native().c_str());
doc.load_file((trophyDir.string() + "/trophy00/Xml/TROP.XML").c_str());
if (result) { ASSERT_MSG(result, "Couldnt parse trophy XML : {}", result.description());
uint32_t numGroups = 0; GameTrophyInfo game_info{};
uint32_t numTrophies = 0;
uint32_t numTrophiesByRarity[5];
numTrophiesByRarity[1] = 0;
numTrophiesByRarity[2] = 0;
numTrophiesByRarity[3] = 0;
numTrophiesByRarity[4] = 0;
uint32_t unlockedTrophies = 0;
uint32_t unlockedTrophiesByRarity[5];
unlockedTrophiesByRarity[1] = 0;
unlockedTrophiesByRarity[2] = 0;
unlockedTrophiesByRarity[3] = 0;
unlockedTrophiesByRarity[4] = 0;
auto trophyconf = doc.child("trophyconf"); auto trophyconf = doc.child("trophyconf");
for (pugi::xml_node_iterator it = trophyconf.children().begin(); for (const pugi::xml_node& node : trophyconf.children()) {
it != trophyconf.children().end(); ++it) { std::string_view node_name = node.name();
if (std::string(it->name()) == "title-name") { if (node_name == "title-name") {
strncpy(details->title, it->text().as_string(), strncpy(details->title, node.text().as_string(), ORBIS_NP_TROPHY_GAME_TITLE_MAX_SIZE);
ORBIS_NP_TROPHY_GAME_TITLE_MAX_SIZE);
}
if (std::string(it->name()) == "title-detail") {
strncpy(details->description, it->text().as_string(),
ORBIS_NP_TROPHY_GAME_DESCR_MAX_SIZE);
}
if (std::string(it->name()) == "group")
numGroups++;
if (std::string(it->name()) == "trophy") {
std::string currentTrophyUnlockState = it->attribute("unlockstate").value();
std::string currentTrophyGrade = it->attribute("ttype").value();
numTrophies++;
if (!currentTrophyGrade.empty()) {
int trophyGrade = GetTrophyGradeFromChar(currentTrophyGrade.at(0));
numTrophiesByRarity[trophyGrade]++;
if (currentTrophyUnlockState == "unlocked") {
unlockedTrophies++;
unlockedTrophiesByRarity[trophyGrade]++;
}
}
}
} }
details->numGroups = numGroups; if (node_name == "title-detail") {
details->numTrophies = numTrophies; strncpy(details->description, node.text().as_string(),
details->numPlatinum = numTrophiesByRarity[ORBIS_NP_TROPHY_GRADE_PLATINUM]; ORBIS_NP_TROPHY_GAME_DESCR_MAX_SIZE);
details->numGold = numTrophiesByRarity[ORBIS_NP_TROPHY_GRADE_GOLD]; }
details->numSilver = numTrophiesByRarity[ORBIS_NP_TROPHY_GRADE_SILVER];
details->numBronze = numTrophiesByRarity[ORBIS_NP_TROPHY_GRADE_BRONZE];
data->unlockedTrophies = unlockedTrophies;
data->unlockedPlatinum = unlockedTrophiesByRarity[ORBIS_NP_TROPHY_GRADE_PLATINUM];
data->unlockedGold = unlockedTrophiesByRarity[ORBIS_NP_TROPHY_GRADE_GOLD];
data->unlockedSilver = unlockedTrophiesByRarity[ORBIS_NP_TROPHY_GRADE_SILVER];
data->unlockedBronze = unlockedTrophiesByRarity[ORBIS_NP_TROPHY_GRADE_BRONZE];
// maybe this should be 1 instead of 100? if (node_name == "group")
data->progressPercentage = 100; game_info.num_groups++;
} else if (node_name == "trophy") {
LOG_INFO(Lib_NpTrophy, "couldnt parse xml : {}", result.description()); 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];
// maybe this should be 1 instead of 100?
data->progress_percentage = 100;
return ORBIS_OK; return ORBIS_OK;
} }
@ -323,6 +316,13 @@ int PS4_SYSV_ABI sceNpTrophyGetGroupIcon(OrbisNpTrophyContext context, OrbisNpTr
return ORBIS_OK; 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, int PS4_SYSV_ABI sceNpTrophyGetGroupInfo(OrbisNpTrophyContext context, OrbisNpTrophyHandle handle,
OrbisNpTrophyGroupId groupId, OrbisNpTrophyGroupId groupId,
OrbisNpTrophyGroupDetails* details, OrbisNpTrophyGroupDetails* details,
@ -341,89 +341,75 @@ int PS4_SYSV_ABI sceNpTrophyGetGroupInfo(OrbisNpTrophyContext context, OrbisNpTr
if (details->size != 0x4A0 || data->size != 0x28) if (details->size != 0x4A0 || data->size != 0x28)
return ORBIS_NP_TROPHY_ERROR_INVALID_ARGUMENT; return ORBIS_NP_TROPHY_ERROR_INVALID_ARGUMENT;
const auto trophyDir = const auto trophy_dir =
Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) / game_serial / "TrophyFiles"; Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) / game_serial / "TrophyFiles";
auto trophy_file = trophy_dir / "trophy00" / "Xml" / "TROP.XML";
pugi::xml_document doc; pugi::xml_document doc;
pugi::xml_parse_result result = pugi::xml_parse_result result = doc.load_file(trophy_file.native().c_str());
doc.load_file((trophyDir.string() + "/trophy00/Xml/TROP.XML").c_str());
if (result) { ASSERT_MSG(result, "Couldnt parse trophy XML : {}", result.description());
uint32_t numGroups = 0; GroupTrophyInfo group_info{};
uint32_t numTrophies = 0;
uint32_t numTrophiesByRarity[5];
numTrophiesByRarity[1] = 0;
numTrophiesByRarity[2] = 0;
numTrophiesByRarity[3] = 0;
numTrophiesByRarity[4] = 0;
uint32_t unlockedTrophies = 0;
uint32_t unlockedTrophiesByRarity[5];
unlockedTrophiesByRarity[1] = 0;
unlockedTrophiesByRarity[2] = 0;
unlockedTrophiesByRarity[3] = 0;
unlockedTrophiesByRarity[4] = 0;
auto trophyconf = doc.child("trophyconf"); auto trophyconf = doc.child("trophyconf");
for (pugi::xml_node_iterator it = trophyconf.children().begin(); for (const pugi::xml_node& node : trophyconf.children()) {
it != trophyconf.children().end(); ++it) { std::string_view node_name = node.name();
if (std::string(it->name()) == "group") { if (node_name == "group") {
numGroups++; int current_group_id = node.attribute("id").as_int(ORBIS_NP_TROPHY_INVALID_GROUP_ID);
std::string currentGroupId = it->attribute("id").value(); if (current_group_id != ORBIS_NP_TROPHY_INVALID_GROUP_ID) {
if (!currentGroupId.empty()) { if (current_group_id == groupId) {
if (std::stoi(currentGroupId) == groupId) { std::string_view current_group_name = node.child("name").text().as_string();
std::string currentGroupName = it->child("name").text().as_string(); std::string_view current_group_description =
std::string currentGroupDescription = node.child("detail").text().as_string();
it->child("detail").text().as_string();
strncpy(details->title, currentGroupName.c_str(), strncpy(details->title, current_group_name.data(),
ORBIS_NP_TROPHY_GROUP_TITLE_MAX_SIZE); ORBIS_NP_TROPHY_GROUP_TITLE_MAX_SIZE);
strncpy(details->description, currentGroupDescription.c_str(), strncpy(details->description, current_group_description.data(),
ORBIS_NP_TROPHY_GAME_DESCR_MAX_SIZE); ORBIS_NP_TROPHY_GAME_DESCR_MAX_SIZE);
}
}
}
data->groupId = groupId;
if (std::string(it->name()) == "trophy") {
std::string currentTrophyUnlockState = it->attribute("unlockstate").value();
std::string currentTrophyGrade = it->attribute("ttype").value();
std::string currentTrophyGroupID = it->attribute("gid").value();
if (!currentTrophyGroupID.empty()) {
if (std::stoi(currentTrophyGroupID) == groupId) {
numTrophies++;
if (!currentTrophyGrade.empty()) {
int trophyGrade = GetTrophyGradeFromChar(currentTrophyGrade.at(0));
numTrophiesByRarity[trophyGrade]++;
if (currentTrophyUnlockState == "unlocked") {
unlockedTrophies++;
unlockedTrophiesByRarity[trophyGrade]++;
}
}
}
} }
} }
} }
details->numTrophies = numTrophies; details->group_id = groupId;
details->numPlatinum = numTrophiesByRarity[ORBIS_NP_TROPHY_GRADE_PLATINUM]; data->group_id = groupId;
details->numGold = numTrophiesByRarity[ORBIS_NP_TROPHY_GRADE_GOLD];
details->numSilver = numTrophiesByRarity[ORBIS_NP_TROPHY_GRADE_SILVER];
details->numBronze = numTrophiesByRarity[ORBIS_NP_TROPHY_GRADE_BRONZE];
data->unlockedTrophies = unlockedTrophies;
data->unlockedPlatinum = unlockedTrophiesByRarity[ORBIS_NP_TROPHY_GRADE_PLATINUM];
data->unlockedGold = unlockedTrophiesByRarity[ORBIS_NP_TROPHY_GRADE_GOLD];
data->unlockedSilver = unlockedTrophiesByRarity[ORBIS_NP_TROPHY_GRADE_SILVER];
data->unlockedBronze = unlockedTrophiesByRarity[ORBIS_NP_TROPHY_GRADE_BRONZE];
// maybe this should be 1 instead of 100? if (node_name == "trophy") {
data->progressPercentage = 100; 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);
} else if (current_trophy_grade.empty()) {
LOG_INFO(Lib_NpTrophy, "couldnt parse xml : {}", result.description()); 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];
// maybe this should be 1 instead of 100?
data->progress_percentage = 100;
return ORBIS_OK; return ORBIS_OK;
} }
@ -454,87 +440,48 @@ int PS4_SYSV_ABI sceNpTrophyGetTrophyInfo(OrbisNpTrophyContext context, OrbisNpT
if (details->size != 0x498 || data->size != 0x18) if (details->size != 0x498 || data->size != 0x18)
return ORBIS_NP_TROPHY_ERROR_INVALID_ARGUMENT; return ORBIS_NP_TROPHY_ERROR_INVALID_ARGUMENT;
const auto trophyDir = const auto trophy_dir =
Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) / game_serial / "TrophyFiles"; Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) / game_serial / "TrophyFiles";
auto trophy_file = trophy_dir / "trophy00" / "Xml" / "TROP.XML";
pugi::xml_document doc; pugi::xml_document doc;
pugi::xml_parse_result result = pugi::xml_parse_result result = doc.load_file(trophy_file.native().c_str());
doc.load_file((trophyDir.string() + "/trophy00/Xml/TROP.XML").c_str());
if (result) { ASSERT_MSG(result, "Couldnt parse trophy XML : {}", result.description());
auto trophyconf = doc.child("trophyconf");
for (pugi::xml_node_iterator it = trophyconf.children().begin();
it != trophyconf.children().end(); ++it) {
if (std::string(it->name()) == "trophy") { auto trophyconf = doc.child("trophyconf");
std::string currentTrophyId = it->attribute("id").value();
if (std::stoi(currentTrophyId) == trophyId) {
std::string currentTrophyUnlockState = it->attribute("unlockstate").value();
std::string currentTrophyTimestamp = it->attribute("timestamp").value();
std::string currentTrophyGrade = it->attribute("ttype").value();
std::string currentTrophyGroupID = it->attribute("gid").value();
std::string currentTrophyHidden = it->attribute("hidden").value();
std::string currentTrophyName = it->child("name").text().as_string();
std::string currentTrophyDescription = it->child("detail").text().as_string();
if (currentTrophyUnlockState == "unlocked") { for (const pugi::xml_node& node : trophyconf.children()) {
details->trophyId = trophyId; std::string_view node_name = node.name();
if (currentTrophyGrade.empty()) {
details->trophyGrade = ORBIS_NP_TROPHY_GRADE_UNKNOWN;
} else {
details->trophyGrade = GetTrophyGradeFromChar(currentTrophyGrade.at(0));
}
if (currentTrophyGroupID.empty()) {
details->groupId = ORBIS_NP_TROPHY_BASE_GAME_GROUP_ID;
} else {
details->groupId = std::stoi(currentTrophyGroupID);
}
if (currentTrophyHidden == "yes") {
details->hidden = true;
} else {
details->hidden = false;
}
strncpy(details->name, currentTrophyName.c_str(), if (node_name == "trophy") {
ORBIS_NP_TROPHY_NAME_MAX_SIZE); int current_trophy_id = node.attribute("id").as_int(ORBIS_NP_TROPHY_INVALID_TROPHY_ID);
strncpy(details->description, currentTrophyDescription.c_str(), if (current_trophy_id == trophyId) {
ORBIS_NP_TROPHY_DESCR_MAX_SIZE); bool current_trophy_unlockstate = node.attribute("unlockstate").as_bool();
std::string_view current_trophy_grade = node.attribute("ttype").value();
std::string_view current_trophy_name = node.child("name").text().as_string();
std::string_view current_trophy_description =
node.child("detail").text().as_string();
data->trophyId = trophyId; uint64_t current_trophy_timestamp = node.attribute("timestamp").as_ullong();
data->unlocked = true; int current_trophy_groupid = node.attribute("gid").as_int(-1);
data->timestamp.tick = std::stoull(currentTrophyTimestamp); bool current_trophy_hidden = node.attribute("hidden").as_bool();
} else {
details->trophyId = trophyId;
if (currentTrophyGrade.empty()) {
details->trophyGrade = ORBIS_NP_TROPHY_GRADE_UNKNOWN;
} else {
details->trophyGrade = GetTrophyGradeFromChar(currentTrophyGrade.at(0));
}
if (currentTrophyGroupID.empty()) {
details->groupId = ORBIS_NP_TROPHY_BASE_GAME_GROUP_ID;
} else {
details->groupId = std::stoi(currentTrophyGroupID);
}
if (currentTrophyHidden == "yes") {
details->hidden = true;
} else {
details->hidden = false;
}
strncpy(details->name, currentTrophyName.c_str(), details->trophy_id = trophyId;
ORBIS_NP_TROPHY_NAME_MAX_SIZE); details->trophy_grade = GetTrophyGradeFromChar(current_trophy_grade.at(0));
strncpy(details->description, currentTrophyDescription.c_str(), details->group_id = current_trophy_groupid;
ORBIS_NP_TROPHY_DESCR_MAX_SIZE); details->hidden = current_trophy_hidden;
data->trophyId = trophyId; strncpy(details->name, current_trophy_name.data(), ORBIS_NP_TROPHY_NAME_MAX_SIZE);
data->unlocked = false; strncpy(details->description, current_trophy_description.data(),
data->timestamp.tick = 0; ORBIS_NP_TROPHY_DESCR_MAX_SIZE);
}
} data->trophy_id = trophyId;
data->unlocked = current_trophy_unlockstate;
data->timestamp.tick = current_trophy_timestamp;
} }
} }
} else }
LOG_INFO(Lib_NpTrophy, "couldnt parse xml : {}", result.description());
return ORBIS_OK; return ORBIS_OK;
} }
@ -555,35 +502,33 @@ s32 PS4_SYSV_ABI sceNpTrophyGetTrophyUnlockState(OrbisNpTrophyContext context,
ORBIS_NP_TROPHY_FLAG_ZERO(flags); ORBIS_NP_TROPHY_FLAG_ZERO(flags);
const auto trophyDir = const auto trophy_dir =
Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) / game_serial / "TrophyFiles"; Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) / game_serial / "TrophyFiles";
auto trophy_file = trophyDir / "trophy00" / "Xml" / "TROP.XML"; auto trophy_file = trophy_dir / "trophy00" / "Xml" / "TROP.XML";
pugi::xml_document doc; pugi::xml_document doc;
pugi::xml_parse_result result = doc.load_file(trophy_file.native().c_str()); pugi::xml_parse_result result = doc.load_file(trophy_file.native().c_str());
int numTrophies = 0; ASSERT_MSG(result, "Couldnt parse trophy XML : {}", result.description());
if (result) { int num_trophies = 0;
auto trophyconf = doc.child("trophyconf"); auto trophyconf = doc.child("trophyconf");
for (pugi::xml_node_iterator it = trophyconf.children().begin();
it != trophyconf.children().end(); ++it) {
std::string currentTrophyId = it->attribute("id").value(); for (const pugi::xml_node& node : trophyconf.children()) {
std::string currentTrophyUnlockState = it->attribute("unlockstate").value(); 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 (std::string(it->name()) == "trophy") { if (node_name == "trophy") {
numTrophies++; num_trophies++;
}
if (currentTrophyUnlockState == "unlocked") {
ORBIS_NP_TROPHY_FLAG_SET(std::stoi(currentTrophyId), flags);
}
} }
} else
LOG_INFO(Lib_NpTrophy, "couldnt parse xml : {}", result.description());
*count = numTrophies; if (current_trophy_unlockstate) {
ORBIS_NP_TROPHY_FLAG_SET(current_trophy_id, flags);
}
}
*count = num_trophies;
return ORBIS_OK; return ORBIS_OK;
} }
@ -912,148 +857,116 @@ int PS4_SYSV_ABI sceNpTrophyUnlockTrophy(OrbisNpTrophyContext context, OrbisNpTr
if (platinumId == nullptr) if (platinumId == nullptr)
return ORBIS_NP_TROPHY_ERROR_INVALID_ARGUMENT; return ORBIS_NP_TROPHY_ERROR_INVALID_ARGUMENT;
const auto trophyDir = const auto trophy_dir =
Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) / game_serial / "TrophyFiles"; Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) / game_serial / "TrophyFiles";
auto trophy_file = trophy_dir / "trophy00" / "Xml" / "TROP.XML";
pugi::xml_document doc; pugi::xml_document doc;
pugi::xml_parse_result result = pugi::xml_parse_result result = doc.load_file(trophy_file.native().c_str());
doc.load_file((trophyDir.string() + "/trophy00/Xml/TROP.XML").c_str());
ASSERT_MSG(result, "Couldnt parse trophy XML : {}", result.description());
*platinumId = ORBIS_NP_TROPHY_INVALID_TROPHY_ID; *platinumId = ORBIS_NP_TROPHY_INVALID_TROPHY_ID;
int numTrophies = 0; int num_trophies = 0;
int numTrophiesUnlocked = 0; int num_trophies_unlocked = 0;
pugi::xml_node platinum_node;
pugi::xml_node_iterator platinumIt; auto trophyconf = doc.child("trophyconf");
int platinumTrophyGroup = -1;
if (result) { for (pugi::xml_node& node : trophyconf.children()) {
auto trophyconf = doc.child("trophyconf"); int current_trophy_id = node.attribute("id").as_int(ORBIS_NP_TROPHY_INVALID_TROPHY_ID);
for (pugi::xml_node_iterator it = trophyconf.children().begin(); bool current_trophy_unlockstate = node.attribute("unlockstate").as_bool();
it != trophyconf.children().end(); ++it) { const char* current_trophy_name = node.child("name").text().as_string();
std::string_view current_trophy_description = node.child("detail").text().as_string();
std::string_view current_trophy_type = node.attribute("ttype").value();
std::string currentTrophyId = it->attribute("id").value(); if (current_trophy_type == "P") {
std::string currentTrophyName = it->child("name").text().as_string(); platinum_node = node;
std::string currentTrophyDescription = it->child("detail").text().as_string(); if (trophyId == current_trophy_id) {
std::string currentTrophyType = it->attribute("ttype").value(); return ORBIS_NP_TROPHY_ERROR_PLATINUM_CANNOT_UNLOCK;
std::string currentTrophyUnlockState = it->attribute("unlockstate").value(); }
}
if (currentTrophyType == "P") { if (std::string_view(node.name()) == "trophy") {
platinumIt = it; if (node.attribute("pid").as_int(-1) != ORBIS_NP_TROPHY_INVALID_TROPHY_ID) {
num_trophies++;
if (std::string(platinumIt->attribute("gid").value()).empty()) { if (current_trophy_unlockstate) {
platinumTrophyGroup = -1; num_trophies_unlocked++;
} else {
platinumTrophyGroup =
std::stoi(std::string(platinumIt->attribute("gid").value()));
}
if (trophyId == std::stoi(currentTrophyId)) {
return ORBIS_NP_TROPHY_ERROR_PLATINUM_CANNOT_UNLOCK;
} }
} }
if (std::string(it->name()) == "trophy") { if (current_trophy_id == trophyId) {
if (platinumTrophyGroup == -1) { if (current_trophy_unlockstate) {
if (std::string(it->attribute("gid").value()).empty()) { LOG_INFO(Lib_NpTrophy, "Trophy already unlocked");
numTrophies++; return ORBIS_NP_TROPHY_ERROR_TROPHY_ALREADY_UNLOCKED;
if (currentTrophyUnlockState == "unlocked") {
numTrophiesUnlocked++;
}
}
} else { } else {
if (!std::string(it->attribute("gid").value()).empty()) { if (node.attribute("unlockstate").empty()) {
if (std::stoi(std::string(it->attribute("gid").value())) == node.append_attribute("unlockstate") = "true";
platinumTrophyGroup) {
numTrophies++;
if (currentTrophyUnlockState == "unlocked") {
numTrophiesUnlocked++;
}
}
}
}
if (std::stoi(currentTrophyId) == trophyId) {
LOG_INFO(Lib_NpTrophy, "Found trophy to unlock {} : {}",
it->child("name").text().as_string(),
it->child("detail").text().as_string());
if (currentTrophyUnlockState == "unlocked") {
LOG_INFO(Lib_NpTrophy, "Trophy already unlocked");
return ORBIS_NP_TROPHY_ERROR_TROPHY_ALREADY_UNLOCKED;
} else { } else {
if (std::string(it->attribute("unlockstate").value()).empty()) { node.attribute("unlockstate").set_value("true");
it->append_attribute("unlockstate") = "unlocked";
} else {
it->attribute("unlockstate").set_value("unlocked");
}
Rtc::OrbisRtcTick trophyTimestamp;
Rtc::sceRtcGetCurrentTick(&trophyTimestamp);
if (std::string(it->attribute("timestamp").value()).empty()) {
it->append_attribute("timestamp") =
std::to_string(trophyTimestamp.tick).c_str();
} else {
it->attribute("timestamp")
.set_value(std::to_string(trophyTimestamp.tick).c_str());
}
g_trophy_ui.AddTrophyToQueue(trophyId, currentTrophyName);
} }
Rtc::OrbisRtcTick trophyTimestamp;
Rtc::sceRtcGetCurrentTick(&trophyTimestamp);
if (node.attribute("timestamp").empty()) {
node.append_attribute("timestamp") =
std::to_string(trophyTimestamp.tick).c_str();
} else {
node.attribute("timestamp")
.set_value(std::to_string(trophyTimestamp.tick).c_str());
}
std::string trophy_icon_file = "TROP";
trophy_icon_file.append(node.attribute("id").value());
trophy_icon_file.append(".PNG");
std::filesystem::path current_icon_path =
trophy_dir / "trophy00" / "Icons" / trophy_icon_file;
AddTrophyToQueue(current_icon_path, current_trophy_name);
} }
} }
} }
}
if (std::string(platinumIt->attribute("unlockstate").value()).empty()) { if (!platinum_node.attribute("unlockstate").as_bool()) {
if ((numTrophies - 2) == numTrophiesUnlocked) { if ((num_trophies - 1) == num_trophies_unlocked) {
if (platinum_node.attribute("unlockstate").empty()) {
platinumIt->append_attribute("unlockstate") = "unlocked"; platinum_node.append_attribute("unlockstate") = "true";
} else {
Rtc::OrbisRtcTick trophyTimestamp; platinum_node.attribute("unlockstate").set_value("true");
Rtc::sceRtcGetCurrentTick(&trophyTimestamp);
if (std::string(platinumIt->attribute("timestamp").value()).empty()) {
platinumIt->append_attribute("timestamp") =
std::to_string(trophyTimestamp.tick).c_str();
} else {
platinumIt->attribute("timestamp")
.set_value(std::to_string(trophyTimestamp.tick).c_str());
}
std::string platinumTrophyId = platinumIt->attribute("id").value();
std::string platinumTrophyName = platinumIt->child("name").text().as_string();
*platinumId = std::stoi(platinumTrophyId);
g_trophy_ui.AddTrophyToQueue(*platinumId, platinumTrophyName);
} }
} else if (std::string(platinumIt->attribute("unlockstate").value()) == "locked") {
if ((numTrophies - 2) == numTrophiesUnlocked) {
platinumIt->attribute("unlockstate").set_value("unlocked"); Rtc::OrbisRtcTick trophyTimestamp;
Rtc::sceRtcGetCurrentTick(&trophyTimestamp);
Rtc::OrbisRtcTick trophyTimestamp; if (platinum_node.attribute("timestamp").empty()) {
Rtc::sceRtcGetCurrentTick(&trophyTimestamp); platinum_node.append_attribute("timestamp") =
std::to_string(trophyTimestamp.tick).c_str();
if (std::string(platinumIt->attribute("timestamp").value()).empty()) { } else {
platinumIt->append_attribute("timestamp") = platinum_node.attribute("timestamp")
std::to_string(trophyTimestamp.tick).c_str(); .set_value(std::to_string(trophyTimestamp.tick).c_str());
} else {
platinumIt->attribute("timestamp")
.set_value(std::to_string(trophyTimestamp.tick).c_str());
}
std::string platinumTrophyId = platinumIt->attribute("id").value();
std::string platinumTrophyName = platinumIt->child("name").text().as_string();
*platinumId = std::stoi(platinumTrophyId);
g_trophy_ui.AddTrophyToQueue(*platinumId, platinumTrophyName);
} }
int platinum_trophy_id =
platinum_node.attribute("id").as_int(ORBIS_NP_TROPHY_INVALID_TROPHY_ID);
const char* platinum_trophy_name = platinum_node.child("name").text().as_string();
std::string platinum_icon_file = "TROP";
platinum_icon_file.append(platinum_node.attribute("id").value());
platinum_icon_file.append(".PNG");
std::filesystem::path platinum_icon_path =
trophy_dir / "trophy00" / "Icons" / platinum_icon_file;
*platinumId = platinum_trophy_id;
AddTrophyToQueue(platinum_icon_path, platinum_trophy_name);
} }
}
doc.save_file((trophyDir.string() + "/trophy00/Xml/TROP.XML").c_str()); doc.save_file((trophy_dir.string() + "/trophy00/Xml/TROP.XML").c_str());
} else
LOG_INFO(Lib_NpTrophy, "couldnt parse xml : {}", result.description());
return ORBIS_OK; return ORBIS_OK;
} }

View file

@ -47,7 +47,7 @@ bool ORBIS_NP_TROPHY_FLAG_ISSET(int32_t trophyId, OrbisNpTrophyFlagArray* p);
struct OrbisNpTrophyData { struct OrbisNpTrophyData {
size_t size; size_t size;
OrbisNpTrophyId trophyId; OrbisNpTrophyId trophy_id;
bool unlocked; bool unlocked;
uint8_t reserved[3]; uint8_t reserved[3];
Rtc::OrbisRtcTick timestamp; Rtc::OrbisRtcTick timestamp;
@ -66,9 +66,9 @@ constexpr int ORBIS_NP_TROPHY_INVALID_GROUP_ID = -2;
struct OrbisNpTrophyDetails { struct OrbisNpTrophyDetails {
size_t size; size_t size;
OrbisNpTrophyId trophyId; OrbisNpTrophyId trophy_id;
OrbisNpTrophyGrade trophyGrade; OrbisNpTrophyGrade trophy_grade;
OrbisNpTrophyGroupId groupId; OrbisNpTrophyGroupId group_id;
bool hidden; bool hidden;
uint8_t reserved[3]; uint8_t reserved[3];
char name[ORBIS_NP_TROPHY_NAME_MAX_SIZE]; char name[ORBIS_NP_TROPHY_NAME_MAX_SIZE];
@ -77,46 +77,46 @@ struct OrbisNpTrophyDetails {
struct OrbisNpTrophyGameData { struct OrbisNpTrophyGameData {
size_t size; size_t size;
uint32_t unlockedTrophies; uint32_t unlocked_trophies;
uint32_t unlockedPlatinum; uint32_t unlocked_platinum;
uint32_t unlockedGold; uint32_t unlocked_gold;
uint32_t unlockedSilver; uint32_t unlocked_silver;
uint32_t unlockedBronze; uint32_t unlocked_bronze;
uint32_t progressPercentage; uint32_t progress_percentage;
}; };
struct OrbisNpTrophyGameDetails { struct OrbisNpTrophyGameDetails {
size_t size; size_t size;
uint32_t numGroups; uint32_t num_groups;
uint32_t numTrophies; uint32_t num_trophies;
uint32_t numPlatinum; uint32_t num_platinum;
uint32_t numGold; uint32_t num_gold;
uint32_t numSilver; uint32_t num_silver;
uint32_t numBronze; uint32_t num_bronze;
char title[ORBIS_NP_TROPHY_GAME_TITLE_MAX_SIZE]; char title[ORBIS_NP_TROPHY_GAME_TITLE_MAX_SIZE];
char description[ORBIS_NP_TROPHY_GAME_DESCR_MAX_SIZE]; char description[ORBIS_NP_TROPHY_GAME_DESCR_MAX_SIZE];
}; };
struct OrbisNpTrophyGroupData { struct OrbisNpTrophyGroupData {
size_t size; size_t size;
OrbisNpTrophyGroupId groupId; OrbisNpTrophyGroupId group_id;
uint32_t unlockedTrophies; uint32_t unlocked_trophies;
uint32_t unlockedPlatinum; uint32_t unlocked_platinum;
uint32_t unlockedGold; uint32_t unlocked_gold;
uint32_t unlockedSilver; uint32_t unlocked_silver;
uint32_t unlockedBronze; uint32_t unlocked_bronze;
uint32_t progressPercentage; uint32_t progress_percentage;
uint8_t reserved[4]; uint8_t reserved[4];
}; };
struct OrbisNpTrophyGroupDetails { struct OrbisNpTrophyGroupDetails {
size_t size; size_t size;
OrbisNpTrophyGroupId groupId; OrbisNpTrophyGroupId group_id;
uint32_t numTrophies; uint32_t num_trophies;
uint32_t numPlatinum; uint32_t num_platinum;
uint32_t numGold; uint32_t num_gold;
uint32_t numSilver; uint32_t num_silver;
uint32_t numBronze; uint32_t num_bronze;
char title[ORBIS_NP_TROPHY_GROUP_TITLE_MAX_SIZE]; char title[ORBIS_NP_TROPHY_GROUP_TITLE_MAX_SIZE];
char description[ORBIS_NP_TROPHY_GROUP_DESCR_MAX_SIZE]; char description[ORBIS_NP_TROPHY_GROUP_DESCR_MAX_SIZE];
}; };

View file

@ -2,15 +2,27 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <chrono> #include <chrono>
#include <mutex>
#include <imgui.h> #include <imgui.h>
#include "common/assert.h" #include "common/assert.h"
#include "common/singleton.h"
#include "imgui/imgui_std.h" #include "imgui/imgui_std.h"
#include "trophy_ui.h" #include "trophy_ui.h"
using namespace ImGui; using namespace ImGui;
using namespace Libraries::NpTrophy; namespace Libraries::NpTrophy {
TrophyUI::TrophyUI() { std::optional<TrophyUI> current_trophy_ui;
std::queue<TrophyInfo> trophy_queue;
std::mutex queueMtx;
TrophyUI::TrophyUI(std::filesystem::path trophyIconPath, std::string trophyName)
: trophy_name(trophyName) {
if (std::filesystem::exists(trophyIconPath)) {
trophy_icon = RefCountedTexture::DecodePngFile(trophyIconPath);
} else {
LOG_ERROR(Lib_NpTrophy, "Couldnt load trophy icon at {}", trophyIconPath.string());
}
AddLayer(this); AddLayer(this);
} }
@ -18,57 +30,65 @@ TrophyUI::~TrophyUI() {
Finish(); Finish();
} }
void Libraries::NpTrophy::TrophyUI::AddTrophyToQueue(int trophyId, std::string trophyName) {
TrophyInfo newInfo;
newInfo.trophyId = trophyId;
newInfo.trophyName = trophyName;
trophyQueue.push_back(newInfo);
}
void TrophyUI::Finish() { void TrophyUI::Finish() {
RemoveLayer(this); RemoveLayer(this);
} }
bool displayingTrophy;
std::chrono::steady_clock::time_point trophyStartedTime;
void TrophyUI::Draw() { void TrophyUI::Draw() {
const auto& io = GetIO(); const auto& io = GetIO();
const ImVec2 window_size{ const ImVec2 window_size{
std::min(io.DisplaySize.x, 200.f), std::min(io.DisplaySize.x, 250.f),
std::min(io.DisplaySize.y, 75.f), std::min(io.DisplaySize.y, 70.f),
}; };
if (trophyQueue.size() != 0) { SetNextWindowSize(window_size);
if (!displayingTrophy) { SetNextWindowCollapsed(false);
displayingTrophy = true; SetNextWindowPos(ImVec2(io.DisplaySize.x - 250, 50));
trophyStartedTime = std::chrono::steady_clock::now(); KeepNavHighlight();
if (Begin("Trophy Window", nullptr,
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoSavedSettings |
ImGuiWindowFlags_NoInputs)) {
if (trophy_icon) {
Image(trophy_icon.GetTexture().im_id, ImVec2(50, 50));
ImGui::SameLine();
} else {
// placeholder
const auto pos = GetCursorScreenPos();
ImGui::GetWindowDrawList()->AddRectFilled(pos, pos + ImVec2{50.0f},
GetColorU32(ImVec4{0.7f}));
ImGui::Indent(60);
} }
TextWrapped("Trophy earned!\n%s", trophy_name.c_str());
}
End();
std::chrono::steady_clock::time_point timeNow = std::chrono::steady_clock::now(); trophy_timer -= io.DeltaTime;
std::chrono::seconds duration = if (trophy_timer <= 0) {
std::chrono::duration_cast<std::chrono::seconds>(timeNow - trophyStartedTime); queueMtx.lock();
if (!trophy_queue.empty()) {
if (duration.count() >= 5) { TrophyInfo next_trophy = trophy_queue.front();
trophyQueue.erase(trophyQueue.begin()); trophy_queue.pop();
displayingTrophy = false; current_trophy_ui.emplace(next_trophy.trophy_icon_path, next_trophy.trophy_name);
} } else {
current_trophy_ui.reset();
if (trophyQueue.size() != 0) {
SetNextWindowSize(window_size);
SetNextWindowCollapsed(false);
SetNextWindowPos(ImVec2(io.DisplaySize.x - 200, 50));
KeepNavHighlight();
TrophyInfo currentTrophyInfo = trophyQueue[0];
if (Begin("Trophy Window", nullptr,
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoSavedSettings |
ImGuiWindowFlags_NoInputs)) {
Text("Trophy earned!");
TextWrapped("%s", currentTrophyInfo.trophyName.c_str());
}
End();
} }
queueMtx.unlock();
} }
} }
void AddTrophyToQueue(std::filesystem::path trophyIconPath, std::string trophyName) {
queueMtx.lock();
if (current_trophy_ui.has_value()) {
TrophyInfo new_trophy;
new_trophy.trophy_icon_path = trophyIconPath;
new_trophy.trophy_name = trophyName;
trophy_queue.push(new_trophy);
} else {
current_trophy_ui.emplace(trophyIconPath, trophyName);
}
queueMtx.unlock();
}
} // namespace Libraries::NpTrophy

View file

@ -5,32 +5,36 @@
#include <string> #include <string>
#include <variant> #include <variant>
#include <vector> #include <queue>
#include "common/fixed_value.h" #include "common/fixed_value.h"
#include "common/types.h" #include "common/types.h"
#include "core/libraries/np_trophy/np_trophy.h" #include "core/libraries/np_trophy/np_trophy.h"
#include "imgui/imgui_layer.h" #include "imgui/imgui_layer.h"
#include "imgui/imgui_texture.h"
namespace Libraries::NpTrophy { namespace Libraries::NpTrophy {
struct TrophyInfo {
int trophyId = -1;
std::string trophyName;
};
class TrophyUI final : public ImGui::Layer { class TrophyUI final : public ImGui::Layer {
std::vector<TrophyInfo> trophyQueue;
public: public:
TrophyUI(); TrophyUI(std::filesystem::path trophyIconPath, std::string trophyName);
~TrophyUI() override; ~TrophyUI() override;
void AddTrophyToQueue(int trophyId, std::string trophyName);
void Finish(); void Finish();
void Draw() override; void Draw() override;
private:
std::string trophy_name;
float trophy_timer = 5.0f;
ImGui::RefCountedTexture trophy_icon;
}; };
struct TrophyInfo {
std::filesystem::path trophy_icon_path;
std::string trophy_name;
};
void AddTrophyToQueue(std::filesystem::path trophyIconPath, std::string trophyName);
}; // namespace Libraries::NpTrophy }; // namespace Libraries::NpTrophy

View file

@ -76,7 +76,7 @@ void TrophyViewer::PopulateTrophyWidget(QString title) {
trpType.append(reader.attributes().value("ttype").toString()); trpType.append(reader.attributes().value("ttype").toString());
trpPid.append(reader.attributes().value("pid").toString()); trpPid.append(reader.attributes().value("pid").toString());
if (reader.attributes().hasAttribute("unlockstate")) { if (reader.attributes().hasAttribute("unlockstate")) {
if (reader.attributes().value("unlockstate").toString() == "unlocked") { if (reader.attributes().value("unlockstate").toString() == "true") {
trpUnlocked.append("unlocked"); trpUnlocked.append("unlocked");
} else { } else {
trpUnlocked.append("locked"); trpUnlocked.append("locked");