From 1c45b0b38a8297f1cbfc31082fda3ecb500e541c Mon Sep 17 00:00:00 2001 From: Marc Robledo Date: Sat, 22 Jul 2017 11:58:52 +0200 Subject: [PATCH] updated to RomPatcher.JS * supports UPS format now * full code overhaul * shows CRC32, MD5 and SHA-1 before patching --- ips-patcher.css => RomPatcher.css | 2 +- ips-patcher.js => RomPatcher.js | 404 +++++++++++------------------- ips-patcher.png => RomPatcher.png | Bin index.html | 53 ++-- ips.js | 202 +++++++++++++++ manifest.appcache | 13 + ups.js | 207 +++++++++++++++ 7 files changed, 601 insertions(+), 280 deletions(-) rename ips-patcher.css => RomPatcher.css (99%) rename ips-patcher.js => RomPatcher.js (64%) rename ips-patcher.png => RomPatcher.png (100%) create mode 100644 ips.js create mode 100644 manifest.appcache create mode 100644 ups.js diff --git a/ips-patcher.css b/RomPatcher.css similarity index 99% rename from ips-patcher.css rename to RomPatcher.css index 9f91923..08e936b 100644 --- a/ips-patcher.css +++ b/RomPatcher.css @@ -1,4 +1,4 @@ /* Web App template by Marc Robledo v20170304 */ @import "https://fonts.googleapis.com/css?family=Open+Sans:400,400i,700,800";body{margin:0;font:15px 'Open Sans',sans-serif;cursor:default;background-color:#1e3f59}.help:hover{cursor:help}.hidden{display:none}.mono{font-family:monospace;color:#888}.left{float:left}.right{float:right}.hide{display:none}.underline{text-decoration:underline}.strike{text-decoration:line-through}.clickable{cursor:pointer}.text-ellipsis{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}span.sprite,span.icon,button.with-icon:before{background-image:url(data:image/png;base64,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);background-position:0 0;display:inline-block;vertical-align:middle}span.sprite{width:24px;height:24px}span.icon,button.with-icon:before{width:16px;height:16px;margin-right:4px;content:""}button.with-icon.icon0:before{background-position:-0 -16px}button.with-icon.icon1:before{background-position:-16px -16px}button.with-icon.icon2:before{background-position:-32px -16px}button.with-icon.icon3:before{background-position:-48px -16px}button.with-icon.icon4:before{background-position:-64px -16px}button.with-icon.icon5:before{background-position:-80px -16px}button.with-icon.icon6:before{background-position:-96px -16px}button.with-icon.icon7:before{background-position:-112px -16px}button.with-icon.icon8:before{background-position:-128px -16px}button.with-icon.icon9:before{background-position:-144px -16px}button.with-icon.icon10:before{background-position:-160px -16px}button.with-icon.colored.icon0:before{background-position:-0 -32px}button.with-icon.colored.icon1:before{background-position:-16px -32px}button.with-icon.colored.icon2:before{background-position:-32px -32px}button.with-icon.colored.icon3:before{background-position:-48px -32px}button.with-icon.colored.icon4:before{background-position:-64px -32px}button.with-icon.colored.icon5:before{background-position:-80px -32px}button.with-icon.colored.icon6:before{background-position:-96px -32px}button.with-icon.colored.icon7:before{background-position:-112px -32px}button.with-icon.colored.icon8:before{background-position:-128px -32px}button.with-icon.colored.icon9:before{background-position:-144px -32px}button.with-icon.colored.icon10:before{background-position:-160px -32px}span.with-icon.icon0,button.with-icon.transparent.icon0:before{background-position:-0 0}span.with-icon.icon1,button.with-icon.transparent.icon1:before{background-position:-16px 0}span.with-icon.icon2,button.with-icon.transparent.icon2:before{background-position:-32px 0}span.with-icon.icon3,button.with-icon.transparent.icon3:before{background-position:-48px 0}span.with-icon.icon4,button.with-icon.transparent.icon4:before{background-position:-64px 0}span.with-icon.icon5,button.with-icon.transparent.icon5:before{background-position:-80px 0}span.with-icon.icon6,button.with-icon.transparent.icon6:before{background-position:-96px 0}span.with-icon.icon7,button.with-icon.transparent.icon7:before{background-position:-112px 0}span.with-icon.icon8,button.with-icon.transparent.icon8:before{background-position:-128px 0}span.with-icon.icon9,button.with-icon.transparent.icon9:before{background-position:-144px 0}span.with-icon.icon10,button.with-icon.transparent.icon10:before{background-position:-160px 0}span.sprite{width:24px;height:24px}span.sprite.flag{width:16px;height:11px}span.sprite.flag0{background-position:32px -56px}span.sprite.flag1{background-position:-48px -56px}span.sprite.flag2{background-position:-64px -56px}span.sprite.flag3{background-position:-80px -56px}span.sprite.flag4{background-position:-96px -56px}span.sprite.flag5{background-position:-112px -56px}span.sprite.github{background-position:0 -48px;width:16px;height:16px;margin-right:5px}span.sprite.heart{background-position:-16px -48px;width:16px;height:16px;margin-right:5px}#header{color:#fff;text-align:center;padding:20px 0;line-height:2;margin-bottom:30px}h1:before{background-repeat:no-repeat;background-size:100% 100%;display:inline-block;width:56px;height:56px;vertical-align:middle;content:"";margin-right:10px}#header h1{font-size:140%;margin:0;display:inline-block}h2{font-size:85%;font-weight:400;margin:10px 0 0}h2 a.author{color:#fff;text-decoration:none;border-bottom:1px solid #476277;margin-right:10px}h2 a.author:hover{color:#aebac3;border-color:#aebac3}h2 a.button{text-decoration:none;color:#fff;background-color:#122534;padding:10px 20px;border-radius:3px}h2 a.button:hover{background-color:#0e1c28}.donate.button{background-color:#51a451!important;animation:donateglow 2s infinite}.donate.button:hover{background-color:#3e903e!important}@keyframes donateglow{0%{box-shadow:none}50%{box-shadow:#d8ff7c 0 0 8px inset}100%{box-shadow:none}}hr{border:none;border-top:1px dotted #bbb;margin:15px 0}h3{color:aaa;font-size:90%;font-weight:700;text-align:center;background-color:#fafafa;line-height:1;padding:10px;border-radius:4px;margin:0 0 20px;text-transform:uppercase}h3:first-child{margin:-24px -20px 20px;border-radius:4px 4px 0 0}h4{border-bottom:1px solid #ccc;color:aaa;margin-top:50px;font-style:italic}h4:first-child{margin-top:0}table{width:100%}tbody tr:nth-child(even){background-color:#f2f2f2}th{background-color:#d4d4d4}input[type=text],input[type=number],select{padding:6px 8px;width:250px;max-width:90%;font:14px 'Open Sans',sans-serif;border:1px solid #888;border-radius:2px;box-sizing:border-box}input[type=text]:hover,input[type=number]:hover,select:hover{border-color:#666}input[type=text]:focus,input[type=number]:focus,select:focus{box-shadow:#63bce5 0 0 4px 1px;border-color:#47a8df}input[type=text].error,input[type=number].error,select.error{box-shadow:#f88 0 0 4px 1px;border-color:red}input[type=text].small,input[type=number].small,select.small{width:70px}input[type=text].medium,input[type=number].medium,select.medium{width:130px}.full-width{width:100%!important}button{font-family:inherit;font-size:100%;min-width:120px;border-radius:2px;border:1px solid;border-color:#ccc #ccc #b3b3b3;padding:6px 12px;margin:0 5px;background-color:#f0f0f0;background-image:linear-gradient(to bottom,#fff,#e6e6e6);text-shadow:0 1px 0 #f8f8f8;color:#333;box-shadow:0 1px 0 rgba(255,255,255,0.2) inset;transition:border-color ease-in-out .15s;box-sizing:border-box}button.small{min-width:1px}button:hover{text-shadow:none;border-color:#999}button:active{background-image:none;transform:translate(0px,1px);border-color:#888;text-shadow:none;box-shadow:0 2px 8px -3px rgba(0,0,0,0.5) inset;background-color:#eee;transform:translateY(1px)}button:disabled{opacity:.35}button.colored{color:#fff!important}button.colored.blue{box-shadow:0 1px 0 rgba(120,200,230,0.5) inset;background-color:#21759B;background-image:linear-gradient(to bottom,#2A95C5,#21759B);border-color:#21759B #21759B #1E6A8D;text-shadow:0 -1px 0 #20749a}button.colored.blue:hover{box-shadow:0 1px 0 rgba(120,200,230,0.6) inset;background-color:#278AB7;background-image:linear-gradient(to bottom,#2E9FD2,#21759B);border-color:#1B607F;text-shadow:0 -1px 0 #1b6080}button.colored.blue:active{box-shadow:0 2px 8px -3px rgba(0,0,0,0.5) inset;background:linear-gradient(to bottom,#21759B,#278AB7) repeat scroll 0 0 #1B607F;border-color:#124560 #0e74a3 #0e74a3;text-shadow:0 -1px 0 #177ea4}button.colored.red{box-shadow:0 1px 0 #df4c45 inset;background-color:#b72319;background-image:linear-gradient(to bottom,#ce271d,#a41f17);border-color:#a41f17 #a41f17 #951d15;text-shadow:0 -1px 0 #a01d15}button.colored.red:hover{box-shadow:0 1px 0 #e8564f inset!important;background-color:#bf261c!important;background-image:linear-gradient(to bottom,#db2c20,#a52017)!important;border-color:#871a13!important;text-shadow:0 -1px 0 #8b1b14!important}button.colored.red:active{box-shadow:0 2px 8px -3px rgba(0,0,0,0.5) inset!important;background:linear-gradient(to bottom,#a51f17,#c1251b) repeat scroll 0 0 #b22118!important;border-color:#66140c #a0150d #a0150d!important;text-shadow:0 -1px 0 #a41e17!important}button.colored.green{box-shadow:0 1px 0 #81cf81 inset!important;background-color:#59b259!important;background-image:linear-gradient(to bottom,#61c261,#51a351)!important;border-color:#52a452 #4f9f4f #448944!important;text-shadow:0 -1px 0 #37a137!important}button.colored.green:hover{box-shadow:0 1px 0 #99de99 inset!important;background-color:#278AB7!important;background-image:linear-gradient(to bottom,#64ce64,#51a351)!important;border-color:#3f7f3f!important;text-shadow:0 -1px 0 #1c9216!important}button.colored.green:active{box-shadow:0 2px 8px -3px rgba(0,0,0,0.5) inset!important;background:linear-gradient(to bottom,#1e8f22,#23a627) repeat scroll 0 0 #219b24!important;border-color:#105815 #17941b #17941b!important;text-shadow:0 -1px 0 #17a417!important}button.colored.orange{box-shadow:0 1px 0 #ffb519 inset!important;background-color:#e69501!important;background-image:linear-gradient(to bottom,#fda502,#d08400)!important;border-color:#d38500 #c97e00 #b46f00!important;text-shadow:0 -1px 0 #8f5901!important}button.colored.orange:hover{box-shadow:0 1px 0 #ffc03c inset!important;background-color:#e69501!important;background-image:linear-gradient(to bottom,#ffb01e,#d08400)!important;border-color:#a36300!important;text-shadow:0 -1px 0 #95620f!important}button.colored.orange:active{box-shadow:0 2px 8px -3px rgba(0,0,0,0.5) inset!important;background:linear-gradient(to bottom,#c07400,#ed9500) repeat scroll 0 0 #219b24!important;border-color:#53400b #886912 #886912!important;text-shadow:0 -1px 0 #654e0e!important}button.transparent{min-width:auto;box-shadow:none;padding:0;background:none;text-decoration:underline;border:none;text-shadow:none;color:#000!important}button.transparent:hover{background-color:rgba(0,0,0,.05)}#cards{max-width:720px;margin:0 auto}.card{position:relative;margin-bottom:40px;padding:20px;color:#000;text-shadow:initial;border-radius:4px;line-height:2.5;background-color:#fafafa;box-shadow:#0e2f49 0 0 30px}.card-yellow{border-top:5px solid #feb806}.card-yellow h3{background-color:#feb806;color:#fff}.card-green{border-top:5px solid #9ac430}.card-green h3{background-color:#9ac430;color:#fff}.card-red{border-top:5px solid #fe5d05}.card-red h3{background-color:#fe5d05;color:#fff}.card-notice{background-color:#ffd823;padding:10px}.card-warning{background-color:#b31212;color:#fff;font-weight:700}@media only screen and (max-width:741px){#cards{padding:0 10px}}@media only screen and (max-width:721px){.card,button,input,select{font-size:13px}h1:before{width:32px;height:32px}}@media only screen and (max-width:481px){.author:after{content:"";display:block;margin-bottom:10px}.card,button,input,select{font-size:12px}}.row:before,.row:after{display:table;content:''}.row:after{clear:both}.columns{min-width:1px;float:left;position:relative;box-sizing:border-box}.row .columns:last-child{float:right}.row .columns.end{float:left}.row.collapse{margin:0;max-width:none;width:auto}.row.collapse .columns{padding:0!important}.clearfix:before,.clearfix:after{display:table;content:' '}.clearfix{clear:both}.columns.medium-1,.columns.one{width:8.33333%}.columns.medium-2,.columns.two{width:16.66667%}.columns.medium-3,.columns.three{width:25%}.columns.medium-4,.columns.four{width:33.33333%}.columns.medium-5,.columns.five{width:41.66667%}.columns.medium-6,.columns.six{width:50%}.columns.medium-7,.columns.seven{width:58.33333%}.columns.medium-8,.columns.eight{width:66.66667%}.columns.medium-9,.columns.nine{width:75%}.columns.medium-10,.columns.ten{width:83.33333%}.columns.medium-11,.columns.eleven{width:91.66667%}.columns.medium-12,.columns.twelve{width:100%}@media only screen and (max-width:481px){.columns.small-1{width:8.33333%!important}.columns.small-2{width:16.66667%!important}.columns.small-3{width:25%!important}.columns.small-4{width:33.33333%!important}.columns.small-5{width:41.66667%!important}.columns.small-6{width:50%!important}.columns.small-7{width:58.33333%!important}.columns.small-8{width:66.66667%!important}.columns.small-9{width:75%!important}.columns.small-10{width:83.33333%!important}.columns.small-11{width:91.66667%!important}.columns.small-12{width:100%!important}}.drop-zone{text-align:center;font-size:120%;font-weight:700;padding:30px;border:2px dashed #ccc;color:#ccc;margin:10px 0}#drop-overlay{z-index:1000;background-color:rgba(255,255,255,0.9);color:#000;position:fixed;top:0;left:0;width:100%;height:100%;box-shadow:0 0 0 4px #0b71e5 inset;font:bold 35px 'Open Sans',sans-serif;text-align:center;text-shadow:#fff 0 0 4px;opacity:0;visibility:hidden;transition:visibility .2s,opacity .2s}body.dragging-files #drop-overlay{visibility:visible;opacity:1}#drop-overlay span{display:block;position:relative;top:45%;width:100%;text-align:center;font:bold 35px 'Open Sans',sans-serif;color:#000}#dialog-about{background-color:#ffd823}.dialog-overlay,.dialog{visibility:hidden;opacity:0}.dialog-overlay.active,.dialog.active{visibility:visible;opacity:1;transition-delay:0}.dialog-overlay{transition:visibility 0 .2s,opacity .2s;background-color:#000;-ms-filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=75);background-color:rgba(0,0,0,.75)}.dialog{position:absolute;top:0;left:0;transform:translateY(-10px);transition:visibility 0 .2s,opacity .2s ease-in,transform .2s ease-in;background-color:#fff;padding:15px;min-width:360px;max-width:80%;border-radius:4px;box-shadow:#000 0 0 10px;line-height:1.8}.dialog.active{transform:translateY(0px)}.buttons{margin-top:20px;text-align:center} -h1:before{background-image:url(ips-patcher.png)} +h1:before{background-image:url(RomPatcher.png)} diff --git a/ips-patcher.js b/RomPatcher.js similarity index 64% rename from ips-patcher.js rename to RomPatcher.js index 20e0a7c..ed3ab96 100644 --- a/ips-patcher.js +++ b/RomPatcher.js @@ -1,290 +1,178 @@ -/* 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)]); -} - - - - - - +/* ips-patcher.js v20170722 - Marc Robledo 2016-2017 - http://www.marcrobledo.com/license */ +var MAX_ROM_SIZE=33554432; +var romFile, patch, romFile1, romFile2, tempFile, romHashes={}; /* 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(){ +function _readPatchFile(){ tempFile.littleEndian=false; - if(tempFile.readString(0,5)!=='PATCH'){ - MarcDialogs.alert('Invalid IPS File'); - return false; + if(tempFile.readString(0,5)===IPS_MAGIC){ + patch=readIPSFile(tempFile); + }else if(tempFile.readString(0,4)===UPS_MAGIC){ + patch=readUPSFile(tempFile); + }else{ + MarcDialogs.alert('Invalid IPS/UPS file'); } - - - 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; i>2]=d[i]+(d[i+1]<<8)+(d[i+2]<<16)+(d[i+3]<<24);return md5blks} +function _cmn(q,a,b,x,s,t){a=_add32(_add32(a,q),_add32(x,t));return _add32((a<>>(32-s)),b)} +function ff(a,b,c,d,x,s,t){return _cmn((b&c)|((~b)&d),a,b,x,s,t)} +function gg(a,b,c,d,x,s,t){return _cmn((b&d)|(c&(~d)),a,b,x,s,t)} +function hh(a,b,c,d,x,s,t){return _cmn(b^c^d,a,b,x,s,t)} +function ii(a,b,c,d,x,s,t){return _cmn(c^(b|(~d)),a,b,x,s,t)} +function md5(file){ + var data=new Uint8Array(file.fileReader.dataView.buffer); + + var n=data.length,state=[1732584193,-271733879,-1732584194,271733878],i; + for(i=64;i<=data.length;i+=64) + _md5cycle(state,_md5blk(data.slice(i-64,i))); + data=data.slice(i-64); + var tail=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]; + for(i=0;i>2]|=data[i]<<((i%4)<<3); + tail[i>>2]|=0x80<<((i%4)<<3); + if(i>55){ + _md5cycle(state,tail); + for(i=0;i<16;i++)tail[i]=0; + } + tail[14]=n*8; + _md5cycle(state,tail); + + for(var i=0;i>(j*8+4))&0x0f]+HEX_CHR[(state[i]>>(j*8))&0x0f]; + state[i]=s; + } + return state.join('') +} +/* CRC32 - from Alex - https://stackoverflow.com/a/18639999 */ +function _makeCRCTable(){ + var c,crcTable=[]; + for(var n=0;n<256;n++){ + c=n; + for(var k=0;k<8;k++) + c=((c&1)?(0xedb88320^(c>>>1)):(c>>>1)); + crcTable[n]=c; + } + return crcTable; +} +function crc32(file,ignoreLast4Bytes){ + var data=new Uint8Array(file.fileReader.dataView.buffer); + if(!CRC_TABLE) + CRC_TABLE=_makeCRCTable(); + + var crc=0^(-1); + + var len=ignoreLast4Bytes?data.length-4:data.length; + for(var i=0;i>>8)^CRC_TABLE[(crc^data[i])&0xff]; + + return ((crc^(-1))>>>0); } /* MarcBinFile.js v20190703 - Marc Robledo 2014-2016 - http://www.marcrobledo.com/license */ function MarcBinFile(a,b){if("function"!=typeof window.FileReader)throw console.error("MarcBinFile.js: Browser doesn't support FileReader."),"Invalid browser";if("object"==typeof a&&a.name&&a.size)this.file=a,this.fileName=this.file.name,this.fileSize=this.file.size,this.fileType=a.type;else if("object"==typeof a&&a.files){if(1!=a.files.length){for(var c=[],d=a.files.length,e=function(){d--,0==d&&b&&b.call()},f=0;f0;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> 16, (val & 0x00ff00) >> 8, (val & 0x0000ff)])} /* FileSaver.min.js: https://github.com/eligrey/FileSaver.js/blob/master/FileSaver.min.js */ -var saveAs=saveAs||function(view){"use strict";if(typeof navigator!=="undefined"&&/MSIE [1-9]\./.test(navigator.userAgent)){return}var doc=view.document,get_URL=function(){return view.URL||view.webkitURL||view},save_link=doc.createElementNS("http://www.w3.org/1999/xhtml","a"),can_use_save_link="download"in save_link,click=function(node){var event=new MouseEvent("click");node.dispatchEvent(event)},is_safari=/Version\/[\d\.]+.*Safari/.test(navigator.userAgent),webkit_req_fs=view.webkitRequestFileSystem,req_fs=view.requestFileSystem||webkit_req_fs||view.mozRequestFileSystem,throw_outside=function(ex){(view.setImmediate||view.setTimeout)(function(){throw ex},0)},force_saveable_type="application/octet-stream",fs_min_size=0,arbitrary_revoke_timeout=500,revoke=function(file){var revoker=function(){if(typeof file==="string"){get_URL().revokeObjectURL(file)}else{file.remove()}};if(view.chrome){revoker()}else{setTimeout(revoker,arbitrary_revoke_timeout)}},dispatch=function(filesaver,event_types,event){event_types=[].concat(event_types);var i=event_types.length;while(i--){var listener=filesaver["on"+event_types[i]];if(typeof listener==="function"){try{listener.call(filesaver,event||filesaver)}catch(ex){throw_outside(ex)}}}},auto_bom=function(blob){if(/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)){return new Blob(["\ufeff",blob],{type:blob.type})}return blob},FileSaver=function(blob,name,no_auto_bom){if(!no_auto_bom){blob=auto_bom(blob)}var filesaver=this,type=blob.type,blob_changed=false,object_url,target_view,dispatch_all=function(){dispatch(filesaver,"writestart progress write writeend".split(" "))},fs_error=function(){if(target_view&&is_safari&&typeof FileReader!=="undefined"){var reader=new FileReader;reader.onloadend=function(){var base64Data=reader.result;target_view.location.href="data:attachment/file"+base64Data.slice(base64Data.search(/[,;]/));filesaver.readyState=filesaver.DONE;dispatch_all()};reader.readAsDataURL(blob);filesaver.readyState=filesaver.INIT;return}if(blob_changed||!object_url){object_url=get_URL().createObjectURL(blob)}if(target_view){target_view.location.href=object_url}else{var new_tab=view.open(object_url,"_blank");if(new_tab==undefined&&is_safari){view.location.href=object_url}}filesaver.readyState=filesaver.DONE;dispatch_all();revoke(object_url)},abortable=function(func){return function(){if(filesaver.readyState!==filesaver.DONE){return func.apply(this,arguments)}}},create_if_not_found={create:true,exclusive:false},slice;filesaver.readyState=filesaver.INIT;if(!name){name="download"}if(can_use_save_link){object_url=get_URL().createObjectURL(blob);setTimeout(function(){save_link.href=object_url;save_link.download=name;click(save_link);dispatch_all();revoke(object_url);filesaver.readyState=filesaver.DONE});return}if(view.chrome&&type&&type!==force_saveable_type){slice=blob.slice||blob.webkitSlice;blob=slice.call(blob,0,blob.size,force_saveable_type);blob_changed=true}if(webkit_req_fs&&name!=="download"){name+=".download"}if(type===force_saveable_type||webkit_req_fs){target_view=view}if(!req_fs){fs_error();return}fs_min_size+=blob.size;req_fs(view.TEMPORARY,fs_min_size,abortable(function(fs){fs.root.getDirectory("saved",create_if_not_found,abortable(function(dir){var save=function(){dir.getFile(name,create_if_not_found,abortable(function(file){file.createWriter(abortable(function(writer){writer.onwriteend=function(event){target_view.location.href=file.toURL();filesaver.readyState=filesaver.DONE;dispatch(filesaver,"writeend",event);revoke(file)};writer.onerror=function(){var error=writer.error;if(error.code!==error.ABORT_ERR){fs_error()}};"writestart progress write abort".split(" ").forEach(function(event){writer["on"+event]=filesaver["on"+event]});writer.write(blob);filesaver.abort=function(){writer.abort();filesaver.readyState=filesaver.DONE};filesaver.readyState=filesaver.WRITING}),fs_error)}),fs_error)};dir.getFile(name,{create:false},abortable(function(file){file.remove();save()}),abortable(function(ex){if(ex.code===ex.NOT_FOUND_ERR){save()}else{fs_error()}}))}),fs_error)}),fs_error)},FS_proto=FileSaver.prototype,saveAs=function(blob,name,no_auto_bom){return new FileSaver(blob,name,no_auto_bom)};if(typeof navigator!=="undefined"&&navigator.msSaveOrOpenBlob){return function(blob,name,no_auto_bom){if(!no_auto_bom){blob=auto_bom(blob)}return navigator.msSaveOrOpenBlob(blob,name||"download")}}FS_proto.abort=function(){var filesaver=this;filesaver.readyState=filesaver.DONE;dispatch(filesaver,"abort")};FS_proto.readyState=FS_proto.INIT=0;FS_proto.WRITING=1;FS_proto.DONE=2;FS_proto.error=FS_proto.onwritestart=FS_proto.onprogress=FS_proto.onwrite=FS_proto.onabort=FS_proto.onerror=FS_proto.onwriteend=null;return saveAs}(typeof self!=="undefined"&&self||typeof window!=="undefined"&&window||this.content);if(typeof module!=="undefined"&&module.exports){module.exports.saveAs=saveAs}else if(typeof define!=="undefined"&&define!==null&&define.amd!=null){define([],function(){return saveAs})} \ No newline at end of file +var saveAs=saveAs||function(view){"use strict";if(typeof navigator!=="undefined"&&/MSIE [1-9]\./.test(navigator.userAgent)){return}var doc=view.document,get_URL=function(){return view.URL||view.webkitURL||view},save_link=doc.createElementNS("http://www.w3.org/1999/xhtml","a"),can_use_save_link="download"in save_link,click=function(node){var event=new MouseEvent("click");node.dispatchEvent(event)},is_safari=/Version\/[\d\.]+.*Safari/.test(navigator.userAgent),webkit_req_fs=view.webkitRequestFileSystem,req_fs=view.requestFileSystem||webkit_req_fs||view.mozRequestFileSystem,throw_outside=function(ex){(view.setImmediate||view.setTimeout)(function(){throw ex},0)},force_saveable_type="application/octet-stream",fs_min_size=0,arbitrary_revoke_timeout=500,revoke=function(file){var revoker=function(){if(typeof file==="string"){get_URL().revokeObjectURL(file)}else{file.remove()}};if(view.chrome){revoker()}else{setTimeout(revoker,arbitrary_revoke_timeout)}},dispatch=function(filesaver,event_types,event){event_types=[].concat(event_types);var i=event_types.length;while(i--){var listener=filesaver["on"+event_types[i]];if(typeof listener==="function"){try{listener.call(filesaver,event||filesaver)}catch(ex){throw_outside(ex)}}}},auto_bom=function(blob){if(/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)){return new Blob(["\ufeff",blob],{type:blob.type})}return blob},FileSaver=function(blob,name,no_auto_bom){if(!no_auto_bom){blob=auto_bom(blob)}var filesaver=this,type=blob.type,blob_changed=false,object_url,target_view,dispatch_all=function(){dispatch(filesaver,"writestart progress write writeend".split(" "))},fs_error=function(){if(target_view&&is_safari&&typeof FileReader!=="undefined"){var reader=new FileReader;reader.onloadend=function(){var base64Data=reader.result;target_view.location.href="data:attachment/file"+base64Data.slice(base64Data.search(/[,;]/));filesaver.readyState=filesaver.DONE;dispatch_all()};reader.readAsDataURL(blob);filesaver.readyState=filesaver.INIT;return}if(blob_changed||!object_url){object_url=get_URL().createObjectURL(blob)}if(target_view){target_view.location.href=object_url}else{var new_tab=view.open(object_url,"_blank");if(new_tab==undefined&&is_safari){view.location.href=object_url}}filesaver.readyState=filesaver.DONE;dispatch_all();revoke(object_url)},abortable=function(func){return function(){if(filesaver.readyState!==filesaver.DONE){return func.apply(this,arguments)}}},create_if_not_found={create:true,exclusive:false},slice;filesaver.readyState=filesaver.INIT;if(!name){name="download"}if(can_use_save_link){object_url=get_URL().createObjectURL(blob);setTimeout(function(){save_link.href=object_url;save_link.download=name;click(save_link);dispatch_all();revoke(object_url);filesaver.readyState=filesaver.DONE});return}if(view.chrome&&type&&type!==force_saveable_type){slice=blob.slice||blob.webkitSlice;blob=slice.call(blob,0,blob.size,force_saveable_type);blob_changed=true}if(webkit_req_fs&&name!=="download"){name+=".download"}if(type===force_saveable_type||webkit_req_fs){target_view=view}if(!req_fs){fs_error();return}fs_min_size+=blob.size;req_fs(view.TEMPORARY,fs_min_size,abortable(function(fs){fs.root.getDirectory("saved",create_if_not_found,abortable(function(dir){var save=function(){dir.getFile(name,create_if_not_found,abortable(function(file){file.createWriter(abortable(function(writer){writer.onwriteend=function(event){target_view.location.href=file.toURL();filesaver.readyState=filesaver.DONE;dispatch(filesaver,"writeend",event);revoke(file)};writer.onerror=function(){var error=writer.error;if(error.code!==error.ABORT_ERR){fs_error()}};"writestart progress write abort".split(" ").forEach(function(event){writer["on"+event]=filesaver["on"+event]});writer.write(blob);filesaver.abort=function(){writer.abort();filesaver.readyState=filesaver.DONE};filesaver.readyState=filesaver.WRITING}),fs_error)}),fs_error)};dir.getFile(name,{create:false},abortable(function(file){file.remove();save()}),abortable(function(ex){if(ex.code===ex.NOT_FOUND_ERR){save()}else{fs_error()}}))}),fs_error)}),fs_error)},FS_proto=FileSaver.prototype,saveAs=function(blob,name,no_auto_bom){return new FileSaver(blob,name,no_auto_bom)};if(typeof navigator!=="undefined"&&navigator.msSaveOrOpenBlob){return function(blob,name,no_auto_bom){if(!no_auto_bom){blob=auto_bom(blob)}return navigator.msSaveOrOpenBlob(blob,name||"download")}}FS_proto.abort=function(){var filesaver=this;filesaver.readyState=filesaver.DONE;dispatch(filesaver,"abort")};FS_proto.readyState=FS_proto.INIT=0;FS_proto.WRITING=1;FS_proto.DONE=2;FS_proto.error=FS_proto.onwritestart=FS_proto.onprogress=FS_proto.onwrite=FS_proto.onabort=FS_proto.onerror=FS_proto.onwriteend=null;return saveAs}(typeof self!=="undefined"&&self||typeof window!=="undefined"&&window||this.content);if(typeof module!=="undefined"&&module.exports){module.exports.saveAs=saveAs}else if(typeof define!=="undefined"&&define!==null&&define.amd!=null){define([],function(){return saveAs})} + diff --git a/ips-patcher.png b/RomPatcher.png similarity index 100% rename from ips-patcher.png rename to RomPatcher.png diff --git a/index.html b/index.html index 5244c40..0366302 100644 --- a/index.html +++ b/index.html @@ -1,23 +1,25 @@ - + - IPS Patcher + ROMPatcher.js - - + + - - - + + + + + @@ -26,7 +28,7 @@
- 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.
@@ -34,23 +36,24 @@
-

Apply IPS patch

+

Apply patch

-
- +
+ +
-
-
- +
+
+
- +
@@ -60,23 +63,31 @@
-

Create IPS patch

+

Create patch

- +
- + +
+
+ +
+
Patch type
+
+ +
- +
diff --git a/ips.js b/ips.js new file mode 100644 index 0000000..c3302af --- /dev/null +++ b/ips.js @@ -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; 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; + } + } + } + + tempFile=new MarcBinFile(romFile.fileSize); + + var clonedFileSize=this.truncate || romFile.fileSize; + for(var i=0; imodified.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>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(seekoriginal.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 +} \ No newline at end of file