Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/console/src/libraries/core/ConfigFileManager.cc @ 6014

Last change on this file since 6014 was 5929, checked in by rgrieder, 15 years ago

Merged core5 branch back to the trunk.
Key features include clean level unloading and an extended XML event system.

Two important notes:
Delete your keybindings.ini files! * or you will still get parser errors when loading the key bindings.
Delete build_dir/lib/modules/libgamestates.module! * or orxonox won't start.
Best thing to do is to delete the build folder ;)

  • Property svn:eol-style set to native
File size: 21.1 KB
Line 
1/*
2 *   ORXONOX - the hottest 3D action shooter ever to exist
3 *                    > www.orxonox.net <
4 *
5 *
6 *   License notice:
7 *
8 *   This program is free software; you can redistribute it and/or
9 *   modify it under the terms of the GNU General Public License
10 *   as published by the Free Software Foundation; either version 2
11 *   of the License, or (at your option) any later version.
12 *
13 *   This program is distributed in the hope that it will be useful,
14 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
15 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 *   GNU General Public License for more details.
17 *
18 *   You should have received a copy of the GNU General Public License
19 *   along with this program; if not, write to the Free Software
20 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 *
22 *   Author:
23 *      Fabian 'x3n' Landau
24 *   Co-authors:
25 *      ...
26 *
27 */
28
29#include "ConfigFileManager.h"
30
31#include <boost/filesystem.hpp>
32
33#include "util/Convert.h"
34#include "util/Math.h"
35#include "util/StringUtils.h"
36#include "ConsoleCommand.h"
37#include "ConfigValueContainer.h"
38#include "PathConfig.h"
39
40namespace orxonox
41{
42    SetConsoleCommandShortcutExtern(config).argumentCompleter(0, autocompletion::configvalueclasses()).argumentCompleter(1, autocompletion::configvalues()).argumentCompleter(2, autocompletion::configvalue());
43    SetConsoleCommandShortcutExtern(tconfig).argumentCompleter(0, autocompletion::configvalueclasses()).argumentCompleter(1, autocompletion::configvalues()).argumentCompleter(2, autocompletion::configvalue());
44    SetConsoleCommandShortcutExtern(reloadConfig);
45    SetConsoleCommandShortcutExtern(cleanConfig);
46    SetConsoleCommandShortcutExtern(loadSettings).argumentCompleter(0, autocompletion::files());
47
48    bool config(const std::string& classname, const std::string& varname, const std::string& value)
49    {
50        std::map<std::string, Identifier*>::const_iterator identifier = Identifier::getLowercaseStringIdentifierMap().find(getLowercase(classname));
51        if (identifier != Identifier::getLowercaseStringIdentifierMapEnd())
52        {
53            std::map<std::string, ConfigValueContainer*>::const_iterator variable = (*identifier).second->getLowercaseConfigValueMap().find(getLowercase(varname));
54            if (variable != (*identifier).second->getLowercaseConfigValueMapEnd())
55                return (*variable).second->set(value);
56        }
57        return false;
58    }
59
60    bool tconfig(const std::string& classname, const std::string& varname, const std::string& value)
61    {
62        std::map<std::string, Identifier*>::const_iterator identifier = Identifier::getLowercaseStringIdentifierMap().find(getLowercase(classname));
63        if (identifier != Identifier::getLowercaseStringIdentifierMapEnd())
64        {
65            std::map<std::string, ConfigValueContainer*>::const_iterator variable = (*identifier).second->getLowercaseConfigValueMap().find(getLowercase(varname));
66            if (variable != (*identifier).second->getLowercaseConfigValueMapEnd())
67                return (*variable).second->tset(value);
68        }
69        return false;
70    }
71
72    void reloadConfig()
73    {
74        ConfigFileManager::getInstance().load();
75    }
76
77    void cleanConfig()
78    {
79        ConfigFileManager::getInstance().clean(false);
80    }
81
82    void loadSettings(const std::string& filename)
83    {
84        ConfigFileManager::getInstance().setFilename(ConfigFileType::Settings, filename);
85    }
86
87    //////////////////////////
88    // ConfigFileEntryValue //
89    //////////////////////////
90
91    void ConfigFileEntryValue::setValue(const std::string& value)
92    {
93        if (!this->bString_)
94            this->value_ = value;
95        else
96            this->value_ = "\"" + addSlashes(stripEnclosingQuotes(value)) + "\"";
97    }
98
99    std::string ConfigFileEntryValue::getValue() const
100    {
101        if (!this->bString_)
102            return this->value_;
103        else
104            return removeSlashes(stripEnclosingQuotes(this->value_));
105    }
106
107    std::string ConfigFileEntryValue::getFileEntry() const
108    {
109        if (this->additionalComment_ == "" || this->additionalComment_.size() == 0)
110            return (this->name_ + "=" + this->value_);
111        else
112            return (this->name_ + "=" + this->value_ + " " + this->additionalComment_);
113    }
114
115
116    ////////////////////////////////
117    // ConfigFileEntryVectorValue //
118    ////////////////////////////////
119    std::string ConfigFileEntryVectorValue::getFileEntry() const
120    {
121        if (this->additionalComment_ == "" || this->additionalComment_.size() == 0)
122            return (this->name_ + "[" + multi_cast<std::string>(this->index_) + "]" + "=" + this->value_);
123        else
124            return (this->name_ + "[" + multi_cast<std::string>(this->index_) + "]=" + this->value_ + " " + this->additionalComment_);
125    }
126
127
128    ///////////////////////
129    // ConfigFileSection //
130    ///////////////////////
131    ConfigFileSection::~ConfigFileSection()
132    {
133        for (std::list<ConfigFileEntry*>::iterator it = this->entries_.begin(); it != this->entries_.end(); )
134            delete (*(it++));
135    }
136
137    void ConfigFileSection::deleteVectorEntries(const std::string& name, unsigned int startindex)
138    {
139        for (std::list<ConfigFileEntry*>::iterator it = this->entries_.begin(); it != this->entries_.end(); )
140        {
141            if (((*it)->getName() == name) && ((*it)->getIndex() >= startindex))
142            {
143                delete (*it);
144                this->entries_.erase(it++);
145            }
146            else
147            {
148                ++it;
149            }
150        }
151    }
152
153    unsigned int ConfigFileSection::getVectorSize(const std::string& name)
154    {
155        unsigned int size = 0;
156        for (std::list<ConfigFileEntry*>::const_iterator it = this->entries_.begin(); it != this->entries_.end(); ++it)
157            if ((*it)->getName() == name)
158                if ((*it)->getIndex() > size)
159                    size = (*it)->getIndex();
160        if (size == 0)
161            return 0;
162        else
163            return (size + 1);
164    }
165
166    std::string ConfigFileSection::getFileEntry() const
167    {
168        if (this->additionalComment_ == "" || this->additionalComment_.size() == 0)
169            return ("[" + this->name_ + "]");
170        else
171            return ("[" + this->name_ + "] " + this->additionalComment_);
172    }
173
174    std::list<ConfigFileEntry*>::iterator ConfigFileSection::getEntryIterator(const std::string& name, const std::string& fallback, bool bString)
175    {
176        for (std::list<ConfigFileEntry*>::iterator it = this->entries_.begin(); it != this->entries_.end(); ++it)
177        {
178            if ((*it)->getName() == name)
179            {
180                (*it)->setString(bString);
181                return it;
182            }
183        }
184
185        this->bUpdated_ = true;
186
187        return this->entries_.insert(this->entries_.end(), static_cast<ConfigFileEntry*>(new ConfigFileEntryValue(name, fallback, bString)));
188    }
189
190    std::list<ConfigFileEntry*>::iterator ConfigFileSection::getEntryIterator(const std::string& name, unsigned int index, const std::string& fallback, bool bString)
191    {
192        for (std::list<ConfigFileEntry*>::iterator it = this->entries_.begin(); it != this->entries_.end(); ++it)
193        {
194            if (((*it)->getName() == name) && ((*it)->getIndex() == index))
195            {
196                (*it)->setString(bString);
197                return it;
198            }
199        }
200
201        this->bUpdated_ = true;
202
203        if (index == 0)
204            return this->entries_.insert(this->entries_.end(), static_cast<ConfigFileEntry*>(new ConfigFileEntryVectorValue(name, index, fallback, bString)));
205        else
206            return this->entries_.insert(++this->getEntryIterator(name, index - 1, "", bString), static_cast<ConfigFileEntry*>(new ConfigFileEntryVectorValue(name, index, fallback, bString)));
207    }
208
209
210    ////////////////
211    // ConfigFile //
212    ////////////////
213    ConfigFile::~ConfigFile()
214    {
215        this->clear();
216    }
217
218    void ConfigFile::load(bool bCreateIfNotExisting)
219    {
220        // Be sure we start from new in the memory
221        this->clear();
222
223        // Get default file if necessary and available
224        boost::filesystem::path filepath(PathConfig::getConfigPath() / this->filename_);
225        if (!boost::filesystem::exists(filepath))
226        {
227            // Try to get default one from the data folder
228            boost::filesystem::path defaultFilepath(PathConfig::getDataPath() / "defaultConfig" / this->filename_);
229            if (boost::filesystem::exists(defaultFilepath))
230            {
231                COUT(3) << "Copied " << this->filename_ << " from the defaultConfig folder." << std::endl;
232                boost::filesystem::copy_file(defaultFilepath, filepath);
233            }
234        }
235
236        // Open the file
237        std::ifstream file;
238        file.open(filepath.string().c_str(), std::fstream::in);
239        if (file.is_open())
240        {
241            ConfigFileSection* newsection = 0;
242
243            while (file.good() && !file.eof())
244            {
245                std::string line;
246                std::getline(file, line);
247
248                std::string temp = getStripped(line);
249                if (!isEmpty(temp) && !isComment(temp))
250                {
251                    size_t   pos1 = temp.find('[');
252                    if (pos1 == 0) pos1 = line.find('['); else pos1 = std::string::npos;
253                    size_t   pos2 = line.find(']');
254
255                    if (pos1 != std::string::npos && pos2 != std::string::npos && pos2 > pos1 + 1)
256                    {
257                        // New section
258                        std::string comment = line.substr(pos2 + 1);
259                        if (isComment(comment))
260                            newsection = new ConfigFileSection(line.substr(pos1 + 1, pos2 - pos1 - 1), comment);
261                        else
262                            newsection = new ConfigFileSection(line.substr(pos1 + 1, pos2 - pos1 - 1));
263                        this->sections_.insert(this->sections_.end(), newsection);
264                        continue;
265                    }
266                }
267
268                if (newsection != 0)
269                {
270                    if (isComment(line))
271                    {
272                        // New comment
273                        newsection->getEntries().insert(newsection->getEntries().end(), new ConfigFileEntryComment(removeTrailingWhitespaces(line)));
274                        continue;
275                    }
276                    else
277                    {
278                        size_t pos1 = line.find('=');
279
280                        if (pos1 != std::string::npos && pos1 > 0)
281                        {
282                            // New entry
283                            size_t pos2 = line.find('[');
284                            size_t pos3 = line.find(']');
285                            size_t commentposition = getNextCommentPosition(line, pos1 + 1);
286                            while (isBetweenQuotes(line, commentposition))
287                            {
288                                commentposition = getNextCommentPosition(line, commentposition + 1);
289                            }
290                            std::string value = "", comment = "";
291                            if (commentposition == std::string::npos)
292                            {
293                                value = removeTrailingWhitespaces(line.substr(pos1 + 1));
294                            }
295                            else
296                            {
297                                value = removeTrailingWhitespaces(line.substr(pos1 + 1, commentposition - pos1 - 1));
298                                comment = removeTrailingWhitespaces(line.substr(commentposition));
299                            }
300
301                            if (pos2 != std::string::npos && pos3 != std::string::npos && pos3 > pos2 + 1)
302                            {
303                                // There might be an array index
304                                unsigned int index = 0;
305                                if (convertValue(&index, line.substr(pos2 + 1, pos3 - pos2 - 1)))
306                                {
307                                    // New array
308                                    std::list<ConfigFileEntry*>::iterator it = newsection->getEntryIterator(getStripped(line.substr(0, pos2)), index, value, false);
309                                    (*it)->setValue(value);
310                                    (*it)->setComment(comment);
311                                    continue;
312                                }
313                            }
314
315                            // New value
316                            newsection->getEntries().insert(newsection->getEntries().end(), new ConfigFileEntryValue(getStripped(line.substr(0, pos1)), value, false, comment));
317                            continue;
318                        }
319                    }
320                }
321            }
322
323            file.close();
324
325            COUT(3) << "Loaded config file \"" << this->filename_ << "\"." << std::endl;
326
327            // Save the file in case something changed (like stripped whitespaces)
328            this->save();
329
330            // Update all ConfigValueContainers
331            this->updateConfigValues();
332        } // end file.is_open()
333    }
334
335    void ConfigFile::save() const
336    {
337        std::ofstream file;
338        file.open((PathConfig::getConfigPathString() + filename_).c_str(), std::fstream::out);
339        file.setf(std::ios::fixed, std::ios::floatfield);
340        file.precision(6);
341
342        if (!file.is_open())
343        {
344            COUT(1) << "An error occurred in ConfigFileManager.cc:" << std::endl;
345            COUT(1) << "Error: Couldn't open config-file \"" << this->filename_ << "\"." << std::endl;
346            return;
347        }
348
349        for (std::list<ConfigFileSection*>::const_iterator it = this->sections_.begin(); it != this->sections_.end(); ++it)
350        {
351            file << (*it)->getFileEntry() << std::endl;
352
353            for (std::list<ConfigFileEntry*>::const_iterator it_entries = (*it)->getEntriesBegin(); it_entries != (*it)->getEntriesEnd(); ++it_entries)
354            {
355                file << (*it_entries)->getFileEntry() << std::endl;
356            }
357
358            file << std::endl;
359        }
360
361        file.close();
362
363        COUT(4) << "Saved config file \"" << this->filename_ << "\"." << std::endl;
364    }
365
366    void ConfigFile::saveAs(const std::string& filename)
367    {
368        std::string temp = this->filename_;
369        this->filename_ = filename;
370        this->save();
371        this->filename_ = temp;
372    }
373
374    void ConfigFile::clean(bool bCleanComments)
375    {
376        for (std::list<ConfigFileSection*>::iterator it1 = this->sections_.begin(); it1 != this->sections_.end(); )
377        {
378            std::map<std::string, Identifier*>::const_iterator it2 = Identifier::getStringIdentifierMap().find((*it1)->getName());
379            if (it2 != Identifier::getStringIdentifierMapEnd() && (*it2).second->hasConfigValues())
380            {
381                // The section exists, delete comment
382                if (bCleanComments)
383                    (*it1)->setComment("");
384                for (std::list<ConfigFileEntry*>::iterator it3 = (*it1)->entries_.begin(); it3 != (*it1)->entries_.end(); )
385                {
386                    std::map<std::string, ConfigValueContainer*>::const_iterator it4 = (*it2).second->getConfigValueMap().find((*it3)->getName());
387                    if (it4 != (*it2).second->getConfigValueMapEnd())
388                    {
389                        // The config-value exists, delete comment
390                        if (bCleanComments)
391                            (*it3)->setComment("");
392                        ++it3;
393                    }
394                    else
395                    {
396                        // The config-value doesn't exist
397                        delete (*it3);
398                        (*it1)->entries_.erase(it3++);
399                    }
400                }
401                ++it1;
402            }
403            else
404            {
405                // The section doesn't exist
406                delete (*it1);
407                this->sections_.erase(it1++);
408            }
409        }
410
411        // Save the file
412        this->save();
413    }
414
415    void ConfigFile::clear()
416    {
417        for (std::list<ConfigFileSection*>::iterator it = this->sections_.begin(); it != this->sections_.end(); )
418            delete (*(it++));
419        this->sections_.clear();
420    }
421
422    ConfigFileSection* ConfigFile::getSection(const std::string& section)
423    {
424        for (std::list<ConfigFileSection*>::iterator it = this->sections_.begin(); it != this->sections_.end(); ++it)
425            if ((*it)->getName() == section)
426                return (*it);
427
428        this->bUpdated_ = true;
429
430        return (*this->sections_.insert(this->sections_.end(), new ConfigFileSection(section)));
431    }
432
433    void ConfigFile::saveIfUpdated()
434    {
435        bool sectionsUpdated = false;
436
437        for (std::list<ConfigFileSection*>::iterator it = this->sections_.begin(); it != this->sections_.end(); ++it)
438        {
439            if ((*it)->bUpdated_)
440            {
441                sectionsUpdated = true;
442                (*it)->bUpdated_ = false;
443            }
444        }
445
446        if (this->bUpdated_ || sectionsUpdated)
447        {
448            this->bUpdated_ = false;
449            this->save();
450        }
451    }
452
453    void ConfigFile::updateConfigValues()
454    {
455        if (this->type_ == ConfigFileType::Settings)
456        {
457            for (std::map<std::string, Identifier*>::const_iterator it = Identifier::getStringIdentifierMapBegin(); it != Identifier::getStringIdentifierMapEnd(); ++it)
458            {
459                if (it->second->hasConfigValues())
460                {
461                    for (std::map<std::string, ConfigValueContainer*>::const_iterator it2 = (*it).second->getConfigValueMapBegin(); it2 != (*it).second->getConfigValueMapEnd(); ++it2)
462                        it2->second->update();
463
464                    it->second->updateConfigValues();
465                }
466            }
467        }
468    }
469
470
471    ///////////////////////
472    // ConfigFileManager //
473    ///////////////////////
474
475    ConfigFileManager* ConfigFileManager::singletonPtr_s = 0;
476
477    std::string ConfigFileManager::DEFAULT_CONFIG_FILE = "default.ini";
478
479    ConfigFileManager::ConfigFileManager()
480         : mininmalFreeType_(ConfigFileType::numberOfReservedTypes)
481    {
482    }
483
484    ConfigFileManager::~ConfigFileManager()
485    {
486        for(std::map<ConfigFileType, ConfigFile*>::const_iterator it = this->configFiles_.begin(); it != this->configFiles_.end(); )
487            delete (it++)->second;
488    }
489
490    void ConfigFileManager::setFilename(ConfigFileType type, const std::string& filename)
491    {
492        std::map<ConfigFileType, ConfigFile*>::const_iterator it = this->configFiles_.find(type);
493        if (it != this->configFiles_.end())
494        {
495            assert(it->second);
496            delete it->second;
497        }
498        this->configFiles_[type] = new ConfigFile(filename, type);
499        this->load(type);
500    }
501
502    void ConfigFileManager::load()
503    {
504        for(std::map<ConfigFileType, ConfigFile*>::const_iterator it = this->configFiles_.begin(); it != this->configFiles_.end(); ++it)
505            it->second->load();
506    }
507
508    void ConfigFileManager::save()
509    {
510        for(std::map<ConfigFileType, ConfigFile*>::const_iterator it = this->configFiles_.begin(); it != this->configFiles_.end(); ++it)
511            it->second->save();
512    }
513
514    void ConfigFileManager::clean(bool bCleanComments)
515    {
516        for(std::map<ConfigFileType, ConfigFile*>::const_iterator it = this->configFiles_.begin(); it != this->configFiles_.end(); ++it)
517            this->clean(it->first, bCleanComments);
518    }
519
520    void ConfigFileManager::load(ConfigFileType type)
521    {
522        this->getFile(type)->load();
523    }
524
525    void ConfigFileManager::save(ConfigFileType type)
526    {
527        this->getFile(type)->save();
528    }
529
530    void ConfigFileManager::saveAs(ConfigFileType type, const std::string& saveFilename)
531    {
532        this->getFile(type)->saveAs(saveFilename);
533    }
534
535    void ConfigFileManager::clean(ConfigFileType type, bool bCleanComments)
536    {
537        this->getFile(type)->clean(bCleanComments);
538    }
539
540    void ConfigFileManager::updateConfigValues()
541    {
542        for(std::map<ConfigFileType, ConfigFile*>::const_iterator it = this->configFiles_.begin(); it != this->configFiles_.end(); ++it)
543            it->second->updateConfigValues();
544    }
545
546    void ConfigFileManager::updateConfigValues(ConfigFileType type)
547    {
548        this->getFile(type)->updateConfigValues();
549    }
550
551    const std::string& ConfigFileManager::getFilename(ConfigFileType type)
552    {
553        std::map<ConfigFileType, ConfigFile*>::const_iterator it = this->configFiles_.find(type);
554        if (it != this->configFiles_.end())
555            return it->second->getFilename();
556        else
557            return BLANKSTRING;
558    }
559
560    ConfigFile* ConfigFileManager::getFile(ConfigFileType type)
561    {
562        std::map<ConfigFileType, ConfigFile*>::const_iterator it = this->configFiles_.find(type);
563        if (it != this->configFiles_.end())
564            return it->second;
565        else
566        {
567            COUT(1) << "ConfigFileManager: Can't find a config file for type with ID " << static_cast<int>(type) << std::endl;
568            COUT(1) << "Using " << DEFAULT_CONFIG_FILE << " file." << std::endl;
569            this->setFilename(type, DEFAULT_CONFIG_FILE);
570            return getFile(type);
571        }
572    }
573}
Note: See TracBrowser for help on using the repository browser.