mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-11-05 13:20:11 +00:00
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:
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.
|
||||
Reference in new issue
Block a user