summaryrefslogtreecommitdiff
path: root/mknes_ines2.c
diff options
context:
space:
mode:
Diffstat (limited to 'mknes_ines2.c')
-rw-r--r--mknes_ines2.c176
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;
+}
+
+
+