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