mirror of
https://github.com/marcrobledo/RomPatcher.js.git
synced 2025-07-17 16:38:31 +00:00
added BPS support (apply patch only)
This commit is contained in:
parent
69fcad33bb
commit
3b1b932346
5 changed files with 188 additions and 25 deletions
|
@ -1,4 +1,4 @@
|
||||||
/* ips-patcher.js v20170722 - Marc Robledo 2016-2017 - http://www.marcrobledo.com/license */
|
/* RomPatcher.js v20171102 - Marc Robledo 2016-2017 - http://www.marcrobledo.com/license */
|
||||||
var MAX_ROM_SIZE=33554432;
|
var MAX_ROM_SIZE=33554432;
|
||||||
var romFile, patch, romFile1, romFile2, tempFile, romHashes={};
|
var romFile, patch, romFile1, romFile2, tempFile, romHashes={};
|
||||||
/* Shortcuts */
|
/* Shortcuts */
|
||||||
|
@ -54,10 +54,12 @@ function _readPatchFile(){
|
||||||
patch=readUPSFile(tempFile);
|
patch=readUPSFile(tempFile);
|
||||||
}else if(tempFile.readString(0,5)===APS_MAGIC){
|
}else if(tempFile.readString(0,5)===APS_MAGIC){
|
||||||
patch=readAPSFile(tempFile);
|
patch=readAPSFile(tempFile);
|
||||||
|
}else if(tempFile.readString(0,4)===BPS_MAGIC){
|
||||||
|
patch=readBPSFile(tempFile);
|
||||||
}/*else if(tempFile.readString(0,4)===APSGBA_MAGIC){
|
}/*else if(tempFile.readString(0,4)===APSGBA_MAGIC){
|
||||||
patch=readAPSGBAFile(tempFile);
|
patch=readAPSGBAFile(tempFile);
|
||||||
}*/else {
|
}*/else {
|
||||||
MarcDialogs.alert('Invalid IPS/UPS/APS file');
|
MarcDialogs.alert('Invalid IPS/UPS/APS/BPS file');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function openPatchFile(f){tempFile=new MarcBinFile(f, _readPatchFile)}
|
function openPatchFile(f){tempFile=new MarcBinFile(f, _readPatchFile)}
|
||||||
|
@ -74,7 +76,7 @@ function applyPatchFile(p,r){
|
||||||
|
|
||||||
|
|
||||||
function createPatchFile(){
|
function createPatchFile(){
|
||||||
var MODES=['ips','ups','aps','apsn64'/*,'apsgba'*/];
|
var MODES=['ips','ups','aps','apsn64'/*,'apsgba','bps'*/];
|
||||||
var mode=0;
|
var mode=0;
|
||||||
for(var i=0; i<MODES.length && !mode; i++)
|
for(var i=0; i<MODES.length && !mode; i++)
|
||||||
if(el('radio-'+MODES[i]).checked)
|
if(el('radio-'+MODES[i]).checked)
|
||||||
|
@ -100,6 +102,8 @@ function createPatchFile(){
|
||||||
newPatch=createAPSFromFiles(romFile1, romFile2, true);
|
newPatch=createAPSFromFiles(romFile1, romFile2, true);
|
||||||
}else if(mode==='apsgba'){
|
}else if(mode==='apsgba'){
|
||||||
newPatch=createAPSGBAFromFiles(romFile1, romFile2);
|
newPatch=createAPSGBAFromFiles(romFile1, romFile2);
|
||||||
|
}else if(mode==='bps'){
|
||||||
|
newPatch=createBPSFromFiles(romFile1, romFile2);
|
||||||
}
|
}
|
||||||
newPatch.export().save();
|
newPatch.export().save();
|
||||||
}
|
}
|
||||||
|
|
172
bps.js
Normal file
172
bps.js
Normal file
|
@ -0,0 +1,172 @@
|
||||||
|
/* BPS module for RomPatcher.js v20171103 - Marc Robledo 2016-2017 - http://www.marcrobledo.com/license */
|
||||||
|
/* File format specification: https://www.romhacking.net/documents/746/ */
|
||||||
|
|
||||||
|
var BPS_MAGIC='BPS1';
|
||||||
|
var BPS_ACTION_SOURCE_READ=0;
|
||||||
|
var BPS_ACTION_TARGET_READ=1;
|
||||||
|
var BPS_ACTION_SOURCE_COPY=2;
|
||||||
|
var BPS_ACTION_TARGET_COPY=3;
|
||||||
|
|
||||||
|
|
||||||
|
function BPS(){
|
||||||
|
this.sourceSize=0;
|
||||||
|
this.targetSize=0;
|
||||||
|
this.metaData='';
|
||||||
|
this.actionsOffset=0;
|
||||||
|
this.file=null;
|
||||||
|
this.sourceChecksum=0;
|
||||||
|
this.targetChecksum=0;
|
||||||
|
this.patchChecksum=0;
|
||||||
|
}
|
||||||
|
BPS.prototype.toString=function(){
|
||||||
|
var s='Source size: '+this.sourceSize;
|
||||||
|
s+='\Target size: '+this.targetSize;
|
||||||
|
s+='\nMetadata: '+this.metaData;
|
||||||
|
s+='\nActions offset: '+this.actionsOffset;
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
/*BPS.prototype.export=function(){
|
||||||
|
|
||||||
|
}*/
|
||||||
|
BPS.prototype.apply=function(romFile){
|
||||||
|
if(this.sourceChecksum!==crc32(romFile,false)){
|
||||||
|
MarcDialogs.alert('Warning: invalid source ROM checksum');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// first we determine target file size
|
||||||
|
var newFileSize=0;
|
||||||
|
var seek=this.actionsOffset;
|
||||||
|
while(seek<(this.file.fileSize-12)){
|
||||||
|
var data=decodeBPS(this.file, seek);
|
||||||
|
var action={type: data.number & 3, length: (data.number >> 2)+1};
|
||||||
|
seek+=data.length;
|
||||||
|
|
||||||
|
newFileSize+=action.length;
|
||||||
|
if(action.type===BPS_ACTION_TARGET_READ){
|
||||||
|
seek+=action.length;
|
||||||
|
}else if(action.type===BPS_ACTION_SOURCE_COPY || action.type===BPS_ACTION_TARGET_COPY){
|
||||||
|
seek+=decodeBPS(this.file, seek).length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tempFile=new MarcBinFile(newFileSize);
|
||||||
|
|
||||||
|
|
||||||
|
//patch
|
||||||
|
var outputOffset=0;
|
||||||
|
var sourceRelativeOffset=0;
|
||||||
|
var targetRelativeOffset=0;
|
||||||
|
seek=this.actionsOffset;
|
||||||
|
while(seek<(this.file.fileSize-12)){
|
||||||
|
var data=decodeBPS(this.file, seek);
|
||||||
|
var action={type: data.number & 3, length: (data.number >> 2)+1};
|
||||||
|
//console.log('0x'+seek.toString(16)+' - action: '+action.type+':'+action.length);
|
||||||
|
seek+=data.length;
|
||||||
|
|
||||||
|
if(action.type===BPS_ACTION_SOURCE_READ){
|
||||||
|
tempFile.writeBytes(outputOffset, romFile.readBytes(outputOffset, action.length));
|
||||||
|
outputOffset+=action.length;
|
||||||
|
|
||||||
|
}else if(action.type===BPS_ACTION_TARGET_READ){
|
||||||
|
tempFile.writeBytes(outputOffset, this.file.readBytes(seek, action.length));
|
||||||
|
outputOffset+=action.length;
|
||||||
|
seek+=action.length;
|
||||||
|
|
||||||
|
}else if(action.type===BPS_ACTION_SOURCE_COPY){
|
||||||
|
var data2=decodeBPS(this.file, seek);
|
||||||
|
seek+=data2.length;
|
||||||
|
sourceRelativeOffset+=(data2.number & 1 ? -1 : +1) * (data2.number >> 1);
|
||||||
|
while(action.length--){
|
||||||
|
tempFile.writeByte(outputOffset, romFile.readByte(sourceRelativeOffset));
|
||||||
|
outputOffset++;
|
||||||
|
sourceRelativeOffset++;
|
||||||
|
}
|
||||||
|
}else if(action.type===BPS_ACTION_TARGET_COPY){
|
||||||
|
var data2=decodeBPS(this.file, seek);
|
||||||
|
seek+=data2.length;
|
||||||
|
targetRelativeOffset += (data2.number & 1 ? -1 : +1) * (data2.number >> 1);
|
||||||
|
while(action.length--) {
|
||||||
|
tempFile.writeByte(outputOffset, tempFile.readByte(targetRelativeOffset));
|
||||||
|
outputOffset++;
|
||||||
|
targetRelativeOffset++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this.targetChecksum!==crc32(tempFile,false)){
|
||||||
|
MarcDialogs.alert('Warning: invalid target ROM checksum');
|
||||||
|
}
|
||||||
|
|
||||||
|
return tempFile
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function readBPSFile(file){
|
||||||
|
file.littleEndian=true;
|
||||||
|
var patchFile=new BPS();
|
||||||
|
|
||||||
|
var seek=4; //skip BPS1
|
||||||
|
var decodedSourceSize=decodeBPS(file, seek);
|
||||||
|
patchFile.sourceSize=decodedSourceSize.number;
|
||||||
|
seek+=decodedSourceSize.length;
|
||||||
|
var decodedTargetSize=decodeBPS(file, seek);
|
||||||
|
patchFile.targetSize=decodedTargetSize.number;
|
||||||
|
seek+=decodedTargetSize.length;
|
||||||
|
|
||||||
|
var decodedMetaDataLength=decodeBPS(file, seek);
|
||||||
|
seek+=decodedMetaDataLength.length;
|
||||||
|
patchFile.metaData=file.readString(seek, decodedMetaDataLength.length);
|
||||||
|
seek+=patchFile.metaData.length;
|
||||||
|
|
||||||
|
patchFile.actionsOffset=seek;
|
||||||
|
patchFile.file=file;
|
||||||
|
|
||||||
|
patchFile.sourceChecksum=file.readInt(file.fileSize-12);
|
||||||
|
patchFile.targetChecksum=file.readInt(file.fileSize-8);
|
||||||
|
patchFile.patchChecksum=file.readInt(file.fileSize-4);
|
||||||
|
|
||||||
|
if(patchFile.patchChecksum!==crc32(file,true)){
|
||||||
|
MarcDialogs.alert('Warning: invalid patch checksum');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return patchFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*function createBPSFromFiles(original, modified){
|
||||||
|
|
||||||
|
}*/
|
||||||
|
|
||||||
|
|
||||||
|
/*function encodeBPS(number){
|
||||||
|
number=number>>>0;
|
||||||
|
var dataBytes=[];
|
||||||
|
while(true){
|
||||||
|
var x = number & 0x7f;
|
||||||
|
number >>= 7;
|
||||||
|
if(number == 0){
|
||||||
|
dataBytes.push(0x80 | x);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
dataBytes.push(x);
|
||||||
|
number--;
|
||||||
|
}
|
||||||
|
return dataBytes;
|
||||||
|
}*/
|
||||||
|
function decodeBPS(dataBytes, i){
|
||||||
|
var number = 0, shift = 1;
|
||||||
|
var len=0;
|
||||||
|
while(true){
|
||||||
|
var x = dataBytes.readByte(i);
|
||||||
|
i++;
|
||||||
|
len++;
|
||||||
|
number += (x & 0x7f) * shift;
|
||||||
|
if(x & 0x80)
|
||||||
|
break;
|
||||||
|
shift <<= 7;
|
||||||
|
number += shift;
|
||||||
|
}
|
||||||
|
return {number:number,length:len};
|
||||||
|
}
|
11
index.html
11
index.html
|
@ -12,6 +12,8 @@
|
||||||
<script type="text/javascript" src="./ips.js"></script>
|
<script type="text/javascript" src="./ips.js"></script>
|
||||||
<script type="text/javascript" src="./ups.js"></script>
|
<script type="text/javascript" src="./ups.js"></script>
|
||||||
<script type="text/javascript" src="./aps.js"></script>
|
<script type="text/javascript" src="./aps.js"></script>
|
||||||
|
<!-- <script type="text/javascript" src="./apsgba.js"></script> -->
|
||||||
|
<script type="text/javascript" src="./bps.js"></script>
|
||||||
<script type="text/javascript" src="./ByteFlipper.js"></script>
|
<script type="text/javascript" src="./ByteFlipper.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
@ -35,7 +37,7 @@
|
||||||
<!-- APP -->
|
<!-- APP -->
|
||||||
<div class="wrapper" id="the-app">
|
<div class="wrapper" id="the-app">
|
||||||
<h3 class="red">Apply patch</h3>
|
<h3 class="red">Apply patch</h3>
|
||||||
<div class="container-description">Apply an IPS/UPS patch to your ROM</div>
|
<div class="container-description">Apply a patch to your ROM</div>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="six columns text-right"><label for="input-file-rom">ROM file:</label></div>
|
<div class="six columns text-right"><label for="input-file-rom">ROM file:</label></div>
|
||||||
|
@ -46,7 +48,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="six columns text-right"><label for="input-file-patch">Patch file (IPS/UPS/APS):</label></div>
|
<div class="six columns text-right"><label for="input-file-patch">Patch file <small>(IPS/UPS/APS/BPS)</small>:</label></div>
|
||||||
<div class="six columns">
|
<div class="six columns">
|
||||||
<input type="file" id="input-file-patch" />
|
<input type="file" id="input-file-patch" />
|
||||||
</div>
|
</div>
|
||||||
|
@ -60,7 +62,7 @@
|
||||||
|
|
||||||
|
|
||||||
<h3 class="blue">Create patch</h3>
|
<h3 class="blue">Create patch</h3>
|
||||||
<div class="container-description">Create an IPS/UPS/APS patch from two different ROMs </div>
|
<div class="container-description">Create a patch from two different ROMs </div>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="six columns text-right"><label for="input-file-rom1">Original ROM:</label></div>
|
<div class="six columns text-right"><label for="input-file-rom1">Original ROM:</label></div>
|
||||||
|
@ -83,6 +85,7 @@
|
||||||
<input type="radio" id="radio-ups" name="patch-type" /><label for="radio-ups">UPS</label>
|
<input type="radio" id="radio-ups" name="patch-type" /><label for="radio-ups">UPS</label>
|
||||||
<input type="radio" id="radio-aps" name="patch-type" /><label for="radio-aps">APS</label>
|
<input type="radio" id="radio-aps" name="patch-type" /><label for="radio-aps">APS</label>
|
||||||
<input type="radio" id="radio-apsn64" name="patch-type" /><label for="radio-apsn64">APS (N64)</label>
|
<input type="radio" id="radio-apsn64" name="patch-type" /><label for="radio-apsn64">APS (N64)</label>
|
||||||
|
<!-- <input type="radio" id="radio-bps" name="patch-type" /><label for="radio-bps">BPS</label> -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -94,7 +97,7 @@
|
||||||
|
|
||||||
|
|
||||||
<h3 class="green">Byte flipper</h3>
|
<h3 class="green">Byte flipper</h3>
|
||||||
<div class="container-description">This tool can flip bytes on a file in order to change its endianness</div>
|
<div class="container-description">This tool flips bytes in a file in order to change its endianness</div>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="six columns text-right"><label for="input-file-flip">File</label></div>
|
<div class="six columns text-right"><label for="input-file-flip">File</label></div>
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
CACHE MANIFEST
|
|
||||||
#v20170723
|
|
||||||
# WARNING: THIS FILE WILL BE DEPRECATED
|
|
||||||
#CACHE:
|
|
||||||
index.html
|
|
||||||
RomPatcher.css
|
|
||||||
RomPatcher.js
|
|
||||||
favicon.png
|
|
||||||
logo.png
|
|
||||||
ips.js
|
|
||||||
ups.js
|
|
||||||
aps.js
|
|
||||||
ByteFlipper.js
|
|
||||||
|
|
||||||
# force these files to be loaded in network
|
|
||||||
NETWORK:
|
|
||||||
*
|
|
|
@ -1,5 +1,5 @@
|
||||||
CACHE MANIFEST
|
CACHE MANIFEST
|
||||||
#v20171021
|
#v20171103
|
||||||
#CACHE:
|
#CACHE:
|
||||||
index.html
|
index.html
|
||||||
RomPatcher.css
|
RomPatcher.css
|
||||||
|
@ -9,6 +9,7 @@ logo.png
|
||||||
ips.js
|
ips.js
|
||||||
ups.js
|
ups.js
|
||||||
aps.js
|
aps.js
|
||||||
|
bps.js
|
||||||
ByteFlipper.js
|
ByteFlipper.js
|
||||||
|
|
||||||
# force these files to be loaded in network
|
# force these files to be loaded in network
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue