mirror of
https://github.com/marcrobledo/RomPatcher.js.git
synced 2025-06-27 16:25:54 +00:00
269 lines
No EOL
6.2 KiB
JavaScript
269 lines
No EOL
6.2 KiB
JavaScript
/* PPF module for Rom Patcher JS v20200221 - Marc Robledo 2019-2020 - http://www.marcrobledo.com/license */
|
|
/* File format specification: https://www.romhacking.net/utilities/353/ */
|
|
|
|
|
|
const PPF_MAGIC='PPF';
|
|
const PPF_IMAGETYPE_BIN=0x00;
|
|
const PPF_IMAGETYPE_GI=0x01;
|
|
const PPF_BEGIN_FILE_ID_DIZ_MAGIC='@BEG';//@BEGIN_FILE_ID.DIZ
|
|
|
|
if(typeof module !== "undefined" && module.exports){
|
|
module.exports = PPF;
|
|
}
|
|
function PPF(){
|
|
this.version=3;
|
|
this.imageType=PPF_IMAGETYPE_BIN;
|
|
this.blockCheck=false;
|
|
this.undoData=false;
|
|
this.records=[];
|
|
}
|
|
PPF.prototype.addRecord=function(offset, data, undoData){
|
|
if(this.undoData){
|
|
this.records.push({offset:offset, data:data, undoData:undoData});
|
|
}else{
|
|
this.records.push({offset:offset, data:data});
|
|
}
|
|
}
|
|
PPF.prototype.toString=function(){
|
|
var s=this.description;
|
|
s+='\nPPF version: '+this.version;
|
|
s+='\n#Records: '+this.records.length;
|
|
s+='\nImage type: '+this.imageType;
|
|
s+='\nBlock check: '+!!this.blockCheck;
|
|
s+='\nUndo data: '+this.undoData;
|
|
if(this.fileIdDiz)
|
|
s+='\nFILE_ID.DIZ: '+this.fileIdDiz;
|
|
return s
|
|
}
|
|
PPF.prototype.export=function(fileName){
|
|
var patchFileSize=5+1+50; //PPFx0
|
|
for(var i=0; i<this.records.length; i++){
|
|
patchFileSize+=4+1+this.records[i].data.length;
|
|
if(this.version===3)
|
|
patchFileSize+=4; //offsets are u64
|
|
}
|
|
|
|
if(this.version===3 || this.version===2){
|
|
patchFileSize+=4;
|
|
}
|
|
if(this.blockCheck){
|
|
patchFileSize+=1024;
|
|
}
|
|
if(this.fileIdDiz){
|
|
patchFileSize+=18+this.fileIdDiz.length+16+4;
|
|
}
|
|
|
|
tempFile=new BinFile(patchFileSize);
|
|
tempFile.fileName=fileName+'.ppf';
|
|
tempFile.writeString(PPF_MAGIC);
|
|
tempFile.writeString((this.version*10).toString());
|
|
tempFile.writeU8(parseInt(this.version)-1);
|
|
tempFile.writeString(this.description, 50);
|
|
|
|
|
|
if(this.version===3){
|
|
tempFile.writeU8(this.imageType);
|
|
tempFile.writeU8(this.blockCheck?0x01:0x00);
|
|
tempFile.writeU8(this.undoData?0x01:0x00);
|
|
tempFile.writeU8(0x00); //dummy
|
|
|
|
}else if(this.version===2){
|
|
tempFile.writeU32(this.inputFileSize);
|
|
}
|
|
|
|
if(this.blockCheck){
|
|
tempFile.writeBytes(this.blockCheck);
|
|
}
|
|
|
|
|
|
|
|
tempFile.littleEndian=true;
|
|
for(var i=0; i<this.records.length; i++){
|
|
tempFile.writeU32(this.records[i].offset & 0xffffffff);
|
|
|
|
if(this.version===3){
|
|
var offset2=this.records[i].offset;
|
|
for(var j=0; j<32; j++)
|
|
offset2=parseInt((offset2/2)>>>0);
|
|
tempFile.writeU32(offset2);
|
|
}
|
|
tempFile.writeU8(this.records[i].data.length);
|
|
tempFile.writeBytes(this.records[i].data);
|
|
if(this.undoData)
|
|
tempFile.writeBytes(this.records[i].undoData);
|
|
}
|
|
|
|
if(this.fileIdDiz){
|
|
tempFile.writeString('@BEGIN_FILE_ID.DIZ');
|
|
tempFile.writeString(this.fileIdDiz);
|
|
tempFile.writeString('@END_FILE_ID.DIZ');
|
|
tempFile.writeU16(this.fileIdDiz.length);
|
|
tempFile.writeU16(0x00);
|
|
}
|
|
|
|
|
|
|
|
return tempFile
|
|
}
|
|
PPF.prototype.apply=function(romFile){
|
|
var newFileSize=romFile.fileSize;
|
|
for(var i=0; i<this.records.length; i++){
|
|
if(this.records[i].offset+this.records[i].data.length>newFileSize)
|
|
newFileSize=this.records[i].offset+this.records[i].data.length;
|
|
}
|
|
if(newFileSize===romFile.fileSize){
|
|
tempFile=romFile.slice(0, romFile.fileSize);
|
|
}else{
|
|
tempFile=new BinFile(newFileSize);
|
|
romFile.copyTo(tempFile,0);
|
|
}
|
|
|
|
//check if undoing
|
|
var undoingData=false;
|
|
if(this.undoData){
|
|
tempFile.seek(this.records[0].offset);
|
|
var originalBytes=tempFile.readBytes(this.records[0].data.length);
|
|
var foundDifferences=false;
|
|
for(var i=0; i<originalBytes.length && !foundDifferences; i++){
|
|
if(originalBytes[i]!==this.records[0].data[i]){
|
|
foundDifferences=true;
|
|
}
|
|
}
|
|
if(!foundDifferences){
|
|
undoingData=true;
|
|
}
|
|
}
|
|
|
|
for(var i=0; i<this.records.length; i++){
|
|
tempFile.seek(this.records[i].offset);
|
|
|
|
if(undoingData){
|
|
tempFile.writeBytes(this.records[i].undoData);
|
|
}else{
|
|
tempFile.writeBytes(this.records[i].data);
|
|
}
|
|
}
|
|
|
|
return tempFile
|
|
}
|
|
|
|
PPF.MAGIC=PPF_MAGIC;
|
|
|
|
|
|
|
|
PPF.fromFile=function(patchFile){
|
|
var patch=new PPF();
|
|
|
|
|
|
patchFile.seek(3);
|
|
var version1=parseInt(patchFile.readString(2))/10;
|
|
var version2=patchFile.readU8()+1;
|
|
if(version1!==version2 || version1>3){
|
|
throw new Error('invalid PPF version');
|
|
}
|
|
|
|
patch.version=version1;
|
|
patch.description=patchFile.readString(50).replace(/ +$/,'');
|
|
|
|
|
|
|
|
|
|
if(patch.version===3){
|
|
patch.imageType=patchFile.readU8();
|
|
if(patchFile.readU8())
|
|
patch.blockCheck=true;
|
|
if(patchFile.readU8())
|
|
patch.undoData=true;
|
|
|
|
patchFile.skip(1);
|
|
}else if(patch.version===2){
|
|
patch.blockCheck=true;
|
|
patch.inputFileSize=patchFile.readU32();
|
|
}
|
|
|
|
if(patch.blockCheck){
|
|
patch.blockCheck=patchFile.readBytes(1024);
|
|
}
|
|
|
|
|
|
|
|
patchFile.littleEndian=true;
|
|
while(!patchFile.isEOF()){
|
|
|
|
if(patchFile.readString(4)===PPF_BEGIN_FILE_ID_DIZ_MAGIC){
|
|
patchFile.skip(14);
|
|
//console.log('found file_id.diz begin');
|
|
patch.fileIdDiz=patchFile.readString(3072);
|
|
patch.fileIdDiz=patch.fileIdDiz.substr(0, patch.fileIdDiz.indexOf('@END_FILE_ID.DIZ'));
|
|
break;
|
|
}
|
|
patchFile.skip(-4);
|
|
|
|
var offset;
|
|
if(patch.version===3){
|
|
var u64_1=patchFile.readU32();
|
|
var u64_2=patchFile.readU32();
|
|
offset=u64_1+(u64_2*0x100000000);
|
|
}else
|
|
offset=patchFile.readU32();
|
|
|
|
var len=patchFile.readU8();
|
|
var data=patchFile.readBytes(len);
|
|
|
|
var undoData=false;
|
|
if(patch.undoData){
|
|
undoData=patchFile.readBytes(len);
|
|
}
|
|
|
|
patch.addRecord(offset, data, undoData);
|
|
}
|
|
|
|
|
|
|
|
return patch;
|
|
}
|
|
|
|
|
|
PPF.buildFromRoms=function(original, modified){
|
|
var patch=new PPF();
|
|
|
|
patch.description='Patch description';
|
|
|
|
if(original.fileSize>modified.fileSize){
|
|
var expandedModified=new BinFile(original.fileSize);
|
|
modified.copyTo(expandedModified,0);
|
|
modified=expandedModified;
|
|
}
|
|
|
|
original.seek(0);
|
|
modified.seek(0);
|
|
while(!modified.isEOF()){
|
|
var b1=original.isEOF()?0x00:original.readU8();
|
|
var b2=modified.readU8();
|
|
|
|
if(b1!==b2){
|
|
var differentData=[];
|
|
var offset=modified.offset-1;
|
|
|
|
while(b1!==b2 && differentData.length<0xff){
|
|
differentData.push(b2);
|
|
|
|
if(modified.isEOF() || differentData.length===0xff)
|
|
break;
|
|
|
|
b1=original.isEOF()?0x00:original.readU8();
|
|
b2=modified.readU8();
|
|
}
|
|
|
|
patch.addRecord(offset, differentData);
|
|
}
|
|
}
|
|
|
|
if(original.fileSize<modified.fileSize){
|
|
modified.seek(modified.fileSize-1);
|
|
if(modified.readU8()===0x00)
|
|
patch.addRecord(modified.fileSize-1, [0x00]);
|
|
}
|
|
|
|
return patch
|
|
} |