diff options
108 files changed, 1538 insertions, 967 deletions
@@ -2,7 +2,11 @@ For a more comprehensive changelog of the latest experimental code, see: https://github.com/scummvm/scummvm/commits/ 1.9.0 (XXXX-XX-XX) - + AGI: + - Added support for Hercules rendering (green + amber) + - Added support for the Hercules hires font (also usable outside of Hercules rendering) + - Added optional "pause, when entering commands" feature, that was only available + in the original interpreter for Hercules rendering. 1.8.0 (2016-03-04) New Games: diff --git a/backends/midi/windows.cpp b/backends/midi/windows.cpp index e2b327ffa7..52a46200cb 100644 --- a/backends/midi/windows.cpp +++ b/backends/midi/windows.cpp @@ -185,6 +185,9 @@ MusicDevices WindowsMusicPlugin::getDevices() const { deviceNames.push_back(tmp.szPname); } + // Limit us to the number of actually retrieved devices. + numDevs = deviceNames.size(); + // Check for non-unique device names. This may happen if someone has devices with identical // names (e. g. more than one USB device of the exact same hardware type). It seems that this // does happen in reality sometimes. We generate index numbers for these devices. diff --git a/backends/platform/dingux/build.gcw0.sh b/backends/platform/dingux/build.gcw0.sh index c4bb9d2641..eafccb1e51 100755 --- a/backends/platform/dingux/build.gcw0.sh +++ b/backends/platform/dingux/build.gcw0.sh @@ -3,4 +3,4 @@ export PATH=/opt/gcw0-toolchain/usr/bin:$PATH # Disable high resolution engines since we have 320x240 hardware -./configure --host=gcw0 --enable-plugins --default-dynamic --enable-release --disable-engine=mohawk,neverhood,sword25,toltecs,wintermute,zvision --disable-mt32emu --disable-hq-scalers && make -j6 gcw-opk && ls -l scummvm.opk +./configure --host=gcw0 --enable-plugins --default-dynamic --enable-release --disable-engine=he,mohawk,neverhood,sword1,sword2,sword25,toltecs,wintermute,zvision --disable-mt32emu --disable-hq-scalers && make -j6 gcw-opk && ls -l scummvm.opk diff --git a/common/macresman.cpp b/common/macresman.cpp index d83bde8fd8..adca1ea10b 100644 --- a/common/macresman.cpp +++ b/common/macresman.cpp @@ -29,6 +29,7 @@ #include "common/md5.h" #include "common/substream.h" #include "common/textconsole.h" +#include "common/archive.h" #ifdef MACOSX #include "common/config-manager.h" @@ -261,6 +262,76 @@ bool MacResManager::exists(const String &fileName) { return false; } +void MacResManager::listFiles(StringArray &files, const String &pattern) { + // Base names discovered so far. + typedef HashMap<String, bool, IgnoreCase_Hash, IgnoreCase_EqualTo> BaseNameSet; + BaseNameSet baseNames; + + // List files itself. + ArchiveMemberList memberList; + SearchMan.listMatchingMembers(memberList, pattern); + SearchMan.listMatchingMembers(memberList, pattern + ".rsrc"); + SearchMan.listMatchingMembers(memberList, pattern + ".bin"); + SearchMan.listMatchingMembers(memberList, constructAppleDoubleName(pattern)); + + for (ArchiveMemberList::const_iterator i = memberList.begin(), end = memberList.end(); i != end; ++i) { + String filename = (*i)->getName(); + + // For raw resource forks and MacBinary files we strip the extension + // here to obtain a valid base name. + int lastDotPos = filename.size() - 1; + for (; lastDotPos >= 0; --lastDotPos) { + if (filename[lastDotPos] == '.') { + break; + } + } + + if (lastDotPos != -1) { + const char *extension = filename.c_str() + lastDotPos + 1; + bool removeExtension = false; + + // TODO: Should we really keep filenames suggesting raw resource + // forks or MacBinary files but not being such around? This might + // depend on the pattern the client requests... + if (!scumm_stricmp(extension, "rsrc")) { + SeekableReadStream *stream = (*i)->createReadStream(); + removeExtension = stream && isRawFork(*stream); + delete stream; + } else if (!scumm_stricmp(extension, "bin")) { + SeekableReadStream *stream = (*i)->createReadStream(); + removeExtension = stream && isMacBinary(*stream); + delete stream; + } + + if (removeExtension) { + filename.erase(lastDotPos); + } + } + + // Strip AppleDouble '._' prefix if applicable. + bool isAppleDoubleName = false; + const String filenameAppleDoubleStripped = disassembleAppleDoubleName(filename, &isAppleDoubleName); + + if (isAppleDoubleName) { + SeekableReadStream *stream = (*i)->createReadStream(); + if (stream->readUint32BE() == 0x00051607) { + filename = filenameAppleDoubleStripped; + } + // TODO: Should we really keep filenames suggesting AppleDouble + // but not being AppleDouble around? This might depend on the + // pattern the client requests... + delete stream; + } + + baseNames[filename] = true; + } + + // Append resulting base names to list to indicate found files. + for (BaseNameSet::const_iterator i = baseNames.begin(), end = baseNames.end(); i != end; ++i) { + files.push_back(i->_key); + } +} + bool MacResManager::loadFromAppleDouble(SeekableReadStream &stream) { if (stream.readUint32BE() != 0x00051607) // tag return false; @@ -314,6 +385,18 @@ bool MacResManager::isMacBinary(SeekableReadStream &stream) { return true; } +bool MacResManager::isRawFork(SeekableReadStream &stream) { + // TODO: Is there a better way to detect whether this is a raw fork? + const uint32 dataOffset = stream.readUint32BE(); + const uint32 mapOffset = stream.readUint32BE(); + const uint32 dataLength = stream.readUint32BE(); + const uint32 mapLength = stream.readUint32BE(); + + return !stream.eos() && !stream.err() + && dataOffset < (uint32)stream.size() && dataOffset + dataLength <= (uint32)stream.size() + && mapOffset < (uint32)stream.size() && mapOffset + mapLength <= (uint32)stream.size(); +} + bool MacResManager::loadFromMacBinary(SeekableReadStream &stream) { byte infoHeader[MBI_INFOHDR]; stream.read(infoHeader, MBI_INFOHDR); @@ -592,4 +675,32 @@ String MacResManager::constructAppleDoubleName(String name) { return name; } +String MacResManager::disassembleAppleDoubleName(String name, bool *isAppleDouble) { + if (isAppleDouble) { + *isAppleDouble = false; + } + + // Remove "._" before the last portion of a path name. + for (int i = name.size() - 1; i >= 0; --i) { + if (i == 0) { + if (name.size() > 2 && name[0] == '.' && name[1] == '_') { + name.erase(0, 2); + if (isAppleDouble) { + *isAppleDouble = true; + } + } + } else if (name[i] == '/') { + if ((uint)(i + 2) < name.size() && name[i + 1] == '.' && name[i + 2] == '_') { + name.erase(i + 1, 2); + if (isAppleDouble) { + *isAppleDouble = true; + } + } + break; + } + } + + return name; +} + } // End of namespace Common diff --git a/common/macresman.h b/common/macresman.h index 43ec8d8e2c..05b2a875f4 100644 --- a/common/macresman.h +++ b/common/macresman.h @@ -33,6 +33,7 @@ #include "common/array.h" #include "common/fs.h" #include "common/str.h" +#include "common/str-array.h" #ifndef COMMON_MACRESMAN_H #define COMMON_MACRESMAN_H @@ -82,6 +83,16 @@ public: static bool exists(const String &fileName); /** + * List all filenames matching pattern for opening with open(). + * + * @param files Array containing all matching filenames discovered. Only + * adds to the list. + * @param pattern Pattern to match against. Taking String::matchPattern's + * format. + */ + static void listFiles(StringArray &files, const String &pattern); + + /** * Close the Mac data/resource fork pair. */ void close(); @@ -176,6 +187,7 @@ private: bool loadFromAppleDouble(SeekableReadStream &stream); static String constructAppleDoubleName(String name); + static String disassembleAppleDoubleName(String name, bool *isAppleDouble); /** * Check if the given stream is in the MacBinary format. @@ -183,6 +195,13 @@ private: */ static bool isMacBinary(SeekableReadStream &stream); + /** + * Do a sanity check whether the given stream is a raw resource fork. + * + * @param stream Stream object to check. Will not preserve its position. + */ + static bool isRawFork(SeekableReadStream &stream); + enum { kResForkNone = 0, kResForkRaw, diff --git a/common/rect.h b/common/rect.h index 32424d3e6a..e6534e55d3 100644 --- a/common/rect.h +++ b/common/rect.h @@ -163,7 +163,8 @@ struct Rect { * * @param r the rectangle to check * - * @return true if the given rectangle is inside the rectangle, false otherwise + * @return true if the given rectangle has a non-empty intersection with + * this rectangle, false otherwise */ bool intersects(const Rect &r) const { return (left < r.right) && (r.left < right) && (top < r.bottom) && (r.top < bottom); @@ -162,6 +162,7 @@ _translation=yes # Default platform settings _backend=sdl _16bit=auto +_highres=auto _savegame_timestamp=auto _dynamic_modules=no _elf_loader=no @@ -181,6 +182,7 @@ _stagingpath="staging" _win32path="c:/scummvm" _amigaospath="Games:ScummVM" _staticlibpath= +_xcodetoolspath= _sdlconfig=sdl-config _freetypeconfig=freetype-config _sdlpath="$PATH" @@ -201,6 +203,7 @@ add_feature 16bit "16bit color" "_16bit" add_feature faad "libfaad" "_faad" add_feature flac "FLAC" "_flac" add_feature freetype2 "FreeType2" "_freetype2" +add_feature highres "high resolution" "_highres" add_feature mad "MAD" "_mad" add_feature jpeg "JPEG" "_jpeg" add_feature png "PNG" "_png" @@ -922,6 +925,7 @@ Optional Features: --default-dynamic make plugins dynamic by default --disable-mt32emu don't enable the integrated MT-32 emulator --disable-16bit don't enable 16bit color support + --disable-highres don't enable support for high resolution engines >320x240 --disable-savegame-timestamp don't use timestamps for blank savegame descriptions --disable-scalers exclude scalers --disable-hq-scalers exclude HQ2x and HQ3x scalers @@ -1019,6 +1023,8 @@ done # for parm in ... for ac_option in $@; do case "$ac_option" in --disable-16bit) _16bit=no ;; + --enable-highres) _highres=yes ;; + --disable-highres) _highres=no ;; --disable-savegame-timestamp) _savegame_timestamp=no ;; --disable-scalers) _build_scalers=no ;; --disable-hq-scalers) _build_hq_scalers=no ;; @@ -1230,6 +1236,9 @@ for ac_option in $@; do --with-staticlib-prefix=*) _staticlibpath=`echo $ac_option | cut -d '=' -f 2` ;; + --with-xcodetools-path=*) + _xcodetoolspath=`echo $ac_option | cut -d '=' -f 2` + ;; --host=*) _host=`echo $ac_option | cut -d '=' -f 2` ;; @@ -2345,6 +2354,17 @@ case $_host_os in echo "Could not determine prefix for static libraries" fi fi + + # If _xcodetoolspath is not set yet use xcode-select to get the path + if test -z "$_xcodetoolspath"; then + _xcodetoolspath=`xcode-select -print-path`/Tools + if test -d "$_xcodetoolspath"; then + echo "Set xcodetools-path to ${_xcodetoolspath}" + else + _xcodetoolspath= + echo "Could not determine path for Xcode Tools" + fi + fi ;; dreamcast) append_var DEFINES "-D__DC__" @@ -2438,6 +2458,10 @@ case $_host_os in mint*) append_var DEFINES "-DSYSTEM_NOT_SUPPORTING_D_TYPE" ;; + msys) + echo ERROR: Using the MSYS shell in msys mode is not supported. Please use the MSYS shell in mingw mode instead. + exit 1 + ;; n64) append_var DEFINES "-D__N64__" append_var DEFINES "-DLIMIT_FPS" @@ -3210,6 +3234,26 @@ case $_backend in esac # +# Enable High resolution engines (>320x240) support only for backends which support it +# +case $_backend in + gcw0) + if test "$_highres" = yes ; then + _highres=yes + else + _highres=no + fi + ;; + *) + if test "$_highres" = no ; then + _highres=no + else + _highres=yes + fi + ;; +esac + +# # Enable Event Recorder only for backends that support it # case $_backend in @@ -3522,6 +3566,11 @@ define_in_config_if_yes "$_mt32emu" 'USE_MT32EMU' define_in_config_if_yes "$_16bit" 'USE_RGB_COLOR' # +# Check whether High resolution graphics support is requested +# +define_in_config_if_yes "$_highres" 'USE_HIGHRES' + +# # Check whether save games use the current time as default description # define_in_config_if_yes "$_savegame_timestamp" 'USE_SAVEGAME_TIMESTAMP' @@ -4424,6 +4473,10 @@ if test "$_16bit" = yes ; then echo_n ", 16bit color" fi +if test "$_highres" = yes ; then + echo_n ", highres" +fi + if test "$_savegame_timestamp" = yes ; then echo_n ", savegame timestamp" fi @@ -4694,6 +4747,7 @@ STAGINGPATH=$_stagingpath WIN32PATH=$_win32path AMIGAOSPATH=$_amigaospath STATICLIBPATH=$_staticlibpath +XCODETOOLSPATH=$_xcodetoolspath SDLCONFIG=$_sdlconfig ABI := $ABI diff --git a/dists/macosx/DS_Store b/dists/macosx/DS_Store Binary files differindex 7ad5a19d61..164e7beb9a 100644 --- a/dists/macosx/DS_Store +++ b/dists/macosx/DS_Store diff --git a/dists/openpandora/pnd_make.sh b/dists/openpandora/pnd_make.sh index 202f137acc..a24beaf5d4 100755 --- a/dists/openpandora/pnd_make.sh +++ b/dists/openpandora/pnd_make.sh @@ -42,14 +42,14 @@ cecho () # Color-echo. Argument $1 = message, Argument $2 = color local default_msg="No message passed." # Doesn't really need to be a local variable. message=${1:-$default_msg} # Defaults to default message. - # We only output colors when a TERM environment variable is set. This - # fixes execution of the script on buildbot, which does not have this set. - if [ -z "$TERM" ]; then - echo "$message" - else + # We only output colors when stdout is outputting to a terminal. + # This avoids color codes being output in log files created on buildbot. + if [ -t 1 -a -n "$TERM" ]; then color=${2:-$black} # Defaults to black, if not specified. echo -e "$color$message" - tput sgr0 # Reset to normal. + tput -T"$TERM" sgr0 # Reset to normal. + else + echo "$message" fi return } diff --git a/doc/de/Neues b/doc/de/Neues index 3643aa8cba..a4fa192692 100644 --- a/doc/de/Neues +++ b/doc/de/Neues @@ -2,6 +2,9 @@ Umfangreichere Informationen über die Änderungen des aktuellen experimentellen Programmcodes finden Sie auf Englisch unter: https://github.com/scummvm/scummvm/commits/ +1.9.0 (DD.MM.YYYY) + + 1.8.0 (04.03.2016) Neue Spiele: - Unterstützung für Rex Nebular and the Cosmic Gender Bender hinzugefügt. diff --git a/engines/agos/configure.engine b/engines/agos/configure.engine index 3ae1fb16f2..cd7fcf9d78 100644 --- a/engines/agos/configure.engine +++ b/engines/agos/configure.engine @@ -1,4 +1,4 @@ # This file is included from the main "configure" script # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] add_engine agos "AGOS" yes "agos2" "AGOS 1 games" -add_engine agos2 "AGOS 2 games" yes +add_engine agos2 "AGOS 2 games" yes "" "" "highres" diff --git a/engines/avalanche/configure.engine b/engines/avalanche/configure.engine index 28d6a558db..9b913ff053 100644 --- a/engines/avalanche/configure.engine +++ b/engines/avalanche/configure.engine @@ -1,3 +1,3 @@ # This file is included from the main "configure" script # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] -add_engine avalanche "Lord Avalot d'Argent" no +add_engine avalanche "Lord Avalot d'Argent" no "" "" "highres" diff --git a/engines/cine/saveload.cpp b/engines/cine/saveload.cpp index 1f4f286694..dfd3a1f4bc 100644 --- a/engines/cine/saveload.cpp +++ b/engines/cine/saveload.cpp @@ -691,6 +691,11 @@ bool CineEngine::loadPlainSaveFW(Common::SeekableReadStream &in, CineSaveGameFor } if (strlen(bgName)) { + if (g_cine->getGameType() == GType_FW && (g_cine->getFeatures() & GF_CD)) { + char buffer[20]; + removeExtention(buffer, bgName); + g_sound->setBgMusic(atoi(buffer + 1)); + } loadBg(bgName); } diff --git a/engines/cine/script_fw.cpp b/engines/cine/script_fw.cpp index 6ad38f4433..86eb709d5a 100644 --- a/engines/cine/script_fw.cpp +++ b/engines/cine/script_fw.cpp @@ -1858,7 +1858,9 @@ int FWScript::o1_playSample() { if (g_cine->getGameType() == Cine::GType_OS && size == 0) { return 0; } - g_sound->stopMusic(); + // The DOS CD version of Future Wars uses CD audio for music + if (!(g_cine->getGameType() == Cine::GType_FW && (g_cine->getFeatures() & GF_CD))) + g_sound->stopMusic(); if (size == 0xFFFF) { g_sound->playSound(channel, 0, data, 0, 0, 0, volume, 0); } else { diff --git a/engines/composer/configure.engine b/engines/composer/configure.engine index 71a79acb5d..17120a3a3d 100644 --- a/engines/composer/configure.engine +++ b/engines/composer/configure.engine @@ -1,3 +1,3 @@ # This file is included from the main "configure" script # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] -add_engine composer "Magic Composer" yes +add_engine composer "Magic Composer" yes "" "" "highres" diff --git a/engines/fullpipe/configure.engine b/engines/fullpipe/configure.engine index a9042449db..611d0188dc 100644 --- a/engines/fullpipe/configure.engine +++ b/engines/fullpipe/configure.engine @@ -1,3 +1,3 @@ # This file is included from the main "configure" script # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] -add_engine fullpipe "Full Pipe" no "" "" "16bit" +add_engine fullpipe "Full Pipe" no "" "" "16bit highres" diff --git a/engines/groovie/configure.engine b/engines/groovie/configure.engine index 212a49bec8..f283731a58 100644 --- a/engines/groovie/configure.engine +++ b/engines/groovie/configure.engine @@ -1,4 +1,4 @@ # This file is included from the main "configure" script # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] -add_engine groovie "Groovie" yes "groovie2" "7th Guest" +add_engine groovie "Groovie" yes "groovie2" "7th Guest" "highres" add_engine groovie2 "Groovie 2 games" no "" "" "jpeg 16bit" diff --git a/engines/hopkins/configure.engine b/engines/hopkins/configure.engine index c38ecd4cd2..cd9f50a5f9 100644 --- a/engines/hopkins/configure.engine +++ b/engines/hopkins/configure.engine @@ -1,3 +1,3 @@ # This file is included from the main "configure" script # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] -add_engine hopkins "Hopkins FBI" yes "" "" "16bit" +add_engine hopkins "Hopkins FBI" yes "" "" "16bit highres" diff --git a/engines/lab/processroom.cpp b/engines/lab/processroom.cpp index 44c8d65d7c..5093e8ef85 100644 --- a/engines/lab/processroom.cpp +++ b/engines/lab/processroom.cpp @@ -238,6 +238,8 @@ void LabEngine::doActions(const ActionList &actionList) { ActionList::const_iterator action; for (action = actionList.begin(); action != actionList.end(); ++action) { updateEvents(); + if (_quitLab || shouldQuit()) + return; switch (action->_actionType) { case kActionPlaySound: @@ -381,6 +383,8 @@ void LabEngine::doActions(const ActionList &actionList) { while (_system->getMillis() < targetMillis) { updateEvents(); + if (_quitLab || shouldQuit()) + return; _anim->diffNextFrame(); } } @@ -409,6 +413,8 @@ void LabEngine::doActions(const ActionList &actionList) { case kActionWaitSound: // used in scene 44 (heart of the labyrinth / ending) while (_music->isSoundEffectActive()) { updateEvents(); + if (_quitLab || shouldQuit()) + return; _anim->diffNextFrame(); waitTOF(); } diff --git a/engines/lastexpress/configure.engine b/engines/lastexpress/configure.engine index 807b1a088b..66bac55dea 100644 --- a/engines/lastexpress/configure.engine +++ b/engines/lastexpress/configure.engine @@ -1,3 +1,3 @@ # This file is included from the main "configure" script # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] -add_engine lastexpress "The Last Express" no "" "" "16bit" +add_engine lastexpress "The Last Express" no "" "" "16bit highres" diff --git a/engines/mohawk/configure.engine b/engines/mohawk/configure.engine index 47402c4560..ccb9499ef0 100644 --- a/engines/mohawk/configure.engine +++ b/engines/mohawk/configure.engine @@ -1,6 +1,6 @@ # This file is included from the main "configure" script # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] -add_engine mohawk "Mohawk" yes "cstime myst riven" "Living Books" +add_engine mohawk "Mohawk" yes "cstime myst riven" "Living Books" "highres" add_engine cstime "Where in Time is Carmen Sandiego?" no add_engine riven "Riven: The Sequel to Myst" no "" "" "16bit" -add_engine myst "Myst" no +add_engine myst "Myst" yes diff --git a/engines/mohawk/detection.cpp b/engines/mohawk/detection.cpp index 986b35c85e..2579712105 100644 --- a/engines/mohawk/detection.cpp +++ b/engines/mohawk/detection.cpp @@ -26,6 +26,7 @@ #include "common/savefile.h" #include "common/system.h" #include "common/textconsole.h" +#include "common/translation.h" #include "mohawk/livingbooks.h" @@ -160,9 +161,23 @@ static const char *directoryGlobs[] = { 0 }; +static const ADExtraGuiOptionsMap optionsList[] = { + { + GAMEOPTION_PLAY_MYST_FLYBY, + { + _s("Play the Myst fly by movie"), + _s("The Myst fly by movie was not played by the original engine."), + "playmystflyby", + false + } + }, + + AD_EXTRA_GUI_OPTIONS_TERMINATOR +}; + class MohawkMetaEngine : public AdvancedMetaEngine { public: - MohawkMetaEngine() : AdvancedMetaEngine(Mohawk::gameDescriptions, sizeof(Mohawk::MohawkGameDescription), mohawkGames) { + MohawkMetaEngine() : AdvancedMetaEngine(Mohawk::gameDescriptions, sizeof(Mohawk::MohawkGameDescription), mohawkGames, optionsList) { _singleid = "mohawk"; _maxScanDepth = 2; _directoryGlobs = directoryGlobs; diff --git a/engines/mohawk/detection_tables.h b/engines/mohawk/detection_tables.h index 97d2932d57..7941a0d51a 100644 --- a/engines/mohawk/detection_tables.h +++ b/engines/mohawk/detection_tables.h @@ -22,6 +22,13 @@ namespace Mohawk { +#define GAMEOPTION_PLAY_MYST_FLYBY GUIO_GAMEOPTIONS1 + +#define GUI_OPTIONS_MYST GUIO3(GUIO_NOASPECT, GUIO_NOSUBTITLES, GUIO_NOMIDI) +#define GUI_OPTIONS_MYST_ME GUIO4(GUIO_NOASPECT, GUIO_NOSUBTITLES, GUIO_NOMIDI, GAMEOPTION_PLAY_MYST_FLYBY) +#define GUI_OPTIONS_MYST_DEMO GUIO4(GUIO_NOASPECT, GUIO_NOSUBTITLES, GUIO_NOMIDI, GUIO_NOLAUNCHLOAD) +#define GUI_OPTIONS_MYST_MAKING_OF GUIO4(GUIO_NOASPECT, GUIO_NOSUBTITLES, GUIO_NOMIDI, GUIO_NOLAUNCHLOAD) + static const MohawkGameDescription gameDescriptions[] = { // Myst // English Windows 3.11 @@ -34,7 +41,7 @@ static const MohawkGameDescription gameDescriptions[] = { Common::EN_ANY, Common::kPlatformWindows, ADGF_UNSTABLE, - GUIO1(GUIO_NOASPECT) + GUI_OPTIONS_MYST }, GType_MYST, 0, @@ -52,7 +59,7 @@ static const MohawkGameDescription gameDescriptions[] = { Common::EN_ANY, Common::kPlatformWindows, ADGF_DEMO | ADGF_UNSTABLE, - GUIO1(GUIO_NOASPECT) + GUI_OPTIONS_MYST_DEMO }, GType_MYST, GF_DEMO, @@ -70,7 +77,7 @@ static const MohawkGameDescription gameDescriptions[] = { Common::DE_DEU, Common::kPlatformWindows, ADGF_UNSTABLE, - GUIO1(GUIO_NOASPECT) + GUI_OPTIONS_MYST }, GType_MYST, 0, @@ -88,7 +95,7 @@ static const MohawkGameDescription gameDescriptions[] = { Common::DE_DEU, Common::kPlatformWindows, ADGF_UNSTABLE, - GUIO1(GUIO_NOASPECT) + GUI_OPTIONS_MYST }, GType_MYST, 0, @@ -106,7 +113,7 @@ static const MohawkGameDescription gameDescriptions[] = { Common::ES_ESP, Common::kPlatformWindows, ADGF_UNSTABLE, - GUIO1(GUIO_NOASPECT) + GUI_OPTIONS_MYST }, GType_MYST, 0, @@ -124,7 +131,7 @@ static const MohawkGameDescription gameDescriptions[] = { Common::IT_ITA, Common::kPlatformWindows, ADGF_UNSTABLE, - GUIO1(GUIO_NOASPECT) + GUI_OPTIONS_MYST }, GType_MYST, 0, @@ -142,7 +149,7 @@ static const MohawkGameDescription gameDescriptions[] = { Common::JA_JPN, Common::kPlatformWindows, ADGF_UNSTABLE, - GUIO1(GUIO_NOASPECT) + GUI_OPTIONS_MYST }, GType_MYST, 0, @@ -160,7 +167,7 @@ static const MohawkGameDescription gameDescriptions[] = { Common::FR_FRA, Common::kPlatformWindows, ADGF_UNSTABLE, - GUIO1(GUIO_NOASPECT) + GUI_OPTIONS_MYST }, GType_MYST, 0, @@ -178,7 +185,7 @@ static const MohawkGameDescription gameDescriptions[] = { Common::EN_ANY, Common::kPlatformWindows, ADGF_UNSTABLE, - GUIO1(GUIO_NOASPECT) + GUI_OPTIONS_MYST_MAKING_OF }, GType_MAKINGOF, 0, @@ -196,7 +203,7 @@ static const MohawkGameDescription gameDescriptions[] = { Common::JA_JPN, Common::kPlatformWindows, ADGF_UNSTABLE, - GUIO1(GUIO_NOASPECT) + GUI_OPTIONS_MYST_MAKING_OF }, GType_MAKINGOF, 0, @@ -214,7 +221,7 @@ static const MohawkGameDescription gameDescriptions[] = { Common::EN_ANY, Common::kPlatformWindows, ADGF_UNSTABLE, - GUIO1(GUIO_NOASPECT) + GUI_OPTIONS_MYST_ME }, GType_MYST, GF_ME, @@ -232,7 +239,7 @@ static const MohawkGameDescription gameDescriptions[] = { Common::DE_DEU, Common::kPlatformWindows, ADGF_UNSTABLE, - GUIO1(GUIO_NOASPECT) + GUI_OPTIONS_MYST_ME }, GType_MYST, GF_ME, @@ -250,7 +257,7 @@ static const MohawkGameDescription gameDescriptions[] = { Common::FR_FRA, Common::kPlatformWindows, ADGF_UNSTABLE, - GUIO1(GUIO_NOASPECT) + GUI_OPTIONS_MYST_ME }, GType_MYST, GF_ME, @@ -268,7 +275,7 @@ static const MohawkGameDescription gameDescriptions[] = { Common::PL_POL, Common::kPlatformWindows, ADGF_UNSTABLE, - GUIO1(GUIO_NOASPECT) + GUI_OPTIONS_MYST_ME }, GType_MYST, GF_ME, @@ -2698,7 +2705,7 @@ static const MohawkGameDescription fallbackDescs[] = { Common::UNK_LANG, Common::kPlatformWindows, ADGF_UNSTABLE, - GUIO1(GUIO_NOASPECT) + GUI_OPTIONS_MYST }, GType_MYST, 0, @@ -2713,7 +2720,7 @@ static const MohawkGameDescription fallbackDescs[] = { Common::UNK_LANG, Common::kPlatformWindows, ADGF_UNSTABLE, - GUIO1(GUIO_NOASPECT) + GUI_OPTIONS_MYST_MAKING_OF }, GType_MAKINGOF, 0, @@ -2728,7 +2735,7 @@ static const MohawkGameDescription fallbackDescs[] = { Common::UNK_LANG, Common::kPlatformWindows, ADGF_UNSTABLE, - GUIO1(GUIO_NOASPECT) + GUI_OPTIONS_MYST_ME }, GType_MYST, GF_ME, diff --git a/engines/mohawk/dialogs.cpp b/engines/mohawk/dialogs.cpp index f8aaf0f4af..6c6ae9e77f 100644 --- a/engines/mohawk/dialogs.cpp +++ b/engines/mohawk/dialogs.cpp @@ -207,9 +207,13 @@ void MystOptionsDialog::handleCommand(GUI::CommandSender *sender, uint32 cmd, ui save(); break; case kQuitCmd: { - Common::Event eventQ; - eventQ.type = Common::EVENT_QUIT; - g_system->getEventManager()->pushEvent(eventQ); + if (_vm->getGameType() != GType_MAKINGOF) { + _vm->_needsShowCredits = true; + } else { + Common::Event eventQ; + eventQ.type = Common::EVENT_QUIT; + g_system->getEventManager()->pushEvent(eventQ); + } close(); } break; diff --git a/engines/mohawk/myst.cpp b/engines/mohawk/myst.cpp index 3bc2b2dccb..c16fab9131 100644 --- a/engines/mohawk/myst.cpp +++ b/engines/mohawk/myst.cpp @@ -66,11 +66,6 @@ MohawkEngine_Myst::MohawkEngine_Myst(OSystem *syst, const MohawkGameDescription DebugMan.addDebugChannel(kDebugHelp, "Help", "Track Help File (HELP) Parsing"); DebugMan.addDebugChannel(kDebugCache, "Cache", "Track Resource Cache Accesses"); - // Engine tweaks - // Disabling this makes engine behavior as per - // original, including bugs, missing bits etc. :) - _tweaksEnabled = true; - _currentCursor = 0; _mainCursor = kDefaultMystCursor; _showResourceRects = false; @@ -312,6 +307,7 @@ Common::Error MohawkEngine_Myst::run() { _needsPageDrop = false; _needsShowMap = false; _needsShowDemoMenu = false; + _needsShowCredits = false; _canSafelySaveLoad = true; runDialog(*_optionsDialog); @@ -331,6 +327,12 @@ Common::Error MohawkEngine_Myst::run() { changeToStack(kDemoStack, 2002, 0, 0); _needsShowDemoMenu = false; } + + if (_needsShowCredits) { + _cursor->hideCursor(); + changeToStack(kCreditsStack, 10000, 0, 0); + _needsShowCredits = false; + } break; default: break; @@ -503,8 +505,9 @@ void MohawkEngine_Myst::changeToStack(uint16 stack, uint16 card, uint16 linkSrcS flyby = "stoneship flyby"; break; // Myst Flyby Movie not used in Original Masterpiece Edition Engine + // We play it when first arriving on Myst, and if the user has chosen so. case kMystStack: - if (_tweaksEnabled) + if (ConfMan.getBool("playmystflyby") && card == 4134) flyby = "myst flyby"; break; case kMechanicalStack: diff --git a/engines/mohawk/myst.h b/engines/mohawk/myst.h index 0803c69e55..0b249e5499 100644 --- a/engines/mohawk/myst.h +++ b/engines/mohawk/myst.h @@ -192,11 +192,11 @@ public: MystSoundBlock readSoundBlock(Common::ReadStream *stream) const; void applySoundBlock(const MystSoundBlock &block); - bool _tweaksEnabled; bool _needsUpdate; bool _needsPageDrop; bool _needsShowMap; bool _needsShowDemoMenu; + bool _needsShowCredits; bool _showResourceRects; diff --git a/engines/mohawk/myst_areas.cpp b/engines/mohawk/myst_areas.cpp index 59871ed49f..4b9cf546fa 100644 --- a/engines/mohawk/myst_areas.cpp +++ b/engines/mohawk/myst_areas.cpp @@ -240,6 +240,7 @@ VideoHandle MystAreaVideo::playMovie() { } else { // Resume the video handle->pause(false); + handle->start(); } if (_playBlocking) { @@ -250,6 +251,20 @@ VideoHandle MystAreaVideo::playMovie() { return handle; } +VideoHandle MystAreaVideo::getMovieHandle() { + // If the video is already in the manager, just return the handle + VideoHandle handle = _vm->_video->findVideoHandle(_videoFile); + if (!handle) { + // If the video has not been loaded yet, do it but don't start playing it + handle = _vm->_video->playMovie(_videoFile); + if (!handle) + error("Failed to open '%s'", _videoFile.c_str()); + handle->stop(); + } + + return handle; +} + void MystAreaVideo::handleCardChange() { if (_playOnCardChange) playMovie(); diff --git a/engines/mohawk/myst_areas.h b/engines/mohawk/myst_areas.h index 09ec6a2742..b19a2df9e2 100644 --- a/engines/mohawk/myst_areas.h +++ b/engines/mohawk/myst_areas.h @@ -108,6 +108,8 @@ public: MystAreaVideo(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystArea *parent); VideoHandle playMovie(); + VideoHandle getMovieHandle(); + void handleCardChange() override; bool isPlaying(); void setDirection(int16 direction) { _direction = direction; } diff --git a/engines/mohawk/myst_stacks/channelwood.cpp b/engines/mohawk/myst_stacks/channelwood.cpp index 659c5dcdf2..21c3042359 100644 --- a/engines/mohawk/myst_stacks/channelwood.cpp +++ b/engines/mohawk/myst_stacks/channelwood.cpp @@ -621,24 +621,32 @@ void Channelwood::o_hologramMonitor(uint16 op, uint16 var, uint16 argc, uint16 * switch (button) { case 0: handle = _vm->_video->playMovie(_vm->wrapMovieFilename("monalgh", kChannelwoodStack)); + if (!handle) + error("Failed to open monalgh movie"); + handle->moveTo(227, 70); break; case 1: handle = _vm->_video->playMovie(_vm->wrapMovieFilename("monamth", kChannelwoodStack)); + if (!handle) + error("Failed to open monamth movie"); + handle->moveTo(227, 70); break; case 2: handle = _vm->_video->playMovie(_vm->wrapMovieFilename("monasirs", kChannelwoodStack)); + if (!handle) + error("Failed to open monasirs movie"); + handle->moveTo(227, 70); break; case 3: handle = _vm->_video->playMovie(_vm->wrapMovieFilename("monsmsg", kChannelwoodStack)); + if (!handle) + error("Failed to open monsmsg movie"); + handle->moveTo(226, 68); break; default: warning("Opcode %d Control Variable Out of Range", op); break; } - - // Move the video to the right location - if (handle) - handle->moveTo(227, 70); } } diff --git a/engines/mohawk/myst_stacks/credits.cpp b/engines/mohawk/myst_stacks/credits.cpp index 6548dd3171..b4a2076528 100644 --- a/engines/mohawk/myst_stacks/credits.cpp +++ b/engines/mohawk/myst_stacks/credits.cpp @@ -67,8 +67,10 @@ void Credits::runPersistentScripts() { _curImage++; // After the 6th image has shown, it's time to quit - if (_curImage == 7) + if (_curImage == 7) { _vm->quitGame(); + return; + } // Draw next image _vm->drawCardBackground(); diff --git a/engines/mohawk/myst_stacks/mechanical.cpp b/engines/mohawk/myst_stacks/mechanical.cpp index 3214c643a5..3324c9a22d 100644 --- a/engines/mohawk/myst_stacks/mechanical.cpp +++ b/engines/mohawk/myst_stacks/mechanical.cpp @@ -667,7 +667,7 @@ void Mechanical::o_elevatorTopMovie(uint16 op, uint16 var, uint16 argc, uint16 * void Mechanical::o_fortressRotationSetPosition(uint16 op, uint16 var, uint16 argc, uint16 *argv) { debugC(kDebugScript, "Opcode %d: Set fortress position", op); - VideoHandle gears = _fortressRotationGears->playMovie(); + VideoHandle gears = _fortressRotationGears->getMovieHandle(); uint32 moviePosition = Audio::Timestamp(gears->getTime(), 600).totalNumberOfFrames(); // Myst ME short movie workaround, explained in o_fortressRotation_init @@ -801,7 +801,7 @@ void Mechanical::o_elevatorRotation_init(uint16 op, uint16 var, uint16 argc, uin } void Mechanical::fortressRotation_run() { - VideoHandle gears = _fortressRotationGears->playMovie(); + VideoHandle gears = _fortressRotationGears->getMovieHandle(); double oldRate = gears->getRate().toDouble(); uint32 moviePosition = Audio::Timestamp(gears->getTime(), 600).totalNumberOfFrames(); @@ -949,7 +949,7 @@ void Mechanical::fortressSimulation_run() { _fortressSimulationInit = false; } else { - VideoHandle holo = _fortressSimulationHolo->playMovie(); + VideoHandle holo = _fortressSimulationHolo->getMovieHandle(); double oldRate = holo->getRate().toDouble(); diff --git a/engines/mohawk/myst_stacks/myst.cpp b/engines/mohawk/myst_stacks/myst.cpp index 5033f0fef8..9d23d2fb10 100644 --- a/engines/mohawk/myst_stacks/myst.cpp +++ b/engines/mohawk/myst_stacks/myst.cpp @@ -52,6 +52,7 @@ Myst::Myst(MohawkEngine_Myst *vm) : _cabinDoorOpened = 0; _cabinHandleDown = 0; _cabinMatchState = 2; + _cabinGaugeMovieEnabled = false; _matchBurning = false; _tree = nullptr; _treeAlcove = nullptr; @@ -2142,7 +2143,7 @@ void Myst::tree_run() { // Check if alcove is accessible treeSetAlcoveAccessible(); - if (_cabinGaugeMovie) { + if (_cabinGaugeMovieEnabled) { Common::Rational rate = boilerComputeGaugeRate(pressure, delay); boilerResetGauge(rate); } @@ -2313,7 +2314,7 @@ void Myst::o_rocketPianoStart(uint16 op, uint16 var, uint16 argc, uint16 *argv) dest.bottom = 332 - rect.top; // Draw pressed piano key - _vm->_gfx->copyImageSectionToScreen(key->getSubImage(0).wdib, src, dest); + _vm->_gfx->copyImageSectionToScreen(key->getSubImage(1).wdib, src, dest); _vm->_system->updateScreen(); // Play note @@ -3703,6 +3704,8 @@ void Myst::boilerGaugeInit() { frame = Audio::Timestamp(0, 0, 600); _vm->_video->drawVideoFrame(_cabinGaugeMovie, frame); + + _cabinGaugeMovieEnabled = true; } void Myst::o_rocketSliders_init(uint16 op, uint16 var, uint16 argc, uint16 *argv) { @@ -3842,6 +3845,8 @@ void Myst::o_boiler_exit(uint16 op, uint16 var, uint16 argc, uint16 *argv) { _cabinGaugeMovie = VideoHandle(); _cabinFireMovie = VideoHandle(); + + _cabinGaugeMovieEnabled = false; } void Myst::o_generatorControlRoom_exit(uint16 op, uint16 var, uint16 argc, uint16 *argv) { diff --git a/engines/mohawk/myst_stacks/myst.h b/engines/mohawk/myst_stacks/myst.h index 9d66b798c5..6e2f7cc1c8 100644 --- a/engines/mohawk/myst_stacks/myst.h +++ b/engines/mohawk/myst_stacks/myst.h @@ -261,6 +261,8 @@ protected: uint32 _matchGoOutTime; // 144 VideoHandle _cabinFireMovie; // 240 + + bool _cabinGaugeMovieEnabled; VideoHandle _cabinGaugeMovie; // 244 bool _boilerPressureIncreasing; diff --git a/engines/mohawk/myst_stacks/selenitic.cpp b/engines/mohawk/myst_stacks/selenitic.cpp index 2617bd04aa..5402e5a581 100644 --- a/engines/mohawk/myst_stacks/selenitic.cpp +++ b/engines/mohawk/myst_stacks/selenitic.cpp @@ -672,6 +672,11 @@ void Selenitic::soundReceiverUpdate() { } void Selenitic::soundReceiverDrawView() { + soundReceiverSetSubimageRect(); + soundReceiverDrawAngle(); +} + +void Selenitic::soundReceiverSetSubimageRect() const { uint32 left = ((*_soundReceiverPosition) * 1800) / 3600; Common::Rect rect = _soundReceiverViewer->getSubImage(0).rect; @@ -681,8 +686,6 @@ void Selenitic::soundReceiverDrawView() { _soundReceiverViewer->setSubImageRect(0, rect); _soundReceiverViewer->drawConditionalDataToScreen(0); - - soundReceiverDrawAngle(); } void Selenitic::soundReceiverDrawAngle() { @@ -948,10 +951,13 @@ void Selenitic::soundReceiver_run() { if (_soundReceiverDirection) { uint32 currentTime = _vm->_system->getMillis(); - if (_soundReceiverSpeed == 50 && currentTime > _soundReceiverStartTime + 500) - soundReceiverIncreaseSpeed(); - else if (currentTime > _soundReceiverStartTime + 1000) - soundReceiverIncreaseSpeed(); + if (_soundReceiverSpeed == 50 && currentTime > _soundReceiverStartTime + 500) { + soundReceiverIncreaseSpeed(); + _soundReceiverStartTime = currentTime; + } else if (currentTime > _soundReceiverStartTime + 1000) { + soundReceiverIncreaseSpeed(); + _soundReceiverStartTime = currentTime; + } if (currentTime > _soundReceiverStartTime + 100) soundReceiverUpdate(); @@ -1080,6 +1086,8 @@ void Selenitic::o_soundReceiver_init(uint16 op, uint16 var, uint16 argc, uint16 _soundReceiverPosition = &_state.soundReceiverPositions[currentSource]; _soundReceiverCurrentSource = _soundReceiverSources[currentSource]; + soundReceiverSetSubimageRect(); + _soundReceiverSigmaPressed = false; } diff --git a/engines/mohawk/myst_stacks/selenitic.h b/engines/mohawk/myst_stacks/selenitic.h index ffd8941f2c..fc9649755d 100644 --- a/engines/mohawk/myst_stacks/selenitic.h +++ b/engines/mohawk/myst_stacks/selenitic.h @@ -117,6 +117,7 @@ private: void soundReceiverLeftRight(uint direction); void soundReceiverUpdate(); + void soundReceiverSetSubimageRect() const; void soundReceiverDrawView(); void soundReceiverDrawAngle(); void soundReceiverIncreaseSpeed(); diff --git a/engines/mohawk/video.cpp b/engines/mohawk/video.cpp index 522dd5ecdd..eec543235e 100644 --- a/engines/mohawk/video.cpp +++ b/engines/mohawk/video.cpp @@ -146,7 +146,7 @@ VideoHandle::VideoHandle(const VideoHandle &handle) : _ptr(handle._ptr) { VideoManager::VideoManager(MohawkEngine* vm) : _vm(vm) { // Set dithering enabled, if required - _enableDither = _vm->getGameType() == GType_MYST && !(_vm->getFeatures() & GF_ME); + _enableDither = (_vm->getGameType() == GType_MYST || _vm->getGameType() == GType_MAKINGOF) && !(_vm->getFeatures() & GF_ME); } VideoManager::~VideoManager() { @@ -317,69 +317,8 @@ bool VideoManager::updateMovies() { // Check if we need to draw a frame if (video->needsUpdate()) { - const Graphics::Surface *frame = video->decodeNextFrame(); - Graphics::Surface *convertedFrame = 0; - - if (frame && (*it)->isEnabled()) { - Graphics::PixelFormat pixelFormat = _vm->_system->getScreenFormat(); - - if (frame->format != pixelFormat) { - // We don't support downconverting to 8bpp without having - // support in the codec. Set _enableDither if shows up. - if (pixelFormat.bytesPerPixel == 1) { - warning("Cannot convert high color video frame to 8bpp"); - (*it)->close(); - it = _videos.erase(it); - continue; - } - - // Convert to the current screen format - convertedFrame = frame->convertTo(pixelFormat, video->getPalette()); - frame = convertedFrame; - } else if (pixelFormat.bytesPerPixel == 1 && video->hasDirtyPalette()) { - // Set the palette when running in 8bpp mode only - // Don't do this for Myst, which has its own per-stack handling - if (_vm->getGameType() != GType_MYST) - _vm->_system->getPaletteManager()->setPalette(video->getPalette(), 0, 256); - } - - // Clip the video to make sure it stays on the screen (Myst does this a few times) - Common::Rect targetRect = Common::Rect(video->getWidth(), video->getHeight()); - targetRect.translate((*it)->getX(), (*it)->getY()); - - Common::Rect frameRect = Common::Rect(video->getWidth(), video->getHeight()); - - if (targetRect.left < 0) { - frameRect.left -= targetRect.left; - targetRect.left = 0; - } - - if (targetRect.top < 0) { - frameRect.top -= targetRect.top; - targetRect.top = 0; - } - - if (targetRect.right > _vm->_system->getWidth()) { - frameRect.right -= targetRect.right - _vm->_system->getWidth(); - targetRect.right = _vm->_system->getWidth(); - } - - if (targetRect.bottom > _vm->_system->getHeight()) { - frameRect.bottom -= targetRect.bottom - _vm->_system->getHeight(); - targetRect.bottom = _vm->_system->getHeight(); - } - - _vm->_system->copyRectToScreen(frame->getBasePtr(frameRect.left, frameRect.top), frame->pitch, - targetRect.left, targetRect.top, targetRect.width(), targetRect.height()); - - // We've drawn something to the screen, make sure we update it + if (drawNextFrame(*it)) { updateScreen = true; - - // Delete 8bpp conversion surface - if (convertedFrame) { - convertedFrame->free(); - delete convertedFrame; - } } } @@ -394,6 +333,74 @@ bool VideoManager::updateMovies() { return updateScreen; } +bool VideoManager::drawNextFrame(VideoEntryPtr videoEntry) { + Video::VideoDecoder *video = videoEntry->_video; + const Graphics::Surface *frame = video->decodeNextFrame(); + + if (!frame || !videoEntry->isEnabled()) { + return false; + } + + Graphics::Surface *convertedFrame = 0; + Graphics::PixelFormat pixelFormat = _vm->_system->getScreenFormat(); + + if (frame->format != pixelFormat) { + // We don't support downconverting to 8bpp without having + // support in the codec. Set _enableDither if shows up. + if (pixelFormat.bytesPerPixel == 1) { + warning("Cannot convert high color video frame to 8bpp"); + return false; + } + + // Convert to the current screen format + convertedFrame = frame->convertTo(pixelFormat, video->getPalette()); + frame = convertedFrame; + } else if (pixelFormat.bytesPerPixel == 1 && video->hasDirtyPalette()) { + // Set the palette when running in 8bpp mode only + // Don't do this for Myst, which has its own per-stack handling + if (_vm->getGameType() != GType_MYST) + _vm->_system->getPaletteManager()->setPalette(video->getPalette(), 0, 256); + } + + // Clip the video to make sure it stays on the screen (Myst does this a few times) + Common::Rect targetRect = Common::Rect(video->getWidth(), video->getHeight()); + targetRect.translate(videoEntry->getX(), videoEntry->getY()); + + Common::Rect frameRect = Common::Rect(video->getWidth(), video->getHeight()); + + if (targetRect.left < 0) { + frameRect.left -= targetRect.left; + targetRect.left = 0; + } + + if (targetRect.top < 0) { + frameRect.top -= targetRect.top; + targetRect.top = 0; + } + + if (targetRect.right > _vm->_system->getWidth()) { + frameRect.right -= targetRect.right - _vm->_system->getWidth(); + targetRect.right = _vm->_system->getWidth(); + } + + if (targetRect.bottom > _vm->_system->getHeight()) { + frameRect.bottom -= targetRect.bottom - _vm->_system->getHeight(); + targetRect.bottom = _vm->_system->getHeight(); + } + + _vm->_system->copyRectToScreen(frame->getBasePtr(frameRect.left, frameRect.top), frame->pitch, + targetRect.left, targetRect.top, targetRect.width(), targetRect.height()); + + // Delete 8bpp conversion surface + if (convertedFrame) { + convertedFrame->free(); + delete convertedFrame; + } + + // We've drawn something to the screen, make sure we update it + return true; +} + void VideoManager::activateMLST(uint16 mlstId, uint16 card) { Common::SeekableReadStream *mlstStream = _vm->getResource(ID_MLST, card); uint16 recordCount = mlstStream->readUint16BE(); @@ -582,11 +589,9 @@ bool VideoManager::isVideoPlaying() { } void VideoManager::drawVideoFrame(VideoHandle handle, const Audio::Timestamp &time) { - // FIXME: This should be done separately from the "playing" - // videos eventually. assert(handle); handle->seek(time); - updateMovies(); + drawNextFrame(handle._ptr); handle->stop(); } diff --git a/engines/mohawk/video.h b/engines/mohawk/video.h index c5a3dc7041..d0edab9def 100644 --- a/engines/mohawk/video.h +++ b/engines/mohawk/video.h @@ -350,6 +350,8 @@ private: VideoList::iterator findEntry(VideoEntryPtr ptr); void removeEntry(VideoEntryPtr ptr); + bool drawNextFrame(VideoEntryPtr videoEntry); + // Dithering control bool _enableDither; void checkEnableDither(VideoEntryPtr &entry); diff --git a/engines/mortevielle/configure.engine b/engines/mortevielle/configure.engine index a7fb2ccda6..0fe89acc99 100644 --- a/engines/mortevielle/configure.engine +++ b/engines/mortevielle/configure.engine @@ -1,3 +1,3 @@ # This file is included from the main "configure" script # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] -add_engine mortevielle "Mortevielle" yes +add_engine mortevielle "Mortevielle" yes "" "" "highres" diff --git a/engines/neverhood/configure.engine b/engines/neverhood/configure.engine index 46910e293e..f04e6c69f6 100644 --- a/engines/neverhood/configure.engine +++ b/engines/neverhood/configure.engine @@ -1,3 +1,3 @@ # This file is included from the main "configure" script # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] -add_engine neverhood "Neverhood" yes +add_engine neverhood "Neverhood" yes "" "" "highres" diff --git a/engines/pegasus/configure.engine b/engines/pegasus/configure.engine index ed7e295287..650d57a602 100644 --- a/engines/pegasus/configure.engine +++ b/engines/pegasus/configure.engine @@ -1,3 +1,3 @@ # This file is included from the main "configure" script # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] -add_engine pegasus "The Journeyman Project: Pegasus Prime" yes "" "" "16bit" +add_engine pegasus "The Journeyman Project: Pegasus Prime" yes "" "" "16bit highres" diff --git a/engines/prince/configure.engine b/engines/prince/configure.engine index 50740d9f41..827579b00d 100644 --- a/engines/prince/configure.engine +++ b/engines/prince/configure.engine @@ -1,3 +1,3 @@ # This file is included from the main "configure" script # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] -add_engine prince "The Prince and The Coward" no +add_engine prince "The Prince and The Coward" no "" "" "highres" diff --git a/engines/saga/configure.engine b/engines/saga/configure.engine index 99e2ab367b..adb904a6dd 100644 --- a/engines/saga/configure.engine +++ b/engines/saga/configure.engine @@ -1,5 +1,5 @@ # This file is included from the main "configure" script # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] add_engine saga "SAGA" yes "ihnm saga2" "ITE" -add_engine ihnm "IHNM" yes -add_engine saga2 "SAGA 2 games" no +add_engine ihnm "IHNM" yes "" "" "highres" +add_engine saga2 "SAGA 2 games" no "" "" "highres" diff --git a/engines/sci/configure.engine b/engines/sci/configure.engine index d1c45a4654..f8a519002e 100644 --- a/engines/sci/configure.engine +++ b/engines/sci/configure.engine @@ -1,4 +1,4 @@ # This file is included from the main "configure" script # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] add_engine sci "SCI" yes "sci32" "SCI 0-1.1 games" -add_engine sci32 "SCI32 games" no +add_engine sci32 "SCI32 games" no "" "" "highres" diff --git a/engines/sci/console.cpp b/engines/sci/console.cpp index cbc6dfaf74..6cd072ee8b 100644 --- a/engines/sci/console.cpp +++ b/engines/sci/console.cpp @@ -192,6 +192,7 @@ Console::Console(SciEngine *engine) : GUI::Debugger(), registerCmd("send", WRAP_METHOD(Console, cmdSend)); registerCmd("go", WRAP_METHOD(Console, cmdGo)); registerCmd("logkernel", WRAP_METHOD(Console, cmdLogKernel)); + registerCmd("vocab994", WRAP_METHOD(Console, cmdMapVocab994)); // Breakpoints registerCmd("bp_list", WRAP_METHOD(Console, cmdBreakpointList)); registerCmd("bplist", WRAP_METHOD(Console, cmdBreakpointList)); // alias @@ -1001,7 +1002,7 @@ bool Console::cmdHexgrep(int argc, const char **argv) { for (; resNumber <= resMax; resNumber++) { script = _engine->getResMan()->findResource(ResourceId(restype, resNumber), 0); if (script) { - unsigned int seeker = 0, seekerold = 0; + uint32 seeker = 0, seekerold = 0; uint32 comppos = 0; int output_script_name = 0; @@ -1507,7 +1508,7 @@ bool Console::cmdSaid(int argc, const char **argv) { } // TODO: Maybe turn this into a proper said spec compiler - unsigned int len = 0; + uint32 len = 0; for (p++; p < argc; p++) { if (strcmp(argv[p], ",") == 0) { spec[len++] = 0xf0; @@ -1544,7 +1545,7 @@ bool Console::cmdSaid(int argc, const char **argv) { spec[len++] = 0xfe; spec[len++] = 0xf6; } else { - unsigned int s = strtol(argv[p], 0, 16); + uint32 s = strtol(argv[p], 0, 16); if (s >= 0xf0 && s <= 0xff) { spec[len++] = s; } else { @@ -3923,6 +3924,55 @@ bool Console::cmdSfx01Track(int argc, const char **argv) { return true; } +bool Console::cmdMapVocab994(int argc, const char **argv) { + EngineState *s = _engine->_gamestate; // for the several defines in this function + reg_t reg; + + if (argc != 4) { + debugPrintf("Attempts to map a range of vocab.994 entries to a given class\n"); + debugPrintf("Usage: %s <class addr> <first> <last>\n", argv[0]); + return true; + } + + if (parse_reg_t(_engine->_gamestate, argv[1], ®, false)) { + debugPrintf("Invalid address passed.\n"); + debugPrintf("Check the \"addresses\" command on how to use addresses\n"); + return true; + } + + Resource *resource = _engine->_resMan->findResource(ResourceId(kResourceTypeVocab, 994), 0); + const Object *obj = s->_segMan->getObject(reg); + uint16 *data = (uint16 *) resource->data; + uint32 first = atoi(argv[2]); + uint32 last = atoi(argv[3]); + Common::Array<bool> markers; + + markers.resize(_engine->getKernel()->getSelectorNamesSize()); + if (!obj->isClass() && getSciVersion() != SCI_VERSION_3) + obj = s->_segMan->getObject(obj->getSuperClassSelector()); + + first = MIN(first, (uint32) (resource->size / 2 - 2)); + last = MIN(last, (uint32) (resource->size / 2 - 2)); + + for (uint32 i = first; i <= last; ++i) { + uint16 ofs = data[i]; + + if (obj && ofs < obj->getVarCount()) { + uint16 varSelector = obj->getVarSelector(ofs); + debugPrintf("%d: property at index %04x of %s is %s %s\n", i, ofs, + s->_segMan->derefString(obj->getNameSelector()), + _engine->getKernel()->getSelectorName(varSelector).c_str(), + markers[varSelector] ? "(repeat!)" : ""); + markers[varSelector] = true; + } + else { + debugPrintf("%d: property at index %04x doesn't match up with %s\n", i, ofs, + s->_segMan->derefString(obj->getNameSelector())); + } + } + + return true; +} bool Console::cmdQuit(int argc, const char **argv) { if (argc != 2) { } @@ -4361,7 +4411,8 @@ int Console::printObject(reg_t pos) { debugPrintf(" "); if (var_container && i < var_container->getVarCount()) { uint16 varSelector = var_container->getVarSelector(i); - debugPrintf("[%03x] %s = ", varSelector, _engine->getKernel()->getSelectorName(varSelector).c_str()); + // Times two commented out for now for easy parsing of vocab.994 + debugPrintf("(%04x) [%03x] %s = ", i /* *2 */, varSelector, _engine->getKernel()->getSelectorName(varSelector).c_str()); } else debugPrintf("p#%x = ", i); diff --git a/engines/sci/console.h b/engines/sci/console.h index 7c4de02182..b20f1f7251 100644 --- a/engines/sci/console.h +++ b/engines/sci/console.h @@ -138,6 +138,7 @@ private: bool cmdSend(int argc, const char **argv); bool cmdGo(int argc, const char **argv); bool cmdLogKernel(int argc, const char **argv); + bool cmdMapVocab994(int argc, const char **argv); // Breakpoints bool cmdBreakpointList(int argc, const char **argv); bool cmdBreakpointDelete(int argc, const char **argv); diff --git a/engines/sci/decompressor.cpp b/engines/sci/decompressor.cpp index e65ff148de..ca2298e67e 100644 --- a/engines/sci/decompressor.cpp +++ b/engines/sci/decompressor.cpp @@ -257,7 +257,7 @@ int DecompressorLZW::unpackLZW1(Common::ReadStream *src, byte *dest, uint32 nPac init(src, dest, nPacked, nUnpacked); byte *stak = (byte *)malloc(0x1014); - unsigned int tokensSize = 0x1004 * sizeof(Tokenlist); + uint32 tokensSize = 0x1004 * sizeof(Tokenlist); Tokenlist *tokens = (Tokenlist *)malloc(tokensSize); if (!stak || !tokens) { free(stak); diff --git a/engines/sci/detection_tables.h b/engines/sci/detection_tables.h index 2fd433240b..11218142a4 100644 --- a/engines/sci/detection_tables.h +++ b/engines/sci/detection_tables.h @@ -941,6 +941,15 @@ static const struct ADGameDescription SciGameDescriptions[] = { AD_LISTEND}, Common::EN_ANY, Common::kPlatformDOS, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Hoyle 2 - English DOS (supplied by m_kiewitz) + // SCI interpreter version 0.000.668, Ver 1.000.014, 2x5.25" + {"hoyle2", "", { + {"resource.map", 0, "8cef06c93d17d96f44aacd5902d84b30", 2100}, + {"resource.001", 0, "8f2dd70abe01112eca464cda818b5eb6", 98289}, + {"resource.002", 0, "8f2dd70abe01112eca464cda818b5eb6", 197326}, + AD_LISTEND}, + Common::EN_ANY, Common::kPlatformDOS, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Hoyle 2 - English DOS (supplied by misterhands in bug report #6598) // Game v1.000.016, interpreter 0.000.668, INT #12.5.90 {"hoyle2", "", { diff --git a/engines/sci/engine/kernel.h b/engines/sci/engine/kernel.h index 92916ecc68..f844d96c35 100644 --- a/engines/sci/engine/kernel.h +++ b/engines/sci/engine/kernel.h @@ -460,7 +460,22 @@ reg_t kUpdateScreenItem(EngineState *s, int argc, reg_t *argv); reg_t kDeleteScreenItem(EngineState *s, int argc, reg_t *argv); reg_t kCreateTextBitmap(EngineState *s, int argc, reg_t *argv); -reg_t kDisposeTextBitmap(EngineState *s, int argc, reg_t *argv); +reg_t kBitmap(EngineState *s, int argc, reg_t *argv); +reg_t kBitmapCreate(EngineState *s, int argc, reg_t *argv); +reg_t kBitmapDestroy(EngineState *s, int argc, reg_t *argv); +reg_t kBitmapDrawLine(EngineState *s, int argc, reg_t *argv); +reg_t kBitmapDrawView(EngineState *s, int argc, reg_t *argv); +reg_t kBitmapDrawText(EngineState *s, int argc, reg_t *argv); +reg_t kBitmapDrawColor(EngineState *s, int argc, reg_t *argv); +reg_t kBitmapDrawBitmap(EngineState *s, int argc, reg_t *argv); +reg_t kBitmapInvert(EngineState *s, int argc, reg_t *argv); +reg_t kBitmapSetDisplace(EngineState *s, int argc, reg_t *argv); +reg_t kBitmapCreateFromView(EngineState *s, int argc, reg_t *argv); +reg_t kBitmapCopyPixels(EngineState *s, int argc, reg_t *argv); +reg_t kBitmapClone(EngineState *s, int argc, reg_t *argv); +reg_t kBitmapGetInfo(EngineState *s, int argc, reg_t *argv); +reg_t kBitmapScale(EngineState *s, int argc, reg_t *argv); +reg_t kBitmapCreateFromUnknown(EngineState *s, int argc, reg_t *argv); reg_t kAddPlane(EngineState *s, int argc, reg_t *argv); reg_t kDeletePlane(EngineState *s, int argc, reg_t *argv); @@ -499,6 +514,8 @@ reg_t kPalVaryMergeStart(EngineState *s, int argc, reg_t *argv); // SCI2.1 Kernel Functions reg_t kMorphOn(EngineState *s, int argc, reg_t *argv); reg_t kText(EngineState *s, int argc, reg_t *argv); +reg_t kTextSize32(EngineState *s, int argc, reg_t *argv); +reg_t kTextWidth(EngineState *s, int argc, reg_t *argv); reg_t kSave(EngineState *s, int argc, reg_t *argv); reg_t kAutoSave(EngineState *s, int argc, reg_t *argv); reg_t kList(EngineState *s, int argc, reg_t *argv); @@ -516,9 +533,9 @@ reg_t kGetSierraProfileInt(EngineState *s, int argc, reg_t *argv); reg_t kCelInfo(EngineState *s, int argc, reg_t *argv); reg_t kSetLanguage(EngineState *s, int argc, reg_t *argv); reg_t kScrollWindow(EngineState *s, int argc, reg_t *argv); +reg_t kSetFontHeight(EngineState *s, int argc, reg_t *argv); reg_t kSetFontRes(EngineState *s, int argc, reg_t *argv); reg_t kFont(EngineState *s, int argc, reg_t *argv); -reg_t kBitmap(EngineState *s, int argc, reg_t *argv); reg_t kAddLine(EngineState *s, int argc, reg_t *argv); reg_t kUpdateLine(EngineState *s, int argc, reg_t *argv); reg_t kDeleteLine(EngineState *s, int argc, reg_t *argv); diff --git a/engines/sci/engine/kernel_tables.h b/engines/sci/engine/kernel_tables.h index fbd0b13c88..6258197206 100644 --- a/engines/sci/engine/kernel_tables.h +++ b/engines/sci/engine/kernel_tables.h @@ -60,15 +60,19 @@ struct SciKernelMapSubEntry { #define SCI_SUBOPENTRY_TERMINATOR { SCI_VERSION_NONE, SCI_VERSION_NONE, 0, NULL, NULL, NULL, NULL } -#define SIG_SCIALL SCI_VERSION_NONE, SCI_VERSION_NONE -#define SIG_SCI0 SCI_VERSION_NONE, SCI_VERSION_01 -#define SIG_SCI1 SCI_VERSION_1_EGA_ONLY, SCI_VERSION_1_LATE -#define SIG_SCI11 SCI_VERSION_1_1, SCI_VERSION_1_1 -#define SIG_SINCE_SCI11 SCI_VERSION_1_1, SCI_VERSION_NONE -#define SIG_SCI21EARLY_ONLY SCI_VERSION_2_1_EARLY, SCI_VERSION_2_1_EARLY -#define SIG_SINCE_SCI21 SCI_VERSION_2_1_EARLY, SCI_VERSION_3 -#define SIG_UNTIL_SCI21MID SCI_VERSION_2, SCI_VERSION_2_1_MIDDLE -#define SIG_SINCE_SCI21LATE SCI_VERSION_2_1_LATE, SCI_VERSION_3 +#define SIG_SCIALL SCI_VERSION_NONE, SCI_VERSION_NONE +#define SIG_SCI0 SCI_VERSION_NONE, SCI_VERSION_01 +#define SIG_SCI1 SCI_VERSION_1_EGA_ONLY, SCI_VERSION_1_LATE +#define SIG_SCI11 SCI_VERSION_1_1, SCI_VERSION_1_1 +#define SIG_SINCE_SCI11 SCI_VERSION_1_1, SCI_VERSION_NONE +#define SIG_SCI2 SCI_VERSION_2, SCI_VERSION_2 +#define SIG_SCI21EARLY SCI_VERSION_2_1_EARLY, SCI_VERSION_2_1_EARLY +#define SIG_UNTIL_SCI21EARLY SCI_VERSION_2, SCI_VERSION_2_1_EARLY +#define SIG_UNTIL_SCI21MID SCI_VERSION_2, SCI_VERSION_2_1_MIDDLE +#define SIG_SINCE_SCI21 SCI_VERSION_2_1_EARLY, SCI_VERSION_3 +#define SIG_SINCE_SCI21MID SCI_VERSION_2_1_MIDDLE, SCI_VERSION_3 +#define SIG_SINCE_SCI21LATE SCI_VERSION_2_1_LATE, SCI_VERSION_3 +#define SIG_SCI3 SCI_VERSION_3, SCI_VERSION_3 #define SIG_SCI16 SCI_VERSION_NONE, SCI_VERSION_1_1 #define SIG_SCI32 SCI_VERSION_2, SCI_VERSION_NONE @@ -211,7 +215,7 @@ static const SciKernelMapSubEntry kPalVary_subops[] = { { SIG_SCI16, 6, MAP_CALL(PalVaryPauseResume), "i", NULL }, #ifdef ENABLE_SCI32 { SIG_SCI32, 0, MAP_CALL(PalVarySetVary), "i(i)(i)(ii)", NULL }, - { SIG_SCI32, 1, MAP_CALL(PalVarySetPercent), "(i)(i)", NULL }, + { SIG_SCI32, 1, MAP_CALL(PalVarySetPercent), "(i)(i)", kPalVarySetPercent_workarounds }, { SIG_SCI32, 2, MAP_CALL(PalVaryGetPercent), "", NULL }, { SIG_SCI32, 3, MAP_CALL(PalVaryOff), "", NULL }, { SIG_SCI32, 4, MAP_CALL(PalVaryMergeTarget), "i", NULL }, @@ -284,6 +288,42 @@ static const SciKernelMapSubEntry kSave_subops[] = { }; // version, subId, function-mapping, signature, workarounds +static const SciKernelMapSubEntry kFont_subops[] = { + { SIG_SINCE_SCI21MID, 0, MAP_CALL(SetFontHeight), "i", NULL }, + { SIG_SINCE_SCI21MID, 1, MAP_CALL(SetFontRes), "ii", NULL }, + SCI_SUBOPENTRY_TERMINATOR +}; + +// version, subId, function-mapping, signature, workarounds +static const SciKernelMapSubEntry kText_subops[] = { + { SIG_SINCE_SCI21MID, 0, MAP_CALL(TextSize32), "r[r0]i(i)(i)", NULL }, + { SIG_SINCE_SCI21MID, 1, MAP_CALL(TextWidth), "ri", NULL }, + SCI_SUBOPENTRY_TERMINATOR +}; + +// version, subId, function-mapping, signature, workarounds +static const SciKernelMapSubEntry kBitmap_subops[] = { + { SIG_SINCE_SCI21, 0, MAP_CALL(BitmapCreate), "iiii(i)(i)(i)", NULL }, + { SIG_SINCE_SCI21, 1, MAP_CALL(BitmapDestroy), "r", NULL }, + { SIG_SINCE_SCI21, 2, MAP_CALL(BitmapDrawLine), "riiiii(i)(i)", NULL }, + { SIG_SINCE_SCI21, 3, MAP_CALL(BitmapDrawView), "riii(i)(i)(0)(i)(i)", NULL }, + { SIG_SINCE_SCI21, 4, MAP_CALL(BitmapDrawText), "rriiiiiiiiiii", NULL }, + { SIG_SINCE_SCI21, 5, MAP_CALL(BitmapDrawColor), "riiiii", NULL }, + { SIG_SINCE_SCI21, 6, MAP_CALL(BitmapDrawBitmap), "rr(i)(i)(i)", NULL }, + { SIG_SINCE_SCI21, 7, MAP_CALL(BitmapInvert), "riiiiii", NULL }, + { SIG_SINCE_SCI21MID, 8, MAP_CALL(BitmapSetDisplace), "rii", NULL }, + { SIG_SINCE_SCI21MID, 9, MAP_CALL(BitmapCreateFromView), "iii(i)(i)(i)([r0])", NULL }, + { SIG_SINCE_SCI21MID, 10, MAP_CALL(BitmapCopyPixels), "rr", NULL }, + { SIG_SINCE_SCI21MID, 11, MAP_CALL(BitmapClone), "r", NULL }, + { SIG_SINCE_SCI21LATE, 12, MAP_CALL(BitmapGetInfo), "r(i)(i)", NULL }, + { SIG_SINCE_SCI21LATE, 13, MAP_CALL(BitmapScale), "r...ii", NULL }, + { SIG_SCI3, 14, MAP_CALL(BitmapCreateFromUnknown), "......", NULL }, + { SIG_SCI3, 15, MAP_EMPTY(Bitmap), "(.*)", NULL }, + { SIG_SCI3, 16, MAP_EMPTY(Bitmap), "(.*)", NULL }, + SCI_SUBOPENTRY_TERMINATOR +}; + +// version, subId, function-mapping, signature, workarounds static const SciKernelMapSubEntry kList_subops[] = { { SIG_SINCE_SCI21, 0, MAP_CALL(NewList), "", NULL }, { SIG_SINCE_SCI21, 1, MAP_CALL(DisposeList), "l", NULL }, @@ -546,10 +586,10 @@ static SciKernelMapEntry s_kernelMap[] = { { MAP_CALL(StrEnd), SIG_EVERYWHERE, "r", NULL, NULL }, { MAP_CALL(StrLen), SIG_EVERYWHERE, "[r0]", NULL, kStrLen_workarounds }, { MAP_CALL(StrSplit), SIG_EVERYWHERE, "rr[r0]", NULL, NULL }, - { MAP_CALL(TextColors), SIG_EVERYWHERE, "(i*)", NULL, NULL }, - { MAP_CALL(TextFonts), SIG_EVERYWHERE, "(i*)", NULL, NULL }, - { MAP_CALL(TextSize), SIG_SCIALL, SIGFOR_MAC, "r[r0]i(i)(r0)(i)", NULL, NULL }, - { MAP_CALL(TextSize), SIG_EVERYWHERE, "r[r0]i(i)(r0)", NULL, NULL }, + { MAP_CALL(TextColors), SIG_SCI16, SIGFOR_ALL, "(i*)", NULL, NULL }, + { MAP_CALL(TextFonts), SIG_SCI16, SIGFOR_ALL, "(i*)", NULL, NULL }, + { MAP_CALL(TextSize), SIG_SCI16, SIGFOR_MAC, "r[r0]i(i)(r0)(i)", NULL, NULL }, + { MAP_CALL(TextSize), SIG_SCI16, SIGFOR_ALL, "r[r0]i(i)(r0)", NULL, NULL }, { MAP_CALL(TimesCos), SIG_EVERYWHERE, "ii", NULL, NULL }, { "CosMult", kTimesCos, SIG_EVERYWHERE, "ii", NULL, NULL }, { MAP_CALL(TimesCot), SIG_EVERYWHERE, "ii", NULL, NULL }, @@ -581,13 +621,17 @@ static SciKernelMapEntry s_kernelMap[] = { #ifdef ENABLE_SCI32 // SCI2 Kernel Functions // TODO: whoever knows his way through those calls, fix the signatures. + { "TextSize", kTextSize32, SIG_UNTIL_SCI21EARLY, SIGFOR_ALL, "r[r0]i(i)", NULL, NULL }, + { MAP_DUMMY(TextColors), SIG_UNTIL_SCI21EARLY, SIGFOR_ALL, "(.*)", NULL, NULL }, + { MAP_DUMMY(TextFonts), SIG_UNTIL_SCI21EARLY, SIGFOR_ALL, "(.*)", NULL, NULL }, + { MAP_CALL(AddPlane), SIG_EVERYWHERE, "o", NULL, NULL }, { MAP_CALL(AddScreenItem), SIG_EVERYWHERE, "o", NULL, NULL }, { MAP_CALL(Array), SIG_EVERYWHERE, "(.*)", NULL, NULL }, { MAP_CALL(CreateTextBitmap), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, { MAP_CALL(DeletePlane), SIG_EVERYWHERE, "o", NULL, NULL }, { MAP_CALL(DeleteScreenItem), SIG_EVERYWHERE, "o", NULL, NULL }, - { MAP_CALL(DisposeTextBitmap), SIG_EVERYWHERE, "r", NULL, NULL }, + { "DisposeTextBitmap", kBitmapDestroy, SIG_SCI2, SIGFOR_ALL, "r", NULL, NULL }, { MAP_CALL(FrameOut), SIG_EVERYWHERE, "(i)", NULL, NULL }, { MAP_CALL(GetHighPlanePri), SIG_EVERYWHERE, "", NULL, NULL }, { MAP_CALL(InPolygon), SIG_EVERYWHERE, "iio", NULL, NULL }, @@ -651,7 +695,7 @@ static SciKernelMapEntry s_kernelMap[] = { { MAP_DUMMY(ShowStylePercent), SIG_EVERYWHERE, "(.*)", NULL, NULL }, { MAP_DUMMY(InvertRect), SIG_EVERYWHERE, "(.*)", NULL, NULL }, { MAP_DUMMY(InputText), SIG_EVERYWHERE, "(.*)", NULL, NULL }, - { MAP_DUMMY(TextWidth), SIG_EVERYWHERE, "(.*)", NULL, NULL }, + { MAP_CALL(TextWidth), SIG_UNTIL_SCI21EARLY, SIGFOR_ALL, "ri", NULL, NULL }, { MAP_DUMMY(PointSize), SIG_EVERYWHERE, "(.*)", NULL, NULL }, // SCI2.1 Kernel Functions @@ -662,7 +706,7 @@ static SciKernelMapEntry s_kernelMap[] = { { MAP_CALL(PlayVMD), SIG_EVERYWHERE, "(.*)", NULL, NULL }, { MAP_CALL(Robot), SIG_EVERYWHERE, "(.*)", NULL, NULL }, { MAP_CALL(Save), SIG_EVERYWHERE, "i(.*)", kSave_subops, NULL }, - { MAP_CALL(Text), SIG_EVERYWHERE, "(.*)", NULL, NULL }, + { MAP_CALL(Text), SIG_SINCE_SCI21MID, SIGFOR_ALL, "i(.*)", kText_subops, NULL }, { MAP_CALL(AddPicAt), SIG_EVERYWHERE, "oiii", NULL, NULL }, { MAP_CALL(GetWindowsOption), SIG_EVERYWHERE, "i", NULL, NULL }, { MAP_CALL(WinHelp), SIG_EVERYWHERE, "(.*)", NULL, NULL }, @@ -671,9 +715,9 @@ static SciKernelMapEntry s_kernelMap[] = { { MAP_CALL(CelInfo), SIG_EVERYWHERE, "iiiiii", NULL, NULL }, { MAP_CALL(SetLanguage), SIG_EVERYWHERE, "r", NULL, NULL }, { MAP_CALL(ScrollWindow), SIG_EVERYWHERE, "i(.*)", kScrollWindow_subops, NULL }, - { MAP_CALL(SetFontRes), SIG_SCI21EARLY_ONLY, SIGFOR_ALL, "ii", NULL, NULL }, - { MAP_CALL(Font), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, - { MAP_CALL(Bitmap), SIG_EVERYWHERE, "(.*)", NULL, NULL }, + { MAP_CALL(SetFontRes), SIG_SCI21EARLY, SIGFOR_ALL, "ii", NULL, NULL }, + { MAP_CALL(Font), SIG_SINCE_SCI21MID, SIGFOR_ALL, "i(.*)", kFont_subops, NULL }, + { MAP_CALL(Bitmap), SIG_EVERYWHERE, "(.*)", kBitmap_subops, NULL }, { MAP_CALL(AddLine), SIG_EVERYWHERE, "oiiiiiiiii", NULL, NULL }, { MAP_CALL(UpdateLine), SIG_EVERYWHERE, "[r0]oiiiiiiiii", NULL, NULL }, { MAP_CALL(DeleteLine), SIG_EVERYWHERE, "[r0]o", NULL, NULL }, @@ -1030,7 +1074,6 @@ static const char *const sci2_default_knames[] = { /*0x89*/ "TextWidth", // for debugging(?), only in SCI2, not used in any SCI2 game /*0x8a*/ "PointSize", // for debugging(?), only in SCI2, not used in any SCI2 game - // GK2 Demo (and similar) only kernel functions /*0x8b*/ "AddLine", /*0x8c*/ "DeleteLine", /*0x8d*/ "UpdateLine", diff --git a/engines/sci/engine/kevent.cpp b/engines/sci/engine/kevent.cpp index bb595e9960..254342111b 100644 --- a/engines/sci/engine/kevent.cpp +++ b/engines/sci/engine/kevent.cpp @@ -101,7 +101,25 @@ reg_t kGetEvent(EngineState *s, int argc, reg_t *argv) { // question. Check GfxCursor::setPosition(), for a more detailed // explanation and a list of cursor position workarounds. if (s->_cursorWorkaroundRect.contains(mousePos.x, mousePos.y)) { - s->_cursorWorkaroundActive = false; + // For OpenPandora and possibly other platforms, that support analog-stick control + touch screen + // control at the same time: in case the cursor is currently at the coordinate set by the scripts, + // we will count down instead of immediately disabling the workaround. + // On OpenPandora the cursor position is set, but it's overwritten shortly afterwards by the + // touch screen. In this case we would sometimes disable the workaround, simply because the touch + // screen hasn't yet overwritten the position and thus the workaround would not work anymore. + // On OpenPandora it would sometimes work and sometimes not without this. + if (s->_cursorWorkaroundPoint == mousePos) { + // Cursor is still at the same spot as set by the scripts + if (s->_cursorWorkaroundPosCount > 0) { + s->_cursorWorkaroundPosCount--; + } else { + // Was for quite a bit of time at that spot, so disable workaround now + s->_cursorWorkaroundActive = false; + } + } else { + // Cursor has moved, but is within the rect -> disable workaround immediately + s->_cursorWorkaroundActive = false; + } } else { mousePos.x = s->_cursorWorkaroundPoint.x; mousePos.y = s->_cursorWorkaroundPoint.y; diff --git a/engines/sci/engine/kgraphics.cpp b/engines/sci/engine/kgraphics.cpp index 91d241fb79..c0a3be4c11 100644 --- a/engines/sci/engine/kgraphics.cpp +++ b/engines/sci/engine/kgraphics.cpp @@ -359,12 +359,7 @@ reg_t kTextSize(EngineState *s, int argc, reg_t *argv) { uint16 languageSplitter = 0; Common::String splitText = g_sci->strSplitLanguage(text.c_str(), &languageSplitter, sep); -#ifdef ENABLE_SCI32 - if (g_sci->_gfxText32) - g_sci->_gfxText32->kernelTextSize(splitText.c_str(), font_nr, maxwidth, &textWidth, &textHeight); - else -#endif - g_sci->_gfxText16->kernelTextSize(splitText.c_str(), languageSplitter, font_nr, maxwidth, &textWidth, &textHeight); + g_sci->_gfxText16->kernelTextSize(splitText.c_str(), languageSplitter, font_nr, maxwidth, &textWidth, &textHeight); // One of the game texts in LB2 German contains loads of spaces in // its end. We trim the text here, otherwise the graphics code will diff --git a/engines/sci/engine/kgraphics32.cpp b/engines/sci/engine/kgraphics32.cpp index 4d48ae4e99..0463b125ae 100644 --- a/engines/sci/engine/kgraphics32.cpp +++ b/engines/sci/engine/kgraphics32.cpp @@ -49,11 +49,12 @@ #include "sci/graphics/text16.h" #include "sci/graphics/view.h" #ifdef ENABLE_SCI32 -#include "sci/graphics/palette32.h" +#include "sci/graphics/celobj32.h" #include "sci/graphics/controls32.h" #include "sci/graphics/font.h" // TODO: remove once kBitmap is moved in a separate class -#include "sci/graphics/text32.h" #include "sci/graphics/frameout.h" +#include "sci/graphics/palette32.h" +#include "sci/graphics/text32.h" #endif namespace Sci { @@ -132,7 +133,7 @@ reg_t kGetHighPlanePri(EngineState *s, int argc, reg_t *argv) { reg_t kFrameOut(EngineState *s, int argc, reg_t *argv) { bool showBits = argc > 0 ? argv[0].toUint16() : true; - g_sci->_gfxFrameout->kernelFrameout(showBits); + g_sci->_gfxFrameout->kernelFrameOut(showBits); s->speedThrottler(16); s->_throttleTrigger = true; return NULL_REG; @@ -197,7 +198,7 @@ reg_t kCreateTextBitmap(EngineState *s, int argc, reg_t *argv) { if (subop == 0) { TextAlign alignment = (TextAlign)readSelectorValue(segMan, object, SELECTOR(mode)); reg_t out; - return g_sci->_gfxText32->createFontBitmap(width, height, rect, text, foreColor, backColor, skipColor, fontId, alignment, borderColor, dimmed, 1, &out); + return g_sci->_gfxText32->createFontBitmap(width, height, rect, text, foreColor, backColor, skipColor, fontId, alignment, borderColor, dimmed, true, &out); } else { CelInfo32 celInfo; celInfo.type = kCelTypeView; @@ -205,13 +206,37 @@ reg_t kCreateTextBitmap(EngineState *s, int argc, reg_t *argv) { celInfo.loopNo = readSelectorValue(segMan, object, SELECTOR(loop)); celInfo.celNo = readSelectorValue(segMan, object, SELECTOR(cel)); reg_t out; - return g_sci->_gfxText32->createTitledFontBitmap(celInfo, rect, text, foreColor, backColor, fontId, skipColor, borderColor, dimmed, &out); + return g_sci->_gfxText32->createFontBitmap(celInfo, rect, text, foreColor, backColor, fontId, skipColor, borderColor, dimmed, &out); } } -reg_t kDisposeTextBitmap(EngineState *s, int argc, reg_t *argv) { - g_sci->_gfxText32->disposeTextBitmap(argv[0]); - return s->r_acc; +reg_t kText(EngineState *s, int argc, reg_t *argv) { + if (!s) + return make_reg(0, getSciVersion()); + error("not supposed to call this"); +} + +reg_t kTextSize32(EngineState *s, int argc, reg_t *argv) { + g_sci->_gfxText32->setFont(argv[2].toUint16()); + + reg_t *rect = s->_segMan->derefRegPtr(argv[0], 4); + + Common::String text = s->_segMan->getString(argv[1]); + int16 maxWidth = argc > 3 ? argv[3].toSint16() : 0; + bool doScaling = argc > 4 ? argv[4].toSint16() : true; + + Common::Rect textRect = g_sci->_gfxText32->getTextSize(text, maxWidth, doScaling); + rect[0] = make_reg(0, textRect.left); + rect[1] = make_reg(0, textRect.top); + rect[2] = make_reg(0, textRect.right - 1); + rect[3] = make_reg(0, textRect.bottom - 1); + return NULL_REG; +} + +reg_t kTextWidth(EngineState *s, int argc, reg_t *argv) { + g_sci->_gfxText32->setFont(argv[1].toUint16()); + Common::String text = s->_segMan->getString(argv[0]); + return make_reg(0, g_sci->_gfxText32->getStringWidth(text)); } reg_t kWinHelp(EngineState *s, int argc, reg_t *argv) { @@ -454,18 +479,18 @@ reg_t kScrollWindow(EngineState *s, int argc, reg_t *argv) { #endif reg_t kFont(EngineState *s, int argc, reg_t *argv) { - // TODO: Handle font settings for SCI2.1 - - switch (argv[0].toUint16()) { - case 1: - g_sci->_gfxText32->_scaledWidth = argv[1].toUint16(); - g_sci->_gfxText32->_scaledHeight = argv[2].toUint16(); - return NULL_REG; - default: - error("kFont: unknown subop %d", argv[0].toUint16()); - } + if (!s) + return make_reg(0, getSciVersion()); + error("not supposed to call this"); +} - return s->r_acc; +reg_t kSetFontHeight(EngineState *s, int argc, reg_t *argv) { + // TODO: Setting font may have just been for side effect + // of setting the fontHeight on the font manager, in + // which case we could just get the font directly ourselves. + g_sci->_gfxText32->setFont(argv[0].toUint16()); + g_sci->_gfxText32->_scaledHeight = (g_sci->_gfxText32->_font->getHeight() * g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight + g_sci->_gfxText32->_scaledHeight - 1) / g_sci->_gfxText32->_scaledHeight; + return NULL_REG; } reg_t kSetFontRes(EngineState *s, int argc, reg_t *argv) { @@ -474,178 +499,171 @@ reg_t kSetFontRes(EngineState *s, int argc, reg_t *argv) { return NULL_REG; } -// TODO: Eventually, all of the kBitmap operations should be put -// in a separate class +reg_t kBitmap(EngineState *s, int argc, reg_t *argv) { + if (!s) + return make_reg(0, getSciVersion()); + error("not supposed to call this"); +} -// NOTE: This size is correct only for SCI2.1mid; the size for -// SCI2/2.1early is 36 -#define BITMAP_HEADER_SIZE 46 +reg_t kBitmapCreate(EngineState *s, int argc, reg_t *argv) { + uint32 bitmapHeaderSize = CelObjMem::getBitmapHeaderSize(); + int16 width = argv[0].toSint16(); + int16 height = argv[1].toSint16(); + int16 skipColor = argv[2].toSint16(); + int16 backColor = argv[3].toSint16(); + int16 scaledWidth = argc > 4 ? argv[4].toSint16() : g_sci->_gfxText32->_scaledWidth; + int16 scaledHeight = argc > 5 ? argv[5].toSint16() : g_sci->_gfxText32->_scaledHeight; + bool useRemap = argc > 6 ? argv[6].toSint16() : false; -reg_t kBitmap(EngineState *s, int argc, reg_t *argv) { - // Used for bitmap operations in SCI2.1 and SCI3. - // This is the SCI2.1 version, the functionality seems to have changed in SCI3. + reg_t bitmapMemId = s->_segMan->allocateHunkEntry("Bitmap()", width * height + bitmapHeaderSize); + byte *bitmap = s->_segMan->getHunkPointer(bitmapMemId); + memset(bitmap + bitmapHeaderSize, backColor, width * height); + CelObjMem::buildBitmapHeader(bitmap, width, height, skipColor, 0, 0, scaledWidth, scaledHeight, 0, useRemap); + return bitmapMemId; +} - switch (argv[0].toUint16()) { - case 0: // init bitmap surface - { - // 6 params, called e.g. from TextView::init() in Torin's Passage, - // script 64890 and TransView::init() in script 64884 - uint16 width = argv[1].toUint16(); - uint16 height = argv[2].toUint16(); - //uint16 skip = argv[3].toUint16(); - uint16 back = argv[4].toUint16(); // usually equals skip - //uint16 width2 = (argc >= 6) ? argv[5].toUint16() : 0; - //uint16 height2 = (argc >= 7) ? argv[6].toUint16() : 0; - //uint16 transparentFlag = (argc >= 8) ? argv[7].toUint16() : 0; - - // TODO: skip, width2, height2, transparentFlag - // (used for transparent bitmaps) - int entrySize = width * height + BITMAP_HEADER_SIZE; - reg_t memoryId = s->_segMan->allocateHunkEntry("Bitmap()", entrySize); - byte *memoryPtr = s->_segMan->getHunkPointer(memoryId); - memset(memoryPtr, 0, BITMAP_HEADER_SIZE); // zero out the bitmap header - memset(memoryPtr + BITMAP_HEADER_SIZE, back, width * height); - // Save totalWidth, totalHeight - // TODO: Save the whole bitmap header, like SSCI does - WRITE_SCI11ENDIAN_UINT16(memoryPtr, width); - WRITE_SCI11ENDIAN_UINT16(memoryPtr + 2, height); - WRITE_SCI11ENDIAN_UINT16(memoryPtr + 4, 0); - WRITE_SCI11ENDIAN_UINT16(memoryPtr + 6, 0); - memoryPtr[8] = 0; - WRITE_SCI11ENDIAN_UINT16(memoryPtr + 10, 0); - WRITE_SCI11ENDIAN_UINT16(memoryPtr + 20, BITMAP_HEADER_SIZE); - WRITE_SCI11ENDIAN_UINT32(memoryPtr + 28, 46); - WRITE_SCI11ENDIAN_UINT16(memoryPtr + 36, width); - WRITE_SCI11ENDIAN_UINT16(memoryPtr + 38, width); - return memoryId; - } - break; - case 1: // dispose text bitmap surface - return kDisposeTextBitmap(s, argc - 1, argv + 1); - case 2: // dispose bitmap surface, with extra param - // 2 params, called e.g. from MenuItem::dispose in Torin's Passage, - // script 64893 - warning("kBitmap(2), unk1 %d, bitmap ptr %04x:%04x", argv[1].toUint16(), PRINT_REG(argv[2])); - break; - case 3: // tiled surface - { - // 6 params, called e.g. from TiledBitmap::resize() in Torin's Passage, - // script 64869 - reg_t hunkId = argv[1]; // obtained from kBitmap(0) - // The tiled view seems to always have 2 loops. - // These loops need to have 1 cel in loop 0 and 8 cels in loop 1. - uint16 viewNum = argv[2].toUint16(); // vTiles selector - uint16 loop = argv[3].toUint16(); - uint16 cel = argv[4].toUint16(); - uint16 x = argv[5].toUint16(); - uint16 y = argv[6].toUint16(); - - byte *memoryPtr = s->_segMan->getHunkPointer(hunkId); - // Get totalWidth, totalHeight - uint16 totalWidth = READ_LE_UINT16(memoryPtr); - uint16 totalHeight = READ_LE_UINT16(memoryPtr + 2); - byte *bitmap = memoryPtr + BITMAP_HEADER_SIZE; - - GfxView *view = g_sci->_gfxCache->getView(viewNum); - uint16 tileWidth = view->getWidth(loop, cel); - uint16 tileHeight = view->getHeight(loop, cel); - const byte *tileBitmap = view->getBitmap(loop, cel); - uint16 width = MIN<uint16>(totalWidth - x, tileWidth); - uint16 height = MIN<uint16>(totalHeight - y, tileHeight); - - for (uint16 curY = 0; curY < height; curY++) { - for (uint16 curX = 0; curX < width; curX++) { - bitmap[(curY + y) * totalWidth + (curX + x)] = tileBitmap[curY * tileWidth + curX]; - } - } +reg_t kBitmapDestroy(EngineState *s, int argc, reg_t *argv) { + s->_segMan->freeHunkEntry(argv[0]); + return NULL_REG; +} - } - break; - case 4: // add text to bitmap - { - // 13 params, called e.g. from TextButton::createBitmap() in Torin's Passage, - // script 64894 - reg_t hunkId = argv[1]; // obtained from kBitmap(0) - Common::String text = s->_segMan->getString(argv[2]); - uint16 textX = argv[3].toUint16(); - uint16 textY = argv[4].toUint16(); - //reg_t unk5 = argv[5]; - //reg_t unk6 = argv[6]; - //reg_t unk7 = argv[7]; // skip? - //reg_t unk8 = argv[8]; // back? - //reg_t unk9 = argv[9]; - uint16 fontId = argv[10].toUint16(); - //uint16 mode = argv[11].toUint16(); - uint16 dimmed = argv[12].toUint16(); - //warning("kBitmap(4): bitmap ptr %04x:%04x, font %d, mode %d, dimmed %d - text: \"%s\"", - // PRINT_REG(bitmapPtr), font, mode, dimmed, text.c_str()); - uint16 foreColor = 255; // TODO - - byte *memoryPtr = s->_segMan->getHunkPointer(hunkId); - // Get totalWidth, totalHeight - uint16 totalWidth = READ_LE_UINT16(memoryPtr); - uint16 totalHeight = READ_LE_UINT16(memoryPtr + 2); - byte *bitmap = memoryPtr + BITMAP_HEADER_SIZE; - - GfxFont *font = g_sci->_gfxCache->getFont(fontId); - - int16 charCount = 0; - uint16 curX = textX, curY = textY; - const char *txt = text.c_str(); - - while (*txt) { - charCount = g_sci->_gfxText32->GetLongest(txt, totalWidth, font); - if (charCount == 0) - break; - - for (int i = 0; i < charCount; i++) { - unsigned char curChar = txt[i]; - font->drawToBuffer(curChar, curY, curX, foreColor, dimmed, bitmap, totalWidth, totalHeight); - curX += font->getCharWidth(curChar); - } - - curX = textX; - curY += font->getHeight(); - txt += charCount; - while (*txt == ' ') - txt++; // skip over breaking spaces - } +reg_t kBitmapDrawLine(EngineState *s, int argc, reg_t *argv) { + // bitmapMemId, (x1, y1, x2, y2) OR (x2, y2, x1, y1), line color, unknown int, unknown int + return kStubNull(s, argc + 1, argv - 1); +} +reg_t kBitmapDrawView(EngineState *s, int argc, reg_t *argv) { + // viewId, loopNo, celNo, displace x, displace y, unused, view x, view y + + // called e.g. from TiledBitmap::resize() in Torin's Passage, script 64869 + // The tiled view seems to always have 2 loops. + // These loops need to have 1 cel in loop 0 and 8 cels in loop 1. + + return kStubNull(s, argc + 1, argv - 1); + +#if 0 + // tiled surface + // 6 params, called e.g. from TiledBitmap::resize() in Torin's Passage, + // script 64869 + reg_t hunkId = argv[1]; // obtained from kBitmap(0) + // The tiled view seems to always have 2 loops. + // These loops need to have 1 cel in loop 0 and 8 cels in loop 1. + uint16 viewNum = argv[2].toUint16(); // vTiles selector + uint16 loop = argv[3].toUint16(); + uint16 cel = argv[4].toUint16(); + uint16 x = argv[5].toUint16(); + uint16 y = argv[6].toUint16(); + + byte *memoryPtr = s->_segMan->getHunkPointer(hunkId); + // Get totalWidth, totalHeight + uint16 totalWidth = READ_LE_UINT16(memoryPtr); + uint16 totalHeight = READ_LE_UINT16(memoryPtr + 2); + byte *bitmap = memoryPtr + BITMAP_HEADER_SIZE; + + GfxView *view = g_sci->_gfxCache->getView(viewNum); + uint16 tileWidth = view->getWidth(loop, cel); + uint16 tileHeight = view->getHeight(loop, cel); + const byte *tileBitmap = view->getBitmap(loop, cel); + uint16 width = MIN<uint16>(totalWidth - x, tileWidth); + uint16 height = MIN<uint16>(totalHeight - y, tileHeight); + + for (uint16 curY = 0; curY < height; curY++) { + for (uint16 curX = 0; curX < width; curX++) { + bitmap[(curY + y) * totalWidth + (curX + x)] = tileBitmap[curY * tileWidth + curX]; } - break; - case 5: // fill with color - { - // 6 params, called e.g. from TextView::init() and TextView::draw() - // in Torin's Passage, script 64890 - reg_t hunkId = argv[1]; // obtained from kBitmap(0) - uint16 x = argv[2].toUint16(); - uint16 y = argv[3].toUint16(); - uint16 fillWidth = argv[4].toUint16(); // width - 1 - uint16 fillHeight = argv[5].toUint16(); // height - 1 - uint16 back = argv[6].toUint16(); - - byte *memoryPtr = s->_segMan->getHunkPointer(hunkId); - // Get totalWidth, totalHeight - uint16 totalWidth = READ_LE_UINT16(memoryPtr); - uint16 totalHeight = READ_LE_UINT16(memoryPtr + 2); - uint16 width = MIN<uint16>(totalWidth - x, fillWidth); - uint16 height = MIN<uint16>(totalHeight - y, fillHeight); - byte *bitmap = memoryPtr + BITMAP_HEADER_SIZE; - - for (uint16 curY = 0; curY < height; curY++) { - for (uint16 curX = 0; curX < width; curX++) { - bitmap[(curY + y) * totalWidth + (curX + x)] = back; - } - } + } +#endif +} + +reg_t kBitmapDrawText(EngineState *s, int argc, reg_t *argv) { + // called e.g. from TextButton::createBitmap() in Torin's Passage, script 64894 + // bitmap, text, textLeft, textTop, textRight, textBottom, foreColor, backColor, skipColor, fontNo, alignment, borderColor, dimmed + return kStubNull(s, argc + 1, argv - 1); +} + +reg_t kBitmapDrawColor(EngineState *s, int argc, reg_t *argv) { + // bitmap, left, top, right, bottom, color + + // called e.g. from TextView::init() and TextView::draw() in Torin's Passage, script 64890 + return kStubNull(s, argc + 1, argv - 1); +#if 0 + reg_t hunkId = argv[1]; // obtained from kBitmap(0) + uint16 x = argv[2].toUint16(); + uint16 y = argv[3].toUint16(); + uint16 fillWidth = argv[4].toUint16(); // width - 1 + uint16 fillHeight = argv[5].toUint16(); // height - 1 + uint16 back = argv[6].toUint16(); + + byte *memoryPtr = s->_segMan->getHunkPointer(hunkId); + // Get totalWidth, totalHeight + uint16 totalWidth = READ_LE_UINT16(memoryPtr); + uint16 totalHeight = READ_LE_UINT16(memoryPtr + 2); + uint16 width = MIN<uint16>(totalWidth - x, fillWidth); + uint16 height = MIN<uint16>(totalHeight - y, fillHeight); + byte *bitmap = memoryPtr + BITMAP_HEADER_SIZE; + + for (uint16 curY = 0; curY < height; curY++) { + for (uint16 curX = 0; curX < width; curX++) { + bitmap[(curY + y) * totalWidth + (curX + x)] = back; } - break; - default: - kStub(s, argc, argv); - break; } +#endif +} - return s->r_acc; +reg_t kBitmapDrawBitmap(EngineState *s, int argc, reg_t *argv) { + // target bitmap, source bitmap, x, y, unknown boolean + + return kStubNull(s, argc + 1, argv - 1); +} + +reg_t kBitmapInvert(EngineState *s, int argc, reg_t *argv) { + // bitmap, left, top, right, bottom, foreColor, backColor + + return kStubNull(s, argc + 1, argv - 1); +} + +reg_t kBitmapSetDisplace(EngineState *s, int argc, reg_t *argv) { + // bitmap, x, y + + return kStubNull(s, argc + 1, argv - 1); +} + +reg_t kBitmapCreateFromView(EngineState *s, int argc, reg_t *argv) { + // viewId, loopNo, celNo, skipColor, backColor, useRemap, source overlay bitmap + + return kStub(s, argc + 1, argv - 1); +} + +reg_t kBitmapCopyPixels(EngineState *s, int argc, reg_t *argv) { + // target bitmap, source bitmap + + return kStubNull(s, argc + 1, argv - 1); +} + +reg_t kBitmapClone(EngineState *s, int argc, reg_t *argv) { + // bitmap + + return kStub(s, argc + 1, argv - 1); +} + +reg_t kBitmapGetInfo(EngineState *s, int argc, reg_t *argv) { + // bitmap + + // argc 1 = get width + // argc 2 = pixel at row 0 col n + // argc 3 = pixel at row n col n + return kStub(s, argc + 1, argv - 1); +} + +reg_t kBitmapScale(EngineState *s, int argc, reg_t *argv) { + // TODO: SCI3 + return kStubNull(s, argc + 1, argv - 1); +} + +reg_t kBitmapCreateFromUnknown(EngineState *s, int argc, reg_t *argv) { + // TODO: SCI3 + return kStub(s, argc + 1, argv - 1); } // Used for edit boxes in save/load dialogs. It's a rewritten version of kEditControl, @@ -764,6 +782,10 @@ reg_t kPalVarySetVary(EngineState *s, int argc, reg_t *argv) { reg_t kPalVarySetPercent(EngineState *s, int argc, reg_t *argv) { int time = argc > 0 ? argv[0].toSint16() * 60 : 0; int16 percent = argc > 1 ? argv[1].toSint16() : 0; + + // TODO: GK1 adds a third optional parameter here, at the end of chapter 1 + // (during the sunset/sunrise sequence, the parameter is 1) + g_sci->_gfxPalette32->setVaryPercent(percent, time, -1, -1); return NULL_REG; } diff --git a/engines/sci/engine/kpathing.cpp b/engines/sci/engine/kpathing.cpp index 5b2245e84d..7ac744f584 100644 --- a/engines/sci/engine/kpathing.cpp +++ b/engines/sci/engine/kpathing.cpp @@ -1943,14 +1943,14 @@ static int liesBefore(const Vertex *v, const Common::Point &p1, const Common::Po // indexp1/vertexp1 on the polygon being merged. // It ends with the point intersection2, being the analogous intersection. struct Patch { - unsigned int indexw1; - unsigned int indexp1; + uint32 indexw1; + uint32 indexp1; const Vertex *vertexw1; const Vertex *vertexp1; Common::Point intersection1; - unsigned int indexw2; - unsigned int indexp2; + uint32 indexw2; + uint32 indexp2; const Vertex *vertexw2; const Vertex *vertexp2; Common::Point intersection2; @@ -1960,7 +1960,7 @@ struct Patch { // Check if the given vertex on the work polygon is bypassed by this patch. -static bool isVertexCovered(const Patch &p, unsigned int wi) { +static bool isVertexCovered(const Patch &p, uint32 wi) { // / v (outside) // ---w1--1----p----w2--2---- @@ -2402,7 +2402,7 @@ reg_t kMergePoly(EngineState *s, int argc, reg_t *argv) { // Copy work.vertices into arrayRef Vertex *vertex; - unsigned int n = 0; + uint32 n = 0; CLIST_FOREACH(vertex, &work.vertices) { if (vertex == work.vertices._head || vertex->v != vertex->_prev->v) writePoint(arrayRef, n++, vertex->v); diff --git a/engines/sci/engine/kstring.cpp b/engines/sci/engine/kstring.cpp index 310e38dbd1..f598cf7457 100644 --- a/engines/sci/engine/kstring.cpp +++ b/engines/sci/engine/kstring.cpp @@ -661,19 +661,6 @@ reg_t kStrSplit(EngineState *s, int argc, reg_t *argv) { #ifdef ENABLE_SCI32 -reg_t kText(EngineState *s, int argc, reg_t *argv) { - switch (argv[0].toUint16()) { - case 0: - return kTextSize(s, argc - 1, argv + 1); - default: - // TODO: Other subops here too, perhaps kTextColors and kTextFonts - warning("kText(%d)", argv[0].toUint16()); - break; - } - - return s->r_acc; -} - // TODO: there is an unused second argument, happens at least in LSL6 right during the intro reg_t kStringNew(EngineState *s, int argc, reg_t *argv) { reg_t stringHandle; diff --git a/engines/sci/engine/object.cpp b/engines/sci/engine/object.cpp index 0626c084c1..0566d6955f 100644 --- a/engines/sci/engine/object.cpp +++ b/engines/sci/engine/object.cpp @@ -255,6 +255,8 @@ void Object::initSelectorsSci3(const byte *buf) { if (g_sci->getKernel()->getSelectorNamesSize() % 32) ++groups; + _mustSetViewVisible.resize(groups); + methods = properties = 0; // Selectors are divided into groups of 32, of which the first @@ -270,7 +272,9 @@ void Object::initSelectorsSci3(const byte *buf) { // This object actually has selectors belonging to this group int typeMask = READ_SCI11ENDIAN_UINT32(seeker); - for (int bit = 2; bit < 32; ++bit) { + _mustSetViewVisible[groupNr] = (typeMask & 1); + + for (int bit = 2; bit < 32; ++bit) { int value = READ_SCI11ENDIAN_UINT16(seeker + bit * 2); if (typeMask & (1 << bit)) { // Property ++properties; @@ -281,7 +285,8 @@ void Object::initSelectorsSci3(const byte *buf) { } } - } + } else + _mustSetViewVisible[groupNr] = false; } _variables.resize(properties); diff --git a/engines/sci/engine/object.h b/engines/sci/engine/object.h index cc9f5ebb52..a7be170f4f 100644 --- a/engines/sci/engine/object.h +++ b/engines/sci/engine/object.h @@ -262,6 +262,8 @@ public: bool initBaseObject(SegManager *segMan, reg_t addr, bool doInitSuperClass = true); void syncBaseObject(const byte *ptr) { _baseObj = ptr; } + bool mustSetViewVisibleSci3(int selector) const { return _mustSetViewVisible[selector/32]; } + private: void initSelectorsSci3(const byte *buf); @@ -278,6 +280,7 @@ private: reg_t _superClassPosSci3; /**< reg_t pointing to superclass for SCI3 */ reg_t _speciesSelectorSci3; /**< reg_t containing species "selector" for SCI3 */ reg_t _infoSelectorSci3; /**< reg_t containing info "selector" for SCI3 */ + Common::Array<bool> _mustSetViewVisible; /** cached bit of info to make lookup fast, SCI3 only */ }; diff --git a/engines/sci/engine/savegame.cpp b/engines/sci/engine/savegame.cpp index 18cee3321f..ae7ab431f8 100644 --- a/engines/sci/engine/savegame.cpp +++ b/engines/sci/engine/savegame.cpp @@ -1081,8 +1081,13 @@ void gamestate_restore(EngineState *s, Common::SeekableReadStream *fh) { // We MUST NOT delete all planes/screen items. At least Space Quest 6 has a few in memory like for example // the options plane, which are not re-added and are in memory all the time right from the start of the // game. Sierra SCI32 did not clear planes, only scripts cleared the ones inside planes::elements. - if (getSciVersion() >= SCI_VERSION_2) - g_sci->_gfxFrameout->syncWithScripts(false); + if (getSciVersion() >= SCI_VERSION_2) { + if (!s->_delayedRestoreFromLauncher) { + // Only do it, when we are restoring regulary and not from launcher + // As it could result in option planes etc. on the screen (happens in gk1) + g_sci->_gfxFrameout->syncWithScripts(false); + } + } #endif s->reset(true); @@ -1131,6 +1136,8 @@ void gamestate_restore(EngineState *s, Common::SeekableReadStream *fh) { // signal restored game to game scripts s->gameIsRestarting = GAMEISRESTARTING_RESTORE; + + s->_delayedRestoreFromLauncher = false; } bool get_savegame_metadata(Common::SeekableReadStream *stream, SavegameMetadata *meta) { diff --git a/engines/sci/engine/scriptdebug.cpp b/engines/sci/engine/scriptdebug.cpp index f0157a6569..7d70f30d55 100644 --- a/engines/sci/engine/scriptdebug.cpp +++ b/engines/sci/engine/scriptdebug.cpp @@ -499,7 +499,7 @@ void Kernel::dumpScriptClass(char *data, int seeker, int objsize) { void Kernel::dissectScript(int scriptNumber, Vocabulary *vocab) { int objectctr[11] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - unsigned int _seeker = 0; + uint32 _seeker = 0; Resource *script = _resMan->findResource(ResourceId(kResourceTypeScript, scriptNumber), 0); if (!script) { @@ -510,7 +510,7 @@ void Kernel::dissectScript(int scriptNumber, Vocabulary *vocab) { while (_seeker < script->size) { int objType = (int16)READ_SCI11ENDIAN_UINT16(script->data + _seeker); int objsize; - unsigned int seeker = _seeker + 4; + uint32 seeker = _seeker + 4; if (!objType) { debugN("End of script object (#0) encountered.\n"); diff --git a/engines/sci/engine/segment.h b/engines/sci/engine/segment.h index de7f60ac16..2699bc2e5b 100644 --- a/engines/sci/engine/segment.h +++ b/engines/sci/engine/segment.h @@ -203,7 +203,7 @@ struct List { struct Hunk { void *mem; - unsigned int size; + uint32 size; const char *type; }; diff --git a/engines/sci/engine/selector.cpp b/engines/sci/engine/selector.cpp index 320f2c0664..1393e96880 100644 --- a/engines/sci/engine/selector.cpp +++ b/engines/sci/engine/selector.cpp @@ -207,10 +207,10 @@ reg_t readSelector(SegManager *segMan, reg_t object, Selector selectorId) { } #ifdef ENABLE_SCI32 -void updateInfoFlagViewVisible(Object *obj, int offset) { +void updateInfoFlagViewVisible(Object *obj, int index) { // TODO: Make this correct for all SCI versions // Selectors 26 through 44 are selectors for View script objects in SQ6 - if (offset >= 26 && offset <= 44 && getSciVersion() >= SCI_VERSION_2) { + if (index >= 26 && index <= 44 && getSciVersion() >= SCI_VERSION_2) { obj->setInfoSelectorFlag(kInfoFlagViewVisible); } } diff --git a/engines/sci/engine/selector.h b/engines/sci/engine/selector.h index 1952ca0599..a8bbbe75e3 100644 --- a/engines/sci/engine/selector.h +++ b/engines/sci/engine/selector.h @@ -201,10 +201,10 @@ void invokeSelector(EngineState *s, reg_t object, int selectorId, /** * SCI32 set kInfoFlagViewVisible in the -info- selector if a certain * range of properties was written to. - * This function checks if offset is in the right range, and sets the flag + * This function checks if index is in the right range, and sets the flag * on obj.-info- if it is. */ -void updateInfoFlagViewVisible(Object *obj, int offset); +void updateInfoFlagViewVisible(Object *obj, int index); #endif } // End of namespace Sci diff --git a/engines/sci/engine/state.cpp b/engines/sci/engine/state.cpp index d53e6b48c8..fda78317b5 100644 --- a/engines/sci/engine/state.cpp +++ b/engines/sci/engine/state.cpp @@ -95,6 +95,7 @@ void EngineState::reset(bool isRestoring) { // reset delayed restore game functionality _delayedRestoreGame = false; _delayedRestoreGameId = 0; + _delayedRestoreFromLauncher = false; executionStackBase = 0; _executionStackPosChanged = false; diff --git a/engines/sci/engine/state.h b/engines/sci/engine/state.h index 0f04e32fe5..cf9a753f5c 100644 --- a/engines/sci/engine/state.h +++ b/engines/sci/engine/state.h @@ -138,10 +138,12 @@ public: // see detection.cpp / SciEngine::loadGameState() bool _delayedRestoreGame; // boolean, that triggers delayed restore (triggered by ScummVM menu) int _delayedRestoreGameId; // the saved game id, that it supposed to get restored (triggered by ScummVM menu) + bool _delayedRestoreFromLauncher; // is set, when the the delayed restore game was triggered from launcher uint _chosenQfGImportItem; // Remembers the item selected in QfG import rooms bool _cursorWorkaroundActive; // Refer to GfxCursor::setPosition() + int16 _cursorWorkaroundPosCount; // When the cursor is reported to be at the previously set coordinate, we won't disable the workaround unless it happened for this many times Common::Point _cursorWorkaroundPoint; Common::Rect _cursorWorkaroundRect; diff --git a/engines/sci/engine/vm.cpp b/engines/sci/engine/vm.cpp index 66d9fee5fd..64e6c045db 100644 --- a/engines/sci/engine/vm.cpp +++ b/engines/sci/engine/vm.cpp @@ -1100,7 +1100,7 @@ void run_vm(EngineState *s) { // Accumulator To Property validate_property(s, obj, opparams[0]) = s->r_acc; #ifdef ENABLE_SCI32 - updateInfoFlagViewVisible(obj, opparams[0]); + updateInfoFlagViewVisible(obj, opparams[0]>>1); #endif break; @@ -1113,7 +1113,7 @@ void run_vm(EngineState *s) { // Stack To Property validate_property(s, obj, opparams[0]) = POP32(); #ifdef ENABLE_SCI32 - updateInfoFlagViewVisible(obj, opparams[0]); + updateInfoFlagViewVisible(obj, opparams[0]>>1); #endif break; @@ -1130,7 +1130,7 @@ void run_vm(EngineState *s) { else opProperty -= 1; #ifdef ENABLE_SCI32 - updateInfoFlagViewVisible(obj, opparams[0]); + updateInfoFlagViewVisible(obj, opparams[0]>>1); #endif if (opcode == op_ipToa || opcode == op_dpToa) s->r_acc = opProperty; diff --git a/engines/sci/engine/workarounds.cpp b/engines/sci/engine/workarounds.cpp index aab32032f7..10731e270c 100644 --- a/engines/sci/engine/workarounds.cpp +++ b/engines/sci/engine/workarounds.cpp @@ -656,6 +656,12 @@ const SciWorkaroundEntry kNewWindow_workarounds[] = { }; // gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround +const SciWorkaroundEntry kPalVarySetPercent_workarounds[] = { + { GID_GK1, 370, 370, 0, "graceComeOut", "changeState", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // there's an extra parameter in GK1, when changing chapters. This extra parameter seems to be a bug or just unimplemented functionality, as there's no visible change from the original in the chapter change room + SCI_WORKAROUNDENTRY_TERMINATOR +}; + +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround const SciWorkaroundEntry kReadNumber_workarounds[] = { { GID_CNICK_LAURABOW,100, 101, 0, "dominoes.opt", "doit", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // When dominoes.opt is present, the game scripts call kReadNumber with an extra integer parameter - bug #6425 { GID_HOYLE3, 100, 101, 0, "dominoes.opt", "doit", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // When dominoes.opt is present, the game scripts call kReadNumber with an extra integer parameter - bug #6425 diff --git a/engines/sci/engine/workarounds.h b/engines/sci/engine/workarounds.h index 46059a175c..8f519a8c9c 100644 --- a/engines/sci/engine/workarounds.h +++ b/engines/sci/engine/workarounds.h @@ -89,6 +89,7 @@ extern const SciWorkaroundEntry kIsObject_workarounds[]; extern const SciWorkaroundEntry kMemory_workarounds[]; extern const SciWorkaroundEntry kMoveCursor_workarounds[]; extern const SciWorkaroundEntry kNewWindow_workarounds[]; +extern const SciWorkaroundEntry kPalVarySetPercent_workarounds[]; extern const SciWorkaroundEntry kReadNumber_workarounds[]; extern const SciWorkaroundEntry kPaletteUnsetFlag_workarounds[]; extern const SciWorkaroundEntry kSetCursor_workarounds[]; diff --git a/engines/sci/event.cpp b/engines/sci/event.cpp index 9a87c6de0b..3322a273ae 100644 --- a/engines/sci/event.cpp +++ b/engines/sci/event.cpp @@ -328,7 +328,7 @@ void EventManager::updateScreen() { } } -SciEvent EventManager::getSciEvent(unsigned int mask) { +SciEvent EventManager::getSciEvent(uint32 mask) { SciEvent event = { SCI_EVENT_NONE, 0, 0, Common::Point(0, 0) }; EventManager::updateScreen(); diff --git a/engines/sci/event.h b/engines/sci/event.h index 76c884aba4..0d0c550622 100644 --- a/engines/sci/event.h +++ b/engines/sci/event.h @@ -121,7 +121,7 @@ public: ~EventManager(); void updateScreen(); - SciEvent getSciEvent(unsigned int mask); + SciEvent getSciEvent(uint32 mask); private: SciEvent getScummVMEvent(); diff --git a/engines/sci/graphics/celobj32.cpp b/engines/sci/graphics/celobj32.cpp index f8bce26a2c..787d295d5e 100644 --- a/engines/sci/graphics/celobj32.cpp +++ b/engines/sci/graphics/celobj32.cpp @@ -109,8 +109,8 @@ struct SCALER_NoScale { const int16 _lastIndex; SCALER_NoScale(const CelObj &celObj, const int16 maxWidth) : - _reader(celObj, maxWidth), - _lastIndex(maxWidth - 1) {} + _reader(celObj, FLIP ? celObj._width : maxWidth), + _lastIndex(celObj._width - 1) {} inline void setSource(const int16 x, const int16 y) { _row = _reader.getRow(y); @@ -968,6 +968,40 @@ byte *CelObjPic::getResPointer() const { #pragma mark - #pragma mark CelObjMem +void CelObjMem::buildBitmapHeader(byte *bitmap, const int16 width, const int16 height, const uint8 skipColor, const int16 displaceX, const int16 displaceY, const int16 scaledWidth, const int16 scaledHeight, const uint32 hunkPaletteOffset, const bool useRemap) { + const uint16 bitmapHeaderSize = getBitmapHeaderSize(); + + WRITE_SCI11ENDIAN_UINT16(bitmap + 0, width); + WRITE_SCI11ENDIAN_UINT16(bitmap + 2, height); + WRITE_SCI11ENDIAN_UINT16(bitmap + 4, (uint16)displaceX); + WRITE_SCI11ENDIAN_UINT16(bitmap + 6, (uint16)displaceY); + bitmap[8] = skipColor; + bitmap[9] = 0; + WRITE_SCI11ENDIAN_UINT16(bitmap + 10, 0); + + if (useRemap) { + bitmap[10] |= 2; + } + + WRITE_SCI11ENDIAN_UINT32(bitmap + 12, width * height); + WRITE_SCI11ENDIAN_UINT32(bitmap + 16, 0); + + if (hunkPaletteOffset) { + WRITE_SCI11ENDIAN_UINT32(bitmap + 20, hunkPaletteOffset + bitmapHeaderSize); + } else { + WRITE_SCI11ENDIAN_UINT32(bitmap + 20, 0); + } + + WRITE_SCI11ENDIAN_UINT32(bitmap + 24, bitmapHeaderSize); + WRITE_SCI11ENDIAN_UINT32(bitmap + 28, bitmapHeaderSize); + WRITE_SCI11ENDIAN_UINT32(bitmap + 32, 0); + + if (bitmapHeaderSize >= 40) { + WRITE_SCI11ENDIAN_UINT16(bitmap + 36, scaledWidth); + WRITE_SCI11ENDIAN_UINT16(bitmap + 38, scaledHeight); + } +} + CelObjMem::CelObjMem(const reg_t bitmap) { _info.type = kCelTypeMem; _info.bitmap = bitmap; @@ -976,8 +1010,9 @@ CelObjMem::CelObjMem(const reg_t bitmap) { _celHeaderOffset = 0; _transparent = true; + const uint32 bitmapHeaderSize = getBitmapHeaderSize(); byte *bitmapData = g_sci->getEngineState()->_segMan->getHunkPointer(bitmap); - if (bitmapData == nullptr || READ_SCI11ENDIAN_UINT32(bitmapData + 28) != 46) { + if (bitmapData == nullptr || READ_SCI11ENDIAN_UINT32(bitmapData + 28) != bitmapHeaderSize) { error("Invalid Text bitmap %04x:%04x", PRINT_REG(bitmap)); } @@ -986,8 +1021,12 @@ CelObjMem::CelObjMem(const reg_t bitmap) { _displace.x = READ_SCI11ENDIAN_UINT16(bitmapData + 4); _displace.y = READ_SCI11ENDIAN_UINT16(bitmapData + 6); _transparentColor = bitmapData[8]; - _scaledWidth = READ_SCI11ENDIAN_UINT16(bitmapData + 36); - _scaledHeight = READ_SCI11ENDIAN_UINT16(bitmapData + 38); + if (bitmapHeaderSize >= 40) { + _scaledWidth = READ_SCI11ENDIAN_UINT16(bitmapData + 36); + _scaledHeight = READ_SCI11ENDIAN_UINT16(bitmapData + 38); + } else { + error("TODO: SCI2 bitmaps not implemented yet!"); + } _hunkPaletteOffset = READ_SCI11ENDIAN_UINT16(bitmapData + 20); _remap = (READ_SCI11ENDIAN_UINT16(bitmapData + 10) & 2) ? true : false; } diff --git a/engines/sci/graphics/celobj32.h b/engines/sci/graphics/celobj32.h index 1422b76a57..5707333818 100644 --- a/engines/sci/graphics/celobj32.h +++ b/engines/sci/graphics/celobj32.h @@ -547,6 +547,34 @@ public: */ class CelObjMem : public CelObj { public: + /** + * Writes a bitmap header to the given data buffer. + */ + static void buildBitmapHeader(byte *bitmap, const int16 width, const int16 height, const uint8 skipColor, const int16 displaceX, const int16 displaceY, const int16 scaledWidth, const int16 scaledHeight, const uint32 hunkPaletteOffset, const bool useRemap); + + /** + * Gets the size of the bitmap header for the current + * engine version. + */ + inline static uint32 getBitmapHeaderSize() { + // TODO: These values are accurate for each engine, but there may be no reason + // to not simply just always use size 40, since SCI2.1mid does not seem to + // actually store any data above byte 40, and SCI2 did not allow bitmaps with + // scaling resolutions other than the default (320x200). Perhaps SCI3 used + // the extra bytes, or there is some reason why they tried to align the header + // size with other headers like pic headers? +// uint32 bitmapHeaderSize; +// if (getSciVersion() >= SCI_VERSION_2_1_MIDDLE) { +// bitmapHeaderSize = 46; +// } else if (getSciVersion() == SCI_VERSION_2_1_EARLY) { +// bitmapHeaderSize = 40; +// } else { +// bitmapHeaderSize = 36; +// } +// return bitmapHeaderSize; + return 46; + } + CelObjMem(reg_t bitmap); virtual ~CelObjMem() override {}; diff --git a/engines/sci/graphics/controls32.cpp b/engines/sci/graphics/controls32.cpp index 04a70d35f3..df518888a9 100644 --- a/engines/sci/graphics/controls32.cpp +++ b/engines/sci/graphics/controls32.cpp @@ -178,11 +178,14 @@ void GfxControls32::kernelTexteditChange(reg_t controlObject) { // Write back string _segMan->strcpy(textReference, text.c_str()); // Modify the buffer and show it + warning("kernelTexteditChange"); +#if 0 _text->createTextBitmap(controlObject, 0, 0, hunkId); _text->drawTextBitmap(0, 0, nsRect, controlObject); //texteditCursorDraw(rect, text.c_str(), cursorPos); // TODO: Cursor g_system->updateScreen(); +#endif } else { // TODO: Cursor /* diff --git a/engines/sci/graphics/cursor.cpp b/engines/sci/graphics/cursor.cpp index e8496b96e5..f5dd473959 100644 --- a/engines/sci/graphics/cursor.cpp +++ b/engines/sci/graphics/cursor.cpp @@ -336,6 +336,9 @@ void GfxCursor::setPosition(Common::Point pos) { && ((workaround->newPositionX == pos.x) && (workaround->newPositionY == pos.y))) { EngineState *s = g_sci->getEngineState(); s->_cursorWorkaroundActive = true; + // At least on OpenPandora it seems that the cursor is actually set, but a bit afterwards + // touch screen controls will overwrite the position. More information see kGetEvent in kevent.cpp. + s->_cursorWorkaroundPosCount = 5; // should be enough for OpenPandora s->_cursorWorkaroundPoint = pos; s->_cursorWorkaroundRect = Common::Rect(workaround->rectLeft, workaround->rectTop, workaround->rectRight, workaround->rectBottom); return; diff --git a/engines/sci/graphics/frameout.cpp b/engines/sci/graphics/frameout.cpp index ccce8ef046..ab2ed7a4a5 100644 --- a/engines/sci/graphics/frameout.cpp +++ b/engines/sci/graphics/frameout.cpp @@ -1600,16 +1600,15 @@ void GfxFrameout::processShowStyles() { } if (doFrameOut) { - Common::Rect frameOutRect(0, 0); - frameOut(true, frameOutRect); - - // TODO: It seems like transitions without the “animate” - // flag are too fast in in SCI2–2.1early, but the throttle - // value is arbitrary. Someone on real hardware probably - // needs to test what the actual speed of transitions - // should be - //state->speedThrottler(30); - //state->_throttleTrigger = true; + frameOut(true); + + // TODO: Transitions without the “animate” flag are too + // fast, but the throttle value is arbitrary. Someone on + // real hardware probably needs to test what the actual + // speed of these transitions should be + EngineState *state = g_sci->getEngineState(); + state->speedThrottler(33); + state->_throttleTrigger = true; } } while(continueProcessing && doFrameOut); } @@ -1948,7 +1947,7 @@ bool GfxFrameout::processShowStyle12(ShowStyleEntry *const showStyle) { } #endif -void GfxFrameout::kernelFrameout(const bool shouldShowBits) { +void GfxFrameout::kernelFrameOut(const bool shouldShowBits) { if (_showStyles != nullptr) { processShowStyles(); } else if (_palMorphIsOn) { @@ -1960,8 +1959,7 @@ void GfxFrameout::kernelFrameout(const bool shouldShowBits) { // doScroll(); // } - Common::Rect frameOutRect(0, 0); - frameOut(shouldShowBits, frameOutRect); + frameOut(shouldShowBits); } } diff --git a/engines/sci/graphics/frameout.h b/engines/sci/graphics/frameout.h index 0da6866f7c..f172997704 100644 --- a/engines/sci/graphics/frameout.h +++ b/engines/sci/graphics/frameout.h @@ -427,7 +427,7 @@ private: * frame. If `shouldShowBits` is true, also sends the * buffer to hardware. */ - void frameOut(const bool shouldShowBits, const Common::Rect &rect); + void frameOut(const bool shouldShowBits, const Common::Rect &rect = Common::Rect()); /** * Adds a new rectangle to the list of regions to write @@ -466,7 +466,7 @@ public: return _currentBuffer; } - void kernelFrameout(const bool showBits); + void kernelFrameOut(const bool showBits); /** * Modifies the raw pixel data for the next frame with diff --git a/engines/sci/graphics/helpers.h b/engines/sci/graphics/helpers.h index fbad120374..19dddd74b8 100644 --- a/engines/sci/graphics/helpers.h +++ b/engines/sci/graphics/helpers.h @@ -139,16 +139,28 @@ inline void mul(Common::Rect &rect, const Common::Rational &ratioX, const Common } /** + * Multiplies a rectangle by two ratios with default + * rounding. Modifies the rect directly. Uses inclusive + * rectangle rounding. + */ +inline void mulinc(Common::Rect &rect, const Common::Rational &ratioX, const Common::Rational &ratioY) { + rect.left = (rect.left * ratioX).toInt(); + rect.top = (rect.top * ratioY).toInt(); + rect.right = ((rect.right - 1) * ratioX).toInt() + 1; + rect.bottom = ((rect.bottom - 1) * ratioY).toInt() + 1; +} + +/** * Multiplies a number by a rational number, rounding up to * the nearest whole number. */ -inline int mulru(const int value, const Common::Rational &ratio) { - int num = value * ratio.getNumerator(); +inline int mulru(const int value, const Common::Rational &ratio, const int extra = 0) { + int num = (value + extra) * ratio.getNumerator(); int result = num / ratio.getDenominator(); if (num > ratio.getDenominator() && num % ratio.getDenominator()) { ++result; } - return result; + return result - extra; } /** @@ -165,19 +177,12 @@ inline void mulru(Common::Point &point, const Common::Rational &ratioX, const Co * Multiplies a point by two rational numbers for X and Y, * rounding up to the nearest whole number. Modifies the * rect directly. - * - * @note In SCI engine, the bottom-right corner of rects - * received an additional one pixel during the - * multiplication in order to round up to include the - * bottom-right corner. Since ScummVM rects do not include - * the bottom-right corner, doing this ends up making rects - * a pixel too wide/tall depending upon the remainder. */ -inline void mulru(Common::Rect &rect, const Common::Rational &ratioX, const Common::Rational &ratioY) { +inline void mulru(Common::Rect &rect, const Common::Rational &ratioX, const Common::Rational &ratioY, const int extra) { rect.left = mulru(rect.left, ratioX); rect.top = mulru(rect.top, ratioY); - rect.right = mulru(rect.right, ratioX); - rect.bottom = mulru(rect.bottom, ratioY); + rect.right = mulru(rect.right - 1, ratioX, extra) + 1; + rect.bottom = mulru(rect.bottom - 1, ratioY, extra) + 1; } struct Buffer : public Graphics::Surface { diff --git a/engines/sci/graphics/plane32.cpp b/engines/sci/graphics/plane32.cpp index d0de5b5917..e23017f21e 100644 --- a/engines/sci/graphics/plane32.cpp +++ b/engines/sci/graphics/plane32.cpp @@ -136,7 +136,7 @@ void Plane::convertGameRectToPlaneRect() { const Ratio ratioY = Ratio(screenHeight, scriptHeight); _planeRect = _gameRect; - mulru(_planeRect, ratioX, ratioY); + mulru(_planeRect, ratioX, ratioY, 1); } void Plane::printDebugInfo(Console *con) const { @@ -381,20 +381,21 @@ void Plane::calcLists(Plane &visiblePlane, const PlaneList &planeList, DrawList breakEraseListByPlanes(eraseList, planeList); breakDrawListByPlanes(drawList, planeList); + // NOTE: Setting this to true fixes the menu bars in GK1 if (/* TODO: dword_C6288 */ false) { // "high resolution pictures"???? _screenItemList.sort(); bool encounteredPic = false; bool v81 = false; for (RectList::size_type i = 0; i < eraseList.size(); ++i) { - Common::Rect *rect = eraseList[i]; + const Common::Rect *rect = eraseList[i]; for (ScreenItemList::size_type j = 0; j < _screenItemList.size(); ++j) { ScreenItem *item = _screenItemList[j]; if (j < _screenItemList.size() && item != nullptr) { if (rect->intersects(item->_screenRect)) { - Common::Rect intersection = rect->findIntersectingRect(item->_screenRect); + const Common::Rect intersection = rect->findIntersectingRect(item->_screenRect); if (!item->_deleted) { if (encounteredPic) { if (item->_celInfo.type == kCelTypePic) { @@ -427,13 +428,13 @@ void Plane::calcLists(Plane &visiblePlane, const PlaneList &planeList, DrawList for (RectList::size_type i = 0; i < eraseList.size(); ++i) { for (ScreenItemList::size_type j = 0; j < _screenItemList.size(); ++j) { ScreenItem *item = _screenItemList[j]; - if (j < _screenItemList.size() && item != nullptr && !item->_updated && !item->_deleted && !item->_created && eraseList[i]->intersects(item->_screenRect)) { + if (item != nullptr && !item->_updated && !item->_deleted && !item->_created && eraseList[i]->intersects(item->_screenRect)) { drawList.add(item, eraseList[i]->findIntersectingRect(item->_screenRect)); } } } } - if (/* TODO: g_Remap_numActiveRemaps */ false) { // no remaps active? + if (/* TODO: g_Remap_numActiveRemaps == 0 */ true) { // no remaps active? // Add all items that overlap with items in the drawlist and have higher // priority for (DrawList::size_type i = 0; i < drawList.size(); ++i) { @@ -446,7 +447,7 @@ void Plane::calcLists(Plane &visiblePlane, const PlaneList &planeList, DrawList if (j < _screenItemList.size() && sli) { if (!sli->_updated && !sli->_deleted && !sli->_created) { ScreenItem *item = dli->screenItem; - if (sli->_priority > item->_priority || (sli->_priority == item->_priority && sli->_object > item->_object)) { + if (sli->_priority > item->_priority /* TODO: || (sli->_priority == item->_priority && sli->_object > item->_object)*/) { if (dli->rect.intersects(sli->_screenRect)) { drawList.add(sli, dli->rect.findIntersectingRect(sli->_screenRect)); } @@ -510,7 +511,7 @@ void Plane::decrementScreenItemArrayCounts(Plane *visiblePlane, const bool force void Plane::filterDownEraseRects(DrawList &drawList, RectList &eraseList, RectList &transparentEraseList) const { if (_type == kPlaneTypeTransparent) { for (RectList::size_type i = 0; i < transparentEraseList.size(); ++i) { - Common::Rect *r = transparentEraseList[i]; + const Common::Rect *r = transparentEraseList[i]; for (ScreenItemList::size_type j = 0; j < _screenItemList.size(); ++j) { ScreenItem *item = _screenItemList[j]; if (item != nullptr) { @@ -538,7 +539,7 @@ void Plane::filterDownEraseRects(DrawList &drawList, RectList &eraseList, RectLi } Common::Rect ptr[4]; - Common::Rect *r2 = transparentEraseList[i]; + const Common::Rect *r2 = transparentEraseList[i]; int count = splitRects(*r2, *r, ptr); for (int k = count - 1; k >= 0; --k) { transparentEraseList.add(ptr[k]); @@ -553,7 +554,7 @@ void Plane::filterDownEraseRects(DrawList &drawList, RectList &eraseList, RectLi void Plane::filterUpDrawRects(DrawList &transparentDrawList, const DrawList &drawList) const { for (DrawList::size_type i = 0; i < drawList.size(); ++i) { - Common::Rect &r = drawList[i]->rect; + const Common::Rect &r = drawList[i]->rect; for (ScreenItemList::size_type j = 0; j < _screenItemList.size(); ++j) { ScreenItem *item = _screenItemList[j]; @@ -568,7 +569,7 @@ void Plane::filterUpDrawRects(DrawList &transparentDrawList, const DrawList &dra void Plane::filterUpEraseRects(DrawList &drawList, RectList &eraseList) const { for (RectList::size_type i = 0; i < eraseList.size(); ++i) { - Common::Rect &r = *eraseList[i]; + const Common::Rect &r = *eraseList[i]; for (ScreenItemList::size_type j = 0; j < _screenItemList.size(); ++j) { ScreenItem *item = _screenItemList[j]; @@ -594,7 +595,7 @@ void Plane::mergeToDrawList(const DrawList::size_type index, const Common::Rect r = *rects[i]; for (DrawList::size_type j = 0; j < drawList.size(); ++j) { - DrawItem *drawitem = drawList[j]; + const DrawItem *drawitem = drawList[j]; if (item->_object == drawitem->screenItem->_object) { if (drawitem->rect.contains(r)) { rects.erase_at(i); @@ -602,7 +603,7 @@ void Plane::mergeToDrawList(const DrawList::size_type index, const Common::Rect } Common::Rect outRects[4]; - int count = splitRects(r, drawitem->rect, outRects); + const int count = splitRects(r, drawitem->rect, outRects); if (count != -1) { for (int k = count - 1; k >= 0; --k) { rects.add(outRects[k]); @@ -632,14 +633,14 @@ void Plane::mergeToRectList(const Common::Rect &rect, RectList &rectList) const Common::Rect r = *temp[i]; for (RectList::size_type j = 0; j < rectList.size(); ++j) { - Common::Rect *innerRect = rectList[j]; + const Common::Rect *innerRect = rectList[j]; if (innerRect->contains(r)) { temp.erase_at(i); break; } Common::Rect out[4]; - int count = splitRects(r, *innerRect, out); + const int count = splitRects(r, *innerRect, out); if (count != -1) { for (int k = count - 1; k >= 0; --k) { temp.add(out[k]); @@ -838,7 +839,7 @@ int16 PlaneList::getTopSciPlanePriority() const { void PlaneList::add(Plane *plane) { for (iterator it = begin(); it != end(); ++it) { - if ((*it)->_priority < plane->_priority) { + if ((*it)->_priority > plane->_priority) { insert(it, plane); return; } diff --git a/engines/sci/graphics/screen_item32.cpp b/engines/sci/graphics/screen_item32.cpp index 0bbb056071..a07dacee83 100644 --- a/engines/sci/graphics/screen_item32.cpp +++ b/engines/sci/graphics/screen_item32.cpp @@ -256,7 +256,7 @@ void ScreenItem::calcRects(const Plane &plane) { if (_useInsetRect) { Ratio celScriptXRatio(_celObj->_scaledWidth, scriptWidth); Ratio celScriptYRatio(_celObj->_scaledHeight, scriptHeight); - mulru(_screenItemRect, celScriptXRatio, celScriptYRatio); + mulru(_screenItemRect, celScriptXRatio, celScriptYRatio, 0); if (_screenItemRect.intersects(celRect)) { _screenItemRect.clip(celRect); @@ -273,7 +273,7 @@ void ScreenItem::calcRects(const Plane &plane) { } if (!newRatioX.isOne() || !newRatioY.isOne()) { - mulru(_screenItemRect, newRatioX, newRatioY); + mulinc(_screenItemRect, newRatioX, newRatioY); displaceX = (displaceX * newRatioX).toInt(); displaceY = (displaceY * newRatioY).toInt(); } @@ -284,7 +284,7 @@ void ScreenItem::calcRects(const Plane &plane) { displaceX = (displaceX * celXRatio).toInt(); displaceY = (displaceY * celYRatio).toInt(); - mulru(_screenItemRect, celXRatio, celYRatio); + mulinc(_screenItemRect, celXRatio, celYRatio); if (/* TODO: dword_C6288 */ false && _celInfo.type == kCelTypePic) { _scaledPosition.x = _position.x; @@ -300,18 +300,16 @@ void ScreenItem::calcRects(const Plane &plane) { Common::Rect temp(_insetRect); if (!newRatioX.isOne()) { - mulru(temp, newRatioX, Ratio()); + mulinc(temp, newRatioX, Ratio()); } - mulru(temp, celXRatio, Ratio()); + mulinc(temp, celXRatio, Ratio()); CelObjPic *celObjPic = dynamic_cast<CelObjPic *>(_celObj); - temp.translate(celObjPic->_relativePosition.x * screenWidth / scriptWidth - displaceX, 0); - // TODO: This is weird, and probably wrong calculation of widths - // due to BR-inclusion - int deltaX = plane._planeRect.right - plane._planeRect.left + 1 - temp.right - 1 - temp.left; + // TODO: This is weird. + int deltaX = plane._planeRect.width() - temp.right - 1 - temp.left; _scaledPosition.x += deltaX; _screenItemRect.translate(deltaX, 0); @@ -330,9 +328,9 @@ void ScreenItem::calcRects(const Plane &plane) { } if (!newRatioX.isOne() || !newRatioY.isOne()) { - mulru(_screenItemRect, newRatioX, newRatioY); + mulinc(_screenItemRect, newRatioX, newRatioY); // TODO: This was in the original code, baked into the - // multiplication though it is not immediately clear + // multiplication though it is not immediately clear // why this is the only one that reduces the BR corner _screenItemRect.right -= 1; _screenItemRect.bottom -= 1; @@ -346,16 +344,15 @@ void ScreenItem::calcRects(const Plane &plane) { Common::Rect temp(_insetRect); if (!newRatioX.isOne()) { - mulru(temp, newRatioX, Ratio()); + mulinc(temp, newRatioX, Ratio()); temp.right -= 1; } CelObjPic *celObjPic = dynamic_cast<CelObjPic *>(_celObj); temp.translate(celObjPic->_relativePosition.x - (displaceX * newRatioX).toInt(), celObjPic->_relativePosition.y - (_celObj->_displace.y * newRatioY).toInt()); - // TODO: This is weird, and probably wrong calculation of widths - // due to BR-inclusion - int deltaX = plane._gameRect.right - plane._gameRect.left + 1 - temp.right - 1 - temp.left; + // TODO: This is weird. + int deltaX = plane._gameRect.width() - temp.right - 1 - temp.left; _scaledPosition.x += deltaX; _screenItemRect.translate(deltaX, 0); @@ -369,7 +366,7 @@ void ScreenItem::calcRects(const Plane &plane) { Ratio celXRatio(screenWidth, _celObj->_scaledWidth); Ratio celYRatio(screenHeight, _celObj->_scaledHeight); mulru(_scaledPosition, celXRatio, celYRatio); - mulru(_screenItemRect, celXRatio, celYRatio); + mulru(_screenItemRect, celXRatio, celYRatio, 1); } _ratioX = newRatioX * Ratio(screenWidth, _celObj->_scaledWidth); diff --git a/engines/sci/graphics/text32.cpp b/engines/sci/graphics/text32.cpp index eabc329ee0..8e3222f580 100644 --- a/engines/sci/graphics/text32.cpp +++ b/engines/sci/graphics/text32.cpp @@ -29,6 +29,7 @@ #include "sci/engine/selector.h" #include "sci/engine/state.h" #include "sci/graphics/cache.h" +#include "sci/graphics/celobj32.h" #include "sci/graphics/compare.h" #include "sci/graphics/font.h" #include "sci/graphics/frameout.h" @@ -37,7 +38,7 @@ namespace Sci { -#define BITMAP_HEADER_SIZE 46 +int16 GfxText32::_defaultFontId = 0; GfxText32::GfxText32(SegManager *segMan, GfxCache *fonts, GfxScreen *screen) : _segMan(segMan), @@ -45,40 +46,20 @@ GfxText32::GfxText32(SegManager *segMan, GfxCache *fonts, GfxScreen *screen) : _screen(screen), _scaledWidth(g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth), _scaledHeight(g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight), - _bitmap(NULL_REG) {} - -void GfxText32::buildBitmapHeader(byte *bitmap, const int16 width, const int16 height, const uint8 skipColor, const int16 displaceX, const int16 displaceY, const int16 scaledWidth, const int16 scaledHeight, const uint32 hunkPaletteOffset, const bool useRemap) const { - - WRITE_SCI11ENDIAN_UINT16(bitmap + 0, width); - WRITE_SCI11ENDIAN_UINT16(bitmap + 2, height); - WRITE_SCI11ENDIAN_UINT16(bitmap + 4, (uint16)displaceX); - WRITE_SCI11ENDIAN_UINT16(bitmap + 6, (uint16)displaceY); - bitmap[8] = skipColor; - bitmap[9] = 0; - WRITE_SCI11ENDIAN_UINT16(bitmap + 10, 0); - - if (useRemap) { - bitmap[10] |= 2; - } - - WRITE_SCI11ENDIAN_UINT32(bitmap + 12, width * height); - WRITE_SCI11ENDIAN_UINT32(bitmap + 16, 0); - - if (hunkPaletteOffset) { - WRITE_SCI11ENDIAN_UINT32(bitmap + 20, hunkPaletteOffset + BITMAP_HEADER_SIZE); - } else { - WRITE_SCI11ENDIAN_UINT32(bitmap + 20, 0); + // Not a typo, the original engine did not initialise height, only width + _width(0), + _text(""), + _field_20(0), + _field_2C(2), + _field_30(0), + _field_34(0), + _field_38(0), + _field_3C(0), + _bitmap(NULL_REG) { + _fontId = _defaultFontId; + _font = _cache->getFont(_defaultFontId); } - WRITE_SCI11ENDIAN_UINT32(bitmap + 24, BITMAP_HEADER_SIZE); - WRITE_SCI11ENDIAN_UINT32(bitmap + 28, BITMAP_HEADER_SIZE); - WRITE_SCI11ENDIAN_UINT32(bitmap + 32, 0); - WRITE_SCI11ENDIAN_UINT16(bitmap + 36, scaledWidth); - WRITE_SCI11ENDIAN_UINT16(bitmap + 38, scaledHeight); -} - -int16 GfxText32::_defaultFontId = 0; - reg_t GfxText32::createFontBitmap(int16 width, int16 height, const Common::Rect &rect, const Common::String &text, const uint8 foreColor, const uint8 backColor, const uint8 skipColor, const GuiResourceId fontId, const TextAlign alignment, const int16 borderColor, const bool dimmed, const bool doScaling, reg_t *outBitmapObject) { _field_22 = 0; @@ -93,10 +74,7 @@ reg_t GfxText32::createFontBitmap(int16 width, int16 height, const Common::Rect _alignment = alignment; _dimmed = dimmed; - if (fontId != _fontId) { - _fontId = fontId == -1 ? _defaultFontId : fontId; - _font = _cache->getFont(_fontId); - } + setFont(fontId); if (doScaling) { int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; @@ -107,7 +85,7 @@ reg_t GfxText32::createFontBitmap(int16 width, int16 height, const Common::Rect _width = (_width * scaleX).toInt(); _height = (_height * scaleY).toInt(); - mul(_textRect, scaleX, scaleY); + mulinc(_textRect, scaleX, scaleY); } // _textRect represents where text is drawn inside the @@ -120,10 +98,10 @@ reg_t GfxText32::createFontBitmap(int16 width, int16 height, const Common::Rect _textRect = Common::Rect(); } - _bitmap = _segMan->allocateHunkEntry("FontBitmap()", _width * _height + BITMAP_HEADER_SIZE); + _bitmap = _segMan->allocateHunkEntry("FontBitmap()", _width * _height + CelObjMem::getBitmapHeaderSize()); byte *bitmap = _segMan->getHunkPointer(_bitmap); - buildBitmapHeader(bitmap, _width, _height, _skipColor, 0, 0, _scaledWidth, _scaledHeight, 0, false); + CelObjMem::buildBitmapHeader(bitmap, _width, _height, _skipColor, 0, 0, _scaledWidth, _scaledHeight, 0, false); erase(bitmapRect, false); @@ -133,460 +111,455 @@ reg_t GfxText32::createFontBitmap(int16 width, int16 height, const Common::Rect drawTextBox(); - debug("Drawing a bitmap %dx%d, scaled %dx%d, border %d, font %d", width, height, _width, _height, _borderColor, _fontId); - *outBitmapObject = _bitmap; return _bitmap; } -reg_t GfxText32::createTitledFontBitmap(CelInfo32 &celInfo, Common::Rect &rect, Common::String &text, int16 foreColor, int16 backColor, int font, int16 skipColor, int16 borderColor, bool dimmed, void *unknown1) { - warning("TODO: createTitledFontBitmap"); - return NULL_REG; -} +reg_t GfxText32::createFontBitmap(const CelInfo32 &celInfo, const Common::Rect &rect, const Common::String &text, const int16 foreColor, const int16 backColor, const GuiResourceId fontId, const int16 skipColor, const int16 borderColor, const bool dimmed, reg_t *outBitmapObject) { + _field_22 = 0; + _borderColor = borderColor; + _text = text; + _textRect = rect; + _foreColor = foreColor; + _dimmed = dimmed; -void GfxText32::drawFrame(const Common::Rect &rect, const int size, const uint8 color, const bool doScaling) { - Common::Rect targetRect = doScaling ? scaleRect(rect) : rect; + setFont(fontId); - byte *bitmap = _segMan->getHunkPointer(_bitmap); - byte *pixels = bitmap + READ_SCI11ENDIAN_UINT32(bitmap + 28); + int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; + int16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight; - // NOTE: Not fully disassembled, but this should be right - // TODO: Implement variable frame size - assert(size == 1); - Buffer buffer(_width, _height, pixels); - buffer.frameRect(targetRect, color); -} + int borderSize = 1; + mulinc(_textRect, Ratio(_scaledWidth, scriptWidth), Ratio(_scaledHeight, scriptHeight)); -// TODO: This is not disassembled -void GfxText32::drawTextBox() { - int16 charCount = 0; - uint16 curX = 0, curY = 0; - const char *txt = _text.c_str(); - int16 textWidth, textHeight, totalHeight = 0, offsetX = 0, offsetY = 0; - uint16 start = 0; - - // Calculate total text height - while (*txt) { - charCount = GetLongest(txt, _textRect.width(), _font); - if (charCount == 0) - break; - - Width(txt, 0, (int16)strlen(txt), _fontId, textWidth, textHeight, true); - - totalHeight += textHeight; - txt += charCount; - while (*txt == ' ') { - txt++; // skip over breaking spaces - } + CelObjView view(celInfo.resourceId, celInfo.loopNo, celInfo.celNo); + _skipColor = view._transparentColor; + _width = view._width * _scaledWidth / view._scaledWidth; + _height = view._height * _scaledHeight / view._scaledHeight; + + Common::Rect bitmapRect(_width, _height); + if (_textRect.intersects(bitmapRect)) { + _textRect.clip(bitmapRect); + } else { + _textRect = Common::Rect(); } - txt = _text.c_str(); - - byte *pixels = _segMan->getHunkPointer(_bitmap); - pixels = pixels + READ_SCI11ENDIAN_UINT32(pixels + 28) + _width * _textRect.top + _textRect.left; - - // Draw text in buffer - while (*txt) { - charCount = GetLongest(txt, _textRect.width(), _font); - if (charCount == 0) - break; - Width(txt, start, charCount, _fontId, textWidth, textHeight, true); - - switch (_alignment) { - case kTextAlignRight: - offsetX = _textRect.width() - textWidth; - break; - case kTextAlignCenter: - // Center text both horizontally and vertically - offsetX = (_textRect.width() - textWidth) / 2; - offsetY = (_textRect.height() - totalHeight) / 2; - break; - case kTextAlignLeft: - offsetX = 0; - break; - - default: - warning("Invalid alignment %d used in TextBox()", _alignment); - } + _bitmap = _segMan->allocateHunkEntry("FontBitmap()", _width * _height + CelObjMem::getBitmapHeaderSize()); + byte *bitmap = _segMan->getHunkPointer(_bitmap); + CelObjMem::buildBitmapHeader(bitmap, _width, _height, _skipColor, 0, 0, _scaledWidth, _scaledHeight, 0, false); + + Buffer buffer(_width, _height, bitmap + READ_SCI11ENDIAN_UINT32(bitmap + 28)); - byte curChar; - - for (int i = 0; i < charCount; i++) { - curChar = txt[i]; - - switch (curChar) { - case 0x0A: - case 0x0D: - case 0: - break; - case 0x7C: - warning("Code processing isn't implemented in SCI32"); - break; - default: - _font->drawToBuffer(curChar, curY + offsetY, curX + offsetX, _foreColor, _dimmed, pixels, _width, _height); - curX += _font->getCharWidth(curChar); - break; + // NOTE: The engine filled the bitmap pixels with 11 here, which is silly + // because then it just erased the bitmap using the skip color. So we don't + // fill the bitmap redundantly here. + + _backColor = _skipColor; + erase(bitmapRect, false); + _backColor = backColor; + + view.draw(buffer, bitmapRect, Common::Point(0, 0), false, Ratio(_scaledWidth, view._scaledWidth), Ratio(_scaledHeight, view._scaledHeight)); + + if (_backColor != skipColor && _foreColor != skipColor) { + erase(_textRect, false); + } + + if (text.size() > 0) { + if (_foreColor == skipColor) { + error("TODO: Implement transparent text"); + } else { + if (borderColor != -1) { + drawFrame(bitmapRect, borderSize, _borderColor, false); } - } - curX = 0; - curY += _font->getHeight(); - txt += charCount; - while (*txt == ' ') { - txt++; // skip over breaking spaces + drawTextBox(); } } + + *outBitmapObject = _bitmap; + return _bitmap; +} + +void GfxText32::setFont(const GuiResourceId fontId) { + // NOTE: In SCI engine this calls FontMgr::BuildFontTable and then a font + // table is built on the FontMgr directly; instead, because we already have + // font resources, this code just grabs a font out of GfxCache. + if (fontId != _fontId) { + _fontId = fontId == -1 ? _defaultFontId : fontId; + _font = _cache->getFont(_fontId); + } } -void GfxText32::erase(const Common::Rect &rect, const bool doScaling) { - Common::Rect targetRect = doScaling ? rect : scaleRect(rect); +void GfxText32::drawFrame(const Common::Rect &rect, const int16 size, const uint8 color, const bool doScaling) { + Common::Rect targetRect = doScaling ? scaleRect(rect) : rect; byte *bitmap = _segMan->getHunkPointer(_bitmap); byte *pixels = bitmap + READ_SCI11ENDIAN_UINT32(bitmap + 28); - // NOTE: There is an extra optimisation within the SCI code to - // do a single memset if the scaledRect is the same size as - // the bitmap, not implemented here. + // NOTE: Not fully disassembled, but this should be right + // TODO: Implement variable frame size + assert(size == 1); Buffer buffer(_width, _height, pixels); - buffer.fillRect(targetRect, _backColor); -} - -reg_t GfxText32::createScrollTextBitmap(Common::String text, reg_t textObject, uint16 maxWidth, uint16 maxHeight, reg_t prevHunk) { - return createTextBitmapInternal(text, textObject, maxWidth, maxHeight, prevHunk); + buffer.frameRect(targetRect, color); } -reg_t GfxText32::createTextBitmap(reg_t textObject, uint16 maxWidth, uint16 maxHeight, reg_t prevHunk) { - reg_t stringObject = readSelector(_segMan, textObject, SELECTOR(text)); - // The object in the text selector of the item can be either a raw string - // or a Str object. In the latter case, we need to access the object's data - // selector to get the raw string. - if (_segMan->isHeapObject(stringObject)) - stringObject = readSelector(_segMan, stringObject, SELECTOR(data)); - Common::String text = _segMan->getString(stringObject); +void GfxText32::drawChar(const uint8 charIndex) { + byte *bitmap = _segMan->getHunkPointer(_bitmap); + byte *pixels = bitmap + READ_SCI11ENDIAN_UINT32(bitmap + 28); - return createTextBitmapInternal(text, textObject, maxWidth, maxHeight, prevHunk); + _font->drawToBuffer(charIndex, _drawPosition.y, _drawPosition.x, _foreColor, _dimmed, pixels, _width, _height); + _drawPosition.x += _font->getCharWidth(charIndex); } -reg_t GfxText32::createTextBitmapInternal(Common::String &text, reg_t textObject, uint16 maxWidth, uint16 maxHeight, reg_t prevHunk) { - GuiResourceId fontId = readSelectorValue(_segMan, textObject, SELECTOR(font)); - GfxFont *font = _cache->getFont(fontId); - bool dimmed = readSelectorValue(_segMan, textObject, SELECTOR(dimmed)); - int16 alignment = readSelectorValue(_segMan, textObject, SELECTOR(mode)); - uint16 foreColor = readSelectorValue(_segMan, textObject, SELECTOR(fore)); - uint16 backColor = readSelectorValue(_segMan, textObject, SELECTOR(back)); - - Common::Rect nsRect = g_sci->_gfxCompare->getNSRect(textObject); - uint16 width = nsRect.width() + 1; - uint16 height = nsRect.height() + 1; - - // Limit rectangle dimensions, if requested - if (maxWidth > 0) - width = maxWidth; - if (maxHeight > 0) - height = maxHeight; - - // Upscale the coordinates/width if the fonts are already upscaled - if (_screen->fontIsUpscaled()) { - width = width * _screen->getDisplayWidth() / _screen->getWidth(); - height = height * _screen->getDisplayHeight() / _screen->getHeight(); +uint16 GfxText32::getCharWidth(const uint8 charIndex, const bool doScaling) const { + uint16 width = _font->getCharWidth(charIndex); + if (doScaling) { + width = scaleUpWidth(width); } + return width; +} - int entrySize = width * height + BITMAP_HEADER_SIZE; - reg_t memoryId = NULL_REG; - if (prevHunk.isNull()) { - memoryId = _segMan->allocateHunkEntry("TextBitmap()", entrySize); - - // Scroll text objects have no bitmap selector! - ObjVarRef varp; - if (lookupSelector(_segMan, textObject, SELECTOR(bitmap), &varp, NULL) == kSelectorVariable) - writeSelector(_segMan, textObject, SELECTOR(bitmap), memoryId); - } else { - memoryId = prevHunk; +void GfxText32::drawTextBox() { + if (_text.size() == 0) { + return; } - byte *memoryPtr = _segMan->getHunkPointer(memoryId); - - if (prevHunk.isNull()) - memset(memoryPtr, 0, BITMAP_HEADER_SIZE); - - byte *bitmap = memoryPtr + BITMAP_HEADER_SIZE; - memset(bitmap, backColor, width * height); - - // Save totalWidth, totalHeight - WRITE_SCI11ENDIAN_UINT16(memoryPtr, width); - WRITE_SCI11ENDIAN_UINT16(memoryPtr + 2, height); - WRITE_SCI11ENDIAN_UINT16(memoryPtr + 4, 0); - WRITE_SCI11ENDIAN_UINT16(memoryPtr + 6, 0); - memoryPtr[8] = 0; - WRITE_SCI11ENDIAN_UINT16(memoryPtr + 10, 0); - WRITE_SCI11ENDIAN_UINT16(memoryPtr + 20, BITMAP_HEADER_SIZE); - WRITE_SCI11ENDIAN_UINT32(memoryPtr + 28, 46); - WRITE_SCI11ENDIAN_UINT16(memoryPtr + 36, width); - WRITE_SCI11ENDIAN_UINT16(memoryPtr + 38, height); - - int16 charCount = 0; - uint16 curX = 0, curY = 0; - const char *txt = text.c_str(); - int16 textWidth, textHeight, totalHeight = 0, offsetX = 0, offsetY = 0; - uint16 start = 0; - - // Calculate total text height - while (*txt) { - charCount = GetLongest(txt, width, font); - if (charCount == 0) - break; - - Width(txt, 0, (int16)strlen(txt), fontId, textWidth, textHeight, true); - - totalHeight += textHeight; - txt += charCount; - while (*txt == ' ') - txt++; // skip over breaking spaces + + const char *text = _text.c_str(); + const char *sourceText = text; + int16 textRectWidth = _textRect.width(); + _drawPosition.y = _textRect.top; + uint charIndex = 0; + if (getLongest(&charIndex, textRectWidth) == 0) { + error("DrawTextBox GetLongest=0"); } - txt = text.c_str(); - - // Draw text in buffer - while (*txt) { - charCount = GetLongest(txt, width, font); - if (charCount == 0) - break; - Width(txt, start, charCount, fontId, textWidth, textHeight, true); - - switch (alignment) { - case kTextAlignRight: - offsetX = width - textWidth; - break; - case kTextAlignCenter: - // Center text both horizontally and vertically - offsetX = (width - textWidth) / 2; - offsetY = (height - totalHeight) / 2; - break; - case kTextAlignLeft: - offsetX = 0; - break; - - default: - warning("Invalid alignment %d used in TextBox()", alignment); - } + charIndex = 0; + uint nextCharIndex = 0; + while (*text != '\0') { + _drawPosition.x = _textRect.left; - byte curChar; - - for (int i = 0; i < charCount; i++) { - curChar = txt[i]; - - switch (curChar) { - case 0x0A: - case 0x0D: - case 0: - break; - case 0x7C: - warning("Code processing isn't implemented in SCI32"); - break; - default: - font->drawToBuffer(curChar, curY + offsetY, curX + offsetX, foreColor, dimmed, bitmap, width, height); - curX += font->getCharWidth(curChar); - break; - } + uint length = getLongest(&nextCharIndex, textRectWidth); + int16 textWidth = getTextWidth(charIndex, length); + + if (_alignment == kTextAlignCenter) { + _drawPosition.x += (textRectWidth - textWidth) / 2; + } else if (_alignment == kTextAlignRight) { + _drawPosition.x += textRectWidth - textWidth; } - curX = 0; - curY += font->getHeight(); - txt += charCount; - while (*txt == ' ') - txt++; // skip over breaking spaces + drawText(charIndex, length); + charIndex = nextCharIndex; + text = sourceText + charIndex; + _drawPosition.y += _font->getHeight(); } - - return memoryId; } -void GfxText32::disposeTextBitmap(reg_t hunkId) { - _segMan->freeHunkEntry(hunkId); -} +void GfxText32::drawText(const uint index, uint length) { + assert(index + length <= _text.size()); -void GfxText32::drawTextBitmap(int16 x, int16 y, Common::Rect planeRect, reg_t textObject) { - reg_t hunkId = readSelector(_segMan, textObject, SELECTOR(bitmap)); - drawTextBitmapInternal(x, y, planeRect, textObject, hunkId); -} + // NOTE: This draw loop implementation is somewhat different than the + // implementation in the actual engine, but should be accurate. Primarily + // the changes revolve around eliminating some extra temporaries and + // fixing the logic to match. + const char *text = _text.c_str() + index; + while (length-- > 0) { + char currentChar = *text++; -void GfxText32::drawScrollTextBitmap(reg_t textObject, reg_t hunkId, uint16 x, uint16 y) { - /*reg_t plane = readSelector(_segMan, textObject, SELECTOR(plane)); - Common::Rect planeRect; - planeRect.top = readSelectorValue(_segMan, plane, SELECTOR(top)); - planeRect.left = readSelectorValue(_segMan, plane, SELECTOR(left)); - planeRect.bottom = readSelectorValue(_segMan, plane, SELECTOR(bottom)); - planeRect.right = readSelectorValue(_segMan, plane, SELECTOR(right)); + if (currentChar == '|') { + const char controlChar = *text++; + --length; - drawTextBitmapInternal(x, y, planeRect, textObject, hunkId);*/ + if (length == 0) { + return; + } - // HACK: we pretty much ignore the plane rect and x, y... - drawTextBitmapInternal(0, 0, Common::Rect(20, 390, 600, 460), textObject, hunkId); + if (controlChar == 'a' || controlChar == 'c' || controlChar == 'f') { + uint16 value = 0; + + while (length > 0) { + const char valueChar = *text; + if (valueChar < '0' || valueChar > '9') { + break; + } + + ++text; + --length; + value = 10 * value + (valueChar - '0'); + } + + if (length == 0) { + return; + } + + if (controlChar == 'a') { + _alignment = (TextAlign)value; + } else if (controlChar == 'c') { + _foreColor = value; + } else if (controlChar == 'f') { + setFont(value); + } + } + + while (length > 0 && *text != '|') { + ++text; + --length; + } + } else { + drawChar(currentChar); + } + } } -void GfxText32::drawTextBitmapInternal(int16 x, int16 y, Common::Rect planeRect, reg_t textObject, reg_t hunkId) { - int16 backColor = (int16)readSelectorValue(_segMan, textObject, SELECTOR(back)); - // Sanity check: Check if the hunk is set. If not, either the game scripts - // didn't set it, or an old saved game has been loaded, where it wasn't set. - if (hunkId.isNull()) - return; +uint GfxText32::getLongest(uint *charIndex, const int16 width) { + assert(width > 0); - // Negative coordinates indicate that text shouldn't be displayed - if (x < 0 || y < 0) - return; + uint testLength = 0; + uint length = 0; - byte *memoryPtr = _segMan->getHunkPointer(hunkId); + const uint initialCharIndex = *charIndex; - if (!memoryPtr) { - // Happens when restoring in some SCI32 games (e.g. SQ6). - // Commented out to reduce console spam - //warning("Attempt to draw an invalid text bitmap"); - return; - } + // The index of the next word after the last word break + uint lastWordBreakIndex = *charIndex; - byte *surface = memoryPtr + BITMAP_HEADER_SIZE; + const char *text = _text.c_str() + *charIndex; - int curByte = 0; - int16 skipColor = (int16)readSelectorValue(_segMan, textObject, SELECTOR(skip)); - uint16 textX = planeRect.left + x; - uint16 textY = planeRect.top + y; - // Get totalWidth, totalHeight - uint16 width = READ_LE_UINT16(memoryPtr); - uint16 height = READ_LE_UINT16(memoryPtr + 2); + char currentChar; + while ((currentChar = *text++) != '\0') { + // NOTE: In the original engine, the font, color, and alignment were + // reset here to their initial values - // Upscale the coordinates/width if the fonts are already upscaled - if (_screen->fontIsUpscaled()) { - textX = textX * _screen->getDisplayWidth() / _screen->getWidth(); - textY = textY * _screen->getDisplayHeight() / _screen->getHeight(); - } + // The text to render contains a line break; stop at the line break + if (currentChar == '\r' || currentChar == '\n') { + // Skip the rest of the line break if it is a Windows-style + // \r\n or non-standard \n\r + // NOTE: In the original engine, the `text` pointer had not been + // advanced yet so the indexes used to access characters were + // one higher + if ( + (currentChar == '\r' && text[0] == '\n') || + (currentChar == '\n' && text[0] == '\r' && text[1] != '\n') + ) { + ++*charIndex; + } - bool translucent = (skipColor == -1 && backColor == -1); + // We are at the end of a line but the last word in the line made + // it too wide to fit in the text area; return up to the previous + // word + if (length && getTextWidth(initialCharIndex, testLength) > width) { + *charIndex = lastWordBreakIndex; + return length; + } + + // Skip the line break and return all text seen up to now + // NOTE: In original engine, the font, color, and alignment were + // reset, then getTextWidth was called to use its side-effects to + // set font, color, and alignment according to the text from + // `initialCharIndex` to `testLength` + ++*charIndex; + return testLength; + } else if (currentChar == ' ') { + // The last word in the line made it too wide to fit in the text area; + // return up to the previous word, then collapse the whitespace + // between that word and its next sibling word into the line break + if (getTextWidth(initialCharIndex, testLength) > width) { + *charIndex = lastWordBreakIndex; + const char *nextChar = _text.c_str() + lastWordBreakIndex; + while (*nextChar++ == ' ') { + ++*charIndex; + } + + // NOTE: In original engine, the font, color, and alignment were + // set here to the values that were seen at the last space character + return length; + } - for (int curY = 0; curY < height; curY++) { - for (int curX = 0; curX < width; curX++) { - byte pixel = surface[curByte++]; - if ((!translucent && pixel != skipColor && pixel != backColor) || - (translucent && pixel != 0xFF)) - _screen->putFontPixel(textY, curX + textX, curY, pixel); + // NOTE: In the original engine, the values of _fontId, _foreColor, + // and _alignment were stored for use in the return path mentioned + // just above here + + // We found a word break that was within the text area, memorise it + // and continue processing. +1 on the character index because it has + // not been incremented yet so currently points to the word break + // and not the word after the break + length = testLength; + lastWordBreakIndex = *charIndex + 1; } - } -} -int16 GfxText32::GetLongest(const char *text, int16 maxWidth, GfxFont *font) { - uint16 curChar = 0; - int16 maxChars = 0, curCharCount = 0; - uint16 width = 0; - - while (width <= maxWidth) { - curChar = (*(const byte *)text++); - - switch (curChar) { - // We need to add 0xD, 0xA and 0xD 0xA to curCharCount and then exit - // which means, we split text like - // 'Mature, experienced software analyst available.' 0xD 0xA - // 'Bug installation a proven speciality. "No version too clean."' (normal game text, this is from lsl2) - // and 0xA '-------' 0xA (which is the official sierra subtitle separator) - // Sierra did it the same way. - case 0xD: - // Check, if 0xA is following, if so include it as well - if ((*(const unsigned char *)text) == 0xA) - curCharCount++; - // it's meant to pass through here - case 0xA: - curCharCount++; - // and it's also meant to pass through here - case 0: - return curCharCount; - case ' ': - maxChars = curCharCount; // return count up to (but not including) breaking space - break; + // In the middle of a line, keep processing + ++*charIndex; + ++testLength; + + // NOTE: In the original engine, the font, color, and alignment were + // reset here to their initial values + + // The text to render contained no word breaks yet but is already too + // wide for the text area; just split the word in half at the point + // where it overflows + if (length == 0 && getTextWidth(initialCharIndex, testLength) > width) { + *charIndex = --testLength + lastWordBreakIndex; + return testLength; } - if (width + font->getCharWidth(curChar) > maxWidth) - break; - width += font->getCharWidth(curChar); - curCharCount++; } - return maxChars; -} + // The complete text to render was a single word, or was narrower than + // the text area, so return the entire line + if (length == 0 || getTextWidth(initialCharIndex, testLength) <= width) { + // NOTE: In original engine, the font, color, and alignment were + // reset, then getTextWidth was called to use its side-effects to + // set font, color, and alignment according to the text from + // `initialCharIndex` to `testLength` + return testLength; + } -void GfxText32::kernelTextSize(const char *text, int16 font, int16 maxWidth, int16 *textWidth, int16 *textHeight) { - Common::Rect rect(0, 0, 0, 0); - Size(rect, text, font, maxWidth); - *textWidth = rect.width(); - *textHeight = rect.height(); + // The last word in the line made it wider than the text area, so return + // up to the penultimate word + *charIndex = lastWordBreakIndex; + return length; } -void GfxText32::StringWidth(const char *str, GuiResourceId fontId, int16 &textWidth, int16 &textHeight) { - Width(str, 0, (int16)strlen(str), fontId, textWidth, textHeight, true); -} +int16 GfxText32::getTextWidth(const uint index, uint length) const { + int16 width = 0; + + const char *text = _text.c_str() + index; + + GfxFont *font = _font; + + char currentChar = *text++; + while (length > 0 && currentChar != '\0') { + // Control codes are in the format `|<code><value>|` + if (currentChar == '|') { + // NOTE: Original engine code changed the global state of the + // FontMgr here upon encountering any color, alignment, or + // font control code. + // To avoid requiring all callers to manually restore these + // values on every call, we ignore control codes other than + // font change (since alignment and color do not change the + // width of characters), and simply update the font pointer + // on stack instead of the member property font. + currentChar = *text++; + --length; + + if (length > 0 && currentChar == 'f') { + GuiResourceId fontId = 0; + do { + currentChar = *text++; + --length; + + fontId = fontId * 10 + currentChar - '0'; + } while (length > 0 && currentChar >= '0' && currentChar <= '9'); + + if (length > 0) { + font = _cache->getFont(fontId); + } + } -void GfxText32::Width(const char *text, int16 from, int16 len, GuiResourceId fontId, int16 &textWidth, int16 &textHeight, bool restoreFont) { - byte curChar; - textWidth = 0; textHeight = 0; - - GfxFont *font = _cache->getFont(fontId); - - if (font) { - text += from; - while (len--) { - curChar = (*(const byte *)text++); - switch (curChar) { - case 0x0A: - case 0x0D: - textHeight = MAX<int16> (textHeight, font->getHeight()); - break; - case 0x7C: - warning("Code processing isn't implemented in SCI32"); - break; - default: - textHeight = MAX<int16> (textHeight, font->getHeight()); - textWidth += font->getCharWidth(curChar); - break; + // Forward through any more unknown control character data + while (length > 0 && currentChar != '|') { + ++text; + --length; } + } else { + width += font->getCharWidth(currentChar); } + + currentChar = *text++; + --length; } + + return width; +} + +int16 GfxText32::getTextWidth(const Common::String &text, const uint index, const uint length) { + _text = text; + return scaleUpWidth(getTextWidth(index, length)); } -int16 GfxText32::Size(Common::Rect &rect, const char *text, GuiResourceId fontId, int16 maxWidth) { - int16 charCount; - int16 maxTextWidth = 0, textWidth; - int16 totalHeight = 0, textHeight; +Common::Rect GfxText32::getTextSize(const Common::String &text, int16 maxWidth, bool doScaling) { + // NOTE: Like most of the text rendering code, this function was pretty + // weird in the original engine. The initial result rectangle was actually + // a 1x1 rectangle (0, 0, 0, 0), which was then "fixed" after the main + // text size loop finished running by subtracting 1 from the right and + // bottom edges. Like other functions in SCI32, this has been converted + // to use exclusive rects with inclusive rounding. + + Common::Rect result; int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; int16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight; maxWidth = maxWidth * _scaledWidth / scriptWidth; - rect.top = rect.left = 0; - GfxFont *font = _cache->getFont(fontId); + _text = text; - if (maxWidth < 0) { // force output as single line - StringWidth(text, fontId, textWidth, textHeight); - rect.bottom = textHeight; - rect.right = textWidth; - } else { - // rect.right=found widest line with RTextWidth and GetLongest - // rect.bottom=num. lines * GetPointSize - rect.right = (maxWidth ? maxWidth : 192); - const char *curPos = text; - while (*curPos) { - charCount = GetLongest(curPos, rect.right, font); - if (charCount == 0) - break; - Width(curPos, 0, charCount, fontId, textWidth, textHeight, false); - maxTextWidth = MAX(textWidth, maxTextWidth); - totalHeight += textHeight; - curPos += charCount; - while (*curPos == ' ') - curPos++; // skip over breaking spaces + if (maxWidth >= 0) { + if (maxWidth == 0) { + // TODO: This was hardcoded to 192, but guessing + // that it was originally 60% of the scriptWidth + // before the compiler took over. + // Verify this by looking at a game that uses a + // scriptWidth other than 320, like LSL7 + maxWidth = _scaledWidth * (scriptWidth * 0.6) / scriptWidth; + } + + result.right = maxWidth; + + int16 textWidth = 0; + if (_text.size() > 0) { + const char *rawText = _text.c_str(); + const char *sourceText = rawText; + uint charIndex = 0; + uint nextCharIndex = 0; + while (*rawText != '\0') { + uint length = getLongest(&nextCharIndex, result.width()); + textWidth = MAX(textWidth, getTextWidth(charIndex, length)); + charIndex = nextCharIndex; + rawText = sourceText + charIndex; + // TODO: Due to getLongest and getTextWidth not having side + // effects, it is possible that the currently loaded font's + // height is wrong for this line if it was changed inline + result.bottom += _font->getHeight(); + } + } + + if (textWidth < maxWidth) { + result.right = textWidth; } - rect.bottom = totalHeight; - rect.right = maxWidth ? maxWidth : MIN(rect.right, maxTextWidth); + } else { + result.right = getTextWidth(0, 10000); + // NOTE: In the original engine code, the bottom was not decremented + // by 1, which means that the rect was actually a pixel taller than + // the height of the font. This was not the case in the other branch, + // which decremented the bottom by 1 at the end of the loop. + result.bottom = _font->getHeight() + 1; } - rect.right = rect.right * scriptWidth / _scaledWidth; - rect.bottom = rect.bottom * scriptHeight / _scaledHeight; + if (doScaling) { + // NOTE: The original engine code also scaled top/left but these are + // always zero so there is no reason to do that. + result.right = ((result.right - 1) * scriptWidth + _scaledWidth - 1) / _scaledWidth + 1; + result.bottom = ((result.bottom - 1) * scriptHeight + _scaledHeight - 1) / _scaledHeight + 1; + } + + return result; +} + +void GfxText32::erase(const Common::Rect &rect, const bool doScaling) { + Common::Rect targetRect = doScaling ? rect : scaleRect(rect); + + byte *bitmap = _segMan->getHunkPointer(_bitmap); + byte *pixels = bitmap + READ_SCI11ENDIAN_UINT32(bitmap + 28); + + // NOTE: There is an extra optimisation within the SCI code to + // do a single memset if the scaledRect is the same size as + // the bitmap, not implemented here. + Buffer buffer(_width, _height, pixels); + buffer.fillRect(targetRect, _backColor); +} - return rect.right; +int16 GfxText32::getStringWidth(const Common::String &text) { + // TODO: The fact that this double-scales the text makes it + // seem pretty unlikely that this is ever called in real life + error("Called weirdo getStringWidth (FontMgr::StringWidth)"); + return scaleUpWidth(getTextWidth(text, 0, 10000)); } } // End of namespace Sci diff --git a/engines/sci/graphics/text32.h b/engines/sci/graphics/text32.h index 08568e4958..5de54d318f 100644 --- a/engines/sci/graphics/text32.h +++ b/engines/sci/graphics/text32.h @@ -31,7 +31,7 @@ namespace Sci { enum TextAlign { kTextAlignLeft = 0, kTextAlignCenter = 1, - kTextAlignRight = -1 + kTextAlignRight = 2 }; /** @@ -43,6 +43,10 @@ enum TextAlign { */ class GfxText32 { private: + SegManager *_segMan; + GfxCache *_cache; + GfxScreen *_screen; + /** * The resource ID of the default font used by the game. * @@ -54,6 +58,8 @@ private: /** * The width and height of the currently active text * bitmap, in text-system coordinates. + * + * @note These are unsigned in the actual engine. */ int16 _width, _height; @@ -110,29 +116,58 @@ private: */ reg_t _bitmap; + int16 _field_20; + /** * TODO: Document */ - int _field_22; + int16 _field_22; + + int _field_2C, _field_30, _field_34, _field_38; + + int16 _field_3C; /** - * The currently active font resource used to write text - * into the bitmap. - * - * @note SCI engine builds the font table directly - * inside of FontMgr; we use GfxFont instead. + * The position of the text draw cursor. */ - GfxFont *_font; - - // TODO: This is general for all CelObjMem and should be - // put in a single location, like maybe as a static - // method of CelObjMem?! - void buildBitmapHeader(byte *bitmap, const int16 width, const int16 height, const uint8 skipColor, const int16 displaceX, const int16 displaceY, const int16 scaledWidth, const int16 scaledHeight, const uint32 hunkPaletteOffset, const bool useRemap) const; + Common::Point _drawPosition; - void drawFrame(const Common::Rect &rect, const int size, const uint8 color, const bool doScaling); + void drawFrame(const Common::Rect &rect, const int16 size, const uint8 color, const bool doScaling); void drawTextBox(); void erase(const Common::Rect &rect, const bool doScaling); + void drawChar(const uint8 charIndex); + uint16 getCharWidth(const uint8 charIndex, const bool doScaling) const; + void drawText(const uint index, uint length); + + inline int scaleUpWidth(int value) const { + const int scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; + return (value * scriptWidth + _scaledWidth - 1) / _scaledWidth; + } + + /** + * Gets the length of the longest run of text available + * within the currently loaded text, starting from the + * given `charIndex` and running for up to `maxWidth` + * pixels. Returns the number of characters that can be + * written, and mutates the value pointed to by + * `charIndex` to point to the index of the next + * character to render. + */ + uint getLongest(uint *charIndex, const int16 maxWidth); + + /** + * Gets the pixel width of a substring of the currently + * loaded text, without scaling. + */ + int16 getTextWidth(const uint index, uint length) const; + + /** + * Gets the pixel width of a substring of the currently + * loaded text, with scaling. + */ + int16 getTextWidth(const Common::String &text, const uint index, const uint length); + inline Common::Rect scaleRect(const Common::Rect &rect) { Common::Rect scaledRect(rect); int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; @@ -158,32 +193,41 @@ public: */ int16 _scaledHeight; - reg_t createFontBitmap(int16 width, int16 height, const Common::Rect &rect, const Common::String &text, const uint8 foreColor, const uint8 backColor, const uint8 skipColor, const GuiResourceId fontId, TextAlign alignment, const int16 borderColor, bool dimmed, const bool doScaling, reg_t *outBitmapObject); - - reg_t createTitledFontBitmap(CelInfo32 &celInfo, Common::Rect &rect, Common::String &text, int16 foreColor, int16 backColor, int font, int16 skipColor, int16 borderColor, bool dimmed, void *unknown1); + /** + * The currently active font resource used to write text + * into the bitmap. + * + * @note SCI engine builds the font table directly + * inside of FontMgr; we use GfxFont instead. + */ + GfxFont *_font; -#pragma mark - -#pragma mark Old stuff + /** + * Creates a plain font bitmap with a flat color + * background. + */ + reg_t createFontBitmap(int16 width, int16 height, const Common::Rect &rect, const Common::String &text, const uint8 foreColor, const uint8 backColor, const uint8 skipColor, const GuiResourceId fontId, TextAlign alignment, const int16 borderColor, bool dimmed, const bool doScaling, reg_t *outBitmapObject); - reg_t createTextBitmap(reg_t textObject, uint16 maxWidth = 0, uint16 maxHeight = 0, reg_t prevHunk = NULL_REG); - reg_t createScrollTextBitmap(Common::String text, reg_t textObject, uint16 maxWidth = 0, uint16 maxHeight = 0, reg_t prevHunk = NULL_REG); - void drawTextBitmap(int16 x, int16 y, Common::Rect planeRect, reg_t textObject); - void drawScrollTextBitmap(reg_t textObject, reg_t hunkId, uint16 x, uint16 y); - void disposeTextBitmap(reg_t hunkId); - int16 GetLongest(const char *text, int16 maxWidth, GfxFont *font); + /** + * Creates a font bitmap with a view background. + */ + reg_t createFontBitmap(const CelInfo32 &celInfo, const Common::Rect &rect, const Common::String &text, const int16 foreColor, const int16 backColor, const GuiResourceId fontId, const int16 skipColor, const int16 borderColor, const bool dimmed, reg_t *outBitmapObject); - void kernelTextSize(const char *text, int16 font, int16 maxWidth, int16 *textWidth, int16 *textHeight); + /** + * Sets the font to be used for rendering and + * calculation of text dimensions. + */ + void setFont(const GuiResourceId fontId); -private: - reg_t createTextBitmapInternal(Common::String &text, reg_t textObject, uint16 maxWidth, uint16 maxHeight, reg_t hunkId); - void drawTextBitmapInternal(int16 x, int16 y, Common::Rect planeRect, reg_t textObject, reg_t hunkId); - int16 Size(Common::Rect &rect, const char *text, GuiResourceId fontId, int16 maxWidth); - void Width(const char *text, int16 from, int16 len, GuiResourceId orgFontId, int16 &textWidth, int16 &textHeight, bool restoreFont); - void StringWidth(const char *str, GuiResourceId orgFontId, int16 &textWidth, int16 &textHeight); + /** + * Retrieves the width and height of a block of text. + */ + Common::Rect getTextSize(const Common::String &text, const int16 maxWidth, bool doScaling); - SegManager *_segMan; - GfxCache *_cache; - GfxScreen *_screen; + /** + * Retrieves the width of a line of text. + */ + int16 getStringWidth(const Common::String &text); }; } // End of namespace Sci diff --git a/engines/sci/parser/vocabulary.cpp b/engines/sci/parser/vocabulary.cpp index 828a57abeb..a09ba8f3ce 100644 --- a/engines/sci/parser/vocabulary.cpp +++ b/engines/sci/parser/vocabulary.cpp @@ -121,7 +121,7 @@ bool Vocabulary::loadParserWords() { } } - unsigned int seeker; + uint32 seeker; if (resourceType == kVocabularySCI1) seeker = 255 * 2; // vocab.900 starts with 255 16-bit pointers which we don't use else @@ -202,7 +202,7 @@ bool Vocabulary::loadSuffixes() { if (!resource) return false; // No vocabulary found - unsigned int seeker = 1; + uint32 seeker = 1; while ((seeker < resource->size - 1) && (resource->data[seeker + 1] != 0xff)) { suffix_t suffix; @@ -288,7 +288,7 @@ bool Vocabulary::loadAltInputs() { AltInput t; t._input = data; - unsigned int l = strlen(data); + uint32 l = strlen(data); t._inputLength = l; data += l + 1; @@ -325,15 +325,15 @@ bool Vocabulary::checkAltInput(Common::String& text, uint16& cursorPos) { return false; bool ret = false; - unsigned int loopCount = 0; + uint32 loopCount = 0; bool changed; do { changed = false; const char* t = text.c_str(); - unsigned int tlen = text.size(); + uint32 tlen = text.size(); - for (unsigned int p = 0; p < tlen && !changed; ++p) { + for (uint32 p = 0; p < tlen && !changed; ++p) { unsigned char s = t[p]; if (s >= _altInputs.size() || _altInputs[s].empty()) continue; @@ -351,7 +351,7 @@ bool Vocabulary::checkAltInput(Common::String& text, uint16& cursorPos) { cursorPos = p + strlen(i->_replacement); } - for (unsigned int j = 0; j < i->_inputLength; ++j) + for (uint32 j = 0; j < i->_inputLength; ++j) text.deleteChar(p); const char *r = i->_replacement; while (*r) diff --git a/engines/sci/parser/vocabulary.h b/engines/sci/parser/vocabulary.h index f4adee6e55..59558ce18a 100644 --- a/engines/sci/parser/vocabulary.h +++ b/engines/sci/parser/vocabulary.h @@ -156,7 +156,7 @@ typedef Common::Array<synonym_t> SynonymList; struct AltInput { const char *_input; const char *_replacement; - unsigned int _inputLength; + uint32 _inputLength; bool _prefix; }; diff --git a/engines/sci/resource.cpp b/engines/sci/resource.cpp index 2a4cd95b91..6a5af1a6d6 100644 --- a/engines/sci/resource.cpp +++ b/engines/sci/resource.cpp @@ -307,7 +307,7 @@ bool Resource::loadPatch(Common::SeekableReadStream *file) { error("Can't allocate %d bytes needed for loading %s", res->size + res->_headerSize, res->_id.toString().c_str()); } - unsigned int really_read; + uint32 really_read; if (res->_headerSize > 0) { really_read = file->read(res->_header, res->_headerSize); if (really_read != res->_headerSize) @@ -565,12 +565,11 @@ Resource *ResourceManager::testResource(ResourceId id) { } int ResourceManager::addAppropriateSources() { - Common::ArchiveMemberList files; - if (Common::File::exists("resource.map")) { // SCI0-SCI2 file naming scheme ResourceSource *map = addExternalMap("resource.map"); + Common::ArchiveMemberList files; SearchMan.listMatchingMembers(files, "resource.0??"); for (Common::ArchiveMemberList::const_iterator x = files.begin(); x != files.end(); ++x) { @@ -587,20 +586,20 @@ int ResourceManager::addAppropriateSources() { #endif } else if (Common::MacResManager::exists("Data1")) { // Mac SCI1.1+ file naming scheme - SearchMan.listMatchingMembers(files, "Data?*"); + Common::StringArray files; + Common::MacResManager::listFiles(files, "Data?"); - for (Common::ArchiveMemberList::const_iterator x = files.begin(); x != files.end(); ++x) { - Common::String filename = (*x)->getName(); - addSource(new MacResourceForkResourceSource(filename, atoi(filename.c_str() + 4))); + for (Common::StringArray::const_iterator x = files.begin(); x != files.end(); ++x) { + addSource(new MacResourceForkResourceSource(*x, atoi(x->c_str() + 4))); } #ifdef ENABLE_SCI32 // There can also be a "Patches" resource fork with patches - if (Common::File::exists("Patches")) + if (Common::MacResManager::exists("Patches")) addSource(new MacResourceForkResourceSource("Patches", 100)); } else { // SCI2.1-SCI3 file naming scheme - Common::ArchiveMemberList mapFiles; + Common::ArchiveMemberList mapFiles, files; SearchMan.listMatchingMembers(mapFiles, "resmap.0??"); SearchMan.listMatchingMembers(files, "ressci.0??"); diff --git a/engines/sci/resource_audio.cpp b/engines/sci/resource_audio.cpp index 6869e6379e..82099413cb 100644 --- a/engines/sci/resource_audio.cpp +++ b/engines/sci/resource_audio.cpp @@ -139,7 +139,7 @@ bool Resource::loadFromAudioVolumeSCI1(Common::SeekableReadStream *file) { error("Can't allocate %d bytes needed for loading %s", size, _id.toString().c_str()); } - unsigned int really_read = file->read(data, size); + uint32 really_read = file->read(data, size); if (really_read != size) warning("Read %d bytes from %s but expected %d", really_read, _id.toString().c_str(), size); diff --git a/engines/sci/sci.cpp b/engines/sci/sci.cpp index 6d36fabde9..52188db0fb 100644 --- a/engines/sci/sci.cpp +++ b/engines/sci/sci.cpp @@ -316,6 +316,7 @@ Common::Error SciEngine::run() { if (directSaveSlotLoading >= 0) { _gamestate->_delayedRestoreGame = true; _gamestate->_delayedRestoreGameId = directSaveSlotLoading; + _gamestate->_delayedRestoreFromLauncher = true; // Jones only initializes its menus when restarting/restoring, thus set // the gameIsRestarting flag here before initializing. Fixes bug #6536. diff --git a/engines/sci/sound/drivers/amigamac.cpp b/engines/sci/sound/drivers/amigamac.cpp index 5ce49086ca..0f93b19e7c 100644 --- a/engines/sci/sound/drivers/amigamac.cpp +++ b/engines/sci/sound/drivers/amigamac.cpp @@ -497,7 +497,7 @@ MidiDriver_AmigaMac::InstrumentSample *MidiDriver_AmigaMac::readInstrumentSCI0(C } instrument->samples = (int8 *) malloc(size + 1); - if (file.read(instrument->samples, size) < (unsigned int)size) { + if (file.read(instrument->samples, size) < (uint32)size) { warning("Amiga/Mac driver: failed to read instrument samples"); free(instrument->samples); delete instrument; diff --git a/engines/scumm/configure.engine b/engines/scumm/configure.engine index e1de788061..e8962a371e 100644 --- a/engines/scumm/configure.engine +++ b/engines/scumm/configure.engine @@ -2,4 +2,4 @@ # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] add_engine scumm "SCUMM" yes "scumm_7_8 he" "v0-v6 games" add_engine scumm_7_8 "v7 & v8 games" yes -add_engine he "HE71+ games" yes +add_engine he "HE71+ games" yes "" "" "highres" diff --git a/engines/sword1/configure.engine b/engines/sword1/configure.engine index 0578d176a9..1d17903b69 100644 --- a/engines/sword1/configure.engine +++ b/engines/sword1/configure.engine @@ -1,3 +1,3 @@ # This file is included from the main "configure" script # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] -add_engine sword1 "Broken Sword" yes +add_engine sword1 "Broken Sword" yes "" "" "highres" diff --git a/engines/sword2/configure.engine b/engines/sword2/configure.engine index 7153605433..a794e7287c 100644 --- a/engines/sword2/configure.engine +++ b/engines/sword2/configure.engine @@ -1,3 +1,3 @@ # This file is included from the main "configure" script # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] -add_engine sword2 "Broken Sword II" yes +add_engine sword2 "Broken Sword II" yes "" "" "highres" diff --git a/engines/sword25/configure.engine b/engines/sword25/configure.engine index 6a9428c758..f805483f54 100644 --- a/engines/sword25/configure.engine +++ b/engines/sword25/configure.engine @@ -1,3 +1,3 @@ # This file is included from the main "configure" script # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] -add_engine sword25 "Broken Sword 2.5" yes "" "" "png zlib 16bit" +add_engine sword25 "Broken Sword 2.5" yes "" "" "png zlib 16bit highres" diff --git a/engines/sword25/gfx/animationresource.cpp b/engines/sword25/gfx/animationresource.cpp index 431d466658..423a2b86b4 100644 --- a/engines/sword25/gfx/animationresource.cpp +++ b/engines/sword25/gfx/animationresource.cpp @@ -211,8 +211,9 @@ bool AnimationResource::precacheAllFrames() const { error("Could not precache \"%s\".", (*iter).fileName.c_str()); return false; } -#else - Kernel::getInstance()->getResourceManager()->requestResource((*iter).fileName); +#else + Resource *pResource = Kernel::getInstance()->getResourceManager()->requestResource((*iter).fileName); + pResource->release(); //unlock precached resource #endif } diff --git a/engines/sword25/gfx/fontresource.cpp b/engines/sword25/gfx/fontresource.cpp index c4d4c3c52e..1d7aedcb6e 100644 --- a/engines/sword25/gfx/fontresource.cpp +++ b/engines/sword25/gfx/fontresource.cpp @@ -103,8 +103,9 @@ bool FontResource::parserCallback_font(ParserNode *node) { if (!_pKernel->getResourceManager()->precacheResource(_bitmapFileName)) { error("Could not precache \"%s\".", _bitmapFileName.c_str()); } -#else - _pKernel->getResourceManager()->requestResource(_bitmapFileName); +#else + Resource *pResource = _pKernel->getResourceManager()->requestResource(_bitmapFileName); + pResource->release(); //unlock precached resource #endif return true; diff --git a/engines/sword25/gfx/text.cpp b/engines/sword25/gfx/text.cpp index d409c538c0..769c9b1162 100644 --- a/engines/sword25/gfx/text.cpp +++ b/engines/sword25/gfx/text.cpp @@ -77,7 +77,8 @@ bool Text::setFont(const Common::String &font) { return false; } #else - getResourceManager()->requestResource(font); + Resource *pResource = getResourceManager()->requestResource(font); + pResource->release(); //unlock precached resource _font = font; updateFormat(); forceRefresh(); diff --git a/engines/toltecs/configure.engine b/engines/toltecs/configure.engine index be5533efa2..8310a6d6ef 100644 --- a/engines/toltecs/configure.engine +++ b/engines/toltecs/configure.engine @@ -1,3 +1,3 @@ # This file is included from the main "configure" script # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] -add_engine toltecs "3 Skulls of the Toltecs" yes +add_engine toltecs "3 Skulls of the Toltecs" yes "" "" "highres" diff --git a/engines/tony/configure.engine b/engines/tony/configure.engine index f85f45d158..2df4434982 100644 --- a/engines/tony/configure.engine +++ b/engines/tony/configure.engine @@ -1,3 +1,3 @@ # This file is included from the main "configure" script # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] -add_engine tony "Tony Tough and the Night of Roasted Moths" yes "" "" "16bit" +add_engine tony "Tony Tough and the Night of Roasted Moths" yes "" "" "16bit highres" diff --git a/engines/toon/configure.engine b/engines/toon/configure.engine index 00c98f7d8a..689bce1c02 100644 --- a/engines/toon/configure.engine +++ b/engines/toon/configure.engine @@ -1,3 +1,3 @@ # This file is included from the main "configure" script # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] -add_engine toon "Toonstruck" yes +add_engine toon "Toonstruck" yes "" "" "highres" diff --git a/engines/touche/configure.engine b/engines/touche/configure.engine index 777578e623..f35940ef47 100644 --- a/engines/touche/configure.engine +++ b/engines/touche/configure.engine @@ -1,3 +1,3 @@ # This file is included from the main "configure" script # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] -add_engine touche "Touche: The Adventures of the Fifth Musketeer" yes +add_engine touche "Touche: The Adventures of the Fifth Musketeer" yes "" "" "highres" diff --git a/engines/wage/design.cpp b/engines/wage/design.cpp index 2a63436f5a..a3dbd9700b 100644 --- a/engines/wage/design.cpp +++ b/engines/wage/design.cpp @@ -435,7 +435,7 @@ void Design::drawBitmap(Graphics::Surface *surface, Common::SeekableReadStream & } ff.fill(); - for (y = 0; y < h; y++) { + for (y = 0; y < h && y1 + y < surface->h; y++) { byte *src = (byte *)tmp.getBasePtr(0, y); byte *dst = (byte *)surface->getBasePtr(x1, y1 + y); for (x = 0; x < w; x++) { diff --git a/engines/wage/detection.cpp b/engines/wage/detection.cpp index 91e20f3750..35e33cd7bc 100644 --- a/engines/wage/detection.cpp +++ b/engines/wage/detection.cpp @@ -42,6 +42,7 @@ static const PlainGameDescriptor wageGames[] = { {"amot", "A Mess O' Trouble"}, {"cantitoe", "Camp Cantitoe"}, {"scepters", "Enchanted Scepters"}, + {"twisted", "Twisted!"}, {"wage", "WAGE"}, {0, 0} }; diff --git a/engines/wage/detection_tables.h b/engines/wage/detection_tables.h index 8723310294..2df16f13b5 100644 --- a/engines/wage/detection_tables.h +++ b/engines/wage/detection_tables.h @@ -25,30 +25,44 @@ namespace Wage { #define ADGF_DEFAULT (ADGF_DROPLANGUAGE|ADGF_DROPPLATFORM) #define ADGF_GENERIC (ADGF_DROPLANGUAGE|ADGF_DROPPLATFORM|ADGF_USEEXTRAASTITLE) -#define FANGAME(n,f,m,s) { "wage",n,AD_ENTRY1s(f,m,s),Common::EN_ANY,Common::kPlatformMacintosh,ADGF_GENERIC,GUIO0()} +#define FANGAME(n,m,s) { "wage",n,AD_ENTRY1s(n,m,s),Common::EN_ANY,Common::kPlatformMacintosh,ADGF_GENERIC,GUIO0()} +#define FANGAMEN(n,f,m,s) { "wage",n,AD_ENTRY1s(f,m,s),Common::EN_ANY,Common::kPlatformMacintosh,ADGF_GENERIC,GUIO0()} #define BIGGAME(t,v,f,m,s) { t,v,AD_ENTRY1s(f,m,s),Common::EN_ANY,Common::kPlatformMacintosh,ADGF_DEFAULT,GUIO0()} static const ADGameDescription gameDescriptions[] = { - FANGAME("3rd Floor", "3rd Floor", "a107d7a177970b2259e32681bd8b47c9", 285056), + FANGAME("3rd Floor", "a107d7a177970b2259e32681bd8b47c9", 285056), BIGGAME("afm", "v1.8", "Another Fine Mess 1.8", "8e5aa915f3253efb2aab52435647b25e", 1456000), BIGGAME("amot", "v1.8", "A Mess O' Trouble 1.8", "b3ef53afed282671b704e45df829350c", 1895552), - FANGAME("Bug Hunt", "Bug Hunt", "2ebd3515a87941063ad66c3cf93c5e78", 200064), + FANGAME("Bug Hunt", "2ebd3515a87941063ad66c3cf93c5e78", 200064), // Problems with letter rendering - FANGAME("Canal District", "Canal District", "8856bc699a20fc5b7fc67accee12cac7", 658176), + FANGAME("Canal District", "8856bc699a20fc5b7fc67accee12cac7", 658176), BIGGAME("cantitoe", "", "Camp Cantitoe", "098aa5c11c58e1ef274a30a9e01b4755", 621440), - FANGAME("Deep Angst", "Deep Angst", "635f62bbc569e72b03cab9107927d03d", 335232), - FANGAME("Dungeon World II", "DungeonWorld2", "e10c5e3cc17879c298b1551f33571b15", 234880), + // Invalid rect in scene "FINALE" + FANGAME("Castle of Ert", "85f56731635e825e49a672c4fb0490dd", 205312), + FANGAME("Deep Angst", "635f62bbc569e72b03cab9107927d03d", 335232), + FANGAMEN("Dungeon World II", "DungeonWorld2", "e10c5e3cc17879c298b1551f33571b15", 234880), // Polygons with ignored byte 1 - FANGAME("Double Trouble", "Double Trouble", "5e9ee13d09ac54918ed111fa9727ac1c", 557184), - FANGAME("Eidisi I", "Eidisi I", "299d1de4baccf1c66118396519953652", 180480), - FANGAME("Escape from School!", "Escape from School!", "a854be48d4af20126d18a9cad93a969b", 51840), - FANGAME("Magic Rings", "Magic Rings", "6e0d1dd561d3dad8f9a7a20ed1f09b16", 112000), - FANGAME("Midnight Snack", "Midnight Snack", "346982a32fc701f53bb19771d72063d0", 69504), - FANGAME("Queen Quest", "Queen Quest", "730605d312efedb5e3ff108522fcac18", 59776), + FANGAME("Double Trouble", "5e9ee13d09ac54918ed111fa9727ac1c", 557184), + FANGAME("Eidisi I", "299d1de4baccf1c66118396519953652", 180480), + FANGAME("Escape from School!", "a854be48d4af20126d18a9cad93a969b", 51840), + FANGAME("Exploration Zeta!", "b9fbb704017d7ea9613b0160f86527bb", 370944), + FANGAME("Lost Crystal", "4f21ba8ee64f8d655b9eeb1e3ffd50f7", 792064), + FANGAME("Magic Rings", "6e0d1dd561d3dad8f9a7a20ed1f09b16", 112000), + FANGAME("Midnight Snack", "346982a32fc701f53bb19771d72063d0", 69504), + FANGAME("Queen Quest", "730605d312efedb5e3ff108522fcac18", 59776), BIGGAME("scepters", "", "Scepters", "b80bff315897776dda7689cdf829fab4", 360832), - FANGAME("Time Bomb", "Time Bomb", "2df84b636237686b624e736a698a16c4", 66432), - FANGAME("ZikTuria", "ZikTuria", "e793155bed1a70fa2074a3fcd696b751", 54784), - FANGAME("Zoony", "Zoony", "e6cc8a914a4215dafbcce6315dd12cf5", 160256), + // ??? problems with dog bitmap? + FANGAMEN("Space Adventure", "SpaceAdventure", "e5b0d8ad6d235ede2f08583342642dfa", 158720), + FANGAME("Time Bomb", "2df84b636237686b624e736a698a16c4", 66432), + // Invalid rect in scene "Access Tube 1" + FANGAMEN("The Phoenix v1.2", "The Phoenix", "7fa2a2ac740f22572516843922b7c630", 434560), + // Admission for on 3rd screen is messed up + FANGAME("The Tower", "75eba57a12ed181e07f34eaf6aa9d2c4", 568320), + // Doesn't go past first scene + BIGGAME("Twisted!", "", "Twisted! 1.6", "8ea8cc13d26d7975dc43ea7e6c193217", 992896), + FANGAME("Wishing Well", "3ee884d0d1a168d088cf2250d3a83c73", 105600), + FANGAME("ZikTuria", "e793155bed1a70fa2074a3fcd696b751", 54784), + FANGAME("Zoony", "e6cc8a914a4215dafbcce6315dd12cf5", 160256), AD_TABLE_END_MARKER }; diff --git a/engines/wage/entities.cpp b/engines/wage/entities.cpp index d9e4b60591..a2648c49fe 100644 --- a/engines/wage/entities.cpp +++ b/engines/wage/entities.cpp @@ -97,6 +97,8 @@ Scene::Scene() { } Scene::Scene(Common::String name, Common::SeekableReadStream *data) { + debug(9, "Creating scene: %s", name.c_str()); + _name = name; _classType = SCENE; _design = new Design(data); diff --git a/engines/wage/util.cpp b/engines/wage/util.cpp index 1b3dfc9452..f31a83ca04 100644 --- a/engines/wage/util.cpp +++ b/engines/wage/util.cpp @@ -81,6 +81,8 @@ Common::Rect *readRect(Common::SeekableReadStream *in) { y2 = in->readUint16BE() + 4; x2 = in->readUint16BE() + 4; + debug(9, "readRect: %d, %d, %d, %d", x1, y1, x2, y2); + return new Common::Rect(x1, y1, x2, y2); } diff --git a/engines/wintermute/base/base_engine.h b/engines/wintermute/base/base_engine.h index 0f4a6b0775..cbf5d92d00 100644 --- a/engines/wintermute/base/base_engine.h +++ b/engines/wintermute/base/base_engine.h @@ -74,7 +74,7 @@ public: static const Timer *getTimer(); static const Timer *getLiveTimer(); static void LOG(bool res, const char *fmt, ...); - const char *getGameTargetName() const { return _targetName.c_str(); } + Common::String getGameTargetName() const { return _targetName; } Common::String getGameId() const { return _gameId; } Common::Language getLanguage() const { return _language; } WMETargetExecutable getTargetExecutable() const { diff --git a/engines/wintermute/base/base_persistence_manager.cpp b/engines/wintermute/base/base_persistence_manager.cpp index bb5e0c4091..39462f7a15 100644 --- a/engines/wintermute/base/base_persistence_manager.cpp +++ b/engines/wintermute/base/base_persistence_manager.cpp @@ -56,7 +56,7 @@ namespace Wintermute { #define SAVE_MAGIC_3 0x12564154 ////////////////////////////////////////////////////////////////////////// -BasePersistenceManager::BasePersistenceManager(const char *savePrefix, bool deleteSingleton) { +BasePersistenceManager::BasePersistenceManager(const Common::String &savePrefix, bool deleteSingleton) { _saving = false; _offset = 0; _saveStream = nullptr; @@ -91,7 +91,7 @@ BasePersistenceManager::BasePersistenceManager(const char *savePrefix, bool dele _thumbnailDataSize = 0; _thumbnailData = nullptr; - if (savePrefix) { + if (savePrefix != "") { _savePrefix = savePrefix; } else if (_gameRef) { _savePrefix = _gameRef->getGameTargetName(); @@ -215,8 +215,8 @@ bool BasePersistenceManager::getSaveExists(int slot) { } ////////////////////////////////////////////////////////////////////////// -bool BasePersistenceManager::initSave(const char *desc) { - if (!desc) { +bool BasePersistenceManager::initSave(const Common::String &desc) { + if (desc == "") { return STATUS_FAILED; } @@ -297,11 +297,11 @@ bool BasePersistenceManager::initSave(const char *desc) { uint32 dataOffset = _offset + sizeof(uint32) + // data offset - sizeof(uint32) + strlen(desc) + 1 + // description + sizeof(uint32) + strlen(desc.c_str()) + 1 + // description sizeof(uint32); // timestamp putDWORD(dataOffset); - putString(desc); + putString(desc.c_str()); g_system->getTimeAndDate(_savedTimestamp); putTimeDate(_savedTimestamp); diff --git a/engines/wintermute/base/base_persistence_manager.h b/engines/wintermute/base/base_persistence_manager.h index 373d1580de..760b45c907 100644 --- a/engines/wintermute/base/base_persistence_manager.h +++ b/engines/wintermute/base/base_persistence_manager.h @@ -63,7 +63,7 @@ public: uint32 getMaxUsedSlot(); bool getSaveExists(int slot); bool initLoad(const Common::String &filename); - bool initSave(const char *desc); + bool initSave(const Common::String &desc); bool getBytes(byte *buffer, uint32 size); bool putBytes(byte *buffer, uint32 size); uint32 _offset; @@ -86,7 +86,7 @@ public: bool transferCharPtr(const char *name, char **val); bool transferString(const char *name, Common::String *val); bool transferVector2(const char *name, Vector2 *val); - BasePersistenceManager(const char *savePrefix = nullptr, bool deleteSingleton = false); + BasePersistenceManager(const Common::String &savePrefix = "", bool deleteSingleton = false); virtual ~BasePersistenceManager(); bool checkVersion(byte verMajor, byte verMinor, byte verBuild); diff --git a/engines/wintermute/configure.engine b/engines/wintermute/configure.engine index bdaf49de3f..55385776de 100644 --- a/engines/wintermute/configure.engine +++ b/engines/wintermute/configure.engine @@ -1,3 +1,3 @@ # This file is included from the main "configure" script # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] -add_engine wintermute "Wintermute" yes "" "" "jpeg png zlib vorbis 16bit" +add_engine wintermute "Wintermute" yes "" "" "jpeg png zlib vorbis 16bit highres" diff --git a/engines/wintermute/detection_tables.h b/engines/wintermute/detection_tables.h index 25a01766e4..ca30204462 100644 --- a/engines/wintermute/detection_tables.h +++ b/engines/wintermute/detection_tables.h @@ -181,10 +181,13 @@ static const WMEGameDescription gameDescriptions[] = { WME_ENTRY1s("data.dcp", "b3f8b09bb4b05ee3e9d14697525257f9", 59296246), Common::EN_ANY, ADGF_UNSTABLE | ADGF_DEMO, LATEST_VERSION), // Carol Reed 4 - East Side Story WME_WINENTRY("carolreed4", "", - WME_ENTRY1s("data.dcp", "b26377797f060afc2d440d820100c1ce", 529320536), Common::EN_ANY, ADGF_UNSTABLE | ADGF_DEMO, LATEST_VERSION), + WME_ENTRY1s("data.dcp", "b26377797f060afc2d440d820100c1ce", 529320536), Common::EN_ANY, ADGF_UNSTABLE, LATEST_VERSION), // Carol Reed 5 - The Colour of Murder WME_WINENTRY("carolreed5", "", WME_ENTRY1s("data.dcp", "3fcfca44209545d0e26774156427b494", 603660415), Common::EN_ANY, ADGF_UNSTABLE, LATEST_VERSION), + // Carol Reed 5 - The Colour of Murder (1.0 Demo) + WME_WINENTRY("carolreed5", "Demo", + WME_ENTRY1s("data.dcp", "27b3efc018ade5ee8f4adf08b4e3c0dd", 92019500), Common::EN_ANY, ADGF_UNSTABLE | ADGF_DEMO, LATEST_VERSION), // Carol Reed 6 - Black Circle WME_WINENTRY("carolreed6", "", WME_ENTRY1s("data.dcp", "0e4c532beecf23d85012168753f41189", 456258147), Common::EN_ANY, ADGF_UNSTABLE, LATEST_VERSION), diff --git a/engines/zvision/configure.engine b/engines/zvision/configure.engine index 226870c3fd..8681522a35 100644 --- a/engines/zvision/configure.engine +++ b/engines/zvision/configure.engine @@ -1,3 +1,3 @@ # This file is included from the main "configure" script # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] -add_engine zvision "Z-Vision" yes "" "" "freetype2 16bit" +add_engine zvision "Z-Vision" yes "" "" "freetype2 16bit highres" @@ -341,7 +341,7 @@ osxsnap: bundle mkdir ScummVM-snapshot/doc/se cp $(srcdir)/doc/se/LasMig ./ScummVM-snapshot/doc/se/LasMig cp $(srcdir)/doc/se/Snabbstart ./ScummVM-snapshot/doc/se/Snabbstart - /Developer/Tools/SetFile -t ttro -c ttxt ./ScummVM-snapshot/* + $(XCODETOOLSPATH)/SetFile -t ttro -c ttxt ./ScummVM-snapshot/* xattr -w "com.apple.TextEncoding" "utf-8;134217984" ./ScummVM-snapshot/doc/cz/* xattr -w "com.apple.TextEncoding" "utf-8;134217984" ./ScummVM-snapshot/doc/da/* xattr -w "com.apple.TextEncoding" "utf-8;134217984" ./ScummVM-snapshot/doc/de/* @@ -350,11 +350,11 @@ osxsnap: bundle xattr -w "com.apple.TextEncoding" "utf-8;134217984" ./ScummVM-snapshot/doc/it/* xattr -w "com.apple.TextEncoding" "utf-8;134217984" ./ScummVM-snapshot/doc/no-nb/* xattr -w "com.apple.TextEncoding" "utf-8;134217984" ./ScummVM-snapshot/doc/se/* - /Developer/Tools/CpMac -r $(bundle_name) ./ScummVM-snapshot/ + $(XCODETOOLSPATH)/CpMac -r $(bundle_name) ./ScummVM-snapshot/ cp $(srcdir)/dists/macosx/DS_Store ./ScummVM-snapshot/.DS_Store cp $(srcdir)/dists/macosx/background.jpg ./ScummVM-snapshot/background.jpg - /Developer/Tools/SetFile -a V ./ScummVM-snapshot/.DS_Store - /Developer/Tools/SetFile -a V ./ScummVM-snapshot/background.jpg + $(XCODETOOLSPATH)/SetFile -a V ./ScummVM-snapshot/.DS_Store + $(XCODETOOLSPATH)/SetFile -a V ./ScummVM-snapshot/background.jpg hdiutil create -ov -format UDZO -imagekey zlib-level=9 -fs HFS+ \ -srcfolder ScummVM-snapshot \ -volname "ScummVM" \ |