1 // license:BSD-3-Clause
2 // copyright-holders:Maurizio Petrarota, Vas Crabb
3 /***************************************************************************
4
5 ui/utils.cpp
6
7 Internal UI user interface.
8
9 ***************************************************************************/
10
11 #include "emu.h"
12 #include "ui/utils.h"
13
14 #include "ui/inifile.h"
15 #include "ui/selector.h"
16
17 #include "language.h"
18 #include "mame.h"
19
20 #include "drivenum.h"
21 #include "rendfont.h"
22 #include "romload.h"
23 #include "softlist.h"
24
25 #include <atomic>
26 #include <bitset>
27 #include <condition_variable>
28 #include <cstdlib>
29 #include <cstring>
30 #include <iterator>
31 #include <unordered_set>
32
33
34 namespace ui {
35
36 namespace {
37
38 constexpr char const *SOFTWARE_REGIONS[] = {
39 "arab", "arg", "asia", "aus", "aut",
40 "bel", "blr", "bra",
41 "can", "chi", "chn", "cze",
42 "den",
43 "ecu", "esp", "euro",
44 "fin", "fra",
45 "gbr", "ger", "gre",
46 "hkg", "hun",
47 "irl", "isr", "isv", "ita",
48 "jpn",
49 "kaz", "kor",
50 "lat", "lux",
51 "mex",
52 "ned", "nld", "nor", "nzl",
53 "pol",
54 "rus",
55 "slo", "spa", "sui", "swe",
56 "tha", "tpe", "tw",
57 "uk", "ukr", "usa" };
58
59 constexpr char const *MACHINE_FILTER_NAMES[machine_filter::COUNT] = {
60 __("Unfiltered"),
61 __("Available"),
62 __("Unavailable"),
63 __("Working"),
64 __("Not Working"),
65 __("Mechanical"),
66 __("Not Mechanical"),
67 __("Category"),
68 __("Favorites"),
69 __("BIOS"),
70 __("Not BIOS"),
71 __("Parents"),
72 __("Clones"),
73 __("Manufacturer"),
74 __("Year"),
75 __("Save Supported"),
76 __("Save Unsupported"),
77 __("CHD Required"),
78 __("No CHD Required"),
79 __("Vertical Screen"),
80 __("Horizontal Screen"),
81 __("Custom Filter") };
82
83 constexpr char const *SOFTWARE_FILTER_NAMES[software_filter::COUNT] = {
84 __("Unfiltered"),
85 __("Available"),
86 __("Unavailable"),
87 __("Favorites"),
88 __("Parents"),
89 __("Clones"),
90 __("Year"),
91 __("Publisher"),
92 __("Supported"),
93 __("Partially Supported"),
94 __("Unsupported"),
95 __("Release Region"),
96 __("Device Type"),
97 __("Software List"),
98 __("Custom Filter") };
99
100
101
102 //-------------------------------------------------
103 // base implementation for simple filters
104 //-------------------------------------------------
105
106 template <class Base, typename Base::type Type>
107 class simple_filter_impl_base : public Base
108 {
109 public:
config_name() const110 virtual char const *config_name() const override { return Base::config_name(Type); }
display_name() const111 virtual char const *display_name() const override { return Base::display_name(Type); }
filter_text() const112 virtual char const *filter_text() const override { return nullptr; }
113
show_ui(mame_ui_manager & mui,render_container & container,std::function<void (Base &)> && handler)114 virtual void show_ui(mame_ui_manager &mui, render_container &container, std::function<void (Base &)> &&handler) override
115 {
116 handler(*this);
117 }
118
wants_adjuster() const119 virtual bool wants_adjuster() const override { return false; }
adjust_text() const120 virtual char const *adjust_text() const override { return filter_text(); }
arrow_flags() const121 virtual uint32_t arrow_flags() const override { return 0; }
adjust_left()122 virtual bool adjust_left() override { return false; }
adjust_right()123 virtual bool adjust_right() override { return false; }
124
save_ini(emu_file & file,unsigned indent) const125 virtual void save_ini(emu_file &file, unsigned indent) const override
126 {
127 file.puts(util::string_format("%2$*1$s%3$s = 1\n", 2 * indent, "", config_name()).c_str());
128 }
129
get_type() const130 virtual typename Base::type get_type() const override { return Type; }
131
adorned_display_name(typename Base::type n) const132 virtual std::string adorned_display_name(typename Base::type n) const override
133 {
134 std::string result;
135 if (Type == n)
136 {
137 result = "_> ";
138 convert_command_glyph(result);
139 }
140 result.append(Base::display_name(n));
141 return result;
142 }
143
144 using Base::config_name;
145 using Base::display_name;
146
147 protected:
simple_filter_impl_base()148 simple_filter_impl_base() { }
149 };
150
151
152
153 //-------------------------------------------------
154 // base implementation for single-choice filters
155 //-------------------------------------------------
156
157 template <class Base, typename Base::type Type>
158 class choice_filter_impl_base : public simple_filter_impl_base<Base, Type>
159 {
160 public:
filter_text() const161 virtual char const *filter_text() const override { return selection_valid() ? selection_text().c_str() : nullptr; }
162
show_ui(mame_ui_manager & mui,render_container & container,std::function<void (Base &)> && handler)163 virtual void show_ui(mame_ui_manager &mui, render_container &container, std::function<void (Base &)> &&handler) override
164 {
165 if (m_choices.empty())
166 {
167 handler(*this);
168 }
169 else
170 {
171 menu::stack_push<menu_selector>(
172 mui, container,
173 std::vector<std::string>(m_choices), // ouch, a vector copy!
174 m_selection,
175 [this, cb = std::move(handler)] (int selection)
176 {
177 m_selection = selection;
178 cb(*this);
179 });
180 }
181 }
182
wants_adjuster() const183 virtual bool wants_adjuster() const override { return have_choices(); }
184
arrow_flags() const185 virtual uint32_t arrow_flags() const override
186 {
187 return ((have_choices() && m_selection) ? menu::FLAG_LEFT_ARROW : 0) | ((m_choices.size() > (m_selection + 1)) ? menu::FLAG_RIGHT_ARROW : 0);
188 }
189
adjust_left()190 virtual bool adjust_left() override
191 {
192 if (!have_choices() || !m_selection)
193 return false;
194 m_selection = (std::min)(m_selection - 1, unsigned(m_choices.size() - 1));
195 return true;
196 }
197
adjust_right()198 virtual bool adjust_right() override
199 {
200 if (m_choices.size() <= (m_selection + 1))
201 return false;
202 ++m_selection;
203 return true;
204 }
205
save_ini(emu_file & file,unsigned indent) const206 virtual void save_ini(emu_file &file, unsigned indent) const override
207 {
208 char const *const text(filter_text());
209 file.puts(util::string_format("%2$*1$s%3$s = %4$s\n", 2 * indent, "", this->config_name(), text ? text : "").c_str());
210 }
211
212 protected:
choice_filter_impl_base(std::vector<std::string> const & choices,char const * value)213 choice_filter_impl_base(std::vector<std::string> const &choices, char const *value)
214 : m_choices(choices)
215 , m_selection(0U)
216 {
217 if (value)
218 {
219 std::vector<std::string>::const_iterator const found(std::find(choices.begin(), choices.end(), value));
220 if (choices.end() != found)
221 m_selection = std::distance(choices.begin(), found);
222 }
223 }
224
have_choices() const225 bool have_choices() const { return !m_choices.empty(); }
selection_valid() const226 bool selection_valid() const { return m_choices.size() > m_selection; }
selection_index() const227 unsigned selection_index() const { return m_selection; }
selection_text() const228 std::string const &selection_text() const { return m_choices[m_selection]; }
229
230 private:
231 std::vector<std::string> const &m_choices;
232 unsigned m_selection;
233 };
234
235
236
237 //-------------------------------------------------
238 // base implementation for composite filters
239 //-------------------------------------------------
240
241 template <class Impl, class Base, typename Base::type Type>
242 class composite_filter_impl_base : public simple_filter_impl_base<Base, Type>
243 {
244 public:
245 virtual void show_ui(mame_ui_manager &mui, render_container &container, std::function<void (Base &)> &&handler) override;
246
wants_adjuster() const247 virtual bool wants_adjuster() const override { return true; }
adjust_text() const248 virtual char const *adjust_text() const override { return _("<set up filters>"); }
249
save_ini(emu_file & file,unsigned indent) const250 virtual void save_ini(emu_file &file, unsigned indent) const override
251 {
252 auto const tail(std::find_if(std::begin(m_filters), std::end(m_filters), [] (typename Base::ptr const &flt) { return !flt; }));
253 file.puts(util::string_format("%2$*1$s%3$s = %4$d\n", 2 * indent, "", this->config_name(), std::distance(std::begin(m_filters), tail)).c_str());
254 for (auto it = std::begin(m_filters); tail != it; ++it)
255 (*it)->save_ini(file, indent + 1);
256 }
257
adorned_display_name(typename Base::type n) const258 virtual std::string adorned_display_name(typename Base::type n) const override
259 {
260 std::string result;
261 if (Type == n)
262 {
263 result = "_> ";
264 convert_command_glyph(result);
265 }
266 else
267 {
268 for (unsigned i = 0; (MAX > i) && m_filters[i]; ++i)
269 {
270 if (m_filters[i]->get_type() == n)
271 {
272 result = util::string_format("@custom%u ", i + 1);
273 convert_command_glyph(result);
274 break;
275 }
276 }
277 }
278 result.append(Base::display_name(n));
279 return result;
280 }
281
apply(typename Base::entry_type const & info) const282 virtual bool apply(typename Base::entry_type const &info) const override
283 {
284 std::bitset<Base::COUNT> inclusions, included;
285 for (typename Base::ptr const &flt : m_filters)
286 {
287 if (!flt)
288 break;
289
290 typename Base::type const t(flt->get_type());
291 if (Impl::is_inclusion(t))
292 {
293 if (!included.test(t))
294 {
295 inclusions.set(t);
296 included.set(t, flt->apply(info));
297 }
298 }
299 else if (!flt->apply(info))
300 {
301 return false;
302 }
303 }
304 return inclusions == included;
305 }
306
307 protected:
composite_filter_impl_base()308 composite_filter_impl_base() { }
309
populate(char const * value,emu_file * file,unsigned indent)310 void populate(char const *value, emu_file *file, unsigned indent)
311 {
312 // try to load filters from a file
313 if (value && file)
314 {
315 unsigned const cnt(unsigned((std::max)(std::min(int(MAX), std::atoi(value)), 0)));
316 for (unsigned i = 0; cnt > i; ++i)
317 {
318 typename Base::ptr flt(static_cast<Impl &>(*this).create(*file, indent + 1));
319 if (!flt || !check_type(i, flt->get_type()))
320 break;
321 m_filters[i] = std::move(flt);
322 }
323 }
324
325 // instantiate first allowed filter type if we're still empty
326 for (typename Base::type t = Base::FIRST; (Base::COUNT > t) && !m_filters[0]; ++t)
327 {
328 if (Impl::type_allowed(0, t))
329 m_filters[0] = static_cast<Impl &>(*this).create(t);
330 }
331 }
332
333 private:
334 static constexpr unsigned MAX = 8;
335
336 class menu_configure : public menu
337 {
338 public:
menu_configure(mame_ui_manager & mui,render_container & container,Impl & parent,std::function<void (Base & filter)> && handler)339 menu_configure(
340 mame_ui_manager &mui,
341 render_container &container,
342 Impl &parent,
343 std::function<void (Base &filter)> &&handler)
344 : menu(mui, container)
345 , m_parent(parent)
346 , m_handler(std::move(handler))
347 , m_added(false)
348 {
349 }
350
~menu_configure()351 virtual ~menu_configure() override { m_handler(m_parent); }
352
353 protected:
custom_render(void * selectedref,float top,float bottom,float x,float y,float x2,float y2)354 virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override
355 {
356 char const *const text[] = { _("Select custom filters:") };
357 draw_text_box(
358 std::begin(text), std::end(text),
359 x, x2, y - top, y - ui().box_tb_border(),
360 ui::text_layout::CENTER, ui::text_layout::NEVER, false,
361 ui().colors().text_color(), UI_GREEN_COLOR, 1.0f);
362 }
363
364 private:
365 enum : uintptr_t
366 {
367 FILTER_FIRST = 1,
368 FILTER_LAST = FILTER_FIRST + MAX - 1,
369 ADJUST_FIRST,
370 ADJUST_LAST = ADJUST_FIRST + MAX - 1,
371 REMOVE_FILTER,
372 ADD_FILTER
373 };
374
375 virtual void populate(float &customtop, float &custombottom) override;
376 virtual void handle() override;
377
set_filter_type(unsigned pos,typename Base::type n)378 bool set_filter_type(unsigned pos, typename Base::type n)
379 {
380 if (!m_parent.m_filters[pos] || (m_parent.m_filters[pos]->get_type()))
381 {
382 save_filter(pos);
383 retrieve_filter(pos, n);
384 return true;
385 }
386 else
387 {
388 return false;
389 }
390 }
391
append_filter()392 bool append_filter()
393 {
394 unsigned pos = 0;
395 while (m_parent.m_filters[pos])
396 {
397 if (MAX <= ++pos)
398 return false;
399 }
400 for (typename Base::type candidate = Base::FIRST; Base::COUNT > candidate; ++candidate)
401 {
402 if (m_parent.check_type(pos, candidate))
403 {
404 set_filter_type(pos, candidate);
405 return true;
406 }
407 }
408 return false;
409 }
410
drop_last_filter()411 bool drop_last_filter()
412 {
413 for (unsigned i = 2; MAX >= i; ++i)
414 {
415 if ((MAX <= i) || !m_parent.m_filters[i])
416 {
417 save_filter(i - 1);
418 m_parent.m_filters[i - 1].reset();
419 return true;
420 }
421 }
422 return false;
423 }
424
save_filter(unsigned pos)425 void save_filter(unsigned pos)
426 {
427 typename Base::ptr &flt(m_parent.m_filters[pos]);
428 if (flt && flt->wants_adjuster())
429 m_saved_filters[pos][flt->get_type()] = std::move(flt);
430 }
431
retrieve_filter(unsigned pos,typename Base::type n)432 void retrieve_filter(unsigned pos, typename Base::type n)
433 {
434 typename Base::ptr &flt(m_parent.m_filters[pos]);
435 auto const found(m_saved_filters[pos].find(n));
436 if (m_saved_filters[pos].end() != found)
437 {
438 flt = std::move(found->second);
439 m_saved_filters[pos].erase(found);
440 }
441 else
442 {
443 flt = m_parent.create(n);
444 }
445 }
446
get_arrow_flags(unsigned pos)447 uint32_t get_arrow_flags(unsigned pos)
448 {
449 uint32_t result(0);
450 typename Base::type const current(m_parent.m_filters[pos]->get_type());
451
452 // look for a lower type that's allowed and isn't contradictory
453 typename Base::type prev(current);
454 while ((Base::FIRST < prev) && !(FLAG_LEFT_ARROW & result))
455 {
456 if (m_parent.check_type(pos, --prev))
457 result |= FLAG_LEFT_ARROW;
458 }
459
460 // look for a higher type that's allowed and isn't contradictory
461 typename Base::type next(current);
462 while ((Base::LAST > next) && !(FLAG_RIGHT_ARROW & result))
463 {
464 if (m_parent.check_type(pos, ++next))
465 result |= FLAG_RIGHT_ARROW;
466 }
467
468 return result;
469 }
470
471 Impl &m_parent;
472 std::map<typename Base::type, typename Base::ptr> m_saved_filters[MAX];
473 std::function<void (Base &)> m_handler;
474 bool m_added;
475 };
476
check_type(unsigned pos,typename Base::type candidate)477 bool check_type(unsigned pos, typename Base::type candidate)
478 {
479 if (!Impl::type_allowed(pos, candidate))
480 return false;
481 unsigned j = 0;
482 while ((MAX > j) && m_filters[j] && ((pos == j) || !Impl::types_contradictory(m_filters[j]->get_type(), candidate)))
483 ++j;
484 return (MAX <= j) || !m_filters[j];
485 };
486
487 typename Base::ptr m_filters[MAX];
488 };
489
490 template <class Impl, class Base, typename Base::type Type>
show_ui(mame_ui_manager & mui,render_container & container,std::function<void (Base & filter)> && handler)491 void composite_filter_impl_base<Impl, Base, Type>::show_ui(
492 mame_ui_manager &mui,
493 render_container &container,
494 std::function<void (Base &filter)> &&handler)
495 {
496 menu::stack_push<menu_configure>(mui, container, static_cast<Impl &>(*this), std::move(handler));
497 }
498
499
500 template <class Impl, class Base, typename Base::type Type>
populate(float & customtop,float & custombottom)501 void composite_filter_impl_base<Impl, Base, Type>::menu_configure::populate(float &customtop, float &custombottom)
502 {
503 // add items for each active filter
504 unsigned i = 0;
505 for (i = 0; (MAX > i) && m_parent.m_filters[i]; ++i)
506 {
507 item_append(util::string_format(_("Filter %1$u"), i + 1), m_parent.m_filters[i]->display_name(), get_arrow_flags(i), (void *)(FILTER_FIRST + i));
508 if (m_added)
509 set_selected_index(item_count() - 2);
510 if (m_parent.m_filters[i]->wants_adjuster())
511 {
512 std::string name("^!");
513 convert_command_glyph(name);
514 item_append(name, m_parent.m_filters[i]->adjust_text(), m_parent.m_filters[i]->arrow_flags(), (void *)(ADJUST_FIRST + i));
515 }
516 item_append(menu_item_type::SEPARATOR);
517 }
518 m_added = false;
519
520 // add remove/add handlers
521 if (1 < i)
522 item_append(_("Remove last filter"), "", 0, (void *)REMOVE_FILTER);
523 if (MAX > i)
524 item_append(_("Add filter"), "", 0, (void *)ADD_FILTER);
525 item_append(menu_item_type::SEPARATOR);
526
527 // leave space for heading
528 customtop = ui().get_line_height() + 3.0f * ui().box_tb_border();
529 }
530
531 template <class Impl, class Base, typename Base::type Type>
handle()532 void composite_filter_impl_base<Impl, Base, Type>::menu_configure::handle()
533 {
534 const event *menu_event = process(PROCESS_LR_REPEAT);
535 if (menu_event && menu_event->itemref)
536 {
537 m_added = false;
538 bool changed(false);
539 uintptr_t const ref(reinterpret_cast<uintptr_t>(menu_event->itemref));
540 switch (menu_event->iptkey)
541 {
542 case IPT_UI_LEFT:
543 case IPT_UI_RIGHT:
544 if ((FILTER_FIRST <= ref) && (FILTER_LAST >= ref))
545 {
546 // change filter type
547 unsigned const pos(ref - FILTER_FIRST);
548 typename Base::type const current(m_parent.m_filters[pos]->get_type());
549 if (IPT_UI_LEFT == menu_event->iptkey)
550 {
551 typename Base::type n(current);
552 while ((Base::FIRST < n) && !changed)
553 {
554 if (m_parent.check_type(pos, --n))
555 changed = set_filter_type(pos, n);
556 }
557 }
558 else
559 {
560 typename Base::type n(current);
561 while ((Base::LAST > n) && !changed)
562 {
563 if (m_parent.check_type(pos, ++n))
564 changed = set_filter_type(pos, n);
565 }
566 }
567 }
568 else if ((ADJUST_FIRST <= ref) && (ADJUST_LAST >= ref))
569 {
570 // change filter value
571 Base &pos(*m_parent.m_filters[ref - ADJUST_FIRST]);
572 changed = (IPT_UI_LEFT == menu_event->iptkey) ? pos.adjust_left() : pos.adjust_right();
573 }
574 break;
575
576 case IPT_UI_SELECT:
577 if ((FILTER_FIRST <= ref) && (FILTER_LAST >= ref))
578 {
579 // show selector with non-contradictory types
580 std::vector<typename Base::type> types;
581 std::vector<std::string> names;
582 types.reserve(Base::COUNT);
583 names.reserve(Base::COUNT);
584 int sel(-1);
585 unsigned const pos(ref - FILTER_FIRST);
586 typename Base::type const current(m_parent.m_filters[pos]->get_type());
587 for (typename Base::type candidate = Base::FIRST; Base::COUNT > candidate; ++candidate)
588 {
589 if (Impl::type_allowed(pos, candidate))
590 {
591 if (current == candidate)
592 sel = types.size();
593 unsigned i = 0;
594 while ((MAX > i) && m_parent.m_filters[i] && ((pos == i) || !Impl::types_contradictory(m_parent.m_filters[i]->get_type(), candidate)))
595 ++i;
596 if ((MAX <= i) || !m_parent.m_filters[i])
597 {
598 types.emplace_back(candidate);
599 names.emplace_back(Base::display_name(candidate));
600 }
601 }
602 }
603 menu::stack_push<menu_selector>(
604 ui(),
605 container(),
606 std::move(names),
607 sel,
608 [this, pos, t = std::move(types)] (int selection)
609 {
610 if (set_filter_type(pos, t[selection]))
611 reset(reset_options::REMEMBER_REF);
612 });
613 }
614 else if ((ADJUST_FIRST <= ref) && (ADJUST_LAST >= ref))
615 {
616 // show selected filter's UI
617 m_parent.m_filters[ref - ADJUST_FIRST]->show_ui(ui(), container(), [this] (Base &filter) { reset(reset_options::REMEMBER_REF); });
618 }
619 else if (REMOVE_FILTER == ref)
620 {
621 changed = drop_last_filter();
622 }
623 else if (ADD_FILTER == ref)
624 {
625 m_added = append_filter();
626 }
627 break;
628 }
629
630 // rebuild if anything changed
631 if (changed)
632 reset(reset_options::REMEMBER_REF);
633 else if (m_added)
634 reset(reset_options::SELECT_FIRST);
635 }
636 }
637
638
639
640 //-------------------------------------------------
641 // invertable machine filters
642 //-------------------------------------------------
643
644 template <machine_filter::type Type = machine_filter::AVAILABLE>
645 class available_machine_filter_impl : public simple_filter_impl_base<machine_filter, Type>
646 {
647 public:
available_machine_filter_impl(machine_filter_data const & data,char const * value,emu_file * file,unsigned indent)648 available_machine_filter_impl(machine_filter_data const &data, char const *value, emu_file *file, unsigned indent) { }
649
apply(ui_system_info const & system) const650 virtual bool apply(ui_system_info const &system) const override { return system.available; }
651 };
652
653
654 template <machine_filter::type Type = machine_filter::WORKING>
655 class working_machine_filter_impl : public simple_filter_impl_base<machine_filter, Type>
656 {
657 public:
working_machine_filter_impl(machine_filter_data const & data,char const * value,emu_file * file,unsigned indent)658 working_machine_filter_impl(machine_filter_data const &data, char const *value, emu_file *file, unsigned indent) { }
659
apply(ui_system_info const & system) const660 virtual bool apply(ui_system_info const &system) const override { return !(system.driver->flags & machine_flags::NOT_WORKING); }
661 };
662
663
664 template <machine_filter::type Type = machine_filter::MECHANICAL>
665 class mechanical_machine_filter_impl : public simple_filter_impl_base<machine_filter, Type>
666 {
667 public:
mechanical_machine_filter_impl(machine_filter_data const & data,char const * value,emu_file * file,unsigned indent)668 mechanical_machine_filter_impl(machine_filter_data const &data, char const *value, emu_file *file, unsigned indent) { }
669
apply(ui_system_info const & system) const670 virtual bool apply(ui_system_info const &system) const override { return system.driver->flags & machine_flags::MECHANICAL; }
671 };
672
673
674 template <machine_filter::type Type = machine_filter::BIOS>
675 class bios_machine_filter_impl : public simple_filter_impl_base<machine_filter, Type>
676 {
677 public:
bios_machine_filter_impl(machine_filter_data const & data,char const * value,emu_file * file,unsigned indent)678 bios_machine_filter_impl(machine_filter_data const &data, char const *value, emu_file *file, unsigned indent) { }
679
apply(ui_system_info const & system) const680 virtual bool apply(ui_system_info const &system) const override { return system.driver->flags & machine_flags::IS_BIOS_ROOT; }
681 };
682
683
684 template <machine_filter::type Type = machine_filter::PARENTS>
685 class parents_machine_filter_impl : public simple_filter_impl_base<machine_filter, Type>
686 {
687 public:
parents_machine_filter_impl(machine_filter_data const & data,char const * value,emu_file * file,unsigned indent)688 parents_machine_filter_impl(machine_filter_data const &data, char const *value, emu_file *file, unsigned indent) { }
689
apply(ui_system_info const & system) const690 virtual bool apply(ui_system_info const &system) const override
691 {
692 bool const have_parent(strcmp(system.driver->parent, "0"));
693 auto const parent_idx(have_parent ? driver_list::find(system.driver->parent) : -1);
694 return !have_parent || (0 > parent_idx) || (driver_list::driver(parent_idx).flags & machine_flags::IS_BIOS_ROOT);
695 }
696 };
697
698
699 template <machine_filter::type Type = machine_filter::CHD>
700 class chd_machine_filter_impl : public simple_filter_impl_base<machine_filter, Type>
701 {
702 public:
chd_machine_filter_impl(machine_filter_data const & data,char const * value,emu_file * file,unsigned indent)703 chd_machine_filter_impl(machine_filter_data const &data, char const *value, emu_file *file, unsigned indent) { }
704
apply(ui_system_info const & system) const705 virtual bool apply(ui_system_info const &system) const override
706 {
707 for (tiny_rom_entry const *rom = system.driver->rom; !ROMENTRY_ISEND(rom); ++rom)
708 {
709 if (ROMENTRY_ISREGION(rom) && ROMREGION_ISDISKDATA(rom))
710 return true;
711 }
712 return false;
713 }
714 };
715
716
717 template <machine_filter::type Type = machine_filter::SAVE>
718 class save_machine_filter_impl : public simple_filter_impl_base<machine_filter, Type>
719 {
720 public:
save_machine_filter_impl(machine_filter_data const & data,char const * value,emu_file * file,unsigned indent)721 save_machine_filter_impl(machine_filter_data const &data, char const *value, emu_file *file, unsigned indent) { }
722
apply(ui_system_info const & system) const723 virtual bool apply(ui_system_info const &system) const override { return system.driver->flags & machine_flags::SUPPORTS_SAVE; }
724 };
725
726
727 template <machine_filter::type Type = machine_filter::VERTICAL>
728 class vertical_machine_filter_impl : public simple_filter_impl_base<machine_filter, Type>
729 {
730 public:
vertical_machine_filter_impl(machine_filter_data const & data,char const * value,emu_file * file,unsigned indent)731 vertical_machine_filter_impl(machine_filter_data const &data, char const *value, emu_file *file, unsigned indent) { }
732
apply(ui_system_info const & system) const733 virtual bool apply(ui_system_info const &system) const override { return system.driver->flags & machine_flags::SWAP_XY; }
734 };
735
736
737
738 //-------------------------------------------------
739 // concrete machine filters
740 //-------------------------------------------------
741
742 class manufacturer_machine_filter : public choice_filter_impl_base<machine_filter, machine_filter::MANUFACTURER>
743 {
744 public:
manufacturer_machine_filter(machine_filter_data const & data,char const * value,emu_file * file,unsigned indent)745 manufacturer_machine_filter(machine_filter_data const &data, char const *value, emu_file *file, unsigned indent)
746 : choice_filter_impl_base<machine_filter, machine_filter::MANUFACTURER>(data.manufacturers(), value)
747 {
748 }
749
apply(ui_system_info const & system) const750 virtual bool apply(ui_system_info const &system) const override
751 {
752 if (!have_choices())
753 return true;
754 else if (!selection_valid())
755 return false;
756
757 std::string const name(machine_filter_data::extract_manufacturer(system.driver->manufacturer));
758 return !name.empty() && (selection_text() == name);
759 }
760 };
761
762
763 class year_machine_filter : public choice_filter_impl_base<machine_filter, machine_filter::YEAR>
764 {
765 public:
year_machine_filter(machine_filter_data const & data,char const * value,emu_file * file,unsigned indent)766 year_machine_filter(machine_filter_data const &data, char const *value, emu_file *file, unsigned indent)
767 : choice_filter_impl_base<machine_filter, machine_filter::YEAR>(data.years(), value)
768 {
769 }
770
apply(ui_system_info const & system) const771 virtual bool apply(ui_system_info const &system) const override { return !have_choices() || (selection_valid() && (selection_text() == system.driver->year)); }
772 };
773
774
775
776 //-------------------------------------------------
777 // complementary machine filters
778 //-------------------------------------------------
779
780 template <template <machine_filter::type T> class Base, machine_filter::type Type>
781 class inverted_machine_filter : public Base<Type>
782 {
783 public:
inverted_machine_filter(machine_filter_data const & data,char const * value,emu_file * file,unsigned indent)784 inverted_machine_filter(machine_filter_data const &data, char const *value, emu_file *file, unsigned indent)
785 : Base<Type>(data, value, file, indent)
786 {
787 }
788
apply(ui_system_info const & system) const789 virtual bool apply(ui_system_info const &system) const override { return !Base<Type>::apply(system); }
790 };
791
792 using available_machine_filter = available_machine_filter_impl<>;
793 using working_machine_filter = working_machine_filter_impl<>;
794 using mechanical_machine_filter = mechanical_machine_filter_impl<>;
795 using bios_machine_filter = bios_machine_filter_impl<>;
796 using parents_machine_filter = parents_machine_filter_impl<>;
797 using save_machine_filter = save_machine_filter_impl<>;
798 using chd_machine_filter = chd_machine_filter_impl<>;
799 using vertical_machine_filter = vertical_machine_filter_impl<>;
800
801 using unavailable_machine_filter = inverted_machine_filter<available_machine_filter_impl, machine_filter::UNAVAILABLE>;
802 using not_working_machine_filter = inverted_machine_filter<working_machine_filter_impl, machine_filter::NOT_WORKING>;
803 using not_mechanical_machine_filter = inverted_machine_filter<mechanical_machine_filter_impl, machine_filter::NOT_MECHANICAL>;
804 using not_bios_machine_filter = inverted_machine_filter<bios_machine_filter_impl, machine_filter::NOT_BIOS>;
805 using clones_machine_filter = inverted_machine_filter<parents_machine_filter_impl, machine_filter::CLONES>;
806 using nosave_machine_filter = inverted_machine_filter<save_machine_filter_impl, machine_filter::NOSAVE>;
807 using nochd_machine_filter = inverted_machine_filter<chd_machine_filter_impl, machine_filter::NOCHD>;
808 using horizontal_machine_filter = inverted_machine_filter<vertical_machine_filter_impl, machine_filter::HORIZONTAL>;
809
810
811
812 //-------------------------------------------------
813 // dummy machine filters (special-cased in menu)
814 //-------------------------------------------------
815
816 template <machine_filter::type Type>
817 class inclusive_machine_filter_impl : public simple_filter_impl_base<machine_filter, Type>
818 {
819 public:
inclusive_machine_filter_impl(machine_filter_data const & data,char const * value,emu_file * file,unsigned indent)820 inclusive_machine_filter_impl(machine_filter_data const &data, char const *value, emu_file *file, unsigned indent) { }
821
apply(ui_system_info const & system) const822 virtual bool apply(ui_system_info const &system) const override { return true; }
823 };
824
825 using all_machine_filter = inclusive_machine_filter_impl<machine_filter::ALL>;
826 using favorite_machine_filter = inclusive_machine_filter_impl<machine_filter::FAVORITE>;
827
828
829
830 //-------------------------------------------------
831 // category machine filter
832 //-------------------------------------------------
833
834 class category_machine_filter : public simple_filter_impl_base<machine_filter, machine_filter::CATEGORY>
835 {
836 public:
category_machine_filter(machine_filter_data const & data,char const * value,emu_file * file,unsigned indent)837 category_machine_filter(machine_filter_data const &data, char const *value, emu_file *file, unsigned indent)
838 : m_ini(0)
839 , m_group(0)
840 , m_include_clones(false)
841 , m_adjust_text()
842 , m_cache()
843 , m_cache_valid(false)
844 {
845 inifile_manager const &mgr(mame_machine_manager::instance()->inifile());
846 if (value)
847 {
848 char const *const split(std::strchr(value, '/'));
849 std::string ini;
850 if (split)
851 ini.assign(value, split);
852 else
853 ini.assign(value);
854
855 for (unsigned i = 0; mgr.get_file_count() > i; ++i)
856 {
857 if (mgr.get_file_name(i) == ini)
858 {
859 m_ini = i;
860 if (split)
861 {
862 std::string const group(split + 1);
863 for (unsigned j = 0; mgr.get_category_count(i) > j; ++j)
864 {
865 if (mgr.get_category_name(i, j) == group)
866 {
867 m_group = j;
868 break;
869 }
870 }
871 }
872 break;
873 }
874 }
875 }
876
877 if (mgr.get_file_count() > m_ini)
878 m_include_clones = include_clones_default(mgr.get_file_name(m_ini));
879
880 set_adjust_text();
881 }
882
filter_text() const883 virtual char const *filter_text() const override
884 {
885 inifile_manager const &mgr(mame_machine_manager::instance()->inifile());
886 return ((mgr.get_file_count() > m_ini) && (mgr.get_category_count(m_ini) > m_group)) ? m_adjust_text.c_str() : nullptr;
887 }
888
889 virtual void show_ui(mame_ui_manager &mui, render_container &container, std::function<void (machine_filter &)> &&handler) override;
890
wants_adjuster() const891 virtual bool wants_adjuster() const override { return mame_machine_manager::instance()->inifile().get_file_count(); }
adjust_text() const892 virtual char const *adjust_text() const override { return m_adjust_text.c_str(); }
893
save_ini(emu_file & file,unsigned indent) const894 virtual void save_ini(emu_file &file, unsigned indent) const override
895 {
896 char const *const text(filter_text());
897 file.puts(util::string_format("%2$*1$s%3$s = %4$s\n", 2 * indent, "", this->config_name(), text ? text : "").c_str());
898 }
899
apply(ui_system_info const & system) const900 virtual bool apply(ui_system_info const &system) const override
901 {
902 inifile_manager const &mgr(mame_machine_manager::instance()->inifile());
903 if (!mgr.get_file_count())
904 return true;
905 else if ((mgr.get_file_count() <= m_ini) || (mgr.get_category_count(m_ini) <= m_group))
906 return false;
907
908 if (!m_cache_valid)
909 mame_machine_manager::instance()->inifile().load_ini_category(m_ini, m_group, m_cache);
910 m_cache_valid = true;
911
912 if (m_cache.end() != m_cache.find(system.driver))
913 return true;
914
915 if (m_include_clones)
916 {
917 int const found(driver_list::find(system.driver->parent));
918 return m_cache.end() != m_cache.find(&driver_list::driver(found));
919 }
920
921 return false;
922 }
923
924 private:
925 class menu_configure : public menu
926 {
927 public:
menu_configure(mame_ui_manager & mui,render_container & container,category_machine_filter & parent,std::function<void (machine_filter & filter)> && handler)928 menu_configure(
929 mame_ui_manager &mui,
930 render_container &container,
931 category_machine_filter &parent,
932 std::function<void (machine_filter &filter)> &&handler)
933 : menu(mui, container)
934 , m_parent(parent)
935 , m_handler(std::move(handler))
936 , m_state(std::make_unique<std::pair<unsigned, bool> []>(mame_machine_manager::instance()->inifile().get_file_count()))
937 , m_ini(parent.m_ini)
938 {
939 inifile_manager const &mgr(mame_machine_manager::instance()->inifile());
940 for (size_t i = 0; mgr.get_file_count() > i; ++i)
941 {
942 m_state[i].first = (m_ini == i) ? m_parent.m_group : 0U;
943 m_state[i].second = (m_ini == i) ? m_parent.m_include_clones : include_clones_default(mgr.get_file_name(i));
944 }
945 }
946
~menu_configure()947 virtual ~menu_configure() override
948 {
949 bool const valid(mame_machine_manager::instance()->inifile().get_file_count() > m_ini);
950 unsigned const group(valid ? m_state[m_ini].first : 0);
951 if ((m_ini != m_parent.m_ini) || (group != m_parent.m_group))
952 {
953 m_parent.m_cache.clear();
954 m_parent.m_cache_valid = false;
955 }
956 m_parent.m_ini = m_ini;
957 m_parent.m_group = group;
958 m_parent.m_include_clones = valid ? m_state[m_ini].second : false;
959 m_parent.set_adjust_text();
960 m_handler(m_parent);
961 }
962
963 protected:
custom_render(void * selectedref,float top,float bottom,float x,float y,float x2,float y2)964 virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override
965 {
966 char const *const text[] = { _("Select category:") };
967 draw_text_box(
968 std::begin(text), std::end(text),
969 x, x2, y - top, y - ui().box_tb_border(),
970 ui::text_layout::CENTER, ui::text_layout::NEVER, false,
971 ui().colors().text_color(), UI_GREEN_COLOR, 1.0f);
972 }
973
974 private:
975 enum : uintptr_t
976 {
977 INI_FILE = 1,
978 SYSTEM_GROUP,
979 INCLUDE_CLONES
980 };
981
982 virtual void populate(float &customtop, float &custombottom) override;
983 virtual void handle() override;
984
985 category_machine_filter &m_parent;
986 std::function<void (machine_filter &)> m_handler;
987 std::unique_ptr<std::pair<unsigned, bool> []> const m_state;
988 unsigned m_ini;
989 };
990
set_adjust_text()991 void set_adjust_text()
992 {
993 inifile_manager const &mgr(mame_machine_manager::instance()->inifile());
994 unsigned const filecnt(mgr.get_file_count());
995 if (!filecnt)
996 {
997 m_adjust_text = _("[no category INI files]");
998 }
999 else
1000 {
1001 m_ini = std::min(m_ini, filecnt - 1);
1002 unsigned const groupcnt(mgr.get_category_count(m_ini));
1003 if (!groupcnt)
1004 {
1005 m_adjust_text = _("[no groups in INI file]");
1006 }
1007 else
1008 {
1009 m_group = std::min(m_group, groupcnt - 1);
1010 m_adjust_text = util::string_format("%s/%s", mgr.get_file_name(m_ini), mgr.get_category_name(m_ini, m_group));
1011 }
1012 }
1013 }
1014
include_clones_default(std::string const & name)1015 static bool include_clones_default(std::string const &name)
1016 {
1017 return !core_stricmp(name.c_str(), "category.ini") || !core_stricmp(name.c_str(), "alltime.ini");
1018 }
1019
1020 unsigned m_ini, m_group;
1021 bool m_include_clones;
1022 std::string m_adjust_text;
1023 mutable std::unordered_set<game_driver const *> m_cache;
1024 mutable bool m_cache_valid;
1025 };
1026
show_ui(mame_ui_manager & mui,render_container & container,std::function<void (machine_filter &)> && handler)1027 void category_machine_filter::show_ui(mame_ui_manager &mui, render_container &container, std::function<void (machine_filter &)> &&handler)
1028 {
1029 menu::stack_push<menu_configure>(mui, container, *this, std::move(handler));
1030 }
1031
1032
populate(float & customtop,float & custombottom)1033 void category_machine_filter::menu_configure::populate(float &customtop, float &custombottom)
1034 {
1035 inifile_manager const &mgr(mame_machine_manager::instance()->inifile());
1036 unsigned const filecnt(mgr.get_file_count());
1037 if (!filecnt)
1038 {
1039 item_append(_("No category INI files found"), "", FLAG_DISABLE, nullptr);
1040 }
1041 else
1042 {
1043 m_ini = std::min(m_ini, filecnt - 1);
1044 item_append(_("File"), mgr.get_file_name(m_ini), get_arrow_flags(0U, filecnt - 1, m_ini), reinterpret_cast<void *>(INI_FILE));
1045 unsigned const groupcnt(mgr.get_category_count(m_ini));
1046 if (!groupcnt)
1047 {
1048 item_append(_("No groups found in category file"), "", FLAG_DISABLE, nullptr);
1049 }
1050 else
1051 {
1052 m_state[m_ini].first = std::min(m_state[m_ini].first, groupcnt - 1);
1053 item_append(_("Group"), mgr.get_category_name(m_ini, m_state[m_ini].first), get_arrow_flags(0U, groupcnt - 1, m_state[m_ini].first), reinterpret_cast<void *>(SYSTEM_GROUP));
1054 item_append(_("Include clones"), m_state[m_ini].second ? _("Yes") : _("No"), m_state[m_ini].second ? FLAG_LEFT_ARROW : FLAG_RIGHT_ARROW, reinterpret_cast<void *>(INCLUDE_CLONES));
1055 }
1056 }
1057 item_append(menu_item_type::SEPARATOR);
1058 customtop = ui().get_line_height() + 3.0f * ui().box_tb_border();
1059 }
1060
handle()1061 void category_machine_filter::menu_configure::handle()
1062 {
1063 const event *menu_event = process(PROCESS_LR_REPEAT);
1064 if (menu_event && menu_event->itemref)
1065 {
1066 bool changed(false);
1067 uintptr_t const ref(reinterpret_cast<uintptr_t>(menu_event->itemref));
1068 inifile_manager const &mgr(mame_machine_manager::instance()->inifile());
1069 switch (menu_event->iptkey)
1070 {
1071 case IPT_UI_LEFT:
1072 if ((INI_FILE == ref) && m_ini)
1073 {
1074 --m_ini;
1075 changed = true;
1076 }
1077 else if ((SYSTEM_GROUP == ref) && m_state[m_ini].first)
1078 {
1079 --m_state[m_ini].first;
1080 changed = true;
1081 }
1082 else if ((INCLUDE_CLONES == ref) && m_state[m_ini].second)
1083 {
1084 m_state[m_ini].second = false;
1085 changed = true;
1086 }
1087 break;
1088 case IPT_UI_RIGHT:
1089 if ((INI_FILE == ref) && (mgr.get_file_count() > (m_ini + 1)))
1090 {
1091 ++m_ini;
1092 changed = true;
1093 }
1094 else if ((SYSTEM_GROUP == ref) && (mgr.get_category_count(m_ini) > (m_state[m_ini].first + 1)))
1095 {
1096 ++m_state[m_ini].first;
1097 changed = true;
1098 }
1099 else if ((INCLUDE_CLONES == ref) && !m_state[m_ini].second)
1100 {
1101 m_state[m_ini].second = true;
1102 changed = true;
1103 }
1104 break;
1105
1106 case IPT_UI_SELECT:
1107 if (INI_FILE == ref)
1108 {
1109 std::vector<std::string> choices;
1110 choices.reserve(mgr.get_file_count());
1111 for (size_t i = 0; mgr.get_file_count() > i; ++i)
1112 choices.emplace_back(mgr.get_file_name(i));
1113 menu::stack_push<menu_selector>(
1114 ui(),
1115 container(),
1116 std::move(choices),
1117 m_ini,
1118 [this] (int selection)
1119 {
1120 if (selection != m_ini)
1121 {
1122 m_ini = selection;
1123 reset(reset_options::REMEMBER_REF);
1124 }
1125 });
1126 }
1127 else if (SYSTEM_GROUP == ref)
1128 {
1129 std::vector<std::string> choices;
1130 choices.reserve(mgr.get_category_count(m_ini));
1131 for (size_t i = 0; mgr.get_category_count(m_ini) > i; ++i)
1132 choices.emplace_back(mgr.get_category_name(m_ini, i));
1133 menu::stack_push<menu_selector>(
1134 ui(),
1135 container(),
1136 std::move(choices),
1137 m_state[m_ini].first,
1138 [this] (int selection)
1139 {
1140 if (selection != m_state[m_ini].first)
1141 {
1142 m_state[m_ini].first = selection;
1143 reset(reset_options::REMEMBER_REF);
1144 }
1145 });
1146 }
1147 else if (INCLUDE_CLONES == ref)
1148 {
1149 m_state[m_ini].second = !m_state[m_ini].second;
1150 reset(reset_options::REMEMBER_REF);
1151 }
1152 break;
1153 }
1154
1155 // rebuild if anything changed
1156 if (changed)
1157 reset(reset_options::REMEMBER_REF);
1158 }
1159 }
1160
1161
1162
1163 //-------------------------------------------------
1164 // composite machine filter
1165 //-------------------------------------------------
1166
1167 class custom_machine_filter : public composite_filter_impl_base<custom_machine_filter, machine_filter, machine_filter::CUSTOM>
1168 {
1169 public:
custom_machine_filter(machine_filter_data const & data,char const * value,emu_file * file,unsigned indent)1170 custom_machine_filter(machine_filter_data const &data, char const *value, emu_file *file, unsigned indent)
1171 : composite_filter_impl_base<custom_machine_filter, machine_filter, machine_filter::CUSTOM>()
1172 , m_data(data)
1173 {
1174 populate(value, file, indent);
1175 }
1176
create(type n) const1177 ptr create(type n) const { return machine_filter::create(n, m_data); }
create(emu_file & file,unsigned indent) const1178 ptr create(emu_file &file, unsigned indent) const { return machine_filter::create(file, m_data, indent); }
1179
type_allowed(unsigned pos,type n)1180 static bool type_allowed(unsigned pos, type n)
1181 {
1182 return (FIRST <= n) && (LAST >= n) && (ALL != n) && (FAVORITE != n) && (CUSTOM != n);
1183 }
1184
types_contradictory(type n,type m)1185 static bool types_contradictory(type n, type m)
1186 {
1187 switch (n)
1188 {
1189 case AVAILABLE: return UNAVAILABLE == m;
1190 case UNAVAILABLE: return AVAILABLE == m;
1191 case WORKING: return NOT_WORKING == m;
1192 case NOT_WORKING: return WORKING == m;
1193 case MECHANICAL: return NOT_MECHANICAL == m;
1194 case NOT_MECHANICAL: return MECHANICAL == m;
1195 case BIOS: return NOT_BIOS == m;
1196 case NOT_BIOS: return BIOS == m;
1197 case PARENTS: return CLONES == m;
1198 case CLONES: return PARENTS == m;
1199 case SAVE: return NOSAVE == m;
1200 case NOSAVE: return SAVE == m;
1201 case CHD: return NOCHD == m;
1202 case NOCHD: return CHD == m;
1203 case VERTICAL: return HORIZONTAL == m;
1204 case HORIZONTAL: return VERTICAL == m;
1205
1206 case ALL:
1207 case CATEGORY:
1208 case FAVORITE:
1209 case MANUFACTURER:
1210 case YEAR:
1211 case CUSTOM:
1212 case COUNT:
1213 break;
1214 }
1215 return false;
1216 }
1217
is_inclusion(type n)1218 static bool is_inclusion(type n)
1219 {
1220 return (CATEGORY == n) || (MANUFACTURER == n) || (YEAR == n);
1221 }
1222
1223 private:
1224 machine_filter_data const &m_data;
1225 };
1226
1227
1228
1229 //-------------------------------------------------
1230 // concrete software filters
1231 //-------------------------------------------------
1232
1233 class all_software_filter : public simple_filter_impl_base<software_filter, software_filter::ALL>
1234 {
1235 public:
all_software_filter(software_filter_data const & data,char const * value,emu_file * file,unsigned indent)1236 all_software_filter(software_filter_data const &data, char const *value, emu_file *file, unsigned indent) { }
1237
apply(ui_software_info const & info) const1238 virtual bool apply(ui_software_info const &info) const override { return true; }
1239 };
1240
1241
1242 class available_software_filter : public simple_filter_impl_base<software_filter, software_filter::AVAILABLE>
1243 {
1244 public:
available_software_filter(software_filter_data const & data,char const * value,emu_file * file,unsigned indent)1245 available_software_filter(software_filter_data const &data, char const *value, emu_file *file, unsigned indent) { }
1246
apply(ui_software_info const & info) const1247 virtual bool apply(ui_software_info const &info) const override { return info.available; }
1248 };
1249
1250
1251 class unavailable_software_filter : public simple_filter_impl_base<software_filter, software_filter::UNAVAILABLE>
1252 {
1253 public:
unavailable_software_filter(software_filter_data const & data,char const * value,emu_file * file,unsigned indent)1254 unavailable_software_filter(software_filter_data const &data, char const *value, emu_file *file, unsigned indent) { }
1255
apply(ui_software_info const & info) const1256 virtual bool apply(ui_software_info const &info) const override { return !info.available; }
1257 };
1258
1259
1260 class favorite_software_filter : public simple_filter_impl_base<software_filter, software_filter::FAVORITE>
1261 {
1262 public:
favorite_software_filter(software_filter_data const & data,char const * value,emu_file * file,unsigned indent)1263 favorite_software_filter(software_filter_data const &data, char const *value, emu_file *file, unsigned indent)
1264 : m_manager(mame_machine_manager::instance()->favorite())
1265 {
1266 }
1267
apply(ui_software_info const & info) const1268 virtual bool apply(ui_software_info const &info) const override { return m_manager.is_favorite_software(info); }
1269
1270 private:
1271 favorite_manager const &m_manager;
1272 };
1273
1274
1275 class parents_software_filter : public simple_filter_impl_base<software_filter, software_filter::PARENTS>
1276 {
1277 public:
parents_software_filter(software_filter_data const & data,char const * value,emu_file * file,unsigned indent)1278 parents_software_filter(software_filter_data const &data, char const *value, emu_file *file, unsigned indent) { }
1279
apply(ui_software_info const & info) const1280 virtual bool apply(ui_software_info const &info) const override { return info.parentname.empty(); }
1281 };
1282
1283
1284 class clones_software_filter : public simple_filter_impl_base<software_filter, software_filter::CLONES>
1285 {
1286 public:
clones_software_filter(software_filter_data const & data,char const * value,emu_file * file,unsigned indent)1287 clones_software_filter(software_filter_data const &data, char const *value, emu_file *file, unsigned indent) { }
1288
apply(ui_software_info const & info) const1289 virtual bool apply(ui_software_info const &info) const override { return !info.parentname.empty(); }
1290 };
1291
1292
1293 class years_software_filter : public choice_filter_impl_base<software_filter, software_filter::YEAR>
1294 {
1295 public:
years_software_filter(software_filter_data const & data,char const * value,emu_file * file,unsigned indent)1296 years_software_filter(software_filter_data const &data, char const *value, emu_file *file, unsigned indent)
1297 : choice_filter_impl_base<software_filter, software_filter::YEAR>(data.years(), value)
1298 {
1299 }
1300
apply(ui_software_info const & info) const1301 virtual bool apply(ui_software_info const &info) const override { return !have_choices() || (selection_valid() && (selection_text() == info.year)); }
1302 };
1303
1304
1305 class publishers_software_filter : public choice_filter_impl_base<software_filter, software_filter::PUBLISHERS>
1306 {
1307 public:
publishers_software_filter(software_filter_data const & data,char const * value,emu_file * file,unsigned indent)1308 publishers_software_filter(software_filter_data const &data, char const *value, emu_file *file, unsigned indent)
1309 : choice_filter_impl_base<software_filter, software_filter::PUBLISHERS>(data.publishers(), value)
1310 {
1311 }
1312
apply(ui_software_info const & info) const1313 virtual bool apply(ui_software_info const &info) const override
1314 {
1315 if (!have_choices())
1316 return true;
1317 else if (!selection_valid())
1318 return false;
1319
1320 std::string const name(software_filter_data::extract_publisher(info.publisher));
1321 return !name.empty() && (selection_text() == name);
1322 }
1323 };
1324
1325
1326 class supported_software_filter : public simple_filter_impl_base<software_filter, software_filter::SUPPORTED>
1327 {
1328 public:
supported_software_filter(software_filter_data const & data,char const * value,emu_file * file,unsigned indent)1329 supported_software_filter(software_filter_data const &data, char const *value, emu_file *file, unsigned indent) { }
1330
apply(ui_software_info const & info) const1331 virtual bool apply(ui_software_info const &info) const override { return SOFTWARE_SUPPORTED_YES == info.supported; }
1332 };
1333
1334
1335
1336 class partial_supported_software_filter : public simple_filter_impl_base<software_filter, software_filter::PARTIAL_SUPPORTED>
1337 {
1338 public:
partial_supported_software_filter(software_filter_data const & data,char const * value,emu_file * file,unsigned indent)1339 partial_supported_software_filter(software_filter_data const &data, char const *value, emu_file *file, unsigned indent) { }
1340
apply(ui_software_info const & info) const1341 virtual bool apply(ui_software_info const &info) const override { return SOFTWARE_SUPPORTED_PARTIAL == info.supported; }
1342 };
1343
1344
1345 class unsupported_software_filter : public simple_filter_impl_base<software_filter, software_filter::UNSUPPORTED>
1346 {
1347 public:
unsupported_software_filter(software_filter_data const & data,char const * value,emu_file * file,unsigned indent)1348 unsupported_software_filter(software_filter_data const &data, char const *value, emu_file *file, unsigned indent) { }
1349
apply(ui_software_info const & info) const1350 virtual bool apply(ui_software_info const &info) const override { return SOFTWARE_SUPPORTED_NO == info.supported; }
1351 };
1352
1353
1354 class region_software_filter : public choice_filter_impl_base<software_filter, software_filter::REGION>
1355 {
1356 public:
region_software_filter(software_filter_data const & data,char const * value,emu_file * file,unsigned indent)1357 region_software_filter(software_filter_data const &data, char const *value, emu_file *file, unsigned indent)
1358 : choice_filter_impl_base<software_filter, software_filter::REGION>(data.regions(), value)
1359 {
1360 }
1361
apply(ui_software_info const & info) const1362 virtual bool apply(ui_software_info const &info) const override
1363 {
1364 if (!have_choices())
1365 return true;
1366 else if (!selection_valid())
1367 return false;
1368
1369 std::string const name(software_filter_data::extract_region(info.longname));
1370 return !name.empty() && (selection_text() == name);
1371 }
1372 };
1373
1374
1375 class device_type_software_filter : public choice_filter_impl_base<software_filter, software_filter::DEVICE_TYPE>
1376 {
1377 public:
device_type_software_filter(software_filter_data const & data,char const * value,emu_file * file,unsigned indent)1378 device_type_software_filter(software_filter_data const &data, char const *value, emu_file *file, unsigned indent)
1379 : choice_filter_impl_base<software_filter, software_filter::DEVICE_TYPE>(data.device_types(), value)
1380 {
1381 }
1382
apply(ui_software_info const & info) const1383 virtual bool apply(ui_software_info const &info) const override { return !have_choices() || (selection_valid() && (selection_text() == info.devicetype)); }
1384 };
1385
1386
1387 class list_software_filter : public choice_filter_impl_base<software_filter, software_filter::LIST>
1388 {
1389 public:
list_software_filter(software_filter_data const & data,char const * value,emu_file * file,unsigned indent)1390 list_software_filter(software_filter_data const &data, char const *value, emu_file *file, unsigned indent)
1391 : choice_filter_impl_base<software_filter, software_filter::LIST>(data.list_descriptions(), value)
1392 , m_data(data)
1393 {
1394 }
1395
apply(ui_software_info const & info) const1396 virtual bool apply(ui_software_info const &info) const override
1397 {
1398 return !have_choices() || (selection_valid() && (m_data.list_names()[selection_index()] == info.listname));
1399 }
1400
1401 private:
1402 software_filter_data const &m_data;
1403 };
1404
1405
1406
1407 //-------------------------------------------------
1408 // composite software filter
1409 //-------------------------------------------------
1410
1411 class custom_software_filter : public composite_filter_impl_base<custom_software_filter, software_filter, software_filter::CUSTOM>
1412 {
1413 public:
custom_software_filter(software_filter_data const & data,char const * value,emu_file * file,unsigned indent)1414 custom_software_filter(software_filter_data const &data, char const *value, emu_file *file, unsigned indent)
1415 : composite_filter_impl_base<custom_software_filter, software_filter, software_filter::CUSTOM>()
1416 , m_data(data)
1417 {
1418 populate(value, file, indent);
1419 }
1420
create(type n) const1421 ptr create(type n) const { return software_filter::create(n, m_data); }
create(emu_file & file,unsigned indent) const1422 ptr create(emu_file &file, unsigned indent) const { return software_filter::create(file, m_data, indent); }
1423
type_allowed(unsigned pos,type n)1424 static bool type_allowed(unsigned pos, type n)
1425 {
1426 return (FIRST <= n) && (LAST >= n) && (ALL != n) && (CUSTOM != n);
1427 }
1428
types_contradictory(type n,type m)1429 static bool types_contradictory(type n, type m)
1430 {
1431 switch (n)
1432 {
1433 case AVAILABLE: return UNAVAILABLE == m;
1434 case UNAVAILABLE: return AVAILABLE == m;
1435 case PARENTS: return CLONES == m;
1436 case CLONES: return PARENTS == m;
1437 case SUPPORTED: return (PARTIAL_SUPPORTED == m) || (UNSUPPORTED == m);
1438 case PARTIAL_SUPPORTED: return (SUPPORTED == m) || (UNSUPPORTED == m);
1439 case UNSUPPORTED: return (SUPPORTED == m) || (PARTIAL_SUPPORTED == m);
1440
1441 case ALL:
1442 case FAVORITE:
1443 case YEAR:
1444 case PUBLISHERS:
1445 case REGION:
1446 case DEVICE_TYPE:
1447 case LIST:
1448 case CUSTOM:
1449 case COUNT:
1450 break;
1451 }
1452 return false;
1453 }
1454
is_inclusion(type n)1455 static bool is_inclusion(type n)
1456 {
1457 return (YEAR == n) || (PUBLISHERS == n) || (REGION == n) || (DEVICE_TYPE == n) || (LIST == n);
1458 }
1459
1460 private:
1461 software_filter_data const &m_data;
1462 };
1463
1464 } // anonymous namespace
1465
1466
1467
1468 //-------------------------------------------------
1469 // static data for machine filters
1470 //-------------------------------------------------
1471
add_manufacturer(std::string const & manufacturer)1472 void machine_filter_data::add_manufacturer(std::string const &manufacturer)
1473 {
1474 std::string name(extract_manufacturer(manufacturer));
1475 std::vector<std::string>::iterator const pos(std::lower_bound(m_manufacturers.begin(), m_manufacturers.end(), name));
1476 if ((m_manufacturers.end() == pos) || (*pos != name))
1477 m_manufacturers.emplace(pos, std::move(name));
1478 }
1479
add_year(std::string const & year)1480 void machine_filter_data::add_year(std::string const &year)
1481 {
1482 std::vector<std::string>::iterator const pos(std::lower_bound(m_years.begin(), m_years.end(), year));
1483 if ((m_years.end() == pos) || (*pos != year))
1484 m_years.emplace(pos, year);
1485 }
1486
finalise()1487 void machine_filter_data::finalise()
1488 {
1489 std::stable_sort(m_manufacturers.begin(), m_manufacturers.end());
1490 std::stable_sort(m_years.begin(), m_years.end());
1491 }
1492
extract_manufacturer(std::string const & manufacturer)1493 std::string machine_filter_data::extract_manufacturer(std::string const &manufacturer)
1494 {
1495 size_t const found(manufacturer.find('('));
1496 if ((found != std::string::npos) && (found > 0))
1497 return manufacturer.substr(0, found - 1);
1498 else
1499 return manufacturer;
1500 }
1501
set_filter(machine_filter::ptr && filter)1502 void machine_filter_data::set_filter(machine_filter::ptr &&filter)
1503 {
1504 m_filters[filter->get_type()] = std::move(filter);
1505 }
1506
get_filter(machine_filter::type type)1507 machine_filter &machine_filter_data::get_filter(machine_filter::type type)
1508 {
1509 auto it(m_filters.find(type));
1510 if (m_filters.end() == it)
1511 it = m_filters.emplace(type, machine_filter::create(type, *this)).first;
1512
1513 assert(it->second);
1514 return *it->second;
1515 }
1516
get_config_string() const1517 std::string machine_filter_data::get_config_string() const
1518 {
1519 auto const active_filter(m_filters.find(m_current_filter));
1520 if (m_filters.end() != active_filter)
1521 {
1522 char const *const val(active_filter->second->filter_text());
1523 return val ? util::string_format("%s,%s", active_filter->second->config_name(), val) : active_filter->second->config_name();
1524 }
1525 else
1526 {
1527 return machine_filter::config_name(m_current_filter);
1528 }
1529 }
1530
load_ini(emu_file & file)1531 bool machine_filter_data::load_ini(emu_file &file)
1532 {
1533 machine_filter::ptr flt(machine_filter::create(file, *this));
1534 if (flt)
1535 {
1536 // TODO: it should possibly replace an existing item here, but it may be relying on that not happening because it never clears the first start flag
1537 m_current_filter = flt->get_type();
1538 m_filters.emplace(m_current_filter, std::move(flt));
1539 return true;
1540 }
1541 else
1542 {
1543 return false;
1544 }
1545 }
1546
1547
1548 //-------------------------------------------------
1549 // static data for software filters
1550 //-------------------------------------------------
1551
add_region(std::string const & longname)1552 void software_filter_data::add_region(std::string const &longname)
1553 {
1554 std::string name(extract_region(longname));
1555 std::vector<std::string>::iterator const pos(std::lower_bound(m_regions.begin(), m_regions.end(), name));
1556 if ((m_regions.end() == pos) || (*pos != name))
1557 m_regions.emplace(pos, std::move(name));
1558 }
1559
add_publisher(std::string const & publisher)1560 void software_filter_data::add_publisher(std::string const &publisher)
1561 {
1562 std::string name(extract_publisher(publisher));
1563 std::vector<std::string>::iterator const pos(std::lower_bound(m_publishers.begin(), m_publishers.end(), name));
1564 if ((m_publishers.end() == pos) || (*pos != name))
1565 m_publishers.emplace(pos, std::move(name));
1566 }
1567
add_year(std::string const & year)1568 void software_filter_data::add_year(std::string const &year)
1569 {
1570 std::vector<std::string>::iterator const pos(std::lower_bound(m_years.begin(), m_years.end(), year));
1571 if ((m_years.end() == pos) || (*pos != year))
1572 m_years.emplace(pos, year);
1573 }
1574
add_device_type(std::string const & device_type)1575 void software_filter_data::add_device_type(std::string const &device_type)
1576 {
1577 std::vector<std::string>::iterator const pos(std::lower_bound(m_device_types.begin(), m_device_types.end(), device_type));
1578 if ((m_device_types.end() == pos) || (*pos != device_type))
1579 m_device_types.emplace(pos, device_type);
1580 }
1581
add_list(std::string const & name,std::string const & description)1582 void software_filter_data::add_list(std::string const &name, std::string const &description)
1583 {
1584 m_list_names.emplace_back(name);
1585 m_list_descriptions.emplace_back(description);
1586 }
1587
finalise()1588 void software_filter_data::finalise()
1589 {
1590 std::stable_sort(m_regions.begin(), m_regions.end());
1591 std::stable_sort(m_publishers.begin(), m_publishers.end());
1592 std::stable_sort(m_years.begin(), m_years.end());
1593 std::stable_sort(m_device_types.begin(), m_device_types.end());
1594 }
1595
extract_region(std::string const & longname)1596 std::string software_filter_data::extract_region(std::string const &longname)
1597 {
1598 std::string fullname(longname);
1599 strmakelower(fullname);
1600 std::string::size_type const found(fullname.find('('));
1601 if (found != std::string::npos)
1602 {
1603 std::string::size_type const ends(fullname.find_first_not_of("abcdefghijklmnopqrstuvwxyz", found + 1));
1604 std::string const temp(fullname.substr(found + 1, ends - found - 1));
1605 auto const match(std::find_if(
1606 std::begin(SOFTWARE_REGIONS),
1607 std::end(SOFTWARE_REGIONS),
1608 [&temp] (char const *elem) { return temp == elem; }));
1609 if (std::end(SOFTWARE_REGIONS) != match)
1610 return longname.substr(found + 1, (std::string::npos != ends) ? (ends - found - 1) : ends);
1611 }
1612 return "<none>";
1613 }
1614
extract_publisher(std::string const & publisher)1615 std::string software_filter_data::extract_publisher(std::string const &publisher)
1616 {
1617 std::string::size_type const found(publisher.find('('));
1618 return publisher.substr(0, found - ((found && (std::string::npos != found)) ? 1 : 0));
1619 }
1620
1621
1622
1623 //-------------------------------------------------
1624 // public machine filter interface
1625 //-------------------------------------------------
1626
create(type n,machine_filter_data const & data,char const * value,emu_file * file,unsigned indent)1627 machine_filter::ptr machine_filter::create(type n, machine_filter_data const &data, char const *value, emu_file *file, unsigned indent)
1628 {
1629 assert(COUNT > n);
1630 switch (n)
1631 {
1632 case ALL:
1633 return std::make_unique<all_machine_filter>(data, value, file, indent);
1634 case AVAILABLE:
1635 return std::make_unique<available_machine_filter>(data, value, file, indent);
1636 case UNAVAILABLE:
1637 return std::make_unique<unavailable_machine_filter>(data, value, file, indent);
1638 case WORKING:
1639 return std::make_unique<working_machine_filter>(data, value, file, indent);
1640 case NOT_WORKING:
1641 return std::make_unique<not_working_machine_filter>(data, value, file, indent);
1642 case MECHANICAL:
1643 return std::make_unique<mechanical_machine_filter>(data, value, file, indent);
1644 case NOT_MECHANICAL:
1645 return std::make_unique<not_mechanical_machine_filter>(data, value, file, indent);
1646 case CATEGORY:
1647 return std::make_unique<category_machine_filter>(data, value, file, indent);
1648 case FAVORITE:
1649 return std::make_unique<favorite_machine_filter>(data, value, file, indent);
1650 case BIOS:
1651 return std::make_unique<bios_machine_filter>(data, value, file, indent);
1652 case NOT_BIOS:
1653 return std::make_unique<not_bios_machine_filter>(data, value, file, indent);
1654 case PARENTS:
1655 return std::make_unique<parents_machine_filter>(data, value, file, indent);
1656 case CLONES:
1657 return std::make_unique<clones_machine_filter>(data, value, file, indent);
1658 case MANUFACTURER:
1659 return std::make_unique<manufacturer_machine_filter>(data, value, file, indent);
1660 case YEAR:
1661 return std::make_unique<year_machine_filter>(data, value, file, indent);
1662 case SAVE:
1663 return std::make_unique<save_machine_filter>(data, value, file, indent);
1664 case NOSAVE:
1665 return std::make_unique<nosave_machine_filter>(data, value, file, indent);
1666 case CHD:
1667 return std::make_unique<chd_machine_filter>(data, value, file, indent);
1668 case NOCHD:
1669 return std::make_unique<nochd_machine_filter>(data, value, file, indent);
1670 case VERTICAL:
1671 return std::make_unique<vertical_machine_filter>(data, value, file, indent);
1672 case HORIZONTAL:
1673 return std::make_unique<horizontal_machine_filter>(data, value, file, indent);
1674 case CUSTOM:
1675 return std::make_unique<custom_machine_filter>(data, value, file, indent);
1676 case COUNT: // not valid, but needed to suppress warnings
1677 break;
1678 }
1679 return nullptr;
1680 }
1681
create(emu_file & file,machine_filter_data const & data,unsigned indent)1682 machine_filter::ptr machine_filter::create(emu_file &file, machine_filter_data const &data, unsigned indent)
1683 {
1684 char buffer[MAX_CHAR_INFO];
1685 if (!file.gets(buffer, ARRAY_LENGTH(buffer)))
1686 return nullptr;
1687
1688 // split it into a key/value or bail
1689 std::string key(buffer);
1690 for (std::string::size_type i = 0; (2 * indent) > i; ++i)
1691 {
1692 if ((key.length() <= i) || (' ' != key[i]))
1693 return nullptr;
1694 }
1695 key = key.substr(2 * indent);
1696 std::string::size_type const split(key.find(" = "));
1697 if (std::string::npos == split)
1698 return nullptr;
1699 std::string::size_type const nl(key.find_first_of("\r\n", split));
1700 std::string const value(key.substr(split + 3, (std::string::npos == nl) ? nl : (nl - split - 3)));
1701 key.resize(split);
1702
1703 // look for a filter type that matches
1704 for (type n = FIRST; COUNT > n; ++n)
1705 {
1706 if (key == config_name(n))
1707 return create(n, data, value.c_str(), &file, indent);
1708 }
1709 return nullptr;
1710 }
1711
config_name(type n)1712 char const *machine_filter::config_name(type n)
1713 {
1714 assert(COUNT > n);
1715 return MACHINE_FILTER_NAMES[n];
1716 }
1717
display_name(type n)1718 char const *machine_filter::display_name(type n)
1719 {
1720 assert(COUNT > n);
1721 return _(MACHINE_FILTER_NAMES[n]);
1722 }
1723
machine_filter()1724 machine_filter::machine_filter()
1725 {
1726 }
1727
1728
1729 //-------------------------------------------------
1730 // public software filter interface
1731 //-------------------------------------------------
1732
config_name(type n)1733 char const *software_filter::config_name(type n)
1734 {
1735 assert(COUNT > n);
1736 return SOFTWARE_FILTER_NAMES[n];
1737 }
1738
display_name(type n)1739 char const *software_filter::display_name(type n)
1740 {
1741 assert(COUNT > n);
1742 return _(SOFTWARE_FILTER_NAMES[n]);
1743 }
1744
software_filter()1745 software_filter::software_filter()
1746 {
1747 }
1748
create(type n,software_filter_data const & data,char const * value,emu_file * file,unsigned indent)1749 software_filter::ptr software_filter::create(type n, software_filter_data const &data, char const *value, emu_file *file, unsigned indent)
1750 {
1751 assert(COUNT > n);
1752 switch (n)
1753 {
1754 case ALL:
1755 return std::make_unique<all_software_filter>(data, value, file, indent);
1756 case AVAILABLE:
1757 return std::make_unique<available_software_filter>(data, value, file, indent);
1758 case UNAVAILABLE:
1759 return std::make_unique<unavailable_software_filter>(data, value, file, indent);
1760 case FAVORITE:
1761 return std::make_unique<favorite_software_filter>(data, value, file, indent);
1762 case PARENTS:
1763 return std::make_unique<parents_software_filter>(data, value, file, indent);
1764 case CLONES:
1765 return std::make_unique<clones_software_filter>(data, value, file, indent);
1766 case YEAR:
1767 return std::make_unique<years_software_filter>(data, value, file, indent);
1768 case PUBLISHERS:
1769 return std::make_unique<publishers_software_filter>(data, value, file, indent);
1770 case SUPPORTED:
1771 return std::make_unique<supported_software_filter>(data, value, file, indent);
1772 case PARTIAL_SUPPORTED:
1773 return std::make_unique<partial_supported_software_filter>(data, value, file, indent);
1774 case UNSUPPORTED:
1775 return std::make_unique<unsupported_software_filter>(data, value, file, indent);
1776 case REGION:
1777 return std::make_unique<region_software_filter>(data, value, file, indent);
1778 case DEVICE_TYPE:
1779 return std::make_unique<device_type_software_filter>(data, value, file, indent);
1780 case LIST:
1781 return std::make_unique<list_software_filter>(data, value, file, indent);
1782 case CUSTOM:
1783 return std::make_unique<custom_software_filter>(data, value, file, indent);
1784 case COUNT: // not valid, but needed to suppress warnings
1785 break;
1786 }
1787 return nullptr;
1788 }
1789
create(emu_file & file,software_filter_data const & data,unsigned indent)1790 software_filter::ptr software_filter::create(emu_file &file, software_filter_data const &data, unsigned indent)
1791 {
1792 char buffer[MAX_CHAR_INFO];
1793 if (!file.gets(buffer, ARRAY_LENGTH(buffer)))
1794 return nullptr;
1795
1796 // split it into a key/value or bail
1797 std::string key(buffer);
1798 for (std::string::size_type i = 0; (2 * indent) > i; ++i)
1799 {
1800 if ((key.length() <= i) || (' ' != key[i]))
1801 return nullptr;
1802 }
1803 key = key.substr(2 * indent);
1804 std::string::size_type const split(key.find(" = "));
1805 if (std::string::npos == split)
1806 return nullptr;
1807 std::string::size_type const nl(key.find_first_of("\r\n", split));
1808 std::string const value(key.substr(split + 3, (std::string::npos == nl) ? nl : (nl - split - 3)));
1809 key.resize(split);
1810
1811 // look for a filter type that matches
1812 for (type n = FIRST; COUNT > n; ++n)
1813 {
1814 if (key == config_name(n))
1815 return create(n, data, value.c_str(), &file, indent);
1816 }
1817 return nullptr;
1818 }
1819
1820 } // namesapce ui
1821
1822
1823 extern const char UI_VERSION_TAG[];
1824 const char UI_VERSION_TAG[] = "# UI INFO ";
1825
1826 // Globals
1827 uint8_t ui_globals::rpanel = 0;
1828 uint8_t ui_globals::curdats_view = 0;
1829 uint8_t ui_globals::cur_sw_dats_total = 0;
1830 uint8_t ui_globals::curdats_total = 0;
1831 uint8_t ui_globals::cur_sw_dats_view = 0;
1832 bool ui_globals::reset = false;
1833 int ui_globals::visible_main_lines = 0;
1834 int ui_globals::visible_sw_lines = 0;
1835 uint16_t ui_globals::panels_status = 0;
1836
chartrimcarriage(char str[])1837 char* chartrimcarriage(char str[])
1838 {
1839 char *pstr = strrchr(str, '\n');
1840 if (pstr)
1841 str[pstr - str] = '\0';
1842 pstr = strrchr(str, '\r');
1843 if (pstr)
1844 str[pstr - str] = '\0';
1845 return str;
1846 }
1847
strensure(const char * s)1848 const char* strensure(const char* s)
1849 {
1850 return s == nullptr ? "" : s;
1851 }
1852
getprecisionchr(const char * s)1853 int getprecisionchr(const char* s)
1854 {
1855 int precision = 1;
1856 char *dp = const_cast<char *>(strchr(s, '.'));
1857 if (dp != nullptr)
1858 precision = strlen(s) - (dp - s) - 1;
1859 return precision;
1860 }
1861
tokenize(const std::string & text,char sep)1862 std::vector<std::string> tokenize(const std::string &text, char sep)
1863 {
1864 std::vector<std::string> tokens;
1865 tokens.reserve(64);
1866 std::size_t start = 0, end = 0;
1867 while ((end = text.find(sep, start)) != std::string::npos)
1868 {
1869 std::string temp = text.substr(start, end - start);
1870 if (!temp.empty()) tokens.push_back(temp);
1871 start = end + 1;
1872 }
1873 std::string temp = text.substr(start);
1874 if (!temp.empty()) tokens.push_back(temp);
1875 return tokens;
1876 }
1877
1878
ui_software_info(software_info const & info,software_part const & p,game_driver const & d,std::string const & li,std::string const & is,std::string const & de)1879 ui_software_info::ui_software_info(
1880 software_info const &info,
1881 software_part const &p,
1882 game_driver const &d,
1883 std::string const &li,
1884 std::string const &is,
1885 std::string const &de)
1886 : shortname(info.shortname()), longname(info.longname()), parentname(info.parentname())
1887 , year(info.year()), publisher(info.publisher())
1888 , supported(info.supported())
1889 , part(p.name())
1890 , driver(&d)
1891 , listname(li), interface(p.interface()), instance(is)
1892 , startempty(0)
1893 , parentlongname()
1894 , usage()
1895 , devicetype(de)
1896 , available(false)
1897 {
1898 for (feature_list_item const &feature : info.other_info())
1899 {
1900 if (feature.name() == "usage")
1901 {
1902 usage = feature.value();
1903 break;
1904 }
1905 }
1906 }
1907
1908 // info for starting empty
ui_software_info(game_driver const & d)1909 ui_software_info::ui_software_info(game_driver const &d)
1910 : shortname(d.name), longname(d.type.fullname()), driver(&d), startempty(1), available(true)
1911 {
1912 }
1913