From 65f72372f07cb835c0e842ddfc2c00a756a6cb09 Mon Sep 17 00:00:00 2001 From: CrazyBloo Date: Tue, 1 Oct 2024 16:39:43 -0400 Subject: [PATCH] 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 --- src/core/libraries/np_trophy/np_trophy.cpp | 587 +++++++++------------ src/core/libraries/np_trophy/np_trophy.h | 58 +- src/core/libraries/np_trophy/trophy_ui.cpp | 102 ++-- src/core/libraries/np_trophy/trophy_ui.h | 26 +- src/qt_gui/trophy_viewer.cpp | 2 +- 5 files changed, 356 insertions(+), 419 deletions(-) diff --git a/src/core/libraries/np_trophy/np_trophy.cpp b/src/core/libraries/np_trophy/np_trophy.cpp index 91fdeb99..0641a2c0 100644 --- a/src/core/libraries/np_trophy/np_trophy.cpp +++ b/src/core/libraries/np_trophy/np_trophy.cpp @@ -14,8 +14,6 @@ namespace Libraries::NpTrophy { -static TrophyUI g_trophy_ui; - std::string game_serial; static constexpr auto MaxTrophyHandles = 4u; @@ -223,6 +221,14 @@ int PS4_SYSV_ABI sceNpTrophyGetGameIcon(OrbisNpTrophyContext context, OrbisNpTro 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) { @@ -240,79 +246,66 @@ int PS4_SYSV_ABI sceNpTrophyGetGameInfo(OrbisNpTrophyContext context, OrbisNpTro if (details->size != 0x4A0 || data->size != 0x20) return ORBIS_NP_TROPHY_ERROR_INVALID_ARGUMENT; - const auto trophyDir = + const auto trophy_dir = 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_parse_result result = - doc.load_file((trophyDir.string() + "/trophy00/Xml/TROP.XML").c_str()); + pugi::xml_parse_result result = doc.load_file(trophy_file.native().c_str()); - if (result) { + ASSERT_MSG(result, "Couldnt parse trophy XML : {}", result.description()); - uint32_t numGroups = 0; - 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; + GameTrophyInfo game_info{}; - auto trophyconf = doc.child("trophyconf"); - for (pugi::xml_node_iterator it = trophyconf.children().begin(); - it != trophyconf.children().end(); ++it) { + auto trophyconf = doc.child("trophyconf"); + for (const pugi::xml_node& node : trophyconf.children()) { + std::string_view node_name = node.name(); - if (std::string(it->name()) == "title-name") { - strncpy(details->title, it->text().as_string(), - 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]++; - } - } - } + if (node_name == "title-name") { + strncpy(details->title, node.text().as_string(), ORBIS_NP_TROPHY_GAME_TITLE_MAX_SIZE); } - details->numGroups = numGroups; - details->numTrophies = numTrophies; - details->numPlatinum = numTrophiesByRarity[ORBIS_NP_TROPHY_GRADE_PLATINUM]; - 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]; + if (node_name == "title-detail") { + strncpy(details->description, node.text().as_string(), + ORBIS_NP_TROPHY_GAME_DESCR_MAX_SIZE); + } - // maybe this should be 1 instead of 100? - data->progressPercentage = 100; + if (node_name == "group") + game_info.num_groups++; - } else - LOG_INFO(Lib_NpTrophy, "couldnt parse xml : {}", result.description()); + 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]; + + // maybe this should be 1 instead of 100? + data->progress_percentage = 100; return ORBIS_OK; } @@ -323,6 +316,13 @@ int PS4_SYSV_ABI sceNpTrophyGetGroupIcon(OrbisNpTrophyContext context, OrbisNpTr 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, @@ -341,89 +341,75 @@ int PS4_SYSV_ABI sceNpTrophyGetGroupInfo(OrbisNpTrophyContext context, OrbisNpTr if (details->size != 0x4A0 || data->size != 0x28) return ORBIS_NP_TROPHY_ERROR_INVALID_ARGUMENT; - const auto trophyDir = + const auto trophy_dir = 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_parse_result result = - doc.load_file((trophyDir.string() + "/trophy00/Xml/TROP.XML").c_str()); + pugi::xml_parse_result result = doc.load_file(trophy_file.native().c_str()); - if (result) { + ASSERT_MSG(result, "Couldnt parse trophy XML : {}", result.description()); - uint32_t numGroups = 0; - 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; + GroupTrophyInfo group_info{}; - auto trophyconf = doc.child("trophyconf"); - for (pugi::xml_node_iterator it = trophyconf.children().begin(); - it != trophyconf.children().end(); ++it) { + auto trophyconf = doc.child("trophyconf"); + for (const pugi::xml_node& node : trophyconf.children()) { + std::string_view node_name = node.name(); - if (std::string(it->name()) == "group") { - numGroups++; - std::string currentGroupId = it->attribute("id").value(); - if (!currentGroupId.empty()) { - if (std::stoi(currentGroupId) == groupId) { - std::string currentGroupName = it->child("name").text().as_string(); - std::string currentGroupDescription = - it->child("detail").text().as_string(); + 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, currentGroupName.c_str(), - ORBIS_NP_TROPHY_GROUP_TITLE_MAX_SIZE); - strncpy(details->description, currentGroupDescription.c_str(), - 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]++; - } - } - } + 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->numTrophies = numTrophies; - details->numPlatinum = numTrophiesByRarity[ORBIS_NP_TROPHY_GRADE_PLATINUM]; - 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]; + details->group_id = groupId; + data->group_id = groupId; - // maybe this should be 1 instead of 100? - data->progressPercentage = 100; + 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); - } else - LOG_INFO(Lib_NpTrophy, "couldnt parse xml : {}", result.description()); + 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]; + + // maybe this should be 1 instead of 100? + data->progress_percentage = 100; return ORBIS_OK; } @@ -454,87 +440,48 @@ int PS4_SYSV_ABI sceNpTrophyGetTrophyInfo(OrbisNpTrophyContext context, OrbisNpT if (details->size != 0x498 || data->size != 0x18) return ORBIS_NP_TROPHY_ERROR_INVALID_ARGUMENT; - const auto trophyDir = + const auto trophy_dir = 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_parse_result result = - doc.load_file((trophyDir.string() + "/trophy00/Xml/TROP.XML").c_str()); + pugi::xml_parse_result result = doc.load_file(trophy_file.native().c_str()); - if (result) { - auto trophyconf = doc.child("trophyconf"); - for (pugi::xml_node_iterator it = trophyconf.children().begin(); - it != trophyconf.children().end(); ++it) { + ASSERT_MSG(result, "Couldnt parse trophy XML : {}", result.description()); - if (std::string(it->name()) == "trophy") { - 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(); + auto trophyconf = doc.child("trophyconf"); - if (currentTrophyUnlockState == "unlocked") { - 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; - } + for (const pugi::xml_node& node : trophyconf.children()) { + std::string_view node_name = node.name(); - strncpy(details->name, currentTrophyName.c_str(), - ORBIS_NP_TROPHY_NAME_MAX_SIZE); - strncpy(details->description, currentTrophyDescription.c_str(), - ORBIS_NP_TROPHY_DESCR_MAX_SIZE); + 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(); + 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; - data->unlocked = true; - data->timestamp.tick = std::stoull(currentTrophyTimestamp); - } 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; - } + 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(); - strncpy(details->name, currentTrophyName.c_str(), - ORBIS_NP_TROPHY_NAME_MAX_SIZE); - strncpy(details->description, currentTrophyDescription.c_str(), - ORBIS_NP_TROPHY_DESCR_MAX_SIZE); + 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->trophyId = trophyId; - data->unlocked = false; - data->timestamp.tick = 0; - } - } + 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); + + 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; } @@ -555,35 +502,33 @@ s32 PS4_SYSV_ABI sceNpTrophyGetTrophyUnlockState(OrbisNpTrophyContext context, ORBIS_NP_TROPHY_FLAG_ZERO(flags); - const auto trophyDir = + const auto trophy_dir = 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_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) { - auto trophyconf = doc.child("trophyconf"); - for (pugi::xml_node_iterator it = trophyconf.children().begin(); - it != trophyconf.children().end(); ++it) { + int num_trophies = 0; + auto trophyconf = doc.child("trophyconf"); - std::string currentTrophyId = it->attribute("id").value(); - std::string currentTrophyUnlockState = it->attribute("unlockstate").value(); + 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 (std::string(it->name()) == "trophy") { - numTrophies++; - } - - if (currentTrophyUnlockState == "unlocked") { - ORBIS_NP_TROPHY_FLAG_SET(std::stoi(currentTrophyId), flags); - } + if (node_name == "trophy") { + num_trophies++; } - } 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; } @@ -912,148 +857,116 @@ int PS4_SYSV_ABI sceNpTrophyUnlockTrophy(OrbisNpTrophyContext context, OrbisNpTr if (platinumId == nullptr) return ORBIS_NP_TROPHY_ERROR_INVALID_ARGUMENT; - const auto trophyDir = + const auto trophy_dir = 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_parse_result result = - doc.load_file((trophyDir.string() + "/trophy00/Xml/TROP.XML").c_str()); + pugi::xml_parse_result result = doc.load_file(trophy_file.native().c_str()); + + ASSERT_MSG(result, "Couldnt parse trophy XML : {}", result.description()); *platinumId = ORBIS_NP_TROPHY_INVALID_TROPHY_ID; - int numTrophies = 0; - int numTrophiesUnlocked = 0; + int num_trophies = 0; + int num_trophies_unlocked = 0; + pugi::xml_node platinum_node; - pugi::xml_node_iterator platinumIt; - int platinumTrophyGroup = -1; + auto trophyconf = doc.child("trophyconf"); - if (result) { - auto trophyconf = doc.child("trophyconf"); - for (pugi::xml_node_iterator it = trophyconf.children().begin(); - it != trophyconf.children().end(); ++it) { + for (pugi::xml_node& node : trophyconf.children()) { + int current_trophy_id = node.attribute("id").as_int(ORBIS_NP_TROPHY_INVALID_TROPHY_ID); + bool current_trophy_unlockstate = node.attribute("unlockstate").as_bool(); + 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(); - std::string currentTrophyName = it->child("name").text().as_string(); - std::string currentTrophyDescription = it->child("detail").text().as_string(); - std::string currentTrophyType = it->attribute("ttype").value(); - std::string currentTrophyUnlockState = it->attribute("unlockstate").value(); + if (current_trophy_type == "P") { + platinum_node = node; + if (trophyId == current_trophy_id) { + return ORBIS_NP_TROPHY_ERROR_PLATINUM_CANNOT_UNLOCK; + } + } - if (currentTrophyType == "P") { - platinumIt = it; - - if (std::string(platinumIt->attribute("gid").value()).empty()) { - platinumTrophyGroup = -1; - } 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_view(node.name()) == "trophy") { + if (node.attribute("pid").as_int(-1) != ORBIS_NP_TROPHY_INVALID_TROPHY_ID) { + num_trophies++; + if (current_trophy_unlockstate) { + num_trophies_unlocked++; } } - if (std::string(it->name()) == "trophy") { - if (platinumTrophyGroup == -1) { - if (std::string(it->attribute("gid").value()).empty()) { - numTrophies++; - if (currentTrophyUnlockState == "unlocked") { - numTrophiesUnlocked++; - } - } + if (current_trophy_id == trophyId) { + if (current_trophy_unlockstate) { + LOG_INFO(Lib_NpTrophy, "Trophy already unlocked"); + return ORBIS_NP_TROPHY_ERROR_TROPHY_ALREADY_UNLOCKED; } else { - if (!std::string(it->attribute("gid").value()).empty()) { - if (std::stoi(std::string(it->attribute("gid").value())) == - 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; + if (node.attribute("unlockstate").empty()) { + node.append_attribute("unlockstate") = "true"; } else { - if (std::string(it->attribute("unlockstate").value()).empty()) { - 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); + node.attribute("unlockstate").set_value("true"); } + + 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 ((numTrophies - 2) == numTrophiesUnlocked) { - - platinumIt->append_attribute("unlockstate") = "unlocked"; - - Rtc::OrbisRtcTick trophyTimestamp; - 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); + if (!platinum_node.attribute("unlockstate").as_bool()) { + if ((num_trophies - 1) == num_trophies_unlocked) { + if (platinum_node.attribute("unlockstate").empty()) { + platinum_node.append_attribute("unlockstate") = "true"; + } else { + platinum_node.attribute("unlockstate").set_value("true"); } - } 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; - 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); + if (platinum_node.attribute("timestamp").empty()) { + platinum_node.append_attribute("timestamp") = + std::to_string(trophyTimestamp.tick).c_str(); + } else { + platinum_node.attribute("timestamp") + .set_value(std::to_string(trophyTimestamp.tick).c_str()); } + + 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()); - - } else - LOG_INFO(Lib_NpTrophy, "couldnt parse xml : {}", result.description()); + doc.save_file((trophy_dir.string() + "/trophy00/Xml/TROP.XML").c_str()); return ORBIS_OK; } diff --git a/src/core/libraries/np_trophy/np_trophy.h b/src/core/libraries/np_trophy/np_trophy.h index ae08b296..ac13a9ab 100644 --- a/src/core/libraries/np_trophy/np_trophy.h +++ b/src/core/libraries/np_trophy/np_trophy.h @@ -47,7 +47,7 @@ bool ORBIS_NP_TROPHY_FLAG_ISSET(int32_t trophyId, OrbisNpTrophyFlagArray* p); struct OrbisNpTrophyData { size_t size; - OrbisNpTrophyId trophyId; + OrbisNpTrophyId trophy_id; bool unlocked; uint8_t reserved[3]; Rtc::OrbisRtcTick timestamp; @@ -66,9 +66,9 @@ constexpr int ORBIS_NP_TROPHY_INVALID_GROUP_ID = -2; struct OrbisNpTrophyDetails { size_t size; - OrbisNpTrophyId trophyId; - OrbisNpTrophyGrade trophyGrade; - OrbisNpTrophyGroupId groupId; + OrbisNpTrophyId trophy_id; + OrbisNpTrophyGrade trophy_grade; + OrbisNpTrophyGroupId group_id; bool hidden; uint8_t reserved[3]; char name[ORBIS_NP_TROPHY_NAME_MAX_SIZE]; @@ -77,46 +77,46 @@ struct OrbisNpTrophyDetails { struct OrbisNpTrophyGameData { size_t size; - uint32_t unlockedTrophies; - uint32_t unlockedPlatinum; - uint32_t unlockedGold; - uint32_t unlockedSilver; - uint32_t unlockedBronze; - uint32_t progressPercentage; + uint32_t unlocked_trophies; + uint32_t unlocked_platinum; + uint32_t unlocked_gold; + uint32_t unlocked_silver; + uint32_t unlocked_bronze; + uint32_t progress_percentage; }; struct OrbisNpTrophyGameDetails { size_t size; - uint32_t numGroups; - uint32_t numTrophies; - uint32_t numPlatinum; - uint32_t numGold; - uint32_t numSilver; - uint32_t numBronze; + uint32_t num_groups; + uint32_t num_trophies; + uint32_t num_platinum; + uint32_t num_gold; + uint32_t num_silver; + uint32_t num_bronze; char title[ORBIS_NP_TROPHY_GAME_TITLE_MAX_SIZE]; char description[ORBIS_NP_TROPHY_GAME_DESCR_MAX_SIZE]; }; struct OrbisNpTrophyGroupData { size_t size; - OrbisNpTrophyGroupId groupId; - uint32_t unlockedTrophies; - uint32_t unlockedPlatinum; - uint32_t unlockedGold; - uint32_t unlockedSilver; - uint32_t unlockedBronze; - uint32_t progressPercentage; + OrbisNpTrophyGroupId group_id; + uint32_t unlocked_trophies; + uint32_t unlocked_platinum; + uint32_t unlocked_gold; + uint32_t unlocked_silver; + uint32_t unlocked_bronze; + uint32_t progress_percentage; uint8_t reserved[4]; }; struct OrbisNpTrophyGroupDetails { size_t size; - OrbisNpTrophyGroupId groupId; - uint32_t numTrophies; - uint32_t numPlatinum; - uint32_t numGold; - uint32_t numSilver; - uint32_t numBronze; + OrbisNpTrophyGroupId group_id; + uint32_t num_trophies; + uint32_t num_platinum; + uint32_t num_gold; + uint32_t num_silver; + uint32_t num_bronze; char title[ORBIS_NP_TROPHY_GROUP_TITLE_MAX_SIZE]; char description[ORBIS_NP_TROPHY_GROUP_DESCR_MAX_SIZE]; }; diff --git a/src/core/libraries/np_trophy/trophy_ui.cpp b/src/core/libraries/np_trophy/trophy_ui.cpp index 8deaac25..740bd3a1 100644 --- a/src/core/libraries/np_trophy/trophy_ui.cpp +++ b/src/core/libraries/np_trophy/trophy_ui.cpp @@ -2,15 +2,27 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include #include #include "common/assert.h" +#include "common/singleton.h" #include "imgui/imgui_std.h" #include "trophy_ui.h" using namespace ImGui; -using namespace Libraries::NpTrophy; +namespace Libraries::NpTrophy { -TrophyUI::TrophyUI() { +std::optional current_trophy_ui; +std::queue 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); } @@ -18,57 +30,65 @@ TrophyUI::~TrophyUI() { 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() { RemoveLayer(this); } -bool displayingTrophy; -std::chrono::steady_clock::time_point trophyStartedTime; - void TrophyUI::Draw() { const auto& io = GetIO(); const ImVec2 window_size{ - std::min(io.DisplaySize.x, 200.f), - std::min(io.DisplaySize.y, 75.f), + std::min(io.DisplaySize.x, 250.f), + std::min(io.DisplaySize.y, 70.f), }; - if (trophyQueue.size() != 0) { - if (!displayingTrophy) { - displayingTrophy = true; - trophyStartedTime = std::chrono::steady_clock::now(); + SetNextWindowSize(window_size); + SetNextWindowCollapsed(false); + SetNextWindowPos(ImVec2(io.DisplaySize.x - 250, 50)); + 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(); - std::chrono::seconds duration = - std::chrono::duration_cast(timeNow - trophyStartedTime); - - if (duration.count() >= 5) { - trophyQueue.erase(trophyQueue.begin()); - displayingTrophy = false; - } - - 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(); + trophy_timer -= io.DeltaTime; + if (trophy_timer <= 0) { + queueMtx.lock(); + if (!trophy_queue.empty()) { + TrophyInfo next_trophy = trophy_queue.front(); + trophy_queue.pop(); + current_trophy_ui.emplace(next_trophy.trophy_icon_path, next_trophy.trophy_name); + } else { + current_trophy_ui.reset(); } + 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 \ No newline at end of file diff --git a/src/core/libraries/np_trophy/trophy_ui.h b/src/core/libraries/np_trophy/trophy_ui.h index 060d80de..4448c228 100644 --- a/src/core/libraries/np_trophy/trophy_ui.h +++ b/src/core/libraries/np_trophy/trophy_ui.h @@ -5,32 +5,36 @@ #include #include -#include +#include #include "common/fixed_value.h" #include "common/types.h" #include "core/libraries/np_trophy/np_trophy.h" #include "imgui/imgui_layer.h" +#include "imgui/imgui_texture.h" namespace Libraries::NpTrophy { -struct TrophyInfo { - int trophyId = -1; - std::string trophyName; -}; - class TrophyUI final : public ImGui::Layer { - std::vector trophyQueue; - public: - TrophyUI(); + TrophyUI(std::filesystem::path trophyIconPath, std::string trophyName); ~TrophyUI() override; - void AddTrophyToQueue(int trophyId, std::string trophyName); - void Finish(); 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 \ No newline at end of file diff --git a/src/qt_gui/trophy_viewer.cpp b/src/qt_gui/trophy_viewer.cpp index e4394c7e..a14da118 100644 --- a/src/qt_gui/trophy_viewer.cpp +++ b/src/qt_gui/trophy_viewer.cpp @@ -76,7 +76,7 @@ void TrophyViewer::PopulateTrophyWidget(QString title) { trpType.append(reader.attributes().value("ttype").toString()); trpPid.append(reader.attributes().value("pid").toString()); if (reader.attributes().hasAttribute("unlockstate")) { - if (reader.attributes().value("unlockstate").toString() == "unlocked") { + if (reader.attributes().value("unlockstate").toString() == "true") { trpUnlocked.append("unlocked"); } else { trpUnlocked.append("locked");