#include "prx/libc/include/general/VabiMacros.hpp" #include #include #include #include #include extern "C" { void APS5_VABI _ZN3sce2Np9CppWebApi6Common6StringC1EPNS2_10LibContextE(void* self, void* libContext); void APS5_VABI _ZN3sce2Np9CppWebApi6Common6StringD1Ev(void* self); int APS5_VABI _ZN3sce2Np9CppWebApi6Common6String6appendEPKc(void* self, const char* text); const char* APS5_VABI _ZNK3sce2Np9CppWebApi6Common6String5c_strEv(const void* self); void APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_6StringEEC1EPNS2_10LibContextE(void* self, void* libContext); void APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_6StringEED1Ev(void* self); int APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_6StringEE8pushBackERKS4_(void* self, const void* element); } namespace { void Require(bool value) { if (!value) std::abort(); } constexpr std::size_t kStringSize = 0x20; constexpr std::size_t kGuardSize = 0x10; constexpr std::size_t kReferenceCountOffset = 0x00; constexpr std::size_t kBufferOffset = 0x08; constexpr std::size_t kBufferSizeOffset = 0x10; constexpr std::size_t kLibContextOffset = 0x18; constexpr unsigned char kFill = 0xA5; constexpr std::size_t kVectorSize = 0x18; constexpr std::size_t kObjectOffset = 0x00; constexpr std::size_t kBodyMagicOffset = 0x00; constexpr std::size_t kBodyReferenceCountOffset = 0x30; constexpr std::size_t kBodyElementKindOffset = 0x34; constexpr std::size_t kBodyElementsOffset = 0x38; constexpr std::size_t kBodySizeOffset = 0x40; struct Storage { alignas(8) unsigned char bytes[kStringSize + kGuardSize]; }; struct VectorStorage { alignas(8) unsigned char bytes[kVectorSize + kGuardSize]; }; template TValue Load(const Storage& storage, std::size_t offset) { TValue value; std::memcpy(&value, storage.bytes + offset, sizeof(value)); return value; } template void Store(Storage& storage, std::size_t offset, TValue value) { std::memcpy(storage.bytes + offset, &value, sizeof(value)); } template TValue LoadBody(const void* body, std::size_t offset) { TValue value; std::memcpy(&value, static_cast(body) + offset, sizeof(value)); return value; } bool GuardIntact(const Storage& storage) { for (std::size_t i = kStringSize; i < sizeof(storage.bytes); i++) { if (storage.bytes[i] != kFill) return false; } return true; } bool VectorGuardIntact(const VectorStorage& storage) { for (std::size_t i = kVectorSize; i < sizeof(storage.bytes); i++) { if (storage.bytes[i] != kFill) return false; } return true; } void TestConstructedStringIsEmpty() { std::uint64_t libContext = 1; Storage storage; std::memset(storage.bytes, kFill, sizeof(storage.bytes)); _ZN3sce2Np9CppWebApi6Common6StringC1EPNS2_10LibContextE(storage.bytes, &libContext); Require(Load(storage, kReferenceCountOffset) == 0); Require(Load(storage, kBufferOffset) == nullptr); Require(Load(storage, kBufferSizeOffset) == 0); Require(Load(storage, kLibContextOffset) == &libContext); Require(GuardIntact(storage)); const char* text = _ZNK3sce2Np9CppWebApi6Common6String5c_strEv(storage.bytes); Require(text != nullptr && text[0] == '\0'); _ZN3sce2Np9CppWebApi6Common6StringD1Ev(storage.bytes); Require(Load(storage, kBufferOffset) == nullptr); Require(Load(storage, kBufferSizeOffset) == 0); Require(GuardIntact(storage)); } void TestNullLibContextIsStored() { Storage storage; std::memset(storage.bytes, kFill, sizeof(storage.bytes)); _ZN3sce2Np9CppWebApi6Common6StringC1EPNS2_10LibContextE(storage.bytes, nullptr); Require(Load(storage, kLibContextOffset) == nullptr); Require(Load(storage, kBufferOffset) == nullptr); Require(_ZNK3sce2Np9CppWebApi6Common6String5c_strEv(storage.bytes)[0] == '\0'); _ZN3sce2Np9CppWebApi6Common6StringD1Ev(storage.bytes); } void TestAppendBuildsAndClearsBuffer() { std::uint64_t libContext = 1; Storage storage; std::memset(storage.bytes, kFill, sizeof(storage.bytes)); _ZN3sce2Np9CppWebApi6Common6StringC1EPNS2_10LibContextE(storage.bytes, &libContext); Require(_ZN3sce2Np9CppWebApi6Common6String6appendEPKc(storage.bytes, "rank") == 0); Require(std::strcmp(_ZNK3sce2Np9CppWebApi6Common6String5c_strEv(storage.bytes), "rank") == 0); Require(_ZN3sce2Np9CppWebApi6Common6String6appendEPKc(storage.bytes, "ing") == 0); Require(std::strcmp(_ZNK3sce2Np9CppWebApi6Common6String5c_strEv(storage.bytes), "ranking") == 0); Require(Load(storage, kBufferOffset) != nullptr); Require(GuardIntact(storage)); _ZN3sce2Np9CppWebApi6Common6StringD1Ev(storage.bytes); Require(Load(storage, kBufferOffset) == nullptr); Require(Load(storage, kBufferSizeOffset) == 0); Require(GuardIntact(storage)); } void TestAppendNullKeepsEmpty() { std::uint64_t libContext = 1; Storage storage; std::memset(storage.bytes, kFill, sizeof(storage.bytes)); _ZN3sce2Np9CppWebApi6Common6StringC1EPNS2_10LibContextE(storage.bytes, &libContext); Require(_ZN3sce2Np9CppWebApi6Common6String6appendEPKc(storage.bytes, nullptr) == 0); Require(Load(storage, kBufferOffset) == nullptr); Require(_ZNK3sce2Np9CppWebApi6Common6String5c_strEv(storage.bytes)[0] == '\0'); _ZN3sce2Np9CppWebApi6Common6StringD1Ev(storage.bytes); } void TestVectorPushBackCopiesStringContent() { std::uint64_t libContext = 1; VectorStorage vector; Storage element; std::memset(vector.bytes, kFill, sizeof(vector.bytes)); std::memset(element.bytes, kFill, sizeof(element.bytes)); _ZN3sce2Np9CppWebApi6Common6VectorINS2_6StringEEC1EPNS2_10LibContextE(vector.bytes, &libContext); Require(VectorGuardIntact(vector)); _ZN3sce2Np9CppWebApi6Common6StringC1EPNS2_10LibContextE(element.bytes, &libContext); Require(_ZN3sce2Np9CppWebApi6Common6String6appendEPKc(element.bytes, "privilege") == 0); char* elementBuffer = Load(element, kBufferOffset); Require(elementBuffer != nullptr); Require(_ZN3sce2Np9CppWebApi6Common6VectorINS2_6StringEE8pushBackERKS4_(vector.bytes, element.bytes) == 0); void* body; std::memcpy(&body, vector.bytes + kObjectOffset, sizeof(body)); Require(body != nullptr); Require(LoadBody(body, kBodyReferenceCountOffset) == 1); Require(LoadBody(body, kBodyElementKindOffset) == 2); Require(LoadBody(body, kBodySizeOffset) == 1); void* elements = LoadBody(body, kBodyElementsOffset); char* storedBuffer; std::memcpy(&storedBuffer, static_cast(elements) + kBufferOffset, sizeof(storedBuffer)); Require(storedBuffer != nullptr); Require(storedBuffer != elementBuffer); Require(std::strcmp(storedBuffer, "privilege") == 0); _ZN3sce2Np9CppWebApi6Common6StringD1Ev(element.bytes); Require(std::strcmp(storedBuffer, "privilege") == 0); _ZN3sce2Np9CppWebApi6Common6VectorINS2_6StringEED1Ev(vector.bytes); Require(VectorGuardIntact(vector)); } } int main() { TestConstructedStringIsEmpty(); TestNullLibContextIsStored(); TestAppendBuildsAndClearsBuffer(); TestAppendNullKeepsEmpty(); TestVectorPushBackCopiesStringContent(); }