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