diff options
author | 2021-10-09 12:16:17 +1100 | |
---|---|---|
committer | 2021-10-09 12:16:17 +1100 | |
commit | 38082ccbee749d650ccea886ae376a5d1dec337c (patch) | |
tree | 9ba9a900ba826bda58832834278025ced17f42f5 /src | |
parent | 34b3bf701098082feb9077db49987507962c1578 (diff) |
Overdue internal UI enhancements (#8674)
* frontend: Added support for message context to localisations.
* frontend: Added string_view versions of the message lookup functions.
* frontend: Added a few more folder options to the internal UI.
* emu/softlist.cpp: Use more appropriate containers.
* Switched to Python 3 by default - this will become a requirement.
* Updated msgfmt.py for message context support.
* frontend: Show all software item info in the internal UI.
* frontend: Search alternate titles in software selection menu.
* 3rdparty/utf8proc: Updated to v2.6.1 (has several fixes).
* frontend: Added software filters for common info fields.
* frontend: Allow UI manager to hold onto persistent session data.
* frontend: Cache software lists for eight machines.
* frontend: Added support for loading localised system names.
* frontend: Add UI for selecting localised system names.
Diffstat (limited to 'src')
34 files changed, 1715 insertions, 1029 deletions
diff --git a/src/emu/emuopts.cpp b/src/emu/emuopts.cpp index 44f775dedfa..16ec1d29fb2 100644 --- a/src/emu/emuopts.cpp +++ b/src/emu/emuopts.cpp @@ -36,7 +36,7 @@ const options_entry emu_options::s_option_entries[] = // search path options { nullptr, nullptr, OPTION_HEADER, "CORE SEARCH PATH OPTIONS" }, - { OPTION_HOMEPATH, ".", OPTION_STRING, "path to base folder for plugin data (read/write)" }, + { OPTION_PLUGINDATAPATH, ".", OPTION_STRING, "path to base folder for plugin data (read/write)" }, { OPTION_MEDIAPATH ";rp;biospath;bp", "roms", OPTION_STRING, "path to ROM sets and hard disk images" }, { OPTION_HASHPATH ";hash_directory;hash", "hash", OPTION_STRING, "path to software definition files" }, { OPTION_SAMPLEPATH ";sp", "samples", OPTION_STRING, "path to audio sample sets" }, @@ -951,7 +951,7 @@ emu_options::software_options emu_options::evaluate_initial_softlist_options(con // ... // <sharedfeat name = "ctrl1_default" value = "paddle" /> // </software> - for (const feature_list_item &fi : swinfo->shared_info()) + for (const software_info_item &fi : swinfo->shared_features()) { const std::string default_suffix = "_default"; if (fi.name().size() > default_suffix.size() diff --git a/src/emu/emuopts.h b/src/emu/emuopts.h index 3df399ce8d6..59f651d946f 100644 --- a/src/emu/emuopts.h +++ b/src/emu/emuopts.h @@ -25,7 +25,7 @@ #define OPTION_WRITECONFIG "writeconfig" // core search path options -#define OPTION_HOMEPATH "homepath" +#define OPTION_PLUGINDATAPATH "homepath" #define OPTION_MEDIAPATH "rompath" #define OPTION_HASHPATH "hashpath" #define OPTION_SAMPLEPATH "samplepath" @@ -311,7 +311,7 @@ public: bool write_config() const { return bool_value(OPTION_WRITECONFIG); } // core search path options - const char *home_path() const { return value(OPTION_HOMEPATH); } + const char *plugin_data_path() const { return value(OPTION_PLUGINDATAPATH); } const char *media_path() const { return value(OPTION_MEDIAPATH); } const char *hash_path() const { return value(OPTION_HASHPATH); } const char *sample_path() const { return value(OPTION_SAMPLEPATH); } diff --git a/src/emu/softlist.cpp b/src/emu/softlist.cpp index 8ee8d0d8500..fe9d862ef1f 100644 --- a/src/emu/softlist.cpp +++ b/src/emu/softlist.cpp @@ -32,10 +32,10 @@ static std::regex s_potential_softlist_regex("\\w+(\\:\\w+)*"); //************************************************************************** //------------------------------------------------- -// feature_list_item - constructor +// software_info_item - constructor //------------------------------------------------- -feature_list_item::feature_list_item(const std::string &name, const std::string &value) : +software_info_item::software_info_item(const std::string &name, const std::string &value) : m_name(name), m_value(value) { @@ -43,10 +43,10 @@ feature_list_item::feature_list_item(const std::string &name, const std::string //------------------------------------------------- -// feature_list_item - constructor +// software_info_item - constructor //------------------------------------------------- -feature_list_item::feature_list_item(std::string &&name, std::string &&value) : +software_info_item::software_info_item(std::string &&name, std::string &&value) : m_name(std::move(name)), m_value(std::move(value)) { @@ -77,14 +77,8 @@ software_part::software_part(software_info &info, std::string &&name, std::strin const char *software_part::feature(std::string_view feature_name) const noexcept { // scan the feature list for an entry matching feature_name and return the value - auto iter = std::find_if( - m_featurelist.begin(), - m_featurelist.end(), - [&feature_name] (const feature_list_item &feature) { return feature.name() == feature_name; }); - - return iter != m_featurelist.end() - ? iter->value().c_str() - : nullptr; + auto const iter = m_features.find(feature_name); + return (iter != m_features.end()) ? iter->value().c_str() : nullptr; } @@ -192,7 +186,7 @@ class softlist_parser public: // construction (== execution) softlist_parser( - util::random_read &file, + util::read_stream &file, std::string_view filename, std::string &listname, std::string &description, @@ -261,7 +255,7 @@ private: //------------------------------------------------- softlist_parser::softlist_parser( - util::random_read &file, + util::read_stream &file, std::string_view filename, std::string &listname, std::string &description, @@ -290,7 +284,6 @@ softlist_parser::softlist_parser( XML_SetCharacterDataHandler(m_parser, &softlist_parser::data_handler); // parse the file contents - file.seek(0, SEEK_SET); char buffer[1024]; for (bool done = false; !done; ) { @@ -582,7 +575,7 @@ void softlist_parser::parse_main_start(const char *tagname, const char **attribu void softlist_parser::parse_main_end(const char *tagname) { if (strcmp(tagname, "notes") == 0) - m_notes = m_data_accum; + m_notes = std::move(m_data_accum); } @@ -622,7 +615,7 @@ void softlist_parser::parse_soft_start(const char *tagname, const char **attribu std::string infoname, infovalue; if (parse_name_and_value(attributes, infoname, infovalue)) - m_current_info->m_other_info.emplace_back(std::move(infoname), std::move(infovalue)); + m_current_info->m_info.emplace_back(std::move(infoname), std::move(infovalue)); else parse_error("Incomplete other_info definition"); } @@ -633,9 +626,14 @@ void softlist_parser::parse_soft_start(const char *tagname, const char **attribu std::string featname, featvalue; if (parse_name_and_value(attributes, featname, featvalue)) - m_current_info->m_shared_info.emplace_back(std::move(featname), std::move(featvalue)); + { + if (!m_current_info->m_shared_features.emplace(std::move(featname), std::move(featvalue)).second) + parse_error("Duplicate sharedfeat name"); + } else + { parse_error("Incomplete sharedfeat definition"); + } } // <part name='' interface=''> @@ -731,9 +729,14 @@ void softlist_parser::parse_part_start(const char *tagname, const char **attribu std::string featname, featvalue; if (parse_name_and_value(attributes, featname, featvalue)) - m_current_part->m_featurelist.emplace_back(std::move(featname), std::move(featvalue)); + { + if (!m_current_part->m_features.emplace(std::move(featname), std::move(featvalue)).second) + parse_error("Duplicate feature name"); + } else + { parse_error("Incomplete feature definition"); + } } // <dipswitch> @@ -871,19 +874,19 @@ void softlist_parser::parse_soft_end(const char *tagname) // <description> if (strcmp(tagname, "description") == 0) - m_current_info->m_longname = m_data_accum; + m_current_info->m_longname = std::move(m_data_accum); // <year> else if (strcmp(tagname, "year") == 0) - m_current_info->m_year = m_data_accum; + m_current_info->m_year = std::move(m_data_accum); // <publisher> else if (strcmp(tagname, "publisher") == 0) - m_current_info->m_publisher = m_data_accum; + m_current_info->m_publisher = std::move(m_data_accum); // <notes> else if (strcmp(tagname, "notes") == 0) - m_current_info->m_notes = m_data_accum; + m_current_info->m_notes = std::move(m_data_accum); // </part> else if (strcmp(tagname, "part") == 0) @@ -897,11 +900,10 @@ void softlist_parser::parse_soft_end(const char *tagname) if (!m_current_part->m_romdata.empty()) add_rom_entry("", "", 0, 0, ROMENTRYTYPE_END); - // get the info; if present, copy shared data (we assume name/value strings live - // in the string pool and don't need to be reallocated) + // get the info; if present, copy shared data if (m_current_info != nullptr) - for (const feature_list_item &item : m_current_info->shared_info()) - m_current_part->m_featurelist.emplace_back(item.name(), item.value()); + for (const software_info_item &item : m_current_info->shared_features()) + m_current_part->m_features.emplace(item.name(), item.value()); } } @@ -909,7 +911,7 @@ void softlist_parser::parse_soft_end(const char *tagname) void parse_software_list( - util::random_read &file, + util::read_stream &file, std::string_view filename, std::string &listname, std::string &description, diff --git a/src/emu/softlist.h b/src/emu/softlist.h index 2c7a4e545dd..e8587dd4505 100644 --- a/src/emu/softlist.h +++ b/src/emu/softlist.h @@ -19,6 +19,9 @@ #include "corefile.h" #include <list> +#include <set> +#include <string> +#include <string_view> //************************************************************************** @@ -45,21 +48,31 @@ enum class software_support //************************************************************************** // an item in a list of name/value pairs -class feature_list_item +class software_info_item { public: // construction/destruction - feature_list_item(std::string &&name, std::string &&value); - feature_list_item(const std::string &name, const std::string &value); - feature_list_item(feature_list_item const &) = delete; - feature_list_item(feature_list_item &&) = delete; - feature_list_item& operator=(feature_list_item const &) = delete; - feature_list_item& operator=(feature_list_item &&) = delete; + software_info_item(std::string &&name, std::string &&value); + software_info_item(const std::string &name, const std::string &value); + software_info_item(software_info_item const &) = default; + software_info_item(software_info_item &&) = default; + software_info_item& operator=(software_info_item const &) = default; + software_info_item& operator=(software_info_item &&) = default; // getters const std::string &name() const noexcept { return m_name; } const std::string &value() const noexcept { return m_value; } + // collection + struct compare + { + using is_transparent = void; + bool operator()(const software_info_item &a, const software_info_item &b) const noexcept { return a.m_name < b.m_name; } + bool operator()(const software_info_item &a, const std::string_view &b) const noexcept { return a.m_name < b; } + bool operator()(const std::string_view &a, const software_info_item &b) const noexcept { return a < b.m_name; } + }; + using set = std::set<software_info_item, compare>; + private: // internal state std::string m_name; @@ -84,7 +97,7 @@ public: software_info &info() const noexcept { return m_info; } const std::string &name() const noexcept { return m_name; } const std::string &interface() const noexcept { return m_interface; } - const std::list<feature_list_item> &featurelist() const noexcept { return m_featurelist; } + const software_info_item::set &features() const noexcept { return m_features; } const std::vector<rom_entry> &romdata() const noexcept { return m_romdata; } // helpers @@ -93,11 +106,11 @@ public: private: // internal state - software_info & m_info; - std::string m_name; - std::string m_interface; - std::list<feature_list_item> m_featurelist; - std::vector<rom_entry> m_romdata; + software_info & m_info; + std::string m_name; + std::string m_interface; + software_info_item::set m_features; + std::vector<rom_entry> m_romdata; }; @@ -121,8 +134,8 @@ public: const std::string &year() const { return m_year; } const std::string &publisher() const { return m_publisher; } const std::string ¬es() const { return m_notes; } - const std::list<feature_list_item> &other_info() const { return m_other_info; } - const std::list<feature_list_item> &shared_info() const { return m_shared_info; } + const std::list<software_info_item> &info() const { return m_info; } + const software_info_item::set &shared_features() const { return m_shared_features; } software_support supported() const { return m_supported; } const std::list<software_part> &parts() const { return m_partdata; } @@ -132,16 +145,16 @@ public: private: // internal state - software_support m_supported; - std::string m_shortname; - std::string m_longname; - std::string m_parentname; - std::string m_year; // Copyright year on title screen, actual release dates can be tracked in external resources - std::string m_publisher; - std::string m_notes; - std::list<feature_list_item> m_other_info; // Here we store info like developer, serial #, etc. which belong to the software entry as a whole - std::list<feature_list_item> m_shared_info; // Here we store info like TV standard compatibility, or add-on requirements, etc. which get inherited - // by each part of this software entry (after loading these are stored in partdata->featurelist) + software_support m_supported; + std::string m_shortname; + std::string m_longname; + std::string m_parentname; + std::string m_year; // Copyright year on title screen, actual release dates can be tracked in external resources + std::string m_publisher; + std::string m_notes; + std::list<software_info_item> m_info; // Here we store info like developer, serial #, etc. which belong to the software entry as a whole + software_info_item::set m_shared_features; // Here we store info like TV standard compatibility, or add-on requirements, etc. which get inherited + // by each part of this software entry (after loading these are stored in partdata->features) std::list<software_part> m_partdata; }; @@ -150,7 +163,7 @@ private: // parses a software list void parse_software_list( - util::random_read &file, + util::read_stream &file, std::string_view filename, std::string &listname, std::string &description, diff --git a/src/frontend/mame/clifront.cpp b/src/frontend/mame/clifront.cpp index bc78cb1112c..191ae747a4c 100644 --- a/src/frontend/mame/clifront.cpp +++ b/src/frontend/mame/clifront.cpp @@ -1136,9 +1136,12 @@ void cli_frontend::output_single_softlist(std::ostream &out, software_list_devic if (!swinfo.notes().empty()) util::stream_format(out, "\t\t\t<notes>%s</notes>\n", util::xml::normalize_string(swinfo.notes().c_str())); - for (const feature_list_item &flist : swinfo.other_info()) + for (const auto &flist : swinfo.info()) util::stream_format(out, "\t\t\t<info name=\"%s\" value=\"%s\"/>\n", flist.name(), util::xml::normalize_string(flist.value().c_str())); + for (const auto &flist : swinfo.shared_features()) + util::stream_format(out, "\t\t\t<sharedfeat name=\"%s\" value=\"%s\"/>\n", flist.name(), util::xml::normalize_string(flist.value().c_str())); + for (const software_part &part : swinfo.parts()) { util::stream_format(out, "\t\t\t<part name=\"%s\"", util::xml::normalize_string(part.name().c_str())); @@ -1147,7 +1150,7 @@ void cli_frontend::output_single_softlist(std::ostream &out, software_list_devic out << ">\n"; - for (const feature_list_item &flist : part.featurelist()) + for (const auto &flist : part.features()) util::stream_format(out, "\t\t\t\t<feature name=\"%s\" value=\"%s\" />\n", flist.name(), util::xml::normalize_string(flist.value().c_str())); // TODO: display ROM region information diff --git a/src/frontend/mame/language.cpp b/src/frontend/mame/language.cpp index eae75b3fd6f..4ecdc2aba0c 100644 --- a/src/frontend/mame/language.cpp +++ b/src/frontend/mame/language.cpp @@ -9,6 +9,8 @@ ***************************************************************************/ #include "emu.h" +#include "language.h" + #include "emuopts.h" #include "corestr.h" @@ -17,46 +19,20 @@ #include <memory> #include <new> #include <unordered_map> +#include <utility> namespace { -constexpr uint32_t MO_MAGIC = 0x950412de; -constexpr uint32_t MO_MAGIC_REVERSED = 0xde120495; - -struct cstr_hash -{ - size_t operator()(char const *s) const noexcept - { - // Bernstein string hash - size_t result(5381); - while (*s) - result = ((result << 5) + result) + u8(*s++); - return result; - } -}; - -struct cstr_compare -{ - size_t operator()(char const *x, char const *y) const noexcept - { - return !std::strcmp(x, y); - } -}; +constexpr u32 MO_MAGIC = 0x950412de; +constexpr u32 MO_MAGIC_REVERSED = 0xde120495; std::unique_ptr<u32 []> f_translation_data; -std::unordered_map<char const *, char const *, cstr_hash, cstr_compare> f_translation_map; +std::unordered_map<std::string_view, std::pair<char const *, u32> > f_translation_map; } // anonymous namespace -char const *lang_translate(char const *word) -{ - auto const found = f_translation_map.find(word); - return (f_translation_map.end() != found) ? found->second : word; -} - - void load_translation(emu_options &m_options) { f_translation_data.reset(); @@ -84,7 +60,7 @@ void load_translation(emu_options &m_options) return; } - f_translation_data.reset(new (std::nothrow) uint32_t [(size + 3) / 4]); + f_translation_data.reset(new (std::nothrow) u32 [(size + 3) / 4]); if (!f_translation_data) { file.close(); @@ -169,12 +145,81 @@ void load_translation(emu_options &m_options) continue; } - char const *const original = &data[original_offset]; - char const *const translation = &data[translation_offset]; - auto const ins = f_translation_map.emplace(original, translation); + std::string_view const original(&data[original_offset], original_length); + char const *const translation(&data[translation_offset]); + auto const ins = f_translation_map.emplace(original, std::make_pair(translation, translation_length)); if (!ins.second) - osd_printf_warning("Loading translation file %s: translation %u '%s'='%s' conflicts with previous translation '%s'='%s'\n", name, i, original, translation, ins.first->first, ins.first->second); + { + osd_printf_warning( + "Loading translation file %s: translation %u '%s'='%s' conflicts with previous translation '%s'='%s'\n", + name, + i, + original, + translation, + ins.first->first, + ins.first->second.first); + } } osd_printf_verbose("Loaded %u translations from file %s\n", f_translation_map.size(), name); } + + +char const *lang_translate(char const *message) +{ + auto const found = f_translation_map.find(message); + if (f_translation_map.end() != found) + return found->second.first; + return message; +} + + +std::string_view lang_translate(std::string_view message) +{ + auto const found = f_translation_map.find(message); + if (f_translation_map.end() != found) + return std::string_view(found->second.first, found->second.second); + return message; +} + + +char const *lang_translate(char const *context, char const *message) +{ + if (!f_translation_map.empty()) + { + auto const ctxlen(std::strlen(context)); + auto const msglen(std::strlen(message)); + std::string key; + key.reserve(ctxlen + 1 + msglen); + key.append(context, ctxlen); + key.append(1, '\004'); + key.append(message, msglen); + auto const found = f_translation_map.find(key); + if (f_translation_map.end() != found) + return found->second.first; + } + return message; +} + + +std::string_view lang_translate(char const *context, std::string_view message) +{ + return lang_translate(std::string_view(context), message); +} + + +std::string_view lang_translate(std::string_view context, std::string_view message) +{ + if (!f_translation_map.empty()) + { + std::string key; + key.reserve(context.length() + 1 + message.length()); + key.append(context); + key.append(1, '\004'); + key.append(message); + auto const found = f_translation_map.find(key); + if (f_translation_map.end() != found) + return std::string_view(found->second.first, found->second.second); + } + return message; +} diff --git a/src/frontend/mame/language.h b/src/frontend/mame/language.h index d688b2ba93a..93e3c491382 100644 --- a/src/frontend/mame/language.h +++ b/src/frontend/mame/language.h @@ -12,16 +12,25 @@ #pragma once +#include <string_view> + + //************************************************************************** // LOCALIZATION SUPPORT //************************************************************************** -#define _(param) lang_translate(param) -// Fake one to make possible using it in static text definitions, on those -// lang_translate must be called afterwards -#define __(param) param +#define _(...) lang_translate(__VA_ARGS__) + +#define N_(msg) (msg) +#define N_p(ctx, msg) (msg) void load_translation(emu_options &option); -const char *lang_translate(const char *word); + +char const *lang_translate(char const *message); +std::string_view lang_translate(std::string_view message); + +char const *lang_translate(char const *context, char const *message); +std::string_view lang_translate(char const *context, std::string_view message); +std::string_view lang_translate(std::string_view context, std::string_view message); #endif // MAME_FRONTEND_MAME_LANGUAGE_H diff --git a/src/frontend/mame/luaengine.cpp b/src/frontend/mame/luaengine.cpp index 54fdd78b01c..78521783541 100644 --- a/src/frontend/mame/luaengine.cpp +++ b/src/frontend/mame/luaengine.cpp @@ -761,7 +761,9 @@ void lua_engine::initialize() engine->machine().scheduler().timer_set(attotime::from_double(lua_tonumber(L, 1)), timer_expired_delegate(FUNC(lua_engine::resume), engine), ref, nullptr); return lua_yield(L, 0); }); - emu["lang_translate"] = &lang_translate; + emu["lang_translate"] = sol::overload( + static_cast<char const *(*)(char const *)>(&lang_translate), + static_cast<char const *(*)(char const *, char const *)>(&lang_translate)); emu["pid"] = &osd_getpid; emu["subst_env"] = [] (const std::string &str) diff --git a/src/frontend/mame/ui/auditmenu.cpp b/src/frontend/mame/ui/auditmenu.cpp index 66c1aab0597..6da405b3698 100644 --- a/src/frontend/mame/ui/auditmenu.cpp +++ b/src/frontend/mame/ui/auditmenu.cpp @@ -31,54 +31,6 @@ 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') || x->parent[1]; - int cx = -1; - if (clonex) - { - cx = driver_list::find(x->parent); - if ((0 > cx) || (driver_list::driver(cx).flags & machine_flags::IS_BIOS_ROOT)) - clonex = false; - } - - bool cloney = (y->parent[0] != '0') || y->parent[1]; - int cy = -1; - if (cloney) - { - cy = driver_list::find(y->parent); - if ((0 > cy) || (driver_list::driver(cy).flags & machine_flags::IS_BIOS_ROOT)) - cloney = false; - } - - if (!clonex && !cloney) - { - return (core_stricmp(x->type.fullname(), y->type.fullname()) < 0); - } - else if (clonex && cloney) - { - if (!core_stricmp(x->parent, y->parent)) - return (core_stricmp(x->type.fullname(), y->type.fullname()) < 0); - else - return (core_stricmp(driver_list::driver(cx).type.fullname(), driver_list::driver(cy).type.fullname()) < 0); - } - else if (!clonex && cloney) - { - if (!core_stricmp(x->name, y->parent)) - return true; - else - return (core_stricmp(x->type.fullname(), driver_list::driver(cy).type.fullname()) < 0); - } - else - { - if (!core_stricmp(x->parent, y->name)) - return false; - else - return (core_stricmp(driver_list::driver(cx).type.fullname(), y->type.fullname()) < 0); - } -} - - 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() diff --git a/src/frontend/mame/ui/auditmenu.h b/src/frontend/mame/ui/auditmenu.h index 435bf52cd34..33b0c54863a 100644 --- a/src/frontend/mame/ui/auditmenu.h +++ b/src/frontend/mame/ui/auditmenu.h @@ -53,8 +53,6 @@ private: phase m_phase; }; -bool sorted_game_list(const game_driver *x, const game_driver *y); - } // namespace ui #endif // MAME_FRONTEND_UI_AUDITMENU_H diff --git a/src/frontend/mame/ui/custui.cpp b/src/frontend/mame/ui/custui.cpp index c748bee1e7c..9b06465f9f8 100644 --- a/src/frontend/mame/ui/custui.cpp +++ b/src/frontend/mame/ui/custui.cpp @@ -30,10 +30,10 @@ namespace ui { const char *const menu_custom_ui::HIDE_STATUS[] = { - __("Show All"), - __("Hide Filters"), - __("Hide Info/Image"), - __("Hide Both") }; + N_("Show All"), + N_("Hide Filters"), + N_("Hide Info/Image"), + N_("Hide Both") }; //------------------------------------------------- // ctor @@ -43,30 +43,10 @@ menu_custom_ui::menu_custom_ui(mame_ui_manager &mui, render_container &container : menu(mui, container) , m_handler(std::move(handler)) , m_currlang(0) + , m_currsysnames(0) { - // load languages - file_enumerator path(mui.machine().options().language_path()); - const char *const lang = mui.machine().options().language(); - const osd::directory::entry *dirent; - std::string name; - while ((dirent = path.next())) - { - if (dirent->type == osd::directory::entry::entry_type::DIR && strcmp(dirent->name, ".") != 0 && strcmp(dirent->name, "..") != 0) - { - name = dirent->name; - auto i = strreplace(name, "_", " ("); - if (i > 0) - name.append(")"); - m_lang.emplace_back(std::move(name)); - } - } - std::sort( - m_lang.begin(), - m_lang.end(), - [] (const std::string &x, const std::string &y) { return 0 > core_stricmp(x.c_str(), y.c_str()); }); - const auto found = std::lower_bound(m_lang.begin(), m_lang.end(), lang, [] (std::string const &x, const char *y) { return 0 > core_stricmp(x.c_str(), y); }); - if ((m_lang.end() != found) && !core_stricmp(found->c_str(), lang)) - m_currlang = std::distance(m_lang.begin(), found); + find_languages(); + find_sysnames(); } //------------------------------------------------- @@ -76,11 +56,12 @@ menu_custom_ui::menu_custom_ui(mame_ui_manager &mui, render_container &container menu_custom_ui::~menu_custom_ui() { ui().options().set_value(OPTION_HIDE_PANELS, ui_globals::panels_status, OPTION_PRIORITY_CMDLINE); - if (!m_lang.empty()) - { - machine().options().set_value(OPTION_LANGUAGE, m_lang[m_currlang], OPTION_PRIORITY_CMDLINE); - load_translation(machine().options()); - } + + machine().options().set_value(OPTION_LANGUAGE, m_currlang ? m_languages[m_currlang] : "", OPTION_PRIORITY_CMDLINE); + load_translation(machine().options()); + + ui().options().set_value(OPTION_SYSTEM_NAMES, m_currsysnames ? m_sysnames[m_currsysnames] : "", OPTION_PRIORITY_CMDLINE); + ui_globals::reset = true; if (m_handler) @@ -96,7 +77,7 @@ void menu_custom_ui::handle() bool changed = false; // process the menu - const event *menu_event = process(0); + const event *menu_event = process(PROCESS_LR_REPEAT); if (menu_event != nullptr && menu_event->itemref != nullptr) { @@ -140,13 +121,16 @@ void menu_custom_ui::handle() if (menu_event->iptkey == IPT_UI_LEFT || menu_event->iptkey == IPT_UI_RIGHT) { changed = true; - (menu_event->iptkey == IPT_UI_RIGHT) ? m_currlang++ : m_currlang--; + if (menu_event->iptkey == IPT_UI_LEFT) + m_currlang = (m_currlang ? m_currlang : m_languages.size())- 1; + else if (++m_currlang >= m_languages.size()) + m_currlang = 0; } else if (menu_event->iptkey == IPT_UI_SELECT) { // copying list of language names - expensive menu::stack_push<menu_selector>( - ui(), container(), std::vector<std::string>(m_lang), m_currlang, + ui(), container(), std::vector<std::string>(m_languages), m_currlang, [this] (int selection) { m_currlang = selection; @@ -154,6 +138,27 @@ void menu_custom_ui::handle() }); } break; + case SYSNAMES_MENU: + if (menu_event->iptkey == IPT_UI_LEFT || menu_event->iptkey == IPT_UI_RIGHT) + { + changed = true; + if (menu_event->iptkey == IPT_UI_LEFT) + m_currsysnames = (m_currsysnames ? m_currsysnames : m_sysnames.size())- 1; + else if (++m_currsysnames >= m_sysnames.size()) + m_currsysnames = 0; + } + else if (menu_event->iptkey == IPT_UI_SELECT) + { + // copying list of file names - expensive + menu::stack_push<menu_selector>( + ui(), container(), std::vector<std::string>(m_sysnames), m_currsysnames, + [this] (int selection) + { + m_currsysnames = selection; + reset(reset_options::REMEMBER_REF); + }); + } + break; } } @@ -171,11 +176,11 @@ void menu_custom_ui::populate(float &customtop, float &custombottom) item_append(_("Fonts"), 0, (void *)(uintptr_t)FONT_MENU); item_append(_("Colors"), 0, (void *)(uintptr_t)COLORS_MENU); - if (!m_lang.empty()) - { - arrow_flags = get_arrow_flags<std::uint16_t>(0, m_lang.size() - 1, m_currlang); - item_append(_("Language"), m_lang[m_currlang], arrow_flags, (void *)(uintptr_t)LANGUAGE_MENU); - } + arrow_flags = get_arrow_flags<std::size_t>(0, m_languages.size() - 1, m_currlang); + item_append(_("Language"), m_languages[m_currlang], arrow_flags, (void *)(uintptr_t)LANGUAGE_MENU); + + arrow_flags = get_arrow_flags<std::size_t>(0, m_sysnames.size() - 1, m_currsysnames); + item_append(_("System Names"), m_sysnames[m_currsysnames], arrow_flags, (void *)(uintptr_t)SYSNAMES_MENU); arrow_flags = get_arrow_flags<uint16_t>(0, HIDE_BOTH, ui_globals::panels_status); item_append(_("Show side panels"), _(HIDE_STATUS[ui_globals::panels_status]), arrow_flags, (void *)(uintptr_t)HIDE_MENU); @@ -190,7 +195,7 @@ void menu_custom_ui::populate(float &customtop, float &custombottom) void menu_custom_ui::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2) { - char const *const text[] = { _("Custom UI Settings") }; + char const *const text[] = { _("UI Customization Settings") }; draw_text_box( std::begin(text), std::end(text), origx1, origx2, origy1 - top, origy1 - ui().box_tb_border(), @@ -199,6 +204,94 @@ void menu_custom_ui::custom_render(void *selectedref, float top, float bottom, f } //------------------------------------------------- +// find UI translation files +//------------------------------------------------- + +void menu_custom_ui::find_languages() +{ + m_languages.emplace_back(_("[built-in]")); + + file_enumerator path(machine().options().language_path()); + osd::directory::entry const *dirent; + std::string name; + while ((dirent = path.next())) + { + if (dirent->type == osd::directory::entry::entry_type::DIR && strcmp(dirent->name, ".") != 0 && strcmp(dirent->name, "..") != 0) + { + name = dirent->name; + auto i = strreplace(name, "_", " ("); + if (i > 0) + name.append(")"); + m_languages.emplace_back(std::move(name)); + } + } + std::sort( + std::next(m_languages.begin()), + m_languages.end(), + [] (std::string const &x, std::string const &y) { return 0 > core_stricmp(x.c_str(), y.c_str()); }); + + char const *const lang = machine().options().language(); + if (*lang) + { + auto const found = std::lower_bound( + std::next(m_languages.begin()), + m_languages.end(), + lang, + [] (std::string const &x, char const *y) { return 0 > core_stricmp(x.c_str(), y); }); + if ((m_languages.end() != found) && !core_stricmp(found->c_str(), lang)) + m_currlang = std::distance(m_languages.begin(), found); + } + else + { + m_currlang = 0; + } +} + +//------------------------------------------------- +// find translated system names +//------------------------------------------------- + +void menu_custom_ui::find_sysnames() +{ + m_sysnames.emplace_back(_("[built-in]")); + + path_iterator search(ui().options().history_path()); + std::string path; + while (search.next(path)) + { + file_enumerator dir(path); + osd::directory::entry const *dirent; + while ((dirent = dir.next())) + { + if (dirent->type == osd::directory::entry::entry_type::FILE && core_filename_ends_with(dirent->name, ".lst")) + m_sysnames.emplace_back(dirent->name); + } + } + std::sort( + m_sysnames.begin(), + m_sysnames.end(), + [] (std::string const &x, std::string const &y) { return 0 > core_stricmp(x.c_str(), y.c_str()); }); + + char const *const names = ui().options().system_names(); + if (*names) + { + auto const found = std::lower_bound( + std::next(m_sysnames.begin()), + m_sysnames.end(), + names, + [] (std::string const &x, char const *y) { return 0 > core_stricmp(x.c_str(), y); }); + m_currsysnames = std::distance(m_sysnames.begin(), found); + if ((m_sysnames.end() == found) || core_stricmp(found->c_str(), names)) + m_sysnames.emplace(found, names); + } + else + { + m_currsysnames = 0; + } +} + + +//------------------------------------------------- // ctor //------------------------------------------------- @@ -781,34 +874,34 @@ void menu_rgb_ui::populate(float &customtop, float &custombottom) if (m_lock_ref != RGB_ALPHA) { arrow_flags = get_arrow_flags<uint8_t>(0, 255, m_color->a()); - item_append(_("Alpha"), string_format("%3u", m_color->a()), arrow_flags, (void *)(uintptr_t)RGB_ALPHA); + item_append(_("color-channel", "Alpha"), string_format("%3u", m_color->a()), arrow_flags, (void *)(uintptr_t)RGB_ALPHA); } else - item_append(_("Alpha"), s_text, 0, (void *)(uintptr_t)RGB_ALPHA); + item_append(_("color-channel", "Alpha"), s_text, 0, (void *)(uintptr_t)RGB_ALPHA); if (m_lock_ref != RGB_RED) { arrow_flags = get_arrow_flags<uint8_t>(0, 255, m_color->r()); - item_append(_("Red"), string_format("%3u", m_color->r()), arrow_flags, (void *)(uintptr_t)RGB_RED); + item_append(_("color-channel", "Red"), string_format("%3u", m_color->r()), arrow_flags, (void *)(uintptr_t)RGB_RED); } else - item_append(_("Red"), s_text, 0, (void *)(uintptr_t)RGB_RED); + item_append(_("color-channel", "Red"), s_text, 0, (void *)(uintptr_t)RGB_RED); if (m_lock_ref != RGB_GREEN) { arrow_flags = get_arrow_flags<uint8_t>(0, 255, m_color->g()); - item_append(_("Green"), string_format("%3u", m_color->g()), arrow_flags, (void *)(uintptr_t)RGB_GREEN); + item_append(_("color-channel", "Green"), string_format("%3u", m_color->g()), arrow_flags, (void *)(uintptr_t)RGB_GREEN); } else - item_append(_("Green"), s_text, 0, (void *)(uintptr_t)RGB_GREEN); + item_append(_("color-channel", "Green"), s_text, 0, (void *)(uintptr_t)RGB_GREEN); if (m_lock_ref != RGB_BLUE) { arrow_flags = get_arrow_flags<uint8_t>(0, 255, m_color->b()); - item_append(_("Blue"), string_format("%3u", m_color->b()), arrow_flags, (void *)(uintptr_t)RGB_BLUE); + item_append(_("color-channel", "Blue"), string_format("%3u", m_color->b()), arrow_flags, (void *)(uintptr_t)RGB_BLUE); } else - item_append(_("Blue"), s_text, 0, (void *)(uintptr_t)RGB_BLUE); + item_append(_("color-channel", "Blue"), s_text, 0, (void *)(uintptr_t)RGB_BLUE); item_append(menu_item_type::SEPARATOR); item_append(_("Choose from palette"), 0, (void *)(uintptr_t)PALETTE_CHOOSE); @@ -931,16 +1024,16 @@ void menu_rgb_ui::inkey_special(const event *menu_event) } std::pair<const char *, const char *> const menu_palette_sel::s_palette[] = { - { __("White"), "FFFFFFFF" }, - { __("Silver"), "FFC0C0C0" }, - { __("Gray"), "FF808080" }, - { __("Black"), "FF000000" }, - { __("Red"), "FFFF0000" }, - { __("Orange"), "FFFFA500" }, - { __("Yellow"), "FFFFFF00" }, - { __("Green"), "FF00FF00" }, - { __("Blue"), "FF0000FF" }, - { __("Violet"), "FF8F00FF" } + { N_p("color-preset", "White"), "FFFFFFFF" }, + { N_p("color-preset", "Silver"), "FFC0C0C0" }, + { N_p("color-preset", "Gray"), "FF808080" }, + { N_p("color-preset", "Black"), "FF000000" }, + { N_p("color-preset", "Red"), "FFFF0000" }, + { N_p("color-preset", "Orange"), "FFFFA500" }, + { N_p("color-preset", "Yellow"), "FFFFFF00" }, + { N_p("color-preset", "Green"), "FF00FF00" }, + { N_p("color-preset", "Blue"), "FF0000FF" }, + { N_p("color-preset", "Violet"), "FF8F00FF" } }; //------------------------------------------------- @@ -986,7 +1079,7 @@ void menu_palette_sel::handle() void menu_palette_sel::populate(float &customtop, float &custombottom) { for (unsigned x = 0; x < std::size(s_palette); ++x) - item_append(_(s_palette[x].first), s_palette[x].second, FLAG_COLOR_BOX, (void *)(uintptr_t)(x + 1)); + item_append(_("color-preset", s_palette[x].first), s_palette[x].second, FLAG_COLOR_BOX, (void *)(uintptr_t)(x + 1)); item_append(menu_item_type::SEPARATOR); } diff --git a/src/frontend/mame/ui/custui.h b/src/frontend/mame/ui/custui.h index 2da5cfd132b..33e8149ffdc 100644 --- a/src/frontend/mame/ui/custui.h +++ b/src/frontend/mame/ui/custui.h @@ -37,6 +37,7 @@ private: enum { LANGUAGE_MENU = 1, + SYSNAMES_MENU, FONT_MENU, COLORS_MENU, HIDE_MENU @@ -45,11 +46,16 @@ private: virtual void populate(float &customtop, float &custombottom) override; virtual void handle() override; + void find_languages(); + void find_sysnames(); + static const char *const HIDE_STATUS[]; std::function<void ()> m_handler; - std::vector<std::string> m_lang; - std::uint16_t m_currlang; + std::vector<std::string> m_languages; + std::vector<std::string> m_sysnames; + std::size_t m_currlang; + std::size_t m_currsysnames; }; //------------------------------------------------- diff --git a/src/frontend/mame/ui/datmenu.cpp b/src/frontend/mame/ui/datmenu.cpp index b772273d61a..6dabbc09223 100644 --- a/src/frontend/mame/ui/datmenu.cpp +++ b/src/frontend/mame/ui/datmenu.cpp @@ -77,10 +77,10 @@ menu_dats_view::menu_dats_view(mame_ui_manager &mui, render_container &container , m_issoft(true) { - if (swinfo != nullptr && !swinfo->usage.empty()) - m_items_list.emplace_back(_("Software Usage"), 0, ""); + if (swinfo != nullptr && !swinfo->infotext.empty()) + m_items_list.emplace_back(_("Software List Info"), 0, ""); std::vector<std::string> lua_list; - if(mame_machine_manager::instance()->lua()->call_plugin("data_list", std::string(m_short).append(1, ',').append(m_list).c_str(), lua_list)) + if (mame_machine_manager::instance()->lua()->call_plugin("data_list", std::string(m_short).append(1, ',').append(m_list).c_str(), lua_list)) { int count = 1; for(std::string &item : lua_list) @@ -417,7 +417,7 @@ void menu_dats_view::get_data_sw() std::vector<int> xend; std::string buffer; if (m_items_list[m_actual].option == 0) - buffer = m_swinfo->usage; + buffer = m_swinfo->infotext; else mame_machine_manager::instance()->lua()->call_plugin("data", m_items_list[m_actual].option - 1, buffer); diff --git a/src/frontend/mame/ui/dirmenu.cpp b/src/frontend/mame/ui/dirmenu.cpp index 9d6fd462e74..85ca2fc085b 100644 --- a/src/frontend/mame/ui/dirmenu.cpp +++ b/src/frontend/mame/ui/dirmenu.cpp @@ -34,35 +34,37 @@ struct folders_entry static const folders_entry s_folders[] = { - { __("ROMs"), OPTION_MEDIAPATH, ADDING }, - { __("Software Media"), OPTION_SWPATH, CHANGE }, - { __("UI"), OPTION_UI_PATH, CHANGE }, - { __("Language"), OPTION_LANGUAGEPATH, CHANGE }, - { __("Samples"), OPTION_SAMPLEPATH, ADDING }, - { __("DATs"), OPTION_HISTORY_PATH, ADDING }, - { __("INIs"), OPTION_INIPATH, ADDING }, - { __("Category INIs"), OPTION_CATEGORYINI_PATH, CHANGE }, - { __("Icons"), OPTION_ICONS_PATH, ADDING }, - { __("Cheats"), OPTION_CHEATPATH, ADDING }, - { __("Snapshots"), OPTION_SNAPSHOT_DIRECTORY, ADDING }, - { __("Cabinets"), OPTION_CABINETS_PATH, ADDING }, - { __("Flyers"), OPTION_FLYERS_PATH, ADDING }, - { __("Titles"), OPTION_TITLES_PATH, ADDING }, - { __("Ends"), OPTION_ENDS_PATH, ADDING }, - { __("PCBs"), OPTION_PCBS_PATH, ADDING }, - { __("Marquees"), OPTION_MARQUEES_PATH, ADDING }, - { __("Controls Panels"), OPTION_CPANELS_PATH, ADDING }, - { __("Crosshairs"), OPTION_CROSSHAIRPATH, ADDING }, - { __("Artworks"), OPTION_ARTPATH, ADDING }, - { __("Bosses"), OPTION_BOSSES_PATH, ADDING }, - { __("Artworks Preview"), OPTION_ARTPREV_PATH, ADDING }, - { __("Select"), OPTION_SELECT_PATH, ADDING }, - { __("GameOver"), OPTION_GAMEOVER_PATH, ADDING }, - { __("HowTo"), OPTION_HOWTO_PATH, ADDING }, - { __("Logos"), OPTION_LOGOS_PATH, ADDING }, - { __("Scores"), OPTION_SCORES_PATH, ADDING }, - { __("Versus"), OPTION_VERSUS_PATH, ADDING }, - { __("Covers"), OPTION_COVER_PATH, ADDING } + { N_p("path-option", "ROMs"), OPTION_MEDIAPATH, ADDING }, + { N_p("path-option", "Software Media"), OPTION_SWPATH, CHANGE }, + { N_p("path-option", "Sound Samples"), OPTION_SAMPLEPATH, ADDING }, + { N_p("path-option", "Artwork"), OPTION_ARTPATH, ADDING }, + { N_p("path-option", "Crosshairs"), OPTION_CROSSHAIRPATH, ADDING }, + { N_p("path-option", "Cheat Files"), OPTION_CHEATPATH, ADDING }, + { N_p("path-option", "Plugins"), OPTION_PLUGINSPATH, ADDING }, + { N_p("path-option", "UI Translations"), OPTION_LANGUAGEPATH, CHANGE }, + { N_p("path-option", "INIs"), OPTION_INIPATH, ADDING }, + { N_p("path-option", "UI Settings"), OPTION_UI_PATH, CHANGE }, + { N_p("path-option", "Plugin Data"), OPTION_PLUGINDATAPATH, CHANGE }, + { N_p("path-option", "DATs"), OPTION_HISTORY_PATH, ADDING }, + { N_p("path-option", "Category INIs"), OPTION_CATEGORYINI_PATH, CHANGE }, + { N_p("path-option", "Snapshots"), OPTION_SNAPSHOT_DIRECTORY, ADDING }, + { N_p("path-option", "Icons"), OPTION_ICONS_PATH, ADDING }, + { N_p("path-option", "Control Panels"), OPTION_CPANELS_PATH, ADDING }, + { N_p("path-option", "Cabinets"), OPTION_CABINETS_PATH, ADDING }, + { N_p("path-option", "Marquees"), OPTION_MARQUEES_PATH, ADDING }, + { N_p("path-option", "PCBs"), OPTION_PCBS_PATH, ADDING }, + { N_p("path-option", "Flyers"), OPTION_FLYERS_PATH, ADDING }, + { N_p("path-option", "Title Screens"), OPTION_TITLES_PATH, ADDING }, + { N_p("path-option", "Game Endings"), OPTION_ENDS_PATH, ADDING }, + { N_p("path-option", "Bosses"), OPTION_BOSSES_PATH, ADDING }, + { N_p("path-option", "Artwork Previews"), OPTION_ARTPREV_PATH, ADDING }, + { N_p("path-option", "Select"), OPTION_SELECT_PATH, ADDING }, + { N_p("path-option", "Game Over Screens"), OPTION_GAMEOVER_PATH, ADDING }, + { N_p("path-option", "HowTo"), OPTION_HOWTO_PATH, ADDING }, + { N_p("path-option", "Logos"), OPTION_LOGOS_PATH, ADDING }, + { N_p("path-option", "Scores"), OPTION_SCORES_PATH, ADDING }, + { N_p("path-option", "Versus"), OPTION_VERSUS_PATH, ADDING }, + { N_p("path-option", "Covers"), OPTION_COVER_PATH, ADDING } }; @@ -103,7 +105,7 @@ void menu_directory::handle() void menu_directory::populate(float &customtop, float &custombottom) { for (auto & elem : s_folders) - item_append(_(elem.name), 0, (void *)(uintptr_t)elem.action); + item_append(_("path-option", elem.name), 0, (void *)(uintptr_t)elem.action); item_append(menu_item_type::SEPARATOR); customtop = ui().get_line_height() + 3.0f * ui().box_tb_border(); @@ -166,7 +168,7 @@ void menu_display_actual::handle() void menu_display_actual::populate(float &customtop, float &custombottom) { - m_heading[0] = string_format(_("Current %1$s Folders"), _(s_folders[m_ref].name)); + m_heading[0] = string_format(_("Current %1$s Folders"), _("path-option", s_folders[m_ref].name)); if (ui().options().exists(s_folders[m_ref].option)) m_searchpath.assign(ui().options().value(s_folders[m_ref].option)); else @@ -417,7 +419,7 @@ void menu_add_change_folder::custom_render(void *selectedref, float top, float b std::string const toptext[] = { util::string_format( m_change ? _("Change %1$s Folder - Search: %2$s_") : _("Add %1$s Folder - Search: %2$s_"), - _(s_folders[m_ref].name), + _("path-option", s_folders[m_ref].name), m_search), m_current_path }; draw_text_box( @@ -511,7 +513,7 @@ void menu_remove_folder::populate(float &customtop, float &custombottom) void menu_remove_folder::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2) { - std::string const toptext[] = {string_format(_("Remove %1$s Folder"), _(s_folders[m_ref].name)) }; + std::string const toptext[] = {string_format(_("Remove %1$s Folder"), _("path-option", s_folders[m_ref].name)) }; draw_text_box( std::begin(toptext), std::end(toptext), origx1, origx2, origy1 - top, origy1 - ui().box_tb_border(), diff --git a/src/frontend/mame/ui/info.cpp b/src/frontend/mame/ui/info.cpp index 1c9e649dce7..e3f557ed54e 100644 --- a/src/frontend/mame/ui/info.cpp +++ b/src/frontend/mame/ui/info.cpp @@ -28,27 +28,27 @@ constexpr machine_flags::type MACHINE_WARNINGS = machine_flags::NO_COCKTAIL | m constexpr machine_flags::type MACHINE_BTANB = machine_flags::NO_SOUND_HW | machine_flags::IS_INCOMPLETE; constexpr std::pair<device_t::feature_type, char const *> FEATURE_NAMES[] = { - { device_t::feature::PROTECTION, __("protection") }, - { device_t::feature::TIMING, __("timing") }, - { device_t::feature::GRAPHICS, __("graphics") }, - { device_t::feature::PALETTE, __("color palette") }, - { device_t::feature::SOUND, __("sound") }, - { device_t::feature::CAPTURE, __("capture hardware") }, - { device_t::feature::CAMERA, __("camera") }, - { device_t::feature::MICROPHONE, __("microphone") }, - { device_t::feature::CONTROLS, __("controls") }, - { device_t::feature::KEYBOARD, __("keyboard") }, - { device_t::feature::MOUSE, __("mouse") }, - { device_t::feature::MEDIA, __("media") }, - { device_t::feature::DISK, __("disk") }, - { device_t::feature::PRINTER, __("printer") }, - { device_t::feature::TAPE, __("magnetic tape") }, - { device_t::feature::PUNCH, __("punch tape") }, - { device_t::feature::DRUM, __("magnetic drum") }, - { device_t::feature::ROM, __("solid state storage") }, - { device_t::feature::COMMS, __("communications") }, - { device_t::feature::LAN, __("LAN") }, - { device_t::feature::WAN, __("WAN") } }; + { device_t::feature::PROTECTION, N_p("emulation-feature", "protection") }, + { device_t::feature::TIMING, N_p("emulation-feature", "timing") }, + { device_t::feature::GRAPHICS, N_p("emulation-feature", "graphics") }, + { device_t::feature::PALETTE, N_p("emulation-feature", "color palette") }, + { device_t::feature::SOUND, N_p("emulation-feature", "sound") }, + { device_t::feature::CAPTURE, N_p("emulation-feature", "capture hardware") }, + { device_t::feature::CAMERA, N_p("emulation-feature", "camera") }, + { device_t::feature::MICROPHONE, N_p("emulation-feature", "microphone") }, + { device_t::feature::CONTROLS, N_p("emulation-feature", "controls") }, + { device_t::feature::KEYBOARD, N_p("emulation-feature", "keyboard") }, + { device_t::feature::MOUSE, N_p("emulation-feature", "mouse") }, + { device_t::feature::MEDIA, N_p("emulation-feature", "media") }, + { device_t::feature::DISK, N_p("emulation-feature", "disk") }, + { device_t::feature::PRINTER, N_p("emulation-feature", "printer") }, + { device_t::feature::TAPE, N_p("emulation-feature", "magnetic tape") }, + { device_t::feature::PUNCH, N_p("emulation-feature", "punch tape") }, + { device_t::feature::DRUM, N_p("emulation-feature", "magnetic drum") }, + { device_t::feature::ROM, N_p("emulation-feature", "solid state storage") }, + { device_t::feature::COMMS, N_p("emulation-feature", "communications") }, + { device_t::feature::LAN, N_p("emulation-feature", "LAN") }, + { device_t::feature::WAN, N_p("emulation-feature", "WAN") } }; } // anonymous namespace @@ -253,7 +253,7 @@ std::string machine_info::warnings_string() const { if (unemulated_features() & feature.first) { - util::stream_format(buf, first ? _("%s") : _(", %s"), _(feature.second)); + util::stream_format(buf, first ? _("%s") : _(", %s"), _("emulation-feature", feature.second)); first = false; } } @@ -269,7 +269,7 @@ std::string machine_info::warnings_string() const { if (imperfect_features() & feature.first) { - util::stream_format(buf, first ? _("%s") : _(", %s"), _(feature.second)); + util::stream_format(buf, first ? _("%s") : _(", %s"), _("emulation-feature", feature.second)); first = false; } } diff --git a/src/frontend/mame/ui/inifile.cpp b/src/frontend/mame/ui/inifile.cpp index 000ea922a4a..317df1c637a 100644 --- a/src/frontend/mame/ui/inifile.cpp +++ b/src/frontend/mame/ui/inifile.cpp @@ -219,48 +219,48 @@ favorite_manager::favorite_manager(ui_options &options) if (!file.open(FAVORITE_FILENAME)) { char readbuf[1024]; - file.gets(readbuf, 1024); + file.gets(readbuf, std::size(readbuf)); while (readbuf[0] == '[') - file.gets(readbuf, 1024); + file.gets(readbuf, std::size(readbuf)); - while (file.gets(readbuf, 1024)) + while (file.gets(readbuf, std::size(readbuf))) { ui_software_info tmpmatches; tmpmatches.shortname = chartrimcarriage(readbuf); - file.gets(readbuf, 1024); + file.gets(readbuf, std::size(readbuf)); tmpmatches.longname = chartrimcarriage(readbuf); - file.gets(readbuf, 1024); + file.gets(readbuf, std::size(readbuf)); tmpmatches.parentname = chartrimcarriage(readbuf); - file.gets(readbuf, 1024); + file.gets(readbuf, std::size(readbuf)); tmpmatches.year = chartrimcarriage(readbuf); - file.gets(readbuf, 1024); + file.gets(readbuf, std::size(readbuf)); tmpmatches.publisher = chartrimcarriage(readbuf); - file.gets(readbuf, 1024); + file.gets(readbuf, std::size(readbuf)); tmpmatches.supported = software_support(atoi(readbuf)); - file.gets(readbuf, 1024); + file.gets(readbuf, std::size(readbuf)); tmpmatches.part = chartrimcarriage(readbuf); - file.gets(readbuf, 1024); + file.gets(readbuf, std::size(readbuf)); chartrimcarriage(readbuf); auto dx = driver_list::find(readbuf); if (0 > dx) continue; tmpmatches.driver = &driver_list::driver(dx); - file.gets(readbuf, 1024); + file.gets(readbuf, std::size(readbuf)); tmpmatches.listname = chartrimcarriage(readbuf); - file.gets(readbuf, 1024); + file.gets(readbuf, std::size(readbuf)); tmpmatches.interface = chartrimcarriage(readbuf); - file.gets(readbuf, 1024); + file.gets(readbuf, std::size(readbuf)); tmpmatches.instance = chartrimcarriage(readbuf); - file.gets(readbuf, 1024); + file.gets(readbuf, std::size(readbuf)); tmpmatches.startempty = atoi(readbuf); - file.gets(readbuf, 1024); + file.gets(readbuf, std::size(readbuf)); tmpmatches.parentlongname = chartrimcarriage(readbuf); - file.gets(readbuf, 1024); - tmpmatches.usage = chartrimcarriage(readbuf); - file.gets(readbuf, 1024); + file.gets(readbuf, std::size(readbuf)); + //tmpmatches.usage = chartrimcarriage(readbuf); TODO: recover multi-line info + file.gets(readbuf, std::size(readbuf)); tmpmatches.devicetype = chartrimcarriage(readbuf); - file.gets(readbuf, 1024); + file.gets(readbuf, std::size(readbuf)); tmpmatches.available = atoi(readbuf); m_favorites.emplace(std::move(tmpmatches)); } @@ -292,25 +292,18 @@ void favorite_manager::add_favorite(running_machine &machine) if (imagedev) { // creating this is fairly expensive, but we'll assume this usually succeeds - ui_software_info info; software_part const *const part(imagedev->part_entry()); assert(software); assert(part); - - // start with simple stuff that can just be copied - info.shortname = software->shortname(); - info.longname = software->longname(); - info.parentname = software->parentname(); - info.year = software->year(); - info.publisher = software->publisher(); - info.supported = software->supported(); - info.part = part->name(); - info.driver = &driver; - info.listname = imagedev->software_list_name(); - info.interface = part->interface(); - info.instance = imagedev->instance_name(); - info.startempty = 0; - info.devicetype = strensure(imagedev->image_type_name()); + ui_software_info info( + *software, + *part, + driver, + imagedev->software_list_name(), + imagedev->instance_name(), + strensure(imagedev->image_type_name())); + + // assume it's available if it's mounted info.available = true; // look up the parent in the list if necessary (eugh, O(n) walk) @@ -328,16 +321,6 @@ void favorite_manager::add_favorite(running_machine &machine) } } - // fill in with the first usage entry we find - for (feature_list_item const &feature : software->other_info()) - { - if (feature.name() == "usage") - { - info.usage = feature.value(); - break; - } - } - // hooray for move semantics! add_impl(std::move(info)); } @@ -567,7 +550,7 @@ void favorite_manager::save_favorites() buf << info.instance << '\n'; util::stream_format(buf, "%d\n", info.startempty); buf << info.parentlongname << '\n'; - buf << info.usage << '\n'; + buf << '\n'; //buf << info.usage << '\n'; TODO: store multi-line info in a recoverable format buf << info.devicetype << '\n'; util::stream_format(buf, "%d\n", info.available); diff --git a/src/frontend/mame/ui/menu.cpp b/src/frontend/mame/ui/menu.cpp index 923a0fae6df..b785deea72b 100644 --- a/src/frontend/mame/ui/menu.cpp +++ b/src/frontend/mame/ui/menu.cpp @@ -78,7 +78,6 @@ bool menu::exclusive_input_pressed(int &iptkey, int key, int repeat) menu::global_state::global_state(running_machine &machine, ui_options const &options) : widgets_manager(machine) , m_machine(machine) - , m_cleanup_callbacks() , m_bgrnd_bitmap() , m_bgrnd_texture(nullptr, machine.render()) , m_stack() @@ -120,15 +119,6 @@ menu::global_state::~global_state() stack_reset(); clear_free_list(); - - for (auto const &callback : m_cleanup_callbacks) - callback(m_machine); -} - - -void menu::global_state::add_cleanup_callback(cleanup_callback &&callback) -{ - m_cleanup_callbacks.emplace_back(std::move(callback)); } diff --git a/src/frontend/mame/ui/menu.h b/src/frontend/mame/ui/menu.h index bb99e84f0ad..2d8d5e9173c 100644 --- a/src/frontend/mame/ui/menu.h +++ b/src/frontend/mame/ui/menu.h @@ -132,7 +132,6 @@ private: void draw_text_box(); protected: - using cleanup_callback = std::function<void(running_machine &)>; using bitmap_ptr = widgets_manager::bitmap_ptr; using texture_ptr = widgets_manager::texture_ptr; @@ -181,8 +180,6 @@ protected: void stack_reset() { m_global_state->stack_reset(); } bool stack_has_special_main_menu() const { return m_global_state->stack_has_special_main_menu(); } - void add_cleanup_callback(cleanup_callback &&callback) { m_global_state->add_cleanup_callback(std::move(callback)); } - // process a menu, drawing it and returning any interesting events const event *process(uint32_t flags, float x0 = 0.0f, float y0 = 0.0f); void process_parent() { m_parent->process(PROCESS_NOINPUT); } @@ -336,8 +333,6 @@ private: global_state(global_state &&) = delete; ~global_state(); - void add_cleanup_callback(cleanup_callback &&callback); - bitmap_argb32 *bgrnd_bitmap() { return m_bgrnd_bitmap.get(); } render_texture *bgrnd_texture() { return m_bgrnd_texture.get(); } @@ -351,10 +346,7 @@ private: bool stack_has_special_main_menu() const; private: - using cleanup_callback_vector = std::vector<cleanup_callback>; - running_machine &m_machine; - cleanup_callback_vector m_cleanup_callbacks; bitmap_ptr m_bgrnd_bitmap; texture_ptr m_bgrnd_texture; diff --git a/src/frontend/mame/ui/miscmenu.cpp b/src/frontend/mame/ui/miscmenu.cpp index da7989e6e3b..f053d2572f4 100644 --- a/src/frontend/mame/ui/miscmenu.cpp +++ b/src/frontend/mame/ui/miscmenu.cpp @@ -15,6 +15,7 @@ #include "ui/selector.h" #include "ui/submenu.h" #include "ui/ui.h" +#include "ui/utils.h" #include "infoxml.h" #include "mame.h" @@ -702,11 +703,33 @@ void menu_export::populate(float &customtop, float &custombottom) menu_machine_configure::menu_machine_configure( mame_ui_manager &mui, render_container &container, + ui_system_info const &info, + std::function<void (bool, bool)> &&handler, + float x0, float y0) + : menu_machine_configure(mui, container, info.description.c_str(), *info.driver, std::move(handler), x0, y0) +{ +} + +menu_machine_configure::menu_machine_configure( + mame_ui_manager &mui, + render_container &container, + game_driver const &drv, + std::function<void (bool, bool)> &&handler, + float x0, float y0) + : menu_machine_configure(mui, container, drv.type.fullname(), drv, std::move(handler), x0, y0) +{ +} + +menu_machine_configure::menu_machine_configure( + mame_ui_manager &mui, + render_container &container, + char const *description, game_driver const &drv, std::function<void (bool, bool)> &&handler, float x0, float y0) : menu(mui, container) , m_handler(std::move(handler)) + , m_description(description) , m_drv(drv) , m_x0(x0) , m_y0(y0) @@ -835,7 +858,7 @@ void menu_machine_configure::populate(float &customtop, float &custombottom) void menu_machine_configure::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2) { - char const *const text[] = { _("Configure Machine:"), m_drv.type.fullname() }; + char const *const text[] = { _("Configure Machine:"), m_description }; draw_text_box( std::begin(text), std::end(text), origx1, origx2, origy1 - top, origy1 - ui().box_tb_border(), diff --git a/src/frontend/mame/ui/miscmenu.h b/src/frontend/mame/ui/miscmenu.h index c8d833ae4e2..5fdf7fb8a8e 100644 --- a/src/frontend/mame/ui/miscmenu.h +++ b/src/frontend/mame/ui/miscmenu.h @@ -21,6 +21,9 @@ #include <vector> +struct ui_system_info; + + namespace ui { class menu_network_devices : public menu @@ -130,6 +133,12 @@ public: menu_machine_configure( mame_ui_manager &mui, render_container &container, + ui_system_info const &info, + std::function<void (bool, bool)> &&handler = nullptr, + float x0 = 0.0f, float y0 = 0.0f); + menu_machine_configure( + mame_ui_manager &mui, + render_container &container, game_driver const &drv, std::function<void (bool, bool)> &&handler = nullptr, float x0 = 0.0f, float y0 = 0.0f); @@ -153,12 +162,21 @@ private: LAST = ADVANCED }; + menu_machine_configure( + mame_ui_manager &mui, + render_container &container, + char const *description, + game_driver const &drv, + std::function<void (bool, bool)> &&handler, + float x0, float y0); + virtual void populate(float &customtop, float &custombottom) override; virtual void handle() override; void setup_bios(); std::function<void (bool, bool)> const m_handler; + char const *const m_description; game_driver const &m_drv; emu_options m_opts; float const m_x0; diff --git a/src/frontend/mame/ui/moptions.cpp b/src/frontend/mame/ui/moptions.cpp index cf1d521ef3b..375bbddf16a 100644 --- a/src/frontend/mame/ui/moptions.cpp +++ b/src/frontend/mame/ui/moptions.cpp @@ -21,7 +21,7 @@ const options_entry ui_options::s_option_entries[] = { // search path options { nullptr, nullptr, OPTION_HEADER, "UI SEARCH PATH OPTIONS" }, - { OPTION_HISTORY_PATH, "history;dats;.", OPTION_STRING, "path to history files" }, + { OPTION_HISTORY_PATH, "history;dats;.", OPTION_STRING, "path to system/software info files" }, { OPTION_CATEGORYINI_PATH, "folders", OPTION_STRING, "path to category ini files" }, { OPTION_CABINETS_PATH, "cabinets;cabdevs", OPTION_STRING, "path to cabinets / devices image" }, { OPTION_CPANELS_PATH, "cpanel", OPTION_STRING, "path to control panel image" }, @@ -44,6 +44,7 @@ const options_entry ui_options::s_option_entries[] = // misc options { nullptr, nullptr, OPTION_HEADER, "UI MISC OPTIONS" }, + { OPTION_SYSTEM_NAMES, "", OPTION_STRING, "translated system names file" }, { OPTION_SKIP_WARNINGS, "0", OPTION_BOOLEAN, "display fewer repeated warnings about imperfect emulation" }, { OPTION_REMEMBER_LAST, "1", OPTION_BOOLEAN, "initially select last used system in main menu" }, { OPTION_ENLARGE_SNAPS, "1", OPTION_BOOLEAN, "enlarge artwork (snapshot, title, etc.) in right panel (keeping aspect ratio)" }, diff --git a/src/frontend/mame/ui/moptions.h b/src/frontend/mame/ui/moptions.h index a345972bdc0..4b228aad974 100644 --- a/src/frontend/mame/ui/moptions.h +++ b/src/frontend/mame/ui/moptions.h @@ -38,6 +38,7 @@ #define OPTION_UI_PATH "ui_path" // core misc options +#define OPTION_SYSTEM_NAMES "system_names" #define OPTION_SKIP_WARNINGS "skip_warnings" #define OPTION_REMEMBER_LAST "remember_last" #define OPTION_ENLARGE_SNAPS "enlarge_snaps" @@ -104,6 +105,7 @@ public: const char *ui_path() const { return value(OPTION_UI_PATH); } // Misc options + const char *system_names() const { return value(OPTION_SYSTEM_NAMES); } bool skip_warnings() const { return bool_value(OPTION_SKIP_WARNINGS); } bool remember_last() const { return bool_value(OPTION_REMEMBER_LAST); } bool enlarge_snaps() const { return bool_value(OPTION_ENLARGE_SNAPS); } diff --git a/src/frontend/mame/ui/selgame.cpp b/src/frontend/mame/ui/selgame.cpp index 7582c90e0d8..89892664fc0 100644 --- a/src/frontend/mame/ui/selgame.cpp +++ b/src/frontend/mame/ui/selgame.cpp @@ -37,8 +37,10 @@ #include <condition_variable> #include <cstring> #include <iterator> +#include <locale> #include <memory> #include <mutex> +#include <string_view> #include <thread> @@ -57,13 +59,15 @@ 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 + 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_UCS_DFLT_DESC = 1U << 5, + AVAIL_UCS_MANUF_DFLT_DESC = 1U << 6, + AVAIL_FILTER_DATA = 1U << 7 }; ~persistent_data() @@ -72,10 +76,31 @@ public: m_thread->join(); } - void cache_data() + void cache_data(ui_options const &options) { std::unique_lock<std::mutex> lock(m_mutex); - do_start_caching(); + if (!m_started) + { + m_started = true; + m_thread = std::make_unique<std::thread>( + [this, datpath = std::string(options.history_path()), titles = std::string(options.system_names())] + { + do_cache_data(datpath, titles); + }); + } + } + + void reset_cache() + { + if (m_thread) + m_thread->join(); + std::unique_lock<std::mutex> lock(m_mutex); + m_thread.reset(); + m_started = false; + m_available = AVAIL_NONE; + m_sorted_list.clear(); + m_filter_data = machine_filter_data(); + m_bios_count = 0U; } bool is_available(available desired) @@ -87,8 +112,8 @@ public: { if (!is_available(desired)) { + assert(m_started); std::unique_lock<std::mutex> lock(m_mutex); - do_start_caching(); m_condition.wait(lock, [this, desired] () { return is_available(desired); }); } } @@ -138,42 +163,86 @@ private: m_condition.notify_all(); } - void do_start_caching() + void do_cache_data(std::string const &datpath, std::string const &titles) { - if (!m_started) - { - m_started = true; - m_thread = std::make_unique<std::thread>([this] { do_cache_data(); }); - } - } + // try to open the titles file for optimisation reasons + emu_file titles_file(datpath, OPEN_FLAG_READ); + bool const try_titles(!titles.empty() && !titles_file.open(titles)); - 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); - } - } + // generate full list - initially ordered by shortname + populate_list(!try_titles); // notify that BIOS count is valie notify_available(AVAIL_BIOS_COUNT); + // try to load localised descriptions + if (try_titles) + load_titles(titles_file); + + // populate parent descriptions while still ordered by shortname + populate_parents(); + // sort drivers and notify + std::collate<wchar_t> const &coll = std::use_facet<std::collate<wchar_t>>(std::locale()); + 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_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); }); + [&compare_names] (ui_system_info const &lhs, ui_system_info const &rhs) + { + game_driver const &x(*lhs.driver); + game_driver const &y(*rhs.driver); + + bool clonex = (x.parent[0] != '0') || x.parent[1]; + int cx = -1; + if (clonex) + { + cx = driver_list::find(x.parent); + if ((0 > cx) || (driver_list::driver(cx).flags & machine_flags::IS_BIOS_ROOT)) + clonex = false; + } + + bool cloney = (y.parent[0] != '0') || y.parent[1]; + int cy = -1; + if (cloney) + { + cy = driver_list::find(y.parent); + if ((0 > cy) || (driver_list::driver(cy).flags & machine_flags::IS_BIOS_ROOT)) + cloney = false; + } + + if (!clonex && !cloney) + { + return compare_names(lhs.description, rhs.description); + } + else if (clonex && cloney) + { + if (!std::strcmp(x.parent, y.parent)) + return compare_names(lhs.description, rhs.description); + else + return compare_names(lhs.parent, rhs.parent); + } + else if (!clonex && cloney) + { + if (!std::strcmp(x.name, y.parent)) + return true; + else + return compare_names(lhs.description, rhs.parent); + } + else + { + if (!std::strcmp(x.parent, y.name)) + return false; + else + return compare_names(lhs.parent, rhs.description); + } + }); notify_available(AVAIL_SORTED_LIST); // sort manufacturers and years @@ -187,7 +256,7 @@ private: // 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)); + info.ucs_description = ustr_from_utf8(normalize_unicode(info.description, unicode_normalization_form::D, true)); notify_available(AVAIL_UCS_DESCRIPTION); // convert "<manufacturer> <description>" to UCS-4 @@ -196,10 +265,141 @@ private: { buf.assign(info.driver->manufacturer); buf.append(1, ' '); - buf.append(info.driver->type.fullname()); + buf.append(info.description); info.ucs_manufacturer_description = ustr_from_utf8(normalize_unicode(buf, unicode_normalization_form::D, true)); } notify_available(AVAIL_UCS_MANUF_DESC); + + // convert default descriptions to UCS-4 + if (try_titles) + { + for (ui_system_info &info : m_sorted_list) + { + std::string_view const fullname(info.driver->type.fullname()); + if (info.description != fullname) + info.ucs_default_description = ustr_from_utf8(normalize_unicode(fullname, unicode_normalization_form::D, true)); + } + } + notify_available(AVAIL_UCS_DFLT_DESC); + + // convert "<manufacturer> <default description>" to UCS-4 + if (try_titles) + { + for (ui_system_info &info : m_sorted_list) + { + std::string_view const fullname(info.driver->type.fullname()); + if (info.description != fullname) + { + buf.assign(info.driver->manufacturer); + buf.append(1, ' '); + buf.append(fullname); + info.ucs_manufacturer_default_description = ustr_from_utf8(normalize_unicode(buf, unicode_normalization_form::D, true)); + } + } + } + notify_available(AVAIL_UCS_MANUF_DFLT_DESC); + } + + void populate_list(bool copydesc) + { + 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; + + ui_system_info &ins(m_sorted_list.emplace_back(driver, x, false)); + if (copydesc) + ins.description = driver.type.fullname(); + + m_filter_data.add_manufacturer(driver.manufacturer); + m_filter_data.add_year(driver.year); + } + } + } + + void load_titles(util::core_file &file) + { + char readbuf[1024]; + while (file.gets(readbuf, std::size(readbuf))) + { + // shortname and description separated by tab + auto const split(std::find(std::begin(readbuf), std::end(readbuf), '\t')); + if (std::end(readbuf) == split) + continue; + std::string_view const shortname(readbuf, split - readbuf); + + // find matching system - still sorted by shortname at this point + auto const found( + std::lower_bound( + m_sorted_list.begin(), + m_sorted_list.end(), + shortname, + [] (ui_system_info const &a, std::string_view const &b) + { + return a.driver->name < b; + })); + if ((m_sorted_list.end() == found) || (shortname != found->driver->name)) + { + //osd_printf_verbose("System '%s' not found\n", shortname); very spammy for single-driver builds + continue; + } + + // strip additional columns + auto const start(std::next(split)); + auto const end(std::find(start, std::end(readbuf), '\t')); + auto const description(strtrimspace(std::string_view(start, end - start))); + if (description.empty()) + { + osd_printf_warning("Empty translated description for system '%s'\n", shortname); + } + else if (!found->description.empty()) + { + osd_printf_warning( + "Multiple translated descriptions for system '%s' ('%s' and '%s')\n", + shortname, + found->description, + description); + } + else + { + found->description = description; + } + } + + // fill in untranslated descriptions + for (ui_system_info &info : m_sorted_list) + { + if (info.description.empty()) + info.description = info.driver->type.fullname(); + } + } + + void populate_parents() + { + for (ui_system_info &info : m_sorted_list) + { + if (info.driver->parent[0] != '0') + { + auto const found( + std::lower_bound( + m_sorted_list.begin(), + m_sorted_list.end(), + std::string_view(info.driver->parent), + [] (ui_system_info const &a, std::string_view const &b) + { + return a.driver->name < b; + })); + if (m_sorted_list.end() != found) + info.parent = found->description; + else + info.parent = info.driver->parent; + } + } } // synchronisation @@ -236,7 +436,7 @@ menu_select_game::menu_select_game(mame_ui_manager &mui, render_container &conta ui_options &moptions = mui.options(); // load drivers cache - m_persistent_data.cache_data(); + m_persistent_data.cache_data(mui.options()); // check if there are available system icons check_for_icons(nullptr); @@ -294,14 +494,13 @@ menu_select_game::menu_select_game(mame_ui_manager &mui, render_container &conta menu_select_game::~menu_select_game() { std::string error_string, last_driver; - game_driver const *driver; + ui_system_info const *system; ui_software_info const *swinfo; - get_selection(swinfo, driver); + get_selection(swinfo, system); if (swinfo) last_driver = swinfo->shortname; - else - if (driver) - last_driver = driver->name; + else if (system) + last_driver = system->driver->name; std::string const filter(m_persistent_data.filter_data().get_config_string()); @@ -325,6 +524,10 @@ void menu_select_game::handle() // if I have to load datfile, perform a hard reset if (ui_globals::reset) { + // dumb workaround for not being able to add an exit notifier + struct cache_reset { ~cache_reset() { persistent_data::instance().reset_cache(); } }; + ui().get_session_data<cache_reset, cache_reset>(); + ui_globals::reset = false; machine().schedule_hard_reset(); stack_reset(); @@ -334,10 +537,10 @@ void menu_select_game::handle() // if I have to select software, force software list submenu if (reselect_last::get()) { - const game_driver *driver; + const ui_system_info *system; const ui_software_info *software; - get_selection(software, driver); - menu::stack_push<menu_select_software>(ui(), container(), *driver); + get_selection(software, system); + menu::stack_push<menu_select_software>(ui(), container(), *system); return; } @@ -403,7 +606,7 @@ void menu_select_game::handle() { menu::stack_push<menu_machine_configure>( ui(), container(), - *reinterpret_cast<const game_driver *>(m_prev_selected), + *reinterpret_cast<ui_system_info const *>(m_prev_selected), nullptr, menu_event->mouse.x0, menu_event->mouse.y0); } @@ -454,16 +657,17 @@ void menu_select_game::handle() favorite_manager &mfav(mame_machine_manager::instance()->favorite()); if (!m_populated_favorites) { - game_driver const *const driver(reinterpret_cast<game_driver const *>(menu_event->itemref)); - if (!mfav.is_favorite_system(*driver)) + auto const &info(*reinterpret_cast<ui_system_info const *>(menu_event->itemref)); + auto const &driver(*info.driver); + if (!mfav.is_favorite_system(driver)) { - mfav.add_favorite_system(*driver); - machine().popmessage(_("%s\n added to favorites list."), driver->type.fullname()); + mfav.add_favorite_system(driver); + machine().popmessage(_("%s\n added to favorites list."), info.description); } else { - mfav.remove_favorite_system(*driver); - machine().popmessage(_("%s\n removed from favorites list."), driver->type.fullname()); + mfav.remove_favorite_system(driver); + machine().popmessage(_("%s\n removed from favorites list."), info.description); } } else @@ -557,7 +761,7 @@ void menu_select_game::populate(float &customtop, float &custombottom) cloneof = false; } - item_append(elem.driver->type.fullname(), (cloneof) ? (FLAGS_UI | FLAG_INVERT) : FLAGS_UI, (void *)elem.driver); + item_append(elem.description, (cloneof) ? (FLAGS_UI | FLAG_INVERT) : FLAGS_UI, (void *)&elem); curitem++; } } @@ -761,9 +965,9 @@ void menu_select_game::force_game_select(mame_ui_manager &mui, render_container void menu_select_game::inkey_select(const event *menu_event) { - const game_driver *driver = (const game_driver *)menu_event->itemref; + auto const system = reinterpret_cast<ui_system_info const *>(menu_event->itemref); - if ((uintptr_t)driver == CONF_OPTS) + if (uintptr_t(system) == CONF_OPTS) { // special case for configure options menu::stack_push<menu_game_options>( @@ -772,14 +976,14 @@ void menu_select_game::inkey_select(const event *menu_event) m_persistent_data.filter_data(), [this] () { reset(reset_options::SELECT_FIRST); }); } - else if (uintptr_t(driver) == CONF_MACHINE) + else if (uintptr_t(system) == 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 ui_system_info *>(m_prev_selected)); return; } - else if ((uintptr_t)driver == CONF_PLUGINS) + else if (uintptr_t(system) == CONF_PLUGINS) { // special case for configure plugins menu::stack_push<menu_plugins_configure>(ui(), container()); @@ -787,7 +991,7 @@ void menu_select_game::inkey_select(const event *menu_event) else { // anything else is a driver - driver_enumerator enumerator(machine().options(), *driver); + driver_enumerator enumerator(machine().options(), *system->driver); enumerator.next(); // if there are software entries, show a software selection menu @@ -795,7 +999,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(), *system); return; } } @@ -807,8 +1011,8 @@ void menu_select_game::inkey_select(const event *menu_event) // if everything looks good, schedule the new driver if (audit_passed(summary)) { - if (!select_bios(*driver, false)) - launch_system(*driver); + if (!select_bios(*system->driver, false)) + launch_system(*system->driver); } else { @@ -853,7 +1057,7 @@ void menu_select_game::inkey_select_favorite(const event *menu_event) } return; } - else if ((uintptr_t)ui_swinfo == CONF_PLUGINS) + else if (uintptr_t(ui_swinfo) == CONF_PLUGINS) { // special case for configure plugins menu::stack_push<menu_plugins_configure>(ui(), container()); @@ -954,12 +1158,12 @@ void menu_select_game::change_info_pane(int delta) m_topline_datsview = 0; } }; - game_driver const *drv; + ui_system_info const *sys; ui_software_info const *soft; - get_selection(soft, drv); + get_selection(soft, sys); if (!m_populated_favorites) { - if (uintptr_t(drv) > skip_main_items) + if (uintptr_t(sys) > skip_main_items) cap_delta(ui_globals::curdats_view, ui_globals::curdats_total); } else if (uintptr_t(soft) > skip_main_items) @@ -1025,6 +1229,34 @@ void menu_select_game::populate_search() } } + // match default description + if (m_persistent_data.is_available(persistent_data::AVAIL_UCS_DFLT_DESC)) + { + m_searched_fields |= persistent_data::AVAIL_UCS_DFLT_DESC; + for (std::pair<double, std::reference_wrapper<ui_system_info const> > &info : m_searchlist) + { + if (info.first && !info.second.get().ucs_default_description.empty()) + { + double const penalty(util::edit_distance(ucs_search, info.second.get().ucs_default_description)); + info.first = (std::min)(penalty, info.first); + } + } + } + + // match default description + if (m_persistent_data.is_available(persistent_data::AVAIL_UCS_MANUF_DFLT_DESC)) + { + m_searched_fields |= persistent_data::AVAIL_UCS_DFLT_DESC; + for (std::pair<double, std::reference_wrapper<ui_system_info const> > &info : m_searchlist) + { + if (info.first && !info.second.get().ucs_manufacturer_default_description.empty()) + { + double const penalty(util::edit_distance(ucs_search, info.second.get().ucs_manufacturer_default_description)); + info.first = (std::min)(penalty, info.first); + } + } + } + // sort according to edit distance std::stable_sort( m_searchlist.begin(), @@ -1036,20 +1268,21 @@ void menu_select_game::populate_search() // generate general info //------------------------------------------------- -void menu_select_game::general_info(const game_driver *driver, std::string &buffer) +void menu_select_game::general_info(ui_system_info const &system, std::string &buffer) { - system_flags const &flags(get_system_flags(*driver)); + game_driver const &driver(*system.driver); + system_flags const &flags(get_system_flags(driver)); std::ostringstream str; str << "#j2\n"; - util::stream_format(str, _("Romset\t%1$-.100s\n"), driver->name); - util::stream_format(str, _("Year\t%1$s\n"), driver->year); - util::stream_format(str, _("Manufacturer\t%1$-.100s\n"), driver->manufacturer); + util::stream_format(str, _("Romset\t%1$-.100s\n"), driver.name); + util::stream_format(str, _("Year\t%1$s\n"), driver.year); + util::stream_format(str, _("Manufacturer\t%1$-.100s\n"), driver.manufacturer); - int cloneof = driver_list::non_bios_clone(*driver); + int cloneof = driver_list::non_bios_clone(driver); if (cloneof != -1) - util::stream_format(str, _("Driver is Clone of\t%1$-.100s\n"), driver_list::driver(cloneof).type.fullname()); + util::stream_format(str, _("Driver is Clone of\t%1$-.100s\n"), system.parent); else str << _("Driver is Parent\t\n"); @@ -1179,7 +1412,7 @@ void menu_select_game::general_info(const game_driver *driver, std::string &buff str << ((flags.machine_flags() & machine_flags::SUPPORTS_SAVE) ? _("Support Save\tYes\n") : _("Support Save\tNo\n")); str << ((flags.machine_flags() & ORIENTATION_SWAP_XY) ? _("Screen Orientation\tVertical\n") : _("Screen Orientation\tHorizontal\n")); bool found = false; - for (romload::region const ®ion : romload::entries(driver->rom).get_regions()) + for (romload::region const ®ion : romload::entries(driver.rom).get_regions()) { if (region.is_diskdata()) { @@ -1192,7 +1425,7 @@ void menu_select_game::general_info(const game_driver *driver, std::string &buff // audit the game first to see if we're going to work if (ui().options().info_audit()) { - driver_enumerator enumerator(machine().options(), *driver); + driver_enumerator enumerator(machine().options(), driver); enumerator.next(); media_auditor auditor(enumerator); media_auditor::summary summary = auditor.audit_media(AUDIT_VALIDATE_FAST); @@ -1228,7 +1461,7 @@ render_texture *menu_select_game::get_icon_texture(int linenum, void *selectedre { game_driver const *const driver(m_populated_favorites ? reinterpret_cast<ui_software_info const *>(selectedref)->driver - : reinterpret_cast<game_driver const *>(selectedref)); + : reinterpret_cast<ui_system_info const *>(selectedref)->driver); assert(driver); icon_cache::iterator icon(m_icons.find(driver)); @@ -1387,17 +1620,17 @@ float menu_select_game::draw_left_panel(float x1, float y1, float x2, float y2) // get selected software and/or driver //------------------------------------------------- -void menu_select_game::get_selection(ui_software_info const *&software, game_driver const *&driver) const +void menu_select_game::get_selection(ui_software_info const *&software, ui_system_info const *&system) const { if (m_populated_favorites) { software = reinterpret_cast<ui_software_info const *>(get_selection_ptr()); - driver = software ? software->driver : nullptr; + system = nullptr; } else { software = nullptr; - driver = reinterpret_cast<game_driver const *>(get_selection_ptr()); + system = reinterpret_cast<ui_system_info const *>(get_selection_ptr()); } } @@ -1428,17 +1661,10 @@ void menu_select_game::make_topbox_text(std::string &line0, std::string &line1, } -std::string menu_select_game::make_driver_description(game_driver const &driver) const -{ - // first line is game name - return string_format(_("Romset: %1$-.100s"), driver.name); -} - - std::string menu_select_game::make_software_description(ui_software_info const &software) const { // first line is system - return string_format(_("System: %1$-.100s"), software.driver->type.fullname()); + return string_format(_("System: %1$-.100s"), software.driver->type.fullname()); // TODO: localise description } diff --git a/src/frontend/mame/ui/selgame.h b/src/frontend/mame/ui/selgame.h index 13bfd1170ac..7eac71d534d 100644 --- a/src/frontend/mame/ui/selgame.h +++ b/src/frontend/mame/ui/selgame.h @@ -60,12 +60,11 @@ private: virtual render_texture *get_icon_texture(int linenum, void *selectedref) override; // get selected software and/or driver - virtual void get_selection(ui_software_info const *&software, game_driver const *&driver) const override; + virtual void get_selection(ui_software_info const *&software, ui_system_info const *&system) const override; virtual bool accept_search() const override { return !isfavorite(); } // text for main top/bottom panels virtual void make_topbox_text(std::string &line0, std::string &line1, std::string &line2) const override; - virtual std::string make_driver_description(game_driver const &driver) const override; virtual std::string make_software_description(ui_software_info const &software) const override; // filter navigation @@ -85,7 +84,7 @@ private: void load_custom_filters(); // General info - virtual void general_info(const game_driver *driver, std::string &buffer) override; + virtual void general_info(ui_system_info const &system, std::string &buffer) override; // handlers void inkey_select(const event *menu_event); diff --git a/src/frontend/mame/ui/selmenu.cpp b/src/frontend/mame/ui/selmenu.cpp index 15c7e1c3364..a8ee5faa263 100644 --- a/src/frontend/mame/ui/selmenu.cpp +++ b/src/frontend/mame/ui/selmenu.cpp @@ -73,29 +73,29 @@ enum std::pair<char const *, char const *> const arts_info[] = { - { __("Snapshots"), OPTION_SNAPSHOT_DIRECTORY }, - { __("Cabinets"), OPTION_CABINETS_PATH }, - { __("Control Panels"), OPTION_CPANELS_PATH }, - { __("PCBs"), OPTION_PCBS_PATH }, - { __("Flyers"), OPTION_FLYERS_PATH }, - { __("Titles"), OPTION_TITLES_PATH }, - { __("Ends"), OPTION_ENDS_PATH }, - { __("Artwork Preview"), OPTION_ARTPREV_PATH }, - { __("Bosses"), OPTION_BOSSES_PATH }, - { __("Logos"), OPTION_LOGOS_PATH }, - { __("Versus"), OPTION_VERSUS_PATH }, - { __("Game Over"), OPTION_GAMEOVER_PATH }, - { __("HowTo"), OPTION_HOWTO_PATH }, - { __("Scores"), OPTION_SCORES_PATH }, - { __("Select"), OPTION_SELECT_PATH }, - { __("Marquees"), OPTION_MARQUEES_PATH }, - { __("Covers"), OPTION_COVER_PATH }, + { N_p("selmenu-artwork", "Snapshots"), OPTION_SNAPSHOT_DIRECTORY }, + { N_p("selmenu-artwork", "Cabinet"), OPTION_CABINETS_PATH }, + { N_p("selmenu-artwork", "Control Panel"), OPTION_CPANELS_PATH }, + { N_p("selmenu-artwork", "PCB"), OPTION_PCBS_PATH }, + { N_p("selmenu-artwork", "Flyer"), OPTION_FLYERS_PATH }, + { N_p("selmenu-artwork", "Title Screen"), OPTION_TITLES_PATH }, + { N_p("selmenu-artwork", "Ending"), OPTION_ENDS_PATH }, + { N_p("selmenu-artwork", "Artwork Preview"), OPTION_ARTPREV_PATH }, + { N_p("selmenu-artwork", "Bosses"), OPTION_BOSSES_PATH }, + { N_p("selmenu-artwork", "Logo"), OPTION_LOGOS_PATH }, + { N_p("selmenu-artwork", "Versus"), OPTION_VERSUS_PATH }, + { N_p("selmenu-artwork", "Game Over"), OPTION_GAMEOVER_PATH }, + { N_p("selmenu-artwork", "HowTo"), OPTION_HOWTO_PATH }, + { N_p("selmenu-artwork", "Scores"), OPTION_SCORES_PATH }, + { N_p("selmenu-artwork", "Select"), OPTION_SELECT_PATH }, + { N_p("selmenu-artwork", "Marquee"), OPTION_MARQUEES_PATH }, + { N_p("selmenu-artwork", "Covers"), OPTION_COVER_PATH }, }; char const *const hover_msg[] = { - __("Add or remove favorites"), - __("Export displayed list to file"), - __("Show DATs view"), + N_("Add or remove favorites"), + N_("Export displayed list to file"), + N_("Show DATs view"), }; @@ -202,9 +202,6 @@ std::string menu_select_launch::reselect_last::s_software; std::string menu_select_launch::reselect_last::s_swlist; bool menu_select_launch::reselect_last::s_reselect = false; -std::mutex menu_select_launch::s_cache_guard; -menu_select_launch::cache_ptr_map menu_select_launch::s_caches; - // instantiate possible variants of these so derived classes don't get link errors template bool menu_select_launch::select_bios(game_driver const &, bool); template bool menu_select_launch::select_bios(ui_software_info const &, bool); @@ -514,7 +511,7 @@ menu_select_launch::menu_select_launch(mame_ui_manager &mui, render_container &c , m_info_view(-1) , m_items_list() , m_info_buffer() - , m_cache() + , m_cache(mui.get_session_data<menu_select_launch, cache_wrapper>(machine())) , m_is_swlist(is_swlist) , m_focus(focused_menu::MAIN) , m_pressed(false) @@ -526,22 +523,6 @@ menu_select_launch::menu_select_launch(mame_ui_manager &mui, render_container &c , m_image_view(FIRST_VIEW) , m_flags(256) { - // set up persistent cache for machine run - { - std::lock_guard<std::mutex> guard(s_cache_guard); - auto const found(s_caches.find(&machine())); - if (found != s_caches.end()) - { - assert(found->second); - m_cache = found->second; - } - else - { - m_cache = std::make_shared<cache>(machine()); - s_caches.emplace(&machine(), m_cache); - add_cleanup_callback(&menu_select_launch::exit); - } - } } @@ -663,12 +644,12 @@ void menu_select_launch::custom_render(void *selectedref, float top, float botto // determine the text to render below ui_software_info const *swinfo; - game_driver const *driver; - get_selection(swinfo, driver); + ui_system_info const *system; + get_selection(swinfo, system); bool isstar = false; rgb_t color = ui().colors().background_color(); - if (swinfo && ((swinfo->startempty != 1) || !driver)) + if (swinfo && !swinfo->startempty) { isstar = mame_machine_manager::instance()->favorite().is_favorite_system_software(*swinfo); @@ -704,26 +685,29 @@ void menu_select_launch::custom_render(void *selectedref, float top, float botto // last line is romset name tempbuf[4] = string_format(_("Software list/item: %1$s:%2$s"), swinfo->listname, swinfo->shortname); } - else if (driver) + else if (system || (swinfo && swinfo->driver)) { - isstar = mame_machine_manager::instance()->favorite().is_favorite_system(*driver); + game_driver const &driver(system ? *system->driver : *swinfo->driver); + isstar = mame_machine_manager::instance()->favorite().is_favorite_system(driver); - // first line is game description/game name - tempbuf[0] = make_driver_description(*driver); + // first line is the ROM set + tempbuf[0] = string_format(_("Romset: %1$-.100s"), driver.name); // next line is year, manufacturer - tempbuf[1] = string_format(_("%1$s, %2$-.100s"), driver->year, driver->manufacturer); + tempbuf[1] = string_format(_("%1$s, %2$-.100s"), driver.year, driver.manufacturer); // next line is clone/parent status - int cloneof = driver_list::non_bios_clone(*driver); + int cloneof = driver_list::non_bios_clone(driver); - if (cloneof != -1) - tempbuf[2] = string_format(_("Driver is clone of: %1$-.100s"), driver_list::driver(cloneof).type.fullname()); - else + if (0 > cloneof) tempbuf[2] = _("Driver is parent"); + else if (system) + tempbuf[2] = string_format(_("Driver is clone of: %1$-.100s"), system->parent); + else + tempbuf[2] = string_format(_("Driver is clone of: %1$-.100s"), driver_list::driver(cloneof).type.fullname()); // next line is overall driver status - system_flags const &flags(get_system_flags(*driver)); + system_flags const &flags(get_system_flags(driver)); if (flags.machine_flags() & machine_flags::NOT_WORKING) tempbuf[3] = _("Overall: NOT WORKING"); else if ((flags.unemulated_features() | flags.imperfect_features()) & device_t::feature::PROTECTION) @@ -739,7 +723,7 @@ void menu_select_launch::custom_render(void *selectedref, float top, float botto else tempbuf[4] = _("Graphics: OK, "); - if (driver->flags & machine_flags::NO_SOUND_HW) + if (driver.flags & machine_flags::NO_SOUND_HW) tempbuf[4].append(_("Sound: None")); else if (flags.unemulated_features() & device_t::feature::SOUND) tempbuf[4].append(_("Sound: Unimplemented")); @@ -852,19 +836,19 @@ void menu_select_launch::rotate_focus(int dir) void menu_select_launch::inkey_dats() { ui_software_info const *software; - game_driver const *driver; - get_selection(software, driver); + ui_system_info const *system; + get_selection(software, system); if (software) { if (software->startempty && mame_machine_manager::instance()->lua()->call_plugin_check<const char *>("data_list", software->driver->name, true)) menu::stack_push<menu_dats_view>(ui(), container(), software->driver); - else if (mame_machine_manager::instance()->lua()->call_plugin_check<const char *>("data_list", std::string(software->shortname).append(1, ',').append(software->listname).c_str()) || !software->usage.empty()) + else if (mame_machine_manager::instance()->lua()->call_plugin_check<const char *>("data_list", std::string(software->shortname).append(1, ',').append(software->listname).c_str()) || !software->infotext.empty()) menu::stack_push<menu_dats_view>(ui(), container(), software); } - else if (driver) + else if (system) { - if (mame_machine_manager::instance()->lua()->call_plugin_check<const char *>("data_list", driver->name, true)) - menu::stack_push<menu_dats_view>(ui(), container(), driver); + if (mame_machine_manager::instance()->lua()->call_plugin_check<const char *>("data_list", system->driver->name, true)) + menu::stack_push<menu_dats_view>(ui(), container(), system->driver); } } @@ -1261,8 +1245,8 @@ void menu_select_launch::draw_toolbar(float x1, float y1, float x2, float y2) y1 += ui().box_tb_border(); y2 -= ui().box_tb_border(); - texture_ptr_vector const &t_texture(m_is_swlist ? m_cache->sw_toolbar_texture() : m_cache->toolbar_texture()); - bitmap_vector const &t_bitmap(m_is_swlist ? m_cache->sw_toolbar_bitmap() : m_cache->toolbar_bitmap()); + texture_ptr_vector const &t_texture(m_is_swlist ? m_cache.sw_toolbar_texture() : m_cache.toolbar_texture()); + bitmap_vector const &t_bitmap(m_is_swlist ? m_cache.sw_toolbar_bitmap() : m_cache.toolbar_bitmap()); auto const num_valid(std::count_if(std::begin(t_bitmap), std::end(t_bitmap), [](bitmap_argb32 const &e) { return e.valid(); })); @@ -1300,7 +1284,7 @@ void menu_select_launch::draw_star(float x0, float y0) { float y1 = y0 + ui().get_line_height(); float x1 = x0 + ui().get_line_height() * container().manager().ui_aspect(&container()); - container().add_quad(x0, y0, x1, y1, rgb_t::white(), m_cache->star_texture(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_PACKABLE); + container().add_quad(x0, y0, x1, y1, rgb_t::white(), m_cache.star_texture(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_PACKABLE); } @@ -1335,7 +1319,7 @@ void menu_select_launch::draw_icon(int linenum, void *selectedref, float x0, flo void menu_select_launch::get_title_search(std::string &snaptext, std::string &searchstr) { // get arts title text - snaptext.assign(_(arts_info[m_image_view].first)); + snaptext.assign(_("selmenu-artwork", arts_info[m_image_view].first)); // get search path std::string addpath; @@ -2253,12 +2237,12 @@ float menu_select_launch::draw_right_box_title(float x1, float y1, float x2, flo void menu_select_launch::arts_render(float origx1, float origy1, float origx2, float origy2) { ui_software_info const *software; - game_driver const *driver; - get_selection(software, driver); + ui_system_info const *system; + get_selection(software, system); - if (software && (!software->startempty || !driver)) + if (software && (!software->startempty || !system)) { - m_cache->set_snapx_driver(nullptr); + m_cache.set_snapx_driver(nullptr); if (m_default_image) m_image_view = (software->startempty == 0) ? SNAPSHOT_VIEW : CABINETS_VIEW; @@ -2267,7 +2251,7 @@ void menu_select_launch::arts_render(float origx1, float origy1, float origx2, f std::string const searchstr = arts_render_common(origx1, origy1, origx2, origy2); // loads the image if necessary - if (!m_cache->snapx_software_is(software) || !snapx_valid() || m_switch_image) + if (!m_cache.snapx_software_is(software) || !snapx_valid() || m_switch_image) { emu_file snapfile(searchstr.c_str(), OPEN_FLAG_READ); bitmap_argb32 tmp_bitmap; @@ -2287,7 +2271,7 @@ void menu_select_launch::arts_render(float origx1, float origy1, float origx2, f load_image(tmp_bitmap, snapfile, util::path_concat(software->driver->name + software->part, software->shortname)); } - m_cache->set_snapx_software(software); + m_cache.set_snapx_software(software); m_switch_image = false; arts_render_images(std::move(tmp_bitmap), origx1, origy1, origx2, origy2); } @@ -2295,23 +2279,23 @@ void menu_select_launch::arts_render(float origx1, float origy1, float origx2, f // if the image is available, loaded and valid, display it draw_snapx(origx1, origy1, origx2, origy2); } - else if (driver) + else if (system) { - m_cache->set_snapx_software(nullptr); + m_cache.set_snapx_software(nullptr); if (m_default_image) - m_image_view = ((driver->flags & machine_flags::MASK_TYPE) != machine_flags::TYPE_ARCADE) ? CABINETS_VIEW : SNAPSHOT_VIEW; + m_image_view = ((system->driver->flags & machine_flags::MASK_TYPE) != machine_flags::TYPE_ARCADE) ? CABINETS_VIEW : SNAPSHOT_VIEW; std::string const searchstr = arts_render_common(origx1, origy1, origx2, origy2); // loads the image if necessary - if (!m_cache->snapx_driver_is(driver) || !snapx_valid() || m_switch_image) + if (!m_cache.snapx_driver_is(system->driver) || !snapx_valid() || m_switch_image) { emu_file snapfile(searchstr, OPEN_FLAG_READ); bitmap_argb32 tmp_bitmap; - load_driver_image(tmp_bitmap, snapfile, *driver); + load_driver_image(tmp_bitmap, snapfile, *system->driver); - m_cache->set_snapx_driver(driver); + m_cache.set_snapx_driver(system->driver); m_switch_image = false; arts_render_images(std::move(tmp_bitmap), origx1, origy1, origx2, origy2); } @@ -2340,7 +2324,7 @@ std::string menu_select_launch::arts_render_common(float origx1, float origy1, f { float text_length; ui().draw_text_full(container(), - _(arts_info[x].first), origx1, origy1, origx2 - origx1, + _("selmenu-artwork", arts_info[x].first), origx1, origy1, origx2 - origx1, ui::text_layout::CENTER, ui::text_layout::TRUNCATE, mame_ui_manager::NONE, rgb_t::white(), rgb_t::black(), &text_length, nullptr); title_size = (std::max)(text_length + 0.01f, title_size); @@ -2389,7 +2373,7 @@ void menu_select_launch::arts_render_images(bitmap_argb32 &&tmp_bitmap, float or if (!tmp_bitmap.valid()) { tmp_bitmap.allocate(256, 256); - const bitmap_argb32 &src(m_cache->no_avail_bitmap()); + const bitmap_argb32 &src(m_cache.no_avail_bitmap()); for (int x = 0; x < 256; x++) { for (int y = 0; y < 256; y++) @@ -2398,7 +2382,7 @@ void menu_select_launch::arts_render_images(bitmap_argb32 &&tmp_bitmap, float or no_available = true; } - bitmap_argb32 &snapx_bitmap(m_cache->snapx_bitmap()); + bitmap_argb32 &snapx_bitmap(m_cache.snapx_bitmap()); if (tmp_bitmap.valid()) { float panel_width = origx2 - origx1 - 0.02f; @@ -2459,7 +2443,7 @@ void menu_select_launch::arts_render_images(bitmap_argb32 &&tmp_bitmap, float or snapx_bitmap.pix(y + y1, x + x1) = dest_bitmap.pix(y, x); // apply bitmap - m_cache->snapx_texture()->set_bitmap(snapx_bitmap, snapx_bitmap.cliprect(), TEXFORMAT_ARGB32); + m_cache.snapx_texture()->set_bitmap(snapx_bitmap, snapx_bitmap.cliprect(), TEXFORMAT_ARGB32); } else { @@ -2484,7 +2468,7 @@ void menu_select_launch::draw_snapx(float origx1, float origy1, float origx2, fl float const y2 = origy2 - ui().box_tb_border() - line_height; // apply texture - container().add_quad(x1, y1, x2, y2, rgb_t::white(), m_cache->snapx_texture(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA)); + container().add_quad(x1, y1, x2, y2, rgb_t::white(), m_cache.snapx_texture(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA)); } } @@ -2598,13 +2582,6 @@ bool menu_select_launch::has_multiple_bios(game_driver const &driver, s_bios &bi } -void menu_select_launch::exit(running_machine &machine) -{ - std::lock_guard<std::mutex> guard(s_cache_guard); - s_caches.erase(&machine); -} - - //------------------------------------------------- // draw collapsed left panel //------------------------------------------------- @@ -2648,14 +2625,14 @@ void menu_select_launch::infos_render(float origx1, float origy1, float origx2, std::vector<int> xend; const char *first = ""; ui_software_info const *software; - game_driver const *driver; + ui_system_info const *system; int total; - get_selection(software, driver); + get_selection(software, system); - if (software && (!software->startempty || !driver)) + if (software && (!software->startempty || !system)) { m_info_driver = nullptr; - first = __("Usage"); + first = N_("Software List Info"); if ((m_info_software != software) || (m_info_view != ui_globals::cur_sw_dats_view)) { @@ -2677,7 +2654,7 @@ void menu_select_launch::infos_render(float origx1, float origy1, float origx2, if (m_info_view == 0) { - m_info_buffer = software->usage; + m_info_buffer = software->infotext; } else { @@ -2687,31 +2664,33 @@ void menu_select_launch::infos_render(float origx1, float origy1, float origx2, } total = ui_globals::cur_sw_dats_total; } - else if (driver) + else if (system) { m_info_software = nullptr; - first = __("General Info"); + first = N_("General Info"); - if (driver != m_info_driver || ui_globals::curdats_view != m_info_view) + if (system->driver != m_info_driver || ui_globals::curdats_view != m_info_view) { m_info_buffer.clear(); - if (driver == m_info_driver) + if (system->driver == m_info_driver) { m_info_view = ui_globals::curdats_view; } else { - m_info_driver = driver; + m_info_driver = system->driver; m_info_view = 0; ui_globals::curdats_view = 0; m_items_list.clear(); - mame_machine_manager::instance()->lua()->call_plugin("data_list", driver->name, m_items_list); + mame_machine_manager::instance()->lua()->call_plugin("data_list", system->driver->name, m_items_list); ui_globals::curdats_total = m_items_list.size() + 1; } if (m_info_view == 0) - general_info(driver, m_info_buffer); + { + general_info(*system, m_info_buffer); + } else { m_info_buffer = ""; @@ -2731,13 +2710,13 @@ void menu_select_launch::infos_render(float origx1, float origy1, float origx2, float const ud_arrow_width = line_height * aspect; float oy1 = origy1 + line_height; - std::string_view const snaptext(m_info_view ? std::string_view(m_items_list[m_info_view - 1]) : std::string_view(_(first))); + std::string_view const snaptext(m_info_view ? std::string_view(m_items_list[m_info_view - 1]) : std::string_view(_("selmenu-artwork", first))); // get width of widest title float title_size(0.0f); for (std::size_t x = 0; total > x; ++x) { - std::string_view const name(x ? std::string_view(m_items_list[x - 1]) : std::string_view(_(first))); + std::string_view const name(x ? std::string_view(m_items_list[x - 1]) : std::string_view(_("selmenu-artwork", first))); float txt_length(0.0f); ui().draw_text_full( container(), name, diff --git a/src/frontend/mame/ui/selmenu.h b/src/frontend/mame/ui/selmenu.h index 251df450e72..040c4e991fb 100644 --- a/src/frontend/mame/ui/selmenu.h +++ b/src/frontend/mame/ui/selmenu.h @@ -20,12 +20,13 @@ #include <map> #include <memory> -#include <mutex> #include <vector> +struct ui_system_info; struct ui_software_info; + namespace ui { class machine_static_info; @@ -220,8 +221,16 @@ private: texture_ptr_vector m_toolbar_texture; texture_ptr_vector m_sw_toolbar_texture; }; - using cache_ptr = std::shared_ptr<cache>; - using cache_ptr_map = std::map<running_machine *, cache_ptr>; + + // this is to satisfy the std::any requirement that objects be copyable + class cache_wrapper : public cache + { + public: + cache_wrapper(running_machine &machine) : cache(machine), m_machine(machine) { } + cache_wrapper(cache_wrapper const &that) : cache(that.m_machine), m_machine(that.m_machine) { } + private: + running_machine &m_machine; + }; using flags_cache = util::lru_cache_map<game_driver const *, system_flags>; @@ -229,7 +238,7 @@ private: bool mouse_pressed() const { return (osd_ticks() >= m_repeat); } void set_pressed(); - bool snapx_valid() const { return m_cache->snapx_bitmap().valid(); } + bool snapx_valid() const { return m_cache.snapx_bitmap().valid(); } // draw left panel virtual float draw_left_panel(float x1, float y1, float x2, float y2) = 0; @@ -237,10 +246,10 @@ private: // draw infos void infos_render(float x1, float y1, float x2, float y2); - virtual void general_info(const game_driver *driver, std::string &buffer) = 0; + virtual void general_info(ui_system_info const &system, std::string &buffer) = 0; // get selected software and/or driver - virtual void get_selection(ui_software_info const *&software, game_driver const *&driver) const = 0; + virtual void get_selection(ui_software_info const *&software, ui_system_info const *&system) const = 0; virtual bool accept_search() const { return true; } void select_prev() { @@ -293,7 +302,6 @@ private: // text for main top/bottom panels virtual void make_topbox_text(std::string &line0, std::string &line1, std::string &line2) const = 0; - virtual std::string make_driver_description(game_driver const &driver) const = 0; virtual std::string make_software_description(ui_software_info const &software) const = 0; // filter navigation @@ -305,9 +313,6 @@ private: static bool has_multiple_bios(ui_software_info const &swinfo, s_bios &biosname); static bool has_multiple_bios(game_driver const &driver, s_bios &biosname); - // cleanup function - static void exit(running_machine &machine); - bool m_ui_error; std::string m_error_text; @@ -317,7 +322,7 @@ private: std::vector<std::string> m_items_list; std::string m_info_buffer; - cache_ptr m_cache; + cache &m_cache; bool m_is_swlist; focused_menu m_focus; bool m_pressed; // mouse button held down @@ -330,9 +335,6 @@ private: bool m_default_image; uint8_t m_image_view; flags_cache m_flags; - - static std::mutex s_cache_guard; - static cache_ptr_map s_caches; }; } // namespace ui diff --git a/src/frontend/mame/ui/selsoft.cpp b/src/frontend/mame/ui/selsoft.cpp index e00a1618016..8e48cbd51cb 100644 --- a/src/frontend/mame/ui/selsoft.cpp +++ b/src/frontend/mame/ui/selsoft.cpp @@ -29,26 +29,329 @@ #include <algorithm> #include <iterator> #include <functional> +#include <thread> #include <locale> namespace ui { -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) +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))); + } + + 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; +}; -void menu_select_software::search_item::set_penalty(std::u32string const &search) + + +class menu_select_software::machine_data { - // 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)); -} +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() + { + // add start empty item + m_swinfo.emplace_back(menu.m_driver); + + machine_config config(menu.m_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_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::collate<wchar_t> const &coll = std::use_facet<std::collate<wchar_t>>(std::locale()); + 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.longname); + } + else if (clonex && !cloney) + { + if ((a.parentname == b.shortname) && (a.instance == b.instance)) + return false; + else + return compare_names(a.longname, 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) + 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_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(); + } + } + + ~machine_data() + { + if (m_search_thread) + m_search_thread->join(); + } + + 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 + { + auto const found(m_filters.find(m_filter_type)); + return (m_filters.end() != found) ? found->second.get() : nullptr; + } + + software_filter &get_filter(software_filter::type type) + { + 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; + } + + std::vector<ui_software_info> const &swinfo() const noexcept { return m_swinfo; } + + std::vector<search_item> const &find_matches(std::string const &search) + { + // 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; + } + +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; + + std::unique_ptr<std::thread> m_search_thread; +}; //------------------------------------------------- @@ -56,23 +359,35 @@ void menu_select_software::search_item::set_penalty(std::u32string const &search //------------------------------------------------- menu_select_software::menu_select_software(mame_ui_manager &mui, render_container &container, game_driver const &driver) + : menu_select_software(mui, container, driver.type.fullname(), driver) +{ +} + +menu_select_software::menu_select_software(mame_ui_manager &mui, render_container &container, ui_system_info const &system) + : menu_select_software(mui, container, system.description.c_str(), *system.driver) +{ +} + +menu_select_software::menu_select_software( + mame_ui_manager &mui, + render_container &container, + char const *description, + game_driver const &driver) : menu_select_launch(mui, container, true) , m_icon_paths() - , m_icons(MAX_ICONS_RENDER) + , m_description(description) , m_driver(driver) - , m_has_empty_start(false) - , m_filter_data() - , m_filters() - , m_filter_type(software_filter::ALL) - , m_swinfo() - , m_searchlist() , 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)[&driver]); + if (!cached) + 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; @@ -204,7 +519,7 @@ void menu_select_software::handle() void menu_select_software::populate(float &customtop, float &custombottom) { - 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; @@ -212,37 +527,37 @@ void menu_select_software::populate(float &customtop, float &custombottom) // 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]); + if (m_data->has_empty_start()) + item_append("[Start empty]", flags_ui, (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); } } @@ -254,7 +569,7 @@ 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 = m_data->has_empty_start() ? curitem + 1 : curitem; item_append( m_displaylist[curitem].get().longname, m_displaylist[curitem].get().devicetype, @@ -276,191 +591,6 @@ void menu_select_software::populate(float &customtop, float &custombottom) 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()); - - // 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()); - 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, 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 = 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::collate<wchar_t> const &coll = std::use_facet<std::collate<wchar_t>>(std::locale()); - 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.longname); - } - else if (clonex && !cloney) - { - if ((a.parentname == b.shortname) && (a.instance == b.instance)) - return false; - else - return compare_names(a.longname, 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); - } - }); - - - m_filter_data.finalise(); -} - //------------------------------------------------- // handle select key event @@ -512,54 +642,12 @@ void menu_select_software::inkey_select(const event *menu_event) //------------------------------------------------- -// 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))) - { - 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) { - 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>(m_data->filter_type(), m_data->filters(), x1, y1, x2, y2); } @@ -575,17 +663,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 { @@ -617,36 +705,29 @@ 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 = 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_data->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_data->swinfo().size() - 1); + line1 = string_format(_("Driver: \"%1$s\" software list "), m_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 -{ - // first line is game description - return string_format(_("%1$-.100s"), driver.type.fullname()); -} - - std::string menu_select_software::make_software_description(ui_software_info const &software) const { // first line is long name @@ -658,10 +739,7 @@ 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( + m_data->get_filter(software_filter::type(m_filter_highlight)).show_ui( ui(), container(), [this] (software_filter &filter) @@ -676,7 +754,7 @@ void menu_select_software::filter_selected() file.close(); } } - m_filter_type = new_type; + m_data->set_filter_type(new_type); reset(reset_options::SELECT_FIRST); }); } diff --git a/src/frontend/mame/ui/selsoft.h b/src/frontend/mame/ui/selsoft.h index 1bfdd9c1e48..ebf3e77df89 100644 --- a/src/frontend/mame/ui/selsoft.h +++ b/src/frontend/mame/ui/selsoft.h @@ -15,7 +15,10 @@ #include "ui/selmenu.h" #include "ui/utils.h" +#include "lrucache.h" + #include <map> +#include <memory> #include <string> #include <vector> @@ -27,26 +30,17 @@ class menu_select_software : public menu_select_launch { public: menu_select_software(mame_ui_manager &mui, render_container &container, game_driver const &driver); + menu_select_software(mame_ui_manager &mui, render_container &container, ui_system_info const &system); virtual ~menu_select_software() override; private: using filter_map = std::map<software_filter::type, software_filter::ptr>; using icon_cache = texture_lru<ui_software_info const *>; - struct search_item - { - search_item(ui_software_info const &s); - search_item(search_item const &) = default; - search_item(search_item &&) = default; - search_item &operator=(search_item const &) = default; - search_item &operator=(search_item &&) = default; - void set_penalty(std::u32string const &search); - - std::reference_wrapper<ui_software_info const> software; - std::u32string ucs_shortname; - std::u32string ucs_longname; - double penalty; - }; + struct search_item; + class machine_data; + + menu_select_software(mame_ui_manager &mui, render_container &container, char const *description, game_driver const &driver); virtual void populate(float &customtop, float &custombottom) override; virtual void handle() override; @@ -56,11 +50,10 @@ private: virtual render_texture *get_icon_texture(int linenum, void *selectedref) override; // get selected software and/or driver - virtual void get_selection(ui_software_info const *&software, game_driver const *&driver) const override; + virtual void get_selection(ui_software_info const *&software, ui_system_info const *&system) const override; // text for main top/bottom panels virtual void make_topbox_text(std::string &line0, std::string &line1, std::string &line2) const override; - virtual std::string make_driver_description(game_driver const &driver) const override; virtual std::string make_software_description(ui_software_info const &software) const override; // filter navigation @@ -69,25 +62,16 @@ private: // toolbar virtual void inkey_export() override { throw false; } - void build_software_list(); - void find_matches(); - void load_sw_custom_filters(); - // handlers void inkey_select(const event *menu_event); - virtual void general_info(const game_driver *driver, std::string &buffer) override { } + virtual void general_info(ui_system_info const &system, std::string &buffer) override { } std::map<std::string, std::string> m_icon_paths; - icon_cache m_icons; + char const *const m_description; game_driver const &m_driver; - bool m_has_empty_start; - software_filter_data m_filter_data; - filter_map m_filters; - software_filter::type m_filter_type; + std::shared_ptr<machine_data> m_data; - std::vector<ui_software_info> m_swinfo; - std::vector<search_item> m_searchlist; std::vector<std::reference_wrapper<ui_software_info const> > m_displaylist; }; diff --git a/src/frontend/mame/ui/submenu.cpp b/src/frontend/mame/ui/submenu.cpp index d93b7a8af6a..5e6e0cc4152 100644 --- a/src/frontend/mame/ui/submenu.cpp +++ b/src/frontend/mame/ui/submenu.cpp @@ -28,111 +28,111 @@ namespace ui { std::vector<submenu::option> submenu::misc_options() { return std::vector<option>{ - { option_type::HEAD, __("Miscellaneous Options") }, - { option_type::UI, __("Skip imperfect emulation warnings"), OPTION_SKIP_WARNINGS }, - { option_type::UI, __("Re-select last machine launched"), OPTION_REMEMBER_LAST }, - { option_type::UI, __("Enlarge images in the right panel"), OPTION_ENLARGE_SNAPS }, - { option_type::EMU, __("Cheats"), OPTION_CHEAT }, - { option_type::EMU, __("Show mouse pointer"), OPTION_UI_MOUSE }, - { option_type::EMU, __("Confirm quit from machines"), OPTION_CONFIRM_QUIT }, - { option_type::EMU, __("Skip information screen at startup"), OPTION_SKIP_GAMEINFO }, - { option_type::UI, __("Force 4:3 aspect for snapshot display"), OPTION_FORCED4X3 }, - { option_type::UI, __("Use image as background"), OPTION_USE_BACKGROUND }, - { option_type::UI, __("Skip BIOS selection menu"), OPTION_SKIP_BIOS_MENU }, - { option_type::UI, __("Skip software parts selection menu"), OPTION_SKIP_PARTS_MENU }, - { option_type::UI, __("Info auto audit"), OPTION_INFO_AUTO_AUDIT }, - { option_type::UI, __("Hide romless machine from available list"),OPTION_HIDE_ROMLESS } }; + { option_type::HEAD, N_("Miscellaneous Options") }, + { option_type::UI, N_("Skip imperfect emulation warnings"), OPTION_SKIP_WARNINGS }, + { option_type::UI, N_("Re-select last machine launched"), OPTION_REMEMBER_LAST }, + { option_type::UI, N_("Enlarge images in the right panel"), OPTION_ENLARGE_SNAPS }, + { option_type::EMU, N_("Cheats"), OPTION_CHEAT }, + { option_type::EMU, N_("Show mouse pointer"), OPTION_UI_MOUSE }, + { option_type::EMU, N_("Confirm quit from machines"), OPTION_CONFIRM_QUIT }, + { option_type::EMU, N_("Skip information screen at startup"), OPTION_SKIP_GAMEINFO }, + { option_type::UI, N_("Force 4:3 aspect for snapshot display"), OPTION_FORCED4X3 }, + { option_type::UI, N_("Use image as background"), OPTION_USE_BACKGROUND }, + { option_type::UI, N_("Skip BIOS selection menu"), OPTION_SKIP_BIOS_MENU }, + { option_type::UI, N_("Skip software parts selection menu"), OPTION_SKIP_PARTS_MENU }, + { option_type::UI, N_("Info auto audit"), OPTION_INFO_AUTO_AUDIT }, + { option_type::UI, N_("Hide romless machine from available list"),OPTION_HIDE_ROMLESS } }; } std::vector<submenu::option> submenu::advanced_options() { return std::vector<option>{ - { option_type::HEAD, __("Advanced Options") }, - { option_type::HEAD, __("Performance Options") }, - { option_type::EMU, __("Auto frame skip"), OPTION_AUTOFRAMESKIP }, - { option_type::EMU, __("Frame skip"), OPTION_FRAMESKIP }, - { option_type::EMU, __("Throttle"), OPTION_THROTTLE }, - { option_type::UI, __("Mute when unthrottled"), OPTION_UNTHROTTLE_MUTE }, - { option_type::EMU, __("Sleep"), OPTION_SLEEP }, - { option_type::EMU, __("Speed"), OPTION_SPEED }, - { option_type::EMU, __("Adjust speed to match refresh rate"), OPTION_REFRESHSPEED }, - { option_type::EMU, __("Low latency"), OPTION_LOWLATENCY }, - - { option_type::HEAD, __("Rotation Options") }, - { option_type::EMU, __("Rotate"), OPTION_ROTATE }, - { option_type::EMU, __("Rotate right"), OPTION_ROR }, - { option_type::EMU, __("Rotate left"), OPTION_ROL }, - { option_type::EMU, __("Auto rotate right"), OPTION_AUTOROR }, - { option_type::EMU, __("Auto rotate left"), OPTION_AUTOROL }, - { option_type::EMU, __("Flip X"), OPTION_FLIPX }, - { option_type::EMU, __("Flip Y"), OPTION_FLIPY }, - - { option_type::HEAD, __("Artwork Options") }, - { option_type::EMU, __("Zoom to screen area"), OPTION_ARTWORK_CROP }, - - { option_type::HEAD, __("State/Playback Options") }, - { option_type::EMU, __("Automatic save/restore"), OPTION_AUTOSAVE }, - { option_type::EMU, __("Allow rewind"), OPTION_REWIND }, - { option_type::EMU, __("Rewind capacity"), OPTION_REWIND_CAPACITY }, - { option_type::EMU, __("Bilinear filtering for snapshots"), OPTION_SNAPBILINEAR }, - { option_type::EMU, __("Burn-in"), OPTION_BURNIN }, - - { option_type::HEAD, __("Input Options") }, - { option_type::EMU, __("Coin lockout"), OPTION_COIN_LOCKOUT }, - { option_type::EMU, __("Mouse"), OPTION_MOUSE }, - { option_type::EMU, __("Joystick"), OPTION_JOYSTICK }, - { option_type::EMU, __("Lightgun"), OPTION_LIGHTGUN }, - { option_type::EMU, __("Multi-keyboard"), OPTION_MULTIKEYBOARD }, - { option_type::EMU, __("Multi-mouse"), OPTION_MULTIMOUSE }, - { option_type::EMU, __("Steadykey"), OPTION_STEADYKEY }, - { option_type::EMU, __("UI active"), OPTION_UI_ACTIVE }, - { option_type::EMU, __("Offscreen reload"), OPTION_OFFSCREEN_RELOAD }, - { option_type::EMU, __("Joystick deadzone"), OPTION_JOYSTICK_DEADZONE }, - { option_type::EMU, __("Joystick saturation"), OPTION_JOYSTICK_SATURATION }, - { option_type::EMU, __("Natural keyboard"), OPTION_NATURAL_KEYBOARD }, - { option_type::EMU, __("Simultaneous contradictory"), OPTION_JOYSTICK_CONTRADICTORY }, - { option_type::EMU, __("Coin impulse"), OPTION_COIN_IMPULSE } }; + { option_type::HEAD, N_("Advanced Options") }, + { option_type::HEAD, N_("Performance Options") }, + { option_type::EMU, N_("Auto frame skip"), OPTION_AUTOFRAMESKIP }, + { option_type::EMU, N_("Frame skip"), OPTION_FRAMESKIP }, + { option_type::EMU, N_("Throttle"), OPTION_THROTTLE }, + { option_type::UI, N_("Mute when unthrottled"), OPTION_UNTHROTTLE_MUTE }, + { option_type::EMU, N_("Sleep"), OPTION_SLEEP }, + { option_type::EMU, N_("Speed"), OPTION_SPEED }, + { option_type::EMU, N_("Adjust speed to match refresh rate"), OPTION_REFRESHSPEED }, + { option_type::EMU, N_("Low latency"), OPTION_LOWLATENCY }, + + { option_type::HEAD, N_("Rotation Options") }, + { option_type::EMU, N_("Rotate"), OPTION_ROTATE }, + { option_type::EMU, N_("Rotate right"), OPTION_ROR }, + { option_type::EMU, N_("Rotate left"), OPTION_ROL }, + { option_type::EMU, N_("Auto rotate right"), OPTION_AUTOROR }, + { option_type::EMU, N_("Auto rotate left"), OPTION_AUTOROL }, + { option_type::EMU, N_("Flip X"), OPTION_FLIPX }, + { option_type::EMU, N_("Flip Y"), OPTION_FLIPY }, + + { option_type::HEAD, N_("Artwork Options") }, + { option_type::EMU, N_("Zoom to screen area"), OPTION_ARTWORK_CROP }, + + { option_type::HEAD, N_("State/Playback Options") }, + { option_type::EMU, N_("Automatic save/restore"), OPTION_AUTOSAVE }, + { option_type::EMU, N_("Allow rewind"), OPTION_REWIND }, + { option_type::EMU, N_("Rewind capacity"), OPTION_REWIND_CAPACITY }, + { option_type::EMU, N_("Bilinear filtering for snapshots"), OPTION_SNAPBILINEAR }, + { option_type::EMU, N_("Burn-in"), OPTION_BURNIN }, + + { option_type::HEAD, N_("Input Options") }, + { option_type::EMU, N_("Coin lockout"), OPTION_COIN_LOCKOUT }, + { option_type::EMU, N_("Mouse"), OPTION_MOUSE }, + { option_type::EMU, N_("Joystick"), OPTION_JOYSTICK }, + { option_type::EMU, N_("Lightgun"), OPTION_LIGHTGUN }, + { option_type::EMU, N_("Multi-keyboard"), OPTION_MULTIKEYBOARD }, + { option_type::EMU, N_("Multi-mouse"), OPTION_MULTIMOUSE }, + { option_type::EMU, N_("Steadykey"), OPTION_STEADYKEY }, + { option_type::EMU, N_("UI active"), OPTION_UI_ACTIVE }, + { option_type::EMU, N_("Offscreen reload"), OPTION_OFFSCREEN_RELOAD }, + { option_type::EMU, N_("Joystick deadzone"), OPTION_JOYSTICK_DEADZONE }, + { option_type::EMU, N_("Joystick saturation"), OPTION_JOYSTICK_SATURATION }, + { option_type::EMU, N_("Natural keyboard"), OPTION_NATURAL_KEYBOARD }, + { option_type::EMU, N_("Simultaneous contradictory"), OPTION_JOYSTICK_CONTRADICTORY }, + { option_type::EMU, N_("Coin impulse"), OPTION_COIN_IMPULSE } }; } std::vector<submenu::option> submenu::control_options() { return std::vector<option>{ - { option_type::HEAD, __("Device Mapping") }, - { option_type::EMU, __("Lightgun Device Assignment"), OPTION_LIGHTGUN_DEVICE }, - { option_type::EMU, __("Trackball Device Assignment"), OPTION_TRACKBALL_DEVICE }, - { option_type::EMU, __("Pedal Device Assignment"), OPTION_PEDAL_DEVICE }, - { option_type::EMU, __("Adstick Device Assignment"), OPTION_ADSTICK_DEVICE }, - { option_type::EMU, __("Paddle Device Assignment"), OPTION_PADDLE_DEVICE }, - { option_type::EMU, __("Dial Device Assignment"), OPTION_DIAL_DEVICE }, - { option_type::EMU, __("Positional Device Assignment"), OPTION_POSITIONAL_DEVICE }, - { option_type::EMU, __("Mouse Device Assignment"), OPTION_MOUSE_DEVICE } }; + { option_type::HEAD, N_("Device Mapping") }, + { option_type::EMU, N_("Lightgun Device Assignment"), OPTION_LIGHTGUN_DEVICE }, + { option_type::EMU, N_("Trackball Device Assignment"), OPTION_TRACKBALL_DEVICE }, + { option_type::EMU, N_("Pedal Device Assignment"), OPTION_PEDAL_DEVICE }, + { option_type::EMU, N_("Adstick Device Assignment"), OPTION_ADSTICK_DEVICE }, + { option_type::EMU, N_("Paddle Device Assignment"), OPTION_PADDLE_DEVICE }, + { option_type::EMU, N_("Dial Device Assignment"), OPTION_DIAL_DEVICE }, + { option_type::EMU, N_("Positional Device Assignment"), OPTION_POSITIONAL_DEVICE }, + { option_type::EMU, N_("Mouse Device Assignment"), OPTION_MOUSE_DEVICE } }; } std::vector<submenu::option> submenu::video_options() { return std::vector<option>{ - { option_type::HEAD, __("Video Options") }, - { option_type::OSD, __("Video Mode"), OSDOPTION_VIDEO }, - { option_type::OSD, __("Number Of Screens"), OSDOPTION_NUMSCREENS }, + { option_type::HEAD, N_("Video Options") }, + { option_type::OSD, N_("Video Mode"), OSDOPTION_VIDEO }, + { option_type::OSD, N_("Number Of Screens"), OSDOPTION_NUMSCREENS }, #if defined(UI_WINDOWS) && !defined(UI_SDL) - { option_type::OSD, __("Triple Buffering"), WINOPTION_TRIPLEBUFFER }, - { option_type::OSD, __("HLSL"), WINOPTION_HLSL_ENABLE }, + { option_type::OSD, N_("Triple Buffering"), WINOPTION_TRIPLEBUFFER }, + { option_type::OSD, N_("HLSL"), WINOPTION_HLSL_ENABLE }, #endif - { option_type::OSD, __("GLSL"), OSDOPTION_GL_GLSL }, - { option_type::OSD, __("Bilinear Filtering"), OSDOPTION_FILTER }, - { option_type::OSD, __("Bitmap Prescaling"), OSDOPTION_PRESCALE }, - { option_type::OSD, __("Window Mode"), OSDOPTION_WINDOW }, - { option_type::EMU, __("Enforce Aspect Ratio"), OPTION_KEEPASPECT }, - { option_type::OSD, __("Start Out Maximized"), OSDOPTION_MAXIMIZE }, - { option_type::OSD, __("Synchronized Refresh"), OSDOPTION_SYNCREFRESH }, - { option_type::OSD, __("Wait Vertical Sync"), OSDOPTION_WAITVSYNC } }; + { option_type::OSD, N_("GLSL"), OSDOPTION_GL_GLSL }, + { option_type::OSD, N_("Bilinear Filtering"), OSDOPTION_FILTER }, + { option_type::OSD, N_("Bitmap Prescaling"), OSDOPTION_PRESCALE }, + { option_type::OSD, N_("Window Mode"), OSDOPTION_WINDOW }, + { option_type::EMU, N_("Enforce Aspect Ratio"), OPTION_KEEPASPECT }, + { option_type::OSD, N_("Start Out Maximized"), OSDOPTION_MAXIMIZE }, + { option_type::OSD, N_("Synchronized Refresh"), OSDOPTION_SYNCREFRESH }, + { option_type::OSD, N_("Wait Vertical Sync"), OSDOPTION_WAITVSYNC } }; } //std::vector<submenu::option> submenu::export_options() //{ // return std::vector<option>{ -// { option_type::COMMAND, __("Export XML format (like -listxml)"), "exportxml" }, -// { option_type::COMMAND, __("Export TXT format (like -listfull)"), "exporttxt" } }; +// { option_type::COMMAND, N_("Export XML format (like -listxml)"), "exportxml" }, +// { option_type::COMMAND, N_("Export TXT format (like -listfull)"), "exporttxt" } }; //} diff --git a/src/frontend/mame/ui/ui.cpp b/src/frontend/mame/ui/ui.cpp index 1f8e5b7bda8..2e5f999da08 100644 --- a/src/frontend/mame/ui/ui.cpp +++ b/src/frontend/mame/ui/ui.cpp @@ -251,6 +251,9 @@ void mame_ui_manager::exit() // free the font m_font.reset(); + + // free persistent data for other classes + m_session_data.clear(); } diff --git a/src/frontend/mame/ui/ui.h b/src/frontend/mame/ui/ui.h index e9df57b3b2d..633d3c476f4 100644 --- a/src/frontend/mame/ui/ui.h +++ b/src/frontend/mame/ui/ui.h @@ -21,10 +21,15 @@ #include "ui/slider.h" #include "ui/text.h" +#include <any> +#include <cassert> #include <ctime> #include <functional> +#include <map> #include <set> #include <string_view> +#include <typeindex> +#include <typeinfo> #include <utility> #include <vector> @@ -213,9 +218,22 @@ public: virtual void menu_reset() override; + template <typename Owner, typename Data, typename... Param> + Data &get_session_data(Param &&... args) + { + auto const ins(m_session_data.try_emplace(typeid(Owner))); + assert(!ins.first->second.has_value() == ins.second); + if (ins.second) + return ins.first->second.emplace<Data>(std::forward<Param>(args)...); + Data *const result(std::any_cast<Data>(&ins.first->second)); + assert(result); + return *result; + } + private: using handler_callback_func = std::function<uint32_t (render_container &)>; using device_feature_set = std::set<std::pair<std::string, std::string> >; + using session_data_map = std::map<std::type_index, std::any>; // instance variables std::unique_ptr<render_font> m_font; @@ -243,6 +261,8 @@ private: std::time_t m_last_launch_time; std::time_t m_last_warning_time; + session_data_map m_session_data; + // static variables static std::string messagebox_text; static std::string messagebox_poptext; @@ -284,10 +304,10 @@ private: int32_t slider_beam_dot_size(screen_device &screen, std::string *str, int32_t newval); int32_t slider_beam_intensity_weight(screen_device &screen, std::string *str, int32_t newval); std::string slider_get_screen_desc(screen_device &screen); - #ifdef MAME_DEBUG +#ifdef MAME_DEBUG int32_t slider_crossscale(ioport_field &field, std::string *str, int32_t newval); int32_t slider_crossoffset(ioport_field &field, std::string *str, int32_t newval); - #endif +#endif std::vector<std::unique_ptr<slider_state>> m_sliders; }; diff --git a/src/frontend/mame/ui/utils.cpp b/src/frontend/mame/ui/utils.cpp index b18b3aa22af..7da3abb7111 100644 --- a/src/frontend/mame/ui/utils.cpp +++ b/src/frontend/mame/ui/utils.cpp @@ -30,6 +30,7 @@ #include <cstring> #include <iterator> #include <unordered_set> +#include <utility> namespace ui { @@ -57,46 +58,87 @@ constexpr char const *SOFTWARE_REGIONS[] = { "tha", "tpe", "tw", "uk", "ukr", "usa" }; +// must be sorted in std::string comparison order +constexpr std::pair<char const *, char const *> SOFTWARE_INFO_NAMES[] = { + { "alt_title", N_p("swlist-info", "Alternate Title") }, + { "author", N_p("swlist-info", "Author") }, + { "barcode", N_p("swlist-info", "Barcode Number") }, + { "developer", N_p("swlist-info", "Developer") }, + { "distributor", N_p("swlist-info", "Distributor") }, + { "install", N_p("swlist-info", "Installation Instructions") }, + { "isbn", N_p("swlist-info", "ISBN") }, + { "oem", N_p("swlist-info", "OEM") }, + { "original_publisher", N_p("swlist-info", "Original Publisher") }, + { "partno", N_p("swlist-info", "Part Number") }, + { "pcb", N_p("swlist-info", "PCB") }, + { "programmer", N_p("swlist-info", "Programmer") }, + { "release", N_p("swlist-info", "Release Date") }, + { "serial", N_p("swlist-info", "Serial Number") }, + { "usage", N_p("swlist-info", "Usage Instructions") }, + { "version", N_p("swlist-info", "Version") } }; + + + +// must be in sync with the machine_filter::type enum constexpr char const *MACHINE_FILTER_NAMES[machine_filter::COUNT] = { - __("Unfiltered"), - __("Available"), - __("Unavailable"), - __("Working"), - __("Not Working"), - __("Mechanical"), - __("Not Mechanical"), - __("Category"), - __("Favorites"), - __("BIOS"), - __("Not BIOS"), - __("Parents"), - __("Clones"), - __("Manufacturer"), - __("Year"), - __("Save Supported"), - __("Save Unsupported"), - __("CHD Required"), - __("No CHD Required"), - __("Vertical Screen"), - __("Horizontal Screen"), - __("Custom Filter") }; - + N_p("machine-filter", "Unfiltered"), + N_p("machine-filter", "Available"), + N_p("machine-filter", "Unavailable"), + N_p("machine-filter", "Working"), + N_p("machine-filter", "Not Working"), + N_p("machine-filter", "Mechanical"), + N_p("machine-filter", "Not Mechanical"), + N_p("machine-filter", "Category"), + N_p("machine-filter", "Favorites"), + N_p("machine-filter", "BIOS"), + N_p("machine-filter", "Not BIOS"), + N_p("machine-filter", "Parents"), + N_p("machine-filter", "Clones"), + N_p("machine-filter", "Manufacturer"), + N_p("machine-filter", "Year"), + N_p("machine-filter", "Save Supported"), + N_p("machine-filter", "Save Unsupported"), + N_p("machine-filter", "CHD Required"), + N_p("machine-filter", "No CHD Required"), + N_p("machine-filter", "Vertical Screen"), + N_p("machine-filter", "Horizontal Screen"), + N_p("machine-filter", "Custom Filter") }; + +// must be in sync with the software_filter::type enum constexpr char const *SOFTWARE_FILTER_NAMES[software_filter::COUNT] = { - __("Unfiltered"), - __("Available"), - __("Unavailable"), - __("Favorites"), - __("Parents"), - __("Clones"), - __("Year"), - __("Publisher"), - __("Supported"), - __("Partially Supported"), - __("Unsupported"), - __("Release Region"), - __("Device Type"), - __("Software List"), - __("Custom Filter") }; + N_p("software-filter", "Unfiltered"), + N_p("software-filter", "Available"), + N_p("software-filter", "Unavailable"), + N_p("software-filter", "Favorites"), + N_p("software-filter", "Parents"), + N_p("software-filter", "Clones"), + N_p("software-filter", "Year"), + N_p("software-filter", "Publisher"), + N_p("software-filter", "Developer"), + N_p("software-filter", "Distributor"), + N_p("software-filter", "Author"), + N_p("software-filter", "Programmer"), + N_p("software-filter", "Supported"), + N_p("software-filter", "Partially Supported"), + N_p("software-filter", "Unsupported"), + N_p("software-filter", "Release Region"), + N_p("software-filter", "Device Type"), + N_p("software-filter", "Software List"), + N_p("software-filter", "Custom Filter") }; + + + +//------------------------------------------------- +// helper for building a sorted vector +//------------------------------------------------- + +template <typename T> +void add_info_value(std::vector<std::string> &items, T &&value) +{ + std::vector<std::string>::iterator const pos(std::lower_bound(items.begin(), items.end(), value)); + if ((items.end() == pos) || (*pos != value)) + items.emplace(pos, std::forward<T>(value)); +} @@ -213,11 +255,14 @@ protected: , m_selection(0U) { if (value) - { - std::vector<std::string>::const_iterator const found(std::find(choices.begin(), choices.end(), value)); - if (choices.end() != found) - m_selection = std::distance(choices.begin(), found); - } + set_value(value); + } + + void set_value(char const *value) + { + auto const found(std::find(m_choices.begin(), m_choices.end(), value)); + if (m_choices.end() != found) + m_selection = std::distance(m_choices.begin(), found); } bool have_choices() const { return !m_choices.empty(); } @@ -1395,6 +1440,93 @@ private: //------------------------------------------------- +// software info filters +//------------------------------------------------- + +template <software_filter::type Type> +class software_info_filter_base : public choice_filter_impl_base<software_filter, Type> +{ +public: + virtual bool apply(ui_software_info const &info) const override + { + if (!this->have_choices()) + { + return true; + } + else if (!this->selection_valid()) + { + return false; + } + else + { + auto const found( + std::find_if( + info.info.begin(), + info.info.end(), + [this] (software_info_item const &i) { return this->apply(i); })); + return info.info.end() != found; + } + } + +protected: + software_info_filter_base(char const *type, std::vector<std::string> const &choices, char const *value) + : choice_filter_impl_base<software_filter, Type>(choices, value) + , m_info_type(type) + { + } + +private: + bool apply(software_info_item const &info) const + { + return (info.name() == m_info_type) && (info.value() == this->selection_text()); + } + + char const *const m_info_type; +}; + + +class developer_software_filter : public software_info_filter_base<software_filter::DEVELOPERS> +{ +public: + developer_software_filter(software_filter_data const &data, char const *value, emu_file *file, unsigned indent) + : software_info_filter_base<software_filter::DEVELOPERS>("developer", data.developers(), value) + { + } +}; + + +class distributor_software_filter : public software_info_filter_base<software_filter::DISTRIBUTORS> +{ +public: + distributor_software_filter(software_filter_data const &data, char const *value, emu_file *file, unsigned indent) + : software_info_filter_base<software_filter::DISTRIBUTORS>("distributor", data.distributors(), value) + { + } +}; + + +class author_software_filter : public software_info_filter_base<software_filter::AUTHORS> +{ +public: + author_software_filter(software_filter_data const &data, char const *value, emu_file *file, unsigned indent) + : software_info_filter_base<software_filter::AUTHORS>("author", data.authors(), value) + { + } +}; + + +class programmer_software_filter : public software_info_filter_base<software_filter::PROGRAMMERS> +{ +public: + programmer_software_filter(software_filter_data const &data, char const *value, emu_file *file, unsigned indent) + : software_info_filter_base<software_filter::PROGRAMMERS>("programmer", data.programmers(), value) + { + } +}; + + + +//------------------------------------------------- // composite software filter //------------------------------------------------- @@ -1432,6 +1564,10 @@ public: case FAVORITE: case YEAR: case PUBLISHERS: + case DEVELOPERS: + case DISTRIBUTORS: + case AUTHORS: + case PROGRAMMERS: case REGION: case DEVICE_TYPE: case LIST: @@ -1444,7 +1580,15 @@ public: static bool is_inclusion(type n) { - return (YEAR == n) || (PUBLISHERS == n) || (REGION == n) || (DEVICE_TYPE == n) || (LIST == n); + return (YEAR == n) + || (PUBLISHERS == n) + || (DEVELOPERS == n) + || (DISTRIBUTORS == n) + || (AUTHORS == n) + || (PROGRAMMERS == n) + || (REGION == n) + || (DEVICE_TYPE == n) + || (LIST == n); } private: @@ -1542,31 +1686,35 @@ bool machine_filter_data::load_ini(emu_file &file) void software_filter_data::add_region(std::string const &longname) { std::string name(extract_region(longname)); - std::vector<std::string>::iterator const pos(std::lower_bound(m_regions.begin(), m_regions.end(), name)); - if ((m_regions.end() == pos) || (*pos != name)) - m_regions.emplace(pos, std::move(name)); + add_info_value(m_regions, std::move(name)); } void software_filter_data::add_publisher(std::string const &publisher) { std::string name(extract_publisher(publisher)); - std::vector<std::string>::iterator const pos(std::lower_bound(m_publishers.begin(), m_publishers.end(), name)); - if ((m_publishers.end() == pos) || (*pos != name)) - m_publishers.emplace(pos, std::move(name)); + add_info_value(m_publishers, std::move(name)); } void software_filter_data::add_year(std::string const &year) { - std::vector<std::string>::iterator const pos(std::lower_bound(m_years.begin(), m_years.end(), year)); - if ((m_years.end() == pos) || (*pos != year)) - m_years.emplace(pos, year); + add_info_value(m_years, year); +} + +void software_filter_data::add_info(software_info_item const &info) +{ + if (info.name() == "developer") + add_info_value(m_developers, info.value()); + else if (info.name() == "distributor") + add_info_value(m_distributors, info.value()); + else if (info.name() == "author") + add_info_value(m_authors, info.value()); + else if (info.name() == "programmer") + add_info_value(m_programmers, info.value()); } void software_filter_data::add_device_type(std::string const &device_type) { - std::vector<std::string>::iterator const pos(std::lower_bound(m_device_types.begin(), m_device_types.end(), device_type)); - if ((m_device_types.end() == pos) || (*pos != device_type)) - m_device_types.emplace(pos, device_type); + add_info_value(m_device_types, device_type); } void software_filter_data::add_list(std::string const &name, std::string const &description) @@ -1707,7 +1855,7 @@ char const *machine_filter::config_name(type n) char const *machine_filter::display_name(type n) { assert(COUNT > n); - return _(MACHINE_FILTER_NAMES[n]); + return _("machine-filter", MACHINE_FILTER_NAMES[n]); } machine_filter::machine_filter() @@ -1728,7 +1876,7 @@ char const *software_filter::config_name(type n) char const *software_filter::display_name(type n) { assert(COUNT > n); - return _(SOFTWARE_FILTER_NAMES[n]); + return _("software-filter", SOFTWARE_FILTER_NAMES[n]); } software_filter::software_filter() @@ -1756,6 +1904,14 @@ software_filter::ptr software_filter::create(type n, software_filter_data const return std::make_unique<years_software_filter>(data, value, file, indent); case PUBLISHERS: return std::make_unique<publishers_software_filter>(data, value, file, indent); + case DEVELOPERS: + return std::make_unique<developer_software_filter>(data, value, file, indent); + case DISTRIBUTORS: + return std::make_unique<distributor_software_filter>(data, value, file, indent); + case AUTHORS: + return std::make_unique<author_software_filter>(data, value, file, indent); + case PROGRAMMERS: + return std::make_unique<programmer_software_filter>(data, value, file, indent); case SUPPORTED: return std::make_unique<supported_software_filter>(data, value, file, indent); case PARTIAL_SUPPORTED: @@ -1866,31 +2022,52 @@ std::vector<std::string> tokenize(const std::string &text, char sep) ui_software_info::ui_software_info( - software_info const &info, + software_info const &sw, software_part const &p, game_driver const &d, std::string const &li, std::string const &is, std::string const &de) - : shortname(info.shortname()), longname(info.longname()), parentname(info.parentname()) - , year(info.year()), publisher(info.publisher()) - , supported(info.supported()) + : shortname(sw.shortname()), longname(sw.longname()), parentname(sw.parentname()) + , year(sw.year()), publisher(sw.publisher()) + , supported(sw.supported()) , part(p.name()) , driver(&d) , listname(li), interface(p.interface()), instance(is) , startempty(0) , parentlongname() - , usage() + , infotext() , devicetype(de) + , info() + , alttitles() , available(false) { - for (feature_list_item const &feature : info.other_info()) + info.reserve(sw.info().size()); + bool firstinfo(true); + for (software_info_item const &feature : sw.info()) { - if (feature.name() == "usage") - { - usage = feature.value(); - break; - } + // add info for the internal UI, localising recognised + if (!firstinfo) + infotext.append(2, '\n'); + firstinfo = false; + auto const found = std::lower_bound( + std::begin(ui::SOFTWARE_INFO_NAMES), + std::end(ui::SOFTWARE_INFO_NAMES), + feature.name().c_str(), + [] (std::pair<char const *, char const *> const &a, char const *b) + { + return 0 > std::strcmp(a.first, b); + }); + if ((std::end(ui::SOFTWARE_INFO_NAMES) != found) && (feature.name() == found->first)) + infotext.append(_("swlist-info", found->second)); + else + infotext.append(feature.name()); + infotext.append(1, '\n').append(feature.value()); + + // keep references to stuff for filtering and searching + auto const &ins = info.emplace_back(feature.name(), feature.value()); + if (feature.name() == "alt_title") + alttitles.emplace_back(ins.value()); } } @@ -1899,3 +2076,67 @@ ui_software_info::ui_software_info(game_driver const &d) : shortname(d.name), longname(d.type.fullname()), driver(&d), startempty(1), available(true) { } + +ui_software_info::ui_software_info(ui_software_info const &that) + : shortname(that.shortname) + , longname(that.longname) + , parentname(that.parentname) + , year(that.year) + , publisher(that.publisher) + , supported(that.supported) + , part(that.part) + , driver(that.driver) + , listname(that.listname) + , interface(that.interface) + , instance(that.instance) + , startempty(that.startempty) + , parentlongname(that.parentlongname) + , infotext(that.infotext) + , devicetype(that.devicetype) + , info(that.info) + , alttitles() + , available(that.available) +{ + // build self-referencing member + alttitles.reserve(that.alttitles.size()); + for (software_info_item const &feature : info) + { + if (feature.name() == "alt_title") + alttitles.emplace_back(feature.value()); + } +} + +ui_software_info &ui_software_info::operator=(ui_software_info const &that) +{ + if (&that != this) + { + // copy simple stuff + shortname = that.shortname; + longname = that.longname; + parentname = that.parentname; + year = that.year; + publisher = that.publisher; + supported = that.supported; + part = that.part; + driver = that.driver; + listname = that.listname; + interface = that.interface; + instance = that.instance; + startempty = that.startempty; + parentlongname = that.parentlongname; + infotext = that.infotext; + devicetype = that.devicetype; + info = that.info; + alttitles.clear(); + available = that.available; + + // build self-referencing member + alttitles.reserve(that.alttitles.size()); + for (software_info_item const &feature : info) + { + if (feature.name() == "alt_title") + alttitles.emplace_back(feature.value()); + } + } + return *this; +} diff --git a/src/frontend/mame/ui/utils.h b/src/frontend/mame/ui/utils.h index 67320c19b6f..184f922d7d2 100644 --- a/src/frontend/mame/ui/utils.h +++ b/src/frontend/mame/ui/utils.h @@ -16,6 +16,7 @@ #include "unicode.h" #include <algorithm> +#include <functional> #include <limits> #include <memory> #include <string> @@ -38,9 +39,14 @@ struct ui_system_info int index; bool available = false; + std::string description; + std::string parent; + std::u32string ucs_shortname; std::u32string ucs_description; std::u32string ucs_manufacturer_description; + std::u32string ucs_default_description; + std::u32string ucs_manufacturer_default_description; }; struct ui_software_info @@ -49,7 +55,7 @@ struct ui_software_info // info for software list item ui_software_info( - software_info const &info, + software_info const &sw, software_part const &p, game_driver const &d, std::string const &li, @@ -60,19 +66,19 @@ struct ui_software_info ui_software_info(game_driver const &d); // copyable/movable - ui_software_info(ui_software_info const &) = default; - ui_software_info(ui_software_info &&) = default; - ui_software_info &operator=(ui_software_info const &) = default; + ui_software_info(ui_software_info const &that); + ui_software_info(ui_software_info &&that) = default; + ui_software_info &operator=(ui_software_info const &that); ui_software_info &operator=(ui_software_info &&) = default; 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 - && interface == r.interface && instance == r.instance && startempty == r.startempty - && parentlongname == r.parentlongname && usage == r.usage && devicetype == r.devicetype; + // compares all fields except info (fragile), alttitles (included in info) and available (environmental) + 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) + && (interface == r.interface) && (instance == r.instance) && (startempty == r.startempty) + && (parentlongname == r.parentlongname) && (devicetype == r.devicetype); } std::string shortname; @@ -88,8 +94,10 @@ struct ui_software_info std::string instance; uint8_t startempty = 0; std::string parentlongname; - std::string usage; + std::string infotext; std::string devicetype; + std::vector<software_info_item> info; + std::vector<std::reference_wrapper<std::string const> > alttitles; bool available = false; }; @@ -206,6 +214,10 @@ public: CLONES, YEAR, PUBLISHERS, + DEVELOPERS, + DISTRIBUTORS, + AUTHORS, + PROGRAMMERS, SUPPORTED, PARTIAL_SUPPORTED, UNSUPPORTED, @@ -287,6 +299,10 @@ public: std::vector<std::string> const ®ions() const { return m_regions; } std::vector<std::string> const &publishers() const { return m_publishers; } std::vector<std::string> const &years() const { return m_years; } + std::vector<std::string> const &developers() const { return m_developers; } + std::vector<std::string> const &distributors() const { return m_distributors; } + std::vector<std::string> const &authors() const { return m_authors; } + std::vector<std::string> const &programmers() const { return m_programmers; } std::vector<std::string> const &device_types() const { return m_device_types; } std::vector<std::string> const &list_names() const { return m_list_names; } std::vector<std::string> const &list_descriptions() const { return m_list_descriptions; } @@ -295,6 +311,7 @@ public: void add_region(std::string const &longname); void add_publisher(std::string const &publisher); void add_year(std::string const &year); + void add_info(software_info_item const &info); void add_device_type(std::string const &device_type); void add_list(std::string const &name, std::string const &description); void finalise(); @@ -307,6 +324,10 @@ private: std::vector<std::string> m_regions; std::vector<std::string> m_publishers; std::vector<std::string> m_years; + std::vector<std::string> m_developers; + std::vector<std::string> m_distributors; + std::vector<std::string> m_authors; + std::vector<std::string> m_programmers; std::vector<std::string> m_device_types; std::vector<std::string> m_list_names, m_list_descriptions; }; diff --git a/src/lib/util/unicode.cpp b/src/lib/util/unicode.cpp index 6787db4a501..3b6f665f63a 100644 --- a/src/lib/util/unicode.cpp +++ b/src/lib/util/unicode.cpp @@ -14,7 +14,6 @@ #ifdef _WIN32 #include "strconv.h" -#define UTF8PROC_DLLEXPORT #endif #include <utf8proc.h> |