diff options
Diffstat (limited to 'docs/release/src/osd/winui/win_options.cpp')
-rw-r--r-- | docs/release/src/osd/winui/win_options.cpp | 808 |
1 files changed, 808 insertions, 0 deletions
diff --git a/docs/release/src/osd/winui/win_options.cpp b/docs/release/src/osd/winui/win_options.cpp new file mode 100644 index 00000000000..c39f399e9b6 --- /dev/null +++ b/docs/release/src/osd/winui/win_options.cpp @@ -0,0 +1,808 @@ +// For licensing and usage information, read docs/winui_license.txt +//**************************************************************************** +/*************************************************************************** + + win_options.c + + Core options code code + +***************************************************************************/ + +#include <stdarg.h> +#include <stdlib.h> +#include <ctype.h> +#include "win_options.h" + + +//************************************************************************** +// REPLACE MACRO: option --> win_options +//************************************************************************** + +#define options win_options + + +//************************************************************************** +// GLOBAL VARIABLES +//************************************************************************** + +const char *const options::s_option_unadorned[MAX_UNADORNED_OPTIONS] = +{ + "<UNADORNED0>", + "<UNADORNED1>", + "<UNADORNED2>", + "<UNADORNED3>", + "<UNADORNED4>", + "<UNADORNED5>", + "<UNADORNED6>", + "<UNADORNED7>", + "<UNADORNED8>", + "<UNADORNED9>", + "<UNADORNED10>", + "<UNADORNED11>", + "<UNADORNED12>", + "<UNADORNED13>", + "<UNADORNED14>", + "<UNADORNED15>" +}; + + + +//************************************************************************** +// CORE OPTIONS ENTRY +//************************************************************************** + +//------------------------------------------------- +// entry - constructor +//------------------------------------------------- + +options::entry::entry(const char *name, const char *description, UINT32 flags, const char *defvalue) + : m_next(NULL), + m_flags(flags), + m_seqid(0), + m_error_reported(false), + m_priority(OPTION_PRIORITY_DEFAULT), + m_description(description) +{ + // copy in the name(s) as appropriate + if (name != nullptr) + { + // first extract any range + std::string namestr(name); + int lparen = namestr.find('('); + int dash = namestr.find(lparen + 1, '-'); + int rparen = namestr.find(dash + 1, ')'); + if (lparen != -1 && dash != -1 && rparen != -1) + { + m_minimum.assign(namestr.substr(lparen + 1, dash - (lparen + 1))); + strtrimspace(m_minimum); + m_maximum.assign(namestr.substr(dash + 1, rparen - (dash + 1))); + strtrimspace(m_maximum); + namestr.erase(lparen, rparen + 1 - lparen); + } + + // then chop up any semicolon-separated names + int semi; + int nameindex = 0; + while ((semi = namestr.find(';')) != -1 && nameindex < ARRAY_LENGTH(m_name)) + { + m_name[nameindex++].assign(namestr.substr(0, semi)); + namestr.erase(0, semi + 1); + } + + // finally add the last item + if (nameindex < ARRAY_LENGTH(m_name)) + m_name[nameindex++] = namestr; + } + + // set the default value + if (defvalue != nullptr) + m_defdata = defvalue; + m_data = m_defdata; +} + + +//------------------------------------------------- +// set_value - update our data value +//------------------------------------------------- + +void options::entry::set_value(const char *newdata, int priority) +{ + // ignore if we don't have priority + if (priority < m_priority) + return; + + // set the data and priority, then bump the sequence + m_data = newdata; + m_priority = priority; + m_seqid++; +} + + +//------------------------------------------------- +// set_default_value - set the default value of +// an option, and reset the current value to it +//------------------------------------------------- + +void options::entry::set_default_value(const char *defvalue) +{ + m_data = defvalue; + m_defdata = defvalue; + m_priority = OPTION_PRIORITY_DEFAULT; +} + + +//------------------------------------------------- +// set_description - set the description of +// an option +//------------------------------------------------- + +void options::entry::set_description(const char *description) +{ + m_description = description; +} + + +void options::entry::set_flag(UINT32 mask, UINT32 flag) +{ + m_flags = ( m_flags & mask ) | flag; +} + + +//------------------------------------------------- +// revert - revert back to our default if we are +// at or below the given priority +//------------------------------------------------- + +void options::entry::revert(int priority) +{ + // if our priority is low enough, revert to the default + if (m_priority <= priority) + { + m_data = m_defdata; + m_priority = OPTION_PRIORITY_DEFAULT; + } +} + + + +//************************************************************************** +// CORE OPTIONS +//************************************************************************** + +//------------------------------------------------- +// options - constructor +//------------------------------------------------- + +options::options() +{ +} + +options::options(const options_entry *entrylist) +{ + add_entries(entrylist); +} + +options::options(const options_entry *entrylist1, const options_entry *entrylist2) +{ + add_entries(entrylist1); + add_entries(entrylist2); +} + +options::options(const options_entry *entrylist1, const options_entry *entrylist2, const options_entry *entrylist3) +{ + add_entries(entrylist1); + add_entries(entrylist2); + add_entries(entrylist3); +} + +options::options(const options &src) +{ + copyfrom(src); +} + + +//------------------------------------------------- +// ~options - destructor +//------------------------------------------------- + +options::~options() +{ +} + + +//------------------------------------------------- +// operator= - assignment operator +//------------------------------------------------- + +options &options::operator=(const options &rhs) +{ + // ignore self-assignment + if (this != &rhs) + copyfrom(rhs); + return *this; +} + + +//------------------------------------------------- +// operator== - compare two sets of options +//------------------------------------------------- + +bool options::operator==(const options &rhs) +{ + // iterate over options in the first list + for (entry *curentry = m_entrylist.first(); curentry != nullptr; curentry = curentry->next()) + if (!curentry->is_header()) + { + // if the values differ, return false + if (strcmp(curentry->value(), rhs.value(curentry->name())) != 0) + return false; + } + + return true; +} + + +//------------------------------------------------- +// operator!= - compare two sets of options +//------------------------------------------------- + +bool options::operator!=(const options &rhs) +{ + return !operator==(rhs); +} + + +//------------------------------------------------- +// add_entry - add an entry to the current +// options set +//------------------------------------------------- + +void options::add_entry(const char *name, const char *description, UINT32 flags, const char *defvalue, bool override_existing) +{ + // allocate a new entry + entry *newentry = global_alloc(entry(name, description, flags, defvalue)); + if (newentry->name() != nullptr) + { + // 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) + 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; + } + } + } + + // add us to the list and maps + append_entry(*newentry); +} + + +//------------------------------------------------- +// add_entries - add entries to the current +// options sets +//------------------------------------------------- + +void options::add_entries(const options_entry *entrylist, bool override_existing) +{ + // loop over entries until we hit a NULL name + for (; entrylist->name != nullptr || (entrylist->flags & OPTION_HEADER) != 0; entrylist++) + add_entry(*entrylist, override_existing); +} + + +//------------------------------------------------- +// set_default_value - change the default value +// of an option +//------------------------------------------------- + +void 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 + curentry->second->set_default_value(defvalue); +} + + +//------------------------------------------------- +// set_description - change the description +// of an option +//------------------------------------------------- + +void 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 + curentry->second->set_description(description); +} + + +//------------------------------------------------- +// parse_command_line - parse a series of +// command line arguments +//------------------------------------------------- + +bool options::parse_command_line(int argc, char **argv, int priority, std::string &error_string) +{ + // reset the errors and the command + error_string.clear(); + m_command.clear(); + + // iterate through arguments + int unadorned_index = 0; + bool retval = true; + for (int arg = 1; arg < argc; arg++) + { + // determine the entry name to search for + const char *curarg = argv[arg]; + bool is_unadorned = (curarg[0] != '-'); + const char *optionname = is_unadorned ? options::unadorned(unadorned_index++) : &curarg[1]; + + // find our entry; if not found, indicate invalid option + auto curentry = m_entrymap.find(optionname); + if (curentry == m_entrymap.end()) + { + error_string.append(string_format("Error: unknown option: %s\n", curarg)); + retval = false; + if (!is_unadorned) arg++; + continue; + } + + // process commands first + if (curentry->second->type() == OPTION_COMMAND) + { + // can only have one command + if (!m_command.empty()) + { + error_string.append(string_format("Error: multiple commands specified -%s and %s\n", m_command.c_str(), curarg)); + return false; + } + m_command = curentry->second->name(); + continue; + } + + // get the data for this argument, special casing booleans + const char *newdata; + if (curentry->second->type() == OPTION_BOOLEAN) + newdata = (strncmp(&curarg[1], "no", 2) == 0) ? "0" : "1"; + else if (is_unadorned) + newdata = curarg; + else if (arg + 1 < argc) + newdata = argv[++arg]; + else + { + error_string.append(string_format("Error: option %s expected a parameter\n", curarg)); + return false; + } + + // set the new data + validate_and_set_data(*curentry->second, newdata, priority, error_string); + } + return retval; +} + + +//------------------------------------------------- +// parse_ini_file - parse a series of entries in +// an INI file +//------------------------------------------------- + +bool options::parse_ini_file(util::core_file &inifile, int priority, int ignore_priority, std::string &error_string) +{ + // loop over lines in the file + char buffer[4096]; + while (inifile.gets(buffer, ARRAY_LENGTH(buffer)) != nullptr) + { + // find the extent of the name + char *optionname; + for (optionname = buffer; *optionname != 0; optionname++) + if (!isspace((UINT8)*optionname)) + break; + + // skip comments + if (*optionname == 0 || *optionname == '#') + continue; + + // scan forward to find the first space + char *temp; + for (temp = optionname; *temp != 0; temp++) + if (isspace((UINT8)*temp)) + break; + + // if we hit the end early, print a warning and continue + if (*temp == 0) + { + error_string.append(string_format("Warning: invalid line in INI: %s", buffer)); + continue; + } + + // NULL-terminate + *temp++ = 0; + char *optiondata = temp; + + // scan the data, stopping when we hit a comment + bool inquotes = false; + for (temp = optiondata; *temp != 0; temp++) + { + if (*temp == '"') + inquotes = !inquotes; + if (*temp == '#' && !inquotes) + break; + } + *temp = 0; + + // find our entry + auto curentry = m_entrymap.find(optionname); + if (curentry == m_entrymap.end()) + { + if (priority >= ignore_priority) + error_string.append(string_format("Warning: unknown option in INI: %s\n", optionname)); + continue; + } + + // set the new data + validate_and_set_data(*curentry->second, optiondata, priority, error_string); + } + return true; +} + + +//------------------------------------------------- +// revert - revert options at or below a certain +// priority back to their defaults +//------------------------------------------------- + +void options::revert(int priority) +{ + // iterate over options and revert to defaults if below the given priority + for (entry *curentry = m_entrylist.first(); curentry != nullptr; curentry = curentry->next()) + curentry->revert(priority); +} + + +//------------------------------------------------- +// output_ini - output the options in INI format, +// only outputting entries that different from +// the optional diff +//------------------------------------------------- + +const char *options::output_ini(std::string &buffer, const options *diff) +{ + // INI files are complete, so always start with a blank buffer + buffer.clear(); + + int num_valid_headers = 0; + int unadorned_index = 0; + const char *last_header = NULL; + + // loop over all items + for (entry *curentry = m_entrylist.first(); curentry != nullptr; curentry = curentry->next()) + { + const char *name = curentry->name(); + const char *value = curentry->value(); + bool is_unadorned = false; + + // check if it's unadorned + if (name && strlen(name) && !strcmp(name, options::unadorned(unadorned_index))) + { + unadorned_index++; + is_unadorned = true; + } + + // header: record description + if (curentry->is_header()) + last_header = curentry->description(); + + // 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 == nullptr || strcmp(value, diff->value(name)) != 0) + { + // output header, if we have one + if (last_header) + { + if (num_valid_headers++) + buffer += '\n'; + buffer.append(string_format("#\n# %s\n#\n", last_header)); + last_header = nullptr; + } + + // and finally output the data, skip if unadorned + if (!is_unadorned) + { + if (strchr(value, ' ')) + buffer.append(string_format("%-25s \"%s\"\n", name, value)); + else + buffer.append(string_format("%-25s %s\n", name, value)); + } + } + } + } + } + return buffer.c_str(); +} + + +//------------------------------------------------- +// output_help - output option help to a string +//------------------------------------------------- + +const char *options::output_help(std::string &buffer) +{ + // start empty + buffer.clear(); + + // loop over all items + for (entry *curentry = m_entrylist.first(); curentry; curentry = curentry->next()) + { + // header: just print + if (curentry->is_header()) + buffer.append(string_format("\n#\n# %s\n#\n", curentry->description())); + + // otherwise, output entries for all non-deprecated items + else if (curentry->description()) + buffer.append(string_format("-%-20s%s\n", curentry->name(), curentry->description())); + } + return buffer.c_str(); +} + + +//------------------------------------------------- +// value - return the raw option value +//------------------------------------------------- + +const char *options::value(const char *name) const +{ + auto curentry = m_entrymap.find(name); + return (curentry != m_entrymap.end()) ? curentry->second->value() : ""; +} + + +//------------------------------------------------- +// description - return description of option +//------------------------------------------------- + +const char *options::description(const char *name) const +{ + auto curentry = m_entrymap.find(name); + return (curentry != m_entrymap.end()) ? curentry->second->description() : ""; +} + + +//------------------------------------------------- +// priority - return the priority of option +//------------------------------------------------- + +int options::priority(const char *name) const +{ + auto curentry = m_entrymap.find(name); + return (curentry != m_entrymap.end()) ? curentry->second->priority() : 0; +} + + +//------------------------------------------------- +// seqid - return the seqid for a given option +//------------------------------------------------- + +UINT32 options::seqid(const char *name) const +{ + auto curentry = m_entrymap.find(name); + return (curentry != m_entrymap.end()) ? curentry->second->seqid() : 0; +} + +//------------------------------------------------- +// exists - return if option exists in list +//------------------------------------------------- + +bool options::exists(const char *name) const +{ + return (m_entrymap.find(name) != m_entrymap.end()); +} + +//------------------------------------------------- +// set_value - set the raw option value +//------------------------------------------------- + +bool options::set_value(const char *name, const char *value, int priority, std::string &error_string) +{ + // 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; + } + + // validate and set the item normally + return validate_and_set_data(*curentry->second, value, priority, error_string); +} + +bool 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 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); +} + + +void options::set_flag(const char *name, UINT32 mask, UINT32 flag) +{ + // find the entry first + auto curentry = m_entrymap.find(name); + if ( curentry == m_entrymap.end()) + { + return; + } + curentry->second->set_flag(mask, flag); +} + + +//------------------------------------------------- +// reset - reset the options state, removing +// everything +//------------------------------------------------- + +void options::reset() +{ + m_entrylist.reset(); + m_entrymap.clear(); +} + + +//------------------------------------------------- +// append_entry - append an entry to our list +// and index it in the map +//------------------------------------------------- + +void options::append_entry(options::entry &newentry) +{ + 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)); + } +} + + +//------------------------------------------------- +// remove_entry - remove an entry from our list +// and map +//------------------------------------------------- + +void options::remove_entry(options::entry &delentry) +{ + // 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); +} + + +//------------------------------------------------- +// copyfrom - copy options from another set +//------------------------------------------------- + +void options::copyfrom(const options &src) +{ + // reset ourselves first + reset(); + + // iterate through the src options and make our own + for (entry *curentry = src.m_entrylist.first(); curentry != nullptr; curentry = curentry->next()) + append_entry(*global_alloc(entry(curentry->name(), curentry->description(), curentry->flags(), curentry->default_value()))); +} + + +//------------------------------------------------- +// validate_and_set_data - make sure the data is +// of the appropriate type and within range, +// then set it +//------------------------------------------------- + +bool options::validate_and_set_data(options::entry &curentry, const char *newdata, int priority, std::string &error_string) +{ + // trim any whitespace + std::string data(newdata); + strtrimspace(data); + + // trim quotes + if (data.find('"') == 0 && data.find_last_of('"') == data.length() - 1) + { + data.erase(0, 1); + data.erase(data.length() - 1, 1); + } + + // 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()))) + { + 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; + + // 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() && (fval < atof(curentry.minimum()) || 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); + return true; +} |