diff options
Diffstat (limited to 'src/frontend/mame/ui/selgame.cpp')
-rw-r--r-- | src/frontend/mame/ui/selgame.cpp | 755 |
1 files changed, 490 insertions, 265 deletions
diff --git a/src/frontend/mame/ui/selgame.cpp b/src/frontend/mame/ui/selgame.cpp index 0b47fa1d7d1..def4ceaa5f9 100644 --- a/src/frontend/mame/ui/selgame.cpp +++ b/src/frontend/mame/ui/selgame.cpp @@ -1,5 +1,5 @@ // license:BSD-3-Clause -// copyright-holders:Maurizio Petrarota +// copyright-holders:Maurizio Petrarota, Vas Crabb /********************************************************************* ui/selgame.cpp @@ -12,6 +12,7 @@ #include "ui/selgame.h" #include "ui/auditmenu.h" +#include "ui/icorender.h" #include "ui/inifile.h" #include "ui/miscmenu.h" #include "ui/optsmenu.h" @@ -31,17 +32,190 @@ #include "uiinput.h" #include "luaengine.h" +#include <atomic> +#include <condition_variable> #include <cstring> #include <iterator> +#include <memory> +#include <mutex> +#include <thread> extern const char UI_VERSION_TAG[]; namespace ui { -bool menu_select_game::first_start = true; -std::vector<const game_driver *> menu_select_game::m_sortedlist; -int menu_select_game::m_isabios = 0; +namespace { + +constexpr uint32_t FLAGS_UI = ui::menu::FLAG_LEFT_ARROW | ui::menu::FLAG_RIGHT_ARROW; + +} // anonymous namespace + +class menu_select_game::persistent_data +{ +public: + enum available : unsigned + { + AVAIL_NONE = 0U, + AVAIL_SORTED_LIST = 1U << 0, + AVAIL_BIOS_COUNT = 1U << 1, + AVAIL_UCS_SHORTNAME = 1U << 2, + AVAIL_UCS_DESCRIPTION = 1U << 3, + AVAIL_UCS_MANUF_DESC = 1U << 4, + AVAIL_FILTER_DATA = 1U << 5 + }; + + ~persistent_data() + { + if (m_thread) + m_thread->join(); + } + + void cache_data() + { + std::unique_lock<std::mutex> lock(m_mutex); + do_start_caching(); + } + + bool is_available(available desired) + { + return (m_available.load(std::memory_order_acquire) & desired) == desired; + } + + void wait_available(available desired) + { + if (!is_available(desired)) + { + std::unique_lock<std::mutex> lock(m_mutex); + do_start_caching(); + m_condition.wait(lock, [this, desired] () { return is_available(desired); }); + } + } + + std::vector<ui_system_info> &sorted_list() + { + wait_available(AVAIL_SORTED_LIST); + return m_sorted_list; + } + + int bios_count() + { + wait_available(AVAIL_BIOS_COUNT); + return m_bios_count; + } + + bool unavailable_systems() + { + wait_available(AVAIL_SORTED_LIST); + return std::find_if(m_sorted_list.begin(), m_sorted_list.end(), [] (ui_system_info const &info) { return !info.available; }) != m_sorted_list.end(); + } + + machine_filter_data &filter_data() + { + wait_available(AVAIL_FILTER_DATA); + return m_filter_data; + } + + static persistent_data &instance() + { + static persistent_data data; + return data; + } + +private: + persistent_data() + : m_started(false) + , m_available(AVAIL_NONE) + , m_bios_count(0) + { + } + + void notify_available(available value) + { + std::unique_lock<std::mutex> lock(m_mutex); + m_available.fetch_or(value, std::memory_order_release); + m_condition.notify_all(); + } + + void do_start_caching() + { + if (!m_started) + { + m_started = true; + m_thread = std::make_unique<std::thread>([this] { do_cache_data(); }); + } + } + + void do_cache_data() + { + // generate full list + m_sorted_list.reserve(driver_list::total()); + std::unordered_set<std::string> manufacturers, years; + for (int x = 0; x < driver_list::total(); ++x) + { + game_driver const &driver(driver_list::driver(x)); + if (&driver != &GAME_NAME(___empty)) + { + if (driver.flags & machine_flags::IS_BIOS_ROOT) + ++m_bios_count; + + m_sorted_list.emplace_back(driver, x, false); + m_filter_data.add_manufacturer(driver.manufacturer); + m_filter_data.add_year(driver.year); + } + } + + // notify that BIOS count is valie + notify_available(AVAIL_BIOS_COUNT); + + // sort drivers and notify + std::stable_sort( + m_sorted_list.begin(), + m_sorted_list.end(), + [] (ui_system_info const &lhs, ui_system_info const &rhs) { return sorted_game_list(lhs.driver, rhs.driver); }); + notify_available(AVAIL_SORTED_LIST); + + // sort manufacturers and years + m_filter_data.finalise(); + notify_available(AVAIL_FILTER_DATA); + + // convert shortnames to UCS-4 + for (ui_system_info &info : m_sorted_list) + info.ucs_shortname = ustr_from_utf8(normalize_unicode(info.driver->name, unicode_normalization_form::D, true)); + notify_available(AVAIL_UCS_SHORTNAME); + + // convert descriptions to UCS-4 + for (ui_system_info &info : m_sorted_list) + info.ucs_description = ustr_from_utf8(normalize_unicode(info.driver->type.fullname(), unicode_normalization_form::D, true)); + notify_available(AVAIL_UCS_DESCRIPTION); + + // convert "<manufacturer> <description>" to UCS-4 + std::string buf; + for (ui_system_info &info : m_sorted_list) + { + buf.assign(info.driver->manufacturer); + buf.append(1, ' '); + buf.append(info.driver->type.fullname()); + info.ucs_manufacturer_description = ustr_from_utf8(normalize_unicode(buf, unicode_normalization_form::D, true)); + } + notify_available(AVAIL_UCS_MANUF_DESC); + } + + // synchronisation + std::mutex m_mutex; + std::condition_variable m_condition; + std::unique_ptr<std::thread> m_thread; + std::atomic<bool> m_started; + std::atomic<unsigned> m_available; + + // data + std::vector<ui_system_info> m_sorted_list; + machine_filter_data m_filter_data; + int m_bios_count; +}; + +bool menu_select_game::s_first_start = true; + //------------------------------------------------- // ctor @@ -49,33 +223,30 @@ int menu_select_game::m_isabios = 0; menu_select_game::menu_select_game(mame_ui_manager &mui, render_container &container, const char *gamename) : menu_select_launch(mui, container, false) + , m_persistent_data(persistent_data::instance()) + , m_icons(MAX_ICONS_RENDER) + , m_icon_paths() + , m_displaylist() + , m_searchlist() + , m_searched_fields(persistent_data::AVAIL_NONE) + , m_populated_favorites(false) { std::string error_string, last_filter, sub_filter; ui_options &moptions = mui.options(); // load drivers cache - init_sorted_list(); + m_persistent_data.cache_data(); - // check if there are available icons - ui_globals::has_icons = false; - file_enumerator path(moptions.icons_directory()); - const osd::directory::entry *dir; - while ((dir = path.next()) != nullptr) - { - std::string src(dir->name); - if (src.find(".ico") != std::string::npos || src.find("icons") != std::string::npos) - { - ui_globals::has_icons = true; - break; - } - } + // check if there are available system icons + check_for_icons(nullptr); // build drivers list if (!load_available_machines()) build_available_list(); - if (first_start) + if (s_first_start) { + //s_first_start = false; TODO: why wansn't it ever clearing the first start flag? reselect_last::set_driver(moptions.last_used_machine()); ui_globals::rpanel = std::min<int>(std::max<int>(moptions.last_right_panel(), RP_FIRST), RP_LAST); @@ -96,32 +267,23 @@ menu_select_game::menu_select_game(mame_ui_manager &mui, render_container &conta emu_file file(ui().options().ui_path(), OPEN_FLAG_READ); if (file.open_ram(fake_ini.c_str(), fake_ini.size()) == osd_file::error::NONE) { - machine_filter::ptr flt(machine_filter::create(file)); - if (flt) - { - main_filters::actual = flt->get_type(); - main_filters::filters.emplace(main_filters::actual, std::move(flt)); - } + m_persistent_data.filter_data().load_ini(file); file.close(); } } // do this after processing the last used filter setting so it overwrites the placeholder load_custom_filters(); - m_filter_highlight = main_filters::actual; + m_filter_highlight = m_persistent_data.filter_data().get_current_filter_type(); if (!moptions.remember_last()) reselect_last::reset(); mui.machine().options().set_value(OPTION_SNAPNAME, "%g/%i", OPTION_PRIORITY_CMDLINE); - ui_globals::curimage_view = FIRST_VIEW; ui_globals::curdats_view = 0; - ui_globals::switch_image = false; - ui_globals::default_image = true; ui_globals::panels_status = moptions.hide_panels(); ui_globals::curdats_total = 1; - m_searchlist[0] = nullptr; } //------------------------------------------------- @@ -140,17 +302,7 @@ menu_select_game::~menu_select_game() if (driver) last_driver = driver->name; - std::string filter; - auto const active_filter(main_filters::filters.find(main_filters::actual)); - if (main_filters::filters.end() != active_filter) - { - char const *val(active_filter->second->filter_text()); - filter = val ? util::string_format("%s,%s", active_filter->second->config_name(), val) : active_filter->second->config_name(); - } - else - { - filter = machine_filter::config_name(main_filters::actual); - } + std::string const filter(m_persistent_data.filter_data().get_config_string()); ui_options &mopt = ui().options(); mopt.set_value(OPTION_LAST_RIGHT_PANEL, ui_globals::rpanel, OPTION_PRIORITY_CMDLINE); @@ -178,13 +330,13 @@ void menu_select_game::handle() return; } - // if i have to reselect a software, force software list submenu + // if I have to select software, force software list submenu if (reselect_last::get()) { const game_driver *driver; const ui_software_info *software; get_selection(software, driver); - menu::stack_push<menu_select_software>(ui(), container(), driver); + menu::stack_push<menu_select_software>(ui(), container(), *driver); return; } @@ -241,7 +393,7 @@ void menu_select_game::handle() case IPT_UI_SELECT: if (get_focus() == focused_menu::MAIN) { - if (isfavorite()) + if (m_populated_favorites) inkey_select_favorite(menu_event); else inkey_select(menu_event); @@ -250,11 +402,12 @@ void menu_select_game::handle() case IPT_CUSTOM: // handle IPT_CUSTOM (mouse right click) - if (!isfavorite()) + if (!m_populated_favorites) { menu::stack_push<menu_machine_configure>( ui(), container(), - reinterpret_cast<const game_driver *>(m_prev_selected), + *reinterpret_cast<const game_driver *>(m_prev_selected), + nullptr, menu_event->mouse.x0, menu_event->mouse.y0); } else @@ -262,18 +415,21 @@ void menu_select_game::handle() ui_software_info *sw = reinterpret_cast<ui_software_info *>(m_prev_selected); menu::stack_push<menu_machine_configure>( ui(), container(), - (const game_driver *)sw->driver, + *sw->driver, + [this, empty = sw->startempty] (bool fav, bool changed) + { + if (changed) + reset(empty ? reset_options::SELECT_FIRST : reset_options::REMEMBER_REF); + }, menu_event->mouse.x0, menu_event->mouse.y0); } break; case IPT_UI_LEFT: - if (ui_globals::rpanel == RP_IMAGES && ui_globals::curimage_view > FIRST_VIEW) + if (ui_globals::rpanel == RP_IMAGES) { // Images - ui_globals::curimage_view--; - ui_globals::switch_image = true; - ui_globals::default_image = false; + previous_image_view(); } else if (ui_globals::rpanel == RP_INFOS) { @@ -283,12 +439,10 @@ void menu_select_game::handle() break; case IPT_UI_RIGHT: - if (ui_globals::rpanel == RP_IMAGES && ui_globals::curimage_view < LAST_VIEW) + if (ui_globals::rpanel == RP_IMAGES) { // Images - ui_globals::curimage_view++; - ui_globals::switch_image = true; - ui_globals::default_image = false; + next_image_view(); } else if (ui_globals::rpanel == RP_INFOS) { @@ -301,17 +455,17 @@ void menu_select_game::handle() if (uintptr_t(menu_event->itemref) > skip_main_items) { favorite_manager &mfav(mame_machine_manager::instance()->favorite()); - if (!isfavorite()) + if (!m_populated_favorites) { game_driver const *const driver(reinterpret_cast<game_driver const *>(menu_event->itemref)); - if (!mfav.isgame_favorite(driver)) + if (!mfav.is_favorite_system(*driver)) { - mfav.add_favorite_game(driver); + mfav.add_favorite_system(*driver); machine().popmessage(_("%s\n added to favorites list."), driver->type.fullname()); } else { - mfav.remove_favorite_game(); + mfav.remove_favorite_system(*driver); machine().popmessage(_("%s\n removed from favorites list."), driver->type.fullname()); } } @@ -319,19 +473,19 @@ void menu_select_game::handle() { ui_software_info const *const swinfo(reinterpret_cast<ui_software_info const *>(menu_event->itemref)); machine().popmessage(_("%s\n removed from favorites list."), swinfo->longname); - mfav.remove_favorite_game(*swinfo); + mfav.remove_favorite_software(*swinfo); reset(reset_options::SELECT_FIRST); } } break; case IPT_UI_AUDIT_FAST: - 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); + if (m_persistent_data.unavailable_systems()) + menu::stack_push<menu_audit>(ui(), container(), m_persistent_data.sorted_list(), menu_audit::mode::FAST); break; case IPT_UI_AUDIT_ALL: - menu::stack_push<menu_audit>(ui(), container(), m_availsortedlist, menu_audit::mode::ALL); + menu::stack_push<menu_audit>(ui(), container(), m_persistent_data.sorted_list(), menu_audit::mode::ALL); break; } } @@ -348,98 +502,113 @@ void menu_select_game::handle() void menu_select_game::populate(float &customtop, float &custombottom) { - ui_globals::redraw_icon = true; - ui_globals::switch_image = true; + for (auto &icon : m_icons) // TODO: why is this here? maybe better on resize or setting change? + icon.second.texture.reset(); + + set_switch_image(); int old_item_selected = -1; - uint32_t flags_ui = FLAG_LEFT_ARROW | FLAG_RIGHT_ARROW; if (!isfavorite()) { + m_populated_favorites = false; + m_displaylist.clear(); + machine_filter const *const flt(m_persistent_data.filter_data().get_current_filter()); + // if search is not empty, find approximate matches if (!m_search.empty()) { populate_search(); + if (flt) + { + for (auto it = m_searchlist.begin(); (m_searchlist.end() != it) && (MAX_VISIBLE_SEARCH > m_displaylist.size()); ++it) + { + if (flt->apply(it->second)) + m_displaylist.emplace_back(it->second); + } + } + else + { + std::transform( + m_searchlist.begin(), + std::next(m_searchlist.begin(), (std::min)(m_searchlist.size(), MAX_VISIBLE_SEARCH)), + std::back_inserter(m_displaylist), + [] (auto const &entry) { return entry.second; }); + } } else { - // reset search string - m_search.clear(); - m_displaylist.clear(); - // if filter is set on category, build category list - auto const it(main_filters::filters.find(main_filters::actual)); - if (main_filters::filters.end() == it) - m_displaylist = m_availsortedlist; + std::vector<ui_system_info> const &sorted(m_persistent_data.sorted_list()); + if (!flt) + std::copy(sorted.begin(), sorted.end(), std::back_inserter(m_displaylist)); else - it->second->apply(m_availsortedlist.begin(), m_availsortedlist.end(), std::back_inserter(m_displaylist)); - - // iterate over entries - int curitem = 0; - for (ui_system_info const &elem : m_displaylist) - { - if (old_item_selected == -1 && elem.driver->name == reselect_last::driver()) - old_item_selected = curitem; + flt->apply(sorted.begin(), sorted.end(), std::back_inserter(m_displaylist)); + } - bool cloneof = strcmp(elem.driver->parent, "0"); - if (cloneof) - { - int cx = driver_list::find(elem.driver->parent); - if (cx != -1 && ((driver_list::driver(cx).flags & machine_flags::IS_BIOS_ROOT) != 0)) - cloneof = false; - } + // iterate over entries + int curitem = 0; + for (ui_system_info const &elem : m_displaylist) + { + if (old_item_selected == -1 && elem.driver->name == reselect_last::driver()) + old_item_selected = curitem; - item_append(elem.driver->type.fullname(), "", (cloneof) ? (flags_ui | FLAG_INVERT) : flags_ui, (void *)elem.driver); - curitem++; + bool cloneof = strcmp(elem.driver->parent, "0"); + if (cloneof) + { + 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.driver->type.fullname(), "", (cloneof) ? (FLAGS_UI | FLAG_INVERT) : FLAGS_UI, (void *)elem.driver); + curitem++; } } else { // populate favorites list + m_populated_favorites = true; m_search.clear(); - int curitem = 0; - - // iterate over entries - for (auto & favmap : mame_machine_manager::instance()->favorite().m_list) - { - auto flags = flags_ui | FLAG_UI_FAVORITE; - if (favmap.second.startempty == 1) - { - if (old_item_selected == -1 && favmap.second.shortname == reselect_last::driver()) - old_item_selected = curitem; - - bool cloneof = strcmp(favmap.second.driver->parent, "0"); - if (cloneof) + mame_machine_manager::instance()->favorite().apply_sorted( + [this, &old_item_selected, curitem = 0] (ui_software_info const &info) mutable { - int cx = driver_list::find(favmap.second.driver->parent); - if (cx != -1 && ((driver_list::driver(cx).flags & machine_flags::IS_BIOS_ROOT) != 0)) - cloneof = false; - } + if (info.startempty == 1) + { + if (old_item_selected == -1 && info.shortname == reselect_last::driver()) + old_item_selected = curitem; - item_append(favmap.second.longname, "", (cloneof) ? (flags | FLAG_INVERT) : flags, (void *)&favmap.second); - } - else - { - if (old_item_selected == -1 && favmap.second.shortname == reselect_last::driver()) - old_item_selected = curitem; - item_append(favmap.second.longname, favmap.second.devicetype, - favmap.second.parentname.empty() ? flags : (FLAG_INVERT | flags), (void *)&favmap.second); - } - curitem++; - } + bool cloneof = strcmp(info.driver->parent, "0"); + if (cloneof) + { + int cx = driver_list::find(info.driver->parent); + if (cx != -1 && ((driver_list::driver(cx).flags & machine_flags::IS_BIOS_ROOT) != 0)) + cloneof = false; + } + + item_append(info.longname, "", cloneof ? (FLAGS_UI | FLAG_INVERT) : FLAGS_UI, (void *)&info); + } + else + { + if (old_item_selected == -1 && info.shortname == reselect_last::driver()) + old_item_selected = curitem; + item_append(info.longname, info.devicetype, + info.parentname.empty() ? FLAGS_UI : (FLAG_INVERT | FLAGS_UI), (void *)&info); + } + curitem++; + }); } - item_append(menu_item_type::SEPARATOR, flags_ui); + item_append(menu_item_type::SEPARATOR, FLAGS_UI); // add special items if (stack_has_special_main_menu()) { - item_append(_("Configure Options"), "", flags_ui, (void *)(uintptr_t)CONF_OPTS); - item_append(_("Configure Machine"), "", flags_ui, (void *)(uintptr_t)CONF_MACHINE); + item_append(_("Configure Options"), "", FLAGS_UI, (void *)(uintptr_t)CONF_OPTS); + item_append(_("Configure Machine"), "", FLAGS_UI, (void *)(uintptr_t)CONF_MACHINE); skip_main_items = 2; if (machine().options().plugins()) { - item_append(_("Plugins"), "", flags_ui, (void *)(uintptr_t)CONF_PLUGINS); + item_append(_("Plugins"), "", FLAGS_UI, (void *)(uintptr_t)CONF_PLUGINS); skip_main_items++; } } @@ -487,11 +656,11 @@ void menu_select_game::build_available_list() // build a name for it for (src = dir->name; *src != 0 && *src != '.' && dst < &drivername[ARRAY_LENGTH(drivername) - 1]; ++src) - *dst++ = tolower((uint8_t) * src); + *dst++ = tolower(uint8_t(*src)); *dst = 0; - int drivnum = driver_list::find(drivername); - if (drivnum != -1 && !included[drivnum]) + int const drivnum = driver_list::find(drivername); + if (0 <= drivnum) included[drivnum] = true; } @@ -562,18 +731,9 @@ void menu_select_game::build_available_list() } } - // sort - 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); }); + // copy into the persistent sorted list + for (ui_system_info &info : m_persistent_data.sorted_list()) + info.available = included[info.index]; } @@ -609,13 +769,17 @@ void menu_select_game::inkey_select(const event *menu_event) if ((uintptr_t)driver == CONF_OPTS) { // special case for configure options - menu::stack_push<menu_game_options>(ui(), container()); + menu::stack_push<menu_game_options>( + ui(), + container(), + m_persistent_data.filter_data(), + [this] () { reset(reset_options::SELECT_FIRST); }); } else if (uintptr_t(driver) == CONF_MACHINE) { // special case for configure machine if (m_prev_selected) - menu::stack_push<menu_machine_configure>(ui(), container(), reinterpret_cast<const game_driver *>(m_prev_selected)); + menu::stack_push<menu_machine_configure>(ui(), container(), *reinterpret_cast<const game_driver *>(m_prev_selected)); return; } else if ((uintptr_t)driver == CONF_PLUGINS) @@ -640,7 +804,7 @@ void menu_select_game::inkey_select(const event *menu_event) { if (!swlistdev.get_info().empty()) { - menu::stack_push<menu_select_software>(ui(), container(), driver); + menu::stack_push<menu_select_software>(ui(), container(), *driver); return; } } @@ -667,7 +831,11 @@ void menu_select_game::inkey_select_favorite(const event *menu_event) if ((uintptr_t)ui_swinfo == CONF_OPTS) { // special case for configure options - menu::stack_push<menu_game_options>(ui(), container()); + menu::stack_push<menu_game_options>( + ui(), + container(), + m_persistent_data.filter_data(), + [this] () { reset(reset_options::SELECT_FIRST); }); } else if ((uintptr_t)ui_swinfo == CONF_MACHINE) { @@ -675,7 +843,15 @@ void menu_select_game::inkey_select_favorite(const event *menu_event) if (m_prev_selected) { ui_software_info *swinfo = reinterpret_cast<ui_software_info *>(m_prev_selected); - menu::stack_push<menu_machine_configure>(ui(), container(), (const game_driver *)swinfo->driver); + menu::stack_push<menu_machine_configure>( + ui(), + container(), + *swinfo->driver, + [this, empty = swinfo->startempty] (bool fav, bool changed) + { + if (changed) + reset(empty ? reset_options::SELECT_FIRST : reset_options::REMEMBER_REF); + }); } return; } @@ -698,7 +874,7 @@ void menu_select_game::inkey_select_favorite(const event *menu_event) { if (!swlistdev.get_info().empty()) { - menu::stack_push<menu_select_software>(ui(), container(), ui_swinfo->driver); + menu::stack_push<menu_select_software>(ui(), container(), *ui_swinfo->driver); return; } } @@ -746,7 +922,7 @@ void menu_select_game::inkey_select_favorite(const event *menu_event) bool menu_select_game::isfavorite() const { - return machine_filter::FAVORITE == main_filters::actual; + return machine_filter::FAVORITE == m_persistent_data.filter_data().get_current_filter_type(); } @@ -771,7 +947,7 @@ void menu_select_game::change_info_pane(int delta) game_driver const *drv; ui_software_info const *soft; get_selection(soft, drv); - if (!isfavorite()) + if (!m_populated_favorites) { if (uintptr_t(drv) > skip_main_items) cap_delta(ui_globals::curdats_view, ui_globals::curdats_total); @@ -791,49 +967,59 @@ void menu_select_game::change_info_pane(int delta) void menu_select_game::populate_search() { - // allocate memory to track the penalty value - std::vector<int> penalty(VISIBLE_GAMES_IN_SEARCH, 9999); - int index = 0; - for (; index < m_displaylist.size(); ++index) + // ensure search list is populated + if (m_searchlist.empty()) { - // pick the best match between driver name and description - 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); + std::vector<ui_system_info> const &sorted(m_persistent_data.sorted_list()); + m_searchlist.reserve(sorted.size()); + for (ui_system_info const &info : sorted) + m_searchlist.emplace_back(1.0, std::ref(info)); + } - // insert into the sorted table of matches - for (int matchnum = VISIBLE_GAMES_IN_SEARCH - 1; matchnum >= 0; --matchnum) - { - // stop if we're worse than the current entry - if (curpenalty >= penalty[matchnum]) - break; + // keep track of what we matched against + const std::u32string ucs_search(ustr_from_utf8(normalize_unicode(m_search, unicode_normalization_form::D, true))); + + // match shortnames + if (m_persistent_data.is_available(persistent_data::AVAIL_UCS_SHORTNAME)) + { + m_searched_fields |= persistent_data::AVAIL_UCS_SHORTNAME; + for (std::pair<double, std::reference_wrapper<ui_system_info const> > &info : m_searchlist) + info.first = util::edit_distance(ucs_search, info.second.get().ucs_shortname); + } - // as long as this isn't the last entry, bump this one down - if (matchnum < VISIBLE_GAMES_IN_SEARCH - 1) + // match descriptions + if (m_persistent_data.is_available(persistent_data::AVAIL_UCS_DESCRIPTION)) + { + m_searched_fields |= persistent_data::AVAIL_UCS_DESCRIPTION; + for (std::pair<double, std::reference_wrapper<ui_system_info const> > &info : m_searchlist) + { + if (info.first) { - penalty[matchnum + 1] = penalty[matchnum]; - m_searchlist[matchnum + 1] = m_searchlist[matchnum]; + double const penalty(util::edit_distance(ucs_search, info.second.get().ucs_description)); + info.first = (std::min)(penalty, info.first); } - - m_searchlist[matchnum] = m_displaylist[index].driver; - penalty[matchnum] = curpenalty; } } - (index < VISIBLE_GAMES_IN_SEARCH) ? m_searchlist[index] = nullptr : m_searchlist[VISIBLE_GAMES_IN_SEARCH] = nullptr; - uint32_t flags_ui = FLAG_LEFT_ARROW | FLAG_RIGHT_ARROW; - for (int curitem = 0; m_searchlist[curitem]; ++curitem) + // match "<manufacturer> <description>" + if (m_persistent_data.is_available(persistent_data::AVAIL_UCS_MANUF_DESC)) { - bool cloneof = strcmp(m_searchlist[curitem]->parent, "0"); - if (cloneof) + m_searched_fields |= persistent_data::AVAIL_UCS_MANUF_DESC; + for (std::pair<double, std::reference_wrapper<ui_system_info const> > &info : m_searchlist) { - int cx = driver_list::find(m_searchlist[curitem]->parent); - if (cx != -1 && ((driver_list::driver(cx).flags & machine_flags::IS_BIOS_ROOT) != 0)) - cloneof = false; + if (info.first) + { + double const penalty(util::edit_distance(ucs_search, info.second.get().ucs_manufacturer_description)); + info.first = (std::min)(penalty, info.first); + } } - item_append(m_searchlist[curitem]->type.fullname(), "", (!cloneof) ? flags_ui : (FLAG_INVERT | flags_ui), - (void *)m_searchlist[curitem]); } + + // sort according to edit distance + std::stable_sort( + m_searchlist.begin(), + m_searchlist.end(), + [] (auto const &lhs, auto const &rhs) { return lhs.first < rhs.first; }); } //------------------------------------------------- @@ -889,6 +1075,21 @@ void menu_select_game::general_info(const game_driver *driver, std::string &buff else str << _("Sound\tOK\n"); + if (flags.unemulated_features() & device_t::feature::CAPTURE) + str << _("Capture\tUnimplemented\n"); + else if (flags.imperfect_features() & device_t::feature::CAPTURE) + str << _("Capture\tImperfect\n"); + + if (flags.unemulated_features() & device_t::feature::CAMERA) + str << _("Camera\tUnimplemented\n"); + else if (flags.imperfect_features() & device_t::feature::CAMERA) + str << _("Camera\tImperfect\n"); + + if (flags.unemulated_features() & device_t::feature::MICROPHONE) + str << _("Microphone\tUnimplemented\n"); + else if (flags.imperfect_features() & device_t::feature::MICROPHONE) + str << _("Microphone\tImperfect\n"); + if (flags.unemulated_features() & device_t::feature::CONTROLS) str << _("Controls\tUnimplemented\n"); else if (flags.imperfect_features() & device_t::feature::CONTROLS) @@ -904,15 +1105,10 @@ void menu_select_game::general_info(const game_driver *driver, std::string &buff else if (flags.imperfect_features() & device_t::feature::MOUSE) str << _("Mouse\tImperfect\n"); - if (flags.unemulated_features() & device_t::feature::MICROPHONE) - str << _("Microphone\tUnimplemented\n"); - else if (flags.imperfect_features() & device_t::feature::MICROPHONE) - str << _("Microphone\tImperfect\n"); - - if (flags.unemulated_features() & device_t::feature::CAMERA) - str << _("Camera\tUnimplemented\n"); - else if (flags.imperfect_features() & device_t::feature::CAMERA) - str << _("Camera\tImperfect\n"); + if (flags.unemulated_features() & device_t::feature::MEDIA) + str << _("Media\tUnimplemented\n"); + else if (flags.imperfect_features() & device_t::feature::MEDIA) + str << _("Media\tImperfect\n"); if (flags.unemulated_features() & device_t::feature::DISK) str << _("Disk\tUnimplemented\n"); @@ -924,6 +1120,31 @@ void menu_select_game::general_info(const game_driver *driver, std::string &buff else if (flags.imperfect_features() & device_t::feature::PRINTER) str << _("Printer\tImperfect\n"); + if (flags.unemulated_features() & device_t::feature::TAPE) + str << _("Mag. Tape\tUnimplemented\n"); + else if (flags.imperfect_features() & device_t::feature::TAPE) + str << _("Mag. Tape\tImperfect\n"); + + if (flags.unemulated_features() & device_t::feature::PUNCH) + str << _("Punch Tape\tUnimplemented\n"); + else if (flags.imperfect_features() & device_t::feature::PUNCH) + str << _("Punch Tape\tImperfect\n"); + + if (flags.unemulated_features() & device_t::feature::DRUM) + str << _("Mag. Drum\tUnimplemented\n"); + else if (flags.imperfect_features() & device_t::feature::DRUM) + str << _("Mag. Drum\tImperfect\n"); + + if (flags.unemulated_features() & device_t::feature::ROM) + str << _("(EP)ROM\tUnimplemented\n"); + else if (flags.imperfect_features() & device_t::feature::ROM) + str << _("(EP)ROM\tImperfect\n"); + + if (flags.unemulated_features() & device_t::feature::COMMS) + str << _("Communications\tUnimplemented\n"); + else if (flags.imperfect_features() & device_t::feature::COMMS) + str << _("Communications\tImperfect\n"); + if (flags.unemulated_features() & device_t::feature::LAN) str << _("LAN\tUnimplemented\n"); else if (flags.imperfect_features() & device_t::feature::LAN) @@ -987,71 +1208,86 @@ void menu_select_game::general_info(const game_driver *driver, std::string &buff buffer = str.str(); } -void menu_select_game::inkey_export() + +//------------------------------------------------- +// get (possibly cached) icon texture +//------------------------------------------------- + +render_texture *menu_select_game::get_icon_texture(int linenum, void *selectedref) { - std::vector<game_driver const *> list; - if (!m_search.empty()) - { - for (int curitem = 0; m_searchlist[curitem]; ++curitem) - list.push_back(m_searchlist[curitem]); - } - else + game_driver const *const driver(m_populated_favorites + ? reinterpret_cast<ui_software_info const *>(selectedref)->driver + : reinterpret_cast<game_driver const *>(selectedref)); + assert(driver); + + icon_cache::iterator icon(m_icons.find(driver)); + if ((m_icons.end() == icon) || !icon->second.texture) { - if (isfavorite()) + if (m_icon_paths.empty()) + m_icon_paths = make_icon_paths(nullptr); + + // allocate an entry or allocate a texture on forced redraw + if (m_icons.end() == icon) { - // iterate over favorites - for (auto & favmap : mame_machine_manager::instance()->favorite().m_list) - { - if (favmap.second.startempty == 1) - list.push_back(favmap.second.driver); - else - return; - } + icon = m_icons.emplace(driver, texture_ptr(machine().render().texture_alloc(), machine().render())).first; } else { - list.reserve(m_displaylist.size()); - for (ui_system_info const &info : m_displaylist) - list.emplace_back(info.driver); + assert(!icon->second.texture); + icon->second.texture.reset(machine().render().texture_alloc()); } + + // set clone status + bool cloneof = strcmp(driver->parent, "0"); + if (cloneof) + { + auto cx = driver_list::find(driver->parent); + if ((cx >= 0) && (driver_list::driver(cx).flags & machine_flags::IS_BIOS_ROOT)) + cloneof = false; + } + + bitmap_argb32 tmp; + emu_file snapfile(std::string(m_icon_paths), OPEN_FLAG_READ); + if (snapfile.open(std::string(driver->name), ".ico") == osd_file::error::NONE) + { + render_load_ico_highest_detail(snapfile, tmp); + snapfile.close(); + } + if (!tmp.valid() && cloneof && (snapfile.open(std::string(driver->parent), ".ico") == osd_file::error::NONE)) + { + render_load_ico_highest_detail(snapfile, tmp); + snapfile.close(); + } + + scale_icon(std::move(tmp), icon->second); } - menu::stack_push<menu_export>(ui(), container(), std::move(list)); + return icon->second.bitmap.valid() ? icon->second.texture.get() : nullptr; } -//------------------------------------------------- -// save drivers infos to file -//------------------------------------------------- -void menu_select_game::init_sorted_list() +void menu_select_game::inkey_export() { - if (!m_sortedlist.empty()) - return; - - // generate full list - std::unordered_set<std::string> manufacturers, years; - for (int x = 0; x < driver_list::total(); ++x) + std::vector<game_driver const *> list; + if (m_populated_favorites) { - game_driver const &driver(driver_list::driver(x)); - if (&driver != &GAME_NAME(___empty)) - { - if (driver.flags & machine_flags::IS_BIOS_ROOT) - m_isabios++; - - m_sortedlist.push_back(&driver); - manufacturers.emplace(c_mnfct::getname(driver.manufacturer)); - years.emplace(driver.year); - } + // iterate over favorites + mame_machine_manager::instance()->favorite().apply( + [&list] (ui_software_info const &info) + { + assert(info.driver); + if (info.startempty) + list.push_back(info.driver); + }); + } + else + { + list.reserve(m_displaylist.size()); + for (ui_system_info const &info : m_displaylist) + list.emplace_back(info.driver); } - // sort manufacturers - years and driver - for (auto it = manufacturers.begin(); manufacturers.end() != it; it = manufacturers.erase(it)) - c_mnfct::ui.emplace_back(*it); - std::sort(c_mnfct::ui.begin(), c_mnfct::ui.end(), [] (std::string const &x, std::string const &y) { return 0 > core_stricmp(x.c_str(), y.c_str()); }); - for (auto it = years.begin(); years.end() != it; it = years.erase(it)) - c_year::ui.emplace_back(*it); - std::stable_sort(c_year::ui.begin(), c_year::ui.end()); - std::stable_sort(m_sortedlist.begin(), m_sortedlist.end(), sorted_game_list); + menu::stack_push<menu_export>(ui(), container(), std::move(list)); } //------------------------------------------------- @@ -1093,27 +1329,18 @@ bool menu_select_game::load_available_machines() else available.emplace(std::move(readbuf)); } + file.close(); // 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) + for (ui_system_info &info : m_persistent_data.sorted_list()) { - 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::unordered_set<std::string>::iterator const it(available.find(&info.driver->name[0])); + bool const found(available.end() != it); + info.available = 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; } @@ -1126,9 +1353,9 @@ void menu_select_game::load_custom_filters() emu_file file(ui().options().ui_path(), OPEN_FLAG_READ); if (file.open("custom_", emulator_info::get_configname(), "_filter.ini") == osd_file::error::NONE) { - machine_filter::ptr flt(machine_filter::create(file)); + machine_filter::ptr flt(machine_filter::create(file, m_persistent_data.filter_data())); if (flt) - main_filters::filters[flt->get_type()] = std::move(flt); // not emplace/insert - could replace bogus filter from ui.ini line + m_persistent_data.filter_data().set_filter(std::move(flt)); // not emplace/insert - could replace bogus filter from ui.ini line file.close(); } @@ -1141,7 +1368,8 @@ void menu_select_game::load_custom_filters() float menu_select_game::draw_left_panel(float x1, float y1, float x2, float y2) { - return menu_select_launch::draw_left_panel<machine_filter>(main_filters::actual, main_filters::filters, x1, y1, x2, y2); + machine_filter_data &filter_data(m_persistent_data.filter_data()); + return menu_select_launch::draw_left_panel<machine_filter>(filter_data.get_current_filter_type(), filter_data.get_filters(), x1, y1, x2, y2); } @@ -1151,7 +1379,7 @@ float menu_select_game::draw_left_panel(float x1, float y1, float x2, float y2) void menu_select_game::get_selection(ui_software_info const *&software, game_driver const *&driver) const { - if (item[0].flags & FLAG_UI_FAVORITE) // TODO: work out why this doesn't use isfavorite() + if (m_populated_favorites) { software = reinterpret_cast<ui_software_info const *>(get_selection_ptr()); driver = software ? software->driver : nullptr; @@ -1170,18 +1398,18 @@ void menu_select_game::make_topbox_text(std::string &line0, std::string &line1, bare_build_version, visible_items, (driver_list::total() - 1), - m_isabios); + m_persistent_data.bios_count()); - if (isfavorite()) + if (m_populated_favorites) { line1.clear(); } else { - auto const it(main_filters::filters.find(main_filters::actual)); - char const *const filter((main_filters::filters.end() != it) ? it->second->filter_text() : nullptr); + machine_filter const *const it(m_persistent_data.filter_data().get_current_filter()); + char const *const filter(it ? it->filter_text() : nullptr); if (filter) - line1 = string_format(_("%1$s: %2$s - Search: %3$s_"), it->second->display_name(), filter, m_search); + line1 = string_format(_("%1$s: %2$s - Search: %3$s_"), it->display_name(), filter, m_search); else line1 = string_format(_("Search: %1$s_"), m_search); } @@ -1208,15 +1436,12 @@ void menu_select_game::filter_selected() { if ((machine_filter::FIRST <= m_filter_highlight) && (machine_filter::LAST >= m_filter_highlight)) { - m_search.clear(); - auto it(main_filters::filters.find(machine_filter::type(m_filter_highlight))); - if (main_filters::filters.end() == it) - it = main_filters::filters.emplace(machine_filter::type(m_filter_highlight), machine_filter::create(machine_filter::type(m_filter_highlight))).first; - it->second->show_ui( + m_persistent_data.filter_data().get_filter(machine_filter::type(m_filter_highlight)).show_ui( ui(), container(), [this] (machine_filter &filter) { + set_switch_image(); machine_filter::type const new_type(filter.get_type()); if (machine_filter::CUSTOM == new_type) { @@ -1227,7 +1452,7 @@ void menu_select_game::filter_selected() file.close(); } } - main_filters::actual = new_type; + m_persistent_data.filter_data().set_current_filter_type(new_type); reset(reset_options::SELECT_FIRST); }); } |