diff options
author | Vas Crabb <vas@vastheman.com> | 2020-04-14 22:13:30 +1000 |
---|---|---|
committer | Vas Crabb <vas@vastheman.com> | 2020-04-15 02:20:23 +1000 |
commit | cf078d736add075a78f488a3ec50c567d1a8fc29 (patch) | |
tree | 3d81c39e4021c7991fbcfe804dc7fd99929bff62 | |
parent | 9341daa9edca31cb55ef3a8b842310bdd79551c5 (diff) |
ROM loading cleanup:
* More flexible constructors for path_iterator and emu_file
* More straightforward system/device ROM loading and software loading when using ROM loader
* Proper parent walk when searching for identical CHDs with different names from software list
* Fixed hangs if software item parents form a loop
* Fixed layouts being loaded from bogus empty paths
Note that there are changes in behaviour:
* For software list ROMs/disks, MAME will now search the software path before searching the machine path
* The search path for the owner of the software list device is used, which may not be the driver itself
* MAME will no longer load loose CHDs from the media path - it's just too unwieldy with the number of supported systems
* MAME will no longer search archives above the top level of the media path
-rw-r--r-- | src/emu/diimage.cpp | 147 | ||||
-rw-r--r-- | src/emu/fileio.cpp | 209 | ||||
-rw-r--r-- | src/emu/fileio.h | 113 | ||||
-rw-r--r-- | src/emu/render.cpp | 40 | ||||
-rw-r--r-- | src/emu/romload.cpp | 613 | ||||
-rw-r--r-- | src/emu/romload.h | 27 | ||||
-rw-r--r-- | src/emu/softlist_dev.cpp | 5 | ||||
-rw-r--r-- | src/frontend/mame/audit.cpp | 169 | ||||
-rw-r--r-- | src/frontend/mame/audit.h | 33 | ||||
-rw-r--r-- | src/frontend/mame/clifront.cpp | 4 | ||||
-rw-r--r-- | src/frontend/mame/ui/imgcntrl.cpp | 2 | ||||
-rw-r--r-- | src/frontend/mame/ui/selgame.cpp | 2 | ||||
-rw-r--r-- | src/frontend/mame/ui/selmenu.cpp | 3 | ||||
-rw-r--r-- | src/frontend/mame/ui/selsoft.cpp | 2 | ||||
-rw-r--r-- | src/mame/mame.lst | 2 |
15 files changed, 764 insertions, 607 deletions
diff --git a/src/emu/diimage.cpp b/src/emu/diimage.cpp index 8cc849ee189..4ec5ff7898c 100644 --- a/src/emu/diimage.cpp +++ b/src/emu/diimage.cpp @@ -836,56 +836,44 @@ std::vector<u32> device_image_interface::determine_open_plan(bool is_create) //------------------------------------------------- -// dump_wrong_and_correct_checksums - dump an -// error message containing the wrong and the -// correct checksums for a given software item -//------------------------------------------------- - -static void dump_wrong_and_correct_checksums(const util::hash_collection &hashes, const util::hash_collection &acthashes) -{ - osd_printf_error(" EXPECTED: %s\n", hashes.macro_string()); - osd_printf_error(" FOUND: %s\n", acthashes.macro_string()); -} - - -//------------------------------------------------- // verify_length_and_hash - verify the length // and hash signatures of a file //------------------------------------------------- static int verify_length_and_hash(emu_file *file, const char *name, u32 explength, const util::hash_collection &hashes) { - int retVal = 0; - if (file==nullptr) return 0; + int retval = 0; + if (!file) + return 0; // verify length u32 actlength = file->size(); if (explength != actlength) { osd_printf_error("%s WRONG LENGTH (expected: %d found: %d)\n", name, explength, actlength); - retVal++; + retval++; } - // If there is no good dump known, write it 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 it osd_printf_error("%s NO GOOD DUMP KNOWN\n", name); } - // verify checksums else if (hashes != acthashes) { // otherwise, it's just bad osd_printf_error("%s WRONG CHECKSUMS:\n", name); - dump_wrong_and_correct_checksums(hashes, acthashes); - retVal++; + osd_printf_error(" EXPECTED: %s\n", hashes.macro_string()); + osd_printf_error(" FOUND: %s\n", acthashes.macro_string()); + retval++; } - // If it matches, but it is actually a bad dump, write it else if (hashes.flag(util::hash_collection::FLAG_BAD_DUMP)) { + // If it matches, but it is actually a bad dump, write it osd_printf_error("%s NEEDS REDUMP\n",name); } - return retVal; + return retval; } @@ -895,102 +883,91 @@ static int verify_length_and_hash(emu_file *file, const char *name, u32 explengt bool device_image_interface::load_software(software_list_device &swlist, const char *swname, const rom_entry *start) { - std::string locationtag, breakstr("%"); - const rom_entry *region; - bool retVal = false; + bool retval = false; int warningcount = 0; - for (region = start; region != nullptr; region = rom_next_region(region)) + for (const rom_entry *region = start; region; region = rom_next_region(region)) { // loop until we hit the end of this region - const rom_entry *romp = region + 1; - while (!ROMENTRY_ISREGIONEND(romp)) + for (const rom_entry *romp = region + 1; !ROMENTRY_ISREGIONEND(romp); romp++) { // handle files if (ROMENTRY_ISFILE(romp)) { - osd_file::error filerr = osd_file::error::NOT_FOUND; - - u32 crc = 0; - bool has_crc = util::hash_collection(ROM_GETHASHDATA(romp)).crc(crc); - - const software_info *swinfo = swlist.find(swname); - if (swinfo == nullptr) + const software_info *const swinfo = swlist.find(swname); + if (!swinfo) return false; - u32 supported = swinfo->supported(); + const u32 supported = swinfo->supported(); if (supported == SOFTWARE_SUPPORTED_PARTIAL) osd_printf_error("WARNING: support for software %s (in list %s) is only partial\n", swname, swlist.list_name()); if (supported == SOFTWARE_SUPPORTED_NO) osd_printf_error("WARNING: support for software %s (in list %s) is only preliminary\n", swname, swlist.list_name()); - // attempt reading up the chain through the parents and create a locationtag std::string in the format - // " swlist % clonename % parentname " - // below, we have the code to split the elements and to create paths to load from + u32 crc = 0; + const bool has_crc = util::hash_collection(ROM_GETHASHDATA(romp)).crc(crc); + std::vector<const software_info *> parents; + std::vector<std::string> searchpath; - while (swinfo != nullptr) + // search <rompath>/<list>/<software> following parents + searchpath.emplace_back(util::string_format("%s" PATH_SEPARATOR "%s", swlist.list_name(), swname)); + for (const software_info *i = swinfo; i && !i->parentname().empty(); i = swlist.find(i->parentname())) { - locationtag.append(swinfo->shortname()).append(breakstr); - swinfo = !swinfo->parentname().empty() ? swlist.find(swinfo->parentname()) : nullptr; + if (std::find(parents.begin(), parents.end(), i) != parents.end()) + { + osd_printf_warning("WARNING: parent/clone relationships form a loop for software %s (in list %s)\n", swname, swlist.list_name()); + break; + } + parents.emplace_back(i); + searchpath.emplace_back(util::string_format("%s" PATH_SEPARATOR "%s", swlist.list_name(), i->parentname())); } - // strip the final '%' - locationtag.erase(locationtag.length() - 1, 1); + // search <rompath>/<software> following parents + searchpath.emplace_back(swname); + parents.clear(); + for (software_info const *i = swinfo; i && !i->parentname().empty(); i = swlist.find(i->parentname())) + { + if (std::find(parents.begin(), parents.end(), i) != parents.end()) + break; + parents.emplace_back(i); + searchpath.emplace_back(i->parentname()); + } - // check if locationtag actually contains two locations separated by '%' - // (i.e. check if we are dealing with a clone in softwarelist) - std::string tag2, tag3, tag4(locationtag), tag5; - int separator = tag4.find_first_of('%'); - if (separator != -1) + // for historical reasons, add the search path for the software list device's owner + const device_t *const listowner = swlist.owner(); + if (listowner) { - // we are loading a clone through softlists, split the setname from the parentname - tag5.assign(tag4.substr(separator + 1, tag4.length() - separator + 1)); - tag4.erase(separator, tag4.length() - separator); + std::vector<std::string> devsearch = listowner->searchpath(); + for (std::string &path : devsearch) + searchpath.emplace_back(std::move(path)); } - // prepare locations where we have to load from: list/parentname & list/clonename - std::string tag1(swlist.list_name()); - tag1.append(PATH_SEPARATOR); - tag2.assign(tag1.append(tag4)); - tag1.assign(swlist.list_name()); - tag1.append(PATH_SEPARATOR); - tag3.assign(tag1.append(tag5)); - - if (tag5.find_first_of('%') != -1) - fatalerror("We do not support clones of clones!\n"); - - // try to load from the available location(s): - // - if we are not using lists, we have regiontag only; - // - if we are using lists, we have: list/clonename, list/parentname, clonename, parentname - // try to load from list/setname - if ((m_mame_file == nullptr) && (tag2.c_str() != nullptr)) - m_mame_file = common_process_file(device().machine().options(), tag2.c_str(), has_crc, crc, romp, filerr); - // try to load from list/parentname - if ((m_mame_file == nullptr) && (tag3.c_str() != nullptr)) - m_mame_file = common_process_file(device().machine().options(), tag3.c_str(), has_crc, crc, romp, filerr); - // try to load from setname - if ((m_mame_file == nullptr) && (tag4.c_str() != nullptr)) - m_mame_file = common_process_file(device().machine().options(), tag4.c_str(), has_crc, crc, romp, filerr); - // try to load from parentname - if ((m_mame_file == nullptr) && (tag5.c_str() != nullptr)) - m_mame_file = common_process_file(device().machine().options(), tag5.c_str(), has_crc, crc, romp, filerr); - - warningcount += verify_length_and_hash(m_mame_file.get(),ROM_GETNAME(romp),ROM_GETLENGTH(romp), util::hash_collection(ROM_GETHASHDATA(romp))); + // try to load the file + m_mame_file.reset(new emu_file(device().machine().options().media_path(), searchpath, OPEN_FLAG_READ)); + m_mame_file->set_restrict_to_mediapath(1); + osd_file::error filerr; + if (has_crc) + filerr = m_mame_file->open(ROM_GETNAME(romp), crc); + else + filerr = 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; + retval = true; break; // load first item for start } - romp++; /* something else; skip */ } } + if (warningcount > 0) - { osd_printf_error("WARNING: the software item might not run correctly.\n"); - } - return retVal; + + return retval; } diff --git a/src/emu/fileio.cpp b/src/emu/fileio.cpp index 53eac8d55cf..5c392b01ffd 100644 --- a/src/emu/fileio.cpp +++ b/src/emu/fileio.cpp @@ -9,9 +9,31 @@ ***************************************************************************/ #include "emu.h" -#include "unzip.h" #include "fileio.h" +#include "unzip.h" + +//#define VERBOSE 1 +#define LOG_OUTPUT_FUNC osd_printf_verbose +#include "logmacro.h" + + +template path_iterator::path_iterator(char *&, int); +template path_iterator::path_iterator(char * const &, int); +template path_iterator::path_iterator(char const *&, int); +template path_iterator::path_iterator(char const * const &, int); +template path_iterator::path_iterator(std::vector<std::string> &, int); +template path_iterator::path_iterator(const std::vector<std::string> &, int); + +template emu_file::emu_file(std::string &, u32); +template emu_file::emu_file(const std::string &, u32); +template emu_file::emu_file(char *&, u32); +template emu_file::emu_file(char * const &, u32); +template emu_file::emu_file(char const *&, u32); +template emu_file::emu_file(char const * const &, u32); +template emu_file::emu_file(std::vector<std::string> &, u32); +template emu_file::emu_file(const std::vector<std::string> &, u32); + const u32 OPEN_FLAG_HAS_CRC = 0x10000; @@ -28,6 +50,7 @@ const u32 OPEN_FLAG_HAS_CRC = 0x10000; path_iterator::path_iterator(std::string &&searchpath) : m_searchpath(std::move(searchpath)) , m_current(m_searchpath.cbegin()) + , m_separator(';') // FIXME this should be a macro - UNIX prefers : , m_is_first(true) { } @@ -35,6 +58,7 @@ path_iterator::path_iterator(std::string &&searchpath) path_iterator::path_iterator(std::string const &searchpath) : m_searchpath(searchpath) , m_current(m_searchpath.cbegin()) + , m_separator(';') // FIXME this should be a macro - UNIX prefers : , m_is_first(true) { } @@ -47,6 +71,7 @@ path_iterator::path_iterator(path_iterator &&that) path_iterator::path_iterator(path_iterator const &that) : m_searchpath(that.m_searchpath) , m_current(std::next(m_searchpath.cbegin(), std::distance(that.m_searchpath.cbegin(), that.m_current))) + , m_separator(that.m_separator) , m_is_first(that.m_is_first) { } @@ -61,6 +86,7 @@ path_iterator &path_iterator::operator=(path_iterator &&that) auto const current(std::distance(that.m_searchpath.cbegin(), that.m_current)); m_searchpath = std::move(that.m_searchpath); m_current = std::next(m_searchpath.cbegin(), current); + m_separator = that.m_separator; m_is_first = that.m_is_first; return *this; } @@ -69,6 +95,7 @@ path_iterator &path_iterator::operator=(path_iterator const &that) { m_searchpath = that.m_searchpath; m_current = std::next(m_searchpath.cbegin(), std::distance(that.m_searchpath.cbegin(), that.m_current)); + m_separator = that.m_separator; m_is_first = that.m_is_first; return *this; } @@ -86,7 +113,7 @@ bool path_iterator::next(std::string &buffer, const char *name) return false; // copy up to the next separator - auto const sep(std::find(m_current, m_searchpath.cend(), ';')); // FIXME this should be a macro - UNIX prefers : + auto const sep(std::find(m_current, m_searchpath.cend(), m_separator)); buffer.assign(m_current, sep); m_current = sep; if (m_searchpath.cend() != m_current) @@ -165,31 +192,31 @@ const osd::directory::entry *file_enumerator::next() //------------------------------------------------- emu_file::emu_file(u32 openflags) - : m_file() - , m_iterator(std::string()) - , m_mediapaths(std::string()) - , m_crc(0) - , m_openflags(openflags) - , m_zipfile(nullptr) - , m_ziplength(0) - , m_remove_on_close(false) - , m_restrict_to_mediapath(false) + : emu_file(path_iterator(std::string()), openflags) { - // sanity check the open flags - if ((m_openflags & OPEN_FLAG_HAS_CRC) && (m_openflags & OPEN_FLAG_WRITE)) - throw emu_fatalerror("Attempted to open a file for write with OPEN_FLAG_HAS_CRC"); + } -emu_file::emu_file(std::string &&searchpath, u32 openflags) - : m_file() - , m_iterator(searchpath) - , m_mediapaths(std::move(searchpath)) +emu_file::emu_file(path_iterator &&searchpath, u32 openflags) + : emu_file(openflags, EMPTY) +{ + m_iterator.emplace_back(searchpath, std::string()); + m_mediapaths.emplace_back(std::move(searchpath), std::string()); +} + +emu_file::emu_file(u32 openflags, empty_t) + : m_filename() + , m_fullpath() + , m_file() + , m_iterator() + , m_mediapaths() + , m_first(true) , m_crc(0) , m_openflags(openflags) , m_zipfile(nullptr) , m_ziplength(0) , m_remove_on_close(false) - , m_restrict_to_mediapath(false) + , m_restrict_to_mediapath(0) { // sanity check the open flags if ((m_openflags & OPEN_FLAG_HAS_CRC) && (m_openflags & OPEN_FLAG_WRITE)) @@ -279,7 +306,7 @@ osd_file::error emu_file::open(const std::string &name) m_openflags &= ~OPEN_FLAG_HAS_CRC; // reset the iterator and open_next - m_iterator.reset(); + m_first = true; return open_next(); } @@ -291,28 +318,10 @@ osd_file::error emu_file::open(const std::string &name, u32 crc) m_openflags |= OPEN_FLAG_HAS_CRC; // reset the iterator and open_next - m_iterator.reset(); + m_first = true; return open_next(); } -osd_file::error emu_file::open(const std::string &name1, const std::string &name2, u32 crc) -{ - // concatenate the strings and do a standard open - return open(name1 + name2, crc); -} - -osd_file::error emu_file::open(const std::string &name1, const std::string &name2, const std::string &name3, u32 crc) -{ - // concatenate the strings and do a standard open - return open(name1 + name2 + name3, crc); -} - -osd_file::error emu_file::open(const std::string &name1, const std::string &name2, const std::string &name3, const std::string &name4, u32 crc) -{ - // concatenate the strings and do a standard open - return open(name1 + name2 + name3 + name4, crc); -} - //------------------------------------------------- // open_next - open the next file that matches @@ -322,24 +331,64 @@ osd_file::error emu_file::open(const std::string &name1, const std::string &name osd_file::error emu_file::open_next() { // if we're open from a previous attempt, close up now - if (m_file != nullptr) + if (m_file) close(); // loop over paths + LOG("emu_file: open next '%s'\n", m_filename); osd_file::error filerr = osd_file::error::NOT_FOUND; - while (m_iterator.next(m_fullpath, m_filename.c_str())) + while (osd_file::error::NONE != filerr) { + if (m_first) + { + m_first = false; + for (searchpath_vector::value_type &i : m_iterator) + { + i.first.reset(); + if (!i.first.next(i.second)) + return filerr; + } + } + else + { + searchpath_vector::iterator i(m_iterator.begin()); + while (i != m_iterator.end()) + { + if (i->first.next(i->second)) + { + LOG("emu_file: next path %d '%s'\n", std::distance(m_iterator.begin(), i), i->second); + for (searchpath_vector::iterator j = m_iterator.begin(); i != j; ++j) + { + j->first.reset(); + j->first.next(j->second); + } + break; + } + ++i; + } + if (m_iterator.end() == i) + return filerr; + } + + // build full path + m_fullpath.clear(); + for (searchpath_vector::value_type const &path : m_iterator) + { + m_fullpath.append(path.second); + if (!m_fullpath.empty() && !util::is_directory_separator(m_fullpath.back())) + m_fullpath.append(PATH_SEPARATOR); + } + m_fullpath.append(m_filename); + // attempt to open the file directly + LOG("emu_file: attempting to open '%s' directly\n", m_fullpath); filerr = util::core_file::open(m_fullpath, m_openflags, m_file); - if (filerr == osd_file::error::NONE) - break; // if we're opening for read-only we have other options - if ((m_openflags & (OPEN_FLAG_READ | OPEN_FLAG_WRITE)) == OPEN_FLAG_READ) + if ((osd_file::error::NONE != filerr) && ((m_openflags & (OPEN_FLAG_READ | OPEN_FLAG_WRITE)) == OPEN_FLAG_READ)) { + LOG("emu_file: attempting to open '%s' from archives\n", m_fullpath); filerr = attempt_zipped(); - if (filerr == osd_file::error::NONE) - break; } } return filerr; @@ -402,7 +451,7 @@ osd_file::error emu_file::compress(int level) // loading if needed //------------------------------------------------- -bool emu_file::compressed_file_ready(void) +bool emu_file::compressed_file_ready() { // load the ZIP file now if we haven't yet if (m_zipfile && (load_zipped_file() != osd_file::error::NONE)) @@ -611,16 +660,55 @@ void emu_file::flush() // any media path //------------------------------------------------- -bool emu_file::part_of_mediapath(std::string path) +bool emu_file::part_of_mediapath(const std::string &path) { - bool result = false; + if (!m_restrict_to_mediapath) + return true; + + for (size_t i = 0U; (m_mediapaths.size() > i) && ((0 > m_restrict_to_mediapath) || (i < m_restrict_to_mediapath)); i++) + { + m_mediapaths[i].first.reset(); + if (!m_mediapaths[i].first.next(m_mediapaths[i].second)) + return false; + } + std::string mediapath; - m_mediapaths.reset(); - while (m_mediapaths.next(mediapath, nullptr) && !result) { - if (path.compare(mediapath.substr(0, mediapath.length()))) - result = true; + while (true) + { + mediapath.clear(); + for (size_t i = 0U; (m_mediapaths.size() > i) && ((0 > m_restrict_to_mediapath) || (i < m_restrict_to_mediapath)); i++) + { + mediapath.append(m_mediapaths[i].second); + if (!mediapath.empty() && !util::is_directory_separator(mediapath.back())) + mediapath.append(PATH_SEPARATOR); + } + + if (!path.compare(0, mediapath.size(), mediapath)) + { + LOG("emu_file: path '%s' matches media path '%s'\n", path, mediapath); + return true; + } + + size_t i = 0U; + while ((m_mediapaths.size() > i) && ((0 > m_restrict_to_mediapath) || (i < m_restrict_to_mediapath))) + { + if (m_mediapaths[i].first.next(m_mediapaths[i].second)) + { + for (size_t j = 0U; i != j; j++) + { + m_mediapaths[j].first.reset(); + m_mediapaths[j].first.next(m_mediapaths[j].second); + } + break; + } + i++; + } + if ((m_mediapaths.size() == i) || ((0 <= m_restrict_to_mediapath) && (i == m_restrict_to_mediapath))) + { + LOG("emu_file: path '%s' not in media path\n", path); + return false; + } } - return result; } //------------------------------------------------- @@ -641,21 +729,24 @@ osd_file::error emu_file::attempt_zipped() // loop over directory parts up to the start of filename while (1) { - // find the final path separator - auto const dirsep = m_fullpath.find_last_of(PATH_SEPARATOR[0]); - if (dirsep == std::string::npos) + if (!part_of_mediapath(m_fullpath)) break; - if (restrict_to_mediapath() && !part_of_mediapath(m_fullpath)) + // find the final path separator + auto const dirsepiter(std::find_if(m_fullpath.rbegin(), m_fullpath.rend(), util::is_directory_separator)); + if (dirsepiter == m_fullpath.rend()) break; + std::string::size_type const dirsep(std::distance(m_fullpath.begin(), dirsepiter.base()) - 1); // insert the part from the right of the separator into the head of the filename - if (!filename.empty()) filename.insert(0, 1, '/'); + if (!filename.empty()) + filename.insert(0, 1, '/'); filename.insert(0, m_fullpath.substr(dirsep + 1, std::string::npos)); // remove this part of the filename and append an archive extension m_fullpath.resize(dirsep); m_fullpath.append(suffixes[i]); + LOG("emu_file: looking for '%s' in archive '%s'\n", filename, m_fullpath); // attempt to open the archive file util::archive_file::ptr zip; diff --git a/src/emu/fileio.h b/src/emu/fileio.h index ad7ef5028a6..20be11eb022 100644 --- a/src/emu/fileio.h +++ b/src/emu/fileio.h @@ -16,6 +16,13 @@ #include "corefile.h" #include "hash.h" +#include <iterator> +#include <string> +#include <type_traits> +#include <utility> +#include <vector> + + // some systems use macros for getc/putc rather than functions #ifdef getc #undef getc @@ -37,6 +44,17 @@ public: path_iterator(path_iterator &&that); path_iterator(path_iterator const &that); + template <typename T> + path_iterator(T &&searchpath, std::enable_if_t<std::is_constructible<std::string, T>::value, int> = 0) + : path_iterator(std::string(std::forward<T>(searchpath))) + { } + + // TODO: this doesn't work with C arrays (only vector, std::array, etc.) + template <typename T> + path_iterator(T &&paths, std::enable_if_t<std::is_constructible<std::string, typename std::remove_reference_t<T>::value_type>::value, int> = 0) + : path_iterator(concatenate_paths(std::forward<T>(paths))) + { m_separator = '\0'; } + // assignment operators path_iterator &operator=(path_iterator &&that); path_iterator &operator=(path_iterator const &that); @@ -46,9 +64,30 @@ public: void reset(); private: + // helpers + template <typename T> + static std::string concatenate_paths(T &&paths) + { + std::string result; + auto it(std::begin(paths)); + if (std::end(paths) != it) + { + result.append(*it); + ++it; + } + while (std::end(paths) != it) + { + result.append(1, '\0'); + result.append(*it); + ++it; + } + return result; + } + // internal state std::string m_searchpath; std::string::const_iterator m_current; + char m_separator; bool m_is_first; }; @@ -83,10 +122,24 @@ private: class emu_file { + enum empty_t { EMPTY }; + using searchpath_vector = std::vector<std::pair<path_iterator, std::string> >; + public: // file open/creation emu_file(u32 openflags); - emu_file(std::string &&searchpath, u32 openflags); + template <typename T> + emu_file(T &&searchpath, std::enable_if_t<std::is_constructible<path_iterator, T>::value, u32> openflags) + : emu_file(path_iterator(std::forward<T>(searchpath)), openflags) + { } + template <typename T, typename U, typename V, typename... W> + emu_file(T &&searchpath, U &&x, V &&y, W &&... z) + : emu_file(0U, EMPTY) + { + m_iterator.reserve(sizeof...(W) + 1); + m_mediapaths.reserve(sizeof...(W) + 1); + set_searchpaths(std::forward<T>(searchpath), std::forward<U>(x), std::forward<V>(y), std::forward<W>(z)...); + } virtual ~emu_file(); // getters @@ -96,20 +149,15 @@ public: const char *fullpath() const { return m_fullpath.c_str(); } u32 openflags() const { return m_openflags; } util::hash_collection &hashes(const char *types); - bool restrict_to_mediapath() const { return m_restrict_to_mediapath; } - bool part_of_mediapath(std::string path); // setters void remove_on_close() { m_remove_on_close = true; } void set_openflags(u32 openflags) { assert(!m_file); m_openflags = openflags; } - void set_restrict_to_mediapath(bool rtmp = true) { m_restrict_to_mediapath = rtmp; } + void set_restrict_to_mediapath(int rtmp) { m_restrict_to_mediapath = rtmp; } // open/close osd_file::error open(const std::string &name); osd_file::error open(const std::string &name, u32 crc); - osd_file::error open(const std::string &name1, const std::string &name2, u32 crc); - osd_file::error open(const std::string &name1, const std::string &name2, const std::string &name3, u32 crc); - osd_file::error open(const std::string &name1, const std::string &name2, const std::string &name3, const std::string &name4, u32 crc); osd_file::error open_next(); osd_file::error open_ram(const void *data, u32 length); void close(); @@ -142,7 +190,26 @@ public: void flush(); private: - bool compressed_file_ready(void); + emu_file(u32 openflags, empty_t); + emu_file(path_iterator &&searchpath, u32 openflags); + + template <typename T> + void set_searchpaths(T &&searchpath, u32 openflags) + { + m_iterator.emplace_back(searchpath, ""); + m_mediapaths.emplace_back(std::forward<T>(searchpath), ""); + m_openflags = openflags; + } + template <typename T, typename U, typename V, typename... W> + void set_searchpaths(T &&searchpath, U &&x, V &&y, W &&... z) + { + m_iterator.emplace_back(searchpath, ""); + m_mediapaths.emplace_back(std::forward<T>(searchpath), ""); + set_searchpaths(std::forward<U>(x), std::forward<V>(y), std::forward<W>(z)...); + } + + bool part_of_mediapath(const std::string &path); + bool compressed_file_ready(); // internal helpers osd_file::error attempt_zipped(); @@ -152,18 +219,36 @@ private: std::string m_filename; // original filename provided std::string m_fullpath; // full filename util::core_file::ptr m_file; // core file pointer - path_iterator m_iterator; // iterator for paths - path_iterator m_mediapaths; // media-path iterator + searchpath_vector m_iterator; // iterator for paths + searchpath_vector m_mediapaths; // media-path iterator + bool m_first; // true if this is the start of iteration u32 m_crc; // file's CRC u32 m_openflags; // flags we used for the open util::hash_collection m_hashes; // collection of hashes std::unique_ptr<util::archive_file> m_zipfile; // ZIP file pointer - std::vector<u8> m_zipdata; // ZIP file data - u64 m_ziplength; // ZIP file length + std::vector<u8> m_zipdata; // ZIP file data + u64 m_ziplength; // ZIP file length - bool m_remove_on_close; // flag: remove the file when closing - bool m_restrict_to_mediapath; // flag: restrict to paths inside the media-path + bool m_remove_on_close; // flag: remove the file when closing + int m_restrict_to_mediapath; // flag: restrict to paths inside the media-path }; + +extern template path_iterator::path_iterator(char *&, int); +extern template path_iterator::path_iterator(char * const &, int); +extern template path_iterator::path_iterator(char const *&, int); +extern template path_iterator::path_iterator(char const * const &, int); +extern template path_iterator::path_iterator(std::vector<std::string> &, int); +extern template path_iterator::path_iterator(const std::vector<std::string> &, int); + +extern template emu_file::emu_file(std::string &, u32); +extern template emu_file::emu_file(const std::string &, u32); +extern template emu_file::emu_file(char *&, u32); +extern template emu_file::emu_file(char * const &, u32); +extern template emu_file::emu_file(char const *&, u32); +extern template emu_file::emu_file(char const * const &, u32); +extern template emu_file::emu_file(std::vector<std::string> &, u32); +extern template emu_file::emu_file(const std::vector<std::string> &, u32); + #endif // MAME_EMU_FILEIO_H diff --git a/src/emu/render.cpp b/src/emu/render.cpp index f35ab90d431..8501bdf99df 100644 --- a/src/emu/render.cpp +++ b/src/emu/render.cpp @@ -1561,11 +1561,12 @@ void render_target::load_additional_layout_files(const char *basename, bool have bool have_override = false; // if override_artwork defined, load that and skip artwork other than default - if (m_manager.machine().options().override_artwork()) + const char *const override_art = m_manager.machine().options().override_artwork(); + if (override_art && *override_art) { - if (load_layout_file(m_manager.machine().options().override_artwork(), m_manager.machine().options().override_artwork())) + if (load_layout_file(override_art, override_art)) have_override = true; - else if (load_layout_file(m_manager.machine().options().override_artwork(), "default")) + else if (load_layout_file(override_art, "default")) have_override = true; } @@ -1582,7 +1583,7 @@ void render_target::load_additional_layout_files(const char *basename, bool have have_artwork = true; // if a default view has been specified, use that as a fallback - if (system.default_layout != nullptr) + if (system.default_layout) have_default |= load_layout_file(nullptr, *system.default_layout); m_manager.machine().config().apply_default_layouts( [this, &have_default] (device_t &dev, internal_layout const &layout) @@ -1590,36 +1591,30 @@ void render_target::load_additional_layout_files(const char *basename, bool have // try to load another file based on the parent driver name int cloneof = driver_list::clone(system); - if (cloneof != -1) + while (0 <= cloneof) { if (!load_layout_file(driver_list::driver(cloneof).name, driver_list::driver(cloneof).name)) have_artwork |= load_layout_file(driver_list::driver(cloneof).name, "default"); else have_artwork = true; + + // Check the parent of the parent to cover bios based artwork + const game_driver &parent(driver_list::driver(cloneof)); + cloneof = driver_list::clone(parent); } - // Check the parent of the parent to cover bios based artwork - if (cloneof != -1) { - const game_driver &clone(driver_list::driver(cloneof)); - int cloneofclone = driver_list::clone(clone); - if (cloneofclone != -1 && cloneofclone != cloneof) + // Use fallback artwork if defined and no artwork has been found yet + if (!have_artwork) + { + const char *const fallback_art = m_manager.machine().options().fallback_artwork(); + if (fallback_art && *fallback_art) { - if (!load_layout_file(driver_list::driver(cloneofclone).name, driver_list::driver(cloneofclone).name)) - have_artwork |= load_layout_file(driver_list::driver(cloneofclone).name, "default"); + if (!load_layout_file(fallback_art, fallback_art)) + have_artwork |= load_layout_file(fallback_art, "default"); else have_artwork = true; } } - - // Use fallback artwork if defined and no artwork has been found yet - if (!have_artwork && m_manager.machine().options().fallback_artwork()) - { - if (!load_layout_file(m_manager.machine().options().fallback_artwork(), m_manager.machine().options().fallback_artwork())) - have_artwork |= load_layout_file(m_manager.machine().options().fallback_artwork(), "default"); - else - have_artwork = true; - } - } // local screen info to avoid repeated code @@ -2041,6 +2036,7 @@ bool render_target::load_layout_file(const char *dirname, const char *filename) // attempt to open the file; bail if we can't emu_file layoutfile(m_manager.machine().options().art_path(), OPEN_FLAG_READ); + layoutfile.set_restrict_to_mediapath(1); osd_file::error const filerr(layoutfile.open(fname)); if (filerr != osd_file::error::NONE) return false; 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); } } } diff --git a/src/emu/romload.h b/src/emu/romload.h index 87f3054326d..7db106a52ce 100644 --- a/src/emu/romload.h +++ b/src/emu/romload.h @@ -6,15 +6,18 @@ ROM loading functions. *********************************************************************/ - -#pragma once - #ifndef MAME_EMU_ROMLOAD_H #define MAME_EMU_ROMLOAD_H +#pragma once + #include "chd.h" +#include <functional> +#include <initializer_list> +#include <string> #include <type_traits> +#include <vector> /*************************************************************************** @@ -420,6 +423,10 @@ public: void load_software_part_region(device_t &device, software_list_device &swlist, const char *swname, const rom_entry *start_region); + /* open a disk image, searching up the parent and loading by checksum */ + static chd_error open_disk_image(const emu_options &options, const device_t &device, const rom_entry *romp, chd_file &image_chd); + static chd_error open_disk_image(const emu_options &options, software_list_device &swlist, const software_info &swinfo, const rom_entry *romp, chd_file &image_chd); + private: void determine_bios_rom(device_t &device, const char *specbios); void count_roms(); @@ -430,18 +437,18 @@ private: void display_loading_rom_message(const char *name, bool from_list); void display_rom_load_results(bool from_list); void region_post_process(memory_region *region, bool invert); - std::unique_ptr<emu_file> 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> 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); + std::unique_ptr<emu_file> 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); int rom_fread(emu_file *file, u8 *buffer, int length, const rom_entry *parent_region); int read_rom_data(emu_file *file, const rom_entry *parent_region, const rom_entry *romp); void fill_rom_data(const rom_entry *romp); void copy_rom_data(const rom_entry *romp); - void process_rom_entries(const device_t &device, const char *regiontag, const rom_entry *parent_region, const rom_entry *romp, bool from_list); + void 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); chd_error open_disk_diff(emu_options &options, const rom_entry *romp, chd_file &source, chd_file &diff_chd); - void process_disk_entries(const char *regiontag, const rom_entry *parent_region, const rom_entry *romp, const char *locationtag); + void 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); void normalize_flags_for_device(const char *rgntag, u8 &width, endianness_t &endian); void process_region_list(); - // internal state running_machine & m_machine; // reference to our machine @@ -465,8 +472,6 @@ private: /* ----- Helpers ----- */ -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); - /* return pointer to the first ROM region within a source */ const rom_entry *rom_first_region(const device_t &device); @@ -491,8 +496,4 @@ const rom_entry *rom_next_parameter(const rom_entry *romp); // builds a rom_entry vector from a tiny_rom_entry array std::vector<rom_entry> rom_build_entries(const tiny_rom_entry *tinyentries); - -/* 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); - #endif // MAME_EMU_ROMLOAD_H diff --git a/src/emu/softlist_dev.cpp b/src/emu/softlist_dev.cpp index 9d5e03d9b3a..d33e357ebc7 100644 --- a/src/emu/softlist_dev.cpp +++ b/src/emu/softlist_dev.cpp @@ -9,10 +9,11 @@ ***************************************************************************/ #include "emu.h" -#include "emuopts.h" +#include "softlist_dev.h" + #include "diimage.h" +#include "emuopts.h" #include "romload.h" -#include "softlist_dev.h" #include "validity.h" #include <cctype> diff --git a/src/frontend/mame/audit.cpp b/src/frontend/mame/audit.cpp index 256f9876f1b..3ff706d2c8a 100644 --- a/src/frontend/mame/audit.cpp +++ b/src/frontend/mame/audit.cpp @@ -9,14 +9,19 @@ ***************************************************************************/ #include "emu.h" -#include "emuopts.h" #include "audit.h" -#include "chd.h" + +#include "sound/samples.h" + +#include "emuopts.h" #include "drivenum.h" #include "romload.h" -#include "sound/samples.h" #include "softlist_dev.h" +#include "chd.h" + +#include <algorithm> + //************************************************************************** // CORE FUNCTIONS @@ -29,7 +34,6 @@ media_auditor::media_auditor(const driver_enumerator &enumerator) : m_enumerator(enumerator) , m_validation(AUDIT_VALIDATE_FULL) - , m_searchpath() { } @@ -53,22 +57,19 @@ media_auditor::summary media_auditor::audit_media(const char *validation) std::size_t shared_required = 0; // iterate over devices and regions + std::vector<std::string> searchpath; for (device_t &device : device_iterator(m_enumerator.config()->root_device())) { - // determine the search path for this source and iterate through the regions - m_searchpath.clear(); - for (const std::string &path : device.searchpath()) - { - if (!m_searchpath.empty()) - m_searchpath += ';'; - m_searchpath += path; - } + searchpath.clear(); // now iterate over regions and ROMs within for (const rom_entry *region = rom_first_region(device); region; region = rom_next_region(region)) { for (const rom_entry *rom = rom_first_file(region); rom; rom = rom_next_file(rom)) { + if (searchpath.empty()) + searchpath = device.searchpath(); + char const *const name(ROM_GETNAME(rom)); util::hash_collection const hashes(ROM_GETHASHDATA(rom)); device_t *const shared_device(find_shared_device(device, name, hashes, ROM_GETLENGTH(rom))); @@ -83,9 +84,9 @@ media_auditor::summary media_auditor::audit_media(const char *validation) audit_record *record = nullptr; if (ROMREGION_ISROMDATA(region)) - record = &audit_one_rom(rom); + record = &audit_one_rom(searchpath, rom); else if (ROMREGION_ISDISKDATA(region)) - record = &audit_one_disk(rom, nullptr); + record = &audit_one_disk(rom, device); if (record) { @@ -126,12 +127,32 @@ media_auditor::summary media_auditor::audit_device(device_t &device, const char // store validation for later m_validation = validation; - m_searchpath = device.shortname(); std::size_t found = 0; std::size_t required = 0; - audit_regions(rom_first_region(device), nullptr, found, required); + std::vector<std::string> searchpath; + audit_regions( + [this, &device, &searchpath] (rom_entry const *region, rom_entry const *rom) -> audit_record const * + { + if (ROMREGION_ISROMDATA(region)) + { + if (searchpath.empty()) + searchpath = device.searchpath(); + return &audit_one_rom(searchpath, rom); + } + else if (ROMREGION_ISDISKDATA(region)) + { + return &audit_one_disk(rom, device); + } + else + { + return nullptr; + } + }, + rom_first_region(device), + found, + required); if ((found == 0) && (required > 0)) { @@ -147,7 +168,7 @@ media_auditor::summary media_auditor::audit_device(device_t &device, const char //------------------------------------------------- // audit_software //------------------------------------------------- -media_auditor::summary media_auditor::audit_software(const std::string &list_name, const software_info *swinfo, const char *validation) +media_auditor::summary media_auditor::audit_software(software_list_device &swlist, const software_info &swinfo, const char *validation) { // start fresh m_record_list.clear(); @@ -155,21 +176,57 @@ media_auditor::summary media_auditor::audit_software(const std::string &list_nam // store validation for later m_validation = validation; - std::string combinedpath(util::string_format("%s;%s%s%s", swinfo->shortname(), list_name, PATH_SEPARATOR, swinfo->shortname())); - std::string locationtag(util::string_format("%s%%%s%%", list_name, swinfo->shortname())); - if (!swinfo->parentname().empty()) - { - locationtag.append(swinfo->parentname()); - combinedpath.append(util::string_format(";%s;%s%s%s", swinfo->parentname(), list_name, PATH_SEPARATOR, swinfo->parentname())); - } - m_searchpath = combinedpath; - std::size_t found = 0; std::size_t required = 0; // now iterate over software parts - for (const software_part &part : swinfo->parts()) - audit_regions(part.romdata().data(), locationtag.c_str(), found, required); + std::vector<std::string> searchpath; + auto const do_audit = + [this, &swlist, &swinfo, &searchpath] (rom_entry const *region, rom_entry const *rom) -> audit_record const * + { + if (ROMREGION_ISROMDATA(region)) + { + if (searchpath.empty()) + { + std::vector<software_info const *> parents; + + // search <rompath>/<list>/<software> following parents + searchpath.emplace_back(util::string_format("%s" PATH_SEPARATOR "%s", swlist.list_name(), swinfo.shortname())); + for (software_info const *i = &swinfo; i && !i->parentname().empty(); i = swlist.find(i->parentname())) + { + if (std::find(parents.begin(), parents.end(), i) != parents.end()) + { + osd_printf_warning("WARNING: software parent/clone relationships form a loop (%s:%s)\n", swlist.list_name(), swinfo.shortname()); + break; + } + parents.emplace_back(i); + searchpath.emplace_back(util::string_format("%s" PATH_SEPARATOR "%s", swlist.list_name(), i->parentname())); + } + + // search <rompath>/<software> following parents + searchpath.emplace_back(swinfo.shortname()); + parents.clear(); + for (software_info const *i = &swinfo; i && !i->parentname().empty(); i = swlist.find(i->parentname())) + { + if (std::find(parents.begin(), parents.end(), i) != parents.end()) + break; + parents.emplace_back(i); + searchpath.emplace_back(i->parentname()); + } + } + return &audit_one_rom(searchpath, rom); + } + else if (ROMREGION_ISDISKDATA(region)) + { + return &audit_one_disk(rom, swlist, swinfo); + } + else + { + return nullptr; + } + }; + for (const software_part &part : swinfo.parts()) + audit_regions(do_audit, part.romdata().data(), found, required); if ((found == 0) && (required > 0)) { @@ -178,7 +235,7 @@ media_auditor::summary media_auditor::audit_software(const std::string &list_nam } // return a summary - return summarize(list_name.c_str()); + return summarize(swlist.list_name().c_str()); } @@ -342,25 +399,22 @@ media_auditor::summary media_auditor::summarize(const char *name, std::ostream * // audit_regions - validate/count for regions //------------------------------------------------- -void media_auditor::audit_regions(const rom_entry *region, const char *locationtag, std::size_t &found, std::size_t &required) +template <typename T> +void media_auditor::audit_regions(T do_audit, const rom_entry *region, std::size_t &found, std::size_t &required) { // now iterate over regions + std::vector<std::string> searchpath; for ( ; region; region = rom_next_region(region)) { // now iterate over rom definitions - for (const rom_entry *rom = rom_first_file(region); rom; rom = rom_next_file(rom)) + for (rom_entry const *rom = rom_first_file(region); rom; rom = rom_next_file(rom)) { - util::hash_collection const hashes(ROM_GETHASHDATA(rom)); - // count the number of files with hashes + util::hash_collection const hashes(ROM_GETHASHDATA(rom)); if (!hashes.flag(util::hash_collection::FLAG_NO_DUMP) && !ROM_ISOPTIONAL(rom)) required++; - audit_record const *record = nullptr; - if (ROMREGION_ISROMDATA(region)) - record = &audit_one_rom(rom); - else if (ROMREGION_ISDISKDATA(region)) - record = &audit_one_disk(rom, locationtag); + audit_record const *const record = do_audit(region, rom); // count the number of files that are found. if (record && ((record->status() == audit_status::GOOD) || (record->status() == audit_status::FOUND_INVALID))) @@ -374,7 +428,7 @@ void media_auditor::audit_regions(const rom_entry *region, const char *locationt // audit_one_rom - validate a single ROM entry //------------------------------------------------- -media_auditor::audit_record &media_auditor::audit_one_rom(const rom_entry *rom) +media_auditor::audit_record &media_auditor::audit_one_rom(const std::vector<std::string> &searchpath, const rom_entry *rom) { // allocate and append a new record audit_record &record = *m_record_list.emplace(m_record_list.end(), *rom, media_type::ROM); @@ -384,27 +438,19 @@ media_auditor::audit_record &media_auditor::audit_one_rom(const rom_entry *rom) bool const has_crc = record.expected_hashes().crc(crc); // find the file and checksum it, getting the file length along the way - emu_file file(m_enumerator.options().media_path(), OPEN_FLAG_READ | OPEN_FLAG_NO_PRELOAD); - file.set_restrict_to_mediapath(true); - path_iterator path(m_searchpath); - std::string curpath; - // FIXME: needs to be adjusted to match ROM loading behaviour - while (path.next(curpath, record.name())) - { - // open the file if we can - osd_file::error filerr; - if (has_crc) - filerr = file.open(curpath, crc); - else - filerr = file.open(curpath); + emu_file file(m_enumerator.options().media_path(), searchpath, OPEN_FLAG_READ | OPEN_FLAG_NO_PRELOAD); + file.set_restrict_to_mediapath(1); - // if it worked, get the actual length and hashes, then stop - if (filerr == osd_file::error::NONE) - { - record.set_actual(file.hashes(m_validation), file.size()); - break; - } - } + // open the file if we can + osd_file::error filerr; + if (has_crc) + filerr = file.open(record.name(), crc); + else + filerr = file.open(record.name()); + + // if it worked, get the actual length and hashes, then stop + if (filerr == osd_file::error::NONE) + record.set_actual(file.hashes(m_validation), file.size()); // compute the final status compute_status(record, rom, record.actual_length() != 0); @@ -416,14 +462,15 @@ media_auditor::audit_record &media_auditor::audit_one_rom(const rom_entry *rom) // audit_one_disk - validate a single disk entry //------------------------------------------------- -media_auditor::audit_record &media_auditor::audit_one_disk(const rom_entry *rom, const char *locationtag) +template <typename... T> +media_auditor::audit_record &media_auditor::audit_one_disk(const rom_entry *rom, T &&... args) { // allocate and append a new record audit_record &record = *m_record_list.emplace(m_record_list.end(), *rom, media_type::DISK); // open the disk chd_file source; - chd_error err = chd_error(open_disk_image(m_enumerator.options(), &m_enumerator.driver(), rom, source, locationtag)); + const chd_error err = rom_load_manager::open_disk_image(m_enumerator.options(), std::forward<T>(args)..., rom, source); // if we succeeded, get the hashes if (err == CHDERR_NONE) diff --git a/src/frontend/mame/audit.h b/src/frontend/mame/audit.h index 42296d9260b..511b4c98845 100644 --- a/src/frontend/mame/audit.h +++ b/src/frontend/mame/audit.h @@ -7,12 +7,11 @@ ROM, disk, and sample auditing functions. ***************************************************************************/ - -#pragma once - #ifndef MAME_FRONTEND_AUDIT_H #define MAME_FRONTEND_AUDIT_H +#pragma once + #include "hash.h" #include <iosfwd> @@ -38,6 +37,7 @@ // forward declarations class driver_enumerator; +class software_list_device; @@ -137,15 +137,15 @@ public: private: // internal state - media_type m_type; // type of item that was audited - audit_status m_status; // status of audit on this item - audit_substatus m_substatus; // finer-detail status - const char * m_name; // name of item - uint64_t m_explength; // expected length of item - uint64_t m_length; // actual length of item - util::hash_collection m_exphashes; // expected hash data - util::hash_collection m_hashes; // actual hash information - device_t * m_shared_device; // device that shares the rom + media_type m_type; // type of item that was audited + audit_status m_status; // status of audit on this item + audit_substatus m_substatus; // finer-detail status + const char * m_name; // name of item + uint64_t m_explength; // expected length of item + uint64_t m_length; // actual length of item + util::hash_collection m_exphashes; // expected hash data + util::hash_collection m_hashes; // actual hash information + device_t * m_shared_device; // device that shares the rom }; using record_list = std::list<audit_record>; @@ -158,15 +158,15 @@ public: // audit operations summary audit_media(const char *validation = AUDIT_VALIDATE_FULL); summary audit_device(device_t &device, const char *validation = AUDIT_VALIDATE_FULL); - summary audit_software(const std::string &list_name, const software_info *swinfo, const char *validation = AUDIT_VALIDATE_FULL); + summary audit_software(software_list_device &swlist, const software_info &swinfo, const char *validation = AUDIT_VALIDATE_FULL); summary audit_samples(); summary summarize(const char *name, std::ostream *output = nullptr) const; private: // internal helpers - void audit_regions(const rom_entry *region, const char *locationtag, std::size_t &found, std::size_t &required); - audit_record &audit_one_rom(const rom_entry *rom); - audit_record &audit_one_disk(const rom_entry *rom, const char *locationtag); + template <typename T> void audit_regions(T do_audit, const rom_entry *region, std::size_t &found, std::size_t &required); + audit_record &audit_one_rom(const std::vector<std::string> &searchpath, const rom_entry *rom); + template <typename... T> audit_record &audit_one_disk(const rom_entry *rom, T &&... args); void compute_status(audit_record &record, const rom_entry *rom, bool found); device_t *find_shared_device(device_t &device, const char *name, const util::hash_collection &romhashes, uint64_t romlength); @@ -174,7 +174,6 @@ private: record_list m_record_list; const driver_enumerator & m_enumerator; const char * m_validation; - std::string m_searchpath; }; diff --git a/src/frontend/mame/clifront.cpp b/src/frontend/mame/clifront.cpp index fa63a283465..aa5ec7606b8 100644 --- a/src/frontend/mame/clifront.cpp +++ b/src/frontend/mame/clifront.cpp @@ -1279,7 +1279,7 @@ void cli_frontend::verifysoftware(const std::vector<std::string> &args) nrlists++; for (const software_info &swinfo : swlistdev.get_info()) { - media_auditor::summary summary = auditor.audit_software(swlistdev.list_name(), &swinfo, AUDIT_VALIDATE_FAST); + media_auditor::summary summary = auditor.audit_software(swlistdev, swinfo, AUDIT_VALIDATE_FAST); print_summary( auditor, summary, false, @@ -1386,7 +1386,7 @@ void cli_frontend::verifysoftlist(const std::vector<std::string> &args) // Get the actual software list contents for (const software_info &swinfo : swlistdev.get_info()) { - media_auditor::summary summary = auditor.audit_software(swlistdev.list_name(), &swinfo, AUDIT_VALIDATE_FAST); + media_auditor::summary summary = auditor.audit_software(swlistdev, swinfo, AUDIT_VALIDATE_FAST); print_summary( auditor, summary, false, diff --git a/src/frontend/mame/ui/imgcntrl.cpp b/src/frontend/mame/ui/imgcntrl.cpp index 8d3ef0974c1..c0b5c44f1af 100644 --- a/src/frontend/mame/ui/imgcntrl.cpp +++ b/src/frontend/mame/ui/imgcntrl.cpp @@ -138,7 +138,7 @@ void menu_control_device_image::load_software_part() driver_enumerator drivlist(machine().options(), machine().options().system_name()); drivlist.next(); media_auditor auditor(drivlist); - media_auditor::summary summary = auditor.audit_software(m_sld->list_name(), (software_info *)m_swi, AUDIT_VALIDATE_FAST); + media_auditor::summary summary = auditor.audit_software(*m_sld, *m_swi, AUDIT_VALIDATE_FAST); // if everything looks good, load software if (summary == media_auditor::CORRECT || summary == media_auditor::BEST_AVAILABLE || summary == media_auditor::NONE_NEEDED) { diff --git a/src/frontend/mame/ui/selgame.cpp b/src/frontend/mame/ui/selgame.cpp index 6bbe0c874f1..1f7603b4f31 100644 --- a/src/frontend/mame/ui/selgame.cpp +++ b/src/frontend/mame/ui/selgame.cpp @@ -897,7 +897,7 @@ void menu_select_game::inkey_select_favorite(const event *menu_event) software_list_device *swlist = software_list_device::find_by_name(*drv.config(), ui_swinfo->listname); const software_info *swinfo = swlist->find(ui_swinfo->shortname); - media_auditor::summary const summary = auditor.audit_software(swlist->list_name(), swinfo, AUDIT_VALIDATE_FAST); + media_auditor::summary const summary = auditor.audit_software(*swlist, *swinfo, AUDIT_VALIDATE_FAST); if (summary == media_auditor::CORRECT || summary == media_auditor::BEST_AVAILABLE || summary == media_auditor::NONE_NEEDED) { diff --git a/src/frontend/mame/ui/selmenu.cpp b/src/frontend/mame/ui/selmenu.cpp index 4fd71e2798e..92bdd3ada64 100644 --- a/src/frontend/mame/ui/selmenu.cpp +++ b/src/frontend/mame/ui/selmenu.cpp @@ -2282,8 +2282,7 @@ void menu_select_launch::arts_render(float origx1, float origy1, float origx2, f // loads the image if necessary if (!m_cache->snapx_driver_is(driver) || !snapx_valid() || m_switch_image) { - emu_file snapfile(searchstr.c_str(), OPEN_FLAG_READ); - snapfile.set_restrict_to_mediapath(true); + emu_file snapfile(searchstr, OPEN_FLAG_READ); bitmap_argb32 tmp_bitmap; // try to load snapshot first from saved "0000.png" file diff --git a/src/frontend/mame/ui/selsoft.cpp b/src/frontend/mame/ui/selsoft.cpp index 4bbfb6fd294..84fa109bea6 100644 --- a/src/frontend/mame/ui/selsoft.cpp +++ b/src/frontend/mame/ui/selsoft.cpp @@ -486,7 +486,7 @@ void menu_select_software::inkey_select(const event *menu_event) software_list_device *swlist = software_list_device::find_by_name(*drivlist.config(), ui_swinfo->listname); const software_info *swinfo = swlist->find(ui_swinfo->shortname); - media_auditor::summary const summary = auditor.audit_software(swlist->list_name(), swinfo, AUDIT_VALIDATE_FAST); + media_auditor::summary const summary = auditor.audit_software(*swlist, *swinfo, AUDIT_VALIDATE_FAST); if (summary == media_auditor::CORRECT || summary == media_auditor::BEST_AVAILABLE || summary == media_auditor::NONE_NEEDED) { diff --git a/src/mame/mame.lst b/src/mame/mame.lst index ea8d18c018a..885557eb4df 100644 --- a/src/mame/mame.lst +++ b/src/mame/mame.lst @@ -39926,7 +39926,7 @@ tvcapcom // theboat // @source:tvgame.cpp -tvgame // +tvgame // 2011 @source:twin16.cpp cuebrickj // GX903 (c) 1989 (Japan) |