1
0
Fork 0
mirror of https://github.com/marcrobledo/RomPatcher.js.git synced 2025-06-27 16:25:54 +00:00

added APS support

This commit is contained in:
Marc Robledo 2017-07-23 12:33:13 +02:00
parent 679de31233
commit 2a1a1c4d07
6 changed files with 239 additions and 8 deletions

View file

@ -1,7 +1,11 @@
# RomPatcher.js # RomPatcher.js
An IPS/UPS ROM patcher made in HTML5. A ROM patcher made in HTML5.
**Features:** **Features:**
* Supported formats:
* IPS
* UPS
* APS (N64)
* can patch and create patches * can patch and create patches
* compatible with IPS and UPS formats * compatible with IPS and UPS formats
* shows ROM CRC32, MD5 and SHA-1 before patching * shows ROM CRC32, MD5 and SHA-1 before patching

View file

@ -52,8 +52,12 @@ function _readPatchFile(){
patch=readIPSFile(tempFile); patch=readIPSFile(tempFile);
}else if(tempFile.readString(0,4)===UPS_MAGIC){ }else if(tempFile.readString(0,4)===UPS_MAGIC){
patch=readUPSFile(tempFile); patch=readUPSFile(tempFile);
}else{ }else if(tempFile.readString(0,5)===APS_MAGIC){
MarcDialogs.alert('Invalid IPS/UPS file'); patch=readAPSFile(tempFile);
}/*else if(tempFile.readString(0,4)===APSGBA_MAGIC){
patch=readAPSGBAFile(tempFile);
}*/else {
MarcDialogs.alert('Invalid IPS/UPS/APS file');
} }
} }
function openPatchFile(f){tempFile=new MarcBinFile(f, _readPatchFile)} function openPatchFile(f){tempFile=new MarcBinFile(f, _readPatchFile)}
@ -70,7 +74,7 @@ function applyPatchFile(p,r){
function createPatchFile(){ function createPatchFile(){
var MODES=['ips','ups']; var MODES=['ips','ups','aps','apsn64'/*,'apsgba'*/];
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)
@ -90,6 +94,12 @@ function createPatchFile(){
newPatch=createIPSFromFiles(romFile1, romFile2); newPatch=createIPSFromFiles(romFile1, romFile2);
}else if(mode==='ups'){ }else if(mode==='ups'){
newPatch=createUPSFromFiles(romFile1, romFile2); newPatch=createUPSFromFiles(romFile1, romFile2);
}else if(mode==='aps'){
newPatch=createAPSFromFiles(romFile1, romFile2, false);
}else if(mode==='apsn64'){
newPatch=createAPSFromFiles(romFile1, romFile2, true);
}else if(mode==='apsgba'){
newPatch=createAPSGBAFromFiles(romFile1, romFile2);
} }
newPatch.export().save(); newPatch.export().save();
} }

212
aps.js Normal file
View file

@ -0,0 +1,212 @@
/* APS (N64) module for RomPatcher.js v20170723 - Marc Robledo 2017 - http://www.marcrobledo.com/license */
/* File format specification: https://github.com/btimofeev/UniPatcher/wiki/APS-(N64) */
var RECORD_RLE=0x0000;
var RECORD_SIMPLE=1;
var APS_MAGIC='APS10';
function APS(){
this.records=[];
this.headerType=0;
this.encodingMethod=0;
this.description='no description';
this.header={};
}
APS.prototype.addRecord=function(o, d){
this.records.push({offset:o, type:RECORD_SIMPLE, data:d})
}
APS.prototype.addRLERecord=function(o, l, b){
this.records.push({offset:o, type:RECORD_RLE, length:l, byte:b})
}
APS.prototype.toString=function(){
nSimpleRecords=0;
nRLERecords=0;
for(var i=0; i<this.records.length; i++){
if(this.records[i].type===RECORD_RLE)
nRLERecords++;
else
nSimpleRecords++;
}
var s='';
s+='\Simple records: '+nSimpleRecords;
s+='\nRLE records: '+nRLERecords;
s+='\nTotal records: '+this.records.length;
s+='\nHeader type: '+this.headerType;
s+='\nEncoding method: '+this.encodingMethod;
s+='\nDescription: '+this.description;
s+='\nHeader: '+JSON.stringify(this.header);
return s
}
APS.prototype.export=function(){
var patchFileSize=(this.headerType===1)?78:61;
for(var i=0; i<this.records.length; i++){
if(this.records[i].type===RECORD_RLE)
patchFileSize+=7;
else
patchFileSize+=5+this.records[i].data.length; //offset+length+data
}
tempFile=new MarcBinFile(patchFileSize);
tempFile.littleEndian=true;
tempFile.fileName='patch.aps';
tempFile.writeString(0, APS_MAGIC, APS_MAGIC.length);
tempFile.writeByte(5, this.headerType);
tempFile.writeByte(6, this.encodingMethod);
tempFile.writeString(7, this.description, 50);
var seek;
if(this.headerType===1){
tempFile.writeByte(57, this.header.originalFileFormat);
tempFile.writeString(58, this.header.cartId, 3);
tempFile.writeBytes(61, this.header.crc);
tempFile.writeBytes(69, this.header.pad);
tempFile.writeInt(74, this.header.sizeOutput);
seek=78;
}else{
tempFile.writeInt(57, this.header.sizeOutput);
seek=61;
}
for(var i=0; i<this.records.length; i++){
var rec=this.records[i];
if(rec.type===RECORD_RLE){
tempFile.writeInt(seek, rec.offset);
tempFile.writeByte(seek+4, 0x00);
tempFile.writeByte(seek+5, rec.byte);
tempFile.writeByte(seek+6, rec.length);
seek+=7;
}else{
tempFile.writeInt(seek, rec.offset);
tempFile.writeByte(seek+4, rec.data.length);
tempFile.writeBytes(seek+5, rec.data);
seek+=5+rec.data.length;
}
}
return tempFile
}
APS.prototype.apply=function(romFile){
if(this.headerType===1){
if(romFile.readString(0x3c, 3)!==this.header.cartId){
MarcDialogs.alert('Invalid ROM cart id');
return false;
}
var crc=romFile.readBytes(0x10, 8);
var crcOk=true;
for(var i=0; i<8 && crcOk; i++){
if(crc[i]!==this.header.crc[i])
crcOk=false;
}
if(!crcOk){
MarcDialogs.alert('Invalid ROM checksum');
return false;
}
}
tempFile=new MarcBinFile(this.header.sizeOutput);
for(var i=0; i<romFile.fileSize && i<this.header.sizeOutput; i++)
tempFile.writeByte(i, romFile.readByte(i));
for(var i=0; i<this.records.length; i++){
var rec=this.records[i];
if(rec.type===RECORD_RLE){
for(var j=0; j<rec.length; j++)
tempFile.writeByte(rec.offset+j, rec.byte);
}else{
for(var j=0; j<rec.data.length; j++)
tempFile.writeByte(rec.offset+j, rec.data[j]);
}
}
return tempFile
}
function readAPSFile(file){
var patchFile=new APS();
file.littleEndian=true;
patchFile.headerType=file.readByte(5);
patchFile.encodingMethod=file.readByte(6);
patchFile.description=file.readString(7, 50);
var seek;
if(patchFile.headerType===1){
patchFile.header.originalFileFormat=file.readByte(57);
patchFile.header.cartId=file.readString(58, 3);
patchFile.header.crc=file.readBytes(61, 8);
patchFile.header.pad=file.readBytes(69, 5);
patchFile.header.sizeOutput=file.readInt(74);
seek=78;
}else{
patchFile.header.sizeOutput=file.readInt(57);
seek=61;
}
while(seek<file.fileSize){
var offset=file.readInt(seek);
seek+=4;
var length=file.readByte(seek);
seek+=1;
if(length==RECORD_RLE){
patchFile.addRLERecord(offset, file.readByte(seek+1), file.readByte(seek));
seek+=2;
}else{
patchFile.addRecord(offset, file.readBytes(seek, length));
seek+=length;
}
}
return patchFile;
}
function createAPSFromFiles(original, modified, N64header){
tempFile=new APS();
if(N64header){
tempFile.headerType=1;
tempFile.header.originalFileFormat=0;
tempFile.header.cartId=original.readString(0x3c, 3);
tempFile.header.crc=original.readBytes(0x10, 8);
tempFile.header.pad=[0,0,0,0,0];
}
tempFile.header.sizeOutput=modified.fileSize;
var seek=0;
while(seek<modified.fileSize){
var b1=original.readByte(seek);
var b2=modified.readByte(seek);
if(b1!==b2){
var RLERecord=true;
var differentBytes=[];
var offset=seek;
while(b1!==b2 && differentBytes.length<255){
differentBytes.push(b2);
if(b2!==differentBytes[0])
RLERecord=false;
seek++;
b1=seek>original.fileSize?0x00:original.readByte(seek);
b2=modified.readByte(seek);
}
if(RLERecord && differentBytes.length>2){
tempFile.addRLERecord(offset, differentBytes.length, differentBytes[0]);
}else{
tempFile.addRecord(offset, differentBytes);
}
//seek++;
}else{
seek++;
}
}
return tempFile
}

View file

@ -11,6 +11,7 @@
<script type="text/javascript" src="./RomPatcher.js"></script> <script type="text/javascript" src="./RomPatcher.js"></script>
<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="./ByteFlipper.js"></script> <script type="text/javascript" src="./ByteFlipper.js"></script>
</head> </head>
<body> <body>
@ -45,7 +46,7 @@
</div> </div>
<div class="row"> <div class="row">
<div class="six columns text-right"><label for="input-file-patch">Patch file (IPS/UPS):</label></div> <div class="six columns text-right"><label for="input-file-patch">Patch file (IPS/UPS/APS):</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>
@ -59,7 +60,7 @@
<h3 class="blue">Create patch</h3> <h3 class="blue">Create patch</h3>
<div class="container-description">Create an IPS/UPS patch from two different ROMs </div> <div class="container-description">Create an IPS/UPS/APS 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>
@ -80,6 +81,8 @@
<div class="six columns end"> <div class="six columns end">
<input type="radio" id="radio-ips" name="patch-type" checked /><label for="radio-ips">IPS</label> <input type="radio" id="radio-ips" name="patch-type" checked /><label for="radio-ips">IPS</label>
<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-apsn64" name="patch-type" /><label for="radio-apsn64">APS (N64)</label>
</div> </div>
</div> </div>

View file

@ -1,5 +1,5 @@
CACHE MANIFEST CACHE MANIFEST
#v20170722b #v20170723
# WARNING: THIS FILE WILL BE DEPRECATED # WARNING: THIS FILE WILL BE DEPRECATED
#CACHE: #CACHE:
index.html index.html
@ -9,6 +9,7 @@ favicon.png
logo.png logo.png
ips.js ips.js
ups.js ups.js
aps.js
ByteFlipper.js ByteFlipper.js
# force these files to be loaded in network # force these files to be loaded in network

View file

@ -1,5 +1,5 @@
CACHE MANIFEST CACHE MANIFEST
#v20170722b #v2017073
#CACHE: #CACHE:
index.html index.html
RomPatcher.css RomPatcher.css
@ -8,6 +8,7 @@ favicon.png
logo.png logo.png
ips.js ips.js
ups.js ups.js
aps.js
ByteFlipper.js ByteFlipper.js
# force these files to be loaded in network # force these files to be loaded in network