diff --git a/src/drivers/common/SConscript b/src/drivers/common/SConscript index 94f47a8e..64fadbe7 100644 --- a/src/drivers/common/SConscript +++ b/src/drivers/common/SConscript @@ -8,6 +8,7 @@ scale2x.cpp scale3x.cpp scalebit.cpp vidblit.cpp +configSys.cpp """) for x in range(len(my_list)): diff --git a/src/drivers/common/configSys.cpp b/src/drivers/common/configSys.cpp new file mode 100644 index 00000000..a40f12b8 --- /dev/null +++ b/src/drivers/common/configSys.cpp @@ -0,0 +1,409 @@ +#include <iostream> +#include <fstream> +#include <stdio.h> + +#include "configSys.h" + +/** + * 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) { + 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())) { + return -1; + } + + // add the option + switch(type) { + case(INTEGER): + _intOptMap[name] = 0; + break; + case(STRING): + _strOptMap[name] = ""; + break; + default: + break; + } + _shortArgMap[shortArg] = name; + _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 type, + int defaultValue) +{ + int error; + + // add the option to the config system + error = _addOption(shortArg, longArg, name, type); + 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, + int type, + const std::string &defaultValue) +{ + int error; + + // add the option to the config system + error = _addOption(shortArg, longArg, name, type); + if(error) { + return error; + } + + // set the option to the default value + error = setOption(name, defaultValue); + if(error) { + return error; + } + + 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 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; +} + +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; +} + + +/** + * 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::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); + if(str_i != _strOptMap.end()) { + str_i->second = argv[i]; + } else if(int_i != _intOptMap.end()) { + int_i->second = atol(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 + error = _parseArgs(argc, argv); + if(error) { + return error; + } + + return 0; +} + + +/** + * 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. + */ +int +Config::_load() +{ + int error; + unsigned int pos, eqPos; + std::fstream config; + std::map<std::string, int>::iterator int_i; + std::map<std::string, std::string>::iterator str_i; + std::string configFile = _dir + "/fceu.cfg"; + 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); + + while(!config.eof()) { + // read a line + config.getline(buf, 1024); + line = buf; + + // check line validity + eqPos = line.find("="); + if(line[0] == '#' || eqPos == std::string::npos) { + // 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); + value = line.substr(pos); + + // check if the option exists, and if so, set it appropriately + str_i = _strOptMap.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()); + } + } + + // 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() +{ + int error; + std::fstream config; + std::map<std::string, int>::iterator int_i; + std::map<std::string, std::string>::iterator str_i; + std::string configFile = _dir + "/fceu.cfg"; + 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"); + 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(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; +} diff --git a/src/drivers/common/configSys.h b/src/drivers/common/configSys.h new file mode 100644 index 00000000..da72c959 --- /dev/null +++ b/src/drivers/common/configSys.h @@ -0,0 +1,62 @@ +#ifndef __CONFIGSYS_H +#define __CONFIGSYS_H + +#include <map> +#include <string> + +class Config { +private: + std::string _dir; + + std::map<std::string, std::string> _strOptMap; + std::map<std::string, int> _intOptMap; + + std::map<char, std::string> _shortArgMap; + std::map<std::string, std::string> _longArgMap; + +private: + int _addOption(char, const std::string &, const std::string &, int); + int _load(void); + int _parseArgs(int, char **); + +public: + const static int STRING = 1; + const static int INTEGER = 2; + +public: + Config(std::string d) : _dir(d) { } + ~Config() { } + + /** + * Adds a configuration option. All options must be added before + * parse(). + */ + int addOption(char, const std::string &, + const std::string &, int, int); + int addOption(char, const std::string &, + const std::string &, int, const std::string &); + + /** + * Sets a configuration option. Can be called at any time. + */ + int setOption(const std::string &, int); + int setOption(const std::string &, const std::string &); + + int getOption(const std::string &, std::string *); + int getOption(const std::string &, const char **); + int getOption(const std::string &, int *); + + /** + * Parse the arguments. Also read in the configuration file and + * set the variables accordingly. + */ + int parse(int, char **); + + /** + * Save all of the current configuration options to the + * configuration file. + */ + int save(); +}; + +#endif // !__CONFIGSYS_H