diff options
Diffstat (limited to 'ines2.c')
| -rw-r--r-- | ines2.c | 90 |
1 files changed, 90 insertions, 0 deletions
@@ -0,0 +1,90 @@ + + + +// iNES header fields +#define INES_HEADER_SIZE 16 +#define INES_PRG_SIZE_LSB 4 +#define INES_CHR_SIZE_LSB 5 +#define INES_FLAGS6 6 +#define INES_FLAGS7 7 +#define INES_PRG_CHR_MSB 9 +#define INES_CHR_EXP 8 +#define INES_PRG_EXP 9 + +// Size units +#define PRG_ROM_UNIT 16384 +#define CHR_ROM_UNIT 8192 +#define TRAINER_SIZE 512 + +// Flag masks +#define FLAG6_TRAINER 0x04 +#define FLAG6_FOUR_SCREEN 0x08 +#define FLAG6_VERTICAL_MIRROR 0x01 + +// Mirroring modes +#define MIRROR_HORIZONTAL 0 +#define MIRROR_VERTICAL 1 +#define MIRROR_FOUR_SCREEN 2 + + +static int load_ines2(struct nes_state *state, const char *path) { + FILE *f = fopen(path, "rb"); + if(!f) { + return -1; + } + + uint8_t header[INES_HEADER_SIZE]; + fread(header, 1, INES_HEADER_SIZE, f); + + uint8_t prg_lsb = header[INES_PRG_SIZE_LSB]; + uint8_t chr_lsb = header[INES_CHR_SIZE_LSB]; + uint8_t prg_msb = header[INES_PRG_CHR_MSB] & 0x0f; + uint8_t chr_msb = (header[INES_PRG_CHR_MSB] & 0xf0) >> 4; + + uint32_t prg_size = (prg_msb << 8 | prg_lsb) * PRG_ROM_UNIT; + uint32_t chr_size = (chr_msb << 8 | chr_lsb) * CHR_ROM_UNIT; + + // NES 2.0 exponential format (only if lower byte is 0x0f) + if(prg_lsb == 0x0f) { + prg_size = 1 << header[INES_PRG_EXP]; + } + if(chr_lsb == 0x0f) { + chr_size = 1 << header[INES_CHR_EXP]; + } + + // Extract mapper + uint8_t mapper_low = (header[INES_FLAGS6] >> 4); + uint8_t mapper_high = (header[INES_FLAGS7] & 0xf0); + uint8_t mapper_ext = (header[INES_PRG_CHR_MSB] & 0x0f) << 8; + state->ines.mapper = mapper_low | mapper_high | mapper_ext; + + // Extract mirroring + if(header[INES_FLAGS6] & FLAG6_FOUR_SCREEN) { + state->ines.mirroring = MIRROR_FOUR_SCREEN; + } else if(header[INES_FLAGS6] & FLAG6_VERTICAL_MIRROR) { + state->ines.mirroring = MIRROR_VERTICAL; + } else { + state->ines.mirroring = MIRROR_HORIZONTAL; + } + + state->ines.prg_size = prg_size; + state->ines.chr_size = chr_size; + + // Skip trainer if present + if(header[INES_FLAGS6] & FLAG6_TRAINER) { + fseek(f, TRAINER_SIZE, SEEK_CUR); + } + + // Read PRG + fread(state->rom, 1, prg_size, f); + + // Read CHR if present + if(chr_size > 0) { + fread(state->chrrom, 1, chr_size, f); + } + + fclose(f); + return 0; +} + + |
