/* dbdata_probe.c -- call FIFA 17's dbdata.dll!getTableData and dump what it returns.
 *
 * FINDING (2026-08-04): dbdata.dll is NOT a player database. Its single export
 * `getTableData` is an ANTI-TAMPER ATTESTATION function whose name is a decoy.
 * Signature resolved empirically (crash-matrix over the 4 Win64 register args):
 *
 *     const char * __cdecl getTableData(int *pOutBase64Len);
 *
 * It returns a heap-allocated, NUL-terminated base64url string:
 *   dbdata.dll    -> 1012 chars (759 bytes decoded)   md5 59b46dce231e419f4c1effbd8024e5ae
 *   dbdataEA.dll  -> 1004 chars (753 bytes decoded)   md5 8e10d1c4ce5ca54974aad42a10f05de0
 * Deterministic across calls and across processes. There is no table selector
 * argument; args 2..4 are ignored.
 *
 * What it actually does (Wine +relay trace):
 *   CommandLineToArgvW(GetCommandLineW())
 *   for each argv: StrStrW(argv[i], L"/antitamperdiagnosis")
 *   GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, &getTableData)
 *   GetModuleFileNameA -> CreateFileW(own .dll, GENERIC_READ)
 *   ReadFile(0x28f000)              <- whole file into a heap buffer
 *   VirtualAlloc(1MB, PAGE_EXECUTE_READWRITE)   <- unpack scratch, freed before return
 *   SetFilePointerEx(0x28e200); ReadFile(0x200) <- Authenticode Security Directory
 *   -> 759-byte attestation blob, base64url encoded
 *
 * Flip one byte inside the .xdata payload and it raises 0xC0000096
 * (PRIVILEGED_INSTRUCTION) at image+0x3C0DF instead of returning.
 *
 * Build + run:
 *   x86_64-w64-mingw32-gcc -O0 -o dbdata_probe.exe dbdata_probe.c
 *   cp "/mnt/games/FIFA 17/dbdata.dll" .   # must sit next to the exe
 *   WINEDEBUG=-all wine dbdata_probe.exe dbdata.dll 1 out
 * Add WINEDEBUG=+relay to re-derive the call sequence above.
 */
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

typedef void *(*F4)(uint64_t, uint64_t, uint64_t, uint64_t);

static LONG CALLBACK veh(EXCEPTION_POINTERS *ep) {
    fprintf(stderr, "!! EXCEPTION 0x%08lx rip=%p addr=%p\n",
            (unsigned long)ep->ExceptionRecord->ExceptionCode,
            (void *)ep->ContextRecord->Rip, ep->ExceptionRecord->ExceptionAddress);
    fflush(stderr);
    ExitProcess(9);
    return EXCEPTION_CONTINUE_SEARCH;
}

int main(int argc, char **argv) {
    AddVectoredExceptionHandler(1, veh);
    const char *dllname = (argc > 1) ? argv[1] : "dbdata.dll";
    int iters = (argc > 2) ? atoi(argv[2]) : 1;
    const char *pfx = (argc > 3) ? argv[3] : "gtd";
    HMODULE h = LoadLibraryA(dllname);
    if (!h) { printf("LoadLibraryA(%s) failed err=%lu\n", dllname, GetLastError()); return 1; }
    F4 f = (F4)GetProcAddress(h, "getTableData");
    printf("base=%p getTableData=%p (rva 0x%llx)\n", (void *)h, (void *)f,
           (unsigned long long)((uintptr_t)f - (uintptr_t)h));
    if (!f) return 1;
    for (int i = 0; i < iters; i++) {
        volatile int len = -1;
        void *r = f((uint64_t)(uintptr_t)&len, 0, 0, 0);
        printf("call %d: ret=%p len=%d\n", i, r, len);
        if (!r || len <= 0) continue;
        char fn[256]; sprintf(fn, "%s_%d.bin", pfx, i);
        FILE *fp = fopen(fn, "wb");
        fwrite(r, 1, (size_t)len, fp);
        fclose(fp);
        printf("  wrote %s (%d bytes of base64url)\n", fn, len);
    }
    return 0;
}
