Softlist support cleanup and addition of compatibility filtering [Miodrag Milanovic]

This commit is contained in:
Miodrag Milanovic 2011-12-22 13:01:48 +00:00
parent eb2eb54049
commit 90314f2bb4
6 changed files with 420 additions and 458 deletions

View File

@ -189,55 +189,47 @@ int cli_frontend::execute(int argc, char **argv)
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();
for (int i = 0; i < DEVINFO_STR_SWLIST_MAX - DEVINFO_STR_SWLIST_0; i++)
software_list *list = software_list_open(m_options, swlist->list_name, FALSE, NULL);
if (list)
{
if (swlist->list_name[i] && *swlist->list_name[i])
{
software_list *list = software_list_open(m_options, swlist->list_name[i], FALSE, NULL);
if (list)
software_info *swinfo = software_list_find(list, m_options.software_name(), NULL);
if (swinfo!=NULL) {
for (software_part *swpart = software_find_part(swinfo, NULL, NULL); swpart != NULL; swpart = software_part_next(swpart))
{
software_info *swinfo = software_list_find(list, m_options.software_name(), NULL);
if (swinfo!=NULL) {
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 (mount==NULL || strcmp(mount,"no")!=0) {
// loop trough all parts
// search for a 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 *mount = software_part_get_feature(swpart, "automount");
if (mount==NULL || strcmp(mount,"no")!=0) {
// loop trough all parts
// search for a 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 *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[i],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;
}
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;
}
}
}
software_list_close(list);
found = TRUE;
break;
}
}
software_list_close(list);
found = TRUE;
break;
}
if (found) break;
}
software_list_close(list);
if (found) break;
}
if (!found) {
@ -1000,10 +992,8 @@ void cli_frontend::listsoftware(const char *gamename)
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();
for (int listnum = 0; listnum < DEVINFO_STR_SWLIST_MAX - DEVINFO_STR_SWLIST_0; listnum++)
if (swlist->list_name[listnum] && *swlist->list_name[listnum] && swlist->list_type == SOFTWARE_LIST_ORIGINAL_SYSTEM)
list_count++;
if (swlist->list_type == SOFTWARE_LIST_ORIGINAL_SYSTEM)
list_count++;
}
// allocate a list
@ -1088,162 +1078,159 @@ void cli_frontend::listsoftware(const char *gamename)
{
software_list_config *swlist = (software_list_config *)downcast<const legacy_device_base *>(dev)->inline_config();
for (int listnum = 0; listnum < DEVINFO_STR_SWLIST_MAX - DEVINFO_STR_SWLIST_0; listnum++)
if (swlist->list_type == SOFTWARE_LIST_ORIGINAL_SYSTEM)
{
if (swlist->list_name[listnum] && *swlist->list_name[listnum] && swlist->list_type == SOFTWARE_LIST_ORIGINAL_SYSTEM)
software_list *list = software_list_open(m_options, swlist->list_name, FALSE, NULL);
if ( list )
{
software_list *list = software_list_open(m_options, swlist->list_name[listnum], FALSE, NULL);
/* 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 ( list )
if (!seen_before)
{
/* 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[listnum])
seen_before = true;
lists[list_count++] = swlist->list_name;
software_list_parse( list, NULL, NULL );
if (!seen_before)
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 ) )
{
lists[list_count++] = swlist->list_name[listnum];
software_list_parse( list, NULL, NULL );
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 ) );
fprintf(out, "\t<softwarelist name=\"%s\" description=\"%s\">\n", swlist->list_name[listnum], 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 ) )
for ( software_part *part = software_find_part( swinfo, NULL, NULL ); part != NULL; part = software_part_next( part ) )
{
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 ) );
fprintf( out, "\t\t\t<part name=\"%s\"", part->name );
if ( part->interface_ )
fprintf( out, " interface=\"%s\"", part->interface_ );
for ( software_part *part = software_find_part( swinfo, NULL, NULL ); part != NULL; part = software_part_next( part ) )
fprintf( out, ">\n");
if ( part->featurelist )
{
fprintf( out, "\t\t\t<part name=\"%s\"", part->name );
if ( part->interface_ )
fprintf( out, " interface=\"%s\"", part->interface_ );
feature_list *flist = part->featurelist;
fprintf( out, ">\n");
if ( part->featurelist )
while( flist )
{
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;
}
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" );
/* 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</softwarelist>\n" );
fprintf( out, "\t\t</software>\n" );
}
software_list_close( list );
fprintf(out, "\t</softwarelist>\n" );
}
software_list_close( list );
}
}
}
@ -1645,32 +1632,26 @@ int media_identifier::find_by_hash(const hash_collection &hashes, int length)
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 (int listnum = 0; listnum < DEVINFO_STR_SWLIST_MAX - DEVINFO_STR_SWLIST_0; listnum++)
if (swlist->list_name[listnum] != NULL)
{
software_list *list = software_list_open(m_drivlist.options(), swlist->list_name[listnum], 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);
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[listnum], swinfo->shortname, swinfo->longname);
found++;
}
}
software_list_close(list);
}
// 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);
}
}

View File

@ -181,6 +181,7 @@ const char info_xml_creator::s_dtd_string[] =
"\t\t<!ELEMENT softwarelist EMPTY>\n"
"\t\t\t<!ATTLIST softwarelist name CDATA #REQUIRED>\n"
"\t\t\t<!ATTLIST softwarelist status (original|compatible) #REQUIRED>\n"
"\t\t\t<!ATTLIST softwarelist filter CDATA #IMPLIED>\n"
"\t\t<!ELEMENT ramoption (#PCDATA)>\n"
"\t\t\t<!ATTLIST ramoption default CDATA #IMPLIED>\n"
"]>";
@ -1229,13 +1230,12 @@ void info_xml_creator::output_software_list()
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();
for (int i = 0; i < DEVINFO_STR_SWLIST_MAX - DEVINFO_STR_SWLIST_0; i++)
if (swlist->list_name[i])
{
fprintf(m_output, "\t\t<softwarelist name=\"%s\" ", swlist->list_name[i]);
fprintf(m_output, "status=\"%s\" />\n", (swlist->list_type == SOFTWARE_LIST_ORIGINAL_SYSTEM) ? "original" : "compatible");
}
fprintf(m_output, "\t\t<softwarelist name=\"%s\" ", swlist->list_name);
fprintf(m_output, "status=\"%s\" ", (swlist->list_type == SOFTWARE_LIST_ORIGINAL_SYSTEM) ? "original" : "compatible");
if (swlist->filter) {
fprintf(m_output, "filter=\"%s\" ", swlist->filter);
}
fprintf(m_output, "/>\n");
}
}

View File

@ -1433,39 +1433,32 @@ void software_display_matches(const device_list &devlist,emu_options &options, c
for (device_t *swlists = devlist.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(options, swlist->list_name, FALSE, NULL);
for (int i = 0; i < DEVINFO_STR_SWLIST_MAX - DEVINFO_STR_SWLIST_0; i++)
if (list)
{
if (swlist->list_name[i] && *swlist->list_name[i])
software_info *matches[10] = { 0 };
int softnum;
software_list_parse(list, list->error_proc, NULL);
// get the top 5 approximate matches for the selected device interface (i.e. only carts for cartslot, etc.)
software_list_find_approx_matches(list, name, ARRAY_LENGTH(matches), matches, interface);
if (matches[0] != 0)
{
software_list *list = software_list_open(options, swlist->list_name[i], FALSE, NULL);
if (swlist->list_type == SOFTWARE_LIST_ORIGINAL_SYSTEM)
mame_printf_error("* Software list \"%s\" (%s) matches: \n", swlist->list_name, software_list_get_description(list));
else
mame_printf_error("* Compatible software list \"%s\" (%s) matches: \n", swlist->list_name, software_list_get_description(list));
if (list)
{
software_info *matches[10] = { 0 };
int softnum;
// print them out
for (softnum = 0; softnum < ARRAY_LENGTH(matches); softnum++)
if (matches[softnum] != NULL)
mame_printf_error("%-18s%s\n", matches[softnum]->shortname, matches[softnum]->longname);
software_list_parse(list, list->error_proc, NULL);
// get the top 5 approximate matches for the selected device interface (i.e. only carts for cartslot, etc.)
software_list_find_approx_matches(list, name, ARRAY_LENGTH(matches), matches, interface);
if (matches[0] != 0)
{
if (swlist->list_type == SOFTWARE_LIST_ORIGINAL_SYSTEM)
mame_printf_error("* Software list \"%s\" (%s) matches: \n", swlist->list_name[i], software_list_get_description(list));
else
mame_printf_error("* Compatible software list \"%s\" (%s) matches: \n", swlist->list_name[i], software_list_get_description(list));
// print them out
for (softnum = 0; softnum < ARRAY_LENGTH(matches); softnum++)
if (matches[softnum] != NULL)
mame_printf_error("%-18s%s\n", matches[softnum]->shortname, matches[softnum]->longname);
mame_printf_error("\n");
}
software_list_close(list);
}
mame_printf_error("\n");
}
software_list_close(list);
}
}
}
@ -1508,32 +1501,28 @@ static void find_software_item(const device_list &devlist, emu_options &options,
{
software_list_config *swlist = (software_list_config *)downcast<const legacy_device_base *>(swlists)->inline_config();
UINT32 i = DEVINFO_STR_SWLIST_0;
swlist_name = swlist->list_name;
while ( ! *software_part_ptr && i <= DEVINFO_STR_SWLIST_MAX )
if (swlist->list_type == SOFTWARE_LIST_ORIGINAL_SYSTEM)
{
swlist_name = swlist->list_name[i-DEVINFO_STR_SWLIST_0];
if ( swlist_name && *swlist_name && (swlist->list_type == SOFTWARE_LIST_ORIGINAL_SYSTEM))
if ( *software_list_ptr )
{
if ( *software_list_ptr )
software_list_close( *software_list_ptr );
}
*software_list_ptr = software_list_open( options, swlist_name, FALSE, NULL );
if ( software_list_ptr )
{
*software_info_ptr = software_list_find( *software_list_ptr, swname, NULL );
if ( *software_info_ptr )
{
software_list_close( *software_list_ptr );
}
*software_list_ptr = software_list_open( options, swlist_name, FALSE, NULL );
if ( software_list_ptr )
{
*software_info_ptr = software_list_find( *software_list_ptr, swname, NULL );
if ( *software_info_ptr )
{
*software_part_ptr = software_find_part( *software_info_ptr, swpart, interface );
}
*software_part_ptr = software_find_part( *software_info_ptr, swpart, interface );
break;
}
}
i++;
}
}
}
@ -1811,9 +1800,8 @@ void validate_softlists(emu_options &options)
{
software_list_config *swlist = (software_list_config *)downcast<const legacy_device_base *>(dev)->inline_config();
for (int listnum = 0; listnum < DEVINFO_STR_SWLIST_MAX - DEVINFO_STR_SWLIST_0; listnum++)
if (swlist->list_name[listnum] && *swlist->list_name[listnum] && swlist->list_type == SOFTWARE_LIST_ORIGINAL_SYSTEM)
list_count++;
if (swlist->list_type == SOFTWARE_LIST_ORIGINAL_SYSTEM)
list_count++;
}
// allocate a list
@ -1832,161 +1820,155 @@ void validate_softlists(emu_options &options)
enum { NAME_LEN_PARENT = 8, NAME_LEN_CLONE = 16 };
for (int i = 0; i < DEVINFO_STR_SWLIST_MAX - DEVINFO_STR_SWLIST_0; i++)
software_list *list = software_list_open(options, swlist->list_name, FALSE, NULL);
if ( list )
{
if (swlist->list_name[i])
/* 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)
{
software_list *list = software_list_open(options, swlist->list_name[i], FALSE, NULL);
if ( list )
lists[list_count++] = swlist->list_name;
software_list_parse( list, &validate_error_proc, NULL );
for (software_info *swinfo = software_list_find(list, "*", NULL); swinfo != NULL; swinfo = software_list_find(list, "*", swinfo))
{
/* 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[i])
seen_before = true;
const char *s;
int is_clone = 0;
if (!seen_before)
/* First, check if the xml got corrupted: */
/* Did we lost any description? */
if (swinfo->longname == NULL)
{
lists[list_count++] = swlist->list_name[i];
software_list_parse( list, &validate_error_proc, NULL );
mame_printf_error("%s: %s has no description\n", list->file->filename(), swinfo->shortname);
error = TRUE; break;
}
for (software_info *swinfo = software_list_find(list, "*", NULL); swinfo != NULL; swinfo = software_list_find(list, "*", swinfo))
/* Did we lost any year? */
if (swinfo->year == NULL)
{
mame_printf_error("%s: %s has no year\n", list->file->filename(), swinfo->shortname);
error = TRUE; break;
}
/* Did we lost any publisher? */
if (swinfo->publisher == NULL)
{
mame_printf_error("%s: %s has no publisher\n", list->file->filename(), swinfo->shortname);
error = TRUE; break;
}
/* Second, since the xml is fine, run additional checks: */
/* check for duplicate names */
if (names.add(swinfo->shortname, swinfo, FALSE) == TMERR_DUPLICATE)
{
software_info *match = names.find(swinfo->shortname);
mame_printf_error("%s: %s is a duplicate name (%s)\n", list->file->filename(), swinfo->shortname, match->shortname);
error = TRUE;
}
/* check for duplicate descriptions */
if (descriptions.add(astring(swinfo->longname).tolower().cstr(), swinfo, FALSE) == TMERR_DUPLICATE)
{
mame_printf_error("%s: %s is a duplicate description (%s)\n", list->file->filename(), swinfo->longname, swinfo->shortname);
error = TRUE;
}
if (swinfo->parentname != NULL)
{
is_clone = 1;
if (strcmp(swinfo->parentname, swinfo->shortname) == 0)
{
const char *s;
int is_clone = 0;
mame_printf_error("%s: %s is set as a clone of itself\n", list->file->filename(), swinfo->shortname);
error = TRUE;
break;
}
/* First, check if the xml got corrupted: */
/* make sure the parent exists */
software_info *swinfo2 = software_list_find(list, swinfo->parentname, NULL );
/* Did we lost any description? */
if (swinfo->longname == NULL)
if (!swinfo2)
{
mame_printf_error("%s: parent '%s' software for '%s' not found\n", list->file->filename(), swinfo->parentname, swinfo->shortname);
error = TRUE;
}
else
{
if (swinfo2->parentname != NULL)
{
mame_printf_error("%s: %s has no description\n", list->file->filename(), swinfo->shortname);
error = TRUE; break;
}
/* Did we lost any year? */
if (swinfo->year == NULL)
{
mame_printf_error("%s: %s has no year\n", list->file->filename(), swinfo->shortname);
error = TRUE; break;
}
/* Did we lost any publisher? */
if (swinfo->publisher == NULL)
{
mame_printf_error("%s: %s has no publisher\n", list->file->filename(), swinfo->shortname);
error = TRUE; break;
}
/* Second, since the xml is fine, run additional checks: */
/* check for duplicate names */
if (names.add(swinfo->shortname, swinfo, FALSE) == TMERR_DUPLICATE)
{
software_info *match = names.find(swinfo->shortname);
mame_printf_error("%s: %s is a duplicate name (%s)\n", list->file->filename(), swinfo->shortname, match->shortname);
mame_printf_error("%s: %s is a clone of a clone\n", list->file->filename(), swinfo->shortname);
error = TRUE;
}
}
}
/* check for duplicate descriptions */
if (descriptions.add(astring(swinfo->longname).tolower().cstr(), swinfo, FALSE) == TMERR_DUPLICATE)
/* make sure the driver name is 8 chars or less */
if ((is_clone && strlen(swinfo->shortname) > NAME_LEN_CLONE) || ((!is_clone) && strlen(swinfo->shortname) > NAME_LEN_PARENT))
{
mame_printf_error("%s: %s %s driver name must be %d characters or less\n", list->file->filename(), swinfo->shortname,
is_clone ? "clone" : "parent", is_clone ? NAME_LEN_CLONE : NAME_LEN_PARENT);
error = TRUE;
}
/* make sure the year is only digits, '?' or '+' */
for (s = swinfo->year; *s; s++)
if (!isdigit((UINT8)*s) && *s != '?' && *s != '+')
{
mame_printf_error("%s: %s has an invalid year '%s'\n", list->file->filename(), swinfo->shortname, swinfo->year);
error = TRUE;
break;
}
for (software_part *swpart = software_find_part(swinfo, NULL, NULL); swpart != NULL; swpart = software_part_next(swpart))
{
if (swpart->interface_ == NULL)
{
mame_printf_error("%s: %s has a part (%s) without interface\n", list->file->filename(), swinfo->shortname, swpart->name);
error = TRUE;
}
if (software_find_romdata(swpart, NULL) == NULL)
{
mame_printf_error("%s: %s has a part (%s) with no data\n", list->file->filename(), swinfo->shortname, swpart->name);
error = TRUE;
}
for (struct rom_entry *swdata = software_find_romdata(swpart, NULL); swdata != NULL; swdata = software_romdata_next(swdata))
{
struct rom_entry *data = swdata;
if (data->_name && data->_hashdata)
{
mame_printf_error("%s: %s is a duplicate description (%s)\n", list->file->filename(), swinfo->longname, swinfo->shortname);
error = TRUE;
}
const char *str;
if (swinfo->parentname != NULL)
{
is_clone = 1;
if (strcmp(swinfo->parentname, swinfo->shortname) == 0)
{
mame_printf_error("%s: %s is set as a clone of itself\n", list->file->filename(), swinfo->shortname);
error = TRUE;
break;
}
/* make sure the parent exists */
software_info *swinfo2 = software_list_find(list, swinfo->parentname, NULL );
if (!swinfo2)
{
mame_printf_error("%s: parent '%s' software for '%s' not found\n", list->file->filename(), swinfo->parentname, swinfo->shortname);
error = TRUE;
}
else
{
if (swinfo2->parentname != NULL)
/* make sure it's all lowercase */
for (str = data->_name; *str; str++)
if (tolower((UINT8)*str) != *str)
{
mame_printf_error("%s: %s is a clone of a clone\n", list->file->filename(), swinfo->shortname);
mame_printf_error("%s: %s has upper case ROM name %s\n", list->file->filename(), swinfo->shortname, data->_name);
error = TRUE;
break;
}
}
}
/* make sure the driver name is 8 chars or less */
if ((is_clone && strlen(swinfo->shortname) > NAME_LEN_CLONE) || ((!is_clone) && strlen(swinfo->shortname) > NAME_LEN_PARENT))
{
mame_printf_error("%s: %s %s driver name must be %d characters or less\n", list->file->filename(), swinfo->shortname,
is_clone ? "clone" : "parent", is_clone ? NAME_LEN_CLONE : NAME_LEN_PARENT);
error = TRUE;
}
/* make sure the year is only digits, '?' or '+' */
for (s = swinfo->year; *s; s++)
if (!isdigit((UINT8)*s) && *s != '?' && *s != '+')
/* make sure the hash is valid */
hash_collection hashes;
if (!hashes.from_internal_string(data->_hashdata))
{
mame_printf_error("%s: %s has an invalid year '%s'\n", list->file->filename(), swinfo->shortname, swinfo->year);
mame_printf_error("%s: %s has rom '%s' with an invalid hash string '%s'\n", list->file->filename(), swinfo->shortname, data->_name, data->_hashdata);
error = TRUE;
break;
}
for (software_part *swpart = software_find_part(swinfo, NULL, NULL); swpart != NULL; swpart = software_part_next(swpart))
{
if (swpart->interface_ == NULL)
{
mame_printf_error("%s: %s has a part (%s) without interface\n", list->file->filename(), swinfo->shortname, swpart->name);
error = TRUE;
}
if (software_find_romdata(swpart, NULL) == NULL)
{
mame_printf_error("%s: %s has a part (%s) with no data\n", list->file->filename(), swinfo->shortname, swpart->name);
error = TRUE;
}
for (struct rom_entry *swdata = software_find_romdata(swpart, NULL); swdata != NULL; swdata = software_romdata_next(swdata))
{
struct rom_entry *data = swdata;
if (data->_name && data->_hashdata)
{
const char *str;
/* make sure it's all lowercase */
for (str = data->_name; *str; str++)
if (tolower((UINT8)*str) != *str)
{
mame_printf_error("%s: %s has upper case ROM name %s\n", list->file->filename(), swinfo->shortname, data->_name);
error = TRUE;
break;
}
/* make sure the hash is valid */
hash_collection hashes;
if (!hashes.from_internal_string(data->_hashdata))
{
mame_printf_error("%s: %s has rom '%s' with an invalid hash string '%s'\n", list->file->filename(), swinfo->shortname, data->_name, data->_hashdata);
error = TRUE;
}
}
}
}
}
}
software_list_close(list);
}
}
software_list_close(list);
}
}
}
@ -2013,14 +1995,6 @@ DEVICE_GET_INFO( software_list )
case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break;
case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright MESS Team"); break;
}
if ( state >= DEVINFO_STR_SWLIST_0 && state <= DEVINFO_STR_SWLIST_MAX )
{
software_list_config *config = (software_list_config *)downcast<const legacy_device_base *>(device)->inline_config();
if ( config->list_name[ state - DEVINFO_STR_SWLIST_0 ] )
strcpy(info->s, config->list_name[ state - DEVINFO_STR_SWLIST_0 ]);
}
}

View File

@ -130,44 +130,41 @@ DECLARE_LEGACY_DEVICE(SOFTWARE_LIST, software_list);
#define SOFTWARE_SUPPORTED_PARTIAL 1
#define SOFTWARE_SUPPORTED_NO 2
#define SOFTWARE_LIST_CONFIG_SIZE 10
typedef struct _software_list_config software_list_config;
struct _software_list_config
{
char *list_name[SOFTWARE_LIST_CONFIG_SIZE];
char *list_name;
UINT32 list_type;
const char *filter;
};
#define DEVINFO_STR_SWLIST_0 (DEVINFO_STR_DEVICE_SPECIFIC+0)
#define DEVINFO_STR_SWLIST_MAX (DEVINFO_STR_SWLIST_0 + SOFTWARE_LIST_CONFIG_SIZE - 1)
#define SOFTWARE_LIST_ORIGINAL_SYSTEM 0
#define SOFTWARE_LIST_COMPATIBLE_SYSTEM 1
#define MCFG_SOFTWARE_LIST_CONFIG(_idx,_list,_list_type) \
MCFG_DEVICE_CONFIG_DATAPTR_ARRAY(software_list_config, list_name, _idx, _list) \
#define MCFG_SOFTWARE_LIST_CONFIG(_list,_list_type) \
MCFG_DEVICE_CONFIG_DATAPTR(software_list_config, list_name, _list) \
MCFG_DEVICE_CONFIG_DATA32(software_list_config, list_type, _list_type)
#define MCFG_SOFTWARE_LIST_ADD( _tag, _list ) \
MCFG_DEVICE_ADD( _tag, SOFTWARE_LIST, 0 ) \
MCFG_SOFTWARE_LIST_CONFIG(0,_list, SOFTWARE_LIST_ORIGINAL_SYSTEM)
MCFG_SOFTWARE_LIST_CONFIG(_list, SOFTWARE_LIST_ORIGINAL_SYSTEM)
#define MCFG_SOFTWARE_LIST_COMPATIBLE_ADD( _tag, _list ) \
MCFG_DEVICE_ADD( _tag, SOFTWARE_LIST, 0 ) \
MCFG_SOFTWARE_LIST_CONFIG(0,_list, SOFTWARE_LIST_COMPATIBLE_SYSTEM)
MCFG_SOFTWARE_LIST_CONFIG(_list, SOFTWARE_LIST_COMPATIBLE_SYSTEM)
#define MCFG_SOFTWARE_LIST_MODIFY( _tag, _list ) \
MCFG_DEVICE_MODIFY( _tag ) \
MCFG_SOFTWARE_LIST_CONFIG(0,_list, SOFTWARE_LIST_ORIGINAL_SYSTEM)
MCFG_SOFTWARE_LIST_CONFIG(_list, SOFTWARE_LIST_ORIGINAL_SYSTEM)
#define MCFG_SOFTWARE_LIST_COMPATIBLE_MODIFY( _tag, _list ) \
MCFG_DEVICE_MODIFY( _tag ) \
MCFG_SOFTWARE_LIST_CONFIG(0,_list, SOFTWARE_LIST_COMPATIBLE_SYSTEM)
MCFG_SOFTWARE_LIST_CONFIG(_list, SOFTWARE_LIST_COMPATIBLE_SYSTEM)
#define MCFG_SOFTWARE_LIST_FILTER( _tag, _filter ) \
MCFG_DEVICE_MODIFY( _tag ) \
MCFG_DEVICE_CONFIG_DATAPTR(software_list_config, filter, _filter)
#endif

View File

@ -126,9 +126,23 @@ int ui_menu_software_list::compare_entries(const ui_menu_software_entry_info *e1
return result;
}
bool ui_menu_software_list::if_compatible(const char *compatibility, const char *filter)
{
if ((compatibility==NULL) || (filter==NULL)) return TRUE;
astring comp = astring(compatibility,",");
char *filt = core_strdup(filter);
char *token = strtok(filt,",");
while (token!= NULL)
{
if (comp.find(0,astring(token,","))!=-1) return TRUE;
token = strtok (NULL, ",");
}
return FALSE;
}
/* populate a specific list */
ui_menu_software_entry_info *ui_menu_software_list::append_software_entry(software_info *swinfo, char *list_name, device_image_interface* image)
ui_menu_software_entry_info *ui_menu_software_list::append_software_entry(software_info *swinfo, device_image_interface* image)
{
ui_menu_software_entry_info *entry = NULL;
ui_menu_software_entry_info **entryptr;
@ -138,7 +152,8 @@ ui_menu_software_entry_info *ui_menu_software_list::append_software_entry(softwa
// check if at least one of the parts has the correct interface and add a menu entry only in this case
for (software_part *swpart = software_find_part(swinfo, NULL, NULL); swpart != NULL; swpart = software_part_next(swpart))
{
if (strcmp(interface, swpart->interface_) == 0)
const char *compatibility = software_part_get_feature(swpart, "compatibility");
if ((strcmp(interface, swpart->interface_) == 0) && if_compatible(compatibility, swlist->filter))
{
entry_updated = TRUE;
// allocate a new entry
@ -147,7 +162,7 @@ ui_menu_software_entry_info *ui_menu_software_list::append_software_entry(softwa
entry->short_name = pool_strdup(swinfo->shortname);
entry->long_name = pool_strdup(swinfo->longname);
entry->list_name = list_name;
entry->list_name = swlist->list_name;
entry->image = image;
entry->interface = pool_strdup(swpart->interface_);
break;
@ -170,9 +185,9 @@ ui_menu_software_entry_info *ui_menu_software_list::append_software_entry(softwa
return entry;
}
ui_menu_software_list::ui_menu_software_list(running_machine &machine, render_container *container, char *_list_name, device_image_interface *_image) : ui_menu(machine, container)
ui_menu_software_list::ui_menu_software_list(running_machine &machine, render_container *container, software_list_config *_swlist, device_image_interface *_image) : ui_menu(machine, container)
{
list_name = _list_name;
swlist = _swlist;
image = _image;
entrylist = NULL;
ordered_by_shortname = true;
@ -184,13 +199,13 @@ ui_menu_software_list::~ui_menu_software_list()
void ui_menu_software_list::populate()
{
software_list *list = software_list_open(machine().options(), list_name, false, NULL);
software_list *list = software_list_open(machine().options(), swlist->list_name, false, NULL);
// build up the list of entries for the menu
if (list)
{
for (software_info *swinfo = software_list_find(list, "*", NULL); swinfo != NULL; swinfo = software_list_find(list, "*", swinfo))
append_software_entry(swinfo, list_name, image);
append_software_entry(swinfo, image);
software_list_close(list);
}
@ -241,7 +256,7 @@ void ui_menu_software_list::handle()
else if (event->iptkey == IPT_UI_SELECT)
{
ui_menu_software_entry_info *entry = (ui_menu_software_entry_info *) event->itemref;
software_list *tmp_list = software_list_open(machine().options(), list_name, false, NULL);
software_list *tmp_list = software_list_open(machine().options(), swlist->list_name, false, NULL);
software_info *tmp_info = software_list_find(tmp_list, entry->short_name, NULL);
// if the selected software has multiple parts that can be loaded, open the submenu
@ -369,28 +384,25 @@ void ui_menu_software::populate()
{
software_list_config *swlist = (software_list_config *)downcast<const legacy_device_base *>(dev)->inline_config();
for (int i = 0; i < DEVINFO_STR_SWLIST_MAX - DEVINFO_STR_SWLIST_0; i++)
if (swlist->list_type == SOFTWARE_LIST_ORIGINAL_SYSTEM)
{
if (swlist->list_name[i] && (swlist->list_type == SOFTWARE_LIST_ORIGINAL_SYSTEM))
software_list *list = software_list_open(machine().options(), swlist->list_name, false, NULL);
if (list)
{
software_list *list = software_list_open(machine().options(), swlist->list_name[i], false, NULL);
if (list)
bool found = false;
for (software_info *swinfo = software_list_find(list, "*", NULL); swinfo != NULL; swinfo = software_list_find(list, "*", swinfo))
{
bool found = false;
for (software_info *swinfo = software_list_find(list, "*", NULL); swinfo != NULL; swinfo = software_list_find(list, "*", swinfo))
{
software_part *part = software_find_part(swinfo, NULL, NULL);
if (strcmp(interface,part->interface_)==0) {
found = true;
}
software_part *part = software_find_part(swinfo, NULL, NULL);
if (strcmp(interface,part->interface_)==0) {
found = true;
}
if (found) {
item_append(list->description, NULL, 0, swlist->list_name[i]);
}
software_list_close(list);
}
if (found) {
item_append(list->description, NULL, 0, swlist);
}
software_list_close(list);
}
}
}
@ -400,32 +412,29 @@ void ui_menu_software::populate()
{
software_list_config *swlist = (software_list_config *)downcast<const legacy_device_base *>(dev)->inline_config();
for (int i = 0; i < DEVINFO_STR_SWLIST_MAX - DEVINFO_STR_SWLIST_0; i++)
if (swlist->list_type == SOFTWARE_LIST_COMPATIBLE_SYSTEM)
{
if (swlist->list_name[i] && (swlist->list_type == SOFTWARE_LIST_COMPATIBLE_SYSTEM))
software_list *list = software_list_open(machine().options(), swlist->list_name, false, NULL);
if (list)
{
software_list *list = software_list_open(machine().options(), swlist->list_name[i], false, NULL);
if (list)
bool found = false;
for (software_info *swinfo = software_list_find(list, "*", NULL); swinfo != NULL; swinfo = software_list_find(list, "*", swinfo))
{
bool found = false;
for (software_info *swinfo = software_list_find(list, "*", NULL); swinfo != NULL; swinfo = software_list_find(list, "*", swinfo))
{
software_part *part = software_find_part(swinfo, NULL, NULL);
if (strcmp(interface,part->interface_)==0) {
found = true;
}
software_part *part = software_find_part(swinfo, NULL, NULL);
if (strcmp(interface,part->interface_)==0) {
found = true;
}
if (found) {
if (!haveCompatible) {
item_append("[compatible lists]", NULL, MENU_FLAG_DISABLE, NULL);
}
item_append(list->description, NULL, 0, swlist->list_name[i]);
}
haveCompatible = true;
software_list_close(list);
}
if (found) {
if (!haveCompatible) {
item_append("[compatible lists]", NULL, MENU_FLAG_DISABLE, NULL);
}
item_append(list->description, NULL, 0, swlist);
}
haveCompatible = true;
software_list_close(list);
}
}
}
@ -442,5 +451,5 @@ void ui_menu_software::handle()
const ui_menu_event *event = process(0);
if (event != NULL && event->iptkey == IPT_UI_SELECT)
ui_menu::stack_push(auto_alloc_clear(machine(), ui_menu_software_list(machine(), container, (char *)event->itemref, image)));
ui_menu::stack_push(auto_alloc_clear(machine(), ui_menu_software_list(machine(), container, (software_list_config *)event->itemref, image)));
}

View File

@ -40,21 +40,22 @@ private:
class ui_menu_software_list : public ui_menu {
public:
ui_menu_software_list(running_machine &machine, render_container *container, char *list_name, device_image_interface *image);
ui_menu_software_list(running_machine &machine, render_container *container, software_list_config *swlist, device_image_interface *image);
virtual ~ui_menu_software_list();
virtual void populate();
virtual void handle();
private:
char *list_name; /* currently selected list */
software_list_config *swlist; /* currently selected list */
device_image_interface *image;
ui_menu_software_entry_info *entrylist;
char filename_buffer[1024];
bool ordered_by_shortname;
int compare_entries(const ui_menu_software_entry_info *e1, const ui_menu_software_entry_info *e2, bool shortname);
ui_menu_software_entry_info *append_software_entry(software_info *swinfo, char *list_name, device_image_interface* image);
ui_menu_software_entry_info *append_software_entry(software_info *swinfo, device_image_interface* image);
bool swinfo_has_multiple_parts(software_info *swinfo, const char *interface);
bool if_compatible(const char *compatibility, const char *filter);
};
class ui_menu_software : public ui_menu {