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 #ifndef RELEASE_BUILD
00145 static bool _shownBackwardSeekingWarning = false;
00146 #endif
00147 
00153 class GZipReadStream : public SeekableReadStream {
00154 protected:
00155     enum {
00156         BUFSIZE = 16384     // 1 << MAX_WBITS
00157     };
00158 
00159     byte    _buf[BUFSIZE];
00160 
00161     ScopedPtr<SeekableReadStream> _wrapped;
00162     z_stream _stream;
00163     int _zlibErr;
00164     uint32 _pos;
00165     uint32 _origSize;
00166     bool _eos;
00167 
00168 public:
00169 
00170     GZipReadStream(SeekableReadStream *w, uint32 knownSize = 0) : _wrapped(w), _stream() {
00171         assert(w != nullptr);
00172 
00173         // Verify file header is correct
00174         w->seek(0, SEEK_SET);
00175         uint16 header = w->readUint16BE();
00176         assert(header == 0x1F8B ||
00177                ((header & 0x0F00) == 0x0800 && header % 31 == 0));
00178 
00179         if (header == 0x1F8B) {
00180             // Retrieve the original file size
00181             w->seek(-4, SEEK_END);
00182             _origSize = w->readUint32LE();
00183         } else {
00184             // Original size not available in zlib format
00185             // use an otherwise known size if supplied.
00186             _origSize = knownSize;
00187         }
00188         _pos = 0;
00189         w->seek(0, SEEK_SET);
00190         _eos = false;
00191 
00192         // Adding 32 to windowBits indicates to zlib that it is supposed to
00193         // automatically detect whether gzip or zlib headers are used for
00194         // the compressed file. This feature was added in zlib 1.2.0.4,
00195         // released 10 August 2003.
00196         // Note: This is *crucial* for savegame compatibility, do *not* remove!
00197         _zlibErr = inflateInit2(&_stream, MAX_WBITS + 32);
00198         if (_zlibErr != Z_OK)
00199             return;
00200 
00201         // Setup input buffer
00202         _stream.next_in = _buf;
00203         _stream.avail_in = 0;
00204     }
00205 
00206     ~GZipReadStream() {
00207         inflateEnd(&_stream);
00208     }
00209 
00210     bool err() const { return (_zlibErr != Z_OK) && (_zlibErr != Z_STREAM_END); }
00211     void clearErr() {
00212         // only reset _eos; I/O errors are not recoverable
00213         _eos = false;
00214     }
00215 
00216     uint32 read(void *dataPtr, uint32 dataSize) {
00217         _stream.next_out = (byte *)dataPtr;
00218         _stream.avail_out = dataSize;
00219 
00220         // Keep going while we get no error
00221         while (_zlibErr == Z_OK && _stream.avail_out) {
00222             if (_stream.avail_in == 0 && !_wrapped->eos()) {
00223                 // If we are out of input data: Read more data, if available.
00224                 _stream.next_in = _buf;
00225                 _stream.avail_in = _wrapped->read(_buf, BUFSIZE);
00226             }
00227             _zlibErr = inflate(&_stream, Z_NO_FLUSH);
00228         }
00229 
00230         // Update the position counter
00231         _pos += dataSize - _stream.avail_out;
00232 
00233         if (_zlibErr == Z_STREAM_END && _stream.avail_out > 0)
00234             _eos = true;
00235 
00236         return dataSize - _stream.avail_out;
00237     }
00238 
00239     bool eos() const {
00240         return _eos;
00241     }
00242     int32 pos() const {
00243         return _pos;
00244     }
00245     int32 size() const {
00246         return _origSize;
00247     }
00248     bool seek(int32 offset, int whence = SEEK_SET) {
00249         int32 newPos = 0;
00250         switch (whence) {
00251         case SEEK_SET:
00252             newPos = offset;
00253             break;
00254         case SEEK_CUR:
00255             newPos = _pos + offset;
00256             break;
00257         case SEEK_END:
00258             // NOTE: This can be an expensive operation (see below).
00259             newPos = size() + offset;
00260             break;
00261         }
00262 
00263         assert(newPos >= 0);
00264 
00265         if ((uint32)newPos < _pos) {
00266             // To search backward, we have to restart the whole decompression
00267             // from the start of the file. A rather wasteful operation, best
00268             // to avoid it. :/
00269 
00270 #ifndef RELEASE_BUILD
00271             if (!_shownBackwardSeekingWarning) {
00272                 // We only throw this warning once per stream, to avoid
00273                 // getting the console swarmed with warnings when consecutive
00274                 // seeks are made.
00275                 debug(1, "Backward seeking in GZipReadStream detected");
00276                 _shownBackwardSeekingWarning = true;
00277             }
00278 #endif
00279 
00280             _pos = 0;
00281             _wrapped->seek(0, SEEK_SET);
00282             _zlibErr = inflateReset(&_stream);
00283             if (_zlibErr != Z_OK)
00284                 return false; // FIXME: STREAM REWRITE
00285             _stream.next_in = _buf;
00286             _stream.avail_in = 0;
00287         }
00288 
00289         offset = newPos - _pos;
00290 
00291         // Skip the given amount of data (very inefficient if one tries to skip
00292         // huge amounts of data, but usually client code will only skip a few
00293         // bytes, so this should be fine.
00294         byte tmpBuf[1024];
00295         while (!err() && offset > 0) {
00296             offset -= read(tmpBuf, MIN((int32)sizeof(tmpBuf), offset));
00297         }
00298 
00299         _eos = false;
00300         return true; // FIXME: STREAM REWRITE
00301     }
00302 };
00303 
00309 class GZipWriteStream : public WriteStream {
00310 protected:
00311     enum {
00312         BUFSIZE = 16384     // 1 << MAX_WBITS
00313     };
00314 
00315     byte    _buf[BUFSIZE];
00316     ScopedPtr<WriteStream> _wrapped;
00317     z_stream _stream;
00318     int _zlibErr;
00319     uint32 _pos;
00320 
00321     void processData(int flushType) {
00322         // This function is called by both write() and finalize().
00323         while (_zlibErr == Z_OK && (_stream.avail_in || flushType == Z_FINISH)) {
00324             if (_stream.avail_out == 0) {
00325                 if (_wrapped->write(_buf, BUFSIZE) != BUFSIZE) {
00326                     _zlibErr = Z_ERRNO;
00327                     break;
00328                 }
00329                 _stream.next_out = _buf;
00330                 _stream.avail_out = BUFSIZE;
00331             }
00332             _zlibErr = deflate(&_stream, flushType);
00333         }
00334     }
00335 
00336 public:
00337     GZipWriteStream(WriteStream *w) : _wrapped(w), _stream(), _pos(0) {
00338         assert(w != nullptr);
00339 
00340         // Adding 16 to windowBits indicates to zlib that it is supposed to
00341         // write gzip headers. This feature was added in zlib 1.2.0.4,
00342         // released 10 August 2003.
00343         // Note: This is *crucial* for savegame compatibility, do *not* remove!
00344         _zlibErr = deflateInit2(&_stream,
00345                          Z_DEFAULT_COMPRESSION,
00346                          Z_DEFLATED,
00347                          MAX_WBITS + 16,
00348                          8,
00349                  Z_DEFAULT_STRATEGY);
00350         assert(_zlibErr == Z_OK);
00351 
00352         _stream.next_out = _buf;
00353         _stream.avail_out = BUFSIZE;
00354         _stream.avail_in = 0;
00355         _stream.next_in = nullptr;
00356     }
00357 
00358     ~GZipWriteStream() {
00359         finalize();
00360         deflateEnd(&_stream);
00361     }
00362 
00363     bool err() const {
00364         // CHECKME: does Z_STREAM_END make sense here?
00365         return (_zlibErr != Z_OK && _zlibErr != Z_STREAM_END) || _wrapped->err();
00366     }
00367 
00368     void clearErr() {
00369         // Note: we don't reset the _zlibErr here, as it is not
00370         // clear in general how
00371         _wrapped->clearErr();
00372     }
00373 
00374     void finalize() {
00375         if (_zlibErr != Z_OK)
00376             return;
00377 
00378         // Process whatever remaining data there is.
00379         processData(Z_FINISH);
00380 
00381         // Since processData only writes out blocks of size BUFSIZE,
00382         // we may have to flush some stragglers.
00383         uint remainder = BUFSIZE - _stream.avail_out;
00384         if (remainder > 0) {
00385             if (_wrapped->write(_buf, remainder) != remainder) {
00386                 _zlibErr = Z_ERRNO;
00387             }
00388         }
00389 
00390         // Finalize the wrapped savefile, too
00391         _wrapped->finalize();
00392     }
00393 
00394     uint32 write(const void *dataPtr, uint32 dataSize) {
00395         if (err())
00396             return 0;
00397 
00398         // Hook in the new data ...
00399         // Note: We need to make a const_cast here, as zlib is not aware
00400         // of the const keyword.
00401         _stream.next_in = const_cast<byte *>((const byte *)dataPtr);
00402         _stream.avail_in = dataSize;
00403 
00404         // ... and flush it to disk
00405         processData(Z_NO_FLUSH);
00406 
00407         _pos += dataSize - _stream.avail_in;
00408         return dataSize - _stream.avail_in;
00409     }
00410 
00411     virtual int32 pos() const { return _pos; }
00412 };
00413 
00414 #endif  // USE_ZLIB
00415 
00416 SeekableReadStream *wrapCompressedReadStream(SeekableReadStream *toBeWrapped, uint32 knownSize) {
00417     if (toBeWrapped) {
00418         uint16 header = toBeWrapped->readUint16BE();
00419         bool isCompressed = (header == 0x1F8B ||
00420                      ((header & 0x0F00) == 0x0800 &&
00421                       header % 31 == 0));
00422         toBeWrapped->seek(-2, SEEK_CUR);
00423         if (isCompressed) {
00424 #if defined(USE_ZLIB)
00425             return new GZipReadStream(toBeWrapped, knownSize);
00426 #else
00427             delete toBeWrapped;
00428             return NULL;
00429 #endif
00430         }
00431     }
00432     return toBeWrapped;
00433 }
00434 
00435 WriteStream *wrapCompressedWriteStream(WriteStream *toBeWrapped) {
00436 #if defined(USE_ZLIB)
00437     if (toBeWrapped)
00438         return new GZipWriteStream(toBeWrapped);
00439 #endif
00440     return toBeWrapped;
00441 }
00442 
00443 
00444 } // End of namespace Common


Generated on Sat Feb 16 2019 05:01:12 for ResidualVM by doxygen 1.7.1
curved edge   curved edge