#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); const char* APS5_VABI _ZNK3sce2Np9CppWebApi6Common6String5c_strEv(const void* self); } 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; struct Storage { alignas(8) unsigned char bytes[kStringSize + 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)); } 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 DestructorThrows(Storage& storage) { try { _ZN3sce2Np9CppWebApi6Common6StringD1Ev(storage.bytes); } catch (const std::runtime_error&) { return true; } return false; } 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'); Storage before = storage; _ZN3sce2Np9CppWebApi6Common6StringD1Ev(storage.bytes); Require(std::memcmp(before.bytes, storage.bytes, sizeof(storage.bytes)) == 0); } 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 TestBufferIsReturnedAndNotDroppedSilently() { std::uint64_t libContext = 1; char text[] = "ranking"; Storage storage; std::memset(storage.bytes, kFill, sizeof(storage.bytes)); _ZN3sce2Np9CppWebApi6Common6StringC1EPNS2_10LibContextE(storage.bytes, &libContext); Store(storage, kBufferOffset, text); Store(storage, kBufferSizeOffset, sizeof(text)); Require(_ZNK3sce2Np9CppWebApi6Common6String5c_strEv(storage.bytes) == text); Require(DestructorThrows(storage)); Require(std::strcmp(text, "ranking") == 0); } } int main() { TestConstructedStringIsEmpty(); TestNullLibContextIsStored(); TestBufferIsReturnedAndNotDroppedSilently(); }