3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
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.
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.
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.
20 #ifndef SETTINGS_HEADER
21 #define SETTINGS_HEADER
23 #include "common_irrlicht.h"
27 #include <jmutexautolock.h>
39 VALUETYPE_FLAG // Doesn't take any arguments
44 ValueSpec(ValueType a_type, const char *a_help=NULL)
61 void writeLines(std::ostream &os)
63 JMutexAutoLock lock(m_mutex);
65 for(core::map<std::string, std::string>::Iterator
66 i = m_settings.getIterator();
67 i.atEnd() == false; i++)
69 std::string name = i.getNode()->getKey();
70 std::string value = i.getNode()->getValue();
71 os<<name<<" = "<<value<<"\n";
75 bool parseConfigLine(const std::string &line)
77 JMutexAutoLock lock(m_mutex);
79 std::string trimmedline = trim(line);
81 // Ignore empty lines and comments
82 if(trimmedline.size() == 0 || trimmedline[0] == '#')
85 //infostream<<"trimmedline=\""<<trimmedline<<"\""<<std::endl;
87 Strfnd sf(trim(line));
89 std::string name = sf.next("=");
95 std::string value = sf.next("\n");
98 /*infostream<<"Config name=\""<<name<<"\" value=\""
99 <<value<<"\""<<std::endl;*/
101 m_settings[name] = value;
106 void parseConfigLines(std::istream &is, const std::string &endstring)
112 std::getline(is, line);
113 std::string trimmedline = trim(line);
115 if(trimmedline == endstring)
118 parseConfigLine(line);
122 // Returns false on EOF
123 bool parseConfigObject(std::istream &is)
129 NOTE: This function might be expanded to allow multi-line
133 std::getline(is, line);
134 //infostream<<"got line: \""<<line<<"\""<<std::endl;
136 return parseConfigLine(line);
140 Read configuration file
142 Returns true on success
144 bool readConfigFile(const char *filename)
146 std::ifstream is(filename);
147 if(is.good() == false)
149 errorstream<<"Error opening configuration file \""
150 <<filename<<"\""<<std::endl;
154 infostream<<"Parsing configuration file: \""
155 <<filename<<"\""<<std::endl;
157 while(parseConfigObject(is));
163 Reads a configuration object from stream (usually a single line)
166 Preserves comments and empty lines.
168 Settings that were added to dst are also added to updated.
169 key of updated is setting name, value of updated is dummy.
173 bool getUpdatedConfigObject(std::istream &is,
174 core::list<std::string> &dst,
175 core::map<std::string, bool> &updated,
178 JMutexAutoLock lock(m_mutex);
183 // NOTE: This function will be expanded to allow multi-line settings
185 std::getline(is, line);
187 std::string trimmedline = trim(line);
189 std::string line_end = "";
190 if(is.eof() == false)
193 // Ignore empty lines and comments
194 if(trimmedline.size() == 0 || trimmedline[0] == '#')
196 dst.push_back(line+line_end);
200 Strfnd sf(trim(line));
202 std::string name = sf.next("=");
207 dst.push_back(line+line_end);
211 std::string value = sf.next("\n");
214 if(m_settings.find(name))
216 std::string newvalue = m_settings[name];
218 if(newvalue != value)
220 infostream<<"Changing value of \""<<name<<"\" = \""
221 <<value<<"\" -> \""<<newvalue<<"\""
223 value_changed = true;
226 dst.push_back(name + " = " + newvalue + line_end);
228 updated[name] = true;
235 Updates configuration file
237 Returns true on success
239 bool updateConfigFile(const char *filename)
241 infostream<<"Updating configuration file: \""
242 <<filename<<"\""<<std::endl;
244 core::list<std::string> objects;
245 core::map<std::string, bool> updated;
246 bool something_actually_changed = false;
248 // Read and modify stuff
250 std::ifstream is(filename);
251 if(is.good() == false)
253 infostream<<"updateConfigFile():"
254 " Error opening configuration file"
256 <<filename<<"\""<<std::endl;
260 while(getUpdatedConfigObject(is, objects, updated,
261 something_actually_changed));
265 JMutexAutoLock lock(m_mutex);
267 // If something not yet determined to have been changed, check if
268 // any new stuff was added
269 if(!something_actually_changed){
270 for(core::map<std::string, std::string>::Iterator
271 i = m_settings.getIterator();
272 i.atEnd() == false; i++)
274 if(updated.find(i.getNode()->getKey()))
276 something_actually_changed = true;
281 // If nothing was actually changed, skip writing the file
282 if(!something_actually_changed){
283 infostream<<"Skipping writing of "<<filename
284 <<" because content wouldn't be modified"<<std::endl;
290 std::ofstream os(filename);
291 if(os.good() == false)
293 errorstream<<"Error opening configuration file"
295 <<filename<<"\""<<std::endl;
302 for(core::list<std::string>::Iterator
304 i != objects.end(); i++)
310 Write stuff that was not already in the file
312 for(core::map<std::string, std::string>::Iterator
313 i = m_settings.getIterator();
314 i.atEnd() == false; i++)
316 if(updated.find(i.getNode()->getKey()))
318 std::string name = i.getNode()->getKey();
319 std::string value = i.getNode()->getValue();
320 infostream<<"Adding \""<<name<<"\" = \""<<value<<"\""
322 os<<name<<" = "<<value<<"\n";
330 NOTE: Types of allowed_options are ignored
332 returns true on success
334 bool parseCommandLine(int argc, char *argv[],
335 core::map<std::string, ValueSpec> &allowed_options)
342 std::string argname = argv[i];
343 if(argname.substr(0, 2) != "--")
345 errorstream<<"Invalid command-line parameter \""
346 <<argname<<"\": --<option> expected."<<std::endl;
351 std::string name = argname.substr(2);
353 core::map<std::string, ValueSpec>::Node *n;
354 n = allowed_options.find(name);
357 errorstream<<"Unknown command-line parameter \""
358 <<argname<<"\""<<std::endl;
362 ValueType type = n->getValue().type;
364 std::string value = "";
366 if(type == VALUETYPE_FLAG)
374 errorstream<<"Invalid command-line parameter \""
375 <<name<<"\": missing value"<<std::endl;
383 infostream<<"Valid command-line parameter: \""
384 <<name<<"\" = \""<<value<<"\""
392 void set(std::string name, std::string value)
394 JMutexAutoLock lock(m_mutex);
396 m_settings[name] = value;
399 void set(std::string name, const char *value)
401 JMutexAutoLock lock(m_mutex);
403 m_settings[name] = value;
407 void setDefault(std::string name, std::string value)
409 JMutexAutoLock lock(m_mutex);
411 m_defaults[name] = value;
414 bool exists(std::string name)
416 JMutexAutoLock lock(m_mutex);
418 return (m_settings.find(name) || m_defaults.find(name));
421 std::string get(std::string name)
423 JMutexAutoLock lock(m_mutex);
425 core::map<std::string, std::string>::Node *n;
426 n = m_settings.find(name);
429 n = m_defaults.find(name);
432 infostream<<"Settings: Setting not found: \""
433 <<name<<"\""<<std::endl;
434 throw SettingNotFoundException("Setting not found");
438 return n->getValue();
441 bool getBool(std::string name)
443 return is_yes(get(name));
446 bool getFlag(std::string name)
450 return getBool(name);
452 catch(SettingNotFoundException &e)
459 bool getBoolAsk(std::string name, std::string question, bool def)
461 // If it is in settings
463 return getBool(name);
467 std::cout<<question<<" [y/N]: ";
468 std::cin.getline(templine, 10);
477 float getFloat(std::string name)
479 return stof(get(name));
482 u16 getU16(std::string name)
484 return stoi(get(name), 0, 65535);
487 u16 getU16Ask(std::string name, std::string question, u16 def)
489 // If it is in settings
495 std::cout<<question<<" ["<<def<<"]: ";
496 std::cin.getline(templine, 10);
502 return stoi(s, 0, 65535);
505 s16 getS16(std::string name)
507 return stoi(get(name), -32768, 32767);
510 s32 getS32(std::string name)
512 return stoi(get(name));
515 v3f getV3F(std::string name)
520 value.X = stof(f.next(","));
521 value.Y = stof(f.next(","));
522 value.Z = stof(f.next(")"));
526 v2f getV2F(std::string name)
531 value.X = stof(f.next(","));
532 value.Y = stof(f.next(")"));
536 u64 getU64(std::string name)
539 std::string s = get(name);
540 std::istringstream ss(s);
545 void setBool(std::string name, bool value)
553 void setS32(std::string name, s32 value)
555 set(name, itos(value));
558 void setFloat(std::string name, float value)
560 set(name, ftos(value));
563 void setV3F(std::string name, v3f value)
565 std::ostringstream os;
566 os<<"("<<value.X<<","<<value.Y<<","<<value.Z<<")";
570 void setV2F(std::string name, v2f value)
572 std::ostringstream os;
573 os<<"("<<value.X<<","<<value.Y<<")";
577 void setU64(std::string name, u64 value)
579 std::ostringstream os;
586 JMutexAutoLock lock(m_mutex);
592 void updateValue(Settings &other, const std::string &name)
594 JMutexAutoLock lock(m_mutex);
600 std::string val = other.get(name);
601 m_settings[name] = val;
602 } catch(SettingNotFoundException &e){
608 void update(Settings &other)
610 JMutexAutoLock lock(m_mutex);
611 JMutexAutoLock lock2(other.m_mutex);
616 for(core::map<std::string, std::string>::Iterator
617 i = other.m_settings.getIterator();
618 i.atEnd() == false; i++)
620 m_settings[i.getNode()->getKey()] = i.getNode()->getValue();
623 for(core::map<std::string, std::string>::Iterator
624 i = other.m_defaults.getIterator();
625 i.atEnd() == false; i++)
627 m_defaults[i.getNode()->getKey()] = i.getNode()->getValue();
633 Settings & operator+=(Settings &other)
635 JMutexAutoLock lock(m_mutex);
636 JMutexAutoLock lock2(other.m_mutex);
641 for(core::map<std::string, std::string>::Iterator
642 i = other.m_settings.getIterator();
643 i.atEnd() == false; i++)
645 m_settings.insert(i.getNode()->getKey(),
646 i.getNode()->getValue());
649 for(core::map<std::string, std::string>::Iterator
650 i = other.m_defaults.getIterator();
651 i.atEnd() == false; i++)
653 m_defaults.insert(i.getNode()->getKey(),
654 i.getNode()->getValue());
661 Settings & operator=(Settings &other)
663 JMutexAutoLock lock(m_mutex);
664 JMutexAutoLock lock2(other.m_mutex);
676 core::map<std::string, std::string> m_settings;
677 core::map<std::string, std::string> m_defaults;
678 // All methods that access m_settings/m_defaults directly should lock this.