diff options
Diffstat (limited to 'src/emu/romload.cpp')
-rw-r--r-- | src/emu/romload.cpp | 613 |
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 ¤t_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); } } } |