Skip to content

Commit

Permalink
SCI: Improve kAnimate fastcast detection, Remove EQ1 hack
Browse files Browse the repository at this point in the history
- 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)
  • Loading branch information
Martin Kiewitz committed Feb 23, 2016
1 parent 64e2107 commit 82165bb
Show file tree
Hide file tree
Showing 8 changed files with 250 additions and 153 deletions.
3 changes: 3 additions & 0 deletions engines/sci/console.cpp
Expand Up @@ -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());
Expand Down
260 changes: 139 additions & 121 deletions engines/sci/engine/script_patches.cpp
Expand Up @@ -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++;
Expand All @@ -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
Expand All @@ -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++;
}
Expand Down
21 changes: 20 additions & 1 deletion engines/sci/engine/script_patches.h
Expand Up @@ -90,13 +90,32 @@ class ScriptPatcher {
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;
Expand Down

0 comments on commit 82165bb

Please sign in to comment.