diff options
author | D G Turner | 2012-10-12 17:03:32 +0100 |
---|---|---|
committer | D G Turner | 2012-10-12 17:03:32 +0100 |
commit | 151b7beb47ec4b964862d6779bd48e3a33482bbd (patch) | |
tree | 867717c5266d0908d95edd82560599be20a4ede9 /common | |
parent | 80af0e239473f85c49cc2da3c848dfcde41d4a37 (diff) | |
parent | 2b55837650c4229dc3d75b660cecfc7a3292e5e0 (diff) | |
download | scummvm-rg350-151b7beb47ec4b964862d6779bd48e3a33482bbd.tar.gz scummvm-rg350-151b7beb47ec4b964862d6779bd48e3a33482bbd.tar.bz2 scummvm-rg350-151b7beb47ec4b964862d6779bd48e3a33482bbd.zip |
Merge branch 'master' into teenagentRefactor
Conflicts:
engines/teenagent/callbacks.cpp
Diffstat (limited to 'common')
-rw-r--r-- | common/array.h | 2 | ||||
-rw-r--r-- | common/coroutines.cpp | 10 | ||||
-rw-r--r-- | common/cosinetables.cpp | 2 | ||||
-rw-r--r-- | common/endian.h | 6 | ||||
-rw-r--r-- | common/gui_options.h | 2 | ||||
-rw-r--r-- | common/installshield_cab.cpp | 212 | ||||
-rw-r--r-- | common/installshield_cab.h | 43 | ||||
-rw-r--r-- | common/keyboard.h | 9 | ||||
-rw-r--r-- | common/macresman.cpp | 22 | ||||
-rw-r--r-- | common/macresman.h | 3 | ||||
-rw-r--r-- | common/memstream.h | 11 | ||||
-rw-r--r-- | common/module.mk | 1 | ||||
-rw-r--r-- | common/quicktime.h | 3 | ||||
-rw-r--r-- | common/rational.h | 3 | ||||
-rw-r--r-- | common/rect.h | 14 | ||||
-rw-r--r-- | common/savefile.h | 4 | ||||
-rw-r--r-- | common/sinetables.cpp | 2 | ||||
-rw-r--r-- | common/winexe_pe.cpp | 2 | ||||
-rw-r--r-- | common/xmlparser.cpp | 18 | ||||
-rw-r--r-- | common/zlib.cpp | 63 | ||||
-rw-r--r-- | common/zlib.h | 29 |
21 files changed, 424 insertions, 37 deletions
diff --git a/common/array.h b/common/array.h index a2c3023362..ca89523a0b 100644 --- a/common/array.h +++ b/common/array.h @@ -332,7 +332,7 @@ protected: // Copy a part of the new data to the position inside the // initialized space. copy(first, first + (_size - idx), pos); - + // Copy a part of the new data to the position inside the // uninitialized space. uninitialized_copy(first + (_size - idx), last, _storage + _size); diff --git a/common/coroutines.cpp b/common/coroutines.cpp index 042b15b5d7..849b881177 100644 --- a/common/coroutines.cpp +++ b/common/coroutines.cpp @@ -433,9 +433,9 @@ void CoroutineScheduler::waitForMultipleObjects(CORO_PARAM, int nCount, uint32 * // Determine the signalled state _ctx->pidSignalled = (_ctx->pProcess) || !_ctx->pEvent ? false : _ctx->pEvent->signalled; - if (bWaitAll && _ctx->pidSignalled) + if (bWaitAll && !_ctx->pidSignalled) _ctx->signalled = false; - else if (!bWaitAll & _ctx->pidSignalled) + else if (!bWaitAll && _ctx->pidSignalled) _ctx->signalled = true; } @@ -445,7 +445,7 @@ void CoroutineScheduler::waitForMultipleObjects(CORO_PARAM, int nCount, uint32 * for (_ctx->i = 0; _ctx->i < nCount; ++_ctx->i) { _ctx->pEvent = getEvent(pidList[_ctx->i]); - if (_ctx->pEvent->manualReset) + if (!_ctx->pEvent->manualReset) _ctx->pEvent->signalled = false; } @@ -717,12 +717,12 @@ void CoroutineScheduler::pulseEvent(uint32 pidEvent) { EVENT *evt = getEvent(pidEvent); if (!evt) return; - + // Set the event as signalled and pulsing evt->signalled = true; evt->pulsing = true; - // If there's an active process, and it's not the first in the queue, then reschedule all + // If there's an active process, and it's not the first in the queue, then reschedule all // the other prcoesses in the queue to run again this frame if (pCurrent && pCurrent != active->pNext) rescheduleAll(); diff --git a/common/cosinetables.cpp b/common/cosinetables.cpp index bf158e904f..fe8f454e14 100644 --- a/common/cosinetables.cpp +++ b/common/cosinetables.cpp @@ -36,7 +36,7 @@ CosineTable::CosineTable(int bitPrecision) { double freq = 2 * M_PI / m; _table = new float[m]; - // Table contains cos(2*pi*x/n) for 0<=x<=n/4, + // Table contains cos(2*pi*x/n) for 0<=x<=n/4, // followed by its reverse for (int i = 0; i <= m / 4; i++) _table[i] = cos(i * freq); diff --git a/common/endian.h b/common/endian.h index 394437ec67..759513efef 100644 --- a/common/endian.h +++ b/common/endian.h @@ -146,6 +146,12 @@ */ #define MKTAG(a0,a1,a2,a3) ((uint32)((a3) | ((a2) << 8) | ((a1) << 16) | ((a0) << 24))) +/** + * A wrapper macro used around two character constants, like 'wb', to + * ensure portability. Typical usage: MKTAG16('w','b'). + */ +#define MKTAG16(a0,a1) ((uint16)((a1) | ((a0) << 8))) + // Functions for reading/writing native integers. // They also transparently handle the need for alignment. diff --git a/common/gui_options.h b/common/gui_options.h index 9da19b1c3e..447fff43ed 100644 --- a/common/gui_options.h +++ b/common/gui_options.h @@ -55,7 +55,7 @@ #define GUIO_RENDERPC9821 "\037" #define GUIO_RENDERPC9801 "\040" -// Special GUIO flags for the AdvancedDetector's caching of game specific +// Special GUIO flags for the AdvancedDetector's caching of game specific // options. #define GUIO_GAMEOPTIONS1 "\041" #define GUIO_GAMEOPTIONS2 "\042" diff --git a/common/installshield_cab.cpp b/common/installshield_cab.cpp new file mode 100644 index 0000000000..e25d14741a --- /dev/null +++ b/common/installshield_cab.cpp @@ -0,0 +1,212 @@ +/* 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. + * + */ + +// The following code is based on unshield +// Original copyright: + +// Copyright (c) 2003 David Eriksson <twogood@users.sourceforge.net> +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include "common/archive.h" +#include "common/debug.h" +#include "common/hash-str.h" +#include "common/installshield_cab.h" +#include "common/memstream.h" +#include "common/zlib.h" + +namespace Common { + +class InstallShieldCabinet : public Archive { +public: + InstallShieldCabinet(SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse); + ~InstallShieldCabinet(); + + // Archive API implementation + bool hasFile(const String &name) const; + int listMembers(ArchiveMemberList &list) const; + const ArchiveMemberPtr getMember(const String &name) const; + SeekableReadStream *createReadStreamForMember(const String &name) const; + +private: + struct FileEntry { + uint32 uncompressedSize; + uint32 compressedSize; + uint32 offset; + uint16 flags; + }; + + typedef HashMap<String, FileEntry, IgnoreCase_Hash, IgnoreCase_EqualTo> FileMap; + FileMap _map; + Common::SeekableReadStream *_stream; + DisposeAfterUse::Flag _disposeAfterUse; +}; + +InstallShieldCabinet::~InstallShieldCabinet() { + _map.clear(); + + if (_disposeAfterUse == DisposeAfterUse::YES) + delete _stream; +} + +InstallShieldCabinet::InstallShieldCabinet(SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) : _stream(stream), _disposeAfterUse(disposeAfterUse) { + // Note that we only support a limited subset of cabinet files + // Only single cabinet files and ones without data shared between + // cabinets. + + // Check for the magic uint32 + if (_stream->readUint32LE() != 0x28635349) { + warning("InstallShieldCabinet::InstallShieldCabinet(): Magic ID doesn't match"); + return; + } + + uint32 version = _stream->readUint32LE(); + + if (version != 0x01000004) { + warning("Unsupported CAB version %08x", version); + return; + } + + /* uint32 volumeInfo = */ _stream->readUint32LE(); + uint32 cabDescriptorOffset = _stream->readUint32LE(); + /* uint32 cabDescriptorSize = */ _stream->readUint32LE(); + + _stream->seek(cabDescriptorOffset); + + _stream->skip(12); + uint32 fileTableOffset = _stream->readUint32LE(); + _stream->skip(4); + uint32 fileTableSize = _stream->readUint32LE(); + uint32 fileTableSize2 = _stream->readUint32LE(); + uint32 directoryCount = _stream->readUint32LE(); + _stream->skip(8); + uint32 fileCount = _stream->readUint32LE(); + + if (fileTableSize != fileTableSize2) + warning("file table sizes do not match"); + + // We're ignoring file groups and components since we + // should not need them. Moving on to the files... + + _stream->seek(cabDescriptorOffset + fileTableOffset); + uint32 fileTableCount = directoryCount + fileCount; + uint32 *fileTableOffsets = new uint32[fileTableCount]; + for (uint32 i = 0; i < fileTableCount; i++) + fileTableOffsets[i] = _stream->readUint32LE(); + + for (uint32 i = directoryCount; i < fileCount + directoryCount; i++) { + _stream->seek(cabDescriptorOffset + fileTableOffset + fileTableOffsets[i]); + uint32 nameOffset = _stream->readUint32LE(); + /* uint32 directoryIndex = */ _stream->readUint32LE(); + + // First read in data needed by us to get at the file data + FileEntry entry; + entry.flags = _stream->readUint16LE(); + entry.uncompressedSize = _stream->readUint32LE(); + entry.compressedSize = _stream->readUint32LE(); + _stream->skip(20); + entry.offset = _stream->readUint32LE(); + + // Then let's get the string + _stream->seek(cabDescriptorOffset + fileTableOffset + nameOffset); + String fileName; + + char c = _stream->readByte(); + while (c) { + fileName += c; + c = _stream->readByte(); + } + _map[fileName] = entry; + } + + delete[] fileTableOffsets; +} + +bool InstallShieldCabinet::hasFile(const String &name) const { + return _map.contains(name); +} + +int InstallShieldCabinet::listMembers(ArchiveMemberList &list) const { + for (FileMap::const_iterator it = _map.begin(); it != _map.end(); it++) + list.push_back(getMember(it->_key)); + + return _map.size(); +} + +const ArchiveMemberPtr InstallShieldCabinet::getMember(const String &name) const { + return ArchiveMemberPtr(new GenericArchiveMember(name, this)); +} + +SeekableReadStream *InstallShieldCabinet::createReadStreamForMember(const String &name) const { + if (!_map.contains(name)) + return 0; + + const FileEntry &entry = _map[name]; + + _stream->seek(entry.offset); + + if (!(entry.flags & 0x04)) // Not compressed + return _stream->readStream(entry.uncompressedSize); + +#ifdef USE_ZLIB + byte *src = (byte *)malloc(entry.compressedSize); + byte *dst = (byte *)malloc(entry.uncompressedSize); + + _stream->read(src, entry.compressedSize); + + bool result = inflateZlibInstallShield(dst, entry.uncompressedSize, src, entry.compressedSize); + free(src); + + if (!result) { + warning("failed to inflate CAB file '%s'", name.c_str()); + free(dst); + return 0; + } + + return new MemoryReadStream(dst, entry.uncompressedSize, DisposeAfterUse::YES); +#else + warning("zlib required to extract compressed CAB file '%s'", name.c_str()); + return 0; +#endif +} + +Archive *makeInstallShieldArchive(SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) { + return new InstallShieldCabinet(stream, disposeAfterUse); +} + +} // End of namespace AGOS diff --git a/common/installshield_cab.h b/common/installshield_cab.h new file mode 100644 index 0000000000..7c4f294578 --- /dev/null +++ b/common/installshield_cab.h @@ -0,0 +1,43 @@ +/* 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. + * + */ + +#ifndef COMMON_INSTALLSHIELD_CAB_H +#define COMMON_INSTALLSHIELD_CAB_H + +#include "common/types.h" + +namespace Common { + +class Archive; +class SeekableReadStream; + +/** + * This factory method creates an Archive instance corresponding to the content + * of the InstallShield compressed stream. + * + * May return 0 in case of a failure. + */ +Archive *makeInstallShieldArchive(SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES); + +} // End of namespace Common + +#endif diff --git a/common/keyboard.h b/common/keyboard.h index f9e94e6656..3262a15c3f 100644 --- a/common/keyboard.h +++ b/common/keyboard.h @@ -224,12 +224,13 @@ enum { KBD_CTRL = 1 << 0, KBD_ALT = 1 << 1, KBD_SHIFT = 1 << 2, - KBD_NON_STICKY = (KBD_CTRL|KBD_ALT|KBD_SHIFT), + KBD_META = 1 << 3, + KBD_NON_STICKY = (KBD_CTRL|KBD_ALT|KBD_SHIFT|KBD_META), // Sticky modifier flags - KBD_NUM = 1 << 3, - KBD_CAPS = 1 << 4, - KBD_SCRL = 1 << 5, + KBD_NUM = 1 << 4, + KBD_CAPS = 1 << 5, + KBD_SCRL = 1 << 6, KBD_STICKY = (KBD_NUM|KBD_CAPS|KBD_SCRL) }; diff --git a/common/macresman.cpp b/common/macresman.cpp index 14bdfa7080..f2f020c6de 100644 --- a/common/macresman.cpp +++ b/common/macresman.cpp @@ -124,7 +124,7 @@ bool MacResManager::open(String filename) { File *file = new File(); // First, let's try to see if the Mac converted name exists - if (file->open("._" + filename) && loadFromAppleDouble(*file)) { + if (file->open(constructAppleDoubleName(filename)) && loadFromAppleDouble(*file)) { _baseFileName = filename; return true; } @@ -185,7 +185,7 @@ bool MacResManager::open(FSNode path, String filename) { #endif // First, let's try to see if the Mac converted name exists - FSNode fsNode = path.getChild("._" + filename); + FSNode fsNode = path.getChild(constructAppleDoubleName(filename)); if (fsNode.exists() && !fsNode.isDirectory()) { SeekableReadStream *stream = fsNode.createReadStream(); if (loadFromAppleDouble(*stream)) { @@ -253,7 +253,7 @@ bool MacResManager::exists(const String &filename) { return true; // Check if we have an AppleDouble file - if (tempFile.open("._" + filename) && tempFile.readUint32BE() == 0x00051607) + if (tempFile.open(constructAppleDoubleName(filename)) && tempFile.readUint32BE() == 0x00051607) return true; return false; @@ -574,4 +574,20 @@ void MacResManager::readMap() { } } +Common::String MacResManager::constructAppleDoubleName(Common::String name) { + // Insert "._" before the last portion of a path name + for (int i = name.size() - 1; i >= 0; i--) { + if (i == 0) { + name.insertChar('_', 0); + name.insertChar('.', 0); + } else if (name[i] == '/') { + name.insertChar('_', i + 1); + name.insertChar('.', i + 1); + break; + } + } + + return name; +} + } // End of namespace Common diff --git a/common/macresman.h b/common/macresman.h index 6820106925..ed74da9cc6 100644 --- a/common/macresman.h +++ b/common/macresman.h @@ -25,6 +25,7 @@ * Macintosh resource fork manager used in engines: * - groovie * - mohawk + * - pegasus * - sci * - scumm */ @@ -175,6 +176,8 @@ private: bool loadFromMacBinary(SeekableReadStream &stream); bool loadFromAppleDouble(SeekableReadStream &stream); + static Common::String constructAppleDoubleName(Common::String name); + enum { kResForkNone = 0, kResForkRaw, diff --git a/common/memstream.h b/common/memstream.h index 69fe6ec18e..497a178ab9 100644 --- a/common/memstream.h +++ b/common/memstream.h @@ -92,13 +92,17 @@ private: byte *_ptr; const uint32 _bufSize; uint32 _pos; + bool _err; public: - MemoryWriteStream(byte *buf, uint32 len) : _ptr(buf), _bufSize(len), _pos(0) {} + MemoryWriteStream(byte *buf, uint32 len) : _ptr(buf), _bufSize(len), _pos(0), _err(false) {} uint32 write(const void *dataPtr, uint32 dataSize) { // Write at most as many bytes as are still available... - if (dataSize > _bufSize - _pos) + if (dataSize > _bufSize - _pos) { dataSize = _bufSize - _pos; + // We couldn't write all the data => set error indicator + _err = true; + } memcpy(_ptr, dataPtr, dataSize); _ptr += dataSize; _pos += dataSize; @@ -107,6 +111,9 @@ public: uint32 pos() const { return _pos; } uint32 size() const { return _bufSize; } + + virtual bool err() const { return _err; } + virtual void clearErr() { _err = false; } }; /** diff --git a/common/module.mk b/common/module.mk index 92279740e5..d96b11ee40 100644 --- a/common/module.mk +++ b/common/module.mk @@ -16,6 +16,7 @@ MODULE_OBJS := \ gui_options.o \ hashmap.o \ iff_container.o \ + installshield_cab.o \ language.o \ localization.o \ macresman.o \ diff --git a/common/quicktime.h b/common/quicktime.h index 974502d075..641718e13a 100644 --- a/common/quicktime.h +++ b/common/quicktime.h @@ -35,6 +35,7 @@ #include "common/scummsys.h" #include "common/stream.h" #include "common/rational.h" +#include "common/types.h" namespace Common { class MacResManager; @@ -167,7 +168,7 @@ protected: Rational _scaleFactorX; Rational _scaleFactorY; Array<Track *> _tracks; - + void init(); private: diff --git a/common/rational.h b/common/rational.h index 45aa6a7a20..8270d2194e 100644 --- a/common/rational.h +++ b/common/rational.h @@ -80,6 +80,9 @@ public: double toDouble() const; frac_t toFrac() const; + int getNumerator() const { return _num; } + int getDenominator() const { return _denom; } + void debugPrint(int debuglevel = 0, const char *caption = "Rational:") const; private: diff --git a/common/rect.h b/common/rect.h index 2bd3affafe..8d1243f7e4 100644 --- a/common/rect.h +++ b/common/rect.h @@ -170,6 +170,20 @@ struct Rect { } /** + * Find the intersecting rectangle between this rectangle and the given rectangle + * + * @param r the intersecting rectangle + * + * @return the intersection of the rectangles or an empty rectangle if not intersecting + */ + Rect findIntersectingRect(const Rect &r) const { + if (!intersects(r)) + return Rect(); + + return Rect(MAX(r.left, left), MAX(r.top, top), MIN(r.right, right), MIN(r.bottom, bottom)); + } + + /** * Extend this rectangle so that it contains r * * @param r the rectangle to extend by diff --git a/common/savefile.h b/common/savefile.h index da787289ee..19536da54f 100644 --- a/common/savefile.h +++ b/common/savefile.h @@ -109,12 +109,12 @@ public: * * Saved games are compressed by default, and engines are expected to * always write compressed saves. - * + * * A notable exception is if uncompressed files are needed for * compatibility with games not supported by ScummVM, such as character * 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. diff --git a/common/sinetables.cpp b/common/sinetables.cpp index a4467383cc..a6ec99469d 100644 --- a/common/sinetables.cpp +++ b/common/sinetables.cpp @@ -36,7 +36,7 @@ SineTable::SineTable(int bitPrecision) { double freq = 2 * M_PI / m; _table = new float[m]; - // Table contains sin(2*pi*x/n) for 0<=x<=n/4, + // Table contains sin(2*pi*x/n) for 0<=x<=n/4, // followed by its reverse for (int i = 0; i <= m / 4; i++) _table[i] = sin(i * freq); diff --git a/common/winexe_pe.cpp b/common/winexe_pe.cpp index 6c0f9c9962..b3c45ffe73 100644 --- a/common/winexe_pe.cpp +++ b/common/winexe_pe.cpp @@ -64,7 +64,7 @@ bool PEResources::loadFromEXE(SeekableReadStream *stream) { if (!stream) return false; - if (stream->readUint16BE() != 'MZ') + if (stream->readUint16BE() != MKTAG16('M', 'Z')) return false; stream->skip(58); diff --git a/common/xmlparser.cpp b/common/xmlparser.cpp index ea3d44cf87..f0b7f1cc81 100644 --- a/common/xmlparser.cpp +++ b/common/xmlparser.cpp @@ -20,15 +20,11 @@ * */ -// FIXME: Avoid using fprintf -#define FORBIDDEN_SYMBOL_EXCEPTION_fprintf -#define FORBIDDEN_SYMBOL_EXCEPTION_stderr - - #include "common/xmlparser.h" #include "common/archive.h" #include "common/fs.h" #include "common/memstream.h" +#include "common/system.h" namespace Common { @@ -123,17 +119,19 @@ bool XMLParser::parserError(const String &errStr) { keyClosing = currentPosition; } - fprintf(stderr, "\n File <%s>, line %d:\n", _fileName.c_str(), lineCount); + Common::String errorMessage = Common::String::format("\n File <%s>, line %d:\n", _fileName.c_str(), lineCount); currentPosition = (keyClosing - keyOpening); _stream->seek(keyOpening, SEEK_SET); while (currentPosition--) - fprintf(stderr, "%c", _stream->readByte()); + errorMessage += (char)_stream->readByte(); + + errorMessage += "\n\nParser error: "; + errorMessage += errStr; + errorMessage += "\n\n"; - fprintf(stderr, "\n\nParser error: "); - fprintf(stderr, "%s", errStr.c_str()); - fprintf(stderr, "\n\n"); + g_system->logMessage(LogMessageType::kError, errorMessage.c_str()); return false; } diff --git a/common/zlib.cpp b/common/zlib.cpp index 7d765fc539..fc8f351054 100644 --- a/common/zlib.cpp +++ b/common/zlib.cpp @@ -85,6 +85,60 @@ bool inflateZlibHeaderless(byte *dst, uint dstLen, const byte *src, uint srcLen, return true; } +enum { + kTempBufSize = 65536 +}; + +bool inflateZlibInstallShield(byte *dst, uint dstLen, const byte *src, uint srcLen) { + if (!dst || !dstLen || !src || !srcLen) + return false; + + // See if we have sync bytes. If so, just use our function for that. + if (srcLen >= 4 && READ_BE_UINT32(src + srcLen - 4) == 0xFFFF) + return inflateZlibHeaderless(dst, dstLen, src, srcLen); + + // Otherwise, we have some custom code we get to use here. + + byte *temp = (byte *)malloc(kTempBufSize); + + uint32 bytesRead = 0, bytesProcessed = 0; + while (bytesRead < srcLen) { + uint16 chunkSize = READ_LE_UINT16(src + bytesRead); + bytesRead += 2; + + // Initialize zlib + z_stream stream; + stream.next_in = const_cast<byte *>(src + bytesRead); + stream.avail_in = chunkSize; + stream.next_out = temp; + stream.avail_out = kTempBufSize; + stream.zalloc = Z_NULL; + stream.zfree = Z_NULL; + stream.opaque = Z_NULL; + + // Negative MAX_WBITS tells zlib there's no zlib header + int err = inflateInit2(&stream, -MAX_WBITS); + if (err != Z_OK) + return false; + + err = inflate(&stream, Z_FINISH); + if (err != Z_OK && err != Z_STREAM_END) { + inflateEnd(&stream); + free(temp); + return false; + } + + memcpy(dst + bytesProcessed, temp, stream.total_out); + bytesProcessed += stream.total_out; + + inflateEnd(&stream); + bytesRead += chunkSize; + } + + free(temp); + return true; +} + /** * A simple wrapper class which can be used to wrap around an arbitrary * other SeekableReadStream and will then provide on-the-fly decompression support. @@ -107,7 +161,7 @@ protected: public: - GZipReadStream(SeekableReadStream *w) : _wrapped(w), _stream() { + GZipReadStream(SeekableReadStream *w, uint32 knownSize = 0) : _wrapped(w), _stream() { assert(w != 0); // Verify file header is correct @@ -122,7 +176,8 @@ public: _origSize = w->readUint32LE(); } else { // Original size not available in zlib format - _origSize = 0; + // use an otherwise known size if supplied. + _origSize = knownSize; } _pos = 0; w->seek(0, SEEK_SET); @@ -336,7 +391,7 @@ public: #endif // USE_ZLIB -SeekableReadStream *wrapCompressedReadStream(SeekableReadStream *toBeWrapped) { +SeekableReadStream *wrapCompressedReadStream(SeekableReadStream *toBeWrapped, uint32 knownSize) { #if defined(USE_ZLIB) if (toBeWrapped) { uint16 header = toBeWrapped->readUint16BE(); @@ -345,7 +400,7 @@ SeekableReadStream *wrapCompressedReadStream(SeekableReadStream *toBeWrapped) { header % 31 == 0)); toBeWrapped->seek(-2, SEEK_CUR); if (isCompressed) - return new GZipReadStream(toBeWrapped); + return new GZipReadStream(toBeWrapped, knownSize); } #endif return toBeWrapped; diff --git a/common/zlib.h b/common/zlib.h index 61322c286a..6a840f5fdc 100644 --- a/common/zlib.h +++ b/common/zlib.h @@ -77,6 +77,25 @@ bool uncompress(byte *dst, unsigned long *dstLen, const byte *src, unsigned long */ bool inflateZlibHeaderless(byte *dst, uint dstLen, const byte *src, uint srcLen, const byte *dict = 0, uint dictLen = 0); +/** + * Wrapper around zlib's inflate functions. This function will call the + * necessary inflate functions to uncompress data compressed for InstallShield + * cabinet files. + * + * Decompresses the src buffer into the dst buffer. + * srcLen is the byte length of the source buffer, dstLen is the byte + * length of the output buffer. + * It decompress as much data as possible, up to dstLen bytes. + * + * @param dst the buffer to store into. + * @param dstLen the size of the destination buffer. + * @param src the data to be decompressed. + * @param dstLen the size of the compressed data. + * + * @return true on success (Z_OK or Z_STREAM_END), false otherwise. + */ +bool inflateZlibInstallShield(byte *dst, uint dstLen, const byte *src, uint srcLen); + #endif /** @@ -86,10 +105,18 @@ bool inflateZlibHeaderless(byte *dst, uint dstLen, const byte *src, uint srcLen, * format. In the former case, the original stream is returned unmodified * (and in particular, not wrapped). * + * Certain GZip-formats don't supply an easily readable length, if you + * still need the length carried along with the stream, and you know + * the decompressed length at wrap-time, then it can be supplied as knownSize + * here. knownSize will be ignored if the GZip-stream DOES include a length. + * * It is safe to call this with a NULL parameter (in this case, NULL is * returned). + * + * @param toBeWrapped the stream to be wrapped (if it is in gzip-format) + * @param knownSize a supplied length of the compressed data (if not available directly) */ -SeekableReadStream *wrapCompressedReadStream(SeekableReadStream *toBeWrapped); +SeekableReadStream *wrapCompressedReadStream(SeekableReadStream *toBeWrapped, uint32 knownSize = 0); /** * Take an arbitrary WriteStream and wrap it in a custom stream which provides |