mame/src/emu/clifront.c

1665 lines
56 KiB
C

/***************************************************************************
clifront.c
Command-line interface frontend for MAME.
****************************************************************************
Copyright Aaron Giles
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
* Neither the name 'MAME' nor the names of its contributors may be
used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
***************************************************************************/
#include "emu.h"
#include "emuopts.h"
#include "hash.h"
#include "jedparse.h"
#include "audit.h"
#include "info.h"
#include "unzip.h"
#include "validity.h"
#include "sound/samples.h"
#include "clifront.h"
#include "xmlfile.h"
#include <new>
#include <ctype.h>
//**************************************************************************
// COMMAND-LINE OPTIONS
//**************************************************************************
const options_entry cli_options::s_option_entries[] =
{
/* core commands */
{ NULL, NULL, OPTION_HEADER, "CORE COMMANDS" },
{ CLICOMMAND_HELP ";h;?", "0", OPTION_COMMAND, "show help message" },
{ CLICOMMAND_VALIDATE ";valid", "0", OPTION_COMMAND, "perform driver validation on all game drivers" },
/* configuration commands */
{ NULL, NULL, OPTION_HEADER, "CONFIGURATION COMMANDS" },
{ CLICOMMAND_CREATECONFIG ";cc", "0", OPTION_COMMAND, "create the default configuration file" },
{ CLICOMMAND_SHOWCONFIG ";sc", "0", OPTION_COMMAND, "display running parameters" },
{ CLICOMMAND_SHOWUSAGE ";su", "0", OPTION_COMMAND, "show this help" },
/* frontend commands */
{ NULL, NULL, OPTION_HEADER, "FRONTEND COMMANDS" },
{ CLICOMMAND_LISTXML ";lx", "0", OPTION_COMMAND, "all available info on driver in XML format" },
{ CLICOMMAND_LISTFULL ";ll", "0", OPTION_COMMAND, "short name, full name" },
{ CLICOMMAND_LISTSOURCE ";ls", "0", OPTION_COMMAND, "driver sourcefile" },
{ CLICOMMAND_LISTCLONES ";lc", "0", OPTION_COMMAND, "show clones" },
{ CLICOMMAND_LISTBROTHERS ";lb", "0", OPTION_COMMAND, "show \"brothers\", or other drivers from same sourcefile" },
{ CLICOMMAND_LISTCRC, "0", OPTION_COMMAND, "CRC-32s" },
{ CLICOMMAND_LISTROMS, "0", OPTION_COMMAND, "list required roms for a driver" },
{ CLICOMMAND_LISTSAMPLES, "0", OPTION_COMMAND, "list optional samples for a driver" },
{ CLICOMMAND_VERIFYROMS, "0", OPTION_COMMAND, "report romsets that have problems" },
{ CLICOMMAND_VERIFYSAMPLES, "0", OPTION_COMMAND, "report samplesets that have problems" },
{ CLICOMMAND_ROMIDENT, "0", OPTION_COMMAND, "compare files with known MAME roms" },
{ CLICOMMAND_LISTDEVICES ";ld", "0", OPTION_COMMAND, "list available devices" },
{ CLICOMMAND_LISTSLOTS ";lslot", "0", OPTION_COMMAND, "list available slots and slot devices" },
{ CLICOMMAND_LISTMEDIA ";lm", "0", OPTION_COMMAND, "list available media for the system" },
{ CLICOMMAND_LISTSOFTWARE ";lsoft", "0", OPTION_COMMAND, "list known software for the system" },
{ NULL }
};
//**************************************************************************
// CLI OPTIONS
//**************************************************************************
//-------------------------------------------------
// cli_options - constructor
//-------------------------------------------------
cli_options::cli_options()
{
add_entries(s_option_entries);
}
//**************************************************************************
// CLI FRONTEND
//**************************************************************************
//-------------------------------------------------
// cli_frontend - constructor
//-------------------------------------------------
cli_frontend::cli_frontend(cli_options &options, osd_interface &osd)
: m_options(options),
m_osd(osd),
m_result(MAMERR_NONE)
{
// begin tracking memory
track_memory(true);
}
//-------------------------------------------------
// ~cli_frontend - destructor
//-------------------------------------------------
cli_frontend::~cli_frontend()
{
// report any unfreed memory on clean exits
track_memory(false);
if (m_result == MAMERR_NONE)
dump_unfreed_mem();
}
//-------------------------------------------------
// execute - execute a game via the standard
// command line interface
//-------------------------------------------------
int cli_frontend::execute(int argc, char **argv)
{
// wrap the core execution in a try/catch to field all fatal errors
m_result = MAMERR_NONE;
try
{
// parse the command line, adding any system-specific options
astring option_errors;
if (!m_options.parse_command_line(argc, argv, option_errors))
{
// if we failed, check for no command and a system name first; in that case error on the name
if (strlen(m_options.command()) == 0 && m_options.system() == NULL && strlen(m_options.system_name()) > 0)
throw emu_fatalerror(MAMERR_NO_SUCH_GAME, "Unknown system '%s'", m_options.system_name());
// otherwise, error on the options
throw emu_fatalerror(MAMERR_INVALID_CONFIG, "%s", option_errors.trimspace().cstr());
}
if (option_errors)
printf("Error in command line:\n%s\n", option_errors.trimspace().cstr());
// determine the base name of the EXE
astring exename;
core_filename_extract_base(exename, argv[0], true);
// if we have a command, execute that
if (strlen(m_options.command()) != 0)
execute_commands(exename);
// otherwise, check for a valid system
else
{
// if we can't find it, give an appropriate error
const game_driver *system = m_options.system();
if (system == NULL && strlen(m_options.system_name()) > 0)
throw emu_fatalerror(MAMERR_NO_SUCH_GAME, "Unknown system '%s'", m_options.system_name());
if (strlen(m_options.software_name()) > 0)
{
machine_config config(*system, m_options);
if (!config.devicelist().first(SOFTWARE_LIST))
throw emu_fatalerror(MAMERR_FATALERROR, "Error: unknown option: %s\n", m_options.software_name());
bool found = FALSE;
for (device_t *swlists = config.devicelist().first(SOFTWARE_LIST); swlists != NULL; swlists = swlists->typenext())
{
software_list_config *swlist = (software_list_config *)downcast<const legacy_device_base *>(swlists)->inline_config();
software_list *list = software_list_open(m_options, swlist->list_name, FALSE, NULL);
if (list)
{
software_info *swinfo = software_list_find(list, m_options.software_name(), NULL);
if (swinfo != NULL)
{
// loop through all parts
for (software_part *swpart = software_find_part(swinfo, NULL, NULL); swpart != NULL; swpart = software_part_next(swpart))
{
const char *mount = software_part_get_feature(swpart, "automount");
if (is_software_compatible(swpart, swlist))
{
if (mount == NULL || strcmp(mount,"no") != 0)
{
// search for an image device with the right interface
const device_image_interface *image = NULL;
for (bool gotone = config.devicelist().first(image); gotone; gotone = image->next(image))
{
const char *interface = image->image_interface();
if (interface != NULL)
{
if (!strcmp(interface, swpart->interface_))
{
const char *option = m_options.value(image->brief_instance_name());
// mount only if not already mounted
if (strlen(option) == 0)
{
astring val;
val.printf("%s:%s:%s",swlist->list_name,m_options.software_name(),swpart->name);
// call this in order to set slot devices according to mounting
m_options.parse_slot_devices(argc, argv, option_errors, image->instance_name(), val.cstr());
break;
}
}
}
}
}
found = TRUE;
}
}
}
software_list_close(list);
}
if (found) break;
}
if (!found)
{
software_display_matches(config.devicelist(),m_options, NULL,m_options.software_name());
throw emu_fatalerror(MAMERR_FATALERROR, "");
}
}
// otherwise just run the game
m_result = mame_execute(m_options, m_osd);
}
}
// handle exceptions of various types
catch (emu_fatalerror &fatal)
{
astring string(fatal.string());
fprintf(stderr, "%s\n", string.trimspace().cstr());
m_result = (fatal.exitcode() != 0) ? fatal.exitcode() : MAMERR_FATALERROR;
// if a game was specified, wasn't a wildcard, and our error indicates this was the
// reason for failure, offer some suggestions
if (m_result == MAMERR_NO_SUCH_GAME && strlen(m_options.system_name()) > 0 && strchr(m_options.system_name(), '*') == NULL && m_options.system() == NULL)
{
// get the top 10 approximate matches
driver_enumerator drivlist(m_options);
int matches[10];
drivlist.find_approximate_matches(m_options.system_name(), ARRAY_LENGTH(matches), matches);
// print them out
fprintf(stderr, "\n\"%s\" approximately matches the following\n"
"supported %s (best match first):\n\n", m_options.system_name(),emulator_info::get_gamesnoun());
for (int matchnum = 0; matchnum < ARRAY_LENGTH(matches); matchnum++)
if (matches[matchnum] != -1)
fprintf(stderr, "%-18s%s\n", drivlist.driver(matches[matchnum]).name, drivlist.driver(matches[matchnum]).description);
}
}
catch (emu_exception &)
{
fprintf(stderr, "Caught unhandled emulator exception\n");
m_result = MAMERR_FATALERROR;
}
catch (std::bad_alloc &)
{
fprintf(stderr, "Out of memory!\n");
m_result = MAMERR_FATALERROR;
}
// handle any other exceptions
catch (...)
{
fprintf(stderr, "Caught unhandled exception\n");
m_result = MAMERR_FATALERROR;
}
return m_result;
}
//-------------------------------------------------
// listxml - output the XML data for one or more
// games
//-------------------------------------------------
void cli_frontend::listxml(const char *gamename)
{
// determine which drivers to output; return an error if none found
driver_enumerator drivlist(m_options, gamename);
if (drivlist.count() == 0)
throw emu_fatalerror(MAMERR_NO_SUCH_GAME, "No matching games found for '%s'", gamename);
// create the XML and print it to stdout
info_xml_creator creator(drivlist);
creator.output(stdout);
}
//-------------------------------------------------
// listfull - output the name and description of
// one or more games
//-------------------------------------------------
void cli_frontend::listfull(const char *gamename)
{
// determine which drivers to output; return an error if none found
driver_enumerator drivlist(m_options, gamename);
if (drivlist.count() == 0)
throw emu_fatalerror(MAMERR_NO_SUCH_GAME, "No matching games found for '%s'", gamename);
// print the header
mame_printf_info("Name: Description:\n");
// iterate through drivers and output the info
while (drivlist.next())
if ((drivlist.driver().flags & GAME_NO_STANDALONE) == 0)
mame_printf_info("%-18s\"%s\"\n", drivlist.driver().name, drivlist.driver().description);
}
//-------------------------------------------------
// listsource - output the name and source
// filename of one or more games
//-------------------------------------------------
void cli_frontend::listsource(const char *gamename)
{
// determine which drivers to output; return an error if none found
driver_enumerator drivlist(m_options, gamename);
if (drivlist.count() == 0)
throw emu_fatalerror(MAMERR_NO_SUCH_GAME, "No matching games found for '%s'", gamename);
// iterate through drivers and output the info
astring filename;
while (drivlist.next())
mame_printf_info("%-16s %s\n", drivlist.driver().name, core_filename_extract_base(filename, drivlist.driver().source_file).cstr());
}
//-------------------------------------------------
// listclones - output the name and parent of all
// clones matching the given pattern
//-------------------------------------------------
void cli_frontend::listclones(const char *gamename)
{
// start with a filtered list of drivers
driver_enumerator drivlist(m_options, gamename);
int original_count = drivlist.count();
// iterate through the remaining ones to see if their parent matches
while (drivlist.next_excluded())
{
// if we have a non-bios clone and it matches, keep it
int clone_of = drivlist.clone();
if (clone_of != -1 && (drivlist.driver(clone_of).flags & GAME_IS_BIOS_ROOT) == 0)
if (drivlist.matches(gamename, drivlist.driver(clone_of).name))
drivlist.include();
}
// return an error if none found
if (drivlist.count() == 0)
{
// see if we match but just weren't a clone
if (original_count == 0)
throw emu_fatalerror(MAMERR_NO_SUCH_GAME, "No matching games found for '%s'", gamename);
else
mame_printf_info("Found %d matches for '%s' but none were clones\n", drivlist.count(), gamename);
return;
}
// print the header
mame_printf_info("Name: Clone of:\n");
// iterate through drivers and output the info
drivlist.reset();
while (drivlist.next())
{
int clone_of = drivlist.clone();
if (clone_of != -1 && (drivlist.driver(clone_of).flags & GAME_IS_BIOS_ROOT) == 0)
mame_printf_info("%-16s %-8s\n", drivlist.driver().name, drivlist.driver(clone_of).name);
}
}
//-------------------------------------------------
// listbrothers - for each matching game, output
// the list of other games that share the same
// source file
//-------------------------------------------------
void cli_frontend::listbrothers(const char *gamename)
{
// start with a filtered list of drivers; return an error if none found
driver_enumerator initial_drivlist(m_options, gamename);
if (initial_drivlist.count() == 0)
throw emu_fatalerror(MAMERR_NO_SUCH_GAME, "No matching games found for '%s'", gamename);
// for the final list, start with an empty driver list
driver_enumerator drivlist(m_options);
drivlist.exclude_all();
// scan through the initially-selected drivers
while (initial_drivlist.next())
{
// if we are already marked in the final list, we don't need to do anything
if (drivlist.included(initial_drivlist.current()))
continue;
// otherwise, walk excluded items in the final list and mark any that match
drivlist.reset();
while (drivlist.next_excluded())
if (strcmp(drivlist.driver().source_file, initial_drivlist.driver().source_file) == 0)
drivlist.include();
}
// print the header
mame_printf_info("Source file: Name: Parent:\n");
// output the entries found
drivlist.reset();
astring filename;
while (drivlist.next())
{
int clone_of = drivlist.clone();
mame_printf_info("%-16s %-16s %-16s\n", core_filename_extract_base(filename, drivlist.driver().source_file).cstr(), drivlist.driver().name, (clone_of == -1 ? "" : drivlist.driver(clone_of).name));
}
}
//-------------------------------------------------
// listcrc - output the CRC and name of all ROMs
// referenced by the emulator
//-------------------------------------------------
void cli_frontend::listcrc(const char *gamename)
{
// determine which drivers to output; return an error if none found
driver_enumerator drivlist(m_options, gamename);
if (drivlist.count() == 0)
throw emu_fatalerror(MAMERR_NO_SUCH_GAME, "No matching games found for '%s'", gamename);
// iterate through matches, and then through ROMs
while (drivlist.next())
for (const rom_source *source = rom_first_source(drivlist.config()); source != NULL; source = rom_next_source(*source))
{
bool isdriver = (source == rom_first_source(drivlist.config()));
for (const rom_entry *region = rom_first_region(*source); region; region = rom_next_region(region))
for (const rom_entry *rom = rom_first_file(region); rom; rom = rom_next_file(rom))
{
// if we have a CRC, display it
UINT32 crc;
if (hash_collection(ROM_GETHASHDATA(rom)).crc(crc))
mame_printf_info("%08x %-16s \t %-8s \t %s\n", crc, ROM_GETNAME(rom), source->shortname(), isdriver ? drivlist.driver().description : source->name());
}
}
}
//-------------------------------------------------
// listroms - output the list of ROMs referenced
// by a given game or set of games
//-------------------------------------------------
void cli_frontend::listroms(const char *gamename)
{
// determine which drivers to output; return an error if none found
driver_enumerator drivlist(m_options, gamename);
if (drivlist.count() == 0)
throw emu_fatalerror(MAMERR_NO_SUCH_GAME, "No matching games found for '%s'", gamename);
// iterate through matches
astring tempstr;
bool first = true;
while (drivlist.next())
{
// print a header
if (!first)
mame_printf_info("\n");
first = false;
mame_printf_info("ROMs required for driver \"%s\".\n"
"Name Size Checksum\n", drivlist.driver().name);
// iterate through roms
for (const rom_source *source = rom_first_source(drivlist.config()); source != NULL; source = rom_next_source(*source))
for (const rom_entry *region = rom_first_region(*source); region; region = rom_next_region(region))
for (const rom_entry *rom = rom_first_file(region); rom; rom = rom_next_file(rom))
{
// accumulate the total length of all chunks
int length = -1;
if (ROMREGION_ISROMDATA(region))
length = rom_file_size(rom);
// start with the name
const char *name = ROM_GETNAME(rom);
mame_printf_info("%-20s ", name);
// output the length next
if (length >= 0)
mame_printf_info("%7d", length);
else
mame_printf_info(" ");
// output the hash data
hash_collection hashes(ROM_GETHASHDATA(rom));
if (!hashes.flag(hash_collection::FLAG_NO_DUMP))
{
if (hashes.flag(hash_collection::FLAG_BAD_DUMP))
mame_printf_info(" BAD");
mame_printf_info(" %s", hashes.macro_string(tempstr));
}
else
mame_printf_info(" NO GOOD DUMP KNOWN");
// end with a CR
mame_printf_info("\n");
}
}
}
//-------------------------------------------------
// listsamples - output the list of samples
// referenced by a given game or set of games
//-------------------------------------------------
void cli_frontend::listsamples(const char *gamename)
{
// determine which drivers to output; return an error if none found
driver_enumerator drivlist(m_options, gamename);
if (drivlist.count() == 0)
throw emu_fatalerror(MAMERR_NO_SUCH_GAME, "No matching games found for '%s'", gamename);
// iterate over drivers, looking for SAMPLES devices
bool first = true;
while (drivlist.next())
{
// see if we have samples
const device_t *device;
for (device = drivlist.config().first_device(); device != NULL; device = device->next())
if (device->type() == SAMPLES)
break;
if (device == NULL)
continue;
// print a header
if (!first)
mame_printf_info("\n");
first = false;
mame_printf_info("Samples required for driver \"%s\".\n", drivlist.driver().name);
// iterate over samples devices
for ( ; device != NULL; device = device->next())
if (device->type() == SAMPLES)
{
// if the list is legit, walk it and print the sample info
const char *const *samplenames = reinterpret_cast<const samples_interface *>(device->static_config())->samplenames;
if (samplenames != NULL)
for (int sampnum = 0; samplenames[sampnum] != NULL; sampnum++)
if (samplenames[sampnum][0] != '*')
mame_printf_info("%s\n", samplenames[sampnum]);
}
}
}
//-------------------------------------------------
// listdevices - output the list of devices
// referenced by a given game or set of games
//-------------------------------------------------
void cli_frontend::listdevices(const char *gamename)
{
// determine which drivers to output; return an error if none found
driver_enumerator drivlist(m_options, gamename);
if (drivlist.count() == 0)
throw emu_fatalerror(MAMERR_NO_SUCH_GAME, "No matching games found for '%s'", gamename);
// iterate over drivers, looking for SAMPLES devices
bool first = true;
while (drivlist.next())
{
// print a header
if (!first)
printf("\n");
first = false;
printf("Driver %s (%s):\n", drivlist.driver().name, drivlist.driver().description);
// iterate through devices
for (const device_t *device = drivlist.config().first_device(); device != NULL; device = device->next())
{
printf(" %s ('%s')", device->name(), device->tag());
UINT32 clock = device->clock();
if (clock >= 1000000000)
printf(" @ %d.%02d GHz\n", clock / 1000000000, (clock / 10000000) % 100);
else if (clock >= 1000000)
printf(" @ %d.%02d MHz\n", clock / 1000000, (clock / 10000) % 100);
else if (clock >= 1000)
printf(" @ %d.%02d kHz\n", clock / 1000, (clock / 10) % 100);
else if (clock > 0)
printf(" @ %d Hz\n", clock);
else
printf("\n");
}
}
}
//-------------------------------------------------
// listslots - output the list of slot devices
// referenced by a given game or set of games
//-------------------------------------------------
void cli_frontend::listslots(const char *gamename)
{
// determine which drivers to output; return an error if none found
driver_enumerator drivlist(m_options, gamename);
if (drivlist.count() == 0)
throw emu_fatalerror(MAMERR_NO_SUCH_GAME, "No matching games found for '%s'", gamename);
// print header
printf(" SYSTEM SLOT NAME SLOT OPTIONS SLOT DEVICE NAME \n");
printf("---------- ----------- -------------- ----------------------\n");
// iterate over drivers
while (drivlist.next())
{
// iterate
const device_slot_interface *slot = NULL;
bool first = true;
for (bool gotone = drivlist.config().devicelist().first(slot); gotone; gotone = slot->next(slot))
{
// output the line, up to the list of extensions
printf("%-13s%-10s ", first ? drivlist.driver().name : "", slot->device().tag());
// get the options and print them
const slot_interface* intf = slot->get_slot_interfaces();
for (int i = 0; intf[i].name != NULL; i++)
{
device_t *dev = (*intf[i].devtype)(drivlist.config(), "dummy", drivlist.config().devicelist().first(), 0);
dev->config_complete();
if (i==0) {
printf("%-15s %s\n", intf[i].name,dev->name());
} else {
printf("%-23s %-15s %s\n", "",intf[i].name,dev->name());
}
global_free(dev);
}
// end the line
printf("\n");
first = false;
}
// if we didn't get any at all, just print a none line
if (first)
printf("%-13s(none)\n", drivlist.driver().name);
}
}
//-------------------------------------------------
// listmedia - output the list of image devices
// referenced by a given game or set of games
//-------------------------------------------------
void cli_frontend::listmedia(const char *gamename)
{
// determine which drivers to output; return an error if none found
driver_enumerator drivlist(m_options, gamename);
if (drivlist.count() == 0)
throw emu_fatalerror(MAMERR_NO_SUCH_GAME, "No matching games found for '%s'", gamename);
// print header
printf(" SYSTEM MEDIA NAME (brief) IMAGE FILE EXTENSIONS SUPPORTED \n");
printf("---------- -------------------- ------------------------------------\n");
// iterate over drivers
while (drivlist.next())
{
// iterate
const device_image_interface *imagedev = NULL;
bool first = true;
for (bool gotone = drivlist.config().devicelist().first(imagedev); gotone; gotone = imagedev->next(imagedev))
{
// extract the shortname with parentheses
astring paren_shortname;
paren_shortname.format("(%s)", imagedev->brief_instance_name());
// output the line, up to the list of extensions
printf("%-13s%-12s%-8s ", first ? drivlist.driver().name : "", imagedev->instance_name(), paren_shortname.cstr());
// get the extensions and print them
astring extensions(imagedev->file_extensions());
for (int start = 0, end = extensions.chr(0, ','); ; start = end + 1, end = extensions.chr(start, ','))
{
astring curext(extensions, start, (end == -1) ? extensions.len() - start : end - start);
printf(".%-5s", curext.cstr());
if (end == -1)
break;
}
// end the line
printf("\n");
first = false;
}
// if we didn't get any at all, just print a none line
if (first)
printf("%-13s(none)\n", drivlist.driver().name);
}
}
//-------------------------------------------------
// verifyroms - verify the ROM sets of one or
// more games
//-------------------------------------------------
extern int m_device_count;
extern const device_type *s_devices_sorted[];
void cli_frontend::verifyroms(const char *gamename)
{
// determine which drivers to output;
driver_enumerator drivlist(m_options, gamename);
int correct = 0;
int incorrect = 0;
int notfound = 0;
int matched = 0;
// iterate over drivers
media_auditor auditor(drivlist);
while (drivlist.next())
{
matched++;
// audit the ROMs in this set
media_auditor::summary summary = auditor.audit_media(AUDIT_VALIDATE_FAST);
// if not found, count that and leave it at that
if (summary == media_auditor::NOTFOUND)
notfound++;
// else display information about what we discovered
else if (summary != media_auditor::NONE_NEEDED)
{
// output the summary of the audit
astring summary_string;
auditor.summarize(drivlist.driver().name,&summary_string);
mame_printf_info("%s", summary_string.cstr());
// output the name of the driver and its clone
mame_printf_info("romset %s ", drivlist.driver().name);
int clone_of = drivlist.clone();
if (clone_of != -1)
mame_printf_info("[%s] ", drivlist.driver(clone_of).name);
// switch off of the result
switch (summary)
{
case media_auditor::INCORRECT:
mame_printf_info("is bad\n");
incorrect++;
break;
case media_auditor::CORRECT:
mame_printf_info("is good\n");
correct++;
break;
case media_auditor::BEST_AVAILABLE:
mame_printf_info("is best available\n");
correct++;
break;
default:
break;
}
}
}
driver_enumerator dummy_drivlist(m_options);
dummy_drivlist.next();
machine_config &config = dummy_drivlist.config();
device_t *owner = config.devicelist().first();
// check if all are listed, note that empty one is included
for (int i = 0; i < m_device_count; i++)
{
device_type type = *s_devices_sorted[i];
device_t *dev = (*type)(config, "dummy", owner, 0);
dev->config_complete();
if (mame_strwildcmp(gamename, dev->shortname()) == 0)
{
matched++;
// audit the ROMs in this set
media_auditor::summary summary = auditor.audit_device(dev, AUDIT_VALIDATE_FAST);
// if not found, count that and leave it at that
if (summary == media_auditor::NOTFOUND)
notfound++;
// else display information about what we discovered
else
{
// output the summary of the audit
astring summary_string;
auditor.summarize(dev->shortname(),&summary_string);
mame_printf_info("%s", summary_string.cstr());
// display information about what we discovered
mame_printf_info("romset %s ", dev->shortname());
// switch off of the result
switch (summary)
{
case media_auditor::INCORRECT:
mame_printf_info("is bad\n");
incorrect++;
break;
case media_auditor::CORRECT:
mame_printf_info("is good\n");
correct++;
break;
case media_auditor::BEST_AVAILABLE:
mame_printf_info("is best available\n");
correct++;
break;
default:
break;
}
}
}
global_free(dev);
}
// clear out any cached files
zip_file_cache_clear();
// return an error if none found
if (matched == 0)
throw emu_fatalerror(MAMERR_NO_SUCH_GAME, "No matching games found for '%s'", gamename);
// if we didn't get anything at all, display a generic end message
if (matched == 1 && correct == 0 && incorrect == 0)
{
if (notfound > 0)
throw emu_fatalerror(MAMERR_MISSING_FILES, "romset \"%s\" not found!\n", gamename);
else
throw emu_fatalerror(MAMERR_MISSING_FILES, "romset \"%s\" has no roms!\n", gamename);
}
// otherwise, print a summary
else
{
if (incorrect > 0)
throw emu_fatalerror(MAMERR_MISSING_FILES, "%d romsets found, %d were OK.\n", correct + incorrect, correct);
mame_printf_info("%d romsets found, %d were OK.\n", correct, correct);
}
}
//-------------------------------------------------
// info_verifysamples - verify the sample sets of
// one or more games
//-------------------------------------------------
void cli_frontend::verifysamples(const char *gamename)
{
// determine which drivers to output; return an error if none found
driver_enumerator drivlist(m_options, gamename);
int correct = 0;
int incorrect = 0;
int notfound = 0;
int matched = 0;
// iterate over drivers
media_auditor auditor(drivlist);
while (drivlist.next())
{
matched++;
// audit the samples in this set
media_auditor::summary summary = auditor.audit_samples();
// if not found, count that and leave it at that
if (summary == media_auditor::NOTFOUND)
notfound++;
// else display information about what we discovered
else if (summary != media_auditor::NONE_NEEDED)
{
// output the summary of the audit
astring summary_string;
auditor.summarize(drivlist.driver().name,&summary_string);
mame_printf_info("%s", summary_string.cstr());
// output the name of the driver and its clone
mame_printf_info("sampleset %s ", drivlist.driver().name);
int clone_of = drivlist.clone();
if (clone_of != -1)
mame_printf_info("[%s] ", drivlist.driver(clone_of).name);
// switch off of the result
switch (summary)
{
case media_auditor::INCORRECT:
mame_printf_info("is bad\n");
incorrect++;
break;
case media_auditor::CORRECT:
mame_printf_info("is good\n");
correct++;
break;
case media_auditor::BEST_AVAILABLE:
mame_printf_info("is best available\n");
correct++;
break;
default:
break;
}
}
}
// clear out any cached files
zip_file_cache_clear();
// return an error if none found
if (matched == 0)
throw emu_fatalerror(MAMERR_NO_SUCH_GAME, "No matching games found for '%s'", gamename);
// if we didn't get anything at all, display a generic end message
if (matched == 1 && correct == 0 && incorrect == 0)
{
if (notfound > 0)
throw emu_fatalerror(MAMERR_MISSING_FILES, "sampleset \"%s\" not found!\n", gamename);
else
throw emu_fatalerror(MAMERR_MISSING_FILES, "sampleset \"%s\" not required!\n", gamename);
}
// otherwise, print a summary
else
{
if (incorrect > 0)
throw emu_fatalerror(MAMERR_MISSING_FILES, "%d samplesets found, %d were OK.\n", correct + incorrect, correct);
mame_printf_info("%d samplesets found, %d were OK.\n", correct, correct);
}
}
/*-------------------------------------------------
info_listsoftware - output the list of
software supported by a given game or set of
games
TODO: Add all information read from the source files
Possible improvement: use a sorted list for
identifying duplicate lists.
-------------------------------------------------*/
void cli_frontend::listsoftware(const char *gamename)
{
FILE *out = stdout;
// determine which drivers to output; return an error if none found
driver_enumerator drivlist(m_options, gamename);
if (drivlist.count() == 0)
throw emu_fatalerror(MAMERR_NO_SUCH_GAME, "No matching games found for '%s'", gamename);
// first determine the maximum number of lists we might encounter
int list_count = 0;
while (drivlist.next())
for (const device_t *dev = drivlist.config().devicelist().first(SOFTWARE_LIST); dev != NULL; dev = dev->typenext())
{
software_list_config *swlist = (software_list_config *)downcast<const legacy_device_base *>(dev)->inline_config();
if (swlist->list_type == SOFTWARE_LIST_ORIGINAL_SYSTEM)
list_count++;
}
// allocate a list
astring *lists = global_alloc_array(astring, list_count);
if (list_count)
{
fprintf( out,
"<?xml version=\"1.0\"?>\n"
"<!DOCTYPE softwarelist [\n"
"<!ELEMENT softwarelists (softwarelist*)>\n"
"\t<!ELEMENT softwarelist (software+)>\n"
"\t\t<!ATTLIST softwarelist name CDATA #REQUIRED>\n"
"\t\t<!ATTLIST softwarelist description CDATA #IMPLIED>\n"
"\t\t<!ELEMENT software (description, year?, publisher, info*, sharedfeat*, part*)>\n"
"\t\t\t<!ATTLIST software name CDATA #REQUIRED>\n"
"\t\t\t<!ATTLIST software cloneof CDATA #IMPLIED>\n"
"\t\t\t<!ATTLIST software supported (yes|partial|no) \"yes\">\n"
"\t\t\t<!ELEMENT description (#PCDATA)>\n"
"\t\t\t<!ELEMENT year (#PCDATA)>\n"
"\t\t\t<!ELEMENT publisher (#PCDATA)>\n"
// we still do not store the info strings internally, so there is no output here
// TODO: add parsing info in softlist.c and then add output here!
"\t\t\t<!ELEMENT info EMPTY>\n"
"\t\t\t\t<!ATTLIST info name CDATA #REQUIRED>\n"
"\t\t\t\t<!ATTLIST info value CDATA #IMPLIED>\n"
// shared features get stored in the part->feature below and are output there
// this means that we don't output any <sharedfeat> and that -lsoft output will
// be different from the list in hash/ when the list uses sharedfeat. But this
// is by design: sharedfeat is only available to simplify the life to list creators,
// to e.g. avoid manually adding the same feature to each disk of a 9 floppies game!
"\t\t\t<!ELEMENT sharedfeat EMPTY>\n"
"\t\t\t\t<!ATTLIST sharedfeat name CDATA #REQUIRED>\n"
"\t\t\t\t<!ATTLIST sharedfeat value CDATA #IMPLIED>\n"
"\t\t\t<!ELEMENT part (feature*, dataarea*, diskarea*, dipswitch*)>\n"
"\t\t\t\t<!ATTLIST part name CDATA #REQUIRED>\n"
"\t\t\t\t<!ATTLIST part interface CDATA #REQUIRED>\n"
"\t\t\t\t<!ELEMENT feature EMPTY>\n"
"\t\t\t\t\t<!ATTLIST feature name CDATA #REQUIRED>\n"
"\t\t\t\t\t<!ATTLIST feature value CDATA #IMPLIED>\n"
"\t\t\t\t<!ELEMENT dataarea (rom*)>\n"
"\t\t\t\t\t<!ATTLIST dataarea name CDATA #REQUIRED>\n"
"\t\t\t\t\t<!ATTLIST dataarea size CDATA #REQUIRED>\n"
"\t\t\t\t\t<!ATTLIST dataarea databits (8|16|32|64) \"8\">\n"
"\t\t\t\t\t<!ATTLIST dataarea endian (big|little) \"little\">\n"
"\t\t\t\t\t<!ELEMENT rom EMPTY>\n"
"\t\t\t\t\t\t<!ATTLIST rom name CDATA #IMPLIED>\n"
"\t\t\t\t\t\t<!ATTLIST rom size CDATA #IMPLIED>\n"
"\t\t\t\t\t\t<!ATTLIST rom length CDATA #IMPLIED>\n"
"\t\t\t\t\t\t<!ATTLIST rom crc CDATA #IMPLIED>\n"
"\t\t\t\t\t\t<!ATTLIST rom sha1 CDATA #IMPLIED>\n"
"\t\t\t\t\t\t<!ATTLIST rom offset CDATA #IMPLIED>\n"
"\t\t\t\t\t\t<!ATTLIST rom value CDATA #IMPLIED>\n"
"\t\t\t\t\t\t<!ATTLIST rom status (baddump|nodump|good) \"good\">\n"
"\t\t\t\t\t\t<!ATTLIST rom loadflag (load16_byte|load16_word|load16_word_swap|load32_byte|load32_word|load32_word_swap|load32_dword|load64_word|load64_word_swap|reload|fill|continue) #IMPLIED>\n"
"\t\t\t\t<!ELEMENT diskarea (disk*)>\n"
"\t\t\t\t\t<!ATTLIST diskarea name CDATA #REQUIRED>\n"
"\t\t\t\t\t<!ELEMENT disk EMPTY>\n"
"\t\t\t\t\t\t<!ATTLIST disk name CDATA #REQUIRED>\n"
"\t\t\t\t\t\t<!ATTLIST disk sha1 CDATA #IMPLIED>\n"
"\t\t\t\t\t\t<!ATTLIST disk status (baddump|nodump|good) \"good\">\n"
"\t\t\t\t\t\t<!ATTLIST disk writeable (yes|no) \"no\">\n"
// we still do not store the dipswitch values internally, so there is no output here
// TODO: add parsing dipsw in softlist.c and then add output here!
"\t\t\t\t<!ELEMENT dipswitch (dipvalue*)>\n"
"\t\t\t\t\t<!ATTLIST dipswitch name CDATA #REQUIRED>\n"
"\t\t\t\t\t<!ATTLIST dipswitch tag CDATA #REQUIRED>\n"
"\t\t\t\t\t<!ATTLIST dipswitch mask CDATA #REQUIRED>\n"
"\t\t\t\t\t<!ELEMENT dipvalue EMPTY>\n"
"\t\t\t\t\t\t<!ATTLIST dipvalue name CDATA #REQUIRED>\n"
"\t\t\t\t\t\t<!ATTLIST dipvalue value CDATA #REQUIRED>\n"
"\t\t\t\t\t\t<!ATTLIST dipvalue default (yes|no) \"no\">\n"
"]>\n\n"
"<softwarelists>\n"
);
}
drivlist.reset();
list_count = 0;
while (drivlist.next())
for (const device_t *dev = drivlist.config().devicelist().first(SOFTWARE_LIST); dev != NULL; dev = dev->typenext())
{
software_list_config *swlist = (software_list_config *)downcast<const legacy_device_base *>(dev)->inline_config();
if (swlist->list_type == SOFTWARE_LIST_ORIGINAL_SYSTEM)
{
software_list *list = software_list_open(m_options, swlist->list_name, FALSE, NULL);
if ( list )
{
/* Verify if we have encountered this list before */
bool seen_before = false;
for (int seen_index = 0; seen_index < list_count && !seen_before; seen_index++)
if (lists[seen_index] == swlist->list_name)
seen_before = true;
if (!seen_before)
{
lists[list_count++] = swlist->list_name;
software_list_parse( list, NULL, NULL );
fprintf(out, "\t<softwarelist name=\"%s\" description=\"%s\">\n", swlist->list_name, xml_normalize_string(software_list_get_description(list)) );
for ( software_info *swinfo = software_list_find( list, "*", NULL ); swinfo != NULL; swinfo = software_list_find( list, "*", swinfo ) )
{
fprintf( out, "\t\t<software name=\"%s\"", swinfo->shortname );
if ( swinfo->parentname != NULL )
fprintf( out, " cloneof=\"%s\"", swinfo->parentname );
if ( swinfo->supported == SOFTWARE_SUPPORTED_PARTIAL )
fprintf( out, " supported=\"partial\"" );
if ( swinfo->supported == SOFTWARE_SUPPORTED_NO )
fprintf( out, " supported=\"no\"" );
fprintf( out, ">\n" );
fprintf( out, "\t\t\t<description>%s</description>\n", xml_normalize_string(swinfo->longname) );
fprintf( out, "\t\t\t<year>%s</year>\n", xml_normalize_string( swinfo->year ) );
fprintf( out, "\t\t\t<publisher>%s</publisher>\n", xml_normalize_string( swinfo->publisher ) );
for ( software_part *part = software_find_part( swinfo, NULL, NULL ); part != NULL; part = software_part_next( part ) )
{
fprintf( out, "\t\t\t<part name=\"%s\"", part->name );
if ( part->interface_ )
fprintf( out, " interface=\"%s\"", part->interface_ );
fprintf( out, ">\n");
if ( part->featurelist )
{
feature_list *flist = part->featurelist;
while( flist )
{
fprintf( out, "\t\t\t\t<feature name=\"%s\" value=\"%s\" />\n", flist->name, flist->value );
flist = flist->next;
}
}
/* TODO: display rom region information */
for ( const rom_entry *region = part->romdata; region; region = rom_next_region( region ) )
{
int is_disk = ROMREGION_ISDISKDATA(region);
if (!is_disk)
fprintf( out, "\t\t\t\t<dataarea name=\"%s\" size=\"%d\">\n", ROMREGION_GETTAG(region), ROMREGION_GETLENGTH(region) );
else
fprintf( out, "\t\t\t\t<diskarea name=\"%s\">\n", ROMREGION_GETTAG(region) );
for ( const rom_entry *rom = rom_first_file( region ); rom && !ROMENTRY_ISREGIONEND(rom); rom++ )
{
if ( ROMENTRY_ISFILE(rom) )
{
if (!is_disk)
fprintf( out, "\t\t\t\t\t<rom name=\"%s\" size=\"%d\"", xml_normalize_string(ROM_GETNAME(rom)), rom_file_size(rom) );
else
fprintf( out, "\t\t\t\t\t<disk name=\"%s\"", xml_normalize_string(ROM_GETNAME(rom)) );
/* dump checksum information only if there is a known dump */
hash_collection hashes(ROM_GETHASHDATA(rom));
if (!hashes.flag(hash_collection::FLAG_NO_DUMP))
{
astring tempstr;
for (hash_base *hash = hashes.first(); hash != NULL; hash = hash->next())
fprintf(out, " %s=\"%s\"", hash->name(), hash->string(tempstr));
}
if (!is_disk)
fprintf( out, " offset=\"0x%x\"", ROM_GETOFFSET(rom) );
if ( hashes.flag(hash_collection::FLAG_BAD_DUMP) )
fprintf( out, " status=\"baddump\"" );
if ( hashes.flag(hash_collection::FLAG_NO_DUMP) )
fprintf( out, " status=\"nodump\"" );
if (is_disk)
fprintf( out, " writeable=\"%s\"", (ROM_GETFLAGS(rom) & DISK_READONLYMASK) ? "no" : "yes");
if ((ROM_GETFLAGS(rom) & ROM_SKIPMASK) == ROM_SKIP(1))
fprintf( out, " loadflag=\"load16_byte\"" );
if ((ROM_GETFLAGS(rom) & ROM_SKIPMASK) == ROM_SKIP(3))
fprintf( out, " loadflag=\"load32_byte\"" );
if (((ROM_GETFLAGS(rom) & ROM_SKIPMASK) == ROM_SKIP(2)) && ((ROM_GETFLAGS(rom) & ROM_GROUPMASK) == ROM_GROUPWORD))
{
if (!(ROM_GETFLAGS(rom) & ROM_REVERSEMASK))
fprintf( out, " loadflag=\"load32_word\"" );
else
fprintf( out, " loadflag=\"load32_word_swap\"" );
}
if (((ROM_GETFLAGS(rom) & ROM_SKIPMASK) == ROM_SKIP(6)) && ((ROM_GETFLAGS(rom) & ROM_GROUPMASK) == ROM_GROUPWORD))
{
if (!(ROM_GETFLAGS(rom) & ROM_REVERSEMASK))
fprintf( out, " loadflag=\"load64_word\"" );
else
fprintf( out, " loadflag=\"load64_word_swap\"" );
}
if (((ROM_GETFLAGS(rom) & ROM_SKIPMASK) == ROM_NOSKIP) && ((ROM_GETFLAGS(rom) & ROM_GROUPMASK) == ROM_GROUPWORD))
{
if (!(ROM_GETFLAGS(rom) & ROM_REVERSEMASK))
fprintf( out, " loadflag=\"load32_dword\"" );
else
fprintf( out, " loadflag=\"load16_word_swap\"" );
}
fprintf( out, "/>\n" );
}
else if ( ROMENTRY_ISRELOAD(rom) )
{
fprintf( out, "\t\t\t\t\t<rom size=\"%d\" offset=\"0x%x\" loadflag=\"reload\" />\n", ROM_GETLENGTH(rom), ROM_GETOFFSET(rom) );
}
else if ( ROMENTRY_ISCONTINUE(rom) )
{
fprintf( out, "\t\t\t\t\t<rom size=\"%d\" offset=\"0x%x\" loadflag=\"continue\" />\n", ROM_GETLENGTH(rom), ROM_GETOFFSET(rom) );
}
else if ( ROMENTRY_ISFILL(rom) )
{
fprintf( out, "\t\t\t\t\t<rom size=\"%d\" offset=\"0x%x\" loadflag=\"fill\" />\n", ROM_GETLENGTH(rom), ROM_GETOFFSET(rom) );
}
}
if (!is_disk)
fprintf( out, "\t\t\t\t</dataarea>\n" );
else
fprintf( out, "\t\t\t\t</diskarea>\n" );
}
fprintf( out, "\t\t\t</part>\n" );
}
fprintf( out, "\t\t</software>\n" );
}
fprintf(out, "\t</softwarelist>\n" );
}
software_list_close( list );
}
}
}
if (list_count > 0)
fprintf( out, "</softwarelists>\n" );
else
fprintf( out, "No software lists found for this system\n" );
global_free( lists );
}
//-------------------------------------------------
// romident - identify ROMs by looking for
// matches in our internal database
//-------------------------------------------------
void cli_frontend::romident(const char *filename)
{
media_identifier ident(m_options);
// identify the file, then output results
mame_printf_info("Identifying %s....\n", filename);
ident.identify(filename);
// return the appropriate error code
if (ident.matches() == ident.total())
return;
else if (ident.matches() == ident.total() - ident.nonroms())
throw emu_fatalerror(MAMERR_IDENT_NONROMS, "");
else if (ident.matches() > 0)
throw emu_fatalerror(MAMERR_IDENT_PARTIAL, "");
else
throw emu_fatalerror(MAMERR_IDENT_NONE, "");
}
//-------------------------------------------------
// execute_commands - execute various frontend
// commands
//-------------------------------------------------
void cli_frontend::execute_commands(const char *exename)
{
// help?
if (strcmp(m_options.command(), CLICOMMAND_HELP) == 0)
{
display_help();
return;
}
// showusage?
if (strcmp(m_options.command(), CLICOMMAND_SHOWUSAGE) == 0)
{
astring helpstring;
emulator_info::printf_usage(exename, emulator_info::get_gamenoun());
mame_printf_info("\n\nOptions:\n%s", m_options.output_help(helpstring));
return;
}
// validate?
if (strcmp(m_options.command(), CLICOMMAND_VALIDATE) == 0)
{
validate_drivers(m_options);
validate_softlists(m_options);
return;
}
// other commands need the INIs parsed
astring option_errors;
m_options.parse_standard_inis(option_errors);
if (option_errors)
printf("%s\n", option_errors.cstr());
// createconfig?
if (strcmp(m_options.command(), CLICOMMAND_CREATECONFIG) == 0)
{
// attempt to open the output file
emu_file file(OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS);
if (file.open(emulator_info::get_configname(), ".ini") != FILERR_NONE)
throw emu_fatalerror("Unable to create file %s.ini\n",emulator_info::get_configname());
// generate the updated INI
astring initext;
file.puts(m_options.output_ini(initext));
return;
}
// showconfig?
if (strcmp(m_options.command(), CLICOMMAND_SHOWCONFIG) == 0)
{
// print the INI text
astring initext;
printf("%s\n", m_options.output_ini(initext));
return;
}
// all other commands call out to one of these helpers
static const struct
{
const char *option;
void (cli_frontend::*function)(const char *gamename);
} info_commands[] =
{
{ CLICOMMAND_LISTXML, &cli_frontend::listxml },
{ CLICOMMAND_LISTFULL, &cli_frontend::listfull },
{ CLICOMMAND_LISTSOURCE, &cli_frontend::listsource },
{ CLICOMMAND_LISTCLONES, &cli_frontend::listclones },
{ CLICOMMAND_LISTBROTHERS, &cli_frontend::listbrothers },
{ CLICOMMAND_LISTCRC, &cli_frontend::listcrc },
{ CLICOMMAND_LISTDEVICES, &cli_frontend::listdevices },
{ CLICOMMAND_LISTSLOTS, &cli_frontend::listslots },
{ CLICOMMAND_LISTROMS, &cli_frontend::listroms },
{ CLICOMMAND_LISTSAMPLES, &cli_frontend::listsamples },
{ CLICOMMAND_VERIFYROMS, &cli_frontend::verifyroms },
{ CLICOMMAND_VERIFYSAMPLES, &cli_frontend::verifysamples },
{ CLICOMMAND_LISTMEDIA, &cli_frontend::listmedia },
{ CLICOMMAND_LISTSOFTWARE, &cli_frontend::listsoftware },
{ CLICOMMAND_ROMIDENT, &cli_frontend::romident }
};
// find the command
for (int cmdindex = 0; cmdindex < ARRAY_LENGTH(info_commands); cmdindex++)
if (strcmp(m_options.command(), info_commands[cmdindex].option) == 0)
{
// parse any relevant INI files before proceeding
const char *sysname = m_options.system_name();
(this->*info_commands[cmdindex].function)((sysname[0] == 0) ? "*" : sysname);
return;
}
// if we get here, we don't know what has been requested
throw emu_fatalerror(MAMERR_INVALID_CONFIG, "Unknown command '%s' specified", m_options.command());
}
//-------------------------------------------------
// display_help - display help to standard
// output
//-------------------------------------------------
void cli_frontend::display_help()
{
mame_printf_info("%s v%s - %s\n%s\n\n", emulator_info::get_applongname(),build_version,emulator_info::get_fulllongname(),emulator_info::get_copyright_info());
mame_printf_info("%s\n", emulator_info::get_disclaimer());
emulator_info::printf_usage(emulator_info::get_appname(),emulator_info::get_gamenoun());
mame_printf_info("\n\n"
" %s -showusage for a brief list of options\n"
" %s -showconfig for a list of configuration options\n"
" %s -listmedia for a full list of supported media\n"
" %s -createconfig to create a %s.ini\n\n"
"For usage instructions, please consult the files config.txt and windows.txt.\n",emulator_info::get_appname(),
emulator_info::get_appname(),emulator_info::get_appname(),emulator_info::get_appname(),emulator_info::get_configname());
}
//-------------------------------------------------
// display_suggestions - display 10 possible
// matches for a given invalid gamename
//-------------------------------------------------
void cli_frontend::display_suggestions(const char *gamename)
{
}
//**************************************************************************
// MEDIA IDENTIFIER
//**************************************************************************
//-------------------------------------------------
// media_identifier - constructor
//-------------------------------------------------
media_identifier::media_identifier(cli_options &options)
: m_drivlist(options),
m_total(0),
m_matches(0),
m_nonroms(0)
{
}
//-------------------------------------------------
// identify - identify a directory, ZIP file,
// or raw file
//-------------------------------------------------
void media_identifier::identify(const char *filename)
{
// first try to open as a directory
osd_directory *directory = osd_opendir(filename);
if (directory != NULL)
{
// iterate over all files in the directory
for (const osd_directory_entry *entry = osd_readdir(directory); entry != NULL; entry = osd_readdir(directory))
if (entry->type == ENTTYPE_FILE)
{
astring curfile(filename, PATH_SEPARATOR, entry->name);
identify_file(curfile);
}
// close the directory and be done
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)
{
// loop over entries in the ZIP, skipping empty files and directories
for (const zip_file_header *entry = zip_file_first_file(zip); entry != NULL; entry = zip_file_next_file(zip))
if (entry->uncompressed_length != 0)
{
UINT8 *data = global_alloc_array(UINT8, 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);
global_free(data);
}
}
// close up
zip_file_close(zip);
}
// clear out any cached files
zip_file_cache_clear();
}
// otherwise, identify as a raw file
else
identify_file(filename);
}
//-------------------------------------------------
// identify_file - identify a file
//-------------------------------------------------
void media_identifier::identify_file(const char *name)
{
// CHD files need to be parsed and their hashes extracted from the header
if (core_filename_ends_with(name, ".chd"))
{
// output the name
astring basename;
mame_printf_info("%-20s", core_filename_extract_base(basename, name).cstr());
m_total++;
// attempt to open as a CHD; fail if not
chd_file *chd;
chd_error err = chd_open(name, CHD_OPEN_READ, NULL, &chd);
if (err != CHDERR_NONE)
{
mame_printf_info("NOT A CHD\n");
m_nonroms++;
return;
}
// fetch the header and close the file
chd_header header = *chd_get_header(chd);
chd_close(chd);
// error on writable CHDs
if (header.flags & CHDFLAGS_IS_WRITEABLE)
{
mame_printf_info("is a writeable CHD\n");
return;
}
// otherwise, get the hash collection for this CHD
static const UINT8 nullhash[20] = { 0 };
hash_collection hashes;
if (memcmp(nullhash, header.sha1, sizeof(header.sha1)) != 0)
hashes.add_from_buffer(hash_collection::HASH_SHA1, header.sha1, sizeof(header.sha1));
// determine whether this file exists
int found = find_by_hash(hashes, header.logicalbytes);
if (found == 0)
mame_printf_info("NO MATCH\n");
else
m_matches++;
}
// all other files have their hashes computed directly
else
{
// load the file and process if it opens and has a valid length
UINT32 length;
void *data;
file_error filerr = core_fload(name, &data, &length);
if (filerr == FILERR_NONE && length > 0)
{
identify_data(name, reinterpret_cast<UINT8 *>(data), length);
osd_free(data);
}
}
}
//-------------------------------------------------
// identify_data - identify a buffer full of
// data; if it comes from a .JED file, parse the
// fusemap into raw data first
//-------------------------------------------------
void media_identifier::identify_data(const char *name, const UINT8 *data, int length)
{
// if this is a '.jed' file, process it into raw bits first
UINT8 *tempjed = NULL;
jed_data jed;
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 = global_alloc_array(UINT8, length);
jedbin_output(&jed, tempjed, length);
data = tempjed;
}
// compute the hash of the data
hash_collection hashes;
hashes.compute(data, length, hash_collection::HASH_TYPES_CRC_SHA1);
// output the name
m_total++;
astring basename;
mame_printf_info("%-20s", core_filename_extract_base(basename, name).cstr());
// see if we can find a match in the ROMs
int found = find_by_hash(hashes, length);
// 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");
m_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
m_matches++;
// free any temporary JED data
global_free(tempjed);
}
//-------------------------------------------------
// find_by_hash - scan for a file in the list
// of drivers by hash
//-------------------------------------------------
int media_identifier::find_by_hash(const hash_collection &hashes, int length)
{
int found = 0;
// iterate over drivers
m_drivlist.reset();
while (m_drivlist.next())
{
// iterate over sources, regions and files within the region */
for (const rom_source *source = rom_first_source(m_drivlist.config()); source != NULL; source = rom_next_source(*source))
for (const rom_entry *region = rom_first_region(*source); region != NULL; region = rom_next_region(region))
for (const rom_entry *rom = rom_first_file(region); rom != NULL; rom = rom_next_file(rom))
{
hash_collection romhashes(ROM_GETHASHDATA(rom));
if (!romhashes.flag(hash_collection::FLAG_NO_DUMP) && hashes == romhashes)
{
bool baddump = romhashes.flag(hash_collection::FLAG_BAD_DUMP);
// output information about the match
if (found)
mame_printf_info(" ");
mame_printf_info("= %s%-20s %-10s %s\n", baddump ? "(BAD) " : "", ROM_GETNAME(rom), m_drivlist.driver().name, m_drivlist.driver().description);
found++;
}
}
// next iterate over softlists
for (const device_t *dev = m_drivlist.config().devicelist().first(SOFTWARE_LIST); dev != NULL; dev = dev->typenext())
{
software_list_config *swlist = (software_list_config *)downcast<const legacy_device_base *>(dev)->inline_config();
software_list *list = software_list_open(m_drivlist.options(), swlist->list_name, FALSE, NULL);
for (software_info *swinfo = software_list_find(list, "*", NULL); swinfo != NULL; swinfo = software_list_find(list, "*", swinfo))
for (software_part *part = software_find_part(swinfo, NULL, NULL); part != NULL; part = software_part_next(part))
for (const rom_entry *region = part->romdata; region != NULL; region = rom_next_region(region))
for (const rom_entry *rom = rom_first_file(region); rom != NULL; rom = rom_next_file(rom))
{
hash_collection romhashes(ROM_GETHASHDATA(rom));
if (hashes == romhashes)
{
bool baddump = romhashes.flag(hash_collection::FLAG_BAD_DUMP);
// output information about the match
if (found)
mame_printf_info(" ");
mame_printf_info("= %s%-20s %s:%s %s\n", baddump ? "(BAD) " : "", ROM_GETNAME(rom), swlist->list_name, swinfo->shortname, swinfo->longname);
found++;
}
}
software_list_close(list);
}
}
return found;
}