[Scummvm-cvs-logs] SF.net SVN: scummvm:[33428] residual/trunk
aquadran at users.sourceforge.net
aquadran at users.sourceforge.net
Wed Jul 30 00:12:53 CEST 2008
Revision: 33428
http://scummvm.svn.sourceforge.net/scummvm/?rev=33428&view=rev
Author: aquadran
Date: 2008-07-29 22:12:52 +0000 (Tue, 29 Jul 2008)
Log Message:
-----------
added config manager common code
Modified Paths:
--------------
residual/trunk/common/sys.h
residual/trunk/dists/msvc7/residual.vcproj
residual/trunk/dists/msvc71/residual.vcproj
residual/trunk/dists/msvc8/residual.vcproj
residual/trunk/dists/msvc9/residual.vcproj
Added Paths:
-----------
residual/trunk/common/config-file.cpp
residual/trunk/common/config-file.h
residual/trunk/common/config-manager.cpp
residual/trunk/common/config-manager.h
Added: residual/trunk/common/config-file.cpp
===================================================================
--- residual/trunk/common/config-file.cpp (rev 0)
+++ residual/trunk/common/config-file.cpp 2008-07-29 22:12:52 UTC (rev 33428)
@@ -0,0 +1,375 @@
+/* Residual - Virtual machine to run LucasArts' 3D adventure games
+ *
+ * Residual is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the AUTHORS
+ * 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/config-file.h"
+#include "common/file.h"
+#include "common/savefile.h"
+#include "common/util.h"
+#include "common/debug.h"
+
+#include "engine/backend/driver.h"
+
+#define MAXLINELEN 256
+
+namespace Common {
+
+/**
+ * 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!
+ */
+bool ConfigFile::isValidName(const Common::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, File::kFileReadMode))
+ return loadFromStream(file);
+ else
+ return false;
+}
+
+bool ConfigFile::loadFromSaveFile(const char *filename) {
+ SaveFileManager *saveFileMan = g_driver->getSavefileManager();
+ SeekableReadStream *loadFile;
+
+ if (!(loadFile = saveFileMan->openForLoading(filename)))
+ return false;
+
+ bool status = loadFromStream(*loadFile);
+ delete loadFile;
+ return status;
+}
+
+bool ConfigFile::loadFromStream(SeekableReadStream &stream) {
+ char buf[MAXLINELEN];
+ 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()) {
+ lineno++;
+ if (!stream.readLine(buf, MAXLINELEN))
+ break;
+
+ if (buf[0] == '#') {
+ // 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.
+ comment += buf;
+ comment += "\n";
+ } else if (buf[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 += buf;
+ comment += "\n";
+ } else if (buf[0] == '[') {
+ // It's a new section which begins here.
+ char *p = buf + 1;
+ // Get the section name, and check whether it's valid (that
+ // is, verify that it only consists of alphanumerics,
+ // dashes and underscores).
+ while (*p && (isalnum(*p) || *p == '-' || *p == '_'))
+ p++;
+
+ if (*p == '\0')
+ error("ConfigFile::loadFromStream: missing ] in line %d", lineno);
+ else if (*p != ']')
+ error("ConfigFile::loadFromStream: Invalid character '%c' occured in section name in line %d", *p, lineno);
+
+ *p = 0;
+
+ // Previous section is finished now, store it.
+ if (!section.name.empty())
+ _sections.push_back(section);
+
+ section.name = buf + 1;
+ section.keys.clear();
+ section.comment = comment;
+ comment.clear();
+
+ assert(isValidName(section.name));
+ } else {
+ // Skip leading & trailing whitespaces
+ char *t = rtrim(ltrim(buf));
+
+ // Skip empty lines
+ 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'.
+ char *p = strchr(t, '=');
+ if (!p)
+ error("ConfigFile::loadFromStream: Junk found in line line %d: '%s'", lineno, t);
+ *p = 0;
+
+ kv.key = rtrim(t);
+ kv.value = ltrim(p + 1);
+ 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.ioFailed() || stream.eos());
+}
+
+bool ConfigFile::saveToFile(const String &filename) {
+ File file;
+ if (file.open(filename, File::kFileWriteMode))
+ return saveToStream(file);
+ else
+ return false;
+}
+
+bool ConfigFile::saveToSaveFile(const char *filename) {
+ SaveFileManager *saveFileMan = g_driver->getSavefileManager();
+ WriteStream *saveFile;
+
+ if (!(saveFile = saveFileMan->openForSaving(filename)))
+ return false;
+
+ bool status = saveToStream(*saveFile);
+ delete saveFile;
+ return status;
+}
+
+bool ConfigFile::saveToStream(WriteStream &stream) {
+ for (List<Section>::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<KeyValue>::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.ioFailed();
+}
+
+
+void ConfigFile::removeSection(const String §ion) {
+ assert(isValidName(section));
+ for (List<Section>::iterator i = _sections.begin(); i != _sections.end(); ++i) {
+ if (!strcasecmp(section.c_str(), i->name.c_str())) {
+ _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);
+ Section *ns = getSection(newName);
+ if (ns) {
+ ns->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<Section>::iterator i = _sections.begin(); i != _sections.end(); ++i) {
+ if (!strcasecmp(section.c_str(), i->name.c_str())) {
+ return &(*i);
+ }
+ }
+ return 0;
+}
+
+const ConfigFile::Section *ConfigFile::getSection(const String §ion) const {
+ for (List<Section>::const_iterator i = _sections.begin(); i != _sections.end(); ++i) {
+ if (!strcasecmp(section.c_str(), i->name.c_str())) {
+ 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<KeyValue>::const_iterator i = keys.begin(); i != keys.end(); ++i) {
+ if (!strcasecmp(key.c_str(), i->key.c_str())) {
+ return &(*i);
+ }
+ }
+ return 0;
+}
+
+void ConfigFile::Section::setKey(const String &key, const String &value) {
+ for (List<KeyValue>::iterator i = keys.begin(); i != keys.end(); ++i) {
+ if (!strcasecmp(key.c_str(), i->key.c_str())) {
+ 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<KeyValue>::iterator i = keys.begin(); i != keys.end(); ++i) {
+ if (!strcasecmp(key.c_str(), i->key.c_str())) {
+ keys.erase(i);
+ return;
+ }
+ }
+}
+
+} // End of namespace Common
Property changes on: residual/trunk/common/config-file.cpp
___________________________________________________________________
Added: svn:mime-type
+ text/native
Added: svn:keywords
+ Date Revision Author URL Id
Added: svn:eol-style
+ native
Added: residual/trunk/common/config-file.h
===================================================================
--- residual/trunk/common/config-file.h (rev 0)
+++ residual/trunk/common/config-file.h 2008-07-29 22:12:52 UTC (rev 33428)
@@ -0,0 +1,143 @@
+/* Residual - Virtual machine to run LucasArts' 3D adventure games
+ *
+ * Residual is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the AUTHORS
+ * 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef COMMON_CONFIG_FILE_H
+#define COMMON_CONFIG_FILE_H
+
+#include "common/config-manager.h"
+#include "common/list.h"
+#include "common/str.h"
+#include "common/stream.h"
+
+namespace Common {
+
+/**
+ * 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:
+ typedef HashMap<String, bool, IgnoreCase_Hash, IgnoreCase_EqualTo> StringSet;
+
+ struct KeyValue {
+ String key;
+ String value;
+ String comment;
+ };
+
+ typedef List<KeyValue> 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<KeyValue> 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<Section> 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 Common::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 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 listSections(StringSet &set);
+ 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
Property changes on: residual/trunk/common/config-file.h
___________________________________________________________________
Added: svn:mime-type
+ text/plain
Added: svn:keywords
+ Date Revision Author URL Id
Added: svn:eol-style
+ native
Added: residual/trunk/common/config-manager.cpp
===================================================================
--- residual/trunk/common/config-manager.cpp (rev 0)
+++ residual/trunk/common/config-manager.cpp 2008-07-29 22:12:52 UTC (rev 33428)
@@ -0,0 +1,696 @@
+/* Residual - Virtual machine to run LucasArts' 3D adventure games
+ *
+ * Residual is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the AUTHORS
+ * 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#if defined(WIN32)
+#include <windows.h>
+// winnt.h defines ARRAYSIZE, but we want our own one...
+#undef ARRAYSIZE
+#endif
+
+#include "common/config-manager.h"
+#include "common/file.h"
+#include "common/util.h"
+#include "common/debug.h"
+
+DECLARE_SINGLETON(Common::ConfigManager);
+
+#ifdef __PLAYSTATION2__
+#include "backends/platform/ps2/systemps2.h"
+#endif
+
+#ifdef IPHONE
+#include "backends/platform/iphone/osys_iphone.h"
+#endif
+
+#if defined(UNIX)
+#ifdef MACOSX
+#define DEFAULT_CONFIG_FILE "Library/Preferences/Resdial Preferences"
+#else
+#define DEFAULT_CONFIG_FILE ".residualrc"
+#endif
+#else
+#define DEFAULT_CONFIG_FILE "residual.ini"
+#endif
+
+#define MAXLINELEN 256
+
+static bool isValidDomainName(const Common::String &domName) {
+ const char *p = domName.c_str();
+ while (*p && (isalnum(*p) || *p == '-' || *p == '_'))
+ p++;
+ return *p == 0;
+}
+
+namespace Common {
+
+#if !(defined(PALMOS_ARM) || defined(PALMOS_DEBUG) || defined(__GP32__))
+
+const String ConfigManager::kApplicationDomain("residual");
+const String ConfigManager::kTransientDomain("__TRANSIENT");
+
+#else
+
+const char *ConfigManager::kApplicationDomain = "residual";
+const char *ConfigManager::kTransientDomain = "__TRANSIENT";
+
+#endif
+
+#pragma mark -
+
+
+ConfigManager::ConfigManager()
+ : _activeDomain(0) {
+}
+
+
+void ConfigManager::loadDefaultConfigFile() {
+ char configFile[MAXPATHLEN];
+ // GP2X is Linux based but Home dir can be read only so do not use it and put the config in the executable dir.
+ // On the iPhone, the home dir of the user when you launch the app from the Springboard, is /. Which we don't want.
+#if defined(UNIX) && !defined(GP2X) && !defined(IPHONE)
+ const char *home = getenv("HOME");
+ if (home != NULL && strlen(home) < MAXPATHLEN)
+ snprintf(configFile, MAXPATHLEN, "%s/%s", home, DEFAULT_CONFIG_FILE);
+ else
+ strcpy(configFile, DEFAULT_CONFIG_FILE);
+#else
+ #if defined (WIN32) && !defined(_WIN32_WCE) && !defined(__SYMBIAN32__)
+ OSVERSIONINFO win32OsVersion;
+ ZeroMemory(&win32OsVersion, sizeof(OSVERSIONINFO));
+ win32OsVersion.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+ GetVersionEx(&win32OsVersion);
+ // Check for non-9X version of Windows.
+ if (win32OsVersion.dwPlatformId != VER_PLATFORM_WIN32_WINDOWS) {
+ // Use the Application Data directory of the user profile.
+ if (win32OsVersion.dwMajorVersion >= 5) {
+ if (!GetEnvironmentVariable("APPDATA", configFile, sizeof(configFile)))
+ error("Unable to access application data directory");
+ } else {
+ if (!GetEnvironmentVariable("USERPROFILE", configFile, sizeof(configFile)))
+ error("Unable to access user profile directory");
+
+ strcat(configFile, "\\Application Data");
+ CreateDirectory(configFile, NULL);
+ }
+
+ strcat(configFile, "\\Residual");
+ CreateDirectory(configFile, NULL);
+ strcat(configFile, "\\" DEFAULT_CONFIG_FILE);
+ } else {
+ // Check windows directory
+ GetWindowsDirectory(configFile, MAXPATHLEN);
+ strcat(configFile, "\\" DEFAULT_CONFIG_FILE);
+ }
+
+ #elif defined(PALMOS_MODE)
+ strcpy(configFile,"/PALM/Programs/ScummVM/" DEFAULT_CONFIG_FILE);
+ #elif defined(IPHONE)
+ strcpy(configFile, OSystem_IPHONE::getConfigPath());
+ #elif defined(__PLAYSTATION2__)
+ ((OSystem_PS2*)g_system)->makeConfigPath(configFile);
+ #elif defined(__PSP__)
+ strcpy(configFile, "ms0:/" DEFAULT_CONFIG_FILE);
+ #elif defined (__SYMBIAN32__)
+ strcpy(configFile, Symbian::GetExecutablePath());
+ strcat(configFile, DEFAULT_CONFIG_FILE);
+ #else
+ strcpy(configFile, DEFAULT_CONFIG_FILE);
+ #endif
+#endif
+
+ loadConfigFile(configFile);
+ flushToDisk();
+}
+
+void ConfigManager::loadConfigFile(const String &filename) {
+ _appDomain.clear();
+ _gameDomains.clear();
+ _transientDomain.clear();
+
+ _filename = filename;
+ _domainSaveOrder.clear();
+ loadFile(_filename);
+ printf("Using configuration file: %s\n", _filename.c_str());
+}
+
+void ConfigManager::loadFile(const String &filename) {
+ File cfg_file;
+
+ if (!cfg_file.open(filename)) {
+ printf("Creating configuration file: %s\n", filename.c_str());
+ } else {
+ 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 (!cfg_file.eof() && !cfg_file.ioFailed()) {
+ lineno++;
+
+ // Read a line
+ String line;
+ while (line.lastChar() != '\n') {
+ char buf[MAXLINELEN];
+ if (!cfg_file.readLine_NEW(buf, MAXLINELEN))
+ break;
+ line += buf;
+ }
+
+ if (line.size() == 0) {
+ // Do nothing
+ } else if (line[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 += line;
+ } else if (line[0] == '[') {
+ // It's a new domain which begins here.
+ const char *p = line.c_str() + 1;
+ // Get the domain name, and check whether it's valid (that
+ // is, verify that it only consists of alphanumerics,
+ // dashes and underscores).
+ while (*p && (isalnum(*p) || *p == '-' || *p == '_'))
+ p++;
+
+ switch (*p) {
+ case '\0':
+ error("Config file buggy: missing ] in line %d", lineno);
+ break;
+ case ']':
+ domain = String(line.c_str() + 1, p - (line.c_str() + 1));
+ //domain = String(line.c_str() + 1, p); // TODO: Pending Common::String changes
+ break;
+ default:
+ error("Config file buggy: Invalid character '%c' occured in domain name in line %d", *p, lineno);
+ }
+
+ // Store domain comment
+ if (domain == kApplicationDomain) {
+ _appDomain.setDomainComment(comment);
+ } else {
+ _gameDomains[domain].setDomainComment(comment);
+ }
+ comment.clear();
+
+ _domainSaveOrder.push_back(domain);
+ } 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 domain has been set, this config file is invalid!
+ if (domain.empty()) {
+ error("Config file buggy: Key/value pair found outside a domain 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);
+
+ // Trim spaces before the '=' to obtain the key
+ const char *p2 = p;
+ while (p2 > t && isspace(*(p2-1)))
+ p2--;
+ String key(t, p2 - t);
+
+ // Skip spaces after the '='
+ t = p + 1;
+ while (isspace(*t))
+ t++;
+
+ // Trim trailing spaces
+ p2 = t + strlen(t);
+ while (p2 > t && isspace(*(p2-1)))
+ p2--;
+
+ String value(t, p2 - t);
+
+ // Finally, store the key/value pair in the active domain
+ set(key, value, domain);
+
+ // Store comment
+ if (domain == kApplicationDomain) {
+ _appDomain.setKVComment(key, comment);
+ } else {
+ _gameDomains[domain].setKVComment(key, comment);
+ }
+ comment.clear();
+ }
+ }
+ }
+}
+
+void ConfigManager::flushToDisk() {
+#ifndef __DC__
+ File cfg_file;
+
+// TODO
+// if (!willwrite)
+// return;
+
+ if (!cfg_file.open(_filename, File::kFileWriteMode)) {
+ warning("Unable to write configuration file: %s", _filename.c_str());
+ } else {
+ // First write the domains in _domainSaveOrder, in that order.
+ // Note: It's possible for _domainSaveOrder to list domains which
+ // are not present anymore.
+ StringList::const_iterator i;
+ for (i = _domainSaveOrder.begin(); i != _domainSaveOrder.end(); ++i) {
+ if (kApplicationDomain == *i) {
+ writeDomain(cfg_file, *i, _appDomain);
+ } else if (_gameDomains.contains(*i)) {
+ writeDomain(cfg_file, *i, _gameDomains[*i]);
+ }
+ }
+
+ DomainMap::const_iterator d;
+
+
+ // Now write the domains which haven't been written yet
+ if (find(_domainSaveOrder.begin(), _domainSaveOrder.end(), kApplicationDomain) == _domainSaveOrder.end())
+ writeDomain(cfg_file, kApplicationDomain, _appDomain);
+ for (d = _gameDomains.begin(); d != _gameDomains.end(); ++d) {
+ if (find(_domainSaveOrder.begin(), _domainSaveOrder.end(), d->_key) == _domainSaveOrder.end())
+ writeDomain(cfg_file, d->_key, d->_value);
+ }
+ }
+#endif // !__DC__
+}
+
+void ConfigManager::writeDomain(WriteStream &stream, const String &name, const Domain &domain) {
+ if (domain.empty())
+ return; // Don't bother writing empty domains.
+
+ String comment;
+
+ // Write domain comment (if any)
+ comment = domain.getDomainComment();
+ if (!comment.empty())
+ stream.writeString(comment);
+
+ // Write domain start
+ stream.writeByte('[');
+ stream.writeString(name);
+ stream.writeByte(']');
+ stream.writeByte('\n');
+
+ // Write all key/value pairs in this domain, including comments
+ Domain::const_iterator x;
+ for (x = domain.begin(); x != domain.end(); ++x) {
+ if (!x->_value.empty()) {
+ // Write comment (if any)
+ if (domain.hasKVComment(x->_key)) {
+ comment = domain.getKVComment(x->_key);
+ stream.writeString(comment);
+ }
+ // Write the key/value pair
+ stream.writeString(x->_key);
+ stream.writeByte('=');
+ stream.writeString(x->_value);
+ stream.writeByte('\n');
+ }
+ }
+ stream.writeByte('\n');
+}
+
+
+#pragma mark -
+
+
+const ConfigManager::Domain *ConfigManager::getDomain(const String &domName) const {
+ assert(!domName.empty());
+ assert(isValidDomainName(domName));
+
+ if (domName == kTransientDomain)
+ return &_transientDomain;
+ if (domName == kApplicationDomain)
+ return &_appDomain;
+ if (_gameDomains.contains(domName))
+ return &_gameDomains[domName];
+
+ return 0;
+}
+
+ConfigManager::Domain *ConfigManager::getDomain(const String &domName) {
+ assert(!domName.empty());
+ assert(isValidDomainName(domName));
+
+ if (domName == kTransientDomain)
+ return &_transientDomain;
+ if (domName == kApplicationDomain)
+ return &_appDomain;
+ if (_gameDomains.contains(domName))
+ return &_gameDomains[domName];
+
+ return 0;
+}
+
+
+#pragma mark -
+
+
+bool ConfigManager::hasKey(const String &key) const {
+ // Search the domains in the following order:
+ // 1) the transient domain,
+ // 2) the active game domain (if any),
+ // 3) the application domain.
+ // The defaults domain is explicitly *not* checked.
+
+ if (_transientDomain.contains(key))
+ return true;
+
+ if (_activeDomain && _activeDomain->contains(key))
+ return true;
+
+ if (_appDomain.contains(key))
+ return true;
+
+ return false;
+}
+
+bool ConfigManager::hasKey(const String &key, const String &domName) const {
+ // FIXME: For now we continue to allow empty domName to indicate
+ // "use 'default' domain". This is mainly needed for the SCUMM ConfigDialog
+ // and should be removed ASAP.
+ if (domName.empty())
+ return hasKey(key);
+
+ const Domain *domain = getDomain(domName);
+
+ if (!domain)
+ return false;
+ return domain->contains(key);
+}
+
+void ConfigManager::removeKey(const String &key, const String &domName) {
+ Domain *domain = getDomain(domName);
+
+ if (!domain)
+ error("ConfigManager::removeKey(%s, %s) called on non-existent domain",
+ key.c_str(), domName.c_str());
+
+ domain->erase(key);
+}
+
+
+#pragma mark -
+
+
+const String & ConfigManager::get(const String &key) const {
+ if (_transientDomain.contains(key))
+ return _transientDomain[key];
+ else if (_activeDomain && _activeDomain->contains(key))
+ return (*_activeDomain)[key];
+ else if (_appDomain.contains(key))
+ return _appDomain[key];
+ else if (_defaultsDomain.contains(key))
+ return _defaultsDomain[key];
+
+#if !(defined(PALMOS_ARM) || defined(PALMOS_DEBUG) || defined(__GP32__))
+ return String::emptyString;
+#else
+ return ConfMan._emptyString;
+#endif
+}
+
+const String & ConfigManager::get(const String &key, const String &domName) const {
+ // FIXME: For now we continue to allow empty domName to indicate
+ // "use 'default' domain". This is mainly needed for the SCUMM ConfigDialog
+ // and should be removed ASAP.
+ if (domName.empty())
+ return get(key);
+
+ const Domain *domain = getDomain(domName);
+
+ if (!domain)
+ error("ConfigManager::get(%s,%s) called on non-existent domain",
+ key.c_str(), domName.c_str());
+
+ if (domain->contains(key))
+ return (*domain)[key];
+
+ return _defaultsDomain.get(key);
+
+ if (!domain->contains(key)) {
+#if 1
+#if !(defined(PALMOS_ARM) || defined(PALMOS_DEBUG) || defined(__GP32__))
+ return String::emptyString;
+#else
+ return ConfMan._emptyString;
+#endif
+#else
+ error("ConfigManager::get(%s,%s) called on non-existent key",
+ key.c_str(), domName.c_str());
+#endif
+ }
+
+ return (*domain)[key];
+}
+
+int ConfigManager::getInt(const String &key, const String &domName) const {
+ String value(get(key, domName));
+ char *errpos;
+
+ // For now, be tolerant against missing config keys. Strictly spoken, it is
+ // a bug in the calling code to retrieve an int for a key which isn't even
+ // present... and a default value of 0 seems rather arbitrary.
+ if (value.empty())
+ return 0;
+
+ // We use the special value '0' for the base passed to strtol. Doing that
+ // makes it possible to enter hex values as "0x1234", but also decimal
+ // values ("123") are still valid.
+ int ivalue = (int)strtol(value.c_str(), &errpos, 0);
+ if (value.c_str() == errpos)
+ error("ConfigManager::getInt(%s,%s): '%s' is not a valid integer",
+ key.c_str(), domName.c_str(), errpos);
+
+ return ivalue;
+}
+
+bool ConfigManager::getBool(const String &key, const String &domName) const {
+ String value(get(key, domName));
+
+ if ((value == "true") || (value == "yes") || (value == "1"))
+ return true;
+ if ((value == "false") || (value == "no") || (value == "0"))
+ return false;
+
+ error("ConfigManager::getBool(%s,%s): '%s' is not a valid bool",
+ key.c_str(), domName.c_str(), value.c_str());
+ return false;
+}
+
+
+#pragma mark -
+
+
+void ConfigManager::set(const String &key, const String &value) {
+ // Remove the transient domain value, if any.
+ _transientDomain.erase(key);
+
+ // Write the new key/value pair into the active domain, resp. into
+ // the application domain if no game domain is active.
+ if (_activeDomain)
+ (*_activeDomain)[key] = value;
+ else
+ _appDomain[key] = value;
+}
+
+void ConfigManager::set(const String &key, const String &value, const String &domName) {
+ // FIXME: For now we continue to allow empty domName to indicate
+ // "use 'default' domain". This is mainly needed for the SCUMM ConfigDialog
+ // and should be removed ASAP.
+ if (domName.empty()) {
+ set(key, value);
+ return;
+ }
+
+ Domain *domain = getDomain(domName);
+
+ if (!domain)
+ error("ConfigManager::set(%s,%s,%s) called on non-existent domain",
+ key.c_str(), value.c_str(), domName.c_str());
+
+ (*domain)[key] = value;
+
+ // TODO/FIXME: We used to erase the given key from the transient domain
+ // here. Do we still want to do that?
+ // It was probably there to simplify the options dialogs code:
+ // Imagine you are editing the current options (via the SCUMM ConfigDialog,
+ // for example). If you edit the game domain for that, but a matching
+ // entry in the transient domain is present, than your changes may not take
+ // effect. So you want to remove the key from the transient domain before
+ // adding it to the active domain.
+ // But doing this here seems rather evil... need to comb the options dialog
+ // code to find out if it's still necessary, and if that's the case, how
+ // to replace it in a clean fashion...
+/*
+ if (domName == kTransientDomain)
+ _transientDomain[key] = value;
+ else {
+ if (domName == kApplicationDomain) {
+ _appDomain[key] = value;
+ if (_activeDomainName.empty() || !_gameDomains[_activeDomainName].contains(key))
+ _transientDomain.erase(key);
+ } else {
+ _gameDomains[domName][key] = value;
+ if (domName == _activeDomainName)
+ _transientDomain.erase(key);
+ }
+ }
+*/
+}
+
+void ConfigManager::setInt(const String &key, int value, const String &domName) {
+ char tmp[128];
+ snprintf(tmp, sizeof(tmp), "%i", value);
+ set(key, String(tmp), domName);
+}
+
+void ConfigManager::setBool(const String &key, bool value, const String &domName) {
+ set(key, String(value ? "true" : "false"), domName);
+}
+
+
+#pragma mark -
+
+
+void ConfigManager::registerDefault(const String &key, const String &value) {
+ _defaultsDomain[key] = value;
+}
+
+void ConfigManager::registerDefault(const String &key, const char *value) {
+ registerDefault(key, String(value));
+}
+
+void ConfigManager::registerDefault(const String &key, int value) {
+ char tmp[128];
+ snprintf(tmp, sizeof(tmp), "%i", value);
+ registerDefault(key, tmp);
+}
+
+void ConfigManager::registerDefault(const String &key, bool value) {
+ registerDefault(key, value ? "true" : "false");
+}
+
+
+#pragma mark -
+
+
+void ConfigManager::setActiveDomain(const String &domName) {
+ if (domName.empty()) {
+ _activeDomain = 0;
+ } else {
+ assert(isValidDomainName(domName));
+ _activeDomain = & _gameDomains[domName];
+ }
+ _activeDomainName = domName;
+}
+
+void ConfigManager::addGameDomain(const String &domName) {
+ assert(!domName.empty());
+ assert(isValidDomainName(domName));
+
+ // TODO: Do we want to generate an error/warning if a domain with
+ // the given name already exists?
+
+ _gameDomains[domName];
+}
+
+void ConfigManager::removeGameDomain(const String &domName) {
+ assert(!domName.empty());
+ assert(isValidDomainName(domName));
+ _gameDomains.erase(domName);
+}
+
+void ConfigManager::renameGameDomain(const String &oldName, const String &newName) {
+ if (oldName == newName)
+ return;
+
+ assert(!oldName.empty());
+ assert(!newName.empty());
+ assert(isValidDomainName(oldName));
+ assert(isValidDomainName(newName));
+
+// _gameDomains[newName].merge(_gameDomains[oldName]);
+ Domain &oldDom = _gameDomains[oldName];
+ Domain &newDom = _gameDomains[newName];
+ Domain::const_iterator iter;
+ for (iter = oldDom.begin(); iter != oldDom.end(); ++iter)
+ newDom[iter->_key] = iter->_value;
+
+ _gameDomains.erase(oldName);
+}
+
+bool ConfigManager::hasGameDomain(const String &domName) const {
+ assert(!domName.empty());
+ return isValidDomainName(domName) && _gameDomains.contains(domName);
+}
+
+
+#pragma mark -
+
+
+const String &ConfigManager::Domain::get(const String &key) const {
+ const_iterator iter(find(key));
+ if (iter != end())
+ return iter->_value;
+
+#if !(defined(PALMOS_ARM) || defined(PALMOS_DEBUG) || defined(__GP32__))
+ return String::emptyString;
+#else
+ return ConfMan._emptyString;
+#endif
+}
+
+void ConfigManager::Domain::setDomainComment(const String &comment) {
+ _domainComment = comment;
+}
+const String &ConfigManager::Domain::getDomainComment() const {
+ return _domainComment;
+}
+
+void ConfigManager::Domain::setKVComment(const String &key, const String &comment) {
+ _keyValueComments[key] = comment;
+}
+const String &ConfigManager::Domain::getKVComment(const String &key) const {
+ return _keyValueComments[key];
+}
+bool ConfigManager::Domain::hasKVComment(const String &key) const {
+ return _keyValueComments.contains(key);
+}
+
+} // End of namespace Common
Property changes on: residual/trunk/common/config-manager.cpp
___________________________________________________________________
Added: svn:mime-type
+ text/plain
Added: svn:keywords
+ Date Revision Author URL Id
Added: svn:eol-style
+ native
Added: residual/trunk/common/config-manager.h
===================================================================
--- residual/trunk/common/config-manager.h (rev 0)
+++ residual/trunk/common/config-manager.h 2008-07-29 22:12:52 UTC (rev 33428)
@@ -0,0 +1,179 @@
+/* Residual - Virtual machine to run LucasArts' 3D adventure games
+ *
+ * Residual is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the AUTHORS
+ * 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef COMMON_CONFIG_MANAGER_H
+#define COMMON_CONFIG_MANAGER_H
+
+#include "common/array.h"
+#include "common/hashmap.h"
+#include "common/singleton.h"
+#include "common/str.h"
+#include "common/hash-str.h"
+
+namespace Common {
+
+class WriteStream;
+
+
+/**
+ * The (singleton) configuration manager, used to query & set configuration
+ * values using string keys.
+ *
+ * @todo Implement the callback based notification system (outlined below)
+ * which sends out notifications to interested parties whenever the value
+ * of some specific (or any) configuration key changes.
+ */
+class ConfigManager : public Singleton<ConfigManager> {
+
+public:
+
+ class Domain : public StringMap {
+ private:
+ StringMap _keyValueComments;
+ String _domainComment;
+
+ public:
+ const String &get(const String &key) const;
+
+ void setDomainComment(const String &comment);
+ const String &getDomainComment() const;
+
+ void setKVComment(const String &key, const String &comment);
+ const String &getKVComment(const String &key) const;
+ bool hasKVComment(const String &key) const;
+ };
+
+ typedef HashMap<String, Domain, IgnoreCase_Hash, IgnoreCase_EqualTo> DomainMap;
+
+#if !(defined(PALMOS_ARM) || defined(PALMOS_DEBUG) || defined(__GP32__))
+ /** The name of the application domain (normally 'scummvm'). */
+ static const String kApplicationDomain;
+
+ /** The transient (pseudo) domain. */
+ static const String kTransientDomain;
+#else
+ static const char *kApplicationDomain;
+ static const char *kTransientDomain;
+
+ const String _emptyString;
+#endif
+
+ void loadDefaultConfigFile();
+ void loadConfigFile(const String &filename);
+
+ /**
+ * Retrieve the config domain with the given name.
+ * @param domName the name of the domain to retrieve
+ * @return pointer to the domain, or 0 if the domain doesn't exist.
+ */
+ Domain * getDomain(const String &domName);
+ const Domain * getDomain(const String &domName) const;
+
+
+ //
+ // Generic access methods: No domain specified, use the values from the
+ // various domains in the order of their priority.
+ //
+
+ bool hasKey(const String &key) const;
+ const String & get(const String &key) const;
+ void set(const String &key, const String &value);
+
+#if 1
+ //
+ // Domain specific access methods: Acces *one specific* domain and modify it.
+ // TODO: I'd like to get rid of most of those if possible, or at least reduce
+ // their usage, by using getDomain as often as possible. For example in the
+ // options dialog code...
+ //
+
+ bool hasKey(const String &key, const String &domName) const;
+ const String & get(const String &key, const String &domName) const;
+ void set(const String &key, const String &value, const String &domName);
+
+ void removeKey(const String &key, const String &domName);
+#endif
+
+ //
+ // Some additional convenience accessors.
+ //
+ int getInt(const String &key, const String &domName = String::emptyString) const;
+ bool getBool(const String &key, const String &domName = String::emptyString) const;
+ void setInt(const String &key, int value, const String &domName = String::emptyString);
+ void setBool(const String &key, bool value, const String &domName = String::emptyString);
+
+
+ void registerDefault(const String &key, const String &value);
+ void registerDefault(const String &key, const char *value);
+ void registerDefault(const String &key, int value);
+ void registerDefault(const String &key, bool value);
+
+ void flushToDisk();
+
+ void setActiveDomain(const String &domName);
+ Domain * getActiveDomain() { return _activeDomain; }
+ const Domain * getActiveDomain() const { return _activeDomain; }
+ const String & getActiveDomainName() const { return _activeDomainName; }
+
+ void addGameDomain(const String &domName);
+ void removeGameDomain(const String &domName);
+ void renameGameDomain(const String &oldName, const String &newName);
+ bool hasGameDomain(const String &domName) const;
+ const DomainMap & getGameDomains() const { return _gameDomains; }
+
+/*
+ TODO: Callback/change notification system
+ typedef void (*ConfigCallback)(const ConstString &key, void *refCon);
+
+ void registerCallback(ConfigCallback cfgc, void *refCon, const ConstString &key = String::emptyString)
+ void unregisterCallback(ConfigCallback cfgc, const ConstString &key = String::emptyString)
+*/
+
+private:
+ friend class Singleton<SingletonBaseType>;
+ ConfigManager();
+
+ void loadFile(const String &filename);
+ void writeDomain(WriteStream &stream, const String &name, const Domain &domain);
+
+ Domain _transientDomain;
+ DomainMap _gameDomains;
+ Domain _appDomain;
+ Domain _defaultsDomain;
+
+ StringList _domainSaveOrder;
+
+ String _activeDomainName;
+ Domain * _activeDomain;
+
+ String _filename;
+};
+
+} // End of namespace Common
+
+/** Shortcut for accessing the configuration manager. */
+#define ConfMan Common::ConfigManager::instance()
+
+#endif
Property changes on: residual/trunk/common/config-manager.h
___________________________________________________________________
Added: svn:mime-type
+ text/plain
Added: svn:keywords
+ Date Revision Author URL Id
Added: svn:eol-style
+ native
Modified: residual/trunk/common/sys.h
===================================================================
--- residual/trunk/common/sys.h 2008-07-29 20:21:54 UTC (rev 33427)
+++ residual/trunk/common/sys.h 2008-07-29 22:12:52 UTC (rev 33428)
@@ -115,6 +115,7 @@
#elif defined(_WIN32_WCE)
#define strcasecmp stricmp
+ #define snprintf _snprintf
#define SYSTEM_LITTLE_ENDIAN
#define SYSTEM_NEED_ALIGNMENT
@@ -129,6 +130,7 @@
#elif defined(_MSC_VER)
#define strcasecmp stricmp
+ #define snprintf _snprintf
#define SYSTEM_LITTLE_ENDIAN
Modified: residual/trunk/dists/msvc7/residual.vcproj
===================================================================
--- residual/trunk/dists/msvc7/residual.vcproj 2008-07-29 20:21:54 UTC (rev 33427)
+++ residual/trunk/dists/msvc7/residual.vcproj 2008-07-29 22:12:52 UTC (rev 33428)
@@ -118,6 +118,18 @@
RelativePath="..\..\common\array.h">
</File>
<File
+ RelativePath="..\..\common\config-file.cpp">
+ </File>
+ <File
+ RelativePath="..\..\common\config-file.h">
+ </File>
+ <File
+ RelativePath="..\..\common\config-manager.cpp">
+ </File>
+ <File
+ RelativePath="..\..\common\config-manager.h">
+ </File>
+ <File
RelativePath="..\..\common\debug.cpp">
</File>
<File
Modified: residual/trunk/dists/msvc71/residual.vcproj
===================================================================
--- residual/trunk/dists/msvc71/residual.vcproj 2008-07-29 20:21:54 UTC (rev 33427)
+++ residual/trunk/dists/msvc71/residual.vcproj 2008-07-29 22:12:52 UTC (rev 33428)
@@ -132,6 +132,18 @@
RelativePath="..\..\common\array.h">
</File>
<File
+ RelativePath="..\..\common\config-file.cpp">
+ </File>
+ <File
+ RelativePath="..\..\common\config-file.h">
+ </File>
+ <File
+ RelativePath="..\..\common\config-manager.cpp">
+ </File>
+ <File
+ RelativePath="..\..\common\config-manager.h">
+ </File>
+ <File
RelativePath="..\..\common\debug.cpp">
</File>
<File
Modified: residual/trunk/dists/msvc8/residual.vcproj
===================================================================
--- residual/trunk/dists/msvc8/residual.vcproj 2008-07-29 20:21:54 UTC (rev 33427)
+++ residual/trunk/dists/msvc8/residual.vcproj 2008-07-29 22:12:52 UTC (rev 33428)
@@ -196,6 +196,22 @@
>
</File>
<File
+ RelativePath="..\..\common\config-file.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\common\config-file.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\common\config-manager.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\common\config-manager.h"
+ >
+ </File>
+ <File
RelativePath="..\..\common\debug.cpp"
>
</File>
Modified: residual/trunk/dists/msvc9/residual.vcproj
===================================================================
--- residual/trunk/dists/msvc9/residual.vcproj 2008-07-29 20:21:54 UTC (rev 33427)
+++ residual/trunk/dists/msvc9/residual.vcproj 2008-07-29 22:12:52 UTC (rev 33428)
@@ -201,6 +201,22 @@
>
</File>
<File
+ RelativePath="..\..\common\config-file.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\common\config-file.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\common\config-manager.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\common\config-manager.h"
+ >
+ </File>
+ <File
RelativePath="..\..\common\debug.cpp"
>
</File>
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
More information about the Scummvm-git-logs
mailing list