/*************************************************************************** diimage.c Device image interfaces. **************************************************************************** Copyright Miodrag Milanovic All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name 'MAME' nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #include "emu.h" #include "ui.h" #include "zippath.h" #include "uiimage.h" #include "uiswlist.h" //************************************************************************** // DEVICE CONFIG IMAGE INTERFACE //************************************************************************** const image_device_type_info device_image_interface::m_device_info_array[] = { { IO_CARTSLOT, "cartridge", "cart" }, /* 0 */ { IO_FLOPPY, "floppydisk", "flop" }, /* 1 */ { IO_HARDDISK, "harddisk", "hard" }, /* 2 */ { IO_CYLINDER, "cylinder", "cyln" }, /* 3 */ { IO_CASSETTE, "cassette", "cass" }, /* 4 */ { IO_PUNCHCARD, "punchcard", "pcrd" }, /* 5 */ { IO_PUNCHTAPE, "punchtape", "ptap" }, /* 6 */ { IO_PRINTER, "printer", "prin" }, /* 7 */ { IO_SERIAL, "serial", "serl" }, /* 8 */ { IO_PARALLEL, "parallel", "parl" }, /* 9 */ { IO_SNAPSHOT, "snapshot", "dump" }, /* 10 */ { IO_QUICKLOAD, "quickload", "quik" }, /* 11 */ { IO_MEMCARD, "memcard", "memc" }, /* 12 */ { IO_CDROM, "cdrom", "cdrm" }, /* 13 */ { IO_MAGTAPE, "magtape", "magt" }, /* 14 */ }; //************************************************************************** // DEVICE IMAGE INTERFACE //************************************************************************** //------------------------------------------------- // device_image_interface - constructor //------------------------------------------------- device_image_interface::device_image_interface(const machine_config &mconfig, device_t &device) : device_interface(device), m_file(NULL), m_mame_file(NULL), m_full_software_name(NULL), m_software_info_ptr(NULL), m_software_part_ptr(NULL), m_software_list_name(NULL), m_readonly(false), m_created(false), m_formatlist(NULL), m_is_loading(FALSE) { } //------------------------------------------------- // ~device_image_interface - destructor //------------------------------------------------- device_image_interface::~device_image_interface() { image_device_format **formatptr = &m_formatlist; /* free all entries */ while (*formatptr != NULL) { image_device_format *entry = *formatptr; *formatptr = entry->m_next; global_free(entry); } } //------------------------------------------------- // find_device_type - search trough list of // device types to extract data //------------------------------------------------- const image_device_type_info *device_image_interface::find_device_type(iodevice_t type) { int i; for (i = 0; i < ARRAY_LENGTH(device_image_interface::m_device_info_array); i++) { if (m_device_info_array[i].m_type == type) return &m_device_info_array[i]; } return NULL; } //------------------------------------------------- // device_typename - retrieves device type name //------------------------------------------------- const char *device_image_interface::device_typename(iodevice_t type) { const image_device_type_info *info = find_device_type(type); return (info != NULL) ? info->m_name : NULL; } //------------------------------------------------- // device_brieftypename - retrieves device // brief type name //------------------------------------------------- const char *device_image_interface::device_brieftypename(iodevice_t type) { const image_device_type_info *info = find_device_type(type); return (info != NULL) ? info->m_shortname : NULL; } //------------------------------------------------- // device_typeid - retrieves device type id //------------------------------------------------- iodevice_t device_image_interface::device_typeid(const char *name) { int i; for (i = 0; i < ARRAY_LENGTH(device_image_interface::m_device_info_array); i++) { if (!mame_stricmp(name, m_device_info_array[i].m_name) || !mame_stricmp(name, m_device_info_array[i].m_shortname)) return m_device_info_array[i].m_type; } return (iodevice_t)-1; } /*------------------------------------------------- device_compute_hash - compute a hash, using this device's partial hash if appropriate -------------------------------------------------*/ void device_image_interface::device_compute_hash(hash_collection &hashes, const void *data, size_t length, const char *types) const { /* retrieve the partial hash func */ device_image_partialhash_func partialhash = get_partial_hash(); /* compute the hash */ if (partialhash) partialhash(hashes, (const unsigned char*)data, length, types); else hashes.compute(reinterpret_cast(data), length, types); } /*------------------------------------------------- set_image_filename - specifies the filename of an image -------------------------------------------------*/ image_error_t device_image_interface::set_image_filename(const char *filename) { m_image_name = filename; zippath_parent(m_working_directory, filename); m_basename.cpy(m_image_name); int loc1 = m_image_name.rchr(0,'\\'); int loc2 = m_image_name.rchr(0,'/'); int loc3 = m_image_name.rchr(0,':'); int loc = MAX(loc1,MAX(loc2,loc3)); if (loc!=-1) { if (loc == loc3) { // temp workaround for softlists now that m_image_name contains the part name too (e.g. list:gamename:cart) astring tmpstr = astring(m_basename.substr(0,loc)); int tmploc = tmpstr.rchr(0,':'); m_basename = m_basename.substr(tmploc + 1,loc-tmploc); } else 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 ****************************************************************************/ /*------------------------------------------------- device_get_indexed_creatable_format - accesses a specific image format available for image creation by index -------------------------------------------------*/ const image_device_format *device_image_interface::device_get_indexed_creatable_format(int index) { const image_device_format *format = device_get_creatable_formats(); while(index-- && (format != NULL)) format = format->m_next; return format; } /*------------------------------------------------- device_get_named_creatable_format - accesses a specific image format available for image creation by name -------------------------------------------------*/ const image_device_format *device_image_interface::device_get_named_creatable_format(const char *format_name) { const image_device_format *format = device_get_creatable_formats(); while((format != NULL) && strcmp(format->m_name, format_name)) format = format->m_next; return format; } /**************************************************************************** ERROR HANDLING ****************************************************************************/ /*------------------------------------------------- image_clear_error - clear out any specified error -------------------------------------------------*/ void device_image_interface::clear_error() { m_err = IMAGE_ERROR_SUCCESS; if (m_err_message) { m_err_message.reset(); } } /*------------------------------------------------- error - returns the error text for an image error -------------------------------------------------*/ static const char *const messages[] = { "", "Internal error", "Unsupported operation", "Out of memory", "File not found", "Invalid image", "File already open", "Unspecified error" }; const char *device_image_interface::error() { return (m_err_message) ? m_err_message.cstr() : messages[m_err]; } /*------------------------------------------------- seterror - specifies an error on an image -------------------------------------------------*/ void device_image_interface::seterror(image_error_t err, const char *message) { clear_error(); m_err = err; if (message != NULL) { m_err_message = message; } } /*------------------------------------------------- message - used to display a message while loading -------------------------------------------------*/ void device_image_interface::message(const char *format, ...) { va_list args; char buffer[256]; /* format the message */ va_start(args, format); vsnprintf(buffer, ARRAY_LENGTH(buffer), format, args); va_end(args); /* display the popup for a standard amount of time */ ui_popup_time(5, "%s: %s", basename(), buffer); } /*************************************************************************** WORKING DIRECTORIES ***************************************************************************/ /*------------------------------------------------- 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; } /*------------------------------------------------- setup_working_directory - sets up the working directory according to a few defaults -------------------------------------------------*/ void device_image_interface::setup_working_directory() { 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 */ int gamedrv = driver_list::find(device().machine().system()); while(gamedrv != -1 && !try_change_working_directory(driver_list::driver(gamedrv).name)) { gamedrv = driver_list::compatible_with(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) setup_working_directory(); return m_working_directory; } /*------------------------------------------------- get_software_region -------------------------------------------------*/ UINT8 *device_image_interface::get_software_region(const char *tag) { char full_tag[256]; if ( m_software_info_ptr == NULL || m_software_part_ptr == NULL ) return NULL; sprintf( full_tag, "%s:%s", device().tag(), tag ); return device().machine().region( full_tag )->base(); } /*------------------------------------------------- image_get_software_region_length -------------------------------------------------*/ UINT32 device_image_interface::get_software_region_length(const char *tag) { char full_tag[256]; sprintf( full_tag, "%s:%s", device().tag(), tag ); return device().machine().region( full_tag )->bytes(); } /*------------------------------------------------- image_get_feature -------------------------------------------------*/ const char *device_image_interface::get_feature(const char *feature_name) { 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; } /**************************************************************************** Hash info loading If the hash is not checked and the relevant info not loaded, force that info to be loaded ****************************************************************************/ void device_image_interface::run_hash(void (*partialhash)(hash_collection &, const unsigned char *, unsigned long, const char *), hash_collection &hashes, const char *types) { UINT32 size; UINT8 *buf = NULL; hashes.reset(); size = (UINT32) length(); buf = (UINT8*)malloc(size); memset(buf,0,size); /* read the file */ fseek(0, SEEK_SET); fread(buf, size); if (partialhash) partialhash(hashes, buf, size, types); else hashes.compute(buf, size, types); /* cleanup */ free(buf); fseek(0, SEEK_SET); } void device_image_interface::image_checkhash() { device_image_partialhash_func partialhash; /* only calculate CRC if it hasn't been calculated, and the open_mode is read only */ UINT32 crcval; if (!m_hash.crc(crcval) && m_readonly && !m_created) { /* do not cause a linear read of 600 megs please */ /* TODO: use SHA1 in the CHD header as the hash */ if (image_type() == IO_CDROM) return; /* Skip calculating the hash when we have an image mounted through a software list */ if ( m_software_info_ptr ) return; /* retrieve the partial hash func */ partialhash = get_partial_hash(); run_hash(partialhash, m_hash, hash_collection::HASH_TYPES_ALL); } return; } UINT32 device_image_interface::crc() { UINT32 crc = 0; image_checkhash(); m_hash.crc(crc); return crc; } /**************************************************************************** Battery functions These functions provide transparent access to battery-backed RAM on an image; typically for cartridges. ****************************************************************************/ /*------------------------------------------------- battery_load - retrieves the battery backed RAM for an image. The file name is created from the machine driver name and the image name. -------------------------------------------------*/ void device_image_interface::battery_load(void *buffer, int length, int fill) { astring fname(device().machine().system().name, PATH_SEPARATOR, m_basename_noext, ".nv"); image_battery_load_by_name(device().machine().options(), fname, buffer, length, fill); } /*------------------------------------------------- battery_save - stores the battery backed RAM for an image. The file name is created from the machine driver name and the image name. -------------------------------------------------*/ void device_image_interface::battery_save(const void *buffer, int length) { astring fname(device().machine().system().name, PATH_SEPARATOR, m_basename_noext, ".nv"); image_battery_save_by_name(device().machine().options(), fname, buffer, length); } //------------------------------------------------- // uses_file_extension - update configuration // based on completed device setup //------------------------------------------------- bool device_image_interface::uses_file_extension(const char *file_extension) const { bool result = FALSE; if (file_extension[0] == '.') file_extension++; /* find the extensions */ astring extensions(file_extensions()); char *ext = strtok((char*)extensions.cstr(),","); while (ext != NULL) { if (!mame_stricmp(ext, file_extension)) { result = TRUE; break; } ext = strtok (NULL, ","); } return result; } /**************************************************************************** IMAGE LOADING ****************************************************************************/ /*------------------------------------------------- is_loaded - quick check to determine whether an image is loaded -------------------------------------------------*/ bool device_image_interface::is_loaded() { return (m_file != NULL); } /*------------------------------------------------- load_image_by_path - loads an image with a specific path -------------------------------------------------*/ image_error_t device_image_interface::load_image_by_path(UINT32 open_flags, const char *path) { file_error filerr = FILERR_NOT_FOUND; image_error_t err = IMAGE_ERROR_FILENOTFOUND; astring revised_path; /* attempt to read the file */ filerr = zippath_fopen(path, open_flags, m_file, revised_path); /* did the open succeed? */ switch(filerr) { case FILERR_NONE: /* success! */ m_readonly = (open_flags & OPEN_FLAG_WRITE) ? 0 : 1; m_created = (open_flags & OPEN_FLAG_CREATE) ? 1 : 0; err = IMAGE_ERROR_SUCCESS; break; case FILERR_NOT_FOUND: case FILERR_ACCESS_DENIED: /* file not found (or otherwise cannot open); continue */ err = IMAGE_ERROR_FILENOTFOUND; break; case FILERR_OUT_OF_MEMORY: /* out of memory */ err = IMAGE_ERROR_OUTOFMEMORY; break; case FILERR_ALREADY_OPEN: /* this shouldn't happen */ err = IMAGE_ERROR_ALREADYOPEN; break; case FILERR_FAILURE: case FILERR_TOO_MANY_FILES: case FILERR_INVALID_DATA: default: /* other errors */ err = IMAGE_ERROR_INTERNAL; break; } /* if successful, set the file name */ if (filerr == FILERR_NONE) set_image_filename(revised_path); return err; } int device_image_interface::reopen_for_write(const char *path) { if(m_file) core_fclose(m_file); file_error filerr = FILERR_NOT_FOUND; image_error_t err = IMAGE_ERROR_FILENOTFOUND; astring revised_path; /* attempt to open the file for writing*/ filerr = zippath_fopen(path, OPEN_FLAG_READ|OPEN_FLAG_WRITE|OPEN_FLAG_CREATE, m_file, revised_path); /* did the open succeed? */ switch(filerr) { case FILERR_NONE: /* success! */ m_readonly = 0; m_created = 1; err = IMAGE_ERROR_SUCCESS; break; case FILERR_NOT_FOUND: case FILERR_ACCESS_DENIED: /* file not found (or otherwise cannot open); continue */ err = IMAGE_ERROR_FILENOTFOUND; break; case FILERR_OUT_OF_MEMORY: /* out of memory */ err = IMAGE_ERROR_OUTOFMEMORY; break; case FILERR_ALREADY_OPEN: /* this shouldn't happen */ err = IMAGE_ERROR_ALREADYOPEN; break; case FILERR_FAILURE: case FILERR_TOO_MANY_FILES: case FILERR_INVALID_DATA: default: /* other errors */ err = IMAGE_ERROR_INTERNAL; break; } /* if successful, set the file name */ if (filerr == FILERR_NONE) set_image_filename(revised_path); return err; } /*------------------------------------------------- determine_open_plan - determines which open flags to use, and in what order -------------------------------------------------*/ void device_image_interface::determine_open_plan(int is_create, UINT32 *open_plan) { int i = 0; /* emit flags */ if (!is_create && is_readable() && is_writeable()) open_plan[i++] = OPEN_FLAG_READ | OPEN_FLAG_WRITE; if (!is_create && !is_readable() && is_writeable()) open_plan[i++] = OPEN_FLAG_WRITE; if (!is_create && is_readable()) open_plan[i++] = OPEN_FLAG_READ; if (is_writeable() && is_creatable()) open_plan[i++] = OPEN_FLAG_READ | OPEN_FLAG_WRITE | OPEN_FLAG_CREATE; open_plan[i] = 0; } /*------------------------------------------------- load_software - software image loading -------------------------------------------------*/ bool device_image_interface::load_software(char *swlist, char *swname, rom_entry *start) { astring locationtag, breakstr("%"); const rom_entry *region; astring regiontag; bool retVal = FALSE; for (region = start; region != NULL; region = rom_next_region(region)) { /* loop until we hit the end of this region */ const rom_entry *romp = region + 1; while (!ROMENTRY_ISREGIONEND(romp)) { /* handle files */ if (ROMENTRY_ISFILE(romp)) { file_error filerr = FILERR_NOT_FOUND; UINT32 crc = 0; bool has_crc = hash_collection(ROM_GETHASHDATA(romp)).crc(crc); // attempt reading up the chain through the parents and create a locationtag astring in the format // " swlist % clonename % parentname " // below, we have the code to split the elements and to create paths to load from software_list *software_list_ptr = software_list_open(device().machine().options(), swlist, FALSE, NULL); if (software_list_ptr) { for (software_info *swinfo = software_list_find(software_list_ptr, swname, NULL); swinfo != NULL; ) { { astring tmp(swinfo->shortname); locationtag.cat(tmp); locationtag.cat(breakstr); //printf("%s\n", locationtag.cstr()); } const char *parentname = software_get_clone(device().machine().options(), swlist, swinfo->shortname); if (parentname != NULL) swinfo = software_list_find(software_list_ptr, parentname, NULL); else swinfo = NULL; } // strip the final '%' locationtag.del(locationtag.len() - 1, 1); software_list_close(software_list_ptr); } if (software_get_support(device().machine().options(), swlist, swname) == SOFTWARE_SUPPORTED_PARTIAL) mame_printf_error("WARNING: support for software %s (in list %s) is only partial\n", swname, swlist); if (software_get_support(device().machine().options(), swlist, swname) == SOFTWARE_SUPPORTED_NO) mame_printf_error("WARNING: support for software %s (in list %s) is only preliminary\n", swname, swlist); // check if locationtag actually contains two locations separated by '%' // (i.e. check if we are dealing with a clone in softwarelist) astring tag2, tag3, tag4(locationtag), tag5; int separator = tag4.chr(0, '%'); if (separator != -1) { // we are loading a clone through softlists, split the setname from the parentname tag5.cpysubstr(tag4, separator + 1, tag4.len() - separator + 1); tag4.del(separator, tag4.len() - separator); } // prepare locations where we have to load from: list/parentname & list/clonename astring tag1(swlist); tag1.cat(PATH_SEPARATOR); tag2.cpy(tag1.cat(tag4)); tag1.cpy(swlist); tag1.cat(PATH_SEPARATOR); tag3.cpy(tag1.cat(tag5)); if (tag5.chr(0, '%') != -1) fatalerror("We do not support clones of clones!\n"); // try to load from the available location(s): // - if we are not using lists, we have regiontag only; // - if we are using lists, we have: list/clonename, list/parentname, clonename, parentname // try to load from list/setname if ((m_mame_file == NULL) && (tag2.cstr() != NULL)) filerr = common_process_file(device().machine().options(), tag2.cstr(), has_crc, crc, romp, &m_mame_file); // try to load from list/parentname if ((m_mame_file == NULL) && (tag3.cstr() != NULL)) filerr = common_process_file(device().machine().options(), tag3.cstr(), has_crc, crc, romp, &m_mame_file); // try to load from setname if ((m_mame_file == NULL) && (tag4.cstr() != NULL)) filerr = common_process_file(device().machine().options(), tag4.cstr(), has_crc, crc, romp, &m_mame_file); // try to load from parentname if ((m_mame_file == NULL) && (tag5.cstr() != NULL)) filerr = common_process_file(device().machine().options(), tag5.cstr(), has_crc, crc, romp, &m_mame_file); if (filerr == FILERR_NONE) { m_file = *m_mame_file; retVal = TRUE; } break; // load first item for start } romp++; /* something else; skip */ } } return retVal; } /*------------------------------------------------- load_internal - core image loading -------------------------------------------------*/ bool device_image_interface::load_internal(const char *path, bool is_create, int create_format, option_resolution *create_args) { UINT32 open_plan[4]; int i; bool softload = FALSE; m_from_swlist = FALSE; // if the path contains no period, we are using softlists, so we won't create an image astring pathstr(path); bool filename_has_period = (pathstr.rchr(0, '.') != -1) ? TRUE : FALSE; /* first unload the image */ unload(); /* clear any possible error messages */ clear_error(); /* we are now loading */ m_is_loading = TRUE; /* record the filename */ m_err = set_image_filename(path); if (m_err) goto done; /* Check if there's a software list defined for this device and use that if we're not creating an image */ if (!filename_has_period) { softload = load_software_part( device().machine().options(), this, path, &m_software_info_ptr, &m_software_part_ptr, &m_full_software_name, &m_software_list_name ); // if we had launched from softlist with a specified part, e.g. "shortname:part" // we would have recorded the wrong name, so record it again based on software_info if (m_software_info_ptr && m_full_software_name) m_err = set_image_filename(m_full_software_name); m_from_swlist = TRUE; } if (is_create || filename_has_period) { /* determine open plan */ determine_open_plan(is_create, open_plan); /* attempt to open the file in various ways */ for (i = 0; !m_file && open_plan[i]; i++) { /* open the file */ m_err = load_image_by_path(open_plan[i], path); if (m_err && (m_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() && !softload) { m_err = IMAGE_ERROR_FILENOTFOUND; goto done; } /* call device load or create */ m_create_format = create_format; m_create_args = create_args; if (m_init_phase==FALSE) { m_err = (image_error_t)finish_load(); if (m_err) goto done; } /* success! */ done: if (m_err!=0) { if (!m_init_phase) { if (device().machine().phase() == MACHINE_PHASE_RUNNING) popmessage("Error: Unable to %s image '%s': %s\n", is_create ? "create" : "load", path, error()); else mame_printf_error("Error: Unable to %s image '%s': %s", is_create ? "create" : "load", path, error()); } clear(); } else { /* do we need to reset the CPU? only schedule it if load/create is successful */ if (device().machine().time() > attotime::zero && is_reset_on_load()) device().machine().schedule_hard_reset(); else { if (!m_init_phase) { if (device().machine().phase() == MACHINE_PHASE_RUNNING) popmessage("Image '%s' was successfully %s.", path, is_create ? "created" : "loaded"); else mame_printf_info("Image '%s' was successfully %s.\n", path, is_create ? "created" : "loaded"); } } } return m_err ? IMAGE_INIT_FAIL : IMAGE_INIT_PASS; } /*------------------------------------------------- load - load an image into MESS -------------------------------------------------*/ bool device_image_interface::load(const char *path) { return load_internal(path, FALSE, 0, NULL); } /*------------------------------------------------- image_finish_load - special call - only use from core -------------------------------------------------*/ bool device_image_interface::finish_load() { bool err = IMAGE_INIT_PASS; if (m_is_loading) { image_checkhash(); if (m_from_swlist) call_display_info(); if (has_been_created() )// && get_legacy_fct(DEVINFO_FCT_IMAGE_CREATE) != NULL) { err = call_create(m_create_format, m_create_args); if (err) { if (!m_err) m_err = IMAGE_ERROR_UNSPECIFIED; } } else { /* using device load */ err = call_load(); if (err) { if (!m_err) m_err = IMAGE_ERROR_UNSPECIFIED; } } } m_is_loading = FALSE; m_create_format = 0; m_create_args = NULL; m_init_phase = FALSE; return err; } /*------------------------------------------------- create - create a image -------------------------------------------------*/ bool device_image_interface::create(const char *path, const image_device_format *create_format, option_resolution *create_args) { int format_index = (create_format != NULL) ? create_format->m_index : 0; return load_internal(path, TRUE, format_index, create_args); } /*------------------------------------------------- clear - clear all internal data pertaining to an image -------------------------------------------------*/ void device_image_interface::clear() { if (m_mame_file) { global_free(m_mame_file); m_mame_file = NULL; m_file = NULL; } else { if (m_file) { core_fclose(m_file); m_file = NULL; } } m_image_name.reset(); m_readonly = false; m_created = false; m_longname.reset(); m_manufacturer.reset(); m_year.reset(); m_basename.reset(); m_basename_noext.reset(); m_filetype.reset(); m_full_software_name = NULL; m_software_info_ptr = NULL; m_software_part_ptr = NULL; m_software_list_name = NULL; } /*------------------------------------------------- unload - main call to unload an image -------------------------------------------------*/ void device_image_interface::unload() { if (is_loaded() || m_software_info_ptr) { call_unload(); } clear(); clear_error(); } /*------------------------------------------------- update_names - update brief and instance names -------------------------------------------------*/ void device_image_interface::update_names() { image_interface_iterator iter(device().mconfig().root_device()); int count = 0; int index = -1; for (const device_image_interface *image = iter.first(); image != NULL; image = iter.next()) { if (this == image) index = count; if (image->image_type() == image_type()) count++; } if (count > 1) { m_instance_name.printf("%s%d", device_typename(image_type()), index + 1); m_brief_instance_name.printf("%s%d", device_brieftypename(image_type()), index + 1); } else { m_instance_name = device_typename(image_type()); m_brief_instance_name = device_brieftypename(image_type()); } } /*------------------------------------------------- get_selection_menu - create the menu stack for ui-level image selection -------------------------------------------------*/ ui_menu *device_image_interface::get_selection_menu(running_machine &machine, render_container *container) { return auto_alloc_clear(machine, ui_menu_control_device_image(machine, container, this)); } ui_menu_control_device_image::ui_menu_control_device_image(running_machine &machine, render_container *container, device_image_interface *_image) : ui_menu(machine, container) { image = _image; sld = 0; if (image->software_list_name()) { software_list_device_iterator iter(machine.config().root_device()); for (const software_list_device *swlist = iter.first(); swlist != NULL; swlist = iter.next()) { if (strcmp(swlist->list_name(),image->software_list_name())==0) sld = swlist; } } swi = image->software_entry(); swp = image->part_entry(); if(swi) { state = START_OTHER_PART; current_directory.cpy(image->working_directory()); } else { state = START_FILE; /* if the image exists, set the working directory to the parent directory */ if (image->exists()) { current_file.cpy(image->filename()); zippath_parent(current_directory, current_file); } else current_directory.cpy(image->working_directory()); /* check to see if the path exists; if not clear it */ if (zippath_opendir(current_directory, NULL) != FILERR_NONE) current_directory.reset(); } } ui_menu_control_device_image::~ui_menu_control_device_image() { } /*------------------------------------------------- create_new_image - creates a new disk image -------------------------------------------------*/ void ui_menu_control_device_image::test_create(bool &can_create, bool &need_confirm) { astring path; osd_directory_entry *entry; osd_dir_entry_type file_type; /* assemble the full path */ zippath_combine(path, current_directory, current_file); /* does a file or a directory exist at the path */ entry = osd_stat(path); file_type = (entry != NULL) ? entry->type : ENTTYPE_NONE; switch(file_type) { case ENTTYPE_NONE: /* no file/dir here - always create */ can_create = true; need_confirm = false; break; case ENTTYPE_FILE: /* a file exists here - ask for permission from the user */ can_create = true; need_confirm = true; break; case ENTTYPE_DIR: /* a directory exists here - we can't save over it */ ui_popup_time(5, "Cannot save over directory"); can_create = false; need_confirm = false; break; default: fatalerror("Unexpected"); can_create = false; need_confirm = false; break; } } void ui_menu_control_device_image::load_software_part() { astring temp_name(sld->list_name()); temp_name.cat(":"); temp_name.cat(swi->shortname); temp_name.cat(":"); temp_name.cat(swp->name); hook_load(temp_name, true); } void ui_menu_control_device_image::hook_load(astring name, bool softlist) { image->load(name); ui_menu::stack_pop(machine()); } void ui_menu_control_device_image::populate() { } void ui_menu_control_device_image::handle() { switch(state) { case START_FILE: { bool can_create = false; if(image->is_creatable()) { zippath_directory *directory = NULL; file_error err = zippath_opendir(current_directory, &directory); can_create = err == FILERR_NONE && !zippath_is_zip(directory); if(directory) zippath_closedir(directory); } submenu_result = -1; ui_menu::stack_push(auto_alloc_clear(machine(), ui_menu_file_selector(machine(), container, image, current_directory, current_file, true, true, can_create, &submenu_result))); state = SELECT_FILE; break; } case START_SOFTLIST: sld = 0; ui_menu::stack_push(auto_alloc_clear(machine(), ui_menu_software(machine(), container, image->image_interface(), &sld))); state = SELECT_SOFTLIST; break; case START_OTHER_PART: { submenu_result = -1; ui_menu::stack_push(auto_alloc_clear(machine(), ui_menu_software_parts(machine(), container, swi, swp->interface_, &swp, true, &submenu_result))); state = SELECT_OTHER_PART; break; } case SELECT_SOFTLIST: if(!sld) { ui_menu::stack_pop(machine()); break; } software_info_name = ""; ui_menu::stack_push(auto_alloc_clear(machine(), ui_menu_software_list(machine(), container, sld, image->image_interface(), software_info_name))); state = SELECT_PARTLIST; break; case SELECT_PARTLIST: swl = software_list_open(machine().options(), sld->list_name(), false, NULL); swi = software_list_find(swl, software_info_name, NULL); if(swinfo_has_multiple_parts(swi, image->image_interface())) { submenu_result = -1; swp = 0; ui_menu::stack_push(auto_alloc_clear(machine(), ui_menu_software_parts(machine(), container, swi, image->image_interface(), &swp, false, &submenu_result))); state = SELECT_ONE_PART; } else { swp = software_find_part(swi, NULL, NULL); load_software_part(); software_list_close(swl); ui_menu::stack_pop(machine()); } break; case SELECT_ONE_PART: switch(submenu_result) { case ui_menu_software_parts::T_ENTRY: { load_software_part(); software_list_close(swl); ui_menu::stack_pop(machine()); break; } case -1: // return to list software_list_close(swl); state = SELECT_SOFTLIST; break; } break; case SELECT_OTHER_PART: switch(submenu_result) { case ui_menu_software_parts::T_ENTRY: { load_software_part(); break; } case ui_menu_software_parts::T_FMGR: state = START_FILE; handle(); break; case -1: // return to system ui_menu::stack_pop(machine()); break; } break; case SELECT_FILE: switch(submenu_result) { case ui_menu_file_selector::R_EMPTY: image->unload(); ui_menu::stack_pop(machine()); break; case ui_menu_file_selector::R_FILE: hook_load(current_file, false); break; case ui_menu_file_selector::R_CREATE: ui_menu::stack_push(auto_alloc_clear(machine(), ui_menu_file_create(machine(), container, image, current_directory, current_file))); state = CREATE_FILE; break; case ui_menu_file_selector::R_SOFTLIST: state = START_SOFTLIST; handle(); break; case -1: // return to system ui_menu::stack_pop(machine()); break; } break; case CREATE_FILE: { bool can_create, need_confirm; test_create(can_create, need_confirm); if(can_create) { if(need_confirm) { ui_menu::stack_push(auto_alloc_clear(machine(), ui_menu_confirm_save_as(machine(), container, &create_confirmed))); state = CREATE_CONFIRM; } else { state = DO_CREATE; handle(); } } else { state = START_FILE; handle(); } break; } case CREATE_CONFIRM: { state = create_confirmed ? DO_CREATE : START_FILE; handle(); break; } case DO_CREATE: { astring path; zippath_combine(path, current_directory, current_file); int err = image->create(path, 0, NULL); if (err != 0) popmessage("Error: %s", image->error()); ui_menu::stack_pop(machine()); break; } } }