diff options
| author | 2017-05-07 14:40:12 +1000 | |
|---|---|---|
| committer | 2017-05-07 14:40:12 +1000 | |
| commit | a400c011a96197281e205d265c267ecd915fb597 (patch) | |
| tree | 68bb8b132d9c8dea4e587c22db6b32c6ece08b73 /src/lib/util | |
| parent | 1970bcfab8103d41ce65bffa7933d19eda1f1b19 (diff) | |
Revert "Overhaul to how MAME handles options (#2260)"
This reverts commit 536990e77b49ccc50ef275bfbf1018cc29c16154.
Conflicts:
src/frontend/mame/mame.cpp
Sorry, but this change was half-baked. It breaks a lot of existing
functionality and clearly hasn't been tested in more than a tiny subset
of use cases. Please play this work back onto your own branch, and test
it before submitting another PR.
Diffstat (limited to 'src/lib/util')
| -rw-r--r-- | src/lib/util/options.cpp | 968 | ||||
| -rw-r--r-- | src/lib/util/options.h | 294 |
2 files changed, 550 insertions, 712 deletions
diff --git a/src/lib/util/options.cpp b/src/lib/util/options.cpp index 8889107eba1..8500eda327d 100644 --- a/src/lib/util/options.cpp +++ b/src/lib/util/options.cpp @@ -2,7 +2,7 @@ // copyright-holders:Aaron Giles /*************************************************************************** - options.cpp + options.c Core options code code @@ -44,335 +44,155 @@ const char *const core_options::s_option_unadorned[MAX_UNADORNED_OPTIONS] = }; -//************************************************************************** -// OPTIONS EXCEPTION CLASS -//************************************************************************** - -//------------------------------------------------- -// options_exception - constructor -//------------------------------------------------- - -options_exception::options_exception(std::string &&message) - : m_message(std::move(message)) -{ -} - - -//------------------------------------------------- -// options_warning_exception - constructor -//------------------------------------------------- - -options_warning_exception::options_warning_exception(std::string &&message) - : options_exception(std::move(message)) -{ -} - - -//------------------------------------------------- -// options_error_exception - constructor -//------------------------------------------------- - -options_error_exception::options_error_exception(std::string &&message) - : options_exception(std::move(message)) -{ -} - //************************************************************************** -// CORE OPTIONS ENTRY BASE CLASS +// CORE OPTIONS ENTRY //************************************************************************** //------------------------------------------------- // entry - constructor //------------------------------------------------- -core_options::entry::entry(std::vector<std::string> &&names, core_options::option_type type, const char *description) - : m_names(std::move(names)) - , m_priority(OPTION_PRIORITY_DEFAULT) - , m_type(type) - , m_description(description) -{ - if (type == option_type::HEADER) - assert(m_names.size() == 0); - else - assert(m_names.size() > 0); -} - -core_options::entry::entry(std::string &&name, core_options::option_type type, const char *description) - : entry(std::vector<std::string>({ std::move(name) }), type, description) -{ -} - - -//------------------------------------------------- -// entry - destructor -//------------------------------------------------- - -core_options::entry::~entry() +core_options::entry::entry(const char *name, const char *description, uint32_t flags, const char *defvalue) + : m_next(nullptr), + m_flags(flags), + m_error_reported(false), + m_priority(OPTION_PRIORITY_DEFAULT), + m_description(description), + m_changed(false) { -} - - -//------------------------------------------------- -// entry::value -//------------------------------------------------- - -const char *core_options::entry::value() const -{ - // returning 'nullptr' from here signifies a value entry that is essentially "write only" - // and cannot be meaningfully persisted (e.g. - a command or the software name) - return nullptr; -} - - -//------------------------------------------------- -// entry::set_value -//------------------------------------------------- - -void core_options::entry::set_value(std::string &&newvalue, int priority_value, 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 || priority_value >= priority()) - { - internal_set_value(std::move(newvalue)); - m_priority = priority_value; - - // invoke the value changed handler, if appropriate - if (m_value_changed_handler) - m_value_changed_handler(value()); - } -} - - -//------------------------------------------------- -// entry::set_default_value -//------------------------------------------------- - -void core_options::entry::set_default_value(std::string &&newvalue) -{ - // set_default_value() is not necessarily supported for all entry types - throw false; -} - - -//------------------------------------------------- -// entry::validate -//------------------------------------------------- - -void core_options::entry::validate(const std::string &data) -{ - float fval; - int ival; - int minimum_integer, maximum_integer; - float minimum_float, maximum_float; - - switch (type()) + // copy in the name(s) as appropriate + if (name != nullptr) { - case option_type::BOOLEAN: - // booleans must be 0 or 1 - if (sscanf(data.c_str(), "%d", &ival) != 1 || ival < 0 || ival > 1) - throw options_warning_exception("Illegal boolean value for %s: \"%s\"; reverting to %s\n", name(), data, value()); - break; - - case option_type::INTEGER: - // integers must be integral - if (sscanf(data.c_str(), "%d", &ival) != 1) - throw options_warning_exception("Illegal integer value for %s: \"%s\"; reverting to %s\n", name(), data, value()); - - // range checking - if (has_range()) + // first extract any range + std::string namestr(name); + int lparen = namestr.find_first_of('(',0); + int dash = namestr.find_first_of('-',lparen + 1); + int rparen = namestr.find_first_of(')',dash + 1); + if (lparen != -1 && dash != -1 && rparen != -1) { - minimum_integer = atoi(minimum()); - maximum_integer = atoi(maximum()); - if (ival < minimum_integer || ival > maximum_integer) - throw options_warning_exception("Out-of-range integer value for %s: \"%s\" (must be between %d and %d); reverting to %s\n", name(), data, minimum_integer, maximum_integer, value()); + strtrimspace(m_minimum.assign(namestr.substr(lparen + 1, dash - (lparen + 1)))); + strtrimspace(m_maximum.assign(namestr.substr(dash + 1, rparen - (dash + 1)))); + namestr.erase(lparen, rparen + 1 - lparen); } - break; - - case option_type::FLOAT: - if (sscanf(data.c_str(), "%f", &fval) != 1) - throw options_warning_exception("Illegal float value for %s: \"%s\"; reverting to %s\n", name(), data, value()); - // range checking - if (has_range()) + // then chop up any semicolon-separated names + int semi; + int nameindex = 0; + while ((semi = namestr.find_first_of(';')) != -1 && nameindex < ARRAY_LENGTH(m_name)) { - minimum_float = atof(minimum()); - maximum_float = atof(maximum()); - if (fval < minimum_float || fval > maximum_float) - throw options_warning_exception("Out-of-range float value for %s: \"%s\" (must be between %f and %f); reverting to %s\n", name(), data, minimum_float, maximum_float, value()); + m_name[nameindex++].assign(namestr.substr(0, semi)); + namestr.erase(0, semi + 1); } - break; - case OPTION_STRING: - // strings can be anything - break; - - case OPTION_INVALID: - case OPTION_HEADER: - default: - // anything else is invalid - throw options_error_exception("Attempted to set invalid option %s\n", name()); + // finally add the last item + if (nameindex < ARRAY_LENGTH(m_name)) + m_name[nameindex++] = namestr; } -} - -//------------------------------------------------- -// entry::minimum -//------------------------------------------------- - -const char *core_options::entry::minimum() const -{ - return nullptr; + // set the default value + if (defvalue != nullptr) + m_defdata = defvalue; + m_data = m_defdata; } //------------------------------------------------- -// entry::maximum +// set_value - update our data value //------------------------------------------------- -const char *core_options::entry::maximum() const +void core_options::entry::set_value(const char *newdata, int priority) { - return nullptr; -} - - -//------------------------------------------------- -// entry::has_range -//------------------------------------------------- + // ignore if we don't have priority + if (priority < m_priority) + return; -bool core_options::entry::has_range() const -{ - return minimum() && maximum(); + // set the data and priority, then bump the sequence + m_data = newdata; + m_priority = priority; } //------------------------------------------------- -// entry::default_value +// set_default_value - set the default value of +// an option, and reset the current value to it //------------------------------------------------- -const std::string &core_options::entry::default_value() const +void core_options::entry::set_default_value(const char *defvalue) { - // I don't really want this generally available, but MewUI seems to need it. Please - // do not use - throw false; + m_data = defvalue; + m_defdata = defvalue; + m_priority = OPTION_PRIORITY_DEFAULT; } -//************************************************************************** -// CORE OPTIONS SIMPLE ENTRYCLASS -//************************************************************************** - //------------------------------------------------- -// simple_entry - constructor +// set_description - set the description of +// an option //------------------------------------------------- -core_options::simple_entry::simple_entry(std::vector<std::string> &&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_minimum(std::move(minimum)) - , m_maximum(std::move(maximum)) +void core_options::entry::set_description(const char *description) { - m_data = m_defdata; + m_description = description; } -//------------------------------------------------- -// simple_entry - destructor -//------------------------------------------------- - -core_options::simple_entry::~simple_entry() +void core_options::entry::set_flag(uint32_t mask, uint32_t flag) { + m_flags = ( m_flags & mask ) | flag; } //------------------------------------------------- -// simple_entry::value +// revert - revert back to our default if we are +// within the given priority range //------------------------------------------------- -const char *core_options::simple_entry::value() const +void core_options::entry::revert(int priority_hi, int priority_lo) { - const char *result; - switch (type()) + // if our priority is within the range, revert to the default + if (m_priority <= priority_hi && m_priority >= priority_lo) { - case core_options::option_type::BOOLEAN: - case core_options::option_type::INTEGER: - case core_options::option_type::FLOAT: - case core_options::option_type::STRING: - result = m_data.c_str(); - break; - - default: - result = nullptr; - break; + m_data = m_defdata; + m_priority = OPTION_PRIORITY_DEFAULT; } - return result; } -//------------------------------------------------- -// simple_entry::default_value -//------------------------------------------------- - -const std::string &core_options::simple_entry::default_value() const -{ - // only MewUI seems to need this; please don't use - return m_defdata; -} +//************************************************************************** +// CORE OPTIONS +//************************************************************************** //------------------------------------------------- -// internal_set_value +// core_options - constructor //------------------------------------------------- -void core_options::simple_entry::internal_set_value(std::string &&newvalue) +core_options::core_options() { - m_data = std::move(newvalue); } - -//------------------------------------------------- -// set_default_value -//------------------------------------------------- - -void core_options::simple_entry::set_default_value(std::string &&newvalue) +core_options::core_options(const options_entry *entrylist) { - m_defdata = std::move(newvalue); + add_entries(entrylist); } - -//------------------------------------------------- -// minimum -//------------------------------------------------- - -const char *core_options::simple_entry::minimum() const +core_options::core_options(const options_entry *entrylist1, const options_entry *entrylist2) { - return m_minimum.c_str(); + add_entries(entrylist1); + add_entries(entrylist2); } - -//------------------------------------------------- -// maximum -//------------------------------------------------- - -const char *core_options::simple_entry::maximum() const +core_options::core_options(const options_entry *entrylist1, const options_entry *entrylist2, const options_entry *entrylist3) { - return m_maximum.c_str(); + add_entries(entrylist1); + add_entries(entrylist2); + add_entries(entrylist3); } - -//************************************************************************** -// CORE OPTIONS -//************************************************************************** - -//------------------------------------------------- -// core_options - constructor -//------------------------------------------------- - -core_options::core_options() +core_options::core_options(const core_options &src) { + copyfrom(src); } @@ -386,140 +206,82 @@ core_options::~core_options() //------------------------------------------------- -// add_entry - adds an entry +// operator= - assignment operator //------------------------------------------------- -void core_options::add_entry(entry::shared_ptr &&entry, const char *after_header) +core_options &core_options::operator=(const core_options &rhs) { - // update the entry map - for (const std::string &name : entry->names()) - { - // append the entry - add_to_entry_map(std::string(name), entry); - - // for booleans, add the "-noXYZ" option as well - if (entry->type() == option_type::BOOLEAN) - add_to_entry_map(std::string("no") + name, entry); - } - - // and add the entry to the vector - m_entries.emplace_back(std::move(entry)); + // ignore self-assignment + if (this != &rhs) + copyfrom(rhs); + return *this; } //------------------------------------------------- -// add_to_entry_map - adds an entry to the entry -// map +// operator== - compare two sets of options //------------------------------------------------- -void core_options::add_to_entry_map(std::string &&name, entry::shared_ptr &entry) +bool core_options::operator==(const core_options &rhs) { - // it is illegal to call this method for something that already ex0ists - assert(m_entrymap.find(name) == m_entrymap.end()); + // iterate over options in the first list + for (entry &curentry : m_entrylist) + if (!curentry.is_header()) + { + // if the values differ, return false + if (strcmp(curentry.value(), rhs.value(curentry.name())) != 0) + return false; + } - // append the entry - m_entrymap.emplace(std::make_pair(name, entry::weak_ptr(entry))); + return true; } //------------------------------------------------- -// add_entry - adds an entry based on an -// options_entry +// operator!= - compare two sets of options //------------------------------------------------- -void core_options::add_entry(const options_entry &opt, bool override_existing) +bool core_options::operator!=(const core_options &rhs) { - std::vector<std::string> names; - std::string minimum, maximum; - - // copy in the name(s) as appropriate - if (opt.name) - { - // first extract any range - std::string namestr(opt.name); - int lparen = namestr.find_first_of('(', 0); - int dash = namestr.find_first_of('-', lparen + 1); - int rparen = namestr.find_first_of(')', dash + 1); - if (lparen != -1 && dash != -1 && rparen != -1) - { - strtrimspace(minimum.assign(namestr.substr(lparen + 1, dash - (lparen + 1)))); - strtrimspace(maximum.assign(namestr.substr(dash + 1, rparen - (dash + 1)))); - namestr.erase(lparen, rparen + 1 - lparen); - } + return !operator==(rhs); +} - // then chop up any semicolon-separated names - size_t semi; - while ((semi = namestr.find_first_of(';')) != std::string::npos) - { - names.push_back(namestr.substr(0, semi)); - namestr.erase(0, semi + 1); - } - // finally add the last item - names.push_back(std::move(namestr)); - } +//------------------------------------------------- +// add_entry - add an entry to the current +// options set +//------------------------------------------------- - // we might be called with an existing entry - entry::shared_ptr existing_entry; - do +void core_options::add_entry(const char *name, const char *description, uint32_t flags, const char *defvalue, bool override_existing) +{ + // allocate a new entry + auto newentry = global_alloc(entry(name, description, flags, defvalue)); + if (newentry->name() != nullptr) { - for (const std::string &name : names) - { - existing_entry = get_entry(name.c_str()); - if (existing_entry) - break; - } - - if (existing_entry) + // see if we match an existing entry + auto checkentry = m_entrymap.find(newentry->name()); + if (checkentry != m_entrymap.end()) { + entry *existing = checkentry->second; + // if we're overriding existing entries, then remove the old one if (override_existing) - remove_entry(*existing_entry); + m_entrylist.remove(*existing); + + // otherwise, just override the default and current values and throw out the new entry else + { + existing->set_default_value(newentry->value()); + global_free(newentry); return; + } } - } while (existing_entry); - - // set the default value - std::string defdata = opt.defvalue ? opt.defvalue : ""; - - // create and add the entry - add_entry( - std::move(names), - opt.description, - opt.type, - std::move(defdata), - std::move(minimum), - std::move(maximum)); -} - - -//------------------------------------------------- -// add_entry -//------------------------------------------------- - -void core_options::add_entry(std::vector<std::string> &&names, const char *description, option_type type, std::string &&default_value, std::string &&minimum, std::string &&maximum) -{ - // create the entry - entry::shared_ptr new_entry = std::make_shared<simple_entry>( - std::move(names), - description, - type, - std::move(default_value), - std::move(minimum), - std::move(maximum)); - - // and add it - add_entry(std::move(new_entry)); -} + // need to call value_changed() with initial value + value_changed(newentry->name(), newentry->value()); + } -//------------------------------------------------- -// add_header -//------------------------------------------------- - -void core_options::add_header(const char *description) -{ - add_entry(std::vector<std::string>(), description, option_type::HEADER); + // add us to the list and maps + append_entry(*newentry); } @@ -531,8 +293,8 @@ void core_options::add_header(const char *description) void core_options::add_entries(const options_entry *entrylist, bool override_existing) { // loop over entries until we hit a nullptr name - for (int i = 0; entrylist[i].name || entrylist[i].type == option_type::HEADER; i++) - add_entry(entrylist[i], override_existing); + for ( ; entrylist->name != nullptr || (entrylist->flags & OPTION_HEADER) != 0; entrylist++) + add_entry(*entrylist, override_existing); } @@ -543,8 +305,13 @@ void core_options::add_entries(const options_entry *entrylist, bool override_exi void core_options::set_default_value(const char *name, const char *defvalue) { + // find the entry and bail if we can't + auto curentry = m_entrymap.find(name); + if (curentry == m_entrymap.end()) + return; + // update the data and default data - get_entry(name)->set_default_value(defvalue); + curentry->second->set_default_value(defvalue); } @@ -555,8 +322,13 @@ void core_options::set_default_value(const char *name, const char *defvalue) void core_options::set_description(const char *name, const char *description) { + // find the entry and bail if we can't + auto curentry = m_entrymap.find(name); + if (curentry == m_entrymap.end()) + return; + // update the data and default data - get_entry(name)->set_description(description); + curentry->second->set_description(description); } @@ -565,12 +337,10 @@ void core_options::set_description(const char *name, const char *description) // command line arguments //------------------------------------------------- -void core_options::parse_command_line(std::vector<std::string> &args, int priority) +bool core_options::parse_command_line(std::vector<std::string> &args, int priority, std::string &error_string) { - std::ostringstream error_stream; - condition_type condition = condition_type::NONE; - // reset the errors and the command + error_string.clear(); m_command.clear(); // iterate through arguments @@ -584,8 +354,8 @@ void core_options::parse_command_line(std::vector<std::string> &args, int priori const char *optionname = is_unadorned ? core_options::unadorned(unadorned_index++) : &curarg[1]; // find our entry; if not found, continue - auto curentry = get_entry(optionname); - if (!curentry) + auto curentry = m_entrymap.find(optionname); + if (curentry == m_entrymap.end()) { // we need to relocate this option if (new_argc != arg) @@ -606,19 +376,21 @@ void core_options::parse_command_line(std::vector<std::string> &args, int priori } // process commands first - if (curentry->type() == option_type::COMMAND) + if (curentry->second->type() == OPTION_COMMAND) { // can only have one command if (!m_command.empty()) - throw options_error_exception("Error: multiple commands specified -%s and %s\n", m_command, curarg); - - m_command = curentry->name(); + { + error_string.append(string_format("Error: multiple commands specified -%s and %s\n", m_command, curarg)); + return false; + } + m_command = curentry->second->name(); continue; } // get the data for this argument, special casing booleans std::string newdata; - if (curentry->type() == option_type::BOOLEAN) + if (curentry->second->type() == OPTION_BOOLEAN) { newdata = (strncmp(&curarg[1], "no", 2) == 0) ? "0" : "1"; } @@ -633,18 +405,17 @@ void core_options::parse_command_line(std::vector<std::string> &args, int priori } else { - throw options_error_exception("Error: option %s expected a parameter\n", curarg); + error_string.append(string_format("Error: option %s expected a parameter\n", curarg)); + return false; } args[arg].clear(); // set the new data - prettify_and_set_value(*curentry, std::move(newdata), priority, error_stream, condition); + validate_and_set_data(*curentry->second, std::move(newdata), priority, error_string); } args.resize(new_argc); - - // did we have any errors that may need to be aggregated? - throw_options_exception_if_appropriate(condition, error_stream); + return true; } @@ -653,11 +424,8 @@ void core_options::parse_command_line(std::vector<std::string> &args, int priori // an INI file //------------------------------------------------- -void core_options::parse_ini_file(util::core_file &inifile, int priority, bool always_override) +bool core_options::parse_ini_file(util::core_file &inifile, int priority, int ignore_priority, std::string &error_string) { - std::ostringstream error_stream; - condition_type condition = condition_type::NONE; - // loop over lines in the file char buffer[4096]; while (inifile.gets(buffer, ARRAY_LENGTH(buffer)) != nullptr) @@ -681,8 +449,7 @@ void core_options::parse_ini_file(util::core_file &inifile, int priority, bool a // if we hit the end early, print a warning and continue if (*temp == 0) { - condition = std::max(condition, condition_type::WARN); - util::stream_format(error_stream, "Warning: invalid line in INI: %s", buffer); + error_string.append(string_format("Warning: invalid line in INI: %s", buffer)); continue; } @@ -702,68 +469,77 @@ void core_options::parse_ini_file(util::core_file &inifile, int priority, bool a *temp = 0; // find our entry - entry::shared_ptr curentry = get_entry(optionname); - if (!curentry) + auto curentry = m_entrymap.find(optionname); + if (curentry == m_entrymap.end()) { - condition = std::max(condition, condition_type::WARN); - util::stream_format(error_stream, "Warning: unknown option in INI: %s\n", optionname); + if (priority >= ignore_priority) + error_string.append(string_format("Warning: unknown option in INI: %s\n", optionname)); continue; } // set the new data - prettify_and_set_value(*curentry, optiondata, priority, error_stream, condition); + validate_and_set_data(*curentry->second, optiondata, priority, error_string); } - - // did we have any errors that may need to be aggregated? - throw_options_exception_if_appropriate(condition, error_stream); + return true; } //------------------------------------------------- -// throw_options_exception_if_appropriate +// pluck_from_command_line - finds a specific +// value from within a command line //------------------------------------------------- -void core_options::throw_options_exception_if_appropriate(core_options::condition_type condition, std::ostringstream &error_stream) +bool core_options::pluck_from_command_line(std::vector<std::string> &args, const std::string &optionname, std::string &result) { - switch(condition) - { - case condition_type::NONE: - // do nothing - break; + // find this entry within the options (it is illegal to call this with a non-existant option + // so we assert if not present) + auto curentry = m_entrymap.find(optionname); + assert(curentry != m_entrymap.end()); - case condition_type::WARN: - throw options_warning_exception(error_stream.str()); + // build a vector with potential targets + std::vector<std::string> targets; + const char *potential_target; + int index = 0; + while ((potential_target = curentry->second->name(index++)) != nullptr) + { + // not supporting unadorned options for now + targets.push_back(std::string("-") + potential_target); + } - case condition_type::ERR: - throw options_warning_exception(error_stream.str()); + // find each of the targets in the argv array + for (int i = 1; i < args.size() - 1; i++) + { + auto const iter = std::find_if( + targets.begin(), + targets.end(), + [&args, i](const std::string &targ) { return targ == args[i]; }); + if (iter != targets.end()) + { + // get the result + result = std::move(args[i + 1]); - default: - // should not get here - throw false; + // remove this arguments from the list + auto const pos = std::next(args.begin(), i); + args.erase(pos, std::next(pos, 2)); + return true; + } } + + result.clear(); + return false; } //------------------------------------------------- -// copy_from +// revert - revert options at or below a certain +// priority back to their defaults //------------------------------------------------- -void core_options::copy_from(const core_options &that) +void core_options::revert(int priority_hi, int priority_lo) { - for (auto &dest_entry : m_entries) - { - if (dest_entry->names().size() > 0) - { - // identify the source entry - const entry::shared_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); - } - } - } + // iterate over options and revert to defaults if below the given priority + for (entry &curentry : m_entrylist) + curentry.revert(priority_hi, priority_lo); } @@ -784,34 +560,47 @@ std::string core_options::output_ini(const core_options *diff) const std::string overridden_value; // loop over all items - for (auto &curentry : m_entries) + for (entry &curentry : m_entrylist) { - if (curentry->type() == option_type::HEADER) + const char *name = curentry.name(); + const char *value; + switch (override_get_value(name, overridden_value)) { - // header: record description - last_header = curentry->description(); + case override_get_value_result::NONE: + default: + value = curentry.value(); + break; + + case override_get_value_result::SKIP: + continue; + + case override_get_value_result::OVERRIDE: + value = overridden_value.c_str(); + break; } - else + bool is_unadorned = false; + + // check if it's unadorned + if (name && strlen(name) && !strcmp(name, core_options::unadorned(unadorned_index))) { - const std::string &name(curentry->name()); - const char *value(curentry->value()); + unadorned_index++; + is_unadorned = true; + } - // check if it's unadorned - bool is_unadorned = false; - if (name == core_options::unadorned(unadorned_index)) - { - unadorned_index++; - is_unadorned = true; - } + // header: record description + if (curentry.is_header()) + last_header = curentry.description(); - // output entries for all non-command items (items with value) - if (value) + // otherwise, output entries for all non-command items + else if (!curentry.is_command()) + { + if (!curentry.is_internal()) { // look up counterpart in diff, if diff is specified - if (!diff || strcmp(value, diff->value(name.c_str()))) + if (diff == nullptr || strcmp(value, diff->value(name)) != 0) { // output header, if we have one - if (last_header) + if (last_header != nullptr) { if (num_valid_headers++) buffer << '\n'; @@ -822,7 +611,7 @@ std::string core_options::output_ini(const core_options *diff) const // and finally output the data, skip if unadorned if (!is_unadorned) { - if (strchr(value, ' ')) + if (strchr(value, ' ') != nullptr) util::stream_format(buffer, "%-25s \"%s\"\n", name, value); else util::stream_format(buffer, "%-25s %s\n", name, value); @@ -845,15 +634,15 @@ std::string core_options::output_help() const std::ostringstream buffer; // loop over all items - for (auto &curentry : m_entries) + for (entry &curentry : m_entrylist) { // header: just print - if (curentry->type() == option_type::HEADER) - util::stream_format(buffer, "\n#\n# %s\n#\n", curentry->description()); + if (curentry.is_header()) + util::stream_format(buffer, "\n#\n# %s\n#\n", curentry.description()); // otherwise, output entries for all non-deprecated items - else if (curentry->description() != nullptr) - util::stream_format(buffer, "-%-20s%s\n", curentry->name(), curentry->description()); + else if (curentry.description() != nullptr) + util::stream_format(buffer, "-%-20s%s\n", curentry.name(), curentry.description()); } return buffer.str(); } @@ -863,9 +652,10 @@ std::string core_options::output_help() const // value - return the raw option value //------------------------------------------------- -const char *core_options::value(const char *option) const +const char *core_options::value(const char *name) const { - return get_entry(option)->value(); + auto curentry = m_entrymap.find(name); + return (curentry != m_entrymap.end()) ? curentry->second->value() : ""; } @@ -873,143 +663,251 @@ const char *core_options::value(const char *option) const // description - return description of option //------------------------------------------------- -const char *core_options::description(const char *option) const +const char *core_options::description(const char *name) const { - return get_entry(option)->description(); + auto curentry = m_entrymap.find(name); + return (curentry != m_entrymap.end()) ? curentry->second->description() : ""; } -//************************************************************************** -// LEGACY -//************************************************************************** - //------------------------------------------------- -// set_value - set the raw option value +// priority - return the priority of option //------------------------------------------------- -void core_options::set_value(const std::string &name, const std::string &value, int priority) +int core_options::priority(const char *name) const { - set_value(name, std::string(value), priority); + auto curentry = m_entrymap.find(name); + return (curentry != m_entrymap.end()) ? curentry->second->priority() : 0; } -void core_options::set_value(const std::string &name, std::string &&value, int priority) -{ - get_entry(name)->set_value(std::move(value), priority); -} -void core_options::set_value(const std::string &name, int value, int priority) -{ - set_value(name, string_format("%d", value), priority); -} +//------------------------------------------------- +// exists - return if option exists in list +//------------------------------------------------- -void core_options::set_value(const std::string &name, float value, int priority) +bool core_options::exists(const char *name) const { - set_value(name, string_format("%f", value), priority); + return (m_entrymap.find(name) != m_entrymap.end()); } //------------------------------------------------- -// remove_entry - remove an entry from our list -// and map +// set_value - set the raw option value //------------------------------------------------- -void core_options::remove_entry(core_options::entry &delentry) +bool core_options::set_value(const char *name, const char *value, int priority, std::string &error_string) { - // find this in m_entries - auto iter = std::find_if( - m_entries.begin(), - m_entries.end(), - [&delentry](const auto &x) { return &*x == &delentry; }); - assert(iter != m_entries.end()); + // find the entry first + auto curentry = m_entrymap.find(name); + if (curentry == m_entrymap.end()) + { + error_string.append(string_format("Attempted to set unknown option %s\n", name)); + return false; + } - // erase each of the items out of the entry map - for (const std::string &name : delentry.names()) - m_entrymap.erase(name); + // validate and set the item normally + return validate_and_set_data(*curentry->second, value, priority, error_string); +} - // finally erase it - m_entries.erase(iter); +bool core_options::set_value(const char *name, int value, int priority, std::string &error_string) +{ + return set_value(name, string_format("%d", value).c_str(), priority, error_string); } +bool core_options::set_value(const char *name, float value, int priority, std::string &error_string) +{ + return set_value(name, string_format("%f", value).c_str(), priority, error_string); +} -//------------------------------------------------- -// prettify_and_set_value -//------------------------------------------------- -void core_options::prettify_and_set_value(entry &curentry, std::string &&data, int priority, std::ostream &error_stream, condition_type &condition) +void core_options::set_flag(const char *name, uint32_t mask, uint32_t flag) { - // trim any whitespace - strtrimspace(data); - - // trim quotes - if (data.find_first_of('"') == 0 && data.find_last_of('"') == data.length() - 1) + // find the entry first + auto curentry = m_entrymap.find(name); + if ( curentry == m_entrymap.end()) { - data.erase(0, 1); - data.erase(data.length() - 1, 1); + return; } + curentry->second->set_flag(mask, flag); +} - // 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::move(data), priority); - } - catch (options_warning_exception &ex) - { - // we want to aggregate option exceptions - error_stream << ex.message(); - condition = std::max(condition, condition_type::WARN); - } - catch (options_error_exception &ex) +void core_options::mark_changed(const char* name) +{ + // find the entry first + auto curentry = m_entrymap.find(name); + if (curentry == m_entrymap.end()) { - // we want to aggregate option exceptions - error_stream << ex.message(); - condition = std::max(condition, condition_type::ERR); + return; } + curentry->second->mark_changed(); } - //------------------------------------------------- -// get_entry +// reset - reset the options state, removing +// everything //------------------------------------------------- -const core_options::entry::shared_ptr core_options::get_entry(const std::string &name) const +void core_options::reset() { - auto curentry = m_entrymap.find(name); - return (curentry != m_entrymap.end()) ? curentry->second.lock() : nullptr; + m_entrylist.reset(); + m_entrymap.clear(); } -core_options::entry::shared_ptr core_options::get_entry(const std::string &name) + +//------------------------------------------------- +// append_entry - append an entry to our list +// and index it in the map +//------------------------------------------------- + +void core_options::append_entry(core_options::entry &newentry) { - auto curentry = m_entrymap.find(name); - return (curentry != m_entrymap.end()) ? curentry->second.lock() : nullptr; + m_entrylist.append(newentry); + + // if we have names, add them to the map + for (int name = 0; name < ARRAY_LENGTH(newentry.m_name); name++) + if (newentry.name(name) != nullptr) + { + m_entrymap.insert(std::make_pair(newentry.name(name), &newentry)); + // for boolean options add a "no" variant as well + if (newentry.type() == OPTION_BOOLEAN) + m_entrymap.insert(std::make_pair(std::string("no").append(newentry.name(name)), &newentry)); + } } //------------------------------------------------- -// set_value_changed_handler +// remove_entry - remove an entry from our list +// and map //------------------------------------------------- -void core_options::set_value_changed_handler(const std::string &name, std::function<void(const char *)> &&handler) +void core_options::remove_entry(core_options::entry &delentry) { - get_entry(name)->set_value_changed_handler(std::move(handler)); + // remove all names from the map + for (int name = 0; name < ARRAY_LENGTH(delentry.m_name); name++) + if (!delentry.m_name[name].empty()) + { + auto entry = m_entrymap.find(delentry.m_name[name]); + if (entry!= m_entrymap.end()) m_entrymap.erase(entry); + } + + // remove the entry from the list + m_entrylist.remove(delentry); } +/** + * @fn void core_options::copyfrom(const core_options &src) + * + * @brief ------------------------------------------------- + * copyfrom - copy options from another set + * -------------------------------------------------. + * + * @param src Source for the. + */ -//------------------------------------------------- -// header_exists -//------------------------------------------------- +void core_options::copyfrom(const core_options &src) +{ + // reset ourselves first + reset(); + + // iterate through the src options and make our own + for (entry &curentry : src.m_entrylist) + append_entry(*global_alloc(entry(curentry.name(), curentry.description(), curentry.flags(), curentry.default_value()))); +} -bool core_options::header_exists(const char *description) const +/** + * @fn bool core_options::validate_and_set_data(core_options::entry &curentry, const char *newdata, int priority, std::string &error_string) + * + * @brief ------------------------------------------------- + * validate_and_set_data - make sure the data is of the appropriate type and within + * range, then set it + * -------------------------------------------------. + * + * @param [in,out] curentry The curentry. + * @param newdata The newdata. + * @param priority The priority. + * @param [in,out] error_string The error string. + * + * @return true if it succeeds, false if it fails. + */ + +bool core_options::validate_and_set_data(core_options::entry &curentry, std::string &&data, int priority, std::string &error_string) { - auto iter = std::find_if( - m_entries.begin(), - m_entries.end(), - [description](const auto &entry) + // trim any whitespace + strtrimspace(data); + + // trim quotes + if (data.find_first_of('"') == 0 && data.find_last_of('"') == data.length() - 1) + { + data.erase(0, 1); + data.erase(data.length() - 1, 1); + } + + // let derived classes override how we set this data + if (override_set_value(curentry.name(), data)) + return true; + + // validate the type of data and optionally the range + float fval; + int ival; + switch (curentry.type()) + { + // booleans must be 0 or 1 + case OPTION_BOOLEAN: + if (sscanf(data.c_str(), "%d", &ival) != 1 || ival < 0 || ival > 1) + { + error_string.append(string_format("Illegal boolean value for %s: \"%s\"; reverting to %s\n", curentry.name(), data.c_str(), curentry.value())); + return false; + } + break; + + // integers must be integral + case OPTION_INTEGER: + if (sscanf(data.c_str(), "%d", &ival) != 1) + { + error_string.append(string_format("Illegal integer value for %s: \"%s\"; reverting to %s\n", curentry.name(), data.c_str(), curentry.value())); + return false; + } + if (curentry.has_range() && (ival < atoi(curentry.minimum()) || ival > atoi(curentry.maximum()))) { - return entry->type() == option_type::HEADER - && entry->description() - && !strcmp(entry->description(), description); - }); + error_string.append(string_format("Out-of-range integer value for %s: \"%s\" (must be between %s and %s); reverting to %s\n", curentry.name(), data.c_str(), curentry.minimum(), curentry.maximum(), curentry.value())); + return false; + } + break; - return iter != m_entries.end(); + // floating-point values must be numeric + case OPTION_FLOAT: + if (sscanf(data.c_str(), "%f", &fval) != 1) + { + error_string.append(string_format("Illegal float value for %s: \"%s\"; reverting to %s\n", curentry.name(), data.c_str(), curentry.value())); + return false; + } + if (curentry.has_range() && ((double) fval < atof(curentry.minimum()) || (double) fval > atof(curentry.maximum()))) + { + error_string.append(string_format("Out-of-range float value for %s: \"%s\" (must be between %s and %s); reverting to %s\n", curentry.name(), data.c_str(), curentry.minimum(), curentry.maximum(), curentry.value())); + return false; + } + break; + + // strings can be anything + case OPTION_STRING: + break; + + // anything else is invalid + case OPTION_INVALID: + case OPTION_HEADER: + default: + error_string.append(string_format("Attempted to set invalid option %s\n", curentry.name())); + return false; + } + + // set the data + curentry.set_value(data.c_str(), priority); + value_changed(curentry.name(), data); + return true; +} + +core_options::entry *core_options::get_entry(const char *name) const +{ + auto curentry = m_entrymap.find(name); + return (curentry != m_entrymap.end()) ? curentry->second : nullptr; } diff --git a/src/lib/util/options.h b/src/lib/util/options.h index a36ff95cab6..edf7f8600c8 100644 --- a/src/lib/util/options.h +++ b/src/lib/util/options.h @@ -13,13 +13,26 @@ #include "corefile.h" #include <unordered_map> -#include <sstream> + //************************************************************************** // CONSTANTS //************************************************************************** +// option types +const uint32_t OPTION_TYPE_MASK = 0x0007; // up to 8 different types +enum +{ + OPTION_INVALID, // invalid + OPTION_HEADER, // a header item + OPTION_COMMAND, // a command + OPTION_BOOLEAN, // boolean option + OPTION_INTEGER, // integer option + OPTION_FLOAT, // floating-point option + OPTION_STRING // string option +}; + // option priorities const int OPTION_PRIORITY_DEFAULT = 0; // defaults are at 0 priority const int OPTION_PRIORITY_LOW = 50; // low priority @@ -27,55 +40,20 @@ const int OPTION_PRIORITY_NORMAL = 100; // normal priority const int OPTION_PRIORITY_HIGH = 150; // high priority const int OPTION_PRIORITY_MAXIMUM = 255; // maximum priority +const uint32_t OPTION_FLAG_INTERNAL = 0x40000000; + //************************************************************************** // TYPE DEFINITIONS //************************************************************************** -struct options_entry; - -// exception thrown by core_options when an illegal request is made -class options_exception : public std::exception -{ -public: - const std::string &message() const { return m_message; } - virtual const char *what() const noexcept override { return message().c_str(); } - -protected: - options_exception(std::string &&message); - -private: - std::string m_message; -}; - -class options_warning_exception : public options_exception -{ -public: - template <typename... Params> options_warning_exception(const char *fmt, Params &&...args) - : options_warning_exception(util::string_format(fmt, std::forward<Params>(args)...)) - { - } - - options_warning_exception(std::string &&message); - options_warning_exception(const options_warning_exception &) = default; - options_warning_exception(options_warning_exception &&) = default; - options_warning_exception& operator=(const options_warning_exception &) = default; - options_warning_exception& operator=(options_warning_exception &&) = default; -}; - -class options_error_exception : public options_exception +// static structure describing a single option with its description and default value +struct options_entry { -public: - template <typename... Params> options_error_exception(const char *fmt, Params &&...args) - : options_error_exception(util::string_format(fmt, std::forward<Params>(args)...)) - { - } - - options_error_exception(std::string &&message); - options_error_exception(const options_error_exception &) = default; - options_error_exception(options_error_exception &&) = default; - options_error_exception& operator=(const options_error_exception &) = default; - options_error_exception& operator=(options_error_exception &&) = default; + const char * name; // name on the command line + const char * defvalue; // default value of this argument + uint32_t flags; // flags to describe the option + const char * description; // description for -showusage }; @@ -85,95 +63,94 @@ class core_options static const int MAX_UNADORNED_OPTIONS = 16; public: - enum class option_type - { - INVALID, // invalid - HEADER, // a header item - COMMAND, // a command - BOOLEAN, // boolean option - INTEGER, // integer option - FLOAT, // floating-point option - STRING // string option - }; - // information about a single entry in the options class entry { - public: - typedef std::shared_ptr<entry> shared_ptr; - typedef std::weak_ptr<entry> weak_ptr; - - // constructor/destructor - entry(std::vector<std::string> &&names, option_type type = option_type::STRING, const char *description = nullptr); - entry(std::string &&name, option_type type = option_type::STRING, const char *description = nullptr); - entry(const entry &) = delete; - entry(entry &&) = delete; - entry& operator=(const entry &) = delete; - entry& operator=(entry &&) = delete; - virtual ~entry(); - - // accessors - const std::vector<std::string> &names() const { return m_names; } - const std::string &name() const { return m_names[0]; } - virtual const char *value() const; - int priority() const { return m_priority; } - option_type type() const { return m_type; } - const char *description() const { return m_description; } - virtual const std::string &default_value() const; - virtual const char *minimum() const; - virtual const char *maximum() const; - bool has_range() const; + friend class core_options; + friend class simple_list<entry>; - // mutators - void set_value(std::string &&newvalue, int priority, bool always_override = 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<void(const char *)> &&handler) { m_value_changed_handler = std::move(handler); } + // construction/destruction + entry(const char *name, const char *description, uint32_t flags = 0, const char *defvalue = nullptr); - protected: - virtual void internal_set_value(std::string &&newvalue) = 0; + public: + // getters + entry *next() const { return m_next; } + const char *name(int index = 0) const { return (index < ARRAY_LENGTH(m_name) && !m_name[index].empty()) ? m_name[index].c_str() : nullptr; } + const char *description() const { return m_description; } + const char *value() const { return m_data.c_str(); } + const char *default_value() const { return m_defdata.c_str(); } + const char *minimum() const { return m_minimum.c_str(); } + const char *maximum() const { return m_maximum.c_str(); } + int type() const { return (m_flags & OPTION_TYPE_MASK); } + uint32_t flags() const { return m_flags; } + bool is_header() const { return type() == OPTION_HEADER; } + bool is_command() const { return type() == OPTION_COMMAND; } + bool is_internal() const { return m_flags & OPTION_FLAG_INTERNAL; } + bool has_range() const { return (!m_minimum.empty() && !m_maximum.empty()); } + int priority() const { return m_priority; } + bool is_changed() const { return m_changed; } + + // setters + void set_value(const char *newvalue, int priority); + void set_default_value(const char *defvalue); + void set_description(const char *description); + void set_flag(uint32_t mask, uint32_t flag); + void mark_changed() { m_changed = true; } + void revert(int priority_hi, int priority_lo); private: - void validate(const std::string &value); - - std::vector<std::string> m_names; - int m_priority; - core_options::option_type m_type; - const char * m_description; - std::function<void(const char *)> m_value_changed_handler; + // internal state + entry * m_next; // link to the next data + uint32_t m_flags; // flags from the entry + bool m_error_reported; // have we reported an error on this option yet? + int m_priority; // priority of the data set + const char * m_description; // description for this item + std::string m_name[4]; // up to 4 names for the item + std::string m_data; // data for this item + std::string m_defdata; // default data for this item + std::string m_minimum; // minimum value + std::string m_maximum; // maximum value + bool m_changed; // changed flag }; // construction/destruction core_options(); - core_options(const core_options &) = delete; - core_options(core_options &&) = delete; - core_options& operator=(const core_options &) = delete; - core_options& operator=(core_options &&) = delete; + core_options(const options_entry *entrylist); + core_options(const options_entry *entrylist1, const options_entry *entrylist2); + core_options(const options_entry *entrylist1, const options_entry *entrylist2, const options_entry *entrylist3); + core_options(const core_options &src); virtual ~core_options(); + // operators + core_options &operator=(const core_options &rhs); + bool operator==(const core_options &rhs); + bool operator!=(const core_options &rhs); + // getters + entry *first() const { return m_entrylist.first(); } const std::string &command() const { return m_command; } - const entry::shared_ptr get_entry(const std::string &name) const; - entry::shared_ptr get_entry(const std::string &name); - const std::vector<entry::shared_ptr> &entries() const { return m_entries; } - bool exists(const std::string &name) const { return get_entry(name) != nullptr; } - bool header_exists(const char *description) const; + entry *get_entry(const char *name) const; + + // range iterators + using auto_iterator = simple_list<entry>::auto_iterator; + auto_iterator begin() const { return m_entrylist.begin(); } + auto_iterator end() const { return m_entrylist.end(); } // configuration - void add_entry(entry::shared_ptr &&entry, const char *after_header = nullptr); - void add_entry(const options_entry &entry, bool override_existing = false); - void add_entry(std::vector<std::string> &&names, const char *description, option_type type, std::string &&default_value = "", std::string &&minimum = "", std::string &&maximum = ""); - void add_header(const char *description); + void add_entry(const char *name, const char *description, uint32_t flags = 0, const char *defvalue = nullptr, bool override_existing = false); + void add_entry(const options_entry &data, bool override_existing = false) { add_entry(data.name, data.description, data.flags, data.defvalue, override_existing); } void add_entries(const options_entry *entrylist, bool override_existing = false); void set_default_value(const char *name, const char *defvalue); void set_description(const char *name, const char *description); void remove_entry(entry &delentry); - void set_value_changed_handler(const std::string &name, std::function<void(const char *)> &&handler); // parsing/input - void parse_command_line(std::vector<std::string> &args, int priority); - void parse_ini_file(util::core_file &inifile, int priority, bool always_override); - void copy_from(const core_options &that); + bool parse_command_line(std::vector<std::string> &args, int priority, std::string &error_string); + bool parse_ini_file(util::core_file &inifile, int priority, int ignore_priority, std::string &error_string); + bool pluck_from_command_line(std::vector<std::string> &args, const std::string &name, std::string &result); + + // reverting + void revert(int priority_hi = OPTION_PRIORITY_MAXIMUM, int priority_lo = OPTION_PRIORITY_DEFAULT); // output std::string output_ini(const core_options *diff = nullptr) const; @@ -182,88 +159,51 @@ public: // reading const char *value(const char *option) const; const char *description(const char *option) const; - bool bool_value(const char *option) const { return int_value(option) != 0; } - int int_value(const char *option) const { return atoi(value(option)); } - float float_value(const char *option) const { return atof(value(option)); } + int priority(const char *option) const; + bool bool_value(const char *name) const { return (atoi(value(name)) != 0); } + int int_value(const char *name) const { return atoi(value(name)); } + float float_value(const char *name) const { return atof(value(name)); } + bool exists(const char *name) const; // setting - void set_value(const std::string &name, const std::string &value, int priority); - void set_value(const std::string &name, std::string &&value, int priority); - void set_value(const std::string &name, int value, int priority); - void set_value(const std::string &name, float value, int priority); + bool set_value(const char *name, const char *value, int priority, std::string &error_string); + bool set_value(const char *name, int value, int priority, std::string &error_string); + bool set_value(const char *name, float value, int priority, std::string &error_string); + void set_flag(const char *name, uint32_t mask, uint32_t flags); + void mark_changed(const char *name); // misc static const char *unadorned(int x = 0) { return s_option_unadorned[std::min(x, MAX_UNADORNED_OPTIONS - 1)]; } + int options_count() const { return m_entrylist.count(); } -private: - class simple_entry : public entry - { - public: - // construction/destruction - simple_entry(std::vector<std::string> &&names, const char *description, core_options::option_type type, std::string &&defdata, std::string &&minimum, std::string &&maximum); - simple_entry(const simple_entry &) = delete; - simple_entry(simple_entry &&) = delete; - simple_entry& operator=(const simple_entry &) = delete; - simple_entry& operator=(simple_entry &&) = delete; - ~simple_entry(); - - // getters - virtual const char *value() const override; - virtual const char *minimum() const override; - virtual const char *maximum() const override; - virtual const std::string &default_value() const override; - - virtual void set_default_value(std::string &&newvalue) override; - - protected: - virtual void internal_set_value(std::string &&newvalue) override; - - private: - // internal state - std::string m_data; // data for this item - std::string m_defdata; // default data for this item - std::string m_minimum; // minimum value - std::string m_maximum; // maximum value - }; - - // used internally in core_options - enum class condition_type +protected: + // This is a hook to allow option value retrieval to be overridden for various reasons; this is a crude + // extensibility mechanism that should really be replaced by something better + enum class override_get_value_result { NONE, - WARN, - ERR + OVERRIDE, + SKIP }; + virtual void value_changed(const std::string &name, const std::string &value) {} + virtual override_get_value_result override_get_value(const char *name, std::string &value) const { return override_get_value_result::NONE; } + virtual bool override_set_value(const char *name, const std::string &value) { return false; } + +private: // internal helpers - void add_to_entry_map(std::string &&name, entry::shared_ptr &entry); - void prettify_and_set_value(entry &curentry, std::string &&data, int priority, std::ostream &error_stream, condition_type &condition); - void throw_options_exception_if_appropriate(condition_type condition, std::ostringstream &error_stream); + void reset(); + void append_entry(entry &newentry); + void copyfrom(const core_options &src); + bool validate_and_set_data(entry &curentry, std::string &&newdata, int priority, std::string &error_string); // internal state - std::vector<entry::shared_ptr> m_entries; // cannonical list of entries - std::unordered_map<std::string, entry::weak_ptr> m_entrymap; // map for fast lookup - std::string m_command; // command found - static const char *const s_option_unadorned[]; // array of unadorned option "names" -}; - - -// static structure describing a single option with its description and default value -struct options_entry -{ - const char * name; // name on the command line - const char * defvalue; // default value of this argument - core_options::option_type type; // type of option - const char * description; // description for -showusage + simple_list<entry> m_entrylist; // head of list of entries + std::unordered_map<std::string,entry *> m_entrymap; // map for fast lookup + std::string m_command; // command found + static const char *const s_option_unadorned[]; // array of unadorned option "names" }; -// legacy option types -const core_options::option_type OPTION_INVALID = core_options::option_type::INVALID; -const core_options::option_type OPTION_HEADER = core_options::option_type::HEADER; -const core_options::option_type OPTION_COMMAND = core_options::option_type::COMMAND; -const core_options::option_type OPTION_BOOLEAN = core_options::option_type::BOOLEAN; -const core_options::option_type OPTION_INTEGER = core_options::option_type::INTEGER; -const core_options::option_type OPTION_FLOAT = core_options::option_type::FLOAT; -const core_options::option_type OPTION_STRING = core_options::option_type::STRING; #endif // MAME_LIB_UTIL_OPTIONS_H |
