xoreos  0.0.5
configman.cpp
Go to the documentation of this file.
1 /* xoreos - A reimplementation of BioWare's Aurora engine
2  *
3  * xoreos is the legal property of its developers, whose names
4  * can be found in the AUTHORS file distributed with this source
5  * distribution.
6  *
7  * xoreos is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 3
10  * of the License, or (at your option) any later version.
11  *
12  * xoreos is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with xoreos. If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 // Inspired by ScummVM's config file and manager code
22 
27 #include <cassert>
28 
29 #include "src/common/configman.h"
30 #include "src/common/strutil.h"
31 #include "src/common/readfile.h"
32 #include "src/common/writefile.h"
33 #include "src/common/filepath.h"
34 #include "src/common/configfile.h"
35 
37 
38 namespace Common {
39 
40 const char *ConfigManager::kDomainApp = "xoreos";
41 
42 ConfigManager::ConfigManager() : _changed(false), _domainApp(0), _domainGame(0) {
43  _domainDefaultApp.reset(new ConfigDomain("appDefault"));
44  _domainCommandline.reset(new ConfigDomain("commandline"));
45 }
46 
48 }
49 
51  _configFile = file;
52 }
53 
55  _changed = false;
56 
57  _domainGame = 0;
58  _domainApp = 0;
59 
60  _domainGameTemp.reset();
61  _domainDefaultGame.reset();
62  _domainDefaultApp.reset(new ConfigDomain("appDefault"));
63 
64  _config.reset();
65 }
66 
68  _domainCommandline.reset(new ConfigDomain("commandline"));
69 }
70 
73 }
74 
75 bool ConfigManager::changed() const {
76  return _changed;
77 }
78 
80  clear();
81 
82  // Check that the config file actually exists.
83  UString file = getConfigFile();
84  if (!FilePath::isRegularFile(file))
85  return false;
86 
87  try {
88 
89  // Open and load the config
90  ReadFile config;
91  if (!config.open(file))
92  throw Exception(kOpenError);
93 
94  load(config);
95 
96  } catch (...) {
97  exceptionDispatcherWarning("Failed loading config file \"%s\"", file.c_str());
98  return false;
99  }
100 
101  return true;
102 }
103 
105  clear();
106 
107  _config.reset(new ConfigFile);
108  _config->load(stream);
109 
110  // Get the application domain
111  _domainApp = _config->addDomain(kDomainApp);
112 }
113 
115  if (!_config)
116  return true;
117 
118  if (!getBool("saveconf", true))
119  return true;
120 
121  // Create the directories in the path, if necessary
123 
124  try {
126 
127  // Open and save the config
128  WriteFile config;
129  if (!config.open(file))
130  throw Exception(kOpenError);
131 
132  save(config, true);
133 
134  } catch (...) {
135  exceptionDispatcherWarning("Failed saving config file \"%s\"", file.c_str());
136  return false;
137  }
138 
139  return true;
140 }
141 
142 void ConfigManager::save(WriteStream &stream, bool clearChanged) {
143  if (!_config)
144  throw Exception("No config");
145 
146  _config->save(stream);
147 
148  // We saved our changes, so we're no in a not-changed state again
149  if (clearChanged)
150  _changed = false;
151 }
152 
154  clear();
155 
156  _config.reset(new ConfigFile);
157 
158  _domainApp = _config->addDomain(kDomainApp);
159 }
160 
162  if (!_config)
163  return "";
164 
165  UString canonicalPath = FilePath::canonicalize(path);
166 
167  try {
168  const ConfigFile::DomainList &domains = _config->getDomains();
169  for (ConfigFile::DomainList::const_iterator d = domains.begin(); d != domains.end(); ++d) {
170  if ((*d)->getName() == kDomainApp)
171  continue;
172 
173  Common::UString domainPath = (*d)->getString("path");
174  if (domainPath.empty())
175  continue;
176 
177  if (FilePath::canonicalize(domainPath) == canonicalPath)
178  return (*d)->getName();
179  }
180  } catch (...) {
181  return "";
182  }
183 
184  return "";
185 }
186 
188  if (!_config)
189  return "";
190 
191  UString canonicalStem = FilePath::getStem(FilePath::canonicalize(path));
192 
193  UString target;
194  for (UString::iterator s = canonicalStem.begin(); s != canonicalStem.end(); ++s) {
195  uint32 c = *s;
196 
197  if (UString::isAlNum(c))
198  target += c;
199  }
200 
201  if (target.empty())
202  target = "game";
203 
204  if (!_config->hasDomain(target))
205  return target;
206 
207  for (uint32 i = 0; i < 65536; i++) {
208  UString targetNumbered = UString::format("%s_%d", target.c_str(), i);
209 
210  if (!_config->hasDomain(targetNumbered))
211  return targetNumbered;
212  }
213 
214  return "";
215 }
216 
218  if (target.empty()) {
219  target = createGameID(path);
220  if (target.empty())
221  return "";
222  }
223 
224  ConfigDomain *gameDomain = _config->addDomain(target);
225  assert(gameDomain);
226 
227  gameDomain->setString("path", Common::FilePath::canonicalize(path, false));
228 
229  _changed = true;
230 
231  return target;
232 }
233 
234 bool ConfigManager::hasGame(const UString &gameID) {
235  if (!_config)
236  return false;
237 
238  return _config->getDomain(gameID) != 0;
239 }
240 
241 bool ConfigManager::setGame(const UString &gameID) {
242  // Clear the current game domain
243  _domainDefaultGame.reset();
244  _domainGameTemp.reset();
245  _domainGame = 0;
246 
247  if (gameID.empty())
248  // No ID specified, work done
249  return true;
250 
251  // No config? There's something wrong here
252  if (!_config)
253  return false;
254 
255  // Find the game domain
256  _domainGame = _config->getDomain(gameID);
257  if (!_domainGame)
258  // Doesn't exist? Fail.
259  return false;
260 
261  // Create a new defaults domain for the game too
262  _domainDefaultGame.reset(new ConfigDomain("gameDefault"));
263  // And a temporary settings domain too
264  _domainGameTemp.reset(new ConfigDomain("gameTemp"));
265 
266  return true;
267 }
268 
271 }
272 
273 bool ConfigManager::hasKey(const UString &key) const {
274  // Look up the key in order of priority
275  return hasKey(_domainCommandline.get(), key) || // First command line
276  hasKey(_domainGame , key) || // Then game
277  hasKey(_domainApp , key); // Then application
278 }
279 
280 bool ConfigManager::getKey(const UString &key, UString &value) const {
281  // Look up the key in order of priority
282  return getKey(_domainCommandline.get(), key, value) || // First command line
283  getKey(_domainGameTemp.get() , key, value) || // Then temporary game settings
284  getKey(_domainGame , key, value) || // Then game
285  getKey(_domainApp , key, value) || // Then application
286  getKey(_domainDefaultGame.get(), key, value) || // Then game defaults
287  getKey(_domainDefaultApp.get() , key, value); // Then application defaults
288 }
289 
291  UString value;
292  if (!getKey(key, value))
293  value = getDefaultKey(key);
294 
295  return value;
296 }
297 
298 bool ConfigManager::getBool(const UString &key) const {
299  UString value;
300  if (!getKey(key, value))
301  value = getDefaultKey(key);
302 
303  bool x;
304  parseString(value, x);
305 
306  return x;
307 }
308 
309 int ConfigManager::getInt(const UString &key) const {
310  UString value;
311  if (!getKey(key, value))
312  value = getDefaultKey(key);
313 
314  int x;
315  parseString(value, x);
316 
317  return x;
318 }
319 
320 double ConfigManager::getDouble(const UString &key) const {
321  UString value;
322  if (!getKey(key, value))
323  value = getDefaultKey(key);
324 
325  double x;
326  parseString(value, x);
327 
328  return x;
329 }
330 
331 UString ConfigManager::getString(const UString &key, const UString &def) const {
332  UString value;
333  if (!getKey(key, value))
334  return def;
335 
336  return value;
337 }
338 
339 bool ConfigManager::getBool(const UString &key, bool def) const {
340  UString value;
341  if (!getKey(key, value))
342  return def;
343 
344  bool x = def;
345  try {
346  parseString(value, x);
347  } catch (...) {
349  }
350 
351  return x;
352 }
353 
354 int ConfigManager::getInt(const UString &key, int def) const {
355  UString value;
356  if (!getKey(key, value))
357  return def;
358 
359  int x = def;
360  try {
361  parseString(value, x);
362  } catch (...) {
364  }
365 
366  return x;
367 }
368 
369 double ConfigManager::getDouble(const UString &key, double def) const {
370  UString value;
371  if (!getKey(key, value))
372  return def;
373 
374  double x = def;
375  try {
376  parseString(value, x);
377  } catch (...) {
379  }
380 
381  return x;
382 }
383 
384 void ConfigManager::setKey(const UString &key, const UString &value, bool update) {
385  // Commandline options always get overwritten
386  _domainCommandline->removeKey(key);
387 
388  if (update) {
389  // Don't do anything if we only want to update a value and there's no change
390 
391  UString current;
392  if (getKey(key, current))
393  if (current == value)
394  return;
395  }
396 
397  // Setting a config key => We've changed something
398  _changed = true;
399 
400  if (setKey(_domainGame, key, value))
401  return;
402 
403  setKey(_domainApp, key, value);
404 }
405 
406 void ConfigManager::setString(const UString &key, const UString &value, bool update) {
407  setKey(key, value, update);
408 }
409 
410 void ConfigManager::setBool(const UString &key, bool value, bool update) {
411  setKey(key, composeString(value), update);
412 }
413 
414 void ConfigManager::setInt(const UString &key, int value, bool update) {
415  setKey(key, composeString(value), update);
416 }
417 
418 void ConfigManager::setDouble(const UString &key, double value, bool update) {
419  setKey(key, composeString(value), update);
420 }
421 
422 void ConfigManager::setKey(ConfigRealm realm, const UString &key, const UString &value) {
423  if (realm == kConfigRealmDefault) {
424 
425  // If we're in a game, set the game defaults
426  if (setKey(_domainDefaultGame.get(), key, value))
427  return;
428 
429  // Else, set the application defaults
430  setKey(_domainDefaultApp.get(), key, value);
431 
432  } else if (realm == kConfigRealmGameTemp) {
433 
434  // Set a temporary game setting
435  setKey(_domainGameTemp.get(), key, value);
436 
437  }
438 }
439 
440 void ConfigManager::setString(ConfigRealm realm, const UString &key, const UString &value) {
441  setKey(realm, key, value);
442 }
443 
444 void ConfigManager::setBool(ConfigRealm realm, const UString &key, bool value) {
445  setKey(realm, key, composeString(value));
446 }
447 
448 void ConfigManager::setInt(ConfigRealm realm, const UString &key, int value) {
449  setKey(realm, key, composeString(value));
450 }
451 
452 void ConfigManager::setDouble(ConfigRealm realm, const UString &key, double value) {
453  setKey(realm, key, composeString(value));
454 }
455 
458  // If we're already in a game, overwrite the game config
460  } else if (_domainApp && _domainDefaultApp)
461  // Else, overwrite the application defaults
463 
464  // Resetting to defaults => We've got changes
465  _changed = true;
466 }
467 
468 bool ConfigManager::hasDefaultKey(const UString &key) const {
469  return hasKey(_domainDefaultGame.get(), key) ||
470  hasKey(_domainDefaultApp.get(), key);
471 }
472 
474  UString value;
475 
476  if (getKey(_domainDefaultGame.get(), key, value))
477  return value;
478  if (getKey(_domainDefaultApp.get(), key, value))
479  return value;
480 
481  throw Exception("No such default config key \"%s\"", key.c_str());
482 }
483 
485  return getDefaultKey(key);
486 }
487 
488 bool ConfigManager::getDefaultBool(const UString &key) const {
489  bool x;
490  parseString(getDefaultKey(key), x);
491 
492  return x;
493 }
494 
495 int ConfigManager::getDefaultInt(const UString &key) const {
496  int x;
497  parseString(getDefaultKey(key), x);
498 
499  return x;
500 }
501 
502 double ConfigManager::getDefaultDouble(const UString &key) const {
503  double x;
504  parseString(getDefaultKey(key), x);
505 
506  return x;
507 }
508 
509 void ConfigManager::setCommandlineKey(const UString &key, const UString &value) {
510  setKey(_domainCommandline.get(), key, value);
511 }
512 
514  if (!_configFile.empty())
515  return _configFile;
516 
517  return getDefaultConfigFile();
518 }
519 
521  // By default, the config file is in the config directory
522  return FilePath::getConfigDirectory() + "/xoreos.conf";
523 }
524 
525 bool ConfigManager::hasKey(const ConfigDomain *domain, const UString &key) const {
526  return domain && domain->hasKey(key);
527 }
528 
529 bool ConfigManager::getKey(const ConfigDomain *domain, const UString &key, UString &value) const {
530  return domain && domain->getKey(key, value);
531 }
532 
533 bool ConfigManager::setKey(ConfigDomain *domain, const UString &key, const UString &value) {
534  if (!domain)
535  return false;
536 
537  domain->setKey(key, value);
538  return true;
539 }
540 
541 } // End of namespace Common
This class allows reading/writing INI style config files.
Definition: configfile.h:113
void setDefaults()
Overwrite the current config with the defaults.
Definition: configman.cpp:456
Definition: 2dafile.h:39
bool hasKey(const UString &key) const
Definition: configman.cpp:273
void setString(const UString &key, const UString &value, bool update=false)
Definition: configman.cpp:406
A class holding an UTF-8 string.
Definition: ustring.h:48
Temporary game settings/properties.
Definition: configman.h:47
The global config manager.
ScopedPtr< ConfigDomain > _domainDefaultGame
Game defaults domain.
Definition: configman.h:156
UString composeString(T value)
Convert any POD integer, float/double or bool type into a string.
Definition: strutil.cpp:276
void setString(const UString &key, const UString &value)
Definition: configfile.cpp:149
UString createGame(const UString &path, UString target="")
Create the game domain with this path and target.
Definition: configman.cpp:217
A simple streaming file reading class.
Definition: readfile.h:40
bool load()
Load from the default config file.
Definition: configman.cpp:79
bool fileExists() const
Does the config file exist?
Definition: configman.cpp:71
iterator begin() const
Definition: ustring.cpp:253
bool setGame(const UString &gameID="")
Set the game domain to gameID.
Definition: configman.cpp:241
int getDefaultInt(const UString &key) const
Definition: configman.cpp:495
void clear()
Clear everything except the command line options.
Definition: configman.cpp:54
bool getKey(const UString &key, UString &value) const
Definition: configman.cpp:280
UString getDefaultKey(const UString &key) const
Definition: configman.cpp:473
double getDefaultDouble(const UString &key) const
Definition: configman.cpp:502
ScopedPtr< ConfigDomain > _domainDefaultApp
Application defaults domain.
Definition: configman.h:155
The global config manager, storing all config keys.
Definition: configman.h:51
Utility templates and functions for working with strings and streams.
void setConfigFile(const UString &file="")
Set the config file to use.
Definition: configman.cpp:50
void setBool(const UString &key, bool value, bool update=false)
Definition: configman.cpp:410
UString createGameID(const UString &path)
Definition: configman.cpp:187
void exceptionDispatcherWarning(const char *s,...)
Exception dispatcher that prints the exception as a warning, and adds another reason on top...
Definition: error.cpp:158
utf8::iterator< std::string::const_iterator > iterator
Definition: ustring.h:50
const char * c_str() const
Return the (utf8 encoded) string data.
Definition: ustring.cpp:249
bool open(const UString &fileName)
Try to open the file with the given fileName.
Definition: writefile.cpp:50
static UString format(const char *s,...) GCC_PRINTF(1
Print formatted data into an UString object, similar to sprintf().
Definition: ustring.cpp:718
ConfigDomain * _domainApp
Application domain, pointer into the config file.
Definition: configman.h:160
bool open(const UString &fileName)
Try to open the file with the given fileName.
Definition: readfile.cpp:61
static bool isRegularFile(const UString &p)
Does specified path exist and is it a regular file?
Definition: filepath.cpp:52
bool getKey(const UString &key, UString &value) const
Definition: configfile.cpp:54
#define DECLARE_SINGLETON(T)
Note that you need to use this macro from the global namespace.
Definition: singleton.h:122
A class storing a basic configuration file.
static const char * kDomainApp
The name of the application domain.
Definition: configman.h:147
UString _configFile
The config file to use.
Definition: configman.h:149
static UString canonicalize(const UString &p, bool resolveSymLinks=true)
Return the canonical, absolutized and normalized path.
Definition: filepath.cpp:230
bool getDefaultBool(const UString &key) const
Definition: configman.cpp:488
bool empty() const
Is the string empty?
Definition: ustring.cpp:245
ConfigRealm
Special config realms.
Definition: configman.h:45
ConfigDomain * _domainGame
Game domain, pointer into the config file.
Definition: configman.h:161
void create()
Create a new, empty config.
Definition: configman.cpp:153
StackException Exception
Definition: error.h:59
static UString getDirectory(const UString &p)
Return a path&#39;s directory.
Definition: filepath.cpp:107
ScopedPtr< ConfigDomain > _domainCommandline
Command line domain.
Definition: configman.h:157
UString getDefaultString(const UString &key) const
Definition: configman.cpp:484
Generic interface for a writable data stream.
Definition: writestream.h:64
Implementing the stream reading interfaces for files.
UString findGame(const UString &path)
Find the game domain using this path.
Definition: configman.cpp:161
bool changed() const
Was at least on setting changed?
Definition: configman.cpp:75
static UString getConfigDirectory()
Return the OS-specific path of the config directory.
Definition: filepath.cpp:375
static UString getStem(const UString &p)
Return a file name&#39;s stem.
Definition: filepath.cpp:87
ScopedPtr< ConfigFile > _config
The actual config.
Definition: configman.h:153
Application or game defaults.
Definition: configman.h:46
void clearCommandline()
Clear the command line options.
Definition: configman.cpp:67
void setKey(const UString &key, const UString &value, bool update=false)
Definition: configman.cpp:384
static bool createDirectories(const UString &path)
Create all directories in this path.
Definition: filepath.cpp:342
Accessor for a domain (section) in a config file.
Definition: configfile.h:46
bool hasDefaultKey(const UString &key) const
Definition: configman.cpp:468
uint32_t uint32
Definition: types.h:204
void set(const ConfigDomain &domain, bool clobber=true)
Add the keys of another domain.
Definition: configfile.cpp:230
bool getBool(const UString &key) const
Definition: configman.cpp:298
UString getConfigFile() const
Return the config file that&#39;s currently in use.
Definition: configman.cpp:513
static UString getDefaultConfigFile()
Definition: configman.cpp:520
void setDouble(const UString &key, double value, bool update=false)
Definition: configman.cpp:418
double getDouble(const UString &key) const
Definition: configman.cpp:320
Implementing the stream writing interfaces for files.
bool save()
Save to the default config file.
Definition: configman.cpp:114
ScopedPtr< ConfigDomain > _domainGameTemp
Temporary game settings domain.
Definition: configman.h:158
const Exception kOpenError("Can't open file")
Exception when a file couldn&#39;t be opened.
Definition: error.h:61
iterator end() const
Definition: ustring.cpp:257
bool isInGame() const
Are we currently in a game?
Definition: configman.cpp:269
A simple streaming file writing class.
Definition: writefile.h:40
UString getString(const UString &key) const
Definition: configman.cpp:290
void setKey(const UString &key, const UString &value)
Definition: configfile.cpp:131
bool hasGame(const UString &gameID)
Does the specified game domain exist?
Definition: configman.cpp:234
bool hasKey(const UString &key) const
Definition: configfile.cpp:50
Interface for a seekable & readable data stream.
Definition: readstream.h:265
void parseString(const UString &str, T &value, bool allowEmpty)
Parse a string into any POD integer, float/double or bool type.
Definition: strutil.cpp:215
Utility class for manipulating file paths.
static bool isAlNum(uint32 c)
Is the character an ASCII alphanumeric character?
Definition: ustring.cpp:801
void setCommandlineKey(const UString &key, const UString &value)
Set a config value that came from the command line.
Definition: configman.cpp:509
int getInt(const UString &key) const
Definition: configman.cpp:309
void setInt(const UString &key, int value, bool update=false)
Definition: configman.cpp:414