Ticket #8877: config_changes_take_3-rev.diff

File config_changes_take_3-rev.diff, 61.8 KB (added by fingolfin, 12 years ago)

Take 3, revised

  • gui/Actions.cpp

     
    2525
    2626#include "gui/Actions.h"
    2727#include "gui/message.h"
    28 #include "scumm/scumm.h"
    2928#include "common/config-manager.h"
    3029
    3130#ifdef _WIN32_WCE
     
    110109        current_version = ConfMan.getInt("action_mapping_version", domain());
    111110        if (current_version != version())
    112111                return false;
    113         tempo = ConfMan.get("action_mapping", domain()).c_str();
     112        tempo = ConfMan.getKey("action_mapping", domain()).c_str();
    114113        if (tempo && strlen(tempo)) {
    115114                for (i=0; i<size(); i++) {
    116115                        char x[7];
  • gui/theme.h

     
    3030#include "common/str.h"
    3131#include "common/file.h"
    3232#include "common/config-file.h"
    33 
     33#include "common/config-manager.h"
    3434#include "graphics/surface.h"
    3535#include "graphics/fontman.h"
    3636
  • gui/massadd.cpp

     
    8989        new ButtonWidget(this, "massadddialog_cancel", "Cancel", kCancelCmd, Common::ASCII_ESCAPE);
    9090
    9191        // Build a map from all configured game paths to the targets using them
    92         const Common::ConfigManager::DomainMap &domains = ConfMan.getGameDomains();
    93         Common::ConfigManager::DomainMap::const_iterator iter;
    94         for (iter = domains.begin(); iter != domains.end(); ++iter) {
     92        const Common::ConfigManager::SectionRefList &sections = ConfMan.getGameSections();
     93        Common::ConfigManager::SectionRefList::const_iterator iter;
     94        for (iter = sections.begin(); iter != sections.end(); ++iter) {
    9595
    9696#ifdef __DS__
    9797                // DS port uses an extra section called 'ds'.  This prevents the section from being
    9898                // detected as a game.
    99                 if (iter->_key == "ds") {
     99                if ((*iter)->name == "ds") {
    100100                        continue;
    101101                }
    102102#endif
    103103
    104                 Common::String path(iter->_value.get("path"));
     104                Common::String path((*iter)->getKey("path")->value);
    105105                // Remove trailing slash, so that "/foo" and "/foo/" match.
    106106                // This works around a bug in the POSIX FS code (and others?)
    107107                // where paths are not normalized (so FSNodes refering to identical
     
    109109                while (path != "/" && path.lastChar() == '/')
    110110                        path.deleteLastChar();
    111111                if (!path.empty())
    112                         _pathToTargets[path].push_back(iter->_key);
     112                        _pathToTargets[path].push_back((*iter)->name);
    113113        }
    114114}
    115115
     
    176176                                const Common::StringList &targets = _pathToTargets[path];
    177177                                for (Common::StringList::const_iterator iter = targets.begin(); iter != targets.end(); ++iter) {
    178178                                        // If the gameid, platform and language match -> skip it
    179                                         Common::ConfigManager::Domain *dom = ConfMan.getDomain(*iter);
    180                                         assert(dom);
     179                                        const Common::ConfigFile::Section *sec = ConfMan.getSection(*iter);
     180                                        const Common::ConfigFile::KeyValue *kv;
     181                                        assert(sec);
    181182
    182                                         if ((*dom)["gameid"] == result["gameid"] &&
    183                                             (*dom)["platform"] == result["platform"] &&
    184                                             (*dom)["language"] == result["language"]) {
    185                                                 duplicate = true;
    186                                                 break;
    187                                         }
     183                                        kv = (*sec).getKey("gameid");
     184                                        if (kv && kv->value != result["gameid"])
     185                                                continue;       // No match
     186
     187                                        kv = (*sec).getKey("platform");
     188                                        if ((kv && kv->value != result["platform"]) || (!kv && result.contains("platform")))
     189                                                continue;       // No match
     190
     191                                        kv = (*sec).getKey("language");
     192                                        if ((kv && kv->value != result["language"]) || (!kv && result.contains("language")))
     193                                                continue;       // No match
     194                               
     195                                        // gameid, platform and language match -> duplicate entry
     196                                        duplicate = true;
     197                                        break;
    188198                                }
    189199                                if (duplicate)
    190200                                        break;  // Skip duplicates
  • gui/options.cpp

     
    130130
    131131                if (ConfMan.hasKey("gfx_mode", _domain)) {
    132132                        const OSystem::GraphicsMode *gm = g_system->getSupportedGraphicsModes();
    133                         String gfxMode(ConfMan.get("gfx_mode", _domain));
     133                        String gfxMode(ConfMan.getKey("gfx_mode", _domain));
    134134                        int gfxCount = 1;
    135135                        while (gm->name) {
    136136                                gfxCount++;
     
    146146
    147147                if (ConfMan.hasKey("render_mode", _domain)) {
    148148                        const Common::RenderModeDescription *p = Common::g_renderModes;
    149                         const Common::RenderMode renderMode = Common::parseRenderMode(ConfMan.get("render_mode", _domain));
     149                        const Common::RenderMode renderMode = Common::parseRenderMode(ConfMan.getKey("render_mode", _domain));
    150150                        int sel = 0;
    151151                        for (int i = 0; p->code; ++p, ++i) {
    152152                                if (renderMode == p->id)
     
    171171                int i = 0;
    172172                const int midiDriver =
    173173                        ConfMan.hasKey("music_driver", _domain)
    174                                 ? MidiDriver::parseMusicDriver(ConfMan.get("music_driver", _domain))
     174                                ? MidiDriver::parseMusicDriver(ConfMan.getKey("music_driver", _domain))
    175175                                : MD_AUTO;
    176176                while (md->name && md->id != midiDriver) {
    177177                        i++;
     
    200200                // GS extensions setting
    201201                _enableGSCheckbox->setState(ConfMan.getBool("enable_gs", _domain));
    202202
    203                 String soundFont(ConfMan.get("soundfont", _domain));
     203                String soundFont(ConfMan.getKey("soundfont", _domain));
    204204                if (soundFont.empty() || !ConfMan.hasKey("soundfont", _domain)) {
    205205                        _soundFont->setLabel("None");
    206206                        _soundFontClearButton->setEnabled(false);
     
    659659
    660660
    661661GlobalOptionsDialog::GlobalOptionsDialog()
    662         : OptionsDialog(Common::ConfigManager::kApplicationDomain, "globaloptions") {
     662        : OptionsDialog(Common::ConfigManager::kApplicationSection, "globaloptions") {
    663663
    664664        // The tab widget
    665665        TabWidget *tab = new TabWidget(this, "globaloptions_tabwidget");
     
    757757
    758758#if !( defined(__DC__) || defined(__GP32__) || defined(__PLAYSTATION2__) )
    759759        // Set _savePath to the current save path
    760         Common::String savePath(ConfMan.get("savepath", _domain));
    761         Common::String themePath(ConfMan.get("themepath", _domain));
    762         Common::String extraPath(ConfMan.get("extrapath", _domain));
     760        Common::String savePath(ConfMan.getKey("savepath", _domain));
     761        Common::String themePath(ConfMan.getKey("themepath", _domain));
     762        Common::String extraPath(ConfMan.getKey("extrapath", _domain));
    763763
    764764        if (savePath.empty() || !ConfMan.hasKey("savepath", _domain)) {
    765765                _savePath->setLabel("None");
  • gui/launcher.cpp

     
    2626#include "base/version.h"
    2727
    2828#include "common/config-manager.h"
     29#include "common/config-file.h"
    2930#include "common/events.h"
    3031#include "common/fs.h"
    3132#include "common/util.h"
     
    147148        int labelWidth = g_gui.evaluator()->getVar("tabPopupsLabelW");
    148149
    149150        // GAME: Path to game data (r/o), extra data (r/o), and save data (r/w)
    150         String gamePath(ConfMan.get("path", _domain));
    151         String extraPath(ConfMan.get("extrapath", _domain));
    152         String savePath(ConfMan.get("savepath", _domain));
     151        String gamePath(ConfMan.getKey("path", _domain));
     152        String extraPath(ConfMan.getKey("extrapath", _domain));
     153        String savePath(ConfMan.getKey("savepath", _domain));
    153154
    154155        // GAME: Determine the description string
    155         String description(ConfMan.get("description", domain));
     156        String description(ConfMan.getKey("description", domain));
    156157        if (description.empty() && !desc.empty()) {
    157158                description = desc;
    158159        }
     
    309310        // TODO: game path
    310311
    311312        const Common::LanguageDescription *l = Common::g_languages;
    312         const Common::Language lang = Common::parseLanguage(ConfMan.get("language", _domain));
     313        const Common::Language lang = Common::parseLanguage(ConfMan.getKey("language", _domain));
    313314
    314315        sel = 0;
    315316        if (ConfMan.hasKey("language", _domain)) {
     
    322323
    323324
    324325        const Common::PlatformDescription *p = Common::g_platforms;
    325         const Common::Platform platform = Common::parsePlatform(ConfMan.get("platform", _domain));
     326        const Common::Platform platform = Common::parsePlatform(ConfMan.getKey("platform", _domain));
    326327        sel = 0;
    327328        for (i = 0; p->code; ++p, ++i) {
    328329                if (platform == p->id)
     
    449450                // Write back changes made to config object
    450451                String newDomain(_domainWidget->getEditString());
    451452                if (newDomain != _domain) {
    452                         if (newDomain.empty() || ConfMan.hasGameDomain(newDomain)) {
     453                        if (newDomain.empty() || ConfMan.hasGameSection(newDomain)) {
    453454                                MessageDialog alert("This game ID is already taken. Please choose another one.");
    454455                                alert.runModal();
    455456                                return;
    456457                        }
    457                         ConfMan.renameGameDomain(_domain, newDomain);
     458                        ConfMan.renameGameSection(_domain, newDomain);
    458459                        _domain = newDomain;
    459460                }
    460461                }
     
    517518        updateListing();
    518519
    519520        // Restore last selection
    520         String last(ConfMan.get("lastselectedgame", ConfigManager::kApplicationDomain));
     521        String last(ConfMan.getKey("lastselectedgame", ConfigManager::kApplicationSection));
    521522        selectGame(last);
    522523
    523524        // En-/disable the buttons depending on the list selection
     
    548549        // Clear the active domain, in case we return to the dialog from a
    549550        // failure to launch a game. Otherwise, pressing ESC will attempt to
    550551        // re-launch the same game again.
    551         ConfMan.setActiveDomain("");
     552        ConfMan.setActiveSection("");
    552553        Dialog::open();
    553554
    554555        updateButtons();
     
    558559        // Save last selection
    559560        const int sel = _list->getSelected();
    560561        if (sel >= 0)
    561                 ConfMan.set("lastselectedgame", _domains[sel], ConfigManager::kApplicationDomain);
     562                ConfMan.set("lastselectedgame", _domains[sel], ConfigManager::kApplicationSection);
    562563        else
    563                 ConfMan.removeKey("lastselectedgame", ConfigManager::kApplicationDomain);
     564                ConfMan.removeKey("lastselectedgame", ConfigManager::kApplicationSection);
    564565
    565566        ConfMan.flushToDisk();
    566567        Dialog::close();
     
    571572
    572573        // Retrieve a list of all games defined in the config file
    573574        _domains.clear();
    574         const ConfigManager::DomainMap &domains = ConfMan.getGameDomains();
    575         ConfigManager::DomainMap::const_iterator iter;
    576         for (iter = domains.begin(); iter != domains.end(); ++iter) {
     575        const Common::ConfigManager::SectionRefList &sections = ConfMan.getGameSections();
     576        Common::ConfigManager::SectionRefList::const_iterator iter;
     577        for (iter = sections.begin(); iter != sections.end(); ++iter) {
     578                Common::ConfigFile::Section section = *(*iter);
    577579#ifdef __DS__
    578580                // DS port uses an extra section called 'ds'.  This prevents the section from being
    579581                // detected as a game.
    580                 if (iter->_key == "ds") {
     582                if ((*iter)->name == "ds") {
    581583                        continue;
    582584                }
    583585#endif
     586               
     587                String gameid(section.getKey("gameid")->value);
     588                String description(section.getKey("description")->value);
    584589
    585                 String gameid(iter->_value.get("gameid"));
    586                 String description(iter->_value.get("description"));
    587 
    588590                if (gameid.empty())
    589                         gameid = iter->_key;
     591                        gameid = section.name;
    590592                if (description.empty()) {
    591593                        GameDescriptor g = EngineMan.findGame(gameid);
    592594                        if (g.contains("description"))
     
    594596                }
    595597
    596598                if (description.empty())
    597                         description = "Unknown (target " + iter->_key + ", gameid " + gameid + ")";
     599                        description = "Unknown (target " + section.name + ", gameid " + gameid + ")";
    598600
    599601                if (!gameid.empty() && !description.empty()) {
    600602                        // Insert the game into the launcher list
     
    603605                        while (pos < size && (scumm_stricmp(description.c_str(), l[pos].c_str()) > 0))
    604606                                pos++;
    605607                        l.insert_at(pos, description);
    606                         _domains.insert_at(pos, iter->_key);
     608                        _domains.insert_at(pos, section.name);
    607609                }
    608610        }
    609611
     
    700702                                draw();
    701703                        } else {
    702704                                // User aborted, remove the the new domain again
    703                                 ConfMan.removeGameDomain(domain);
     705                                ConfMan.removeGameSection(domain);
    704706                        }
    705707
    706708                }
     
    719721                domain = result.gameid();
    720722
    721723        assert(!domain.empty());
    722         if (ConfMan.hasGameDomain(domain)) {
     724        if (ConfMan.hasGameSection(domain)) {
    723725                int suffixN = 1;
    724726                char suffix[16];
    725727                String gameid(domain);
    726728
    727                 while (ConfMan.hasGameDomain(domain)) {
     729                while (ConfMan.hasGameSection(domain)) {
    728730                        snprintf(suffix, 16, "-%d", suffixN);
    729731                        domain = gameid + suffix;
    730732                        suffixN++;
     
    732734        }
    733735
    734736        // Add the name domain
    735         ConfMan.addGameDomain(domain);
     737        ConfMan.addGameSection(domain);
    736738
    737739        // Copy all non-empty key/value pairs into the new domain
    738740        for (GameDescriptor::const_iterator iter = result.begin(); iter != result.end(); ++iter) {
     
    759761        if (alert.runModal() == GUI::kMessageOK) {
    760762                // Remove the currently selected game from the list
    761763                assert(item >= 0);
    762                 ConfMan.removeGameDomain(_domains[item]);
     764                ConfMan.removeGameSection(_domains[item]);
    763765
    764766                // Write config to disk
    765767                ConfMan.flushToDisk();
     
    778780        // This is useful because e.g. MonkeyVGA needs Adlib music to have decent
    779781        // music support etc.
    780782        assert(item >= 0);
    781         String gameId(ConfMan.get("gameid", _domains[item]));
     783        String gameId(ConfMan.getKey("gameid", _domains[item]));
    782784        if (gameId.empty())
    783785                gameId = _domains[item];
    784786        EditGameDialog editDialog(_domains[item], EngineMan.findGame(gameId).description());
     
    832834        case kListItemDoubleClickedCmd:
    833835                // Print out what was selected
    834836                assert(item >= 0);
    835                 ConfMan.setActiveDomain(_domains[item]);
     837                ConfMan.setActiveSection(_domains[item]);
    836838                close();
    837839                break;
    838840        case kListItemRemovalRequestCmd:
     
    842844                updateButtons();
    843845                break;
    844846        case kQuitCmd:
    845                 ConfMan.setActiveDomain("");
     847                ConfMan.setActiveSection("");
    846848                setResult(-1);
    847849                close();
    848850                break;
  • common/config-manager.h

     
    2727#define COMMON_CONFIG_MANAGER_H
    2828
    2929#include "common/array.h"
    30 //#include "common/config-file.h"
     30#include "common/config-file.h"
    3131#include "common/hashmap.h"
    3232#include "common/singleton.h"
    3333#include "common/str.h"
     
    4646 *       which sends out notifications to interested parties whenever the value
    4747 *       of some specific (or any) configuration key changes.
    4848 */
    49 class ConfigManager : public Singleton<ConfigManager> {
     49class ConfigManager : public Singleton<ConfigManager>, public ConfigFile {
    5050
    5151public:
    5252
    53         class Domain : public StringMap {
    54         private:
    55                 StringMap _keyValueComments;
    56                 String _domainComment;
    57 
    58         public:
    59                 const String &get(const String &key) const;
    60 
    61                 void setDomainComment(const String &comment);
    62                 const String &getDomainComment() const;
    63 
    64                 void setKVComment(const String &key, const String &comment);
    65                 const String &getKVComment(const String &key) const;
    66                 bool hasKVComment(const String &key) const;
    67         };
    68 
    69         typedef HashMap<String, Domain, IgnoreCase_Hash, IgnoreCase_EqualTo> DomainMap;
    70 
    7153#if !(defined(PALMOS_ARM) || defined(PALMOS_DEBUG) || defined(__GP32__))
    72         /** The name of the application domain (normally 'scummvm'). */
    73         static const String kApplicationDomain;
     54        /** The name of the application section (normally 'scummvm'). */
     55        static const String kApplicationSection;
    7456
    75         /** The transient (pseudo) domain. */
    76         static const String kTransientDomain;
     57        /** The transient (pseudo) section. */
     58        static const String kTransientSection;
    7759#else
    78         static const char *kApplicationDomain;
    79         static const char *kTransientDomain;
     60        static const char *kApplicationSection;
     61        static const char *kTransientSection;
    8062
    81         const String _emptyString;
    8263#endif
     64       
     65        typedef List<Section*> SectionRefList;
    8366
    8467        void                            loadDefaultConfigFile();
    8568        void                            loadConfigFile(const String &filename);
    8669
    8770        /**
    88          * Retrieve the config domain with the given name.
    89          * @param domName       the name of the domain to retrieve
    90          * @return pointer to the domain, or 0 if the domain doesn't exist.
     71         * Retrieve the config section with the given name.
     72         * @param secName       the name of the section to retrieve
     73         * @return pointer to the section, or 0 if the section doesn't exist.
    9174         */
    92         Domain *                        getDomain(const String &domName);
    93         const Domain *          getDomain(const String &domName) const;
     75        Section *                       getSection(const String &secName);
     76        const Section *         getSection(const String &secName) const;
    9477
    9578
    9679        //
    97         // Generic access methods: No domain specified, use the values from the
    98         // various domains in the order of their priority.
     80        // Generic access methods: No section specified, use the values from the
     81        // various sections in the order of their priority.
    9982        //
    10083
    10184        bool                            hasKey(const String &key) const;
    10285        const String &          get(const String &key) const;
    10386        void                            set(const String &key, const String &value);
    104 
     87        const String &          getKey(const String &key) const;
    10588#if 1
    10689        //
    107         // Domain specific access methods: Acces *one specific* domain and modify it.
     90        // Section specific access methods: Acces *one specific* section and modify it.
    10891        // TODO: I'd like to get rid of most of those if possible, or at least reduce
    109         // their usage, by using getDomain as often as possible. For example in the
     92        // their usage, by using getSection as often as possible. For example in the
    11093        // options dialog code...
    11194        //
    11295
    113         bool                            hasKey(const String &key, const String &domName) const;
    114         const String &          get(const String &key, const String &domName) const;
    115         void                            set(const String &key, const String &value, const String &domName);
    116 
    117         void                            removeKey(const String &key, const String &domName);
     96        bool                            hasKey(const String &key, const String &secName) const;
     97        const String &          getKey(const String &key, const String &secName) const;
     98        void                            set(const String &key, const String &value, const String &secName);
     99        void                            removeKey(const String &key, const String &secName);
     100       
    118101#endif
    119102
    120103        //
    121104        // Some additional convenience accessors.
    122105        //
    123         int                                     getInt(const String &key, const String &domName = String::emptyString) const;
    124         bool                            getBool(const String &key, const String &domName = String::emptyString) const;
    125         void                            setInt(const String &key, int value, const String &domName = String::emptyString);
    126         void                            setBool(const String &key, bool value, const String &domName = String::emptyString);
     106        int                                     getInt(const String &key, const String &secName = String::emptyString) const;
     107        bool                            getBool(const String &key, const String &secName = String::emptyString) const;
     108        void                            setInt(const String &key, int value, const String &secName = String::emptyString);
     109        void                            setBool(const String &key, bool value, const String &secName = String::emptyString);
    127110
    128111
    129112        void                            registerDefault(const String &key, const String &value);
     
    133116
    134117        void                            flushToDisk();
    135118
    136         void                            setActiveDomain(const String &domName);
    137         Domain *                        getActiveDomain() { return _activeDomain; }
    138         const Domain *          getActiveDomain() const { return _activeDomain; }
    139         const String &          getActiveDomainName() const { return _activeDomainName; }
     119        void                            setActiveSection(const String &secName);
     120        Section *                       getActiveSection() { return _activeSection; }
     121        const Section *         getActiveSection() const { return _activeSection; }
     122        const String &          getActiveSectionName() const { return _activeSectionName; }
    140123
    141         void                            addGameDomain(const String &domName);
    142         void                            removeGameDomain(const String &domName);
    143         void                            renameGameDomain(const String &oldName, const String &newName);
    144         bool                            hasGameDomain(const String &domName) const;
    145         const DomainMap &       getGameDomains() const { return _gameDomains; }
     124        void                            addGameSection(const String &secName);
     125        void                            removeGameSection(const String &secName);
     126        void                            renameGameSection(const String &oldName, const String &newName);
     127        bool                            hasGameSection(const String &secName) const;
     128        const SectionRefList &getGameSections() const { return _gameSections; }
    146129
    147130/*
    148131        TODO: Callback/change notification system
     
    157140        ConfigManager();
    158141
    159142        void                    loadFile(const String &filename);
    160         void                    writeDomain(WriteStream &stream, const String &name, const Domain &domain);
     143        void                    writeSection(WriteStream &stream, const String &name, const Section &section);
    161144
    162         Domain                  _transientDomain;
    163         DomainMap               _gameDomains;
    164         Domain                  _appDomain;
    165         Domain                  _defaultsDomain;
     145        Section                 _transientSection;
     146        SectionRefList  _gameSections;
     147        Section                 *_appSection;
     148        Section                 _defaultsSection;
    166149
    167         StringList              _domainSaveOrder;
     150        String                  _activeSectionName;
     151        Section                 *_activeSection;
    168152
    169         String                  _activeDomainName;
    170         Domain *                _activeDomain;
    171 
    172153        String                  _filename;
    173154};
    174155
  • common/config-file.h

     
    2626#ifndef COMMON_CONFIG_FILE_H
    2727#define COMMON_CONFIG_FILE_H
    2828
    29 #include "common/config-manager.h"
    3029#include "common/list.h"
    3130#include "common/str.h"
    3231#include "common/stream.h"
     32#include "common/hash-str.h"
    3333
    3434namespace Common {
    3535
     
    5353 */
    5454class ConfigFile {
    5555public:
    56         typedef HashMap<String, bool, IgnoreCase_Hash, IgnoreCase_EqualTo> StringSet;
    57 
     56        enum{
     57                kSectionChanged,
     58                kSectionAdded,
     59                kSectionRemoved
     60        };
     61       
     62       
     63        class Observer{
     64        public:
     65                virtual void configFileChanged(int event, const String &section, const String &key) = 0;
     66                virtual ~Observer();
     67        };
     68        /** A section in a config file. I.e. corresponds to something like this:
     69         *   [mySection]
     70         *   key=value
     71         *
     72         * Comments are also stored, to keep users happy who like editing their
     73         * config files manually.
     74         */
     75       
     76       
    5877        struct KeyValue {
    5978                String key;
    6079                String value;
    6180                String comment;
    6281        };
    63 
     82       
    6483        typedef List<KeyValue> SectionKeyList;
    65 
     84       
    6685        /** A section in a config file. I.e. corresponds to something like this:
    6786         *   [mySection]
    6887         *   key=value
     
    7493                String name;
    7594                List<KeyValue> keys;
    7695                String comment;
    77 
     96               
     97                void clear() { keys.clear(); }
    7898                bool hasKey(const String &key) const;
    7999                const KeyValue* getKey(const String &key) const;
    80100                void setKey(const String &key, const String &value);
    81101                void removeKey(const String &key);
    82102                const SectionKeyList getKeys() const { return keys; }
    83103        };
    84 
     104       
    85105        typedef List<Section> SectionList;
    86 
     106       
    87107public:
    88108        ConfigFile();
    89         ~ConfigFile();
     109        virtual ~ConfigFile();
    90110
     111        static const String _emptyString;
    91112        // TODO: Maybe add a copy constructor etc.?
    92113
    93114        /**
     
    100121
    101122        /** Reset everything stored in this config file. */
    102123        void    clear();
    103 
     124       
    104125        bool    loadFromFile(const String &filename);
    105126        bool    loadFromSaveFile(const char *filename);
    106127        bool    loadFromStream(SeekableReadStream &stream);
    107128        bool    saveToFile(const String &filename);
    108129        bool    saveToSaveFile(const char *filename);
    109130        bool    saveToStream(WriteStream &stream);
    110 
     131        void    writeSection(WriteStream &stream, const Section &section);
     132       
     133        void    addSection(const String &section);
    111134        bool    hasSection(const String &section) const;
    112135        void    removeSection(const String &section);
    113136        void    renameSection(const String &oldName, const String &newName);
    114 
     137        const SectionKeyList getKeys(const String &section) const;
     138       
    115139        bool    hasKey(const String &key, const String &section) const;
    116         bool    getKey(const String &key, const String &section, String &value) const;
     140        virtual const String &getKey(const String &key, const String &section) const;
     141        virtual bool    getKey(const String &key, const String &section, String &value) const;
    117142        void    setKey(const String &key, const String &section, const String &value);
    118143        void    removeKey(const String &key, const String &section);
    119144
    120         const SectionList getSections() const { return _sections; }
    121         const SectionKeyList getKeys(const String &section) const;
     145        void    registerObserver(Observer *ob);
     146        void    notifyObservers(int event, const String &section, const String &key);
    122147
    123         void listSections(StringSet &set);
    124         void listKeyValues(StringMap &kv);
    125 
    126 private:
    127         SectionList _sections;
    128 
     148protected:
     149        SectionList     _sections;
    129150        Section *getSection(const String &section);
    130151        const Section *getSection(const String &section) const;
     152        List<Observer*> _observers;
     153
     154       
    131155};
    132156
    133157/*
  • common/config-manager.cpp

     
    6666
    6767#if !(defined(PALMOS_ARM) || defined(PALMOS_DEBUG) || defined(__GP32__))
    6868
    69 const String ConfigManager::kApplicationDomain("scummvm");
    70 const String ConfigManager::kTransientDomain("__TRANSIENT");
     69const String ConfigManager::kApplicationSection("scummvm");
     70const String ConfigManager::kTransientSection("__TRANSIENT");
    7171
    7272#else
    7373
    74 const char *ConfigManager::kApplicationDomain = "scummvm";
    75 const char *ConfigManager::kTransientDomain = "__TRANSIENT";
     74const char *ConfigManager::kApplicationSection = "scummvm";
     75const char *ConfigManager::kTransientSection = "__TRANSIENT";
    7676
    7777#endif
    7878
     
    8080
    8181
    8282ConfigManager::ConfigManager()
    83  : _activeDomain(0) {
     83 : _activeSection(0){
    8484}
    85 
    86 
    8785void ConfigManager::loadDefaultConfigFile() {
    8886        char configFile[MAXPATHLEN];
    8987        // GP2X is Linux based but Home dir can be read only so do not use it and put the config in the executable dir.
    9088        // On the iPhone, the home dir of the user when you launch the app from the Springboard, is /. Which we don't want.
    91 #if defined(UNIX) && !defined(GP2X) && !defined(IPHONE)
     89#if defined(UNIX) || defined(MACOSX) && !defined(GP2X) && !defined(IPHONE)
    9290        const char *home = getenv("HOME");
    9391        if (home != NULL && strlen(home) < MAXPATHLEN)
    9492                snprintf(configFile, MAXPATHLEN, "%s/%s", home, DEFAULT_CONFIG_FILE);
     
    160158}
    161159
    162160void ConfigManager::loadConfigFile(const String &filename) {
    163         _appDomain.clear();
    164         _gameDomains.clear();
    165         _transientDomain.clear();
     161       
     162        if(_appSection)
     163                _appSection->keys.clear();
     164        _gameSections.clear();
     165        _transientSection.keys.clear();
    166166
    167167        _filename = filename;
    168         _domainSaveOrder.clear();
    169         loadFile(_filename);
     168        loadFromFile(filename);
     169       
     170        if(!hasSection(kApplicationSection))
     171                addSection(kApplicationSection);
     172        _appSection = ConfigFile::getSection(kApplicationSection);
     173       
     174        SectionList::const_iterator iter;
     175        for(iter = _sections.begin(); iter != _sections.end(); ++iter){
     176                if(iter->name != kApplicationSection)
     177                        _gameSections.push_back(ConfigFile::getSection(iter->name));
     178        }
     179       
    170180        printf("Using configuration file: %s\n", _filename.c_str());
    171181}
    172182
    173 void ConfigManager::loadFile(const String &filename) {
    174         File cfg_file;
    175183
    176         if (!cfg_file.open(filename)) {
    177                 printf("Creating configuration file: %s\n", filename.c_str());
    178         } else {
    179                 char buf[MAXLINELEN];
    180                 String domain;
    181                 String comment;
    182                 int lineno = 0;
    183 
    184                 // TODO: Detect if a domain occurs multiple times (or likewise, if
    185                 // a key occurs multiple times inside one domain).
    186 
    187                 while (!cfg_file.eof()) {
    188                         lineno++;
    189                         if (!cfg_file.readLine(buf, MAXLINELEN))
    190                                 break;
    191 
    192                         if (buf[0] == '#') {
    193                                 // Accumulate comments here. Once we encounter either the start
    194                                 // of a new domain, or a key-value-pair, we associate the value
    195                                 // of the 'comment' variable with that entity.
    196                                 comment += buf;
    197                                 comment += '\n';
    198                         } else if (buf[0] == '[') {
    199                                 // It's a new domain which begins here.
    200                                 char *p = buf + 1;
    201                                 // Get the domain name, and check whether it's valid (that
    202                                 // is, verify that it only consists of alphanumerics,
    203                                 // dashes and underscores).
    204                                 while (*p && (isalnum(*p) || *p == '-' || *p == '_'))
    205                                         p++;
    206 
    207                                 switch (*p) {
    208                                 case '\0':
    209                                         error("Config file buggy: missing ] in line %d", lineno);
    210                                         break;
    211                                 case ']':
    212                                         *p = 0;
    213                                         domain = buf + 1;
    214                                         break;
    215                                 default:
    216                                         error("Config file buggy: Invalid character '%c' occured in domain name in line %d", *p, lineno);
    217                                 }
    218 
    219                                 // Store domain comment
    220                                 if (domain == kApplicationDomain) {
    221                                         _appDomain.setDomainComment(comment);
    222                                 } else {
    223                                         _gameDomains[domain].setDomainComment(comment);
    224                                 }
    225                                 comment.clear();
    226 
    227                                 _domainSaveOrder.push_back(domain);
    228                         } else {
    229                                 // Skip leading & trailing whitespaces
    230                                 char *t = rtrim(ltrim(buf));
    231 
    232                                 // Skip empty lines
    233                                 if (*t == 0)
    234                                         continue;
    235 
    236                                 // If no domain has been set, this config file is invalid!
    237                                 if (domain.empty()) {
    238                                         error("Config file buggy: Key/value pair found outside a domain in line %d", lineno);
    239                                 }
    240 
    241                                 // Split string at '=' into 'key' and 'value'.
    242                                 char *p = strchr(t, '=');
    243                                 if (!p)
    244                                         error("Config file buggy: Junk found in line line %d: '%s'", lineno, t);
    245                                 *p = 0;
    246                                 String key = rtrim(t);
    247                                 String value = ltrim(p + 1);
    248                                 set(key, value, domain);
    249 
    250                                 // Store comment
    251                                 if (domain == kApplicationDomain) {
    252                                         _appDomain.setKVComment(key, comment);
    253                                 } else {
    254                                         _gameDomains[domain].setKVComment(key, comment);
    255                                 }
    256                                 comment.clear();
    257                         }
    258                 }
    259         }
    260 }
    261 
    262184void ConfigManager::flushToDisk() {
    263 #ifndef __DC__
    264         File cfg_file;
    265 
    266 // TODO
    267 //      if (!willwrite)
    268 //              return;
    269 
    270         if (!cfg_file.open(_filename, File::kFileWriteMode)) {
    271                 warning("Unable to write configuration file: %s", _filename.c_str());
    272         } else {
    273                 // First write the domains in _domainSaveOrder, in that order.
    274                 // Note: It's possible for _domainSaveOrder to list domains which
    275                 // are not present anymore.
    276                 StringList::const_iterator i;
    277                 for (i = _domainSaveOrder.begin(); i != _domainSaveOrder.end(); ++i) {
    278                         if (kApplicationDomain == *i) {
    279                                 writeDomain(cfg_file, *i, _appDomain);
    280                         } else if (_gameDomains.contains(*i)) {
    281                                 writeDomain(cfg_file, *i, _gameDomains[*i]);
    282                         }
    283                 }
    284 
    285                 DomainMap::const_iterator d;
    286 
    287 
    288                 // Now write the domains which haven't been written yet
    289                 if (find(_domainSaveOrder.begin(), _domainSaveOrder.end(), kApplicationDomain) == _domainSaveOrder.end())
    290                         writeDomain(cfg_file, kApplicationDomain, _appDomain);
    291                 for (d = _gameDomains.begin(); d != _gameDomains.end(); ++d) {
    292                         if (find(_domainSaveOrder.begin(), _domainSaveOrder.end(), d->_key) == _domainSaveOrder.end())
    293                                 writeDomain(cfg_file, d->_key, d->_value);
    294                 }
    295         }
    296 #endif // !__DC__
     185        saveToFile(_filename);
    297186}
    298187
    299 void ConfigManager::writeDomain(WriteStream &stream, const String &name, const Domain &domain) {
    300         if (domain.empty())
    301                 return;         // Don't bother writing empty domains.
    302188
    303         String comment;
     189#pragma mark -
    304190
    305         // Write domain comment (if any)
    306         comment = domain.getDomainComment();
    307         if (!comment.empty())
    308                 stream.writeString(comment);
    309191
    310         // Write domain start
    311         stream.writeByte('[');
    312         stream.writeString(name);
    313         stream.writeByte(']');
    314         stream.writeByte('\n');
     192const ConfigManager::Section *ConfigManager::getSection(const String &secName) const {
     193        assert(!secName.empty());
     194        assert(isValidDomainName(secName));
    315195
    316         // Write all key/value pairs in this domain, including comments
    317         Domain::const_iterator x;
    318         for (x = domain.begin(); x != domain.end(); ++x) {
    319                 if (!x->_value.empty()) {
    320                         // Write comment (if any)
    321                         if (domain.hasKVComment(x->_key)) {
    322                                 comment = domain.getKVComment(x->_key);
    323                                 stream.writeString(comment);
    324                         }
    325                         // Write the key/value pair
    326                         stream.writeString(x->_key);
    327                         stream.writeByte('=');
    328                         stream.writeString(x->_value);
    329                         stream.writeByte('\n');
     196        if (secName == kTransientSection)
     197                return &_transientSection;
     198        if (secName == kApplicationSection)
     199                return _appSection;
     200        for (SectionRefList::iterator i = _gameSections.begin(); i != _gameSections.end(); ++i) {
     201                if (!scumm_stricmp(secName.c_str(), (*i)->name.c_str())) {
     202                        return (*i);
    330203                }
    331204        }
    332         stream.writeByte('\n');
    333 }
    334205
    335 
    336 #pragma mark -
    337 
    338 
    339 const ConfigManager::Domain *ConfigManager::getDomain(const String &domName) const {
    340         assert(!domName.empty());
    341         assert(isValidDomainName(domName));
    342 
    343         if (domName == kTransientDomain)
    344                 return &_transientDomain;
    345         if (domName == kApplicationDomain)
    346                 return &_appDomain;
    347         if (_gameDomains.contains(domName))
    348                 return &_gameDomains[domName];
    349 
    350206        return 0;
    351207}
    352208
    353 ConfigManager::Domain *ConfigManager::getDomain(const String &domName) {
    354         assert(!domName.empty());
    355         assert(isValidDomainName(domName));
     209ConfigManager::Section *ConfigManager::getSection(const String &secName) {
     210        assert(!secName.empty());
     211        assert(isValidDomainName(secName));
     212       
     213        if (secName == kTransientSection)
     214                return &_transientSection;
     215        if (secName == kApplicationSection)
     216                return _appSection;
     217        for (SectionRefList::iterator i = _gameSections.begin(); i != _gameSections.end(); ++i) {
     218                if (!scumm_stricmp(secName.c_str(), (*i)->name.c_str())) {
     219                        return (*i);
     220                }
     221        }
    356222
    357         if (domName == kTransientDomain)
    358                 return &_transientDomain;
    359         if (domName == kApplicationDomain)
    360                 return &_appDomain;
    361         if (_gameDomains.contains(domName))
    362                 return &_gameDomains[domName];
    363 
    364223        return 0;
    365224}
    366225
    367 
    368226#pragma mark -
    369227
    370228
     
    375233        // 3) the application domain.
    376234        // The defaults domain is explicitly *not* checked.
    377235
    378         if (_transientDomain.contains(key))
     236        if (_transientSection.hasKey(key))
    379237                return true;
    380238
    381         if (_activeDomain && _activeDomain->contains(key))
     239        if (_activeSection && _activeSection->hasKey(key))
    382240                return true;
    383241
    384         if (_appDomain.contains(key))
     242        if (_appSection && _appSection->hasKey(key))
    385243                return true;
    386244
    387245        return false;
    388246}
    389 
    390 bool ConfigManager::hasKey(const String &key, const String &domName) const {
    391         // FIXME: For now we continue to allow empty domName to indicate
    392         // "use 'default' domain". This is mainly needed for the SCUMM ConfigDialog
     247       
     248bool ConfigManager::hasKey(const String &key, const String &secName) const {
     249        // FIXME: For now we continue to allow empty secName to indicate
     250        // "use 'default' section". This is mainly needed for the SCUMM ConfigDialog
    393251        // and should be removed ASAP.
    394         if (domName.empty())
     252        if (secName.empty())
    395253                return hasKey(key);
    396254
    397         const Domain *domain = getDomain(domName);
     255        const Section *section = getSection(secName);
    398256
    399         if (!domain)
     257        if (!section)
    400258                return false;
    401         return domain->contains(key);
     259        return section->hasKey(key);
    402260}
    403261
    404 void ConfigManager::removeKey(const String &key, const String &domName) {
    405         Domain *domain = getDomain(domName);
     262void ConfigManager::removeKey(const String &key, const String &secName) {
     263        Section *section = getSection(secName);
    406264
    407         if (!domain)
     265        if (!section)
    408266                error("ConfigManager::removeKey(%s, %s) called on non-existent domain",
    409                                         key.c_str(), domName.c_str());
     267                                        key.c_str(), secName.c_str());
    410268
    411         domain->erase(key);
     269        section->removeKey(key);
    412270}
    413271
    414272
    415273#pragma mark -
     274       
    416275
    417 
    418276const String & ConfigManager::get(const String &key) const {
    419         if (_transientDomain.contains(key))
    420                 return _transientDomain[key];
    421         else if (_activeDomain && _activeDomain->contains(key))
    422                 return (*_activeDomain)[key];
    423         else if (_appDomain.contains(key))
    424                 return _appDomain[key];
    425         else if (_defaultsDomain.contains(key))
    426                 return _defaultsDomain[key];
     277        return getKey(key);
     278}
    427279
     280const String &ConfigManager::getKey(const String &key) const {
     281        if (_transientSection.hasKey(key))
     282                return _transientSection.getKey(key)->value;
     283        else if (_activeSection && _activeSection->hasKey(key))
     284                return (*_activeSection).getKey(key)->value;
     285        else if (_appSection && _appSection->hasKey(key))
     286                return _appSection->getKey(key)->value;
     287        else if (_defaultsSection.hasKey(key))
     288                return _defaultsSection.getKey(key)->value;
     289       
    428290#if !(defined(PALMOS_ARM) || defined(PALMOS_DEBUG) || defined(__GP32__))
    429291        return String::emptyString;
    430292#else
    431         return ConfMan._emptyString;
     293        return _emptyString;
    432294#endif
    433295}
    434296
    435 const String & ConfigManager::get(const String &key, const String &domName) const {
     297const String & ConfigManager::getKey(const String &key, const String &secName) const {
    436298        // FIXME: For now we continue to allow empty domName to indicate
    437299        // "use 'default' domain". This is mainly needed for the SCUMM ConfigDialog
    438300        // and should be removed ASAP.
    439         if (domName.empty())
     301        if (secName.empty())
    440302                return get(key);
    441 
    442         const Domain *domain = getDomain(domName);
    443 
    444         if (!domain)
     303       
     304        const Section *section = getSection(secName);
     305       
     306        if (!section)
    445307                error("ConfigManager::get(%s,%s) called on non-existent domain",
    446                                                                 key.c_str(), domName.c_str());
     308                                                                key.c_str(), secName.c_str());
     309       
    447310
    448         if (domain->contains(key))
    449                 return (*domain)[key];
     311        if (section->hasKey(key))
     312                return section->getKey(key)->value;
    450313
    451         return _defaultsDomain.get(key);
    452 
    453         if (!domain->contains(key)) {
     314        if(_defaultsSection.hasKey(key))
     315                return _defaultsSection.getKey(key)->value;
     316       
    454317#if 1
    455318#if !(defined(PALMOS_ARM) || defined(PALMOS_DEBUG) || defined(__GP32__))
    456         return String::emptyString;
     319                return String::emptyString;
    457320#else
    458         return ConfMan._emptyString;
     321                return _emptyString;
    459322#endif
    460323#else
    461324                error("ConfigManager::get(%s,%s) called on non-existent key",
    462                                         key.c_str(), domName.c_str());
     325                          key.c_str(), secName.c_str());
    463326#endif
    464         }
    465 
    466         return (*domain)[key];
    467327}
    468328
    469329int ConfigManager::getInt(const String &key, const String &domName) const {
    470         String value(get(key, domName));
     330        String value(getKey(key, domName));
    471331        char *errpos;
    472332
    473333        // For now, be tolerant against missing config keys. Strictly spoken, it is
     
    488348}
    489349
    490350bool ConfigManager::getBool(const String &key, const String &domName) const {
    491         String value(get(key, domName));
     351        String value(getKey(key, domName));
    492352
    493353        if ((value == "true") || (value == "yes") || (value == "1"))
    494354                return true;
     
    505365
    506366void ConfigManager::set(const String &key, const String &value) {
    507367        // Remove the transient domain value, if any.
    508         _transientDomain.erase(key);
     368        _transientSection.removeKey(key);
    509369
    510370        // Write the new key/value pair into the active domain, resp. into
    511371        // the application domain if no game domain is active.
    512         if (_activeDomain)
    513                 (*_activeDomain)[key] = value;
     372        if (_activeSection)
     373                (*_activeSection).setKey(key, value);
    514374        else
    515                 _appDomain[key] = value;
     375                (*_appSection).setKey(key, value);
    516376}
    517377
    518 void ConfigManager::set(const String &key, const String &value, const String &domName) {
     378void ConfigManager::set(const String &key, const String &value, const String &secName) {
    519379        // FIXME: For now we continue to allow empty domName to indicate
    520380        // "use 'default' domain". This is mainly needed for the SCUMM ConfigDialog
    521381        // and should be removed ASAP.
    522         if (domName.empty()) {
     382        if (secName.empty()) {
    523383                set(key, value);
    524384                return;
    525385        }
    526386
    527         Domain *domain = getDomain(domName);
     387        Section *section = getSection(secName);
    528388
    529         if (!domain)
    530                 error("ConfigManager::set(%s,%s,%s) called on non-existent domain",
    531                                         key.c_str(), value.c_str(), domName.c_str());
     389        if (!section)
     390                error("ConfigManager::set(%s,%s,%s) called on non-existent section",
     391                                        key.c_str(), value.c_str(), secName.c_str());
    532392
    533         (*domain)[key] = value;
    534 
     393        (*section).setKey(key,value);
     394       
    535395        // TODO/FIXME: We used to erase the given key from the transient domain
    536396        // here. Do we still want to do that?
    537397        // It was probably there to simplify the options dialogs code:
     
    575435
    576436
    577437void ConfigManager::registerDefault(const String &key, const String &value) {
    578         _defaultsDomain[key] = value;
     438        _defaultsSection.setKey(key, value);
    579439}
    580440
    581441void ConfigManager::registerDefault(const String &key, const char *value) {
     
    596456#pragma mark -
    597457
    598458
    599 void ConfigManager::setActiveDomain(const String &domName) {
    600         if (domName.empty()) {
    601                 _activeDomain = 0;
     459void ConfigManager::setActiveSection(const String &secName) {
     460        if (secName.empty()) {
     461                _activeSection = 0;
    602462        } else {
    603                 assert(isValidDomainName(domName));
    604                 _activeDomain = & _gameDomains[domName];
     463                assert(isValidDomainName(secName));
     464                _activeSection = getSection(secName);
    605465        }
    606         _activeDomainName = domName;
     466        _activeSectionName = secName;
    607467}
    608468
    609 void ConfigManager::addGameDomain(const String &domName) {
    610         assert(!domName.empty());
    611         assert(isValidDomainName(domName));
    612 
     469void ConfigManager::addGameSection(const String &secName) {
     470        assert(!secName.empty());
     471        assert(isValidDomainName(secName));
     472       
    613473        // TODO: Do we want to generate an error/warning if a domain with
    614474        // the given name already exists?
    615475
    616         _gameDomains[domName];
     476        addSection(secName);
     477        _gameSections.push_back(ConfigFile::getSection(secName));
    617478}
    618479
    619 void ConfigManager::removeGameDomain(const String &domName) {
    620         assert(!domName.empty());
    621         assert(isValidDomainName(domName));
    622         _gameDomains.erase(domName);
     480void ConfigManager::removeGameSection(const String &secName) {
     481        assert(!secName.empty());
     482        assert(isValidDomainName(secName));
     483        _gameSections.remove(ConfigFile::getSection(secName));
     484        removeSection(secName);
    623485}
    624486
    625 void ConfigManager::renameGameDomain(const String &oldName, const String &newName) {
     487void ConfigManager::renameGameSection(const String &oldName, const String &newName) {
    626488        if (oldName == newName)
    627489                return;
    628490
     
    631493        assert(isValidDomainName(oldName));
    632494        assert(isValidDomainName(newName));
    633495
     496        renameSection(oldName, newName);
    634497//      _gameDomains[newName].merge(_gameDomains[oldName]);
    635         Domain &oldDom = _gameDomains[oldName];
    636         Domain &newDom = _gameDomains[newName];
    637         Domain::const_iterator iter;
    638         for (iter = oldDom.begin(); iter != oldDom.end(); ++iter)
    639                 newDom[iter->_key] = iter->_value;
    640 
    641         _gameDomains.erase(oldName);
    642498}
    643499
    644 bool ConfigManager::hasGameDomain(const String &domName) const {
    645         assert(!domName.empty());
    646         return isValidDomainName(domName) && _gameDomains.contains(domName);
     500bool ConfigManager::hasGameSection(const String &secName) const {
     501        assert(!secName.empty());
     502        if (!isValidDomainName(secName))
     503                return false;
     504       
     505        SectionRefList::const_iterator iter;
     506        for (iter = _gameSections.begin(); iter != _gameSections.end(); ++iter){
     507                if (secName.equalsIgnoreCase((*iter)->name))
     508                   return true;
     509        }
     510        return false;
    647511}
    648512
    649513
    650 #pragma mark -
    651 
    652 
    653 const String &ConfigManager::Domain::get(const String &key) const {
    654         const_iterator iter(find(key));
    655         if (iter != end())
    656                 return iter->_value;
    657 
    658 #if !(defined(PALMOS_ARM) || defined(PALMOS_DEBUG) || defined(__GP32__))
    659         return String::emptyString;
    660 #else
    661         return ConfMan._emptyString;
    662 #endif
    663 }
    664 
    665 void ConfigManager::Domain::setDomainComment(const String &comment) {
    666         _domainComment = comment;
    667 }
    668 const String &ConfigManager::Domain::getDomainComment() const {
    669         return _domainComment;
    670 }
    671 
    672 void ConfigManager::Domain::setKVComment(const String &key, const String &comment) {
    673         _keyValueComments[key] = comment;
    674 }
    675 const String &ConfigManager::Domain::getKVComment(const String &key) const {
    676         return _keyValueComments[key];
    677 }
    678 bool ConfigManager::Domain::hasKVComment(const String &key) const {
    679         return _keyValueComments.contains(key);
    680 }
    681 
    682514}       // End of namespace Common
  • common/config-file.cpp

     
    2828#include "common/savefile.h"
    2929#include "common/system.h"
    3030#include "common/util.h"
    31 
     31#include "config-manager.h"
    3232#define MAXLINELEN 256
    3333
    3434namespace Common {
     
    8282        KeyValue kv;
    8383        String comment;
    8484        int lineno = 0;
    85 
     85       
    8686        // TODO: Detect if a section occurs multiple times (or likewise, if
    8787        // a key occurs multiple times inside one section).
    88 
     88       
    8989        while (!stream.eos()) {
    9090                lineno++;
    9191                if (!stream.readLine(buf, MAXLINELEN))
    9292                        break;
    93 
     93               
    9494                if (buf[0] == '#') {
    9595                        // Accumulate comments here. Once we encounter either the start
    9696                        // of a new section, or a key-value-pair, we associate the value
     
    115115                        // dashes and underscores).
    116116                        while (*p && (isalnum(*p) || *p == '-' || *p == '_'))
    117117                                p++;
    118 
     118                       
    119119                        if (*p == '\0')
    120120                                error("ConfigFile::loadFromStream: missing ] in line %d", lineno);
    121121                        else if (*p != ']')
    122122                                error("ConfigFile::loadFromStream: Invalid character '%c' occured in section name in line %d", *p, lineno);
    123 
     123                       
    124124                        *p = 0;
    125 
     125                       
    126126                        // Previous section is finished now, store it.
    127127                        if (!section.name.empty())
    128128                                _sections.push_back(section);
    129 
     129                       
    130130                        section.name = buf + 1;
    131131                        section.keys.clear();
    132132                        section.comment = comment;
    133133                        comment.clear();
    134 
     134                       
    135135                        assert(isValidName(section.name));
    136136                } else {
    137137                        // Skip leading & trailing whitespaces
    138138                        char *t = rtrim(ltrim(buf));
    139 
     139                       
    140140                        // Skip empty lines
    141141                        if (*t == 0)
    142142                                continue;
    143 
     143                       
    144144                        // If no section has been set, this config file is invalid!
    145145                        if (section.name.empty()) {
    146146                                error("ConfigFile::loadFromStream: Key/value pair found outside a section in line %d", lineno);
    147147                        }
    148 
     148                       
    149149                        // Split string at '=' into 'key' and 'value'.
    150150                        char *p = strchr(t, '=');
    151151                        if (!p)
    152152                                error("ConfigFile::loadFromStream: Junk found in line line %d: '%s'", lineno, t);
    153153                        *p = 0;
    154 
     154                       
    155155                        kv.key = rtrim(t);
    156156                        kv.value = ltrim(p + 1);
    157157                        kv.comment = comment;
    158158                        comment.clear();
    159 
     159                       
    160160                        assert(isValidName(kv.key));
    161 
     161                       
    162162                        section.keys.push_back(kv);
    163163                }
    164164        }
    165 
     165       
    166166        // Save last section
    167167        if (!section.name.empty())
    168168                _sections.push_back(section);
    169 
     169       
    170170        return (!stream.ioFailed() || stream.eos());
    171171}
    172172
     
    191191}
    192192
    193193bool ConfigFile::saveToStream(WriteStream &stream) {
    194         for (List<Section>::iterator i = _sections.begin(); i != _sections.end(); ++i) {
    195                 // Write out the section comment, if any
    196                 if (! i->comment.empty()) {
    197                         stream.writeString(i->comment);
     194        for (List<Section>::const_iterator i = _sections.begin(); i != _sections.end(); ++i) {
     195                writeSection(stream, *i);
     196        }
     197       
     198        stream.flush();
     199        return !stream.ioFailed();
     200}
     201       
     202void ConfigFile::writeSection(WriteStream &stream, const Section &section) {
     203       
     204        if(section.keys.empty())
     205                return;
     206       
     207        // Write out the section comment, if any
     208        if (! section.comment.empty()) {
     209                stream.writeString(section.comment);
     210        }
     211       
     212        // Write out the section name
     213        stream.writeByte('[');
     214        stream.writeString(section.name);
     215        stream.writeByte(']');
     216        stream.writeByte('\n');
     217       
     218        // Write out the key/value pairs
     219        for (List<KeyValue>::iterator kv = section.keys.begin(); kv != section.keys.end(); ++kv) {
     220                // Write out the comment, if any
     221                if (! section.comment.empty()) {
     222                        stream.writeString(kv->comment);
    198223                }
    199 
    200                 // Write out the section name
    201                 stream.writeByte('[');
    202                 stream.writeString(i->name);
    203                 stream.writeByte(']');
     224                // Write out the key/value pair
     225                stream.writeString(kv->key);
     226                stream.writeByte('=');
     227                stream.writeString(kv->value);
    204228                stream.writeByte('\n');
    205 
    206                 // Write out the key/value pairs
    207                 for (List<KeyValue>::iterator kv = i->keys.begin(); kv != i->keys.end(); ++kv) {
    208                         // Write out the comment, if any
    209                         if (! kv->comment.empty()) {
    210                                 stream.writeString(kv->comment);
    211                         }
    212                         // Write out the key/value pair
    213                         stream.writeString(kv->key);
    214                         stream.writeByte('=');
    215                         stream.writeString(kv->value);
    216                         stream.writeByte('\n');
    217                 }
    218229        }
     230}
     231       
     232void ConfigFile::addSection(const String &secName) {
     233        Section section;
     234        section.name = secName;
     235        section.keys.clear();
     236        section.comment.clear();
     237       
     238        _sections.push_back(section);
    219239
    220         stream.flush();
    221         return !stream.ioFailed();
    222240}
    223 
    224 
    225241void ConfigFile::removeSection(const String &section) {
    226242        assert(isValidName(section));
    227243        for (List<Section>::iterator i = _sections.begin(); i != _sections.end(); ++i) {
     
    231247                }
    232248        }
    233249}
    234 
     250       
    235251bool ConfigFile::hasSection(const String &section) const {
    236252        assert(isValidName(section));
    237253        const Section *s = getSection(section);
    238254        return s != 0;
    239255}
    240 
     256       
    241257void ConfigFile::renameSection(const String &oldName, const String &newName) {
    242258        assert(isValidName(oldName));
    243259        assert(isValidName(newName));
    244 
     260       
    245261        //Section *os = getSection(oldName);
    246262        Section *ns = getSection(newName);
    247263        if (ns) {
     
    253269        // - error out
    254270        // - merge the two sections "oldName" and "newName"
    255271}
     272       
    256273
    257 
    258274bool ConfigFile::hasKey(const String &key, const String &section) const {
    259275        assert(isValidName(key));
    260276        assert(isValidName(section));
    261 
     277       
    262278        const Section *s = getSection(section);
    263279        if (!s)
    264280                return false;
     
    268284void ConfigFile::removeKey(const String &key, const String &section) {
    269285        assert(isValidName(key));
    270286        assert(isValidName(section));
    271 
     287       
    272288        Section *s = getSection(section);
    273289        if (s)
    274                  s->removeKey(key);
     290                s->removeKey(key);
    275291}
    276292
     293const String &ConfigFile::getKey(const String &key, const String &section) const {
     294        assert(isValidName(key));
     295        assert(isValidName(section));
     296       
     297        const Section *s = getSection(section);
     298        if (s){
     299                const KeyValue *kv = s->getKey(key);
     300                if (kv)
     301                        return kv->value;
     302        }
     303#if !(defined(PALMOS_ARM) || defined(PALMOS_DEBUG) || defined(__GP32__))
     304        return String::emptyString;
     305#else
     306        return ConfMan._emptyString;
     307#endif
     308}
     309       
    277310bool ConfigFile::getKey(const String &key, const String &section, String &value) const {
    278311        assert(isValidName(key));
    279312        assert(isValidName(section));
    280 
     313       
    281314        const Section *s = getSection(section);
    282315        if (!s)
    283316                return false;
     
    293326        assert(isValidName(section));
    294327        // TODO: Verify that value is valid, too. In particular, it shouldn't
    295328        // contain CR or LF...
    296 
     329       
    297330        Section *s = getSection(section);
    298331        if (!s) {
    299332                KeyValue newKV;
    300333                newKV.key = key;
    301334                newKV.value = value;
    302 
     335               
    303336                Section newSection;
    304337                newSection.name = section;
    305338                newSection.keys.push_back(newKV);
    306 
     339               
    307340                _sections.push_back(newSection);
    308341        } else {
    309342                s->setKey(key, value);
     
    312345
    313346const ConfigFile::SectionKeyList ConfigFile::getKeys(const String &section) const {
    314347        const Section *s = getSection(section);
    315 
     348       
    316349        return s->getKeys();
    317350}
    318 
     351       
    319352ConfigFile::Section *ConfigFile::getSection(const String &section) {
    320353        for (List<Section>::iterator i = _sections.begin(); i != _sections.end(); ++i) {
    321354                if (!scumm_stricmp(section.c_str(), i->name.c_str())) {
     
    333366        }
    334367        return 0;
    335368}
     369       
     370void ConfigFile::notifyObservers(int event, const String &section, const String &key){
     371        List<Observer*>::iterator i;
     372       
     373        for(i=_observers.begin(); i!=_observers.end() ;++i) {
     374                (*i)->configFileChanged(event, section, key);
     375        }
     376}
     377       
     378void ConfigFile::registerObserver(Observer *ob){
     379        _observers.push_back(ob);
     380}
    336381
     382
     383#pragma mark -
     384
    337385bool ConfigFile::Section::hasKey(const String &key) const {
    338386        return getKey(key) != 0;
    339387}
     
    344392                        return &(*i);
    345393                }
    346394        }
     395       
    347396        return 0;
    348397}
    349398
     
    354403                        return;
    355404                }
    356405        }
    357 
     406       
    358407        KeyValue newKV;
    359408        newKV.key = key;
    360409        newKV.value = value;
     
    368417                        return;
    369418                }
    370419        }
    371 }
     420}       
    372421
    373422}       // End of namespace Common
  • common/system.cpp

     
    135135#endif
    136136
    137137FilesystemFactory *OSystem::getFilesystemFactory() {
    138         #if defined(__amigaos4__) || defined(__DC__) || defined(__SYMBIAN32__) || defined(UNIX) || defined(WIN32) || defined(__WII__) || defined(__PSP__) || defined(__DS__)
     138        #if defined(__amigaos4__) || defined(__DC__) || defined(__SYMBIAN32__) || defined(UNIX) || defined(WIN32) || defined(__WII__) || defined(__PSP__) || defined(__DS__) || defined(MACOSX)
    139139                // These ports already implement this function, so it should never be called.
    140140                return 0;
    141141        #elif defined(PALMOS_MODE)
  • engines/engine.cpp

     
    5252                _timer(_system->getTimerManager()),
    5353                _eventMan(_system->getEventManager()),
    5454                _saveFileMan(_system->getSavefileManager()),
    55                 _targetName(ConfMan.getActiveDomainName()),
     55                _targetName(ConfMan.getActiveSectionName()),
    5656                _gameDataPath(ConfMan.get("path")),
    5757                _pauseLevel(0) {
    5858
     
    7777}
    7878
    7979void Engine::initCommonGFX(bool defaultTo1XScaler) {
    80         const Common::ConfigManager::Domain *transientDomain = ConfMan.getDomain(Common::ConfigManager::kTransientDomain);
    81         const Common::ConfigManager::Domain *gameDomain = ConfMan.getActiveDomain();
     80        const Common::ConfigFile::Section *transientSection = ConfMan.getSection(Common::ConfigManager::kTransientSection);
     81        const Common::ConfigFile::Section *gameSection = ConfMan.getActiveSection();
    8282
    83         assert(transientDomain);
     83        assert(transientSection);
    8484
    8585        const bool useDefaultGraphicsMode =
    86                 !transientDomain->contains("gfx_mode") &&
     86                !transientSection->hasKey("gfx_mode") &&
    8787                (
    88                 !gameDomain ||
    89                 !gameDomain->contains("gfx_mode") ||
    90                 !scumm_stricmp(gameDomain->get("gfx_mode").c_str(), "normal") ||
    91                 !scumm_stricmp(gameDomain->get("gfx_mode").c_str(), "default")
     88                !gameSection ||
     89                !gameSection->hasKey("gfx_mode") ||
     90                !scumm_stricmp(gameSection->getKey("gfx_mode")->value.c_str(), "normal") ||
     91                !scumm_stricmp(gameSection->getKey("gfx_mode")->value.c_str(), "default")
    9292                );
    9393
    9494        // See if the game should default to 1x scaler
     
    112112        // we get here. Hence we only do something
    113113
    114114        // (De)activate aspect-ratio correction as determined by the config settings
    115         if (gameDomain && gameDomain->contains("aspect_ratio"))
     115        if (gameSection && gameSection->hasKey("aspect_ratio"))
    116116                _system->setFeatureState(OSystem::kFeatureAspectRatioCorrection, ConfMan.getBool("aspect_ratio"));
    117117
    118118        // (De)activate fullscreen mode as determined by the config settings
    119         if (gameDomain && gameDomain->contains("fullscreen"))
     119        if (gameSection && gameSection->hasKey("fullscreen"))
    120120                _system->setFeatureState(OSystem::kFeatureFullscreenMode, ConfMan.getBool("fullscreen"));
    121121}
    122122
  • backends/saves/default/default-saves.cpp

     
    271271        // Work around a bug (#999122) in the original 0.6.1 release of
    272272        // ScummVM, which would insert a bad savepath value into config files.
    273273        if (dir == "None") {
    274                 ConfMan.removeKey("savepath", ConfMan.getActiveDomainName());
     274                ConfMan.removeKey("savepath", ConfMan.getActiveSectionName());
    275275                ConfMan.flushToDisk();
    276276                dir = ConfMan.get("savepath");
    277277        }
  • backends/platform/sdl/sdl.cpp

     
    4545 */
    4646#if defined(__amigaos4__)
    4747        #include "backends/fs/amigaos4/amigaos4-fs-factory.h"
    48 #elif defined(UNIX)
     48#elif defined(UNIX) || defined(MACOSX)
    4949        #include "backends/fs/posix/posix-fs-factory.h"
    5050#elif defined(WIN32)
    5151        #include "backends/fs/windows/windows-fs-factory.h"
  • backends/platform/sdl/main.cpp

     
    3131
    3232#include "backends/platform/sdl/sdl.h"
    3333#include "backends/plugins/sdl/sdl-provider.h"
    34 #include "base/main.h"
     34#include "base/main.h" 
    3535
    3636#if defined(__SYMBIAN32__)
    3737#include "SymbianOs.h"
  • base/main.cpp

     
    8282        const EnginePlugin *plugin = 0;
    8383
    8484        // Make sure the gameid is set in the config manager, and that it is lowercase.
    85         Common::String gameid(ConfMan.getActiveDomainName());
     85        Common::String gameid(ConfMan.getActiveSectionName());
    8686        assert(!gameid.empty());
    8787        if (ConfMan.hasKey("gameid"))
    8888                gameid = ConfMan.get("gameid");
     
    9090        ConfMan.set("gameid", gameid);
    9191
    9292        // Query the plugins and find one that will handle the specified gameid
    93         printf("User picked target '%s' (gameid '%s')...\n", ConfMan.getActiveDomainName().c_str(), gameid.c_str());
     93        printf("User picked target '%s' (gameid '%s')...\n", ConfMan.getActiveSectionName().c_str(), gameid.c_str());
    9494        printf("  Looking for a plugin supporting this gameid... ");
    9595        GameDescriptor game = EngineMan.findGame(gameid, &plugin);
    9696
     
    118118#endif
    119119                                        && gameDataPath.lastChar() != '\\') {
    120120                gameDataPath += '/';
    121                 ConfMan.set("path", gameDataPath, Common::ConfigManager::kTransientDomain);
     121                ConfMan.set("path", gameDataPath, Common::ConfigManager::kTransientSection);
    122122        }
    123123
    124124        // We add the game "path" to the file search path via File::addDefaultDirectory(),
     
    163163                warning("%s failed to instantiate engine: %s (target '%s', path '%s')",
    164164                        plugin->getName(),
    165165                        errMsg,
    166                         ConfMan.getActiveDomainName().c_str(),
     166                        ConfMan.getActiveSectionName().c_str(),
    167167                        path.c_str()
    168168                        );
    169169                return 0;
     
    176176        if (caption.empty() && !desc.empty())
    177177                caption = desc;
    178178        if (caption.empty())
    179                 caption = ConfMan.getActiveDomainName();        // Use the domain (=target) name
     179                caption = ConfMan.getActiveSectionName();       // Use the domain (=target) name
    180180        if (!caption.empty())   {
    181181                system.setWindowCaption(caption.c_str());
    182182        }
     
    190190        if (ConfMan.hasKey("extrapath"))
    191191                Common::File::addDefaultDirectoryRecursive(ConfMan.get("extrapath"));
    192192
    193         if (ConfMan.hasKey("extrapath", Common::ConfigManager::kApplicationDomain))
    194                 Common::File::addDefaultDirectoryRecursive(ConfMan.get("extrapath", Common::ConfigManager::kApplicationDomain));
     193        if (ConfMan.hasKey("extrapath", Common::ConfigManager::kApplicationSection))
     194                Common::File::addDefaultDirectoryRecursive(ConfMan.getKey("extrapath", Common::ConfigManager::kApplicationSection));
    195195
    196196#ifdef DATA_PATH
    197197        // Add the global DATA_PATH to the directory search list
     
    256256        }
    257257
    258258        // Update the config file
    259         ConfMan.set("versioninfo", gScummVMVersion, Common::ConfigManager::kApplicationDomain);
     259        ConfMan.set("versioninfo", gScummVMVersion, Common::ConfigManager::kApplicationSection);
    260260
    261261
    262262        // Load and setup the debuglevel and the debug flags. We do this at the
     
    287287        system.initBackend();
    288288
    289289        // Unless a game was specified, show the launcher dialog
    290         if (0 == ConfMan.getActiveDomain()) {
     290        if (0 == ConfMan.getActiveSection()) {
    291291                launcherDialog(system);
    292292
    293293                // Discard any command line options. Those that affect the graphics
    294294                // mode etc. already have should have been handled by the backend at
    295295                // this point. And the others (like bootparam etc.) should not
    296296                // blindly be passed to the first game launched from the launcher.
    297                 ConfMan.getDomain(Common::ConfigManager::kTransientDomain)->clear();
     297                ConfMan.getSection(Common::ConfigManager::kTransientSection)->clear();
    298298        }
    299299
    300300        // FIXME: We're now looping the launcher. This, of course, doesn't
    301301        // work as well as it should. In theory everything should be destroyed
    302302        // cleanly, so this is now enabled to encourage people to fix bits :)
    303         while (0 != ConfMan.getActiveDomain()) {
     303        while (0 != ConfMan.getActiveSection()) {
    304304                // Try to find a plugin which feels responsible for the specified game.
    305305                const EnginePlugin *plugin = detectPlugin();
    306306                if (plugin) {
     
    318318
    319319                        // Discard any command line options. It's unlikely that the user
    320320                        // wanted to apply them to *all* games ever launched.
    321                         ConfMan.getDomain(Common::ConfigManager::kTransientDomain)->clear();
     321                        ConfMan.getSection(Common::ConfigManager::kTransientSection)->clear();
    322322
    323323                        // Clear the active config domain
    324                         ConfMan.setActiveDomain("");
     324                        ConfMan.setActiveSection("");
    325325
    326326                        // PluginManager::instance().unloadPlugins();
    327327                        PluginManager::instance().loadPlugins();
  • base/commandLine.cpp

     
    580580               "-------------------- ------------------------------------------------------\n");
    581581
    582582        using namespace Common;
    583         const ConfigManager::DomainMap &domains = ConfMan.getGameDomains();
    584         ConfigManager::DomainMap::const_iterator iter;
    585         for (iter = domains.begin(); iter != domains.end(); ++iter) {
    586                 Common::String name(iter->_key);
    587                 Common::String description(iter->_value.get("description"));
     583        const ConfigManager::SectionRefList &sections = ConfMan.getGameSections();
     584        ConfigManager::SectionRefList::const_iterator iter;
     585        for (iter = sections.begin(); iter != sections.end(); ++iter) {
     586                Common::String name((*iter)->name);
     587                Common::String description((*iter)->getKey("description")->value);
    588588
    589589                if (description.empty()) {
    590590                        // FIXME: At this point, we should check for a "gameid" override
     
    607607        g_system->initBackend();
    608608
    609609        // Grab the "target" domain, if any
    610         const Common::ConfigManager::Domain *domain = ConfMan.getDomain(target);
     610        const Common::ConfigFile::Section *section = ConfMan.getSection(target);
    611611
    612612        // Grab the gameid from the domain resp. use the target as gameid
    613613        Common::String gameid;
    614         if (domain)
    615                 gameid = domain->get("gameid");
     614        if (section)
     615                gameid = section->getKey("gameid")->value;
    616616        if (gameid.empty())
    617617                gameid = target;
    618618        gameid.toLowercase();   // Normalize it to lower case
     
    648648        // for the given path. It then prints out the result and also checks
    649649        // whether the result agrees with the settings of the target.
    650650
    651         const Common::ConfigManager::DomainMap &domains = ConfMan.getGameDomains();
    652         Common::ConfigManager::DomainMap::const_iterator iter = domains.begin();
     651        const Common::ConfigManager::SectionRefList &sections = ConfMan.getGameSections();
     652        Common::ConfigManager::SectionRefList::const_iterator iter;
    653653        int success = 0, failure = 0;
    654         for (iter = domains.begin(); iter != domains.end(); ++iter) {
    655                 Common::String name(iter->_key);
    656                 Common::String gameid(iter->_value.get("gameid"));
    657                 Common::String path(iter->_value.get("path"));
     654        for (iter = sections.begin(); iter != sections.end(); ++iter) {
     655                Common::String name((*iter)->name);
     656                Common::String gameid((*iter)->getKey("gameid")->value);
     657                Common::String path((*iter)->getKey("path")->value);
    658658                printf("Looking at target '%s', gameid '%s', path '%s' ...\n",
    659659                                name.c_str(), gameid.c_str(), path.c_str());
    660660                if (path.empty()) {
     
    705705                                   Common::getPlatformCode(x->platform()));
    706706                }
    707707        }
    708         int total = domains.size();
     708        int total = sections.size();
    709709        printf("Detector test run: %d fail, %d success, %d skipped, out of %d\n",
    710710                        failure, success, total - failure - success, total);
    711711}
     
    746746        // whether there is a gameid matching that name.
    747747        if (!command.empty()) {
    748748                GameDescriptor gd = EngineMan.findGame(command);
    749                 if (ConfMan.hasGameDomain(command) || !gd.gameid().empty()) {
     749                if (ConfMan.hasGameSection(command) || !gd.gameid().empty()) {
    750750                        bool idCameFromCommandLine = false;
    751751
    752752                        // WORKAROUND: Fix for bug #1719463: "DETECTOR: Launching
     
    755755                        // We designate gameids which come strictly from command line
    756756                        // so AdvancedDetector will not save config file with invalid
    757757                        // gameid in case target autoupgrade was performed
    758                         if (!ConfMan.hasGameDomain(command)) {
     758                        if (!ConfMan.hasGameSection(command)) {
    759759                                idCameFromCommandLine = true;
    760760                        }
    761761
    762                         ConfMan.setActiveDomain(command);
     762                        ConfMan.setActiveSection(command);
    763763
    764764                        if (idCameFromCommandLine)
    765765                                ConfMan.set("id_came_from_command_line", "1");
     
    801801                                *c = '_';
    802802
    803803                // Store it into ConfMan.
    804                 ConfMan.set(key, value, Common::ConfigManager::kTransientDomain);
     804                ConfMan.set(key, value, Common::ConfigManager::kTransientSection);
    805805        }
    806806
    807807        return true;