diff --git a/README.md b/README.md index 0e87d9c..22f12f2 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ A ROM patcher made in HTML5. * Supported formats: * IPS * UPS - * APS (N64) + * APS * BPS * RUP * VCDiff (xdelta) @@ -13,5 +13,6 @@ A ROM patcher made in HTML5. * can patch and create patches * shows ROM CRC32, MD5 and SHA-1 before patching * can remove headers before patching +* unzips files automatically * made in Vanilla JS * can be run in any modern web browser, including mobile diff --git a/RomPatcher.css b/RomPatcher.css index d38edf3..ca4ffe5 100644 --- a/RomPatcher.css +++ b/RomPatcher.css @@ -1,4 +1,4 @@ -/* WebApps CSS template by Marc Robledo v20180428 */ +/* WebApps CSS template by Marc Robledo v20190531 */ /* minify at https://cssminifier.com/ + https://www.base64-image.de/ */ /* @FONT-FACES */ @@ -147,6 +147,10 @@ hr{border:none;border-top:1px dotted #bbb;margin:15px 0} .tab{background-color:#f9fafa;padding:30px 15px;border-radius: 3px} #tab1{display:none} +.buttons{ + margin-top:20px; + text-align:right +} @@ -291,6 +295,12 @@ button.no-text.with-icon:before{margin-right:0px} + + + + + + /* responsive */ @media only screen and (max-width:641px){ #wrapper{font-size:14px} @@ -303,35 +313,48 @@ button.no-text.with-icon:before{margin-right:0px} -/* MarcDialogs */ -.dialog-overlay,.dialog{visibility:hidden;opacity:0} -.dialog-overlay.active,.dialog.active{visibility:visible;opacity:1;transition-delay:0s}/* fixes fade-in/fade-out*/ +/* ZIP dialog */ +.zip-overlay{ + position:fixed; + display:flex; + top:0; + left:0; + width:100%; + height:100%; + background-color:rgba(0,0,0,.65); -.dialog-overlay{ - transition:visibility 0s .2s, opacity .2s; - - background-color:black; - -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=75)";/* IE8 */ - background-color:rgba(0,0,0,.7) + margin:0; + padding:0; } - -.dialog{ - position:absolute;top:0;left:0; /* fix for reserved space */ - - transform:translateY(-10px); - transition:visibility 0s .2s, opacity .2s ease-in, transform .2s ease-in; - - background-color:#f9fafa; - padding:15px; - min-width:360px; - max-width:80%; +.zip-dialog{ + vertical-align:middle; + color:#999; + text-align:center; + margin:auto; + background-color:white; + box-sizing:border-box; + padding:4px; border-radius:3px; - box-shadow:0 5px 15px 0 rgba(0,0,0,.5); - line-height:1.8; + min-width:340px; + max-width: 90%; } -.dialog.active{transform:translateY(0px)} -.buttons{ - margin-top:20px; - text-align:right +.zipped-files{ + list-style:none; + padding:0; + margin: 0; + max-height:300px; + overflow-y:auto; } - +.zipped-files li{ + color:#3c3c3c; + padding: 2px 8px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +.zipped-files li:hover{ + background-color:#eee; + cursor:pointer; + color: black; + border-radius: 3px; +} \ No newline at end of file diff --git a/RomPatcher.js b/RomPatcher.js index 529ec0a..3c83daf 100644 --- a/RomPatcher.js +++ b/RomPatcher.js @@ -1,4 +1,4 @@ -/* Rom Patcher JS v20190411 - Marc Robledo 2016-2018 - http://www.marcrobledo.com/license */ +/* Rom Patcher JS v20190531 - Marc Robledo 2016-2019 - http://www.marcrobledo.com/license */ const TOO_BIG_ROM_SIZE=67108863; const HEADERS_INFO=[ [/\.nes$/, 16, 1024], //interNES @@ -10,8 +10,8 @@ const HEADERS_INFO=[ -const FORCE_HTTPS=true; /* service worker */ +const FORCE_HTTPS=true; if(FORCE_HTTPS && location.protocol==='http:') location.href=window.location.href.replace('http:','https:'); else if(location.protocol==='https:' && 'serviceWorker' in navigator) @@ -28,37 +28,30 @@ var webWorkerApply,webWorkerCreate,webWorkerCrc; try{ webWorkerApply=new Worker('./worker_apply.js'); webWorkerApply.onmessage = event => { // listen for events from the worker - //romFile._u8array=event.data.romFileU8Array; - //romFile._dataView=new DataView(romFile._u8array.buffer); - //patchFile._u8array=event.data.patchFileU8Array; - //patchFile._dataView=new DataView(patchFile._u8array.buffer); - //if(patch.file){ - // patch.file._u8array=patchFile._u8array; - // patch.file._dataView=patchFile._dataView; - //} - + //retrieve arraybuffers back from webworker + if(!el('checkbox-removeheader').checked && !el('checkbox-addheader').checked){ //when adding/removing header we don't need the arraybuffer back + romFile._u8array=event.data.romFileU8Array; + romFile._dataView=new DataView(romFile._u8array.buffer); + } + patchFile._u8array=event.data.patchFileU8Array; + patchFile._dataView=new DataView(patchFile._u8array.buffer); + + preparePatchedRom(romFile, new MarcFile(event.data.patchedRomU8Array.buffer), headerSize); - setTabApplyEnabled(true); }; webWorkerApply.onerror = event => { // listen for events from the worker + romFile=new MarcFile(el('input-file-rom'), _parseROM); + patchFile=new MarcFile(el('input-file-patch'), _readPatchFile); + setMessage('apply', _(event.message.replace('Error: ','')), 'error'); setTabApplyEnabled(true); }; - - webWorkerCreate=new Worker('./worker_create.js'); webWorkerCreate.onmessage = event => { // listen for events from the worker - //console.log('received_create'); - //romFile1._u8array=event.data.sourceFileU8Array; - //romFile1._dataView=new DataView(romFile1._u8array.buffer); - //romFile2._u8array=event.data.modifiedFileU8Array; - //romFile2._dataView=new DataView(romFile2._u8array.buffer); - - var newPatchFile=new MarcFile(event.data.patchFileU8Array); newPatchFile.fileName=romFile2.fileName.replace(/\.[^\.]+$/,'')+'.'+el('select-patch-type').value; newPatchFile.save(); @@ -73,8 +66,6 @@ try{ - - webWorkerCrc=new Worker('./worker_crc.js'); webWorkerCrc.onmessage = event => { // listen for events from the worker //console.log('received_crc'); @@ -105,27 +96,25 @@ function _(str){return userLanguage[str] || str} -function selectFetchedPatch(i){ - patchFile=fetchedPatches[i].slice(0); - _readPatchFile(); -} -function fetchPredefinedPatch(i, doNotDisable){ - if(!doNotDisable) - setTabApplyEnabled(false); - - var patchFromUrl=PREDEFINED_PATCHES[i].patch; +function fetchPatch(uri){ + setTabApplyEnabled(false); setMessage('apply', _('downloading'), 'loading'); + var isCompressed=/\#/.test(uri); + var patchURI=decodeURI(uri.replace(/\#.*?$/, '')); + //console.log(patchURI); + var compressedName=uri.replace(/^.*?\#/,''); + //console.log(compressedName); + + if(typeof window.fetch==='function'){ - fetch(patchFromUrl) + fetch(patchURI) .then(result => result.arrayBuffer()) // Gets the response and returns it as a blob .then(arrayBuffer => { - fetchedPatches[i]=new MarcFile(arrayBuffer); - fetchedPatches[i].fileName=decodeURIComponent(patchFromUrl); - - if(i===el('input-file-patch').selectedIndex) - selectFetchedPatch(i); + fetchedPatches[patchURI]=patchFile=new MarcFile(arrayBuffer); + fetchedPatches[patchURI].fileName=patchURI.replace(/^(.*?\/)+/g, ''); + _readPatchFile(); }) .catch(function(evt){ setMessage('apply', _('error_downloading'), 'error'); @@ -133,16 +122,14 @@ function fetchPredefinedPatch(i, doNotDisable){ }); }else{ var xhr=new XMLHttpRequest(); - xhr.open('GET', patchFromUrl, true); + xhr.open('GET', patchURI, true); xhr.responseType='arraybuffer'; xhr.onload=function(evt){ if(this.status===200){ - fetchedPatches[i]=new MarcFile(xhr.response); - fetchedPatches[i].fileName=decodeURIComponent(patchFromUrl); - - if(i===el('input-file-patch').selectedIndex) - selectFetchedPatch(i); + fetchedPatches[patchURI]=patchFile=new MarcFile(xhr.response); + fetchedPatches[patchURI].fileName=patchURI.replace(/^(.*?\/)+/g, ''); + _readPatchFile(); }else{ setMessage('apply', _('error_downloading')+' ('+this.status+')', 'error'); } @@ -157,9 +144,48 @@ function fetchPredefinedPatch(i, doNotDisable){ } +function _parseROM(){ + el('checkbox-addheader').checked=false; + el('checkbox-removeheader').checked=false; + + if(romFile.readString(4).startsWith(ZIP_MAGIC)){ + parseZIPFile(romFile); + setTabApplyEnabled(false); + }else{ + if(headerSize=canHaveFakeHeader(romFile)){ + el('row-addheader').style.display='flex'; + if(headerSize<1024){ + el('headersize').innerHTML=headerSize+'b'; + }else{ + el('headersize').innerHTML=parseInt(headerSize/1024)+'kb'; + } + el('row-removeheader').style.display='none'; + }else if(headerSize=hasHeader(romFile)){ + el('row-addheader').style.display='none'; + el('row-removeheader').style.display='flex'; + }else{ + el('row-addheader').style.display='none'; + el('row-removeheader').style.display='none'; + } + + updateChecksums(romFile, 0); + } +} + /* initialize app */ addEvent(window,'load',function(){ + /* zip-js web worker */ + if(CAN_USE_WEB_WORKERS){ + zip.useWebWorkers=true; + zip.workerScriptsPath='./libs/'; + }else{ + zip.useWebWorkers=false; + + var script=document.createElement('script'); + script.src='./libs/inflate.js'; + document.getElementsByTagName('head')[0].appendChild(script); + } /* language */ var langCode=(navigator.language || navigator.userLanguage).substr(0,2); @@ -181,36 +207,38 @@ addEvent(window,'load',function(){ addEvent(el('input-file-rom'), 'change', function(){ setTabApplyEnabled(false); - romFile=new MarcFile(this, function(){ - el('checkbox-addheader').checked=false; - el('checkbox-removeheader').checked=false; - - if(headerSize=canHaveFakeHeader(romFile)){ - el('row-addheader').style.display='flex'; - if(headerSize<1024){ - el('headersize').innerHTML=headerSize+'b'; - }else{ - el('headersize').innerHTML=parseInt(headerSize/1024)+'kb'; - } - el('row-removeheader').style.display='none'; - }else if(headerSize=hasHeader(romFile)){ - el('row-addheader').style.display='none'; - el('row-removeheader').style.display='flex'; - }else{ - el('row-addheader').style.display='none'; - el('row-removeheader').style.display='none'; - } - - updateChecksums(romFile, 0); - }); + romFile=new MarcFile(this, _parseROM); }); + /* predefined patches: parse URL parameter */ + /*if(/\?.*?patch=[^=]+/.test(window.location.href)){ + + var patchUri=decodeURI(window.location.href.match(/\?.*?patch=([^=]+)/)[1]); + var patchInfo={ + patch:patchUri, + name:patchUri + }; + + if(/\?.*?name=[^=]+/.test(window.location.href)){ + patchInfo.name=decodeURI(window.location.href.match(/\?.*?name=([^=]+)/)[1]); + } + if(/\?.*?crc=[0-9a-f]{8}/i.test(window.location.href)){ + patchInfo.crc=parseInt(window.location.href.match(/\?.*?patch=([0-9a-f]{8})/)[1], 16); + } + + if(typeof PREDEFINED_PATCHES === 'undefined'){ + PREDEFINED_PATCHES=[patchInfo]; + }else{ + PREDEFINED_PATCHES.push(patchInfo); + } + }*/ + /* predefined patches */ if(typeof PREDEFINED_PATCHES!=='undefined'){ - fetchedPatches=new Array(PREDEFINED_PATCHES.length); + fetchedPatches={}; var container=el('input-file-patch').parentElement; container.removeChild(el('input-file-patch')); @@ -219,22 +247,26 @@ addEvent(window,'load',function(){ select.id='input-file-patch'; for(var i=0; i { evt.waitUntil( diff --git a/index.html b/index.html index cb67b7a..bbbaa56 100644 --- a/index.html +++ b/index.html @@ -9,11 +9,18 @@ - + + + + + + + - + + @@ -22,11 +29,27 @@ + + @@ -53,28 +76,28 @@
SHA-1:
- +
- +
@@ -120,9 +143,10 @@ +