Skip to content

Commit

Permalink
Feature: ROM patching documentation (#172)
Browse files Browse the repository at this point in the history
  • Loading branch information
emmercm authored Nov 19, 2022
1 parent 3357d2e commit 1eabc48
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 18 deletions.
41 changes: 23 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,12 @@ A video of an example use case:

With a large ROM collection it can be difficult to:

- Organize ROM files by console
- Consistently name ROM files
- Make sure ROMs have the right extension
- Archive ROMs individually in mass
- Filter out duplicate ROMs
- Filter out ROMs for languages you don't understand
- Know what ROMs are missing for each console
- 📂 Organize ROM files by console
- 🪄 Name ROM files consistently, including the right extension
- 🗜️ Archive ROMs individually in mass
- ✂️ Filter out duplicate ROMs, or ROMs in languages you don't understand
- 🩹 Patch ROMs automatically in mass
- 🔍 Know what ROMs are missing for each console

`igir` helps solve all of these problems!

Expand Down Expand Up @@ -54,16 +53,18 @@ The `igir --help` command shown below includes examples of how to use multiple c

`igir` runs these steps in the following order:

1. Scans the DAT input path for every file and parses them, if specified
1. Scans the DAT input path for every file and parses them, if provided
2. Scans each ROM input path for every file
1. Then detects headers in those files, if applicable (see [docs](docs/rom-headers.md))
3. ROMs are matched to the DATs
1. Then filtering and sorting options are applied (see [docs](docs/rom-filtering.md))
2. Then ROMs are written to the output directory, if specified (`copy`, `move`)
3. Then written ROMs are tested for accuracy, if specified (`test`)
4. Then input ROMs are deleted, if specified (`move`)
4. Unknown files are recycled from the output directory, if specified (`clean`)
5. An output report is written to the output directory, if specified (`report`)
- Then detects headers in those files, if applicable (see [docs](docs/rom-headers.md))
3. Scans each patch input path for every file (see [docs](docs/rom-patching.md))
4. ROMs are matched to the DATs, if provided
- Then ROMs are matched to any applicable patches, creating multiple versions from the same ROM
- Then filtering and sorting options are applied (see [docs](docs/rom-filtering.md))
- Then ROMs are written to the output directory, if specified (`copy`, `move`)
- Then written ROMs are tested for accuracy, if specified (`test`)
- Then input ROMs are deleted, if specified (`move`)
5. Unknown files are recycled from the output directory, if specified (`clean`)
6. An output report is written to the output directory, if specified (`report`)

## How do I run `igir`?

Expand Down Expand Up @@ -213,11 +214,15 @@ Each manager has its own pros, but many have the same drawbacks or limitations:
- Limited parent/clone, region, language, version, and ROM type filtering
- No ability to prioritize parent/clones when creating a 1G1R set

## Additional documentation

See the [docs](/docs) page for more in-depth information!

## Feature requests, bug reports, and contributing

[![Contributors](https://badgen.net/github/contributors/emmercm/igir?icon=github)](https://github.com/emmercm/igir/graphs/contributors)
[![Feature Requests](https://badgen.net/github/label-issues/emmercm/igir/enhancement/open?icon=github&label=Open%20Feature%20Requests)](https://github.com/emmercm/igir/issues?q=is%3Aopen+is%3Aissue+label%3Aenhancement)
[![Bugs](https://badgen.net/github/label-issues/emmercm/igir/bug/open?icon=github&label=Open%20Bugs)](https://github.com/emmercm/igir/issues?q=is%3Aopen+is%3Aissue+label%3Abug)
[![Feature Requests](https://badgen.net/github/label-issues/emmercm/igir/enhancement/open?icon=github&label=open%20feature%20requests)](https://github.com/emmercm/igir/issues?q=is%3Aopen+is%3Aissue+label%3Aenhancement)
[![Bugs](https://badgen.net/github/label-issues/emmercm/igir/bug/open?icon=github&label=open%20bugs)](https://github.com/emmercm/igir/issues?q=is%3Aopen+is%3Aissue+label%3Abug)

Feedback is a gift! Your feature requests and bug reports help improve the project for everyone. Feel free to submit an issue on GitHub using one of the templates.

Expand Down
65 changes: 65 additions & 0 deletions docs/rom-patching.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# ROM Patching

Patches contain a set of changes that can be applied to a file, turning that file into something different. Common examples for patching ROMs are: translating text to a different language but keeping game logic the same, and fan-made creations such as new levels for an existing game.

Games and their ROMs are protected under copyrights, so patches are used in order to not share copyrighted code online. A person needs the original ROM file plus a patch file in order to get the resulting patched ROM that will be played with an emulator.

## Patch types

There are many, _many_ patch types that ROM hackers use to distribute their changes on the internet ([xkcd "Standards"](https://xkcd.com/927/)). Typically, a patch will only be distributed in one format, so gamers are entirely at the mercy of the ROM hacker's choice.

Not all patch types are created equal. Here are some tables of some existing formats, whether `igir` supports them, and what they support.

**Common patch types:**

| Type | Supported | CRC32 in patch contents | Notes |
|----------------------|-------------------|-------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `.bps` ||| |
| `.ips` | ⚠️ IPS, not IPS32 || |
| `.ppf` | ✅ 2.0, 3.0 || |
| `.ups` ||| ⚠️ UPS patches read and write fies byte-by-byte, making them horribly slow and inefficient. The author, byuu, created `.ups` to replace `.ips`, but then created `.bps` as a replacement for `.ups`. |
| `.vcdiff`, `.xdelta` ||| |

**Uncommon patch types:**

| Type | Supported | CRC32 in patch contents | Notes |
|---------------------|-----------|-------------------------|---------------------------------------------------------------------------------------------------------------------------------------|
| `.aps` (GBA) ||| |
| `.aps` (N64) || ⚠️ only type 1 patches | |
| `.bdf` (BSDiff) ||| |
| `.bsp` ||| Binary Script Patching will probably never be supported, the implementation is [non-trivial](https://github.com/aaaaaa123456789/bsp). |
| `.dps` ||| |
| `.ebp` (EarthBound) ||| |
| `.ffp` ||| |
| `.gdiff` ||| |
| `.mod` (Star Rod) ||| |
| `.pat` (FireFlower) ||| |
| `.pds` ||| |
| `.rup` (NINJA 2.0) || ❌ uses MD5 | |
| `.rxl` ||| |

If you have a choice in patch format, choose one that does contain CRC32 checksums in the patch file contents, otherwise see any notes above.

## ROM checksums

`igir` needs to be able to know what source ROM each patch file applies to, and it does this using CRC32 checksums.

A few patch formats include the source ROM's CRC32 checksum in the patch's file contents. This is the most accurate and therefore the best way to get this information. `.bps` is a great example of an efficient and simple patch format that includes this information.

Most patch formats do not include the source ROM's CRC32 checksum. `.ips` patches are some of the most likely you will come across. For those patches, you need to put the source ROM's CRC32 checksum in the patch's filename, either at the beginning or end, like this:

```text
Source ROM filename:
Super Mario Land (World).gb
Patch filename:
Super Mario Land DX v2.0 (World) 90776841.ips
```

```text
Source ROM filename:
NBA Jam - Tournament Edition (USA) (Track 1).bin
Patch filename:
a8f1adf5 NBA Jam 22 v1.4.ppf
```
1 change: 1 addition & 0 deletions src/types/patches/bpsPatch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ enum BPSAction {

/**
* @link https://github.com/blakesmith/rombp/blob/master/docs/bps_spec.md
* @link https://github.com/btimofeev/UniPatcher/wiki/BPS
*/
export default class BPSPatch extends Patch {
static readonly SUPPORTED_EXTENSIONS = ['.bps'];
Expand Down
1 change: 1 addition & 0 deletions src/types/patches/upsPatch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import Patch from './patch.js';
* large patches can perform tremendously poorly if they contain many small records.
*
* @link https://www.romhacking.net/documents/392/
* @link https://github.com/btimofeev/UniPatcher/wiki/UPS
* @link https://www.gamebrew.org/wiki/Upset
*/
export default class UPSPatch extends Patch {
Expand Down

0 comments on commit 1eabc48

Please sign in to comment.