summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--blastem.c82
-rw-r--r--config.c2
-rw-r--r--system.c40
-rw-r--r--system.h12
-rw-r--r--util.c24
-rw-r--r--util.h4
6 files changed, 124 insertions, 40 deletions
diff --git a/blastem.c b/blastem.c
index 4b85b03..e44429a 100644
--- a/blastem.c
+++ b/blastem.c
@@ -49,7 +49,7 @@ tern_node * config;
#define SMD_MAGIC3 0xBB
#define SMD_BLOCK_SIZE 0x4000
-int load_smd_rom(long filesize, FILE * f, uint16_t **buffer)
+int load_smd_rom(long filesize, FILE * f, void **buffer)
{
uint8_t block[SMD_BLOCK_SIZE];
filesize -= SMD_HEADER_SIZE;
@@ -67,7 +67,7 @@ int load_smd_rom(long filesize, FILE * f, uint16_t **buffer)
return rom_size;
}
-int load_rom(char * filename, uint16_t **dst, system_type *stype)
+uint32_t load_rom(char * filename, void **dst, system_type *stype)
{
uint8_t header[10];
FILE * f = fopen(filename, "rb");
@@ -102,9 +102,6 @@ int load_rom(char * filename, uint16_t **dst, system_type *stype)
fatal_error("Error reading from %s\n", filename);
}
fclose(f);
- if (stype) {
- *stype = detect_system_type((uint8_t *)*dst, filesize);
- }
return filesize;
}
@@ -177,12 +174,11 @@ int main(int argc, char ** argv)
int debug = 0;
uint32_t opts = 0;
int loaded = 0;
- system_type stype;
+ system_type stype = SYSTEM_UNKNOWN, force_stype = SYSTEM_UNKNOWN;
uint8_t force_region = 0;
char * romfname = NULL;
char * statefile = NULL;
- int rom_size, lock_on_size;
- uint16_t *cart = NULL, *lock_on = NULL;
+ system_media cart = {.chain = NULL}, lock_on;
debugger_type dtype = DEBUGGER_NATIVE;
uint8_t start_in_debugger = 0;
uint8_t fullscreen = FULLSCREEN_DEFAULT, use_gl = 1;
@@ -235,6 +231,21 @@ int main(int argc, char ** argv)
fatal_error("'%c' is not a valid region character for the -r option\n", argv[i][0]);
}
break;
+ case 'm':
+ i++;
+ if (i >= argc) {
+ fatal_error("-r must be followed by a machine type (sms, gen or jag)\n");
+ }
+ if (!strcmp("sms", argv[i])) {
+ stype = force_stype = SYSTEM_SMS;
+ } else if (!strcmp("gen", argv[i])) {
+ stype = force_stype = SYSTEM_GENESIS;
+ } else if (!strcmp("jag", argv[i])) {
+ stype = force_stype = SYSTEM_JAGUAR;
+ } else {
+ fatal_error("Unrecognized machine type %s\n", argv[i]);
+ }
+ break;
case 's':
i++;
if (i >= argc) {
@@ -253,10 +264,12 @@ int main(int argc, char ** argv)
if (i >= argc) {
fatal_error("-o must be followed by a lock on cartridge filename\n");
}
- lock_on_size = load_rom(argv[i], &lock_on, NULL);
- if (!lock_on_size) {
+ lock_on.size = load_rom(argv[i], &lock_on.buffer, NULL);
+ if (!lock_on.size) {
fatal_error("Failed to load lock on cartridge %s\n", argv[i]);
}
+ lock_on.extension = path_extension(argv[i]);
+ cart.chain = &lock_on;
break;
}
case 'h':
@@ -265,6 +278,10 @@ int main(int argc, char ** argv)
"Options:\n"
" -h Print this help text\n"
" -r (J|U|E) Force region to Japan, US or Europe respectively\n"
+ " -m MACHINE Force emulated machine type to MACHINE. Valid values are:\n"
+ " sms - Sega Master System/Mark III\n"
+ " gen - Sega Genesis/Megadrive\n"
+ " jag - Atari Jaguar\n"
" -f Toggles fullscreen mode\n"
" -g Disable OpenGL rendering\n"
" -s FILE Load a GST format savestate from FILE\n"
@@ -280,9 +297,10 @@ int main(int argc, char ** argv)
fatal_error("Unrecognized switch %s\n", argv[i]);
}
} else if (!loaded) {
- if (!(rom_size = load_rom(argv[i], &cart, &stype))) {
+ if (!(cart.size = load_rom(argv[i], &cart.buffer, stype == SYSTEM_UNKNOWN ? &stype : NULL))) {
fatal_error("Failed to open %s for reading\n", argv[i]);
}
+ cart.extension = path_extension(argv[i]);
romfname = argv[i];
loaded = 1;
} else if (width < 0) {
@@ -299,21 +317,23 @@ int main(int argc, char ** argv)
romfname = "menu.bin";
}
if (is_absolute_path(romfname)) {
- if (!(rom_size = load_rom(romfname, &cart, &stype))) {
+ if (!(cart.size = load_rom(romfname, &cart.buffer, &stype))) {
fatal_error("Failed to open UI ROM %s for reading", romfname);
}
} else {
- long fsize;
- cart = (uint16_t *)read_bundled_file(romfname, &fsize);
- if (!cart) {
+ cart.buffer = (uint16_t *)read_bundled_file(romfname, &cart.size);
+ if (!cart.buffer) {
fatal_error("Failed to open UI ROM %s for reading", romfname);
}
- stype = detect_system_type((uint8_t *)cart, fsize);
- rom_size = nearest_pow2(fsize);
- if (rom_size > fsize) {
- cart = realloc(cart, rom_size);
+ uint32_t rom_size = nearest_pow2(cart.size);
+ if (rom_size > cart.size) {
+ cart.buffer = realloc(cart.buffer, rom_size);
+ cart.size = rom_size;
}
}
+ //force system detection, value on command line is only for games not the menu
+ stype = detect_system_type(&cart);
+ cart.extension = path_extension(romfname);
loaded = 1;
}
@@ -337,10 +357,16 @@ int main(int argc, char ** argv)
render_init(width, height, "BlastEm", fullscreen);
}
+ if (stype == SYSTEM_UNKNOWN) {
+ stype = detect_system_type(&cart);
+ }
+ if (stype == SYSTEM_UNKNOWN) {
+ fatal_error("Failed to detect system type for %s\n", romfname);
+ }
rom_info info;
- current_system = alloc_config_system(stype, cart, rom_size, lock_on, lock_on_size, menu ? 0 : opts, force_region, &info);
+ current_system = alloc_config_system(stype, &cart, menu ? 0 : opts, force_region, &info);
if (!current_system) {
- fatal_error("Failed to detect system type for %s\n", romfname);
+ fatal_error("Failed to configure emulated machine for %s\n", romfname);
}
setup_saves(romfname, &info, current_system);
update_title(info.name);
@@ -369,13 +395,21 @@ int main(int argc, char ** argv)
//start a new arena and save old one in suspended genesis context
current_system->arena = start_new_arena();
}
- if (!(rom_size = load_rom(menu_context->next_rom, &cart, &stype))) {
+ if (!(cart.size = load_rom(menu_context->next_rom, &cart.buffer, &stype))) {
fatal_error("Failed to open %s for reading\n", menu_context->next_rom);
}
+ cart.extension = path_extension(menu_context->next_rom);
+ stype = force_stype;
+ if (stype == SYSTEM_UNKNOWN) {
+ stype = detect_system_type(&cart);
+ }
+ if (stype == SYSTEM_UNKNOWN) {
+ fatal_error("Failed to detect system type for %s\n", menu_context->next_rom);
+ }
//allocate new genesis context
- game_context = alloc_config_system(stype, cart, rom_size, lock_on, lock_on_size, opts,force_region, &info);
+ game_context = alloc_config_system(stype, &cart, opts,force_region, &info);
if (!game_context) {
- fatal_error("Failed to detect system type for %s\n", menu_context->next_rom);
+ fatal_error("Failed to configure emulated machine for %s\n", menu_context->next_rom);
}
menu_context->next_context = game_context;
game_context->next_context = menu_context;
diff --git a/config.c b/config.c
index 9930b73..bf8c5e0 100644
--- a/config.c
+++ b/config.c
@@ -116,7 +116,7 @@ open_fail:
tern_node *parse_bundled_config(char *config_name)
{
- long confsize;
+ uint32_t confsize;
char *confdata = read_bundled_file(config_name, &confsize);
tern_node *ret = NULL;
if (confdata) {
diff --git a/system.c b/system.c
index cef8575..2097bbf 100644
--- a/system.c
+++ b/system.c
@@ -9,24 +9,38 @@ uint8_t safe_cmp(char *str, long offset, uint8_t *buffer, long filesize)
return filesize >= offset+len && !memcmp(str, buffer + offset, len);
}
-system_type detect_system_type(uint8_t *rom, long filesize)
+system_type detect_system_type(system_media *media)
{
- if (safe_cmp("SEGA", 0x100, rom, filesize)) {
+ if (safe_cmp("SEGA", 0x100, media->buffer, media->size)) {
//TODO: Differentiate between vanilla Genesis and Sega CD/32X games
return SYSTEM_GENESIS;
}
- if (safe_cmp("TMR SEGA", 0x1FF0, rom, filesize)
- || safe_cmp("TMR SEGA", 0x3FF0, rom, filesize)
- || safe_cmp("TMR SEGA", 0x7FF0, rom, filesize)
+ if (safe_cmp("TMR SEGA", 0x1FF0, media->buffer, media->size)
+ || safe_cmp("TMR SEGA", 0x3FF0, media->buffer, media->size)
+ || safe_cmp("TMR SEGA", 0x7FF0, media->buffer, media->size)
) {
return SYSTEM_SMS;
}
//TODO: Detect Jaguar ROMs here
+ //Header based detection failed, examine filename for clues
+ if (media->extension) {
+ if (!strcmp("md", media->extension) || !strcmp("gen", media->extension)) {
+ return SYSTEM_GENESIS;
+ }
+ if (!strcmp("sms", media->extension)) {
+ return SYSTEM_SMS;
+ }
+ if (!strcmp("j64", media->extension)) {
+ return SYSTEM_JAGUAR;
+ }
+ }
+
//More certain checks failed, look for a valid 68K reset vector
- if (filesize >= 8) {
+ if (media->size >= 8) {
+ char *rom = media->buffer;
uint32_t reset = rom[4] << 24 | rom[5] << 16 | rom[6] << 8 | rom[7];
- if (!(reset & 1) && reset < filesize) {
+ if (!(reset & 1) && reset < media->size) {
//we have a valid looking reset vector, assume it's a Genesis ROM
return SYSTEM_GENESIS;
}
@@ -34,15 +48,21 @@ system_type detect_system_type(uint8_t *rom, long filesize)
return SYSTEM_UNKNOWN;
}
-system_header *alloc_config_system(system_type stype, void *rom, uint32_t rom_size, void *lock_on, uint32_t lock_on_size, uint32_t opts, uint8_t force_region, rom_info *info_out)
+system_header *alloc_config_system(system_type stype, system_media *media, uint32_t opts, uint8_t force_region, rom_info *info_out)
{
+ void *lock_on = NULL;
+ uint32_t lock_on_size = 0;
+ if (media->chain) {
+ lock_on = media->chain->buffer;
+ lock_on_size = media->chain->size;
+ }
switch (stype)
{
case SYSTEM_GENESIS:
- return &(alloc_config_genesis(rom, rom_size, lock_on, lock_on_size, opts, force_region, info_out))->header;
+ return &(alloc_config_genesis(media->buffer, media->size, lock_on, lock_on_size, opts, force_region, info_out))->header;
#ifndef NO_Z80
case SYSTEM_SMS:
- return &(alloc_configure_sms(rom, rom_size, lock_on, lock_on_size, opts, force_region, info_out))->header;
+ return &(alloc_configure_sms(media->buffer, media->size, lock_on, lock_on_size, opts, force_region, info_out))->header;
#endif
default:
return NULL;
diff --git a/system.h b/system.h
index c1e74d3..d4e7ac3 100644
--- a/system.h
+++ b/system.h
@@ -5,6 +5,7 @@
#include "romdb.h"
typedef struct system_header system_header;
+typedef struct system_media system_media;
typedef enum {
SYSTEM_UNKNOWN,
@@ -45,9 +46,16 @@ struct system_header {
system_type type;
};
+struct system_media {
+ void *buffer;
+ char *extension;
+ system_media *chain;
+ uint32_t size;
+};
+
#define OPT_ADDRESS_LOG (1U << 31U)
-system_type detect_system_type(uint8_t *rom, long filesize);
-system_header *alloc_config_system(system_type stype, void *rom, uint32_t rom_size, void *lock_on, uint32_t lock_on_size, uint32_t opts, uint8_t force_region, rom_info *info_out);
+system_type detect_system_type(system_media *media);
+system_header *alloc_config_system(system_type stype, system_media *media, uint32_t opts, uint8_t force_region, rom_info *info_out);
#endif //SYSTEM_H_
diff --git a/util.c b/util.c
index 2e36b33..59f51b6 100644
--- a/util.c
+++ b/util.c
@@ -149,6 +149,26 @@ char * basename_no_extension(char *path)
return barename;
}
+char *path_extension(char *path)
+{
+ char *lastdot = NULL;
+ char *lastslash = NULL;
+ char *cur;
+ for (cur = path; *cur; cur++)
+ {
+ if (*cur == '.') {
+ lastdot = cur;
+ } else if (is_path_sep(*cur)) {
+ lastslash = cur + 1;
+ }
+ }
+ if (!lastdot || (lastslash && lastslash > lastdot)) {
+ //no extension
+ return NULL;
+ }
+ return strdup(lastdot+1);
+}
+
uint32_t nearest_pow2(uint32_t val)
{
uint32_t ret = 1;
@@ -524,7 +544,7 @@ void free_dir_list(dir_entry *list, size_t numentries)
#ifdef __ANDROID__
#include <SDL.h>
-char *read_bundled_file(char *name, long *sizeret)
+char *read_bundled_file(char *name, uint32_t *sizeret)
{
SDL_RWops *rw = SDL_RWFromFile(name, "rb");
if (!rw) {
@@ -564,7 +584,7 @@ char const *get_save_dir()
#else
-char *read_bundled_file(char *name, long *sizeret)
+char *read_bundled_file(char *name, uint32_t *sizeret)
{
char *exe_dir = get_exe_dir();
if (!exe_dir) {
diff --git a/util.h b/util.h
index 7029d16..c29d040 100644
--- a/util.h
+++ b/util.h
@@ -35,6 +35,8 @@ char is_path_sep(char c);
char is_absolute_path(char *path);
//Returns the basename of a path with th extension (if any) stripped
char * basename_no_extension(char *path);
+//Returns the extension from a path or NULL if there is no extension
+char *path_extension(char *path);
//Gets the smallest power of two that is >= a certain value, won't work for values > 0x80000000
uint32_t nearest_pow2(uint32_t val);
//Should be called by main with the value of argv[0] for use by get_exe_dir
@@ -48,7 +50,7 @@ char const *get_config_dir();
//Returns an appropriate path for saving non-config data like savestates
char const *get_save_dir();
//Reads a file bundled with the executable
-char *read_bundled_file(char *name, long *sizeret);
+char *read_bundled_file(char *name, uint32_t *sizeret);
//Retunrs an array of normal files and directories residing in a directory
dir_entry *get_dir_list(char *path, size_t *numret);
//Frees a dir list returned by get_dir_list