Skip to content

Apply IPS patches to a ROM on the way in, from either front end - #35

Closed
dimiro1 wants to merge 1 commit into
masterfrom
dimiro1/gitignore-dsstore
Closed

Apply IPS patches to a ROM on the way in, from either front end#35
dimiro1 wants to merge 1 commit into
masterfrom
dimiro1/gitignore-dsstore

Conversation

@dimiro1

@dimiro1 dimiro1 commented Aug 18, 2026

Copy link
Copy Markdown
Owner

--patch FILE on the command line and File > Open with Patch... in the window apply an IPS patch to a ROM's bytes as they are read, so the .nes on disk is left exactly as it was and no patched copy of it exists anywhere. The patcher is a module of its own, mynes-patch, depending on nothing at all including mynes-core — IPS is a binary diff that says nothing about what it patches — and it reads records, RLE runs, growth past the end of the image and Lunar IPS's truncation extension, refusing anything malformed with a sentence saying what is wrong. It runs before Cart.load, since a patch is entitled to rewrite the iNES header and change the mapper or the bank sizes, which is why cart.sha256 in the report is the digest of the patched image and cart.patches says how many records each patch held. In the window a patched game keeps its own save states, .sav and screenshots, named after the patch rather than the ROM so a hack cannot write over the original's save — which turned up a pre-existing bug in the method that had to change for it: loadRom moved romPath before startMachine's saveBattery(), so loading a game while another was running wrote the outgoing game's save RAM into the incoming game's .sav. Tests cover the format (IPSPatchTests), the flag end to end (HeadlessRunTests, OptionsTests) and a real romhack (RomHackTests, which patches a vendored public-domain hello-world cartridge with a checked-in .ips and compares the two pictures); .DS_Store is now ignored, and CLAUDE.md, the README and PROVENANCE are updated.

🤖 Generated with Claude Code

A romhack is handed out as a patch rather than as a ROM, and until now there was
nothing here to do with one. `--patch FILE` and **File > Open with Patch...** now
apply an IPS patch to the bytes as they are read, so the .nes on disk is left
exactly as it was and no patched copy of it exists anywhere.

The patcher is a module of its own. IPS is a binary diff from 1990 that says
nothing about what it patches -- a ROM, a save file, a disk image -- so
`mynes-patch` depends on nothing at all, `mynes-core` included, and it is the
front ends that join it to `Cart.load`. Records, RLE runs, growth past the end of
the image and Lunar IPS's truncation extension are all read; anything malformed
is refused with a sentence saying what is wrong with it.

The patch is applied before the cartridge is parsed, because a patch is entitled
to rewrite the iNES header and so to change the mapper or the size of the banks.
That makes the patched image the thing that ran, which is why `cart.sha256` in
the report is its digest rather than the file's, and why `cart.patches` lists
what each patch held -- zero records is what a patch cut against a different dump
of the same game looks like.

In the window, a patched game keeps its own save states, .sav and screenshots,
named after the patch rather than the ROM. Sharing them would let an afternoon
with a hack write over fifty hours of the original.

Which turned up a bug in the method that had to change for it: loadRom moved
romPath before startMachine, whose first act is saveBattery(), so loading a game
while another was running wrote the outgoing game's save RAM into the incoming
game's .sav. The paths move after the save now.

RomHackTests is the worked example, and the reason there is a public-domain
hello-world cartridge and a real .ips vendored beside it: the twelve ASCII bytes
of "Hello World!" become "MyNES Patch!", and the two pictures are compared.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@dimiro1 dimiro1 closed this Aug 18, 2026
@dimiro1
dimiro1 deleted the dimiro1/gitignore-dsstore branch August 18, 2026 21:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant