diff options
Diffstat (limited to 'engines/sci/engine')
-rw-r--r-- | engines/sci/engine/file.cpp | 3 | ||||
-rw-r--r-- | engines/sci/engine/kernel.cpp | 45 | ||||
-rw-r--r-- | engines/sci/engine/kernel.h | 2 | ||||
-rw-r--r-- | engines/sci/engine/kernel_tables.h | 4 | ||||
-rw-r--r-- | engines/sci/engine/kevent.cpp | 13 | ||||
-rw-r--r-- | engines/sci/engine/kfile.cpp | 5 | ||||
-rw-r--r-- | engines/sci/engine/kgraphics.cpp | 10 | ||||
-rw-r--r-- | engines/sci/engine/kmovement.cpp | 27 | ||||
-rw-r--r-- | engines/sci/engine/kparse.cpp | 3 | ||||
-rw-r--r-- | engines/sci/engine/kvideo.cpp | 2 | ||||
-rw-r--r-- | engines/sci/engine/savegame.cpp | 76 | ||||
-rw-r--r-- | engines/sci/engine/savegame.h | 8 | ||||
-rw-r--r-- | engines/sci/engine/script.cpp | 416 | ||||
-rw-r--r-- | engines/sci/engine/script.h | 36 | ||||
-rw-r--r-- | engines/sci/engine/script_patches.cpp | 743 | ||||
-rw-r--r-- | engines/sci/engine/script_patches.h | 9 | ||||
-rw-r--r-- | engines/sci/engine/state.cpp | 6 | ||||
-rw-r--r-- | engines/sci/engine/state.h | 4 | ||||
-rw-r--r-- | engines/sci/engine/vm.cpp | 18 | ||||
-rw-r--r-- | engines/sci/engine/workarounds.cpp | 955 | ||||
-rw-r--r-- | engines/sci/engine/workarounds.h | 2 |
21 files changed, 1889 insertions, 498 deletions
diff --git a/engines/sci/engine/file.cpp b/engines/sci/engine/file.cpp index 2ba7d15ac0..623caec856 100644 --- a/engines/sci/engine/file.cpp +++ b/engines/sci/engine/file.cpp @@ -252,6 +252,9 @@ void DirSeeker::addAsVirtualFiles(Common::String title, Common::String fileMask) Common::SaveFileManager *saveFileMan = g_sci->getSaveFileManager(); Common::StringArray foundFiles = saveFileMan->listSavefiles(fileMask); if (!foundFiles.empty()) { + // Sort all filenames alphabetically + Common::sort(foundFiles.begin(), foundFiles.end()); + _files.push_back(title); _virtualFiles.push_back(""); Common::StringArray::iterator it; diff --git a/engines/sci/engine/kernel.cpp b/engines/sci/engine/kernel.cpp index 2b16bb3d99..bfb7bfcd08 100644 --- a/engines/sci/engine/kernel.cpp +++ b/engines/sci/engine/kernel.cpp @@ -432,57 +432,66 @@ static const SignatureDebugType signatureDebugTypeList[] = { { 0, NULL } }; -static void kernelSignatureDebugType(const uint16 type) { +static void kernelSignatureDebugType(Common::String &signatureDetailsStr, const uint16 type) { bool firstPrint = true; const SignatureDebugType *list = signatureDebugTypeList; while (list->typeCheck) { if (type & list->typeCheck) { if (!firstPrint) - debugN(", "); - debugN("%s", list->text); +// debugN(", "); + signatureDetailsStr += ", "; +// debugN("%s", list->text); +// signatureDetailsStr += signatureDetailsStr.format("%s", list->text); + signatureDetailsStr += list->text; firstPrint = false; } list++; } } -// Shows kernel call signature and current arguments for debugging purposes -void Kernel::signatureDebug(const uint16 *sig, int argc, const reg_t *argv) { +// Create string, that holds the details of a kernel call signature and current arguments +// For debugging purposes +void Kernel::signatureDebug(Common::String &signatureDetailsStr, const uint16 *sig, int argc, const reg_t *argv) { int argnr = 0; + + // add ERROR: to debug output + debugN("ERROR:"); + while (*sig || argc) { - debugN("parameter %d: ", argnr++); + // add leading spaces for additional parameters + signatureDetailsStr += signatureDetailsStr.format("parameter %d: ", argnr++); if (argc) { reg_t parameter = *argv; - debugN("%04x:%04x (", PRINT_REG(parameter)); + signatureDetailsStr += signatureDetailsStr.format("%04x:%04x (", PRINT_REG(parameter)); int regType = findRegType(parameter); if (regType) - kernelSignatureDebugType(regType); + kernelSignatureDebugType(signatureDetailsStr, regType); else - debugN("unknown type of %04x:%04x", PRINT_REG(parameter)); - debugN(")"); + signatureDetailsStr += signatureDetailsStr.format("unknown type of %04x:%04x", PRINT_REG(parameter)); + signatureDetailsStr += ")"; argv++; argc--; } else { - debugN("not passed"); + signatureDetailsStr += "not passed"; } if (*sig) { const uint16 signature = *sig; if ((signature & SIG_MAYBE_ANY) == SIG_MAYBE_ANY) { - debugN(", may be any"); + signatureDetailsStr += ", may be any"; } else { - debugN(", should be "); - kernelSignatureDebugType(signature); + signatureDetailsStr += ", should be "; + kernelSignatureDebugType(signatureDetailsStr, signature); } if (signature & SIG_IS_OPTIONAL) - debugN(" (optional)"); + signatureDetailsStr += " (optional)"; if (signature & SIG_NEEDS_MORE) - debugN(" (needs more)"); + signatureDetailsStr += " (needs more)"; if (signature & SIG_MORE_MAY_FOLLOW) - debugN(" (more may follow)"); + signatureDetailsStr += " (more may follow)"; sig++; } - debugN("\n"); + signatureDetailsStr += "\n"; } } diff --git a/engines/sci/engine/kernel.h b/engines/sci/engine/kernel.h index a65bcb7df5..57b4d9455b 100644 --- a/engines/sci/engine/kernel.h +++ b/engines/sci/engine/kernel.h @@ -186,7 +186,7 @@ public: bool signatureMatch(const uint16 *sig, int argc, const reg_t *argv); // Prints out debug information in case a signature check fails - void signatureDebug(const uint16 *sig, int argc, const reg_t *argv); + void signatureDebug(Common::String &signatureDetails, const uint16 *sig, int argc, const reg_t *argv); /** * Determines the type of the object indicated by reg. diff --git a/engines/sci/engine/kernel_tables.h b/engines/sci/engine/kernel_tables.h index 0c2fd4e3ea..2cbd79366d 100644 --- a/engines/sci/engine/kernel_tables.h +++ b/engines/sci/engine/kernel_tables.h @@ -105,10 +105,6 @@ static const SciKernelMapSubEntry kDoSound_subops[] = { { SIG_SOUNDSCI1EARLY, 5, MAP_CALL(DoSoundInit), NULL, NULL }, { SIG_SOUNDSCI1EARLY, 6, MAP_CALL(DoSoundDispose), NULL, NULL }, { SIG_SOUNDSCI1EARLY, 7, MAP_CALL(DoSoundPlay), "oi", NULL }, - // ^^ TODO: In SCI1-SCI1.1 DoSound (play) is called by 2 methods of the Sound object: play and - // playBed. The methods are the same, apart from the second integer parameter: it's 0 in - // play and 1 in playBed, to distinguish the caller. It's passed on, we should find out what - // it actually does internally { SIG_SOUNDSCI1EARLY, 8, MAP_CALL(DoSoundStop), NULL, NULL }, { SIG_SOUNDSCI1EARLY, 9, MAP_CALL(DoSoundPause), "[o0]i", NULL }, { SIG_SOUNDSCI1EARLY, 10, MAP_CALL(DoSoundFade), "oiiii", kDoSoundFade_workarounds }, diff --git a/engines/sci/engine/kevent.cpp b/engines/sci/engine/kevent.cpp index cb81da2279..8e16e0a07a 100644 --- a/engines/sci/engine/kevent.cpp +++ b/engines/sci/engine/kevent.cpp @@ -24,9 +24,10 @@ #include "sci/sci.h" #include "sci/engine/features.h" -#include "sci/engine/state.h" -#include "sci/engine/selector.h" #include "sci/engine/kernel.h" +#include "sci/engine/savegame.h" +#include "sci/engine/selector.h" +#include "sci/engine/state.h" #include "sci/console.h" #include "sci/debug.h" // for g_debug_simulated_key #include "sci/event.h" @@ -71,9 +72,15 @@ reg_t kGetEvent(EngineState *s, int argc, reg_t *argv) { g_debug_simulated_key = 0; return make_reg(0, 1); } - + curEvent = g_sci->getEventManager()->getSciEvent(mask); + if (s->_delayedRestoreGame) { + // delayed restore game from ScummVM menu got triggered + gamestate_delayedrestore(s); + return NULL_REG; + } + // For a real event we use its associated mouse position mousePos = curEvent.mousePos; #ifdef ENABLE_SCI32 diff --git a/engines/sci/engine/kfile.cpp b/engines/sci/engine/kfile.cpp index c56eb09482..61ac76d0a7 100644 --- a/engines/sci/engine/kfile.cpp +++ b/engines/sci/engine/kfile.cpp @@ -933,6 +933,11 @@ reg_t kRestoreGame(EngineState *s, int argc, reg_t *argv) { g_sci->_gfxMenu->kernelSetAttribute(1025 >> 8, 1025 & 0xFF, SCI_MENU_ATTRIBUTE_ENABLED, TRUE_REG); // Status -> Statistics g_sci->_gfxMenu->kernelSetAttribute(1026 >> 8, 1026 & 0xFF, SCI_MENU_ATTRIBUTE_ENABLED, TRUE_REG); // Status -> Goals break; + case GID_PQ2: + // HACK: Same as above - enable the save game menu option when loading in PQ2 (bug #6875). + // It gets disabled in the game's death screen. + g_sci->_gfxMenu->kernelSetAttribute(2, 1, SCI_MENU_ATTRIBUTE_ENABLED, TRUE_REG); // Game -> Save Game + break; default: break; } diff --git a/engines/sci/engine/kgraphics.cpp b/engines/sci/engine/kgraphics.cpp index ee2249bd9d..8b790e6a58 100644 --- a/engines/sci/engine/kgraphics.cpp +++ b/engines/sci/engine/kgraphics.cpp @@ -32,6 +32,7 @@ #include "sci/event.h" #include "sci/resource.h" #include "sci/engine/features.h" +#include "sci/engine/savegame.h" #include "sci/engine/state.h" #include "sci/engine/selector.h" #include "sci/engine/kernel.h" @@ -400,6 +401,12 @@ reg_t kWait(EngineState *s, int argc, reg_t *argv) { s->wait(sleep_time); + if (s->_delayedRestoreGame) { + // delayed restore game from ScummVM menu got triggered + gamestate_delayedrestore(s); + return NULL_REG; + } + return s->r_acc; } @@ -955,8 +962,9 @@ reg_t kDrawControl(EngineState *s, int argc, reg_t *argv) { reg_t textReference = readSelector(s->_segMan, controlObject, SELECTOR(text)); if (!textReference.isNull()) { Common::String text = s->_segMan->getString(textReference); - if ((text == "a:hq1_hero.sav") || (text == "a:glory1.sav") || (text == "a:glory2.sav") || (text == "a:glory3.sav")) { + if ((text == "a:hq1_hero.sav") || (text == "a:glory1.sav") || (text == "a:glory2.sav") || (text == "a:glory3.sav") || (text == "a:gloire3.sauv")) { // Remove "a:" from hero quest / quest for glory export default filenames + // The french version of Quest For Glory 3 uses "gloire3.sauv". It seems a translator translated the filename. text.deleteChar(0); text.deleteChar(0); s->_segMan->strcpy(textReference, text.c_str()); diff --git a/engines/sci/engine/kmovement.cpp b/engines/sci/engine/kmovement.cpp index 51d49eea9f..9b83dbc52d 100644 --- a/engines/sci/engine/kmovement.cpp +++ b/engines/sci/engine/kmovement.cpp @@ -305,12 +305,23 @@ reg_t kDoBresen(EngineState *s, int argc, reg_t *argv) { for (uint i = 0; i < clientVarNum; ++i) clientBackup[i] = clientObject->getVariable(i); - if (mover_xAxis) { - if (ABS(mover_x - client_x) < ABS(mover_dx)) - completed = true; + if ((getSciVersion() <= SCI_VERSION_1_EGA_ONLY)) { + if (mover_xAxis) { + if (ABS(mover_x - client_x) < ABS(mover_dx)) + completed = true; + } else { + if (ABS(mover_y - client_y) < ABS(mover_dy)) + completed = true; + } } else { - if (ABS(mover_y - client_y) < ABS(mover_dy)) - completed = true; + // SCI1EARLY+ code + if (mover_xAxis) { + if (ABS(mover_x - client_x) <= ABS(mover_dx)) + completed = true; + } else { + if (ABS(mover_y - client_y) <= ABS(mover_dy)) + completed = true; + } } if (completed) { client_x = mover_x; @@ -336,10 +347,10 @@ reg_t kDoBresen(EngineState *s, int argc, reg_t *argv) { bool collision = false; reg_t cantBeHere = NULL_REG; + // adding this here for hoyle 3 to get happy. CantBeHere is a dummy in hoyle 3 and acc is != 0 so we would + // get a collision otherwise. Resetting the result was always done in SSCI + s->r_acc = NULL_REG; if (SELECTOR(cantBeHere) != -1) { - // adding this here for hoyle 3 to get happy. CantBeHere is a dummy in hoyle 3 and acc is != 0 so we would - // get a collision otherwise - s->r_acc = NULL_REG; invokeSelector(s, client, SELECTOR(cantBeHere), argc, argv); if (!s->r_acc.isNull()) collision = true; diff --git a/engines/sci/engine/kparse.cpp b/engines/sci/engine/kparse.cpp index aa89b963cc..f85f33e3e8 100644 --- a/engines/sci/engine/kparse.cpp +++ b/engines/sci/engine/kparse.cpp @@ -117,6 +117,8 @@ reg_t kParse(EngineState *s, int argc, reg_t *argv) { } #endif + voc->replacePronouns(words); + int syntax_fail = voc->parseGNF(words); if (syntax_fail) { @@ -130,6 +132,7 @@ reg_t kParse(EngineState *s, int argc, reg_t *argv) { } else { voc->parserIsValid = true; + voc->storePronounReference(); writeSelectorValue(segMan, event, SELECTOR(claimed), 0); #ifdef DEBUG_PARSER diff --git a/engines/sci/engine/kvideo.cpp b/engines/sci/engine/kvideo.cpp index 319469cb08..f925111fc9 100644 --- a/engines/sci/engine/kvideo.cpp +++ b/engines/sci/engine/kvideo.cpp @@ -123,6 +123,8 @@ void playVideo(Video::VideoDecoder *videoDecoder, VideoState videoState) { if ((event.type == Common::EVENT_KEYDOWN && event.kbd.keycode == Common::KEYCODE_ESCAPE) || event.type == Common::EVENT_LBUTTONUP) skipVideo = true; } + if (g_sci->getEngineState()->_delayedRestoreGame) + skipVideo = true; g_system->delayMillis(10); } diff --git a/engines/sci/engine/savegame.cpp b/engines/sci/engine/savegame.cpp index 0b55425406..93b3a997cc 100644 --- a/engines/sci/engine/savegame.cpp +++ b/engines/sci/engine/savegame.cpp @@ -20,6 +20,7 @@ * */ +#include "common/savefile.h" #include "common/stream.h" #include "common/system.h" #include "common/func.h" @@ -40,6 +41,7 @@ #include "sci/graphics/helpers.h" #include "sci/graphics/palette.h" #include "sci/graphics/ports.h" +#include "sci/graphics/screen.h" #include "sci/parser/vocabulary.h" #include "sci/sound/audio.h" #include "sci/sound/music.h" @@ -132,13 +134,6 @@ void SegManager::saveLoadWithSerializer(Common::Serializer &s) { // Reset _scriptSegMap, to be restored below _scriptSegMap.clear(); - -#ifdef ENABLE_SCI32 - // Clear any planes/screen items currently showing so they - // don't show up after the load. - if (getSciVersion() >= SCI_VERSION_2) - g_sci->_gfxFrameout->clear(); -#endif } s.skip(4, VER(14), VER(18)); // OBSOLETE: Used to be _exportsAreWide @@ -617,6 +612,14 @@ void MusicEntry::saveLoadWithSerializer(Common::Serializer &s) { s.syncAsSint32LE(fadeTicker); s.syncAsSint32LE(fadeTickerStep); s.syncAsByte(status); + if (s.getVersion() >= 32) + s.syncAsByte(playBed); + else if (s.isLoading()) + playBed = false; + if (s.getVersion() >= 33) + s.syncAsByte(overridePriority); + else if (s.isLoading()) + overridePriority = false; // pMidiParser and pStreamAud will be initialized when the // sound list is reconstructed in gamestate_restore() @@ -635,8 +638,12 @@ void SoundCommandParser::syncPlayList(Common::Serializer &s) { void SoundCommandParser::reconstructPlayList() { Common::StackLock lock(_music->_mutex); - const MusicList::iterator end = _music->getPlayListEnd(); - for (MusicList::iterator i = _music->getPlayListStart(); i != end; ++i) { + // We store all songs here because starting songs may re-shuffle their order + MusicList songs; + for (MusicList::iterator i = _music->getPlayListStart(); i != _music->getPlayListEnd(); ++i) + songs.push_back(*i); + + for (MusicList::iterator i = songs.begin(); i != songs.end(); ++i) { initSoundResource(*i); if ((*i)->status == kSoundPlaying) { @@ -650,7 +657,7 @@ void SoundCommandParser::reconstructPlayList() { if (_soundVersion >= SCI_VERSION_1_EARLY) writeSelectorValue(_segMan, (*i)->soundObj, SELECTOR(vol), (*i)->volume); - processPlaySound((*i)->soundObj); + processPlaySound((*i)->soundObj, (*i)->playBed); } } } @@ -715,9 +722,7 @@ void GfxPalette::saveLoadWithSerializer(Common::Serializer &s) { } void GfxPorts::saveLoadWithSerializer(Common::Serializer &s) { - if (s.isLoading()) - reset(); // remove all script generated windows - + // reset() is called directly way earlier in gamestate_restore() if (s.getVersion() >= 27) { uint windowCount = 0; uint id = PORTS_FIRSTSCRIPTWINDOWID; @@ -760,10 +765,17 @@ void GfxPorts::saveLoadWithSerializer(Common::Serializer &s) { if (window->counterTillFree) { _freeCounter++; } else { - if (window->wndStyle & SCI_WINDOWMGR_STYLE_TOPMOST) - _windowList.push_front(window); - else - _windowList.push_back(window); + // we don't put the saved script windows into _windowList[], so that they aren't used + // by kernel functions. This is important and would cause issues otherwise. + // see Conquests of Camelot - bug #6744 - when saving on the map screen (room 103), + // restoring would result in a black window in place + // where the area name was displayed before + // In Sierra's SCI the behaviour is identical to us + // Sierra's SCI won't show those windows after restoring + // If this should cause issues in another game, we would have to add a flag to simply + // avoid any drawing operations for such windows + // We still have to restore script windows, because for example Conquests of Camelot + // will immediately delete windows, that were created before saving the game. } windowCount--; @@ -853,6 +865,22 @@ bool gamestate_save(EngineState *s, Common::WriteStream *fh, const Common::Strin extern void showScummVMDialog(const Common::String &message); +void gamestate_delayedrestore(EngineState *s) { + Common::String fileName = g_sci->getSavegameName(s->_delayedRestoreGameId); + Common::SeekableReadStream *in = g_sci->getSaveFileManager()->openForLoading(fileName); + + if (in) { + // found a savegame file + gamestate_restore(s, in); + delete in; + if (s->r_acc != make_reg(0, 1)) { + return; + } + } + + error("Restoring gamestate '%s' failed", fileName.c_str()); +} + void gamestate_restore(EngineState *s, Common::SeekableReadStream *fh) { SavegameMetadata meta; @@ -889,6 +917,20 @@ void gamestate_restore(EngineState *s, Common::SeekableReadStream *fh) { // We don't need the thumbnail here, so just read it and discard it Graphics::skipThumbnail(*fh); + // reset ports is one of the first things we do, because that may free() some hunk memory + // and we don't want to do that after we read in the saved game hunk memory + if (g_sci->_gfxPorts) + g_sci->_gfxPorts->reset(); + // clear screen + if (g_sci->_gfxScreen) + g_sci->_gfxScreen->clearForRestoreGame(); +#ifdef ENABLE_SCI32 + // Also clear any SCI32 planes/screen items currently showing so they + // don't show up after the load. + if (getSciVersion() >= SCI_VERSION_2) + g_sci->_gfxFrameout->clear(); +#endif + s->reset(true); s->saveLoadWithSerializer(ser); // FIXME: Error handling? diff --git a/engines/sci/engine/savegame.h b/engines/sci/engine/savegame.h index 8f2835654b..5512b90fa8 100644 --- a/engines/sci/engine/savegame.h +++ b/engines/sci/engine/savegame.h @@ -37,6 +37,8 @@ struct EngineState; * * Version - new/changed feature * ============================= + * 33 - new overridePriority flag in MusicEntry + * 32 - new playBed flag in MusicEntry * 31 - priority for sound effects/music is now a signed int16, instead of a byte * 30 - synonyms * 29 - system strings @@ -56,7 +58,7 @@ struct EngineState; */ enum { - CURRENT_SAVEGAME_VERSION = 31, + CURRENT_SAVEGAME_VERSION = 33, MINIMUM_SAVEGAME_VERSION = 14 }; @@ -72,7 +74,6 @@ struct SavegameMetadata { uint16 script0Size; }; - /** * Saves a game state to the hard disk in a portable way. * @param s The state to save @@ -82,6 +83,9 @@ struct SavegameMetadata { */ bool gamestate_save(EngineState *s, Common::WriteStream *save, const Common::String &savename, const Common::String &version); +// does a delayed saved game restore, used by ScummVM game menu - see detection.cpp / SciEngine::loadGameState() +void gamestate_delayedrestore(EngineState *s); + /** * Restores a game state from a directory. * @param s An older state from the same game diff --git a/engines/sci/engine/script.cpp b/engines/sci/engine/script.cpp index 2fe1aba975..36e33ccfa6 100644 --- a/engines/sci/engine/script.cpp +++ b/engines/sci/engine/script.cpp @@ -20,6 +20,7 @@ * */ +#include "sci/console.h" #include "sci/sci.h" #include "sci/resource.h" #include "sci/util.h" @@ -64,6 +65,11 @@ void Script::freeScript() { _lockers = 1; _markedAsDeleted = false; _objects.clear(); + + _offsetLookupArray.clear(); + _offsetLookupObjectCount = 0; + _offsetLookupStringCount = 0; + _offsetLookupSaidCount = 0; } void Script::load(int script_nr, ResourceManager *resMan, ScriptPatcher *scriptPatcher) { @@ -115,8 +121,8 @@ void Script::load(int script_nr, ResourceManager *resMan, ScriptPatcher *scriptP // // TODO: Remove this once such a mechanism is in place if (script->size > 65535) - error("TODO: SCI script %d is over 64KB - it's %d bytes long. This can't " - "be handled at the moment, thus stopping", script_nr, script->size); + warning("TODO: SCI script %d is over 64KB - it's %d bytes long. This can't " + "be fully handled at the moment", script_nr, script->size); } uint extraLocalsWorkaround = 0; @@ -136,9 +142,6 @@ void Script::load(int script_nr, ResourceManager *resMan, ScriptPatcher *scriptP assert(_bufSize >= script->size); memcpy(_buf, script->data, script->size); - // Check scripts for matching signatures and patch those, if found - scriptPatcher->processScript(_nr, _buf, script->size); - if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1) { Resource *heap = resMan->findResource(ResourceId(kResourceTypeHeap, _nr), 0); assert(heap != 0); @@ -149,6 +152,9 @@ void Script::load(int script_nr, ResourceManager *resMan, ScriptPatcher *scriptP memcpy(_heapStart, heap->data, heap->size); } + // Check scripts (+ possibly SCI 1.1 heap) for matching signatures and patch those, if found + scriptPatcher->processScript(_nr, _buf, _bufSize); + if (getSciVersion() <= SCI_VERSION_1_LATE) { _exportTable = (const uint16 *)findBlockSCI0(SCI_OBJ_EXPORTS); if (_exportTable) { @@ -204,6 +210,397 @@ void Script::load(int script_nr, ResourceManager *resMan, ScriptPatcher *scriptP //_localsCount = (_bufSize - _localsOffset) >> 1; } } + + // find all strings of this script + identifyOffsets(); +} + +void Script::identifyOffsets() { + offsetLookupArrayEntry arrayEntry; + const byte *scriptDataPtr = NULL; + const byte *stringStartPtr = NULL; + const byte *stringDataPtr = NULL; + uint32 scriptDataLeft = 0; + uint32 stringDataLeft = 0; + byte stringDataByte = 0; + uint16 typeObject_id = 0; + uint16 typeString_id = 0; + uint16 typeSaid_id = 0; + + uint16 blockType = 0; + uint16 blockSize = 0; + + _offsetLookupArray.clear(); + _offsetLookupObjectCount = 0; + _offsetLookupStringCount = 0; + _offsetLookupSaidCount = 0; + + if (getSciVersion() < SCI_VERSION_1_1) { + // SCI0 + SCI1 + scriptDataPtr = _buf; + scriptDataLeft = _bufSize; + + // Go through all blocks + if (getSciVersion() == SCI_VERSION_0_EARLY) { + if (scriptDataLeft < 2) + error("Script::identifyOffsets(): unexpected end of script %d", _nr); + scriptDataPtr += 2; + scriptDataLeft -= 2; + } + + do { + if (scriptDataLeft < 2) + error("Script::identifyOffsets(): unexpected end of script %d", _nr); + + blockType = READ_LE_UINT16(scriptDataPtr); + scriptDataPtr += 2; + scriptDataLeft -= 2; + if (blockType == 0) // end of blocks detected + break; + + if (scriptDataLeft < 2) + error("Script::identifyOffsets(): unexpected end of script %d", _nr); + + blockSize = READ_LE_UINT16(scriptDataPtr); + if (blockSize < 4) + error("Script::identifyOffsets(): invalid block size in script %d", _nr); + blockSize -= 4; // block size includes block-type UINT16 and block-size UINT16 + scriptDataPtr += 2; + scriptDataLeft -= 2; + + if (scriptDataLeft < blockSize) + error("Script::identifyOffsets(): invalid block size in script %d", _nr); + + switch (blockType) { + case SCI_OBJ_OBJECT: + case SCI_OBJ_CLASS: + typeObject_id++; + arrayEntry.type = SCI_SCR_OFFSET_TYPE_OBJECT; + arrayEntry.id = typeObject_id; + arrayEntry.offset = scriptDataPtr - _buf + 8; // Calculate offset inside script data (VM uses +8) + arrayEntry.stringSize = 0; + _offsetLookupArray.push_back(arrayEntry); + _offsetLookupObjectCount++; + break; + + case SCI_OBJ_STRINGS: + // string block detected, we now grab all NUL terminated strings out of this block + stringDataPtr = scriptDataPtr; + stringDataLeft = blockSize; + + arrayEntry.type = SCI_SCR_OFFSET_TYPE_STRING; + + do { + if (stringDataLeft < 1) // no more bytes left + break; + + stringStartPtr = stringDataPtr; + + if (stringDataLeft == 1) { + // only 1 byte left and that byte is a [00], in that case we also exit + stringDataByte = *stringStartPtr; + if (stringDataByte == 0x00) + break; + } + + // now look for terminating [NUL] + do { + stringDataByte = *stringDataPtr; + stringDataPtr++; + stringDataLeft--; + if (!stringDataByte) // NUL found, exit this loop + break; + if (stringDataLeft < 1) { + // no more bytes left + warning("Script::identifyOffsets(): string without terminating NUL in script %d", _nr); + break; + } + } while (1); + + if (stringDataByte) + break; + + typeString_id++; + arrayEntry.id = typeString_id; + arrayEntry.offset = stringStartPtr - _buf; // Calculate offset inside script data + arrayEntry.stringSize = stringDataPtr - stringStartPtr; + _offsetLookupArray.push_back(arrayEntry); + _offsetLookupStringCount++; + } while (1); + break; + + case SCI_OBJ_SAID: + // said block detected, we now try to find every single said "string" inside this block + // said strings are terminated with a 0xFF, the string itself may contain words (2 bytes), where + // the second byte of a word may also be a 0xFF. + stringDataPtr = scriptDataPtr; + stringDataLeft = blockSize; + + arrayEntry.type = SCI_SCR_OFFSET_TYPE_SAID; + + do { + if (stringDataLeft < 1) // no more bytes left + break; + + stringStartPtr = stringDataPtr; + if (stringDataLeft == 1) { + // only 1 byte left and that byte is a [00], in that case we also exit + // happens in some scripts, for example Conquests of Camelot, script 997 + // may have been a bug in the compiler or just an intentional filler byte + stringDataByte = *stringStartPtr; + if (stringDataByte == 0x00) + break; + } + + // now look for terminating 0xFF + do { + stringDataByte = *stringDataPtr; + stringDataPtr++; + stringDataLeft--; + if (stringDataByte == 0xFF) // Terminator found, exit this loop + break; + if (stringDataLeft < 1) // no more bytes left + error("Script::identifyOffsets(): said-string without terminator in script %d", _nr); + if (stringDataByte < 0xF0) { + // Part of a word, skip second byte + stringDataPtr++; + stringDataLeft--; + if (stringDataLeft < 1) // no more bytes left + error("Script::identifyOffsets(): said-string without terminator in script %d", _nr); + } + } while (1); + + typeSaid_id++; + arrayEntry.id = typeSaid_id; + arrayEntry.offset = stringStartPtr - _buf; // Calculate offset inside script data + arrayEntry.stringSize = 0; + _offsetLookupArray.push_back(arrayEntry); + _offsetLookupSaidCount++; + } while (1); + break; + + default: + break; + } + + scriptDataPtr += blockSize; + scriptDataLeft -= blockSize; + } while (1); + + } else if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1) { + // Strings in SCI1.1 up to SCI2 come after the object instances + scriptDataPtr = _heapStart; + scriptDataLeft = _heapSize; + + if (scriptDataLeft < 4) + error("Script::identifyOffsets(): unexpected end of script in script %d", _nr); + + uint16 endOfStringOffset = READ_SCI11ENDIAN_UINT16(scriptDataPtr); + uint16 objectStartOffset = READ_SCI11ENDIAN_UINT16(scriptDataPtr + 2) * 2 + 4; + + if (scriptDataLeft < objectStartOffset) + error("Script::identifyOffsets(): object start is beyond heap size in script %d", _nr); + if (scriptDataLeft < endOfStringOffset) + error("Script::identifyOffsets(): end of string is beyond heap size in script %d", _nr); + + const byte *endOfStringPtr = scriptDataPtr + endOfStringOffset; + + scriptDataPtr += objectStartOffset; + scriptDataLeft -= objectStartOffset; + + // go through all objects + do { + if (scriptDataLeft < 2) + error("Script::identifyOffsets(): unexpected end of script %d", _nr); + + blockType = READ_SCI11ENDIAN_UINT16(scriptDataPtr); + scriptDataPtr += 2; + scriptDataLeft -= 2; + if (blockType != SCRIPT_OBJECT_MAGIC_NUMBER) + break; + + // Object found, add offset of object + typeObject_id++; + arrayEntry.type = SCI_SCR_OFFSET_TYPE_OBJECT; + arrayEntry.id = typeObject_id; + arrayEntry.offset = scriptDataPtr - _buf - 2; // the VM uses a pointer to the Magic-Number + arrayEntry.stringSize = 0; + _offsetLookupArray.push_back(arrayEntry); + _offsetLookupObjectCount++; + + if (scriptDataLeft < 2) + error("Script::identifyOffsets(): unexpected end of script in script %d", _nr); + + blockSize = READ_SCI11ENDIAN_UINT16(scriptDataPtr) * 2; + if (blockSize < 4) + error("Script::identifyOffsets(): invalid block size in script %d", _nr); + scriptDataPtr += 2; + scriptDataLeft -= 2; + blockSize -= 4; // blocksize contains UINT16 type and UINT16 size + if (scriptDataLeft < blockSize) + error("Script::identifyOffsets(): invalid block size in script %d", _nr); + + scriptDataPtr += blockSize; + scriptDataLeft -= blockSize; + } while (1); + + // now scriptDataPtr points to right at the start of the strings + if (scriptDataPtr > endOfStringPtr) + error("Script::identifyOffsets(): string block / end-of-string block mismatch in script %d", _nr); + + stringDataPtr = scriptDataPtr; + stringDataLeft = endOfStringPtr - scriptDataPtr; // Calculate byte count within string-block + + arrayEntry.type = SCI_SCR_OFFSET_TYPE_STRING; + do { + if (stringDataLeft < 1) // no more bytes left + break; + + stringStartPtr = stringDataPtr; + // now look for terminating [NUL] + do { + stringDataByte = *stringDataPtr; + stringDataPtr++; + stringDataLeft--; + if (!stringDataByte) // NUL found, exit this loop + break; + if (stringDataLeft < 1) { + // no more bytes left + warning("Script::identifyOffsets(): string without terminating NUL in script %d", _nr); + break; + } + } while (1); + + if (stringDataByte) + break; + + typeString_id++; + arrayEntry.id = typeString_id; + arrayEntry.offset = stringStartPtr - _buf; // Calculate offset inside script data + arrayEntry.stringSize = stringDataPtr - stringStartPtr; + _offsetLookupArray.push_back(arrayEntry); + _offsetLookupStringCount++; + } while (1); + + } else if (getSciVersion() == SCI_VERSION_3) { + // SCI3 + uint32 sci3StringOffset = 0; + uint32 sci3RelocationOffset = 0; + uint32 sci3BoundaryOffset = 0; + + if (_bufSize < 22) + error("Script::identifyOffsets(): script %d smaller than expected SCI3-header", _nr); + + sci3StringOffset = READ_LE_UINT32(_buf + 4); + sci3RelocationOffset = READ_LE_UINT32(_buf + 8); + + if (sci3RelocationOffset > _bufSize) + error("Script::identifyOffsets(): relocation offset is beyond end of script %d", _nr); + + // First we get all the objects + scriptDataPtr = getSci3ObjectsPointer(); + scriptDataLeft = _bufSize - (scriptDataPtr - _buf); + do { + if (scriptDataLeft < 2) + error("Script::identifyOffsets(): unexpected end of script %d", _nr); + + blockType = READ_SCI11ENDIAN_UINT16(scriptDataPtr); + scriptDataPtr += 2; + scriptDataLeft -= 2; + if (blockType != SCRIPT_OBJECT_MAGIC_NUMBER) + break; + + // Object found, add offset of object + typeObject_id++; + arrayEntry.type = SCI_SCR_OFFSET_TYPE_OBJECT; + arrayEntry.id = typeObject_id; + arrayEntry.offset = scriptDataPtr - _buf - 2; // the VM uses a pointer to the Magic-Number + arrayEntry.stringSize = 0; + _offsetLookupArray.push_back(arrayEntry); + _offsetLookupObjectCount++; + + if (scriptDataLeft < 2) + error("Script::identifyOffsets(): unexpected end of script in script %d", _nr); + + blockSize = READ_SCI11ENDIAN_UINT16(scriptDataPtr); + if (blockSize < 4) + error("Script::identifyOffsets(): invalid block size in script %d", _nr); + scriptDataPtr += 2; + scriptDataLeft -= 2; + blockSize -= 4; // blocksize contains UINT16 type and UINT16 size + if (scriptDataLeft < blockSize) + error("Script::identifyOffsets(): invalid block size in script %d", _nr); + + scriptDataPtr += blockSize; + scriptDataLeft -= blockSize; + } while (1); + + // And now we get all the strings + if (sci3StringOffset > 0) { + // string offset set, we expect strings + if (sci3StringOffset > _bufSize) + error("Script::identifyOffsets(): string offset is beyond end of script %d", _nr); + + if (sci3RelocationOffset < sci3StringOffset) + error("Script::identifyOffsets(): string offset points beyond relocation offset in script %d", _nr); + + stringDataPtr = _buf + sci3StringOffset; + stringDataLeft = sci3RelocationOffset - sci3StringOffset; + + arrayEntry.type = SCI_SCR_OFFSET_TYPE_STRING; + + do { + if (stringDataLeft < 1) // no more bytes left + break; + + stringStartPtr = stringDataPtr; + + if (stringDataLeft == 1) { + // only 1 byte left and that byte is a [00], in that case we also exit + stringDataByte = *stringStartPtr; + if (stringDataByte == 0x00) + break; + } + + // now look for terminating [NUL] + do { + stringDataByte = *stringDataPtr; + stringDataPtr++; + stringDataLeft--; + if (!stringDataByte) // NUL found, exit this loop + break; + if (stringDataLeft < 1) { + // no more bytes left + warning("Script::identifyOffsets(): string without terminating NUL in script %d", _nr); + break; + } + } while (1); + + if (stringDataByte) + break; + + typeString_id++; + arrayEntry.id = typeString_id; + arrayEntry.offset = stringStartPtr - _buf; // Calculate offset inside script data + arrayEntry.stringSize = stringDataPtr - stringStartPtr; + _offsetLookupArray.push_back(arrayEntry); + _offsetLookupStringCount++; + + // SCI3 seems to have aligned all string on DWORD boundaries + sci3BoundaryOffset = stringDataPtr - _buf; // Calculate current offset inside script data + sci3BoundaryOffset = sci3BoundaryOffset & 3; // Check boundary offset + if (sci3BoundaryOffset) { + // lower 2 bits are set? Then we have to adjust the offset + sci3BoundaryOffset = 4 - sci3BoundaryOffset; + if (stringDataLeft < sci3BoundaryOffset) + error("Script::identifyOffsets(): SCI3 string boundary adjustment goes beyond end of string block in script %d", _nr); + stringDataLeft -= sci3BoundaryOffset; + stringDataPtr += sci3BoundaryOffset; + } + } while (1); + } + return; + } } const byte *Script::getSci3ObjectsPointer() { @@ -689,9 +1086,14 @@ void Script::initializeObjectsSci3(SegManager *segMan, SegmentId segmentId) { const byte *seeker = getSci3ObjectsPointer(); while (READ_SCI11ENDIAN_UINT16(seeker) == SCRIPT_OBJECT_MAGIC_NUMBER) { - reg_t reg = make_reg(segmentId, seeker - _buf); - Object *obj = scriptObjInit(reg); + // We call setSegment and setOffset directly here, instead of using + // make_reg, as in large scripts, seeker - _buf can be larger than + // a 16-bit integer + reg_t reg; + reg.setSegment(segmentId); + reg.setOffset(seeker - _buf); + Object *obj = scriptObjInit(reg); obj->setSuperClassSelector(segMan->getClassAddress(obj->getSuperClassSelector().getOffset(), SCRIPT_GET_LOCK, 0)); seeker += READ_SCI11ENDIAN_UINT16(seeker + 2); } diff --git a/engines/sci/engine/script.h b/engines/sci/engine/script.h index 46d6ace917..755e2f3698 100644 --- a/engines/sci/engine/script.h +++ b/engines/sci/engine/script.h @@ -49,6 +49,21 @@ enum ScriptObjectTypes { typedef Common::HashMap<uint16, Object> ObjMap; +enum ScriptOffsetEntryTypes { + SCI_SCR_OFFSET_TYPE_OBJECT = 0, // classes are handled by this type as well + SCI_SCR_OFFSET_TYPE_STRING, + SCI_SCR_OFFSET_TYPE_SAID +}; + +struct offsetLookupArrayEntry { + uint16 type; // type of entry + uint16 id; // id of this type, first item inside script data is 1, second item is 2, etc. + uint32 offset; // offset of entry within script resource data + uint16 stringSize; // size of string, including terminating [NUL] +}; + +typedef Common::Array<offsetLookupArrayEntry> offsetLookupArrayType; + class Script : public SegmentObj { private: int _nr; /**< Script number */ @@ -75,6 +90,14 @@ private: ObjMap _objects; /**< Table for objects, contains property variables */ +protected: + offsetLookupArrayType _offsetLookupArray; // Table of all elements of currently loaded script, that may get pointed to + +private: + uint16 _offsetLookupObjectCount; + uint16 _offsetLookupStringCount; + uint16 _offsetLookupSaidCount; + public: int getLocalsOffset() const { return _localsOffset; } uint16 getLocalsCount() const { return _localsCount; } @@ -248,6 +271,14 @@ public: */ int getCodeBlockOffsetSci3() { return READ_SCI11ENDIAN_UINT32(_buf); } + /** + * Get the offset array + */ + const offsetLookupArrayType *getOffsetArray() { return &_offsetLookupArray; }; + uint16 getOffsetObjectCount() { return _offsetLookupObjectCount; }; + uint16 getOffsetStringCount() { return _offsetLookupStringCount; }; + uint16 getOffsetSaidCount() { return _offsetLookupSaidCount; }; + private: /** * Processes a relocation block within a SCI0-SCI2.1 script @@ -294,6 +325,11 @@ private: void initializeObjectsSci3(SegManager *segMan, SegmentId segmentId); LocalVariables *allocLocalsSegment(SegManager *segMan); + + /** + * Identifies certain offsets within script data and set up lookup-table + */ + void identifyOffsets(); }; } // End of namespace Sci diff --git a/engines/sci/engine/script_patches.cpp b/engines/sci/engine/script_patches.cpp index 03cd1d06e9..6915e12a0e 100644 --- a/engines/sci/engine/script_patches.cpp +++ b/engines/sci/engine/script_patches.cpp @@ -94,6 +94,8 @@ static const char *const selectorNameTable[] = { "deskSarg", // Gabriel Knight "localize", // Freddy Pharkas "put", // Police Quest 1 VGA + "say", // Quest For Glory 1 VGA + "contains", // Quest For Glory 2 "solvePuzzle", // Quest For Glory 3 "timesShownID", // Space Quest 1 VGA "startText", // King's Quest 6 CD / Laura Bow 2 CD for audio+text support @@ -119,6 +121,8 @@ enum ScriptPatcherSelectors { SELECTOR_deskSarg, SELECTOR_localize, SELECTOR_put, + SELECTOR_say, + SELECTOR_contains, SELECTOR_solvePuzzle, SELECTOR_timesShownID, SELECTOR_startText, @@ -562,12 +566,12 @@ static const uint16 gk1SignatureDay6PoliceSleep[] = { 0x34, SIG_UINT16(0x00dc), // ldi 220 0x65, SIG_ADDTOOFFSET(+1), // aTop cycles (1a for PC, 1c for Mac) 0x32, // jmp [end] - 0 + SIG_END }; static const uint16 gk1PatchDay6PoliceSleep[] = { PATCH_ADDTOOFFSET(+5), - 0x34, SIG_UINT16(0x002a), // ldi 42 + 0x34, PATCH_UINT16(0x002a), // ldi 42 0x65, PATCH_GETORIGINALBYTEADJUST(+9, +2), // aTop seconds (1c for PC, 1e for Mac) PATCH_END }; @@ -1421,6 +1425,37 @@ static const SciScriptPatcherEntry larry2Signatures[] = { // =========================================================================== // Leisure Suit Larry 5 +// In Miami the player can call the green card telephone number and get +// green card including limo at the same time in the English 1.000 PC release. +// This results later in a broken game in case the player doesn't read +// the second telephone number for the actual limousine service, because +// in that case it's impossible for the player to get back to the airport. +// +// We disable the code, that is responsible to make the limo arrive. +// +// This bug was fixed in the European (dual language) versions of the game. +// +// Applies to at least: English PC floppy (1.000) +// Responsible method: sPhone::changeState(40) +static const uint16 larry5SignatureGreenCardLimoBug[] = { + 0x7a, // push2 + SIG_MAGICDWORD, + 0x39, 0x07, // pushi 07 + 0x39, 0x0c, // pushi 0Ch + 0x45, 0x0a, 0x04, // call export 10 of script 0 + 0x78, // push1 + 0x39, 0x26, // pushi 26h (limo arrived flag) + 0x45, 0x07, 0x02, // call export 7 of script 0 (sets flag) + SIG_END +}; + +static const uint16 larry5PatchGreenCardLimoBug[] = { + PATCH_ADDTOOFFSET(+8), + 0x34, PATCH_UINT16(0), // ldi 0000 (dummy) + 0x34, PATCH_UINT16(0), // ldi 0000 (dummy) + PATCH_END +}; + // 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. @@ -1444,6 +1479,7 @@ static const uint16 larry5PatchGermanEndingPattiTalker[] = { // script, description, signature patch static const SciScriptPatcherEntry larry5Signatures[] = { + { true, 280, "English-only: fix green card limo bug", 1, larry5SignatureGreenCardLimoBug, larry5PatchGreenCardLimoBug }, { true, 380, "German-only: Enlarge Patti Textbox", 1, larry5SignatureGermanEndingPattiTalker, larry5PatchGermanEndingPattiTalker }, SCI_SIGNATUREENTRY_TERMINATOR }; @@ -2087,21 +2123,30 @@ static const uint16 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. +// location where Ego is moved from 111, 111 to 116, 116. +// target coordinate is really problematic here. +// +// 114, 114 works when the speed slider is all the way up, but doesn't work +// when the speed slider is not. +// +// It seems that this bug was fixed by Sierra for the Macintosh version. +// +// Applies to at least: English PC floppy +// Responsible method: GuardsTrumpet::changeState(8) // Fixes bug: #6248 static const uint16 qfg1vgaSignatureMoveToCastleGate[] = { + 0x51, SIG_ADDTOOFFSET(+1), // class MoveTo SIG_MAGICDWORD, - 0x51, 0x1f, // class MoveTo 0x36, // push - 0x39, 0x6f, // pushi 6f (111 - x) - 0x3c, // dup (111 - y) + 0x39, 0x6f, // pushi 6f (111d) + 0x3c, // dup (111d) - coordinates 111, 111 0x7c, // pushSelf SIG_END }; static const uint16 qfg1vgaPatchMoveToCastleGate[] = { PATCH_ADDTOOFFSET(+3), - 0x39, 0x72, // pushi 72 (114 - x) + 0x39, 0x74, // pushi 74 (116d), changes coordinates to 116, 116 PATCH_END }; @@ -2116,7 +2161,7 @@ static const uint16 qfg1vgaSignatureCheetaurDescription[] = { 0x34, SIG_UINT16(0x01b8), // ldi 01b8 0x1a, // eq? 0x31, 0x16, // bnt 16 - 0x38, SIG_UINT16(0x0127), // pushi 0127 + 0x38, SIG_SELECTOR16(say), // pushi 0127h (selector "say") 0x39, 0x06, // pushi 06 0x39, 0x03, // pushi 03 0x78, // push1 @@ -2140,6 +2185,7 @@ static const uint16 qfg1vgaPatchCheetaurDescription[] = { // Local 5 of that room is a timer, that closes the door (object door11). // Setting it to 1 during happyFace::changeState(0) stops door11::doit from // calling goTo6::init, so the whole issue is stopped from happening. +// // Applies to at least: English floppy // Responsible method: happyFace::changeState, door11::doit // Fixes bug #6181 @@ -2166,20 +2212,172 @@ static const uint16 qfg1vgaPatchFunnyRoomFix[] = { PATCH_END }; +// The player is able to buy (and also steal) potions in the healer's hut +// Strangely Sierra delays the actual buy/get potion code for 60 ticks +// Why they did that is unknown. The code is triggered anyway only after +// the relevant dialog boxes are closed. +// +// This delay causes problems in case the user quickly enters the inventory. +// That's why we change the amount of ticks to 1, so that the remaining states +// are executed right after the dialog boxes are closed. +// +// Applies to at least: English floppy +// Responsible method: cueItScript::changeState +// Fixes bug #6706 +static const uint16 qfg1vgaSignatureHealerHutNoDelay[] = { + 0x65, 0x14, // aTop 14 (state) + 0x36, // push + 0x3c, // dup + 0x35, 0x00, // ldi 00 + 0x1a, // eq? + 0x31, 0x07, // bnt 07 [-> next state] + SIG_MAGICDWORD, + 0x35, 0x3c, // ldi 3c (60 ticks) + 0x65, 0x20, // aTop ticks + 0x32, // jmp [-> end of method] + SIG_END +}; + +static const uint16 qfg1vgaPatchHealerHutNoDelay[] = { + PATCH_ADDTOOFFSET(+9), + 0x35, 0x01, // ldi 01 (1 tick only, so that execution will resume as soon as dialog box is closed) + PATCH_END +}; + +// When following the white stag, you can actually enter the 2nd room from the mushroom/fairy location, +// which results in ego entering from the top. When you then throw a dagger at the stag, one animation +// frame will stay on screen, because of a script bug. +// +// Applies to at least: English floppy, Mac floppy +// Responsible method: stagHurt::changeState +// Fixes bug #6135 +static const uint16 qfg1vgaSignatureWhiteStagDagger[] = { + 0x87, 0x01, // lap param[1] + 0x65, 0x14, // aTop state + 0x36, // push + 0x3c, // dup + 0x35, 0x00, // ldi 0 + 0x1a, // eq? + 0x31, 0x16, // bnt [next parameter check] + 0x76, // push0 + 0x45, 0x02, 0x00, // callb export 2 from script 0, 0 + SIG_MAGICDWORD, + 0x38, SIG_SELECTOR16(say), // pushi 0127h (selector "say") + 0x39, 0x05, // pushi 05 + 0x39, 0x03, // pushi 03 + 0x39, 0x51, // pushi 51h + 0x76, // push0 + 0x76, // push0 + 0x7c, // pushSelf + 0x81, 0x5b, // lag global[5Bh] -> qg1Messager + 0x4a, 0x0e, // send 0Eh -> qg1Messager::say(3, 51h, 0, 0, stagHurt) + 0x33, 0x12, // jmp -> [ret] + 0x3c, // dup + 0x35, 0x01, // ldi 1 + 0x1a, // eq? + 0x31, 0x0c, // bnt [ret] + 0x38, // pushi... + SIG_ADDTOOFFSET(+11), + 0x3a, // toss + 0x48, // ret + SIG_END +}; + +static const uint16 qfg1vgaPatchWhiteStagDagger[] = { + PATCH_ADDTOOFFSET(+4), + 0x2f, 0x05, // bt [next check] (state != 0) + // state = 0 code + 0x35, 0x01, // ldi 1 + 0x65, 0x1a, // aTop cycles + 0x48, // ret + 0x36, // push + 0x35, 0x01, // ldi 1 + 0x1a, // eq? + 0x31, 0x16, // bnt [state = 2 code] + // state = 1 code + 0x76, // push0 + 0x45, 0x02, 0x00, // callb export 2 from script 0, 0 + 0x38, PATCH_SELECTOR16(say), // pushi 0127h (selector "say") + 0x39, 0x05, // pushi 05 + 0x39, 0x03, // pushi 03 + 0x39, 0x51, // pushi 51h + 0x76, // push0 + 0x76, // push0 + 0x7c, // pushSelf + 0x81, 0x5b, // lag global[5Bh] -> qg1Messager + 0x4a, 0x0e, // send 0Eh -> qg1Messager::say(3, 51h, 0, 0, stagHurt) + 0x48, // ret + // state = 2 code + PATCH_ADDTOOFFSET(+13), + 0x48, // ret (remove toss) + PATCH_END +}; + // script, description, signature patch static const SciScriptPatcherEntry qfg1vgaSignatures[] = { + { true, 41, "moving to castle gate", 1, qfg1vgaSignatureMoveToCastleGate, qfg1vgaPatchMoveToCastleGate }, + { true, 55, "healer's hut, no delay for buy/steal", 1, qfg1vgaSignatureHealerHutNoDelay, qfg1vgaPatchHealerHutNoDelay }, + { true, 77, "white stag dagger throw animation glitch", 1, qfg1vgaSignatureWhiteStagDagger, qfg1vgaPatchWhiteStagDagger }, + { true, 96, "funny room script bug fixed", 1, qfg1vgaSignatureFunnyRoomFix, qfg1vgaPatchFunnyRoomFix }, + { true, 210, "cheetaur description fixed", 1, qfg1vgaSignatureCheetaurDescription, qfg1vgaPatchCheetaurDescription }, { true, 215, "fight event issue", 1, qfg1vgaSignatureFightEvents, qfg1vgaPatchFightEvents }, { true, 216, "weapon master event issue", 1, qfg1vgaSignatureFightEvents, qfg1vgaPatchFightEvents }, + { true, 331, "moving to crusher", 1, qfg1vgaSignatureMoveToCrusher, qfg1vgaPatchMoveToCrusher }, { true, 814, "window text temp space", 1, qfg1vgaSignatureTempSpace, qfg1vgaPatchTempSpace }, { true, 814, "dialog header offset", 3, qfg1vgaSignatureDialogHeader, qfg1vgaPatchDialogHeader }, - { true, 331, "moving to crusher", 1, qfg1vgaSignatureMoveToCrusher, qfg1vgaPatchMoveToCrusher }, - { true, 41, "moving to castle gate", 1, qfg1vgaSignatureMoveToCastleGate, qfg1vgaPatchMoveToCastleGate }, - { true, 210, "cheetaur description fixed", 1, qfg1vgaSignatureCheetaurDescription, qfg1vgaPatchCheetaurDescription }, - { true, 96, "funny room script bug fixed", 1, qfg1vgaSignatureFunnyRoomFix, qfg1vgaPatchFunnyRoomFix }, SCI_SIGNATUREENTRY_TERMINATOR }; // =========================================================================== + +// This is a very complicated bug. +// When the player encounters an enemy in the desert while riding a saurus and later +// tries to get back on it by entering "ride", the game will not give control back +// to the player. +// +// This is caused by script mountSaurus getting triggered twice. +// Once by entering the command "ride" and then a second time by a proximity check. +// +// Both are calling mountSaurus::init() in script 20, this one disables controls +// then mountSaurus::changeState() from script 660 is triggered +// mountSaurus::changeState(5) finally calls mountSaurus::dispose(), which is also in script 20 +// which finally re-enables controls +// +// A fix is difficult to implement. The code in script 20 is generic and used by multiple objects +// +// Originally I decided to change the responsible globals (66h and A1h) during mountSaurus::changeState(5). +// This worked as far as for controls, but mountSaurus::init changes a few selectors of ego as well, which +// won't get restored in that situation, which then messes up room changes and other things. +// +// I have now decided to change sheepScript::changeState(2) in script 665 instead. +// +// This fix could cause issues in case there is a cutscene, where ego is supposed to get onto the saurus using +// sheepScript. +// +// Applies to at least: English PC Floppy, English Amiga Floppy +// Responsible method: mountSaurus::changeState(), mountSaurus::init(), mountSaurus::dispose() +// Fixes bug: #5156 +static const uint16 qfg2SignatureSaurusFreeze[] = { + 0x3c, // dup + 0x35, 0x02, // ldi 5 + SIG_MAGICDWORD, + 0x1a, // eq? + 0x30, SIG_UINT16(0x0043), // bnt [ret] + 0x76, // push0 + SIG_ADDTOOFFSET(+61), // skip to dispose code + 0x39, SIG_SELECTOR8(dispose), // pushi "dispose" + 0x76, // push0 + 0x54, 0x04, // self 04 + SIG_END +}; + +static const uint16 qfg2PatchSaurusFreeze[] = { + 0x81, 0x66, // lag 66h + 0x2e, SIG_UINT16(0x0040), // bt [to dispose code] + 0x35, 0x00, // ldi 0 (waste 2 bytes) + PATCH_END +}; + // Script 944 in QFG2 contains the FileSelector system class, used in the // character import screen. This gets incorrectly called constantly, whenever // the user clicks on a button in order to refresh the file list. This was @@ -2211,9 +2409,58 @@ static const uint16 qfg2PatchImportDialog[] = { PATCH_END }; -// script, description, signature patch +// Quest For Glory 2 character import doesn't properly set the character type +// in versions 1.102 and below, which makes all importerted characters a fighter. +// +// Sierra released an official patch. However the fix is really easy to +// implement on our side, so we also patch the flaw in here in case we find it. +// +// The version released on GOG is 1.102 without this patch applied, so us +// patching it is quite useful. +// +// Applies to at least: English Floppy +// Responsible method: importHero::changeState +// Fixes bug: inside versions 1.102 and below +static const uint16 qfg2SignatureImportCharType[] = { + 0x35, 0x04, // ldi 04 + 0x90, SIG_UINT16(0x023b), // lagi global[23Bh] + 0x02, // add + 0x36, // push + 0x35, 0x04, // ldi 04 + 0x08, // div + 0x36, // push + 0x35, 0x0d, // ldi 0D + 0xb0, SIG_UINT16(0x023b), // sagi global[023Bh] + 0x8b, 0x1f, // lsl local[1Fh] + 0x35, 0x05, // ldi 05 + SIG_MAGICDWORD, + 0xb0, SIG_UINT16(0x0150), // sagi global[0150h] + 0x8b, 0x02, // lsl local[02h] + SIG_END +}; + +static const uint16 qfg2PatchImportCharType[] = { + 0x80, PATCH_UINT16(0x023f), // lag global[23Fh] <-- patched to save 2 bytes + 0x02, // add + 0x36, // push + 0x35, 0x04, // ldi 04 + 0x08, // div + 0x36, // push + 0xa8, SIG_UINT16(0x0248), // ssg global[0248h] <-- patched to save 2 bytes + 0x8b, 0x1f, // lsl local[1Fh] + 0xa8, SIG_UINT16(0x0155), // ssg global[0155h] <-- patched to save 2 bytes + // new code, directly from the official sierra patch file + 0x83, 0x01, // lal local[01h] + 0xa1, 0xbb, // sag global[BBh] + 0xa1, 0x73, // sag global[73h] + PATCH_END +}; + +// script, description, signature patch static const SciScriptPatcherEntry qfg2Signatures[] = { - { true, 944, "import dialog continuous calls", 1, qfg2SignatureImportDialog, qfg2PatchImportDialog }, + { true, 665, "getting back on saurus freeze fix", 1, qfg2SignatureSaurusFreeze, qfg2PatchSaurusFreeze }, + { true, 805, "import character type fix", 1, qfg2SignatureImportCharType, qfg2PatchImportCharType }, + { true, 944, "import dialog continuous calls", 1, qfg2SignatureImportDialog, qfg2PatchImportDialog }, SCI_SIGNATUREENTRY_TERMINATOR }; @@ -2282,10 +2529,158 @@ static const uint16 qfg3PatchWooDialog[] = { PATCH_END }; -// script, description, signature patch +// Alternative version, with uint16 offsets, for GOG release of QfG3. +static const uint16 qfg3SignatureWooDialogAlt[] = { + SIG_MAGICDWORD, + 0x67, 0x12, // pTos 12 (query) + 0x35, 0xb6, // ldi b6 + 0x1a, // eq? + 0x2e, SIG_UINT16(0x0005), // bt 05 + 0x67, 0x12, // pTos 12 (query) + 0x35, 0x9b, // ldi 9b + 0x1a, // eq? + 0x30, SIG_UINT16(0x000c), // bnt 0c + 0x38, SIG_SELECTOR16(solvePuzzle), // pushi 0297 + 0x7a, // push2 + 0x38, SIG_UINT16(0x010c), // pushi 010c + 0x7a, // push2 + 0x81, 0x00, // lag 00 + 0x4a, 0x08, // send 08 + 0x67, 0x12, // pTos 12 (query) + 0x35, 0xb5, // ldi b5 + SIG_END +}; + +static const uint16 qfg3PatchWooDialogAlt[] = { + PATCH_ADDTOOFFSET(+0x2C), + 0x33, 0x12, // jmp to 0x708, the call to hero::solvePuzzle for 0xFFFC + PATCH_END +}; + +// When exporting characters at the end of Quest for Glory 3, the underlying +// code has issues with values, that are above 9999. +// For further study: https://github.com/Blazingstix/QFGImporter/blob/master/QFGImporter/QFGImporter/QFG3.txt +// +// If a value is above 9999, parts or even the whole character file will get corrupted. +// +// We are fixing the code because of that. We are patching code, that is calculating the checksum +// and add extra code to lower such values to 9999. +// +// Applies to at least: English, French, German, Italian, Spanish floppy +// Responsible method: saveHero::changeState +// Fixes bug #6807 +static const uint16 qfg3SignatureExportChar[] = { + 0x35, SIG_ADDTOOFFSET(+1), // ldi 00 / ldi 01 (2 loops, we patch both) + 0xa5, 0x00, // sat temp[0] [contains index to data] + 0x8d, 0x00, // lst temp[0] + SIG_MAGICDWORD, + 0x35, 0x2c, // ldi 2c + 0x22, // lt? [index above or equal 2Ch (44d)? + 0x31, 0x23, // bnt [exit loop] + // from this point it's actually useless code, maybe a sci compiler bug + 0x8d, 0x00, // lst temp[0] + 0x35, 0x01, // ldi 01 + 0x02, // add + 0x9b, 0x00, // lsli local[0] ---------- load local[0 + ACC] onto stack + 0x8d, 0x00, // lst temp[0] + 0x35, 0x01, // ldi 01 + 0x02, // add + 0xb3, 0x00, // sali local[0] ---------- save stack to local[0 + ACC] + // end of useless code + 0x8b, SIG_ADDTOOFFSET(+1), // lsl local[36h/37h] ---- load local[36h/37h] onto stack + 0x8d, 0x00, // lst temp[0] + 0x35, 0x01, // ldi 01 + 0x02, // add + 0x93, 0x00, // lali local[0] ---------- load local[0 + ACC] into ACC + 0x02, // add -------------------- add ACC + stack and put into ACC + 0xa3, SIG_ADDTOOFFSET(+1), // sal local[36h/37h] ---- save ACC to local[36h/37h] + 0x8d, 0x00, // lst temp[0] ------------ temp[0] to stack + 0x35, 0x02, // ldi 02 + 0x02, // add -------------------- add 2 to stack + 0xa5, 0x00, // sat temp[0] ------------ save ACC to temp[0] + 0x33, 0xd6, // jmp [loop] + SIG_END +}; + +static const uint16 qfg3PatchExportChar[] = { + PATCH_ADDTOOFFSET(+11), + 0x85, 0x00, // lat temp[0] + 0x9b, 0x01, // lsli local[0] + 1 ------ load local[ ACC + 1] onto stack + 0x3c, // dup + 0x34, PATCH_UINT16(0x2710), // ldi 2710h (10000d) + 0x2c, // ult? ------------------- is value smaller than 10000? + 0x2f, 0x0a, // bt [jump over] + 0x3a, // toss + 0x38, PATCH_UINT16(0x270f), // pushi 270fh (9999d) + 0x3c, // dup + 0x85, 0x00, // lat temp[0] + 0xba, PATCH_UINT16(0x0001), // ssli local[0] + 1 ------ save stack to local[ ACC + 1] (UINT16 to waste 1 byte) + // jump offset + 0x83, PATCH_GETORIGINALBYTE(+26), // lal local[37h/36h] ---- load local[37h/36h] into ACC + 0x02, // add -------------------- add local[37h/36h] + data value + PATCH_END +}; + +// Quest for Glory 3 doesn't properly import the character type of Quest for Glory 1 character files. +// This issue was never addressed. It's caused by Sierra reading data directly from the local +// area, which is only set by Quest For Glory 2 import data, instead of reading the properly set global variable. +// +// We fix it, by also directly setting the local variable. +// +// Applies to at least: English, French, German, Italian, Spanish floppy +// Responsible method: importHero::changeState(4) +static const uint16 qfg3SignatureImportQfG1Char[] = { + SIG_MAGICDWORD, + 0x82, SIG_UINT16(0x0238), // lal local[0x0238] + 0xa0, SIG_UINT16(0x016a), // sag global[0x016a] + 0xa1, 0x7d, // sag global[0x7d] + 0x35, 0x01, // ldi 01 + 0x99, 0xfb, // lsgi global[0xfb] + SIG_END +}; + +static const uint16 qfg3PatchImportQfG1Char[] = { + PATCH_ADDTOOFFSET(+8), + 0xa3, 0x01, // sal 01 -> also set local[01] + 0x89, 0xfc, // lsg global[0xFD] -> save 2 bytes + PATCH_END +}; + +// The chief in his hut (room 640) is not drawn using the correct priority, +// which results in a graphical glitch. This is a game bug and also happens +// in Sierra's SCI. We adjust priority accordingly to fix it. +// +// Applies to at least: English, French, German, Italian, Spanish floppy +// Responsible method: heap in script 640 +// Fixes bug #5173 +static const uint16 qfg3SignatureChiefPriority[] = { + SIG_MAGICDWORD, + SIG_UINT16(0x0002), // yStep 0x0002 + SIG_UINT16(0x0281), // view 0x0281 + SIG_UINT16(0x0000), // loop 0x0000 + SIG_UINT16(0x0000), // cel 0x0000 + SIG_UINT16(0x0000), // priority 0x0000 + SIG_UINT16(0x0000), // underbits 0x0000 + SIG_UINT16(0x1000), // signal 0x1000 + SIG_END +}; + +static const uint16 qfg3PatchChiefPriority[] = { + PATCH_ADDTOOFFSET(+8), + PATCH_UINT16(0x000A), // new priority 0x000A (10d) + PATCH_ADDTOOFFSET(+2), + PATCH_UINT16(0x1010), // signal 0x1010 (set fixed priority flag) + PATCH_END +}; + +// script, description, signature patch static const SciScriptPatcherEntry qfg3Signatures[] = { - { true, 944, "import dialog continuous calls", 1, qfg3SignatureImportDialog, qfg3PatchImportDialog }, - { true, 440, "dialog crash when asking about Woo", 1, qfg3SignatureWooDialog, qfg3PatchWooDialog }, + { true, 944, "import dialog continuous calls", 1, qfg3SignatureImportDialog, qfg3PatchImportDialog }, + { true, 440, "dialog crash when asking about Woo", 1, qfg3SignatureWooDialog, qfg3PatchWooDialog }, + { true, 440, "dialog crash when asking about Woo", 1, qfg3SignatureWooDialogAlt, qfg3PatchWooDialogAlt }, + { true, 52, "export character save bug", 2, qfg3SignatureExportChar, qfg3PatchExportChar }, + { true, 54, "import character from QfG1 bug", 1, qfg3SignatureImportQfG1Char, qfg3PatchImportQfG1Char }, + { true, 640, "chief in hut priority fix", 1, qfg3SignatureChiefPriority, qfg3PatchChiefPriority }, SCI_SIGNATUREENTRY_TERMINATOR }; @@ -2345,6 +2740,102 @@ static const uint16 sq4FloppyPatchThrowStuffAtSequelPoliceBug[] = { PATCH_END }; +// Right at the start of Space Quest 4 CD, when walking up in the first room, ego will +// immediately walk down just after entering the upper room. +// +// This is caused by the scripts setting ego's vertical coordinate to 189 (BDh), which is the +// trigger in rooms to walk to the room below it. Sometimes this isn't triggered, because +// the scripts also initiate a motion to vertical coordinate 188 (BCh). When you lower the game's speed, +// this bug normally always triggers. And it triggers of course also in the original interpreter. +// +// It doesn't happen in PC floppy, because nsRect is not the same as in CD. +// +// We fix it by setting ego's vertical coordinate to 188 and we also initiate a motion to 187. +// +// Applies to at least: English PC CD +// Responsible method: rm045::doit +// Fixes bug: #5468 +static const uint16 sq4CdSignatureWalkInFromBelowRoom45[] = { + 0x76, // push0 + SIG_MAGICDWORD, + 0x78, // push1 + 0x38, SIG_UINT16(0x00bd), // pushi 00BDh + 0x38, SIG_ADDTOOFFSET(+2), // pushi [setMotion selector] + 0x39, 0x03, // pushi 3 + 0x51, SIG_ADDTOOFFSET(+1), // class [MoveTo] + 0x36, // push + 0x78, // push1 + 0x76, // push0 + 0x81, 0x00, // lag global[0] + 0x4a, 0x04, // send 04 -> get ego::x + 0x36, // push + 0x38, SIG_UINT16(0x00bc), // pushi 00BCh + SIG_END +}; + +static const uint16 sq4CdPatchWalkInFromBelowRoom45[] = { + PATCH_ADDTOOFFSET(+2), + 0x38, PATCH_UINT16(0x00bc), // pushi 00BCh + PATCH_ADDTOOFFSET(+15), + 0x38, PATCH_UINT16(0x00bb), // pushi 00BBh + PATCH_END +}; + +// It seems that Sierra forgot to set a script flag, when cleaning out the bank account +// in Space Quest 4 CD. This was probably caused by the whole bank account interaction +// getting a rewrite and polish in the CD version. +// +// Because of this bug, points for changing back clothes will not get awarded, which +// makes it impossible to get a perfect point score in the CD version of the game. +// The points are awarded by rm371::doit in script 371. +// +// We fix this. Bug also happened, when using the original interpreter. +// Bug does not happen for PC floppy. +// +// Attention: Some Let's Plays on youtube show that points are in fact awarded. Which is true. +// But those Let's Plays were actually created by playing a hacked Space Quest 4 version +// (which is part Floppy, part CD version - we consider it to be effectively pirated) +// and not the actual CD version of Space Quest 4. +// It's easy to identify - talkie + store called "Radio Shack" -> is hacked version. +// +// Applies to at least: English PC CD +// Responsible method: but2Script::changeState(2) +// Fixes bug: #6866 +static const uint16 sq4CdSignatureGetPointsForChangingBackClothes[] = { + 0x35, 0x02, // ldi 02 + SIG_MAGICDWORD, + 0x1a, // eq? + 0x30, SIG_UINT16(0x006a), // bnt [state 3] + 0x76, + SIG_ADDTOOFFSET(+46), // jump over "withdraw funds" code + 0x33, 0x33, // jmp [end of state 2, set cycles code] + SIG_ADDTOOFFSET(+51), // jump over "clean bank account" code + 0x35, 0x02, // ldi 02 + 0x65, 0x1a, // aTop cycles + 0x33, 0x0b, // jmp [toss/ret] + 0x3c, // dup + 0x35, 0x03, // ldi 03 + 0x1a, // eq? + 0x31, 0x05, // bnt [toss/ret] + SIG_END +}; + +static const uint16 sq4CdPatchGetPointsForChangingBackClothes[] = { + PATCH_ADDTOOFFSET(+3), + 0x30, PATCH_UINT16(0x0070), // bnt [state 3] + PATCH_ADDTOOFFSET(+47), // "withdraw funds" code + 0x33, 0x39, // jmp [end of state 2, set cycles code] + PATCH_ADDTOOFFSET(+51), + 0x78, // push1 + 0x39, 0x1d, // ldi 1Dh + 0x45, 0x07, 0x02, // call export 7 of script 0 (set flag) -> effectively sets global 73h, bit 2 + 0x35, 0x02, // ldi 02 + 0x65, 0x1c, // aTop cycles + 0x33, 0x05, // jmp [toss/ret] + // check for state 3 code removed to save 6 bytes + PATCH_END +}; + // The scripts in SQ4CD support simultaneous playing of speech and subtitles, // but this was not available as an option. The following two patches enable // this functionality in the game's GUI options dialog. @@ -2439,8 +2930,10 @@ static const uint16 sq4CdPatchTextOptions[] = { static const SciScriptPatcherEntry sq4Signatures[] = { { true, 298, "Floppy: endless flight", 1, sq4FloppySignatureEndlessFlight, sq4FloppyPatchEndlessFlight }, { true, 700, "Floppy: throw stuff at sequel police bug", 1, sq4FloppySignatureThrowStuffAtSequelPoliceBug, sq4FloppyPatchThrowStuffAtSequelPoliceBug }, - { true, 818, "CD: Speech and subtitles option", 1, sq4CdSignatureTextOptions, sq4CdPatchTextOptions }, + { true, 45, "CD: walk in from below for room 45 fix", 1, sq4CdSignatureWalkInFromBelowRoom45, sq4CdPatchWalkInFromBelowRoom45 }, + { true, 396, "CD: get points for changing back clothes fix",1, sq4CdSignatureGetPointsForChangingBackClothes, sq4CdPatchGetPointsForChangingBackClothes }, { true, 0, "CD: Babble icon speech and subtitles fix", 1, sq4CdSignatureBabbleIcon, sq4CdPatchBabbleIcon }, + { true, 818, "CD: Speech and subtitles option", 1, sq4CdSignatureTextOptions, sq4CdPatchTextOptions }, { true, 818, "CD: Speech and subtitles option button", 1, sq4CdSignatureTextOptionsButton, sq4CdPatchTextOptionsButton }, SCI_SIGNATUREENTRY_TERMINATOR }; @@ -2471,6 +2964,29 @@ static const uint16 sq1vgaPatchUlenceFlatsTimepodGfxGlitch[] = { PATCH_END }; +// In Ulence Flats, there is a space ship, that you will use at some point. +// Near that space ship are 2 force field generators. +// When you look at the top of those generators, the game will crash. +// This happens also in Sierra SCI. It's caused by a jump, that goes out of bounds. +// We currently do not know if this was caused by a compiler glitch or if it was a developer error. +// Anyway we patch this glitchy code, so that the game won't crash anymore. +// +// Applies to at least: English Floppy +// Responsible method: radar1::doVerb +// Fixes bug: #6816 +static const uint16 sq1vgaSignatureUlenceFlatsGeneratorGlitch[] = { + SIG_MAGICDWORD, 0x1a, // eq? + 0x30, SIG_UINT16(0xcdf4), // bnt absolute 0xf000 + SIG_END +}; + +static const uint16 sq1vgaPatchUlenceFlatsGeneratorGlitch[] = { + PATCH_ADDTOOFFSET(+1), + 0x32, PATCH_UINT16(0x0000), // jmp 0x0000 (waste bytes) + PATCH_END +}; + +// No documentation for this patch (TODO) static const uint16 sq1vgaSignatureEgoShowsCard[] = { SIG_MAGICDWORD, 0x38, SIG_SELECTOR16(timesShownID), // push "timesShownID" @@ -2595,6 +3111,7 @@ static const uint16 sq1vgaPatchSpiderDroidTiming[] = { // script, description, signature patch static const SciScriptPatcherEntry sq1vgaSignatures[] = { { true, 45, "Ulence Flats: timepod graphic glitch", 1, sq1vgaSignatureUlenceFlatsTimepodGfxGlitch, sq1vgaPatchUlenceFlatsTimepodGfxGlitch }, + { true, 45, "Ulence Flats: force field generator glitch", 1, sq1vgaSignatureUlenceFlatsGeneratorGlitch, sq1vgaPatchUlenceFlatsGeneratorGlitch }, { true, 58, "Sarien armory droid zapping ego first time", 1, sq1vgaSignatureEgoShowsCard, sq1vgaPatchEgoShowsCard }, { true, 704, "spider droid timing issue", 1, sq1vgaSignatureSpiderDroidTiming, sq1vgaPatchSpiderDroidTiming }, SCI_SIGNATUREENTRY_TERMINATOR @@ -2670,6 +3187,7 @@ ScriptPatcher::ScriptPatcher() { _selectorIdTable[selectorNr] = -1; _runtimeTable = NULL; + _isMacSci11 = false; } ScriptPatcher::~ScriptPatcher() { @@ -2678,7 +3196,7 @@ ScriptPatcher::~ScriptPatcher() { } // will actually patch previously found signature area -void ScriptPatcher::applyPatch(const SciScriptPatcherEntry *patchEntry, byte *scriptData, const uint32 scriptSize, int32 signatureOffset, const bool isMacSci11) { +void ScriptPatcher::applyPatch(const SciScriptPatcherEntry *patchEntry, byte *scriptData, const uint32 scriptSize, int32 signatureOffset) { const uint16 *patchData = patchEntry->patchData; byte orgData[PATCH_VALUELIMIT]; int32 offset = signatureOffset; @@ -2742,7 +3260,7 @@ void ScriptPatcher::applyPatch(const SciScriptPatcherEntry *patchEntry, byte *sc default: byte1 = 0; byte2 = 0; } - if (!isMacSci11) { + if (!_isMacSci11) { scriptData[offset++] = byte1; scriptData[offset++] = byte2; } else { @@ -2769,8 +3287,94 @@ void ScriptPatcher::applyPatch(const SciScriptPatcherEntry *patchEntry, byte *sc } } +bool ScriptPatcher::verifySignature(uint32 byteOffset, const uint16 *signatureData, const char *signatureDescription, const byte *scriptData, const uint32 scriptSize) { + uint16 sigSelector = 0; + + uint16 sigWord = *signatureData; + while (sigWord != SIG_END) { + uint16 sigCommand = sigWord & SIG_COMMANDMASK; + uint16 sigValue = sigWord & SIG_VALUEMASK; + switch (sigCommand) { + case SIG_CODE_ADDTOOFFSET: { + // add value to offset + byteOffset += sigValue; + break; + } + case SIG_CODE_UINT16: + case SIG_CODE_SELECTOR16: { + if ((byteOffset + 1) < scriptSize) { + byte byte1; + byte byte2; + + switch (sigCommand) { + case SIG_CODE_UINT16: { + byte1 = sigValue & SIG_BYTEMASK; + signatureData++; sigWord = *signatureData; + if (sigWord & SIG_COMMANDMASK) + error("Script-Patcher: signature inconsistent\nFaulty signature: '%s'", signatureDescription); + byte2 = sigWord & SIG_BYTEMASK; + break; + } + case SIG_CODE_SELECTOR16: { + sigSelector = _selectorIdTable[sigValue]; + 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; + } + case SIG_CODE_SELECTOR8: { + if (byteOffset < scriptSize) { + sigSelector = _selectorIdTable[sigValue]; + if (sigSelector & 0xFF00) + error("Script-Patcher: 8 bit selector required, game uses 16 bit selector\nFaulty signature: '%s'", signatureDescription); + if (scriptData[byteOffset] != (sigSelector & 0xFF)) + sigWord = SIG_MISMATCH; + byteOffset++; + } else { + sigWord = SIG_MISMATCH; // out of bounds + } + break; + } + case SIG_CODE_BYTE: + if (byteOffset < scriptSize) { + if (scriptData[byteOffset] != sigWord) + sigWord = SIG_MISMATCH; + byteOffset++; + } else { + sigWord = SIG_MISMATCH; // out of bounds + } + } + + if (sigWord == SIG_MISMATCH) + break; + + signatureData++; + sigWord = *signatureData; + } + + if (sigWord == SIG_END) // signature fully matched? + return true; + return false; +} + // will return -1 if no match was found, otherwise an offset to the start of the signature match -int32 ScriptPatcher::findSignature(const SciScriptPatcherEntry *patchEntry, SciScriptPatcherRuntimeEntry *runtimeEntry, const byte *scriptData, const uint32 scriptSize, const bool isMacSci11) { +int32 ScriptPatcher::findSignature(const SciScriptPatcherEntry *patchEntry, SciScriptPatcherRuntimeEntry *runtimeEntry, const byte *scriptData, const uint32 scriptSize) { if (scriptSize < 4) // we need to find a DWORD, so less than 4 bytes is not okay return -1; @@ -2782,89 +3386,8 @@ int32 ScriptPatcher::findSignature(const SciScriptPatcherEntry *patchEntry, SciS if (magicDWord == READ_UINT32(scriptData + DWordOffset)) { // magic DWORD found, check if actual signature matches uint32 offset = DWordOffset + runtimeEntry->magicOffset; - uint32 byteOffset = offset; - 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_CODE_ADDTOOFFSET: { - // add value to offset - byteOffset += sigValue; - break; - } - case SIG_CODE_UINT16: - case SIG_CODE_SELECTOR16: { - if ((byteOffset + 1) < scriptSize) { - byte byte1; - byte byte2; - - switch (sigCommand) { - case SIG_CODE_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_CODE_SELECTOR16: { - sigSelector = _selectorIdTable[sigValue]; - 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; - } - case SIG_CODE_SELECTOR8: { - if (byteOffset < scriptSize) { - sigSelector = _selectorIdTable[sigValue]; - 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_CODE_BYTE: - if (byteOffset < scriptSize) { - if (scriptData[byteOffset] != sigWord) - sigWord = SIG_MISMATCH; - byteOffset++; - } else { - sigWord = SIG_MISMATCH; // out of bounds - } - } - - if (sigWord == SIG_MISMATCH) - break; - - signatureData++; - sigWord = *signatureData; - } - if (sigWord == SIG_END) // signature fully matched? + if (verifySignature(offset, patchEntry->signatureData, patchEntry->description, scriptData, scriptSize)) return offset; } DWordOffset++; @@ -2875,7 +3398,7 @@ int32 ScriptPatcher::findSignature(const SciScriptPatcherEntry *patchEntry, SciS // This method calculates the magic DWORD for each entry in the signature table // and it also initializes the selector table for selectors used in the signatures/patches of the current game -void ScriptPatcher::initSignature(const SciScriptPatcherEntry *patchTable, bool isMacSci11) { +void ScriptPatcher::initSignature(const SciScriptPatcherEntry *patchTable) { const SciScriptPatcherEntry *curEntry = patchTable; SciScriptPatcherRuntimeEntry *curRuntimeEntry; Selector curSelector = -1; @@ -2943,7 +3466,7 @@ void ScriptPatcher::initSignature(const SciScriptPatcherEntry *patchTable, bool curData++; curWord = *curData; if (curWord & SIG_COMMANDMASK) error("Script-Patcher: signature entry inconsistent\nFaulty patch: '%s'", curEntry->description); - if (!isMacSci11) { + if (!_isMacSci11) { byte1 = curValue; byte2 = curWord & SIG_BYTEMASK; } else { @@ -2958,7 +3481,7 @@ void ScriptPatcher::initSignature(const SciScriptPatcherEntry *patchTable, bool curSelector = g_sci->getKernel()->findSelector(selectorNameTable[curValue]); _selectorIdTable[curValue] = curSelector; } - if (!isMacSci11) { + if (!_isMacSci11) { byte1 = curSelector & 0x00FF; byte2 = curSelector >> 8; } else { @@ -3120,7 +3643,7 @@ void ScriptPatcher::processScript(uint16 scriptNr, byte *scriptData, const uint3 } if (signatureTable) { - bool isMacSci11 = (g_sci->getPlatform() == Common::kPlatformMacintosh && getSciVersion() >= SCI_VERSION_1_1); + _isMacSci11 = (g_sci->getPlatform() == Common::kPlatformMacintosh && getSciVersion() >= SCI_VERSION_1_1); if (!_runtimeTable) { // Abort, in case selectors are not yet initialized (happens for games w/o selector-dictionary) @@ -3128,7 +3651,7 @@ void ScriptPatcher::processScript(uint16 scriptNr, byte *scriptData, const uint3 return; // signature table needs to get initialized (Magic DWORD set, selector table set) - initSignature(signatureTable, isMacSci11); + initSignature(signatureTable); // Do additional game-specific initialization switch (gameId) { @@ -3163,11 +3686,11 @@ void ScriptPatcher::processScript(uint16 scriptNr, byte *scriptData, const uint3 int32 foundOffset = 0; int16 applyCount = curEntry->applyCount; do { - foundOffset = findSignature(curEntry, curRuntimeEntry, scriptData, scriptSize, isMacSci11); + foundOffset = findSignature(curEntry, curRuntimeEntry, scriptData, scriptSize); if (foundOffset != -1) { // found, so apply the patch debugC(kDebugLevelScriptPatcher, "Script-Patcher: '%s' on script %d offset %d", curEntry->description, scriptNr, foundOffset); - applyPatch(curEntry, scriptData, scriptSize, foundOffset, isMacSci11); + applyPatch(curEntry, scriptData, scriptSize, foundOffset); } applyCount--; } while ((foundOffset != -1) && (applyCount)); diff --git a/engines/sci/engine/script_patches.h b/engines/sci/engine/script_patches.h index 7023ef327e..d15fce321b 100644 --- a/engines/sci/engine/script_patches.h +++ b/engines/sci/engine/script_patches.h @@ -74,7 +74,6 @@ struct SciScriptPatcherEntry { const uint16 *patchData; }; -//#define SCI_SIGNATUREENTRY_TERMINATOR { false, 0, NULL, 0, 0, 0, NULL, NULL } #define SCI_SIGNATUREENTRY_TERMINATOR { false, 0, NULL, 0, NULL, NULL } struct SciScriptPatcherRuntimeEntry { @@ -92,15 +91,17 @@ public: ~ScriptPatcher(); void processScript(uint16 scriptNr, byte *scriptData, const uint32 scriptSize); + bool verifySignature(uint32 byteOffset, const uint16 *signatureData, const char *signatureDescription, const byte *scriptData, const uint32 scriptSize); private: - void initSignature(const SciScriptPatcherEntry *patchTable, bool isMacSci11); + void initSignature(const SciScriptPatcherEntry *patchTable); void enablePatch(const SciScriptPatcherEntry *patchTable, const char *searchDescription); - int32 findSignature(const SciScriptPatcherEntry *patchEntry, SciScriptPatcherRuntimeEntry *runtimeEntry, const byte *scriptData, const uint32 scriptSize, bool isMacSci11); - void applyPatch(const SciScriptPatcherEntry *patchEntry, byte *scriptData, const uint32 scriptSize, int32 signatureOffset, bool isMacSci11); + int32 findSignature(const SciScriptPatcherEntry *patchEntry, SciScriptPatcherRuntimeEntry *runtimeEntry, const byte *scriptData, const uint32 scriptSize); + void applyPatch(const SciScriptPatcherEntry *patchEntry, byte *scriptData, const uint32 scriptSize, int32 signatureOffset); Selector *_selectorIdTable; SciScriptPatcherRuntimeEntry *_runtimeTable; + bool _isMacSci11; }; } // End of namespace Sci diff --git a/engines/sci/engine/state.cpp b/engines/sci/engine/state.cpp index c07dc925e0..417d98e2e2 100644 --- a/engines/sci/engine/state.cpp +++ b/engines/sci/engine/state.cpp @@ -92,6 +92,10 @@ void EngineState::reset(bool isRestoring) { abortScriptProcessing = kAbortNone; } + // reset delayed restore game functionality + _delayedRestoreGame = false; + _delayedRestoreGameId = 0; + executionStackBase = 0; _executionStackPosChanged = false; stack_base = 0; @@ -205,7 +209,7 @@ static kLanguage charToLanguage(const char c) { Common::String SciEngine::getSciLanguageString(const Common::String &str, kLanguage requestedLanguage, kLanguage *secondaryLanguage, uint16 *languageSplitter) const { kLanguage foundLanguage = K_LANG_NONE; - const byte *textPtr = (byte *)str.c_str(); + const byte *textPtr = (const byte *)str.c_str(); byte curChar = 0; byte curChar2 = 0; diff --git a/engines/sci/engine/state.h b/engines/sci/engine/state.h index ecc8cb7dfe..e7499e66c9 100644 --- a/engines/sci/engine/state.h +++ b/engines/sci/engine/state.h @@ -131,6 +131,10 @@ public: VirtualIndexFile *_virtualIndexFile; #endif + // see detection.cpp / SciEngine::loadGameState() + bool _delayedRestoreGame; // boolean, that triggers delayed restore (triggered by ScummVM menu) + int _delayedRestoreGameId; // the saved game id, that it supposed to get restored (triggered by ScummVM menu) + uint _chosenQfGImportItem; // Remembers the item selected in QfG import rooms bool _cursorWorkaroundActive; // Refer to GfxCursor::setPosition() diff --git a/engines/sci/engine/vm.cpp b/engines/sci/engine/vm.cpp index 06858540ec..6f02c96de8 100644 --- a/engines/sci/engine/vm.cpp +++ b/engines/sci/engine/vm.cpp @@ -358,12 +358,15 @@ static void callKernelFunc(EngineState *s, int kernelCallNr, int argc) { SciTrackOriginReply originReply; SciWorkaroundSolution solution = trackOriginAndFindWorkaround(0, kernelCall.workarounds, &originReply); switch (solution.type) { - case WORKAROUND_NONE: - kernel->signatureDebug(kernelCall.signature, argc, argv); - error("[VM] k%s[%x]: signature mismatch via method %s::%s (room %d, script %d, localCall 0x%x)", + case WORKAROUND_NONE: { + Common::String signatureDetailsStr; + kernel->signatureDebug(signatureDetailsStr, kernelCall.signature, argc, argv); + error("\n%s[VM] k%s[%x]: signature mismatch in method %s::%s (room %d, script %d, localCall 0x%x)", + signatureDetailsStr.c_str(), kernelCall.name, kernelCallNr, originReply.objectName.c_str(), originReply.methodName.c_str(), s->currentRoomNumber(), originReply.scriptNr, originReply.localCallOffset); break; + } case WORKAROUND_IGNORE: // don't do kernel call, leave acc alone return; case WORKAROUND_STILLCALL: // call kernel anyway @@ -408,15 +411,18 @@ static void callKernelFunc(EngineState *s, int kernelCallNr, int argc) { SciWorkaroundSolution solution = trackOriginAndFindWorkaround(0, kernelSubCall.workarounds, &originReply); switch (solution.type) { case WORKAROUND_NONE: { - kernel->signatureDebug(kernelSubCall.signature, argc, argv); + Common::String signatureDetailsStr; + kernel->signatureDebug(signatureDetailsStr, kernelSubCall.signature, argc, argv); int callNameLen = strlen(kernelCall.name); if (strncmp(kernelCall.name, kernelSubCall.name, callNameLen) == 0) { const char *subCallName = kernelSubCall.name + callNameLen; - error("[VM] k%s(%s): signature mismatch via method %s::%s (room %d, script %d, localCall %x)", + error("\n%s[VM] k%s(%s): signature mismatch in method %s::%s (room %d, script %d, localCall %x)", + signatureDetailsStr.c_str(), kernelCall.name, subCallName, originReply.objectName.c_str(), originReply.methodName.c_str(), s->currentRoomNumber(), originReply.scriptNr, originReply.localCallOffset); } - error("[VM] k%s: signature mismatch via method %s::%s (room %d, script %d, localCall %x)", + error("\n%s[VM] k%s: signature mismatch in method %s::%s (room %d, script %d, localCall %x)", + signatureDetailsStr.c_str(), kernelSubCall.name, originReply.objectName.c_str(), originReply.methodName.c_str(), s->currentRoomNumber(), originReply.scriptNr, originReply.localCallOffset); break; diff --git a/engines/sci/engine/workarounds.cpp b/engines/sci/engine/workarounds.cpp index c5730b5345..aab32032f7 100644 --- a/engines/sci/engine/workarounds.cpp +++ b/engines/sci/engine/workarounds.cpp @@ -24,426 +24,716 @@ #include "sci/engine/object.h" #include "sci/engine/state.h" #include "sci/engine/vm.h" +#include "sci/engine/script_patches.h" #include "sci/engine/workarounds.h" -#define SCI_WORKAROUNDENTRY_TERMINATOR { (SciGameId)0, -1, -1, 0, NULL, NULL, -1, 0, { WORKAROUND_NONE, 0 } } +#define SCI_WORKAROUNDENTRY_TERMINATOR { (SciGameId)0, -1, -1, 0, NULL, NULL, NULL, 0, { WORKAROUND_NONE, 0 } } namespace Sci { -// gameID, room,script,lvl, object-name, method-name, call,index, workaround +// Attention: +// To identify local-call-subroutines code signatures are used. +// Offsets change a lot between different versions of games, especially between different language releases. +// That's why it isn't good to hardcode the offsets of those subroutines. +// +// Those signatures are just like the script patcher signatures (for further study: engine\script_patches.cpp) +// However you may NOT use command SIG_SELECTOR8 nor SIG_SELECTOR16 atm. Proper support for those may be added later. + +// Game: Conquests of Camelot +// Calling method: endingCartoon2::changeState +// Subroutine offset: English 0x020d (script 92) +// Applies to at least: English PC floppy +static const uint16 sig_arithmetic_camelot_1[] = { + 0x83, 0x32, // lal local[32h] + 0x30, SIG_UINT16(0x001d), // bnt [...] + 0x7a, // push2 + 0x39, 0x08, // pushi 08 + 0x36, // push + 0x43, // callk Graph + SIG_END +}; + +// Game: Eco Quest 2 +// Calling method: Rain::points +// Subroutine offset: English 0x0cc6, French/Spanish 0x0ce0 (script 0) +// Applies to at least: English/French/Spanish PC floppy +static const uint16 sig_arithmetic_ecoq2_1[] = { + 0x8f, 0x01, // lsp param[1] + 0x35, 0x10, // ldi 10h + 0x08, // div + 0x99, 0x6e, // lsgi global[6Eh] + 0x38, SIG_UINT16(0x8000), // pushi 8000h + 0x8f, 0x01, // lsp param[1] + 0x35, 0x10, // ldi 10h + 0x0a, // mod + 0x0c, // shr + 0x14, // or + 0x36, // push + SIG_END +}; + +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround const SciWorkaroundEntry arithmeticWorkarounds[] = { - { GID_CAMELOT, 92, 92, 0, "endingCartoon2", "changeState", 0x20d, 0, { WORKAROUND_FAKE, 0 } }, // op_lai: during the ending, sub gets called with no parameters, uses parameter 1 which is theGrail in this case - bug #5237 - { GID_ECOQUEST2, 100, 0, 0, "Rain", "points", 0xcc6, 0, { WORKAROUND_FAKE, 0 } }, // op_or: when giving the papers to the customs officer, gets called against a pointer instead of a number - bug #4939 - { GID_ECOQUEST2, 100, 0, 0, "Rain", "points", 0xce0, 0, { WORKAROUND_FAKE, 0 } }, // Same as above, for the Spanish version - bug #5750 - { 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 #5124 - { 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 #5101 - { GID_MOTHERGOOSEHIRES,90, 90, 0, "newGameButton", "select", -1, 0, { WORKAROUND_FAKE, 0 } }, // op_ge: MUMG Deluxe, when selecting "New Game" in the main menu. It tries to compare an integer with a list. Needs to return false for the game to continue. - { GID_PHANTASMAGORIA, 902, 0, 0, "", "export 7", -1, 0, { WORKAROUND_FAKE, 0 } }, // op_shr: when starting a chapter in Phantasmagoria - { GID_QFG1VGA, 301, 928, 0, "Blink", "init", -1, 0, { WORKAROUND_FAKE, 0 } }, // op_div: when entering the inn, gets called with 1 parameter, but 2nd parameter is used for div which happens to be an object - { GID_QFG2, 200, 200, 0, "astro", "messages", -1, 0, { WORKAROUND_FAKE, 0 } }, // op_lsi: when getting asked for your name by the astrologer - bug #5152 - { GID_QFG3, 780, 999, 0, "", "export 6", -1, 0, { WORKAROUND_FAKE, 0 } }, // op_add: trying to talk to yourself at the top of the giant tree - bug #6692 - { GID_QFG4, 710,64941, 0, "RandCycle", "doit", -1, 0, { WORKAROUND_FAKE, 1 } }, // op_gt: when the tentacle appears in the third room of the caves - SCI_WORKAROUNDENTRY_TERMINATOR -}; - -// gameID, room,script,lvl, object-name, method-name, call,index, workaround + { GID_CAMELOT, 92, 92, 0, "endingCartoon2", "changeState", sig_arithmetic_camelot_1, 0, { WORKAROUND_FAKE, 0 } }, // op_lai: during the ending, sub gets called with no parameters, uses parameter 1 which is theGrail in this case - bug #5237 + { GID_ECOQUEST2, 100, 0, 0, "Rain", "points", sig_arithmetic_ecoq2_1, 0, { WORKAROUND_FAKE, 0 } }, // op_or: when giving the papers to the customs officer, gets called against a pointer instead of a number - bug #4939, Spanish version - bug #5750 + { GID_FANMADE, 516, 983, 0, "Wander", "setTarget", NULL, 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 #5124 + { GID_GK1, 800,64992, 0, "Fwd", "doit", NULL, 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", NULL, 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", NULL, 0, { WORKAROUND_FAKE, 0 } }, // op_add: While dancing with the girl + { GID_MOTHERGOOSE256, -1, 999, 0, "Event", "new", NULL, 0, { WORKAROUND_FAKE, 0 } }, // op_and: constantly during the game (SCI1 version) + { GID_MOTHERGOOSE256, -1, 4, 0, "rm004", "doit", NULL, 0, { WORKAROUND_FAKE, 0 } }, // op_or: when going north and reaching the castle (rooms 4 and 37) - bug #5101 + { GID_MOTHERGOOSEHIRES,90, 90, 0, "newGameButton", "select", NULL, 0, { WORKAROUND_FAKE, 0 } }, // op_ge: MUMG Deluxe, when selecting "New Game" in the main menu. It tries to compare an integer with a list. Needs to return false for the game to continue. + { GID_PHANTASMAGORIA, 902, 0, 0, "", "export 7", NULL, 0, { WORKAROUND_FAKE, 0 } }, // op_shr: when starting a chapter in Phantasmagoria + { GID_QFG1VGA, 301, 928, 0, "Blink", "init", NULL, 0, { WORKAROUND_FAKE, 0 } }, // op_div: when entering the inn, gets called with 1 parameter, but 2nd parameter is used for div which happens to be an object + { GID_QFG2, 200, 200, 0, "astro", "messages", NULL, 0, { WORKAROUND_FAKE, 0 } }, // op_lsi: when getting asked for your name by the astrologer - bug #5152 + { GID_QFG3, 780, 999, 0, "", "export 6", NULL, 0, { WORKAROUND_FAKE, 0 } }, // op_add: trying to talk to yourself at the top of the giant tree - bug #6692 + { GID_QFG4, 710,64941, 0, "RandCycle", "doit", NULL, 0, { WORKAROUND_FAKE, 1 } }, // op_gt: when the tentacle appears in the third room of the caves + SCI_WORKAROUNDENTRY_TERMINATOR +}; + +// Game: Fan-Made "Ocean Battle" +// Calling method: RoomScript::doit +// Subroutine offset: 0x1f17 +// Applies to at least: Ocean Battle +static const uint16 sig_uninitread_fanmade_1[] = { + 0x3f, 0x04, // link 04 + 0x88, SIG_UINT16(0x023b), // lsg global[23Bh] + SIG_END +}; + +// Game: Hoyle 1 +// Calling method: export 0 +// Subroutine offset: 0x037c (script 16) +// Applies to at least: English PC floppy +static const uint16 sig_uninitread_hoyle1_1[] = { + 0x3f, 0x05, // link 05 + 0x78, // push1 + 0x76, // push0 + 0x40, // call [...] + SIG_END +}; + +// Game: Hoyle 4 +// Calling method: export 2 +// Subroutine offset: 0x1d4d (script 300) +// Applies to at least: English PC floppy +static const uint16 sig_uninitread_hoyle4_1[] = { + 0x3f, 0x01, // link 01 + 0x39, 0x40, // pushi 40h + 0x78, // push1 + SIG_END +}; + +// Game: Jones in the fast lane +// Calling method: weekendText::draw +// Subroutine offset: 0x03d3 (script 232) +// Applies to at least: English PC CD +static const uint16 sig_uninitread_jones_1[] = { + 0x3f, 0x02, // link 02 + 0x8d, 0x00, // lst temp[0] + 0x35, 0x01, // ldi 01 + 0x22, // lt? + SIG_END +}; + +// Game: Conquests of the Longbow +// Calling method: letter::handleEvent +// Subroutine offset: English PC/Amiga 0x00a8 (script 213) +// Applies to at least: English PC floppy, English Amiga floppy +static const uint16 sig_uninitread_longbow_1[] = { + 0x3f, 0x02, // link 02 + 0x35, 0x00, // ldi 00 + 0xa5, 0x00, // sat temp[0] + 0x8d, 0x00, // lst temp[0] + SIG_END +}; + +// Game: Quest for Glory 1 / Hero's Quest 1 +// Calling method: Encounter::init +// Subroutine offset: English Hero's Quest 0x0bd0, English Quest for Glory 1 0x0be4 (script 210) +// Applies to at least: English PC floppy (Hero's Quest, Quest For Glory 1), Japanese PC-9801 floppy +static const uint16 sig_uninitread_qfg1_1[] = { + 0x3f, 0x02, // link 02 + 0x87, 0x00, // lap param[0] + 0x30, SIG_UINT16(0x000c), // bnt [...] + 0x87, 0x01, // lap param[1] + 0x30, SIG_UINT16(0x0007), // bnt [...] + 0x87, 0x01, // lap param[1] + 0xa5, 0x01, // sat temp[1] + SIG_END +}; + +// Game: Quest for Glory 1 VGA +// Calling method: Encounter::init +// Subroutine offset: English w/o patch 0x0cee, w/ patch 0x0ce7 (script 210) +// Applies to at least: English PC floppy +static const uint16 sig_uninitread_qfg1vga_1[] = { + 0x3f, 0x02, // link 02 + 0x87, 0x00, // lap param[0] + 0x31, 0x0b, // bnt [...] + 0x87, 0x01, // lap param[1] + 0x31, 0x07, // bnt [...] + 0x87, 0x01, // lap param[1] + 0xa5, 0x01, // sat temp[1] + // following jump is different for patched and unpatched game + SIG_END +}; + +// Game: Quest for Glory 2 +// Calling method: abdulS::changeState, jabbarS::changeState +// Subroutine offset: English 0x2d22 (script 260) +// Applies to at least: English PC floppy +static const uint16 sig_uninitread_qfg2_1[] = { + 0x3f, 0x03, // link 03 + 0x39, 0x3b, // pushi 3Bh + 0x76, // push0 + 0x81, 0x00, // lag global[0] + 0x4a, 0x04, // send 04 + SIG_END +}; + +// Game: Quest for Glory 3 +// Calling method: rm140::init +// Subroutine offset: English 0x1008 (script 140) +// Applies to at least: English, French, German, Italian, Spanish PC floppy +static const uint16 sig_uninitread_qfg3_1[] = { + 0x3f, 0x01, // link 01 + 0x89, 0x7d, // lsg global[7Dh] + 0x35, 0x03, // ldi 03 + SIG_END +}; + +// Game: Quest for Glory 3 +// Calling method: computersMove::changeState +// Subroutine offset: English/etc. 0x0f53 (script 490) +// Applies to at least: English, French, German, Italian, Spanish PC floppy +static const uint16 sig_uninitread_qfg3_2[] = { + 0x3f, 0x1d, // link 1Dh + 0x35, 0x01, // ldi 01 + 0xa5, 0x18, // sat temp[18h] + 0x35, 0xce, // ldi CEh + 0xa5, 0x19, // sat temp[19h] + 0x35, 0x00, // ldi 00 + 0xa5, 0x00, // sat temp[0] + SIG_END +}; + +// Game: Space Quest 1 +// Calling method: firePulsar::changeState +// Subroutine offset: English 0x018a (script 703) +// Applies to at least: English PC floppy, English Amiga floppy +static const uint16 sig_uninitread_sq1_1[] = { + 0x3f, 0x01, // link 01 + 0x38, SIG_ADDTOOFFSET(+2), // pushi 0242 (selector egoStatus) + 0x76, // push0 + 0x72, SIG_ADDTOOFFSET(+2), // lofsa DeltaurRegion + 0x4a, 0x04, // send 04 + SIG_END +}; + +// gameID, room,script,lvl, object-name, method-name, local-call-signature, 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 #6401 - { 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 #5143). 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 #6255 - { 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 #6426 (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 #6424 - { GID_CNICK_LAURABOW, -1, 700, 0, NULL, "open", -1, -1, { WORKAROUND_FAKE, 0 } }, // when entering control menu - bug #6423 (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 #6429 (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 #6430 - { 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 - { GID_FANMADE, 528, 990, 0, "GDialog", "doit", -1, 4, { WORKAROUND_FAKE, 0 } }, // Happens in Cascade Quest when closing the glossary - bug #5116 - { GID_FANMADE, 488, 1, 0, "RoomScript", "doit", 0x1f17, 1, { WORKAROUND_FAKE, 0 } }, // Happens in Ocean Battle while playing - bug #5335 - { GID_FREDDYPHARKAS, -1, 24, 0, "gcWin", "open", -1, 5, { WORKAROUND_FAKE, 0xf } }, // is used as priority for game menu - { GID_FREDDYPHARKAS, -1, 31, 0, "quitWin", "open", -1, 5, { WORKAROUND_FAKE, 0xf } }, // is used as priority for game menu - { GID_FREDDYPHARKAS, 540, 540, 0, "WaverCode", "init", -1, -1, { WORKAROUND_FAKE, 0 } }, // Gun pratice mini-game - bug #5232 - { GID_GK1, -1, 64950, -1, "Feature", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // sometimes when walk-clicking - { GID_GK2, -1, 11, 0, "", "export 10", -1, 3, { WORKAROUND_FAKE, 0 } }, // called when the game starts - { GID_GK2, -1, 11, 0, "", "export 10", -1, 4, { WORKAROUND_FAKE, 0 } }, // called during the game - { GID_HOYLE1, 4, 104, 0, "GinRummyCardList", "calcRuns", -1, 4, { WORKAROUND_FAKE, 0 } }, // Gin Rummy / right when the game starts - { GID_HOYLE1, 5, 204, 0, "tableau", "checkRuns", -1, 2, { WORKAROUND_FAKE, 0 } }, // Cribbage / during the game - { GID_HOYLE1, 3, 16, 0, "", "export 0", 0x37c, 3, { WORKAROUND_FAKE, 0 } }, // Hearts / during the game - bug #5299 - { GID_HOYLE1, -1, 997, 0, "MenuBar", "doit", -1, 0, { WORKAROUND_FAKE, 0 } }, // When changing game speed settings - bug #5512 - { 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 #5042 - { GID_HOYLE3, 100, 110, 0, "OKButton", "doit", -1, -1, { WORKAROUND_FAKE, 0 } }, // when changing the "Dominoes per hand" setting - bug #6430 - { 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 #5132 - { GID_HOYLE4, 910, 18, 0, NULL, "init", -1, 0, { WORKAROUND_FAKE, 0 } }, // during tutorial - bug #5213 - { GID_HOYLE4, 910, 910, 0, NULL, "setup", -1, 3, { WORKAROUND_FAKE, 0 } }, // when selecting "Tutorial" from the main menu - bug #5132 - { 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 #5794 - { 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 #5663 and #5794 - { GID_HOYLE4, 700, 921, 0, "Print", "addEdit", -1, 0, { WORKAROUND_FAKE, 118 } }, // when saving the game (may also occur in other situations) - bug #6601, bug #6614 - { GID_HOYLE4, 700, 921, 0, "Print", "addEdit", -1, 1, { WORKAROUND_FAKE, 1 } }, // see above, Text-control saves its coordinates to temp[0] and temp[1], Edit-control adjusts to those uninitialized temps, who by accident were left over from the Text-control - { 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 #5665 - { 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 #5662 - { GID_HOYLE4, -1, 937, 0, "IconBar", "dispatchEvent", -1, 408, { WORKAROUND_FAKE, 0 } }, // pressing ENTER on scoreboard while mouse is not on OK button, may not happen all the time - bug #6603 - { GID_ISLANDBRAIN, 100, 937, 0, "IconBar", "dispatchEvent", -1, 58, { WORKAROUND_FAKE, 0 } }, // when using ENTER at the startup menu - bug #5241 - { GID_ISLANDBRAIN, 140, 140, 0, "piece", "init", -1, 3, { WORKAROUND_FAKE, 1 } }, // first puzzle right at the start, some initialization variable. bnt is done on it, and it should be non-0 - { GID_ISLANDBRAIN, 200, 268, 0, "anElement", "select", -1, 0, { WORKAROUND_FAKE, 0 } }, // elements puzzle, gets used before super TextIcon - { GID_JONES, 1, 232, 0, "weekendText", "draw", 0x3d3, 0, { WORKAROUND_FAKE, 0 } }, // jones/cd only - gets called during the game - { GID_JONES, 1, 255, 0, "", "export 0", -1, -1, { WORKAROUND_FAKE, 0 } }, // jones/cd only - called when a game ends, temps 13 and 14 - { GID_JONES, 764, 255, 0, "", "export 0", -1, -1, { WORKAROUND_FAKE, 0 } }, // jones/ega&vga only - called when the game starts, temps 13 and 14 - //{ GID_KQ5, -1, 0, 0, "", "export 29", -1, 3, { WORKAROUND_FAKE, 0xf } }, // called when playing harp for the harpies or when aborting dialog in toy shop, is used for kDoAudio - bug #4961 + { GID_CAMELOT, 40, 40, 0, "Rm40", "handleEvent", NULL, 0, { WORKAROUND_FAKE, 0 } }, // when looking at the ground at the pool of Siloam - bug #6401 + { GID_CASTLEBRAIN, 280, 280, 0, "programmer", "dispatchEvent", NULL, 0, { WORKAROUND_FAKE, 0xf } }, // pressing 'q' on the computer screen in the robot room, and closing the help dialog that pops up (bug #5143). Moves the cursor to the view with the ID returned (in this case, the robot hand) + { GID_CNICK_KQ, -1, 0, 1, "Character", "say", NULL, -1, { WORKAROUND_FAKE, 0 } }, // checkers/backgammon, like in hoyle 3 - temps 504 and 505 - bug #6255 + { GID_CNICK_KQ, -1, 700, 0, "gcWindow", "open", NULL, -1, { WORKAROUND_FAKE, 0 } }, // when entering the control menu, like in hoyle 3 + { GID_CNICK_KQ, 300, 303, 0, "theDoubleCube", "<noname520>", NULL, 5, { WORKAROUND_FAKE, 0 } }, // while playing backgammon with doubling enabled - bug #6426 (same as the theDoubleCube::make workaround for Hoyle 3) + { GID_CNICK_KQ, 300, 303, 0, "theDoubleCube", "<noname519>", NULL, 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", NULL, -1, { WORKAROUND_FAKE, 0 } }, // Yatch, like in hoyle 3 - temps 504 and 505 - bug #6424 + { GID_CNICK_LAURABOW, -1, 700, 0, NULL, "open", NULL, -1, { WORKAROUND_FAKE, 0 } }, // when entering control menu - bug #6423 (same as the gcWindow workaround for Hoyle 3) + { GID_CNICK_LAURABOW,100, 100, 0, NULL, "<noname144>", NULL, 1, { WORKAROUND_FAKE, 0 } }, // while playing domino - bug #6429 (same as the dominoHand2 workaround for Hoyle 3) + { GID_CNICK_LAURABOW,100, 110, 0, NULL, "doit", NULL, -1, { WORKAROUND_FAKE, 0 } }, // when changing the "Dominoes per hand" setting - bug #6430 + { GID_CNICK_LONGBOW, 0, 0, 0, "RH Budget", "init", NULL, 1, { WORKAROUND_FAKE, 0 } }, // when starting the game + { GID_ECOQUEST, -1, -1, 0, NULL, "doVerb", NULL, 0, { WORKAROUND_FAKE, 0 } }, // almost clicking anywhere triggers this in almost all rooms + { GID_FANMADE, 516, 979, 0, "", "export 0", NULL, 20, { WORKAROUND_FAKE, 0 } }, // Happens in Grotesteing after the logos + { GID_FANMADE, 528, 990, 0, "GDialog", "doit", NULL, 4, { WORKAROUND_FAKE, 0 } }, // Happens in Cascade Quest when closing the glossary - bug #5116 + { GID_FANMADE, 488, 1, 0, "RoomScript", "doit", sig_uninitread_fanmade_1, 1, { WORKAROUND_FAKE, 0 } }, // Happens in Ocean Battle while playing - bug #5335 + { GID_FREDDYPHARKAS, -1, 24, 0, "gcWin", "open", NULL, 5, { WORKAROUND_FAKE, 0xf } }, // is used as priority for game menu + { GID_FREDDYPHARKAS, -1, 31, 0, "quitWin", "open", NULL, 5, { WORKAROUND_FAKE, 0xf } }, // is used as priority for game menu + { GID_FREDDYPHARKAS, 540, 540, 0, "WaverCode", "init", NULL, -1, { WORKAROUND_FAKE, 0 } }, // Gun pratice mini-game - bug #5232 + { GID_GK1, -1, 64950, -1, "Feature", "handleEvent", NULL, 0, { WORKAROUND_FAKE, 0 } }, // sometimes when walk-clicking + { GID_GK2, -1, 11, 0, "", "export 10", NULL, 3, { WORKAROUND_FAKE, 0 } }, // called when the game starts + { GID_GK2, -1, 11, 0, "", "export 10", NULL, 4, { WORKAROUND_FAKE, 0 } }, // called during the game + { GID_HOYLE1, 4, 104, 0, "GinRummyCardList", "calcRuns", NULL, 4, { WORKAROUND_FAKE, 0 } }, // Gin Rummy / right when the game starts + { GID_HOYLE1, 5, 204, 0, "tableau", "checkRuns", NULL, 2, { WORKAROUND_FAKE, 0 } }, // Cribbage / during the game + { GID_HOYLE1, 3, 16, 0, "", "export 0", sig_uninitread_hoyle1_1, 3, { WORKAROUND_FAKE, 0 } }, // Hearts / during the game - bug #5299 + { GID_HOYLE1, -1, 997, 0, "MenuBar", "doit", NULL, 0, { WORKAROUND_FAKE, 0 } }, // When changing game speed settings - bug #5512 + { GID_HOYLE3, -1, 0, 1, "Character", "say", NULL, -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", NULL, -1, { WORKAROUND_FAKE, 0 } }, // when entering control menu + { GID_HOYLE3, 100, 100, 0, "dominoHand2", "cue", NULL, 1, { WORKAROUND_FAKE, 0 } }, // while playing domino - bug #5042 + { GID_HOYLE3, 100, 110, 0, "OKButton", "doit", NULL, -1, { WORKAROUND_FAKE, 0 } }, // when changing the "Dominoes per hand" setting - bug #6430 + { GID_HOYLE3, 300, 303, 0, "theDoubleCube", "make", NULL, 5, { WORKAROUND_FAKE, 0 } }, // while playing backgammon with doubling enabled + { GID_HOYLE3, 300, 303, 0, "theDoubleCube", "accept", NULL, 9, { WORKAROUND_FAKE, 0 } }, // when accepting a double, while playing backgammon with doubling enabled + { GID_HOYLE4, -1, 0, 0, NULL, "open", NULL, -1, { WORKAROUND_FAKE, 0 } }, // when selecting "Control" from the menu (temp vars 0-3) - bug #5132 + { GID_HOYLE4, 910, 18, 0, NULL, "init", NULL, 0, { WORKAROUND_FAKE, 0 } }, // during tutorial - bug #5213 + { GID_HOYLE4, 910, 910, 0, NULL, "setup", NULL, 3, { WORKAROUND_FAKE, 0 } }, // when selecting "Tutorial" from the main menu - bug #5132 + { GID_HOYLE4, 700, 700, 1, "BridgeHand", "calcQTS", NULL, 3, { WORKAROUND_FAKE, 0 } }, // when placing a bid in bridge (always) + { GID_HOYLE4, 700, 710, 1, "BridgeStrategyPlay", "checkSplitTops", NULL, 10, { WORKAROUND_FAKE, 0 } }, // while playing bridge, objects LeadReturn_Trump, SecondSeat_Trump, ThirdSeat_Trump and others - bug #5794 + { GID_HOYLE4, 700, -1, 1, "BridgeDefense", "think", NULL, -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", NULL, 3, { WORKAROUND_FAKE, 0 } }, // rarely while playing bridge + { GID_HOYLE4, 700, -1, 1, "Code", "doit", NULL, -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 #5663 and #5794 + { GID_HOYLE4, 700, 921, 0, "Print", "addEdit", NULL, 0, { WORKAROUND_FAKE, 118 } }, // when saving the game (may also occur in other situations) - bug #6601, bug #6614 + { GID_HOYLE4, 700, 921, 0, "Print", "addEdit", NULL, 1, { WORKAROUND_FAKE, 1 } }, // see above, Text-control saves its coordinates to temp[0] and temp[1], Edit-control adjusts to those uninitialized temps, who by accident were left over from the Text-control + { GID_HOYLE4, 300, 300, 0, "", "export 2", sig_uninitread_hoyle4_1, 0, { WORKAROUND_FAKE, 0 } }, // after passing around cards in hearts + { GID_HOYLE4, 400, 400, 1, "GinHand", "calcRuns", NULL, 4, { WORKAROUND_FAKE, 0 } }, // sometimes while playing Gin Rummy (e.g. when knocking and placing a card) - bug #5665 + { GID_HOYLE4, 500, 17, 1, "Character", "say", NULL, 504, { WORKAROUND_FAKE, 0 } }, // sometimes while playing Cribbage (e.g. when the opponent says "Last Card") - bug #5662 + { GID_HOYLE4, 800, 870, 0, "EuchreStrategy", "thinkLead", NULL, 0, { WORKAROUND_FAKE, 0 } }, // while playing Euchre, happens at least on 2nd or 3rd turn - bug #6602 + { GID_HOYLE4, -1, 937, 0, "IconBar", "dispatchEvent", NULL, 408, { WORKAROUND_FAKE, 0 } }, // pressing ENTER on scoreboard while mouse is not on OK button, may not happen all the time - bug #6603 + { GID_ISLANDBRAIN, 100, 937, 0, "IconBar", "dispatchEvent", NULL, 58, { WORKAROUND_FAKE, 0 } }, // when using ENTER at the startup menu - bug #5241 + { GID_ISLANDBRAIN, 140, 140, 0, "piece", "init", NULL, 3, { WORKAROUND_FAKE, 1 } }, // first puzzle right at the start, some initialization variable. bnt is done on it, and it should be non-0 + { GID_ISLANDBRAIN, 200, 268, 0, "anElement", "select", NULL, 0, { WORKAROUND_FAKE, 0 } }, // elements puzzle, gets used before super TextIcon + { GID_JONES, 1, 232, 0, "weekendText", "draw", sig_uninitread_jones_1, 0, { WORKAROUND_FAKE, 0 } }, // jones/cd only - gets called during the game + { GID_JONES, 1, 255, 0, "", "export 0", NULL, -1, { WORKAROUND_FAKE, 0 } }, // jones/cd only - called when a game ends, temps 13 and 14 + { GID_JONES, 764, 255, 0, "", "export 0", NULL, -1, { WORKAROUND_FAKE, 0 } }, // jones/ega&vga only - called when the game starts, temps 13 and 14 + //{ GID_KQ5, -1, 0, 0, "", "export 29", NULL, 3, { WORKAROUND_FAKE, 0xf } }, // called when playing harp for the harpies or when aborting dialog in toy shop, is used for kDoAudio - bug #4961 // ^^ shouldn't be needed anymore, we got a script patch instead (kq5PatchCdHarpyVolume) - { GID_KQ5, 25, 25, 0, "rm025", "doit", -1, 0, { WORKAROUND_FAKE, 0 } }, // inside witch forest, when going to the room where the walking rock is - { GID_KQ5, 55, 55, 0, "helpScript", "doit", -1, 0, { WORKAROUND_FAKE, 0 } }, // when giving the tambourine to the monster in the labyrinth (only happens at one of the locations) - bug #5198 - { GID_KQ5, -1, 755, 0, "gcWin", "open", -1, -1, { WORKAROUND_FAKE, 0 } }, // when entering control menu in the FM-Towns version - { GID_KQ6, -1, 30, 0, "rats", "changeState", -1, -1, { WORKAROUND_FAKE, 0 } }, // rats in the catacombs (temps 1 - 5) - bugs #4958, #4998, #5017 - { GID_KQ6, 210, 210, 0, "rm210", "scriptCheck", -1, 0, { WORKAROUND_FAKE, 1 } }, // using inventory in that room - bug #4953 - { GID_KQ6, 500, 500, 0, "rm500", "init", -1, 0, { WORKAROUND_FAKE, 0 } }, // going to island of the beast - { 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 #5331 - { 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 #5084 - { GID_LAURABOW, -1, 967, 0, "myIcon", "cycle", -1, 1, { WORKAROUND_FAKE, 0 } }, // having any portrait conversation coming up - initial bug #4971 - { GID_LAURABOW2, -1, 24, 0, "gcWin", "open", -1, 5, { WORKAROUND_FAKE, 0xf } }, // is used as priority for game menu - { 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 #4979, #5026 - { GID_LAURABOW2, -1, 90, 1, "MuseumActor", "init", -1, 6, { WORKAROUND_FAKE, 0 } }, // Random actors in museum - bug #5197 - { GID_LAURABOW2, 240, 240, 0, "sSteveAnimates", "changeState", -1, 0, { WORKAROUND_FAKE, 0 } }, // Steve Dorian's idle animation at the docks - bug #5028 - { 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 #5244 - { 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 #5035 - { GID_LSL1, 250, 250, 0, "increase", "handleEvent", -1, 2, { WORKAROUND_FAKE, 0 } }, // casino, playing game, increasing bet - { GID_LSL1, 720, 720, 0, "rm720", "init", -1, 0, { WORKAROUND_FAKE, 0 } }, // age check room - { GID_LSL2, 38, 38, 0, "cloudScript", "changeState", -1, 1, { WORKAROUND_FAKE, 0 } }, // entering the room in the middle deck of the ship - bug #5034 - { GID_LSL3, 340, 340, 0, "ComicScript", "changeState", -1, -1, { WORKAROUND_FAKE, 0 } }, // right after entering the 3 ethnic groups inside comedy club (temps 200, 201, 202, 203) - { GID_LSL3, -1, 997, 0, "TheMenuBar", "handleEvent", -1, 1, { WORKAROUND_FAKE, 0xf } }, // when setting volume the first time, this temp is used to set volume on entry (normally it would have been initialized to 's') - { GID_LSL6, 820, 82, 0, "", "export 0", -1, -1, { WORKAROUND_FAKE, 0 } }, // when touching the electric fence - bug #5103 - { GID_LSL6, -1, 85, 0, "washcloth", "doVerb", -1, 0, { WORKAROUND_FAKE, 0 } }, // washcloth in inventory - { GID_LSL6, -1, 928, -1, "Narrator", "startText", -1, 0, { WORKAROUND_FAKE, 0 } }, // used by various objects that are even translated in foreign versions, that's why we use the base-class - { GID_LSL6HIRES, 0, 85, 0, "LL6Inv", "init", -1, 0, { WORKAROUND_FAKE, 0 } }, // on startup - { GID_LSL6HIRES, -1, 64950, 1, "Feature", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // at least when entering swimming pool area - { GID_LSL6HIRES, -1, 64964, 0, "DPath", "init", -1, 1, { WORKAROUND_FAKE, 0 } }, // during the game - { GID_MOTHERGOOSE256, -1, 0, 0, "MG", "doit", -1, 5, { WORKAROUND_FAKE, 0 } }, // SCI1.1: When moving the cursor all the way to the left during the game - bug #5224 - { GID_MOTHERGOOSE256, -1, 992, 0, "AIPath", "init", -1, 0, { WORKAROUND_FAKE, 0 } }, // Happens in the demo and full version. In the demo, it happens when walking two screens from mother goose's house to the north. In the full version, it happens in rooms 7 and 23 - bug #5269 - { GID_MOTHERGOOSE256, 90, 90, 0, "introScript", "changeState", -1, 65, { WORKAROUND_FAKE, 0 } }, // SCI1(CD): At the very end, after the game is completed and restarted - bug #5626 - { GID_MOTHERGOOSE256, 94, 94, 0, "sunrise", "changeState", -1, 367, { WORKAROUND_FAKE, 0 } }, // At the very end, after the game is completed - bug #5294 - { GID_MOTHERGOOSEHIRES,-1,64950, 1, "Feature", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // right when clicking on a child at the start and probably also later - { GID_MOTHERGOOSEHIRES,-1,64950, 1, "View", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // see above - { GID_PEPPER, -1, 894, 0, "Package", "doVerb", -1, 3, { WORKAROUND_FAKE, 0 } }, // using the hand on the book in the inventory - bug #5154 - { GID_PEPPER, 150, 928, 0, "Narrator", "startText", -1, 0, { WORKAROUND_FAKE, 0 } }, // happens during the non-interactive demo of Pepper - { GID_PQ4, -1, 25, 0, "iconToggle", "select", -1, 1, { WORKAROUND_FAKE, 0 } }, // when toggling the icon bar to auto-hide or not - { GID_PQSWAT, -1, 64950, 0, "View", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // Using the menu in the beginning - { GID_QFG1, -1, 210, 0, "Encounter", "init", 0xbd0, 0, { WORKAROUND_FAKE, 0 } }, // hq1: going to the brigands hideout - { GID_QFG1, -1, 210, 0, "Encounter", "init", 0xbe4, 0, { WORKAROUND_FAKE, 0 } }, // qfg1: going to the brigands hideout - { GID_QFG1VGA, 16, 16, 0, "lassoFailed", "changeState", -1, -1, { WORKAROUND_FAKE, 0 } }, // qfg1vga: casting the "fetch" spell in the screen with the flowers, temps 0 and 1 - bug #5309 - { GID_QFG1VGA, -1, 210, 0, "Encounter", "init", 0xcee, 0, { WORKAROUND_FAKE, 0 } }, // qfg1vga: going to the brigands hideout - bug #5515 - { GID_QFG1VGA, -1, 210, 0, "Encounter", "init", 0xce7, 0, { WORKAROUND_FAKE, 0 } }, // qfg1vga: going to room 92 - { GID_QFG2, -1, 71, 0, "theInvSheet", "doit", -1, 1, { WORKAROUND_FAKE, 0 } }, // accessing the inventory - { GID_QFG2, -1, 701, -1, "Alley", "at", -1, 0, { WORKAROUND_FAKE, 0 } }, // when walking inside the alleys in the town - bug #5019 & #5106 - { GID_QFG2, -1, 990, 0, "Restore", "doit", -1, 364, { WORKAROUND_FAKE, 0 } }, // when pressing enter in restore dialog w/o any saved games present - { GID_QFG2, 260, 260, 0, "abdulS", "changeState",0x2d22, -1, { WORKAROUND_FAKE, 0 } }, // During the thief's first mission (in the house), just before Abdul is about to enter the house (where you have to hide in the wardrobe), bug #5153, temps 1 and 2 - { GID_QFG2, 260, 260, 0, "jabbarS", "changeState",0x2d22, -1, { WORKAROUND_FAKE, 0 } }, // During the thief's first mission (in the house), just before Jabbar is about to enter the house (where you have to hide in the wardrobe), bug #5164, temps 1 and 2 - { GID_QFG2, 500, 500, 0, "lightNextCandleS", "changeState", -1, -1, { WORKAROUND_FAKE, 0 } }, // Inside the last room, while Ad Avis performs the ritual to summon the genie - bug #5566 - { GID_QFG2, -1, 700, 0, NULL, "showSign", -1, 10, { WORKAROUND_FAKE, 0 } }, // Occurs sometimes when reading a sign in Raseir, Shapeir et al - bugs #5627, #5635 - { GID_QFG3, 510, 510, 0, "awardPrize", "changeState", -1, 0, { WORKAROUND_FAKE, 1 } }, // Simbani warrior challenge, after throwing the spears and retrieving the ring - bug #5277. Must be non-zero, otherwise the prize is awarded twice - bug #6160 - { GID_QFG3, 140, 140, 0, "rm140", "init", 0x1008, 0, { WORKAROUND_FAKE, 0 } }, // when importing a character and selecting the previous profession - bug #5163 - { GID_QFG3, 330, 330, -1, "Teller", "doChild", -1, -1, { WORKAROUND_FAKE, 0 } }, // when talking to King Rajah about "Rajah" (bug #5033, temp 1) or "Tarna" (temp 0), or when clicking on yourself and saying "Greet" (bug #5148, temp 1) - { GID_QFG3, 700, 700, -1, "monsterIsDead", "changeState", -1, 0, { WORKAROUND_FAKE, 0 } }, // in the jungle, after winning any fight, bug #5169 - { GID_QFG3, 470, 470, -1, "rm470", "notify", -1, 0, { WORKAROUND_FAKE, 0 } }, // closing the character screen in the Simbani village in the room with the bridge, bug #5165 - { GID_QFG3, 490, 490, -1, "computersMove", "changeState", -1, 0, { WORKAROUND_FAKE, 0 } }, // when finishing awari game, bug #5167 - { GID_QFG3, 490, 490, -1, "computersMove", "changeState", 0xf53, 4, { WORKAROUND_FAKE, 0 } }, // also when finishing awari game - { GID_QFG3, 851, 32, -1, "ProjObj", "doit", -1, 1, { WORKAROUND_FAKE, 0 } }, // near the end, when throwing the spear of death, bug #5282 - { GID_QFG4, -1, 15, -1, "charInitScreen", "dispatchEvent", -1, 5, { WORKAROUND_FAKE, 0 } }, // floppy version, when viewing the character screen - { GID_QFG4, -1, 64917, -1, "controlPlane", "setBitmap", -1, 3, { WORKAROUND_FAKE, 0 } }, // floppy version, when entering the game menu - { GID_QFG4, -1, 64917, -1, "Plane", "setBitmap", -1, 3, { WORKAROUND_FAKE, 0 } }, // floppy version, happens sometimes in fight scenes - { GID_QFG4, 520, 64950, 0, "fLake2", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // CD version, at the lake, when meeting the Rusalka and attempting to leave - { GID_QFG4, 800, 64950, 0, "View", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // CD version, in the room with the spider pillar, when climbing on the pillar - { GID_RAMA, 12, 64950, -1, "InterfaceFeature", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // Demo, right when it starts - { GID_RAMA, 12, 64950, -1, "hiliteOptText", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // Demo, right when it starts - { GID_RAMA, 12, 64950, -1, "View", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // Demo, right when it starts - { GID_SHIVERS, -1, 952, 0, "SoundManager", "stop", -1, 2, { WORKAROUND_FAKE, 0 } }, // Just after Sierra logo - { GID_SHIVERS, -1, 64950, 0, "Feature", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // When clicking on the locked door at the beginning - { GID_SHIVERS, -1, 64950, 0, "View", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // When clicking on the gargoyle eye at the beginning - { GID_SHIVERS, 20311, 64964, 0, "DPath", "init", -1, 1, { WORKAROUND_FAKE, 0 } }, // Just after door puzzle is solved and the metal balls start to roll - { GID_SHIVERS, 29260, 29260, 0, "spMars", "handleEvent", -1, 4, { WORKAROUND_FAKE, 0 } }, // When clicking mars after seeing fortune to align earth etc... - { GID_SHIVERS, 29260, 29260, 0, "spVenus", "handleEvent", -1, 4, { WORKAROUND_FAKE, 0 } }, // When clicking venus after seeing fortune to align earth etc... - { GID_SQ1, 103, 103, 0, "hand", "internalEvent", -1, -1, { WORKAROUND_FAKE, 0 } }, // Spanish (and maybe early versions?) only: when moving cursor over input pad, temps 1 and 2 - { GID_SQ1, -1, 703, 0, "", "export 1", -1, 0, { WORKAROUND_FAKE, 0 } }, // sub that's called from several objects while on sarien battle cruiser - { 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 #5112 - { 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 - { GID_SQ6, -1, 64964, 0, "DPath", "init", -1, 1, { WORKAROUND_FAKE, 0 } }, // during the game - { GID_TORIN, -1, 64017, 0, "oFlags", "clear", -1, 0, { WORKAROUND_FAKE, 0 } }, // entering Torin's home in the French version - SCI_WORKAROUNDENTRY_TERMINATOR -}; - -// gameID, room,script,lvl, object-name, method-name, call,index, workaround + { GID_KQ5, 25, 25, 0, "rm025", "doit", NULL, 0, { WORKAROUND_FAKE, 0 } }, // inside witch forest, when going to the room where the walking rock is + { GID_KQ5, 55, 55, 0, "helpScript", "doit", NULL, 0, { WORKAROUND_FAKE, 0 } }, // when giving the tambourine to the monster in the labyrinth (only happens at one of the locations) - bug #5198 + { GID_KQ5, -1, 755, 0, "gcWin", "open", NULL, -1, { WORKAROUND_FAKE, 0 } }, // when entering control menu in the FM-Towns version + { GID_KQ6, -1, 30, 0, "rats", "changeState", NULL, -1, { WORKAROUND_FAKE, 0 } }, // rats in the catacombs (temps 1 - 5) - bugs #4958, #4998, #5017 + { GID_KQ6, 210, 210, 0, "rm210", "scriptCheck", NULL, 0, { WORKAROUND_FAKE, 1 } }, // using inventory in that room - bug #4953 + { GID_KQ6, 500, 500, 0, "rm500", "init", NULL, 0, { WORKAROUND_FAKE, 0 } }, // going to island of the beast + { GID_KQ6, 520, 520, 0, "rm520", "init", NULL, 0, { WORKAROUND_FAKE, 0 } }, // going to boiling water trap on beast isle + { GID_KQ6, -1, 903, 0, "controlWin", "open", NULL, 4, { WORKAROUND_FAKE, 0 } }, // when opening the controls window (save, load etc) + { GID_KQ6, -1, 907, 0, "tomato", "doVerb", NULL, 2, { WORKAROUND_FAKE, 0 } }, // when looking at the rotten tomato in the inventory - bug #5331 + { GID_KQ6, -1, 928, 0, NULL, "startText", NULL, 0, { WORKAROUND_FAKE, 0 } }, // gets caused by Text+Audio support (see script patcher) + { GID_KQ7, -1, 64996, 0, "User", "handleEvent", NULL, 1, { WORKAROUND_FAKE, 0 } }, // called when pushing a keyboard key + { GID_LAURABOW, 37, 0, 0, "CB1", "doit", NULL, 1, { WORKAROUND_FAKE, 0 } }, // when going up the stairs - bug #5084 + { GID_LAURABOW, -1, 967, 0, "myIcon", "cycle", NULL, 1, { WORKAROUND_FAKE, 0 } }, // having any portrait conversation coming up - initial bug #4971 + { GID_LAURABOW2, -1, 24, 0, "gcWin", "open", NULL, 5, { WORKAROUND_FAKE, 0xf } }, // is used as priority for game menu + { GID_LAURABOW2, -1, 21, 0, "dropCluesCode", "doit", NULL, 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 #4979, #5026 + { GID_LAURABOW2, -1, 90, 1, "MuseumActor", "init", NULL, 6, { WORKAROUND_FAKE, 0 } }, // Random actors in museum - bug #5197 + { GID_LAURABOW2, 240, 240, 0, "sSteveAnimates", "changeState", NULL, 0, { WORKAROUND_FAKE, 0 } }, // Steve Dorian's idle animation at the docks - bug #5028 + { GID_LAURABOW2, -1, 928, 0, NULL, "startText", NULL, 0, { WORKAROUND_FAKE, 0 } }, // gets caused by Text+Audio support (see script patcher) + { GID_LONGBOW, -1, 0, 0, "Longbow", "restart", NULL, 0, { WORKAROUND_FAKE, 0 } }, // When canceling a restart game - bug #5244 + { GID_LONGBOW, -1, 213, 0, "clear", "handleEvent", NULL, 0, { WORKAROUND_FAKE, 0 } }, // When giving an answer using the druid hand sign code in any room + { GID_LONGBOW, -1, 213, 0, "letter", "handleEvent", sig_uninitread_longbow_1, 1, { WORKAROUND_FAKE, 0 } }, // When using the druid hand sign code in any room - bug #5035 + { GID_LSL1, 250, 250, 0, "increase", "handleEvent", NULL, 2, { WORKAROUND_FAKE, 0 } }, // casino, playing game, increasing bet + { GID_LSL1, 720, 720, 0, "rm720", "init", NULL, 0, { WORKAROUND_FAKE, 0 } }, // age check room + { GID_LSL2, 38, 38, 0, "cloudScript", "changeState", NULL, 1, { WORKAROUND_FAKE, 0 } }, // entering the room in the middle deck of the ship - bug #5034 + { GID_LSL3, 340, 340, 0, "ComicScript", "changeState", NULL, -1, { WORKAROUND_FAKE, 0 } }, // right after entering the 3 ethnic groups inside comedy club (temps 200, 201, 202, 203) + { GID_LSL3, -1, 997, 0, "TheMenuBar", "handleEvent", NULL, 1, { WORKAROUND_FAKE, 0xf } }, // when setting volume the first time, this temp is used to set volume on entry (normally it would have been initialized to 's') + { GID_LSL6, 820, 82, 0, "", "export 0", NULL, -1, { WORKAROUND_FAKE, 0 } }, // when touching the electric fence - bug #5103 + { GID_LSL6, -1, 85, 0, "washcloth", "doVerb", NULL, 0, { WORKAROUND_FAKE, 0 } }, // washcloth in inventory + { GID_LSL6, -1, 928, -1, "Narrator", "startText", NULL, 0, { WORKAROUND_FAKE, 0 } }, // used by various objects that are even translated in foreign versions, that's why we use the base-class + { GID_LSL6HIRES, 0, 85, 0, "LL6Inv", "init", NULL, 0, { WORKAROUND_FAKE, 0 } }, // on startup + { GID_LSL6HIRES, -1, 64950, 1, "Feature", "handleEvent", NULL, 0, { WORKAROUND_FAKE, 0 } }, // at least when entering swimming pool area + { GID_LSL6HIRES, -1, 64964, 0, "DPath", "init", NULL, 1, { WORKAROUND_FAKE, 0 } }, // during the game + { GID_MOTHERGOOSE256, -1, 0, 0, "MG", "doit", NULL, 5, { WORKAROUND_FAKE, 0 } }, // SCI1.1: When moving the cursor all the way to the left during the game - bug #5224 + { GID_MOTHERGOOSE256, -1, 992, 0, "AIPath", "init", NULL, 0, { WORKAROUND_FAKE, 0 } }, // Happens in the demo and full version. In the demo, it happens when walking two screens from mother goose's house to the north. In the full version, it happens in rooms 7 and 23 - bug #5269 + { GID_MOTHERGOOSE256, 90, 90, 0, "introScript", "changeState", NULL, 65, { WORKAROUND_FAKE, 0 } }, // SCI1(CD): At the very end, after the game is completed and restarted - bug #5626 + { GID_MOTHERGOOSE256, 94, 94, 0, "sunrise", "changeState", NULL, 367, { WORKAROUND_FAKE, 0 } }, // At the very end, after the game is completed - bug #5294 + { GID_MOTHERGOOSEHIRES,-1,64950, 1, "Feature", "handleEvent", NULL, 0, { WORKAROUND_FAKE, 0 } }, // right when clicking on a child at the start and probably also later + { GID_MOTHERGOOSEHIRES,-1,64950, 1, "View", "handleEvent", NULL, 0, { WORKAROUND_FAKE, 0 } }, // see above + { GID_PEPPER, -1, 894, 0, "Package", "doVerb", NULL, 3, { WORKAROUND_FAKE, 0 } }, // using the hand on the book in the inventory - bug #5154 + { GID_PEPPER, 150, 928, 0, "Narrator", "startText", NULL, 0, { WORKAROUND_FAKE, 0 } }, // happens during the non-interactive demo of Pepper + { GID_PQ4, -1, 25, 0, "iconToggle", "select", NULL, 1, { WORKAROUND_FAKE, 0 } }, // when toggling the icon bar to auto-hide or not + { GID_PQSWAT, -1, 64950, 0, "View", "handleEvent", NULL, 0, { WORKAROUND_FAKE, 0 } }, // Using the menu in the beginning + { GID_QFG1, -1, 210, 0, "Encounter", "init", sig_uninitread_qfg1_1, 0, { WORKAROUND_FAKE, 0 } }, // qfg1/hq1: going to the brigands hideout + { GID_QFG1VGA, 16, 16, 0, "lassoFailed", "changeState", NULL, -1, { WORKAROUND_FAKE, 0 } }, // qfg1vga: casting the "fetch" spell in the screen with the flowers, temps 0 and 1 - bug #5309 + { GID_QFG1VGA, -1, 210, 0, "Encounter", "init", sig_uninitread_qfg1vga_1, 0, { WORKAROUND_FAKE, 0 } }, // qfg1vga: going to the brigands hideout - bug #5515 + { GID_QFG2, -1, 71, 0, "theInvSheet", "doit", NULL, 1, { WORKAROUND_FAKE, 0 } }, // accessing the inventory + { GID_QFG2, -1, 79, 0, "TryToMoveTo", "onTarget", NULL, 0, { WORKAROUND_FAKE, 0 } }, // when throwing pot at air elemental, happens when client coordinates are the same as airElemental coordinates. happened to me right after room change - bug #6859 + { GID_QFG2, -1, 701, -1, "Alley", "at", NULL, 0, { WORKAROUND_FAKE, 0 } }, // when walking inside the alleys in the town - bug #5019 & #5106 + { GID_QFG2, -1, 990, 0, "Restore", "doit", NULL, 364, { WORKAROUND_FAKE, 0 } }, // when pressing enter in restore dialog w/o any saved games present + { GID_QFG2, 260, 260, 0, "abdulS", "changeState", sig_uninitread_qfg2_1, -1, { WORKAROUND_FAKE, 0 } }, // During the thief's first mission (in the house), just before Abdul is about to enter the house (where you have to hide in the wardrobe), bug #5153, temps 1 and 2 + { GID_QFG2, 260, 260, 0, "jabbarS", "changeState", sig_uninitread_qfg2_1, -1, { WORKAROUND_FAKE, 0 } }, // During the thief's first mission (in the house), just before Jabbar is about to enter the house (where you have to hide in the wardrobe), bug #5164, temps 1 and 2 + { GID_QFG2, 500, 500, 0, "lightNextCandleS", "changeState", NULL, -1, { WORKAROUND_FAKE, 0 } }, // Inside the last room, while Ad Avis performs the ritual to summon the genie - bug #5566 + { GID_QFG2, -1, 700, 0, NULL, "showSign", NULL, 10, { WORKAROUND_FAKE, 0 } }, // Occurs sometimes when reading a sign in Raseir, Shapeir et al - bugs #5627, #5635 + { GID_QFG3, 510, 510, 0, "awardPrize", "changeState", NULL, 0, { WORKAROUND_FAKE, 1 } }, // Simbani warrior challenge, after throwing the spears and retrieving the ring - bug #5277. Must be non-zero, otherwise the prize is awarded twice - bug #6160 + { GID_QFG3, 140, 140, 0, "rm140", "init", sig_uninitread_qfg3_1, 0, { WORKAROUND_FAKE, 0 } }, // when importing a character and selecting the previous profession - bug #5163 + { GID_QFG3, 330, 330, -1, "Teller", "doChild", NULL, -1, { WORKAROUND_FAKE, 0 } }, // when talking to King Rajah about "Rajah" (bug #5033, temp 1) or "Tarna" (temp 0), or when clicking on yourself and saying "Greet" (bug #5148, temp 1) + { GID_QFG3, 700, 700, -1, "monsterIsDead", "changeState", NULL, 0, { WORKAROUND_FAKE, 0 } }, // in the jungle, after winning any fight, bug #5169 + { GID_QFG3, 470, 470, -1, "rm470", "notify", NULL, 0, { WORKAROUND_FAKE, 0 } }, // closing the character screen in the Simbani village in the room with the bridge, bug #5165 + { GID_QFG3, 490, 490, -1, "computersMove", "changeState", NULL, 0, { WORKAROUND_FAKE, 0 } }, // when finishing awari game, bug #5167 + { GID_QFG3, 490, 490, -1, "computersMove", "changeState", sig_uninitread_qfg3_2, 4, { WORKAROUND_FAKE, 0 } }, // also when finishing awari game + { GID_QFG3, 851, 32, -1, "ProjObj", "doit", NULL, 1, { WORKAROUND_FAKE, 0 } }, // near the end, when throwing the spear of death, bug #5282 + { GID_QFG4, -1, 15, -1, "charInitScreen", "dispatchEvent", NULL, 5, { WORKAROUND_FAKE, 0 } }, // floppy version, when viewing the character screen + { GID_QFG4, -1, 64917, -1, "controlPlane", "setBitmap", NULL, 3, { WORKAROUND_FAKE, 0 } }, // floppy version, when entering the game menu + { GID_QFG4, -1, 64917, -1, "Plane", "setBitmap", NULL, 3, { WORKAROUND_FAKE, 0 } }, // floppy version, happens sometimes in fight scenes + { GID_QFG4, 520, 64950, 0, "fLake2", "handleEvent", NULL, 0, { WORKAROUND_FAKE, 0 } }, // CD version, at the lake, when meeting the Rusalka and attempting to leave + { GID_QFG4, 800, 64950, 0, "View", "handleEvent", NULL, 0, { WORKAROUND_FAKE, 0 } }, // CD version, in the room with the spider pillar, when climbing on the pillar + { GID_RAMA, 12, 64950, -1, "InterfaceFeature", "handleEvent", NULL, 0, { WORKAROUND_FAKE, 0 } }, // Demo, right when it starts + { GID_RAMA, 12, 64950, -1, "hiliteOptText", "handleEvent", NULL, 0, { WORKAROUND_FAKE, 0 } }, // Demo, right when it starts + { GID_RAMA, 12, 64950, -1, "View", "handleEvent", NULL, 0, { WORKAROUND_FAKE, 0 } }, // Demo, right when it starts + { GID_SHIVERS, -1, 952, 0, "SoundManager", "stop", NULL, 2, { WORKAROUND_FAKE, 0 } }, // Just after Sierra logo + { GID_SHIVERS, -1, 64950, 0, "Feature", "handleEvent", NULL, 0, { WORKAROUND_FAKE, 0 } }, // When clicking on the locked door at the beginning + { GID_SHIVERS, -1, 64950, 0, "View", "handleEvent", NULL, 0, { WORKAROUND_FAKE, 0 } }, // When clicking on the gargoyle eye at the beginning + { GID_SHIVERS, 20311, 64964, 0, "DPath", "init", NULL, 1, { WORKAROUND_FAKE, 0 } }, // Just after door puzzle is solved and the metal balls start to roll + { GID_SHIVERS, 29260, 29260, 0, "spMars", "handleEvent", NULL, 4, { WORKAROUND_FAKE, 0 } }, // When clicking mars after seeing fortune to align earth etc... + { GID_SHIVERS, 29260, 29260, 0, "spVenus", "handleEvent", NULL, 4, { WORKAROUND_FAKE, 0 } }, // When clicking venus after seeing fortune to align earth etc... + { GID_SQ1, 103, 103, 0, "hand", "internalEvent", NULL, -1, { WORKAROUND_FAKE, 0 } }, // Spanish (and maybe early versions?) only: when moving cursor over input pad, temps 1 and 2 + { GID_SQ1, -1, 703, 0, "", "export 1", NULL, 0, { WORKAROUND_FAKE, 0 } }, // sub that's called from several objects while on sarien battle cruiser + { GID_SQ1, -1, 703, 0, "firePulsar", "changeState", sig_uninitread_sq1_1, 0, { WORKAROUND_FAKE, 0 } }, // export 1, but called locally (when shooting at aliens) + { GID_SQ4, -1, 398, 0, "showBox", "changeState", NULL, 0, { WORKAROUND_FAKE, 0 } }, // CD: called when rummaging in Software Excess bargain bin + { GID_SQ4, -1, 928, -1, "Narrator", "startText", NULL, 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", NULL, 0, { WORKAROUND_FAKE, 0 } }, // Floppy: happens, when looking at the "close" button in the sq4 hintbook - bug #6447 + { GID_SQ4, -1, 708, -1, "", "doVerb", NULL, 0, { WORKAROUND_FAKE, 0 } }, // Floppy: happens, when looking at the "close" button... in Russian version - bug #5573 + { GID_SQ4, -1, 708, -1, "prevBut", "doVerb", NULL, 0, { WORKAROUND_FAKE, 0 } }, // Floppy: happens, when looking at the "previous" button in the sq4 hintbook - bug #6447 + { GID_SQ4, -1, 708, -1, "\xA8\xE6\xE3 \xAD\xA0\xA7\xA0\xA4.", "doVerb", NULL, 0, { WORKAROUND_FAKE, 0 } }, // Floppy: happens, when looking at the "previous" button... in Russian version - bug #5573 + { GID_SQ4, -1, 708, -1, "nextBut", "doVerb", NULL, 0, { WORKAROUND_FAKE, 0 } }, // Floppy: happens, when looking at the "next" button in the sq4 hintbook - bug #6447 + { GID_SQ4, -1, 708, -1, ".", "doVerb", NULL, 0, { WORKAROUND_FAKE, 0 } }, // Floppy: happens, when looking at the "next" button... in Russian version - bug #5573 + { GID_SQ5, 201, 201, 0, "buttonPanel", "doVerb", NULL, 0, { WORKAROUND_FAKE, 1 } }, // when looking at the orange or red button - bug #5112 + { GID_SQ6, -1, 0, 0, "SQ6", "init", NULL, 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", NULL, 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 + { GID_SQ6, -1, 64964, 0, "DPath", "init", NULL, 1, { WORKAROUND_FAKE, 0 } }, // during the game + { GID_TORIN, -1, 64017, 0, "oFlags", "clear", NULL, 0, { WORKAROUND_FAKE, 0 } }, // entering Torin's home in the French version + SCI_WORKAROUNDENTRY_TERMINATOR +}; + +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround const SciWorkaroundEntry kAbs_workarounds[] = { - { GID_HOYLE1, 1, 1, 0, "room1", "doit", -1, 0, { WORKAROUND_FAKE, 0x3e9 } }, // crazy eights - called with objects instead of integers - { GID_HOYLE1, 2, 2, 0, "room2", "doit", -1, 0, { WORKAROUND_FAKE, 0x3e9 } }, // old maid - called with objects instead of integers - { GID_HOYLE1, 3, 3, 0, "room3", "doit", -1, 0, { WORKAROUND_FAKE, 0x3e9 } }, // hearts - called with objects instead of integers - { GID_QFG1VGA, -1, -1, 0, NULL, "doit", -1, 0, { WORKAROUND_FAKE, 0x3e9 } }, // when the game is patched with the NRS patch - { GID_QFG3 , -1, -1, 0, NULL, "doit", -1, 0, { WORKAROUND_FAKE, 0x3e9 } }, // when the game is patched with the NRS patch - bugs #6042, #6043 + { GID_HOYLE1, 1, 1, 0, "room1", "doit", NULL, 0, { WORKAROUND_FAKE, 0x3e9 } }, // crazy eights - called with objects instead of integers + { GID_HOYLE1, 2, 2, 0, "room2", "doit", NULL, 0, { WORKAROUND_FAKE, 0x3e9 } }, // old maid - called with objects instead of integers + { GID_HOYLE1, 3, 3, 0, "room3", "doit", NULL, 0, { WORKAROUND_FAKE, 0x3e9 } }, // hearts - called with objects instead of integers + { GID_QFG1VGA, -1, -1, 0, NULL, "doit", NULL, 0, { WORKAROUND_FAKE, 0x3e9 } }, // when the game is patched with the NRS patch + { GID_QFG3 , -1, -1, 0, NULL, "doit", NULL, 0, { WORKAROUND_FAKE, 0x3e9 } }, // when the game is patched with the NRS patch - bugs #6042, #6043 SCI_WORKAROUNDENTRY_TERMINATOR }; -// gameID, room,script,lvl, object-name, method-name, call,index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround const SciWorkaroundEntry kCelHigh_workarounds[] = { - { GID_KQ5, -1, 255, 0, "deathIcon", "setSize", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // english floppy: when getting beaten up in the inn and probably more, called with 2nd parameter as object - bug #5049 - { GID_PQ2, -1, 255, 0, "DIcon", "setSize", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // when showing picture within windows, called with 2nd/3rd parameters as objects - { GID_SQ1, 1, 255, 0, "DIcon", "setSize", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // DEMO: Called with 2nd/3rd parameters as objects when clicking on the menu - bug #5012 - { GID_FANMADE, -1, 979, 0, "DIcon", "setSize", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // In The Gem Scenario and perhaps other fanmade games, this is called with 2nd/3rd parameters as objects - bug #5144 + { GID_KQ5, -1, 255, 0, "deathIcon", "setSize", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // english floppy: when getting beaten up in the inn and probably more, called with 2nd parameter as object - bug #5049 + { GID_PQ2, -1, 255, 0, "DIcon", "setSize", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // when showing picture within windows, called with 2nd/3rd parameters as objects + { GID_SQ1, 1, 255, 0, "DIcon", "setSize", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // DEMO: Called with 2nd/3rd parameters as objects when clicking on the menu - bug #5012 + { GID_FANMADE, -1, 979, 0, "DIcon", "setSize", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // In The Gem Scenario and perhaps other fanmade games, this is called with 2nd/3rd parameters as objects - bug #5144 SCI_WORKAROUNDENTRY_TERMINATOR }; -// gameID, room,script,lvl, object-name, method-name, call,index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround const SciWorkaroundEntry kCelWide_workarounds[] = { - { GID_KQ5, -1, 255, 0, "deathIcon", "setSize", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // english floppy: when getting beaten up in the inn and probably more, called with 2nd parameter as object - bug #5049 - { GID_PQ2, -1, 255, 0, "DIcon", "setSize", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // when showing picture within windows, called with 2nd/3rd parameters as objects - { GID_SQ1, 1, 255, 0, "DIcon", "setSize", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // DEMO: Called with 2nd/3rd parameters as objects when clicking on the menu - bug #5012 - { GID_FANMADE, -1, 979, 0, "DIcon", "setSize", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // In The Gem Scenario and perhaps other fanmade games, this is called with 2nd/3rd parameters as objects - bug #5144 + { GID_KQ5, -1, 255, 0, "deathIcon", "setSize", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // english floppy: when getting beaten up in the inn and probably more, called with 2nd parameter as object - bug #5049 + { GID_PQ2, -1, 255, 0, "DIcon", "setSize", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // when showing picture within windows, called with 2nd/3rd parameters as objects + { GID_SQ1, 1, 255, 0, "DIcon", "setSize", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // DEMO: Called with 2nd/3rd parameters as objects when clicking on the menu - bug #5012 + { GID_FANMADE, -1, 979, 0, "DIcon", "setSize", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // In The Gem Scenario and perhaps other fanmade games, this is called with 2nd/3rd parameters as objects - bug #5144 SCI_WORKAROUNDENTRY_TERMINATOR }; -// gameID, room,script,lvl, object-name, method-name, call,index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround const SciWorkaroundEntry kDeleteKey_workarounds[] = { - { GID_HOYLE4, 300, 999, 0, "handleEventList", "delete", -1, 0, { WORKAROUND_IGNORE, 0 } }, // restarting hearts, while tray is shown - bug #6604 - { GID_HOYLE4, 500, 999, 0, "handleEventList", "delete", -1, 0, { WORKAROUND_IGNORE, 0 } }, // restarting cribbage, while tray is shown - bug #6604 - { GID_HOYLE4, 975, 999, 0, "handleEventList", "delete", -1, 0, { WORKAROUND_IGNORE, 0 } }, // going back to gamelist from hearts/cribbage, while tray is shown - bug #6604 - SCI_WORKAROUNDENTRY_TERMINATOR -}; - -// gameID, room,script,lvl, object-name, method-name, call,index, workaround + { GID_HOYLE4, 300, 999, 0, "handleEventList", "delete", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // restarting hearts, while tray is shown - bug #6604 + { GID_HOYLE4, 500, 999, 0, "handleEventList", "delete", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // restarting cribbage, while tray is shown - bug #6604 + { GID_HOYLE4, 975, 999, 0, "handleEventList", "delete", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // going back to gamelist from hearts/cribbage, while tray is shown - bug #6604 + SCI_WORKAROUNDENTRY_TERMINATOR +}; + +// Game: Fan-Made games (SCI Studio) +// Calling method: Game::save, Game::restore +// Subroutine offset: Al Pond 2: 0x0e5c (ldi 0001) +// Black Cauldron: 0x000a (ldi 01) +// Cascade Quest: 0x0d1c (ldi 0001) +// Demo Quest: 0x0e55 (ldi 0001) +// I want my C64 back: 0x0e57 (ldi 0001) +// Applies to at least: games listed above +static const uint16 sig_kDeviceInfo_Fanmade_1[] = { + 0x3f, 0x79, // link 79h + 0x34, SIG_UINT16(0x0001), // ldi 0001 + 0xa5, 0x00, // sat temp[0] + SIG_END +}; +static const uint16 sig_kDeviceInfo_Fanmade_2[] = { + 0x3f, 0x79, // link 79h + 0x35, 0x01, // ldi 01 + 0xa5, 0x00, // sat temp[0] + SIG_END +}; + +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround 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) - { GID_FANMADE, -1, 994, 1, "Game", "restore", 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, 1, "Game", "restore", 0xe5c, 0, { WORKAROUND_STILLCALL, 0 } }, // In fanmade games, this is called with one parameter for CurDevice (Most of them) - SCI_WORKAROUNDENTRY_TERMINATOR -}; - -// gameID, room,script,lvl, object-name, method-name, call,index, workaround + { GID_FANMADE, -1, 994, 1, "Game", "save", sig_kDeviceInfo_Fanmade_1, 0, { WORKAROUND_STILLCALL, 0 } }, // In fanmade games (SCI Studio), this is called with one parameter for CurDevice LDI 01 variant + { GID_FANMADE, -1, 994, 1, "Game", "save", sig_kDeviceInfo_Fanmade_2, 0, { WORKAROUND_STILLCALL, 0 } }, // In fanmade games (SCI Studio), this is called with one parameter for CurDevice LDI 0001 variant + { GID_FANMADE, -1, 994, 0, "Black", "save", sig_kDeviceInfo_Fanmade_1, 0, { WORKAROUND_IGNORE, 0 } }, // In fanmade games (SCI Studio), this is called with one parameter for CurDevice (Black Cauldron Remake) + { GID_FANMADE, -1, 994, 1, "Game", "restore", sig_kDeviceInfo_Fanmade_1, 0, { WORKAROUND_STILLCALL, 0 } }, // In fanmade games (SCI Studio), this is called with one parameter for CurDevice LDI 01 variant + { GID_FANMADE, -1, 994, 1, "Game", "restore", sig_kDeviceInfo_Fanmade_2, 0, { WORKAROUND_STILLCALL, 0 } }, // In fanmade games (SCI Studio), this is called with one parameter for CurDevice LDI 0001 variant + SCI_WORKAROUNDENTRY_TERMINATOR +}; + +// Game: Police Quest 2 +// Calling method: rm23Script::elements +// Subroutine offset: English 1.001.000: 0x04ae, English 1.002.011: 0x04ca, Japanese: 0x04eb (script 23) +// Applies to at least: English PC floppy, Japanese PC-9801 +static const uint16 sig_kDisplay_pq2_1[] = { + 0x35, 0x00, // ldi 00 + 0xa3, 0x09, // sal local[9] + 0x35, 0x01, // ldi 01 + 0xa3, 0x0a, // sal local[0Ah] + 0x38, SIG_ADDTOOFFSET(+2), // pushi selector[drawPic] TODO: implement selectors + 0x7a, // push2 + 0x39, 0x5a, // pushi 5Ah + 0x7a, // push2 + 0x81, 0x02, // lag global[2] + 0x4a, 0x08, // send 08 + SIG_END +}; + +// Game: Space Quest 4 +// Calling method: doCatalog::mode +// Subroutine offset: English PC CD: 0x0084 (script 391) +// Applies to at least: English PC CD +static const uint16 sig_kDisplay_sq4_1[] = { + 0x38, SIG_UINT16(0x0187), // pushi 0187h (drawPic) + 0x78, // push1 + 0x38, SIG_UINT16(0x0189), // pushi 0189h (reflectPosn) + 0x81, 0x02, // lag global[2] + 0x4a, 0x06, // send 06 + SIG_END +}; + +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround const SciWorkaroundEntry kDisplay_workarounds[] = { - { GID_ISLANDBRAIN, 300, 300, 0, "geneDude", "show", -1, 0, { WORKAROUND_IGNORE, 0 } }, // when looking at the gene explanation chart - a parameter is an object - { GID_PQ2, 23, 23, 0, "rm23Script", "elements", 0x4ae, 0, { WORKAROUND_IGNORE, 0 } }, // when looking at the 2nd page of pate's file - 0x75 as id - { GID_PQ2, 23, 23, 0, "rm23Script", "elements", 0x4c1, 0, { WORKAROUND_IGNORE, 0 } }, // when looking at the 2nd page of pate's file - 0x75 as id (another pq2 version, bug #5223) - { GID_QFG1, 11, 11, 0, "battle", "<noname90>", -1, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: When entering battle, 0x75 as id - { GID_SQ4, 397, 0, 0, "", "export 12", -1, 0, { WORKAROUND_IGNORE, 0 } }, // FLOPPY: when going into the computer store - bug #5227 - { GID_SQ4, 391, 391, 0, "doCatalog", "mode", 0x84, 0, { WORKAROUND_IGNORE, 0 } }, // CD: clicking on catalog in roboter sale - a parameter is an object - { GID_SQ4, 391, 391, 0, "choosePlug", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // CD: ordering connector in roboter sale - a parameter is an object + { GID_ISLANDBRAIN, 300, 300, 0, "geneDude", "show", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // when looking at the gene explanation chart - a parameter is an object + { GID_LONGBOW, 95, 95, 0, "countDown", "changeState", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: during title screen "Robin Hood! Your bow is needed" + { GID_LONGBOW, 220, 220, 0, "moveOn", "changeState", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: during second room "Outwit and outfight..." + { GID_LONGBOW, 210, 210, 0, "mama", "changeState", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: during third room "Fall under the spell..." + { GID_LONGBOW, 320, 320, 0, "flyin", "changeState", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: during fourth room "Conspiracies, love..." + { GID_PQ2, 23, 23, 0, "rm23Script", "elements", sig_kDisplay_pq2_1, 0, { WORKAROUND_IGNORE, 0 } }, // when looking at the 2nd page of pate's file - 0x75 as id - bug #5223 + { GID_QFG1, 11, 11, 0, "battle", "init", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: When entering battle, 0x75 as id + { GID_SQ4, 397, 0, 0, "", "export 12", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // FLOPPY: when going into the computer store - bug #5227 + { GID_SQ4, 391, 391, 0, "doCatalog", "mode", sig_kDisplay_sq4_1, 0, { WORKAROUND_IGNORE, 0 } }, // CD: clicking on catalog in roboter sale - a parameter is an object + { GID_SQ4, 391, 391, 0, "choosePlug", "changeState", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // CD: ordering connector in roboter sale - a parameter is an object SCI_WORKAROUNDENTRY_TERMINATOR }; -// gameID, room,script,lvl, object-name, method-name, call,index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround const SciWorkaroundEntry kDirLoop_workarounds[] = { - { GID_KQ4, 4, 992, 0, "Avoid", "doit", -1, 0, { WORKAROUND_IGNORE, 0 } }, // when the ogre catches you in front of his house, second parameter points to the same object as the first parameter, instead of being an integer (the angle) - bug #5217 + { GID_KQ4, 4, 992, 0, "Avoid", "doit", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // when the ogre catches you in front of his house, second parameter points to the same object as the first parameter, instead of being an integer (the angle) - bug #5217 SCI_WORKAROUNDENTRY_TERMINATOR }; -// gameID, room,script,lvl, object-name, method-name, call,index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround const SciWorkaroundEntry kDisposeScript_workarounds[] = { - { GID_LAURABOW, 777, 777, 0, "myStab", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: after the will is signed, parameter 0 is an object - bug #4967 - { GID_QFG1, -1, 64, 0, "rm64", "dispose", -1, 0, { WORKAROUND_IGNORE, 0 } }, // when leaving graveyard, parameter 0 is an object - { GID_SQ4, 150, 151, 0, "fightScript", "dispose", -1, 0, { WORKAROUND_IGNORE, 0 } }, // during fight with Vohaul, parameter 0 is an object - { GID_SQ4, 150, 152, 0, "driveCloseUp", "dispose", -1, 0, { WORKAROUND_IGNORE, 0 } }, // when choosing "beam download", parameter 0 is an object + { GID_LAURABOW, 777, 777, 0, "myStab", "changeState", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: after the will is signed, parameter 0 is an object - bug #4967 + { GID_LSL2, -1, 54, 0, "rm54", "dispose", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // Amiga: room 55, script tries to kDisposeScript an object (does not happen for DOS) - bug #6818 + { GID_QFG1, -1, 64, 0, "rm64", "dispose", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // when leaving graveyard, parameter 0 is an object + { GID_SQ4, 150, 151, 0, "fightScript", "dispose", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // during fight with Vohaul, parameter 0 is an object + { GID_SQ4, 150, 152, 0, "driveCloseUp", "dispose", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // when choosing "beam download", parameter 0 is an object + { GID_SQ4, 150, 152, 0, "", "dispose", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // when choosing "beam download"... in Russian version - bug #5573 SCI_WORKAROUNDENTRY_TERMINATOR }; -// gameID, room,script,lvl, object-name, method-name, call,index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround const SciWorkaroundEntry kDoSoundFade_workarounds[] = { - { GID_KQ5, 213, 989, 0, "globalSound3", "fade", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // english floppy: when bandits leave the secret temple, parameter 4 is an object - bug #5078 - { 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 #4954 - { 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 #6341 + { GID_KQ5, 213, 989, 0, "globalSound3", "fade", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // english floppy: when bandits leave the secret temple, parameter 4 is an object - bug #5078 + { GID_KQ6, 105, 989, 0, "globalSound", "fade", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // floppy: during intro, parameter 4 is an object + { GID_KQ6, 460, 989, 0, "globalSound2", "fade", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // after pulling the black widow's web on the isle of wonder, parameter 4 is an object - bug #4954 + { GID_QFG4, -1, 64989, 0, "longSong", "fade", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // CD version: many places, parameter 4 is an object (longSong) + { GID_SQ5, 800, 989, 0, "sq5Music1", "fade", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // when cutting the wrong part of Goliath with the laser - bug #6341 SCI_WORKAROUNDENTRY_TERMINATOR }; -// gameID, room,script,lvl, object-name, method-name, call,index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround const SciWorkaroundEntry kGetAngle_workarounds[] = { - { GID_FANMADE, 516, 992, 0, "Motion", "init", -1, 0, { WORKAROUND_FAKE, 0 } }, // The Legend of the Lost Jewel Demo (fan made): called with third/fourth parameters as objects - { GID_KQ6, -1, 752, 0, "throwDazzle", "changeState", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // room 740/790 after the Genie is exposed in the Palace (short and long ending), it starts shooting lightning bolts around. An extra 5th parameter is passed - bug #4959 & #5203 - { GID_SQ1, -1, 927, 0, "PAvoider", "doit", -1, 0, { WORKAROUND_FAKE, 0 } }, // all rooms in Ulence Flats after getting the Pilot Droid: called with a single parameter when the droid is in Roger's path - bug #6016 + { GID_FANMADE, 516, 992, 0, "Motion", "init", NULL, 0, { WORKAROUND_FAKE, 0 } }, // The Legend of the Lost Jewel Demo (fan made): called with third/fourth parameters as objects + { GID_KQ6, -1, 752, 0, "throwDazzle", "changeState", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // room 740/790 after the Genie is exposed in the Palace (short and long ending), it starts shooting lightning bolts around. An extra 5th parameter is passed - bug #4959 & #5203 + { GID_SQ1, -1, 927, 0, "PAvoider", "doit", NULL, 0, { WORKAROUND_FAKE, 0 } }, // all rooms in Ulence Flats after getting the Pilot Droid: called with a single parameter when the droid is in Roger's path - bug #6016 SCI_WORKAROUNDENTRY_TERMINATOR }; -// gameID, room,script,lvl, object-name, method-name, call,index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround const SciWorkaroundEntry kFindKey_workarounds[] = { - { GID_ECOQUEST2, 100, 999, 0, "myList", "contains", -1, 0, { WORKAROUND_FAKE, 0 } }, // When Noah Greene gives Adam the Ecorder, and just before the game gives a demonstration, a null reference to a list is passed - bug #4987 - { GID_HOYLE4, 300, 999, 0, "Piles", "contains", -1, 0, { WORKAROUND_FAKE, 0 } }, // When passing the three cards in Hearts, a null reference to a list is passed - bug #5664 + { GID_ECOQUEST2, 100, 999, 0, "myList", "contains", NULL, 0, { WORKAROUND_FAKE, 0 } }, // When Noah Greene gives Adam the Ecorder, and just before the game gives a demonstration, a null reference to a list is passed - bug #4987 + { GID_HOYLE4, 300, 999, 0, "Piles", "contains", NULL, 0, { WORKAROUND_FAKE, 0 } }, // When passing the three cards in Hearts, a null reference to a list is passed - bug #5664 SCI_WORKAROUNDENTRY_TERMINATOR }; -// gameID, room,script,lvl, object-name, method-name, call,index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround const SciWorkaroundEntry kGraphDrawLine_workarounds[] = { - { GID_ISLANDBRAIN, 300, 300, 0, "dudeViewer", "show", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // when looking at the gene explanation chart, gets called with 1 extra parameter - { GID_SQ1, 43, 43, 0, "someoneDied", "changeState", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // when ordering beer, gets called with 1 extra parameter - { GID_SQ1, 71, 71, 0, "destroyXenon", "changeState", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // during the Xenon destruction cutscene (which results in death), gets called with 1 extra parameter - bug #5176 - { GID_SQ1, 53, 53, 0, "blastEgo", "changeState", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // when Roger is found and zapped by the cleaning robot, gets called with 1 extra parameter - bug #5177 + { GID_ISLANDBRAIN, 300, 300, 0, "dudeViewer", "show", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // when looking at the gene explanation chart, gets called with 1 extra parameter + { GID_SQ1, 43, 43, 0, "someoneDied", "changeState", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // when ordering beer, gets called with 1 extra parameter + { GID_SQ1, 71, 71, 0, "destroyXenon", "changeState", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // during the Xenon destruction cutscene (which results in death), gets called with 1 extra parameter - bug #5176 + { GID_SQ1, 53, 53, 0, "blastEgo", "changeState", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // when Roger is found and zapped by the cleaning robot, gets called with 1 extra parameter - bug #5177 SCI_WORKAROUNDENTRY_TERMINATOR }; -// gameID, room,script,lvl, object-name, method-name, call,index, workaround +// Game: Island of Dr. Brain +// Calling method: upElevator::changeState, downElevator::changeState, correctElevator::changeState +// Subroutine offset: 0x201f (script 291) +// Applies to at least: English PC floppy +static const uint16 sig_kGraphSaveBox_ibrain_1[] = { + 0x3f, 0x01, // link 01 + 0x87, 0x01, // lap param[1] + 0x30, SIG_UINT16(0x0043), // bnt [...] + 0x76, // push0 + SIG_END +}; + +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround const SciWorkaroundEntry kGraphSaveBox_workarounds[] = { - { GID_CASTLEBRAIN, 420, 427, 0, "alienIcon", "select", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // when selecting a card during the alien card game, gets called with 1 extra parameter - { GID_ISLANDBRAIN, 290, 291, 0, "upElevator", "changeState",0x201f, 0, { WORKAROUND_STILLCALL, 0 } }, // when testing in the elevator puzzle, gets called with 1 argument less - 15 is on stack - bug #4943 - { GID_ISLANDBRAIN, 290, 291, 0, "downElevator", "changeState",0x201f, 0, { WORKAROUND_STILLCALL, 0 } }, // see above - { GID_ISLANDBRAIN, 290, 291, 0, "correctElevator", "changeState",0x201f, 0, { WORKAROUND_STILLCALL, 0 } }, // see above (when testing the correct solution) - { GID_PQ3, 202, 202, 0, "MapEdit", "movePt", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // when plotting crimes, gets called with 2 extra parameters - bug #5099 + { GID_CASTLEBRAIN, 420, 427, 0, "alienIcon", "select", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // when selecting a card during the alien card game, gets called with 1 extra parameter + { GID_ISLANDBRAIN, 290, 291, 0, "upElevator", "changeState", sig_kGraphSaveBox_ibrain_1, 0, { WORKAROUND_STILLCALL, 0 } }, // when testing in the elevator puzzle, gets called with 1 argument less - 15 is on stack - bug #4943 + { GID_ISLANDBRAIN, 290, 291, 0, "downElevator", "changeState", sig_kGraphSaveBox_ibrain_1, 0, { WORKAROUND_STILLCALL, 0 } }, // see above + { GID_ISLANDBRAIN, 290, 291, 0, "correctElevator", "changeState", sig_kGraphSaveBox_ibrain_1, 0, { WORKAROUND_STILLCALL, 0 } }, // see above (when testing the correct solution) + { GID_PQ3, 202, 202, 0, "MapEdit", "movePt", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // when plotting crimes, gets called with 2 extra parameters - bug #5099 SCI_WORKAROUNDENTRY_TERMINATOR }; -// gameID, room,script,lvl, object-name, method-name, call,index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround const SciWorkaroundEntry kGraphRestoreBox_workarounds[] = { - { GID_LSL6, -1, 86, 0, "LL6Inv", "hide", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // happens during the game, gets called with 1 extra parameter + { GID_LSL6, -1, 86, 0, "LL6Inv", "hide", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // happens during the game, gets called with 1 extra parameter SCI_WORKAROUNDENTRY_TERMINATOR }; -// gameID, room,script,lvl, object-name, method-name, call,index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround const SciWorkaroundEntry kGraphFillBoxForeground_workarounds[] = { - { GID_LSL6, -1, 0, 0, "LSL6", "hideControls", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // happens when giving the bungee key to merrily (room 240) and at least in room 650 too - gets called with additional 5th parameter + { GID_LSL6, -1, 0, 0, "LSL6", "hideControls", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // happens when giving the bungee key to merrily (room 240) and at least in room 650 too - gets called with additional 5th parameter SCI_WORKAROUNDENTRY_TERMINATOR }; -// gameID, room,script,lvl, object-name, method-name, call,index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround const SciWorkaroundEntry kGraphFillBoxAny_workarounds[] = { - { GID_ECOQUEST2, 100, 333, 0, "showEcorder", "changeState", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // necessary workaround for our ecorder script patch, because there isn't enough space to patch the function - { GID_SQ4, -1, 818, 0, "iconTextSwitch", "show", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // CD: game menu "text/speech" display - parameter 5 is missing, but the right color number is on the stack + { GID_ECOQUEST2, 100, 333, 0, "showEcorder", "changeState", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // necessary workaround for our ecorder script patch, because there isn't enough space to patch the function + { GID_SQ4, -1, 818, 0, "iconTextSwitch", "show", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // CD: game menu "text/speech" display - parameter 5 is missing, but the right color number is on the stack SCI_WORKAROUNDENTRY_TERMINATOR }; -// gameID, room,script,lvl, object-name, method-name, call,index, workaround +// Game: Space Quest 4 +// Calling method: laserScript::changeState +// Subroutine offset: English/German/French/Russian PC floppy, Japanese PC-9801: 0x0016, English PC CD: 0x00b2 (script 150) +// Applies to at least: English/German/French/Russian PC floppy, English PC CD, Japanese PC-9801 +static const uint16 sig_kGraphRedrawBox_sq4_1[] = { + 0x3f, 0x07, // link 07 + 0x39, SIG_ADDTOOFFSET(+1), // pushi 2Ah for PC floppy, pushi 27h for PC CD + 0x76, // push0 + 0x72, // lofsa laserSound + SIG_END +}; + +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround const SciWorkaroundEntry kGraphRedrawBox_workarounds[] = { - { GID_SQ4, 405, 405, 0, "swimAfterEgo", "changeState", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // skateOrama when "swimming" in the air - accidental additional parameter specified - { GID_SQ4, 406, 406, 0, "egoFollowed", "changeState", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // FLOPPY: when getting shot by the police - accidental additional parameter specified - { GID_SQ4, 406, 406, 0, "swimAndShoot", "changeState", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // skateOrama when "swimming" in the air - accidental additional parameter specified - { GID_SQ4, 410, 410, 0, "swimAfterEgo", "changeState", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // skateOrama when "swimming" in the air - accidental additional parameter specified - { GID_SQ4, 411, 411, 0, "swimAndShoot", "changeState", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // skateOrama when "swimming" in the air - accidental additional parameter specified - { GID_SQ4, 150, 150, 0, "laserScript", "changeState", 0xb2, 0, { WORKAROUND_STILLCALL, 0 } }, // when visiting the pedestral where Roger Jr. is trapped, before trashing the brain icon in the programming chapter, accidental additional parameter specified - bug #5479 - { GID_SQ4, 150, 150, 0, "laserScript", "changeState", 0x16, 0, { WORKAROUND_STILLCALL, 0 } }, // same as above, for the German version - bug #5527 - { GID_SQ4, -1, 704, 0, "shootEgo", "changeState", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // When shot by Droid in Super Computer Maze (Rooms 500, 505, 510...) - accidental additional parameter specified - { GID_KQ5, -1, 981, 0, "myWindow", "dispose", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // Happens in the floppy version, when closing any dialog box, accidental additional parameter specified - bug #5031 - { GID_KQ5, -1, 995, 0, "invW", "doit", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // Happens in the floppy version, when closing the inventory window, accidental additional parameter specified - { GID_KQ5, -1, 995, 0, "", "export 0", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // Happens in the floppy version, when opening the gem pouch, accidental additional parameter specified - bug #5138 - { GID_KQ5, -1, 403, 0, "KQ5Window", "dispose", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // Happens in the FM Towns version when closing any dialog box, accidental additional parameter specified - SCI_WORKAROUNDENTRY_TERMINATOR -}; - -// gameID, room,script,lvl, object-name, method-name, call,index, workaround + { GID_SQ4, 405, 405, 0, "swimAfterEgo", "changeState", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // skateOrama when "swimming" in the air - accidental additional parameter specified + { GID_SQ4, 405, 405, 0, "", "changeState", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // skateOrama when "swimming" in the air... Russian version - bug #5573 + { GID_SQ4, 406, 406, 0, "egoFollowed", "changeState", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // FLOPPY: when getting shot by the police - accidental additional parameter specified + { GID_SQ4, -1, 406, 0, "swimAndShoot", "changeState", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // skateOrama when "swimming" in the air - accidental additional parameter specified + { GID_SQ4, -1, 406, 0, "", "changeState", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // skateOrama when "swimming" in the air... Russian version - bug #5573 (is for both egoFollowed and swimAndShoot) + { GID_SQ4, 410, 410, 0, "swimAfterEgo", "changeState", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // skateOrama when "swimming" in the air - accidental additional parameter specified + { GID_SQ4, 410, 410, 0, "", "changeState", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // skateOrama when "swimming" in the air... Russian version - bug #5573 + { GID_SQ4, 411, 411, 0, "swimAndShoot", "changeState", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // skateOrama when "swimming" in the air - accidental additional parameter specified + { GID_SQ4, 411, 411, 0, "", "changeState", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // skateOrama when "swimming" in the air... Russian version - bug #5573 + { GID_SQ4, 150, 150, 0, "laserScript", "changeState", sig_kGraphRedrawBox_sq4_1, 0, { WORKAROUND_STILLCALL, 0 } }, // when visiting the pedestral where Roger Jr. is trapped, before trashing the brain icon in the programming chapter, accidental additional parameter specified - bug #5479, German - bug #5527 + { GID_SQ4, 150, 150, 0, "", "changeState", sig_kGraphRedrawBox_sq4_1, 0, { WORKAROUND_STILLCALL, 0 } }, // same as above, for the Russian version - bug #5573 + { GID_SQ4, -1, 704, 0, "shootEgo", "changeState", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // When shot by Droid in Super Computer Maze (Rooms 500, 505, 510...) - accidental additional parameter specified + { GID_KQ5, -1, 981, 0, "myWindow", "dispose", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // Happens in the floppy version, when closing any dialog box, accidental additional parameter specified - bug #5031 + { GID_KQ5, -1, 995, 0, "invW", "doit", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // Happens in the floppy version, when closing the inventory window, accidental additional parameter specified + { GID_KQ5, -1, 995, 0, "", "export 0", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // Happens in the floppy version, when opening the gem pouch, accidental additional parameter specified - bug #5138 + { GID_KQ5, -1, 403, 0, "KQ5Window", "dispose", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // Happens in the FM Towns version when closing any dialog box, accidental additional parameter specified + SCI_WORKAROUNDENTRY_TERMINATOR +}; + +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround const SciWorkaroundEntry kGraphUpdateBox_workarounds[] = { - { GID_ECOQUEST2, 100, 333, 0, "showEcorder", "changeState", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // necessary workaround for our ecorder script patch, because there isn't enough space to patch the function - { GID_PQ3, 202, 202, 0, "MapEdit", "addPt", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // when plotting crimes, gets called with 2 extra parameters - bug #5099 - { GID_PQ3, 202, 202, 0, "MapEdit", "movePt", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // when plotting crimes, gets called with 2 extra parameters - bug #5099 - { GID_PQ3, 202, 202, 0, "MapEdit", "dispose", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // when plotting crimes, gets called with 2 extra parameters + { GID_ECOQUEST2, 100, 333, 0, "showEcorder", "changeState", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // necessary workaround for our ecorder script patch, because there isn't enough space to patch the function + { GID_PQ3, 202, 202, 0, "MapEdit", "addPt", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // when plotting crimes, gets called with 2 extra parameters - bug #5099 + { GID_PQ3, 202, 202, 0, "MapEdit", "movePt", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // when plotting crimes, gets called with 2 extra parameters - bug #5099 + { GID_PQ3, 202, 202, 0, "MapEdit", "dispose", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // when plotting crimes, gets called with 2 extra parameters SCI_WORKAROUNDENTRY_TERMINATOR }; -// gameID, room,script,lvl, object-name, method-name, call,index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround const SciWorkaroundEntry kIsObject_workarounds[] = { - { GID_GK1, 50, 999, 0, "List", "eachElementDo", -1, 0, { WORKAROUND_FAKE, 0 } }, // GK1 demo, when asking Grace for messages it gets called with an invalid parameter (type "error") - bug #4950 - { GID_ISLANDBRAIN, -1, 999, 0, "List", "eachElementDo", -1, 0, { WORKAROUND_FAKE, 0 } }, // when going to the game options, choosing "Info" and selecting anything from the list, gets called with an invalid parameter (type "error") - bug #4989 - { GID_QFG3, -1, 999, 0, "List", "eachElementDo", -1, 0, { WORKAROUND_FAKE, 0 } }, // when asking for something, gets called with type error parameter + { GID_GK1, 50, 999, 0, "List", "eachElementDo", NULL, 0, { WORKAROUND_FAKE, 0 } }, // GK1 demo, when asking Grace for messages it gets called with an invalid parameter (type "error") - bug #4950 + { GID_ISLANDBRAIN, -1, 999, 0, "List", "eachElementDo", NULL, 0, { WORKAROUND_FAKE, 0 } }, // when going to the game options, choosing "Info" and selecting anything from the list, gets called with an invalid parameter (type "error") - bug #4989 + { GID_QFG3, -1, 999, 0, "List", "eachElementDo", NULL, 0, { WORKAROUND_FAKE, 0 } }, // when asking for something, gets called with type error parameter SCI_WORKAROUNDENTRY_TERMINATOR }; -// gameID, room,script,lvl, object-name, method-name, call,index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround const SciWorkaroundEntry kMemory_workarounds[] = { - { GID_LAURABOW2, -1, 999, 0, "", "export 6", -1, 0, { WORKAROUND_FAKE, 0 } }, // during the intro, when exiting the train (room 160), talking to Mr. Augustini, etc. - bug #4944 - { GID_SQ1, -1, 999, 0, "", "export 6", -1, 0, { WORKAROUND_FAKE, 0 } }, // during walking Roger around Ulence Flats - bug #6017 + { GID_LAURABOW2, -1, 999, 0, "", "export 6", NULL, 0, { WORKAROUND_FAKE, 0 } }, // during the intro, when exiting the train (room 160), talking to Mr. Augustini, etc. - bug #4944 + { GID_SQ1, -1, 999, 0, "", "export 6", NULL, 0, { WORKAROUND_FAKE, 0 } }, // during walking Roger around Ulence Flats - bug #6017 SCI_WORKAROUNDENTRY_TERMINATOR }; -// gameID, room,script,lvl, object-name, method-name, call,index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround const SciWorkaroundEntry kMoveCursor_workarounds[] = { - { GID_KQ5, -1, 937, 0, "IconBar", "handleEvent", -1, 0, { WORKAROUND_IGNORE, 0 } }, // when pressing escape to open the menu, gets called with one parameter instead of 2 - bug #5575 + { GID_KQ5, -1, 937, 0, "IconBar", "handleEvent", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // when pressing escape to open the menu, gets called with one parameter instead of 2 - bug #5575 SCI_WORKAROUNDENTRY_TERMINATOR }; -// gameID, room,script,lvl, object-name, method-name, call,index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround const SciWorkaroundEntry kNewWindow_workarounds[] = { - { GID_ECOQUEST, -1, 981, 0, "SysWindow", "open", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // EcoQuest 1 demo uses an in-between interpreter from SCI1 to SCI1.1. It's SCI1.1, but uses the SCI1 semantics for this call - bug #4976 + { GID_ECOQUEST, -1, 981, 0, "SysWindow", "open", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // EcoQuest 1 demo uses an in-between interpreter from SCI1 to SCI1.1. It's SCI1.1, but uses the SCI1 semantics for this call - bug #4976 SCI_WORKAROUNDENTRY_TERMINATOR }; -// gameID, room,script,lvl, object-name, method-name, call,index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround const SciWorkaroundEntry kReadNumber_workarounds[] = { - { GID_CNICK_LAURABOW,100, 101, 0, "dominoes.opt", "doit", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // When dominoes.opt is present, the game scripts call kReadNumber with an extra integer parameter - bug #6425 - { GID_HOYLE3, 100, 101, 0, "dominoes.opt", "doit", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // When dominoes.opt is present, the game scripts call kReadNumber with an extra integer parameter - bug #6425 + { GID_CNICK_LAURABOW,100, 101, 0, "dominoes.opt", "doit", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // When dominoes.opt is present, the game scripts call kReadNumber with an extra integer parameter - bug #6425 + { GID_HOYLE3, 100, 101, 0, "dominoes.opt", "doit", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // When dominoes.opt is present, the game scripts call kReadNumber with an extra integer parameter - bug #6425 SCI_WORKAROUNDENTRY_TERMINATOR }; -// gameID, room,script,lvl, object-name, method-name, call,index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, 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 #4947 + { GID_QFG4, 100, 100, 0, "doMovie", "changeState", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // after the Sierra logo, no flags are passed, thus the call is meaningless - bug #4947 SCI_WORKAROUNDENTRY_TERMINATOR }; -// gameID, room,script,lvl, object-name, method-name, call,index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround const SciWorkaroundEntry kSetCursor_workarounds[] = { - { GID_KQ5, -1, 768, 0, "KQCursor", "init", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // CD: gets called with 4 additional "900d" parameters + { GID_KQ5, -1, 768, 0, "KQCursor", "init", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // CD: gets called with 4 additional "900d" parameters SCI_WORKAROUNDENTRY_TERMINATOR }; -// gameID, room,script,lvl, object-name, method-name, call,index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround const SciWorkaroundEntry kSetPort_workarounds[] = { - { GID_LSL6, 740, 740, 0, "rm740", "drawPic", -1, 0, { WORKAROUND_IGNORE, 0 } }, // ending scene, is called with additional 3 (!) parameters - { GID_QFG3, 830, 830, 0, "portalOpens", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // when the portal appears during the end, gets called with 4 parameters - bug #5174 + { GID_LSL6, 740, 740, 0, "rm740", "drawPic", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // ending scene, is called with additional 3 (!) parameters + { GID_QFG3, 830, 830, 0, "portalOpens", "changeState", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // when the portal appears during the end, gets called with 4 parameters - bug #5174 SCI_WORKAROUNDENTRY_TERMINATOR }; -// gameID, room,script,lvl, object-name, method-name, call,index, workaround +// Game: Island of Dr. Brain +// Calling method: childBreed::changeState +// Subroutine offset: 0x1c7c (script 310) +// Applies to at least: English PC floppy +static const uint16 sig_kStrAt_ibrain_1[] = { + 0x3f, 0x16, // link 16 + 0x78, // push1 + 0x8f, 0x01, // lsp param[1] + 0x43, // callk StrLen + SIG_END +}; + +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround const SciWorkaroundEntry kStrAt_workarounds[] = { - { GID_CASTLEBRAIN, 220, 220, 0, "robotJokes", "animateOnce", -1, 0, { WORKAROUND_FAKE, 0 } }, // when trying to view the terminal at the end of the maze without having collected any robot jokes - bug #5127 - { GID_ISLANDBRAIN, 300, 310, 0, "childBreed", "changeState",0x1c7c, 0, { WORKAROUND_FAKE, 0 } }, // when clicking Breed to get the second-generation cyborg hybrid (Standard difficulty), the two parameters are swapped - bug #5088 + { GID_CASTLEBRAIN, 220, 220, 0, "robotJokes", "animateOnce", NULL, 0, { WORKAROUND_FAKE, 0 } }, // when trying to view the terminal at the end of the maze without having collected any robot jokes - bug #5127 + { GID_ISLANDBRAIN, 300, 310, 0, "childBreed", "changeState", sig_kStrAt_ibrain_1, 0, { WORKAROUND_FAKE, 0 } }, // when clicking Breed to get the second-generation cyborg hybrid (Standard difficulty), the two parameters are swapped - bug #5088 SCI_WORKAROUNDENTRY_TERMINATOR }; +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround const SciWorkaroundEntry kStrCpy_workarounds[] = { - { GID_MOTHERGOOSE, 23, 23, 0, "talkScript", "changeState", -1, 0, { WORKAROUND_FAKE, 0 } }, // when talking to the girl in scene 23, there's no destination parameter (script bug - wrong instruction order). The original source is used directly afterwards in kDisplay, to show the girl's text - bug #6485 + { GID_MOTHERGOOSE, 23, 23, 0, "talkScript", "changeState", NULL, 0, { WORKAROUND_FAKE, 0 } }, // when talking to the girl in scene 23, there's no destination parameter (script bug - wrong instruction order). The original source is used directly afterwards in kDisplay, to show the girl's text - bug #6485 SCI_WORKAROUNDENTRY_TERMINATOR }; -// gameID, room,script,lvl, object-name, method-name, call,index, workaround +// Game: Quest for Glory 2 +// Calling method: export 21 of script 2 +// Subroutine offset: English 0x0deb (script 2) +// Applies to at least: English PC floppy +static const uint16 sig_kStrLen_qfg2_1[] = { + 0x3f, 0x04, // link 04 + 0x78, // push1 + 0x8f, 0x02, // lsp param[2] + 0x43, // callk StrLen + SIG_END +}; + +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround const SciWorkaroundEntry kStrLen_workarounds[] = { - { GID_QFG2, 210, 2, 0, "", "export 21", 0xdeb, 0, { WORKAROUND_FAKE, 0 } }, // When saying something incorrect at the WIT, an integer is passed instead of a reference - bug #5489 + { GID_QFG2, 210, 2, 0, "", "export 21", sig_kStrLen_qfg2_1, 0, { WORKAROUND_FAKE, 0 } }, // When saying something incorrect at the WIT, an integer is passed instead of a reference - bug #5489 SCI_WORKAROUNDENTRY_TERMINATOR }; -// gameID, room,script,lvl, object-name, method-name, call,index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround const SciWorkaroundEntry kUnLoad_workarounds[] = { - { GID_ECOQUEST, 380, 61, 0, "gotIt", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // CD version: after talking to the dolphin the first time, a 3rd parameter is passed by accident - { GID_ECOQUEST, 380, 69, 0, "lookAtBlackBoard", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // German version, when closing the blackboard closeup in the dolphin room, a 3rd parameter is passed by accident - bug #5483 - { GID_LAURABOW2, -1, -1, 0, "sCartoon", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: during the intro, a 3rd parameter is passed by accident - bug #4966 - { GID_LSL6, 130, 130, 0, "recruitLarryScr", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // during intro, a 3rd parameter is passed by accident - { GID_LSL6, 740, 740, 0, "showCartoon", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // during ending, 4 additional parameters are passed by accident - { GID_LSL6HIRES, 130, 130, 0, "recruitLarryScr", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // during intro, a 3rd parameter is passed by accident - { GID_SQ1, 43, 303, 0, "slotGuy", "dispose", -1, 0, { WORKAROUND_IGNORE, 0 } }, // when leaving ulence flats bar, parameter 1 is not passed - script error - { GID_QFG4, -1, 110, 0, "dreamer", "dispose", -1, 0, { WORKAROUND_IGNORE, 0 } }, // during the dream sequence, a 3rd parameter is passed by accident + { GID_ECOQUEST, 380, 61, 0, "gotIt", "changeState", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // CD version: after talking to the dolphin the first time, a 3rd parameter is passed by accident + { GID_ECOQUEST, 380, 69, 0, "lookAtBlackBoard", "changeState", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // German version, when closing the blackboard closeup in the dolphin room, a 3rd parameter is passed by accident - bug #5483 + { GID_LAURABOW2, -1, -1, 0, "sCartoon", "changeState", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: during the intro, a 3rd parameter is passed by accident - bug #4966 + { GID_LSL6, 130, 130, 0, "recruitLarryScr", "changeState", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // during intro, a 3rd parameter is passed by accident + { GID_LSL6, 740, 740, 0, "showCartoon", "changeState", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // during ending, 4 additional parameters are passed by accident + { GID_LSL6HIRES, 130, 130, 0, "recruitLarryScr", "changeState", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // during intro, a 3rd parameter is passed by accident + { GID_SQ1, 43, 303, 0, "slotGuy", "dispose", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // when leaving ulence flats bar, parameter 1 is not passed - script error + { GID_QFG4, -1, 110, 0, "dreamer", "dispose", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // during the dream sequence, a 3rd parameter is passed by accident SCI_WORKAROUNDENTRY_TERMINATOR }; @@ -461,8 +751,9 @@ SciWorkaroundSolution trackOriginAndFindWorkaround(int index, const SciWorkaroun ExecStack *lastCall = state->xs; const Script *localScript = state->_segMan->getScriptIfLoaded(lastCall->local_segment); int curScriptNr = localScript->getScriptNumber(); + int curLocalCallOffset = lastCall->debugLocalCallOffset; - if (lastCall->debugLocalCallOffset != -1) { + if (curLocalCallOffset != -1) { // if lastcall was actually a local call search back for a real call Common::List<ExecStack>::const_iterator callIterator = state->_executionStack.end(); while (callIterator != state->_executionStack.begin()) { @@ -494,19 +785,17 @@ SciWorkaroundSolution trackOriginAndFindWorkaround(int index, const SciWorkaroun // Search if there is a workaround for this one const SciWorkaroundEntry *workaround; int16 inheritanceLevel = 0; - Common::String searchObjectName = curObjectName; + Common::String searchObjectName = g_sci->getSciLanguageString(curObjectName, K_LANG_ENGLISH); reg_t searchObject = lastCall->sendp; + const byte *curScriptPtr = NULL; + uint32 curScriptSize = 0; + bool matched = false; + do { workaround = workaroundList; while (workaround->methodName) { bool objectNameMatches = (workaround->objectName == NULL) || - (workaround->objectName == g_sci->getSciLanguageString(searchObjectName, K_LANG_ENGLISH)); - - // Special case: in the fanmade Russian translation of SQ4, all - // of the object names have been deleted or renamed to Russian, - // thus we disable checking of the object name. Fixes bug #5573. - if (g_sci->getLanguage() == Common::RU_RUS && g_sci->getGameId() == GID_SQ4) - objectNameMatches = true; + (workaround->objectName == searchObjectName); if (workaround->gameId == gameId && ((workaround->scriptNr == -1) || (workaround->scriptNr == curScriptNr)) @@ -514,10 +803,46 @@ SciWorkaroundSolution trackOriginAndFindWorkaround(int index, const SciWorkaroun && ((workaround->inheritanceLevel == -1) || (workaround->inheritanceLevel == inheritanceLevel)) && objectNameMatches && workaround->methodName == g_sci->getSciLanguageString(curMethodName, K_LANG_ENGLISH) - && workaround->localCallOffset == lastCall->debugLocalCallOffset && ((workaround->index == -1) || (workaround->index == index))) { // Workaround found - return workaround->newValue; + if ((workaround->localCallSignature) || (curLocalCallOffset >= 0)) { + // local call signature found and/or subcall was made + if ((workaround->localCallSignature) && (curLocalCallOffset >= 0)) { + // local call signature found and subcall was made -> check signature accordingly + if (!curScriptPtr) { + // get script data + int segmentId = g_sci->getEngineState()->_segMan->getScriptSegment(curScriptNr); + SegmentObj *segmentObj = NULL; + if (segmentId) { + segmentObj = g_sci->getEngineState()->_segMan->getScriptIfLoaded(segmentId); + } + if (!segmentObj) { + workaround++; + continue; + } + Script *scriptObj = (Script *)segmentObj; + curScriptPtr = scriptObj->getBuf(); + curScriptSize = scriptObj->getScriptSize(); + } + + // now actually check for signature match + if (g_sci->getScriptPatcher()->verifySignature(curLocalCallOffset, workaround->localCallSignature, "workaround signature", curScriptPtr, curScriptSize)) { + matched = true; + } + + } else { + // mismatch, so workaround doesn't match + workaround++; + continue; + } + } else { + // no localcalls involved -> workaround matches + matched = true; + } + if (matched) { + debugC(kDebugLevelWorkarounds, "Workaround: '%s:%s' in script %d, localcall %x", workaround->objectName, workaround->methodName, curScriptNr, curLocalCallOffset); + return workaround->newValue; + } } workaround++; } diff --git a/engines/sci/engine/workarounds.h b/engines/sci/engine/workarounds.h index 9cad618481..46059a175c 100644 --- a/engines/sci/engine/workarounds.h +++ b/engines/sci/engine/workarounds.h @@ -60,7 +60,7 @@ struct SciWorkaroundEntry { int16 inheritanceLevel; const char *objectName; const char *methodName; - int localCallOffset; + const uint16 *localCallSignature; int index; SciWorkaroundSolution newValue; }; |