diff options
Diffstat (limited to 'common')
-rw-r--r-- | common/algorithm.h | 27 | ||||
-rw-r--r-- | common/archive.cpp | 29 | ||||
-rw-r--r-- | common/array.h | 95 | ||||
-rw-r--r-- | common/dcl.cpp | 4 | ||||
-rw-r--r-- | common/dcl.h | 1 | ||||
-rw-r--r-- | common/fs.h | 7 | ||||
-rw-r--r-- | common/gui_options.cpp | 23 | ||||
-rw-r--r-- | common/gui_options.h | 75 | ||||
-rw-r--r-- | common/macresman.cpp | 111 | ||||
-rw-r--r-- | common/macresman.h | 19 | ||||
-rw-r--r-- | common/memstream.h | 5 | ||||
-rw-r--r-- | common/module.mk | 5 | ||||
-rw-r--r-- | common/platform.cpp | 1 | ||||
-rw-r--r-- | common/platform.h | 1 | ||||
-rw-r--r-- | common/rational.h | 2 | ||||
-rw-r--r-- | common/recorderfile.cpp | 4 | ||||
-rw-r--r-- | common/rect.h | 3 | ||||
-rw-r--r-- | common/rendermode.cpp | 30 | ||||
-rw-r--r-- | common/rendermode.h | 5 | ||||
-rw-r--r-- | common/savefile.h | 45 | ||||
-rw-r--r-- | common/scummsys.h | 19 | ||||
-rw-r--r-- | common/str.cpp | 94 | ||||
-rw-r--r-- | common/str.h | 59 | ||||
-rw-r--r-- | common/taskbar.h | 2 | ||||
-rw-r--r-- | common/updates.cpp | 68 | ||||
-rw-r--r-- | common/updates.h | 42 | ||||
-rw-r--r-- | common/xmlparser.cpp | 48 |
27 files changed, 665 insertions, 159 deletions
diff --git a/common/algorithm.h b/common/algorithm.h index 6453073ae5..13cdd9f991 100644 --- a/common/algorithm.h +++ b/common/algorithm.h @@ -177,7 +177,8 @@ T sortChoosePivot(T first, T last) { template<typename T, class StrictWeakOrdering> T sortPartition(T first, T last, T pivot, StrictWeakOrdering &comp) { --last; - SWAP(*pivot, *last); + if (pivot != last) + SWAP(*pivot, *last); T sorted; for (sorted = first; first != last; ++first) { @@ -188,7 +189,8 @@ T sortPartition(T first, T last, T pivot, StrictWeakOrdering &comp) { } } - SWAP(*last, *sorted); + if (last != sorted) + SWAP(*last, *sorted); return sorted; } @@ -268,5 +270,26 @@ T gcd(T a, T b) { #pragma warning(pop) #endif +/** + * Replacement algorithm for iterables. + * + * Replaces all occurrences of "original" in [begin, end) with occurrences of "replaced". + * + * @param[in, out] begin: First element to be examined. + * @param[in] end: Last element in the seubsection. Not examined. + * @param[in] original: Elements to be replaced. + * @param[in] replaced: Element to replace occurrences of "original". + * + * @note Usage examples and unit tests may be found in "test/common/algorithm.h" + */ +template<class It, class Dat> +void replace(It begin, It end, const Dat &original, const Dat &replaced) { + for (; begin != end; ++begin) { + if (*begin == original) { + *begin = replaced; + } + } +} + } // End of namespace Common #endif diff --git a/common/archive.cpp b/common/archive.cpp index 36d420561f..5a339900b6 100644 --- a/common/archive.cpp +++ b/common/archive.cpp @@ -48,7 +48,7 @@ int Archive::listMatchingMembers(ArchiveMemberList &list, const String &pattern) int matches = 0; ArchiveMemberList::const_iterator it = allNames.begin(); - for ( ; it != allNames.end(); ++it) { + for (; it != allNames.end(); ++it) { // TODO: We match case-insenstivie for now, our API does not define whether that's ok or not though... // For our use case case-insensitive is probably what we want to have though. if ((*it)->getName().matchString(pattern, true, true)) { @@ -64,7 +64,7 @@ int Archive::listMatchingMembers(ArchiveMemberList &list, const String &pattern) SearchSet::ArchiveNodeList::iterator SearchSet::find(const String &name) { ArchiveNodeList::iterator it = _list.begin(); - for ( ; it != _list.end(); ++it) { + for (; it != _list.end(); ++it) { if (it->_name == name) break; } @@ -73,7 +73,7 @@ SearchSet::ArchiveNodeList::iterator SearchSet::find(const String &name) { SearchSet::ArchiveNodeList::const_iterator SearchSet::find(const String &name) const { ArchiveNodeList::const_iterator it = _list.begin(); - for ( ; it != _list.end(); ++it) { + for (; it != _list.end(); ++it) { if (it->_name == name) break; } @@ -81,13 +81,13 @@ SearchSet::ArchiveNodeList::const_iterator SearchSet::find(const String &name) c } /* - Keep the nodes sorted according to descending priorities. - In case two or node nodes have the same priority, insertion - order prevails. + Keep the nodes sorted according to descending priorities. + In case two or node nodes have the same priority, insertion + order prevails. */ void SearchSet::insert(const Node &node) { ArchiveNodeList::iterator it = _list.begin(); - for ( ; it != _list.end(); ++it) { + for (; it != _list.end(); ++it) { if (it->_priority < node._priority) break; } @@ -131,8 +131,7 @@ void SearchSet::addSubDirectoriesMatching(const FSNode &directory, String origPa ++sep; if (sep != origPattern.end()) nextPattern = String(sep, origPattern.end()); - } - else { + } else { pattern = origPattern; } @@ -211,7 +210,7 @@ bool SearchSet::hasFile(const String &name) const { return false; ArchiveNodeList::const_iterator it = _list.begin(); - for ( ; it != _list.end(); ++it) { + for (; it != _list.end(); ++it) { if (it->_arc->hasFile(name)) return true; } @@ -223,7 +222,7 @@ int SearchSet::listMatchingMembers(ArchiveMemberList &list, const String &patter int matches = 0; ArchiveNodeList::const_iterator it = _list.begin(); - for ( ; it != _list.end(); ++it) + for (; it != _list.end(); ++it) matches += it->_arc->listMatchingMembers(list, pattern); return matches; @@ -233,7 +232,7 @@ int SearchSet::listMembers(ArchiveMemberList &list) const { int matches = 0; ArchiveNodeList::const_iterator it = _list.begin(); - for ( ; it != _list.end(); ++it) + for (; it != _list.end(); ++it) matches += it->_arc->listMembers(list); return matches; @@ -244,7 +243,7 @@ const ArchiveMemberPtr SearchSet::getMember(const String &name) const { return ArchiveMemberPtr(); ArchiveNodeList::const_iterator it = _list.begin(); - for ( ; it != _list.end(); ++it) { + for (; it != _list.end(); ++it) { if (it->_arc->hasFile(name)) return it->_arc->getMember(name); } @@ -257,7 +256,7 @@ SeekableReadStream *SearchSet::createReadStreamForMember(const String &name) con return 0; ArchiveNodeList::const_iterator it = _list.begin(); - for ( ; it != _list.end(); ++it) { + for (; it != _list.end(); ++it) { SeekableReadStream *stream = it->_arc->createReadStreamForMember(name); if (stream) return stream; @@ -268,7 +267,7 @@ SeekableReadStream *SearchSet::createReadStreamForMember(const String &name) con SearchManager::SearchManager() { - clear(); // Force a reset + clear(); // Force a reset } void SearchManager::clear() { diff --git a/common/array.h b/common/array.h index f240a9c2f5..04ec9f9ccb 100644 --- a/common/array.h +++ b/common/array.h @@ -141,6 +141,12 @@ public: insert_aux(_storage + idx, array.begin(), array.end()); } + /** + * Inserts element before pos. + */ + void insert(iterator pos, const T &element) { + insert_aux(pos, &element, &element + 1); + } T remove_at(size_type idx) { assert(idx < _size); @@ -187,6 +193,14 @@ public: _capacity = 0; } + iterator erase(iterator pos) { + copy(pos + 1, _storage + _size, pos); + _size--; + // We also need to destroy the last object properly here. + _storage[_size].~T(); + return pos; + } + bool empty() const { return (_size == 0); } @@ -347,6 +361,87 @@ protected: }; +/** + * Double linked list with sorted nodes. + */ +template<class T> +class SortedArray : public Array<T> { +public: + typedef T *iterator; + typedef uint size_type; + + SortedArray(int (*comparator)(const void *, const void *)) { + _comparator = comparator; + } + + /** + * Inserts element at the sorted position. + */ + void insert(const T &element) { + if (!this->_size) { + this->insert_aux(this->_storage, &element, &element + 1); + return; + } + + T *where = bsearchMin(element); + + if (where > this->_storage + this->_size) + Array<T>::push_back(element); + else + Array<T>::insert(where, element); + } + + T &operator[](size_type idx) { + error("Operation []= not allowed with SortedArray"); + } + + void insert_at(size_type idx, const T &element) { + error("Operation insert_at(idx, element) not allowed with SortedArray"); + } + + void insert_at(size_type idx, const Array<T> &array) { + error("Operation insert_at(idx, array) not allowed with SortedArray"); + } + + void insert(iterator pos, const T &element) { + error("Operation insert(pos, elemnet) not allowed with SortedArray"); + } + + void push_back(const T &element) { + error("Operation push_back(element) not allowed with SortedArray"); + } + + void push_back(const Array<T> &array) { + error("Operation push_back(array) not allowed with SortedArray"); + } + +private: + // Based on code Copyright (C) 2008-2009 Ksplice, Inc. + // Author: Tim Abbott <tabbott@ksplice.com> + // Licensed under GPLv2+ + T *bsearchMin(void *key) { + uint start_ = 0, end_ = this->_size; + int result; + + while (start_ < end_) { + uint mid = start_ + (end_ - start_) / 2; + + result = this->_comparator(key, this->_storage[mid]); + if (result < 0) + end_ = mid; + else if (result > 0) + start_ = mid + 1; + else + return &this->_storage[mid]; + } + + return &this->_storage[start_]; + } + +private: + int (*_comparator)(const void *, const void *); +}; + } // End of namespace Common #endif diff --git a/common/dcl.cpp b/common/dcl.cpp index 5993c218cb..75a533aa9d 100644 --- a/common/dcl.cpp +++ b/common/dcl.cpp @@ -449,6 +449,8 @@ bool DecompressorDCL::unpack(SeekableReadStream *sourceStream, WriteStream *targ } if (_targetFixedSize) { + if (_bytesWritten != _targetSize) + warning("DCL-INFLATE Error: Inconsistent bytes written (%d) and target buffer size (%d)", _bytesWritten, _targetSize); return _bytesWritten == _targetSize; } return true; // For targets featuring dynamic size we always succeed @@ -468,7 +470,7 @@ bool decompressDCL(ReadStream *src, byte *dest, uint32 packedSize, uint32 unpack // Read source into memory src->read(sourceBufferPtr, packedSize); - Common::MemoryReadStream *sourceStream = new MemoryReadStream(sourceBufferPtr, packedSize, DisposeAfterUse::NO); + Common::MemoryReadStream *sourceStream = new MemoryReadStream(sourceBufferPtr, packedSize, DisposeAfterUse::YES); Common::MemoryWriteStream *targetStream = new MemoryWriteStream(dest, unpackedSize); success = dcl.unpack(sourceStream, targetStream, unpackedSize, true); diff --git a/common/dcl.h b/common/dcl.h index f90bc23c8d..ade7ebd985 100644 --- a/common/dcl.h +++ b/common/dcl.h @@ -25,6 +25,7 @@ * PKWARE DCL ("explode") ("PKWARE data compression library") decompressor used in engines: * - agos (exclusively for Simon 2 setup.shr file) * - mohawk + * - neverhood * - sci */ diff --git a/common/fs.h b/common/fs.h index b5b88ba8cb..f516bf7a9c 100644 --- a/common/fs.h +++ b/common/fs.h @@ -57,7 +57,14 @@ class FSList : public Array<FSNode> {}; */ class FSNode : public ArchiveMember { private: + friend class ::AbstractFSNode; SharedPtr<AbstractFSNode> _realNode; + /** + * Construct a FSNode from a backend's AbstractFSNode implementation. + * + * @param realNode Pointer to a heap allocated instance. FSNode will take + * ownership of the pointer. + */ FSNode(AbstractFSNode *realNode); public: diff --git a/common/gui_options.cpp b/common/gui_options.cpp index d79bf1b82f..df880f4fee 100644 --- a/common/gui_options.cpp +++ b/common/gui_options.cpp @@ -53,15 +53,18 @@ const struct GameOpt { { GUIO_NOASPECT, "noAspect" }, - { GUIO_RENDERHERCGREEN, "hercGreen" }, - { GUIO_RENDERHERCAMBER, "hercAmber" }, - { GUIO_RENDERCGA, "cga" }, - { GUIO_RENDEREGA, "ega" }, - { GUIO_RENDERVGA, "vga" }, - { GUIO_RENDERAMIGA, "amiga" }, - { GUIO_RENDERFMTOWNS, "fmtowns" }, - { GUIO_RENDERPC9821, "pc9821" }, - { GUIO_RENDERPC9801, "pc9801" }, + { GUIO_RENDERHERCGREEN, "hercGreen" }, + { GUIO_RENDERHERCAMBER, "hercAmber" }, + { GUIO_RENDERCGA, "cga" }, + { GUIO_RENDEREGA, "ega" }, + { GUIO_RENDERVGA, "vga" }, + { GUIO_RENDERAMIGA, "amiga" }, + { GUIO_RENDERFMTOWNS, "fmtowns" }, + { GUIO_RENDERPC9821, "pc9821" }, + { GUIO_RENDERPC9801, "pc9801" }, + { GUIO_RENDERAPPLE2GS, "2gs" }, + { GUIO_RENDERATARIST, "atari" }, + { GUIO_RENDERMACINTOSH, "macintosh" }, { GUIO_GAMEOPTIONS1, "gameOption1" }, { GUIO_GAMEOPTIONS2, "gameOption2" }, @@ -70,6 +73,8 @@ const struct GameOpt { { GUIO_GAMEOPTIONS5, "gameOption5" }, { GUIO_GAMEOPTIONS6, "gameOption6" }, { GUIO_GAMEOPTIONS7, "gameOption7" }, + { GUIO_GAMEOPTIONS8, "gameOption8" }, + { GUIO_GAMEOPTIONS9, "gameOption9" }, { GUIO_NONE, 0 } }; diff --git a/common/gui_options.h b/common/gui_options.h index 78e9cc7199..ec3eccd161 100644 --- a/common/gui_options.h +++ b/common/gui_options.h @@ -23,47 +23,52 @@ #ifndef COMMON_GUI_OPTIONS_H #define COMMON_GUI_OPTIONS_H -#define GUIO_NONE "\000" -#define GUIO_NOSUBTITLES "\001" -#define GUIO_NOMUSIC "\002" -#define GUIO_NOSPEECH "\003" -#define GUIO_NOSFX "\004" -#define GUIO_NOMIDI "\005" -#define GUIO_NOLAUNCHLOAD "\006" +#define GUIO_NONE "\000" +#define GUIO_NOSUBTITLES "\001" +#define GUIO_NOMUSIC "\002" +#define GUIO_NOSPEECH "\003" +#define GUIO_NOSFX "\004" +#define GUIO_NOMIDI "\005" +#define GUIO_NOLAUNCHLOAD "\006" -#define GUIO_MIDIPCSPK "\007" -#define GUIO_MIDICMS "\010" -#define GUIO_MIDIPCJR "\011" -#define GUIO_MIDIADLIB "\012" -#define GUIO_MIDIC64 "\013" -#define GUIO_MIDIAMIGA "\014" -#define GUIO_MIDIAPPLEIIGS "\015" -#define GUIO_MIDITOWNS "\016" -#define GUIO_MIDIPC98 "\017" -#define GUIO_MIDIMT32 "\020" -#define GUIO_MIDIGM "\021" +#define GUIO_MIDIPCSPK "\007" +#define GUIO_MIDICMS "\010" +#define GUIO_MIDIPCJR "\011" +#define GUIO_MIDIADLIB "\012" +#define GUIO_MIDIC64 "\013" +#define GUIO_MIDIAMIGA "\014" +#define GUIO_MIDIAPPLEIIGS "\015" +#define GUIO_MIDITOWNS "\016" +#define GUIO_MIDIPC98 "\017" +#define GUIO_MIDIMT32 "\020" +#define GUIO_MIDIGM "\021" -#define GUIO_NOASPECT "\022" +#define GUIO_NOASPECT "\022" -#define GUIO_RENDERHERCGREEN "\030" -#define GUIO_RENDERHERCAMBER "\031" -#define GUIO_RENDERCGA "\032" -#define GUIO_RENDEREGA "\033" -#define GUIO_RENDERVGA "\034" -#define GUIO_RENDERAMIGA "\035" -#define GUIO_RENDERFMTOWNS "\036" -#define GUIO_RENDERPC9821 "\037" -#define GUIO_RENDERPC9801 "\040" +#define GUIO_RENDERHERCGREEN "\030" +#define GUIO_RENDERHERCAMBER "\031" +#define GUIO_RENDERCGA "\032" +#define GUIO_RENDEREGA "\033" +#define GUIO_RENDERVGA "\034" +#define GUIO_RENDERAMIGA "\035" +#define GUIO_RENDERFMTOWNS "\036" +#define GUIO_RENDERPC9821 "\037" +#define GUIO_RENDERPC9801 "\040" +#define GUIO_RENDERAPPLE2GS "\041" +#define GUIO_RENDERATARIST "\042" +#define GUIO_RENDERMACINTOSH "\043" // Special GUIO flags for the AdvancedDetector's caching of game specific // options. -#define GUIO_GAMEOPTIONS1 "\041" -#define GUIO_GAMEOPTIONS2 "\042" -#define GUIO_GAMEOPTIONS3 "\043" -#define GUIO_GAMEOPTIONS4 "\044" -#define GUIO_GAMEOPTIONS5 "\045" -#define GUIO_GAMEOPTIONS6 "\046" -#define GUIO_GAMEOPTIONS7 "\047" +#define GUIO_GAMEOPTIONS1 "\050" +#define GUIO_GAMEOPTIONS2 "\051" +#define GUIO_GAMEOPTIONS3 "\052" +#define GUIO_GAMEOPTIONS4 "\053" +#define GUIO_GAMEOPTIONS5 "\054" +#define GUIO_GAMEOPTIONS6 "\055" +#define GUIO_GAMEOPTIONS7 "\056" +#define GUIO_GAMEOPTIONS8 "\057" +#define GUIO_GAMEOPTIONS9 "\058" #define GUIO0() (GUIO_NONE) #define GUIO1(a) (a) diff --git a/common/macresman.cpp b/common/macresman.cpp index d83bde8fd8..adca1ea10b 100644 --- a/common/macresman.cpp +++ b/common/macresman.cpp @@ -29,6 +29,7 @@ #include "common/md5.h" #include "common/substream.h" #include "common/textconsole.h" +#include "common/archive.h" #ifdef MACOSX #include "common/config-manager.h" @@ -261,6 +262,76 @@ bool MacResManager::exists(const String &fileName) { return false; } +void MacResManager::listFiles(StringArray &files, const String &pattern) { + // Base names discovered so far. + typedef HashMap<String, bool, IgnoreCase_Hash, IgnoreCase_EqualTo> BaseNameSet; + BaseNameSet baseNames; + + // List files itself. + ArchiveMemberList memberList; + SearchMan.listMatchingMembers(memberList, pattern); + SearchMan.listMatchingMembers(memberList, pattern + ".rsrc"); + SearchMan.listMatchingMembers(memberList, pattern + ".bin"); + SearchMan.listMatchingMembers(memberList, constructAppleDoubleName(pattern)); + + for (ArchiveMemberList::const_iterator i = memberList.begin(), end = memberList.end(); i != end; ++i) { + String filename = (*i)->getName(); + + // For raw resource forks and MacBinary files we strip the extension + // here to obtain a valid base name. + int lastDotPos = filename.size() - 1; + for (; lastDotPos >= 0; --lastDotPos) { + if (filename[lastDotPos] == '.') { + break; + } + } + + if (lastDotPos != -1) { + const char *extension = filename.c_str() + lastDotPos + 1; + bool removeExtension = false; + + // TODO: Should we really keep filenames suggesting raw resource + // forks or MacBinary files but not being such around? This might + // depend on the pattern the client requests... + if (!scumm_stricmp(extension, "rsrc")) { + SeekableReadStream *stream = (*i)->createReadStream(); + removeExtension = stream && isRawFork(*stream); + delete stream; + } else if (!scumm_stricmp(extension, "bin")) { + SeekableReadStream *stream = (*i)->createReadStream(); + removeExtension = stream && isMacBinary(*stream); + delete stream; + } + + if (removeExtension) { + filename.erase(lastDotPos); + } + } + + // Strip AppleDouble '._' prefix if applicable. + bool isAppleDoubleName = false; + const String filenameAppleDoubleStripped = disassembleAppleDoubleName(filename, &isAppleDoubleName); + + if (isAppleDoubleName) { + SeekableReadStream *stream = (*i)->createReadStream(); + if (stream->readUint32BE() == 0x00051607) { + filename = filenameAppleDoubleStripped; + } + // TODO: Should we really keep filenames suggesting AppleDouble + // but not being AppleDouble around? This might depend on the + // pattern the client requests... + delete stream; + } + + baseNames[filename] = true; + } + + // Append resulting base names to list to indicate found files. + for (BaseNameSet::const_iterator i = baseNames.begin(), end = baseNames.end(); i != end; ++i) { + files.push_back(i->_key); + } +} + bool MacResManager::loadFromAppleDouble(SeekableReadStream &stream) { if (stream.readUint32BE() != 0x00051607) // tag return false; @@ -314,6 +385,18 @@ bool MacResManager::isMacBinary(SeekableReadStream &stream) { return true; } +bool MacResManager::isRawFork(SeekableReadStream &stream) { + // TODO: Is there a better way to detect whether this is a raw fork? + const uint32 dataOffset = stream.readUint32BE(); + const uint32 mapOffset = stream.readUint32BE(); + const uint32 dataLength = stream.readUint32BE(); + const uint32 mapLength = stream.readUint32BE(); + + return !stream.eos() && !stream.err() + && dataOffset < (uint32)stream.size() && dataOffset + dataLength <= (uint32)stream.size() + && mapOffset < (uint32)stream.size() && mapOffset + mapLength <= (uint32)stream.size(); +} + bool MacResManager::loadFromMacBinary(SeekableReadStream &stream) { byte infoHeader[MBI_INFOHDR]; stream.read(infoHeader, MBI_INFOHDR); @@ -592,4 +675,32 @@ String MacResManager::constructAppleDoubleName(String name) { return name; } +String MacResManager::disassembleAppleDoubleName(String name, bool *isAppleDouble) { + if (isAppleDouble) { + *isAppleDouble = false; + } + + // Remove "._" before the last portion of a path name. + for (int i = name.size() - 1; i >= 0; --i) { + if (i == 0) { + if (name.size() > 2 && name[0] == '.' && name[1] == '_') { + name.erase(0, 2); + if (isAppleDouble) { + *isAppleDouble = true; + } + } + } else if (name[i] == '/') { + if ((uint)(i + 2) < name.size() && name[i + 1] == '.' && name[i + 2] == '_') { + name.erase(i + 1, 2); + if (isAppleDouble) { + *isAppleDouble = true; + } + } + break; + } + } + + return name; +} + } // End of namespace Common diff --git a/common/macresman.h b/common/macresman.h index 43ec8d8e2c..05b2a875f4 100644 --- a/common/macresman.h +++ b/common/macresman.h @@ -33,6 +33,7 @@ #include "common/array.h" #include "common/fs.h" #include "common/str.h" +#include "common/str-array.h" #ifndef COMMON_MACRESMAN_H #define COMMON_MACRESMAN_H @@ -82,6 +83,16 @@ public: static bool exists(const String &fileName); /** + * List all filenames matching pattern for opening with open(). + * + * @param files Array containing all matching filenames discovered. Only + * adds to the list. + * @param pattern Pattern to match against. Taking String::matchPattern's + * format. + */ + static void listFiles(StringArray &files, const String &pattern); + + /** * Close the Mac data/resource fork pair. */ void close(); @@ -176,6 +187,7 @@ private: bool loadFromAppleDouble(SeekableReadStream &stream); static String constructAppleDoubleName(String name); + static String disassembleAppleDoubleName(String name, bool *isAppleDouble); /** * Check if the given stream is in the MacBinary format. @@ -183,6 +195,13 @@ private: */ static bool isMacBinary(SeekableReadStream &stream); + /** + * Do a sanity check whether the given stream is a raw resource fork. + * + * @param stream Stream object to check. Will not preserve its position. + */ + static bool isRawFork(SeekableReadStream &stream); + enum { kResForkNone = 0, kResForkRaw, diff --git a/common/memstream.h b/common/memstream.h index 5ecc553454..94407f5cc9 100644 --- a/common/memstream.h +++ b/common/memstream.h @@ -25,6 +25,7 @@ #include "common/stream.h" #include "common/types.h" +#include "common/util.h" namespace Common { @@ -156,7 +157,7 @@ public: * that grows as it's written to. */ class MemoryWriteStreamDynamic : public WriteStream { -private: +protected: uint32 _capacity; uint32 _size; byte *_ptr; @@ -170,7 +171,7 @@ private: byte *old_data = _data; - _capacity = new_len + 32; + _capacity = MAX(new_len + 32, _capacity * 2); _data = (byte *)malloc(_capacity); _ptr = _data + _pos; diff --git a/common/module.mk b/common/module.mk index 67c498df00..570040c8e1 100644 --- a/common/module.mk +++ b/common/module.mk @@ -56,5 +56,10 @@ MODULE_OBJS += \ recorderfile.o endif +ifdef USE_UPDATES +MODULE_OBJS += \ + updates.o +endif + # Include common rules include $(srcdir)/rules.mk diff --git a/common/platform.cpp b/common/platform.cpp index 636c1ddb52..280185d862 100644 --- a/common/platform.cpp +++ b/common/platform.cpp @@ -27,6 +27,7 @@ namespace Common { const PlatformDescription g_platforms[] = { { "2gs", "2gs", "2gs", "Apple IIgs", kPlatformApple2GS }, + { "apple2", "apple2", "apple2", "Apple II", kPlatformApple2 }, { "3do", "3do", "3do", "3DO", kPlatform3DO }, { "acorn", "acorn", "acorn", "Acorn", kPlatformAcorn }, { "amiga", "ami", "amiga", "Amiga", kPlatformAmiga }, diff --git a/common/platform.h b/common/platform.h index 17a332b851..15bcddb62e 100644 --- a/common/platform.h +++ b/common/platform.h @@ -50,6 +50,7 @@ enum Platform { kPlatformSegaCD, kPlatform3DO, kPlatformPCEngine, + kPlatformApple2, kPlatformApple2GS, kPlatformPC98, kPlatformWii, diff --git a/common/rational.h b/common/rational.h index 55fb361774..89caaf25b4 100644 --- a/common/rational.h +++ b/common/rational.h @@ -84,6 +84,8 @@ public: int getNumerator() const { return _num; } int getDenominator() const { return _denom; } + bool isOne() const { return _num == _denom; } + void debugPrint(int debuglevel = 0, const char *caption = "Rational:") const; private: diff --git a/common/recorderfile.cpp b/common/recorderfile.cpp index 71f8272b44..04802aa0c8 100644 --- a/common/recorderfile.cpp +++ b/common/recorderfile.cpp @@ -30,6 +30,8 @@ #include "graphics/surface.h" #include "graphics/scaler.h" +#ifdef ENABLE_EVENTRECORDER + #define RECORD_VERSION 1 namespace Common { @@ -714,3 +716,5 @@ void PlaybackFile::checkRecordedMD5() { } + +#endif // ENABLE_EVENTRECORDER diff --git a/common/rect.h b/common/rect.h index 32424d3e6a..e6534e55d3 100644 --- a/common/rect.h +++ b/common/rect.h @@ -163,7 +163,8 @@ struct Rect { * * @param r the rectangle to check * - * @return true if the given rectangle is inside the rectangle, false otherwise + * @return true if the given rectangle has a non-empty intersection with + * this rectangle, false otherwise */ bool intersects(const Rect &r) const { return (left < r.right) && (r.left < right) && (top < r.bottom) && (r.top < bottom); diff --git a/common/rendermode.cpp b/common/rendermode.cpp index 6115666399..e07cac4b4e 100644 --- a/common/rendermode.cpp +++ b/common/rendermode.cpp @@ -38,9 +38,12 @@ const RenderModeDescription g_renderModes[] = { { "ega", "EGA", kRenderEGA }, { "vga", "VGA", kRenderVGA }, { "amiga", "Amiga", kRenderAmiga }, - { "fmtowns", "FM-Towns", kRenderFMTowns }, - { "pc9821", "PC-9821 (256 Colors)", kRenderPC9821 }, - { "pc9801", "PC-9801 (16 Colors)", kRenderPC9801 }, + { "fmtowns", "FM-TOWNS", kRenderFMTowns }, + { "pc9821", _s("PC-9821 (256 Colors)"), kRenderPC9821 }, + { "pc9801", _s("PC-9801 (16 Colors)"), kRenderPC9801 }, + { "2gs", "Apple IIgs", kRenderApple2GS }, + { "atari", "Atari ST", kRenderAtariST }, + { "macintosh", "Macintosh", kRenderMacintosh }, {0, 0, kRenderDefault} }; @@ -53,15 +56,18 @@ struct RenderGUIOMapping { // could be used to indicate "any" mode when passed to renderMode2GUIO (if // we wanted to merge allRenderModesGUIOs back into) static const RenderGUIOMapping s_renderGUIOMapping[] = { - { kRenderHercG, GUIO_RENDERHERCGREEN }, - { kRenderHercA, GUIO_RENDERHERCAMBER }, - { kRenderCGA, GUIO_RENDERCGA }, - { kRenderEGA, GUIO_RENDEREGA }, - { kRenderVGA, GUIO_RENDERVGA }, - { kRenderAmiga, GUIO_RENDERAMIGA }, - { kRenderFMTowns, GUIO_RENDERFMTOWNS }, - { kRenderPC9821, GUIO_RENDERPC9821 }, - { kRenderPC9801, GUIO_RENDERPC9801 } + { kRenderHercG, GUIO_RENDERHERCGREEN }, + { kRenderHercA, GUIO_RENDERHERCAMBER }, + { kRenderCGA, GUIO_RENDERCGA }, + { kRenderEGA, GUIO_RENDEREGA }, + { kRenderVGA, GUIO_RENDERVGA }, + { kRenderAmiga, GUIO_RENDERAMIGA }, + { kRenderFMTowns, GUIO_RENDERFMTOWNS }, + { kRenderPC9821, GUIO_RENDERPC9821 }, + { kRenderPC9801, GUIO_RENDERPC9801 }, + { kRenderApple2GS, GUIO_RENDERAPPLE2GS }, + { kRenderAtariST, GUIO_RENDERATARIST }, + { kRenderMacintosh, GUIO_RENDERMACINTOSH } }; DECLARE_TRANSLATION_ADDITIONAL_CONTEXT("Hercules Green", "lowres") diff --git a/common/rendermode.h b/common/rendermode.h index 59fa860c6c..ae1a7bc790 100644 --- a/common/rendermode.h +++ b/common/rendermode.h @@ -45,7 +45,10 @@ enum RenderMode { kRenderAmiga = 6, kRenderFMTowns = 7, kRenderPC9821 = 8, - kRenderPC9801 = 9 + kRenderPC9801 = 9, + kRenderApple2GS = 10, + kRenderAtariST = 11, + kRenderMacintosh = 12 }; struct RenderModeDescription { diff --git a/common/savefile.h b/common/savefile.h index b0c4d31f53..9fca07f9d5 100644 --- a/common/savefile.h +++ b/common/savefile.h @@ -56,6 +56,12 @@ typedef WriteStream OutSaveFile; * i.e. typically save states, but also configuration files and similar * things. * + * Savefile names represent SaveFiles. These names are case insensitive, that + * means a name of "Kq1.000" represents the same savefile as "kq1.000". In + * addition, SaveFileManager does not allow for names which contain path + * separators like '/' or '\'. This is because we do not support directories + * in SaveFileManager. + * * While not declared as a singleton, it is effectively used as such, * with OSystem::getSavefileManager returning a pointer to the single * SaveFileManager instances to be used. @@ -115,49 +121,56 @@ public: * exports from the Quest for Glory series. QfG5 is a 3D game and won't be * supported by ScummVM. * - * @param name the name of the savefile - * @param compress toggles whether to compress the resulting save file - * (default) or not. - * @return pointer to an OutSaveFile, or NULL if an error occurred. + * @param name The name of the savefile. + * @param compress Toggles whether to compress the resulting save file + * (default) or not. + * @return Pointer to an OutSaveFile, or NULL if an error occurred. */ virtual OutSaveFile *openForSaving(const String &name, bool compress = true) = 0; /** * Open the file with the specified name in the given directory for loading. - * @param name the name of the savefile - * @return pointer to an InSaveFile, or NULL if an error occurred. + * + * @param name The name of the savefile. + * @return Pointer to an InSaveFile, or NULL if an error occurred. */ virtual InSaveFile *openForLoading(const String &name) = 0; /** * Removes the given savefile from the system. - * @param name the name of the savefile to be removed. + * + * @param name The name of the savefile to be removed. * @return true if no error occurred, false otherwise. */ virtual bool removeSavefile(const String &name) = 0; /** * Renames the given savefile. - * @param oldName Old name. - * @param newName New name. + * + * @param oldName Old name. + * @param newName New name. * @return true if no error occurred. false otherwise. */ virtual bool renameSavefile(const String &oldName, const String &newName); /** * Copy the given savefile. - * @param oldName Old name. - * @param newName New name. + * + * @param oldName Old name. + * @param newName New name. * @return true if no error occurred. false otherwise. */ virtual bool copySavefile(const String &oldName, const String &newName); /** - * Request a list of available savegames with a given DOS-style pattern, - * also known as "glob" in the POSIX world. Refer to the Common::matchString() - * function to learn about the precise pattern format. - * @param pattern Pattern to match. Wildcards like * or ? are available. - * @return list of strings for all present file names. + * List available savegames matching a given pattern. + * + * Our pattern format is based on DOS paterns, also known as "glob" in the + * POSIX world. Please refer to the Common::matchString() function to learn + * about the precise pattern format. + * + * @param pattern Pattern to match. Wildcards like * or ? are available. + * @return List of strings for all present file names. * @see Common::matchString() */ virtual StringArray listSavefiles(const String &pattern) = 0; diff --git a/common/scummsys.h b/common/scummsys.h index b8cf7678a4..3513ee2d7d 100644 --- a/common/scummsys.h +++ b/common/scummsys.h @@ -23,8 +23,8 @@ #ifndef COMMON_SCUMMSYS_H #define COMMON_SCUMMSYS_H -#ifndef __has_feature // Optional of course. - #define __has_feature(x) 0 // Compatibility with non-clang compilers. +#ifndef __has_feature // Optional of course. + #define __has_feature(x) 0 // Compatibility with non-clang compilers. #endif // This is a convenience macro to test whether the compiler used is a GCC @@ -215,6 +215,10 @@ #include "config.h" #endif +// Now we need to adjust some settings when running tests +#ifdef COMPILING_TESTS +#undef ENABLE_EVENTRECORDER +#endif // In the following we configure various targets, in particular those // which can't use our "configure" tool and hence don't use config.h. @@ -251,6 +255,7 @@ #if defined(__DC__) || \ defined(__DS__) || \ + defined(__3DS__) || \ defined(__GP32__) || \ defined(IPHONE) || \ defined(__PLAYSTATION2__) || \ @@ -367,11 +372,11 @@ #endif #ifndef STRINGBUFLEN - #if defined(__N64__) || defined(__DS__) - #define STRINGBUFLEN 256 - #else - #define STRINGBUFLEN 1024 - #endif + #if defined(__N64__) || defined(__DS__) || defined(__3DS__) + #define STRINGBUFLEN 256 + #else + #define STRINGBUFLEN 1024 + #endif #endif #ifndef MAXPATHLEN diff --git a/common/str.cpp b/common/str.cpp index faf84d722f..3ff49a90c6 100644 --- a/common/str.cpp +++ b/common/str.cpp @@ -75,7 +75,7 @@ void String::initWithCStr(const char *str, uint32 len) { } String::String(const String &str) - : _size(str._size) { + : _size(str._size) { if (str.isStorageIntern()) { // String in internal storage: just copy it memcpy(_storage, str._storage, _builtinCapacity); @@ -91,7 +91,7 @@ String::String(const String &str) } String::String(char c) - : _size(0), _str(_storage) { + : _size(0), _str(_storage) { _storage[0] = c; _storage[1] = 0; @@ -132,24 +132,19 @@ void String::ensureCapacity(uint32 new_size, bool keep_old) { if (!isShared && new_size < curCapacity) return; - if (isShared && new_size < _builtinCapacity) { - // We share the storage, but there is enough internal storage: Use that. - newStorage = _storage; - newCapacity = _builtinCapacity; - } else { - // We need to allocate storage on the heap! - - // Compute a suitable new capacity limit - // If the current capacity is sufficient we use the same capacity - if (new_size < curCapacity) - newCapacity = curCapacity; - else - newCapacity = MAX(curCapacity * 2, computeCapacity(new_size+1)); - - // Allocate new storage - newStorage = new char[newCapacity]; - assert(newStorage); - } + // We need to allocate storage on the heap! + + // Compute a suitable new capacity limit + // If the current capacity is sufficient we use the same capacity + if (new_size < curCapacity) + newCapacity = curCapacity; + else + newCapacity = MAX(curCapacity * 2, computeCapacity(new_size+1)); + + // Allocate new storage + newStorage = new char[newCapacity]; + assert(newStorage); + // Copy old data if needed, elsewise reset the new storage. if (keep_old) { @@ -444,6 +439,58 @@ uint String::hash() const { return hashit(c_str()); } +void String::replace(uint32 pos, uint32 count, const String &str) { + replace(pos, count, str, 0, str._size); +} + +void String::replace(uint32 pos, uint32 count, const char *str) { + replace(pos, count, str, 0, strlen(str)); +} + +void String::replace(iterator begin_, iterator end_, const String &str) { + replace(begin_ - _str, end_ - begin_, str._str, 0, str._size); +} + +void String::replace(iterator begin_, iterator end_, const char *str) { + replace(begin_ - _str, end_ - begin_, str, 0, strlen(str)); +} + +void String::replace(uint32 posOri, uint32 countOri, const String &str, + uint32 posDest, uint32 countDest) { + replace(posOri, countOri, str._str, posDest, countDest); +} + +void String::replace(uint32 posOri, uint32 countOri, const char *str, + uint32 posDest, uint32 countDest) { + + ensureCapacity(_size + countDest - countOri, true); + + // Prepare string for the replaced text. + if (countOri < countDest) { + uint32 offset = countDest - countOri; ///< Offset to copy the characters + uint32 newSize = _size + offset; + _size = newSize; + + // Push the old characters to the end of the string + for (uint32 i = _size; i >= posOri + countDest; i--) + _str[i] = _str[i - offset]; + + } else if (countOri > countDest){ + uint32 offset = countOri - countDest; ///< Number of positions that we have to pull back + + // Pull the remainder string back + for (uint32 i = posOri + countDest; i < _size; i++) + _str[i] = _str[i + offset]; + + _size -= offset; + } + + // Copy the replaced part of the string + for (uint32 i = 0; i < countDest; i++) + _str[posOri + i] = str[posDest + i]; + +} + // static String String::format(const char *fmt, ...) { String output; @@ -751,6 +798,13 @@ bool matchString(const char *str, const char *pat, bool ignoreCase, bool pathMod return true; break; + case '#': + if (!isDigit(*str)) + return false; + pat++; + str++; + break; + default: if ((!ignoreCase && *pat != *str) || (ignoreCase && tolower(*pat) != tolower(*str))) { diff --git a/common/str.h b/common/str.h index dede87a005..9ada8aaaa0 100644 --- a/common/str.h +++ b/common/str.h @@ -46,6 +46,17 @@ namespace Common { class String { public: static const uint32 npos = 0xFFFFFFFF; + + typedef char value_type; + /** + * Unsigned version of the underlying type. This can be used to cast + * individual string characters to bigger integer types without sign + * extension happening. + */ + typedef unsigned char unsigned_type; + typedef char * iterator; + typedef const char * const_iterator; + protected: /** * The size of the internal storage. Increasing this means less heap @@ -158,6 +169,7 @@ public: * Token meaning: * "*": any character, any amount of times. * "?": any character, only once. + * "#": any decimal digit, only once. * * Example strings/patterns: * String: monkey.s01 Pattern: monkey.s?? => true @@ -165,6 +177,8 @@ public: * String: monkey.s99 Pattern: monkey.s?1 => false * String: monkey.s101 Pattern: monkey.s* => true * String: monkey.s99 Pattern: monkey.s*1 => false + * String: monkey.s01 Pattern: monkey.s## => true + * String: monkey.s01 Pattern: monkey.### => false * * @param pat Glob pattern. * @param ignoreCase Whether to ignore the case when doing pattern match @@ -180,6 +194,7 @@ public: inline uint size() const { return _size; } inline bool empty() const { return (_size == 0); } + char firstChar() const { return (_size > 0) ? _str[0] : 0; } char lastChar() const { return (_size > 0) ? _str[_size - 1] : 0; } char operator[](int idx) const { @@ -218,6 +233,38 @@ public: void trim(); uint hash() const; + + /**@{ + * Functions to replace some amount of chars with chars from some other string. + * + * @note The implementation follows that of the STL's std::string: + * http://www.cplusplus.com/reference/string/string/replace/ + * + * @param pos Starting position for the replace in the original string. + * @param count Number of chars to replace from the original string. + * @param str Source of the new chars. + * @param posOri Same as pos + * @param countOri Same as count + * @param posDest Initial position to read str from. + * @param countDest Number of chars to read from str. npos by default. + */ + // Replace 'count' bytes, starting from 'pos' with str. + void replace(uint32 pos, uint32 count, const String &str); + // The same as above, but accepts a C-like array of characters. + void replace(uint32 pos, uint32 count, const char *str); + // Replace the characters in [begin, end) with str._str. + void replace(iterator begin, iterator end, const String &str); + // Replace the characters in [begin, end) with str. + void replace(iterator begin, iterator end, const char *str); + // Replace _str[posOri, posOri + countOri) with + // str._str[posDest, posDest + countDest) + void replace(uint32 posOri, uint32 countOri, const String &str, + uint32 posDest, uint32 countDest); + // Replace _str[posOri, posOri + countOri) with + // str[posDest, posDest + countDest) + void replace(uint32 posOri, uint32 countOri, const char *str, + uint32 posDest, uint32 countDest); + /**@}*/ /** * Print formatted data into a String object. Similar to sprintf, @@ -234,15 +281,6 @@ public: static String vformat(const char *fmt, va_list args); public: - typedef char value_type; - /** - * Unsigned version of the underlying type. This can be used to cast - * individual string characters to bigger integer types without sign - * extension happening. - */ - typedef unsigned char unsigned_type; - typedef char * iterator; - typedef const char * const_iterator; iterator begin() { // Since the user could potentially @@ -329,6 +367,7 @@ String normalizePath(const String &path, const char sep); * Token meaning: * "*": any character, any amount of times. * "?": any character, only once. + * "#": any decimal digit, only once. * * Example strings/patterns: * String: monkey.s01 Pattern: monkey.s?? => true @@ -336,6 +375,8 @@ String normalizePath(const String &path, const char sep); * String: monkey.s99 Pattern: monkey.s?1 => false * String: monkey.s101 Pattern: monkey.s* => true * String: monkey.s99 Pattern: monkey.s*1 => false + * String: monkey.s01 Pattern: monkey.s## => true + * String: monkey.s01 Pattern: monkey.### => false * * @param str Text to be matched against the given pattern. * @param pat Glob pattern. diff --git a/common/taskbar.h b/common/taskbar.h index b4ec673739..f1a9adb2d9 100644 --- a/common/taskbar.h +++ b/common/taskbar.h @@ -123,7 +123,7 @@ public: virtual void addRecent(const String &name, const String &description) {} /** - * Notifies the user an error occured through the taskbar icon + * Notifies the user an error occurred through the taskbar icon * * This will for example show the taskbar icon as red (using progress of 100% and an error state) * on Windows, and set the launcher icon in the urgent state on Unity diff --git a/common/updates.cpp b/common/updates.cpp new file mode 100644 index 0000000000..087002a7d3 --- /dev/null +++ b/common/updates.cpp @@ -0,0 +1,68 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/system.h" +#include "common/updates.h" +#include "common/translation.h" + +namespace Common { + +static const int updateIntervals[] = { + UpdateManager::kUpdateIntervalNotSupported, + UpdateManager::kUpdateIntervalOneDay, + UpdateManager::kUpdateIntervalOneWeek, + UpdateManager::kUpdateIntervalOneMonth, + -1 +}; + +const int *UpdateManager::getUpdateIntervals() { + return updateIntervals; +} + +int UpdateManager::normalizeInterval(int interval) { + const int *val = updateIntervals; + + while (*val != -1) { + if (*val >= interval) + return *val; + val++; + } + + return val[-1]; // Return maximal acceptable value +} + +const char *UpdateManager::updateIntervalToString(int interval) { + switch (interval) { + case kUpdateIntervalNotSupported: + return _("Never"); + case kUpdateIntervalOneDay: + return _("Daily"); + case kUpdateIntervalOneWeek: + return _("Weekly"); + case kUpdateIntervalOneMonth: + return _("Monthly"); + default: + return _("<Bad value>"); + } +} + +} // End of namespace Common diff --git a/common/updates.h b/common/updates.h index 4c30987c38..3a3049d4df 100644 --- a/common/updates.h +++ b/common/updates.h @@ -20,8 +20,8 @@ * */ -#ifndef BACKENDS_UPDATES_ABSTRACT_H -#define BACKENDS_UPDATES_ABSTRACT_H +#ifndef COMMON_UPDATES_H +#define COMMON_UPDATES_H #if defined(USE_UPDATES) @@ -85,18 +85,50 @@ public: * * @param interval The interval. */ - virtual void setUpdateCheckInterval(UpdateInterval interval) {} + virtual void setUpdateCheckInterval(int interval) {} /** * Gets the update check interval. * * @return the update check interval. */ - virtual UpdateInterval getUpdateCheckInterval() { return kUpdateIntervalNotSupported; } + virtual int getUpdateCheckInterval() { return kUpdateIntervalNotSupported; } + + /** + * Gets last update check time + * + * @param t TimeDate struct to fill out + * @return flag indicating success + */ + virtual bool getLastUpdateCheckTimeAndDate(TimeDate &t) { return false; } + + /** + * Returns list of supported uptate intervals. + * Ending with '-1' which is not acceptable value. + * + * @return list of integer values representing update intervals in seconds. + */ + static const int *getUpdateIntervals(); + + /** + * Returns string representation of a given interval. + * + * @param interval The interval. + * @return pointer to localized string of given interval. + */ + static const char *updateIntervalToString(int interval); + + /** + * Rounds up the given interval to acceptable value. + * + * @param interval The interval. + * @return rounded up interval + */ + static int normalizeInterval(int interval); }; } // End of namespace Common #endif -#endif // BACKENDS_UPDATES_ABSTRACT_H +#endif // COMMON_UPDATES_H diff --git a/common/xmlparser.cpp b/common/xmlparser.cpp index 67a3d36cec..da4f577e3c 100644 --- a/common/xmlparser.cpp +++ b/common/xmlparser.cpp @@ -97,36 +97,38 @@ bool XMLParser::parserError(const String &errStr) { assert(_stream->pos() == startPosition); currentPosition = startPosition; - int keyOpening = 0; - int keyClosing = 0; - - while (currentPosition-- && keyOpening == 0) { - _stream->seek(-2, SEEK_CUR); - c = _stream->readByte(); + Common::String errorMessage = Common::String::format("\n File <%s>, line %d:\n", _fileName.c_str(), lineCount); - if (c == '<') - keyOpening = currentPosition - 1; - else if (c == '>') - keyClosing = currentPosition; - } + if (startPosition > 1) { + int keyOpening = 0; + int keyClosing = 0; - _stream->seek(startPosition, SEEK_SET); - currentPosition = startPosition; - while (keyClosing == 0 && c && currentPosition++) { - c = _stream->readByte(); + while (currentPosition-- && keyOpening == 0) { + _stream->seek(-2, SEEK_CUR); + c = _stream->readByte(); - if (c == '>') - keyClosing = currentPosition; - } + if (c == '<') + keyOpening = currentPosition - 1; + else if (c == '>') + keyClosing = currentPosition; + } - Common::String errorMessage = Common::String::format("\n File <%s>, line %d:\n", _fileName.c_str(), lineCount); + _stream->seek(startPosition, SEEK_SET); + currentPosition = startPosition; + while (keyClosing == 0 && c && currentPosition++) { + c = _stream->readByte(); - currentPosition = (keyClosing - keyOpening); - _stream->seek(keyOpening, SEEK_SET); + if (c == '>') + keyClosing = currentPosition; + } - while (currentPosition--) - errorMessage += (char)_stream->readByte(); + currentPosition = (keyClosing - keyOpening); + _stream->seek(keyOpening, SEEK_SET); + while (currentPosition--) + errorMessage += (char)_stream->readByte(); + } + errorMessage += "\n\nParser error: "; errorMessage += errStr; errorMessage += "\n\n"; |