-
Notifications
You must be signed in to change notification settings - Fork 149
/
Copy pathtest.js
191 lines (161 loc) · 7.44 KB
/
test.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
#!/usr/bin/env node
/*
Test battery for Rom Patcher JS
https://github.com/marcrobledo/RomPatcher.js
by Marc Robledo, released under MIT license: https://github.com/marcrobledo/RomPatcher.js/blob/master/LICENSE
Usage:
> npm install
> npm run test
You need to provide the following ROMs and patches, unzip them in
_test_files/roms and _test_files/patches folders respectively:
- IPS test
- Patch: https://www.romhacking.net/hacks/3784/
- ROM: Super Mario Land 2 - 6 Golden Coins (USA, Europe).gb [CRC32=d5ec24e4]
- BPS test
- Patch: https://www.romhacking.net/translations/6297/
- ROM: Samurai Kid (Japan).gbc [CRC32=44a9ddfb]
- UPS test
- Patch: https://mother3.fobby.net/
- ROM: Mother 3 (Japan).gba [CRC32=42ac9cb9]
- APS test
- Patch: http://dorando.emuverse.com/projects/eduardo_a2j/zelda-ocarina-of-time.html
- ROM: Legend of Zelda, The - Ocarina of Time (USA).z64 [CRC32=7e107c35]
- 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('./rom-patcher-js/modules/BinFile');
const HashCalculator = require('./rom-patcher-js/modules/HashCalculator');
const RomPatcher = require('./rom-patcher-js/RomPatcher');
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
}
];
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_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, 102, 207, 63, 117, 164, 223, 10, 223, 200, 150, 4, 77, 250, 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, 160, 161, 245, 125, 144, 193, 178, 31, 253, 119, 168, 169, 187, 195, 165, 205, 140, 222, 134, 249, 68, 224, 248, 144, 207, 18, 126
])).buffer;
_test('HashCalculator integrity', function () {
if (HashCalculator.md5(TEST_DATA) !== '55c76e7e683fd7cd63c673c5df3efa6e')
throw new Error('invalid MD5');
if (HashCalculator.crc32(TEST_DATA) !== 0x903a031b)
throw new Error('invalid CRC32');
if (HashCalculator.adler32(TEST_DATA) !== 0xef984205)
throw new Error('invalid ADLER32');
if (HashCalculator.crc16(TEST_DATA) !== 0x96e4)
throw new Error('invalid SHA1');
});
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 (patchFormat === 'bps') {
if (patch.patchChecksum !== patch.calculateFileChecksum())
throw new Error('invalid patch checksum');
else if (patch.export().hashCRC32() !== 0x2144df1c)
throw new Error('invalid BPS crc32');
}
if (patchedFile.hashCRC32() !== modifiedFile.hashCRC32())
throw new Error('modified and patched files\' crc32 do not match');
})
});
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) {
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));
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));
return false;
}
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)
throw new Error('invalid patched file crc32');
});
});