diff options
author | Colin Snover | 2016-09-15 08:54:10 -0500 |
---|---|---|
committer | Colin Snover | 2016-09-29 19:39:16 -0500 |
commit | 1eb6d90eec4aa2dab3532e9630c81b6b3dbddafb (patch) | |
tree | c18f725995654edd1bf903fd527719683d1df951 | |
parent | b5d0fffb8b111c5d8154a37ba9a0d7b18866c7dd (diff) | |
download | scummvm-rg350-1eb6d90eec4aa2dab3532e9630c81b6b3dbddafb.tar.gz scummvm-rg350-1eb6d90eec4aa2dab3532e9630c81b6b3dbddafb.tar.bz2 scummvm-rg350-1eb6d90eec4aa2dab3532e9630c81b6b3dbddafb.zip |
SCI32: Implement ScummVM save/load
-rw-r--r-- | engines/sci/engine/kernel_tables.h | 8 | ||||
-rw-r--r-- | engines/sci/engine/kfile.cpp | 45 | ||||
-rw-r--r-- | engines/sci/sci.cpp | 123 |
3 files changed, 121 insertions, 55 deletions
diff --git a/engines/sci/engine/kernel_tables.h b/engines/sci/engine/kernel_tables.h index c74b3ae1fb..e11326fc47 100644 --- a/engines/sci/engine/kernel_tables.h +++ b/engines/sci/engine/kernel_tables.h @@ -351,8 +351,8 @@ static const SciKernelMapSubEntry kPalCycle_subops[] = { // version, subId, function-mapping, signature, workarounds static const SciKernelMapSubEntry kSave_subops[] = { - { SIG_SCI32, 0, MAP_CALL(SaveGame32), "rir[r0]", NULL }, - { SIG_SCI32, 1, MAP_CALL(RestoreGame32), "ri[r0]", NULL }, + { SIG_SCI32, 0, MAP_CALL(SaveGame32), "[r0]i[r0][r0]", NULL }, + { SIG_SCI32, 1, MAP_CALL(RestoreGame32), "[r0]i[r0]", NULL }, // System script 64994 in several SCI2.1mid games (KQ7 2.00b, Phant1, // PQ:SWAT, SQ6, Torin) calls GetSaveDir with an extra unused argument, and // it is easier to just handle it here than to bother with creating @@ -754,12 +754,12 @@ 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_UNTIL_SCI21EARLY, SIGFOR_ALL, "ri[r0]", NULL, NULL }, + { "RestoreGame", kRestoreGame32, SIG_UNTIL_SCI21EARLY, SIGFOR_ALL, "[r0]i[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_UNTIL_SCI21EARLY, SIGFOR_ALL, "rir[r0]", NULL, NULL }, + { "SaveGame", kSaveGame32, SIG_UNTIL_SCI21EARLY, SIGFOR_ALL, "[r0]i[r0][r0]", 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 }, diff --git a/engines/sci/engine/kfile.cpp b/engines/sci/engine/kfile.cpp index 7e5af79b23..50560be7d1 100644 --- a/engines/sci/engine/kfile.cpp +++ b/engines/sci/engine/kfile.cpp @@ -1077,10 +1077,32 @@ reg_t kGetSaveFiles(EngineState *s, int argc, reg_t *argv) { #ifdef ENABLE_SCI32 reg_t kSaveGame32(EngineState *s, int argc, reg_t *argv) { - const Common::String gameName = s->_segMan->getString(argv[0]); - int16 saveNo = argv[1].toSint16(); - const Common::String saveDescription = s->_segMan->getString(argv[2]); - const Common::String gameVersion = argv[3].isNull() ? "" : s->_segMan->getString(argv[3]); + Common::String gameName = ""; + int16 saveNo; + Common::String saveDescription; + Common::String gameVersion = (argc <= 3 || argv[3].isNull()) ? "" : s->_segMan->getString(argv[3]); + + if (argv[0].isNull()) { + // 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) { + 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]); + } // Auto-save system used by Torin and LSL7 if (gameName == "Autosave" || gameName == "Autosv") { @@ -1149,10 +1171,23 @@ reg_t kSaveGame32(EngineState *s, int argc, reg_t *argv) { } reg_t kRestoreGame32(EngineState *s, int argc, reg_t *argv) { - const Common::String gameName = s->_segMan->getString(argv[0]); + Common::String gameName = ""; int16 saveNo = argv[1].toSint16(); const Common::String gameVersion = argv[2].isNull() ? "" : s->_segMan->getString(argv[2]); + if (argv[0].isNull() && 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) { + 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 diff --git a/engines/sci/sci.cpp b/engines/sci/sci.cpp index bdfb6ccc9c..f161569598 100644 --- a/engines/sci/sci.cpp +++ b/engines/sci/sci.cpp @@ -496,61 +496,86 @@ static byte patchGameRestoreSave[] = { }; // SCI2 version: Same as above, but the second parameter to callk is a word -static byte patchGameRestoreSaveSci2[] = { - 0x39, 0x03, // pushi 03 - 0x76, // push0 - 0x38, 0xff, 0xff, // pushi -1 - 0x76, // push0 - 0x43, 0xff, 0x06, 0x00, // callk kRestoreGame/kSaveGame (will get changed afterwards) - 0x48, // ret +// 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 }; -// SCI21 version: Same as above, but the second parameter to callk is a word -static byte patchGameRestoreSaveSci21[] = { - 0x39, 0x04, // pushi 04 - 0x76, // push0 // 0: save, 1: restore (will get changed afterwards) - 0x76, // push0 - 0x38, 0xff, 0xff, // pushi -1 - 0x76, // push0 - 0x43, 0xff, 0x08, 0x00, // callk kSave (will get changed afterwards) - 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 }; 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())); - if (getSciVersion() <= SCI_VERSION_1_1) { - memcpy(patchPtr, patchGameRestoreSave, sizeof(patchGameRestoreSave)); - } else { // SCI2+ - memcpy(patchPtr, patchGameRestoreSaveSci2, sizeof(patchGameRestoreSaveSci2)); - - if (g_sci->isBE()) { - // LE -> BE - patchPtr[9] = 0x00; - patchPtr[10] = 0x06; - } - } - + memcpy(patchPtr, patchGameRestoreSave, sizeof(patchGameRestoreSave)); patchPtr[8] = id; } -static void patchGameSaveRestoreCodeSci21(SegManager *segMan, reg_t methodAddress, byte id, bool doRestore) { +#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())); - memcpy(patchPtr, patchGameRestoreSaveSci21, sizeof(patchGameRestoreSaveSci21)); + int kcallOffset; - if (doRestore) - patchPtr[2] = 0x78; // push1 + 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()) { - // LE -> BE - patchPtr[10] = 0x00; - patchPtr[11] = 0x08; + SWAP(patchPtr[kcallOffset + 1], patchPtr[kcallOffset + 2]); } - - patchPtr[9] = id; } +#endif void SciEngine::patchGameSaveRestore() { SegManager *segMan = _gamestate->_segMan; @@ -595,17 +620,21 @@ void SciEngine::patchGameSaveRestore() { uint16 selectorId = gameSuperObject->getFuncSelector(methodNr); Common::String methodName = _kernel->getSelectorName(selectorId); if (methodName == "restore") { - if (kernelIdSave != kernelIdRestore) +#ifdef ENABLE_SCI32 + if (getSciVersion() >= SCI_VERSION_2) { + patchGameSaveRestoreCodeSci2(segMan, gameSuperObject->getFunction(methodNr), kernelIdRestore, true); + } else +#endif patchGameSaveRestoreCode(segMan, gameSuperObject->getFunction(methodNr), kernelIdRestore); - else - patchGameSaveRestoreCodeSci21(segMan, gameSuperObject->getFunction(methodNr), kernelIdRestore, true); } else if (methodName == "save") { if (_gameId != GID_FAIRYTALES) { // Fairy Tales saves automatically without a dialog - if (kernelIdSave != kernelIdRestore) +#ifdef ENABLE_SCI32 + if (getSciVersion() >= SCI_VERSION_2) { + patchGameSaveRestoreCodeSci2(segMan, gameSuperObject->getFunction(methodNr), kernelIdSave, false); + } else +#endif patchGameSaveRestoreCode(segMan, gameSuperObject->getFunction(methodNr), kernelIdSave); - else - patchGameSaveRestoreCodeSci21(segMan, gameSuperObject->getFunction(methodNr), kernelIdSave, false); } } } @@ -629,10 +658,12 @@ void SciEngine::patchGameSaveRestore() { Common::String methodName = _kernel->getSelectorName(selectorId); if (methodName == "save") { if (_gameId != GID_FAIRYTALES) { // Fairy Tales saves automatically without a dialog - if (kernelIdSave != kernelIdRestore) +#ifdef ENABLE_SCI32 + if (getSciVersion() >= SCI_VERSION_2) { + patchGameSaveRestoreCodeSci2(segMan, gameSuperObject->getFunction(methodNr), kernelIdSave, false); + } else +#endif patchGameSaveRestoreCode(segMan, patchObjectSave->getFunction(methodNr), kernelIdSave); - else - patchGameSaveRestoreCodeSci21(segMan, patchObjectSave->getFunction(methodNr), kernelIdSave, false); } break; } |