summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/clifront.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/emu/clifront.c')
-rw-r--r--src/emu/clifront.c977
1 files changed, 977 insertions, 0 deletions
diff --git a/src/emu/clifront.c b/src/emu/clifront.c
new file mode 100644
index 00000000000..ad7c39a74d2
--- /dev/null
+++ b/src/emu/clifront.c
@@ -0,0 +1,977 @@
+/***************************************************************************
+
+ clifront.c
+
+ Command-line interface frontend for MAME.
+
+ Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team.
+ Visit http://mamedev.org for licensing and usage restrictions.
+
+***************************************************************************/
+
+#include "driver.h"
+#include "emuopts.h"
+#include "clifront.h"
+#include "hash.h"
+#include "jedparse.h"
+#include "audit.h"
+#include "info.h"
+#include "unzip.h"
+#include "romload.h"
+#include "sound/samples.h"
+
+#include <ctype.h>
+
+#ifdef MESS
+#include "climess.h"
+#endif /* MESS */
+
+
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+typedef struct _romident_status romident_status;
+struct _romident_status
+{
+ int total; /* total files processed */
+ int matches; /* number of matches found */
+ int nonroms; /* number of non-ROM files found */
+};
+
+
+
+/***************************************************************************
+ FUNCTION PROTOTYPES
+***************************************************************************/
+
+static int execute_simple_commands(core_options *options, const char *exename);
+static int execute_commands(core_options *options, const char *exename, const game_driver *driver);
+static void display_help(void);
+
+/* informational functions */
+static int info_verifyroms(core_options *options, const char *gamename);
+static int info_verifysamples(core_options *options, const char *gamename);
+static int info_romident(core_options *options, const char *gamename);
+
+/* utilities */
+static void romident(const char *filename, romident_status *status);
+static void identify_file(const char *name, romident_status *status);
+static void identify_data(const char *name, const UINT8 *data, int length, romident_status *status);
+static void match_roms(const char *hash, int length, int *found);
+
+
+
+/***************************************************************************
+ COMMAND-LINE OPTIONS
+***************************************************************************/
+
+const options_entry cli_options[] =
+{
+ /* core commands */
+ { NULL, NULL, OPTION_HEADER, "CORE COMMANDS" },
+ { "help;h;?", "0", OPTION_COMMAND, "show help message" },
+ { "validate;valid", "0", OPTION_COMMAND, "perform driver validation on all game drivers" },
+
+ /* configuration commands */
+ { NULL, NULL, OPTION_HEADER, "CONFIGURATION COMMANDS" },
+ { "createconfig;cc", "0", OPTION_COMMAND, "create the default configuration file" },
+ { "showconfig;sc", "0", OPTION_COMMAND, "display running parameters" },
+ { "showusage;su", "0", OPTION_COMMAND, "show this help" },
+
+ /* frontend commands */
+ { NULL, NULL, OPTION_HEADER, "FRONTEND COMMANDS" },
+ { "listxml;lx", "0", OPTION_COMMAND, "all available info on driver in XML format" },
+ { "listfull;ll", "0", OPTION_COMMAND, "short name, full name" },
+ { "listsource;ls", "0", OPTION_COMMAND, "driver sourcefile" },
+ { "listclones;lc", "0", OPTION_COMMAND, "show clones" },
+ { "listcrc", "0", OPTION_COMMAND, "CRC-32s" },
+ { "listroms", "0", OPTION_COMMAND, "list required roms for a driver" },
+ { "listsamples", "0", OPTION_COMMAND, "list optional samples for a driver" },
+ { "verifyroms", "0", OPTION_COMMAND, "report romsets that have problems" },
+ { "verifysamples", "0", OPTION_COMMAND, "report samplesets that have problems" },
+ { "romident", "0", OPTION_COMMAND, "compare files with known MAME roms" },
+#ifdef MESS
+ { "listdevices", "0", OPTION_COMMAND, "list available devices" },
+#endif
+
+ { NULL }
+};
+
+
+
+/***************************************************************************
+ CORE IMPLEMENTATION
+***************************************************************************/
+
+/*-------------------------------------------------
+ cli_execute - execute a game via the standard
+ command line interface
+-------------------------------------------------*/
+
+int cli_execute(int argc, char **argv, const options_entry *osd_options)
+{
+ core_options *options;
+ astring *gamename = astring_alloc();
+ astring *exename = astring_alloc();
+ const game_driver *driver;
+ int result;
+
+ /* initialize the options manager and add the CLI-specific options */
+ options = mame_options_init(osd_options);
+ options_add_entries(options, cli_options);
+
+ /* parse the command line first; if we fail here, we're screwed */
+ if (options_parse_command_line(options, argc, argv, OPTION_PRIORITY_CMDLINE))
+ {
+ result = MAMERR_INVALID_CONFIG;
+ goto error;
+ }
+
+ /* parse the simple commmands before we go any further */
+ core_filename_extract_base(exename, argv[0], TRUE);
+ result = execute_simple_commands(options, astring_c(exename));
+ if (result != -1)
+ goto error;
+
+ /* find out what game we might be referring to */
+ core_filename_extract_base(gamename, options_get_string(options, OPTION_GAMENAME), TRUE);
+ driver = driver_get_name(astring_c(gamename));
+
+ /* execute any commands specified */
+ result = execute_commands(options, astring_c(exename), driver);
+ if (result != -1)
+ goto error;
+
+ /* if we don't have a valid driver selected, offer some suggestions */
+ if (astring_len(gamename) > 0 && driver == NULL)
+ {
+ const game_driver *matches[10];
+ int drvnum;
+
+ /* get the top 10 approximate matches */
+ driver_list_get_approx_matches(drivers, astring_c(gamename), ARRAY_LENGTH(matches), matches);
+
+ /* print them out */
+ fprintf(stderr, "\n\"%s\" approximately matches the following\n"
+ "supported " GAMESNOUN " (best match first):\n\n", astring_c(gamename));
+ for (drvnum = 0; drvnum < ARRAY_LENGTH(matches); drvnum++)
+ if (matches[drvnum] != NULL)
+ fprintf(stderr, "%-10s%s\n", matches[drvnum]->name, matches[drvnum]->description);
+
+ /* exit with an error */
+ result = MAMERR_NO_SUCH_GAME;
+ goto error;
+ }
+
+ /* run the game */
+ result = mame_execute(options);
+
+error:
+ /* free our options and exit */
+ options_free(options);
+ astring_free(gamename);
+ astring_free(exename);
+ return result;
+}
+
+
+/*-------------------------------------------------
+ help_output - output callback for printing
+ requested help information
+-------------------------------------------------*/
+
+static void help_output(const char *s)
+{
+ mame_printf_info("%s", s);
+}
+
+
+/*-------------------------------------------------
+ execute_simple_commands - execute basic
+ commands that don't require any context
+-------------------------------------------------*/
+
+static int execute_simple_commands(core_options *options, const char *exename)
+{
+ /* help? */
+ if (options_get_bool(options, CLIOPTION_HELP))
+ {
+ display_help();
+ return MAMERR_NONE;
+ }
+
+ /* showusage? */
+ if (options_get_bool(options, CLIOPTION_SHOWUSAGE))
+ {
+ mame_printf_info("Usage: %s [%s] [options]\n\nOptions:\n", exename, GAMENOUN);
+ options_output_help(options, help_output);
+ return MAMERR_NONE;
+ }
+
+ /* validate? */
+ if (options_get_bool(options, CLIOPTION_VALIDATE))
+ {
+ extern int mame_validitychecks(const game_driver *driver);
+ return mame_validitychecks(NULL);
+ }
+
+ return -1;
+}
+
+
+/*-------------------------------------------------
+ execute_commands - execute various frontend
+ commands
+-------------------------------------------------*/
+
+static int execute_commands(core_options *options, const char *exename, const game_driver *driver)
+{
+ static const struct
+ {
+ const char *option;
+ int (*function)(core_options *options, const char *gamename);
+ } info_commands[] =
+ {
+ { CLIOPTION_LISTXML, cli_info_listxml },
+ { CLIOPTION_LISTFULL, cli_info_listfull },
+ { CLIOPTION_LISTSOURCE, cli_info_listsource },
+ { CLIOPTION_LISTCLONES, cli_info_listclones },
+ { CLIOPTION_LISTCRC, cli_info_listcrc },
+#ifdef MESS
+ { CLIOPTION_LISTDEVICES, info_listdevices },
+#endif
+ { CLIOPTION_LISTROMS, cli_info_listroms },
+ { CLIOPTION_LISTSAMPLES, cli_info_listsamples },
+ { CLIOPTION_VERIFYROMS, info_verifyroms },
+ { CLIOPTION_VERIFYSAMPLES, info_verifysamples },
+ { CLIOPTION_ROMIDENT, info_romident }
+ };
+ int i;
+
+ /* createconfig? */
+ if (options_get_bool(options, CLIOPTION_CREATECONFIG))
+ {
+ file_error filerr;
+ mame_file *file;
+
+ /* parse any relevant INI files before proceeding */
+ mame_parse_ini_files(options, driver);
+
+ /* make the output filename */
+ filerr = mame_fopen_options(options, NULL, CONFIGNAME ".ini", OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS, &file);
+
+ /* error if unable to create the file */
+ if (filerr != FILERR_NONE)
+ {
+ fprintf(stderr, "Unable to create file " CONFIGNAME ".ini\n");
+ return MAMERR_FATALERROR;
+ }
+
+ /* output the configuration and exit cleanly */
+ options_output_ini_file(options, mame_core_file(file));
+ mame_fclose(file);
+ return MAMERR_NONE;
+ }
+
+ /* showconfig? */
+ if (options_get_bool(options, CLIOPTION_SHOWCONFIG))
+ {
+ /* parse any relevant INI files before proceeding */
+ mame_parse_ini_files(options, driver);
+ options_output_ini_stdfile(options, stdout);
+ return MAMERR_NONE;
+ }
+
+ /* informational commands? */
+ for (i = 0; i < ARRAY_LENGTH(info_commands); i++)
+ if (options_get_bool(options, info_commands[i].option))
+ {
+ const char *gamename = options_get_string(options, OPTION_GAMENAME);
+
+ /* parse any relevant INI files before proceeding */
+ mame_parse_ini_files(options, driver);
+ return (*info_commands[i].function)(options, (gamename[0] == 0) ? "*" : gamename);
+ }
+
+ return -1;
+}
+
+
+/*-------------------------------------------------
+ display_help - display help to standard
+ output
+-------------------------------------------------*/
+
+static void display_help(void)
+{
+#ifndef MESS
+ mame_printf_info("M.A.M.E. v%s - Multiple Arcade Machine Emulator\n"
+ "Copyright (C) 1997-2007 by Nicola Salmoria and the MAME Team\n\n", build_version);
+ mame_printf_info("%s\n", mame_disclaimer);
+ mame_printf_info("Usage: MAME gamename [options]\n\n"
+ " MAME -showusage for a brief list of options\n"
+ " MAME -showconfig for a list of configuration options\n"
+ " MAME -createconfig to create a " CONFIGNAME ".ini\n\n"
+ "For usage instructions, please consult the file windows.txt\n");
+#else
+ mess_display_help();
+#endif
+}
+
+
+
+/***************************************************************************
+ INFORMATIONAL FUNCTIONS
+***************************************************************************/
+
+/*-------------------------------------------------
+ cli_info_listxml - output the XML data for one
+ or more games
+-------------------------------------------------*/
+
+int cli_info_listxml(core_options *options, const char *gamename)
+{
+ /* since print_mame_xml expands the machine driver, we need to set things up */
+ init_resource_tracking();
+ cpuintrf_init(NULL);
+ sndintrf_init(NULL);
+
+ print_mame_xml(stdout, drivers, gamename);
+
+ /* clean up our tracked resources */
+ exit_resource_tracking();
+ return MAMERR_NONE;
+}
+
+
+/*-------------------------------------------------
+ cli_info_listfull - output the name and description
+ of one or more games
+-------------------------------------------------*/
+
+int cli_info_listfull(core_options *options, const char *gamename)
+{
+ int drvindex, count = 0;
+
+ /* iterate over drivers */
+ for (drvindex = 0; drivers[drvindex]; drvindex++)
+ if ((drivers[drvindex]->flags & GAME_NO_STANDALONE) == 0 && mame_strwildcmp(gamename, drivers[drvindex]->name) == 0)
+ {
+ /* print the header on the first one */
+ if (count == 0)
+ mame_printf_info("Name: Description:\n");
+
+ /* output the remaining information */
+ mame_printf_info("%-10s\"%s\"\n", drivers[drvindex]->name, drivers[drvindex]->description);
+ count++;
+ }
+
+ /* return an error if none found */
+ return (count > 0) ? MAMERR_NONE : MAMERR_NO_SUCH_GAME;
+}
+
+
+/*-------------------------------------------------
+ cli_info_listsource - output the name and source
+ filename of one or more games
+-------------------------------------------------*/
+
+int cli_info_listsource(core_options *options, const char *gamename)
+{
+ astring *filename = astring_alloc();
+ int drvindex, count = 0;
+
+ /* iterate over drivers */
+ for (drvindex = 0; drivers[drvindex]; drvindex++)
+ if (mame_strwildcmp(gamename, drivers[drvindex]->name) == 0)
+ {
+ /* output the remaining information */
+ mame_printf_info("%-8s %s\n", drivers[drvindex]->name, astring_c(core_filename_extract_base(filename, drivers[drvindex]->source_file, FALSE)));
+ count++;
+ }
+
+ /* return an error if none found */
+ astring_free(filename);
+ return (count > 0) ? MAMERR_NONE : MAMERR_NO_SUCH_GAME;
+}
+
+
+/*-------------------------------------------------
+ cli_info_listclones - output the name and source
+ filename of one or more games
+-------------------------------------------------*/
+
+int cli_info_listclones(core_options *options, const char *gamename)
+{
+ int drvindex, count = 0;
+
+ /* iterate over drivers */
+ for (drvindex = 0; drivers[drvindex]; drvindex++)
+ {
+ const game_driver *clone_of = driver_get_clone(drivers[drvindex]);
+
+ /* if we are a clone, and either our name matches the gamename, or the clone's name matches, display us */
+ if (clone_of != NULL && (clone_of->flags & GAME_IS_BIOS_ROOT) == 0)
+ if (mame_strwildcmp(gamename, drivers[drvindex]->name) == 0 || mame_strwildcmp(gamename, clone_of->name) == 0)
+ {
+ /* print the header on the first one */
+ if (count == 0)
+ mame_printf_info("Name: Clone of:\n");
+
+ /* output the remaining information */
+ mame_printf_info("%-8s %-8s\n", drivers[drvindex]->name, clone_of->name);
+ count++;
+ }
+ }
+
+ /* return an error if none found */
+ return (count > 0) ? MAMERR_NONE : MAMERR_NO_SUCH_GAME;
+}
+
+
+/*-------------------------------------------------
+ cli_info_listcrc - output the CRC and name of
+ all ROMs referenced by MAME
+-------------------------------------------------*/
+
+int cli_info_listcrc(core_options *options, const char *gamename)
+{
+ int drvindex, count = 0;
+
+ /* iterate over drivers */
+ for (drvindex = 0; drivers[drvindex]; drvindex++)
+ if (mame_strwildcmp(gamename, drivers[drvindex]->name) == 0)
+ {
+ const rom_entry *region, *rom;
+
+ /* iterate over regions, and then ROMs within the region */
+ for (region = rom_first_region(drivers[drvindex]); region; region = rom_next_region(region))
+ for (rom = rom_first_file(region); rom; rom = rom_next_file(rom))
+ {
+ char hashbuf[HASH_BUF_SIZE];
+
+ /* if we have a CRC, display it */
+ if (hash_data_extract_printable_checksum(ROM_GETHASHDATA(rom), HASH_CRC, hashbuf))
+ mame_printf_info("%s %-12s %s\n", hashbuf, ROM_GETNAME(rom), drivers[drvindex]->description);
+ }
+
+ count++;
+ }
+
+ /* return an error if none found */
+ return (count > 0) ? MAMERR_NONE : MAMERR_NO_SUCH_GAME;
+}
+
+
+/*-------------------------------------------------
+ cli_info_listroms - output the list of ROMs
+ referenced by a given game or set of games
+-------------------------------------------------*/
+
+int cli_info_listroms(core_options *options, const char *gamename)
+{
+ int drvindex, count = 0;
+
+ /* iterate over drivers */
+ for (drvindex = 0; drivers[drvindex]; drvindex++)
+ if (mame_strwildcmp(gamename, drivers[drvindex]->name) == 0)
+ {
+ const rom_entry *region, *rom, *chunk;
+
+ /* print the header */
+ if (count > 0)
+ mame_printf_info("\n");
+ mame_printf_info("This is the list of the ROMs required for driver \"%s\".\n"
+ "Name Size Checksum\n", drivers[drvindex]->name);
+
+ /* iterate over regions and then ROMs within the region */
+ for (region = drivers[drvindex]->rom; region; region = rom_next_region(region))
+ for (rom = rom_first_file(region); rom; rom = rom_next_file(rom))
+ {
+ const char *name = ROM_GETNAME(rom);
+ const char* hash = ROM_GETHASHDATA(rom);
+ char hashbuf[HASH_BUF_SIZE];
+ int length = -1;
+
+ /* accumulate the total length of all chunks */
+ if (ROMREGION_ISROMDATA(region))
+ {
+ length = 0;
+ for (chunk = rom_first_chunk(rom); chunk; chunk = rom_next_chunk(chunk))
+ length += ROM_GETLENGTH(chunk);
+ }
+
+ /* start with the name */
+ mame_printf_info("%-12s ", name);
+
+ /* output the length next */
+ if (length >= 0)
+ mame_printf_info("%7d", length);
+ else
+ mame_printf_info(" ");
+
+ /* output the hash data */
+ if (!hash_data_has_info(hash, HASH_INFO_NO_DUMP))
+ {
+ if (hash_data_has_info(hash, HASH_INFO_BAD_DUMP))
+ mame_printf_info(" BAD");
+
+ hash_data_print(hash, 0, hashbuf);
+ mame_printf_info(" %s", hashbuf);
+ }
+ else
+ mame_printf_info(" NO GOOD DUMP KNOWN");
+
+ /* end with a CR */
+ mame_printf_info("\n");
+ }
+
+ count++;
+ }
+
+ return (count > 0) ? MAMERR_NONE : MAMERR_NO_SUCH_GAME;
+}
+
+
+/*-------------------------------------------------
+ cli_info_listsamples - output the list of samples
+ referenced by a given game or set of games
+-------------------------------------------------*/
+
+int cli_info_listsamples(core_options *options, const char *gamename)
+{
+ int count = 0;
+
+#if (HAS_SAMPLES)
+ int drvindex;
+
+ /* since we expand the machine driver, we need to set things up */
+ init_resource_tracking();
+ cpuintrf_init(NULL);
+ sndintrf_init(NULL);
+
+ /* iterate over drivers */
+ for (drvindex = 0; drivers[drvindex]; drvindex++)
+ if (mame_strwildcmp(gamename, drivers[drvindex]->name) == 0)
+ {
+ machine_config drv;
+ int sndnum;
+
+ /* expand the machine driver */
+ expand_machine_driver(drivers[drvindex]->drv, &drv);
+
+ /* find samples interfaces */
+ for (sndnum = 0; sndnum < MAX_SOUND && drv.sound[sndnum].type != SOUND_DUMMY; sndnum++)
+ if (drv.sound[sndnum].type == SOUND_SAMPLES)
+ {
+ const char **samplenames = ((struct Samplesinterface *)drv.sound[sndnum].config)->samplenames;
+ int sampnum;
+
+ /* if the list is legit, walk it and print the sample info */
+ if (samplenames != NULL)
+ for (sampnum = 0; samplenames[sampnum] != NULL; sampnum++)
+ mame_printf_info("%s\n", samplenames[sampnum]);
+ }
+
+ count++;
+ }
+
+ /* clean up our tracked resources */
+ exit_resource_tracking();
+#else
+ mame_printf_error("Samples not supported in this build\n");
+#endif
+
+ return (count > 0) ? MAMERR_NONE : MAMERR_NO_SUCH_GAME;
+}
+
+
+/*-------------------------------------------------
+ info_verifyroms - verify the ROM sets of
+ one or more games
+-------------------------------------------------*/
+
+static int info_verifyroms(core_options *options, const char *gamename)
+{
+ int correct = 0;
+ int incorrect = 0;
+ int notfound = 0;
+ int drvindex;
+
+ /* iterate over drivers */
+ for (drvindex = 0; drivers[drvindex]; drvindex++)
+ if (mame_strwildcmp(gamename, drivers[drvindex]->name) == 0)
+ {
+ audit_record *audit;
+ int audit_records;
+ int res;
+
+ /* audit the ROMs in this set */
+ audit_records = audit_images(options, drivers[drvindex], AUDIT_VALIDATE_FAST, &audit);
+ res = audit_summary(drivers[drvindex], audit_records, audit, TRUE);
+ if (audit_records > 0)
+ free(audit);
+
+ /* if not found, count that and leave it at that */
+ if (res == NOTFOUND)
+ notfound++;
+
+ /* else display information about what we discovered */
+ else
+ {
+ const game_driver *clone_of;
+
+ /* output the name of the driver and its clone */
+ mame_printf_info("romset %s ", drivers[drvindex]->name);
+ clone_of = driver_get_clone(drivers[drvindex]);
+ if (clone_of != NULL)
+ mame_printf_info("[%s] ", clone_of->name);
+
+ /* switch off of the result */
+ switch (res)
+ {
+ case INCORRECT:
+ mame_printf_info("is bad\n");
+ incorrect++;
+ break;
+
+ case CORRECT:
+ mame_printf_info("is good\n");
+ correct++;
+ break;
+
+ case BEST_AVAILABLE:
+ mame_printf_info("is best available\n");
+ correct++;
+ break;
+ }
+ }
+ }
+
+ /* clear out any cached files */
+ zip_file_cache_clear();
+
+ /* if we didn't get anything at all, display a generic end message */
+ if (correct + incorrect == 0)
+ {
+ if (notfound > 0)
+ mame_printf_info("romset \"%s\" not found!\n", gamename);
+ else
+ mame_printf_info("romset \"%s\" not supported!\n", gamename);
+ return MAMERR_NO_SUCH_GAME;
+ }
+
+ /* otherwise, print a summary */
+ else
+ {
+ mame_printf_info("%d romsets found, %d were OK.\n", correct + incorrect, correct);
+ return (incorrect > 0) ? MAMERR_MISSING_FILES : MAMERR_NONE;
+ }
+}
+
+
+/*-------------------------------------------------
+ info_verifysamples - verify the sample sets of
+ one or more games
+-------------------------------------------------*/
+
+static int info_verifysamples(core_options *options, const char *gamename)
+{
+ int correct = 0;
+ int incorrect = 0;
+ int notfound = 0;
+ int drvindex;
+
+ /* now iterate over drivers */
+ for (drvindex = 0; drivers[drvindex]; drvindex++)
+ if (mame_strwildcmp(gamename, drivers[drvindex]->name) == 0)
+ {
+ audit_record *audit;
+ int audit_records;
+ int res;
+
+ /* audit the samples in this set */
+ audit_records = audit_samples(options, drivers[drvindex], &audit);
+ res = audit_summary(drivers[drvindex], audit_records, audit, TRUE);
+ if (audit_records > 0)
+ free(audit);
+ else
+ continue;
+
+ /* if not found, count that and leave it at that */
+ if (res == NOTFOUND)
+ notfound++;
+
+ /* else display information about what we discovered */
+ else
+ {
+ mame_printf_info("sampleset %s ", drivers[drvindex]->name);
+
+ /* switch off of the result */
+ switch (res)
+ {
+ case INCORRECT:
+ mame_printf_info("is bad\n");
+ incorrect++;
+ break;
+
+ case CORRECT:
+ mame_printf_info("is good\n");
+ correct++;
+ break;
+
+ case BEST_AVAILABLE:
+ mame_printf_info("is best available\n");
+ correct++;
+ break;
+ }
+ }
+ }
+
+ /* if we didn't get anything at all, display a generic end message */
+ if (correct + incorrect == 0)
+ {
+ if (notfound > 0)
+ mame_printf_error("sampleset \"%s\" not found!\n", gamename);
+ else
+ mame_printf_error("sampleset \"%s\" not supported!\n", gamename);
+ return MAMERR_NO_SUCH_GAME;
+ }
+
+ /* otherwise, print a summary */
+ else
+ {
+ mame_printf_info("%d samplesets found, %d were OK.\n", correct + incorrect, correct);
+ return (incorrect > 0) ? MAMERR_MISSING_FILES : MAMERR_NONE;
+ }
+}
+
+
+/*-------------------------------------------------
+ info_romident - identify ROMs by looking for
+ matches in our internal database
+-------------------------------------------------*/
+
+static int info_romident(core_options *options, const char *gamename)
+{
+ romident_status status;
+
+ /* a NULL gamename is a fatal error */
+ if (gamename == NULL)
+ return MAMERR_FATALERROR;
+
+ /* do the identification */
+ romident(gamename, &status);
+
+ /* clear out any cached files */
+ zip_file_cache_clear();
+
+ /* return the appropriate error code */
+ if (status.matches == status.total)
+ return MAMERR_NONE;
+ else if (status.matches == status.total - status.nonroms)
+ return MAMERR_IDENT_NONROMS;
+ else if (status.matches > 0)
+ return MAMERR_IDENT_PARTIAL;
+ else
+ return MAMERR_IDENT_NONE;
+}
+
+
+
+/***************************************************************************
+ UTILITIES
+***************************************************************************/
+
+/*-------------------------------------------------
+ romident - identify files
+-------------------------------------------------*/
+
+static void romident(const char *filename, romident_status *status)
+{
+ osd_directory *directory;
+
+ /* reset the status */
+ memset(status, 0, sizeof(*status));
+
+ /* first try to open as a directory */
+ directory = osd_opendir(filename);
+ if (directory != NULL)
+ {
+ const osd_directory_entry *entry;
+
+ /* iterate over all files in the directory */
+ while ((entry = osd_readdir(directory)) != NULL)
+ if (entry->type == ENTTYPE_FILE)
+ {
+ astring *curfile = astring_assemble_3(astring_alloc(), filename, PATH_SEPARATOR, entry->name);
+ identify_file(astring_c(curfile), status);
+ astring_free(curfile);
+ }
+ osd_closedir(directory);
+ }
+
+ /* if that failed, and the filename ends with .zip, identify as a ZIP file */
+ else if (core_filename_ends_with(filename, ".zip"))
+ {
+ /* first attempt to examine it as a valid ZIP file */
+ zip_file *zip = NULL;
+ zip_error ziperr = zip_file_open(filename, &zip);
+ if (ziperr == ZIPERR_NONE && zip != NULL)
+ {
+ const zip_file_header *entry;
+
+ /* loop over entries in the ZIP, skipping empty files and directories */
+ for (entry = zip_file_first_file(zip); entry; entry = zip_file_next_file(zip))
+ if (entry->uncompressed_length != 0)
+ {
+ UINT8 *data = (UINT8 *)malloc(entry->uncompressed_length);
+ if (data != NULL)
+ {
+ /* decompress data into RAM and identify it */
+ ziperr = zip_file_decompress(zip, data, entry->uncompressed_length);
+ if (ziperr == ZIPERR_NONE)
+ identify_data(entry->filename, data, entry->uncompressed_length, status);
+ free(data);
+ }
+ }
+
+ /* close up */
+ zip_file_close(zip);
+ }
+ }
+
+ /* otherwise, identify as a raw file */
+ else
+ identify_file(filename, status);
+}
+
+
+/*-------------------------------------------------
+ identify_file - identify a file; if it is a
+ ZIP file, scan it and identify all enclosed
+ files
+-------------------------------------------------*/
+
+static void identify_file(const char *name, romident_status *status)
+{
+ file_error filerr;
+ osd_file *file;
+ UINT64 length;
+
+ /* open for read and process if it opens and has a valid length */
+ filerr = osd_open(name, OPEN_FLAG_READ, &file, &length);
+ if (filerr == FILERR_NONE && length > 0 && (UINT32)length == length)
+ {
+ UINT8 *data = (UINT8 *)malloc(length);
+ if (data != NULL)
+ {
+ UINT32 bytes;
+
+ /* read file data into RAM and identify it */
+ filerr = osd_read(file, data, 0, length, &bytes);
+ if (filerr == FILERR_NONE)
+ identify_data(name, data, bytes, status);
+ free(data);
+ }
+ osd_close(file);
+ }
+}
+
+
+/*-------------------------------------------------
+ identify_data - identify a buffer full of
+ data; if it comes from a .JED file, parse the
+ fusemap into raw data first
+-------------------------------------------------*/
+
+static void identify_data(const char *name, const UINT8 *data, int length, romident_status *status)
+{
+ char hash[HASH_BUF_SIZE];
+ UINT8 *tempjed = NULL;
+ astring *basename;
+ int found = 0;
+ jed_data jed;
+
+ /* if this is a '.jed' file, process it into raw bits first */
+ if (core_filename_ends_with(name, ".jed") && jed_parse(data, length, &jed) == JEDERR_NONE)
+ {
+ /* now determine the new data length and allocate temporary memory for it */
+ length = jedbin_output(&jed, NULL, 0);
+ tempjed = malloc(length);
+ if (tempjed == NULL)
+ return;
+
+ /* create a binary output of the JED data and use that instead */
+ jedbin_output(&jed, tempjed, length);
+ data = tempjed;
+ }
+
+ /* compute the hash of the data */
+ hash_data_clear(hash);
+ hash_compute(hash, data, length, HASH_SHA1 | HASH_CRC);
+
+ /* output the name */
+ status->total++;
+ basename = core_filename_extract_base(astring_alloc(), name, FALSE);
+ mame_printf_info("%-20s", astring_c(basename));
+ astring_free(basename);
+
+ /* see if we can find a match in the ROMs */
+ match_roms(hash, length, &found);
+
+ /* if we didn't find it, try to guess what it might be */
+ if (found == 0)
+ {
+ /* if not a power of 2, assume it is a non-ROM file */
+ if ((length & (length - 1)) != 0)
+ {
+ mame_printf_info("NOT A ROM\n");
+ status->nonroms++;
+ }
+
+ /* otherwise, it's just not a match */
+ else
+ mame_printf_info("NO MATCH\n");
+ }
+
+ /* if we did find it, count it as a match */
+ else
+ status->matches++;
+
+ /* free any temporary JED data */
+ if (tempjed != NULL)
+ free(tempjed);
+}
+
+
+/*-------------------------------------------------
+ match_roms - scan for a matching ROM by hash
+-------------------------------------------------*/
+
+static void match_roms(const char *hash, int length, int *found)
+{
+ int drvindex;
+
+ /* iterate over drivers */
+ for (drvindex = 0; drivers[drvindex]; drvindex++)
+ {
+ const rom_entry *region, *rom;
+
+ /* iterate over regions and files within the region */
+ for (region = rom_first_region(drivers[drvindex]); region; region = rom_next_region(region))
+ for (rom = rom_first_file(region); rom; rom = rom_next_file(rom))
+ if (hash_data_is_equal(hash, ROM_GETHASHDATA(rom), 0))
+ {
+ int baddump = hash_data_has_info(ROM_GETHASHDATA(rom), HASH_INFO_BAD_DUMP);
+
+ /* output information about the match */
+ if (*found != 0)
+ mame_printf_info(" ");
+ mame_printf_info("= %s%-20s %s\n", baddump ? "(BAD) " : "", ROM_GETNAME(rom), drivers[drvindex]->description);
+ (*found)++;
+ }
+ }
+}