Files
goldeneye-pc-port/tools_pc/romverify.c
T
James Dansereau bd56cff073 PC port: phase 1.5 — ROM dump verification groundwork
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.
2026-08-21 20:29:03 -04:00

78 lines
2.7 KiB
C

// 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 <file.z64>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "n64cksum.h"
#include "utils.h"
int main(int argc, char **argv)
{
if (argc != 2) {
fprintf(stderr, "usage: %s <file.z64>\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;
}