diff options
-rw-r--r-- | engines/sci/engine/script.cpp | 2 | ||||
-rw-r--r-- | engines/sci/engine/script.h | 9 | ||||
-rw-r--r-- | engines/sci/engine/script_patches.cpp | 782 |
3 files changed, 535 insertions, 258 deletions
diff --git a/engines/sci/engine/script.cpp b/engines/sci/engine/script.cpp index 36d2841b07..6616a0ee13 100644 --- a/engines/sci/engine/script.cpp +++ b/engines/sci/engine/script.cpp @@ -136,7 +136,7 @@ void Script::load(int script_nr, ResourceManager *resMan) { memcpy(_buf, script->data, script->size); // Check scripts for matching signatures and patch those, if found - matchSignatureAndPatch(_nr, _buf, script->size); + patcherProcessScript(_nr, _buf, script->size); if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1) { Resource *heap = resMan->findResource(ResourceId(kResourceTypeHeap, _nr), 0); diff --git a/engines/sci/engine/script.h b/engines/sci/engine/script.h index 7dde7f4be6..56a9004226 100644 --- a/engines/sci/engine/script.h +++ b/engines/sci/engine/script.h @@ -30,7 +30,7 @@ namespace Sci { struct EngineState; class ResourceManager; -struct SciScriptSignature; +struct SciScriptPatcherEntry; enum ScriptObjectTypes { SCI_OBJ_TERMINATOR, @@ -98,9 +98,10 @@ public: void freeScript(); void load(int script_nr, ResourceManager *resMan); - void matchSignatureAndPatch(uint16 scriptNr, byte *scriptData, const uint32 scriptSize); - int32 findSignature(const SciScriptSignature *signature, const byte *scriptData, const uint32 scriptSize, bool isMacSci11); - void applyPatch(const uint16 *patch, byte *scriptData, const uint32 scriptSize, int32 signatureOffset, bool isMacSci11); + void patcherProcessScript(uint16 scriptNr, byte *scriptData, const uint32 scriptSize); + void patcherInitSignature(SciScriptPatcherEntry *patchTable, bool isMacSci11); + int32 patcherFindSignature(const SciScriptPatcherEntry *patchEntry, const byte *scriptData, const uint32 scriptSize, bool isMacSci11); + void patcherApplyPatch(const SciScriptPatcherEntry *patchEntry, byte *scriptData, const uint32 scriptSize, int32 signatureOffset, bool isMacSci11); virtual bool isValidOffset(uint16 offset) const; virtual SegmentRef dereference(reg_t pointer); diff --git a/engines/sci/engine/script_patches.cpp b/engines/sci/engine/script_patches.cpp index cb80ba79f7..94e351080a 100644 --- a/engines/sci/engine/script_patches.cpp +++ b/engines/sci/engine/script_patches.cpp @@ -21,6 +21,7 @@ */ #include "sci/sci.h" +#include "sci/engine/kernel.h" #include "sci/engine/script.h" #include "sci/engine/state.h" #include "sci/engine/features.h" @@ -34,41 +35,83 @@ namespace Sci { #define SIG_COMMANDMASK 0xF000 #define SIG_VALUEMASK 0x0FFF #define SIG_BYTEMASK 0x00FF +#define SIG_MAGICDWORD 0xF000 #define SIG_ADDTOOFFSET 0xE000 +#define SIG_SELECTOR16 0x9000 +#define SIG_SELECTOR8 0x8000 #define SIG_UINT16 0x1000 +#define SIG_BYTE 0x0000 -#define PATCH_END 0xFFFF -#define PATCH_COMMANDMASK 0xF000 -#define PATCH_VALUEMASK 0x0FFF -#define PATCH_BYTEMASK 0x00FF -#define PATCH_ADDTOOFFSET 0xE000 +#define PATCH_END SIG_END +#define PATCH_COMMANDMASK SIG_COMMANDMASK +#define PATCH_VALUEMASK SIG_VALUEMASK +#define PATCH_BYTEMASK SIG_BYTEMASK +#define PATCH_ADDTOOFFSET SIG_ADDTOOFFSET #define PATCH_GETORIGINALBYTE 0xD000 #define PATCH_GETORIGINALBYTEADJUST 0xC000 -#define PATCH_ADJUSTWORD 0xB000 -#define PATCH_ADJUSTWORD_NEG 0xA000 -#define PATCH_UINT16 0x1000 +#define PATCH_SELECTOR16 SIG_SELECTOR16 +#define PATCH_SELECTOR8 SIG_SELECTOR8 +#define PATCH_UINT16 SIG_UINT16 +#define PATCH_BYTE SIG_BYTE -#define PATCH_MAGICDWORD(a, b, c, d) CONSTANT_LE_32(a | (b << 8) | (c << 16) | (d << 24)) +// defines maximum scratch area for getting original bytes from unpatched script data #define PATCH_VALUELIMIT 4096 -struct SciScriptSignature { +struct SciScriptPatcherEntry { uint16 scriptNr; const char *description; int16 applyCount; uint32 magicDWord; int magicOffset; - const uint16 *data; - const uint16 *patch; + const uint16 *signatureData; + const uint16 *patchData; }; #define SCI_SIGNATUREENTRY_TERMINATOR { 0, NULL, 0, 0, 0, NULL, NULL } -// signatures are built like this: -// - first a counter of the bytes that follow -// - then the actual bytes that need to get matched -// - then another counter of bytes (0 for EOS) -// - if not EOS, an adjust offset and the actual bytes -// - rinse and repeat +struct SciScriptPatcherSelector { + const char *name; + int16 id; +}; + +SciScriptPatcherSelector selectorTable[] = { + "cycles", -1, // system selector + "seconds", -1, // system selector + "init", -1, // system selector + "dispose", -1, // system selector + "new", -1, // system selector + "curEvent", -1, // system selector + "disable", -1, // system selector + "show", -1, // system selector + "x", -1, // system selector + "cel", -1, // system selector + "setMotion", -1, // system selector + "deskSarg", -1, // Gabriel Knight + "localize", -1, // Freddy Pharkas + "put", -1, // Police Quest 1 VGA + "solvePuzzle", -1, // Quest For Glory 3 + "timesShownID", -1, // Space Quest 1 VGA + NULL, -1 +}; + +enum ScriptPatcherSelectors { + SELECTOR_cycles = 0, + SELECTOR_seconds, + SELECTOR_init, + SELECTOR_dispose, + SELECTOR_new, + SELECTOR_curEvent, + SELECTOR_disable, + SELECTOR_show, + SELECTOR_x, + SELECTOR_cel, + SELECTOR_setMotion, + SELECTOR_deskSarg, + SELECTOR_localize, + SELECTOR_put, + SELECTOR_solvePuzzle, + SELECTOR_timesShownID +}; // =========================================================================== // Conquests of Camelot @@ -91,7 +134,7 @@ struct SciScriptSignature { // Responsible method: fawaz::handleEvent // Fixes bug #3614969 const uint16 camelotSignaturePeepingTom[] = { - 0x72, SIG_UINT16 + 0x7e, 0x07, // lofsa fawaz <-- start of proper initializion code + 0x72, SIG_MAGICDWORD, SIG_UINT16 + 0x7e, 0x07, // lofsa fawaz <-- start of proper initializion code 0xa1, 0xb9, // sag b9h SIG_ADDTOOFFSET +571, // skip 571 bytes 0x39, 0x7a, // pushi 7a <-- initialization code when walking automatically @@ -113,9 +156,9 @@ const uint16 camelotPatchPeepingTom[] = { PATCH_END }; -// script, description, magic DWORD, adjust -const SciScriptSignature camelotSignatures[] = { - { 62, "fix peepingTom Sierra bug", 1, PATCH_MAGICDWORD(0x7e, 0x07, 0xa1, 0xb9), -1, camelotSignaturePeepingTom, camelotPatchPeepingTom }, +// script, description, signature patch +SciScriptPatcherEntry camelotSignatures[] = { + { 62, "fix peepingTom Sierra bug", 1, 0, 0, camelotSignaturePeepingTom, camelotPatchPeepingTom }, SCI_SIGNATUREENTRY_TERMINATOR }; @@ -124,7 +167,10 @@ const SciScriptSignature camelotSignatures[] = { // boundaries of room 660. Normally a textbox is supposed to get on screen // but the call is wrong, so not only do we get an error message the script // is also hanging because the cue won't get sent out -// This also happens in sierra sci - refer to bug #3038387 +// This also happens in sierra sci +// Applies to at least: PC-CD +// Responsible method: stayAndHelp::changeState +// Fixes bug: #3038387 const uint16 ecoquest1SignatureStayAndHelp[] = { 0x3f, 0x01, // link 01 0x87, 0x01, // lap param[1] @@ -136,12 +182,13 @@ const uint16 ecoquest1SignatureStayAndHelp[] = { 0x31, 0x1c, // bnt [next state] 0x76, // push0 0x45, 0x01, 0x00, // callb export1 from script 0 (switching control off) + SIG_MAGICDWORD, 0x38, SIG_UINT16 + 0x22, 0x01, // pushi 0122 0x78, // push1 0x76, // push0 0x81, 0x00, // lag global[0] 0x4a, 0x06, // send 06 - call ego::setMotion(0) - 0x39, 0x6e, // pushi 6e (selector init) + 0x39, SIG_SELECTOR8 + SELECTOR_init, // pushi "init" 0x39, 0x04, // pushi 04 0x76, // push0 0x76, // push0 @@ -165,7 +212,7 @@ const uint16 ecoquest1PatchStayAndHelp[] = { 0x76, // push0 0x81, 0x00, // lag global[0] 0x4a, 0x06, // send 06 - call ego::setMotion(0) - 0x39, 0x6e, // pushi 6e (selector init) + 0x39, PATCH_SELECTOR8 + SELECTOR_init, // pushi "init" 0x39, 0x06, // pushi 06 0x39, 0x02, // pushi 02 (additional 2 bytes) 0x76, // push0 @@ -178,9 +225,9 @@ const uint16 ecoquest1PatchStayAndHelp[] = { PATCH_END }; -// script, description, magic DWORD, adjust -const SciScriptSignature ecoquest1Signatures[] = { - { 660, "CD: bad messagebox and freeze", 1, PATCH_MAGICDWORD(0x38, 0x22, 0x01, 0x78), -17, ecoquest1SignatureStayAndHelp, ecoquest1PatchStayAndHelp }, +// script, description, signature patch +SciScriptPatcherEntry ecoquest1Signatures[] = { + { 660, "CD: bad messagebox and freeze", 1, 0, 0, ecoquest1SignatureStayAndHelp, ecoquest1PatchStayAndHelp }, SCI_SIGNATUREENTRY_TERMINATOR }; @@ -195,6 +242,7 @@ const uint16 ecoquest2SignatureEcorder[] = { 0x39, 0x0a, // pushi 0a 0x5b, 0x04, 0x1e, // lea temp[1e] 0x36, // push + SIG_MAGICDWORD, 0x39, 0x64, // pushi 64 0x39, 0x7d, // pushi 7d 0x39, 0x32, // pushi 32 @@ -249,6 +297,7 @@ const uint16 ecoquest2SignatureEcorderTutorial[] = { 0x39, 0x0a, // pushi 0a 0x5b, 0x04, 0x1f, // lea temp[1f] 0x36, // push + SIG_MAGICDWORD, 0x39, 0x64, // pushi 64 0x39, 0x7d, // pushi 7d 0x39, 0x32, // pushi 32 @@ -267,42 +316,42 @@ const uint16 ecoquest2SignatureEcorderTutorial[] = { }; const uint16 ecoquest2PatchEcorderTutorial[] = { - 0x31, 0x23, // bnt [next state] (save 1 byte) + 0x31, 0x23, // bnt [next state] (save 1 byte) // The parameter count below should be 7, but we're out of bytes // to patch! A workaround has been added because of this - 0x78, // push1 (parameter count) - //0x39, 0x07, // pushi 07 (parameter count) - 0x39, 0x0b, // push (FillBoxAny) - 0x39, 0x1d, // pushi 29d - 0x39, 0x73, // pushi 115d - 0x39, 0x5e, // pushi 94d - 0x38, 0xd7, 0x00, // pushi 215d - 0x78, // push1 (visual screen) - 0x39, 0x17, // pushi 17 (color) - 0x43, 0x6c, 0x0e, // call kGraph + 0x78, // push1 (parameter count) + //0x39, 0x07, // pushi 07 (parameter count) + 0x39, 0x0b, // push (FillBoxAny) + 0x39, 0x1d, // pushi 29d + 0x39, 0x73, // pushi 115d + 0x39, 0x5e, // pushi 94d + 0x38, PATCH_UINT16 + 0xd7, 0x00, // pushi 215d + 0x78, // push1 (visual screen) + 0x39, 0x17, // pushi 17 (color) + 0x43, 0x6c, 0x0e, // call kGraph // The parameter count below should be 5, but we're out of bytes // to patch! A workaround has been added because of this - 0x78, // push1 (parameter count) - //0x39, 0x05, // pushi 05 (parameter count) - 0x39, 0x0c, // pushi 12d (UpdateBox) - 0x39, 0x1d, // pushi 29d - 0x39, 0x73, // pushi 115d - 0x39, 0x5e, // pushi 94d - 0x38, 0xd7, 0x00, // pushi 215d - 0x43, 0x6c, 0x0a, // call kGraph + 0x78, // push1 (parameter count) + //0x39, 0x05, // pushi 05 (parameter count) + 0x39, 0x0c, // pushi 12d (UpdateBox) + 0x39, 0x1d, // pushi 29d + 0x39, 0x73, // pushi 115d + 0x39, 0x5e, // pushi 94d + 0x38, PATCH_UINT16 + 0xd7, 0x00, // pushi 215d + 0x43, 0x6c, 0x0a, // call kGraph // We are out of bytes to patch at this point, // so we skip 494 (0x1EE) bytes to reuse this code: // ldi 1e // aTop 20 // jmp 030e (jump to end) - 0x32, 0xee, 0x01, // skip 494 (0x1EE) bytes + 0x32, PATCH_UINT16 + 0xee, 0x01, // skip 494 (0x1EE) bytes PATCH_END }; -// script, description, magic DWORD, adjust -const SciScriptSignature ecoquest2Signatures[] = { - { 50, "initial text not removed on ecorder", 1, PATCH_MAGICDWORD(0x39, 0x64, 0x39, 0x7d), -8, ecoquest2SignatureEcorder, ecoquest2PatchEcorder }, - { 333, "initial text not removed on ecorder tutorial",1, PATCH_MAGICDWORD(0x39, 0x64, 0x39, 0x7d), -9, ecoquest2SignatureEcorderTutorial, ecoquest2PatchEcorderTutorial }, +// script, description, signature patch +SciScriptPatcherEntry ecoquest2Signatures[] = { + { 50, "initial text not removed on ecorder", 1, 0, 0, ecoquest2SignatureEcorder, ecoquest2PatchEcorder }, + { 333, "initial text not removed on ecorder tutorial",1, 0, 0, ecoquest2SignatureEcorderTutorial, ecoquest2PatchEcorderTutorial }, SCI_SIGNATUREENTRY_TERMINATOR }; @@ -317,6 +366,7 @@ const uint16 fanmadeSignatureInfiniteLoop[] = { 0x39, 0x00, // pushi 00 0x87, 0x01, // lap 01 0x4b, 0x04, // send 04 + SIG_MAGICDWORD, 0x18, // not 0x30, SIG_UINT16 + 0x2f, 0x00, // bnt 002f [06a5] --> jmp ffbc [0664] --> BUG! infinite loop SIG_END @@ -328,9 +378,9 @@ const uint16 fanmadePatchInfiniteLoop[] = { PATCH_END }; -// script, description, magic DWORD, adjust -const SciScriptSignature fanmadeSignatures[] = { - { 999, "infinite loop on typo", 1, PATCH_MAGICDWORD(0x18, 0x30, 0x2f, 0x00), -9, fanmadeSignatureInfiniteLoop, fanmadePatchInfiniteLoop }, +// script, description, signature patch +SciScriptPatcherEntry fanmadeSignatures[] = { + { 999, "infinite loop on typo", 1, 0, 0, fanmadeSignatureInfiniteLoop, fanmadePatchInfiniteLoop }, SCI_SIGNATUREENTRY_TERMINATOR }; @@ -342,13 +392,16 @@ const SciScriptSignature fanmadeSignatures[] = { // was already playing in the sound driver. In our case we would also stop // the sample from playing, so we patch it out // The "score" code is already buggy and sets volume to 0 when playing +// Applies to at least: English PC-CD +// Responsible method: unknown const uint16 freddypharkasSignatureScoreDisposal[] = { - 0x67, 0x32, // pTos 32 (selector theAudCount) - 0x78, // push1 - 0x39, 0x0d, // pushi 0d - 0x43, 0x75, 0x02, // call kDoAudio - 0x1c, // ne? - 0x31, // bnt (-> to skip disposal) + 0x67, 0x32, // pTos 32 (selector theAudCount) + 0x78, // push1 + SIG_MAGICDWORD, + 0x39, 0x0d, // pushi 0d + 0x43, 0x75, 0x02, // call kDoAudio + 0x1c, // ne? + 0x31, // bnt (-> to skip disposal) SIG_END }; @@ -365,9 +418,12 @@ const uint16 freddypharkasPatchScoreDisposal[] = { // in IconBar::disable doing endless loops even in sierra sci, because there // is no enabled icon left. We remove disabling of icon 8 (which is help), // this fixes the issue. +// Applies to at least: English PC-CD +// Responsible method: rm235::init and sEnterFrom500::changeState const uint16 freddypharkasSignatureCanisterHang[] = { - 0x38, SIG_UINT16 + 0xf1, 0x00, // pushi f1 (selector disable) + 0x38, SIG_SELECTOR16 + SELECTOR_disable, // pushi disable 0x7a, // push2 + SIG_MAGICDWORD, 0x39, 0x07, // pushi 07 0x39, 0x08, // pushi 08 0x81, 0x45, // lag 45 @@ -377,11 +433,11 @@ const uint16 freddypharkasSignatureCanisterHang[] = { const uint16 freddypharkasPatchCanisterHang[] = { PATCH_ADDTOOFFSET | +3, - 0x78, // push1 + 0x78, // push1 PATCH_ADDTOOFFSET | +2, - 0x33, 0x00, // ldi 00 (waste 2 bytes) + 0x33, 0x00, // ldi 00 (waste 2 bytes) PATCH_ADDTOOFFSET | +3, - 0x06, // send 06 - call IconBar::disable(7) + 0x06, // send 06 - call IconBar::disable(7) PATCH_END }; @@ -393,35 +449,41 @@ const uint16 freddypharkasPatchCanisterHang[] = { // ego, sometimes clicks also won't get registered. Strangely it's not nearly // as bad as in our sci, but these differences may be caused by timing. // We just reuse the active event, thus removing the duplicate kGetEvent call. +// Applies to at least: English PC-CD, German Floppy, English Mac +// Responsible method: lowerLadder::doit and highLadder::doit const uint16 freddypharkasSignatureLadderEvent[] = { - 0x39, 0x6d, // pushi 6d (selector new) + 0x39, SIG_MAGICDWORD, + SIG_SELECTOR8 + SELECTOR_new, // pushi new 0x76, // push0 - 0x38, SIG_UINT16 + 0xf5, 0x00, // pushi f5 (selector curEvent) + 0x38, SIG_SELECTOR16 + SELECTOR_curEvent, // pushi curEvent 0x76, // push0 0x81, 0x50, // lag global[50] 0x4a, 0x04, // send 04 - read User::curEvent 0x4a, 0x04, // send 04 - call curEvent::new 0xa5, 0x00, // sat temp[0] - 0x38, SIG_UINT16 + 0x94, 0x00, // pushi 94 (selector localize) + 0x38, SIG_SELECTOR16 + SELECTOR_localize, 0x76, // push0 0x4a, 0x04, // send 04 - call curEvent::localize SIG_END }; const uint16 freddypharkasPatchLadderEvent[] = { - 0x34, 0x00, 0x00, // ldi 0000 (waste 3 bytes, overwrites first 2 pushes) + 0x34, 0x00, 0x00, // ldi 0000 (waste 3 bytes, overwrites first 2 pushes) PATCH_ADDTOOFFSET | +8, - 0xa5, 0x00, // sat temp[0] (waste 2 bytes, overwrites 2nd send) + 0xa5, 0x00, // sat temp[0] (waste 2 bytes, overwrites 2nd send) PATCH_ADDTOOFFSET | +2, - 0x34, 0x00, 0x00, // ldi 0000 - 0x34, 0x00, 0x00, // ldi 0000 (waste 6 bytes, overwrites last 3 opcodes) + 0x34, 0x00, 0x00, // ldi 0000 + 0x34, 0x00, 0x00, // ldi 0000 (waste 6 bytes, overwrites last 3 opcodes) PATCH_END }; // In the Macintosh version of Freddy Pharkas, kRespondsTo is broken for // property selectors. They hacked the script to work around the issue, // so we revert the script back to using the values of the DOS script. +// Applies to at least: English Mac +// Responsible method: unknown const uint16 freddypharkasSignatureMacInventory[] = { + SIG_MAGICDWORD, 0x39, 0x23, // pushi 23 0x39, 0x74, // pushi 74 0x78, // push1 @@ -437,28 +499,31 @@ const uint16 freddypharkasPatchMacInventory[] = { PATCH_END }; -// script, description, magic DWORD, adjust -const SciScriptSignature freddypharkasSignatures[] = { - { 0, "CD: score early disposal", 1, PATCH_MAGICDWORD(0x39, 0x0d, 0x43, 0x75), -3, freddypharkasSignatureScoreDisposal, freddypharkasPatchScoreDisposal }, - { 15, "Mac: broken inventory", 1, PATCH_MAGICDWORD(0x39, 0x23, 0x39, 0x74), 0, freddypharkasSignatureMacInventory, freddypharkasPatchMacInventory }, - { 235, "CD: canister pickup hang", 3, PATCH_MAGICDWORD(0x39, 0x07, 0x39, 0x08), -4, freddypharkasSignatureCanisterHang, freddypharkasPatchCanisterHang }, - { 320, "ladder event issue", 2, PATCH_MAGICDWORD(0x6d, 0x76, 0x38, 0xf5), -1, freddypharkasSignatureLadderEvent, freddypharkasPatchLadderEvent }, +// script, description, signature patch +SciScriptPatcherEntry freddypharkasSignatures[] = { + { 0, "CD: score early disposal", 1, 0, 0, freddypharkasSignatureScoreDisposal, freddypharkasPatchScoreDisposal }, + { 15, "Mac: broken inventory", 1, 0, 0, freddypharkasSignatureMacInventory, freddypharkasPatchMacInventory }, + { 235, "CD: canister pickup hang", 3, 0, 0, freddypharkasSignatureCanisterHang, freddypharkasPatchCanisterHang }, + { 320, "ladder event issue", 2, 0, 0, freddypharkasSignatureLadderEvent, freddypharkasPatchLadderEvent }, SCI_SIGNATUREENTRY_TERMINATOR }; // =========================================================================== // daySixBeignet::changeState (4) is called when the cop goes out and sets cycles to 220. // this is not enough time to get to the door, so we patch that to 23 seconds +// Applies to at least: English PC-CD, German PC-CD, English Mac +// Responsible method: daySixBeignet::changeState const uint16 gk1SignatureDay6PoliceBeignet[] = { 0x35, 0x04, // ldi 04 0x1a, // eq? 0x30, SIG_ADDTOOFFSET +2, // bnt [next state check] - 0x38, SIG_UINT16 + 0x93, 0x00, // pushi 93 (selector dispose) + 0x38, SIG_SELECTOR16 + SELECTOR_dispose, // pushi dispose 0x76, // push0 0x72, SIG_ADDTOOFFSET +2, // lofsa deskSarg - 0x4a, 0x04, 0x00, // send 04 + 0x4a, SIG_UINT16 + 0x04, 0x00, // send 04 + SIG_MAGICDWORD, 0x34, SIG_UINT16 + 0xdc, 0x00, // ldi 220 - 0x65, 0x1a, // aTop cycles + 0x65, SIG_ADDTOOFFSET +1, // aTop cycles (1a for PC, 1c for Mac) 0x32, // jmp [end] SIG_END }; @@ -466,18 +531,21 @@ const uint16 gk1SignatureDay6PoliceBeignet[] = { const uint16 gk1PatchDay6PoliceBeignet[] = { PATCH_ADDTOOFFSET +16, 0x34, PATCH_UINT16 + 0x17, 0x00, // ldi 23 - 0x65, 0x1c, // aTop seconds + 0x65, PATCH_GETORIGINALBYTEADJUST +20, +2, // aTop seconds (1c for PC, 1e for Mac) PATCH_END }; // sargSleeping::changeState (8) is called when the cop falls asleep and sets cycles to 220. // this is not enough time to get to the door, so we patch it to 42 seconds +// Applies to at least: English PC-CD, German PC-CD, English Mac +// Responsible method: sargSleeping::changeState const uint16 gk1SignatureDay6PoliceSleep[] = { 0x35, 0x08, // ldi 08 0x1a, // eq? 0x31, SIG_ADDTOOFFSET +1, // bnt [next state check] + SIG_MAGICDWORD, 0x34, SIG_UINT16 + 0xdc, 0x00, // ldi 220 - 0x65, 0x1a, // aTop cycles + 0x65, SIG_ADDTOOFFSET +1, // aTop cycles (1a for PC, 1c for Mac) 0x32, // jmp [end] 0 }; @@ -485,14 +553,18 @@ const uint16 gk1SignatureDay6PoliceSleep[] = { const uint16 gk1PatchDay6PoliceSleep[] = { PATCH_ADDTOOFFSET +5, 0x34, SIG_UINT16 + 0x2a, 0x00, // ldi 42 - 0x65, 0x1c, // aTop seconds + 0x65, PATCH_GETORIGINALBYTEADJUST +9, +2, // aTop seconds (1c for PC, 1e for Mac) PATCH_END }; // startOfDay5::changeState (20h) - when gabriel goes to the phone the script will hang +// Applies to at least: English PC-CD, German PC-CD, English Mac +// Responsible method: startOfDay5::changeState const uint16 gk1SignatureDay5PhoneFreeze[] = { + 0x4a, + SIG_MAGICDWORD, SIG_UINT16 + 0x0c, 0x00, // send 0c 0x35, 0x03, // ldi 03 - 0x65, 0x1a, // aTop cycles + 0x65, SIG_ADDTOOFFSET +1, // aTop cycles 0x32, SIG_ADDTOOFFSET +2, // jmp [end] 0x3c, // dup 0x35, 0x21, // ldi 21 @@ -500,8 +572,9 @@ const uint16 gk1SignatureDay5PhoneFreeze[] = { }; const uint16 gk1PatchDay5PhoneFreeze[] = { - 0x35, 0x06, // ldi 06 - 0x65, 0x20, // aTop ticks + PATCH_ADDTOOFFSET +3, + 0x35, 0x06, // ldi 01 + 0x65, PATCH_GETORIGINALBYTEADJUST +6, +6, // aTop ticks PATCH_END }; @@ -513,7 +586,11 @@ const uint16 gk1PatchDay5PhoneFreeze[] = { // comparison between a number an an object. In the CD version, the checks are // in the correct order, thus the comparison is correct, thus we use the code // from the CD version in the floppy one. +// Applies to at least: English Floppy +// Responsible method: Interrogation::dispose +// TODO: Check, if English Mac is affected too and if this patch applies const uint16 gk1SignatureInterrogationBug[] = { + SIG_MAGICDWORD, 0x65, 0x4c, // aTop 4c 0x67, 0x50, // pTos 50 0x34, SIG_UINT16 + 0x10, 0x27, // ldi 2710 @@ -527,12 +604,12 @@ const uint16 gk1SignatureInterrogationBug[] = { 0x31, 0x15, // bnt 15 [05b9] 0x39, 0x0e, // pushi 0e 0x76, // push0 - 0x4a, 0x04, 0x00, // send 0004 + 0x4a, SIG_UINT16 + 0x04, 0x00, // send 0004 0xa5, 0x00, // sat 00 - 0x38, SIG_UINT16 + 0x93, 0x00, // pushi 0093 + 0x38, SIG_SELECTOR16 + SELECTOR_dispose, // pushi dispose 0x76, // push0 0x63, 0x50, // pToa 50 - 0x4a, 0x04, 0x00, // send 0004 + 0x4a, SIG_UINT16 + 0x04, 0x00, // send 0004 0x85, 0x00, // lat 00 0x65, 0x50, // aTop 50 SIG_END @@ -546,7 +623,7 @@ const uint16 gk1PatchInterrogationBug[] = { 0x76, // push0 0x4a, 0x04, 0x00, // send 0004 0xa5, 0x00, // sat 00 - 0x38, PATCH_UINT16 + 0x93, 0x00, // pushi 0093 + 0x38, SIG_SELECTOR16 + SELECTOR_dispose, // pushi dispose 0x76, // push0 0x63, 0x50, // pToa 50 0x4a, 0x04, 0x00, // send 0004 @@ -563,12 +640,12 @@ const uint16 gk1PatchInterrogationBug[] = { PATCH_END }; -// script, description, magic DWORD, adjust -const SciScriptSignature gk1Signatures[] = { - { 51, "interrogation bug", 1, PATCH_MAGICDWORD(0x65, 0x4c, 0x67, 0x50), 0, gk1SignatureInterrogationBug, gk1PatchInterrogationBug }, - { 212, "day 5 phone freeze", 1, PATCH_MAGICDWORD(0x35, 0x03, 0x65, 0x1a), 0, gk1SignatureDay5PhoneFreeze, gk1PatchDay5PhoneFreeze }, - { 230, "day 6 police beignet timer issue", 1, PATCH_MAGICDWORD(0x34, 0xdc, 0x00, 0x65), -16, gk1SignatureDay6PoliceBeignet, gk1PatchDay6PoliceBeignet }, - { 230, "day 6 police sleep timer issue", 1, PATCH_MAGICDWORD(0x34, 0xdc, 0x00, 0x65), -5, gk1SignatureDay6PoliceSleep, gk1PatchDay6PoliceSleep }, +// script, description, signature patch +SciScriptPatcherEntry gk1Signatures[] = { + { 51, "interrogation bug", 1, 0, 0, gk1SignatureInterrogationBug, gk1PatchInterrogationBug }, + { 212, "day 5 phone freeze", 1, 0, 0, gk1SignatureDay5PhoneFreeze, gk1PatchDay5PhoneFreeze }, + { 230, "day 6 police beignet timer issue", 1, 0, 0, gk1SignatureDay6PoliceBeignet, gk1PatchDay6PoliceBeignet }, + { 230, "day 6 police sleep timer issue", 1, 0, 0, gk1SignatureDay6PoliceSleep, gk1PatchDay6PoliceSleep }, SCI_SIGNATUREENTRY_TERMINATOR }; @@ -579,6 +656,7 @@ const SciScriptSignature gk1Signatures[] = { // the volume to max. We fix the export, so volume won't get modified in // those cases. const uint16 kq5SignatureCdHarpyVolume[] = { + SIG_MAGICDWORD, 0x80, SIG_UINT16 + 0x91, 0x01, // lag global[191h] 0x18, // not 0x30, SIG_UINT16 + 0x2c, 0x00, // bnt [jump further] (jumping, if global 191h is 1) @@ -641,6 +719,7 @@ const uint16 kq5SignatureWitchCageInit[] = { SIG_UINT16 + 0x00, 0x00, // bottom SIG_UINT16 + 0x00, 0x00, // right SIG_UINT16 + 0x00, 0x00, // extra property #1 + SIG_MAGICDWORD, SIG_UINT16 + 0x7a, 0x00, // extra property #2 SIG_UINT16 + 0xc8, 0x00, // extra property #3 SIG_UINT16 + 0xa3, 0x00, // extra property #4 @@ -670,6 +749,7 @@ const uint16 kq5PatchWitchCageInit[] = { // compatibilty between the DOS and Windows CD versions of KQ5. // TODO: Investigate these side effects more closely. const uint16 kq5SignatureWinGMSignals[] = { + SIG_MAGICDWORD, 0x80, SIG_UINT16 + 0x90, 0x01, // lag 0x190 0x18, // not 0x30, SIG_UINT16 + 0x1b, 0x00, // bnt +0x001B @@ -682,17 +762,17 @@ const uint16 kq5PatchWinGMSignals[] = { PATCH_END }; -// script, description, magic DWORD, adjust -const SciScriptSignature kq5Signatures[] = { - { 0, "CD: harpy volume change", 1, PATCH_MAGICDWORD(0x80, 0x91, 0x01, 0x18), 0, kq5SignatureCdHarpyVolume, kq5PatchCdHarpyVolume }, - { 200, "CD: witch cage init", 1, PATCH_MAGICDWORD(0x7a, 0x00, 0xc8, 0x00), -10, kq5SignatureWitchCageInit, kq5PatchWitchCageInit }, +// script, description, signature patch +SciScriptPatcherEntry kq5Signatures[] = { + { 0, "CD: harpy volume change", 1, 0, 0, kq5SignatureCdHarpyVolume, kq5PatchCdHarpyVolume }, + { 200, "CD: witch cage init", 1, 0, 0, kq5SignatureWitchCageInit, kq5PatchWitchCageInit }, SCI_SIGNATUREENTRY_TERMINATOR }; -const SciScriptSignature kq5WinGMSignatures[] = { - { 0, "CD: harpy volume change", 1, PATCH_MAGICDWORD(0x80, 0x91, 0x01, 0x18), 0, kq5SignatureCdHarpyVolume, kq5PatchCdHarpyVolume }, - { 200, "CD: witch cage init", 1, PATCH_MAGICDWORD(0x7a, 0x00, 0xc8, 0x00), -10, kq5SignatureWitchCageInit, kq5PatchWitchCageInit }, - { 124, "Win: GM Music signal checks", 4, PATCH_MAGICDWORD(0x80, 0x90, 0x01, 0x18), 0, kq5SignatureWinGMSignals, kq5PatchWinGMSignals }, +SciScriptPatcherEntry kq5WinGMSignatures[] = { + { 0, "CD: harpy volume change", 1, 0, 0, kq5SignatureCdHarpyVolume, kq5PatchCdHarpyVolume }, + { 200, "CD: witch cage init", 1, 0, 0, kq5SignatureWitchCageInit, kq5PatchWitchCageInit }, + { 124, "Win: GM Music signal checks", 4, 0, 0, kq5SignatureWinGMSignals, kq5PatchWinGMSignals }, SCI_SIGNATUREENTRY_TERMINATOR }; @@ -706,6 +786,7 @@ const SciScriptSignature kq5WinGMSignatures[] = { // constantly restarting (since it's being looped anyway), thus the normal // game speech can work while the baby cry sound is heard. Fixes bug #3034579. const uint16 kq6SignatureDuplicateBabyCry[] = { + SIG_MAGICDWORD, 0x83, 0x00, // lal 00 0x31, 0x1e, // bnt 1e [07f4] 0x78, // push1 @@ -724,9 +805,9 @@ const uint16 kq6PatchDuplicateBabyCry[] = { // grow the stack, because it's calling itself per switch. // Which means after a while ScummVM will bomb out because the stack frame // will be too large. This patch fixes the buggy script. -// This patch applies to at least CD, English floppy, German floppy +// Applies to at least: PC-CD, English PC floppy, German PC floppy, English Mac // Responsible method: KqInv::showSelf -// Fixes bug #3293954 +// Fixes bug: #3293954 const uint16 kq6SignatureInventoryStackFix[] = { 0x67, 0x30, // pTos state 0x34, SIG_UINT16 + 0x00, 0x20, // ldi 2000 @@ -734,12 +815,13 @@ const uint16 kq6SignatureInventoryStackFix[] = { 0x18, // not 0x31, 0x04, // bnt [not first refresh] 0x35, 0x00, // ldi 00 + SIG_MAGICDWORD, 0x65, 0x1e, // aTop curIcon 0x67, 0x30, // pTos state 0x34, SIG_UINT16 + 0xff, 0xdf, // ldi dfff 0x12, // and 0x65, 0x30, // aTop state - 0x38, SIG_ADDTOOFFSET + 2, // pushi "show" ("show" may be e100h for KQ6CD, xxxxh for KQ6 floppy) + 0x38, SIG_SELECTOR16 + SELECTOR_show, // pushi "show" ("show" is e1h for KQ6CD) 0x78, // push1 0x87, 0x00, // lap param[0] 0x31, 0x04, // bnt [use global for show] @@ -764,7 +846,6 @@ const uint16 kq6PatchInventoryStackFix[] = { 0x2f, 0x02, // bt [not first refresh] - saves 3 bytes in total 0x65, 0x1e, // aTop curIcon 0x00, // neg (either 2000 or 0000 in acc, this will create dfff or ffff) - saves 2 bytes -// 0x34, PATCH_UINT16 + 0xff, 0xdf, // ldi dfff 0x12, // and 0x65, 0x30, // aTop state 0x38, // pushi "show" @@ -789,10 +870,10 @@ const uint16 kq6PatchInventoryStackFix[] = { PATCH_END }; -// script, description, magic DWORD, adjust -const SciScriptSignature kq6Signatures[] = { - { 481, "duplicate baby cry", 1, PATCH_MAGICDWORD(0x83, 0x00, 0x31, 0x1e), 0, kq6SignatureDuplicateBabyCry, kq6PatchDuplicateBabyCry }, - { 907, "inventory stack fix", 1, PATCH_MAGICDWORD(0x65, 0x1e, 0x67, 0x30), -11, kq6SignatureInventoryStackFix, kq6PatchInventoryStackFix }, +// script, description, signature patch +SciScriptPatcherEntry kq6Signatures[] = { + { 481, "duplicate baby cry", 1, 0, 0, kq6SignatureDuplicateBabyCry, kq6PatchDuplicateBabyCry }, + { 907, "inventory stack fix", 1, 0, 0, kq6SignatureInventoryStackFix, kq6PatchInventoryStackFix }, SCI_SIGNATUREENTRY_TERMINATOR }; @@ -805,7 +886,10 @@ const SciScriptSignature kq6Signatures[] = { // the function is undefined, thus kStrCat() that is called inside the function // reads a random pointer and crashes. We patch all of the 5 function calls // (one for each letter typed from "R", "O", "B", "I", "N") so that they are -// the same as the English version. Fixes bug #3048054. +// the same as the English version. +// Applies to at least: German floppy +// Responsible method: unknown +// Fixes bug: #3048054 const uint16 longbowSignatureShowHandCode[] = { 0x78, // push1 0x78, // push1 @@ -815,9 +899,10 @@ const uint16 longbowSignatureShowHandCode[] = { 0x02, // perform the call above with 2 parameters 0x36, // push 0x40, SIG_ADDTOOFFSET +2, // call + SIG_MAGICDWORD, 0x02, // perform the call above with 2 parameters - 0x38, SIG_UINT16 + 0x1c, 0x01, // pushi 011c (setMotion) - 0x39, 0x04, // pushi 04 (x) + 0x38, SIG_SELECTOR16 + SELECTOR_setMotion, // pushi "setMotion" (0x11c in Longbow German) + 0x39, SIG_SELECTOR8 + SELECTOR_x, // pushi "x" (0x04 in Longbow German) 0x51, 0x1e, // class MoveTo SIG_END }; @@ -832,9 +917,9 @@ const uint16 longbowPatchShowHandCode[] = { PATCH_END }; -// script, description, magic DWORD, adjust -const SciScriptSignature longbowSignatures[] = { - { 210, "hand code crash", 5, PATCH_MAGICDWORD(0x02, 0x38, 0x1c, 0x01), -14, longbowSignatureShowHandCode, longbowPatchShowHandCode }, +// script, description, signature patch +SciScriptPatcherEntry longbowSignatures[] = { + { 210, "hand code crash", 5, 0, 0, longbowSignatureShowHandCode, longbowPatchShowHandCode }, SCI_SIGNATUREENTRY_TERMINATOR }; @@ -852,11 +937,12 @@ const SciScriptSignature longbowSignatures[] = { // "worked" in SSCI, but ScummVM/SCI doesn't allow that. // That's why those points weren't granted here at all. // We patch the script to use global 90, which seems to be unused in the whole game. +// Applies to at least: English floppy // Responsible method: rm63Script::handleEvent -// Fixes bug #3614419 +// Fixes bug: #3614419 const uint16 larry2SignatureWearParachutePoints[] = { 0x35, 0x01, // ldi 01 - 0xa1, 0x8e, // sag 8e + 0xa1, SIG_MAGICDWORD, 0x8e, // sag 8e 0x80, SIG_UINT16 + 0xe0, 0x01, // lag 1e0 0x18, // not 0x30, SIG_UINT16 + 0x0f, 0x00, // bnt [don't give points] @@ -873,9 +959,9 @@ const uint16 larry2PatchWearParachutePoints[] = { PATCH_END }; -// script, description, magic DWORD, adjust -const SciScriptSignature larry2Signatures[] = { - { 63, "plane: no points for wearing plane", 1, PATCH_MAGICDWORD(0x8e, 0x80, 0xe0, 0x01), -3, larry2SignatureWearParachutePoints, larry2PatchWearParachutePoints }, +// script, description, signature patch +SciScriptPatcherEntry larry2Signatures[] = { + { 63, "plane: no points for wearing plane", 1, 0, 0, larry2SignatureWearParachutePoints, larry2PatchWearParachutePoints }, SCI_SIGNATUREENTRY_TERMINATOR }; @@ -887,7 +973,10 @@ const SciScriptSignature larry2Signatures[] = { // doesn't happen anymore. We would otherwise get a crash // calling for invalid views (this happens of course also // in sierra sci) +// Applies to at least: German PC-CD +// Responsible method: unknown const uint16 larry6SignatureDeathDialog[] = { + SIG_MAGICDWORD, 0x3e, SIG_UINT16 + 0x33, 0x01, // link 0133 (offset 0x20) 0x35, 0xff, // ldi ff 0xa3, 0x00, // sal 00 @@ -921,18 +1010,21 @@ const uint16 larry6PatchDeathDialog[] = { PATCH_END }; -// script, description, magic DWORD, adjust -const SciScriptSignature larry6Signatures[] = { - { 82, "death dialog memory corruption", 1, PATCH_MAGICDWORD(0x3e, 0x33, 0x01, 0x35), 0, larry6SignatureDeathDialog, larry6PatchDeathDialog }, +// script, description, magic DWORD adjust signature patch +SciScriptPatcherEntry larry6Signatures[] = { + { 82, "death dialog memory corruption", 1, 0, 0, larry6SignatureDeathDialog, larry6PatchDeathDialog }, SCI_SIGNATUREENTRY_TERMINATOR }; // =========================================================================== // rm560::doit was supposed to close the painting, when Heimlich enters the -// room. The code is buggy, so it actually closes the painting, when heimlich +// room. The code is buggy. It actually closes the painting, when heimlich // is not in the room. We fix that. +// Applies to at least: English floppy +// Responsible method: rm560::doit const uint16 laurabow2SignaturePaintingClosing[] = { 0x4a, 0x04, // send 04 - read aHeimlich::room + SIG_MAGICDWORD, 0x36, // push 0x81, 0x0b, // lag global[11d] -> current room 0x1c, // ne? @@ -951,9 +1043,9 @@ const uint16 laurabow2PatchPaintingClosing[] = { PATCH_END }; -// script, description, magic DWORD, adjust -const SciScriptSignature laurabow2Signatures[] = { - { 560, "painting closing immediately", 1, PATCH_MAGICDWORD(0x36, 0x81, 0x0b, 0x1c), -2, laurabow2SignaturePaintingClosing, laurabow2PatchPaintingClosing }, +// script, description, signature patch +SciScriptPatcherEntry laurabow2Signatures[] = { + { 560, "painting closing immediately", 1, 0, 0, laurabow2SignaturePaintingClosing, laurabow2PatchPaintingClosing }, SCI_SIGNATUREENTRY_TERMINATOR }; @@ -964,7 +1056,7 @@ const SciScriptSignature laurabow2Signatures[] = { // We set the savedgame-id directly right after restoring in kRestoreGame. const uint16 mothergoose256SignatureReplay[] = { 0x36, // push - 0x35, 0x20, // ldi 20 + 0x35, SIG_MAGICDWORD, 0x20, // ldi 20 0x04, // sub 0xa1, 0xb3, // sag global[b3] SIG_END @@ -979,9 +1071,9 @@ const uint16 mothergoose256PatchReplay[] = { // when saving, it also checks if the savegame ID is below 13. // we change this to check if below 113 instead const uint16 mothergoose256SignatureSaveLimit[] = { - 0x89, 0xb3, // lsg global[b3] - 0x35, 0x0d, // ldi 0d - 0x20, // ge? + 0x89, SIG_MAGICDWORD, 0xb3, // lsg global[b3] + 0x35, 0x0d, // ldi 0d + 0x20, // ge? SIG_END }; @@ -991,11 +1083,11 @@ const uint16 mothergoose256PatchSaveLimit[] = { PATCH_END }; -// script, description, magic DWORD, adjust -const SciScriptSignature mothergoose256Signatures[] = { - { 0, "replay save issue", 1, PATCH_MAGICDWORD(0x20, 0x04, 0xa1, 0xb3), -2, mothergoose256SignatureReplay, mothergoose256PatchReplay }, - { 0, "save limit dialog (SCI1.1)", 1, PATCH_MAGICDWORD(0xb3, 0x35, 0x0d, 0x20), -1, mothergoose256SignatureSaveLimit, mothergoose256PatchSaveLimit }, - { 994, "save limit dialog (SCI1)", 1, PATCH_MAGICDWORD(0xb3, 0x35, 0x0d, 0x20), -1, mothergoose256SignatureSaveLimit, mothergoose256PatchSaveLimit }, +// script, description, magic DWORD adjust signature patch +SciScriptPatcherEntry mothergoose256Signatures[] = { + { 0, "replay save issue", 1, 0, 0, mothergoose256SignatureReplay, mothergoose256PatchReplay }, + { 0, "save limit dialog (SCI1.1)", 1, 0, 0, mothergoose256SignatureSaveLimit, mothergoose256PatchSaveLimit }, + { 994, "save limit dialog (SCI1)", 1, 0, 0, mothergoose256SignatureSaveLimit, mothergoose256PatchSaveLimit }, SCI_SIGNATUREENTRY_TERMINATOR }; @@ -1009,14 +1101,16 @@ const SciScriptSignature mothergoose256Signatures[] = { // SSCI. // This patch changes the code, so that the gun is actually given away // when the 2 seconds have passed and the locker got closed. +// Applies to at least: English floppy // Responsible method: putGun::changeState (script 341) -// Fixes bug #3036933 / #3303802 +// Fixes bug: #3036933 / #3303802 const uint16 pq1vgaSignaturePutGunInLockerBug[] = { 0x35, 0x00, // ldi 00 0x1a, // eq? 0x31, 0x25, // bnt [next state check] SIG_ADDTOOFFSET +22, // [skip 22 bytes] - 0x38, SIG_UINT16 + 0x5f, 0x01, // pushi 15fh + SIG_MAGICDWORD, + 0x38, SIG_SELECTOR16 + SELECTOR_put, // pushi "put" 0x78, // push1 0x76, // push0 0x81, 0x00, // lag 00 @@ -1028,7 +1122,7 @@ const uint16 pq1vgaSignaturePutGunInLockerBug[] = { 0x35, 0x01, // ldi 01 0x1a, // eq? 0x31, 0x08, // bnt [end of method] - 0x39, 0x6f, // pushi 6fh + 0x39, SIG_SELECTOR8 + SELECTOR_dispose, // pushi "dispose" 0x76, // push0 0x72, SIG_UINT16 + 0x88, 0x00, // lofsa 0088 0x4a, 0x04, // send 04 - locker::dispose @@ -1046,7 +1140,7 @@ const uint16 pq1vgaPatchPutGunInLockerBug[] = { 0x35, 0x01, // ldi 01 0x1a, // eq? 0x31, 0x11, // bnt [end of method] - 0x38, SIG_UINT16 + 0x5f, 0x01, // pushi 15fh + 0x38, PATCH_SELECTOR16 + SELECTOR_put, // pushi "put" 0x78, // push1 0x76, // push0 0x81, 0x00, // lag 00 @@ -1054,9 +1148,9 @@ const uint16 pq1vgaPatchPutGunInLockerBug[] = { PATCH_END }; -// script, description, magic DWORD, adjust -const SciScriptSignature pq1vgaSignatures[] = { - { 341, "put gun in locker bug", 1, PATCH_MAGICDWORD(0x38, 0x5f, 0x01, 0x78), -27, pq1vgaSignaturePutGunInLockerBug, pq1vgaPatchPutGunInLockerBug }, +// script, description, signature patch +SciScriptPatcherEntry pq1vgaSignatures[] = { + { 341, "put gun in locker bug", 1, 0, 0, pq1vgaSignaturePutGunInLockerBug, pq1vgaPatchPutGunInLockerBug }, SCI_SIGNATUREENTRY_TERMINATOR }; @@ -1069,8 +1163,11 @@ const SciScriptSignature pq1vgaSignatures[] = { // not nearly as bad as in our sci, but these differences may be caused by // timing. // We just reuse the active event, thus removing the duplicate kGetEvent call. +// Applies to at least: English floppy +// Responsible method: pointBox::doit const uint16 qfg1vgaSignatureFightEvents[] = { - 0x39, 0x6d, // pushi 6d (selector new) + 0x39, SIG_MAGICDWORD, + SIG_SELECTOR8 + SELECTOR_new, // pushi "new" 0x76, // push0 0x51, 0x07, // class Event 0x4a, 0x04, // send 04 - call Event::new @@ -1090,7 +1187,7 @@ const uint16 qfg1vgaSignatureFightEvents[] = { }; const uint16 qfg1vgaPatchFightEvents[] = { - 0x38, PATCH_UINT16 + 0x5a, 0x01, // pushi 15a (selector curEvent) + 0x38, PATCH_SELECTOR16 + SELECTOR_curEvent, // pushi 15a (selector curEvent) 0x76, // push0 0x81, 0x50, // lag global[50] 0x4a, 0x04, // send 04 - read User::curEvent -> needs one byte more than previous code @@ -1119,6 +1216,7 @@ const uint16 qfg1vgaPatchFightEvents[] = { // Patch 1: Increase temp space const uint16 qfg1vgaSignatureTempSpace[] = { + SIG_MAGICDWORD, 0x3f, 0xba, // link 0xba 0x87, 0x00, // lap 0 SIG_END @@ -1131,6 +1229,7 @@ const uint16 qfg1vgaPatchTempSpace[] = { // Patch 2: Move the pointer used for the window header a little bit const uint16 qfg1vgaSignatureDialogHeader[] = { + SIG_MAGICDWORD, 0x5b, 0x04, 0x80, // lea temp[0x80] 0x36, // push SIG_END @@ -1151,6 +1250,7 @@ const uint16 qfg1vgaPatchDialogHeader[] = { // to 85, 165, which is not an edge case thus the freeze is avoided. // Fixes bug #3585189. const uint16 qfg1vgaSignatureMoveToCrusher[] = { + SIG_MAGICDWORD, 0x51, 0x1f, // class Motion 0x36, // push 0x39, 0x4f, // pushi 4f (79 - x) @@ -1169,6 +1269,7 @@ const uint16 qfg1vgaPatchMoveToCrusher[] = { // spot when sneaking. In GuardsTrumpet::changeState, we change the final // location where Ego is moved from 111, 111 to 114, 114. Fixes bug #3604939. const uint16 qfg1vgaSignatureMoveToCastleGate[] = { + SIG_MAGICDWORD, 0x51, 0x1f, // class MoveTo 0x36, // push 0x39, 0x6f, // pushi 6f (111 - x) @@ -1186,9 +1287,11 @@ const uint16 qfg1vgaPatchMoveToCastleGate[] = { // Typo in the original Sierra scripts // Looking at a cheetaur resulted in a text about a Saurus Rex // The code treats both monster types the same. +// Applies to at least: English floppy // Responsible method: smallMonster::doVerb // Fixes bug #3604943. const uint16 qfg1vgaSignatureCheetaurDescription[] = { + SIG_MAGICDWORD, 0x34, SIG_UINT16 + 0xb8, 0x01, // ldi 01b8 0x1a, // eq? 0x31, 0x16, // bnt 16 @@ -1216,6 +1319,7 @@ const uint16 qfg1vgaPatchCheetaurDescription[] = { // Local 5 of that room is a timer, that closes the door (object door11). // Setting it to 1 during happyFace::changeState(0) stops door11::doit from // calling goTo6::init, so the whole issue is stopped from happening. +// Applies to at least: English floppy // Responsible method: happyFace::changeState, door11::doit // Fixes bug #3585793 const uint16 qfg1vgaSignatureFunnyRoomFix[] = { @@ -1225,6 +1329,7 @@ const uint16 qfg1vgaSignatureFunnyRoomFix[] = { 0x35, 0x00, // ldi 00 0x1a, // eq? 0x30, SIG_UINT16 + 0x25, 0x00, // bnt 0025 [-> next state] + SIG_MAGICDWORD, 0x35, 0x01, // ldi 01 0xa3, 0x4e, // sal 4e SIG_END @@ -1240,16 +1345,16 @@ const uint16 qfg1vgaPatchFunnyRoomFix[] = { PATCH_END }; -// script, description, magic DWORD, adjust -const SciScriptSignature qfg1vgaSignatures[] = { - { 215, "fight event issue", 1, PATCH_MAGICDWORD(0x6d, 0x76, 0x51, 0x07), -1, qfg1vgaSignatureFightEvents, qfg1vgaPatchFightEvents }, - { 216, "weapon master event issue", 1, PATCH_MAGICDWORD(0x6d, 0x76, 0x51, 0x07), -1, qfg1vgaSignatureFightEvents, qfg1vgaPatchFightEvents }, - { 814, "window text temp space", 1, PATCH_MAGICDWORD(0x3f, 0xba, 0x87, 0x00), 0, qfg1vgaSignatureTempSpace, qfg1vgaPatchTempSpace }, - { 814, "dialog header offset", 3, PATCH_MAGICDWORD(0x5b, 0x04, 0x80, 0x36), 0, qfg1vgaSignatureDialogHeader, qfg1vgaPatchDialogHeader }, - { 331, "moving to crusher", 1, PATCH_MAGICDWORD(0x51, 0x1f, 0x36, 0x39), 0, qfg1vgaSignatureMoveToCrusher, qfg1vgaPatchMoveToCrusher }, - { 41, "moving to castle gate", 1, PATCH_MAGICDWORD(0x51, 0x1f, 0x36, 0x39), 0, qfg1vgaSignatureMoveToCastleGate, qfg1vgaPatchMoveToCastleGate }, - { 210, "cheetaur description fixed", 1, PATCH_MAGICDWORD(0x34, 0xb8, 0x01, 0x1a), 0, qfg1vgaSignatureCheetaurDescription, qfg1vgaPatchCheetaurDescription }, - { 96, "funny room script bug fixed", 1, PATCH_MAGICDWORD(0x35, 0x01, 0xa3, 0x4e), -10, qfg1vgaSignatureFunnyRoomFix, qfg1vgaPatchFunnyRoomFix }, +// script, description, magic DWORD adjust signature patch +SciScriptPatcherEntry qfg1vgaSignatures[] = { + { 215, "fight event issue", 1, 0, 0, qfg1vgaSignatureFightEvents, qfg1vgaPatchFightEvents }, + { 216, "weapon master event issue", 1, 0, 0, qfg1vgaSignatureFightEvents, qfg1vgaPatchFightEvents }, + { 814, "window text temp space", 1, 0, 0, qfg1vgaSignatureTempSpace, qfg1vgaPatchTempSpace }, + { 814, "dialog header offset", 3, 0, 0, qfg1vgaSignatureDialogHeader, qfg1vgaPatchDialogHeader }, + { 331, "moving to crusher", 1, 0, 0, qfg1vgaSignatureMoveToCrusher, qfg1vgaPatchMoveToCrusher }, + { 41, "moving to castle gate", 1, 0, 0, qfg1vgaSignatureMoveToCastleGate, qfg1vgaPatchMoveToCastleGate }, + { 210, "cheetaur description fixed", 1, 0, 0, qfg1vgaSignatureCheetaurDescription, qfg1vgaPatchCheetaurDescription }, + { 96, "funny room script bug fixed", 1, 0, 0, qfg1vgaSignatureFunnyRoomFix, qfg1vgaPatchFunnyRoomFix }, SCI_SIGNATUREENTRY_TERMINATOR }; @@ -1268,7 +1373,7 @@ const SciScriptSignature qfg1vgaSignatures[] = { // crashes because of these constant quick object reallocations. Fixes bug // #3037996. const uint16 qfg2SignatureImportDialog[] = { - 0x63, 0x20, // pToa text + 0x63, SIG_MAGICDWORD, 0x20, // pToa text 0x30, SIG_UINT16 + 0x0b, 0x00, // bnt [next state] 0x7a, // push2 0x39, 0x03, // pushi 03 @@ -1285,16 +1390,16 @@ const uint16 qfg2PatchImportDialog[] = { PATCH_END }; -// script, description, magic DWORD, adjust -const SciScriptSignature qfg2Signatures[] = { - { 944, "import dialog continuous calls", 1, PATCH_MAGICDWORD(0x20, 0x30, 0x0b, 0x00), -1, qfg2SignatureImportDialog, qfg2PatchImportDialog }, +// script, description, signature patch +SciScriptPatcherEntry qfg2Signatures[] = { + { 944, "import dialog continuous calls", 1, 0, 0, qfg2SignatureImportDialog, qfg2PatchImportDialog }, SCI_SIGNATUREENTRY_TERMINATOR }; // =========================================================================== // Patch for the import screen in QFG3, same as the one for QFG2 above const uint16 qfg3SignatureImportDialog[] = { - 0x63, 0x2a, // pToa text + 0x63, SIG_MAGICDWORD, 0x2a, // pToa text 0x31, 0x0b, // bnt [next state] 0x7a, // push2 0x39, 0x03, // pushi 03 @@ -1326,8 +1431,10 @@ const uint16 qfg3PatchImportDialog[] = { // hero::solvePuzzle (0xfffc) which does a ret afterwards without going to // Teller::doChild. We jump to this call of hero::solvePuzzle to get that same // behaviour. - +// Applies to at least: English, German, Italian, French, Spanish Floppy +// Responsible method: unknown const uint16 qfg3SignatureWooDialog[] = { + SIG_MAGICDWORD, 0x67, 0x12, // pTos 12 (query) 0x35, 0xb6, // ldi b6 0x1a, // eq? @@ -1336,7 +1443,7 @@ const uint16 qfg3SignatureWooDialog[] = { 0x35, 0x9b, // ldi 9b 0x1a, // eq? 0x31, 0x0c, // bnt 0c - 0x38, SIG_UINT16 + 0x97, 0x02, // pushi 0297 + 0x38, SIG_SELECTOR16 + SELECTOR_solvePuzzle, // pushi 0297 0x7a, // push2 0x38, SIG_UINT16 + 0x0c, 0x01, // pushi 010c 0x7a, // push2 @@ -1353,10 +1460,10 @@ const uint16 qfg3PatchWooDialog[] = { PATCH_END }; -// script, description, magic DWORD, adjust -const SciScriptSignature qfg3Signatures[] = { - { 944, "import dialog continuous calls", 1, PATCH_MAGICDWORD(0x2a, 0x31, 0x0b, 0x7a), -1, qfg3SignatureImportDialog, qfg3PatchImportDialog }, - { 440, "dialog crash when asking about Woo", 1, PATCH_MAGICDWORD(0x67, 0x12, 0x35, 0xb5), -26, qfg3SignatureWooDialog, qfg3PatchWooDialog }, +// script, description, signature patch +SciScriptPatcherEntry qfg3Signatures[] = { + { 944, "import dialog continuous calls", 1, 0, 0, qfg3SignatureImportDialog, qfg3PatchImportDialog }, + { 440, "dialog crash when asking about Woo", 1, 0, 0, qfg3SignatureWooDialog, qfg3PatchWooDialog }, SCI_SIGNATUREENTRY_TERMINATOR }; @@ -1369,19 +1476,10 @@ const SciScriptSignature qfg3Signatures[] = { // but I think just patching it out is cleaner (bug #3037938) const uint16 sq4FloppySignatureEndlessFlight[] = { 0x39, 0x04, // pushi 04 (selector x) + SIG_MAGICDWORD, 0x78, // push1 0x67, 0x08, // pTos 08 (property x) - 0x63, 0x44, // pToa 44 (invalid property) - 0x02, // add - SIG_END -}; - -// Similar to the above, for the German version (bug #3110215) -const uint16 sq4FloppySignatureEndlessFlightGerman[] = { - 0x39, 0x04, // pushi 04 (selector x) - 0x78, // push1 - 0x67, 0x08, // pTos 08 (property x) - 0x63, 0x4c, // pToa 4c (invalid property) + 0x63, SIG_ADDTOOFFSET + 1, // pToa (invalid property) - 44h for English floppy, 4ch for German floppy 0x02, // add SIG_END }; @@ -1399,6 +1497,7 @@ const uint16 sq4FloppyPatchEndlessFlight[] = { // This is patched to add the "Both" text resource (i.e. we end up with // "Speech", "Text" and "Both") const uint16 sq4CdSignatureTextOptionsButton[] = { + SIG_MAGICDWORD, 0x35, 0x01, // ldi 0x01 0xa1, 0x53, // sag 0x53 0x39, 0x03, // pushi 0x03 @@ -1418,6 +1517,7 @@ const uint16 sq4CdPatchTextOptionsButton[] = { // (e.g. the two guys from Andromeda) shown when dying/quitting. // Fixes bug #3538418. const uint16 sq4CdSignatureBabbleIcon[] = { + SIG_MAGICDWORD, 0x89, 0x5a, // lsg 5a 0x35, 0x02, // ldi 02 0x1a, // eq? @@ -1438,6 +1538,7 @@ const uint16 sq4CdPatchBabbleIcon[] = { // Refer to the patch above for additional details. // iconTextSwitch::doit (called when the text options button is clicked) const uint16 sq4CdSignatureTextOptions[] = { + SIG_MAGICDWORD, 0x89, 0x5a, // lsg 0x5a (load global 90 to stack) 0x3c, // dup 0x35, 0x01, // ldi 0x01 @@ -1453,7 +1554,7 @@ const uint16 sq4CdSignatureTextOptions[] = { 0x35, 0x01, // ldi 0x01 0xa1, 0x5a, // sag 0x5a (save acc to global 90) 0x3a, // toss - 0x38, SIG_UINT16 + 0xd9, 0x00, // pushi 0x00d9 + 0x38, SIG_SELECTOR16 + SELECTOR_show, // pushi 0x00d9 0x76, // push0 0x54, 0x04, // self 0x04 0x48, // ret @@ -1479,13 +1580,12 @@ const uint16 sq4CdPatchTextOptions[] = { PATCH_END }; -// script, description, magic DWORD, adjust -const SciScriptSignature sq4Signatures[] = { - { 298, "Floppy: endless flight", 1, PATCH_MAGICDWORD(0x67, 0x08, 0x63, 0x44), -3, sq4FloppySignatureEndlessFlight, sq4FloppyPatchEndlessFlight }, - { 298, "Floppy (German): endless flight", 1, PATCH_MAGICDWORD(0x67, 0x08, 0x63, 0x4c), -3, sq4FloppySignatureEndlessFlightGerman, sq4FloppyPatchEndlessFlight }, - { 818, "CD: Speech and subtitles option", 1, PATCH_MAGICDWORD(0x89, 0x5a, 0x3c, 0x35), 0, sq4CdSignatureTextOptions, sq4CdPatchTextOptions }, - { 0, "CD: Babble icon speech and subtitles fix", 1, PATCH_MAGICDWORD(0x89, 0x5a, 0x35, 0x02), 0, sq4CdSignatureBabbleIcon, sq4CdPatchBabbleIcon }, - { 818, "CD: Speech and subtitles option button", 1, PATCH_MAGICDWORD(0x35, 0x01, 0xa1, 0x53), 0, sq4CdSignatureTextOptionsButton, sq4CdPatchTextOptionsButton }, +// script, description, magic DWORD adjust signature patch +SciScriptPatcherEntry sq4Signatures[] = { + { 298, "Floppy: endless flight", 1, 0, 0, sq4FloppySignatureEndlessFlight, sq4FloppyPatchEndlessFlight }, + { 818, "CD: Speech and subtitles option", 1, 0, 0, sq4CdSignatureTextOptions, sq4CdPatchTextOptions }, + { 0, "CD: Babble icon speech and subtitles fix", 1, 0, 0, sq4CdSignatureBabbleIcon, sq4CdPatchBabbleIcon }, + { 818, "CD: Speech and subtitles option button", 1, 0, 0, sq4CdSignatureTextOptionsButton, sq4CdPatchTextOptionsButton }, SCI_SIGNATUREENTRY_TERMINATOR }; @@ -1501,7 +1601,8 @@ const SciScriptSignature sq4Signatures[] = { // We simply set the correct starting cel number to fix the bug. // Responsible method: robotIntoShip::changeState(9) const uint16 sq1vgaSignatureUlenceFlatsTimepodGfxGlitch[] = { - 0x39, 0x07, // pushi 07 (ship::cel) + 0x39, + SIG_MAGICDWORD, SIG_SELECTOR8 + SELECTOR_cel, // pushi "cel" 0x78, // push1 0x39, 0x0a, // pushi 0x0a (set ship::cel to 10) 0x38, SIG_UINT16 + 0xa0, 0x00, // pushi 0x00a0 (ship::setLoop) @@ -1515,9 +1616,10 @@ const uint16 sq1vgaPatchUlenceFlatsTimepodGfxGlitch[] = { }; const uint16 sq1vgaSignatureEgoShowsCard[] = { - 0x38, SIG_UINT16 + 0x46, 0x02, // push 0x246 (set up send frame to set timesShownID) + SIG_MAGICDWORD, + 0x38, SIG_SELECTOR16 + SELECTOR_timesShownID, // push "timesShownID" 0x78, // push1 - 0x38, SIG_UINT16 + 0x46, 0x02, // push 0x246 (set up send frame to get timesShownID) + 0x38, SIG_SELECTOR16 + SELECTOR_timesShownID, // push "timesShownID" 0x76, // push0 0x51, 0x7c, // class DeltaurRegion 0x4a, 0x04, // send 0x04 (get timesShownID) @@ -1536,7 +1638,7 @@ const uint16 sq1vgaSignatureEgoShowsCard[] = { // Note that this script patch is merely a reordering of the // instructions in the original script. const uint16 sq1vgaPatchEgoShowsCard[] = { - 0x38, PATCH_UINT16 + 0x46, 0x02, // push 0x246 (set up send frame to get timesShownID) + 0x38, PATCH_SELECTOR16 + SELECTOR_timesShownID, // push "timesShownID" 0x76, // push0 0x51, 0x7c, // class DeltaurRegion 0x4a, 0x04, // send 0x04 (get timesShownID) @@ -1544,7 +1646,7 @@ const uint16 sq1vgaPatchEgoShowsCard[] = { 0x35, 0x01, // ldi 1 0x02, // add 0x36, // push (this push corresponds to the wrong one above) - 0x38, PATCH_UINT16 + 0x46, 0x02, // push 0x246 (set up send frame to set timesShownID) + 0x38, PATCH_SELECTOR16 + SELECTOR_timesShownID, // push "timesShownID" 0x78, // push1 0x36, // push 0x51, 0x7c, // class DeltaurRegion @@ -1555,18 +1657,19 @@ const uint16 sq1vgaPatchEgoShowsCard[] = { }; -// script, description, magic DWORD, adjust -const SciScriptSignature sq1vgaSignatures[] = { - { 45, "Ulence Flats: timepod graphic glitch", 1, PATCH_MAGICDWORD( 0x07, 0x78, 0x39, 0x0a ), -1, sq1vgaSignatureUlenceFlatsTimepodGfxGlitch, sq1vgaPatchUlenceFlatsTimepodGfxGlitch }, - { 58, "Sarien armory droid zapping ego first time", 1, PATCH_MAGICDWORD( 0x72, 0x88, 0x15, 0x36 ), -70, sq1vgaSignatureEgoShowsCard, sq1vgaPatchEgoShowsCard }, - +// script, description, signature patch +SciScriptPatcherEntry sq1vgaSignatures[] = { + { 45, "Ulence Flats: timepod graphic glitch", 1, 0, 0, sq1vgaSignatureUlenceFlatsTimepodGfxGlitch, sq1vgaPatchUlenceFlatsTimepodGfxGlitch }, + { 58, "Sarien armory droid zapping ego first time", 1, 0, 0, sq1vgaSignatureEgoShowsCard, sq1vgaPatchEgoShowsCard }, SCI_SIGNATUREENTRY_TERMINATOR}; // will actually patch previously found signature area -void Script::applyPatch(const uint16 *patch, byte *scriptData, const uint32 scriptSize, int32 signatureOffset, const bool isMacSci11) { +void Script::patcherApplyPatch(const SciScriptPatcherEntry *patchEntry, byte *scriptData, const uint32 scriptSize, int32 signatureOffset, const bool isMacSci11) { + const uint16 *patchData = patchEntry->patchData; byte orgData[PATCH_VALUELIMIT]; int32 offset = signatureOffset; - uint16 patchWord = *patch; + uint16 patchWord = *patchEntry->patchData; + uint16 patchSelector = 0; // Copy over original bytes from script uint32 orgDataSize = scriptSize - offset; @@ -1575,8 +1678,9 @@ void Script::applyPatch(const uint16 *patch, byte *scriptData, const uint32 scri memcpy(&orgData, &scriptData[offset], orgDataSize); while (patchWord != PATCH_END) { + uint16 patchCommand = patchWord & PATCH_COMMANDMASK; uint16 patchValue = patchWord & PATCH_VALUEMASK; - switch (patchWord & PATCH_COMMANDMASK) { + switch (patchCommand) { case PATCH_ADDTOOFFSET: { // add value to offset offset += patchValue; @@ -1585,7 +1689,7 @@ void Script::applyPatch(const uint16 *patch, byte *scriptData, const uint32 scri case PATCH_GETORIGINALBYTE: { // get original byte from script if (patchValue >= orgDataSize) - error("patching: can not get requested original byte from script"); + error("Script-Patcher: can not get requested original byte from script"); scriptData[offset] = orgData[patchValue]; offset++; break; @@ -1593,38 +1697,37 @@ void Script::applyPatch(const uint16 *patch, byte *scriptData, const uint32 scri case PATCH_GETORIGINALBYTEADJUST: { // get original byte from script and adjust it if (patchValue >= orgDataSize) - error("patching: can not get requested original byte from script"); + error("Script-Patcher: can not get requested original byte from script"); byte orgByte = orgData[patchValue]; int16 adjustValue; - patch++; adjustValue = (int16)(*patch); + patchData++; adjustValue = (int16)(*patchData); scriptData[offset] = orgByte + adjustValue; offset++; break; } - case PATCH_ADJUSTWORD: { - // Adjust word right before current position - byte *adjustPtr = &scriptData[offset - 2]; - uint16 adjustWord = READ_LE_UINT16(adjustPtr); - adjustWord += patchValue; - WRITE_LE_UINT16(adjustPtr, adjustWord); - break; - } - case PATCH_ADJUSTWORD_NEG: { - // Adjust word right before current position (negative way) - byte *adjustPtr = &scriptData[offset - 2]; - uint16 adjustWord = READ_LE_UINT16(adjustPtr); - adjustWord -= patchValue; - WRITE_LE_UINT16(adjustPtr, adjustWord); - break; - } - case PATCH_UINT16: { - byte byte1 = patchValue & PATCH_BYTEMASK; + case PATCH_UINT16: + case PATCH_SELECTOR16: { + byte byte1; byte byte2; - patch++; patchWord = *patch; - if (patchWord & PATCH_COMMANDMASK) { - error("Patch inconsistent"); + + switch (patchCommand) { + case PATCH_UINT16: { + byte1 = patchValue & PATCH_BYTEMASK; + patchData++; patchWord = *patchData; + if (patchWord & PATCH_COMMANDMASK) + error("Script-Patcher: Patch inconsistent"); + byte2 = patchWord & PATCH_BYTEMASK; + break; + } + case PATCH_SELECTOR16: { + patchSelector = selectorTable[patchValue].id; + byte1 = patchSelector & 0xFF; + byte2 = patchSelector >> 8; + break; + } + default: + byte1 = 0; byte2 = 0; } - byte2 = patchWord & PATCH_BYTEMASK; if (!isMacSci11) { scriptData[offset++] = byte1; scriptData[offset++] = byte2; @@ -1635,49 +1738,74 @@ void Script::applyPatch(const uint16 *patch, byte *scriptData, const uint32 scri } break; } - default: + case PATCH_SELECTOR8: { + patchSelector = selectorTable[patchValue].id; + if (patchSelector & 0xFF00) + error("Script-Patcher: 8 bit selector required, game uses 16 bit selector"); + scriptData[offset] = patchSelector & 0xFF; + offset++; + break; + } + case PATCH_BYTE: scriptData[offset] = patchValue & PATCH_BYTEMASK; offset++; } - patch++; - patchWord = *patch; + patchData++; + patchWord = *patchData; } } // will return -1 if no match was found, otherwise an offset to the start of the signature match -int32 Script::findSignature(const SciScriptSignature *signature, const byte *scriptData, const uint32 scriptSize, const bool isMacSci11) { +int32 Script::patcherFindSignature(const SciScriptPatcherEntry *patchEntry, const byte *scriptData, const uint32 scriptSize, const bool isMacSci11) { if (scriptSize < 4) // we need to find a DWORD, so less than 4 bytes is not okay return -1; - const uint32 magicDWord = signature->magicDWord; // is platform-specific BE/LE form, so that the later match will work + const uint32 magicDWord = patchEntry->magicDWord; // is platform-specific BE/LE form, so that the later match will work 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 + signature->magicOffset; + uint32 offset = DWordOffset + patchEntry->magicOffset; uint32 byteOffset = offset; - const uint16 *signatureData = signature->data; - + const uint16 *signatureData = patchEntry->signatureData; + uint16 sigSelector = 0; + uint16 sigWord = *signatureData; while (sigWord != SIG_END) { - uint16 sigValue = sigWord & PATCH_VALUEMASK; - switch (sigWord & SIG_COMMANDMASK) { + uint16 sigCommand = sigWord & SIG_COMMANDMASK; + uint16 sigValue = sigWord & SIG_VALUEMASK; + switch (sigCommand) { case SIG_ADDTOOFFSET: { // add value to offset byteOffset += sigValue; break; } - case PATCH_UINT16: { + case SIG_UINT16: + case SIG_SELECTOR16: { if ((byteOffset + 1) < scriptSize) { - byte byte1 = sigValue & SIG_BYTEMASK; + byte byte1; byte byte2; - signatureData++; sigWord = *signatureData; - if (sigWord & SIG_COMMANDMASK) { - error("Patch inconsistent"); + + switch (sigCommand) { + case SIG_UINT16: { + byte1 = sigValue & SIG_BYTEMASK; + signatureData++; sigWord = *signatureData; + if (sigWord & SIG_COMMANDMASK) + error("Script-Patcher: signature inconsistent\nFaulty patch: '%s'", patchEntry->description); + byte2 = sigWord & SIG_BYTEMASK; + break; + } + case SIG_SELECTOR16: { + sigSelector = selectorTable[sigValue].id; + byte1 = sigSelector & 0xFF; + byte2 = sigSelector >> 8; + break; + } + default: + byte1 = 0; byte2 = 0; } - byte2 = sigWord & SIG_BYTEMASK; if (!isMacSci11) { if ((scriptData[byteOffset] != byte1) || (scriptData[byteOffset + 1] != byte2)) sigWord = SIG_MISMATCH; @@ -1692,15 +1820,27 @@ int32 Script::findSignature(const SciScriptSignature *signature, const byte *scr } break; } - default: + case SIG_SELECTOR8: { + if (byteOffset < scriptSize) { + sigSelector = selectorTable[sigValue].id; + if (sigSelector & 0xFF00) + error("Script-Patcher: 8 bit selector required, game uses 16 bit selector\nFaulty patch: '%s'", patchEntry->description); + if (scriptData[byteOffset] != (sigSelector & 0xFF)) + sigWord = SIG_MISMATCH; + byteOffset++; + } else { + sigWord = SIG_MISMATCH; // out of bounds + } + break; + } + case SIG_BYTE: if (byteOffset < scriptSize) { if (scriptData[byteOffset] != sigWord) sigWord = SIG_MISMATCH; + byteOffset++; } else { - // out of bounds - sigWord = SIG_MISMATCH; + sigWord = SIG_MISMATCH; // out of bounds } - byteOffset++; } if (sigWord == SIG_MISMATCH) @@ -1719,8 +1859,139 @@ int32 Script::findSignature(const SciScriptSignature *signature, const byte *scr return -1; } -void Script::matchSignatureAndPatch(uint16 scriptNr, byte *scriptData, const uint32 scriptSize) { - const SciScriptSignature *signatureTable = NULL; +// 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 Script::patcherInitSignature(SciScriptPatcherEntry *patchTable, bool isMacSci11) { + SciScriptPatcherEntry *curEntry = patchTable; + SciScriptPatcherSelector *curSelector = NULL; + int step; + int magicOffset; + byte magicDWord[4]; + int magicDWordLeft = 0; + const uint16 *curData; + uint16 curWord; + uint16 curCommand; + uint32 curValue; + byte byte1; + byte byte2; + + while (curEntry->signatureData) { + // process signature + memset(magicDWord, 0, sizeof(magicDWord)); + + 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 ((curEntry->magicDWord) || (magicDWordLeft)) + error("Script-Patcher: Magic-DWORD specified multiple times in signature\nFaulty patch: '%s'", curEntry->description); + magicDWordLeft = 4; + curEntry->magicOffset = magicOffset; + } + break; + } + case SIG_ADDTOOFFSET: { + magicOffset -= curValue; + if (magicDWordLeft) + error("Script-Patcher: Magic-DWORD contains AddToOffset command\nFaulty patch: '%s'", curEntry->description); + break; + } + case SIG_UINT16: + case SIG_SELECTOR16: { + // UINT16 or 1 + switch (curCommand) { + case SIG_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_SELECTOR16: { + curSelector = &selectorTable[curValue]; + if (curSelector->id == -1) + curSelector->id = g_sci->getKernel()->findSelector(curSelector->name); + if (!isMacSci11) { + byte1 = curSelector->id & 0x00FF; + byte2 = curSelector->id >> 8; + } else { + byte1 = curSelector->id >> 8; + byte2 = curSelector->id & 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) { + curEntry->magicDWord = READ_LE_UINT32(magicDWord); + } + } + break; + } + case SIG_BYTE: + case SIG_SELECTOR8: { + if (curCommand == SIG_SELECTOR8) { + curSelector = &selectorTable[curValue]; + if (curSelector->id == -1) { + curSelector->id = g_sci->getKernel()->findSelector(curSelector->name); + if (curSelector->id != -1) { + if (curSelector->id & 0xFF00) + error("Script-Patcher: 8 bit selector required, game uses 16 bit selector\nFaulty patch: '%s'", curEntry->description); + } + } + curValue = curSelector->id; + } + magicOffset--; + if (magicDWordLeft) { + // Remember current byte for Magic DWORD + magicDWord[4 - magicDWordLeft] = (byte)curValue; + magicDWordLeft--; + if (!magicDWordLeft) { + curEntry->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 (!curEntry->magicDWord) + error("Script-Patcher: Magic-DWORD not specified in signature\nFaulty patch: '%s'", curEntry->description); + + curEntry++; + } +} + +void Script::patcherProcessScript(uint16 scriptNr, byte *scriptData, const uint32 scriptSize) { + SciScriptPatcherEntry *signatureTable = NULL; + switch (g_sci->getGameId()) { case GID_CAMELOT: signatureTable = camelotSignatures; @@ -1789,17 +2060,22 @@ void Script::matchSignatureAndPatch(uint16 scriptNr, byte *scriptData, const uin if (signatureTable) { bool isMacSci11 = (g_sci->getPlatform() == Common::kPlatformMacintosh && getSciVersion() >= SCI_VERSION_1_1); + + if (!signatureTable->magicDWord) { + // signature table needs to get initialized (Magic DWORD set, selector table set) + patcherInitSignature(signatureTable, isMacSci11); + } - while (signatureTable->data) { + while (signatureTable->signatureData) { if (scriptNr == signatureTable->scriptNr) { int32 foundOffset = 0; int16 applyCount = signatureTable->applyCount; do { - foundOffset = findSignature(signatureTable, scriptData, scriptSize, isMacSci11); + foundOffset = patcherFindSignature(signatureTable, scriptData, scriptSize, isMacSci11); if (foundOffset != -1) { // found, so apply the patch - debugC(kDebugLevelScriptPatcher, "matched and patched %s on script %d offset %d", signatureTable->description, scriptNr, foundOffset); - applyPatch(signatureTable->patch, scriptData, scriptSize, foundOffset, isMacSci11); + debugC(kDebugLevelScriptPatcher, "Script-Patcher: '%s' on script %d offset %d", signatureTable->description, scriptNr, foundOffset); + patcherApplyPatch(signatureTable, scriptData, scriptSize, foundOffset, isMacSci11); } applyCount--; } while ((foundOffset != -1) && (applyCount)); |