#include "prx/libSceNpUniversalDataSystem/include/Api.hpp" #include "prx/libc/include/general/VabiMacros.hpp" #include #include #include #include #include #include #include #include namespace { constexpr int kInvalidArgument = static_cast(0x80553102u); constexpr int kInvalidName = static_cast(0x8055310Du); constexpr int kInvalidParamSize = static_cast(0x80553112u); constexpr int kInvalidKey = static_cast(0x80553114u); constexpr int kNullValue = static_cast(0x80553115u); constexpr int kNullRecord = static_cast(0x80553116u); constexpr int kNotInitialized = static_cast(0x80553117u); constexpr int kAlreadyInitialized = static_cast(0x80553118u); constexpr int kNullObject = static_cast(0x80553119u); constexpr int kNullArray = static_cast(0x8055311Au); void Require(bool condition, const char* message) { if (!condition) { std::fprintf(stderr, "NpUniversalDataSystem records: %s\n", message); std::abort(); } } const std::shared_ptr* StoredObject(void* record, const char* key) { const auto* object = static_cast(record); const auto* value = object->Find(key); return value != nullptr ? std::get_if>(value) : nullptr; } const std::shared_ptr* StoredArray(void* record, const char* key) { const auto* object = static_cast(record); const auto* value = object->Find(key); return value != nullptr ? std::get_if>(value) : nullptr; } } int main() { std::uint8_t memory[16] = {}; NpUniversalDataSystemIntInitParam param{sizeof(NpUniversalDataSystemIntInitParam), memory}; NpUniversalDataSystemIntInitParam wrongSize{0, memory}; Require(sceNpUniversalDataSystemIntTerminate() == kNotInitialized, "terminate before initialize"); Require(sceNpUniversalDataSystemIntDeleteAllRecord() == kNotInitialized, "delete records before initialize"); Require(sceNpUniversalDataSystemIntGetMemoryStat(nullptr) == kNotInitialized, "memory stat before initialize"); Require(sceNpUniversalDataSystemIntCreateRecordArray(nullptr) == kNotInitialized, "create array before initialize"); Require(sceNpUniversalDataSystemIntInitialize(nullptr) == kInvalidArgument, "null initialize parameter"); Require(sceNpUniversalDataSystemIntInitialize(&wrongSize) == kInvalidParamSize, "wrong parameter size"); param.memory = nullptr; Require(sceNpUniversalDataSystemIntInitialize(¶m) == kInvalidArgument, "null memory pool"); param.memory = memory; Require(sceNpUniversalDataSystemIntInitialize(¶m) == 0, "initialize failed"); Require(sceNpUniversalDataSystemIntInitialize(¶m) == kAlreadyInitialized, "second initialize"); Require(sceNpUniversalDataSystemIntGetMemoryStat(nullptr) == kInvalidArgument, "null memory stat"); void* array = nullptr; void* object = nullptr; void* data = nullptr; Require(sceNpUniversalDataSystemIntCreateRecordArray(nullptr) == kInvalidArgument, "null array output"); Require(sceNpUniversalDataSystemIntCreateRecordArray(&array) == 0 && array != nullptr, "create array"); Require(sceNpUniversalDataSystemIntCreateRecordObject(nullptr) == kInvalidArgument, "null object output"); Require(sceNpUniversalDataSystemIntCreateRecordObject(&object) == 0 && object != nullptr, "create object"); Require(sceNpUniversalDataSystemIntCreateRecordData(nullptr, &data) == kInvalidName, "null record type"); Require(sceNpUniversalDataSystemIntCreateRecordData("", &data) == kInvalidName, "empty record type"); Require(sceNpUniversalDataSystemIntCreateRecordData("type", nullptr) == kInvalidArgument, "null data output"); Require(sceNpUniversalDataSystemIntCreateRecordData("type", &data) == 0 && data != nullptr, "create data"); std::size_t size = 0; Require(sceNpUniversalDataSystemIntRecordDataEstimateSize(nullptr, &size) == kNullRecord, "null estimate record"); Require(sceNpUniversalDataSystemIntRecordDataEstimateSize(data, nullptr) == kInvalidArgument, "null estimate output"); Require(sceNpUniversalDataSystemIntRecordArraySetBool(array, true) == 0, "array bool"); Require(sceNpUniversalDataSystemIntRecordArraySetInt32(array, -1) == 0, "array int32"); Require(sceNpUniversalDataSystemIntRecordArraySetInt64(array, -2) == 0, "array int64"); Require(sceNpUniversalDataSystemIntRecordArraySetUInt32(array, 3) == 0, "array uint32"); Require(sceNpUniversalDataSystemIntRecordArraySetUInt64(array, 4) == 0, "array uint64"); Require(sceNpUniversalDataSystemIntRecordArraySetFloat32(array, 1.5f) == 0, "array float32"); Require(sceNpUniversalDataSystemIntRecordArraySetFloat64(array, 2.5) == 0, "array float64"); Require(sceNpUniversalDataSystemIntRecordArraySetString(array, "value") == 0, "array string"); Require(sceNpUniversalDataSystemIntRecordArraySetString(array, nullptr) == kNullValue, "null array string"); const std::uint8_t bytes[3] = {1, 2, 3}; Require(sceNpUniversalDataSystemIntRecordArraySetBinary(array, bytes, sizeof(bytes)) == 0, "array binary"); Require(sceNpUniversalDataSystemIntRecordArraySetBinary(array, nullptr, 0) == kNullValue, "null array binary"); void* child = nullptr; Require(sceNpUniversalDataSystemIntRecordArraySetObject(array, nullptr, &child) == kNullValue, "null array object"); Require(sceNpUniversalDataSystemIntRecordArraySetObject(array, reinterpret_cast(1), &child) == kNullObject, "unknown array object"); Require(sceNpUniversalDataSystemIntRecordArraySetObject(array, object, &child) == 0 && child != nullptr, "array object"); const auto* storedArrayObject = static_cast(array); Require(storedArrayObject->values.size() >= 1, "array object stored"); const auto* storedObject = std::get_if>(&storedArrayObject->values.back()); Require(storedObject != nullptr && storedObject->get() == child, "array object child pointer"); Require(sceNpUniversalDataSystemIntRecordObjectSetInt32(child, "nested", 7) == 0, "set on array child"); Require((*storedObject)->Find("nested") != nullptr, "array child mutation stored"); Require(static_cast(object)->Find("nested") == nullptr, "array child clone independent"); void* arrayChild = nullptr; Require(sceNpUniversalDataSystemIntRecordArraySetArray(array, nullptr, &arrayChild) == kNullValue, "null array array"); Require(sceNpUniversalDataSystemIntRecordArraySetArray(array, array, &arrayChild) == 0 && arrayChild != nullptr, "array array"); const auto* storedNestedArray = std::get_if>(&storedArrayObject->values.back()); Require(storedNestedArray != nullptr && storedNestedArray->get() == arrayChild, "array array child pointer"); Require(sceNpUniversalDataSystemIntRecordArraySetInt32(arrayChild, 8) == 0, "set on array child array"); Require((*storedNestedArray)->values.size() >= 1, "array child array mutation stored"); Require(sceNpUniversalDataSystemIntRecordArraySetInt32(nullptr, 0) == kNullArray, "null array target"); Require(sceNpUniversalDataSystemIntRecordArraySetInt32(reinterpret_cast(1), 0) == kNullArray, "unknown array target"); Require(sceNpUniversalDataSystemIntRecordObjectSetBool(object, nullptr, true) == kInvalidKey, "null object key"); Require(sceNpUniversalDataSystemIntRecordObjectSetBool(object, "", true) == kInvalidKey, "empty object key"); Require(sceNpUniversalDataSystemIntRecordObjectSetBool(object, "bool", true) == 0, "object bool"); Require(sceNpUniversalDataSystemIntRecordObjectSetInt32(object, "int32", -1) == 0, "object int32"); Require(sceNpUniversalDataSystemIntRecordObjectSetInt64(object, "int64", -2) == 0, "object int64"); Require(sceNpUniversalDataSystemIntRecordObjectSetUInt32(object, "uint32", 3) == 0, "object uint32"); Require(sceNpUniversalDataSystemIntRecordObjectSetUInt64(object, "uint64", 4) == 0, "object uint64"); Require(sceNpUniversalDataSystemIntRecordObjectSetFloat32(object, "float32", 1.5f) == 0, "object float32"); Require(sceNpUniversalDataSystemIntRecordObjectSetFloat64(object, "float64", 2.5) == 0, "object float64"); Require(sceNpUniversalDataSystemIntRecordObjectSetString(object, "string", "value") == 0, "object string"); Require(sceNpUniversalDataSystemIntRecordObjectSetString(object, "string", nullptr) == kNullValue, "null object string"); Require(sceNpUniversalDataSystemIntRecordObjectSetBinary(object, "binary", bytes, sizeof(bytes)) == 0, "object binary"); Require(sceNpUniversalDataSystemIntRecordObjectSetBinary(object, "binary", nullptr, 0) == kNullValue, "null object binary"); child = nullptr; Require(sceNpUniversalDataSystemIntRecordObjectSetObject(object, "object", nullptr, &child) == kNullValue, "null object object"); Require(sceNpUniversalDataSystemIntRecordObjectSetObject(object, "object", reinterpret_cast(1), &child) == kNullObject, "unknown object object"); Require(sceNpUniversalDataSystemIntRecordObjectSetObject(object, "object", object, &child) == 0 && child != nullptr, "object object"); const auto* storedChild = StoredObject(object, "object"); Require(storedChild != nullptr && storedChild->get() == child, "object child pointer"); Require(sceNpUniversalDataSystemIntRecordObjectSetInt32(child, "nested", 9) == 0, "set on object child"); Require((*storedChild)->Find("nested") != nullptr, "object child mutation stored"); Require(static_cast(object)->Find("nested") == nullptr, "object child clone independent"); void* objectArrayChild = nullptr; Require(sceNpUniversalDataSystemIntRecordObjectSetArray(object, "array", nullptr, &objectArrayChild) == kNullValue, "null object array"); Require(sceNpUniversalDataSystemIntRecordObjectSetArray(object, "array", reinterpret_cast(1), &objectArrayChild) == kNullArray, "unknown object array"); Require(sceNpUniversalDataSystemIntRecordObjectSetArray(object, "array", array, &objectArrayChild) == 0 && objectArrayChild != nullptr, "object array"); const auto* storedChildArray = StoredArray(object, "array"); Require(storedChildArray != nullptr && storedChildArray->get() == objectArrayChild, "object child array pointer"); Require(sceNpUniversalDataSystemIntRecordArraySetInt32(objectArrayChild, 10) == 0, "set on object child array"); Require((*storedChildArray)->values.size() >= 1, "object child array mutation stored"); Require(sceNpUniversalDataSystemIntRecordObjectSetInt32(nullptr, "key", 0) == kNullObject, "null object target"); Require(sceNpUniversalDataSystemIntRecordObjectSetInt32(reinterpret_cast(1), "key", 0) == kNullObject, "unknown object target"); Require(sceNpUniversalDataSystemIntRecordObjectSetString(data, "extra", "value") == 0, "set on record data"); Require(static_cast(data)->Find("extra") != nullptr, "record data mutation stored"); void* destroyedChild = nullptr; Require(sceNpUniversalDataSystemIntRecordObjectSetObject(data, "child", object, &destroyedChild) == 0, "destroyed child setup"); Require(sceNpUniversalDataSystemIntDestroyRecordData(data) == 0, "destroy data"); Require(sceNpUniversalDataSystemIntRecordObjectSetInt32(destroyedChild, "key", 0) == kNullObject, "destroyed owner invalidates child"); Require(sceNpUniversalDataSystemIntDestroyRecordObject(destroyedChild) == 0, "destroy child is a no-op"); Require(sceNpUniversalDataSystemIntDestroyRecordArray(nullptr) == 0, "destroy null array"); Require(sceNpUniversalDataSystemIntDestroyRecordObject(nullptr) == 0, "destroy null object"); Require(sceNpUniversalDataSystemIntDestroyRecordData(nullptr) == 0, "destroy null data"); Require(sceNpUniversalDataSystemIntDestroyRecordArray(array) == 0, "destroy array"); Require(sceNpUniversalDataSystemIntDestroyRecordObject(object) == 0, "destroy object"); std::size_t unused = 0; Require(sceNpUniversalDataSystemIntRecordDataEstimateSize(array, &unused) == kNullRecord, "destroyed array estimate"); Require(sceNpUniversalDataSystemIntRecordDataEstimateSize(object, &unused) == kNullRecord, "destroyed object estimate"); Require(sceNpUniversalDataSystemIntCreateRecordArray(&array) == 0, "recreate array"); Require(sceNpUniversalDataSystemIntCreateRecordObject(&object) == 0, "recreate object"); Require(sceNpUniversalDataSystemIntDeleteAllRecord() == 0, "delete all records"); Require(sceNpUniversalDataSystemIntRecordDataEstimateSize(array, &unused) == kNullRecord, "deleted array estimate"); Require(sceNpUniversalDataSystemIntRecordDataEstimateSize(object, &unused) == kNullRecord, "deleted object estimate"); for (int round = 0; round < 32; ++round) { Require(sceNpUniversalDataSystemIntTerminate() == 0, "contention terminate"); std::atomic go{false}; std::atomic succeeded{0}; std::atomic rejected{0}; std::vector threads; for (int i = 0; i < 8; ++i) { threads.emplace_back([&] { while (!go.load()) std::this_thread::yield(); const int result = sceNpUniversalDataSystemIntInitialize(¶m); if (result == 0) ++succeeded; if (result == kAlreadyInitialized) ++rejected; }); } go.store(true); for (auto& thread : threads) thread.join(); Require(succeeded.load() == 1, "one initializer wins"); Require(rejected.load() == 7, "other initializers rejected"); } Require(sceNpUniversalDataSystemIntTerminate() == 0, "terminate after contention"); Require(sceNpUniversalDataSystemIntInitialize(¶m) == 0, "reinitialize after contention"); for (int round = 0; round < 64; ++round) { void* owner = nullptr; Require(sceNpUniversalDataSystemIntCreateRecordObject(&owner) == 0, "lifetime owner"); void* lifetimeChild = nullptr; Require(sceNpUniversalDataSystemIntRecordObjectSetObject(owner, "child", owner, &lifetimeChild) == 0, "lifetime child"); std::atomic go{false}; std::atomic stop{false}; std::atomic unexpected{0}; std::thread worker([&] { while (!go.load()) std::this_thread::yield(); while (!stop.load()) { const int result = sceNpUniversalDataSystemIntRecordObjectSetInt32(lifetimeChild, "key", 1); if (result != 0 && result != kNullObject) ++unexpected; } }); go.store(true); for (int i = 0; i < 100; ++i) std::this_thread::yield(); Require(sceNpUniversalDataSystemIntDestroyRecordObject(owner) == 0, "destroy owner"); stop.store(true); worker.join(); Require(unexpected.load() == 0, "operation against destruction"); Require(sceNpUniversalDataSystemIntRecordObjectSetInt32(lifetimeChild, "key", 1) == kNullObject, "child invalid after destruction"); } Require(sceNpUniversalDataSystemIntTerminate() == 0, "terminate failed"); Require(sceNpUniversalDataSystemIntTerminate() == kNotInitialized, "second terminate"); Require(sceNpUniversalDataSystemIntCreateRecordArray(&array) == kNotInitialized, "create after terminate"); Require(sceNpUniversalDataSystemIntInitialize(¶m) == 0, "reinitialize"); Require(sceNpUniversalDataSystemIntTerminate() == 0, "final terminate"); }