From 13910382a5e846677f30d1645bfb44e4983de557 Mon Sep 17 00:00:00 2001 From: npwoods Date: Fri, 16 Dec 2022 14:03:59 -0500 Subject: osd/modules/file: Don't magically substitute environment variables when opening files. (#9859) * util/options.cpp: Added option types for single and multiple paths. * util/options.cpp: Substitute environment variables in values from defaults and INI files. * ui/dirmenu.cpp: Removed hard-coded list of multi-path options. * plugins: Don't substitute environment variables in path options. --- plugins/autofire/autofire_save.lua | 2 +- plugins/boot.lua | 2 +- plugins/cheat/init.lua | 4 +- plugins/cheatfind/init.lua | 2 +- plugins/console/init.lua | 2 +- plugins/data/data_hiscore.lua | 6 +- plugins/data/database.lua | 4 +- plugins/hiscore/init.lua | 2 +- plugins/inputmacro/inputmacro_persist.lua | 2 +- plugins/portname/init.lua | 2 +- plugins/timecode/init.lua | 2 +- plugins/timer/timer_persist.lua | 2 +- src/emu/emuopts.cpp | 62 ++++---- src/frontend/mame/clifront.cpp | 2 +- src/frontend/mame/luaengine.cpp | 2 +- src/frontend/mame/mame.cpp | 7 +- src/frontend/mame/ui/dirmenu.cpp | 142 ++++++++++-------- src/frontend/mame/ui/moptions.cpp | 44 +++--- src/frontend/mame/ui/submenu.cpp | 6 +- src/lib/util/options.cpp | 201 ++++++++++++++++++++++---- src/lib/util/options.h | 23 ++- src/osd/modules/file/posixdir.cpp | 2 +- src/osd/modules/file/posixfile.cpp | 2 - src/osd/modules/file/winfile.cpp | 6 +- src/osd/modules/file/winrtfile.cpp | 6 +- src/osd/modules/lib/osdobj_common.cpp | 4 +- src/osd/modules/render/bgfx/chainmanager.cpp | 4 +- src/osd/modules/render/bgfx/effectmanager.cpp | 2 +- src/osd/modules/render/bgfx/shadermanager.cpp | 2 +- src/osd/sdl/sdlmain.cpp | 4 +- src/osd/windows/winmain.cpp | 8 +- 31 files changed, 368 insertions(+), 193 deletions(-) diff --git a/plugins/autofire/autofire_save.lua b/plugins/autofire/autofire_save.lua index 5ca4cd217b6..52586c461ad 100644 --- a/plugins/autofire/autofire_save.lua +++ b/plugins/autofire/autofire_save.lua @@ -1,7 +1,7 @@ local lib = {} local function get_settings_path() - return emu.subst_env(manager.machine.options.entries.homepath:value():match('([^;]+)')) .. '/autofire' + return manager.machine.options.entries.homepath:value():match('([^;]+)') .. '/autofire' end local function get_settings_filename() diff --git a/plugins/boot.lua b/plugins/boot.lua index 712fe0db7c6..390ce652ff6 100644 --- a/plugins/boot.lua +++ b/plugins/boot.lua @@ -8,7 +8,7 @@ _G.N_ = function (message) return message end _G.N_p = function (context, message) return message end _G.emu.plugin = {} -- table to contain plugin interfaces -- substitute environment variables in the plugins path from options -local dirs = emu.subst_env(manager.options.entries.pluginspath:value()) +local dirs = manager.options.entries.pluginspath:value() -- and split the paths apart and make them suitable for package.path package.path = "" diff --git a/plugins/cheat/init.lua b/plugins/cheat/init.lua index 896fc1e101b..47d7db26cc2 100644 --- a/plugins/cheat/init.lua +++ b/plugins/cheat/init.lua @@ -130,7 +130,7 @@ function cheat.startplugin() local function load_hotkeys() local json = require("json") - local file = io.open(emu.subst_env(manager.machine.options.entries.cheatpath:value():match("([^;]+)")) .. "/" .. cheatname .. "_hotkeys.json", "r") + local file = io.open(manager.machine.options.entries.cheatpath:value():match("([^;]+)") .. "/" .. cheatname .. "_hotkeys.json", "r") if not file then return end @@ -154,7 +154,7 @@ function cheat.startplugin() end end end - local path = emu.subst_env(manager.machine.options.entries.cheatpath:value():match("([^;]+)")) + local path = manager.machine.options.entries.cheatpath:value():match("([^;]+)") local filepath = path .. "/" .. cheatname .. "_hotkeys.json" if #hotkeys > 0 then local json = require("json") diff --git a/plugins/cheatfind/init.lua b/plugins/cheatfind/init.lua index 96617e12fb6..7c3cea73e8b 100644 --- a/plugins/cheatfind/init.lua +++ b/plugins/cheatfind/init.lua @@ -965,7 +965,7 @@ function cheatfind.startplugin() end end end - cheat_save.path = emu.subst_env(manager.machine.options.entries.cheatpath:value()):match("([^;]+)") + cheat_save.path = manager.machine.options.entries.cheatpath:value():match("([^;]+)") cheat_save.filename = string.format("%s/%s", cheat_save.path, setname) cheat_save.name = cheat.desc local json = require("json") diff --git a/plugins/console/init.lua b/plugins/console/init.lua index 6b8643fe168..1b40ac50bee 100644 --- a/plugins/console/init.lua +++ b/plugins/console/init.lua @@ -243,7 +243,7 @@ function console.startplugin() end if (not started) then -- options are not available in startplugin, so we load the history here - local homepath = emu.subst_env(manager.options.entries.homepath:value():match("([^;]+)")) + local homepath = manager.options.entries.homepath:value():match("([^;]+)") history_fullpath = homepath .. '/' .. history_file ln.loadhistory(history_fullpath) started = true diff --git a/plugins/data/data_hiscore.lua b/plugins/data/data_hiscore.lua index c0794b20195..f05e4ddd173 100644 --- a/plugins/data/data_hiscore.lua +++ b/plugins/data/data_hiscore.lua @@ -5,7 +5,7 @@ local dat = {} local env = {} local output local curset -local path = emu.subst_env(mame_manager.ui.options.entries.historypath:value():gsub("([^;]+)", "%1/hi2txt")) +local path = mame_manager.ui.options.entries.historypath:value():gsub("([^;]+)", "%1/hi2txt") local htmlentites = { ["amp"] = "&", ["quot"] = utf8.char(34), @@ -355,7 +355,7 @@ local htmlentites = { function env.open(file, size, swap) if file == ".hi" then local path = "hi" - local ini = emu.file(emu.subst_env(manager.options.entries.inipath:value()), 1) + local ini = emu.file(manager.options.entries.inipath:value(), 1) local ret = ini:open("hiscore.ini") if not ret then local inifile = ini:read(ini:size()) @@ -369,7 +369,7 @@ function env.open(file, size, swap) end file = path .. "/" .. curset .. ".hi" else - file = emu.subst_env(manager.options.entries.nvram_directory:value()) .. "/" .. curset .. "/" .. file + file = manager.options.entries.nvram_directory:value() .. "/" .. curset .. "/" .. file end local f = io.open(file, "rb") local content = f:read("*all") diff --git a/plugins/data/database.lua b/plugins/data/database.lua index e0249182409..4fb4eaa2ddf 100644 --- a/plugins/data/database.lua +++ b/plugins/data/database.lua @@ -10,7 +10,7 @@ local function check_db_result(msg) end local function settings_path() - return emu.subst_env(manager.machine.options.entries.homepath:value():match('([^;]+)')) .. '/data' + return manager.machine.options.entries.homepath:value():match('([^;]+)') .. '/data' end local function check_version_table() @@ -147,7 +147,7 @@ end function dbtable.open_data_file(file) local fh, filepath for path in mame_manager.ui.options.entries.historypath:value():gmatch('([^;]+)') do - filepath = emu.subst_env(path) .. '/' .. file + filepath = path .. '/' .. file fh = io.open(filepath, 'r') if fh then break diff --git a/plugins/hiscore/init.lua b/plugins/hiscore/init.lua index 652061c92cf..5f93b91bd1a 100644 --- a/plugins/hiscore/init.lua +++ b/plugins/hiscore/init.lua @@ -23,7 +23,7 @@ end function hiscore.startplugin() local function get_data_path() - return emu.subst_env(manager.machine.options.entries.homepath:value():match('([^;]+)')) .. '/hiscore' + return manager.machine.options.entries.homepath:value():match('([^;]+)') .. '/hiscore' end -- configuration diff --git a/plugins/inputmacro/inputmacro_persist.lua b/plugins/inputmacro/inputmacro_persist.lua index 6c2aa6fd188..6519f3a048a 100644 --- a/plugins/inputmacro/inputmacro_persist.lua +++ b/plugins/inputmacro/inputmacro_persist.lua @@ -5,7 +5,7 @@ -- Helpers local function settings_path() - return emu.subst_env(manager.machine.options.entries.homepath:value():match('([^;]+)')) .. '/inputmacro' + return manager.machine.options.entries.homepath:value():match('([^;]+)') .. '/inputmacro' end local function settings_filename() diff --git a/plugins/portname/init.lua b/plugins/portname/init.lua index 1f2c8d80874..b855f27cc6e 100644 --- a/plugins/portname/init.lua +++ b/plugins/portname/init.lua @@ -28,7 +28,7 @@ local portname = exports function portname.startplugin() local json = require("json") - local ctrlrpath = emu.subst_env(manager.options.entries.ctrlrpath:value():match("([^;]+)")) + local ctrlrpath = manager.options.entries.ctrlrpath:value():match("([^;]+)") local function get_filename(nosoft) local filename if emu.softname() ~= "" and not nosoft then diff --git a/plugins/timecode/init.lua b/plugins/timecode/init.lua index 09e49229cae..93435ef2870 100644 --- a/plugins/timecode/init.lua +++ b/plugins/timecode/init.lua @@ -34,7 +34,7 @@ function timecode.startplugin() local function get_settings_path() - return emu.subst_env(manager.machine.options.entries.homepath:value():match('([^;]+)')) .. '/timecode' + return manager.machine.options.entries.homepath:value():match('([^;]+)') .. '/timecode' end diff --git a/plugins/timer/timer_persist.lua b/plugins/timer/timer_persist.lua index 4781c263ba8..cb24b485dcb 100644 --- a/plugins/timer/timer_persist.lua +++ b/plugins/timer/timer_persist.lua @@ -130,7 +130,7 @@ end local function open_database() -- make sure settings directory exists - local dir = emu.subst_env(manager.machine.options.entries.homepath:value():match('([^;]+)')) .. '/timer' + local dir = manager.machine.options.entries.homepath:value():match('([^;]+)') .. '/timer' local attr = lfs.attributes(dir) if not attr then lfs.mkdir(dir) diff --git a/src/emu/emuopts.cpp b/src/emu/emuopts.cpp index a3972edcb0e..41b33874e08 100644 --- a/src/emu/emuopts.cpp +++ b/src/emu/emuopts.cpp @@ -37,30 +37,30 @@ const options_entry emu_options::s_option_entries[] = // search path options { nullptr, nullptr, core_options::option_type::HEADER, "CORE SEARCH PATH OPTIONS" }, - { OPTION_PLUGINDATAPATH, ".", core_options::option_type::STRING, "path to base folder for plugin data (read/write)" }, - { OPTION_MEDIAPATH ";rp;biospath;bp", "roms", core_options::option_type::STRING, "path to ROM sets and hard disk images" }, - { OPTION_HASHPATH ";hash_directory;hash", "hash", core_options::option_type::STRING, "path to software definition files" }, - { OPTION_SAMPLEPATH ";sp", "samples", core_options::option_type::STRING, "path to audio sample sets" }, - { OPTION_ARTPATH, "artwork", core_options::option_type::STRING, "path to artwork files" }, - { OPTION_CTRLRPATH, "ctrlr", core_options::option_type::STRING, "path to controller definitions" }, - { OPTION_INIPATH, ".;ini;ini/presets", core_options::option_type::STRING, "path to ini files" }, - { OPTION_FONTPATH, ".", core_options::option_type::STRING, "path to font files" }, - { OPTION_CHEATPATH, "cheat", core_options::option_type::STRING, "path to cheat files" }, - { OPTION_CROSSHAIRPATH, "crosshair", core_options::option_type::STRING, "path to crosshair files" }, - { OPTION_PLUGINSPATH, "plugins", core_options::option_type::STRING, "path to plugin files" }, - { OPTION_LANGUAGEPATH, "language", core_options::option_type::STRING, "path to UI translation files" }, - { OPTION_SWPATH, "software", core_options::option_type::STRING, "path to loose software" }, + { OPTION_PLUGINDATAPATH, ".", core_options::option_type::PATH, "path to base folder for plugin data (read/write)" }, + { OPTION_MEDIAPATH ";rp;biospath;bp", "roms", core_options::option_type::MULTIPATH, "path to ROM sets and hard disk images" }, + { OPTION_HASHPATH ";hash_directory;hash", "hash", core_options::option_type::MULTIPATH, "path to software definition files" }, + { OPTION_SAMPLEPATH ";sp", "samples", core_options::option_type::MULTIPATH, "path to audio sample sets" }, + { OPTION_ARTPATH, "artwork", core_options::option_type::MULTIPATH, "path to artwork files" }, + { OPTION_CTRLRPATH, "ctrlr", core_options::option_type::MULTIPATH, "path to controller definitions" }, + { OPTION_INIPATH, ".;ini;ini/presets", core_options::option_type::MULTIPATH, "path to ini files" }, + { OPTION_FONTPATH, ".", core_options::option_type::MULTIPATH, "path to font files" }, + { OPTION_CHEATPATH, "cheat", core_options::option_type::MULTIPATH, "path to cheat files" }, + { OPTION_CROSSHAIRPATH, "crosshair", core_options::option_type::MULTIPATH, "path to crosshair files" }, + { OPTION_PLUGINSPATH, "plugins", core_options::option_type::MULTIPATH, "path to plugin files" }, + { OPTION_LANGUAGEPATH, "language", core_options::option_type::MULTIPATH, "path to UI translation files" }, + { OPTION_SWPATH, "software", core_options::option_type::MULTIPATH, "path to loose software" }, // output directory options { nullptr, nullptr, core_options::option_type::HEADER, "CORE OUTPUT DIRECTORY OPTIONS" }, - { OPTION_CFG_DIRECTORY, "cfg", core_options::option_type::STRING, "directory to save configurations" }, - { OPTION_NVRAM_DIRECTORY, "nvram", core_options::option_type::STRING, "directory to save NVRAM contents" }, - { OPTION_INPUT_DIRECTORY, "inp", core_options::option_type::STRING, "directory to save input device logs" }, - { OPTION_STATE_DIRECTORY, "sta", core_options::option_type::STRING, "directory to save states" }, - { OPTION_SNAPSHOT_DIRECTORY, "snap", core_options::option_type::STRING, "directory to save/load screenshots" }, - { OPTION_DIFF_DIRECTORY, "diff", core_options::option_type::STRING, "directory to save hard drive image difference files" }, - { OPTION_COMMENT_DIRECTORY, "comments", core_options::option_type::STRING, "directory to save debugger comments" }, - { OPTION_SHARE_DIRECTORY, "share", core_options::option_type::STRING, "directory to share with emulated machines" }, + { OPTION_CFG_DIRECTORY, "cfg", core_options::option_type::PATH, "directory to save configurations" }, + { OPTION_NVRAM_DIRECTORY, "nvram", core_options::option_type::PATH, "directory to save NVRAM contents" }, + { OPTION_INPUT_DIRECTORY, "inp", core_options::option_type::PATH, "directory to save input device logs" }, + { OPTION_STATE_DIRECTORY, "sta", core_options::option_type::PATH, "directory to save states" }, + { OPTION_SNAPSHOT_DIRECTORY, "snap", core_options::option_type::PATH, "directory to save/load screenshots" }, + { OPTION_DIFF_DIRECTORY, "diff", core_options::option_type::PATH, "directory to save hard drive image difference files" }, + { OPTION_COMMENT_DIRECTORY, "comments", core_options::option_type::PATH, "directory to save debugger comments" }, + { OPTION_SHARE_DIRECTORY, "share", core_options::option_type::PATH, "directory to share with emulated machines" }, // state/playback options { nullptr, nullptr, core_options::option_type::HEADER, "CORE STATE/PLAYBACK OPTIONS" }, @@ -72,9 +72,9 @@ const options_entry emu_options::s_option_entries[] = { OPTION_RECORD ";rec", nullptr, core_options::option_type::STRING, "record an input file" }, { OPTION_EXIT_AFTER_PLAYBACK, "0", core_options::option_type::BOOLEAN, "close the program at the end of playback" }, - { OPTION_MNGWRITE, nullptr, core_options::option_type::STRING, "optional filename to write a MNG movie of the current session" }, - { OPTION_AVIWRITE, nullptr, core_options::option_type::STRING, "optional filename to write an AVI movie of the current session" }, - { OPTION_WAVWRITE, nullptr, core_options::option_type::STRING, "optional filename to write a WAV file of the current session" }, + { OPTION_MNGWRITE, nullptr, core_options::option_type::PATH, "optional filename to write a MNG movie of the current session" }, + { OPTION_AVIWRITE, nullptr, core_options::option_type::PATH, "optional filename to write an AVI movie of the current session" }, + { OPTION_WAVWRITE, nullptr, core_options::option_type::PATH, "optional filename to write a WAV file of the current session" }, { OPTION_SNAPNAME, "%g/%i", core_options::option_type::STRING, "override of the default snapshot/movie naming; %g == gamename, %i == index" }, { OPTION_SNAPSIZE, "auto", core_options::option_type::STRING, "specify snapshot/movie resolution (x) or 'auto' to use minimal size " }, { OPTION_SNAPVIEW, "auto", core_options::option_type::STRING, "snapshot/movie view - 'auto' for default, or 'native' for per-screen pixel-aspect views" }, @@ -181,7 +181,7 @@ const options_entry emu_options::s_option_entries[] = { OPTION_OSLOG, "0", core_options::option_type::BOOLEAN, "output error.log data to system diagnostic output (debugger or standard error)" }, { OPTION_DEBUG ";d", "0", core_options::option_type::BOOLEAN, "enable/disable debugger" }, { OPTION_UPDATEINPAUSE, "0", core_options::option_type::BOOLEAN, "keep calling video updates while in pause" }, - { OPTION_DEBUGSCRIPT, nullptr, core_options::option_type::STRING, "script for debugger" }, + { OPTION_DEBUGSCRIPT, nullptr, core_options::option_type::PATH, "script for debugger" }, { OPTION_DEBUGLOG, "0", core_options::option_type::BOOLEAN, "write debug console output to debug.log" }, // comm options @@ -212,7 +212,7 @@ const options_entry emu_options::s_option_entries[] = { nullptr, nullptr, core_options::option_type::HEADER, "SCRIPTING OPTIONS" }, { OPTION_AUTOBOOT_COMMAND ";ab", nullptr, core_options::option_type::STRING, "command to execute after machine boot" }, { OPTION_AUTOBOOT_DELAY, "0", core_options::option_type::INTEGER, "delay before executing autoboot command (seconds)" }, - { OPTION_AUTOBOOT_SCRIPT ";script", nullptr, core_options::option_type::STRING, "Lua script to execute after machine boot" }, + { OPTION_AUTOBOOT_SCRIPT ";script", nullptr, core_options::option_type::PATH, "Lua script to execute after machine boot" }, { OPTION_CONSOLE, "0", core_options::option_type::BOOLEAN, "enable emulator Lua console" }, { OPTION_PLUGINS, "1", core_options::option_type::BOOLEAN, "enable Lua plugin support" }, { OPTION_PLUGIN, nullptr, core_options::option_type::STRING, "list of plugins to enable" }, @@ -221,7 +221,7 @@ const options_entry emu_options::s_option_entries[] = { nullptr, nullptr, core_options::option_type::HEADER, "HTTP SERVER OPTIONS" }, { OPTION_HTTP, "0", core_options::option_type::BOOLEAN, "enable HTTP server" }, { OPTION_HTTP_PORT, "8080", core_options::option_type::INTEGER, "HTTP server port" }, - { OPTION_HTTP_ROOT, "web", core_options::option_type::STRING, "HTTP server document root" }, + { OPTION_HTTP_ROOT, "web", core_options::option_type::PATH, "HTTP server document root" }, { nullptr } }; @@ -255,7 +255,7 @@ namespace } protected: - virtual void internal_set_value(std::string &&newvalue) override + virtual void internal_set_value(std::string &&newvalue, bool perform_substitutions) override { m_host.set_system_name(std::move(newvalue)); } @@ -275,7 +275,7 @@ namespace } protected: - virtual void internal_set_value(std::string &&newvalue) override + virtual void internal_set_value(std::string &&newvalue, bool perform_substitutions) override { m_host.set_software(std::move(newvalue)); } @@ -313,7 +313,7 @@ namespace } protected: - virtual void internal_set_value(std::string &&newvalue) override + virtual void internal_set_value(std::string &&newvalue, bool perform_substitutions) override { m_host.specify(std::move(newvalue), false); } @@ -339,7 +339,7 @@ namespace } protected: - virtual void internal_set_value(std::string &&newvalue) override + virtual void internal_set_value(std::string &&newvalue, bool perform_substitutions) override { m_host.specify(std::move(newvalue), false); } diff --git a/src/frontend/mame/clifront.cpp b/src/frontend/mame/clifront.cpp index dfe0436e079..aa32a94a860 100644 --- a/src/frontend/mame/clifront.cpp +++ b/src/frontend/mame/clifront.cpp @@ -1732,7 +1732,7 @@ void cli_frontend::execute_commands(std::string_view exename) path_iterator iter(m_options.plugins_path()); std::string pluginpath; while (iter.next(pluginpath)) - plugin_opts.scan_directory(osd_subst_env(pluginpath), true); + 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")) diff --git a/src/frontend/mame/luaengine.cpp b/src/frontend/mame/luaengine.cpp index 386d6dc841d..d406a502583 100644 --- a/src/frontend/mame/luaengine.cpp +++ b/src/frontend/mame/luaengine.cpp @@ -1172,7 +1172,7 @@ void lua_engine::initialize() e.set_value(string_format("%d", val), OPTION_PRIORITY_CMDLINE); }, [this](core_options::entry &e, const char *val) { - if(e.type() != core_options::option_type::STRING) + if(e.type() != core_options::option_type::STRING && e.type() != core_options::option_type::PATH && e.type() != core_options::option_type::MULTIPATH) luaL_error(m_lua_state, "Cannot set option to wrong type"); else e.set_value(val, OPTION_PRIORITY_CMDLINE); diff --git a/src/frontend/mame/mame.cpp b/src/frontend/mame/mame.cpp index 5cfe2a5b243..d2d3d886acd 100644 --- a/src/frontend/mame/mame.cpp +++ b/src/frontend/mame/mame.cpp @@ -142,10 +142,7 @@ void mame_machine_manager::start_luaengine() std::string pluginpath; while (iter.next(pluginpath)) { - // user may specify environment variables; subsitute them - pluginpath = osd_subst_env(pluginpath); - - // and then scan the directory recursively + // scan the directory recursively m_plugins->scan_directory(pluginpath, true); } @@ -202,7 +199,7 @@ void mame_machine_manager::start_luaengine() std::error_condition const filerr = file.open("boot.lua"); if (!filerr) { - const std::string exppath = osd_subst_env(file.fullpath()); + const std::string exppath = file.fullpath(); auto &l(*lua()); auto load_result = l.load_script(exppath); if (!load_result.valid()) diff --git a/src/frontend/mame/ui/dirmenu.cpp b/src/frontend/mame/ui/dirmenu.cpp index ab55b742054..1e28c88cc37 100644 --- a/src/frontend/mame/ui/dirmenu.cpp +++ b/src/frontend/mame/ui/dirmenu.cpp @@ -29,50 +29,46 @@ namespace ui { namespace { -constexpr int ADDING = 1; -constexpr int CHANGE = 2; - struct folders_entry { const char *name; const char *option; - const int action; }; const folders_entry f_folders[] = { - { N_p("path-option", "ROMs"), OPTION_MEDIAPATH, ADDING }, - { N_p("path-option", "Software Media"), OPTION_SWPATH, CHANGE }, - { N_p("path-option", "Sound Samples"), OPTION_SAMPLEPATH, ADDING }, - { N_p("path-option", "Artwork"), OPTION_ARTPATH, ADDING }, - { N_p("path-option", "Crosshairs"), OPTION_CROSSHAIRPATH, ADDING }, - { N_p("path-option", "Cheat Files"), OPTION_CHEATPATH, ADDING }, - { N_p("path-option", "Plugins"), OPTION_PLUGINSPATH, ADDING }, - { N_p("path-option", "UI Translations"), OPTION_LANGUAGEPATH, CHANGE }, - { N_p("path-option", "Software Lists"), OPTION_HASHPATH, ADDING }, - { N_p("path-option", "INIs"), OPTION_INIPATH, ADDING }, - { N_p("path-option", "UI Settings"), OPTION_UI_PATH, CHANGE }, - { N_p("path-option", "Plugin Data"), OPTION_PLUGINDATAPATH, CHANGE }, - { N_p("path-option", "DATs"), OPTION_HISTORY_PATH, ADDING }, - { N_p("path-option", "Category INIs"), OPTION_CATEGORYINI_PATH, CHANGE }, - { N_p("path-option", "Snapshots"), OPTION_SNAPSHOT_DIRECTORY, ADDING }, - { N_p("path-option", "Icons"), OPTION_ICONS_PATH, ADDING }, - { N_p("path-option", "Control Panels"), OPTION_CPANELS_PATH, ADDING }, - { N_p("path-option", "Cabinets"), OPTION_CABINETS_PATH, ADDING }, - { N_p("path-option", "Marquees"), OPTION_MARQUEES_PATH, ADDING }, - { N_p("path-option", "PCBs"), OPTION_PCBS_PATH, ADDING }, - { N_p("path-option", "Flyers"), OPTION_FLYERS_PATH, ADDING }, - { N_p("path-option", "Title Screens"), OPTION_TITLES_PATH, ADDING }, - { N_p("path-option", "Game Endings"), OPTION_ENDS_PATH, ADDING }, - { N_p("path-option", "Bosses"), OPTION_BOSSES_PATH, ADDING }, - { N_p("path-option", "Artwork Previews"), OPTION_ARTPREV_PATH, ADDING }, - { N_p("path-option", "Select"), OPTION_SELECT_PATH, ADDING }, - { N_p("path-option", "Game Over Screens"), OPTION_GAMEOVER_PATH, ADDING }, - { N_p("path-option", "HowTo"), OPTION_HOWTO_PATH, ADDING }, - { N_p("path-option", "Logos"), OPTION_LOGOS_PATH, ADDING }, - { N_p("path-option", "Scores"), OPTION_SCORES_PATH, ADDING }, - { N_p("path-option", "Versus"), OPTION_VERSUS_PATH, ADDING }, - { N_p("path-option", "Covers"), OPTION_COVER_PATH, ADDING } + { N_p("path-option", "ROMs"), OPTION_MEDIAPATH }, + { N_p("path-option", "Software Media"), OPTION_SWPATH }, + { N_p("path-option", "Sound Samples"), OPTION_SAMPLEPATH }, + { N_p("path-option", "Artwork"), OPTION_ARTPATH }, + { N_p("path-option", "Crosshairs"), OPTION_CROSSHAIRPATH }, + { N_p("path-option", "Cheat Files"), OPTION_CHEATPATH }, + { N_p("path-option", "Plugins"), OPTION_PLUGINSPATH }, + { N_p("path-option", "UI Translations"), OPTION_LANGUAGEPATH }, + { N_p("path-option", "Software Lists"), OPTION_HASHPATH }, + { N_p("path-option", "INIs"), OPTION_INIPATH }, + { N_p("path-option", "UI Settings"), OPTION_UI_PATH }, + { N_p("path-option", "Plugin Data"), OPTION_PLUGINDATAPATH }, + { N_p("path-option", "DATs"), OPTION_HISTORY_PATH }, + { N_p("path-option", "Category INIs"), OPTION_CATEGORYINI_PATH }, + { N_p("path-option", "Snapshots"), OPTION_SNAPSHOT_DIRECTORY }, + { N_p("path-option", "Icons"), OPTION_ICONS_PATH }, + { N_p("path-option", "Control Panels"), OPTION_CPANELS_PATH }, + { N_p("path-option", "Cabinets"), OPTION_CABINETS_PATH }, + { N_p("path-option", "Marquees"), OPTION_MARQUEES_PATH }, + { N_p("path-option", "PCBs"), OPTION_PCBS_PATH }, + { N_p("path-option", "Flyers"), OPTION_FLYERS_PATH }, + { N_p("path-option", "Title Screens"), OPTION_TITLES_PATH }, + { N_p("path-option", "Game Endings"), OPTION_ENDS_PATH }, + { N_p("path-option", "Bosses"), OPTION_BOSSES_PATH }, + { N_p("path-option", "Artwork Previews"), OPTION_ARTPREV_PATH }, + { N_p("path-option", "Select"), OPTION_SELECT_PATH }, + { N_p("path-option", "Game Over Screens"), OPTION_GAMEOVER_PATH }, + { N_p("path-option", "HowTo"), OPTION_HOWTO_PATH }, + { N_p("path-option", "Logos"), OPTION_LOGOS_PATH }, + { N_p("path-option", "Scores"), OPTION_SCORES_PATH }, + { N_p("path-option", "Versus"), OPTION_VERSUS_PATH }, + { N_p("path-option", "Covers"), OPTION_COVER_PATH } }; @@ -166,7 +162,7 @@ void menu_remove_folder::populate(float &customtop, float &custombottom) class menu_add_change_folder : public menu { public: - menu_add_change_folder(mame_ui_manager &mui, render_container &container, int ref); + menu_add_change_folder(mame_ui_manager &mui, render_container &container, int ref, bool multipath); protected: virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override; @@ -180,9 +176,9 @@ private: void update_search(); int const m_ref; + bool const m_multipath; std::string m_current_path; std::string m_search; - bool const m_change; std::vector m_folders; }; @@ -190,10 +186,10 @@ private: // ctor //------------------------------------------------- -menu_add_change_folder::menu_add_change_folder(mame_ui_manager &mui, render_container &container, int ref) +menu_add_change_folder::menu_add_change_folder(mame_ui_manager &mui, render_container &container, int ref, bool multipath) : menu(mui, container) , m_ref(ref) - , m_change(f_folders[ref].action == CHANGE) + , m_multipath(multipath) { // configure the starting path osd_get_full_path(m_current_path, "."); @@ -255,7 +251,7 @@ void menu_add_change_folder::handle(event const *ev) { // Tab key, save current path std::string error_string; - if (m_change) + if (!m_multipath) { if (ui().options().exists(f_folders[m_ref].option)) ui().options().set_value(f_folders[m_ref].option, m_current_path, OPTION_PRIORITY_CMDLINE); @@ -349,7 +345,7 @@ void menu_add_change_folder::custom_render(void *selectedref, float top, float b { std::string const toptext[] = { util::string_format( - m_change ? _("Change %1$s Folder - Search: %2$s_") : _("Add %1$s Folder - Search: %2$s_"), + m_multipath ? _("Add %1$s Folder - Search: %2$s_") : _("Change %1$s Folder - Search: %2$s_"), _("path-option", f_folders[m_ref].name), m_search), m_current_path }; @@ -428,8 +424,9 @@ class menu_display_actual : public menu public: menu_display_actual(mame_ui_manager &mui, render_container &container, int selectedref) : menu(mui, container) - , m_heading{ util::string_format((f_folders[selectedref].action == ADDING) ? _("%1$s Folders") : _("%1$s Folder"), _("path-option", f_folders[selectedref].name)) } , m_ref(selectedref) + , m_multipath(is_multipath(f_folders[selectedref].option)) + , m_heading{ util::string_format(m_multipath ? _("%1$s Folders") : _("%1$s Folder"), _("path-option", f_folders[selectedref].name)) } { } @@ -446,12 +443,29 @@ private: virtual void populate(float &customtop, float &custombottom) override; virtual void handle(event const *ev) override; + bool is_multipath(std::string_view folder) const; + + int const m_ref; + bool const m_multipath; std::string const m_heading[1]; std::string m_searchpath; std::vector m_folders; - int const m_ref; }; +//------------------------------------------------- +// is_multipath +//------------------------------------------------- + +bool menu_display_actual::is_multipath(std::string_view folder) const +{ + auto option = ui().options().get_entry(folder); + if (!option) + option = machine().options().get_entry(folder); + assert(option); + + return option->type() == core_options::option_type::MULTIPATH; +} + //------------------------------------------------- // handle //------------------------------------------------- @@ -468,7 +482,7 @@ void menu_display_actual::handle(event const *ev) break; case ADD_CHANGE: - menu::stack_push(ui(), container(), m_ref); + menu::stack_push(ui(), container(), m_ref, m_multipath); break; } } @@ -480,21 +494,31 @@ void menu_display_actual::handle(event const *ev) void menu_display_actual::populate(float &customtop, float &custombottom) { - if (ui().options().exists(f_folders[m_ref].option)) - m_searchpath.assign(ui().options().value(f_folders[m_ref].option)); - else - m_searchpath.assign(machine().options().value(f_folders[m_ref].option)); + auto const &folder = f_folders[m_ref]; + auto option = ui().options().get_entry(folder.option); + if (!option) + option = machine().options().get_entry(folder.option); + assert(option); - path_iterator path(m_searchpath); - std::string curpath; - m_folders.clear(); - while (path.next(curpath)) - m_folders.push_back(curpath); - - item_append((f_folders[m_ref].action == CHANGE) ? _("Change Folder") : _("Add Folder"), 0, (void *)ADD_CHANGE); + m_searchpath.assign(option->value()); - if (m_folders.size() > 1) - item_append(_("Remove Folder"), 0, (void *)REMOVE); + m_folders.clear(); + if (m_multipath) + { + path_iterator path(m_searchpath); + std::string curpath; + while (path.next(curpath)) + m_folders.push_back(curpath); + + item_append(_("Add Folder"), 0, (void *)ADD_CHANGE); + if (m_folders.size() > 1) + item_append(_("Remove Folder"), 0, (void *)REMOVE); + } + else + { + m_folders.push_back(m_searchpath); + item_append(_("Change Folder"), 0, (void *)ADD_CHANGE); + } item_append(menu_item_type::SEPARATOR); @@ -559,7 +583,7 @@ void menu_directory::handle(event const *ev) void menu_directory::populate(float &customtop, float &custombottom) { for (auto & elem : f_folders) - item_append(_("path-option", elem.name), 0, (void *)(uintptr_t)elem.action); + item_append(_("path-option", elem.name), 0, this); // need a non-null reference pointer - value is immaterial item_append(menu_item_type::SEPARATOR); } diff --git a/src/frontend/mame/ui/moptions.cpp b/src/frontend/mame/ui/moptions.cpp index ff08ab3f9e6..cf019588834 100644 --- a/src/frontend/mame/ui/moptions.cpp +++ b/src/frontend/mame/ui/moptions.cpp @@ -20,31 +20,31 @@ const options_entry ui_options::s_option_entries[] = { // search path options - { nullptr, nullptr, core_options::option_type::HEADER, "UI SEARCH PATH OPTIONS" }, - { OPTION_HISTORY_PATH, "history;dats;.", core_options::option_type::STRING, "path to system/software info files" }, - { OPTION_CATEGORYINI_PATH, "folders", core_options::option_type::STRING, "path to category ini files" }, - { OPTION_CABINETS_PATH, "cabinets;cabdevs", core_options::option_type::STRING, "path to cabinets / devices image" }, - { OPTION_CPANELS_PATH, "cpanel", core_options::option_type::STRING, "path to control panel image" }, - { OPTION_PCBS_PATH, "pcb", core_options::option_type::STRING, "path to pcbs image" }, - { OPTION_FLYERS_PATH, "flyers", core_options::option_type::STRING, "path to flyers image" }, - { OPTION_TITLES_PATH, "titles", core_options::option_type::STRING, "path to titles image" }, - { OPTION_ENDS_PATH, "ends", core_options::option_type::STRING, "path to ends image" }, - { OPTION_MARQUEES_PATH, "marquees", core_options::option_type::STRING, "path to marquees image" }, - { OPTION_ARTPREV_PATH, "artwork preview;artpreview", core_options::option_type::STRING, "path to artwork preview image" }, - { OPTION_BOSSES_PATH, "bosses", core_options::option_type::STRING, "path to bosses image" }, - { OPTION_LOGOS_PATH, "logo", core_options::option_type::STRING, "path to logos image" }, - { OPTION_SCORES_PATH, "scores", core_options::option_type::STRING, "path to scores image" }, - { OPTION_VERSUS_PATH, "versus", core_options::option_type::STRING, "path to versus image" }, - { OPTION_GAMEOVER_PATH, "gameover", core_options::option_type::STRING, "path to gameover image" }, - { OPTION_HOWTO_PATH, "howto", core_options::option_type::STRING, "path to howto image" }, - { OPTION_SELECT_PATH, "select", core_options::option_type::STRING, "path to select image" }, - { OPTION_ICONS_PATH, "icons", core_options::option_type::STRING, "path to ICOns image" }, - { OPTION_COVER_PATH, "covers", core_options::option_type::STRING, "path to software cover image" }, - { OPTION_UI_PATH, "ui", core_options::option_type::STRING, "path to UI files" }, + { nullptr, nullptr, core_options::option_type::HEADER, "UI SEARCH PATH OPTIONS" }, + { OPTION_HISTORY_PATH, "history;dats;.", core_options::option_type::MULTIPATH, "path to system/software info files" }, + { OPTION_CATEGORYINI_PATH, "folders", core_options::option_type::MULTIPATH, "path to category ini files" }, + { OPTION_CABINETS_PATH, "cabinets;cabdevs", core_options::option_type::MULTIPATH, "path to cabinets / devices image" }, + { OPTION_CPANELS_PATH, "cpanel", core_options::option_type::MULTIPATH, "path to control panel image" }, + { OPTION_PCBS_PATH, "pcb", core_options::option_type::MULTIPATH, "path to pcbs image" }, + { OPTION_FLYERS_PATH, "flyers", core_options::option_type::MULTIPATH, "path to flyers image" }, + { OPTION_TITLES_PATH, "titles", core_options::option_type::MULTIPATH, "path to titles image" }, + { OPTION_ENDS_PATH, "ends", core_options::option_type::MULTIPATH, "path to ends image" }, + { OPTION_MARQUEES_PATH, "marquees", core_options::option_type::MULTIPATH, "path to marquees image" }, + { OPTION_ARTPREV_PATH, "artwork preview;artpreview", core_options::option_type::MULTIPATH, "path to artwork preview image" }, + { OPTION_BOSSES_PATH, "bosses", core_options::option_type::MULTIPATH, "path to bosses image" }, + { OPTION_LOGOS_PATH, "logo", core_options::option_type::MULTIPATH, "path to logos image" }, + { OPTION_SCORES_PATH, "scores", core_options::option_type::MULTIPATH, "path to scores image" }, + { OPTION_VERSUS_PATH, "versus", core_options::option_type::MULTIPATH, "path to versus image" }, + { OPTION_GAMEOVER_PATH, "gameover", core_options::option_type::MULTIPATH, "path to gameover image" }, + { OPTION_HOWTO_PATH, "howto", core_options::option_type::MULTIPATH, "path to howto image" }, + { OPTION_SELECT_PATH, "select", core_options::option_type::MULTIPATH, "path to select image" }, + { OPTION_ICONS_PATH, "icons", core_options::option_type::MULTIPATH, "path to ICOns image" }, + { OPTION_COVER_PATH, "covers", core_options::option_type::MULTIPATH, "path to software cover image" }, + { OPTION_UI_PATH, "ui", core_options::option_type::MULTIPATH, "path to UI files" }, // misc options { nullptr, nullptr, core_options::option_type::HEADER, "UI MISC OPTIONS" }, - { OPTION_SYSTEM_NAMES, "", core_options::option_type::STRING, "translated system names file" }, + { OPTION_SYSTEM_NAMES, "", core_options::option_type::MULTIPATH, "translated system names file" }, { OPTION_SKIP_WARNINGS, "0", core_options::option_type::BOOLEAN, "display fewer repeated warnings about imperfect emulation" }, { OPTION_REMEMBER_LAST, "1", core_options::option_type::BOOLEAN, "initially select last used system in main menu" }, { OPTION_ENLARGE_SNAPS, "1", core_options::option_type::BOOLEAN, "enlarge artwork (snapshot, title, etc.) in right panel (keeping aspect ratio)" }, diff --git a/src/frontend/mame/ui/submenu.cpp b/src/frontend/mame/ui/submenu.cpp index 4e470251b7a..450834a5458 100644 --- a/src/frontend/mame/ui/submenu.cpp +++ b/src/frontend/mame/ui/submenu.cpp @@ -173,7 +173,7 @@ submenu::submenu(mame_ui_manager &mui, render_container &container, std::vector< case option_type::EMU: sm_option.entry = opts->get_entry(sm_option.name); sm_option.options = opts; - if (sm_option.entry->type() == core_options::option_type::STRING) + if ((sm_option.entry->type() == core_options::option_type::STRING) || (sm_option.entry->type() == core_options::option_type::PATH) || (sm_option.entry->type() == core_options::option_type::MULTIPATH)) { sm_option.value.clear(); std::string namestr(sm_option.entry->description()); @@ -197,7 +197,7 @@ submenu::submenu(mame_ui_manager &mui, render_container &container, std::vector< case option_type::OSD: sm_option.entry = opts->get_entry(sm_option.name); sm_option.options = opts; - if (sm_option.entry->type() == core_options::option_type::STRING) + if ((sm_option.entry->type() == core_options::option_type::STRING) || (sm_option.entry->type() == core_options::option_type::PATH) || (sm_option.entry->type() == core_options::option_type::MULTIPATH)) { sm_option.value.clear(); std::string descr(machine().options().get_entry(sm_option.name)->description()), delim(", "); @@ -307,6 +307,8 @@ void submenu::handle(event const *ev) } break; case core_options::option_type::STRING: + case core_options::option_type::PATH: + case core_options::option_type::MULTIPATH: if (ev->iptkey == IPT_UI_LEFT || ev->iptkey == IPT_UI_RIGHT) { changed = true; diff --git a/src/lib/util/options.cpp b/src/lib/util/options.cpp index cac92c37020..7bf64c344e4 100644 --- a/src/lib/util/options.cpp +++ b/src/lib/util/options.cpp @@ -12,6 +12,7 @@ #include "corefile.h" #include "corestr.h" +#include "osdcore.h" #include #include @@ -20,6 +21,7 @@ #include #include #include +#include const int core_options::MAX_UNADORNED_OPTIONS; @@ -113,7 +115,7 @@ options_error_exception::options_error_exception(std::string &&message) // entry - constructor //------------------------------------------------- -core_options::entry::entry(std::vector &&names, core_options::option_type type, const char *description) +core_options::entry::entry(std::vector &&names, option_type type, const char *description) : m_names(std::move(names)) , m_priority(OPTION_PRIORITY_DEFAULT) , m_type(type) @@ -122,7 +124,7 @@ core_options::entry::entry(std::vector &&names, core_options::optio assert(m_names.empty() == (m_type == option_type::HEADER)); } -core_options::entry::entry(std::string &&name, core_options::option_type type, const char *description) +core_options::entry::entry(std::string &&name, option_type type, const char *description) : entry(std::vector({ std::move(name) }), type, description) { } @@ -149,11 +151,45 @@ const char *core_options::entry::value() const noexcept } +//------------------------------------------------- +// entry::value_unsubstituted +//------------------------------------------------- + +const char *core_options::entry::value_unsubstituted() const noexcept +{ + return value(); +} + + +//------------------------------------------------- +// entry::copy_from +//------------------------------------------------- + +void core_options::entry::copy_from(const entry &that, bool always_override) +{ + // it is invalid to set the value on a header + assert(type() != option_type::HEADER); + + // only set the value if we have priority + if (always_override || that.priority() >= priority()) + { + if (internal_copy_value(that)) + { + m_priority = that.priority(); + + // invoke the value changed handler, if appropriate + if (m_value_changed_handler) + m_value_changed_handler(value()); + } + } +} + + //------------------------------------------------- // entry::set_value //------------------------------------------------- -void core_options::entry::set_value(std::string &&newvalue, int priority_value, bool always_override) +void core_options::entry::set_value(std::string &&newvalue, int priority_value, bool always_override, bool perform_substitutions) { // it is invalid to set the value on a header assert(type() != option_type::HEADER); @@ -163,7 +199,7 @@ void core_options::entry::set_value(std::string &&newvalue, int priority_value, // only set the value if we have priority if (always_override || priority_value >= priority()) { - internal_set_value(std::move(newvalue)); + internal_set_value(std::move(newvalue), perform_substitutions); m_priority = priority_value; // invoke the value changed handler, if appropriate @@ -184,6 +220,29 @@ void core_options::entry::set_default_value(std::string &&newvalue) } +//------------------------------------------------- +// entry::internal_copy_value +//------------------------------------------------- + +bool core_options::entry::internal_copy_value(const entry &that) +{ + char const *const newvalue = that.value(); + if (newvalue) + { + std::string stringval = newvalue; + validate(stringval); + + internal_set_value(std::move(stringval), false); + + return true; + } + else + { + return false; + } +} + + //------------------------------------------------- // entry::validate //------------------------------------------------- @@ -273,12 +332,14 @@ void core_options::entry::validate(const std::string &data) } break; - case core_options::option_type::STRING: + case option_type::STRING: + case option_type::PATH: + case option_type::MULTIPATH: // strings can be anything break; - case core_options::option_type::INVALID: - case core_options::option_type::HEADER: + case option_type::INVALID: + case option_type::HEADER: default: // anything else is invalid throw options_error_exception("Attempted to set invalid option %s\n", name()); @@ -337,10 +398,12 @@ const std::string &core_options::entry::default_value() const noexcept core_options::simple_entry::simple_entry(std::vector &&names, const char *description, core_options::option_type type, std::string &&defdata, std::string &&minimum, std::string &&maximum) : entry(std::move(names), type, description) - , m_defdata(std::move(defdata)) + , m_defdata_unsubst(std::move(defdata)) , m_minimum(std::move(minimum)) , m_maximum(std::move(maximum)) { + m_defdata = type_specific_substitutions(m_defdata_unsubst); + m_data_unsubst = m_defdata_unsubst; m_data = m_defdata; } @@ -366,6 +429,8 @@ const char *core_options::simple_entry::value() const noexcept case core_options::option_type::INTEGER: case core_options::option_type::FLOAT: case core_options::option_type::STRING: + case core_options::option_type::PATH: + case core_options::option_type::MULTIPATH: return m_data.c_str(); default: @@ -376,6 +441,30 @@ const char *core_options::simple_entry::value() const noexcept } +//------------------------------------------------- +// simple_entry::value_unsubstituted +//------------------------------------------------- + +const char *core_options::simple_entry::value_unsubstituted() const noexcept +{ + switch (type()) + { + case core_options::option_type::BOOLEAN: + case core_options::option_type::INTEGER: + case core_options::option_type::FLOAT: + case core_options::option_type::STRING: + case core_options::option_type::PATH: + case core_options::option_type::MULTIPATH: + return m_data_unsubst.c_str(); + + default: + // this is an option type for which returning a value is + // a meaningless operation (e.g. - core_options::option_type::COMMAND) + return nullptr; + } +} + + //------------------------------------------------- // simple_entry::default_value //------------------------------------------------- @@ -388,12 +477,38 @@ const std::string &core_options::simple_entry::default_value() const noexcept //------------------------------------------------- -// internal_set_value +// simple_entry::internal_set_value +//------------------------------------------------- + +void core_options::simple_entry::internal_set_value(std::string &&newvalue, bool perform_substitutions) +{ + m_data = perform_substitutions + ? type_specific_substitutions(newvalue) + : newvalue; + m_data_unsubst = std::move(newvalue); +} + + +//------------------------------------------------- +// simple_entry::internal_copy_value //------------------------------------------------- -void core_options::simple_entry::internal_set_value(std::string &&newvalue) +bool core_options::simple_entry::internal_copy_value(const entry &that) { - m_data = std::move(newvalue); + simple_entry const *const simple = dynamic_cast(&that); + if (!simple) + { + return entry::internal_copy_value(that); + } + else + { + validate(simple->m_data); + + m_data = simple->m_data; + m_data_unsubst = simple->m_data_unsubst; + + return true; + } } @@ -403,7 +518,47 @@ void core_options::simple_entry::internal_set_value(std::string &&newvalue) void core_options::simple_entry::set_default_value(std::string &&newvalue) { - m_data = m_defdata = std::move(newvalue); + m_data = m_defdata = type_specific_substitutions(newvalue); + m_data_unsubst = std::move(newvalue); +} + + +//------------------------------------------------- +// type_specific_substitutions +//------------------------------------------------- + +std::string core_options::simple_entry::type_specific_substitutions(std::string_view s) const noexcept +{ + switch (type()) + { + case option_type::PATH: + return osd_subst_env(s); + + case option_type::MULTIPATH: + { + std::ostringstream result; + while (!s.empty()) + { + std::string_view::size_type split = s.find(';'); + if (std::string_view::npos == split) + split = s.length(); + result << osd_subst_env(s.substr(0, split)); + if (s.length() > split) + { + result << s[split]; + s.remove_prefix(split + 1); + } + else + { + s.remove_prefix(split); + } + } + return std::move(result).str(); + } + + default: + return std::string(s); + } } @@ -717,7 +872,7 @@ void core_options::parse_command_line(const std::vector &args, int } // set the new data - do_set_value(*curentry, newdata, priority, error_stream, condition); + do_set_value(*curentry, newdata, priority, error_stream, condition, false); } // did we have any errors that may need to be aggregated? @@ -791,7 +946,7 @@ void core_options::parse_ini_file(util::core_file &inifile, int priority, bool i } // set the new data - do_set_value(*curentry, trim_spaces_and_quotes(optiondata), priority, error_stream, condition); + do_set_value(*curentry, trim_spaces_and_quotes(optiondata), priority, error_stream, condition, true); } // did we have any errors that may need to be aggregated? @@ -837,11 +992,7 @@ void core_options::copy_from(const core_options &that) // identify the source entry const entry::shared_const_ptr source_entry = that.get_entry(dest_entry->name()); if (source_entry) - { - const char *value = source_entry->value(); - if (value) - dest_entry->set_value(value, source_entry->priority(), true); - } + dest_entry->copy_from(*source_entry, false); } } } @@ -875,7 +1026,7 @@ std::string core_options::output_ini(const core_options *diff) const else { const std::string &name(curentry->name()); - const char *value(curentry->value()); + const char *value(curentry->value_unsubstituted()); // check if it's unadorned bool is_unadorned = false; @@ -889,7 +1040,7 @@ std::string core_options::output_ini(const core_options *diff) const if (value) { // look up counterpart in diff, if diff is specified - if (!diff || strcmp(value, diff->value(name.c_str()))) + if (!diff || strcmp(value, diff->get_entry(name.c_str())->value_unsubstituted())) { // output header, if we have one if (last_header) @@ -1022,7 +1173,7 @@ void core_options::set_value(std::string_view name, std::string &&value, int pri { auto entry = get_entry(name); assert(entry != nullptr); - entry->set_value(std::move(value), priority); + entry->set_value(std::move(value), priority, false, false); } void core_options::set_value(std::string_view name, int value, int priority) @@ -1063,13 +1214,13 @@ void core_options::remove_entry(core_options::entry &delentry) // do_set_value //------------------------------------------------- -void core_options::do_set_value(entry &curentry, std::string_view data, int priority, std::ostream &error_stream, condition_type &condition) +void core_options::do_set_value(entry &curentry, std::string_view data, int priority, std::ostream &error_stream, condition_type &condition, bool perform_substitutions) { // this is called when parsing a command line or an INI - we want to catch the option_exception and write // any exception messages to the error stream try { - curentry.set_value(std::string(data), priority); + curentry.set_value(std::string(data), priority, false, perform_substitutions); } catch (options_warning_exception &ex) { @@ -1156,7 +1307,7 @@ void core_options::simple_entry::revert(int priority_hi, int priority_lo) // if our priority is within the range, revert to the default if (priority() <= priority_hi && priority() >= priority_lo) { - set_value(std::string(default_value()), priority(), true); + set_value(std::string(m_defdata_unsubst), priority(), true, true); set_priority(OPTION_PRIORITY_DEFAULT); } } diff --git a/src/lib/util/options.h b/src/lib/util/options.h index 57ca7160203..99db519a5e5 100644 --- a/src/lib/util/options.h +++ b/src/lib/util/options.h @@ -104,7 +104,9 @@ public: BOOLEAN, // boolean option INTEGER, // integer option FLOAT, // floating-point option - STRING // string option + STRING, // string option + PATH, // single path option + MULTIPATH // semicolon-delimited paths option }; // information about a single entry in the options @@ -128,6 +130,7 @@ public: const std::vector &names() const noexcept { return m_names; } const std::string &name() const noexcept { return m_names[0]; } virtual const char *value() const noexcept; + virtual const char *value_unsubstituted() const noexcept; int priority() const noexcept { return m_priority; } void set_priority(int priority) noexcept { m_priority = priority; } option_type type() const noexcept { return m_type; } @@ -138,18 +141,20 @@ public: bool has_range() const noexcept; // mutators - void set_value(std::string &&newvalue, int priority, bool always_override = false); + void copy_from(const entry &that, bool always_override); + void set_value(std::string &&newvalue, int priority, bool always_override = false, bool perform_substitutions = false); virtual void set_default_value(std::string &&newvalue); void set_description(const char *description) { m_description = description; } void set_value_changed_handler(std::function &&handler) { m_value_changed_handler = std::move(handler); } virtual void revert(int priority_hi, int priority_lo) { } protected: - virtual void internal_set_value(std::string &&newvalue) = 0; + virtual void internal_set_value(std::string &&newvalue, bool perform_substitutions) = 0; + virtual bool internal_copy_value(const entry &that); - private: void validate(const std::string &value); + private: const std::vector m_names; int m_priority; core_options::option_type m_type; @@ -231,6 +236,7 @@ private: // getters virtual const char *value() const noexcept override; + virtual const char *value_unsubstituted() const noexcept override; virtual const char *minimum() const noexcept override; virtual const char *maximum() const noexcept override; virtual const std::string &default_value() const noexcept override; @@ -239,14 +245,19 @@ private: virtual void set_default_value(std::string &&newvalue) override; protected: - virtual void internal_set_value(std::string &&newvalue) override; + virtual void internal_set_value(std::string &&newvalue, bool perform_substitutions) override; + virtual bool internal_copy_value(const entry &that) override; private: // internal state std::string m_data; // data for this item + std::string m_data_unsubst; // data for this item, prior to any substitution std::string m_defdata; // default data for this item + std::string m_defdata_unsubst; // default data for this item, prior to any substitution std::string m_minimum; // minimum value std::string m_maximum; // maximum value + + std::string type_specific_substitutions(std::string_view s) const noexcept; }; // used internally in core_options @@ -259,7 +270,7 @@ private: // internal helpers void add_to_entry_map(const std::string &name, entry::shared_ptr &entry); - void do_set_value(entry &curentry, std::string_view data, int priority, std::ostream &error_stream, condition_type &condition); + void do_set_value(entry &curentry, std::string_view data, int priority, std::ostream &error_stream, condition_type &condition, bool perform_substitutions); void throw_options_exception_if_appropriate(condition_type condition, std::ostringstream &error_stream); // internal state diff --git a/src/osd/modules/file/posixdir.cpp b/src/osd/modules/file/posixdir.cpp index efe68c2eda9..282a58acfc9 100644 --- a/src/osd/modules/file/posixdir.cpp +++ b/src/osd/modules/file/posixdir.cpp @@ -193,7 +193,7 @@ bool posix_directory::open_impl(std::string const &dirname) { assert(!m_fd); - m_path = osd_subst_env(dirname); + m_path = dirname; m_fd.reset(::opendir(m_path.c_str())); return bool(m_fd); } diff --git a/src/osd/modules/file/posixfile.cpp b/src/osd/modules/file/posixfile.cpp index 3b0e7b4b537..c5ce8bf7b4e 100644 --- a/src/osd/modules/file/posixfile.cpp +++ b/src/osd/modules/file/posixfile.cpp @@ -257,8 +257,6 @@ std::error_condition osd_file::open(std::string const &path, std::uint32_t openf for (auto it = dst.begin(); it != dst.end(); ++it) *it = (INVPATHSEPCH == *it) ? PATHSEPCH : *it; #endif - try { dst = osd_subst_env(dst); } - catch (...) { return std::errc::not_enough_memory; } // attempt to open the file int fd = -1; diff --git a/src/osd/modules/file/winfile.cpp b/src/osd/modules/file/winfile.cpp index 173021121f1..84c754cdd6f 100644 --- a/src/osd/modules/file/winfile.cpp +++ b/src/osd/modules/file/winfile.cpp @@ -161,12 +161,8 @@ DWORD create_path_recursive(TCHAR *path) // osd_open //============================================================ -std::error_condition osd_file::open(std::string const &orig_path, uint32_t openflags, ptr &file, std::uint64_t &filesize) noexcept +std::error_condition osd_file::open(std::string const &path, uint32_t openflags, ptr &file, std::uint64_t &filesize) noexcept { - std::string path; - try { path = osd_subst_env(orig_path); } - catch (...) { return std::errc::not_enough_memory; } - if (win_check_socket_path(path)) return win_open_socket(path, openflags, file, filesize); else if (win_check_ptty_path(path)) diff --git a/src/osd/modules/file/winrtfile.cpp b/src/osd/modules/file/winrtfile.cpp index 3de1c19605d..5053f6e0199 100644 --- a/src/osd/modules/file/winrtfile.cpp +++ b/src/osd/modules/file/winrtfile.cpp @@ -160,12 +160,8 @@ DWORD create_path_recursive(TCHAR *path) // osd_open //============================================================ -osd_file::error osd_file::open(std::string const &orig_path, uint32_t openflags, ptr &file, std::uint64_t &filesize) +osd_file::error osd_file::open(std::string const &path, uint32_t openflags, ptr &file, std::uint64_t &filesize) { - std::string path; - try { path = osd_subst_env(orig_path); } - catch (...) { return std::errc::not_enough_memory; } - if (win_check_socket_path(path)) return win_open_socket(path, openflags, file, filesize); else if (win_check_ptty_path(path)) diff --git a/src/osd/modules/lib/osdobj_common.cpp b/src/osd/modules/lib/osdobj_common.cpp index 09254f32042..5b3cebb84b9 100644 --- a/src/osd/modules/lib/osdobj_common.cpp +++ b/src/osd/modules/lib/osdobj_common.cpp @@ -164,13 +164,13 @@ const options_entry osd_options::s_option_entries[] = #endif { nullptr, nullptr, core_options::option_type::HEADER, "BGFX POST-PROCESSING OPTIONS" }, - { OSDOPTION_BGFX_PATH, "bgfx", core_options::option_type::STRING, "path to BGFX-related files" }, + { OSDOPTION_BGFX_PATH, "bgfx", core_options::option_type::PATH, "path to BGFX-related files" }, { OSDOPTION_BGFX_BACKEND, "auto", core_options::option_type::STRING, "BGFX backend to use (d3d9, d3d11, d3d12, metal, opengl, gles, vulkan)" }, { OSDOPTION_BGFX_DEBUG, "0", core_options::option_type::BOOLEAN, "enable BGFX debugging statistics" }, { OSDOPTION_BGFX_SCREEN_CHAINS, "default", core_options::option_type::STRING, "comma-delimited list of screen chain JSON names, colon-delimited per-window" }, { OSDOPTION_BGFX_SHADOW_MASK, "slot-mask.png", core_options::option_type::STRING, "shadow mask texture name" }, { OSDOPTION_BGFX_LUT, "lut-default.png", core_options::option_type::STRING, "LUT texture name" }, - { OSDOPTION_BGFX_AVI_NAME, OSDOPTVAL_AUTO, core_options::option_type::STRING, "filename for BGFX output logging" }, + { OSDOPTION_BGFX_AVI_NAME, OSDOPTVAL_AUTO, core_options::option_type::PATH, "filename for BGFX output logging" }, // End of list { nullptr } diff --git a/src/osd/modules/render/bgfx/chainmanager.cpp b/src/osd/modules/render/bgfx/chainmanager.cpp index c3b80e88d14..2ae3d9a3daf 100644 --- a/src/osd/modules/render/bgfx/chainmanager.cpp +++ b/src/osd/modules/render/bgfx/chainmanager.cpp @@ -92,7 +92,7 @@ void chain_manager::refresh_available_chains() m_available_chains.clear(); m_available_chains.push_back(chain_desc("none", "")); - const std::string chains_path = osd_subst_env(util::string_format("%s" PATH_SEPARATOR "chains", m_options.bgfx_path())); + const std::string chains_path = util::string_format("%s" PATH_SEPARATOR "chains", m_options.bgfx_path()); find_available_chains(chains_path, ""); destroy_unloaded_chains(); @@ -169,7 +169,7 @@ bgfx_chain* chain_manager::load_chain(std::string name, uint32_t screen_index) { name = name + ".json"; } - std::string path = osd_subst_env(util::string_format("%s" PATH_SEPARATOR "chains" PATH_SEPARATOR, m_options.bgfx_path())); + std::string path = util::string_format("%s" PATH_SEPARATOR "chains" PATH_SEPARATOR, m_options.bgfx_path()); path += name; bx::FileReader reader; diff --git a/src/osd/modules/render/bgfx/effectmanager.cpp b/src/osd/modules/render/bgfx/effectmanager.cpp index f57f56f708d..33e056b45fe 100644 --- a/src/osd/modules/render/bgfx/effectmanager.cpp +++ b/src/osd/modules/render/bgfx/effectmanager.cpp @@ -33,7 +33,7 @@ static bool prepare_effect_document(std::string &name, osd_options &options, rap full_name = full_name + ".json"; } - std::string path = osd_subst_env(util::string_format("%s" PATH_SEPARATOR "effects" PATH_SEPARATOR, options.bgfx_path())); + std::string path = util::string_format("%s" PATH_SEPARATOR "effects" PATH_SEPARATOR, options.bgfx_path()); path += full_name; bx::FileReader reader; diff --git a/src/osd/modules/render/bgfx/shadermanager.cpp b/src/osd/modules/render/bgfx/shadermanager.cpp index dd894426794..11e298a4e7a 100644 --- a/src/osd/modules/render/bgfx/shadermanager.cpp +++ b/src/osd/modules/render/bgfx/shadermanager.cpp @@ -119,7 +119,7 @@ std::string shader_manager::make_path_string(osd_options &options, std::string n fatalerror("Unknown BGFX renderer type %d", bgfx::getRendererType()); } shader_path += PATH_SEPARATOR; - return osd_subst_env(shader_path); + return shader_path; } const bgfx::Memory* shader_manager::load_mem(std::string name) diff --git a/src/osd/sdl/sdlmain.cpp b/src/osd/sdl/sdlmain.cpp index f238c7bd2cd..241e7f46148 100644 --- a/src/osd/sdl/sdlmain.cpp +++ b/src/osd/sdl/sdlmain.cpp @@ -77,7 +77,7 @@ int sdl_entered_debugger; const options_entry sdl_options::s_option_entries[] = { - { SDLOPTION_INIPATH, INI_PATH, core_options::option_type::STRING, "path to ini files" }, + { SDLOPTION_INIPATH, INI_PATH, core_options::option_type::MULTIPATH, "path to ini files" }, // performance options { nullptr, nullptr, core_options::option_type::HEADER, "SDL PERFORMANCE OPTIONS" }, @@ -99,7 +99,7 @@ const options_entry sdl_options::s_option_entries[] = // keyboard mapping { nullptr, nullptr, core_options::option_type::HEADER, "SDL KEYBOARD MAPPING" }, { SDLOPTION_KEYMAP, "0", core_options::option_type::BOOLEAN, "enable keymap" }, - { SDLOPTION_KEYMAP_FILE, "keymap.dat", core_options::option_type::STRING, "keymap filename" }, + { SDLOPTION_KEYMAP_FILE, "keymap.dat", core_options::option_type::PATH, "keymap filename" }, // joystick mapping { nullptr, nullptr, core_options::option_type::HEADER, "SDL JOYSTICK MAPPING" }, diff --git a/src/osd/windows/winmain.cpp b/src/osd/windows/winmain.cpp index ab9ba5e9167..293074b3ec8 100644 --- a/src/osd/windows/winmain.cpp +++ b/src/osd/windows/winmain.cpp @@ -176,10 +176,10 @@ const options_entry windows_options::s_option_entries[] = // post-processing options { nullptr, nullptr, core_options::option_type::HEADER, "DIRECT3D POST-PROCESSING OPTIONS" }, - { WINOPTION_HLSLPATH, "hlsl", core_options::option_type::STRING, "path to HLSL support files" }, + { WINOPTION_HLSLPATH, "hlsl", core_options::option_type::PATH, "path to HLSL support files" }, { WINOPTION_HLSL_ENABLE";hlsl", "0", core_options::option_type::BOOLEAN, "enable HLSL post-processing (PS3.0 required)" }, { WINOPTION_HLSL_OVERSAMPLING, "0", core_options::option_type::BOOLEAN, "enable HLSL oversampling" }, - { WINOPTION_HLSL_WRITE, OSDOPTVAL_AUTO, core_options::option_type::STRING, "enable HLSL AVI writing (huge disk bandwidth suggested)" }, + { WINOPTION_HLSL_WRITE, OSDOPTVAL_AUTO, core_options::option_type::PATH, "enable HLSL AVI writing (huge disk bandwidth suggested)" }, { WINOPTION_HLSL_SNAP_WIDTH, "2048", core_options::option_type::STRING, "HLSL upscaled-snapshot width" }, { WINOPTION_HLSL_SNAP_HEIGHT, "1536", core_options::option_type::STRING, "HLSL upscaled-snapshot height" }, { WINOPTION_SHADOW_MASK_TILE_MODE, "0", core_options::option_type::INTEGER, "shadow mask tile mode (0 for screen based, 1 for source based)" }, @@ -262,9 +262,9 @@ const options_entry windows_options::s_option_entries[] = { WINOPTION_BLOOM_LEVEL6_WEIGHT, "0.04", core_options::option_type::FLOAT, "bloom level 6 weight (1/4 smaller that level 5 target)" }, { WINOPTION_BLOOM_LEVEL7_WEIGHT, "0.02", core_options::option_type::FLOAT, "bloom level 7 weight (1/4 smaller that level 6 target)" }, { WINOPTION_BLOOM_LEVEL8_WEIGHT, "0.01", core_options::option_type::FLOAT, "bloom level 8 weight (1/4 smaller that level 7 target)" }, - { WINOPTION_LUT_TEXTURE, "lut-default.png", core_options::option_type::STRING, "3D LUT texture filename for screen, PNG format" }, + { WINOPTION_LUT_TEXTURE, "lut-default.png", core_options::option_type::PATH, "3D LUT texture filename for screen, PNG format" }, { WINOPTION_LUT_ENABLE, "0", core_options::option_type::BOOLEAN, "Enables 3D LUT to be applied to screen after post-processing" }, - { WINOPTION_UI_LUT_TEXTURE, "lut-default.png", core_options::option_type::STRING, "3D LUT texture filename of UI, PNG format" }, + { WINOPTION_UI_LUT_TEXTURE, "lut-default.png", core_options::option_type::PATH, "3D LUT texture filename of UI, PNG format" }, { WINOPTION_UI_LUT_ENABLE, "0", core_options::option_type::BOOLEAN, "enable 3D LUT to be applied to UI and artwork after post-processing" }, // full screen options -- cgit v1.2.3