diff options
Diffstat (limited to 'src/settings.h')
-rw-r--r-- | src/settings.h | 583 |
1 files changed, 583 insertions, 0 deletions
diff --git a/src/settings.h b/src/settings.h new file mode 100644 index 000000000..f972ce3ec --- /dev/null +++ b/src/settings.h @@ -0,0 +1,583 @@ +/* +Minetest-c55 +Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef SETTINGS_HEADER +#define SETTINGS_HEADER + +#include "common_irrlicht.h" +#include <string> +#include <jthread.h> +#include <jmutex.h> +#include <jmutexautolock.h> +#include "strfnd.h" +#include <iostream> +#include <fstream> +#include <sstream> +#include "debug.h" +#include "utility.h" + +enum ValueType +{ + VALUETYPE_STRING, + VALUETYPE_FLAG // Doesn't take any arguments +}; + +struct ValueSpec +{ + ValueSpec(ValueType a_type, const char *a_help=NULL) + { + type = a_type; + help = a_help; + } + ValueType type; + const char *help; +}; + +class Settings +{ +public: + Settings() + { + m_mutex.Init(); + } + + void writeLines(std::ostream &os) + { + JMutexAutoLock lock(m_mutex); + + for(core::map<std::string, std::string>::Iterator + i = m_settings.getIterator(); + i.atEnd() == false; i++) + { + std::string name = i.getNode()->getKey(); + std::string value = i.getNode()->getValue(); + os<<name<<" = "<<value<<"\n"; + } + } + + bool parseConfigLine(const std::string &line) + { + JMutexAutoLock lock(m_mutex); + + std::string trimmedline = trim(line); + + // Ignore comments + if(trimmedline[0] == '#') + return true; + + //dstream<<"trimmedline=\""<<trimmedline<<"\""<<std::endl; + + Strfnd sf(trim(line)); + + std::string name = sf.next("="); + name = trim(name); + + if(name == "") + return true; + + std::string value = sf.next("\n"); + value = trim(value); + + /*dstream<<"Config name=\""<<name<<"\" value=\"" + <<value<<"\""<<std::endl;*/ + + m_settings[name] = value; + + return true; + } + + // Returns false on EOF + bool parseConfigObject(std::istream &is) + { + if(is.eof()) + return false; + + /* + NOTE: This function might be expanded to allow multi-line + settings. + */ + std::string line; + std::getline(is, line); + //dstream<<"got line: \""<<line<<"\""<<std::endl; + + return parseConfigLine(line); + } + + /* + Read configuration file + + Returns true on success + */ + bool readConfigFile(const char *filename) + { + std::ifstream is(filename); + if(is.good() == false) + { + dstream<<"Error opening configuration file \"" + <<filename<<"\""<<std::endl; + return false; + } + + dstream<<"Parsing configuration file: \"" + <<filename<<"\""<<std::endl; + + while(parseConfigObject(is)); + + return true; + } + + /* + Reads a configuration object from stream (usually a single line) + and adds it to dst. + + Preserves comments and empty lines. + + Settings that were added to dst are also added to updated. + key of updated is setting name, value of updated is dummy. + + Returns false on EOF + */ + bool getUpdatedConfigObject(std::istream &is, + core::list<std::string> &dst, + core::map<std::string, bool> &updated) + { + JMutexAutoLock lock(m_mutex); + + if(is.eof()) + return false; + + // NOTE: This function will be expanded to allow multi-line settings + std::string line; + std::getline(is, line); + + std::string trimmedline = trim(line); + + std::string line_end = ""; + if(is.eof() == false) + line_end = "\n"; + + // Ignore comments + if(trimmedline[0] == '#') + { + dst.push_back(line+line_end); + return true; + } + + Strfnd sf(trim(line)); + + std::string name = sf.next("="); + name = trim(name); + + if(name == "") + { + dst.push_back(line+line_end); + return true; + } + + std::string value = sf.next("\n"); + value = trim(value); + + if(m_settings.find(name)) + { + std::string newvalue = m_settings[name]; + + if(newvalue != value) + { + dstream<<"Changing value of \""<<name<<"\" = \"" + <<value<<"\" -> \""<<newvalue<<"\"" + <<std::endl; + } + + dst.push_back(name + " = " + newvalue + line_end); + + updated[name] = true; + } + + return true; + } + + /* + Updates configuration file + + Returns true on success + */ + bool updateConfigFile(const char *filename) + { + dstream<<"Updating configuration file: \"" + <<filename<<"\""<<std::endl; + + core::list<std::string> objects; + core::map<std::string, bool> updated; + + // Read and modify stuff + { + std::ifstream is(filename); + if(is.good() == false) + { + dstream<<"INFO: updateConfigFile():" + " Error opening configuration file" + " for reading: \"" + <<filename<<"\""<<std::endl; + } + else + { + while(getUpdatedConfigObject(is, objects, updated)); + } + } + + JMutexAutoLock lock(m_mutex); + + // Write stuff back + { + std::ofstream os(filename); + if(os.good() == false) + { + dstream<<"Error opening configuration file" + " for writing: \"" + <<filename<<"\""<<std::endl; + return false; + } + + /* + Write updated stuff + */ + for(core::list<std::string>::Iterator + i = objects.begin(); + i != objects.end(); i++) + { + os<<(*i); + } + + /* + Write stuff that was not already in the file + */ + for(core::map<std::string, std::string>::Iterator + i = m_settings.getIterator(); + i.atEnd() == false; i++) + { + if(updated.find(i.getNode()->getKey())) + continue; + std::string name = i.getNode()->getKey(); + std::string value = i.getNode()->getValue(); + dstream<<"Adding \""<<name<<"\" = \""<<value<<"\"" + <<std::endl; + os<<name<<" = "<<value<<"\n"; + } + } + + return true; + } + + /* + NOTE: Types of allowed_options are ignored + + returns true on success + */ + bool parseCommandLine(int argc, char *argv[], + core::map<std::string, ValueSpec> &allowed_options) + { + int i=1; + for(;;) + { + if(i >= argc) + break; + std::string argname = argv[i]; + if(argname.substr(0, 2) != "--") + { + dstream<<"Invalid command-line parameter \"" + <<argname<<"\": --<option> expected."<<std::endl; + return false; + } + i++; + + std::string name = argname.substr(2); + + core::map<std::string, ValueSpec>::Node *n; + n = allowed_options.find(name); + if(n == NULL) + { + dstream<<"Unknown command-line parameter \"" + <<argname<<"\""<<std::endl; + return false; + } + + ValueType type = n->getValue().type; + + std::string value = ""; + + if(type == VALUETYPE_FLAG) + { + value = "true"; + } + else + { + if(i >= argc) + { + dstream<<"Invalid command-line parameter \"" + <<name<<"\": missing value"<<std::endl; + return false; + } + value = argv[i]; + i++; + } + + + dstream<<"Valid command-line parameter: \"" + <<name<<"\" = \""<<value<<"\"" + <<std::endl; + set(name, value); + } + + return true; + } + + void set(std::string name, std::string value) + { + JMutexAutoLock lock(m_mutex); + + m_settings[name] = value; + } + + void set(std::string name, const char *value) + { + JMutexAutoLock lock(m_mutex); + + m_settings[name] = value; + } + + + void setDefault(std::string name, std::string value) + { + JMutexAutoLock lock(m_mutex); + + m_defaults[name] = value; + } + + bool exists(std::string name) + { + JMutexAutoLock lock(m_mutex); + + return (m_settings.find(name) || m_defaults.find(name)); + } + + std::string get(std::string name) + { + JMutexAutoLock lock(m_mutex); + + core::map<std::string, std::string>::Node *n; + n = m_settings.find(name); + if(n == NULL) + { + n = m_defaults.find(name); + if(n == NULL) + { + dstream<<"INFO: Settings: Setting not found: \"" + <<name<<"\""<<std::endl; + throw SettingNotFoundException("Setting not found"); + } + } + + return n->getValue(); + } + + bool getBool(std::string name) + { + return is_yes(get(name)); + } + + bool getFlag(std::string name) + { + try + { + return getBool(name); + } + catch(SettingNotFoundException &e) + { + return false; + } + } + + // Asks if empty + bool getBoolAsk(std::string name, std::string question, bool def) + { + // If it is in settings + if(exists(name)) + return getBool(name); + + std::string s; + char templine[10]; + std::cout<<question<<" [y/N]: "; + std::cin.getline(templine, 10); + s = templine; + + if(s == "") + return def; + + return is_yes(s); + } + + float getFloat(std::string name) + { + return stof(get(name)); + } + + u16 getU16(std::string name) + { + return stoi(get(name), 0, 65535); + } + + u16 getU16Ask(std::string name, std::string question, u16 def) + { + // If it is in settings + if(exists(name)) + return getU16(name); + + std::string s; + char templine[10]; + std::cout<<question<<" ["<<def<<"]: "; + std::cin.getline(templine, 10); + s = templine; + + if(s == "") + return def; + + return stoi(s, 0, 65535); + } + + s16 getS16(std::string name) + { + return stoi(get(name), -32768, 32767); + } + + s32 getS32(std::string name) + { + return stoi(get(name)); + } + + v3f getV3F(std::string name) + { + v3f value; + Strfnd f(get(name)); + f.next("("); + value.X = stof(f.next(",")); + value.Y = stof(f.next(",")); + value.Z = stof(f.next(")")); + return value; + } + + u64 getU64(std::string name) + { + u64 value = 0; + std::string s = get(name); + std::istringstream ss(s); + ss>>value; + return value; + } + + void setBool(std::string name, bool value) + { + if(value) + set(name, "true"); + else + set(name, "false"); + } + + void setS32(std::string name, s32 value) + { + set(name, itos(value)); + } + + void setFloat(std::string name, float value) + { + set(name, ftos(value)); + } + + void setV3F(std::string name, v3f value) + { + std::ostringstream os; + os<<"("<<value.X<<","<<value.Y<<","<<value.Z<<")"; + set(name, os.str()); + } + + void setU64(std::string name, u64 value) + { + std::ostringstream os; + os<<value; + set(name, os.str()); + } + + void clear() + { + JMutexAutoLock lock(m_mutex); + + m_settings.clear(); + m_defaults.clear(); + } + + Settings & operator+=(Settings &other) + { + JMutexAutoLock lock(m_mutex); + JMutexAutoLock lock2(other.m_mutex); + + if(&other == this) + return *this; + + for(core::map<std::string, std::string>::Iterator + i = other.m_settings.getIterator(); + i.atEnd() == false; i++) + { + m_settings.insert(i.getNode()->getKey(), + i.getNode()->getValue()); + } + + for(core::map<std::string, std::string>::Iterator + i = other.m_defaults.getIterator(); + i.atEnd() == false; i++) + { + m_defaults.insert(i.getNode()->getKey(), + i.getNode()->getValue()); + } + + return *this; + + } + + Settings & operator=(Settings &other) + { + JMutexAutoLock lock(m_mutex); + JMutexAutoLock lock2(other.m_mutex); + + if(&other == this) + return *this; + + clear(); + (*this) += other; + + return *this; + } + +private: + core::map<std::string, std::string> m_settings; + core::map<std::string, std::string> m_defaults; + // All methods that access m_settings/m_defaults directly should lock this. + JMutex m_mutex; +}; + +#endif + |