// license:BSD-3-Clause // copyright-holders:Maurizio Petrarota, Vas Crabb /********************************************************************* ui/auditmenu.cpp Internal UI user interface. *********************************************************************/ #include "emu.h" #include "ui/auditmenu.h" #include "ui/ui.h" #include "audit.h" #include "drivenum.h" #include extern const char UI_VERSION_TAG[]; namespace ui { namespace { void *const ITEMREF_START = reinterpret_cast(std::uintptr_t(1)); } // anonymous namespace bool sorted_game_list(const game_driver *x, const game_driver *y) { bool clonex = (x->parent[0] != '0') || x->parent[1]; int cx = -1; if (clonex) { cx = driver_list::find(x->parent); if ((0 > cx) || (driver_list::driver(cx).flags & machine_flags::IS_BIOS_ROOT)) clonex = false; } bool cloney = (y->parent[0] != '0') || y->parent[1]; int cy = -1; if (cloney) { cy = driver_list::find(y->parent); if ((0 > cy) || (driver_list::driver(cy).flags & machine_flags::IS_BIOS_ROOT)) cloney = false; } if (!clonex && !cloney) { return (core_stricmp(x->type.fullname(), y->type.fullname()) < 0); } else if (clonex && cloney) { if (!core_stricmp(x->parent, y->parent)) return (core_stricmp(x->type.fullname(), y->type.fullname()) < 0); else return (core_stricmp(driver_list::driver(cx).type.fullname(), driver_list::driver(cy).type.fullname()) < 0); } else if (!clonex && cloney) { if (!core_stricmp(x->name, y->parent)) return true; else return (core_stricmp(x->type.fullname(), driver_list::driver(cy).type.fullname()) < 0); } else { if (!core_stricmp(x->parent, y->name)) return false; else return (core_stricmp(driver_list::driver(cx).type.fullname(), y->type.fullname()) < 0); } } menu_audit::menu_audit(mame_ui_manager &mui, render_container &container, std::vector &availablesorted, mode audit_mode) : menu(mui, container) , m_worker_thread() , m_audit_mode(audit_mode) , m_total((mode::FAST == audit_mode) ? std::accumulate(availablesorted.begin(), availablesorted.end(), std::size_t(0), [] (std::size_t n, ui_system_info const &info) { return n + (info.available ? 0 : 1); }) : availablesorted.size()) , m_availablesorted(availablesorted) , m_audited(0) , m_current(nullptr) , m_phase(phase::CONSENT) { switch (m_audit_mode) { case mode::FAST: m_prompt[0] = util::string_format(_("Audit ROMs for %1$u machines marked unavailable?"), m_total); break; case mode::ALL: m_prompt[0] = util::string_format(_("Audit ROMs for all %1$u machines?"), m_total); break; } std::string filename(emulator_info::get_configname()); filename += "_avail.ini"; m_prompt[1] = util::string_format(_("(results will be saved to %1$s)"), filename); } menu_audit::~menu_audit() { } void menu_audit::custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) { switch (m_phase) { case phase::CONSENT: draw_text_box( std::begin(m_prompt), std::end(m_prompt), x, x2, y - top, y - ui().box_tb_border(), ui::text_layout::CENTER, ui::text_layout::NEVER, false, ui().colors().text_color(), UI_GREEN_COLOR, 1.0f); break; case phase::AUDIT: { // there's a race here between the total audited being updated and the next driver pointer being loaded // it doesn't matter because we redraw on every frame anyway so it sorts itself out very quickly game_driver const *const driver(m_current.load()); std::size_t const audited(m_audited.load()); std::string const text(util::string_format( _("Auditing ROMs for machine %2$u of %3$u...\n%1$s"), driver ? driver->type.fullname() : "", audited + 1, m_total)); ui().draw_text_box(container(), text.c_str(), ui::text_layout::CENTER, 0.5f, 0.5f, UI_GREEN_COLOR); } break; } } void menu_audit::populate(float &customtop, float &custombottom) { item_append(_("Start Audit"), "", 0, ITEMREF_START); customtop = (ui().get_line_height() * 2.0f) + (ui().box_tb_border() * 3.0f); } void menu_audit::handle() { switch (m_phase) { case phase::CONSENT: { event const *const menu_event(process(0)); if (menu_event && (ITEMREF_START == menu_event->itemref) && (IPT_UI_SELECT == menu_event->iptkey)) { m_phase = phase::AUDIT; m_worker_thread = std::thread( [this] () { switch (m_audit_mode) { case mode::FAST: audit_fast(); return; case mode::ALL: audit_all(); return; } throw false; }); } } break; case phase::AUDIT: process(PROCESS_CUSTOM_ONLY | PROCESS_NOINPUT); if (m_audited.load() >= m_total) { m_worker_thread.join(); save_available_machines(); reset_parent(reset_options::SELECT_FIRST); stack_pop(); } break; } } void menu_audit::audit_fast() { for (ui_system_info &info : m_availablesorted) { if (!info.available) { m_current.store(info.driver); driver_enumerator enumerator(machine().options(), info.driver->name); enumerator.next(); media_auditor auditor(enumerator); media_auditor::summary const summary(auditor.audit_media(AUDIT_VALIDATE_FAST)); info.available = (summary == media_auditor::CORRECT) || (summary == media_auditor::BEST_AVAILABLE) || (summary == media_auditor::NONE_NEEDED); // if everything looks good, include the driver info.available = (summary == media_auditor::CORRECT) || (summary == media_auditor::BEST_AVAILABLE) || (summary == media_auditor::NONE_NEEDED); ++m_audited; } } } void menu_audit::audit_all() { driver_enumerator enumerator(machine().options()); media_auditor auditor(enumerator); std::vector available(driver_list::total(), false); while (enumerator.next()) { m_current.store(&enumerator.driver()); media_auditor::summary const summary(auditor.audit_media(AUDIT_VALIDATE_FAST)); // if everything looks good, include the driver available[enumerator.current()] = (summary == media_auditor::CORRECT) || (summary == media_auditor::BEST_AVAILABLE) || (summary == media_auditor::NONE_NEEDED); ++m_audited; } for (ui_system_info &info : m_availablesorted) info.available = available[info.index]; } void menu_audit::save_available_machines() { // attempt to open the output file emu_file file(ui().options().ui_path(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS); if (file.open(emulator_info::get_configname(), "_avail.ini") == osd_file::error::NONE) { // generate header file.printf("#\n%s%s\n#\n\n", UI_VERSION_TAG, emulator_info::get_bare_build_version()); // generate available list for (ui_system_info const &info : m_availablesorted) { if (info.available) file.printf("%s\n", info.driver->name); } file.close(); } } } // namespace ui