mirror of
https://github.com/ApfelTeeSaft/OpenRoyale.git
synced 2026-08-27 11:53:34 +00:00
122 lines
5.4 KiB
C++
122 lines
5.4 KiB
C++
// test_reconstruction.cpp - behavioral tests for the reconstructed libg native code.
|
|
//
|
|
// Compiled for the HOST (x86_64) and run directly. Because the reconstruction is arch-independent
|
|
// C++, this exercises the exact logic that ships in the arm64/v7a .so - so it validates the
|
|
// *function-matching* claim behaviourally, not just that the code compiles. Covers the String class
|
|
// (SSO + heap) and the dual-mode FileHandle (stdio backend; the AAsset backend needs a device).
|
|
#include "engine_structs.h"
|
|
#include "file_handle.h"
|
|
#include <cstdio>
|
|
#include <cstring>
|
|
#include <cstdlib>
|
|
|
|
using namespace titan;
|
|
|
|
// Host stubs for the NDK AAsset C API. These tests exercise the stdio (FILE*) backend only, so the
|
|
// asset branch is never taken; the stubs merely satisfy the linker (on device these live in
|
|
// libandroid.so). Signatures must match the forward declarations in file_handle.h.
|
|
extern "C" {
|
|
int AAsset_read(AAsset*, void*, size_t) { return -1; }
|
|
long AAsset_seek(AAsset*, long, int) { return -1; }
|
|
long AAsset_getLength(AAsset*) { return 0; }
|
|
void AAsset_close(AAsset*) {}
|
|
AAsset* AAssetManager_open(AAssetManager*, const char*, int) { return nullptr; }
|
|
}
|
|
|
|
// Reconstructed String methods (defined in titan_string.cpp / sub_1fe_cluster.cpp).
|
|
namespace titan {
|
|
struct_unknown_1* sub_1fe43c(struct_unknown_1*);
|
|
int sub_1f7160(const struct_unknown_1*);
|
|
const char* fcn_001f712c(const struct_unknown_1*);
|
|
bool fcn_00200064(const struct_unknown_1*, const void*, int, int);
|
|
bool fcn_0020010c(const struct_unknown_1*, const char*);
|
|
bool fcn_002000d8(const struct_unknown_1*, const struct_unknown_1*);
|
|
struct_unknown_1* fcn_001fea7c(struct_unknown_1*, const char*);
|
|
struct_unknown_1* fcn_001feb70(struct_unknown_1*, const char*);
|
|
struct_unknown_1* fcn_001fe9b8(struct_unknown_1*);
|
|
struct_unknown_1* fcn_001feb9c(struct_unknown_1*, const struct_unknown_1*);
|
|
const char* fcn_001f7044(struct_unknown_1*);
|
|
}
|
|
|
|
static int g_fail = 0, g_pass = 0;
|
|
#define CHECK(cond, msg) do { if (cond) { ++g_pass; } else { ++g_fail; \
|
|
std::printf(" FAIL: %s\n", msg); } } while (0)
|
|
|
|
static void test_string_sso() {
|
|
std::printf("[String] short string (SSO path)\n");
|
|
struct_unknown_1 s;
|
|
fcn_001feb70(&s, "hello"); // String("hello")
|
|
CHECK(titan_string_length(&s) == 5, "length==5");
|
|
CHECK(sub_1f7160(&s) == 1, "isSSO (len<8)");
|
|
CHECK(std::strcmp(fcn_001f712c(&s), "hello") == 0, "data()==\"hello\"");
|
|
CHECK(std::strcmp(fcn_001f7044(&s), "hello") == 0, "c_str()==\"hello\"");
|
|
CHECK(fcn_0020010c(&s, "he") == true, "startsWith(\"he\")");
|
|
CHECK(fcn_0020010c(&s, "xy") == false, "!startsWith(\"xy\")");
|
|
CHECK(fcn_00200064(&s, "ll", 2, 2) == true, "matchAt(2,\"ll\")");
|
|
fcn_001fe9b8(&s); // ~String (no heap to free)
|
|
}
|
|
|
|
static void test_string_heap() {
|
|
std::printf("[String] long string (heap path)\n");
|
|
struct_unknown_1 s;
|
|
const char* L = "this_is_a_long_string_over_8_chars";
|
|
fcn_001feb70(&s, L);
|
|
CHECK(titan_string_length(&s) == (int)std::strlen(L), "length matches");
|
|
CHECK(sub_1f7160(&s) == 0, "!isSSO (len>=8 -> heap)");
|
|
CHECK(std::strcmp(fcn_001f712c(&s), L) == 0, "heap data() matches");
|
|
CHECK(fcn_0020010c(&s, "this_is") == true, "startsWith prefix");
|
|
|
|
struct_unknown_1 c; // copy-ctor
|
|
sub_1fe43c(&c);
|
|
fcn_001feb9c(&c, &s);
|
|
CHECK(std::strcmp(fcn_001f712c(&c), L) == 0, "copy has same content");
|
|
CHECK(fcn_002000d8(&s, &c) == true, "startsWith(String copy)");
|
|
fcn_001fe9b8(&c);
|
|
fcn_001fe9b8(&s); // frees heap (no crash / leak-clean)
|
|
std::printf(" (heap alloc/free exercised)\n");
|
|
}
|
|
|
|
static void test_filehandle_stdio() {
|
|
std::printf("[FileHandle] stdio backend (read/seek/getLength/getc/close)\n");
|
|
char tmpl[] = "/tmp/titan_fh_XXXXXX";
|
|
int fd = mkstemp(tmpl);
|
|
CHECK(fd >= 0, "temp file created");
|
|
const char* payload = "Hello\nWorld"; // 11 bytes
|
|
FILE* w = fdopen(fd, "wb"); std::fwrite(payload, 1, 11, w); std::fclose(w);
|
|
|
|
FileHandle fh;
|
|
fcn_0022af94(&fh, tmpl, "rb"); // open (falls to fopen: real file)
|
|
CHECK(fcn_002293dc(&fh) == true, "isOpen after open");
|
|
CHECK(fh.fp != nullptr && fh.asset == nullptr, "stdio backend selected");
|
|
CHECK(fcn_00229410(&fh) == 11, "getLength==11");
|
|
|
|
char buf[6] = {0};
|
|
int n = fcn_00229484(&fh, buf, 1, 5); // read 5 bytes
|
|
CHECK(n == 5 && std::memcmp(buf, "Hello", 5) == 0, "read 5 == \"Hello\"");
|
|
|
|
fcn_0022b2f4(&fh, 6, SEEK_SET); // seek past the '\n'
|
|
CHECK(fcn_0022b18c(&fh) == 'W', "getc after seek == 'W'");
|
|
|
|
fcn_002294e0(&fh); // closeImpl
|
|
CHECK(fh.fp == nullptr, "fp nulled after close");
|
|
std::remove(tmpl);
|
|
}
|
|
|
|
static void test_filehandle_missing() {
|
|
std::printf("[FileHandle] missing file (no backend, isOpen false)\n");
|
|
FileHandle fh;
|
|
fcn_0022af94(&fh, "/no/such/titan/path.dat", "rb"); // fopen fails; no asset mgr -> nothing
|
|
CHECK(fcn_002293dc(&fh) == false, "isOpen false for missing file");
|
|
fcn_0022b204(&fh); // close+reset is safe on empty
|
|
}
|
|
|
|
int main() {
|
|
std::printf("== reconstructed libg behavioral tests ==\n");
|
|
test_string_sso();
|
|
test_string_heap();
|
|
test_filehandle_stdio();
|
|
test_filehandle_missing();
|
|
std::printf("\n== %d passed, %d failed ==\n", g_pass, g_fail);
|
|
return g_fail == 0 ? 0 : 1;
|
|
}
|