Skip to content

Instantly share code, notes, and snippets.

@cxx
Last active April 3, 2023 18:24
Show Gist options
  • Save cxx/81b9f45eb5b3cb87b4f3783ccdf8894f to your computer and use it in GitHub Desktop.
Save cxx/81b9f45eb5b3cb87b4f3783ccdf8894f to your computer and use it in GitHub Desktop.
// dotemu2mame.js - convert data of games ported by DotEmu to MAME ROM sets
//
// Usage:
// node dotemu2mame.js [ROM directory]
//
// Requirements:
// - Node.js v6 or later
// - [Microsoft Windows] .NET Framework 4.5 or later (included in Windows 8/10)
// - [Linux] /usr/bin/zip
//
// Supported games:
// - Double Dragon Trilogy (GOG.com)
// * Double Dragon
// * Double Dragon 2
// * Double Dragon 3 ("mb7114h.ic38" is missing)
// - R-Type (Google Play)
// - R-Type II (Google Play)
// - Irem Arcade Hits (Mac App Store)
// * Air Duel
// * Battle Chopper
// * Blade Master
// * Cosmic Cop
// * Dragon Breed
// * Gunforce
// * Gunforce 2
// * Hammerin' Harry
// * Image Fight
// * In the Hunt
// * Kung-Fu Master ("b-6f-.bin" is missing)
// * Legend of Hero Tonma
// * Mystic Riders
// * Ninja Spirit ("proms" and "plds" ROMs are missing)
// * R-Type Leo
// * Superior Soldiers
// * Undercover Cops
// * Vigilante ("plds" ROMs are missing)
// - Raiden Legacy (Google Play, GOG.com)
// * Raiden
// * Raiden Fighters (wrong checksums)
// * Raiden Fighters 2 (wrong checksums)
// * Raiden Fighters Jet (wrong checksums)
// - Neo Geo (Steam, Google Play) (neogeo.zip is incomplete, cannot be run)
// * Baseball Stars 2
// * Blazing Star
// * The King of Fighters '97
// * The King of Fighters '98 (KOF '98 Ultimate Match is NOT supported)
// * Metal Slug
// * Metal Slug 2
// * Metal Slug X (wrong checksums)
// * Metal Slug 3
// * Samurai Shodown II
// * Shock Troopers
// * Twinkle Star Sprites
// * Fatal Fury Special (not tested, may not work)
// * The Last Blade (not tested, may not work)
// * Shock Troopers 2nd Squad (not tested, may not work)
//
// Changelog:
// - 2017-12-21: Place dummy files for missing ROMs.
// - 2017-12-20: Support Irem Arcade Hits (Mac App Store).
// - 2017-06-25: Support R-Type II (Google Play).
// - 2017-04-23: Initial release.
const child_process = require('child_process');
const crypto = require('crypto');
const fs = require('fs');
const os = require('os');
const path = require('path');
Array.prototype.flatten = function() {
return this.reduce((acc, cur) => acc.concat(
Array.isArray(cur) ? cur.flatten() : cur
), []);
};
Array.prototype.flatMap = function(callback) {
return this.map(callback).flatten();
};
Array.prototype.sum = function() {
return this.reduce((acc, cur) => acc + cur, 0);
};
function split(buf, size)
{
const ret = [];
for (let i = 0; i < buf.length; i += size)
ret.push(buf.slice(i, Math.min(i+size,buf.length)));
return ret;
}
function split_at(buf, ...pos)
{
const ret = [];
pos = [0, ...pos, buf.length];
for (let i = 1; i < pos.length; i++)
ret.push(buf.slice(pos[i-1], pos[i]));
return ret;
}
function interleave(buf, pat=[1,1])
{
const step = pat.sum();
return pat.map((n, index) => {
const b = Buffer.allocUnsafe(buf.length * n / step);
const offset = pat.slice(0, index).sum();
if (n === 1)
for (let i = 0; i < b.length; i++)
b[i] = buf[i*step+offset];
else { /* n === 2 */
for (let i = 0; i < b.length/2; i++) {
b[i*2+0] = buf[i*step+offset+1];
b[i*2+1] = buf[i*step+offset+0];
}
}
return b;
});
}
function bitswap(val, ...indices)
{
const n = indices.length;
let ret = 0;
for (let i = 0; i < n; i++)
ret |= (val >> indices[i] & 1) << n-1-i;
return ret;
}
function reverse_bitswap(val, ...indices)
{
const n = indices.length;
let ret = 0;
for (let i = 0; i < n; i++)
ret |= (val >> n-1-i & 1) << indices[i];
return ret;
}
function sha1(buf)
{
const hash = crypto.createHash('sha1');
hash.update(buf);
return hash.digest('hex');
}
function encode_gfx(buf, layout)
{
const np = layout.planes;
const dest = Buffer.alloc(buf.length * np / 8);
if (Array.isArray(layout.total)) {
const [num, den] = layout.total;
layout = Object.assign({}, layout, {
total: dest.length * 8 / layout.charincrement * num / den,
planeoffset: layout.planeoffset.map(x => {
if (Array.isArray(x)) {
let [num, den, add] = x;
add = add || 0;
return dest.length * 8 * num / den + add;
}
else
return x;
})
});
}
let i = 0;
for (let c = 0; c < layout.total; c++) {
const charoffset = layout.charincrement * c;
for (let y = 0; y < layout.height; y++) {
const yoffset = charoffset + layout.yoffset[y];
for (let x = 0; x < layout.width; x++) {
const xoffset = yoffset + layout.xoffset[x];
for (let p = 0; p < np; p++) {
const offset = xoffset + layout.planeoffset[p];
dest[offset >> 3] |=
((buf[i] >> np-1-p) & 1) << (~offset & 7);
}
i++;
}
}
}
return dest;
}
function zip(name, dir)
{
let cmd;
if (os.type() === 'Windows_NT')
cmd = `powershell Add-Type -AssemblyName System.IO.Compression.FileSystem; [System.IO.Compression.ZipFile]::CreateFromDirectory('${dir}', '${name}.zip')`;
else
cmd = `zip -j ${name}.zip ${dir}/*`;
child_process.execSync(cmd);
}
function convert_roms(name, srcdir, maps)
{
const bins = {};
for (const region in maps) {
const map = maps[region];
let bin;
if (map.input instanceof Buffer)
bin = map.input;
else {
let file;
let layout;
if (typeof map.input === 'string')
file = map.input;
else
({file, layout} = map.input);
bin = fs.readFileSync(path.join(srcdir, file));
if (layout)
bin = encode_gfx(bin, layout);
}
if (map.transform)
bin = map.transform(bin);
bins[region] = bin;
}
const dstdir = fs.mkdtempSync(path.join(os.tmpdir(), name));
for (const region in maps) {
let bin = bins[region];
let {output} = maps[region];
if (typeof output === 'string') {
output = [output];
bin = [bin];
}
if (!Array.isArray(bin))
bin = split(bin, bin.length/output.length);
for (let i = 0; i < output.length; i++)
fs.writeFileSync(path.join(dstdir, output[i]), bin[i]);
}
zip(name, dstdir);
for (const f of fs.readdirSync(dstdir))
fs.unlinkSync(path.join(dstdir, f));
fs.rmdirSync(dstdir);
console.log(`saved as ${name}.zip.`);
}
// from https://github.com/mamedev/mame/blob/ddb290d5f615019c33c42b8d94e5a5254cabcf33/src/mame/video/vigilant.cpp
// license:BSD-3-Clause
// copyright-holders:Mike Balfour
function vigilant_reorder(src)
{
const pages = 4;
const width = 512;
const height = 256;
var dst = Buffer.alloc(src.length/2);
var i = 0;
for (var p = 0; p < pages; p++)
for (var y = 0; y < height; y++) {
var j = (width*pages*y + width*p) * 2;
for (var x = 0; x < width; x++) {
dst[i] = src[j] & 0xf;
i++;
j += 2;
}
}
return dst;
}
const SPI = (function() {
// from https://github.com/mamedev/mame/blob/ddb290d5f615019c33c42b8d94e5a5254cabcf33/src/mame/machine/seibuspi.cpp
// license:BSD-3-Clause
// copyright-holders:Ville Linde, hap, Nicola Salmoria
function partial_borrow_diff(minu, sub, carry_mask, bits)
{
let res = 0;
let borrow = 0;
for (let i = 0; i < bits; i++) {
bit = (minu >> i & 1) - (sub >> i & 1) - borrow;
res |= (bit & 1) << i;
borrow = (carry_mask >> i & 1) & (bit >> 1);
}
return res ^ borrow;
}
const KEY_TABLE = [
0x3ad7,0x54b1,0x2d41,0x8ca0,0xa69b,0x9018,0x9db9,0x6559,
0xe9a7,0xb087,0x8a5e,0x821c,0xaafc,0x2ae7,0x557b,0xcd80,
0xcfee,0x653e,0x9b31,0x7ab5,0x8b2a,0xbda8,0x707a,0x3c83,
0xcbb7,0x7157,0x8226,0x5c4a,0x8bf2,0x6397,0x13e2,0x3102,
0x8093,0x44cd,0x5f2d,0x7639,0xa7a4,0x9974,0x5263,0x8318,
0xb78c,0xa120,0xafb4,0x615f,0x6e0b,0x1d7d,0x8c29,0x4466,
0x3f35,0x794e,0xaea6,0x601c,0xe478,0xcf6e,0x4ee3,0xa009,
0x4b99,0x51d3,0x3474,0x3e4d,0xe5b7,0x9088,0xb5c0,0xba9f,
0x5646,0xa0af,0x970b,0xb14f,0x8216,0x2386,0x496d,0x9245,
0x7e4c,0xad5f,0x89d9,0xb801,0xdf64,0x8ca8,0xe019,0xde9b,
0x6836,0x70e2,0x7dcd,0x7ac1,0x98ef,0x71aa,0x7d6f,0x70bd,
0x9e14,0x75b6,0x8153,0xab6c,0x1f85,0x79cd,0xb2a1,0x934a,
0x6f74,0x37d7,0xa05a,0x6563,0x1972,0x2dcd,0x7e59,0x6a60,
0x5163,0x84c4,0xc451,0x8d80,0x4287,0x57e8,0xacc9,0x539d,
0xbe71,0xdb7c,0x9424,0xb224,0xcc0f,0xe3dd,0xb79c,0x461e,
0x96a9,0x4c7c,0x5443,0x6b2b,0x3cdc,0xbee8,0x2602,0x3282,
0x7f9c,0x59c3,0xc69a,0x39f4,0x5138,0xb7ca,0x6ca7,0x62e7,
0xc455,0x56cf,0x8a9a,0x695c,0x5af2,0xdebf,0x4dbb,0xdaec,
0xb564,0xc89c,0x7d2d,0x6dc3,0xa15a,0x6584,0xb8ea,0xb7ac,
0x88d8,0xc5aa,0x98c5,0xc506,0xc13c,0x7f59,0xab65,0x8fc8,
0x3a3c,0xd5f6,0x554d,0x5682,0x8ce7,0x40fc,0x8fd7,0x535c,
0x6aa0,0x52fe,0x8834,0x5316,0x6c27,0x80a9,0x9e6f,0x2c08,
0x4092,0xc7c1,0xc468,0x9520,0xbc4d,0xb621,0x3cdb,0xdce8,
0x481f,0xd0bd,0x3a57,0x807e,0x3025,0x5aa0,0x5e49,0xa29b,
0xd2d6,0x7bee,0x97f0,0xe28e,0x2fff,0x48e4,0x6367,0x933f,
0x57c5,0x28d4,0x68a0,0xd22e,0x39a6,0x9d2b,0x7a64,0x7e72,
0x5379,0xe86c,0x7554,0x8fbb,0xc06a,0x9533,0x7eec,0x4d52,
0xa800,0x5d35,0xa47d,0xe515,0x8d19,0x703b,0x5a2e,0x627c,
0x7cea,0x1b2c,0x5a05,0x8598,0x9e00,0xcf01,0x62d9,0x7a10,
0x1f42,0x87ce,0x575d,0x6e23,0x86ef,0x93c2,0x3d1a,0x89aa,
0xe199,0xba1d,0x1b72,0x4513,0x5131,0xc23c,0xba9f,0xa069,
0xfbfb,0xda92,0x42b2,0x3a48,0xdb96,0x5fad,0xba96,0xc6eb
];
const SPI_BITSWAP = [
[15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0],
[ 7, 6, 5,14, 0,15, 4, 3, 2, 8, 9,10,11,12,13, 1],
[ 9,15,14,13,12, 0, 1, 2,10, 8, 7, 6, 5, 4, 3,11],
[ 5, 4, 3, 2, 9,14,13,12,11, 6, 7, 8, 1,15, 0,10],
[12,11, 0, 7, 8, 5, 6,15,10,13,14, 1, 2, 3, 4, 9],
[14, 0, 1, 2, 3, 9, 8, 7,15, 5, 6,13,12,11,10, 4],
[13,12,11,10, 2, 7, 8, 9, 0,14,15, 3, 4, 5, 6, 1],
[ 2, 9,10,11,12, 7, 6, 5,14, 3, 4, 0,15, 1, 8,13],
[ 8, 7, 4, 3, 2,13,12,11, 0, 9,10,14,15, 6, 5, 1],
[ 3, 2,10,11,12, 5,14, 0, 1, 4,15, 6, 7, 8, 9,13],
[ 2,10, 6, 5, 4,14,13,12,11, 1, 0,15, 9, 8, 7, 3],
[12,11, 8, 1,15, 3, 2, 9,10,13,14, 4, 5, 6, 7, 0],
[ 8, 7, 0,11,12, 5, 6,15,14, 9,10, 1, 2, 3, 4,13],
[ 3, 2, 1, 0,14, 9, 8, 7, 6, 4,15,13,12,11,10, 5],
[ 2,10,11,12,13, 7, 8, 9,15, 1, 0, 3, 4, 5, 6,14],
[12,11,10, 9, 2, 7, 6, 5, 4,13,14, 0,15, 1, 8, 3]
];
function key(table, addr)
{
const xorbit = 8 + ((table & 0xc) >> 2);
return ((KEY_TABLE[addr & 0xff] >> 4) >> table & 1) ^ (addr >> xorbit & 1);
}
function seibuspi_sprite_encrypt(src)
{
const rom_size = src.length / 3;
for (let i = 0; i < rom_size/2; i++) {
const addr = i >> 8;
const plane5 = src[2*i+0*rom_size+0];
const plane4 = src[2*i+0*rom_size+1];
const plane3 = src[2*i+1*rom_size+0];
const plane2 = src[2*i+1*rom_size+1];
const plane1 = src[2*i+2*rom_size+0];
const plane0 = src[2*i+2*rom_size+1];
let s1 = 0;
let s2 = 0;
for (let j = 0; j < 8; j++) {
s1 |= (plane5 >> j & 1) << 2*j+1 |
(plane4 >> j & 1) << 2*j+0;
s2 |= (plane3 >> j & 1) << 4*j+3 |
(plane2 >> j & 1) << 4*j+2 |
(plane1 >> j & 1) << 4*j+1 |
(plane0 >> j & 1) << 4*j+0;
}
const sub1 = (addr >> 11 & 1) << 0 |
(addr >> 10 & 1) << 1 |
key(10, addr) << 2 |
key( 5, addr) << 3 |
key( 4, addr) << 4 |
(addr >> 11 & 1) << 5 |
(addr >> 11 & 1) << 6 |
key( 7, addr) << 7 |
key( 6, addr) << 8 |
key( 1, addr) << 9 |
key( 0, addr) << 10 |
(addr >> 11 & 1) << 11 |
key( 9, addr) << 12 |
key( 8, addr) << 13 |
key( 3, addr) << 14 |
key( 2, addr) << 15;
const sub2 = key( 0, addr) << 0 |
key( 1, addr) << 1 |
key( 2, addr) << 2 |
key( 3, addr) << 3 |
key( 4, addr) << 4 |
key( 5, addr) << 5 |
key( 6, addr) << 6 |
key( 7, addr) << 7 |
key( 8, addr) << 8 |
key( 9, addr) << 9 |
key(10, addr) << 10 |
(addr >> 10 & 1) << 11 |
(addr >> 11 & 1) << 12 |
(addr >> 11 & 1) << 13 |
(addr >> 11 & 1) << 14 |
(addr >> 11 & 1) << 15 |
(addr >> 11 & 1) << 16 |
key( 7, addr) << 17 |
(addr >> 11 & 1) << 18 |
key( 6, addr) << 19 |
(addr >> 11 & 1) << 20 |
key( 5, addr) << 21 |
(addr >> 11 & 1) << 22 |
key( 4, addr) << 23 |
(addr >> 10 & 1) << 24 |
key( 3, addr) << 25 |
key(10, addr) << 26 |
key( 2, addr) << 27 |
key( 9, addr) << 28 |
key( 1, addr) << 29 |
key( 8, addr) << 30 |
key( 0, addr) << 31;
s1 = partial_borrow_diff(s1 ^ 0x843a, sub1, 0x3a59, 16);
s2 = partial_borrow_diff(s2 ^ 0xc8e29f84, sub2, 0x28d49cac, 32);
let y1 = (s2 >> 22 & 1) << 0 |
(s1 >> 6 & 1) << 1 |
(s2 >> 6 & 1) << 2 |
(s2 >> 16 & 1) << 3 |
(s1 >> 0 & 1) << 4 |
(s2 >> 0 & 1) << 5 |
(s2 >> 11 & 1) << 6 |
(s1 >> 11 & 1) << 7 |
(s2 >> 27 & 1) << 8 |
(s2 >> 21 & 1) << 9 |
(s1 >> 5 & 1) << 10 |
(s2 >> 5 & 1) << 11 |
(s2 >> 15 & 1) << 12 |
(s1 >> 15 & 1) << 13 |
(s2 >> 31 & 1) << 14 |
(s2 >> 10 & 1) << 15;
let y2 = (s1 >> 10 & 1) << 0 |
(s2 >> 26 & 1) << 1 |
(s2 >> 20 & 1) << 2 |
(s1 >> 4 & 1) << 3 |
(s2 >> 4 & 1) << 4 |
(s2 >> 14 & 1) << 5 |
(s1 >> 14 & 1) << 6 |
(s2 >> 30 & 1) << 7 |
(s2 >> 9 & 1) << 8 |
(s1 >> 9 & 1) << 9 |
(s2 >> 25 & 1) << 10 |
(s2 >> 19 & 1) << 11 |
(s1 >> 3 & 1) << 12 |
(s2 >> 3 & 1) << 13 |
(s2 >> 13 & 1) << 14 |
(s1 >> 13 & 1) << 15;
let y3 = (s2 >> 1 & 1) << 0 |
(s2 >> 24 & 1) << 1 |
(s1 >> 8 & 1) << 2 |
(s2 >> 8 & 1) << 3 |
(s2 >> 29 & 1) << 4 |
(s2 >> 2 & 1) << 5 |
(s1 >> 2 & 1) << 6 |
(s1 >> 1 & 1) << 7 |
(s2 >> 17 & 1) << 8 |
(s2 >> 7 & 1) << 9 |
(s2 >> 23 & 1) << 10 |
(s2 >> 12 & 1) << 11 |
(s1 >> 12 & 1) << 12 |
(s2 >> 28 & 1) << 13 |
(s1 >> 7 & 1) << 14 |
(s2 >> 18 & 1) << 15;
y3 = reverse_bitswap(y3, ...SPI_BITSWAP[KEY_TABLE[addr & 0xff] & 0xf]);
src[2*i+0*rom_size+0] = y1 >> 0 & 0xff;
src[2*i+0*rom_size+1] = y1 >> 8 & 0xff;
src[2*i+1*rom_size+0] = y2 >> 0 & 0xff;
src[2*i+1*rom_size+1] = y2 >> 8 & 0xff;
src[2*i+2*rom_size+0] = y3 >> 0 & 0xff;
src[2*i+2*rom_size+1] = y3 >> 8 & 0xff;
}
return src;
}
function sprite_reorder(buf)
{
const tmp = Buffer.allocUnsafe(64);
for (let i = 0; i < buf.length; i += 64) {
for (let j = 0; j < 16; j++) {
tmp[2*j+ 0] = buf[i+4*j+0];
tmp[2*j+ 1] = buf[i+4*j+1];
tmp[2*j+32] = buf[i+4*j+2];
tmp[2*j+33] = buf[i+4*j+3];
}
tmp.copy(buf, i);
}
}
function seibuspi_rise10_sprite_encrypt(rom)
{
const size = rom.length / 3;
sprite_reorder(rom);
for (let i = 0; i < size/2; i++) {
let plane54 = rom[0*size+2*i+0] << 8 |
rom[0*size+2*i+1] << 0;
let plane3210 = rom[1*size+2*i+0] << 24 |
rom[1*size+2*i+1] << 16 |
rom[2*size+2*i+0] << 8 |
rom[2*size+2*i+1] << 0;
plane54 = partial_borrow_diff(plane54 ^ 0x6699, 0xabcb, 0x55aa, 16);
plane3210 = partial_borrow_diff(plane3210 ^ 0x0ca352a9,
0x654321d9 ^ 0x42, 0x1d463748, 32);
plane3210 = reverse_bitswap(
plane3210, 23,13,24,4,16,12,25,30, 3,5,29,17,14,22,2,11,
27,6,15,21,1,28,10,20, 7,31,26,0,18,9,19,8
);
rom[0*size+2*i+0] = plane54 >> 0 & 0xff;
rom[0*size+2*i+1] = plane54 >> 8 & 0xff;
rom[1*size+2*i+0] = plane3210 >> 0 & 0xff;
rom[1*size+2*i+1] = plane3210 >> 8 & 0xff;
rom[2*size+2*i+0] = plane3210 >> 16 & 0xff;
rom[2*size+2*i+1] = plane3210 >> 24 & 0xff;
}
}
function seibuspi_rise11_sprite_encrypt(rom, k1, k2, k3, k4, k5)
{
const size = rom.length / 3;
sprite_reorder(rom);
for (let i = 0; i < size/2; i++) {
let plane543 = rom[0*size+2*i+0] << 16 |
rom[0*size+2*i+1] << 8 |
rom[1*size+2*i+0] << 0;
let plane210 = rom[1*size+2*i+1] << 16 |
rom[2*size+2*i+0] << 8 |
rom[2*size+2*i+1] << 0;
plane543 = partial_borrow_diff(plane543 ^ k3, k1, k2, 32);
plane210 = partial_borrow_diff(plane210 ^ k5, i, k4, 24);
rom[0*size+2*i+0] = (plane210 >> 12 & 1) << 0 |
(plane210 >> 1 & 1) << 1 |
(plane543 >> 9 & 1) << 2 |
(plane543 >> 16 & 1) << 3 |
(plane543 >> 21 & 1) << 4 |
(plane210 >> 22 & 1) << 5 |
(plane543 >> 1 & 1) << 6 |
(plane210 >> 4 & 1) << 7;
rom[0*size+2*i+1] = (plane210 >> 16 & 1) << 0 |
(plane543 >> 11 & 1) << 1 |
(plane210 >> 19 & 1) << 2 |
(plane543 >> 7 & 1) << 3 |
(plane543 >> 8 & 1) << 4 |
(plane210 >> 2 & 1) << 5 |
(plane210 >> 0 & 1) << 6 |
(plane543 >> 18 & 1) << 7;
rom[1*size+2*i+0] = (plane543 >> 23 & 1) << 0 |
(plane210 >> 9 & 1) << 1 |
(plane543 >> 13 & 1) << 2 |
(plane210 >> 21 & 1) << 3 |
(plane210 >> 6 & 1) << 4 |
(plane543 >> 10 & 1) << 5 |
(plane210 >> 11 & 1) << 6 |
(plane543 >> 17 & 1) << 7;
rom[1*size+2*i+1] = (plane210 >> 14 & 1) << 0 |
(plane210 >> 7 & 1) << 1 |
(plane543 >> 14 & 1) << 2 |
(plane543 >> 0 & 1) << 3 |
(plane543 >> 4 & 1) << 4 |
(plane543 >> 20 & 1) << 5 |
(plane210 >> 5 & 1) << 6 |
(plane210 >> 20 & 1) << 7;
rom[2*size+2*i+0] = (plane210 >> 3 & 1) << 0 |
(plane543 >> 12 & 1) << 1 |
(plane543 >> 22 & 1) << 2 |
(plane543 >> 3 & 1) << 3 |
(plane543 >> 6 & 1) << 4 |
(plane543 >> 15 & 1) << 5 |
(plane210 >> 18 & 1) << 6 |
(plane210 >> 10 & 1) << 7;
rom[2*size+2*i+1] = (plane210 >> 8 & 1) << 0 |
(plane543 >> 19 & 1) << 1 |
(plane210 >> 17 & 1) << 2 |
(plane210 >> 13 & 1) << 3 |
(plane543 >> 2 & 1) << 4 |
(plane210 >> 15 & 1) << 5 |
(plane543 >> 5 & 1) << 6 |
(plane210 >> 23 & 1) << 7;
}
}
function seibuspi_rise11_sprite_encrypt_rfjet(rom)
{
seibuspi_rise11_sprite_encrypt(rom, 0xabcb64, 0x55aadd,
0xab6a4c, 0xd6375b, 0x8bf23b);
return rom;
}
// from https://github.com/mamedev/mame/blob/6c23897483a0201dd0b65b450253fd9bf8fb8723/src/mame/video/seibuspi.cpp
// license:BSD-3-Clause
// copyright-holders:Ville Linde, hap, Nicola Salmoria
function encrypt_tile(val, tileno, key1, key2, key3)
{
val = partial_borrow_diff(val ^ key3, tileno + key1, key2, 24);
return reverse_bitswap(
val, 18,19,9,5,10,17,16,20, 21,22,6,11,15,14,4,23, 0,1,7,8,13,12,3,2
);
}
function encrypt_text(rom, key1, key2, key3)
{
for (let i = 0; i < 0x10000; i++) {
let w = rom[i*3+0] << 16 | rom[i*3+1] << 8 | rom[i*3+2];
w = encrypt_tile(w, i >> 4, key1, key2, key3);
rom[i*3+0] = w >> 16 & 0xff;
rom[i*3+1] = w >> 8 & 0xff;
rom[i*3+2] = w >> 0 & 0xff;
}
}
function encrypt_bg(rom, key1, key2, key3)
{
for (let j = 0; j < rom.length; j += 0xc0000) {
for (let i = 0; i < 0x40000; i++) {
let w = rom[j+i*3+0] << 16 | rom[j+i*3+1] << 8 | rom[j+i*3+2];
w = encrypt_tile(w, i >> 6, key1, key2, key3);
rom[j+i*3+0] = w >> 16 & 0xff;
rom[j+i*3+1] = w >> 8 & 0xff;
rom[j+i*3+2] = w >> 0 & 0xff;
}
}
}
function seibuspi_text_encrypt(rom)
{
encrypt_text(rom, 0x5a3845, 0x77cf5b, 0x1378df);
return rom;
}
function seibuspi_bg_encrypt(rom)
{
encrypt_bg(rom, 0x5a3845, 0x77cf5b, 0x1378df);
return rom;
}
function rdft2_text_encrypt(rom)
{
encrypt_text(rom, 0x823146, 0x4de2f8, 0x157adc);
return rom;
}
function rdft2_bg_encrypt(rom)
{
encrypt_bg(rom, 0x823146, 0x4de2f8, 0x157adc);
return rom;
}
function rfjet_text_encrypt(rom)
{
encrypt_text(rom, 0xaea754, 0xfe8530, 0xccb666);
return rom;
}
function rfjet_bg_encrypt(rom)
{
encrypt_bg(rom, 0xaea754, 0xfe8530, 0xccb666);
return rom;
}
return {
seibuspi_text_encrypt: seibuspi_text_encrypt,
seibuspi_bg_encrypt: seibuspi_bg_encrypt,
seibuspi_sprite_encrypt: seibuspi_sprite_encrypt,
rdft2_text_encrypt: rdft2_text_encrypt,
rdft2_bg_encrypt: rdft2_bg_encrypt,
seibuspi_rise10_sprite_encrypt: seibuspi_rise10_sprite_encrypt,
rfjet_text_encrypt: rfjet_text_encrypt,
rfjet_bg_encrypt: rfjet_bg_encrypt,
seibuspi_rise11_sprite_encrypt_rfjet: seibuspi_rise11_sprite_encrypt_rfjet
};
})();
const NeoGeo = (function() {
// from http://i486.mods.jp/ichild/?page_id=62
// Original author: Imaha486
function deoptimize_sprites(buf)
{
const tmp = Buffer.allocUnsafe(0x80);
for (let i = 0; i < buf.length; i += 0x80) {
tmp.fill(0);
for (let y = 0; y < 0x10; y++) {
let dstData;
dstData = buf[i+(y*8)+0] << 0 |
buf[i+(y*8)+1] << 8 |
buf[i+(y*8)+2] << 16 |
buf[i+(y*8)+3] << 24;
for (let x = 0; x < 8; x++) {
tmp[0x43 | y << 2] |= (dstData >> x*4+3 & 1) << 7-x;
tmp[0x41 | y << 2] |= (dstData >> x*4+2 & 1) << 7-x;
tmp[0x42 | y << 2] |= (dstData >> x*4+1 & 1) << 7-x;
tmp[0x40 | y << 2] |= (dstData >> x*4+0 & 1) << 7-x;
}
dstData = buf[i+(y*8)+4] << 0 |
buf[i+(y*8)+5] << 8 |
buf[i+(y*8)+6] << 16 |
buf[i+(y*8)+7] << 24;
for (let x = 0; x < 8; x++) {
tmp[0x03 | y << 2] |= (dstData >> x*4+3 & 1) << 7-x;
tmp[0x01 | y << 2] |= (dstData >> x*4+2 & 1) << 7-x;
tmp[0x02 | y << 2] |= (dstData >> x*4+1 & 1) << 7-x;
tmp[0x00 | y << 2] |= (dstData >> x*4+0 & 1) << 7-x;
}
}
tmp.copy(buf, i);
}
return buf;
}
function sfix_reorder(bin)
{
const tmp = Buffer.allocUnsafe(32);
for (let i = 0; i < bin.length; i += 32) {
for (let j = 0; j < 8; j++) {
tmp[j+16] = bin[i+4*j+0];
tmp[j+24] = bin[i+4*j+1];
tmp[j+ 0] = bin[i+4*j+2];
tmp[j+ 8] = bin[i+4*j+3];
}
tmp.copy(bin, i);
}
return bin;
}
// from https://github.com/mamedev/mame/blob/6c23897483a0201dd0b65b450253fd9bf8fb8723/src/devices/bus/neogeo/prot_sma.cpp
// license:BSD-3-Clause
// copyright-holders:S. Smith,David Haywood,Fabio Priuli
function mslug3_encrypt_68k(buf)
{
let rom = buf.slice(0x100000);
const tmp = Buffer.allocUnsafe(0x10000);
for (let i = 0; i < 0x800000/2; i += 0x10000/2) {
rom.copy(tmp, 0, i*2, i*2+0x10000);
for (let j = 0; j < 0x10000/2; j++) {
const k = bitswap(j, 2,11,0,14,6,4,13,8,9,3,10,7,5,12,1);
rom[(i+k)*2+0] = tmp[j*2+0];
rom[(i+k)*2+1] = tmp[j*2+1];
}
}
rom = buf;
for (let i = 0; i < 0x0c0000/2; i++) {
const j = bitswap(i, 18,15,2,1,13,3,0,9,6,16,4,11,5,7,12,17,14,10,8);
rom[0x5d0000+j*2+0] = rom[i*2+0];
rom[0x5d0000+j*2+1] = rom[i*2+1];
}
rom = buf.slice(0x100000);
for (let i = 0; i < 0x800000/2; i++) {
let w = rom[i*2+0] | rom[i*2+1] << 8;
w = reverse_bitswap(w, 4,11,14,3,1,13,0,7,2,8,12,15,10,9,5,6);
rom[i*2+0] = w >> 0 & 0xff;
rom[i*2+1] = w >> 8 & 0xff;
}
}
// from https://github.com/mamedev/mame/blob/ddb290d5f615019c33c42b8d94e5a5254cabcf33/src/devices/bus/neogeo/prot_cmc.h
// license:BSD-3-Clause
// copyright-holders:S. Smith,David Haywood,Fabio Priuli
const MSLUG3_GFX_KEY = 0xad;
// from https://github.com/mamedev/mame/blob/6c23897483a0201dd0b65b450253fd9bf8fb8723/src/devices/bus/neogeo/prot_cmc.cpp
// license:BSD-3-Clause
// copyright-holders:S. Smith,David Haywood,Fabio Priuli
const CMC42_TYPE0_T03 = [
0xfb, 0x86, 0x9d, 0xf1, 0xbf, 0x80, 0xd5, 0x43, 0xab, 0xb3, 0x9f, 0x6a, 0x33, 0xd9, 0xdb, 0xb6,
0x66, 0x08, 0x69, 0x88, 0xcc, 0xb7, 0xde, 0x49, 0x97, 0x64, 0x1f, 0xa6, 0xc0, 0x2f, 0x52, 0x42,
0x44, 0x5a, 0xf2, 0x28, 0x98, 0x87, 0x96, 0x8a, 0x83, 0x0b, 0x03, 0x61, 0x71, 0x99, 0x6b, 0xb5,
0x1a, 0x8e, 0xfe, 0x04, 0xe1, 0xf7, 0x7d, 0xdd, 0xed, 0xca, 0x37, 0xfc, 0xef, 0x39, 0x72, 0xda,
0xb8, 0xbe, 0xee, 0x7f, 0xe5, 0x31, 0x78, 0xf3, 0x91, 0x9a, 0xd2, 0x11, 0x19, 0xb9, 0x09, 0x4c,
0xfd, 0x6d, 0x2a, 0x4d, 0x65, 0xa1, 0x89, 0xc7, 0x75, 0x50, 0x21, 0xfa, 0x16, 0x00, 0xe9, 0x12,
0x74, 0x2b, 0x1e, 0x4f, 0x14, 0x01, 0x70, 0x3a, 0x4e, 0x3f, 0xf5, 0xf4, 0x1d, 0x3d, 0x15, 0x27,
0xa7, 0xff, 0x45, 0xe0, 0x6e, 0xf9, 0x54, 0xc8, 0x48, 0xad, 0xa5, 0x0a, 0xf6, 0x2d, 0x2c, 0xe2,
0x68, 0x67, 0xd6, 0x85, 0xb4, 0xc3, 0x34, 0xbc, 0x62, 0xd3, 0x5f, 0x84, 0x06, 0x5b, 0x0d, 0x95,
0xea, 0x5e, 0x9e, 0xd4, 0xeb, 0x90, 0x7a, 0x05, 0x81, 0x57, 0xe8, 0x60, 0x2e, 0x20, 0x25, 0x7c,
0x46, 0x0c, 0x93, 0xcb, 0xbd, 0x17, 0x7e, 0xec, 0x79, 0xb2, 0xc2, 0x22, 0x41, 0xb1, 0x10, 0xac,
0xa8, 0xbb, 0x9b, 0x82, 0x4b, 0x9c, 0x8b, 0x07, 0x47, 0x35, 0x24, 0x56, 0x8d, 0xaf, 0xe6, 0x26,
0x40, 0x38, 0xc4, 0x5d, 0x1b, 0xc5, 0xd1, 0x0f, 0x6c, 0x7b, 0xb0, 0xe3, 0xa3, 0x23, 0x6f, 0x58,
0xc1, 0xba, 0xcf, 0xd7, 0xa2, 0xe7, 0xd0, 0x63, 0x5c, 0xf8, 0x73, 0xa0, 0x13, 0xdc, 0x29, 0xcd,
0xc9, 0x76, 0xae, 0x8f, 0xe4, 0x59, 0x30, 0xaa, 0x94, 0x1c, 0x3c, 0x0e, 0x55, 0x92, 0x77, 0x32,
0xc6, 0xce, 0x18, 0x36, 0xdf, 0xa9, 0x8c, 0xd8, 0xa4, 0xf0, 0x3b, 0x51, 0x4a, 0x02, 0x3e, 0x53
];
const CMC42_TYPE0_T12 = [
0x1f, 0xac, 0x4d, 0xcd, 0xca, 0x70, 0x02, 0x6b, 0x18, 0x40, 0x62, 0xb2, 0x3f, 0x9b, 0x5b, 0xef,
0x69, 0x68, 0x71, 0x3b, 0xcb, 0xd4, 0x30, 0xbc, 0x47, 0x72, 0x74, 0x5e, 0x84, 0x4c, 0x1b, 0xdb,
0x6a, 0x35, 0x1d, 0xf5, 0xa1, 0xb3, 0x87, 0x5d, 0x57, 0x28, 0x2f, 0xc4, 0xfd, 0x24, 0x26, 0x36,
0xad, 0xbe, 0x61, 0x63, 0x73, 0xaa, 0x82, 0xee, 0x29, 0xd0, 0xdf, 0x8c, 0x15, 0xb5, 0x96, 0xf3,
0xdd, 0x7e, 0x3a, 0x37, 0x58, 0x7f, 0x0c, 0xfc, 0x0b, 0x07, 0xe8, 0xf7, 0xf4, 0x14, 0xb8, 0x81,
0xb6, 0xd7, 0x1e, 0xc8, 0x85, 0xe6, 0x9d, 0x33, 0x60, 0xc5, 0x95, 0xd5, 0x55, 0x00, 0xa3, 0xb7,
0x7d, 0x50, 0x0d, 0xd2, 0xc1, 0x12, 0xe5, 0xed, 0xd8, 0xa4, 0x9c, 0x8f, 0x2a, 0x4f, 0xa8, 0x01,
0x52, 0x83, 0x65, 0xea, 0x9a, 0x6c, 0x44, 0x4a, 0xe2, 0xa5, 0x2b, 0x46, 0xe1, 0x34, 0x25, 0xf8,
0xc3, 0xda, 0xc7, 0x6e, 0x48, 0x38, 0x7c, 0x78, 0x06, 0x53, 0x64, 0x16, 0x98, 0x3c, 0x91, 0x42,
0x39, 0xcc, 0xb0, 0xf1, 0xeb, 0x13, 0xbb, 0x05, 0x32, 0x86, 0x0e, 0xa2, 0x0a, 0x9e, 0xfa, 0x66,
0x54, 0x8e, 0xd3, 0xe7, 0x19, 0x20, 0x77, 0xec, 0xff, 0xbd, 0x6d, 0x43, 0x23, 0x03, 0xab, 0x75,
0x3d, 0xcf, 0xd1, 0xde, 0x92, 0x31, 0xa7, 0x45, 0x4b, 0xc2, 0x97, 0xf9, 0x7a, 0x88, 0xd9, 0x1c,
0xe9, 0xe4, 0x10, 0xc9, 0x22, 0x2d, 0x90, 0x76, 0x17, 0x79, 0x04, 0x51, 0x1a, 0x5a, 0x5f, 0x2c,
0x21, 0x6f, 0x3e, 0xe0, 0xf0, 0xbf, 0xd6, 0x94, 0x0f, 0x80, 0x11, 0xa0, 0x5c, 0xa9, 0x49, 0x2e,
0xce, 0xaf, 0xa6, 0x9f, 0x7b, 0x99, 0xb9, 0xb4, 0xe3, 0xfb, 0xf6, 0x27, 0xf2, 0x93, 0xfe, 0x08,
0x67, 0xae, 0x09, 0x89, 0xdc, 0x4e, 0xc6, 0xc0, 0x8a, 0xb1, 0x59, 0x8b, 0x41, 0x56, 0x8d, 0xba
];
const CMC42_TYPE1_T03 = [
0xa9, 0x17, 0xaf, 0x0d, 0x34, 0x6e, 0x53, 0xb6, 0x7f, 0x58, 0xe9, 0x14, 0x5f, 0x55, 0xdb, 0xd4,
0x42, 0x80, 0x99, 0x59, 0xa8, 0x3a, 0x57, 0x5d, 0xd5, 0x6f, 0x4c, 0x68, 0x35, 0x46, 0xa6, 0xe7,
0x7b, 0x71, 0xe0, 0x93, 0xa2, 0x1f, 0x64, 0x21, 0xe3, 0xb1, 0x98, 0x26, 0xab, 0xad, 0xee, 0xe5,
0xbb, 0xd9, 0x1e, 0x2e, 0x95, 0x36, 0xef, 0x23, 0x79, 0x45, 0x04, 0xed, 0x13, 0x1d, 0xf4, 0x85,
0x96, 0xec, 0xc2, 0x32, 0xaa, 0x7c, 0x15, 0xd8, 0xda, 0x92, 0x90, 0x9d, 0xb7, 0x56, 0x6a, 0x66,
0x41, 0xfc, 0x00, 0xf6, 0x50, 0x24, 0xcf, 0xfb, 0x11, 0xfe, 0x82, 0x48, 0x9b, 0x27, 0x1b, 0x67,
0x4e, 0x84, 0x69, 0x97, 0x6d, 0x8c, 0xd2, 0xba, 0x74, 0xf9, 0x8f, 0xa5, 0x54, 0x5c, 0xcd, 0x73,
0x07, 0xd1, 0x01, 0x09, 0xf1, 0x19, 0x3b, 0x5e, 0x87, 0x30, 0x76, 0xcc, 0xc0, 0x5a, 0xa7, 0x49,
0x22, 0xfa, 0x16, 0x02, 0xdf, 0xa4, 0xff, 0xb3, 0x75, 0x33, 0xbd, 0x88, 0x2f, 0xcb, 0x2a, 0x44,
0xb8, 0xbf, 0x1c, 0x0f, 0x81, 0x10, 0x43, 0xb4, 0xc8, 0x7e, 0x9a, 0x25, 0xea, 0x83, 0x4b, 0x38,
0x7a, 0xd7, 0x3d, 0x1a, 0x4f, 0x62, 0x51, 0xc9, 0x47, 0x0e, 0xce, 0x3f, 0xc7, 0x4d, 0x2c, 0xa1,
0x86, 0xb9, 0xc5, 0xca, 0xdd, 0x6b, 0x70, 0x6c, 0x91, 0x9c, 0xbe, 0x0a, 0x9f, 0xf5, 0x94, 0xbc,
0x18, 0x2b, 0x60, 0x20, 0x29, 0xf7, 0xf2, 0x28, 0xc4, 0xa0, 0x0b, 0x65, 0xde, 0x8d, 0x78, 0x12,
0x3e, 0xd0, 0x77, 0x08, 0x8b, 0xae, 0x05, 0x31, 0x3c, 0xd6, 0xa3, 0x89, 0x06, 0xdc, 0x52, 0x72,
0xb0, 0xb5, 0x37, 0xd3, 0xc3, 0x8a, 0xc6, 0xf0, 0xc1, 0x61, 0xfd, 0x4a, 0x5b, 0x7d, 0x9e, 0xf3,
0x63, 0x40, 0x2d, 0xe8, 0xb2, 0xe6, 0x39, 0x03, 0xeb, 0x8e, 0xe1, 0x0c, 0xe4, 0xe2, 0xf8, 0xac
];
const CMC42_TYPE1_T12 = [
0xea, 0xe6, 0x5e, 0xa7, 0x8e, 0xac, 0x34, 0x03, 0x30, 0x97, 0x52, 0x53, 0x76, 0xf2, 0x62, 0x0b,
0x0a, 0xfc, 0x94, 0xb8, 0x67, 0x36, 0x11, 0xbc, 0xae, 0xca, 0xfa, 0x15, 0x04, 0x2b, 0x17, 0xc4,
0x3e, 0x5b, 0x59, 0x01, 0x57, 0xe2, 0xba, 0xb7, 0xd1, 0x3f, 0xf0, 0x6a, 0x9c, 0x2a, 0xcb, 0xa9,
0xe3, 0x2c, 0xc0, 0x0f, 0x46, 0x91, 0x8a, 0xd0, 0x98, 0xc5, 0xa6, 0x1b, 0x96, 0x29, 0x12, 0x09,
0x63, 0xed, 0xe0, 0xa2, 0x86, 0x77, 0xbe, 0xe5, 0x65, 0xdb, 0xbd, 0x50, 0xb3, 0x9d, 0x1a, 0x4e,
0x79, 0x0c, 0x00, 0x43, 0xdf, 0x3d, 0x54, 0x33, 0x8f, 0x89, 0xa8, 0x7b, 0xf9, 0xd5, 0x27, 0x82,
0xbb, 0xc2, 0x8c, 0x47, 0x88, 0x6b, 0xb4, 0xc3, 0xf8, 0xaa, 0x06, 0x1e, 0x83, 0x7d, 0x05, 0x78,
0x85, 0xf6, 0x6e, 0x2e, 0xec, 0x5a, 0x31, 0x45, 0x38, 0x14, 0x16, 0x8b, 0x02, 0xe4, 0x4f, 0xb0,
0xbf, 0xab, 0xa4, 0x9e, 0x48, 0x60, 0x19, 0x35, 0x08, 0xde, 0xdd, 0x66, 0x90, 0x51, 0xcc, 0xa3,
0xaf, 0x70, 0x9b, 0x75, 0x95, 0x49, 0x6c, 0x64, 0x72, 0x7e, 0x44, 0xa0, 0x73, 0x25, 0x68, 0x55,
0x1f, 0x40, 0x7a, 0x74, 0x0e, 0x8d, 0xdc, 0x1c, 0x71, 0xc8, 0xcf, 0xd7, 0xe8, 0xce, 0xeb, 0x32,
0x3a, 0xee, 0x07, 0x61, 0x4d, 0xfe, 0x5c, 0x7c, 0x56, 0x2f, 0x2d, 0x5f, 0x6f, 0x9f, 0x81, 0x22,
0x58, 0x4b, 0xad, 0xda, 0xb9, 0x10, 0x18, 0x23, 0xe1, 0xf3, 0x6d, 0xe7, 0xe9, 0x28, 0xd6, 0xd8,
0xf4, 0x4c, 0x39, 0x21, 0xb2, 0x84, 0xc1, 0x24, 0x26, 0xf1, 0x93, 0x37, 0xc6, 0x4a, 0xcd, 0x20,
0xc9, 0xd9, 0xc7, 0xb1, 0xff, 0x99, 0xd4, 0x5d, 0xb5, 0xa1, 0x87, 0x0d, 0x69, 0x92, 0x13, 0x80,
0xd2, 0xd3, 0xfd, 0x1d, 0xf5, 0x3b, 0xa5, 0x7f, 0xef, 0x9a, 0xb6, 0x42, 0xfb, 0x3c, 0xf7, 0x41
];
const CMC42_ADDRESS_8_15_XOR1 = [
0x00, 0xb1, 0x1e, 0xc5, 0x3d, 0x40, 0x45, 0x5e, 0xf2, 0xf8, 0x04, 0x63, 0x36, 0x87, 0x88, 0xbf,
0xab, 0xcc, 0x78, 0x08, 0xdd, 0x20, 0xd4, 0x35, 0x09, 0x8e, 0x44, 0xae, 0x33, 0xa9, 0x9e, 0xcd,
0xb3, 0xe5, 0xad, 0x41, 0xda, 0xbe, 0xf4, 0x16, 0x57, 0x2e, 0x53, 0x67, 0xaf, 0xdb, 0x8a, 0xd8,
0x34, 0x17, 0x3c, 0x01, 0x55, 0x73, 0xcf, 0xe3, 0xe8, 0xc7, 0x0d, 0xe9, 0xa3, 0x13, 0x0c, 0xf6,
0x90, 0x4e, 0xfb, 0x97, 0x6d, 0x5f, 0xa8, 0x71, 0x11, 0xfc, 0xd1, 0x95, 0x81, 0xba, 0x8c, 0x1b,
0x39, 0xfe, 0xa2, 0x15, 0xa6, 0x52, 0x4d, 0x5b, 0x59, 0xa5, 0xe0, 0x96, 0xd9, 0x8f, 0x7b, 0xed,
0x29, 0xd3, 0x1f, 0x0e, 0xec, 0x23, 0x0f, 0xb8, 0x6c, 0x6f, 0x7d, 0x18, 0x46, 0xd6, 0xe4, 0xb5,
0x9a, 0x79, 0x02, 0xf5, 0x03, 0xc0, 0x60, 0x66, 0x5c, 0x2f, 0x76, 0x85, 0x9d, 0x54, 0x1a, 0x6a,
0x28, 0xce, 0x7f, 0x7c, 0x91, 0x99, 0x4c, 0x83, 0x3e, 0xb4, 0x1d, 0x05, 0xc1, 0xc3, 0xd7, 0x47,
0xde, 0xbc, 0x62, 0x6e, 0x86, 0x14, 0x80, 0x77, 0xeb, 0xf3, 0x07, 0x31, 0x56, 0xd2, 0xc2, 0xc6,
0x6b, 0xdc, 0xfd, 0x22, 0x92, 0xf0, 0x06, 0x51, 0x2d, 0x38, 0xe6, 0xa0, 0x25, 0xdf, 0xd5, 0x2c,
0x1c, 0x94, 0x12, 0x9c, 0xb0, 0x9b, 0xc4, 0x0b, 0xc8, 0xd0, 0xf7, 0x30, 0xcb, 0x27, 0xfa, 0x7a,
0x10, 0x61, 0xaa, 0xa4, 0x70, 0xb7, 0x2a, 0x5a, 0xc9, 0xf1, 0x0a, 0x49, 0x65, 0xee, 0x69, 0x4b,
0x3a, 0x8d, 0x32, 0x5d, 0x68, 0xb9, 0x9f, 0x75, 0x19, 0x3f, 0xac, 0x37, 0x4f, 0xe7, 0x93, 0x89,
0x7e, 0x4a, 0x3b, 0xea, 0x74, 0x72, 0x43, 0xbd, 0x24, 0xef, 0xb6, 0xff, 0x64, 0x58, 0x84, 0x8b,
0xa7, 0xbb, 0xb2, 0xe1, 0x26, 0x2b, 0x50, 0xca, 0x21, 0xf9, 0x98, 0xa1, 0xe2, 0x42, 0x82, 0x48
];
const CMC42_ADDRESS_8_15_XOR2 = [
0x9b, 0x9d, 0xc1, 0x3d, 0xa9, 0xb8, 0xf4, 0x6f, 0xf6, 0x25, 0xc7, 0x47, 0xd5, 0x97, 0xdf, 0x6b,
0xeb, 0x90, 0xa4, 0xb2, 0x5d, 0xf5, 0x66, 0xb0, 0xb9, 0x8b, 0x93, 0x64, 0xec, 0x7b, 0x65, 0x8c,
0xf1, 0x43, 0x42, 0x6e, 0x45, 0x9f, 0xb3, 0x35, 0x06, 0x71, 0x96, 0xdb, 0xa0, 0xfb, 0x0b, 0x3a,
0x1f, 0xf8, 0x8e, 0x69, 0xcd, 0x26, 0xab, 0x86, 0xa2, 0x0c, 0xbd, 0x63, 0xa5, 0x7a, 0xe7, 0x6a,
0x5f, 0x18, 0x9e, 0xbf, 0xad, 0x55, 0xb1, 0x1c, 0x5c, 0x03, 0x30, 0xc6, 0x37, 0x20, 0xe3, 0xc9,
0x52, 0xe8, 0xee, 0x4f, 0x01, 0x70, 0xc4, 0x77, 0x29, 0x2a, 0xba, 0x53, 0x12, 0x04, 0x7d, 0xaf,
0x33, 0x8f, 0xa8, 0x4d, 0xaa, 0x5b, 0xb4, 0x0f, 0x92, 0xbb, 0xed, 0xe1, 0x2f, 0x50, 0x6c, 0xd2,
0x2c, 0x95, 0xd9, 0xf9, 0x98, 0xc3, 0x76, 0x4c, 0xf2, 0xe4, 0xe5, 0x2b, 0xef, 0x9c, 0x49, 0xb6,
0x31, 0x3b, 0xbc, 0xa1, 0xca, 0xde, 0x62, 0x74, 0xea, 0x81, 0x00, 0xdd, 0xa6, 0x46, 0x88, 0x3f,
0x39, 0xd6, 0x23, 0x54, 0x24, 0x4a, 0xd8, 0xdc, 0xd7, 0xd1, 0xcc, 0xbe, 0x57, 0x7c, 0xda, 0x44,
0x61, 0xce, 0xd3, 0xd4, 0xe9, 0x28, 0x80, 0xe0, 0x56, 0x8a, 0x09, 0x05, 0x9a, 0x89, 0x1b, 0xf7,
0xf3, 0x99, 0x6d, 0x5e, 0x48, 0x91, 0xc0, 0xd0, 0xc5, 0x79, 0x78, 0x41, 0x59, 0x21, 0x2e, 0xff,
0xc2, 0x4b, 0x38, 0x83, 0x32, 0xe6, 0xe2, 0x7f, 0x1e, 0x17, 0x58, 0x1d, 0x1a, 0xfa, 0x85, 0x82,
0x94, 0xc8, 0x72, 0x7e, 0xb7, 0xac, 0x0e, 0xfc, 0xfd, 0x16, 0x27, 0x75, 0x8d, 0xcb, 0x08, 0xfe,
0x0a, 0x02, 0x0d, 0x36, 0x11, 0x22, 0x84, 0x40, 0x34, 0x3e, 0x2d, 0x68, 0x5a, 0xa7, 0x67, 0xae,
0x87, 0x07, 0x10, 0x60, 0x14, 0x73, 0x3c, 0x51, 0x19, 0xa3, 0xb5, 0xcf, 0x13, 0xf0, 0x15, 0x4e
];
const CMC42_ADDRESS_16_23_XOR1 = [
0x00, 0x5f, 0x03, 0x52, 0xce, 0xe3, 0x7d, 0x8f, 0x6b, 0xf8, 0x20, 0xde, 0x7b, 0x7e, 0x39, 0xbe,
0xf5, 0x94, 0x18, 0x78, 0x80, 0xc9, 0x7f, 0x7a, 0x3e, 0x63, 0xf2, 0xe0, 0x4e, 0xf7, 0x87, 0x27,
0x69, 0x6c, 0xa4, 0x1d, 0x85, 0x5b, 0xe6, 0x44, 0x25, 0x0c, 0x98, 0xc7, 0x01, 0x02, 0xa3, 0x26,
0x09, 0x38, 0xdb, 0xc3, 0x1e, 0xcf, 0x23, 0x45, 0x68, 0x76, 0xd6, 0x22, 0x5d, 0x5a, 0xae, 0x16,
0x9f, 0xa2, 0xb5, 0xcd, 0x81, 0xea, 0x5e, 0xb8, 0xb9, 0x9d, 0x9c, 0x1a, 0x0f, 0xff, 0xe1, 0xe7,
0x74, 0xaa, 0xd4, 0xaf, 0xfc, 0xc6, 0x33, 0x29, 0x5c, 0xab, 0x95, 0xf0, 0x19, 0x47, 0x59, 0x67,
0xf3, 0x96, 0x60, 0x1f, 0x62, 0x92, 0xbd, 0x89, 0xee, 0x28, 0x13, 0x06, 0xfe, 0xfa, 0x32, 0x6d,
0x57, 0x3c, 0x54, 0x50, 0x2c, 0x58, 0x49, 0xfb, 0x17, 0xcc, 0xef, 0xb2, 0xb4, 0xf9, 0x07, 0x70,
0xc5, 0xa9, 0xdf, 0xd5, 0x3b, 0x86, 0x2b, 0x0d, 0x6e, 0x4d, 0x0a, 0x90, 0x43, 0x31, 0xc1, 0xf6,
0x88, 0x0b, 0xda, 0x53, 0x14, 0xdc, 0x75, 0x8e, 0xb0, 0xeb, 0x99, 0x46, 0xa1, 0x15, 0x71, 0xc8,
0xe9, 0x3f, 0x4a, 0xd9, 0x73, 0xe5, 0x7c, 0x30, 0x77, 0xd3, 0xb3, 0x4b, 0x37, 0x72, 0xc2, 0x04,
0x97, 0x08, 0x36, 0xb1, 0x3a, 0x61, 0xec, 0xe2, 0x1c, 0x9a, 0x8b, 0xd1, 0x1b, 0x2e, 0x9e, 0x8a,
0xd8, 0x41, 0xe4, 0xc4, 0x40, 0x2f, 0xad, 0xc0, 0xb6, 0x84, 0x51, 0x66, 0xbb, 0x12, 0xe8, 0xdd,
0xcb, 0xbc, 0x6f, 0xd0, 0x11, 0x83, 0x56, 0x4c, 0xca, 0xbf, 0x05, 0x10, 0xd7, 0xba, 0xfd, 0xed,
0x8c, 0x0e, 0x4f, 0x3d, 0x35, 0x91, 0xb7, 0xac, 0x34, 0x64, 0x2a, 0xf1, 0x79, 0x6a, 0x9b, 0x2d,
0x65, 0xf4, 0x42, 0xa0, 0x8d, 0xa7, 0x48, 0x55, 0x21, 0x93, 0x24, 0xd2, 0xa6, 0xa5, 0xa8, 0x82
];
const CMC42_ADDRESS_16_23_XOR2 = [
0x29, 0x97, 0x1a, 0x2c, 0x0b, 0x94, 0x3e, 0x75, 0x01, 0x0d, 0x1b, 0xe1, 0x4d, 0x38, 0x39, 0x8f,
0xe7, 0xd0, 0x60, 0x90, 0xb2, 0x0f, 0xbb, 0x70, 0x1f, 0xe6, 0x5b, 0x87, 0xb4, 0x43, 0xfd, 0xf5,
0xf6, 0xf9, 0xad, 0xc0, 0x98, 0x17, 0x9f, 0x91, 0x15, 0x51, 0x55, 0x64, 0x6c, 0x18, 0x61, 0x0e,
0xd9, 0x93, 0xab, 0xd6, 0x24, 0x2f, 0x6a, 0x3a, 0x22, 0xb1, 0x4f, 0xaa, 0x23, 0x48, 0xed, 0xb9,
0x88, 0x8b, 0xa3, 0x6b, 0x26, 0x4c, 0xe8, 0x2d, 0x1c, 0x99, 0xbd, 0x5c, 0x58, 0x08, 0x50, 0xf2,
0x2a, 0x62, 0xc1, 0x72, 0x66, 0x04, 0x10, 0x37, 0x6e, 0xfc, 0x44, 0xa9, 0xdf, 0xd4, 0x20, 0xdd,
0xee, 0x41, 0xdb, 0x73, 0xde, 0x54, 0xec, 0xc9, 0xf3, 0x4b, 0x2e, 0xae, 0x5a, 0x4a, 0x5e, 0x47,
0x07, 0x2b, 0x76, 0xa4, 0xe3, 0x28, 0xfe, 0xb0, 0xf0, 0x02, 0x06, 0xd1, 0xaf, 0x42, 0xc2, 0xa5,
0xe0, 0x67, 0xbf, 0x16, 0x8e, 0x35, 0xce, 0x8a, 0xe5, 0x3d, 0x7b, 0x96, 0xd7, 0x79, 0x52, 0x1e,
0xa1, 0xfb, 0x9b, 0xbe, 0x21, 0x9c, 0xe9, 0x56, 0x14, 0x7f, 0xa0, 0xe4, 0xc3, 0xc4, 0x46, 0xea,
0xf7, 0xd2, 0x1d, 0x31, 0x0a, 0x5f, 0xeb, 0xa2, 0x68, 0x8d, 0xb5, 0xc5, 0x74, 0x0c, 0xdc, 0x82,
0x80, 0x09, 0x19, 0x95, 0x71, 0x9a, 0x11, 0x57, 0x77, 0x4e, 0xc6, 0xff, 0x12, 0x03, 0xa7, 0xc7,
0xf4, 0xc8, 0xb6, 0x7a, 0x59, 0x36, 0x3c, 0x53, 0xe2, 0x69, 0x8c, 0x25, 0x05, 0x45, 0x63, 0xf8,
0x34, 0x89, 0x33, 0x3f, 0x85, 0x27, 0xbc, 0x65, 0xfa, 0xa8, 0x6d, 0x84, 0x5d, 0xba, 0x40, 0x32,
0x30, 0xef, 0x83, 0x13, 0xa6, 0x78, 0xcc, 0x81, 0x9e, 0xda, 0xca, 0xd3, 0x7e, 0x9d, 0x6f, 0xcd,
0xb7, 0xb3, 0xd8, 0xcf, 0x3b, 0x00, 0x92, 0xb8, 0x86, 0xac, 0x49, 0x7c, 0xf1, 0xd5, 0xcb, 0x7d
];
const CMC42_ADDRESS_0_7_XOR = [
0x74, 0xad, 0x5d, 0x1d, 0x9e, 0xc3, 0xfa, 0x4e, 0xf7, 0xdb, 0xca, 0xa2, 0x64, 0x36, 0x56, 0x0c,
0x4f, 0xcf, 0x43, 0x66, 0x1e, 0x91, 0xe3, 0xa5, 0x58, 0xc2, 0xc1, 0xd4, 0xb9, 0xdd, 0x76, 0x16,
0xce, 0x61, 0x75, 0x01, 0x2b, 0x22, 0x38, 0x55, 0x50, 0xef, 0x6c, 0x99, 0x05, 0xe9, 0xe8, 0xe0,
0x2d, 0xa4, 0x4b, 0x4a, 0x42, 0xae, 0xba, 0x8c, 0x6f, 0x93, 0x14, 0xbd, 0x71, 0x21, 0xb0, 0x02,
0x15, 0xc4, 0xe6, 0x60, 0xd7, 0x44, 0xfd, 0x85, 0x7e, 0x78, 0x8f, 0x00, 0x81, 0xf1, 0xa7, 0x3b,
0xa0, 0x10, 0xf4, 0x9f, 0x39, 0x88, 0x35, 0x62, 0xcb, 0x19, 0x31, 0x11, 0x51, 0xfb, 0x2a, 0x20,
0x45, 0xd3, 0x7d, 0x92, 0x1b, 0xf2, 0x09, 0x0d, 0x97, 0xa9, 0xb5, 0x3c, 0xee, 0x5c, 0xaf, 0x7b,
0xd2, 0x3a, 0x49, 0x8e, 0xb6, 0xcd, 0xd9, 0xde, 0x8a, 0x29, 0x6e, 0xd8, 0x0b, 0xe1, 0x69, 0x87,
0x1a, 0x96, 0x18, 0xcc, 0xdf, 0xe7, 0xc5, 0xc7, 0xf8, 0x52, 0xc9, 0xf0, 0xb7, 0xe5, 0x33, 0xda,
0x67, 0x9d, 0xa3, 0x03, 0x0e, 0x72, 0x26, 0x79, 0xe2, 0xb8, 0xfc, 0xaa, 0xfe, 0xb4, 0x86, 0xc8,
0xd1, 0xbc, 0x12, 0x08, 0x77, 0xeb, 0x40, 0x8d, 0x04, 0x25, 0x4d, 0x5a, 0x6a, 0x7a, 0x2e, 0x41,
0x65, 0x1c, 0x13, 0x94, 0xb2, 0x63, 0x28, 0x59, 0x5e, 0x9a, 0x30, 0x07, 0xc6, 0xbf, 0x17, 0xf5,
0x0f, 0x89, 0xf3, 0x1f, 0xea, 0x6d, 0xb3, 0xc0, 0x70, 0x47, 0xf9, 0x53, 0xf6, 0xd6, 0x54, 0xed,
0x6b, 0x4c, 0xe4, 0x8b, 0x83, 0x24, 0x90, 0xb1, 0x7c, 0xbb, 0x73, 0xab, 0xd5, 0x2f, 0x5f, 0xec,
0x9c, 0x2c, 0xa8, 0x34, 0x46, 0x37, 0x27, 0xa1, 0x0a, 0x06, 0x80, 0x68, 0x82, 0x32, 0x84, 0xff,
0x48, 0xac, 0x7f, 0x3f, 0x95, 0xdc, 0x98, 0x9b, 0xbe, 0x23, 0x57, 0x3e, 0x5b, 0xd0, 0x3d, 0xa6
];
function encrypt(src, dst, pos0, pos1,
table0hi, table0lo, table1, base, invert, address_0_7_xor)
{
const tmp = table1[(base & 0xff) ^ address_0_7_xor[(base >> 8) & 0xff]];
const xor0 = (table0hi[(base >> 8) & 0xff] & 0xfe) | (tmp & 0x01);
const xor1 = (tmp & 0xfe) | (table0lo[(base >> 8) & 0xff] & 0x01);
if (invert) {
dst[pos1] = src[pos0] ^ xor0;
dst[pos0] = src[pos1] ^ xor1;
}
else {
dst[pos0] = src[pos0] ^ xor0;
dst[pos1] = src[pos1] ^ xor1;
}
}
function gfx_encrypt(rom, extra_xor,
type0_t03, type0_t12, type1_t03, type1_t12,
address_8_15_xor1, address_8_15_xor2,
address_16_23_xor1, address_16_23_xor2,
address_0_7_xor)
{
const buf = Buffer.allocUnsafe(rom.length);
for (let rpos = 0; rpos < rom.length/4; rpos++) {
let baser = rpos;
baser ^= extra_xor;
baser ^= address_8_15_xor1[(baser >> 16) & 0xff] << 8;
baser ^= address_8_15_xor2[baser & 0xff] << 8;
baser ^= address_16_23_xor1[baser & 0xff] << 16;
baser ^= address_16_23_xor2[(baser >> 8) & 0xff] << 16;
baser ^= address_0_7_xor[(baser >> 8) & 0xff];
baser &= (rom.length/4)-1;
buf[4*baser+0] = rom[4*rpos+0];
buf[4*baser+1] = rom[4*rpos+1];
buf[4*baser+2] = rom[4*rpos+2];
buf[4*baser+3] = rom[4*rpos+3];
}
for (let rpos = 0; rpos < rom.length/4; rpos++) {
encrypt(buf, rom, 4*rpos+0, 4*rpos+3,
type0_t03, type0_t12, type1_t03, rpos, (rpos>>8) & 1,
address_0_7_xor);
encrypt(buf, rom, 4*rpos+1, 4*rpos+2,
type0_t12, type0_t03, type1_t12, rpos,
((rpos>>16) ^ address_16_23_xor2[(rpos>>8) & 0xff]) & 1,
address_0_7_xor);
}
}
function cmc42_gfx_encrypt(rom, extra_xor)
{
gfx_encrypt(
rom, extra_xor,
CMC42_TYPE0_T03, CMC42_TYPE0_T12, CMC42_TYPE1_T03, CMC42_TYPE1_T12,
CMC42_ADDRESS_8_15_XOR1, CMC42_ADDRESS_8_15_XOR2,
CMC42_ADDRESS_16_23_XOR1, CMC42_ADDRESS_16_23_XOR2,
CMC42_ADDRESS_0_7_XOR
);
}
return {
deoptimize_sprites: deoptimize_sprites,
sfix_reorder: sfix_reorder,
MSLUG3_GFX_KEY: MSLUG3_GFX_KEY,
mslug3_encrypt_68k: mslug3_encrypt_68k,
cmc42_gfx_encrypt: cmc42_gfx_encrypt
};
})();
// from https://github.com/mamedev/mame/blob/32f4e130ee99038854786024acc3e0871a4f7d0c/src/mame/drivers/ddragon.cpp
// license:BSD-3-Clause
// copyright-holders:Philip Bennett,Carlos A. Lozano, Rob Rosenbrock, Phil Stroffolino, Ernesto Corvi, David Haywood, R. Belmont
const DDRAGON_CHAR_LAYOUT = {
width: 8,
height: 8,
total: [1,1],
planes: 4,
planeoffset: [0, 2, 4, 6],
xoffset: [1, 0, 8*8+1, 8*8+0, 16*8+1, 16*8+0, 24*8+1, 24*8+0],
yoffset: [0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8],
charincrement: 32*8
};
const DDRAGON_TILE_LAYOUT = {
width: 16,
height: 16,
total: [1,2],
planes: 4,
planeoffset: [[1,2,0], [1,2,4], 0, 4],
xoffset: [3, 2, 1, 0, 16*8+3, 16*8+2, 16*8+1, 16*8+0,
32*8+3, 32*8+2, 32*8+1, 32*8+0, 48*8+3, 48*8+2, 48*8+1, 48*8+0],
yoffset: [0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8,
8*8, 9*8, 10*8, 11*8, 12*8, 13*8, 14*8, 15*8],
charincrement: 64*8
};
// from https://github.com/mamedev/mame/blob/b888b8c4edaeccea889b97e1c2df6f914ae6e303/src/mame/drivers/ddragon3.cpp
// license:BSD-3-Clause
// copyright-holders:Bryan McPhail, David Haywood
const WWF_TILE_LAYOUT = {
width: 16,
height: 16,
total: [1,2],
planes: 4,
planeoffset: [8, 0, [1,2,8], [1,2,0]],
xoffset: [0, 1, 2, 3, 4, 5, 6, 7,
32*8+0, 32*8+1, 32*8+2, 32*8+3, 32*8+4, 32*8+5, 32*8+6, 32*8+7],
yoffset: [0*16, 1*16, 2*16, 3*16, 4*16, 5*16, 6*16, 7*16,
16*8, 16*9, 16*10, 16*11, 16*12, 16*13, 16*14, 16*15],
charincrement: 64*8
};
const WWF_SPRITE_LAYOUT = {
width: 16,
height: 16,
total: [1,4],
planes: 4,
planeoffset: [[0,4], [1,4], [2,4], [3,4]],
xoffset: [0, 1, 2, 3, 4, 5, 6, 7,
16*8+0, 16*8+1, 16*8+2, 16*8+3, 16*8+4, 16*8+5, 16*8+6, 16*8+7],
yoffset: [0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8,
8*8, 9*8, 10*8, 11*8, 12*8, 13*8, 14*8, 15*8],
charincrement: 32*8
};
// from https://github.com/mamedev/mame/blob/6c23897483a0201dd0b65b450253fd9bf8fb8723/src/mame/drivers/m72.cpp
// license:BSD-3-Clause
// copyright-holders:Nicola Salmoria
const M72_TILE_LAYOUT = {
width: 8,
height: 8,
total: [1,4],
planes: 4,
planeoffset: [[3,4], [2,4], [1,4], [0,4]],
xoffset: [0, 1, 2, 3, 4, 5, 6, 7],
yoffset: [0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8],
charincrement: 8*8
};
const M72_SPRITE_LAYOUT = {
width: 16,
height: 16,
total: [1,4],
planes: 4,
planeoffset: [[3,4], [2,4], [1,4], [0,4]],
xoffset: [0, 1, 2, 3, 4, 5, 6, 7,
16*8+0, 16*8+1, 16*8+2, 16*8+3, 16*8+4, 16*8+5, 16*8+6, 16*8+7],
yoffset: [0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8,
8*8, 9*8, 10*8, 11*8, 12*8, 13*8, 14*8, 15*8],
charincrement: 32*8
};
// from https://github.com/mamedev/mame/blob/27600407666e5222233ae298c88c9da181266937/src/mame/drivers/vigilant.cpp
// license:BSD-3-Clause
// copyright-holders:Mike Balfour
const VIGILANT_TEXT_LAYOUT = {
width: 8,
height: 8,
total: [1,2],
planes: 4,
planeoffset: [[1,2], [1,2,4], 0, 4],
xoffset: [0,1,2,3, 64+0,64+1,64+2,64+3],
yoffset: [0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8],
charincrement: 128
};
const VIGILANT_SPRITE_LAYOUT = {
width: 16,
height: 16,
total: [1,2],
planes: 4,
planeoffset: [[1,2], [1,2,4], 0, 4],
xoffset: [
0x00*8+0,0x00*8+1,0x00*8+2,0x00*8+3,
0x10*8+0,0x10*8+1,0x10*8+2,0x10*8+3,
0x20*8+0,0x20*8+1,0x20*8+2,0x20*8+3,
0x30*8+0,0x30*8+1,0x30*8+2,0x30*8+3
],
yoffset: [
0x00*8, 0x01*8, 0x02*8, 0x03*8,
0x04*8, 0x05*8, 0x06*8, 0x07*8,
0x08*8, 0x09*8, 0x0A*8, 0x0B*8,
0x0C*8, 0x0D*8, 0x0E*8, 0x0F*8
],
charincrement: 0x40*8
};
const VIGILANT_BACK_LAYOUT = {
width: 32,
height: 1,
total: [1,1],
planes: 4,
planeoffset: [0,2,4,6],
xoffset: [
0*8+1, 0*8, 1*8+1, 1*8, 2*8+1, 2*8, 3*8+1, 3*8, 4*8+1, 4*8, 5*8+1, 5*8,
6*8+1,6*8, 7*8+1,7*8, 8*8+1,8*8, 9*8+1,9*8, 10*8+1,10*8, 11*8+1,11*8,
12*8+1, 12*8, 13*8+1, 13*8, 14*8+1, 14*8, 15*8+1, 15*8
],
yoffset: [0],
charincrement: 16*8
};
// from https://github.com/mamedev/mame/blob/b888b8c4edaeccea889b97e1c2df6f914ae6e303/src/mame/drivers/raiden.cpp
// license:BSD-3-Clause
// copyright-holders:Bryan McPhail
// thanks-to:Oliver Bergmann,Randy Mongenel (for initial CPU core)
const RAIDEN_SPRITE_LAYOUT = {
width: 16,
height: 16,
total: 4096,
planes: 4,
planeoffset: [12, 8, 4, 0],
xoffset: [0,1,2,3, 16,17,18,19,
512+0,512+1,512+2,512+3, 512+8+8,512+9+8,512+10+8,512+11+8],
yoffset: [0*32, 1*32, 2*32, 3*32, 4*32, 5*32, 6*32, 7*32,
8*32, 9*32, 10*32, 11*32, 12*32, 13*32, 14*32, 15*32],
charincrement: 1024
};
// from https://github.com/mamedev/mame/blob/6c23897483a0201dd0b65b450253fd9bf8fb8723/src/mame/drivers/seibuspi.cpp
// license:BSD-3-Clause
// copyright-holders:Ville Linde, hap, Nicola Salmoria
const SPI_CHAR_LAYOUT = {
width: 8,
height: 8,
total: 4096,
planes: 6,
planeoffset: [0, 4, 8, 12, 16, 20],
xoffset: [3, 2, 1, 0, 27, 26, 25, 24],
yoffset: [0*48, 1*48, 2*48, 3*48, 4*48, 5*48, 6*48, 7*48],
charincrement: 6*8*8
};
const SPI_TILE_LAYOUT = {
width: 16,
height: 16,
total: [1,1],
planes: 6,
planeoffset: [0, 4, 8, 12, 16, 20],
xoffset: [3, 2, 1, 0, 27,26,25,24, 51,50,49,48, 75,74,73,72],
yoffset: [0*96, 1*96, 2*96, 3*96, 4*96, 5*96, 6*96, 7*96,
8*96, 9*96, 10*96, 11*96, 12*96, 13*96, 14*96, 15*96],
charincrement: 6*16*16
};
const SPI_SPRITE_LAYOUT = {
width: 16,
height: 16,
total: [1,3],
planes: 6,
planeoffset: [0,8,[1,3,0],[1,3,8],[2,3,0],[2,3,8]],
xoffset: [7,6,5,4,3,2,1,0,23,22,21,20,19,18,17,16],
yoffset: [0*32,1*32,2*32,3*32,4*32,5*32,6*32,7*32,
8*32,9*32,10*32,11*32,12*32,13*32,14*32,15*32],
charincrement: 16*32
};
function ddragon(srcdir)
{
convert_roms('ddragon', srcdir, {
maincpu: {
input: 'ddragon_hd6309.bin',
output: ['21j-1-5.26', '21j-2-3.25', '21j-3.24', '21j-4-1.23']
},
sub: { input: 'ddragon_hd63701.bin', output: '21jm-0.ic55' },
soundcpu: { input: 'ddragon_m6809.bin', output: '21j-0-1' },
gfx1: {
input: {
file: 'ddragon_gfxdata1.bin', layout: DDRAGON_CHAR_LAYOUT
},
output: '21j-5'
},
gfx2: {
input: {
file: 'ddragon_gfxdata2.bin', layout: DDRAGON_TILE_LAYOUT
},
output: ['21j-a', '21j-b', '21j-c', '21j-d',
'21j-e', '21j-f', '21j-g', '21j-h']
},
gfx3: {
input: {
file: 'ddragon_gfxdata3.bin', layout: DDRAGON_TILE_LAYOUT
},
output: ['21j-8', '21j-9', '21j-i', '21j-j']
},
adpcm: { input: 'ddragon_adpcm.bin', output: ['21j-6', '21j-7'] },
proms: {
input: 'proms.bin', output: ['21j-k-0', '21j-l-0'],
transform: bin => split_at(bin, 0x100)
}
});
}
function ddragon2(srcdir)
{
convert_roms('ddragon2', srcdir, {
maincpu: {
input: 'ddragon2_hd6309.bin',
output: ['26a9-04.bin', '26aa-03.bin', '26ab-0.bin', '26ac-0e.63']
},
sub: { input: 'ddragon2_z80sub.bin', output: '26ae-0.bin' },
soundcpu: { input: 'ddragon2_z80sound.bin', output: '26ad-0.bin' },
gfx1: {
input: {
file: 'ddragon2_gfxdata1.bin', layout: DDRAGON_CHAR_LAYOUT
},
output: '26a8-0e.19'
},
gfx2: {
input: {
file: 'ddragon2_gfxdata2.bin', layout: DDRAGON_TILE_LAYOUT
},
output: ['26j0-0.bin', '26j1-0.bin', '26af-0.bin',
'26j2-0.bin', '26j3-0.bin', '26j10-0.bin']
},
gfx3: {
input: {
file: 'ddragon2_gfxdata3.bin', layout: DDRAGON_TILE_LAYOUT
},
output: ['26j4-0.bin', '26j5-0.bin']
},
oki: {
input: 'ddragon2_oki.bin', output: ['26j6-0.bin', '26j7-0.bin']
},
proms: {
input: 'proms.bin', output: 'prom.16',
transform: bin => bin.slice(0x100)
}
});
}
function ddragon3_mame2000(srcdir)
{
convert_roms('ddragon3', srcdir, {
cpu1: {
input: 'ddragon3_m68k.bin', output: ['30a14', '30a15'],
transform: bin => {
const a = interleave(bin);
return [a[1], a[0].slice(0, 0x20000)];
}
},
cpu2: { input: 'ddragon3_z80.bin', output: 'dd3.06' },
gfx1: {
input: { file: 'ddragon3_gfxdata1.bin', layout: WWF_TILE_LAYOUT },
output: ['dd3.e', 'dd3.a', 'dd3.f', 'dd3.b'],
transform: bin => interleave(bin).flatMap(b => split_at(b, 0x40000))
},
gfx2: {
input: { file: 'ddragon3_gfxdata2.bin', layout: WWF_SPRITE_LAYOUT},
output: ['dd3.3e', 'dd3.3d', 'dd3.3c', 'dd3.3b', 'dd3.3a',
'dd3.2e', 'dd3.2d', 'dd3.2c', 'dd3.2b', 'dd3.2a',
'dd3.1e', 'dd3.1d', 'dd3.1c', 'dd3.1b', 'dd3.1a',
'dd3.0e', 'dd3.0d', 'dd3.0c', 'dd3.0b', 'dd3.0a'],
transform: bin =>
split(bin, 0x90000).flatMap(b => split(b, 0x20000))
},
sound1: { input: 'ddragon3_oki.bin', output: ['dd3.j7', 'dd3.j8'] }
});
}
function ddragon3(srcdir)
{
convert_roms('ddragon3', srcdir, {
maincpu: {
input: 'ddragon3_m68k.bin',
output: ['30a15-0.ic79', '30a14-0.ic78'],
transform: bin => {
const a = interleave(bin);
return [a[0].slice(0, 0x20000), a[1]];
}
},
audiocpu: { input: 'ddragon3_z80.bin', output: '30a13-0.ic43' },
gfx1: {
input: { file: 'ddragon3_gfxdata1.bin', layout: WWF_TILE_LAYOUT },
output: ['30j-6.ic5', '30j-4.ic7', '30j-7.ic4', '30j-5.ic6'],
transform: bin => interleave(bin).flatMap(b => split_at(b, 0x40000))
},
gfx2: {
input: { file: 'ddragon3_gfxdata2.bin', layout: WWF_SPRITE_LAYOUT},
output: ['30j-3.ic9', '30a12-0.ic8', '30j-2.ic11', '30a11-0.ic10',
'30j-1.ic13', '30a10-0.ic12', '30j-0.ic15', '30a9-0.ic14'],
transform: bin =>
split(bin, 0x90000).flatMap(b => split_at(b, 0x80000))
},
oki: { input: 'ddragon3_oki.bin', output: '30j-8.ic73' },
// missing
proms: { input: Buffer.alloc(0x100), output: 'mb7114h.ic38' }
});
}
function rtype(srcdir)
{
convert_roms('rtype', srcdir, {
maincpu: {
input: 'RTYPE_CPU.BIN',
output: ['rt_r-l0-b.3b', 'rt_r-l1-b.3c',
'rt_r-h0-b.1b', 'rt_r-h1-b.1c'],
transform: bin =>
interleave(bin).flatMap(b => split(b, 0x10000).slice(0, 2))
},
sprites: {
input: { file: 'RTYPE_GFXDATA1.BIN', layout: M72_SPRITE_LAYOUT },
output: ['rt_r-00.1h', 'rt_r-01.1j', 'rt_r-10.1k', 'rt_r-11.1l',
'rt_r-20.3h', 'rt_r-21.3j', 'rt_r-30.3k', 'rt_r-31.3l'],
transform: bin =>
split(bin, 0x10000).map((b, i) =>
i % 2 == 0 ? b : b.slice(0, 0x08000))
},
gfx2: {
input: { file: 'RTYPE_GFXDATA2.BIN', layout: M72_TILE_LAYOUT },
output: ['rt_b-a0.3c', 'rt_b-a1.3d', 'rt_b-a2.3a', 'rt_b-a3.3e']
},
gfx3: {
input: { file: 'RTYPE_GFXDATA3.BIN', layout: M72_TILE_LAYOUT },
output: ['rt_b-b0.3j', 'rt_b-b1.3k', 'rt_b-b2.3h', 'rt_b-b3.3f']
}
});
}
function rtype2(srcdir)
{
convert_roms('rtype2', srcdir, {
maincpu: {
input: 'RTYPE2_CPU1.BIN',
output: ['rt2-a-l0-d.60', 'rt2-a-l1-d.59',
'rt2-a-h0-d.54', 'rt2-a-h1-d.53'],
transform: bin =>
interleave(bin).flatMap(b => split(b, 0x20000).slice(0, 2))
},
soundcpu: { input: 'RTYPE2_CPU2.BIN', output: 'ic17.4f' },
sprites: {
input: 'RTYPE2_GFX1.BIN',
output: ['ic31.6l', 'ic21.4l', 'ic32.6m', 'ic22.4m']
},
gfx2: {
input: 'RTYPE2_GFX2.BIN',
output: ['ic50.7s', 'ic51.7u', 'ic56.8s', 'ic57.8u',
'ic65.9r', 'ic66.9u', 'ic63.9m', 'ic64.9p']
},
samples: { input: 'RTYPE2_SAMPLES.BIN', output: 'ic14.4c' }
});
}
function airduelm72(srcdir)
{
convert_roms('airduelm72', srcdir, {
maincpu: {
input: 'AIRDUEL_CPU.BIN',
output: ['ad-c-l0.bin', 'ad-c-l3.bin',
'ad-c-h0.bin', 'ad-c-h3.bin'],
transform: bin =>
interleave(bin.slice(0, 0x80000))
.flatMap(b => split(b, 0x20000))
},
sprites: {
input: 'AIRDUEL_GFX1.BIN',
output: ['ad-00.bin', 'ad-10.bin', 'ad-20.bin', 'ad-30.bin']
},
gfx2: {
input: 'AIRDUEL_GFX2.BIN',
output: ['ad-a0.bin', 'ad-a1.bin', 'ad-a2.bin', 'ad-a3.bin']
},
gfx3: {
input: 'AIRDUEL_GFX3.BIN',
output: ['ad-b0.bin', 'ad-b1.bin', 'ad-b2.bin', 'ad-b3.bin']
},
samples: { input: 'AIRDUEL_SAMPLES.BIN', output: 'ad-v0.bin' }
});
}
function bchopper(srcdir)
{
convert_roms('bchopper', srcdir, {
maincpu: {
input: 'BCHOPPER_CPU.BIN',
output: ['c-l0-b.rom', 'c-l1-b.rom', 'c-l3-b.rom',
'c-h0-b.rom', 'c-h1-b.rom', 'c-h3-b.rom'],
transform: bin =>
interleave(bin).flatMap(b => {
var a = split(b, 0x10000);
return [a[0], a[1], a[3]];
})
},
sprites: {
input: 'BCHOPPER_GFX1.BIN',
output: ['c-00-a.rom', 'c-01-b.rom', 'c-10-a.rom', 'c-11-b.rom',
'c-20-a.rom', 'c-21-b.rom', 'c-30-a.rom', 'c-31-b.rom']
},
gfx2: {
input: 'BCHOPPER_GFX2.BIN',
output: ['b-a0-b.rom', 'b-a1-b.rom', 'b-a2-b.rom', 'b-a3-b.rom']
},
gfx3: {
input: 'BCHOPPER_GFX3.BIN',
output: ['b-b0-.rom', 'b-b1-.rom', 'b-b2-.rom', 'b-b3-.rom']
},
samples: { input: 'BCHOPPER_SAMPLES.BIN', output: 'c-v0-b.rom' }
});
}
function bmaster(srcdir)
{
convert_roms('bmaster', srcdir, {
maincpu: {
input: 'BMASTER_CPU1.BIN',
output: ['bm_d-l0-b.5f', 'bm_d-l1-b.5j',
'bm_d-h0-b.5m', 'bm_d-h1-b.5l'],
transform: bin =>
interleave(bin.slice(0, 0xa0000)).flatMap(b => {
return split_at(b, 0x40000);
})
},
soundcpu: {
input: 'BMASTER_CPU2.BIN',
output: ['bm_d-sl0.rom', 'bm_d-sh0.rom'],
transform: interleave
},
gfx1: {
input: 'BMASTER_GFX1.BIN',
output: ['bm_c0.rom', 'bm_c1.rom',
'bm_c2.rom', 'bm_c3.rom']
},
gfx2: {
input: 'BMASTER_GFX2.BIN',
output: ['bm_000.rom', 'bm_010.rom',
'bm_020.rom', 'bm_030.rom']
},
irem: { input: 'BMASTER_SOUND.BIN', output: 'bm_da.rom' }
});
}
function cosmccop(srcdir)
{
convert_roms('cosmccop', srcdir, {
maincpu: {
input: 'COSMCCOP_CPU1.BIN',
output: ['cc-d-l0b.bin', 'cc-d-h0b.bin'],
transform: bin => interleave(bin.slice(0, 0x80000))
},
soundcpu: { input: 'COSMCCOP_CPU2.BIN', output: 'cc-d-sp.bin' },
sprites: {
input: 'COSMCCOP_GFX1.BIN',
output: ['cc-c-00.bin', 'cc-c-10.bin', 'cc-c-20.bin', 'cc-c-30.bin']
},
gfx2: {
input: 'COSMCCOP_GFX2.BIN',
output: ['cc-d-g00.bin', 'cc-d-g10.bin',
'cc-d-g20.bin', 'cc-d-g30.bin']
},
samples: { input: 'COSMCCOP_SAMPLES.BIN', output: 'cc-c-v0.bin' }
});
}
function dbreedm72(srcdir)
{
convert_roms('dbreedm72', srcdir, {
maincpu: {
input: 'DBREED72_CPU.BIN',
output: ['db_c-l3.rom', 'db_c-l0.rom',
'db_c-h3.rom', 'db_c-h0.rom'],
transform: bin =>
interleave(bin).flatMap(b => {
return [b.slice(0, 0x20000), b.slice(0x30000, 0x40000)]
})
},
sprites: {
input: 'DBREED72_GFX1.BIN',
output: ['db_k800m.00', 'db_k801m.10', 'db_k802m.20', 'db_k803m.30']
},
gfx2: {
input: 'DBREED72_GFX2.BIN',
output: ['db_k804m.a0', 'db_k805m.a1', 'db_k806m.a2', 'db_k807m.a3']
},
gfx3: {
input: 'DBREED72_GFX3.BIN',
output: ['db_k804m.b0', 'db_k805m.b1', 'db_k806m.b2', 'db_k807m.b3']
},
samples: { input: 'DBREED72_SAMPLES.BIN', output: 'db_c-v0.rom' }
});
}
function gunforce(srcdir)
{
convert_roms('gunforce', srcdir, {
maincpu: {
input: 'GUNFORCE_CPU1.BIN',
output: ['gf_l0-c.5f', 'gf_l1-c.5j',
'gf_h0-c.5m', 'gf_h1-c.5l'],
transform: bin =>
interleave(bin.slice(0, 0x80000)).flatMap(b => {
return split_at(b, 0x20000);
})
},
soundcpu: {
input: 'GUNFORCE_CPU2.BIN',
output: ['gf_sl0.rom', 'gf_sh0.rom'],
transform: bin => interleave(bin.slice(0, 0x20000))
},
gfx1: {
input: 'GUNFORCE_GFX1.BIN',
output: ['gf_c0.rom', 'gf_c1.rom',
'gf_c2.rom', 'gf_c3.rom']
},
gfx2: {
input: 'GUNFORCE_GFX2.BIN',
output: ['gf_000.rom', 'gf_010.rom',
'gf_020.rom', 'gf_030.rom']
},
irem: { input: 'GUNFORCE_SOUND.BIN', output: 'gf-da.rom' }
});
}
function gunforc2(srcdir)
{
convert_roms('gunforc2', srcdir, {
maincpu: {
input: 'GUNFORC2_CPU1.BIN',
output: ['a2-l0-a.8h', 'a2-l1-a.8f',
'a2-h0-a.6h', 'a2-h1-a.6f'],
transform: bin =>
interleave(bin).flatMap(b => {
return [b.slice(0, 0x40000), b.slice(0x80000, 0xc0000)];
})
},
soundcpu: {
input: 'GUNFORC2_CPU2.BIN',
output: ['a2_sl0.5l', 'a2_sh0.3l'],
transform: bin => interleave(bin.slice(0, 0x20000))
},
gfx1: {
input: 'GUNFORC2_GFX1.BIN',
output: ['a2_c0.1a', 'a2_c1.1b',
'a2_c2.3a', 'a2_c3.3b']
},
gfx2: {
input: 'GUNFORC2_GFX2.BIN',
output: ['a2_000.8a', 'a2_010.8b',
'a2_020.8c', 'a2_030.8d']
},
irem: { input: 'GUNFORC2_SOUND.BIN', output: 'a2_da.1l' }
});
}
function hharry(srcdir)
{
convert_roms('hharry', srcdir, {
maincpu: {
input: 'HHARRY_CPU1.BIN',
output: ['a-l0-v.rom', 'a-l1-0.rom', 'a-h0-v.rom', 'a-h1-0.rom'],
transform: bin =>
interleave(bin).flatMap(b => {
return [b.slice(0, 0x20000), b.slice(0x30000, 0x40000)]
})
},
soundcpu: { input: 'HHARRY_CPU2.BIN', output: 'a-sp-0.rom' },
sprites: {
input: 'HHARRY_GFX1.BIN',
output: ['hh_00.rom', 'hh_10.rom', 'hh_20.rom', 'hh_30.rom']
},
gfx2: {
input: 'HHARRY_GFX2.BIN',
output: ['hh_a0.rom', 'hh_a1.rom',
'hh_a2.rom', 'hh_a3.rom']
},
samples: { input: 'HHARRY_SAMPLES.BIN', output: 'a-v0-0.rom' }
});
}
function imgfight(srcdir)
{
convert_roms('imgfight', srcdir, {
maincpu: {
input: 'IMGFIGHT_CPU.BIN',
output: ['if-c-l0-a.bin', 'if-c-l3.bin',
'if-c-h0-a.bin', 'if-c-h3.bin'],
transform: bin =>
interleave(bin).flatMap(b => {
return [b.slice(0, 0x10000), b.slice(0x20000, 0x40000)]
})
},
sprites: {
input: 'IMGFIGHT_GFX1.BIN',
output: ['if-c-00.bin', 'if-c-10.bin', 'if-c-20.bin', 'if-c-30.bin']
},
gfx2: {
input: 'IMGFIGHT_GFX2.BIN',
output: ['if-a-a0.bin', 'if-a-a1.bin', 'if-a-a2.bin', 'if-a-a3.bin']
},
gfx3: {
input: 'IMGFIGHT_GFX3.BIN',
output: ['if-a-b0.bin', 'if-a-b1.bin', 'if-a-b2.bin', 'if-a-b3.bin']
},
samples: {
input: 'IMGFIGHT_SAMPLES.BIN',
output: ['if-c-v0.bin', 'if-c-v1.bin']
}
});
}
function inthunt(srcdir)
{
convert_roms('inthunt', srcdir, {
maincpu: {
input: 'INTHUNT_CPU1.BIN',
output: ['ith-l0-d.bin', 'ith-l1-b.bin',
'ith-h0-d.bin', 'ith-h1-b.bin'],
transform: bin =>
interleave(bin.slice(0, 0xc0000)).flatMap(b => {
return split_at(b, 0x40000);
})
},
soundcpu: {
input: 'INTHUNT_CPU2.BIN',
output: ['ith-sl0.rom', 'ith-sh0.rom'],
transform: interleave
},
gfx1: {
input: 'INTHUNT_GFX1.BIN',
output: ['ith_ic26.rom', 'ith_ic25.rom',
'ith_ic24.rom', 'ith_ic23.rom']
},
gfx2: {
input: 'INTHUNT_GFX2.BIN',
output: ['ith_ic34.rom', 'ith_ic35.rom',
'ith_ic36.rom', 'ith_ic37.rom']
},
irem: { input: 'INTHUNT_SOUND.BIN', output: 'ith_ic9.rom' }
});
}
function kungfum(srcdir)
{
convert_roms('kungfum', srcdir, {
maincpu: {
input: 'KUNGFUM_Z80.BIN',
output: ['a-4e-c.bin', 'a-4d-c.bin']
},
irem_audio: {
input: 'KUNGFUM_M6803.BIN',
output: ['a-3e-.bin', 'a-3f-.bin', 'a-3h-.bin']
},
gfx1: {
input: 'KUNGFUM_GFX1.BIN',
output: ['g-4c-a.bin', 'g-4d-a.bin', 'g-4e-a.bin']
},
gfx2: {
input: 'KUNGFUM_GFX2.BIN',
output: ['b-4k-.bin', 'b-4f-.bin', 'b-4l-.bin', 'b-4h-.bin',
'b-3n-.bin', 'b-4n-.bin', 'b-4m-.bin', 'b-3m-.bin',
'b-4c-.bin', 'b-4e-.bin', 'b-4d-.bin', 'b-4a-.bin']
},
spr_height_prom: { input: 'KUNGFUM_SPRH.BIN', output: 'b-5f-.bin' },
color_proms: {
input: 'KUNGFUM_PAL.BIN',
output: ['g-1j-.bin', 'b-1m-.bin',
'g-1f-.bin', 'b-1n-.bin',
'g-1h-.bin', 'b-1l-.bin']
},
// missing
timing: { input: Buffer.alloc(0x100), output: 'b-6f-.bin' }
});
}
function loht(srcdir)
{
convert_roms('loht', srcdir, {
maincpu: {
input: 'LOHT_CPU.BIN',
output: ['tom_c-l0.rom', 'tom_c-l3-',
'tom_c-h0.rom', 'tom_c-h3-'],
transform: bin =>
interleave(bin.slice(0, 0x80000)).flatMap(b => {
return split_at(b, 0x20000);
})
},
sprites: {
input: 'LOHT_GFX1.BIN',
output: ['tom_m53.rom', 'tom_m51.rom', 'tom_m49.rom', 'tom_m47.rom']
},
gfx2: {
input: 'LOHT_GFX2.BIN',
output: ['tom_m21.rom', 'tom_m22.rom', 'tom_m20.rom', 'tom_m23.rom']
},
gfx3: {
input: 'LOHT_GFX3.BIN',
output: ['tom_m26.rom', 'tom_m27.rom', 'tom_m25.rom', 'tom_m24.rom']
},
samples: { input: 'LOHT_SAMPLES.BIN', output: 'tom_m44.rom' }
});
}
function mysticri(srcdir)
{
convert_roms('mysticri', srcdir, {
maincpu: {
input: 'MYSTICRI_CPU1.BIN',
output: ['mr-l0-b.bin', 'mr-l1-b.bin',
'mr-h0-b.bin', 'mr-h1-b.bin'],
transform: bin =>
interleave(bin.slice(0, 0xa0000)).flatMap(b => {
return split_at(b, 0x40000);
})
},
soundcpu: {
input: 'MYSTICRI_CPU2.BIN',
output: ['mr-sl0.bin', 'mr-sh0.bin'],
transform: bin => interleave(bin.slice(0, 0x20000))
},
gfx1: {
input: 'MYSTICRI_GFX1.BIN',
output: ['mr-c0.bin', 'mr-c1.bin',
'mr-c2.bin', 'mr-c3.bin']
},
gfx2: {
input: 'MYSTICRI_GFX2.BIN',
output: ['mr-o00.bin', 'mr-o10.bin',
'mr-o20.bin', 'mr-o30.bin'],
transform: bin => {
var a = split(bin, 0x80000);
return [a[0], a[2], a[4], a[6]];
}
},
irem: { input: 'MYSTICRI_SOUND.BIN', output: 'mr-da.bin' }
});
}
function nspirit_mame2010(srcdir)
{
convert_roms('nspirit', srcdir, {
maincpu: {
input: 'NSPIRIT_CPU.BIN',
output: ['nin-c-l0.rom', 'nin-c-l1.rom',
'nin-c-l2.rom', 'nin-c-l3.rom',
'nin-c-h0.rom', 'nin-c-h1.rom',
'nin-c-h2.rom', 'nin-c-h3.rom'],
transform: bin =>
interleave(bin.slice(0, 0x80000)).flatMap(b => {
return split(b, 0x10000);
})
},
gfx1: {
input: 'NSPIRIT_GFX1.BIN',
output: ['nin-r00.rom', 'nin-r10.rom', 'nin-r20.rom', 'nin-r30.rom']
},
gfx2: {
input: 'NSPIRIT_GFX2.BIN',
output: ['nin-b-a0.rom', 'nin-b-a1.rom',
'nin-b-a2.rom', 'nin-b-a3.rom']
},
gfx3: {
input: 'NSPIRIT_GFX3.BIN',
output: ['nin-b0.rom', 'nin-b1.rom',
'nin-b2.rom', 'nin-b3.rom']
},
samples: { input: 'NSPIRIT_SAMPLES.BIN', output: 'nin-v0.rom' }
});
}
function nspirit(srcdir)
{
convert_roms('nspirit', srcdir, {
maincpu: {
input: 'NSPIRIT_CPU.BIN',
output: ['nin_c-l0.6d', 'nin_c-l1.6c',
'nin_c-l2.6b', 'nin_c-l3.6a',
'nin_c-h0.6h', 'nin_c-h1.6j',
'nin_c-h2.6l', 'nin_c-h3.6m'],
transform: bin =>
interleave(bin.slice(0, 0x80000)).flatMap(b => {
return split(b, 0x10000);
})
},
sprites: {
input: 'NSPIRIT_GFX1.BIN',
output: ['nin-r00.7m', 'nin-r10.7j', 'nin-r20.7f', 'nin-r30.7d']
},
gfx2: {
input: 'NSPIRIT_GFX2.BIN',
output: ['nin_b-a0.4c', 'nin_b-a1.4d',
'nin_b-a2.4b', 'nin_b-a3.4e']
},
gfx3: {
input: 'NSPIRIT_GFX3.BIN',
output: ['b0.4j', 'b1.4k', 'b2.4h', 'b3.4f']
},
samples: { input: 'NSPIRIT_SAMPLES.BIN', output: 'nin-v0.7a' },
// missing
proms: {
input: Buffer.alloc(0x100 * 2),
output: [ 'm72_a-8l.8l', 'm72_a-9l.9l' ]
},
plds: {
input: Buffer.alloc(0x100 * 3),
output: ['nin_c-3f.3f', 'm72_a-3d.3d', 'm72_a-4d.4d']
}
});
}
function rtypeleo(srcdir)
{
convert_roms('rtypeleo', srcdir, {
maincpu: {
input: 'RTYPELEO_CPU1.BIN',
output: ['rtl-l0-c.bin', 'rtl-l1-d.bin',
'rtl-h0-c.bin', 'rtl-h1-d.bin'],
transform: bin =>
interleave(bin.slice(0, 0xc0000)).flatMap(b => {
return split_at(b, 0x40000);
})
},
soundcpu: {
input: 'RTYPELEO_CPU2.BIN',
output: ['rtl-sl0a.bin', 'rtl-sh0a.bin'],
transform: interleave
},
gfx1: {
input: 'RTYPELEO_GFX1.BIN',
output: ['rtl-c0.bin', 'rtl-c1.bin',
'rtl-c2.bin', 'rtl-c3.bin']
},
gfx2: {
input: 'RTYPELEO_GFX2.BIN',
output: ['rtl-000.bin', 'rtl-010.bin',
'rtl-020.bin', 'rtl-030.bin']
},
irem: { input: 'RTYPELEO_SOUND.BIN', output: 'rtl-da.bin' }
});
}
function ssoldier(srcdir)
{
convert_roms('ssoldier', srcdir, {
maincpu: {
input: 'SSOLDIER_CPU1.BIN',
output: ['f3-l0-h.bin', 'f3-l1-a.bin',
'f3-h0-h.bin', 'f3-h1-a.bin'],
transform: bin =>
interleave(bin.slice(0, 0xc0000)).flatMap(b => {
return split_at(b, 0x40000);
})
},
soundcpu: {
input: 'SSOLDIER_CPU2.BIN',
output: ['f3_sl0.sl0', 'f3_sh0.sh0'],
transform: interleave
},
gfx1: {
input: 'SSOLDIER_GFX1.BIN',
output: ['f3_w50.c0', 'f3_w51.c1',
'f3_w52.c2', 'f3_w53.c3'],
transform: bin => split(bin, 0x80000).map(b => b.slice(0, 0x40000))
},
gfx2: {
input: 'SSOLDIER_GFX2.BIN',
output: ['f3_w38.001', 'f3_w40.011',
'f3_w42.021', 'f3_w44.031',
'f3_w37.000', 'f3_w39.010',
'f3_w41.020', 'f3_w43.030'],
transform: bin => interleave(bin).flatMap(b => split(b, 0x100000))
},
irem: { input: 'SSOLDIER_SOUND.BIN', output: 'f3_w95.da' }
});
}
function uccops(srcdir)
{
convert_roms('uccops', srcdir, {
maincpu: {
input: 'UCCOPS_CPU1.BIN',
output: ['uc_l0.rom', 'uc_l1.rom',
'uc_h0.rom', 'uc_h1.rom'],
transform: bin =>
interleave(bin.slice(0, 0xc0000)).flatMap(b => {
return split_at(b, 0x40000);
})
},
soundcpu: {
input: 'UCCOPS_CPU2.BIN',
output: ['uc_sl0.rom', 'uc_sh0.rom'],
transform: interleave
},
gfx1: {
input: 'UCCOPS_GFX1.BIN',
output: ['uc_w38m.rom', 'uc_w39m.rom',
'uc_w40m.rom', 'uc_w41m.rom']
},
gfx2: {
input: 'UCCOPS_GFX2.BIN',
output: ['uc_k16m.rom', 'uc_k17m.rom',
'uc_k18m.rom', 'uc_k19m.rom']
},
irem: { input: 'UCCOPS_SOUND.BIN', output: 'uc_w42.rom' }
});
}
function vigilant_mame2003(srcdir)
{
convert_roms('vigilant', srcdir, {
cpu1: {
input: 'VIGILANT_CPU1.BIN',
output: ['g07_c03.bin', 'j07_c04.bin'],
transform: bin =>
[bin.slice(0, 0x08000), bin.slice(0x10000, 0x20000)]
},
cpu2: { input: 'VIGILANT_CPU2.BIN', output: 'g05_c02.bin' },
gfx1: {
input: { file: 'VIGILANT_GFX1.BIN', layout: VIGILANT_TEXT_LAYOUT },
output: ['f05_c08.bin', 'h05_c09.bin']
},
gfx2: {
input: {
file: 'VIGILANT_GFX2.BIN', layout: VIGILANT_SPRITE_LAYOUT
},
output: ['n07_c12.bin', 'k07_c10.bin',
'o07_c13.bin', 'l07_c11.bin',
't07_c16.bin', 'p07_c14.bin',
'v07_c17.bin', 's07_c15.bin']
},
gfx3: {
input: 'VIGILANT_GFX_BG.BIN',
output: ['d01_c05.bin', 'e01_c06.bin', 'f01_c07.bin'],
transform: bin =>
encode_gfx(vigilant_reorder(bin), VIGILANT_BACK_LAYOUT)
.slice(0, 0x30000)
},
sound1: { input: 'VIGILANT_SAMPLES.BIN', output: 'd04_c01.bin' }
});
}
function vigilantbl(srcdir)
{
convert_roms('vigilantbl', srcdir, {
maincpu: {
input: 'VIGILANT_CPU1.BIN',
output: ['g07_c03.bin', 'j07_c04.bin'],
transform: bin =>
[bin.slice(0, 0x08000), bin.slice(0x10000, 0x20000)]
},
soundcpu: { input: 'VIGILANT_CPU2.BIN', output: 'g05_c02.bin' },
gfx1: {
input: { file: 'VIGILANT_GFX1.BIN', layout: VIGILANT_TEXT_LAYOUT },
output: ['f05_c08.bin', 'h05_c09.bin']
},
gfx2: {
input: {
file: 'VIGILANT_GFX2.BIN', layout: VIGILANT_SPRITE_LAYOUT
},
output: ['n07_c12.bin', 'k07_c10.bin',
'o07_c13.bin', 'l07_c11.bin',
't07_c16.bin', 'p07_c14.bin',
'v07_c17.bin', 's07_c15.bin']
},
gfx3: {
input: 'VIGILANT_GFX_BG.BIN',
output: ['d01_c05.bin', 'e01_c06.bin', 'f01_c07.bin'],
transform: bin =>
encode_gfx(vigilant_reorder(bin), VIGILANT_BACK_LAYOUT)
.slice(0, 0x30000)
},
samples: { input: 'VIGILANT_SAMPLES.BIN', output: 'd04_c01.bin' },
// missing
plds: {
input: Buffer.alloc(0x117 * 3),
output: ['VG_B-8R.ic90', 'VG_B-4M.ic38', 'VG_B-1B.ic1']
}
});
}
function raidenb(srcdir)
{
convert_roms('raidenb', srcdir, {
maincpu: {
input: 'raiden_maincpu.bin',
output: ['1.u0253', '3__(raidenb).u022',
'2.u0252', '4__(raidenb).u023'],
transform: bin => interleave(bin).flatMap(b => split_at(b, 0x10000))
},
sub: {
input: 'raiden_subcpu.bin',
output: ['5__(raidenb).u042', '6__(raidenb).u043'],
transform: interleave
},
audiocpu: {
input: 'raiden_audiocpu.bin',
output: 'rai6.u212',
transform: bin => {
const a = split(bin, 0x8000);
return Buffer.concat([a[0], a[2]]);
}
},
gfx1: {
input: 'raiden_gfx1.bin',
output: ['9', '10']
},
gfx2: {
input: {
file: 'raiden_gfxdata2.bin', layout: RAIDEN_SPRITE_LAYOUT
},
output: 'sei420'
},
gfx3: {
input: {
file: 'raiden_gfxdata3.bin', layout: RAIDEN_SPRITE_LAYOUT
},
output: 'sei430'
},
gfx4: {
input: {
file: 'raiden_gfxdata4.bin', layout: RAIDEN_SPRITE_LAYOUT
},
output: 'sei440'
},
oki: { input: 'raiden_okim6295.bin', output: '7.u203' }
});
}
function rdftj(srcdir)
{
convert_roms('rdftj', srcdir, {
maincpu: {
input: 'rdft_i386.bin',
output: ['gd_1.211', 'gd_2.212', 'gd_3.210', 'gd_4.29'],
transform: bin => interleave(bin, [1,1,1,1])
},
gfx1: { // wrong checksums
input: { file: 'rdft_gfxdata1.bin', layout: SPI_CHAR_LAYOUT },
output: ['gd_5.423', 'gd_6.424', 'gd_7.48'],
transform: bin => {
SPI.seibuspi_text_encrypt(bin);
return interleave(bin, [1,1,1]);
}
},
gfx2: {
input: { file: 'rdft_gfxdata2.bin', layout: SPI_TILE_LAYOUT },
output: ['gd_bg1-d.415', 'gd_bg2-d.416',
'gd_bg1-p.410', 'gd_bg2-p.49'],
transform: bin => {
SPI.seibuspi_bg_encrypt(bin);
return interleave(bin, [2,1])
.flatMap(b => split_at(b, b.length/2));
}
},
gfx3: {
input: { file: 'rdft_gfxdata3.bin', layout: SPI_SPRITE_LAYOUT },
output: ['gd_obj-1.322', 'gd_obj-2.324', 'gd_obj-3.323'],
transform: SPI.seibuspi_sprite_encrypt
},
sound01: {
input: 'rdft_soundrom.bin',
output: ['gd_pcm.217', 'gd_8.216'],
transform: bin => split_at(bin, 0x200000)
},
soundflash1: {
input: Buffer.alloc(0x100000, 0xff),
output: 'flash0_blank_region01.u1053',
transform: bin => { bin[0] = 0x01; return bin; }
}
});
}
function rdft2(srcdir)
{
convert_roms('rdft2', srcdir, {
maincpu: {
input: 'rdft2_i386.bin',
output: ['prg0.tun', 'prg1.bin', 'prg2.bin', 'prg3.bin'],
transform: bin => interleave(bin, [1,1,1,1])
},
gfx1: { // wrong checksums
input: { file: 'rdft2_gfxdata1.bin', layout: SPI_CHAR_LAYOUT },
output: ['fix1.u0518', 'fix0.u0524', 'fixp.u0514'],
transform: bin => {
SPI.rdft2_text_encrypt(bin);
return interleave(bin, [1,1,1]);
}
},
gfx2: {
input: { file: 'rdft2_gfxdata2.bin', layout: SPI_TILE_LAYOUT },
output: ['bg-1d.u0535', 'bg-2d.u0536',
'bg-1p.u0537', 'bg-2p.u0538'],
transform: bin => {
SPI.rdft2_bg_encrypt(bin);
return interleave(bin, [2,1])
.flatMap(b => split_at(b, b.length/2));
}
},
gfx3: {
input: { file: 'rdft2_gfxdata3.bin', layout: SPI_SPRITE_LAYOUT },
output: ['obj3.u0434', 'obj3b.u0433', 'obj1.u0429',
'obj1b.u0430', 'obj2.u0431', 'obj2b.u0432'],
transform: bin => {
SPI.seibuspi_rise10_sprite_encrypt(bin);
return split(bin, 0x600000).flatMap(b => split_at(b, 0x400000));
}
},
sound01: {
input: 'rdft2_soundrom.bin',
output: ['pcm.u0217', 'sound1.u0222'],
transform: bin => split_at(bin, 0x200000)
},
soundflash1: {
input: Buffer.alloc(0x100000, 0xff),
output: 'flash0_blank_region80.u1053',
transform: bin => { bin[0] = 0x80; return bin; }
}
});
}
function rfjet(srcdir)
{
convert_roms('rfjet', srcdir, {
maincpu: {
input: 'rfjet_i386.bin',
output: ['prg0.u0211', 'prg1.u0212', 'prg2.u0221', 'prg3.u0220'],
transform: bin => interleave(bin, [1,1,1,1])
},
gfx1: { // wrong checksums
input: { file: 'rfjet_gfxdata1.bin', layout: SPI_CHAR_LAYOUT },
output: ['fix1.u0518', 'fix0.u0524', 'fixp.u0514'],
transform: bin => {
SPI.rfjet_text_encrypt(bin);
return interleave(bin, [1,1,1]);
}
},
gfx2: {
input: { file: 'rfjet_gfxdata2.bin', layout: SPI_TILE_LAYOUT },
output: ['bg-1d.u0543', 'bg-2d.u0545',
'bg-1p.u0544', 'bg-2p.u0546'],
transform: bin => {
SPI.rfjet_bg_encrypt(bin);
return interleave(bin, [2,1])
.flatMap(b => split_at(b, b.length*2/3));
}
},
gfx3: {
input: { file: 'rfjet_gfxdata3.bin', layout: SPI_SPRITE_LAYOUT },
output: ['obj-1.u0442', 'obj-2.u0443', 'obj-3.u0444'],
transform: bin => SPI.seibuspi_rise11_sprite_encrypt_rfjet(bin)
},
sound01: {
input: 'rfjet_soundrom.bin',
output: ['pcm-d.u0227', 'sound1.u0222'],
transform: bin => split_at(bin, 0x200000)
},
soundflash1: {
input: Buffer.alloc(0x100000, 0xff),
output: 'flash0_blank_region80.u1053',
transform: bin => { bin[0] = 0x80; return bin; }
}
});
}
const NEOGEO_CONFS = {
bstars2: {
id: "041",
rom_size: [ 0x080000, 0x100000, 0x100000 ]
},
blazstar: {
id: "239",
rom_size: [ 0x200000, 0x400000, 0x400000 ]
},
kof97: {
id: "232",
rom_size: [ 0x400000, 0x400000, 0x800000 ]
},
kof98: {
id: "242",
rom_size: [ 0x400000, 0x400000, 0x800000 ],
name: "kof98h",
maincpu: { output: ['242-pn1.p1', '242-p2.sp2'] },
audiocpu: { output: '242-mg1.m1' }
},
mslug: {
id: "201",
rom_size: [ 0x200000, 0x400000, 0x400000 ],
swap_68k: true
},
mslug2: {
id: "241",
rom_size: [ 0x200000, 0x400000, 0x800000 ]
},
mslugx: { // wrong checksums
id: "250",
rom_size: [ 0x400000, 0x400000, 0x800000 ],
maincpu: { output: ['250-p1.p1', '250-p2.ep1'] }
},
mslug3: {
id: "256",
rom_size: [ 0x400000, 0x400000, 0x800000 ],
sma_encrypt: NeoGeo.mslug3_encrypt_68k,
cmc_encrypt_gfx: bin =>
NeoGeo.cmc42_gfx_encrypt(bin, NeoGeo.MSLUG3_GFX_KEY),
maincpu: { output: ['neo-sma', '256-pg1.p1', '256-pg2.p2'] }
},
samsho2: {
id: "063",
rom_size: [ 0x200000, 0x200000, 0x200000 ],
swap_68k: true
},
shocktro: {
id: "238",
rom_size: [ 0x400000, 0x400000, 0x400000 ]
},
twinspri: {
id: "224",
rom_size: [ 0x200000, 0x400000, 0x400000 ],
swap_68k: true
},
fatfursp: { // not tested
id: "058",
rom_size: [ 0x080000, 0x200000, 0x200000 ]
},
lastblad: { // not tested
id: "234",
rom_size: [ 0x400000, 0x400000, 0x800000 ]
},
shocktr2: { // not tested
id: "246",
rom_size: [ 0x400000, 0x400000, 0x800000 ]
}
};
function neogeo(srcdir, name)
{
const conf = NEOGEO_CONFS[name];
const maps_neogeo = {
zoomy: { input: `${name}_zoom_table`, output: '000-lo.lo' }
};
const mainbios = fs.readFileSync(path.join(srcdir, `${name}_bios_m68k`));
const fixed_bios = fs.readFileSync(path.join(srcdir, `${name}_bios_sfix`));
let mainbios_name;
switch (sha1(mainbios)) {
case '5c6bba07d2ec8ac95776aa3511109f5e1e2e92eb':
mainbios_name = 'sp-u2.sp1';
break;
case '1b3b22092f30c4d1b2c15f04d1670eb1e9fbea07':
mainbios_name = 'neo-epo.bin';
break;
}
if (mainbios_name)
maps_neogeo.mainbios = { input: mainbios, output: mainbios_name };
if (fs.existsSync(path.join(srcdir, `${name}_bios_m68k_jap`))) {
maps_neogeo.mainbios_jp = {
input: `${name}_bios_m68k_jap`, output: 'vs-bios.rom'
};
}
if (sha1(fixed_bios) === '3d9c878d6d8e5d47fe58dfbdee31aed5c5b23360') {
maps_neogeo.fixed_bios = {
input: fixed_bios, output: 'sfix.sfix',
transform: NeoGeo.sfix_reorder
};
}
const { id } = conf;
let maincpu = fs.readFileSync(path.join(srcdir, `${name}_game_m68k`));
const ymsnd_size = fs.statSync(path.join(srcdir, `${name}_adpcm`)).size;
const sprites_size = fs.statSync(path.join(srcdir, `${name}_tiles`)).size;
const maincpu_files = [`${id}-p1.p1`];
const ymsnd_files = [];
const sprites_files = [];
if (maincpu.length > conf.rom_size[0]) {
if (maincpu.slice(conf.rom_size[0]).every(b => b == 0))
maincpu = maincpu.slice(0, conf.rom_size[0]);
else
maincpu_files.push(`${id}-p2.sp2`);
}
for (let i = 1; i <= Math.ceil(ymsnd_size/conf.rom_size[1]); i++)
ymsnd_files.push(`${id}-v${i}.v${i}`);
for (let i = 1; i <= Math.ceil(sprites_size/2/conf.rom_size[2])*2; i++)
sprites_files.push(`${id}-c${i}.c${i}`);
const maps_game = {
maincpu: {
input: maincpu, output: maincpu_files,
transform: bin => {
if (conf.swap_68k) {
const a = split_at(bin, bin.length/2);
bin = Buffer.concat([a[1], a[0]]);
}
if ('sma_encrypt' in conf)
conf.sma_encrypt(bin);
const rom_size = conf.rom_size[0];
if (bin.length == rom_size) return bin;
const p1_size = bin.length >= 0x100000 + rom_size ?
0x100000 : bin.length - rom_size;
const a = split_at(bin, p1_size);
if ('sma_encrypt' in conf)
a[0] = a[0].slice(0x0c0000);
return [a[0], ...split(a[1], rom_size)];
}
},
sfix: {
input: `${name}_game_sfix`, output: `${id}-s1.s1`,
transform: NeoGeo.sfix_reorder
},
audiocpu: { input: `${name}_game_z80`, output: `${id}-m1.m1` },
ymsnd: {
input: `${name}_adpcm`, output: ymsnd_files,
transform: bin => split(bin, conf.rom_size[1])
},
sprites: {
input: `${name}_tiles`, output: sprites_files,
transform: bin => {
NeoGeo.deoptimize_sprites(bin);
if ('cmc_encrypt_gfx' in conf)
conf.cmc_encrypt_gfx(bin);
const a = interleave(bin).map(b => split(b, conf.rom_size[2]));
return a[0].flatMap((b, i) => [b, a[1][i]]);
}
}
};
for (const region of ['maincpu', 'audiocpu'])
if (region in conf)
Object.assign(maps_game[region], conf[region]);
if ('cmc_encrypt_gfx' in conf)
delete maps_game.sfix;
convert_roms('neogeo', srcdir, maps_neogeo);
convert_roms(conf.name || name, srcdir, maps_game);
}
function find_neogeo_roms(dir)
{
for (const f of fs.readdirSync(dir))
if (f.endsWith('_game_m68k'))
return f.replace('_game_m68k', '');
return null;
}
const srcdir = process.argv[2] || '';
if (fs.existsSync(path.join(srcdir, 'ddragon_hd6309.bin'))) {
ddragon(srcdir);
ddragon2(srcdir);
ddragon3(srcdir);
}
else if (fs.existsSync(path.join(srcdir, 'RTYPE_CPU.BIN')))
rtype(srcdir);
else if (fs.existsSync(path.join(srcdir, 'RTYPE2_CPU1.BIN')))
rtype2(srcdir);
else if (fs.existsSync(path.join(srcdir, 'AIRDUEL_CPU.BIN'))) {
airduelm72(srcdir);
bchopper(srcdir);
bmaster(srcdir);
cosmccop(srcdir);
dbreedm72(srcdir);
gunforce(srcdir);
gunforc2(srcdir);
hharry(srcdir);
imgfight(srcdir);
inthunt(srcdir);
kungfum(srcdir);
loht(srcdir);
mysticri(srcdir);
nspirit(srcdir);
rtypeleo(srcdir);
ssoldier(srcdir);
uccops(srcdir);
vigilantbl(srcdir);
}
else if (fs.existsSync(path.join(srcdir, 'raiden_maincpu.bin')))
raidenb(srcdir);
else if (fs.existsSync(path.join(srcdir, 'rdft_i386.bin')))
rdftj(srcdir);
else if (fs.existsSync(path.join(srcdir, 'rdft2_i386.bin')))
rdft2(srcdir);
else if (fs.existsSync(path.join(srcdir, 'rfjet_i386.bin')))
rfjet(srcdir);
else if (find_neogeo_roms(srcdir))
neogeo(srcdir, find_neogeo_roms(srcdir));
else
console.log('Usage: node dotemu2mame.js [ROM directory]');
@themazingness
Copy link

Amazing resource. Thank you! I see Karateka (Google Play) isn't supported. It'd be awesome if it were (if it is possible).

@RebelWax
Copy link

Are there any instructions on how to use this or can anyone help? I have the R-Type APK and I've also installed node.js v14.15.1.

I've tried running dotemu2mame.js, however I get the following error.

dotemu2mame js error

Thanks in advance

@RebelWax
Copy link

Never mind, I worked it out

@m0rb
Copy link

m0rb commented Apr 24, 2022

shocktr2 romident:

246-v3.v3 NO MATCH
246-v4.v4 NO MATCH
Out of 14 files, 12 matched, 2 did not match.

dd if=shocktr2_adpcm of=246-v3.v3 bs=2097152 count=1 skip=4
zip -f shocktr2.zip 246-v3.v3

246-v3.v3 should be a 2MB ROM image. 246-v4.v4 was just created iteratively from the dotemu combined rom image.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment