694 lines
16 KiB
C++
694 lines
16 KiB
C++
#include <iostream>
|
|
#include <fstream>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "../../types.h"
|
|
#include "configSys.h"
|
|
|
|
std::string cfgFile = "fceux.cfg";
|
|
/**
|
|
* Add a given option. The option is specified as a short command
|
|
* line (-f), long command line (--foo), option name (Foo), its type
|
|
* (integer or string).
|
|
*/
|
|
int
|
|
Config::_addOption(char shortArg,
|
|
const std::string &longArg,
|
|
const std::string &name,
|
|
int type)
|
|
{
|
|
// make sure we have a valid type
|
|
if(type != INTEGER && type != STRING &&
|
|
type != DOUBLE && type != FUNCTION) {
|
|
return -1;
|
|
}
|
|
|
|
// check if the option already exists
|
|
if(_shortArgMap.find(shortArg) != _shortArgMap.end() ||
|
|
_longArgMap.find(longArg) != _longArgMap.end() ||
|
|
(type == INTEGER && _intOptMap.find(name) != _intOptMap.end()) ||
|
|
(type == STRING && _strOptMap.find(name) != _strOptMap.end()) ||
|
|
(type == DOUBLE && _dblOptMap.find(name) != _dblOptMap.end())) {
|
|
return -1;
|
|
}
|
|
|
|
// add the option
|
|
switch(type) {
|
|
case(STRING):
|
|
_strOptMap[name] = "";
|
|
break;
|
|
case(INTEGER):
|
|
_intOptMap[name] = 0;
|
|
break;
|
|
case(DOUBLE):
|
|
_dblOptMap[name] = 0.0;
|
|
break;
|
|
case(FUNCTION):
|
|
_fnOptMap[name] = NULL;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
_shortArgMap[shortArg] = name;
|
|
_longArgMap[longArg] = name;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
Config::_addOption(const std::string &longArg,
|
|
const std::string &name,
|
|
int type)
|
|
{
|
|
// make sure we have a valid type
|
|
if(type != STRING && type != INTEGER && type != DOUBLE) {
|
|
return -1;
|
|
}
|
|
|
|
// check if the option already exists
|
|
if(_longArgMap.find(longArg) != _longArgMap.end() ||
|
|
(type == STRING && _strOptMap.find(name) != _strOptMap.end()) ||
|
|
(type == INTEGER && _intOptMap.find(name) != _intOptMap.end()) ||
|
|
(type == DOUBLE && _dblOptMap.find(name) != _dblOptMap.end())) {
|
|
return -1;
|
|
}
|
|
|
|
// add the option
|
|
switch(type) {
|
|
case(STRING):
|
|
_strOptMap[name] = "";
|
|
break;
|
|
case(INTEGER):
|
|
_intOptMap[name] = 0;
|
|
break;
|
|
case(DOUBLE):
|
|
_dblOptMap[name] = 0.0;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
_longArgMap[longArg] = name;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* Add a given option and sets its default value. The option is
|
|
* specified as a short command line (-f), long command line (--foo),
|
|
* option name (Foo), its type (integer or string), and its default
|
|
* value.
|
|
*/
|
|
int
|
|
Config::addOption(char shortArg,
|
|
const std::string &longArg,
|
|
const std::string &name,
|
|
int defaultValue)
|
|
{
|
|
int error;
|
|
|
|
// add the option to the config system
|
|
error = _addOption(shortArg, longArg, name, INTEGER);
|
|
if(error) {
|
|
return error;
|
|
}
|
|
|
|
// set the option to the default value
|
|
error = setOption(name, defaultValue);
|
|
if(error) {
|
|
return error;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Add a given option and sets its default value. The option is
|
|
* specified as a short command line (-f), long command line (--foo),
|
|
* option name (Foo), its type (integer or string), and its default
|
|
* value.
|
|
*/
|
|
int
|
|
Config::addOption(char shortArg,
|
|
const std::string &longArg,
|
|
const std::string &name,
|
|
double defaultValue)
|
|
{
|
|
int error;
|
|
|
|
// add the option to the config system
|
|
error = _addOption(shortArg, longArg, name, DOUBLE);
|
|
if(error) {
|
|
return error;
|
|
}
|
|
|
|
// set the option to the default value
|
|
error = setOption(name, defaultValue);
|
|
if(error) {
|
|
return error;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* Add a given option and sets its default value. The option is
|
|
* specified as a short command line (-f), long command line (--foo),
|
|
* option name (Foo), its type (integer or string), and its default
|
|
* value.
|
|
*/
|
|
int
|
|
Config::addOption(char shortArg,
|
|
const std::string &longArg,
|
|
const std::string &name,
|
|
const std::string &defaultValue)
|
|
{
|
|
int error;
|
|
|
|
// add the option to the config system
|
|
error = _addOption(shortArg, longArg, name, STRING);
|
|
if(error) {
|
|
return error;
|
|
}
|
|
|
|
// set the option to the default value
|
|
error = setOption(name, defaultValue);
|
|
if(error) {
|
|
return error;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
Config::addOption(char shortArg,
|
|
const std::string &longArg,
|
|
const std::string &name,
|
|
void (*defaultFn)(const std::string &))
|
|
{
|
|
int error;
|
|
|
|
// add the option to the config system
|
|
error = _addOption(shortArg, longArg, name, FUNCTION);
|
|
if(error) {
|
|
return error;
|
|
}
|
|
|
|
// set the option to the default value
|
|
error = setOption(name, defaultFn);
|
|
if(error) {
|
|
return error;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
Config::addOption(const std::string &longArg,
|
|
const std::string &name,
|
|
const std::string &defaultValue)
|
|
{
|
|
int error;
|
|
|
|
// add the option to the config system
|
|
error = _addOption(longArg, name, STRING);
|
|
if(error) {
|
|
return error;
|
|
}
|
|
|
|
// set the option to the default value
|
|
error = setOption(name, defaultValue);
|
|
if(error) {
|
|
return error;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
Config::addOption(const std::string &longArg,
|
|
const std::string &name,
|
|
int defaultValue)
|
|
{
|
|
int error;
|
|
|
|
// add the option to the config system
|
|
error = _addOption(longArg, name, INTEGER);
|
|
if(error) {
|
|
return error;
|
|
}
|
|
|
|
// set the option to the default value
|
|
error = setOption(name, defaultValue);
|
|
if(error) {
|
|
return error;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
Config::addOption(const std::string &longArg,
|
|
const std::string &name,
|
|
double defaultValue)
|
|
{
|
|
int error;
|
|
|
|
// add the option to the config system
|
|
error = _addOption(longArg, name, DOUBLE);
|
|
if(error) {
|
|
return error;
|
|
}
|
|
|
|
// set the option to the default value
|
|
error = setOption(name, defaultValue);
|
|
if(error) {
|
|
return error;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
Config::addOption(const std::string &name,
|
|
const std::string &defaultValue)
|
|
{
|
|
if(_strOptMap.find(name) != _strOptMap.end()) {
|
|
return -1;
|
|
}
|
|
|
|
// add the option
|
|
_strOptMap[name] = defaultValue;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
Config::addOption(const std::string &name,
|
|
int defaultValue)
|
|
{
|
|
if(_intOptMap.find(name) != _intOptMap.end()) {
|
|
return -1;
|
|
}
|
|
|
|
// add the option
|
|
_intOptMap[name] = defaultValue;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
Config::addOption(const std::string &name,
|
|
double defaultValue)
|
|
{
|
|
if(_dblOptMap.find(name) != _dblOptMap.end()) {
|
|
return -1;
|
|
}
|
|
|
|
// add the option
|
|
_dblOptMap[name] = defaultValue;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Sets the specified option to the given integer value.
|
|
*/
|
|
int
|
|
Config::setOption(const std::string &name,
|
|
int value)
|
|
{
|
|
std::map<std::string, int>::iterator opt_i;
|
|
|
|
// confirm that the option exists
|
|
opt_i = _intOptMap.find(name);
|
|
if(opt_i == _intOptMap.end()) {
|
|
return -1;
|
|
}
|
|
|
|
// set the option
|
|
opt_i->second = value;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Sets the specified option to the given integer value.
|
|
*/
|
|
int
|
|
Config::setOption(const std::string &name,
|
|
double value)
|
|
{
|
|
std::map<std::string, double>::iterator opt_i;
|
|
|
|
// confirm that the option exists
|
|
opt_i = _dblOptMap.find(name);
|
|
if(opt_i == _dblOptMap.end()) {
|
|
return -1;
|
|
}
|
|
|
|
// set the option
|
|
opt_i->second = value;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Sets the specified option to the given string value.
|
|
*/
|
|
int
|
|
Config::setOption(const std::string &name,
|
|
const std::string &value)
|
|
{
|
|
std::map<std::string, std::string>::iterator opt_i;
|
|
|
|
// confirm that the option exists
|
|
opt_i = _strOptMap.find(name);
|
|
if(opt_i == _strOptMap.end()) {
|
|
return -1;
|
|
}
|
|
|
|
// set the option
|
|
opt_i->second = value;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Sets the specified option to the given function.
|
|
*/
|
|
int
|
|
Config::setOption(const std::string &name,
|
|
void (*value)(const std::string &))
|
|
{
|
|
std::map<std::string, void (*)(const std::string &)>::iterator opt_i;
|
|
|
|
// confirm that the option exists
|
|
opt_i = _fnOptMap.find(name);
|
|
if(opt_i == _fnOptMap.end()) {
|
|
return -1;
|
|
}
|
|
|
|
// set the option
|
|
opt_i->second = value;
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
Config::getOption(const std::string &name,
|
|
std::string *value)
|
|
{
|
|
std::map<std::string, std::string>::iterator opt_i;
|
|
|
|
// confirm that the option exists
|
|
opt_i = _strOptMap.find(name);
|
|
if(opt_i == _strOptMap.end()) {
|
|
return -1;
|
|
}
|
|
|
|
// get the option
|
|
(*value) = opt_i->second;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
Config::getOption(const std::string &name,
|
|
const char **value)
|
|
{
|
|
std::map<std::string, std::string>::iterator opt_i;
|
|
|
|
// confirm that the option exists
|
|
opt_i = _strOptMap.find(name);
|
|
if(opt_i == _strOptMap.end()) {
|
|
return -1;
|
|
}
|
|
|
|
// get the option
|
|
(*value) = opt_i->second.c_str();
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
Config::getOption(const std::string &name,
|
|
int *value)
|
|
{
|
|
std::map<std::string, int>::iterator opt_i;
|
|
|
|
// confirm that the option exists
|
|
opt_i = _intOptMap.find(name);
|
|
if(opt_i == _intOptMap.end()) {
|
|
return -1;
|
|
}
|
|
|
|
// get the option
|
|
(*value) = opt_i->second;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
Config::getOption(const std::string &name,
|
|
double *value)
|
|
{
|
|
std::map<std::string, double>::iterator opt_i;
|
|
|
|
// confirm that the option exists
|
|
opt_i = _dblOptMap.find(name);
|
|
if(opt_i == _dblOptMap.end()) {
|
|
return -1;
|
|
}
|
|
|
|
// get the option
|
|
(*value) = opt_i->second;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Parses the command line arguments. Short args are of the form -f
|
|
* <opt>, long args are of the form --foo <opt>. Returns < 0 on error,
|
|
* or the index of the rom file in argv.
|
|
*/
|
|
int
|
|
Config::_parseArgs(int argc,
|
|
char **argv)
|
|
{
|
|
int retval = 0;
|
|
std::map<std::string, std::string>::iterator long_i, str_i;
|
|
std::map<char, std::string>::iterator short_i;
|
|
std::map<std::string, int>::iterator int_i;
|
|
std::map<std::string, double>::iterator dbl_i;
|
|
std::map<std::string, void (*)(const std::string &)>::iterator fn_i;
|
|
std::string arg, opt, value;
|
|
|
|
for(int i = 1; i < argc; i++) {
|
|
arg = argv[i];
|
|
if(arg[0] != '-') {
|
|
// must be a rom name?
|
|
retval = i;
|
|
continue;
|
|
}
|
|
|
|
if(arg.size() < 2) {
|
|
// XXX invalid argument
|
|
return -1;
|
|
}
|
|
|
|
// parse the argument and get the option name
|
|
if(arg[1] == '-') {
|
|
// long arg
|
|
long_i = _longArgMap.find(arg.substr(2));
|
|
if(long_i == _longArgMap.end()) {
|
|
// XXX invalid argument
|
|
return -1;
|
|
}
|
|
|
|
opt = long_i->second;
|
|
} else {
|
|
// short arg
|
|
short_i = _shortArgMap.find(arg[1]);
|
|
if(short_i == _shortArgMap.end()) {
|
|
// XXX invalid argument
|
|
return -1;
|
|
}
|
|
|
|
opt = short_i->second;
|
|
}
|
|
|
|
// make sure we've got a value
|
|
if(i + 1 >= argc) {
|
|
// XXX missing value
|
|
return -1;
|
|
}
|
|
i++;
|
|
|
|
// now, find the appropriate option entry, and update it
|
|
str_i = _strOptMap.find(opt);
|
|
int_i = _intOptMap.find(opt);
|
|
dbl_i = _dblOptMap.find(opt);
|
|
fn_i = _fnOptMap.find(opt);
|
|
if(str_i != _strOptMap.end()) {
|
|
str_i->second = argv[i];
|
|
} else if(int_i != _intOptMap.end()) {
|
|
int_i->second = atol(argv[i]);
|
|
} else if(dbl_i != _dblOptMap.end()) {
|
|
dbl_i->second = atof(argv[i]);
|
|
} else if(fn_i != _fnOptMap.end()) {
|
|
(*(fn_i->second))(argv[i]);
|
|
} else {
|
|
// XXX invalid option? shouldn't happen
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
// if we didn't get a rom-name, return error
|
|
return (retval) ? retval : -1;
|
|
}
|
|
|
|
|
|
/**
|
|
* Parses first the configuration file, and then overrides with any
|
|
* command-line options that were specified.
|
|
*/
|
|
int
|
|
Config::parse(int argc,
|
|
char **argv)
|
|
{
|
|
int error;
|
|
|
|
// read the config file
|
|
error = _load();
|
|
if(error) {
|
|
return error;
|
|
}
|
|
|
|
// parse the arguments
|
|
return _parseArgs(argc, argv);
|
|
}
|
|
|
|
|
|
/**
|
|
* Read each line of the config file and put the variables into the
|
|
* config maps. Valid configuration lines are of the form:
|
|
*
|
|
* <option name> = <option value>
|
|
*
|
|
* Lines beginning with # are ignored.
|
|
*/
|
|
char* Config::getConfigDirectory()
|
|
{
|
|
return strdup(_dir.c_str());
|
|
}
|
|
|
|
int
|
|
Config::_load()
|
|
{
|
|
signed int pos, eqPos;
|
|
std::fstream config;
|
|
std::map<std::string, int>::iterator int_i;
|
|
std::map<std::string, double>::iterator dbl_i;
|
|
std::map<std::string, std::string>::iterator str_i;
|
|
std::string configFile = _dir + "/" + cfgFile;
|
|
std::string line, name, value;
|
|
char buf[1024];
|
|
|
|
// set the exception handling to catch i/o errors
|
|
config.exceptions(std::fstream::badbit);
|
|
|
|
try {
|
|
// open the file for reading (create if it doesn't exist)
|
|
config.open(configFile.c_str(), std::ios::in | std::ios::out);
|
|
if(!config.is_open()) {
|
|
// XXX file couldn't be opened?
|
|
return 0;
|
|
}
|
|
|
|
while(!config.eof()) {
|
|
// read a line
|
|
config.getline(buf, 1024);
|
|
line = buf;
|
|
|
|
// check line validity
|
|
eqPos = line.find("=");
|
|
if(line[0] == '#') {
|
|
// skip this line
|
|
continue;
|
|
}
|
|
|
|
// get the name and value for the option
|
|
pos = line.find(" ");
|
|
name = line.substr(0, (pos > eqPos) ? eqPos : pos);
|
|
pos = line.find_first_not_of(" ", eqPos + 1);
|
|
if (pos == std::string::npos) value = "";
|
|
else value = line.substr(pos);
|
|
|
|
// check if the option exists, and if so, set it appropriately
|
|
str_i = _strOptMap.find(name);
|
|
dbl_i = _dblOptMap.find(name);
|
|
int_i = _intOptMap.find(name);
|
|
if(str_i != _strOptMap.end()) {
|
|
str_i->second = value;
|
|
} else if(int_i != _intOptMap.end()) {
|
|
int_i->second = atol(value.c_str());
|
|
} else if(dbl_i != _dblOptMap.end()) {
|
|
dbl_i->second = atof(value.c_str());
|
|
}
|
|
}
|
|
|
|
// close the file
|
|
config.close();
|
|
} catch(std::fstream::failure e) {
|
|
std::cerr << e.what() << std::endl;
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Writes the configuration file with the current configuration settings.
|
|
*/
|
|
int
|
|
Config::save()
|
|
{
|
|
std::fstream config;
|
|
std::map<std::string, int>::iterator int_i;
|
|
std::map<std::string, double>::iterator dbl_i;
|
|
std::map<std::string, std::string>::iterator str_i;
|
|
std::string configFile = _dir + "/" + cfgFile;
|
|
char buf[1024];
|
|
|
|
// set the exception handling to catch i/o errors
|
|
config.exceptions(std::ios::failbit | std::ios::badbit);
|
|
|
|
try {
|
|
// open the file, truncate and for write
|
|
config.open(configFile.c_str(), std::ios::out | std::ios::trunc);
|
|
|
|
// write a warning
|
|
strcpy(buf, "# Auto-generated\n# SDL keycodes defined in /usr/include/SDL/SDL_keysym.h\n");
|
|
config.write(buf, strlen(buf));
|
|
|
|
// write each configuration setting
|
|
for(int_i = _intOptMap.begin(); int_i != _intOptMap.end(); int_i++) {
|
|
snprintf(buf, 1024, "%s = %d\n",
|
|
int_i->first.c_str(), int_i->second);
|
|
config.write(buf, strlen(buf));
|
|
}
|
|
for(dbl_i = _dblOptMap.begin(); dbl_i != _dblOptMap.end(); dbl_i++) {
|
|
snprintf(buf, 1024, "%s = %f\n",
|
|
dbl_i->first.c_str(), dbl_i->second);
|
|
config.write(buf, strlen(buf));
|
|
}
|
|
for(str_i = _strOptMap.begin(); str_i != _strOptMap.end(); str_i++) {
|
|
snprintf(buf, 1024, "%s = %s\n",
|
|
str_i->first.c_str(), str_i->second.c_str());
|
|
config.write(buf, strlen(buf));
|
|
}
|
|
|
|
// close the file
|
|
config.close();
|
|
} catch(std::fstream::failure e) {
|
|
std::cerr << e.what() << std::endl;
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|