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