ResidualVM logo ResidualVM website - Forums - Contact us BuildBot - Doxygen - Wiki curved edge

zlib.cpp

Go to the documentation of this file.
00001 /* ScummVM - Graphic Adventure Engine
00002  *
00003  * ScummVM is the legal property of its developers, whose names
00004  * are too numerous to list here. Please refer to the COPYRIGHT
00005  * file distributed with this source distribution.
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  *
00021  */
00022 
00023 // Disable symbol overrides so that we can use zlib.h
00024 #define FORBIDDEN_SYMBOL_ALLOW_ALL
00025 
00026 #include "common/zlib.h"
00027 #include "common/ptr.h"
00028 #include "common/util.h"
00029 #include "common/stream.h"
00030 #include "common/debug.h"
00031 #include "common/textconsole.h"
00032 
00033 #if defined(USE_ZLIB)
00034   #ifdef __SYMBIAN32__
00035     #include <zlib\zlib.h>
00036   #else
00037     #include <zlib.h>
00038   #endif
00039 
00040   #if ZLIB_VERNUM < 0x1204
00041   #error Version 1.2.0.4 or newer of zlib is required for this code
00042   #endif
00043 #endif
00044 
00045 
00046 namespace Common {
00047 
00048 #if defined(USE_ZLIB)
00049 
00050 bool uncompress(byte *dst, unsigned long *dstLen, const byte *src, unsigned long srcLen) {
00051     return Z_OK == ::uncompress(dst, dstLen, src, srcLen);
00052 }
00053 
00054 bool inflateZlibHeaderless(byte *dst, uint dstLen, const byte *src, uint srcLen, const byte *dict, uint dictLen) {
00055     if (!dst || !dstLen || !src || !srcLen)
00056         return false;
00057 
00058     // Initialize zlib
00059     z_stream stream;
00060     stream.next_in = const_cast<byte *>(src);
00061     stream.avail_in = srcLen;
00062     stream.next_out = dst;
00063     stream.avail_out = dstLen;
00064     stream.zalloc = Z_NULL;
00065     stream.zfree = Z_NULL;
00066     stream.opaque = Z_NULL;
00067 
00068     // Negative MAX_WBITS tells zlib there's no zlib header
00069     int err = inflateInit2(&stream, -MAX_WBITS);
00070     if (err != Z_OK)
00071         return false;
00072 
00073     // Set the dictionary, if provided
00074     if (dict != nullptr) {
00075         err = inflateSetDictionary(&stream, const_cast<byte *>(dict), dictLen);
00076         if (err != Z_OK)
00077             return false;
00078     }
00079 
00080     err = inflate(&stream, Z_SYNC_FLUSH);
00081     if (err != Z_OK && err != Z_STREAM_END) {
00082         inflateEnd(&stream);
00083         return false;
00084     }
00085 
00086     inflateEnd(&stream);
00087     return true;
00088 }
00089 
00090 enum {
00091     kTempBufSize = 65536
00092 };
00093 
00094 bool inflateZlibInstallShield(byte *dst, uint dstLen, const byte *src, uint srcLen) {
00095     if (!dst || !dstLen || !src || !srcLen)
00096         return false;
00097 
00098     // See if we have sync bytes. If so, just use our function for that.
00099     if (srcLen >= 4 && READ_BE_UINT32(src + srcLen - 4) == 0xFFFF)
00100         return inflateZlibHeaderless(dst, dstLen, src, srcLen);
00101 
00102     // Otherwise, we have some custom code we get to use here.
00103 
00104     byte *temp = (byte *)malloc(kTempBufSize);
00105 
00106     uint32 bytesRead = 0, bytesProcessed = 0;
00107     while (bytesRead < srcLen) {
00108         uint16 chunkSize = READ_LE_UINT16(src + bytesRead);
00109         bytesRead += 2;
00110 
00111         // Initialize zlib
00112         z_stream stream;
00113         stream.next_in = const_cast<byte *>(src + bytesRead);
00114         stream.avail_in = chunkSize;
00115         stream.next_out = temp;
00116         stream.avail_out = kTempBufSize;
00117         stream.zalloc = Z_NULL;
00118         stream.zfree = Z_NULL;
00119         stream.opaque = Z_NULL;
00120 
00121         // Negative MAX_WBITS tells zlib there's no zlib header
00122         int err = inflateInit2(&stream, -MAX_WBITS);
00123         if (err != Z_OK)
00124             return false;
00125 
00126         err = inflate(&stream, Z_FINISH);
00127         if (err != Z_OK && err != Z_STREAM_END) {
00128             inflateEnd(&stream);
00129             free(temp);
00130             return false;
00131         }
00132 
00133         memcpy(dst + bytesProcessed, temp, stream.total_out);
00134         bytesProcessed += stream.total_out;
00135 
00136         inflateEnd(&stream);
00137         bytesRead += chunkSize;
00138     }
00139 
00140     free(temp);
00141     return true;
00142 }
00143 
00144 bool inflateZlibHeaderless(Common::WriteStream *dst, Common::SeekableReadStream *src) {
00145     byte *inBuffer, *outBuffer;
00146     z_stream stream;
00147     int status;
00148 
00149     // Allocate buffers
00150     inBuffer = new byte[kTempBufSize];
00151     outBuffer = new byte[kTempBufSize];
00152 
00153     /* Initialize Zlib inflation functions. */
00154     stream.next_out = outBuffer;
00155     stream.avail_out = kTempBufSize;
00156     stream.next_in = inBuffer;
00157     stream.avail_in = 0;
00158 
00159     stream.zalloc = Z_NULL;
00160     stream.zfree = Z_NULL;
00161     stream.opaque = Z_NULL;
00162 
00163     status = inflateInit(&stream);
00164     if (status != Z_OK) {
00165         delete[] inBuffer;
00166         delete[] outBuffer;
00167         return false;
00168     }
00169 
00170     // Inflate the input buffers. */
00171     for (;;) {
00172         int inBytes, outBytes;
00173 
00174         /* If the input buffer is empty, try to obtain more data. */
00175         if (stream.avail_in == 0) {
00176             inBytes = src->read(inBuffer, kTempBufSize);
00177             stream.next_in = inBuffer;
00178             stream.avail_in = inBytes;
00179         }
00180 
00181         // Decompress as much stream data as we can. */
00182         status = inflate(&stream, Z_SYNC_FLUSH);
00183         if (status != Z_STREAM_END && status != Z_OK) {
00184             delete[] inBuffer;
00185             delete[] outBuffer;
00186             return false;
00187         }
00188         outBytes = kTempBufSize - stream.avail_out;
00189 
00190         // See if decompressed data is available. */
00191         if (outBytes > 0) {
00192             // Add data from the buffer to the output
00193             int consumed = dst->write(outBuffer, outBytes);
00194 
00195             // Move unused buffer data to buffer start
00196             memmove(outBuffer, outBuffer + consumed, kTempBufSize - consumed);
00197 
00198             // Reset inflation stream for available space
00199             stream.next_out = outBuffer + outBytes - consumed;
00200             stream.avail_out += consumed;
00201         }
00202 
00203         // If at inflation stream end and output is empty, leave loop
00204         if (status == Z_STREAM_END && stream.avail_out == kTempBufSize)
00205             break;
00206     }
00207 
00208     // End inflation buffers
00209     status = inflateEnd(&stream);
00210     delete[] inBuffer;
00211     delete[] outBuffer;
00212 
00213     // Return result
00214     return (status == Z_OK);
00215 }
00216 
00217 #ifndef RELEASE_BUILD
00218 static bool _shownBackwardSeekingWarning = false;
00219 #endif
00220 
00226 class GZipReadStream : public SeekableReadStream {
00227 protected:
00228     enum {
00229         BUFSIZE = 16384     // 1 << MAX_WBITS
00230     };
00231 
00232     byte    _buf[BUFSIZE];
00233 
00234     ScopedPtr<SeekableReadStream> _wrapped;
00235     z_stream _stream;
00236     int _zlibErr;
00237     uint32 _pos;
00238     uint32 _origSize;
00239     bool _eos;
00240 
00241 public:
00242 
00243     GZipReadStream(SeekableReadStream *w, uint32 knownSize = 0) : _wrapped(w), _stream() {
00244         assert(w != nullptr);
00245 
00246         // Verify file header is correct
00247         w->seek(0, SEEK_SET);
00248         uint16 header = w->readUint16BE();
00249         assert(header == 0x1F8B ||
00250                ((header & 0x0F00) == 0x0800 && header % 31 == 0));
00251 
00252         if (header == 0x1F8B) {
00253             // Retrieve the original file size
00254             w->seek(-4, SEEK_END);
00255             _origSize = w->readUint32LE();
00256         } else {
00257             // Original size not available in zlib format
00258             // use an otherwise known size if supplied.
00259             _origSize = knownSize;
00260         }
00261         _pos = 0;
00262         w->seek(0, SEEK_SET);
00263         _eos = false;
00264 
00265         // Adding 32 to windowBits indicates to zlib that it is supposed to
00266         // automatically detect whether gzip or zlib headers are used for
00267         // the compressed file. This feature was added in zlib 1.2.0.4,
00268         // released 10 August 2003.
00269         // Note: This is *crucial* for savegame compatibility, do *not* remove!
00270         _zlibErr = inflateInit2(&_stream, MAX_WBITS + 32);
00271         if (_zlibErr != Z_OK)
00272             return;
00273 
00274         // Setup input buffer
00275         _stream.next_in = _buf;
00276         _stream.avail_in = 0;
00277     }
00278 
00279     ~GZipReadStream() {
00280         inflateEnd(&_stream);
00281     }
00282 
00283     bool err() const { return (_zlibErr != Z_OK) && (_zlibErr != Z_STREAM_END); }
00284     void clearErr() {
00285         // only reset _eos; I/O errors are not recoverable
00286         _eos = false;
00287     }
00288 
00289     uint32 read(void *dataPtr, uint32 dataSize) {
00290         _stream.next_out = (byte *)dataPtr;
00291         _stream.avail_out = dataSize;
00292 
00293         // Keep going while we get no error
00294         while (_zlibErr == Z_OK && _stream.avail_out) {
00295             if (_stream.avail_in == 0 && !_wrapped->eos()) {
00296                 // If we are out of input data: Read more data, if available.
00297                 _stream.next_in = _buf;
00298                 _stream.avail_in = _wrapped->read(_buf, BUFSIZE);
00299             }
00300             _zlibErr = inflate(&_stream, Z_NO_FLUSH);
00301         }
00302 
00303         // Update the position counter
00304         _pos += dataSize - _stream.avail_out;
00305 
00306         if (_zlibErr == Z_STREAM_END && _stream.avail_out > 0)
00307             _eos = true;
00308 
00309         return dataSize - _stream.avail_out;
00310     }
00311 
00312     bool eos() const {
00313         return _eos;
00314     }
00315     int32 pos() const {
00316         return _pos;
00317     }
00318     int32 size() const {
00319         return _origSize;
00320     }
00321     bool seek(int32 offset, int whence = SEEK_SET) {
00322         int32 newPos = 0;
00323         switch (whence) {
00324         default:
00325             // fallthrough intended
00326         case SEEK_SET:
00327             newPos = offset;
00328             break;
00329         case SEEK_CUR:
00330             newPos = _pos + offset;
00331             break;
00332         case SEEK_END:
00333             // NOTE: This can be an expensive operation (see below).
00334             newPos = size() + offset;
00335             break;
00336         }
00337 
00338         assert(newPos >= 0);
00339 
00340         if ((uint32)newPos < _pos) {
00341             // To search backward, we have to restart the whole decompression
00342             // from the start of the file. A rather wasteful operation, best
00343             // to avoid it. :/
00344 
00345 #ifndef RELEASE_BUILD
00346             if (!_shownBackwardSeekingWarning) {
00347                 // We only throw this warning once per stream, to avoid
00348                 // getting the console swarmed with warnings when consecutive
00349                 // seeks are made.
00350                 debug(1, "Backward seeking in GZipReadStream detected");
00351                 _shownBackwardSeekingWarning = true;
00352             }
00353 #endif
00354 
00355             _pos = 0;
00356             _wrapped->seek(0, SEEK_SET);
00357             _zlibErr = inflateReset(&_stream);
00358             if (_zlibErr != Z_OK)
00359                 return false; // FIXME: STREAM REWRITE
00360             _stream.next_in = _buf;
00361             _stream.avail_in = 0;
00362         }
00363 
00364         offset = newPos - _pos;
00365 
00366         // Skip the given amount of data (very inefficient if one tries to skip
00367         // huge amounts of data, but usually client code will only skip a few
00368         // bytes, so this should be fine.
00369         byte tmpBuf[1024];
00370         while (!err() && offset > 0) {
00371             offset -= read(tmpBuf, MIN((int32)sizeof(tmpBuf), offset));
00372         }
00373 
00374         _eos = false;
00375         return true; // FIXME: STREAM REWRITE
00376     }
00377 };
00378 
00384 class GZipWriteStream : public WriteStream {
00385 protected:
00386     enum {
00387         BUFSIZE = 16384     // 1 << MAX_WBITS
00388     };
00389 
00390     byte    _buf[BUFSIZE];
00391     ScopedPtr<WriteStream> _wrapped;
00392     z_stream _stream;
00393     int _zlibErr;
00394     uint32 _pos;
00395 
00396     void processData(int flushType) {
00397         // This function is called by both write() and finalize().
00398         while (_zlibErr == Z_OK && (_stream.avail_in || flushType == Z_FINISH)) {
00399             if (_stream.avail_out == 0) {
00400                 if (_wrapped->write(_buf, BUFSIZE) != BUFSIZE) {
00401                     _zlibErr = Z_ERRNO;
00402                     break;
00403                 }
00404                 _stream.next_out = _buf;
00405                 _stream.avail_out = BUFSIZE;
00406             }
00407             _zlibErr = deflate(&_stream, flushType);
00408         }
00409     }
00410 
00411 public:
00412     GZipWriteStream(WriteStream *w) : _wrapped(w), _stream(), _pos(0) {
00413         assert(w != nullptr);
00414 
00415         // Adding 16 to windowBits indicates to zlib that it is supposed to
00416         // write gzip headers. This feature was added in zlib 1.2.0.4,
00417         // released 10 August 2003.
00418         // Note: This is *crucial* for savegame compatibility, do *not* remove!
00419         _zlibErr = deflateInit2(&_stream,
00420                          Z_DEFAULT_COMPRESSION,
00421                          Z_DEFLATED,
00422                          MAX_WBITS + 16,
00423                          8,
00424                  Z_DEFAULT_STRATEGY);
00425         assert(_zlibErr == Z_OK);
00426 
00427         _stream.next_out = _buf;
00428         _stream.avail_out = BUFSIZE;
00429         _stream.avail_in = 0;
00430         _stream.next_in = nullptr;
00431     }
00432 
00433     ~GZipWriteStream() {
00434         finalize();
00435         deflateEnd(&_stream);
00436     }
00437 
00438     bool err() const {
00439         // CHECKME: does Z_STREAM_END make sense here?
00440         return (_zlibErr != Z_OK && _zlibErr != Z_STREAM_END) || _wrapped->err();
00441     }
00442 
00443     void clearErr() {
00444         // Note: we don't reset the _zlibErr here, as it is not
00445         // clear in general how
00446         _wrapped->clearErr();
00447     }
00448 
00449     void finalize() {
00450         if (_zlibErr != Z_OK)
00451             return;
00452 
00453         // Process whatever remaining data there is.
00454         processData(Z_FINISH);
00455 
00456         // Since processData only writes out blocks of size BUFSIZE,
00457         // we may have to flush some stragglers.
00458         uint remainder = BUFSIZE - _stream.avail_out;
00459         if (remainder > 0) {
00460             if (_wrapped->write(_buf, remainder) != remainder) {
00461                 _zlibErr = Z_ERRNO;
00462             }
00463         }
00464 
00465         // Finalize the wrapped savefile, too
00466         _wrapped->finalize();
00467     }
00468 
00469     uint32 write(const void *dataPtr, uint32 dataSize) {
00470         if (err())
00471             return 0;
00472 
00473         // Hook in the new data ...
00474         // Note: We need to make a const_cast here, as zlib is not aware
00475         // of the const keyword.
00476         _stream.next_in = const_cast<byte *>((const byte *)dataPtr);
00477         _stream.avail_in = dataSize;
00478 
00479         // ... and flush it to disk
00480         processData(Z_NO_FLUSH);
00481 
00482         _pos += dataSize - _stream.avail_in;
00483         return dataSize - _stream.avail_in;
00484     }
00485 
00486     virtual int32 pos() const { return _pos; }
00487 };
00488 
00489 #endif  // USE_ZLIB
00490 
00491 SeekableReadStream *wrapCompressedReadStream(SeekableReadStream *toBeWrapped, uint32 knownSize) {
00492     if (toBeWrapped) {
00493         uint16 header = toBeWrapped->readUint16BE();
00494         bool isCompressed = (header == 0x1F8B ||
00495                      ((header & 0x0F00) == 0x0800 &&
00496                       header % 31 == 0));
00497         toBeWrapped->seek(-2, SEEK_CUR);
00498         if (isCompressed) {
00499 #if defined(USE_ZLIB)
00500             return new GZipReadStream(toBeWrapped, knownSize);
00501 #else
00502             delete toBeWrapped;
00503             return NULL;
00504 #endif
00505         }
00506     }
00507     return toBeWrapped;
00508 }
00509 
00510 WriteStream *wrapCompressedWriteStream(WriteStream *toBeWrapped) {
00511 #if defined(USE_ZLIB)
00512     if (toBeWrapped)
00513         return new GZipWriteStream(toBeWrapped);
00514 #endif
00515     return toBeWrapped;
00516 }
00517 
00518 
00519 } // End of namespace Common


Generated on Sat Jul 4 2020 05:01:15 for ResidualVM by doxygen 1.7.1
curved edge   curved edge