diff options
60 files changed, 2296 insertions, 256 deletions
@@ -7,6 +7,9 @@ For a more comprehensive changelog of the latest experimental code, see: - Added support for Blue's Birthday Adventure - Added support for Ringworld: Revenge Of The Patriarch + New Ports: + - Added PlayStation 3 port. + AGI: - Implemented sound support for the DOS version of Winnie the Pooh in the Hundred Acre Wood. diff --git a/common/zlib.cpp b/common/zlib.cpp index 86c618830e..70133fea30 100644 --- a/common/zlib.cpp +++ b/common/zlib.cpp @@ -49,6 +49,35 @@ bool uncompress(byte *dst, unsigned long *dstLen, const byte *src, unsigned long return Z_OK == ::uncompress(dst, dstLen, src, srcLen); } +bool inflateZlibHeaderless(byte *dst, uint dstLen, const byte *src, uint srcLen) { + 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; + + err = inflate(&stream, Z_SYNC_FLUSH); + if (err != Z_OK && err != Z_STREAM_END) { + inflateEnd(&stream); + return false; + } + + inflateEnd(&stream); + return true; +} + /** * A simple wrapper class which can be used to wrap around an arbitrary * other SeekableReadStream and will then provide on-the-fly decompression support. diff --git a/common/zlib.h b/common/zlib.h index 1925034310..7af7df0da8 100644 --- a/common/zlib.h +++ b/common/zlib.h @@ -41,6 +41,15 @@ class WriteStream; */ 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. + * + * @return true on success (Z_OK or Z_STREAM_END), false otherwise + */ +bool inflateZlibHeaderless(byte *dst, uint dstLen, const byte *src, uint srcLen); + #endif /** @@ -1072,12 +1072,12 @@ arm-riscos) ;; bada) _host_os=bada - if test "$_debug_build" = yes; then - _host_cpu=i686 - _host_alias=i686-mingw32 - else - _host_cpu=arm - _host_alias=arm-samsung-nucleuseabi + if test "$_debug_build" = yes; then + _host_cpu=i686 + _host_alias=i686-mingw32 + else + _host_cpu=arm + _host_alias=arm-samsung-nucleuseabi fi ;; caanoo) @@ -1793,12 +1793,12 @@ case $_host_os in _seq_midi=no ;; bada) - BADA_SDK_ROOT="`cygpath -m ${BADA_SDK}`" - add_line_to_config_mk "BADA_SDK = $BADA_SDK" - add_line_to_config_mk "BADA_SDK_ROOT = $BADA_SDK_ROOT" + BADA_SDK_ROOT="`cygpath -m ${BADA_SDK}`" + add_line_to_config_mk "BADA_SDK = $BADA_SDK" + add_line_to_config_mk "BADA_SDK_ROOT = $BADA_SDK_ROOT" - # assume dependencies have been installed in cygwin's /usr/local - CYGWIN_USR_LOCAL="`cygpath -m /usr/local`" + # assume dependencies have been installed in cygwin's /usr/local + CYGWIN_USR_LOCAL="`cygpath -m /usr/local`" LDFLAGS="$LDFLAGS -L${CYGWIN_USR_LOCAL}/lib" CXXFLAGS="$CXXFLAGS -I${CYGWIN_USR_LOCAL}/include" ;; @@ -1819,6 +1819,28 @@ case $_host_os in DEFINES="$DEFINES -DMACOSX" LIBS="$LIBS -framework AudioUnit -framework AudioToolbox -framework Carbon -framework CoreMIDI" add_line_to_config_mk 'MACOSX = 1' + + # Now we may have MacPorts or Fink installed + # Which put libraries and headers in non-standard places + # Checking them here + + # MacPorts + # There is no way to get the prefix, so implementing a hack here + macport_version=`port version 2>/dev/null` + if test "$?" -eq 0; then + macport_version="`echo "${macport_version}" | sed -ne 's/Version: \([0-9]\.[0-9]\.[0-9]\)/\1/gp'sed -ne 's/Version: \([0-9]\.[0-9]\.[0-9]\)/\1/gp'`" + echo_n "You seem to be running MacPorts version ${macport_version}..." + + macport_prefix=`which port` + # strip off /bin/port from /opt/local/bin/port + macport_prefix=`dirname ${macport_prefix}` + macport_prefix=`dirname ${macport_prefix}` + + echo "adding ${macport_prefix} to paths" + + LDFLAGS="-L${macport_prefix}/lib $LDFLAGS" + CXXFLAGS="-I${macport_prefix}/include $CXXFLAGS" + fi ;; dreamcast) DEFINES="$DEFINES -D__DC__" @@ -2029,21 +2051,21 @@ if test -n "$_host"; then DEFINES="$DEFINES -DLINUPY" ;; bada) - _unix=yes - _backend="bada" - _port_mk="backends/platform/bada/bada.mk" - if test "$_debug_build" = yes; then - _arm_asm=no - else - _arm_asm=yes - fi - _taskbar=no - _build_scalers=no - _seq_midi=no - _mt32emu=no - _timidity=no + _unix=yes + _backend="bada" + _port_mk="backends/platform/bada/bada.mk" + if test "$_debug_build" = yes; then + _arm_asm=no + else + _arm_asm=yes + fi + _taskbar=no + _build_scalers=no + _seq_midi=no + _mt32emu=no + _timidity=no _vkeybd=yes - ;; + ;; bfin*) ;; caanoo) @@ -2378,33 +2400,33 @@ case $_backend in LDFLAGS="$LDFLAGS -Wl,-z,noexecstack" ;; bada) - # dirent.h not available. NONSTANDARD_PORT==ensure portdefs.h is included - DEFINES="$DEFINES -DBADA -DDISABLE_STDIO_FILESTREAM -DNONSTANDARD_PORT" - DEFINES="$DEFINES -DNO_STDERR_STDOUT" + # dirent.h not available. NONSTANDARD_PORT==ensure portdefs.h is included + DEFINES="$DEFINES -DBADA -DDISABLE_STDIO_FILESTREAM -DNONSTANDARD_PORT" + DEFINES="$DEFINES -DNO_STDERR_STDOUT" DEFINES="$DEFINES -DDISABLE_COMMAND_LINE" - INCLUDES="$INCLUDES "'-I$(srcdir)/backends/platform/bada ' - INCLUDES="$INCLUDES "'-I$(BADA_SDK)/include' - INCLUDES="$INCLUDES "'-I$(BADA_SDK_ROOT)/Include' - if test "$_debug_build" = yes; then - # debug using with the simulator - CXXFLAGS="$CXXFLAGS -D_DEBUG -DSHP -DBUILD_DLL -fmessage-length=0" - else - # created a shared library for inclusion via the eclipse build - CXXFLAGS="$CXXFLAGS -DSHP" - CXXFLAGS="$CXXFLAGS -fpic" - CXXFLAGS="$CXXFLAGS -fshort-wchar" - CXXFLAGS="$CXXFLAGS -mcpu=cortex-a8" - CXXFLAGS="$CXXFLAGS -mfpu=vfpv3" - CXXFLAGS="$CXXFLAGS -mfloat-abi=hard" - CXXFLAGS="$CXXFLAGS -mlittle-endian" - CXXFLAGS="$CXXFLAGS -mthumb-interwork" - CXXFLAGS="$CXXFLAGS -Wno-psabi" - CXXFLAGS="$CXXFLAGS -fno-strict-aliasing" - CXXFLAGS="$CXXFLAGS -fno-short-enums" - fi - HOSTEXEPRE=lib - HOSTEXEEXT=.a - ;; + INCLUDES="$INCLUDES "'-I$(srcdir)/backends/platform/bada ' + INCLUDES="$INCLUDES "'-I$(BADA_SDK)/include' + INCLUDES="$INCLUDES "'-I$(BADA_SDK_ROOT)/Include' + if test "$_debug_build" = yes; then + # debug using with the simulator + CXXFLAGS="$CXXFLAGS -D_DEBUG -DSHP -DBUILD_DLL -fmessage-length=0" + else + # created a shared library for inclusion via the eclipse build + CXXFLAGS="$CXXFLAGS -DSHP" + CXXFLAGS="$CXXFLAGS -fpic" + CXXFLAGS="$CXXFLAGS -fshort-wchar" + CXXFLAGS="$CXXFLAGS -mcpu=cortex-a8" + CXXFLAGS="$CXXFLAGS -mfpu=vfpv3" + CXXFLAGS="$CXXFLAGS -mfloat-abi=hard" + CXXFLAGS="$CXXFLAGS -mlittle-endian" + CXXFLAGS="$CXXFLAGS -mthumb-interwork" + CXXFLAGS="$CXXFLAGS -Wno-psabi" + CXXFLAGS="$CXXFLAGS -fno-strict-aliasing" + CXXFLAGS="$CXXFLAGS -fno-short-enums" + fi + HOSTEXEPRE=lib + HOSTEXEEXT=.a + ;; dc) INCLUDES="$INCLUDES "'-I$(srcdir)/backends/platform/dc' INCLUDES="$INCLUDES "'-isystem $(ronindir)/include' @@ -3262,10 +3284,10 @@ fi case $_host_os in bada) - # components live in non-standard locations so just assume sane SDK - _opengl=yes - _opengles=yes - ;; + # components live in non-standard locations so just assume sane SDK + _opengl=yes + _opengles=yes + ;; esac if test "$_opengles" = "yes" ; then diff --git a/engines/agos/agos.cpp b/engines/agos/agos.cpp index 97c594684c..465b6e0786 100644 --- a/engines/agos/agos.cpp +++ b/engines/agos/agos.cpp @@ -41,18 +41,51 @@ namespace AGOS { static const GameSpecificSettings simon1_settings = { + "", // base_filename + "", // restore_filename + "", // tbl_filename "EFFECTS", // effects_filename "SIMON", // speech_filename }; static const GameSpecificSettings simon2_settings = { + "", // base_filename + "", // restore_filename + "", // tbl_filename "", // effects_filename "SIMON2", // speech_filename }; -static const GameSpecificSettings puzzlepack_settings = { +static const GameSpecificSettings dimp_settings = { + "Gdimp", // base_filename + "", // restore_filename + "", // tbl_filename "", // effects_filename - "MUSIC", // speech_filename + "MUSIC", // speech_filename +}; + +static const GameSpecificSettings jumble_settings = { + "Gjumble", // base_filename + "", // restore_filename + "", // tbl_filename + "", // effects_filename + "MUSIC", // speech_filename +}; + +static const GameSpecificSettings puzzle_settings = { + "Gpuzzle", // base_filename + "", // restore_filename + "", // tbl_filename + "", // effects_filename + "MUSIC", // speech_filename +}; + +static const GameSpecificSettings swampy_settings = { + "Gswampy", // base_filename + "", // restore_filename + "", // tbl_filename + "", // effects_filename + "MUSIC", // speech_filename }; #ifdef ENABLE_AGOS2 @@ -678,7 +711,15 @@ static const uint16 initialVideoWindows_PN[20] = { #ifdef ENABLE_AGOS2 void AGOSEngine_PuzzlePack::setupGame() { - gss = &puzzlepack_settings; + if (getGameId() == GID_DIMP) { + gss = &dimp_settings; + } else if (getGameId() == GID_JUMBLE) { + gss = &jumble_settings; + } else if (getGameId() == GID_PUZZLE) { + gss = &puzzle_settings; + } else if (getGameId() == GID_SWAMPY) { + gss = &swampy_settings; + } _numVideoOpcodes = 85; _vgaMemSize = 7500000; _itemMemSize = 20000; @@ -963,6 +1004,10 @@ void AGOSEngine::pause() { } Common::Error AGOSEngine::go() { +#ifdef ENABLE_AGOS2 + loadArchives(); +#endif + loadGamePcFile(); addTimeEvent(0, 1); diff --git a/engines/agos/agos.h b/engines/agos/agos.h index 820bc0260b..ec979abc20 100644 --- a/engines/agos/agos.h +++ b/engines/agos/agos.h @@ -25,6 +25,7 @@ #include "engines/engine.h" +#include "common/archive.h" #include "common/array.h" #include "common/error.h" #include "common/keyboard.h" @@ -186,6 +187,22 @@ class Debugger; # define _OPCODE(ver, x) { &ver::x, "" } #endif +class ArchiveMan : public Common::SearchSet { +public: + ArchiveMan(); + + void enableFallback(bool val) { _fallBack = val; } + +#ifdef ENABLE_AGOS2 + void registerArchive(const Common::String &filename, int priority); +#endif + + Common::SeekableReadStream *open(const Common::String &filename); + +private: + bool _fallBack; +}; + class AGOSEngine : public Engine { protected: friend class Debugger; @@ -599,6 +616,8 @@ public: AGOSEngine(OSystem *system, const AGOSGameDescription *gd); virtual ~AGOSEngine(); + ArchiveMan _archives; + byte *_curSfxFile; uint32 _curSfxFileSize; uint16 _sampleEnd, _sampleWait; @@ -608,6 +627,10 @@ protected: virtual uint16 readUint16Wrapper(const void *src); virtual uint32 readUint32Wrapper(const void *src); +#ifdef ENABLE_AGOS2 + void loadArchives(); +#endif + int allocGamePcVars(Common::SeekableReadStream *in); void createPlayer(); void allocateStringTable(int num); @@ -792,14 +815,14 @@ protected: void loadTextIntoMem(uint16 stringId); uint loadTextFile(const char *filename, byte *dst); - Common::File *openTablesFile(const char *filename); - void closeTablesFile(Common::File *in); + Common::SeekableReadStream *openTablesFile(const char *filename); + void closeTablesFile(Common::SeekableReadStream *in); uint loadTextFile_simon1(const char *filename, byte *dst); - Common::File *openTablesFile_simon1(const char *filename); + Common::SeekableReadStream *openTablesFile_simon1(const char *filename); uint loadTextFile_gme(const char *filename, byte *dst); - Common::File *openTablesFile_gme(const char *filename); + Common::SeekableReadStream *openTablesFile_gme(const char *filename); void invokeTimeEvent(TimeEvent *te); bool kickoffTimeEvents(); diff --git a/engines/agos/animation.cpp b/engines/agos/animation.cpp index d9a585bd05..b05623525f 100644 --- a/engines/agos/animation.cpp +++ b/engines/agos/animation.cpp @@ -251,8 +251,11 @@ bool MoviePlayerDXA::load() { } Common::String videoName = Common::String::format("%s.dxa", baseName); - if (!loadFile(videoName)) + Common::SeekableReadStream *videoStream = _vm->_archives.open(videoName); + if (!videoStream) error("Failed to load video file %s", videoName.c_str()); + if (!loadStream(videoStream)) + error("Failed to load video stream from file %s", videoName.c_str()); debug(0, "Playing video %s", videoName.c_str()); @@ -412,8 +415,11 @@ MoviePlayerSMK::MoviePlayerSMK(AGOSEngine_Feeble *vm, const char *name) bool MoviePlayerSMK::load() { Common::String videoName = Common::String::format("%s.smk", baseName); - if (!loadFile(videoName)) + Common::SeekableReadStream *videoStream = _vm->_archives.open(videoName); + if (!videoStream) error("Failed to load video file %s", videoName.c_str()); + if (!loadStream(videoStream)) + error("Failed to load video stream from file %s", videoName.c_str()); debug(0, "Playing video %s", videoName.c_str()); diff --git a/engines/agos/detection.cpp b/engines/agos/detection.cpp index 2be888b92a..a14b660817 100644 --- a/engines/agos/detection.cpp +++ b/engines/agos/detection.cpp @@ -240,6 +240,22 @@ Common::Platform AGOSEngine::getPlatform() const { } const char *AGOSEngine::getFileName(int type) const { + // Required if the InstallShield cab is been used + if (getGameType() == GType_PP) { + if (type == GAME_BASEFILE) + return gss->base_filename; + } + + // Required if the InstallShield cab is been used + if (getGameType() == GType_FF && getPlatform() == Common::kPlatformWindows) { + if (type == GAME_BASEFILE) + return gss->base_filename; + if (type == GAME_RESTFILE) + return gss->restore_filename; + if (type == GAME_TBLFILE) + return gss->tbl_filename; + } + for (int i = 0; _gameDescription->desc.filesDescriptions[i].fileType; i++) { if (_gameDescription->desc.filesDescriptions[i].fileType == type) return _gameDescription->desc.filesDescriptions[i].fileName; @@ -247,4 +263,19 @@ const char *AGOSEngine::getFileName(int type) const { return NULL; } +#ifdef ENABLE_AGOS2 +void AGOSEngine::loadArchives() { + const ADGameFileDescription *ag; + + if (getFeatures() & GF_PACKED) { + for (ag = _gameDescription->desc.filesDescriptions; ag->fileName; ag++) { + if (!_archives.hasArchive(ag->fileName)) + _archives.registerArchive(ag->fileName, ag->fileType); + } + } + + _archives.enableFallback(true); +} +#endif + } // End of namespace AGOS diff --git a/engines/agos/detection_tables.h b/engines/agos/detection_tables.h index d9f321d98e..a7a384a496 100644 --- a/engines/agos/detection_tables.h +++ b/engines/agos/detection_tables.h @@ -2519,6 +2519,27 @@ static const AGOSGameDescription gameDescriptions[] = { GF_OLD_BUNDLE | GF_ZLIBCOMP | GF_TALKIE }, + // The Feeble Files - English Windows 2CD (with InstallShield cab) + { + { + "feeble", + "2CD", + + { + { "data1.cab", 0, "600db08891e7a21badc8215e604cd88f", 28845430}, + { NULL, 0, NULL, 0} + }, + Common::EN_ANY, + Common::kPlatformWindows, + ADGF_NO_FLAGS, + GUIO_NOSUBTITLES | GUIO_NOMUSIC + }, + + GType_FF, + GID_FEEBLEFILES, + GF_OLD_BUNDLE | GF_TALKIE | GF_PACKED + }, + // The Feeble Files - English Windows 2CD { { @@ -2565,6 +2586,27 @@ static const AGOSGameDescription gameDescriptions[] = { GF_OLD_BUNDLE | GF_TALKIE }, + // The Feeble Files - English Windows 4CD (with InstallShield cab) + { + { + "feeble", + "4CD", + + { + { "data1.cab", 0, "65804cbc9036ac4b1275d97e0de3be2f", 28943062}, + { NULL, 0, NULL, 0} + }, + Common::EN_ANY, + Common::kPlatformWindows, + ADGF_NO_FLAGS, + GUIO_NOSUBTITLES | GUIO_NOMUSIC + }, + + GType_FF, + GID_FEEBLEFILES, + GF_OLD_BUNDLE | GF_TALKIE | GF_PACKED + }, + // The Feeble Files - English Windows 4CD { { @@ -2703,6 +2745,27 @@ static const AGOSGameDescription gameDescriptions[] = { GF_OLD_BUNDLE | GF_TALKIE }, + // Simon the Sorcerer's Puzzle Pack - Demon in my Pocket (with InstallShield cab) + { + { + "dimp", + "CD", + + { + { "data1.cab", 0, "36dd86c1d872cea81ac1de7753dd684a", 40394693}, + { NULL, 0, NULL, 0} + }, + Common::EN_ANY, + Common::kPlatformWindows, + ADGF_NO_FLAGS, + GUIO_NOSUBTITLES | GUIO_NOMUSIC + }, + + GType_PP, + GID_DIMP, + GF_OLD_BUNDLE | GF_TALKIE | GF_PACKED + }, + // Simon the Sorcerer's Puzzle Pack - Demon in my Pocket { { @@ -2724,6 +2787,27 @@ static const AGOSGameDescription gameDescriptions[] = { GF_OLD_BUNDLE | GF_TALKIE }, + // Simon the Sorcerer's Puzzle Pack - Jumble (with InstallShield cab) + { + { + "jumble", + "CD", + + { + { "data1.cab", 0, "36dd86c1d872cea81ac1de7753dd684a", 40394693}, + { NULL, 0, NULL, 0} + }, + Common::EN_ANY, + Common::kPlatformWindows, + ADGF_NO_FLAGS, + GUIO_NOSUBTITLES + }, + + GType_PP, + GID_JUMBLE, + GF_OLD_BUNDLE | GF_TALKIE | GF_PACKED + }, + // Simon the Sorcerer's Puzzle Pack - Jumble { { @@ -2745,6 +2829,27 @@ static const AGOSGameDescription gameDescriptions[] = { GF_OLD_BUNDLE | GF_TALKIE }, + // Simon the Sorcerer's Puzzle Pack - NoPatience (with InstallShield cab) + { + { + "puzzle", + "CD", + + { + { "data1.cab", 0, "36dd86c1d872cea81ac1de7753dd684a", 40394693}, + { NULL, 0, NULL, 0} + }, + Common::EN_ANY, + Common::kPlatformWindows, + ADGF_NO_FLAGS, + GUIO_NOSUBTITLES + }, + + GType_PP, + GID_PUZZLE, + GF_OLD_BUNDLE | GF_TALKIE | GF_PACKED + }, + // Simon the Sorcerer's Puzzle Pack - NoPatience { { @@ -2766,6 +2871,27 @@ static const AGOSGameDescription gameDescriptions[] = { GF_OLD_BUNDLE | GF_TALKIE }, + // Simon the Sorcerer's Puzzle Pack - Swampy Adventures - English (with InstallShield cab) + { + { + "swampy", + "CD", + + { + { "data1.cab", 0, "36dd86c1d872cea81ac1de7753dd684a", 40394693}, + { NULL, 0, NULL, 0} + }, + Common::EN_ANY, + Common::kPlatformWindows, + ADGF_NO_FLAGS, + GUIO_NOSUBTITLES + }, + + GType_PP, + GID_SWAMPY, + GF_OLD_BUNDLE | GF_TALKIE | GF_PACKED + }, + // Simon the Sorcerer's Puzzle Pack - Swampy Adventures - English { { diff --git a/engines/agos/feeble.cpp b/engines/agos/feeble.cpp index 4c82b7e19d..e9a91e3b76 100644 --- a/engines/agos/feeble.cpp +++ b/engines/agos/feeble.cpp @@ -45,6 +45,9 @@ AGOSEngine_Feeble::~AGOSEngine_Feeble() { } static const GameSpecificSettings feeblefiles_settings = { + "game22", // base_filename + "save.999", // restore_filename + "tbllist", // tbl_filename "", // effects_filename "VOICES", // speech_filename }; diff --git a/engines/agos/installshield_cab.cpp b/engines/agos/installshield_cab.cpp new file mode 100644 index 0000000000..f7b49a64c5 --- /dev/null +++ b/engines/agos/installshield_cab.cpp @@ -0,0 +1,221 @@ +/* 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. + * + */ + +// The following code is based on unshield +// Original copyright: + +// Copyright (c) 2003 David Eriksson <twogood@users.sourceforge.net> +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include "agos/installshield_cab.h" + +#include "common/debug.h" +#include "common/file.h" +#include "common/memstream.h" +#include "common/zlib.h" + +namespace AGOS { + +class InstallShieldCabinet : public Common::Archive { + Common::String _installShieldFilename; + +public: + InstallShieldCabinet(const Common::String &filename); + ~InstallShieldCabinet(); + + // Common::Archive API implementation + bool hasFile(const Common::String &name); + int listMembers(Common::ArchiveMemberList &list); + Common::ArchiveMemberPtr getMember(const Common::String &name); + Common::SeekableReadStream *createReadStreamForMember(const Common::String &name) const; + +private: + struct FileEntry { + uint32 uncompressedSize; + uint32 compressedSize; + uint32 offset; + uint16 flags; + }; + + typedef Common::HashMap<Common::String, FileEntry, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> FileMap; + FileMap _map; +}; + +InstallShieldCabinet::~InstallShieldCabinet() { + _map.clear(); +} + +InstallShieldCabinet::InstallShieldCabinet(const Common::String &filename) : _installShieldFilename(filename) { + Common::File installShieldFile; + + if (!installShieldFile.open(_installShieldFilename)) { + warning("InstallShieldCabinet::InstallShieldCabinet(): Could not find the archive file %s", _installShieldFilename.c_str()); + return; + } + + // Note that we only support a limited subset of cabinet files + // Only single cabinet files and ones without data shared between + // cabinets. + + // Check for the magic uint32 + if (installShieldFile.readUint32LE() != 0x28635349) { + warning("InstallShieldCabinet::InstallShieldCabinet(): Magic ID doesn't match"); + return; + } + + uint32 version = installShieldFile.readUint32LE(); + + if (version != 0x01000004) { + warning("Unsupported CAB version %08x", version); + return; + } + + /* uint32 volumeInfo = */ installShieldFile.readUint32LE(); + uint32 cabDescriptorOffset = installShieldFile.readUint32LE(); + /* uint32 cabDescriptorSize = */ installShieldFile.readUint32LE(); + + installShieldFile.seek(cabDescriptorOffset); + + installShieldFile.skip(12); + uint32 fileTableOffset = installShieldFile.readUint32LE(); + installShieldFile.skip(4); + uint32 fileTableSize = installShieldFile.readUint32LE(); + uint32 fileTableSize2 = installShieldFile.readUint32LE(); + uint32 directoryCount = installShieldFile.readUint32LE(); + installShieldFile.skip(8); + uint32 fileCount = installShieldFile.readUint32LE(); + + if (fileTableSize != fileTableSize2) + warning("file table sizes do not match"); + + // We're ignoring file groups and components since we + // should not need them. Moving on to the files... + + installShieldFile.seek(cabDescriptorOffset + fileTableOffset); + uint32 fileTableCount = directoryCount + fileCount; + uint32 *fileTableOffsets = new uint32[fileTableCount]; + for (uint32 i = 0; i < fileTableCount; i++) + fileTableOffsets[i] = installShieldFile.readUint32LE(); + + for (uint32 i = directoryCount; i < fileCount + directoryCount; i++) { + installShieldFile.seek(cabDescriptorOffset + fileTableOffset + fileTableOffsets[i]); + uint32 nameOffset = installShieldFile.readUint32LE(); + /* uint32 directoryIndex = */ installShieldFile.readUint32LE(); + + // First read in data needed by us to get at the file data + FileEntry entry; + entry.flags = installShieldFile.readUint16LE(); + entry.uncompressedSize = installShieldFile.readUint32LE(); + entry.compressedSize = installShieldFile.readUint32LE(); + installShieldFile.skip(20); + entry.offset = installShieldFile.readUint32LE(); + + // Then let's get the string + installShieldFile.seek(cabDescriptorOffset + fileTableOffset + nameOffset); + Common::String fileName; + + char c = installShieldFile.readByte(); + while (c) { + fileName += c; + c = installShieldFile.readByte(); + } + _map[fileName] = entry; + } + + delete[] fileTableOffsets; +} + +bool InstallShieldCabinet::hasFile(const Common::String &name) { + warning("hasFile: Filename %s", name.c_str()); + return _map.contains(name); +} + +int InstallShieldCabinet::listMembers(Common::ArchiveMemberList &list) { + for (FileMap::const_iterator it = _map.begin(); it != _map.end(); it++) + list.push_back(getMember(it->_key)); + + return _map.size(); +} + +Common::ArchiveMemberPtr InstallShieldCabinet::getMember(const Common::String &name) { + return Common::ArchiveMemberPtr(new Common::GenericArchiveMember(name, this)); +} + +Common::SeekableReadStream *InstallShieldCabinet::createReadStreamForMember(const Common::String &name) const { + if (!_map.contains(name)) + return 0; + + const FileEntry &entry = _map[name]; + + Common::File archiveFile; + archiveFile.open(_installShieldFilename); + archiveFile.seek(entry.offset); + + if (!(entry.flags & 0x04)) { + // Not compressed + return archiveFile.readStream(entry.uncompressedSize); + } + +#ifdef USE_ZLIB + byte *src = (byte *)malloc(entry.compressedSize); + byte *dst = (byte *)malloc(entry.uncompressedSize); + + archiveFile.read(src, entry.compressedSize); + + bool result = Common::inflateZlibHeaderless(dst, entry.uncompressedSize, src, entry.compressedSize); + free(src); + + if (!result) { + warning("failed to inflate CAB file '%s'", name.c_str()); + free(dst); + return 0; + } + + return new Common::MemoryReadStream(dst, entry.uncompressedSize, DisposeAfterUse::YES); +#else + warning("zlib required to extract compressed CAB file '%s'", name.c_str()); + return 0; +#endif +} + +Common::Archive *makeInstallShieldArchive(const Common::String &name) { + return new InstallShieldCabinet(name); +} + +} // End of namespace AGOS diff --git a/engines/agos/installshield_cab.h b/engines/agos/installshield_cab.h new file mode 100644 index 0000000000..f7e8bed277 --- /dev/null +++ b/engines/agos/installshield_cab.h @@ -0,0 +1,41 @@ +/* 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. + * + */ + +#include "common/archive.h" +#include "common/str.h" + +#ifndef AGOS_INSTALLSHIELD_CAB_H +#define AGOS_INSTALLSHIELD_CAB_H + +namespace AGOS { + +/** + * This factory method creates an Archive instance corresponding to the content + * of the InstallShield compressed file with the given name. + * + * May return 0 in case of a failure. + */ +Common::Archive *makeInstallShieldArchive(const Common::String &name); + +} // End of namespace AGOS + +#endif diff --git a/engines/agos/intern.h b/engines/agos/intern.h index 18f56be4a4..773b9c15bd 100644 --- a/engines/agos/intern.h +++ b/engines/agos/intern.h @@ -193,6 +193,9 @@ struct TimeEvent { }; struct GameSpecificSettings { + const char *base_filename; + const char *restore_filename; + const char *tbl_filename; const char *effects_filename; const char *speech_filename; }; @@ -251,7 +254,8 @@ enum GameFeatures { GF_32COLOR = 1 << 5, GF_EGA = 1 << 6, GF_PLANAR = 1 << 7, - GF_DEMO = 1 << 8 + GF_DEMO = 1 << 8, + GF_PACKED = 1 << 9 }; enum GameFileTypes { diff --git a/engines/agos/module.mk b/engines/agos/module.mk index 7069d8005b..7ae5e17bf2 100644 --- a/engines/agos/module.mk +++ b/engines/agos/module.mk @@ -51,6 +51,7 @@ ifdef ENABLE_AGOS2 MODULE_OBJS += \ animation.o \ feeble.o \ + installshield_cab.o \ oracle.o \ script_dp.o \ script_ff.o \ diff --git a/engines/agos/res.cpp b/engines/agos/res.cpp index a71d4d8150..fe7bcd62a9 100644 --- a/engines/agos/res.cpp +++ b/engines/agos/res.cpp @@ -31,11 +31,30 @@ #include "agos/agos.h" #include "agos/intern.h" #include "agos/sound.h" +#include "agos/installshield_cab.h" #include "common/zlib.h" namespace AGOS { +ArchiveMan::ArchiveMan() { + _fallBack = false; +} + +#ifdef ENABLE_AGOS2 +void ArchiveMan::registerArchive(const Common::String &filename, int priority) { + add(filename, makeInstallShieldArchive(filename), priority); +} +#endif + +Common::SeekableReadStream *ArchiveMan::open(const Common::String &filename) { + if (_fallBack && SearchMan.hasFile(filename)) { + return SearchMan.createReadStreamForMember(filename); + } + + return createReadStreamForMember(filename); +} + #ifdef ENABLE_AGOS2 uint16 AGOSEngine_Feeble::to16Wrapper(uint value) { return TO_LE_16(value); @@ -150,21 +169,21 @@ int AGOSEngine::allocGamePcVars(Common::SeekableReadStream *in) { } void AGOSEngine_PN::loadGamePcFile() { - Common::File in; + Common::SeekableReadStream *in; if (getFileName(GAME_BASEFILE) != NULL) { // Read dataBase - in.open(getFileName(GAME_BASEFILE)); - if (in.isOpen() == false) { + in = _archives.open(getFileName(GAME_BASEFILE)); + if (!in) { error("loadGamePcFile: Can't load database file '%s'", getFileName(GAME_BASEFILE)); } - _dataBaseSize = in.size(); + _dataBaseSize = in->size(); _dataBase = (byte *)malloc(_dataBaseSize); if (_dataBase == NULL) error("loadGamePcFile: Out of memory for dataBase"); - in.read(_dataBase, _dataBaseSize); - in.close(); + in->read(_dataBase, _dataBaseSize); + delete in; if (_dataBase[31] != 0) error("Later version of system requested"); @@ -172,17 +191,17 @@ void AGOSEngine_PN::loadGamePcFile() { if (getFileName(GAME_TEXTFILE) != NULL) { // Read textBase - in.open(getFileName(GAME_TEXTFILE)); - if (in.isOpen() == false) { + in = _archives.open(getFileName(GAME_TEXTFILE)); + if (!in) { error("loadGamePcFile: Can't load textbase file '%s'", getFileName(GAME_TEXTFILE)); } - _textBaseSize = in.size(); + _textBaseSize = in->size(); _textBase = (byte *)malloc(_textBaseSize); if (_textBase == NULL) error("loadGamePcFile: Out of memory for textBase"); - in.read(_textBase, _textBaseSize); - in.close(); + in->read(_textBase, _textBaseSize); + delete in; if (_textBase[getlong(30L)] != 128) error("Unknown compression format"); @@ -190,20 +209,20 @@ void AGOSEngine_PN::loadGamePcFile() { } void AGOSEngine::loadGamePcFile() { - Common::File in; + Common::SeekableReadStream *in; int fileSize; if (getFileName(GAME_BASEFILE) != NULL) { /* Read main gamexx file */ - in.open(getFileName(GAME_BASEFILE)); - if (in.isOpen() == false) { + in = _archives.open(getFileName(GAME_BASEFILE)); + if (!in) { error("loadGamePcFile: Can't load gamexx file '%s'", getFileName(GAME_BASEFILE)); } if (getFeatures() & GF_CRUNCHED_GAMEPC) { - uint srcSize = in.size(); + uint srcSize = in->size(); byte *srcBuf = (byte *)malloc(srcSize); - in.read(srcBuf, srcSize); + in->read(srcBuf, srcSize); uint dstSize = READ_BE_UINT32(srcBuf + srcSize - 4); byte *dstBuf = (byte *)malloc(dstSize); @@ -214,25 +233,25 @@ void AGOSEngine::loadGamePcFile() { readGamePcFile(&stream); free(dstBuf); } else { - readGamePcFile(&in); + readGamePcFile(in); } - in.close(); + delete in; } if (getFileName(GAME_TBLFILE) != NULL) { /* Read list of TABLE resources */ - in.open(getFileName(GAME_TBLFILE)); - if (in.isOpen() == false) { + in = _archives.open(getFileName(GAME_TBLFILE)); + if (!in) { error("loadGamePcFile: Can't load table resources file '%s'", getFileName(GAME_TBLFILE)); } - fileSize = in.size(); + fileSize = in->size(); _tblList = (byte *)malloc(fileSize); if (_tblList == NULL) error("loadGamePcFile: Out of memory for strip table list"); - in.read(_tblList, fileSize); - in.close(); + in->read(_tblList, fileSize); + delete in; /* Remember the current state */ _subroutineListOrg = _subroutineList; @@ -242,71 +261,71 @@ void AGOSEngine::loadGamePcFile() { if (getFileName(GAME_STRFILE) != NULL) { /* Read list of TEXT resources */ - in.open(getFileName(GAME_STRFILE)); - if (in.isOpen() == false) + in = _archives.open(getFileName(GAME_STRFILE)); + if (!in) error("loadGamePcFile: Can't load text resources file '%s'", getFileName(GAME_STRFILE)); - fileSize = in.size(); + fileSize = in->size(); _strippedTxtMem = (byte *)malloc(fileSize); if (_strippedTxtMem == NULL) error("loadGamePcFile: Out of memory for strip text list"); - in.read(_strippedTxtMem, fileSize); - in.close(); + in->read(_strippedTxtMem, fileSize); + delete in; } if (getFileName(GAME_STATFILE) != NULL) { /* Read list of ROOM STATE resources */ - in.open(getFileName(GAME_STATFILE)); - if (in.isOpen() == false) { + in = _archives.open(getFileName(GAME_STATFILE)); + if (!in) { error("loadGamePcFile: Can't load state resources file '%s'", getFileName(GAME_STATFILE)); } - _numRoomStates = in.size() / 8; + _numRoomStates = in->size() / 8; _roomStates = (RoomState *)calloc(_numRoomStates, sizeof(RoomState)); if (_roomStates == NULL) error("loadGamePcFile: Out of memory for room state list"); for (uint s = 0; s < _numRoomStates; s++) { - uint16 num = in.readUint16BE() - (_itemArrayInited - 2); + uint16 num = in->readUint16BE() - (_itemArrayInited - 2); - _roomStates[num].state = in.readUint16BE(); - _roomStates[num].classFlags = in.readUint16BE(); - _roomStates[num].roomExitStates = in.readUint16BE(); + _roomStates[num].state = in->readUint16BE(); + _roomStates[num].classFlags = in->readUint16BE(); + _roomStates[num].roomExitStates = in->readUint16BE(); } - in.close(); + delete in; } if (getFileName(GAME_RMSLFILE) != NULL) { /* Read list of ROOM ITEMS resources */ - in.open(getFileName(GAME_RMSLFILE)); - if (in.isOpen() == false) { + in = _archives.open(getFileName(GAME_RMSLFILE)); + if (!in) { error("loadGamePcFile: Can't load room resources file '%s'", getFileName(GAME_RMSLFILE)); } - fileSize = in.size(); + fileSize = in->size(); _roomsList = (byte *)malloc(fileSize); if (_roomsList == NULL) error("loadGamePcFile: Out of memory for room items list"); - in.read(_roomsList, fileSize); - in.close(); + in->read(_roomsList, fileSize); + delete in; } if (getFileName(GAME_XTBLFILE) != NULL) { /* Read list of XTABLE resources */ - in.open(getFileName(GAME_XTBLFILE)); - if (in.isOpen() == false) { + in = _archives.open(getFileName(GAME_XTBLFILE)); + if (!in) { error("loadGamePcFile: Can't load xtable resources file '%s'", getFileName(GAME_XTBLFILE)); } - fileSize = in.size(); + fileSize = in->size(); _xtblList = (byte *)malloc(fileSize); if (_xtblList == NULL) error("loadGamePcFile: Out of memory for strip xtable list"); - in.read(_xtblList, fileSize); - in.close(); + in->read(_xtblList, fileSize); + delete in; /* Remember the current state */ _xsubroutineListOrg = _subroutineList; diff --git a/engines/agos/res_snd.cpp b/engines/agos/res_snd.cpp index 9a04ce2d26..1d4e2d1060 100644 --- a/engines/agos/res_snd.cpp +++ b/engines/agos/res_snd.cpp @@ -450,17 +450,17 @@ static const char *dimpSoundList[32] = { void AGOSEngine::loadSoundFile(const char* filename) { - Common::File in; + Common::SeekableReadStream *in; - in.open(filename); - if (in.isOpen() == false) + in = _archives.open(filename); + if (!in) error("loadSound: Can't load %s", filename); - uint32 dstSize = in.size(); + uint32 dstSize = in->size(); byte *dst = (byte *)malloc(dstSize); - if (in.read(dst, dstSize) != dstSize) + if (in->read(dst, dstSize) != dstSize) error("loadSound: Read failed"); - in.close(); + delete in; _sound->playSfxData(dst, 0, 0, 0); } @@ -469,21 +469,21 @@ void AGOSEngine::loadSound(uint16 sound, int16 pan, int16 vol, uint16 type) { byte *dst; if (getGameId() == GID_DIMP) { - Common::File in; + Common::SeekableReadStream *in; char filename[15]; assert(sound >= 1 && sound <= 32); sprintf(filename, "%s.wav", dimpSoundList[sound - 1]); - in.open(filename); - if (in.isOpen() == false) + in = _archives.open(filename); + if (!in) error("loadSound: Can't load %s", filename); - uint32 dstSize = in.size(); + uint32 dstSize = in->size(); dst = (byte *)malloc(dstSize); - if (in.read(dst, dstSize) != dstSize) + if (in->read(dst, dstSize) != dstSize) error("loadSound: Read failed"); - in.close(); + delete in; } else if (getFeatures() & GF_ZLIBCOMP) { char filename[15]; diff --git a/engines/agos/rooms.cpp b/engines/agos/rooms.cpp index f2629e419e..fb7b313e9c 100644 --- a/engines/agos/rooms.cpp +++ b/engines/agos/rooms.cpp @@ -401,9 +401,9 @@ bool AGOSEngine::loadRoomItems(uint16 room) { filename[i] = 0; p++; - for (;;) { - _roomsListPtr = p; + _roomsListPtr = p; + for (;;) { minNum = READ_BE_UINT16(p); p += 2; if (minNum == 0) break; diff --git a/engines/agos/saveload.cpp b/engines/agos/saveload.cpp index 10830db002..6779eabdbf 100644 --- a/engines/agos/saveload.cpp +++ b/engines/agos/saveload.cpp @@ -1019,9 +1019,7 @@ bool AGOSEngine::loadGame(const char *filename, bool restartMode) { if (restartMode) { // Load restart state - Common::File *file = new Common::File(); - file->open(filename); - f = file; + f = _archives.open(filename); } else { f = _saveFileMan->openForLoading(filename); } @@ -1195,9 +1193,7 @@ bool AGOSEngine_Elvira2::loadGame(const char *filename, bool restartMode) { if (restartMode) { // Load restart state - Common::File *file = new Common::File(); - file->open(filename); - f = file; + f = _archives.open(filename); } else { f = _saveFileMan->openForLoading(filename); } diff --git a/engines/agos/subroutine.cpp b/engines/agos/subroutine.cpp index bd9abb16b5..10c1c1aaf9 100644 --- a/engines/agos/subroutine.cpp +++ b/engines/agos/subroutine.cpp @@ -258,22 +258,21 @@ void AGOSEngine::endCutscene() { _runScriptReturn1 = true; } -Common::File *AGOSEngine::openTablesFile(const char *filename) { +Common::SeekableReadStream *AGOSEngine::openTablesFile(const char *filename) { if (getFeatures() & GF_OLD_BUNDLE) return openTablesFile_simon1(filename); else return openTablesFile_gme(filename); } -Common::File *AGOSEngine::openTablesFile_simon1(const char *filename) { - Common::File *fo = new Common::File(); - fo->open(filename); - if (fo->isOpen() == false) +Common::SeekableReadStream *AGOSEngine::openTablesFile_simon1(const char *filename) { + Common::SeekableReadStream *in = _archives.open(filename); + if (!in) error("openTablesFile: Can't open '%s'", filename); - return fo; + return in; } -Common::File *AGOSEngine::openTablesFile_gme(const char *filename) { +Common::SeekableReadStream *AGOSEngine::openTablesFile_gme(const char *filename) { uint res; uint32 offs; @@ -287,7 +286,7 @@ Common::File *AGOSEngine::openTablesFile_gme(const char *filename) { bool AGOSEngine::loadTablesIntoMem(uint16 subrId) { byte *p; uint16 min_num, max_num, file_num; - Common::File *in; + Common::SeekableReadStream *in; char filename[30]; if (_tblList == NULL) @@ -336,7 +335,7 @@ bool AGOSEngine::loadTablesIntoMem(uint16 subrId) { bool AGOSEngine_Waxworks::loadTablesIntoMem(uint16 subrId) { byte *p; uint min_num, max_num; - Common::File *in; + Common::SeekableReadStream *in; p = _tblList; if (p == NULL) @@ -403,7 +402,7 @@ bool AGOSEngine::loadXTablesIntoMem(uint16 subrId) { int i; uint min_num, max_num; char filename[30]; - Common::File *in; + Common::SeekableReadStream *in; p = _xtblList; if (p == NULL) @@ -453,9 +452,8 @@ bool AGOSEngine::loadXTablesIntoMem(uint16 subrId) { return 0; } -void AGOSEngine::closeTablesFile(Common::File *in) { +void AGOSEngine::closeTablesFile(Common::SeekableReadStream *in) { if (getFeatures() & GF_OLD_BUNDLE) { - in->close(); delete in; } } diff --git a/engines/gob/console.cpp b/engines/gob/console.cpp index b8aed37727..e7296fb81b 100644 --- a/engines/gob/console.cpp +++ b/engines/gob/console.cpp @@ -29,6 +29,7 @@ namespace Gob { GobConsole::GobConsole(GobEngine *vm) : GUI::Debugger(), _vm(vm) { DCmd_Register("varSize", WRAP_METHOD(GobConsole, cmd_varSize)); + DCmd_Register("dumpVars", WRAP_METHOD(GobConsole, cmd_dumpVars)); DCmd_Register("var8", WRAP_METHOD(GobConsole, cmd_var8)); DCmd_Register("var16", WRAP_METHOD(GobConsole, cmd_var16)); DCmd_Register("var32", WRAP_METHOD(GobConsole, cmd_var32)); @@ -44,6 +45,23 @@ bool GobConsole::cmd_varSize(int argc, const char **argv) { return true; } +bool GobConsole::cmd_dumpVars(int argc, const char **argv) { + if (!_vm->_inter->_variables) + return true; + + Common::DumpFile file; + + if (!file.open("variables.dmp")) + return true; + + file.write(_vm->_inter->_variables->getAddressOff8(0), _vm->_inter->_variables->getSize()); + + file.flush(); + file.close(); + + return true; +} + bool GobConsole::cmd_var8(int argc, const char **argv) { if (argc == 1) { DebugPrintf("Usage: var8 <var offset> (<value>)\n"); diff --git a/engines/gob/console.h b/engines/gob/console.h index b9f9b81d0e..b9c3f5ed70 100644 --- a/engines/gob/console.h +++ b/engines/gob/console.h @@ -38,6 +38,7 @@ private: GobEngine *_vm; bool cmd_varSize(int argc, const char **argv); + bool cmd_dumpVars(int argc, const char **argv); bool cmd_var8(int argc, const char **argv); bool cmd_var16(int argc, const char **argv); bool cmd_var32(int argc, const char **argv); diff --git a/engines/gob/detection_tables.h b/engines/gob/detection_tables.h index 04173837da..4c1ff9a8e3 100644 --- a/engines/gob/detection_tables.h +++ b/engines/gob/detection_tables.h @@ -2496,7 +2496,7 @@ static const GOBGameDescription gameDescriptions[] = { GUIO_NOSUBTITLES | GUIO_NOSPEECH }, kGameTypeGeisha, - kFeaturesEGA, + kFeaturesEGA | kFeaturesAdLib, "disk1.stk", "intro.tot", 0 }, { @@ -2510,7 +2510,7 @@ static const GOBGameDescription gameDescriptions[] = { GUIO_NOSUBTITLES | GUIO_NOSPEECH }, kGameTypeGeisha, - kFeaturesEGA, + kFeaturesEGA | kFeaturesAdLib, "disk1.stk", "intro.tot", 0 }, { diff --git a/engines/gob/draw_v1.cpp b/engines/gob/draw_v1.cpp index 3873a99d5f..064c74958a 100644 --- a/engines/gob/draw_v1.cpp +++ b/engines/gob/draw_v1.cpp @@ -44,8 +44,8 @@ void Draw_v1::initScreen() { _backSurface = _vm->_video->initSurfDesc(320, 200); _frontSurface = _vm->_global->_primarySurfDesc; - _cursorSprites = _vm->_video->initSurfDesc(32, 16, 2); - _scummvmCursor = _vm->_video->initSurfDesc(16, 16, SCUMMVM_CURSOR); + _cursorSprites = _vm->_video->initSurfDesc(_cursorWidth * 2, _cursorHeight, 2); + _scummvmCursor = _vm->_video->initSurfDesc(_cursorWidth , _cursorHeight, SCUMMVM_CURSOR); } void Draw_v1::closeScreen() { diff --git a/engines/gob/game.cpp b/engines/gob/game.cpp index 926027e15d..7b43e9c4d7 100644 --- a/engines/gob/game.cpp +++ b/engines/gob/game.cpp @@ -492,9 +492,6 @@ void Game::prepareStart() { _vm->_draw->_noInvalidated = true; _vm->_draw->_applyPal = false; _vm->_draw->_paletteCleared = false; - _vm->_draw->_cursorWidth = 16; - _vm->_draw->_cursorHeight = 16; - _vm->_draw->_transparentCursor = 1; for (int i = 0; i < 40; i++) { _vm->_draw->_cursorAnimLow[i] = -1; diff --git a/engines/gob/gob.cpp b/engines/gob/gob.cpp index 59a96951dd..7bb7928406 100644 --- a/engines/gob/gob.cpp +++ b/engines/gob/gob.cpp @@ -414,7 +414,7 @@ bool GobEngine::initGameParts() { break; case kGameTypeGeisha: - _init = new Init_v1(this); + _init = new Init_Geisha(this); _video = new Video_v1(this); _inter = new Inter_Geisha(this); _mult = new Mult_v1(this); diff --git a/engines/gob/init.h b/engines/gob/init.h index 1cb2904099..e8c948c72e 100644 --- a/engines/gob/init.h +++ b/engines/gob/init.h @@ -56,6 +56,14 @@ public: void initVideo(); }; +class Init_Geisha : public Init_v1 { +public: + Init_Geisha(GobEngine *vm); + ~Init_Geisha(); + + void initVideo(); +}; + class Init_v2 : public Init_v1 { public: Init_v2(GobEngine *vm); diff --git a/engines/gob/init_geisha.cpp b/engines/gob/init_geisha.cpp new file mode 100644 index 0000000000..01081a5af6 --- /dev/null +++ b/engines/gob/init_geisha.cpp @@ -0,0 +1,47 @@ +/* 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. + * + */ + +#include "common/endian.h" + +#include "gob/gob.h" +#include "gob/init.h" +#include "gob/global.h" +#include "gob/draw.h" +#include "gob/video.h" + +namespace Gob { + +Init_Geisha::Init_Geisha(GobEngine *vm) : Init_v1(vm) { +} + +Init_Geisha::~Init_Geisha() { +} + +void Init_Geisha::initVideo() { + Init_v1::initVideo(); + + _vm->_draw->_cursorWidth = 16; + _vm->_draw->_cursorHeight = 23; + _vm->_draw->_transparentCursor = 1; +} + +} // End of namespace Gob diff --git a/engines/gob/init_v1.cpp b/engines/gob/init_v1.cpp index 6772a13eb0..25d521aca6 100644 --- a/engines/gob/init_v1.cpp +++ b/engines/gob/init_v1.cpp @@ -52,6 +52,10 @@ void Init_v1::initVideo() { _vm->_global->_pPaletteDesc->unused2 = _vm->_global->_unusedPalette2; _vm->_video->initSurfDesc(320, 200, PRIMARY_SURFACE); + + _vm->_draw->_cursorWidth = 16; + _vm->_draw->_cursorHeight = 16; + _vm->_draw->_transparentCursor = 1; } } // End of namespace Gob diff --git a/engines/gob/init_v2.cpp b/engines/gob/init_v2.cpp index f10d586a34..1289d561ea 100644 --- a/engines/gob/init_v2.cpp +++ b/engines/gob/init_v2.cpp @@ -62,6 +62,10 @@ void Init_v2::initVideo() { _vm->_global->_pPaletteDesc->unused2 = _vm->_global->_unusedPalette2; _vm->_video->initSurfDesc(_vm->_video->_surfWidth, _vm->_video->_surfHeight, PRIMARY_SURFACE); + + _vm->_draw->_cursorWidth = 16; + _vm->_draw->_cursorHeight = 16; + _vm->_draw->_transparentCursor = 1; } } // End of namespace Gob diff --git a/engines/gob/inter.h b/engines/gob/inter.h index ecbf22610d..84180f407d 100644 --- a/engines/gob/inter.h +++ b/engines/gob/inter.h @@ -344,7 +344,19 @@ protected: virtual void setupOpcodesFunc(); virtual void setupOpcodesGob(); + void oGeisha_loadCursor(OpFuncParams ¶ms); + void oGeisha_goblinFunc(OpFuncParams ¶ms); void oGeisha_loadSound(OpFuncParams ¶ms); + void oGeisha_checkData(OpFuncParams ¶ms); + + void oGeisha_gamePenetration(OpGobParams ¶ms); + void oGeisha_gameDiving(OpGobParams ¶ms); + void oGeisha_loadTitleMusic(OpGobParams ¶ms); + void oGeisha_playMusic(OpGobParams ¶ms); + void oGeisha_stopMusic(OpGobParams ¶ms); + + void oGeisha_caress1(OpGobParams ¶ms); + void oGeisha_caress2(OpGobParams ¶ms); int16 loadSound(int16 slot); }; diff --git a/engines/gob/inter_geisha.cpp b/engines/gob/inter_geisha.cpp index e3f3a24a98..658f2346f4 100644 --- a/engines/gob/inter_geisha.cpp +++ b/engines/gob/inter_geisha.cpp @@ -26,7 +26,10 @@ #include "gob/inter.h" #include "gob/dataio.h" #include "gob/script.h" +#include "gob/resources.h" #include "gob/game.h" +#include "gob/draw.h" +#include "gob/video.h" #include "gob/sound/sound.h" #include "gob/sound/sounddesc.h" @@ -47,16 +50,46 @@ void Inter_Geisha::setupOpcodesDraw() { void Inter_Geisha::setupOpcodesFunc() { Inter_v1::setupOpcodesFunc(); + OPCODEFUNC(0x03, oGeisha_loadCursor); + OPCODEFUNC(0x25, oGeisha_goblinFunc); OPCODEFUNC(0x3A, oGeisha_loadSound); + OPCODEFUNC(0x3F, oGeisha_checkData); + + OPCODEGOB(0, oGeisha_gamePenetration); + OPCODEGOB(1, oGeisha_gameDiving); + OPCODEGOB(2, oGeisha_loadTitleMusic); + OPCODEGOB(3, oGeisha_playMusic); + OPCODEGOB(4, oGeisha_stopMusic); + OPCODEGOB(6, oGeisha_caress1); + OPCODEGOB(7, oGeisha_caress2); } void Inter_Geisha::setupOpcodesGob() { } +void Inter_Geisha::oGeisha_loadCursor(OpFuncParams ¶ms) { + if (_vm->_game->_script->peekByte(1) & 0x80) + warning("Geisha Stub: oGeisha_loadCursor: script[1] & 0x80"); + + o1_loadCursor(params); +} + void Inter_Geisha::oGeisha_loadSound(OpFuncParams ¶ms) { loadSound(-1); } +void Inter_Geisha::oGeisha_goblinFunc(OpFuncParams ¶ms) { + OpGobParams gobParams; + int16 cmd; + + cmd = _vm->_game->_script->readInt16(); + + gobParams.paramCount = _vm->_game->_script->readInt16(); + gobParams.extraData = cmd; + + executeOpcodeGob(cmd, gobParams); +} + int16 Inter_Geisha::loadSound(int16 slot) { const char *sndFile = _vm->_game->_script->evalString(); @@ -80,4 +113,74 @@ int16 Inter_Geisha::loadSound(int16 slot) { return 0; } +void Inter_Geisha::oGeisha_checkData(OpFuncParams ¶ms) { + const char *file = _vm->_game->_script->evalString(); + int16 varOff = _vm->_game->_script->readVarIndex(); + + Common::String fileName(file); + + fileName.toLowercase(); + if (fileName.hasSuffix(".0ot")) + fileName.setChar('t', fileName.size() - 3); + + if (!_vm->_dataIO->hasFile(fileName)) { + warning("File \"%s\" not found", fileName.c_str()); + WRITE_VAR_OFFSET(varOff, (uint32) -1); + } else + WRITE_VAR_OFFSET(varOff, 50); // "handle" between 50 and 128 = in archive +} + +void Inter_Geisha::oGeisha_gamePenetration(OpGobParams ¶ms) { + uint16 var1 = _vm->_game->_script->readUint16(); + uint16 var2 = _vm->_game->_script->readUint16(); + uint16 var3 = _vm->_game->_script->readUint16(); + uint16 var4 = _vm->_game->_script->readUint16(); + + WRITE_VAR_UINT32(var4, 0); + + warning("Geisha Stub: Minigame \"Penetration\": %d, %d, %d, %d", var1, var2, var3, var4); + + // Fudge a win for now + WRITE_VAR_UINT32(var4, 1); +} + +void Inter_Geisha::oGeisha_gameDiving(OpGobParams ¶ms) { + uint16 var1 = _vm->_game->_script->readUint16(); + uint16 var2 = _vm->_game->_script->readUint16(); + uint16 var3 = _vm->_game->_script->readUint16(); + + WRITE_VAR_UINT32(var3, 1); + + warning("Geisha Stub: Minigame \"Diving\": %d, %d, %d", var1, var2, var3); + + // Fudge a win for now + WRITE_VAR_UINT32(var3, 0); +} + +void Inter_Geisha::oGeisha_loadTitleMusic(OpGobParams ¶ms) { + _vm->_sound->adlibLoadTBR("geisha.tbr"); + _vm->_sound->adlibLoadMDY("geisha.mdy"); +} + +void Inter_Geisha::oGeisha_playMusic(OpGobParams ¶ms) { + // TODO: The MDYPlayer is still broken! + warning("Geisha Stub: oGeisha_playMusic"); + // _vm->_sound->adlibPlay(); +} + +void Inter_Geisha::oGeisha_stopMusic(OpGobParams ¶ms) { + _vm->_sound->adlibStop(); + _vm->_sound->adlibUnload(); +} + +void Inter_Geisha::oGeisha_caress1(OpGobParams ¶ms) { + if (_vm->_draw->_spritesArray[0]) + _vm->_video->drawPackedSprite("hp1.cmp", *_vm->_draw->_spritesArray[0]); +} + +void Inter_Geisha::oGeisha_caress2(OpGobParams ¶ms) { + if (_vm->_draw->_spritesArray[1]) + _vm->_video->drawPackedSprite("hpsc1.cmp", *_vm->_draw->_spritesArray[1]); +} + } // End of namespace Gob diff --git a/engines/gob/inter_v1.cpp b/engines/gob/inter_v1.cpp index 8d675d1500..0eb8be1a03 100644 --- a/engines/gob/inter_v1.cpp +++ b/engines/gob/inter_v1.cpp @@ -658,6 +658,20 @@ void Inter_v1::o1_callSub(OpFuncParams ¶ms) { return; } + // A cheat to get around the stupid mastermind puzzle in Geisha, + // while we're still testing it + if ((_vm->getGameType() == kGameTypeGeisha) && (offset == 12934) && + _vm->isCurrentTot("hard.tot") && _vm->_inter->_variables) { + + uint32 digit1 = READ_VARO_UINT32(0x768); + uint32 digit2 = READ_VARO_UINT32(0x76C); + uint32 digit3 = READ_VARO_UINT32(0x770); + uint32 digit4 = READ_VARO_UINT32(0x774); + uint32 digit5 = READ_VARO_UINT32(0x778); + + warning("Mastermind solution: %d %d %d %d %d", digit1, digit2, digit3, digit4, digit5); + } + // Skipping the copy protection screen in Gobliiins if (!_vm->_copyProtection && (_vm->getGameType() == kGameTypeGob1) && (offset == 3905) && _vm->isCurrentTot(_vm->_startTot)) { diff --git a/engines/gob/module.mk b/engines/gob/module.mk index 7c634dfaa1..b85c387734 100644 --- a/engines/gob/module.mk +++ b/engines/gob/module.mk @@ -25,6 +25,7 @@ MODULE_OBJS := \ iniconfig.o \ init.o \ init_v1.o \ + init_geisha.o \ init_v2.o \ init_fascin.o \ init_v3.o \ diff --git a/engines/mohawk/graphics.cpp b/engines/mohawk/graphics.cpp index 35c9d478d8..c4326d175f 100644 --- a/engines/mohawk/graphics.cpp +++ b/engines/mohawk/graphics.cpp @@ -672,12 +672,50 @@ void MystGraphics::simulatePreviousDrawDelay(const Common::Rect &dest) { _nextAllowedDrawTime = time + _constantDrawDelay + dest.height() * dest.width() / _proportionalDrawDelay; } +void MystGraphics::copyBackBufferToScreenWithSaturation(int16 saturation) { + Graphics::Surface *screen = _vm->_system->lockScreen(); + + for (uint16 y = 0; y < _viewport.height(); y++) + for (uint16 x = 0; x < _viewport.width(); x++) { + uint32 color; + uint8 r, g, b; + + if (_pixelFormat.bytesPerPixel == 2) + color = *(const uint16 *)_backBuffer->getBasePtr(x, y); + else + color = *(const uint32 *)_backBuffer->getBasePtr(x, y); + + _pixelFormat.colorToRGB(color, r, g, b); + + r = CLIP<int16>((int16)r - saturation, 0, 255); + g = CLIP<int16>((int16)g - saturation, 0, 255); + b = CLIP<int16>((int16)b - saturation, 0, 255); + + color = _pixelFormat.RGBToColor(r, g, b); + + if (_pixelFormat.bytesPerPixel == 2) { + uint16 *dst = (uint16 *)screen->getBasePtr(x, y); + *dst = color; + } else { + uint32 *dst = (uint32 *)screen->getBasePtr(x, y); + *dst = color; + } + } + + _vm->_system->unlockScreen(); + _vm->_system->updateScreen(); +} + void MystGraphics::fadeToBlack() { - // TODO: Implement + for (int16 i = 0; i < 256; i += 32) { + copyBackBufferToScreenWithSaturation(i); + } } void MystGraphics::fadeFromBlack() { - // TODO: Implement + for (int16 i = 256; i >= 0; i -= 32) { + copyBackBufferToScreenWithSaturation(i); + } } #endif // ENABLE_MYST diff --git a/engines/mohawk/graphics.h b/engines/mohawk/graphics.h index 96357bbff1..463608a2aa 100644 --- a/engines/mohawk/graphics.h +++ b/engines/mohawk/graphics.h @@ -136,6 +136,7 @@ protected: MohawkSurface *decodeImage(uint16 id); MohawkEngine *getVM() { return (MohawkEngine *)_vm; } void simulatePreviousDrawDelay(const Common::Rect &dest); + void copyBackBufferToScreenWithSaturation(int16 saturation); private: MohawkEngine_Myst *_vm; diff --git a/engines/mohawk/myst_stacks/channelwood.cpp b/engines/mohawk/myst_stacks/channelwood.cpp index 0dd69a673a..9ca47cc92a 100644 --- a/engines/mohawk/myst_stacks/channelwood.cpp +++ b/engines/mohawk/myst_stacks/channelwood.cpp @@ -159,6 +159,16 @@ uint16 Channelwood::getVar(uint16 var) { return ((_state.waterValveStates & 0xe2) == 0x80) ? 1 : 0; case 30: // Door State return _doorOpened; + case 31: // Water flowing in pipe fork ? + // 0 -> keep sound. + // 1 -> not flowing. + // 2 --> flowing. + if ((_state.waterValveStates & 0xe2) == 0x82) // From left. + return 2; + if ((_state.waterValveStates & 0xf4) == 0xa0) // From right. + return 1; + + return 0; case 32: // Sound - Water Flowing in Pipe to Book Room Elevator return ((_state.waterValveStates & 0xf8) == 0xb0 && _state.pipeState) ? 1 : 0; case 33: // Channelwood Lower Walkway to Upper Walkway Spiral Stair Upper Door State diff --git a/engines/mohawk/myst_stacks/demo.cpp b/engines/mohawk/myst_stacks/demo.cpp index c9e806655e..fbad7dc384 100644 --- a/engines/mohawk/myst_stacks/demo.cpp +++ b/engines/mohawk/myst_stacks/demo.cpp @@ -84,7 +84,10 @@ void Demo::o_stopIntro(uint16 op, uint16 var, uint16 argc, uint16 *argv) { void Demo::o_fadeFromBlack(uint16 op, uint16 var, uint16 argc, uint16 *argv) { debugC(kDebugScript, "Opcode %d: Fade from black", op); - _vm->_gfx->fadeFromBlack(); + + // FIXME: This glitches when enabled. The backbuffer is drawn to screen, + // and then the fading occurs, causing the background to appear for one frame. + // _vm->_gfx->fadeFromBlack(); } void Demo::o_fadeToBlack(uint16 op, uint16 var, uint16 argc, uint16 *argv) { @@ -101,14 +104,14 @@ void Demo::returnToMenu_run() { switch (_returnToMenuStep){ case 0: _vm->_gfx->fadeToBlack(); - _vm->changeToCard(2003, true); + _vm->changeToCard(2003, false); _vm->_gfx->fadeFromBlack(); _returnToMenuStep++; break; case 1: _vm->_gfx->fadeToBlack(); - _vm->changeToCard(2001, true); + _vm->changeToCard(2001, false); _vm->_gfx->fadeFromBlack(); _vm->_cursor->showCursor(); diff --git a/engines/mohawk/myst_stacks/dni.cpp b/engines/mohawk/myst_stacks/dni.cpp index cf28945c71..2ced265f02 100644 --- a/engines/mohawk/myst_stacks/dni.cpp +++ b/engines/mohawk/myst_stacks/dni.cpp @@ -106,7 +106,7 @@ void Dni::o_handPage(uint16 op, uint16 var, uint16 argc, uint16 *argv) { if (_globals.ending == 1 && _vm->_video->getElapsedTime(atrus) > (uint)Audio::Timestamp(0, 6801, 600).msecs()) { _globals.ending = 2; _globals.heldPage = 0; - _vm->_cursor->setCursor(kDefaultMystCursor); + _vm->setMainCursor(kDefaultMystCursor); // Play movie end (atrus leaving) _vm->_video->setVideoBounds(atrus, Audio::Timestamp(0, 14813, 600), Audio::Timestamp(0xFFFFFFFF)); diff --git a/engines/mohawk/myst_stacks/myst.cpp b/engines/mohawk/myst_stacks/myst.cpp index 66492d1200..b67b333a85 100644 --- a/engines/mohawk/myst_stacks/myst.cpp +++ b/engines/mohawk/myst_stacks/myst.cpp @@ -1350,8 +1350,10 @@ void Myst::o_generatorButtonPressed(uint16 op, uint16 var, uint16 argc, uint16 * if (_state.generatorVoltage) _vm->_sound->replaceSoundMyst(8297); - else + else { _vm->_sound->replaceSoundMyst(9297); + _vm->_sound->stopBackgroundMyst(); + } } else { if (_generatorVoltage) _vm->_sound->replaceSoundMyst(6297); diff --git a/engines/mohawk/myst_stacks/preview.cpp b/engines/mohawk/myst_stacks/preview.cpp index 1b72c85d96..31e22bb8c5 100644 --- a/engines/mohawk/myst_stacks/preview.cpp +++ b/engines/mohawk/myst_stacks/preview.cpp @@ -85,7 +85,10 @@ void Preview::o_fadeToBlack(uint16 op, uint16 var, uint16 argc, uint16 *argv) { void Preview::o_fadeFromBlack(uint16 op, uint16 var, uint16 argc, uint16 *argv) { debugC(kDebugScript, "Opcode %d: Fade from black", op); - _vm->_gfx->fadeFromBlack(); + + // FIXME: This glitches when enabled. The backbuffer is drawn to screen, + // and then the fading occurs, causing the background to appear for one frame. + // _vm->_gfx->fadeFromBlack(); } void Preview::o_stayHere(uint16 op, uint16 var, uint16 argc, uint16 *argv) { @@ -99,6 +102,7 @@ void Preview::o_stayHere(uint16 op, uint16 var, uint16 argc, uint16 *argv) { void Preview::o_speechStop(uint16 op, uint16 var, uint16 argc, uint16 *argv) { debugC(kDebugScript, "Opcode %d: Speech stop", op); + _vm->_sound->stopSound(3001); _speechRunning = false; _globals.currentAge = 2; } @@ -143,7 +147,7 @@ void Preview::speech_run() { case 2: // Go to Myst if (_currentCue >= 2) { _vm->_gfx->fadeToBlack(); - _vm->changeToCard(3002, true); + _vm->changeToCard(3002, false); _vm->_gfx->fadeFromBlack(); _speechStep++; @@ -186,7 +190,7 @@ void Preview::speech_run() { break; _vm->_gfx->fadeToBlack(); - _vm->changeToCard(3005, true); + _vm->changeToCard(3005, false); _vm->_gfx->fadeFromBlack(); _speechNextTime = time + 1000; _speechStep++; diff --git a/engines/mohawk/myst_stacks/slides.cpp b/engines/mohawk/myst_stacks/slides.cpp index 943cb90071..794793e49c 100644 --- a/engines/mohawk/myst_stacks/slides.cpp +++ b/engines/mohawk/myst_stacks/slides.cpp @@ -63,7 +63,7 @@ void Slides::runPersistentScripts() { // Used on Cards... if (_vm->_system->getMillis() > _nextCardTime) { _vm->_gfx->fadeToBlack(); - _vm->changeToCard(_nextCardID, true); + _vm->changeToCard(_nextCardID, false); _vm->_gfx->fadeFromBlack(); } } diff --git a/engines/sci/sound/midiparser_sci.cpp b/engines/sci/sound/midiparser_sci.cpp index 95b165468d..f48a68dc66 100644 --- a/engines/sci/sound/midiparser_sci.cpp +++ b/engines/sci/sound/midiparser_sci.cpp @@ -626,7 +626,11 @@ void MidiParser_SCI::parseNextEvent(EventInfo &info) { if (info.ext.type == 0x2F) {// end of track reached if (_pSnd->loop) _pSnd->loop--; - if (_pSnd->loop) { + // QFG3 abuses the hold flag. Its scripts call kDoSoundSetHold, + // but sometimes there's no hold marker in the associated songs + // (e.g. song 110, during the intro). The original interpreter + // treats this case as an infinite loop (bug #3311911). + if (_pSnd->loop || _pSnd->hold > 0) { // We need to play it again... jumpToTick(_loopTick); } else { diff --git a/engines/tsage/blue_force/blueforce_logic.cpp b/engines/tsage/blue_force/blueforce_logic.cpp index 82d70e5589..60bbddbabc 100644 --- a/engines/tsage/blue_force/blueforce_logic.cpp +++ b/engines/tsage/blue_force/blueforce_logic.cpp @@ -23,8 +23,10 @@ #include "tsage/blue_force/blueforce_logic.h" #include "tsage/blue_force/blueforce_scenes0.h" #include "tsage/blue_force/blueforce_scenes1.h" +#include "tsage/blue_force/blueforce_scenes3.h" #include "tsage/scenes.h" #include "tsage/tsage.h" +#include "tsage/graphics.h" #include "tsage/staticres.h" namespace TsAGE { @@ -33,7 +35,7 @@ namespace BlueForce { void BlueForceGame::start() { // Start the game - _globals->_sceneManager.changeScene(50); + _globals->_sceneManager.changeScene(300); _globals->_events.setCursor(CURSOR_WALK); } @@ -76,6 +78,8 @@ Scene *BlueForceGame::createScene(int sceneNumber) { case 280: error("Scene group 2 not implemented"); case 300: + // Outside Police Station + return new Scene300(); case 315: case 325: case 330: @@ -212,6 +216,13 @@ void Timer::remove() { ((Scene100 *)BF_GLOBALS._sceneManager._scene)->removeTimer(this); } +void Timer::synchronize(Serializer &s) { + EventHandler::synchronize(s); + SYNC_POINTER(_tickAction); + SYNC_POINTER(_endAction); + s.syncAsUint32LE(_endFrame); +} + void Timer::signal() { assert(_endAction); Action *action = _endAction; @@ -231,24 +242,49 @@ void Timer::dispatch() { } } -void Timer::set(uint32 delay, Action *action) { +void Timer::set(uint32 delay, Action *endAction) { assert(delay != 0); _endFrame = BF_GLOBALS._sceneHandler->getFrameDifference() + delay; - _endAction = action; + _endAction = endAction; ((SceneExt *)BF_GLOBALS._sceneManager._scene)->addTimer(this); } /*--------------------------------------------------------------------------*/ -void SceneItemType1::process(Event &event) { - if (_action) - _action->process(event); +TimerExt::TimerExt(): Timer() { + _action = NULL; } -void SceneItemType1::startMove(SceneObject *sceneObj, va_list va) { - warning("TODO: sub_1621C"); +void TimerExt::set(uint32 delay, Action *endAction, Action *newAction) { + _newAction = newAction; + Timer::set(delay, endAction); +} + +void TimerExt::synchronize(Serializer &s) { + EventHandler::synchronize(s); + SYNC_POINTER(_action); +} + +void TimerExt::remove() { + _action = NULL; + remove(); +} + +void TimerExt::signal() { + Action *endAction = _endAction; + Action *newAction = _newAction; + remove(); + + // If the end action doesn't have an action anymore, set it to the specified new action + assert(endAction); + if (!endAction->_action) + endAction->setAction(newAction); +} + +void TimerExt::dispatch() { + } /*--------------------------------------------------------------------------*/ @@ -258,6 +294,124 @@ void SceneItemType2::startMove(SceneObject *sceneObj, va_list va) { /*--------------------------------------------------------------------------*/ +void NamedObject::postInit(SceneObjectList *OwnerList) { + _lookLineNum = _talkLineNum = _useLineNum = -1; + SceneObject::postInit(); +} + +void NamedObject::synchronize(Serializer &s) { + SceneObject::synchronize(s); + s.syncAsSint16LE(_resNum); + s.syncAsSint16LE(_lookLineNum); + s.syncAsSint16LE(_talkLineNum); + s.syncAsSint16LE(_useLineNum); +} + +void NamedObject::setup(int resNum, int lookLineNum, int talkLineNum, int useLineNum, int mode, SceneItem *item) { + _resNum = resNum; + _lookLineNum = lookLineNum; + _talkLineNum = talkLineNum; + _useLineNum = useLineNum; + + switch (mode) { + case 2: + _globals->_sceneItems.push_front(this); + break; + case 4: + _globals->_sceneItems.addBefore(item, this); + break; + case 5: + _globals->_sceneItems.addAfter(item, this); + break; + default: + _globals->_sceneItems.push_back(this); + break; + } +} + +/*--------------------------------------------------------------------------*/ + +CountdownObject::CountdownObject(): NamedObject() { + _countDown = 0; +} + +void CountdownObject::synchronize(Serializer &s) { + SceneObject::synchronize(s); + s.syncAsSint16LE(_countDown); +} + +void CountdownObject::dispatch() { + int frameNum = _frame; + SceneObject::dispatch(); + + if ((frameNum != _frame) && (_countDown > 0)) { + if (--_countDown == 0) { + animate(ANIM_MODE_NONE, 0); + _frame = 1; + } + } +} + +void CountdownObject::fixCountdown(int mode, ...) { + if (mode == 8) { + va_list va; + va_start(va, mode); + + _countDown = va_arg(va, int); + animate(ANIM_MODE_8, _countDown, NULL); + va_end(va); + } +} + +/*--------------------------------------------------------------------------*/ + +FollowerObject::FollowerObject(): NamedObject() { + _object = NULL; +} + +void FollowerObject::synchronize(Serializer &s) { + NamedObject::synchronize(s); + SYNC_POINTER(_object); +} + +void FollowerObject::remove() { + NamedObject::remove(); + _object = NULL; +} + +void FollowerObject::dispatch() { + SceneObject::dispatch(); + assert(_object); + + if ((_object->_flags & OBJFLAG_HIDE) || ((_object->_visage != 307) && + ((_object->_visage != 308) || (_object->_strip != 1)))) { + hide(); + } else if ((_object->_visage != 308) || (_object->_strip != 1)) { + show(); + setStrip(_object->_strip); + setPosition(_object->_position, _object->_yDiff); + } +} + +void FollowerObject::reposition() { + assert(_object); + setStrip(_object->_strip); + setPosition(_object->_position, _object->_yDiff); + reposition(); +} + +void FollowerObject::setup(SceneObject *object, int visage, int frameNum, int yDiff) { + SceneObject::postInit(); + _object = object; + _yDiff = yDiff; + setVisage(visage); + setFrame(frameNum); + + dispatch(); +} + +/*--------------------------------------------------------------------------*/ + SceneExt::SceneExt(): Scene() { warning("TODO: dword_503AA/dword_503AE/dword_53030"); @@ -352,6 +506,142 @@ void SceneHandlerExt::process(Event &event) { /*--------------------------------------------------------------------------*/ +VisualSpeaker::VisualSpeaker(): Speaker() { + _textWidth = 312; + _color1 = 19; + _hideObjects = false; + _removeObject1 = false; + _removeObject2 = false; + _field20E = 160; + _fontNumber = 4; + _color2 = 82; + _offsetPos = Common::Point(4, 170); + _numFrames = 0; +} + +void VisualSpeaker::remove() { + if (_removeObject2) + _object2.remove(); + if (_removeObject1) + _object1.remove(); + + Speaker::remove(); +} + +void VisualSpeaker::synchronize(Serializer &s) { + Speaker::synchronize(s); + + s.syncAsByte(_removeObject1); + s.syncAsByte(_removeObject2); + s.syncAsSint16LE(_field20C); + s.syncAsSint16LE(_field20E); + s.syncAsSint16LE(_numFrames); + s.syncAsSint16LE(_offsetPos.x); + s.syncAsSint16LE(_offsetPos.y); +} + +void VisualSpeaker::proc12(Action *action) { + Speaker::proc12(action); + _textPos = Common::Point(_offsetPos.x + BF_GLOBALS._sceneManager._scene->_sceneBounds.left, + _offsetPos.y + BF_GLOBALS._sceneManager._scene->_sceneBounds.top); + _numFrames = 0; +} + +void VisualSpeaker::setText(const Common::String &msg) { + BF_GLOBALS._events.waitForPress(); + _objectList.draw(); + + _sceneText._color1 = _color1; + _sceneText._color2 = _color2; + _sceneText._color3 = _color3; + _sceneText._width = _textWidth; + _sceneText._fontNumber = _fontNumber; + _sceneText._textMode = _textMode; + _sceneText.setup(msg); + + // Get the string bounds + GfxFont f; + f.setFontNumber(_fontNumber); + Rect bounds; + f.getStringBounds(msg.c_str(), bounds, _textWidth); + + // Set the position for the text + switch (_textMode) { + case ALIGN_LEFT: + case ALIGN_JUSTIFIED: + _sceneText.setPosition(_textPos); + break; + case ALIGN_CENTER: + _sceneText.setPosition(Common::Point(_textPos.x + (_textWidth - bounds.width()) / 2, _textPos.y)); + break; + case ALIGN_RIGHT: + _sceneText.setPosition(Common::Point(_textPos.x + _textWidth - bounds.width(), _textPos.y)); + break; + default: + break; + } + + // Ensure the text is in the foreground + _sceneText.fixPriority(256); + + // Count the number of words (by spaces) in the string + const char *s = msg.c_str(); + int spaceCount = 0; + while (*s) { + if (*s++ == ' ') + ++spaceCount; + } + + _numFrames = spaceCount * 3 + 2; +} + +/*--------------------------------------------------------------------------*/ + +SpeakerSutter::SpeakerSutter() { + _speakerName = "SUTTER"; + _color1 = 20; + _color2 = 22; + _textMode = ALIGN_CENTER; +} + +void SpeakerSutter::setText(const Common::String &msg) { + _removeObject1 = _removeObject2 = true; + + _object1.postInit(); + _object1.setVisage(329); + _object1.setStrip2(2); + _object1.fixPriority(254); + _object1.changeZoom(100); + _object1.setPosition(Common::Point(BF_GLOBALS._sceneManager._scene->_sceneBounds.left + 45, + BF_GLOBALS._sceneManager._scene->_sceneBounds.top + 166)); + + _object2.postInit(); + _object2.setVisage(329); + _object2.setStrip2(1); + _object2.fixPriority(255); + _object1.setPosition(Common::Point(BF_GLOBALS._sceneManager._scene->_sceneBounds.left + 45, + BF_GLOBALS._sceneManager._scene->_sceneBounds.top + 166)); + + VisualSpeaker::setText(msg); + _object2.fixCountdown(8, _numFrames); +} + +/*--------------------------------------------------------------------------*/ + +SpeakerDoug::SpeakerDoug(): VisualSpeaker() { + _color1 = 32; + _speakerName = "DOUG"; +} + +/*--------------------------------------------------------------------------*/ + +SpeakerJakeNoHead::SpeakerJakeNoHead(): VisualSpeaker() { + _color1 = 13; + _speakerName = "JAKE_NO_HEAD"; +} + +/*--------------------------------------------------------------------------*/ + BlueForceInvObjectList::BlueForceInvObjectList(): _business_card(9, 4, 2, 0), _lauras_sweater(9, 4, 3, 0), diff --git a/engines/tsage/blue_force/blueforce_logic.h b/engines/tsage/blue_force/blueforce_logic.h index 39f0b9be26..d756d85cb3 100644 --- a/engines/tsage/blue_force/blueforce_logic.h +++ b/engines/tsage/blue_force/blueforce_logic.h @@ -29,8 +29,6 @@ #include "tsage/scenes.h" #include "tsage/globals.h" -#define BF_INTERFACE_Y 168 - namespace TsAGE { namespace BlueForce { @@ -71,27 +69,69 @@ public: uint32 _endFrame; public: Timer(); - void set(uint32 delay, Action *action); + void set(uint32 delay, Action *endAction); + virtual Common::String getClassName() { return "Timer"; } + virtual void synchronize(Serializer &s); virtual void remove(); virtual void signal(); virtual void dispatch(); }; -class SceneItemType1: public SceneItem { +class TimerExt: public Timer { +public: + Action *_newAction; +public: + TimerExt(); + void set(uint32 delay, Action *endAction, Action *action); + + virtual Common::String getClassName() { return "TimerExt"; } + virtual void synchronize(Serializer &s); + virtual void remove(); + virtual void signal(); + virtual void dispatch(); +}; + +class SceneItemType2: public SceneHotspot { public: - virtual void process(Event &event); virtual void startMove(SceneObject *sceneObj, va_list va); }; -class SceneItemType2: public SceneItemType1 { +class NamedObject: public SceneObject { public: - virtual void startMove(SceneObject *sceneObj, va_list va); + int _resNum; + int _lookLineNum, _talkLineNum, _useLineNum; + + virtual Common::String getClassName() { return "NamedObject"; } + virtual void synchronize(Serializer &s); + virtual void postInit(SceneObjectList *OwnerList = NULL); + + void setup(int resNum, int lookLineNum, int talkLineNum, int useLineNum, int mode, SceneItem *item); }; -class SceneItemType3: public SceneItemType1 { +class CountdownObject: public NamedObject { public: + int _countDown; + CountdownObject(); + void fixCountdown(int mode, ...); + virtual Common::String getClassName() { return "CountdownObject"; } + virtual void synchronize(Serializer &s); + virtual void dispatch(); +}; + +class FollowerObject: public NamedObject { +public: + SceneObject *_object; + FollowerObject(); + + virtual Common::String getClassName() { return "SceneObjectExt4"; } + virtual void synchronize(Serializer &s); + virtual void remove(); + virtual void dispatch(); + virtual void reposition(); + + void setup(SceneObject *object, int visage, int frameNum, int yDiff); }; class SceneExt: public Scene { @@ -133,8 +173,44 @@ public: virtual void process(Event &event); }; -class BlueAnimatedSpeaker: public Speaker { +class VisualSpeaker: public Speaker { +public: + NamedObject _object1; + CountdownObject _object2; + bool _removeObject1, _removeObject2; + int _field20C, _field20E; + int _numFrames; + Common::Point _offsetPos; +public: + VisualSpeaker(); + + virtual Common::String getClassName() { return "VisualSpeaker"; } + virtual void synchronize(Serializer &s); + virtual void remove(); + virtual void proc12(Action *action); + virtual void setText(const Common::String &msg); +}; + +class SpeakerSutter: public VisualSpeaker { +public: + SpeakerSutter(); + + virtual Common::String getClassName() { return "SpeakerSutter"; } + virtual void setText(const Common::String &msg); +}; + +class SpeakerDoug: public VisualSpeaker { public: + SpeakerDoug(); + + virtual Common::String getClassName() { return "SpeakerDoug"; } +}; + +class SpeakerJakeNoHead: public VisualSpeaker { +public: + SpeakerJakeNoHead(); + + virtual Common::String getClassName() { return "SpeakerJakeNoHead"; } }; class BlueForceInvObjectList : public InvObjectList { diff --git a/engines/tsage/blue_force/blueforce_scenes1.h b/engines/tsage/blue_force/blueforce_scenes1.h index 0769c6e3c6..2b07e2b48f 100644 --- a/engines/tsage/blue_force/blueforce_scenes1.h +++ b/engines/tsage/blue_force/blueforce_scenes1.h @@ -71,7 +71,7 @@ public: Action1 _action1; Action2 _action2; ScenePalette _scenePalette; - SceneObjectExt2 _object1, _object2, _object3, _object4, _object5; + NamedObject _object1, _object2, _object3, _object4, _object5; int _index; Scene100(); @@ -115,7 +115,6 @@ public: SceneObject _object1, _object2, _protaginist2, _protaginist1, _object5; SceneObject _drunk, _object7, _bartender, _object9, _object10; Text _text; - BlueAnimatedSpeaker _speaker; Action1 _action1; Action _action2, _action3; public: diff --git a/engines/tsage/blue_force/blueforce_scenes3.cpp b/engines/tsage/blue_force/blueforce_scenes3.cpp new file mode 100644 index 0000000000..e49037abf9 --- /dev/null +++ b/engines/tsage/blue_force/blueforce_scenes3.cpp @@ -0,0 +1,602 @@ +/* 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. + * + */ + +#include "common/config-manager.h" +#include "tsage/blue_force/blueforce_scenes3.h" +#include "tsage/scenes.h" +#include "tsage/tsage.h" +#include "tsage/staticres.h" +#include "tsage/globals.h" + +namespace TsAGE { + +namespace BlueForce { + +/*-------------------------------------------------------------------------- + * Scene 300 - Outside Police Station + * + *--------------------------------------------------------------------------*/ + +void Scene300::Object::startMover(CursorType action) { + if (action == CURSOR_TALK) { + Scene300 *scene = (Scene300 *)BF_GLOBALS._sceneManager._scene; + scene->_stripManager.start(_stripNumber, scene); + } else { + NamedObject::startMover(action); + } +} + +void Scene300::Object17::startMover(CursorType action) { + if ((action != CURSOR_USE) || !BF_GLOBALS.getFlag(3)) { + NamedObject::startMover(action); + } else if ((BF_GLOBALS._v4CEA2 != 2) || (BF_GLOBALS._bikiniHutState >= 12)) { + Scene300 *scene = (Scene300 *)BF_GLOBALS._sceneManager._scene; + setAction(&scene->_action4); + } else { + SceneItem::display2(300, 33); + } +} + +void Scene300::Item1::startMover(CursorType action) { + if (action == CURSOR_TALK) { + Scene300 *scene = (Scene300 *)BF_GLOBALS._sceneManager._scene; + BF_GLOBALS._player.disableControl(); + scene->_sceneMode = 305; + scene->setAction(&scene->_sequenceManager1, scene, 305, &BF_GLOBALS._player, + &scene->_object8, NULL); + } else { + NamedHotspot::startMover(action); + } +} + +void Scene300::Item2::startMover(CursorType action) { + if ((action == CURSOR_LOOK) || (action == CURSOR_USE)) { + Scene300 *scene = (Scene300 *)BF_GLOBALS._sceneManager._scene; + scene->setAction(&scene->_sequenceManager1, scene, 304, &scene->_object11, NULL); + } else { + NamedHotspot::startMover(action); + } +} + +void Scene300::Item14::startMover(CursorType action) { + ADD_PLAYER_MOVER_NULL(BF_GLOBALS._player, 151, 54); +} + +void Scene300::Item15::startMover(CursorType action) { + ADD_PLAYER_MOVER_NULL(BF_GLOBALS._player, 316, 90); +} + +/*--------------------------------------------------------------------------*/ + +void Scene300::Action1::signal() { + switch (_actionIndex++) { + case 0: + BF_GLOBALS._player.disableControl(); + setDelay(1); + break; + case 1: + if (BF_GLOBALS.getFlag(7)) + SceneItem::display2(300, 0); + else + SceneItem::display2(666, 27); + setDelay(1); + break; + case 2: { + ADD_PLAYER_MOVER_THIS(BF_GLOBALS._player, BF_GLOBALS._player._position.x - 8, + BF_GLOBALS._player._position.y); + break; + } + case 3: + BF_GLOBALS._player.enableControl(); + remove(); + break; + default: + break; + } +} + +void Scene300::Action2::signal() { + switch (_actionIndex++) { + case 0: + BF_GLOBALS._player.disableControl(); + setDelay(1); + break; + case 1: + SceneItem::display2(300, 28); + setDelay(1); + break; + case 2: { + ADD_MOVER(BF_GLOBALS._player, BF_GLOBALS._player._position.x + 8, + BF_GLOBALS._player._position.y); + break; + } + case 3: + BF_GLOBALS._player.enableControl(); + remove(); + break; + default: + break; + } +} + +void Scene300::Action3::signal() { + Scene300 *scene = (Scene300 *)BF_GLOBALS._sceneManager._scene; + + switch (_actionIndex++) { + case 0: + BF_GLOBALS._player.disableControl(); + setDelay(1); + break; + case 1: + BF_GLOBALS._player.setAction(&scene->_sequenceManager1, this, 306, &BF_GLOBALS._player, + &scene->_object8, NULL); + break; + case 2: + SceneItem::display2(300, 35); + setDelay(1); + break; + case 3: + BF_GLOBALS._player.enableControl(); + remove(); + break; + default: + break; + } +} + +void Scene300::Action4::signal() { + Scene300 *scene = (Scene300 *)BF_GLOBALS._sceneManager._scene; + + switch (_actionIndex++) { + case 0: + BF_GLOBALS._player.disableControl(); + setDelay(1); + break; + case 1: + setAction(&scene->_sequenceManager1, this, 316, &BF_GLOBALS._player, &scene->_object19, NULL); + break; + case 2: + BF_GLOBALS._sceneManager.changeScene(15); + break; + case 3: + setAction(&scene->_sequenceManager1, this, 319, &scene->_object19, NULL); + break; + case 4: + BF_GLOBALS.setFlag(2); + BF_GLOBALS._sceneManager.changeScene(190); + break; + default: + break; + } +} + +void Scene300::Action5::signal() { + Scene300 *scene = (Scene300 *)BF_GLOBALS._sceneManager._scene; + + switch (_actionIndex++) { + case 0: + BF_GLOBALS._player.disableControl(); + scene->_field2760 = 1; + setDelay(1); + break; + case 1: + setAction(&scene->_sequenceManager1, this, 1306, &scene->_object1, &scene->_object8, NULL); + break; + case 2: + scene->_stripManager.start(3004, this); + BF_GLOBALS._sceneManager.changeScene(15); + break; + case 3: { + ADD_PLAYER_MOVER_NULL(BF_GLOBALS._player, 186, 140); + break; + } + case 4: + remove(); + break; + default: + break; + } +} + +/*--------------------------------------------------------------------------*/ + +Scene300::Scene300(): SceneExt(), _object13(3000), _object14(3001), _object15(3002), + _object16(3003) { + _field2760 = _field2762 = 0; +} + +void Scene300::postInit(SceneObjectList *OwnerList) { + SceneExt::postInit(); + loadScene(300); + + // Add the speakers + _stripManager.addSpeaker(&_gameTextSpeaker); + _stripManager.addSpeaker(&_sutterSpeaker); + _stripManager.addSpeaker(&_dougSpeaker); + _stripManager.addSpeaker(&_jakeSpeaker); + + _field2762 = 0; + _item14.setup(Rect(144, 27, 160, 60), 300, -1, -1, -1, 1, NULL); + _item15.setup(Rect(310, 76, SCREEN_WIDTH, 105), 300, -1, -1, -1, 1, NULL); + + // Setup the player + int playerVisage = BF_GLOBALS._player._visage; + BF_GLOBALS._player.postInit(); + BF_GLOBALS._player.setVisage(playerVisage); + BF_GLOBALS._player.setStrip(3); + BF_GLOBALS._player.setPosition(Common::Point(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2)); + BF_GLOBALS._player.setObjectWrapper(new SceneObjectWrapper()); + BF_GLOBALS._player.animate(ANIM_MODE_1, NULL); + BF_GLOBALS._player._moveDiff = Common::Point(3, 1); + BF_GLOBALS._player.disableControl(); + + _object8.setStrip(2); + _object8.setPosition(Common::Point(300, 77)); + + if ((BF_GLOBALS._v4CEA2 != 2) || (BF_GLOBALS._bikiniHutState < 12)) { + _object17.postInit(); + _object17.setVisage(301); + _object17.setStrip(1); + _object17.setPosition(Common::Point(87, 88)); + _object17.setup(300, 11, 13, 2, 1, NULL); + + _object18.postInit(); + _object18.setVisage(301); + _object18.setStrip(1); + _object18.setPosition(Common::Point(137, 92)); + _object18.setup(300, 11, 13, 3, 1, NULL); + } + + _object19.postInit(); + _object19.setVisage(301); + _object19.setStrip(1); + _object19.setPosition(Common::Point(175, 99)); + _object19.setup(300, 11, 13, 34, 1, NULL); + + _object11.postInit(); + _object11.setVisage(301); + _object11.setStrip(8); + _object11.setPosition(Common::Point(265, 91)); + _object11.hide(); + + //***DEBUG*** +BF_GLOBALS.setFlag(2); +BF_GLOBALS._sceneManager._previousScene = 190; +BF_GLOBALS._player.setVisage(190); + + switch (BF_GLOBALS._sceneManager._previousScene) { + case 50: + case 60: + BF_GLOBALS.clearFlag(2); + if (BF_GLOBALS.getFlag(3)) { + BF_GLOBALS._player.disableControl(); + _sceneMode = 318; + setAction(&_sequenceManager1, this, 318, &BF_GLOBALS._player, &_object19, NULL); + } else { + BF_GLOBALS._player.disableControl(); + _sceneMode = 300; + setAction(&_sequenceManager1, this, 1300, &BF_GLOBALS._player, NULL); + } + break; + case 190: + _sceneMode = 0; + if (!BF_GLOBALS.getFlag(2)) { + _sceneMode = 7308; + BF_GLOBALS._player.setPosition(Common::Point(175, 50)); + ADD_PLAYER_MOVER_THIS(BF_GLOBALS._player, 123, 71); + + if ((BF_GLOBALS._v4CEA2 == 2) && (BF_GLOBALS._bikiniHutState < 12)) + setup(); + } else if (!BF_GLOBALS.getFlag(3)) { + BF_GLOBALS._player.disableControl(); + _sceneMode = 300; + setAction(&_sequenceManager1, this, 300, &BF_GLOBALS._player, NULL); + } else { + BF_GLOBALS._player.disableControl(); + _sceneMode = 318; + setAction(&_sequenceManager1, this, 318, &BF_GLOBALS._player, &_object19, NULL); + } + break; + case 315: + BF_GLOBALS._player.setPosition(Common::Point(305, 66)); + if ((BF_GLOBALS._v4CEA2 != 2) || (BF_GLOBALS._bikiniHutState >= 12)) { + BF_GLOBALS._player.setVisage(BF_GLOBALS.getFlag(3) ? 1304 : 303); + BF_GLOBALS._player.disableControl(); + _sceneMode = 0; + setAction(&_sequenceManager1, this, 306, &BF_GLOBALS._player, &_object8, NULL); + } else { + BF_GLOBALS._player.setVisage(1304); + setup(); + BF_GLOBALS._player.disableControl(); + _sceneMode = 0; + setAction(&_sequenceManager1, this, 306, &BF_GLOBALS._player, &_object8, NULL); + } + break; + default: + _sceneMode = 0; + BF_GLOBALS._player.setVisage(1304); + BF_GLOBALS._player.disableControl(); + setAction(&_sequenceManager1, this, 306, &BF_GLOBALS._player, &_object8, NULL); + break; + } +} + +void Scene300::signal() { + switch (_sceneMode) { + case 300: + BF_GLOBALS._sound1.fadeSound(33); + BF_GLOBALS.clearFlag(2); + _sceneMode = 0; + + if ((BF_GLOBALS._v4CEA2 != 1) || (BF_GLOBALS._bikiniHutState != 0)) { + signal(); + } else { + _stripManager.start(3005, this); + } + break; + case 301: + if (_field2760) { + _sceneMode = 1302; + signal(); + } else { + BF_GLOBALS._player.disableControl(); + _sceneMode = 1302; + setAction(&_sequenceManager1, this, 306, &_object1, &_object8, NULL); + } + + _object12.show(); + _object5.dispatch(); + BF_GLOBALS._player.hide(); + break; + case 303: + BF_GLOBALS._player.disableControl(); + _sceneMode = 2307; + setAction(&_sequenceManager1, this, 303, &_object13, &_object1, NULL); + break; + case 305: + if ((BF_GLOBALS._v4CEA2 == 4) || (BF_GLOBALS._v4CEA2 == 5)) { + _sceneMode = 0; + setAction(&_action3); + } else { + BF_GLOBALS._sound1.fadeOut2(NULL); + BF_GLOBALS._sceneManager.changeScene(315); + } + break; + case 309: + BF_GLOBALS._player.disableControl(); + _sceneMode = 3307; + setAction(&_sequenceManager1, this, 309, &_object14, &_object1, NULL); + break; + case 310: + BF_GLOBALS._player.disableControl(); + _sceneMode = 4307; + setAction(&_sequenceManager1, this, 310, &_object12, &_object1, NULL); + break; + case 311: + BF_GLOBALS._player.disableControl(); + _sceneMode = 5307; + setAction(&_sequenceManager1, this, 311, &_object15, &_object1, NULL); + break; + case 312: + case 5307: + BF_GLOBALS._player.disableControl(); + _sceneMode = 1305; + setAction(&_sequenceManager1, this, 312, &_object1, &_object16, NULL); + break; + case 317: + BF_GLOBALS.setFlag(2); + BF_GLOBALS._sceneManager.changeScene(60); + break; + case 318: + BF_GLOBALS.clearFlag(2); + _sceneMode = 0; + signal(); + break; + case 1302: + _field2762 = 0; + BF_GLOBALS._player.disableControl(); + _sceneMode = 1308; + setAction(&_sequenceManager1, this, 302, &_object1, NULL); + break; + case 1305: + BF_GLOBALS._player.disableControl(); + _sceneMode = 1313; + setAction(&_sequenceManager1, this, 305, &_object1, &_object8, NULL); + BF_GLOBALS._player.show(); + _object12.hide(); + break; + case 1307: + case 2308: + BF_GLOBALS._player.disableControl(); + _sceneMode = 303; + setAction(&_sequenceManager1, this, 308, &_object14, NULL); + break; + case 1308: + BF_GLOBALS._player.disableControl(); + _sceneMode = 1307; + setAction(&_sequenceManager1, this, 308, &_object13, NULL); + break; + case 1313: + BF_GLOBALS._player.disableControl(); + _sceneMode = 0; + _object15.setAction(&_sequenceManager4, NULL, 315, &_object15, &_object16, NULL); + _object13.setAction(&_sequenceManager2, NULL, 313, &_object13, &_object17, NULL); + _object14.setAction(&_sequenceManager3, this, 314, &_object14, &_object18, NULL); + + BF_GLOBALS._bikiniHutState = 12; + BF_GLOBALS._sound1.changeSound(33); + break; + case 2307: + case 3308: + BF_GLOBALS._player.disableControl(); + _sceneMode = 309; + setAction(&_sequenceManager1, this, 308, &_object12, NULL); + break; + case 3307: + _object9.postInit(); + _object9.hide(); + _object10.postInit(); + _object10.hide(); + + if (BF_GLOBALS.getFlag(1)) { + BF_GLOBALS._player.disableControl(); + _sceneMode = 4308; + setAction(&_sequenceManager1, this, 6307, &_object2, &_object1, &_object9, &_object10, NULL); + } else { + BF_GLOBALS._player.disableControl(); + _sceneMode = 4308; + setAction(&_sequenceManager1, this, 7307, &_object12, &_object1, &_object9, &_object10, NULL); + } + break; + case 4307: + case 5308: + BF_GLOBALS._player.disableControl(); + _sceneMode = 311; + setAction(&_sequenceManager1, this, 308, &_object16, NULL); + break; + case 4308: + BF_GLOBALS._player.disableControl(); + _sceneMode = 310; + setAction(&_sequenceManager1, this, 308, &_object15, NULL); + break; + case 6308: + BF_GLOBALS._sceneManager.changeScene(190); + break; + case 7308: + if (_field2762) { + BF_GLOBALS._player.disableControl(); + _sceneMode = 301; + setAction(&_sequenceManager1, this, 301, &BF_GLOBALS._player, NULL); + } else { + BF_GLOBALS._player.enableControl(); + } + break; + case 0: + default: + if (_field2762) { + BF_GLOBALS._player.disableControl(); + _sceneMode = 301; + setAction(&_sequenceManager1, this, 301, &BF_GLOBALS._player, NULL); + } else { + BF_GLOBALS._player.enableControl(); + } + break; + } +} + +void Scene300::process(Event &event) { + if ((BF_GLOBALS._player._field8E != 0) && !_eventHandler && (event.mousePos.y < (BF_INTERFACE_Y - 1))) { + Visage visage; + + if (_item14.contains(event.mousePos)) { + visage.setVisage(1, 8); + GfxSurface surface = visage.getFrame(2); + BF_GLOBALS._events.setCursor(surface); + } else if (_item15.contains(event.mousePos)) { + visage.setVisage(1, 8); + GfxSurface surface = visage.getFrame(3); + BF_GLOBALS._events.setCursor(surface); + } else { + CursorType cursorId = BF_GLOBALS._events.hideCursor(); + BF_GLOBALS._events.setCursor(cursorId); + } + } +} + +void Scene300::dispatch() { + SceneExt::dispatch(); + + if (_action) { + int regionIndex = BF_GLOBALS._player.getRegionIndex(); + if ((regionIndex == 1) && (_field2762 == 1)) { + BF_GLOBALS._player.disableControl(); + _sceneMode = 301; + setAction(&_sequenceManager1, this, 301, &BF_GLOBALS._player, NULL); + } + + if ((BF_GLOBALS._player._position.y < 59) && (BF_GLOBALS._player._position.x > 137) && + (_sceneMode != 6308) && (_sceneMode != 7308)) { + BF_GLOBALS._v4CEA4 = 3; + _sceneMode = 6308; + BF_GLOBALS._player.disableControl(); + ADD_MOVER(BF_GLOBALS._player, BF_GLOBALS._player._position.x + 20, + BF_GLOBALS._player._position.y - 5); + } + } +} + +void Scene300::setup() { + _object13.postInit(); + _object13.setVisage(307); + _object13.setStrip(6); + _object13.setPosition(Common::Point(156, 134)); + _object13._moveDiff = Common::Point(3, 1); + _object3.setup(&_object13, 306, 1, 29); + + _object14.postInit(); + _object14.setVisage(307); + _object14.setStrip(6); + _object14.setPosition(Common::Point(171, 137)); + _object14._moveDiff = Common::Point(3, 1); + _object4.setup(&_object14, 306, 2, 29); + + _object12.postInit(); + _object12.setVisage(307); + _object12.setStrip(6); + _object12.setPosition(Common::Point(186, 140)); + _object12._moveDiff = Common::Point(3, 1); + _object5.setup(&_object12, 306, 2, 29); + _object12.hide(); + + _object15.postInit(); + _object15.setVisage(307); + _object15.setStrip(6); + _object15.setPosition(Common::Point(201, 142)); + _object15._moveDiff = Common::Point(3, 1); + _object6.setup(&_object15, 306, 3, 29); + + _object16.postInit(); + _object16.setVisage(307); + _object16.setStrip(6); + _object16.setPosition(Common::Point(216, 145)); + _object16._moveDiff = Common::Point(3, 1); + _object7.setup(&_object16, 306, 1, 29); + + _object1.postInit(); + _object1.setVisage(307); + _object1.setStrip(6); + _object1.setPosition(Common::Point(305, 66)); + _object1._moveDiff = Common::Point(3, 1); + _object1.setObjectWrapper(new SceneObjectWrapper()); + _object1.animate(ANIM_MODE_1, NULL); + _object2.setup(&_object1, 306, 4, 9); + + BF_GLOBALS._sceneItems.addItems(&_object13, &_object14, &_object15, &_object16, NULL); + _timer.set(3600, this, &_action5); + + _field2760 = 0; + _field2762 = 1; +} + +} // End of namespace BlueForce + +} // End of namespace TsAGE diff --git a/engines/tsage/blue_force/blueforce_scenes3.h b/engines/tsage/blue_force/blueforce_scenes3.h new file mode 100644 index 0000000000..42ae69fc3f --- /dev/null +++ b/engines/tsage/blue_force/blueforce_scenes3.h @@ -0,0 +1,137 @@ +/* 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 TSAGE_BLUEFORCE_SCENES3_H +#define TSAGE_BLUEFORCE_SCENES3_H + +#include "common/scummsys.h" +#include "tsage/blue_force/blueforce_logic.h" +#include "tsage/converse.h" +#include "tsage/events.h" +#include "tsage/core.h" +#include "tsage/scenes.h" +#include "tsage/globals.h" +#include "tsage/sound.h" + +namespace TsAGE { + +namespace BlueForce { + +using namespace TsAGE; + +class Scene300: public SceneExt { + /* Objects */ + class Object: public NamedObject { + public: + int _stripNumber; + public: + Object(int stripNumber) { _stripNumber = stripNumber; } + + virtual void startMover(CursorType action); + }; + class Object17: public NamedObject { + public: + virtual void startMover(CursorType action); + }; + + /* Items */ + class Item1: public NamedHotspot { + public: + virtual void startMover(CursorType action); + }; + class Item2: public NamedHotspot { + public: + virtual void startMover(CursorType action); + }; + class Item14: public NamedHotspot { + public: + virtual void startMover(CursorType action); + }; + class Item15: public NamedHotspot { + public: + virtual void startMover(CursorType action); + }; + + /* Actions */ + class Action1: public Action { + public: + virtual void signal(); + }; + class Action2: public Action { + public: + virtual void signal(); + }; + class Action3: public Action { + public: + virtual void signal(); + }; + class Action4: public Action { + public: + virtual void signal(); + }; + class Action5: public Action { + public: + virtual void signal(); + }; +private: + void setup(); +public: + SequenceManager _sequenceManager1, _sequenceManager2; + SequenceManager _sequenceManager3, _sequenceManager4; + NamedObject _object1; + FollowerObject _object2, _object3, _object4, _object5, _object6, _object7; + SceneObject _object8, _object9, _object10; + NamedObject _object11, _object12; + Object _object13, _object14, _object15, _object16; + Object17 _object17; + NamedObject _object18, _object19; + Item1 _item1; + Item2 _item2; + NamedHotspot _item3, _item4, _item5, _item6, _item7; + NamedHotspot _item8, _item9, _item10, _item11; + NamedHotspot _item12, _item13; + Item14 _item14; + Item15 _item15; + Action1 _action1; + Action2 _action2; + Action3 _action3; + Action4 _action4; + Action5 _action5; + SpeakerGameText _gameTextSpeaker; + SpeakerSutter _sutterSpeaker; + SpeakerDoug _dougSpeaker; + SpeakerJakeNoHead _jakeSpeaker; + TimerExt _timer; + int _field2760, _field2762; + + Scene300(); + virtual void postInit(SceneObjectList *OwnerList = NULL); + virtual void signal(); + virtual void process(Event &event); + virtual void dispatch(); +}; + +} // End of namespace BlueForce + +} // End of namespace TsAGE + +#endif diff --git a/engines/tsage/blue_force/blueforce_ui.cpp b/engines/tsage/blue_force/blueforce_ui.cpp index 9214f2b5cd..e27c744486 100644 --- a/engines/tsage/blue_force/blueforce_ui.cpp +++ b/engines/tsage/blue_force/blueforce_ui.cpp @@ -68,10 +68,7 @@ void UIQuestion::showDescription(int lineNum) { // Unknown object description } else { // Display object description - SceneItem::display(9001, lineNum, SET_WIDTH, 312, SET_X, 4, SET_Y, - GLOBALS._sceneManager._scene->_sceneBounds.top + BF_INTERFACE_Y + 2, - SET_FONT, 4, SET_BG_COLOR, 1, SET_FG_COLOR, 19, SET_EXT_BGCOLOR, 9, - SET_EXT_FGCOLOR, 13, LIST_END); + SceneItem::display2(9001, lineNum); } } diff --git a/engines/tsage/core.cpp b/engines/tsage/core.cpp index cb97a2902a..3a489e0024 100644 --- a/engines/tsage/core.cpp +++ b/engines/tsage/core.cpp @@ -1583,6 +1583,17 @@ void SceneItem::display(int resNum, int lineNum, ...) { } } +void SceneItem::display2(int resNum, int lineNum) { + if (_vm->getGameID() == GType_BlueForce) + display(resNum, lineNum, SET_WIDTH, 312, + SET_X, 4 + GLOBALS._sceneManager._scene->_sceneBounds.left, + SET_Y, GLOBALS._sceneManager._scene->_sceneBounds.top + BF_INTERFACE_Y + 2, + SET_FONT, 4, SET_BG_COLOR, 1, SET_FG_COLOR, 19, SET_EXT_BGCOLOR, 9, + SET_EXT_FGCOLOR, 13, LIST_END); + else + display(resNum, lineNum, SET_WIDTH, 200, SET_EXT_BGCOLOR, 7, LIST_END); +} + /*--------------------------------------------------------------------------*/ void SceneHotspot::doAction(int action) { @@ -1606,6 +1617,11 @@ void SceneHotspot::doAction(int action) { /*--------------------------------------------------------------------------*/ +NamedHotspot::NamedHotspot() : SceneHotspot() { + _resNum = 0; + _lookLineNum = _useLineNum = _talkLineNum = -1; +} + void NamedHotspot::doAction(int action) { switch (action) { case CURSOR_WALK: @@ -1615,13 +1631,19 @@ void NamedHotspot::doAction(int action) { if (_lookLineNum == -1) SceneHotspot::doAction(action); else - SceneItem::display(_resnum, _lookLineNum, SET_Y, 20, SET_WIDTH, 200, SET_EXT_BGCOLOR, 7, LIST_END); + SceneItem::display(_resNum, _lookLineNum, SET_Y, 20, SET_WIDTH, 200, SET_EXT_BGCOLOR, 7, LIST_END); break; case CURSOR_USE: if (_useLineNum == -1) SceneHotspot::doAction(action); else - SceneItem::display(_resnum, _useLineNum, SET_Y, 20, SET_WIDTH, 200, SET_EXT_BGCOLOR, 7, LIST_END); + SceneItem::display(_resNum, _useLineNum, SET_Y, 20, SET_WIDTH, 200, SET_EXT_BGCOLOR, 7, LIST_END); + break; + case CURSOR_TALK: + if (_talkLineNum == -1) + SceneHotspot::doAction(action); + else + SceneItem::display2(_resNum, _talkLineNum); break; default: SceneHotspot::doAction(action); @@ -1631,17 +1653,52 @@ void NamedHotspot::doAction(int action) { void NamedHotspot::setup(int ys, int xs, int ye, int xe, const int resnum, const int lookLineNum, const int useLineNum) { setBounds(ys, xe, ye, xs); - _resnum = resnum; + _resNum = resnum; _lookLineNum = lookLineNum; _useLineNum = useLineNum; + _talkLineNum = -1; _globals->_sceneItems.addItems(this, NULL); } +void NamedHotspot::setup(const Rect &bounds, int resNum, int lookLineNum, int talkLineNum, int useLineNum, int mode, SceneItem *item) { + setBounds(bounds); + _resNum = resNum; + _lookLineNum = lookLineNum; + _talkLineNum = talkLineNum; + _useLineNum = useLineNum; + + switch (mode) { + case 2: + _globals->_sceneItems.push_front(this); + break; + case 4: + _globals->_sceneItems.addBefore(item, this); + break; + case 5: + _globals->_sceneItems.addAfter(item, this); + break; + default: + _globals->_sceneItems.push_back(this); + break; + } +} + +void NamedHotspot::setup(int sceneRegionId, int resNum, int lookLineNum, int talkLineNum, int useLineNum, int mode) { + _sceneRegionId = sceneRegionId; + _resNum = resNum; + _lookLineNum = lookLineNum; + _talkLineNum = talkLineNum; + _useLineNum = useLineNum; +} + void NamedHotspot::synchronize(Serializer &s) { SceneHotspot::synchronize(s); - s.syncAsSint16LE(_resnum); + s.syncAsSint16LE(_resNum); s.syncAsSint16LE(_lookLineNum); s.syncAsSint16LE(_useLineNum); + + if (_vm->getGameID() == GType_BlueForce) + s.syncAsSint16LE(_talkLineNum); } /*--------------------------------------------------------------------------*/ @@ -2015,6 +2072,7 @@ void SceneObject::animate(AnimateMode animMode, ...) { break; case ANIM_MODE_8: + case ANIM_MODE_9: _field68 = va_arg(va, int); _endAction = va_arg(va, Action *); _frameChange = 1; @@ -2201,7 +2259,25 @@ void SceneObject::dispatch() { } else { setFrame(changeFrame()); } + break; + + case ANIM_MODE_9: + if (_frame == _endFrame) { + if (_frameChange != -1) { + _frameChange = -1; + _strip = ((_strip - 1) ^ 1) + 1; + _endFrame = 1; + } else if ((_field68 == 0) || (--_field68 != 0)) { + _frameChange = 1; + _endFrame = getFrameCount(); + setFrame(changeFrame()); + } else { + animEnded(); + } + } else { + setFrame(changeFrame()); + } break; default: @@ -2323,22 +2399,6 @@ void AltSceneObject::draw() { /*--------------------------------------------------------------------------*/ -void SceneObjectExt2::postInit(SceneObjectList *OwnerList) { - _v8A = -1; - _v8C = -1; - _v8E = -1; - SceneObject::postInit(); -} - -void SceneObjectExt2::synchronize(Serializer &s) { - SceneObject::synchronize(s); - s.syncAsSint16LE(_v8A); - s.syncAsSint16LE(_v8C); - s.syncAsSint16LE(_v8E); -} - -/*--------------------------------------------------------------------------*/ - void SceneObjectList::draw() { Common::Array<SceneObject *> objList; int paneNum = 0; diff --git a/engines/tsage/core.h b/engines/tsage/core.h index d9fa59a98a..b1cbf74bd3 100644 --- a/engines/tsage/core.h +++ b/engines/tsage/core.h @@ -162,6 +162,18 @@ public: int _state; }; +#define ADD_PLAYER_MOVER(X, Y) { Common::Point pt(X, Y); PlayerMover *mover = new PlayerMover(); \ + _globals->_player.addMover(mover, &pt, this); } +#define ADD_PLAYER_MOVER_NULL(OBJ, X, Y) { Common::Point pt(X, Y); PlayerMover *mover = new PlayerMover(); \ + OBJ.addMover(mover, &pt, NULL); } +#define ADD_PLAYER_MOVER_THIS(OBJ, X, Y) { Common::Point pt(X, Y); PlayerMover *mover = new PlayerMover(); \ + OBJ.addMover(mover, &pt, this); } + +#define ADD_MOVER(OBJ, X, Y) { Common::Point pt(X, Y); NpcMover *mover = new NpcMover(); \ + OBJ.addMover(mover, &pt, this); } +#define ADD_MOVER_NULL(OBJ, X, Y) { Common::Point pt(X, Y); NpcMover *mover = new NpcMover(); \ + OBJ.addMover(mover, &pt, NULL); } + class ObjectMover : public EventHandler { public: Common::Point _destPosition; @@ -406,9 +418,7 @@ public: void setBounds(const Rect &newBounds) { _bounds = newBounds; } void setBounds(const int ys, const int xe, const int ye, const int xs) { _bounds = Rect(MIN(xs, xe), MIN(ys, ye), MAX(xs, xe), MAX(ys, ye)); } static void display(int resNum, int lineNum, ...); - static void display2(int resNum, int lineNum) { - display(resNum, lineNum, SET_WIDTH, 200, SET_EXT_BGCOLOR, 7, LIST_END); - } + static void display2(int resNum, int lineNum); }; class SceneItemExt : public SceneItem { @@ -432,13 +442,15 @@ public: class NamedHotspot : public SceneHotspot { public: - int _resnum, _lookLineNum, _useLineNum; - NamedHotspot() : SceneHotspot() {} + int _resNum, _lookLineNum, _useLineNum, _talkLineNum; + NamedHotspot(); - void setup(int ys, int xs, int ye, int xe, const int resnum, const int lookLineNum, const int useLineNum); virtual void doAction(int action); virtual Common::String getClassName() { return "NamedHotspot"; } virtual void synchronize(Serializer &s); + void setup(int ys, int xs, int ye, int xe, const int resnum, const int lookLineNum, const int useLineNum); + virtual void setup(const Rect &bounds, int resNum, int lookLineNum, int talkLineNum, int useLineNum, int mode, SceneItem *item); + virtual void setup(int sceneRegionId, int resNum, int lookLineNum, int talkLineNum, int useLineNum, int mode); }; enum AnimateMode {ANIM_MODE_NONE = 0, ANIM_MODE_1 = 1, ANIM_MODE_2 = 2, ANIM_MODE_3 = 3, @@ -579,27 +591,6 @@ public: virtual void draw(); }; -class SceneObjectExt : public SceneObject { -public: - int _state; - - virtual void synchronize(Serializer &s) { - SceneObject::synchronize(s); - s.syncAsSint16LE(_state); - } - virtual Common::String getClassName() { return "SceneObjectExt"; } -}; - -class SceneObjectExt2: public SceneObject { -public: - int _v88, _v8A, _v8C, _v8E; - - virtual Common::String getClassName() { return "BF100Object"; } - virtual void synchronize(Serializer &s); - virtual void postInit(SceneObjectList *OwnerList = NULL); -}; - - class SceneText : public SceneObject { public: int _fontNumber; diff --git a/engines/tsage/events.cpp b/engines/tsage/events.cpp index 44c79bd2fe..a6471dc8ab 100644 --- a/engines/tsage/events.cpp +++ b/engines/tsage/events.cpp @@ -270,6 +270,17 @@ void EventsClass::setCursor(Graphics::Surface &cursor, int transColor, const Com _currentCursor = cursorId; } +void EventsClass::setCursor(GfxSurface &cursor) { + // TODO: Find proper parameters for this form in Blue Force + Graphics::Surface s = cursor.lockSurface(); + + const byte *cursorData = (const byte *)s.getBasePtr(0, 0); + CursorMan.replaceCursor(cursorData, cursor.getBounds().width(), cursor.getBounds().height(), + cursor._centroid.x, cursor._centroid.y, cursor._transColor); + + _currentCursor = CURSOR_NONE; +} + void EventsClass::setCursorFromFlag() { setCursor(isCursorVisible() ? _currentCursor : CURSOR_NONE); } @@ -278,8 +289,10 @@ void EventsClass::showCursor() { setCursor(_currentCursor); } -void EventsClass::hideCursor() { +CursorType EventsClass::hideCursor() { + CursorType oldCursor = _currentCursor; setCursor(CURSOR_NONE); + return oldCursor; } bool EventsClass::isCursorVisible() const { diff --git a/engines/tsage/events.h b/engines/tsage/events.h index c36db59270..db1941082d 100644 --- a/engines/tsage/events.h +++ b/engines/tsage/events.h @@ -67,6 +67,8 @@ enum CursorType { CURSOR_NONE = -1, CURSOR_CROSSHAIRS = -2, CURSOR_ARROW = -3 }; +class GfxSurface; + class EventsClass : public SaveListener { private: Common::Event _event; @@ -84,10 +86,11 @@ public: void pushCursor(CursorType cursorType); void popCursor(); void setCursor(Graphics::Surface &cursor, int transColor, const Common::Point &hotspot, CursorType cursorId); + void setCursor(GfxSurface &cursor); void setCursorFromFlag(); CursorType getCursor() const { return _currentCursor; } void showCursor(); - void hideCursor(); + CursorType hideCursor(); bool isCursorVisible() const; bool pollEvent(); diff --git a/engines/tsage/globals.cpp b/engines/tsage/globals.cpp index b20280ae87..52bd42fc5c 100644 --- a/engines/tsage/globals.cpp +++ b/engines/tsage/globals.cpp @@ -187,6 +187,7 @@ BlueForceGlobals::BlueForceGlobals(): Globals() { _interfaceY = 0; _v51C44 = 1; _v4CEA2 = 0; + _v4CEA4 = 0; _v4CEA8 = 0; _v4CEF2 = 0; _v4CEF4 = 0; @@ -196,7 +197,7 @@ BlueForceGlobals::BlueForceGlobals(): Globals() { _v51C42 = 0; _bikiniHutState = 0; _mapLocationId = 1; - Common::set_to(&_globalFlags[0], &_globalFlags[12], 0); + Common::set_to(_globalFlags, _globalFlags + 12, 0); } void BlueForceGlobals::synchronize(Serializer &s) { @@ -205,11 +206,11 @@ void BlueForceGlobals::synchronize(Serializer &s) { } bool BlueForceGlobals::getFlag(int v) { - return _globalFlags[v / 16] & (1 << (v % 8)); + return _globalFlags[v / 8] & (1 << (v % 8)); } void BlueForceGlobals::setFlag(int v) { - _globalFlags[v / 16] |= 1 << (v % 8); + _globalFlags[v / 8] |= 1 << (v % 8); } } // end of namespace BlueForce diff --git a/engines/tsage/globals.h b/engines/tsage/globals.h index fb95f009cc..f5d4aaa16f 100644 --- a/engines/tsage/globals.h +++ b/engines/tsage/globals.h @@ -116,6 +116,7 @@ public: ASoundExt _sound1, _sound2, _sound3; UIElements _uiElements; int _v4CEA2; + int _v4CEA4; int _v4CEA8; int _v4CEF2; int _v4CEF4; diff --git a/engines/tsage/module.mk b/engines/tsage/module.mk index 2e775a6f2c..ed6fb296a0 100644 --- a/engines/tsage/module.mk +++ b/engines/tsage/module.mk @@ -4,6 +4,7 @@ MODULE_OBJS := \ blue_force/blueforce_logic.o \ blue_force/blueforce_scenes0.o \ blue_force/blueforce_scenes1.o \ + blue_force/blueforce_scenes3.o \ blue_force/blueforce_ui.o \ converse.o \ core.o \ diff --git a/engines/tsage/ringworld/ringworld_logic.h b/engines/tsage/ringworld/ringworld_logic.h index 73ecc9722b..69e5520581 100644 --- a/engines/tsage/ringworld/ringworld_logic.h +++ b/engines/tsage/ringworld/ringworld_logic.h @@ -35,19 +35,6 @@ namespace Ringworld { using namespace TsAGE; -#define ADD_PLAYER_MOVER(X, Y) { Common::Point pt(X, Y); PlayerMover *mover = new PlayerMover(); \ - _globals->_player.addMover(mover, &pt, this); } -#define ADD_PLAYER_MOVER_NULL(OBJ, X, Y) { Common::Point pt(X, Y); PlayerMover *mover = new PlayerMover(); \ - OBJ.addMover(mover, &pt, NULL); } -#define ADD_PLAYER_MOVER_THIS(OBJ, X, Y) { Common::Point pt(X, Y); PlayerMover *mover = new PlayerMover(); \ - OBJ.addMover(mover, &pt, this); } - -#define ADD_MOVER(OBJ, X, Y) { Common::Point pt(X, Y); NpcMover *mover = new NpcMover(); \ - OBJ.addMover(mover, &pt, this); } -#define ADD_MOVER_NULL(OBJ, X, Y) { Common::Point pt(X, Y); NpcMover *mover = new NpcMover(); \ - OBJ.addMover(mover, &pt, NULL); } - - class SceneFactory { public: static Scene *createScene(int sceneNumber); @@ -79,6 +66,17 @@ public: } }; +class SceneObjectExt : public SceneObject { +public: + int _state; + + virtual void synchronize(Serializer &s) { + SceneObject::synchronize(s); + s.syncAsSint16LE(_state); + } + virtual Common::String getClassName() { return "SceneObjectExt"; } +}; + class SceneArea : public SavedObject { public: GfxSurface _surface; diff --git a/engines/tsage/saveload.h b/engines/tsage/saveload.h index 03beafed7c..f81454d5e9 100644 --- a/engines/tsage/saveload.h +++ b/engines/tsage/saveload.h @@ -138,6 +138,18 @@ public: } } } + + void addBefore(T existingItem, T newItem) { + typename SynchronizedList<T>::iterator i = this->begin(); + while ((i != this->end()) && (*i != existingItem)) ++i; + this->insert(i, newItem); + } + void addAfter(T existingItem, T newItem) { + typename SynchronizedList<T>::iterator i = this->begin(); + while ((i != this->end()) && (*i != existingItem)) ++i; + if (i != this->end()) ++i; + this->insert(i, newItem); + } }; /** diff --git a/engines/tsage/sound.cpp b/engines/tsage/sound.cpp index 0b77628801..2139056770 100644 --- a/engines/tsage/sound.cpp +++ b/engines/tsage/sound.cpp @@ -2456,7 +2456,7 @@ void ASound::unPrime() { _action = NULL; } -void ASound::fade(int fadeDest, int fadeSteps, int fadeTicks, bool stopAfterFadeFlag, Action *action) { +void ASound::fade(int fadeDest, int fadeSteps, int fadeTicks, bool stopAfterFadeFlag, EventHandler *action) { if (action) _action = action; @@ -2485,10 +2485,19 @@ void ASoundExt::signal() { } } -void ASoundExt::fadeOut2(Action *action) { +void ASoundExt::fadeOut2(EventHandler *action) { fade(0, 10, 10, true, action); } +void ASoundExt::changeSound(int soundNum) { + if (isPlaying()) { + _soundNum = soundNum; + fadeOut2(this); + } else { + fadeSound(soundNum); + } +} + /*--------------------------------------------------------------------------*/ SoundDriver::SoundDriver() { diff --git a/engines/tsage/sound.h b/engines/tsage/sound.h index afcc8f6377..a8ff348bc6 100644 --- a/engines/tsage/sound.h +++ b/engines/tsage/sound.h @@ -365,7 +365,7 @@ public: class ASound: public EventHandler { public: Sound _sound; - Action *_action; + EventHandler *_action; int _cueValue; ASound(); @@ -385,7 +385,7 @@ public: bool isMuted() const { return _sound.isMuted(); } void pause(bool flag) { _sound.pause(flag); } void mute(bool flag) { _sound.mute(flag); } - void fade(int fadeDest, int fadeSteps, int fadeTicks, bool stopAfterFadeFlag, Action *action); + void fade(int fadeDest, int fadeSteps, int fadeTicks, bool stopAfterFadeFlag, EventHandler *action); void fadeIn() { fade(127, 5, 10, false, NULL); } void fadeOut(Action *action) { fade(0, 5, 10, true, action); } void setTimeIndex(uint32 timeIndex) { _sound.setTimeIndex(timeIndex); } @@ -406,7 +406,8 @@ public: int _soundNum; ASoundExt(); - void fadeOut2(Action *action); + void fadeOut2(EventHandler *action); + void changeSound(int soundNum); virtual Common::String getClassName() { return "ASoundExt"; } virtual void synchronize(Serializer &s); diff --git a/engines/tsage/tsage.h b/engines/tsage/tsage.h index 88175f92ce..7bb3c7a989 100644 --- a/engines/tsage/tsage.h +++ b/engines/tsage/tsage.h @@ -62,6 +62,7 @@ struct tSageGameDescription; #define SCREEN_HEIGHT 200 #define SCREEN_CENTER_X 160 #define SCREEN_CENTER_Y 100 +#define BF_INTERFACE_Y 168 class TSageEngine : public Engine { private: |