mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-11-07 13:39:55 +00:00
226 lines
16 KiB
C++
226 lines
16 KiB
C++
#include "prx/libSceNpUniversalDataSystem/include/Api.hpp"
|
|
#include "prx/libc/include/general/VabiMacros.hpp"
|
|
#include <atomic>
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <memory>
|
|
#include <thread>
|
|
#include <vector>
|
|
|
|
namespace {
|
|
|
|
constexpr int kInvalidArgument = static_cast<int>(0x80553102u);
|
|
constexpr int kInvalidName = static_cast<int>(0x8055310Du);
|
|
constexpr int kInvalidParamSize = static_cast<int>(0x80553112u);
|
|
constexpr int kInvalidKey = static_cast<int>(0x80553114u);
|
|
constexpr int kNullValue = static_cast<int>(0x80553115u);
|
|
constexpr int kNullRecord = static_cast<int>(0x80553116u);
|
|
constexpr int kNotInitialized = static_cast<int>(0x80553117u);
|
|
constexpr int kAlreadyInitialized = static_cast<int>(0x80553118u);
|
|
constexpr int kNullObject = static_cast<int>(0x80553119u);
|
|
constexpr int kNullArray = static_cast<int>(0x8055311Au);
|
|
|
|
void Require(bool condition, const char* message) {
|
|
if (!condition) {
|
|
std::fprintf(stderr, "NpUniversalDataSystem records: %s\n", message);
|
|
std::abort();
|
|
}
|
|
}
|
|
|
|
const std::shared_ptr<NpUniversalDataSystemEventPropertyObject>* StoredObject(void* record, const char* key) {
|
|
const auto* object = static_cast<NpUniversalDataSystemEventPropertyObject*>(record);
|
|
const auto* value = object->Find(key);
|
|
return value != nullptr ? std::get_if<std::shared_ptr<NpUniversalDataSystemEventPropertyObject>>(value) : nullptr;
|
|
}
|
|
|
|
const std::shared_ptr<NpUniversalDataSystemEventPropertyArray>* StoredArray(void* record, const char* key) {
|
|
const auto* object = static_cast<NpUniversalDataSystemEventPropertyObject*>(record);
|
|
const auto* value = object->Find(key);
|
|
return value != nullptr ? std::get_if<std::shared_ptr<NpUniversalDataSystemEventPropertyArray>>(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<void*>(1), &child) == kNullObject, "unknown array object");
|
|
Require(sceNpUniversalDataSystemIntRecordArraySetObject(array, object, &child) == 0 && child != nullptr, "array object");
|
|
const auto* storedArrayObject = static_cast<NpUniversalDataSystemEventPropertyArray*>(array);
|
|
Require(storedArrayObject->values.size() >= 1, "array object stored");
|
|
const auto* storedObject = std::get_if<std::shared_ptr<NpUniversalDataSystemEventPropertyObject>>(&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<NpUniversalDataSystemEventPropertyObject*>(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<std::shared_ptr<NpUniversalDataSystemEventPropertyArray>>(&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<void*>(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<void*>(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<NpUniversalDataSystemEventPropertyObject*>(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<void*>(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<void*>(1), "key", 0) == kNullObject, "unknown object target");
|
|
|
|
Require(sceNpUniversalDataSystemIntRecordObjectSetString(data, "extra", "value") == 0, "set on record data");
|
|
Require(static_cast<NpUniversalDataSystemEventPropertyObject*>(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<bool> go{false};
|
|
std::atomic<int> succeeded{0};
|
|
std::atomic<int> rejected{0};
|
|
std::vector<std::thread> 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<bool> go{false};
|
|
std::atomic<bool> stop{false};
|
|
std::atomic<int> 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");
|
|
}
|