Merge pull request #13134 from BunnyFl/fix/uds-event-size-and-string

fix(libSceNpUniversalDataSystem): size and describe events as the console does
This commit is contained in:
Dean authored and GitHub committed 2026-10-30 22:22:55 +13:00
commit c70974fefb
7 files changed
+273 -21

No files matched your search

@@ -1,6 +1,7 @@
#ifndef CORE_LIBS_PRX_LIBSCENPUNIVERSALDATASYSTEM_INCLUDE_UDSEVENT_HPP
#define CORE_LIBS_PRX_LIBSCENPUNIVERSALDATASYSTEM_INCLUDE_UDSEVENT_HPP
#include <cstddef>
#include <cstdint>
#include <memory>
#include <string>
@@ -48,6 +49,8 @@ namespace Uds {
inline constexpr std::string_view UnlockTrophyEvent = "_UnlockTrophy";
inline constexpr std::string_view UpdateTrophyProgressEvent = "_UpdateTrophyProgress";
inline constexpr std::string_view TrophyIdProperty = "_trophy_id";
inline constexpr std::string_view EventNameKey = "eventName";
inline constexpr std::string_view PropertiesKey = "properties";
enum class TrophyEventKind { None, Unlock, Progress, Malformed };
@@ -61,6 +64,8 @@ TrophyEvent RecognizeTrophyEvent(const NpUniversalDataSystemEvent& event);
std::string Describe(const Value& value);
std::string Describe(const NpUniversalDataSystemEventPropertyObject& object);
std::string Describe(const NpUniversalDataSystemEvent& event);
std::size_t MessagePackSize(const NpUniversalDataSystemEvent& event);
std::string DebugString(const NpUniversalDataSystemEvent& event);
NpTrophy2Event StatEvent(const NpUniversalDataSystemEvent& event);
}
@@ -1,4 +1,5 @@
#include "prx/libSceNpUniversalDataSystem/include/Api.hpp"
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <cstdio>
@@ -84,10 +85,9 @@ int APS5_VABI sceNpUniversalDataSystemPostEvent(int context, int handle, const v
}
int APS5_VABI sceNpUniversalDataSystemEventEstimateSize(const NpUniversalDataSystemEvent* event, size_t* size) {
if (event == nullptr || size == nullptr) {
APS5_INVALID_ARG_EX;
}
*size = NP_UNIVERSAL_DATA_SYSTEM_EMPTY_EVENT_SIZE;
if (event == nullptr) return SCE_NP_UNIVERSAL_DATA_SYSTEM_ERROR_NULL_EVENT;
if (size == nullptr) return SCE_NP_UNIVERSAL_DATA_SYSTEM_ERROR_INVALID_ARGUMENT;
*size = Uds::MessagePackSize(*event);
return SCE_NP_UNIVERSAL_DATA_SYSTEM_OK;
}
@@ -97,16 +97,18 @@ int APS5_VABI sceNpUniversalDataSystemEventToString(
size_t buf_size,
size_t* string_size)
{
if (event == nullptr) {
APS5_INVALID_ARG_EX;
if (event == nullptr) return SCE_NP_UNIVERSAL_DATA_SYSTEM_ERROR_NULL_EVENT;
if (buf == nullptr) {
if (string_size == nullptr) return SCE_NP_UNIVERSAL_DATA_SYSTEM_ERROR_INVALID_ARGUMENT;
buf_size = 0;
}
const size_t json_len = std::strlen(NP_UNIVERSAL_DATA_SYSTEM_EMPTY_JSON) + 1;
if (string_size != nullptr) {
*string_size = json_len;
}
if (buf != nullptr && buf_size > 0) {
std::snprintf(buf, buf_size, "%s", NP_UNIVERSAL_DATA_SYSTEM_EMPTY_JSON);
const std::string text = Uds::DebugString(*event);
if (buf_size != 0) {
const size_t copied = std::min(text.size(), buf_size - 1);
std::memcpy(buf, text.data(), copied);
buf[copied] = '\0';
}
if (string_size != nullptr) *string_size = text.size();
return SCE_NP_UNIVERSAL_DATA_SYSTEM_OK;
}
@@ -14,13 +14,11 @@
static constexpr int SCE_NP_UNIVERSAL_DATA_SYSTEM_OK = 0;
static constexpr int SCE_NP_UNIVERSAL_DATA_SYSTEM_ERROR_INVALID_ARGUMENT = -2141900542;
static constexpr int SCE_NP_UNIVERSAL_DATA_SYSTEM_ERROR_NULL_EVENT = -2141900522;
static constexpr int NP_UNIVERSAL_DATA_SYSTEM_CONTEXT_DEFAULT = 1;
static constexpr int NP_UNIVERSAL_DATA_SYSTEM_HANDLE_DEFAULT = 1;
static constexpr const char* NP_UNIVERSAL_DATA_SYSTEM_EMPTY_JSON = "{}";
static constexpr size_t NP_UNIVERSAL_DATA_SYSTEM_EMPTY_EVENT_SIZE = 3;
namespace Uds {
template <typename TValue>
@@ -91,6 +91,126 @@ void appendValue(std::string& out, const Value& value) {
}, value);
}
std::size_t lengthPrefixed(std::size_t length, std::size_t smallLimit) {
if (length < smallLimit) return (smallLimit == 32 ? 1 : 2) + length;
if (length < 0x100) return 2 + length;
if (length < 0x10000) return 3 + length;
return 5 + length;
}
std::size_t containerHeader(std::size_t count) {
if (count < 16) return 1;
if (count < 0x10000) return 3;
return 5;
}
std::size_t packedSize(const Value& value);
std::size_t packedSize(const NpUniversalDataSystemEventPropertyObject& object) {
std::size_t size = containerHeader(object.properties.size());
for (const auto& [key, value] : object.properties) size += lengthPrefixed(key.size(), 32) + packedSize(value);
return size;
}
std::size_t packedSize(const Value& value) {
return std::visit([](const auto& held) -> std::size_t {
using Held = std::decay_t<decltype(held)>;
if constexpr (std::is_same_v<Held, bool>) {
return 1;
} else if constexpr (std::is_same_v<Held, std::int32_t> || std::is_same_v<Held, std::uint32_t> || std::is_same_v<Held, float>) {
return 5;
} else if constexpr (std::is_same_v<Held, std::int64_t> || std::is_same_v<Held, std::uint64_t> || std::is_same_v<Held, double>) {
return 9;
} else if constexpr (std::is_same_v<Held, std::string>) {
return lengthPrefixed(held.size(), 32);
} else if constexpr (std::is_same_v<Held, Binary>) {
return lengthPrefixed(held.size(), 0x100);
} else if constexpr (std::is_same_v<Held, std::shared_ptr<NpUniversalDataSystemEventPropertyObject>>) {
return packedSize(*held);
} else {
std::size_t size = containerHeader(held->values.size());
for (const auto& element : held->values) size += packedSize(element);
return size;
}
}, value);
}
template <typename T>
void appendTagged(std::string& out, T value, const char* tag) {
char number[40];
const auto result = std::to_chars(number, number + sizeof(number), value);
if (result.ec != std::errc{}) throw std::runtime_error("event property number formatting failed");
out.append(number, static_cast<std::string::size_type>(result.ptr - number));
out += tag;
}
void appendTaggedReal(std::string& out, double value, const char* tag) {
char number[40];
const auto result = std::to_chars(number, number + sizeof(number), value, std::chars_format::general, 6);
if (result.ec != std::errc{}) throw std::runtime_error("event property number formatting failed");
out.append(number, static_cast<std::string::size_type>(result.ptr - number));
out += tag;
}
void appendDebug(std::string& out, const Value& value, std::size_t indent);
void appendDebug(std::string& out, const NpUniversalDataSystemEventPropertyObject& object, std::size_t indent) {
out += "{\n";
for (const auto& [key, value] : object.properties) {
out.append(indent + 4, ' ');
out += '"';
out += key;
out += "\": ";
appendDebug(out, value, indent + 4);
out += ",\n";
}
out.append(indent, ' ');
out += '}';
}
void appendDebug(std::string& out, const Value& value, std::size_t indent) {
std::visit([&](const auto& held) {
using Held = std::decay_t<decltype(held)>;
if constexpr (std::is_same_v<Held, bool>) {
out += held ? "true" : "false";
} else if constexpr (std::is_same_v<Held, std::int32_t>) {
appendTagged(out, held, " (Int32)");
} else if constexpr (std::is_same_v<Held, std::uint32_t>) {
appendTagged(out, held, " (UInt32)");
} else if constexpr (std::is_same_v<Held, std::int64_t>) {
appendTagged(out, held, " (Int64)");
} else if constexpr (std::is_same_v<Held, std::uint64_t>) {
appendTagged(out, held, " (UInt64)");
} else if constexpr (std::is_same_v<Held, float>) {
appendTaggedReal(out, held, " (Float32)");
} else if constexpr (std::is_same_v<Held, double>) {
appendTaggedReal(out, held, " (Float64)");
} else if constexpr (std::is_same_v<Held, std::string>) {
out += '"';
out += held;
out += '"';
} else if constexpr (std::is_same_v<Held, Binary>) {
static constexpr char Digits[] = "0123456789ABCDEF";
for (const std::uint8_t byte : held) {
out += Digits[byte >> 4];
out += Digits[byte & 0xF];
}
out += " (Binary)";
} else if constexpr (std::is_same_v<Held, std::shared_ptr<NpUniversalDataSystemEventPropertyObject>>) {
appendDebug(out, *held, indent);
} else {
out += "[\n";
for (const auto& element : held->values) {
out.append(indent + 4, ' ');
appendDebug(out, element, indent + 4);
out += ",\n";
}
out.append(indent, ' ');
out += ']';
}
}, value);
}
std::optional<std::uint64_t> unsignedValue(const Value& value) {
return std::visit([](const auto& held) -> std::optional<std::uint64_t> {
using Held = std::decay_t<decltype(held)>;
@@ -150,6 +270,20 @@ std::string Describe(const NpUniversalDataSystemEvent& event) {
return event.name + " " + Describe(event.properties);
}
std::size_t MessagePackSize(const NpUniversalDataSystemEvent& event) {
return containerHeader(2) + lengthPrefixed(EventNameKey.size(), 32) + lengthPrefixed(event.name.size(), 32) +
lengthPrefixed(PropertiesKey.size(), 32) + packedSize(event.properties);
}
std::string DebugString(const NpUniversalDataSystemEvent& event) {
NpUniversalDataSystemEventPropertyObject root;
root.Set(EventNameKey, event.name);
root.Set(PropertiesKey, std::make_shared<NpUniversalDataSystemEventPropertyObject>(event.properties));
std::string out;
appendDebug(out, root, 0);
return out;
}
NpTrophy2Event StatEvent(const NpUniversalDataSystemEvent& event) {
NpTrophy2Event converted;
converted.name = event.name;
@@ -7,10 +7,12 @@
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <memory>
#include <optional>
#include <stdexcept>
#include <string>
#include <string_view>
#include <tuple>
#include <vector>
@@ -143,6 +145,115 @@ void testEvents() {
}), "an event without a name was created");
}
void checkEventText(const NpUniversalDataSystemEvent* event, size_t expectedSize, std::string_view expected, const char* message) {
size_t size = 0;
call(sceNpUniversalDataSystemEventEstimateSize(event, &size), "EstimateSize failed");
check(size == expectedSize, message);
size_t length = 0;
call(sceNpUniversalDataSystemEventToString(event, nullptr, 0, &length), "ToString without a buffer failed");
check(length == expected.size(), message);
std::string text(expected.size() + 2, '\x7f');
length = 0;
call(sceNpUniversalDataSystemEventToString(event, text.data(), expected.size() + 1, &length), "ToString failed");
check(length == expected.size() && text.compare(0, expected.size(), expected) == 0 && text[expected.size()] == '\0' && text.back() == '\x7f', message);
}
void testEventSizeAndString() {
using Fill = void (*)(NpUniversalDataSystemEventPropertyObject*);
struct Case {
Fill fill;
size_t size;
std::string_view text;
const char* message;
};
const Case cases[] = {
{[](NpUniversalDataSystemEventPropertyObject*) {}, 25,
"{\n \"eventName\": \"E\",\n \"properties\": {\n },\n}", "an empty event was not described as the console does"},
{[](NpUniversalDataSystemEventPropertyObject* object) { call(sceNpUniversalDataSystemEventPropertyObjectSetInt64(object, "k", INT64_MIN), "SetInt64 failed"); }, 36,
"{\n \"eventName\": \"E\",\n \"properties\": {\n \"k\": -9223372036854775808 (Int64),\n },\n}", "an int64 property was not described as the console does"},
{[](NpUniversalDataSystemEventPropertyObject* object) { call(sceNpUniversalDataSystemEventPropertyObjectSetUInt64(object, "k", UINT64_MAX), "SetUInt64 failed"); }, 36,
"{\n \"eventName\": \"E\",\n \"properties\": {\n \"k\": 18446744073709551615 (UInt64),\n },\n}", "a uint64 property was not described as the console does"},
{[](NpUniversalDataSystemEventPropertyObject* object) { call(sceNpUniversalDataSystemEventPropertyObjectSetFloat32(object, "k", 0.1f), "SetFloat32 failed"); }, 32,
"{\n \"eventName\": \"E\",\n \"properties\": {\n \"k\": 0.1 (Float32),\n },\n}", "a float32 property was not described as the console does"},
{[](NpUniversalDataSystemEventPropertyObject* object) { call(sceNpUniversalDataSystemEventPropertyObjectSetFloat64(object, "k", 1e300), "SetFloat64 failed"); }, 36,
"{\n \"eventName\": \"E\",\n \"properties\": {\n \"k\": 1e+300 (Float64),\n },\n}", "a float64 property was not described as the console does"},
{[](NpUniversalDataSystemEventPropertyObject* object) { call(sceNpUniversalDataSystemEventPropertyObjectSetString(object, "s", "a\"b\\c\n\x01/z"), "SetString failed"); }, 37,
"{\n \"eventName\": \"E\",\n \"properties\": {\n \"s\": \"a\"b\\c\n\x01/z\",\n },\n}", "a string property was escaped or resized"},
{[](NpUniversalDataSystemEventPropertyObject* object) { call(sceNpUniversalDataSystemEventPropertyObjectSetBinary(object, "b", "\x00\x7f\xff", 3), "SetBinary failed"); }, 32,
"{\n \"eventName\": \"E\",\n \"properties\": {\n \"b\": 007FFF (Binary),\n },\n}", "a binary property was not described as the console does"},
{[](NpUniversalDataSystemEventPropertyObject* object) {
call(sceNpUniversalDataSystemEventPropertyObjectSetInt32(object, "zeta", 1), "SetInt32 failed");
call(sceNpUniversalDataSystemEventPropertyObjectSetInt32(object, "alpha", 2), "SetInt32 failed");
call(sceNpUniversalDataSystemEventPropertyObjectSetInt32(object, "mid", 3), "SetInt32 failed");
call(sceNpUniversalDataSystemEventPropertyObjectSetInt32(object, "alpha", 4), "SetInt32 failed");
}, 55,
"{\n \"eventName\": \"E\",\n \"properties\": {\n \"zeta\": 1 (Int32),\n \"alpha\": 4 (Int32),\n \"mid\": 3 (Int32),\n },\n}",
"properties were not described in the order they were first set"},
{[](NpUniversalDataSystemEventPropertyObject* object) {
auto* child = createObject();
call(sceNpUniversalDataSystemEventPropertyObjectSetInt32(child, "x", 7), "SetInt32 failed");
NpUniversalDataSystemEventPropertyObject* stored = nullptr;
call(sceNpUniversalDataSystemEventPropertyObjectSetObject(object, "obj", child, &stored), "SetObject failed");
call(sceNpUniversalDataSystemEventPropertyObjectSetInt32(child, "after", 1), "SetInt32 failed");
call(sceNpUniversalDataSystemEventPropertyObjectSetInt32(stored, "viaPtr", 2), "SetInt32 failed");
call(sceNpUniversalDataSystemDestroyEventPropertyObject(child), "child object was not destroyed");
}, 49,
"{\n \"eventName\": \"E\",\n \"properties\": {\n \"obj\": {\n \"x\": 7 (Int32),\n \"viaPtr\": 2 (Int32),\n },\n },\n}",
"a nested object was not described as the console does"},
{[](NpUniversalDataSystemEventPropertyObject* object) {
NpUniversalDataSystemEventPropertyArray* array = nullptr;
call(sceNpUniversalDataSystemCreateEventPropertyArray(&array), "array was not created");
call(sceNpUniversalDataSystemEventPropertyArraySetInt32(array, -1), "array SetInt32 failed");
call(sceNpUniversalDataSystemEventPropertyArraySetString(array, "x"), "array SetString failed");
call(sceNpUniversalDataSystemEventPropertyArraySetBool(array, true), "array SetBool failed");
call(sceNpUniversalDataSystemEventPropertyArraySetUInt64(array, 9), "array SetUInt64 failed");
call(sceNpUniversalDataSystemEventPropertyArraySetFloat32(array, 2.5f), "array SetFloat32 failed");
call(sceNpUniversalDataSystemEventPropertyArraySetBinary(array, "\x01\x02", 2), "array SetBinary failed");
auto* inner = createObject();
call(sceNpUniversalDataSystemEventPropertyObjectSetInt32(inner, "i", 1), "SetInt32 failed");
call(sceNpUniversalDataSystemEventPropertyArraySetObject(array, inner, nullptr), "array SetObject failed");
NpUniversalDataSystemEventPropertyArray* empty = nullptr;
call(sceNpUniversalDataSystemCreateEventPropertyArray(&empty), "empty array was not created");
call(sceNpUniversalDataSystemEventPropertyArraySetArray(array, empty, nullptr), "array SetArray failed");
call(sceNpUniversalDataSystemEventPropertyObjectSetArray(object, "arr", array, nullptr), "SetArray failed");
call(sceNpUniversalDataSystemDestroyEventPropertyObject(inner), "inner object was not destroyed");
call(sceNpUniversalDataSystemDestroyEventPropertyArray(empty), "empty array was not destroyed");
call(sceNpUniversalDataSystemDestroyEventPropertyArray(array), "array was not destroyed");
}, 65,
"{\n \"eventName\": \"E\",\n \"properties\": {\n \"arr\": [\n -1 (Int32),\n \"x\",\n true,\n 9 (UInt64),\n"
" 2.5 (Float32),\n 0102 (Binary),\n {\n \"i\": 1 (Int32),\n },\n [\n ],\n ],\n },\n}",
"an array was not described as the console does"},
};
for (const auto& item : cases) {
NpUniversalDataSystemEvent* event = nullptr;
NpUniversalDataSystemEventPropertyObject* properties = nullptr;
call(sceNpUniversalDataSystemCreateEvent("E", nullptr, &event, &properties), "event was not created");
item.fill(properties);
checkEventText(event, item.size, item.text, item.message);
call(sceNpUniversalDataSystemDestroyEvent(event), "event was not destroyed");
}
NpUniversalDataSystemEvent* event = nullptr;
call(sceNpUniversalDataSystemCreateEvent("E", nullptr, &event, nullptr), "event was not created");
size_t length = 0;
check(sceNpUniversalDataSystemEventEstimateSize(nullptr, &length) == SCE_NP_UNIVERSAL_DATA_SYSTEM_ERROR_NULL_EVENT, "EstimateSize accepted a null event");
check(sceNpUniversalDataSystemEventEstimateSize(event, nullptr) == SCE_NP_UNIVERSAL_DATA_SYSTEM_ERROR_INVALID_ARGUMENT, "EstimateSize accepted a null size");
char text[8];
check(sceNpUniversalDataSystemEventToString(nullptr, text, sizeof(text), &length) == SCE_NP_UNIVERSAL_DATA_SYSTEM_ERROR_NULL_EVENT, "ToString accepted a null event");
check(sceNpUniversalDataSystemEventToString(event, nullptr, 0, nullptr) == SCE_NP_UNIVERSAL_DATA_SYSTEM_ERROR_INVALID_ARGUMENT, "ToString accepted no buffer and no length");
length = 0;
call(sceNpUniversalDataSystemEventToString(event, nullptr, sizeof(text), &length), "ToString without a buffer but with a capacity failed");
check(length == 52, "ToString without a buffer did not report the full length");
std::memset(text, 'x', sizeof(text));
call(sceNpUniversalDataSystemEventToString(event, text, 0, &length), "ToString into an empty buffer failed");
check(length == 52 && text[0] == 'x', "ToString wrote into an empty buffer");
call(sceNpUniversalDataSystemEventToString(event, text, 1, nullptr), "ToString into a one-byte buffer failed");
check(text[0] == '\0' && text[1] == 'x', "a one-byte buffer did not get only the terminator");
call(sceNpUniversalDataSystemEventToString(event, text, 7, &length), "ToString into a short buffer failed");
check(length == 52 && std::string_view(text, 7) == std::string_view("{\n \0", 7) && text[7] == 'x', "a short buffer was not cut like snprintf");
call(sceNpUniversalDataSystemDestroyEvent(event), "event was not destroyed");
}
Uds::TrophyEvent recognize(const char* name, std::optional<Uds::Value> id) {
NpUniversalDataSystemEvent event;
event.name = name;
@@ -287,6 +398,7 @@ int main() {
testObjectSetters();
testNestedValues();
testEvents();
testEventSizeAndString();
testRecognition();
testStatEvent();
testLocaleIndependentNumberFormatting();
@@ -49,18 +49,18 @@ int main() {
NpUniversalDataSystemEvent* event = nullptr;
CHECK(sceNpUniversalDataSystemCreateEvent("QueryEvent", nullptr, &event, nullptr) == 0 && event != nullptr);
std::size_t size = 0;
CHECK(RejectsArgument([&] { sceNpUniversalDataSystemEventEstimateSize(nullptr, &size); }));
CHECK(RejectsArgument([&] { sceNpUniversalDataSystemEventEstimateSize(event, nullptr); }));
CHECK(sceNpUniversalDataSystemEventEstimateSize(event, &size) == 0 && size == 3);
CHECK(sceNpUniversalDataSystemEventEstimateSize(nullptr, &size) == static_cast<int>(0x80553116u));
CHECK(sceNpUniversalDataSystemEventEstimateSize(event, nullptr) == static_cast<int>(0x80553102u));
CHECK(sceNpUniversalDataSystemEventEstimateSize(event, &size) == 0 && size == 34);
char text[8];
std::memset(text, 'x', sizeof(text));
std::size_t length = 0;
CHECK(RejectsArgument([&] { sceNpUniversalDataSystemEventToString(nullptr, text, sizeof(text), &length); }));
CHECK(sceNpUniversalDataSystemEventToString(nullptr, text, sizeof(text), &length) == static_cast<int>(0x80553116u));
CHECK(text[0] == 'x' && length == 0);
CHECK(sceNpUniversalDataSystemEventToString(event, text, sizeof(text), &length) == 0 && length == 3 && std::strcmp(text, "{}") == 0);
CHECK(sceNpUniversalDataSystemEventToString(event, text, sizeof(text), &length) == 0 && length == 61 && std::strcmp(text, "{\n \"") == 0);
length = 0;
CHECK(sceNpUniversalDataSystemEventToString(event, nullptr, 0, &length) == 0 && length == 3);
CHECK(sceNpUniversalDataSystemEventToString(event, nullptr, 0, &length) == 0 && length == 61);
char tiny[2] = {'x', 'x'};
CHECK(sceNpUniversalDataSystemEventToString(event, tiny, sizeof(tiny), nullptr) == 0 && tiny[0] == '{' && tiny[1] == '\0');
CHECK(sceNpUniversalDataSystemDestroyEvent(event) == 0);
@@ -41,3 +41,4 @@
- [Trophy events](../../../core/libs/prx/libSceNpUniversalDataSystem/src/Event.cpp): `sceNpUniversalDataSystemPostEvent` stores an `_UnlockTrophy` event with an int32 `_trophy_id` of 0 or more as an unlock for the user and service label of its context, as KytyPS5 does, except an id that [libSceNpTrophy2](../../../core/libs/prx/libSceNpTrophy2/src/TrophyStore.cpp) refuses when the title's trophy data is loaded (one not in the data, a platinum or a progressive trophy). `_UpdateTrophyProgress` goes through the title's statistic rules like any other event (below). A context need not be registered, and every post of an event that `sceNpUniversalDataSystemCreateEvent` returned and that is not yet destroyed returns success (a null or any other event pointer stops the game): KytyPS5's errors for an unknown or unregistered context and for a malformed unlock are not verified. The storage statistics stay zero.
- [Statistics](../../../core/libs/prx/libSceNpUniversalDataSystem/src/Event.cpp): every other event goes through the title's rules in `sce_sys/uds/udsNN.ucp`, which [libSceNpTrophy2](../../../core/libs/prx/libSceNpTrophy2/src/TitleTrophies.cpp) reads. Only `"aggregation":"latest"` and `"max"` statistics of `dataType` `uint32`, `uint64` or `int32` are kept; no public source documents the other aggregations (such as `count`, `collection` and `eachLatest`) or types, so their definitions and rules are skipped. Only rules whose input is a `$.name` path (dotted for nested objects) are applied. A value is used when it is an integer of any width that is not negative and fits the statistic's type and `minValue`/`maxValue`; KytyPS5 requires the statistic's exact width, and whether the console converts, clamps or rejects other values is not known. Statistics are stored per user and service label, and every trophy's `unlockCondition` is compared after each event that sets one.
- [Event checks](../../../core/libs/prx/libSceNpUniversalDataSystem/src/Event.cpp): the 13.42 module returns 0x80553117 from `sceNpUniversalDataSystemCreateEvent`, `sceNpUniversalDataSystemEventEstimateSize` and `sceNpUniversalDataSystemEventToString` before `sceNpUniversalDataSystemInitialize`, and `sceNpUniversalDataSystemCreateEvent` returns 0x8055310d for an empty name; its disassembly also compares packed sizes with 0x5000 bytes when `prop` is given, which was not measured. These functions do neither. `sceNpUniversalDataSystemEventToString` writes NaN and infinite properties as `std::to_chars` does; the console's text for them is not measured.