Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/core/ConfigFileManager.cc @ 4165

Last change on this file since 4165 was 3370, checked in by rgrieder, 15 years ago

Merged resource branch back to the trunk. Changes:

  • Automated graphics loading by evaluating whether a GameState requires it
  • Using native Tcl library (x3n)

Windows users: Update your dependency package!

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