diff options
Diffstat (limited to 'engines/sci/engine')
-rw-r--r-- | engines/sci/engine/features.cpp | 6 | ||||
-rw-r--r-- | engines/sci/engine/kernel.cpp | 12 | ||||
-rw-r--r-- | engines/sci/engine/kernel.h | 4 | ||||
-rw-r--r-- | engines/sci/engine/kernel_tables.h | 2 | ||||
-rw-r--r-- | engines/sci/engine/kfile.cpp | 2 | ||||
-rw-r--r-- | engines/sci/engine/kpathing.cpp | 2 | ||||
-rw-r--r-- | engines/sci/engine/kstring.cpp | 2 | ||||
-rw-r--r-- | engines/sci/engine/kvideo.cpp | 4 | ||||
-rw-r--r-- | engines/sci/engine/savegame.cpp | 7 | ||||
-rw-r--r-- | engines/sci/engine/savegame.h | 3 | ||||
-rw-r--r-- | engines/sci/engine/script.cpp | 2 | ||||
-rw-r--r-- | engines/sci/engine/script.h | 10 | ||||
-rw-r--r-- | engines/sci/engine/script_patches.cpp | 2686 | ||||
-rw-r--r-- | engines/sci/engine/seg_manager.cpp | 10 | ||||
-rw-r--r-- | engines/sci/engine/seg_manager.h | 2 | ||||
-rw-r--r-- | engines/sci/engine/vm.cpp | 17 | ||||
-rw-r--r-- | engines/sci/engine/vm_types.cpp | 32 | ||||
-rw-r--r-- | engines/sci/engine/vm_types.h | 2 | ||||
-rw-r--r-- | engines/sci/engine/workarounds.cpp | 33 | ||||
-rw-r--r-- | engines/sci/engine/workarounds.h | 1 |
20 files changed, 1965 insertions, 874 deletions
diff --git a/engines/sci/engine/features.cpp b/engines/sci/engine/features.cpp index 6005ac50be..c26c787fbd 100644 --- a/engines/sci/engine/features.cpp +++ b/engines/sci/engine/features.cpp @@ -467,9 +467,9 @@ bool GameFeatures::autoDetectSci21KernelType() { // seen it happen in the RAMA demo, thus we can assume that the // game is using a SCI2.1 table - // HACK: The Inside the Chest Demo doesn't have sounds at all, but - // it's using a SCI2 kernel - if (g_sci->getGameId() == GID_CHEST) { + // HACK: The Inside the Chest Demo and King's Questions minigame + // don't have sounds at all, but they're using a SCI2 kernel + if (g_sci->getGameId() == GID_CHEST || g_sci->getGameId() == GID_KQUESTIONS) { _sci21KernelType = SCI_VERSION_2; return true; } diff --git a/engines/sci/engine/kernel.cpp b/engines/sci/engine/kernel.cpp index 8d55790ad2..12746e17d6 100644 --- a/engines/sci/engine/kernel.cpp +++ b/engines/sci/engine/kernel.cpp @@ -35,8 +35,6 @@ namespace Sci { Kernel::Kernel(ResourceManager *resMan, SegManager *segMan) : _resMan(resMan), _segMan(segMan), _invalid("<invalid>") { - loadSelectorNames(); - mapSelectors(); // Map a few special selectors for later use } Kernel::~Kernel() { @@ -53,6 +51,11 @@ Kernel::~Kernel() { } } +void Kernel::init() { + loadSelectorNames(); + mapSelectors(); // Map a few special selectors for later use +} + uint Kernel::getSelectorNamesSize() const { return _selectorNames.size(); } @@ -104,6 +107,11 @@ int Kernel::findSelector(const char *selectorName) const { return -1; } +// used by Script patcher to figure out, if it's okay to initialize signature/patch-table +bool Kernel::selectorNamesAvailable() { + return !_selectorNames.empty(); +} + void Kernel::loadSelectorNames() { Resource *r = _resMan->findResource(ResourceId(kResourceTypeVocab, VOCAB_RESOURCE_SELECTORS), 0); bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY); diff --git a/engines/sci/engine/kernel.h b/engines/sci/engine/kernel.h index 8a021073fc..69c3a6d0c9 100644 --- a/engines/sci/engine/kernel.h +++ b/engines/sci/engine/kernel.h @@ -145,6 +145,8 @@ public: */ Kernel(ResourceManager *resMan, SegManager *segMan); ~Kernel(); + + void init(); uint getSelectorNamesSize() const; const Common::String &getSelectorName(uint selector); @@ -159,6 +161,8 @@ public: * @return The appropriate selector ID, or -1 on error */ int findSelector(const char *selectorName) const; + + bool selectorNamesAvailable(); // Script dissection/dumping functions void dissectScript(int scriptNumber, Vocabulary *vocab); diff --git a/engines/sci/engine/kernel_tables.h b/engines/sci/engine/kernel_tables.h index d7858180f1..39244bd760 100644 --- a/engines/sci/engine/kernel_tables.h +++ b/engines/sci/engine/kernel_tables.h @@ -421,7 +421,7 @@ static SciKernelMapEntry s_kernelMap[] = { { MAP_CALL(PrevNode), SIG_EVERYWHERE, "n", NULL, NULL }, { MAP_CALL(PriCoord), SIG_EVERYWHERE, "i", NULL, NULL }, { MAP_CALL(Random), SIG_EVERYWHERE, "i(i)(i)", NULL, NULL }, - { MAP_CALL(ReadNumber), SIG_EVERYWHERE, "r", NULL, NULL }, + { MAP_CALL(ReadNumber), SIG_EVERYWHERE, "r", NULL, kReadNumber_workarounds }, { MAP_CALL(RemapColors), SIG_SCI11, SIGFOR_ALL, "i(i)(i)(i)(i)", NULL, NULL }, #ifdef ENABLE_SCI32 { "RemapColors", kRemapColors32, SIG_SCI32, SIGFOR_ALL, "i(i)(i)(i)(i)(i)", NULL, NULL }, diff --git a/engines/sci/engine/kfile.cpp b/engines/sci/engine/kfile.cpp index c6635f2f27..b940eca6f5 100644 --- a/engines/sci/engine/kfile.cpp +++ b/engines/sci/engine/kfile.cpp @@ -718,7 +718,7 @@ reg_t kSave(EngineState *s, int argc, reg_t *argv) { reg_t kSaveGame(EngineState *s, int argc, reg_t *argv) { Common::String game_id; - int16 virtualId = argv[1].toSint16(); + int16 virtualId = argv[1].toSint16(); int16 savegameId = -1; Common::String game_description; Common::String version; diff --git a/engines/sci/engine/kpathing.cpp b/engines/sci/engine/kpathing.cpp index 64793efa6c..3c223bebbe 100644 --- a/engines/sci/engine/kpathing.cpp +++ b/engines/sci/engine/kpathing.cpp @@ -1966,7 +1966,7 @@ static bool isVertexCovered(const Patch &p, unsigned int wi) { // ---w1--1----p----w2--2---- // ^ \ (inside) if (wi > p.indexw1 && wi <= p.indexw2) - return true; + return true; // v / (outside) // ---w2--2----p----w1--1---- diff --git a/engines/sci/engine/kstring.cpp b/engines/sci/engine/kstring.cpp index 15a9f54996..d72b1d1772 100644 --- a/engines/sci/engine/kstring.cpp +++ b/engines/sci/engine/kstring.cpp @@ -551,7 +551,7 @@ reg_t kMessage(EngineState *s, int argc, reg_t *argv) { // NOTE: To fix a corrupted jar object, type "send Glass_Jar message 52" // in the debugger. if (g_sci->getGameId() == GID_PEPPER && func == 0 && argc >= 6 && module == 894 && - tuple.noun == 26 && tuple.cond == 0 && tuple.seq == 1 && + tuple.noun == 26 && tuple.cond == 0 && tuple.seq == 1 && !s->_msgState->getMessage(module, tuple, NULL_REG)) tuple.verb = 0; diff --git a/engines/sci/engine/kvideo.cpp b/engines/sci/engine/kvideo.cpp index 9b0cb38f51..3964ccc1f8 100644 --- a/engines/sci/engine/kvideo.cpp +++ b/engines/sci/engine/kvideo.cpp @@ -103,10 +103,10 @@ void playVideo(Video::VideoDecoder *videoDecoder, VideoState videoState) { if (frame) { if (scaleBuffer) { // TODO: Probably should do aspect ratio correction in e.g. GK1 Windows - g_sci->_gfxScreen->scale2x((byte *)frame->pixels, scaleBuffer, videoDecoder->getWidth(), videoDecoder->getHeight(), bytesPerPixel); + g_sci->_gfxScreen->scale2x((const byte *)frame->getPixels(), scaleBuffer, videoDecoder->getWidth(), videoDecoder->getHeight(), bytesPerPixel); g_system->copyRectToScreen(scaleBuffer, pitch, x, y, width, height); } else { - g_system->copyRectToScreen(frame->pixels, frame->pitch, x, y, width, height); + g_system->copyRectToScreen(frame->getPixels(), frame->pitch, x, y, width, height); } if (videoDecoder->hasDirtyPalette()) { diff --git a/engines/sci/engine/savegame.cpp b/engines/sci/engine/savegame.cpp index c8076ec819..6955225fe5 100644 --- a/engines/sci/engine/savegame.cpp +++ b/engines/sci/engine/savegame.cpp @@ -465,7 +465,7 @@ void Script::syncStringHeap(Common::Serializer &s) { break; } while (1); - } else if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1){ + } else if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1){ // Strings in SCI1.1 come after the object instances byte *buf = _heapStart + 4 + READ_SCI11ENDIAN_UINT16(_heapStart + 2) * 2; @@ -600,7 +600,10 @@ void MusicEntry::saveLoadWithSerializer(Common::Serializer &s) { s.syncAsSint16LE(dataInc); s.syncAsSint16LE(ticker); s.syncAsSint16LE(signal, VER(17)); - s.syncAsByte(priority); + if (s.getVersion() >= 31) // FE sound/music.h -> priority + s.syncAsSint16LE(priority); + else + s.syncAsByte(priority); s.syncAsSint16LE(loop, VER(17)); s.syncAsByte(volume); s.syncAsByte(hold, VER(17)); diff --git a/engines/sci/engine/savegame.h b/engines/sci/engine/savegame.h index 1d899b0d37..f1f02f89f2 100644 --- a/engines/sci/engine/savegame.h +++ b/engines/sci/engine/savegame.h @@ -37,6 +37,7 @@ struct EngineState; * * Version - new/changed feature * ============================= + * 31 - priority for sound effects/music is now a signed int16, instead of a byte * 30 - synonyms * 29 - system strings * 28 - heap @@ -55,7 +56,7 @@ struct EngineState; */ enum { - CURRENT_SAVEGAME_VERSION = 30, + CURRENT_SAVEGAME_VERSION = 31, MINIMUM_SAVEGAME_VERSION = 14 }; 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 0b499203c2..6a27dc7f64 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,11 @@ 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); - void applyPatch(const uint16 *patch, byte *scriptData, const uint32 scriptSize, int32 signatureOffset); + void patcherProcessScript(uint16 scriptNr, byte *scriptData, const uint32 scriptSize); + void patcherInitSignature(SciScriptPatcherEntry *patchTable, bool isMacSci11); + void patcherEnablePatch(SciScriptPatcherEntry *patchTable, const char *searchDescription); + 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 20c5c52178..6293fb42ae 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" @@ -29,98 +30,257 @@ namespace Sci { -#define PATCH_END 0xFFFF -#define PATCH_COMMANDMASK 0xF000 -#define PATCH_VALUEMASK 0x0FFF -#define PATCH_ADDTOOFFSET 0xE000 -#define PATCH_GETORIGINALBYTE 0xD000 -#define PATCH_ADJUSTWORD 0xC000 -#define PATCH_ADJUSTWORD_NEG 0xB000 -#define PATCH_MAGICDWORD(a, b, c, d) CONSTANT_LE_32(a | (b << 8) | (c << 16) | (d << 24)) +// IMPORTANT: +// every patch entry needs the following: +// - script number (pretty obvious) +// +// - apply count +// specifies the number of times a patch is supposed to get applied. +// Most of the time, it should be 1. +// +// - magicDWORD + magicOffset +// please ALWAYS put 0 for those two. Both will get filled out at runtime by the patcher. +// +// - signature data (is used to identify certain script code, that needs patching) +// every signature needs to contain SIG_MAGICDWORD once. +// The following 4 bytes after SIG_MAGICDWORD - which don't have to be fixed, you may for example +// use SIG_SELECTOR16, will get used to quickly search for a partly match before verifying that +// the whole signature actually matches. If it's not included, the script patcher will error() out +// right when loading up the game. +// If selector-IDs are included, please use SIG_SELECTOR16 + SIG_SELECTOR8 [1]. Simply +// specify the selector that way, so that the patcher will search for the specific +// selector instead of looking for a hardcoded value. Selectors may not be the same +// between game versions. +// For UINT16s either use SIG_UINT16 or SIG_SELECTOR16. +// Macintosh versions of SCI games are using BE ordering instead of LE since SCI1.1 for UINT16s in scripts +// By using those 2 commands, it's possible to make patches work for PC and Mac versions of the same game. +// You may also skip bytes by using the SIG_ADDTOOFFSET command +// Every signature data needs to get terminated using SIGNATURE_END +// +// - patch data (is used for actually patching scripts) +// When a match is found, the patch data will get applied. +// Patch data is similar to signature data. Just use PATCH_SELECTOR16 + PATCH_SELECTOR8 [1] +// for patching in selectors. +// There are also patch specific commands. +// Those are PATCH_GETORIGINALBYTE, which fetches a byte from the original script +// and PATCH_GETORIGINALBYTEADJUST, which does the same but gets a second value +// from the uint16 array and uses that value to adjust the original byte. +// Every patch data needs to get terminated using PATCH_END +// +// - and please always add a comment about why the patch was done and what's causing issues. +// If possible make sure, that the patch works on localized (or just different) game versions +// as well in case those need patching too. +// +// [1] - selectors need to get specified in selectorTable[] and ScriptPatcherSelectors-enum +// before they can get used using the SIG_SELECTORx and PATCH_SELECTORx commands. +// You have to use the exact same order in both the table and the enum, otherwise +// it won't work. + +#define SIG_END 0xFFFF +#define SIG_MISMATCH 0xFFFE +#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 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_SELECTOR16 SIG_SELECTOR16 +#define PATCH_SELECTOR8 SIG_SELECTOR8 +#define PATCH_UINT16 SIG_UINT16 +#define PATCH_BYTE SIG_BYTE + +// defines maximum scratch area for getting original bytes from unpatched script data #define PATCH_VALUELIMIT 4096 -struct SciScriptSignature { +struct SciScriptPatcherEntry { + bool active; uint16 scriptNr; const char *description; int16 applyCount; uint32 magicDWord; int magicOffset; - const byte *data; - const uint16 *patch; + const uint16 *signatureData; + const uint16 *patchData; +}; + +#define SCI_SIGNATUREENTRY_TERMINATOR { false, 0, NULL, 0, 0, 0, NULL, NULL } + +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 + { "startText", -1, }, // King's Quest 6 CD / Laura Bow 2 CD for audio+text support + { "startAudio", -1, }, // King's Quest 6 CD / Laura Bow 2 CD for audio+text support + { "modNum", -1, }, // King's Quest 6 CD / Laura Bow 2 CD for audio+text support + { 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, + SELECTOR_startText, + SELECTOR_startAudio, + SELECTOR_modNum }; -#define SCI_SIGNATUREENTRY_TERMINATOR { 0, NULL, 0, 0, 0, NULL, NULL } +// =========================================================================== +// Conquests of Camelot +// At the bazaar in Jerusalem, it's possible to see a girl taking a shower. +// If you get too close, you get warned by the father - if you don't get away, +// he will kill you. +// Instead of walking there manually, it's also possible to enter "look window" +// and ego will automatically walk to the window. It seems that this is something +// that wasn't properly implemented, because instead of getting killed, you will +// get an "Oops" message in Sierra SCI. +// +// This is caused by peepingTom in script 169 not getting properly initialized. +// peepingTom calls the object behind global b9h. This global variable is +// properly initialized, when walking there manually (method fawaz::doit). +// When you instead walk there automatically (method fawaz::handleEvent), that +// global isn't initialized, which then results in the Oops-message in Sierra SCI +// and an error message in ScummVM/SCI. +// +// We fix the script by patching in a jump to the proper code inside fawaz::doit. +// Responsible method: fawaz::handleEvent +// Fixes bug: #6402 +const uint16 camelotSignaturePeepingTom[] = { + 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 + 0x78, // push1 + 0x7a, // push2 + 0x38, SIG_UINT16 + 0xa9, 0x00, // pushi 00a9 - script 169 + 0x78, // push1 + 0x43, 0x02, 0x04, // call kScriptID + 0x36, // push + 0x81, 0x00, // lag 00 + 0x4a, 0x06, // send 06 + 0x32, SIG_UINT16 + 0x20, 0x05, // jmp [end of fawaz::handleEvent] + SIG_END +}; + +const uint16 camelotPatchPeepingTom[] = { + PATCH_ADDTOOFFSET +576, + 0x32, PATCH_UINT16 + 0xbd, 0xfd, // jmp to fawaz::doit / properly init peepingTom code + PATCH_END +}; -// 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 +// script, description, signature patch +SciScriptPatcherEntry camelotSignatures[] = { + { true, 62, "fix peepingTom Sierra bug", 1, 0, 0, camelotSignaturePeepingTom, camelotPatchPeepingTom }, + SCI_SIGNATUREENTRY_TERMINATOR +}; // =========================================================================== // stayAndHelp::changeState (0) is called when ego swims to the left or right // 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 -const byte ecoquest1SignatureStayAndHelp[] = { - 40, - 0x3f, 0x01, // link 01 - 0x87, 0x01, // lap param[1] - 0x65, 0x14, // aTop state - 0x36, // push - 0x3c, // dup - 0x35, 0x00, // ldi 00 - 0x1a, // eq? - 0x31, 0x1c, // bnt [next state] - 0x76, // push0 - 0x45, 0x01, 0x00, // callb export1 from script 0 (switching control off) - 0x38, 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, 0x04, // pushi 04 - 0x76, // push0 - 0x76, // push0 - 0x39, 0x17, // pushi 17 - 0x7c, // pushSelf - 0x51, 0x82, // class EcoNarrator - 0x4a, 0x0c, // send 0c - call EcoNarrator::init(0, 0, 23, self) (BADLY BROKEN!) - 0x33, // jmp [end] - 0 +// This also happens in sierra sci +// Applies to at least: PC-CD +// Responsible method: stayAndHelp::changeState +// Fixes bug: #5107 +const uint16 ecoquest1SignatureStayAndHelp[] = { + 0x3f, 0x01, // link 01 + 0x87, 0x01, // lap param[1] + 0x65, 0x14, // aTop state + 0x36, // push + 0x3c, // dup + 0x35, 0x00, // ldi 00 + 0x1a, // eq? + 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, SIG_SELECTOR8 + SELECTOR_init, // pushi "init" + 0x39, 0x04, // pushi 04 + 0x76, // push0 + 0x76, // push0 + 0x39, 0x17, // pushi 17 + 0x7c, // pushSelf + 0x51, 0x82, // class EcoNarrator + 0x4a, 0x0c, // send 0c - call EcoNarrator::init(0, 0, 23, self) (BADLY BROKEN!) + 0x33, // jmp [end] + SIG_END }; const uint16 ecoquest1PatchStayAndHelp[] = { - 0x87, 0x01, // lap param[1] - 0x65, 0x14, // aTop state - 0x36, // push - 0x2f, 0x22, // bt [next state] (this optimization saves 6 bytes) - 0x39, 0x00, // pushi 0 (wasting 1 byte here) - 0x45, 0x01, 0x00, // callb export1 from script 0 (switching control off) - 0x38, 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, 0x06, // pushi 06 - 0x39, 0x02, // pushi 02 (additional 2 bytes) - 0x76, // push0 - 0x76, // push0 - 0x39, 0x17, // pushi 17 - 0x7c, // pushSelf - 0x38, 0x80, 0x02, // pushi 280 (additional 3 bytes) - 0x51, 0x82, // class EcoNarrator - 0x4a, 0x10, // send 10 - call EcoNarrator::init(2, 0, 0, 23, self, 640) + 0x87, 0x01, // lap param[1] + 0x65, 0x14, // aTop state + 0x36, // push + 0x2f, 0x22, // bt [next state] (this optimization saves 6 bytes) + 0x39, 0x00, // pushi 0 (wasting 1 byte here) + 0x45, 0x01, 0x00, // callb export1 from script 0 (switching control off) + 0x38, PATCH_UINT16 + 0x22, 0x01, // pushi 0122 + 0x78, // push1 + 0x76, // push0 + 0x81, 0x00, // lag global[0] + 0x4a, 0x06, // send 06 - call ego::setMotion(0) + 0x39, PATCH_SELECTOR8 + SELECTOR_init, // pushi "init" + 0x39, 0x06, // pushi 06 + 0x39, 0x02, // pushi 02 (additional 2 bytes) + 0x76, // push0 + 0x76, // push0 + 0x39, 0x17, // pushi 17 + 0x7c, // pushSelf + 0x38, PATCH_UINT16 + 0x80, 0x02, // pushi 280 (additional 3 bytes) + 0x51, 0x82, // class EcoNarrator + 0x4a, 0x10, // send 10 - call EcoNarrator::init(2, 0, 0, 23, self, 640) 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[] = { + { true, 660, "CD: bad messagebox and freeze", 1, 0, 0, ecoquest1SignatureStayAndHelp, ecoquest1PatchStayAndHelp }, SCI_SIGNATUREENTRY_TERMINATOR }; @@ -129,122 +289,124 @@ const SciScriptSignature ecoquest1Signatures[] = { // ecorder. This is done by reusing temp-space, that was filled on state 1. // this worked in sierra sci just by accident. In our sci, the temp space // is resetted every time, which means the previous text isn't available -// anymore. We have to patch the code because of that - bug #3035386 -const byte ecoquest2SignatureEcorder[] = { - 35, - 0x31, 0x22, // bnt [next state] - 0x39, 0x0a, // pushi 0a - 0x5b, 0x04, 0x1e, // lea temp[1e] - 0x36, // push - 0x39, 0x64, // pushi 64 - 0x39, 0x7d, // pushi 7d - 0x39, 0x32, // pushi 32 - 0x39, 0x66, // pushi 66 - 0x39, 0x17, // pushi 17 - 0x39, 0x69, // pushi 69 - 0x38, 0x31, 0x26, // pushi 2631 - 0x39, 0x6a, // pushi 6a - 0x39, 0x64, // pushi 64 - 0x43, 0x1b, 0x14, // call kDisplay - 0x35, 0x0a, // ldi 0a - 0x65, 0x20, // aTop ticks - 0x33, // jmp [end] - +1, 5, // [skip 1 byte] - 0x3c, // dup - 0x35, 0x03, // ldi 03 - 0x1a, // eq? - 0x31, // bnt [end] - 0 +// anymore. We have to patch the code because of that. +// Fixes bug: #4993 +const uint16 ecoquest2SignatureEcorder[] = { + 0x31, 0x22, // bnt [next state] + 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 + 0x39, 0x66, // pushi 66 + 0x39, 0x17, // pushi 17 + 0x39, 0x69, // pushi 69 + 0x38, PATCH_UINT16 + 0x31, 0x26, // pushi 2631 + 0x39, 0x6a, // pushi 6a + 0x39, 0x64, // pushi 64 + 0x43, 0x1b, 0x14, // call kDisplay + 0x35, 0x0a, // ldi 0a + 0x65, 0x20, // aTop ticks + 0x33, // jmp [end] + SIG_ADDTOOFFSET +1, // [skip 1 byte] + 0x3c, // dup + 0x35, 0x03, // ldi 03 + 0x1a, // eq? + 0x31, // bnt [end] + SIG_END }; const uint16 ecoquest2PatchEcorder[] = { - 0x2f, 0x02, // bt [to pushi 07] - 0x3a, // toss - 0x48, // ret - 0x38, 0x07, 0x00, // pushi 07 (parameter count) (waste 1 byte) - 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) - 0x38, 0x17, 0x00, // pushi 17 (color) (waste 1 byte) - 0x43, 0x6c, 0x0e, // call kGraph - 0x38, 0x05, 0x00, // pushi 05 (parameter count) (waste 1 byte) - 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 + 0x2f, 0x02, // bt [to pushi 07] + 0x3a, // toss + 0x48, // ret + 0x38, PATCH_UINT16 + 0x07, 0x00, // pushi 07 (parameter count) (waste 1 byte) + 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) + 0x38, PATCH_UINT16 + 0x17, 0x00, // pushi 17 (color) (waste 1 byte) + 0x43, 0x6c, 0x0e, // call kGraph + 0x38, PATCH_UINT16 + 0x05, 0x00, // pushi 05 (parameter count) (waste 1 byte) + 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 PATCH_END }; // =========================================================================== -// Same patch as above for the ecorder introduction. Fixes bug #3092115. +// Same patch as above for the ecorder introduction. // Two workarounds are needed for this patch in workarounds.cpp (when calling // kGraphFillBoxAny and kGraphUpdateBox), as there isn't enough space to patch // the function otherwise. -const byte ecoquest2SignatureEcorderTutorial[] = { - 36, - 0x30, 0x23, 0x00, // bnt [next state] - 0x39, 0x0a, // pushi 0a - 0x5b, 0x04, 0x1f, // lea temp[1f] - 0x36, // push - 0x39, 0x64, // pushi 64 - 0x39, 0x7d, // pushi 7d - 0x39, 0x32, // pushi 32 - 0x39, 0x66, // pushi 66 - 0x39, 0x17, // pushi 17 - 0x39, 0x69, // pushi 69 - 0x38, 0x31, 0x26, // pushi 2631 - 0x39, 0x6a, // pushi 6a - 0x39, 0x64, // pushi 64 - 0x43, 0x1b, 0x14, // call kDisplay - 0x35, 0x1e, // ldi 1e - 0x65, 0x20, // aTop ticks - 0x32, // jmp [end] +// Fixes bug: #6467 +const uint16 ecoquest2SignatureEcorderTutorial[] = { + 0x30, SIG_UINT16 + 0x23, 0x00, // bnt [next state] + 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 + 0x39, 0x66, // pushi 66 + 0x39, 0x17, // pushi 17 + 0x39, 0x69, // pushi 69 + 0x38, SIG_UINT16 + 0x31, 0x26, // pushi 2631 + 0x39, 0x6a, // pushi 6a + 0x39, 0x64, // pushi 64 + 0x43, 0x1b, 0x14, // call kDisplay + 0x35, 0x1e, // ldi 1e + 0x65, 0x20, // aTop ticks + 0x32, // jmp [end] // 2 extra bytes, jmp offset - 0 + SIG_END }; 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[] = { + { true, 50, "initial text not removed on ecorder", 1, 0, 0, ecoquest2SignatureEcorder, ecoquest2PatchEcorder }, + { true, 333, "initial text not removed on ecorder tutorial",1, 0, 0, ecoquest2SignatureEcorderTutorial, ecoquest2PatchEcorderTutorial }, SCI_SIGNATUREENTRY_TERMINATOR }; @@ -253,27 +415,27 @@ const SciScriptSignature ecoquest2Signatures[] = { // wrong address when an incorrect word is typed, therefore leading to an // infinite loop. This script bug was not apparent in SSCI, probably because // event handling was slightly different there, so it was never discovered. -// Fixes bug #3038870. -const byte fanmadeSignatureInfiniteLoop[] = { - 13, - 0x38, 0x4c, 0x00, // pushi 004c - 0x39, 0x00, // pushi 00 - 0x87, 0x01, // lap 01 - 0x4b, 0x04, // send 04 - 0x18, // not - 0x30, 0x2f, 0x00, // bnt 002f [06a5] --> jmp ffbc [0664] --> BUG! infinite loop - 0 +// Fixes bug: #5120 +const uint16 fanmadeSignatureInfiniteLoop[] = { + 0x38, SIG_UINT16 + 0x4c, 0x00, // pushi 004c + 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 }; const uint16 fanmadePatchInfiniteLoop[] = { PATCH_ADDTOOFFSET | +10, - 0x30, 0x32, 0x00, // bnt 0032 [06a8] --> pushi 004c + 0x30, SIG_UINT16 + 0x32, 0x00, // bnt 0032 [06a8] --> pushi 004c 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[] = { + { true, 999, "infinite loop on typo", 1, 0, 0, fanmadeSignatureInfiniteLoop, fanmadePatchInfiniteLoop }, SCI_SIGNATUREENTRY_TERMINATOR }; @@ -285,21 +447,23 @@ 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 -const byte freddypharkasSignatureScoreDisposal[] = { - 10, - 0x67, 0x32, // pTos 32 (selector theAudCount) - 0x78, // push1 - 0x39, 0x0d, // pushi 0d - 0x43, 0x75, 0x02, // call kDoAudio - 0x1c, // ne? - 0x31, // bnt (-> to skip disposal) - 0 +// Applies to at least: English PC-CD +// Responsible method: unknown +const uint16 freddypharkasSignatureScoreDisposal[] = { + 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 }; const uint16 freddypharkasPatchScoreDisposal[] = { - 0x34, 0x00, 0x00, // ldi 0000 - 0x34, 0x00, 0x00, // ldi 0000 - 0x34, 0x00, 0x00, // ldi 0000 + 0x34, PATCH_UINT16 + 0x00, 0x00, // ldi 0000 + 0x34, PATCH_UINT16 + 0x00, 0x00, // ldi 0000 + 0x34, PATCH_UINT16 + 0x00, 0x00, // ldi 0000 PATCH_END }; @@ -309,24 +473,26 @@ 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. -const byte freddypharkasSignatureCanisterHang[] = { - 12, - 0x38, 0xf1, 0x00, // pushi f1 (selector disable) - 0x7a, // push2 - 0x39, 0x07, // pushi 07 - 0x39, 0x08, // pushi 08 - 0x81, 0x45, // lag 45 - 0x4a, 0x08, // send 08 - call IconBar::disable(7, 8) - 0 +// Applies to at least: English PC-CD +// Responsible method: rm235::init and sEnterFrom500::changeState +const uint16 freddypharkasSignatureCanisterHang[] = { + 0x38, SIG_SELECTOR16 + SELECTOR_disable, // pushi disable + 0x7a, // push2 + SIG_MAGICDWORD, + 0x39, 0x07, // pushi 07 + 0x39, 0x08, // pushi 08 + 0x81, 0x45, // lag 45 + 0x4a, 0x08, // send 08 - call IconBar::disable(7, 8) + SIG_END }; 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 }; @@ -338,135 +504,132 @@ 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. -const byte freddypharkasSignatureLadderEvent[] = { - 21, - 0x39, 0x6d, // pushi 6d (selector new) - 0x76, // push0 - 0x38, 0xf5, 0x00, // pushi f5 (selector 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, 0x94, 0x00, // pushi 94 (selector localize) - 0x76, // push0 - 0x4a, 0x04, // send 04 - call curEvent::localize - 0 +// Applies to at least: English PC-CD, German Floppy, English Mac +// Responsible method: lowerLadder::doit and highLadder::doit +const uint16 freddypharkasSignatureLadderEvent[] = { + 0x39, SIG_MAGICDWORD, + SIG_SELECTOR8 + SELECTOR_new, // pushi new + 0x76, // push0 + 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_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. -const byte freddypharkasSignatureMacInventory[] = { - 10, - 0x39, 0x23, // pushi 23 - 0x39, 0x74, // pushi 74 - 0x78, // push1 - 0x38, 0x01, 0x74, // pushi 0174 - 0x85, 0x15, // lat 15 - 0 +// Applies to at least: English Mac +// Responsible method: unknown +const uint16 freddypharkasSignatureMacInventory[] = { + SIG_MAGICDWORD, + 0x39, 0x23, // pushi 23 + 0x39, 0x74, // pushi 74 + 0x78, // push1 + 0x38, SIG_UINT16 + 0x74, 0x01, // pushi 0174 (on mac it's actually 0x01, 0x74) + 0x85, 0x15, // lat 15 + SIG_END }; const uint16 freddypharkasPatchMacInventory[] = { - 0x39, 0x02, // pushi 02 (now matches the DOS version) - 0x39, 0x74, // pushi 74 - 0x78, // push1 - 0x38, 0x01, 0x74, // pushi 0174 - 0x85, 0x15, // lat 15 - 0x4a, 0x06, // send 06 - 0x31, 0x08, // bnt 08 - 0x38, 0x01, 0x74, // pushi 0174 - 0x76, // push0 - 0x85, 0x15, // lat 15 - 0x4a, 0x04, // send 04 - 0x02, // add - 0xa5, 0x12, // sat 12 - 0x39, 0x04, // pushi 04 (now matches the DOS version) + 0x39, 0x02, // pushi 02 (now matches the DOS version) + PATCH_ADDTOOFFSET +23, + 0x39, 0x04, // pushi 04 (now matches the DOS version) 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[] = { + { true, 0, "CD: score early disposal", 1, 0, 0, freddypharkasSignatureScoreDisposal, freddypharkasPatchScoreDisposal }, + { true, 15, "Mac: broken inventory", 1, 0, 0, freddypharkasSignatureMacInventory, freddypharkasPatchMacInventory }, + { true, 235, "CD: canister pickup hang", 3, 0, 0, freddypharkasSignatureCanisterHang, freddypharkasPatchCanisterHang }, + { true, 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 -const byte gk1SignatureDay6PoliceBeignet[] = { - 4, - 0x35, 0x04, // ldi 04 - 0x1a, // eq? - 0x30, // bnt [next state check] - +2, 5, // [skip 2 bytes, offset of bnt] - 0x38, 0x93, 0x00, // pushi 93 (selector dispose) - 0x76, // push0 - 0x72, // lofsa deskSarg - +2, 9, // [skip 2 bytes, offset of lofsa] - 0x4a, 0x04, 0x00, // send 04 - 0x34, 0xdc, 0x00, // ldi 220 - 0x65, 0x1a, // aTop cycles - 0x32, // jmp [end] - 0 +// 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_SELECTOR16 + SELECTOR_dispose, // pushi dispose + 0x76, // push0 + 0x72, SIG_ADDTOOFFSET +2, // lofsa deskSarg + 0x4a, SIG_UINT16 + 0x04, 0x00, // send 04 + SIG_MAGICDWORD, + 0x34, SIG_UINT16 + 0xdc, 0x00, // ldi 220 + 0x65, SIG_ADDTOOFFSET +1, // aTop cycles (1a for PC, 1c for Mac) + 0x32, // jmp [end] + SIG_END }; const uint16 gk1PatchDay6PoliceBeignet[] = { - PATCH_ADDTOOFFSET | +16, - 0x34, 0x17, 0x00, // ldi 23 - 0x65, 0x1c, // aTop seconds + PATCH_ADDTOOFFSET +16, + 0x34, PATCH_UINT16 + 0x17, 0x00, // ldi 23 + 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 -const byte gk1SignatureDay6PoliceSleep[] = { - 4, - 0x35, 0x08, // ldi 08 - 0x1a, // eq? - 0x31, // bnt [next state check] - +1, 6, // [skip 1 byte, offset of bnt] - 0x34, 0xdc, 0x00, // ldi 220 - 0x65, 0x1a, // aTop cycles - 0x32, // jmp [end] +// 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, SIG_ADDTOOFFSET +1, // aTop cycles (1a for PC, 1c for Mac) + 0x32, // jmp [end] 0 }; const uint16 gk1PatchDay6PoliceSleep[] = { - PATCH_ADDTOOFFSET | +5, - 0x34, 0x2a, 0x00, // ldi 42 - 0x65, 0x1c, // aTop seconds + PATCH_ADDTOOFFSET +5, + 0x34, SIG_UINT16 + 0x2a, 0x00, // ldi 42 + 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 -const byte gk1SignatureDay5PhoneFreeze[] = { - 5, - 0x35, 0x03, // ldi 03 - 0x65, 0x1a, // aTop cycles - 0x32, // jmp [end] - +2, 3, // [skip 2 bytes, offset of jmp] - 0x3c, // dup - 0x35, 0x21, // ldi 21 - 0 +// 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, SIG_ADDTOOFFSET +1, // aTop cycles + 0x32, SIG_ADDTOOFFSET +2, // jmp [end] + 0x3c, // dup + 0x35, 0x21, // ldi 21 + SIG_END }; 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 }; @@ -478,63 +641,66 @@ 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. -const byte gk1SignatureInterrogationBug[] = { - 43, - 0x65, 0x4c, // aTop 4c - 0x67, 0x50, // pTos 50 - 0x34, 0x10, 0x27, // ldi 2710 - 0x1e, // gt? - 0x31, 0x08, // bnt 08 [05a0] - 0x67, 0x50, // pTos 50 - 0x34, 0x10, 0x27, // ldi 2710 - 0x04, // sub - 0x65, 0x50, // aTop 50 - 0x63, 0x50, // pToa 50 - 0x31, 0x15, // bnt 15 [05b9] - 0x39, 0x0e, // pushi 0e - 0x76, // push0 - 0x4a, 0x04, 0x00, // send 0004 - 0xa5, 0x00, // sat 00 - 0x38, 0x93, 0x00, // pushi 0093 - 0x76, // push0 - 0x63, 0x50, // pToa 50 - 0x4a, 0x04, 0x00, // send 0004 - 0x85, 0x00, // lat 00 - 0x65, 0x50, // aTop 50 - 0 +// 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 + 0x1e, // gt? + 0x31, 0x08, // bnt 08 [05a0] + 0x67, 0x50, // pTos 50 + 0x34, SIG_UINT16 + 0x10, 0x27, // ldi 2710 + 0x04, // sub + 0x65, 0x50, // aTop 50 + 0x63, 0x50, // pToa 50 + 0x31, 0x15, // bnt 15 [05b9] + 0x39, 0x0e, // pushi 0e + 0x76, // push0 + 0x4a, SIG_UINT16 + 0x04, 0x00, // send 0004 + 0xa5, 0x00, // sat 00 + 0x38, SIG_SELECTOR16 + SELECTOR_dispose, // pushi dispose + 0x76, // push0 + 0x63, 0x50, // pToa 50 + 0x4a, SIG_UINT16 + 0x04, 0x00, // send 0004 + 0x85, 0x00, // lat 00 + 0x65, 0x50, // aTop 50 + SIG_END }; const uint16 gk1PatchInterrogationBug[] = { - 0x65, 0x4c, // aTop 4c - 0x63, 0x50, // pToa 50 - 0x31, 0x15, // bnt 15 [05b9] - 0x39, 0x0e, // pushi 0e - 0x76, // push0 - 0x4a, 0x04, 0x00, // send 0004 - 0xa5, 0x00, // sat 00 - 0x38, 0x93, 0x00, // pushi 0093 - 0x76, // push0 - 0x63, 0x50, // pToa 50 - 0x4a, 0x04, 0x00, // send 0004 - 0x85, 0x00, // lat 00 - 0x65, 0x50, // aTop 50 - 0x67, 0x50, // pTos 50 - 0x34, 0x10, 0x27, // ldi 2710 - 0x1e, // gt? - 0x31, 0x08, // bnt 08 [05b9] - 0x67, 0x50, // pTos 50 - 0x34, 0x10, 0x27, // ldi 2710 - 0x04, // sub - 0x65, 0x50, // aTop 50 + 0x65, 0x4c, // aTop 4c + 0x63, 0x50, // pToa 50 + 0x31, 0x15, // bnt 15 [05b9] + 0x39, 0x0e, // pushi 0e + 0x76, // push0 + 0x4a, 0x04, 0x00, // send 0004 + 0xa5, 0x00, // sat 00 + 0x38, SIG_SELECTOR16 + SELECTOR_dispose, // pushi dispose + 0x76, // push0 + 0x63, 0x50, // pToa 50 + 0x4a, 0x04, 0x00, // send 0004 + 0x85, 0x00, // lat 00 + 0x65, 0x50, // aTop 50 + 0x67, 0x50, // pTos 50 + 0x34, PATCH_UINT16 + 0x10, 0x27, // ldi 2710 + 0x1e, // gt? + 0x31, 0x08, // bnt 08 [05b9] + 0x67, 0x50, // pTos 50 + 0x34, PATCH_UINT16 + 0x10, 0x27, // ldi 2710 + 0x04, // sub + 0x65, 0x50, // aTop 50 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[] = { + { true, 51, "interrogation bug", 1, 0, 0, gk1SignatureInterrogationBug, gk1PatchInterrogationBug }, + { true, 212, "day 5 phone freeze", 1, 0, 0, gk1SignatureDay5PhoneFreeze, gk1PatchDay5PhoneFreeze }, + { true, 230, "day 6 police beignet timer issue", 1, 0, 0, gk1SignatureDay6PoliceBeignet, gk1PatchDay6PoliceBeignet }, + { true, 230, "day 6 police sleep timer issue", 1, 0, 0, gk1SignatureDay6PoliceSleep, gk1PatchDay6PoliceSleep }, SCI_SIGNATUREENTRY_TERMINATOR }; @@ -544,47 +710,47 @@ const SciScriptSignature gk1Signatures[] = { // is later used to set master volume. This issue makes sierra sci set // the volume to max. We fix the export, so volume won't get modified in // those cases. -const byte kq5SignatureCdHarpyVolume[] = { - 34, - 0x80, 0x91, 0x01, // lag global[191h] - 0x18, // not - 0x30, 0x2c, 0x00, // bnt [jump further] (jumping, if global 191h is 1) - 0x35, 0x01, // ldi 01 - 0xa0, 0x91, 0x01, // sag global[191h] (setting global 191h to 1) - 0x38, 0x7b, 0x01, // pushi 017b - 0x76, // push0 - 0x81, 0x01, // lag global[1] - 0x4a, 0x04, // send 04 - read KQ5::masterVolume - 0xa5, 0x03, // sat temp[3] (store volume in temp 3) - 0x38, 0x7b, 0x01, // pushi 017b - 0x76, // push0 - 0x81, 0x01, // lag global[1] - 0x4a, 0x04, // send 04 - read KQ5::masterVolume - 0x36, // push - 0x35, 0x04, // ldi 04 - 0x20, // ge? (followed by bnt) - 0 +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) + 0x35, 0x01, // ldi 01 + 0xa0, SIG_UINT16 + 0x91, 0x01, // sag global[191h] (setting global 191h to 1) + 0x38, SIG_UINT16 + 0x7b, 0x01, // pushi 017b + 0x76, // push0 + 0x81, 0x01, // lag global[1] + 0x4a, 0x04, // send 04 - read KQ5::masterVolume + 0xa5, 0x03, // sat temp[3] (store volume in temp 3) + 0x38, SIG_UINT16 + 0x7b, 0x01, // pushi 017b + 0x76, // push0 + 0x81, 0x01, // lag global[1] + 0x4a, 0x04, // send 04 - read KQ5::masterVolume + 0x36, // push + 0x35, 0x04, // ldi 04 + 0x20, // ge? (followed by bnt) + SIG_END }; const uint16 kq5PatchCdHarpyVolume[] = { - 0x38, 0x2f, 0x02, // pushi 022f (selector theVol) (3 new bytes) - 0x76, // push0 (1 new byte) - 0x51, 0x88, // class SpeakTimer (2 new bytes) - 0x4a, 0x04, // send 04 (2 new bytes) -> read SpeakTimer::theVol - 0xa5, 0x03, // sat temp[3] (2 new bytes) -> write to temp 3 - 0x80, 0x91, 0x01, // lag global[191h] + 0x38, PATCH_UINT16 + 0x2f, 0x02, // pushi 022f (selector theVol) (3 new bytes) + 0x76, // push0 (1 new byte) + 0x51, 0x88, // class SpeakTimer (2 new bytes) + 0x4a, 0x04, // send 04 (2 new bytes) -> read SpeakTimer::theVol + 0xa5, 0x03, // sat temp[3] (2 new bytes) -> write to temp 3 + 0x80, PATCH_UINT16 + 0x91, 0x01, // lag global[191h] // saving 1 byte due optimization - 0x2e, 0x23, 0x00, // bt [jump further] (jumping, if global 191h is 1) - 0x35, 0x01, // ldi 01 - 0xa0, 0x91, 0x01, // sag global[191h] (setting global 191h to 1) - 0x38, 0x7b, 0x01, // pushi 017b - 0x76, // push0 - 0x81, 0x01, // lag global[1] - 0x4a, 0x04, // send 04 - read KQ5::masterVolume - 0xa5, 0x03, // sat temp[3] (store volume in temp 3) + 0x2e, PATCH_UINT16 + 0x23, 0x00, // bt [jump further] (jumping, if global 191h is 1) + 0x35, 0x01, // ldi 01 + 0xa0, PATCH_UINT16 + 0x91, 0x01, // sag global[191h] (setting global 191h to 1) + 0x38, PATCH_UINT16 + 0x7b, 0x01, // pushi 017b + 0x76, // push0 + 0x81, 0x01, // lag global[1] + 0x4a, 0x04, // send 04 - read KQ5::masterVolume + 0xa5, 0x03, // sat temp[3] (store volume in temp 3) // saving 8 bytes due removing of duplicate code - 0x39, 0x04, // pushi 04 (saving 1 byte due swapping) - 0x22, // lt? (because we switched values) + 0x39, 0x04, // pushi 04 (saving 1 byte due swapping) + 0x22, // lt? (because we switched values) PATCH_END }; @@ -596,30 +762,32 @@ const uint16 kq5PatchCdHarpyVolume[] = { // Additionally its top,left,bottom,right properties are set to 0 rather // than the right values. We fix the object by setting the right values. // If they are all zero, this causes an impossible position check in -// witch::cantBeHere and an infinite loop when entering room 22 (bug #3034714). +// witch::cantBeHere and an infinite loop when entering room 22. // // This bug is accidentally not triggered in SSCI because the invalid number // of variables effectively hides witchCage::doit, causing this position check // to be bypassed entirely. // See also the warning+comment in Object::initBaseObject -const byte kq5SignatureWitchCageInit[] = { - 16, - 0x00, 0x00, // top - 0x00, 0x00, // left - 0x00, 0x00, // bottom - 0x00, 0x00, // right - 0x00, 0x00, // extra property #1 - 0x7a, 0x00, // extra property #2 - 0xc8, 0x00, // extra property #3 - 0xa3, 0x00, // extra property #4 - 0 +// +// Fixes bug: #4964 +const uint16 kq5SignatureWitchCageInit[] = { + SIG_UINT16 + 0x00, 0x00, // top + SIG_UINT16 + 0x00, 0x00, // left + 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 + SIG_END }; const uint16 kq5PatchWitchCageInit[] = { - 0x00, 0x00, // top - 0x7a, 0x00, // left - 0xc8, 0x00, // bottom - 0xa3, 0x00, // right + PATCH_UINT16 + 0x00, 0x00, // top + PATCH_UINT16 + 0x7a, 0x00, // left + PATCH_UINT16 + 0xc8, 0x00, // bottom + PATCH_UINT16 + 0xa3, 0x00, // right PATCH_END }; @@ -637,31 +805,25 @@ const uint16 kq5PatchWitchCageInit[] = { // changes to GameFeatures::detectsetCursorType() ) and breaking savegame // compatibilty between the DOS and Windows CD versions of KQ5. // TODO: Investigate these side effects more closely. -const byte kq5SignatureWinGMSignals[] = { - 9, - 0x80, 0x90, 0x01, // lag 0x190 - 0x18, // not - 0x30, 0x1b, 0x00, // bnt +0x001B - 0x89, 0x57, // lsg 0x57 - 0 +const uint16 kq5SignatureWinGMSignals[] = { + SIG_MAGICDWORD, + 0x80, SIG_UINT16 + 0x90, 0x01, // lag 0x190 + 0x18, // not + 0x30, SIG_UINT16 + 0x1b, 0x00, // bnt +0x001B + 0x89, 0x57, // lsg 0x57 + SIG_END }; const uint16 kq5PatchWinGMSignals[] = { - 0x34, 0x01, 0x00, // ldi 0x0001 + 0x34, PATCH_UINT16 + 0x01, 0x00, // ldi 0x0001 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 }, - 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 }, +// script, description, signature patch +SciScriptPatcherEntry kq5Signatures[] = { + { true, 0, "CD: harpy volume change", 1, 0, 0, kq5SignatureCdHarpyVolume, kq5PatchCdHarpyVolume }, + { true, 200, "CD: witch cage init", 1, 0, 0, kq5SignatureWitchCageInit, kq5PatchWitchCageInit }, + { false, 124, "Win: GM Music signal checks", 4, 0, 0, kq5SignatureWinGMSignals, kq5PatchWinGMSignals }, SCI_SIGNATUREENTRY_TERMINATOR }; @@ -673,25 +835,203 @@ const SciScriptSignature kq5WinGMSignatures[] = { // sound is played twice, squelching all other sounds. We just rip the // unnecessary cryMusic::check method out, thereby stopping the sound from // 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 byte kq6SignatureDuplicateBabyCry[] = { - 10, - 0x83, 0x00, // lal 00 - 0x31, 0x1e, // bnt 1e [07f4] - 0x78, // push1 - 0x39, 0x04, // pushi 04 - 0x43, 0x75, 0x02, // callk DoAudio[75] 02 - 0 +// game speech can work while the baby cry sound is heard. +// Fixes bug: #4955 +const uint16 kq6SignatureDuplicateBabyCry[] = { + SIG_MAGICDWORD, + 0x83, 0x00, // lal 00 + 0x31, 0x1e, // bnt 1e [07f4] + 0x78, // push1 + 0x39, 0x04, // pushi 04 + 0x43, 0x75, 0x02, // callk DoAudio[75] 02 + SIG_END }; const uint16 kq6PatchDuplicateBabyCry[] = { - 0x48, // ret + 0x48, // ret + PATCH_END +}; + +// The inventory of King's Quest 6 is buggy. When it grows too large, +// it will get split into 2 pages. Switching between those pages will +// 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. +// Applies to at least: PC-CD, English PC floppy, German PC floppy, English Mac +// Responsible method: KqInv::showSelf +// Fixes bug: #5681 +const uint16 kq6SignatureInventoryStackFix[] = { + 0x67, 0x30, // pTos state + 0x34, SIG_UINT16 + 0x00, 0x20, // ldi 2000 + 0x12, // and + 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_SELECTOR16 + SELECTOR_show, // pushi "show" ("show" is e1h for KQ6CD) + 0x78, // push1 + 0x87, 0x00, // lap param[0] + 0x31, 0x04, // bnt [use global for show] + 0x87, 0x01, // lap param[1] + 0x33, 0x02, // jmp [use param for show] + 0x81, 0x00, // lag global[0] + 0x36, // push + 0x54, 0x06, // self 06 (KqInv::show) + 0x31, SIG_ADDTOOFFSET + 1, // bnt [exit menu code] (0x08 for PC, 0x07 for mac) + 0x39, 0x39, // pushi 39 + 0x76, // push0 + 0x54, 0x04, // self 04 (KqInv::doit) + SIG_END // followed by jmp (0x32 for PC, 0x33 for mac) +}; + +const uint16 kq6PatchInventoryStackFix[] = { + 0x67, 0x30, // pTos state + 0x3c, // dup (1 more byte, needed for patch) + 0x3c, // dup (1 more byte, saves 1 byte later) + 0x34, PATCH_UINT16 + 0x00, 0x20, // ldi 2000 + 0x12, // and + 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 + 0x12, // and + 0x65, 0x30, // aTop state + 0x38, // pushi "show" + PATCH_GETORIGINALBYTE +22, + PATCH_GETORIGINALBYTE +23, + 0x78, // push1 + 0x87, 0x00, // lap param[0] + 0x31, 0x04, // bnt [call show using global 0] + 0x8f, 0x01, // lsp param[1], save 1 byte total with lsg global[0] combined + 0x33, 0x02, // jmp [call show using param 1] + 0x89, 0x00, // lsg global[0], save 1 byte total, see above + 0x54, 0x06, // self 06 (call x::show) + 0x31, // bnt [menu exit code] + PATCH_GETORIGINALBYTEADJUST +39, +6,// dynamic offset must be 0x0E for PC and 0x0D for mac + 0x34, PATCH_UINT16 + 0x00, 0x20, // ldi 2000 + 0x12, // and + 0x2f, 0x05, // bt [to return] + 0x39, 0x39, // pushi 39 + 0x76, // push0 + 0x54, 0x04, // self 04 (self::doit) + 0x48, // ret (saves 2 bytes for PC, 1 byte for mac) + PATCH_END +}; + +// Audio + subtitles support - SHARED! - used for King's Quest 6 and Laura Bow 2 +// this patch gets enabled, when the user selects "both" in the ScummVM "Speech + Subtitles" menu +// We currently use global 98d to hold a kMemory pointer. +// Patched method: Messager::sayNext / lb2Messager::sayNext (always use text branch) +const uint16 kq6laurabow2CDSignatureAudioTextSupport1[] = { + 0x89, 0x5a, // lsg global[5a] + 0x35, 0x02, // ldi 02 + 0x12, // and + SIG_MAGICDWORD, + 0x31, 0x13, // bnt [audio call] + 0x38, SIG_SELECTOR16 + SELECTOR_modNum, // pushi modNum + SIG_END +}; + +const uint16 kq6laurabow2CDPatchAudioTextSupport1[] = { + PATCH_ADDTOOFFSET +5, + 0x33, 0x13, // jmp [audio call] + PATCH_END +}; + +// Patched method: Messager::sayNext / lb2Messager::sayNext (allocate audio memory) +const uint16 kq6laurabow2CDSignatureAudioTextSupport2[] = { + 0x7a, // push2 + 0x78, // push1 + 0x39, 0x0c, // pushi 0c + 0x43, SIG_MAGICDWORD, 0x72, 0x04, // kMemory + 0xa5, 0xc9, // sat global[c9] + SIG_END +}; + +const uint16 kq6laurabow2CDPatchAudioTextSupport2[] = { + PATCH_ADDTOOFFSET +7, + 0xa1, 98, // sag global[98d] PATCH_END }; -// script, description, magic DWORD, adjust -const SciScriptSignature kq6Signatures[] = { - { 481, "duplicate baby cry", 1, PATCH_MAGICDWORD(0x83, 0x00, 0x31, 0x1e), 0, kq6SignatureDuplicateBabyCry, kq6PatchDuplicateBabyCry }, +// Patched method: Messager::sayNext / lb2Messager::sayNext (release audio memory) +const uint16 kq6laurabow2CDSignatureAudioTextSupport3[] = { + 0x7a, // push2 + 0x39, 0x03, // pushi 03 + SIG_MAGICDWORD, + 0x8d, 0xc9, // lst temp[c9] + 0x43, 0x72, 0x04, // kMemory + SIG_END +}; + +const uint16 kq6laurabow2CDPatchAudioTextSupport3[] = { + PATCH_ADDTOOFFSET +3, + 0x89, 98, // lsg global[98d] + PATCH_END +}; + +// Patched method: Narrator::say (use audio memory) +const uint16 kq6laurabow2CDSignatureAudioTextSupport4[] = { + 0x89, 0x5a, // lsg global[5a] + 0x35, 0x01, // ldi 01 + 0x12, // and + 0x31, 0x08, // bnt [skip code] + 0x38, SIG_SELECTOR16 + SELECTOR_startText, // pushi startText + 0x78, // push1 + 0x8f, 0x01, // lsp param[1] + 0x54, 0x06, // self 06 + 0x89, 0x5a, // lsg global[5a] + 0x35, 0x02, // ldi 02 + 0x12, // and + 0x31, 0x08, // bnt [skip code] + SIG_MAGICDWORD, + 0x38, SIG_SELECTOR16 + SELECTOR_startAudio, // pushi startAudio + 0x78, // push1 + 0x8f, 0x01, // lsp param[1] + 0x54, 0x06, // self 06 + SIG_END +}; + +const uint16 kq6laurabow2CDPatchAudioTextSupport4[] = { + PATCH_ADDTOOFFSET +5, + 0x18, // not (never jump here) + 0x18, // not (never jump here) + PATCH_ADDTOOFFSET +19, + 0x89, 98, // lsp global[98d] + PATCH_END +}; + +// Patched method: Talker::display/Narrator::say (remove reset saved mouse cursor code) +// code would screw over mouse cursor +const uint16 kq6laurabow2CDSignatureAudioTextSupport5[] = { + SIG_MAGICDWORD, + 0x35, 0x00, // ldi 00 + 0x65, 0x82, // aTop saveCursor + SIG_END +}; + +const uint16 kq6laurabow2CDPatchAudioTextSupport5[] = { + 0x18, 0x18, 0x18, 0x18, // waste bytes, do nothing + PATCH_END +}; + +// script, description, signature patch +SciScriptPatcherEntry kq6Signatures[] = { + { true, 481, "duplicate baby cry", 1, 0, 0, kq6SignatureDuplicateBabyCry, kq6PatchDuplicateBabyCry }, + { true, 907, "inventory stack fix", 1, 0, 0, kq6SignatureInventoryStackFix, kq6PatchInventoryStackFix }, + // King's Quest 6 and Laura Bow 2 share basic patches for audio + text support + // *** King's Quest 6 audio + text support - CURRENTLY DISABLED *** + // TODO: fix window placement (currently part of the text windows go off-screen) + // TODO: fix hi-res portraits mode graphic glitches + { false, 924, "CD: audio + text support 1", 1, 0, 0, kq6laurabow2CDSignatureAudioTextSupport1, kq6laurabow2CDPatchAudioTextSupport1 }, + { false, 924, "CD: audio + text support 2", 1, 0, 0, kq6laurabow2CDSignatureAudioTextSupport2, kq6laurabow2CDPatchAudioTextSupport2 }, + { false, 924, "CD: audio + text support 3", 1, 0, 0, kq6laurabow2CDSignatureAudioTextSupport3, kq6laurabow2CDPatchAudioTextSupport3 }, + { false, 928, "CD: audio + text support 4", 1, 0, 0, kq6laurabow2CDSignatureAudioTextSupport4, kq6laurabow2CDPatchAudioTextSupport4 }, + { false, 928, "CD: audio + text support 5", 2, 0, 0, kq6laurabow2CDSignatureAudioTextSupport5, kq6laurabow2CDPatchAudioTextSupport5 }, SCI_SIGNATUREENTRY_TERMINATOR }; @@ -704,40 +1044,111 @@ 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. -const byte longbowSignatureShowHandCode[] = { - 3, - 0x78, // push1 - 0x78, // push1 - 0x72, // lofsa - +2, 2, // skip 2 bytes, offset of lofsa (the letter typed) - 0x36, // push - 0x40, // call - +2, 3, // skip 2 bytes, offset of call - 0x02, // perform the call above with 2 parameters - 0x36, // push - 0x40, // call - +2, 8, // skip 2 bytes, offset of call - 0x02, // perform the call above with 2 parameters - 0x38, 0x1c, 0x01, // pushi 011c (setMotion) - 0x39, 0x04, // pushi 04 (x) - 0x51, 0x1e, // class MoveTo - 0 +// the same as the English version. +// Applies to at least: German floppy +// Responsible method: unknown +// Fixes bug: #5264 +const uint16 longbowSignatureShowHandCode[] = { + 0x78, // push1 + 0x78, // push1 + 0x72, SIG_ADDTOOFFSET +2, // lofsa (letter, that was typed) + 0x36, // push + 0x40, SIG_ADDTOOFFSET +2, // call + 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_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 }; const uint16 longbowPatchShowHandCode[] = { - 0x39, 0x01, // pushi 1 (combine the two push1's in one, like in the English version) - PATCH_ADDTOOFFSET | +3, // leave the lofsa call untouched + 0x39, 0x01, // pushi 1 (combine the two push1's in one, like in the English version) + PATCH_ADDTOOFFSET +3, // leave the lofsa call untouched // The following will remove the duplicate call - 0x32, 0x02, 0x00, // jmp 02 - skip 2 bytes (the remainder of the first call) - 0x48, // ret (dummy, should never be reached) - 0x48, // ret (dummy, should never be reached) + 0x32, PATCH_UINT16 + 0x02, 0x00, // jmp 02 - skip 2 bytes (the remainder of the first call) + 0x48, // ret (dummy, should never be reached) + 0x48, // ret (dummy, should never be reached) 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[] = { + { true, 210, "hand code crash", 5, 0, 0, longbowSignatureShowHandCode, longbowPatchShowHandCode }, + SCI_SIGNATUREENTRY_TERMINATOR +}; + +// =========================================================================== +// Leisure Suit Larry 2 +// On the plane, Larry is able to wear the parachute. This grants 4 points. +// In early versions of LSL2, it was possible to get "unlimited" points by +// simply wearing it multiple times. +// They fixed it in later versions by remembering, if the parachute was already +// used before. +// But instead of adding it properly, it seems they hacked the script / forgot +// to replace script 0 as well, which holds information about how many global +// variables are allocated at the start of the game. +// The script tries to read an out-of-bounds global variable, which somewhat +// "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: #6346 +const uint16 larry2SignatureWearParachutePoints[] = { + 0x35, 0x01, // ldi 01 + 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] + 0x35, 0x01, // ldi 01 + 0xa0, 0xe0, 0x01, // sag 1e0 + SIG_END +}; + +const uint16 larry2PatchWearParachutePoints[] = { + PATCH_ADDTOOFFSET +4, + 0x80, PATCH_UINT16 + 0x5a, 0x00, // lag 5a (global 90) + PATCH_ADDTOOFFSET +6, + 0xa0, PATCH_UINT16 + 0x5a, 0x00, // sag 5a (global 90) + PATCH_END +}; + +// script, description, signature patch +SciScriptPatcherEntry larry2Signatures[] = { + { true, 63, "plane: no points for wearing plane", 1, 0, 0, larry2SignatureWearParachutePoints, larry2PatchWearParachutePoints }, + SCI_SIGNATUREENTRY_TERMINATOR +}; + +// =========================================================================== +// Leisure Suit Larry 5 +// In one of the conversations near the end (to be exact - room 380 and the text +// about using champagne on Reverse Biaz - only used when you actually did that +// in the game), the German text is too large, causing the textbox to get too large. +// Because of that the talking head of Patti is drawn over the textbox. A translation oversight. +// Applies to at least: German floppy +// Responsible method: none, position of talker object on screen needs to get modified +const uint16 larry5SignatureGermanEndingPattiTalker[] = { + SIG_MAGICDWORD, + SIG_UINT16 + 0x6e, 0x00, // object pattiTalker::x (110) + SIG_UINT16 + 0xb4, 0x00, // object pattiTalker::y (180) + SIG_ADDTOOFFSET + 469, // verify that it's really the German version + 0x59, 0x6f, 0x75, // (object name) "You" + 0x23, 0x47, 0x44, 0x75, // "#GDu" + SIG_END +}; + +const uint16 larry5PatchGermanEndingPattiTalker[] = { + PATCH_UINT16 + 0x5a, 0x00, // change pattiTalker::x to 90 + PATCH_END +}; + +// script, description, signature patch +SciScriptPatcherEntry larry5Signatures[] = { + { true, 380, "German-only: Enlarge Patti Textbox", 1, 0, 0, larry5SignatureGermanEndingPattiTalker, larry5PatchGermanEndingPattiTalker }, SCI_SIGNATUREENTRY_TERMINATOR }; @@ -749,77 +1160,177 @@ const SciScriptSignature longbowSignatures[] = { // doesn't happen anymore. We would otherwise get a crash // calling for invalid views (this happens of course also // in sierra sci) -const byte larry6SignatureDeathDialog[] = { - 7, - 0x3e, 0x33, 0x01, // link 0133 (offset 0x20) - 0x35, 0xff, // ldi ff - 0xa3, 0x00, // sal 00 - +255, 0, - +255, 0, - +170, 12, // [skip 680 bytes] - 0x8f, 0x01, // lsp 01 (offset 0x2cf) - 0x7a, // push2 - 0x5a, 0x04, 0x00, 0x0e, 0x01, // lea 0004 010e - 0x36, // push - 0x43, 0x7c, 0x0e, // kMessage[7c] 0e - +90, 10, // [skip 90 bytes] - 0x38, 0xd6, 0x00, // pushi 00d6 (offset 0x335) - 0x78, // push1 - 0x5a, 0x04, 0x00, 0x0e, 0x01, // lea 0004 010e - 0x36, // push - +76, 11, // [skip 76 bytes] - 0x38, 0xcd, 0x00, // pushi 00cd (offset 0x38b) - 0x39, 0x03, // pushi 03 - 0x5a, 0x04, 0x00, 0x0e, 0x01, // lea 0004 010e +// 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 + SIG_ADDTOOFFSET +680, // [skip 680 bytes] + 0x8f, 0x01, // lsp 01 (offset 0x2cf) + 0x7a, // push2 + 0x5a, SIG_UINT16 + 0x04, 0x00, SIG_UINT16 + 0x0e, 0x01, // lea 0004 010e + 0x36, // push + 0x43, 0x7c, 0x0e, // kMessage[7c] 0e + SIG_ADDTOOFFSET +90, // [skip 90 bytes] + 0x38, SIG_UINT16 + 0xd6, 0x00, // pushi 00d6 (offset 0x335) + 0x78, // push1 + 0x5a, SIG_UINT16 + 0x04, 0x00, SIG_UINT16 + 0x0e, 0x01, // lea 0004 010e + 0x36, // push + SIG_ADDTOOFFSET +76, // [skip 76 bytes] + 0x38, SIG_UINT16 + 0xcd, 0x00, // pushi 00cd (offset 0x38b) + 0x39, 0x03, // pushi 03 + 0x5a, SIG_UINT16 + 0x04, 0x00, SIG_UINT16 + 0x0e, 0x01, // lea 0004 010e 0x36, - 0 + SIG_END }; const uint16 larry6PatchDeathDialog[] = { - 0x3e, 0x00, 0x02, // link 0200 - PATCH_ADDTOOFFSET | +687, - 0x5a, 0x04, 0x00, 0x40, 0x01, // lea 0004 0140 - PATCH_ADDTOOFFSET | +98, - 0x5a, 0x04, 0x00, 0x40, 0x01, // lea 0004 0140 - PATCH_ADDTOOFFSET | +82, - 0x5a, 0x04, 0x00, 0x40, 0x01, // lea 0004 0140 + 0x3e, 0x00, 0x02, // link 0200 + PATCH_ADDTOOFFSET +687, + 0x5a, PATCH_UINT16 + 0x04, 0x00, PATCH_UINT16 + 0x40, 0x01, // lea 0004 0140 + PATCH_ADDTOOFFSET +98, + 0x5a, PATCH_UINT16 + 0x04, 0x00, PATCH_UINT16 + 0x40, 0x01, // lea 0004 0140 + PATCH_ADDTOOFFSET +82, + 0x5a, PATCH_UINT16 + 0x04, 0x00, PATCH_UINT16 + 0x40, 0x01, // lea 0004 0140 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, signature patch +SciScriptPatcherEntry larry6Signatures[] = { + { true, 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 -// is not in the room. We fix that. -const byte laurabow2SignaturePaintingClosing[] = { - 17, - 0x4a, 0x04, // send 04 - read aHeimlich::room - 0x36, // push - 0x81, 0x0b, // lag global[11d] -> current room - 0x1c, // ne? - 0x31, 0x0e, // bnt [don't close] - 0x35, 0x00, // ldi 00 - 0xa3, 0x00, // sal local[0] - 0x38, 0x92, 0x00, // pushi 0092 - 0x78, // push1 - 0x72, // lofsa sDumpSafe - 0 +// Laura Bow 2 +// +// Moving away the painting in the room with the hidden safe is problematic +// for the CD version of the game. safePic::doVerb gets triggered by the mouse-click. +// This method sets local 0 as signal, which is only meant to get handled, when +// the player clicks again to move the painting back. This signal is processed by +// the room doit-script. +// That doit-script checks safePic::cel to be not equal 0 and would then skip over +// the "close painting" trigger code. On very fast computers this script may +// get called too early (which is the case when running under ScummVM and when +// running the game using Sierra SCI in DOS-Box with cycles 15000) and thinks +// that it's supposed to move the painting back. Which then results in the painting +// getting moved to its original position immediately (which means it won't be possible +// to access the safe behind it). +// +// We patch the script, so that we check for cel to be not equal 4 (the final cel) and +// we also reset the safePic-signal immediately as well. +// +// In the floppy version Laura's coordinates are checked directly in rm560::doit +// and as soon as she moves, the painting will automatically move to its original position. +// This is not the case for the CD version of the game. The painting will only "move" back, +// when the player actually exits the room and re-enters. +// +// Applies to at least: English PC-CD +// Responsible method: rm560::doit +// Fixes bug: #6460 +const uint16 laurabow2CDSignaturePaintingClosing[] = { + 0x39, 0x04, // pushi 04 (cel) + 0x76, // push0 + SIG_MAGICDWORD, + 0x7a, // push2 + 0x38, SIG_UINT16 + 0x31, 0x02, // pushi 0231h (561) + 0x76, // push0 + 0x43, 0x02, 0x04, // kScriptID (get export 0 of script 561) + 0x4a, 0x04, // send 04 (gets safePicture::cel) + 0x18, // not + 0x31, 0x21, // bnt [exit] + 0x38, SIG_UINT16 + 0x83, 0x02, // pushi 0283h + 0x76, // push0 + 0x7a, // push2 + 0x39, 0x20, // pushi 20 + 0x76, // push0 + 0x43, 0x02, 0x04, // kScriptID (get export 0 of script 32) + 0x4a, 0x04, // send 04 (get sHeimlich::room) + 0x36, // push + 0x81, 0x0b, // lag global[b] (current room) + 0x1c, // ne? + 0x31, 0x0e, // bnt [exit] + 0x35, 0x00, // ldi 00 + 0xa3, 0x00, // sal local[0] -> reset safePic signal + SIG_END +}; + +const uint16 laurabow2CDPatchPaintingClosing[] = { + PATCH_ADDTOOFFSET +2, + 0x3c, // dup (1 additional byte) + 0x76, // push0 + 0x3c, // dup (1 additional byte) + 0xab, 0x00, // ssl local[0] -> reset safePic signal + 0x7a, // push2 + 0x38, PATCH_UINT16 + 0x31, 0x02, // pushi 0231h (561) + 0x76, // push0 + 0x43, 0x02, 0x04, // kScriptID (get export 0 of script 561) + 0x4a, 0x04, // send 04 (gets safePicture::cel) + 0x1a, // eq? + 0x31, 0x1d, // bnt [exit] + 0x38, PATCH_UINT16 + 0x83, 0x02, // pushi 0283h + 0x76, // push0 + 0x7a, // push2 + 0x39, 0x20, // pushi 20 + 0x76, // push0 + 0x43, 0x02, 0x04, // kScriptID (get export 0 of script 32) + 0x4a, 0x04, // send 04 (get sHeimlich::room) + 0x36, // push + 0x81, 0x0b, // lag global[b] (current room) + 0x1a, // eq? (2 opcodes changed, to save 2 bytes) + 0x2f, 0x0a, // bt [exit] + PATCH_END }; -const uint16 laurabow2PatchPaintingClosing[] = { - PATCH_ADDTOOFFSET | +6, - 0x2f, 0x0e, // bt [don't close] +// In the CD version the system menu is disabled for certain rooms. LB2::handsOff is called, +// when leaving the room (and in other cases as well). This method remembers the disabled +// icons of the icon bar. In the new room LB2::handsOn will get called, which then enables +// all icons, but also disabled the ones, that were disabled before. +// +// Because of this behaviour certain rooms, that should have the system menu enabled, have +// it disabled, when entering those rooms from rooms, where the menu is supposed to be +// disabled. +// +// We patch this by injecting code into LB2::newRoom (which is called right after a room change) +// and reset the global variable there, that normally holds the disabled buttons. +// +// This patch may cause side-effects and it's difficult to test, because it affects every room +// in the game. At least for the intro, the speakeasy and plenty of rooms in the beginning it +// seems to work correctly. +// +// Applies to at least: English PC-CD +// Responsible method: LB2::newRoom, LB2::handsOff, LB2::handsOn +// Fixes bug: #6440 +const uint16 laurabow2CDSignatureFixProblematicIconBar[] = { + SIG_MAGICDWORD, + 0x38, SIG_UINT16 + 0xf1, 0x00, // pushi 00f1 (disable) - hardcoded, we only want to patch the CD version + 0x76, // push0 + 0x81, 0x45, // lag global[45] + 0x4a, 0x04, // send 04 + SIG_END +}; + +const uint16 laurabow2CDPatchFixProblematicIconBar[] = { + 0x35, 0x00, // ldi 00 + 0xa1, 0x74, // sag 74h + 0x35, 0x00, // ldi 00 (waste bytes) + 0x35, 0x00, // ldi 00 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[] = { + { true, 560, "CD: painting closing immediately", 1, 0, 0, laurabow2CDSignaturePaintingClosing, laurabow2CDPatchPaintingClosing }, + { true, 0, "CD: fix problematic icon bar", 1, 0, 0, laurabow2CDSignatureFixProblematicIconBar, laurabow2CDPatchFixProblematicIconBar }, + // King's Quest 6 and Laura Bow 2 share basic patches for audio + text support + { false, 924, "CD: audio + text support 1", 1, 0, 0, kq6laurabow2CDSignatureAudioTextSupport1, kq6laurabow2CDPatchAudioTextSupport1 }, + { false, 924, "CD: audio + text support 2", 1, 0, 0, kq6laurabow2CDSignatureAudioTextSupport2, kq6laurabow2CDPatchAudioTextSupport2 }, + { false, 924, "CD: audio + text support 3", 1, 0, 0, kq6laurabow2CDSignatureAudioTextSupport3, kq6laurabow2CDPatchAudioTextSupport3 }, + { false, 928, "CD: audio + text support 4", 1, 0, 0, kq6laurabow2CDSignatureAudioTextSupport4, kq6laurabow2CDPatchAudioTextSupport4 }, + { false, 928, "CD: audio + text support 5", 2, 0, 0, kq6laurabow2CDSignatureAudioTextSupport5, kq6laurabow2CDPatchAudioTextSupport5 }, SCI_SIGNATUREENTRY_TERMINATOR }; @@ -828,29 +1339,27 @@ const SciScriptSignature laurabow2Signatures[] = { // MG::replay somewhat calculates the savedgame-id used when saving again // this doesn't work right and we remove the code completely. // We set the savedgame-id directly right after restoring in kRestoreGame. -const byte mothergoose256SignatureReplay[] = { - 6, - 0x36, // push - 0x35, 0x20, // ldi 20 - 0x04, // sub - 0xa1, 0xb3, // sag global[b3] - 0 +const uint16 mothergoose256SignatureReplay[] = { + 0x36, // push + 0x35, SIG_MAGICDWORD, 0x20, // ldi 20 + 0x04, // sub + 0xa1, 0xb3, // sag global[b3] + SIG_END }; const uint16 mothergoose256PatchReplay[] = { - 0x34, 0x00, 0x00, // ldi 0000 (dummy) - 0x34, 0x00, 0x00, // ldi 0000 (dummy) + 0x34, PATCH_UINT16 + 0x00, 0x00, // ldi 0000 (dummy) + 0x34, PATCH_UINT16 + 0x00, 0x00, // ldi 0000 (dummy) PATCH_END }; // when saving, it also checks if the savegame ID is below 13. // we change this to check if below 113 instead -const byte mothergoose256SignatureSaveLimit[] = { - 5, - 0x89, 0xb3, // lsg global[b3] - 0x35, 0x0d, // ldi 0d - 0x20, // ge? - 0 +const uint16 mothergoose256SignatureSaveLimit[] = { + 0x89, SIG_MAGICDWORD, 0xb3, // lsg global[b3] + 0x35, 0x0d, // ldi 0d + 0x20, // ge? + SIG_END }; const uint16 mothergoose256PatchSaveLimit[] = { @@ -859,11 +1368,74 @@ 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, signature patch +SciScriptPatcherEntry mothergoose256Signatures[] = { + { true, 0, "replay save issue", 1, 0, 0, mothergoose256SignatureReplay, mothergoose256PatchReplay }, + { true, 0, "save limit dialog (SCI1.1)", 1, 0, 0, mothergoose256SignatureSaveLimit, mothergoose256PatchSaveLimit }, + { true, 994, "save limit dialog (SCI1)", 1, 0, 0, mothergoose256SignatureSaveLimit, mothergoose256PatchSaveLimit }, + SCI_SIGNATUREENTRY_TERMINATOR +}; + +// =========================================================================== +// Police Quest 1 VGA +// When at the police station, you can put or get your gun from your locker. +// The script, that handles this, is buggy. It disposes the gun as soon as +// you click, but then waits 2 seconds before it also closes the locker. +// Problem is that it's possible to click again, which then results in a +// disposed object getting accessed. This happened to work by pure luck in +// 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: #5705 / #6400 +const uint16 pq1vgaSignaturePutGunInLockerBug[] = { + 0x35, 0x00, // ldi 00 + 0x1a, // eq? + 0x31, 0x25, // bnt [next state check] + SIG_ADDTOOFFSET +22, // [skip 22 bytes] + SIG_MAGICDWORD, + 0x38, SIG_SELECTOR16 + SELECTOR_put, // pushi "put" + 0x78, // push1 + 0x76, // push0 + 0x81, 0x00, // lag 00 + 0x4a, 0x06, // send 06 - ego::put(0) + 0x35, 0x02, // ldi 02 + 0x65, 0x1c, // aTop 1c (set timer to 2 seconds) + 0x33, 0x0e, // jmp [end of method] + 0x3c, // dup --- next state check target + 0x35, 0x01, // ldi 01 + 0x1a, // eq? + 0x31, 0x08, // bnt [end of method] + 0x39, SIG_SELECTOR8 + SELECTOR_dispose, // pushi "dispose" + 0x76, // push0 + 0x72, SIG_UINT16 + 0x88, 0x00, // lofsa 0088 + 0x4a, 0x04, // send 04 - locker::dispose + SIG_END +}; + +const uint16 pq1vgaPatchPutGunInLockerBug[] = { + PATCH_ADDTOOFFSET +3, + 0x31, 0x1c, // bnt [next state check] + PATCH_ADDTOOFFSET +22, + 0x35, 0x02, // ldi 02 + 0x65, 0x1c, // aTop 1c (set timer to 2 seconds) + 0x33, 0x17, // jmp [end of method] + 0x3c, // dup --- next state check target + 0x35, 0x01, // ldi 01 + 0x1a, // eq? + 0x31, 0x11, // bnt [end of method] + 0x38, PATCH_SELECTOR16 + SELECTOR_put, // pushi "put" + 0x78, // push1 + 0x76, // push0 + 0x81, 0x00, // lag 00 + 0x4a, 0x06, // send 06 - ego::put(0) + PATCH_END +}; + +// script, description, signature patch +SciScriptPatcherEntry pq1vgaSignatures[] = { + { true, 341, "put gun in locker bug", 1, 0, 0, pq1vgaSignaturePutGunInLockerBug, pq1vgaPatchPutGunInLockerBug }, SCI_SIGNATUREENTRY_TERMINATOR }; @@ -876,43 +1448,45 @@ const SciScriptSignature mothergoose256Signatures[] = { // 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. -const byte qfg1vgaSignatureFightEvents[] = { - 25, - 0x39, 0x6d, // pushi 6d (selector new) - 0x76, // push0 - 0x51, 0x07, // class Event - 0x4a, 0x04, // send 04 - call Event::new - 0xa5, 0x00, // sat temp[0] - 0x78, // push1 - 0x76, // push0 - 0x4a, 0x04, // send 04 - read Event::x - 0xa5, 0x03, // sat temp[3] - 0x76, // push0 (selector y) - 0x76, // push0 - 0x85, 0x00, // lat temp[0] - 0x4a, 0x04, // send 04 - read Event::y - 0x36, // push - 0x35, 0x0a, // ldi 0a - 0x04, // sub (poor mans localization) ;-) - 0 +// Applies to at least: English floppy +// Responsible method: pointBox::doit +const uint16 qfg1vgaSignatureFightEvents[] = { + 0x39, SIG_MAGICDWORD, + SIG_SELECTOR8 + SELECTOR_new, // pushi "new" + 0x76, // push0 + 0x51, 0x07, // class Event + 0x4a, 0x04, // send 04 - call Event::new + 0xa5, 0x00, // sat temp[0] + 0x78, // push1 + 0x76, // push0 + 0x4a, 0x04, // send 04 - read Event::x + 0xa5, 0x03, // sat temp[3] + 0x76, // push0 (selector y) + 0x76, // push0 + 0x85, 0x00, // lat temp[0] + 0x4a, 0x04, // send 04 - read Event::y + 0x36, // push + 0x35, 0x0a, // ldi 0a + 0x04, // sub (poor mans localization) ;-) + SIG_END }; const uint16 qfg1vgaPatchFightEvents[] = { - 0x38, 0x5a, 0x01, // 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 - 0xa5, 0x00, // sat temp[0] - 0x78, // push1 - 0x76, // push0 - 0x4a, 0x04, // send 04 - read Event::x - 0xa5, 0x03, // sat temp[3] - 0x76, // push0 (selector y) - 0x76, // push0 - 0x85, 0x00, // lat temp[0] - 0x4a, 0x04, // send 04 - read Event::y - 0x39, 0x00, // pushi 00 - 0x02, // add (waste 3 bytes) - we don't need localization, User::doit has already done it + 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 + 0xa5, 0x00, // sat temp[0] + 0x78, // push1 + 0x76, // push0 + 0x4a, 0x04, // send 04 - read Event::x + 0xa5, 0x03, // sat temp[3] + 0x76, // push0 (selector y) + 0x76, // push0 + 0x85, 0x00, // lat temp[0] + 0x4a, 0x04, // send 04 - read Event::y + 0x39, 0x00, // pushi 00 + 0x02, // add (waste 3 bytes) - we don't need localization, User::doit has already done it PATCH_END }; @@ -923,66 +1497,150 @@ const uint16 qfg1vgaPatchFightEvents[] = { // window text, which erases the window header text because of its length. To // fix that, we allocate more temp space and move the pointer used for the // window header a little bit, wherever it's used in script 814. -// Fixes bug #3568431. +// Fixes bug: #6139. // Patch 1: Increase temp space -const byte qfg1vgaSignatureTempSpace[] = { - 4, - 0x3f, 0xba, // link 0xba - 0x87, 0x00, // lap 0 - 0 +const uint16 qfg1vgaSignatureTempSpace[] = { + SIG_MAGICDWORD, + 0x3f, 0xba, // link 0xba + 0x87, 0x00, // lap 0 + SIG_END }; const uint16 qfg1vgaPatchTempSpace[] = { - 0x3f, 0xca, // link 0xca + 0x3f, 0xca, // link 0xca PATCH_END }; // Patch 2: Move the pointer used for the window header a little bit -const byte qfg1vgaSignatureDialogHeader[] = { - 4, - 0x5b, 0x04, 0x80, // lea temp[0x80] - 0x36, // push - 0 +const uint16 qfg1vgaSignatureDialogHeader[] = { + SIG_MAGICDWORD, + 0x5b, 0x04, 0x80, // lea temp[0x80] + 0x36, // push + SIG_END }; const uint16 qfg1vgaPatchDialogHeader[] = { - 0x5b, 0x04, 0x90, // lea temp[0x90] + 0x5b, 0x04, 0x90, // lea temp[0x90] PATCH_END }; // When clicking on the crusher in room 331, Ego approaches him to talk to him, // an action that is handled by moveToCrusher::changeState in script 331. The -// scripts set Ego to move close to the crusher, but when Ego is running instead +// scripts set Ego to move close to the crusher, but when Ego is sneaking instead // of walking, the target coordinates specified by script 331 are never reached, // as Ego is making larger steps, and never reaches the required spot. This is an -// edge case that can occur when Ego is set to run. Normally, when clicking on +// edge case that can occur when Ego is set to sneak. Normally, when clicking on // the crusher, ego is supposed to move close to position 79, 165. We change it // to 85, 165, which is not an edge case thus the freeze is avoided. -// Fixes bug #3585189. -const byte qfg1vgaSignatureMoveToCrusher[] = { - 9, - 0x51, 0x1f, // class Motion - 0x36, // push - 0x39, 0x4f, // pushi 4f (79 - x) - 0x38, 0xa5, 0x00, // pushi 00a5 (165 - y) - 0x7c, // pushSelf - 0 +// Fixes bug: #6180 +const uint16 qfg1vgaSignatureMoveToCrusher[] = { + SIG_MAGICDWORD, + 0x51, 0x1f, // class Motion + 0x36, // push + 0x39, 0x4f, // pushi 4f (79 - x) + 0x38, SIG_UINT16 + 0xa5, 0x00, // pushi 00a5 (165 - y) + 0x7c, // pushSelf + SIG_END }; const uint16 qfg1vgaPatchMoveToCrusher[] = { - PATCH_ADDTOOFFSET | +3, - 0x39, 0x55, // pushi 55 (85 - x) + PATCH_ADDTOOFFSET +3, + 0x39, 0x55, // pushi 55 (85 - x) 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 }, +// Same pathfinding bug as above, where Ego is set to move to an impossible +// spot when sneaking. In GuardsTrumpet::changeState, we change the final +// location where Ego is moved from 111, 111 to 114, 114. +// Fixes bug: #6248 +const uint16 qfg1vgaSignatureMoveToCastleGate[] = { + SIG_MAGICDWORD, + 0x51, 0x1f, // class MoveTo + 0x36, // push + 0x39, 0x6f, // pushi 6f (111 - x) + 0x3c, // dup (111 - y) + 0x7c, // pushSelf + SIG_END +}; + +const uint16 qfg1vgaPatchMoveToCastleGate[] = { + PATCH_ADDTOOFFSET +3, + 0x39, 0x72, // pushi 72 (114 - x) + PATCH_END +}; + +// 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 #6249 +const uint16 qfg1vgaSignatureCheetaurDescription[] = { + SIG_MAGICDWORD, + 0x34, SIG_UINT16 + 0xb8, 0x01, // ldi 01b8 + 0x1a, // eq? + 0x31, 0x16, // bnt 16 + 0x38, SIG_UINT16 + 0x27, 0x01, // pushi 0127 + 0x39, 0x06, // pushi 06 + 0x39, 0x03, // pushi 03 + 0x78, // push1 + 0x39, 0x12, // pushi 12 -> monster type Saurus Rex + SIG_END +}; + +const uint16 qfg1vgaPatchCheetaurDescription[] = { + PATCH_ADDTOOFFSET +14, + 0x39, 0x11, // pushi 11 -> monster type cheetaur + PATCH_END +}; + +// In the "funny" room (Yorick's room) in QfG1 VGA, pulling the chain and +// then pressing the button on the right side of the room results in +// a broken game. This also happens in SSCI. +// Problem is that the Sierra programmers forgot to disable the door, that +// gets opened by pulling the chain. So when ego falls down and then +// rolls through the door, one method thinks that the player walks through +// it and acts that way and the other method is still doing the roll animation. +// 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 #6181 +const uint16 qfg1vgaSignatureFunnyRoomFix[] = { + 0x65, 0x14, // aTop 14 (state) + 0x36, // push + 0x3c, // dup + 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 +}; + +const uint16 qfg1vgaPatchFunnyRoomFix[] = { + PATCH_ADDTOOFFSET +3, + 0x2e, PATCH_UINT16 + 0x29, 0x00, // bt 0029 [-> next state] - saves 4 bytes + 0x35, 0x01, // ldi 01 + 0xa3, 0x4e, // sal 4e + 0xa3, 0x05, // sal 05 (sets local 5 to 1) + 0xa3, 0x05, // and again to make absolutely sure (actually to waste 2 bytes) + PATCH_END +}; + +// script, description, signature patch +SciScriptPatcherEntry qfg1vgaSignatures[] = { + { true, 215, "fight event issue", 1, 0, 0, qfg1vgaSignatureFightEvents, qfg1vgaPatchFightEvents }, + { true, 216, "weapon master event issue", 1, 0, 0, qfg1vgaSignatureFightEvents, qfg1vgaPatchFightEvents }, + { true, 814, "window text temp space", 1, 0, 0, qfg1vgaSignatureTempSpace, qfg1vgaPatchTempSpace }, + { true, 814, "dialog header offset", 3, 0, 0, qfg1vgaSignatureDialogHeader, qfg1vgaPatchDialogHeader }, + { true, 331, "moving to crusher", 1, 0, 0, qfg1vgaSignatureMoveToCrusher, qfg1vgaPatchMoveToCrusher }, + { true, 41, "moving to castle gate", 1, 0, 0, qfg1vgaSignatureMoveToCastleGate, qfg1vgaPatchMoveToCastleGate }, + { true, 210, "cheetaur description fixed", 1, 0, 0, qfg1vgaSignatureCheetaurDescription, qfg1vgaPatchCheetaurDescription }, + { true, 96, "funny room script bug fixed", 1, 0, 0, qfg1vgaSignatureFunnyRoomFix, qfg1vgaPatchFunnyRoomFix }, SCI_SIGNATUREENTRY_TERMINATOR }; @@ -998,58 +1656,56 @@ const SciScriptSignature qfg1vgaSignatures[] = { // deleted entries. We don't allow the user to change the directory, thus the // contents of the file list are constant, so we can avoid the constant file // and text entry refreshes whenever a button is pressed, and prevent possible -// crashes because of these constant quick object reallocations. Fixes bug -// #3037996. -const byte qfg2SignatureImportDialog[] = { - 16, - 0x63, 0x20, // pToa text - 0x30, 0x0b, 0x00, // bnt [next state] - 0x7a, // push2 - 0x39, 0x03, // pushi 03 - 0x36, // push - 0x43, 0x72, 0x04, // callk Memory 4 - 0x35, 0x00, // ldi 00 - 0x65, 0x20, // aTop text - 0 +// crashes because of these constant quick object reallocations. +// Fixes bug: #5096 +const uint16 qfg2SignatureImportDialog[] = { + 0x63, SIG_MAGICDWORD, 0x20, // pToa text + 0x30, SIG_UINT16 + 0x0b, 0x00, // bnt [next state] + 0x7a, // push2 + 0x39, 0x03, // pushi 03 + 0x36, // push + 0x43, 0x72, 0x04, // callk Memory 4 + 0x35, 0x00, // ldi 00 + 0x65, 0x20, // aTop text + SIG_END }; const uint16 qfg2PatchImportDialog[] = { - PATCH_ADDTOOFFSET | +5, - 0x48, // ret + PATCH_ADDTOOFFSET +5, + 0x48, // ret 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[] = { + { true, 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 byte qfg3SignatureImportDialog[] = { - 15, - 0x63, 0x2a, // pToa text - 0x31, 0x0b, // bnt [next state] - 0x7a, // push2 - 0x39, 0x03, // pushi 03 - 0x36, // push - 0x43, 0x72, 0x04, // callk Memory 4 - 0x35, 0x00, // ldi 00 - 0x65, 0x2a, // aTop text - 0 +const uint16 qfg3SignatureImportDialog[] = { + 0x63, SIG_MAGICDWORD, 0x2a, // pToa text + 0x31, 0x0b, // bnt [next state] + 0x7a, // push2 + 0x39, 0x03, // pushi 03 + 0x36, // push + 0x43, 0x72, 0x04, // callk Memory 4 + 0x35, 0x00, // ldi 00 + 0x65, 0x2a, // aTop text + SIG_END }; const uint16 qfg3PatchImportDialog[] = { - PATCH_ADDTOOFFSET | +4, - 0x48, // ret + PATCH_ADDTOOFFSET +4, + 0x48, // ret PATCH_END }; // =========================================================================== -// Patch for the Woo dialog option in Uhura's conversation. Bug #3040722 +// Patch for the Woo dialog option in Uhura's conversation. // Problem: The Woo dialog option (0xffb5) is negative, and therefore // treated as an option opening a submenu. This leads to uhuraTell::doChild // being called, which calls hero::solvePuzzle and then proceeds with @@ -1061,38 +1717,40 @@ 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. - -const byte qfg3SignatureWooDialog[] = { - 30, - 0x67, 0x12, // pTos 12 (query) - 0x35, 0xb6, // ldi b6 - 0x1a, // eq? - 0x2f, 0x05, // bt 05 - 0x67, 0x12, // pTos 12 (query) - 0x35, 0x9b, // ldi 9b - 0x1a, // eq? - 0x31, 0x0c, // bnt 0c - 0x38, 0x97, 0x02, // pushi 0297 - 0x7a, // push2 - 0x38, 0x0c, 0x01, // pushi 010c - 0x7a, // push2 - 0x81, 0x00, // lag 00 - 0x4a, 0x08, // send 08 - 0x67, 0x12, // pTos 12 (query) - 0x35, 0xb5, // ldi b5 - 0 +// Applies to at least: English, German, Italian, French, Spanish Floppy +// Responsible method: unknown +// Fixes bug: #5172 +const uint16 qfg3SignatureWooDialog[] = { + SIG_MAGICDWORD, + 0x67, 0x12, // pTos 12 (query) + 0x35, 0xb6, // ldi b6 + 0x1a, // eq? + 0x2f, 0x05, // bt 05 + 0x67, 0x12, // pTos 12 (query) + 0x35, 0x9b, // ldi 9b + 0x1a, // eq? + 0x31, 0x0c, // bnt 0c + 0x38, SIG_SELECTOR16 + SELECTOR_solvePuzzle, // pushi 0297 + 0x7a, // push2 + 0x38, SIG_UINT16 + 0x0c, 0x01, // pushi 010c + 0x7a, // push2 + 0x81, 0x00, // lag 00 + 0x4a, 0x08, // send 08 + 0x67, 0x12, // pTos 12 (query) + 0x35, 0xb5, // ldi b5 + SIG_END }; const uint16 qfg3PatchWooDialog[] = { - PATCH_ADDTOOFFSET | +0x29, - 0x33, 0x11, // jmp to 0x6a2, the call to hero::solvePuzzle for 0xFFFC + PATCH_ADDTOOFFSET +0x29, + 0x33, 0x11, // jmp to 0x6a2, the call to hero::solvePuzzle for 0xFFFC 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[] = { + { true, 944, "import dialog continuous calls", 1, 0, 0, qfg3SignatureImportDialog, qfg3PatchImportDialog }, + { true, 440, "dialog crash when asking about Woo", 1, 0, 0, qfg3SignatureWooDialog, qfg3PatchWooDialog }, SCI_SIGNATUREENTRY_TERMINATOR }; @@ -1102,31 +1760,21 @@ const SciScriptSignature qfg3Signatures[] = { // adds it to nest::x. The problem is that the script also checks if x exceeds // we never reach that of course, so the pterodactyl-flight will go endlessly // we could either calculate property count differently somehow fixing this -// but I think just patching it out is cleaner (bug #3037938) -const byte sq4FloppySignatureEndlessFlight[] = { - 8, - 0x39, 0x04, // pushi 04 (selector x) - 0x78, // push1 - 0x67, 0x08, // pTos 08 (property x) - 0x63, 0x44, // pToa 44 (invalid property) - 0x02, // add - 0 -}; - -// Similar to the above, for the German version (bug #3110215) -const byte sq4FloppySignatureEndlessFlightGerman[] = { - 8, - 0x39, 0x04, // pushi 04 (selector x) - 0x78, // push1 - 0x67, 0x08, // pTos 08 (property x) - 0x63, 0x4c, // pToa 4c (invalid property) - 0x02, // add - 0 +// but I think just patching it out is cleaner. +// Fixes bug: #5093 +const uint16 sq4FloppySignatureEndlessFlight[] = { + 0x39, 0x04, // pushi 04 (selector x) + SIG_MAGICDWORD, + 0x78, // push1 + 0x67, 0x08, // pTos 08 (property x) + 0x63, SIG_ADDTOOFFSET + 1, // pToa (invalid property) - 44h for English floppy, 4ch for German floppy + 0x02, // add + SIG_END }; const uint16 sq4FloppyPatchEndlessFlight[] = { - PATCH_ADDTOOFFSET | +5, - 0x35, 0x03, // ldi 03 (which would be the content of the property) + PATCH_ADDTOOFFSET +5, + 0x35, 0x03, // ldi 03 (which would be the content of the property) PATCH_END }; @@ -1136,40 +1784,40 @@ const uint16 sq4FloppyPatchEndlessFlight[] = { // Patch 1: iconTextSwitch::show, called when the text options button is shown. // This is patched to add the "Both" text resource (i.e. we end up with // "Speech", "Text" and "Both") -const byte sq4CdSignatureTextOptionsButton[] = { - 11, - 0x35, 0x01, // ldi 0x01 - 0xa1, 0x53, // sag 0x53 - 0x39, 0x03, // pushi 0x03 - 0x78, // push1 - 0x39, 0x09, // pushi 0x09 - 0x54, 0x06, // self 0x06 - 0 +const uint16 sq4CdSignatureTextOptionsButton[] = { + SIG_MAGICDWORD, + 0x35, 0x01, // ldi 0x01 + 0xa1, 0x53, // sag 0x53 + 0x39, 0x03, // pushi 0x03 + 0x78, // push1 + 0x39, 0x09, // pushi 0x09 + 0x54, 0x06, // self 0x06 + SIG_END }; const uint16 sq4CdPatchTextOptionsButton[] = { - PATCH_ADDTOOFFSET | +7, - 0x39, 0x0b, // pushi 0x0b + PATCH_ADDTOOFFSET +7, + 0x39, 0x0b, // pushi 0x0b PATCH_END }; // Patch 2: Adjust a check in babbleIcon::init, which handles the babble icon // (e.g. the two guys from Andromeda) shown when dying/quitting. -// Fixes bug #3538418. -const byte sq4CdSignatureBabbleIcon[] = { - 7, - 0x89, 0x5a, // lsg 5a - 0x35, 0x02, // ldi 02 - 0x1a, // eq? - 0x31, 0x26, // bnt 26 [02a7] - 0 +// Fixes bug: #6068 +const uint16 sq4CdSignatureBabbleIcon[] = { + SIG_MAGICDWORD, + 0x89, 0x5a, // lsg 5a + 0x35, 0x02, // ldi 02 + 0x1a, // eq? + 0x31, 0x26, // bnt 26 [02a7] + SIG_END }; const uint16 sq4CdPatchBabbleIcon[] = { - 0x89, 0x5a, // lsg 5a - 0x35, 0x01, // ldi 01 - 0x1a, // eq? - 0x2f, 0x26, // bt 26 [02a7] + 0x89, 0x5a, // lsg 5a + 0x35, 0x01, // ldi 01 + 0x1a, // eq? + 0x2f, 0x26, // bt 26 [02a7] PATCH_END }; @@ -1177,111 +1825,198 @@ const uint16 sq4CdPatchBabbleIcon[] = { // when the text options button is clicked: "Speech", "Text" and "Both". // Refer to the patch above for additional details. // iconTextSwitch::doit (called when the text options button is clicked) -const byte sq4CdSignatureTextOptions[] = { - 32, - 0x89, 0x5a, // lsg 0x5a (load global 90 to stack) - 0x3c, // dup - 0x35, 0x01, // ldi 0x01 - 0x1a, // eq? (global 90 == 1) - 0x31, 0x06, // bnt 0x06 (0x0691) - 0x35, 0x02, // ldi 0x02 - 0xa1, 0x5a, // sag 0x5a (save acc to global 90) - 0x33, 0x0a, // jmp 0x0a (0x69b) - 0x3c, // dup - 0x35, 0x02, // ldi 0x02 - 0x1a, // eq? (global 90 == 2) - 0x31, 0x04, // bnt 0x04 (0x069b) - 0x35, 0x01, // ldi 0x01 - 0xa1, 0x5a, // sag 0x5a (save acc to global 90) - 0x3a, // toss - 0x38, 0xd9, 0x00, // pushi 0x00d9 - 0x76, // push0 - 0x54, 0x04, // self 0x04 - 0x48, // ret - 0 +const uint16 sq4CdSignatureTextOptions[] = { + SIG_MAGICDWORD, + 0x89, 0x5a, // lsg 0x5a (load global 90 to stack) + 0x3c, // dup + 0x35, 0x01, // ldi 0x01 + 0x1a, // eq? (global 90 == 1) + 0x31, 0x06, // bnt 0x06 (0x0691) + 0x35, 0x02, // ldi 0x02 + 0xa1, 0x5a, // sag 0x5a (save acc to global 90) + 0x33, 0x0a, // jmp 0x0a (0x69b) + 0x3c, // dup + 0x35, 0x02, // ldi 0x02 + 0x1a, // eq? (global 90 == 2) + 0x31, 0x04, // bnt 0x04 (0x069b) + 0x35, 0x01, // ldi 0x01 + 0xa1, 0x5a, // sag 0x5a (save acc to global 90) + 0x3a, // toss + 0x38, SIG_SELECTOR16 + SELECTOR_show, // pushi 0x00d9 + 0x76, // push0 + 0x54, 0x04, // self 0x04 + 0x48, // ret + SIG_END }; const uint16 sq4CdPatchTextOptions[] = { - 0x89, 0x5a, // lsg 0x5a (load global 90 to stack) - 0x3c, // dup - 0x35, 0x03, // ldi 0x03 (acc = 3) - 0x1a, // eq? (global 90 == 3) - 0x2f, 0x07, // bt 0x07 - 0x89, 0x5a, // lsg 0x5a (load global 90 to stack again) - 0x35, 0x01, // ldi 0x01 (acc = 1) - 0x02, // add: acc = global 90 (on stack) + 1 (previous acc value) - 0x33, 0x02, // jmp 0x02 - 0x35, 0x01, // ldi 0x01 (reset acc to 1) - 0xa1, 0x5a, // sag 0x5a (save acc to global 90) - 0x33, 0x03, // jmp 0x03 (jump over the wasted bytes below) - 0x34, 0x00, 0x00, // ldi 0x0000 (waste 3 bytes) - 0x3a, // toss + 0x89, 0x5a, // lsg 0x5a (load global 90 to stack) + 0x3c, // dup + 0x35, 0x03, // ldi 0x03 (acc = 3) + 0x1a, // eq? (global 90 == 3) + 0x2f, 0x07, // bt 0x07 + 0x89, 0x5a, // lsg 0x5a (load global 90 to stack again) + 0x35, 0x01, // ldi 0x01 (acc = 1) + 0x02, // add: acc = global 90 (on stack) + 1 (previous acc value) + 0x33, 0x02, // jmp 0x02 + 0x35, 0x01, // ldi 0x01 (reset acc to 1) + 0xa1, 0x5a, // sag 0x5a (save acc to global 90) + 0x33, 0x03, // jmp 0x03 (jump over the wasted bytes below) + 0x34, PATCH_UINT16 + 0x00, 0x00, // ldi 0x0000 (waste 3 bytes) + 0x3a, // toss // (the rest of the code is the same) 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, signature patch +SciScriptPatcherEntry sq4Signatures[] = { + { true, 298, "Floppy: endless flight", 1, 0, 0, sq4FloppySignatureEndlessFlight, sq4FloppyPatchEndlessFlight }, + { true, 818, "CD: Speech and subtitles option", 1, 0, 0, sq4CdSignatureTextOptions, sq4CdPatchTextOptions }, + { true, 0, "CD: Babble icon speech and subtitles fix", 1, 0, 0, sq4CdSignatureBabbleIcon, sq4CdPatchBabbleIcon }, + { true, 818, "CD: Speech and subtitles option button", 1, 0, 0, sq4CdSignatureTextOptionsButton, sq4CdPatchTextOptionsButton }, SCI_SIGNATUREENTRY_TERMINATOR }; -const byte sq1vgaSignatureEgoShowsCard[] = { - 25, - 0x38, 0x46, 0x02, // push 0x246 (set up send frame to set timesShownID) - 0x78, // push1 - 0x38, 0x46, 0x02, // push 0x246 (set up send frame to get timesShownID) - 0x76, // push0 - 0x51, 0x7c, // class DeltaurRegion - 0x4a, 0x04, // send 0x04 (get timesShownID) - 0x36, // push - 0x35, 0x01, // ldi 1 - 0x02, // add - 0x36, // push - 0x51, 0x7c, // class DeltaurRegion - 0x4a, 0x06, // send 0x06 (set timesShownID) - 0x36, // push (wrong, acc clobbered by class, above) - 0x35, 0x03, // ldi 0x03 - 0x22, // lt? - 0}; +// =========================================================================== +// When you leave Ulence Flats, another timepod is supposed to appear. +// On fast machines, that timepod appears fully immediately and then +// starts to appear like it should be. That first appearance is caused +// by the scripts setting an invalid cel number and the machine being +// so fast that there is no time for another script to actually fix +// the cel number. On slower machines, the cel number gets fixed +// by the cycler and that's why only fast machines are affected. +// The same issue happens in Sierra SCI. +// We simply set the correct starting cel number to fix the bug. +// Responsible method: robotIntoShip::changeState(9) +const uint16 sq1vgaSignatureUlenceFlatsTimepodGfxGlitch[] = { + 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) + SIG_END +}; + +const uint16 sq1vgaPatchUlenceFlatsTimepodGfxGlitch[] = { + PATCH_ADDTOOFFSET +3, + 0x39, 0x09, // pushi 0x09 (set ship::cel to 9) + PATCH_END +}; + +const uint16 sq1vgaSignatureEgoShowsCard[] = { + SIG_MAGICDWORD, + 0x38, SIG_SELECTOR16 + SELECTOR_timesShownID, // push "timesShownID" + 0x78, // push1 + 0x38, SIG_SELECTOR16 + SELECTOR_timesShownID, // push "timesShownID" + 0x76, // push0 + 0x51, 0x7c, // class DeltaurRegion + 0x4a, 0x04, // send 0x04 (get timesShownID) + 0x36, // push + 0x35, 0x01, // ldi 1 + 0x02, // add + 0x36, // push + 0x51, 0x7c, // class DeltaurRegion + 0x4a, 0x06, // send 0x06 (set timesShownID) + 0x36, // push (wrong, acc clobbered by class, above) + 0x35, 0x03, // ldi 0x03 + 0x22, // lt? + SIG_END +}; // Note that this script patch is merely a reordering of the // instructions in the original script. const uint16 sq1vgaPatchEgoShowsCard[] = { - 0x38, 0x46, 0x02, // push 0x246 (set up send frame to get timesShownID) - 0x76, // push0 - 0x51, 0x7c, // class DeltaurRegion - 0x4a, 0x04, // send 0x04 (get timesShownID) - 0x36, // push - 0x35, 0x01, // ldi 1 - 0x02, // add - 0x36, // push (this push corresponds to the wrong one above) - 0x38, 0x46, 0x02, // push 0x246 (set up send frame to set timesShownID) - 0x78, // push1 - 0x36, // push - 0x51, 0x7c, // class DeltaurRegion - 0x4a, 0x06, // send 0x06 (set timesShownID) - 0x35, 0x03, // ldi 0x03 - 0x22, // lt? - PATCH_END}; - - -// script, description, magic DWORD, adjust -const SciScriptSignature sq1vgaSignatures[] = { - { 58, "Sarien armory droid zapping ego first time", 1, PATCH_MAGICDWORD( 0x72, 0x88, 0x15, 0x36 ), -70, - sq1vgaSignatureEgoShowsCard, sq1vgaPatchEgoShowsCard }, + 0x38, PATCH_SELECTOR16 + SELECTOR_timesShownID, // push "timesShownID" + 0x76, // push0 + 0x51, 0x7c, // class DeltaurRegion + 0x4a, 0x04, // send 0x04 (get timesShownID) + 0x36, // push + 0x35, 0x01, // ldi 1 + 0x02, // add + 0x36, // push (this push corresponds to the wrong one above) + 0x38, PATCH_SELECTOR16 + SELECTOR_timesShownID, // push "timesShownID" + 0x78, // push1 + 0x36, // push + 0x51, 0x7c, // class DeltaurRegion + 0x4a, 0x06, // send 0x06 (set timesShownID) + 0x35, 0x03, // ldi 0x03 + 0x22, // lt? + PATCH_END +}; + +// script, description, signature patch +SciScriptPatcherEntry sq1vgaSignatures[] = { + { true, 45, "Ulence Flats: timepod graphic glitch", 1, 0, 0, sq1vgaSignatureUlenceFlatsTimepodGfxGlitch, sq1vgaPatchUlenceFlatsTimepodGfxGlitch }, + { true, 58, "Sarien armory droid zapping ego first time", 1, 0, 0, sq1vgaSignatureEgoShowsCard, sq1vgaPatchEgoShowsCard }, SCI_SIGNATUREENTRY_TERMINATOR}; +// =========================================================================== +// The toolbox in sq5 is buggy. When you click on the upper part of the "put +// in inventory"-button (some items only - for example the hole puncher - at the +// upper left), points will get awarded correctly and the item will get put into +// the player's inventory, but you will then get a "not here" message and the +// item will also remain to be the current mouse cursor. +// The bug report also says that items may get lost. I wasn't able to reproduce +// that part. +// This is caused by the mouse-click event getting reprocessed (which wouldn't +// be a problem by itself) and during this reprocessing coordinates are not +// processed the same as during the first click (script 226 includes a local +// subroutine, which checks coordinates in a hardcoded way w/o port-adjustment). +// Because of this, the hotspot for the button is lower than it should be, which +// then results in the game thinking that the user didn't click on the button +// and also results in the previously mentioned message. +// This happened in Sierra SCI as well (of course). +// We fix it by combining state 0 + 1 of takeTool::changeState and so stopping +// the event to get reprocessed. This was the only way possible, because everything +// else is done in SCI system scripts and I don't want to touch those. +// Applies to at least: English/German/French PC floppy +// Responsible method: takeTool::changeState +// Fixes bug: #6457 +const uint16 sq5SignatureToolboxFix[] = { + 0x31, 0x13, // bnt [check for state 1] + SIG_MAGICDWORD, + 0x38, SIG_UINT16 + 0xaa, 0x00, // pushi 00aa + 0x39, 0x05, // pushi 05 + 0x39, 0x16, // pushi 16 + 0x76, // push0 + 0x39, 0x03, // pushi 03 + 0x76, // push0 + 0x7c, // pushSelf + 0x81, 0x5b, // lag 5b + 0x4a, 0x0e, // send 0e + 0x32, SIG_UINT16 + 0x88, 0x00, // jmp [end-of-method] + 0x3c, // dup + 0x35, 0x01, // ldi 01 + 0x1a, // eq? + 0x31, 0x28, // bnt [check for state 2] + SIG_END +}; + +const uint16 sq5PatchToolboxFix[] = { + 0x31, 0x41, // bnt [check for state 2] + PATCH_ADDTOOFFSET +16, // skip to jmp offset + 0x35, 0x01, // ldi 01 + 0x65, 0x14, // aTop [state] + 0x36, 0x00, 0x00, // ldi 0000 (waste 3 bytes) + 0x35, 0x00, // ldi 00 (waste 2 bytes) + PATCH_END +}; + +// script, description, signature patch +SciScriptPatcherEntry sq5Signatures[] = { + { true, 226, "toolbox fix", 1, 0, 0, sq5SignatureToolboxFix, sq5PatchToolboxFix }, + SCI_SIGNATUREENTRY_TERMINATOR +}; + + // will actually patch previously found signature area -void Script::applyPatch(const uint16 *patch, byte *scriptData, const uint32 scriptSize, int32 signatureOffset) { +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; @@ -1290,74 +2025,179 @@ 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) { - case PATCH_ADDTOOFFSET: + switch (patchCommand) { + case PATCH_ADDTOOFFSET: { // add value to offset - offset += patchValue & ~PATCH_ADDTOOFFSET; + offset += patchValue; break; - case PATCH_GETORIGINALBYTE: + } + 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; - 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); + } + case PATCH_GETORIGINALBYTEADJUST: { + // get original byte from script and adjust it + if (patchValue >= orgDataSize) + error("Script-Patcher: can not get requested original byte from script"); + byte orgByte = orgData[patchValue]; + int16 adjustValue; + patchData++; adjustValue = (int16)(*patchData); + scriptData[offset] = orgByte + adjustValue; + offset++; + break; + } + case PATCH_UINT16: + case PATCH_SELECTOR16: { + byte byte1; + byte byte2; + + 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; + } + if (!isMacSci11) { + scriptData[offset++] = byte1; + scriptData[offset++] = byte2; + } else { + // SCI1.1+ on macintosh had uint16s in script in BE-order + scriptData[offset++] = byte2; + scriptData[offset++] = byte1; + } 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); + 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; } - default: - scriptData[offset] = patchValue & 0xFF; + 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) { +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 byte *signatureData = signature->data; - byte matchAdjust = 1; - while (matchAdjust) { - byte matchBytesCount = *signatureData++; - if ((byteOffset + matchBytesCount) > scriptSize) // Out-Of-Bounds? + const uint16 *signatureData = patchEntry->signatureData; + uint16 sigSelector = 0; + + uint16 sigWord = *signatureData; + while (sigWord != SIG_END) { + uint16 sigCommand = sigWord & SIG_COMMANDMASK; + uint16 sigValue = sigWord & SIG_VALUEMASK; + switch (sigCommand) { + case SIG_ADDTOOFFSET: { + // add value to offset + byteOffset += sigValue; + break; + } + case SIG_UINT16: + case SIG_SELECTOR16: { + if ((byteOffset + 1) < scriptSize) { + byte byte1; + byte byte2; + + 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; + } + if (!isMacSci11) { + if ((scriptData[byteOffset] != byte1) || (scriptData[byteOffset + 1] != byte2)) + sigWord = SIG_MISMATCH; + } else { + // SCI1.1+ on macintosh had uint16s in script in BE-order + if ((scriptData[byteOffset] != byte2) || (scriptData[byteOffset + 1] != byte1)) + sigWord = SIG_MISMATCH; + } + byteOffset += 2; + } else { + sigWord = SIG_MISMATCH; + } break; - if (memcmp(signatureData, &scriptData[byteOffset], matchBytesCount)) // Byte-Mismatch? + } + 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 { + sigWord = SIG_MISMATCH; // out of bounds + } + } + + if (sigWord == SIG_MISMATCH) break; - // those bytes matched, adjust offsets accordingly - signatureData += matchBytesCount; - byteOffset += matchBytesCount; - // get offset... - matchAdjust = *signatureData++; - byteOffset += matchAdjust; + + signatureData++; + sigWord = *signatureData; } - if (!matchAdjust) // all matches worked? + + if (sigWord == SIG_END) // signature fully matched? return offset; } DWordOffset++; @@ -1366,9 +2206,164 @@ 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; - switch (g_sci->getGameId()) { +// 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++; + } +} + +// This method enables certain patches +// It's used for patches, which are not meant to get applied all the time +void Script::patcherEnablePatch(SciScriptPatcherEntry *patchTable, const char *searchDescription) { + SciScriptPatcherEntry *curEntry = patchTable; + int searchDescriptionLen = strlen( searchDescription ); + int matchCount = 0; + + while (curEntry->signatureData) { + if (strncmp(curEntry->description, searchDescription, searchDescriptionLen) == 0) { + // match found, enable patch + curEntry->active = true; + matchCount++; + } + curEntry++; + } + + if (!matchCount) + error("Script-Patcher: no patch found to enable"); +} + +void Script::patcherProcessScript(uint16 scriptNr, byte *scriptData, const uint32 scriptSize) { + SciScriptPatcherEntry *signatureTable = NULL; + const Sci::SciGameId gameId = g_sci->getGameId(); + + switch (gameId) { + case GID_CAMELOT: + signatureTable = camelotSignatures; + break; case GID_ECOQUEST: signatureTable = ecoquest1Signatures; break; @@ -1385,11 +2380,7 @@ void Script::matchSignatureAndPatch(uint16 scriptNr, byte *scriptData, const uin signatureTable = gk1Signatures; break; case GID_KQ5: - // See the explanation in the kq5SignatureWinGMSignals comment - if (g_sci->_features->useAltWinGMSound()) - signatureTable = kq5WinGMSignatures; - else - signatureTable = kq5Signatures; + signatureTable = kq5Signatures; break; case GID_KQ6: signatureTable = kq6Signatures; @@ -1400,12 +2391,21 @@ void Script::matchSignatureAndPatch(uint16 scriptNr, byte *scriptData, const uin case GID_LONGBOW: signatureTable = longbowSignatures; break; + case GID_LSL2: + signatureTable = larry2Signatures; + break; + case GID_LSL5: + signatureTable = larry5Signatures; + break; case GID_LSL6: signatureTable = larry6Signatures; break; case GID_MOTHERGOOSE256: signatureTable = mothergoose256Signatures; break; + case GID_PQ1: + signatureTable = pq1vgaSignatures; + break; case GID_QFG1VGA: signatureTable = qfg1vgaSignatures; break; @@ -1421,21 +2421,53 @@ void Script::matchSignatureAndPatch(uint16 scriptNr, byte *scriptData, const uin case GID_SQ4: signatureTable = sq4Signatures; break; + case GID_SQ5: + signatureTable = sq5Signatures; + break; default: break; } if (signatureTable) { - while (signatureTable->data) { - if (scriptNr == signatureTable->scriptNr) { + bool isMacSci11 = (g_sci->getPlatform() == Common::kPlatformMacintosh && getSciVersion() >= SCI_VERSION_1_1); + + if (!signatureTable->magicDWord) { + // Abort, in case selectors are not yet initialized (happens for games w/o selector-dictionary) + if (!g_sci->getKernel()->selectorNamesAvailable()) + return; + + // signature table needs to get initialized (Magic DWORD set, selector table set) + patcherInitSignature(signatureTable, isMacSci11); + + // Do additional game-specific initialization + switch (gameId) { + case GID_KQ5: + if (g_sci->_features->useAltWinGMSound()) { + // See the explanation in the kq5SignatureWinGMSignals comment + patcherEnablePatch(signatureTable, "Win: GM Music signal checks"); + } + break; + case GID_LAURABOW2: + if (g_sci->speechAndSubtitlesEnabled()) { + // Enables Audio + subtitles patches for Laura Bow 2, when "Text and Speech: Both" is selected + patcherEnablePatch(signatureTable, "CD: audio + text support"); + } + break; + default: + break; + } + } + + while (signatureTable->signatureData) { + if ( (scriptNr == signatureTable->scriptNr) && (signatureTable->active) ) { int32 foundOffset = 0; int16 applyCount = signatureTable->applyCount; do { - foundOffset = findSignature(signatureTable, scriptData, scriptSize); + foundOffset = patcherFindSignature(signatureTable, scriptData, scriptSize, isMacSci11); if (foundOffset != -1) { // found, so apply the patch - debugC(kDebugLevelScripts, "matched and patched %s on script %d offset %d", signatureTable->description, scriptNr, foundOffset); - applyPatch(signatureTable->patch, scriptData, scriptSize, foundOffset); + 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)); diff --git a/engines/sci/engine/seg_manager.cpp b/engines/sci/engine/seg_manager.cpp index 97e33f256b..a059bee74e 100644 --- a/engines/sci/engine/seg_manager.cpp +++ b/engines/sci/engine/seg_manager.cpp @@ -262,8 +262,14 @@ const char *SegManager::getObjectName(reg_t pos) { const char *name = 0; if (nameReg.getSegment()) name = derefString(nameReg); - if (!name) - return "<invalid name>"; + if (!name) { + // Crazy Nick Laura Bow is missing some object names needed for the static + // selector vocabulary + if (g_sci->getGameId() == GID_CNICK_LAURABOW && pos == make_reg(1, 0x2267)) + return "Character"; + else + return "<invalid name>"; + } return name; } diff --git a/engines/sci/engine/seg_manager.h b/engines/sci/engine/seg_manager.h index 074d3f6b0a..0667b75651 100644 --- a/engines/sci/engine/seg_manager.h +++ b/engines/sci/engine/seg_manager.h @@ -224,7 +224,7 @@ public: * Allocate a fresh chunk of the hunk * @param[in] size Number of bytes to allocate for the hunk entry * @param[in] hunk_type A descriptive string for the hunk entry, for - * debugging purposes + * debugging purposes * @return The offset of the freshly allocated hunk entry */ reg_t allocateHunkEntry(const char *hunk_type, int size); diff --git a/engines/sci/engine/vm.cpp b/engines/sci/engine/vm.cpp index ef8f165084..d7c2fdc0eb 100644 --- a/engines/sci/engine/vm.cpp +++ b/engines/sci/engine/vm.cpp @@ -200,11 +200,18 @@ static void write_var(EngineState *s, int type, int index, reg_t value) { s->variables[type][index] = value; - // If the game is trying to change its speech/subtitle settings, apply the ScummVM audio - // options first, if they haven't been applied yet - if (type == VAR_GLOBAL && index == 90 && !g_sci->getEngineState()->_syncedAudioOptions) { - g_sci->syncIngameAudioOptions(); - g_sci->getEngineState()->_syncedAudioOptions = true; + if (type == VAR_GLOBAL && index == 90) { + // The game is trying to change its speech/subtitle settings + if (!g_sci->getEngineState()->_syncedAudioOptions || s->variables[VAR_GLOBAL][4] == TRUE_REG) { + // ScummVM audio options haven't been applied yet, so apply them. + // We also force the ScummVM audio options when loading a game from + // the launcher. + g_sci->syncIngameAudioOptions(); + g_sci->getEngineState()->_syncedAudioOptions = true; + } else { + // Update ScummVM's audio options + g_sci->updateScummVMAudioOptions(); + } } } } diff --git a/engines/sci/engine/vm_types.cpp b/engines/sci/engine/vm_types.cpp index 27015d9be4..5327dd1a2e 100644 --- a/engines/sci/engine/vm_types.cpp +++ b/engines/sci/engine/vm_types.cpp @@ -28,12 +28,12 @@ namespace Sci { -reg_t reg_t::lookForWorkaround(const reg_t right) const { +reg_t reg_t::lookForWorkaround(const reg_t right, const char *operation) const { SciTrackOriginReply originReply; SciWorkaroundSolution solution = trackOriginAndFindWorkaround(0, arithmeticWorkarounds, &originReply); if (solution.type == WORKAROUND_NONE) - error("Invalid arithmetic operation (params: %04x:%04x and %04x:%04x) from method %s::%s (room %d, script %d, localCall %x)", - PRINT_REG(*this), PRINT_REG(right), originReply.objectName.c_str(), + error("Invalid arithmetic operation (%s - params: %04x:%04x and %04x:%04x) from method %s::%s (room %d, script %d, localCall %x)", + operation, PRINT_REG(*this), PRINT_REG(right), originReply.objectName.c_str(), originReply.methodName.c_str(), g_sci->getEngineState()->currentRoomNumber(), originReply.scriptNr, originReply.localCallOffset); assert(solution.type == WORKAROUND_FAKE); @@ -55,7 +55,7 @@ reg_t reg_t::operator+(const reg_t right) const { case SEG_TYPE_DYNMEM: return make_reg(getSegment(), getOffset() + right.toSint16()); default: - return lookForWorkaround(right); + return lookForWorkaround(right, "addition"); } } else if (isNumber() && right.isPointer()) { // Adding a pointer to a number, flip the order @@ -64,7 +64,7 @@ reg_t reg_t::operator+(const reg_t right) const { // Normal arithmetics return make_reg(0, toSint16() + right.toSint16()); } else { - return lookForWorkaround(right); + return lookForWorkaround(right, "addition"); } } @@ -82,14 +82,14 @@ reg_t reg_t::operator*(const reg_t right) const { if (isNumber() && right.isNumber()) return make_reg(0, toSint16() * right.toSint16()); else - return lookForWorkaround(right); + return lookForWorkaround(right, "multiplication"); } reg_t reg_t::operator/(const reg_t right) const { if (isNumber() && right.isNumber() && !right.isNull()) return make_reg(0, toSint16() / right.toSint16()); else - return lookForWorkaround(right); + return lookForWorkaround(right, "division"); } reg_t reg_t::operator%(const reg_t right) const { @@ -109,21 +109,21 @@ reg_t reg_t::operator%(const reg_t right) const { result += modulo; return make_reg(0, result); } else - return lookForWorkaround(right); + return lookForWorkaround(right, "modulo"); } reg_t reg_t::operator>>(const reg_t right) const { if (isNumber() && right.isNumber()) return make_reg(0, toUint16() >> right.toUint16()); else - return lookForWorkaround(right); + return lookForWorkaround(right, "shift right"); } reg_t reg_t::operator<<(const reg_t right) const { if (isNumber() && right.isNumber()) return make_reg(0, toUint16() << right.toUint16()); else - return lookForWorkaround(right); + return lookForWorkaround(right, "shift left"); } reg_t reg_t::operator+(int16 right) const { @@ -140,7 +140,7 @@ uint16 reg_t::requireUint16() const { else // The right parameter is NULL_REG because // we're not comparing *this with anything here. - return lookForWorkaround(NULL_REG).toUint16(); + return lookForWorkaround(NULL_REG, "require unsigned number").toUint16(); } int16 reg_t::requireSint16() const { @@ -149,28 +149,28 @@ int16 reg_t::requireSint16() const { else // The right parameter is NULL_REG because // we're not comparing *this with anything here. - return lookForWorkaround(NULL_REG).toSint16(); + return lookForWorkaround(NULL_REG, "require signed number").toSint16(); } reg_t reg_t::operator&(const reg_t right) const { if (isNumber() && right.isNumber()) return make_reg(0, toUint16() & right.toUint16()); else - return lookForWorkaround(right); + return lookForWorkaround(right, "bitwise AND"); } reg_t reg_t::operator|(const reg_t right) const { if (isNumber() && right.isNumber()) return make_reg(0, toUint16() | right.toUint16()); else - return lookForWorkaround(right); + return lookForWorkaround(right, "bitwise OR"); } reg_t reg_t::operator^(const reg_t right) const { if (isNumber() && right.isNumber()) return make_reg(0, toUint16() ^ right.toUint16()); else - return lookForWorkaround(right); + return lookForWorkaround(right, "bitwise XOR"); } int reg_t::cmp(const reg_t right, bool treatAsUnsigned) const { @@ -184,7 +184,7 @@ int reg_t::cmp(const reg_t right, bool treatAsUnsigned) const { } else if (right.pointerComparisonWithInteger(*this)) { return -1; } else - return lookForWorkaround(right).toSint16(); + return lookForWorkaround(right, "comparison").toSint16(); } bool reg_t::pointerComparisonWithInteger(const reg_t right) const { diff --git a/engines/sci/engine/vm_types.h b/engines/sci/engine/vm_types.h index 9a7589e9a7..22bd8beaa1 100644 --- a/engines/sci/engine/vm_types.h +++ b/engines/sci/engine/vm_types.h @@ -156,7 +156,7 @@ private: * - a negative number if *this < right */ int cmp(const reg_t right, bool treatAsUnsigned) const; - reg_t lookForWorkaround(const reg_t right) const; + reg_t lookForWorkaround(const reg_t right, const char *operation) const; bool pointerComparisonWithInteger(const reg_t right) const; }; diff --git a/engines/sci/engine/workarounds.cpp b/engines/sci/engine/workarounds.cpp index 6af6326042..6fdff1ce91 100644 --- a/engines/sci/engine/workarounds.cpp +++ b/engines/sci/engine/workarounds.cpp @@ -37,6 +37,7 @@ const SciWorkaroundEntry arithmeticWorkarounds[] = { { GID_ECOQUEST2, 100, 0, 0, "Rain", "points", 0xce0, 0, { WORKAROUND_FAKE, 0 } }, // Same as above, for the Spanish version - bug #3313962 { GID_FANMADE, 516, 983, 0, "Wander", "setTarget", -1, 0, { WORKAROUND_FAKE, 0 } }, // op_mul: The Legend of the Lost Jewel Demo (fan made): called with object as second parameter when attacked by insects - bug #3038913 { GID_GK1, 800,64992, 0, "Fwd", "doit", -1, 0, { WORKAROUND_FAKE, 1 } }, // op_gt: when Mosely finds Gabriel and Grace near the end of the game, compares the Grooper object with 7 + { GID_HOYLE4, 700, -1, 1, "Code", "doit", -1, 0, { WORKAROUND_FAKE, 1 } }, // op_add: while bidding in Bridge, an object ("Bid") is added to an object in another segment ("hand3") { GID_ICEMAN, 199, 977, 0, "Grooper", "doit", -1, 0, { WORKAROUND_FAKE, 0 } }, // op_add: While dancing with the girl { GID_MOTHERGOOSE256, -1, 999, 0, "Event", "new", -1, 0, { WORKAROUND_FAKE, 0 } }, // op_and: constantly during the game (SCI1 version) { GID_MOTHERGOOSE256, -1, 4, 0, "rm004", "doit", -1, 0, { WORKAROUND_FAKE, 0 } }, // op_or: when going north and reaching the castle (rooms 4 and 37) - bug #3038228 @@ -50,9 +51,16 @@ const SciWorkaroundEntry arithmeticWorkarounds[] = { // gameID, room,script,lvl, object-name, method-name, call,index, workaround const SciWorkaroundEntry uninitializedReadWorkarounds[] = { + { GID_CAMELOT, 40, 40, 0, "Rm40", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // when looking at the ground at the pool of Siloam - bug #3614968 { GID_CASTLEBRAIN, 280, 280, 0, "programmer", "dispatchEvent", -1, 0, { WORKAROUND_FAKE, 0xf } }, // pressing 'q' on the computer screen in the robot room, and closing the help dialog that pops up (bug #3039656). Moves the cursor to the view with the ID returned (in this case, the robot hand) { GID_CNICK_KQ, -1, 0, 1, "Character", "say", -1, -1, { WORKAROUND_FAKE, 0 } }, // checkers/backgammon, like in hoyle 3 - temps 504 and 505 - bug #3606025 { GID_CNICK_KQ, -1, 700, 0, "gcWindow", "open", -1, -1, { WORKAROUND_FAKE, 0 } }, // when entering the control menu, like in hoyle 3 + { GID_CNICK_KQ, 300, 303, 0, "theDoubleCube", "<noname520>", -1, 5, { WORKAROUND_FAKE, 0 } }, // while playing backgammon with doubling enabled - bug #3615121 (same as the theDoubleCube::make workaround for Hoyle 3) + { GID_CNICK_KQ, 300, 303, 0, "theDoubleCube", "<noname519>", -1, 9, { WORKAROUND_FAKE, 0 } }, // when accepting a double, while playing backgammon with doubling enabled (same as the theDoubleCube::accept workaround for Hoyle 3) + { GID_CNICK_LAURABOW, -1, 0, 1, "Character", "say", -1, -1, { WORKAROUND_FAKE, 0 } }, // Yatch, like in hoyle 3 - temps 504 and 505 - bug #3615119 + { GID_CNICK_LAURABOW, -1, 700, 0, NULL, "open", -1, -1, { WORKAROUND_FAKE, 0 } }, // when entering control menu - bug #3615118 (same as the gcWindow workaround for Hoyle 3) + { GID_CNICK_LAURABOW,100, 100, 0, NULL, "<noname144>", -1, 1, { WORKAROUND_FAKE, 0 } }, // while playing domino - bug #3615129 (same as the dominoHand2 workaround for Hoyle 3) + { GID_CNICK_LAURABOW,100, 110, 0, NULL, "doit", -1, -1, { WORKAROUND_FAKE, 0 } }, // when changing the "Dominoes per hand" setting - bug #3615130 { GID_CNICK_LONGBOW, 0, 0, 0, "RH Budget", "init", -1, 1, { WORKAROUND_FAKE, 0 } }, // when starting the game { GID_ECOQUEST, -1, -1, 0, NULL, "doVerb", -1, 0, { WORKAROUND_FAKE, 0 } }, // almost clicking anywhere triggers this in almost all rooms { GID_FANMADE, 516, 979, 0, "", "export 0", -1, 20, { WORKAROUND_FAKE, 0 } }, // Happens in Grotesteing after the logos @@ -71,12 +79,17 @@ const SciWorkaroundEntry uninitializedReadWorkarounds[] = { { GID_HOYLE3, -1, 0, 1, "Character", "say", -1, -1, { WORKAROUND_FAKE, 0 } }, // when starting checkers or dominoes, first time a character says something - temps 504 and 505 { GID_HOYLE3, -1, 700, 0, "gcWindow", "open", -1, -1, { WORKAROUND_FAKE, 0 } }, // when entering control menu { GID_HOYLE3, 100, 100, 0, "dominoHand2", "cue", -1, 1, { WORKAROUND_FAKE, 0 } }, // while playing domino - bug #3036918 + { GID_HOYLE3, 100, 110, 0, "OKButton", "doit", -1, -1, { WORKAROUND_FAKE, 0 } }, // when changing the "Dominoes per hand" setting - bug #3615130 + { GID_HOYLE3, 300, 303, 0, "theDoubleCube", "make", -1, 5, { WORKAROUND_FAKE, 0 } }, // while playing backgammon with doubling enabled + { GID_HOYLE3, 300, 303, 0, "theDoubleCube", "accept", -1, 9, { WORKAROUND_FAKE, 0 } }, // when accepting a double, while playing backgammon with doubling enabled { GID_HOYLE4, -1, 0, 0, NULL, "open", -1, -1, { WORKAROUND_FAKE, 0 } }, // when selecting "Control" from the menu (temp vars 0-3) - bug #3039294 { GID_HOYLE4, 910, 18, 0, NULL, "init", -1, 0, { WORKAROUND_FAKE, 0 } }, // during tutorial - bug #3042756 { GID_HOYLE4, 910, 910, 0, NULL, "setup", -1, 3, { WORKAROUND_FAKE, 0 } }, // when selecting "Tutorial" from the main menu - bug #3039294 - { GID_HOYLE4, 700, 718, 0, "compete_tree", "doit", -1, 75, { WORKAROUND_FAKE, 0 } }, // when placing a bid in bridge - bug #3292332 - { GID_HOYLE4, 700, 716, 0, "other1_tree", "doit", -1, 46, { WORKAROUND_FAKE, 0 } }, // sometimes when placing a bid in bridge - { GID_HOYLE4, 700, 700, 1, "BridgeHand", "calcQTS", -1, 3, { WORKAROUND_FAKE, 0 } }, // sometimes when placing a bid in bridge + { GID_HOYLE4, 700, 700, 1, "BridgeHand", "calcQTS", -1, 3, { WORKAROUND_FAKE, 0 } }, // when placing a bid in bridge (always) + { GID_HOYLE4, 700, 710, 1, "BridgeStrategyPlay", "checkSplitTops", -1, 10, { WORKAROUND_FAKE, 0 } }, // while playing bridge, objects LeadReturn_Trump, SecondSeat_Trump, ThirdSeat_Trump and others - bug #3361925 + { GID_HOYLE4, 700, -1, 1, "BridgeDefense", "think", -1, -1, { WORKAROUND_FAKE, 0 } }, // sometimes while playing bridge, temp var 3, 17 and others, objects LeadReturn_Trump, ThirdSeat_Trump and others + { GID_HOYLE4, 700, 730, 1, "BridgeDefense", "beatTheirBest", -1, 3, { WORKAROUND_FAKE, 0 } }, // rarely while playing bridge + { GID_HOYLE4, 700, -1, 1, "Code", "doit", -1, -1, { WORKAROUND_FAKE, 0 } }, // when placing a bid in bridge (always), temp var 11, 24, 27, 46, 75, objects compete_tree, compwe_tree, other1_tree, b1 - bugs #3292332 and #3361925 { GID_HOYLE4, 300, 300, 0, "", "export 2", 0x1d4d, 0, { WORKAROUND_FAKE, 0 } }, // after passing around cards in hearts { GID_HOYLE4, 400, 400, 1, "GinHand", "calcRuns", -1, 4, { WORKAROUND_FAKE, 0 } }, // sometimes while playing Gin Rummy (e.g. when knocking and placing a card) - bug #3292334 { GID_HOYLE4, 500, 17, 1, "Character", "say", -1, 504, { WORKAROUND_FAKE, 0 } }, // sometimes while playing Cribbage (e.g. when the opponent says "Last Card") - bug #3292327 @@ -97,6 +110,7 @@ const SciWorkaroundEntry uninitializedReadWorkarounds[] = { { GID_KQ6, 520, 520, 0, "rm520", "init", -1, 0, { WORKAROUND_FAKE, 0 } }, // going to boiling water trap on beast isle { GID_KQ6, -1, 903, 0, "controlWin", "open", -1, 4, { WORKAROUND_FAKE, 0 } }, // when opening the controls window (save, load etc) { GID_KQ6, -1, 907, 0, "tomato", "doVerb", -1, 2, { WORKAROUND_FAKE, 0 } }, // when looking at the rotten tomato in the inventory - bug #3059544 + { GID_KQ6, -1, 928, 0, NULL, "startText", -1, 0, { WORKAROUND_FAKE, 0 } }, // gets caused by Text+Audio support (see script patcher) { GID_KQ7, -1, 64996, 0, "User", "handleEvent", -1, 1, { WORKAROUND_FAKE, 0 } }, // called when pushing a keyboard key { GID_LAURABOW, 37, 0, 0, "CB1", "doit", -1, 1, { WORKAROUND_FAKE, 0 } }, // when going up the stairs (bug #3037694) { GID_LAURABOW, -1, 967, 0, "myIcon", "cycle", -1, 1, { WORKAROUND_FAKE, 0 } }, // having any portrait conversation coming up (initial bug #3034985) @@ -104,6 +118,7 @@ const SciWorkaroundEntry uninitializedReadWorkarounds[] = { { GID_LAURABOW2, -1, 21, 0, "dropCluesCode", "doit", -1, 1, { WORKAROUND_FAKE, 0x7fff } }, // when asking some questions (e.g. the reporter about the burglary, or the policeman about Ziggy). Must be big, as the game scripts perform lt on it and start deleting journal entries - bugs #3035068, #3036274 { GID_LAURABOW2, -1, 90, 1, "MuseumActor", "init", -1, 6, { WORKAROUND_FAKE, 0 } }, // Random actors in museum (bug #3041257) { GID_LAURABOW2, 240, 240, 0, "sSteveAnimates", "changeState", -1, 0, { WORKAROUND_FAKE, 0 } }, // Steve Dorian's idle animation at the docks - bug #3036291 + { GID_LAURABOW2, -1, 928, 0, NULL, "startText", -1, 0, { WORKAROUND_FAKE, 0 } }, // gets caused by Text+Audio support (see script patcher) { GID_LONGBOW, -1, 0, 0, "Longbow", "restart", -1, 0, { WORKAROUND_FAKE, 0 } }, // When canceling a restart game - bug #3046200 { GID_LONGBOW, -1, 213, 0, "clear", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // When giving an answer using the druid hand sign code in any room { GID_LONGBOW, -1, 213, 0, "letter", "handleEvent", 0xa8, 1, { WORKAROUND_FAKE, 0 } }, // When using the druid hand sign code in any room - bug #3036601 @@ -167,6 +182,9 @@ const SciWorkaroundEntry uninitializedReadWorkarounds[] = { { GID_SQ1, -1, 703, 0, "firePulsar", "changeState", 0x18a, 0, { WORKAROUND_FAKE, 0 } }, // export 1, but called locally (when shooting at aliens) { GID_SQ4, -1, 398, 0, "showBox", "changeState", -1, 0, { WORKAROUND_FAKE, 0 } }, // CD: called when rummaging in Software Excess bargain bin { GID_SQ4, -1, 928, -1, "Narrator", "startText", -1, 1000, { WORKAROUND_FAKE, 1 } }, // CD: happens in the options dialog and in-game when speech and subtitles are used simultaneously + { GID_SQ4, -1, 708, -1, "exitBut", "doVerb", -1, 0, { WORKAROUND_FAKE, 0 } }, // Floppy: happens, when looking at the "close" button in the sq4 hintbook - bug #6447 + { GID_SQ4, -1, 708, -1, "prevBut", "doVerb", -1, 0, { WORKAROUND_FAKE, 0 } }, // Floppy: happens, when looking at the "previous" button in the sq4 hintbook - bug #6447 + { GID_SQ4, -1, 708, -1, "nextBut", "doVerb", -1, 0, { WORKAROUND_FAKE, 0 } }, // Floppy: happens, when looking at the "next" button in the sq4 hintbook - bug #6447 { GID_SQ5, 201, 201, 0, "buttonPanel", "doVerb", -1, 0, { WORKAROUND_FAKE, 1 } }, // when looking at the orange or red button - bug #3038563 { GID_SQ6, -1, 0, 0, "SQ6", "init", -1, 2, { WORKAROUND_FAKE, 0 } }, // Demo and full version: called when the game starts (demo: room 0, full: room 100) { GID_SQ6, -1, 64950, -1, "Feature", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // called when pressing "Start game" in the main menu, when entering the Orion's Belt bar (room 300), and perhaps other places @@ -208,6 +226,7 @@ const SciWorkaroundEntry kDeviceInfo_workarounds[] = { { GID_FANMADE, -1, 994, 1, "Game", "save", 0xd1c, 0, { WORKAROUND_STILLCALL, 0 } }, // In fanmade games, this is called with one parameter for CurDevice (Cascade Quest) { GID_FANMADE, -1, 994, 1, "Game", "save", 0xe55, 0, { WORKAROUND_STILLCALL, 0 } }, // In fanmade games, this is called with one parameter for CurDevice (Demo Quest) { GID_FANMADE, -1, 994, 1, "Game", "save", 0xe57, 0, { WORKAROUND_STILLCALL, 0 } }, // In fanmade games, this is called with one parameter for CurDevice (I Want My C64 Back) + { GID_FANMADE, -1, 994, 0, "Black", "save", 0xa, 0, { WORKAROUND_IGNORE, 0 } }, // In fanmade games, this is called with one parameter for CurDevice (Black Cauldron Remake) { GID_FANMADE, -1, 994, 1, "Game", "save", 0xe5c, 0, { WORKAROUND_STILLCALL, 0 } }, // In fanmade games, this is called with one parameter for CurDevice (Most of them) { GID_FANMADE, -1, 994, 1, "Game", "restore", 0xd1c, 0, { WORKAROUND_STILLCALL, 0 } }, // In fanmade games, this is called with one parameter for CurDevice (Cascade Quest) { GID_FANMADE, -1, 994, 1, "Game", "restore", 0xe55, 0, { WORKAROUND_STILLCALL, 0 } }, // In fanmade games, this is called with one parameter for CurDevice (Demo Quest) @@ -249,6 +268,7 @@ const SciWorkaroundEntry kDoSoundFade_workarounds[] = { { GID_KQ6, 105, 989, 0, "globalSound", "fade", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // floppy: during intro, parameter 4 is an object { GID_KQ6, 460, 989, 0, "globalSound2", "fade", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // after pulling the black widow's web on the isle of wonder, parameter 4 is an object - bug #3034567 { GID_QFG4, -1, 64989, 0, "longSong", "fade", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // CD version: many places, parameter 4 is an object (longSong) + { GID_SQ5, 800, 989, 0, "sq5Music1", "fade", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // when cutting the wrong part of Goliath with the laser - bug #3614145 SCI_WORKAROUNDENTRY_TERMINATOR }; @@ -359,6 +379,13 @@ const SciWorkaroundEntry kNewWindow_workarounds[] = { }; // gameID, room,script,lvl, object-name, method-name, call,index, workaround +const SciWorkaroundEntry kReadNumber_workarounds[] = { + { GID_CNICK_LAURABOW,100, 101, 0, "dominoes.opt", "doit", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // When dominoes.opt is present, the game scripts call kReadNumber with an extra integer parameter - bug #3615120 + { GID_HOYLE3, 100, 101, 0, "dominoes.opt", "doit", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // When dominoes.opt is present, the game scripts call kReadNumber with an extra integer parameter - bug #3615120 + SCI_WORKAROUNDENTRY_TERMINATOR +}; + +// gameID, room,script,lvl, object-name, method-name, call,index, workaround const SciWorkaroundEntry kPaletteUnsetFlag_workarounds[] = { { GID_QFG4, 100, 100, 0, "doMovie", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // after the Sierra logo, no flags are passed, thus the call is meaningless - bug #3034506 SCI_WORKAROUNDENTRY_TERMINATOR diff --git a/engines/sci/engine/workarounds.h b/engines/sci/engine/workarounds.h index 59054ae552..d5e91b70ec 100644 --- a/engines/sci/engine/workarounds.h +++ b/engines/sci/engine/workarounds.h @@ -88,6 +88,7 @@ extern const SciWorkaroundEntry kIsObject_workarounds[]; extern const SciWorkaroundEntry kMemory_workarounds[]; extern const SciWorkaroundEntry kMoveCursor_workarounds[]; extern const SciWorkaroundEntry kNewWindow_workarounds[]; +extern const SciWorkaroundEntry kReadNumber_workarounds[]; extern const SciWorkaroundEntry kPaletteUnsetFlag_workarounds[]; extern const SciWorkaroundEntry kSetCursor_workarounds[]; extern const SciWorkaroundEntry kSetPort_workarounds[]; |