diff options
Diffstat (limited to 'engines')
-rw-r--r-- | engines/sci/engine/guest_additions.cpp | 177 | ||||
-rw-r--r-- | engines/sci/engine/guest_additions.h | 33 | ||||
-rw-r--r-- | engines/sci/engine/kernel.h | 1 | ||||
-rw-r--r-- | engines/sci/engine/kernel_tables.h | 9 | ||||
-rw-r--r-- | engines/sci/engine/kfile.cpp | 63 | ||||
-rw-r--r-- | engines/sci/engine/savegame.cpp | 13 | ||||
-rw-r--r-- | engines/sci/engine/seg_manager.cpp | 6 | ||||
-rw-r--r-- | engines/sci/graphics/frameout.cpp | 83 | ||||
-rw-r--r-- | engines/sci/graphics/frameout.h | 1 | ||||
-rw-r--r-- | engines/sci/sci.cpp | 198 | ||||
-rw-r--r-- | engines/sci/sci.h | 2 |
11 files changed, 236 insertions, 350 deletions
diff --git a/engines/sci/engine/guest_additions.cpp b/engines/sci/engine/guest_additions.cpp index a2c64239a5..6477e3284b 100644 --- a/engines/sci/engine/guest_additions.cpp +++ b/engines/sci/engine/guest_additions.cpp @@ -29,6 +29,8 @@ #include "sci/engine/state.h" #include "sci/engine/vm.h" #ifdef ENABLE_SCI32 +#include "common/translation.h" +#include "gui/saveload.h" #include "sci/graphics/frameout.h" #endif #include "sci/sound/music.h" @@ -52,9 +54,10 @@ enum { }; -GuestAdditions::GuestAdditions(EngineState *state, GameFeatures *features) : +GuestAdditions::GuestAdditions(EngineState *state, GameFeatures *features, Kernel *kernel) : _state(state), _features(features), + _kernel(kernel), _segMan(state->_segMan), _messageTypeSynced(false) {} @@ -205,6 +208,178 @@ void GuestAdditions::kDoSoundSetVolumeHook(const reg_t soundObj, const int16 vol syncGK1AudioVolumeToScummVM(soundObj, volume); } } + +void GuestAdditions::instantiateScriptHook(Script &script) const { + if (getSciVersion() < SCI_VERSION_2) { + return; + } + + // 64990 is the system script containing SRDialog. This script is normally + // used by the main Game object, but it is not loaded immediately, so we + // wait for it to be loaded before patching it + if (!ConfMan.getBool("originalsaveload") && script.getScriptNumber() == 64990) { + patchGameSaveRestoreSCI32(script); + } +} + +#endif + +#pragma mark - +#pragma mark Save & restore + +void GuestAdditions::patchGameSaveRestore() const { + if (ConfMan.getBool("originalsaveload") || getSciVersion() >= SCI_VERSION_2) + return; + + patchGameSaveRestoreSCI16(); +} + +static const byte kSaveRestorePatch[] = { + 0x39, 0x03, // pushi 03 + 0x76, // push0 + 0x38, 0xff, 0xff, // pushi -1 + 0x76, // push0 + 0x43, 0xff, 0x06, // callk kRestoreGame/kSaveGame (will get changed afterwards) + 0x48 // ret +}; + +static void patchKSaveRestore(SegManager *segMan, reg_t methodAddress, byte id) { + Script *script = segMan->getScript(methodAddress.getSegment()); + byte *patchPtr = const_cast<byte *>(script->getBuf(methodAddress.getOffset())); + memcpy(patchPtr, kSaveRestorePatch, sizeof(kSaveRestorePatch)); + patchPtr[8] = id; +} + +void GuestAdditions::patchGameSaveRestoreSCI16() const { + const Object *gameObject = _segMan->getObject(g_sci->getGameObject()); + const Object *gameSuperObject = _segMan->getObject(gameObject->getSuperClassSelector()); + if (!gameSuperObject) + gameSuperObject = gameObject; // happens in KQ5CD, when loading saved games before r54510 + byte kernelIdRestore = 0; + byte kernelIdSave = 0; + + switch (g_sci->getGameId()) { + case GID_HOYLE1: // gets confused, although the game doesn't support saving/restoring at all + case GID_HOYLE2: // gets confused, see hoyle1 + case GID_JONES: // gets confused, when we patch us in, the game is only able to save to 1 slot, so hooking is not required + case GID_MOTHERGOOSE: // mother goose EGA saves/restores directly and has no save/restore dialogs + case GID_MOTHERGOOSE256: // mother goose saves/restores directly and has no save/restore dialogs + return; + default: + break; + } + + uint16 kernelNamesSize = _kernel->getKernelNamesSize(); + for (uint16 kernelNr = 0; kernelNr < kernelNamesSize; kernelNr++) { + Common::String kernelName = _kernel->getKernelName(kernelNr); + if (kernelName == "RestoreGame") + kernelIdRestore = kernelNr; + if (kernelName == "SaveGame") + kernelIdSave = kernelNr; + if (kernelName == "Save") + kernelIdSave = kernelIdRestore = kernelNr; + } + + // Search for gameobject superclass ::restore + uint16 gameSuperObjectMethodCount = gameSuperObject->getMethodCount(); + for (uint16 methodNr = 0; methodNr < gameSuperObjectMethodCount; methodNr++) { + uint16 selectorId = gameSuperObject->getFuncSelector(methodNr); + Common::String methodName = _kernel->getSelectorName(selectorId); + if (methodName == "restore") { + patchKSaveRestore(_segMan, gameSuperObject->getFunction(methodNr), kernelIdRestore); + } else if (methodName == "save") { + if (g_sci->getGameId() != GID_FAIRYTALES) { // Fairy Tales saves automatically without a dialog + patchKSaveRestore(_segMan, gameSuperObject->getFunction(methodNr), kernelIdSave); + } + } + } + + // Patch gameobject ::save for now for SCI0 - SCI1.1 + // TODO: It seems this was never adjusted to superclass, but adjusting it now may cause + // issues with some game. Needs to get checked and then possibly changed. + const Object *patchObjectSave = gameObject; + + // Search for gameobject ::save, if there is one patch that one too + uint16 patchObjectMethodCount = patchObjectSave->getMethodCount(); + for (uint16 methodNr = 0; methodNr < patchObjectMethodCount; methodNr++) { + uint16 selectorId = patchObjectSave->getFuncSelector(methodNr); + Common::String methodName = _kernel->getSelectorName(selectorId); + if (methodName == "save") { + if (g_sci->getGameId() != GID_FAIRYTALES) { // Fairy Tales saves automatically without a dialog + patchKSaveRestore(_segMan, patchObjectSave->getFunction(methodNr), kernelIdSave); + } + break; + } + } +} + +#ifdef ENABLE_SCI32 +static const byte SRDialogPatch[] = { + 0x76, // push0 + 0x59, 0x00, // &rest 0 + 0x43, 0x57, 0x00, 0x00, // callk kScummVMSaveLoad, 0 + 0x48 // ret +}; + +void GuestAdditions::patchGameSaveRestoreSCI32(Script &script) const { + ObjMap &objMap = script.getObjectMap(); + for (ObjMap::iterator it = objMap.begin(); it != objMap.end(); ++it) { + Object &obj = it->_value; + if (obj.getNameSelector().isNull()) { + continue; + } + + if (Common::String(_segMan->derefString(obj.getNameSelector())) != "SRDialog") { + continue; + } + + const uint16 methodCount = obj.getMethodCount(); + for (uint16 methodNr = 0; methodNr < methodCount; ++methodNr) { + const uint16 selectorId = obj.getFuncSelector(methodNr); + const Common::String methodName = _kernel->getSelectorName(selectorId); + if (methodName == "doit") { + const reg_t methodAddress = obj.getFunction(methodNr); + byte *patchPtr = const_cast<byte *>(script.getBuf(methodAddress.getOffset())); + memcpy(patchPtr, SRDialogPatch, sizeof(SRDialogPatch)); + break; + } + } + } +} + +reg_t GuestAdditions::kScummVMSaveLoad(EngineState *s, int argc, reg_t *argv) const { + const bool isSave = (bool)argv[0].toSint16(); + int saveNo; + + if (isSave) { + GUI::SaveLoadChooser dialog(_("Save game:"), _("Save"), true); + saveNo = dialog.runModalWithCurrentTarget(); + if (saveNo != -1) { + reg_t descriptionId; + if (_segMan->isObject(argv[1])) { + descriptionId = readSelector(_segMan, argv[1], SELECTOR(data)); + } else { + descriptionId = argv[1]; + } + SciArray &description = *_segMan->lookupArray(descriptionId); + description.fromString(dialog.getResultString()); + } + } else { + GUI::SaveLoadChooser dialog(_("Restore game:"), _("Restore"), false); + saveNo = dialog.runModalWithCurrentTarget(); + } + + if (saveNo > 0) { + // The autosave slot in ScummVM takes up slot 0, but in SCI the first + // non-autosave save game number needs to be 0, so reduce the save + // number here to match what would come from the normal SCI save/restore + // dialog. There is additional special code for handling the autosave + // game inside of kRestoreGame32. + --saveNo; + } + + return make_reg(0, saveNo); +} #endif #pragma mark - diff --git a/engines/sci/engine/guest_additions.h b/engines/sci/engine/guest_additions.h index a6875d43ad..ebb328c901 100644 --- a/engines/sci/engine/guest_additions.h +++ b/engines/sci/engine/guest_additions.h @@ -29,6 +29,8 @@ namespace Sci { struct EngineState; class GameFeatures; +class Kernel; +class Script; class SegManager; /** @@ -43,7 +45,9 @@ class SegManager; */ class GuestAdditions { public: - GuestAdditions(EngineState *state, GameFeatures *features); + GuestAdditions(EngineState *state, GameFeatures *features, Kernel *kernel); + +#pragma mark - /** * Synchronises audio volume settings from ScummVM to the game. Called @@ -65,6 +69,7 @@ public: private: EngineState *_state; GameFeatures *_features; + Kernel *_kernel; SegManager *_segMan; /** @@ -117,6 +122,32 @@ public: * Guest additions hook for kDoSoundSetVolume. */ void kDoSoundSetVolumeHook(const reg_t soundObj, const int16 volume) const; + + /** + * Guest additions hook for SegManager::instantiateScript. + */ + void instantiateScriptHook(Script &script) const; +#endif + +#pragma mark - +#pragma mark Save & restore + +public: + /** + * Patches game scripts to hook into the ScummVM launcher UI when a user + * tries to save or restore a game from inside the game. + */ + void patchGameSaveRestore() const; + +private: + void patchGameSaveRestoreSCI16() const; + +#ifdef ENABLE_SCI32 +public: + reg_t kScummVMSaveLoad(EngineState *s, int argc, reg_t *argv) const; + +private: + void patchGameSaveRestoreSCI32(Script &script) const; #endif #pragma mark - diff --git a/engines/sci/engine/kernel.h b/engines/sci/engine/kernel.h index 3d337017af..e5b84dd089 100644 --- a/engines/sci/engine/kernel.h +++ b/engines/sci/engine/kernel.h @@ -491,6 +491,7 @@ reg_t kGetSaveFiles32(EngineState *s, int argc, reg_t *argv); reg_t kCheckSaveGame32(EngineState *s, int argc, reg_t *argv); reg_t kMakeSaveCatName(EngineState *s, int argc, reg_t *argv); reg_t kMakeSaveFileName(EngineState *s, int argc, reg_t *argv); +reg_t kScummVMSaveLoad(EngineState *s, int argc, reg_t *argv); reg_t kSetHotRectangles(EngineState *s, int argc, reg_t *argv); reg_t kIsHiRes(EngineState *s, int argc, reg_t *argv); diff --git a/engines/sci/engine/kernel_tables.h b/engines/sci/engine/kernel_tables.h index 6f696c4a69..5dbd5a5f46 100644 --- a/engines/sci/engine/kernel_tables.h +++ b/engines/sci/engine/kernel_tables.h @@ -777,12 +777,13 @@ static SciKernelMapEntry s_kernelMap[] = { { MAP_CALL(RespondsTo), SIG_EVERYWHERE, ".i", NULL, NULL }, { MAP_CALL(RestartGame), SIG_EVERYWHERE, "", NULL, NULL }, #ifdef ENABLE_SCI32 - { "RestoreGame", kRestoreGame32, SIG_THRU_SCI21EARLY, SIGFOR_ALL, "[r0]i[r0]", NULL, NULL }, + { "RestoreGame", kRestoreGame32, SIG_THRU_SCI21EARLY, SIGFOR_ALL, "ri[r0]", NULL, NULL }, #endif { MAP_CALL(RestoreGame), SIG_EVERYWHERE, "[r0]i[r0]", NULL, NULL }, { MAP_CALL(Said), SIG_EVERYWHERE, "[r0]", NULL, NULL }, #ifdef ENABLE_SCI32 - { "SaveGame", kSaveGame32, SIG_THRU_SCI21EARLY, SIGFOR_ALL, "[r0]i[r0][r0]", NULL, NULL }, + { "SaveGame", kSaveGame32, SIG_THRU_SCI21EARLY, SIGFOR_ALL, "ri[r0][r0]", NULL, NULL }, + { MAP_CALL(ScummVMSaveLoad), SIG_SCI32, SIGFOR_ALL, "i([ro])", NULL, NULL }, #endif { MAP_CALL(SaveGame), SIG_SCI16, SIGFOR_ALL, "[r0]i[r0](r0)", NULL, NULL }, { MAP_CALL(ScriptID), SIG_EVERYWHERE, "[io](i)", NULL, NULL }, @@ -1263,7 +1264,7 @@ static const char *const sci2_default_knames[] = { /*0x54*/ "Dummy", /*0x55*/ "DeleteKey", /*0x56*/ "Dummy", - /*0x57*/ "Dummy", + /*0x57*/ "ScummVMSaveLoad", // Dummy in SSCI /*0x58*/ "ListAt", /*0x59*/ "ListIndexOf", /*0x5a*/ "ListEachElementDo", @@ -1427,7 +1428,7 @@ static const char *const sci21_default_knames[] = { /*0x54*/ "HaveMouse", /*0x55*/ "SetCursor", /*0x56*/ "VibrateMouse", // Dummy in SCI3 - /*0x57*/ "Dummy", + /*0x57*/ "ScummVMSaveLoad", // Dummy in SSCI /*0x58*/ "Dummy", /*0x59*/ "Dummy", /*0x5a*/ "List", diff --git a/engines/sci/engine/kfile.cpp b/engines/sci/engine/kfile.cpp index 6f9aa0d998..bdf9bb69aa 100644 --- a/engines/sci/engine/kfile.cpp +++ b/engines/sci/engine/kfile.cpp @@ -41,6 +41,7 @@ #include "sci/sound/audio.h" #include "sci/console.h" #ifdef ENABLE_SCI32 +#include "sci/engine/guest_additions.h" #include "sci/resource.h" #endif @@ -1179,34 +1180,10 @@ reg_t kGetSaveFiles(EngineState *s, int argc, reg_t *argv) { #ifdef ENABLE_SCI32 reg_t kSaveGame32(EngineState *s, int argc, reg_t *argv) { - const bool isScummVMSave = argv[0].isNull(); - Common::String gameName = ""; - int16 saveNo; - Common::String saveDescription; - Common::String gameVersion = (argc <= 3 || argv[3].isNull()) ? "" : s->_segMan->getString(argv[3]); - - if (isScummVMSave) { - // ScummVM call, from a patched Game::save - g_sci->_soundCmd->pauseAll(true); - GUI::SaveLoadChooser dialog(_("Save game:"), _("Save"), true); - saveNo = dialog.runModalWithCurrentTarget(); - g_sci->_soundCmd->pauseAll(false); - - if (saveNo < 0) { - // User cancelled save - return NULL_REG; - } - - saveDescription = dialog.getResultString(); - if (saveDescription.empty()) { - saveDescription = dialog.createDefaultSaveDescription(saveNo); - } - } else { - // Native script call - gameName = s->_segMan->getString(argv[0]); - saveNo = argv[1].toSint16(); - saveDescription = argv[2].isNull() ? "" : s->_segMan->getString(argv[2]); - } + const Common::String gameName = s->_segMan->getString(argv[0]); + int16 saveNo = argv[1].toSint16(); + const Common::String saveDescription = argv[2].isNull() ? "" : s->_segMan->getString(argv[2]); + const Common::String gameVersion = (argc <= 3 || argv[3].isNull()) ? "" : s->_segMan->getString(argv[3]); debugC(kDebugLevelFile, "Game name %s save %d desc %s ver %s", gameName.c_str(), saveNo, saveDescription.c_str(), gameVersion.c_str()); @@ -1218,9 +1195,7 @@ reg_t kSaveGame32(EngineState *s, int argc, reg_t *argv) { // Autosave slot 1 is a "new game" save saveNo = kNewGameId; } - } else if (!isScummVMSave) { - // ScummVM save screen will give a pre-corrected save number, but native - // save-load will not + } else { saveNo += kSaveIdShift; } @@ -1252,26 +1227,10 @@ reg_t kSaveGame32(EngineState *s, int argc, reg_t *argv) { } reg_t kRestoreGame32(EngineState *s, int argc, reg_t *argv) { - const bool isScummVMRestore = argv[0].isNull(); - Common::String gameName = ""; + const Common::String gameName = s->_segMan->getString(argv[0]); int16 saveNo = argv[1].toSint16(); const Common::String gameVersion = argv[2].isNull() ? "" : s->_segMan->getString(argv[2]); - if (isScummVMRestore && saveNo == -1) { - // ScummVM call, either from lancher or a patched Game::restore - g_sci->_soundCmd->pauseAll(true); - GUI::SaveLoadChooser dialog(_("Restore game:"), _("Restore"), false); - saveNo = dialog.runModalWithCurrentTarget(); - g_sci->_soundCmd->pauseAll(false); - - if (saveNo < 0) { - // User cancelled restore - return s->r_acc; - } - } else { - gameName = s->_segMan->getString(argv[0]); - } - if (gameName == "Autosave" || gameName == "Autosv") { if (saveNo == 0) { // Autosave slot 0 is the autosave @@ -1279,9 +1238,7 @@ reg_t kRestoreGame32(EngineState *s, int argc, reg_t *argv) { // Autosave slot 1 is a "new game" save saveNo = kNewGameId; } - } else if (!isScummVMRestore) { - // ScummVM save screen will give a pre-corrected save number, but native - // save-load will not + } else { saveNo += kSaveIdShift; } @@ -1377,6 +1334,10 @@ reg_t kMakeSaveFileName(EngineState *s, int argc, reg_t *argv) { return argv[0]; } +reg_t kScummVMSaveLoad(EngineState *s, int argc, reg_t *argv) { + return g_sci->_guestAdditions->kScummVMSaveLoad(s, argc, argv); +} + #endif } // End of namespace Sci diff --git a/engines/sci/engine/savegame.cpp b/engines/sci/engine/savegame.cpp index f05fdc5cb9..5a60a6090f 100644 --- a/engines/sci/engine/savegame.cpp +++ b/engines/sci/engine/savegame.cpp @@ -1229,19 +1229,6 @@ void gamestate_restore(EngineState *s, Common::SeekableReadStream *fh) { if (g_sci->_gfxScreen) g_sci->_gfxScreen->clearForRestoreGame(); } -#ifdef ENABLE_SCI32 - // Delete current planes/elements of actively loaded VM, only when our ScummVM dialogs are patched in - // We MUST NOT delete all planes/screen items. At least Space Quest 6 has a few in memory like for example - // the options plane, which are not re-added and are in memory all the time right from the start of the - // game. Sierra SCI32 did not clear planes, only scripts cleared the ones inside planes::elements. - if (getSciVersion() >= SCI_VERSION_2) { - if (!s->_delayedRestoreFromLauncher) { - // Only do it, when we are restoring regulary and not from launcher - // As it could result in option planes etc. on the screen (happens in gk1) - g_sci->_gfxFrameout->syncWithScripts(false); - } - } -#endif s->reset(true); s->saveLoadWithSerializer(ser); // FIXME: Error handling? diff --git a/engines/sci/engine/seg_manager.cpp b/engines/sci/engine/seg_manager.cpp index 12625691fd..d13d94a957 100644 --- a/engines/sci/engine/seg_manager.cpp +++ b/engines/sci/engine/seg_manager.cpp @@ -24,6 +24,9 @@ #include "sci/engine/seg_manager.h" #include "sci/engine/state.h" #include "sci/engine/script.h" +#ifdef ENABLE_SCI32 +#include "sci/engine/guest_additions.h" +#endif namespace Sci { @@ -1041,6 +1044,9 @@ int SegManager::instantiateScript(int scriptNum) { scr->initializeLocals(this); scr->initializeClasses(this); scr->initializeObjects(this, segmentId); +#ifdef ENABLE_SCI32 + g_sci->_guestAdditions->instantiateScriptHook(*scr); +#endif return segmentId; } diff --git a/engines/sci/graphics/frameout.cpp b/engines/sci/graphics/frameout.cpp index 5f96a8e1c9..47992763f1 100644 --- a/engines/sci/graphics/frameout.cpp +++ b/engines/sci/graphics/frameout.cpp @@ -127,95 +127,12 @@ void GfxFrameout::run() { _planes.add(initPlane); } -// SCI32 actually did not clear anything at all it seems on restore. The scripts actually cleared up -// planes + screen items right before restoring. And after restoring they sync'd its internal planes list -// as well. void GfxFrameout::clear() { _planes.clear(); _visiblePlanes.clear(); _showList.clear(); } -// This is what Game::restore does, only needed when our ScummVM dialogs are patched in -// It actually does one pass before actual restore deleting screen items + planes -// And after restore it does another pass adding screen items + planes. -// Attention: at least Space Quest 6's option plane seems to stay in memory right from the start and is not re-created. -void GfxFrameout::syncWithScripts(bool addElements) { - EngineState *engineState = g_sci->getEngineState(); - SegManager *segMan = engineState->_segMan; - - // In case original save/restore dialogs are active, don't do anything - if (ConfMan.getBool("originalsaveload")) - return; - - // Get planes list object - reg_t planesListObject = engineState->variables[VAR_GLOBAL][kGlobalVarPlanes]; - reg_t planesListElements = readSelector(segMan, planesListObject, SELECTOR(elements)); - - List *planesList = segMan->lookupList(planesListElements); - reg_t planesNodeObject = planesList->first; - - // Go through all elements of planes::elements - while (!planesNodeObject.isNull()) { - Node *planesNode = segMan->lookupNode(planesNodeObject); - reg_t planeObject = planesNode->value; - - if (addElements) { - // Add this plane object - kernelAddPlane(planeObject); - } - - reg_t planeCastsObject = readSelector(segMan, planeObject, SELECTOR(casts)); - reg_t setListElements = readSelector(segMan, planeCastsObject, SELECTOR(elements)); - - // Now go through all elements of plane::casts::elements - List *planeCastsList = segMan->lookupList(setListElements); - reg_t planeCastsNodeObject = planeCastsList->first; - - while (!planeCastsNodeObject.isNull()) { - Node *castsNode = segMan->lookupNode(planeCastsNodeObject); - reg_t castsObject = castsNode->value; - - reg_t castsListElements = readSelector(segMan, castsObject, SELECTOR(elements)); - - List *castsList = segMan->lookupList(castsListElements); - reg_t castNodeObject = castsList->first; - - while (!castNodeObject.isNull()) { - Node *castNode = segMan->lookupNode(castNodeObject); - reg_t castObject = castNode->value; - - // read selector "-info-" of this object - // TODO: Seems to have been changed for SCI3 - // Do NOT use getInfoSelector in here. SCI3 games did not use infoToa, but an actual selector. - // Maybe that selector is just a straight copy, but it needs to get verified/checked. - uint16 castInfoSelector = readSelectorValue(segMan, castObject, SELECTOR(_info_)); - - if (castInfoSelector & kInfoFlagViewInserted) { - if (addElements) { - // Flag set, so add this screen item - kernelAddScreenItem(castObject); - } else { - // Flag set, so delete this screen item - kernelDeleteScreenItem(castObject); - } - } - - castNodeObject = castNode->succ; - } - - planeCastsNodeObject = castsNode->succ; - } - - if (!addElements) { - // Delete this plane object - kernelDeletePlane(planeObject); - } - - planesNodeObject = planesNode->succ; - } -} - bool GfxFrameout::gameIsHiRes() const { // QFG4 is always low resolution if (g_sci->getGameId() == GID_QFG4) { diff --git a/engines/sci/graphics/frameout.h b/engines/sci/graphics/frameout.h index 9738203b74..208c5b439c 100644 --- a/engines/sci/graphics/frameout.h +++ b/engines/sci/graphics/frameout.h @@ -58,7 +58,6 @@ public: bool _isHiRes; void clear(); - void syncWithScripts(bool addElements); // this is what Game::restore does, only needed when our ScummVM dialogs are patched in void run(); #pragma mark - diff --git a/engines/sci/sci.cpp b/engines/sci/sci.cpp index c8e500b700..0797fd8cba 100644 --- a/engines/sci/sci.cpp +++ b/engines/sci/sci.cpp @@ -283,7 +283,7 @@ Common::Error SciEngine::run() { _vocabulary = new Vocabulary(_resMan, false); _gamestate = new EngineState(segMan); - _guestAdditions = new GuestAdditions(_gamestate, _features); + _guestAdditions = new GuestAdditions(_gamestate, _features, _kernel); _eventMan = new EventManager(_resMan->detectFontExtended()); #ifdef ENABLE_SCI32 if (getSciVersion() >= SCI_VERSION_2_1_EARLY) { @@ -336,9 +336,7 @@ Common::Error SciEngine::run() { syncSoundSettings(); _guestAdditions->syncAudioOptionsFromScummVM(); - - // Patch in our save/restore code, so that dialogs are replaced - patchGameSaveRestore(); + _guestAdditions->patchGameSaveRestore(); setLauncherLanguage(); // Check whether loading a savestate was requested @@ -495,194 +493,6 @@ bool SciEngine::gameHasFanMadePatch() { return false; } -static byte patchGameRestoreSave[] = { - 0x39, 0x03, // pushi 03 - 0x76, // push0 - 0x38, 0xff, 0xff, // pushi -1 - 0x76, // push0 - 0x43, 0xff, 0x06, // callk kRestoreGame/kSaveGame (will get changed afterwards) - 0x48, // ret -}; - -#ifdef ENABLE_SCI32 -// SCI2 version: Same as above, but the second parameter to callk is a word -// and third parameter is a string reference -static byte patchGameRestoreSci2[] = { - 0x39, 0x03, // pushi 03 - 0x76, // push0 (game name) - 0x38, 0xff, 0xff, // pushi -1 (save number) - 0x89, 0x1b, // lsg global[27] (game version) - 0x43, 0xff, 0x06, 0x00, // callk kRestoreGame (0xFF will be overwritten by patcher) - 0x48, // ret -}; - -static byte patchGameSaveSci2[] = { - 0x39, 0x04, // pushi 04 - 0x76, // push0 (game name) - 0x38, 0xff, 0xff, // pushi -1 (save number) - 0x76, // push0 (save description) - 0x89, 0x1b, // lsg global[27] (game version) - 0x43, 0xff, 0x08, 0x00, // callk kSaveGame (0xFF will be overwritten by patcher) - 0x48, // ret -}; - -// SCI2.1mid version: Same as above, but with an extra subop parameter -static byte patchGameRestoreSci21[] = { - 0x39, 0x04, // pushi 04 - 0x78, // push1 (subop) - 0x76, // push0 (game name) - 0x38, 0xff, 0xff, // pushi -1 (save number) - 0x89, 0x1b, // lsg global[27] (game version) - 0x43, 0xff, 0x08, 0x00, // callk kSave (0xFF will be overwritten by patcher) - 0x48, // ret -}; - -static byte patchGameSaveSci21[] = { - 0x39, 0x05, // pushi 05 - 0x76, // push0 (subop) - 0x76, // push0 (game name) - 0x38, 0xff, 0xff, // pushi -1 (save number) - 0x76, // push0 (save description) - 0x89, 0x1b, // lsg global[27] (game version) - 0x43, 0xff, 0x0a, 0x00, // callk kSave (0xFF will be overwritten by patcher) - 0x48, // ret -}; -#endif - -static void patchGameSaveRestoreCode(SegManager *segMan, reg_t methodAddress, byte id) { - Script *script = segMan->getScript(methodAddress.getSegment()); - byte *patchPtr = const_cast<byte *>(script->getBuf(methodAddress.getOffset())); - - memcpy(patchPtr, patchGameRestoreSave, sizeof(patchGameRestoreSave)); - patchPtr[8] = id; -} - -#ifdef ENABLE_SCI32 -static void patchGameSaveRestoreCodeSci2(SegManager *segMan, reg_t methodAddress, byte id, bool doRestore) { - Script *script = segMan->getScript(methodAddress.getSegment()); - byte *patchPtr = const_cast<byte *>(script->getBuf(methodAddress.getOffset())); - int kcallOffset; - - if (getSciVersion() < SCI_VERSION_2_1_MIDDLE) { - if (doRestore) { - memcpy(patchPtr, patchGameRestoreSci2, sizeof(patchGameRestoreSci2)); - kcallOffset = 9; - } else { - memcpy(patchPtr, patchGameSaveSci2, sizeof(patchGameSaveSci2)); - kcallOffset = 10; - } - } else { - if (doRestore) { - memcpy(patchPtr, patchGameRestoreSci21, sizeof(patchGameRestoreSci21)); - kcallOffset = 10; - } else { - memcpy(patchPtr, patchGameSaveSci21, sizeof(patchGameSaveSci21)); - kcallOffset = 11; - } - } - - patchPtr[kcallOffset] = id; - if (g_sci->isBE()) { - SWAP(patchPtr[kcallOffset + 1], patchPtr[kcallOffset + 2]); - } -} -#endif - -void SciEngine::patchGameSaveRestore() { - SegManager *segMan = _gamestate->_segMan; - const Object *gameObject = segMan->getObject(_gameObjectAddress); - const Object *gameSuperObject = segMan->getObject(gameObject->getSuperClassSelector()); - if (!gameSuperObject) - gameSuperObject = gameObject; // happens in KQ5CD, when loading saved games before r54510 - byte kernelIdRestore = 0; - byte kernelIdSave = 0; - - switch (_gameId) { - case GID_HOYLE1: // gets confused, although the game doesn't support saving/restoring at all - case GID_HOYLE2: // gets confused, see hoyle1 - case GID_JONES: // gets confused, when we patch us in, the game is only able to save to 1 slot, so hooking is not required - case GID_KQ7: // has custom save/load code - case GID_MOTHERGOOSE: // mother goose EGA saves/restores directly and has no save/restore dialogs - case GID_MOTHERGOOSE256: // mother goose saves/restores directly and has no save/restore dialogs - case GID_MOTHERGOOSEHIRES: // has custom save/load code - case GID_PHANTASMAGORIA: // has custom save/load code - case GID_PQSWAT: // has custom save/load code - case GID_SHIVERS: // has custom save/load code - return; - default: - break; - } - - if (ConfMan.getBool("originalsaveload")) - return; - - uint16 kernelNamesSize = _kernel->getKernelNamesSize(); - for (uint16 kernelNr = 0; kernelNr < kernelNamesSize; kernelNr++) { - Common::String kernelName = _kernel->getKernelName(kernelNr); - if (kernelName == "RestoreGame") - kernelIdRestore = kernelNr; - if (kernelName == "SaveGame") - kernelIdSave = kernelNr; - if (kernelName == "Save") - kernelIdSave = kernelIdRestore = kernelNr; - } - - // Search for gameobject superclass ::restore - uint16 gameSuperObjectMethodCount = gameSuperObject->getMethodCount(); - for (uint16 methodNr = 0; methodNr < gameSuperObjectMethodCount; methodNr++) { - uint16 selectorId = gameSuperObject->getFuncSelector(methodNr); - Common::String methodName = _kernel->getSelectorName(selectorId); - if (methodName == "restore") { -#ifdef ENABLE_SCI32 - if (getSciVersion() >= SCI_VERSION_2) { - patchGameSaveRestoreCodeSci2(segMan, gameSuperObject->getFunction(methodNr), kernelIdRestore, true); - } else -#endif - patchGameSaveRestoreCode(segMan, gameSuperObject->getFunction(methodNr), kernelIdRestore); - } - else if (methodName == "save") { - if (_gameId != GID_FAIRYTALES) { // Fairy Tales saves automatically without a dialog -#ifdef ENABLE_SCI32 - if (getSciVersion() >= SCI_VERSION_2) { - patchGameSaveRestoreCodeSci2(segMan, gameSuperObject->getFunction(methodNr), kernelIdSave, false); - } else -#endif - patchGameSaveRestoreCode(segMan, gameSuperObject->getFunction(methodNr), kernelIdSave); - } - } - } - - const Object *patchObjectSave = nullptr; - - if (getSciVersion() < SCI_VERSION_2) { - // Patch gameobject ::save for now for SCI0 - SCI1.1 - // TODO: It seems this was never adjusted to superclass, but adjusting it now may cause - // issues with some game. Needs to get checked and then possibly changed. - patchObjectSave = gameObject; - } else { - // Patch superclass ::save for SCI32 - patchObjectSave = gameSuperObject; - } - - // Search for gameobject ::save, if there is one patch that one too - uint16 patchObjectMethodCount = patchObjectSave->getMethodCount(); - for (uint16 methodNr = 0; methodNr < patchObjectMethodCount; methodNr++) { - uint16 selectorId = patchObjectSave->getFuncSelector(methodNr); - Common::String methodName = _kernel->getSelectorName(selectorId); - if (methodName == "save") { - if (_gameId != GID_FAIRYTALES) { // Fairy Tales saves automatically without a dialog -#ifdef ENABLE_SCI32 - if (getSciVersion() >= SCI_VERSION_2) { - patchGameSaveRestoreCodeSci2(segMan, gameSuperObject->getFunction(methodNr), kernelIdSave, false); - } else -#endif - patchGameSaveRestoreCode(segMan, patchObjectSave->getFunction(methodNr), kernelIdSave); - } - break; - } - } -} - bool SciEngine::initGame() { // Script 0 needs to be allocated here before anything else! int script0Segment = _gamestate->_segMan->getScriptSegment(0, SCRIPT_GET_LOCK); @@ -851,7 +661,7 @@ void SciEngine::runGame() { _gamestate->_segMan->resetSegMan(); initGame(); initStackBaseWithSelector(SELECTOR(play)); - patchGameSaveRestore(); + _guestAdditions->patchGameSaveRestore(); setLauncherLanguage(); _gamestate->gameIsRestarting = GAMEISRESTARTING_RESTART; _gamestate->_throttleLastTime = 0; @@ -863,7 +673,7 @@ void SciEngine::runGame() { _gamestate->abortScriptProcessing = kAbortNone; _gamestate->_executionStack.clear(); initStackBaseWithSelector(SELECTOR(replay)); - patchGameSaveRestore(); + _guestAdditions->patchGameSaveRestore(); setLauncherLanguage(); _gamestate->shrinkStackToBase(); _gamestate->abortScriptProcessing = kAbortNone; diff --git a/engines/sci/sci.h b/engines/sci/sci.h index 744a1b4aed..ef2fb3ba75 100644 --- a/engines/sci/sci.h +++ b/engines/sci/sci.h @@ -314,8 +314,6 @@ public: bool checkSelectorBreakpoint(BreakpointType breakpointType, reg_t send_obj, int selector); bool checkAddressBreakpoint(const reg32_t &address); - void patchGameSaveRestore(); - public: /** |