diff options
author | 2020-10-04 12:52:47 +1100 | |
---|---|---|
committer | 2020-10-04 12:52:47 +1100 | |
commit | aca0aaaa3d6870f0372316912031794329a5ca41 (patch) | |
tree | 5fb3900ceb6f97c39499439984e74ce8605c62b0 | |
parent | f53eb6022eac11db43c36462e3dc84714800cde9 (diff) |
frontend/mame/audit.cpp: Better heuristics for matching shared ROMs.
This should address at least some of the remaining issues from MT07410
and GitHub #7314. If this causes issues like MT07756 to reappear, it
will need to be fixed/reverted before release.
-rw-r--r-- | src/emu/romload.cpp | 6 | ||||
-rw-r--r-- | src/emu/romload.h | 1 | ||||
-rw-r--r-- | src/frontend/mame/audit.cpp | 205 | ||||
-rw-r--r-- | src/frontend/mame/audit.h | 1 |
4 files changed, 157 insertions, 56 deletions
diff --git a/src/emu/romload.cpp b/src/emu/romload.cpp index ef48d87fab0..a728a6a7660 100644 --- a/src/emu/romload.cpp +++ b/src/emu/romload.cpp @@ -192,7 +192,11 @@ chd_error do_open_disk(const emu_options &options, std::initializer_list<std::re const rom_entry *rom_first_region(const device_t &device) { - const rom_entry *romp = &device.rom_region_vector().front(); + return rom_first_region(&device.rom_region_vector().front()); +} + +const rom_entry *rom_first_region(const rom_entry *romp) +{ while (ROMENTRY_ISPARAMETER(romp) || ROMENTRY_ISSYSTEM_BIOS(romp) || ROMENTRY_ISDEFAULT_BIOS(romp)) romp++; return !ROMENTRY_ISEND(romp) ? romp : nullptr; diff --git a/src/emu/romload.h b/src/emu/romload.h index 0a558a790b1..2b9955a1590 100644 --- a/src/emu/romload.h +++ b/src/emu/romload.h @@ -479,6 +479,7 @@ private: /* return pointer to the first ROM region within a source */ const rom_entry *rom_first_region(const device_t &device); +const rom_entry *rom_first_region(const rom_entry *romp); /* return pointer to the next ROM region within a source */ const rom_entry *rom_next_region(const rom_entry *romp); diff --git a/src/frontend/mame/audit.cpp b/src/frontend/mame/audit.cpp index c8eb6d52de5..6509b264e49 100644 --- a/src/frontend/mame/audit.cpp +++ b/src/frontend/mame/audit.cpp @@ -22,6 +22,111 @@ #include <algorithm> +//#define VERBOSE 1 +#define LOG_OUTPUT_FUNC osd_printf_verbose +#include "logmacro.h" + + +namespace { + +struct parent_rom +{ + parent_rom(device_type t, rom_entry const *r) : type(t), name(ROM_GETNAME(r)), hashes(ROM_GETHASHDATA(r)), length(rom_file_size(r)) { } + + std::reference_wrapper<std::remove_reference_t<device_type> > type; + std::string name; + util::hash_collection hashes; + uint64_t length; +}; + + +class parent_rom_vector : public std::vector<parent_rom> +{ +public: + using std::vector<parent_rom>::vector; + + std::add_pointer_t<device_type> find_shared_device(device_t ¤t, char const *name, util::hash_collection const &hashes, uint64_t length) const + { + // if we're examining a child device, it will always have a perfect match + if (current.owner()) + return ¤t.type(); + + // scan backwards through parents for a matching definition + bool const dumped(!hashes.flag(util::hash_collection::FLAG_NO_DUMP)); + std::add_pointer_t<device_type> best(nullptr); + for (const_reverse_iterator it = crbegin(); crend() != it; ++it) + { + if (it->length == length) + { + if (dumped) + { + if (it->hashes == hashes) + return &it->type.get(); + } + else if (it->name == name) + { + if (it->hashes.flag(util::hash_collection::FLAG_NO_DUMP)) + return &it->type.get(); + else if (!best) + best = &it->type.get(); + } + } + } + return best; + } + + std::pair<std::add_pointer_t<device_type>, bool> actual_matches_shared(device_t ¤t, media_auditor::audit_record const &record) + { + // no result if no matching file was found + if ((record.status() != media_auditor::audit_status::GOOD) && (record.status() != media_auditor::audit_status::FOUND_INVALID)) + return std::make_pair(nullptr, false); + + // if we're examining a child device, scan it first + bool matches_device_undumped(false); + if (current.owner()) + { + for (const rom_entry *region = rom_first_region(current); region; region = rom_next_region(region)) + { + for (const rom_entry *rom = rom_first_file(region); rom; rom = rom_next_file(rom)) + { + if (rom_file_size(rom) == record.actual_length()) + { + util::hash_collection const hashes(ROM_GETHASHDATA(rom)); + if (hashes == record.actual_hashes()) + return std::make_pair(¤t.type(), empty()); + else if (hashes.flag(util::hash_collection::FLAG_NO_DUMP) && (rom->name() == record.name())) + matches_device_undumped = true; + } + } + } + } + + // look for a matching parent ROM + std::add_pointer_t<device_type> closest_bad(nullptr); + for (const_reverse_iterator it = crbegin(); crend() != it; ++it) + { + if (it->length == record.actual_length()) + { + if (it->hashes == record.actual_hashes()) + return std::make_pair(&it->type.get(), it->type.get() == front().type.get()); + else if (it->hashes.flag(util::hash_collection::FLAG_NO_DUMP) && (it->name == record.name())) + closest_bad = &it->type.get(); + } + } + + // fall back to the nearest bad dump + if (closest_bad) + return std::make_pair(closest_bad, front().type.get() == *closest_bad); + else if (matches_device_undumped) + return std::make_pair(¤t.type(), empty()); + else + return std::make_pair(nullptr, false); + } +}; + +} // anonymous namespace + + //************************************************************************** // CORE FUNCTIONS @@ -51,10 +156,29 @@ media_auditor::summary media_auditor::audit_media(const char *validation) // store validation for later m_validation = validation; - std::size_t found = 0; - std::size_t required = 0; - std::size_t shared_found = 0; - std::size_t shared_required = 0; + // first walk the parent chain for required ROMs + parent_rom_vector parentroms; + for (auto drvindex = m_enumerator.find(m_enumerator.driver().parent); 0 <= drvindex; drvindex = m_enumerator.find(m_enumerator.driver(drvindex).parent)) + { + game_driver const &parent(m_enumerator.driver(drvindex)); + LOG("Checking parent %s for ROM files\n", parent.type.shortname()); + std::vector<rom_entry> const roms(rom_build_entries(parent.rom)); + for (rom_entry const *region = rom_first_region(&roms.front()); region; region = rom_next_region(region)) + { + for (rom_entry const *rom = rom_first_file(region); rom; rom = rom_next_file(rom)) + { + LOG("Adding parent ROM %s\n", rom->name()); + parentroms.emplace_back(parent.type, rom); + } + } + } + + // count ROMs required/found + std::size_t found(0); + std::size_t required(0); + std::size_t shared_found(0); + std::size_t shared_required(0); + std::size_t parent_found(0); // iterate over devices and regions std::vector<std::string> searchpath; @@ -68,12 +192,20 @@ media_auditor::summary media_auditor::audit_media(const char *validation) for (const rom_entry *rom = rom_first_file(region); rom; rom = rom_next_file(rom)) { if (searchpath.empty()) + { + LOG("Audit media for device %s(%s)\n", device.shortname(), device.tag()); searchpath = device.searchpath(); + } + // look for a matching parent or device ROM char const *const name(ROM_GETNAME(rom)); util::hash_collection const hashes(ROM_GETHASHDATA(rom)); - bool const dumped = !hashes.flag(util::hash_collection::FLAG_NO_DUMP); - std::add_pointer_t<device_type> const shared_device(find_shared_device(device, name, hashes, rom_file_size(rom))); + bool const dumped(!hashes.flag(util::hash_collection::FLAG_NO_DUMP)); + std::add_pointer_t<device_type> const shared_device(parentroms.find_shared_device(device, name, hashes, rom_file_size(rom))); + if (shared_device) + LOG("File '%s' %s%sdumped shared with %s\n", name, ROM_ISOPTIONAL(rom) ? "optional " : "", dumped ? "" : "un", shared_device->shortname()); + else + LOG("File '%s' %s%sdumped\n", name, ROM_ISOPTIONAL(rom) ? "optional " : "", dumped ? "" : "un"); // count the number of files with hashes if (dumped && !ROM_ISOPTIONAL(rom)) @@ -83,7 +215,7 @@ media_auditor::summary media_auditor::audit_media(const char *validation) shared_required++; } - audit_record *record = nullptr; + audit_record *record(nullptr); if (ROMREGION_ISROMDATA(region)) record = &audit_one_rom(searchpath, rom); else if (ROMREGION_ISDISKDATA(region)) @@ -91,22 +223,32 @@ media_auditor::summary media_auditor::audit_media(const char *validation) if (record) { + // see if the actual content found belongs to a parent + auto const matchesshared(parentroms.actual_matches_shared(device, *record)); + if (matchesshared.first) + LOG("Actual ROM file shared with %sparent %s\n", matchesshared.second ? "immediate " : "", matchesshared.first->shortname()); + // count the number of files that are found. - if ((record->status() == audit_status::GOOD) || ((record->status() == audit_status::FOUND_INVALID) && !find_shared_device(device, name, record->actual_hashes(), record->actual_length()))) + if ((record->status() == audit_status::GOOD) || ((record->status() == audit_status::FOUND_INVALID) && !matchesshared.first)) { found++; if (shared_device) shared_found++; + if (matchesshared.second) + parent_found++; } record->set_shared_device(shared_device); } } } + + if (!searchpath.empty()) + LOG("Total required=%u (shared=%u) found=%u (shared=%u parent=%u)\n", required, shared_required, found, shared_found, parent_found); } // if we only find files that are in the parent & either the set has no unique files or the parent is not found, then assume we don't have the set at all - if ((found == shared_found) && (required > 0) && ((required != shared_required) || (shared_found == 0))) + if ((found == shared_found) && required && ((required != shared_required) || !parent_found)) { m_record_list.clear(); return NOTFOUND; @@ -500,51 +642,6 @@ void media_auditor::compute_status(audit_record &record, const rom_entry *rom, b //------------------------------------------------- -// find_shared_device - return the source that -// shares a media entry with the same hashes -//------------------------------------------------- - -std::add_pointer_t<device_type> media_auditor::find_shared_device(device_t &device, const char *name, const util::hash_collection &romhashes, uint64_t romlength) -{ - bool const dumped = !romhashes.flag(util::hash_collection::FLAG_NO_DUMP); - device_t *highest_device = nullptr; - auto const scan = - [dumped, name, &romhashes, romlength, &highest_device] (device_t &curdev) - { - for (const rom_entry *region = rom_first_region(curdev); region; region = rom_next_region(region)) - { - for (const rom_entry *rom = rom_first_file(region); rom; rom = rom_next_file(rom)) - { - if (rom_file_size(rom) == romlength) - { - util::hash_collection hashes(ROM_GETHASHDATA(rom)); - if ((dumped && hashes == romhashes) || (!dumped && ROM_GETNAME(rom) == name)) - highest_device = &curdev; - } - } - } - }; - - if (device.owner()) - { - // special case for non-root devices - scan(device); - } - else - { - // iterate up the parent chain - for (auto drvindex = m_enumerator.find(m_enumerator.driver().parent); drvindex >= 0; drvindex = m_enumerator.find(m_enumerator.driver(drvindex).parent)) - { - for (device_t &scandevice : device_iterator(m_enumerator.config(drvindex)->root_device())) - scan(scandevice); - } - } - - return highest_device ? &highest_device->type() : nullptr; -} - - -//------------------------------------------------- // audit_record - constructor //------------------------------------------------- diff --git a/src/frontend/mame/audit.h b/src/frontend/mame/audit.h index f70867f5dd0..d1904820f03 100644 --- a/src/frontend/mame/audit.h +++ b/src/frontend/mame/audit.h @@ -166,7 +166,6 @@ private: 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); - std::add_pointer_t<device_type> find_shared_device(device_t &device, const char *name, const util::hash_collection &romhashes, uint64_t romlength); // internal state record_list m_record_list; |