// license:BSD-3-Clause// copyright-holders:Miodrag Milanovic/*************************************************************************** diimage.cpp Device image interfaces.***************************************************************************/#include"emu.h"#include"emuopts.h"#include"drivenum.h"#include"romload.h"#include"ui/uimain.h"#include"zippath.h"#include"softlist.h"#include"softlist_dev.h"#include"formats/ioprocs.h"#include<cstring>#include<regex>//**************************************************************************// DEVICE CONFIG IMAGE INTERFACE//**************************************************************************constimage_device_type_infodevice_image_interface::m_device_info_array[]={{IO_UNKNOWN,"unknown","unkn"},/* 0 */{IO_CARTSLOT,"cartridge","cart"},/* 1 */{IO_FLOPPY,"floppydisk","flop"},/* 2 */{IO_HARDDISK,"harddisk","hard"},/* 3 */{IO_CYLINDER,"cylinder","cyln"},/* 4 */{IO_CASSETTE,"cassette","cass"},/* 5 */{IO_PUNCHCARD,"punchcard","pcrd"},/* 6 */{IO_PUNCHTAPE,"punchtape","ptap"},/* 7 */{IO_PRINTER,"printout","prin"},/* 8 */{IO_SERIAL,"serial","serl"},/* 9 */{IO_PARALLEL,"parallel","parl"},/* 10 */{IO_SNAPSHOT,"snapshot","dump"},/* 11 */{IO_QUICKLOAD,"quickload","quik"},/* 12 */{IO_MEMCARD,"memcard","memc"},/* 13 */{IO_CDROM,"cdrom","cdrm"},/* 14 */{IO_MAGTAPE,"magtape","magt"},/* 15 */{IO_ROM,"romimage","rom"},/* 16 */{IO_MIDIIN,"midiin","min"},/* 17 */{IO_MIDIOUT,"midiout","mout"},/* 18 */{IO_PICTURE,"picture","pic"},/* 19 */{IO_VIDEO,"vidfile","vid"}/* 20 */};//**************************************************************************// IMAGE DEVICE FORMAT//**************************************************************************//-------------------------------------------------// ctor//-------------------------------------------------image_device_format::image_device_format(conststd::string&name,conststd::string&description,conststd::string&extensions,conststd::string&optspec):m_name(name),m_description(description),m_optspec(optspec){std::regexcomma_regex("\\,");std::copy(std::sregex_token_iterator(extensions.begin(),extensions.end(),comma_regex,-1),std::sregex_token_iterator(),std::back_inserter(m_extensions));}//-------------------------------------------------// dtor//-------------------------------------------------image_device_format::~image_device_format(){}//**************************************************************************// DEVICE IMAGE INTERFACE//**************************************************************************//-------------------------------------------------// device_image_interface - constructor//-------------------------------------------------device_image_interface::device_image_interface(constmachine_config&mconfig,device_t&device):device_interface(device,"image"),m_err(),m_file(),m_mame_file(),m_software_part_ptr(nullptr),m_supported(0),m_readonly(false),m_created(false),m_create_format(0),m_create_args(nullptr),m_user_loadable(true),m_is_loading(false),m_is_reset_and_loading(false){}//-------------------------------------------------// ~device_image_interface - destructor//-------------------------------------------------device_image_interface::~device_image_interface(){}//-------------------------------------------------// interface_config_complete - perform any// operations now that the configuration is// complete//-------------------------------------------------voiddevice_image_interface::interface_config_complete(){// set brief and instance nameupdate_names();}//-------------------------------------------------// find_device_type - search trough list of// device types to extract data//-------------------------------------------------constimage_device_type_info*device_image_interface::find_device_type(iodevice_ttype){inti;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];}returnnullptr;}//-------------------------------------------------// device_typename - retrieves device type name//-------------------------------------------------constchar*device_image_interface::device_typename(iodevice_ttype){constimage_device_type_info*info=find_device_type(type);return(info!=nullptr)?info->m_name:"unknown";}//-------------------------------------------------// device_brieftypename - retrieves device// brief type name//-------------------------------------------------constchar*device_image_interface::device_brieftypename(iodevice_ttype){constimage_device_type_info*info=find_device_type(type);return(info!=nullptr)?info->m_shortname:"unk";}//-------------------------------------------------// device_typeid - retrieves device type id//-------------------------------------------------iodevice_tdevice_image_interface::device_typeid(constchar*name){inti;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))returnm_device_info_array[i].m_type;}return(iodevice_t)-1;}//-------------------------------------------------// set_image_filename - specifies the filename of// an image//-------------------------------------------------voiddevice_image_interface::set_image_filename(conststd::string&filename){m_image_name=filename;util::zippath_parent(m_working_directory,filename);m_basename.assign(m_image_name);// find the last "path separator"autoiter=std::find_if(m_image_name.rbegin(),m_image_name.rend(),[](charc){return(c=='\\')||(c=='/')||(c==':');});if(iter!=m_image_name.rend())m_basename.assign(iter.base(),m_image_name.end());m_basename_noext=m_basename;autoloc=m_basename_noext.find_last_of('.');if(loc!=std::string::npos)m_basename_noext=m_basename_noext.substr(0,loc);m_filetype=core_filename_extract_extension(m_basename,true);}/**************************************************************************** CREATION FORMATS****************************************************************************///-------------------------------------------------// device_get_named_creatable_format -// accesses a specific image format available for// image creation by name//-------------------------------------------------constimage_device_format*device_image_interface::device_get_named_creatable_format(conststd::string&format_name)noexcept{for(auto&format:m_formatlist)if(format->name()==format_name)returnformat.get();returnnullptr;}//-------------------------------------------------// add_format//-------------------------------------------------voiddevice_image_interface::add_format(std::unique_ptr<image_device_format>&&format){m_formatlist.push_back(std::move(format));}//-------------------------------------------------// add_format//-------------------------------------------------voiddevice_image_interface::add_format(std::string&&name,std::string&&description,std::string&&extensions,std::string&&optspec){autoformat=std::make_unique<image_device_format>(std::move(name),std::move(description),std::move(extensions),std::move(optspec));add_format(std::move(format));}/**************************************************************************** ERROR HANDLING****************************************************************************///-------------------------------------------------// image_clear_error - clear out any specified// error//-------------------------------------------------voiddevice_image_interface::clear_error(){m_err=IMAGE_ERROR_SUCCESS;if(!m_err_message.empty()){m_err_message.clear();}}//-------------------------------------------------// error - returns the error text for an image// error//-------------------------------------------------staticconstchar*constmessages[]={"","Internal error","Unsupported operation","Out of memory","File not found","Invalid image","File already open","Unspecified error"};constchar*device_image_interface::error(){return(!m_err_message.empty())?m_err_message.c_str():messages[m_err];}//-------------------------------------------------// seterror - specifies an error on an image//-------------------------------------------------voiddevice_image_interface::seterror(image_error_terr,constchar*message){clear_error();m_err=err;if(message!=nullptr){m_err_message=message;}}//-------------------------------------------------// message - used to display a message while// loading//-------------------------------------------------voiddevice_image_interface::message(constchar*format,...){va_listargs;charbuffer[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//-------------------------------------------------booldevice_image_interface::try_change_working_directory(conststd::string&subdir){constosd::directory::entry*entry;boolsuccess=false;booldone=false;autodirectory=osd::directory::open(m_working_directory);if(directory){while(!done&&(entry=directory->read())!=nullptr){if(!core_stricmp(subdir.c_str(),entry->name)){done=true;success=entry->type==osd::directory::entry::entry_type::DIR;}}directory.reset();}// did we successfully identify the directory?if(success)m_working_directory=util::zippath_combine(m_working_directory,subdir);returnsuccess;}//-------------------------------------------------// setup_working_directory - sets up the working// directory according to a few defaults//-------------------------------------------------voiddevice_image_interface::setup_working_directory(){boolsuccess=false;// get user-specified directory and make sure it existsm_working_directory=device().mconfig().options().sw_path();// if multipath, get firstsize_ti=m_working_directory.find_first_of(';');if(i!=std::string::npos)m_working_directory.resize(i);// validate directoryif(!m_working_directory.empty())if(osd::directory::open(m_working_directory))success=true;// if not exist, use previous methodif(!success){// first set up the working directory to be the starting directoryosd_get_full_path(m_working_directory,".");// now try browsing down to "software"if(try_change_working_directory("software"))success=true;}if(success){// now down to a directory for this computerintgamedrv=driver_list::find(device().machine().system());while(gamedrv!=-1&&!try_change_working_directory(driver_list::driver(gamedrv).name)){gamedrv=driver_list::compatible_with(gamedrv);}}}//-------------------------------------------------// working_directory - returns the working// directory to use for this image; this is// valid even if not mounted//-------------------------------------------------conststd::string&device_image_interface::working_directory(){// check to see if we've never initialized the working directoryif(m_working_directory.empty())setup_working_directory();returnm_working_directory;}//-------------------------------------------------// software_entry - return a pointer to the// software_info structure from the softlist//-------------------------------------------------constsoftware_info*device_image_interface::software_entry()constnoexcept{return!m_software_part_ptr?nullptr:&m_software_part_ptr->info();}//-------------------------------------------------// get_software_region//-------------------------------------------------u8*device_image_interface::get_software_region(constchar*tag){if(!loaded_through_softlist())returnnullptr;std::stringfull_tag=util::string_format("%s:%s",device().tag(),tag);memory_region*region=device().machine().root_device().memregion(full_tag);returnregion!=nullptr?region->base():nullptr;}//-------------------------------------------------// image_get_software_region_length//-------------------------------------------------u32device_image_interface::get_software_region_length(constchar*tag){std::stringfull_tag=util::string_format("%s:%s",device().tag(),tag);memory_region*region=device().machine().root_device().memregion(full_tag);returnregion!=nullptr?region->bytes():0;}//-------------------------------------------------// image_get_feature//-------------------------------------------------constchar*device_image_interface::get_feature(constchar*feature_name)const{return!m_software_part_ptr?nullptr:m_software_part_ptr->feature(feature_name);}//-------------------------------------------------// load_software_region -//-------------------------------------------------booldevice_image_interface::load_software_region(constchar*tag,optional_shared_ptr<u8>&ptr){size_tsize=get_software_region_length(tag);if(size){ptr.allocate(size);memcpy(ptr,get_software_region(tag),size);}returnsize>0;}// ****************************************************************************// Hash info loading//// If the hash is not checked and the relevant info not loaded, force that info// to be loaded// ****************************************************************************booldevice_image_interface::run_hash(util::core_file&file,u32skip_bytes,util::hash_collection&hashes,constchar*types){// reset the hash; we want to override existing datahashes.reset();// figure out the size, and "cap" the skip bytesu64size=file.size();skip_bytes=(u32)std::min((u64)skip_bytes,size);// seek to the beginningfile.seek(skip_bytes,SEEK_SET);u64position=skip_bytes;// keep on reading hasheshashes.begin(types);while(position<size){uint8_tbuffer[8192];// read bytesconstu32count=(u32)std::min(size-position,(u64)sizeof(buffer));constu32actual_count=file.read(buffer,count);if(actual_count==0)returnfalse;position+=actual_count;// and compute the hasheshashes.buffer(buffer,actual_count);}hashes.end();// cleanupfile.seek(0,SEEK_SET);returntrue;}booldevice_image_interface::image_checkhash(){// only calculate CRC if it hasn't been calculated, and the open_mode is read onlyu32crcval;if(!m_hash.crc(crcval)&&is_readonly()&&!m_created){// do not cause a linear read of 600 megs please// TODO: use SHA1 in the CHD header as the hashif(image_type()==IO_CDROM)returntrue;// Skip calculating the hash when we have an image mounted through a software listif(loaded_through_softlist())returntrue;// run the hashif(!run_hash(*m_file,unhashed_header_length(),m_hash,util::hash_collection::HASH_TYPES_ALL))returnfalse;}returntrue;}util::hash_collectiondevice_image_interface::calculate_hash_on_file(util::core_file&file)const{// calculate the hashutil::hash_collectionhash;if(!run_hash(file,unhashed_header_length(),hash,util::hash_collection::HASH_TYPES_ALL))hash.reset();returnhash;}u32device_image_interface::crc(){u32crc=0;image_checkhash();m_hash.crc(crc);returncrc;}//-------------------------------------------------// support_command_line_image_creation - do we// want to support image creation from the front// end command line?//-------------------------------------------------booldevice_image_interface::support_command_line_image_creation()constnoexcept{boolresult;switch(image_type()){caseIO_PRINTER:caseIO_SERIAL:caseIO_PARALLEL:// going by the assumption that these device image types should support this// behavior; ideally we'd get rid of IO_* and just push this to the specific// devicesresult=true;break;default:result=false;break;}returnresult;}// ****************************************************************************// 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.//-------------------------------------------------voiddevice_image_interface::battery_load(void*buffer,intlength,intfill){if(!buffer||(length<=0))throwemu_fatalerror("device_image_interface::battery_load: Must specify sensical buffer/length");osd_file::errorfilerr;intbytes_read=0;std::stringfname=std::string(device().machine().system().name).append(PATH_SEPARATOR).append(m_basename_noext).append(".nv");/* try to open the battery file and read it in, if possible */emu_filefile(device().machine().options().nvram_directory(),OPEN_FLAG_READ);filerr=file.open(fname);if(filerr==osd_file::error::NONE)bytes_read=file.read(buffer,length);// fill remaining bytes (if necessary)memset(((char*)buffer)+bytes_read,fill,length-bytes_read);}voiddevice_image_interface::battery_load(void*buffer,intlength,constvoid*def_buffer){if(!buffer||(length<=0))throwemu_fatalerror("device_image_interface::battery_load: Must specify sensical buffer/length");osd_file::errorfilerr;intbytes_read=0;std::stringfname=std::string(device().machine().system().name).append(PATH_SEPARATOR).append(m_basename_noext).append(".nv");// try to open the battery file and read it in, if possibleemu_filefile(device().machine().options().nvram_directory(),OPEN_FLAG_READ);filerr=file.open(fname);if(filerr==osd_file::error::NONE)bytes_read=file.read(buffer,length);// if no file was present, copy the default contentsif(!bytes_read&&def_buffer)std::memcpy(buffer,def_buffer,length);}//-------------------------------------------------// battery_save - stores the battery// backed RAM for an image. The file name is// created from the machine driver name and the// image name.//-------------------------------------------------voiddevice_image_interface::battery_save(constvoid*buffer,intlength){if(!buffer||(length<=0))throwemu_fatalerror("device_image_interface::battery_save: Must specify sensical buffer/length");if(!device().machine().options().nvram_save())return;std::stringfname=std::string(device().machine().system().name).append(PATH_SEPARATOR).append(m_basename_noext).append(".nv");// try to open the battery file and write it out, if possibleemu_filefile(device().machine().options().nvram_directory(),OPEN_FLAG_WRITE|OPEN_FLAG_CREATE|OPEN_FLAG_CREATE_PATHS);osd_file::errorfilerr=file.open(fname);if(filerr==osd_file::error::NONE)file.write(buffer,length);}//-------------------------------------------------// uses_file_extension - update configuration// based on completed device setup//-------------------------------------------------booldevice_image_interface::uses_file_extension(constchar*file_extension)const{boolresult=false;if(file_extension[0]=='.')file_extension++;/* find the extensions */std::stringextensions(file_extensions());char*ext=strtok((char*)extensions.c_str(),",");while(ext!=nullptr){if(!core_stricmp(ext,file_extension)){result=true;break;}ext=strtok(nullptr,",");}returnresult;}// ***************************************************************************// IMAGE LOADING// ***************************************************************************//-------------------------------------------------// is_loaded - quick check to determine whether an// image is loaded//-------------------------------------------------booldevice_image_interface::is_loaded(){return(m_file!=nullptr);}//-------------------------------------------------// image_error_from_file_error - converts an image// error to a file error//-------------------------------------------------image_error_tdevice_image_interface::image_error_from_file_error(osd_file::errorfilerr){switch(filerr){caseosd_file::error::NONE:returnIMAGE_ERROR_SUCCESS;caseosd_file::error::NOT_FOUND:caseosd_file::error::ACCESS_DENIED:// file not found (or otherwise cannot open)returnIMAGE_ERROR_FILENOTFOUND;caseosd_file::error::OUT_OF_MEMORY:// out of memoryreturnIMAGE_ERROR_OUTOFMEMORY;caseosd_file::error::ALREADY_OPEN:// this shouldn't happenreturnIMAGE_ERROR_ALREADYOPEN;caseosd_file::error::FAILURE:caseosd_file::error::TOO_MANY_FILES:caseosd_file::error::INVALID_DATA:default:// other errorsreturnIMAGE_ERROR_INTERNAL;}}//-------------------------------------------------// load_image_by_path - loads an image with a// specific path//-------------------------------------------------image_error_tdevice_image_interface::load_image_by_path(u32open_flags,conststd::string&path){std::stringrevised_path;// attempt to read the fileautoconstfilerr=util::zippath_fopen(path,open_flags,m_file,revised_path);if(filerr!=osd_file::error::NONE)returnimage_error_from_file_error(filerr);m_readonly=(open_flags&OPEN_FLAG_WRITE)?0:1;m_created=(open_flags&OPEN_FLAG_CREATE)?1:0;set_image_filename(revised_path);returnIMAGE_ERROR_SUCCESS;}//-------------------------------------------------// reopen_for_write//-------------------------------------------------intdevice_image_interface::reopen_for_write(conststd::string&path){m_file.reset();std::stringrevised_path;// attempt to open the file for writingautoconstfilerr=util::zippath_fopen(path,OPEN_FLAG_READ|OPEN_FLAG_WRITE|OPEN_FLAG_CREATE,m_file,revised_path);if(filerr!=osd_file::error::NONE)returnimage_error_from_file_error(filerr);// success!m_readonly=0;m_created=1;set_image_filename(revised_path);returnIMAGE_ERROR_SUCCESS;}//-------------------------------------------------// determine_open_plan - determines which open// flags to use, and in what order//-------------------------------------------------std::vector<u32>device_image_interface::determine_open_plan(boolis_create){std::vector<u32>open_plan;// emit flags into a vectorif(!is_create&&is_readable()&&is_writeable())open_plan.push_back(OPEN_FLAG_READ|OPEN_FLAG_WRITE);if(!is_create&&!is_readable()&&is_writeable())open_plan.push_back(OPEN_FLAG_WRITE);if(!is_create&&is_readable())open_plan.push_back(OPEN_FLAG_READ);if(is_create&&is_writeable()&&is_creatable())open_plan.push_back(OPEN_FLAG_READ|OPEN_FLAG_WRITE|OPEN_FLAG_CREATE);returnopen_plan;}//-------------------------------------------------// verify_length_and_hash - verify the length// and hash signatures of a file//-------------------------------------------------staticintverify_length_and_hash(emu_file*file,constchar*name,u32explength,constutil::hash_collection&hashes){intretval=0;if(!file)return0;// verify lengthu32actlength=file->size();if(explength!=actlength){osd_printf_error("%s WRONG LENGTH (expected: %d found: %d)\n",name,explength,actlength);retval++;}util::hash_collection&acthashes=file->hashes(hashes.hash_types().c_str());if(hashes.flag(util::hash_collection::FLAG_NO_DUMP)){// If there is no good dump known, write itosd_printf_error("%s NO GOOD DUMP KNOWN\n",name);}elseif(hashes!=acthashes){// otherwise, it's just badosd_printf_error("%s WRONG CHECKSUMS:\n",name);osd_printf_error(" EXPECTED: %s\n",hashes.macro_string());osd_printf_error(" FOUND: %s\n",acthashes.macro_string());retval++;}elseif(hashes.flag(util::hash_collection::FLAG_BAD_DUMP)){// If it matches, but it is actually a bad dump, write itosd_printf_error("%s NEEDS REDUMP\n",name);}returnretval;}//-------------------------------------------------// load_software - software image loading//-------------------------------------------------booldevice_image_interface::load_software(software_list_device&swlist,constchar*swname,constrom_entry*start){boolretval=false;intwarningcount=0;for(constrom_entry*region=start;region;region=rom_next_region(region)){// loop until we hit the end of this regionfor(constrom_entry*romp=region+1;!ROMENTRY_ISREGIONEND(romp);romp++){// handle filesif(ROMENTRY_ISFILE(romp)){constsoftware_info*constswinfo=swlist.find(swname);if(!swinfo)returnfalse;constu32supported=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());u32crc=0;constboolhas_crc=util::hash_collection(ROM_GETHASHDATA(romp)).crc(crc);std::vector<constsoftware_info*>parents;std::vector<std::string>searchpath=rom_load_manager::get_software_searchpath(swlist,*swinfo);// for historical reasons, add the search path for the software list device's ownerconstdevice_t*constlistowner=swlist.owner();if(listowner){std::vector<std::string>devsearch=listowner->searchpath();for(std::string&path:devsearch)searchpath.emplace_back(std::move(path));}// try to load the filem_mame_file.reset(newemu_file(device().machine().options().media_path(),searchpath,OPEN_FLAG_READ));m_mame_file->set_restrict_to_mediapath(1);osd_file::errorfilerr;if(has_crc)filerr=m_mame_file->open(ROM_GETNAME(romp),crc);elsefilerr=m_mame_file->open(ROM_GETNAME(romp));if(filerr!=osd_file::error::NONE)m_mame_file.reset();warningcount+=verify_length_and_hash(m_mame_file.get(),ROM_GETNAME(romp),ROM_GETLENGTH(romp),util::hash_collection(ROM_GETHASHDATA(romp)));if(filerr==osd_file::error::NONE)filerr=util::core_file::open_proxy(*m_mame_file,m_file);if(filerr==osd_file::error::NONE)retval=true;break;// load first item for start}}}if(warningcount>0)osd_printf_error("WARNING: the software item might not run correctly.\n");returnretval;}//-------------------------------------------------// load_internal - core image loading//-------------------------------------------------image_init_resultdevice_image_interface::load_internal(conststd::string&path,boolis_create,intcreate_format,util::option_resolution*create_args){// first unload the imageunload();// clear any possible error messagesclear_error();// we are now loadingm_is_loading=true;// record the filenameset_image_filename(path);if(core_opens_image_file()){// determine open planstd::vector<u32>open_plan=determine_open_plan(is_create);// attempt to open the file in various waysfor(autoiter=open_plan.cbegin();!m_file&&iter!=open_plan.cend();iter++){// open the filem_err=load_image_by_path(*iter,path);if(m_err&&(m_err!=IMAGE_ERROR_FILENOTFOUND))gotodone;}// did we fail to find the file?if(!is_loaded()){m_err=IMAGE_ERROR_FILENOTFOUND;gotodone;}}// call device load or createm_create_format=create_format;m_create_args=create_args;if(init_phase()==false){m_err=(finish_load()==image_init_result::PASS)?IMAGE_ERROR_SUCCESS:IMAGE_ERROR_INTERNAL;if(m_err)gotodone;}// success!done:if(m_err!=0){if(!init_phase()){if(device().machine().phase()==machine_phase::RUNNING)device().popmessage("Error: Unable to %s image '%s': %s",is_create?"create":"load",path,error());elseosd_printf_error("Error: Unable to %s image '%s': %s\n",is_create?"create":"load",path,error());}clear();}returnm_err?image_init_result::FAIL:image_init_result::PASS;}//-------------------------------------------------// load - load an image into MAME//-------------------------------------------------image_init_resultdevice_image_interface::load(conststd::string&path){// is this a reset on load item?if(is_reset_on_load()&&!init_phase()){reset_and_load(path);returnimage_init_result::PASS;}returnload_internal(path,false,0,nullptr);}//-------------------------------------------------// load_software - loads a softlist item by name//-------------------------------------------------image_init_resultdevice_image_interface::load_software(conststd::string&software_identifier){// Is this a software part that forces a reset and we're at runtime? If so, get this loaded through reset_and_loadif(is_reset_on_load()&&!init_phase()){reset_and_load(software_identifier);returnimage_init_result::PASS;}// Prepare to loadunload();clear_error();m_is_loading=true;// Check if there's a software list defined for this device and use that if we're not creating an imageboolsoftload=load_software_part(software_identifier);if(!softload){m_is_loading=false;returnimage_init_result::FAIL;}// set up softlist stuffm_full_software_name=m_software_part_ptr->info().shortname();// specify image name with softlist-derived namesm_image_name=m_full_software_name;m_basename=m_full_software_name;m_basename_noext=m_full_software_name;m_filetype=use_software_list_file_extension_for_filetype()&&m_mame_file!=nullptr?core_filename_extract_extension(m_mame_file->filename(),true):"";// Copy some image information when we have been loaded through a software listsoftware_info&swinfo=m_software_part_ptr->info();// sanitizeif(swinfo.longname().empty()||swinfo.publisher().empty()||swinfo.year().empty())fatalerror("Each entry in an XML list must have all of the following fields: description, publisher, year!\n");// storem_longname=swinfo.longname();m_manufacturer=swinfo.publisher();m_year=swinfo.year();// set file typestd::stringfilename=(m_mame_file!=nullptr)&&(m_mame_file->filename()!=nullptr)?m_mame_file->filename():"";m_filetype=core_filename_extract_extension(filename,true);// call finish_load if necessaryif(init_phase()==false&&(finish_load()!=image_init_result::PASS))returnimage_init_result::FAIL;returnimage_init_result::PASS;}//-------------------------------------------------// image_finish_load - special call - only use// from core//-------------------------------------------------image_init_resultdevice_image_interface::finish_load(){image_init_resulterr=image_init_result::PASS;if(m_is_loading){if(!image_checkhash()){m_err=IMAGE_ERROR_INVALIDIMAGE;err=image_init_result::FAIL;}if(err==image_init_result::PASS){if(m_created){err=call_create(m_create_format,m_create_args);if(err!=image_init_result::PASS){if(!m_err)m_err=IMAGE_ERROR_UNSPECIFIED;}}else{// using device loaderr=call_load();if(err!=image_init_result::PASS){if(!m_err)m_err=IMAGE_ERROR_UNSPECIFIED;}}}}m_is_loading=false;m_create_format=0;m_create_args=nullptr;returnerr;}//-------------------------------------------------// create - create a image//-------------------------------------------------image_init_resultdevice_image_interface::create(conststd::string&path){returncreate(path,nullptr,nullptr);}//-------------------------------------------------// create - create a image//-------------------------------------------------image_init_resultdevice_image_interface::create(conststd::string&path,constimage_device_format*create_format,util::option_resolution*create_args){intformat_index=0;intcnt=0;for(auto&format:m_formatlist){if(create_format==format.get()){format_index=cnt;break;}cnt++;}returnload_internal(path,true,format_index,create_args);}//-------------------------------------------------// reset_and_load - called internally when we try// to load an is_reset_on_load() item; will reset// the emulation and record this image to be loaded//-------------------------------------------------voiddevice_image_interface::reset_and_load(conststd::string&path){// first make sure the reset is scheduleddevice().machine().schedule_hard_reset();// and record the new loaddevice().machine().options().image_option(instance_name()).specify(path);// record that we're reset and loadingm_is_reset_and_loading=true;}//-------------------------------------------------// clear - clear all internal data pertaining// to an image//-------------------------------------------------voiddevice_image_interface::clear(){m_mame_file.reset();m_file.reset();m_image_name.clear();m_readonly=false;m_created=false;m_create_format=0;m_create_args=nullptr;m_longname.clear();m_manufacturer.clear();m_year.clear();m_basename.clear();m_basename_noext.clear();m_filetype.clear();m_full_software_name.clear();m_software_part_ptr=nullptr;m_software_list_name.clear();}//-------------------------------------------------// unload - main call to unload an image//-------------------------------------------------voiddevice_image_interface::unload(){if(is_loaded()||loaded_through_softlist()){call_unload();}clear();clear_error();}//-------------------------------------------------// create_option_guide//-------------------------------------------------OPTION_GUIDE_START(null_option_guide)OPTION_GUIDE_ENDconstutil::option_guide&device_image_interface::create_option_guide()const{returnnull_option_guide;}//-------------------------------------------------// update_names - update brief and instance names//-------------------------------------------------voiddevice_image_interface::update_names(){constchar*inst_name=custom_instance_name();constchar*brief_name=custom_brief_instance_name();if(inst_name==nullptr)inst_name=device_typename(image_type());if(brief_name==nullptr)brief_name=device_brieftypename(image_type());// count instances of the general image type, or device type if customintcount=0;intindex=-1;for(constdevice_image_interface&image:image_interface_iterator(device().mconfig().root_device())){if(this==&image)index=count;constchar*other_name=image.custom_instance_name();if(!other_name)other_name=device_typename(image.image_type());if(other_name==inst_name||!strcmp(other_name,inst_name))count++;}m_canonical_instance_name=string_format("%s%d",inst_name,index+1);if(count>1){m_instance_name=m_canonical_instance_name;m_brief_instance_name=string_format("%s%d",brief_name,index+1);}else{m_instance_name=inst_name;m_brief_instance_name=brief_name;}}//-------------------------------------------------// find_software_item//-------------------------------------------------constsoftware_part*device_image_interface::find_software_item(conststd::string&identifier,boolrestrict_to_interface,software_list_device**dev)const{// split full software name into software list name and short software namestd::stringlist_name,software_name,part_name;if(!software_name_parse(identifier,&list_name,&software_name,&part_name))returnnullptr;// determine interfaceconstchar*interface=restrict_to_interface?image_interface():nullptr;// find the software list if explicitly specifiedfor(software_list_device&swlistdev:software_list_device_iterator(device().mconfig().root_device())){if(list_name.empty()||(list_name==swlistdev.list_name())){constsoftware_info*info=swlistdev.find(software_name);if(info!=nullptr){constsoftware_part*part=info->find_part(part_name,interface);if(part!=nullptr){if(dev!=nullptr)*dev=&swlistdev;returnpart;}}}if(software_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_nameconstsoftware_info*info=swlistdev.find(part_name);if(info!=nullptr){constsoftware_part*part=info->find_part("",interface);if(part!=nullptr){if(dev!=nullptr)*dev=&swlistdev;returnpart;}}}}// if explicitly specified and not found, just error hereif(dev!=nullptr)*dev=nullptr;returnnullptr;}//-------------------------------------------------// get_software_list_loader//-------------------------------------------------constsoftware_list_loader&device_image_interface::get_software_list_loader()const{returnfalse_software_list_loader::instance();}//-------------------------------------------------// 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.//-------------------------------------------------booldevice_image_interface::load_software_part(conststd::string&identifier){// if no match has been found, we suggest similar shortnamessoftware_list_device*swlist;m_software_part_ptr=find_software_item(identifier,true,&swlist);if(m_software_part_ptr==nullptr){software_list_device::display_matches(device().machine().config(),image_interface(),identifier);returnfalse;}// Load the software partconstchar*swname=m_software_part_ptr->info().shortname().c_str();constrom_entry*start_entry=m_software_part_ptr->romdata().data();constsoftware_list_loader&loader=get_software_list_loader();boolresult=loader.load_software(*this,*swlist,swname,start_entry);// check compatibilityswitch(swlist->is_compatible(*m_software_part_ptr)){caseSOFTWARE_IS_COMPATIBLE:break;caseSOFTWARE_IS_INCOMPATIBLE:swlist->popmessage("WARNING! the set %s might not work on this system due to incompatible filter(s) '%s'\n",m_software_part_ptr->info().shortname(),swlist->filter());break;caseSOFTWARE_NOT_COMPATIBLE:swlist->popmessage("WARNING! the set %s might not work on this system due to missing filter(s) '%s'\n",m_software_part_ptr->info().shortname(),swlist->filter());break;}// check requirements and load those imagesconstchar*requirement=m_software_part_ptr->feature("requirement");if(requirement!=nullptr){constsoftware_part*req_swpart=find_software_item(requirement,false);if(req_swpart!=nullptr){device_image_interface*req_image=software_list_device::find_mountable_image(device().mconfig(),*req_swpart);if(req_image!=nullptr)req_image->load_software(requirement);}}m_software_list_name=swlist->list_name();returnresult;}//-------------------------------------------------// software_get_default_slot//-------------------------------------------------std::stringdevice_image_interface::software_get_default_slot(constchar*default_card_slot)const{std::stringresult;conststd::string&image_name(device().mconfig().options().image_option(instance_name()).value());if(!image_name.empty()){result.assign(default_card_slot);constsoftware_part*swpart=find_software_item(image_name,true);if(swpart!=nullptr){constchar*slot=swpart->feature("slot");if(slot!=nullptr)result.assign(slot);}}returnresult;}//-------------------------------------------------// init_phase//-------------------------------------------------booldevice_image_interface::init_phase()const{// diimage.cpp has quite a bit of logic that randomly decides to behave// differently at startup; this is an enc[r]apsulation of the "logic"// that switches these behaviorsreturn!device().has_running_machine()||device().machine().phase()==machine_phase::INIT;}//----------------------------------------------------------------------------staticintimage_fseek_thunk(void*file,s64offset,intwhence){device_image_interface*image=(device_image_interface*)file;returnimage->fseek(offset,whence);}staticsize_timage_fread_thunk(void*file,void*buffer,size_tlength){device_image_interface*image=(device_image_interface*)file;returnimage->fread(buffer,length);}staticsize_timage_fwrite_thunk(void*file,constvoid*buffer,size_tlength){device_image_interface*image=(device_image_interface*)file;returnimage->fwrite(buffer,length);}staticu64image_fsize_thunk(void*file){device_image_interface*image=(device_image_interface*)file;returnimage->length();}//----------------------------------------------------------------------------structio_procsimage_ioprocs={nullptr,image_fseek_thunk,image_fread_thunk,image_fwrite_thunk,image_fsize_thunk};