mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-11-04 13:07:32 +00:00
135 lines
7.3 KiB
C++
135 lines
7.3 KiB
C++
#include "prx/libc/include/general/VabiMacros.hpp"
|
|
#include <algorithm>
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <string_view>
|
|
|
|
struct Value { alignas(8) std::uint64_t words[4]{}; };
|
|
struct Parameter { void* allocator; void* context; std::size_t bufferSize; std::int32_t mode; std::uint32_t reserved[3]; };
|
|
extern "C" {
|
|
void APS5_VABI _ZN3sce4Json11InitializerC1Ev(void*);
|
|
int APS5_VABI _ZN3sce4Json11Initializer10initializeEPKNS0_14InitParameter2E(void*, const Parameter*);
|
|
int APS5_VABI _ZN3sce4Json11Initializer9terminateEv(void*);
|
|
void APS5_VABI _ZN3sce4Json5ValueC1Ev(Value*);
|
|
void APS5_VABI _ZN3sce4Json5ValueD1Ev(Value*);
|
|
int APS5_VABI _ZN3sce4Json6Parser5parseERNS0_5ValueEPKc(Value*, const char*);
|
|
const std::int64_t* APS5_VABI _ZNK3sce4Json5Value10getIntegerEv(const Value*);
|
|
const Value* APS5_VABI _ZNK3sce4Json5ValueixEPKc(const Value*, const char*);
|
|
}
|
|
static void Require(bool valid, const char* message) {
|
|
if (!valid) { std::fprintf(stderr, "%s\n", message); std::abort(); }
|
|
}
|
|
struct FileTrace {
|
|
std::string_view input;
|
|
std::size_t position = 0;
|
|
std::size_t chunk = 1;
|
|
std::size_t bufferSize = 997;
|
|
int descriptor = 99;
|
|
std::int64_t readError = 0;
|
|
int opens = 0;
|
|
int reads = 0;
|
|
int closes = 0;
|
|
int bufferAllocations = 0;
|
|
int bufferFrees = 0;
|
|
int notifications = 0;
|
|
void* buffer = nullptr;
|
|
bool failBuffer = false;
|
|
};
|
|
static FileTrace* activeFile = nullptr;
|
|
extern "C" int APS5_VABI sceKernelOpen(const char* path, int flags, std::uint16_t mode) {
|
|
auto& trace = *activeFile;
|
|
Require(std::strcmp(path, "input.json") == 0 && flags == 0 && mode == 0555, "File parser must pass its filename and native open arguments");
|
|
Require(trace.buffer != nullptr, "Open must follow successful file-buffer allocation");
|
|
++trace.opens;
|
|
return trace.descriptor;
|
|
}
|
|
extern "C" std::int64_t APS5_VABI sceKernelRead(int descriptor, void* output, std::size_t bytes) {
|
|
auto& trace = *activeFile;
|
|
Require(descriptor == trace.descriptor && output == trace.buffer && bytes == trace.bufferSize, "Read must use the configured buffer and descriptor");
|
|
++trace.reads;
|
|
if (trace.readError) return trace.readError;
|
|
const auto count = std::min({bytes, trace.chunk, trace.input.size() - trace.position});
|
|
std::memcpy(output, trace.input.data() + trace.position, count);
|
|
trace.position += count;
|
|
return static_cast<std::int64_t>(count);
|
|
}
|
|
extern "C" int APS5_VABI sceKernelClose(int descriptor) {
|
|
auto& trace = *activeFile;
|
|
Require(descriptor == trace.descriptor && trace.buffer != nullptr, "Close must precede release of the file buffer");
|
|
++trace.closes;
|
|
return 0;
|
|
}
|
|
static void* APS5_VABI AllocateFile(void*, std::size_t bytes, void* context) {
|
|
auto& trace = *static_cast<FileTrace*>(context);
|
|
if (bytes == trace.bufferSize) {
|
|
++trace.bufferAllocations;
|
|
if (trace.failBuffer) return nullptr;
|
|
Require(!trace.buffer, "Only one live file buffer per parse");
|
|
trace.buffer = std::malloc(bytes);
|
|
return trace.buffer;
|
|
}
|
|
return std::malloc(bytes);
|
|
}
|
|
static void APS5_VABI FreeFile(void*, void* memory, void* context) {
|
|
auto& trace = *static_cast<FileTrace*>(context);
|
|
if (memory && memory == trace.buffer) { ++trace.bufferFrees; trace.buffer = nullptr; }
|
|
std::free(memory);
|
|
}
|
|
static void APS5_VABI NotifyFile(void*, int error, std::size_t bytes, void* context) {
|
|
auto& trace = *static_cast<FileTrace*>(context);
|
|
Require(static_cast<unsigned>(error) == 0x80848102 && bytes == trace.bufferSize, "File allocation failure must notify its exact size and OOM status");
|
|
++trace.notifications;
|
|
}
|
|
static void* const FileVtable[]{nullptr, nullptr, reinterpret_cast<void*>(AllocateFile), reinterpret_cast<void*>(FreeFile), reinterpret_cast<void*>(NotifyFile)};
|
|
struct FileAllocator { void* const* vtable = FileVtable; };
|
|
static void CheckParse(FileTrace& trace, std::string_view input, int descriptor, std::int64_t error, int expected) {
|
|
trace.input = input; trace.position = 0; trace.descriptor = descriptor; trace.readError = error;
|
|
const int opens = trace.opens, closes = trace.closes, frees = trace.bufferFrees;
|
|
Value value{};
|
|
_ZN3sce4Json5ValueC1Ev(&value);
|
|
Require(_ZN3sce4Json6Parser5parseERNS0_5ValueEPKc(&value, "input.json") == expected, "File parse must preserve success, syntax, open and read results");
|
|
Require(trace.opens == opens + 1 && trace.closes == closes + (descriptor >= 0 ? 1 : 0) && trace.bufferFrees == frees + 1 && !trace.buffer,
|
|
"Every completed file parse must close its owned descriptor and release its buffer exactly once");
|
|
if (!expected) Require(*_ZNK3sce4Json5Value10getIntegerEv(_ZNK3sce4Json5ValueixEPKc(&value, "answer")) == 42, "Short reads must assemble the parsed result");
|
|
_ZN3sce4Json5ValueD1Ev(&value);
|
|
}
|
|
int main() {
|
|
FileTrace trace; activeFile = &trace;
|
|
FileAllocator allocator;
|
|
Parameter parameter{&allocator, &trace, trace.bufferSize, 2, {}};
|
|
std::uint8_t initializer = 0;
|
|
_ZN3sce4Json11InitializerC1Ev(&initializer);
|
|
Value value{}; _ZN3sce4Json5ValueC1Ev(&value);
|
|
Require(static_cast<unsigned>(_ZN3sce4Json6Parser5parseERNS0_5ValueEPKc(&value, "input.json")) == 0x80848110 && !trace.opens && !trace.bufferAllocations, "Inactive file parser must refuse before allocation or open");
|
|
Require(_ZN3sce4Json11Initializer10initializeEPKNS0_14InitParameter2E(&initializer, ¶meter) == 0, "Initialize file parser");
|
|
parameter.bufferSize = 3;
|
|
for (std::size_t chunk : {std::size_t{1}, std::size_t{7}, trace.bufferSize}) {
|
|
trace.chunk = chunk;
|
|
CheckParse(trace, "{\"answer\":42}", 99, 0, 0);
|
|
}
|
|
CheckParse(trace, "", 99, 0, static_cast<int>(0x80848101));
|
|
CheckParse(trace, "{\"answer\":", 99, 0, static_cast<int>(0x80848101));
|
|
CheckParse(trace, "", static_cast<int>(0x80020002), 0, static_cast<int>(0x80848103));
|
|
CheckParse(trace, "", static_cast<int>(0x8002000d), 0, static_cast<int>(0x8002000d));
|
|
CheckParse(trace, "", -1, 0, static_cast<int>(0x80848110));
|
|
CheckParse(trace, "", 99, static_cast<int>(0x80020005), static_cast<int>(0x80020005));
|
|
trace.failBuffer = true;
|
|
const int opens = trace.opens, closes = trace.closes, frees = trace.bufferFrees;
|
|
Require(static_cast<unsigned>(_ZN3sce4Json6Parser5parseERNS0_5ValueEPKc(&value, "input.json")) == 0x80848110 && trace.notifications == 1 && trace.opens == opens && trace.closes == closes && trace.bufferFrees == frees,
|
|
"Failed file-buffer allocation must notify, avoid open and preserve the native parser status");
|
|
trace.failBuffer = false;
|
|
CheckParse(trace, "{\"answer\":42}", 99, 0, 0);
|
|
_ZN3sce4Json5ValueD1Ev(&value);
|
|
Require(_ZN3sce4Json11Initializer9terminateEv(&initializer) == 0, "Terminate file parser");
|
|
parameter.bufferSize = 0;
|
|
Require(_ZN3sce4Json11Initializer10initializeEPKNS0_14InitParameter2E(&initializer, ¶meter) == 0, "Initialize without a file buffer");
|
|
_ZN3sce4Json5ValueC1Ev(&value);
|
|
Require(static_cast<unsigned>(_ZN3sce4Json6Parser5parseERNS0_5ValueEPKc(&value, "input.json")) == 0x80848105 && trace.opens == opens + 1, "Zero configured file-buffer size must refuse before open");
|
|
_ZN3sce4Json5ValueD1Ev(&value);
|
|
Require(_ZN3sce4Json11Initializer9terminateEv(&initializer) == 0, "Terminate zero-buffer configuration");
|
|
return 0;
|
|
}
|