# SMW Central BPS patching library

[![Latest Stable Version](https://img.shields.io/npm/v/@smwcentral/bps)](https://www.npmjs.com/package/@smwcentral/bps)
[![License](https://img.shields.io/npm/l/@smwcentral/bps)](https://github.com/telinc1/smwcentral-bps/blob/master/LICENSE)

The JavaScript BPS patching library used by [SMW Central](https://www.smwcentral.net), most notably for its [Online
Tools](https://github.com/SMWCentral/OnlineTools) section.

## Installation

### NodeJS

Install from NPM using your favorite package manager:

```shell
$ npm install @smwcentral/bps
$ pnpm add @smwcentral/bps
$ yarn add @smwcentral/bps
```

You can then import the library as an ES module:

```js
import * as bps from "@smwcentral/bps";
```

TypeScript declarations are built in.

### Browser (CDN)

You can import the module directly from a CDN:

```html
<script type="module">
    import * as bps from "https://cdn.jsdelivr.net/npm/@smwcentral/bps@1.0.1/dist/index.js";
</script>
```

For convenience, use an import map:

```html
<script type="importmap">
    {
        "imports": {
            "@smwcentral/bps": "https://cdn.jsdelivr.net/npm/@smwcentral/bps@1.0.1/dist/index.js"
        }
    }
</script>

<script type="module">
    import * as bps from "@smwcentral/bps";
</script>
```

## Usage

### Apply BPS patch

The `applyBPS` function directly applies a BPS patch, with the base file and patch represented as a `Uint8Array` or
equivalent.

```js
import {readFileSync, writeFileSync} from "node:fs";
import {applyBPS} from "@smwcentral/bps";

const base = readFileSync("base.bin");
const patch = readFileSync("patch.bps");

const result = applyBPS(base, patch);

writeFileSync("result.bin", result);
```

If patching fails, `PatchError` will be thrown:

-   `MalformedPatchError`: The patch is not a BPS patch
-   `WrongInputSizeError`: The input file has a wrong size
-   `WrongInputChecksumError`: The input file has a wrong checksum

### Apply BPS patch adaptively

The `adaptiveApplyBPS` function applies a BPS patch and attempts to make adaptations to the input file. The optional 3rd
arguments allows you to tweak which adaptations are enabled (by default, everything is enabled).

```js
import {readFileSync, writeFileSync} from "node:fs";
import {adaptiveApplyBPS} from "@smwcentral/bps";

const base = readFileSync("base.bin");
const patch = readFileSync("patch.bps");

const result = adaptiveApplyBPS(base, patch, {
    trySMC: true,
    trySMB2: true,
});

writeFileSync("result.bin", result);
```

The following adaptations are currently supported:

-   `trySMC`: If enabled, a patch made for a headerless SNES ROM (.sfc) can be applied to a headered input ROM (.smc).
    In this case, the result will be a headered ROM (.smc). Note that patches generated by
    [Flips](https://git.disroot.org/Sir_Walrus/Flips) (such as most BPS patches on SMW Central) are typically based on a
    headerless ROM, even if the original base ROM was headered.
-   `trySMB2`: If enabled, patches made for Super Mario Bros. 2 can be applied to any commonly used ROM (Rev 0, Rev 0 in
    NES 2.0 format, and Rev A), no matter the combination of patch and input ROM.

## Tests

Complete tests are available in the [`tests`](./tests) directory. Run with:

```
$ npm test
```

Some tests require commercial ROM files to be available in order to run. These tests will be skipped by default. In
order to run the full test suite, you need to supply the following the ROM files, which are presumably personal backups
of the copies of the respective games that you already own:

```
47ba60fad332fdea5ae44b7979fe1ee78de1d316ee027fea2ad5fe3c0d86f25a  Super Mario Bros. 2 (U) (PRG0) [!].nes
41300bc4942a8a4f9b53148b404dd5cae3dd708ebdd9b617888d290a51a83e43  Super Mario Bros. 2 (USA).nes
6ca47e9da206914730895e45fef4f7393e59772c1c80e9b9befc1a01d7ecf724  Super Mario Bros. 2 (USA) (Rev A).nes
0838e531fe22c077528febe14cb3ff7c492f1f5fa8de354192bdff7137c27f5b  Super Mario World (U) [!].sfc
d70c9c7716ad12c674fc7dd744736aa48d4d7b4237f58066be620fda26024872  Super Mario World (U) [!].smc
```

Place any ROMs you have in the [`tests/roms`](./tests/roms) directory with exactly the filenames listed above. The test
suite will automatically detect the ROMs, validate their checksum, and enable any tests that use them.

## License

Released under the [MIT License](https://github.com/telinc1/smwcentral-bps/blob/main/LICENSE).

## Credits

Built and maintained by [Telinc1](https://github.com/telinc1). [Original JavaScript BPS
patcher](https://media.smwcentral.net/Alcaro/bps/) by [Sir_Walrus](https://smwc.me/u/1686). SMC support by
[trillian](https://smwc.me/u/32552). Super Mario Bros. 2 support by [CircleFriendo](https://smwc.me/u/49634).
