mirror of
https://github.com/holub/mame
synced 2025-05-07 06:44:51 +03:00

functionality in favor of alternate mechanisms. Errors are now reported via an astring rather than via callbacks. Every option must now specify a type (command, integer, float, string, boolean, etc). Command behavior has changed so that only one command is permitted. [Aaron Giles] Changed fileio system to accept just a raw searchpath instead of an options/option name combination. [Aaron Giles] Created emu_options class dervied from core_options which wraps core emulator options. Added mechanisms to cleanly change the system name and add/remove system-specific options, versus the old way using callbacks. Also added read accessors for all the options, to ensure consistency in how parameters are handled. Changed most core systems to access emu_options instead of core_options. Also changed machine->options() to return emu_options. [Aaron Giles] Created cli_options class derived from emu_options which adds the command-line specific options. Updated clifront code to leverage the new class and the new core behaviors. cli_execute() now accepts a cli_options object when called. [Aaron Giles] Updated both SDL and Windows to have their own options classes, derived from cli_options, which add the OSD-specific options on top of everything else. Added accessors for all the options so that queries are strongly typed and simplified. [Aaron Giles] Out of whatsnew: I've surely screwed up some stuff, though I have smoke tested a bunch of things. Let me know if you hit anything odd. Also I know this change will impact the WINUI stuff, please let me know if there are issues. All the functionality necessary should still be present. If it's not obvious, please talk to me before adding stuff to the core_options class.
755 lines
21 KiB
C
755 lines
21 KiB
C
/***************************************************************************
|
|
|
|
options.c
|
|
|
|
Core options code code
|
|
|
|
****************************************************************************
|
|
|
|
Copyright Aaron Giles
|
|
All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions are
|
|
met:
|
|
|
|
* Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
* Redistributions in binary form must reproduce the above copyright
|
|
notice, this list of conditions and the following disclaimer in
|
|
the documentation and/or other materials provided with the
|
|
distribution.
|
|
* Neither the name 'MAME' nor the names of its contributors may be
|
|
used to endorse or promote products derived from this software
|
|
without specific prior written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR
|
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT,
|
|
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
|
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
***************************************************************************/
|
|
|
|
#include <stdarg.h>
|
|
#include <stdlib.h>
|
|
#include <ctype.h>
|
|
#include "options.h"
|
|
#include "astring.h"
|
|
|
|
|
|
|
|
//**************************************************************************
|
|
// GLOBAL VARIABLES
|
|
//**************************************************************************
|
|
|
|
const char *const core_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
|
|
//-------------------------------------------------
|
|
|
|
core_options::entry::entry(const options_entry &entrylist)
|
|
: m_next(NULL),
|
|
m_flags(entrylist.flags),
|
|
m_seqid(0),
|
|
m_error_reported(false),
|
|
m_priority(OPTION_PRIORITY_DEFAULT),
|
|
m_description(entrylist.description)
|
|
{
|
|
// copy in the name(s) as appropriate
|
|
if (entrylist.name != NULL)
|
|
{
|
|
// first extract any range
|
|
astring namestr(entrylist.name);
|
|
int lparen = namestr.chr(0, '(');
|
|
int dash = namestr.chr(lparen + 1, '-');
|
|
int rparen = namestr.chr(dash + 1, ')');
|
|
if (lparen != -1 && dash != -1 && rparen != -1)
|
|
{
|
|
m_minimum.cpysubstr(namestr, lparen + 1, dash - (lparen + 1)).trimspace();
|
|
m_maximum.cpysubstr(namestr, dash + 1, rparen - (dash + 1)).trimspace();
|
|
namestr.del(lparen, rparen + 1 - lparen);
|
|
}
|
|
|
|
// then chop up any semicolon-separated names
|
|
int semi;
|
|
int nameindex = 0;
|
|
while ((semi = namestr.chr(0, ';')) != -1 && nameindex < ARRAY_LENGTH(m_name))
|
|
{
|
|
m_name[nameindex++].cpysubstr(namestr, 0, semi);
|
|
namestr.del(0, semi + 1);
|
|
}
|
|
|
|
// finally add the last item
|
|
if (nameindex < ARRAY_LENGTH(m_name))
|
|
m_name[nameindex++] = namestr;
|
|
}
|
|
|
|
// set the default value
|
|
if (entrylist.defvalue != NULL)
|
|
m_defdata = entrylist.defvalue;
|
|
m_data = m_defdata;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// set_value - update our data value
|
|
//-------------------------------------------------
|
|
|
|
void core_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 core_options::entry::set_default_value(const char *defvalue)
|
|
{
|
|
m_data = defvalue;
|
|
m_defdata = defvalue;
|
|
m_priority = OPTION_PRIORITY_DEFAULT;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// revert - revert back to our default if we are
|
|
// at or below the given priority
|
|
//-------------------------------------------------
|
|
|
|
void core_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
|
|
//**************************************************************************
|
|
|
|
//-------------------------------------------------
|
|
// core_options - constructor
|
|
//-------------------------------------------------
|
|
|
|
core_options::core_options()
|
|
: m_entrylist(NULL),
|
|
m_entrylist_tailptr(&m_entrylist)
|
|
{
|
|
}
|
|
|
|
core_options::core_options(const options_entry *entrylist)
|
|
: m_entrylist(NULL),
|
|
m_entrylist_tailptr(&m_entrylist)
|
|
{
|
|
add_entries(entrylist);
|
|
}
|
|
|
|
core_options::core_options(const options_entry *entrylist1, const options_entry *entrylist2)
|
|
: m_entrylist(NULL),
|
|
m_entrylist_tailptr(&m_entrylist)
|
|
{
|
|
add_entries(entrylist1);
|
|
add_entries(entrylist2);
|
|
}
|
|
|
|
core_options::core_options(const options_entry *entrylist1, const options_entry *entrylist2, const options_entry *entrylist3)
|
|
: m_entrylist(NULL),
|
|
m_entrylist_tailptr(&m_entrylist)
|
|
{
|
|
add_entries(entrylist1);
|
|
add_entries(entrylist2);
|
|
add_entries(entrylist3);
|
|
}
|
|
|
|
core_options::core_options(const core_options &src)
|
|
: m_entrylist(NULL),
|
|
m_entrylist_tailptr(&m_entrylist)
|
|
{
|
|
copyfrom(src);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// ~core_options - destructor
|
|
//-------------------------------------------------
|
|
|
|
core_options::~core_options()
|
|
{
|
|
// delete all entries from the list
|
|
while (m_entrylist != NULL)
|
|
remove_entry(*m_entrylist);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// operator= - assignment operator
|
|
//-------------------------------------------------
|
|
|
|
core_options &core_options::operator=(const core_options &rhs)
|
|
{
|
|
// ignore self-assignment
|
|
if (this != &rhs)
|
|
copyfrom(rhs);
|
|
return *this;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// operator== - compare two sets of options
|
|
//-------------------------------------------------
|
|
|
|
bool core_options::operator==(const core_options &rhs)
|
|
{
|
|
// iterate over options in the first list
|
|
for (entry *curentry = m_entrylist; curentry != NULL; curentry = curentry->next())
|
|
if (!curentry->is_header())
|
|
{
|
|
// if the values differ, return false
|
|
if (strcmp(curentry->m_data, rhs.value(curentry->name())) != 0)
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// operator!= - compare two sets of options
|
|
//-------------------------------------------------
|
|
|
|
bool core_options::operator!=(const core_options &rhs)
|
|
{
|
|
return !operator==(rhs);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// add_entries - add entries to the current
|
|
// options sets
|
|
//-------------------------------------------------
|
|
|
|
void core_options::add_entries(const options_entry *entrylist, bool override_existing)
|
|
{
|
|
// loop over entries until we hit a NULL name
|
|
for ( ; entrylist->name != NULL || (entrylist->flags & OPTION_HEADER) != 0; entrylist++)
|
|
{
|
|
// allocate a new entry
|
|
entry *newentry = new entry(*entrylist);
|
|
if (newentry->name() != NULL)
|
|
{
|
|
// see if we match an existing entry
|
|
entry *existing = m_entrymap.find(newentry->name());
|
|
if (existing != NULL)
|
|
{
|
|
// if we're overriding existing entries, then remove the old one
|
|
if (override_existing)
|
|
remove_entry(*existing);
|
|
|
|
// otherwise, just override the default and current values and throw out the new entry
|
|
else
|
|
{
|
|
existing->set_default_value(newentry->value());
|
|
delete newentry;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
// add us to the list and maps
|
|
append_entry(*newentry);
|
|
}
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// set_default_value - change the default value
|
|
// of an option
|
|
//-------------------------------------------------
|
|
|
|
void core_options::set_default_value(const char *name, const char *defvalue)
|
|
{
|
|
// find the entry and bail if we can't
|
|
entry *curentry = m_entrymap.find(name);
|
|
if (curentry == NULL)
|
|
return;
|
|
|
|
// update the data and default data
|
|
curentry->set_default_value(defvalue);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// parse_command_line - parse a series of
|
|
// command line arguments
|
|
//-------------------------------------------------
|
|
|
|
bool core_options::parse_command_line(int argc, char **argv, int priority, astring &error_string)
|
|
{
|
|
// reset the errors and the command
|
|
error_string.reset();
|
|
m_command.reset();
|
|
|
|
// iterate through arguments
|
|
int unadorned_index = 0;
|
|
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 ? core_options::unadorned(unadorned_index++) : &curarg[1];
|
|
|
|
// find our entry; if not found, indicate invalid option
|
|
entry *curentry = m_entrymap.find(optionname);
|
|
if (curentry == NULL)
|
|
{
|
|
error_string.catprintf("Error: unknown option: %s\n", curarg);
|
|
return false;
|
|
}
|
|
|
|
// process commands first
|
|
if (curentry->type() == OPTION_COMMAND)
|
|
{
|
|
// can only have one command
|
|
if (m_command)
|
|
{
|
|
error_string.catprintf("Error: multiple commands specified -%s and %s\n", m_command.cstr(), curarg);
|
|
return false;
|
|
}
|
|
m_command = curentry->name();
|
|
continue;
|
|
}
|
|
|
|
// get the data for this argument, special casing booleans
|
|
const char *newdata;
|
|
if (curentry->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.catprintf("Error: option %s expected a parameter\n", curarg);
|
|
return false;
|
|
}
|
|
|
|
// set the new data
|
|
validate_and_set_data(*curentry, newdata, priority, error_string);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// parse_ini_file - parse a series of entries in
|
|
// an INI file
|
|
//-------------------------------------------------
|
|
|
|
bool core_options::parse_ini_file(core_file &inifile, int priority, int ignore_priority, astring &error_string)
|
|
{
|
|
// loop over lines in the file
|
|
char buffer[4096];
|
|
while (core_fgets(buffer, ARRAY_LENGTH(buffer), &inifile) != NULL)
|
|
{
|
|
// 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.catprintf("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
|
|
entry *curentry = m_entrymap.find(optionname);
|
|
if (curentry == NULL)
|
|
{
|
|
if (priority >= ignore_priority)
|
|
error_string.catprintf("Warning: unknown option in INI: %s\n", optionname);
|
|
continue;
|
|
}
|
|
|
|
// set the new data
|
|
validate_and_set_data(*curentry, optiondata, priority, error_string);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// revert - revert options at or below a certain
|
|
// priority back to their defaults
|
|
//-------------------------------------------------
|
|
|
|
void core_options::revert(int priority)
|
|
{
|
|
// iterate over options and revert to defaults if below the given priority
|
|
for (entry *curentry = m_entrylist; curentry != NULL; 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 *core_options::output_ini(astring &buffer, const core_options *diff)
|
|
{
|
|
// INI files are complete, so always start with a blank buffer
|
|
buffer.reset();
|
|
|
|
// loop over all items
|
|
const char *last_header = NULL;
|
|
for (entry *curentry = m_entrylist; curentry != NULL; curentry = curentry->next())
|
|
{
|
|
// header: record description
|
|
if (curentry->is_header())
|
|
last_header = curentry->description();
|
|
|
|
// otherwise, output entries for all non-command items
|
|
else if (!curentry->is_command())
|
|
{
|
|
// look up counterpart in diff, if diff is specified
|
|
const char *name = curentry->name();
|
|
const char *value = curentry->value();
|
|
if (diff == NULL || strcmp(value, diff->value(name)) != 0)
|
|
{
|
|
// output header, if we have one
|
|
if (last_header != NULL)
|
|
{
|
|
buffer.catprintf("\n#\n# %s\n#\n", last_header);
|
|
last_header = NULL;
|
|
}
|
|
|
|
// and finally output the data
|
|
if (strchr(value, ' ') != NULL)
|
|
buffer.catprintf("%-25s \"%s\"\n", name, value);
|
|
else
|
|
buffer.catprintf("%-25s %s\n", name, value);
|
|
}
|
|
}
|
|
}
|
|
return buffer;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// output_help - output option help to a string
|
|
//-------------------------------------------------
|
|
|
|
const char *core_options::output_help(astring &buffer)
|
|
{
|
|
// start empty
|
|
buffer.reset();
|
|
|
|
// loop over all items
|
|
for (entry *curentry = m_entrylist; curentry != NULL; curentry = curentry->next())
|
|
{
|
|
// header: just print
|
|
if (curentry->is_header())
|
|
buffer.catprintf("\n#\n# %s\n#\n", curentry->description());
|
|
|
|
// otherwise, output entries for all non-deprecated items
|
|
else if (curentry->description() != NULL)
|
|
buffer.catprintf("-%-20s%s\n", curentry->name(), curentry->description());
|
|
}
|
|
return buffer;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// value - return the raw option value
|
|
//-------------------------------------------------
|
|
|
|
const char *core_options::value(const char *name) const
|
|
{
|
|
entry *curentry = m_entrymap.find(name);
|
|
return (curentry != NULL) ? curentry->value() : "";
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// seqid - return the seqid for a given option
|
|
//-------------------------------------------------
|
|
|
|
UINT32 core_options::seqid(const char *name) const
|
|
{
|
|
entry *curentry = m_entrymap.find(name);
|
|
return (curentry != NULL) ? curentry->seqid() : 0;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// set_value - set the raw option value
|
|
//-------------------------------------------------
|
|
|
|
bool core_options::set_value(const char *name, const char *value, int priority, astring &error_string)
|
|
{
|
|
// find the entry first
|
|
entry *curentry = m_entrymap.find(name);
|
|
if (curentry == NULL)
|
|
{
|
|
error_string.catprintf("Attempted to set unknown option %s\n", name);
|
|
return false;
|
|
}
|
|
|
|
// validate and set the item normally
|
|
return validate_and_set_data(*curentry, value, priority, error_string);
|
|
}
|
|
|
|
bool core_options::set_value(const char *name, int value, int priority, astring &error_string)
|
|
{
|
|
astring tempstr;
|
|
tempstr.printf("%d", value);
|
|
return set_value(name, tempstr.cstr(), priority, error_string);
|
|
}
|
|
|
|
bool core_options::set_value(const char *name, float value, int priority, astring &error_string)
|
|
{
|
|
astring tempstr;
|
|
tempstr.printf("%f", value);
|
|
return set_value(name, tempstr.cstr(), priority, error_string);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// reset - reset the options state, removing
|
|
// everything
|
|
//-------------------------------------------------
|
|
|
|
void core_options::reset()
|
|
{
|
|
// remove all entries from the list
|
|
while (m_entrylist != NULL)
|
|
remove_entry(*m_entrylist);
|
|
|
|
// reset the map
|
|
m_entrymap.reset();
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// append_entry - append an entry to our list
|
|
// and index it in the map
|
|
//-------------------------------------------------
|
|
|
|
void core_options::append_entry(core_options::entry &newentry)
|
|
{
|
|
// append to the list
|
|
*m_entrylist_tailptr = &newentry;
|
|
m_entrylist_tailptr = &newentry.m_next;
|
|
|
|
// if we have names, add them to the map
|
|
for (int name = 0; name < ARRAY_LENGTH(newentry.m_name); name++)
|
|
if (newentry.m_name[name])
|
|
m_entrymap.add(newentry.m_name[name], &newentry);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// remove_entry - remove an entry from our list
|
|
// and map
|
|
//-------------------------------------------------
|
|
|
|
void core_options::remove_entry(core_options::entry &delentry)
|
|
{
|
|
// remove us from the list
|
|
entry *preventry = NULL;
|
|
for (entry *curentry = m_entrylist; curentry != NULL; curentry = curentry->next())
|
|
if (curentry == &delentry)
|
|
{
|
|
// update link from previous to us
|
|
if (preventry != NULL)
|
|
preventry->m_next = delentry.m_next;
|
|
else
|
|
m_entrylist = delentry.m_next;
|
|
|
|
// if we're the last item, update the next pointer
|
|
if (delentry.m_next == NULL)
|
|
{
|
|
if (preventry != NULL)
|
|
m_entrylist_tailptr = &preventry->m_next;
|
|
else
|
|
m_entrylist_tailptr = &m_entrylist;
|
|
}
|
|
|
|
// remove all entries from the map
|
|
for (int name = 0; name < ARRAY_LENGTH(delentry.m_name); name++)
|
|
if (delentry.m_name[name])
|
|
m_entrymap.remove(delentry.m_name[name]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// copyfrom - copy options from another set
|
|
//-------------------------------------------------
|
|
|
|
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; curentry != NULL; curentry = curentry->next())
|
|
append_entry(*new entry(*curentry));
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// validate_and_set_data - make sure the data is
|
|
// of the appropriate type and within range,
|
|
// then set it
|
|
//-------------------------------------------------
|
|
|
|
bool core_options::validate_and_set_data(core_options::entry &curentry, const char *newdata, int priority, astring &error_string)
|
|
{
|
|
// trim any whitespace
|
|
astring data(newdata);
|
|
data.trimspace();
|
|
|
|
// trim quotes
|
|
if (data.chr(0, '"') == 0 && data.rchr(0, '"') == data.len() - 1)
|
|
{
|
|
data.del(0, 1);
|
|
data.del(data.len() - 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, "%d", &ival) != 1 || ival < 0 || ival > 1)
|
|
{
|
|
error_string.catprintf("Illegal boolean value for %s: \"%s\"; reverting to %s\n", curentry.name(), data.cstr(), curentry.value());
|
|
return false;
|
|
}
|
|
break;
|
|
|
|
// integers must be integral
|
|
case OPTION_INTEGER:
|
|
if (sscanf(data, "%d", &ival) != 1)
|
|
{
|
|
error_string.catprintf("Illegal integer value for %s: \"%s\"; reverting to %s\n", curentry.name(), data.cstr(), curentry.value());
|
|
return false;
|
|
}
|
|
if (curentry.has_range() && (ival < atoi(curentry.minimum()) || ival > atoi(curentry.maximum())))
|
|
{
|
|
error_string.catprintf("Out-of-range integer value for %s: \"%s\" (must be between %s and %s); reverting to %s\n", curentry.name(), data.cstr(), curentry.minimum(), curentry.maximum(), curentry.value());
|
|
return false;
|
|
}
|
|
break;
|
|
|
|
// floating-point values must be numeric
|
|
case OPTION_FLOAT:
|
|
if (sscanf(data, "%f", &fval) != 1)
|
|
{
|
|
error_string.catprintf("Illegal float value for %s: \"%s\"; reverting to %s\n", curentry.name(), data.cstr(), curentry.value());
|
|
return false;
|
|
}
|
|
if (curentry.has_range() && (fval < atof(curentry.minimum()) || fval > atof(curentry.maximum())))
|
|
{
|
|
error_string.catprintf("Out-of-range float value for %s: \"%s\" (must be between %s and %s); reverting to %s\n", curentry.name(), data.cstr(), 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.catprintf("Attempted to set invalid option %s\n", curentry.name());
|
|
return false;
|
|
}
|
|
|
|
// set the data
|
|
curentry.set_value(data, priority);
|
|
return true;
|
|
}
|