/* UPS module for Rom Patcher JS v20220315 - Marc Robledo 2017-2022 - http://www.marcrobledo.com/license */ /* File format specification: http://www.romhacking.net/documents/392/ */ const UPS_MAGIC='UPS1'; function UPS(){ this.records=[]; this.sizeInput=0; this.sizeOutput=0; this.checksumInput=0; this.checksumOutput=0; } UPS.prototype.addRecord=function(relativeOffset, d){ this.records.push({offset:relativeOffset, XORdata:d}) } UPS.prototype.toString=function(){ var s='Records: '+this.records.length; s+='\nInput file size: '+this.sizeInput; s+='\nOutput file size: '+this.sizeOutput; s+='\nInput file checksum: '+padZeroes(this.checksumInput,4); s+='\nOutput file checksum: '+padZeroes(this.checksumOutput,4); return s } UPS.prototype.export=function(fileName){ var patchFileSize=UPS_MAGIC.length;//UPS1 string patchFileSize+=UPS_getVLVLength(this.sizeInput); //input file size patchFileSize+=UPS_getVLVLength(this.sizeOutput); //output file size for(var i=0; i>7; if(data===0){ this.writeU8(0x80 | x); break; } this.writeU8(x); data=data-1; } } function UPS_readVLV(){ var data=0; var shift=1; while(1){ var x=this.readU8(); if(x==-1) throw new Error('Can\'t read UPS VLV at 0x'+(this.offset-1).toString(16)); data+=(x&0x7f)*shift; if((x&0x80)!==0) break; shift=shift<<7; data+=shift; } return data } function UPS_getVLVLength(data){ var len=0; while(1){ var x=data & 0x7f; data=data>>7; len++; if(data===0){ break; } data=data-1; } return len; } function parseUPSFile(file){ var patch=new UPS(); file.readVLV=UPS_readVLV; file.seek(UPS_MAGIC.length); patch.sizeInput=file.readVLV(); patch.sizeOutput=file.readVLV(); var nextOffset=0; while(file.offset<(file.fileSize-12)){ var relativeOffset=file.readVLV(); var XORdifferences=[]; while(file.readU8()){ XORdifferences.push(file._lastRead); } patch.addRecord(relativeOffset, XORdifferences); } file.littleEndian=true; patch.checksumInput=file.readU32(); patch.checksumOutput=file.readU32(); if(file.readU32()!==crc32(file, 0, true)){ throw new Error('error_crc_patch'); } file.littleEndian=false; return patch; } function createUPSFromFiles(original, modified){ var patch=new UPS(); patch.sizeInput=original.fileSize; patch.sizeOutput=modified.fileSize; var previousSeek=1; while(!modified.isEOF()){ var b1=original.isEOF()?0x00:original.readU8(); var b2=modified.readU8(); if(b1!==b2){ var currentSeek=modified.offset; var XORdata=[]; while(b1!==b2){ XORdata.push(b1 ^ b2); if(modified.isEOF()) break; b1=original.isEOF()?0x00:original.readU8(); b2=modified.readU8(); } patch.addRecord(currentSeek-previousSeek, XORdata); previousSeek=currentSeek+XORdata.length+1; } } patch.checksumInput=crc32(original); patch.checksumOutput=crc32(modified); return patch }