aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--backends/saves/compressed/compressed-saves.cpp288
-rw-r--r--common/gzip-stream.cpp327
-rw-r--r--common/gzip-stream.h58
-rw-r--r--common/module.mk1
4 files changed, 389 insertions, 285 deletions
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 <zlib.h>
-
-#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<byte *>((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);
}
diff --git a/common/gzip-stream.cpp b/common/gzip-stream.cpp
new file mode 100644
index 0000000000..2756fe1b5c
--- /dev/null
+++ b/common/gzip-stream.cpp
@@ -0,0 +1,327 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/gzip-stream.h"
+#include "common/util.h"
+
+#if defined(USE_ZLIB)
+ #include <zlib.h>
+
+ #if ZLIB_VERNUM < 0x1204
+ #error Version 1.2.0.4 or newer of zlib is required for this code
+ #endif
+#endif
+
+
+namespace Common {
+
+
+#if defined(USE_ZLIB)
+
+/**
+ * A simple wrapper class which can be used to wrap around an arbitrary
+ * other SeekableReadStream and will then provide on-the-fly decompression support.
+ * Assumes the compressed data to be in gzip format.
+ */
+class GZipReadStream : public Common::SeekableReadStream {
+protected:
+ enum {
+ BUFSIZE = 16384 // 1 << MAX_WBITS
+ };
+
+ byte _buf[BUFSIZE];
+
+ Common::SeekableReadStream *_wrapped;
+ z_stream _stream;
+ int _zlibErr;
+ uint32 _pos;
+ uint32 _origSize;
+ bool _eos;
+
+public:
+
+ GZipReadStream(Common::SeekableReadStream *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;
+ }
+
+ ~GZipReadStream() {
+ 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 GZipReadStream 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 WriteStream and will then provide on-the-fly compression support.
+ * The compressed data is written in the gzip format.
+ */
+class GZipWriteStream : public Common::WriteStream {
+protected:
+ enum {
+ BUFSIZE = 16384 // 1 << MAX_WBITS
+ };
+
+ byte _buf[BUFSIZE];
+ Common::WriteStream *_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:
+ GZipWriteStream(Common::WriteStream *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;
+ }
+
+ ~GZipWriteStream() {
+ 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<byte *>((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::SeekableReadStream *wrapCompressedReadStream(Common::SeekableReadStream *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 GZipReadStream(toBeWrapped);
+ }
+#endif
+ return toBeWrapped;
+}
+
+Common::WriteStream *wrapCompressedWriteStream(Common::WriteStream *toBeWrapped) {
+#if defined(USE_ZLIB)
+ if (toBeWrapped)
+ return new GZipWriteStream(toBeWrapped);
+#endif
+ return toBeWrapped;
+}
+
+
+} // End of namespace Common
diff --git a/common/gzip-stream.h b/common/gzip-stream.h
new file mode 100644
index 0000000000..557f507044
--- /dev/null
+++ b/common/gzip-stream.h
@@ -0,0 +1,58 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef COMMON_GZIP_STREAM_H
+#define COMMON_GZIP_STREAM_H
+
+#include "common/stream.h"
+
+namespace Common {
+
+/**
+ * Take an arbitrary SeekableReadStream and wrap it in a custom stream which
+ * provides transparent on-the-fly decompression. Assumes the data it
+ * retrieves from the wrapped stream to be either uncompressed or in gzip
+ * format. In the former case, the original stream is returned unmodified
+ * (and in particular, not wrapped).
+ *
+ * It is safe to call this with a NULL parameter (in this case, NULL is
+ * returned).
+ */
+Common::SeekableReadStream *wrapCompressedReadStream(Common::SeekableReadStream *toBeWrapped);
+
+/**
+ * Take an arbitrary WriteStream and wrap it in a custom stream which provides
+ * transparent on-the-fly compression. The compressed data is written in the
+ * gzip format, unless ZLIB support has been disabled, in which case the given
+ * stream is returned unmodified (and in particular, not wrapped).
+ *
+ * It is safe to call this with a NULL parameter (in this case, NULL is
+ * returned).
+ */
+Common::WriteStream *wrapCompressedWriteStream(Common::WriteStream *toBeWrapped);
+
+} // End of namespace Common
+
+#endif
diff --git a/common/module.mk b/common/module.mk
index 599ffcf8a6..45a915a613 100644
--- a/common/module.mk
+++ b/common/module.mk
@@ -7,6 +7,7 @@ MODULE_OBJS := \
config-manager.o \
file.o \
fs.o \
+ gzip-stream.o \
hashmap.o \
memorypool.o \
md5.o \