From 7df6071a7715f141e208671ef9d2cd16a7e7a37d Mon Sep 17 00:00:00 2001 From: Max Horn Date: Mon, 15 Sep 2008 21:55:50 +0000 Subject: Turned compressed savefile wrapper into a generic stream wrapper svn-id: r34566 --- backends/saves/compressed/compressed-saves.cpp | 288 +------------------------ 1 file changed, 3 insertions(+), 285 deletions(-) (limited to 'backends') diff --git a/backends/saves/compressed/compressed-saves.cpp b/backends/saves/compressed/compressed-saves.cpp index 5596bf96a8..d4601f77c9 100644 --- a/backends/saves/compressed/compressed-saves.cpp +++ b/backends/saves/compressed/compressed-saves.cpp @@ -23,296 +23,14 @@ * */ -#include "common/savefile.h" -#include "common/util.h" +#include "common/gzip-stream.h" #include "backends/saves/compressed/compressed-saves.h" -#if defined(USE_ZLIB) -#include - -#if ZLIB_VERNUM < 0x1204 -#error Version 1.2.0.4 or newer of zlib is required for this code -#endif - -/** - * A simple wrapper class which can be used to wrap around an arbitrary - * other InSaveFile and will then provide on-the-fly decompression support. - * Assumes the compressed data to be in gzip format. - */ -class CompressedInSaveFile : public Common::InSaveFile { -protected: - enum { - BUFSIZE = 16384 // 1 << MAX_WBITS - }; - - byte _buf[BUFSIZE]; - - Common::InSaveFile *_wrapped; - z_stream _stream; - int _zlibErr; - uint32 _pos; - uint32 _origSize; - bool _eos; - -public: - - CompressedInSaveFile(Common::InSaveFile *w) : _wrapped(w) { - assert(w != 0); - - _stream.zalloc = Z_NULL; - _stream.zfree = Z_NULL; - _stream.opaque = Z_NULL; - - // Verify file header is correct - w->seek(0, SEEK_SET); - uint16 header = w->readUint16BE(); - assert(header == 0x1F8B || - ((header & 0x0F00) == 0x0800 && header % 31 == 0)); - - if (header == 0x1F8B) { - // Retrieve the original file size - w->seek(-4, SEEK_END); - _origSize = w->readUint32LE(); - } else { - // Original size not available in zlib format - _origSize = 0; - } - _pos = 0; - w->seek(0, SEEK_SET); - _eos = false; - - // Adding 32 to windowBits indicates to zlib that it is supposed to - // automatically detect whether gzip or zlib headers are used for - // the compressed file. This feature was added in zlib 1.2.0.4, - // released 10 August 2003. - // Note: This is *crucial* for savegame compatibility, do *not* remove! - _zlibErr = inflateInit2(&_stream, MAX_WBITS + 32); - if (_zlibErr != Z_OK) - return; - - // Setup input buffer - _stream.next_in = _buf; - _stream.avail_in = 0; - } - - ~CompressedInSaveFile() { - inflateEnd(&_stream); - delete _wrapped; - } - - bool err() const { return (_zlibErr != Z_OK) && (_zlibErr != Z_STREAM_END); } - void clearErr() { - // only reset _eos; I/O errors are not recoverable - _eos = false; - } - - uint32 read(void *dataPtr, uint32 dataSize) { - _stream.next_out = (byte *)dataPtr; - _stream.avail_out = dataSize; - - // Keep going while we get no error - while (_zlibErr == Z_OK && _stream.avail_out) { - if (_stream.avail_in == 0 && !_wrapped->eos()) { - // If we are out of input data: Read more data, if available. - _stream.next_in = _buf; - _stream.avail_in = _wrapped->read(_buf, BUFSIZE); - } - _zlibErr = inflate(&_stream, Z_NO_FLUSH); - } - - // Update the position counter - _pos += dataSize - _stream.avail_out; - - if (_zlibErr == Z_STREAM_END && _stream.avail_out > 0) - _eos = true; - - return dataSize - _stream.avail_out; - } - - bool eos() const { - return _eos; - } - int32 pos() const { - return _pos; - } - int32 size() const { - return _origSize; - } - bool seek(int32 offset, int whence = SEEK_SET) { - int32 newPos = 0; - assert(whence != SEEK_END); // SEEK_END not supported - switch(whence) { - case SEEK_SET: - newPos = offset; - break; - case SEEK_CUR: - newPos = _pos + offset; - } - - assert(newPos >= 0); - - if ((uint32)newPos < _pos) { - // To search backward, we have to restart the whole decompression - // from the start of the file. A rather wasteful operation, best - // to avoid it. :/ -#if DEBUG - warning("Backward seeking in CompressedInSaveFile detected"); -#endif - _pos = 0; - _wrapped->seek(0, SEEK_SET); - _zlibErr = inflateReset(&_stream); - if (_zlibErr != Z_OK) - return false; // FIXME: STREAM REWRITE - _stream.next_in = _buf; - _stream.avail_in = 0; - } - - offset = newPos - _pos; - - // Skip the given amount of data (very inefficient if one tries to skip - // huge amounts of data, but usually client code will only skip a few - // bytes, so this should be fine. - byte tmpBuf[1024]; - while (!err() && offset > 0) { - offset -= read(tmpBuf, MIN((int32)sizeof(tmpBuf), offset)); - } - - _eos = false; - return true; // FIXME: STREAM REWRITE - } -}; - -/** - * A simple wrapper class which can be used to wrap around an arbitrary - * other OutSaveFile and will then provide on-the-fly compression support. - * The compressed data is written in the gzip format. - */ -class CompressedOutSaveFile : public Common::OutSaveFile { -protected: - enum { - BUFSIZE = 16384 // 1 << MAX_WBITS - }; - - byte _buf[BUFSIZE]; - Common::OutSaveFile *_wrapped; - z_stream _stream; - int _zlibErr; - - void processData(int flushType) { - // This function is called by both write() and finalize(). - while (_zlibErr == Z_OK && (_stream.avail_in || flushType == Z_FINISH)) { - if (_stream.avail_out == 0) { - if (_wrapped->write(_buf, BUFSIZE) != BUFSIZE) { - _zlibErr = Z_ERRNO; - break; - } - _stream.next_out = _buf; - _stream.avail_out = BUFSIZE; - } - _zlibErr = deflate(&_stream, flushType); - } - } - -public: - CompressedOutSaveFile(Common::OutSaveFile *w) : _wrapped(w) { - assert(w != 0); - _stream.zalloc = Z_NULL; - _stream.zfree = Z_NULL; - _stream.opaque = Z_NULL; - - // Adding 16 to windowBits indicates to zlib that it is supposed to - // write gzip headers. This feature was added in zlib 1.2.0.4, - // released 10 August 2003. - // Note: This is *crucial* for savegame compatibility, do *not* remove! - _zlibErr = deflateInit2(&_stream, - Z_DEFAULT_COMPRESSION, - Z_DEFLATED, - MAX_WBITS + 16, - 8, - Z_DEFAULT_STRATEGY); - assert(_zlibErr == Z_OK); - - _stream.next_out = _buf; - _stream.avail_out = BUFSIZE; - _stream.avail_in = 0; - _stream.next_in = 0; - } - - ~CompressedOutSaveFile() { - finalize(); - deflateEnd(&_stream); - delete _wrapped; - } - - bool err() const { - // CHECKME: does Z_STREAM_END make sense here? - return (_zlibErr != Z_OK && _zlibErr != Z_STREAM_END) || _wrapped->err(); - } - - void clearErr() { - // Note: we don't reset the _zlibErr here, as it is not - // clear in general how - _wrapped->clearErr(); - } - - void finalize() { - if (_zlibErr != Z_OK) - return; - - // Process whatever remaining data there is. - processData(Z_FINISH); - - // Since processData only writes out blocks of size BUFSIZE, - // we may have to flush some stragglers. - uint remainder = BUFSIZE - _stream.avail_out; - if (remainder > 0) { - if (_wrapped->write(_buf, remainder) != remainder) { - _zlibErr = Z_ERRNO; - } - } - - // Finalize the wrapped savefile, too - _wrapped->finalize(); - } - - uint32 write(const void *dataPtr, uint32 dataSize) { - if (err()) - return 0; - - // Hook in the new data ... - // Note: We need to make a const_cast here, as zlib is not aware - // of the const keyword. - _stream.next_in = const_cast((const byte *)dataPtr); - _stream.avail_in = dataSize; - - // ... and flush it to disk - processData(Z_NO_FLUSH); - - return dataSize - _stream.avail_in; - } -}; - -#endif // USE_ZLIB Common::InSaveFile *wrapInSaveFile(Common::InSaveFile *toBeWrapped) { -#if defined(USE_ZLIB) - if (toBeWrapped) { - uint16 header = toBeWrapped->readUint16BE(); - bool isCompressed = (header == 0x1F8B || - ((header & 0x0F00) == 0x0800 && - header % 31 == 0)); - toBeWrapped->seek(-2, SEEK_CUR); - if (isCompressed) - return new CompressedInSaveFile(toBeWrapped); - } -#endif - return toBeWrapped; + return Common::wrapCompressedReadStream(toBeWrapped); } Common::OutSaveFile *wrapOutSaveFile(Common::OutSaveFile *toBeWrapped) { -#if defined(USE_ZLIB) - if (toBeWrapped) - return new CompressedOutSaveFile(toBeWrapped); -#endif - return toBeWrapped; + return Common::wrapCompressedWriteStream(toBeWrapped); } -- cgit v1.2.3