18d6ade7245c185fad1c1cc457e15c6ffee81f9b
[oweals/minetest.git] / src / settings.h
1 /*
2 Minetest-c55
3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #ifndef SETTINGS_HEADER
21 #define SETTINGS_HEADER
22
23 #include "irrlichttypes_bloated.h"
24 #include <string>
25 #include <jthread.h>
26 #include <jmutex.h>
27 #include <jmutexautolock.h>
28 #include "strfnd.h"
29 #include <iostream>
30 #include <fstream>
31 #include <sstream>
32 #include "debug.h"
33 #include "log.h"
34 #include "util/string.h"
35
36 enum ValueType
37 {
38         VALUETYPE_STRING,
39         VALUETYPE_FLAG // Doesn't take any arguments
40 };
41
42 struct ValueSpec
43 {
44         ValueSpec(ValueType a_type, const char *a_help=NULL)
45         {
46                 type = a_type;
47                 help = a_help;
48         }
49         ValueType type;
50         const char *help;
51 };
52
53 class Settings
54 {
55 public:
56         Settings()
57         {
58                 m_mutex.Init();
59         }
60
61         void writeLines(std::ostream &os)
62         {
63                 JMutexAutoLock lock(m_mutex);
64
65                 for(core::map<std::string, std::string>::Iterator
66                                 i = m_settings.getIterator();
67                                 i.atEnd() == false; i++)
68                 {
69                         std::string name = i.getNode()->getKey();
70                         std::string value = i.getNode()->getValue();
71                         os<<name<<" = "<<value<<"\n";
72                 }
73         }
74   
75         // return all keys used 
76         std::vector<std::string> getNames(){
77                 std::vector<std::string> names;
78                 for(core::map<std::string, std::string>::Iterator
79                                 i = m_settings.getIterator();
80                                 i.atEnd() == false; i++)
81                 {
82                         std::string name = i.getNode()->getKey();
83                         names.push_back(name);
84                 }
85                 return names;  
86         }
87
88         // remove a setting
89         bool remove(const std::string& name)
90         {
91                 return m_settings.remove(name);
92         }
93
94
95         bool parseConfigLine(const std::string &line)
96         {
97                 JMutexAutoLock lock(m_mutex);
98
99                 std::string trimmedline = trim(line);
100
101                 // Ignore empty lines and comments
102                 if(trimmedline.size() == 0 || trimmedline[0] == '#')
103                         return true;
104
105                 //infostream<<"trimmedline=\""<<trimmedline<<"\""<<std::endl;
106
107                 Strfnd sf(trim(line));
108
109                 std::string name = sf.next("=");
110                 name = trim(name);
111
112                 if(name == "")
113                         return true;
114
115                 std::string value = sf.next("\n");
116                 value = trim(value);
117
118                 /*infostream<<"Config name=\""<<name<<"\" value=\""
119                                 <<value<<"\""<<std::endl;*/
120
121                 m_settings[name] = value;
122
123                 return true;
124         }
125
126         void parseConfigLines(std::istream &is, const std::string &endstring)
127         {
128                 for(;;){
129                         if(is.eof())
130                                 break;
131                         std::string line;
132                         std::getline(is, line);
133                         std::string trimmedline = trim(line);
134                         if(endstring != ""){
135                                 if(trimmedline == endstring)
136                                         break;
137                         }
138                         parseConfigLine(line);
139                 }
140         }
141
142         // Returns false on EOF
143         bool parseConfigObject(std::istream &is)
144         {
145                 if(is.eof())
146                         return false;
147
148                 /*
149                         NOTE: This function might be expanded to allow multi-line
150                               settings.
151                 */
152                 std::string line;
153                 std::getline(is, line);
154                 //infostream<<"got line: \""<<line<<"\""<<std::endl;
155
156                 return parseConfigLine(line);
157         }
158
159         /*
160                 Read configuration file
161
162                 Returns true on success
163         */
164         bool readConfigFile(const char *filename)
165         {
166                 std::ifstream is(filename);
167                 if(is.good() == false)
168                         return false;
169
170                 /*infostream<<"Parsing configuration file: \""
171                                 <<filename<<"\""<<std::endl;*/
172
173                 while(parseConfigObject(is));
174
175                 return true;
176         }
177
178         /*
179                 Reads a configuration object from stream (usually a single line)
180                 and adds it to dst.
181
182                 Preserves comments and empty lines.
183
184                 Settings that were added to dst are also added to updated.
185                 key of updated is setting name, value of updated is dummy.
186
187                 Returns false on EOF
188         */
189         bool getUpdatedConfigObject(std::istream &is,
190                         core::list<std::string> &dst,
191                         core::map<std::string, bool> &updated,
192                         bool &value_changed)
193         {
194                 JMutexAutoLock lock(m_mutex);
195
196                 if(is.eof())
197                         return false;
198
199                 // NOTE: This function will be expanded to allow multi-line settings
200                 std::string line;
201                 std::getline(is, line);
202
203                 std::string trimmedline = trim(line);
204
205                 std::string line_end = "";
206                 if(is.eof() == false)
207                         line_end = "\n";
208
209                 // Ignore empty lines and comments
210                 if(trimmedline.size() == 0 || trimmedline[0] == '#')
211                 {
212                         dst.push_back(line+line_end);
213                         return true;
214                 }
215
216                 Strfnd sf(trim(line));
217
218                 std::string name = sf.next("=");
219                 name = trim(name);
220
221                 if(name == "")
222                 {
223                         dst.push_back(line+line_end);
224                         return true;
225                 }
226
227                 std::string value = sf.next("\n");
228                 value = trim(value);
229
230                 if(m_settings.find(name))
231                 {
232                         std::string newvalue = m_settings[name];
233
234                         if(newvalue != value)
235                         {
236                                 infostream<<"Changing value of \""<<name<<"\" = \""
237                                                 <<value<<"\" -> \""<<newvalue<<"\""
238                                                 <<std::endl;
239                                 value_changed = true;
240                         }
241
242                         dst.push_back(name + " = " + newvalue + line_end);
243
244                         updated[name] = true;
245                 }
246
247                 return true;
248         }
249
250         /*
251                 Updates configuration file
252
253                 Returns true on success
254         */
255         bool updateConfigFile(const char *filename)
256         {
257                 infostream<<"Updating configuration file: \""
258                                 <<filename<<"\""<<std::endl;
259
260                 core::list<std::string> objects;
261                 core::map<std::string, bool> updated;
262                 bool something_actually_changed = false;
263
264                 // Read and modify stuff
265                 {
266                         std::ifstream is(filename);
267                         if(is.good() == false)
268                         {
269                                 infostream<<"updateConfigFile():"
270                                                 " Error opening configuration file"
271                                                 " for reading: \""
272                                                 <<filename<<"\""<<std::endl;
273                         }
274                         else
275                         {
276                                 while(getUpdatedConfigObject(is, objects, updated,
277                                                 something_actually_changed));
278                         }
279                 }
280
281                 JMutexAutoLock lock(m_mutex);
282
283                 // If something not yet determined to have been changed, check if
284                 // any new stuff was added
285                 if(!something_actually_changed){
286                         for(core::map<std::string, std::string>::Iterator
287                                         i = m_settings.getIterator();
288                                         i.atEnd() == false; i++)
289                         {
290                                 if(updated.find(i.getNode()->getKey()))
291                                         continue;
292                                 something_actually_changed = true;
293                                 break;
294                         }
295                 }
296
297                 // If nothing was actually changed, skip writing the file
298                 if(!something_actually_changed){
299                         infostream<<"Skipping writing of "<<filename
300                                         <<" because content wouldn't be modified"<<std::endl;
301                         return true;
302                 }
303
304                 // Write stuff back
305                 {
306                         std::ofstream os(filename);
307                         if(os.good() == false)
308                         {
309                                 errorstream<<"Error opening configuration file"
310                                                 " for writing: \""
311                                                 <<filename<<"\""<<std::endl;
312                                 return false;
313                         }
314
315                         /*
316                                 Write updated stuff
317                         */
318                         for(core::list<std::string>::Iterator
319                                         i = objects.begin();
320                                         i != objects.end(); i++)
321                         {
322                                 os<<(*i);
323                         }
324
325                         /*
326                                 Write stuff that was not already in the file
327                         */
328                         for(core::map<std::string, std::string>::Iterator
329                                         i = m_settings.getIterator();
330                                         i.atEnd() == false; i++)
331                         {
332                                 if(updated.find(i.getNode()->getKey()))
333                                         continue;
334                                 std::string name = i.getNode()->getKey();
335                                 std::string value = i.getNode()->getValue();
336                                 infostream<<"Adding \""<<name<<"\" = \""<<value<<"\""
337                                                 <<std::endl;
338                                 os<<name<<" = "<<value<<"\n";
339                         }
340                 }
341
342                 return true;
343         }
344
345         /*
346                 NOTE: Types of allowed_options are ignored
347
348                 returns true on success
349         */
350         bool parseCommandLine(int argc, char *argv[],
351                         core::map<std::string, ValueSpec> &allowed_options)
352         {
353                 int nonopt_index = 0;
354                 int i=1;
355                 for(;;)
356                 {
357                         if(i >= argc)
358                                 break;
359                         std::string argname = argv[i];
360                         if(argname.substr(0, 2) != "--")
361                         {
362                                 // If option doesn't start with -, read it in as nonoptX
363                                 if(argname[0] != '-'){
364                                         std::string name = "nonopt";
365                                         name += itos(nonopt_index);
366                                         set(name, argname);
367                                         nonopt_index++;
368                                         i++;
369                                         continue;
370                                 }
371                                 errorstream<<"Invalid command-line parameter \""
372                                                 <<argname<<"\": --<option> expected."<<std::endl;
373                                 return false;
374                         }
375                         i++;
376
377                         std::string name = argname.substr(2);
378
379                         core::map<std::string, ValueSpec>::Node *n;
380                         n = allowed_options.find(name);
381                         if(n == NULL)
382                         {
383                                 errorstream<<"Unknown command-line parameter \""
384                                                 <<argname<<"\""<<std::endl;
385                                 return false;
386                         }
387
388                         ValueType type = n->getValue().type;
389
390                         std::string value = "";
391
392                         if(type == VALUETYPE_FLAG)
393                         {
394                                 value = "true";
395                         }
396                         else
397                         {
398                                 if(i >= argc)
399                                 {
400                                         errorstream<<"Invalid command-line parameter \""
401                                                         <<name<<"\": missing value"<<std::endl;
402                                         return false;
403                                 }
404                                 value = argv[i];
405                                 i++;
406                         }
407
408
409                         infostream<<"Valid command-line parameter: \""
410                                         <<name<<"\" = \""<<value<<"\""
411                                         <<std::endl;
412                         set(name, value);
413                 }
414
415                 return true;
416         }
417
418         void set(std::string name, std::string value)
419         {
420                 JMutexAutoLock lock(m_mutex);
421
422                 m_settings[name] = value;
423         }
424
425         void set(std::string name, const char *value)
426         {
427                 JMutexAutoLock lock(m_mutex);
428
429                 m_settings[name] = value;
430         }
431
432
433         void setDefault(std::string name, std::string value)
434         {
435                 JMutexAutoLock lock(m_mutex);
436
437                 m_defaults[name] = value;
438         }
439
440         bool exists(std::string name)
441         {
442                 JMutexAutoLock lock(m_mutex);
443
444                 return (m_settings.find(name) || m_defaults.find(name));
445         }
446
447         std::string get(std::string name)
448         {
449                 JMutexAutoLock lock(m_mutex);
450
451                 core::map<std::string, std::string>::Node *n;
452                 n = m_settings.find(name);
453                 if(n == NULL)
454                 {
455                         n = m_defaults.find(name);
456                         if(n == NULL)
457                         {
458                                 throw SettingNotFoundException("Setting not found");
459                         }
460                 }
461
462                 return n->getValue();
463         }
464
465         bool getBool(std::string name)
466         {
467                 return is_yes(get(name));
468         }
469
470         bool getFlag(std::string name)
471         {
472                 try
473                 {
474                         return getBool(name);
475                 }
476                 catch(SettingNotFoundException &e)
477                 {
478                         return false;
479                 }
480         }
481
482         // Asks if empty
483         bool getBoolAsk(std::string name, std::string question, bool def)
484         {
485                 // If it is in settings
486                 if(exists(name))
487                         return getBool(name);
488
489                 std::string s;
490                 char templine[10];
491                 std::cout<<question<<" [y/N]: ";
492                 std::cin.getline(templine, 10);
493                 s = templine;
494
495                 if(s == "")
496                         return def;
497
498                 return is_yes(s);
499         }
500
501         float getFloat(std::string name)
502         {
503                 return stof(get(name));
504         }
505
506         u16 getU16(std::string name)
507         {
508                 return stoi(get(name), 0, 65535);
509         }
510
511         u16 getU16Ask(std::string name, std::string question, u16 def)
512         {
513                 // If it is in settings
514                 if(exists(name))
515                         return getU16(name);
516
517                 std::string s;
518                 char templine[10];
519                 std::cout<<question<<" ["<<def<<"]: ";
520                 std::cin.getline(templine, 10);
521                 s = templine;
522
523                 if(s == "")
524                         return def;
525
526                 return stoi(s, 0, 65535);
527         }
528
529         s16 getS16(std::string name)
530         {
531                 return stoi(get(name), -32768, 32767);
532         }
533
534         s32 getS32(std::string name)
535         {
536                 return stoi(get(name));
537         }
538
539         v3f getV3F(std::string name)
540         {
541                 v3f value;
542                 Strfnd f(get(name));
543                 f.next("(");
544                 value.X = stof(f.next(","));
545                 value.Y = stof(f.next(","));
546                 value.Z = stof(f.next(")"));
547                 return value;
548         }
549
550         v2f getV2F(std::string name)
551         {
552                 v2f value;
553                 Strfnd f(get(name));
554                 f.next("(");
555                 value.X = stof(f.next(","));
556                 value.Y = stof(f.next(")"));
557                 return value;
558         }
559
560         u64 getU64(std::string name)
561         {
562                 u64 value = 0;
563                 std::string s = get(name);
564                 std::istringstream ss(s);
565                 ss>>value;
566                 return value;
567         }
568
569 //template<typename T> struct alignment_trick { char c; T member; };
570 //#define ALIGNOF(type) offsetof (alignment_trick<type>, member)
571 #ifdef _WIN32
572         #define ALIGNOF(x) __alignof(x)
573 #else
574         #define ALIGNOF(x) __alignof__(x)
575 #endif
576 #define PADDING(x, y) ((ALIGNOF(y) - ((uintptr_t)(x) & (ALIGNOF(y) - 1))) & (ALIGNOF(y) - 1))
577 #ifdef _WIN32
578         #define strtok_r(x, y, z) strtok_s(x, y, z)
579         #define strtof(x, y) (float)strtod(x, y)
580         #define strtoll(x, y, z) _strtoi64(x, y, z)
581         #define strtoull(x, y, z) _strtoui64(x, y, z)
582 #endif
583
584 typedef long long int s64; //to be added to src/irrlichttypes.h later
585
586         template <class T> T *getStruct(std::string name, std::string format)
587         {
588                 size_t len = sizeof(T);
589                 std::vector<std::string *> strs_alloced;
590                 std::string *str;
591                 std::string valstr = get(name);
592                 char *s = &valstr[0];
593                 T *buf = new T;
594                 char *bufpos = (char *)buf;
595                 char *f, *snext;
596                 size_t pos;
597
598                 char *fmtpos, *fmt = &format[0];
599                 while ((f = strtok_r(fmt, ",", &fmtpos)) && s) {
600                         fmt = NULL;
601
602                         bool is_unsigned = false;
603                         int width = 0;
604                         char valtype = *f;
605
606                         width = (int)strtol(f + 1, &f, 10);
607                         if (width && valtype == 's')
608                                 valtype = 'i';
609
610                         switch (valtype) {
611                                 case 'u':
612                                         is_unsigned = true;
613                                         /* FALLTHROUGH */
614                                 case 'i':
615                                         if (width == 16) {
616                                                 bufpos += PADDING(bufpos, u16);
617                                                 if ((bufpos - (char *)buf) + sizeof(u16) <= len) {
618                                                         if (is_unsigned)
619                                                                 *(u16 *)bufpos = (u16)strtoul(s, &s, 10);
620                                                         else
621                                                                 *(s16 *)bufpos = (s16)strtol(s, &s, 10);
622                                                 }
623                                                 bufpos += sizeof(u16);
624                                         } else if (width == 32) {
625                                                 bufpos += PADDING(bufpos, u32);
626                                                 if ((bufpos - (char *)buf) + sizeof(u32) <= len) {
627                                                         if (is_unsigned)
628                                                                 *(u32 *)bufpos = (u32)strtoul(s, &s, 10);
629                                                         else
630                                                                 *(s32 *)bufpos = (s32)strtol(s, &s, 10);
631                                                 }
632                                                 bufpos += sizeof(u32);
633                                         } else if (width == 64) {
634                                                 bufpos += PADDING(bufpos, u64);
635                                                 if ((bufpos - (char *)buf) + sizeof(u64) <= len) {
636                                                         if (is_unsigned)
637                                                                 *(u64 *)bufpos = (u64)strtoull(s, &s, 10);
638                                                         else
639                                                                 *(s64 *)bufpos = (s64)strtoll(s, &s, 10);
640                                                 }
641                                                 bufpos += sizeof(u64);
642                                         }
643                                         s = strchr(s, ',');
644                                         break;
645                                 case 'b':
646                                         snext = strchr(s, ',');
647                                         if (snext)
648                                                 *snext++ = 0;
649
650                                         bufpos += PADDING(bufpos, bool);
651                                         if ((bufpos - (char *)buf) + sizeof(bool) <= len)
652                                                 *(bool *)bufpos = is_yes(std::string(s));
653                                         bufpos += sizeof(bool);
654
655                                         s = snext;
656                                         break;
657                                 case 'f':
658                                         bufpos += PADDING(bufpos, float);
659                                         if ((bufpos - (char *)buf) + sizeof(float) <= len)
660                                                 *(float *)bufpos = strtof(s, &s);
661                                         bufpos += sizeof(float);
662
663                                         s = strchr(s, ',');
664                                         break;
665                                 case 's':
666                                         while (*s == ' ' || *s == '\t')
667                                                 s++;
668                                         if (*s++ != '"') //error, expected string
669                                                 goto fail;
670                                         snext = s;
671
672                                         while (snext[0] && !(snext[-1] != '\\' && snext[0] == '"'))
673                                                 snext++;
674                                         *snext++ = 0;
675
676                                         bufpos += PADDING(bufpos, std::string *);
677
678                                         str = new std::string(s);
679                                         pos = 0;
680                                         while ((pos = str->find("\\\"", pos)) != std::string::npos)
681                                                 str->erase(pos, 1);
682
683                                         if ((bufpos - (char *)buf) + sizeof(std::string *) <= len)
684                                                 *(std::string **)bufpos = str;
685                                         bufpos += sizeof(std::string *);
686                                         strs_alloced.push_back(str);
687
688                                         s = *snext ? snext + 1 : NULL;
689                                         break;
690                                 case 'v':
691                                         while (*s == ' ' || *s == '\t')
692                                                 s++;
693                                         if (*s++ != '(') //error, expected vector
694                                                 goto fail;
695
696                                         if (width == 2) {
697                                                 bufpos += PADDING(bufpos, v2f);
698
699                                                 if ((bufpos - (char *)buf) + sizeof(v2f) <= len) {
700                                                 v2f *v = (v2f *)bufpos;
701                                                         v->X = strtof(s, &s);
702                                                         s++;
703                                                         v->Y = strtof(s, &s);
704                                                 }
705
706                                                 bufpos += sizeof(v2f);
707                                         } else if (width == 3) {
708                                                 bufpos += PADDING(bufpos, v3f);
709                                                 if ((bufpos - (char *)buf) + sizeof(v3f) <= len) {
710                                                         v3f *v = (v3f *)bufpos;
711                                                         v->X = strtof(s, &s);
712                                                         s++;
713                                                         v->Y = strtof(s, &s);
714                                                         s++;
715                                                         v->Z = strtof(s, &s);
716                                                 }
717
718                                                 bufpos += sizeof(v3f);
719                                         }
720                                         s = strchr(s, ',');
721                                         break;
722                                 default: //error, invalid format specifier
723                                         goto fail;
724                         }
725
726                         if (s && *s == ',')
727                                 s++;
728
729                         if ((size_t)(bufpos - (char *)buf) > len) //error, buffer too small
730                                 goto fail;
731                 }
732
733                 if (f && *f) { //error, mismatched number of fields and values
734 fail:
735                         for (unsigned int i = 0; i != strs_alloced.size(); i++)
736                                 delete strs_alloced[i];
737                         delete buf;
738                         //delete[] buf;
739                         buf = NULL;
740                 }
741
742                 return buf;
743         }
744
745         bool setStruct(std::string name, std::string format, void *value)
746         {
747                 char sbuf[2048];
748                 int sbuflen = sizeof(sbuf) - 1;
749                 sbuf[sbuflen] = 0;
750                 std::string str;
751                 int pos = 0;
752                 size_t fpos;
753                 char *f;
754
755                 char *bufpos = (char *)value;
756                 char *fmtpos, *fmt = &format[0];
757                 while ((f = strtok_r(fmt, ",", &fmtpos))) {
758                         fmt = NULL;
759                         bool is_unsigned = false;
760                         int width = 0, nprinted = 0;
761                         char valtype = *f;
762
763                         width = (int)strtol(f + 1, &f, 10);
764                         if (width && valtype == 's')
765                                 valtype = 'i';
766
767                         switch (valtype) {
768                                 case 'u':
769                                         is_unsigned = true;
770                                         /* FALLTHROUGH */
771                                 case 'i':
772                                         if (width == 16) {
773                                                 bufpos += PADDING(bufpos, u16);
774                                                 nprinted = snprintf(sbuf + pos, sbuflen,
775                                                                         is_unsigned ? "%u, " : "%d, ",
776                                                                         *((u16 *)bufpos));
777                                                 bufpos += sizeof(u16);
778                                         } else if (width == 32) {
779                                                 bufpos += PADDING(bufpos, u32);
780                                                 nprinted = snprintf(sbuf + pos, sbuflen,
781                                                                         is_unsigned ? "%u, " : "%d, ",
782                                                                         *((u32 *)bufpos));
783                                                 bufpos += sizeof(u32);
784                                         } else if (width == 64) {
785                                                 bufpos += PADDING(bufpos, u64);
786                                                 nprinted = snprintf(sbuf + pos, sbuflen,
787                                                                         is_unsigned ? "%llu, " : "%lli, ",
788                                                                         (unsigned long long)*((u64 *)bufpos));
789                                                 bufpos += sizeof(u64);
790                                         }
791                                         break;
792                                 case 'b':
793                                         bufpos += PADDING(bufpos, bool);
794                                         nprinted = snprintf(sbuf + pos, sbuflen, "%s, ",
795                                                                                 *((bool *)bufpos) ? "true" : "false");
796                                         bufpos += sizeof(bool);
797                                         break;
798                                 case 'f':
799                                         bufpos += PADDING(bufpos, float);
800                                         nprinted = snprintf(sbuf + pos, sbuflen, "%f, ",
801                                                                                 *((float *)bufpos));
802                                         bufpos += sizeof(float);
803                                         break;
804                                 case 's':
805                                         bufpos += PADDING(bufpos, std::string *);
806                                         str = **((std::string **)bufpos);
807
808                                         fpos = 0;
809                                         while ((fpos = str.find('"', fpos)) != std::string::npos) {
810                                                 str.insert(fpos, 1, '\\');
811                                                 fpos += 2;
812                                         }
813
814                                         nprinted = snprintf(sbuf + pos, sbuflen, "\"%s\", ",
815                                                                                 (*((std::string **)bufpos))->c_str());
816                                         bufpos += sizeof(std::string *);
817                                         break;
818                                 case 'v':
819                                         if (width == 2) {
820                                                 bufpos += PADDING(bufpos, v2f);
821                                                 v2f *v = (v2f *)bufpos;
822                                                 nprinted = snprintf(sbuf + pos, sbuflen,
823                                                                                         "(%f, %f), ", v->X, v->Y);
824                                                 bufpos += sizeof(v2f);
825                                         } else {
826                                                 bufpos += PADDING(bufpos, v3f);
827                                                 v3f *v = (v3f *)bufpos;
828                                                 nprinted = snprintf(sbuf + pos, sbuflen,
829                                                                                         "(%f, %f, %f), ", v->X, v->Y, v->Z);
830                                                 bufpos += sizeof(v3f);
831                                         }
832                                         break;
833                                 default:
834                                         return false;
835                         }
836                         if (nprinted < 0) //error, buffer too small
837                                 return false;
838                         pos     += nprinted;
839                         sbuflen -= nprinted;
840                 }
841
842                 if (pos >= 2)
843                         sbuf[pos - 2] = 0;
844
845                 set(name, std::string(sbuf));
846                 return true;
847         }
848
849         void setBool(std::string name, bool value)
850         {
851                 if(value)
852                         set(name, "true");
853                 else
854                         set(name, "false");
855         }
856
857         void setFloat(std::string name, float value)
858         {
859                 set(name, ftos(value));
860         }
861
862         void setV3F(std::string name, v3f value)
863         {
864                 std::ostringstream os;
865                 os<<"("<<value.X<<","<<value.Y<<","<<value.Z<<")";
866                 set(name, os.str());
867         }
868
869         void setV2F(std::string name, v2f value)
870         {
871                 std::ostringstream os;
872                 os<<"("<<value.X<<","<<value.Y<<")";
873                 set(name, os.str());
874         }
875
876         void setS16(std::string name, s16 value)
877         {
878                 set(name, itos(value));
879         }
880
881         void setS32(std::string name, s32 value)
882         {
883                 set(name, itos(value));
884         }
885
886         void setU64(std::string name, u64 value)
887         {
888                 std::ostringstream os;
889                 os<<value;
890                 set(name, os.str());
891         }
892
893         void clear()
894         {
895                 JMutexAutoLock lock(m_mutex);
896
897                 m_settings.clear();
898                 m_defaults.clear();
899         }
900
901         void updateValue(Settings &other, const std::string &name)
902         {
903                 JMutexAutoLock lock(m_mutex);
904
905                 if(&other == this)
906                         return;
907
908                 try{
909                         std::string val = other.get(name);
910                         m_settings[name] = val;
911                 } catch(SettingNotFoundException &e){
912                 }
913
914                 return;
915         }
916
917         void update(Settings &other)
918         {
919                 JMutexAutoLock lock(m_mutex);
920                 JMutexAutoLock lock2(other.m_mutex);
921
922                 if(&other == this)
923                         return;
924
925                 for(core::map<std::string, std::string>::Iterator
926                                 i = other.m_settings.getIterator();
927                                 i.atEnd() == false; i++)
928                 {
929                         m_settings[i.getNode()->getKey()] = i.getNode()->getValue();
930                 }
931
932                 for(core::map<std::string, std::string>::Iterator
933                                 i = other.m_defaults.getIterator();
934                                 i.atEnd() == false; i++)
935                 {
936                         m_defaults[i.getNode()->getKey()] = i.getNode()->getValue();
937                 }
938
939                 return;
940         }
941
942         Settings & operator+=(Settings &other)
943         {
944                 JMutexAutoLock lock(m_mutex);
945                 JMutexAutoLock lock2(other.m_mutex);
946
947                 if(&other == this)
948                         return *this;
949
950                 for(core::map<std::string, std::string>::Iterator
951                                 i = other.m_settings.getIterator();
952                                 i.atEnd() == false; i++)
953                 {
954                         m_settings.insert(i.getNode()->getKey(),
955                                         i.getNode()->getValue());
956                 }
957
958                 for(core::map<std::string, std::string>::Iterator
959                                 i = other.m_defaults.getIterator();
960                                 i.atEnd() == false; i++)
961                 {
962                         m_defaults.insert(i.getNode()->getKey(),
963                                         i.getNode()->getValue());
964                 }
965
966                 return *this;
967
968         }
969
970         Settings & operator=(Settings &other)
971         {
972                 JMutexAutoLock lock(m_mutex);
973                 JMutexAutoLock lock2(other.m_mutex);
974
975                 if(&other == this)
976                         return *this;
977
978                 clear();
979                 (*this) += other;
980
981                 return *this;
982         }
983
984 private:
985         core::map<std::string, std::string> m_settings;
986         core::map<std::string, std::string> m_defaults;
987         // All methods that access m_settings/m_defaults directly should lock this.
988         JMutex m_mutex;
989 };
990
991 #endif
992