diff options
-rw-r--r-- | NEWS | 7 | ||||
-rw-r--r-- | TODO | 1 | ||||
-rw-r--r-- | common/config-manager.cpp | 68 | ||||
-rw-r--r-- | common/config-manager.h | 33 |
4 files changed, 76 insertions, 33 deletions
@@ -4,19 +4,20 @@ For a more comprehensive changelog for the latest experimental CVS code, see: 0.?? General: - Added support for FLAC (losless) encoded audio files - - Added an On Screen Display to the SDL backend + - Added an 'On Screen Display' to the SDL backend - Rewrote the backend API partially + - Comments in config files are preserved now SCUMM: - Added graphics decoders for 3DO Humongous Entertainment games -0.6.0(PRE) (2003-??-??) +0.6.0 (2004-03-14) New Games: - Added Broken Sword 1 engine. - Added Broken Sword 2 engine. - Added Flight of the Amazon Queen engine - Added support for V1 SCUMM games 'Maniac Mansion' and 'Zak McKracken' - - SCUMM game Full Throttle is now supported. + - SCUMM game Full Throttle is now supported General: - Subtitles now default to disabled. '-n' option now enabled subtitles. @@ -151,7 +151,6 @@ Audio Config Manager ============== -* Preserve comments in config file somehow * Add a 'notification' system. E.g. the SoundMixer could request to be notified whenever the value of the "volume" config option changes. In other words, instead of a "pull" approach (where each subsystem has to check whether any diff --git a/common/config-manager.cpp b/common/config-manager.cpp index 1dfd546e41..e19afeced0 100644 --- a/common/config-manager.cpp +++ b/common/config-manager.cpp @@ -115,15 +115,22 @@ void ConfigManager::loadFile(const String &filename) { } else { char buf[MAXLINELEN]; String domain; + String comment; int lineno = 0; + + // TODO: Detect if a domain occurs multiple times (or likewise, if + // a key occurs multiple times inside one domain). while (!feof(cfg_file)) { lineno++; if (!fgets(buf, MAXLINELEN, cfg_file)) continue; - if (!buf[0] || buf[0] == '#') { - // Skip empty lines and comments - // TODO: Comments should be preserved here! + + if (buf[0] == '#') { + // Accumulate comments here. Once we encounter either the start + // of a new domain, or a key-value-pair, we associate the value + // of the 'comment' variable with that entity. + comment += buf; } else if (buf[0] == '[') { // It's a new domain which begins here. char *p = buf + 1; @@ -144,9 +151,17 @@ void ConfigManager::loadFile(const String &filename) { default: error("Config file buggy: Invalid character '%c' occured in domain name in line %d", *p, lineno); } + + // Store domain comment + if (_globalDomains.contains(domain)) { + _globalDomains[domain].setDomainComment(comment); + } else { + _gameDomains[domain].setDomainComment(comment); + } + comment.clear(); } else { - // Skip leading whitespaces - char *t = ltrim(buf); + // Skip leading & trailing whitespaces + char *t = rtrim(ltrim(buf)); // Skip empty lines if (*t == 0) @@ -157,24 +172,22 @@ void ConfigManager::loadFile(const String &filename) { error("Config file buggy: Key/value pair found outside a domain in line %d", lineno); } - // It's a new key in the domain. - char *p = strchr(t, '\n'); - if (p) - *p = 0; - p = strchr(t, '\r'); - if (p) - *p = 0; - // Split string at '=' into 'key' and 'value'. - p = strchr(t, '='); - if (!p) { + char *p = strchr(t, '='); + if (!p) error("Config file buggy: Junk found in line line %d: '%s'", lineno, t); + *p = 0; + String key = rtrim(t); + String value = ltrim(p + 1); + set(key, value, domain); + + // Store comment + if (_globalDomains.contains(domain)) { + _globalDomains[domain].setKVComment(key, comment); } else { - *p = 0; - String key = rtrim(t); - String value = rtrim(ltrim(p + 1)); - set(key, value, domain); + _gameDomains[domain].setKVComment(key, comment); } + comment.clear(); } } fclose(cfg_file); @@ -211,13 +224,28 @@ void ConfigManager::writeDomain(FILE *file, const String &name, const Domain &do if (domain.isEmpty()) return; // Don't bother writing empty domains. + String comment; + + // Write domain comment (if any) + comment = domain.getDomainComment(); + if (!comment.isEmpty()) + fprintf(file, "%s", comment.c_str()); + + // Write domain start fprintf(file, "[%s]\n", name.c_str()); + // Write all key/value pairs in this domain, including comments Domain::const_iterator x; for (x = domain.begin(); x != domain.end(); ++x) { const String &value = x->_value; - if (!value.isEmpty()) + if (!value.isEmpty()) { + // Write comment (if any) + comment = domain.getKVComment(x->_key); + if (!comment.isEmpty()) + fprintf(file, "%s", comment.c_str()); + // Write the key/value pair fprintf(file, "%s=%s\n", x->_key.c_str(), value.c_str()); + } } fprintf(file, "\n"); } diff --git a/common/config-manager.h b/common/config-manager.h index 604ccce111..3a54ccfb64 100644 --- a/common/config-manager.h +++ b/common/config-manager.h @@ -38,14 +38,9 @@ namespace Common { * @todo Implement the callback based notification system (outline below) * which sends out notifications to interested parties whenever the value * of some specific (or any) configuration key changes. - * @todo Store comments and write them back out to disk. A simple approach for - * that would be to store comments which come before a section, and - * comments which comes before a specific config key. While this is - * limited, it is at least much better than not saving any comments, and - * it shouldn't be hard to implement either. - * @todo Allow preserving the order of the entries in the config file. Maybe - * even add an API to query/modify that order, which could be used by the - * launcher to allow arranging the game targets. + * @todo Preserve the order of the entries in the config file. Maybe even add + * an API to query/modify that order, which could be used by the launcher + * to allow arranging the game targets. */ class ConfigManager : public Singleton<ConfigManager> { struct IgnoreCaseComparator { @@ -53,12 +48,32 @@ class ConfigManager : public Singleton<ConfigManager> { }; public: - class Domain : public Map<String, String, IgnoreCaseComparator> { + typedef Map<String, String, IgnoreCaseComparator> StringMap; + + class Domain : public StringMap { + private: + StringMap _keyValueComments; + String _domainComment; public: + const String &get(const String &key) const { Node *node = findNode(_root, key); return node ? node->_value : String::emptyString; } + + void setDomainComment(const String &comment) { + _domainComment = comment; + } + const String &getDomainComment() const { + return _domainComment; + } + + void setKVComment(const String &key, const String &comment) { + _keyValueComments[key] = comment; + } + const String &getKVComment(const String &key) const { + return _keyValueComments[key]; + } }; typedef Map<String, Domain, IgnoreCaseComparator> DomainMap; |