summaryrefslogtreecommitdiffstats
path: root/src/emu/romload.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/emu/romload.cpp')
-rw-r--r--src/emu/romload.cpp613
1 files changed, 287 insertions, 326 deletions
diff --git a/src/emu/romload.cpp b/src/emu/romload.cpp
index 19f8d6296dc..4018cdf923e 100644
--- a/src/emu/romload.cpp
+++ b/src/emu/romload.cpp
@@ -15,6 +15,9 @@
#include "softlist_dev.h"
#include "ui/uimain.h"
+#include <algorithm>
+#include <set>
+
#define LOG_LOAD 0
#define LOG(...) do { if (LOG_LOAD) debugload(__VA_ARGS__); } while(0)
@@ -30,28 +33,108 @@
HELPERS (also used by diimage.cpp)
***************************************************************************/
-static osd_file::error common_process_file(emu_options &options, const char *location, const char *ext, const rom_entry *romp, emu_file &image_file)
+namespace {
+
+rom_entry const *next_parent_system(game_driver const *&system, std::vector<rom_entry> &rom_entries)
{
- return (location && *location)
- ? image_file.open(util::string_format("%s" PATH_SEPARATOR "%s%s", location, ROM_GETNAME(romp), ext))
- : image_file.open(std::string(ROM_GETNAME(romp)) + ext);
+ if (!system)
+ return nullptr;
+ int const parent(driver_list::find(system->parent));
+ if (0 > parent)
+ {
+ system = nullptr;
+ return nullptr;
+ }
+ else
+ {
+ system = &driver_list::driver(parent);
+ rom_entries = rom_build_entries(system->rom);
+ return &rom_entries[0];
+ }
}
-std::unique_ptr<emu_file> common_process_file(emu_options &options, const char *location, bool has_crc, u32 crc, const rom_entry *romp, osd_file::error &filerr)
+
+chd_error do_open_disk(const emu_options &options, std::initializer_list<std::reference_wrapper<const std::vector<std::string> > > searchpath, const rom_entry *romp, chd_file &chd, std::function<const rom_entry * ()> next_parent)
{
- auto image_file = std::make_unique<emu_file>(options.media_path(), OPEN_FLAG_READ);
+ // hashes are fixed, but we might need to try multiple filenames
+ std::set<std::string> tried;
+ const util::hash_collection hashes(ROM_GETHASHDATA(romp));
+ std::string filename, fullpath;
+ const rom_entry *parent(nullptr);
+ chd_error result(CHDERR_FILE_NOT_FOUND);
+ while (romp && (CHDERR_NONE != result))
+ {
+ filename = ROM_GETNAME(romp);
+ filename.append(".chd");
+ if (tried.insert(filename).second)
+ {
+ // piggyback on emu_file to find the disk image file
+ std::unique_ptr<emu_file> imgfile;
+ for (const std::vector<std::string> &paths : searchpath)
+ {
+ imgfile.reset(new emu_file(options.media_path(), paths, OPEN_FLAG_READ));
+ imgfile->set_restrict_to_mediapath(1);
+ const osd_file::error filerr(imgfile->open(filename, OPEN_FLAG_READ));
+ if (osd_file::error::NONE == filerr)
+ break;
+ else
+ imgfile.reset();
+ }
- if (has_crc)
- filerr = image_file->open(location, PATH_SEPARATOR, ROM_GETNAME(romp), crc);
- else
- filerr = image_file->open(util::string_format("%s" PATH_SEPARATOR "%s", location, ROM_GETNAME(romp)));
+ // if we couldn't open a candidate file, report an error; otherwise reopen it as a CHD
+ if (imgfile)
+ {
+ fullpath = imgfile->fullpath();
+ imgfile.reset();
+ result = chd.open(fullpath.c_str());
+ }
+ }
- if (filerr != osd_file::error::NONE)
- image_file = nullptr;
+ // walk the parents looking for a CHD with the same hashes but a different name
+ if (CHDERR_NONE != result)
+ {
+ while (romp)
+ {
+ // find a file in a disk region
+ if (parent)
+ romp = rom_next_file(romp);
+ while (!parent || !romp)
+ {
+ if (!parent)
+ {
+ parent = next_parent();
+ if (!parent)
+ {
+ romp = nullptr;
+ break;
+ }
+ while (ROMENTRY_ISPARAMETER(parent) || ROMENTRY_ISSYSTEM_BIOS(parent) || ROMENTRY_ISDEFAULT_BIOS(parent))
+ ++parent;
+ if (ROMENTRY_ISEND(parent))
+ parent = nullptr;
+ }
+ else
+ {
+ parent = rom_next_region(parent);
+ }
+ while (parent && !ROMREGION_ISDISKDATA(parent))
+ parent = rom_next_region(parent);
+ if (parent)
+ romp = rom_first_file(parent);
+ }
- return image_file;
+ // try it if it matches the hashes
+ if (romp && (util::hash_collection(ROM_GETHASHDATA(romp)) == hashes))
+ break;
+ }
+ }
+ }
+ return result;
}
+} // anonymous namespace
+
+
/***************************************************************************
ROM LOADING
***************************************************************************/
@@ -506,7 +589,7 @@ void rom_load_manager::region_post_process(memory_region *region, bool invert)
up the parent and loading by checksum
-------------------------------------------------*/
-std::unique_ptr<emu_file> rom_load_manager::open_rom_file(const char *regiontag, const rom_entry *romp, std::vector<std::string> &tried_file_names, bool from_list)
+std::unique_ptr<emu_file> rom_load_manager::open_rom_file(std::initializer_list<std::reference_wrapper<const std::vector<std::string> > > searchpath, const rom_entry *romp, std::vector<std::string> &tried_file_names, bool from_list)
{
osd_file::error filerr = osd_file::error::NOT_FOUND;
u32 const romsize = rom_file_size(romp);
@@ -520,93 +603,13 @@ std::unique_ptr<emu_file> rom_load_manager::open_rom_file(const char *regiontag,
bool const has_crc = util::hash_collection(ROM_GETHASHDATA(romp)).crc(crc);
// attempt reading up the chain through the parents
- // It also automatically attempts any kind of load by checksum supported by the archives.
+ // it also automatically attempts any kind of load by checksum supported by the archives.
std::unique_ptr<emu_file> result;
- for (int drv = driver_list::find(machine().system()); !result && (0 <= drv); drv = driver_list::clone(drv))
- {
- tried_file_names.emplace_back(driver_list::driver(drv).name);
- result = common_process_file(machine().options(), driver_list::driver(drv).name, has_crc, crc, romp, filerr);
- }
-
- // if the region is load by name, load the ROM from there
- if (!result && regiontag)
+ for (const std::vector<std::string> &paths : searchpath)
{
- // check if we are dealing with softwarelists. if so, locationtag
- // is actually a concatenation of: listname + setname + parentname
- // separated by '%' (parentname being present only for clones)
- std::string tag1(regiontag), tag2, tag3, tag4, tag5;
- bool is_list = false;
- bool has_parent = false;
-
- int separator1 = tag1.find_first_of('%');
- if (separator1 != -1)
- {
- is_list = true;
-
- // we are loading through softlists, split the listname from the regiontag
- tag4.assign(tag1.substr(separator1 + 1, tag1.length() - separator1 + 1));
- tag1.erase(separator1, tag1.length() - separator1);
- tag1.append(PATH_SEPARATOR);
-
- // check if we are loading a clone (if this is the case also tag1 have a separator '%')
- int separator2 = tag4.find_first_of('%');
- if (separator2 != -1)
- {
- has_parent = true;
-
- // we are loading a clone through softlists, split the setname from the parentname
- tag5.assign(tag4.substr(separator2 + 1, tag4.length() - separator2 + 1));
- tag4.erase(separator2, tag4.length() - separator2);
- }
-
- // prepare locations where we have to load from: list/parentname & list/clonename
- std::string swlist(tag1);
- tag2.assign(swlist.append(tag4));
- if (has_parent)
- {
- swlist.assign(tag1);
- tag3.assign(swlist.append(tag5));
- }
- }
-
- if (tag5.find_first_of('%') != -1)
- throw emu_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
- if (!is_list)
- {
- tried_file_names.emplace_back(tag1);
- result = common_process_file(machine().options(), tag1.c_str(), has_crc, crc, romp, filerr);
- }
- else
- {
- // try to load from list/setname
- if (!result && (tag2.c_str() != nullptr)) // FIXME: std::string::c_str will never return nullptr
- {
- tried_file_names.emplace_back(tag2);
- result = common_process_file(machine().options(), tag2.c_str(), has_crc, crc, romp, filerr);
- }
- // try to load from list/parentname
- if (!result && has_parent && (tag3.c_str() != nullptr)) // FIXME: std::string::c_str will never return nullptr
- {
- tried_file_names.emplace_back(tag3);
- result = common_process_file(machine().options(), tag3.c_str(), has_crc, crc, romp, filerr);
- }
- // try to load from setname
- if (!result && (tag4.c_str() != nullptr)) // FIXME: std::string::c_str will never return nullptr
- {
- tried_file_names.emplace_back(tag4);
- result = common_process_file(machine().options(), tag4.c_str(), has_crc, crc, romp, filerr);
- }
- // try to load from parentname
- if (!result && has_parent && (tag5.c_str() != nullptr)) // FIXME: std::string::c_str will never return nullptr
- {
- tried_file_names.emplace_back(tag5);
- result = common_process_file(machine().options(), tag5.c_str(), has_crc, crc, romp, filerr);
- }
- }
+ result = open_rom_file(paths, tried_file_names, has_crc, crc, ROM_GETNAME(romp), filerr);
+ if (result)
+ break;
}
// update counters
@@ -621,6 +624,27 @@ std::unique_ptr<emu_file> rom_load_manager::open_rom_file(const char *regiontag,
}
+std::unique_ptr<emu_file> rom_load_manager::open_rom_file(const std::vector<std::string> &paths, std::vector<std::string> &tried, bool has_crc, u32 crc, const std::string &name, osd_file::error &filerr)
+{
+ // record the set names we search
+ tried.insert(tried.end(), paths.begin(), paths.end());
+
+ // attempt to open the file
+ std::unique_ptr<emu_file> result(new emu_file(machine().options().media_path(), paths, OPEN_FLAG_READ));
+ result->set_restrict_to_mediapath(1);
+ if (has_crc)
+ filerr = result->open(name, crc);
+ else
+ filerr = result->open(name);
+
+ // don't return anything if unsuccessful
+ if (osd_file::error::NONE != filerr)
+ return nullptr;
+ else
+ return result;
+}
+
+
/*-------------------------------------------------
rom_fread - cheesy fread that fills with
random data for a nullptr file
@@ -824,7 +848,7 @@ void rom_load_manager::copy_rom_data(const rom_entry *romp)
for a region
-------------------------------------------------*/
-void rom_load_manager::process_rom_entries(const device_t &device, const char *regiontag, const rom_entry *parent_region, const rom_entry *romp, bool from_list)
+void rom_load_manager::process_rom_entries(std::initializer_list<std::reference_wrapper<const std::vector<std::string> > > searchpath, u8 bios, const rom_entry *parent_region, const rom_entry *romp, bool from_list)
{
u32 lastflags = 0;
std::vector<std::string> tried_file_names;
@@ -845,7 +869,7 @@ void rom_load_manager::process_rom_entries(const device_t &device, const char *r
if (ROMENTRY_ISFILL(romp))
{
- if (!ROM_GETBIOSFLAGS(romp) || ROM_GETBIOSFLAGS(romp) == device.system_bios())
+ if (!ROM_GETBIOSFLAGS(romp) || (ROM_GETBIOSFLAGS(romp) == bios))
fill_rom_data(romp);
romp++;
@@ -857,7 +881,7 @@ void rom_load_manager::process_rom_entries(const device_t &device, const char *r
else if (ROMENTRY_ISFILE(romp))
{
// handle files
- bool const irrelevantbios = (ROM_GETBIOSFLAGS(romp) != 0) && (ROM_GETBIOSFLAGS(romp) != device.system_bios());
+ bool const irrelevantbios = (ROM_GETBIOSFLAGS(romp) != 0) && (ROM_GETBIOSFLAGS(romp) != bios);
rom_entry const *baserom = romp;
int explength = 0;
@@ -866,7 +890,7 @@ void rom_load_manager::process_rom_entries(const device_t &device, const char *r
std::unique_ptr<emu_file> file;
if (!irrelevantbios)
{
- file = open_rom_file(regiontag, romp, tried_file_names, from_list);
+ file = open_rom_file(searchpath, romp, tried_file_names, from_list);
if (!file)
handle_missing_file(romp, tried_file_names, CHDERR_NONE);
}
@@ -926,186 +950,12 @@ void rom_load_manager::process_rom_entries(const device_t &device, const char *r
/*-------------------------------------------------
- open_disk_image - open a disk image,
- searching up the parent and loading by
- checksum
--------------------------------------------------*/
-
-int open_disk_image(emu_options &options, const game_driver *gamedrv, const rom_entry *romp, chd_file &image_chd, const char *locationtag)
-{
- emu_file image_file(options.media_path(), OPEN_FLAG_READ);
- const rom_entry *region, *rom;
- osd_file::error filerr;
- chd_error err;
-
- /* attempt to open the properly named file, scanning up through parent directories */
- filerr = osd_file::error::NOT_FOUND;
- for (int searchdrv = driver_list::find(*gamedrv); searchdrv != -1 && filerr != osd_file::error::NONE; searchdrv = driver_list::clone(searchdrv))
- filerr = common_process_file(options, driver_list::driver(searchdrv).name, ".chd", romp, image_file);
-
- if (filerr != osd_file::error::NONE)
- filerr = common_process_file(options, nullptr, ".chd", romp, image_file);
-
- /* look for the disk in the locationtag too */
- if (filerr != osd_file::error::NONE && locationtag != nullptr)
- {
- // check if we are dealing with softwarelists. if so, locationtag
- // is actually a concatenation of: listname + setname + parentname
- // separated by '%' (parentname being present only for clones)
- std::string tag1(locationtag), tag2, tag3, tag4, tag5;
- bool is_list = false;
- bool has_parent = false;
-
- int separator1 = tag1.find_first_of('%');
- if (separator1 != -1)
- {
- is_list = true;
-
- // we are loading through softlists, split the listname from the regiontag
- tag4.assign(tag1.substr(separator1 + 1, tag1.length() - separator1 + 1));
- tag1.erase(separator1, tag1.length() - separator1);
- tag1.append(PATH_SEPARATOR);
-
- // check if we are loading a clone (if this is the case also tag1 have a separator '%')
- int separator2 = tag4.find_first_of('%');
- if (separator2 != -1)
- {
- has_parent = true;
-
- // we are loading a clone through softlists, split the setname from the parentname
- tag5.assign(tag4.substr(separator2 + 1, tag4.length() - separator2 + 1));
- tag4.erase(separator2, tag4.length() - separator2);
- }
-
- // prepare locations where we have to load from: list/parentname (if any) & list/clonename
- std::string swlist(tag1);
- tag2.assign(swlist.append(tag4));
- if (has_parent)
- {
- swlist.assign(tag1);
- tag3.assign(swlist.append(tag5));
- }
- }
-
- if (tag5.find_first_of('%') != -1)
- throw emu_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 locationtag only;
- // - if we are using lists, we have: list/clonename, list/parentname, clonename, parentname
- if (!is_list)
- filerr = common_process_file(options, locationtag, ".chd", romp, image_file);
- else
- {
- // try to load from list/setname
- if ((filerr != osd_file::error::NONE) && (tag2.c_str() != nullptr))
- filerr = common_process_file(options, tag2.c_str(), ".chd", romp, image_file);
- // try to load from list/parentname (if any)
- if ((filerr != osd_file::error::NONE) && has_parent && (tag3.c_str() != nullptr))
- filerr = common_process_file(options, tag3.c_str(), ".chd", romp, image_file);
- // try to load from setname
- if ((filerr != osd_file::error::NONE) && (tag4.c_str() != nullptr))
- filerr = common_process_file(options, tag4.c_str(), ".chd", romp, image_file);
- // try to load from parentname (if any)
- if ((filerr != osd_file::error::NONE) && has_parent && (tag5.c_str() != nullptr))
- filerr = common_process_file(options, tag5.c_str(), ".chd", romp, image_file);
- // only for CHD we also try to load from list/
- if ((filerr != osd_file::error::NONE) && (tag1.c_str() != nullptr))
- {
- tag1.erase(tag1.length() - 1, 1); // remove the PATH_SEPARATOR
- filerr = common_process_file(options, tag1.c_str(), ".chd", romp, image_file);
- }
- }
- }
-
- /* did the file open succeed? */
- if (filerr == osd_file::error::NONE)
- {
- std::string fullpath(image_file.fullpath());
- image_file.close();
-
- /* try to open the CHD */
- err = image_chd.open(fullpath.c_str());
- if (err == CHDERR_NONE)
- return err;
- }
- else
- err = CHDERR_FILE_NOT_FOUND;
-
- // Otherwise, look at our parents for a CHD with an identical checksum
- // and try to open that
- //
- // An example of a system that requires this is src/mame/drivers/ksys673.cpp, that has declarations like this:
- // ...
- // DISK_IMAGE_READONLY("889aa", 0, BAD_DUMP SHA1(0b567bf2f03ee8089e0b021ea502a53b3f6fe7ac))
- // ...
- // DISK_IMAGE_READONLY("889ea", 0, BAD_DUMP SHA1(0b567bf2f03ee8089e0b021ea502a53b3f6fe7ac))
- // ...
- // DISK_IMAGE_READONLY("889ja", 0, BAD_DUMP SHA1(0b567bf2f03ee8089e0b021ea502a53b3f6fe7ac))
- // ...
- // DISK_IMAGE_READONLY("889ua", 0, BAD_DUMP SHA1(0b567bf2f03ee8089e0b021ea502a53b3f6fe7ac))
- // ...
- util::hash_collection romphashes(ROM_GETHASHDATA(romp));
- for (int drv = driver_list::find(*gamedrv); drv != -1; drv = driver_list::clone(drv))
- {
- const game_driver &current_driver(driver_list::driver(drv));
-
- // Create a single use emu_option structure for the purposes of this lookup, just
- // carrying forward the options that are necessary for CHD lookup. This is because the
- // options passed to us may have slot/image configurations that are "poisonous" for these
- // other drivers
- //
- // A side effect of this approach is that the "dragnet" to find CHDs with identical hashes
- // will only find CHDs for the default configuration. I believe that this in practice will
- // be acceptable.
- emu_options driver_specific_options;
- driver_specific_options.set_system_name(current_driver.name);
- driver_specific_options.set_value(OPTION_MEDIAPATH, options.media_path(), OPTION_PRIORITY_DEFAULT);
- driver_specific_options.set_value(OPTION_DIFF_DIRECTORY, options.diff_directory(), OPTION_PRIORITY_DEFAULT);
-
- // Now that we have an emu_options structure properly set up, we can create a machine_config
- machine_config config(current_driver, driver_specific_options);
-
- for (device_t &device : device_iterator(config.root_device()))
- for (region = rom_first_region(device); region != nullptr; region = rom_next_region(region))
- if (ROMREGION_ISDISKDATA(region))
- for (rom = rom_first_file(region); rom != nullptr; rom = rom_next_file(rom))
-
- // Look for a differing name but with the same hash data
- if (strcmp(ROM_GETNAME(romp), ROM_GETNAME(rom)) != 0 &&
- romphashes == util::hash_collection(ROM_GETHASHDATA(rom)))
- {
- // Attempt to open the properly named file, scanning up through parent directories
- filerr = osd_file::error::NOT_FOUND;
- for (int searchdrv = drv; searchdrv != -1 && filerr != osd_file::error::NONE; searchdrv = driver_list::clone(searchdrv))
- filerr = common_process_file(driver_specific_options, driver_list::driver(searchdrv).name, ".chd", rom, image_file);
-
- if (filerr != osd_file::error::NONE)
- filerr = common_process_file(driver_specific_options, nullptr, ".chd", rom, image_file);
-
- // Did the file open succeed?
- if (filerr == osd_file::error::NONE)
- {
- std::string fullpath(image_file.fullpath());
- image_file.close();
-
- // try to open the CHD
- err = image_chd.open(fullpath.c_str());
- if (err == CHDERR_NONE)
- return err;
- }
- }
- }
- return err;
-}
-
-
-/*-------------------------------------------------
open_disk_diff - open a DISK diff file
-------------------------------------------------*/
chd_error rom_load_manager::open_disk_diff(emu_options &options, const rom_entry *romp, chd_file &source, chd_file &diff_chd)
{
+ // TODO: use system name and/or software list name in the path - the current setup doesn't scale
std::string fname = std::string(ROM_GETNAME(romp)).append(".dif");
/* try to open the diff */
@@ -1149,11 +999,11 @@ chd_error rom_load_manager::open_disk_diff(emu_options &options, const rom_entry
for a region
-------------------------------------------------*/
-void rom_load_manager::process_disk_entries(const char *regiontag, const rom_entry *parent_region, const rom_entry *romp, const char *locationtag)
+void rom_load_manager::process_disk_entries(std::initializer_list<std::reference_wrapper<const std::vector<std::string> > > searchpath, const char *regiontag, const rom_entry *romp, std::function<const rom_entry * ()> next_parent)
{
/* remove existing disk entries for this region */
m_chd_list.erase(std::remove_if(m_chd_list.begin(), m_chd_list.end(),
- [regiontag](std::unique_ptr<open_chd> &chd){ return !strcmp(chd->region(), regiontag); }), m_chd_list.end());
+ [regiontag] (std::unique_ptr<open_chd> &chd) { return !strcmp(chd->region(), regiontag); }), m_chd_list.end());
/* loop until we hit the end of this region */
for ( ; !ROMENTRY_ISREGIONEND(romp); romp++)
@@ -1162,16 +1012,15 @@ void rom_load_manager::process_disk_entries(const char *regiontag, const rom_ent
if (ROMENTRY_ISFILE(romp))
{
auto chd = std::make_unique<open_chd>(regiontag);
-
- util::hash_collection hashes(ROM_GETHASHDATA(romp));
chd_error err;
/* make the filename of the source */
- std::string filename = std::string(ROM_GETNAME(romp)).append(".chd");
+ const std::string filename = std::string(ROM_GETNAME(romp)).append(".chd");
/* first open the source drive */
+ // FIXME: we've lost the ability to search parents here
LOG("Opening disk image: %s\n", filename.c_str());
- err = chd_error(open_disk_image(machine().options(), &machine().system(), romp, chd->orig_chd(), locationtag));
+ err = do_open_disk(machine().options(), searchpath, romp, chd->orig_chd(), next_parent);
if (err != CHDERR_NONE)
{
handle_missing_file(romp, std::vector<std::string>(), err);
@@ -1184,6 +1033,7 @@ void rom_load_manager::process_disk_entries(const char *regiontag, const rom_ent
acthashes.add_sha1(chd->orig_chd().sha1());
/* verify the hash */
+ const util::hash_collection hashes(ROM_GETHASHDATA(romp));
if (hashes != acthashes)
{
m_errorstring.append(string_format("%s WRONG CHECKSUMS:\n", filename));
@@ -1218,6 +1068,57 @@ void rom_load_manager::process_disk_entries(const char *regiontag, const rom_ent
}
+chd_error rom_load_manager::open_disk_image(const emu_options &options, const device_t &device, const rom_entry *romp, chd_file &image_chd)
+{
+ const std::vector<std::string> searchpath(device.searchpath());
+
+ driver_device const *const driver(dynamic_cast<driver_device const *>(&device));
+ std::function<const rom_entry * ()> next_parent;
+ if (driver)
+ next_parent = [sys = &driver->system(), roms = std::vector<rom_entry>()] () mutable { return next_parent_system(sys, roms); };
+ else
+ next_parent = [] () { return nullptr; };
+ return do_open_disk(options, { searchpath }, romp, image_chd, std::move(next_parent));
+}
+
+
+chd_error rom_load_manager::open_disk_image(const emu_options &options, software_list_device &swlist, const software_info &swinfo, const rom_entry *romp, chd_file &image_chd)
+{
+ std::vector<software_info const *> parents;
+ std::vector<std::string> listsearch, freesearch, disksearch;
+ disksearch.emplace_back(swlist.list_name());
+
+ for (software_info const *i = &swinfo; i; )
+ {
+ if (std::find(parents.begin(), parents.end(), i) != parents.end())
+ break;
+ parents.emplace_back(i);
+ listsearch.emplace_back(util::string_format("%s" PATH_SEPARATOR "%s", swlist.list_name(), i->shortname()));
+ freesearch.emplace_back(i->shortname());
+ i = i->parentname().empty() ? nullptr : swlist.find(i->parentname());
+ }
+
+ auto part(parents.front()->parts().end());
+ return do_open_disk(
+ options,
+ { listsearch, freesearch, disksearch },
+ romp,
+ image_chd,
+ [&parents, current = parents.cbegin(), part, end = part] () mutable -> const rom_entry *
+ {
+ if (part == end)
+ {
+ if (parents.end() == current)
+ return nullptr;
+ part = (*current)->parts().cbegin();
+ end = (*current)->parts().cend();
+ do { ++current; } while ((parents.cend() != current) && (*current)->parts().empty());
+ }
+ return &(*part++).romdata()[0];
+ });
+}
+
+
/*-------------------------------------------------
normalize_flags_for_device - modify the region
flags for the given device
@@ -1266,9 +1167,6 @@ void rom_load_manager::normalize_flags_for_device(const char *rgntag, u8 &width,
void rom_load_manager::load_software_part_region(device_t &device, software_list_device &swlist, const char *swname, const rom_entry *start_region)
{
- std::string locationtag(swlist.list_name()), breakstr("%");
- const rom_entry *region;
-
m_errorstring.clear();
m_softwarningstring.clear();
@@ -1276,10 +1174,16 @@ void rom_load_manager::load_software_part_region(device_t &device, software_list
m_romstotalsize = 0;
m_romsloadedsize = 0;
+ std::vector<const software_info *> parents;
+ std::vector<std::string> listsearch, freesearch, disksearch, devsearch;
+ listsearch.emplace_back(util::string_format("%s" PATH_SEPARATOR "%s", swlist.list_name(), swname));
+ freesearch.emplace_back(swname);
const software_info *swinfo = swlist.find(swname);
- if (swinfo != nullptr)
+ if (swinfo)
{
- u32 supported = swinfo->supported();
+ // dispay a warning for unsupported software
+ // TODO: list supported clones like we do for machines?
+ const u32 supported(swinfo->supported());
if (supported == SOFTWARE_SUPPORTED_PARTIAL)
{
m_errorstring.append(string_format("WARNING: support for software %s (in list %s) is only partial\n", swname, swlist.list_name()));
@@ -1291,34 +1195,45 @@ void rom_load_manager::load_software_part_region(device_t &device, software_list
m_softwarningstring.append(string_format("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 std::string in the format
- // " swlist % clonename % parentname "
- // open_rom_file contains the code to split the elements and to create paths to load from
-
- locationtag.append(breakstr);
-
- while (swinfo != nullptr)
+ // walk the chain of parents and add them to the search path
+ parents.emplace_back(swinfo);
+ swinfo = !swinfo->parentname().empty() ? swlist.find(swinfo->parentname()) : nullptr;
+ while (swinfo)
{
- locationtag.append(swinfo->shortname()).append(breakstr);
+ if (std::find(parents.begin(), parents.end(), swinfo) != parents.end())
+ {
+ m_errorstring.append(util::string_format("WARNING: parent/clone relationships form a loop for software %s (in list %s)\n", swname, swlist.list_name()));
+ m_softwarningstring.append(util::string_format("Parent/clone relationships from a loop for software %s (in list %s)\n", swname, swlist.list_name()));
+ break;
+ }
+ parents.emplace_back(swinfo);
+ listsearch.emplace_back(util::string_format("%s" PATH_SEPARATOR "%s", swlist.list_name(), swinfo->shortname()));
+ freesearch.emplace_back(swinfo->shortname());
swinfo = !swinfo->parentname().empty() ? swlist.find(swinfo->parentname()) : nullptr;
}
- // strip the final '%'
- locationtag.erase(locationtag.length() - 1, 1);
}
+ // this is convenient for CD-only lists so you don't need an extra level of directories containing one file each
+ disksearch.emplace_back(swlist.list_name());
- /* loop until we hit the end */
- for (region = start_region; region != nullptr; region = rom_next_region(region))
+ // for historical reasons, add the search path for the software list device's owner
+ const device_t *const listowner = swlist.owner();
+ if (listowner)
+ devsearch = listowner->searchpath();
+
+ // loop until we hit the end
+ std::function<const rom_entry * ()> next_parent;
+ for (const rom_entry *region = start_region; region != nullptr; region = rom_next_region(region))
{
u32 regionlength = ROMREGION_GETLENGTH(region);
std::string regiontag = device.subtag(ROMREGION_GETTAG(region));
LOG("Processing region \"%s\" (length=%X)\n", regiontag.c_str(), regionlength);
- /* the first entry must be a region */
+ // the first entry must be a region
assert(ROMENTRY_ISREGION(region));
- /* if this is a device region, override with the device width and endianness */
+ // if this is a device region, override with the device width and endianness
endianness_t endianness = ROMREGION_ISBIGENDIAN(region) ? ENDIANNESS_BIG : ENDIANNESS_LITTLE;
u8 width = ROMREGION_GETWIDTH(region) / 8;
memory_region *memregion = machine().root_device().memregion(regiontag);
@@ -1326,47 +1241,76 @@ void rom_load_manager::load_software_part_region(device_t &device, software_list
{
normalize_flags_for_device(regiontag.c_str(), width, endianness);
- /* clear old region (todo: should be moved to an image unload function) */
+ // clear old region (TODO: should be moved to an image unload function)
machine().memory().region_free(memregion->name());
}
- /* remember the base and length */
+ // remember the base and length
m_region = machine().memory().region_alloc(regiontag.c_str(), regionlength, width, endianness);
LOG("Allocated %X bytes @ %p\n", m_region->bytes(), m_region->base());
- /* clear the region if it's requested */
- if (ROMREGION_ISERASE(region))
+ if (ROMREGION_ISERASE(region)) // clear the region if it's requested
memset(m_region->base(), ROMREGION_GETERASEVAL(region), m_region->bytes());
-
- /* or if it's sufficiently small (<= 4MB) */
- else if (m_region->bytes() <= 0x400000)
+ else if (m_region->bytes() <= 0x400000) // or if it's sufficiently small (<= 4MB)
memset(m_region->base(), 0, m_region->bytes());
-
#ifdef MAME_DEBUG
- /* if we're debugging, fill region with random data to catch errors */
- else
+ else // if we're debugging, fill region with random data to catch errors
fill_random(m_region->base(), m_region->bytes());
#endif
- /* update total number of roms */
+ // update total number of roms
for (const rom_entry *rom = rom_first_file(region); rom != nullptr; rom = rom_next_file(rom))
{
m_romstotal++;
m_romstotalsize += rom_file_size(rom);
}
- /* now process the entries in the region */
+ // now process the entries in the region
if (ROMREGION_ISROMDATA(region))
- process_rom_entries(device, locationtag.c_str(), region, region + 1, true);
+ {
+ if (devsearch.empty())
+ process_rom_entries({ listsearch, freesearch }, 0U, region, region + 1, true);
+ else
+ process_rom_entries({ listsearch, freesearch, devsearch }, 0U, region, region + 1, true);
+ }
else if (ROMREGION_ISDISKDATA(region))
- process_disk_entries(regiontag.c_str(), region, region + 1, locationtag.c_str());
+ {
+ if (!next_parent)
+ {
+ if (!parents.empty())
+ {
+ auto part(parents.front()->parts().end());
+ next_parent =
+ [&parents, current = parents.cbegin(), part, end = part] () mutable -> const rom_entry *
+ {
+ if (part == end)
+ {
+ if (parents.end() == current)
+ return nullptr;
+ part = (*current)->parts().cbegin();
+ end = (*current)->parts().cend();
+ do { ++current; } while ((parents.cend() != current) && (*current)->parts().empty());
+ }
+ return &(*part++).romdata()[0];
+ };
+ }
+ else
+ {
+ next_parent = [] () { return nullptr; };
+ }
+ }
+ if (devsearch.empty())
+ process_disk_entries({ listsearch, freesearch, disksearch }, regiontag.c_str(), region + 1, next_parent);
+ else
+ process_disk_entries({ listsearch, freesearch, disksearch, devsearch }, regiontag.c_str(), region + 1, next_parent);
+ }
}
- /* now go back and post-process all the regions */
- for (region = start_region; region != nullptr; region = rom_next_region(region))
+ // now go back and post-process all the regions
+ for (const rom_entry *region = start_region; region != nullptr; region = rom_next_region(region))
region_post_process(device.memregion(ROMREGION_GETTAG(region)), ROMREGION_ISINVERTED(region));
- /* display the results and exit */
+ // display the results and exit
display_rom_load_results(true);
}
@@ -1377,10 +1321,13 @@ void rom_load_manager::load_software_part_region(device_t &device, software_list
void rom_load_manager::process_region_list()
{
- /* loop until we hit the end */
+ // loop until we hit the end
device_iterator deviter(machine().root_device());
+ std::vector<std::string> searchpath;
for (device_t &device : deviter)
{
+ searchpath.clear();
+ std::function<const rom_entry * ()> next_parent;
for (const rom_entry *region = rom_first_region(device); region != nullptr; region = rom_next_region(region))
{
u32 regionlength = ROMREGION_GETLENGTH(region);
@@ -1388,17 +1335,17 @@ void rom_load_manager::process_region_list()
std::string regiontag = device.subtag(ROM_GETNAME(region));
LOG("Processing region \"%s\" (length=%X)\n", regiontag.c_str(), regionlength);
- /* the first entry must be a region */
+ // the first entry must be a region
assert(ROMENTRY_ISREGION(region));
if (ROMREGION_ISROMDATA(region))
{
- /* if this is a device region, override with the device width and endianness */
+ // if this is a device region, override with the device width and endianness
u8 width = ROMREGION_GETWIDTH(region) / 8;
endianness_t endianness = ROMREGION_ISBIGENDIAN(region) ? ENDIANNESS_BIG : ENDIANNESS_LITTLE;
normalize_flags_for_device(regiontag.c_str(), width, endianness);
- /* remember the base and length */
+ // remember the base and length
m_region = machine().memory().region_alloc(regiontag.c_str(), regionlength, width, endianness);
LOG("Allocated %X bytes @ %p\n", m_region->bytes(), m_region->base());
@@ -1412,11 +1359,25 @@ void rom_load_manager::process_region_list()
#endif
// now process the entries in the region
- process_rom_entries(device, device.shortname(), region, region + 1, false);
+ if (searchpath.empty())
+ searchpath = device.searchpath();
+ assert(!searchpath.empty());
+ process_rom_entries({ searchpath }, device.system_bios(), region, region + 1, false);
}
else if (ROMREGION_ISDISKDATA(region))
{
- process_disk_entries(regiontag.c_str(), region, region + 1, nullptr);
+ if (searchpath.empty())
+ searchpath = device.searchpath();
+ assert(!searchpath.empty());
+ if (!next_parent)
+ {
+ driver_device const *const driver(dynamic_cast<driver_device const *>(&device));
+ if (driver)
+ next_parent = [sys = &driver->system(), roms = std::vector<rom_entry>()] () mutable { return next_parent_system(sys, roms); };
+ else
+ next_parent = [] () { return nullptr; };
+ }
+ process_disk_entries({ searchpath }, regiontag.c_str(), region + 1, next_parent);
}
}
}