[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 &section) {
+	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 &section) 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 &section) 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 &section) {
+	assert(isValidName(key));
+	assert(isValidName(section));
+
+	Section *s = getSection(section);
+	if (s)
+		 s->removeKey(key);
+}
+
+bool ConfigFile::getKey(const String &key, const String &section, 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 &section, 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 &section) const {
+	const Section *s = getSection(section);
+
+	return s->getKeys();
+}
+
+ConfigFile::Section *ConfigFile::getSection(const String &section) {
+	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 &section) 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 &section) const;
+	void	removeSection(const String &section);
+	void	renameSection(const String &oldName, const String &newName);
+
+	bool	hasKey(const String &key, const String &section) const;
+	bool	getKey(const String &key, const String &section, String &value) const;
+	void	setKey(const String &key, const String &section, const String &value);
+	void	removeKey(const String &key, const String &section);
+
+	const SectionList getSections() const { return _sections; }
+	const SectionKeyList getKeys(const String &section) const;
+
+	void listSections(StringSet &set);
+	void listKeyValues(StringMap &kv);
+
+private:
+	SectionList _sections;
+
+	Section *getSection(const String &section);
+	const Section *getSection(const String &section) 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