diff options
Diffstat (limited to 'mknes_ines2.c')
| -rw-r--r-- | mknes_ines2.c | 176 |
1 files changed, 176 insertions, 0 deletions
diff --git a/mknes_ines2.c b/mknes_ines2.c new file mode 100644 index 0000000..29ceb71 --- /dev/null +++ b/mknes_ines2.c @@ -0,0 +1,176 @@ +#define USE_LIBARCHIVE + +#ifdef USE_LIBARCHIVE +#include <archive.h> +#include <archive_entry.h> +#endif + +// 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 uint8_t *ines2_read_entire_file(const char *path, size_t *out_size) { + FILE *f = fopen(path, "rb"); + if(!f) return 0; + + fseek(f, 0, SEEK_END); + size_t size = ftell(f); + fseek(f, 0, SEEK_SET); + + uint8_t *buffer = (uint8_t *)malloc(size); + size_t read_size = fread(buffer, 1, size, f); + fclose(f); + + if(read_size != size) { + free(buffer); + return 0; + } + + *out_size = size; + return buffer; +} + +#ifdef USE_LIBARCHIVE +uint8_t *ines2_unzip_file_to_memory(const char *zip_path, size_t *out_size) { + struct archive *a = archive_read_new(); + struct archive_entry *entry; + uint8_t *buffer = 0; + size_t size = 0; + + archive_read_support_format_zip(a); + + if(archive_read_open_filename(a, zip_path, 10240) != ARCHIVE_OK) { + fprintf(stderr, "libarchive: failed to open zip: %s\n", archive_error_string(a)); + archive_read_free(a); + return 0; + } + + if(archive_read_next_header(a, &entry) != ARCHIVE_OK) { + fprintf(stderr, "libarchive: no valid file found in zip\n"); + archive_read_free(a); + return 0; + } + + const char *name = archive_entry_pathname(entry); + + size = archive_entry_size(entry); + buffer = (uint8_t*)malloc(size); + if(!buffer) { + fprintf(stderr, "libarchive: malloc failed\n"); + archive_read_free(a); + return 0; + } + + ssize_t read_size = archive_read_data(a, buffer, size); + if(read_size != (ssize_t)size) { + fprintf(stderr, "libarchive: failed to read full file\n"); + free(buffer); + buffer = 0; + } + + + archive_read_free(a); + + if(buffer && out_size) { + *out_size = size; + } + return buffer; +} +#endif + +static int ines2_load(struct nes_state *state, const char *path) { + uint8_t *data = 0; + size_t size = 0; + +#ifdef USE_LIBARCHIVE + if(strstr(path, ".zip")) { + data = ines2_unzip_file_to_memory(path, &size); + } else { + data = ines2_read_entire_file(path, &size); + } +#else + if(strstr(path, ".zip")) { + fprintf(stderr, "ZIP support not compiled in. Please use .nes files directly.\n"); + return -1; + } + data = ines2_read_entire_file(path, &size); +#endif + + if(!data || size < INES_HEADER_SIZE) { + free(data); + return -1; + } + + uint8_t *ptr = data; + uint8_t *header = ptr; ptr += INES_HEADER_SIZE; + + 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; + + if(prg_lsb == 0x0f) prg_size = 1 << header[INES_PRG_EXP]; + if(chr_lsb == 0x0f) chr_size = 1 << header[INES_CHR_EXP]; + + 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); + uint8_t mapper_upper = header[8] & 0x0f; + uint8_t submapper = (header[8] >> 4); + + state->ines.mapper = mapper_low | mapper_high | (mapper_upper << 8); + state->ines.submapper = submapper; + + 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; + + if(header[INES_FLAGS6] & FLAG6_TRAINER) { + ptr += TRAINER_SIZE; + } + + memcpy(state->prg_rom, ptr, prg_size); + ptr += prg_size; + + if(chr_size > 0) { + memcpy(state->chr_rom, ptr, chr_size); + } + + free(data); + return 0; +} + + + |
