+ sceNpTrophyGetGameInfo implementation

This commit is contained in:
igor725 2024-05-04 10:55:22 +03:00
parent 35eb9eecdb
commit d5e8d1a122
No known key found for this signature in database
GPG Key ID: 46F13BBE46F8569D
4 changed files with 131 additions and 70 deletions

View File

@ -67,10 +67,25 @@ class Trophies: public ITrophies {
ctx->itrop.data.title_name.clear();
ctx->itrop.data.title_detail.clear();
ctx->itrop.data.trophyset_version.clear();
ctx->itrop.data.trophy_count = 0;
ctx->itrop.data.group_count = 0;
}
for (auto& chel: rootel.GetChildren()) {
auto& cheln = chel->GetElementName();
if (cheln == "trophyset-version") {
ctx->itrop.data.trophyset_version.assign(chel->GetContent());
} else if (cheln == "title-name") {
ctx->itrop.data.title_name.assign(chel->GetContent());
} else if (cheln == "title-detail") {
ctx->itrop.data.title_detail.assign(chel->GetContent());
} else if (cheln == "trophy") {
++ctx->itrop.data.trophy_count;
} else if (cheln == "group") {
++ctx->itrop.data.group_count;
}
if (!ctx->entry.cancelled && cheln == "trophy") {
ctx->entry.data.id = -1;
ctx->entry.data.group = -1;
@ -81,11 +96,11 @@ class Trophies: public ITrophies {
for (auto& chvar: chel->GetVariables()) {
auto& vname = chvar->GetName();
if (vname == "id") {
ctx->entry.data.id = chvar->GetValueInt(-1);
if ((ctx->entry.data.id = chvar->GetValueInt(-1)) > 128) return ErrCodes::MAX_TROPHY_REACHED;
} else if (vname == "hidden") {
ctx->entry.data.hidden = chvar->GetValue() == "yes";
} else if (vname == "ttype") {
ctx->entry.data.type = chvar->GetValue().at(0);
ctx->entry.data.type = std::tolower(chvar->GetValue().at(0));
} else if (vname == "gid") {
ctx->entry.data.group = chvar->GetValueInt(-1);
}
@ -127,17 +142,6 @@ class Trophies: public ITrophies {
ctx->group.cancelled = ctx->group.func(&ctx->group.data);
}
}
if (!ctx->itrop.cancelled) {
if (cheln == "trophyset-version") {
ctx->itrop.data.trophyset_version.assign(chel->GetContent());
} else if (cheln == "title-name") {
ctx->itrop.data.title_name.assign(chel->GetContent());
} else if (cheln == "title-detail") {
ctx->itrop.data.title_detail.assign(chel->GetContent());
} else if (cheln == "trophy") {
++ctx->itrop.data.trophy_count;
}
}
}
// Pass the final trophyset info to itrop callback
@ -161,7 +165,7 @@ class Trophies: public ITrophies {
if (((ent.flag >> 24) & 0x03) == 0) {
ctx->pngim.data.pngsize = ent.len;
ctx->pngim.data.pngname.assign(ent.name);
ctx->pngim.data.pngdata = new char[ent.len]; // Developer should free this memory manually
ctx->pngim.data.pngdata = ::calloc(ent.len, 1); // Developer should free this memory manually
trfile.seekg(ent.pos);
if (trfile.read((char*)ctx->pngim.data.pngdata, ent.len)) {
ctx->pngim.cancelled = ctx->pngim.func(&ctx->pngim.data);
@ -418,15 +422,36 @@ class Trophies: public ITrophies {
} // trfile.is_open()
return ErrCodes::NO_TROPHIES;
} // parseTRP()
}
bool createContext(int32_t userId, uint32_t label) final {}
const char* getError(ErrCodes ec) final {
switch (ec) {
case ErrCodes::SUCCESS: return "No errors";
case ErrCodes::INVALID_CONTEXT: return "Passed context is nullptr";
case ErrCodes::NO_ITROP: return "Invalid combination: trying to parse extended trophyset info from lightweight file";
case ErrCodes::NO_KEY_SET: return "Invalid trophy key, decrypting is impossible";
case ErrCodes::INVALID_MAGIC: return "Invalid trophy file magic, trophy00.trp is likely corruted";
case ErrCodes::INVALID_VERSION: return "Unsupported trophy file version";
case ErrCodes::INVALID_ENTSIZE: return "Invalid trophy file entry size, trophy00.trp is likely corruted";
case ErrCodes::INVALID_AES: return "Trophy file contains unaligned AES blocks";
case ErrCodes::NOT_IMPLEMENTED: return "This feature is not implemented yet";
case ErrCodes::IO_FAIL: return "Your operating system reported IO failure";
case ErrCodes::NO_CALLBACKS: return "No callbacks passed to parser";
case ErrCodes::DECRYPT: return "Trophy file decryption failed";
case ErrCodes::NO_TROPHIES: return "Trophy file is likely missing or does not contain requested esfm file";
default: return "Unknown error code!";
}
}
bool getProgress(int32_t userId, uint32_t progress[4]) final {}
bool createContext(int32_t userId, uint32_t label) final { return false; }
bool unlockTrophy(int32_t userId, int32_t trophyId) final {}
bool destroyContext(int32_t userId) final { return false; }
bool resetUserInfo(int32_t userId) final {}
bool getProgress(int32_t userId, uint32_t progress[4], uint32_t* count) final { return false; }
bool unlockTrophy(int32_t userId, int32_t trophyId) final { return false; }
bool resetUserInfo(int32_t userId) final { return false; }
};
ITrophies& accessTrophies() {

View File

@ -17,20 +17,21 @@ class ITrophies {
~ITrophies() = default;
enum class ErrCodes {
SUCCESS = 0, // No errors, we're fine
INVALID_CONTEXT, // Context is nullptr
NO_ITROP, // There are no trophyset info in lightweight file
CONTINUE, // Not an actual error code. For internal usage only!
NO_KEY_SET, // No root key installed
INVALID_MAGIC, // TRP file has invalid magic in its header
INVALID_VERSION, // TRP file version is not valid
INVALID_ENTSIZE, // TRP file has bigger entries
INVALID_AES, // TRP file contains unaligned AES blocks
NOT_IMPLEMENTED, // This feature is not implemented yet
IO_FAIL, // Failed to read TRP file
NO_CALLBACKS, // Parser called with no callbacks, it's pointless
DECRYPT, // TRP file decryption failed
NO_TROPHIES, // Failed to open TRP file or the said file does not contain any esfm file
SUCCESS = 0, // No errors, we're fine
INVALID_CONTEXT, // Context is nullptr
NO_ITROP, // There are no trophyset info in lightweight file
CONTINUE, // Not an actual error code. For internal usage only!
NO_KEY_SET, // No root key installed
INVALID_MAGIC, // TRP file has invalid magic in its header
INVALID_VERSION, // TRP file version is not valid
INVALID_ENTSIZE, // TRP file has bigger entries
INVALID_AES, // TRP file contains unaligned AES blocks
NOT_IMPLEMENTED, // This feature is not implemented yet
IO_FAIL, // Failed to read TRP file
NO_CALLBACKS, // Parser called with no callbacks, it's pointless
DECRYPT, // TRP file decryption failed
NO_TROPHIES, // Failed to open TRP file or the said file does not contain any esfm file
MAX_TROPHY_REACHED, // The game hit the hard limit of 128 trophies
};
struct trp_grp_cb {
@ -75,6 +76,7 @@ class ITrophies {
std::string title_detail;
std::string trophyset_version;
uint32_t trophy_count;
uint32_t group_count;
} data;
bool cancelled;
@ -92,12 +94,14 @@ class ITrophies {
inline bool cancelled() { return entry.cancelled && group.cancelled && pngim.cancelled && itrop.cancelled; }
};
virtual ErrCodes parseTRP(trp_context* context) = 0;
virtual ErrCodes parseTRP(trp_context* context) = 0;
virtual const char* getError(ErrCodes ec) = 0;
virtual bool createContext(int32_t userId, uint32_t label) = 0;
virtual bool getProgress(int32_t userId, uint32_t progress[4]) = 0;
virtual bool unlockTrophy(int32_t userId, int32_t trophyId) = 0;
virtual bool resetUserInfo(int32_t userId) = 0;
virtual bool createContext(int32_t userId, uint32_t label) = 0;
virtual bool destroyContext(int32_t userId) = 0;
virtual bool getProgress(int32_t userId, uint32_t progress[4], uint32_t* count) = 0;
virtual bool unlockTrophy(int32_t userId, int32_t trophyId) = 0;
virtual bool resetUserInfo(int32_t userId) = 0;
};
#if defined(__APICALL_EXTERN)

View File

@ -3,9 +3,10 @@
namespace Err {
namespace NpTrophy {
constexpr int32_t INVALID_ARGUMENT = -2141907452;
constexpr int32_t ALREADY_EXISTS = -2141907437;
constexpr int32_t EXCEEDS_MAX = -2141907422;
constexpr int32_t ALREADY_EXISTS = -2141907437;
constexpr int32_t INVALID_CONTEXT = -2141907447;
constexpr int32_t INVALID_ARGUMENT = -2141907452;
} // namespace NpTrophy
} // namespace Err

View File

@ -33,7 +33,7 @@ EXPORT SYSV_ABI int sceNpTrophyCreateContext(SceNpTrophyContext* context, int32_
}
EXPORT SYSV_ABI int sceNpTrophyDestroyContext(SceNpTrophyContext context) {
return Ok;
return accessTrophies().destroyContext(context) ? Ok : Err::NpTrophy::INVALID_ARGUMENT;
}
EXPORT SYSV_ABI int sceNpTrophyRegisterContext(SceNpTrophyContext context, SceNpTrophyHandle handle, uint64_t options) {
@ -46,37 +46,68 @@ EXPORT SYSV_ABI int sceNpTrophyUnlockTrophy(SceNpTrophyContext context, SceNpTro
}
EXPORT SYSV_ABI int sceNpTrophyGetTrophyUnlockState(SceNpTrophyContext context, SceNpTrophyHandle handle, SceNpTrophyFlagArray* flags, uint32_t* count) {
if (flags != nullptr) {
flags->flagBits[0] = 0;
flags->flagBits[1] = 0;
flags->flagBits[2] = 0;
flags->flagBits[3] = 0;
}
*count = 2;
return Ok;
return accessTrophies().getProgress(context, flags->flagBits, count);
}
EXPORT SYSV_ABI int sceNpTrophyGetGameInfo(SceNpTrophyContext context, SceNpTrophyHandle handle, SceNpTrophyGameDetails* details, SceNpTrophyGameData* data) {
if (details != nullptr) {
details->numGroups = 0;
details->numTrophies = 1;
details->numPlatinum = 0;
details->numGold = 0;
details->numSilver = 0;
details->numBronze = 1;
strcpy_s(details->title, "gameName");
strcpy_s(details->description, "gameDesc");
EXPORT SYSV_ABI int sceNpTrophyGetGameInfo(SceNpTrophyContext context, SceNpTrophyHandle handle, SceNpTrophyGameDetails* details, SceNpTrophyGameData* gdata) {
LOG_USE_MODULE(libSceNpTrophy);
SceNpTrophyFlagArray unlock_progr = {0};
uint32_t unlock_count = 0;
uint32_t trophy_count = 0;
if (!accessTrophies().getProgress(context, unlock_progr.flagBits, &unlock_count)) return Err::NpTrophy::INVALID_CONTEXT;
ITrophies::trp_context ctx = {
.lightweight = false,
.entry =
{
.func = [details, gdata, unlock_progr](ITrophies::trp_ent_cb::data_t* data) -> bool {
bool unlocked = SCE_NP_TROPHY_FLAG_ISSET(data->id, (&unlock_progr));
switch (data->type) {
case 'b': // Bronze trophy
if (details) ++details->numBronze;
if (gdata) ++gdata->unlockedBronze;
break;
case 's': // Silver trophy
if (details) ++details->numSilver;
if (gdata) ++gdata->unlockedSilver;
break;
case 'g': // Gold trophy
if (details) ++details->numGold;
if (gdata) ++gdata->unlockedGold;
break;
case 'p': // Platinum trophy
if (details) ++details->numPlatinum;
if (gdata) ++gdata->unlockedPlatinum;
break;
}
return false;
},
},
.itrop =
{
.func = [details](ITrophies::trp_inf_cb::data_t* data) -> bool {
if (details) data->title_name.copy(details->title, sizeof(details->title));
if (details) data->title_detail.copy(details->description, sizeof(details->description));
if (details) details->numTrophies = data->trophy_count;
if (details) details->numGroups = data->group_count;
return true;
},
},
};
ITrophies::ErrCodes ec;
if ((ec = accessTrophies().parseTRP(&ctx)) != ITrophies::ErrCodes::SUCCESS) {
LOG_ERR(L"Failed to parse trophy data: %S", accessTrophies().getError(ec));
return ec == ITrophies::ErrCodes::MAX_TROPHY_REACHED ? Err::NpTrophy::EXCEEDS_MAX : Err::NpTrophy::INVALID_ARGUMENT;
}
if (data != nullptr) {
data->unlockedTrophies = 0;
data->unlockedPlatinum = 0;
data->unlockedGold = 0;
data->unlockedSilver = 0;
data->unlockedBronze = 0;
data->progressPercentage = 0;
}
if (gdata) gdata->progressPercentage = (unlock_count / (float)trophy_count) * 100;
return Ok;
}
@ -136,7 +167,7 @@ EXPORT SYSV_ABI int sceNpTrophyGetGameIcon(SceNpTrophyContext context, SceNpTrop
::memcpy(buffer, data->pngdata, data->pngsize);
*size = data->pngsize;
}
delete data->pngdata;
::free(data->pngdata);
return true;
}
@ -167,7 +198,7 @@ EXPORT SYSV_ABI int sceNpTrophyGetTrophyIcon(SceNpTrophyContext context, SceNpTr
::memcpy(buffer, data->pngdata, data->pngsize);
*size = data->pngsize;
}
delete data->pngdata;
::free(data->pngdata);
return true;
}