diff options
Diffstat (limited to 'src/frontend/mame/ui/selsoft.cpp')
-rw-r--r-- | src/frontend/mame/ui/selsoft.cpp | 851 |
1 files changed, 461 insertions, 390 deletions
diff --git a/src/frontend/mame/ui/selsoft.cpp b/src/frontend/mame/ui/selsoft.cpp index 5a14e3dac7f..5a0a4f1eb87 100644 --- a/src/frontend/mame/ui/selsoft.cpp +++ b/src/frontend/mame/ui/selsoft.cpp @@ -14,106 +14,396 @@ #include "ui/ui.h" #include "ui/icorender.h" #include "ui/inifile.h" +#include "ui/miscmenu.h" #include "ui/selector.h" -#include "audit.h" #include "drivenum.h" #include "emuopts.h" +#include "fileio.h" #include "mame.h" #include "rendutil.h" #include "softlist_dev.h" #include "uiinput.h" #include "luaengine.h" +#include "corestr.h" +#include "path.h" +#include "unicode.h" + #include <algorithm> #include <iterator> #include <functional> +#include <thread> +#include <locale> namespace ui { -namespace { +struct menu_select_software::search_item +{ + search_item(search_item const &) = default; + search_item(search_item &&) = default; + search_item &operator=(search_item const &) = default; + search_item &operator=(search_item &&) = default; + + search_item(ui_software_info const &s) + : software(s) + , ucs_shortname(ustr_from_utf8(normalize_unicode(s.shortname, unicode_normalization_form::D, true))) + , ucs_longname(ustr_from_utf8(normalize_unicode(s.longname, unicode_normalization_form::D, true))) + , ucs_alttitles() + , penalty(1.0) + { + ucs_alttitles.reserve(s.alttitles.size()); + for (std::string const &alttitle : s.alttitles) + ucs_alttitles.emplace_back(ustr_from_utf8(normalize_unicode(alttitle, unicode_normalization_form::D, true))); + } -//------------------------------------------------- -// compares two items in the software list and -// sort them by parent-clone -//------------------------------------------------- + void set_penalty(std::u32string const &search) + { + penalty = util::edit_distance(search, ucs_shortname); + if (penalty) + penalty = (std::min)(penalty, util::edit_distance(search, ucs_longname)); + auto it(ucs_alttitles.begin()); + while (penalty && (ucs_alttitles.end() != it)) + penalty = (std::min)(penalty, util::edit_distance(search, *it++)); + } + + std::reference_wrapper<ui_software_info const> software; + std::u32string ucs_shortname; + std::u32string ucs_longname; + std::vector<std::u32string> ucs_alttitles; + double penalty; +}; -bool compare_software(ui_software_info const &a, ui_software_info const &b) -{ - bool const clonex = !a.parentname.empty() && !a.parentlongname.empty(); - bool const cloney = !b.parentname.empty() && !b.parentlongname.empty(); - if (!clonex && !cloney) + +class menu_select_software::machine_data +{ +public: + machine_data(menu_select_software &menu) + : m_icons(MAX_ICONS_RENDER) + , m_has_empty_start(false) + , m_filter_data() + , m_filters() + , m_filter_type(software_filter::ALL) + , m_swinfo() + , m_searchlist() + , m_right_panel(menu.right_panel()) + , m_right_image(menu.right_image()) { - return 0 > core_stricmp(a.longname.c_str(), b.longname.c_str()); + // add start empty item + m_swinfo.emplace_back(*menu.m_system.driver); + + machine_config config(*menu.m_system.driver, menu.machine().options()); + + // see if any media devices require an image to be loaded + m_has_empty_start = true; + for (device_image_interface &image : image_interface_enumerator(config.root_device())) + { + if (!image.filename() && image.must_be_loaded()) + { + m_has_empty_start = false; + break; + } + } + + // iterate through all software lists + std::vector<std::size_t> orphans; + struct orphan_less + { + std::vector<ui_software_info> &swinfo; + bool operator()(std::string const &a, std::string const &b) const { return a < b; }; + bool operator()(std::string const &a, std::size_t b) const { return a < swinfo[b].parentname; }; + bool operator()(std::size_t a, std::string const &b) const { return swinfo[a].parentname < b; }; + bool operator()(std::size_t a, std::size_t b) const { return swinfo[a].parentname < swinfo[b].parentname; }; + }; + orphan_less const orphan_cmp{ m_swinfo }; + for (software_list_device &swlist : software_list_device_enumerator(config.root_device())) + { + m_filter_data.add_list(swlist.list_name(), swlist.description()); + menu.check_for_icons(swlist.list_name().c_str()); + orphans.clear(); + std::map<std::string, std::string> parentnames; + std::map<std::string, std::string>::const_iterator prevparent(parentnames.end()); + for (const software_info &swinfo : swlist.get_info()) + { + // check for previously-encountered clones + if (swinfo.parentname().empty()) + { + if (parentnames.emplace(swinfo.shortname(), swinfo.longname()).second) + { + auto const clones(std::equal_range(orphans.begin(), orphans.end(), swinfo.shortname(), orphan_cmp)); + for (auto it = clones.first; clones.second != it; ++it) + m_swinfo[*it].parentlongname = swinfo.longname(); + orphans.erase(clones.first, clones.second); + } + else + { + assert([] (auto const x) { return x.first == x.second; } (std::equal_range(orphans.begin(), orphans.end(), swinfo.shortname(), orphan_cmp))); + } + } + + const software_part &part = swinfo.parts().front(); + if (swlist.is_compatible(part) == SOFTWARE_IS_COMPATIBLE) + { + char const *instance_name(nullptr); + char const *type_name(nullptr); + for (device_image_interface &image : image_interface_enumerator(config.root_device())) + { + char const *const interface = image.image_interface(); + if (interface && part.matches_interface(interface)) + { + instance_name = image.instance_name().c_str(); + type_name = image.image_type_name(); + break; + } + } + + if (instance_name && type_name) + { + // add to collection and try to resolve parent if applicable + auto const ins(m_swinfo.emplace(m_swinfo.end(), swinfo, part, *menu.m_system.driver, swlist.list_name(), instance_name, type_name)); + if (!swinfo.parentname().empty()) + { + if ((parentnames.end() == prevparent) || (swinfo.parentname() != prevparent->first)) + prevparent = parentnames.find(swinfo.parentname()); + + if (parentnames.end() != prevparent) + { + ins->parentlongname = prevparent->second; + } + else + { + orphans.emplace( + std::upper_bound(orphans.begin(), orphans.end(), swinfo.parentname(), orphan_cmp), + std::distance(m_swinfo.begin(), ins)); + } + } + + // populate filter choices + m_filter_data.add_region(ins->longname); + m_filter_data.add_publisher(ins->publisher); + m_filter_data.add_year(ins->year); + for (software_info_item const &i : ins->info) + m_filter_data.add_info(i); + m_filter_data.add_device_type(ins->devicetype); + } + } + } + } + + std::string searchstr, curpath; + for (auto &elem : m_filter_data.list_names()) + { + path_iterator path(menu.machine().options().media_path()); + while (path.next(curpath)) + { + searchstr.assign(curpath).append(PATH_SEPARATOR).append(elem).append(";"); + file_enumerator fpath(searchstr.c_str()); + + // iterate while we get new objects + osd::directory::entry const *dir; + while ((dir = fpath.next()) != nullptr) + { + std::string name; + if (dir->type == osd::directory::entry::entry_type::FILE) + name = strmakelower(core_filename_extract_base(dir->name, true)); + else if (dir->type == osd::directory::entry::entry_type::DIR && strcmp(dir->name, ".") != 0) + name = strmakelower(dir->name); + else + continue; + + for (auto & yelem : m_swinfo) + if (yelem.shortname == name && yelem.listname == elem) + { + yelem.available = true; + break; + } + } + } + } + + // sort array + std::locale const lcl; + std::collate<wchar_t> const &coll = std::use_facet<std::collate<wchar_t> >(lcl); + auto const compare_names = + [&coll] (std::string const &x, std::string const &y) -> bool + { + std::wstring const wx = wstring_from_utf8(x); + std::wstring const wy = wstring_from_utf8(y); + return 0 > coll.compare(wx.data(), wx.data() + wx.size(), wy.data(), wy.data() + wy.size()); + }; + std::stable_sort( + m_swinfo.begin() + 1, + m_swinfo.end(), + [&compare_names] (ui_software_info const &a, ui_software_info const &b) -> bool + { + bool const clonex = !a.parentname.empty() && !a.parentlongname.empty(); + bool const cloney = !b.parentname.empty() && !b.parentlongname.empty(); + + if (!clonex && !cloney) + { + return compare_names(a.longname, b.longname); + } + else if (!clonex && cloney) + { + if ((a.shortname == b.parentname) && (a.instance == b.instance)) + return true; + else + return compare_names(a.longname, b.parentlongname); + } + else if (clonex && !cloney) + { + if ((a.parentname == b.shortname) && (a.instance == b.instance)) + return false; + else + return compare_names(a.parentlongname, b.longname); + } + else if ((a.parentname == b.parentname) && (a.instance == b.instance)) + { + return compare_names(a.longname, b.longname); + } + else + { + return compare_names(a.parentlongname, b.parentlongname); + } + }); + + // start populating search info in background + m_search_thread = std::make_unique<std::thread>( + [this] () + { + m_searchlist.reserve(m_swinfo.size()); + for (ui_software_info const &sw : m_swinfo) + { + if (!sw.startempty) + m_searchlist.emplace_back(sw); + } + }); + + // build derivative filter data + m_filter_data.finalise(); + + // load custom filters info from file + emu_file file(menu.ui().options().ui_path(), OPEN_FLAG_READ); + if (!file.open(util::string_format("custom_%s_filter.ini", menu.m_system.driver->name))) + { + software_filter::ptr flt(software_filter::create(file, m_filter_data)); + if (flt) + m_filters.emplace(flt->get_type(), std::move(flt)); + file.close(); + } } - else if (!clonex && cloney) + + ~machine_data() { - if ((a.shortname == b.parentname) && (a.instance == b.instance)) - return true; - else - return 0 > core_stricmp(a.longname.c_str(), b.parentlongname.c_str()); + if (m_search_thread) + m_search_thread->join(); } - else if (clonex && !cloney) + + icon_cache &icons() { return m_icons; } + + bool has_empty_start() const noexcept { return m_has_empty_start; } + + filter_map const &filters() const noexcept { return m_filters; } + + software_filter::type filter_type() const noexcept { return m_filter_type; } + void set_filter_type(software_filter::type type) noexcept { m_filter_type = type; } + + software_filter const *current_filter() const noexcept { - if ((a.parentname == b.shortname) && (a.instance == b.instance)) - return false; - else - return 0 > core_stricmp(a.parentlongname.c_str(), b.longname.c_str()); + auto const found(m_filters.find(m_filter_type)); + return (m_filters.end() != found) ? found->second.get() : nullptr; } - else if ((a.parentname == b.parentname) && (a.instance == b.instance)) + + software_filter &get_filter(software_filter::type type) { - return 0 > core_stricmp(a.longname.c_str(), b.longname.c_str()); + filter_map::const_iterator it(m_filters.find(type)); + if (m_filters.end() != it) + return *it->second; + else + return *m_filters.emplace(type, software_filter::create(type, m_filter_data)).first->second; } - else + + std::vector<ui_software_info> const &swinfo() const noexcept { return m_swinfo; } + + std::vector<search_item> const &find_matches(std::string const &search) { - return 0 > core_stricmp(a.parentlongname.c_str(), b.parentlongname.c_str()); + // ensure search list is populated + if (m_search_thread) + { + m_search_thread->join(); + m_search_thread.reset(); + } + + // update search + const std::u32string ucs_search(ustr_from_utf8(normalize_unicode(search, unicode_normalization_form::D, true))); + for (search_item &entry : m_searchlist) + entry.set_penalty(ucs_search); + + // sort according to edit distance + std::stable_sort( + m_searchlist.begin(), + m_searchlist.end(), + [] (search_item const &lhs, search_item const &rhs) { return lhs.penalty < rhs.penalty; }); + + // return reference to search results + return m_searchlist; } -} -} // anonymous namespace + u8 right_panel() const { return m_right_panel; } + u8 right_image() const { return m_right_image; } + void set_right_panel(u8 index) { m_right_panel = index; } + void set_right_image(u8 index) { m_right_image = index; } +private: + icon_cache m_icons; + bool m_has_empty_start; + software_filter_data m_filter_data; + filter_map m_filters; + software_filter::type m_filter_type; + std::vector<ui_software_info> m_swinfo; + std::vector<search_item> m_searchlist; -menu_select_software::search_item::search_item(ui_software_info const &s) - : software(s) - , ucs_shortname(ustr_from_utf8(normalize_unicode(s.shortname, unicode_normalization_form::D, true))) - , ucs_longname(ustr_from_utf8(normalize_unicode(s.longname, unicode_normalization_form::D, true))) - , penalty(1.0) -{ -} + u8 m_right_panel; + u8 m_right_image; -void menu_select_software::search_item::set_penalty(std::u32string const &search) -{ - // TODO: search alternate title as well - penalty = util::edit_distance(search, ucs_shortname); - if (penalty) - penalty = (std::min)(penalty, util::edit_distance(search, ucs_longname)); -} + std::unique_ptr<std::thread> m_search_thread; +}; //------------------------------------------------- // ctor //------------------------------------------------- -menu_select_software::menu_select_software(mame_ui_manager &mui, render_container &container, game_driver const &driver) +menu_select_software::menu_select_software(mame_ui_manager &mui, render_container &container, ui_system_info const &system) : menu_select_launch(mui, container, true) , m_icon_paths() - , m_icons(MAX_ICONS_RENDER) - , m_driver(driver) - , m_has_empty_start(false) - , m_filter_data() - , m_filters() - , m_filter_type(software_filter::ALL) - , m_swinfo() - , m_searchlist() + , m_system(system) , m_displaylist() { reselect_last::reselect(false); - build_software_list(); - load_sw_custom_filters(); - m_filter_highlight = m_filter_type; + using machine_data_cache = util::lru_cache_map<game_driver const *, std::shared_ptr<machine_data> >; + auto &cached(mui.get_session_data<menu_select_software, machine_data_cache>(8)[system.driver]); + if (cached) + { + // restore last right panel settings for this machine + set_right_panel(cached->right_panel()); + set_right_image(cached->right_image()); + } + else + { + // restore last right panel settings from UI options + ui_options &moptions = ui().options(); + set_right_panel(moptions.software_right_panel()); + set_right_image(moptions.software_right_image()); + + cached = std::make_shared<machine_data>(*this); + } + m_data = cached; + + m_filter_highlight = m_data->filter_type(); set_switch_image(); ui_globals::cur_sw_dats_view = 0; @@ -132,75 +422,27 @@ menu_select_software::~menu_select_software() // handle //------------------------------------------------- -void menu_select_software::handle() +bool menu_select_software::handle(event const *ev) { - if (m_prev_selected == nullptr) - m_prev_selected = item(0).ref; + if (m_prev_selected == nullptr && item_count() > 0) + m_prev_selected = item(0).ref(); - // ignore pause keys by swallowing them before we process the menu - machine().ui_input().pressed(IPT_UI_PAUSE); + // FIXME: everything above here used run before events were processed // process the menu - const event *menu_event = process(PROCESS_LR_REPEAT); - if (menu_event) + bool changed = false; + if (ev) { if (dismiss_error()) { - // reset the error on any future event + // reset the error on any subsequent menu event + changed = true; } - else switch (menu_event->iptkey) + else switch (ev->iptkey) { case IPT_UI_SELECT: - if ((get_focus() == focused_menu::MAIN) && menu_event->itemref) - inkey_select(menu_event); - break; - - case IPT_UI_LEFT: - if (ui_globals::rpanel == RP_IMAGES) - { - // Images - previous_image_view(); - } - else if (ui_globals::rpanel == RP_INFOS && ui_globals::cur_sw_dats_view > 0) - { - // Infos - ui_globals::cur_sw_dats_view--; - m_topline_datsview = 0; - } - break; - - case IPT_UI_RIGHT: - if (ui_globals::rpanel == RP_IMAGES) - { - // Images - next_image_view(); - } - else if (ui_globals::rpanel == RP_INFOS && ui_globals::cur_sw_dats_view < (ui_globals::cur_sw_dats_total - 1)) - { - // Infos - ui_globals::cur_sw_dats_view++; - m_topline_datsview = 0; - } - break; - - case IPT_UI_UP: - if ((get_focus() == focused_menu::LEFT) && (software_filter::FIRST < m_filter_highlight)) - --m_filter_highlight; - break; - - case IPT_UI_DOWN: - if ((get_focus() == focused_menu::LEFT) && (software_filter::LAST > m_filter_highlight)) - ++m_filter_highlight; - break; - - case IPT_UI_HOME: - if (get_focus() == focused_menu::LEFT) - m_filter_highlight = software_filter::FIRST; - break; - - case IPT_UI_END: - if (get_focus() == focused_menu::LEFT) - m_filter_highlight = software_filter::LAST; + if ((get_focus() == focused_menu::MAIN) && ev->itemref) + changed = inkey_select(ev); break; case IPT_UI_DATS: @@ -208,12 +450,12 @@ void menu_select_software::handle() break; default: - if (menu_event->itemref) + if (ev->itemref) { - if (menu_event->iptkey == IPT_UI_FAVORITES) + if (ev->iptkey == IPT_UI_FAVORITES) { // handle UI_FAVORITES - ui_software_info *swinfo = (ui_software_info *)menu_event->itemref; + ui_software_info *swinfo = (ui_software_info *)ev->itemref; if ((uintptr_t)swinfo > 2) { @@ -223,79 +465,95 @@ void menu_select_software::handle() mfav.add_favorite_software(*swinfo); machine().popmessage(_("%s\n added to favorites list."), swinfo->longname); } - else { machine().popmessage(_("%s\n removed from favorites list."), swinfo->longname); mfav.remove_favorite_software(*swinfo); } + changed = true; } } } } } - // if we're in an error state, overlay an error message - draw_error_text(); + return changed; +} + +//------------------------------------------------- +// recompute_metrics +//------------------------------------------------- + +void menu_select_software::recompute_metrics(uint32_t width, uint32_t height, float aspect) +{ + menu_select_launch::recompute_metrics(width, height, aspect); + + // configure the custom rendering + set_custom_space(4.0F * line_height() + 5.0F * tb_border(), 4.0F * line_height() + 3.0F * tb_border()); +} + +//------------------------------------------------- +// menu_deactivated +//------------------------------------------------- + +void menu_select_software::menu_deactivated() +{ + menu_select_launch::menu_deactivated(); + + // save last right panel settings + m_data->set_right_panel(right_panel()); + m_data->set_right_image(right_image()); + ui_options &mopt = ui().options(); + mopt.set_value(OPTION_SOFTWARE_RIGHT_PANEL, right_panel_config_string(), OPTION_PRIORITY_CMDLINE); + mopt.set_value(OPTION_SOFTWARE_RIGHT_IMAGE, right_image_config_string(), OPTION_PRIORITY_CMDLINE); } //------------------------------------------------- // populate //------------------------------------------------- -void menu_select_software::populate(float &customtop, float &custombottom) +void menu_select_software::populate() { - for (auto &icon : m_icons) // TODO: why is this here? maybe better on resize or setting change? + for (auto &icon : m_data->icons()) // TODO: why is this here? maybe better on resize or setting change? icon.second.texture.reset(); - uint32_t flags_ui = FLAG_LEFT_ARROW | FLAG_RIGHT_ARROW; - m_has_empty_start = true; int old_software = -1; - // FIXME: why does it do this relatively expensive operation every time? - machine_config config(m_driver, machine().options()); - for (device_image_interface &image : image_interface_iterator(config.root_device())) - { - if (!image.filename() && image.must_be_loaded()) - { - m_has_empty_start = false; - break; - } - } - // start with an empty list m_displaylist.clear(); - filter_map::const_iterator const flt(m_filters.find(m_filter_type)); + software_filter const *const flt(m_data->current_filter()); // no active search if (m_search.empty()) { - // if the device can be loaded empty, add an item - if (m_has_empty_start) - item_append("[Start empty]", "", flags_ui, (void *)&m_swinfo[0]); + // add an item to start empty or let the user use the file manager + item_append( + m_data->has_empty_start() ? _("[Start empty]") : _("[Use file manager]"), + 0, + (void *)&m_data->swinfo()[0]); - if (m_filters.end() == flt) - std::copy(std::next(m_swinfo.begin()), m_swinfo.end(), std::back_inserter(m_displaylist)); + if (!flt) + std::copy(std::next(m_data->swinfo().begin()), m_data->swinfo().end(), std::back_inserter(m_displaylist)); else - flt->second->apply(std::next(m_swinfo.begin()), m_swinfo.end(), std::back_inserter(m_displaylist)); + flt->apply(std::next(m_data->swinfo().begin()), m_data->swinfo().end(), std::back_inserter(m_displaylist)); } else { - find_matches(); + std::vector<search_item> const &searchlist = m_data->find_matches(m_search); - if (m_filters.end() == flt) + if (!flt) { std::transform( - m_searchlist.begin(), - std::next(m_searchlist.begin(), (std::min)(m_searchlist.size(), MAX_VISIBLE_SEARCH)), + searchlist.begin(), + std::next(searchlist.begin(), (std::min)(searchlist.size(), MAX_VISIBLE_SEARCH)), std::back_inserter(m_displaylist), [] (search_item const &entry) { return entry.software; }); } else { - for (auto it = m_searchlist.begin(); (m_searchlist.end() != it) && (MAX_VISIBLE_SEARCH > m_displaylist.size()); ++it) + for (auto it = searchlist.begin(); (searchlist.end() != it) && (MAX_VISIBLE_SEARCH > m_displaylist.size()); ++it) { - if (flt->second->apply(it->software)) + if (flt->apply(it->software)) m_displaylist.emplace_back(it->software); } } @@ -307,165 +565,30 @@ void menu_select_software::populate(float &customtop, float &custombottom) if (reselect_last::software() == "[Start empty]" && !reselect_last::driver().empty()) old_software = 0; else if (m_displaylist[curitem].get().shortname == reselect_last::software() && m_displaylist[curitem].get().listname == reselect_last::swlist()) - old_software = m_has_empty_start ? curitem + 1 : curitem; + old_software = curitem + 1; item_append( m_displaylist[curitem].get().longname, m_displaylist[curitem].get().devicetype, - m_displaylist[curitem].get().parentname.empty() ? flags_ui : (FLAG_INVERT | flags_ui), (void *)&m_displaylist[curitem].get()); + m_displaylist[curitem].get().parentname.empty() ? 0 : FLAG_INVERT, (void *)&m_displaylist[curitem].get()); } - item_append(menu_item_type::SEPARATOR, flags_ui); - - // configure the custom rendering - customtop = 4.0f * ui().get_line_height() + 5.0f * ui().box_tb_border(); - custombottom = 5.0f * ui().get_line_height() + 4.0f * ui().box_tb_border(); + m_skip_main_items = 0; if (old_software != -1) { set_selected_index(old_software); - top_line = selected_index() - (ui_globals::visible_sw_lines / 2); + centre_selection(); } reselect_last::reset(); } -//------------------------------------------------- -// build a list of software -//------------------------------------------------- - -void menu_select_software::build_software_list() -{ - // add start empty item - m_swinfo.emplace_back(m_driver); - - machine_config config(m_driver, machine().options()); - - // iterate through all software lists - std::vector<std::size_t> orphans; - struct orphan_less - { - std::vector<ui_software_info> &swinfo; - bool operator()(std::string const &a, std::string const &b) const { return a < b; }; - bool operator()(std::string const &a, std::size_t b) const { return a < swinfo[b].parentname; }; - bool operator()(std::size_t a, std::string const &b) const { return swinfo[a].parentname < b; }; - bool operator()(std::size_t a, std::size_t b) const { return swinfo[a].parentname < swinfo[b].parentname; }; - }; - orphan_less const orphan_cmp{ m_swinfo }; - for (software_list_device &swlist : software_list_device_iterator(config.root_device())) - { - m_filter_data.add_list(swlist.list_name(), swlist.description()); - check_for_icons(swlist.list_name().c_str()); - orphans.clear(); - std::map<std::string, std::string> parentnames; - std::map<std::string, std::string>::const_iterator prevparent(parentnames.end()); - for (const software_info &swinfo : swlist.get_info()) - { - // check for previously-encountered clones - if (swinfo.parentname().empty()) - { - if (parentnames.emplace(swinfo.shortname(), swinfo.longname()).second) - { - auto const clones(std::equal_range(orphans.begin(), orphans.end(), swinfo.shortname(), orphan_cmp)); - for (auto it = clones.first; clones.second != it; ++it) - m_swinfo[*it].parentlongname = swinfo.longname(); - orphans.erase(clones.first, clones.second); - } - else - { - assert([] (auto const x) { return x.first == x.second; } (std::equal_range(orphans.begin(), orphans.end(), swinfo.shortname(), orphan_cmp))); - } - } - - const software_part &part = swinfo.parts().front(); - if (swlist.is_compatible(part) == SOFTWARE_IS_COMPATIBLE) - { - char const *instance_name(nullptr); - char const *type_name(nullptr); - for (device_image_interface &image : image_interface_iterator(config.root_device())) - { - char const *const interface = image.image_interface(); - if (interface && part.matches_interface(interface)) - { - instance_name = image.instance_name().c_str(); - type_name = image.image_type_name(); - break; - } - } - - if (instance_name && type_name) - { - // add to collection and try to resolve parent if applicable - auto const ins(m_swinfo.emplace(m_swinfo.end(), swinfo, part, m_driver, swlist.list_name(), instance_name, type_name)); - if (!swinfo.parentname().empty()) - { - if ((parentnames.end() == prevparent) || (swinfo.parentname() != prevparent->first)) - prevparent = parentnames.find(swinfo.parentname()); - - if (parentnames.end() != prevparent) - { - ins->parentlongname = prevparent->second; - } - else - { - orphans.emplace( - std::upper_bound(orphans.begin(), orphans.end(), swinfo.parentname(), orphan_cmp), - std::distance(m_swinfo.begin(), ins)); - } - } - - // populate filter choices - m_filter_data.add_region(ins->longname); - m_filter_data.add_publisher(ins->publisher); - m_filter_data.add_year(ins->year); - m_filter_data.add_device_type(ins->devicetype); - } - } - } - } - - std::string searchstr, curpath; - for (auto & elem : m_filter_data.list_names()) - { - path_iterator path(machine().options().media_path()); - while (path.next(curpath)) - { - searchstr.assign(curpath).append(PATH_SEPARATOR).append(elem).append(";"); - file_enumerator fpath(searchstr.c_str()); - - // iterate while we get new objects - osd::directory::entry const *dir; - while ((dir = fpath.next()) != nullptr) - { - std::string name; - if (dir->type == osd::directory::entry::entry_type::FILE) - name = core_filename_extract_base(dir->name, true); - else if (dir->type == osd::directory::entry::entry_type::DIR && strcmp(dir->name, ".") != 0) - name = dir->name; - else - continue; - - strmakelower(name); - for (auto & yelem : m_swinfo) - if (yelem.shortname == name && yelem.listname == elem) - { - yelem.available = true; - break; - } - } - } - } - - // sort array - std::stable_sort(m_swinfo.begin() + 1, m_swinfo.end(), compare_software); - m_filter_data.finalise(); -} - //------------------------------------------------- // handle select key event //------------------------------------------------- -void menu_select_software::inkey_select(const event *menu_event) +bool menu_select_software::inkey_select(const event *menu_event) { ui_software_info *ui_swinfo = (ui_software_info *)menu_event->itemref; driver_enumerator drivlist(machine().options(), *ui_swinfo->driver); @@ -474,9 +597,10 @@ void menu_select_software::inkey_select(const event *menu_event) // audit the system ROMs first to see if we're going to work media_auditor::summary const sysaudit = auditor.audit_media(AUDIT_VALIDATE_FAST); - if (sysaudit != media_auditor::CORRECT && sysaudit != media_auditor::BEST_AVAILABLE && sysaudit != media_auditor::NONE_NEEDED) + if (!audit_passed(sysaudit)) { - set_error(reset_options::REMEMBER_REF, make_audit_fail_text(media_auditor::NOTFOUND != sysaudit, auditor)); + set_error(reset_options::REMEMBER_REF, make_system_audit_fail_text(auditor, sysaudit)); + return true; } else if (ui_swinfo->startempty == 1) { @@ -485,89 +609,41 @@ void menu_select_software::inkey_select(const event *menu_event) reselect_last::reselect(true); launch_system(*ui_swinfo->driver, *ui_swinfo); } + return false; } else { - // first audit the software + // now audit the software software_list_device *swlist = software_list_device::find_by_name(*drivlist.config(), ui_swinfo->listname); const software_info *swinfo = swlist->find(ui_swinfo->shortname); - media_auditor::summary const swaudit = auditor.audit_software(*swlist, *swinfo, AUDIT_VALIDATE_FAST); - if (swaudit == media_auditor::CORRECT || swaudit == media_auditor::BEST_AVAILABLE || swaudit == media_auditor::NONE_NEEDED) + if (audit_passed(swaudit)) { if (!select_bios(*ui_swinfo, false) && !select_part(*swinfo, *ui_swinfo)) { reselect_last::reselect(true); launch_system(drivlist.driver(), *ui_swinfo); } + return false; } else { // otherwise, display an error - std::ostringstream str; - str << _("The selected software is missing one or more required files. Please select a different software item.\n\n"); - if (media_auditor::NOTFOUND != swaudit) - { - auditor.summarize(nullptr, &str); - str << '\n'; - } - str << _("Press any key to continue."), - set_error(reset_options::REMEMBER_POSITION, str.str()); + set_error(reset_options::REMEMBER_REF, make_software_audit_fail_text(auditor, swaudit)); + return true; } } } //------------------------------------------------- -// load custom filters info from file -//------------------------------------------------- - -void menu_select_software::load_sw_custom_filters() -{ - // attempt to open the output file - emu_file file(ui().options().ui_path(), OPEN_FLAG_READ); - if (file.open(util::string_format("custom_%s_filter.ini", m_driver.name)) == osd_file::error::NONE) - { - software_filter::ptr flt(software_filter::create(file, m_filter_data)); - if (flt) - m_filters.emplace(flt->get_type(), std::move(flt)); - file.close(); - } -} - -//------------------------------------------------- -// find approximate matches -//------------------------------------------------- - -void menu_select_software::find_matches() -{ - // ensure search list is populated - if (m_searchlist.empty()) - { - m_searchlist.reserve(m_swinfo.size()); - std::copy(m_swinfo.begin(), m_swinfo.end(), std::back_inserter(m_searchlist)); - } - - // update search - const std::u32string ucs_search(ustr_from_utf8(normalize_unicode(m_search, unicode_normalization_form::D, true))); - for (search_item &entry : m_searchlist) - entry.set_penalty(ucs_search); - - // sort according to edit distance - std::stable_sort( - m_searchlist.begin(), - m_searchlist.end(), - [] (search_item const &lhs, search_item const &rhs) { return lhs.penalty < rhs.penalty; }); -} - -//------------------------------------------------- // draw left box //------------------------------------------------- -float menu_select_software::draw_left_panel(float x1, float y1, float x2, float y2) +void menu_select_software::draw_left_panel(u32 flags) { - return menu_select_launch::draw_left_panel<software_filter>(m_filter_type, m_filters, x1, y1, x2, y2); + return menu_select_launch::draw_left_panel<software_filter>(flags, m_data->filter_type(), m_data->filters()); } @@ -583,17 +659,17 @@ render_texture *menu_select_software::get_icon_texture(int linenum, void *select if (swinfo->startempty) return nullptr; - icon_cache::iterator icon(m_icons.find(swinfo)); - if ((m_icons.end() == icon) || !icon->second.texture) + icon_cache::iterator icon(m_data->icons().find(swinfo)); + if ((m_data->icons().end() == icon) || !icon->second.texture) { std::map<std::string, std::string>::iterator paths(m_icon_paths.find(swinfo->listname)); if (m_icon_paths.end() == paths) paths = m_icon_paths.emplace(swinfo->listname, make_icon_paths(swinfo->listname.c_str())).first; // allocate an entry or allocate a texture on forced redraw - if (m_icons.end() == icon) + if (m_data->icons().end() == icon) { - icon = m_icons.emplace(swinfo, texture_ptr(machine().render().texture_alloc(), machine().render())).first; + icon = m_data->icons().emplace(swinfo, texture_ptr(machine().render().texture_alloc(), machine().render())).first; } else { @@ -603,12 +679,12 @@ render_texture *menu_select_software::get_icon_texture(int linenum, void *select bitmap_argb32 tmp; emu_file snapfile(std::string(paths->second), OPEN_FLAG_READ); - if (snapfile.open(std::string(swinfo->shortname) + ".ico") == osd_file::error::NONE) + if (!snapfile.open(std::string(swinfo->shortname) + ".ico")) { render_load_ico_highest_detail(snapfile, tmp); snapfile.close(); } - if (!tmp.valid() && !swinfo->parentname.empty() && (snapfile.open(std::string(swinfo->parentname) + ".ico") == osd_file::error::NONE)) + if (!tmp.valid() && !swinfo->parentname.empty() && !snapfile.open(std::string(swinfo->parentname) + ".ico")) { render_load_ico_highest_detail(snapfile, tmp); snapfile.close(); @@ -625,69 +701,64 @@ render_texture *menu_select_software::get_icon_texture(int linenum, void *select // get selected software and/or driver //------------------------------------------------- -void menu_select_software::get_selection(ui_software_info const *&software, game_driver const *&driver) const +void menu_select_software::get_selection(ui_software_info const *&software, ui_system_info const *&system) const { software = reinterpret_cast<ui_software_info const *>(get_selection_ptr()); - driver = software ? software->driver : nullptr; + system = &m_system; +} + + +void menu_select_software::show_config_menu(int index) +{ + menu::stack_push<menu_machine_configure>(ui(), container(), m_system, nullptr); } void menu_select_software::make_topbox_text(std::string &line0, std::string &line1, std::string &line2) const { // determine the text for the header - int vis_item = !m_search.empty() ? m_available_items : (m_has_empty_start ? m_available_items - 1 : m_available_items); - line0 = string_format(_("%1$s %2$s ( %3$d / %4$d software packages )"), emulator_info::get_appname(), bare_build_version, vis_item, m_swinfo.size() - 1); - line1 = string_format(_("Driver: \"%1$s\" software list "), m_driver.type.fullname()); + int vis_item = !m_search.empty() ? m_available_items : (m_available_items - 1); + line0 = string_format(_("%1$s %2$s ( %3$d / %4$d software packages )"), emulator_info::get_appname(), bare_build_version, vis_item, m_data->swinfo().size() - 1); + line1 = string_format(_("%1$s - select software"), m_system.description); - filter_map::const_iterator const it(m_filters.find(m_filter_type)); - char const *const filter((m_filters.end() != it) ? it->second->filter_text() : nullptr); + software_filter const *const it(m_data->current_filter()); + char const *const filter(it ? it->filter_text() : nullptr); if (filter) - line2 = string_format(_("%1$s: %2$s - Search: %3$s_"), it->second->display_name(), filter, m_search); + line2 = string_format(_("%1$s: %2$s - Search: %3$s_"), it->display_name(), filter, m_search); else line2 = string_format(_("Search: %1$s_"), m_search); } -std::string menu_select_software::make_driver_description(game_driver const &driver) const +std::string menu_select_software::make_software_description(ui_software_info const &software, ui_system_info const *system) const { - // first line is game description - return string_format(_("%1$-.100s"), driver.type.fullname()); + // show list/item to make it less confusing when there are multiple lists mixed + return string_format(_("Software list/item: %1$s:%2$s"), software.listname, software.shortname); } -std::string menu_select_software::make_software_description(ui_software_info const &software) const +void menu_select_software::filter_selected(int index) { - // first line is long name - return string_format(_("%1$-.100s"), software.longname); -} + assert((software_filter::FIRST <= index) && (software_filter::LAST >= index)); - -void menu_select_software::filter_selected() -{ - if ((software_filter::FIRST <= m_filter_highlight) && (software_filter::LAST >= m_filter_highlight)) - { - filter_map::const_iterator it(m_filters.find(software_filter::type(m_filter_highlight))); - if (m_filters.end() == it) - it = m_filters.emplace(software_filter::type(m_filter_highlight), software_filter::create(software_filter::type(m_filter_highlight), m_filter_data)).first; - it->second->show_ui( - ui(), - container(), - [this] (software_filter &filter) + m_data->get_filter(software_filter::type(index)).show_ui( + ui(), + container(), + [this] (software_filter &filter) + { + software_filter::type const new_type(filter.get_type()); + if (software_filter::CUSTOM == new_type) { - software_filter::type const new_type(filter.get_type()); - if (software_filter::CUSTOM == new_type) + emu_file file(ui().options().ui_path(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS); + if (!file.open(util::string_format("custom_%s_filter.ini", m_system.driver->name))) { - emu_file file(ui().options().ui_path(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS); - if (file.open(util::string_format("custom_%s_filter.ini", m_driver.name)) == osd_file::error::NONE) - { - filter.save_ini(file, 0); - file.close(); - } + filter.save_ini(file, 0); + file.close(); } - m_filter_type = new_type; - reset(reset_options::SELECT_FIRST); - }); - } + } + m_data->set_filter_type(new_type); + reset(reset_options::REMEMBER_REF); + }); } } // namespace ui |