# HG changeset patch # User Michael Pavone # Date 1483422386 28800 # Node ID 4490c9c1227224d0c842075e8b7745feeace479d # Parent 160e3f597cecff44c1984ee666af5c7849984e0c Detect system type from filename if header based methods fail. Allow overriding system type from command line. diff -r 160e3f597cec -r 4490c9c12272 blastem.c --- a/blastem.c Mon Jan 02 16:33:03 2017 -0800 +++ b/blastem.c Mon Jan 02 21:46:26 2017 -0800 @@ -49,7 +49,7 @@ #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 @@ 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 @@ 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 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 @@ 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 @@ 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 @@ "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 @@ 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 @@ 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 @@ 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 @@ //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 -r 160e3f597cec -r 4490c9c12272 config.c --- a/config.c Mon Jan 02 16:33:03 2017 -0800 +++ b/config.c Mon Jan 02 21:46:26 2017 -0800 @@ -116,7 +116,7 @@ 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 -r 160e3f597cec -r 4490c9c12272 system.c --- a/system.c Mon Jan 02 16:33:03 2017 -0800 +++ b/system.c Mon Jan 02 21:46:26 2017 -0800 @@ -9,24 +9,38 @@ 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 @@ 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 -r 160e3f597cec -r 4490c9c12272 system.h --- a/system.h Mon Jan 02 16:33:03 2017 -0800 +++ b/system.h Mon Jan 02 21:46:26 2017 -0800 @@ -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 @@ 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 -r 160e3f597cec -r 4490c9c12272 util.c --- a/util.c Mon Jan 02 16:33:03 2017 -0800 +++ b/util.c Mon Jan 02 21:46:26 2017 -0800 @@ -149,6 +149,26 @@ 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 @@ #ifdef __ANDROID__ #include -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 @@ #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 -r 160e3f597cec -r 4490c9c12272 util.h --- a/util.h Mon Jan 02 16:33:03 2017 -0800 +++ b/util.h Mon Jan 02 21:46:26 2017 -0800 @@ -35,6 +35,8 @@ 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 @@ //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