From ee98f577e26e8c04b5c3e17f6f0a1235216ad606 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Thu, 17 Mar 2022 23:18:33 +0100 Subject: [PATCH] sceNpTrophyGetTrophyIcon: SCE_NP_TROPHY_ERROR_HIDDEN And prevent some possible segfaults --- Utilities/rXml.cpp | 4 +- Utilities/rXml.h | 6 +-- rpcs3/Emu/Cell/Modules/sceNpTrophy.cpp | 72 ++++++++++++++++++------- rpcs3/Loader/TROPUSR.cpp | 48 ++++++++++++----- rpcs3/Loader/TROPUSR.h | 9 ++++ rpcs3/rpcs3qt/game_list.cpp | 2 +- rpcs3/rpcs3qt/trophy_manager_dialog.cpp | 27 ++++------ rpcs3/rpcs3qt/trophy_manager_dialog.h | 4 +- 8 files changed, 117 insertions(+), 55 deletions(-) diff --git a/Utilities/rXml.cpp b/Utilities/rXml.cpp index 6901945b68..6f223fa4d7 100644 --- a/Utilities/rXml.cpp +++ b/Utilities/rXml.cpp @@ -59,9 +59,9 @@ rXmlDocument::rXmlDocument() { } -void rXmlDocument::Read(const std::string& data) +pugi::xml_parse_result rXmlDocument::Read(const std::string& data) { - handle.load_buffer(data.data(), data.size()); + return handle.load_buffer(data.data(), data.size()); } std::shared_ptr rXmlDocument::GetRoot() diff --git a/Utilities/rXml.h b/Utilities/rXml.h index c5292470d8..4d8f378130 100644 --- a/Utilities/rXml.h +++ b/Utilities/rXml.h @@ -23,7 +23,7 @@ struct rXmlNode std::shared_ptr GetChildren(); std::shared_ptr GetNext(); std::string GetName(); - std::string GetAttribute( const std::string &name); + std::string GetAttribute(const std::string &name); std::string GetNodeContent(); pugi::xml_node handle{}; @@ -34,8 +34,8 @@ struct rXmlDocument rXmlDocument(); rXmlDocument(const rXmlDocument& other) = delete; rXmlDocument &operator=(const rXmlDocument& other) = delete; - void Read(const std::string& data); - std::shared_ptr GetRoot(); + pugi::xml_parse_result Read(const std::string& data); + virtual std::shared_ptr GetRoot(); pugi::xml_document handle{}; }; diff --git a/rpcs3/Emu/Cell/Modules/sceNpTrophy.cpp b/rpcs3/Emu/Cell/Modules/sceNpTrophy.cpp index 9c3e1b15d6..ba19b12042 100644 --- a/rpcs3/Emu/Cell/Modules/sceNpTrophy.cpp +++ b/rpcs3/Emu/Cell/Modules/sceNpTrophy.cpp @@ -746,21 +746,25 @@ error_code sceNpTrophyGetGameInfo(u32 context, u32 handle, vm::ptrtrp_name + "/TROPCONF.SFM")); + const std::string config_path = vfs::get("/dev_hdd0/home/" + Emu.GetUsr() + "/trophy/" + ctxt->trp_name + "/TROPCONF.SFM"); + + fs::file config(config_path); if (!config) { - return SCE_NP_TROPHY_ERROR_CONF_DOES_NOT_EXIST; + return { SCE_NP_TROPHY_ERROR_CONF_DOES_NOT_EXIST, config_path }; } - rXmlDocument doc; - doc.Read(config.to_string()); + trophy_xml_document doc{}; + pugi::xml_parse_result res = doc.Read(config.to_string()); + if (!res) + { + sceNpTrophy.error("sceNpTrophyGetGameInfo: Failed to read TROPCONF.SFM: %s", config_path); + // TODO: return some error + } auto trophy_base = doc.GetRoot(); - if (trophy_base->GetChildren()->GetName() == "trophyconf") - { - trophy_base = trophy_base->GetChildren(); - } + ensure(trophy_base); if (details) *details = {}; @@ -979,24 +983,28 @@ static error_code NpTrophyGetTrophyInfo(const trophy_context_t* ctxt, s32 trophy return SCE_NP_TROPHY_ERROR_CONTEXT_NOT_REGISTERED; } - fs::file config(vfs::get("/dev_hdd0/home/" + Emu.GetUsr() + "/trophy/" + ctxt->trp_name + "/TROPCONF.SFM")); + const std::string config_path = vfs::get("/dev_hdd0/home/" + Emu.GetUsr() + "/trophy/" + ctxt->trp_name + "/TROPCONF.SFM"); + + fs::file config(config_path); if (!config) { - return SCE_NP_TROPHY_ERROR_CONF_DOES_NOT_EXIST; + return { SCE_NP_TROPHY_ERROR_CONF_DOES_NOT_EXIST, config_path }; } SceNpTrophyDetails tmp_details{}; SceNpTrophyData tmp_data{}; - rXmlDocument doc; - doc.Read(config.to_string()); + trophy_xml_document doc{}; + pugi::xml_parse_result res = doc.Read(config.to_string()); + if (!res) + { + sceNpTrophy.error("sceNpTrophyGetGameInfo: Failed to read TROPCONF.SFM: %s", config_path); + // TODO: return some error + } auto trophy_base = doc.GetRoot(); - if (trophy_base->GetChildren()->GetName() == "trophyconf") - { - trophy_base = trophy_base->GetChildren(); - } + ensure(trophy_base); bool found = false; for (std::shared_ptr n = trophy_base->GetChildren(); n; n = n->GetNext()) @@ -1232,8 +1240,36 @@ error_code sceNpTrophyGetTrophyIcon(u32 context, u32 handle, s32 trophyId, vm::p if (!ctxt->tropusr->GetTrophyUnlockState(trophyId)) { - bool hidden = false; // TODO obtain this value - return hidden ? SCE_NP_TROPHY_ERROR_HIDDEN : SCE_NP_TROPHY_ERROR_LOCKED; + const std::string config_path = vfs::get("/dev_hdd0/home/" + Emu.GetUsr() + "/trophy/" + ctxt->trp_name + "/TROPCONF.SFM"); + + fs::file config(config_path); + if (config) + { + trophy_xml_document doc{}; + pugi::xml_parse_result res = doc.Read(config.to_string()); + if (!res) + { + sceNpTrophy.error("sceNpTrophyGetGameInfo: Failed to read TROPCONF.SFM: %s", config_path); + // TODO: return some error + } + + auto trophy_base = doc.GetRoot(); + ensure(trophy_base); + + for (std::shared_ptr n = trophy_base->GetChildren(); n; n = n->GetNext()) + { + if (n->GetName() == "trophy" && trophyId == atoi(n->GetAttribute("id").c_str()) && n->GetAttribute("hidden")[0] == 'y') + { + return SCE_NP_TROPHY_ERROR_HIDDEN; + } + } + } + else + { + // TODO: Maybe return SCE_NP_TROPHY_ERROR_CONF_DOES_NOT_EXIST + } + + return SCE_NP_TROPHY_ERROR_LOCKED; } fs::file icon_file(vfs::get("/dev_hdd0/home/" + Emu.GetUsr() + "/trophy/" + ctxt->trp_name + fmt::format("/TROP%03d.PNG", trophyId))); diff --git a/rpcs3/Loader/TROPUSR.cpp b/rpcs3/Loader/TROPUSR.cpp index 14e34deb7f..590c534daf 100644 --- a/rpcs3/Loader/TROPUSR.cpp +++ b/rpcs3/Loader/TROPUSR.cpp @@ -1,5 +1,4 @@ #include "stdafx.h" -#include "Utilities/rXml.h" #include "Emu/VFS.h" #include "TROPUSR.h" @@ -10,6 +9,25 @@ enum : u32 TROPUSR_MAGIC = 0x818F54AD }; +std::shared_ptr trophy_xml_document::GetRoot() +{ + auto trophy_base = rXmlDocument::GetRoot(); + ensure(trophy_base); + + if (auto trophy_conf = trophy_base->GetChildren(); + trophy_conf && trophy_conf->GetName() == "trophyconf") + { + trophy_base = trophy_conf; + } + else + { + trp_log.error("trophy_xml_document: Root name does not match trophyconf in trophy. Name: %s", trophy_conf ? trophy_conf->GetName() : trophy_base->GetName()); + // TODO: return nullptr or is this possible? + } + + return trophy_base; +} + TROPUSRLoader::load_result TROPUSRLoader::Load(const std::string& filepath, const std::string& configpath) { const std::string& path = vfs::get(filepath); @@ -148,17 +166,19 @@ bool TROPUSRLoader::Generate(const std::string& filepath, const std::string& con return false; } - rXmlDocument doc; - doc.Read(config.to_string()); + trophy_xml_document doc{}; + pugi::xml_parse_result res = doc.Read(config.to_string()); + if (!res) + { + trp_log.error("TROPUSRLoader::Generate: Failed to read file: %s", filepath); + return false; + } m_table4.clear(); m_table6.clear(); auto trophy_base = doc.GetRoot(); - if (trophy_base->GetChildren()->GetName() == "trophyconf") - { - trophy_base = trophy_base->GetChildren(); - } + ensure(trophy_base); for (std::shared_ptr n = trophy_base->GetChildren(); n; n = n->GetNext()) { @@ -247,14 +267,16 @@ u32 TROPUSRLoader::GetUnlockedPlatinumID(u32 trophy_id, const std::string& confi return invalid_trophy_id; } - rXmlDocument doc; - doc.Read(config.to_string()); + trophy_xml_document doc{}; + pugi::xml_parse_result res = doc.Read(config.to_string()); + if (!res) + { + trp_log.error("TROPUSRLoader::GetUnlockedPlatinumID: Failed to read file: %s", config_path); + return invalid_trophy_id; + } auto trophy_base = doc.GetRoot(); - if (trophy_base->GetChildren()->GetName() == "trophyconf") - { - trophy_base = trophy_base->GetChildren(); - } + ensure(trophy_base); const usz trophy_count = m_table4.size(); diff --git a/rpcs3/Loader/TROPUSR.h b/rpcs3/Loader/TROPUSR.h index efc668ba41..f1bac15bd8 100644 --- a/rpcs3/Loader/TROPUSR.h +++ b/rpcs3/Loader/TROPUSR.h @@ -1,4 +1,7 @@ #pragma once +#include "stdafx.h" +#include "Utilities/rXml.h" +#include "Utilities/File.h" struct TROPUSRHeader { @@ -54,6 +57,12 @@ struct TROPUSREntry6 // Note: One of the fields should hold a flag showing whether the trophy is hidden or not }; +struct trophy_xml_document : public rXmlDocument +{ + trophy_xml_document() : rXmlDocument() {} + std::shared_ptr GetRoot() override; +}; + class TROPUSRLoader { enum trophy_grade : u32 diff --git a/rpcs3/rpcs3qt/game_list.cpp b/rpcs3/rpcs3qt/game_list.cpp index 6d3e17c37e..030023225d 100644 --- a/rpcs3/rpcs3qt/game_list.cpp +++ b/rpcs3/rpcs3qt/game_list.cpp @@ -10,7 +10,7 @@ void game_list::clear_list() void game_list::mousePressEvent(QMouseEvent *event) { - if (!indexAt(event->pos()).isValid() || !itemAt(event->pos())->data(Qt::UserRole).isValid()) + if (QTableWidgetItem* item = itemAt(event->pos()); !item || !item->data(Qt::UserRole).isValid()) { clearSelection(); setCurrentItem(nullptr); // Needed for currentItemChanged diff --git a/rpcs3/rpcs3qt/trophy_manager_dialog.cpp b/rpcs3/rpcs3qt/trophy_manager_dialog.cpp index a44ab9e395..27d071b3ba 100644 --- a/rpcs3/rpcs3qt/trophy_manager_dialog.cpp +++ b/rpcs3/rpcs3qt/trophy_manager_dialog.cpp @@ -1,3 +1,4 @@ +#include "stdafx.h" #include "trophy_manager_dialog.h" #include "custom_table_widget_item.h" #include "table_item_delegate.h" @@ -13,8 +14,6 @@ #include "Emu/system_utils.hpp" #include "Emu/Cell/Modules/sceNpTrophy.h" -#include "Loader/TROPUSR.h" - #include #include #include @@ -409,12 +408,16 @@ bool trophy_manager_dialog::LoadTrophyFolderToDB(const std::string& trop_name) } // Get game name - game_trophy_data->trop_config.Read(config.to_string()); - std::shared_ptr trophy_base = game_trophy_data->trop_config.GetRoot(); - if (trophy_base->GetChildren()->GetName() == "trophyconf") + pugi::xml_parse_result res = game_trophy_data->trop_config.Read(config.to_string()); + if (!res) { - trophy_base = trophy_base->GetChildren(); + gui_log.error("Failed to read trophy xml: %s", tropconf_path); + return false; } + + std::shared_ptr trophy_base = game_trophy_data->trop_config.GetRoot(); + ensure(trophy_base); + for (std::shared_ptr n = trophy_base->GetChildren(); n; n = n->GetNext()) { if (n->GetName() == "title-name") @@ -872,15 +875,7 @@ void trophy_manager_dialog::PopulateTrophyTable() m_trophy_table->setSortingEnabled(false); // Disable sorting before using setItem calls std::shared_ptr trophy_base = data->trop_config.GetRoot(); - if (trophy_base->GetChildren()->GetName() == "trophyconf") - { - trophy_base = trophy_base->GetChildren(); - } - else - { - gui_log.error("Root name does not match trophyconf in trophy. Name received: %s", trophy_base->GetChildren()->GetName()); - return; - } + ensure(trophy_base); QPixmap placeholder(m_icon_height, m_icon_height); placeholder.fill(Qt::transparent); @@ -895,7 +890,7 @@ void trophy_manager_dialog::PopulateTrophyTable() } // Get data (stolen graciously from sceNpTrophy.cpp) - SceNpTrophyDetails details; + SceNpTrophyDetails details{}; // Get trophy id const s32 trophy_id = atoi(n->GetAttribute("id").c_str()); diff --git a/rpcs3/rpcs3qt/trophy_manager_dialog.h b/rpcs3/rpcs3qt/trophy_manager_dialog.h index c866ba2b02..c709a9ab3c 100644 --- a/rpcs3/rpcs3qt/trophy_manager_dialog.h +++ b/rpcs3/rpcs3qt/trophy_manager_dialog.h @@ -1,6 +1,6 @@ #pragma once -#include "Utilities/rXml.h" +#include "Loader/TROPUSR.h" #include #include @@ -22,7 +22,7 @@ class TROPUSRLoader; struct GameTrophiesData { std::unique_ptr trop_usr; - rXmlDocument trop_config; // I'd like to use unique but the protocol inside of the function passes around shared pointers.. + trophy_xml_document trop_config; // I'd like to use unique but the protocol inside of the function passes around shared pointers.. std::unordered_map trophy_images; // Cache trophy images to avoid loading from disk as much as possible. std::unordered_map trophy_image_paths; std::string game_name;