diff options
-rw-r--r-- | README | 53 | ||||
-rw-r--r-- | common/zlib.h | 2 | ||||
-rwxr-xr-x | configure | 5 | ||||
-rw-r--r-- | devtools/create_titanic/create_titanic_dat.cpp | 58 | ||||
-rw-r--r-- | devtools/create_titanic/module.mk | 10 | ||||
-rw-r--r-- | devtools/create_titanic/zlib.cpp | 456 | ||||
-rw-r--r-- | devtools/create_titanic/zlib.h | 136 | ||||
-rw-r--r-- | dists/engine-data/titanic.dat | bin | 3830288 -> 2792935 bytes | |||
-rw-r--r-- | engines/bladerunner/vqa_decoder.cpp | 4 | ||||
-rw-r--r-- | engines/titanic/configure.engine | 2 | ||||
-rw-r--r-- | engines/titanic/core/game_object.cpp | 2 | ||||
-rw-r--r-- | engines/titanic/npcs/bilge_succubus.cpp | 2 | ||||
-rw-r--r-- | engines/titanic/npcs/succubus.cpp | 24 | ||||
-rw-r--r-- | engines/titanic/npcs/succubus.h | 2 | ||||
-rw-r--r-- | engines/titanic/support/files_manager.cpp | 20 | ||||
-rw-r--r-- | engines/titanic/support/files_manager.h | 9 | ||||
-rw-r--r-- | engines/titanic/support/simple_file.cpp | 4 | ||||
-rw-r--r-- | rules.mk | 7 |
18 files changed, 745 insertions, 51 deletions
@@ -383,6 +383,7 @@ Other Games: Nippon Safes Inc. [nippon] Rex Nebular and the Cosmic Gender Bender [nebular] Ringworld: Revenge Of The Patriarch [ringworld] + Riven: The Sequel to Myst [riven] Return to Ringworld [ringworld2] Sfinx [sfinx] Soltys [soltys] @@ -909,8 +910,15 @@ that direction. The player can then simply click on the edges of the game's screen to change location, similar to many adventure games, which is simpler and more straightforward than moving around using the menu. +3.15) Myst game notes: +----- ------------------------------- +Left Click: Move/action +Space: Pause the game +Esc: Skip cutscene +F5: Menu + -3.15) Nippon Safes Inc. Amiga notes: +3.16) Nippon Safes Inc. Amiga notes: ----- ------------------------------ For this game, you will need disk0, , global.table, pointer and it (en, fr, ge for the international version). @@ -919,13 +927,24 @@ In addition, you will need to rename disk image 2 to disk1, disk image 3 to disk2, disk image 4 to disk3 and disk image 5 to disk4. -3.16) Simon the Sorcerer games notes: +3.17) Riven game notes: +----- ------------------------------- +Left Click: Move/action +Arrow Keys: Movement +Page Up: Look up +Page Down: Look down +Space: Pause the game +Esc: Skip cutscene +F5: Menu + + +3.18) Simon the Sorcerer games notes: ----- ------------------------------- If you have the dual version of Simon the Sorcerer 1 or 2 on CD, you will find the Windows version in the main directory of the CD and the DOS version in the DOS directory of the CD. -3.17) Starship Titanic game notes: +3.19) Starship Titanic game notes: ---------------------------------- Basic Movements: Left Click: Move action @@ -958,7 +977,7 @@ Miscellaneous controls: Ctrl + C: Open up the developer's cheat room Ctrl + D: Open up the ScummVM Debugger -3.18) The Curse of Monkey Island notes: +3.20) The Curse of Monkey Island notes: ----- --------------------------------- For this game, you will need the comi.la0, comi.la1 and comi.la2 files. The comi.la0 file can be found on either CD, but since they are @@ -970,7 +989,7 @@ two CDs. Some of the files appear on both CDs, but again they're identical. -3.19) The Feeble Files notes: +3.21) The Feeble Files notes: ----- ----------------------- Amiga/Macintosh: You need to install a small pack of cutscenes that are missing in both @@ -997,7 +1016,7 @@ Rename voices.wav on CD3 to voices3.wav Rename voices.wav on CD4 to voices4.wav -3.20) The Legend of Kyrandia notes: +3.22) The Legend of Kyrandia notes: ----- ----------------------------- To run The Legend of Kyrandia under ScummVM you need the 'kyra.dat' file. The file should always be included in official ScummVM packages. @@ -1008,14 +1027,14 @@ thus you only need to grab it in case ScummVM complains about the file being missing. -3.21) Troll's Tale notes: +3.23) Troll's Tale notes: ----- ------------------- The original game came in a PC booter disk, therefore it is necessary to dump the contents of that disk in an image file and name it "troll.img" to be able to play the game under ScummVM. -3.22) Winnie the Pooh notes: +3.24) Winnie the Pooh notes: ----- ---------------------- It is possible to import saved games from the original interpreter of the game into ScummVM. @@ -1030,7 +1049,7 @@ game's screen to change location, similar to many adventure games, which is simpler and more straightforward than moving around using the menu. -3.23) Sierra AGI games: Predictive Input Dialog: +3.25) Sierra AGI games: Predictive Input Dialog: ----- ------------------------------------------ The Predictive Input Dialog is a ScummVM aid for running AGI engine games (which notoriously require command line input) on devices with @@ -1084,7 +1103,7 @@ naturally mapping the functionality to the numeric keypad. Also, the dialog's buttons can be navigated with the arrow and the enter keys. -3.24) Sierra SCI games: Simultaneous speech and subtitles: +3.26) Sierra SCI games: Simultaneous speech and subtitles: ----- ---------------------------------------------------- Certain CD versions of Sierra SCI games had both speech and text resources. Some have an option to toggle between the two, but there are @@ -1173,7 +1192,7 @@ Torin's Passage CD: Mixer" from the in-game "Game" menu and setting the speech volume to zero. -3.25) Zork games notes: +3.27) Zork games notes: ----- ----------------- To run the supported Zork games (Zork Nemesis: The Forbidden Lands and Zork: Grand Inquisitor) you need to copy some (extra) data to it's @@ -1270,7 +1289,7 @@ Copy the zassetsc directory into the game root directory Copy the zassetse directory into the game root directory -3.26) Commodore64 games notes: +3.28) Commodore64 games notes: ----- ------------------------ Both Maniac Mansion and Zak McKracken run but Maniac Mansion is not yet playable. Simply name the D64 disks "maniac1.d64" and "maniac2.d64" @@ -1284,7 +1303,7 @@ to Commodore64. We recommend using the much simpler approach described in the previous paragraph. -3.27) Macintosh games notes: +3.29) Macintosh games notes: ----- ---------------------- All LucasArts SCUMM based adventures, except COMI, also exist in versions for the Macintosh. ScummVM can use most (all?) of them, however, in some @@ -1902,6 +1921,14 @@ versions. - Rename the saved game to 'elvira2-pc.xxx' (DOS version) or 'elvira2.xxx' (Other versions) + Myst + - Rename the saved game to 'myst-xxx.mys' + - Saves from the masterpiece edition and the regular edition are interchangeable + + Riven + - Rename the saved game to 'riven-xxx.rvn' + - Saves from the CD and DVD edition are not interchangeable + Simon the Sorcerer 1 - Rename the saved game to 'simon1.xxx' diff --git a/common/zlib.h b/common/zlib.h index 5adba64076..c8877e98a1 100644 --- a/common/zlib.h +++ b/common/zlib.h @@ -111,6 +111,7 @@ bool inflateZlibInstallShield(byte *dst, uint dstLen, const byte *src, uint srcL * still need the length carried along with the stream, and you know * the decompressed length at wrap-time, then it can be supplied as knownSize * here. knownSize will be ignored if the GZip-stream DOES include a length. + * The created stream also becomes responsible for freeing the passed stream. * * It is safe to call this with a NULL parameter (in this case, NULL is * returned). @@ -125,6 +126,7 @@ SeekableReadStream *wrapCompressedReadStream(SeekableReadStream *toBeWrapped, ui * 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). + * The created stream also becomes responsible for freeing the passed stream. * * It is safe to call this with a NULL parameter (in this case, NULL is * returned). @@ -4132,7 +4132,8 @@ EOF cc_check $ZLIB_CFLAGS $ZLIB_LIBS -lz && _zlib=yes fi if test "$_zlib" = yes ; then - append_var LIBS "$ZLIB_LIBS -lz" + append_var ZLIB_LIBS "-lz" + append_var LIBS "$ZLIB_LIBS" append_var INCLUDES "$ZLIB_CFLAGS" fi define_in_config_if_yes "$_zlib" 'USE_ZLIB' @@ -5193,6 +5194,8 @@ EXEPRE := $HOSTEXEPRE EXEEXT := $HOSTEXEEXT NASM := $NASM NASMFLAGS := $NASMFLAGS +ZLIB_LIBS := $ZLIB_LIBS +ZLIB_CFLAGS := $ZLIB_CFLAGS prefix = $prefix exec_prefix = $exec_prefix diff --git a/devtools/create_titanic/create_titanic_dat.cpp b/devtools/create_titanic/create_titanic_dat.cpp index 6d9b713846..3d5e4476c9 100644 --- a/devtools/create_titanic/create_titanic_dat.cpp +++ b/devtools/create_titanic/create_titanic_dat.cpp @@ -33,7 +33,9 @@ #include <stdlib.h> #include <string.h> #include "common/language.h" +#include "common/memstream.h" #include "common/rect.h" +#include "zlib.h" #include "winexe_pe.h" #include "file.h" #include "script_preresponses.h" @@ -54,8 +56,8 @@ * ASCIIZ - name of the resource */ -#define VERSION_NUMBER 1 -#define HEADER_SIZE 0x1200 +#define VERSION_NUMBER 2 +#define HEADER_SIZE 0x1380 Common::File inputFile, outputFile; Common::PEResources resEng, resGer; @@ -1022,14 +1024,24 @@ void NORETURN_PRE error(const char *s, ...) { exit(1); } -void writeEntryHeader(const char *name, uint offset, uint size) { +void writeEntryHeader(const char *name, uint offset, uint size, uint flags) { assert(headerOffset < HEADER_SIZE); outputFile.seek(headerOffset); outputFile.writeLong(offset); outputFile.writeLong(size); + outputFile.writeWord(flags); outputFile.writeString(name); - headerOffset += 8 + strlen(name) + 1; + headerOffset += 10 + strlen(name) + 1; +} + +void writeEntryHeader(const char *name, uint offset, uint size) { + writeEntryHeader(name, offset, size, 0); +} + +void writeEntryHeader(const char *name, uint offset, uint size, bool isCompressed) { + uint flags = isCompressed ? 1 : 0; + writeEntryHeader(name, offset, size, flags); } void writeFinalEntryHeader() { @@ -1039,6 +1051,10 @@ void writeFinalEntryHeader() { outputFile.writeLong(0); } +void writeCompressedRes(Common::File *src) { + +} + void writeStringArray(const char *name, uint offset, int count) { outputFile.seek(dataOffset); @@ -1130,18 +1146,36 @@ void writeResource(const char *sectionStr, const char *resId, bool isEnglish = t void writeBitmap(const char *name, Common::File *file) { outputFile.seek(dataOffset); + // Set up a memory stream for the compressed data, and wrap + // it with a zlib compressor + Common::MemoryWriteStreamDynamic *compressedData = + new Common::MemoryWriteStreamDynamic(DisposeAfterUse::YES); + Common::WriteStream *zlib = Common::wrapCompressedWriteStream(compressedData); + // Write out the necessary bitmap header so that the ScummVM // BMP decoder can properly handle decoding the bitmaps - outputFile.write("BM", 2); - outputFile.writeLong(file->size() + 14); // Filesize - outputFile.writeLong(0); // res1 & res2 - outputFile.writeLong(0x436); // image offset + zlib->write("BM", 2); + zlib->writeUint32LE(file->size() + 14); // Filesize + zlib->writeUint32LE(0); // res1 & res2 + zlib->writeUint32LE(0x436); // image offset - outputFile.write(*file, file->size()); + // Transfer the bitmap data + int srcSize = file->size(); + byte *data = new byte[srcSize]; + file->read(data, srcSize); + zlib->write(data, srcSize); - writeEntryHeader(name, dataOffset, file->size() + 14); - dataOffset += file->size() + 14; - delete file; + delete[] data; + zlib->finalize(); + + // Write out the compressed data + outputFile.write(compressedData->getData(), compressedData->size()); + + writeEntryHeader(name, dataOffset, compressedData->size() + 14, true); + dataOffset += compressedData->size() + 14; + + // Free the zlib write stream + delete zlib; } void writeBitmap(const char *sectionStr, const char *resId, bool isEnglish = true) { diff --git a/devtools/create_titanic/module.mk b/devtools/create_titanic/module.mk index 9f77866d45..b86fe18a2e 100644 --- a/devtools/create_titanic/module.mk +++ b/devtools/create_titanic/module.mk @@ -1,4 +1,6 @@ +ifdef USE_ZLIB + MODULE := devtools/create_titanic MODULE_OBJS := \ @@ -13,10 +15,16 @@ MODULE_OBJS := \ str.o \ tag_maps.o \ winexe.o \ - winexe_pe.o + winexe_pe.o \ + zlib.o # Set the name of the executable TOOL_EXECUTABLE := create_titanic +TOOL_CFLAGS := $(ZLIB_CFLAGS) +TOOL_LIBS := $(ZLIB_LIBS) + # Include common rules include $(srcdir)/rules.mk + +endif diff --git a/devtools/create_titanic/zlib.cpp b/devtools/create_titanic/zlib.cpp new file mode 100644 index 0000000000..96e0020e31 --- /dev/null +++ b/devtools/create_titanic/zlib.cpp @@ -0,0 +1,456 @@ +/* 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. + * + */ + +// Disable symbol overrides so that we can use zlib.h +#define FORBIDDEN_SYMBOL_ALLOW_ALL +#define USE_ZLIB + +#include "zlib.h" +#include "common/ptr.h" +#include "common/util.h" +#include "common/stream.h" + +#if defined(USE_ZLIB) + #ifdef __SYMBIAN32__ + #include <zlib\zlib.h> + #else + #include <zlib.h> + #endif + + #if ZLIB_VERNUM < 0x1204 + #error Version 1.2.0.4 or newer of zlib is required for this code + #endif +#endif + + +namespace Common { + +/** + * Stubs for ScummVM stuff I don't want to have to link in + */ +void debug(int level, const char *s, ...) {} + +char *SeekableReadStream::readLine(char *buf, size_t bufSize) { + return nullptr; +} + +String SeekableReadStream::readLine() { + return String(); +} + +#if defined(USE_ZLIB) + +bool uncompress(byte *dst, unsigned long *dstLen, const byte *src, unsigned long srcLen) { + return Z_OK == ::uncompress(dst, dstLen, src, srcLen); +} + +bool inflateZlibHeaderless(byte *dst, uint dstLen, const byte *src, uint srcLen, const byte *dict, uint dictLen) { + if (!dst || !dstLen || !src || !srcLen) + return false; + + // Initialize zlib + z_stream stream; + stream.next_in = const_cast<byte *>(src); + stream.avail_in = srcLen; + stream.next_out = dst; + stream.avail_out = dstLen; + stream.zalloc = Z_NULL; + stream.zfree = Z_NULL; + stream.opaque = Z_NULL; + + // Negative MAX_WBITS tells zlib there's no zlib header + int err = inflateInit2(&stream, -MAX_WBITS); + if (err != Z_OK) + return false; + + // Set the dictionary, if provided + if (dict != 0) { + err = inflateSetDictionary(&stream, const_cast<byte *>(dict), dictLen); + if (err != Z_OK) + return false; + } + + err = inflate(&stream, Z_SYNC_FLUSH); + if (err != Z_OK && err != Z_STREAM_END) { + inflateEnd(&stream); + return false; + } + + inflateEnd(&stream); + return true; +} + +enum { + kTempBufSize = 65536 +}; + +bool inflateZlibInstallShield(byte *dst, uint dstLen, const byte *src, uint srcLen) { + if (!dst || !dstLen || !src || !srcLen) + return false; + + // See if we have sync bytes. If so, just use our function for that. + if (srcLen >= 4 && READ_BE_UINT32(src + srcLen - 4) == 0xFFFF) + return inflateZlibHeaderless(dst, dstLen, src, srcLen); + + // Otherwise, we have some custom code we get to use here. + + byte *temp = (byte *)malloc(kTempBufSize); + + uint32 bytesRead = 0, bytesProcessed = 0; + while (bytesRead < srcLen) { + uint16 chunkSize = READ_LE_UINT16(src + bytesRead); + bytesRead += 2; + + // Initialize zlib + z_stream stream; + stream.next_in = const_cast<byte *>(src + bytesRead); + stream.avail_in = chunkSize; + stream.next_out = temp; + stream.avail_out = kTempBufSize; + stream.zalloc = Z_NULL; + stream.zfree = Z_NULL; + stream.opaque = Z_NULL; + + // Negative MAX_WBITS tells zlib there's no zlib header + int err = inflateInit2(&stream, -MAX_WBITS); + if (err != Z_OK) + return false; + + err = inflate(&stream, Z_FINISH); + if (err != Z_OK && err != Z_STREAM_END) { + inflateEnd(&stream); + free(temp); + return false; + } + + memcpy(dst + bytesProcessed, temp, stream.total_out); + bytesProcessed += stream.total_out; + + inflateEnd(&stream); + bytesRead += chunkSize; + } + + free(temp); + return true; +} + +#ifndef RELEASE_BUILD +static bool _shownBackwardSeekingWarning = false; +#endif + +/** + * 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 SeekableReadStream { +protected: + enum { + BUFSIZE = 16384 // 1 << MAX_WBITS + }; + + byte _buf[BUFSIZE]; + + ScopedPtr<SeekableReadStream> _wrapped; + z_stream _stream; + int _zlibErr; + uint32 _pos; + uint32 _origSize; + bool _eos; + +public: + + GZipReadStream(SeekableReadStream *w, uint32 knownSize = 0) : _wrapped(w), _stream() { + assert(w != 0); + + // 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 + // use an otherwise known size if supplied. + _origSize = knownSize; + } + _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); + } + + 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; + switch (whence) { + case SEEK_SET: + newPos = offset; + break; + case SEEK_CUR: + newPos = _pos + offset; + break; + case SEEK_END: + // NOTE: This can be an expensive operation (see below). + newPos = size() + offset; + break; + } + + 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. :/ + +#ifndef RELEASE_BUILD + if (!_shownBackwardSeekingWarning) { + // We only throw this warning once per stream, to avoid + // getting the console swarmed with warnings when consecutive + // seeks are made. + debug(1, "Backward seeking in GZipReadStream detected"); + _shownBackwardSeekingWarning = true; + } +#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 WriteStream { +protected: + enum { + BUFSIZE = 16384 // 1 << MAX_WBITS + }; + + byte _buf[BUFSIZE]; + ScopedPtr<WriteStream> _wrapped; + z_stream _stream; + int _zlibErr; + uint32 _pos; + + 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(WriteStream *w) : _wrapped(w), _stream(), _pos(0) { + assert(w != 0); + + // 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); + } + + 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); + + _pos += dataSize - _stream.avail_in; + return dataSize - _stream.avail_in; + } + + virtual int32 pos() const { return _pos; } +}; + +#endif // USE_ZLIB + +SeekableReadStream *wrapCompressedReadStream(SeekableReadStream *toBeWrapped, uint32 knownSize) { + if (toBeWrapped) { + uint16 header = toBeWrapped->readUint16BE(); + bool isCompressed = (header == 0x1F8B || + ((header & 0x0F00) == 0x0800 && + header % 31 == 0)); + toBeWrapped->seek(-2, SEEK_CUR); + if (isCompressed) { +#if defined(USE_ZLIB) + return new GZipReadStream(toBeWrapped, knownSize); +#else + delete toBeWrapped; + return NULL; +#endif + } + } + return toBeWrapped; +} + +WriteStream *wrapCompressedWriteStream(WriteStream *toBeWrapped) { +#if defined(USE_ZLIB) + if (toBeWrapped) + return new GZipWriteStream(toBeWrapped); +#endif + return toBeWrapped; +} + + +} // End of namespace Common diff --git a/devtools/create_titanic/zlib.h b/devtools/create_titanic/zlib.h new file mode 100644 index 0000000000..5adba64076 --- /dev/null +++ b/devtools/create_titanic/zlib.h @@ -0,0 +1,136 @@ +/* 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. + * + */ + +#ifndef COMMON_ZLIB_H +#define COMMON_ZLIB_H + +#include "common/scummsys.h" + +namespace Common { + +class SeekableReadStream; +class WriteStream; + +#if defined(USE_ZLIB) + +/** + * Thin wrapper around zlib's uncompress() function. This wrapper makes + * it possible to uncompress data in engines without being forced to link + * them against zlib, thus simplifying the build system. + * + * Taken from the zlib manual: + * Decompresses the src buffer into the dst buffer. + * srcLen is the byte length of the source buffer. Upon entry, dstLen is the + * total size of the destination buffer, which must be large enough to hold + * the entire uncompressed data. Upon exit, dstLen is the actual size of the + * compressed buffer. + * + * @param dst the buffer to store into. + * @param dstLen a pointer to the size of the destination buffer. + * @param src the data to be decompressed. + * @param srcLen the size of the compressed data. + * + * @return true on success (i.e. Z_OK), false otherwise. + */ +bool uncompress(byte *dst, unsigned long *dstLen, const byte *src, unsigned long srcLen); + +/** + * Wrapper around zlib's inflate functions. This function will call the + * necessary inflate functions to uncompress data compressed with deflate + * but *not* with the standard zlib header. + * + * Decompresses the src buffer into the dst buffer. + * srcLen is the byte length of the source buffer, dstLen is the byte + * length of the output buffer. + * It decompress as much data as possible, up to dstLen bytes. + * If a dictionary is provided through the dict buffer, uses it to initializes + * the internal decompression dictionary, before the decompression takes place. + * + * @param dst the buffer to store into. + * @param dstLen the size of the destination buffer. + * @param src the data to be decompressed. + * @param srcLen the size of the compressed data. + * @param dict (optional) a decompress dictionary. + * @param dictLen (optional) the size of the dictionary. + * Mandatory if dict is not 0. + * + * @return true on success (Z_OK or Z_STREAM_END), false otherwise. + */ +bool inflateZlibHeaderless(byte *dst, uint dstLen, const byte *src, uint srcLen, const byte *dict = 0, uint dictLen = 0); + +/** + * Wrapper around zlib's inflate functions. This function will call the + * necessary inflate functions to uncompress data compressed for InstallShield + * cabinet files. + * + * Decompresses the src buffer into the dst buffer. + * srcLen is the byte length of the source buffer, dstLen is the byte + * length of the output buffer. + * It decompress as much data as possible, up to dstLen bytes. + * + * @param dst the buffer to store into. + * @param dstLen the size of the destination buffer. + * @param src the data to be decompressed. + * @param srcLen the size of the compressed data. + * + * @return true on success (Z_OK or Z_STREAM_END), false otherwise. + */ +bool inflateZlibInstallShield(byte *dst, uint dstLen, const byte *src, uint srcLen); + +#endif + +/** + * 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). In the latter case the stream is + * returned wrapped, unless there is no ZLIB support, then NULL is returned + * and the old stream is destroyed. + * + * Certain GZip-formats don't supply an easily readable length, if you + * still need the length carried along with the stream, and you know + * the decompressed length at wrap-time, then it can be supplied as knownSize + * here. knownSize will be ignored if the GZip-stream DOES include a length. + * + * It is safe to call this with a NULL parameter (in this case, NULL is + * returned). + * + * @param toBeWrapped the stream to be wrapped (if it is in gzip-format) + * @param knownSize a supplied length of the compressed data (if not available directly) + */ +SeekableReadStream *wrapCompressedReadStream(SeekableReadStream *toBeWrapped, uint32 knownSize = 0); + +/** + * 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). + */ +WriteStream *wrapCompressedWriteStream(WriteStream *toBeWrapped); + +} // End of namespace Common + +#endif diff --git a/dists/engine-data/titanic.dat b/dists/engine-data/titanic.dat Binary files differindex 910bb9ae00..2a9d60122b 100644 --- a/dists/engine-data/titanic.dat +++ b/dists/engine-data/titanic.dat diff --git a/engines/bladerunner/vqa_decoder.cpp b/engines/bladerunner/vqa_decoder.cpp index c43e4c3f9d..377251714b 100644 --- a/engines/bladerunner/vqa_decoder.cpp +++ b/engines/bladerunner/vqa_decoder.cpp @@ -493,8 +493,10 @@ bool VQADecoder::readLNIN(Common::SeekableReadStream *s, uint32 size) { } readIFFChunkHeader(_s, &chd); - if (chd.id != kLNID) + if (chd.id != kLNID) { + free(loopNameOffsets); return false; + } char *names = (char*)malloc(roundup(chd.size)); s->read(names, roundup(chd.size)); diff --git a/engines/titanic/configure.engine b/engines/titanic/configure.engine index 1697a779c0..23a3b4e599 100644 --- a/engines/titanic/configure.engine +++ b/engines/titanic/configure.engine @@ -1,3 +1,3 @@ # This file is included from the main "configure" script # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] -add_engine titanic "Starship Titanic" no "" "" "16bit jpeg highres mad" +add_engine titanic "Starship Titanic" no "" "" "16bit jpeg highres mad zlib" diff --git a/engines/titanic/core/game_object.cpp b/engines/titanic/core/game_object.cpp index 580509252b..5abc9f0f76 100644 --- a/engines/titanic/core/game_object.cpp +++ b/engines/titanic/core/game_object.cpp @@ -954,7 +954,7 @@ CGameObject *CGameObject::findMailByFlags(RoomFlagsComparison compareType, uint for (CGameObject *obj = mailMan->getFirstObject(); obj; obj = mailMan->getNextObject(obj)) { - if (compareRoomFlags(compareType, roomFlags, obj->_roomFlags)) + if (compareRoomFlags(compareType, obj->_roomFlags, roomFlags)) return obj; } diff --git a/engines/titanic/npcs/bilge_succubus.cpp b/engines/titanic/npcs/bilge_succubus.cpp index a1fc46874c..312f067bd4 100644 --- a/engines/titanic/npcs/bilge_succubus.cpp +++ b/engines/titanic/npcs/bilge_succubus.cpp @@ -89,7 +89,7 @@ bool CBilgeSuccUBus::PETReceiveMsg(CPETReceiveMsg *msg) { } else { uint roomFlags = pet->getRoomFlags(); CGameObject *mailObject = findMailByFlags( - _enabled && compareRoomNameTo("Titania") ? RFC_TITANIA : _flagsComparison, + _fuseboxOn && compareRoomNameTo("Titania") ? RFC_TITANIA : _flagsComparison, roomFlags); if (mailObject) { diff --git a/engines/titanic/npcs/succubus.cpp b/engines/titanic/npcs/succubus.cpp index 522269b41b..79d779b487 100644 --- a/engines/titanic/npcs/succubus.cpp +++ b/engines/titanic/npcs/succubus.cpp @@ -46,9 +46,9 @@ BEGIN_MESSAGE_MAP(CSuccUBus, CTrueTalkNPC) ON_MESSAGE(MouseDragStartMsg) END_MESSAGE_MAP() -bool CSuccUBus::_isOn; -bool CSuccUBus::_motherBlocked; -bool CSuccUBus::_enabled; +bool CSuccUBus::_isOn; // SuccUBus turned on +bool CSuccUBus::_motherBlocked; // Bilge SuccUBus is blocked +bool CSuccUBus::_fuseboxOn; // SuccUBus dial in fusebox is on CSuccUBus::CSuccUBus() : CTrueTalkNPC() { _initialStartFrame = -1; @@ -154,7 +154,7 @@ void CSuccUBus::save(SimpleFile *file, int indent) { file->writeNumberLine(_pumpingEndFrame, indent); file->writeNumberLine(_destRoomFlags, indent); - file->writeNumberLine(_enabled, indent); + file->writeNumberLine(_fuseboxOn, indent); file->writeNumberLine(_inProgress, indent); file->writeNumberLine(_field104, indent); @@ -218,7 +218,7 @@ void CSuccUBus::load(SimpleFile *file) { _pumpingEndFrame = file->readNumber(); _destRoomFlags = file->readNumber(); - _enabled = file->readNumber(); + _fuseboxOn = file->readNumber(); _inProgress = file->readNumber(); _field104 = file->readNumber(); @@ -488,8 +488,12 @@ bool CSuccUBus::PETReceiveMsg(CPETReceiveMsg *msg) { break; } } else { + // When the SuccUBus dial in Titania's fusebox is on, then + // any mail can be received by the SuccUBus in the bomb room. + // Otherwise, only get mail sent to this specific SuccUBus CGameObject *mailObject = findMailByFlags( - _enabled && compareRoomNameTo("Titania") ? RFC_TITANIA : _flagsComparison, petRoomFlags); + _fuseboxOn && compareRoomNameTo("Titania") ? RFC_TITANIA : _flagsComparison, petRoomFlags); + if (!mailObject) { // No mail for this SuccUBus if (getRandomNumber(1) == 0) { @@ -539,7 +543,7 @@ bool CSuccUBus::MovieEndMsg(CMovieEndMsg *msg) { bool flag = false; if (pet && !mailExists(petRoomFlags)) { - CGameObject *mailObject = _enabled && compareRoomNameTo("Titania") ? + CGameObject *mailObject = _fuseboxOn && compareRoomNameTo("Titania") ? findMailByFlags(RFC_TITANIA, petRoomFlags) : findMailByFlags(_flagsComparison, petRoomFlags); @@ -750,9 +754,11 @@ bool CSuccUBus::SetChevRoomBits(CSetChevRoomBits *msg) { bool CSuccUBus::ActMsg(CActMsg *msg) { if (msg->_action == "EnableObject") - _enabled = true; + // SuccUBus dial in fusebox was turned on + _fuseboxOn = true; else if (msg->_action == "DisableObject") - _enabled = false; + // SuccUBus dial in fusebox was turned off + _fuseboxOn = false; return true; } diff --git a/engines/titanic/npcs/succubus.h b/engines/titanic/npcs/succubus.h index 94e0bebe54..1bcebc06da 100644 --- a/engines/titanic/npcs/succubus.h +++ b/engines/titanic/npcs/succubus.h @@ -51,7 +51,7 @@ class CSuccUBus : public CTrueTalkNPC { protected: static bool _isOn; static bool _motherBlocked; - static bool _enabled; + static bool _fuseboxOn; protected: int _initialStartFrame; int _initialEndFrame; diff --git a/engines/titanic/support/files_manager.cpp b/engines/titanic/support/files_manager.cpp index 5fc9379917..eebd56c1fa 100644 --- a/engines/titanic/support/files_manager.cpp +++ b/engines/titanic/support/files_manager.cpp @@ -22,6 +22,7 @@ #include "common/file.h" #include "common/memstream.h" +#include "common/zlib.h" #include "titanic/support/files_manager.h" #include "titanic/game_manager.h" #include "titanic/titanic.h" @@ -29,7 +30,7 @@ namespace Titanic { CFilesManager::CFilesManager(TitanicEngine *vm) : _vm(vm), _gameManager(nullptr), - _assetsPath("Assets"), _drive(-1) { + _assetsPath("Assets"), _drive(-1), _version(0) { } CFilesManager::~CFilesManager() { @@ -43,19 +44,21 @@ bool CFilesManager::loadResourceIndex() { } uint headerId = _datFile.readUint32BE(); - uint version = _datFile.readUint16LE(); - if (headerId != MKTAG('S', 'V', 'T', 'N') || version < 1) { + _version = _datFile.readUint16LE(); + if (headerId != MKTAG('S', 'V', 'T', 'N') || _version < 1) { g_vm->GUIError("titanic.dat has invalid contents"); return false; } // Read in entries - uint offset, size; + uint offset, size, flags; char c; Common::String resourceName; for (;;) { offset = _datFile.readUint32LE(); size = _datFile.readUint32LE(); + flags = (_version == 1) ? 0 : _datFile.readUint16LE(); + if (offset == 0 && size == 0) break; @@ -63,7 +66,7 @@ bool CFilesManager::loadResourceIndex() { while ((c = _datFile.readByte()) != '\0') resName += c; - _resources[resName] = ResourceEntry(offset, size); + _resources[resName] = ResourceEntry(offset, size, flags); } return true; @@ -136,8 +139,13 @@ Common::SeekableReadStream *CFilesManager::getResource(const CString &str) { _datFile.seek(resEntry._offset); - return (resEntry._size > 0) ? _datFile.readStream(resEntry._size) : + Common::SeekableReadStream *stream = (resEntry._size > 0) ? + _datFile.readStream(resEntry._size) : new Common::MemoryReadStream(nullptr, 0); + if (resEntry._flags & FLAG_COMPRESSED) + stream = Common::wrapCompressedReadStream(stream); + + return stream; } } // End of namespace Titanic diff --git a/engines/titanic/support/files_manager.h b/engines/titanic/support/files_manager.h index 7627ececdd..c1e3c3b274 100644 --- a/engines/titanic/support/files_manager.h +++ b/engines/titanic/support/files_manager.h @@ -29,6 +29,8 @@ namespace Titanic { +enum ResourceFlag { FLAG_COMPRESSED = 1 }; + class TitanicEngine; class CGameManager; @@ -39,9 +41,11 @@ class CFilesManager { struct ResourceEntry { uint _offset; uint _size; + uint _flags; - ResourceEntry() : _offset(0), _size(0) {} - ResourceEntry(uint offset, uint size) : _offset(offset), _size(size) {} + ResourceEntry() : _offset(0), _size(0), _flags(0) {} + ResourceEntry(uint offset, uint size, uint flags) : + _offset(offset), _size(size), _flags(flags) {} }; typedef Common::HashMap<Common::String, ResourceEntry> ResourceHash; private: @@ -52,6 +56,7 @@ private: CFilesManagerList _list; int _drive; const CString _assetsPath; + int _version; public: CFilesManager(TitanicEngine *vm); ~CFilesManager(); diff --git a/engines/titanic/support/simple_file.cpp b/engines/titanic/support/simple_file.cpp index 65d2c85273..103f062ac6 100644 --- a/engines/titanic/support/simple_file.cpp +++ b/engines/titanic/support/simple_file.cpp @@ -424,8 +424,10 @@ bool SimpleFile::scanf(const char *format, ...) { formatStr.deleteChar(0); safeRead(&c, 1); - if (!Common::isSpace(c)) + if (!Common::isSpace(c)) { + va_end(va); return false; + } // Skip over whitespaces skipSpaces(); @@ -20,12 +20,17 @@ ifdef TOOL_EXECUTABLE # TODO: Refactor this, so that even our master executable can use this rule? ################################################ TOOL-$(MODULE) := $(MODULE)/$(TOOL_EXECUTABLE)$(EXEEXT) +TOOL_LIBS-$(TOOL-$(MODULE)) := $(TOOL_LIBS) +TOOL_CFLAGS-$(TOOL-$(MODULE)) := $(TOOL_CFLAGS) + $(TOOL-$(MODULE)): $(MODULE_OBJS-$(MODULE)) $(TOOL_DEPS) - $(QUIET_CXX)$(CXX) $(LDFLAGS) $+ -o $@ + $(QUIET_CXX)$(CXX) $(LDFLAGS) $(TOOL_CFLAGS-$@) $+ $(TOOL_LIBS-$@) -o $@ # Reset TOOL_* vars TOOL_EXECUTABLE:= TOOL_DEPS:= +TOOL_CFLAGS:= +TOOL_LIBS:= # Add to "devtools" target devtools: $(TOOL-$(MODULE)) |