diff --git a/index.html b/index.html
new file mode 100644
index 0000000..033bea9
--- /dev/null
+++ b/index.html
@@ -0,0 +1,91 @@
+
+
+
+ IPS Patcher
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This small web app allows you to apply a IPS patch to your favorite retro games.
+
+
+
+
+
+
+
+
Apply IPS patch
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Create IPS patch
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ips-patcher.appcache b/ips-patcher.appcache
new file mode 100644
index 0000000..bd20b70
--- /dev/null
+++ b/ips-patcher.appcache
@@ -0,0 +1,6 @@
+CACHE MANIFEST
+# last update 2017-02-28
+# these belong to the CACHE block
+index.html
+ips-patcher.js
+ips-patcher.png
\ No newline at end of file
diff --git a/ips-patcher.js b/ips-patcher.js
new file mode 100644
index 0000000..20e0a7c
--- /dev/null
+++ b/ips-patcher.js
@@ -0,0 +1,290 @@
+/* ips-patcher.js v20160416 - Marc Robledo 2016 - http://www.marcrobledo.com/license */
+
+
+
+
+/* Implement 3-byte reader/writer in MarcBinFile */
+MarcBinFile.prototype.readThreeBytes=function(offset){
+ return (this.readByte(offset+0) << 16)+(this.readByte(offset+1) << 8)+(this.readByte(offset+2))
+}
+MarcBinFile.prototype.writeThreeBytes=function(offset,val){
+ this.writeBytes(offset, [(val & 0xff0000) >> 16, (val & 0x00ff00) >> 8, (val & 0x0000ff)]);
+}
+
+
+
+
+
+
+/* Shortcuts */
+function addEvent(e,ev,f){e.addEventListener(ev,f,false)}
+function el(e){return document.getElementById(e)}
+
+
+
+
+
+/*
+ IPS file format
+ doc: http://www.smwiki.net/wiki/IPS_file_format
+*/
+var RECORD_RLE=0x0000;
+var RECORD_SIMPLE=1;
+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; iromFile.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;
+ }
+ }
+ }
+
+ var patchedFile=new MarcBinFile(romFile.fileSize);
+ patchedFile.fileName=romFile.fileName.replace(/\.(.*?)$/, ' (patched).$1');
+ var clonedFileSize=this.truncate || romFile.fileSize;
+ for(var i=0; i16777215)
+ alert('Too big ROM file');
+ });
+}
+
+function openROM1(f){
+ romFile1=new MarcBinFile(f, function(){
+ if(romFile1.fileSize>16777215)
+ alert('Too big ROM file');
+ });
+}
+function openROM2(f){
+ romFile2=new MarcBinFile(f, function(){
+ if(romFile2.fileSize>16777215)
+ alert('Too big ROM file');
+ });
+}
+
+function openIPS(f){
+ tempFile=new MarcBinFile(f, readIPS);
+}
+
+
+
+
+
+
+function readIPS(){
+ tempFile.littleEndian=false;
+
+ if(tempFile.readString(0,5)!=='PATCH'){
+ MarcDialogs.alert('Invalid IPS File');
+ return false;
+ }
+
+
+ ipsFile=new IPS();
+ var EOF=false;
+ var seek=5;
+
+ while(seekmodified.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; i0;e++)d+=String.fromCharCode(c[e]);return d},MarcBinFile.prototype.writeByte=function(a,b){this.fileReader.dataView.setUint8(a,b,this.littleEndian)},MarcBinFile.prototype.writeByteSigned=function(a,b){this.fileReader.dataView.setInt8(a,b,this.littleEndian)},MarcBinFile.prototype.writeBytes=function(a,b){for(var c=0;c