summaryrefslogtreecommitdiffstatshomepage
path: root/src
diff options
context:
space:
mode:
author Vas Crabb <vas@vastheman.com>2020-10-04 12:52:47 +1100
committer Vas Crabb <vas@vastheman.com>2020-10-04 12:52:47 +1100
commitaca0aaaa3d6870f0372316912031794329a5ca41 (patch)
tree5fb3900ceb6f97c39499439984e74ce8605c62b0 /src
parentf53eb6022eac11db43c36462e3dc84714800cde9 (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.
Diffstat (limited to 'src')
-rw-r--r--src/emu/romload.cpp6
-rw-r--r--src/emu/romload.h1
-rw-r--r--src/frontend/mame/audit.cpp205
-rw-r--r--src/frontend/mame/audit.h1
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 &current, 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 &current.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 &current, 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(&current.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(&current.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;