Browse Source

Finish ROM header parsing code.

master
Ben Kurtovic 9 years ago
parent
commit
df05d736c5
5 changed files with 117 additions and 29 deletions
  1. +3
    -12
      src/config.c
  2. +84
    -14
      src/rom.c
  3. +9
    -3
      src/rom.h
  4. +12
    -0
      src/util.c
  5. +9
    -0
      src/util.h

+ 3
- 12
src/config.c View File

@@ -374,18 +374,9 @@ void config_dump_args(Config* config)
DEBUG("- debug: %s", config->debug ? "true" : "false") DEBUG("- debug: %s", config->debug ? "true" : "false")
DEBUG("- assemble: %s", config->assemble ? "true" : "false") DEBUG("- assemble: %s", config->assemble ? "true" : "false")
DEBUG("- disassemble: %s", config->disassemble ? "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") DEBUG("- overwrite: %s", config->overwrite ? "true" : "false")
} }
#endif #endif

+ 84
- 14
src/rom.c View File

@@ -11,12 +11,13 @@


#include "rom.h" #include "rom.h"
#include "logging.h" #include "logging.h"
#include "util.h"


#define NUM_LOCATIONS 3 #define NUM_LOCATIONS 3
#define MAGIC_LEN 8 #define MAGIC_LEN 8
#define HEADER_SIZE 16 #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"; 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. 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]; char header_hex[3 * HEADER_SIZE], header_chr[3 * HEADER_SIZE];


for (int i = 0; i < HEADER_SIZE; i++) { 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])) if (isprint(header[i]))
snprintf(&header_chr[3 * i], 3, "%2c", header[i]); snprintf(&header_chr[3 * i], 3, "%2c", header[i]);
else { else {
@@ -56,28 +57,74 @@ static void print_header(const char *header)
#endif #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; size_t location, i;
const char *header;
const uint8_t *header;


DEBUG("- looking for header:") DEBUG("- looking for header:")
for (i = 0; i < NUM_LOCATIONS; i++) { for (i = 0; i < NUM_LOCATIONS; i++) {
location = header_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]; header = &rom->data[location];
if (memcmp(header, header_magic, MAGIC_LEN)) { if (memcmp(header, header_magic, MAGIC_LEN)) {
DEBUG(" - magic not present") DEBUG(" - magic not present")
} }
else { else {
DEBUG(" - magic found") 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") DEBUG(" - could not find header")
@@ -116,6 +163,10 @@ const char* rom_open(ROM **rom_ptr, const char *path)
rom->name = NULL; rom->name = NULL;
rom->data = NULL; rom->data = NULL;
rom->size = 0; rom->size = 0;
rom->checksum = 0;
rom->product_code = 0;
rom->version = 0;
rom->region_code = 0;


// Set rom->name: // Set rom->name:
if (!(rom->name = malloc(sizeof(char) * (strlen(path) + 1)))) 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; rom->size = st.st_size;


// Set rom->data: // 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() OUT_OF_MEMORY()
if (!(fread(rom->data, st.st_size, 1, fp))) { if (!(fread(rom->data, st.st_size, 1, fp))) {
rom_close(rom); rom_close(rom);
@@ -143,7 +194,7 @@ const char* rom_open(ROM **rom_ptr, const char *path)
fclose(fp); fclose(fp);


// Parse the header: // Parse the header:
if (!read_header(rom)) {
if (!find_and_read_header(rom)) {
rom_close(rom); rom_close(rom);
return rom_err_badheader; return rom_err_badheader;
} }
@@ -163,3 +214,22 @@ void rom_close(ROM *rom)
free(rom->data); free(rom->data);
free(rom); 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;
}
}

+ 9
- 3
src/rom.h View File

@@ -2,6 +2,7 @@
Released under the terms of the MIT License. See LICENSE for details. */ Released under the terms of the MIT License. See LICENSE for details. */


#pragma once #pragma once
#include <stdint.h>


/* Error strings */ /* Error strings */


@@ -14,12 +15,17 @@ static const char* rom_err_badheader = "Invalid header";
/* Structs */ /* Structs */


typedef struct { 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; } ROM;


/* Functions */ /* Functions */


const char* rom_open(ROM**, const char*); const char* rom_open(ROM**, const char*);
void rom_close(ROM*); void rom_close(ROM*);
const char* rom_region(const ROM*);

+ 12
- 0
src/util.c View File

@@ -0,0 +1,12 @@
/* Copyright (C) 2014-2015 Ben Kurtovic <ben.kurtovic@gmail.com>
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);
}

+ 9
- 0
src/util.h View File

@@ -0,0 +1,9 @@
/* Copyright (C) 2014-2015 Ben Kurtovic <ben.kurtovic@gmail.com>
Released under the terms of the MIT License. See LICENSE for details. */

#pragma once
#include "stdint.h"

/* Functions */

uint8_t bcd_decode(uint8_t);

Loading…
Cancel
Save