Files
gen1recomp/tools/gbromdiff/README.md
T
2026-08-04 06:41:34 -04:00

3.2 KiB

gbromdiff

Compare two Game Boy ROMs bank by bank, and say where and how they disagree.

Written for one specific problem: bringing a localised Gen 1 Pokémon release into a disassembly. It is not specific to Pokémon, or to Gen 1 — any pair of Game Boy ROMs will do.

Why

The European Pokémon releases are rebuilds of their US counterparts. Most banks are byte-identical, the text banks are entirely different, and a handful of code banks are the US code with pointers shifted. Which bank is which is the first thing you need to know and the last thing anyone writes down.

That distinction is the whole point of this tool:

  bank   differing bytes            shape
  0x04       30 (  0.2%)   patched
  0x10    16328 ( 99.7%)   replaced

A bank differing in 99.7% of its bytes is a different payload — translated text — and needs new source. A bank differing in 0.2% is the same code with values moved, and usually needs a pointer table corrected rather than a rewrite. Those two findings lead to completely different work, and a plain cmp cannot tell them apart.

The loop it is built for

1. build the disassembly
2. diff the build against the retail ROM   ← this tool
3. fix the banks that disagree
4. go to 1, until nothing disagrees

Exit status is 0 when the ROMs are identical and 1 when they differ, so it drops straight into that loop:

make && ./gbromdiff.py build.gb retail.gb || echo "not there yet"

Usage

./gbromdiff.py A.gb B.gb                        # bank-by-bank summary
./gbromdiff.py A.gb B.gb --regions              # contiguous differing runs
./gbromdiff.py A.gb B.gb --sym pokeyellow.sym   # name the symbols involved
./gbromdiff.py A.gb B.gb --json                 # machine-readable

With an rgbds .sym file it names the symbol each differing region falls inside, which turns

0x0703A1 differs

into

0x0703A1-0x0703B4  bank 0x1C  20 bytes  TextPredef+0x3A1

--gap N controls how many matching bytes are tolerated inside one region (default 16). Without it, a shifted pointer table reports as hundreds of one-byte findings instead of one region worth looking at.

No dependencies beyond Python 3.

What this does not do

It does not build anything, and it does not write a disassembly. It tells you where two ROMs differ. Turning that into a source tree that rebuilds a localised ROM byte-for-byte is the actual project; this is the instrument you point at it between iterations.

Context

The Spanish Red and Blue releases are supported in gen1recomp by way of einstein95/pokered-es, a shift-matching disassembly whose .sym files provide Spanish addresses for every symbol.

Spanish Yellow has no equivalent, so it cannot be supported the same way. French (Narishma-gb/pokeyellow-fr) and German (Brianum/pokeyellow-de) Yellow disassemblies both exist, so adapting pret's Yellow to a European release is demonstrably possible — it simply has not been done for Spanish. This tool exists to make that attempt less tedious.

MIT licensed. Contributions welcome, particularly from anyone actually attempting pokeyellow-es.