mirror of
https://github.com/marcrobledo/RomPatcher.js.git
synced 2025-06-27 16:25:54 +00:00
111 lines
3 KiB
JavaScript
111 lines
3 KiB
JavaScript
/* APS (GBA) module for Rom Patcher JS v20230331 - Marc Robledo 2017-2023 - http://www.marcrobledo.com/license */
|
|
/* File format specification: https://github.com/btimofeev/UniPatcher/wiki/APS-(GBA) */
|
|
|
|
const APS_GBA_MAGIC='APS1';
|
|
const APS_GBA_BLOCK_SIZE=0x010000; //64Kb
|
|
const APS_GBA_RECORD_SIZE=4 + 2 + 2 + APS_GBA_BLOCK_SIZE;
|
|
|
|
function APSGBA(){
|
|
this.sourceSize=0;
|
|
this.targetSize=0;
|
|
this.records=[];
|
|
}
|
|
APSGBA.prototype.addRecord=function(offset, sourceCrc16, targetCrc16, xorBytes){
|
|
this.records.push({
|
|
offset:offset,
|
|
sourceCrc16:sourceCrc16,
|
|
targetCrc16:targetCrc16,
|
|
xorBytes:xorBytes}
|
|
);
|
|
}
|
|
APSGBA.prototype.toString=function(){
|
|
var s='Total records: '+this.records.length;
|
|
s+='\nInput file size: '+this.sourceSize;
|
|
s+='\nOutput file size: '+this.targetSize;
|
|
return s
|
|
}
|
|
APSGBA.prototype.validateSource=function(sourceFile){
|
|
if(sourceFile.fileSize!==this.sourceSize)
|
|
return false;
|
|
|
|
for(var i=0; i<this.records.length; i++){
|
|
sourceFile.seek(this.records[i].offset);
|
|
var bytes=sourceFile.readBytes(APS_GBA_BLOCK_SIZE);
|
|
if(crc16(sourceFile, this.records[i].offset, APS_GBA_BLOCK_SIZE) !== this.records[i].sourceCrc16)
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
APSGBA.prototype.export=function(fileName){
|
|
var patchFileSize=12 + (this.records.length * APS_GBA_RECORD_SIZE);
|
|
|
|
tempFile=new MarcFile(patchFileSize);
|
|
tempFile.littleEndian=true;
|
|
tempFile.fileName=fileName+'.aps';
|
|
tempFile.writeString(APS_GBA_MAGIC, APS_GBA_MAGIC.length);
|
|
tempFile.writeU32(this.sourceSize);
|
|
tempFile.writeU32(this.targetSize);
|
|
|
|
for(var i=0; i<this.records.length; i++){
|
|
tempFile.writeU32(this.records[i].offset);
|
|
tempFile.writeU16(this.records[i].sourceCrc16);
|
|
tempFile.writeU16(this.records[i].targetCrc16);
|
|
tempFile.writeBytes(this.records[i].xorBytes);
|
|
}
|
|
|
|
return tempFile
|
|
}
|
|
|
|
APSGBA.prototype.apply=function(romFile, validate){
|
|
if(validate && !this.validateSource(romFile)){
|
|
throw new Error('error_crc_input');
|
|
}
|
|
|
|
tempFile=new MarcFile(this.targetSize);
|
|
romFile.copyToFile(tempFile, 0, romFile.fileSize);
|
|
|
|
for(var i=0; i<this.records.length; i++){
|
|
romFile.seek(this.records[i].offset);
|
|
tempFile.seek(this.records[i].offset);
|
|
for(var j=0; j<APS_GBA_BLOCK_SIZE; j++){
|
|
tempFile.writeU8(romFile.readU8() ^ this.records[i].xorBytes[j]);
|
|
}
|
|
|
|
if(validate && crc16(tempFile, this.records[i].offset, APS_GBA_BLOCK_SIZE)!==this.records[i].targetCrc16){
|
|
throw new Error('error_crc_output');
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return tempFile
|
|
}
|
|
|
|
|
|
APSGBA.fromFile=function(patchFile){
|
|
patchFile.seek(0);
|
|
patchFile.littleEndian=true;
|
|
|
|
if(
|
|
patchFile.readString(APS_GBA_MAGIC.length)!==APS_GBA_MAGIC ||
|
|
patchFile.fileSize < (12 + APS_GBA_RECORD_SIZE) ||
|
|
(patchFile.fileSize-12)%APS_GBA_RECORD_SIZE!==0
|
|
)
|
|
return null;
|
|
|
|
var patch=new APSGBA();
|
|
|
|
patch.sourceSize=patchFile.readU32();
|
|
patch.targetSize=patchFile.readU32();
|
|
|
|
while(!patchFile.isEOF()){
|
|
var offset=patchFile.readU32();
|
|
var sourceCrc16=patchFile.readU16();
|
|
var targetCrc16=patchFile.readU16();
|
|
var xorBytes=patchFile.readBytes(APS_GBA_BLOCK_SIZE);
|
|
|
|
patch.addRecord(offset, sourceCrc16, targetCrc16, xorBytes);
|
|
}
|
|
return patch;
|
|
}
|