From 6377b8bdd570d3c23dbdc9239b844492f7e09b9d Mon Sep 17 00:00:00 2001 From: Max Horn Date: Sun, 3 Aug 2008 16:54:18 +0000 Subject: New OSystem API for loading/storing default config file svn-id: r33584 --- common/config-manager.cpp | 375 ++++++++++++++++++---------------------------- common/config-manager.h | 12 +- common/system.cpp | 132 ++++++++++++++++ common/system.h | 19 ++- 4 files changed, 298 insertions(+), 240 deletions(-) diff --git a/common/config-manager.cpp b/common/config-manager.cpp index 40fef1a622..a424c4f6c7 100644 --- a/common/config-manager.cpp +++ b/common/config-manager.cpp @@ -23,38 +23,13 @@ * */ -#if defined(WIN32) -#include -// 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/system.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/ScummVM Preferences" -#else -#define DEFAULT_CONFIG_FILE ".scummvmrc" -#endif -#else -#define DEFAULT_CONFIG_FILE "scummvm.ini" -#endif - -#define MAXLINELEN 256 - static bool isValidDomainName(const Common::String &domName) { const char *p = domName.c_str(); while (*p && (isalnum(*p) || *p == '-' || *p == '_')) @@ -85,238 +60,180 @@ ConfigManager::ConfigManager() 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, "\\ScummVM"); - CreateDirectory(configFile, NULL); - strcat(configFile, "\\" DEFAULT_CONFIG_FILE); - - if (fopen(configFile, "r") == NULL) { - // Check windows directory - char oldConfigFile[MAXPATHLEN]; - GetWindowsDirectory(oldConfigFile, MAXPATHLEN); - strcat(oldConfigFile, "\\" DEFAULT_CONFIG_FILE); - if (fopen(oldConfigFile, "r")) { - printf("The default location of the config file (scummvm.ini) in ScummVM has changed,\n"); - printf("under Windows NT4/2000/XP/Vista. You may want to consider moving your config\n"); - printf("file from the old default location:\n"); - printf("%s\n", oldConfigFile); - printf("to the new default location:\n"); - printf("%s\n\n", configFile); - strcpy(configFile, oldConfigFile); - } - } - } else { - // Check windows directory - GetWindowsDirectory(configFile, MAXPATHLEN); - strcat(configFile, "\\" DEFAULT_CONFIG_FILE); - } + // Open the default config file + SeekableReadStream *stream = g_system->openConfigFileForReading(); + _filename.clear(); // clear the filename to indicate that we are using the 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 + // ... load it ... + assert(stream); + loadFromStream(*stream); + + // ... and close it again. + delete stream; - 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; + printf("Using configuration file: %s\n", _filename.c_str()); + loadFromStream(cfg_file); + } +} + +void ConfigManager::loadFromStream(SeekableReadStream &stream) { + String domain; + String comment; + int lineno = 0; + + _appDomain.clear(); + _gameDomains.clear(); + _transientDomain.clear(); + _domainSaveOrder.clear(); + + // TODO: Detect if a domain occurs multiple times (or likewise, if + // a key occurs multiple times inside one domain). + + while (!stream.eos() && !stream.ioFailed()) { + lineno++; + + // Read a line + String line; + while (line.lastChar() != '\n') { + char buf[256]; + if (!stream.readLine_NEW(buf, 256)) + 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++; + + if (*p == '\0') + error("Config file buggy: missing ] in line %d", lineno); + else if (*p != ']') + error("Config file buggy: Invalid character '%c' occured in section name in line %d", *p, lineno); + + domain = String(line.c_str() + 1, p); + + // Store domain comment + if (domain == kApplicationDomain) { + _appDomain.setDomainComment(comment); + } else { + _gameDomains[domain].setDomainComment(comment); } + comment.clear(); - 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); + _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); + + // Extract the key/value pair + String key(t, p); + String value(p + 1); + + // Trim of spaces + key.trim(); + value.trim(); + + // Finally, store the key/value pair in the active domain + set(key, value, domain); + + // Store comment + if (domain == kApplicationDomain) { + _appDomain.setKVComment(key, comment); } 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(); + _gameDomains[domain].setKVComment(key, comment); } + comment.clear(); } } } void ConfigManager::flushToDisk() { #ifndef __DC__ - DumpFile cfg_file; + WriteStream *stream; - if (!cfg_file.open(_filename)) { - warning("Unable to write configuration file: %s", _filename.c_str()); + if (_filename.empty()) { + // Write to the default config file + stream = g_system->openConfigFileForWriting(); + if (!stream) // If writing to the config file is not possible, do nothing + return; } 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]); - } + DumpFile *dump = new DumpFile(); + assert(dump); + + if (!dump->open(_filename)) { + warning("Unable to write configuration file: %s", _filename.c_str()); + delete dump; + return; } + + stream = dump; + } - DomainMap::const_iterator d; + // 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(*stream, *i, _appDomain); + } else if (_gameDomains.contains(*i)) { + writeDomain(*stream, *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); - } + + // Now write the domains which haven't been written yet + if (find(_domainSaveOrder.begin(), _domainSaveOrder.end(), kApplicationDomain) == _domainSaveOrder.end()) + writeDomain(*stream, kApplicationDomain, _appDomain); + for (d = _gameDomains.begin(); d != _gameDomains.end(); ++d) { + if (find(_domainSaveOrder.begin(), _domainSaveOrder.end(), d->_key) == _domainSaveOrder.end()) + writeDomain(*stream, d->_key, d->_value); } + + delete stream; + #endif // !__DC__ } diff --git a/common/config-manager.h b/common/config-manager.h index bebb59b539..9e5b88a073 100644 --- a/common/config-manager.h +++ b/common/config-manager.h @@ -36,7 +36,7 @@ namespace Common { class WriteStream; - +class SeekableReadStream; /** * The (singleton) configuration manager, used to query & set configuration @@ -144,19 +144,11 @@ public: 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; ConfigManager(); - void loadFile(const String &filename); + void loadFromStream(SeekableReadStream &stream); void writeDomain(WriteStream &stream, const String &name, const Domain &domain); Domain _transientDomain; diff --git a/common/system.cpp b/common/system.cpp index 8d528258f4..1362177011 100644 --- a/common/system.cpp +++ b/common/system.cpp @@ -121,3 +121,135 @@ void OSystem::clearScreen() { memset(screen->pixels, 0, screen->h * screen->pitch); unlockScreen(); } + + +/* +FIXME: The config file loading code below needs to be cleaned up. + Port specific variants should be pushed into the respective ports. + + Ideally, the default OSystem::openConfigFileForReading/Writing methods + should be removed completely. +*/ + +#include "common/file.h" + +#ifdef __PLAYSTATION2__ +#include "backends/platform/ps2/systemps2.h" +#endif + +#ifdef IPHONE +#include "backends/platform/iphone/osys_iphone.h" +#endif + +#if defined(WIN32) +#include +// winnt.h defines ARRAYSIZE, but we want our own one... +#undef ARRAYSIZE +#endif + + +#if defined(UNIX) +#ifdef MACOSX +#define DEFAULT_CONFIG_FILE "Library/Preferences/ScummVM Preferences" +#else +#define DEFAULT_CONFIG_FILE ".scummvmrc" +#endif +#else +#define DEFAULT_CONFIG_FILE "scummvm.ini" +#endif + +static Common::String getDefaultConfigFileName() { + char configFile[MAXPATHLEN]; +#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, "\\ScummVM"); + CreateDirectory(configFile, NULL); + strcat(configFile, "\\" DEFAULT_CONFIG_FILE); + + if (fopen(configFile, "r") == NULL) { + // Check windows directory + char oldConfigFile[MAXPATHLEN]; + GetWindowsDirectory(oldConfigFile, MAXPATHLEN); + strcat(oldConfigFile, "\\" DEFAULT_CONFIG_FILE); + if (fopen(oldConfigFile, "r")) { + printf("The default location of the config file (scummvm.ini) in ScummVM has changed,\n"); + printf("under Windows NT4/2000/XP/Vista. You may want to consider moving your config\n"); + printf("file from the old default location:\n"); + printf("%s\n", oldConfigFile); + printf("to the new default location:\n"); + printf("%s\n\n", configFile); + strcpy(configFile, oldConfigFile); + } + } + } 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); +#elif defined(UNIX) && !defined(GP2X) && !defined(IPHONE) + // On UNIX type systems, by default we store the config file inside + // to the HOME directory of the user. + // + // 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. + 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 + strcpy(configFile, DEFAULT_CONFIG_FILE); +#endif + + return configFile; +} + +Common::SeekableReadStream *OSystem::openConfigFileForReading() { + Common::File *confFile = new Common::File(); + assert(confFile); + confFile->open(getDefaultConfigFileName()); + return confFile; +} + +Common::WriteStream *OSystem::openConfigFileForWriting() { +#ifdef __DC__ + return 0; +#else + Common::DumpFile *confFile = new Common::DumpFile(); + assert(confFile); + confFile->open(getDefaultConfigFileName()); + return confFile; +#endif +} diff --git a/common/system.h b/common/system.h index b895a5cfba..501d0802fd 100644 --- a/common/system.h +++ b/common/system.h @@ -44,6 +44,8 @@ namespace Common { class EventManager; class SaveFileManager; class TimerManager; + class SeekableReadStream; + class WriteStream; } class FilesystemFactory; @@ -900,10 +902,25 @@ public: /** * Returns the FilesystemFactory object, depending on the current architecture. * - * @return FilesystemFactory* The specific factory for the current architecture. + * @return the FSNode factory for the current architecture */ virtual FilesystemFactory *getFilesystemFactory() = 0; + /** + * Open the default config file for reading, by returning a suitable + * ReadStream instance. It is the callers responsiblity to delete + * the stream after use. + */ + virtual Common::SeekableReadStream *openConfigFileForReading(); + + /** + * Open the default config file for writing, by returning a suitable + * WriteStream instance. It is the callers responsiblity to delete + * the stream after use. + * + * May return 0 to indicate that writing to config file is not possible. + */ + virtual Common::WriteStream *openConfigFileForWriting(); /** * Return String which is used for backend-specific addition to theme -- cgit v1.2.3