diff options
author | Vas Crabb <vas@vastheman.com> | 2017-08-21 21:45:15 +1000 |
---|---|---|
committer | Vas Crabb <vas@vastheman.com> | 2017-08-21 21:45:15 +1000 |
commit | 90f7fa47c2c28170ab0dd31e17ebcfbc95fe563f (patch) | |
tree | 1d8d5da3d231d35ae949ff5342e46dd4420af857 | |
parent | f9f4ae9d3654a8efb40e4beaf3bffee5f573e0a4 (diff) |
UI improvements:
* Make (Un)Available a polymorphic filter (works in custom filter again)
* Fix bugs and improve performance when detecting systems without ROMs
* Show progress while auditing ROMs
* Use actual INI file format for mame_avail.ini
-rw-r--r-- | src/frontend/mame/ui/auditmenu.cpp | 254 | ||||
-rw-r--r-- | src/frontend/mame/ui/auditmenu.h | 43 | ||||
-rw-r--r-- | src/frontend/mame/ui/menu.cpp | 5 | ||||
-rw-r--r-- | src/frontend/mame/ui/selgame.cpp | 211 | ||||
-rw-r--r-- | src/frontend/mame/ui/selgame.h | 12 | ||||
-rw-r--r-- | src/frontend/mame/ui/slotopt.cpp | 2 | ||||
-rw-r--r-- | src/frontend/mame/ui/slotopt.h | 6 | ||||
-rw-r--r-- | src/frontend/mame/ui/utils.cpp | 52 | ||||
-rw-r--r-- | src/frontend/mame/ui/utils.h | 27 |
9 files changed, 342 insertions, 270 deletions
diff --git a/src/frontend/mame/ui/auditmenu.cpp b/src/frontend/mame/ui/auditmenu.cpp index 23c0eee30ba..0349e1c2949 100644 --- a/src/frontend/mame/ui/auditmenu.cpp +++ b/src/frontend/mame/ui/auditmenu.cpp @@ -1,5 +1,5 @@ // license:BSD-3-Clause -// copyright-holders:Maurizio Petrarota +// copyright-holders:Maurizio Petrarota, Vas Crabb /********************************************************************* ui/auditmenu.cpp @@ -9,39 +9,31 @@ *********************************************************************/ #include "emu.h" +#include "ui/auditmenu.h" + #include "ui/ui.h" -#include "ui/menu.h" + #include "audit.h" -#include "ui/auditmenu.h" #include "drivenum.h" +#include <numeric> + + extern const char UI_VERSION_TAG[]; namespace ui { -//------------------------------------------------- -// sort -//------------------------------------------------- -inline int cs_stricmp(const char *s1, const char *s2) -{ - for (;;) - { - int c1 = tolower(*s1++); - int c2 = tolower(*s2++); - if (c1 == 0 || c1 != c2) - return c1 - c2; - } -} +namespace { -bool sorted_game_list(const game_driver *x, const game_driver *y) -{ - bool clonex = (x->parent[0] != '0'); - bool cloney = (y->parent[0] != '0'); +void *const ITEMREF_START = reinterpret_cast<void *>(std::uintptr_t(1)); + +} // anonymous namespace - if (!clonex && !cloney) - return (cs_stricmp(x->type.fullname(), y->type.fullname()) < 0); - int cx = -1, cy = -1; +bool sorted_game_list(const game_driver *x, const game_driver *y) +{ + bool clonex = (x->parent[0] != '0') || x->parent[1]; + int cx = -1; if (clonex) { cx = driver_list::find(x->parent); @@ -49,6 +41,8 @@ bool sorted_game_list(const game_driver *x, const game_driver *y) clonex = false; } + bool cloney = (y->parent[0] != '0') || y->parent[1]; + int cy = -1; if (cloney) { cy = driver_list::find(y->parent); @@ -57,123 +51,181 @@ bool sorted_game_list(const game_driver *x, const game_driver *y) } if (!clonex && !cloney) - return (cs_stricmp(x->type.fullname(), y->type.fullname()) < 0); + { + return (core_stricmp(x->type.fullname(), y->type.fullname()) < 0); + } else if (clonex && cloney) { - if (!cs_stricmp(x->parent, y->parent)) - return (cs_stricmp(x->type.fullname(), y->type.fullname()) < 0); + if (!core_stricmp(x->parent, y->parent)) + return (core_stricmp(x->type.fullname(), y->type.fullname()) < 0); else - return (cs_stricmp(driver_list::driver(cx).type.fullname(), driver_list::driver(cy).type.fullname()) < 0); + return (core_stricmp(driver_list::driver(cx).type.fullname(), driver_list::driver(cy).type.fullname()) < 0); } else if (!clonex && cloney) { - if (!cs_stricmp(x->name, y->parent)) + if (!core_stricmp(x->name, y->parent)) return true; else - return (cs_stricmp(x->type.fullname(), driver_list::driver(cy).type.fullname()) < 0); + return (core_stricmp(x->type.fullname(), driver_list::driver(cy).type.fullname()) < 0); } else { - if (!cs_stricmp(x->parent, y->name)) + if (!core_stricmp(x->parent, y->name)) return false; else - return (cs_stricmp(driver_list::driver(cx).type.fullname(), y->type.fullname()) < 0); + return (core_stricmp(driver_list::driver(cx).type.fullname(), y->type.fullname()) < 0); } } -//------------------------------------------------- -// ctor / dtor -//------------------------------------------------- -menu_audit::menu_audit(mame_ui_manager &mui, render_container &container, vptr_game &availablesorted, vptr_game &unavailablesorted, int _audit_mode) +menu_audit::menu_audit(mame_ui_manager &mui, render_container &container, std::vector<ui_system_info> &availablesorted, mode audit_mode) : menu(mui, container) + , m_worker_thread() + , m_audit_mode(audit_mode) + , m_total((mode::FAST == audit_mode) + ? std::accumulate(availablesorted.begin(), availablesorted.end(), std::size_t(0), [] (std::size_t n, ui_system_info const &info) { return n + (info.available ? 0 : 1); }) + : availablesorted.size()) , m_availablesorted(availablesorted) - , m_unavailablesorted(unavailablesorted) - , m_audit_mode(_audit_mode) - , m_first(true) + , m_audited(0) + , m_current(nullptr) + , m_phase(phase::CONSENT) { - if (m_audit_mode == 2) + switch (m_audit_mode) { - m_availablesorted.clear(); - m_unavailablesorted.clear(); + case mode::FAST: + m_prompt[0] = util::string_format(_("Audit ROMs for %1$u machines marked unavailable?"), m_total); + break; + case mode::ALL: + m_prompt[0] = util::string_format(_("Audit ROMs for all %1$u machines?"), m_total); + break; } + std::string filename(emulator_info::get_configname()); + filename += "_avail.ini"; + m_prompt[1] = util::string_format(_("(results will be saved to %1$s)"), filename); } menu_audit::~menu_audit() { } -//------------------------------------------------- -// handle -//------------------------------------------------- - -void menu_audit::handle() +void menu_audit::custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) { - process(PROCESS_CUSTOM_ONLY); - - if (m_first) + switch (m_phase) { - ui().draw_text_box(container(), _("Audit in progress..."), ui::text_layout::CENTER, 0.5f, 0.5f, UI_GREEN_COLOR); - m_first = false; - return; + case phase::CONSENT: + draw_text_box( + std::begin(m_prompt), std::end(m_prompt), + x, x2, y - top, y - UI_BOX_TB_BORDER, + ui::text_layout::CENTER, ui::text_layout::NEVER, false, + UI_TEXT_COLOR, UI_GREEN_COLOR, 1.0f); + break; + + case phase::AUDIT: + { + // there's a race here between the total audited being updated and the next driver pointer being loaded + // it doesn't matter because we redraw on every frame anyway so it sorts itself out very quickly + game_driver const *const driver(m_current.load()); + std::size_t const audited(m_audited.load()); + std::string const text(util::string_format( + _("Auditing ROMs for machine %2$u of %3$u...\n%1$s"), + driver ? driver->type.fullname() : "", + audited + 1, + m_total)); + ui().draw_text_box(container(), text.c_str(), ui::text_layout::CENTER, 0.5f, 0.5f, UI_GREEN_COLOR); + } + break; } +} + +void menu_audit::populate(float &customtop, float &custombottom) +{ + item_append(_("Start Audit"), "", 0, ITEMREF_START); + customtop = (ui().get_line_height() * 2.0f) + (UI_BOX_TB_BORDER * 3.0f); +} - if (m_audit_mode == 1) +void menu_audit::handle() +{ + switch (m_phase) { - vptr_game::iterator iter = m_unavailablesorted.begin(); - while (iter != m_unavailablesorted.end()) + case phase::CONSENT: { - driver_enumerator enumerator(machine().options(), (*iter)->name); - enumerator.next(); - media_auditor auditor(enumerator); - media_auditor::summary summary = auditor.audit_media(AUDIT_VALIDATE_FAST); - - // if everything looks good, include the driver - if (summary == media_auditor::CORRECT || summary == media_auditor::BEST_AVAILABLE || summary == media_auditor::NONE_NEEDED) + event const *const menu_event(process(0)); + if (menu_event && (ITEMREF_START == menu_event->itemref) && (IPT_UI_SELECT == menu_event->iptkey)) { - m_availablesorted.push_back((*iter)); - iter = m_unavailablesorted.erase(iter); + m_phase = phase::AUDIT; + m_worker_thread = std::thread( + [this] () + { + switch (m_audit_mode) + { + case mode::FAST: + audit_fast(); + return; + case mode::ALL: + audit_all(); + return; + } + throw false; + }); } - else - ++iter; } + break; + + case phase::AUDIT: + process(PROCESS_CUSTOM_ONLY | PROCESS_NOINPUT); + + if (m_audited.load() >= m_total) + { + m_worker_thread.join(); + save_available_machines(); + reset_parent(reset_options::SELECT_FIRST); + stack_pop(); + } + break; } - else +} + +void menu_audit::audit_fast() +{ + for (ui_system_info &info : m_availablesorted) { - driver_enumerator enumerator(machine().options()); - media_auditor auditor(enumerator); - while (enumerator.next()) + if (!info.available) { - media_auditor::summary summary = auditor.audit_media(AUDIT_VALIDATE_FAST); + m_current.store(info.driver); + driver_enumerator enumerator(machine().options(), info.driver->name); + enumerator.next(); + media_auditor auditor(enumerator); + media_auditor::summary const summary(auditor.audit_media(AUDIT_VALIDATE_FAST)); + info.available = (summary == media_auditor::CORRECT) || (summary == media_auditor::BEST_AVAILABLE) || (summary == media_auditor::NONE_NEEDED); // if everything looks good, include the driver - if (summary == media_auditor::CORRECT || summary == media_auditor::BEST_AVAILABLE || summary == media_auditor::NONE_NEEDED) - m_availablesorted.push_back(&enumerator.driver()); - else - m_unavailablesorted.push_back(&enumerator.driver()); + info.available = (summary == media_auditor::CORRECT) || (summary == media_auditor::BEST_AVAILABLE) || (summary == media_auditor::NONE_NEEDED); + ++m_audited; } } - - // sort - std::stable_sort(m_availablesorted.begin(), m_availablesorted.end(), sorted_game_list); - std::stable_sort(m_unavailablesorted.begin(), m_unavailablesorted.end(), sorted_game_list); - save_available_machines(); - reset_parent(reset_options::SELECT_FIRST); - stack_pop(); } -//------------------------------------------------- -// populate -//------------------------------------------------- - -void menu_audit::populate(float &customtop, float &custombottom) +void menu_audit::audit_all() { - item_append("Dummy", "", 0, (void *)(uintptr_t)1); -} + m_availablesorted.clear(); + driver_enumerator enumerator(machine().options()); + media_auditor auditor(enumerator); + while (enumerator.next()) + { + m_current.store(&enumerator.driver()); + media_auditor::summary const summary(auditor.audit_media(AUDIT_VALIDATE_FAST)); -//------------------------------------------------- -// save drivers infos to file -//------------------------------------------------- + // if everything looks good, include the driver + m_availablesorted.emplace_back(enumerator.driver(), (summary == media_auditor::CORRECT) || (summary == media_auditor::BEST_AVAILABLE) || (summary == media_auditor::NONE_NEEDED)); + ++m_audited; + } + + // sort + std::stable_sort( + m_availablesorted.begin(), + m_availablesorted.end(), + [] (ui_system_info const &a, ui_system_info const &b) { return sorted_game_list(a.driver, b.driver); }); +} void menu_audit::save_available_machines() { @@ -182,25 +234,15 @@ void menu_audit::save_available_machines() if (file.open(emulator_info::get_configname(), "_avail.ini") == osd_file::error::NONE) { // generate header - std::ostringstream buffer; - buffer << "#\n" << UI_VERSION_TAG << emulator_info::get_bare_build_version() << "\n#\n\n"; - util::stream_format(buffer, "%d\n", m_availablesorted.size()); - util::stream_format(buffer, "%d\n", m_unavailablesorted.size()); + file.printf("#\n%s%s\n#\n\n", UI_VERSION_TAG, emulator_info::get_bare_build_version()); // generate available list - for (size_t x = 0; x < m_availablesorted.size(); ++x) + for (ui_system_info const &info : m_availablesorted) { - int find = driver_list::find(m_availablesorted[x]->name); - util::stream_format(buffer, "%d\n", find); + if (info.available) + file.printf("%s\n", info.driver->name); } - // generate unavailable list - for (size_t x = 0; x < m_unavailablesorted.size(); ++x) - { - int find = driver_list::find(m_unavailablesorted[x]->name); - util::stream_format(buffer, "%d\n", find); - } - file.puts(buffer.str().c_str()); file.close(); } } diff --git a/src/frontend/mame/ui/auditmenu.h b/src/frontend/mame/ui/auditmenu.h index aadebcbac9f..435bf52cd34 100644 --- a/src/frontend/mame/ui/auditmenu.h +++ b/src/frontend/mame/ui/auditmenu.h @@ -1,5 +1,5 @@ // license:BSD-3-Clause -// copyright-holders:Maurizio Petrarota +// copyright-holders:Maurizio Petrarota, Vas Crabb /*************************************************************************** ui/auditmenu.h @@ -7,41 +7,54 @@ Internal UI user interface. ***************************************************************************/ - -#pragma once - #ifndef MAME_FRONTEND_UI_AUDITMENU_H #define MAME_FRONTEND_UI_AUDITMENU_H +#pragma once + #include "ui/menu.h" +#include "ui/utils.h" + +#include <atomic> +#include <thread> +#include <vector> + namespace ui { -//------------------------------------------------- -// class audit menu -//------------------------------------------------- -using vptr_game = std::vector<const game_driver *>; class menu_audit : public menu { public: - menu_audit(mame_ui_manager &mui, render_container &container, vptr_game &availablesorted, vptr_game &unavailablesorted, int audit_mode); + enum class mode { FAST, ALL }; + + menu_audit(mame_ui_manager &mui, render_container &container, std::vector<ui_system_info> &availablesorted, mode audit_mode); virtual ~menu_audit() override; +protected: + virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override; + private: + enum class phase { CONSENT, AUDIT }; + virtual void populate(float &customtop, float &custombottom) override; virtual void handle() override; + void audit_fast(); + void audit_all(); void save_available_machines(); - vptr_game &m_availablesorted; - vptr_game &m_unavailablesorted; - - int m_audit_mode; - bool m_first; + std::thread m_worker_thread; + mode const m_audit_mode; + std::size_t const m_total; + std::string m_prompt[2]; + std::vector<ui_system_info> &m_availablesorted; + std::atomic<std::size_t> m_audited; + std::atomic<game_driver const *> m_current; + phase m_phase; }; bool sorted_game_list(const game_driver *x, const game_driver *y); } // namespace ui -#endif /* MAME_FRONTEND_UI_AUDITMENU_H */ +#endif // MAME_FRONTEND_UI_AUDITMENU_H diff --git a/src/frontend/mame/ui/menu.cpp b/src/frontend/mame/ui/menu.cpp index 5f1056bc45d..a68b4e016f7 100644 --- a/src/frontend/mame/ui/menu.cpp +++ b/src/frontend/mame/ui/menu.cpp @@ -453,7 +453,10 @@ const menu::event *menu::process(uint32_t flags, float x0, float y0) m_event.type = item[selected].type; return &m_event; } - return nullptr; + else + { + return nullptr; + } } diff --git a/src/frontend/mame/ui/selgame.cpp b/src/frontend/mame/ui/selgame.cpp index 9deb0ef6649..c22cb3a54a5 100644 --- a/src/frontend/mame/ui/selgame.cpp +++ b/src/frontend/mame/ui/selgame.cpp @@ -30,6 +30,9 @@ #include "uiinput.h" #include "luaengine.h" +#include <cstring> +#include <iterator> + extern const char UI_VERSION_TAG[]; @@ -326,12 +329,12 @@ void menu_select_game::handle() break; case IPT_UI_AUDIT_FAST: - if (!m_unavailsortedlist.empty()) - menu::stack_push<menu_audit>(ui(), container(), m_availsortedlist, m_unavailsortedlist, 1); + if (std::find_if(m_availsortedlist.begin(), m_availsortedlist.end(), [] (ui_system_info const &info) { return !info.available; }) != m_availsortedlist.end()) + menu::stack_push<menu_audit>(ui(), container(), m_availsortedlist, menu_audit::mode::FAST); break; case IPT_UI_AUDIT_ALL: - menu::stack_push<menu_audit>(ui(), container(), m_availsortedlist, m_unavailsortedlist, 2); + menu::stack_push<menu_audit>(ui(), container(), m_availsortedlist, menu_audit::mode::ALL); break; } } @@ -367,44 +370,25 @@ void menu_select_game::populate(float &customtop, float &custombottom) m_displaylist.clear(); // if filter is set on category, build category list - switch (main_filters::actual) - { - case machine_filter::ALL: - m_displaylist = m_sortedlist; - break; - case machine_filter::AVAILABLE: - m_displaylist = m_availsortedlist; - break; - case machine_filter::UNAVAILABLE: - m_displaylist = m_unavailsortedlist; - break; - default: - { - auto const it(main_filters::filters.find(main_filters::actual)); - std::copy_if( - m_sortedlist.begin(), - m_sortedlist.end(), - std::back_inserter(m_displaylist), - [&flt = *it->second] (game_driver const *drv) { return flt.apply(*drv); }); - } - } + auto const it(main_filters::filters.find(main_filters::actual)); + it->second->apply(m_availsortedlist.begin(), m_availsortedlist.end(), std::back_inserter(m_displaylist)); // iterate over entries int curitem = 0; - for (auto & elem : m_displaylist) + for (ui_system_info const &elem : m_displaylist) { - if (old_item_selected == -1 && elem->name == reselect_last::driver()) + if (old_item_selected == -1 && elem.driver->name == reselect_last::driver()) old_item_selected = curitem; - bool cloneof = strcmp(elem->parent, "0"); + bool cloneof = strcmp(elem.driver->parent, "0"); if (cloneof) { - int cx = driver_list::find(elem->parent); + int cx = driver_list::find(elem.driver->parent); if (cx != -1 && ((driver_list::driver(cx).flags & machine_flags::IS_BIOS_ROOT) != 0)) cloneof = false; } - item_append(elem->type.fullname(), "", (cloneof) ? (flags_ui | FLAG_INVERT) : flags_ui, (void *)elem); + item_append(elem.driver->type.fullname(), "", (cloneof) ? (flags_ui | FLAG_INVERT) : flags_ui, (void *)elem.driver); curitem++; } } @@ -490,19 +474,16 @@ void menu_select_game::populate(float &customtop, float &custombottom) void menu_select_game::build_available_list() { - int m_total = driver_list::total(); - std::vector<bool> m_included(m_total, false); + std::size_t const total = driver_list::total(); + std::vector<bool> included(total, false); - // open a path to the ROMs and find them in the array + // iterate over ROM directories and look for potential ROMs file_enumerator path(machine().options().media_path()); - const osd::directory::entry *dir; - - // iterate while we get new objects - while ((dir = path.next()) != nullptr) + for (osd::directory::entry const *dir = path.next(); dir; dir = path.next()) { char drivername[50]; char *dst = drivername; - const char *src; + char const *src; // build a name for it for (src = dir->name; *src != 0 && *src != '.' && dst < &drivername[ARRAY_LENGTH(drivername) - 1]; ++src) @@ -510,67 +491,63 @@ void menu_select_game::build_available_list() *dst = 0; int drivnum = driver_list::find(drivername); - if (drivnum != -1 && !m_included[drivnum]) - { - m_availsortedlist.push_back(&driver_list::driver(drivnum)); - m_included[drivnum] = true; - } + if (drivnum != -1 && !included[drivnum]) + included[drivnum] = true; } // now check and include NONE_NEEDED if (!ui().options().hide_romless()) { - for (int x = 0; x < m_total; ++x) + // FIXME: can't use the convenience macros tiny ROM entries + auto const is_required_rom = + [] (tiny_rom_entry const &rom) + { + return ((rom.flags & ROMENTRY_TYPEMASK) == ROMENTRYTYPE_ROM) && ((rom.flags & ROM_OPTIONALMASK) != ROM_OPTIONAL) && !std::strchr(rom.hashdata, '!'); + }; + for (std::size_t x = 0; total > x; ++x) { - auto driver = &driver_list::driver(x); - if (!m_included[x] && driver != &GAME_NAME(___empty)) + game_driver const &driver(driver_list::driver(x)); + if (!included[x] && (&GAME_NAME(___empty) != &driver)) { - auto entries = rom_build_entries(driver->rom); - const rom_entry *rom = entries.data(); - bool noroms = true; - - // check NO-DUMP - for (; !ROMENTRY_ISEND(rom) && noroms == true; ++rom) - if (ROMENTRY_ISFILE(rom)) + bool noroms(true); + tiny_rom_entry const *rom; + for (rom = driver.rom; (rom->flags & ROMENTRY_TYPEMASK) != ROMENTRYTYPE_END; ++rom) + { + // check optional and NO_DUMP + if (is_required_rom(*rom)) { - util::hash_collection hashes(ROM_GETHASHDATA(rom)); - if (!hashes.flag(util::hash_collection::FLAG_NO_DUMP) && !ROM_ISOPTIONAL(rom)) - noroms = false; + noroms = false; + break; // break before incrementing, or it will subtly break the check for all ROMs belonging to parent } + } if (!noroms) { // check if clone == parent - auto cx = driver_list::clone(*driver); - if (cx != -1 && m_included[cx]) + auto const cx(driver_list::clone(driver)); + if ((0 <= cx) && included[cx]) { - auto drv = &driver_list::driver(cx); - if (driver->rom == drv->rom) + game_driver const &parent(driver_list::driver(cx)); + if (driver.rom == parent.rom) + { noroms = true; - - // check if clone < parent - if (!noroms) + } + else { + // check if clone < parent noroms = true; - for (; !ROMENTRY_ISEND(rom) && noroms == true; ++rom) + for ( ; noroms && rom && ((rom->flags & ROMENTRY_TYPEMASK) != ROMENTRYTYPE_END); ++rom) { - if (ROMENTRY_ISFILE(rom)) + if (is_required_rom(*rom)) { - util::hash_collection hashes(ROM_GETHASHDATA(rom)); - if (hashes.flag(util::hash_collection::FLAG_NO_DUMP) || ROM_ISOPTIONAL(rom)) - continue; - - uint64_t lenght = ROM_GETLENGTH(rom); - auto found = false; - auto parent_entries = rom_build_entries(drv->rom); - for (auto parentrom = parent_entries.data(); !ROMENTRY_ISEND(parentrom) && found == false; ++parentrom) + util::hash_collection const hashes(rom->hashdata); + + bool found(false); + for (tiny_rom_entry const *parentrom = parent.rom; !found && ((parentrom->flags & ROMENTRY_TYPEMASK) != ROMENTRYTYPE_END); ++parentrom) { - if (ROMENTRY_ISFILE(parentrom) && ROM_GETLENGTH(parentrom) == lenght) + if (is_required_rom(*parentrom) && (rom->length == parentrom->length)) { - util::hash_collection parenthashes(ROM_GETHASHDATA(parentrom)); - if (parenthashes.flag(util::hash_collection::FLAG_NO_DUMP) || ROM_ISOPTIONAL(parentrom)) - continue; - + util::hash_collection const parenthashes(parentrom->hashdata); if (hashes == parenthashes) found = true; } @@ -583,23 +560,23 @@ void menu_select_game::build_available_list() } if (noroms) - { - m_availsortedlist.push_back(&driver_list::driver(x)); - m_included[x] = true; - } + included[x] = true; } } } - // sort - std::stable_sort(m_availsortedlist.begin(), m_availsortedlist.end(), sorted_game_list); - - // now build the unavailable list - for (int x = 0; x < m_total; ++x) - if (!m_included[x] && &driver_list::driver(x) != &GAME_NAME(___empty)) - m_unavailsortedlist.push_back(&driver_list::driver(x)); // sort - std::stable_sort(m_unavailsortedlist.begin(), m_unavailsortedlist.end(), sorted_game_list); + m_availsortedlist.reserve(total); + for (std::size_t x = 0; total > x; ++x) + { + game_driver const &driver(driver_list::driver(x)); + if (&driver != &GAME_NAME(___empty)) + m_availsortedlist.emplace_back(driver, included[x]); + } + std::stable_sort( + m_availsortedlist.begin(), + m_availsortedlist.end(), + [] (ui_system_info const &a, ui_system_info const &b) { return sorted_game_list(a.driver, b.driver); }); } @@ -823,8 +800,8 @@ void menu_select_game::populate_search() for (; index < m_displaylist.size(); ++index) { // pick the best match between driver name and description - int curpenalty = fuzzy_substring(m_search, m_displaylist[index]->type.fullname()); - int tmp = fuzzy_substring(m_search, m_displaylist[index]->name); + int curpenalty = fuzzy_substring(m_search, m_displaylist[index].driver->type.fullname()); + int tmp = fuzzy_substring(m_search, m_displaylist[index].driver->name); curpenalty = std::min(curpenalty, tmp); // insert into the sorted table of matches @@ -841,7 +818,7 @@ void menu_select_game::populate_search() m_searchlist[matchnum + 1] = m_searchlist[matchnum]; } - m_searchlist[matchnum] = m_displaylist[index]; + m_searchlist[matchnum] = m_displaylist[index].driver; penalty[matchnum] = curpenalty; } } @@ -1033,7 +1010,9 @@ void menu_select_game::inkey_export() } else { - list = m_displaylist; + list.reserve(m_displaylist.size()); + for (ui_system_info const &info : m_displaylist) + list.emplace_back(info.driver); } } @@ -1086,8 +1065,8 @@ bool menu_select_game::load_available_machines() if (file.open(emulator_info::get_configname(), "_avail.ini") != osd_file::error::NONE) return false; - std::string readbuf; char rbuf[MAX_CHAR_INFO]; + std::string readbuf; file.gets(rbuf, MAX_CHAR_INFO); file.gets(rbuf, MAX_CHAR_INFO); readbuf = chartrimcarriage(rbuf); @@ -1100,28 +1079,40 @@ bool menu_select_game::load_available_machines() return false; } - file.gets(rbuf, MAX_CHAR_INFO); - file.gets(rbuf, MAX_CHAR_INFO); - file.gets(rbuf, MAX_CHAR_INFO); - auto avsize = atoi(rbuf); - file.gets(rbuf, MAX_CHAR_INFO); - auto unavsize = atoi(rbuf); - // load available list - for (int x = 0; x < avsize; ++x) + std::unordered_set<std::string> available; + while (file.gets(rbuf, MAX_CHAR_INFO)) { - file.gets(rbuf, MAX_CHAR_INFO); - int find = atoi(rbuf); - m_availsortedlist.push_back(&driver_list::driver(find)); + readbuf = rbuf; + strtrimspace(readbuf); + + if (readbuf.empty() || ('#' == readbuf[0])) // ignore empty lines and line comments + ; + else if ('[' == readbuf[0]) // throw out the rest of the file if we find a section heading + break; + else + available.emplace(std::move(readbuf)); } - // load unavailable list - for (int x = 0; x < unavsize; ++x) + // turn it into the sorted system list we all love + m_availsortedlist.reserve(driver_list::total()); + for (std::size_t x = 0; driver_list::total() > x; ++x) { - file.gets(rbuf, MAX_CHAR_INFO); - int find = atoi(rbuf); - m_unavailsortedlist.push_back(&driver_list::driver(find)); + game_driver const &driver(driver_list::driver(x)); + if (&driver != &GAME_NAME(___empty)) + { + std::unordered_set<std::string>::iterator const it(available.find(&driver.name[0])); + bool const found(available.end() != it); + m_availsortedlist.emplace_back(driver, found); + if (found) + available.erase(it); + } } + std::stable_sort( + m_availsortedlist.begin(), + m_availsortedlist.end(), + [] (ui_system_info const &a, ui_system_info const &b) { return sorted_game_list(a.driver, b.driver); }); + file.close(); return true; } diff --git a/src/frontend/mame/ui/selgame.h b/src/frontend/mame/ui/selgame.h index 49d4896f834..5778b2c544c 100644 --- a/src/frontend/mame/ui/selgame.h +++ b/src/frontend/mame/ui/selgame.h @@ -7,13 +7,14 @@ Main UI menu. ***************************************************************************/ - -#pragma once - #ifndef MAME_FRONTEND_UI_SELGAME_H #define MAME_FRONTEND_UI_SELGAME_H +#pragma once + #include "ui/selmenu.h" +#include "ui/utils.h" + class media_auditor; @@ -41,9 +42,8 @@ private: static int m_isabios; static std::vector<const game_driver *> m_sortedlist; - std::vector<const game_driver *> m_availsortedlist; - std::vector<const game_driver *> m_unavailsortedlist; - std::vector<const game_driver *> m_displaylist; + std::vector<ui_system_info> m_availsortedlist; + std::vector<ui_system_info> m_displaylist; const game_driver *m_searchlist[VISIBLE_GAMES_IN_SEARCH + 1]; diff --git a/src/frontend/mame/ui/slotopt.cpp b/src/frontend/mame/ui/slotopt.cpp index 74889c42df9..d61246a7661 100644 --- a/src/frontend/mame/ui/slotopt.cpp +++ b/src/frontend/mame/ui/slotopt.cpp @@ -291,7 +291,7 @@ void menu_slot_devices::rotate_slot_device(device_slot_interface &slot, menu_slo { return opt_value == target; }); - + // we expect the above search to succeed, because if an internal // option was selected, the menu item should be disabled assert(m_current_option_list_iter != m_current_option_list.end()); diff --git a/src/frontend/mame/ui/slotopt.h b/src/frontend/mame/ui/slotopt.h index 3f0b98850d7..1a4421520d4 100644 --- a/src/frontend/mame/ui/slotopt.h +++ b/src/frontend/mame/ui/slotopt.h @@ -43,9 +43,9 @@ private: // variables std::unique_ptr<machine_config> m_config; std::unordered_map<std::string, std::string> m_slot_options; - std::string m_current_option_list_slot_tag; - std::vector<std::string> m_current_option_list; - std::vector<std::string>::const_iterator m_current_option_list_iter; + std::string m_current_option_list_slot_tag; + std::vector<std::string> m_current_option_list; + std::vector<std::string>::const_iterator m_current_option_list_iter; }; } // namespace ui diff --git a/src/frontend/mame/ui/utils.cpp b/src/frontend/mame/ui/utils.cpp index 3de86372fbe..bd5d87f774e 100644 --- a/src/frontend/mame/ui/utils.cpp +++ b/src/frontend/mame/ui/utils.cpp @@ -637,13 +637,23 @@ void composite_filter_impl_base<Impl, Base, Type>::menu_configure::handle() // invertable machine filters //------------------------------------------------- +template <machine_filter::type Type = machine_filter::AVAILABLE> +class available_machine_filter_impl : public simple_filter_impl_base<machine_filter, Type> +{ +public: + available_machine_filter_impl(char const *value, emu_file *file, unsigned indent) { } + + virtual bool apply(ui_system_info const &system) const override { return system.available; } +}; + + template <machine_filter::type Type = machine_filter::WORKING> class working_machine_filter_impl : public simple_filter_impl_base<machine_filter, Type> { public: working_machine_filter_impl(char const *value, emu_file *file, unsigned indent) { } - virtual bool apply(game_driver const &driver) const override { return !(driver.flags & machine_flags::NOT_WORKING); } + virtual bool apply(ui_system_info const &system) const override { return !(system.driver->flags & machine_flags::NOT_WORKING); } }; @@ -653,7 +663,7 @@ class mechanical_machine_filter_impl : public simple_filter_impl_base<machine_fi public: mechanical_machine_filter_impl(char const *value, emu_file *file, unsigned indent) { } - virtual bool apply(game_driver const &driver) const override { return driver.flags & machine_flags::MECHANICAL; } + virtual bool apply(ui_system_info const &system) const override { return system.driver->flags & machine_flags::MECHANICAL; } }; @@ -663,7 +673,7 @@ class bios_machine_filter_impl : public simple_filter_impl_base<machine_filter, public: bios_machine_filter_impl(char const *value, emu_file *file, unsigned indent) { } - virtual bool apply(game_driver const &driver) const override { return driver.flags & machine_flags::IS_BIOS_ROOT; } + virtual bool apply(ui_system_info const &system) const override { return system.driver->flags & machine_flags::IS_BIOS_ROOT; } }; @@ -673,10 +683,10 @@ class parents_machine_filter_impl : public simple_filter_impl_base<machine_filte public: parents_machine_filter_impl(char const *value, emu_file *file, unsigned indent) { } - virtual bool apply(game_driver const &driver) const override + virtual bool apply(ui_system_info const &system) const override { - bool const have_parent(strcmp(driver.parent, "0")); - auto const parent_idx(have_parent ? driver_list::find(driver.parent) : -1); + bool const have_parent(strcmp(system.driver->parent, "0")); + auto const parent_idx(have_parent ? driver_list::find(system.driver->parent) : -1); return !have_parent || (0 > parent_idx) || (driver_list::driver(parent_idx).flags & machine_flags::IS_BIOS_ROOT); } }; @@ -688,9 +698,9 @@ class chd_machine_filter_impl : public simple_filter_impl_base<machine_filter, T public: chd_machine_filter_impl(char const *value, emu_file *file, unsigned indent) { } - virtual bool apply(game_driver const &driver) const override + virtual bool apply(ui_system_info const &system) const override { - for (tiny_rom_entry const *rom = driver.rom; rom && rom->name; ++rom) + for (tiny_rom_entry const *rom = system.driver->rom; ((rom->flags & ROMENTRY_TYPEMASK) != ROMENTRYTYPE_END) && rom->name; ++rom) { // FIXME: can't use the convenience macros tiny ROM entries if ((ROMENTRYTYPE_REGION == (rom->flags & ROMENTRY_TYPEMASK)) && (ROMREGION_DATATYPEDISK == (rom->flags & ROMREGION_DATATYPEMASK))) @@ -707,7 +717,7 @@ class save_machine_filter_impl : public simple_filter_impl_base<machine_filter, public: save_machine_filter_impl(char const *value, emu_file *file, unsigned indent) { } - virtual bool apply(game_driver const &driver) const override { return driver.flags & machine_flags::SUPPORTS_SAVE; } + virtual bool apply(ui_system_info const &system) const override { return system.driver->flags & machine_flags::SUPPORTS_SAVE; } }; @@ -717,7 +727,7 @@ class vertical_machine_filter_impl : public simple_filter_impl_base<machine_filt public: vertical_machine_filter_impl(char const *value, emu_file *file, unsigned indent) { } - virtual bool apply(game_driver const &driver) const override { return driver.flags & machine_flags::SWAP_XY; } + virtual bool apply(ui_system_info const &system) const override { return system.driver->flags & machine_flags::SWAP_XY; } }; @@ -734,14 +744,14 @@ public: { } - virtual bool apply(game_driver const &driver) const override + virtual bool apply(ui_system_info const &system) const override { if (!have_choices()) return true; else if (!selection_valid()) return false; - std::string const name(c_mnfct::getname(driver.manufacturer)); + std::string const name(c_mnfct::getname(system.driver->manufacturer)); return !name.empty() && (selection_text() == name); } }; @@ -755,7 +765,7 @@ public: { } - virtual bool apply(game_driver const &driver) const override { return !have_choices() || (selection_valid() && (selection_text() == driver.year)); } + virtual bool apply(ui_system_info const &system) const override { return !have_choices() || (selection_valid() && (selection_text() == system.driver->year)); } }; @@ -770,9 +780,10 @@ class inverted_machine_filter : public Base<Type> public: inverted_machine_filter(char const *value, emu_file *file, unsigned indent) : Base<Type>(value, file, indent) { } - virtual bool apply(game_driver const &driver) const override { return !Base<Type>::apply(driver); } + virtual bool apply(ui_system_info const &system) const override { return !Base<Type>::apply(system); } }; +using available_machine_filter = available_machine_filter_impl<>; using working_machine_filter = working_machine_filter_impl<>; using mechanical_machine_filter = mechanical_machine_filter_impl<>; using bios_machine_filter = bios_machine_filter_impl<>; @@ -781,6 +792,7 @@ using save_machine_filter = save_machine_filter_impl<>; using chd_machine_filter = chd_machine_filter_impl<>; using vertical_machine_filter = vertical_machine_filter_impl<>; +using unavailable_machine_filter = inverted_machine_filter<available_machine_filter_impl, machine_filter::UNAVAILABLE>; using not_working_machine_filter = inverted_machine_filter<working_machine_filter_impl, machine_filter::NOT_WORKING>; using not_mechanical_machine_filter = inverted_machine_filter<mechanical_machine_filter_impl, machine_filter::NOT_MECHANICAL>; using not_bios_machine_filter = inverted_machine_filter<bios_machine_filter_impl, machine_filter::NOT_BIOS>; @@ -801,12 +813,10 @@ class inclusive_machine_filter_impl : public simple_filter_impl_base<machine_fil public: inclusive_machine_filter_impl(char const *value, emu_file *file, unsigned indent) { } - virtual bool apply(game_driver const &drv) const override { return true; } + virtual bool apply(ui_system_info const &system) const override { return true; } }; using all_machine_filter = inclusive_machine_filter_impl<machine_filter::ALL>; -using available_machine_filter = inclusive_machine_filter_impl<machine_filter::AVAILABLE>; -using unavailable_machine_filter = inclusive_machine_filter_impl<machine_filter::UNAVAILABLE>; using favorite_machine_filter = inclusive_machine_filter_impl<machine_filter::FAVORITE>; @@ -881,7 +891,7 @@ public: file.puts(util::string_format("%2$*1$s%3$s = %4$s\n", 2 * indent, "", this->config_name(), text ? text : "").c_str()); } - virtual bool apply(game_driver const &drv) const override + virtual bool apply(ui_system_info const &system) const override { inifile_manager const &mgr(mame_machine_manager::instance()->inifile()); if (!mgr.get_file_count()) @@ -893,12 +903,12 @@ public: mame_machine_manager::instance()->inifile().load_ini_category(m_ini, m_group, m_cache); m_cache_valid = true; - if (m_cache.end() != m_cache.find(&drv)) + if (m_cache.end() != m_cache.find(system.driver)) return true; if (m_include_clones) { - int const found(driver_list::find(drv.parent)); + int const found(driver_list::find(system.driver->parent)); return m_cache.end() != m_cache.find(&driver_list::driver(found)); } @@ -1162,7 +1172,7 @@ public: static bool type_allowed(unsigned pos, type n) { - return (FIRST <= n) && (LAST >= n) && (ALL != n) && (AVAILABLE != n) && (UNAVAILABLE != n) && (FAVORITE != n) && (CUSTOM != n); + return (FIRST <= n) && (LAST >= n) && (ALL != n) && (FAVORITE != n) && (CUSTOM != n); } static bool types_contradictory(type n, type m) diff --git a/src/frontend/mame/ui/utils.h b/src/frontend/mame/ui/utils.h index 9d52f8acf8c..60030d07205 100644 --- a/src/frontend/mame/ui/utils.h +++ b/src/frontend/mame/ui/utils.h @@ -26,7 +26,17 @@ class mame_ui_manager; class render_container; -// TODO: namespace this thing +// TODO: namespace these things + +struct ui_system_info +{ + ui_system_info() { } + ui_system_info(game_driver const &d, bool a) : driver(&d), available(a) { } + + game_driver const *driver = nullptr; + bool available = false; +}; + struct ui_software_info { ui_software_info() { } @@ -49,8 +59,9 @@ struct ui_software_info ui_software_info &operator=(ui_software_info const &) = default; ui_software_info &operator=(ui_software_info &&) = default; - bool operator==(ui_software_info const &r) + bool operator==(ui_software_info const &r) const { + // compares all fields except available return shortname == r.shortname && longname == r.longname && parentname == r.parentname && year == r.year && publisher == r.publisher && supported == r.supported && part == r.part && driver == r.driver && listname == r.listname @@ -65,7 +76,7 @@ struct ui_software_info std::string publisher; uint8_t supported = 0; std::string part; - const game_driver *driver = nullptr; + game_driver const *driver = nullptr; std::string listname; std::string interface; std::string instance; @@ -137,17 +148,19 @@ public: template <typename InputIt, class OutputIt> void apply(InputIt first, InputIt last, OutputIt dest) const { - std::copy_if(first, last, dest, [this] (Entry const *info) { return apply(*info); }); + std::copy_if(first, last, dest, [this] (auto const &info) { return apply(info); }); } protected: using entry_type = Entry; filter_base() { } + + bool apply(Entry const *info) const { return apply(*info); } }; -class machine_filter : public filter_base<machine_filter, game_driver> +class machine_filter : public filter_base<machine_filter, ui_system_info> { public: enum type : uint16_t @@ -188,8 +201,8 @@ public: static char const *config_name(type n); static char const *display_name(type n); - using filter_base<machine_filter, game_driver>::config_name; - using filter_base<machine_filter, game_driver>::display_name; + using filter_base<machine_filter, ui_system_info>::config_name; + using filter_base<machine_filter, ui_system_info>::display_name; protected: machine_filter(); |