aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Kiewitz2016-02-23 20:17:18 +0100
committerMartin Kiewitz2016-02-23 20:17:18 +0100
commit82165bb6f63290635cedb41b3a8dff1a5d1f5745 (patch)
treeedaf769ebe342f18ca19a31a89d93c20f9c1d6ee
parent64e21078dae76e900ed2c8e338e27fe57b4cae4d (diff)
downloadscummvm-rg350-82165bb6f63290635cedb41b3a8dff1a5d1f5745.tar.gz
scummvm-rg350-82165bb6f63290635cedb41b3a8dff1a5d1f5745.tar.bz2
scummvm-rg350-82165bb6f63290635cedb41b3a8dff1a5d1f5745.zip
SCI: Improve kAnimate fastcast detection, Remove EQ1 hack
- Add "kAnimate fast cast state" to "version" debug command - Make it possible for script patcher signatures to get fully used outside of the regular script patcher - Remove previous fastcast detections and replace them with a signature heuristic - Remove object name checking, when fastcast global is set - Heuristic detects "fast cast" support incorrectly for multilingual KQ5, but it seems the game never sets the global, so it won't matter. KQ5 CD (also SCI1 late) has fastcast support. - Remove hack in GfxView::draw - Add lots of comments to ScriptPatcher class This fixes EcoQuest 1 Floppy showing the anemone on top of the message box (see bug #5170)
-rw-r--r--engines/sci/console.cpp3
-rw-r--r--engines/sci/engine/script_patches.cpp260
-rw-r--r--engines/sci/engine/script_patches.h21
-rw-r--r--engines/sci/graphics/animate.cpp92
-rw-r--r--engines/sci/graphics/animate.h10
-rw-r--r--engines/sci/graphics/view.cpp13
-rw-r--r--engines/sci/sci.cpp2
-rw-r--r--engines/sci/sci.h2
8 files changed, 250 insertions, 153 deletions
diff --git a/engines/sci/console.cpp b/engines/sci/console.cpp
index a395552c81..cbc6dfaf74 100644
--- a/engines/sci/console.cpp
+++ b/engines/sci/console.cpp
@@ -491,6 +491,9 @@ bool Console::cmdGetVersion(int argc, const char **argv) {
debugPrintf("SCI2.1 kernel table: %s\n", (_engine->_features->detectSci21KernelType() == SCI_VERSION_2) ? "modified SCI2 (old)" : "SCI2.1 (new)");
#endif
debugPrintf("View type: %s\n", viewTypeDesc[g_sci->getResMan()->getViewType()]);
+ if (getSciVersion() <= SCI_VERSION_1_1) {
+ debugPrintf("kAnimate fastCast enabled: %s\n", g_sci->_gfxAnimate->isFastCastEnabled() ? "yes" : "no");
+ }
debugPrintf("Uses palette merging: %s\n", g_sci->_gfxPalette16->isMerging() ? "yes" : "no");
debugPrintf("Uses 16 bit color matching: %s\n", g_sci->_gfxPalette16->isUsing16bitColorMatch() ? "yes" : "no");
debugPrintf("Resource volume version: %s\n", g_sci->getResMan()->getVolVersionDesc());
diff --git a/engines/sci/engine/script_patches.cpp b/engines/sci/engine/script_patches.cpp
index fe6475224e..0ddc8d3c4f 100644
--- a/engines/sci/engine/script_patches.cpp
+++ b/engines/sci/engine/script_patches.cpp
@@ -3615,20 +3615,20 @@ bool ScriptPatcher::verifySignature(uint32 byteOffset, const uint16 *signatureDa
}
// will return -1 if no match was found, otherwise an offset to the start of the signature match
-int32 ScriptPatcher::findSignature(const SciScriptPatcherEntry *patchEntry, SciScriptPatcherRuntimeEntry *runtimeEntry, const byte *scriptData, const uint32 scriptSize) {
+int32 ScriptPatcher::findSignature(uint32 magicDWord, int magicOffset, const uint16 *signatureData, const char *patchDescription, const byte *scriptData, const uint32 scriptSize) {
if (scriptSize < 4) // we need to find a DWORD, so less than 4 bytes is not okay
return -1;
- const uint32 magicDWord = runtimeEntry->magicDWord; // is platform-specific BE/LE form, so that the later match will work
+ // magicDWord is in platform-specific BE/LE form, so that the later match will work, this was done for performance
const uint32 searchLimit = scriptSize - 3;
uint32 DWordOffset = 0;
// first search for the magic DWORD
while (DWordOffset < searchLimit) {
if (magicDWord == READ_UINT32(scriptData + DWordOffset)) {
// magic DWORD found, check if actual signature matches
- uint32 offset = DWordOffset + runtimeEntry->magicOffset;
+ uint32 offset = DWordOffset + magicOffset;
- if (verifySignature(offset, patchEntry->signatureData, patchEntry->description, scriptData, scriptSize))
+ if (verifySignature(offset, signatureData, patchDescription, scriptData, scriptSize))
return offset;
}
DWordOffset++;
@@ -3637,22 +3637,146 @@ int32 ScriptPatcher::findSignature(const SciScriptPatcherEntry *patchEntry, SciS
return -1;
}
-// This method calculates the magic DWORD for each entry in the signature table
-// and it also initializes the selector table for selectors used in the signatures/patches of the current game
-void ScriptPatcher::initSignature(const SciScriptPatcherEntry *patchTable) {
- const SciScriptPatcherEntry *curEntry = patchTable;
- SciScriptPatcherRuntimeEntry *curRuntimeEntry;
+int32 ScriptPatcher::findSignature(const SciScriptPatcherEntry *patchEntry, const SciScriptPatcherRuntimeEntry *runtimeEntry, const byte *scriptData, const uint32 scriptSize) {
+ return findSignature(runtimeEntry->magicDWord, runtimeEntry->magicOffset, patchEntry->signatureData, patchEntry->description, scriptData, scriptSize);
+}
+
+// Attention: Magic DWord is returns using platform specific byte order. This is done on purpose for performance.
+void ScriptPatcher::calculateMagicDWordAndVerify(const char *signatureDescription, const uint16 *signatureData, bool magicDWordIncluded, uint32 &calculatedMagicDWord, int &calculatedMagicDWordOffset) {
Selector curSelector = -1;
- int step;
int magicOffset;
byte magicDWord[4];
int magicDWordLeft = 0;
- const uint16 *curData;
uint16 curWord;
uint16 curCommand;
uint32 curValue;
byte byte1 = 0;
byte byte2 = 0;
+
+ memset(magicDWord, 0, sizeof(magicDWord));
+
+ curWord = *signatureData;
+ magicOffset = 0;
+ while (curWord != SIG_END) {
+ curCommand = curWord & SIG_COMMANDMASK;
+ curValue = curWord & SIG_VALUEMASK;
+ switch (curCommand) {
+ case SIG_MAGICDWORD: {
+ if (magicDWordIncluded) {
+ if ((calculatedMagicDWord) || (magicDWordLeft))
+ error("Script-Patcher: Magic-DWORD specified multiple times in signature\nFaulty patch: '%s'", signatureDescription);
+ magicDWordLeft = 4;
+ calculatedMagicDWordOffset = magicOffset;
+ } else {
+ error("Script-Patcher: Magic-DWORD sequence found in patch data\nFaulty patch: '%s'", signatureDescription);
+ }
+ break;
+ }
+ case SIG_CODE_ADDTOOFFSET: {
+ magicOffset -= curValue;
+ if (magicDWordLeft)
+ error("Script-Patcher: Magic-DWORD contains AddToOffset command\nFaulty patch: '%s'", signatureDescription);
+ break;
+ }
+ case SIG_CODE_UINT16:
+ case SIG_CODE_SELECTOR16: {
+ // UINT16 or 1
+ switch (curCommand) {
+ case SIG_CODE_UINT16: {
+ signatureData++; curWord = *signatureData;
+ if (curWord & SIG_COMMANDMASK)
+ error("Script-Patcher: signature entry inconsistent\nFaulty patch: '%s'", signatureDescription);
+ if (!_isMacSci11) {
+ byte1 = curValue;
+ byte2 = curWord & SIG_BYTEMASK;
+ } else {
+ byte1 = curWord & SIG_BYTEMASK;
+ byte2 = curValue;
+ }
+ break;
+ }
+ case SIG_CODE_SELECTOR16: {
+ curSelector = _selectorIdTable[curValue];
+ if (curSelector == -1) {
+ curSelector = g_sci->getKernel()->findSelector(selectorNameTable[curValue]);
+ _selectorIdTable[curValue] = curSelector;
+ }
+ if (!_isMacSci11) {
+ byte1 = curSelector & 0x00FF;
+ byte2 = curSelector >> 8;
+ } else {
+ byte1 = curSelector >> 8;
+ byte2 = curSelector & 0x00FF;
+ }
+ break;
+ }
+ }
+ magicOffset -= 2;
+ if (magicDWordLeft) {
+ // Remember current word for Magic DWORD
+ magicDWord[4 - magicDWordLeft] = byte1;
+ magicDWordLeft--;
+ if (magicDWordLeft) {
+ magicDWord[4 - magicDWordLeft] = byte2;
+ magicDWordLeft--;
+ }
+ if (!magicDWordLeft) {
+ // Magic DWORD is now known, convert to platform specific byte order
+ calculatedMagicDWord = READ_LE_UINT32(magicDWord);
+ }
+ }
+ break;
+ }
+ case SIG_CODE_BYTE:
+ case SIG_CODE_SELECTOR8: {
+ if (curCommand == SIG_CODE_SELECTOR8) {
+ curSelector = _selectorIdTable[curValue];
+ if (curSelector == -1) {
+ curSelector = g_sci->getKernel()->findSelector(selectorNameTable[curValue]);
+ _selectorIdTable[curValue] = curSelector;
+ if (curSelector != -1) {
+ if (curSelector & 0xFF00)
+ error("Script-Patcher: 8 bit selector required, game uses 16 bit selector\nFaulty patch: '%s'", signatureDescription);
+ }
+ }
+ curValue = curSelector;
+ }
+ magicOffset--;
+ if (magicDWordLeft) {
+ // Remember current byte for Magic DWORD
+ magicDWord[4 - magicDWordLeft] = (byte)curValue;
+ magicDWordLeft--;
+ if (!magicDWordLeft) {
+ calculatedMagicDWord = READ_LE_UINT32(magicDWord);
+ }
+ }
+ break;
+ }
+ case PATCH_CODE_GETORIGINALBYTEADJUST: {
+ signatureData++; // skip over extra uint16
+ break;
+ }
+ default:
+ break;
+ }
+ signatureData++;
+ curWord = *signatureData;
+ }
+
+ if (magicDWordLeft)
+ error("Script-Patcher: Magic-DWORD beyond End-Of-Signature\nFaulty patch: '%s'", signatureDescription);
+ if (magicDWordIncluded) {
+ if (!calculatedMagicDWord) {
+ error("Script-Patcher: Magic-DWORD not specified in signature\nFaulty patch: '%s'", signatureDescription);
+ }
+ }
+}
+
+// This method calculates the magic DWORD for each entry in the signature table
+// and it also initializes the selector table for selectors used in the signatures/patches of the current game
+void ScriptPatcher::initSignature(const SciScriptPatcherEntry *patchTable) {
+ const SciScriptPatcherEntry *curEntry = patchTable;
+ SciScriptPatcherRuntimeEntry *curRuntimeEntry;
int patchEntryCount = 0;
// Count entries and allocate runtime data
@@ -3666,120 +3790,14 @@ void ScriptPatcher::initSignature(const SciScriptPatcherEntry *patchTable) {
curRuntimeEntry = _runtimeTable;
while (curEntry->signatureData) {
// process signature
- memset(magicDWord, 0, sizeof(magicDWord));
-
curRuntimeEntry->active = curEntry->defaultActive;
curRuntimeEntry->magicDWord = 0;
curRuntimeEntry->magicOffset = 0;
- for (step = 0; step < 2; step++) {
- switch (step) {
- case 0: curData = curEntry->signatureData; break;
- case 1: curData = curEntry->patchData; break;
- }
-
- curWord = *curData;
- magicOffset = 0;
- while (curWord != SIG_END) {
- curCommand = curWord & SIG_COMMANDMASK;
- curValue = curWord & SIG_VALUEMASK;
- switch (curCommand) {
- case SIG_MAGICDWORD: {
- if (step == 0) {
- if ((curRuntimeEntry->magicDWord) || (magicDWordLeft))
- error("Script-Patcher: Magic-DWORD specified multiple times in signature\nFaulty patch: '%s'", curEntry->description);
- magicDWordLeft = 4;
- curRuntimeEntry->magicOffset = magicOffset;
- }
- break;
- }
- case SIG_CODE_ADDTOOFFSET: {
- magicOffset -= curValue;
- if (magicDWordLeft)
- error("Script-Patcher: Magic-DWORD contains AddToOffset command\nFaulty patch: '%s'", curEntry->description);
- break;
- }
- case SIG_CODE_UINT16:
- case SIG_CODE_SELECTOR16: {
- // UINT16 or 1
- switch (curCommand) {
- case SIG_CODE_UINT16: {
- curData++; curWord = *curData;
- if (curWord & SIG_COMMANDMASK)
- error("Script-Patcher: signature entry inconsistent\nFaulty patch: '%s'", curEntry->description);
- if (!_isMacSci11) {
- byte1 = curValue;
- byte2 = curWord & SIG_BYTEMASK;
- } else {
- byte1 = curWord & SIG_BYTEMASK;
- byte2 = curValue;
- }
- break;
- }
- case SIG_CODE_SELECTOR16: {
- curSelector = _selectorIdTable[curValue];
- if (curSelector == -1) {
- curSelector = g_sci->getKernel()->findSelector(selectorNameTable[curValue]);
- _selectorIdTable[curValue] = curSelector;
- }
- if (!_isMacSci11) {
- byte1 = curSelector & 0x00FF;
- byte2 = curSelector >> 8;
- } else {
- byte1 = curSelector >> 8;
- byte2 = curSelector & 0x00FF;
- }
- break;
- }
- }
- magicOffset -= 2;
- if (magicDWordLeft) {
- // Remember current word for Magic DWORD
- magicDWord[4 - magicDWordLeft] = byte1;
- magicDWordLeft--;
- if (magicDWordLeft) {
- magicDWord[4 - magicDWordLeft] = byte2;
- magicDWordLeft--;
- }
- if (!magicDWordLeft) {
- curRuntimeEntry->magicDWord = READ_LE_UINT32(magicDWord);
- }
- }
- break;
- }
- case SIG_CODE_BYTE:
- case SIG_CODE_SELECTOR8: {
- if (curCommand == SIG_CODE_SELECTOR8) {
- curSelector = _selectorIdTable[curValue];
- if (curSelector == -1) {
- curSelector = g_sci->getKernel()->findSelector(selectorNameTable[curValue]);
- _selectorIdTable[curValue] = curSelector;
- if (curSelector != -1) {
- if (curSelector & 0xFF00)
- error("Script-Patcher: 8 bit selector required, game uses 16 bit selector\nFaulty patch: '%s'", curEntry->description);
- }
- }
- curValue = curSelector;
- }
- magicOffset--;
- if (magicDWordLeft) {
- // Remember current byte for Magic DWORD
- magicDWord[4 - magicDWordLeft] = (byte)curValue;
- magicDWordLeft--;
- if (!magicDWordLeft) {
- curRuntimeEntry->magicDWord = READ_LE_UINT32(magicDWord);
- }
- }
- }
- }
- curData++;
- curWord = *curData;
- }
- }
- if (magicDWordLeft)
- error("Script-Patcher: Magic-DWORD beyond End-Of-Signature\nFaulty patch: '%s'", curEntry->description);
- if (!curRuntimeEntry->magicDWord)
- error("Script-Patcher: Magic-DWORD not specified in signature\nFaulty patch: '%s'", curEntry->description);
+ // We verify the signature data and remember the calculated magic DWord from the signature data
+ calculateMagicDWordAndVerify(curEntry->description, curEntry->signatureData, true, curRuntimeEntry->magicDWord, curRuntimeEntry->magicOffset);
+ // We verify the patch data
+ calculateMagicDWordAndVerify(curEntry->description, curEntry->patchData, false, curRuntimeEntry->magicDWord, curRuntimeEntry->magicOffset);
curEntry++; curRuntimeEntry++;
}
diff --git a/engines/sci/engine/script_patches.h b/engines/sci/engine/script_patches.h
index d15fce321b..645e0946b3 100644
--- a/engines/sci/engine/script_patches.h
+++ b/engines/sci/engine/script_patches.h
@@ -90,13 +90,32 @@ public:
ScriptPatcher();
~ScriptPatcher();
+ // Calculates the magic DWord for fast search and verifies signature/patch data
+ // Returns the magic DWord in platform-specific byte-order. This is done on purpose for performance.
+ void calculateMagicDWordAndVerify(const char *signatureDescription, const uint16 *signatureData, bool magicDWordIncluded, uint32 &calculatedMagicDWord, int &calculatedMagicDWordOffset);
+
+ // Called when a script is loaded to check for signature matches and apply patches in such cases
void processScript(uint16 scriptNr, byte *scriptData, const uint32 scriptSize);
+
+ // Verifies, if a given signature matches the given script data (pointed to by additional byte offset)
bool verifySignature(uint32 byteOffset, const uint16 *signatureData, const char *signatureDescription, const byte *scriptData, const uint32 scriptSize);
+ // searches for a given signature inside script data
+ // returns -1 in case it was not found or an offset to the matching data
+ int32 findSignature(uint32 magicDWord, int magicOffset, const uint16 *signatureData, const char *patchDescription, const byte *scriptData, const uint32 scriptSize);
+
private:
+ // Initializes a patch table and creates run time information for it (for enabling/disabling), also calculates magic DWORD)
void initSignature(const SciScriptPatcherEntry *patchTable);
+
+ // Enables a patch inside the patch table (used for optional patches like CD+Text support for KQ6 & LB2)
void enablePatch(const SciScriptPatcherEntry *patchTable, const char *searchDescription);
- int32 findSignature(const SciScriptPatcherEntry *patchEntry, SciScriptPatcherRuntimeEntry *runtimeEntry, const byte *scriptData, const uint32 scriptSize);
+
+ // Searches for a given signature entry inside script data
+ // returns -1 in case it was not found or an offset to the matching data
+ int32 findSignature(const SciScriptPatcherEntry *patchEntry, const SciScriptPatcherRuntimeEntry *runtimeEntry, const byte *scriptData, const uint32 scriptSize);
+
+ // Applies a patch to a given script + offset (overwrites parts)
void applyPatch(const SciScriptPatcherEntry *patchEntry, byte *scriptData, const uint32 scriptSize, int32 signatureOffset);
Selector *_selectorIdTable;
diff --git a/engines/sci/graphics/animate.cpp b/engines/sci/graphics/animate.cpp
index fdd81a8caa..98278397b7 100644
--- a/engines/sci/graphics/animate.cpp
+++ b/engines/sci/graphics/animate.cpp
@@ -28,6 +28,7 @@
#include "sci/sci.h"
#include "sci/event.h"
#include "sci/engine/kernel.h"
+#include "sci/engine/script_patches.h"
#include "sci/engine/state.h"
#include "sci/engine/selector.h"
#include "sci/engine/vm.h"
@@ -44,8 +45,8 @@
namespace Sci {
-GfxAnimate::GfxAnimate(EngineState *state, GfxCache *cache, GfxPorts *ports, GfxPaint16 *paint16, GfxScreen *screen, GfxPalette *palette, GfxCursor *cursor, GfxTransitions *transitions)
- : _s(state), _cache(cache), _ports(ports), _paint16(paint16), _screen(screen), _palette(palette), _cursor(cursor), _transitions(transitions) {
+GfxAnimate::GfxAnimate(EngineState *state, ScriptPatcher *scriptPatcher, GfxCache *cache, GfxPorts *ports, GfxPaint16 *paint16, GfxScreen *screen, GfxPalette *palette, GfxCursor *cursor, GfxTransitions *transitions)
+ : _s(state), _scriptPatcher(scriptPatcher), _cache(cache), _ports(ports), _paint16(paint16), _screen(screen), _palette(palette), _cursor(cursor), _transitions(transitions) {
init();
}
@@ -55,16 +56,77 @@ GfxAnimate::~GfxAnimate() {
void GfxAnimate::init() {
_lastCastData.clear();
- _ignoreFastCast = false;
- // fastCast object is not found in any SCI games prior SCI1
- if (getSciVersion() <= SCI_VERSION_01)
- _ignoreFastCast = true;
- // Also if fastCast object exists at gamestartup, we can assume that the interpreter doesn't do kAnimate aborts
- // (found in Larry 1)
- if (getSciVersion() > SCI_VERSION_0_EARLY) {
- if (!_s->_segMan->findObjectByName("fastCast").isNull())
- _ignoreFastCast = true;
+ _fastCastEnabled = false;
+ if (getSciVersion() == SCI_VERSION_1_1) {
+ // Seems to have been available for all SCI1.1 games
+ _fastCastEnabled = true;
+ } else if (getSciVersion() >= SCI_VERSION_1_EARLY) {
+ // fastCast only exists for some games between SCI1 early and SCI1 late
+ // Try to detect it by code signature
+ // It's extremely important, that we only enable it for games that actually need it
+ if (detectFastCast()) {
+ _fastCastEnabled = true;
+ }
+ }
+}
+
+// Signature for fastCast detection
+static const uint16 fastCastSignature[] = {
+ SIG_MAGICDWORD,
+ 0x35, 0x00, // ldi 00
+ 0xa1, 84, // sag global[84d]
+ SIG_END
+};
+
+// Fast cast in games:
+
+// SCI1 Early:
+// KQ5 - no fastcast, LSL1 (demo) - no fastcast, Mixed Up Fairy Tales - *has fastcast*, XMas Card 1990 - no fastcast,
+// SQ4Floppy - no fastcast, Mixed Up Mother Goose - no fastcast
+//
+// SCI1 Middle:
+// LSL5 demo - no fastfast, Conquest of the Longbow demo - no fastcast, LSL1 - no fastcast,
+// Astro Chicken II - no fastcast
+//
+// SCI1 Late:
+// Castle of Dr. Brain demo - has fastcast, Castle of Dr. Brain - has fastcast,
+// Conquests of the Longbow - has fastcast, Space Quest 1 EGA - has fastcast,
+// King's Quest 5 multilingual - *NO* fastcast, Police Quest 3 demo - *NO* fastcast,
+// LSL5 multilingual - has fastcast, Police Quest 3 - has fastcast,
+// EcoQuest 1 - has fastcast, Mixed Up Fairy Tales demo - has fastcast,
+// Space Quest 4 multilingual - *NO* fastcast
+//
+// SCI1.1
+// Quest for Glory 3 demo - has fastcast, Police Quest 1 - hast fastcast, Quest for Glory 1 - has fastcast
+// Laura Bow 2 Floppy - has fastcast, Mixed Up Mother Goose - has fastcast, Quest for Glory 3 - has fastcast
+// Island of Dr. Brain - has fastcast, King's Quest 6 - has fastcast, Space Quest 5 - has fastcast
+// Hoyle 4 - has fastcast, Laura Bow 2 CD - has fastcast, Freddy Pharkas CD - has fastcast
+bool GfxAnimate::detectFastCast() {
+ SegManager *segMan = _s->_segMan;
+ const reg_t gameVMObject = g_sci->getGameObject();
+ reg_t gameSuperVMObject = segMan->getObject(gameVMObject)->getSuperClassSelector();
+ uint32 magicDWord = 0; // platform-specific BE/LE for performance
+ int magicDWordOffset = 0;
+
+ if (gameSuperVMObject.isNull()) {
+ gameSuperVMObject = gameVMObject; // Just in case. According to sci.cpp this may happen in KQ5CD, when loading saved games before r54510
+ }
+
+ Script *objectScript = segMan->getScript(gameSuperVMObject.getSegment());
+ byte *scriptData = const_cast<byte *>(objectScript->getBuf(0));
+ uint32 scriptSize = objectScript->getBufSize();
+
+ _scriptPatcher->calculateMagicDWordAndVerify("fast cast detection", fastCastSignature, true, magicDWord, magicDWordOffset);
+
+ // Signature is found for multilingual King's Quest 5 too, but it looks as if the fast cast global is never set
+ // within that game. Which means even though we detect it as having the capability, it's never actually used.
+ // The original multilingual KQ5 interpreter did have this feature disabled.
+ // Sierra probably used latest system scripts and that's why we detect it.
+ if (_scriptPatcher->findSignature(magicDWord, magicDWordOffset, fastCastSignature, "fast cast detection", scriptData, scriptSize) >= 0) {
+ // Signature found, game seems to use fast cast for kAnimate
+ return true;
}
+ return false;
}
void GfxAnimate::disposeLastCast() {
@@ -80,12 +142,14 @@ bool GfxAnimate::invoke(List *list, int argc, reg_t *argv) {
while (curNode) {
curObject = curNode->value;
- if (!_ignoreFastCast) {
+ if (_fastCastEnabled) {
// Check if the game has a fastCast object set
// if we don't abort kAnimate processing, at least in kq5 there will be animation cels drawn into speech boxes.
if (!_s->variables[VAR_GLOBAL][84].isNull()) {
- if (!strcmp(_s->_segMan->getObjectName(_s->variables[VAR_GLOBAL][84]), "fastCast"))
- return false;
+ // This normally points to an object called "fastCast",
+ // but for example in Eco Quest 1 it may also point to an object called "EventHandler" (see bug #5170)
+ // Original SCI only checked, if this global was not 0.
+ return false;
}
}
diff --git a/engines/sci/graphics/animate.h b/engines/sci/graphics/animate.h
index 6c1822c903..ac7078093c 100644
--- a/engines/sci/graphics/animate.h
+++ b/engines/sci/graphics/animate.h
@@ -87,9 +87,13 @@ class GfxView;
*/
class GfxAnimate {
public:
- GfxAnimate(EngineState *state, GfxCache *cache, GfxPorts *ports, GfxPaint16 *paint16, GfxScreen *screen, GfxPalette *palette, GfxCursor *cursor, GfxTransitions *transitions);
+ GfxAnimate(EngineState *state, ScriptPatcher *scriptPatcher, GfxCache *cache, GfxPorts *ports, GfxPaint16 *paint16, GfxScreen *screen, GfxPalette *palette, GfxCursor *cursor, GfxTransitions *transitions);
virtual ~GfxAnimate();
+ bool isFastCastEnabled() {
+ return _fastCastEnabled;
+ }
+
void disposeLastCast();
bool invoke(List *list, int argc, reg_t *argv);
void makeSortedList(List *list);
@@ -110,6 +114,7 @@ public:
private:
void init();
+ bool detectFastCast();
void addToPicSetPicNotValid();
void animateShowPic();
@@ -119,6 +124,7 @@ private:
void setNsRect(GfxView *view, AnimateList::iterator it);
EngineState *_s;
+ ScriptPatcher *_scriptPatcher;
GfxCache *_cache;
GfxPorts *_ports;
GfxPaint16 *_paint16;
@@ -130,7 +136,7 @@ private:
AnimateList _list;
AnimateArray _lastCastData;
- bool _ignoreFastCast;
+ bool _fastCastEnabled;
};
} // End of namespace Sci
diff --git a/engines/sci/graphics/view.cpp b/engines/sci/graphics/view.cpp
index 2ee18b5c9a..4116eda876 100644
--- a/engines/sci/graphics/view.cpp
+++ b/engines/sci/graphics/view.cpp
@@ -833,19 +833,6 @@ void GfxView::draw(const Common::Rect &rect, const Common::Rect &clipRect, const
bitmap += (clipRect.top - rect.top) * celWidth + (clipRect.left - rect.left);
- // WORKAROUND: EcoQuest French and German draw the fish and anemone sprites
- // with priority 15 in scene 440. Afterwards, a dialog is shown on top of
- // these sprites with priority 15 as well. This is undefined behavior
- // actually, as the sprites and dialog share the same priority, so in our
- // implementation the sprites get drawn incorrectly on top of the dialog.
- // Perhaps this worked by mistake in SSCI because of subtle differences in
- // how sprites are drawn. We compensate for this by resetting the priority
- // of all sprites that have a priority of 15 in scene 440 to priority 14,
- // so that the speech bubble can be drawn correctly on top of them. Fixes
- // bug #3040625.
- if (g_sci->getGameId() == GID_ECOQUEST && g_sci->getEngineState()->currentRoomNumber() == 440 && priority == 15)
- priority = 14;
-
if (!_EGAmapping) {
for (y = 0; y < height; y++, bitmap += celWidth) {
for (x = 0; x < width; x++) {
diff --git a/engines/sci/sci.cpp b/engines/sci/sci.cpp
index cce0235fd5..577abb2a8b 100644
--- a/engines/sci/sci.cpp
+++ b/engines/sci/sci.cpp
@@ -717,7 +717,7 @@ void SciEngine::initGraphics() {
_gfxTransitions = new GfxTransitions(_gfxScreen, _gfxPalette16);
_gfxPaint16 = new GfxPaint16(_resMan, _gamestate->_segMan, _gfxCache, _gfxPorts, _gfxCoordAdjuster, _gfxScreen, _gfxPalette16, _gfxTransitions, _audio);
_gfxPaint = _gfxPaint16;
- _gfxAnimate = new GfxAnimate(_gamestate, _gfxCache, _gfxPorts, _gfxPaint16, _gfxScreen, _gfxPalette16, _gfxCursor, _gfxTransitions);
+ _gfxAnimate = new GfxAnimate(_gamestate, _scriptPatcher, _gfxCache, _gfxPorts, _gfxPaint16, _gfxScreen, _gfxPalette16, _gfxCursor, _gfxTransitions);
_gfxText16 = new GfxText16(_gfxCache, _gfxPorts, _gfxPaint16, _gfxScreen);
_gfxControls16 = new GfxControls16(_gamestate->_segMan, _gfxPorts, _gfxPaint16, _gfxText16, _gfxScreen);
_gfxMenu = new GfxMenu(_eventMan, _gamestate->_segMan, _gfxPorts, _gfxPaint16, _gfxText16, _gfxScreen, _gfxCursor);
diff --git a/engines/sci/sci.h b/engines/sci/sci.h
index 5c86d92355..3945e68e33 100644
--- a/engines/sci/sci.h
+++ b/engines/sci/sci.h
@@ -282,7 +282,7 @@ public:
inline EngineState *getEngineState() const { return _gamestate; }
inline Vocabulary *getVocabulary() const { return _vocabulary; }
inline EventManager *getEventManager() const { return _eventMan; }
- inline reg_t getGameObject() const { return _gameObjectAddress; }
+ inline reg_t getGameObject() const { return _gameObjectAddress; } // Gets the game object VM address
Common::RandomSource &getRNG() { return _rng; }