var Mappers = require("./mappers"); var Tile = require("./tile"); var ROM = function (nes) { this.nes = nes; this.mapperName = new Array(92); for (var i = 0; i < 92; i++) { this.mapperName[i] = "Unknown Mapper"; } this.mapperName[0] = "Direct Access"; this.mapperName[1] = "Nintendo MMC1"; this.mapperName[2] = "UNROM"; this.mapperName[3] = "CNROM"; this.mapperName[4] = "Nintendo MMC3"; this.mapperName[5] = "Nintendo MMC5"; this.mapperName[6] = "FFE F4xxx"; this.mapperName[7] = "AOROM"; this.mapperName[8] = "FFE F3xxx"; this.mapperName[9] = "Nintendo MMC2"; this.mapperName[10] = "Nintendo MMC4"; this.mapperName[11] = "Color Dreams Chip"; this.mapperName[12] = "FFE F6xxx"; this.mapperName[15] = "100-in-1 switch"; this.mapperName[16] = "Bandai chip"; this.mapperName[17] = "FFE F8xxx"; this.mapperName[18] = "Jaleco SS8806 chip"; this.mapperName[19] = "Namcot 106 chip"; this.mapperName[20] = "Famicom Disk System"; this.mapperName[21] = "Konami VRC4a"; this.mapperName[22] = "Konami VRC2a"; this.mapperName[23] = "Konami VRC2a"; this.mapperName[24] = "Konami VRC6"; this.mapperName[25] = "Konami VRC4b"; this.mapperName[32] = "Irem G-101 chip"; this.mapperName[33] = "Taito TC0190/TC0350"; this.mapperName[34] = "32kB ROM switch"; this.mapperName[64] = "Tengen RAMBO-1 chip"; this.mapperName[65] = "Irem H-3001 chip"; this.mapperName[66] = "GNROM switch"; this.mapperName[67] = "SunSoft3 chip"; this.mapperName[68] = "SunSoft4 chip"; this.mapperName[69] = "SunSoft5 FME-7 chip"; this.mapperName[71] = "Camerica chip"; this.mapperName[78] = "Irem 74HC161/32-based"; this.mapperName[91] = "Pirate HK-SF3 chip"; }; ROM.prototype = { // Mirroring types: VERTICAL_MIRRORING: 0, HORIZONTAL_MIRRORING: 1, FOURSCREEN_MIRRORING: 2, SINGLESCREEN_MIRRORING: 3, SINGLESCREEN_MIRRORING2: 4, SINGLESCREEN_MIRRORING3: 5, SINGLESCREEN_MIRRORING4: 6, CHRROM_MIRRORING: 7, header: null, rom: null, vrom: null, vromTile: null, romCount: null, vromCount: null, mirroring: null, batteryRam: null, trainer: null, fourScreen: null, mapperType: null, valid: false, load: function (data) { var i, j, v; if (data.indexOf("NES\x1a") === -1) { throw new Error("Not a valid NES ROM."); } this.header = new Array(16); for (i = 0; i < 16; i++) { this.header[i] = data.charCodeAt(i) & 0xff; } this.romCount = this.header[4]; this.vromCount = this.header[5] * 2; // Get the number of 4kB banks, not 8kB this.mirroring = (this.header[6] & 1) !== 0 ? 1 : 0; this.batteryRam = (this.header[6] & 2) !== 0; this.trainer = (this.header[6] & 4) !== 0; this.fourScreen = (this.header[6] & 8) !== 0; this.mapperType = (this.header[6] >> 4) | (this.header[7] & 0xf0); /* TODO if (this.batteryRam) this.loadBatteryRam();*/ // Check whether byte 8-15 are zero's: var foundError = false; for (i = 8; i < 16; i++) { if (this.header[i] !== 0) { foundError = true; break; } } if (foundError) { this.mapperType &= 0xf; // Ignore byte 7 } // Load PRG-ROM banks: this.rom = new Array(this.romCount); var offset = 16; for (i = 0; i < this.romCount; i++) { this.rom[i] = new Array(16384); for (j = 0; j < 16384; j++) { if (offset + j >= data.length) { break; } this.rom[i][j] = data.charCodeAt(offset + j) & 0xff; } offset += 16384; } // Load CHR-ROM banks: this.vrom = new Array(this.vromCount); for (i = 0; i < this.vromCount; i++) { this.vrom[i] = new Array(4096); for (j = 0; j < 4096; j++) { if (offset + j >= data.length) { break; } this.vrom[i][j] = data.charCodeAt(offset + j) & 0xff; } offset += 4096; } // Create VROM tiles: this.vromTile = new Array(this.vromCount); for (i = 0; i < this.vromCount; i++) { this.vromTile[i] = new Array(256); for (j = 0; j < 256; j++) { this.vromTile[i][j] = new Tile(); } } // Convert CHR-ROM banks to tiles: var tileIndex; var leftOver; for (v = 0; v < this.vromCount; v++) { for (i = 0; i < 4096; i++) { tileIndex = i >> 4; leftOver = i % 16; if (leftOver < 8) { this.vromTile[v][tileIndex].setScanline( leftOver, this.vrom[v][i], this.vrom[v][i + 8] ); } else { this.vromTile[v][tileIndex].setScanline( leftOver - 8, this.vrom[v][i - 8], this.vrom[v][i] ); } } } this.valid = true; }, getMirroringType: function () { if (this.fourScreen) { return this.FOURSCREEN_MIRRORING; } if (this.mirroring === 0) { return this.HORIZONTAL_MIRRORING; } return this.VERTICAL_MIRRORING; }, getMapperName: function () { if (this.mapperType >= 0 && this.mapperType < this.mapperName.length) { return this.mapperName[this.mapperType]; } return "Unknown Mapper, " + this.mapperType; }, mapperSupported: function () { return typeof Mappers[this.mapperType] !== "undefined"; }, createMapper: function () { if (this.mapperSupported()) { return new Mappers[this.mapperType](this.nes); } else { throw new Error( "This ROM uses a mapper not supported by JSNES: " + this.getMapperName() + "(" + this.mapperType + ")" ); } }, }; module.exports = ROM;