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

- feat (web): dynamic JS modules loading

- feat (web): added onloadpatch event
- fix (cli): fixed path for JS files
This commit is contained in:
Marc Robledo 2024-08-11 21:30:25 +02:00
parent bc82f6d748
commit de2f0e5c7d
9 changed files with 315 additions and 210 deletions

View file

@ -25,7 +25,7 @@ A ROM patcher made in Javascript.
Modders and hackers can embed Rom Patcher JS in their websites to provide an online ROM patcher for their patches, allowing users to patch ROMs without downloading any files.<br/>
- File [`index_template.html`](https://github.com/marcrobledo/RomPatcher.js/blob/master/index_template.html) includes a simple working example
- Read [the wiki](https://github.com/marcrobledo/RomPatcher.js/wiki) for more detailed instructions
- Read [the wiki](https://github.com/marcrobledo/RomPatcher.js/wiki/Embedding-Rom-Patcher-JS) for more detailed instructions
&nbsp;
@ -36,7 +36,7 @@ Install dependencies:
Patch a ROM:
> node index.js patch "my_rom.bin" "my_patch.ips"
Create a patch:
Create an IPS patch:
> node index.js create "original_rom.bin" modified_rom.bin"
Show all options:

View file

@ -6,7 +6,7 @@
*/
var PRECACHE_ID = 'rom-patcher-js';
var PRECACHE_VERSION = 'v30beta1b';
var PRECACHE_VERSION = 'v30rc1';
var PRECACHE_URLS = [
'/RomPatcher.js/', '/RomPatcher.js/index.html',
'/RomPatcher.js/manifest.json',

View file

@ -34,21 +34,6 @@
<link type="text/css" rel="stylesheet" href="./webapp/style.css" media="all"/>
<script type="text/javascript" src="./rom-patcher-js/modules/BinFile.js"></script>
<script type="text/javascript" src="./rom-patcher-js/modules/HashCalculator.js"></script>
<script type="text/javascript" src="./rom-patcher-js/modules/RomPatcher.format.ips.js"></script>
<script type="text/javascript" src="./rom-patcher-js/modules/RomPatcher.format.ups.js"></script>
<script type="text/javascript" src="./rom-patcher-js/modules/RomPatcher.format.aps_n64.js"></script>
<script type="text/javascript" src="./rom-patcher-js/modules/RomPatcher.format.aps_gba.js"></script>
<script type="text/javascript" src="./rom-patcher-js/modules/RomPatcher.format.bps.js"></script>
<script type="text/javascript" src="./rom-patcher-js/modules/RomPatcher.format.rup.js"></script>
<script type="text/javascript" src="./rom-patcher-js/modules/RomPatcher.format.ppf.js"></script>
<script type="text/javascript" src="./rom-patcher-js/modules/RomPatcher.format.pmsr.js"></script>
<script type="text/javascript" src="./rom-patcher-js/modules/RomPatcher.format.vcdiff.js"></script>
<script type="text/javascript" src="./rom-patcher-js/modules/zip.js/zip.min.js"></script>
<script type="text/javascript" src="./rom-patcher-js/RomPatcher.js"></script>
<script type="text/javascript" src="./rom-patcher-js/RomPatcher.webapp.js"></script>
<script type="text/javascript" src="./webapp/webapp.js"></script>
</head>
@ -67,7 +52,7 @@
<div class="row m-b" id="rom-patcher-row-file-rom">
<div class="text-right"><label for="rom-patcher-input-file-rom" data-localize="yes">ROM file:</label></div>
<div class="rom-patcher-container-input">
<input type="file" id="rom-patcher-input-file-rom" class="w100 empty" />
<input type="file" id="rom-patcher-input-file-rom" class="empty" disabled />
</div>
</div>
<div class="row m-b" id="rom-patcher-row-alter-header">
@ -98,7 +83,7 @@
<div class="row m-b" id="rom-patcher-row-file-patch">
<div class="text-right"><label for="rom-patcher-input-file-patch" data-localize="yes">Patch file:</label></div>
<div class="rom-patcher-container-input">
<input type="file" id="rom-patcher-input-file-patch" class="w100 empty" accept=".ips,.ups,.bps,.aps,.rup,.ppf,.mod,.xdelta,.vcdiff,.zip" />
<input type="file" id="rom-patcher-input-file-patch" class="empty" accept=".ips,.ups,.bps,.aps,.rup,.ppf,.mod,.xdelta,.vcdiff,.zip" disabled />
</div>
</div>
<div class="row m-b" id="rom-patcher-row-patch-description">
@ -120,21 +105,21 @@
<div class="row m-b">
<div class="text-right"><label for="patch-builder-input-file-original" data-localize="yes" >Original ROM:</label></div>
<div>
<input type="file" id="patch-builder-input-file-original" class="w100 empty" />
<input type="file" id="patch-builder-input-file-original" class="empty" disabled />
</div>
</div>
<div class="row m-b">
<div class="text-right"><label for="patch-builder-input-file-modified" data-localize="yes">Modified ROM:</label></div>
<div>
<input type="file" id="patch-builder-input-file-modified" class="w100 empty" />
<input type="file" id="patch-builder-input-file-modified" class="empty" disabled />
</div>
</div>
<div class="row m-b">
<div class="text-right"><label for="patch-builder-select-patch-type" data-localize="yes">Patch type:</label></div>
<div>
<select id="patch-builder-select-patch-type">
<select id="patch-builder-select-patch-type" disabled>
<option value="ips">IPS</option>
<option value="bps">BPS</option>
<option value="ppf">PPF</option>
@ -147,7 +132,7 @@
<div class="buttons text-center">
<div id="patch-builder-row-error-message" class="m-b"><span id="patch-builder-error-message"></span></div>
<button id="patch-builder-button-create" disabled data-localize="yes">Create patch</button>
<button id="patch-builder-button-create" disabled data-localize="yes" disabled>Create patch</button>
</div>
</div>
</div>
@ -166,7 +151,7 @@
<button id="button-settings" class="btn-transparent"><img src="./webapp/icon_settings.svg" loading="lazy" class="icon settings" /> <span data-localize="yes">Settings</span></button>
</div>
Rom Patcher JS <small>v3.0 <a style="color:rgb(255, 197, 7)" href="legacy/" rel="nofollow">Beta 1</a></small> by <a href="/">Marc Robledo</a>
Rom Patcher JS <small>v3.0 <a style="color:rgb(255, 197, 7)" href="legacy/" rel="nofollow">RC1</a></small> by <a href="/">Marc Robledo</a>
<br />
<img src="./webapp/icon_github.svg" loading="lazy" class="icon github" /> <a href="https://github.com/marcrobledo/RomPatcher.js/" target="_blank">See on GitHub</a>
<img src="./webapp/icon_heart.svg" loading="lazy" class="icon heart" /> <a href="https://www.paypal.me/marcrobledo/5" target="_blank" rel="nofollow">Donate</a>

View file

@ -10,22 +10,9 @@
<!-- Rom Patcher JS needed CSS/JS files -->
<link type="text/css" rel="stylesheet" href="./rom-patcher-js/style.css" media="all" />
<script type="text/javascript" src="./rom-patcher-js/modules/BinFile.js"></script>
<script type="text/javascript" src="./rom-patcher-js/modules/HashCalculator.js"></script>
<script type="text/javascript" src="./rom-patcher-js/modules/RomPatcher.format.ips.js"></script>
<script type="text/javascript" src="./rom-patcher-js/modules/RomPatcher.format.ups.js"></script>
<script type="text/javascript" src="./rom-patcher-js/modules/RomPatcher.format.aps_n64.js"></script>
<script type="text/javascript" src="./rom-patcher-js/modules/RomPatcher.format.aps_gba.js"></script>
<script type="text/javascript" src="./rom-patcher-js/modules/RomPatcher.format.bps.js"></script>
<script type="text/javascript" src="./rom-patcher-js/modules/RomPatcher.format.rup.js"></script>
<script type="text/javascript" src="./rom-patcher-js/modules/RomPatcher.format.ppf.js"></script>
<script type="text/javascript" src="./rom-patcher-js/modules/RomPatcher.format.pmsr.js"></script>
<script type="text/javascript" src="./rom-patcher-js/modules/RomPatcher.format.vcdiff.js"></script>
<script type="text/javascript" src="./rom-patcher-js/modules/zip.js/zip.min.js"></script>
<script type="text/javascript" src="./rom-patcher-js/RomPatcher.js"></script>
<script type="text/javascript" src="./rom-patcher-js/RomPatcher.webapp.js"></script>
<!-- Rom Patcher JS initizlization -->
<!-- Rom Patcher JS initialization -->
<script type="text/javascript">
window.addEventListener('load', function (evt) {
try {
@ -70,7 +57,7 @@
<div class="rom-patcher-row margin-bottom" id="rom-patcher-row-file-rom">
<div class="text-right"><label for="rom-patcher-input-file-rom" data-localize="yes">ROM file:</label></div>
<div class="rom-patcher-container-input">
<input type="file" id="rom-patcher-input-file-rom" class="empty" />
<input type="file" id="rom-patcher-input-file-rom" class="empty" disabled />
</div>
</div>
<div class="margin-bottom text-selectable text-mono text-muted" id="rom-patcher-rom-info">
@ -97,7 +84,7 @@
</div>
<div class="rom-patcher-container-input">
<input type="file" id="rom-patcher-input-file-patch" class="empty"
accept=".ips,.ups,.bps,.aps,.rup,.ppf,.mod,.xdelta,.vcdiff,.zip" />
accept=".ips,.ups,.bps,.aps,.rup,.ppf,.mod,.xdelta,.vcdiff,.zip" disabled />
</div>
</div>
<div class="rom-patcher-row margin-bottom" id="rom-patcher-row-patch-description">

View file

@ -31,14 +31,29 @@
/*
to-do list:
- load JS modules dynamically when RomPatchwerWeb is initialized
- allow multiple instances of RomPatcherWeb
- allow multiple instances of RomPatcherWeb?
- switch to ES6 classes and modules?
*/
const ROM_PATCHER_JS_PATH = './rom-patcher-js/';
var RomPatcherWeb = (function () {
const SCRIPT_DEPENDENCIES = [
'modules/BinFile.js',
'modules/HashCalculator.js',
'modules/RomPatcher.format.ips.js',
'modules/RomPatcher.format.ups.js',
'modules/RomPatcher.format.aps_n64.js',
'modules/RomPatcher.format.aps_gba.js',
'modules/RomPatcher.format.bps.js',
'modules/RomPatcher.format.rup.js',
'modules/RomPatcher.format.ppf.js',
'modules/RomPatcher.format.pmsr.js',
'modules/RomPatcher.format.vcdiff.js',
'modules/zip.js/zip.min.js',
'RomPatcher.js'
];
const WEB_CRYPTO_AVAILABLE = window.crypto && window.crypto.subtle && window.crypto.subtle.digest;
const settings = {
language: typeof navigator.language === 'string' ? navigator.language.substring(0, 2) : 'en',
@ -49,6 +64,7 @@ var RomPatcherWeb = (function () {
onloadrom: null,
onvalidaterom: null,
onloadpatch: null,
onpatch: null
};
var romFile, patch;
@ -384,10 +400,6 @@ var RomPatcherWeb = (function () {
webWorkerCrc.onerror = event => { // listen for events from the worker
_setToastError('webWorkerCrc error: ' + event.message);
};
/* zip-js web worker */
zip.useWebWorkers = true;
zip.workerScriptsPath = ROM_PATCHER_JS_PATH + 'modules/zip.js/';
const _getChecksumStartOffset = function () {
if (romFile) {
@ -402,12 +414,177 @@ var RomPatcherWeb = (function () {
}
const _getScriptPath = function () {
const currentScripts = document.querySelectorAll('script');
var scriptPath;
if (document.currentScript) {
scriptPath = document.currentScript.src;
} else {
for (var i = 0; i < currentScripts.length; i++) {
if (currentScripts[i].src.indexOf('RomPatcher.webapp.js') !== -1) {
scriptPath = currentScripts[i].src;
break;
}
}
if (!scriptPath)
scriptPath = './rom-patcher-js/';
}
return scriptPath.substring(0, scriptPath.lastIndexOf('/') + 1);
}
const _getMissingDependencies = function () {
const currentScripts = document.querySelectorAll('script');
const scriptPath = _getScriptPath();
var missingDependencies = [];
for (var i = 0; i < SCRIPT_DEPENDENCIES.length && !isLoaded; i++) {
var isLoaded = false;
for (var j = 0; j < currentScripts.length; j++) {
if (currentScripts[j].src === scriptPath + SCRIPT_DEPENDENCIES[i])
isLoaded = true;
}
if (!isLoaded)
missingDependencies.push(scriptPath + SCRIPT_DEPENDENCIES[i]);
}
return missingDependencies;
}
const _initialize = function (newSettings, embededPatchInfo) {
/* embeded patches */
var validEmbededPatch = false;
if (embededPatchInfo) {
if (typeof embededPatchInfo === 'string')
embededPatchInfo = { file: embededPatchInfo };
if (typeof embededPatchInfo === 'object') {
if (typeof embededPatchInfo.file === 'string') {
validEmbededPatch = true;
} else {
throw new Error('Rom Patcher JS: invalid embeded patch file');
}
}
}
/* check if Rom Patcher JS core is available */
if (typeof RomPatcher !== 'object') {
throw new Error('Rom Patcher JS: core not found');
}
/* check if zip-js web worker is available */
if (typeof zip !== 'object' || typeof zip.useWebWorkers !== 'boolean') {
throw new Error('Rom Patcher JS: zip.js web worker not found');
}
zip.useWebWorkers = true;
zip.workerScriptsPath = ROM_PATCHER_JS_PATH + 'modules/zip.js/';
/* check if all required HTML elements are in DOM */
const htmlInputFileRom = htmlElements.get('input-file-rom');
if (htmlInputFileRom && htmlInputFileRom.tagName === 'INPUT' && htmlInputFileRom.type === 'file') {
htmlInputFileRom.addEventListener('change', function (evt) {
htmlElements.disableAll();
new BinFile(this, RomPatcherWeb.provideRomFile);
});
} else {
throw new Error('Rom Patcher JS: input#rom-patcher-input-file-rom[type=file] not found');
}
const htmlInputFilePatch = htmlElements.get('input-file-patch');
if (htmlInputFilePatch && htmlInputFilePatch.tagName === 'INPUT' && htmlInputFilePatch.type === 'file') {
htmlInputFilePatch.addEventListener('change', function (evt) {
htmlElements.disableAll();
new BinFile(this, RomPatcherWeb.providePatchFile);
});
} else {
throw new Error('Rom Patcher JS: input#rom-patcher-input-file-patch[type=file] not found');
}
const htmlButtonApply = htmlElements.get('button-apply');
if (htmlButtonApply && htmlButtonApply.tagName === 'BUTTON') {
htmlButtonApply.addEventListener('click', RomPatcherWeb.applyPatch);
} else {
throw new Error('Rom Patcher JS: button#rom-patcher-button-apply not found');
}
const htmlCheckboxAlterHeader = htmlElements.get('checkbox-alter-header');
if (htmlCheckboxAlterHeader && htmlCheckboxAlterHeader.tagName === 'INPUT' && htmlCheckboxAlterHeader.type === 'checkbox') {
htmlCheckboxAlterHeader.addEventListener('change', function (evt) {
if (!romFile)
return false;
const headerInfo = RomPatcher.isRomHeadered(romFile);
if (headerInfo) {
htmlElements.disableAll();
webWorkerCrc.postMessage({ u8array: romFile._u8array, fileName: romFile.fileName, checksumStartOffset: _getChecksumStartOffset() }, [romFile._u8array.buffer]);
}
});
}
/* set all default input status just in case HTML is wrong */
htmlElements.disableAll();
/* reset input files */
htmlElements.setValue('input-file-rom', '');
htmlElements.setValue('input-file-patch', '');
/* translatable elements */
const translatableElements = document.querySelectorAll('*[data-localize="yes"]');
for (var i = 0; i < translatableElements.length; i++) {
translatableElements[i].setAttribute('data-localize', translatableElements[i].innerHTML);
}
/* add drag and drop events */
if (newSettings && newSettings.allowDropFiles) {
window.addEventListener('dragover', function (evt) {
if (_dragEventContainsFiles(evt))
evt.preventDefault(); /* needed ! */
});
window.addEventListener('drop', function (evt) {
evt.preventDefault();
if (_dragEventContainsFiles(evt)) {
const droppedFiles = evt.dataTransfer.files;
if (droppedFiles && droppedFiles.length === 1) {
new BinFile(droppedFiles[0], function (binFile) {
if (RomPatcherWeb.getEmbededPatches()) {
RomPatcherWeb.provideRomFile(binFile, true);
} else if (ZIPManager.isZipFile(binFile)) {
ZIPManager.unzipAny(binFile._u8array.buffer);
} else if (RomPatcher.parsePatchFile(binFile)) {
RomPatcherWeb.providePatchFile(binFile, null, true);
} else {
RomPatcherWeb.provideRomFile(binFile, true);
}
});
}
}
});
htmlInputFileRom.addEventListener('drop', function (evt) {
evt.stopPropagation();
});
htmlInputFilePatch.addEventListener('drop', function (evt) {
evt.stopPropagation();
});
}
console.log('Rom Patcher JS initialized');
initialized = true;
/* initialize Rom Patcher */
RomPatcherWeb.setSettings(newSettings);
/* download embeded patch */
if (validEmbededPatch)
_fetchPatchFile(embededPatchInfo);
else
htmlElements.enableAll();
}
/* localization */
const _ = function (str) { return ROM_PATCHER_LOCALE[settings.language] && ROM_PATCHER_LOCALE[settings.language][str] ? ROM_PATCHER_LOCALE[settings.language][str] : str };
var initialized = false;
var loading = 0;
return {
_: function (str) { /* public localization function for external usage purposes */
return _(str);
@ -562,6 +739,10 @@ var RomPatcherWeb = (function () {
RomPatcherWeb.validateCurrentRom(_getChecksumStartOffset());
if (typeof settings.onloadpatch === 'function') {
settings.onloadpatch(binFile, embededPatchInfo, parsedPatch);
}
if (transferFakeFile) {
htmlElements.setFakeFile('input-file-patch', binFile.fileName);
}
@ -606,21 +787,9 @@ var RomPatcherWeb = (function () {
initialize: function (newSettings, embededPatchInfo) {
if (initialized)
throw new Error('Rom Patcher JS was already initialized');
else if (loading)
throw new Error('Rom Patcher JS is already loading or has failed to load');
/* embeded patches */
var validEmbededPatch = false;
if (embededPatchInfo) {
if (typeof embededPatchInfo === 'string')
embededPatchInfo = { file: embededPatchInfo };
if (typeof embededPatchInfo === 'object') {
if (typeof embededPatchInfo.file === 'string') {
validEmbededPatch = true;
} else {
throw new Error('Rom Patcher JS: invalid embeded patch file');
}
}
}
/* check incompatible browsers */
if (
@ -633,101 +802,32 @@ var RomPatcherWeb = (function () {
throw new Error('Rom Patcher JS: incompatible browser');
/* check if all required HTML elements are in DOM */
const htmlInputFileRom = htmlElements.get('input-file-rom');
if (htmlInputFileRom && htmlInputFileRom.tagName === 'INPUT' && htmlInputFileRom.type === 'file') {
htmlInputFileRom.addEventListener('change', function (evt) {
/* queue script dependencies */
const missingDependencies = _getMissingDependencies();
loading = missingDependencies.length;
const onLoadScript = function () {
loading--;
if (loading === 0) {
try {
_initialize(newSettings, embededPatchInfo);
} catch (ex) {
_setToastError(ex.message);
htmlElements.disableAll();
new BinFile(this, RomPatcherWeb.provideRomFile);
});
} else {
throw new Error('Rom Patcher JS: input#rom-patcher-input-file-rom[type=file] not found');
}
const htmlInputFilePatch = htmlElements.get('input-file-patch');
if (htmlInputFilePatch && htmlInputFilePatch.tagName === 'INPUT' && htmlInputFilePatch.type === 'file') {
htmlInputFilePatch.addEventListener('change', function (evt) {
htmlElements.disableAll();
new BinFile(this, RomPatcherWeb.providePatchFile);
});
} else {
throw new Error('Rom Patcher JS: input#rom-patcher-input-file-patch[type=file] not found');
}
const htmlButtonApply = htmlElements.get('button-apply');
if (htmlButtonApply && htmlButtonApply.tagName === 'BUTTON') {
htmlButtonApply.addEventListener('click', RomPatcherWeb.applyPatch);
} else {
throw new Error('Rom Patcher JS: button#rom-patcher-button-apply not found');
}
const htmlCheckboxAlterHeader = htmlElements.get('checkbox-alter-header');
if (htmlCheckboxAlterHeader && htmlCheckboxAlterHeader.tagName === 'INPUT' && htmlCheckboxAlterHeader.type === 'checkbox') {
htmlCheckboxAlterHeader.addEventListener('change', function (evt) {
if (!romFile)
return false;
};
const onErrorScript = function () {
throw new Error('Rom Patcher JS: error loading script ' + script.src);
};
console.log('Rom Patcher JS: loading ' + missingDependencies.length + ' dependencies');
missingDependencies.forEach(function (path) {
var script = document.createElement('script');
script.onload = onLoadScript;
script.onerror = onErrorScript;
script.src = path;
const headerInfo = RomPatcher.isRomHeadered(romFile);
if (headerInfo) {
htmlElements.disableAll();
webWorkerCrc.postMessage({ u8array: romFile._u8array, fileName: romFile.fileName, checksumStartOffset: _getChecksumStartOffset() }, [romFile._u8array.buffer]);
}
document.head.appendChild(script);
});
}
/* set all default input status just in case HTML is wrong */
htmlElements.setEnabled('button-apply', false);
/* reset input files */
htmlElements.setValue('input-file-rom', '');
htmlElements.setValue('input-file-patch', '');
/* translatable elements */
const translatableElements = document.querySelectorAll('*[data-localize="yes"]');
for (var i = 0; i < translatableElements.length; i++) {
translatableElements[i].setAttribute('data-localize', translatableElements[i].innerHTML);
}
/* add drag and drop events */
if (newSettings && newSettings.allowDropFiles) {
window.addEventListener('dragover', function (evt) {
if (_dragEventContainsFiles(evt))
evt.preventDefault(); /* needed ! */
});
window.addEventListener('drop', function (evt) {
evt.preventDefault();
if (_dragEventContainsFiles(evt)) {
const droppedFiles = evt.dataTransfer.files;
if (droppedFiles && droppedFiles.length === 1) {
new BinFile(droppedFiles[0], function (binFile) {
if (RomPatcherWeb.getEmbededPatches()) {
RomPatcherWeb.provideRomFile(binFile, true);
} else if (ZIPManager.isZipFile(binFile)) {
ZIPManager.unzipAny(binFile._u8array.buffer);
} else if (RomPatcher.parsePatchFile(binFile)) {
RomPatcherWeb.providePatchFile(binFile, null, true);
} else {
RomPatcherWeb.provideRomFile(binFile, true);
}
});
}
}
});
htmlInputFileRom.addEventListener('drop', function (evt) {
evt.stopPropagation();
});
htmlInputFilePatch.addEventListener('drop', function (evt) {
evt.stopPropagation();
});
}
console.log('Rom Patcher JS initialized');
initialized = true;
/* initialize Rom Patcher */
RomPatcherWeb.setSettings(newSettings);
/* download embeded patch */
if (validEmbededPatch)
_fetchPatchFile(embededPatchInfo);
},
applyPatch: function () {
@ -859,6 +959,11 @@ var RomPatcherWeb = (function () {
else if (typeof newSettings.onvalidaterom !== 'undefined')
settings.onvalidaterom = null;
if (typeof newSettings.onloadpatch === 'function')
settings.onloadpatch = newSettings.onloadpatch;
else if (typeof newSettings.onloadpatch !== 'undefined')
settings.onloadpatch = null;
if (typeof newSettings.onpatch === 'function')
settings.onpatch = newSettings.onpatch;
else if (typeof newSettings.onpatch !== 'undefined')
@ -935,9 +1040,9 @@ const ZIPManager = (function (romPatcherWeb) {
const _unzipEntry = function (zippedEntry, onUnzip) {
htmlElements.disableAll();
if (onUnzip === RomPatcherWeb.provideRomFile) {
if (onUnzip === romPatcherWeb.provideRomFile) {
_setRomInputSpinner(true);
} else if (onUnzip === RomPatcherWeb.providePatchFile) {
} else if (onUnzip === romPatcherWeb.providePatchFile) {
_setPatchInputSpinner(true);
} else {
throw new Error('ZIPManager._unzipEntry: invalid onUnzip callback');
@ -949,7 +1054,7 @@ const ZIPManager = (function (romPatcherWeb) {
binFile.fileName = zippedEntry.filename;
/* transfer files to input elements */
if (onUnzip === RomPatcherWeb.provideRomFile) {
if (onUnzip === romPatcherWeb.provideRomFile) {
_setRomInputSpinner(false);
onUnzip(binFile, true);
} else {
@ -971,9 +1076,9 @@ const ZIPManager = (function (romPatcherWeb) {
_unzipEntry(this.zipEntry, onUnzip);
}
if (onUnzip === RomPatcherWeb.provideRomFile) {
if (onUnzip === romPatcherWeb.provideRomFile) {
dialogZipMessage.innerHTML = _('ROM file:');
} else if (onUnzip === RomPatcherWeb.providePatchFile) {
} else if (onUnzip === romPatcherWeb.providePatchFile) {
dialogZipMessage.innerHTML = _('Patch file:');
} else {
throw new Error('ZIPManager._unzipEntry: invalid onUnzip callback');
@ -1022,8 +1127,8 @@ const ZIPManager = (function (romPatcherWeb) {
const _unzipError = function (zipReader) {
if (zipReader.message)
console.error('zip.js: ' + zipReader.message);
RomPatcherWeb.enable();
RomPatcherWeb.setErrorMessage(_('Error unzipping file'), 'error');
romPatcherWeb.enable();
romPatcherWeb.setErrorMessage(_('Error unzipping file'), 'error');
};
@ -1042,13 +1147,13 @@ const ZIPManager = (function (romPatcherWeb) {
const filteredEntries = _filterEntriesRoms(zipEntries);
if (filteredEntries.length === 1) {
_unzipEntry(filteredEntries[0], RomPatcherWeb.provideRomFile);
_unzipEntry(filteredEntries[0], romPatcherWeb.provideRomFile);
} else if (filteredEntries.length > 1) {
_showFilePicker(filteredEntries, RomPatcherWeb.provideRomFile);
RomPatcherWeb.enable();
_showFilePicker(filteredEntries, romPatcherWeb.provideRomFile);
romPatcherWeb.enable();
} else {
/* no possible patchable files found in zip, treat zip file as ROM file */
RomPatcherWeb.calculateCurrentRomChecksums();
romPatcherWeb.calculateCurrentRomChecksums();
}
});
},
@ -1066,11 +1171,11 @@ const ZIPManager = (function (romPatcherWeb) {
const filteredEntries = _filterEntriesPatches(zipEntries);
if (filteredEntries.length === 1) {
_unzipEntry(filteredEntries[0], RomPatcherWeb.providePatchFile);
_unzipEntry(filteredEntries[0], romPatcherWeb.providePatchFile);
} else if (filteredEntries.length > 1) {
_showFilePicker(filteredEntries, RomPatcherWeb.providePatchFile);
_showFilePicker(filteredEntries, romPatcherWeb.providePatchFile);
} else {
RomPatcherWeb.providePatchFile(null);
romPatcherWeb.providePatchFile(null);
}
});
@ -1091,16 +1196,16 @@ const ZIPManager = (function (romPatcherWeb) {
if (filteredEntriesRoms.length && filteredEntriesPatches.length === 0) {
if (filteredEntriesRoms.length === 1) {
_unzipEntry(filteredEntriesRoms[0], RomPatcherWeb.provideRomFile);
_unzipEntry(filteredEntriesRoms[0], romPatcherWeb.provideRomFile);
} else {
_showFilePicker(filteredEntriesRoms, RomPatcherWeb.provideRomFile);
RomPatcherWeb.enable();
_showFilePicker(filteredEntriesRoms, romPatcherWeb.provideRomFile);
romPatcherWeb.enable();
}
} else if (filteredEntriesPatches.length && filteredEntriesRoms.length === 0) {
if (filteredEntriesPatches.length === 1) {
_unzipEntry(filteredEntriesPatches[0], RomPatcherWeb.providePatchFile);
_unzipEntry(filteredEntriesPatches[0], romPatcherWeb.providePatchFile);
} else {
_showFilePicker(filteredEntriesPatches, RomPatcherWeb.providePatchFile);
_showFilePicker(filteredEntriesPatches, romPatcherWeb.providePatchFile);
}
} else {
console.warn('ZIPManager.unzipAny: zip file contains both ROMs and patches, cannot guess');
@ -1149,7 +1254,7 @@ const ZIPManager = (function (romPatcherWeb) {
select.addEventListener('change', function (evt) {
const fileIndex = parseInt(this.value);
_unzipEntry(filteredEntries[fileIndex], RomPatcherWeb.providePatchFile);
_unzipEntry(filteredEntries[fileIndex], romPatcherWeb.providePatchFile);
});
} else {
throw new Error('rom-patcher-select-file-patch not found');
@ -1157,10 +1262,10 @@ const ZIPManager = (function (romPatcherWeb) {
}
//_setPatchInputSpinner(false);
_unzipEntry(filteredEntries[0], RomPatcherWeb.providePatchFile);
_unzipEntry(filteredEntries[0], romPatcherWeb.providePatchFile);
} else {
RomPatcherWeb.setErrorMessage(_('No valid patches found in ZIP'), 'error');
RomPatcherWeb.disable();
romPatcherWeb.setErrorMessage(_('No valid patches found in ZIP'), 'error');
romPatcherWeb.disable();
}
});
},
@ -1258,6 +1363,8 @@ const PatchBuilderWeb = (function (romPatcherWeb) {
initialize: function () {
if (initialized)
throw new Error('Patch Builder JS was already initialized');
else if (!romPatcherWeb.isInitialized())
throw new Error('Rom Patcher JS must be initialized before Patch Builder JS');
document.getElementById('patch-builder-button-create').disabled = true;
@ -1269,6 +1376,8 @@ const PatchBuilderWeb = (function (romPatcherWeb) {
if (RomPatcher.isRomTooBig(originalRom))
_setToastError(_('Using big files is not recommended'), 'warning');
else if (ZIPManager.isZipFile(originalRom))
_setToastError(_('Patch creation is not compatible with zipped ROMs'), 'warning');
});
});
document.getElementById('patch-builder-input-file-modified').addEventListener('change', function () {
@ -1279,6 +1388,8 @@ const PatchBuilderWeb = (function (romPatcherWeb) {
if (RomPatcher.isRomTooBig(modifiedRom))
_setToastError(_('Using big files is not recommended'), 'warning');
else if (ZIPManager.isZipFile(modifiedRom))
_setToastError(_('Patch creation is not compatible with zipped ROMs'), 'warning');
});
});
document.getElementById('patch-builder-button-create').addEventListener('click', function () {
@ -1298,6 +1409,7 @@ const PatchBuilderWeb = (function (romPatcherWeb) {
console.log('Patch Builder JS initialized');
initialized = true;
_setElementsStatus(true);
}
}
}(RomPatcherWeb));

View file

@ -184,14 +184,19 @@ VCDIFF.fromFile=function(file){
function VCDIFF_Parser(binFile, offset)
{
BinFile.call(this, 0);
this.fileSize=binFile.fileSize;
this._u8array=binFile._u8array;
this._dataView=binFile._dataView;
this.offset=offset || 0;
}
VCDIFF_Parser.prototype=Object.create(BinFile.prototype);
/* reimplement readU8, readU32 and skip from BinFile */
/* in web implementation, there are no guarantees BinFile will be dynamically loaded before this one */
/* so we cannot rely on cloning BinFile.prototype */
this.readU8 = binFile.readU8;
this.readU32 = binFile.readU32;
this.skip = binFile.skip;
this.isEOF = binFile.isEOF;
this.seek = binFile.seek;
}
VCDIFF_Parser.prototype.read7BitEncodedInt=function(){
var num=0, bits = 0;

18
test.js
View file

@ -24,14 +24,17 @@
- RUP test
- Patch: https://www.romhacking.net/translations/843/
- ROM: Uchuu no Kishi - Tekkaman Blade (Japan).sfc [CRC32=cd16c529]
- xdelta test
- Patch: https://www.romhacking.net/hacks/2871/
- ROM: New Super Mario Bros. (USA, Australia).nds [CRC32=0197576a]
*/
const chalk=require('chalk');
const { existsSync }=require('fs');
const BinFile = require('./app/modules/BinFile');
const HashCalculator = require('./app/modules/HashCalculator');
const RomPatcher = require('./app/RomPatcher');
const BinFile = require('./rom-patcher-js/modules/BinFile');
const HashCalculator = require('./rom-patcher-js/modules/HashCalculator');
const RomPatcher = require('./rom-patcher-js/RomPatcher');
@ -77,10 +80,17 @@ const TEST_PATCHES=[
patchCrc32:0x621ab323,
patchDownload:'https://www.romhacking.net/hacks/4633/',
outputCrc32:0xe83e9b0a
},{
title:'NSMB Hack Domain Infusion',
romFile:'New Super Mario Bros. (USA, Australia).nds',
romCrc32:0x0197576a,
patchFile:'nsmb_infusion10a.xdelta',
patchCrc32:0xa211f97c,
patchDownload:'https://www.romhacking.net/hacks/2871/',
outputCrc32:0x9cecd976
}
];
const _test=function(title, testFunction){
try{
const startTime=(new Date()).getTime();

View file

@ -208,7 +208,10 @@ select:focus:not(:disabled),
input[type=checkbox].styled:focus:not(:disabled){
box-shadow: var(--rom-patcher-color-primary-focus) 0 0 0 2px;
}
input[type=file].w100, select.w100{width:100%}
#rom-patcher-container input[type=file],
#rom-patcher-container select,
#patch-builder-container input[type=file]
{width:100%}
input[type=file]{padding:6px 10px}
input[type=file]::file-selector-button{display:none}
select{

View file

@ -101,6 +101,9 @@ window.addEventListener('load', function (evt) {
});
document.getElementById('switch-create-button').addEventListener('click', function () {
if(!RomPatcherWeb.isInitialized())
throw new Error('Rom Patcher JS is not initialized yet');
if (/disabled/.test(document.getElementById('switch-create').className)) {
try{
if(!PatchBuilderWeb.isInitialized())