mirror of
https://github.com/marcrobledo/RomPatcher.js.git
synced 2025-06-27 16:25:54 +00:00
Created Embedding Rom Patcher JS (markdown)
parent
2326dfe417
commit
5ba1089794
1 changed files with 209 additions and 0 deletions
209
Embedding-Rom-Patcher-JS.md
Normal file
209
Embedding-Rom-Patcher-JS.md
Normal file
|
@ -0,0 +1,209 @@
|
||||||
|
# Embedding Rom Patcher JS in your site
|
||||||
|
If you are self-hosting your own translations and/or hack patches, you can embed Rom Patcher JS and provide a nice solution for your users, allowing them to easily apply your patches to their ROMs without requiring them to download a single file, all thanks to the magic of the web!
|
||||||
|
|
||||||
|
Take a look at [`index_template.html`](https://github.com/marcrobledo/RomPatcher.js/blob/master/index_template.html) for a template. Basically you need to:
|
||||||
|
|
||||||
|
1. [Download the latest version](https://github.com/marcrobledo/RomPatcher.js/releases) and copy the entire `rom-patcher-js/` folder to your site
|
||||||
|
2. Include **all** file imports from the `<header>` into yours:
|
||||||
|
```html
|
||||||
|
<link type="text/css" rel="stylesheet" href="./rom-patcher-js/style.css" media="all" />
|
||||||
|
<script type="text/javascript" src="./rom-patcher-js/modules/BinFile.js"></script>
|
||||||
|
...
|
||||||
|
<script type="text/javascript" src="./rom-patcher-js/RomPatcher.js"></script>
|
||||||
|
<script type="text/javascript" src="./rom-patcher-js/RomPatcher.webapp.js"></script>
|
||||||
|
```
|
||||||
|
3. Copy the needed HTML structure from `index_template.html`:
|
||||||
|
```html
|
||||||
|
<div id="rom-patcher-container">
|
||||||
|
...
|
||||||
|
</div>
|
||||||
|
```
|
||||||
|
4. Build a quick patcher with a simple call to `RomPatcherWeb.initialize`:
|
||||||
|
```html
|
||||||
|
<script type="text/javascript">
|
||||||
|
window.addEventListener('load', function(){
|
||||||
|
const myPatcherSettings={
|
||||||
|
language: 'en',
|
||||||
|
allowDropFiles: false /* if true, it adds drag and drop support,*/
|
||||||
|
};
|
||||||
|
RomPatcherWeb.initialize(myPatcherSettings, 'my_patch.ips');
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Providing additional patch information
|
||||||
|
If you want to improve the user experience, you can provide additional information of your patches.<br/>
|
||||||
|
Instead of a simple file path, you can build a JSON object like this:
|
||||||
|
```js
|
||||||
|
RomPatcherWeb.initialize(myPatcherSettings, {
|
||||||
|
file: 'my_hack.ips',
|
||||||
|
name: 'My game improvement (v2.0)', //patch name that will appear in the dropdown button
|
||||||
|
inputCrc32: 0xdeadbeef, //add checksum validation to patch, will warn user if the ROM is not valid
|
||||||
|
description: 'This patch translates the game and fixes bugs!', //short description that will appear when user picks this patch
|
||||||
|
outputName: 'My game (Hack)' //patched ROM name
|
||||||
|
});
|
||||||
|
```
|
||||||
|
It is recommended, though, to store the patch or patches in a single zip file:
|
||||||
|
```js
|
||||||
|
RomPatcherWeb.initialize(myPatcherSettings, {
|
||||||
|
file: 'my_patches.zip', //zip containing patches
|
||||||
|
patches: [ //information about patches inside the zip
|
||||||
|
{
|
||||||
|
file: 'my_hack.ips', //required, must match the patch file name in the zip
|
||||||
|
name: 'My game improvement hack (v2.0)',
|
||||||
|
inputCrc32: [0xdeadbeef, 0xd0d0cafe], //an array of valid checksums
|
||||||
|
description: 'This patch translates the game and fixes bugs! (Recommended)',
|
||||||
|
outputName: 'My game improvement'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
file: 'my_hack_vanilla.ips',
|
||||||
|
name: 'My game improvement hack (v2.0, no bugfixes)',
|
||||||
|
inputCrc32: [0xdeadbeef, 0xd0d0cafe],
|
||||||
|
description: 'This patch translates the game but keeps the original game\'s bugs untouched for a vanilla experience',
|
||||||
|
outputName: 'My game improvement'
|
||||||
|
},
|
||||||
|
/* ... */
|
||||||
|
]
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Events
|
||||||
|
If you want to go further, you can run your own code via events:
|
||||||
|
```js
|
||||||
|
const myPatcherSettings={
|
||||||
|
language: 'en',
|
||||||
|
|
||||||
|
onloadrom: function (romFile) {
|
||||||
|
/* this event is triggered when user provides a ROM and before validating it */
|
||||||
|
/* can be used to: */
|
||||||
|
/* - modify it before validation (e.g. add/remove/fix header, change endianness...) */
|
||||||
|
/* - to switch to another patch file in the dropdown selector */
|
||||||
|
},
|
||||||
|
onvalidaterom: function (romFile, isRomValid) {
|
||||||
|
/* this event is triggered after ROM is validated */
|
||||||
|
/* can be used to show a custom error message if provided ROM is not valid */
|
||||||
|
},
|
||||||
|
onpatch: function (romFile) {
|
||||||
|
/* this event is triggered a ROM is patched */
|
||||||
|
/* can be used to modify it before the patched ROM file is saved */
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
In all events, the `romFile` parameter is an instance of [BinFile.js](https://github.com/marcrobledo/RomPatcher.js/blob/master/rom-patcher-js/modules/BinFile.js), which allows easy file data and name manipulation.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### Example: Fix ROM endianness
|
||||||
|
N64 ROM files are usually stored in `.z64` (Big Endian) or `.n64` (Little Endian) formats. You can make your patcher compatible with both formats by changing the provided ROM file endianness before patching (and then restoring it after patching).<br/>
|
||||||
|
```js
|
||||||
|
const SSB_LITTE_ENDIAN_CRC32 = 0xf0b7c200;
|
||||||
|
var romIsLittleEndian;
|
||||||
|
|
||||||
|
RomPatcher.initialize(
|
||||||
|
{
|
||||||
|
onloadrom:function(romFile, patch){
|
||||||
|
/* if ROM is little endian */
|
||||||
|
if(romFile.getExtension()==='n64' && romFile.hashMD5()===SSB_LITTE_ENDIAN_CRC32){
|
||||||
|
/* change endianness to big */
|
||||||
|
romFile.swapBytes(4);
|
||||||
|
/* change extension */
|
||||||
|
romFile.setExtension('z64');
|
||||||
|
|
||||||
|
romIsLittleEndian=true;
|
||||||
|
}else{
|
||||||
|
romIsLittleEndian=false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onpatch:function(patchedRomFile){
|
||||||
|
/* if ROM was little endian */
|
||||||
|
if(romIsLittleEndian){
|
||||||
|
/* revert endianness to little */
|
||||||
|
romFile.swapBytes(4);
|
||||||
|
/* revert extension */
|
||||||
|
romFile.setExtension('n64');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
file: 'smashremix1.5.2.xdelta',
|
||||||
|
inputCrc32: 0xeb97929e,
|
||||||
|
name: 'Smash Remix v1.5.2',
|
||||||
|
outputName: 'Smash Remix v1.5.2 (Hack)'
|
||||||
|
}
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### Example: Choose correct patch automatically
|
||||||
|
If your zip file contains several patches for different ROMs, you can make Rom Patcher JS so it picks automatically the patch for the provided ROM.
|
||||||
|
```js
|
||||||
|
const SML2_CHECKSUM_WORLD = 0xd5ec24e4;
|
||||||
|
const SML2_CHECKSUM_JAPAN = 0xa715daf5;
|
||||||
|
const SML2_CHECKSUMS_INVALID = [0xe6f886e5, 0x635a9112, 0xbf733e10, 0x29e0911a];
|
||||||
|
|
||||||
|
RomPatcher.initialize(
|
||||||
|
{
|
||||||
|
onloadrom: function (romFile) {
|
||||||
|
const crc32 = romFile.hashCRC32();
|
||||||
|
/* if ROM region is USA/Europe */
|
||||||
|
if (crc32 === SML2_CHECKSUM_WORLD)
|
||||||
|
RomPatcherWeb.pickEmbededFile('SML2DXv181.ips');
|
||||||
|
/* if ROM region is Japan */
|
||||||
|
else if (crc32 === SML2_CHECKSUM_JAPAN)
|
||||||
|
RomPatcherWeb.pickEmbededFile('SML2DXv181_jap.ips');
|
||||||
|
},
|
||||||
|
onvalidaterom: function (romFile, isRomValid) {
|
||||||
|
const crc32 = romFile.hashCRC32();
|
||||||
|
/* if ROM is a known SML2 revision that is not compatible with the patch*/
|
||||||
|
if (!isRomValid && SML2_CHECKSUMS_INVALID.indexOf(crc32) !== -1)
|
||||||
|
RomPatcherWeb.setErrorMessage('Please provide a v1.0 SML2 ROM');
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
file: 'SML2DXv181.zip',
|
||||||
|
patches: [
|
||||||
|
{
|
||||||
|
file: 'SML2DXv181.ips',
|
||||||
|
name: 'Super Mario Land 2 DX - 6 Golden Coins (v1.8.1)',
|
||||||
|
inputCrc32: SML2_CHECKSUM_WORLD,
|
||||||
|
description: 'SML2 colorization hack (USA/Europe) by toruzz',
|
||||||
|
outputName: 'Super Mario Land 2 DX - 6 Golden Coins (Hack)',
|
||||||
|
outputExtension: 'gbc'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
file: 'SML2DXv181_jap.ips',
|
||||||
|
name: 'Super Mario Land 2 DX - 6-tsu no Kinka (v1.8.1)',
|
||||||
|
inputCrc32: SML2_CHECKSUM_JAPAN,
|
||||||
|
description: 'SML2 colorization hack (Japan) by toruzz',
|
||||||
|
outputName: 'Super Mario Land 2 DX - 6-tsu no Kinka (Hack)',
|
||||||
|
outputExtension: 'gbc'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### Example: Remove header
|
||||||
|
SNES ROM files are usually stored in `.sfc` (headerless) or `.smc` (headered) formats. You can make your patcher compatible with both formats by detecting if the ROM is headered and remove the header before the ROM is validated.<br/>
|
||||||
|
```js
|
||||||
|
RomPatcher.initialize(
|
||||||
|
{
|
||||||
|
onloadrom: function (romFile) {
|
||||||
|
const SMC_HEADER_SIZE=512;
|
||||||
|
if(romFile.getExtension()==='smc' && ((romFile.fileSize - SMC_HEADER_SIZE) % 0x20000) === 0){
|
||||||
|
romFile.removeLeadingBytes(SMC_HEADER_SIZE);
|
||||||
|
romFile.setExtension('sfc');
|
||||||
|
console.log('removed SMC header');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
file: 'Tekkaman Blade v1.0.rup',
|
||||||
|
name: 'English translation by D and Near',
|
||||||
|
outputName: 'Uchuu no Kishi Tekkaman Blade (English v1.0)',
|
||||||
|
}
|
||||||
|
);
|
||||||
|
```
|
Loading…
Add table
Add a link
Reference in a new issue