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
This commit is contained in:
Vas Crabb 2017-08-21 21:45:15 +10:00
parent f9f4ae9d36
commit 90f7fa47c2
9 changed files with 351 additions and 279 deletions

View File

@ -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/ui.h"
#include "ui/menu.h"
#include "audit.h"
#include "ui/auditmenu.h"
#include "ui/ui.h"
#include "audit.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 {
void *const ITEMREF_START = reinterpret_cast<void *>(std::uintptr_t(1));
} // anonymous 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');
if (!clonex && !cloney)
return (cs_stricmp(x->type.fullname(), y->type.fullname()) < 0);
int cx = -1, cy = -1;
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;
if (m_audit_mode == 1)
{
vptr_game::iterator iter = m_unavailablesorted.begin();
while (iter != m_unavailablesorted.end())
case phase::AUDIT:
{
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)
{
m_availablesorted.push_back((*iter));
iter = m_unavailablesorted.erase(iter);
}
else
++iter;
// 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;
}
else
{
driver_enumerator enumerator(machine().options());
media_auditor auditor(enumerator);
while (enumerator.next())
{
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)
m_availablesorted.push_back(&enumerator.driver());
else
m_unavailablesorted.push_back(&enumerator.driver());
}
}
// 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)
{
item_append("Dummy", "", 0, (void *)(uintptr_t)1);
item_append(_("Start Audit"), "", 0, ITEMREF_START);
customtop = (ui().get_line_height() * 2.0f) + (UI_BOX_TB_BORDER * 3.0f);
}
//-------------------------------------------------
// save drivers infos to file
//-------------------------------------------------
void menu_audit::handle()
{
switch (m_phase)
{
case phase::CONSENT:
{
event const *const menu_event(process(0));
if (menu_event && (ITEMREF_START == menu_event->itemref) && (IPT_UI_SELECT == menu_event->iptkey))
{
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;
});
}
}
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;
}
}
void menu_audit::audit_fast()
{
for (ui_system_info &info : m_availablesorted)
{
if (!info.available)
{
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
info.available = (summary == media_auditor::CORRECT) || (summary == media_auditor::BEST_AVAILABLE) || (summary == media_auditor::NONE_NEEDED);
++m_audited;
}
}
}
void menu_audit::audit_all()
{
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));
// 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();
}
}

View File

@ -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

View File

@ -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;
}
}

View File

@ -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)
noroms = true;
// check if clone < parent
if (!noroms)
game_driver const &parent(driver_list::driver(cx));
if (driver.rom == parent.rom)
{
noroms = true;
for (; !ROMENTRY_ISEND(rom) && noroms == true; ++rom)
}
else
{
// check if clone < parent
noroms = true;
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;
util::hash_collection const hashes(rom->hashdata);
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)
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;
}

View File

@ -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];

View File

@ -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());

View File

@ -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

View File

@ -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)

View File

@ -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();