mirror of
https://github.com/holub/mame
synced 2025-04-27 02:33:13 +03:00
2155 lines
67 KiB
C++
2155 lines
67 KiB
C++
// license:BSD-3-Clause
|
|
// copyright-holders:Dankan1890
|
|
/***************************************************************************
|
|
|
|
ui/selsoft.cpp
|
|
|
|
UI softwares menu.
|
|
|
|
***************************************************************************/
|
|
|
|
#include "emu.h"
|
|
#include "emuopts.h"
|
|
#include "ui/ui.h"
|
|
#include "ui/menu.h"
|
|
#include "uiinput.h"
|
|
#include "audit.h"
|
|
#include "ui/selsoft.h"
|
|
#include "ui/datmenu.h"
|
|
#include "ui/datfile.h"
|
|
#include "ui/inifile.h"
|
|
#include "ui/selector.h"
|
|
#include "rendfont.h"
|
|
#include "rendutil.h"
|
|
#include "softlist.h"
|
|
#include <algorithm>
|
|
|
|
std::string reselect_last::driver;
|
|
std::string reselect_last::software;
|
|
std::string reselect_last::swlist;
|
|
bool reselect_last::m_reselect = false;
|
|
static const char *region_lists[] = { "arab", "arg", "asia", "aus", "aut", "bel", "blr", "bra", "can", "chi", "chn", "cze", "den",
|
|
"ecu", "esp", "euro", "fin", "fra", "gbr", "ger", "gre", "hkg", "hun", "irl", "isr",
|
|
"isv", "ita", "jpn", "kaz", "kor", "lat", "lux", "mex", "ned", "nld", "nor", "nzl",
|
|
"pol", "rus", "slo", "spa", "sui", "swe", "tha", "tpe", "tw", "uk", "ukr", "usa" };
|
|
|
|
//-------------------------------------------------
|
|
// compares two items in the software list and
|
|
// sort them by parent-clone
|
|
//-------------------------------------------------
|
|
|
|
bool compare_software(ui_software_info a, ui_software_info b)
|
|
{
|
|
ui_software_info *x = &a;
|
|
ui_software_info *y = &b;
|
|
|
|
bool clonex = (x->parentname[0] != '\0');
|
|
bool cloney = (y->parentname[0] != '\0');
|
|
|
|
if (!clonex && !cloney)
|
|
return (strmakelower(x->longname) < strmakelower(y->longname));
|
|
|
|
std::string cx(x->parentlongname), cy(y->parentlongname);
|
|
|
|
if (clonex && cx[0] == '\0')
|
|
clonex = false;
|
|
|
|
if (cloney && cy[0] == '\0')
|
|
cloney = false;
|
|
|
|
if (!clonex && !cloney)
|
|
return (strmakelower(x->longname) < strmakelower(y->longname));
|
|
else if (clonex && cloney)
|
|
{
|
|
if (!core_stricmp(x->parentname.c_str(), y->parentname.c_str()) && !core_stricmp(x->instance.c_str(), y->instance.c_str()))
|
|
return (strmakelower(x->longname) < strmakelower(y->longname));
|
|
else
|
|
return (strmakelower(cx) < strmakelower(cy));
|
|
}
|
|
else if (!clonex && cloney)
|
|
{
|
|
if (!core_stricmp(x->shortname.c_str(), y->parentname.c_str()) && !core_stricmp(x->instance.c_str(), y->instance.c_str()))
|
|
return true;
|
|
else
|
|
return (strmakelower(x->longname) < strmakelower(cy));
|
|
}
|
|
else
|
|
{
|
|
if (!core_stricmp(x->parentname.c_str(), y->shortname.c_str()) && !core_stricmp(x->instance.c_str(), y->instance.c_str()))
|
|
return false;
|
|
else
|
|
return (strmakelower(cx) < strmakelower(y->longname));
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
// get bios count
|
|
//-------------------------------------------------
|
|
|
|
bool has_multiple_bios(const game_driver *driver, std::vector<s_bios> &biosname)
|
|
{
|
|
if (driver->rom == nullptr)
|
|
return 0;
|
|
|
|
std::string default_name;
|
|
for (const rom_entry *rom = driver->rom; !ROMENTRY_ISEND(rom); ++rom)
|
|
if (ROMENTRY_ISDEFAULT_BIOS(rom))
|
|
default_name = ROM_GETNAME(rom);
|
|
|
|
for (const rom_entry *rom = driver->rom; !ROMENTRY_ISEND(rom); ++rom)
|
|
{
|
|
if (ROMENTRY_ISSYSTEM_BIOS(rom))
|
|
{
|
|
std::string name(ROM_GETHASHDATA(rom));
|
|
std::string bname(ROM_GETNAME(rom));
|
|
int bios_flags = ROM_GETBIOSFLAGS(rom);
|
|
|
|
if (bname == default_name)
|
|
{
|
|
name.append(_(" (default)"));
|
|
biosname.emplace(biosname.begin(), name, bios_flags - 1);
|
|
}
|
|
else
|
|
biosname.emplace_back(name, bios_flags - 1);
|
|
}
|
|
}
|
|
return (biosname.size() > 1);
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
// ctor
|
|
//-------------------------------------------------
|
|
|
|
ui_menu_select_software::ui_menu_select_software(running_machine &machine, render_container *container, const game_driver *driver) : ui_menu(machine, container)
|
|
{
|
|
if (reselect_last::get())
|
|
reselect_last::set(false);
|
|
|
|
sw_filters::actual = 0;
|
|
highlight = 0;
|
|
|
|
m_driver = driver;
|
|
build_software_list();
|
|
load_sw_custom_filters();
|
|
|
|
ui_globals::curimage_view = SNAPSHOT_VIEW;
|
|
ui_globals::switch_image = true;
|
|
ui_globals::cur_sw_dats_view = UI_FIRST_LOAD;
|
|
|
|
std::string error_string;
|
|
machine.options().set_value(OPTION_SOFTWARENAME, "", OPTION_PRIORITY_CMDLINE, error_string);
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
// dtor
|
|
//-------------------------------------------------
|
|
|
|
ui_menu_select_software::~ui_menu_select_software()
|
|
{
|
|
ui_globals::curimage_view = CABINETS_VIEW;
|
|
ui_globals::switch_image = true;
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
// handle
|
|
//-------------------------------------------------
|
|
|
|
void ui_menu_select_software::handle()
|
|
{
|
|
if (m_prev_selected == nullptr)
|
|
m_prev_selected = item[0].ref;
|
|
|
|
bool check_filter = false;
|
|
|
|
// ignore pause keys by swallowing them before we process the menu
|
|
machine().ui_input().pressed(IPT_UI_PAUSE);
|
|
|
|
// process the menu
|
|
const ui_menu_event *m_event = process(UI_MENU_PROCESS_LR_REPEAT);
|
|
|
|
if (m_event != nullptr && m_event->itemref != nullptr)
|
|
{
|
|
// reset the error on any future m_event
|
|
if (ui_error)
|
|
ui_error = false;
|
|
|
|
// handle selections
|
|
else if (m_event->iptkey == IPT_UI_SELECT)
|
|
{
|
|
if (m_focus == focused_menu::main)
|
|
{
|
|
inkey_select(m_event);
|
|
}
|
|
else if (m_focus == focused_menu::left)
|
|
{
|
|
l_sw_hover = highlight;
|
|
check_filter = true;
|
|
m_prev_selected = nullptr;
|
|
}
|
|
}
|
|
|
|
// handle UI_LEFT
|
|
else if (m_event->iptkey == IPT_UI_LEFT)
|
|
{
|
|
// Images
|
|
if (ui_globals::rpanel == RP_IMAGES && ui_globals::curimage_view > FIRST_VIEW)
|
|
{
|
|
ui_globals::curimage_view--;
|
|
ui_globals::switch_image = true;
|
|
ui_globals::default_image = false;
|
|
}
|
|
|
|
// Infos
|
|
else if (ui_globals::rpanel == RP_INFOS && ui_globals::cur_sw_dats_view > 0)
|
|
{
|
|
ui_globals::cur_sw_dats_view--;
|
|
topline_datsview = 0;
|
|
}
|
|
}
|
|
|
|
// handle UI_RIGHT
|
|
else if (m_event->iptkey == IPT_UI_RIGHT)
|
|
{
|
|
// Images
|
|
if (ui_globals::rpanel == RP_IMAGES && ui_globals::curimage_view < LAST_VIEW)
|
|
{
|
|
ui_globals::curimage_view++;
|
|
ui_globals::switch_image = true;
|
|
ui_globals::default_image = false;
|
|
}
|
|
|
|
// Infos
|
|
else if (ui_globals::rpanel == RP_INFOS && ui_globals::cur_sw_dats_view < 1)
|
|
{
|
|
ui_globals::cur_sw_dats_view++;
|
|
topline_datsview = 0;
|
|
}
|
|
}
|
|
|
|
// handle UI_UP_FILTER
|
|
else if (m_event->iptkey == IPT_UI_UP_FILTER && highlight > UI_SW_FIRST)
|
|
{
|
|
highlight--;
|
|
}
|
|
|
|
// handle UI_DOWN_FILTER
|
|
else if (m_event->iptkey == IPT_UI_DOWN_FILTER && highlight < UI_SW_LAST)
|
|
{
|
|
highlight++;
|
|
}
|
|
|
|
// handle UI_DATS
|
|
else if (m_event->iptkey == IPT_UI_DATS && machine().ui().options().enabled_dats())
|
|
{
|
|
ui_software_info *ui_swinfo = (ui_software_info *)m_event->itemref;
|
|
datfile_manager &mdat = machine().datfile();
|
|
|
|
if (ui_swinfo->startempty == 1 && mdat.has_history(ui_swinfo->driver))
|
|
ui_menu::stack_push(global_alloc_clear<ui_menu_dats_view>(machine(), container, ui_swinfo->driver));
|
|
else if (mdat.has_software(ui_swinfo->listname, ui_swinfo->shortname, ui_swinfo->parentname) || !ui_swinfo->usage.empty())
|
|
ui_menu::stack_push(global_alloc_clear<ui_menu_dats_view>(machine(), container, ui_swinfo));
|
|
}
|
|
|
|
// handle UI_LEFT_PANEL
|
|
else if (m_event->iptkey == IPT_UI_LEFT_PANEL)
|
|
ui_globals::rpanel = RP_IMAGES;
|
|
|
|
// handle UI_RIGHT_PANEL
|
|
else if (m_event->iptkey == IPT_UI_RIGHT_PANEL)
|
|
ui_globals::rpanel = RP_INFOS;
|
|
|
|
// escape pressed with non-empty text clears the text
|
|
else if (m_event->iptkey == IPT_UI_CANCEL && m_search[0] != 0)
|
|
{
|
|
m_search[0] = '\0';
|
|
reset(UI_MENU_RESET_SELECT_FIRST);
|
|
}
|
|
|
|
// handle UI_FAVORITES
|
|
else if (m_event->iptkey == IPT_UI_FAVORITES)
|
|
{
|
|
ui_software_info *swinfo = (ui_software_info *)m_event->itemref;
|
|
|
|
if ((FPTR)swinfo > 2)
|
|
{
|
|
if (!machine().favorite().isgame_favorite(*swinfo))
|
|
{
|
|
machine().favorite().add_favorite_game(*swinfo);
|
|
machine().popmessage(_("%s\n added to favorites list."), swinfo->longname.c_str());
|
|
}
|
|
|
|
else
|
|
{
|
|
machine().popmessage(_("%s\n removed from favorites list."), swinfo->longname.c_str());
|
|
machine().favorite().remove_favorite_game();
|
|
}
|
|
}
|
|
}
|
|
|
|
// typed characters append to the buffer
|
|
else if (m_event->iptkey == IPT_SPECIAL)
|
|
inkey_special(m_event);
|
|
|
|
else if (m_event->iptkey == IPT_OTHER)
|
|
{
|
|
highlight = l_sw_hover;
|
|
check_filter = true;
|
|
m_prev_selected = nullptr;
|
|
}
|
|
|
|
else if (m_event->iptkey == IPT_UI_CONFIGURE)
|
|
inkey_configure(m_event);
|
|
}
|
|
|
|
if (m_event != nullptr && m_event->itemref == nullptr)
|
|
{
|
|
|
|
if (m_event->iptkey == IPT_UI_CONFIGURE)
|
|
inkey_configure(m_event);
|
|
|
|
// handle UI_LEFT
|
|
else if (m_event->iptkey == IPT_UI_LEFT)
|
|
{
|
|
// Images
|
|
if (ui_globals::rpanel == RP_IMAGES && ui_globals::curimage_view > FIRST_VIEW)
|
|
{
|
|
ui_globals::curimage_view--;
|
|
ui_globals::switch_image = true;
|
|
ui_globals::default_image = false;
|
|
}
|
|
|
|
// Infos
|
|
else if (ui_globals::rpanel == RP_INFOS && ui_globals::cur_sw_dats_view > 0)
|
|
{
|
|
ui_globals::cur_sw_dats_view--;
|
|
topline_datsview = 0;
|
|
}
|
|
}
|
|
|
|
// handle UI_RIGHT
|
|
else if (m_event->iptkey == IPT_UI_RIGHT)
|
|
{
|
|
// Images
|
|
if (ui_globals::rpanel == RP_IMAGES && ui_globals::curimage_view < LAST_VIEW)
|
|
{
|
|
ui_globals::curimage_view++;
|
|
ui_globals::switch_image = true;
|
|
ui_globals::default_image = false;
|
|
}
|
|
|
|
// Infos
|
|
else if (ui_globals::rpanel == RP_INFOS && ui_globals::cur_sw_dats_view < 1)
|
|
{
|
|
ui_globals::cur_sw_dats_view++;
|
|
topline_datsview = 0;
|
|
}
|
|
}
|
|
|
|
// handle UI_LEFT_PANEL
|
|
else if (m_event->iptkey == IPT_UI_LEFT_PANEL)
|
|
ui_globals::rpanel = RP_IMAGES;
|
|
|
|
// handle UI_RIGHT_PANEL
|
|
else if (m_event->iptkey == IPT_UI_RIGHT_PANEL)
|
|
ui_globals::rpanel = RP_INFOS;
|
|
|
|
// handle UI_UP_FILTER
|
|
else if (m_event->iptkey == IPT_UI_UP_FILTER && highlight > UI_SW_FIRST)
|
|
{
|
|
highlight--;
|
|
}
|
|
|
|
// handle UI_DOWN_FILTER
|
|
else if (m_event->iptkey == IPT_UI_DOWN_FILTER && highlight < UI_SW_LAST)
|
|
{
|
|
highlight++;
|
|
}
|
|
else if (m_event->iptkey == IPT_UI_SELECT && m_focus == focused_menu::left)
|
|
{
|
|
l_sw_hover = highlight;
|
|
check_filter = true;
|
|
m_prev_selected = nullptr;
|
|
}
|
|
|
|
}
|
|
|
|
// if we're in an error state, overlay an error message
|
|
if (ui_error)
|
|
machine().ui().draw_text_box(container, _("The selected software is missing one or more required files. "
|
|
"Please select a different software.\n\nPress any key (except ESC) to continue."),
|
|
JUSTIFY_CENTER, 0.5f, 0.5f, UI_RED_COLOR);
|
|
|
|
// handle filters selection from key shortcuts
|
|
if (check_filter)
|
|
{
|
|
m_search[0] = '\0';
|
|
switch (l_sw_hover)
|
|
{
|
|
case UI_SW_REGION:
|
|
ui_menu::stack_push(global_alloc_clear<ui_menu_selector>(machine(), container, m_filter.region.ui,
|
|
m_filter.region.actual, SELECTOR_SOFTWARE, l_sw_hover));
|
|
break;
|
|
case UI_SW_YEARS:
|
|
ui_menu::stack_push(global_alloc_clear<ui_menu_selector>(machine(), container, m_filter.year.ui,
|
|
m_filter.year.actual, SELECTOR_SOFTWARE, l_sw_hover));
|
|
break;
|
|
case UI_SW_LIST:
|
|
ui_menu::stack_push(global_alloc_clear<ui_menu_selector>(machine(), container, m_filter.swlist.description,
|
|
m_filter.swlist.actual, SELECTOR_SOFTWARE, l_sw_hover));
|
|
break;
|
|
case UI_SW_TYPE:
|
|
ui_menu::stack_push(global_alloc_clear<ui_menu_selector>(machine(), container, m_filter.type.ui,
|
|
m_filter.type.actual, SELECTOR_SOFTWARE, l_sw_hover));
|
|
break;
|
|
case UI_SW_PUBLISHERS:
|
|
ui_menu::stack_push(global_alloc_clear<ui_menu_selector>(machine(), container, m_filter.publisher.ui,
|
|
m_filter.publisher.actual, SELECTOR_SOFTWARE, l_sw_hover));
|
|
break;
|
|
case UI_SW_CUSTOM:
|
|
sw_filters::actual = l_sw_hover;
|
|
ui_menu::stack_push(global_alloc_clear<ui_menu_swcustom_filter>(machine(), container, m_driver, m_filter));
|
|
break;
|
|
default:
|
|
sw_filters::actual = l_sw_hover;
|
|
reset(UI_MENU_RESET_SELECT_FIRST);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
// populate
|
|
//-------------------------------------------------
|
|
|
|
void ui_menu_select_software::populate()
|
|
{
|
|
UINT32 flags_ui = MENU_FLAG_UI_SWLIST | MENU_FLAG_LEFT_ARROW | MENU_FLAG_RIGHT_ARROW;
|
|
m_has_empty_start = true;
|
|
int old_software = -1;
|
|
|
|
machine_config config(*m_driver, machine().options());
|
|
image_interface_iterator iter(config.root_device());
|
|
|
|
for (device_image_interface *image = iter.first(); image != nullptr; image = iter.next())
|
|
if (image->filename() == nullptr && image->must_be_loaded())
|
|
{
|
|
m_has_empty_start = false;
|
|
break;
|
|
}
|
|
|
|
// no active search
|
|
if (m_search[0] == 0)
|
|
{
|
|
// if the device can be loaded empty, add an item
|
|
if (m_has_empty_start)
|
|
item_append("[Start empty]", nullptr, flags_ui, (void *)&m_swinfo[0]);
|
|
|
|
m_displaylist.clear();
|
|
m_tmp.clear();
|
|
|
|
switch (sw_filters::actual)
|
|
{
|
|
case UI_SW_PUBLISHERS:
|
|
build_list(m_tmp, m_filter.publisher.ui[m_filter.publisher.actual].c_str());
|
|
break;
|
|
|
|
case UI_SW_LIST:
|
|
build_list(m_tmp, m_filter.swlist.name[m_filter.swlist.actual].c_str());
|
|
break;
|
|
|
|
case UI_SW_YEARS:
|
|
build_list(m_tmp, m_filter.year.ui[m_filter.year.actual].c_str());
|
|
break;
|
|
|
|
case UI_SW_TYPE:
|
|
build_list(m_tmp, m_filter.type.ui[m_filter.type.actual].c_str());
|
|
break;
|
|
|
|
case UI_SW_REGION:
|
|
build_list(m_tmp, m_filter.region.ui[m_filter.region.actual].c_str());
|
|
break;
|
|
|
|
case UI_SW_CUSTOM:
|
|
build_custom();
|
|
break;
|
|
|
|
default:
|
|
build_list(m_tmp);
|
|
break;
|
|
}
|
|
|
|
// iterate over entries
|
|
for (size_t curitem = 0; curitem < m_displaylist.size(); ++curitem)
|
|
{
|
|
if (reselect_last::software == "[Start empty]" && !reselect_last::driver.empty())
|
|
old_software = 0;
|
|
|
|
else if (m_displaylist[curitem]->shortname == reselect_last::software && m_displaylist[curitem]->listname == reselect_last::swlist)
|
|
old_software = m_has_empty_start ? curitem + 1 : curitem;
|
|
|
|
item_append(m_displaylist[curitem]->longname.c_str(), m_displaylist[curitem]->devicetype.c_str(),
|
|
m_displaylist[curitem]->parentname.empty() ? flags_ui : (MENU_FLAG_INVERT | flags_ui), (void *)m_displaylist[curitem]);
|
|
}
|
|
}
|
|
|
|
else
|
|
{
|
|
find_matches(m_search, VISIBLE_GAMES_IN_SEARCH);
|
|
|
|
for (int curitem = 0; m_searchlist[curitem] != nullptr; ++curitem)
|
|
item_append(m_searchlist[curitem]->longname.c_str(), m_searchlist[curitem]->devicetype.c_str(),
|
|
m_searchlist[curitem]->parentname.empty() ? flags_ui : (MENU_FLAG_INVERT | flags_ui),
|
|
(void *)m_searchlist[curitem]);
|
|
}
|
|
|
|
item_append(MENU_SEPARATOR_ITEM, nullptr, flags_ui, nullptr);
|
|
|
|
// configure the custom rendering
|
|
customtop = 4.0f * machine().ui().get_line_height() + 5.0f * UI_BOX_TB_BORDER;
|
|
custombottom = 5.0f * machine().ui().get_line_height() + 4.0f * UI_BOX_TB_BORDER;
|
|
|
|
if (old_software != -1)
|
|
{
|
|
selected = old_software;
|
|
top_line = selected - (ui_globals::visible_sw_lines / 2);
|
|
}
|
|
|
|
reselect_last::reset();
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
// build a list of softwares
|
|
//-------------------------------------------------
|
|
|
|
void ui_menu_select_software::build_software_list()
|
|
{
|
|
// add start empty item
|
|
m_swinfo.emplace_back(m_driver->name, m_driver->description, "", "", "", 0, "", m_driver, "", "", "", 1, "", "", "", true);
|
|
|
|
machine_config config(*m_driver, machine().options());
|
|
software_list_device_iterator deviter(config.root_device());
|
|
|
|
// iterate thru all software lists
|
|
for (software_list_device *swlist = deviter.first(); swlist != nullptr; swlist = deviter.next())
|
|
{
|
|
m_filter.swlist.name.push_back(swlist->list_name());
|
|
m_filter.swlist.description.push_back(swlist->description());
|
|
for (software_info *swinfo = swlist->first_software_info(); swinfo != nullptr; swinfo = swinfo->next())
|
|
{
|
|
software_part *part = swinfo->first_part();
|
|
if (part->is_compatible(*swlist))
|
|
{
|
|
const char *instance_name = nullptr;
|
|
const char *type_name = nullptr;
|
|
ui_software_info tmpmatches;
|
|
image_interface_iterator imgiter(config.root_device());
|
|
for (device_image_interface *image = imgiter.first(); image != nullptr; image = imgiter.next())
|
|
{
|
|
const char *interface = image->image_interface();
|
|
if (interface != nullptr && part->matches_interface(interface))
|
|
{
|
|
instance_name = image->instance_name();
|
|
if (instance_name != nullptr)
|
|
tmpmatches.instance = image->instance_name();
|
|
|
|
type_name = image->image_type_name();
|
|
if (type_name != nullptr)
|
|
tmpmatches.devicetype = type_name;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (instance_name == nullptr || type_name == nullptr)
|
|
continue;
|
|
|
|
tmpmatches.shortname = strensure(swinfo->shortname());
|
|
tmpmatches.longname = strensure(swinfo->longname());
|
|
tmpmatches.parentname = strensure(swinfo->parentname());
|
|
tmpmatches.year = strensure(swinfo->year());
|
|
tmpmatches.publisher = strensure(swinfo->publisher());
|
|
tmpmatches.supported = swinfo->supported();
|
|
tmpmatches.part = strensure(part->name());
|
|
tmpmatches.driver = m_driver;
|
|
tmpmatches.listname = strensure(swlist->list_name());
|
|
tmpmatches.interface = strensure(part->interface());
|
|
tmpmatches.startempty = 0;
|
|
tmpmatches.parentlongname.clear();
|
|
tmpmatches.usage.clear();
|
|
tmpmatches.available = false;
|
|
|
|
for (feature_list_item *flist = swinfo->other_info(); flist != nullptr; flist = flist->next())
|
|
if (!strcmp(flist->name(), "usage"))
|
|
tmpmatches.usage = flist->value();
|
|
|
|
m_swinfo.push_back(tmpmatches);
|
|
m_filter.region.set(tmpmatches.longname);
|
|
m_filter.publisher.set(tmpmatches.publisher);
|
|
m_filter.year.set(tmpmatches.year);
|
|
m_filter.type.set(tmpmatches.devicetype);
|
|
}
|
|
}
|
|
}
|
|
m_displaylist.resize(m_swinfo.size() + 1);
|
|
|
|
// retrieve and set the long name of software for parents
|
|
for (size_t y = 1; y < m_swinfo.size(); ++y)
|
|
{
|
|
if (!m_swinfo[y].parentname.empty())
|
|
{
|
|
std::string lparent(m_swinfo[y].parentname);
|
|
bool found = false;
|
|
|
|
// first scan backward
|
|
for (int x = y; x > 0; --x)
|
|
if (lparent == m_swinfo[x].shortname && m_swinfo[y].listname == m_swinfo[x].listname)
|
|
{
|
|
m_swinfo[y].parentlongname = m_swinfo[x].longname;
|
|
found = true;
|
|
break;
|
|
}
|
|
|
|
// not found? then scan forward
|
|
for (size_t x = y; !found && x < m_swinfo.size(); ++x)
|
|
if (lparent == m_swinfo[x].shortname && m_swinfo[y].listname == m_swinfo[x].listname)
|
|
{
|
|
m_swinfo[y].parentlongname = m_swinfo[x].longname;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
std::string searchstr, curpath;
|
|
const osd_directory_entry *dir;
|
|
for (auto & elem : m_filter.swlist.name)
|
|
{
|
|
path_iterator path(machine().options().media_path());
|
|
while (path.next(curpath))
|
|
{
|
|
searchstr.assign(curpath).append(PATH_SEPARATOR).append(elem).append(";");
|
|
file_enumerator fpath(searchstr.c_str());
|
|
|
|
// iterate while we get new objects
|
|
while ((dir = fpath.next()) != nullptr)
|
|
{
|
|
std::string name;
|
|
if (dir->type == ENTTYPE_FILE)
|
|
name = core_filename_extract_base(dir->name, true);
|
|
else if (dir->type == ENTTYPE_DIR && strcmp(dir->name, ".") != 0)
|
|
name = dir->name;
|
|
else
|
|
continue;
|
|
|
|
strmakelower(name);
|
|
for (auto & yelem : m_swinfo)
|
|
if (yelem.shortname == name && yelem.listname == elem)
|
|
{
|
|
yelem.available = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// sort array
|
|
std::stable_sort(m_swinfo.begin() + 1, m_swinfo.end(), compare_software);
|
|
std::stable_sort(m_filter.region.ui.begin(), m_filter.region.ui.end());
|
|
std::stable_sort(m_filter.year.ui.begin(), m_filter.year.ui.end());
|
|
std::stable_sort(m_filter.type.ui.begin(), m_filter.type.ui.end());
|
|
std::stable_sort(m_filter.publisher.ui.begin(), m_filter.publisher.ui.end());
|
|
|
|
for (size_t x = 1; x < m_swinfo.size(); ++x)
|
|
m_sortedlist.push_back(&m_swinfo[x]);
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
// perform our special rendering
|
|
//-------------------------------------------------
|
|
|
|
void ui_menu_select_software::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2)
|
|
{
|
|
ui_software_info *swinfo = (selectedref != nullptr) ? (ui_software_info *)selectedref : ((m_prev_selected != nullptr) ? (ui_software_info *)m_prev_selected : nullptr);
|
|
const game_driver *driver = nullptr;
|
|
ui_manager &mui = machine().ui();
|
|
float width;
|
|
std::string tempbuf[5], filtered;
|
|
rgb_t color = UI_BACKGROUND_COLOR;
|
|
bool isstar = false;
|
|
float tbarspace = mui.get_line_height();
|
|
float text_size = 1.0f;
|
|
|
|
// determine the text for the header
|
|
int vis_item = (m_search[0] != 0) ? visible_items : (m_has_empty_start ? visible_items - 1 : visible_items);
|
|
tempbuf[0] = string_format(_("%1$s %2$s ( %3$d / %4$d softwares )"), emulator_info::get_appname(), bare_build_version, vis_item, m_swinfo.size() - 1);
|
|
tempbuf[1] = string_format(_("Driver: \"%1$s\" software list "), m_driver->description);
|
|
|
|
if (sw_filters::actual == UI_SW_REGION && m_filter.region.ui.size() != 0)
|
|
filtered = string_format(_("Region: %1$s -"), m_filter.region.ui[m_filter.region.actual]);
|
|
else if (sw_filters::actual == UI_SW_PUBLISHERS)
|
|
filtered = string_format(_("Publisher: %1$s -"), m_filter.publisher.ui[m_filter.publisher.actual]);
|
|
else if (sw_filters::actual == UI_SW_YEARS)
|
|
filtered = string_format(_("Year: %1$s -"), m_filter.year.ui[m_filter.year.actual]);
|
|
else if (sw_filters::actual == UI_SW_LIST)
|
|
filtered = string_format(_("Software List: %1$s -"), m_filter.swlist.description[m_filter.swlist.actual]);
|
|
else if (sw_filters::actual == UI_SW_TYPE)
|
|
filtered = string_format(_("Device type: %1$s -"), m_filter.type.ui[m_filter.type.actual]);
|
|
|
|
tempbuf[2] = string_format(_("%s Search: %s_"), filtered, m_search);
|
|
|
|
// get the size of the text
|
|
float maxwidth = origx2 - origx1;
|
|
|
|
for (int line = 0; line < 3; ++line)
|
|
{
|
|
mui.draw_text_full(container, tempbuf[line].c_str(), 0.0f, 0.0f, 1.0f, JUSTIFY_CENTER, WRAP_NEVER,
|
|
DRAW_NONE, ARGB_WHITE, ARGB_BLACK, &width, nullptr);
|
|
width += 2 * UI_BOX_LR_BORDER;
|
|
maxwidth = MAX(width, maxwidth);
|
|
}
|
|
|
|
if (maxwidth > origx2 - origx1)
|
|
{
|
|
text_size = (origx2 - origx1) / maxwidth;
|
|
maxwidth = origx2 - origx1;
|
|
}
|
|
|
|
// compute our bounds
|
|
float x1 = 0.5f - 0.5f * maxwidth;
|
|
float x2 = x1 + maxwidth;
|
|
float y1 = origy1 - top;
|
|
float y2 = origy1 - 3.0f * UI_BOX_TB_BORDER - tbarspace;
|
|
|
|
// draw a box
|
|
mui.draw_outlined_box(container, x1, y1, x2, y2, UI_BACKGROUND_COLOR);
|
|
|
|
// take off the borders
|
|
x1 += UI_BOX_LR_BORDER;
|
|
x2 -= UI_BOX_LR_BORDER;
|
|
y1 += UI_BOX_TB_BORDER;
|
|
|
|
// draw the text within it
|
|
for (int line = 0; line < 3; ++line)
|
|
{
|
|
mui.draw_text_full(container, tempbuf[line].c_str(), x1, y1, x2 - x1, JUSTIFY_CENTER, WRAP_NEVER,
|
|
DRAW_NORMAL, UI_TEXT_COLOR, UI_TEXT_BG_COLOR, nullptr, nullptr, text_size);
|
|
y1 += mui.get_line_height();
|
|
}
|
|
|
|
// determine the text to render below
|
|
if (swinfo != nullptr && swinfo->startempty == 1)
|
|
driver = swinfo->driver;
|
|
|
|
if (driver != nullptr)
|
|
{
|
|
isstar = machine().favorite().isgame_favorite(driver);
|
|
|
|
// first line is game description
|
|
tempbuf[0] = string_format(_("%1$-.100s"), driver->description);
|
|
|
|
// next line is year, manufacturer
|
|
tempbuf[1] = string_format(_("%1$s, %2$-.100s"), driver->year, driver->manufacturer);
|
|
|
|
// next line is clone/parent status
|
|
int cloneof = driver_list::non_bios_clone(*driver);
|
|
|
|
if (cloneof != -1)
|
|
tempbuf[2] = string_format(_("Driver is clone of: %1$-.100s"), driver_list::driver(cloneof).description);
|
|
else
|
|
tempbuf[2] = _("Driver is parent");
|
|
|
|
// next line is overall driver status
|
|
if (driver->flags & MACHINE_NOT_WORKING)
|
|
tempbuf[3] = _("Overall: NOT WORKING");
|
|
else if (driver->flags & MACHINE_UNEMULATED_PROTECTION)
|
|
tempbuf[3] = _("Overall: Unemulated Protection");
|
|
else
|
|
tempbuf[3] = _("Overall: Working");
|
|
|
|
// next line is graphics, sound status
|
|
if (driver->flags & (MACHINE_IMPERFECT_GRAPHICS | MACHINE_WRONG_COLORS | MACHINE_IMPERFECT_COLORS))
|
|
tempbuf[4] = _("Graphics: Imperfect, ");
|
|
else
|
|
tempbuf[4] = _("Graphics: OK, ");
|
|
|
|
if (driver->flags & MACHINE_NO_SOUND)
|
|
tempbuf[4].append(_("Sound: Unimplemented"));
|
|
else if (driver->flags & MACHINE_IMPERFECT_SOUND)
|
|
tempbuf[4].append(_("Sound: Imperfect"));
|
|
else
|
|
tempbuf[4].append(_("Sound: OK"));
|
|
|
|
color = UI_GREEN_COLOR;
|
|
|
|
if ((driver->flags & (MACHINE_IMPERFECT_GRAPHICS | MACHINE_WRONG_COLORS | MACHINE_IMPERFECT_COLORS
|
|
| MACHINE_NO_SOUND | MACHINE_IMPERFECT_SOUND)) != 0)
|
|
color = UI_YELLOW_COLOR;
|
|
|
|
if ((driver->flags & (MACHINE_NOT_WORKING | MACHINE_UNEMULATED_PROTECTION)) != 0)
|
|
color = UI_RED_COLOR;
|
|
|
|
}
|
|
|
|
else if (swinfo != nullptr)
|
|
{
|
|
isstar = machine().favorite().isgame_favorite(*swinfo);
|
|
|
|
// first line is long name
|
|
tempbuf[0] = string_format(_("%1$-.100s"), swinfo->longname.c_str());
|
|
|
|
// next line is year, publisher
|
|
tempbuf[1] = string_format(_("%1$s, %2$-.100s"), swinfo->year.c_str(), swinfo->publisher.c_str());
|
|
|
|
// next line is parent/clone
|
|
if (!swinfo->parentname.empty())
|
|
tempbuf[2] = string_format(_("Software is clone of: %1$-.100s"), !swinfo->parentlongname.empty() ? swinfo->parentlongname.c_str() : swinfo->parentname.c_str());
|
|
else
|
|
tempbuf[2] = _("Software is parent");
|
|
|
|
// next line is supported status
|
|
if (swinfo->supported == SOFTWARE_SUPPORTED_NO)
|
|
{
|
|
tempbuf[3] = _("Supported: No");
|
|
color = UI_RED_COLOR;
|
|
}
|
|
else if (swinfo->supported == SOFTWARE_SUPPORTED_PARTIAL)
|
|
{
|
|
tempbuf[3] = _("Supported: Partial");
|
|
color = UI_YELLOW_COLOR;
|
|
}
|
|
else
|
|
{
|
|
tempbuf[3] = _("Supported: Yes");
|
|
color = UI_GREEN_COLOR;
|
|
}
|
|
|
|
// last line is romset name
|
|
tempbuf[4] = string_format(_("romset: %1$-.100s"), swinfo->shortname.c_str());
|
|
}
|
|
|
|
else
|
|
{
|
|
std::string copyright(emulator_info::get_copyright());
|
|
size_t found = copyright.find("\n");
|
|
|
|
tempbuf[0].clear();
|
|
tempbuf[1] = string_format("%s %s", emulator_info::get_appname(), build_version);
|
|
tempbuf[2] = copyright.substr(0, found);
|
|
tempbuf[3] = copyright.substr(found + 1);
|
|
tempbuf[4].clear();
|
|
}
|
|
|
|
// compute our bounds
|
|
x1 = 0.5f - 0.5f * maxwidth;
|
|
x2 = x1 + maxwidth;
|
|
y1 = y2;
|
|
y2 = origy1 - UI_BOX_TB_BORDER;
|
|
|
|
// draw toolbar
|
|
draw_toolbar(x1, y1, x2, y2, true);
|
|
|
|
// get the size of the text
|
|
maxwidth = origx2 - origx1;
|
|
|
|
for (auto & elem : tempbuf)
|
|
{
|
|
mui.draw_text_full(container, elem.c_str(), 0.0f, 0.0f, 1.0f, JUSTIFY_CENTER, WRAP_NEVER,
|
|
DRAW_NONE, ARGB_WHITE, ARGB_BLACK, &width, nullptr);
|
|
width += 2 * UI_BOX_LR_BORDER;
|
|
maxwidth = MAX(maxwidth, width);
|
|
}
|
|
|
|
if (maxwidth > origx2 - origx1)
|
|
{
|
|
text_size = (origx2 - origx1) / maxwidth;
|
|
maxwidth = origx2 - origx1;
|
|
}
|
|
|
|
// compute our bounds
|
|
x1 = 0.5f - 0.5f * maxwidth;
|
|
x2 = x1 + maxwidth;
|
|
y1 = origy2 + UI_BOX_TB_BORDER;
|
|
y2 = origy2 + bottom;
|
|
|
|
// draw a box
|
|
mui.draw_outlined_box(container, x1, y1, x2, y2, color);
|
|
|
|
// take off the borders
|
|
x1 += UI_BOX_LR_BORDER;
|
|
x2 -= UI_BOX_LR_BORDER;
|
|
y1 += UI_BOX_TB_BORDER;
|
|
|
|
// is favorite? draw the star
|
|
if (isstar)
|
|
draw_star(x1, y1);
|
|
|
|
// draw all lines
|
|
for (auto & elem : tempbuf)
|
|
{
|
|
mui.draw_text_full(container, elem.c_str(), x1, y1, x2 - x1, JUSTIFY_CENTER, WRAP_NEVER,
|
|
DRAW_NORMAL, UI_TEXT_COLOR, UI_TEXT_BG_COLOR, nullptr, nullptr, text_size);
|
|
y1 += machine().ui().get_line_height();
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
// handle select key event
|
|
//-------------------------------------------------
|
|
|
|
void ui_menu_select_software::inkey_select(const ui_menu_event *m_event)
|
|
{
|
|
ui_software_info *ui_swinfo = (ui_software_info *)m_event->itemref;
|
|
ui_options &mopt = machine().ui().options();
|
|
|
|
if (ui_swinfo->startempty == 1)
|
|
{
|
|
std::vector<s_bios> biosname;
|
|
if (!mopt.skip_bios_menu() && has_multiple_bios(ui_swinfo->driver, biosname))
|
|
ui_menu::stack_push(global_alloc_clear<ui_bios_selection>(machine(), container, biosname, (void *)ui_swinfo->driver, false, true));
|
|
else
|
|
{
|
|
reselect_last::driver = ui_swinfo->driver->name;
|
|
reselect_last::software = "[Start empty]";
|
|
reselect_last::swlist.clear();
|
|
reselect_last::set(true);
|
|
machine().manager().schedule_new_driver(*ui_swinfo->driver);
|
|
machine().schedule_hard_reset();
|
|
ui_menu::stack_reset(machine());
|
|
}
|
|
}
|
|
|
|
else
|
|
{
|
|
// first validate
|
|
driver_enumerator drivlist(machine().options(), *ui_swinfo->driver);
|
|
media_auditor auditor(drivlist);
|
|
drivlist.next();
|
|
software_list_device *swlist = software_list_device::find_by_name(drivlist.config(), ui_swinfo->listname.c_str());
|
|
software_info *swinfo = swlist->find(ui_swinfo->shortname.c_str());
|
|
|
|
media_auditor::summary summary = auditor.audit_software(swlist->list_name(), swinfo, AUDIT_VALIDATE_FAST);
|
|
|
|
if (summary == media_auditor::CORRECT || summary == media_auditor::BEST_AVAILABLE || summary == media_auditor::NONE_NEEDED)
|
|
{
|
|
std::vector<s_bios> biosname;
|
|
if (!mopt.skip_bios_menu() && has_multiple_bios(ui_swinfo->driver, biosname))
|
|
{
|
|
ui_menu::stack_push(global_alloc_clear<ui_bios_selection>(machine(), container, biosname, (void *)ui_swinfo, true, false));
|
|
return;
|
|
}
|
|
else if (!mopt.skip_parts_menu() && swinfo->has_multiple_parts(ui_swinfo->interface.c_str()))
|
|
{
|
|
std::unordered_map<std::string, std::string> parts;
|
|
for (const software_part *swpart = swinfo->first_part(); swpart != nullptr; swpart = swpart->next())
|
|
{
|
|
if (swpart->matches_interface(ui_swinfo->interface.c_str()))
|
|
{
|
|
std::string menu_part_name(swpart->name());
|
|
if (swpart->feature("part_id") != nullptr)
|
|
menu_part_name.assign("(").append(swpart->feature("part_id")).append(")");
|
|
parts.emplace(swpart->name(), menu_part_name);
|
|
}
|
|
}
|
|
ui_menu::stack_push(global_alloc_clear<ui_software_parts>(machine(), container, parts, ui_swinfo));
|
|
return;
|
|
}
|
|
std::string error_string;
|
|
std::string string_list = std::string(ui_swinfo->listname).append(":").append(ui_swinfo->shortname).append(":").append(ui_swinfo->part).append(":").append(ui_swinfo->instance);
|
|
machine().options().set_value(OPTION_SOFTWARENAME, string_list.c_str(), OPTION_PRIORITY_CMDLINE, error_string);
|
|
std::string snap_list = std::string(ui_swinfo->listname).append(PATH_SEPARATOR).append(ui_swinfo->shortname);
|
|
machine().options().set_value(OPTION_SNAPNAME, snap_list.c_str(), OPTION_PRIORITY_CMDLINE, error_string);
|
|
reselect_last::driver = drivlist.driver().name;
|
|
reselect_last::software = ui_swinfo->shortname;
|
|
reselect_last::swlist = ui_swinfo->listname;
|
|
reselect_last::set(true);
|
|
machine().manager().schedule_new_driver(drivlist.driver());
|
|
machine().schedule_hard_reset();
|
|
ui_menu::stack_reset(machine());
|
|
}
|
|
|
|
// otherwise, display an error
|
|
else
|
|
{
|
|
reset(UI_MENU_RESET_REMEMBER_POSITION);
|
|
ui_error = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
// handle special key event
|
|
//-------------------------------------------------
|
|
|
|
void ui_menu_select_software::inkey_special(const ui_menu_event *m_event)
|
|
{
|
|
int buflen = strlen(m_search);
|
|
|
|
// if it's a backspace and we can handle it, do so
|
|
if ((m_event->unichar == 8 || m_event->unichar == 0x7f) && buflen > 0)
|
|
{
|
|
*(char *)utf8_previous_char(&m_search[buflen]) = 0;
|
|
reset(UI_MENU_RESET_SELECT_FIRST);
|
|
}
|
|
|
|
// if it's any other key and we're not maxed out, update
|
|
else if (m_event->unichar >= ' ' && m_event->unichar < 0x7f)
|
|
{
|
|
buflen += utf8_from_uchar(&m_search[buflen], ARRAY_LENGTH(m_search) - buflen, m_event->unichar);
|
|
m_search[buflen] = 0;
|
|
reset(UI_MENU_RESET_SELECT_FIRST);
|
|
}
|
|
}
|
|
|
|
void ui_menu_select_software::inkey_configure(const ui_menu_event *m_event)
|
|
{
|
|
if (selected <= visible_items && m_focus == focused_menu::main)
|
|
{
|
|
m_prev_selected = item[selected].ref;
|
|
selected = visible_items + 1;
|
|
}
|
|
else if (selected > visible_items && m_focus == focused_menu::main)
|
|
{
|
|
if (ui_globals::panels_status != HIDE_LEFT_PANEL)
|
|
m_focus = focused_menu::left;
|
|
|
|
else if (ui_globals::panels_status == HIDE_BOTH)
|
|
{
|
|
for (int x = 0; x < item.size(); ++x)
|
|
if (item[x].ref == m_prev_selected)
|
|
selected = x;
|
|
}
|
|
else
|
|
m_focus = focused_menu::righttop;
|
|
}
|
|
else if (m_focus == focused_menu::left)
|
|
{
|
|
if (ui_globals::panels_status != HIDE_RIGHT_PANEL)
|
|
m_focus = focused_menu::righttop;
|
|
else
|
|
{
|
|
m_focus = focused_menu::main;
|
|
if (m_prev_selected == nullptr)
|
|
{
|
|
selected = 0;
|
|
return;
|
|
}
|
|
|
|
for (int x = 0; x < item.size(); ++x)
|
|
if (item[x].ref == m_prev_selected)
|
|
selected = x;
|
|
}
|
|
}
|
|
else if (m_focus == focused_menu::righttop)
|
|
m_focus = focused_menu::rightbottom;
|
|
else if (m_focus == focused_menu::rightbottom)
|
|
{
|
|
m_focus = focused_menu::main;
|
|
if (m_prev_selected == nullptr)
|
|
{
|
|
selected = 0;
|
|
return;
|
|
}
|
|
|
|
for (int x = 0; x < item.size(); ++x)
|
|
if (item[x].ref == m_prev_selected)
|
|
selected = x;
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
// load custom filters info from file
|
|
//-------------------------------------------------
|
|
|
|
void ui_menu_select_software::load_sw_custom_filters()
|
|
{
|
|
// attempt to open the output file
|
|
emu_file file(machine().ui().options().ui_path(), OPEN_FLAG_READ);
|
|
if (file.open("custom_", m_driver->name, "_filter.ini") == osd_file::error::NONE)
|
|
{
|
|
char buffer[MAX_CHAR_INFO];
|
|
|
|
// get number of filters
|
|
file.gets(buffer, MAX_CHAR_INFO);
|
|
char *pb = strchr(buffer, '=');
|
|
sw_custfltr::numother = atoi(++pb) - 1;
|
|
|
|
// get main filter
|
|
file.gets(buffer, MAX_CHAR_INFO);
|
|
pb = strchr(buffer, '=') + 2;
|
|
|
|
for (int y = 0; y < sw_filters::length; ++y)
|
|
if (!strncmp(pb, sw_filters::text[y], strlen(sw_filters::text[y])))
|
|
{
|
|
sw_custfltr::main = y;
|
|
break;
|
|
}
|
|
|
|
for (int x = 1; x <= sw_custfltr::numother; ++x)
|
|
{
|
|
file.gets(buffer, MAX_CHAR_INFO);
|
|
char *cb = strchr(buffer, '=') + 2;
|
|
for (int y = 0; y < sw_filters::length; y++)
|
|
{
|
|
if (!strncmp(cb, sw_filters::text[y], strlen(sw_filters::text[y])))
|
|
{
|
|
sw_custfltr::other[x] = y;
|
|
if (y == UI_SW_PUBLISHERS)
|
|
{
|
|
file.gets(buffer, MAX_CHAR_INFO);
|
|
char *ab = strchr(buffer, '=') + 2;
|
|
for (size_t z = 0; z < m_filter.publisher.ui.size(); ++z)
|
|
if (!strncmp(ab, m_filter.publisher.ui[z].c_str(), m_filter.publisher.ui[z].length()))
|
|
sw_custfltr::mnfct[x] = z;
|
|
}
|
|
else if (y == UI_SW_YEARS)
|
|
{
|
|
file.gets(buffer, MAX_CHAR_INFO);
|
|
char *db = strchr(buffer, '=') + 2;
|
|
for (size_t z = 0; z < m_filter.year.ui.size(); ++z)
|
|
if (!strncmp(db, m_filter.year.ui[z].c_str(), m_filter.year.ui[z].length()))
|
|
sw_custfltr::year[x] = z;
|
|
}
|
|
else if (y == UI_SW_LIST)
|
|
{
|
|
file.gets(buffer, MAX_CHAR_INFO);
|
|
char *gb = strchr(buffer, '=') + 2;
|
|
for (size_t z = 0; z < m_filter.swlist.name.size(); ++z)
|
|
if (!strncmp(gb, m_filter.swlist.name[z].c_str(), m_filter.swlist.name[z].length()))
|
|
sw_custfltr::list[x] = z;
|
|
}
|
|
else if (y == UI_SW_TYPE)
|
|
{
|
|
file.gets(buffer, MAX_CHAR_INFO);
|
|
char *fb = strchr(buffer, '=') + 2;
|
|
for (size_t z = 0; z < m_filter.type.ui.size(); ++z)
|
|
if (!strncmp(fb, m_filter.type.ui[z].c_str(), m_filter.type.ui[z].length()))
|
|
sw_custfltr::type[x] = z;
|
|
}
|
|
else if (y == UI_SW_REGION)
|
|
{
|
|
file.gets(buffer, MAX_CHAR_INFO);
|
|
char *eb = strchr(buffer, '=') + 2;
|
|
for (size_t z = 0; z < m_filter.region.ui.size(); ++z)
|
|
if (!strncmp(eb, m_filter.region.ui[z].c_str(), m_filter.region.ui[z].length()))
|
|
sw_custfltr::region[x] = z;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
file.close();
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
// set software regions
|
|
//-------------------------------------------------
|
|
|
|
void c_sw_region::set(std::string &str)
|
|
{
|
|
std::string name = getname(str);
|
|
if (std::find(ui.begin(), ui.end(), name) != ui.end())
|
|
return;
|
|
|
|
ui.push_back(name);
|
|
}
|
|
|
|
std::string c_sw_region::getname(std::string &str)
|
|
{
|
|
std::string fullname(str);
|
|
strmakelower(fullname);
|
|
size_t found = fullname.find("(");
|
|
|
|
if (found != std::string::npos)
|
|
{
|
|
size_t ends = fullname.find_first_not_of("abcdefghijklmnopqrstuvwxyz", found + 1);
|
|
std::string temp(fullname.substr(found + 1, ends - found - 1));
|
|
|
|
for (auto & elem : region_lists)
|
|
if (temp == elem)
|
|
return (str.substr(found + 1, ends - found - 1));
|
|
}
|
|
return std::string("<none>");
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
// set software device type
|
|
//-------------------------------------------------
|
|
|
|
void c_sw_type::set(std::string &str)
|
|
{
|
|
if (std::find(ui.begin(), ui.end(), str) != ui.end())
|
|
return;
|
|
|
|
ui.push_back(str);
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
// set software years
|
|
//-------------------------------------------------
|
|
|
|
void c_sw_year::set(std::string &str)
|
|
{
|
|
if (std::find(ui.begin(), ui.end(), str) != ui.end())
|
|
return;
|
|
|
|
ui.push_back(str);
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
// set software publishers
|
|
//-------------------------------------------------
|
|
|
|
void c_sw_publisher::set(std::string &str)
|
|
{
|
|
std::string name = getname(str);
|
|
if (std::find(ui.begin(), ui.end(), name) != ui.end())
|
|
return;
|
|
|
|
ui.push_back(name);
|
|
}
|
|
|
|
std::string c_sw_publisher::getname(std::string &str)
|
|
{
|
|
size_t found = str.find("(");
|
|
|
|
if (found != std::string::npos)
|
|
return (str.substr(0, found - 1));
|
|
else
|
|
return str;
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
// build display list
|
|
//-------------------------------------------------
|
|
void ui_menu_select_software::build_list(std::vector<ui_software_info *> &s_drivers, const char *filter_text, int filter)
|
|
{
|
|
if (s_drivers.empty() && filter == -1)
|
|
{
|
|
filter = sw_filters::actual;
|
|
s_drivers = m_sortedlist;
|
|
}
|
|
|
|
// iterate over entries
|
|
for (auto & s_driver : s_drivers)
|
|
{
|
|
switch (filter)
|
|
{
|
|
case UI_SW_PARENTS:
|
|
if (s_driver->parentname.empty())
|
|
m_displaylist.push_back(s_driver);
|
|
break;
|
|
|
|
case UI_SW_CLONES:
|
|
if (!s_driver->parentname.empty())
|
|
m_displaylist.push_back(s_driver);
|
|
break;
|
|
|
|
case UI_SW_AVAILABLE:
|
|
if (s_driver->available)
|
|
m_displaylist.push_back(s_driver);
|
|
break;
|
|
|
|
case UI_SW_UNAVAILABLE:
|
|
if (!s_driver->available)
|
|
m_displaylist.push_back(s_driver);
|
|
break;
|
|
|
|
case UI_SW_SUPPORTED:
|
|
if (s_driver->supported == SOFTWARE_SUPPORTED_YES)
|
|
m_displaylist.push_back(s_driver);
|
|
break;
|
|
|
|
case UI_SW_PARTIAL_SUPPORTED:
|
|
if (s_driver->supported == SOFTWARE_SUPPORTED_PARTIAL)
|
|
m_displaylist.push_back(s_driver);
|
|
break;
|
|
|
|
case UI_SW_UNSUPPORTED:
|
|
if (s_driver->supported == SOFTWARE_SUPPORTED_NO)
|
|
m_displaylist.push_back(s_driver);
|
|
break;
|
|
|
|
case UI_SW_REGION:
|
|
{
|
|
std::string name = m_filter.region.getname(s_driver->longname);
|
|
|
|
if(!name.empty() && name == filter_text)
|
|
m_displaylist.push_back(s_driver);
|
|
break;
|
|
}
|
|
|
|
case UI_SW_PUBLISHERS:
|
|
{
|
|
std::string name = m_filter.publisher.getname(s_driver->publisher);
|
|
|
|
if(!name.empty() && name == filter_text)
|
|
m_displaylist.push_back(s_driver);
|
|
break;
|
|
}
|
|
|
|
case UI_SW_YEARS:
|
|
if(s_driver->year == filter_text)
|
|
m_displaylist.push_back(s_driver);
|
|
break;
|
|
|
|
case UI_SW_LIST:
|
|
if(s_driver->listname == filter_text)
|
|
m_displaylist.push_back(s_driver);
|
|
break;
|
|
|
|
case UI_SW_TYPE:
|
|
if(s_driver->devicetype == filter_text)
|
|
m_displaylist.push_back(s_driver);
|
|
break;
|
|
|
|
default:
|
|
m_displaylist.push_back(s_driver);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
// find approximate matches
|
|
//-------------------------------------------------
|
|
|
|
void ui_menu_select_software::find_matches(const char *str, int count)
|
|
{
|
|
// allocate memory to track the penalty value
|
|
std::vector<int> penalty(count, 9999);
|
|
int index = 0;
|
|
|
|
for (; index < m_displaylist.size(); ++index)
|
|
{
|
|
// pick the best match between driver name and description
|
|
int curpenalty = fuzzy_substring(str, m_displaylist[index]->longname);
|
|
int tmp = fuzzy_substring(str, m_displaylist[index]->shortname);
|
|
curpenalty = MIN(curpenalty, tmp);
|
|
|
|
// insert into the sorted table of matches
|
|
for (int matchnum = count - 1; matchnum >= 0; --matchnum)
|
|
{
|
|
// stop if we're worse than the current entry
|
|
if (curpenalty >= penalty[matchnum])
|
|
break;
|
|
|
|
// as long as this isn't the last entry, bump this one down
|
|
if (matchnum < count - 1)
|
|
{
|
|
penalty[matchnum + 1] = penalty[matchnum];
|
|
m_searchlist[matchnum + 1] = m_searchlist[matchnum];
|
|
}
|
|
|
|
m_searchlist[matchnum] = m_displaylist[index];
|
|
penalty[matchnum] = curpenalty;
|
|
}
|
|
}
|
|
(index < count) ? m_searchlist[index] = nullptr : m_searchlist[count] = nullptr;
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
// build custom display list
|
|
//-------------------------------------------------
|
|
|
|
void ui_menu_select_software::build_custom()
|
|
{
|
|
std::vector<ui_software_info *> s_drivers;
|
|
|
|
build_list(m_sortedlist, nullptr, sw_custfltr::main);
|
|
|
|
for (int count = 1; count <= sw_custfltr::numother; ++count)
|
|
{
|
|
int filter = sw_custfltr::other[count];
|
|
s_drivers = m_displaylist;
|
|
m_displaylist.clear();
|
|
|
|
switch (filter)
|
|
{
|
|
case UI_SW_YEARS:
|
|
build_list(s_drivers, m_filter.year.ui[sw_custfltr::year[count]].c_str(), filter);
|
|
break;
|
|
case UI_SW_LIST:
|
|
build_list(s_drivers, m_filter.swlist.name[sw_custfltr::list[count]].c_str(), filter);
|
|
break;
|
|
case UI_SW_TYPE:
|
|
build_list(s_drivers, m_filter.type.ui[sw_custfltr::type[count]].c_str(), filter);
|
|
break;
|
|
case UI_SW_PUBLISHERS:
|
|
build_list(s_drivers, m_filter.publisher.ui[sw_custfltr::mnfct[count]].c_str(), filter);
|
|
break;
|
|
case UI_SW_REGION:
|
|
build_list(s_drivers, m_filter.region.ui[sw_custfltr::region[count]].c_str(), filter);
|
|
break;
|
|
default:
|
|
build_list(s_drivers, nullptr, filter);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
// draw left box
|
|
//-------------------------------------------------
|
|
|
|
float ui_menu_select_software::draw_left_panel(float x1, float y1, float x2, float y2)
|
|
{
|
|
ui_manager &mui = machine().ui();
|
|
|
|
if (ui_globals::panels_status == SHOW_PANELS || ui_globals::panels_status == HIDE_RIGHT_PANEL)
|
|
{
|
|
float origy1 = y1;
|
|
float origy2 = y2;
|
|
float text_size = 0.75f;
|
|
float l_height = mui.get_line_height();
|
|
float line_height = l_height * text_size;
|
|
float left_width = 0.0f;
|
|
int text_lenght = sw_filters::length;
|
|
int afilter = sw_filters::actual;
|
|
int phover = HOVER_SW_FILTER_FIRST;
|
|
const char **text = sw_filters::text;
|
|
float sc = y2 - y1 - (2.0f * UI_BOX_TB_BORDER);
|
|
|
|
if ((text_lenght * line_height) > sc)
|
|
{
|
|
float lm = sc / (text_lenght);
|
|
text_size = lm / l_height;
|
|
line_height = l_height * text_size;
|
|
}
|
|
|
|
float text_sign = mui.get_string_width_ex("_# ", text_size);
|
|
for (int x = 0; x < text_lenght; ++x)
|
|
{
|
|
float total_width;
|
|
|
|
// compute width of left hand side
|
|
total_width = mui.get_string_width_ex(text[x], text_size);
|
|
total_width += text_sign;
|
|
|
|
// track the maximum
|
|
if (total_width > left_width)
|
|
left_width = total_width;
|
|
}
|
|
|
|
x2 = x1 + left_width + 2.0f * UI_BOX_LR_BORDER;
|
|
//machine().ui().draw_outlined_box(container, x1, y1, x2, y2, rgb_t(0xEF, 0x12, 0x47, 0x7B));
|
|
mui.draw_outlined_box(container, x1, y1, x2, y2, UI_BACKGROUND_COLOR);
|
|
|
|
// take off the borders
|
|
x1 += UI_BOX_LR_BORDER;
|
|
x2 -= UI_BOX_LR_BORDER;
|
|
y1 += UI_BOX_TB_BORDER;
|
|
y2 -= UI_BOX_TB_BORDER;
|
|
|
|
for (int filter = 0; filter < text_lenght; ++filter)
|
|
{
|
|
std::string str(text[filter]);
|
|
rgb_t bgcolor = UI_TEXT_BG_COLOR;
|
|
rgb_t fgcolor = UI_TEXT_COLOR;
|
|
|
|
if (mouse_hit && x1 <= mouse_x && x2 > mouse_x && y1 <= mouse_y && y1 + line_height > mouse_y)
|
|
{
|
|
bgcolor = UI_MOUSEOVER_BG_COLOR;
|
|
fgcolor = UI_MOUSEOVER_COLOR;
|
|
hover = phover + filter;
|
|
}
|
|
|
|
if (highlight == filter && m_focus == focused_menu::left)
|
|
{
|
|
fgcolor = rgb_t(0xff, 0xff, 0xff, 0x00);
|
|
bgcolor = rgb_t(0xff, 0xff, 0xff, 0xff);
|
|
}
|
|
|
|
if (bgcolor != UI_TEXT_BG_COLOR)
|
|
mui.draw_textured_box(container, x1, y1, x2, y1 + line_height, bgcolor, rgb_t(255, 43, 43, 43),
|
|
hilight_main_texture, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXWRAP(TRUE));
|
|
|
|
float x1t = x1 + text_sign;
|
|
if (afilter == UI_SW_CUSTOM)
|
|
{
|
|
if (filter == sw_custfltr::main)
|
|
{
|
|
str.assign("@custom1 ").append(text[filter]);
|
|
x1t -= text_sign;
|
|
}
|
|
else
|
|
{
|
|
for (int count = 1; count <= sw_custfltr::numother; ++count)
|
|
{
|
|
int cfilter = sw_custfltr::other[count];
|
|
if (cfilter == filter)
|
|
{
|
|
str = string_format("@custom%d %s", count + 1, text[filter]);
|
|
x1t -= text_sign;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
convert_command_glyph(str);
|
|
}
|
|
else if (filter == sw_filters::actual)
|
|
{
|
|
str.assign("_> ").append(text[filter]);
|
|
x1t -= text_sign;
|
|
convert_command_glyph(str);
|
|
}
|
|
|
|
mui.draw_text_full(container, str.c_str(), x1t, y1, x2 - x1, JUSTIFY_LEFT, WRAP_NEVER,
|
|
DRAW_NORMAL, fgcolor, bgcolor, nullptr, nullptr, text_size);
|
|
y1 += line_height;
|
|
}
|
|
|
|
x1 = x2 + UI_BOX_LR_BORDER;
|
|
x2 = x1 + 2.0f * UI_BOX_LR_BORDER;
|
|
y1 = origy1;
|
|
y2 = origy2;
|
|
line_height = mui.get_line_height();
|
|
float lr_arrow_width = 0.4f * line_height * machine().render().ui_aspect();
|
|
rgb_t fgcolor = UI_TEXT_COLOR;
|
|
|
|
// set left-right arrows dimension
|
|
float ar_x0 = 0.5f * (x2 + x1) - 0.5f * lr_arrow_width;
|
|
float ar_y0 = 0.5f * (y2 + y1) + 0.1f * line_height;
|
|
float ar_x1 = ar_x0 + lr_arrow_width;
|
|
float ar_y1 = 0.5f * (y2 + y1) + 0.9f * line_height;
|
|
|
|
//machine().ui().draw_outlined_box(container, x1, y1, x2, y2, UI_BACKGROUND_COLOR);
|
|
mui.draw_outlined_box(container, x1, y1, x2, y2, rgb_t(0xEF, 0x12, 0x47, 0x7B));
|
|
|
|
if (mouse_hit && x1 <= mouse_x && x2 > mouse_x && y1 <= mouse_y && y2 > mouse_y)
|
|
{
|
|
fgcolor = UI_MOUSEOVER_COLOR;
|
|
hover = HOVER_LPANEL_ARROW;
|
|
}
|
|
|
|
draw_arrow(container, ar_x0, ar_y0, ar_x1, ar_y1, fgcolor, ROT90 ^ ORIENTATION_FLIP_X);
|
|
return x2 + UI_BOX_LR_BORDER;
|
|
}
|
|
else
|
|
{
|
|
float line_height = mui.get_line_height();
|
|
float lr_arrow_width = 0.4f * line_height * machine().render().ui_aspect();
|
|
rgb_t fgcolor = UI_TEXT_COLOR;
|
|
|
|
// set left-right arrows dimension
|
|
float ar_x0 = 0.5f * (x2 + x1) - 0.5f * lr_arrow_width;
|
|
float ar_y0 = 0.5f * (y2 + y1) + 0.1f * line_height;
|
|
float ar_x1 = ar_x0 + lr_arrow_width;
|
|
float ar_y1 = 0.5f * (y2 + y1) + 0.9f * line_height;
|
|
|
|
//machine().ui().draw_outlined_box(container, x1, y1, x2, y2, UI_BACKGROUND_COLOR);
|
|
mui.draw_outlined_box(container, x1, y1, x2, y2, rgb_t(0xEF, 0x12, 0x47, 0x7B));
|
|
|
|
if (mouse_hit && x1 <= mouse_x && x2 > mouse_x && y1 <= mouse_y && y2 > mouse_y)
|
|
{
|
|
fgcolor = UI_MOUSEOVER_COLOR;
|
|
hover = HOVER_LPANEL_ARROW;
|
|
}
|
|
|
|
draw_arrow(container, ar_x0, ar_y0, ar_x1, ar_y1, fgcolor, ROT90);
|
|
return x2 + UI_BOX_LR_BORDER;
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
// draw infos
|
|
//-------------------------------------------------
|
|
|
|
void ui_menu_select_software::infos_render(void *selectedref, float origx1, float origy1, float origx2, float origy2)
|
|
{
|
|
ui_manager &mui = machine().ui();
|
|
float line_height = mui.get_line_height();
|
|
static std::string buffer;
|
|
std::vector<int> xstart;
|
|
std::vector<int> xend;
|
|
float text_size = machine().ui().options().infos_size();
|
|
ui_software_info *soft = (selectedref != nullptr) ? (ui_software_info *)selectedref : ((m_prev_selected != nullptr) ? (ui_software_info *)m_prev_selected : nullptr);
|
|
static ui_software_info *oldsoft = nullptr;
|
|
static int old_sw_view = -1;
|
|
|
|
float gutter_width = 0.4f * line_height * machine().render().ui_aspect() * 1.3f;
|
|
float ud_arrow_width = line_height * machine().render().ui_aspect();
|
|
float oy1 = origy1 + line_height;
|
|
|
|
// apply title to right panel
|
|
if (soft != nullptr && soft->usage.empty())
|
|
{
|
|
float title_size = 0.0f;
|
|
|
|
mui.draw_text_full(container, _("History"), origx1, origy1, origx2 - origx1, JUSTIFY_CENTER, WRAP_TRUNCATE,
|
|
DRAW_NONE, UI_TEXT_COLOR, UI_TEXT_BG_COLOR, &title_size, nullptr);
|
|
title_size += 0.01f;
|
|
|
|
rgb_t fgcolor = UI_TEXT_COLOR;
|
|
rgb_t bgcolor = UI_TEXT_BG_COLOR;
|
|
if (m_focus == focused_menu::rightbottom)
|
|
{
|
|
fgcolor = rgb_t(0xff, 0xff, 0xff, 0x00);
|
|
bgcolor = rgb_t(0xff, 0xff, 0xff, 0xff);
|
|
}
|
|
|
|
float middle = origx2 - origx1;
|
|
|
|
if (bgcolor != UI_TEXT_BG_COLOR)
|
|
mui.draw_textured_box(container, origx1 + ((middle - title_size) * 0.5f), origy1, origx1 + ((middle + title_size) * 0.5f),
|
|
origy1 + line_height, bgcolor, rgb_t(255, 43, 43, 43), hilight_main_texture, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXWRAP(TRUE));
|
|
|
|
mui.draw_text_full(container, _("History"), origx1, origy1, origx2 - origx1, JUSTIFY_CENTER, WRAP_NEVER,
|
|
DRAW_NORMAL, fgcolor, bgcolor, nullptr, nullptr);
|
|
ui_globals::cur_sw_dats_view = 0;
|
|
}
|
|
else
|
|
{
|
|
float title_size = 0.0f;
|
|
float txt_lenght = 0.0f;
|
|
std::string t_text[2];
|
|
t_text[0] = _("History");
|
|
t_text[1] = _("Usage");
|
|
|
|
for (auto & elem : t_text)
|
|
{
|
|
mui.draw_text_full(container, elem.c_str(), origx1, origy1, origx2 - origx1, JUSTIFY_CENTER, WRAP_NEVER,
|
|
DRAW_NONE, UI_TEXT_COLOR, UI_TEXT_BG_COLOR, &txt_lenght, nullptr);
|
|
txt_lenght += 0.01f;
|
|
title_size = MAX(txt_lenght, title_size);
|
|
}
|
|
|
|
rgb_t fgcolor = UI_TEXT_COLOR;
|
|
rgb_t bgcolor = UI_TEXT_BG_COLOR;
|
|
if (m_focus == focused_menu::rightbottom)
|
|
{
|
|
fgcolor = rgb_t(0xff, 0xff, 0xff, 0x00);
|
|
bgcolor = rgb_t(0xff, 0xff, 0xff, 0xff);
|
|
}
|
|
|
|
float middle = origx2 - origx1;
|
|
|
|
// check size
|
|
float sc = title_size + 2.0f * gutter_width;
|
|
float tmp_size = (sc > middle) ? ((middle - 2.0f * gutter_width) / sc) : 1.0f;
|
|
title_size *= tmp_size;
|
|
|
|
if (bgcolor != UI_TEXT_BG_COLOR)
|
|
mui.draw_textured_box(container, origx1 + ((middle - title_size) * 0.5f), origy1, origx1 + ((middle + title_size) * 0.5f),
|
|
origy1 + line_height, bgcolor, rgb_t(255, 43, 43, 43), hilight_main_texture, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXWRAP(TRUE));
|
|
|
|
mui.draw_text_full(container, t_text[ui_globals::cur_sw_dats_view].c_str(), origx1, origy1, origx2 - origx1,
|
|
JUSTIFY_CENTER, WRAP_NEVER, DRAW_NORMAL, fgcolor, bgcolor, nullptr, nullptr, tmp_size);
|
|
|
|
draw_common_arrow(origx1, origy1, origx2, origy2, ui_globals::cur_sw_dats_view, 0, 1, title_size);
|
|
}
|
|
|
|
if (oldsoft != soft || old_sw_view != ui_globals::cur_sw_dats_view)
|
|
{
|
|
buffer.clear();
|
|
old_sw_view = ui_globals::cur_sw_dats_view;
|
|
oldsoft = soft;
|
|
if (ui_globals::cur_sw_dats_view == 0)
|
|
{
|
|
if (soft->startempty == 1)
|
|
machine().datfile().load_data_info(soft->driver, buffer, UI_HISTORY_LOAD);
|
|
else
|
|
machine().datfile().load_software_info(soft->listname, buffer, soft->shortname, soft->parentname);
|
|
}
|
|
else
|
|
buffer = soft->usage;
|
|
}
|
|
|
|
if (buffer.empty())
|
|
{
|
|
mui.draw_text_full(container, _("No Infos Available"), origx1, (origy2 + origy1) * 0.5f, origx2 - origx1, JUSTIFY_CENTER,
|
|
WRAP_WORD, DRAW_NORMAL, UI_TEXT_COLOR, UI_TEXT_BG_COLOR, nullptr, nullptr);
|
|
return;
|
|
}
|
|
else
|
|
totallines = mui.wrap_text(container, buffer.c_str(), origx1, origy1, origx2 - origx1 - (2.0f * gutter_width), xstart, xend, text_size);
|
|
|
|
int r_visible_lines = floor((origy2 - oy1) / (line_height * text_size));
|
|
if (totallines < r_visible_lines)
|
|
r_visible_lines = totallines;
|
|
if (topline_datsview < 0)
|
|
topline_datsview = 0;
|
|
if (topline_datsview + r_visible_lines >= totallines)
|
|
topline_datsview = totallines - r_visible_lines;
|
|
|
|
for (int r = 0; r < r_visible_lines; ++r)
|
|
{
|
|
int itemline = r + topline_datsview;
|
|
std::string tempbuf;
|
|
tempbuf.assign(buffer.substr(xstart[itemline], xend[itemline] - xstart[itemline]));
|
|
|
|
// up arrow
|
|
if (r == 0 && topline_datsview != 0)
|
|
info_arrow(0, origx1, origx2, oy1, line_height, text_size, ud_arrow_width);
|
|
// bottom arrow
|
|
else if (r == r_visible_lines - 1 && itemline != totallines - 1)
|
|
info_arrow(1, origx1, origx2, oy1, line_height, text_size, ud_arrow_width);
|
|
else
|
|
mui.draw_text_full(container, tempbuf.c_str(), origx1 + gutter_width, oy1, origx2 - origx1,
|
|
JUSTIFY_LEFT, WRAP_TRUNCATE, DRAW_NORMAL, UI_TEXT_COLOR, UI_TEXT_BG_COLOR,
|
|
nullptr, nullptr, text_size);
|
|
oy1 += (line_height * text_size);
|
|
}
|
|
|
|
// return the number of visible lines, minus 1 for top arrow and 1 for bottom arrow
|
|
right_visible_lines = r_visible_lines - (topline_datsview != 0) - (topline_datsview + r_visible_lines != totallines);
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
// perform our special rendering
|
|
//-------------------------------------------------
|
|
|
|
void ui_menu_select_software::arts_render(void *selectedref, float origx1, float origy1, float origx2, float origy2)
|
|
{
|
|
ui_manager &mui = machine().ui();
|
|
float line_height = mui.get_line_height();
|
|
static ui_software_info *oldsoft = nullptr;
|
|
static const game_driver *olddriver = nullptr;
|
|
const game_driver *driver = nullptr;
|
|
ui_software_info *soft = (selectedref != nullptr) ? (ui_software_info *)selectedref : ((m_prev_selected != nullptr) ? (ui_software_info *)m_prev_selected : nullptr);
|
|
|
|
if (soft != nullptr && soft->startempty == 1)
|
|
{
|
|
driver = soft->driver;
|
|
oldsoft = nullptr;
|
|
}
|
|
else
|
|
olddriver = nullptr;
|
|
|
|
if (driver != nullptr)
|
|
{
|
|
if (ui_globals::default_image)
|
|
((driver->flags & MACHINE_TYPE_ARCADE) == 0) ? ui_globals::curimage_view = CABINETS_VIEW : ui_globals::curimage_view = SNAPSHOT_VIEW;
|
|
|
|
std::string searchstr;
|
|
searchstr = arts_render_common(origx1, origy1, origx2, origy2);
|
|
|
|
// loads the image if necessary
|
|
if (driver != olddriver || !snapx_bitmap->valid() || ui_globals::switch_image)
|
|
{
|
|
emu_file snapfile(searchstr.c_str(), OPEN_FLAG_READ);
|
|
snapfile.set_restrict_to_mediapath(true);
|
|
bitmap_argb32 *tmp_bitmap;
|
|
tmp_bitmap = auto_alloc(machine(), bitmap_argb32);
|
|
|
|
// try to load snapshot first from saved "0000.png" file
|
|
std::string fullname(driver->name);
|
|
render_load_png(*tmp_bitmap, snapfile, fullname.c_str(), "0000.png");
|
|
|
|
if (!tmp_bitmap->valid())
|
|
render_load_jpeg(*tmp_bitmap, snapfile, fullname.c_str(), "0000.jpg");
|
|
|
|
// if fail, attemp to load from standard file
|
|
if (!tmp_bitmap->valid())
|
|
{
|
|
fullname.assign(driver->name).append(".png");
|
|
render_load_png(*tmp_bitmap, snapfile, nullptr, fullname.c_str());
|
|
|
|
if (!tmp_bitmap->valid())
|
|
{
|
|
fullname.assign(driver->name).append(".jpg");
|
|
render_load_jpeg(*tmp_bitmap, snapfile, nullptr, fullname.c_str());
|
|
}
|
|
}
|
|
|
|
// if fail again, attemp to load from parent file
|
|
if (!tmp_bitmap->valid())
|
|
{
|
|
// set clone status
|
|
bool cloneof = strcmp(driver->parent, "0");
|
|
if (cloneof)
|
|
{
|
|
int cx = driver_list::find(driver->parent);
|
|
if (cx != -1 && ((driver_list::driver(cx).flags & MACHINE_IS_BIOS_ROOT) != 0))
|
|
cloneof = false;
|
|
}
|
|
|
|
if (cloneof)
|
|
{
|
|
fullname.assign(driver->parent).append(".png");
|
|
render_load_png(*tmp_bitmap, snapfile, nullptr, fullname.c_str());
|
|
|
|
if (!tmp_bitmap->valid())
|
|
{
|
|
fullname.assign(driver->parent).append(".jpg");
|
|
render_load_jpeg(*tmp_bitmap, snapfile, nullptr, fullname.c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
olddriver = driver;
|
|
ui_globals::switch_image = false;
|
|
arts_render_images(tmp_bitmap, origx1, origy1, origx2, origy2, false);
|
|
auto_free(machine(), tmp_bitmap);
|
|
}
|
|
|
|
// if the image is available, loaded and valid, display it
|
|
if (snapx_bitmap->valid())
|
|
{
|
|
float x1 = origx1 + 0.01f;
|
|
float x2 = origx2 - 0.01f;
|
|
float y1 = origy1 + UI_BOX_TB_BORDER + line_height;
|
|
float y2 = origy2 - UI_BOX_TB_BORDER - line_height;
|
|
|
|
// apply texture
|
|
container->add_quad( x1, y1, x2, y2, ARGB_WHITE, snapx_texture, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
|
|
}
|
|
}
|
|
else if (soft != nullptr)
|
|
{
|
|
std::string fullname, pathname;
|
|
if (ui_globals::default_image)
|
|
(soft->startempty == 0) ? ui_globals::curimage_view = SNAPSHOT_VIEW : ui_globals::curimage_view = CABINETS_VIEW;
|
|
|
|
// arts title and searchpath
|
|
std::string searchstr;
|
|
searchstr = arts_render_common(origx1, origy1, origx2, origy2);
|
|
|
|
// loads the image if necessary
|
|
if (soft != oldsoft || !snapx_bitmap->valid() || ui_globals::switch_image)
|
|
{
|
|
emu_file snapfile(searchstr.c_str(), OPEN_FLAG_READ);
|
|
bitmap_argb32 *tmp_bitmap;
|
|
tmp_bitmap = auto_alloc(machine(), bitmap_argb32);
|
|
|
|
if (soft->startempty == 1)
|
|
{
|
|
// Load driver snapshot
|
|
fullname.assign(soft->driver->name).append(".png");
|
|
render_load_png(*tmp_bitmap, snapfile, nullptr, fullname.c_str());
|
|
|
|
if (!tmp_bitmap->valid())
|
|
{
|
|
fullname.assign(soft->driver->name).append(".jpg");
|
|
render_load_jpeg(*tmp_bitmap, snapfile, nullptr, fullname.c_str());
|
|
}
|
|
}
|
|
else if (ui_globals::curimage_view == TITLES_VIEW)
|
|
{
|
|
// First attempt from name list
|
|
pathname.assign(soft->listname).append("_titles");
|
|
fullname.assign(soft->shortname).append(".png");
|
|
render_load_png(*tmp_bitmap, snapfile, pathname.c_str(), fullname.c_str());
|
|
|
|
if (!tmp_bitmap->valid())
|
|
{
|
|
fullname.assign(soft->shortname).append(".jpg");
|
|
render_load_jpeg(*tmp_bitmap, snapfile, pathname.c_str(), fullname.c_str());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// First attempt from name list
|
|
pathname = soft->listname;
|
|
fullname.assign(soft->shortname).append(".png");
|
|
render_load_png(*tmp_bitmap, snapfile, pathname.c_str(), fullname.c_str());
|
|
|
|
if (!tmp_bitmap->valid())
|
|
{
|
|
fullname.assign(soft->shortname).append(".jpg");
|
|
render_load_jpeg(*tmp_bitmap, snapfile, pathname.c_str(), fullname.c_str());
|
|
}
|
|
|
|
if (!tmp_bitmap->valid())
|
|
{
|
|
// Second attempt from driver name + part name
|
|
pathname.assign(soft->driver->name).append(soft->part);
|
|
fullname.assign(soft->shortname).append(".png");
|
|
render_load_png(*tmp_bitmap, snapfile, pathname.c_str(), fullname.c_str());
|
|
|
|
if (!tmp_bitmap->valid())
|
|
{
|
|
fullname.assign(soft->shortname).append(".jpg");
|
|
render_load_jpeg(*tmp_bitmap, snapfile, pathname.c_str(), fullname.c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
oldsoft = soft;
|
|
ui_globals::switch_image = false;
|
|
arts_render_images(tmp_bitmap, origx1, origy1, origx2, origy2, true);
|
|
auto_free(machine(), tmp_bitmap);
|
|
}
|
|
|
|
// if the image is available, loaded and valid, display it
|
|
if (snapx_bitmap->valid())
|
|
{
|
|
float x1 = origx1 + 0.01f;
|
|
float x2 = origx2 - 0.01f;
|
|
float y1 = origy1 + UI_BOX_TB_BORDER + line_height;
|
|
float y2 = origy2 - UI_BOX_TB_BORDER - line_height;
|
|
|
|
// apply texture
|
|
container->add_quad(x1, y1, x2, y2, ARGB_WHITE, snapx_texture, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
|
|
}
|
|
}
|
|
}
|
|
|
|
void ui_menu_select_software::draw_right_panel(void *selectedref, float origx1, float origy1, float origx2, float origy2)
|
|
{
|
|
ui_manager &mui = machine().ui();
|
|
float line_height = mui.get_line_height();
|
|
float lr_arrow_width = 0.4f * line_height * machine().render().ui_aspect();
|
|
rgb_t fgcolor = UI_TEXT_COLOR;
|
|
bool hide = (ui_globals::panels_status == HIDE_RIGHT_PANEL || ui_globals::panels_status == HIDE_BOTH);
|
|
float x2 = (hide) ? origx2 : origx1 + 2.0f * UI_BOX_LR_BORDER;
|
|
|
|
// set left-right arrows dimension
|
|
float ar_x0 = 0.5f * (x2 + origx1) - 0.5f * lr_arrow_width;
|
|
float ar_y0 = 0.5f * (origy2 + origy1) + 0.1f * line_height;
|
|
float ar_x1 = ar_x0 + lr_arrow_width;
|
|
float ar_y1 = 0.5f * (origy2 + origy1) + 0.9f * line_height;
|
|
|
|
//machine().ui().draw_outlined_box(container, origx1, origy1, origx2, origy2, UI_BACKGROUND_COLOR);
|
|
mui.draw_outlined_box(container, origx1, origy1, origx2, origy2, rgb_t(0xEF, 0x12, 0x47, 0x7B));
|
|
|
|
if (mouse_hit && origx1 <= mouse_x && x2 > mouse_x && origy1 <= mouse_y && origy2 > mouse_y)
|
|
{
|
|
fgcolor = UI_MOUSEOVER_COLOR;
|
|
hover = HOVER_RPANEL_ARROW;
|
|
}
|
|
|
|
if (hide)
|
|
{
|
|
draw_arrow(container, ar_x0, ar_y0, ar_x1, ar_y1, fgcolor, ROT90 ^ ORIENTATION_FLIP_X);
|
|
return;
|
|
}
|
|
|
|
draw_arrow(container, ar_x0, ar_y0, ar_x1, ar_y1, fgcolor, ROT90);
|
|
origx1 = x2;
|
|
origy1 = draw_right_box_title(origx1, origy1, origx2, origy2);
|
|
|
|
if (ui_globals::rpanel == RP_IMAGES)
|
|
arts_render(selectedref, origx1, origy1, origx2, origy2);
|
|
else
|
|
infos_render(selectedref, origx1, origy1, origx2, origy2);
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
// ctor
|
|
//-------------------------------------------------
|
|
|
|
ui_software_parts::ui_software_parts(running_machine &machine, render_container *container, std::unordered_map<std::string, std::string> parts, ui_software_info *ui_info) : ui_menu(machine, container)
|
|
{
|
|
m_parts = parts;
|
|
m_uiinfo = ui_info;
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
// dtor
|
|
//-------------------------------------------------
|
|
|
|
ui_software_parts::~ui_software_parts()
|
|
{
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
// populate
|
|
//-------------------------------------------------
|
|
|
|
void ui_software_parts::populate()
|
|
{
|
|
for (auto & elem : m_parts)
|
|
item_append(elem.first.c_str(), elem.second.c_str(), 0, (void *)&elem);
|
|
|
|
item_append(MENU_SEPARATOR_ITEM, nullptr, 0, nullptr);
|
|
customtop = machine().ui().get_line_height() + (3.0f * UI_BOX_TB_BORDER);
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
// handle
|
|
//-------------------------------------------------
|
|
|
|
void ui_software_parts::handle()
|
|
{
|
|
// process the menu
|
|
const ui_menu_event *event = process(0);
|
|
if (event != nullptr && event->iptkey == IPT_UI_SELECT && event->itemref != nullptr)
|
|
for (auto & elem : m_parts)
|
|
if ((void*)&elem == event->itemref)
|
|
{
|
|
std::string error_string;
|
|
std::string string_list = std::string(m_uiinfo->listname).append(":").append(m_uiinfo->shortname).append(":").append(elem.first).append(":").append(m_uiinfo->instance);
|
|
machine().options().set_value(OPTION_SOFTWARENAME, string_list.c_str(), OPTION_PRIORITY_CMDLINE, error_string);
|
|
|
|
reselect_last::driver = m_uiinfo->driver->name;
|
|
reselect_last::software = m_uiinfo->shortname;
|
|
reselect_last::swlist = m_uiinfo->listname;
|
|
reselect_last::set(true);
|
|
|
|
std::string snap_list = std::string(m_uiinfo->listname).append("/").append(m_uiinfo->shortname);
|
|
machine().options().set_value(OPTION_SNAPNAME, snap_list.c_str(), OPTION_PRIORITY_CMDLINE, error_string);
|
|
|
|
machine().manager().schedule_new_driver(*m_uiinfo->driver);
|
|
machine().schedule_hard_reset();
|
|
ui_menu::stack_reset(machine());
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
// perform our special rendering
|
|
//-------------------------------------------------
|
|
|
|
void ui_software_parts::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2)
|
|
{
|
|
float width;
|
|
ui_manager &mui = machine().ui();
|
|
mui.draw_text_full(container, _("Software part selection:"), 0.0f, 0.0f, 1.0f, JUSTIFY_CENTER, WRAP_TRUNCATE,
|
|
DRAW_NONE, ARGB_WHITE, ARGB_BLACK, &width, nullptr);
|
|
width += 2 * UI_BOX_LR_BORDER;
|
|
float maxwidth = MAX(origx2 - origx1, width);
|
|
|
|
// compute our bounds
|
|
float x1 = 0.5f - 0.5f * maxwidth;
|
|
float x2 = x1 + maxwidth;
|
|
float y1 = origy1 - top;
|
|
float y2 = origy1 - UI_BOX_TB_BORDER;
|
|
|
|
// draw a box
|
|
mui.draw_outlined_box(container, x1, y1, x2, y2, UI_GREEN_COLOR);
|
|
|
|
// take off the borders
|
|
x1 += UI_BOX_LR_BORDER;
|
|
x2 -= UI_BOX_LR_BORDER;
|
|
y1 += UI_BOX_TB_BORDER;
|
|
|
|
// draw the text within it
|
|
mui.draw_text_full(container, _("Software part selection:"), x1, y1, x2 - x1, JUSTIFY_CENTER, WRAP_TRUNCATE,
|
|
DRAW_NORMAL, UI_TEXT_COLOR, UI_TEXT_BG_COLOR, nullptr, nullptr);
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
// ctor
|
|
//-------------------------------------------------
|
|
|
|
ui_bios_selection::ui_bios_selection(running_machine &machine, render_container *container, std::vector<s_bios> biosname, void *_driver, bool _software, bool _inlist) : ui_menu(machine, container)
|
|
{
|
|
m_bios = biosname;
|
|
m_driver = _driver;
|
|
m_software = _software;
|
|
m_inlist = _inlist;
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
// dtor
|
|
//-------------------------------------------------
|
|
|
|
ui_bios_selection::~ui_bios_selection()
|
|
{
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
// populate
|
|
//-------------------------------------------------
|
|
|
|
void ui_bios_selection::populate()
|
|
{
|
|
for (auto & elem : m_bios)
|
|
item_append(elem.name.c_str(), nullptr, 0, (void *)&elem.name);
|
|
|
|
item_append(MENU_SEPARATOR_ITEM, nullptr, 0, nullptr);
|
|
customtop = machine().ui().get_line_height() + (3.0f * UI_BOX_TB_BORDER);
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
// handle
|
|
//-------------------------------------------------
|
|
|
|
void ui_bios_selection::handle()
|
|
{
|
|
// process the menu
|
|
const ui_menu_event *event = process(0);
|
|
emu_options &moptions = machine().options();
|
|
if (event != nullptr && event->iptkey == IPT_UI_SELECT && event->itemref != nullptr)
|
|
for (auto & elem : m_bios)
|
|
if ((void*)&elem.name == event->itemref)
|
|
{
|
|
if (!m_software)
|
|
{
|
|
const game_driver *s_driver = (const game_driver *)m_driver;
|
|
reselect_last::driver = s_driver->name;
|
|
if (m_inlist)
|
|
reselect_last::software = "[Start empty]";
|
|
else
|
|
{
|
|
reselect_last::software.clear();
|
|
reselect_last::swlist.clear();
|
|
reselect_last::set(true);
|
|
}
|
|
|
|
std::string error;
|
|
moptions.set_value("bios", elem.id, OPTION_PRIORITY_CMDLINE, error);
|
|
machine().manager().schedule_new_driver(*s_driver);
|
|
machine().schedule_hard_reset();
|
|
ui_menu::stack_reset(machine());
|
|
}
|
|
else
|
|
{
|
|
ui_software_info *ui_swinfo = (ui_software_info *)m_driver;
|
|
std::string error;
|
|
machine().options().set_value("bios", elem.id, OPTION_PRIORITY_CMDLINE, error);
|
|
driver_enumerator drivlist(machine().options(), *ui_swinfo->driver);
|
|
drivlist.next();
|
|
software_list_device *swlist = software_list_device::find_by_name(drivlist.config(), ui_swinfo->listname.c_str());
|
|
software_info *swinfo = swlist->find(ui_swinfo->shortname.c_str());
|
|
if (!machine().ui().options().skip_parts_menu() && swinfo->has_multiple_parts(ui_swinfo->interface.c_str()))
|
|
{
|
|
std::unordered_map<std::string, std::string> parts;
|
|
for (const software_part *swpart = swinfo->first_part(); swpart != nullptr; swpart = swpart->next())
|
|
{
|
|
if (swpart->matches_interface(ui_swinfo->interface.c_str()))
|
|
{
|
|
std::string menu_part_name(swpart->name());
|
|
if (swpart->feature("part_id") != nullptr)
|
|
menu_part_name.assign("(").append(swpart->feature("part_id")).append(")");
|
|
parts.emplace(swpart->name(), menu_part_name);
|
|
}
|
|
}
|
|
ui_menu::stack_push(global_alloc_clear<ui_software_parts>(machine(), container, parts, ui_swinfo));
|
|
return;
|
|
}
|
|
std::string error_string;
|
|
std::string string_list = std::string(ui_swinfo->listname).append(":").append(ui_swinfo->shortname).append(":").append(ui_swinfo->part).append(":").append(ui_swinfo->instance);
|
|
moptions.set_value(OPTION_SOFTWARENAME, string_list.c_str(), OPTION_PRIORITY_CMDLINE, error_string);
|
|
std::string snap_list = std::string(ui_swinfo->listname).append(PATH_SEPARATOR).append(ui_swinfo->shortname);
|
|
moptions.set_value(OPTION_SNAPNAME, snap_list.c_str(), OPTION_PRIORITY_CMDLINE, error_string);
|
|
reselect_last::driver = drivlist.driver().name;
|
|
reselect_last::software = ui_swinfo->shortname;
|
|
reselect_last::swlist = ui_swinfo->listname;
|
|
reselect_last::set(true);
|
|
machine().manager().schedule_new_driver(drivlist.driver());
|
|
machine().schedule_hard_reset();
|
|
ui_menu::stack_reset(machine());
|
|
}
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
// perform our special rendering
|
|
//-------------------------------------------------
|
|
|
|
void ui_bios_selection::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2)
|
|
{
|
|
float width;
|
|
ui_manager &mui = machine().ui();
|
|
mui.draw_text_full(container, _("Bios selection:"), 0.0f, 0.0f, 1.0f, JUSTIFY_CENTER, WRAP_TRUNCATE,
|
|
DRAW_NONE, ARGB_WHITE, ARGB_BLACK, &width, nullptr);
|
|
width += 2 * UI_BOX_LR_BORDER;
|
|
float maxwidth = MAX(origx2 - origx1, width);
|
|
|
|
// compute our bounds
|
|
float x1 = 0.5f - 0.5f * maxwidth;
|
|
float x2 = x1 + maxwidth;
|
|
float y1 = origy1 - top;
|
|
float y2 = origy1 - UI_BOX_TB_BORDER;
|
|
|
|
// draw a box
|
|
mui.draw_outlined_box(container, x1, y1, x2, y2, UI_GREEN_COLOR);
|
|
|
|
// take off the borders
|
|
x1 += UI_BOX_LR_BORDER;
|
|
x2 -= UI_BOX_LR_BORDER;
|
|
y1 += UI_BOX_TB_BORDER;
|
|
|
|
// draw the text within it
|
|
mui.draw_text_full(container, _("Bios selection:"), x1, y1, x2 - x1, JUSTIFY_CENTER, WRAP_TRUNCATE,
|
|
DRAW_NORMAL, UI_TEXT_COLOR, UI_TEXT_BG_COLOR, nullptr, nullptr);
|
|
}
|