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

180 lines
No EOL
4.6 KiB
JavaScript

/* BPS module for RomPatcher.js v20180926 - Marc Robledo 2016-2018 - 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.validateSource=function(romFile){return this.sourceChecksum===crc32(romFile,false)}
BPS.prototype.apply=function(romFile){
if(!this.validateSource(romFile)){
MarcDialogs.alert('Error: invalid source ROM checksum');
return false;
}
// 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;
}else{
//console.log(action.type)
}
}
tempFile=new MarcBinFile(newFileSize);
//alert(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;
//seek+=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;
if(decodedMetaDataLength.number){
patchFile.metaData=file.readString(seek, decodedMetaDataLength.number);
seek+=patchFile.metaData.number;
}
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};
}