mirror of
https://github.com/holub/mame
synced 2025-06-03 11:26:56 +03:00
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.
This commit is contained in:
parent
f53eb6022e
commit
aca0aaaa3d
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
@ -499,51 +641,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
|
||||
//-------------------------------------------------
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user