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.
**Features:**
@ -11,3 +11,4 @@ A ROM patcher made in HTML5.
* shows ROM CRC32, MD5 and SHA-1 before patching
* can remove SNES headers before patching
* 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/ */
/* @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{
margin:0;
font:14px 'Open Sans',sans-serif;
font:15px 'Open Sans',sans-serif;
cursor:default;
margin:0px 0 40px;
background-color:#f1f2f3;
color:#3c3c3c
line-height:1.8;
background-color:#31343a;
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 */
.row{
display:flex;
@ -21,81 +43,36 @@ body{
align-items:center; /* vertical align */
justify-content:space-between
}
.columns.c1,.columns.one{width:7.33333%}
.columns.c2,.columns.two{width:15.66667%}
.columns.c3,.columns.three{width:24%}
.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}
.leftcol{width:35%;text-align:right}
.rightcol{width:63%}
.leftcol,.rightcol{margin-bottom:8px}
.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{
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==');
/* icons */
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;
display:inline-block;
vertical-align:middle;
width:16px;height:16px
}
i.icon.reset,i.icon.blank{background-position:-0px 0px}
i.icon.add,i.icon.plus{background-position:-16px 0px}
i.icon.remove,i.icon.minus{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}
footer .icon.github{background-position:0px 0px}
footer .icon.heart{background-position:-16px 0px}
footer .icon.check{background-position:-32px 0px}
@ -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 */
#header{
color:white;
/*position:fixed;*/
top:0;left:0;
width:100%;
z-index:100;
footer{
text-align:center;
color:#767b86;
font-size:85%;
}
#header-top{
background-color:#43454a;
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;
footer a{
color:#969ba6;
text-decoration:none;
border-bottom:1px solid #5d5f63;
margin-right:10px
border-bottom:1px solid #464b56;
}
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}
h3{
/*border-bottom:2px solid #888;*/
font-size:13px;
padding:10px 0;
font-weight:normal;
/* tabs */
#tabs div{
width:50%;
display:inline-block;
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;
}
h3:before,h3:after{
content:"";
display:inline-block;
width:130px;
height:1px;
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}
.tab{background-color:#f9fafa;padding:30px 15px}
#tab0{border-radius: 0px 2px 2px 2px}
#tab1{border-radius: 2px 0px 2px 2px;display:none}
#tabs div:first-child.selected{border-color: #e74c3c;}
#tabs div:last-child.selected{border-color: #25ba84;/*border-color: #3498db;*/}
/* forms */
textarea, input[type=file]{
input[type=file],select{
box-sizing:border-box;
max-width:100%;
}
input[type=text],input[type=number],select{
padding:6px 10px;
font-family:inherit;
font-size:100%;
outline:0;
width:250px;
max-width:90%;
font:14px 'Open Sans', sans-serif;
border:1px solid #191919;
border-radius:4px;
box-sizing:border-box;
background-color:#191919;
color:#f2f2f2;
letter-spacing:.02em;
border:none;
border-radius:3px;
background-color:#edefef;
}
input[type=text]:hover,input[type=number]:hover,select:hover{
background-color:#222;
input[type=file]{padding:6px 10px}
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{
border-color:#41d5ff;
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}
select::-ms-expand{display:none}
input[type=file]:hover,select:hover{background-color:#dee1e1}
@ -234,9 +177,6 @@ button{
box-sizing:border-box
}
button.small{
min-width:1px
}
button:hover{
cursor:pointer;
background-color:#6e7177;
@ -245,9 +185,7 @@ button:active{
background-color:#47494f;
transform:translateY(1px)
}
button:disabled{opacity:.35}
button.colored{color:white;background-color:#40a2c9}
button.colored:hover{background-color:#63bfe3}
button:disabled{opacity:.35 !important;cursor:not-allowed}
button.close{color:white;background-color:#}
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{
max-width:900px;
#wrapper{
box-sizing:border-box;
max-width:95%;
width:600px;
margin:0 auto
}
@ -272,17 +224,10 @@ button.no-text.with-icon:before{margin-right:0px}
/* responsive */
@media only screen and (max-width:961px){
.wrapper{max-width:auto; padding-left:10px;padding-right:10px;}
}
@media only screen and (max-width:721px){
#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}
@media only screen and (max-width:641px){
#wrapper{font-size:14px}
#rom-info{font-size:11px}
header img{max-height:96px}
}
@ -308,19 +253,17 @@ button.no-text.with-icon:before{margin-right:0px}
transform:translateY(-10px);
transition:visibility 0s .2s, opacity .2s ease-in, transform .2s ease-in;
background-color:#45484f;
background-color:#f9fafa;
padding:15px;
min-width:360px;
max-width:80%;
border-radius:4px;
border-radius:3px;
box-shadow:0 5px 15px 0 rgba(0,0,0,.5);
line-height:1.8;
color:#f2f2f2;
}
.dialog.active{transform:translateY(0px)}
.dialog button{color:#f2f2f2}
.buttons{
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 romFile, headeredRomFile, unheaderedRomFile, patch, romFile1, romFile2, tempFile;
/* Shortcuts */
@ -18,7 +18,6 @@ addEvent(window,'load',function(){
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(){
@ -40,6 +39,7 @@ addEvent(window,'load',function(){
}
updateChecksums(romFile);
validateSource();
});
});
addEvent(el('input-file-patch'), 'change', function(){
@ -67,6 +67,7 @@ addEvent(window,'load',function(){
updateChecksums(romFile);
validateSource();
});
addEvent(el('checkbox-addheader'), 'change', function(){
@ -82,7 +83,10 @@ addEvent(window,'load',function(){
else
romFile=unheaderedRomFile;
validateSource();
});
setTab(1);
});
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){
el('rom-info').style.display='block';
el('rom-info').style.display='flex';
sha1(file);
var crc32str=crc32(file).toString(16);
@ -101,6 +105,13 @@ function updateChecksums(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(){
@ -119,6 +130,7 @@ function _readPatchFile(){
}*/else {
MarcDialogs.alert('Invalid IPS/UPS/APS/BPS file');
}
validateSource();
}
function openPatchFile(f){tempFile=new MarcBinFile(f, _readPatchFile)}
function applyPatchFile(p,r){
@ -141,36 +153,51 @@ function applyPatchFile(p,r){
function createPatchFile(){
var mode=el('patch-type').value;
if(!romFile1 || !romFile2){
MarcDialogs.alert('No original/modified ROM file specified');
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');
return false;
}
var newPatch;
if(el('radio-ips').checked){
if(mode==='ips'){
newPatch=createIPSFromFiles(romFile1, romFile2);
}else if(el('radio-ups').checked){
}else if(mode==='ups'){
newPatch=createUPSFromFiles(romFile1, romFile2);
}else if(el('radio-aps').checked){
}else if(mode==='aps'){
newPatch=createAPSFromFiles(romFile1, romFile2, false);
}else if(el('radio-apsn64').checked){
}else if(mode==='apsn64'){
newPatch=createAPSFromFiles(romFile1, romFile2, true);
}/*else if(el('radio-apsgba').checked){
newPatch=createAPSGBAFromFiles(romFile1, romFile2);
}else if(el('radio-bps').checked){
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 */
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
*/
const PRECACHE_ID='v20180427b';
const PRECACHE_ID='v20180428';
const PRECACHE_FILES=[
'index.html','./',
'RomPatcher.css',
@ -22,8 +22,7 @@ const PRECACHE_FILES=[
'ips.js',
'ups.js',
'aps.js',
'bps.js',
'ByteFlipper.js'
'bps.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) */
var RECORD_RLE=0x0000;
@ -38,7 +38,7 @@ APS.prototype.toString=function(){
s+='\nHeader: '+JSON.stringify(this.header);
return s
}
APS.prototype.export=function(){
APS.prototype.export=function(fileName){
var patchFileSize=(this.headerType===1)?78:61;
for(var i=0; i<this.records.length; i++){
@ -50,7 +50,7 @@ APS.prototype.export=function(){
tempFile=new MarcBinFile(patchFileSize);
tempFile.littleEndian=true;
tempFile.fileName='patch.aps';
tempFile.fileName=fileName+'.aps';
tempFile.writeString(0, APS_MAGIC, APS_MAGIC.length);
tempFile.writeByte(5, this.headerType);
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/ */
var BPS_MAGIC='BPS1';
@ -28,8 +28,9 @@ BPS.prototype.toString=function(){
/*BPS.prototype.export=function(){
}*/
BPS.prototype.validateSource=function(romFile){return this.sourceChecksum===crc32(romFile,false)}
BPS.prototype.apply=function(romFile){
if(this.sourceChecksum!==crc32(romFile,false)){
if(!this.validateSource(romFile)){
MarcDialogs.alert('Error: invalid source ROM checksum');
return false;
}

View file

@ -1,137 +1,110 @@
<!DOCTYPE html>
<html>
<head>
<title>RomPatcher.js</title>
<title>Rom Patcher JS</title>
<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="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"/>
<link rel="manifest" href="./manifest.json"/>
<link rel="shortcut icon" href="./favicon.png"/>
<link type="text/css" rel="stylesheet" href="./RomPatcher.css" media="all"/>
<script type="text/javascript" src="./RomPatcher.js"></script>
<script type="text/javascript" src="./ips.js"></script>
<script type="text/javascript" src="./ups.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="./ByteFlipper.js"></script>
<!-- <script type="text/javascript" src="./apsgba.js"></script> -->
</head>
<body>
<body unselectable="on" onselectstart="return false"><div id="column">
<!-- HEADER -->
<div id="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>
<header><img src="logo.png" /><h1>RomPatcher.js</h1></header>
<!-- APP -->
<div class="wrapper" id="the-app">
<h3 class="red">Apply patch</h3>
<div class="container-description">Apply a patch to your ROM</div>
<div class="container">
<div id="wrapper">
<div id="tabs"><div class="selected clickable" onclick="setTab(0)">Apply patch</div><div class="clickable" onclick="setTab(1)">Create patch</div></div>
<div id="tab0" class="tab">
<div class="row">
<div class="six columns text-right"><label for="input-file-rom">ROM file:</label></div>
<div class="six columns">
<div class="leftcol"><label for="input-file-rom">ROM file:</label></div>
<div class="rightcol">
<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 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="six columns text-right"><label for="checkbox-removeheader">Remove header before patching:</label></div>
<div class="six columns">
<div class="leftcol"><label for="checkbox-removeheader">Remove header before patching:</label></div>
<div class="rightcol">
<input type="checkbox" id="checkbox-removeheader" />
</div>
</div>
<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="six columns">
<div class="leftcol"><label for="checkbox-addheader">Patch needs a headered ROM:</label></div>
<div class="rightcol">
<input type="checkbox" id="checkbox-addheader" />
</div>
</div>
<div class="row">
<div class="six columns text-right"><label for="input-file-patch">Patch file <small>(IPS/UPS/APS/BPS)</small>:</label></div>
<div class="six columns">
<div class="row" title="Compatible formats: IPS, UPS, APS and BPS">
<div class="leftcol"><label for="input-file-patch">Patch file:</label></div>
<div class="rightcol">
<input type="file" id="input-file-patch" />
</div>
</div>
<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>
<h3 class="blue">Create patch</h3>
<div class="container-description">Create a patch from two different ROMs </div>
<div class="container">
<div id="tab1" class="tab">
<div class="row">
<div class="six columns text-right"><label for="input-file-rom1">Original ROM:</label></div>
<div class="six columns end">
<div class="leftcol"><label for="input-file-rom1">Original ROM:</label></div>
<div class="rightcol">
<input type="file" id="input-file-rom1" />
</div>
</div>
<div class="row">
<div class="six columns text-right"><label for="input-file-rom2">Modified ROM:</label></div>
<div class="six columns end">
<div class="leftcol"><label for="input-file-rom2">Modified ROM:</label></div>
<div class="rightcol">
<input type="file" id="input-file-rom2" />
</div>
</div>
<div class="row">
<div class="six columns text-right">Patch type</div>
<div class="six columns end">
<input type="radio" id="radio-ips" name="patch-type" checked /><label for="radio-ips">IPS</label>
<input type="radio" id="radio-ups" name="patch-type" /><label for="radio-ups">UPS</label>
<input type="radio" id="radio-aps" name="patch-type" /><label for="radio-aps">APS</label>
<input type="radio" id="radio-apsn64" name="patch-type" /><label for="radio-apsn64">APS (N64)</label>
<!-- <input type="radio" id="radio-bps" name="patch-type" /><label for="radio-bps">BPS</label> -->
<div class="leftcol">Patch type:</div>
<div class="rightcol">
<select id="patch-type">
<option value="ips">IPS</option>
<option value="ups">UPS</option>
<option value="aps">APS</option>
<option value="apsn64">APS (N64)</option>
<!-- <option value="bps">BPS</option> -->
</select>
</div>
</div>
<div class="buttons">
<button class="with-icon icon9" onclick="createPatchFile()"><i class="icon settings"></i> 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>
<button class="with-dot" onclick="createPatchFile()">Create patch</button>
</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>
</body></html>
</div></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 */
var MAX_IPS_SIZE=16777216;
@ -33,7 +33,7 @@ IPS.prototype.toString=function(){
s+='\nTruncate at: 0x'+this.truncate.toString(16);
return s
}
IPS.prototype.export=function(){
IPS.prototype.export=function(fileName){
var binFileSize=0;
binFileSize+=5; //PATCH string
for(var i=0; i<this.records.length; i++){
@ -48,7 +48,7 @@ IPS.prototype.export=function(){
tempFile=new MarcBinFile(binFileSize);
tempFile.littleEndian=false;
tempFile.fileName='patch.ips';
tempFile.fileName=fileName+'.ips';
tempFile.writeString(0, 'PATCH', 5);
var seek=5;
for(var i=0; i<this.records.length; i++){
@ -75,6 +75,7 @@ IPS.prototype.export=function(){
return tempFile
}
IPS.prototype.validateInput=function(romFile){return '?'}
IPS.prototype.apply=function(romFile){
var newFileSize=romFile.fileSize;
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/ */
var UPS_MAGIC='UPS1';
@ -21,7 +21,7 @@ UPS.prototype.toString=function(){
s+='\nOutput file checksum: '+this.checksumOutput;
return s
}
UPS.prototype.export=function(){
UPS.prototype.export=function(fileName){
var encodedSizeInput=encodeVLV(this.sizeInput);
var encodedSizeOutput=encodeVLV(this.sizeOutput);
var encodedRecords=[];
@ -38,7 +38,7 @@ UPS.prototype.export=function(){
tempFile=new MarcBinFile(binFileSize);
tempFile.littleEndian=false;
tempFile.fileName='patch.ups';
tempFile.fileName=fileName+'.ups';
tempFile.writeString(0, UPS_MAGIC, UPS_MAGIC.length);
tempFile.writeBytes(4, encodedSizeInput);
@ -60,8 +60,9 @@ UPS.prototype.export=function(){
return tempFile
}
UPS.prototype.validateSource=function(romFile){return crc32(romFile)===this.checksumInput}
UPS.prototype.apply=function(romFile){
if(crc32(romFile)!==this.checksumInput){
if(!this.validateSource(romFile)){
MarcDialogs.alert('Error: invalid input ROM checksum');
return false;
}