/*
* HalfNES by Andrew Hoffman
* Licensed under the GNU GPL Version 3. See LICENSE file
*/
package com.grapeshot.halfnes;
import com.grapeshot.halfnes.mappers.BadMapperException;
import com.grapeshot.halfnes.mappers.Mapper;
public class ROMLoader {
//this is the oldest code in the project... I'm honestly ashamed
//at how it's structured but for now it works.
//TODO: fix this up
//this SHOULD do just enough to figure out the file type
//and the correct mapper for it, and no more.
public String name;
public int prgsize;
public int chrsize;
public Mapper.MirrorType scrolltype;
public Mapper.TVType tvtype;
public int mappertype;
public int submapper;
public int prgoff, chroff;
public boolean savesram = false;
public int[] header;
private final int[] therom;
public ROMLoader(String filename) {
therom = FileUtils.readfromfile(filename);
name = filename;
}
private void ReadHeader(int len) {
// iNES header is 16 bytes, nsf header is 128,
//other headers increasingly large
header = new int[len];
System.arraycopy(therom, 0, header, 0, len);
}
public void parseHeader() throws BadMapperException {
ReadHeader(16);
// decode iNES 1.0 headers
// 1st 4 bytes : $4E $45 $53 $1A
if (header[0] == 'N' && header[1] == 'E'
&& header[2] == 'S' && header[3] == 0x1A) {
//a valid iNES file, proceed
scrolltype = ((header[6] & (utils.BIT3)) != 0)
? Mapper.MirrorType.FOUR_SCREEN_MIRROR
: ((header[6] & (utils.BIT0)) != 0)
? Mapper.MirrorType.V_MIRROR
: Mapper.MirrorType.H_MIRROR;
savesram = ((header[6] & (utils.BIT1)) != 0);
mappertype = (header[6] >> 4);
//detect NES 2.0 format for the rest of the header
if (((header[7] >> 2) & 3) == 2) {
System.err.println("NES 2 format");
//nes 2
//mapper buts 4-7 in byte 7
mappertype += ((header[7] >> 4) << 4);
//mapper bits 8-12 in byte 8
mappertype += ((header[8] & 15) << 8);
//submapper number is the high 4 bits of byte 8
submapper = (header[8] >> 4);
//extra prg and chr bits in byte 9
prgsize = Math.min(therom.length - 16,
16384 * (header[4] + ((header[9] & 15) << 8)));
if (prgsize == 0) {
throw new BadMapperException("No PRG ROM size in header");
}
chrsize = Math.min(therom.length - 16 - prgsize,
8192 * (header[5] + ((header[9] >> 4) << 8)));
//prg ram size in header byte 10
//chr ram size byte 11
//tv type is byte 12
if ((header[12] & 3) == 1) {
//pal mode only rom
tvtype = Mapper.TVType.PAL;
System.err.println("pal");
} else {
//if ntsc only or works on both we'll use ntsc
tvtype = Mapper.TVType.NTSC;
}
//byte 13 is Vs. System palettes that i don't deal with yet
//byte 14 and 15 must be zero
} else {
//nes 1 format, with hacks
prgsize = Math.min(therom.length - 16, 16384 * header[4]);
if (prgsize == 0) {
throw new BadMapperException("No PRG ROM size in header");
//someone made this field zero on a 4mb multicart ROM
//and someone ELSE made this zero for an 8k PRG dump (no-intro)
//so if anyone gets this error make some heuristics to fix it.
//basically no multicarts > 2mb in iNES 1.0 format
}
chrsize = Math.min(therom.length - 16 - prgsize, 8192 * header[5]);
if (header[11] + header[12] + header[13] + header[14]
+ header[15] == 0) {
//only consider upper bytes of mapper # if the end bytes are zero
mappertype += ((header[7] >> 4) << 4);
if (((header[9] & (utils.BIT0)) != 0)) {
//detect tv type though it's not really used
tvtype = Mapper.TVType.PAL;
System.err.println("pal header type 1");
} else if ((header[10] & 3) == 2) {
tvtype = Mapper.TVType.PAL;
System.err.println("pal header type 2");
} else {
tvtype = Mapper.TVType.NTSC;
}
} else {
System.err.println("diskdude (please clean your roms)");
tvtype = Mapper.TVType.NTSC;
}
}
// calc offsets; header not incl. here
prgoff = 0;
chroff = 0 + prgsize;
} else if (('N' == header[0]) && ('E' == header[1])
&& ('S' == header[2]) && ('M' == header[3])
&& (0x1a == header[4])) {
//nsf file
mappertype = -1;
//reread header since it's 128 bytes
ReadHeader(128);
prgsize = therom.length - 128;
} else if (header[0] == 'U') {
throw new BadMapperException("This is a UNIF file with the wrong extension");
} else {
throw new BadMapperException("iNES Header Invalid");
}
}
public int[] load(int size, int offset) {
int[] bindata = new int[size];
System.arraycopy(therom, offset + header.length, bindata, 0, size);
return bindata;
}
public int romlen() {
return therom.length - header.length;
}
}