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.
26 #include "common/zlib.h"
27 #include "common/util.h"
31 #include <zlib\zlib.h>
36 #if ZLIB_VERNUM < 0x1204
37 #error Version 1.2.0.4 or newer of zlib is required for this code
46 bool uncompress(byte
*dst
, unsigned long *dstLen
, const byte
*src
, unsigned long srcLen
) {
47 return Z_OK
== ::uncompress(dst
, dstLen
, src
, srcLen
);
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
{
58 BUFSIZE
= 16384 // 1 << MAX_WBITS
63 Common::SeekableReadStream
*_wrapped
;
72 GZipReadStream(Common::SeekableReadStream
*w
) : _wrapped(w
) {
75 _stream
.zalloc
= Z_NULL
;
76 _stream
.zfree
= Z_NULL
;
77 _stream
.opaque
= Z_NULL
;
79 // Verify file header is correct
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();
90 // Original size not available in zlib format
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
)
106 // Setup input buffer
107 _stream
.next_in
= _buf
;
108 _stream
.avail_in
= 0;
112 inflateEnd(&_stream
);
116 bool err() const { return (_zlibErr
!= Z_OK
) && (_zlibErr
!= Z_STREAM_END
); }
118 // only reset _eos; I/O errors are not recoverable
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)
142 return dataSize
- _stream
.avail_out
;
154 bool seek(int32 offset
, int whence
= SEEK_SET
) {
156 assert(whence
!= SEEK_END
); // SEEK_END not supported
162 newPos
= _pos
+ offset
;
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
172 warning("Backward seeking in GZipReadStream detected");
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.
189 while (!err() && offset
> 0) {
190 offset
-= read(tmpBuf
, MIN((int32
)sizeof(tmpBuf
), offset
));
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
{
206 BUFSIZE
= 16384 // 1 << MAX_WBITS
210 Common::WriteStream
*_wrapped
;
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
) {
222 _stream
.next_out
= _buf
;
223 _stream
.avail_out
= BUFSIZE
;
225 _zlibErr
= deflate(&_stream
, flushType
);
230 GZipWriteStream(Common::WriteStream
*w
) : _wrapped(w
) {
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
,
246 assert(_zlibErr
== Z_OK
);
248 _stream
.next_out
= _buf
;
249 _stream
.avail_out
= BUFSIZE
;
250 _stream
.avail_in
= 0;
256 deflateEnd(&_stream
);
261 // CHECKME: does Z_STREAM_END make sense here?
262 return (_zlibErr
!= Z_OK
&& _zlibErr
!= Z_STREAM_END
) || _wrapped
->err();
266 // Note: we don't reset the _zlibErr here, as it is not
267 // clear in general how
268 _wrapped
->clearErr();
272 if (_zlibErr
!= Z_OK
)
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
;
282 if (_wrapped
->write(_buf
, remainder
) != remainder
) {
287 // Finalize the wrapped savefile, too
288 _wrapped
->finalize();
291 uint32
write(const void *dataPtr
, uint32 dataSize
) {
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
;
310 Common::SeekableReadStream
*wrapCompressedReadStream(Common::SeekableReadStream
*toBeWrapped
) {
311 #if defined(USE_ZLIB)
313 uint16 header
= toBeWrapped
->readUint16BE();
314 bool isCompressed
= (header
== 0x1F8B ||
315 ((header
& 0x0F00) == 0x0800 &&
317 toBeWrapped
->seek(-2, SEEK_CUR
);
319 return new GZipReadStream(toBeWrapped
);
325 Common::WriteStream
*wrapCompressedWriteStream(Common::WriteStream
*toBeWrapped
) {
326 #if defined(USE_ZLIB)
328 return new GZipWriteStream(toBeWrapped
);
334 } // End of namespace Common