From aac4e79d4b62decf5b53683b100525587b5091c8 Mon Sep 17 00:00:00 2001 From: npwoods Date: Wed, 24 Jul 2019 07:14:56 -0400 Subject: Refactored MAME's plugin system --- plugins/boot.lua | 28 ++---- src/frontend/mame/clifront.cpp | 3 +- src/frontend/mame/luaengine.cpp | 15 +++- src/frontend/mame/mame.cpp | 76 ++++++++++------ src/frontend/mame/pluginopts.cpp | 180 +++++++++++++++++++++++++++----------- src/frontend/mame/pluginopts.h | 35 ++++++-- src/frontend/mame/ui/miscmenu.cpp | 18 ++-- 7 files changed, 240 insertions(+), 115 deletions(-) diff --git a/plugins/boot.lua b/plugins/boot.lua index 5ed1ac803ad..98eb14baa86 100644 --- a/plugins/boot.lua +++ b/plugins/boot.lua @@ -26,27 +26,11 @@ local dir = lfs.env_replace(manager:options().entries.pluginspath:value()) package.path = dir .. "/?.lua;" .. dir .. "/?/init.lua" -local json = require('json') -local function readAll(file) - local f = io.open(file, "rb") - local content = f:read("*all") - f:close() - return content -end - -for file in lfs.dir(dir) do - if (file~="." and file~=".." and lfs.attributes(dir .. "/" .. file,"mode")=="directory") then - local filename = dir .. "/" .. file .. "/plugin.json" - local meta = json.parse(readAll(filename)) - if (meta["plugin"]["type"]=="plugin") and (mame_manager:plugins().entries[meta["plugin"]["name"]]~=nil) then - local entry = mame_manager:plugins().entries[meta["plugin"]["name"]] - if (entry:value()==true) then - emu.print_verbose("Starting plugin " .. meta["plugin"]["name"] .. "...") - plugin = require(meta["plugin"]["name"]) - if plugin.set_folder~=nil then plugin.set_folder(dir .. "/" .. file) end - plugin.startplugin(); - end - end +for _,entry in pairs(manager:plugins()) do + if (entry.type == "plugin" and entry.start) then + emu.print_verbose("Starting plugin " .. entry.name .. "...") + plugin = require(entry.name) + if plugin.set_folder~=nil then plugin.set_folder(entry.directory) end + plugin.startplugin(); end end - diff --git a/src/frontend/mame/clifront.cpp b/src/frontend/mame/clifront.cpp index 7a364f4996a..3c0e5c21544 100644 --- a/src/frontend/mame/clifront.cpp +++ b/src/frontend/mame/clifront.cpp @@ -1666,7 +1666,8 @@ void cli_frontend::execute_commands(const char *exename) std::string pluginpath; while (iter.next(pluginpath)) { - plugin_opts.parse_json(pluginpath); + osd_subst_env(pluginpath, pluginpath); + plugin_opts.scan_directory(pluginpath, true); } emu_file file_plugin(OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS); if (file_plugin.open("plugin.ini") != osd_file::error::NONE) diff --git a/src/frontend/mame/luaengine.cpp b/src/frontend/mame/luaengine.cpp index f50c1d20907..dcd2434881d 100644 --- a/src/frontend/mame/luaengine.cpp +++ b/src/frontend/mame/luaengine.cpp @@ -2507,7 +2507,20 @@ void lua_engine::initialize() sol().registry().new_usertype("manager", "new", sol::no_constructor, "machine", &machine_manager::machine, "options", [](mame_machine_manager &m) { return static_cast(&m.options()); }, - "plugins", [](mame_machine_manager &m) { return static_cast(&m.plugins()); }, + "plugins", [this](mame_machine_manager &m) { + sol::table table = sol().create_table(); + for (auto &curentry : m.plugins().plugins()) + { + sol::table plugin_table = sol().create_table(); + plugin_table["name"] = curentry.m_name; + plugin_table["description"] = curentry.m_description; + plugin_table["type"] = curentry.m_type; + plugin_table["directory"] = curentry.m_directory; + plugin_table["start"] = curentry.m_start; + table[curentry.m_name] = plugin_table; + } + return table; + }, "ui", &mame_machine_manager::ui); sol()["manager"] = std::ref(*mame_machine_manager::instance()); sol()["mame_manager"] = std::ref(*mame_machine_manager::instance()); diff --git a/src/frontend/mame/mame.cpp b/src/frontend/mame/mame.cpp index f3c08f661e9..9334cb3445b 100644 --- a/src/frontend/mame/mame.cpp +++ b/src/frontend/mame/mame.cpp @@ -88,6 +88,11 @@ void mame_machine_manager::schedule_new_driver(const game_driver &driver) /*************************************************************************** CORE IMPLEMENTATION ***************************************************************************/ + +//------------------------------------------------- +// update_machine +//------------------------------------------------- + void mame_machine_manager::update_machine() { m_lua->set_machine(m_machine); @@ -95,7 +100,11 @@ void mame_machine_manager::update_machine() } -std::vector split(const std::string &text, char sep) +//------------------------------------------------- +// split +//------------------------------------------------- + +static std::vector split(const std::string &text, char sep) { std::vector tokens; std::size_t start = 0, end = 0; @@ -110,18 +119,27 @@ std::vector split(const std::string &text, char sep) return tokens; } + +//------------------------------------------------- +// start_luaengine +//------------------------------------------------- + void mame_machine_manager::start_luaengine() { if (options().plugins()) { + // scan all plugin directories path_iterator iter(options().plugins_path()); std::string pluginpath; while (iter.next(pluginpath)) { - m_plugins->parse_json(pluginpath); + // user may specify environment variables; subsitute them + osd_subst_env(pluginpath, pluginpath); + + // and then scan the directory recursively + m_plugins->scan_directory(pluginpath, true); } - std::vector include = split(options().plugin(), ','); - std::vector exclude = split(options().no_plugin(), ','); + { // parse the file // attempt to open the output file @@ -130,7 +148,7 @@ void mame_machine_manager::start_luaengine() { try { - m_plugins->parse_ini_file((util::core_file&)file, OPTION_PRIORITY_MAME_INI, OPTION_PRIORITY_MAME_INI < OPTION_PRIORITY_DRIVER_INI, false); + m_plugins->parse_ini_file((util::core_file&)file); } catch (options_exception &) { @@ -138,31 +156,34 @@ void mame_machine_manager::start_luaengine() } } } - for (auto &curentry : m_plugins->entries()) + + // process includes + for (const std::string &incl : split(options().plugin(), ',')) { - if (curentry->type() != core_options::option_type::HEADER) - { - if (std::find(include.begin(), include.end(), curentry->name()) != include.end()) - { - m_plugins->set_value(curentry->name(), "1", OPTION_PRIORITY_CMDLINE); - } - if (std::find(exclude.begin(), exclude.end(), curentry->name()) != exclude.end()) - { - m_plugins->set_value(curentry->name(), "0", OPTION_PRIORITY_CMDLINE); - } - } + plugin *p = m_plugins->find(incl); + if (!p) + fatalerror("Could not load plugin: %s\n", incl.c_str()); + p->m_start = true; + } + + // process excludes + for (const std::string &excl : split(options().no_plugin(), ',')) + { + plugin *p = m_plugins->find(excl); + if (!p) + fatalerror("Unknown plugin: %s\n", excl.c_str()); + p->m_start = false; } } + + // we have a special way to open the console plugin if (options().console()) { - if (m_plugins->exists(OPTION_CONSOLE)) - { - m_plugins->set_value(OPTION_CONSOLE, "1", OPTION_PRIORITY_CMDLINE); - } - else - { + plugin *p = m_plugins->find(OPTION_CONSOLE); + if (!p) fatalerror("Console plugin not found.\n"); - } + + p->m_start = true; } m_lua->initialize(); @@ -179,9 +200,10 @@ void mame_machine_manager::start_luaengine() } } -/*------------------------------------------------- - execute - run the core emulation --------------------------------------------------*/ + +//------------------------------------------------- +// execute - run the core emulation +//------------------------------------------------- int mame_machine_manager::execute() { diff --git a/src/frontend/mame/pluginopts.cpp b/src/frontend/mame/pluginopts.cpp index cfb04af21a4..ef30cf917f3 100644 --- a/src/frontend/mame/pluginopts.cpp +++ b/src/frontend/mame/pluginopts.cpp @@ -7,8 +7,10 @@ Plugin options manager. ***************************************************************************/ + #include "emu.h" #include "pluginopts.h" +#include "options.h" #include #include @@ -21,77 +23,157 @@ // PLUGIN OPTIONS //************************************************************************** -const options_entry plugin_options::s_option_entries[] = -{ - { nullptr, nullptr, OPTION_HEADER, "PLUGINS OPTIONS" }, - { nullptr } -}; - //------------------------------------------------- // plugin_options - constructor //------------------------------------------------- plugin_options::plugin_options() -: core_options() { - add_entries(plugin_options::s_option_entries); } -void plugin_options::parse_json(std::string path) +//------------------------------------------------- +// scan_directory +//------------------------------------------------- + +void plugin_options::scan_directory(const std::string &path, bool recursive) { // first try to open as a directory - osd_subst_env(path, path); osd::directory::ptr directory = osd::directory::open(path); if (directory) { // iterate over all files in the directory for (const osd::directory::entry *entry = directory->read(); entry != nullptr; entry = directory->read()) { - if (entry->type == osd::directory::entry::entry_type::FILE) + if (entry->type == osd::directory::entry::entry_type::FILE && !strcmp(entry->name, "plugin.json")) { - std::string name = entry->name; - if (name == "plugin.json") - { - std::string curfile = std::string(path).append(PATH_SEPARATOR).append(entry->name); - std::ifstream ifs(curfile); - rapidjson::IStreamWrapper isw(ifs); - rapidjson::Document document; - document.ParseStream<0>(isw); - - if (document.HasParseError()) { - std::string error(GetParseError_En(document.GetParseError())); - osd_printf_error("Unable to parse plugin definition file %s. Errors returned:\n", curfile.c_str()); - osd_printf_error("%s\n", error.c_str()); - return; - } - - if (document["plugin"].IsObject()) - { - std::string plugin_name = document["plugin"]["name"].GetString(); - std::string description = document["plugin"]["description"].GetString(); - std::string type = document["plugin"]["type"].GetString(); - bool start = false; - if (document["plugin"].HasMember("start") && (std::string(document["plugin"]["start"].GetString()) == "true")) - start = true; - - if (type=="plugin") - { - auto const it = m_descriptions.emplace(m_descriptions.end(), std::move(description)); - add_entry({ std::move(plugin_name) }, it->c_str(), option_type::BOOLEAN, start ? "1" : "0"); - } - } - - } + std::string curfile = std::string(path).append(PATH_SEPARATOR).append(entry->name); + load_plugin(curfile); } else if (entry->type == osd::directory::entry::entry_type::DIR) { - std::string name = entry->name; - if (!(name == "." || name == "..")) - { - parse_json(path + PATH_SEPARATOR + name); - } + if (recursive && strcmp(entry->name, ".") && strcmp(entry->name, "..")) + scan_directory(path + PATH_SEPARATOR + entry->name, recursive); } } } } + + +//------------------------------------------------- +// load_plugin +//------------------------------------------------- + +bool plugin_options::load_plugin(const std::string &path) +{ + std::ifstream ifs(path); + rapidjson::IStreamWrapper isw(ifs); + rapidjson::Document document; + document.ParseStream<0>(isw); + + if (document.HasParseError()) + { + std::string error(GetParseError_En(document.GetParseError())); + osd_printf_error("Unable to parse plugin definition file %s. Errors returned:\n", path.c_str()); + osd_printf_error("%s\n", error.c_str()); + return false; + } + + if (!document["plugin"].IsObject()) + { + osd_printf_error("Bad plugin definition file %s:\n", path.c_str()); + return false; + } + + size_t last_path_sep = path.find_last_of(PATH_SEPARATOR[0]); + std::string dir = last_path_sep != std::string::npos + ? path.substr(0, last_path_sep) + : "."; + + plugin p; + p.m_name = document["plugin"]["name"].GetString(); + p.m_description = document["plugin"]["description"].GetString(); + p.m_type = document["plugin"]["type"].GetString(); + p.m_directory = std::move(dir); + p.m_start = false; + if (document["plugin"].HasMember("start") && (std::string(document["plugin"]["start"].GetString()) == "true")) + p.m_start = true; + + m_plugins.push_back(std::move(p)); + return true; +} + + +//------------------------------------------------- +// find +//------------------------------------------------- + +plugin *plugin_options::find(const std::string &name) +{ + auto iter = std::find_if( + m_plugins.begin(), + m_plugins.end(), + [&name](const plugin &p) { return name == p.m_name; }); + + return iter != m_plugins.end() + ? &*iter + : nullptr; +} + + +//------------------------------------------------- +// create_core_options +//------------------------------------------------- + +static void create_core_options(core_options &opts, const plugin_options &plugin_opts) +{ + // we're sort of abusing core_options to just get INI file parsing, so we'll build a + // core_options structure for the sole purpose of parsing an INI file, and then reflect + // the data back + static const options_entry s_option_entries[] = + { + { nullptr, nullptr, OPTION_HEADER, "PLUGINS OPTIONS" }, + { nullptr } + }; + opts.add_entries(s_option_entries); + + // create an entry for each option + for (const plugin &p : plugin_opts.plugins()) + { + opts.add_entry( + { p.m_name }, + nullptr, + core_options::option_type::BOOLEAN, + p.m_start ? "1" : "0"); + } +} + + +//------------------------------------------------- +// parse_ini_file +//------------------------------------------------- + +void plugin_options::parse_ini_file(util::core_file &inifile) +{ + core_options opts; + create_core_options(opts, *this); + + // parse the INI file + opts.parse_ini_file(inifile, OPTION_PRIORITY_NORMAL, true, true); + + // and reflect these options back + for (plugin &p : m_plugins) + p.m_start = opts.bool_value(p.m_name.c_str()); +} + + +//------------------------------------------------- +// output_ini +//------------------------------------------------- + +std::string plugin_options::output_ini() const +{ + core_options opts; + create_core_options(opts, *this); + return opts.output_ini(); +} diff --git a/src/frontend/mame/pluginopts.h b/src/frontend/mame/pluginopts.h index 27e57104293..c722fb8e2a2 100644 --- a/src/frontend/mame/pluginopts.h +++ b/src/frontend/mame/pluginopts.h @@ -7,27 +7,50 @@ Plugin options manager. ***************************************************************************/ + #ifndef MAME_FRONTEND_PLUGINOPTS_H #define MAME_FRONTEND_PLUGINOPTS_H #pragma once -#include "options.h" - #include #include -class plugin_options : public core_options +// ======================> plugin + +struct plugin +{ + std::string m_name; + std::string m_description; + std::string m_type; + std::string m_directory; + bool m_start; +}; + + +// ======================> plugin_options + +class plugin_options { public: plugin_options(); - void parse_json(std::string path); + // accessors + std::list &plugins() { return m_plugins; } + const std::list &plugins() const { return m_plugins; } + + // methods + void scan_directory(const std::string &path, bool recursive); + bool load_plugin(const std::string &path); + plugin *find(const std::string &name); + + // INI functionality + void parse_ini_file(util::core_file &inifile); + std::string output_ini() const; private: - static const options_entry s_option_entries[]; - std::list m_descriptions; + std::list m_plugins; }; #endif // MAME_FRONTEND_PLUGINOPTS_H diff --git a/src/frontend/mame/ui/miscmenu.cpp b/src/frontend/mame/ui/miscmenu.cpp index fbb84d0aec3..3591a2a0ae3 100644 --- a/src/frontend/mame/ui/miscmenu.cpp +++ b/src/frontend/mame/ui/miscmenu.cpp @@ -880,9 +880,12 @@ void menu_plugins_configure::handle() { if (menu_event->iptkey == IPT_UI_LEFT || menu_event->iptkey == IPT_UI_RIGHT || menu_event->iptkey == IPT_UI_SELECT) { - int oldval = plugins.int_value((const char*)menu_event->itemref); - plugins.set_value((const char*)menu_event->itemref, oldval == 1 ? 0 : 1, OPTION_PRIORITY_CMDLINE); - changed = true; + plugin *p = plugins.find((const char*)menu_event->itemref); + if (p) + { + p->m_start = !p->m_start; + changed = true; + } } } if (changed) @@ -897,13 +900,10 @@ void menu_plugins_configure::populate(float &customtop, float &custombottom) { plugin_options& plugins = mame_machine_manager::instance()->plugins(); - for (auto &curentry : plugins.entries()) + for (auto &curentry : plugins.plugins()) { - if (curentry->type() != OPTION_HEADER) - { - auto enabled = !strcmp(curentry->value(), "1"); - item_append_on_off(curentry->description(), enabled, 0, (void *)(uintptr_t)curentry->name().c_str()); - } + bool enabled = curentry.m_start; + item_append_on_off(curentry.m_description, enabled, 0, (void *)(uintptr_t)curentry.m_name.c_str()); } item_append(menu_item_type::SEPARATOR); customtop = ui().get_line_height() + (3.0f * ui().box_tb_border()); -- cgit v1.2.3