1 /* ScummVM - Graphic Adventure Engine
2 *
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.
6 *
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.
11 *
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.
16 *
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.
20 *
21 */
22
23 // Disable symbol overrides so that we can use zlib.h
24 #define FORBIDDEN_SYMBOL_ALLOW_ALL
25
26 #include "common/zlib.h"
27 #include "common/ptr.h"
28 #include "common/util.h"
29 #include "common/stream.h"
30 #include "common/debug.h"
31 #include "common/textconsole.h"
32
33 #if defined(USE_ZLIB)
34 #ifdef __SYMBIAN32__
35 #include <zlib\zlib.h>
36 #elif defined(__MORPHOS__)
37 #define _NO_PPCINLINE
38 #include <zlib.h>
39 #undef _NO_PPCINLINE
40 #else
41 #include <zlib.h>
42 #endif
43
44 #if ZLIB_VERNUM < 0x1204
45 #error Version 1.2.0.4 or newer of zlib is required for this code
46 #endif
47 #endif
48
49
50 namespace Common {
51
52 #if defined(USE_ZLIB)
53
uncompress(byte * dst,unsigned long * dstLen,const byte * src,unsigned long srcLen)54 bool uncompress(byte *dst, unsigned long *dstLen, const byte *src, unsigned long srcLen) {
55 return Z_OK == ::uncompress(dst, dstLen, src, srcLen);
56 }
57
inflateZlibHeaderless(byte * dst,uint dstLen,const byte * src,uint srcLen,const byte * dict,uint dictLen)58 bool inflateZlibHeaderless(byte *dst, uint dstLen, const byte *src, uint srcLen, const byte *dict, uint dictLen) {
59 if (!dst || !dstLen || !src || !srcLen)
60 return false;
61
62 // Initialize zlib
63 z_stream stream;
64 stream.next_in = const_cast<byte *>(src);
65 stream.avail_in = srcLen;
66 stream.next_out = dst;
67 stream.avail_out = dstLen;
68 stream.zalloc = Z_NULL;
69 stream.zfree = Z_NULL;
70 stream.opaque = Z_NULL;
71
72 // Negative MAX_WBITS tells zlib there's no zlib header
73 int err = inflateInit2(&stream, -MAX_WBITS);
74 if (err != Z_OK)
75 return false;
76
77 // Set the dictionary, if provided
78 if (dict != nullptr) {
79 err = inflateSetDictionary(&stream, const_cast<byte *>(dict), dictLen);
80 if (err != Z_OK)
81 return false;
82 }
83
84 err = inflate(&stream, Z_SYNC_FLUSH);
85 if (err != Z_OK && err != Z_STREAM_END) {
86 inflateEnd(&stream);
87 return false;
88 }
89
90 inflateEnd(&stream);
91 return true;
92 }
93
94 enum {
95 kTempBufSize = 65536
96 };
97
inflateZlibInstallShield(byte * dst,uint dstLen,const byte * src,uint srcLen)98 bool inflateZlibInstallShield(byte *dst, uint dstLen, const byte *src, uint srcLen) {
99 if (!dst || !dstLen || !src || !srcLen)
100 return false;
101
102 // See if we have sync bytes. If so, just use our function for that.
103 if (srcLen >= 4 && READ_BE_UINT32(src + srcLen - 4) == 0xFFFF)
104 return inflateZlibHeaderless(dst, dstLen, src, srcLen);
105
106 // Otherwise, we have some custom code we get to use here.
107
108 byte *temp = (byte *)malloc(kTempBufSize);
109
110 uint32 bytesRead = 0, bytesProcessed = 0;
111 while (bytesRead < srcLen) {
112 uint16 chunkSize = READ_LE_UINT16(src + bytesRead);
113 bytesRead += 2;
114
115 // Initialize zlib
116 z_stream stream;
117 stream.next_in = const_cast<byte *>(src + bytesRead);
118 stream.avail_in = chunkSize;
119 stream.next_out = temp;
120 stream.avail_out = kTempBufSize;
121 stream.zalloc = Z_NULL;
122 stream.zfree = Z_NULL;
123 stream.opaque = Z_NULL;
124
125 // Negative MAX_WBITS tells zlib there's no zlib header
126 int err = inflateInit2(&stream, -MAX_WBITS);
127 if (err != Z_OK)
128 return false;
129
130 err = inflate(&stream, Z_FINISH);
131 if (err != Z_OK && err != Z_STREAM_END) {
132 inflateEnd(&stream);
133 free(temp);
134 return false;
135 }
136
137 memcpy(dst + bytesProcessed, temp, stream.total_out);
138 bytesProcessed += stream.total_out;
139
140 inflateEnd(&stream);
141 bytesRead += chunkSize;
142 }
143
144 free(temp);
145 return true;
146 }
147
inflateZlibHeaderless(Common::WriteStream * dst,Common::SeekableReadStream * src)148 bool inflateZlibHeaderless(Common::WriteStream *dst, Common::SeekableReadStream *src) {
149 byte *inBuffer, *outBuffer;
150 z_stream stream;
151 int status;
152
153 // Allocate buffers
154 inBuffer = new byte[kTempBufSize];
155 outBuffer = new byte[kTempBufSize];
156
157 /* Initialize Zlib inflation functions. */
158 stream.next_out = outBuffer;
159 stream.avail_out = kTempBufSize;
160 stream.next_in = inBuffer;
161 stream.avail_in = 0;
162
163 stream.zalloc = Z_NULL;
164 stream.zfree = Z_NULL;
165 stream.opaque = Z_NULL;
166
167 status = inflateInit(&stream);
168 if (status != Z_OK) {
169 delete[] inBuffer;
170 delete[] outBuffer;
171 return false;
172 }
173
174 // Inflate the input buffers. */
175 for (;;) {
176 int inBytes, outBytes;
177
178 /* If the input buffer is empty, try to obtain more data. */
179 if (stream.avail_in == 0) {
180 inBytes = src->read(inBuffer, kTempBufSize);
181 stream.next_in = inBuffer;
182 stream.avail_in = inBytes;
183 }
184
185 // Decompress as much stream data as we can. */
186 status = inflate(&stream, Z_SYNC_FLUSH);
187 if (status != Z_STREAM_END && status != Z_OK) {
188 delete[] inBuffer;
189 delete[] outBuffer;
190 return false;
191 }
192 outBytes = kTempBufSize - stream.avail_out;
193
194 // See if decompressed data is available. */
195 if (outBytes > 0) {
196 // Add data from the buffer to the output
197 int consumed = dst->write(outBuffer, outBytes);
198
199 // Move unused buffer data to buffer start
200 memmove(outBuffer, outBuffer + consumed, kTempBufSize - consumed);
201
202 // Reset inflation stream for available space
203 stream.next_out = outBuffer + outBytes - consumed;
204 stream.avail_out += consumed;
205 }
206
207 // If at inflation stream end and output is empty, leave loop
208 if (status == Z_STREAM_END && stream.avail_out == kTempBufSize)
209 break;
210 }
211
212 // End inflation buffers
213 status = inflateEnd(&stream);
214 delete[] inBuffer;
215 delete[] outBuffer;
216
217 // Return result
218 return (status == Z_OK);
219 }
220
221 #ifndef RELEASE_BUILD
222 static bool _shownBackwardSeekingWarning = false;
223 #endif
224
225 /**
226 * A simple wrapper class which can be used to wrap around an arbitrary
227 * other SeekableReadStream and will then provide on-the-fly decompression support.
228 * Assumes the compressed data to be in gzip format.
229 */
230 class GZipReadStream : public SeekableReadStream {
231 protected:
232 enum {
233 BUFSIZE = 16384 // 1 << MAX_WBITS
234 };
235
236 byte _buf[BUFSIZE];
237
238 ScopedPtr<SeekableReadStream> _wrapped;
239 z_stream _stream;
240 int _zlibErr;
241 uint32 _pos;
242 uint32 _origSize;
243 bool _eos;
244
245 public:
246
GZipReadStream(SeekableReadStream * w,uint32 knownSize=0)247 GZipReadStream(SeekableReadStream *w, uint32 knownSize = 0) : _wrapped(w), _stream() {
248 assert(w != nullptr);
249
250 // Verify file header is correct
251 w->seek(0, SEEK_SET);
252 uint16 header = w->readUint16BE();
253 assert(header == 0x1F8B ||
254 ((header & 0x0F00) == 0x0800 && header % 31 == 0));
255
256 if (header == 0x1F8B) {
257 // Retrieve the original file size
258 w->seek(-4, SEEK_END);
259 _origSize = w->readUint32LE();
260 } else {
261 // Original size not available in zlib format
262 // use an otherwise known size if supplied.
263 _origSize = knownSize;
264 }
265 _pos = 0;
266 w->seek(0, SEEK_SET);
267 _eos = false;
268
269 // Adding 32 to windowBits indicates to zlib that it is supposed to
270 // automatically detect whether gzip or zlib headers are used for
271 // the compressed file. This feature was added in zlib 1.2.0.4,
272 // released 10 August 2003.
273 // Note: This is *crucial* for savegame compatibility, do *not* remove!
274 _zlibErr = inflateInit2(&_stream, MAX_WBITS + 32);
275 if (_zlibErr != Z_OK)
276 return;
277
278 // Setup input buffer
279 _stream.next_in = _buf;
280 _stream.avail_in = 0;
281 }
282
~GZipReadStream()283 ~GZipReadStream() {
284 inflateEnd(&_stream);
285 }
286
err() const287 bool err() const { return (_zlibErr != Z_OK) && (_zlibErr != Z_STREAM_END); }
clearErr()288 void clearErr() {
289 // only reset _eos; I/O errors are not recoverable
290 _eos = false;
291 }
292
read(void * dataPtr,uint32 dataSize)293 uint32 read(void *dataPtr, uint32 dataSize) {
294 _stream.next_out = (byte *)dataPtr;
295 _stream.avail_out = dataSize;
296
297 // Keep going while we get no error
298 while (_zlibErr == Z_OK && _stream.avail_out) {
299 if (_stream.avail_in == 0 && !_wrapped->eos()) {
300 // If we are out of input data: Read more data, if available.
301 _stream.next_in = _buf;
302 _stream.avail_in = _wrapped->read(_buf, BUFSIZE);
303 }
304 _zlibErr = inflate(&_stream, Z_NO_FLUSH);
305 }
306
307 // Update the position counter
308 _pos += dataSize - _stream.avail_out;
309
310 if (_zlibErr == Z_STREAM_END && _stream.avail_out > 0)
311 _eos = true;
312
313 return dataSize - _stream.avail_out;
314 }
315
eos() const316 bool eos() const {
317 return _eos;
318 }
pos() const319 int64 pos() const {
320 return _pos;
321 }
size() const322 int64 size() const {
323 return _origSize;
324 }
seek(int64 offset,int whence=SEEK_SET)325 bool seek(int64 offset, int whence = SEEK_SET) {
326 int32 newPos = 0;
327 switch (whence) {
328 default:
329 // fallthrough intended
330 case SEEK_SET:
331 newPos = offset;
332 break;
333 case SEEK_CUR:
334 newPos = _pos + offset;
335 break;
336 case SEEK_END:
337 // NOTE: This can be an expensive operation (see below).
338 newPos = size() + offset;
339 break;
340 }
341
342 assert(newPos >= 0);
343
344 if ((uint32)newPos < _pos) {
345 // To search backward, we have to restart the whole decompression
346 // from the start of the file. A rather wasteful operation, best
347 // to avoid it. :/
348
349 #ifndef RELEASE_BUILD
350 if (!_shownBackwardSeekingWarning) {
351 // We only throw this warning once per stream, to avoid
352 // getting the console swarmed with warnings when consecutive
353 // seeks are made.
354 debug(1, "Backward seeking in GZipReadStream detected");
355 _shownBackwardSeekingWarning = true;
356 }
357 #endif
358
359 _pos = 0;
360 _wrapped->seek(0, SEEK_SET);
361 _zlibErr = inflateReset(&_stream);
362 if (_zlibErr != Z_OK)
363 return false; // FIXME: STREAM REWRITE
364 _stream.next_in = _buf;
365 _stream.avail_in = 0;
366 }
367
368 offset = newPos - _pos;
369
370 // Skip the given amount of data (very inefficient if one tries to skip
371 // huge amounts of data, but usually client code will only skip a few
372 // bytes, so this should be fine.
373 byte tmpBuf[1024];
374 while (!err() && offset > 0) {
375 offset -= read(tmpBuf, MIN((int64)sizeof(tmpBuf), offset));
376 }
377
378 _eos = false;
379 return true; // FIXME: STREAM REWRITE
380 }
381 };
382
383 /**
384 * A simple wrapper class which can be used to wrap around an arbitrary
385 * other WriteStream and will then provide on-the-fly compression support.
386 * The compressed data is written in the gzip format.
387 */
388 class GZipWriteStream : public WriteStream {
389 protected:
390 enum {
391 BUFSIZE = 16384 // 1 << MAX_WBITS
392 };
393
394 byte _buf[BUFSIZE];
395 ScopedPtr<WriteStream> _wrapped;
396 z_stream _stream;
397 int _zlibErr;
398 uint32 _pos;
399
processData(int flushType)400 void processData(int flushType) {
401 // This function is called by both write() and finalize().
402 while (_zlibErr == Z_OK && (_stream.avail_in || flushType == Z_FINISH)) {
403 if (_stream.avail_out == 0) {
404 if (_wrapped->write(_buf, BUFSIZE) != BUFSIZE) {
405 _zlibErr = Z_ERRNO;
406 break;
407 }
408 _stream.next_out = _buf;
409 _stream.avail_out = BUFSIZE;
410 }
411 _zlibErr = deflate(&_stream, flushType);
412 }
413 }
414
415 public:
GZipWriteStream(WriteStream * w)416 GZipWriteStream(WriteStream *w) : _wrapped(w), _stream(), _pos(0) {
417 assert(w != nullptr);
418
419 // Adding 16 to windowBits indicates to zlib that it is supposed to
420 // write gzip headers. This feature was added in zlib 1.2.0.4,
421 // released 10 August 2003.
422 // Note: This is *crucial* for savegame compatibility, do *not* remove!
423 _zlibErr = deflateInit2(&_stream,
424 Z_DEFAULT_COMPRESSION,
425 Z_DEFLATED,
426 MAX_WBITS + 16,
427 8,
428 Z_DEFAULT_STRATEGY);
429 assert(_zlibErr == Z_OK);
430
431 _stream.next_out = _buf;
432 _stream.avail_out = BUFSIZE;
433 _stream.avail_in = 0;
434 _stream.next_in = nullptr;
435 }
436
~GZipWriteStream()437 ~GZipWriteStream() {
438 finalize();
439 deflateEnd(&_stream);
440 }
441
err() const442 bool err() const {
443 // CHECKME: does Z_STREAM_END make sense here?
444 return (_zlibErr != Z_OK && _zlibErr != Z_STREAM_END) || _wrapped->err();
445 }
446
clearErr()447 void clearErr() {
448 // Note: we don't reset the _zlibErr here, as it is not
449 // clear in general how
450 _wrapped->clearErr();
451 }
452
finalize()453 void finalize() {
454 if (_zlibErr != Z_OK)
455 return;
456
457 // Process whatever remaining data there is.
458 processData(Z_FINISH);
459
460 // Since processData only writes out blocks of size BUFSIZE,
461 // we may have to flush some stragglers.
462 uint remainder = BUFSIZE - _stream.avail_out;
463 if (remainder > 0) {
464 if (_wrapped->write(_buf, remainder) != remainder) {
465 _zlibErr = Z_ERRNO;
466 }
467 }
468
469 // Finalize the wrapped savefile, too
470 _wrapped->finalize();
471 }
472
write(const void * dataPtr,uint32 dataSize)473 uint32 write(const void *dataPtr, uint32 dataSize) {
474 if (err())
475 return 0;
476
477 // Hook in the new data ...
478 // Note: We need to make a const_cast here, as zlib is not aware
479 // of the const keyword.
480 _stream.next_in = const_cast<byte *>((const byte *)dataPtr);
481 _stream.avail_in = dataSize;
482
483 // ... and flush it to disk
484 processData(Z_NO_FLUSH);
485
486 _pos += dataSize - _stream.avail_in;
487 return dataSize - _stream.avail_in;
488 }
489
pos() const490 virtual int64 pos() const { return _pos; }
491 };
492
493 #endif // USE_ZLIB
494
wrapCompressedReadStream(SeekableReadStream * toBeWrapped,uint32 knownSize)495 SeekableReadStream *wrapCompressedReadStream(SeekableReadStream *toBeWrapped, uint32 knownSize) {
496 if (toBeWrapped) {
497 if (toBeWrapped->eos() || toBeWrapped->err() || toBeWrapped->size() < 2) {
498 delete toBeWrapped;
499 return nullptr;
500 }
501 uint16 header = toBeWrapped->readUint16BE();
502 bool isCompressed = (header == 0x1F8B ||
503 ((header & 0x0F00) == 0x0800 &&
504 header % 31 == 0));
505 toBeWrapped->seek(-2, SEEK_CUR);
506 if (isCompressed) {
507 #if defined(USE_ZLIB)
508 return new GZipReadStream(toBeWrapped, knownSize);
509 #else
510 delete toBeWrapped;
511 return nullptr;
512 #endif
513 }
514 }
515 return toBeWrapped;
516 }
517
wrapCompressedWriteStream(WriteStream * toBeWrapped)518 WriteStream *wrapCompressedWriteStream(WriteStream *toBeWrapped) {
519 #if defined(USE_ZLIB)
520 if (toBeWrapped)
521 return new GZipWriteStream(toBeWrapped);
522 #endif
523 return toBeWrapped;
524 }
525
526
527 } // End of namespace Common
528