mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-22 08:33:12 -05:00
333 lines
22 KiB
C++
333 lines
22 KiB
C++
#include "NpUniversalDataSystem.hpp"
|
|
#include "prx/libSceNpTrophy2/include/TrophyStore.hpp"
|
|
#include "prx/libc/include/general/VabiMacros.hpp"
|
|
|
|
#include <clocale>
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <cstdio>
|
|
#include <memory>
|
|
#include <optional>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
#include <tuple>
|
|
#include <vector>
|
|
|
|
extern "C" {
|
|
int APS5_VABI sceNpUniversalDataSystemCreateContext(int*, int, uint32_t, uint64_t);
|
|
int APS5_VABI sceNpUniversalDataSystemDestroyContext(int);
|
|
int APS5_VABI sceNpUniversalDataSystemCreateEvent(const char*, const NpUniversalDataSystemEventPropertyObject*, NpUniversalDataSystemEvent**, NpUniversalDataSystemEventPropertyObject**);
|
|
int APS5_VABI sceNpUniversalDataSystemDestroyEvent(NpUniversalDataSystemEvent*);
|
|
int APS5_VABI sceNpUniversalDataSystemPostEvent(int, int, const void*, uint64_t);
|
|
int APS5_VABI sceNpUniversalDataSystemCreateEventPropertyObject(NpUniversalDataSystemEventPropertyObject**);
|
|
int APS5_VABI sceNpUniversalDataSystemDestroyEventPropertyObject(NpUniversalDataSystemEventPropertyObject*);
|
|
int APS5_VABI sceNpUniversalDataSystemEventPropertyObjectSetString(NpUniversalDataSystemEventPropertyObject*, const char*, const char*);
|
|
int APS5_VABI sceNpUniversalDataSystemEventPropertyObjectSetInt32(NpUniversalDataSystemEventPropertyObject*, const char*, int32_t);
|
|
int APS5_VABI sceNpUniversalDataSystemEventPropertyObjectSetUInt32(NpUniversalDataSystemEventPropertyObject*, const char*, uint32_t);
|
|
int APS5_VABI sceNpUniversalDataSystemEventPropertyObjectSetInt64(NpUniversalDataSystemEventPropertyObject*, const char*, int64_t);
|
|
int APS5_VABI sceNpUniversalDataSystemEventPropertyObjectSetUInt64(NpUniversalDataSystemEventPropertyObject*, const char*, uint64_t);
|
|
int APS5_VABI sceNpUniversalDataSystemEventPropertyObjectSetFloat32(NpUniversalDataSystemEventPropertyObject*, const char*, float);
|
|
int APS5_VABI sceNpUniversalDataSystemEventPropertyObjectSetFloat64(NpUniversalDataSystemEventPropertyObject*, const char*, double);
|
|
int APS5_VABI sceNpUniversalDataSystemEventPropertyObjectSetBool(NpUniversalDataSystemEventPropertyObject*, const char*, bool);
|
|
int APS5_VABI sceNpUniversalDataSystemEventPropertyObjectSetBinary(NpUniversalDataSystemEventPropertyObject*, const char*, const void*, size_t);
|
|
int APS5_VABI sceNpUniversalDataSystemEventPropertyObjectSetObject(NpUniversalDataSystemEventPropertyObject*, const char*, const NpUniversalDataSystemEventPropertyObject*, NpUniversalDataSystemEventPropertyObject**);
|
|
int APS5_VABI sceNpUniversalDataSystemEventPropertyObjectSetArray(NpUniversalDataSystemEventPropertyObject*, const char*, const NpUniversalDataSystemEventPropertyArray*, NpUniversalDataSystemEventPropertyArray**);
|
|
int APS5_VABI sceNpUniversalDataSystemCreateEventPropertyArray(NpUniversalDataSystemEventPropertyArray**);
|
|
int APS5_VABI sceNpUniversalDataSystemDestroyEventPropertyArray(NpUniversalDataSystemEventPropertyArray*);
|
|
int APS5_VABI sceNpUniversalDataSystemEventPropertyArraySetString(NpUniversalDataSystemEventPropertyArray*, const char*);
|
|
int APS5_VABI sceNpUniversalDataSystemEventPropertyArraySetInt32(NpUniversalDataSystemEventPropertyArray*, int32_t);
|
|
int APS5_VABI sceNpUniversalDataSystemEventPropertyArraySetUInt32(NpUniversalDataSystemEventPropertyArray*, uint32_t);
|
|
int APS5_VABI sceNpUniversalDataSystemEventPropertyArraySetInt64(NpUniversalDataSystemEventPropertyArray*, int64_t);
|
|
int APS5_VABI sceNpUniversalDataSystemEventPropertyArraySetUInt64(NpUniversalDataSystemEventPropertyArray*, uint64_t);
|
|
int APS5_VABI sceNpUniversalDataSystemEventPropertyArraySetFloat32(NpUniversalDataSystemEventPropertyArray*, float);
|
|
int APS5_VABI sceNpUniversalDataSystemEventPropertyArraySetFloat64(NpUniversalDataSystemEventPropertyArray*, double);
|
|
int APS5_VABI sceNpUniversalDataSystemEventPropertyArraySetBool(NpUniversalDataSystemEventPropertyArray*, bool);
|
|
int APS5_VABI sceNpUniversalDataSystemEventPropertyArraySetBinary(NpUniversalDataSystemEventPropertyArray*, const void*, size_t);
|
|
int APS5_VABI sceNpUniversalDataSystemEventPropertyArraySetObject(NpUniversalDataSystemEventPropertyArray*, const NpUniversalDataSystemEventPropertyObject*, NpUniversalDataSystemEventPropertyObject**);
|
|
int APS5_VABI sceNpUniversalDataSystemEventPropertyArraySetArray(NpUniversalDataSystemEventPropertyArray*, const NpUniversalDataSystemEventPropertyArray*, NpUniversalDataSystemEventPropertyArray**);
|
|
}
|
|
|
|
namespace {
|
|
|
|
constexpr int InitialUser = 0x10000000;
|
|
|
|
void check(bool condition, const char* message) {
|
|
if (!condition) throw std::runtime_error(message);
|
|
}
|
|
|
|
void call(int result, const char* message) {
|
|
check(result == 0, message);
|
|
}
|
|
|
|
template <typename TAction>
|
|
bool throwsInvalidArgument(TAction action) {
|
|
try {
|
|
action();
|
|
} catch (const std::invalid_argument&) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
NpUniversalDataSystemEventPropertyObject* createObject() {
|
|
NpUniversalDataSystemEventPropertyObject* object = nullptr;
|
|
call(sceNpUniversalDataSystemCreateEventPropertyObject(&object), "property object was not created");
|
|
check(object != nullptr, "property object is null");
|
|
return object;
|
|
}
|
|
|
|
void testObjectSetters() {
|
|
auto* object = createObject();
|
|
const std::uint8_t bytes[] = {1, 2, 3};
|
|
call(sceNpUniversalDataSystemEventPropertyObjectSetInt32(object, "int32", -5), "SetInt32 failed");
|
|
call(sceNpUniversalDataSystemEventPropertyObjectSetUInt32(object, "uint32", 4000000000u), "SetUInt32 failed");
|
|
call(sceNpUniversalDataSystemEventPropertyObjectSetInt64(object, "int64", -9000000000), "SetInt64 failed");
|
|
call(sceNpUniversalDataSystemEventPropertyObjectSetUInt64(object, "uint64", 18000000000000000000ull), "SetUInt64 failed");
|
|
call(sceNpUniversalDataSystemEventPropertyObjectSetFloat32(object, "float32", 0.5f), "SetFloat32 failed");
|
|
call(sceNpUniversalDataSystemEventPropertyObjectSetFloat64(object, "float64", 0.25), "SetFloat64 failed");
|
|
call(sceNpUniversalDataSystemEventPropertyObjectSetBool(object, "bool", true), "SetBool failed");
|
|
call(sceNpUniversalDataSystemEventPropertyObjectSetString(object, "string", "a\"b\\c\n"), "SetString failed");
|
|
call(sceNpUniversalDataSystemEventPropertyObjectSetBinary(object, "binary", bytes, sizeof(bytes)), "SetBinary failed");
|
|
check(Uds::Describe(*object) == R"({"int32":-5,"uint32":4000000000,"int64":-9000000000,"uint64":18000000000000000000,"float32":0.5,"float64":0.25,"bool":true,"string":"a\"b\\c\u000a","binary":<3 bytes>})",
|
|
"properties were not kept with their types");
|
|
const auto* binary = std::get_if<Uds::Binary>(object->Find("binary"));
|
|
check(binary != nullptr && *binary == Uds::Binary{1, 2, 3}, "binary bytes were not copied");
|
|
call(sceNpUniversalDataSystemEventPropertyObjectSetString(object, "int32", "replaced"), "SetString over int32 failed");
|
|
const auto* replaced = std::get_if<std::string>(object->Find("int32"));
|
|
check(object->properties.size() == 9 && replaced != nullptr && *replaced == "replaced", "setting a key again did not replace its value");
|
|
check(object->Find("missing") == nullptr, "a missing key was found");
|
|
check(throwsInvalidArgument([] { sceNpUniversalDataSystemEventPropertyObjectSetInt32(nullptr, "key", 1); }), "a null object was accepted");
|
|
check(throwsInvalidArgument([object] { sceNpUniversalDataSystemEventPropertyObjectSetInt32(object, nullptr, 1); }), "a null key was accepted");
|
|
call(sceNpUniversalDataSystemDestroyEventPropertyObject(object), "property object was not destroyed");
|
|
}
|
|
|
|
void testNestedValues() {
|
|
auto* source = createObject();
|
|
auto* parent = createObject();
|
|
call(sceNpUniversalDataSystemEventPropertyObjectSetInt32(source, "x", 1), "SetInt32 on the source failed");
|
|
NpUniversalDataSystemEventPropertyObject* child = nullptr;
|
|
call(sceNpUniversalDataSystemEventPropertyObjectSetObject(parent, "child", source, &child), "SetObject failed");
|
|
check(child != nullptr && child != source, "SetObject did not return its own copy");
|
|
call(sceNpUniversalDataSystemEventPropertyObjectSetInt32(source, "x", 2), "SetInt32 after SetObject failed");
|
|
call(sceNpUniversalDataSystemEventPropertyObjectSetBool(child, "y", true), "SetBool on the child failed");
|
|
call(sceNpUniversalDataSystemDestroyEventPropertyObject(source), "source object was not destroyed");
|
|
NpUniversalDataSystemEventPropertyArray* list = nullptr;
|
|
call(sceNpUniversalDataSystemEventPropertyObjectSetArray(parent, "list", nullptr, &list), "SetArray failed");
|
|
check(list != nullptr, "SetArray did not return the stored array");
|
|
const std::uint8_t bytes[] = {4, 5};
|
|
call(sceNpUniversalDataSystemEventPropertyArraySetInt32(list, 1), "array SetInt32 failed");
|
|
call(sceNpUniversalDataSystemEventPropertyArraySetUInt32(list, 2), "array SetUInt32 failed");
|
|
call(sceNpUniversalDataSystemEventPropertyArraySetInt64(list, -3), "array SetInt64 failed");
|
|
call(sceNpUniversalDataSystemEventPropertyArraySetUInt64(list, 4), "array SetUInt64 failed");
|
|
call(sceNpUniversalDataSystemEventPropertyArraySetFloat32(list, 1.5f), "array SetFloat32 failed");
|
|
call(sceNpUniversalDataSystemEventPropertyArraySetFloat64(list, 2.5), "array SetFloat64 failed");
|
|
call(sceNpUniversalDataSystemEventPropertyArraySetBool(list, true), "array SetBool failed");
|
|
call(sceNpUniversalDataSystemEventPropertyArraySetString(list, "s"), "array SetString failed");
|
|
call(sceNpUniversalDataSystemEventPropertyArraySetBinary(list, bytes, sizeof(bytes)), "array SetBinary failed");
|
|
NpUniversalDataSystemEventPropertyObject* element = nullptr;
|
|
call(sceNpUniversalDataSystemEventPropertyArraySetObject(list, nullptr, &element), "array SetObject failed");
|
|
call(sceNpUniversalDataSystemEventPropertyObjectSetUInt64(element, "z", 3), "SetUInt64 on the array element failed");
|
|
NpUniversalDataSystemEventPropertyArray* inner = nullptr;
|
|
call(sceNpUniversalDataSystemEventPropertyArraySetArray(list, nullptr, &inner), "array SetArray failed");
|
|
call(sceNpUniversalDataSystemEventPropertyArraySetBool(inner, false), "SetBool on the inner array failed");
|
|
const char* expected = R"({"child":{"x":1,"y":true},"list":[1,2,-3,4,1.5,2.5,true,"s",<2 bytes>,{"z":3},[false]]})";
|
|
check(Uds::Describe(*parent) == expected, "nested properties were not kept");
|
|
const auto copy = parent->Clone();
|
|
call(sceNpUniversalDataSystemEventPropertyObjectSetInt32(child, "x", 5), "SetInt32 on the child failed");
|
|
call(sceNpUniversalDataSystemEventPropertyArraySetBool(inner, true), "SetBool on the inner array failed");
|
|
check(Uds::Describe(copy) == expected, "a clone shares nested values with its source");
|
|
call(sceNpUniversalDataSystemDestroyEventPropertyObject(child), "destroying a child failed");
|
|
call(sceNpUniversalDataSystemDestroyEventPropertyArray(list), "destroying a child array failed");
|
|
check(Uds::Describe(*parent) == R"({"child":{"x":5,"y":true},"list":[1,2,-3,4,1.5,2.5,true,"s",<2 bytes>,{"z":3},[false,true]]})",
|
|
"destroying a child took it from its parent");
|
|
NpUniversalDataSystemEventPropertyArray* array = nullptr;
|
|
call(sceNpUniversalDataSystemCreateEventPropertyArray(&array), "property array was not created");
|
|
call(sceNpUniversalDataSystemEventPropertyArraySetInt32(array, 9), "SetInt32 on the created array failed");
|
|
NpUniversalDataSystemEventPropertyArray* stored = nullptr;
|
|
call(sceNpUniversalDataSystemEventPropertyObjectSetArray(parent, "list", array, &stored), "SetArray over a key failed");
|
|
call(sceNpUniversalDataSystemDestroyEventPropertyArray(array), "property array was not destroyed");
|
|
check(stored != array && Uds::Describe(*parent) == R"({"child":{"x":5,"y":true},"list":[9]})", "SetArray did not store a copy under its key");
|
|
check(throwsInvalidArgument([] { sceNpUniversalDataSystemEventPropertyArraySetInt32(nullptr, 1); }), "a null array was accepted");
|
|
call(sceNpUniversalDataSystemDestroyEventPropertyObject(parent), "parent object was not destroyed");
|
|
}
|
|
|
|
void testEvents() {
|
|
auto* properties = createObject();
|
|
call(sceNpUniversalDataSystemEventPropertyObjectSetInt32(properties, "_trophy_id", 12), "SetInt32 failed");
|
|
NpUniversalDataSystemEvent* event = nullptr;
|
|
NpUniversalDataSystemEventPropertyObject* eventProperties = nullptr;
|
|
call(sceNpUniversalDataSystemCreateEvent("_UnlockTrophy", properties, &event, &eventProperties), "event was not created");
|
|
check(event != nullptr && eventProperties == &event->properties, "CreateEvent did not return the event's property object");
|
|
call(sceNpUniversalDataSystemEventPropertyObjectSetInt32(properties, "_trophy_id", 13), "SetInt32 after CreateEvent failed");
|
|
call(sceNpUniversalDataSystemDestroyEventPropertyObject(properties), "property object was not destroyed");
|
|
check(Uds::Describe(*event) == R"(_UnlockTrophy {"_trophy_id":12})", "the event did not copy its properties");
|
|
call(sceNpUniversalDataSystemEventPropertyObjectSetString(eventProperties, "detail", "boss"), "SetString on the event failed");
|
|
call(sceNpUniversalDataSystemDestroyEventPropertyObject(eventProperties), "destroying the event's property object failed");
|
|
check(Uds::Describe(*event) == R"(_UnlockTrophy {"_trophy_id":12,"detail":"boss"})", "properties set through the returned pointer were lost");
|
|
call(sceNpUniversalDataSystemDestroyEvent(event), "event was not destroyed");
|
|
NpUniversalDataSystemEvent* empty = nullptr;
|
|
call(sceNpUniversalDataSystemCreateEvent("_Empty", nullptr, &empty, nullptr), "event without properties was not created");
|
|
check(Uds::Describe(*empty) == "_Empty {}", "an event without properties was not empty");
|
|
call(sceNpUniversalDataSystemDestroyEvent(empty), "event without properties was not destroyed");
|
|
check(throwsInvalidArgument([] {
|
|
NpUniversalDataSystemEvent* unnamed = nullptr;
|
|
sceNpUniversalDataSystemCreateEvent(nullptr, nullptr, &unnamed, nullptr);
|
|
}), "an event without a name was created");
|
|
}
|
|
|
|
Uds::TrophyEvent recognize(const char* name, std::optional<Uds::Value> id) {
|
|
NpUniversalDataSystemEvent event;
|
|
event.name = name;
|
|
if (id) event.properties.Set(Uds::TrophyIdProperty, *id);
|
|
return Uds::RecognizeTrophyEvent(event);
|
|
}
|
|
|
|
void testRecognition() {
|
|
const auto unlock = recognize("_UnlockTrophy", std::int32_t{12});
|
|
check(unlock.kind == Uds::TrophyEventKind::Unlock && unlock.trophyId == 12, "an unlock event was not recognised");
|
|
const auto first = recognize("_UnlockTrophy", std::int32_t{0});
|
|
check(first.kind == Uds::TrophyEventKind::Unlock && first.trophyId == 0, "an unlock of trophy 0 was not recognised");
|
|
const auto progress = recognize("_UpdateTrophyProgress", std::int32_t{3});
|
|
check(progress.kind == Uds::TrophyEventKind::Progress && progress.trophyId == 3, "a progress event was not recognised");
|
|
const auto anonymous = recognize("_UpdateTrophyProgress", std::nullopt);
|
|
check(anonymous.kind == Uds::TrophyEventKind::Progress && anonymous.trophyId == -1, "a progress event without a trophy id was not recognised");
|
|
const auto missing = recognize("_UnlockTrophy", std::nullopt);
|
|
check(missing.kind == Uds::TrophyEventKind::Malformed && missing.problem == "it has no _trophy_id", "an unlock without a trophy id was accepted");
|
|
const auto widened = recognize("_UnlockTrophy", std::uint32_t{12});
|
|
check(widened.kind == Uds::TrophyEventKind::Malformed && widened.problem == "its _trophy_id is not an int32", "an unlock with an unsigned trophy id was accepted");
|
|
const auto negative = recognize("_UnlockTrophy", std::int32_t{-1});
|
|
check(negative.kind == Uds::TrophyEventKind::Malformed && negative.problem == "its _trophy_id is negative", "an unlock with a negative trophy id was accepted");
|
|
for (const char* name : {"_unlockTrophy", "UnlockTrophy", "_UnlockTrophy2", "_UnlockTrophy ", "_StartActivity", ""}) {
|
|
check(recognize(name, std::int32_t{12}).kind == Uds::TrophyEventKind::None, "an unknown event was taken for a trophy event");
|
|
}
|
|
}
|
|
|
|
void testStatEvent() {
|
|
NpUniversalDataSystemEvent event;
|
|
event.name = "STEPS";
|
|
event.properties.Set("count", std::uint32_t{4});
|
|
event.properties.Set("signed", std::int32_t{-2});
|
|
event.properties.Set("wide", std::int64_t{7});
|
|
event.properties.Set("huge", std::uint64_t{18000000000000000000ull});
|
|
event.properties.Set("ratio", 0.5);
|
|
event.properties.Set("name", std::string("x"));
|
|
auto player = std::make_shared<NpUniversalDataSystemEventPropertyObject>();
|
|
player->Set("steps", std::uint32_t{5});
|
|
auto inner = std::make_shared<NpUniversalDataSystemEventPropertyObject>();
|
|
inner->Set("depth", std::int32_t{1});
|
|
player->Set("inner", inner);
|
|
event.properties.Set("player", player);
|
|
auto list = std::make_shared<NpUniversalDataSystemEventPropertyArray>();
|
|
list->Add(std::int32_t{1});
|
|
event.properties.Set("list", list);
|
|
const auto converted = Uds::StatEvent(event);
|
|
const std::vector<std::tuple<std::string, std::optional<std::uint64_t>, std::string>> expected{
|
|
{"count", 4, "4"}, {"signed", std::nullopt, "-2"}, {"wide", 7, "7"}, {"huge", 18000000000000000000ull, "18000000000000000000"}, {"ratio", std::nullopt, "0.5"},
|
|
{"name", std::nullopt, "\"x\""}, {"player.steps", 5, "5"}, {"player.inner.depth", 1, "1"}, {"list", std::nullopt, "[1]"}};
|
|
check(converted.name == "STEPS" && converted.properties.size() == expected.size(), "the stat event does not hold every leaf property");
|
|
for (std::size_t i = 0; i < expected.size(); ++i) {
|
|
const auto& [path, value, text] = expected[i];
|
|
const auto& property = converted.properties[i];
|
|
check(property.path == path && property.value == value && property.text == text, "a stat event property has the wrong path, value or text");
|
|
}
|
|
}
|
|
|
|
void testLocaleIndependentNumberFormatting() {
|
|
const char* previous = std::setlocale(LC_NUMERIC, nullptr);
|
|
check(previous != nullptr, "the current locale could not be read");
|
|
const std::string saved = previous;
|
|
struct RestoreLocale {
|
|
std::string saved;
|
|
~RestoreLocale() { std::setlocale(LC_NUMERIC, saved.c_str()); }
|
|
} restore{saved};
|
|
bool commaDecimal = false;
|
|
for (const auto* name : {"French", "French_France.1252", "fr_FR.UTF-8", "de_DE.UTF-8"}) {
|
|
if (std::setlocale(LC_NUMERIC, name) != nullptr && std::string(std::localeconv()->decimal_point) == ",") {
|
|
commaDecimal = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!commaDecimal) {
|
|
#ifdef _WIN32
|
|
throw std::runtime_error("No Windows comma-decimal locale available for regression");
|
|
#else
|
|
std::puts("Comma-decimal locale regression unavailable on this host");
|
|
return;
|
|
#endif
|
|
}
|
|
NpUniversalDataSystemEvent event;
|
|
event.properties.Set("float", 0.1f);
|
|
event.properties.Set("double", 0.1);
|
|
check(Uds::Describe(event.properties) == R"({"float":0.100000001,"double":0.10000000000000001})",
|
|
"described event properties changed with the numeric locale");
|
|
const auto converted = Uds::StatEvent(event);
|
|
check(converted.properties.size() == 2, "float and double event properties were lost");
|
|
check(converted.properties[0].text == "0.100000001", "float property text changed with the numeric locale");
|
|
check(converted.properties[1].text == "0.10000000000000001", "double property text changed with the numeric locale");
|
|
}
|
|
|
|
struct Unlocks {
|
|
int count = 0;
|
|
};
|
|
|
|
void countUnlock(void* context, const NpTrophy2Unlock&) {
|
|
++static_cast<Unlocks*>(context)->count;
|
|
}
|
|
|
|
void post(int context, const char* name, std::optional<Uds::Value> id) {
|
|
NpUniversalDataSystemEvent* event = nullptr;
|
|
NpUniversalDataSystemEventPropertyObject* properties = nullptr;
|
|
call(sceNpUniversalDataSystemCreateEvent(name, nullptr, &event, &properties), "event was not created");
|
|
if (id) properties->Set(Uds::TrophyIdProperty, *id);
|
|
call(sceNpUniversalDataSystemPostEvent(context, 1, event, 0), "event was not posted");
|
|
call(sceNpUniversalDataSystemDestroyEvent(event), "event was not destroyed");
|
|
}
|
|
|
|
void testPostingIgnoredEvents() {
|
|
Unlocks unlocks;
|
|
const auto listener = NpTrophy2AddUnlockListener_nid_no_patch(countUnlock, &unlocks);
|
|
int context = 0;
|
|
call(sceNpUniversalDataSystemCreateContext(&context, InitialUser, 0, 0), "context was not created");
|
|
int second = 0;
|
|
call(sceNpUniversalDataSystemCreateContext(&second, InitialUser, 0, 0), "second context was not created");
|
|
check(second != context, "two contexts share an id");
|
|
post(context, "_StartActivity", std::int32_t{1});
|
|
post(context, "_UnlockTrophy", std::string("1"));
|
|
post(context, "_UnlockTrophy", std::nullopt);
|
|
post(context, "_UpdateTrophyProgress", std::int32_t{2});
|
|
post(context, "_UpdateTrophyProgress", std::int32_t{2});
|
|
post(second + 1, "_UnlockTrophy", std::int32_t{4});
|
|
call(sceNpUniversalDataSystemDestroyContext(context), "context was not destroyed");
|
|
post(context, "_UnlockTrophy", std::int32_t{4});
|
|
call(sceNpUniversalDataSystemDestroyContext(second), "second context was not destroyed");
|
|
NpTrophy2RemoveUnlockListener_nid_no_patch(listener);
|
|
check(unlocks.count == 0, "an event that is not a valid unlock reached the trophy store");
|
|
check(throwsInvalidArgument([] { sceNpUniversalDataSystemPostEvent(1, 1, nullptr, 0); }), "a null event was posted");
|
|
NpUniversalDataSystemEvent stray;
|
|
stray.name = "_UnlockTrophy";
|
|
check(throwsInvalidArgument([&stray] { sceNpUniversalDataSystemPostEvent(1, 1, &stray, 0); }), "an event that CreateEvent did not create was posted");
|
|
NpUniversalDataSystemEvent* destroyed = nullptr;
|
|
call(sceNpUniversalDataSystemCreateEvent("_UnlockTrophy", nullptr, &destroyed, nullptr), "event was not created");
|
|
call(sceNpUniversalDataSystemDestroyEvent(destroyed), "event was not destroyed");
|
|
check(throwsInvalidArgument([destroyed] { sceNpUniversalDataSystemPostEvent(1, 1, destroyed, 0); }), "a destroyed event was posted");
|
|
}
|
|
|
|
}
|
|
|
|
int main() {
|
|
try {
|
|
testObjectSetters();
|
|
testNestedValues();
|
|
testEvents();
|
|
testRecognition();
|
|
testStatEvent();
|
|
testLocaleIndependentNumberFormatting();
|
|
testPostingIgnoredEvents();
|
|
std::puts("UDS event tests passed");
|
|
return 0;
|
|
} catch (const std::exception& error) {
|
|
std::fprintf(stderr, "%s\n", error.what());
|
|
return 1;
|
|
}
|
|
}
|