diff --git a/src/config.c b/src/config.c index d7e5bac..fe79b86 100644 --- a/src/config.c +++ b/src/config.c @@ -374,18 +374,9 @@ void config_dump_args(Config* config) DEBUG("- debug: %s", config->debug ? "true" : "false") DEBUG("- assemble: %s", config->assemble ? "true" : "false") DEBUG("- disassemble: %s", config->disassemble ? "true" : "false") - if (config->rom_path) - DEBUG("- rom_path: %s", config->rom_path) - else - DEBUG("- rom_path: (null)") - if (config->src_path) - DEBUG("- src_path: %s", config->src_path) - else - DEBUG("- src_path: (null)") - if (config->dst_path) - DEBUG("- dst_path: %s", config->dst_path) - else - DEBUG("- dst_path: (null)") + DEBUG("- rom_path: %s", config->rom_path ? config->rom_path : "(null)") + DEBUG("- src_path: %s", config->src_path ? config->src_path : "(null)") + DEBUG("- dst_path: %s", config->dst_path ? config->dst_path : "(null)") DEBUG("- overwrite: %s", config->overwrite ? "true" : "false") } #endif diff --git a/src/rom.c b/src/rom.c index 7686ea9..7bcebb2 100644 --- a/src/rom.c +++ b/src/rom.c @@ -11,12 +11,13 @@ #include "rom.h" #include "logging.h" +#include "util.h" #define NUM_LOCATIONS 3 #define MAGIC_LEN 8 #define HEADER_SIZE 16 -static size_t header_locations[NUM_LOCATIONS] = {0x7ff0, 0x1ff0, 0x3ff0}; +static size_t header_locations[NUM_LOCATIONS] = {0x7FF0, 0x1FF0, 0x3FF0}; static const char header_magic[MAGIC_LEN + 1] = "TMR SEGA"; /* @@ -35,12 +36,12 @@ static bool validate_size(off_t size) /* DEBUG FUNCTION: Print out the header to stdout. */ -static void print_header(const char *header) +static void print_header(const uint8_t *header) { char header_hex[3 * HEADER_SIZE], header_chr[3 * HEADER_SIZE]; for (int i = 0; i < HEADER_SIZE; i++) { - snprintf(&header_hex[3 * i], 3, "%02x", header[i]); + snprintf(&header_hex[3 * i], 3, "%02X", header[i]); if (isprint(header[i])) snprintf(&header_chr[3 * i], 3, "%2c", header[i]); else { @@ -56,28 +57,74 @@ static void print_header(const char *header) #endif /* - Read a ROM image's header, and return whether or not it is valid. + Parse a ROM image's header, and return whether or not it is valid. + + The header is 16 bytes long, consisting of: + byte 0: magic ('T') + byte 1: magic ('M') + byte 2: magic ('R') + byte 3: magic (' ') + byte 4: magic ('S') + byte 5: magic ('E') + byte 6: magic ('G') + byte 7: magic ('A') + byte 8: unused + byte 9: unused + byte A: checksum (LSB) + byte B: checksum (MSB) + byte C: product code (LSB) + byte D: product code (middle byte) + byte E (hi nibble): product code (most-significant nibble) + byte E (lo nibble): version + byte F (hi nibble): region code + byte F (lo nibble): ROM size + + (Based on: http://www.smspower.org/Development/ROMHeader) */ -static bool read_header(ROM *rom) +static bool parse_header(ROM *rom, const uint8_t *header) +{ +#ifdef DEBUG_MODE + print_header(header); +#endif + + rom->checksum = header[0xA] + (header[0xB] << 8); + rom->product_code = bcd_decode(header[0xC]) + + (bcd_decode(header[0xD]) * 100) + ((header[0xE] >> 4) * 10000); + rom->version = header[0xE] & 0x0F; + rom->region_code = header[0xF] >> 4; + const char* region = rom_region(rom); + + DEBUG("- header info:") + DEBUG(" - checksum: 0x%04X", rom->checksum) + DEBUG(" - product code: %u", rom->product_code) + DEBUG(" - version: %u", rom->version) + DEBUG(" - region code: %u (%s)", rom->region_code, region ? region : "unknown") + return true; +} + +/* + Find and read a ROM image's header, and return whether or not it is valid. +*/ +static bool find_and_read_header(ROM *rom) { size_t location, i; - const char *header; + const uint8_t *header; DEBUG("- looking for header:") for (i = 0; i < NUM_LOCATIONS; i++) { location = header_locations[i]; - DEBUG(" - trying location 0x%zx:", location) + if (location + HEADER_SIZE > rom->size) { + DEBUG(" - skipping location 0x%zX, out of range", location) + continue; + } + DEBUG(" - trying location 0x%zX:", location) header = &rom->data[location]; if (memcmp(header, header_magic, MAGIC_LEN)) { DEBUG(" - magic not present") } else { DEBUG(" - magic found") -#ifdef DEBUG_MODE - print_header(header); -#endif - // TODO: parse header - return true; + return parse_header(rom, header); } } DEBUG(" - could not find header") @@ -116,6 +163,10 @@ const char* rom_open(ROM **rom_ptr, const char *path) rom->name = NULL; rom->data = NULL; rom->size = 0; + rom->checksum = 0; + rom->product_code = 0; + rom->version = 0; + rom->region_code = 0; // Set rom->name: if (!(rom->name = malloc(sizeof(char) * (strlen(path) + 1)))) @@ -133,7 +184,7 @@ const char* rom_open(ROM **rom_ptr, const char *path) rom->size = st.st_size; // Set rom->data: - if (!(rom->data = malloc(sizeof(char) * st.st_size))) + if (!(rom->data = malloc(sizeof(uint8_t) * st.st_size))) OUT_OF_MEMORY() if (!(fread(rom->data, st.st_size, 1, fp))) { rom_close(rom); @@ -143,7 +194,7 @@ const char* rom_open(ROM **rom_ptr, const char *path) fclose(fp); // Parse the header: - if (!read_header(rom)) { + if (!find_and_read_header(rom)) { rom_close(rom); return rom_err_badheader; } @@ -163,3 +214,22 @@ void rom_close(ROM *rom) free(rom->data); free(rom); } + +/* + Return the region this ROM was intended for, based on header information. + + NULL is returned if the region code is invalid. + + Region code information is taken from http://www.smspower.org/Development/ROMHeader. +*/ +const char* rom_region(const ROM *rom) +{ + switch (rom->region_code) { + case 3: return "SMS Japan"; + case 4: return "SMS Export"; + case 5: return "GG Japan"; + case 6: return "GG Export"; + case 7: return "GG International"; + default: return NULL; + } +} diff --git a/src/rom.h b/src/rom.h index efb7269..2b23a20 100644 --- a/src/rom.h +++ b/src/rom.h @@ -2,6 +2,7 @@ Released under the terms of the MIT License. See LICENSE for details. */ #pragma once +#include /* Error strings */ @@ -14,12 +15,17 @@ static const char* rom_err_badheader = "Invalid header"; /* Structs */ typedef struct { - char* name; - char* data; - unsigned size; + char *name; + uint8_t *data; + size_t size; + uint16_t checksum; + uint32_t product_code; + uint8_t version; + uint8_t region_code; } ROM; /* Functions */ const char* rom_open(ROM**, const char*); void rom_close(ROM*); +const char* rom_region(const ROM*); diff --git a/src/util.c b/src/util.c new file mode 100644 index 0000000..b904600 --- /dev/null +++ b/src/util.c @@ -0,0 +1,12 @@ +/* Copyright (C) 2014-2015 Ben Kurtovic + Released under the terms of the MIT License. See LICENSE for details. */ + +#include "util.h" + +/* + Convert a BCD-encoded hexadecimal number to decimal. +*/ +uint8_t bcd_decode(uint8_t num) +{ + return ((num >> 4) * 10) + (num & 0x0F); +} diff --git a/src/util.h b/src/util.h new file mode 100644 index 0000000..744c989 --- /dev/null +++ b/src/util.h @@ -0,0 +1,9 @@ +/* Copyright (C) 2014-2015 Ben Kurtovic + Released under the terms of the MIT License. See LICENSE for details. */ + +#pragma once +#include "stdint.h" + +/* Functions */ + +uint8_t bcd_decode(uint8_t);