summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--.gitattributes4
-rw-r--r--src/emu/devimage.c119
-rw-r--r--src/emu/devlegcy.h10
-rw-r--r--src/emu/diimage.c185
-rw-r--r--src/emu/diimage.h58
-rw-r--r--src/emu/emu.h1
-rw-r--r--src/emu/emu.mak2
-rw-r--r--src/emu/romload.c2
-rw-r--r--src/emu/romload.h3
-rw-r--r--src/emu/softlist.c1445
-rw-r--r--src/emu/softlist.h113
-rw-r--r--src/emu/ui.c29
-rw-r--r--src/emu/uiimage.c1130
-rw-r--r--src/emu/uiimage.h27
-rw-r--r--src/emu/uimenu.c21
15 files changed, 2981 insertions, 168 deletions
diff --git a/.gitattributes b/.gitattributes
index ef6db93cf97..afc501b3726 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -814,6 +814,8 @@ src/emu/romload.c svneol=native#text/plain
src/emu/romload.h svneol=native#text/plain
src/emu/schedule.c svneol=native#text/plain
src/emu/schedule.h svneol=native#text/plain
+src/emu/softlist.c svneol=native#text/plain
+src/emu/softlist.h svneol=native#text/plain
src/emu/sound.c svneol=native#text/plain
src/emu/sound.h svneol=native#text/plain
src/emu/sound/2151intf.c svneol=native#text/plain
@@ -1050,6 +1052,8 @@ src/emu/ui.c svneol=native#text/plain
src/emu/ui.h svneol=native#text/plain
src/emu/uigfx.c svneol=native#text/plain
src/emu/uigfx.h svneol=native#text/plain
+src/emu/uiimage.c svneol=native#text/plain
+src/emu/uiimage.h svneol=native#text/plain
src/emu/uiinput.c svneol=native#text/plain
src/emu/uiinput.h svneol=native#text/plain
src/emu/uimenu.c svneol=native#text/plain
diff --git a/src/emu/devimage.c b/src/emu/devimage.c
index fb52fc4cd88..8c0f36b1774 100644
--- a/src/emu/devimage.c
+++ b/src/emu/devimage.c
@@ -209,25 +209,13 @@ legacy_image_device_base::legacy_image_device_base(running_machine &machine, con
****************************************************************************/
/*-------------------------------------------------
- set_image_filename - specifies the filename of
- an image
--------------------------------------------------*/
-
-image_error_t legacy_image_device_base::set_image_filename(const char *filename)
-{
- m_name = filename;
- zippath_parent(&m_working_directory, filename);
- return IMAGE_ERROR_SUCCESS;
-}
-
-/*-------------------------------------------------
is_loaded - quick check to determine whether an
image is loaded
-------------------------------------------------*/
bool legacy_image_device_base::is_loaded()
{
- return (m_file != NULL); // (image->software_info_ptr != NULL);
+ return (m_file != NULL) || (m_software_info_ptr != NULL);
}
/*-------------------------------------------------
@@ -332,6 +320,8 @@ bool legacy_image_device_base::load_internal(const char *path, bool is_create, i
goto done;
/* Check if there's a software list defined for this device and use that if we're not creating an image */
+ if (is_create || !load_software_part( this, path, &m_software_info_ptr, &m_software_part_ptr, &m_full_software_name ) )
+ {
/* determine open plan */
determine_open_plan(is_create, open_plan);
@@ -343,7 +333,17 @@ bool legacy_image_device_base::load_internal(const char *path, bool is_create, i
if (err && (err != IMAGE_ERROR_FILENOTFOUND))
goto done;
}
-
+ }
+
+ /* Copy some image information when we have been loaded through a software list */
+ if ( m_software_info_ptr )
+ {
+ m_longname = m_software_info_ptr->longname;
+ m_manufacturer = m_software_info_ptr->publisher;
+ m_year = m_software_info_ptr->year;
+ //m_playable = m_software_info_ptr->supported;
+ }
+
/* did we fail to find the file? */
if (!is_loaded())
{
@@ -469,6 +469,18 @@ void legacy_image_device_base::clear()
m_name.reset();
m_writeable = FALSE;
m_created = FALSE;
+
+ m_longname.reset();
+ m_manufacturer.reset();
+ m_year.reset();
+ m_playable.reset();
+ m_extrainfo.reset();
+ m_basename_noext.reset();
+ m_filetype.reset();
+
+ m_full_software_name = NULL;
+ m_software_info_ptr = NULL;
+ m_software_part_ptr = NULL;
}
/*-------------------------------------------------
@@ -480,82 +492,3 @@ void legacy_image_device_base::unload()
clear();
}
-
-/***************************************************************************
- WORKING DIRECTORIES
-***************************************************************************/
-
-/*-------------------------------------------------
- try_change_working_directory - tries to change
- the working directory, but only if the directory
- actually exists
--------------------------------------------------*/
-bool legacy_image_device_base::try_change_working_directory(const char *subdir)
-{
- osd_directory *directory;
- const osd_directory_entry *entry;
- bool success = FALSE;
- bool done = FALSE;
-
- directory = osd_opendir(m_working_directory.cstr());
- if (directory != NULL)
- {
- while(!done && (entry = osd_readdir(directory)) != NULL)
- {
- if (!mame_stricmp(subdir, entry->name))
- {
- done = TRUE;
- success = entry->type == ENTTYPE_DIR;
- }
- }
-
- osd_closedir(directory);
- }
-
- /* did we successfully identify the directory? */
- if (success)
- zippath_combine(&m_working_directory, m_working_directory, subdir);
-
- return success;
-}
-/*-------------------------------------------------
- setup_working_directory - sets up the working
- directory according to a few defaults
--------------------------------------------------*/
-
-void legacy_image_device_base::setup_working_directory()
-{
- const game_driver *gamedrv;
- char *dst = NULL;
-
- osd_get_full_path(&dst,".");
- /* first set up the working directory to be the starting directory */
- m_working_directory = dst;
-
- /* now try browsing down to "software" */
- if (try_change_working_directory("software"))
- {
- /* now down to a directory for this computer */
- gamedrv = device().machine->gamedrv;
- while(gamedrv && !try_change_working_directory(gamedrv->name))
- {
- gamedrv = driver_get_compatible(gamedrv);
- }
- }
- osd_free(dst);
-}
-
-//-------------------------------------------------
-// working_directory - returns the working
-// directory to use for this image; this is
-// valid even if not mounted
-//-------------------------------------------------
-
-const char * legacy_image_device_base::working_directory()
-{
- /* check to see if we've never initialized the working directory */
- if (m_working_directory.len() == 0)
- setup_working_directory();
-
- return m_working_directory;
-}
diff --git a/src/emu/devlegcy.h b/src/emu/devlegcy.h
index c806796d4a1..3a53e7c2e86 100644
--- a/src/emu/devlegcy.h
+++ b/src/emu/devlegcy.h
@@ -638,8 +638,6 @@ class legacy_image_device_base : public legacy_device_base,
{
friend class legacy_image_device_config_base;
public:
- virtual void set_working_directory(const char *working_directory) { m_working_directory = working_directory; }
- virtual const char * working_directory();
virtual bool load(const char *path);
virtual bool finish_load();
virtual void unload();
@@ -648,16 +646,8 @@ protected:
// construction/destruction
legacy_image_device_base(running_machine &machine, const device_config &config);
// device_image_interface overrides
-
- /* working directory; persists across mounts */
- astring m_working_directory;
-
- void setup_working_directory();
- bool try_change_working_directory(const char *subdir);
-
bool load_internal(const char *path, bool is_create, int create_format, option_resolution *create_args);
void determine_open_plan(int is_create, UINT32 *open_plan);
- image_error_t set_image_filename(const char *filename);
image_error_t load_image_by_path(UINT32 open_flags, const char *path);
void clear();
bool is_loaded();
diff --git a/src/emu/diimage.c b/src/emu/diimage.c
index 48f510ce7b2..f3ed1c757eb 100644
--- a/src/emu/diimage.c
+++ b/src/emu/diimage.c
@@ -39,6 +39,7 @@
#include "emu.h"
#include "ui.h"
+#include "zippath.h"
//**************************************************************************
@@ -130,7 +131,10 @@ const char *device_config_image_interface::device_brieftypename(iodevice_t type)
device_image_interface::device_image_interface(running_machine &machine, const device_config &config, device_t &device)
: device_interface(machine, config, device),
m_image_config(dynamic_cast<const device_config_image_interface &>(config)),
- m_file(NULL)
+ m_file(NULL),
+ m_full_software_name(NULL),
+ m_software_info_ptr(NULL),
+ m_software_part_ptr(NULL)
{
}
@@ -143,6 +147,45 @@ device_image_interface::~device_image_interface()
{
}
+/*-------------------------------------------------
+ display - call image display callback function
+-------------------------------------------------*/
+
+void device_image_interface::display()
+{
+}
+
+
+/*-------------------------------------------------
+ set_image_filename - specifies the filename of
+ an image
+-------------------------------------------------*/
+
+image_error_t device_image_interface::set_image_filename(const char *filename)
+{
+ m_name = filename;
+ zippath_parent(&m_working_directory, filename);
+ m_basename = m_name.cpy(m_name);
+
+ int loc1 = m_name.rchr(0,'\\');
+ int loc2 = m_name.rchr(0,'/');
+ int loc3 = m_name.rchr(0,':');
+ int loc = MAX(loc1,MAX(loc2,loc3));
+ if (loc!=-1) {
+ m_basename = m_basename.substr(loc + 1,m_basename.len()-loc);
+ }
+ m_basename_noext = m_basename.cpy(m_basename);
+ m_filetype = "";
+ loc = m_basename_noext.rchr(0,'.');
+ if (loc!=-1) {
+ m_basename_noext = m_basename_noext.substr(0,loc);
+ m_filetype = m_basename.cpy(m_basename);
+ m_filetype = m_filetype.substr(loc + 1,m_filetype.len()-loc);
+ }
+
+ return IMAGE_ERROR_SUCCESS;
+}
+
/****************************************************************************
CREATION FORMATS
****************************************************************************/
@@ -260,74 +303,132 @@ void device_image_interface::message(const char *format, ...)
}
-/****************************************************************************
- Accessor functions
+/***************************************************************************
+ WORKING DIRECTORIES
+***************************************************************************/
- These provide information about the device; and about the mounted image
-****************************************************************************/
+/*-------------------------------------------------
+ try_change_working_directory - tries to change
+ the working directory, but only if the directory
+ actually exists
+-------------------------------------------------*/
+bool device_image_interface::try_change_working_directory(const char *subdir)
+{
+ osd_directory *directory;
+ const osd_directory_entry *entry;
+ bool success = FALSE;
+ bool done = FALSE;
+
+ directory = osd_opendir(m_working_directory.cstr());
+ if (directory != NULL)
+ {
+ while(!done && (entry = osd_readdir(directory)) != NULL)
+ {
+ if (!mame_stricmp(subdir, entry->name))
+ {
+ done = TRUE;
+ success = entry->type == ENTTYPE_DIR;
+ }
+ }
+
+ osd_closedir(directory);
+ }
+
+ /* did we successfully identify the directory? */
+ if (success)
+ zippath_combine(&m_working_directory, m_working_directory, subdir);
+ return success;
+}
/*-------------------------------------------------
- filename
+ setup_working_directory - sets up the working
+ directory according to a few defaults
-------------------------------------------------*/
-const char *device_image_interface::filename()
+void device_image_interface::setup_working_directory()
{
- const char *name = m_name;
- return (name[0] != '\0') ? name : NULL;
+ const game_driver *gamedrv;
+ char *dst = NULL;
+
+ osd_get_full_path(&dst,".");
+ /* first set up the working directory to be the starting directory */
+ m_working_directory = dst;
+
+ /* now try browsing down to "software" */
+ if (try_change_working_directory("software"))
+ {
+ /* now down to a directory for this computer */
+ gamedrv = device().machine->gamedrv;
+ while(gamedrv && !try_change_working_directory(gamedrv->name))
+ {
+ gamedrv = driver_get_compatible(gamedrv);
+ }
+ }
+ osd_free(dst);
}
+//-------------------------------------------------
+// working_directory - returns the working
+// directory to use for this image; this is
+// valid even if not mounted
+//-------------------------------------------------
+
+const char * device_image_interface::working_directory()
+{
+ /* check to see if we've never initialized the working directory */
+ if (m_working_directory.len() == 0)
+ setup_working_directory();
+
+ return m_working_directory;
+}
+
+
/*-------------------------------------------------
- basename
+ get_software_region
-------------------------------------------------*/
-const char *device_image_interface::basename()
+UINT8 *device_image_interface::get_software_region(const char *tag)
{
- char *fname = (char*)astring(filename()).cstr();
- const char *c;
+ char full_tag[256];
- // NULL begets NULL
- if (!fname)
+ if ( m_software_info_ptr == NULL || m_software_part_ptr == NULL )
return NULL;
- // start at the end and return when we hit a slash or colon
- for (c = fname + strlen(fname) - 1; c >= fname; c--)
- if (*c == '\\' || *c == '/' || *c == ':')
- return c + 1;
-
- // otherwise, return the whole thing
- return fname;
+ sprintf( full_tag, "%s:%s", device().tag(), tag );
+ return memory_region( device().machine, full_tag );
}
/*-------------------------------------------------
- basename_noext
+ image_get_software_region_length
-------------------------------------------------*/
-const char *device_image_interface::basename_noext()
+UINT32 device_image_interface::get_software_region_length(const char *tag)
{
- const char *s;
- char *ext;
+ char full_tag[256];
- s = astring(basename());
- if (s)
- {
- ext = (char *)strrchr(s, '.');
- if (ext)
- *ext = '\0';
- }
- return s;
+ sprintf( full_tag, "%s:%s", device().tag(), tag );
+ return memory_region_length( device().machine, full_tag );
}
-
/*-------------------------------------------------
- filetype
--------------------------------------------------*/
+ image_get_feature
+ -------------------------------------------------*/
-const char *device_image_interface::filetype()
+const char *device_image_interface::get_feature(const char *feature_name)
{
- const char *s = filename();
- if (s != NULL)
- s = strrchr(s, '.');
- return s ? s+1 : NULL;
+ feature_list *feature;
+
+ if ( ! m_software_part_ptr->featurelist )
+ return NULL;
+
+ for ( feature = m_software_part_ptr->featurelist; feature; feature = feature->next )
+ {
+ if ( ! strcmp( feature->name, feature_name ) )
+ return feature->value;
+ }
+
+ return NULL;
}
+
diff --git a/src/emu/diimage.h b/src/emu/diimage.h
index 6a8815d06f9..70bb5556c6d 100644
--- a/src/emu/diimage.h
+++ b/src/emu/diimage.h
@@ -103,6 +103,9 @@ struct image_device_format
};
class device_image_interface;
+struct feature_list;
+struct software_part;
+struct software_info;
// device image interface function types
typedef int (*device_image_load_func)(device_image_interface &image);
@@ -188,12 +191,12 @@ public:
device_image_interface(running_machine &machine, const device_config &config, device_t &device);
virtual ~device_image_interface();
- virtual void set_working_directory(const char *working_directory) = 0;
- virtual const char * working_directory() = 0;
virtual bool load(const char *path) = 0;
virtual bool finish_load() = 0;
virtual void unload() = 0;
+ virtual void display();
+
virtual const image_device_format *device_get_indexed_creatable_format(int index);
virtual const image_device_format *device_get_named_creatable_format(const char *format_name);
const option_guide *image_device_get_creation_option_guide() { return m_image_config.create_option_guide(); }
@@ -206,10 +209,10 @@ public:
void message(const char *format, ...);
bool exists() { return m_name.len() != 0; }
- const char *filename();
- const char *basename();
- const char *basename_noext();
- const char *filetype();
+ const char *filename() { if (m_name.len()==0) return NULL; else return m_name; }
+ const char *basename() { if (m_basename.len()==0) return NULL; else return m_basename; }
+ const char *basename_noext() { if (m_basename_noext.len()==0) return NULL; else return m_basename_noext; }
+ const char *filetype() { if (m_filetype.len()==0) return NULL; else return m_filetype; }
core_file *image_core_file() { return m_file; }
UINT64 length() { check_for_file(); return core_fsize(m_file); }
bool is_writable() { return m_writeable; }
@@ -227,10 +230,33 @@ public:
const device_config_image_interface &image_config() const { return m_image_config; }
void set_init_phase() { m_init_phase = TRUE; }
+
+ const char* longname() { return m_longname; }
+ const char* manufacturer() { return m_manufacturer; }
+ const char* year() { return m_year; }
+ const char* playable() { return m_playable; }
+ const char* pcb() { return m_pcb; }
+ const char* extrainfo() { return m_extrainfo; }
+
+ const software_info *software_entry() { return m_software_info_ptr; }
+
+ virtual void set_working_directory(const char *working_directory) { m_working_directory = working_directory; }
+ virtual const char * working_directory();
+
+ UINT8 *get_software_region(const char *tag);
+ UINT32 get_software_region_length(const char *tag);
+ const char *get_feature(const char *feature_name);
+
protected:
+ image_error_t set_image_filename(const char *filename);
+
void clear_error();
void check_for_file() { assert_always(m_file != NULL, "Illegal operation on unmounted image"); }
+
+ void setup_working_directory();
+ bool try_change_working_directory(const char *subdir);
+
// derived class overrides
// configuration
@@ -243,6 +269,26 @@ protected:
/* variables that are only non-zero when an image is mounted */
core_file *m_file;
astring m_name;
+ astring m_basename;
+ astring m_basename_noext;
+ astring m_filetype;
+
+ /* working directory; persists across mounts */
+ astring m_working_directory;
+
+ /* Software information */
+ char *m_full_software_name;
+ software_info *m_software_info_ptr;
+ software_part *m_software_part_ptr;
+
+ /* info read from the hash file/software list */
+ astring m_longname;
+ astring m_manufacturer;
+ astring m_year;
+ astring m_playable;
+ astring m_pcb;
+ astring m_extrainfo;
+
/* flags */
bool m_writeable;
bool m_created;
diff --git a/src/emu/emu.h b/src/emu/emu.h
index c5625d6d60c..50ab4d3853a 100644
--- a/src/emu/emu.h
+++ b/src/emu/emu.h
@@ -99,6 +99,7 @@
#include "state.h"
// image-related
+#include "softlist.h"
#include "image.h"
// the running machine
diff --git a/src/emu/emu.mak b/src/emu/emu.mak
index e96bf33ea71..a6c40b9ccd2 100644
--- a/src/emu/emu.mak
+++ b/src/emu/emu.mak
@@ -80,6 +80,7 @@ EMUOBJS = \
$(EMUOBJ)/rendutil.o \
$(EMUOBJ)/romload.o \
$(EMUOBJ)/schedule.o \
+ $(EMUOBJ)/softlist.o \
$(EMUOBJ)/sound.o \
$(EMUOBJ)/state.o \
$(EMUOBJ)/streams.o \
@@ -87,6 +88,7 @@ EMUOBJS = \
$(EMUOBJ)/timer.o \
$(EMUOBJ)/ui.o \
$(EMUOBJ)/uigfx.o \
+ $(EMUOBJ)/uiimage.o \
$(EMUOBJ)/uiinput.o \
$(EMUOBJ)/uimenu.o \
$(EMUOBJ)/validity.o \
diff --git a/src/emu/romload.c b/src/emu/romload.c
index 4cbcd2bfbf7..ebf18687066 100644
--- a/src/emu/romload.c
+++ b/src/emu/romload.c
@@ -1264,7 +1264,6 @@ static UINT32 normalize_flags_for_device(running_machine *machine, UINT32 startf
}
-#ifdef MESS
/*-------------------------------------------------
load_software_part_region - load a software part
@@ -1343,7 +1342,6 @@ void load_software_part_region(running_device *device, char *swlist, char *swnam
/* display the results and exit */
display_rom_load_results(romdata);
}
-#endif
/*-------------------------------------------------
diff --git a/src/emu/romload.h b/src/emu/romload.h
index 298d0df63f9..e66a0d04c7d 100644
--- a/src/emu/romload.h
+++ b/src/emu/romload.h
@@ -324,9 +324,6 @@ chd_file *get_disk_handle(running_machine *machine, const char *region);
/* set a pointer to the CHD file associated with the given region */
void set_disk_handle(running_machine *machine, const char *region, mame_file *file, chd_file *chd);
-
-#ifdef MESS
void load_software_part_region(running_device *device, char *swlist, char *swname, rom_entry *start_region);
-#endif
#endif /* __ROMLOAD_H__ */
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
diff --git a/src/emu/softlist.h b/src/emu/softlist.h
new file mode 100644
index 00000000000..2fa52347c9e
--- /dev/null
+++ b/src/emu/softlist.h
@@ -0,0 +1,113 @@
+/*********************************************************************
+
+ softlist.h
+
+ Software and software list information.
+
+*********************************************************************/
+
+#ifndef __SOFTLIST_H_
+#define __SOFTLIST_H_
+
+#include "uimenu.h"
+
+
+/*********************************************************************
+
+ Internal structures and XML file handling
+
+*********************************************************************/
+
+/* Replace this with list<string>? */
+struct feature_list
+{
+ feature_list *next;
+ char *name;
+ char *value;
+};
+
+struct software_part
+{
+ const char *name;
+ const char *interface_;
+ feature_list *featurelist;
+ struct rom_entry *romdata;
+};
+
+
+/* The software info struct holds basic software information. Additional,
+ optional information like local software names, release dates, serial
+ numbers, etc can be maintained and stored in external recources.
+*/
+struct software_info
+{
+ const char *shortname;
+ const char *longname;
+ const char *parentname;
+ const char *year; /* Copyright year on title screen, actual release dates can be tracked in external resources */
+ const char *publisher;
+ UINT32 supported;
+ software_part *partdata;
+};
+
+
+typedef struct _software_list software_list;
+
+/* Handling a software list */
+software_list *software_list_open(core_options *options, const char *listname, int is_preload, void (*error_proc)(const char *message));
+void software_list_close(software_list *swlist);
+software_info *software_list_first(software_list *swlist);
+software_info *software_list_find(software_list *swlist, const char *software);
+software_info *software_list_next(software_list *swlist);
+
+software_part *software_find_part(software_info *sw, const char *partname, const char *interface_);
+software_part *software_part_next(software_part *part);
+
+
+bool load_software_part(device_image_interface *image, const char *path, software_info **sw_info, software_part **sw_part, char **full_sw_name);
+
+void ui_image_menu_software(running_machine *machine, ui_menu *menu, void *parameter, void *state);
+
+
+/*********************************************************************
+
+ Driver software list configuration
+
+*********************************************************************/
+DECLARE_LEGACY_DEVICE(SOFTWARE_LIST, software_list);
+#define __SOFTWARE_LIST_TAG "software_list"
+
+
+#define SOFTWARE_SUPPORTED_YES 0
+#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];
+};
+
+
+#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 MDRV_SOFTWARE_LIST_CONFIG(_idx,_list) \
+ MDRV_DEVICE_CONFIG_DATAPTR_ARRAY(software_list_config, list_name, _idx, _list)
+
+#define MDRV_SOFTWARE_LIST_ADD( _list ) \
+ MDRV_DEVICE_ADD( __SOFTWARE_LIST_TAG, SOFTWARE_LIST, 0 ) \
+ MDRV_SOFTWARE_LIST_CONFIG(0,_list)
+
+
+#define MDRV_SOFTWARE_LIST_MODIFY( _list ) \
+ MDRV_DEVICE_MODIFY( __SOFTWARE_LIST_TAG ) \
+ MDRV_SOFTWARE_LIST_CONFIG(0,_list)
+
+
+#endif
diff --git a/src/emu/ui.c b/src/emu/ui.c
index 4a4361d791c..043b75f1083 100644
--- a/src/emu/ui.c
+++ b/src/emu/ui.c
@@ -21,11 +21,6 @@
#include "uiinput.h"
#include "uimenu.h"
#include "uigfx.h"
-
-#ifdef MESS
-#include "uimess.h"
-#endif /* MESS */
-
#include <ctype.h>
@@ -1266,6 +1261,26 @@ void ui_paste(running_machine *machine)
}
/*-------------------------------------------------
+ ui_image_handler_ingame - execute display
+ callback function for each image device
+-------------------------------------------------*/
+
+void ui_image_handler_ingame(running_machine *machine)
+{
+ device_image_interface *image = NULL;
+
+ /* run display routine for devices */
+ if (mame_get_phase(machine) == MAME_PHASE_RUNNING)
+ {
+ for (bool gotone = machine->devicelist.first(image); gotone; gotone = image->next(image))
+ {
+ image->display();
+ }
+ }
+
+}
+
+/*-------------------------------------------------
handler_ingame - in-game handler takes care
of the standard keypresses
-------------------------------------------------*/
@@ -1345,9 +1360,7 @@ static UINT32 handler_ingame(running_machine *machine, render_container *contain
ui_paste(machine);
}
-#ifdef MESS
- ui_mess_handler_ingame(machine);
-#endif /* MESS */
+ ui_image_handler_ingame(machine);
if (ui_disabled) return ui_disabled;
diff --git a/src/emu/uiimage.c b/src/emu/uiimage.c
new file mode 100644
index 00000000000..76d2e7884e3
--- /dev/null
+++ b/src/emu/uiimage.c
@@ -0,0 +1,1130 @@
+/*********************************************************************
+
+ filemngr.c
+
+ MESS's clunky built-in file manager
+
+ TODO
+ - Support image creation arguments
+ - Restrict directory listing by file extension
+ - Support file manager invocation from the main menu for
+ required images
+ - Restrict empty slot if image required
+
+*********************************************************************/
+
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+
+#include "emu.h"
+#include "image.h"
+#include "ui.h"
+#include "uimenu.h"
+#include "zippath.h"
+#include "unicode.h"
+
+
+
+/***************************************************************************
+ CONSTANTS
+***************************************************************************/
+
+/* conditional compilation to enable chosing of image formats - this is not
+ * yet fully implemented */
+#define ENABLE_FORMATS 0
+
+/* time (in seconds) to display errors */
+#define ERROR_MESSAGE_TIME 5
+
+/* itemrefs for key menu items */
+#define ITEMREF_NEW_IMAGE_NAME ((void *) 0x0001)
+#define ITEMREF_CREATE ((void *) 0x0002)
+#define ITEMREF_FORMAT ((void *) 0x0003)
+#define ITEMREF_NO ((void *) 0x0004)
+#define ITEMREF_YES ((void *) 0x0005)
+
+
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+/* menu item type in the file selector */
+enum _file_selector_entry_type
+{
+ SELECTOR_ENTRY_TYPE_EMPTY,
+ SELECTOR_ENTRY_TYPE_CREATE,
+ SELECTOR_ENTRY_TYPE_DRIVE,
+ SELECTOR_ENTRY_TYPE_DIRECTORY,
+ SELECTOR_ENTRY_TYPE_FILE
+};
+typedef enum _file_selector_entry_type file_selector_entry_type;
+
+
+
+/* an entry within the file manager */
+typedef struct _file_selector_entry file_selector_entry;
+struct _file_selector_entry
+{
+ file_selector_entry *next;
+
+ file_selector_entry_type type;
+ const char *basename;
+ const char *fullpath;
+};
+
+
+
+/* state of the file manager */
+typedef struct _file_manager_menu_state file_manager_menu_state;
+struct _file_manager_menu_state
+{
+ device_image_interface *selected_device;
+ astring *current_directory;
+ astring *current_file;
+};
+
+
+
+/* state of the file selector menu */
+typedef struct _file_selector_menu_state file_selector_menu_state;
+struct _file_selector_menu_state
+{
+ file_manager_menu_state *manager_menustate;
+ file_selector_entry *entrylist;
+};
+
+
+
+/* state of the file creator menu */
+typedef struct _file_create_menu_state file_create_menu_state;
+struct _file_create_menu_state
+{
+ file_manager_menu_state *manager_menustate;
+ const image_device_format *current_format;
+ int confirm_save_as_yes;
+ char filename_buffer[1024];
+};
+
+
+/* state of the confirm save as menu */
+typedef struct _confirm_save_as_menu_state confirm_save_as_menu_state;
+struct _confirm_save_as_menu_state
+{
+ int *yes;
+};
+
+char *stripspace(const char *src)
+{
+ static char buff[512];
+ if( src )
+ {
+ char *dst;
+ while( *src && isspace(*src) )
+ src++;
+ strcpy(buff, src);
+ dst = buff + strlen(buff);
+ while( dst >= buff && isspace(*--dst) )
+ *dst = '\0';
+ return buff;
+ }
+ return NULL;
+}
+
+//============================================================
+// strip_extension
+//============================================================
+
+char *strip_extension(const char *filename)
+{
+ char *newname;
+ char *c;
+
+ // NULL begets NULL
+ if (!filename)
+ return NULL;
+
+ // allocate space for it
+ newname = (char *) malloc(strlen(filename) + 1);
+ if (!newname)
+ return NULL;
+
+ // copy in the name
+ strcpy(newname, filename);
+
+ // search backward for a period, failing if we hit a slash or a colon
+ for (c = newname + strlen(newname) - 1; c >= newname; c--)
+ {
+ // if we hit a period, NULL terminate and break
+ if (*c == '.')
+ {
+ *c = 0;
+ break;
+ }
+
+ // if we hit a slash or colon just stop
+ if (*c == '\\' || *c == '/' || *c == ':')
+ break;
+ }
+
+ return newname;
+}
+
+
+/***************************************************************************
+ MENU HELPERS
+***************************************************************************/
+
+/*-------------------------------------------------
+ input_character - inputs a typed character
+ into a buffer
+-------------------------------------------------*/
+
+static void input_character(char *buffer, size_t buffer_length, unicode_char unichar, int (*filter)(unicode_char))
+{
+ size_t buflen = strlen(buffer);
+
+ if ((unichar == 8) && (buflen > 0))
+ {
+ *(char *)utf8_previous_char(&buffer[buflen]) = 0;
+ }
+ else if ((unichar > ' ') && ((filter == NULL) || (*filter)(unichar)))
+ {
+ buflen += utf8_from_uchar(&buffer[buflen], buffer_length - buflen, unichar);
+ buffer[buflen] = 0;
+ }
+}
+
+
+
+/*-------------------------------------------------
+ extra_text_draw_box - generically adds header
+ or footer text
+-------------------------------------------------*/
+
+static void extra_text_draw_box(float origx1, float origx2, float origy, float yspan, const char *text, int direction)
+{
+ float text_width, text_height;
+ float width, maxwidth;
+ float x1, y1, x2, y2, temp;
+
+ /* get the size of the text */
+ ui_draw_text_full( render_container_get_ui(),text, 0.0f, 0.0f, 1.0f, JUSTIFY_LEFT, WRAP_WORD,
+ DRAW_NONE, ARGB_WHITE, ARGB_BLACK, &text_width, &text_height);
+ width = text_width + (2 * UI_BOX_LR_BORDER);
+ maxwidth = MAX(width, origx2 - origx1);
+
+ /* compute our bounds */
+ x1 = 0.5f - 0.5f * maxwidth;
+ x2 = x1 + maxwidth;
+ y1 = origy + (yspan * direction);
+ y2 = origy + (UI_BOX_TB_BORDER * direction);
+
+ if (y1 > y2)
+ {
+ temp = y1;
+ y1 = y2;
+ y2 = temp;
+ }
+
+ /* draw a box */
+ ui_draw_outlined_box(render_container_get_ui(),x1, y1, x2, y2, UI_BACKGROUND_COLOR);
+
+ /* take off the borders */
+ x1 += UI_BOX_LR_BORDER;
+ x2 -= UI_BOX_LR_BORDER;
+ y1 += UI_BOX_TB_BORDER;
+ y2 -= UI_BOX_TB_BORDER;
+
+ /* draw the text within it */
+ ui_draw_text_full(render_container_get_ui(),text, x1, y1, text_width, JUSTIFY_LEFT, WRAP_WORD,
+ DRAW_NORMAL, ARGB_WHITE, ARGB_BLACK, NULL, NULL);
+}
+
+
+
+/*-------------------------------------------------
+ extra_text_render - generically adds header
+ and footer text
+-------------------------------------------------*/
+
+static void extra_text_render(running_machine *machine, ui_menu *menu, void *state, void *selectedref, float top, float bottom,
+ float origx1, float origy1, float origx2, float origy2,
+ const char *header, const char *footer)
+{
+ header = ((header != NULL) && (header[0] != '\0')) ? header : NULL;
+ footer = ((footer != NULL) && (footer[0] != '\0')) ? footer : NULL;
+
+ if (header != NULL)
+ extra_text_draw_box(origx1, origx2, origy1, top, header, -1);
+ if (footer != NULL)
+ extra_text_draw_box(origx1, origx2, origy2, bottom, footer, +1);
+}
+
+
+
+/***************************************************************************
+ CONFIRM SAVE AS MENU
+***************************************************************************/
+
+/*-------------------------------------------------
+ menu_confirm_save_as_populate - populates the
+ confirm save as menu
+-------------------------------------------------*/
+
+static void menu_confirm_save_as_populate(running_machine *machine, ui_menu *menu, void *state)
+{
+ ui_menu_item_append(menu, "File Already Exists - Overide?", NULL, MENU_FLAG_DISABLE, NULL);
+ ui_menu_item_append(menu, MENU_SEPARATOR_ITEM, NULL, MENU_FLAG_DISABLE, NULL);
+ ui_menu_item_append(menu, "No", NULL, 0, ITEMREF_NO);
+ ui_menu_item_append(menu, "Yes", NULL, 0, ITEMREF_YES);
+}
+
+
+
+/*-------------------------------------------------
+ menu_confirm_save_as - confirm save as menu
+-------------------------------------------------*/
+
+static void menu_confirm_save_as(running_machine *machine, ui_menu *menu, void *parameter, void *state)
+{
+ const ui_menu_event *event;
+ confirm_save_as_menu_state *menustate = (confirm_save_as_menu_state *) state;
+
+ /* if the menu isn't built, populate now */
+ if (!ui_menu_populated(menu))
+ menu_confirm_save_as_populate(machine, menu, state);
+
+ /* process the menu */
+ event = ui_menu_process(machine, menu, 0);
+
+ /* process the event */
+ if ((event != NULL) && (event->iptkey == IPT_UI_SELECT))
+ {
+ if (event->itemref == ITEMREF_YES)
+ *menustate->yes = TRUE;
+
+ /* no matter what, pop out */
+ ui_menu_stack_pop(machine);
+ }
+}
+
+
+
+/***************************************************************************
+ FILE CREATE MENU
+***************************************************************************/
+
+/*-------------------------------------------------
+ is_valid_filename_char - tests to see if a
+ character is valid in a filename
+-------------------------------------------------*/
+
+static int is_valid_filename_char(unicode_char unichar)
+{
+ /* this should really be in the OSD layer */
+ static const char valid_filename_char[] =
+ {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 00-0f */
+ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10-1f */
+ 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, /* !"#$%&'()*+,-./ */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 0123456789:;<=>? */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* @ABCDEFGHIJKLMNO */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, /* PQRSTUVWXYZ[\]^_ */
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* `abcdefghijklmno */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, /* pqrstuvwxyz{|}~ */
+ };
+ return (unichar < ARRAY_LENGTH(valid_filename_char)) && valid_filename_char[unichar];
+}
+
+
+
+/*-------------------------------------------------
+ file_create_render_extra - perform our
+ special rendering
+-------------------------------------------------*/
+
+static void file_create_render_extra(running_machine *machine, ui_menu *menu, void *state, void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2)
+{
+ file_create_menu_state *menustate = (file_create_menu_state *) state;
+
+ extra_text_render(machine, menu, state, selectedref, top, bottom, origx1, origy1, origx2, origy2,
+ astring_c(menustate->manager_menustate->current_directory),
+ NULL);
+}
+
+
+
+/*-------------------------------------------------
+ menu_file_create_populate - populates the file
+ creator menu
+-------------------------------------------------*/
+
+static void menu_file_create_populate(running_machine *machine, ui_menu *menu, void *state, void *selection)
+{
+ astring buffer;
+ file_create_menu_state *menustate = (file_create_menu_state *) state;
+ device_image_interface *device = menustate->manager_menustate->selected_device;
+ const image_device_format *format;
+ const char *new_image_name;
+
+ /* append the "New Image Name" item */
+ if (selection == ITEMREF_NEW_IMAGE_NAME)
+ {
+ astring_assemble_2(&buffer, menustate->filename_buffer, "_");
+ new_image_name = astring_c(&buffer);
+ }
+ else
+ {
+ new_image_name = menustate->filename_buffer;
+ }
+ ui_menu_item_append(menu, "New Image Name:", new_image_name, 0, ITEMREF_NEW_IMAGE_NAME);
+
+ /* do we support multiple formats? */
+ format = device->device_get_creatable_formats();
+ if (ENABLE_FORMATS && (format != NULL))
+ {
+ ui_menu_item_append(menu, "Image Format:", menustate->current_format->m_description, 0, ITEMREF_FORMAT);
+ menustate->current_format = format;
+ }
+
+ /* finish up the menu */
+ ui_menu_item_append(menu, MENU_SEPARATOR_ITEM, NULL, 0, NULL);
+ ui_menu_item_append(menu, "Create", NULL, 0, ITEMREF_CREATE);
+
+ /* set up custom render proc */
+ ui_menu_set_custom_render(menu, file_create_render_extra, ui_get_line_height() + 3.0f * UI_BOX_TB_BORDER, 0);
+}
+
+
+
+/*-------------------------------------------------
+ create_new_image - creates a new disk image
+-------------------------------------------------*/
+
+static int create_new_image(device_image_interface *image, const char *directory, const char *filename, int *yes)
+{
+ astring *path;
+ osd_directory_entry *entry;
+ osd_dir_entry_type file_type;
+ int do_create, err;
+ int result = FALSE;
+ ui_menu *child_menu;
+ confirm_save_as_menu_state *child_menustate;
+
+ /* assemble the full path */
+ path = zippath_combine(astring_alloc(), directory, filename);
+
+ /* does a file or a directory exist at the path */
+ entry = osd_stat(astring_c(path));
+ file_type = (entry != NULL) ? entry->type : ENTTYPE_NONE;
+ if (entry != NULL)
+ free(entry);
+
+ /* special case */
+ if ((file_type == ENTTYPE_FILE) && *yes)
+ file_type = ENTTYPE_NONE;
+
+ switch(file_type)
+ {
+ case ENTTYPE_NONE:
+ /* no file/dir here - always create */
+ do_create = TRUE;
+ break;
+
+ case ENTTYPE_FILE:
+ /* a file exists here - ask for permission from the user */
+ child_menu = ui_menu_alloc(image->device().machine, render_container_get_ui(), menu_confirm_save_as, NULL);
+ child_menustate = (confirm_save_as_menu_state*)ui_menu_alloc_state(child_menu, sizeof(*child_menustate), NULL);
+ child_menustate->yes = yes;
+ ui_menu_stack_push(child_menu);
+ do_create = FALSE;
+ break;
+
+ case ENTTYPE_DIR:
+ /* a directory exists here - we can't save over it */
+ ui_popup_time(ERROR_MESSAGE_TIME, "Cannot save over directory");
+ do_create = FALSE;
+ break;
+
+ default:
+ fatalerror("Unexpected");
+ do_create = FALSE;
+ break;
+ }
+
+ /* create the image, if appropriate */
+ if (do_create)
+ {
+ err = image->create(astring_c(path), 0, NULL);
+ if (err != 0)
+ popmessage("Error: %s", image->error());
+ else
+ result = TRUE;
+ }
+
+ /* free the path */
+ astring_free(path);
+
+ return result;
+}
+
+
+
+/*-------------------------------------------------
+ menu_file_create - file creator menu
+-------------------------------------------------*/
+
+static void menu_file_create(running_machine *machine, ui_menu *menu, void *parameter, void *state)
+{
+ void *selection;
+ const ui_menu_event *event;
+ ui_menu_event fake_event;
+ file_create_menu_state *menustate = (file_create_menu_state *) state;
+
+ /* identify the selection */
+ selection = ui_menu_get_selection(menu);
+
+ /* rebuild the menu */
+ ui_menu_reset(menu, UI_MENU_RESET_REMEMBER_POSITION);
+ menu_file_create_populate(machine, menu, state, selection);
+
+ if (menustate->confirm_save_as_yes)
+ {
+ /* we just returned from a "confirm save as" dialog and the user said "yes" - fake an event */
+ memset(&fake_event, 0, sizeof(fake_event));
+ fake_event.iptkey = IPT_UI_SELECT;
+ fake_event.itemref = ITEMREF_CREATE;
+ event = &fake_event;
+ }
+ else
+ {
+ /* process the menu */
+ event = ui_menu_process(machine, menu, 0);
+ }
+
+ /* process the event */
+ if (event != NULL)
+ {
+ /* handle selections */
+ switch(event->iptkey)
+ {
+ case IPT_UI_SELECT:
+ if ((event->itemref == ITEMREF_CREATE) || (event->itemref == ITEMREF_NEW_IMAGE_NAME))
+ {
+ if (create_new_image(
+ menustate->manager_menustate->selected_device,
+ astring_c(menustate->manager_menustate->current_directory),
+ menustate->filename_buffer,
+ &menustate->confirm_save_as_yes))
+ {
+ /* success - pop out twice to device view */
+ ui_menu_stack_pop(machine);
+ ui_menu_stack_pop(machine);
+ }
+ }
+ break;
+
+ case IPT_SPECIAL:
+ if (ui_menu_get_selection(menu) == ITEMREF_NEW_IMAGE_NAME)
+ {
+ input_character(
+ menustate->filename_buffer,
+ ARRAY_LENGTH(menustate->filename_buffer),
+ event->unichar,
+ is_valid_filename_char);
+ }
+ break;
+ }
+ }
+}
+
+
+
+/***************************************************************************
+ FILE SELECTOR MENU
+***************************************************************************/
+
+/*-------------------------------------------------
+ file_selector_render_extra - perform our
+ special rendering
+-------------------------------------------------*/
+
+static void file_selector_render_extra(running_machine *machine, ui_menu *menu, void *state, void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2)
+{
+ file_selector_menu_state *menustate = (file_selector_menu_state *) state;
+
+ extra_text_render(machine, menu, state, selectedref, top, bottom,
+ origx1, origy1, origx2, origy2,
+ astring_c(menustate->manager_menustate->current_directory),
+ NULL);
+}
+
+
+
+/*-------------------------------------------------
+ compare_file_selector_entries - sorting proc
+ for file selector entries
+-------------------------------------------------*/
+
+static int compare_file_selector_entries(const file_selector_entry *e1, const file_selector_entry *e2)
+{
+ int result;
+ const char *e1_basename = (e1->basename != NULL) ? e1->basename : "";
+ const char *e2_basename = (e2->basename != NULL) ? e2->basename : "";
+
+ if (e1->type < e2->type)
+ {
+ result = -1;
+ }
+ else if (e1->type > e2->type)
+ {
+ result = 1;
+ }
+ else
+ {
+ result = mame_stricmp(e1_basename, e2_basename);
+ if (result == 0)
+ {
+ result = strcmp(e1_basename, e2_basename);
+ if (result == 0)
+ {
+ if (e1 < e2)
+ result = -1;
+ else if (e1 > e2)
+ result = 1;
+ }
+ }
+ }
+
+ return result;
+}
+
+
+
+/*-------------------------------------------------
+ append_file_selector_entry - appends a new
+ file selector entry to an entry list
+-------------------------------------------------*/
+
+static file_selector_entry *append_file_selector_entry(ui_menu *menu, file_selector_menu_state *menustate,
+ file_selector_entry_type entry_type, const char *entry_basename, const char *entry_fullpath)
+{
+ file_selector_entry *entry;
+ file_selector_entry **entryptr;
+
+ /* allocate a new entry */
+ entry = (file_selector_entry *) ui_menu_pool_alloc(menu, sizeof(*entry));
+ memset(entry, 0, sizeof(*entry));
+ entry->type = entry_type;
+ entry->basename = (entry_basename != NULL) ? ui_menu_pool_strdup(menu, entry_basename) : entry_basename;
+ entry->fullpath = (entry_fullpath != NULL) ? ui_menu_pool_strdup(menu, entry_fullpath) : entry_fullpath;
+
+ /* find the end of the list */
+ entryptr = &menustate->entrylist;
+ while ((*entryptr != NULL) && (compare_file_selector_entries(entry, *entryptr) >= 0))
+ entryptr = &(*entryptr)->next;
+
+ /* insert the entry */
+ entry->next = *entryptr;
+ *entryptr = entry;
+ return entry;
+}
+
+
+
+/*-------------------------------------------------
+ append_file_selector_entry_menu_item - appends
+ a menu item for a file selector entry
+-------------------------------------------------*/
+
+static file_selector_entry *append_dirent_file_selector_entry(ui_menu *menu, file_selector_menu_state *menustate,
+ const osd_directory_entry *dirent)
+{
+ astring *buffer;
+ file_selector_entry_type entry_type;
+ file_selector_entry *entry;
+
+ switch(dirent->type)
+ {
+ case ENTTYPE_FILE:
+ entry_type = SELECTOR_ENTRY_TYPE_FILE;
+ break;
+
+ case ENTTYPE_DIR:
+ entry_type = SELECTOR_ENTRY_TYPE_DIRECTORY;
+ break;
+
+ default:
+ /* exceptional case; do not add a menu item */
+ return NULL;
+ }
+
+ /* determine the full path */
+ buffer = zippath_combine(
+ astring_alloc(),
+ astring_c(menustate->manager_menustate->current_directory),
+ dirent->name);
+
+ /* create the file selector entry */
+ entry = append_file_selector_entry(
+ menu,
+ menustate,
+ entry_type,
+ dirent->name,
+ astring_c(buffer));
+
+ astring_free(buffer);
+ return entry;
+}
+
+
+
+/*-------------------------------------------------
+ append_file_selector_entry_menu_item - appends
+ a menu item for a file selector entry
+-------------------------------------------------*/
+
+static void append_file_selector_entry_menu_item(ui_menu *menu, const file_selector_entry *entry)
+{
+ const char *text = NULL;
+ const char *subtext = NULL;
+
+ switch(entry->type)
+ {
+ case SELECTOR_ENTRY_TYPE_EMPTY:
+ text = "[empty slot]";
+ break;
+
+ case SELECTOR_ENTRY_TYPE_CREATE:
+ text = "[create]";
+ break;
+
+ case SELECTOR_ENTRY_TYPE_DRIVE:
+ text = entry->basename;
+ subtext = "[DRIVE]";
+ break;
+
+ case SELECTOR_ENTRY_TYPE_DIRECTORY:
+ text = entry->basename;
+ subtext = "[DIR]";
+ break;
+
+ case SELECTOR_ENTRY_TYPE_FILE:
+ text = entry->basename;
+ subtext = "[FILE]";
+ break;
+ }
+ ui_menu_item_append(menu, text, subtext, 0, (void *) entry);
+}
+
+
+
+/*-------------------------------------------------
+ menu_file_selector_populate - creates and
+ allocates all menu items for a directory
+-------------------------------------------------*/
+
+static file_error menu_file_selector_populate(running_machine *machine, ui_menu *menu, file_selector_menu_state *menustate)
+{
+ zippath_directory *directory = NULL;
+ file_error err = FILERR_NONE;
+ const osd_directory_entry *dirent;
+ const file_selector_entry *entry;
+ const file_selector_entry *selected_entry = NULL;
+ int i;
+ const char *volume_name;
+ device_image_interface *device = menustate->manager_menustate->selected_device;
+ const char *path = astring_c(menustate->manager_menustate->current_directory);
+
+ /* open the directory */
+ err = zippath_opendir(path, &directory);
+ if (err != FILERR_NONE)
+ goto done;
+
+ /* clear out the menu entries */
+ menustate->entrylist = NULL;
+
+ /* add the "[empty slot]" entry */
+ append_file_selector_entry(menu, menustate, SELECTOR_ENTRY_TYPE_EMPTY, NULL, NULL);
+
+ if (device->image_config().is_creatable() && !zippath_is_zip(directory))
+ {
+ /* add the "[create]" entry */
+ append_file_selector_entry(menu, menustate, SELECTOR_ENTRY_TYPE_CREATE, NULL, NULL);
+ }
+
+ /* add the drives */
+ i = 0;
+ while((volume_name = osd_get_volume_name(i))!=NULL)
+ {
+ append_file_selector_entry(menu, menustate, SELECTOR_ENTRY_TYPE_DRIVE,
+ volume_name, volume_name);
+ i++;
+ }
+
+ /* build the menu for each item */
+ while((dirent = zippath_readdir(directory)) != NULL)
+ {
+ /* append a dirent entry */
+ entry = append_dirent_file_selector_entry(menu, menustate, dirent);
+
+ if (entry != NULL)
+ {
+ /* set the selected item to be the first non-parent directory or file */
+ if ((selected_entry == NULL) && strcmp(dirent->name, ".."))
+ selected_entry = entry;
+
+ /* do we have to select this file? */
+ if (!mame_stricmp(astring_c(menustate->manager_menustate->current_file), dirent->name))
+ selected_entry = entry;
+ }
+ }
+
+ /* append all of the menu entries */
+ for (entry = menustate->entrylist; entry != NULL; entry = entry->next)
+ append_file_selector_entry_menu_item(menu, entry);
+
+ /* set the selection (if we have one) */
+ if (selected_entry != NULL)
+ ui_menu_set_selection(menu, (void *) selected_entry);
+
+ /* set up custom render proc */
+ ui_menu_set_custom_render(menu, file_selector_render_extra, ui_get_line_height() + 3.0f * UI_BOX_TB_BORDER, 0);
+
+done:
+ if (directory != NULL)
+ zippath_closedir(directory);
+ return err;
+}
+
+
+
+/*-------------------------------------------------
+ check_path - performs a quick check to see if
+ a path exists
+-------------------------------------------------*/
+
+static file_error check_path(const char *path)
+{
+ return zippath_opendir(path, NULL);
+}
+
+
+
+/*-------------------------------------------------
+ menu_file_selector - file selector menu
+-------------------------------------------------*/
+
+static void menu_file_selector(running_machine *machine, ui_menu *menu, void *parameter, void *state)
+{
+ file_error err;
+ const ui_menu_event *event;
+ ui_menu *child_menu;
+ file_selector_menu_state *menustate;
+ file_create_menu_state *child_menustate;
+ const file_selector_entry *entry;
+
+ /* get menu state */
+ menustate = (file_selector_menu_state *) state;
+
+ /* if the menu isn't built, populate now */
+ if (!ui_menu_populated(menu))
+ {
+ err = menu_file_selector_populate(machine, menu, menustate);
+
+ /* pop out if there was an error */
+ if (err != FILERR_NONE)
+ {
+ ui_menu_stack_pop(machine);
+ return;
+ }
+ }
+
+ /* process the menu */
+ event = ui_menu_process(machine, menu, 0);
+ if (event != NULL && event->itemref != NULL)
+ {
+ /* handle selections */
+ if (event->iptkey == IPT_UI_SELECT)
+ {
+ entry = (const file_selector_entry *) event->itemref;
+ switch(entry->type)
+ {
+ case SELECTOR_ENTRY_TYPE_EMPTY:
+ /* empty slot - unload */
+ menustate->manager_menustate->selected_device->unload();
+ ui_menu_stack_pop(machine);
+ break;
+
+ case SELECTOR_ENTRY_TYPE_CREATE:
+ /* create */
+ child_menu = ui_menu_alloc(machine, render_container_get_ui(), menu_file_create, NULL);
+ child_menustate = (file_create_menu_state*)ui_menu_alloc_state(child_menu, sizeof(*child_menustate), NULL);
+ child_menustate->manager_menustate = menustate->manager_menustate;
+ ui_menu_stack_push(child_menu);
+ break;
+
+ case SELECTOR_ENTRY_TYPE_DRIVE:
+ case SELECTOR_ENTRY_TYPE_DIRECTORY:
+ /* drive/directory - first check the path */
+ err = check_path(entry->fullpath);
+ if (err != FILERR_NONE)
+ {
+ /* this path is problematic; present the user with an error and bail */
+ ui_popup_time(1, "Error accessing %s", entry->fullpath);
+ break;
+ }
+ astring_cpyc(menustate->manager_menustate->current_directory, entry->fullpath);
+ ui_menu_reset(menu, (ui_menu_reset_options)0);
+ break;
+
+ case SELECTOR_ENTRY_TYPE_FILE:
+ /* file */
+ menustate->manager_menustate->selected_device->load(entry->fullpath);
+ ui_menu_stack_pop(machine);
+ break;
+ }
+ }
+ }
+}
+
+
+
+/***************************************************************************
+ FILE MANAGER
+***************************************************************************/
+
+/*-------------------------------------------------
+ fix_working_directory - checks the working
+ directory for this device to ensure that it
+ "makes sense"
+-------------------------------------------------*/
+
+static void fix_working_directory(device_image_interface *image)
+{
+ /* if the image exists, set the working directory to the parent directory */
+ if (image->exists())
+ {
+ astring *astr = astring_alloc();
+ zippath_parent(astr, image->filename());
+ image->set_working_directory(astring_c(astr));
+ astring_free(astr);
+ }
+
+ /* check to see if the path exists; if not clear it */
+ if (check_path(image->working_directory()) != FILERR_NONE)
+ image->set_working_directory(NULL);
+}
+
+
+
+/*-------------------------------------------------
+ file_manager_render_extra - perform our
+ special rendering
+-------------------------------------------------*/
+
+static void file_manager_render_extra(running_machine *machine, ui_menu *menu, void *state, void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2)
+{
+ file_manager_menu_state *menustate = (file_manager_menu_state *) state;
+ const char *path;
+
+ /* access the path */
+ path = (menustate->selected_device != NULL) ? menustate->selected_device->filename() : NULL;
+ extra_text_render(machine, menu, state, selectedref, top, bottom,
+ origx1, origy1, origx2, origy2, NULL, path);
+}
+
+
+
+/*-------------------------------------------------
+ menu_file_manager_populate - populates the main
+ file manager menu
+-------------------------------------------------*/
+
+static void menu_file_manager_populate(running_machine *machine, ui_menu *menu, void *state)
+{
+ char buffer[2048];
+ device_image_interface *image = NULL;
+ const char *entry_basename;
+
+ /* cycle through all devices for this system */
+ for (bool gotone = machine->devicelist.first(image); gotone; gotone = image->next(image))
+ {
+ /* get the image type/id */
+ snprintf(buffer, ARRAY_LENGTH(buffer),
+ "%s",
+ image->image_config().name());
+
+ /* get the base name */
+ entry_basename = image->basename();
+
+ /* record the menu item */
+ ui_menu_item_append(menu, buffer, (entry_basename != NULL) ? entry_basename : "---", 0, (void *) image);
+ }
+
+ /* set up custom render proc */
+ ui_menu_set_custom_render(menu, file_manager_render_extra, 0, ui_get_line_height() + 3.0f * UI_BOX_TB_BORDER);
+}
+
+
+
+/*-------------------------------------------------
+ file_manager_destroy_state - state destructor
+-------------------------------------------------*/
+
+static void file_manager_destroy_state(ui_menu *menu, void *state)
+{
+ file_manager_menu_state *menustate = (file_manager_menu_state *) state;
+
+ if (menustate->current_directory != NULL)
+ astring_free(menustate->current_directory);
+
+ if (menustate->current_file != NULL)
+ astring_free(menustate->current_file);
+}
+
+
+
+/*-------------------------------------------------
+ menu_file_manager - main file manager menu
+-------------------------------------------------*/
+
+void ui_image_menu_file_manager(running_machine *machine, ui_menu *menu, void *parameter, void *state)
+{
+ const ui_menu_event *event;
+ file_manager_menu_state *menustate;
+ ui_menu *child_menu;
+ file_selector_menu_state *child_menustate;
+
+ /* if no state, allocate now */
+ if (state == NULL)
+ {
+ state = ui_menu_alloc_state(menu, sizeof(*menustate), file_manager_destroy_state);
+ menustate = (file_manager_menu_state *) state;
+
+ menustate->current_directory = astring_alloc();
+ menustate->current_file = astring_alloc();
+ }
+ menustate = (file_manager_menu_state *) state;
+
+ /* if the menu isn't built, populate now */
+ if (!ui_menu_populated(menu))
+ menu_file_manager_populate(machine, menu, state);
+
+ /* update the selected device */
+ menustate->selected_device = (device_image_interface *) ui_menu_get_selection(menu);
+
+ /* process the menu */
+ event = ui_menu_process(machine, menu, 0);
+ if (event != NULL && event->iptkey == IPT_UI_SELECT)
+ {
+ menustate->selected_device = (device_image_interface *) event->itemref;
+ if (menustate->selected_device != NULL)
+ {
+ /* ensure that the working directory for this device exists */
+ fix_working_directory(menustate->selected_device);
+
+ /* set up current_directory and current_file - depends on whether we have an image */
+ astring_cpyc(menustate->current_directory, menustate->selected_device->working_directory());
+ astring_cpyc(menustate->current_file, menustate->selected_device->exists() ? menustate->selected_device->basename() : "");
+
+ /* reset the existing menu */
+ ui_menu_reset(menu, UI_MENU_RESET_REMEMBER_POSITION);
+
+ /* push the menu */
+ child_menu = ui_menu_alloc(machine, render_container_get_ui(), menu_file_selector, NULL);
+ child_menustate = (file_selector_menu_state *)ui_menu_alloc_state(child_menu, sizeof(*child_menustate), NULL);
+ child_menustate->manager_menustate = menustate;
+ ui_menu_stack_push(child_menu);
+ }
+ }
+}
+
+/*-------------------------------------------------
+ image_info_astring - populate an allocated
+ string with the image info text
+-------------------------------------------------*/
+
+static astring *image_info_astring(running_machine *machine, astring *string)
+{
+ device_image_interface *image = NULL;
+
+ astring_printf(string, "%s\n\n", machine->gamedrv->description);
+
+#if 0
+ if (mess_ram_size > 0)
+ {
+ char buf2[RAM_STRING_BUFLEN];
+ astring_catprintf(string, "RAM: %s\n\n", ram_string(buf2, mess_ram_size));
+ }
+#endif
+
+ for (bool gotone = machine->devicelist.first(image); gotone; gotone = image->next(image))
+ {
+ const char *name = image->filename();
+ if (name != NULL)
+ {
+ const char *base_filename;
+ const char *info;
+ char *base_filename_noextension;
+
+ base_filename = image->basename();
+ base_filename_noextension = strip_extension(base_filename);
+
+ /* display device type and filename */
+ astring_catprintf(string, "%s: %s\n", image->image_config().name(), base_filename);
+
+ /* display long filename, if present and doesn't correspond to name */
+ info = image->longname();
+ if (info && (!base_filename_noextension || mame_stricmp(info, base_filename_noextension)))
+ astring_catprintf(string, "%s\n", info);
+
+ /* display manufacturer, if available */
+ info = image->manufacturer();
+ if (info != NULL)
+ {
+ astring_catprintf(string, "%s", info);
+ info = stripspace(image->year());
+ if (info && *info)
+ astring_catprintf(string, ", %s", info);
+ astring_catprintf(string,"\n");
+ }
+
+ /* display playable information, if available */
+ info = image->playable();
+ if (info != NULL)
+ astring_catprintf(string, "%s\n", info);
+
+ if (base_filename_noextension != NULL)
+ free(base_filename_noextension);
+ }
+ else
+ {
+ astring_catprintf(string, "%s: ---\n", image->image_config().name());
+ }
+ }
+ return string;
+}
+
+
+
+/*-------------------------------------------------
+ ui_image_menu_image_info - menu that shows info
+ on all loaded images
+-------------------------------------------------*/
+
+void ui_image_menu_image_info(running_machine *machine, ui_menu *menu, void *parameter, void *state)
+{
+ /* if the menu isn't built, populate now */
+ if (!ui_menu_populated(menu))
+ {
+ astring *tempstring = image_info_astring(machine, astring_alloc());
+ ui_menu_item_append(menu, astring_c(tempstring), NULL, MENU_FLAG_MULTILINE, NULL);
+ astring_free(tempstring);
+ }
+
+ /* process the menu */
+ ui_menu_process(machine, menu, 0);
+}
diff --git a/src/emu/uiimage.h b/src/emu/uiimage.h
new file mode 100644
index 00000000000..25c08636058
--- /dev/null
+++ b/src/emu/uiimage.h
@@ -0,0 +1,27 @@
+/***************************************************************************
+
+ uiimage.h
+
+ Internal MAME user interface image.
+
+ Copyright Nicola Salmoria and the MAME Team.
+ Visit http://mamedev.org for licensing and usage restrictions.
+
+***************************************************************************/
+
+#pragma once
+
+#ifndef __UIIMAGE_H__
+#define __UIIMAGE_H__
+
+
+
+/***************************************************************************
+ FUNCTION PROTOTYPES
+***************************************************************************/
+
+void ui_image_menu_file_manager(running_machine *machine, ui_menu *menu, void *parameter, void *state);
+
+void ui_image_menu_image_info(running_machine *machine, ui_menu *menu, void *parameter, void *state);
+
+#endif /* __UIIMAGE_H__ */
diff --git a/src/emu/uimenu.c b/src/emu/uimenu.c
index c100f89b367..57d31a0dec8 100644
--- a/src/emu/uimenu.c
+++ b/src/emu/uimenu.c
@@ -14,6 +14,7 @@
#include "ui.h"
#include "rendutil.h"
#include "cheat.h"
+#include "uiimage.h"
#include "uiinput.h"
#include "uimenu.h"
#include "audit.h"
@@ -1530,11 +1531,23 @@ static void menu_main_populate(running_machine *machine, ui_menu *menu, void *st
/* add game info menu */
ui_menu_item_append(menu, CAPSTARTGAMENOUN " Information", NULL, 0, (void *)menu_game_info);
-#ifdef MESS
- /* add MESS-specific menus */
- ui_mess_main_menu_populate(machine, menu);
-#endif /* MESS */
+ device_image_interface *image = NULL;
+ if (machine->devicelist.first(image))
+ {
+ /* add image info menu */
+ ui_menu_item_append(menu, "Image Information", NULL, 0, (void*)ui_image_menu_image_info);
+ /* add file manager menu */
+ ui_menu_item_append(menu, "File Manager", NULL, 0, (void*)ui_image_menu_file_manager);
+
+ /* add software menu */
+ ui_menu_item_append(menu, "Software", NULL, 0, (void*)ui_image_menu_software);
+
+ #ifdef MESS
+ /* add MESS-specific menus */
+ ui_mess_main_menu_populate(machine, menu);
+ #endif /* MESS */
+ }
/* add keyboard mode menu */
if (input_machine_has_keyboard(machine) && inputx_can_post(machine))
ui_menu_item_append(menu, "Keyboard Mode", NULL, 0, (void *)ui_menu_keyboard_mode);