1
0
Fork 0
mirror of https://github.com/marcrobledo/RomPatcher.js.git synced 2025-06-27 16:25:54 +00:00

GUI redesign among other fixes

This commit is contained in:
Marc Robledo 2018-04-28 15:57:54 +02:00
parent eb40a592b5
commit a56fa9d4e8
13 changed files with 251 additions and 314 deletions

View file

@ -1,26 +0,0 @@
/* ByteFlipper.js v20170717 - Marc Robledo 2013-2017 - http://www.marcrobledo.com/license */
function flipFile(){
var type=el('radio8').checked?8:4;
if(tempFile.fileSize>MAX_ROM_SIZE){
MarcDialogs.alert('Too big file');
return false;
}else if(tempFile.fileSize%type!==0){
MarcDialogs.alert('File is not divisible by '+type);
return false;
}
var tempFile2=new MarcBinFile(tempFile.fileSize);
var offset=0;
for(var i=0; i<tempFile.fileSize; i+=type){
tempFile2.writeBytes(offset, tempFile.readBytes(offset, type).reverse());
offset+=type;
}
tempFile2.fileName=tempFile.fileName.replace(/\.(.*?)$/, ' (byteflipped).$1');
tempFile2.save();
}
function flipBytesInFile(){
if(el('input-file-flip').files.length)
tempFile=new MarcBinFile(el('input-file-flip'), flipFile);
else
MarcDialogs.alert('No file selected');
}

View file

@ -1,4 +1,4 @@
# RomPatcher.js # Rom Patcher JS
A ROM patcher made in HTML5. A ROM patcher made in HTML5.
**Features:** **Features:**
@ -11,3 +11,4 @@ A ROM patcher made in HTML5.
* shows ROM CRC32, MD5 and SHA-1 before patching * shows ROM CRC32, MD5 and SHA-1 before patching
* can remove SNES headers before patching * can remove SNES headers before patching
* made in Vanilla JS * made in Vanilla JS
* can be run in any modern web browser, including mobile

View file

@ -1,19 +1,41 @@
/* WebApps CSS template by Marc Robledo v20170722 */ /* WebApps CSS template by Marc Robledo v20180428 */
/* minify at https://cssminifier.com/ + https://www.base64-image.de/ */ /* minify at https://cssminifier.com/ + https://www.base64-image.de/ */
/* @FONT-FACES */ /* @FONT-FACES */
@import url('https://fonts.googleapis.com/css?family=Open+Sans:400,400i,700,800'); @import url('https://fonts.googleapis.com/css?family=Open+Sans:400,700');
@import url(https://fonts.googleapis.com/css?family=Roboto+Mono:300);
body{ body{
margin:0; margin:0;
font:14px 'Open Sans',sans-serif; font:15px 'Open Sans',sans-serif;
cursor:default; cursor:default;
margin:0px 0 40px; line-height:1.8;
background-color:#f1f2f3; background-color:#31343a;
color:#3c3c3c color:#3c3c3c;
-moz-user-select:none;
-webkit-user-select: none;
-ms-user-select:none;
-o-user-select:none;
user-select:none
} }
/* flex main column */
html, body{height:100%}
#column{
display: flex;
flex-wrap: nowrap;
height: 100%;
flex-direction: column;
}
header{padding: 5% 0 8%}
header h1{display:none}
footer{padding: 50px 0 20px}
#wrapper{flex-basis:100%}
.clickable{cursor:pointer}
/* flex box */ /* flex box */
.row{ .row{
display:flex; display:flex;
@ -21,81 +43,36 @@ body{
align-items:center; /* vertical align */ align-items:center; /* vertical align */
justify-content:space-between justify-content:space-between
} }
.columns.c1,.columns.one{width:7.33333%} .leftcol{width:35%;text-align:right}
.columns.c2,.columns.two{width:15.66667%} .rightcol{width:63%}
.columns.c3,.columns.three{width:24%} .leftcol,.rightcol{margin-bottom:8px}
.columns.c4,.columns.four{width:32.33333%}
.columns.c5,.columns.five{width:40.66667%}
.columns.c6,.columns.six{width:49%}
.columns.c7,.columns.seven{width:57.33333%}
.columns.c8,.columns.eight{width:65.66667%}
.columns.c9,.columns.nine{width:74%}
.columns.c10,.columns.ten{width:82.33333%}
.columns.c11,.columns.eleven{width:90.66667%}
.columns.c12,.columns.twelve{width:99%}
#the-app .columns{margin-bottom:3px}
.container{background-color:#e4e7ea;padding:15px;border-radius:5px;}
.container-description{text-align:center;margin-bottom:10px;color:#888d92;font-size:95%;}
/* useful classes */
.help:hover{cursor:help}
.hidden{display:none}
.mono{font-family:monospace;color:#888}
.clickable{cursor:pointer} /* also Safari iOS fix for clickable elements */
.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}
.round{border-radius:3px}
/* colors */
.bg-light-gray{background-color:rgba(216,216,216,.99)}
.bg-mid-gray{background-color:rgba(144,144,144,.99);color:white}
.bg-dark-gray{background-color:rgba(72,72,72,.99);color:white}
.bg-red{background-color:#e74c3c}
.bg-orange{background-color:#f39c12}
.bg-blue{background-color:#3498db}
.bg-green{background-color:#25ba84}
.fnt-white{color:white}
.fnt-light-gray{color:#d8d8d8}
.fnt-mid-gray{color:#909090}
.fnt-dark-gray{color:#48484}
.fnt-red{color:#e74c3c}
/*.fnt-red{color:#fe5d05}*/
.fnt-orange{color:#f39c12}
/*.fnt-orange{color:#feb806}*/
.fnt-blue{color:#3498db}
.fnt-green{color:#25ba84}
/*.fnt-green{color:#9ac430}*/
.border-white{border-color:white}
.border-light-gray{border-color:#d8d8d8}
.border-mid-gray{border-color:#909090}
.border-dark-gray{border-color:#48484}
/* Icons */
i.icon{ /* icons */
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAALAAAAAkCAYAAAApQLALAAAFm0lEQVR42u2cf2hWVRjHX+d0ldbyj4JSKwOzFKeMHGQhFBREU8KgENLWD6KCoh+QxRAjo/qjKCL7SVk6SoVgJNjW0n4IlUGtsloZlNF01rJXKzZfy92+D3suPHu4977n3t079r73OfDB+Z5z7r3vPZ+de85zzl3B87yCYVQq/g9ewn+rmbfAf+C3GHUmgpWgExT5Ph0GXeAGUGvSZSNwFshUKTfkWlAPJoPv+NqP82eTWM6akLpzQLcXnb4G83Iu3WrwOTgtZr3zwc9gVVQPHEfO8Spw1Dmj8h7gPLpJB9X1/wX28M+vBUhM8vZ7bulQjiVeK+5Dd8y6G7gePRWvr+YeOOy8UddzCvjTUcAS9wZy2KB73i9BM5gFloAOlf8N9+jjXbg02+8RdQ/WONY7B7wNZoDtXPeXau6Bg87tci1rVbkfwEbwmfr8WVVvpco/AE4Fi8EmcBXL+q0qd2OFyOvShtPBCSF5E8AT6lgPOl7DuWCfGH6RxG1gYRY9cJyURX2X44WVbVCitnHP6ue3irwfwQUir1OdYys3Wg//fzeXe1WV60p4H8aiB41z7kt5wkrf5ySVR0OtF0T9IXCX4zWcB35V5+8sF4UYTQ883gSO8xTQ6cyA6EJ/yPGK6vN/ubfwOO9iMC2gMYopCZRGDzqaX6Be9Ut5ouh5X1Ty3uF4DTSn2K/O26eGbtYDh5Qd4iiELrM35Fhh6Sho5Nn2Fwm+R5ryZjmUa+SJqZ/e4znFK+Izmnjd4ni82QHy/g7musSB8zoGforjvX66LmCIcVzk74zogf20nfN3hOQXMxoCjPaXP0n9RjUJPqqeSCtiyKufVH+ABXmKAyeJQhR4YuGnv8E9oInjjr1qklYn6nWFNDTNlO/m3ico7aigCZxLOy7iRRuZjoFrYox5ewPkXRhnJS7PceAtjsOXIofH/HqrQsp9yuPfn0Lyb6qiKIRPk+iJKdx4dYxFiqBhQ4OtxLlBYa5djgIPqUdirZi0eWqYMVtEI2TqCRlnV0McuJGfPs0x6uiFI5owz7e9EPGYysOB1dyryjFxOzfIV+D2gLpz1USmXA/eUIX3L63Orj9qzDuWPXCls1nc1PkinBZWfh6vsEWlHpM3UuCyY14TOPuhSAv35HI3Gk3Ybq6QYUPF70ajMcxzvBtrgPmeg9JNGVzE/WCQG7uF9xG4JloVWy4mDa3WqPkVeAovdw6VmcC0caA6rYsYFMcngZ8B2xx5SQjsxx+tYXMoMMn7SYyej/Zz1vsH6Wu+hFgAOsAAOAI2g7PBDNAGDoNB0AUupDoBA/iWBF9ieYVFPIwMBH5DiEmB53c5DreboTDH+97whu+PxczcF5iE/Ad4iv2gN+BzEvkiE9hIQ+AlahdWuUovi/KXs8AfBEhajo9MYCMNgTeJxn/aodKjovybLHApgcDHTGAjDYH3iQna6Q6V6nmDhr/eTwL3JRD4oAlspCGwv3NoIEbFQ3LWDxkfTiDwYyawkYbAchvcGQ6VpomthUUWeBJojyHvO6AuQOBbE3yJFSZwvgX+UDT+OodKctvhLhFGI4m3OshLoteFhNG6wVne8KsoLkwXURETOKcC38kNv4f3bz7OEunCM1lwub/1PiEwUQtej5B3C4teCBE4jWQNmzOBp/BEjgReIwS90hv54p5OB/wVOSEwUQOeD5B3A5goy5rARloLGYt5SZfeIF0G7vVG/hmkOiVJiaUuBAhMTABPCnnXs9gFE9jIai/EFTwp28vjyqkib7IQ5AhYKg8SILDPOoo2hOWXEZj2hd7GkYkoOkxgE9iHohDrWZ6T1TbBft48MzPjV1c8vVQtuMwbfsPXp8bCaCbweH2BsN0h5msCm8AmsGECm8BGbgUupShwyRrVBB5rWr2Rf8klSmBaxl4kKKhXih6yRs0X/wMhEBsJUtVSfgAAAABJRU5ErkJggg=='); button.with-dot:before{
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAQAAABKmM6bAAAAWElEQVQI12P4zwCGIv/V/stC2CCC63/J/5f/f////n/nf3OIUMn/v/9h4M5/NZCWR/8R4N//FgagCd/+I4PVIFVPUFR1oZt1778Bso3fEDZC3CULxFwgNgDirn13m99yDAAAAABJRU5ErkJggg==');
background-size:100% 100%;
display:inline-block;
vertical-align:middle;
width:4px;height:4px;
content:"";
margin-right:10px;
}
footer .icon, #crc32.valid:after{
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAQCAYAAABQrvyxAAACUElEQVRIx9XWzUtUURzG8atp04svtCiI3v6EQFIoInBREFgEQtC+VVCLWoQLMTKqRUEbU4uKaOht5a7IoDcoWkiLIKKVgqUUvkGMM2revid+V55u5+qdS5ANfEDvPXN4npl7zpkgDMMgpgFX8QEF8xG9aPKM/6f0n7W4ifkw+eXu5VG33Aq48G/C9K8B1EeTjLTsdrbjMQqYwn1sw2bkMYlp9GOHe89CiDNBuU5jAOujSe5KsFY8wVe8NaN4hsN4bWP7pIAL9B1hzGcMe667IjszFuhAaN65CfbIJ5tP8bVdl/F7rcBzT8ilvMxQ4JyEd9rdBHck0JUUBc7L+HtWoJShwEyswCasSghegUux8G2/7jHBoCzQDSkKuGd/1t4zZAVGMhQYlQLNmEA/1sTCV6JHgs/jxMJ9JihamEIZq3/M3lO0AmczFLggBYYloCuxWj753lj4Y78VZIJxeSQ2pgi/Dj9s/IQVqEZfGeEfIScFGjAmQZ+iDjfk2hyO/vF4McELKdCZokCbjH8l26gr8TBFeFc059lGXYlxCVyUv2dxxLs+mOC4hXkPt7AuYqsn+BYrOCcFTkkBpwq3Fwn/wIoGCedAIyZji3UGrYk7kx1ig1agXQLul/DNnsPsS3QiSwGnEt2e8LewQscmbKNN8k2UcGjRrdUm2YVp9OAgTqJKCuRi4UtWKvAUcCpwWcJ3WbEgRYHocRpCy5Jng4TcZ4vyk522NXJvpYSfwgF9vDwFIp1ut0m6/7d/zAW2C3XhG2rlerVdu2ZrYVn+Gv0v/QQllGUdmIg+uwAAAABJRU5ErkJggg==');
}
footer .icon{
background-position:0px 0px; background-position:0px 0px;
display:inline-block; display:inline-block;
vertical-align:middle; vertical-align:middle;
width:16px;height:16px width:16px;height:16px
} }
i.icon.reset,i.icon.blank{background-position:-0px 0px} footer .icon.github{background-position:0px 0px}
i.icon.add,i.icon.plus{background-position:-16px 0px} footer .icon.heart{background-position:-16px 0px}
i.icon.remove,i.icon.minus{background-position:-32px 0px} footer .icon.check{background-position:-32px 0px}
i.icon.close{background-position:-48px 0px}
i.icon.config,i.icon.settings{background-position:-64px 0px}
i.icon.help{background-position:-80px 0px}
i.icon.down{background-position:-96px 0px}
i.icon.up{background-position:-112px 0px}
i.icon.sort{background-position:-128px 0px}
i.icon.check,i.icon.accept,i.icon.save{background-position:-144px 0px}
i.icon.pencil,i.icon.edit{background-position:-160px 0px}
i.icon.github{background-position:0px -16px}
i.icon.heart{background-position:-16px -16px}
i.icon.disk{background-position:-32px -16px;width:20px;height:20px}
@ -104,116 +81,82 @@ i.icon.disk{background-position:-32px -16px;width:20px;height:20px}
/* header+footer */
header{text-align:center}
header h1{margin:0}
header img{max-width:90%}
/* header+toolbar+footer */ footer{
#header{ text-align:center;
color:white; color:#767b86;
/*position:fixed;*/ font-size:85%;
top:0;left:0;
width:100%;
z-index:100;
} }
#header-top{ footer a{
background-color:#43454a; color:#969ba6;
padding:16px 0
}
#header h1 img{height:24px; vertical-align:middle}
#header h1{font-size:140%;margin:0;display:inline-block}
#header h1 small{color:#717377;font-size:60%}
#toolbar{margin-top:10px}
.header-buttons{
font-size:85%
}
.header-buttons a{
color:white;
text-decoration:none; text-decoration:none;
border-bottom:1px solid #5d5f63; border-bottom:1px solid #464b56;
margin-right:10px }
footer a:hover{
color:white;
border-color:#41d5ff;
} }
.header-buttons a:hover{border-color:#41d5ff}
hr{border:none;border-top:1px dotted #bbb;margin:15px 0} hr{border:none;border-top:1px dotted #bbb;margin:15px 0}
h3{
/*border-bottom:2px solid #888;*/ /* tabs */
font-size:13px; #tabs div{
padding:10px 0; width:50%;
font-weight:normal; display:inline-block;
text-align:center; text-align:center;
margin: 20px 0 0; box-sizing:border-box;
padding: 10px;
color:#646871;
border-radius: 2px 2px 0 0;
transition: .15s all;
border-top: 3px solid transparent;
}
#tabs div:hover{color:white;background-color:#2e3137}
#tabs div.selected{
background-color:#f9fafa;
color:black; color:black;
} }
h3:before,h3:after{ .tab{background-color:#f9fafa;padding:30px 15px}
content:""; #tab0{border-radius: 0px 2px 2px 2px}
display:inline-block; #tab1{border-radius: 2px 0px 2px 2px;display:none}
width:130px; #tabs div:first-child.selected{border-color: #e74c3c;}
height:1px; #tabs div:last-child.selected{border-color: #25ba84;/*border-color: #3498db;*/}
background-color:red;
vertical-align:middle;
}
h3:before{
margin-right:5px;
background:linear-gradient(to right, transparent 0%,#818181 100%);
}
h3:after{
margin-left:5px;
background:linear-gradient(to left, transparent 0%,#818181 100%);
}
h3.red:before{background:linear-gradient(to right, transparent 0%,#e74c3c 100%)}
h3.red:after{background:linear-gradient(to left, transparent 0%,#e74c3c 100%)}
h3.orange:before{background:linear-gradient(to right, transparent 0%,#f39c12 100%)}
h3.orange:after{background:linear-gradient(to left, transparent 0%,#f39c12 100%)}
h3.blue:before{background:linear-gradient(to right, transparent 0%,#3498db 100%)}
h3.blue:after{background:linear-gradient(to left, transparent 0%,#3498db 100%)}
h3.green:before{background:linear-gradient(to right, transparent 0%,#25ba84 100%)}
h3.green:after{background:linear-gradient(to left, transparent 0%,#25ba84 100%)}
table{width:100%}
tbody tr:nth-child(even){background-color:#f2f2f2}
th{background-color:#d4d4d4}
/* forms */ /* forms */
textarea, input[type=file]{ input[type=file],select{
box-sizing:border-box; box-sizing:border-box;
max-width:100%; max-width:100%;
} font-family:inherit;
input[type=text],input[type=number],select{ font-size:100%;
padding:6px 10px;
outline:0; outline:0;
width:250px; border:none;
max-width:90%; border-radius:3px;
font:14px 'Open Sans', sans-serif; background-color:#edefef;
border:1px solid #191919;
border-radius:4px;
box-sizing:border-box;
background-color:#191919;
color:#f2f2f2;
letter-spacing:.02em;
} }
input[type=text]:hover,input[type=number]:hover,select:hover{ input[type=file]{padding:6px 10px}
background-color:#222; select{
padding:6px 18px 6px 10px;
-webkit-appearance:none;
-moz-appearance:none;
text-overflow:'';
background-image:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZlcnNpb249IjEuMSIgeD0iMTJweCIgeT0iMHB4IiB3aWR0aD0iMjRweCIgaGVpZ2h0PSIzcHgiIHZpZXdCb3g9IjAgMCA2IDMiIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDYgMyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHBvbHlnb24gcG9pbnRzPSI1Ljk5MiwwIDIuOTkyLDMgLTAuMDA4LDAgIi8+PC9zdmc+");
background-position:100% center;
background-repeat:no-repeat;
} }
input[type=text]:focus,input[type=number]:focus,select:focus{ select::-ms-expand{display:none}
border-color:#41d5ff; input[type=file]:hover,select:hover{background-color:#dee1e1}
box-shadow:#35a1c0 0 0 3px;
}
input[type=text].error,input[type=number].error,select.error{
box-shadow:#f88 0 0 3px;
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}
input[type=text].fw,input[type=number].fw,select.fw{width:100% !important;max-width:100% !important}
@ -234,9 +177,6 @@ button{
box-sizing:border-box box-sizing:border-box
} }
button.small{
min-width:1px
}
button:hover{ button:hover{
cursor:pointer; cursor:pointer;
background-color:#6e7177; background-color:#6e7177;
@ -245,9 +185,7 @@ button:active{
background-color:#47494f; background-color:#47494f;
transform:translateY(1px) transform:translateY(1px)
} }
button:disabled{opacity:.35} button:disabled{opacity:.35 !important;cursor:not-allowed}
button.colored{color:white;background-color:#40a2c9}
button.colored:hover{background-color:#63bfe3}
button.close{color:white;background-color:#} button.close{color:white;background-color:#}
button.no-text.with-icon:before{margin-right:0px} button.no-text.with-icon:before{margin-right:0px}
@ -257,14 +195,28 @@ button.no-text.with-icon:before{margin-right:0px}
#rom-info{font-family:'Roboto Mono',monospace;color:#888;font-size:12px}
#rom-info .rightcol{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
#crc32.valid{color:green}
/*#crc32.valid:after{content:" \002713"}*/
#crc32.invalid{color: red}
#crc32.valid:after{
background-position:0px 0px;
display:inline-block;
vertical-align:middle;
width:16px;height:16px;
background-position:-32px 0px;
content:"";
margin-left:4px;
}
#wrapper{
box-sizing:border-box;
.wrapper{ max-width:95%;
max-width:900px; width:600px;
margin:0 auto margin:0 auto
} }
@ -272,17 +224,10 @@ button.no-text.with-icon:before{margin-right:0px}
/* responsive */ /* responsive */
@media only screen and (max-width:961px){ @media only screen and (max-width:641px){
.wrapper{max-width:auto; padding-left:10px;padding-right:10px;} #wrapper{font-size:14px}
} #rom-info{font-size:11px}
@media only screen and (max-width:721px){ header img{max-height:96px}
#header .columns-6,#header .columns.six{width:100%;text-align:center}
h1 img{margin-bottom:15px}
#the-app{font-size:13px}
h3:before,h3:after{width:40px}
}
@media only screen and (max-width:481px){
#the-app{font-size:12px}
} }
@ -308,19 +253,17 @@ button.no-text.with-icon:before{margin-right:0px}
transform:translateY(-10px); transform:translateY(-10px);
transition:visibility 0s .2s, opacity .2s ease-in, transform .2s ease-in; transition:visibility 0s .2s, opacity .2s ease-in, transform .2s ease-in;
background-color:#45484f; background-color:#f9fafa;
padding:15px; padding:15px;
min-width:360px; min-width:360px;
max-width:80%; max-width:80%;
border-radius:4px; border-radius:3px;
box-shadow:0 5px 15px 0 rgba(0,0,0,.5); box-shadow:0 5px 15px 0 rgba(0,0,0,.5);
line-height:1.8; line-height:1.8;
color:#f2f2f2;
} }
.dialog.active{transform:translateY(0px)} .dialog.active{transform:translateY(0px)}
.dialog button{color:#f2f2f2}
.buttons{ .buttons{
margin-top:20px; margin-top:20px;
text-align:center text-align:right
} }

View file

@ -1,4 +1,4 @@
/* RomPatcher.js v20180427 - Marc Robledo 2016-2018 - http://www.marcrobledo.com/license */ /* RomPatcher.js v20180428 - Marc Robledo 2016-2018 - http://www.marcrobledo.com/license */
var MAX_ROM_SIZE=33554432; var MAX_ROM_SIZE=33554432;
var romFile, headeredRomFile, unheaderedRomFile, patch, romFile1, romFile2, tempFile; var romFile, headeredRomFile, unheaderedRomFile, patch, romFile1, romFile2, tempFile;
/* Shortcuts */ /* Shortcuts */
@ -18,7 +18,6 @@ addEvent(window,'load',function(){
el('input-file-patch').value=''; el('input-file-patch').value='';
el('input-file-rom1').value=''; el('input-file-rom1').value='';
el('input-file-rom2').value=''; el('input-file-rom2').value='';
el('input-file-flip').value='';
addEvent(el('input-file-rom'), 'change', function(){ addEvent(el('input-file-rom'), 'change', function(){
romFile=new MarcBinFile(this, function(){ romFile=new MarcBinFile(this, function(){
@ -40,6 +39,7 @@ addEvent(window,'load',function(){
} }
updateChecksums(romFile); updateChecksums(romFile);
validateSource();
}); });
}); });
addEvent(el('input-file-patch'), 'change', function(){ addEvent(el('input-file-patch'), 'change', function(){
@ -67,6 +67,7 @@ addEvent(window,'load',function(){
updateChecksums(romFile); updateChecksums(romFile);
validateSource();
}); });
addEvent(el('checkbox-addheader'), 'change', function(){ addEvent(el('checkbox-addheader'), 'change', function(){
@ -82,7 +83,10 @@ addEvent(window,'load',function(){
else else
romFile=unheaderedRomFile; romFile=unheaderedRomFile;
validateSource();
}); });
setTab(1);
}); });
function isSnesRom(fileName){return /\.(smc|sfc|fig|swc)$/.test(fileName)} function isSnesRom(fileName){return /\.(smc|sfc|fig|swc)$/.test(fileName)}
@ -91,7 +95,7 @@ function isHeadered(fileSize,headerSize){return isPowerOfTwo(fileSize-headerSize
function updateChecksums(file){ function updateChecksums(file){
el('rom-info').style.display='block'; el('rom-info').style.display='flex';
sha1(file); sha1(file);
var crc32str=crc32(file).toString(16); var crc32str=crc32(file).toString(16);
@ -101,6 +105,13 @@ function updateChecksums(file){
el('md5').innerHTML=md5(file); el('md5').innerHTML=md5(file);
} }
function validateSource(){
if(patch && romFile && typeof patch.validateSource !== 'undefined'){
el('crc32').className=patch.validateSource(romFile)?'valid':'invalid';
}else{
el('crc32').className='';
}
}
function _readPatchFile(){ function _readPatchFile(){
@ -119,6 +130,7 @@ function _readPatchFile(){
}*/else { }*/else {
MarcDialogs.alert('Invalid IPS/UPS/APS/BPS file'); MarcDialogs.alert('Invalid IPS/UPS/APS/BPS file');
} }
validateSource();
} }
function openPatchFile(f){tempFile=new MarcBinFile(f, _readPatchFile)} function openPatchFile(f){tempFile=new MarcBinFile(f, _readPatchFile)}
function applyPatchFile(p,r){ function applyPatchFile(p,r){
@ -141,36 +153,51 @@ function applyPatchFile(p,r){
function createPatchFile(){ function createPatchFile(){
var mode=el('patch-type').value;
if(!romFile1 || !romFile2){ if(!romFile1 || !romFile2){
MarcDialogs.alert('No original/modified ROM file specified'); MarcDialogs.alert('No original/modified ROM file specified');
return false; return false;
}else if(el('radio-ips').checked && (romFile1.fileSize>MAX_IPS_SIZE || romFile2.fileSize>MAX_IPS_SIZE)){ }else if(mode==='ips' && (romFile1.fileSize>MAX_IPS_SIZE || romFile2.fileSize>MAX_IPS_SIZE)){
MarcDialogs.alert('Files are too big for IPS format'); MarcDialogs.alert('Files are too big for IPS format');
return false; return false;
} }
var newPatch; var newPatch;
if(el('radio-ips').checked){ if(mode==='ips'){
newPatch=createIPSFromFiles(romFile1, romFile2); newPatch=createIPSFromFiles(romFile1, romFile2);
}else if(el('radio-ups').checked){ }else if(mode==='ups'){
newPatch=createUPSFromFiles(romFile1, romFile2); newPatch=createUPSFromFiles(romFile1, romFile2);
}else if(el('radio-aps').checked){ }else if(mode==='aps'){
newPatch=createAPSFromFiles(romFile1, romFile2, false); newPatch=createAPSFromFiles(romFile1, romFile2, false);
}else if(el('radio-apsn64').checked){ }else if(mode==='apsn64'){
newPatch=createAPSFromFiles(romFile1, romFile2, true); newPatch=createAPSFromFiles(romFile1, romFile2, true);
}/*else if(el('radio-apsgba').checked){ }/*else if(el('radio-apsgba').checked){
newPatch=createAPSGBAFromFiles(romFile1, romFile2); newPatch=createAPSGBAFromFiles(romFile1, romFile2);
}else if(el('radio-bps').checked){ }else if(el('radio-bps').checked){
newPatch=createBPSFromFiles(romFile1, romFile2); newPatch=createBPSFromFiles(romFile1, romFile2);
}*/ }*/
newPatch.export().save(); newPatch.export(romFile2.fileName.replace(/\.[^\.]+$/,'')).save();
} }
function setTab(tab){
for(var i=0; i<2; i++){
if(i===tab){
el('tab'+i).style.display='block';
el('tabs').children[i].className='selected';
}else{
el('tab'+i).style.display=i===tab?'block':'none';
el('tabs').children[i].className='clickable'
}
}
}
/* CRC32/MD5/SHA-1 calculators */ /* CRC32/MD5/SHA-1 calculators */
var HEX_CHR='0123456789abcdef'.split(''); var HEX_CHR='0123456789abcdef'.split('');

View file

@ -12,7 +12,7 @@ limitations under the License.
mod by marcrobledo, original from: https://github.com/GoogleChrome/samples/blob/gh-pages/service-worker/basic/service-worker.js mod by marcrobledo, original from: https://github.com/GoogleChrome/samples/blob/gh-pages/service-worker/basic/service-worker.js
*/ */
const PRECACHE_ID='v20180427b'; const PRECACHE_ID='v20180428';
const PRECACHE_FILES=[ const PRECACHE_FILES=[
'index.html','./', 'index.html','./',
'RomPatcher.css', 'RomPatcher.css',
@ -22,8 +22,7 @@ const PRECACHE_FILES=[
'ips.js', 'ips.js',
'ups.js', 'ups.js',
'aps.js', 'aps.js',
'bps.js', 'bps.js'
'ByteFlipper.js'
]; ];

6
aps.js
View file

@ -1,4 +1,4 @@
/* APS (N64) module for RomPatcher.js v20180427 - Marc Robledo 2017-2018 - http://www.marcrobledo.com/license */ /* APS (N64) module for RomPatcher.js v20180428 - Marc Robledo 2017-2018 - http://www.marcrobledo.com/license */
/* File format specification: https://github.com/btimofeev/UniPatcher/wiki/APS-(N64) */ /* File format specification: https://github.com/btimofeev/UniPatcher/wiki/APS-(N64) */
var RECORD_RLE=0x0000; var RECORD_RLE=0x0000;
@ -38,7 +38,7 @@ APS.prototype.toString=function(){
s+='\nHeader: '+JSON.stringify(this.header); s+='\nHeader: '+JSON.stringify(this.header);
return s return s
} }
APS.prototype.export=function(){ APS.prototype.export=function(fileName){
var patchFileSize=(this.headerType===1)?78:61; var patchFileSize=(this.headerType===1)?78:61;
for(var i=0; i<this.records.length; i++){ for(var i=0; i<this.records.length; i++){
@ -50,7 +50,7 @@ APS.prototype.export=function(){
tempFile=new MarcBinFile(patchFileSize); tempFile=new MarcBinFile(patchFileSize);
tempFile.littleEndian=true; tempFile.littleEndian=true;
tempFile.fileName='patch.aps'; tempFile.fileName=fileName+'.aps';
tempFile.writeString(0, APS_MAGIC, APS_MAGIC.length); tempFile.writeString(0, APS_MAGIC, APS_MAGIC.length);
tempFile.writeByte(5, this.headerType); tempFile.writeByte(5, this.headerType);
tempFile.writeByte(6, this.encodingMethod); tempFile.writeByte(6, this.encodingMethod);

5
bps.js
View file

@ -1,4 +1,4 @@
/* BPS module for RomPatcher.js v20171112 - Marc Robledo 2016-2017 - http://www.marcrobledo.com/license */ /* BPS module for RomPatcher.js v20180428 - Marc Robledo 2016-2018 - http://www.marcrobledo.com/license */
/* File format specification: https://www.romhacking.net/documents/746/ */ /* File format specification: https://www.romhacking.net/documents/746/ */
var BPS_MAGIC='BPS1'; var BPS_MAGIC='BPS1';
@ -28,8 +28,9 @@ BPS.prototype.toString=function(){
/*BPS.prototype.export=function(){ /*BPS.prototype.export=function(){
}*/ }*/
BPS.prototype.validateSource=function(romFile){return this.sourceChecksum===crc32(romFile,false)}
BPS.prototype.apply=function(romFile){ BPS.prototype.apply=function(romFile){
if(this.sourceChecksum!==crc32(romFile,false)){ if(!this.validateSource(romFile)){
MarcDialogs.alert('Error: invalid source ROM checksum'); MarcDialogs.alert('Error: invalid source ROM checksum');
return false; return false;
} }

View file

@ -1,137 +1,110 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<title>RomPatcher.js</title> <title>Rom Patcher 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="A web-based IPS/UPS/APS/BPS ROM patcher."/> <meta name="description" content="A web-based IPS/UPS/APS/BPS ROM patcher."/>
<meta name="keywords" content="ips,ups,aps,bps,patcher,online,html5,rom,patch,hack,translation"/> <meta name="keywords" content="ips,ups,aps,bps,patcher,online,html5,web,online,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="manifest" href="./manifest.json"/>
<link rel="shortcut icon" href="./favicon.png"/> <link rel="shortcut icon" href="./favicon.png"/>
<link type="text/css" rel="stylesheet" href="./RomPatcher.css" media="all"/> <link type="text/css" rel="stylesheet" href="./RomPatcher.css" media="all"/>
<script type="text/javascript" src="./RomPatcher.js"></script> <script type="text/javascript" src="./RomPatcher.js"></script>
<script type="text/javascript" src="./ips.js"></script> <script type="text/javascript" src="./ips.js"></script>
<script type="text/javascript" src="./ups.js"></script> <script type="text/javascript" src="./ups.js"></script>
<script type="text/javascript" src="./aps.js"></script> <script type="text/javascript" src="./aps.js"></script>
<!-- <script type="text/javascript" src="./apsgba.js"></script> -->
<script type="text/javascript" src="./bps.js"></script> <script type="text/javascript" src="./bps.js"></script>
<script type="text/javascript" src="./ByteFlipper.js"></script>
<!-- <script type="text/javascript" src="./apsgba.js"></script> -->
</head> </head>
<body> <body unselectable="on" onselectstart="return false"><div id="column">
<!-- HEADER --> <!-- HEADER -->
<div id="header"> <header><img src="logo.png" /><h1>RomPatcher.js</h1></header>
<div id="header-top">
<div class="row wrapper">
<h1 class="six columns text-left"><img src="logo.png" /><span class="hidden">RomPatcher.js</span></h1>
<div class="six columns header-buttons text-right">
by <a href="/">Marc Robledo</a>
<i class="icon github"></i> <a href="https://github.com/marcrobledo/RomPatcher.js/" target="_blank">See on GitHub</a>
<i class="icon heart"></i> <a href="https://www.paypal.me/marcrobledo/5" target="_blank" rel="nofollow">Donate</a>
</div>
</div>
</div>
</div>
</div>
<!-- APP --> <!-- APP -->
<div class="wrapper" id="the-app"> <div id="wrapper">
<h3 class="red">Apply patch</h3> <div id="tabs"><div class="selected clickable" onclick="setTab(0)">Apply patch</div><div class="clickable" onclick="setTab(1)">Create patch</div></div>
<div class="container-description">Apply a patch to your ROM</div>
<div class="container"> <div id="tab0" class="tab">
<div class="row"> <div class="row">
<div class="six columns text-right"><label for="input-file-rom">ROM file:</label></div> <div class="leftcol"><label for="input-file-rom">ROM file:</label></div>
<div class="six columns"> <div class="rightcol">
<input type="file" id="input-file-rom" /> <input type="file" id="input-file-rom" />
<div id="rom-info" class="mono" style="display:none;font-size:12px">
CRC32: <span id="crc32"></span><br/>
MD5: <span id="md5"></span><br/>
SHA-1: <span id="sha1"></span><br/>
</div>
</div> </div>
</div> </div>
<div class="row" id="rom-info">
<div class="leftcol">CRC32:</div><div class="rightcol"><span id="crc32"></span></div>
<div class="leftcol">MD5:</div><div class="rightcol"><span id="md5"></span></div>
<div class="leftcol">SHA-1:</div><div class="rightcol"><span id="sha1"></span></div>
</div>
<div class="row" id="row-removeheader" style="display:none"> <div class="row" id="row-removeheader" style="display:none">
<div class="six columns text-right"><label for="checkbox-removeheader">Remove header before patching:</label></div> <div class="leftcol"><label for="checkbox-removeheader">Remove header before patching:</label></div>
<div class="six columns"> <div class="rightcol">
<input type="checkbox" id="checkbox-removeheader" /> <input type="checkbox" id="checkbox-removeheader" />
</div> </div>
</div> </div>
<div class="row" id="row-addheader" style="display:none"> <div class="row" id="row-addheader" style="display:none">
<div class="six columns text-right"><label for="checkbox-addheader">Patch needs a headered ROM:</label></div> <div class="leftcol"><label for="checkbox-addheader">Patch needs a headered ROM:</label></div>
<div class="six columns"> <div class="rightcol">
<input type="checkbox" id="checkbox-addheader" /> <input type="checkbox" id="checkbox-addheader" />
</div> </div>
</div> </div>
<div class="row"> <div class="row" title="Compatible formats: IPS, UPS, APS and BPS">
<div class="six columns text-right"><label for="input-file-patch">Patch file <small>(IPS/UPS/APS/BPS)</small>:</label></div> <div class="leftcol"><label for="input-file-patch">Patch file:</label></div>
<div class="six columns"> <div class="rightcol">
<input type="file" id="input-file-patch" /> <input type="file" id="input-file-patch" />
</div> </div>
</div> </div>
<div class="buttons"> <div class="buttons">
<button class="with-icon icon9" onclick="applyPatchFile(patch, romFile)"><i class="icon check"></i> Apply patch</button> <button class="with-dot" onclick="applyPatchFile(patch, romFile)">Apply patch</button>
</div> </div>
</div> </div>
<h3 class="blue">Create patch</h3> <div id="tab1" class="tab">
<div class="container-description">Create a patch from two different ROMs </div>
<div class="container">
<div class="row"> <div class="row">
<div class="six columns text-right"><label for="input-file-rom1">Original ROM:</label></div> <div class="leftcol"><label for="input-file-rom1">Original ROM:</label></div>
<div class="six columns end"> <div class="rightcol">
<input type="file" id="input-file-rom1" /> <input type="file" id="input-file-rom1" />
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="six columns text-right"><label for="input-file-rom2">Modified ROM:</label></div> <div class="leftcol"><label for="input-file-rom2">Modified ROM:</label></div>
<div class="six columns end"> <div class="rightcol">
<input type="file" id="input-file-rom2" /> <input type="file" id="input-file-rom2" />
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="six columns text-right">Patch type</div> <div class="leftcol">Patch type:</div>
<div class="six columns end"> <div class="rightcol">
<input type="radio" id="radio-ips" name="patch-type" checked /><label for="radio-ips">IPS</label> <select id="patch-type">
<input type="radio" id="radio-ups" name="patch-type" /><label for="radio-ups">UPS</label> <option value="ips">IPS</option>
<input type="radio" id="radio-aps" name="patch-type" /><label for="radio-aps">APS</label> <option value="ups">UPS</option>
<input type="radio" id="radio-apsn64" name="patch-type" /><label for="radio-apsn64">APS (N64)</label> <option value="aps">APS</option>
<!-- <input type="radio" id="radio-bps" name="patch-type" /><label for="radio-bps">BPS</label> --> <option value="apsn64">APS (N64)</option>
<!-- <option value="bps">BPS</option> -->
</select>
</div> </div>
</div> </div>
<div class="buttons"> <div class="buttons">
<button class="with-icon icon9" onclick="createPatchFile()"><i class="icon settings"></i> Create patch</button> <button class="with-dot" onclick="createPatchFile()">Create patch</button>
</div>
</div>
<h3 class="green">Byte flipper</h3>
<div class="container-description">This tool flips bytes in a file in order to change its endianness</div>
<div class="container">
<div class="row">
<div class="six columns text-right"><label for="input-file-flip">File</label></div>
<div class="six columns"><input type="file" id="input-file-flip" /></div>
</div>
<div class="row">
<div class="six columns text-right">Bytes to flip</div>
<div class="six columns">
<input type="radio" value="4" id="radio4" name="radio-bytes" checked /><label for="radio4">4 <small>(N64 ROMs and savegames)</small></label>
<input type="radio" value="8" id="radio8" name="radio-bytes" /><label for="radio8">8 <small>(old GBA flashcards)</small></label>
</div>
</div>
<div class="buttons">
<button onclick="flipBytesInFile()" id="button-save"><i class="icon settings"></i> Flip bytes</button>
</div> </div>
</div> </div>
</div> </div>
<!-- FOOTER -->
<footer>
Rom Patcher JS <small>rev 20170428</small> by <a href="/">Marc Robledo</a>
<i class="icon github"></i> <a href="https://github.com/marcrobledo/RomPatcher.js/" target="_blank">See on GitHub</a>
<i class="icon heart"></i> <a href="https://www.paypal.me/marcrobledo/5" target="_blank" rel="nofollow">Donate</a>
</footer>
</div></body></html>
</body></html>

7
ips.js
View file

@ -1,4 +1,4 @@
/* IPS module for RomPatcher.js v20180427 - Marc Robledo 2016-2018 - http://www.marcrobledo.com/license */ /* IPS module for RomPatcher.js v20180428 - Marc Robledo 2016-2018 - http://www.marcrobledo.com/license */
/* File format specification: http://www.smwiki.net/wiki/IPS_file_format */ /* File format specification: http://www.smwiki.net/wiki/IPS_file_format */
var MAX_IPS_SIZE=16777216; var MAX_IPS_SIZE=16777216;
@ -33,7 +33,7 @@ IPS.prototype.toString=function(){
s+='\nTruncate at: 0x'+this.truncate.toString(16); s+='\nTruncate at: 0x'+this.truncate.toString(16);
return s return s
} }
IPS.prototype.export=function(){ IPS.prototype.export=function(fileName){
var binFileSize=0; var binFileSize=0;
binFileSize+=5; //PATCH string binFileSize+=5; //PATCH string
for(var i=0; i<this.records.length; i++){ for(var i=0; i<this.records.length; i++){
@ -48,7 +48,7 @@ IPS.prototype.export=function(){
tempFile=new MarcBinFile(binFileSize); tempFile=new MarcBinFile(binFileSize);
tempFile.littleEndian=false; tempFile.littleEndian=false;
tempFile.fileName='patch.ips'; tempFile.fileName=fileName+'.ips';
tempFile.writeString(0, 'PATCH', 5); tempFile.writeString(0, 'PATCH', 5);
var seek=5; var seek=5;
for(var i=0; i<this.records.length; i++){ for(var i=0; i<this.records.length; i++){
@ -75,6 +75,7 @@ IPS.prototype.export=function(){
return tempFile return tempFile
} }
IPS.prototype.validateInput=function(romFile){return '?'}
IPS.prototype.apply=function(romFile){ IPS.prototype.apply=function(romFile){
var newFileSize=romFile.fileSize; var newFileSize=romFile.fileSize;
for(var i=0; i<this.records.length; i++){ for(var i=0; i<this.records.length; i++){

BIN
logo.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

BIN
logo192.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

17
manifest.json Normal file
View file

@ -0,0 +1,17 @@
{
"short_name":"Rom Patcher JS",
"name":"Rom Patcher JS",
"icons":[
{
"src": "logo192.png",
"sizes": "192x192",
"type": "image/png",
"density": "1.0"
}
],
"start_url": "index.html",
"display": "standalone",
"orientation": "portrait",
"theme_color": "#31343a",
"background_color": "#31343a"
}

9
ups.js
View file

@ -1,4 +1,4 @@
/* UPS module for RomPatcher.js v20180427 - Marc Robledo 2017-2018 - http://www.marcrobledo.com/license */ /* UPS module for RomPatcher.js v20180428 - Marc Robledo 2017-2018 - http://www.marcrobledo.com/license */
/* File format specification: http://www.romhacking.net/documents/392/ */ /* File format specification: http://www.romhacking.net/documents/392/ */
var UPS_MAGIC='UPS1'; var UPS_MAGIC='UPS1';
@ -21,7 +21,7 @@ UPS.prototype.toString=function(){
s+='\nOutput file checksum: '+this.checksumOutput; s+='\nOutput file checksum: '+this.checksumOutput;
return s return s
} }
UPS.prototype.export=function(){ UPS.prototype.export=function(fileName){
var encodedSizeInput=encodeVLV(this.sizeInput); var encodedSizeInput=encodeVLV(this.sizeInput);
var encodedSizeOutput=encodeVLV(this.sizeOutput); var encodedSizeOutput=encodeVLV(this.sizeOutput);
var encodedRecords=[]; var encodedRecords=[];
@ -38,7 +38,7 @@ UPS.prototype.export=function(){
tempFile=new MarcBinFile(binFileSize); tempFile=new MarcBinFile(binFileSize);
tempFile.littleEndian=false; tempFile.littleEndian=false;
tempFile.fileName='patch.ups'; tempFile.fileName=fileName+'.ups';
tempFile.writeString(0, UPS_MAGIC, UPS_MAGIC.length); tempFile.writeString(0, UPS_MAGIC, UPS_MAGIC.length);
tempFile.writeBytes(4, encodedSizeInput); tempFile.writeBytes(4, encodedSizeInput);
@ -60,8 +60,9 @@ UPS.prototype.export=function(){
return tempFile return tempFile
} }
UPS.prototype.validateSource=function(romFile){return crc32(romFile)===this.checksumInput}
UPS.prototype.apply=function(romFile){ UPS.prototype.apply=function(romFile){
if(crc32(romFile)!==this.checksumInput){ if(!this.validateSource(romFile)){
MarcDialogs.alert('Error: invalid input ROM checksum'); MarcDialogs.alert('Error: invalid input ROM checksum');
return false; return false;
} }