summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/softlist.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/emu/softlist.c')
-rw-r--r--src/emu/softlist.c1445
1 files changed, 1445 insertions, 0 deletions
diff --git a/src/emu/softlist.c b/src/emu/softlist.c
new file mode 100644
index 00000000000..9e08e745cff
--- /dev/null
+++ b/src/emu/softlist.c
@@ -0,0 +1,1445 @@
+/***************************************************************************
+
+ softlist.c
+
+ Software list construction helpers.
+
+
+***************************************************************************/
+
+#include "emu.h"
+#include "pool.h"
+#include "expat.h"
+#include "emuopts.h"
+#include "softlist.h"
+
+#include <ctype.h>
+
+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<software_info *> 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<const legacy_device_config_base *>(&image->device().baseconfig())->inline_config();
+
+ for ( int i = 0; i < DEVINFO_STR_SWLIST_MAX - DEVINFO_STR_SWLIST_0; i++ )
+ {
+ swlist_name = swlist->list_name[i];
+
+ if ( swlist_name && *swlist_name )
+ {
+ if ( software_list_ptr )
+ {
+ software_list_close( software_list_ptr );
+ }
+
+ 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 );
+ }
+ }
+ }
+ }
+ }
+
+ /* 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( 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 );
+ }
+ }
+ }
+
+ /* 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( 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 );
+ }
+
+ 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<const legacy_device_config_base *>(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<const legacy_device_config_base *>(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(mame_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->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->devicelist.first(); dev != NULL; dev = dev->next())
+ {
+ if (!strcmp(dev->tag(), __SOFTWARE_LIST_TAG))
+ {
+ 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])
+ {
+ software_list *list = software_list_open(mame_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); \ No newline at end of file