/*************************************************************************** softlist.c Software list construction helpers. ***************************************************************************/ #include "emu.h" #include "pool.h" #include "expat.h" #include "emuopts.h" #include "softlist.h" #include enum softlist_parse_position { POS_ROOT, POS_MAIN, POS_SOFT, POS_PART, POS_DATA }; typedef struct _parse_state { XML_Parser parser; int done; void (*error_proc)(const char *message); void *param; enum softlist_parse_position pos; char **text_dest; } parse_state; struct software_info_list { software_info info; struct software_info_list *next; }; struct _software_list { mame_file *file; object_pool *pool; parse_state state; const char *description; struct software_info_list *software_info_list; struct software_info_list *current_software_info; software_info *softinfo; const char *look_for; int part_entries; int current_part_entry; int rom_entries; int current_rom_entry; void (*error_proc)(const char *message); }; typedef tagmap_t softlist_map; /*************************************************************************** EXPAT INTERFACES ***************************************************************************/ /*------------------------------------------------- expat_malloc/expat_realloc/expat_free - wrappers for memory allocation functions so that they pass through out memory tracking systems -------------------------------------------------*/ static void *expat_malloc(size_t size) { return global_alloc_array_clear(UINT8,size); } static void *expat_realloc(void *ptr, size_t size) { if (ptr) global_free(ptr); return global_alloc_array_clear(UINT8,size); } static void expat_free(void *ptr) { global_free(ptr); } /*------------------------------------------------- parse_error -------------------------------------------------*/ INLINE void ATTR_PRINTF(2,3) parse_error(parse_state *state, const char *fmt, ...) { char buf[256]; va_list va; if (state->error_proc) { va_start(va, fmt); vsnprintf(buf, ARRAY_LENGTH(buf), fmt, va); va_end(va); (*state->error_proc)(buf); } } /*------------------------------------------------- unknown_tag -------------------------------------------------*/ INLINE void unknown_tag(parse_state *state, const char *tagname) { parse_error(state, "[%lu:%lu]: Unknown tag: %s\n", XML_GetCurrentLineNumber(state->parser), XML_GetCurrentColumnNumber(state->parser), tagname); } /*------------------------------------------------- unknown_attribute -------------------------------------------------*/ INLINE void unknown_attribute(parse_state *state, const char *attrname) { parse_error(state, "[%lu:%lu]: Unknown attribute: %s\n", XML_GetCurrentLineNumber(state->parser), XML_GetCurrentColumnNumber(state->parser), attrname); } /*------------------------------------------------- unknown_attribute_value -------------------------------------------------*/ INLINE void unknown_attribute_value(parse_state *state, const char *attrname, const char *attrvalue) { parse_error(state, "[%lu:%lu]: Unknown attribute value: %s\n", XML_GetCurrentLineNumber(state->parser), XML_GetCurrentColumnNumber(state->parser), attrvalue); } /*------------------------------------------------- software_name_split helper; splits a software_list:software:part string into seperate software_list, software, and part strings. str1:str2:str3 => swlist_name - str1, swname - str2, swpart - str3 str1:str2 => swlist_name - NULL, swname - str1, swpart - str2 str1 => swlist_name - NULL, swname - str1, swpart - NULL swlist_namem, swnane and swpart will be global_alloc'ed from the global pool. So they should be global_free'ed when they are not used anymore. -------------------------------------------------*/ static void software_name_split(running_machine* machine, const char *swlist_swname, char **swlist_name, char **swname, char **swpart ) { const char *split_1st_loc = strchr( swlist_swname, ':' ); const char *split_2nd_loc = ( split_1st_loc ) ? strchr( split_1st_loc + 1, ':' ) : NULL; *swlist_name = NULL; *swname = NULL; *swpart = NULL; if ( split_1st_loc ) { if ( split_2nd_loc ) { int size = split_1st_loc - swlist_swname; *swlist_name = auto_alloc_array_clear(machine,char,size+1); memcpy( *swlist_name, swlist_swname, size ); size = split_2nd_loc - ( split_1st_loc + 1 ); *swname = auto_alloc_array_clear(machine,char,size+1); memcpy( *swname, split_1st_loc + 1, size ); size = strlen( swlist_swname ) - ( split_2nd_loc + 1 - swlist_swname ); *swpart = auto_alloc_array_clear(machine,char,size+1); memcpy( *swpart, split_2nd_loc + 1, size ); } else { int size = split_1st_loc - swlist_swname; *swname = auto_alloc_array_clear(machine,char,size+1); memcpy( *swname, swlist_swname, size ); size = strlen( swlist_swname ) - ( split_1st_loc + 1 - swlist_swname ); *swpart = auto_alloc_array_clear(machine,char,size+1); memcpy( *swpart, split_1st_loc + 1, size ); } } else { *swname = auto_strdup(machine,swlist_swname); } } /*------------------------------------------------- add_rom_entry -------------------------------------------------*/ static void add_rom_entry(software_list *swlist, const char *name, const char *hashdata, UINT32 offset, UINT32 length, UINT32 flags) { software_part *part = &swlist->softinfo->partdata[swlist->current_part_entry-1]; struct rom_entry *entry = &part->romdata[swlist->current_rom_entry]; entry->_name = name; entry->_hashdata = hashdata; entry->_offset = offset; entry->_length = length; entry->_flags = flags; swlist->current_rom_entry += 1; if ( swlist->current_rom_entry >= swlist->rom_entries ) { struct rom_entry *new_entries; swlist->rom_entries += 10; new_entries = (struct rom_entry *)pool_realloc_lib(swlist->pool, part->romdata, swlist->rom_entries * sizeof(struct rom_entry) ); if ( new_entries ) { part->romdata = new_entries; } else { /* Allocation error */ swlist->current_rom_entry -= 1; } } } /*------------------------------------------------- add_feature -------------------------------------------------*/ static void add_feature(software_list *swlist, char *feature_name, char *feature_value) { software_part *part = &swlist->softinfo->partdata[swlist->current_part_entry-1]; feature_list *new_entry; /* First allocate the new entry */ new_entry = (feature_list *)pool_malloc_lib(swlist->pool, sizeof(feature_list) ); if ( new_entry ) { new_entry->next = NULL; new_entry->name = feature_name; new_entry->value = feature_value ? feature_value : feature_name; /* Add new feature to end of feature list */ if ( part->featurelist ) { feature_list *list = part->featurelist; while ( list->next != NULL ) { list = list->next; } list->next = new_entry; } else { part->featurelist = new_entry; } } else { /* Unable to allocate memory */ } } /*------------------------------------------------- add_software_part -------------------------------------------------*/ static void add_software_part(software_list *swlist, const char *name, const char *interface) { software_part *part = &swlist->softinfo->partdata[swlist->current_part_entry]; part->name = name; part->interface_ = interface; part->featurelist = NULL; part->romdata = NULL; swlist->current_part_entry += 1; if ( swlist->current_part_entry >= swlist->part_entries ) { software_part *new_parts; swlist->part_entries += 2; new_parts = (software_part *)pool_realloc_lib(swlist->pool, swlist->softinfo->partdata, swlist->part_entries * sizeof(software_part) ); if ( new_parts ) { swlist->softinfo->partdata = new_parts; } else { /* Allocation error */ swlist->current_part_entry -= 1; } } } /*------------------------------------------------- start_handler -------------------------------------------------*/ static void start_handler(void *data, const char *tagname, const char **attributes) { software_list *swlist = (software_list *) data; char **text_dest; switch(swlist->state.pos) { case POS_ROOT: if (!strcmp(tagname, "softwarelist")) { for( ; attributes[0]; attributes += 2 ) { if ( ! strcmp(attributes[0], "name" ) ) { } if ( ! strcmp(attributes[0], "description" ) ) { swlist->description = (const char *)pool_malloc_lib(swlist->pool, (strlen(attributes[1]) + 1) * sizeof(char)); if (!swlist->description) return; strcpy((char *)swlist->description, attributes[1]); } } } else { unknown_tag(&swlist->state, tagname); } break; case POS_MAIN: if ( !strcmp( tagname, "software" ) ) { const char *name = NULL; const char *parent = NULL; const char *supported = NULL; for ( ; attributes[0]; attributes += 2 ) { if ( !strcmp( attributes[0], "name" ) ) { name = attributes[1]; } if ( !strcmp( attributes[0], "cloneof" ) ) { parent = attributes[1]; } if ( !strcmp( attributes[0], "supported" ) ) { supported = attributes[1]; } } if ( name && !mame_strwildcmp( swlist->look_for, name ) ) { struct software_info_list *elem = (struct software_info_list *)pool_malloc_lib(swlist->pool,sizeof(struct software_info_list)); if ( !elem ) return; /* Clear element and add element to list */ memset(elem,0,sizeof(struct software_info_list)); /* Allocate space to hold the shortname and copy the short name */ elem->info.shortname = (const char*)pool_malloc_lib(swlist->pool, ( strlen( name ) + 1 ) * sizeof(char) ); if ( ! elem->info.shortname ) return; strcpy( (char *)elem->info.shortname, name ); /* Allocate space to hold the parentname and copy the parent name */ if (parent) { elem->info.parentname = (const char*)pool_malloc_lib(swlist->pool, ( strlen(parent) + 1 ) * sizeof(char) ); strcpy((char *)elem->info.parentname, parent); } /* Allocate initial space to hold part information */ swlist->part_entries = 2; swlist->current_part_entry = 0; elem->info.partdata = (software_part *)pool_malloc_lib(swlist->pool, swlist->part_entries * sizeof(software_part) ); if ( !elem->info.partdata ) return; /* Handle the supported flag */ elem->info.supported = SOFTWARE_SUPPORTED_YES; if ( supported && ! strcmp( supported, "partial" ) ) elem->info.supported = SOFTWARE_SUPPORTED_PARTIAL; if ( supported && ! strcmp( supported, "no" ) ) elem->info.supported = SOFTWARE_SUPPORTED_NO; /* Add the entry to the end of the list */ if ( swlist->software_info_list == NULL ) { swlist->software_info_list = elem; swlist->current_software_info = elem; } else { swlist->current_software_info->next = elem; swlist->current_software_info = elem; } /* Quick lookup for setting software information */ swlist->softinfo = &swlist->current_software_info->info; } else { swlist->softinfo = NULL; } } else { unknown_tag(&swlist->state, tagname); } break; case POS_SOFT: text_dest = NULL; if (!strcmp(tagname, "description")) text_dest = (char **) &swlist->softinfo->longname; else if (!strcmp(tagname, "year")) text_dest = (char **) &swlist->softinfo->year; else if (!strcmp(tagname, "publisher")) text_dest = (char **) &swlist->softinfo->publisher; else if ( !strcmp(tagname, "part" ) ) { const char *str_name = NULL; const char *str_interface = NULL; for ( ; attributes[0]; attributes += 2 ) { if ( !strcmp( attributes[0], "name" ) ) str_name = attributes[1]; if ( !strcmp( attributes[0], "interface" ) ) str_interface = attributes[1]; } if ( str_name && str_interface ) { if ( swlist->softinfo ) { char *name = (char *)pool_malloc_lib(swlist->pool, ( strlen( str_name ) + 1 ) * sizeof(char) ); char *interface = (char *)pool_malloc_lib(swlist->pool, ( strlen( str_interface ) + 1 ) * sizeof(char) ); if ( !name || !interface ) return; strcpy( name, str_name ); strcpy( interface, str_interface ); add_software_part( swlist, name, interface ); /* Allocate initial space to hold the rom information */ swlist->rom_entries = 3; swlist->current_rom_entry = 0; swlist->softinfo->partdata[swlist->current_part_entry-1].romdata = (struct rom_entry *)pool_malloc_lib(swlist->pool, swlist->rom_entries * sizeof(struct rom_entry)); if ( ! swlist->softinfo->partdata[swlist->current_part_entry-1].romdata ) return; } } else { /* Incomplete/incorrect part definition */ } } else unknown_tag(&swlist->state, tagname); if (text_dest && swlist->softinfo) swlist->state.text_dest = text_dest; break; case POS_PART: if (!strcmp(tagname, "dataarea")) { const char *str_name = NULL; const char *str_size = NULL; for ( ; attributes[0]; attributes += 2 ) { if ( !strcmp( attributes[0], "name" ) ) str_name = attributes[1]; if ( !strcmp( attributes[0], "size") ) str_size = attributes[1]; } if ( str_name && str_size ) { if ( swlist->softinfo ) { UINT32 length = strtol( str_size, NULL, 10 ); char *s = (char *)pool_malloc_lib(swlist->pool, ( strlen( str_name ) + 1 ) * sizeof(char) ); if ( !s ) return; strcpy( s, str_name ); /* ROM_REGION( length, "name", flags ) */ add_rom_entry( swlist, s, NULL, 0, length, ROMENTRYTYPE_REGION ); } } else { /* Missing dataarea name or size */ } } else if ( !strcmp(tagname, "feature") ) { const char *str_feature_name = NULL; const char *str_feature_value = NULL; for ( ; attributes[0]; attributes += 2 ) { if ( !strcmp( attributes[0], "name" ) ) str_feature_name = attributes[1]; if ( !strcmp( attributes[0], "value" ) ) str_feature_value = attributes[1]; } /* Prepare for adding feature to feature list */ if ( str_feature_name && swlist->softinfo ) { char *name = (char *)pool_malloc_lib(swlist->pool, ( strlen( str_feature_name ) + 1 ) * sizeof(char) ); char *value = NULL; if ( !name ) return; strcpy( name, str_feature_name ); if ( str_feature_value ) { value = (char *)pool_malloc_lib(swlist->pool, ( strlen( str_feature_value ) + 1 ) * sizeof(char) ); if ( !value ) return; strcpy( value, str_feature_value ); } add_feature( swlist, name, value ); } } else unknown_tag( &swlist->state, tagname ); break; case POS_DATA: if (!strcmp(tagname, "rom")) { const char *str_name = NULL; const char *str_size = NULL; const char *str_crc = NULL; const char *str_sha1 = NULL; const char *str_offset = NULL; const char *str_value = NULL; const char *str_status = NULL; const char *str_loadflag = NULL; for ( ; attributes[0]; attributes += 2 ) { if ( !strcmp( attributes[0], "name" ) ) str_name = attributes[1]; if ( !strcmp( attributes[0], "size" ) ) str_size = attributes[1]; if ( !strcmp( attributes[0], "crc" ) ) str_crc = attributes[1]; if ( !strcmp( attributes[0], "sha1" ) ) str_sha1 = attributes[1]; if ( !strcmp( attributes[0], "offset" ) ) str_offset = attributes[1]; if ( !strcmp( attributes[0], "value" ) ) str_value = attributes[1]; if ( !strcmp( attributes[0], "status" ) ) str_status = attributes[1]; if ( !strcmp( attributes[0], "loadflag" ) ) str_loadflag = attributes[1]; } if ( swlist->softinfo ) { if ( str_size && str_offset ) { UINT32 length = strtol( str_size, NULL, 10 ); UINT32 offset = strtol( str_offset, NULL, 16 ); if ( str_loadflag && !strcmp(str_loadflag, "reload") ) { /* Handle 'reload' loadflag */ add_rom_entry( swlist, NULL, NULL, offset, length, ROMENTRYTYPE_RELOAD | ROM_INHERITFLAGS ); } else if ( str_loadflag && !strcmp(str_loadflag, "continue") ) { /* Handle 'continue' loadflag */ add_rom_entry( swlist, NULL, NULL, offset, length, ROMENTRYTYPE_CONTINUE | ROM_INHERITFLAGS ); } else if ( str_loadflag && !strcmp(str_loadflag, "fill") ) { /* Handle 'fill' loadflag */ add_rom_entry( swlist, NULL, (const char*)atoi(str_value), offset, length, ROMENTRYTYPE_FILL ); } else { if ( str_name && str_crc && str_sha1 ) { char *s_name = (char *)pool_malloc_lib(swlist->pool, ( strlen( str_name ) + 1 ) * sizeof(char) ); char *hashdata = (char *)pool_malloc_lib( swlist->pool, sizeof(char) * ( strlen(str_crc) + strlen(str_sha1) + 7 + 4 ) ); int baddump = ( str_status && !strcmp(str_status, "baddump") ) ? 1 : 0; int nodump = ( str_status && !strcmp(str_status, "nodump" ) ) ? 1 : 0; int romflags = 0; if ( !s_name || !hashdata ) return; strcpy( s_name, str_name ); sprintf( hashdata, "c:%s#s:%s#%s", str_crc, str_sha1, ( nodump ? NO_DUMP : ( baddump ? BAD_DUMP : "" ) ) ); /* Handle loadflag attribute */ if ( str_loadflag && !strcmp(str_loadflag, "load16_word_swap") ) romflags = ROM_GROUPWORD | ROM_REVERSE; else if ( str_loadflag && !strcmp(str_loadflag, "load16_byte") ) romflags = ROM_SKIP(1); else if ( str_loadflag && !strcmp(str_loadflag, "load32_word_swap") ) romflags = ROM_GROUPWORD | ROM_REVERSE | ROM_SKIP(2); else if ( str_loadflag && !strcmp(str_loadflag, "load32_word") ) romflags = ROM_GROUPWORD | ROM_SKIP(2); /* ROM_LOAD( name, offset, length, hash ) */ add_rom_entry( swlist, s_name, hashdata, offset, length, ROMENTRYTYPE_ROM | romflags ); } } } } else { /* Missing name, size, crc, sha1, or offset */ } } else unknown_tag(&swlist->state, tagname); break; } swlist->state.pos = (softlist_parse_position) (swlist->state.pos + 1); } /*------------------------------------------------- end_handler -------------------------------------------------*/ static void end_handler(void *data, const char *name) { software_list *swlist = (software_list *) data; swlist->state.text_dest = NULL; swlist->state.pos = (softlist_parse_position) (swlist->state.pos - 1); switch(swlist->state.pos) { case POS_ROOT: break; case POS_SOFT: if ( ! strcmp( name, "part" ) && swlist->softinfo ) { /* ROM_END */ add_rom_entry( swlist, NULL, NULL, 0, 0, ROMENTRYTYPE_END ); } break; case POS_MAIN: if ( swlist->softinfo ) { add_software_part( swlist, NULL, NULL ); } break; case POS_PART: break; case POS_DATA: break; } } /*------------------------------------------------- data_handler -------------------------------------------------*/ static void data_handler(void *data, const XML_Char *s, int len) { software_list *swlist = (software_list *) data; int text_len; char *text; if (swlist->state.text_dest) { text = *swlist->state.text_dest; text_len = text ? strlen(text) : 0; text = (char*)pool_realloc_lib(swlist->pool, text, text_len + len + 1); if (!text) return; memcpy(&text[text_len], s, len); text[text_len + len] = '\0'; *swlist->state.text_dest = text; } } /*------------------------------------------------- software_list_parse -------------------------------------------------*/ static void software_list_parse(software_list *swlist, void (*error_proc)(const char *message), void *param) { char buf[1024]; UINT32 len; XML_Memory_Handling_Suite memcallbacks; mame_fseek(swlist->file, 0, SEEK_SET); memset(&swlist->state, 0, sizeof(swlist->state)); swlist->state.error_proc = error_proc; swlist->state.param = param; /* create the XML parser */ memcallbacks.malloc_fcn = expat_malloc; memcallbacks.realloc_fcn = expat_realloc; memcallbacks.free_fcn = expat_free; swlist->state.parser = XML_ParserCreate_MM(NULL, &memcallbacks, NULL); if (!swlist->state.parser) goto done; XML_SetUserData(swlist->state.parser, swlist); XML_SetElementHandler(swlist->state.parser, start_handler, end_handler); XML_SetCharacterDataHandler(swlist->state.parser, data_handler); while(!swlist->state.done) { len = mame_fread(swlist->file, buf, sizeof(buf)); swlist->state.done = mame_feof(swlist->file); if (XML_Parse(swlist->state.parser, buf, len, swlist->state.done) == XML_STATUS_ERROR) { parse_error(&swlist->state, "[%lu:%lu]: %s\n", XML_GetCurrentLineNumber(swlist->state.parser), XML_GetCurrentColumnNumber(swlist->state.parser), XML_ErrorString(XML_GetErrorCode(swlist->state.parser))); goto done; } } done: if (swlist->state.parser) XML_ParserFree(swlist->state.parser); swlist->state.parser = NULL; swlist->current_software_info = swlist->software_info_list; } /*------------------------------------------------- software_list_open -------------------------------------------------*/ software_list *software_list_open(core_options *options, const char *listname, int is_preload, void (*error_proc)(const char *message)) { file_error filerr; astring *fname; software_list *swlist = NULL; object_pool *pool = NULL; /* create a pool for this software list file */ pool = pool_alloc_lib(error_proc); if (!pool) goto error; /* allocate space for this software list file */ swlist = (software_list *) pool_malloc_lib(pool, sizeof(*swlist)); if (!swlist) goto error; /* set up the software_list structure */ memset(swlist, 0, sizeof(*swlist)); swlist->pool = pool; swlist->error_proc = error_proc; /* open a file */ fname = astring_assemble_2(astring_alloc(), listname, ".xml"); filerr = mame_fopen_options(options, SEARCHPATH_HASH, astring_c(fname), OPEN_FLAG_READ, &swlist->file); astring_free(fname); if (filerr != FILERR_NONE) goto error; // if (is_preload) // software_list_parse(swlist, swlist->error_proc, NULL); return swlist; error: if (swlist != NULL) software_list_close(swlist); return NULL; } /*------------------------------------------------- software_list_close -------------------------------------------------*/ void software_list_close(software_list *swlist) { if (swlist == NULL) return; if (swlist->file) mame_fclose(swlist->file); pool_free_lib(swlist->pool); } /*------------------------------------------------- software_list_find -------------------------------------------------*/ software_info *software_list_find(software_list *swlist, const char *software) { if (swlist == NULL) return NULL; swlist->look_for = software; software_list_parse( swlist, swlist->error_proc, NULL ); return swlist->current_software_info ? &swlist->current_software_info->info : NULL; } /*------------------------------------------------- software_list_first -------------------------------------------------*/ software_info *software_list_first(software_list *swlist) { return software_list_find(swlist, "*"); } /*------------------------------------------------- software_list_next -------------------------------------------------*/ software_info *software_list_next(software_list *swlist) { if ( swlist->current_software_info ) { swlist->current_software_info = swlist->current_software_info->next; } return swlist->current_software_info ? &swlist->current_software_info->info : NULL; } /*------------------------------------------------- software_find_part -------------------------------------------------*/ software_part *software_find_part(software_info *sw, const char *partname, const char *interface) { software_part *part = sw ? sw->partdata : NULL; /* If neither partname nor interface supplied, then we just return the first entry */ if ( partname || interface ) { while( part && part->name ) { if ( partname ) { if ( !strcmp(partname, part->name ) ) { if ( interface ) { if ( !strcmp(interface, part->interface_) ) { break; } } else { break; } } } else { /* No specific partname supplied, find the first match based on interface */ if ( interface ) { if ( !strcmp(interface, part->interface_) ) { break; } } } part++; } } if ( ! part->name ) part = NULL; return part; } /*------------------------------------------------- software_part_next -------------------------------------------------*/ software_part *software_part_next(software_part *part) { if ( part && part->name ) { part++; } if ( ! part->name ) part = NULL; return part; } /*------------------------------------------------- load_software_part Load a software part for a device. The part to load is determined by the "path", software lists configured for a driver, and the interface supported by the device. returns true if the software could be loaded, false otherwise. If the software could be loaded sw_info and sw_part are also set. -------------------------------------------------*/ bool load_software_part(device_image_interface *image, const char *path, software_info **sw_info, software_part **sw_part, char **full_sw_name) { char *swlist_name, *swname, *swpart; bool result = false; software_list *software_list_ptr = NULL; software_info *software_info_ptr = NULL; software_part *software_part_ptr = NULL; *sw_info = NULL; *sw_part = NULL; /* Split full software name into software list name and short software name */ software_name_split( image->device().machine, path, &swlist_name, &swname, &swpart ); const char *interface = image->image_config().image_interface(); if ( swlist_name ) { /* Try to open the software list xml file explicitly named by the user */ software_list_ptr = software_list_open( mame_options(), swlist_name, FALSE, NULL ); if ( software_list_ptr ) { software_info_ptr = software_list_find( software_list_ptr, swname ); if ( software_info_ptr ) { software_part_ptr = software_find_part( software_info_ptr, swpart, interface ); } } } else { /* Loop through all the software lists named in the driver */ running_device *swlists = devtag_get_device( image->device().machine, __SOFTWARE_LIST_TAG ); if ( swlists ) { software_list_config *swlist = (software_list_config *)downcast(&swlists->baseconfig())->inline_config(); UINT32 i = DEVINFO_STR_SWLIST_0; while ( ! software_part_ptr && i <= DEVINFO_STR_SWLIST_MAX ) { swlist_name = swlist->list_name[i-DEVINFO_STR_SWLIST_0]; if ( swlist_name && *swlist_name ) { if ( software_list_ptr ) { software_list_close( software_list_ptr ); } software_list_ptr = software_list_open( image->device().machine->options(), swlist_name, FALSE, NULL ); if ( software_list_ptr ) { software_info_ptr = software_list_find( software_list_ptr, swname ); if ( software_info_ptr ) { software_part_ptr = software_find_part( software_info_ptr, swpart, interface ); } } } i++; } } /* If not found try to load the software list using the driver name */ if ( ! software_part_ptr ) { swlist_name = (char *)image->device().machine->gamedrv->name; if ( software_list_ptr ) { software_list_close( software_list_ptr ); } software_list_ptr = software_list_open( image->device().machine->options(), swlist_name, FALSE, NULL ); if ( software_list_ptr ) { software_info_ptr = software_list_find( software_list_ptr, swname ); if ( software_info_ptr ) { software_part_ptr = software_find_part( software_info_ptr, swpart, interface ); } } } /* If not found try to load the software list using the software name as software */ /* list name and software part name as software name. */ if ( ! software_part_ptr ) { swlist_name = swname; swname = swpart; swpart = NULL; if ( software_list_ptr ) { software_list_close( software_list_ptr ); } software_list_ptr = software_list_open( image->device().machine->options(), swlist_name, FALSE, NULL ); if ( software_list_ptr ) { software_info_ptr = software_list_find( software_list_ptr, swname ); if ( software_info_ptr ) { software_part_ptr = software_find_part( software_info_ptr, swpart, interface ); } if ( ! software_part_ptr ) { software_list_close( software_list_ptr ); software_list_ptr = NULL; } } } } if ( software_part_ptr ) { /* Load the software part */ try { load_software_part_region( &image->device(), (char *)swlist_name, (char *)software_info_ptr->shortname, software_part_ptr->romdata ); } catch (emu_fatalerror &fatal) { software_list_close( software_list_ptr ); throw fatal; } /* Create a copy of the software and part information */ *sw_info = auto_alloc_clear( image->device().machine, software_info ); (*sw_info)->shortname = auto_strdup( image->device().machine, software_info_ptr->shortname ); (*sw_info)->longname = auto_strdup( image->device().machine, software_info_ptr->longname ); if ( software_info_ptr->year ) (*sw_info)->year = auto_strdup( image->device().machine, software_info_ptr->year ); if ( software_info_ptr->publisher ) (*sw_info)->publisher = auto_strdup( image->device().machine, software_info_ptr->publisher ); *sw_part = auto_alloc_clear( image->device().machine, software_part ); (*sw_part)->name = auto_strdup( image->device().machine, software_part_ptr->name ); if ( software_part_ptr->interface_ ) (*sw_part)->interface_ = auto_strdup( image->device().machine, software_part_ptr->interface_ ); if ( software_part_ptr->featurelist ) { feature_list *list = software_part_ptr->featurelist; feature_list *new_list = auto_alloc_clear( image->device().machine, feature_list ); (*sw_part)->featurelist = new_list; new_list->name = auto_strdup( image->device().machine, list->name ); new_list->value = auto_strdup( image->device().machine, list->value ); list = list->next; while( list ) { new_list->next = auto_alloc_clear( image->device().machine, feature_list ); new_list = new_list->next; new_list->name = auto_strdup( image->device().machine, list->name ); new_list->value = auto_strdup( image->device().machine, list->value ); list = list->next; } new_list->next = NULL; } /* Tell the world which part we actually loaded */ *full_sw_name = auto_alloc_array( image->device().machine, char, strlen(swlist_name) + strlen(software_info_ptr->shortname) + strlen(software_part_ptr->name) + 3 ); sprintf( *full_sw_name, "%s:%s:%s", swlist_name, software_info_ptr->shortname, software_part_ptr->name ); result = true; } /* Close the software list if it's still open */ if ( software_list_ptr ) { software_list_close( software_list_ptr ); software_info_ptr = NULL; software_list_ptr = NULL; } auto_free( image->device().machine, swlist_name ); auto_free( image->device().machine, swname ); auto_free( image->device().machine, swpart ); return result; } /*************************************************************************** DEVICE INTERFACE ***************************************************************************/ static DEVICE_START( software_list ) { } static DEVICE_VALIDITY_CHECK( software_list ) { software_list_config *swlist = (software_list_config *)downcast(device)->inline_config(); int error = FALSE; softlist_map names; softlist_map descriptions; enum { NAME_LEN_PARENT = 8, NAME_LEN_CLONE = 16 }; for (int i = 0; i < DEVINFO_STR_SWLIST_MAX - DEVINFO_STR_SWLIST_0; i++) { if (swlist->list_name[i]) { if (mame_options() == NULL) return FALSE; software_list *list = software_list_open(mame_options(), swlist->list_name[i], FALSE, NULL); /* if no .xml list is found, then return (this happens e.g. if you moved/renamed the xml list) */ if (list == NULL) return FALSE; for (software_info *swinfo = software_list_first(list); swinfo != NULL; swinfo = software_list_next(list)) { const char *s; int is_clone = 0; /* First, check if the xml got corrupted: */ /* Did we lost any description? */ if (swinfo->longname == NULL) { mame_printf_error("%s: %s has no description\n", swlist->list_name[i], swinfo->shortname); return TRUE; } /* Did we lost any year? */ if (swinfo->year == NULL) { mame_printf_error("%s: %s has no year\n", swlist->list_name[i], swinfo->shortname); return TRUE; } /* Did we lost any publisher? */ if (swinfo->publisher == NULL) { mame_printf_error("%s: %s has no publisher\n", swlist->list_name[i], swinfo->shortname); return TRUE; } /* 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", swlist->list_name[i], swinfo->shortname, match->shortname); error = TRUE; } /* check for duplicate descriptions */ if (descriptions.add(swinfo->longname, swinfo, FALSE) == TMERR_DUPLICATE) { software_info *match = names.find(swinfo->shortname); mame_printf_error("%s: %s is a duplicate description (%s)\n", swlist->list_name[i], swinfo->longname, match->longname); error = TRUE; } if (swinfo->parentname != NULL) is_clone = 1; /* 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", swlist->list_name[i], 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", swlist->list_name[i], swinfo->shortname, swinfo->year); error = TRUE; break; } // TODO: shall we verify that all parts have some dataarea? and what about checking that a shortname is really present? } software_list_close(list); } } return error; } DEVICE_GET_INFO( software_list ) { switch (state) { /* --- the following bits of info are returned as 64-bit signed integers --- */ case DEVINFO_INT_TOKEN_BYTES: info->i = 1; break; case DEVINFO_INT_INLINE_CONFIG_BYTES: info->i = sizeof(software_list_config); break; /* --- the following bits of info are returned as pointers to data or functions --- */ case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( software_list ); break; case DEVINFO_FCT_STOP: /* Nothing */ break; case DEVINFO_FCT_VALIDITY_CHECK: info->p = (void*)DEVICE_VALIDITY_CHECK_NAME( software_list ); break; /* --- the following bits of info are returned as NULL-terminated strings --- */ case DEVINFO_STR_NAME: strcpy(info->s, "Software lists"); break; case DEVINFO_STR_FAMILY: strcpy(info->s, "Software lists"); break; case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; 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(device)->inline_config(); if ( config->list_name[ state - DEVINFO_STR_SWLIST_0 ] ) strcpy(info->s, config->list_name[ state - DEVINFO_STR_SWLIST_0 ]); } } /*************************************************************************** MENU SUPPORT ***************************************************************************/ /* state of the software menu */ typedef struct _software_menu_state software_menu_state; struct _software_menu_state { char *list_name; /* currently selected list */ }; /* state of a software entry */ typedef struct _software_entry_state software_entry_state; struct _software_entry_state { const char *short_name; const char *interface; }; /* populate a specific list */ static void ui_mess_menu_populate_software_entries(running_machine *machine, ui_menu *menu, char *list_name) { software_list *list = software_list_open(machine->options(), list_name, FALSE, NULL); if (list) { for (software_info *swinfo = software_list_first(list); swinfo != NULL; swinfo = software_list_next(list)) { software_entry_state *entry = (software_entry_state *) ui_menu_pool_alloc(menu, sizeof(*entry)); entry->short_name = ui_menu_pool_strdup(menu, swinfo->shortname); software_part *part = software_find_part(swinfo, NULL, NULL); entry->interface = ui_menu_pool_strdup(menu, part->interface_); ui_menu_item_append(menu, swinfo->shortname, swinfo->longname, 0, entry); } software_list_close(list); } } void ui_mess_menu_software_list(running_machine *machine, ui_menu *menu, void *parameter, void *state) { const ui_menu_event *event; software_menu_state *sw_state = (software_menu_state *)state; if (!ui_menu_populated(menu)) ui_mess_menu_populate_software_entries(machine, menu, sw_state->list_name); /* process the menu */ event = ui_menu_process(machine, menu, 0); if (event != NULL && event->iptkey == IPT_UI_SELECT && event->itemref != NULL) { device_image_interface *image = NULL; device_image_interface *sel_image = NULL; software_entry_state *entry = (software_entry_state *) event->itemref; /* search for a device with the right interface */ for (bool gotone = machine->m_devicelist.first(image); gotone; gotone = image->next(image)) { const char *interface = image->image_config().image_interface(); if (interface != NULL) { if (!strcmp(interface, entry->interface)) { sel_image = image; break; } } } if (sel_image != NULL) sel_image->load(entry->short_name); else popmessage("No matching device found for interface '%s'!", entry->interface); } } /* list of available software lists - i.e. cartridges, floppies */ static void ui_mess_menu_populate_software_list(running_machine *machine, ui_menu *menu) { for (const device_config *dev = machine->config->m_devicelist.first(); dev != NULL; dev = dev->next()) { if (!strcmp(dev->tag(), __SOFTWARE_LIST_TAG)) { software_list_config *swlist = (software_list_config *)downcast(dev)->inline_config(); for (int i = 0; i < DEVINFO_STR_SWLIST_MAX - DEVINFO_STR_SWLIST_0; i++) { if (swlist->list_name[i]) { software_list *list = software_list_open(machine->options(), swlist->list_name[i], FALSE, NULL); if (list) { /* todo: fix this */ for (software_info *swinfo = software_list_first(list); swinfo != NULL; swinfo = software_list_next(list)) { } ui_menu_item_append(menu, list->description, NULL, 0, swlist->list_name[i]); software_list_close(list); } } } } } } void ui_image_menu_software(running_machine *machine, ui_menu *menu, void *parameter, void *state) { const ui_menu_event *event; if (!ui_menu_populated(menu)) ui_mess_menu_populate_software_list(machine, menu); /* process the menu */ event = ui_menu_process(machine, menu, 0); if (event != NULL && event->iptkey == IPT_UI_SELECT) { ui_menu *child_menu = ui_menu_alloc(machine, render_container_get_ui(), ui_mess_menu_software_list, NULL); software_menu_state *child_menustate = (software_menu_state *)ui_menu_alloc_state(child_menu, sizeof(*child_menustate), NULL); child_menustate->list_name = (char *)event->itemref; ui_menu_stack_push(child_menu); } } DEFINE_LEGACY_DEVICE(SOFTWARE_LIST, software_list);