/* RomPatcher.js v20171107 - Marc Robledo 2016-2017 - http://www.marcrobledo.com/license */ var MAX_ROM_SIZE=33554432;var SNES_HEADERED_ROMS_SIZE=[ 262144+512, 524288+512, 1048576+512, 2097152+512, 4194304+512, 8388608+512, 16777216+512, 33554432+512, 50331648+512 ]; var romFile, headeredRomFile, unheaderedRomFile, patch, romFile1, romFile2, tempFile; /* Shortcuts */ function addEvent(e,ev,f){e.addEventListener(ev,f,false)} function el(e){return document.getElementById(e)} /* initialize app */ addEvent(window,'load',function(){ el('input-file-rom').value=''; el('input-file-patch').value=''; el('input-file-rom1').value=''; el('input-file-rom2').value=''; el('input-file-flip').value=''; addEvent(el('input-file-rom'), 'change', function(){ romFile=new MarcBinFile(this, function(){ el('checkbox-removeheader').checked=false; unheaderedRomFile=null; if(SNES_HEADERED_ROMS_SIZE.indexOf(romFile.fileSize)>=0){ el('row-removeheader').style.display='flex'; }else{ el('row-removeheader').style.display='none'; } updateChecksums(romFile); }); }); addEvent(el('input-file-patch'), 'change', function(){ openPatchFile(this); }); addEvent(el('input-file-rom1'), 'change', function(){ romFile1=new MarcBinFile(this); }); addEvent(el('input-file-rom2'), 'change', function(){ romFile2=new MarcBinFile(this); }); addEvent(el('checkbox-removeheader'), 'change', function(){ if(!unheaderedRomFile){ headeredRomFile=romFile; unheaderedRomFile=new MarcBinFile(headeredRomFile.fileSize-512); unheaderedRomFile.writeBytes(0, headeredRomFile.readBytes(512, headeredRomFile.fileSize-512)); unheaderedRomFile.fileName=headeredRomFile.fileName; } if(this.checked) romFile=unheaderedRomFile; else romFile=headeredRomFile; updateChecksums(romFile); }); }); function updateChecksums(file){ el('rom-info').style.display='block'; sha1(file); var crc32str=crc32(file).toString(16); while(crc32str.length<8) crc32str='0'+crc32str; el('crc32').innerHTML=crc32str; el('md5').innerHTML=md5(file); } function _readPatchFile(){ tempFile.littleEndian=false; if(tempFile.readString(0,5)===IPS_MAGIC){ patch=readIPSFile(tempFile); }else if(tempFile.readString(0,4)===UPS_MAGIC){ patch=readUPSFile(tempFile); }else if(tempFile.readString(0,5)===APS_MAGIC){ patch=readAPSFile(tempFile); }else if(tempFile.readString(0,4)===BPS_MAGIC){ patch=readBPSFile(tempFile); }/*else if(tempFile.readString(0,4)===APSGBA_MAGIC){ patch=readAPSGBAFile(tempFile); }*/else { MarcDialogs.alert('Invalid IPS/UPS/APS/BPS file'); } } function openPatchFile(f){tempFile=new MarcBinFile(f, _readPatchFile)} function applyPatchFile(p,r){ if(p && r){ var patchedROM=p.apply(r); patchedROM.fileName=r.fileName.replace(/\.(.*?)$/, ' (patched).$1'); patchedROM.save() }else{ MarcDialogs.alert('No ROM/patch selected'); } } function createPatchFile(){ var MODES=['ips','ups','aps','apsn64'/*,'apsgba','bps'*/]; var mode=0; for(var i=0; iMAX_IPS_SIZE || romFile2.fileSize>MAX_IPS_SIZE)){ MarcDialogs.alert('Files are too big for IPS format'); return false; } var newPatch; if(mode==='ips'){ newPatch=createIPSFromFiles(romFile1, romFile2); }else if(mode==='ups'){ newPatch=createUPSFromFiles(romFile1, romFile2); }else if(mode==='aps'){ newPatch=createAPSFromFiles(romFile1, romFile2, false); }else if(mode==='apsn64'){ newPatch=createAPSFromFiles(romFile1, romFile2, true); }else if(mode==='apsgba'){ newPatch=createAPSGBAFromFiles(romFile1, romFile2); }else if(mode==='bps'){ newPatch=createBPSFromFiles(romFile1, romFile2); } newPatch.export().save(); } /* CRC32/MD5/SHA-1 calculators */ var HEX_CHR='0123456789abcdef'.split(''); var CRC_TABLE=false; var CAN_USE_CRYPTO_API=(window.crypto&&window.crypto.subtle&&window.crypto.subtle.digest); /* SHA-1 using WebCryptoAPI */ function sha1(file){ if(CAN_USE_CRYPTO_API && file.fileSize<=MAX_ROM_SIZE){ window.crypto.subtle.digest('SHA-1', file.fileReader.dataView.buffer).then(function(hash){ //console.log('SHA-1:'+); var bytes=new Uint8Array(hash); var hexString=''; for(var i=0;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); } /* MarcDialogs.js */ MarcDialogs=function(){function e(e,t,n){a?e.attachEvent("on"+t,n):e.addEventListener(t,n,!1)}function t(){s&&(o?history.go(-1):(c.className="dialog-overlay",s.className=s.className.replace(/ active/g,""),s=null))}function n(e){for(var t=0;t0;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})}