mirror of
https://github.com/SysRay/psOff_public.git
synced 2024-11-23 06:19:41 +00:00
Trophies file parser
This commit is contained in:
parent
f204f20b49
commit
e8d4f46bdf
@ -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
|
||||
$<TARGET_OBJECTS:timer>
|
||||
$<TARGET_OBJECTS:systemContent>
|
||||
$<TARGET_OBJECTS:networking>
|
||||
$<TARGET_OBJECTS:trophies>
|
||||
$<TARGET_OBJECTS:videoout>
|
||||
$<TARGET_OBJECTS:fileManager>
|
||||
$<TARGET_OBJECTS:memory>
|
||||
@ -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}
|
||||
|
5
core/trophies/CMakeLists.txt
Normal file
5
core/trophies/CMakeLists.txt
Normal file
@ -0,0 +1,5 @@
|
||||
add_library(trophies OBJECT
|
||||
trophies.cpp
|
||||
)
|
||||
|
||||
add_dependencies(trophies third_party)
|
388
core/trophies/trophies.cpp
Normal file
388
core/trophies/trophies.cpp
Normal file
@ -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 <fstream>
|
||||
#include <openssl/evp.h>
|
||||
|
||||
#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<unsigned char>(a)) == std::tolower(static_cast<unsigned char>(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, "<!--Sce-Np-Trophy-Signature", 27) != 0) {
|
||||
EVP_CIPHER_CTX_free(data_ctx);
|
||||
return false;
|
||||
}
|
||||
|
||||
// We found valid NPID, now we can continue our thing
|
||||
::memcpy(mem, outbuffer, outlen);
|
||||
char* mem_off = mem + outlen;
|
||||
trfile.seekg(ent.pos + 64);
|
||||
|
||||
size_t copied;
|
||||
while ((copied = size_t(mem_off - mem)) < ent.len) {
|
||||
size_t len = std::min(ent.len - copied, sizeof(inbuffer));
|
||||
if (!trfile.read((char*)inbuffer, len)) {
|
||||
EVP_CIPHER_CTX_free(data_ctx);
|
||||
return false;
|
||||
}
|
||||
if (!EVP_DecryptUpdate(data_ctx, (uint8_t*)mem_off, &outlen, inbuffer, len)) {
|
||||
EVP_CIPHER_CTX_free(data_ctx);
|
||||
return false;
|
||||
}
|
||||
|
||||
mem_off += outlen;
|
||||
}
|
||||
|
||||
EVP_CIPHER_CTX_free(data_ctx);
|
||||
*mem_off = '\0'; // Finally
|
||||
}
|
||||
//- Data decipher context
|
||||
|
||||
return true;
|
||||
}; // lambda: trydecrypt
|
||||
|
||||
int npid;
|
||||
bool success = false;
|
||||
auto npid_path = accessFileManager().getGameFilesDir() / ".npcommid";
|
||||
|
||||
{
|
||||
std::ifstream npid_f(npid_path);
|
||||
if (npid_f.is_open()) {
|
||||
npid_f >> npid;
|
||||
success = trydecrypt(npid);
|
||||
}
|
||||
}
|
||||
|
||||
if (success == false) {
|
||||
for (uint32_t n = 0; n < 99999; n++) {
|
||||
trfile.seekg(ent.pos + TR_AES_BLOCK_SIZE);
|
||||
if ((success = trydecrypt(n)) == true) {
|
||||
npid = n;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (success == true) {
|
||||
std::ofstream npid_f(npid_path, std::ios::out);
|
||||
if (npid_f.is_open()) {
|
||||
npid_f << npid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Restore cursor position to file list
|
||||
trfile.seekg(ppos);
|
||||
|
||||
if (success == false) {
|
||||
LOG_ERR(L"Failed to guess ID for trophy file");
|
||||
delete[] mem;
|
||||
return ErrCodes::DECRYPT;
|
||||
}
|
||||
}
|
||||
|
||||
XML3::XML xml(mem, strlen(mem));
|
||||
delete[] mem;
|
||||
|
||||
{ // xml parser
|
||||
auto& rootel = xml.GetRootElement();
|
||||
if (rootel.GetElementName() == "trophyconf") {
|
||||
for (auto& chel: rootel.GetChildren()) {
|
||||
auto& cheln = chel->GetElementName();
|
||||
if (trpcb != nullptr && cheln == "trophy") {
|
||||
trpcb->data.id = -1;
|
||||
trpcb->data.group = -1;
|
||||
trpcb->data.type = 0xFF;
|
||||
trpcb->data.name.clear();
|
||||
trpcb->data.detail.clear();
|
||||
|
||||
for (auto& chvar: chel->GetVariables()) {
|
||||
auto& vname = chvar->GetName();
|
||||
if (vname == "id") {
|
||||
trpcb->data.id = chvar->GetValueInt(-1);
|
||||
} else if (vname == "hidden") {
|
||||
trpcb->data.hidden = chvar->GetValue() != "no";
|
||||
} else if (vname == "ttype") {
|
||||
trpcb->data.type = chvar->GetValue().at(0);
|
||||
} else if (vname == "gid") {
|
||||
trpcb->data.group = chvar->GetValueInt(-1);
|
||||
}
|
||||
}
|
||||
|
||||
// There is no `name` and `detail` fields if we read TROPCONF.ESFM
|
||||
if (lightweight == true) continue;
|
||||
|
||||
for (auto& chch: chel->GetChildren()) {
|
||||
auto& cname = chch->GetElementName();
|
||||
if (cname == "name") {
|
||||
trpcb->data.name.assign(chch->GetContent());
|
||||
} else if (cname == "detail") {
|
||||
trpcb->data.detail.assign(chch->GetContent());
|
||||
}
|
||||
}
|
||||
|
||||
trpcb->cancelled = trpcb->func(&trpcb->data);
|
||||
} else if (grpcb != nullptr && cheln == "group") {
|
||||
for (auto& chvar: chel->GetVariables()) {
|
||||
auto& vname = chvar->GetName();
|
||||
if (vname == "id") {
|
||||
grpcb->data.id = chvar->GetValueInt(-1);
|
||||
}
|
||||
|
||||
// There is no `name` and `detail` fields if we read TROPCONF.ESFM
|
||||
if (lightweight == true) continue;
|
||||
|
||||
for (auto& chch: chel->GetChildren()) {
|
||||
auto& cname = chch->GetElementName();
|
||||
if (cname == "name") {
|
||||
grpcb->data.name.assign(chch->GetContent());
|
||||
} else if (cname == "detail") {
|
||||
grpcb->data.detail.assign(chch->GetContent());
|
||||
}
|
||||
}
|
||||
|
||||
grpcb->cancelled = grpcb->func(&grpcb->data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We already parsed trophy info, we don't need more of it
|
||||
if (trpcb != nullptr) trpcb->cancelled = true;
|
||||
if (grpcb != nullptr) grpcb->cancelled = true;
|
||||
} // element: trophyconf
|
||||
} // xml parser
|
||||
} // group & trophy callbacks
|
||||
} // entries loop
|
||||
|
||||
return ErrCodes::SUCCESS;
|
||||
} // trfile.is_open()
|
||||
|
||||
return ErrCodes::NO_TROPHIES;
|
||||
} // parseTRP()
|
||||
};
|
||||
|
||||
ITrophies& accessTrophies() {
|
||||
static Trophies ti;
|
||||
return ti;
|
||||
}
|
80
core/trophies/trophies.h
Normal file
80
core/trophies/trophies.h
Normal file
@ -0,0 +1,80 @@
|
||||
#pragma once
|
||||
|
||||
#include "utility/utility.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
class ITrophies {
|
||||
CLASS_NO_COPY(ITrophies);
|
||||
CLASS_NO_MOVE(ITrophies);
|
||||
|
||||
protected:
|
||||
ITrophies() = default;
|
||||
|
||||
public:
|
||||
~ITrophies() = default;
|
||||
|
||||
enum class ErrCodes {
|
||||
SUCCESS = 0,
|
||||
NO_KEY_SET,
|
||||
INVALID_MAGIC,
|
||||
INVALID_VERSION,
|
||||
INVALID_ENTSIZE,
|
||||
INVALID_AES,
|
||||
NOT_IMPLEMENTED,
|
||||
IO_FAIL,
|
||||
NO_CALLBACKS,
|
||||
NO_PNG,
|
||||
DECRYPT,
|
||||
NO_TROPHIES,
|
||||
};
|
||||
|
||||
struct trp_grp_cb {
|
||||
struct data_t {
|
||||
int32_t id;
|
||||
std::string name;
|
||||
std::string detail;
|
||||
} data;
|
||||
|
||||
bool cancelled;
|
||||
std::function<bool(data_t*)> func;
|
||||
};
|
||||
|
||||
struct trp_ent_cb {
|
||||
struct data_t {
|
||||
int32_t id;
|
||||
int32_t group;
|
||||
bool hidden;
|
||||
uint8_t type;
|
||||
std::string name;
|
||||
std::string detail;
|
||||
} data;
|
||||
|
||||
bool cancelled;
|
||||
std::function<bool(data_t*)> func;
|
||||
};
|
||||
|
||||
struct trp_png_cb {
|
||||
struct data_t {
|
||||
void* pngdata;
|
||||
size_t pngsize;
|
||||
} data;
|
||||
|
||||
bool cancelled;
|
||||
std::function<bool(data_t*)> func;
|
||||
};
|
||||
|
||||
virtual ErrCodes parseTRP(trp_grp_cb* grpcb = nullptr, trp_ent_cb* trpcb = nullptr, trp_png_cb* pngcb = nullptr, bool lightweight = false) = 0;
|
||||
};
|
||||
|
||||
#if defined(__APICALL_EXTERN)
|
||||
#define __APICALL __declspec(dllexport)
|
||||
#elif defined(__APICALL_IMPORT)
|
||||
#define __APICALL __declspec(dllimport)
|
||||
#else
|
||||
#define __APICALL
|
||||
#endif
|
||||
__APICALL ITrophies& accessTrophies();
|
||||
#undef __APICALL
|
2706
core/trophies/xml3all.h
Normal file
2706
core/trophies/xml3all.h
Normal file
File diff suppressed because it is too large
Load Diff
@ -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"
|
||||
]
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -6,4 +6,7 @@ project(${libName})
|
||||
|
||||
add_library(${libName} SHARED entry.cpp)
|
||||
|
||||
add_dependencies(${libName} core)
|
||||
target_link_libraries(${libName} PRIVATE core.lib)
|
||||
|
||||
setupModule(${libName})
|
@ -2,18 +2,26 @@
|
||||
#include <stdint.h>
|
||||
|
||||
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
|
||||
|
@ -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 <fstream>
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -269,6 +269,7 @@ Config::Config() {
|
||||
{"systemlang", 1u},
|
||||
{"netEnabled", false},
|
||||
{"netMAC", "00:00:00:00:00:00"},
|
||||
{"trophyKey", ""},
|
||||
{"userIndex", 1u},
|
||||
{"onlineUsers", 1u},
|
||||
{"profiles", defprofiles},
|
||||
|
Loading…
Reference in New Issue
Block a user