Add Settings::getStruct and Settings::setStruct
[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 #define PADDING(x, y) ((ALIGNOF(y) - ((uintptr_t)(x) & (ALIGNOF(y) - 1))) & (ALIGNOF(y) - 1))
572 typedef int64_t s64;
573
574         void *getStruct(std::string &name, std::string format, size_t len)
575         {
576                 std::vector<std::string *> strs_alloced;
577                 std::string *str;
578                 char *s = &(get(name))[0];
579                 char *buf = new char[len];
580                 char *bufpos = buf;
581                 char *f, *snext;
582                 size_t pos;
583
584                 char *fmt = &format[0];
585                 while ((f = strsep(&fmt, ",")) && s) {
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 (*f) {
595                                 case 'u':
596                                         is_unsigned = true;
597                                         /* FALLTHROUGH */
598                                 case 'i':
599                                         if (width == 16) {
600                                                 bufpos += PADDING(bufpos, u16);
601                                                 if ((bufpos - 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 - 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 - 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 - 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 - 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 - 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 - 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 - 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 ((bufpos - 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 (int i = 0; i != strs_alloced.size(); i++)
720                                 delete strs_alloced[i];
721                         delete[] buf;
722                         buf = NULL;
723                 }
724
725                 return buf;
726         }
727
728         bool setStruct(std::string name, std::string format, void *value)
729         {
730                 char sbuf[2048];
731                 int sbuflen = sizeof(sbuf) - 1;
732                 sbuf[sbuflen] = 0;
733                 std::string str;
734                 int pos = 0;
735                 size_t fpos;
736                 char *f;
737
738                 int nprinted;
739                 char *bufpos = (char *)value;
740                 char *fmt = &format[0];
741
742                 while ((f = strsep(&fmt, ","))) {
743                         bool is_unsigned = false;
744                         int width = 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                                                                                                  *((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