From bd56cff07370f62fe3c6b50e098eeb78146e8265 Mon Sep 17 00:00:00 2001 From: James Dansereau <106631954+jkdansereau@users.noreply.github.com> Date: Fri, 21 Aug 2026 20:29:03 -0400 Subject: [PATCH] =?UTF-8?q?PC=20port:=20phase=201.5=20=E2=80=94=20ROM=20du?= =?UTF-8?q?mp=20verification=20groundwork?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No-Intro 'GoldenEye 007 (U) [!].z64' placed in data/ and verified byte-identical (MD5 70c52588...) to the dev ROM copies (data/ge007.ntsc-final.z64, baserom.u.z64). Header magic/title/country check out; embedded CRCs DCBC50D1/09FD1AA3 re-computed and matched via tools/n64cksum.c's CIC-6102 routine. Adds tools_pc/romverify.c: one-shot integrity check (size, GE header magic at 0x00, title, country byte, CRC1/CRC2) so any future ROM swap is detectable in seconds. Fingerprint + provenance recorded in docs/HANDOFF.md; gitignore the built romverify.exe. --- .gitignore | 1 + docs/HANDOFF.md | 6 ++++ tools_pc/romverify.c | 77 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+) create mode 100644 tools_pc/romverify.c diff --git a/.gitignore b/.gitignore index 29041e2a..a42e656e 100644 --- a/.gitignore +++ b/.gitignore @@ -70,6 +70,7 @@ notes/* # PC port (see CMakeLists.txt / port/) build-pc/ +tools_pc/*.exe data/ ge007.ini !build/test.c diff --git a/docs/HANDOFF.md b/docs/HANDOFF.md index 5d5bbaba..8898037d 100644 --- a/docs/HANDOFF.md +++ b/docs/HANDOFF.md @@ -96,6 +96,12 @@ fixup loop's pointer stride on x86-64**; rebuild; confirm boot advances. Log eac `export PATH="/c/msys64/mingw64/bin:$PATH"`. - Build: `./build-pc.sh ntsc-final` (incremental; full rebuild only when `port/shim/` changes). ROM is at `data/ge007.ntsc-final.z64` (= `baserom.u.z64`, byte-identical). +- ROM provenance (verified 2026-08): all three copies are byte-identical to the No-Intro + good dump `GoldenEye 007 (U) [!].z64` (kept in `data/`). MD5 + `70c525880240c1e838b8b1be35666c3b`, SHA-256 + `2cdcec8a9f0cb6e36337f3ee39d8ad105dc8afa6ba1c02d466e8f5b771f9a162`, 0xC00000 bytes, + country 'E' (US). Header CRCs DCBC50D1/09FD1AA3 re-verify with + `tools_pc/romverify.exe` (build line in its header comment). - gdb: **launch** mode only (`gdb -batch -ex "handle SIGSEGV stop" -ex run …`). Symbolicate offline with `addr2line -e build-pc/ge007.x86_64.exe -f -C <0x140000000+rel>`. Image base is `0x140000000` (re-verify with `info address` after a rebuild). diff --git a/tools_pc/romverify.c b/tools_pc/romverify.c new file mode 100644 index 00000000..da3a6479 --- /dev/null +++ b/tools_pc/romverify.c @@ -0,0 +1,77 @@ +// romverify.c — one-shot ROM integrity check for the PC port. +// +// Verifies a .z64 against the repo's ground truths without modifying it: +// 1. size (must be >= 0x100000, the n64cksum coverage window) +// 2. GE header magic 80 37 12 40 at 0x00 (src/rom_header.s, port/src/romdata.c) +// 3. title "GOLDENEYE" at 0x20 +// 4. country byte at 0x3E (E=US/LANG_US, P=EU, J=JP) +// 5. embedded checksums at 0x10/0x14 vs n64cksum_calc_6102() over [0x1000, 0x100000) +// (tools/n64cksum.c — the exact routine the N64 build pipeline uses) +// +// Build (from tools_pc/): gcc -O2 -I../tools romverify.c ../tools/n64cksum.c +// ../tools/utils.c -o romverify.exe +// Usage: ./romverify.exe + +#include +#include +#include +#include + +#include "n64cksum.h" +#include "utils.h" + +int main(int argc, char **argv) +{ + if (argc != 2) { + fprintf(stderr, "usage: %s \n", argv[0]); + return 2; + } + + unsigned char *rom; + long len = read_file(argv[1], &rom); + if (len < 0) { + fprintf(stderr, "FAIL: cannot read %s\n", argv[1]); + return 2; + } + + int fail = 0; +#define CHECK(cond, ...) do { \ + printf(" [%s] ", (cond) ? "ok " : "FAIL"); \ + printf(__VA_ARGS__); \ + printf("\n"); \ + if (!(cond)) fail = 1; \ + } while (0) + + printf("ROM: %s (%ld bytes)\n", argv[1], len); + + CHECK(len >= 0x100000, "size >= 0x100000 (got 0x%lX)", (unsigned long)len); + if (fail) { free(rom); return 1; } + + const uint8_t *h = rom; + static const uint8_t magic[4] = { 0x80, 0x37, 0x12, 0x40 }; + CHECK(memcmp(h, magic, 4) == 0, + "magic at 0x00 = %02x%02x%02x%02x (want 80371240)", + h[0], h[1], h[2], h[3]); + + CHECK(memcmp(h + 0x20, "GOLDENEYE", 9) == 0, "title at 0x20 starts \"GOLDENEYE\""); + + char country = (char)h[0x3E]; + const char *cname = (country == 'E') ? "US (LANG_US)" : + (country == 'P') ? "EU (LANG_EU)" : + (country == 'J') ? "JP (LANG_JP)" : "?"; + printf(" [info] country byte at 0x3E = '%c' -> %s\n", country, cname); + + uint32_t stored1 = ((uint32_t)h[0x10] << 24) | ((uint32_t)h[0x11] << 16) | + ((uint32_t)h[0x12] << 8) | (uint32_t)h[0x13]; + uint32_t stored2 = ((uint32_t)h[0x14] << 24) | ((uint32_t)h[0x15] << 16) | + ((uint32_t)h[0x16] << 8) | (uint32_t)h[0x17]; + + unsigned int calc[2]; + n64cksum_calc_6102(rom, calc); + CHECK(calc[0] == stored1, "CRC1: header 0x%08X vs computed 0x%08X", stored1, calc[0]); + CHECK(calc[1] == stored2, "CRC2: header 0x%08X vs computed 0x%08X", stored2, calc[1]); + + free(rom); + printf(fail ? "RESULT: FAIL\n" : "RESULT: PASS\n"); + return fail; +}