RomPatcher.ts
Zero-dependency JS ROM patching library for browsers
Installable (PWA) Web Demo
RomPatcher.ts is a re-write of MarcroBledo's RomPatcher.js - providing modularity, typing & simplicity.
To install, run
npm install rompatcher-ts
As of now, only Browsers are supported. For Node support, check out node-rompatcher on Gitlab.
Supported Formats
- BPS
- UPS
- IPS
PR Contributions for more formats are welcome.
Usage
import Patcher from 'rompatcher-ts';
// 1. Initialize a patcher instance with a format.
const patcher = new Patcher([
new IPSPattern,
new BPSPattern,
new UPSPattern
]);
// 2. Load ROM file
patcher.setROMFile(romFile);
// 3. Load Patch file asynchronously (2 & 3 can be interchanged)
try {
await patcher.setPatchFile(patchFile)
} catch(e) {
// Cannot recognize type of patch file
}
// This returns the pattern that will be used - i.e. IPS
// Must be called after patch file is loaded.
const patternToUse: string = patcher.pattern;
// 4. Load files asynchronously
await patcher.loadFiles();
// 5. Parse files
patcher.parseFile();
// 6. Patch the ROM
const resultPreparedFile = patcher.patch();
// 7. The blob can be obtained by calling export() on the resulting PreparedPatchFile.
const resBlob: Blob = resultPreparedFile.export();
API & Helpers
Patcher.ts
Patcher -
constructor(patternsToLoad: IPatchPattern[])
Initialize a patcher instance with the patterns to be used. -
async setPatchFile(patchFile: File): Promise<void>
Asynchronously sets a patch file, which determines the type of the patch file supplied. If it is not recognized, it throws an Error. -
setROMFile(romFile: File)
Sets a ROM file to be patched -
async loadFiles()
Asynchronously loads the supplied ROM file & patch file into the stateful supplied pattern. Throws an error if both are not supplied. -
parseFile()
Parses the supplied ROM file & patch file with the supplied pattern. Throws an error if both are not supplied. -
patch(validateStrictly = false)
(Do not change validateStrictly)
Patches the ROM file with the patch file, returning aPreparedPatchFile
instance with the resulting file.
utils/crc.ts
Utils -
crc32Table()
Generates a CRC32 table. Caching recommended. -
crc32(preparedPatcherFile: PreparedPatchFile, headerSize: number | boolean, ignoreLastFourBytes: boolean)
Uses a CRC32 table to return a crc32 value for checksum purposes.
utils.ts
PreparedPatchFile -
static async create(file: File)
Asynchronously create an instance of aPreparedPatchFile
, using theload
function. -
static createEmpty(size: number)
Ccreate an instance of aPreparedPatchFile
, Create an empty PreparedPatchFile with the size of the provided integer. -
async load(file: File)
Asynchronously load the supplied file, which creates an internal Uint8Array byte array of the data. -
public copyTo(target: PreparedPatchFile, offsetSource: number, length?: number, offsetTarget: number = offsetSource)
Copy the current instance to the target. -
seek(offset: number)
Set internal offset to offset. -
skip(offset: number)
Skip internal offset by the value of offset. -
slice(offset: number, length?: number)
Create a copy of the current file, starting from the offset of the current file with the length of the provided number. Returns the new file. -
isEOF()
Returns if the internal offset counter is at the end of file. -
readString(len: number)
Reads the string of byte array. Adds to internal offset counter by len. -
readBytes(len: number)
Reads the bytes of the byte array. Adds to internal offset counter by len. -
writeBytes(len: number)
Reads the bytes of the byte array. Adds to internal offset counter by len. -
read/writeU*(forceLittleEndian?: boolean = false)
Reads or writes bits. forceLittleEndian options are for U16 and higher. Increases the internal offset counter by bytes read or written. -
export()
Returns aBlob
of the current instance.
Writing Patterns
All patterns extend IPatchPattern
, where you must implement these methods:
interface IPatchPattern {
// Magic string for patch formats - i.e. BPS - "BPS1"
MAGIC: string;
// Constructor. Load the rom file & the patch file here, using
// PreparedPatchFile.create(romFile | patchFile) here.
// Initialize class variables here too.
// Load the romFile and the patchFile as a class variable.
init(romFile: File, patchFile: File): void;
// Parse the patch file - current implementations use an array
// to go through the Uint8Array and digest it to array of records or actions.
parse(): void;
// Create an output file with createEmpty(), and using the array of
// records/actions created in parse(), write to the output file.
// This varies by format.
patch(strictValidation?: boolean): PreparedPatchFile;
// Uses header to check if it belongs to the pattern.
belongsTo(header: string): boolean;
// Name of the patch - i.e. ups - "ups"
name(): string;
}
Refer to implemented examples and MarcroBledo's RomPatcher.js.
Contributing
- Run prettier
- Respect ESLint rules
- Testing before creating a PR. Unfortunately, tests cannot be done due to licensing issues for Nintendo ROMs.
- For changing content inside
/docs/
folder (which is the demo app), runnpm run docs:build
before pushing.
Examples
View the source code for the web demo in the docs/
directory, or go here for the installable live demo.
License
This library is licensed under GPLv3.