#define USE_LIBARCHIVE #ifdef USE_LIBARCHIVE #include #include #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; }