aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--engines/sci/engine/guest_additions.cpp177
-rw-r--r--engines/sci/engine/guest_additions.h33
-rw-r--r--engines/sci/engine/kernel.h1
-rw-r--r--engines/sci/engine/kernel_tables.h9
-rw-r--r--engines/sci/engine/kfile.cpp63
-rw-r--r--engines/sci/engine/savegame.cpp13
-rw-r--r--engines/sci/engine/seg_manager.cpp6
-rw-r--r--engines/sci/graphics/frameout.cpp83
-rw-r--r--engines/sci/graphics/frameout.h1
-rw-r--r--engines/sci/sci.cpp198
-rw-r--r--engines/sci/sci.h2
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:
/**