Translated using Weblate (Chinese (Simplified))
[oweals/minetest.git] / src / settings.h
index 2b46676c6b7ae03a440b2a9870f4494b2599a51f..0c9a155db4f0ac258259166a13f8a3311490db65 100644 (file)
@@ -1,6 +1,6 @@
 /*
-Minetest-c55
-Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
+Minetest
+Copyright (C) 2010-2013 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 Lesser General Public License as published by
@@ -17,962 +17,236 @@ 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
+#pragma once
 
 #include "irrlichttypes_bloated.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 "log.h"
 #include "util/string.h"
-#include "porting.h"
+#include <string>
+#include <list>
+#include <set>
+#include <mutex>
+
+class Settings;
+struct NoiseParams;
+
+// Global objects
+extern Settings *g_settings;
+extern std::string g_settings_path;
+
+// Type for a settings changed callback function
+typedef void (*SettingsChangedCallback)(const std::string &name, void *data);
+
+typedef std::vector<
+       std::pair<
+               SettingsChangedCallback,
+               void *
+       >
+> SettingsCallbackList;
 
-enum ValueType
-{
+typedef std::unordered_map<std::string, SettingsCallbackList> SettingsCallbackMap;
+
+enum ValueType {
        VALUETYPE_STRING,
        VALUETYPE_FLAG // Doesn't take any arguments
 };
 
-struct ValueSpec
-{
+enum SettingsParseEvent {
+       SPE_NONE,
+       SPE_INVALID,
+       SPE_COMMENT,
+       SPE_KVPAIR,
+       SPE_END,
+       SPE_GROUP,
+       SPE_MULTILINE,
+};
+
+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";
-               }
-       }
-  
-       // return all keys used 
-       std::vector<std::string> getNames(){
-               std::vector<std::string> names;
-               for(core::map<std::string, std::string>::Iterator
-                               i = m_settings.getIterator();
-                               i.atEnd() == false; i++)
-               {
-                       std::string name = i.getNode()->getKey();
-                       names.push_back(name);
-               }
-               return names;  
-       }
-
-       // remove a setting
-       bool remove(const std::string& name)
-       {
-               return m_settings.remove(name);
-       }
-
-
-       bool parseConfigLine(const std::string &line)
-       {
-               JMutexAutoLock lock(m_mutex);
-
-               std::string trimmedline = trim(line);
-
-               // Ignore empty lines and comments
-               if(trimmedline.size() == 0 || trimmedline[0] == '#')
-                       return true;
-
-               //infostream<<"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);
-
-               /*infostream<<"Config name=\""<<name<<"\" value=\""
-                               <<value<<"\""<<std::endl;*/
-
-               m_settings[name] = value;
-
-               return true;
-       }
-
-       void parseConfigLines(std::istream &is, const std::string &endstring)
-       {
-               for(;;){
-                       if(is.eof())
-                               break;
-                       std::string line;
-                       std::getline(is, line);
-                       std::string trimmedline = trim(line);
-                       if(endstring != ""){
-                               if(trimmedline == endstring)
-                                       break;
-                       }
-                       parseConfigLine(line);
-               }
-       }
-
-       // 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);
-               //infostream<<"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)
-                       return false;
-
-               /*infostream<<"Parsing configuration file: \""
-                               <<filename<<"\""<<std::endl;*/
+struct SettingsEntry {
+       SettingsEntry() = default;
 
-               while(parseConfigObject(is));
+       SettingsEntry(const std::string &value_) :
+               value(value_)
+       {}
 
-               return true;
-       }
+       SettingsEntry(Settings *group_) :
+               group(group_),
+               is_group(true)
+       {}
 
-       /*
-               Reads a configuration object from stream (usually a single line)
-               and adds it to dst.
+       std::string value = "";
+       Settings *group = nullptr;
+       bool is_group = false;
+};
 
-               Preserves comments and empty lines.
+typedef std::unordered_map<std::string, SettingsEntry> SettingEntries;
 
-               Settings that were added to dst are also added to updated.
-               key of updated is setting name, value of updated is dummy.
+class Settings {
+public:
+       Settings() = default;
 
-               Returns false on EOF
-       */
-       bool getUpdatedConfigObject(std::istream &is,
-                       core::list<std::string> &dst,
-                       core::map<std::string, bool> &updated,
-                       bool &value_changed)
-       {
-               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 empty lines and comments
-               if(trimmedline.size() == 0 || 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)
-                       {
-                               infostream<<"Changing value of \""<<name<<"\" = \""
-                                               <<value<<"\" -> \""<<newvalue<<"\""
-                                               <<std::endl;
-                               value_changed = true;
-                       }
-
-                       dst.push_back(name + " = " + newvalue + line_end);
-
-                       updated[name] = true;
-               }
-               else //file contains a setting which is not in m_settings
-                       value_changed=true;
-                       
-               return true;
-       }
+       ~Settings();
 
-       /*
-               Updates configuration file
+       Settings & operator += (const Settings &other);
+       Settings & operator = (const Settings &other);
 
-               Returns true on success
-       */
-       bool updateConfigFile(const char *filename)
-       {
-               infostream<<"Updating configuration file: \""
-                               <<filename<<"\""<<std::endl;
-
-               core::list<std::string> objects;
-               core::map<std::string, bool> updated;
-               bool something_actually_changed = false;
-
-               // Read and modify stuff
-               {
-                       std::ifstream is(filename);
-                       if(is.good() == false)
-                       {
-                               infostream<<"updateConfigFile():"
-                                               " Error opening configuration file"
-                                               " for reading: \""
-                                               <<filename<<"\""<<std::endl;
-                       }
-                       else
-                       {
-                               while(getUpdatedConfigObject(is, objects, updated,
-                                               something_actually_changed));
-                       }
-               }
-
-               JMutexAutoLock lock(m_mutex);
-
-               // If something not yet determined to have been changed, check if
-               // any new stuff was added
-               if(!something_actually_changed){
-                       for(core::map<std::string, std::string>::Iterator
-                                       i = m_settings.getIterator();
-                                       i.atEnd() == false; i++)
-                       {
-                               if(updated.find(i.getNode()->getKey()))
-                                       continue;
-                               something_actually_changed = true;
-                               break;
-                       }
-               }
-
-               // If nothing was actually changed, skip writing the file
-               if(!something_actually_changed){
-                       infostream<<"Skipping writing of "<<filename
-                                       <<" because content wouldn't be modified"<<std::endl;
-                       return true;
-               }
-
-               // Write stuff back
-               {
-                       std::ofstream os(filename);
-                       if(os.good() == false)
-                       {
-                               errorstream<<"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();
-                               infostream<<"Adding \""<<name<<"\" = \""<<value<<"\""
-                                               <<std::endl;
-                               os<<name<<" = "<<value<<"\n";
-                       }
-               }
-
-               return true;
-       }
+       /***********************
+        * Reading and writing *
+        ***********************/
 
-       /*
-               NOTE: Types of allowed_options are ignored
-
-               returns true on success
-       */
+       // Read configuration file.  Returns success.
+       bool readConfigFile(const char *filename);
+       //Updates configuration file.  Returns success.
+       bool updateConfigFile(const char *filename);
+       // NOTE: Types of allowed_options are ignored.  Returns success.
        bool parseCommandLine(int argc, char *argv[],
-                       core::map<std::string, ValueSpec> &allowed_options)
-       {
-               int nonopt_index = 0;
-               int i=1;
-               for(;;)
-               {
-                       if(i >= argc)
-                               break;
-                       std::string argname = argv[i];
-                       if(argname.substr(0, 2) != "--")
-                       {
-                               // If option doesn't start with -, read it in as nonoptX
-                               if(argname[0] != '-'){
-                                       std::string name = "nonopt";
-                                       name += itos(nonopt_index);
-                                       set(name, argname);
-                                       nonopt_index++;
-                                       i++;
-                                       continue;
-                               }
-                               errorstream<<"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)
-                       {
-                               errorstream<<"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)
-                               {
-                                       errorstream<<"Invalid command-line parameter \""
-                                                       <<name<<"\": missing value"<<std::endl;
-                                       return false;
-                               }
-                               value = argv[i];
-                               i++;
-                       }
-
-
-                       infostream<<"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)
-                       {
-                               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;
-       }
-
-       v2f getV2F(std::string name)
-       {
-               v2f value;
-               Strfnd f(get(name));
-               f.next("(");
-               value.X = stof(f.next(","));
-               value.Y = 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;
-       }
-
-       template <class T> T *getStruct(std::string name, std::string format)
-       {
-               size_t len = sizeof(T);
-               std::vector<std::string *> strs_alloced;
-               std::string *str;
-               std::string valstr = get(name);
-               char *s = &valstr[0];
-               T *buf = new T;
-               char *bufpos = (char *)buf;
-               char *f, *snext;
-               size_t pos;
-
-               char *fmtpos, *fmt = &format[0];
-               while ((f = strtok_r(fmt, ",", &fmtpos)) && s) {
-                       fmt = NULL;
-
-                       bool is_unsigned = false;
-                       int width = 0;
-                       char valtype = *f;
-
-                       width = (int)strtol(f + 1, &f, 10);
-                       if (width && valtype == 's')
-                               valtype = 'i';
-
-                       switch (valtype) {
-                               case 'u':
-                                       is_unsigned = true;
-                                       /* FALLTHROUGH */
-                               case 'i':
-                                       if (width == 16) {
-                                               bufpos += PADDING(bufpos, u16);
-                                               if ((bufpos - (char *)buf) + sizeof(u16) <= len) {
-                                                       if (is_unsigned)
-                                                               *(u16 *)bufpos = (u16)strtoul(s, &s, 10);
-                                                       else
-                                                               *(s16 *)bufpos = (s16)strtol(s, &s, 10);
-                                               }
-                                               bufpos += sizeof(u16);
-                                       } else if (width == 32) {
-                                               bufpos += PADDING(bufpos, u32);
-                                               if ((bufpos - (char *)buf) + sizeof(u32) <= len) {
-                                                       if (is_unsigned)
-                                                               *(u32 *)bufpos = (u32)strtoul(s, &s, 10);
-                                                       else
-                                                               *(s32 *)bufpos = (s32)strtol(s, &s, 10);
-                                               }
-                                               bufpos += sizeof(u32);
-                                       } else if (width == 64) {
-                                               bufpos += PADDING(bufpos, u64);
-                                               if ((bufpos - (char *)buf) + sizeof(u64) <= len) {
-                                                       if (is_unsigned)
-                                                               *(u64 *)bufpos = (u64)strtoull(s, &s, 10);
-                                                       else
-                                                               *(s64 *)bufpos = (s64)strtoll(s, &s, 10);
-                                               }
-                                               bufpos += sizeof(u64);
-                                       }
-                                       s = strchr(s, ',');
-                                       break;
-                               case 'b':
-                                       snext = strchr(s, ',');
-                                       if (snext)
-                                               *snext++ = 0;
-
-                                       bufpos += PADDING(bufpos, bool);
-                                       if ((bufpos - (char *)buf) + sizeof(bool) <= len)
-                                               *(bool *)bufpos = is_yes(std::string(s));
-                                       bufpos += sizeof(bool);
-
-                                       s = snext;
-                                       break;
-                               case 'f':
-                                       bufpos += PADDING(bufpos, float);
-                                       if ((bufpos - (char *)buf) + sizeof(float) <= len)
-                                               *(float *)bufpos = strtof(s, &s);
-                                       bufpos += sizeof(float);
-
-                                       s = strchr(s, ',');
-                                       break;
-                               case 's':
-                                       while (*s == ' ' || *s == '\t')
-                                               s++;
-                                       if (*s++ != '"') //error, expected string
-                                               goto fail;
-                                       snext = s;
-
-                                       while (snext[0] && !(snext[-1] != '\\' && snext[0] == '"'))
-                                               snext++;
-                                       *snext++ = 0;
-
-                                       bufpos += PADDING(bufpos, std::string *);
-
-                                       str = new std::string(s);
-                                       pos = 0;
-                                       while ((pos = str->find("\\\"", pos)) != std::string::npos)
-                                               str->erase(pos, 1);
-
-                                       if ((bufpos - (char *)buf) + sizeof(std::string *) <= len)
-                                               *(std::string **)bufpos = str;
-                                       bufpos += sizeof(std::string *);
-                                       strs_alloced.push_back(str);
-
-                                       s = *snext ? snext + 1 : NULL;
-                                       break;
-                               case 'v':
-                                       while (*s == ' ' || *s == '\t')
-                                               s++;
-                                       if (*s++ != '(') //error, expected vector
-                                               goto fail;
-
-                                       if (width == 2) {
-                                               bufpos += PADDING(bufpos, v2f);
-
-                                               if ((bufpos - (char *)buf) + sizeof(v2f) <= len) {
-                                               v2f *v = (v2f *)bufpos;
-                                                       v->X = strtof(s, &s);
-                                                       s++;
-                                                       v->Y = strtof(s, &s);
-                                               }
-
-                                               bufpos += sizeof(v2f);
-                                       } else if (width == 3) {
-                                               bufpos += PADDING(bufpos, v3f);
-                                               if ((bufpos - (char *)buf) + sizeof(v3f) <= len) {
-                                                       v3f *v = (v3f *)bufpos;
-                                                       v->X = strtof(s, &s);
-                                                       s++;
-                                                       v->Y = strtof(s, &s);
-                                                       s++;
-                                                       v->Z = strtof(s, &s);
-                                               }
-
-                                               bufpos += sizeof(v3f);
-                                       }
-                                       s = strchr(s, ',');
-                                       break;
-                               default: //error, invalid format specifier
-                                       goto fail;
-                       }
-
-                       if (s && *s == ',')
-                               s++;
-
-                       if ((size_t)(bufpos - (char *)buf) > len) //error, buffer too small
-                               goto fail;
-               }
-
-               if (f && *f) { //error, mismatched number of fields and values
-fail:
-                       for (unsigned int i = 0; i != strs_alloced.size(); i++)
-                               delete strs_alloced[i];
-                       delete buf;
-                       //delete[] buf;
-                       buf = NULL;
-               }
-
-               return buf;
-       }
-
-       bool setStruct(std::string name, std::string format, void *value)
-       {
-               char sbuf[2048];
-               int sbuflen = sizeof(sbuf) - 1;
-               sbuf[sbuflen] = 0;
-               std::string str;
-               int pos = 0;
-               size_t fpos;
-               char *f;
-
-               char *bufpos = (char *)value;
-               char *fmtpos, *fmt = &format[0];
-               while ((f = strtok_r(fmt, ",", &fmtpos))) {
-                       fmt = NULL;
-                       bool is_unsigned = false;
-                       int width = 0, nprinted = 0;
-                       char valtype = *f;
-
-                       width = (int)strtol(f + 1, &f, 10);
-                       if (width && valtype == 's')
-                               valtype = 'i';
-
-                       switch (valtype) {
-                               case 'u':
-                                       is_unsigned = true;
-                                       /* FALLTHROUGH */
-                               case 'i':
-                                       if (width == 16) {
-                                               bufpos += PADDING(bufpos, u16);
-                                               nprinted = snprintf(sbuf + pos, sbuflen,
-                                                                       is_unsigned ? "%u, " : "%d, ",
-                                                                       *((u16 *)bufpos));
-                                               bufpos += sizeof(u16);
-                                       } else if (width == 32) {
-                                               bufpos += PADDING(bufpos, u32);
-                                               nprinted = snprintf(sbuf + pos, sbuflen,
-                                                                       is_unsigned ? "%u, " : "%d, ",
-                                                                       *((u32 *)bufpos));
-                                               bufpos += sizeof(u32);
-                                       } else if (width == 64) {
-                                               bufpos += PADDING(bufpos, u64);
-                                               nprinted = snprintf(sbuf + pos, sbuflen,
-                                                                       is_unsigned ? "%llu, " : "%lli, ",
-                                                                       (unsigned long long)*((u64 *)bufpos));
-                                               bufpos += sizeof(u64);
-                                       }
-                                       break;
-                               case 'b':
-                                       bufpos += PADDING(bufpos, bool);
-                                       nprinted = snprintf(sbuf + pos, sbuflen, "%s, ",
-                                                                               *((bool *)bufpos) ? "true" : "false");
-                                       bufpos += sizeof(bool);
-                                       break;
-                               case 'f':
-                                       bufpos += PADDING(bufpos, float);
-                                       nprinted = snprintf(sbuf + pos, sbuflen, "%f, ",
-                                                                               *((float *)bufpos));
-                                       bufpos += sizeof(float);
-                                       break;
-                               case 's':
-                                       bufpos += PADDING(bufpos, std::string *);
-                                       str = **((std::string **)bufpos);
-
-                                       fpos = 0;
-                                       while ((fpos = str.find('"', fpos)) != std::string::npos) {
-                                               str.insert(fpos, 1, '\\');
-                                               fpos += 2;
-                                       }
-
-                                       nprinted = snprintf(sbuf + pos, sbuflen, "\"%s\", ",
-                                                                               (*((std::string **)bufpos))->c_str());
-                                       bufpos += sizeof(std::string *);
-                                       break;
-                               case 'v':
-                                       if (width == 2) {
-                                               bufpos += PADDING(bufpos, v2f);
-                                               v2f *v = (v2f *)bufpos;
-                                               nprinted = snprintf(sbuf + pos, sbuflen,
-                                                                                       "(%f, %f), ", v->X, v->Y);
-                                               bufpos += sizeof(v2f);
-                                       } else {
-                                               bufpos += PADDING(bufpos, v3f);
-                                               v3f *v = (v3f *)bufpos;
-                                               nprinted = snprintf(sbuf + pos, sbuflen,
-                                                                                       "(%f, %f, %f), ", v->X, v->Y, v->Z);
-                                               bufpos += sizeof(v3f);
-                                       }
-                                       break;
-                               default:
-                                       return false;
-                       }
-                       if (nprinted < 0) //error, buffer too small
-                               return false;
-                       pos     += nprinted;
-                       sbuflen -= nprinted;
-               }
-
-               if (pos >= 2)
-                       sbuf[pos - 2] = 0;
-
-               set(name, std::string(sbuf));
-               return true;
-       }
-
-       void setBool(std::string name, bool value)
-       {
-               if(value)
-                       set(name, "true");
-               else
-                       set(name, "false");
-       }
+                       std::map<std::string, ValueSpec> &allowed_options);
+       bool parseConfigLines(std::istream &is, const std::string &end = "");
+       void writeLines(std::ostream &os, u32 tab_depth=0) const;
+
+       SettingsParseEvent parseConfigObject(const std::string &line,
+               const std::string &end, std::string &name, std::string &value);
+       bool updateConfigObject(std::istream &is, std::ostream &os,
+               const std::string &end, u32 tab_depth=0);
+
+       static bool checkNameValid(const std::string &name);
+       static bool checkValueValid(const std::string &value);
+       static std::string getMultiline(std::istream &is, size_t *num_lines=NULL);
+       static void printEntry(std::ostream &os, const std::string &name,
+               const SettingsEntry &entry, u32 tab_depth=0);
+
+       /***********
+        * Getters *
+        ***********/
+
+       const SettingsEntry &getEntry(const std::string &name) const;
+       const SettingsEntry &getEntryDefault(const std::string &name) const;
+       Settings *getGroup(const std::string &name) const;
+       const std::string &get(const std::string &name) const;
+       const std::string &getDefault(const std::string &name) const;
+       bool getBool(const std::string &name) const;
+       u16 getU16(const std::string &name) const;
+       s16 getS16(const std::string &name) const;
+       u32 getU32(const std::string &name) const;
+       s32 getS32(const std::string &name) const;
+       u64 getU64(const std::string &name) const;
+       float getFloat(const std::string &name) const;
+       v2f getV2F(const std::string &name) const;
+       v3f getV3F(const std::string &name) const;
+       u32 getFlagStr(const std::string &name, const FlagDesc *flagdesc,
+                       u32 *flagmask) const;
+       // N.B. if getStruct() is used to read a non-POD aggregate type,
+       // the behavior is undefined.
+       bool getStruct(const std::string &name, const std::string &format,
+                       void *out, size_t olen) const;
+       bool getNoiseParams(const std::string &name, NoiseParams &np) const;
+       bool getNoiseParamsFromValue(const std::string &name, NoiseParams &np) const;
+       bool getNoiseParamsFromGroup(const std::string &name, NoiseParams &np) const;
+
+       // return all keys used
+       std::vector<std::string> getNames() const;
+       bool exists(const std::string &name) const;
+
+
+       /***************************************
+        * Getters that don't throw exceptions *
+        ***************************************/
+
+       bool getEntryNoEx(const std::string &name, SettingsEntry &val) const;
+       bool getEntryDefaultNoEx(const std::string &name, SettingsEntry &val) const;
+       bool getGroupNoEx(const std::string &name, Settings *&val) const;
+       bool getNoEx(const std::string &name, std::string &val) const;
+       bool getDefaultNoEx(const std::string &name, std::string &val) const;
+       bool getFlag(const std::string &name) const;
+       bool getU16NoEx(const std::string &name, u16 &val) const;
+       bool getS16NoEx(const std::string &name, s16 &val) const;
+       bool getS32NoEx(const std::string &name, s32 &val) const;
+       bool getU64NoEx(const std::string &name, u64 &val) const;
+       bool getFloatNoEx(const std::string &name, float &val) const;
+       bool getV2FNoEx(const std::string &name, v2f &val) const;
+       bool getV3FNoEx(const std::string &name, v3f &val) const;
+
+       // Like other getters, but handling each flag individualy:
+       // 1) Read default flags (or 0)
+       // 2) Override using user-defined flags
+       bool getFlagStrNoEx(const std::string &name, u32 &val,
+               const FlagDesc *flagdesc) const;
+
+
+       /***********
+        * Setters *
+        ***********/
+
+       // N.B. Groups not allocated with new must be set to NULL in the settings
+       // tree before object destruction.
+       bool setEntry(const std::string &name, const void *entry,
+               bool set_group, bool set_default);
+       bool set(const std::string &name, const std::string &value);
+       bool setDefault(const std::string &name, const std::string &value);
+       bool setGroup(const std::string &name, Settings *group);
+       bool setGroupDefault(const std::string &name, Settings *group);
+       bool setBool(const std::string &name, bool value);
+       bool setS16(const std::string &name, s16 value);
+       bool setU16(const std::string &name, u16 value);
+       bool setS32(const std::string &name, s32 value);
+       bool setU64(const std::string &name, u64 value);
+       bool setFloat(const std::string &name, float value);
+       bool setV2F(const std::string &name, v2f value);
+       bool setV3F(const std::string &name, v3f value);
+       bool setFlagStr(const std::string &name, u32 flags,
+               const FlagDesc *flagdesc = nullptr, u32 flagmask = U32_MAX);
+       bool setNoiseParams(const std::string &name, const NoiseParams &np,
+               bool set_default=false);
+       // N.B. if setStruct() is used to write a non-POD aggregate type,
+       // the behavior is undefined.
+       bool setStruct(const std::string &name, const std::string &format, void *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 setV2F(std::string name, v2f value)
-       {
-               std::ostringstream os;
-               os<<"("<<value.X<<","<<value.Y<<")";
-               set(name, os.str());
-       }
-
-       void setS16(std::string name, s16 value)
-       {
-               set(name, itos(value));
-       }
-
-       void setS32(std::string name, s32 value)
-       {
-               set(name, itos(value));
-       }
-
-       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();
-       }
-
-       void updateValue(Settings &other, const std::string &name)
-       {
-               JMutexAutoLock lock(m_mutex);
-
-               if(&other == this)
-                       return;
-
-               try{
-                       std::string val = other.get(name);
-                       m_settings[name] = val;
-               } catch(SettingNotFoundException &e){
-               }
-
-               return;
-       }
-
-       void update(Settings &other)
-       {
-               JMutexAutoLock lock(m_mutex);
-               JMutexAutoLock lock2(other.m_mutex);
-
-               if(&other == this)
-                       return;
-
-               for(core::map<std::string, std::string>::Iterator
-                               i = other.m_settings.getIterator();
-                               i.atEnd() == false; i++)
-               {
-                       m_settings[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[i.getNode()->getKey()] = i.getNode()->getValue();
-               }
-
-               return;
-       }
-
-       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;
+       // remove a setting
+       bool remove(const std::string &name);
+       void clear();
+       void clearDefaults();
+       void updateValue(const Settings &other, const std::string &name);
+       void update(const Settings &other);
+
+       /**************
+        * Miscellany *
+        **************/
+
+       void setDefault(const std::string &name, const FlagDesc *flagdesc, u32 flags);
+       // Takes the provided setting values and uses them as new defaults
+       void overrideDefaults(Settings *other);
+       const FlagDesc *getFlagDescFallback(const std::string &name) const;
+
+       void registerChangedCallback(const std::string &name,
+               SettingsChangedCallback cbf, void *userdata = NULL);
+       void deregisterChangedCallback(const std::string &name,
+               SettingsChangedCallback cbf, void *userdata = NULL);
 
-       }
+private:
+       void updateNoLock(const Settings &other);
+       void clearNoLock();
+       void clearDefaultsNoLock();
 
-       Settings & operator=(Settings &other)
-       {
-               JMutexAutoLock lock(m_mutex);
-               JMutexAutoLock lock2(other.m_mutex);
+       void doCallbacks(const std::string &name) const;
 
-               if(&other == this)
-                       return *this;
+       SettingEntries m_settings;
+       SettingEntries m_defaults;
+       std::unordered_map<std::string, const FlagDesc *> m_flags;
 
-               clear();
-               (*this) += other;
+       SettingsCallbackMap m_callbacks;
 
-               return *this;
-       }
+       mutable std::mutex m_callback_mutex;
 
-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
+       mutable std::mutex m_mutex;
 
+};