diff options
-rw-r--r-- | common/config-manager.cpp | 368 | ||||
-rw-r--r-- | common/config-manager.h | 4 | ||||
-rw-r--r-- | common/system.cpp | 128 | ||||
-rw-r--r-- | common/system.h | 19 |
4 files changed, 347 insertions, 172 deletions
diff --git a/common/config-manager.cpp b/common/config-manager.cpp index c5c7098d25..044474a927 100644 --- a/common/config-manager.cpp +++ b/common/config-manager.cpp @@ -32,10 +32,27 @@ #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) { @@ -68,22 +85,77 @@ ConfigManager::ConfigManager() void ConfigManager::loadDefaultConfigFile() { - _appDomain.clear(); - _gameDomains.clear(); - _transientDomain.clear(); - _domainSaveOrder.clear(); + 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"); - // 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 + strcat(configFile, "\\Application Data"); + CreateDirectory(configFile, NULL); + } - // ... load it ... - assert(stream); - loadStream(*stream); - - // ... and close it again. - delete stream; + 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); + #else + strcpy(configFile, DEFAULT_CONFIG_FILE); + #endif +#endif + + loadConfigFile(configFile); flushToDisk(); } @@ -91,168 +163,160 @@ void ConfigManager::loadConfigFile(const String &filename) { _appDomain.clear(); _gameDomains.clear(); _transientDomain.clear(); - _domainSaveOrder.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 { - printf("Using configuration file: %s\n", _filename.c_str()); - loadStream(cfg_file); - } -} - -void ConfigManager::loadStream(SeekableReadStream &stream) { - - 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 (!stream.eos() && !stream.ioFailed()) { - lineno++; - - // Read a line - String line; - while (line.lastChar() != '\n') { - char buf[MAXLINELEN]; - if (!stream.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); + 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; } - // Store domain comment - if (domain == kApplicationDomain) { - _appDomain.setDomainComment(comment); + 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 { - _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); + // 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(); } - - // 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 { - _gameDomains[domain].setKVComment(key, comment); - } - comment.clear(); } } } void ConfigManager::flushToDisk() { #ifndef __DC__ - WriteStream *stream; + DumpFile cfg_file; - 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; + if (!cfg_file.open(_filename)) { + warning("Unable to write configuration file: %s", _filename.c_str()); } else { - 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; - } - - // 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]); + // 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; + DomainMap::const_iterator d; - // 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); + // 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); + } } - - delete stream; - #endif // !__DC__ } diff --git a/common/config-manager.h b/common/config-manager.h index 0b1aa73fdb..bebb59b539 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 @@ -156,7 +156,7 @@ private: friend class Singleton<SingletonBaseType>; ConfigManager(); - void loadStream(SeekableReadStream &stream); + void loadFile(const String &filename); void writeDomain(WriteStream &stream, const String &name, const Domain &domain); Domain _transientDomain; diff --git a/common/system.cpp b/common/system.cpp index 2dd0192aec..200b1a3fc7 100644 --- a/common/system.cpp +++ b/common/system.cpp @@ -250,3 +250,131 @@ Common::WriteStream *OSystem::openConfigFileForWriting() { return confFile; #endif } + + +/* +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(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(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 + #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); + #else + strcpy(configFile, DEFAULT_CONFIG_FILE); + #endif +#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 501d0802fd..b895a5cfba 100644 --- a/common/system.h +++ b/common/system.h @@ -44,8 +44,6 @@ namespace Common { class EventManager; class SaveFileManager; class TimerManager; - class SeekableReadStream; - class WriteStream; } class FilesystemFactory; @@ -902,25 +900,10 @@ public: /** * Returns the FilesystemFactory object, depending on the current architecture. * - * @return the FSNode factory for the current architecture + * @return FilesystemFactory* The specific 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 |