void info_xml_creator::output_rom(device_t &device) { // iterate over 3 different ROM "types": BIOS, ROMs, DISKs for (int rom_type = 0; rom_type < 3; rom_type++) for (const rom_entry *region = rom_first_region(device); region != NULL; region = rom_next_region(region)) { bool is_disk = ROMREGION_ISDISKDATA(region); // disk regions only work for disks if ((is_disk && rom_type != 2) || (!is_disk && rom_type == 2)) continue; // iterate through ROM entries for (const rom_entry *rom = rom_first_file(region); rom != NULL; rom = rom_next_file(rom)) { bool is_bios = ROM_GETBIOSFLAGS(rom); const char *name = ROM_GETNAME(rom); int offset = ROM_GETOFFSET(rom); const char *merge_name = NULL; char bios_name[100]; // BIOS ROMs only apply to bioses if ((is_bios && rom_type != 0) || (!is_bios && rom_type == 0)) continue; // if we have a valid ROM and we are a clone, see if we can find the parent ROM hash_collection hashes(ROM_GETHASHDATA(rom)); if (!hashes.flag(hash_collection::FLAG_NO_DUMP)) merge_name = get_merge_name(hashes); if (&device != &m_drivlist.config().root_device()) merge_name = NULL; // scan for a BIOS name bios_name[0] = 0; if (!is_disk && is_bios) { // scan backwards through the ROM entries for (const rom_entry *brom = rom - 1; brom != m_drivlist.driver().rom; brom--) if (ROMENTRY_ISSYSTEM_BIOS(brom)) { strcpy(bios_name, ROM_GETNAME(brom)); break; } } // opening tag if (!is_disk) fprintf(m_output, "\t\t<rom"); else fprintf(m_output, "\t\t<disk"); // add name, merge, bios, and size tags */ if (name != NULL && name[0] != 0) fprintf(m_output, " name=\"%s\"", xml_normalize_string(name)); if (merge_name != NULL) fprintf(m_output, " merge=\"%s\"", xml_normalize_string(merge_name)); if (bios_name[0] != 0) fprintf(m_output, " bios=\"%s\"", xml_normalize_string(bios_name)); if (!is_disk) fprintf(m_output, " size=\"%d\"", rom_file_size(rom)); // dump checksum information only if there is a known dump if (!hashes.flag(hash_collection::FLAG_NO_DUMP)) { // iterate over hash function types and print m_output their values astring tempstr; fprintf(m_output, " %s", hashes.attribute_string(tempstr)); } else fprintf(m_output, " status=\"nodump\""); // append a region name fprintf(m_output, " region=\"%s\"", ROMREGION_GETTAG(region)); // for non-disk entries, print offset if (!is_disk) fprintf(m_output, " offset=\"%x\"", offset); // for disk entries, add the disk index else { fprintf(m_output, " index=\"%x\"", DISK_GETINDEX(rom)); fprintf(m_output, " writable=\"%s\"", DISK_ISREADONLY(rom) ? "no" : "yes"); } // add optional flag if ((!is_disk && ROM_ISOPTIONAL(rom)) || (is_disk && DISK_ISOPTIONAL(rom))) fprintf(m_output, " optional=\"yes\""); fprintf(m_output, "/>\n"); } } }
static int open_rom_file(rom_load_data *romdata, const char *regiontag, const rom_entry *romp) { file_error filerr = FILERR_NOT_FOUND; UINT32 romsize = rom_file_size(romp); /* update status display */ display_loading_rom_message(romdata, ROM_GETNAME(romp)); /* extract CRC to use for searching */ UINT32 crc = 0; bool has_crc = hash_collection(ROM_GETHASHDATA(romp)).crc(crc); /* attempt reading up the chain through the parents. It automatically also attempts any kind of load by checksum supported by the archives. */ romdata->file = NULL; for (int drv = driver_list::find(romdata->machine().system()); romdata->file == NULL && drv != -1; drv = driver_list::clone(drv)) filerr = common_process_file(romdata->machine().options(), driver_list::driver(drv).name, has_crc, crc, romp, &romdata->file); /* if the region is load by name, load the ROM from there */ if (romdata->file == NULL && regiontag != NULL) { // check if we are dealing with softwarelists. if so, locationtag // is actually a concatenation of: listname + setname + parentname // separated by '%' (parentname being present only for clones) astring tag1(regiontag), tag2, tag3, tag4, tag5; bool is_list = FALSE; bool has_parent = FALSE; int separator1 = tag1.chr(0, '%'); if (separator1 != -1) { is_list = TRUE; // we are loading through softlists, split the listname from the regiontag tag4.cpysubstr(tag1, separator1 + 1, tag1.len() - separator1 + 1); tag1.del(separator1, tag1.len() - separator1); tag1.cat(PATH_SEPARATOR); // check if we are loading a clone (if this is the case also tag1 have a separator '%') int separator2 = tag4.chr(0, '%'); if (separator2 != -1) { has_parent = TRUE; // we are loading a clone through softlists, split the setname from the parentname tag5.cpysubstr(tag4, separator2 + 1, tag4.len() - separator2 + 1); tag4.del(separator2, tag4.len() - separator2); } // prepare locations where we have to load from: list/parentname & list/clonename astring swlist(tag1.cstr()); tag2.cpy(swlist.cat(tag4)); if (has_parent) { swlist.cpy(tag1); tag3.cpy(swlist.cat(tag5)); } } if (tag5.chr(0, '%') != -1) fatalerror("We do not support clones of clones!\n"); // try to load from the available location(s): // - if we are not using lists, we have regiontag only; // - if we are using lists, we have: list/clonename, list/parentname, clonename, parentname if (!is_list) filerr = common_process_file(romdata->machine().options(), tag1.cstr(), has_crc, crc, romp, &romdata->file); else { // try to load from list/setname if ((romdata->file == NULL) && (tag2.cstr() != NULL)) filerr = common_process_file(romdata->machine().options(), tag2.cstr(), has_crc, crc, romp, &romdata->file); // try to load from list/parentname if ((romdata->file == NULL) && has_parent && (tag3.cstr() != NULL)) filerr = common_process_file(romdata->machine().options(), tag3.cstr(), has_crc, crc, romp, &romdata->file); // try to load from setname if ((romdata->file == NULL) && (tag4.cstr() != NULL)) filerr = common_process_file(romdata->machine().options(), tag4.cstr(), has_crc, crc, romp, &romdata->file); // try to load from parentname if ((romdata->file == NULL) && has_parent && (tag5.cstr() != NULL)) filerr = common_process_file(romdata->machine().options(), tag5.cstr(), has_crc, crc, romp, &romdata->file); } } /* update counters */ romdata->romsloaded++; romdata->romsloadedsize += romsize; /* return the result */ return (filerr == FILERR_NONE); }
int rom_load_manager::open_rom_file(const char *regiontag, const rom_entry *romp, std::string &tried_file_names, bool from_list) { osd_file::error filerr = osd_file::error::NOT_FOUND; UINT32 romsize = rom_file_size(romp); tried_file_names = ""; /* update status display */ display_loading_rom_message(ROM_GETNAME(romp), from_list); /* extract CRC to use for searching */ UINT32 crc = 0; bool has_crc = util::hash_collection(ROM_GETHASHDATA(romp)).crc(crc); /* attempt reading up the chain through the parents. It automatically also attempts any kind of load by checksum supported by the archives. */ m_file = nullptr; for (int drv = driver_list::find(machine().system()); m_file == nullptr && drv != -1; drv = driver_list::clone(drv)) { if (tried_file_names.length() != 0) tried_file_names += " "; tried_file_names += driver_list::driver(drv).name; m_file = common_process_file(machine().options(), driver_list::driver(drv).name, has_crc, crc, romp, filerr); } /* if the region is load by name, load the ROM from there */ if (m_file == nullptr && regiontag != nullptr) { // check if we are dealing with softwarelists. if so, locationtag // is actually a concatenation of: listname + setname + parentname // separated by '%' (parentname being present only for clones) std::string tag1(regiontag), tag2, tag3, tag4, tag5; bool is_list = FALSE; bool has_parent = FALSE; int separator1 = tag1.find_first_of('%'); if (separator1 != -1) { is_list = TRUE; // we are loading through softlists, split the listname from the regiontag tag4.assign(tag1.substr(separator1 + 1, tag1.length() - separator1 + 1)); tag1.erase(separator1, tag1.length() - separator1); tag1.append(PATH_SEPARATOR); // check if we are loading a clone (if this is the case also tag1 have a separator '%') int separator2 = tag4.find_first_of('%'); if (separator2 != -1) { has_parent = TRUE; // we are loading a clone through softlists, split the setname from the parentname tag5.assign(tag4.substr(separator2 + 1, tag4.length() - separator2 + 1)); tag4.erase(separator2, tag4.length() - separator2); } // prepare locations where we have to load from: list/parentname & list/clonename std::string swlist(tag1); tag2.assign(swlist.append(tag4)); if (has_parent) { swlist.assign(tag1); tag3.assign(swlist.append(tag5)); } } if (tag5.find_first_of('%') != -1) fatalerror("We do not support clones of clones!\n"); // try to load from the available location(s): // - if we are not using lists, we have regiontag only; // - if we are using lists, we have: list/clonename, list/parentname, clonename, parentname if (!is_list) { tried_file_names += " " + tag1; m_file = common_process_file(machine().options(), tag1.c_str(), has_crc, crc, romp, filerr); } else { // try to load from list/setname if ((m_file == nullptr) && (tag2.c_str() != nullptr)) { tried_file_names += " " + tag2; m_file = common_process_file(machine().options(), tag2.c_str(), has_crc, crc, romp, filerr); } // try to load from list/parentname if ((m_file == nullptr) && has_parent && (tag3.c_str() != nullptr)) { tried_file_names += " " + tag3; m_file = common_process_file(machine().options(), tag3.c_str(), has_crc, crc, romp, filerr); } // try to load from setname if ((m_file == nullptr) && (tag4.c_str() != nullptr)) { tried_file_names += " " + tag4; m_file = common_process_file(machine().options(), tag4.c_str(), has_crc, crc, romp, filerr); } // try to load from parentname if ((m_file == nullptr) && has_parent && (tag5.c_str() != nullptr)) { tried_file_names += " " + tag5; m_file = common_process_file(machine().options(), tag5.c_str(), has_crc, crc, romp, filerr); } } } /* update counters */ m_romsloaded++; m_romsloadedsize += romsize; /* return the result */ return (filerr == osd_file::error::NONE); }
void rom_load_manager::load_software_part_region(device_t &device, software_list_device &swlist, const char *swname, const rom_entry *start_region) { std::string locationtag(swlist.list_name()), breakstr("%"); const rom_entry *region; std::string regiontag; m_errorstring.clear(); m_softwarningstring.clear(); m_romstotal = 0; m_romstotalsize = 0; m_romsloadedsize = 0; const software_info *swinfo = swlist.find(swname); if (swinfo != nullptr) { UINT32 supported = swinfo->supported(); if (supported == SOFTWARE_SUPPORTED_PARTIAL) { m_errorstring.append(string_format("WARNING: support for software %s (in list %s) is only partial\n", swname, swlist.list_name())); m_softwarningstring.append(string_format("Support for software %s (in list %s) is only partial\n", swname, swlist.list_name())); } if (supported == SOFTWARE_SUPPORTED_NO) { m_errorstring.append(string_format("WARNING: support for software %s (in list %s) is only preliminary\n", swname, swlist.list_name())); m_softwarningstring.append(string_format("Support for software %s (in list %s) is only preliminary\n", swname, swlist.list_name())); } // attempt reading up the chain through the parents and create a locationtag std::string in the format // " swlist % clonename % parentname " // open_rom_file contains the code to split the elements and to create paths to load from locationtag.append(breakstr); while (swinfo != nullptr) { locationtag.append(swinfo->shortname()).append(breakstr); swinfo = !swinfo->parentname().empty() ? swlist.find(swinfo->parentname().c_str()) : nullptr; } // strip the final '%' locationtag.erase(locationtag.length() - 1, 1); } /* loop until we hit the end */ for (region = start_region; region != nullptr; region = rom_next_region(region)) { UINT32 regionlength = ROMREGION_GETLENGTH(region); regiontag = device.subtag(ROMREGION_GETTAG(region)); LOG(("Processing region \"%s\" (length=%X)\n", regiontag.c_str(), regionlength)); /* the first entry must be a region */ assert(ROMENTRY_ISREGION(region)); /* if this is a device region, override with the device width and endianness */ endianness_t endianness = ROMREGION_ISBIGENDIAN(region) ? ENDIANNESS_BIG : ENDIANNESS_LITTLE; UINT8 width = ROMREGION_GETWIDTH(region) / 8; memory_region *memregion = machine().root_device().memregion(regiontag.c_str()); if (memregion != nullptr) { if (machine().device(regiontag.c_str()) != nullptr) normalize_flags_for_device(machine(), regiontag.c_str(), width, endianness); /* clear old region (todo: should be moved to an image unload function) */ machine().memory().region_free(memregion->name()); } /* remember the base and length */ m_region = machine().memory().region_alloc(regiontag.c_str(), regionlength, width, endianness); LOG(("Allocated %X bytes @ %p\n", m_region->bytes(), m_region->base())); /* clear the region if it's requested */ if (ROMREGION_ISERASE(region)) memset(m_region->base(), ROMREGION_GETERASEVAL(region), m_region->bytes()); /* or if it's sufficiently small (<= 4MB) */ else if (m_region->bytes() <= 0x400000) memset(m_region->base(), 0, m_region->bytes()); #ifdef MAME_DEBUG /* if we're debugging, fill region with random data to catch errors */ else fill_random(m_region->base(), m_region->bytes()); #endif /* update total number of roms */ for (const rom_entry *rom = rom_first_file(region); rom != nullptr; rom = rom_next_file(rom)) { m_romstotal++; m_romstotalsize += rom_file_size(rom); } /* now process the entries in the region */ if (ROMREGION_ISROMDATA(region)) process_rom_entries(locationtag.c_str(), region, region + 1, &device, TRUE); else if (ROMREGION_ISDISKDATA(region)) process_disk_entries(regiontag.c_str(), region, region + 1, locationtag.c_str()); } /* now go back and post-process all the regions */ for (region = start_region; region != nullptr; region = rom_next_region(region)) { regiontag = device.subtag(ROMREGION_GETTAG(region)); region_post_process(regiontag.c_str(), ROMREGION_ISINVERTED(region)); } /* display the results and exit */ display_rom_load_results(TRUE); }
static int info_listsoftware(core_options *options, const char *gamename) { FILE *out = stdout; int nr_lists = 0; char ** lists = NULL; int list_idx = 0; /* First determine the maximum number of lists we might encounter */ for ( int drvindex = 0; drivers[drvindex] != NULL; drvindex++ ) { if ( mame_strwildcmp( gamename, drivers[drvindex]->name ) == 0 ) { /* allocate the machine config */ machine_config *config = global_alloc(machine_config(drivers[drvindex]->machine_config)); for (const device_config *dev = config->m_devicelist.first(SOFTWARE_LIST); dev != NULL; dev = dev->typenext()) { software_list_config *swlist = (software_list_config *)downcast<const legacy_device_config_base *>(dev)->inline_config(); for ( int i = 0; i < DEVINFO_STR_SWLIST_MAX - DEVINFO_STR_SWLIST_0; i++ ) { if ( swlist->list_name[i] && *swlist->list_name[i] && (swlist->list_type == SOFTWARE_LIST_ORIGINAL_SYSTEM)) nr_lists++; } } /* free the machine config */ global_free(config); } } lists = global_alloc_array( char *, nr_lists ); 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, 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" "\t\t\t<!ELEMENT part (dataarea*)>\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<!ATTLIST part feature 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 #REQUIRED>\n" "\t\t\t\t\t\t<!ATTLIST rom crc CDATA #IMPLIED>\n" "\t\t\t\t\t\t<!ATTLIST rom md5 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 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) #IMPLIED>\n" "]>\n\n" "<softwarelists>\n" ); for ( int drvindex = 0; drivers[drvindex] != NULL; drvindex++ ) { if ( mame_strwildcmp( gamename, drivers[drvindex]->name ) == 0 ) { /* allocate the machine config */ machine_config *config = global_alloc(machine_config(drivers[drvindex]->machine_config)); for (const device_config *dev = config->m_devicelist.first(SOFTWARE_LIST); dev != NULL; dev = dev->typenext()) { software_list_config *swlist = (software_list_config *)downcast<const legacy_device_config_base *>(dev)->inline_config(); for ( int i = 0; i < DEVINFO_STR_SWLIST_MAX - DEVINFO_STR_SWLIST_0; i++ ) { if ( swlist->list_name[i] && *swlist->list_name[i] && (swlist->list_type == SOFTWARE_LIST_ORIGINAL_SYSTEM)) { software_list *list = software_list_open( options, swlist->list_name[i], FALSE, NULL ); if ( list ) { /* Verify if we have encountered this list before */ bool seen_before = false; for ( int l = 0; l < list_idx && !seen_before; l++ ) { if ( ! strcmp( swlist->list_name[i], lists[l] ) ) { seen_before = true; } } if ( ! seen_before ) { lists[list_idx] = core_strdup( swlist->list_name[i] ); list_idx++; fprintf(out, "\t<softwarelist name=\"%s\">\n", swlist->list_name[i] ); 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_ ); // if ( part->feature ) // fprintf( out, " features=\"%s\"", part->feature ); fprintf( out, ">\n"); /* TODO: display rom region information */ for ( const rom_entry *region = part->romdata; region; region = rom_next_region( region ) ) { fprintf( out, "\t\t\t\t<dataarea name=\"%s\" size=\"%x\">\n", ROMREGION_GETTAG(region), ROMREGION_GETLENGTH(region) ); for ( const rom_entry *rom = rom_first_file( region ); rom && !ROMENTRY_ISREGIONEND(rom); rom++ ) { if ( ROMENTRY_ISFILE(rom) ) { fprintf( out, "\t\t\t\t\t<rom name=\"%s\" size=\"%d\"", xml_normalize_string(ROM_GETNAME(rom)), rom_file_size(rom) ); /* dump checksum information only if there is a known dump */ if (!hash_data_has_info(ROM_GETHASHDATA(rom), HASH_INFO_NO_DUMP)) { char checksum[HASH_BUF_SIZE]; int hashtype; /* iterate over hash function types and print out their values */ for (hashtype = 0; hashtype < HASH_NUM_FUNCTIONS; hashtype++) if (hash_data_extract_printable_checksum(ROM_GETHASHDATA(rom), 1 << hashtype, checksum)) fprintf(out, " %s=\"%s\"", hash_function_name(1 << hashtype), checksum); } fprintf( out, " offset=\"%x\"", ROM_GETOFFSET(rom) ); if ( hash_data_has_info(ROM_GETHASHDATA(rom), HASH_INFO_BAD_DUMP) ) fprintf( out, " status=\"baddump\"" ); if ( hash_data_has_info(ROM_GETHASHDATA(rom), HASH_INFO_NO_DUMP) ) fprintf( out, " status=\"nodump\"" ); fprintf( out, "/>\n" ); } else if ( ROMENTRY_ISRELOAD(rom) ) { fprintf( out, "\t\t\t\t\t<rom size=\"%d\" offset=\"%x\" loadflag=\"reload\" />\n", ROM_GETLENGTH(rom), ROM_GETOFFSET(rom) ); } } fprintf( out, "\t\t\t\t</dataarea>\n" ); } fprintf( out, "\t\t\t</part>\n" ); } fprintf( out, "\t\t</software>\n" ); } fprintf(out, "\t</softwarelist>\n" ); } software_list_close( list ); } } } } global_free(config); } } fprintf( out, "</softwarelists>\n" ); global_free( lists ); return MAMERR_NONE; }
int cli_info_listroms(core_options *options, const char *gamename) { int drvindex, count = 0; /* iterate over drivers */ for (drvindex = 0; drivers[drvindex] != NULL; drvindex++) if (mame_strwildcmp(gamename, drivers[drvindex]->name) == 0) { machine_config *config = global_alloc(machine_config(drivers[drvindex]->machine_config)); const rom_entry *region, *rom; const rom_source *source; /* 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 sources, regions and then ROMs within the region */ for (source = rom_first_source(drivers[drvindex], config); source != NULL; source = rom_next_source(drivers[drvindex], config, source)) for (region = rom_first_region(drivers[drvindex], source); region != NULL; region = rom_next_region(region)) for (rom = rom_first_file(region); rom != NULL; 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 = rom_file_size(rom); /* 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++; global_free(config); } return (count > 0) ? MAMERR_NONE : MAMERR_NO_SUCH_GAME; }