summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/uiimage.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/emu/uiimage.c')
-rw-r--r--src/emu/uiimage.c1130
1 files changed, 1130 insertions, 0 deletions
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);
+}