mame/src/emu/diimage.c

1346 lines
39 KiB
C

// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic
/***************************************************************************
diimage.c
Device image interfaces.
***************************************************************************/
#include "emu.h"
#include "emuopts.h"
#include "drivenum.h"
#include "ui/ui.h"
#include "zippath.h"
#include "ui/filesel.h"
#include "ui/swlist.h"
#include "ui/imgcntrl.h"
//**************************************************************************
// DEVICE CONFIG IMAGE INTERFACE
//**************************************************************************
const image_device_type_info device_image_interface::m_device_info_array[] =
{
{ IO_UNKNOWN, "unknown", "unkn" },
{ 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 */
{ IO_ROM, "romimage", "rom" }, /* 15 */
{ IO_MIDIIN, "midiin", "min" }, /* 16 */
{ IO_MIDIOUT, "midiout", "mout" } /* 17 */
};
//**************************************************************************
// DEVICE IMAGE INTERFACE
//**************************************************************************
//-------------------------------------------------
// device_image_interface - constructor
//-------------------------------------------------
device_image_interface::device_image_interface(const machine_config &mconfig, device_t &device)
: device_interface(device, "image"),
m_file(NULL),
m_mame_file(NULL),
m_software_info_ptr(NULL),
m_software_part_ptr(NULL),
m_readonly(false),
m_created(false),
m_is_loading(FALSE)
{
}
//-------------------------------------------------
// ~device_image_interface - destructor
//-------------------------------------------------
device_image_interface::~device_image_interface()
{
}
//-------------------------------------------------
// 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 (!core_stricmp(name, m_device_info_array[i].m_name) || !core_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<const UINT8 *>(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_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)
{
for (const image_device_format *format = m_formatlist.first(); format != NULL; format = format->next())
if (strcmp(format->name(), format_name) == 0)
return format;
return NULL;
}
/****************************************************************************
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.c_str() : 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 */
device().machine().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.c_str());
if (directory != NULL)
{
while(!done && (entry = osd_readdir(directory)) != NULL)
{
if (!core_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.c_str(), 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.c_str();
}
/*-------------------------------------------------
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().root_device().memregion( 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().root_device().memregion( full_tag )->bytes();
}
/*-------------------------------------------------
image_get_feature
-------------------------------------------------*/
const char *device_image_interface::get_feature(const char *feature_name)
{
return (m_software_part_ptr == NULL) ? NULL : m_software_part_ptr->feature(feature_name);
}
//-------------------------------------------------
// load_software_region -
//-------------------------------------------------
bool device_image_interface::load_software_region(const char *tag, optional_shared_ptr<UINT8> &ptr)
{
size_t size = get_software_region_length(tag);
if (size)
{
ptr.allocate(size);
memcpy(ptr, get_software_region(tag), size);
}
return size > 0;
}
/****************************************************************************
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;
dynamic_buffer buf;
hashes.reset();
size = (UINT32) length();
buf.resize_and_clear(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 */
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 = astring(device().machine().system().name).cat(PATH_SEPARATOR).cat(m_basename_noext.c_str()).cat(".nv");
image_battery_load_by_name(device().machine().options(), fname.c_str(), buffer, length, fill);
}
void device_image_interface::battery_load(void *buffer, int length, void *def_buffer)
{
astring fname = astring(device().machine().system().name).cat(PATH_SEPARATOR).cat(m_basename_noext.c_str()).cat(".nv");
image_battery_load_by_name(device().machine().options(), fname.c_str(), buffer, length, def_buffer);
}
/*-------------------------------------------------
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 = astring(device().machine().system().name).cat(PATH_SEPARATOR).cat(m_basename_noext.c_str()).cat(".nv");
image_battery_save_by_name(device().machine().options(), fname.c_str(), 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.c_str(),",");
while (ext != NULL)
{
if (!core_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.c_str());
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.c_str());
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;
}
/*-------------------------------------------------
dump_wrong_and_correct_checksums - dump an
error message containing the wrong and the
correct checksums for a given software item
-------------------------------------------------*/
static void dump_wrong_and_correct_checksums(const hash_collection &hashes, const hash_collection &acthashes)
{
astring tempstr;
osd_printf_error(" EXPECTED: %s\n", hashes.macro_string(tempstr));
osd_printf_error(" FOUND: %s\n", acthashes.macro_string(tempstr));
}
/*-------------------------------------------------
verify_length_and_hash - verify the length
and hash signatures of a file
-------------------------------------------------*/
static int verify_length_and_hash(emu_file *file, const char *name, UINT32 explength, const hash_collection &hashes)
{
int retVal = 0;
if (file==NULL) return 0;
/* verify length */
UINT32 actlength = file->size();
if (explength != actlength)
{
osd_printf_error("%s WRONG LENGTH (expected: %d found: %d)\n", name, explength, actlength);
retVal++;
}
/* If there is no good dump known, write it */
astring tempstr;
hash_collection &acthashes = file->hashes(hashes.hash_types(tempstr));
if (hashes.flag(hash_collection::FLAG_NO_DUMP))
{
osd_printf_error("%s NO GOOD DUMP KNOWN\n", name);
}
/* verify checksums */
else if (hashes != acthashes)
{
/* otherwise, it's just bad */
osd_printf_error("%s WRONG CHECKSUMS:\n", name);
dump_wrong_and_correct_checksums(hashes, acthashes);
retVal++;
}
/* If it matches, but it is actually a bad dump, write it */
else if (hashes.flag(hash_collection::FLAG_BAD_DUMP))
{
osd_printf_error("%s NEEDS REDUMP\n",name);
}
return retVal;
}
/*-------------------------------------------------
load_software - software image loading
-------------------------------------------------*/
bool device_image_interface::load_software(software_list_device &swlist, const char *swname, const rom_entry *start)
{
astring locationtag, breakstr("%");
const rom_entry *region;
astring regiontag;
bool retVal = FALSE;
int warningcount = 0;
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);
software_info *swinfo = swlist.find(swname);
if (swinfo == NULL)
return false;
UINT32 supported = swinfo->supported();
if (supported == SOFTWARE_SUPPORTED_PARTIAL)
osd_printf_error("WARNING: support for software %s (in list %s) is only partial\n", swname, swlist.list_name());
if (supported == SOFTWARE_SUPPORTED_NO)
osd_printf_error("WARNING: support for software %s (in list %s) is only preliminary\n", swname, swlist.list_name());
// 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
while (swinfo != NULL)
{
locationtag.cat(swinfo->shortname()).cat(breakstr);
const char *parentname = swinfo->parentname();
swinfo = (parentname != NULL) ? swlist.find(parentname) : NULL;
}
// strip the final '%'
locationtag.del(locationtag.len() - 1, 1);
// 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.list_name());
tag1.cat(PATH_SEPARATOR);
tag2.cpy(tag1.cat(tag4));
tag1.cpy(swlist.list_name());
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.c_str() != NULL))
filerr = common_process_file(device().machine().options(), tag2.c_str(), has_crc, crc, romp, &m_mame_file);
// try to load from list/parentname
if ((m_mame_file == NULL) && (tag3.c_str() != NULL))
filerr = common_process_file(device().machine().options(), tag3.c_str(), has_crc, crc, romp, &m_mame_file);
// try to load from setname
if ((m_mame_file == NULL) && (tag4.c_str() != NULL))
filerr = common_process_file(device().machine().options(), tag4.c_str(), has_crc, crc, romp, &m_mame_file);
// try to load from parentname
if ((m_mame_file == NULL) && (tag5.c_str() != NULL))
filerr = common_process_file(device().machine().options(), tag5.c_str(), has_crc, crc, romp, &m_mame_file);
warningcount += verify_length_and_hash(m_mame_file,ROM_GETNAME(romp),ROM_GETLENGTH(romp),hash_collection(ROM_GETHASHDATA(romp)));
if (filerr == FILERR_NONE)
{
m_file = *m_mame_file;
retVal = TRUE;
}
break; // load first item for start
}
romp++; /* something else; skip */
}
}
if (warningcount > 0)
{
osd_printf_error("WARNING: the software item might not run correctly.\n");
}
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, bool just_load)
{
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;
if (core_opens_image_file())
{
/* 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 && !just_load)
{
softload = load_software_part(path, m_software_part_ptr);
if (softload)
{
m_software_info_ptr = &m_software_part_ptr->info();
m_software_list_name.cpy(m_software_info_ptr->list().list_name());
m_full_software_name.cpy(m_software_part_ptr->info().shortname());
// 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.c_str());
// check if image should be read-only
const char *read_only = get_feature("read_only");
if (read_only && !strcmp(read_only, "true")) {
make_readonly();
}
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 )
{
// sanitize
if (m_software_info_ptr->longname() == NULL || m_software_info_ptr->publisher() == NULL || m_software_info_ptr->year() == NULL)
fatalerror("Each entry in an XML list must have all of the following fields: description, publisher, year!\n");
// store
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 (just_load) {
if(m_err) clear();
return m_err ? IMAGE_INIT_FAIL : IMAGE_INIT_PASS;
}
if (m_err!=0) {
if (!m_init_phase)
{
if (device().machine().phase() == MACHINE_PHASE_RUNNING)
popmessage("Error: Unable to %s image '%s': %s", is_create ? "create" : "load", path, error());
else
osd_printf_error("Error: Unable to %s image '%s': %s\n", 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
osd_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, FALSE);
}
/*-------------------------------------------------
open_image_file - opening plain image file
-------------------------------------------------*/
bool device_image_interface::open_image_file(emu_options &options)
{
const char* path = options.value(instance_name());
if (*path != 0)
{
set_init_phase();
if (load_internal(path, FALSE, 0, NULL, TRUE)==IMAGE_INIT_PASS)
{
if (software_entry()==NULL) return true;
}
}
return false;
}
/*-------------------------------------------------
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 (has_been_created())
{
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) ? m_formatlist.indexof(*create_format) : 0;
return load_internal(path, TRUE, format_index, create_args, FALSE);
}
/*-------------------------------------------------
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.reset();
m_software_info_ptr = NULL;
m_software_part_ptr = NULL;
m_software_list_name.reset();
}
/*-------------------------------------------------
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(const device_type device_type, const char *inst, const char *brief)
{
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() && device_type==NULL) || (device_type==image->device().type()))
count++;
}
const char *inst_name = (device_type!=NULL) ? inst : device_typename(image_type());
const char *brief_name = (device_type!=NULL) ? brief : device_brieftypename(image_type());
if (count > 1)
{
m_instance_name.printf("%s%d", inst_name , index + 1);
m_brief_instance_name.printf("%s%d", brief_name, index + 1);
}
else
{
m_instance_name = inst_name;
m_brief_instance_name = brief_name;
}
}
//-------------------------------------------------
// software_name_split - helper that splits a
// software_list:software:part string into
// separate 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
//
// Notice however that we could also have been
// passed a string swlist_name:swname, and thus
// some special check has to be performed in this
// case.
//-------------------------------------------------
void device_image_interface::software_name_split(const char *swlist_swname, astring &swlist_name, astring &swname, astring &swpart)
{
// reset all output parameters
swlist_name.reset();
swname.reset();
swpart.reset();
// if no colon, this is the swname by itself
const char *split1 = strchr(swlist_swname, ':');
if (split1 == NULL)
{
swname.cpy(swlist_swname);
return;
}
// if one colon, it is the swname and swpart alone
const char *split2 = strchr(split1 + 1, ':');
if (split2 == NULL)
{
swname.cpy(swlist_swname, split1 - swlist_swname);
swpart.cpy(split1 + 1);
return;
}
// if two colons present, split into 3 parts
swlist_name.cpy(swlist_swname, split1 - swlist_swname);
swname.cpy(split1 + 1, split2 - (split1 + 1));
swpart.cpy(split2 + 1);
}
software_part *device_image_interface::find_software_item(const char *path, bool restrict_to_interface)
{
// split full software name into software list name and short software name
astring swlist_name, swinfo_name, swpart_name;
software_name_split(path, swlist_name, swinfo_name, swpart_name);
// determine interface
const char *interface = NULL;
if (restrict_to_interface)
interface = image_interface();
// find the software list if explicitly specified
software_list_device_iterator deviter(device().mconfig().root_device());
for (software_list_device *swlistdev = deviter.first(); swlistdev != NULL; swlistdev = deviter.next())
{
if (swlist_name == swlistdev->list_name() || !(swlist_name.len() > 0))
{
software_info *info = swlistdev->find(swinfo_name.c_str());
if (info != NULL)
{
software_part *part = info->find_part(swpart_name.c_str(), interface);
if (part != NULL)
return part;
}
}
if (swinfo_name == swlistdev->list_name())
{
// ad hoc handling for the case path = swlist_name:swinfo_name (e.g.
// gameboy:sml) which is not handled properly by software_name_split
// since the function cannot distinguish between this and the case
// path = swinfo_name:swpart_name
software_info *info = swlistdev->find(swpart_name.c_str());
if (info != NULL)
{
software_part *part = info->find_part(NULL, interface);
if (part != NULL)
return part;
}
}
}
// if explicitly specified and not found, just error here
return NULL;
}
//-------------------------------------------------
// 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 device_image_interface::load_software_part(const char *path, software_part *&swpart)
{
// if no match has been found, we suggest similar shortnames
swpart = find_software_item(path, true);
if (swpart == NULL)
{
software_list_device::display_matches(device().machine().config(), image_interface(), path);
return false;
}
// Load the software part
bool result = call_softlist_load(swpart->info().list(), swpart->info().shortname(), swpart->romdata());
// Tell the world which part we actually loaded
astring full_sw_name;
full_sw_name.printf("%s:%s:%s", swpart->info().list().list_name(), swpart->info().shortname(), swpart->name());
// check compatibility
if (!swpart->is_compatible(swpart->info().list()))
osd_printf_warning("WARNING! the set %s might not work on this system due to missing filter(s) '%s'\n", swpart->info().shortname(), swpart->info().list().filter());
// check requirements and load those images
const char *requirement = swpart->feature("requirement");
if (requirement != NULL)
{
software_part *req_swpart = find_software_item(requirement, false);
if (req_swpart != NULL)
{
image_interface_iterator imgiter(device().machine().root_device());
for (device_image_interface *req_image = imgiter.first(); req_image != NULL; req_image = imgiter.next())
{
const char *interface = req_image->image_interface();
if (interface != NULL)
{
if (req_swpart->matches_interface(interface))
{
const char *option = device().mconfig().options().value(req_image->brief_instance_name());
// mount only if not already mounted
if (strlen(option) == 0 && !req_image->filename())
{
req_image->set_init_phase();
req_image->load(requirement);
}
break;
}
}
}
}
}
return result;
}
//-------------------------------------------------
// software_get_default_slot
//-------------------------------------------------
void device_image_interface::software_get_default_slot(astring &result, const char *default_card_slot)
{
const char *path = device().mconfig().options().value(instance_name());
result.reset();
if (strlen(path) > 0)
{
result.cpy(default_card_slot);
software_part *swpart = find_software_item(path, true);
if (swpart != NULL)
{
const char *slot = swpart->feature("slot");
if (slot != NULL)
result.cpy(slot);
}
}
}
/*-------------------------------------------------
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));
}