diff options
Diffstat (limited to 'common')
-rw-r--r-- | common/advancedDetector.cpp | 82 | ||||
-rw-r--r-- | common/algorithm.h | 54 | ||||
-rw-r--r-- | common/config-file.cpp | 6 | ||||
-rw-r--r-- | common/config-manager.cpp | 12 | ||||
-rw-r--r-- | common/file.cpp | 170 | ||||
-rw-r--r-- | common/file.h | 65 | ||||
-rw-r--r-- | common/func.h | 131 | ||||
-rw-r--r-- | common/hashmap.h | 6 | ||||
-rw-r--r-- | common/ptr.h | 22 | ||||
-rw-r--r-- | common/rect.h | 4 | ||||
-rw-r--r-- | common/str.cpp | 152 | ||||
-rw-r--r-- | common/str.h | 3 | ||||
-rw-r--r-- | common/stream.cpp | 79 | ||||
-rw-r--r-- | common/stream.h | 48 | ||||
-rw-r--r-- | common/unarj.cpp | 2 | ||||
-rw-r--r-- | common/unarj.h | 2 |
16 files changed, 594 insertions, 244 deletions
diff --git a/common/advancedDetector.cpp b/common/advancedDetector.cpp index 4387bd199e..e328cf7787 100644 --- a/common/advancedDetector.cpp +++ b/common/advancedDetector.cpp @@ -48,7 +48,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 ¶ms, Language language, Platform platform, const Common::String extra); +static ADGameDescList detectGame(const FSList &fslist, const Common::ADParams ¶ms, Language language, Platform platform, const Common::String extra); /** @@ -194,7 +194,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 +233,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++) { @@ -287,7 +301,7 @@ static void reportUnknown(StringMap &filesMD5, IntMap &filesSize) { printf("\n"); } -static ADGameDescList detectGame(const FSList *fslist, const Common::ADParams ¶ms, Language language, Platform platform, const Common::String extra) { +static ADGameDescList detectGame(const FSList &fslist, const Common::ADParams ¶ms, Language language, Platform platform, const Common::String extra) { StringSet filesList; StringMap filesMD5; @@ -319,57 +333,35 @@ static ADGameDescList detectGame(const FSList *fslist, const Common::ADParams &p } } - // 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(); + // 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(); - allFiles[tstr] = true; + // Strip any trailing dot + if (tstr.lastChar() == '.') + tstr.deleteLastChar(); - debug(3, "+ %s", tstr.c_str()); + allFiles[tstr] = true; - if (!filesList.contains(tstr)) continue; + debug(3, "+ %s", tstr.c_str()); - if (!md5_file_string(*file, md5str, params.md5Bytes)) - continue; - filesMD5[tstr] = md5str; + if (!filesList.contains(tstr)) continue; - debug(3, "> %s: %s", tstr.c_str(), 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; + if (!md5_file_string(*file, md5str, params.md5Bytes)) + continue; + filesMD5[tstr] = 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(); + debug(3, "> %s: %s", tstr.c_str(), md5str); - if (md5_file_string(file->_key.c_str(), md5str, params.md5Bytes)) { - filesMD5[tstr] = md5str; - debug(3, "> %s: %s", tstr.c_str(), md5str); - } - } - } + if (testFile.open(file->getPath())) { + filesSize[tstr] = (int32)testFile.size(); + testFile.close(); } } + ADGameDescList matched; int maxFilesMatched = 0; 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..044474a927 100644 --- a/common/config-manager.cpp +++ b/common/config-manager.cpp @@ -289,13 +289,9 @@ void ConfigManager::loadFile(const String &filename) { void ConfigManager::flushToDisk() { #ifndef __DC__ - File cfg_file; - -// TODO -// if (!willwrite) -// return; + DumpFile cfg_file; - if (!cfg_file.open(_filename, File::kFileWriteMode)) { + if (!cfg_file.open(_filename)) { warning("Unable to write configuration file: %s", _filename.c_str()); } else { // First write the domains in _domainSaveOrder, in that order. @@ -642,6 +638,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/file.cpp b/common/file.cpp index a1ea1aff77..5b465b5e01 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; - } - - - _name = filename; - -#ifdef DEBUG_FILE_REFCOUNT - warning("File::open on file '%s'", _name.c_str()); -#endif + if (_handle == NULL) + debug(2, "File %s not opened", filename.c_str()); + else + _name = filename; - 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); - - 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; - } + _handle = fopen(node.getPath().c_str(), "rb"); - _name = filename; + if (_handle == NULL) + debug(2, "File %s not found", filename.c_str()); + else + _name = filename; -#ifdef DEBUG_FILE_REFCOUNT - warning("File::open on file '%s'", _name.c_str()); -#endif - - 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,62 @@ 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; +} + +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 len; + return (uint32)fwrite(ptr, 1, len, (FILE *)_handle); } + } // End of namespace Common diff --git a/common/file.h b/common/file.h index 8a69318128..3c2520b07c 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,52 @@ public: virtual uint32 size() const; void seek(int32 offs, int whence = SEEK_SET); uint32 read(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; + uint32 write(const void *dataPtr, uint32 dataSize); }; + } // End of namespace Common #endif diff --git a/common/func.h b/common/func.h index 95df96123a..1c045b9e5d 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; } @@ -70,6 +85,10 @@ public: } }; +/** + * 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, class T> inline Binder1st<Op> bind1st(const Op &op, const T &t) { return Binder1st<Op>(op, t); @@ -88,6 +107,10 @@ public: } }; +/** + * Transforms a binary function object into an unary function object. + * To achieve that the second parameter is bound to the passed value t. + */ template<class Op, class T> inline Binder2nd<Op> bind2nd(const Op &op, const T &t) { return Binder2nd<Op>(op, t); @@ -119,11 +142,17 @@ 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); @@ -143,7 +172,7 @@ public: }; template<class Result, class T> -class ConstMemFunc0 : public UnaryFunction<T*, Result> { +class ConstMemFunc0 : public UnaryFunction<T *, Result> { private: Result (T::*_func)() const; public: @@ -156,7 +185,7 @@ public: }; 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,7 +198,7 @@ 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: @@ -181,21 +210,43 @@ public: } }; +/** + * 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); @@ -203,6 +254,11 @@ inline ConstMemFunc1<Result, Arg, T> mem_fun(Result (T::*f)(Arg) const) { // functor code +/** + * Generic functor object for function objects without parameters. + * + * @see Functor1 + */ template<class Res> struct Functor0 { virtual ~Functor0() {} @@ -211,6 +267,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 +286,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 +295,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 +335,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 +349,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 +358,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 +371,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 +385,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..ab6e737d74 100644 --- a/common/hashmap.h +++ b/common/hashmap.h @@ -58,7 +58,13 @@ #include "common/str.h" #include "common/util.h" +// FIXME: Since this define is very system dependant, +// it should be moved to the appropriate H file instead. +// Portdefs might be a good location for example +#if !defined(__SYMBIAN32__) #define USE_HASHMAP_MEMORY_POOL +#endif + #ifdef USE_HASHMAP_MEMORY_POOL #include "common/memorypool.h" // FIXME: we sadly can't assume standard C++ to be present 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 0c149f613f..d6badb1efd 100644 --- a/common/rect.h +++ b/common/rect.h @@ -144,6 +144,10 @@ struct Rect : public Shape { 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/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..c19db3a9a5 100644 --- a/common/stream.h +++ b/common/stream.h @@ -350,15 +350,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), _pos(0), _end(end), - _disposeParentStream(disposeParentStream) {} + _disposeParentStream(disposeParentStream) { + assert(parentStream); + } ~SubReadStream() { if (_disposeParentStream) delete _parentStream; } @@ -414,6 +416,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 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); |