diff --git a/_cache_service_worker.js b/_cache_service_worker.js
index ec49986..fc5e7e4 100644
--- a/_cache_service_worker.js
+++ b/_cache_service_worker.js
@@ -6,7 +6,7 @@
*/
var PRECACHE_ID = 'rom-patcher-js';
-var PRECACHE_VERSION = 'v30rc2c';
+var PRECACHE_VERSION = 'v30rc3';
var PRECACHE_URLS = [
'/RomPatcher.js/', '/RomPatcher.js/index.html',
'/RomPatcher.js/manifest.json',
diff --git a/index.html b/index.html
index 536a7f4..e4dbab7 100644
--- a/index.html
+++ b/index.html
@@ -151,7 +151,7 @@
- Rom Patcher JS v3.0 RC2 by Marc Robledo
+ Rom Patcher JS v3.0 RC3 by Marc Robledo
See on GitHub
Donate
diff --git a/rom-patcher-js/RomPatcher.webapp.js b/rom-patcher-js/RomPatcher.webapp.js
index 7cbf8e2..9dd2fbd 100644
--- a/rom-patcher-js/RomPatcher.webapp.js
+++ b/rom-patcher-js/RomPatcher.webapp.js
@@ -736,106 +736,110 @@ var RomPatcherWeb = (function () {
if (ZIPManager.isZipFile(binFile)) {
ZIPManager.unzipPatches(binFile._u8array.buffer);
} else {
- const parsedPatch = RomPatcher.parsePatchFile(binFile);
- if (parsedPatch) {
- patch = parsedPatch;
- _setPatchInputSpinner(false);
-
- const embededPatchInfo = _getEmbededPatchInfo(binFile.fileName);
- if (embededPatchInfo) {
- /* custom crc32s validation */
- if (embededPatchInfo.inputCrc32) {
- patch.validateSource = function (romFile, headerSize) {
- for (var i = 0; i < embededPatchInfo.inputCrc32.length; i++) {
- if (embededPatchInfo.inputCrc32[i] === romFile.hashCRC32(headerSize))
- return true;
+ try{
+ const parsedPatch = RomPatcher.parsePatchFile(binFile);
+ if (parsedPatch) {
+ patch = parsedPatch;
+ _setPatchInputSpinner(false);
+
+ const embededPatchInfo = _getEmbededPatchInfo(binFile.fileName);
+ if (embededPatchInfo) {
+ /* custom crc32s validation */
+ if (embededPatchInfo.inputCrc32) {
+ patch.validateSource = function (romFile, headerSize) {
+ for (var i = 0; i < embededPatchInfo.inputCrc32.length; i++) {
+ if (embededPatchInfo.inputCrc32[i] === romFile.hashCRC32(headerSize))
+ return true;
+ }
+ return false;
}
- return false;
+ patch.getValidationInfo = function () {
+ return {
+ 'type': 'CRC32',
+ 'value': embededPatchInfo.inputCrc32
+ }
+ };
}
- patch.getValidationInfo = function () {
- return {
- 'type': 'CRC32',
- 'value': embededPatchInfo.inputCrc32
+
+ /* custom description */
+ if (embededPatchInfo.description) {
+ patch.getDescription = function () {
+ return embededPatchInfo.description;
}
- };
- }
-
- /* custom description */
- if (embededPatchInfo.description) {
- patch.getDescription = function () {
- return embededPatchInfo.description;
}
}
- }
-
- /* toggle ROM requirements */
- if (htmlElements.get('row-patch-requirements') && htmlElements.get('patch-requirements-value')) {
- if (typeof patch.getValidationInfo === 'function' && patch.getValidationInfo()) {
- var validationInfo = patch.getValidationInfo();
- if (Array.isArray(validationInfo) || !validationInfo.type) {
- validationInfo = {
- type: 'ROM',
- value: validationInfo
- }
- }
- htmlElements.setText('patch-requirements-value', '');
-
- htmlElements.setText('patch-requirements-type', validationInfo.type === 'ROM' ? _('Required ROM:') : _('Required %s:').replace('%s', validationInfo.type));
-
- if (!Array.isArray(validationInfo.value))
- validationInfo.value = [validationInfo.value];
-
- validationInfo.value.forEach(function (value) {
- var line = document.createElement('div');
- if (typeof value !== 'string') {
- if (validationInfo.type === 'CRC32') {
- value = value.toString(16);
- while (value.length < 8)
- value = '0' + value;
- } else {
- value = value.toString();
+
+ /* toggle ROM requirements */
+ if (htmlElements.get('row-patch-requirements') && htmlElements.get('patch-requirements-value')) {
+ if (typeof patch.getValidationInfo === 'function' && patch.getValidationInfo()) {
+ var validationInfo = patch.getValidationInfo();
+ if (Array.isArray(validationInfo) || !validationInfo.type) {
+ validationInfo = {
+ type: 'ROM',
+ value: validationInfo
}
}
- /*
- var a=document.createElement('a');
- a.href='https://www.google.com/search?q=%22'+value+'%22';
- a.target='_blank';
- a.className='clickable';
- a.innerHTML=value;
- line.appendChild(a);
- */
- line.innerHTML = value;
- htmlElements.get('patch-requirements-value').appendChild(line);
- });
- htmlElements.addClass('row-patch-requirements', 'show');
- } else {
- htmlElements.setText('patch-requirements-value', '');
- htmlElements.removeClass('row-patch-requirements', 'show');
+ htmlElements.setText('patch-requirements-value', '');
+
+ htmlElements.setText('patch-requirements-type', validationInfo.type === 'ROM' ? _('Required ROM:') : _('Required %s:').replace('%s', validationInfo.type));
+
+ if (!Array.isArray(validationInfo.value))
+ validationInfo.value = [validationInfo.value];
+
+ validationInfo.value.forEach(function (value) {
+ var line = document.createElement('div');
+ if (typeof value !== 'string') {
+ if (validationInfo.type === 'CRC32') {
+ value = value.toString(16);
+ while (value.length < 8)
+ value = '0' + value;
+ } else {
+ value = value.toString();
+ }
+ }
+ /*
+ var a=document.createElement('a');
+ a.href='https://www.google.com/search?q=%22'+value+'%22';
+ a.target='_blank';
+ a.className='clickable';
+ a.innerHTML=value;
+ line.appendChild(a);
+ */
+ line.innerHTML = value;
+ htmlElements.get('patch-requirements-value').appendChild(line);
+ });
+ htmlElements.addClass('row-patch-requirements', 'show');
+ } else {
+ htmlElements.setText('patch-requirements-value', '');
+ htmlElements.removeClass('row-patch-requirements', 'show');
+ }
+ }
+
+ /* toggle patch description */
+ if (typeof patch.getDescription === 'function' && patch.getDescription()) {
+ htmlElements.setText('patch-description', patch.getDescription()/* .replace(/\n/g, '
') */);
+ //htmlElements.setTitle('patch-description', patch.getDescription());
+ htmlElements.addClass('row-patch-description', 'show');
+ } else {
+ htmlElements.setText('patch-description', '');
+ //htmlElements.setTitle('patch-description', '');
+ htmlElements.removeClass('row-patch-description', 'show');
+ }
+
+ RomPatcherWeb.validateCurrentRom(_getChecksumStartOffset());
+
+ if (typeof settings.onloadpatch === 'function') {
+ settings.onloadpatch(binFile, embededPatchInfo, parsedPatch);
+ }
+
+ if (transferFakeFile) {
+ htmlElements.setFakeFile('patch', binFile.fileName);
}
- }
-
- /* toggle patch description */
- if (typeof patch.getDescription === 'function' && patch.getDescription()) {
- htmlElements.setText('patch-description', patch.getDescription()/* .replace(/\n/g, '
') */);
- //htmlElements.setTitle('patch-description', patch.getDescription());
- htmlElements.addClass('row-patch-description', 'show');
} else {
- htmlElements.setText('patch-description', '');
- //htmlElements.setTitle('patch-description', '');
- htmlElements.removeClass('row-patch-description', 'show');
+ _setToastError(_('Invalid patch file'));
}
-
- RomPatcherWeb.validateCurrentRom(_getChecksumStartOffset());
-
- if (typeof settings.onloadpatch === 'function') {
- settings.onloadpatch(binFile, embededPatchInfo, parsedPatch);
- }
-
- if (transferFakeFile) {
- htmlElements.setFakeFile('patch', binFile.fileName);
- }
- } else {
- _setToastError(_('Invalid patch file'));
+ }catch(ex){
+ _setToastError(ex.message);
}
}
}
diff --git a/rom-patcher-js/modules/BinFile.js b/rom-patcher-js/modules/BinFile.js
index 53e3dfc..643cb93 100644
--- a/rom-patcher-js/modules/BinFile.js
+++ b/rom-patcher-js/modules/BinFile.js
@@ -1,5 +1,5 @@
/*
-* BinFile.js (last update: 2024-02-27)
+* BinFile.js (last update: 2024-08-21)
* by Marc Robledo, https://www.marcrobledo.com
*
* a JS class for reading/writing sequentially binary data from/to a file
@@ -188,7 +188,7 @@ BinFile.prototype.slice = function (offset, len, doNotClone) {
else if (len === 0)
throw new Error('zero length provided for slicing');
else
- offset = Math.floor(offset);
+ len = Math.floor(len);
if (offset === 0 && len === this.fileSize && doNotClone)
return this;
diff --git a/rom-patcher-js/modules/RomPatcher.format.aps_n64.js b/rom-patcher-js/modules/RomPatcher.format.aps_n64.js
index 156bc84..34e371e 100644
--- a/rom-patcher-js/modules/RomPatcher.format.aps_n64.js
+++ b/rom-patcher-js/modules/RomPatcher.format.aps_n64.js
@@ -204,7 +204,6 @@ APS.buildFromRoms=function(original, modified){
}else{
patch.addRecord(offset, differentBytes);
}
- //NO se puede comentar??? why????
}
}
diff --git a/test.js b/test.js
index 695e0cb..f6442ea 100644
--- a/test.js
+++ b/test.js
@@ -29,8 +29,8 @@
- ROM: New Super Mario Bros. (USA, Australia).nds [CRC32=0197576a]
*/
-const chalk=require('chalk');
-const { existsSync }=require('fs');
+const chalk = require('chalk');
+const { existsSync } = require('fs');
const BinFile = require('./rom-patcher-js/modules/BinFile');
const HashCalculator = require('./rom-patcher-js/modules/HashCalculator');
@@ -38,68 +38,68 @@ const RomPatcher = require('./rom-patcher-js/RomPatcher');
-const TEST_PATH='_test_files/';
-const TEST_PATCHES=[
+const TEST_PATH = '_test_files/';
+const TEST_PATCHES = [
{
- title:'IPS - Super Mario Land 2 DX',
- romFile:'Super Mario Land 2 - 6 Golden Coins (USA, Europe).gb',
- romCrc32:0xd5ec24e4,
- patchFile:'SML2DXv181.ips',
- patchCrc32:0x0b742316,
- patchDownload:'https://www.romhacking.net/hacks/3784/',
- outputCrc32:0xf0799017
- },{
- title:'BPS - Samurai Kid translation',
- romFile:'Samurai Kid (Japan).gbc',
- romCrc32:0x44a9ddfb,
- patchFile:'samurai_kid_en_v1.bps',
- patchCrc32:0x2144df1c,
- patchDownload:'https://www.romhacking.net/translations/6297/',
- outputCrc32:0xed238edb
- },{
- title:'UPS - Mother 3 translation',
- romFile:'Mother 3 (Japan).gba',
- romCrc32:0x42ac9cb9,
- patchFile:'mother3.ups',
- patchCrc32:0x2144df1c,
- patchDownload:'https://mother3.fobby.net/',
- outputCrc32:0x8a3bc5a8
- },{
- title:'APS - Zelda OoT spanish translation',
- romFile:'Legend of Zelda, The - Ocarina of Time (USA).z64',
- romCrc32:0xcd16c529,
- patchFile:'ZELDA64.APS',
- patchCrc32:0x7b70119d,
- patchDownload:'http://dorando.emuverse.com/projects/eduardo_a2j/zelda-ocarina-of-time.html',
- outputCrc32:0x7866f1ca
- },{
- title:'Tekkaman Blade translation',
- romFile:'Uchuu no Kishi - Tekkaman Blade (Japan).sfc',
- romCrc32:0x7e107c35,
- patchFile:'Tekkaman Blade v1.0.rup',
- 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
+ title: 'IPS - Super Mario Land 2 DX',
+ romFile: 'Super Mario Land 2 - 6 Golden Coins (USA, Europe).gb',
+ romCrc32: 0xd5ec24e4,
+ patchFile: 'SML2DXv181.ips',
+ patchCrc32: 0x0b742316,
+ patchDownload: 'https://www.romhacking.net/hacks/3784/',
+ outputCrc32: 0xf0799017
+ }, {
+ title: 'BPS - Samurai Kid translation',
+ romFile: 'Samurai Kid (Japan).gbc',
+ romCrc32: 0x44a9ddfb,
+ patchFile: 'samurai_kid_en_v1.bps',
+ patchCrc32: 0x2144df1c,
+ patchDownload: 'https://www.romhacking.net/translations/6297/',
+ outputCrc32: 0xed238edb
+ }, {
+ title: 'UPS - Mother 3 translation',
+ romFile: 'Mother 3 (Japan).gba',
+ romCrc32: 0x42ac9cb9,
+ patchFile: 'mother3.ups',
+ patchCrc32: 0x2144df1c,
+ patchDownload: 'https://mother3.fobby.net/',
+ outputCrc32: 0x8a3bc5a8
+ }, {
+ title: 'APS - Zelda OoT spanish translation',
+ romFile: 'Legend of Zelda, The - Ocarina of Time (USA).z64',
+ romCrc32: 0xcd16c529,
+ patchFile: 'ZELDA64.APS',
+ patchCrc32: 0x7b70119d,
+ patchDownload: 'http://dorando.emuverse.com/projects/eduardo_a2j/zelda-ocarina-of-time.html',
+ outputCrc32: 0x7866f1ca
+ }, {
+ title: 'Tekkaman Blade translation',
+ romFile: 'Uchuu no Kishi - Tekkaman Blade (Japan).sfc',
+ romCrc32: 0x7e107c35,
+ patchFile: 'Tekkaman Blade v1.0.rup',
+ 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();
- const result=testFunction.call();
-
- const executionTime=((new Date()).getTime() - startTime) / 1000;
- console.log(chalk.greenBright('√ '+title + ' ('+executionTime+'s)'));
- }catch(err){
- console.log(chalk.redBright('× '+title + ' - failed with error: '+err.message));
+const _test = function (title, testFunction) {
+ try {
+ const startTime = (new Date()).getTime();
+ const result = testFunction.call();
+
+ const executionTime = ((new Date()).getTime() - startTime) / 1000;
+ console.log(chalk.greenBright('√ ' + title + ' (' + executionTime + 's)'));
+ } catch (err) {
+ console.log(chalk.redBright('× ' + title + ' - failed with error: ' + err.message));
}
};
@@ -111,14 +111,14 @@ const TEST_DATA = (new Uint8Array([
-_test('HashCalculator integrity', function(){
- if(HashCalculator.md5(TEST_DATA) !== '55c76e7e683fd7cd63c673c5df3efa6e')
+_test('HashCalculator integrity', function () {
+ if (HashCalculator.md5(TEST_DATA) !== '55c76e7e683fd7cd63c673c5df3efa6e')
throw new Error('invalid MD5');
- if(HashCalculator.crc32(TEST_DATA).toString(16) !== '903a031b')
+ if (HashCalculator.crc32(TEST_DATA).toString(16) !== '903a031b')
throw new Error('invalid CRC32');
- if(HashCalculator.adler32(TEST_DATA).toString(16) !== 'ef984205')
+ if (HashCalculator.adler32(TEST_DATA).toString(16) !== 'ef984205')
throw new Error('invalid ADLER32');
- if(HashCalculator.crc16(TEST_DATA).toString(16) !== '96e4')
+ if (HashCalculator.crc16(TEST_DATA).toString(16) !== '96e4')
throw new Error('invalid SHA1');
});
@@ -126,13 +126,23 @@ _test('HashCalculator integrity', function(){
const MODIFIED_TEST_DATA = (new Uint8Array([
98, 91, 64, 8, 35, 53, 122, 167, 52, 253, 222, 156, 247, 82, 227, 213, 22, 221, 17, 247, 107, 102, 164, 254, 221, 8, 207, 63, 117, 164, 223, 10, 1, 77, 87, 123, 48, 9, 111, 64, 233, 118, 1, 36, 1, 60, 208, 245, 136, 126, 29, 231, 168, 18, 125, 172, 11, 184, 81, 20, 16, 30, 154, 16, 236, 21, 5, 74, 255, 112, 171, 198, 185, 89, 2, 98, 45, 164, 214, 55, 103, 15, 217, 95, 212, 133, 184, 21, 67, 144, 198, 163, 76, 35, 248, 229, 163, 37, 103, 33, 193, 96, 77, 255, 117, 89, 193, 61, 64, 253, 119, 82, 49, 187, 195, 165, 205, 140, 222, 134, 249, 68, 224, 248, 144, 207, 18, 126
])).buffer;
-['ips','bps','ppf','ups','aps','rup'].forEach(function(patchFormat){
- _test('create and apply '+patchFormat.toUpperCase(), function(){
- const originalFile=new BinFile(TEST_DATA);
- const modifiedFile=new BinFile(MODIFIED_TEST_DATA);
- const patch=RomPatcher.createPatch(originalFile, modifiedFile, patchFormat);
- const patchedFile=RomPatcher.applyPatch(originalFile, patch, {requireValidation:true});
- if(patchedFile.hashCRC32() !== modifiedFile.hashCRC32())
+['ips', 'bps', 'ppf', 'ups', 'aps', 'rup'].forEach(function (patchFormat) {
+ _test('create and apply ' + patchFormat.toUpperCase(), function () {
+ const originalFile = new BinFile(TEST_DATA);
+ const modifiedFile = new BinFile(MODIFIED_TEST_DATA);
+ const patch = RomPatcher.createPatch(originalFile, modifiedFile, patchFormat);
+ const patchedFile = RomPatcher.applyPatch(originalFile, patch, { requireValidation: true });
+
+ if (patchFormat === 'bps') {
+ const patchFile = patch.export();
+ const patchChecksum = patchFile.hashCRC32(0, patchFile.fileSize - 4);
+ if (patch.patchChecksum !== patchChecksum)
+ throw new Error('invalid patch checksum');
+ else if (patchFile.hashCRC32() !== 0x2144df1c)
+ throw new Error('invalid BPS crc32');
+ }
+
+ if (patchedFile.hashCRC32() !== modifiedFile.hashCRC32())
throw new Error('modified and patched files\' crc32 do not match');
})
});
@@ -140,40 +150,40 @@ const MODIFIED_TEST_DATA = (new Uint8Array([
-TEST_PATCHES.forEach(function(patchInfo){
- const patchPath=TEST_PATH+'patches/'+patchInfo.patchFile;
- if(!existsSync(patchPath)){
- console.log(chalk.yellow('! skipping patch '+patchInfo.title));
- console.log(chalk.yellow(' patch file not found: '+patchInfo.patchFile));
- console.log(chalk.yellow(' download patch at '+patchInfo.patchDownload));
+TEST_PATCHES.forEach(function (patchInfo) {
+ const patchPath = TEST_PATH + 'patches/' + patchInfo.patchFile;
+ if (!existsSync(patchPath)) {
+ console.log(chalk.yellow('! skipping patch ' + patchInfo.title));
+ console.log(chalk.yellow(' patch file not found: ' + patchInfo.patchFile));
+ console.log(chalk.yellow(' download patch at ' + patchInfo.patchDownload));
return false;
}
- const patchFile=new BinFile(patchPath);
- if(patchFile.hashCRC32() !== patchInfo.patchCrc32){
+ const patchFile = new BinFile(patchPath);
+ if (patchFile.hashCRC32() !== patchInfo.patchCrc32) {
console.log(patchFile.hashCRC32().toString(16));
- console.log(chalk.yellow('! skipping '+patchInfo.title+' test: invalid patch crc32'));
- console.log(chalk.yellow(' download correct patch at '+patchInfo.patchDownload));
+ console.log(chalk.yellow('! skipping ' + patchInfo.title + ' test: invalid patch crc32'));
+ console.log(chalk.yellow(' download correct patch at ' + patchInfo.patchDownload));
return false;
}
- const romPath=TEST_PATH+'roms/'+patchInfo.romFile;
- if(!existsSync(romPath)){
- console.log(chalk.yellow('! skipping patch '+patchInfo.title));
- console.log(chalk.yellow(' ROM file not found: '+patchInfo.romFile));
+ const romPath = TEST_PATH + 'roms/' + patchInfo.romFile;
+ if (!existsSync(romPath)) {
+ console.log(chalk.yellow('! skipping patch ' + patchInfo.title));
+ console.log(chalk.yellow(' ROM file not found: ' + patchInfo.romFile));
return false;
}
- const romFile=new BinFile(romPath);
- if(romFile.hashCRC32() !== patchInfo.romCrc32){
- console.log(chalk.yellow('! skipping '+patchInfo.title+' test: invalid ROM crc32'));
+ const romFile = new BinFile(romPath);
+ if (romFile.hashCRC32() !== patchInfo.romCrc32) {
+ console.log(chalk.yellow('! skipping ' + patchInfo.title + ' test: invalid ROM crc32'));
return false;
}
- _test('patch '+patchInfo.title, function(){
- const patch=RomPatcher.parsePatchFile(patchFile);
- const patchedRom=RomPatcher.applyPatch(romFile, patch, {requireValidation:true});
- if(patchedRom.hashCRC32() !== patchInfo.outputCrc32)
+ _test('patch ' + patchInfo.title, function () {
+ const patch = RomPatcher.parsePatchFile(patchFile);
+ const patchedRom = RomPatcher.applyPatch(romFile, patch, { requireValidation: true });
+ if (patchedRom.hashCRC32() !== patchInfo.outputCrc32)
throw new Error('invalid patched file crc32');
});
});