aboutsummaryrefslogtreecommitdiff
path: root/common
diff options
context:
space:
mode:
authorChristopher Page2008-08-04 22:34:07 +0000
committerChristopher Page2008-08-04 22:34:07 +0000
commit4198ee962399305a4a158b1f43224c00e2e04a1b (patch)
treeeea375b5cb471509df2e2c5d9123d963a62403c6 /common
parenta51f45407659bba43254b466d20b6af2e8f17ffd (diff)
parent4f5479ee744ac6b419cdf7ec1e96fbf7c83d36ef (diff)
downloadscummvm-rg350-4198ee962399305a4a158b1f43224c00e2e04a1b.tar.gz
scummvm-rg350-4198ee962399305a4a158b1f43224c00e2e04a1b.tar.bz2
scummvm-rg350-4198ee962399305a4a158b1f43224c00e2e04a1b.zip
Merged revisions 33188-33189,33191-33193,33196,33198,33202-33203,33206,33210,33212,33218-33220,33222,33224-33226,33229-33243,33246,33248-33250,33252,33258-33261,33263,33266,33270,33272-33283,33285,33287-33290,33295-33298,33321,33325-33330,33332-33335,33337-33340,33342,33345,33347,33349-33350,33352-33357,33359-33367,33369-33371,33373,33375-33377,33379-33380,33383-33385,33387-33389,33392-33394,33400-33402,33404-33405,33407-33410,33412-33416,33418-33419,33425-33427,33432,33436-33438,33444,33446,33452-33453,33455-33459,33463-33464,33466-33471,33473-33474,33478,33490,33492,33495-33496,33509-33512,33518-33519,33522-33527,33529-33530,33537,33541,33544,33546,33550,33552-33554,33556,33558,33561-33562,33565,33568,33570,33574,33576,33578-33581,33584-33587,33590,33596,33604-33611,33614-33615,33617-33618,33620-33621 via svnmerge from
https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/trunk svn-id: r33624
Diffstat (limited to 'common')
-rw-r--r--common/advancedDetector.cpp274
-rw-r--r--common/advancedDetector.h5
-rw-r--r--common/algorithm.h54
-rw-r--r--common/config-file.cpp6
-rw-r--r--common/config-manager.cpp389
-rw-r--r--common/config-manager.h12
-rw-r--r--common/file.cpp197
-rw-r--r--common/file.h69
-rw-r--r--common/fs.cpp41
-rw-r--r--common/fs.h67
-rw-r--r--common/func.h248
-rw-r--r--common/hashmap.h4
-rw-r--r--common/ptr.h22
-rw-r--r--common/rect.h4
-rw-r--r--common/savefile.h17
-rw-r--r--common/str.cpp152
-rw-r--r--common/str.h3
-rw-r--r--common/stream.cpp79
-rw-r--r--common/stream.h70
-rw-r--r--common/system.cpp71
-rw-r--r--common/system.h19
-rw-r--r--common/unarj.cpp2
-rw-r--r--common/unarj.h2
23 files changed, 1163 insertions, 644 deletions
diff --git a/common/advancedDetector.cpp b/common/advancedDetector.cpp
index 4387bd199e..522b24163e 100644
--- a/common/advancedDetector.cpp
+++ b/common/advancedDetector.cpp
@@ -34,7 +34,11 @@
namespace Common {
-using namespace AdvancedDetector;
+/**
+ * A list of pointers to ADGameDescription structs (or subclasses thereof).
+ */
+typedef Array<const ADGameDescription*> ADGameDescList;
+
/**
* Detect games in specified directory.
@@ -48,7 +52,7 @@ using namespace AdvancedDetector;
* @param platform restrict results to specified platform only
* @return list of ADGameDescription (or subclass) pointers corresponding to matched games
*/
-static ADGameDescList detectGame(const FSList *fslist, const Common::ADParams &params, Language language, Platform platform, const Common::String extra);
+static ADGameDescList detectGame(const FSList &fslist, const Common::ADParams &params, Language language, Platform platform, const Common::String extra);
/**
@@ -90,6 +94,8 @@ static void upgradeTargetIfNecessary(const Common::ADParams &params) {
warning("Target upgraded from %s to %s", o->from, o->to);
+ // WORKAROUND: Fix for bug #1719463: "DETECTOR: Launching
+ // undefined target adds launcher entry"
if (ConfMan.hasKey("id_came_from_command_line")) {
warning("Target came from command line. Skipping save");
} else {
@@ -194,7 +200,7 @@ static void updateGameDescriptor(GameDescriptor &desc, const ADGameDescription *
}
GameList AdvancedMetaEngine::detectGames(const FSList &fslist) const {
- ADGameDescList matches = detectGame(&fslist, params, Common::UNK_LANG, Common::kPlatformUnknown, "");
+ ADGameDescList matches = detectGame(fslist, params, Common::UNK_LANG, Common::kPlatformUnknown, "");
GameList detectedGames;
// Use fallback detector if there were no matches by other means
@@ -233,7 +239,21 @@ PluginError AdvancedMetaEngine::createInstance(OSystem *syst, Engine **engine) c
Common::String gameid = ConfMan.get("gameid");
- ADGameDescList matches = detectGame(0, params, language, platform, extra);
+ Common::String path;
+ if (ConfMan.hasKey("path")) {
+ path = ConfMan.get("path");
+ } else {
+ path = ".";
+ warning("No path was provided. Assuming the data files are in the current directory");
+ }
+ FilesystemNode dir(path);
+ FSList files;
+ if (!dir.isDirectory() || !dir.getChildren(files, FilesystemNode::kListAll)) {
+ warning("Game data path does not exist or is not a directory (%s)", path.c_str());
+ return kNoGameDataFoundError;
+ }
+
+ ADGameDescList matches = detectGame(files, params, language, platform, extra);
if (params.singleid == NULL) {
for (uint i = 0; i < matches.size(); i++) {
@@ -268,10 +288,10 @@ PluginError AdvancedMetaEngine::createInstance(OSystem *syst, Engine **engine) c
return kNoError;
}
-typedef HashMap<String, bool> StringSet;
-typedef HashMap<String, int32> IntMap;
+typedef HashMap<String, bool, IgnoreCase_Hash, IgnoreCase_EqualTo> StringSet;
+typedef HashMap<String, int32, IgnoreCase_Hash, IgnoreCase_EqualTo> IntMap;
-static void reportUnknown(StringMap &filesMD5, IntMap &filesSize) {
+static void reportUnknown(const StringMap &filesMD5, const IntMap &filesSize) {
// TODO: This message should be cleaned up / made more specific.
// For example, we should specify at least which engine triggered this.
//
@@ -287,96 +307,77 @@ static void reportUnknown(StringMap &filesMD5, IntMap &filesSize) {
printf("\n");
}
-static ADGameDescList detectGame(const FSList *fslist, const Common::ADParams &params, Language language, Platform platform, const Common::String extra) {
- StringSet filesList;
+static ADGameDescList detectGameFilebased(const StringMap &allFiles, const Common::ADParams &params);
+static ADGameDescList detectGame(const FSList &fslist, const Common::ADParams &params, Language language, Platform platform, const Common::String extra) {
+ StringMap allFiles;
+
+ StringSet detectFiles;
StringMap filesMD5;
IntMap filesSize;
- IntMap allFiles;
-
- File testFile;
- String tstr;
-
- uint i;
- char md5str[32+1];
-
- bool fileMissing;
const ADGameFileDescription *fileDesc;
const ADGameDescription *g;
const byte *descPtr;
debug(3, "Starting detection");
- // First we compose list of files which we need MD5s for
+ // First we compose an efficient to query set of all files in fslist.
+ // Includes nifty stuff like removing trailing dots and ignoring case.
+ for (FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
+ if (file->isDirectory())
+ continue;
+
+ String tstr = file->getName();
+
+ // Strip any trailing dot
+ if (tstr.lastChar() == '.')
+ tstr.deleteLastChar();
+
+ allFiles[tstr] = file->getPath(); // Record the presence of this file
+ }
+
+ // Compute the set of files for which we need MD5s for. I.e. files which are
+ // included in some ADGameDescription *and* present in fslist.
for (descPtr = params.descs; ((const ADGameDescription *)descPtr)->gameid != 0; descPtr += params.descItemSize) {
g = (const ADGameDescription *)descPtr;
for (fileDesc = g->filesDescriptions; fileDesc->fileName; fileDesc++) {
- tstr = String(fileDesc->fileName);
- tstr.toLowercase();
- filesList[tstr] = true;
+ String tstr = fileDesc->fileName;
+ if (allFiles.contains(tstr))
+ detectFiles[tstr] = true;
}
}
- // TODO/FIXME: Fingolfin says: It's not good that we have two different code paths here,
- // one using a FSList, one using File::open, as that will lead to discrepancies and subtle
- // problems caused by those.
- if (fslist != 0) {
- // Get the information of the existing files
- for (FSList::const_iterator file = fslist->begin(); file != fslist->end(); ++file) {
- if (file->isDirectory()) continue;
- tstr = file->getName();
- tstr.toLowercase();
-
- // Strip any trailing dot
- if (tstr.lastChar() == '.')
- tstr.deleteLastChar();
-
- allFiles[tstr] = true;
-
- debug(3, "+ %s", tstr.c_str());
-
- if (!filesList.contains(tstr)) continue;
+ // Get the information for all detection files, if they exist
+ for (StringSet::const_iterator file = detectFiles.begin(); file != detectFiles.end(); ++file) {
+ String fname = file->_key;
- if (!md5_file_string(*file, md5str, params.md5Bytes))
- continue;
- filesMD5[tstr] = md5str;
+ debug(3, "+ %s", fname.c_str());
- debug(3, "> %s: %s", tstr.c_str(), md5str);
+ char md5str[32+1];
+ if (!md5_file_string(allFiles[fname].c_str(), md5str, params.md5Bytes))
+ continue;
+ filesMD5[fname] = md5str;
- if (testFile.open(file->getPath())) {
- filesSize[tstr] = (int32)testFile.size();
- testFile.close();
- }
- }
- } else {
- // Get the information of the requested files
- for (StringSet::const_iterator file = filesList.begin(); file != filesList.end(); ++file) {
- tstr = file->_key;
+ debug(3, "> %s: %s", fname.c_str(), md5str);
- debug(3, "+ %s", tstr.c_str());
- if (!filesMD5.contains(tstr)) {
- if (testFile.open(tstr) || testFile.open(tstr + ".")) {
- filesSize[tstr] = (int32)testFile.size();
- testFile.close();
-
- if (md5_file_string(file->_key.c_str(), md5str, params.md5Bytes)) {
- filesMD5[tstr] = md5str;
- debug(3, "> %s: %s", tstr.c_str(), md5str);
- }
- }
- }
+ File testFile;
+ if (testFile.open(allFiles[fname])) {
+ filesSize[fname] = (int32)testFile.size();
+ testFile.close();
}
}
+
ADGameDescList matched;
int maxFilesMatched = 0;
// MD5 based matching
+ uint i;
for (i = 0, descPtr = params.descs; ((const ADGameDescription *)descPtr)->gameid != 0; descPtr += params.descItemSize, ++i) {
g = (const ADGameDescription *)descPtr;
- fileMissing = false;
+ bool fileMissing = false;
// Do not even bother to look at entries which do not have matching
// language and platform (if specified).
@@ -385,32 +386,28 @@ static ADGameDescList detectGame(const FSList *fslist, const Common::ADParams &p
continue;
}
- if ((params.flags & kADFlagUseExtraAsHint) && extra != "" && g->extra != extra)
+ if ((params.flags & kADFlagUseExtraAsHint) && !extra.empty() && g->extra != extra)
continue;
// Try to match all files for this game
for (fileDesc = g->filesDescriptions; fileDesc->fileName; fileDesc++) {
- tstr = fileDesc->fileName;
- tstr.toLowercase();
+ String tstr = fileDesc->fileName;
if (!filesMD5.contains(tstr)) {
fileMissing = true;
break;
}
- if (fileDesc->md5 != NULL) {
- if (fileDesc->md5 != filesMD5[tstr]) {
- debug(3, "MD5 Mismatch. Skipping (%s) (%s)", fileDesc->md5, filesMD5[tstr].c_str());
- fileMissing = true;
- break;
- }
+
+ if (fileDesc->md5 != NULL && fileDesc->md5 != filesMD5[tstr]) {
+ debug(3, "MD5 Mismatch. Skipping (%s) (%s)", fileDesc->md5, filesMD5[tstr].c_str());
+ fileMissing = true;
+ break;
}
- if (fileDesc->fileSize != -1) {
- if (fileDesc->fileSize != filesSize[tstr]) {
- debug(3, "Size Mismatch. Skipping");
- fileMissing = true;
- break;
- }
+ if (fileDesc->fileSize != -1 && fileDesc->fileSize != filesSize[tstr]) {
+ debug(3, "Size Mismatch. Skipping");
+ fileMissing = true;
+ break;
}
debug(3, "Matched file: %s", tstr.c_str());
@@ -448,85 +445,68 @@ static ADGameDescList detectGame(const FSList *fslist, const Common::ADParams &p
}
}
- // We've found a match
- if (!matched.empty())
- return matched;
-
- if (!filesMD5.empty())
- reportUnknown(filesMD5, filesSize);
-
- // Filename based fallback
- if (params.fileBasedFallback != 0) {
- const ADFileBasedFallback *ptr = params.fileBasedFallback;
- const char* const* filenames = 0;
-
- // First we create list of files required for detection.
- // The filenames can be different than the MD5 based match ones.
- for (; ptr->desc; ptr++) {
- filenames = ptr->filenames;
- for (; *filenames; filenames++) {
- tstr = String(*filenames);
- tstr.toLowercase();
-
- if (!allFiles.contains(tstr)) {
- if (testFile.open(tstr) || testFile.open(tstr + ".")) {
- allFiles[tstr] = true;
- testFile.close();
- }
- }
- }
- }
-
- // Then we perform the actual filename matching. If there are
- // several matches, only the one with the maximum numbers of
- // files is considered.
- int maxNumMatchedFiles = 0;
- const ADGameDescription *matchedDesc = 0;
-
- ptr = params.fileBasedFallback;
+ // We didn't find a match
+ if (matched.empty()) {
+ if (!filesMD5.empty())
+ reportUnknown(filesMD5, filesSize);
+
+ // Filename based fallback
+ if (params.fileBasedFallback != 0)
+ matched = detectGameFilebased(allFiles, params);
+ }
- for (; ptr->desc; ptr++) {
- const ADGameDescription *agdesc = (const ADGameDescription *)ptr->desc;
- int numMatchedFiles = 0;
- fileMissing = false;
+ return matched;
+}
- filenames = ptr->filenames;
- for (; *filenames; filenames++) {
- if (fileMissing) {
- continue;
- }
+/**
+ * Check for each ADFileBasedFallback record whether all files listed
+ * in it are present. If multiple pass this test, we pick the one with
+ * the maximal number of matching files. In case of a tie, the entry
+ * coming first in the list is chosen.
+ */
+static ADGameDescList detectGameFilebased(const StringMap &allFiles, const Common::ADParams &params) {
+ const ADFileBasedFallback *ptr;
+ const char* const* filenames;
- tstr = String(*filenames);
- tstr.toLowercase();
+ int maxNumMatchedFiles = 0;
+ const ADGameDescription *matchedDesc = 0;
- debug(3, "++ %s", *filenames);
- if (!allFiles.contains(tstr)) {
- fileMissing = true;
- continue;
- }
+ for (ptr = params.fileBasedFallback; ptr->desc; ++ptr) {
+ const ADGameDescription *agdesc = (const ADGameDescription *)ptr->desc;
+ int numMatchedFiles = 0;
+ bool fileMissing = false;
- numMatchedFiles++;
+ for (filenames = ptr->filenames; *filenames; ++filenames) {
+ debug(3, "++ %s", *filenames);
+ if (!allFiles.contains(*filenames)) {
+ fileMissing = true;
+ break;
}
- if (!fileMissing)
- debug(4, "Matched: %s", agdesc->gameid);
+ numMatchedFiles++;
+ }
- if (!fileMissing && numMatchedFiles > maxNumMatchedFiles) {
+ if (!fileMissing) {
+ debug(4, "Matched: %s", agdesc->gameid);
+
+ if (numMatchedFiles > maxNumMatchedFiles) {
matchedDesc = agdesc;
maxNumMatchedFiles = numMatchedFiles;
-
+
debug(4, "and overriden");
}
}
+ }
- if (matchedDesc) { // We got a match
- matched.push_back(matchedDesc);
- if (params.flags & kADFlagPrintWarningOnFileBasedFallback) {
- printf("Your game version has been detected using filename matching as a\n");
- printf("variant of %s.\n", matchedDesc->gameid);
- printf("If this is an original and unmodified version, please report any\n");
- printf("information previously printed by ScummVM to the team.\n");
- }
+ ADGameDescList matched;
+
+ if (matchedDesc) { // We got a match
+ matched.push_back(matchedDesc);
+ if (params.flags & kADFlagPrintWarningOnFileBasedFallback) {
+ printf("Your game version has been detected using filename matching as a\n");
+ printf("variant of %s.\n", matchedDesc->gameid);
+ printf("If this is an original and unmodified version, please report any\n");
+ printf("information previously printed by ScummVM to the team.\n");
}
}
diff --git a/common/advancedDetector.h b/common/advancedDetector.h
index 48b9e213d7..40f5823d1b 100644
--- a/common/advancedDetector.h
+++ b/common/advancedDetector.h
@@ -69,11 +69,6 @@ struct ADGameDescription {
};
/**
- * A list of pointers to ADGameDescription structs (or subclasses thereof).
- */
-typedef Array<const ADGameDescription*> ADGameDescList;
-
-/**
* End marker for a table of ADGameDescription structs. Use this to
* terminate a list to be passed to the AdvancedDetector API.
*/
diff --git a/common/algorithm.h b/common/algorithm.h
index beae34245f..3b6c63d55c 100644
--- a/common/algorithm.h
+++ b/common/algorithm.h
@@ -29,6 +29,11 @@
namespace Common {
+/**
+ * Copies data from the range [first, last) to [dst, dst + (last - first)).
+ * It requires the range [dst, dst + (last - first)) to be valid.
+ * It also requires dst not to be in the range [first, last).
+ */
template<class In, class Out>
Out copy(In first, In last, Out dst) {
while (first != last)
@@ -36,6 +41,13 @@ Out copy(In first, In last, Out dst) {
return dst;
}
+/**
+ * Copies data from the range [first, last) to [dst - (last - first), dst).
+ * It requires the range [dst - (last - first), dst) to be valid.
+ * It also requires dst not to be in the range [first, last).
+ *
+ * Unlike copy copy_backward copies the data from the end to the beginning.
+ */
template<class In, class Out>
Out copy_backward(In first, In last, Out dst) {
while (first != last)
@@ -43,6 +55,15 @@ Out copy_backward(In first, In last, Out dst) {
return dst;
}
+/**
+ * Copies data from the range [first, last) to [dst, dst + (last - first)).
+ * It requires the range [dst, dst + (last - first)) to be valid.
+ * It also requires dst not to be in the range [first, last).
+ *
+ * Unlike copy or copy_backward it does not copy all data. It only copies
+ * a data element when operator() of the op parameter returns true for the
+ * passed data element.
+ */
template<class In, class Out, class Op>
Out copy_if(In first, In last, Out dst, Op op) {
while (first != last) {
@@ -76,6 +97,9 @@ char *set_to(char *first, char *last, Value val) {
return last;
}
+/**
+ * Sets all elements in the range [first, last) to val.
+ */
template<class In, class Value>
In set_to(In first, In last, Value val) {
while (first != last)
@@ -83,6 +107,10 @@ In set_to(In first, In last, Value val) {
return first;
}
+/**
+ * Finds the first data value in the range [first, last) matching v.
+ * For data comperance it uses operator == of the data elements.
+ */
template<class In, class T>
In find(In first, In last, const T &v) {
while (first != last) {
@@ -93,6 +121,10 @@ In find(In first, In last, const T &v) {
return last;
}
+/**
+ * Finds the first data value in the range [first, last) for which
+ * the specified predicate p returns true.
+ */
template<class In, class Pred>
In find_if(In first, In last, Pred p) {
while (first != last) {
@@ -103,15 +135,22 @@ In find_if(In first, In last, Pred p) {
return last;
}
+/**
+ * Applies the function f on all elements of the range [first, last).
+ * The processing order is from beginning to end.
+ */
template<class In, class Op>
Op for_each(In first, In last, Op f) {
while (first != last) f(*first++);
return f;
}
-// Simple sort function, modeled after std::sort.
-// Use it like this: sort(container.begin(), container.end()).
-// Also work on plain old int arrays etc.
+/**
+ * Simple sort function, modeled after std::sort.
+ * Use it like this: sort(container.begin(), container.end()).
+ * Also works on plain old i.e. int arrays etc. For comperance
+ * operator < is used.
+ */
template<class T>
void sort(T first, T last) {
if (first == last)
@@ -131,8 +170,13 @@ void sort(T first, T last) {
}
}
-// Using this with: Common::Less from common/func.h
-// will give the same results as the function above.
+/**
+ * Simple sort function, modeled after std::sort.
+ * It compares data with the given comparator object comp.
+ *
+ * Note: Using this with: Common::Less from common/func.h
+ * will give the same results as the plain sort function.
+ */
template<class T, class StrictWeakOrdering>
void sort(T first, T last, StrictWeakOrdering comp) {
if (first == last)
diff --git a/common/config-file.cpp b/common/config-file.cpp
index fe827e32dc..9f54c9ddde 100644
--- a/common/config-file.cpp
+++ b/common/config-file.cpp
@@ -58,7 +58,7 @@ void ConfigFile::clear() {
bool ConfigFile::loadFromFile(const String &filename) {
File file;
- if (file.open(filename, File::kFileReadMode))
+ if (file.open(filename))
return loadFromStream(file);
else
return false;
@@ -171,8 +171,8 @@ bool ConfigFile::loadFromStream(SeekableReadStream &stream) {
}
bool ConfigFile::saveToFile(const String &filename) {
- File file;
- if (file.open(filename, File::kFileWriteMode))
+ DumpFile file;
+ if (file.open(filename))
return saveToStream(file);
else
return false;
diff --git a/common/config-manager.cpp b/common/config-manager.cpp
index 59855cf6c9..a424c4f6c7 100644
--- a/common/config-manager.cpp
+++ b/common/config-manager.cpp
@@ -23,38 +23,13 @@
*
*/
-#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/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,242 +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");
+ // 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);
+ loadFromStream(*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();
}
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();
+
+ _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);
}
- 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);
+ // 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__
- File cfg_file;
+ WriteStream *stream;
-// TODO
-// if (!willwrite)
-// return;
-
- if (!cfg_file.open(_filename, File::kFileWriteMode)) {
- 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__
}
@@ -328,6 +241,12 @@ void ConfigManager::writeDomain(WriteStream &stream, const String &name, const D
if (domain.empty())
return; // Don't bother writing empty domains.
+ // WORKAROUND: Fix for bug #1972625 "ALL: On-the-fly targets are
+ // written to the config file": Do not save domains that came from
+ // the command line
+ if (domain.contains("id_came_from_command_line"))
+ return;
+
String comment;
// Write domain comment (if any)
@@ -642,6 +561,10 @@ void ConfigManager::addGameDomain(const String &domName) {
// the given name already exists?
_gameDomains[domName];
+
+ // Add it to the _domainSaveOrder, if it's not already in there
+ if (find(_domainSaveOrder.begin(), _domainSaveOrder.end(), domName) == _domainSaveOrder.end())
+ _domainSaveOrder.push_back(domName);
}
void ConfigManager::removeGameDomain(const String &domName) {
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<SingletonBaseType>;
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/file.cpp b/common/file.cpp
index a1ea1aff77..fb837b9499 100644
--- a/common/file.cpp
+++ b/common/file.cpp
@@ -104,7 +104,7 @@
//#define fgets(str, size, file) DS::std_fgets(str, size, file) // not used
//#define getc(handle) DS::std_getc(handle) // not used
//#define getcwd(dir, dunno) DS::std_getcwd(dir, dunno) // not used
- //#define ferror(handle) DS::std_ferror(handle) // not used
+ #define ferror(handle) DS::std_ferror(handle)
#endif
@@ -273,26 +273,14 @@ File::File()
: _handle(0), _ioFailed(false) {
}
-//#define DEBUG_FILE_REFCOUNT
-
File::~File() {
-#ifdef DEBUG_FILE_REFCOUNT
- warning("File::~File on file '%s'", _name.c_str());
-#endif
close();
}
-bool File::open(const String &filename, AccessMode mode) {
- assert(mode == kFileReadMode || mode == kFileWriteMode);
-
- if (filename.empty()) {
- error("File::open: No filename was specified");
- }
-
- if (_handle) {
- error("File::open: This file object already is opened (%s), won't open '%s'", _name.c_str(), filename.c_str());
- }
+bool File::open(const String &filename) {
+ assert(!filename.empty());
+ assert(!_handle);
_name.clear();
clearIOFailed();
@@ -300,32 +288,29 @@ bool File::open(const String &filename, AccessMode mode) {
String fname(filename);
fname.toLowercase();
- const char *modeStr = (mode == kFileReadMode) ? "rb" : "wb";
- if (mode == kFileWriteMode) {
- _handle = fopenNoCase(filename, "", modeStr);
- } else if (_filesMap && _filesMap->contains(fname)) {
+ if (_filesMap && _filesMap->contains(fname)) {
fname = (*_filesMap)[fname];
debug(3, "Opening hashed: %s", fname.c_str());
- _handle = fopen(fname.c_str(), modeStr);
+ _handle = fopen(fname.c_str(), "rb");
} else if (_filesMap && _filesMap->contains(fname + ".")) {
// WORKAROUND: Bug #1458388: "SIMON1: Game Detection fails"
// sometimes instead of "GAMEPC" we get "GAMEPC." (note trailing dot)
fname = (*_filesMap)[fname + "."];
debug(3, "Opening hashed: %s", fname.c_str());
- _handle = fopen(fname.c_str(), modeStr);
+ _handle = fopen(fname.c_str(), "rb");
} else {
if (_defaultDirectories) {
// Try all default directories
StringIntMap::const_iterator x(_defaultDirectories->begin());
for (; _handle == NULL && x != _defaultDirectories->end(); ++x) {
- _handle = fopenNoCase(filename, x->_key, modeStr);
+ _handle = fopenNoCase(filename, x->_key, "rb");
}
}
// Last resort: try the current directory
if (_handle == NULL)
- _handle = fopenNoCase(filename, "", modeStr);
+ _handle = fopenNoCase(filename, "", "rb");
// Last last (really) resort: try looking inside the application bundle on Mac OS X for the lowercase file.
#if defined(MACOSX) || defined(IPHONE)
@@ -335,7 +320,7 @@ bool File::open(const String &filename, AccessMode mode) {
if (fileUrl) {
UInt8 buf[256];
if (CFURLGetFileSystemRepresentation(fileUrl, false, (UInt8 *)buf, 256)) {
- _handle = fopen((char *)buf, modeStr);
+ _handle = fopen((char *)buf, "rb");
}
CFRelease(fileUrl);
}
@@ -345,26 +330,15 @@ bool File::open(const String &filename, AccessMode mode) {
}
- if (_handle == NULL) {
- if (mode == kFileReadMode)
- debug(2, "File %s not found", filename.c_str());
- else
- debug(2, "File %s not opened", filename.c_str());
- return false;
- }
-
+ if (_handle == NULL)
+ debug(2, "File %s not opened", filename.c_str());
+ else
+ _name = filename;
- _name = filename;
-
-#ifdef DEBUG_FILE_REFCOUNT
- warning("File::open on file '%s'", _name.c_str());
-#endif
-
- return true;
+ return _handle != NULL;
}
-bool File::open(const FilesystemNode &node, AccessMode mode) {
- assert(mode == kFileReadMode || mode == kFileWriteMode);
+bool File::open(const FilesystemNode &node) {
if (!node.exists()) {
warning("File::open: Trying to open a FilesystemNode which does not exist");
@@ -389,25 +363,14 @@ bool File::open(const FilesystemNode &node, AccessMode mode) {
clearIOFailed();
_name.clear();
- const char *modeStr = (mode == kFileReadMode) ? "rb" : "wb";
-
- _handle = fopen(node.getPath().c_str(), modeStr);
+ _handle = fopen(node.getPath().c_str(), "rb");
- if (_handle == NULL) {
- if (mode == kFileReadMode)
- debug(2, "File %s not found", filename.c_str());
- else
- debug(2, "File %s not opened", filename.c_str());
- return false;
- }
-
- _name = filename;
-
-#ifdef DEBUG_FILE_REFCOUNT
- warning("File::open on file '%s'", _name.c_str());
-#endif
+ if (_handle == NULL)
+ debug(2, "File %s not found", filename.c_str());
+ else
+ _name = filename;
- return true;
+ return _handle != NULL;
}
bool File::exists(const String &filename) {
@@ -438,7 +401,7 @@ bool File::exists(const String &filename) {
//Try opening the file inside the local directory as a last resort
File tmp;
- return tmp.open(filename, kFileReadMode);
+ return tmp.open(filename);
}
void File::close() {
@@ -462,28 +425,19 @@ void File::clearIOFailed() {
}
bool File::eof() const {
- if (_handle == NULL) {
- error("File::eof: File is not open!");
- return false;
- }
+ assert(_handle);
return feof((FILE *)_handle) != 0;
}
uint32 File::pos() const {
- if (_handle == NULL) {
- error("File::pos: File is not open!");
- return 0;
- }
+ assert(_handle);
return ftell((FILE *)_handle);
}
uint32 File::size() const {
- if (_handle == NULL) {
- error("File::size: File is not open!");
- return 0;
- }
+ assert(_handle);
uint32 oldPos = ftell((FILE *)_handle);
fseek((FILE *)_handle, 0, SEEK_END);
@@ -494,10 +448,7 @@ uint32 File::size() const {
}
void File::seek(int32 offs, int whence) {
- if (_handle == NULL) {
- error("File::seek: File is not open!");
- return;
- }
+ assert(_handle);
if (fseek((FILE *)_handle, offs, whence) != 0)
clearerr((FILE *)_handle);
@@ -507,10 +458,7 @@ uint32 File::read(void *ptr, uint32 len) {
byte *ptr2 = (byte *)ptr;
uint32 real_len;
- if (_handle == NULL) {
- error("File::read: File is not open!");
- return 0;
- }
+ assert(_handle);
if (len == 0)
return 0;
@@ -523,20 +471,91 @@ uint32 File::read(void *ptr, uint32 len) {
return real_len;
}
-uint32 File::write(const void *ptr, uint32 len) {
- if (_handle == NULL) {
- error("File::write: File is not open!");
- return 0;
- }
+
+DumpFile::DumpFile() : _handle(0) {
+}
+
+DumpFile::~DumpFile() {
+ close();
+}
+
+bool DumpFile::open(const String &filename) {
+ assert(!filename.empty());
+ assert(!_handle);
+
+ String fname(filename);
+ fname.toLowercase();
+
+ _handle = fopenNoCase(filename, "", "wb");
+
+ if (_handle == NULL)
+ debug(2, "Failed to open '%s' for writing", filename.c_str());
+
+ return _handle != NULL;
+}
+
+bool DumpFile::open(const FilesystemNode &node) {
+ assert(!_handle);
+
+ if (node.isDirectory()) {
+ warning("File::open: Trying to open a FilesystemNode which is a directory");
+ return false;
+ } /*else if (!node.isReadable() && mode == kFileReadMode) {
+ warning("File::open: Trying to open an unreadable FilesystemNode object for reading");
+ return false;
+ } else if (!node.isWritable() && mode == kFileWriteMode) {
+ warning("File::open: Trying to open an unwritable FilesystemNode object for writing");
+ return false;
+ }*/
+
+ _handle = fopen(node.getPath().c_str(), "wb");
+
+ if (_handle == NULL)
+ debug(2, "File %s not found", node.getName().c_str());
+
+ return _handle != NULL;
+}
+
+void DumpFile::close() {
+ if (_handle)
+ fclose((FILE *)_handle);
+ _handle = NULL;
+}
+
+bool DumpFile::isOpen() const {
+ return _handle != NULL;
+}
+
+bool DumpFile::ioFailed() const {
+ assert(_handle);
+ return ferror((FILE *)_handle) != 0;
+}
+
+void DumpFile::clearIOFailed() {
+ assert(_handle);
+ clearerr((FILE *)_handle);
+}
+
+bool DumpFile::eof() const {
+ assert(_handle);
+ return feof((FILE *)_handle) != 0;
+}
+
+uint32 DumpFile::write(const void *ptr, uint32 len) {
+ assert(_handle);
if (len == 0)
return 0;
- if ((uint32)fwrite(ptr, 1, len, (FILE *)_handle) != len) {
- _ioFailed = true;
- }
+ return (uint32)fwrite(ptr, 1, len, (FILE *)_handle);
+}
- return len;
+void DumpFile::flush() {
+ assert(_handle);
+ // TODO: Should check the return value of fflush, and if it is non-zero,
+ // check errno and set an error flag.
+ fflush((FILE *)_handle);
}
+
} // End of namespace Common
diff --git a/common/file.h b/common/file.h
index 8a69318128..3adeb6ff36 100644
--- a/common/file.h
+++ b/common/file.h
@@ -27,6 +27,7 @@
#define COMMON_FILE_H
#include "common/scummsys.h"
+#include "common/noncopyable.h"
#include "common/str.h"
#include "common/stream.h"
@@ -34,7 +35,10 @@ class FilesystemNode;
namespace Common {
-class File : public SeekableReadStream, public WriteStream {
+/**
+ * TODO: vital to document this core class properly!!! For both users and implementors
+ */
+class File : public SeekableReadStream, public NonCopyable {
protected:
/** File handle to the actual file; 0 if no file is open. */
void *_handle;
@@ -45,19 +49,7 @@ protected:
/** The name of this file, for debugging. */
String _name;
-private:
- // Disallow copying File objects. There is not strict reason for this,
- // except that so far we never had real need for such a feature, and
- // code that accidentally copied File objects tended to break in strange
- // ways.
- File(const File &f);
- File &operator =(const File &f);
-
public:
- enum AccessMode {
- kFileReadMode = 1,
- kFileWriteMode = 2
- };
static void addDefaultDirectory(const String &directory);
static void addDefaultDirectoryRecursive(const String &directory, int level = 4, const String &prefix = "");
@@ -80,8 +72,8 @@ public:
*/
static bool exists(const String &filename);
- virtual bool open(const String &filename, AccessMode mode = kFileReadMode);
- virtual bool open(const FilesystemNode &node, AccessMode mode = kFileReadMode);
+ virtual bool open(const String &filename);
+ virtual bool open(const FilesystemNode &node);
virtual void close();
@@ -114,9 +106,54 @@ public:
virtual uint32 size() const;
void seek(int32 offs, int whence = SEEK_SET);
uint32 read(void *dataPtr, uint32 dataSize);
- uint32 write(const void *dataPtr, uint32 dataSize);
};
+
+/**
+ * TODO: document this class
+ *
+ * Some design ideas:
+ * - automatically drop all files into dumps/ dir? Might not be desired in all cases
+ */
+class DumpFile : public WriteStream, public NonCopyable {
+protected:
+ /** File handle to the actual file; 0 if no file is open. */
+ void *_handle;
+
+public:
+ DumpFile();
+ virtual ~DumpFile();
+
+ virtual bool open(const String &filename);
+ virtual bool open(const FilesystemNode &node);
+
+ virtual void close();
+
+ /**
+ * Checks if the object opened a file successfully.
+ *
+ * @return: true if any file is opened, false otherwise.
+ */
+ bool isOpen() const;
+
+
+ bool ioFailed() const;
+ void clearIOFailed();
+ bool eos() const { return eof(); }
+
+ /**
+ * Checks for end of file.
+ *
+ * @return: true if the end of file is reached, false otherwise.
+ */
+ virtual bool eof() const;
+
+ virtual uint32 write(const void *dataPtr, uint32 dataSize);
+
+ virtual void flush();
+};
+
+
} // End of namespace Common
#endif
diff --git a/common/fs.cpp b/common/fs.cpp
index 7d803dacd4..3f585c6038 100644
--- a/common/fs.cpp
+++ b/common/fs.cpp
@@ -23,10 +23,13 @@
*/
#include "common/util.h"
+#include "common/file.h"
#include "common/system.h"
#include "backends/fs/abstract-fs.h"
#include "backends/fs/fs-factory.h"
+//namespace Common {
+
FilesystemNode::FilesystemNode() {
}
@@ -170,3 +173,41 @@ bool FilesystemNode::lookupFile(FSList &results, const Common::String &p, bool h
return !results.empty();
}
+
+Common::SeekableReadStream *FilesystemNode::openForReading() {
+ if (_realNode == 0)
+ return 0;
+#if 0
+ return _realNode->openForReading();
+#else
+ // FIXME: Until we support openForReading in AbstractFilesystemNode,
+ // we just use Common::File.
+ Common::File *confFile = new Common::File();
+ assert(confFile);
+ if (!confFile->open(*this)) {
+ delete confFile;
+ confFile = 0;
+ }
+ return confFile;
+#endif
+}
+
+Common::WriteStream *FilesystemNode::openForWriting() {
+ if (_realNode == 0)
+ return 0;
+#if 0
+ return _realNode->openForWriting();
+#else
+ // FIXME: Until we support openForWriting in AbstractFilesystemNode,
+ // we just use Common::DumpFile.
+ Common::DumpFile *confFile = new Common::DumpFile();
+ assert(confFile);
+ if (!confFile->open(*this)) {
+ delete confFile;
+ confFile = 0;
+ }
+ return confFile;
+#endif
+}
+
+//} // End of namespace Common
diff --git a/common/fs.h b/common/fs.h
index ed7355cc00..972e0d86af 100644
--- a/common/fs.h
+++ b/common/fs.h
@@ -29,10 +29,18 @@
#include "common/ptr.h"
#include "common/str.h"
+class AbstractFilesystemNode;
+
+namespace Common {
+ class SeekableReadStream;
+ class WriteStream;
+}
+
//namespace Common {
class FilesystemNode;
-class AbstractFilesystemNode;
+//class SeekableReadStream;
+//class WriteStream;
/**
* List of multiple file system nodes. E.g. the contents of a given directory.
@@ -49,22 +57,6 @@ class FSList : public Common::Array<FilesystemNode> {};
* To this end, we abstract away from paths; implementations can be based on
* paths (and it's left to them whether / or \ or : is the path separator :-);
* but it is also possible to use inodes or vrefs (MacOS 9) or anything else.
- *
- * NOTE: Backends still have to provide a way to extract a path from a FSIntern
- *
- * You may ask now: "isn't this cheating? Why do we go through all this when we use
- * a path in the end anyway?!?".
- * Well, for once as long as we don't provide our own file open/read/write API, we
- * still have to use fopen(). Since all our targets already support fopen(), it should
- * be possible to get a fopen() compatible string for any file system node.
- *
- * Secondly, with this abstraction layer, we still avoid a lot of complications based on
- * differences in FS roots, different path separators, or even systems with no real
- * paths (MacOS 9 doesn't even have the notion of a "current directory").
- * And if we ever want to support devices with no FS in the classical sense (Palm...),
- * we can build upon this.
- *
- * This class acts as a wrapper around the AbstractFilesystemNode class defined in backends/fs.
*/
class FilesystemNode {
private:
@@ -108,9 +100,9 @@ public:
bool operator<(const FilesystemNode& node) const;
/**
- * Indicates whether the object referred by this path exists in the filesystem or not.
+ * Indicates whether the object referred by this node exists in the filesystem or not.
*
- * @return bool true if the path exists, false otherwise.
+ * @return bool true if the node exists, false otherwise.
*/
virtual bool exists() const;
@@ -168,7 +160,7 @@ public:
FilesystemNode getParent() const;
/**
- * Indicates whether the path refers to a directory or not.
+ * Indicates whether the node refers to a directory or not.
*
* @todo Currently we assume that a node that is not a directory
* automatically is a file (ignoring things like symlinks or pipes).
@@ -179,28 +171,28 @@ public:
virtual bool isDirectory() const;
/**
- * Indicates whether the object referred by this path can be read from or not.
+ * Indicates whether the object referred by this node can be read from or not.
*
- * If the path refers to a directory, readability implies being able to read
+ * If the node refers to a directory, readability implies being able to read
* and list the directory entries.
*
- * If the path refers to a file, readability implies being able to read the
+ * If the node refers to a file, readability implies being able to read the
* contents of the file.
*
- * @return bool true if the object can be read, false otherwise.
+ * @return true if the object can be read, false otherwise.
*/
virtual bool isReadable() const;
/**
- * Indicates whether the object referred by this path can be written to or not.
+ * Indicates whether the object referred by this node can be written to or not.
*
- * If the path refers to a directory, writability implies being able to modify
+ * If the node refers to a directory, writability implies being able to modify
* the directory entry (i.e. rename the directory, remove it or write files inside of it).
*
- * If the path refers to a file, writability implies being able to write data
+ * If the node refers to a file, writability implies being able to write data
* to the file.
*
- * @return bool true if the object can be written to, false otherwise.
+ * @return true if the object can be written to, false otherwise.
*/
virtual bool isWritable() const;
@@ -221,6 +213,25 @@ public:
* @return true if matches could be found, false otherwise.
*/
virtual bool lookupFile(FSList &results, const Common::String &pattern, bool hidden, bool exhaustive, int depth = -1) const;
+
+
+ /**
+ * Creates a SeekableReadStream instance corresponding to the file
+ * referred by this node. This assumes that the node actually refers
+ * to a readable file. If this is not the case, 0 is returned.
+ *
+ * @return pointer to the stream object, 0 in case of a failure
+ */
+ virtual Common::SeekableReadStream *openForReading();
+
+ /**
+ * Creates a WriteStream instance corresponding to the file
+ * referred by this node. This assumes that the node actually refers
+ * to a readable file. If this is not the case, 0 is returned.
+ *
+ * @return pointer to the stream object, 0 in case of a failure
+ */
+ virtual Common::WriteStream *openForWriting();
};
//} // End of namespace Common
diff --git a/common/func.h b/common/func.h
index 95df96123a..6aa5b76ed4 100644
--- a/common/func.h
+++ b/common/func.h
@@ -29,12 +29,18 @@
namespace Common {
+/**
+ * Generic unary function.
+ */
template<class Arg, class Result>
struct UnaryFunction {
typedef Arg ArgumenType;
typedef Result ResultType;
};
+/**
+ * Generic binary function.
+ */
template<class Arg1, class Arg2, class Result>
struct BinaryFunction {
typedef Arg1 FirstArgumentType;
@@ -42,16 +48,25 @@ struct BinaryFunction {
typedef Result ResultType;
};
+/**
+ * Predicate to check for equallity of two data elements.
+ */
template<class T>
struct EqualTo : public BinaryFunction<T, T, bool> {
bool operator()(const T &x, const T &y) const { return x == y; }
};
+/**
+ * Predicate to check for x being less than y.
+ */
template<class T>
struct Less : public BinaryFunction<T, T, bool> {
bool operator()(const T &x, const T &y) const { return x < y; }
};
+/**
+ * Predicate to check for x being greater than y.
+ */
template<class T>
struct Greater : public BinaryFunction<T, T, bool> {
bool operator()(const T &x, const T &y) const { return x > y; }
@@ -63,15 +78,19 @@ private:
Op _op;
typename Op::FirstArgumentType _arg1;
public:
- Binder1st(const Op &op, const typename Op::FirstArgumentType &arg1) : _op(op), _arg1(arg1) {}
+ Binder1st(const Op &op, typename Op::FirstArgumentType arg1) : _op(op), _arg1(arg1) {}
typename Op::ResultType operator()(typename Op::SecondArgumentType v) const {
return _op(_arg1, v);
}
};
-template<class Op, class T>
-inline Binder1st<Op> bind1st(const Op &op, const T &t) {
+/**
+ * Transforms a binary function object into an unary function object.
+ * To achieve that the first parameter is bound to the passed value t.
+ */
+template<class Op>
+inline Binder1st<Op> bind1st(const Op &op, typename Op::FirstArgumentType t) {
return Binder1st<Op>(op, t);
}
@@ -81,15 +100,19 @@ private:
Op _op;
typename Op::SecondArgumentType _arg2;
public:
- Binder2nd(const Op &op, const typename Op::SecondArgumentType &arg2) : _op(op), _arg2(arg2) {}
+ Binder2nd(const Op &op, typename Op::SecondArgumentType arg2) : _op(op), _arg2(arg2) {}
typename Op::ResultType operator()(typename Op::FirstArgumentType v) const {
return _op(v, _arg2);
}
};
-template<class Op, class T>
-inline Binder2nd<Op> bind2nd(const Op &op, const T &t) {
+/**
+ * Transforms a binary function object into an unary function object.
+ * To achieve that the first parameter is bound to the passed value t.
+ */
+template<class Op>
+inline Binder2nd<Op> bind2nd(const Op &op, typename Op::SecondArgumentType t) {
return Binder2nd<Op>(op, t);
}
@@ -119,18 +142,24 @@ public:
}
};
+/**
+ * Creates an unary function object from a function pointer.
+ */
template<class Arg, class Result>
inline PointerToUnaryFunc<Arg, Result> ptr_fun(Result (*func)(Arg)) {
return PointerToUnaryFunc<Arg, Result>(func);
}
+/**
+ * Creates an binary function object from a function pointer.
+ */
template<class Arg1, class Arg2, class Result>
inline PointerToBinaryFunc<Arg1, Arg2, Result> ptr_fun(Result (*func)(Arg1, Arg2)) {
return PointerToBinaryFunc<Arg1, Arg2, Result>(func);
}
template<class Result, class T>
-class MemFunc0 : public UnaryFunction<T*, Result> {
+class MemFunc0 : public UnaryFunction<T *, Result> {
private:
Result (T::*_func)();
public:
@@ -143,20 +172,20 @@ public:
};
template<class Result, class T>
-class ConstMemFunc0 : public UnaryFunction<T*, Result> {
+class ConstMemFunc0 : public UnaryFunction<T *, Result> {
private:
Result (T::*_func)() const;
public:
typedef Result (T::*FuncType)() const;
ConstMemFunc0(const FuncType &func) : _func(func) {}
- Result operator()(T *v) const {
+ Result operator()(const T *v) const {
return (v->*_func)();
}
};
template<class Result, class Arg, class T>
-class MemFunc1 : public BinaryFunction<T*, Arg, Result> {
+class MemFunc1 : public BinaryFunction<T *, Arg, Result> {
private:
Result (T::*_func)(Arg);
public:
@@ -169,40 +198,166 @@ public:
};
template<class Result, class Arg, class T>
-class ConstMemFunc1 : public BinaryFunction<T*, Arg, Result> {
+class ConstMemFunc1 : public BinaryFunction<T *, Arg, Result> {
private:
Result (T::*_func)(Arg) const;
public:
typedef Result (T::*FuncType)(Arg) const;
ConstMemFunc1(const FuncType &func) : _func(func) {}
- Result operator()(T *v1, Arg v2) const {
+ Result operator()(const T *v1, Arg v2) const {
return (v1->*_func)(v2);
}
};
+/**
+ * Creates a unary function object from a class member function pointer.
+ * The parameter passed to the function object is the 'this' pointer to
+ * be used for the function call.
+ */
template<class Result, class T>
inline MemFunc0<Result, T> mem_fun(Result (T::*f)()) {
return MemFunc0<Result, T>(f);
}
+/**
+ * Creates a unary function object from a class member function pointer.
+ * The parameter passed to the function object is the 'this' pointer to
+ * be used for the function call.
+ */
template<class Result, class T>
inline ConstMemFunc0<Result, T> mem_fun(Result (T::*f)() const) {
return ConstMemFunc0<Result, T>(f);
}
+/**
+ * Creates a binary function object from a class member function pointer.
+ * The first parameter passed to the function object is the 'this' pointer to
+ * be used for the function call.
+ * The second one is the parameter passed to the member function.
+ */
template<class Result, class Arg, class T>
inline MemFunc1<Result, Arg, T> mem_fun(Result (T::*f)(Arg)) {
return MemFunc1<Result, Arg, T>(f);
}
+/**
+ * Creates a binary function object from a class member function pointer.
+ * The first parameter passed to the function object is the 'this' pointer to
+ * be used for the function call.
+ * The second one is the parameter passed to the member function.
+ */
template<class Result, class Arg, class T>
inline ConstMemFunc1<Result, Arg, T> mem_fun(Result (T::*f)(Arg) const) {
return ConstMemFunc1<Result, Arg, T>(f);
}
+template<class Result, class T>
+class MemFuncRef0 : public UnaryFunction<T &, Result> {
+private:
+ Result (T::*_func)();
+public:
+ typedef Result (T::*FuncType)();
+
+ MemFuncRef0(const FuncType &func) : _func(func) {}
+ Result operator()(T &v) const {
+ return (v.*_func)();
+ }
+};
+
+template<class Result, class T>
+class ConstMemFuncRef0 : public UnaryFunction<T &, Result> {
+private:
+ Result (T::*_func)() const;
+public:
+ typedef Result (T::*FuncType)() const;
+
+ ConstMemFuncRef0(const FuncType &func) : _func(func) {}
+ Result operator()(const T &v) const {
+ return (v.*_func)();
+ }
+};
+
+template<class Result, class Arg, class T>
+class MemFuncRef1 : public BinaryFunction<T &, Arg, Result> {
+private:
+ Result (T::*_func)(Arg);
+public:
+ typedef Result (T::*FuncType)(Arg);
+
+ MemFuncRef1(const FuncType &func) : _func(func) {}
+ Result operator()(T &v1, Arg v2) const {
+ return (v1.*_func)(v2);
+ }
+};
+
+template<class Result, class Arg, class T>
+class ConstMemFuncRef1 : public BinaryFunction<T &, Arg, Result> {
+private:
+ Result (T::*_func)(Arg) const;
+public:
+ typedef Result (T::*FuncType)(Arg) const;
+
+ ConstMemFuncRef1(const FuncType &func) : _func(func) {}
+ Result operator()(const T &v1, Arg v2) const {
+ return (v1.*_func)(v2);
+ }
+};
+
+/**
+ * Creates a unary function object from a class member function pointer.
+ * The parameter passed to the function object is the object instance to
+ * be used for the function call. Note unlike mem_fun, it takes a reference
+ * as parameter. Note unlike mem_fun, it takes a reference
+ * as parameter.
+ */
+template<class Result, class T>
+inline MemFuncRef0<Result, T> mem_fun_ref(Result (T::*f)()) {
+ return MemFuncRef0<Result, T>(f);
+}
+
+/**
+ * Creates a unary function object from a class member function pointer.
+ * The parameter passed to the function object is the object instance to
+ * be used for the function call. Note unlike mem_fun, it takes a reference
+ * as parameter.
+ */
+template<class Result, class T>
+inline ConstMemFuncRef0<Result, T> mem_fun_Ref(Result (T::*f)() const) {
+ return ConstMemFuncRef0<Result, T>(f);
+}
+
+/**
+ * Creates a binary function object from a class member function pointer.
+ * The first parameter passed to the function object is the object instance to
+ * be used for the function call. Note unlike mem_fun, it takes a reference
+ * as parameter.
+ * The second one is the parameter passed to the member function.
+ */
+template<class Result, class Arg, class T>
+inline MemFuncRef1<Result, Arg, T> mem_fun_ref(Result (T::*f)(Arg)) {
+ return MemFuncRef1<Result, Arg, T>(f);
+}
+
+/**
+ * Creates a binary function object from a class member function pointer.
+ * The first parameter passed to the function object is the object instance to
+ * be used for the function call. Note unlike mem_fun, it takes a reference
+ * as parameter.
+ * The second one is the parameter passed to the member function.
+ */
+template<class Result, class Arg, class T>
+inline ConstMemFuncRef1<Result, Arg, T> mem_fun_ref(Result (T::*f)(Arg) const) {
+ return ConstMemFuncRef1<Result, Arg, T>(f);
+}
+
// functor code
+/**
+ * Generic functor object for function objects without parameters.
+ *
+ * @see Functor1
+ */
template<class Res>
struct Functor0 {
virtual ~Functor0() {}
@@ -211,6 +366,18 @@ struct Functor0 {
virtual Res operator()() const = 0;
};
+/**
+ * Functor object for a class member function without parameter.
+ *
+ * Example creation:
+ *
+ * Foo bar;
+ * Functor0Men<void, Foo> myFunctor(&bar, &Foo::myFunc);
+ *
+ * Example usage:
+ *
+ * myFunctor();
+ */
template<class Res, class T>
class Functor0Mem : public Functor0<Res> {
public:
@@ -218,7 +385,7 @@ public:
Functor0Mem(T *t, const FuncType &func) : _t(t), _func(func) {}
- bool isValid() const { return _func != 0; }
+ bool isValid() const { return _func != 0 && _t != 0; }
Res operator()() const {
return (_t->*_func)();
}
@@ -227,6 +394,38 @@ private:
const FuncType _func;
};
+/**
+ * Generic functor object for unary function objects.
+ *
+ * A typical usage for an unary function object is for executing opcodes
+ * in a script interpreter. To achieve that one can create an Common::Array
+ * object with 'Functor1<Arg, Res> *' as type. Now after the right engine version
+ * has been determined and the opcode table to use is found one could easily
+ * add the opcode implementations like this:
+ *
+ * Common::Array<Functor1<ScriptState, void> *> opcodeTable;
+ * opcodeTable[0] = new Functor1Mem<ScriptState, void, MyEngine_v1>(&myEngine, &MyEngine_v1::o1_foo);
+ * opcodeTable[1] = new Functor1Mem<ScriptState, void, MyEngine_v2>(&myEngine, &MyEngine_v2::o2_foo);
+ * // unimplemented/unused opcode
+ * opcodeTable[2] = 0;
+ * etc.
+ *
+ * This makes it easy to add member functions of different classes as
+ * opcode functions to the function table. Since with the generic
+ * Functor1<ScriptState, void> object the only requirement for an
+ * function to be used is 'ScriptState' as argument and 'void' as return
+ * value.
+ *
+ * Now for calling the opcodes one has simple to do:
+ * if (opcodeTable[opcodeNum] && opcodeTable[opcodeNum]->isValid())
+ * (*opcodeTable[opcodeNum])(scriptState);
+ * else
+ * warning("Unimplemented opcode %d", opcodeNum);
+ *
+ * If you want to see an real world example check the kyra engine.
+ * Files: engines/kyra/script.cpp and .h and engine/kyra/script_*.cpp
+ * are interesting for that matter.
+ */
template<class Arg, class Res>
struct Functor1 : public Common::UnaryFunction<Arg, Res> {
virtual ~Functor1() {}
@@ -235,6 +434,13 @@ struct Functor1 : public Common::UnaryFunction<Arg, Res> {
virtual Res operator()(Arg) const = 0;
};
+/**
+ * Functor object for an unary class member function.
+ * Usage is like with Functor0Mem. The resulting functor object
+ * will take one parameter though.
+ *
+ * @see Functor0Men
+ */
template<class Arg, class Res, class T>
class Functor1Mem : public Functor1<Arg, Res> {
public:
@@ -242,7 +448,7 @@ public:
Functor1Mem(T *t, const FuncType &func) : _t(t), _func(func) {}
- bool isValid() const { return _func != 0; }
+ bool isValid() const { return _func != 0 && _t != 0; }
Res operator()(Arg v1) const {
return (_t->*_func)(v1);
}
@@ -251,6 +457,11 @@ private:
const FuncType _func;
};
+/**
+ * Generic functor object for binary function objects.
+ *
+ * @see Functor1
+ */
template<class Arg1, class Arg2, class Res>
struct Functor2 : public Common::BinaryFunction<Arg1, Arg2, Res> {
virtual ~Functor2() {}
@@ -259,6 +470,13 @@ struct Functor2 : public Common::BinaryFunction<Arg1, Arg2, Res> {
virtual Res operator()(Arg1, Arg2) const = 0;
};
+/**
+ * Functor object for a binary class member function.
+ * Usage is like with Functor0Mem. The resulting functor object
+ * will take two parameter though.
+ *
+ * @see Functor0Men
+ */
template<class Arg1, class Arg2, class Res, class T>
class Functor2Mem : public Functor2<Arg1, Arg2, Res> {
public:
@@ -266,7 +484,7 @@ public:
Functor2Mem(T *t, const FuncType &func) : _t(t), _func(func) {}
- bool isValid() const { return _func != 0; }
+ bool isValid() const { return _func != 0 && _t != 0; }
Res operator()(Arg1 v1, Arg2 v2) const {
return (_t->*_func)(v1, v2);
}
diff --git a/common/hashmap.h b/common/hashmap.h
index 1bae44e98e..69f367de97 100644
--- a/common/hashmap.h
+++ b/common/hashmap.h
@@ -65,8 +65,12 @@
// on every system we support, so we should get rid of this.
// The solution should be to write a simple placement new
// on our own.
+
+// Symbian does not have <new> but the new operator
+#if !defined(__SYMBIAN32__)
#include <new>
#endif
+#endif
namespace Common {
diff --git a/common/ptr.h b/common/ptr.h
index eea3c39882..c6fcaa4f75 100644
--- a/common/ptr.h
+++ b/common/ptr.h
@@ -121,7 +121,7 @@ public:
~SharedPtr() { decRef(); }
- SharedPtr &operator =(const SharedPtr &r) {
+ SharedPtr &operator=(const SharedPtr &r) {
if (r._refCount)
++(*r._refCount);
decRef();
@@ -134,7 +134,7 @@ public:
}
template<class T2>
- SharedPtr &operator =(const SharedPtr<T2> &r) {
+ SharedPtr &operator=(const SharedPtr<T2> &r) {
if (r._refCount)
++(*r._refCount);
decRef();
@@ -146,8 +146,8 @@ public:
return *this;
}
- ValueType &operator *() const { assert(_pointer); return *_pointer; }
- Pointer operator ->() const { assert(_pointer); return _pointer; }
+ ValueType &operator*() const { assert(_pointer); return *_pointer; }
+ Pointer operator->() const { assert(_pointer); return _pointer; }
/**
* Returns the plain pointer value. Be sure you know what you
@@ -171,6 +171,16 @@ public:
bool unique() const { return refCount() == 1; }
/**
+ * Resets the SharedPtr object to a NULL pointer.
+ */
+ void reset() {
+ decRef();
+ _deletion = 0;
+ _refCount = 0;
+ _pointer = 0;
+ }
+
+ /**
* Returns the number of references to the assigned pointer.
* This should just be used for debugging purposes.
*/
@@ -199,12 +209,12 @@ private:
} // end of namespace Common
template<class T1, class T2>
-bool operator ==(const Common::SharedPtr<T1> &l, const Common::SharedPtr<T2> &r) {
+bool operator==(const Common::SharedPtr<T1> &l, const Common::SharedPtr<T2> &r) {
return l.get() == r.get();
}
template<class T1, class T2>
-bool operator !=(const Common::SharedPtr<T1> &l, const Common::SharedPtr<T2> &r) {
+bool operator!=(const Common::SharedPtr<T1> &l, const Common::SharedPtr<T2> &r) {
return l.get() != r.get();
}
diff --git a/common/rect.h b/common/rect.h
index f71124434a..dcf1c8b421 100644
--- a/common/rect.h
+++ b/common/rect.h
@@ -177,6 +177,10 @@ struct Rect {
clip(Rect(0, 0, maxw, maxh));
}
+ bool isEmpty() const {
+ return (left >= right || top >= bottom);
+ }
+
bool isValidRect() const {
return (left <= right && top <= bottom);
}
diff --git a/common/savefile.h b/common/savefile.h
index f30ddfc160..d44f946d48 100644
--- a/common/savefile.h
+++ b/common/savefile.h
@@ -39,27 +39,14 @@ namespace Common {
* That typically means "save games", but also includes things like the
* IQ points in Indy3.
*/
-class InSaveFile : public SeekableReadStream {};
+typedef SeekableReadStream InSaveFile;
/**
* A class which allows game engines to save game state data.
* That typically means "save games", but also includes things like the
* IQ points in Indy3.
*/
-class OutSaveFile : public WriteStream {
-public:
- /**
- * Close this savefile, to be called right before destruction of this
- * savefile. The idea is that this ways, I/O errors that occur
- * during closing/flushing of the file can still be handled by the
- * game engine.
- *
- * By default, this just flushes the stream.
- */
- virtual void finalize() {
- flush();
- }
-};
+typedef WriteStream OutSaveFile;
/**
diff --git a/common/str.cpp b/common/str.cpp
index a2e6e0c66d..5f8d4ffb7e 100644
--- a/common/str.cpp
+++ b/common/str.cpp
@@ -111,6 +111,74 @@ String::~String() {
decRefCount(_extern._refCount);
}
+void String::makeUnique() {
+ ensureCapacity(_len, true);
+}
+
+/**
+ * Ensure that enough storage is available to store at least new_len
+ * characters plus a null byte. In addition, if we currently share
+ * the storage with another string, unshare it, so that we can safely
+ * write to the storage.
+ */
+void String::ensureCapacity(uint32 new_len, bool keep_old) {
+ bool isShared;
+ uint32 curCapacity, newCapacity;
+ char *newStorage;
+ int *oldRefCount = _extern._refCount;
+
+ if (isStorageIntern()) {
+ isShared = false;
+ curCapacity = _builtinCapacity - 1;
+ } else {
+ isShared = (oldRefCount && *oldRefCount > 1);
+ curCapacity = _extern._capacity;
+ }
+
+ // Special case: If there is enough space, and we do not share
+ // the storage, then there is nothing to do.
+ if (!isShared && new_len <= curCapacity)
+ return;
+
+ if (isShared && new_len <= _builtinCapacity - 1) {
+ // We share the storage, but there is enough internal storage: Use that.
+ newStorage = _storage;
+ newCapacity = _builtinCapacity - 1;
+ } else {
+ // We need to allocate storage on the heap!
+
+ // Compute a suitable new capacity limit
+ newCapacity = computeCapacity(new_len);
+
+ // Allocate new storage
+ newStorage = (char *)malloc(newCapacity+1);
+ assert(newStorage);
+ }
+
+ // Copy old data if needed, elsewise reset the new storage.
+ if (keep_old) {
+ assert(_len <= newCapacity);
+ memcpy(newStorage, _str, _len + 1);
+ } else {
+ _len = 0;
+ newStorage[0] = 0;
+ }
+
+ // Release hold on the old storage ...
+ decRefCount(oldRefCount);
+
+ // ... in favor of the new storage
+ _str = newStorage;
+
+ if (!isStorageIntern()) {
+ // Set the ref count & capacity if we use an external storage.
+ // It is important to do this *after* copying any old content,
+ // else we would override data that has not yet been copied!
+ _extern._refCount = 0;
+ _extern._capacity = newCapacity;
+ }
+}
+
void String::incRefCount() const {
assert(!isStorageIntern());
if (_extern._refCount == 0) {
@@ -170,7 +238,8 @@ String &String::operator =(const String &str) {
}
String& String::operator =(char c) {
- ensureCapacity(1, false);
+ decRefCount(_extern._refCount);
+ _str = _storage;
_len = 1;
_str[0] = c;
_str[1] = 0;
@@ -253,10 +322,7 @@ void String::deleteLastChar() {
void String::deleteChar(uint32 p) {
assert(p < _len);
- // Call ensureCapacity to make sure we actually *own* the storage
- // to which _str points to -- we wouldn't want to modify a storage
- // which other string objects are sharing, after all.
- ensureCapacity(_len, true);
+ makeUnique();
while (p++ < _len)
_str[p-1] = _str[p];
_len--;
@@ -273,7 +339,7 @@ void String::clear() {
void String::setChar(char c, uint32 p) {
assert(p <= _len);
- ensureCapacity(_len, true);
+ makeUnique();
_str[p] = c;
}
@@ -288,78 +354,36 @@ void String::insertChar(char c, uint32 p) {
}
void String::toLowercase() {
- ensureCapacity(_len, true);
+ makeUnique();
for (uint32 i = 0; i < _len; ++i)
_str[i] = tolower(_str[i]);
}
void String::toUppercase() {
- ensureCapacity(_len, true);
+ makeUnique();
for (uint32 i = 0; i < _len; ++i)
_str[i] = toupper(_str[i]);
}
-/**
- * Ensure that enough storage is available to store at least new_len
- * characters plus a null byte. In addition, if we currently share
- * the storage with another string, unshare it, so that we can safely
- * write to the storage.
- */
-void String::ensureCapacity(uint32 new_len, bool keep_old) {
- bool isShared;
- uint32 curCapacity, newCapacity;
- char *newStorage;
- int *oldRefCount = _extern._refCount;
-
- if (isStorageIntern()) {
- isShared = false;
- curCapacity = _builtinCapacity - 1;
- } else {
- isShared = (oldRefCount && *oldRefCount > 1);
- curCapacity = _extern._capacity;
- }
-
- // Special case: If there is enough space, and we do not share
- // the storage, then there is nothing to do.
- if (!isShared && new_len <= curCapacity)
+void String::trim() {
+ if (_len == 0)
return;
- if (isShared && new_len <= _builtinCapacity - 1) {
- // We share the storage, but there is enough internal storage: Use that.
- newStorage = _storage;
- newCapacity = _builtinCapacity - 1;
- } else {
- // We need to allocate storage on the heap!
-
- // Compute a suitable new capacity limit
- newCapacity = computeCapacity(new_len);
+ makeUnique();
- // Allocate new storage
- newStorage = (char *)malloc(newCapacity+1);
- assert(newStorage);
- }
-
- // Copy old data if needed, elsewise reset the new storage.
- if (keep_old) {
- assert(_len <= newCapacity);
- memcpy(newStorage, _str, _len + 1);
- } else {
- _len = 0;
- newStorage[0] = 0;
- }
-
- // Release hold on the old storage ...
- decRefCount(oldRefCount);
+ // Trim trailing whitespace
+ while (_len >= 1 && isspace(_str[_len-1]))
+ _len--;
+ _str[_len] = 0;
- // ... in favor of the new storage
- _str = newStorage;
+ // Trim leading whitespace
+ char *t = _str;
+ while (isspace(*t))
+ t++;
- if (!isStorageIntern()) {
- // Set the ref count & capacity if we use an external storage.
- // It is important to do this *after* copying any old content,
- // else we would override data that has not yet been copied!
- _extern._refCount = 0;
- _extern._capacity = newCapacity;
+ if (t != _str) {
+ _len -= t - _str;
+ memmove(_str, t, _len + 1);
}
}
diff --git a/common/str.h b/common/str.h
index ae9cb992b6..3479fee8e4 100644
--- a/common/str.h
+++ b/common/str.h
@@ -177,6 +177,8 @@ public:
void toLowercase();
void toUppercase();
+ void trim();
+
uint hash() const;
public:
@@ -200,6 +202,7 @@ public:
}
protected:
+ void makeUnique();
void ensureCapacity(uint32 new_len, bool keep_old);
void incRefCount() const;
void decRefCount(int *oldRefCount);
diff --git a/common/stream.cpp b/common/stream.cpp
index 61166fd451..e06cc28415 100644
--- a/common/stream.cpp
+++ b/common/stream.cpp
@@ -242,4 +242,83 @@ void SeekableSubReadStream::seek(int32 offset, int whence) {
_parentStream->seek(_pos);
}
+BufferedReadStream::BufferedReadStream(ReadStream *parentStream, uint32 bufSize, bool disposeParentStream)
+ : _parentStream(parentStream),
+ _disposeParentStream(disposeParentStream),
+ _pos(0),
+ _bufSize(0),
+ _realBufSize(bufSize) {
+
+ assert(parentStream);
+ _buf = new byte[bufSize];
+ assert(_buf);
+}
+
+BufferedReadStream::~BufferedReadStream() {
+ if (_disposeParentStream)
+ delete _parentStream;
+ delete _buf;
+}
+
+uint32 BufferedReadStream::read(void *dataPtr, uint32 dataSize) {
+ uint32 alreadyRead = 0;
+ const uint32 bufBytesLeft = _bufSize - _pos;
+
+ // Check whether the data left in the buffer suffices....
+ if (dataSize > bufBytesLeft) {
+ // Nope, we need to read more data
+
+ // First, flush the buffer, if it is non-empty
+ if (0 < bufBytesLeft) {
+ memcpy(dataPtr, _buf + _pos, bufBytesLeft);
+ _pos = _bufSize;
+ alreadyRead += bufBytesLeft;
+ dataPtr = (byte *)dataPtr + bufBytesLeft;
+ dataSize -= bufBytesLeft;
+ }
+
+ // At this point the buffer is empty. Now if the read request
+ // exceeds the buffer size, just satisfy it directly.
+ if (dataSize > _bufSize)
+ return alreadyRead + _parentStream->read(dataPtr, dataSize);
+
+ // Refill the buffer.
+ // If we didn't read as many bytes as requested, the reason
+ // is EOF or an error. In that case we truncate the buffer
+ // size, as well as the number of bytes we are going to
+ // return to the caller.
+ _bufSize = _parentStream->read(_buf, _realBufSize);
+ _pos = 0;
+ if (dataSize > _bufSize)
+ dataSize = _bufSize;
+ }
+
+ // Satisfy the request from the buffer
+ memcpy(dataPtr, _buf + _pos, dataSize);
+ _pos += dataSize;
+ return alreadyRead + dataSize;
+}
+
+BufferedSeekableReadStream::BufferedSeekableReadStream(SeekableReadStream *parentStream, uint32 bufSize, bool disposeParentStream)
+ : BufferedReadStream(parentStream, bufSize, disposeParentStream),
+ _parentStream(parentStream) {
+}
+
+void BufferedSeekableReadStream::seek(int32 offset, int whence) {
+ // If it is a "local" seek, we may get away with "seeking" around
+ // in the buffer only.
+ // Note: We could try to handle SEEK_END and SEEK_SET, too, but
+ // since they are rarely used, it seems not worth the effort.
+ if (whence == SEEK_CUR && (int)_pos + offset >= 0 && _pos + offset <= _bufSize) {
+ _pos += offset;
+ } else {
+ // Seek was not local enough, so we reset the buffer and
+ // just seeks normally in the parent stream.
+ if (whence == SEEK_CUR)
+ offset -= (_bufSize - _pos);
+ _pos = _bufSize;
+ _parentStream->seek(offset, whence);
+ }
+}
+
} // End of namespace Common
diff --git a/common/stream.h b/common/stream.h
index 4cf5fae114..01a946e685 100644
--- a/common/stream.h
+++ b/common/stream.h
@@ -78,6 +78,22 @@ public:
*/
virtual void flush() {}
+ /**
+ * Finalize and close this stream. To be called right before this
+ * stream instance is deleted. The goal here is to enable calling
+ * code to detect and handle I/O errors which might occur when
+ * closing (and this flushing, if buffered) the stream.
+ *
+ * After this method has been called, no further writes may be
+ * peformed on the stream. Calling ioFailed() is allowed.
+ *
+ * By default, this just flushes the stream.
+ */
+ virtual void finalize() {
+ flush();
+ }
+
+
// The remaining methods all have default implementations; subclasses
// need not (and should not) overload them.
@@ -350,15 +366,17 @@ public:
class SubReadStream : virtual public ReadStream {
protected:
ReadStream *_parentStream;
+ bool _disposeParentStream;
uint32 _pos;
uint32 _end;
- bool _disposeParentStream;
public:
SubReadStream(ReadStream *parentStream, uint32 end, bool disposeParentStream = false)
: _parentStream(parentStream),
+ _disposeParentStream(disposeParentStream),
_pos(0),
- _end(end),
- _disposeParentStream(disposeParentStream) {}
+ _end(end) {
+ assert(parentStream);
+ }
~SubReadStream() {
if (_disposeParentStream) delete _parentStream;
}
@@ -414,6 +432,48 @@ public:
}
};
+/**
+ * Wrapper class which adds buffering to any given ReadStream.
+ * Users can specify how big the buffer should be, and whether the
+ * wrapped stream should be disposed when the wrapper is disposed.
+ */
+class BufferedReadStream : virtual public ReadStream {
+protected:
+ ReadStream *_parentStream;
+ bool _disposeParentStream;
+ byte *_buf;
+ uint32 _pos;
+ uint32 _bufSize;
+ uint32 _realBufSize;
+
+public:
+ BufferedReadStream(ReadStream *parentStream, uint32 bufSize, bool disposeParentStream = false);
+ ~BufferedReadStream();
+
+ virtual bool eos() const { return (_pos == _bufSize) && _parentStream->eos(); }
+ virtual bool ioFailed() const { return _parentStream->ioFailed(); }
+ virtual void clearIOFailed() { _parentStream->clearIOFailed(); }
+
+ virtual uint32 read(void *dataPtr, uint32 dataSize);
+};
+
+/**
+ * Wrapper class which adds buffering to any given SeekableReadStream.
+ * @see BufferedReadStream
+ */
+class BufferedSeekableReadStream : public BufferedReadStream, public SeekableReadStream {
+protected:
+ SeekableReadStream *_parentStream;
+public:
+ BufferedSeekableReadStream(SeekableReadStream *parentStream, uint32 bufSize, bool disposeParentStream = false);
+
+ virtual uint32 pos() const { return _parentStream->pos() - (_bufSize - _pos); }
+ virtual uint32 size() const { return _parentStream->size(); }
+
+ virtual void seek(int32 offset, int whence = SEEK_SET);
+};
+
+
/**
* Simple memory based 'stream', which implements the ReadStream interface for
@@ -514,9 +574,9 @@ public:
uint32 size() const { return _bufSize; }
};
-/**
+/**
* A sort of hybrid between MemoryWriteStream and Array classes. A stream
- * that grows as it's written to.
+ * that grows as it's written to.
*/
class MemoryWriteStreamDynamic : public Common::WriteStream {
private:
diff --git a/common/system.cpp b/common/system.cpp
index 8d528258f4..e3f81a69b6 100644
--- a/common/system.cpp
+++ b/common/system.cpp
@@ -29,6 +29,10 @@
#include "common/config-manager.h"
#include "common/system.h"
#include "common/timer.h"
+#if defined(WIN32) && defined(ARRAYSIZE)
+// winnt.h defines ARRAYSIZE, but we want our own one... - this is needed before including util.h
+#undef ARRAYSIZE
+#endif
#include "common/util.h"
#include "graphics/colormasks.h"
@@ -121,3 +125,70 @@ 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(UNIX)
+#define DEFAULT_CONFIG_FILE ".scummvmrc"
+#else
+#define DEFAULT_CONFIG_FILE "scummvm.ini"
+#endif
+
+static Common::String getDefaultConfigFileName() {
+ char configFile[MAXPATHLEN];
+#if 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);
+#else
+ strcpy(configFile, DEFAULT_CONFIG_FILE);
+#endif
+
+ return configFile;
+}
+
+Common::SeekableReadStream *OSystem::openConfigFileForReading() {
+ Common::File *confFile = new Common::File();
+ assert(confFile);
+ if (!confFile->open(getDefaultConfigFileName())) {
+ delete confFile;
+ confFile = 0;
+ }
+ return confFile;
+}
+
+Common::WriteStream *OSystem::openConfigFileForWriting() {
+#ifdef __DC__
+ return 0;
+#else
+ Common::DumpFile *confFile = new Common::DumpFile();
+ assert(confFile);
+ if (!confFile->open(getDefaultConfigFileName())) {
+ delete confFile;
+ confFile = 0;
+ }
+ 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
diff --git a/common/unarj.cpp b/common/unarj.cpp
index f3ac20c285..da88c11fc9 100644
--- a/common/unarj.cpp
+++ b/common/unarj.cpp
@@ -231,7 +231,7 @@ ArjHeader *ArjFile::readHeader() {
}
-bool ArjFile::open(const Common::String &filename, AccessMode mode) {
+bool ArjFile::open(const Common::String &filename) {
if (_isOpen)
error("Attempt to open another instance of archive");
diff --git a/common/unarj.h b/common/unarj.h
index b015999671..c8965968f6 100644
--- a/common/unarj.h
+++ b/common/unarj.h
@@ -110,7 +110,7 @@ public:
void registerArchive(const String &filename);
- bool open(const Common::String &filename, AccessMode mode = kFileReadMode);
+ bool open(const Common::String &filename);
void close();
uint32 read(void *dataPtr, uint32 dataSize);