mame/src/emu/devimage.c

690 lines
22 KiB
C

/***************************************************************************
devimage.c
Legacy image device helpers.
****************************************************************************
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 "emuopts.h"
#include "devlegcy.h"
#include "hashfile.h"
#include "zippath.h"
//**************************************************************************
// LEGACY IMAGE DEVICE CONFIGURATION
//**************************************************************************
//-------------------------------------------------
// legacy_image_device_config_base - constructor
//-------------------------------------------------
legacy_image_device_config_base::legacy_image_device_config_base(const machine_config &mconfig, device_type type, const char *tag, const device_config *owner, UINT32 clock, device_get_config_func get_config)
: legacy_device_config_base(mconfig, type, tag, owner, clock, get_config),
device_config_image_interface(mconfig, *this),
m_create_option_guide(NULL),
m_formatlist(NULL)
{
}
//-------------------------------------------------
// device_config_complete - update configuration
// based on completed device setup
//-------------------------------------------------
void legacy_image_device_config_base::device_config_complete()
{
const device_config_image_interface *image = NULL;
int count = 0;
int index = -1;
image_device_format **formatptr;
image_device_format *format;
formatptr = &m_formatlist;
int cnt = 0;
m_type = static_cast<iodevice_t>(get_legacy_config_int(DEVINFO_INT_IMAGE_TYPE));
m_readable = get_legacy_config_int(DEVINFO_INT_IMAGE_READABLE)!=0;
m_writeable = get_legacy_config_int(DEVINFO_INT_IMAGE_WRITEABLE)!=0;
m_creatable = get_legacy_config_int(DEVINFO_INT_IMAGE_CREATABLE)!=0;
m_must_be_loaded = get_legacy_config_int(DEVINFO_INT_IMAGE_MUST_BE_LOADED)!=0;
m_reset_on_load = get_legacy_config_int(DEVINFO_INT_IMAGE_RESET_ON_LOAD)!=0;
m_has_partial_hash = get_legacy_config_int(DEVINFO_FCT_IMAGE_PARTIAL_HASH)!=0;
m_interface_name = get_legacy_config_string(DEVINFO_STR_IMAGE_INTERFACE);
m_file_extensions = get_legacy_config_string(DEVINFO_STR_IMAGE_FILE_EXTENSIONS);
m_create_option_guide = reinterpret_cast<const option_guide *>(get_legacy_config_ptr(DEVINFO_PTR_IMAGE_CREATE_OPTGUIDE));
int format_count = get_legacy_config_int(DEVINFO_INT_IMAGE_CREATE_OPTCOUNT);
for (int i = 0; i < format_count; i++)
{
// only add if creatable
if (get_legacy_config_string(DEVINFO_PTR_IMAGE_CREATE_OPTSPEC + i)) {
// allocate a new format
format = global_alloc_clear(image_device_format);
// populate it
format->m_index = cnt;
format->m_name = get_legacy_config_string(DEVINFO_STR_IMAGE_CREATE_OPTNAME + i);
format->m_description = get_legacy_config_string(DEVINFO_STR_IMAGE_CREATE_OPTDESC + i);
format->m_extensions = get_legacy_config_string(DEVINFO_STR_IMAGE_CREATE_OPTEXTS + i);
format->m_optspec = get_legacy_config_string(DEVINFO_PTR_IMAGE_CREATE_OPTSPEC + i);
// and append it to the list
*formatptr = format;
formatptr = &format->m_next;
cnt++;
}
}
for (bool gotone = device_config::m_machine_config.m_devicelist.first(image); gotone; gotone = image->next(image))
{
if (this == image)
index = count;
if (image->image_type_direct() == m_type)
count++;
}
if (count > 1) {
m_instance_name.printf("%s%d", device_typename(m_type), index + 1);
m_brief_instance_name.printf("%s%d", device_brieftypename(m_type), index + 1);
}
else
{
m_instance_name = device_typename(m_type);
m_brief_instance_name = device_brieftypename(m_type);
}
// Override in case of hardcoded values
if (strlen(get_legacy_config_string(DEVINFO_STR_IMAGE_INSTANCE_NAME))>0) {
m_instance_name = get_legacy_config_string(DEVINFO_STR_IMAGE_INSTANCE_NAME);
}
if (strlen(get_legacy_config_string(DEVINFO_STR_IMAGE_BRIEF_INSTANCE_NAME))>0) {
m_brief_instance_name = get_legacy_config_string(DEVINFO_STR_IMAGE_BRIEF_INSTANCE_NAME);
}
}
//-------------------------------------------------
// uses_file_extension - update configuration
// based on completed device setup
//-------------------------------------------------
bool legacy_image_device_config_base::uses_file_extension(const char *file_extension) const
{
bool result = FALSE;
if (file_extension[0] == '.')
file_extension++;
/* find the extensions */
astring extensions(m_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;
}
//-------------------------------------------------
// ~legacy_device_config_base - destructor
//-------------------------------------------------
legacy_image_device_config_base::~legacy_image_device_config_base()
{
image_device_format **formatptr = &m_formatlist;
/* free all entries */
while (*formatptr != NULL)
{
image_device_format *entry = *formatptr;
*formatptr = entry->m_next;
global_free(entry);
}
}
device_image_partialhash_func legacy_image_device_config_base::get_partial_hash() const
{
return reinterpret_cast<device_image_partialhash_func>(get_legacy_config_fct(DEVINFO_FCT_IMAGE_PARTIAL_HASH));
}
//**************************************************************************
// LIVE LEGACY IMAGE DEVICE
//**************************************************************************
//-------------------------------------------------
// legacy_image_device_base - constructor
//-------------------------------------------------
legacy_image_device_base::legacy_image_device_base(running_machine &machine, const device_config &config)
: legacy_device_base(machine, config),
device_image_interface(machine, config, *this),
m_is_loading(FALSE)
{
}
/****************************************************************************
IMAGE LOADING
****************************************************************************/
/*-------------------------------------------------
is_loaded - quick check to determine whether an
image is loaded
-------------------------------------------------*/
bool legacy_image_device_base::is_loaded()
{
return (m_file != NULL);
}
/*-------------------------------------------------
load_image_by_path - loads an image with a
specific path
-------------------------------------------------*/
image_error_t legacy_image_device_base::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_writeable = (open_flags & OPEN_FLAG_WRITE) ? 1 : 0;
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;
}
/*-------------------------------------------------
determine_open_plan - determines which open
flags to use, and in what order
-------------------------------------------------*/
void legacy_image_device_base::determine_open_plan(int is_create, UINT32 *open_plan)
{
int i = 0;
/* emit flags */
if (!is_create && m_image_config.is_readable() && m_image_config.is_writeable())
open_plan[i++] = OPEN_FLAG_READ | OPEN_FLAG_WRITE;
if (!is_create && !m_image_config.is_readable() && m_image_config.is_writeable())
open_plan[i++] = OPEN_FLAG_WRITE;
if (!is_create && m_image_config.is_readable())
open_plan[i++] = OPEN_FLAG_READ;
if (m_image_config.is_writeable() && m_image_config.is_creatable())
open_plan[i++] = OPEN_FLAG_READ | OPEN_FLAG_WRITE | OPEN_FLAG_CREATE;
open_plan[i] = 0;
}
/*-------------------------------------------------
load_software - software image loading
-------------------------------------------------*/
bool legacy_image_device_base::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))
{
UINT32 crc = 0;
UINT8 crcbytes[4];
file_error filerr = FILERR_NOT_FOUND;
bool has_crc = hash_data_extract_binary_checksum(ROM_GETHASHDATA(romp), HASH_CRC, crcbytes);
if (has_crc)
crc = (crcbytes[0] << 24) | (crcbytes[1] << 16) | (crcbytes[2] << 8) | crcbytes[3];
// 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(mame_options(), swlist, FALSE, NULL);
if (software_list_ptr)
{
for (software_info *swinfo = software_list_find(software_list_ptr, swname, NULL); swinfo != NULL; )
{
if (swinfo != NULL)
{
astring tmp(swinfo->shortname);
locationtag.cat(tmp);
locationtag.cat(breakstr);
//printf("%s\n", locationtag.cstr());
}
const char *parentname = software_get_clone(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(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(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(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(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(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(tag5.cstr(), has_crc, crc, romp, &m_mame_file);
if (filerr == FILERR_NONE)
{
m_file = mame_core_file(m_mame_file);
retVal = TRUE;
}
break; // load first item for start
}
romp++; /* something else; skip */
}
}
return retVal;
}
/*-------------------------------------------------
load_internal - core image loading
-------------------------------------------------*/
bool legacy_image_device_base::load_internal(const char *path, bool is_create, int create_format, option_resolution *create_args)
{
UINT32 open_plan[4];
int i;
bool softload = 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( this, path, &m_software_info_ptr, &m_software_part_ptr, &m_full_software_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->shortname != NULL)
m_err = set_image_filename(m_software_info_ptr->shortname);
}
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 (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 ((attotime_compare(timer_get_time(device().machine), attotime_zero) > 0) && m_image_config.is_reset_on_load())
device().machine->schedule_hard_reset();
else
{
if (!m_init_phase)
{
if (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 legacy_image_device_base::load(const char *path)
{
return load_internal(path, FALSE, 0, NULL);
}
/*-------------------------------------------------
image_finish_load - special call - only use
from core
-------------------------------------------------*/
bool legacy_image_device_base::finish_load()
{
bool err = IMAGE_INIT_PASS;
if (m_is_loading)
{
image_checkhash();
if (has_been_created() && m_config.get_legacy_config_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 legacy_image_device_base::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 legacy_image_device_base::clear()
{
if (m_mame_file)
{
mame_fclose(m_mame_file);
m_mame_file = NULL;
m_file = NULL;
} else {
if (m_file)
{
core_fclose(m_file);
m_file = NULL;
}
}
m_name.reset();
m_writeable = FALSE;
m_created = FALSE;
m_longname.reset();
m_manufacturer.reset();
m_year.reset();
m_playable.reset();
m_extrainfo.reset();
m_basename.reset();
m_basename_noext.reset();
m_filetype.reset();
m_full_software_name = NULL;
m_software_info_ptr = NULL;
m_software_part_ptr = NULL;
}
/*-------------------------------------------------
unload - main call to unload an image
-------------------------------------------------*/
void legacy_image_device_base::unload()
{
if (is_loaded()) {
call_unload();
}
clear();
clear_error();
}
int legacy_image_device_base::call_load()
{
device_image_load_func func = reinterpret_cast<device_image_load_func>(m_config.get_legacy_config_fct(DEVINFO_FCT_IMAGE_LOAD));
if (func) {
return (*func)(*this);
} else {
return FALSE;
}
}
bool legacy_image_device_base::call_softlist_load(char *swlist, char *swname, rom_entry *start_entry)
{
device_image_softlist_load_func func = reinterpret_cast<device_image_softlist_load_func>(m_config.get_legacy_config_fct(DEVINFO_FCT_IMAGE_SOFTLIST_LOAD));
if (func) {
return (*func)(*this,swlist,swname,start_entry);
} else {
return FALSE;
}
}
int legacy_image_device_base::call_create(int format_type, option_resolution *format_options)
{
device_image_create_func func = reinterpret_cast<device_image_create_func>(m_config.get_legacy_config_fct(DEVINFO_FCT_IMAGE_CREATE));
if (func) {
return (*func)(*this,format_type,format_options);
} else {
return FALSE;
}
}
void legacy_image_device_base::call_unload()
{
device_image_unload_func func = reinterpret_cast<device_image_unload_func>(m_config.get_legacy_config_fct(DEVINFO_FCT_IMAGE_UNLOAD));
if (func) (*func)(*this);
}
void legacy_image_device_base::call_display()
{
device_image_display_func func = reinterpret_cast<device_image_display_func>(m_config.get_legacy_config_fct(DEVINFO_FCT_IMAGE_DISPLAY));
if (func) (*func)(*this);
}
device_image_partialhash_func legacy_image_device_base::get_partial_hash()
{
return reinterpret_cast<device_image_partialhash_func>(m_config.get_legacy_config_fct(DEVINFO_FCT_IMAGE_PARTIAL_HASH));
}
void legacy_image_device_base::call_get_devices()
{
device_image_get_devices_func func = reinterpret_cast<device_image_get_devices_func>(m_config.get_legacy_config_fct(DEVINFO_FCT_IMAGE_GET_DEVICES));
if (func) (*func)(*this);
}
void *legacy_image_device_base::get_device_specific_call()
{
return (void*) m_config.get_legacy_config_fct(DEVINFO_FCT_DEVICE_SPECIFIC);
}