0x47 stub
[scummvm-innocent.git] / common / zlib.cpp
blob519b7c4806fe2dd4c8dfa4ebc20b32eebac52ab7
1 /* ScummVM - Graphic Adventure Engine
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 * $URL$
22 * $Id$
26 #include "common/zlib.h"
27 #include "common/util.h"
29 #if defined(USE_ZLIB)
30 #ifdef __SYMBIAN32__
31 #include <zlib\zlib.h>
32 #else
33 #include <zlib.h>
34 #endif
36 #if ZLIB_VERNUM < 0x1204
37 #error Version 1.2.0.4 or newer of zlib is required for this code
38 #endif
39 #endif
42 namespace Common {
44 #if defined(USE_ZLIB)
46 bool uncompress(byte *dst, unsigned long *dstLen, const byte *src, unsigned long srcLen) {
47 return Z_OK == ::uncompress(dst, dstLen, src, srcLen);
50 /**
51 * A simple wrapper class which can be used to wrap around an arbitrary
52 * other SeekableReadStream and will then provide on-the-fly decompression support.
53 * Assumes the compressed data to be in gzip format.
55 class GZipReadStream : public Common::SeekableReadStream {
56 protected:
57 enum {
58 BUFSIZE = 16384 // 1 << MAX_WBITS
61 byte _buf[BUFSIZE];
63 Common::SeekableReadStream *_wrapped;
64 z_stream _stream;
65 int _zlibErr;
66 uint32 _pos;
67 uint32 _origSize;
68 bool _eos;
70 public:
72 GZipReadStream(Common::SeekableReadStream *w) : _wrapped(w) {
73 assert(w != 0);
75 _stream.zalloc = Z_NULL;
76 _stream.zfree = Z_NULL;
77 _stream.opaque = Z_NULL;
79 // Verify file header is correct
80 w->seek(0, SEEK_SET);
81 uint16 header = w->readUint16BE();
82 assert(header == 0x1F8B ||
83 ((header & 0x0F00) == 0x0800 && header % 31 == 0));
85 if (header == 0x1F8B) {
86 // Retrieve the original file size
87 w->seek(-4, SEEK_END);
88 _origSize = w->readUint32LE();
89 } else {
90 // Original size not available in zlib format
91 _origSize = 0;
93 _pos = 0;
94 w->seek(0, SEEK_SET);
95 _eos = false;
97 // Adding 32 to windowBits indicates to zlib that it is supposed to
98 // automatically detect whether gzip or zlib headers are used for
99 // the compressed file. This feature was added in zlib 1.2.0.4,
100 // released 10 August 2003.
101 // Note: This is *crucial* for savegame compatibility, do *not* remove!
102 _zlibErr = inflateInit2(&_stream, MAX_WBITS + 32);
103 if (_zlibErr != Z_OK)
104 return;
106 // Setup input buffer
107 _stream.next_in = _buf;
108 _stream.avail_in = 0;
111 ~GZipReadStream() {
112 inflateEnd(&_stream);
113 delete _wrapped;
116 bool err() const { return (_zlibErr != Z_OK) && (_zlibErr != Z_STREAM_END); }
117 void clearErr() {
118 // only reset _eos; I/O errors are not recoverable
119 _eos = false;
122 uint32 read(void *dataPtr, uint32 dataSize) {
123 _stream.next_out = (byte *)dataPtr;
124 _stream.avail_out = dataSize;
126 // Keep going while we get no error
127 while (_zlibErr == Z_OK && _stream.avail_out) {
128 if (_stream.avail_in == 0 && !_wrapped->eos()) {
129 // If we are out of input data: Read more data, if available.
130 _stream.next_in = _buf;
131 _stream.avail_in = _wrapped->read(_buf, BUFSIZE);
133 _zlibErr = inflate(&_stream, Z_NO_FLUSH);
136 // Update the position counter
137 _pos += dataSize - _stream.avail_out;
139 if (_zlibErr == Z_STREAM_END && _stream.avail_out > 0)
140 _eos = true;
142 return dataSize - _stream.avail_out;
145 bool eos() const {
146 return _eos;
148 int32 pos() const {
149 return _pos;
151 int32 size() const {
152 return _origSize;
154 bool seek(int32 offset, int whence = SEEK_SET) {
155 int32 newPos = 0;
156 assert(whence != SEEK_END); // SEEK_END not supported
157 switch(whence) {
158 case SEEK_SET:
159 newPos = offset;
160 break;
161 case SEEK_CUR:
162 newPos = _pos + offset;
165 assert(newPos >= 0);
167 if ((uint32)newPos < _pos) {
168 // To search backward, we have to restart the whole decompression
169 // from the start of the file. A rather wasteful operation, best
170 // to avoid it. :/
171 #if DEBUG
172 warning("Backward seeking in GZipReadStream detected");
173 #endif
174 _pos = 0;
175 _wrapped->seek(0, SEEK_SET);
176 _zlibErr = inflateReset(&_stream);
177 if (_zlibErr != Z_OK)
178 return false; // FIXME: STREAM REWRITE
179 _stream.next_in = _buf;
180 _stream.avail_in = 0;
183 offset = newPos - _pos;
185 // Skip the given amount of data (very inefficient if one tries to skip
186 // huge amounts of data, but usually client code will only skip a few
187 // bytes, so this should be fine.
188 byte tmpBuf[1024];
189 while (!err() && offset > 0) {
190 offset -= read(tmpBuf, MIN((int32)sizeof(tmpBuf), offset));
193 _eos = false;
194 return true; // FIXME: STREAM REWRITE
199 * A simple wrapper class which can be used to wrap around an arbitrary
200 * other WriteStream and will then provide on-the-fly compression support.
201 * The compressed data is written in the gzip format.
203 class GZipWriteStream : public Common::WriteStream {
204 protected:
205 enum {
206 BUFSIZE = 16384 // 1 << MAX_WBITS
209 byte _buf[BUFSIZE];
210 Common::WriteStream *_wrapped;
211 z_stream _stream;
212 int _zlibErr;
214 void processData(int flushType) {
215 // This function is called by both write() and finalize().
216 while (_zlibErr == Z_OK && (_stream.avail_in || flushType == Z_FINISH)) {
217 if (_stream.avail_out == 0) {
218 if (_wrapped->write(_buf, BUFSIZE) != BUFSIZE) {
219 _zlibErr = Z_ERRNO;
220 break;
222 _stream.next_out = _buf;
223 _stream.avail_out = BUFSIZE;
225 _zlibErr = deflate(&_stream, flushType);
229 public:
230 GZipWriteStream(Common::WriteStream *w) : _wrapped(w) {
231 assert(w != 0);
232 _stream.zalloc = Z_NULL;
233 _stream.zfree = Z_NULL;
234 _stream.opaque = Z_NULL;
236 // Adding 16 to windowBits indicates to zlib that it is supposed to
237 // write gzip headers. This feature was added in zlib 1.2.0.4,
238 // released 10 August 2003.
239 // Note: This is *crucial* for savegame compatibility, do *not* remove!
240 _zlibErr = deflateInit2(&_stream,
241 Z_DEFAULT_COMPRESSION,
242 Z_DEFLATED,
243 MAX_WBITS + 16,
245 Z_DEFAULT_STRATEGY);
246 assert(_zlibErr == Z_OK);
248 _stream.next_out = _buf;
249 _stream.avail_out = BUFSIZE;
250 _stream.avail_in = 0;
251 _stream.next_in = 0;
254 ~GZipWriteStream() {
255 finalize();
256 deflateEnd(&_stream);
257 delete _wrapped;
260 bool err() const {
261 // CHECKME: does Z_STREAM_END make sense here?
262 return (_zlibErr != Z_OK && _zlibErr != Z_STREAM_END) || _wrapped->err();
265 void clearErr() {
266 // Note: we don't reset the _zlibErr here, as it is not
267 // clear in general how
268 _wrapped->clearErr();
271 void finalize() {
272 if (_zlibErr != Z_OK)
273 return;
275 // Process whatever remaining data there is.
276 processData(Z_FINISH);
278 // Since processData only writes out blocks of size BUFSIZE,
279 // we may have to flush some stragglers.
280 uint remainder = BUFSIZE - _stream.avail_out;
281 if (remainder > 0) {
282 if (_wrapped->write(_buf, remainder) != remainder) {
283 _zlibErr = Z_ERRNO;
287 // Finalize the wrapped savefile, too
288 _wrapped->finalize();
291 uint32 write(const void *dataPtr, uint32 dataSize) {
292 if (err())
293 return 0;
295 // Hook in the new data ...
296 // Note: We need to make a const_cast here, as zlib is not aware
297 // of the const keyword.
298 _stream.next_in = const_cast<byte *>((const byte *)dataPtr);
299 _stream.avail_in = dataSize;
301 // ... and flush it to disk
302 processData(Z_NO_FLUSH);
304 return dataSize - _stream.avail_in;
308 #endif // USE_ZLIB
310 Common::SeekableReadStream *wrapCompressedReadStream(Common::SeekableReadStream *toBeWrapped) {
311 #if defined(USE_ZLIB)
312 if (toBeWrapped) {
313 uint16 header = toBeWrapped->readUint16BE();
314 bool isCompressed = (header == 0x1F8B ||
315 ((header & 0x0F00) == 0x0800 &&
316 header % 31 == 0));
317 toBeWrapped->seek(-2, SEEK_CUR);
318 if (isCompressed)
319 return new GZipReadStream(toBeWrapped);
321 #endif
322 return toBeWrapped;
325 Common::WriteStream *wrapCompressedWriteStream(Common::WriteStream *toBeWrapped) {
326 #if defined(USE_ZLIB)
327 if (toBeWrapped)
328 return new GZipWriteStream(toBeWrapped);
329 #endif
330 return toBeWrapped;
334 } // End of namespace Common