mirror of
https://github.com/marcrobledo/RomPatcher.js.git
synced 2025-06-27 16:25:54 +00:00
updated to RomPatcher.JS
* supports UPS format now * full code overhaul * shows CRC32, MD5 and SHA-1 before patching
This commit is contained in:
parent
52adda0bbe
commit
1c45b0b38a
7 changed files with 601 additions and 280 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
53
index.html
53
index.html
|
@ -1,23 +1,25 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html manifest="ips-patcher.appcache">
|
<html manifest="manifest.appcache">
|
||||||
<head>
|
<head>
|
||||||
<title>IPS Patcher</title>
|
<title>ROMPatcher.js</title>
|
||||||
<meta http-equiv="content-Type" content="text/html; charset=UTF-8"/>
|
<meta http-equiv="content-Type" content="text/html; charset=UTF-8"/>
|
||||||
<meta name="description" content="This small web app allows you to apply IPS patches to any ROM file."/>
|
<meta name="description" content="This small web app allows you to apply IPS/UPS patches to your ROM dump files."/>
|
||||||
<meta name="keywords" content="ips,patcher,html5,rom,patch,hack,translation"/>
|
<meta name="keywords" content="ips,ups,patcher,online,html5,rom,patch,hack,translation"/>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"/>
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"/>
|
||||||
<link rel="shortcut icon" href="./ips-patcher.png"/>
|
<link rel="shortcut icon" href="./RomPatcher.png"/>
|
||||||
<link type="text/css" rel="stylesheet" href="./ips-patcher.css" media="all"/>
|
<link type="text/css" rel="stylesheet" href="./RomPatcher.css" media="all"/>
|
||||||
<script type="text/javascript" src="./ips-patcher.js"></script>
|
<script type="text/javascript" src="./RomPatcher.js"></script>
|
||||||
|
<script type="text/javascript" src="./ips.js"></script>
|
||||||
|
<script type="text/javascript" src="./ups.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<!-- HEADER -->
|
<!-- HEADER -->
|
||||||
<div id="header">
|
<div id="header">
|
||||||
<h1>IPS Patcher</h1>
|
<h1>RomPatcher.js</h1>
|
||||||
<h2>
|
<h2>
|
||||||
by <a href="/" class="author">Marc Robledo</a>
|
by <a href="/" class="author">Marc Robledo</a>
|
||||||
<a href="https://github.com/marcrobledo/ips-patcher/" target="_blank" class="button"><span class="sprite github"></span> See on GitHub</a>
|
<a href="https://github.com/marcrobledo/RomPatcher.js/" target="_blank" class="button"><span class="sprite github"></span> See on GitHub</a>
|
||||||
<a href="https://www.paypal.me/marcrobledo/5" target="_blank" rel="nofollow" class="donate button"><span class="sprite heart"></span> Donate</a>
|
<a href="https://www.paypal.me/marcrobledo/5" target="_blank" rel="nofollow" class="donate button"><span class="sprite heart"></span> Donate</a>
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
|
@ -26,7 +28,7 @@
|
||||||
|
|
||||||
<div id="cards">
|
<div id="cards">
|
||||||
<div class="card card-notice text-center">
|
<div class="card card-notice text-center">
|
||||||
This small web app allows you to apply a IPS patch to your favorite retro games.
|
This small web app allows you to apply a IPS/UPS patch to your favorite retro games.
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
@ -34,23 +36,24 @@
|
||||||
|
|
||||||
|
|
||||||
<div class="card card-red">
|
<div class="card card-red">
|
||||||
<h3>Apply IPS patch</h3>
|
<h3>Apply patch</h3>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="three columns"><label for="input-file-rom">ROM file:</label></div>
|
<div class="three columns"><label for="input-file-rom">ROM file:</label></div>
|
||||||
<div class="six columns end">
|
<div class="nine columns">
|
||||||
<input type="file" id="input-file-rom" onchange="openROM(this)"/>
|
<input type="file" id="input-file-rom" />
|
||||||
|
<textarea disabled id="rom-info" rows="3" style="width:100%"></textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="three columns"><label for="input-file-ips">IPS file:</label></div>
|
<div class="three columns"><label for="input-file-patch">Patch file (IPS/UPS):</label></div>
|
||||||
<div class="six columns end">
|
<div class="nine columns">
|
||||||
<input type="file" id="input-file-ips" onchange="openIPS(this)"/>
|
<input type="file" id="input-file-patch" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
<button class="with-icon icon9" onclick="ipsFile.applyOnROM(romFile)">Apply IPS patch</button>
|
<button class="with-icon icon9" onclick="applyPatchFile(patch, romFile)">Apply patch</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -60,23 +63,31 @@
|
||||||
|
|
||||||
|
|
||||||
<div class="card card-green">
|
<div class="card card-green">
|
||||||
<h3>Create IPS patch</h3>
|
<h3>Create patch</h3>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="three columns"><label for="input-file-rom1">Original ROM:</label></div>
|
<div class="three columns"><label for="input-file-rom1">Original ROM:</label></div>
|
||||||
<div class="six columns end">
|
<div class="six columns end">
|
||||||
<input type="file" id="input-file-rom1" onchange="openROM1(this)"/>
|
<input type="file" id="input-file-rom1" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="three columns"><label for="input-file-rom2">Modified ROM:</label></div>
|
<div class="three columns"><label for="input-file-rom2">Modified ROM:</label></div>
|
||||||
<div class="six columns end">
|
<div class="six columns end">
|
||||||
<input type="file" id="input-file-rom2" onchange="openROM2(this)"/>
|
<input type="file" id="input-file-rom2" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="three columns">Patch type</div>
|
||||||
|
<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-ups" name="patch-type" /><label for="radio-ups">UPS</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
<button class="with-icon icon9" onclick="createIPS(romFile1, romFile2)">Create IPS patch</button>
|
<button class="with-icon icon9" onclick="createPatchFile()">Create patch</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
202
ips.js
Normal file
202
ips.js
Normal file
|
@ -0,0 +1,202 @@
|
||||||
|
/* IPS module for RomPatcher.js v20170721 - Marc Robledo 2016-2017 - http://www.marcrobledo.com/license */
|
||||||
|
/* File format specification: http://www.smwiki.net/wiki/IPS_file_format */
|
||||||
|
var MAX_IPS_SIZE=16777216;
|
||||||
|
var RECORD_RLE=0x0000;
|
||||||
|
var RECORD_SIMPLE=1;
|
||||||
|
var IPS_MAGIC='PATCH';
|
||||||
|
|
||||||
|
function IPS(){
|
||||||
|
this.records=[];
|
||||||
|
this.truncate=false;
|
||||||
|
}
|
||||||
|
IPS.prototype.addSimpleRecord=function(o, d){
|
||||||
|
this.records.push({offset:o, type:RECORD_SIMPLE, data:d})
|
||||||
|
}
|
||||||
|
IPS.prototype.addRLERecord=function(o, l, b){
|
||||||
|
this.records.push({offset:o, type:RECORD_RLE, length:l, byte:b})
|
||||||
|
}
|
||||||
|
IPS.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;
|
||||||
|
if(this.truncate)
|
||||||
|
s+='\nTruncate at: 0x'+this.truncate.toString(16);
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
IPS.prototype.export=function(){
|
||||||
|
var binFileSize=0;
|
||||||
|
binFileSize+=5; //PATCH string
|
||||||
|
for(var i=0; i<this.records.length; i++){
|
||||||
|
if(this.records[i].type===RECORD_RLE)
|
||||||
|
binFileSize+=(3+2+2+1); //offset+0x0000+length+RLE byte to be written
|
||||||
|
else
|
||||||
|
binFileSize+=(3+2+this.records[i].data.length); //offset+length+data
|
||||||
|
}
|
||||||
|
binFileSize+=3; //EOF string
|
||||||
|
if(this.truncate)
|
||||||
|
binFileSize+=3; //truncate
|
||||||
|
|
||||||
|
tempFile=new MarcBinFile(binFileSize);
|
||||||
|
tempFile.littleEndian=false;
|
||||||
|
tempFile.fileName='patch.ips';
|
||||||
|
tempFile.writeString(0, 'PATCH', 5);
|
||||||
|
var seek=5;
|
||||||
|
for(var i=0; i<this.records.length; i++){
|
||||||
|
var rec=this.records[i];
|
||||||
|
if(rec.type===RECORD_RLE){
|
||||||
|
tempFile.writeThreeBytes(seek, rec.offset);
|
||||||
|
tempFile.writeShort(seek+3, 0x0000);
|
||||||
|
tempFile.writeShort(seek+5, rec.length);
|
||||||
|
tempFile.writeByte(seek+7, rec.byte);
|
||||||
|
seek+=3+2+2+1;
|
||||||
|
}else{
|
||||||
|
tempFile.writeThreeBytes(seek, rec.offset);
|
||||||
|
tempFile.writeShort(seek+3, rec.data.length);
|
||||||
|
tempFile.writeBytes(seek+5, rec.data);
|
||||||
|
seek+=3+2+rec.data.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tempFile.writeString(seek, 'EOF', 3);
|
||||||
|
seek+=3;
|
||||||
|
if(rec.truncate){
|
||||||
|
tempFile.writeThreeBytes(seek, rec.truncate);
|
||||||
|
}
|
||||||
|
|
||||||
|
return tempFile
|
||||||
|
}
|
||||||
|
IPS.prototype.apply=function(romFile){
|
||||||
|
for(var i=0; i<this.records.length; i++){
|
||||||
|
var rec=this.records[i];
|
||||||
|
if(rec.type===RECORD_RLE){
|
||||||
|
if(rec.offset+rec.length>romFile.fileSize){
|
||||||
|
alert('Invalid ROM file (too big?).');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
if(rec.offset+rec.data.length>romFile.fileSize){
|
||||||
|
alert('Invalid ROM file (too big?).');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tempFile=new MarcBinFile(romFile.fileSize);
|
||||||
|
|
||||||
|
var clonedFileSize=this.truncate || romFile.fileSize;
|
||||||
|
for(var i=0; i<romFile.fileSize; 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 readIPSFile(file){
|
||||||
|
var patchFile=new IPS();
|
||||||
|
var EOF=false;
|
||||||
|
var seek=5;
|
||||||
|
|
||||||
|
while(seek<file.fileSize){
|
||||||
|
var address=file.readThreeBytes(seek);
|
||||||
|
seek+=3;
|
||||||
|
|
||||||
|
if(!EOF && address===0x454f46){ /* EOF */
|
||||||
|
EOF=true;
|
||||||
|
}else if(EOF){
|
||||||
|
patchFile.truncate=address;
|
||||||
|
}else{
|
||||||
|
var length=file.readShort(seek);
|
||||||
|
seek+=2;
|
||||||
|
|
||||||
|
if(length==RECORD_RLE){
|
||||||
|
patchFile.addRLERecord(address, file.readShort(seek), file.readByte(seek+2));
|
||||||
|
seek+=3;
|
||||||
|
}else{
|
||||||
|
patchFile.addSimpleRecord(address, file.readBytes(seek, length));
|
||||||
|
seek+=length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return patchFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function createIPSFromFiles(original, modified){
|
||||||
|
tempFile=new IPS();
|
||||||
|
|
||||||
|
if(modified.fileSize<original.fileSize)
|
||||||
|
tempFile.truncate=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 originalSeek=seek;
|
||||||
|
var length=1;
|
||||||
|
|
||||||
|
/* find difference in next 6 bytes (in order to save space) */
|
||||||
|
var nearbyDifference=true;
|
||||||
|
while(nearbyDifference){
|
||||||
|
var seekStart=6;
|
||||||
|
while(seek+seekStart>modified.fileSize){
|
||||||
|
seekStart--;
|
||||||
|
}
|
||||||
|
for(var i=seekStart;i>0 && nearbyDifference;i--){
|
||||||
|
var bc1=original.readByte(seek+i);
|
||||||
|
var bc2=modified.readByte(seek+i);
|
||||||
|
|
||||||
|
if(bc1!=bc2){
|
||||||
|
length+=i;
|
||||||
|
seek+=i;
|
||||||
|
break;
|
||||||
|
}else if(i==1){
|
||||||
|
nearbyDifference=false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var data=modified.readBytes(originalSeek, length);
|
||||||
|
/* check RLE record */
|
||||||
|
for(var i=1; i<length && RLERecord; i++){
|
||||||
|
if(data[i]!==data[0])
|
||||||
|
RLERecord=false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(RLERecord){
|
||||||
|
tempFile.addRLERecord(originalSeek, length, data[0]);
|
||||||
|
}else{
|
||||||
|
tempFile.addSimpleRecord(originalSeek, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
seek=originalSeek+length;
|
||||||
|
}else{
|
||||||
|
seek++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tempFile
|
||||||
|
}
|
13
manifest.appcache
Normal file
13
manifest.appcache
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
CACHE MANIFEST
|
||||||
|
#v20170722
|
||||||
|
#CACHE:
|
||||||
|
index.html
|
||||||
|
RomPatcher.css
|
||||||
|
RomPatcher.js
|
||||||
|
RomPatcher.png
|
||||||
|
ips.js
|
||||||
|
ups.js
|
||||||
|
|
||||||
|
# force these files to be loaded in network
|
||||||
|
NETWORK:
|
||||||
|
*
|
207
ups.js
Normal file
207
ups.js
Normal file
|
@ -0,0 +1,207 @@
|
||||||
|
/* UPS module for RomPatcher.js v20170721 - Marc Robledo 2017 - http://www.marcrobledo.com/license */
|
||||||
|
/* File format specification: http://www.romhacking.net/documents/392/ */
|
||||||
|
/* https://github.com/btimofeev/UniPatcher/blob/master/app/src/main/java/org/emunix/unipatcher/patcher/UPS.java */
|
||||||
|
var UPS_MAGIC='UPS1';
|
||||||
|
function UPS(){
|
||||||
|
this.records=[];
|
||||||
|
this.sizeInput=0;
|
||||||
|
this.sizeOutput=0;
|
||||||
|
this.checksumInput=0;
|
||||||
|
this.checksumOutput=0;
|
||||||
|
this.checksumPatch=0;
|
||||||
|
}
|
||||||
|
UPS.prototype.addRecord=function(o, d){
|
||||||
|
this.records.push({offset:o, 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: '+this.checksumInput;
|
||||||
|
s+='\nOutput file checksum: '+this.checksumOutput;
|
||||||
|
s+='\nPatch file checksum: '+this.checksumPatch;
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
UPS.prototype.export=function(){
|
||||||
|
var encodedSizeInput=encodeVLV(this.sizeInput);
|
||||||
|
var encodedSizeOutput=encodeVLV(this.sizeOutput);
|
||||||
|
var encodedRecords=[];
|
||||||
|
var binFileSize=0;
|
||||||
|
binFileSize+=UPS_MAGIC.length; //UPS1 string
|
||||||
|
binFileSize+=encodedSizeInput.length; //input file size
|
||||||
|
binFileSize+=encodedSizeOutput.length; //output file size
|
||||||
|
for(var i=0; i<this.records.length; i++){
|
||||||
|
encodedRecords.push(encodeVLV(this.records[i].offset));
|
||||||
|
binFileSize+=encodedRecords[i].length;
|
||||||
|
binFileSize+=this.records[i].XORdata.length+1;
|
||||||
|
}
|
||||||
|
binFileSize+=12; //input/output/patch checksums
|
||||||
|
|
||||||
|
tempFile=new MarcBinFile(binFileSize);
|
||||||
|
tempFile.littleEndian=false;
|
||||||
|
tempFile.fileName='patch.ups';
|
||||||
|
tempFile.writeString(0, UPS_MAGIC, UPS_MAGIC.length);
|
||||||
|
|
||||||
|
tempFile.writeBytes(4, encodedSizeInput);
|
||||||
|
tempFile.writeBytes(4+encodedSizeInput.length, encodedSizeOutput);
|
||||||
|
|
||||||
|
var seek=4+encodedSizeInput.length+encodedSizeOutput.length;
|
||||||
|
for(var i=0; i<this.records.length; i++){
|
||||||
|
tempFile.writeBytes(seek, encodedRecords[i]);
|
||||||
|
seek+=encodedRecords[i].length;
|
||||||
|
tempFile.writeBytes(seek, this.records[i].XORdata);
|
||||||
|
seek+=this.records[i].XORdata.length;
|
||||||
|
tempFile.writeByte(seek, 0);
|
||||||
|
seek+=1;
|
||||||
|
}
|
||||||
|
tempFile.littleEndian=true;
|
||||||
|
tempFile.writeInt(seek, this.checksumInput);
|
||||||
|
tempFile.writeInt(seek+4, this.checksumOutput);
|
||||||
|
tempFile.writeInt(seek+8, this.checksumPatch);
|
||||||
|
|
||||||
|
return tempFile
|
||||||
|
}
|
||||||
|
UPS.prototype.apply=function(romFile){
|
||||||
|
if(crc32(romFile)!==this.checksumInput){
|
||||||
|
alert('Invalid input ROM.');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
tempFile=new MarcBinFile(this.sizeOutput);
|
||||||
|
|
||||||
|
/* copy original file */
|
||||||
|
for(var i=0; i<romFile.fileSize; i++)
|
||||||
|
tempFile.writeByte(i, romFile.readByte(i));
|
||||||
|
|
||||||
|
var nextOffset=0;
|
||||||
|
for(var i=0; i<this.records.length; i++){
|
||||||
|
var nextDifference=this.records[i];
|
||||||
|
nextOffset+=nextDifference.offset;
|
||||||
|
for(var j=0; j<nextDifference.XORdata.length; j++){
|
||||||
|
tempFile.writeByte(nextOffset+j, romFile.readByte(nextOffset+j) ^ nextDifference.XORdata[j]);
|
||||||
|
}
|
||||||
|
nextOffset+=nextDifference.XORdata.length+1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(crc32(tempFile)!==this.checksumOutput){
|
||||||
|
alert('Invalid output ROM.');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return tempFile
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* encode/decode variable length values, used by UPS file structure */
|
||||||
|
function encodeVLV(offset){
|
||||||
|
var bytes=[];
|
||||||
|
while(1){
|
||||||
|
var x=offset & 0x7f;
|
||||||
|
offset=offset>>7;
|
||||||
|
if(offset===0){
|
||||||
|
bytes.push(0x80 | x);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
bytes.push(x);
|
||||||
|
offset=offset-1;
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
function decodeVLV(file, pos){
|
||||||
|
var offset=0;
|
||||||
|
var size=0;
|
||||||
|
var shift=1;
|
||||||
|
while(1){
|
||||||
|
var x=file.readByte(pos);
|
||||||
|
pos++;
|
||||||
|
if(x==-1)
|
||||||
|
console.error('corrupted UPS file?');
|
||||||
|
size++;
|
||||||
|
offset+=(x&0x7f)*shift;
|
||||||
|
if((x&0x80)!==0)
|
||||||
|
break;
|
||||||
|
shift=shift<<7;
|
||||||
|
offset+=shift;
|
||||||
|
}
|
||||||
|
return {offset:offset, size:size}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function readUPSFile(file){
|
||||||
|
var patchFile=new UPS();
|
||||||
|
|
||||||
|
var decodedInputFilesize=decodeVLV(tempFile,4);
|
||||||
|
patchFile.sizeInput=decodedInputFilesize.offset;
|
||||||
|
|
||||||
|
|
||||||
|
var decodedOutputFilesize=decodeVLV(tempFile,4+decodedInputFilesize.size);
|
||||||
|
patchFile.sizeOutput=decodedOutputFilesize.offset;
|
||||||
|
|
||||||
|
var seek=4+decodedInputFilesize.size+decodedOutputFilesize.size;
|
||||||
|
var nextOffset=0;
|
||||||
|
while(seek<(tempFile.fileSize-12)){
|
||||||
|
var decodedOffset=decodeVLV(tempFile, seek);
|
||||||
|
seek+=decodedOffset.size;
|
||||||
|
|
||||||
|
nextOffset+=decodedOffset.offset;
|
||||||
|
|
||||||
|
var bytes=[];
|
||||||
|
var lastByte;
|
||||||
|
while(lastByte=tempFile.readByte(seek)){
|
||||||
|
bytes.push(lastByte);
|
||||||
|
seek++;
|
||||||
|
}
|
||||||
|
seek++;
|
||||||
|
patchFile.addRecord(decodedOffset.offset, bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
file.littleEndian=true;
|
||||||
|
patchFile.checksumInput=tempFile.readInt(seek);
|
||||||
|
patchFile.checksumOutput=tempFile.readInt(seek+4);
|
||||||
|
patchFile.checksumPatch=tempFile.readInt(seek+8);
|
||||||
|
|
||||||
|
if(patchFile.checksumPatch!==crc32(file, true)){
|
||||||
|
alert('Invalid patch checksum.');
|
||||||
|
}
|
||||||
|
return patchFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function createUPSFromFiles(original, modified){
|
||||||
|
tempFile=new UPS();
|
||||||
|
tempFile.sizeInput=original.fileSize;
|
||||||
|
tempFile.sizeOutput=modified.fileSize;
|
||||||
|
|
||||||
|
var seek=0;
|
||||||
|
var previousSeek=0;
|
||||||
|
while(seek<modified.fileSize){
|
||||||
|
var b1=seek>original.fileSize?0x00:original.readByte(seek);
|
||||||
|
var b2=modified.readByte(seek);
|
||||||
|
|
||||||
|
if(b1!==b2){
|
||||||
|
var currentSeek=seek;
|
||||||
|
var differentBytes=[];
|
||||||
|
|
||||||
|
while(b1!==b2){
|
||||||
|
differentBytes.push(b1 ^ b2);
|
||||||
|
seek++;
|
||||||
|
b1=seek>original.fileSize?0x00:original.readByte(seek);
|
||||||
|
b2=modified.readByte(seek);
|
||||||
|
}
|
||||||
|
|
||||||
|
var nextDifference=currentSeek-previousSeek;
|
||||||
|
tempFile.addRecord(nextDifference, differentBytes);
|
||||||
|
previousSeek=currentSeek+differentBytes.length+1;
|
||||||
|
seek++;
|
||||||
|
}else{
|
||||||
|
seek++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
tempFile.checksumInput=crc32(original);
|
||||||
|
tempFile.checksumOutput=crc32(modified);
|
||||||
|
//tempFile.checksumPatch=crc32(tempFile.export(), true);
|
||||||
|
return tempFile
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue