]> git.lizzy.rs Git - dragonfireclient.git/blob - src/settings.h
Make dungeon masters though and make oerkkis disappear when they get to you (because...
[dragonfireclient.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 General Public License as published by
7 the Free Software Foundation; either version 2 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 General Public License for more details.
14
15 You should have received a copy of the GNU 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 "common_irrlicht.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 "utility.h"
34
35 enum ValueType
36 {
37         VALUETYPE_STRING,
38         VALUETYPE_FLAG // Doesn't take any arguments
39 };
40
41 struct ValueSpec
42 {
43         ValueSpec(ValueType a_type, const char *a_help=NULL)
44         {
45                 type = a_type;
46                 help = a_help;
47         }
48         ValueType type;
49         const char *help;
50 };
51
52 class Settings
53 {
54 public:
55         Settings()
56         {
57                 m_mutex.Init();
58         }
59
60         void writeLines(std::ostream &os)
61         {
62                 JMutexAutoLock lock(m_mutex);
63                 
64                 for(core::map<std::string, std::string>::Iterator
65                                 i = m_settings.getIterator();
66                                 i.atEnd() == false; i++)
67                 {
68                         std::string name = i.getNode()->getKey();
69                         std::string value = i.getNode()->getValue();
70                         os<<name<<" = "<<value<<"\n";
71                 }
72         }
73
74         bool parseConfigLine(const std::string &line)
75         {
76                 JMutexAutoLock lock(m_mutex);
77                 
78                 std::string trimmedline = trim(line);
79                 
80                 // Ignore comments
81                 if(trimmedline[0] == '#')
82                         return true;
83
84                 //dstream<<"trimmedline=\""<<trimmedline<<"\""<<std::endl;
85
86                 Strfnd sf(trim(line));
87
88                 std::string name = sf.next("=");
89                 name = trim(name);
90
91                 if(name == "")
92                         return true;
93                 
94                 std::string value = sf.next("\n");
95                 value = trim(value);
96
97                 /*dstream<<"Config name=\""<<name<<"\" value=\""
98                                 <<value<<"\""<<std::endl;*/
99                 
100                 m_settings[name] = value;
101                 
102                 return true;
103         }
104
105         void parseConfigLines(std::istream &is, const std::string &endstring)
106         {
107                 for(;;){
108                         if(is.eof())
109                                 break;
110                         std::string line;
111                         std::getline(is, line);
112                         std::string trimmedline = trim(line);
113                         if(endstring != ""){
114                                 if(trimmedline == endstring)
115                                         break;
116                         }
117                         parseConfigLine(line);
118                 }
119         }
120
121         // Returns false on EOF
122         bool parseConfigObject(std::istream &is)
123         {
124                 if(is.eof())
125                         return false;
126                 
127                 /*
128                         NOTE: This function might be expanded to allow multi-line
129                               settings.
130                 */
131                 std::string line;
132                 std::getline(is, line);
133                 //dstream<<"got line: \""<<line<<"\""<<std::endl;
134
135                 return parseConfigLine(line);
136         }
137
138         /*
139                 Read configuration file
140
141                 Returns true on success
142         */
143         bool readConfigFile(const char *filename)
144         {
145                 std::ifstream is(filename);
146                 if(is.good() == false)
147                 {
148                         dstream<<"Error opening configuration file \""
149                                         <<filename<<"\""<<std::endl;
150                         return false;
151                 }
152
153                 dstream<<"Parsing configuration file: \""
154                                 <<filename<<"\""<<std::endl;
155                                 
156                 while(parseConfigObject(is));
157                 
158                 return true;
159         }
160
161         /*
162                 Reads a configuration object from stream (usually a single line)
163                 and adds it to dst.
164                 
165                 Preserves comments and empty lines.
166
167                 Settings that were added to dst are also added to updated.
168                 key of updated is setting name, value of updated is dummy.
169
170                 Returns false on EOF
171         */
172         bool getUpdatedConfigObject(std::istream &is,
173                         core::list<std::string> &dst,
174                         core::map<std::string, bool> &updated)
175         {
176                 JMutexAutoLock lock(m_mutex);
177                 
178                 if(is.eof())
179                         return false;
180                 
181                 // NOTE: This function will be expanded to allow multi-line settings
182                 std::string line;
183                 std::getline(is, line);
184
185                 std::string trimmedline = trim(line);
186
187                 std::string line_end = "";
188                 if(is.eof() == false)
189                         line_end = "\n";
190                 
191                 // Ignore comments
192                 if(trimmedline[0] == '#')
193                 {
194                         dst.push_back(line+line_end);
195                         return true;
196                 }
197
198                 Strfnd sf(trim(line));
199
200                 std::string name = sf.next("=");
201                 name = trim(name);
202
203                 if(name == "")
204                 {
205                         dst.push_back(line+line_end);
206                         return true;
207                 }
208                 
209                 std::string value = sf.next("\n");
210                 value = trim(value);
211                 
212                 if(m_settings.find(name))
213                 {
214                         std::string newvalue = m_settings[name];
215                         
216                         if(newvalue != value)
217                         {
218                                 dstream<<"Changing value of \""<<name<<"\" = \""
219                                                 <<value<<"\" -> \""<<newvalue<<"\""
220                                                 <<std::endl;
221                         }
222
223                         dst.push_back(name + " = " + newvalue + line_end);
224
225                         updated[name] = true;
226                 }
227                 
228                 return true;
229         }
230
231         /*
232                 Updates configuration file
233
234                 Returns true on success
235         */
236         bool updateConfigFile(const char *filename)
237         {
238                 dstream<<"Updating configuration file: \""
239                                 <<filename<<"\""<<std::endl;
240                 
241                 core::list<std::string> objects;
242                 core::map<std::string, bool> updated;
243                 
244                 // Read and modify stuff
245                 {
246                         std::ifstream is(filename);
247                         if(is.good() == false)
248                         {
249                                 dstream<<"INFO: updateConfigFile():"
250                                                 " Error opening configuration file"
251                                                 " for reading: \""
252                                                 <<filename<<"\""<<std::endl;
253                         }
254                         else
255                         {
256                                 while(getUpdatedConfigObject(is, objects, updated));
257                         }
258                 }
259                 
260                 JMutexAutoLock lock(m_mutex);
261                 
262                 // Write stuff back
263                 {
264                         std::ofstream os(filename);
265                         if(os.good() == false)
266                         {
267                                 dstream<<"Error opening configuration file"
268                                                 " for writing: \""
269                                                 <<filename<<"\""<<std::endl;
270                                 return false;
271                         }
272                         
273                         /*
274                                 Write updated stuff
275                         */
276                         for(core::list<std::string>::Iterator
277                                         i = objects.begin();
278                                         i != objects.end(); i++)
279                         {
280                                 os<<(*i);
281                         }
282
283                         /*
284                                 Write stuff that was not already in the file
285                         */
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                                 std::string name = i.getNode()->getKey();
293                                 std::string value = i.getNode()->getValue();
294                                 dstream<<"Adding \""<<name<<"\" = \""<<value<<"\""
295                                                 <<std::endl;
296                                 os<<name<<" = "<<value<<"\n";
297                         }
298                 }
299                 
300                 return true;
301         }
302
303         /*
304                 NOTE: Types of allowed_options are ignored
305
306                 returns true on success
307         */
308         bool parseCommandLine(int argc, char *argv[],
309                         core::map<std::string, ValueSpec> &allowed_options)
310         {
311                 int i=1;
312                 for(;;)
313                 {
314                         if(i >= argc)
315                                 break;
316                         std::string argname = argv[i];
317                         if(argname.substr(0, 2) != "--")
318                         {
319                                 dstream<<"Invalid command-line parameter \""
320                                                 <<argname<<"\": --<option> expected."<<std::endl;
321                                 return false;
322                         }
323                         i++;
324
325                         std::string name = argname.substr(2);
326
327                         core::map<std::string, ValueSpec>::Node *n;
328                         n = allowed_options.find(name);
329                         if(n == NULL)
330                         {
331                                 dstream<<"Unknown command-line parameter \""
332                                                 <<argname<<"\""<<std::endl;
333                                 return false;
334                         }
335
336                         ValueType type = n->getValue().type;
337
338                         std::string value = "";
339                         
340                         if(type == VALUETYPE_FLAG)
341                         {
342                                 value = "true";
343                         }
344                         else
345                         {
346                                 if(i >= argc)
347                                 {
348                                         dstream<<"Invalid command-line parameter \""
349                                                         <<name<<"\": missing value"<<std::endl;
350                                         return false;
351                                 }
352                                 value = argv[i];
353                                 i++;
354                         }
355                         
356
357                         dstream<<"Valid command-line parameter: \""
358                                         <<name<<"\" = \""<<value<<"\""
359                                         <<std::endl;
360                         set(name, value);
361                 }
362
363                 return true;
364         }
365
366         void set(std::string name, std::string value)
367         {
368                 JMutexAutoLock lock(m_mutex);
369                 
370                 m_settings[name] = value;
371         }
372
373         void set(std::string name, const char *value)
374         {
375                 JMutexAutoLock lock(m_mutex);
376
377                 m_settings[name] = value;
378         }
379
380
381         void setDefault(std::string name, std::string value)
382         {
383                 JMutexAutoLock lock(m_mutex);
384                 
385                 m_defaults[name] = value;
386         }
387
388         bool exists(std::string name)
389         {
390                 JMutexAutoLock lock(m_mutex);
391                 
392                 return (m_settings.find(name) || m_defaults.find(name));
393         }
394
395         std::string get(std::string name)
396         {
397                 JMutexAutoLock lock(m_mutex);
398                 
399                 core::map<std::string, std::string>::Node *n;
400                 n = m_settings.find(name);
401                 if(n == NULL)
402                 {
403                         n = m_defaults.find(name);
404                         if(n == NULL)
405                         {
406                                 dstream<<"INFO: Settings: Setting not found: \""
407                                                 <<name<<"\""<<std::endl;
408                                 throw SettingNotFoundException("Setting not found");
409                         }
410                 }
411
412                 return n->getValue();
413         }
414
415         bool getBool(std::string name)
416         {
417                 return is_yes(get(name));
418         }
419         
420         bool getFlag(std::string name)
421         {
422                 try
423                 {
424                         return getBool(name);
425                 }
426                 catch(SettingNotFoundException &e)
427                 {
428                         return false;
429                 }
430         }
431
432         // Asks if empty
433         bool getBoolAsk(std::string name, std::string question, bool def)
434         {
435                 // If it is in settings
436                 if(exists(name))
437                         return getBool(name);
438                 
439                 std::string s;
440                 char templine[10];
441                 std::cout<<question<<" [y/N]: ";
442                 std::cin.getline(templine, 10);
443                 s = templine;
444
445                 if(s == "")
446                         return def;
447
448                 return is_yes(s);
449         }
450
451         float getFloat(std::string name)
452         {
453                 return stof(get(name));
454         }
455
456         u16 getU16(std::string name)
457         {
458                 return stoi(get(name), 0, 65535);
459         }
460
461         u16 getU16Ask(std::string name, std::string question, u16 def)
462         {
463                 // If it is in settings
464                 if(exists(name))
465                         return getU16(name);
466                 
467                 std::string s;
468                 char templine[10];
469                 std::cout<<question<<" ["<<def<<"]: ";
470                 std::cin.getline(templine, 10);
471                 s = templine;
472
473                 if(s == "")
474                         return def;
475
476                 return stoi(s, 0, 65535);
477         }
478
479         s16 getS16(std::string name)
480         {
481                 return stoi(get(name), -32768, 32767);
482         }
483
484         s32 getS32(std::string name)
485         {
486                 return stoi(get(name));
487         }
488
489         v3f getV3F(std::string name)
490         {
491                 v3f value;
492                 Strfnd f(get(name));
493                 f.next("(");
494                 value.X = stof(f.next(","));
495                 value.Y = stof(f.next(","));
496                 value.Z = stof(f.next(")"));
497                 return value;
498         }
499
500         v2f getV2F(std::string name)
501         {
502                 v2f value;
503                 Strfnd f(get(name));
504                 f.next("(");
505                 value.X = stof(f.next(","));
506                 value.Y = stof(f.next(")"));
507                 return value;
508         }
509
510         u64 getU64(std::string name)
511         {
512                 u64 value = 0;
513                 std::string s = get(name);
514                 std::istringstream ss(s);
515                 ss>>value;
516                 return value;
517         }
518
519         void setBool(std::string name, bool value)
520         {
521                 if(value)
522                         set(name, "true");
523                 else
524                         set(name, "false");
525         }
526
527         void setS32(std::string name, s32 value)
528         {
529                 set(name, itos(value));
530         }
531
532         void setFloat(std::string name, float value)
533         {
534                 set(name, ftos(value));
535         }
536
537         void setV3F(std::string name, v3f value)
538         {
539                 std::ostringstream os;
540                 os<<"("<<value.X<<","<<value.Y<<","<<value.Z<<")";
541                 set(name, os.str());
542         }
543
544         void setV2F(std::string name, v2f value)
545         {
546                 std::ostringstream os;
547                 os<<"("<<value.X<<","<<value.Y<<")";
548                 set(name, os.str());
549         }
550
551         void setU64(std::string name, u64 value)
552         {
553                 std::ostringstream os;
554                 os<<value;
555                 set(name, os.str());
556         }
557
558         void clear()
559         {
560                 JMutexAutoLock lock(m_mutex);
561                 
562                 m_settings.clear();
563                 m_defaults.clear();
564         }
565
566         void updateValue(Settings &other, const std::string &name)
567         {
568                 JMutexAutoLock lock(m_mutex);
569                 
570                 if(&other == this)
571                         return;
572
573                 try{
574                         std::string val = other.get(name);
575                         m_settings[name] = val;
576                 } catch(SettingNotFoundException &e){
577                 }
578
579                 return;
580         }
581
582         void update(Settings &other)
583         {
584                 JMutexAutoLock lock(m_mutex);
585                 JMutexAutoLock lock2(other.m_mutex);
586                 
587                 if(&other == this)
588                         return;
589
590                 for(core::map<std::string, std::string>::Iterator
591                                 i = other.m_settings.getIterator();
592                                 i.atEnd() == false; i++)
593                 {
594                         m_settings[i.getNode()->getKey()] = i.getNode()->getValue();
595                 }
596                 
597                 for(core::map<std::string, std::string>::Iterator
598                                 i = other.m_defaults.getIterator();
599                                 i.atEnd() == false; i++)
600                 {
601                         m_defaults[i.getNode()->getKey()] = i.getNode()->getValue();
602                 }
603
604                 return;
605         }
606
607         Settings & operator+=(Settings &other)
608         {
609                 JMutexAutoLock lock(m_mutex);
610                 JMutexAutoLock lock2(other.m_mutex);
611                 
612                 if(&other == this)
613                         return *this;
614
615                 for(core::map<std::string, std::string>::Iterator
616                                 i = other.m_settings.getIterator();
617                                 i.atEnd() == false; i++)
618                 {
619                         m_settings.insert(i.getNode()->getKey(),
620                                         i.getNode()->getValue());
621                 }
622                 
623                 for(core::map<std::string, std::string>::Iterator
624                                 i = other.m_defaults.getIterator();
625                                 i.atEnd() == false; i++)
626                 {
627                         m_defaults.insert(i.getNode()->getKey(),
628                                         i.getNode()->getValue());
629                 }
630
631                 return *this;
632
633         }
634
635         Settings & operator=(Settings &other)
636         {
637                 JMutexAutoLock lock(m_mutex);
638                 JMutexAutoLock lock2(other.m_mutex);
639                 
640                 if(&other == this)
641                         return *this;
642
643                 clear();
644                 (*this) += other;
645                 
646                 return *this;
647         }
648
649 private:
650         core::map<std::string, std::string> m_settings;
651         core::map<std::string, std::string> m_defaults;
652         // All methods that access m_settings/m_defaults directly should lock this.
653         JMutex m_mutex;
654 };
655
656 #endif
657