From 63750d678068aa92ee95dc9de2b2908a983b489d Mon Sep 17 00:00:00 2001 From: Johannes Schickel Date: Thu, 8 Aug 2013 16:22:08 +0200 Subject: COMMON: Rename ConfigFile to INIFile. This clears up that 'ConfigFile' is actually a class handling only INI-files. --- common/config-file.cpp | 392 ------------------------------------- common/config-file.h | 140 ------------- common/config-manager.h | 2 +- common/ini-file.cpp | 392 +++++++++++++++++++++++++++++++++++++ common/ini-file.h | 140 +++++++++++++ common/module.mk | 2 +- engines/composer/composer.h | 4 +- engines/gob/iniconfig.cpp | 4 +- engines/gob/iniconfig.h | 4 +- engines/mohawk/livingbooks.cpp | 4 +- engines/mohawk/livingbooks.h | 4 +- engines/mohawk/livingbooks_lbx.cpp | 10 +- engines/scumm/he/script_v80he.cpp | 6 +- engines/testbed/config.cpp | 8 +- engines/testbed/config.h | 4 +- 15 files changed, 558 insertions(+), 558 deletions(-) delete mode 100644 common/config-file.cpp delete mode 100644 common/config-file.h create mode 100644 common/ini-file.cpp create mode 100644 common/ini-file.h diff --git a/common/config-file.cpp b/common/config-file.cpp deleted file mode 100644 index 0ce6dcf0c8..0000000000 --- a/common/config-file.cpp +++ /dev/null @@ -1,392 +0,0 @@ -/* ScummVM - Graphic Adventure Engine - * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#include "common/config-file.h" -#include "common/file.h" -#include "common/savefile.h" -#include "common/system.h" -#include "common/textconsole.h" - -namespace Common { - -bool ConfigFile::isValidName(const String &name) { - const char *p = name.c_str(); - while (*p && (isAlnum(*p) || *p == '-' || *p == '_' || *p == '.')) - p++; - return *p == 0; -} - -ConfigFile::ConfigFile() { -} - -ConfigFile::~ConfigFile() { -} - -void ConfigFile::clear() { - _sections.clear(); -} - -bool ConfigFile::loadFromFile(const String &filename) { - File file; - if (file.open(filename)) - return loadFromStream(file); - else - return false; -} - -bool ConfigFile::loadFromSaveFile(const char *filename) { - assert(g_system); - SaveFileManager *saveFileMan = g_system->getSavefileManager(); - SeekableReadStream *loadFile; - - assert(saveFileMan); - if (!(loadFile = saveFileMan->openForLoading(filename))) - return false; - - bool status = loadFromStream(*loadFile); - delete loadFile; - return status; -} - -bool ConfigFile::loadFromStream(SeekableReadStream &stream) { - Section section; - KeyValue kv; - String comment; - int lineno = 0; - - // TODO: Detect if a section occurs multiple times (or likewise, if - // a key occurs multiple times inside one section). - - while (!stream.eos() && !stream.err()) { - lineno++; - - // Read a line - String line = stream.readLine(); - - if (line.size() == 0) { - // Do nothing - } else if (line[0] == '#' || line[0] == ';' || line.hasPrefix("//")) { - // Accumulate comments here. Once we encounter either the start - // of a new section, or a key-value-pair, we associate the value - // of the 'comment' variable with that entity. The semicolon and - // C++-style comments are used for Living Books games in Mohawk. - comment += line; - comment += "\n"; - } else if (line[0] == '(') { - // HACK: The following is a hack added by Kirben to support the - // "map.ini" used in the HE SCUMM game "SPY Fox in Hold the Mustard". - // - // It would be nice if this hack could be restricted to that game, - // but the current design of this class doesn't allow to do that - // in a nice fashion (a "isMustard" parameter is *not* a nice - // solution). - comment += line; - comment += "\n"; - } else if (line[0] == '[') { - // It's a new section which begins here. - const char *p = line.c_str() + 1; - // Get the section name, and check whether it's valid (that - // is, verify that it only consists of alphanumerics, - // periods, dashes and underscores). Mohawk Living Books games - // can have periods in their section names. - while (*p && (isAlnum(*p) || *p == '-' || *p == '_' || *p == '.')) - p++; - - if (*p == '\0') - error("ConfigFile::loadFromStream: missing ] in line %d", lineno); - else if (*p != ']') - error("ConfigFile::loadFromStream: Invalid character '%c' occurred in section name in line %d", *p, lineno); - - // Previous section is finished now, store it. - if (!section.name.empty()) - _sections.push_back(section); - - section.name = String(line.c_str() + 1, p); - section.keys.clear(); - section.comment = comment; - comment.clear(); - - assert(isValidName(section.name)); - } else { - // This line should be a line with a 'key=value' pair, or an empty one. - - // Skip leading whitespaces - const char *t = line.c_str(); - while (isSpace(*t)) - t++; - - // Skip empty lines / lines with only whitespace - if (*t == 0) - continue; - - // If no section has been set, this config file is invalid! - if (section.name.empty()) { - error("ConfigFile::loadFromStream: Key/value pair found outside a section in line %d", lineno); - } - - // Split string at '=' into 'key' and 'value'. First, find the "=" delimeter. - const char *p = strchr(t, '='); - if (!p) - error("Config file buggy: Junk found in line line %d: '%s'", lineno, t); - - // Extract the key/value pair - kv.key = String(t, p); - kv.value = String(p + 1); - - // Trim of spaces - kv.key.trim(); - kv.value.trim(); - - // Store comment - kv.comment = comment; - comment.clear(); - - assert(isValidName(kv.key)); - - section.keys.push_back(kv); - } - } - - // Save last section - if (!section.name.empty()) - _sections.push_back(section); - - return (!stream.err() || stream.eos()); -} - -bool ConfigFile::saveToFile(const String &filename) { - DumpFile file; - if (file.open(filename)) - return saveToStream(file); - else - return false; -} - -bool ConfigFile::saveToSaveFile(const char *filename) { - assert(g_system); - SaveFileManager *saveFileMan = g_system->getSavefileManager(); - WriteStream *saveFile; - - assert(saveFileMan); - if (!(saveFile = saveFileMan->openForSaving(filename))) - return false; - - bool status = saveToStream(*saveFile); - delete saveFile; - return status; -} - -bool ConfigFile::saveToStream(WriteStream &stream) { - for (List
::iterator i = _sections.begin(); i != _sections.end(); ++i) { - // Write out the section comment, if any - if (! i->comment.empty()) { - stream.writeString(i->comment); - } - - // Write out the section name - stream.writeByte('['); - stream.writeString(i->name); - stream.writeByte(']'); - stream.writeByte('\n'); - - // Write out the key/value pairs - for (List::iterator kv = i->keys.begin(); kv != i->keys.end(); ++kv) { - // Write out the comment, if any - if (! kv->comment.empty()) { - stream.writeString(kv->comment); - } - // Write out the key/value pair - stream.writeString(kv->key); - stream.writeByte('='); - stream.writeString(kv->value); - stream.writeByte('\n'); - } - } - - stream.flush(); - return !stream.err(); -} - -void ConfigFile::addSection(const String §ion) { - Section *s = getSection(section); - if (s) - return; - - Section newSection; - newSection.name = section; - _sections.push_back(newSection); -} - -void ConfigFile::removeSection(const String §ion) { - assert(isValidName(section)); - for (List
::iterator i = _sections.begin(); i != _sections.end(); ++i) { - if (section.equalsIgnoreCase(i->name)) { - _sections.erase(i); - return; - } - } -} - -bool ConfigFile::hasSection(const String §ion) const { - assert(isValidName(section)); - const Section *s = getSection(section); - return s != 0; -} - -void ConfigFile::renameSection(const String &oldName, const String &newName) { - assert(isValidName(oldName)); - assert(isValidName(newName)); - - Section *os = getSection(oldName); - const Section *ns = getSection(newName); - if (os) { - // HACK: For now we just print a warning, for more info see the TODO - // below. - if (ns) - warning("ConfigFile::renameSection: Section name \"%s\" already used", newName.c_str()); - else - os->name = newName; - } - // TODO: Check here whether there already is a section with the - // new name. Not sure how to cope with that case, we could: - // - simply remove the existing "newName" section - // - error out - // - merge the two sections "oldName" and "newName" -} - - -bool ConfigFile::hasKey(const String &key, const String §ion) const { - assert(isValidName(key)); - assert(isValidName(section)); - - const Section *s = getSection(section); - if (!s) - return false; - return s->hasKey(key); -} - -void ConfigFile::removeKey(const String &key, const String §ion) { - assert(isValidName(key)); - assert(isValidName(section)); - - Section *s = getSection(section); - if (s) - s->removeKey(key); -} - -bool ConfigFile::getKey(const String &key, const String §ion, String &value) const { - assert(isValidName(key)); - assert(isValidName(section)); - - const Section *s = getSection(section); - if (!s) - return false; - const KeyValue *kv = s->getKey(key); - if (!kv) - return false; - value = kv->value; - return true; -} - -void ConfigFile::setKey(const String &key, const String §ion, const String &value) { - assert(isValidName(key)); - assert(isValidName(section)); - // TODO: Verify that value is valid, too. In particular, it shouldn't - // contain CR or LF... - - Section *s = getSection(section); - if (!s) { - KeyValue newKV; - newKV.key = key; - newKV.value = value; - - Section newSection; - newSection.name = section; - newSection.keys.push_back(newKV); - - _sections.push_back(newSection); - } else { - s->setKey(key, value); - } -} - -const ConfigFile::SectionKeyList ConfigFile::getKeys(const String §ion) const { - const Section *s = getSection(section); - - return s->getKeys(); -} - -ConfigFile::Section *ConfigFile::getSection(const String §ion) { - for (List
::iterator i = _sections.begin(); i != _sections.end(); ++i) { - if (section.equalsIgnoreCase(i->name)) { - return &(*i); - } - } - return 0; -} - -const ConfigFile::Section *ConfigFile::getSection(const String §ion) const { - for (List
::const_iterator i = _sections.begin(); i != _sections.end(); ++i) { - if (section.equalsIgnoreCase(i->name)) { - return &(*i); - } - } - return 0; -} - -bool ConfigFile::Section::hasKey(const String &key) const { - return getKey(key) != 0; -} - -const ConfigFile::KeyValue* ConfigFile::Section::getKey(const String &key) const { - for (List::const_iterator i = keys.begin(); i != keys.end(); ++i) { - if (key.equalsIgnoreCase(i->key)) { - return &(*i); - } - } - return 0; -} - -void ConfigFile::Section::setKey(const String &key, const String &value) { - for (List::iterator i = keys.begin(); i != keys.end(); ++i) { - if (key.equalsIgnoreCase(i->key)) { - i->value = value; - return; - } - } - - KeyValue newKV; - newKV.key = key; - newKV.value = value; - keys.push_back(newKV); -} - -void ConfigFile::Section::removeKey(const String &key) { - for (List::iterator i = keys.begin(); i != keys.end(); ++i) { - if (key.equalsIgnoreCase(i->key)) { - keys.erase(i); - return; - } - } -} - -} // End of namespace Common diff --git a/common/config-file.h b/common/config-file.h deleted file mode 100644 index 8bba851110..0000000000 --- a/common/config-file.h +++ /dev/null @@ -1,140 +0,0 @@ -/* ScummVM - Graphic Adventure Engine - * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#ifndef COMMON_CONFIG_FILE_H -#define COMMON_CONFIG_FILE_H - -#include "common/hash-str.h" -#include "common/list.h" -#include "common/str.h" - -namespace Common { - -class SeekableReadStream; -class WriteStream; - -/** - * This class allows reading/writing INI style config files. - * It is used by the ConfigManager for storage, but can also - * be used by other code if it needs to read/write custom INI - * files. - * - * Lines starting with a '#' are ignored (i.e. treated as comments). - * Some effort is made to preserve comments, though. - * - * This class makes no attempts to provide fast access to key/value pairs. - * In particular, it stores all sections and k/v pairs in lists, not - * in dictionaries/maps. This makes it very easy to read/write the data - * from/to files, but of course is not appropriate for fast access. - * The main reason is that this class is indeed geared toward doing precisely - * that! - * If you need fast access to the game config, use higher level APIs, like the - * one provided by ConfigManager. - */ -class ConfigFile { -public: - struct KeyValue { - String key; - String value; - String comment; - }; - - typedef List SectionKeyList; - - /** A section in a config file. I.e. corresponds to something like this: - * [mySection] - * key=value - * - * Comments are also stored, to keep users happy who like editing their - * config files manually. - */ - struct Section { - String name; - List keys; - String comment; - - bool hasKey(const String &key) const; - const KeyValue* getKey(const String &key) const; - void setKey(const String &key, const String &value); - void removeKey(const String &key); - const SectionKeyList getKeys() const { return keys; } - }; - - typedef List
SectionList; - -public: - ConfigFile(); - ~ConfigFile(); - - // TODO: Maybe add a copy constructor etc.? - - /** - * Check whether the given string is a valid section or key name. - * For that, it must only consist of letters, numbers, dashes and - * underscores. In particular, white space and "#", "=", "[", "]" - * are not valid! - */ - static bool isValidName(const String &name); - - /** Reset everything stored in this config file. */ - void clear(); - - bool loadFromFile(const String &filename); - bool loadFromSaveFile(const char *filename); - bool loadFromStream(SeekableReadStream &stream); - bool saveToFile(const String &filename); - bool saveToSaveFile(const char *filename); - bool saveToStream(WriteStream &stream); - - bool hasSection(const String §ion) const; - void addSection(const String §ion); - void removeSection(const String §ion); - void renameSection(const String &oldName, const String &newName); - - bool hasKey(const String &key, const String §ion) const; - bool getKey(const String &key, const String §ion, String &value) const; - void setKey(const String &key, const String §ion, const String &value); - void removeKey(const String &key, const String §ion); - - const SectionList getSections() const { return _sections; } - const SectionKeyList getKeys(const String §ion) const; - - void listKeyValues(StringMap &kv); - -private: - SectionList _sections; - - Section *getSection(const String §ion); - const Section *getSection(const String §ion) const; -}; - -/* -- ConfigMan owns a config file -- allow direct access to that config file (for the launcher) -- simplify and unify the regular ConfigMan API in exchange - - -*/ - -} // End of namespace Common - -#endif diff --git a/common/config-manager.h b/common/config-manager.h index d43a7bec51..45a1f2d103 100644 --- a/common/config-manager.h +++ b/common/config-manager.h @@ -24,7 +24,7 @@ #define COMMON_CONFIG_MANAGER_H #include "common/array.h" -//#include "common/config-file.h" +//#include "common/ini-file.h" #include "common/hashmap.h" #include "common/singleton.h" #include "common/str.h" diff --git a/common/ini-file.cpp b/common/ini-file.cpp new file mode 100644 index 0000000000..be5247dcfb --- /dev/null +++ b/common/ini-file.cpp @@ -0,0 +1,392 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/ini-file.h" +#include "common/file.h" +#include "common/savefile.h" +#include "common/system.h" +#include "common/textconsole.h" + +namespace Common { + +bool INIFile::isValidName(const String &name) { + const char *p = name.c_str(); + while (*p && (isAlnum(*p) || *p == '-' || *p == '_' || *p == '.')) + p++; + return *p == 0; +} + +INIFile::INIFile() { +} + +INIFile::~INIFile() { +} + +void INIFile::clear() { + _sections.clear(); +} + +bool INIFile::loadFromFile(const String &filename) { + File file; + if (file.open(filename)) + return loadFromStream(file); + else + return false; +} + +bool INIFile::loadFromSaveFile(const char *filename) { + assert(g_system); + SaveFileManager *saveFileMan = g_system->getSavefileManager(); + SeekableReadStream *loadFile; + + assert(saveFileMan); + if (!(loadFile = saveFileMan->openForLoading(filename))) + return false; + + bool status = loadFromStream(*loadFile); + delete loadFile; + return status; +} + +bool INIFile::loadFromStream(SeekableReadStream &stream) { + Section section; + KeyValue kv; + String comment; + int lineno = 0; + + // TODO: Detect if a section occurs multiple times (or likewise, if + // a key occurs multiple times inside one section). + + while (!stream.eos() && !stream.err()) { + lineno++; + + // Read a line + String line = stream.readLine(); + + if (line.size() == 0) { + // Do nothing + } else if (line[0] == '#' || line[0] == ';' || line.hasPrefix("//")) { + // Accumulate comments here. Once we encounter either the start + // of a new section, or a key-value-pair, we associate the value + // of the 'comment' variable with that entity. The semicolon and + // C++-style comments are used for Living Books games in Mohawk. + comment += line; + comment += "\n"; + } else if (line[0] == '(') { + // HACK: The following is a hack added by Kirben to support the + // "map.ini" used in the HE SCUMM game "SPY Fox in Hold the Mustard". + // + // It would be nice if this hack could be restricted to that game, + // but the current design of this class doesn't allow to do that + // in a nice fashion (a "isMustard" parameter is *not* a nice + // solution). + comment += line; + comment += "\n"; + } else if (line[0] == '[') { + // It's a new section which begins here. + const char *p = line.c_str() + 1; + // Get the section name, and check whether it's valid (that + // is, verify that it only consists of alphanumerics, + // periods, dashes and underscores). Mohawk Living Books games + // can have periods in their section names. + while (*p && (isAlnum(*p) || *p == '-' || *p == '_' || *p == '.')) + p++; + + if (*p == '\0') + error("INIFile::loadFromStream: missing ] in line %d", lineno); + else if (*p != ']') + error("INIFile::loadFromStream: Invalid character '%c' occurred in section name in line %d", *p, lineno); + + // Previous section is finished now, store it. + if (!section.name.empty()) + _sections.push_back(section); + + section.name = String(line.c_str() + 1, p); + section.keys.clear(); + section.comment = comment; + comment.clear(); + + assert(isValidName(section.name)); + } else { + // This line should be a line with a 'key=value' pair, or an empty one. + + // Skip leading whitespaces + const char *t = line.c_str(); + while (isSpace(*t)) + t++; + + // Skip empty lines / lines with only whitespace + if (*t == 0) + continue; + + // If no section has been set, this config file is invalid! + if (section.name.empty()) { + error("INIFile::loadFromStream: Key/value pair found outside a section in line %d", lineno); + } + + // Split string at '=' into 'key' and 'value'. First, find the "=" delimeter. + const char *p = strchr(t, '='); + if (!p) + error("Config file buggy: Junk found in line line %d: '%s'", lineno, t); + + // Extract the key/value pair + kv.key = String(t, p); + kv.value = String(p + 1); + + // Trim of spaces + kv.key.trim(); + kv.value.trim(); + + // Store comment + kv.comment = comment; + comment.clear(); + + assert(isValidName(kv.key)); + + section.keys.push_back(kv); + } + } + + // Save last section + if (!section.name.empty()) + _sections.push_back(section); + + return (!stream.err() || stream.eos()); +} + +bool INIFile::saveToFile(const String &filename) { + DumpFile file; + if (file.open(filename)) + return saveToStream(file); + else + return false; +} + +bool INIFile::saveToSaveFile(const char *filename) { + assert(g_system); + SaveFileManager *saveFileMan = g_system->getSavefileManager(); + WriteStream *saveFile; + + assert(saveFileMan); + if (!(saveFile = saveFileMan->openForSaving(filename))) + return false; + + bool status = saveToStream(*saveFile); + delete saveFile; + return status; +} + +bool INIFile::saveToStream(WriteStream &stream) { + for (List
::iterator i = _sections.begin(); i != _sections.end(); ++i) { + // Write out the section comment, if any + if (! i->comment.empty()) { + stream.writeString(i->comment); + } + + // Write out the section name + stream.writeByte('['); + stream.writeString(i->name); + stream.writeByte(']'); + stream.writeByte('\n'); + + // Write out the key/value pairs + for (List::iterator kv = i->keys.begin(); kv != i->keys.end(); ++kv) { + // Write out the comment, if any + if (! kv->comment.empty()) { + stream.writeString(kv->comment); + } + // Write out the key/value pair + stream.writeString(kv->key); + stream.writeByte('='); + stream.writeString(kv->value); + stream.writeByte('\n'); + } + } + + stream.flush(); + return !stream.err(); +} + +void INIFile::addSection(const String §ion) { + Section *s = getSection(section); + if (s) + return; + + Section newSection; + newSection.name = section; + _sections.push_back(newSection); +} + +void INIFile::removeSection(const String §ion) { + assert(isValidName(section)); + for (List
::iterator i = _sections.begin(); i != _sections.end(); ++i) { + if (section.equalsIgnoreCase(i->name)) { + _sections.erase(i); + return; + } + } +} + +bool INIFile::hasSection(const String §ion) const { + assert(isValidName(section)); + const Section *s = getSection(section); + return s != 0; +} + +void INIFile::renameSection(const String &oldName, const String &newName) { + assert(isValidName(oldName)); + assert(isValidName(newName)); + + Section *os = getSection(oldName); + const Section *ns = getSection(newName); + if (os) { + // HACK: For now we just print a warning, for more info see the TODO + // below. + if (ns) + warning("INIFile::renameSection: Section name \"%s\" already used", newName.c_str()); + else + os->name = newName; + } + // TODO: Check here whether there already is a section with the + // new name. Not sure how to cope with that case, we could: + // - simply remove the existing "newName" section + // - error out + // - merge the two sections "oldName" and "newName" +} + + +bool INIFile::hasKey(const String &key, const String §ion) const { + assert(isValidName(key)); + assert(isValidName(section)); + + const Section *s = getSection(section); + if (!s) + return false; + return s->hasKey(key); +} + +void INIFile::removeKey(const String &key, const String §ion) { + assert(isValidName(key)); + assert(isValidName(section)); + + Section *s = getSection(section); + if (s) + s->removeKey(key); +} + +bool INIFile::getKey(const String &key, const String §ion, String &value) const { + assert(isValidName(key)); + assert(isValidName(section)); + + const Section *s = getSection(section); + if (!s) + return false; + const KeyValue *kv = s->getKey(key); + if (!kv) + return false; + value = kv->value; + return true; +} + +void INIFile::setKey(const String &key, const String §ion, const String &value) { + assert(isValidName(key)); + assert(isValidName(section)); + // TODO: Verify that value is valid, too. In particular, it shouldn't + // contain CR or LF... + + Section *s = getSection(section); + if (!s) { + KeyValue newKV; + newKV.key = key; + newKV.value = value; + + Section newSection; + newSection.name = section; + newSection.keys.push_back(newKV); + + _sections.push_back(newSection); + } else { + s->setKey(key, value); + } +} + +const INIFile::SectionKeyList INIFile::getKeys(const String §ion) const { + const Section *s = getSection(section); + + return s->getKeys(); +} + +INIFile::Section *INIFile::getSection(const String §ion) { + for (List
::iterator i = _sections.begin(); i != _sections.end(); ++i) { + if (section.equalsIgnoreCase(i->name)) { + return &(*i); + } + } + return 0; +} + +const INIFile::Section *INIFile::getSection(const String §ion) const { + for (List
::const_iterator i = _sections.begin(); i != _sections.end(); ++i) { + if (section.equalsIgnoreCase(i->name)) { + return &(*i); + } + } + return 0; +} + +bool INIFile::Section::hasKey(const String &key) const { + return getKey(key) != 0; +} + +const INIFile::KeyValue* INIFile::Section::getKey(const String &key) const { + for (List::const_iterator i = keys.begin(); i != keys.end(); ++i) { + if (key.equalsIgnoreCase(i->key)) { + return &(*i); + } + } + return 0; +} + +void INIFile::Section::setKey(const String &key, const String &value) { + for (List::iterator i = keys.begin(); i != keys.end(); ++i) { + if (key.equalsIgnoreCase(i->key)) { + i->value = value; + return; + } + } + + KeyValue newKV; + newKV.key = key; + newKV.value = value; + keys.push_back(newKV); +} + +void INIFile::Section::removeKey(const String &key) { + for (List::iterator i = keys.begin(); i != keys.end(); ++i) { + if (key.equalsIgnoreCase(i->key)) { + keys.erase(i); + return; + } + } +} + +} // End of namespace Common diff --git a/common/ini-file.h b/common/ini-file.h new file mode 100644 index 0000000000..c7da6de50e --- /dev/null +++ b/common/ini-file.h @@ -0,0 +1,140 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef COMMON_INI_FILE_H +#define COMMON_INI_FILE_H + +#include "common/hash-str.h" +#include "common/list.h" +#include "common/str.h" + +namespace Common { + +class SeekableReadStream; +class WriteStream; + +/** + * This class allows reading/writing INI style config files. + * It is used by the ConfigManager for storage, but can also + * be used by other code if it needs to read/write custom INI + * files. + * + * Lines starting with a '#' are ignored (i.e. treated as comments). + * Some effort is made to preserve comments, though. + * + * This class makes no attempts to provide fast access to key/value pairs. + * In particular, it stores all sections and k/v pairs in lists, not + * in dictionaries/maps. This makes it very easy to read/write the data + * from/to files, but of course is not appropriate for fast access. + * The main reason is that this class is indeed geared toward doing precisely + * that! + * If you need fast access to the game config, use higher level APIs, like the + * one provided by ConfigManager. + */ +class INIFile { +public: + struct KeyValue { + String key; + String value; + String comment; + }; + + typedef List SectionKeyList; + + /** A section in a ini file. I.e. corresponds to something like this: + * [mySection] + * key=value + * + * Comments are also stored, to keep users happy who like editing their + * ini files manually. + */ + struct Section { + String name; + List keys; + String comment; + + bool hasKey(const String &key) const; + const KeyValue* getKey(const String &key) const; + void setKey(const String &key, const String &value); + void removeKey(const String &key); + const SectionKeyList getKeys() const { return keys; } + }; + + typedef List
SectionList; + +public: + INIFile(); + ~INIFile(); + + // TODO: Maybe add a copy constructor etc.? + + /** + * Check whether the given string is a valid section or key name. + * For that, it must only consist of letters, numbers, dashes and + * underscores. In particular, white space and "#", "=", "[", "]" + * are not valid! + */ + static bool isValidName(const String &name); + + /** Reset everything stored in this ini file. */ + void clear(); + + bool loadFromFile(const String &filename); + bool loadFromSaveFile(const char *filename); + bool loadFromStream(SeekableReadStream &stream); + bool saveToFile(const String &filename); + bool saveToSaveFile(const char *filename); + bool saveToStream(WriteStream &stream); + + bool hasSection(const String §ion) const; + void addSection(const String §ion); + void removeSection(const String §ion); + void renameSection(const String &oldName, const String &newName); + + bool hasKey(const String &key, const String §ion) const; + bool getKey(const String &key, const String §ion, String &value) const; + void setKey(const String &key, const String §ion, const String &value); + void removeKey(const String &key, const String §ion); + + const SectionList getSections() const { return _sections; } + const SectionKeyList getKeys(const String §ion) const; + + void listKeyValues(StringMap &kv); + +private: + SectionList _sections; + + Section *getSection(const String §ion); + const Section *getSection(const String §ion) const; +}; + +/* +- ConfigMan owns a config file +- allow direct access to that config file (for the launcher) +- simplify and unify the regular ConfigMan API in exchange + + +*/ + +} // End of namespace Common + +#endif diff --git a/common/module.mk b/common/module.mk index 9f9126c8ef..1b34d151d0 100644 --- a/common/module.mk +++ b/common/module.mk @@ -2,7 +2,6 @@ MODULE := common MODULE_OBJS := \ archive.o \ - config-file.o \ config-manager.o \ coroutines.o \ dcl.o \ @@ -15,6 +14,7 @@ MODULE_OBJS := \ gui_options.o \ hashmap.o \ iff_container.o \ + ini-file.o \ installshield_cab.o \ language.o \ localization.o \ diff --git a/engines/composer/composer.h b/engines/composer/composer.h index 33a5356b3a..7d8022455a 100644 --- a/engines/composer/composer.h +++ b/engines/composer/composer.h @@ -23,7 +23,7 @@ #ifndef COMPOSER_H #define COMPOSER_H -#include "common/config-file.h" +#include "common/ini-file.h" #include "common/random.h" #include "common/system.h" #include "common/debug.h" @@ -174,7 +174,7 @@ private: Common::List _sprites; uint _directoriesToStrip; - Common::ConfigFile _bookIni; + Common::INIFile _bookIni; Common::String _bookGroup; Common::List _libraries; Common::Array _pendingPageChanges; diff --git a/engines/gob/iniconfig.cpp b/engines/gob/iniconfig.cpp index bba531723c..032231bd4d 100644 --- a/engines/gob/iniconfig.cpp +++ b/engines/gob/iniconfig.cpp @@ -73,7 +73,7 @@ bool INIConfig::getConfig(const Common::String &file, Config &config) { } bool INIConfig::openConfig(const Common::String &file, Config &config) { - config.config = new Common::ConfigFile(); + config.config = new Common::INIFile(); config.created = false; if (!config.config->loadFromFile(file)) { @@ -89,7 +89,7 @@ bool INIConfig::openConfig(const Common::String &file, Config &config) { } bool INIConfig::createConfig(const Common::String &file, Config &config) { - config.config = new Common::ConfigFile(); + config.config = new Common::INIFile(); config.created = true; _configs.setVal(file, config); diff --git a/engines/gob/iniconfig.h b/engines/gob/iniconfig.h index bf60f2d125..c1890170dc 100644 --- a/engines/gob/iniconfig.h +++ b/engines/gob/iniconfig.h @@ -24,7 +24,7 @@ #define GOB_INICONFIG_H #include "common/str.h" -#include "common/config-file.h" +#include "common/ini-file.h" #include "common/hashmap.h" namespace Gob { @@ -43,7 +43,7 @@ public: private: struct Config { - Common::ConfigFile *config; + Common::INIFile *config; bool created; }; diff --git a/engines/mohawk/livingbooks.cpp b/engines/mohawk/livingbooks.cpp index efa0dd3fd3..634ff441b6 100644 --- a/engines/mohawk/livingbooks.cpp +++ b/engines/mohawk/livingbooks.cpp @@ -311,8 +311,8 @@ void MohawkEngine_LivingBooks::loadBookInfo(const Common::String &filename) { // - fDebugWindow (always 0?) if (_bookInfoFile.hasSection("Globals")) { - const Common::ConfigFile::SectionKeyList globals = _bookInfoFile.getKeys("Globals"); - for (Common::ConfigFile::SectionKeyList::const_iterator i = globals.begin(); i != globals.end(); i++) { + const Common::INIFile::SectionKeyList globals = _bookInfoFile.getKeys("Globals"); + for (Common::INIFile::SectionKeyList::const_iterator i = globals.begin(); i != globals.end(); i++) { Common::String command = Common::String::format("%s = %s", i->key.c_str(), i->value.c_str()); LBCode tempCode(this, 0); uint offset = tempCode.parseCode(command); diff --git a/engines/mohawk/livingbooks.h b/engines/mohawk/livingbooks.h index 76da7d8219..615fcd0e16 100644 --- a/engines/mohawk/livingbooks.h +++ b/engines/mohawk/livingbooks.h @@ -28,7 +28,7 @@ #include "mohawk/livingbooks_graphics.h" #include "mohawk/sound.h" -#include "common/config-file.h" +#include "common/ini-file.h" #include "common/rect.h" #include "common/queue.h" #include "common/random.h" @@ -759,7 +759,7 @@ public: private: LivingBooksConsole *_console; - Common::ConfigFile _bookInfoFile; + Common::INIFile _bookInfoFile; Common::String getBookInfoFileName() const; void loadBookInfo(const Common::String &filename); diff --git a/engines/mohawk/livingbooks_lbx.cpp b/engines/mohawk/livingbooks_lbx.cpp index 2b8b22ec81..dcf8caa4a5 100644 --- a/engines/mohawk/livingbooks_lbx.cpp +++ b/engines/mohawk/livingbooks_lbx.cpp @@ -33,7 +33,7 @@ public: bool call(uint callId, const Common::Array ¶ms, LBValue &result); protected: - Common::ConfigFile _dataFile; + Common::INIFile _dataFile; Common::String _curSection; void open(const Common::String &filename); @@ -77,8 +77,8 @@ bool LBXDataFile::call(uint callId, const Common::Array ¶ms, LBValu case kLBXDataFileGetSectionList: { Common::SharedPtr list = Common::SharedPtr(new LBList); - Common::ConfigFile::SectionList sections = _dataFile.getSections(); - for (Common::List::const_iterator i = sections.begin(); i != sections.end(); ++i) + Common::INIFile::SectionList sections = _dataFile.getSections(); + for (Common::List::const_iterator i = sections.begin(); i != sections.end(); ++i) list->array.push_back(LBValue(i->name)); result = LBValue(list); } @@ -103,8 +103,8 @@ bool LBXDataFile::call(uint callId, const Common::Array ¶ms, LBValu error("incorrect number of parameters (%d) to LBXDataFile::loadCurSectionVars", params.size()); { - const Common::ConfigFile::SectionKeyList globals = _dataFile.getKeys(_curSection); - for (Common::ConfigFile::SectionKeyList::const_iterator i = globals.begin(); i != globals.end(); i++) { + const Common::INIFile::SectionKeyList globals = _dataFile.getKeys(_curSection); + for (Common::INIFile::SectionKeyList::const_iterator i = globals.begin(); i != globals.end(); i++) { Common::String command = Common::String::format("%s = %s", i->key.c_str(), i->value.c_str()); LBCode tempCode(_vm, 0); uint offset = tempCode.parseCode(command); diff --git a/engines/scumm/he/script_v80he.cpp b/engines/scumm/he/script_v80he.cpp index eb62b650a4..ae43d714ad 100644 --- a/engines/scumm/he/script_v80he.cpp +++ b/engines/scumm/he/script_v80he.cpp @@ -23,7 +23,7 @@ #ifdef ENABLE_HE #include "common/archive.h" -#include "common/config-file.h" +#include "common/ini-file.h" #include "common/config-manager.h" #include "common/macresman.h" #include "common/savefile.h" @@ -180,7 +180,7 @@ void ScummEngine_v80he::o80_readConfigFile() { } } else { // Normal Windows INI files - Common::ConfigFile confFile; + Common::INIFile confFile; if (!strcmp((char *)filename + r, "map.ini")) confFile.loadFromFile((const char *)filename + r); else @@ -250,7 +250,7 @@ void ScummEngine_v80he::o80_writeConfigFile() { memcpy(section, "BluesTreasureHunt-Disc2\0", 24); } - Common::ConfigFile ConfFile; + Common::INIFile ConfFile; ConfFile.loadFromSaveFile((const char *)filename + r); ConfFile.setKey((char *)option, (char *)section, (char *)string); ConfFile.saveToSaveFile((const char *)filename + r); diff --git a/engines/testbed/config.cpp b/engines/testbed/config.cpp index 6b56616c9b..a40d239ebf 100644 --- a/engines/testbed/config.cpp +++ b/engines/testbed/config.cpp @@ -213,22 +213,22 @@ void TestbedConfigManager::parseConfigFile() { return; } _configFileInterface.loadFromStream(*rs); - Common::ConfigFile::SectionList sections = _configFileInterface.getSections(); + Common::INIFile::SectionList sections = _configFileInterface.getSections(); Testsuite *currTS = 0; - for (Common::ConfigFile::SectionList::const_iterator i = sections.begin(); i != sections.end(); i++) { + for (Common::INIFile::SectionList::const_iterator i = sections.begin(); i != sections.end(); i++) { if (i->name.equalsIgnoreCase("Global")) { // Global params may be directly queried, ignore them } else { // A testsuite, process it. currTS = getTestsuiteByName(i->name); - Common::ConfigFile::SectionKeyList kList = i->getKeys(); + Common::INIFile::SectionKeyList kList = i->getKeys(); if (!currTS) { Testsuite::logPrintf("Warning! Error in config: Testsuite %s not found\n", i->name.c_str()); continue; } - for (Common::ConfigFile::SectionKeyList::const_iterator j = kList.begin(); j != kList.end(); j++) { + for (Common::INIFile::SectionKeyList::const_iterator j = kList.begin(); j != kList.end(); j++) { if (j->key.equalsIgnoreCase("this")) { currTS->enable(stringToBool(j->value)); } else { diff --git a/engines/testbed/config.h b/engines/testbed/config.h index fd5588aa31..d611ae4ec3 100644 --- a/engines/testbed/config.h +++ b/engines/testbed/config.h @@ -24,7 +24,7 @@ #include "common/array.h" -#include "common/config-file.h" +#include "common/ini-file.h" #include "common/str-array.h" #include "common/tokenizer.h" @@ -62,7 +62,7 @@ public: private: Common::Array &_testsuiteList; Common::String _configFileName; - Common::ConfigFile _configFileInterface; + Common::INIFile _configFileInterface; void parseConfigFile(); }; -- cgit v1.2.3