From e8d4f46bdf06afbcfcae9cd37e1f3c4007a50fa3 Mon Sep 17 00:00:00 2001 From: igor725 Date: Fri, 3 May 2024 04:31:12 +0300 Subject: [PATCH] Trophies file parser --- core/CMakeLists.txt | 4 + core/trophies/CMakeLists.txt | 5 + core/trophies/trophies.cpp | 388 ++++ core/trophies/trophies.h | 80 + core/trophies/xml3all.h | 2706 +++++++++++++++++++++++++ docs/json/general.json | 9 +- docs/wiki/markdown/Config-files.md | 1 + modules/libSceNpTrophy/CMakeLists.txt | 5 +- modules/libSceNpTrophy/codes.h | 34 +- modules/libSceNpTrophy/entry.cpp | 32 +- modules/libSceNpTrophy/types.h | 12 +- tools/config_emu/config_emu.cpp | 1 + 12 files changed, 3252 insertions(+), 25 deletions(-) create mode 100644 core/trophies/CMakeLists.txt create mode 100644 core/trophies/trophies.cpp create mode 100644 core/trophies/trophies.h create mode 100644 core/trophies/xml3all.h diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 396f208..07e3ac4 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -10,6 +10,7 @@ add_subdirectory(initParams) add_subdirectory(timer) add_subdirectory(systemContent) add_subdirectory(networking) +add_subdirectory(trophies) add_subdirectory(fileManager) add_subdirectory(memory) add_subdirectory(dmem) @@ -23,6 +24,7 @@ add_library(core SHARED $ $ $ + $ $ $ $ @@ -46,6 +48,8 @@ target_link_libraries(core PRIVATE onecore.lib IPHLPAPI.lib Ws2_32.lib + libcrypto.lib + libssl.lib ntdll.dll VulkanMemoryAllocator ${Vulkan_LIBRARIES} diff --git a/core/trophies/CMakeLists.txt b/core/trophies/CMakeLists.txt new file mode 100644 index 0000000..ee35399 --- /dev/null +++ b/core/trophies/CMakeLists.txt @@ -0,0 +1,5 @@ +add_library(trophies OBJECT + trophies.cpp +) + +add_dependencies(trophies third_party) diff --git a/core/trophies/trophies.cpp b/core/trophies/trophies.cpp new file mode 100644 index 0000000..073f7f4 --- /dev/null +++ b/core/trophies/trophies.cpp @@ -0,0 +1,388 @@ +#define __APICALL_EXTERN +#include "trophies.h" +#undef __APICALL_EXTERN + +#include "core/fileManager/fileManager.h" +#include "logging.h" +#include "modules_include/system_param.h" +#include "tools/config_emu/config_emu.h" +#include "xml3all.h" + +#include +#include + +#define TR_AES_BLOCK_SIZE 16 +// We don't need it there +#undef min + +LOG_DEFINE_MODULE(core_trophies); + +class Trophies: public ITrophies { + struct trp_header { + uint32_t magic; // should be 0xDCA24D00 + uint32_t version; + uint64_t tfile_size; // size of trp file + uint32_t entry_num; // num of entries + uint32_t entry_size; // size of entry + uint32_t dev_flag; // 1: dev + uint8_t digest[20]; // sha hash + uint32_t key_index; + char padding[44]; + }; + + struct trp_entry { + char name[32]; + uint64_t pos; + uint64_t len; + uint32_t flag; + char padding[12]; + }; + + static bool caseequal(char a, char b) { return std::tolower(static_cast(a)) == std::tolower(static_cast(b)); } + + bool m_bKeySet = false; + uint8_t m_trkey[TR_AES_BLOCK_SIZE] = {}; + + public: + Trophies() { + auto [lock, jData] = accessConfig()->accessModule(ConfigModFlag::GENERAL); + std::string hexdata; + + if (getJsonParam(jData, "trophyKey", hexdata) && hexdata.length() > 31) { + if (hexdata.starts_with("0x")) hexdata.erase(0, 2); + if (hexdata.find_first_not_of("0123456789abcdefABCDEF") != hexdata.npos) return; + m_bKeySet = true; + for (uint32_t i = 0; i < 32; i += 2) { + auto byte = hexdata.substr(i, 2); + m_trkey[i / 2] = (uint8_t)std::strtol(byte.c_str(), nullptr, 16); + } + } + } + + ErrCodes parseTRP(trp_grp_cb* grpcb = nullptr, trp_ent_cb* trpcb = nullptr, trp_png_cb* pngcb = nullptr, bool lightweight = false) final { + if (grpcb == nullptr && trpcb == nullptr && pngcb == nullptr) return ErrCodes::NO_CALLBACKS; + if (pngcb != nullptr && lightweight == true) return ErrCodes::NO_PNG; + if (m_bKeySet == false) return ErrCodes::NO_KEY_SET; + LOG_USE_MODULE(core_trophies); + + auto mpath = accessFileManager().getMappedPath("/app0/sce_sys/trophy/trophy00.trp"); + if (mpath.has_value() == false) return ErrCodes::NO_TROPHIES; + + std::ifstream trfile(mpath->c_str(), std::ios::in | std::ios::binary); + + if (trfile.is_open()) { + trp_header hdr; + + if (!trfile.read((char*)&hdr, sizeof(hdr))) return ErrCodes::IO_FAIL; + if (hdr.magic != 0x004DA2DC) return ErrCodes::INVALID_MAGIC; + if (hdr.version != 0x03000000) return ErrCodes::INVALID_VERSION; + + /** + * This file's numbers encoded in big-endian for some twisted reason. + * Probably because of format history. + */ + hdr.entry_num = _byteswap_ulong(hdr.entry_num); + hdr.entry_size = _byteswap_ulong(hdr.entry_size); + if (hdr.entry_size != sizeof(trp_entry)) return ErrCodes::INVALID_ENTSIZE; + + std::string localized_trp; + { + SystemParamLang systemlang; + auto [lock, jData] = accessConfig()->accessModule(ConfigModFlag::GENERAL); + + try { + (*jData)["systemlang"].get_to(systemlang); + } catch (const json::exception& e) { + systemlang = SystemParamLang::EnglishUS; + } + + if (systemlang != SystemParamLang::EnglishUS) localized_trp = std::format("TROP_{:02}.ESFM", (uint32_t)systemlang); + } + + trp_entry ent, dent = {0}; + for (uint32_t i = 0; i < hdr.entry_num; ++i) { + if ((pngcb != nullptr && pngcb->cancelled) && (grpcb != nullptr && grpcb->cancelled) && (trpcb != nullptr && trpcb->cancelled)) + return ErrCodes::SUCCESS; + + if (!trfile.read((char*)&ent, sizeof(trp_entry))) return ErrCodes::IO_FAIL; + + ent.pos = _byteswap_uint64(ent.pos); + ent.len = _byteswap_uint64(ent.len); + + if (pngcb != nullptr && !pngcb->cancelled) { + static std::string_view ext(".png"); + std::string_view name(ent.name); + if (std::equal(ext.rbegin(), ext.rend(), name.rbegin(), caseequal)) { // Test trp file extension + if (((ent.flag >> 24) & 0x03) == 0) { + pngcb->data.pngsize = ent.len; + pngcb->data.pngdata = new char[ent.len]; // Developer should free this memory manually + auto ppos = trfile.tellg(); + trfile.seekg(ent.pos); + if (trfile.read((char*)pngcb->data.pngdata, ent.len)) { + pngcb->cancelled = pngcb->func(&pngcb->data); + trfile.seekg(ppos); + continue; + } + + return ErrCodes::IO_FAIL; + } else { + // Is this even possible? + return ErrCodes::NOT_IMPLEMENTED; + } + } + } + + if (grpcb != nullptr || trpcb != nullptr) { + static std::string_view ext(".esfm"); + std::string_view name(ent.name); + if (!std::equal(ext.rbegin(), ext.rend(), name.rbegin(), caseequal)) continue; + if ((ent.len % 16) != 0) return ErrCodes::INVALID_AES; + if (lightweight == true) { + static std::string_view lwfile("tropconf.esfm"); + if (!std::equal(lwfile.begin(), lwfile.end(), name.begin(), name.end(), caseequal)) continue; + } else { + static std::string_view dfile("trop.esfm"); + if (localized_trp.length() == 0) { + // No localized trophy needed, using the English one + if (!std::equal(name.begin(), name.end(), dfile.begin(), dfile.end(), caseequal)) continue; + } else { + // Trying to find localized trophy + if (!std::equal(name.begin(), name.end(), localized_trp.begin(), localized_trp.end(), caseequal)) { + if (std::equal(name.begin(), name.end(), dfile.begin(), dfile.end(), caseequal)) dent = ent; + // End of trophy.trp file reached + if (i == (hdr.entry_num - 1)) { + // Failed to find localized and non-localized trophy, probably file packed incorrectly + if (dent.len == 0) continue; + // We failed to find localized trophy, so we use the default one + ent = dent; + } else { + // Not the last entry yet, check the next one + continue; + } + } + } + } + + auto mem = new char[ent.len]; + auto ppos = trfile.tellg(); + trfile.seekg(ent.pos); // Seek to file position + + if (((ent.flag >> 24) & 0x03) == 0) { + auto ppos = trfile.tellg(); + if (!trfile.read(mem, ent.len)) { + delete[] mem; + return ErrCodes::IO_FAIL; + } + } else { + uint8_t d_iv[TR_AES_BLOCK_SIZE]; + uint8_t kg_iv[TR_AES_BLOCK_SIZE]; + uint8_t enc_xmlh[48]; + ::memset(kg_iv, 0, TR_AES_BLOCK_SIZE); + + if (!trfile.read((char*)d_iv, TR_AES_BLOCK_SIZE)) { + delete[] mem; + return ErrCodes::IO_FAIL; + } + + // 384 encrypted bits is just enough to find interesting for us string + if (!trfile.read((char*)enc_xmlh, 48)) { + delete[] mem; + return ErrCodes::IO_FAIL; + } + + const auto trydecrypt = [this, mem, &ent, d_iv, kg_iv, enc_xmlh, &trfile](uint32_t npid) -> bool { + uint8_t outbuffer[512]; + uint8_t inbuffer[512]; + + ::memset(outbuffer, 0, 512); + ::memset(inbuffer, 0, 512); + ::sprintf_s((char*)inbuffer, sizeof(inbuffer), "NPWR%05d_00", npid); + + int outlen; + + // Key creation context + { + EVP_CIPHER_CTX* key_ctx = EVP_CIPHER_CTX_new(); + if (!EVP_EncryptInit(key_ctx, EVP_aes_128_cbc(), m_trkey, kg_iv)) { + EVP_CIPHER_CTX_free(key_ctx); + return false; + } + if (!EVP_EncryptUpdate(key_ctx, outbuffer, &outlen, inbuffer, TR_AES_BLOCK_SIZE)) { + EVP_CIPHER_CTX_free(key_ctx); + return false; + } + /** + * Cipher finalizing is not really necessary there, + * since we use only 16 bytes encrypted by the update function above + */ + EVP_CIPHER_CTX_free(key_ctx); + } + //- Key creation context + + // Data decipher context + EVP_CIPHER_CTX* data_ctx = EVP_CIPHER_CTX_new(); + { + if (!EVP_DecryptInit(data_ctx, EVP_aes_128_cbc(), outbuffer, d_iv)) { + EVP_CIPHER_CTX_free(data_ctx); + return false; + } + if (!EVP_DecryptUpdate(data_ctx, outbuffer, &outlen, enc_xmlh, 48)) { + EVP_CIPHER_CTX_free(data_ctx); + return false; + } + if (::_strnicmp((char*)outbuffer, "", trim(v).c_str()); + return Format("", v.c_str()); +} + +// Serialization +inline string XMLHeader::Serialize() const { + return Format("", version.GetValue().c_str(), encoding.GetValue().c_str(), + standalone.GetValue().c_str()); +} + +inline void XMLHeader::Default() { + version.SetName("version"); + encoding.SetName("encoding"); + standalone.SetName("standalone"); + + version = "1.0"; + encoding = "UTF-8"; + standalone = "yes"; +} + +inline XMLHeader::XMLHeader() { + Default(); +} + +inline XMLElement::XMLElement() { + children.reserve(100); + variables.reserve(20); +} + +// Operators +inline bool XMLElement::operator==(const XMLElement& t) const { + if (param != t.param) return false; + if (el != t.el) return false; + + if (children.size() != t.children.size()) return false; + if (variables.size() != t.variables.size()) return false; + if (contents.size() != t.contents.size()) return false; + if (cdatas.size() != t.cdatas.size()) return false; + if (comments.size() != t.comments.size()) return false; + + for (size_t i = 0; i < children.size(); i++) { + auto a = children[i]; + auto b = t.children[i]; + if (!(a == b)) return false; + } + + for (size_t i = 0; i < variables.size(); i++) { + auto a = variables[i]; + auto b = t.variables[i]; + if (!(a == b)) return false; + } + + for (size_t i = 0; i < contents.size(); i++) { + auto a = contents[i]; + auto b = t.contents[i]; + if (!(a == b)) return false; + } + + for (size_t i = 0; i < comments.size(); i++) { + auto a = comments[i]; + auto b = t.comments[i]; + if (!(a == b)) return false; + } + + for (size_t i = 0; i < cdatas.size(); i++) { + auto a = cdatas[i]; + auto b = t.cdatas[i]; + if (!(a == b)) return false; + } + + return true; +} + +inline bool XMLElement::operator<(const XMLElement& x) const { + // Compare names + if (el > x.el) return false; + return true; +} + +inline XMLElement& XMLElement ::operator=(const XMLElement& t) { + operator=(t.Serialize().c_str()); + return *this; +} + +inline XMLElement& XMLElement::operator=(const char* xx) { + if (!xx) { + clear(); + return *this; + } + if (xx[0] != '<') { + SetElementName(xx); + return *this; + } + + XML x(xx, strlen(xx)); + CloneMirror(*this, x.GetRootElement()); + return *this; +} + +inline XMLElement XMLElement::Mirror() const { + XMLElement t; + CloneMirror(t, *this); + return t; +} + +inline XMLElement::XMLElement(const char* t) { + operator=(t); +} + +inline void XMLElement::CloneMirror(XMLElement& to, const XMLElement& from) { + to.children = from.children; + to.variables = from.variables; + to.comments = from.comments; + to.contents = from.contents; + to.cdatas = from.cdatas; + to.el = from.el; + to.param = from.param; + to.parent = from.parent; + to.id = from.id; +} + +inline XMLElement::XMLElement(const XMLElement& from) { + el = from.el; + param = from.param; + + // cdatas + cdatas.reserve(from.cdatas.size()); + for (auto a: from.cdatas) { + shared_ptr c = make_shared(XMLCData(a->ep, a->v.c_str())); + cdatas.push_back(c); + } + + // comments + comments.reserve(from.comments.size()); + for (auto a: from.comments) { + shared_ptr c = make_shared(XMLComment(a->ep, a->v.c_str())); + comments.push_back(c); + } + + // contents + contents.reserve(from.contents.size()); + for (auto a: from.contents) { + shared_ptr c = make_shared(XMLContent(a->ep, a->v.c_str())); + contents.push_back(c); + } + + // vars + variables.reserve(from.variables.size()); + for (auto a: from.variables) { + shared_ptr c = make_shared(XMLVariable(a->n.c_str(), a->v.c_str())); + variables.push_back(c); + } + + // children + children.reserve(from.children.size()); + for (auto a: from.children) { + shared_ptr c = make_shared(XMLElement()); + c->operator=((XMLElement&)*a.get()); + children.push_back(c); + } + + // operator=(from.Serialize().c_str()); +} + +inline XMLElement::XMLElement(XMLElement&& from) { + el = from.el; + param = from.param; + cdatas = from.cdatas; + comments = from.comments; + contents = from.contents; + variables = from.variables; + children = from.children; +} + +// Gets +inline const XMLElement& XMLElement::operator[](size_t idx) const { + if (idx >= children.size()) throw XML_ERROR::INVALIDIDX; + return *children[idx]; +} + +inline XMLElement& XMLElement::operator[](size_t idx) { + if (idx >= children.size()) throw XML_ERROR::INVALIDIDX; + return *children[idx]; +} + +inline XMLElement& XMLElement::operator[](const char* elm) { + if (elm == 0) { + if (children.size() == 0) throw XML_ERROR::INVALIDIDX; + return *children[0]; + } + shared_ptr e = FindElementZ(elm, true); + return *e; +} + +inline string XMLElement ::vd(const char* nn, const char* def) { + string k; + if (!nn) { + if (variables.size() == 0) return (def ? def : ""); + k = variables[0]->GetValue(); + } else { + shared_ptr v = FindVariableZ(nn, true); + k = v->GetValue(); + } + if (k.empty() && def) k = def; + return k; +} + +inline string XMLElement::vd(const char* nn, const char* def) const { + string k; + if (!nn) { + if (variables.size() == 0) return (def ? def : ""); + k = variables[0]->GetValue(); + } else { + shared_ptr v = FindVariable(nn); + if (v) k = v->GetValue(); + } + if (k.empty() && def) k = def; + return k; +} + +inline const string& XMLElement::v(size_t idx) const { + if (idx >= variables.size()) throw XML_ERROR::INVALIDIDX; + return variables[idx]->GetValue(); +} + +inline const string& XMLElement::v(const char* nn) { + if (!nn) { + if (variables.size() == 0) throw XML_ERROR::INVALIDIDX; + return variables[0]->GetValue(); + } + shared_ptr v = FindVariableZ(nn, true); + return v->GetValue(); +} + +inline wstring XMLElement::wv(const char* nn) { + if (!nn) { + if (variables.size() == 0) throw XML_ERROR::INVALIDIDX; + return variables[0]->GetWideValue(); + } + shared_ptr v = FindVariableZ(nn, true); + return v->GetWideValue(); +} + +inline string XMLElement::Content() const { + if (contents.empty()) return ""; + return contents[0]->GetValue().c_str(); +} + +inline string XMLElement::GetContent() const { + return Content(); +} + +inline XMLVariable& XMLElement::vv(const char* nn) { + shared_ptr v = FindVariableZ(nn, true); + return *v; +} + +inline unsigned long long XMLElement::GetElementParam() const { + return param; +} + +inline const string& XMLElement::GetElementName() const { + return el; +} + +inline void XMLElement::GetAllChildren(vector>& ch) const { + for (auto& a: children) { + ch.push_back(a); + a->GetAllChildren(ch); + } +} + +/* inline shared_ptr XMLElement::GetParent(shared_ptr r) const + { + if (r->par) + return r->par; + if (r->id == parent) + return r; + for (auto a : r->children) + { + if (a->id == parent) + return a; + } + return 0; + } +*/ + +inline XMLElement* XMLElement::GetParent(XMLElement* r) const { + if (par) return par; + if (r->id == parent) return r; + vector> ch; + r->GetAllChildren(ch); + for (auto a: ch) { + if (a->id == parent) return a.get(); + } + return 0; +} + +inline size_t XMLElement::GetElementIndex(const XMLElement& e) const { + for (size_t i = 0; i < children.size(); i++) { + auto& exl = children[i]; + if (exl.get() == &e) return i; + } + return (size_t)-1; +} + +// Sets +inline void XMLElement::SetElementName(const char* x) { + el.clear(); + if (x) el = x; +} + +inline void XMLElement::SetElementName(const wchar_t* x) { + el.clear(); + if (!x) return; + XMLU wh(x); + el = wh; +} + +inline XMLContent& XMLElement::SetContent(const char* vp) { + if (contents.size() == 0) + return AddContent(vp, 0); + else { + GetContents()[0]->SetValue(vp); + return *GetContents()[0]; + } +} + +inline void XMLElement::SetElementParam(unsigned long long p) { + param = p; +} + +inline XMLVariable& XMLElement::SetValue(const char* vn, const char* vp) { + auto& a = vv(vn); + a = vp; + return a; +} + +inline XML_ERROR XMLElement::MoveElement(size_t i, size_t y) { + if (i >= children.size() || y >= children.size()) return XML_ERROR::INVALIDARG; + shared_ptr x = children[i]; + children.erase(children.begin() + i); + children.insert(children.begin() + y, x); + return XML_ERROR::OK; +} + +// Find +inline shared_ptr XMLElement::FindElementZ(const char* n, bool ForceCreate) { + if (!n) return 0; + for (size_t i = 0; i < children.size(); i++) { + shared_ptr& cc = children[i]; + const string& cn = cc->GetElementName(); + if (strcmp(cn.c_str(), n) == 0) return cc; + } + if (ForceCreate == 0) return 0; + XMLElement& vv = AddElement(); + vv.SetElementName(n); + return FindElementZ(vv.GetElementName().c_str()); +} + +inline shared_ptr XMLElement::FindVariable(const char* n) const { + if (!n) return 0; + for (size_t i = 0; i < variables.size(); i++) { + const shared_ptr& cc = variables[i]; + const string& cn = cc->GetName(); + if (strcmp(cn.c_str(), n) == 0) return cc; + } + return 0; +} + +inline shared_ptr XMLElement::FindVariableZ(const char* n, bool ForceCreate, const char* defv, size_t* pidx) { + if (!n) return 0; + for (size_t i = 0; i < variables.size(); i++) { + shared_ptr& cc = variables[i]; + const string& cn = cc->GetName(); + if (strcmp(cn.c_str(), n) == 0) { + if (pidx) *pidx = i; + return cc; + } + } + if (ForceCreate == 0) return 0; + XMLVariable& vv = AddVariable(n, defv); + return FindVariableZ(vv.GetName().c_str()); +} + +// Inserts +inline shared_ptr XMLElement::InsertElement(size_t y, const XMLElement& x) { + if (y >= children.size()) y = children.size(); + shared_ptr xx = make_shared(XMLElement(x)); + children.insert(children.begin() + y, xx); + children[y]->parent = id; + children[y]->par = this; + return children[y]; +} + +inline shared_ptr XMLElement::InsertElement(size_t y, XMLElement&& x) { + if (y >= children.size()) y = children.size(); + shared_ptr xx = make_shared(XMLElement(std::forward(x))); + children.insert(children.begin() + y, xx); + children[y]->parent = id; + children[y]->par = this; + return children[y]; +} + +inline XMLElement& XMLElement::AddElement(const XMLElement& c) { + return *InsertElement((size_t)-1, c); +} + +inline XMLElement& XMLElement::AddElement(XMLElement&& c) { + return *InsertElement((size_t)-1, std::forward(c)); +} + +inline XMLElement& XMLElement::AddElement(const char* n) { + XMLElement c = n; + return *InsertElement((size_t)-1, std::forward(c)); +} + +inline shared_ptr XMLElement::AddElement2(const char* n) { + XMLElement c = n; + return InsertElement((size_t)-1, std::forward(c)); +} + +inline void XMLElement::AddElements(const std::initializer_list& s) { + for (auto& a: s) { + XMLElement c = a.c_str(); + InsertElement((size_t)-1, c); + } +} + +inline void XMLElement::SetVariables(const std::initializer_list& s) { + if (s.size() % 2) return; + for (size_t i = 0; i < s.size(); i += 2) { + auto a = s.begin() + i; + auto b = s.begin() + i + 1; + vv(a->c_str()) = b->c_str(); + } +} + +inline XMLVariable& XMLElement::AddVariable(const XMLVariable& vv, size_t p) { + if (p == (size_t)-1) p = (size_t)variables.size(); + shared_ptr v = make_shared(XMLVariable(vv)); + variables.insert(variables.begin() + p, v); + return *variables[p]; +} + +inline XMLVariable& XMLElement::AddVariable(const char* vn, const char* vv, size_t p) { + if (p == (size_t)-1) p = (size_t)variables.size(); + shared_ptr v = make_shared(XMLVariable(vn, vv)); + variables.insert(variables.begin() + p, v); + return *variables[p]; +} + +inline XMLContent& XMLElement::AddContent(const XMLContent& co, size_t pos) { + if (pos == (size_t)-1) pos = (size_t)contents.size(); + shared_ptr v = make_shared(co); + contents.insert(contents.begin() + pos, v); + return *contents[pos]; +} + +inline XMLContent& XMLElement::AddContent(const char* pv, size_t ep, size_t p) { + if (p == (size_t)-1) p = (size_t)contents.size(); + shared_ptr v = make_shared(XMLContent(ep, pv)); + contents.insert(contents.begin() + p, v); + return *contents[p]; +} + +inline XMLComment& XMLElement::AddComment(const char* pv, size_t ep, size_t p) { + if (p == (size_t)-1) p = (size_t)comments.size(); + shared_ptr v = make_shared(XMLComment(ep, pv)); + comments.insert(comments.begin() + p, v); + return *comments[p]; +} + +inline XMLCData& XMLElement::AddCData(const char* pv, size_t ep, size_t p) { + if (p == (size_t)-1) p = (size_t)cdatas.size(); + shared_ptr v = make_shared(XMLCData(ep, pv)); + cdatas.insert(cdatas.begin() + p, v); + return *cdatas[p]; +} + +// Removals +inline size_t XMLElement::RemoveAllElements() { + children.clear(); + return 0; +} + +inline size_t XMLElement::RemoveElement(size_t i) { + if (i >= children.size()) return (size_t)-1; + children.erase(children.begin() + i); + return children.size(); +} + +inline size_t XMLElement::RemoveElementByName(const char* n) { + for (size_t i = 0; i < children.size(); i++) { + if (strcmp(children[i]->GetElementName().c_str(), n) == 0) { + children.erase(children.begin() + i); + return i; + } + } + return (size_t)-1; +} + +inline void XMLElement::RemoveDuplicateNamespaces(const char* vn) { + if (vn) { + RemoveVariableZ(vn); + for (auto& ch: children) + ch->RemoveDuplicateNamespaces(vn); + return; + } + + for (size_t i = 0; i < variables.size(); i++) { + shared_ptr& cc = variables[i]; + if (cc->IsNS()) { + for (auto& ch: children) + ch->RemoveDuplicateNamespaces(cc->GetName().c_str()); + } + } + + for (auto& ch: children) + ch->RemoveDuplicateNamespaces(0); +} + +inline size_t XMLElement::RemoveElement(XMLElement* p) { + for (size_t i = 0; i < children.size(); i++) { + if (children[i].get() == p) { + children.erase(children.begin() + i); + break; + } + } + return children.size(); +} + +inline shared_ptr XMLElement::RemoveElementAndKeep(size_t i) { + if (i >= children.size()) throw XML_ERROR::INVALIDARG; + auto X = children[i]; + RemoveElement(i); + return X; +} + +inline void XMLElement::clear() { + variables.clear(); + children.clear(); + cdatas.clear(); + comments.clear(); + contents.clear(); + param = 0; + el = "e"; +} + +// Variables +inline size_t XMLElement::RemoveAllVariables() { + variables.clear(); + return 0; +} + +inline size_t XMLElement::RemoveVariable(size_t i) { + if (i >= variables.size()) return variables.size(); + variables.erase(variables.begin() + i); + return variables.size(); +} + +inline bool XMLElement::RemoveVariableZ(const char* n) { + size_t idx = 0; + auto v = FindVariableZ(n, false, "", &idx); + if (!v) return false; + RemoveVariable(idx); + return true; +} + +inline shared_ptr XMLElement::RemoveVariableAndKeep(size_t i) { + if (i >= variables.size()) throw XML_ERROR::INVALIDARG; + auto v = variables[i]; + variables.erase(variables.begin() + i); + return v; +} + +inline string XMLElement::EorE(const string& s, bool N) const { + if (N) return s; + return Encode(s.c_str()); +} + +inline string XMLElement::Serialize(XMLSerialization* srz) const { + string v; + Serialize(v, srz); + return v; +} + +inline void XMLElement::Serialize(string& v, XMLSerialization* srz) const { + XMLSerialization defsrz; + if (!srz) srz = &defsrz; + + string padd; + if (!srz->ExcludeSelf) { + if (srz->Canonical) { + /* for (size_t i = 0; i < srz->deep; i++) + { + padd += " "; + }*/ + } else if (srz->NoCRLF == false) { + for (size_t i = 0; i < srz->deep; i++) { + padd += "\t"; + } + } + + // Canonical) + v += Format("%s<%s>", padd.c_str(), EorE(el, srz->NoEnc).c_str(), EorE(el, srz->NoEnc).c_str()); + else + v += Format(srz->NoCRLF ? "%s<%s />" : "%s<%s />\r\n", padd.c_str(), EorE(el, srz->NoEnc).c_str()); + return; + } + + v += Format("%s<%s", padd.c_str(), EorE(el, srz->NoEnc).c_str()); + if (!variables.empty()) { + vector> v2; + if (srz->Canonical) { + v2 = variables; + std::sort(v2.begin(), v2.end(), [](std::shared_ptr e1, std::shared_ptr e2) -> bool { + if (strncmp(e1->GetName().c_str(), "xmlns:", 6) == 0 && strncmp(e2->GetName().c_str(), "xmlns:", 6) != 0) return true; + if (strncmp(e2->GetName().c_str(), "xmlns:", 6) == 0 && strncmp(e1->GetName().c_str(), "xmlns:", 6) != 0) return false; + if (e1->GetName() < e2->GetName()) { + return true; + } + return 0; + }); + } + const vector>& v3 = srz->Canonical ? v2 : variables; + + for (auto a: v3) { + v += " "; + v += a->Serialize(srz); + } + } + + if (children.empty() && comments.empty() && contents.empty() && cdatas.empty() && !srz->Canonical) { + v += srz->NoCRLF ? "/>" : "/>\r\n"; + return; + } + if (srz->Canonical) + v += ">"; + else + v += srz->NoCRLF ? ">" : ">\r\n"; + } + + auto ac = [&](vector>& cx, size_t B, size_t B2) { + for (auto& a: contents) { + if (a->GetEP() == B || a->GetEP() == B2) cx.push_back(a); + } + for (auto& a: comments) { + if (a->GetEP() == B || a->GetEP() == B2) cx.push_back(a); + } + for (auto& a: cdatas) { + if (a->GetEP() == B || a->GetEP() == B2) cx.push_back(a); + } + }; + + // Stuff + for (size_t i = 0; i < children.size(); i++) { + auto& a = children[i]; + + // Before items + vector> cx; + ac(cx, i, i); + for (auto& n: cx) { + string e = XMLContent::trim(n->Serialize(srz)); + if (srz->Canonical) { + v += e; + // v += "\n"; + } else + v += Format(srz->NoCRLF ? "%s%s" : "%s%s\r\n", padd.c_str(), e.c_str()); + } + + XMLSerialization s2 = *srz; + s2.deep = srz->deep + 1; + a->Serialize(v, &s2); + } + + // After items + vector> cx; + ac(cx, (size_t)-1, children.size()); + for (auto& n: cx) { + string e = XMLContent::trim(n->Serialize(srz)); + if (srz->Canonical) { + v += e; + // v += "\n"; + } else + v += Format(srz->NoCRLF ? "%s%s" : "%s%s\r\n", padd.c_str(), e.c_str()); + } + + if (srz->Canonical) + v += Format("%s", padd.c_str(), EorE(el, srz->NoEnc).c_str()); + else { + if (!srz->ExcludeSelf) v += Format(srz->NoCRLF ? "%s" : "%s\r\n", padd.c_str(), EorE(el, srz->NoEnc).c_str()); + } + return; +} + +// Constructors +inline XML::XML() {} + +inline XML::XML(const char* file) { + UnicodeFile = false; + fname = file; + Load(file); +} + +inline XML::XML(const wchar_t* file) { + UnicodeFile = true; + fname = XMLU(file); + Load(file); +} + +inline XML::XML(const char* mem, size_t l) { + UnicodeFile = false; + Parse(mem, l); +} + +inline void XML::operator=(const char* d) { + UnicodeFile = false; + Parse(d, strlen(d)); +} + +inline XML::XML(const XML& x) { + operator=(x); +} + +inline void XML::operator=(const XML& x) { + UnicodeFile = x.UnicodeFile; + fname = x.fname; + hdr = x.hdr; + doctype = x.doctype; + + // comments + hdrcomments.reserve(x.hdrcomments.size()); + for (auto a: x.hdrcomments) { + shared_ptr c = make_shared(XMLComment(a->ep, a->v.c_str())); + hdrcomments.push_back(c); + } + + // Root + root = x.root; +} + +inline XML::XML(XML&& x) { + operator=(std::forward(x)); +} + +inline void XML::operator=(XML&& x) { + UnicodeFile = x.UnicodeFile; + fname = x.fname; + hdr = x.hdr; + doctype = x.doctype; + hdrcomments = x.hdrcomments; + root = x.GetRootElement().Mirror(); +} + +// Savers +inline size_t XML::SaveFP(FILE* fp) const { + string s = Serialize(); + size_t sz = fwrite(s.c_str(), 1, s.length(), fp); + fclose(fp); + return sz; +} + +inline XML_ERROR XML::Save() const { + if (UnicodeFile) return Save(XMLU(fname.c_str()).operator const wchar_t*()); + return Save(fname.c_str()); +} + +inline XML_ERROR XML::Save(const char* f, XMLSerialization* se) const { + if (!f) f = fname.c_str(); + FILE* fp = 0; + fopen_s(&fp, f, "wb"); + if (!fp) return XML_ERROR::SAVEFAILED; + string s = Serialize(se); + if (hdr.GetEncoding() == "UTF-16") { + fwrite("\xFE\xFF", 1, 3, fp); + wstring s2 = XMLU(s.c_str()).operator const wchar_t*(); + size_t sz = fwrite(s2.data(), 1, s.length() * 2, fp); + fclose(fp); + if (sz != s.length() * 2) return XML_ERROR::SAVEFAILED; + } else { + fwrite("\xEF\xBB\xBF", 1, 3, fp); + size_t sz = fwrite(s.c_str(), 1, s.length(), fp); + fclose(fp); + if (sz != s.length()) return XML_ERROR::SAVEFAILED; + } + + return XML_ERROR::OK; +} + +inline XML_ERROR XML::Save(const wchar_t* f, XMLSerialization* se) const { +#ifdef _MSC_VER + XMLU wf(fname.c_str()); + FILE* fp = 0; + _wfopen_s(&fp, f ? f : wf.operator const wchar_t*(), L"wb"); + if (!fp) return XML_ERROR::SAVEFAILED; + string s = Serialize(se); + if (hdr.GetEncoding() == "UTF-16") { + fwrite("\xFE\xFF", 1, 3, fp); + wstring s2 = XMLU(s.c_str()).operator const wchar_t*(); + size_t sz = fwrite(s2.data(), 1, s.length() * 2, fp); + fclose(fp); + if (sz != s.length() * 2) return XML_ERROR::SAVEFAILED; + } else { + fwrite("\xEF\xBB\xBF", 1, 3, fp); + size_t sz = fwrite(s.c_str(), 1, s.length(), fp); + fclose(fp); + if (sz != s.length()) return XML_ERROR::SAVEFAILED; + } + + return XML_ERROR::OK; +#else + return XML_ERROR::SAVEFAILED; +#endif +} + +// Loaders +inline XML_PARSE XML::ParseFile(FILE* fp) { + fseek(fp, 0, SEEK_END); + size_t S = ftell(fp); + fseek(fp, 0, SEEK_SET); + + BXML b; + b.Resize(S + 1); + fread(b.p(), 1, S, fp); + fclose(fp); + + // Check if this XML we loaded is a unicode XML +#ifdef _WIN32 + unsigned char a1 = b.p()[0]; + unsigned char a2 = b.p()[1]; + unsigned char a3 = b.p()[2]; + if (a1 == 0xFF && a2 == 0xFE) { + BXML b2; + b2.Resize(S * 4 + 32); + WideCharToMultiByte(CP_UTF8, 0, (wchar_t*)b.p(), (int)-1, b2.p(), (int)(S * 4 + 32), 0, 0); + b = b2; + } + // UTF-8 BOM? + // fwrite("\xEF\xBB\xBF",1,3,fp); + // + if (a1 == 0xEF && a2 == 0xBB && a3 == 0xBF) { + BXML b2; + b2.Resize(S); + memcpy(b2.p(), b.p() + 3, S - 3); + + b = b2; + } +#endif + + // b is the text + const char* bb = (const char*)b.p(); + return Parse(bb, strlen(bb)); +} + +inline XML_PARSE XML::Load(const char* f) { + Clear(); + FILE* fp = 0; + fopen_s(&fp, f, "rb"); + if (!fp) return XML_PARSE::OPENFAILED; + return ParseFile(fp); +} + +inline XML_PARSE XML::Load(const wchar_t* f) { +#ifdef _MSC_VER + Clear(); + FILE* fp = 0; + _wfopen_s(&fp, f, L"rb"); + if (!fp) return XML_PARSE::OPENFAILED; + return ParseFile(fp); +#else + return XML_PARSE::OPENFAILED; +#endif +} + +inline XML_PARSE XML::Parse(const char* m, size_t len) { + Clear(); + // TICKCOUNT c("Parse"); + hdrcomments.reserve(3); + + enum class PARSEELEMENT { + STARTHDR, + ENDHDR, + STARTDOCTYPE, + ENDDOCTYPE, + STARTVAR, + ENDVAR, + STARTCOMMENT, + ENDCOMMENT, + STARTCD, + ENDCD, + STARTEL, + ENDEL, + ENDDOCUMENT, + STARTQ, + ENDQ, + }; + + int InCData = 0; + int InComment = 0; + int InHeader = 0; + int InQ = 0; + string t1; + t1.reserve(100000); + XMLElement* l = 0; + int OutsideElementLine = 1; + int InVar = 0; // 1 got =, 2 got ", + int InDocType = 0; + XMLVariable cv; + XMLCData cd; + XMLComment comm; + bool PendingElName = false; + bool PendingElNameClosing = false; + + auto ParseFunc = [&](PARSEELEMENT pe) -> XML_PARSE { + // TICKCOUNT c("ParseFunc"); + XML_PARSE e = XML_PARSE::OK; + +#ifdef _DEBUG + // string adsb = Serialize(); +#endif + + if (pe == PARSEELEMENT::STARTHDR) { + if (InHeader > 0) return XML_PARSE::HDRERROR; + InHeader = 1; + } + if (pe == PARSEELEMENT::STARTEL) { + if (!l) + l = &root; + else { + // XMLElement ne; + XMLElement& ne2 = l->AddElement("el"); + l = &ne2; + } + PendingElName = true; + } + if (pe == PARSEELEMENT::ENDEL) { + if (l) { + l = l->GetParent(&root); + } + } + + if (pe == PARSEELEMENT::ENDHDR) { + if (InHeader != 1) return XML_PARSE::HDRERROR; + InHeader = 2; + } + if (pe == PARSEELEMENT::STARTDOCTYPE) { + if (InDocType > 0) return XML_PARSE::HDRERROR; + InDocType = 1; + } + if (pe == PARSEELEMENT::ENDDOCTYPE) { + if (InDocType != 1) return XML_PARSE::HDRERROR; + InDocType = 2; + doctype.SetValue(Decode(t1.c_str()).c_str()); + t1.clear(); + } + if (pe == PARSEELEMENT::STARTCOMMENT) { + if (InComment != 0) return XML_PARSE::COMMENTERROR; + InComment = 1; + } + if (pe == PARSEELEMENT::STARTCD) { + if (InCData != 0 || !l) return XML_PARSE::COMMENTERROR; + InCData = 1; + } + + if (pe == PARSEELEMENT::ENDCOMMENT) { + if (InComment != 1) return XML_PARSE::COMMENTERROR; + InComment = 0; + if (l) comm.SetEP(l->GetChildren().size()); + comm.SetValue(Decode(t1.c_str()).c_str()); + t1.clear(); + if (l) + l->AddComment(comm.GetValue().c_str(), comm.GetEP()); + else + hdrcomments.push_back(make_shared(XMLComment(comm))); + } + + if (pe == PARSEELEMENT::ENDCD) { + if (InCData != 1 || !l) return XML_PARSE::COMMENTERROR; + InCData = 0; + cd.SetEP(l->GetChildren().size()); + cd.SetValue(Decode(t1.c_str()).c_str()); + t1.clear(); + l->AddCData(cd.GetValue().c_str(), cd.GetEP()); + } + // Header var + if (pe == PARSEELEMENT::STARTVAR && InHeader == 1) { + if (InVar != 0) return XML_PARSE::VARERROR; + cv.Clear(); + InVar = 1; + cv.SetName(Decode(t1.c_str()).c_str()); + t1.clear(); + } + if (pe == PARSEELEMENT::ENDVAR && InHeader == 1) { + if (InVar != 2) return XML_PARSE::VARERROR; + InVar = 0; + cv.SetValue(Decode(t1.c_str()).c_str()); + t1.clear(); + if (cv.GetName() == "version") + hdr.GetVersion() = cv; + else if (cv.GetName() == "encoding") + hdr.GetEncoding() = cv; + else if (cv.GetName() == "standalone") + hdr.GetStandalone() = cv; + else + return e; // ignore it... return XML_PARSE::HDRERROR; + cv.Clear(); + } + + // Element var + if (pe == PARSEELEMENT::STARTVAR && l) { + if (InVar != 0) return XML_PARSE::VARERROR; + cv.Clear(); + InVar = 1; + cv.SetName(Decode(t1.c_str()).c_str()); + t1.clear(); + } + if (pe == PARSEELEMENT::ENDVAR && l) { + if (InVar != 2) return XML_PARSE::VARERROR; + InVar = 0; + cv.SetValue(Decode(t1.c_str()).c_str()); + t1.clear(); + l->AddVariable(cv.GetName().c_str(), cv.GetValue().c_str()); + cv.Clear(); + } + + return e; + }; + + XML_PARSE e = XML_PARSE::OK; + for (size_t i = 0; i < len;) { + const char* mm = m + i; + bool IsSpace = false; + if (m[i] == ' ' || m[i] == '\t' || m[i] == '\r' || m[i] == '\n') IsSpace = true; + + // Opti CData + if (InCData) { + const char* s1 = strstr(mm, "]]>"); + if (!s1) { + e = XML_PARSE::COMMENTERROR; + break; + } + t1.assign(mm, s1 - mm); + i += t1.length(); + e = ParseFunc(PARSEELEMENT::ENDCD); + if (e != XML_PARSE::OK) break; + i += 3; + continue; + } + // Opti Comment + if (InComment) { + const char* s1 = strstr(mm, "-->"); + if (!s1) { + e = XML_PARSE::COMMENTERROR; + break; + } + t1.assign(mm, s1 - mm); + i += t1.length(); + e = ParseFunc(PARSEELEMENT::ENDCOMMENT); + if (e != XML_PARSE::OK) break; + i += 3; + continue; + } + + // El name? + if (l && PendingElName && IsSpace && !t1.empty()) { + l->SetElementName(Trim(t1.c_str()).c_str()); + PendingElName = false; + t1.clear(); + i++; + continue; + } + + // Header test + bool CanHeader = false; + if (!InComment && !InCData && l == 0) CanHeader = true; + if (CanHeader) { + if (_strnicmp(mm, "", 7) == 0) { + e = ParseFunc(PARSEELEMENT::STARTHDR); + if (e != XML_PARSE::OK) break; + i += 5; + continue; + } + if (_strnicmp(mm, "?>", 2) == 0) { + e = ParseFunc(PARSEELEMENT::ENDHDR); + if (e != XML_PARSE::OK) break; + i += 2; + continue; + } + if (_strnicmp(mm, "", 1) == 0 && InDocType == 1) { + e = ParseFunc(PARSEELEMENT::ENDDOCTYPE); + if (e != XML_PARSE::OK) break; + i += 1; + continue; + } + } + + // Comment Tests + bool CanComment = true; + if (l && OutsideElementLine == 0) CanComment = false; + if (InCData) CanComment = false; + if (CanComment) { + if (_strnicmp(mm, "", 3) == 0 && InComment) { + e = ParseFunc(PARSEELEMENT::ENDCOMMENT); + if (e != XML_PARSE::OK) break; + i += 3; + continue; + } + } + + // CData Tests + bool CanCData = true; + if (l && OutsideElementLine == 0) CanCData = false; + if (CanCData) { + if (_strnicmp(mm, "", 3) == 0 && InCData) { + e = ParseFunc(PARSEELEMENT::ENDCD); + if (e != XML_PARSE::OK) break; + i += 3; + continue; + } + } + + // Variable Tests + bool CanVariable = false; + if (!InComment && !InCData && !InQ) { + if (InHeader == 1) + CanVariable = true; + else if (l && OutsideElementLine == 0) + CanVariable = true; + } + if (_strnicmp(mm, "=", 1) == 0 && CanVariable && InVar != 2 && !InQ) { + e = ParseFunc(PARSEELEMENT::STARTVAR); + if (e != XML_PARSE::OK) break; + i += 1; + continue; + } + if (_strnicmp(mm, "\"", 1) == 0 && CanVariable && InVar == 1) { + t1.clear(); + InVar = 2; + i += 1; + continue; + } + if (_strnicmp(mm, "\"", 1) == 0 && CanVariable && InVar == 2) { + e = ParseFunc(PARSEELEMENT::ENDVAR); + if (e != XML_PARSE::OK) break; + i += 1; + continue; + } + + bool CanElement = false; + if (!InComment && !InCData && !InVar && InDocType != 1) CanElement = true; + + if (CanElement) { + if (_strnicmp(mm, "AddContent(t1.c_str(), (size_t)-1); + t1.clear(); + } + + e = ParseFunc(PARSEELEMENT::ENDEL); + if (e != XML_PARSE::OK) break; + t1.clear(); + i += 2; + PendingElNameClosing = true; + continue; + } + + if (_strnicmp(mm, "AddContent(t1.c_str(), l->GetChildren().size()); + t1.clear(); + } + + e = ParseFunc(PARSEELEMENT::STARTEL); + if (e != XML_PARSE::OK) break; + OutsideElementLine = 0; + i += 1; + continue; + } + + if (_strnicmp(mm, "/>", 2) == 0 || _strnicmp(mm, "?>", 2) == 0) { + // El name? + if (l && PendingElName && !t1.empty()) { + l->SetElementName(Trim(t1.c_str()).c_str()); + PendingElName = false; + } + + e = ParseFunc(PARSEELEMENT::ENDEL); + + if (e != XML_PARSE::OK) break; + OutsideElementLine = 1; + InQ = 0; + i += 2; + continue; + } + + if (_strnicmp(mm, ">", 1) == 0 && (OutsideElementLine == 0 || PendingElNameClosing) && l) { + // El name? + if (PendingElName) { + l->SetElementName(Trim(t1.c_str()).c_str()); + PendingElName = false; + t1.clear(); + i++; + OutsideElementLine = 1; + continue; + } + // El name? + if (PendingElNameClosing) { + //* + PendingElNameClosing = false; + t1.clear(); + i++; + OutsideElementLine = 1; + continue; + } + i++; + OutsideElementLine = 1; + continue; + } + } + + if (IsSpace && t1.empty() && !InComment && !InCData) { + + } else + t1 += m[i]; + + i++; + continue; + } + + return e; +} + +// Gets +inline XMLElement& XML::GetRootElement() { + return root; +} + +inline XMLHeader& XML::GetHeader() { + return hdr; +} + +inline size_t XML::MemoryUsage() { + return 0; + // return root.MemoryUsage() + GetHeader().MemoryUsage(); +} + +// Sets +inline void XML::SetRootElement(XMLElement& newroot) { + root = newroot; +} + +inline void XML::SetHeader(const XMLHeader& h) { + hdr = h; +} + +inline void XML::Clear() { + root.clear(); + hdrcomments.clear(); + doctype.Clear(); + hdr.Default(); +} + +inline void XML::Version(XML_VERSION_INFO* x) { + x->VersionLow = (XML3_VERSION & 0xFF); + x->VersionHigh = (XML3_VERSION >> 8); +#ifdef _MSC_VER + strcpy_s(x->RDate, sizeof(x->RDate), XML3_VERSION_REVISION_DATE); +#else + strcpy(x->RDate, XML3_VERSION_REVISION_DATE); +#endif +} + +// ser +inline string XML::Serialize(XMLSerialization* srz) const { + XMLSerialization defsrz; + if (!srz) srz = &defsrz; + + string v; + v.reserve(100000); + + v += hdr.Serialize(); + if (!srz->NoCRLF && !srz->Canonical) v += "\r\n"; + if (doctype.GetValue().length()) { + v += doctype.Serialize(); + if (!srz->NoCRLF) v += "\r\n"; + } + for (auto a: hdrcomments) { + v += a->Serialize(); + if (!srz->NoCRLF) v += "\r\n"; + } + XMLSerialization s2 = *srz; + s2.ExcludeSelf = false; + s2.deep = 0; + root.Serialize(v, &s2); + return v; +} + +}; // namespace XML3 +#endif diff --git a/docs/json/general.json b/docs/json/general.json index aa58ccc..0b51e7c 100644 --- a/docs/json/general.json +++ b/docs/json/general.json @@ -63,6 +63,12 @@ "description": "Index of the user who launched the game, should be less than \"onlineUsers\" or equal.", "minimum": 1, "maximum": 4 + }, + "trophyKey": { + "type": "string", + "description": "ERK trophy key in hex format. You should dump it from your console, otherwise trophies will not work.", + "pattern": "^(0x)?[0-9A-Fa-f]*$", + "maximum": 32 } }, "required": [ @@ -70,6 +76,7 @@ "netMAC", "profiles", "systemlang", - "userIndex" + "userIndex", + "trophyKey" ] } diff --git a/docs/wiki/markdown/Config-files.md b/docs/wiki/markdown/Config-files.md index d8382f8..66736ba 100644 --- a/docs/wiki/markdown/Config-files.md +++ b/docs/wiki/markdown/Config-files.md @@ -73,6 +73,7 @@ You can change the layout now in _controls.json_, if you want to. "systemlang": 0, // System langauge, see the list below to get these indexes "netEnabled": false, // Wether enable networking features or not. Better to keep it disabled, not ready for the actual use. "netMAC": "00:00:00:00:00:00", // Your ethernet adapter's MAC address. Zero-MAC means "first usable device". + "trophyKey": "", // ERK trophy key in hex format. You should dump it from your console, otherwise trophies will not work. "onlineUsers": 1, // Number of authorized users, 1..4 "userIndex": 1, // Id of the user that started the game "profiles": [ // User profiles, you can change your name there diff --git a/modules/libSceNpTrophy/CMakeLists.txt b/modules/libSceNpTrophy/CMakeLists.txt index 35c84a0..84ce6a4 100644 --- a/modules/libSceNpTrophy/CMakeLists.txt +++ b/modules/libSceNpTrophy/CMakeLists.txt @@ -6,4 +6,7 @@ project(${libName}) add_library(${libName} SHARED entry.cpp) -setupModule(${libName}) \ No newline at end of file +add_dependencies(${libName} core) +target_link_libraries(${libName} PRIVATE core.lib) + +setupModule(${libName}) diff --git a/modules/libSceNpTrophy/codes.h b/modules/libSceNpTrophy/codes.h index 71964f3..59c2f45 100644 --- a/modules/libSceNpTrophy/codes.h +++ b/modules/libSceNpTrophy/codes.h @@ -2,18 +2,26 @@ #include namespace Err { -constexpr int32_t SCE_NP_TROPHY_INVALID_HANDLE = (-1); -constexpr int32_t SCE_NP_TROPHY_INVALID_CONTEXT = (-1); -constexpr int32_t SCE_NP_TROPHY_INVALID_TROPHY_ID = (-1); -constexpr int32_t SCE_NP_TROPHY_INVALID_GROUP_ID = (-2); -constexpr int32_t SCE_NP_TROPHY_BASE_GAME_GROUP_ID = (-1); +namespace NpTrophy { +constexpr int32_t INVALID_ARGUMENT = -2141907452; +constexpr int32_t ALREADY_EXISTS = -2141907437; +constexpr int32_t EXCEEDS_MAX = -2141907422; +} // namespace NpTrophy } // namespace Err -constexpr uint32_t SCE_NP_TROPHY_NUM_MAX = (128); -constexpr uint32_t SCE_NP_TROPHY_SCREENSHOT_TARGET_NUM_MAX = (4); -constexpr uint32_t SCE_NP_TROPHY_GAME_TITLE_MAX_SIZE = (128); -constexpr uint32_t SCE_NP_TROPHY_GAME_DESCR_MAX_SIZE = (1024); -constexpr uint32_t SCE_NP_TROPHY_GROUP_TITLE_MAX_SIZE = (128); -constexpr uint32_t SCE_NP_TROPHY_GROUP_DESCR_MAX_SIZE = (1024); -constexpr uint32_t SCE_NP_TROPHY_NAME_MAX_SIZE = (128); -constexpr uint32_t SCE_NP_TROPHY_DESCR_MAX_SIZE = (1024); +namespace NpTrophy { +constexpr int32_t INVALID_HANDLE = -1; +constexpr int32_t INVALID_CONTEXT = -1; +constexpr int32_t INVALID_TROPHY_ID = -1; +constexpr int32_t INVALID_GROUP_ID = -2; +constexpr int32_t BASE_GAME_GROUP_ID = -1; + +constexpr uint32_t NUM_MAX = (128); +constexpr uint32_t SCREENSHOT_TARGET_NUM_MAX = (4); +constexpr uint32_t GAME_TITLE_MAX_SIZE = (128); +constexpr uint32_t GAME_DESCR_MAX_SIZE = (1024); +constexpr uint32_t GROUP_TITLE_MAX_SIZE = (128); +constexpr uint32_t GROUP_DESCR_MAX_SIZE = (1024); +constexpr uint32_t NAME_MAX_SIZE = (128); +constexpr uint32_t DESCR_MAX_SIZE = (1024); +} // namespace NpTrophy diff --git a/modules/libSceNpTrophy/entry.cpp b/modules/libSceNpTrophy/entry.cpp index 00210c9..d6ba164 100644 --- a/modules/libSceNpTrophy/entry.cpp +++ b/modules/libSceNpTrophy/entry.cpp @@ -1,11 +1,22 @@ #include "../libSceNpManager/types.h" #include "common.h" +#include "core/fileManager/fileManager.h" +#include "core/trophies/trophies.h" #include "logging.h" #include "types.h" +#include + LOG_DEFINE_MODULE(libSceNpTrophy); -namespace {} // namespace +namespace { +struct trp_context { + bool created; + SceNpServiceLabel label; +}; + +static trp_context contexts[4] = {}; +} // namespace extern "C" { @@ -25,7 +36,20 @@ EXPORT SYSV_ABI int sceNpTrophyAbortHandle(SceNpTrophyHandle handle) { } EXPORT SYSV_ABI int sceNpTrophyCreateContext(SceNpTrophyContext* context, int32_t userId, SceNpServiceLabel serviceLabel, uint64_t options) { - *context = 1; + auto& ctx = contexts[userId]; + if (ctx.created) return Err::NpTrophy::ALREADY_EXISTS; + // static std::once_flag init; + // std::call_once(init, []() { + // ITrophies::trp_ent_cb ent = {.func = [](ITrophies::trp_ent_cb::data_t* data) { + // LOG_USE_MODULE(libSceNpTrophy); + // LOG_ERR(L"Trophy! %S: %S (id:%d)", data->name.c_str(), data->detail.c_str(), data->id); + // return false; // Do not cancel this callback + // }}; + // accessTrophies().parseTRP(nullptr, &ent); + // }); + ctx.created = true; + ctx.label = serviceLabel; + *context = userId; return Ok; } @@ -38,7 +62,7 @@ EXPORT SYSV_ABI int sceNpTrophyRegisterContext(SceNpTrophyContext context, SceNp } EXPORT SYSV_ABI int sceNpTrophyUnlockTrophy(SceNpTrophyContext context, SceNpTrophyHandle handle, SceNpTrophyId trophyId, SceNpTrophyId* platinumId) { - *platinumId = Err::SCE_NP_TROPHY_INVALID_TROPHY_ID; + *platinumId = NpTrophy::INVALID_TROPHY_ID; return Ok; } @@ -147,4 +171,4 @@ EXPORT SYSV_ABI int sceNpTrophyShowTrophyList(SceNpTrophyContext context, SceNpT EXPORT SYSV_ABI int sceNpTrophyCaptureScreenshot(SceNpTrophyHandle handle, const SceNpTrophyScreenshotTarget* targets, uint32_t numTargets) { return Ok; } -} \ No newline at end of file +} diff --git a/modules/libSceNpTrophy/types.h b/modules/libSceNpTrophy/types.h index 9de35bd..43abadf 100644 --- a/modules/libSceNpTrophy/types.h +++ b/modules/libSceNpTrophy/types.h @@ -54,8 +54,8 @@ struct SceNpTrophyGameDetails { uint32_t numGold; uint32_t numSilver; uint32_t numBronze; - char title[SCE_NP_TROPHY_GAME_TITLE_MAX_SIZE]; - char description[SCE_NP_TROPHY_GAME_DESCR_MAX_SIZE]; + char title[NpTrophy::GAME_TITLE_MAX_SIZE]; + char description[NpTrophy::GAME_DESCR_MAX_SIZE]; }; struct SceNpTrophyGameData { @@ -76,8 +76,8 @@ struct SceNpTrophyGroupDetails { uint32_t numGold; uint32_t numSilver; uint32_t numBronze; - char title[SCE_NP_TROPHY_GROUP_TITLE_MAX_SIZE]; - char description[SCE_NP_TROPHY_GROUP_DESCR_MAX_SIZE]; + char title[NpTrophy::GROUP_TITLE_MAX_SIZE]; + char description[NpTrophy::GROUP_DESCR_MAX_SIZE]; }; struct SceNpTrophyGroupData { @@ -99,8 +99,8 @@ struct SceNpTrophyDetails { SceNpTrophyGroupId groupId; bool hidden; uint8_t reserved[3]; - char name[SCE_NP_TROPHY_NAME_MAX_SIZE]; - char description[SCE_NP_TROPHY_DESCR_MAX_SIZE]; + char name[NpTrophy::NAME_MAX_SIZE]; + char description[NpTrophy::DESCR_MAX_SIZE]; }; struct SceNpTrophyData { diff --git a/tools/config_emu/config_emu.cpp b/tools/config_emu/config_emu.cpp index 08e1014..921449c 100644 --- a/tools/config_emu/config_emu.cpp +++ b/tools/config_emu/config_emu.cpp @@ -269,6 +269,7 @@ Config::Config() { {"systemlang", 1u}, {"netEnabled", false}, {"netMAC", "00:00:00:00:00:00"}, + {"trophyKey", ""}, {"userIndex", 1u}, {"onlineUsers", 1u}, {"profiles", defprofiles},