SCI32: Implement KQ7/SHIVERS custom Mac saving

Implements the custom Mac save and restore kPlatform subops
for KQ7 and Shivers. Still TODO: Mothergoose and Lighthouse.
This commit is contained in:
sluicebox 2020-04-19 23:46:13 -07:00
parent 916ce02661
commit 12d37a352b
6 changed files with 137 additions and 21 deletions

View file

@ -25,6 +25,7 @@
#include "common/gui_options.h"
#include "common/savefile.h"
#include "sci/engine/features.h"
#include "sci/engine/file.h"
#include "sci/engine/guest_additions.h"
#include "sci/engine/kernel.h"
#include "sci/engine/savegame.h"
@ -789,11 +790,25 @@ bool GuestAdditions::restoreFromLauncher() const {
reg_t args[] = { make_reg(0, _state->_delayedRestoreGameId - kSaveIdShift) };
invokeSelector(g_sci->getGameObject(), SELECTOR(restore), 1, args);
} else {
int saveId = _state->_delayedRestoreGameId;
// When `Game::restore` is invoked, it will call to `Restore::doit`
// which will automatically return the `_delayedRestoreGameId` instead
// of prompting the user for a save game
invokeSelector(g_sci->getGameObject(), SELECTOR(restore));
// initialize KQ7 Mac's global save state by recording the save id
// and description. this is necessary for subsequent saves to work
// after restoring from launcher.
if (g_sci->getGameId() == GID_KQ7 || g_sci->getPlatform() == Common::kPlatformMacintosh) {
_state->_kq7MacSaveGameId = saveId;
SavegameDesc savegameDesc;
if (fillSavegameDesc(g_sci->getSavegameName(saveId), savegameDesc)) {
_state->_kq7MacSaveGameDescription = savegameDesc.name;
}
}
// The normal save game system resets _delayedRestoreGameId with a
// call to `EngineState::reset`, but RAMA uses a custom save game
// system which does not reset the engine, so we need to clear the

View file

@ -21,6 +21,7 @@
*/
#include "common/config-manager.h"
#include "common/savefile.h"
#include "common/system.h"
#include "sci/sci.h"
@ -29,10 +30,15 @@
#include "sci/engine/state.h"
#include "sci/engine/kernel.h"
#include "sci/engine/gc.h"
#ifdef ENABLE_SCI32
#include "sci/engine/guest_additions.h"
#endif
#include "sci/engine/savegame.h"
#include "sci/graphics/cursor.h"
#include "sci/graphics/palette.h"
#ifdef ENABLE_SCI32
#include "sci/graphics/cursor32.h"
#include "sci/graphics/frameout.h"
#endif
#include "sci/graphics/maciconbar.h"
#include "sci/console.h"
@ -570,32 +576,118 @@ reg_t kMacPlatform(EngineState *s, int argc, reg_t *argv) {
}
#ifdef ENABLE_SCI32
// kMacKq7InitializeSave is a subop of kMacPlatform32.
// KQ7 Mac would display a native Save dialog with the prompt "Who's game?"
// and store the result in a global variable inside the interpreter
// for subsequent calls to kMacKq7SaveGame.
reg_t kMacKq7InitializeSave(EngineState *s) {
s->_kq7MacSaveGameId = g_sci->_guestAdditions->runSaveRestore(true, s->_kq7MacSaveGameDescription);
s->_kq7MacSaveGameId = shiftSciToScummVMSaveId(s->_kq7MacSaveGameId);
return (s->_kq7MacSaveGameId != -1) ? TRUE_REG : NULL_REG;
}
// kMacKq7SaveGame is a subop of kMacPlatform32.
// Saves the game using the current save id and description that's set
// when initializing or restoring a saved game.
reg_t kMacKq7SaveGame(EngineState *s) {
if (s->_kq7MacSaveGameId == -1) {
error("kMacKq7SaveGame: save game hasn't been initialized");
}
const reg_t version = s->variables[VAR_GLOBAL][kGlobalVarVersion];
const Common::String versionString = s->_segMan->getString(version);
if (gamestate_save(s, s->_kq7MacSaveGameId, s->_kq7MacSaveGameDescription, versionString)) {
return TRUE_REG;
}
return NULL_REG;
}
// kMacKq7RestoreGame is a subop of kMacPlatform32.
// KQ7 Mac would display a native Open dialog with the prompt "Who's game?"
// and store the result in a global variable inside the interpreter to
// use in subsequent calls to kMacKq7SaveGame before restoring.
reg_t kMacKq7RestoreGame(EngineState *s) {
s->_kq7MacSaveGameId = g_sci->_guestAdditions->runSaveRestore(false, s->_kq7MacSaveGameDescription);
s->_kq7MacSaveGameId = shiftSciToScummVMSaveId(s->_kq7MacSaveGameId);
if (s->_kq7MacSaveGameId == -1) {
return NULL_REG;
}
// gamestate_restore() resets s->_kq7MacSaveGameId and
// s->_kq7MacSaveGameDescription so save and restore them.
int kq7MacSaveGameId = s->_kq7MacSaveGameId;
Common::String kq7MacSaveGameDescription = s->_kq7MacSaveGameDescription;
bool success = gamestate_restore(s, s->_kq7MacSaveGameId);
s->_kq7MacSaveGameId = kq7MacSaveGameId;
s->_kq7MacSaveGameDescription = kq7MacSaveGameDescription;
return success ? TRUE_REG : NULL_REG;
}
// kMacShiversInitializeSave is a subop of kMacPlatform32.
reg_t kMacShiversInitializeSave(EngineState *s, int argc, reg_t *argv) {
return TRUE_REG; // NULL_REG if i/o errors
}
// kMacShiversSaveGame is a subop of kMacPlatform32.
reg_t kMacShiversSaveGame(EngineState *s, int argc, reg_t *argv) {
g_sci->_gfxFrameout->kernelFrameOut(true); // see kSaveGame32
const int saveId = shiftSciToScummVMSaveId(argv[1].toUint16());
const Common::String description = s->_segMan->getString(argv[2]);
const reg_t version = s->variables[VAR_GLOBAL][kGlobalVarVersion];
const Common::String versionString = s->_segMan->getString(version);
if (gamestate_save(s, saveId, description, versionString)) {
return TRUE_REG;
}
return NULL_REG;
}
// kMacShiversRestoreGame is a subop of kMacPlatform32.
reg_t kMacShiversRestoreGame(EngineState *s, int argc, reg_t *argv) {
const int saveId = shiftSciToScummVMSaveId(argv[1].toUint16());
if (gamestate_restore(s, saveId)) {
return TRUE_REG;
}
return NULL_REG;
}
reg_t kMacPlatform32(EngineState *s, int argc, reg_t *argv) {
switch (argv[0].toUint16()) {
case 0: // build cursor view map
g_sci->_gfxCursor32->setMacCursorRemapList(argc - 1, argv + 1);
break;
return s->r_acc;
case 1: // compact/purge mac memory
case 2: // hands-off/hands-on for mac menus
break;
return s->r_acc;
// TODO: Save game handling in KQ7, Shivers, and Lighthouse.
// - KQ7 uses all three with no parameters; the interpreter would
// remember the current save file.
// - Shivers uses all three but passes parameters in a similar
// manner as the normal kSave\kRestore calls.
// - Lighthouse goes insane and only uses subop 3 but adds sub-subops
// which appear to do the three operations.
// Temporarily stubbing these out with success values so that KQ7 can start.
case 3: // initialize save game file
warning("Unimplemented kMacPlatform32(%d): Initialize save game file", argv[0].toUint16());
return TRUE_REG;
case 4: // save game
warning("Unimplemented kMacPlatform32(%d): Save game", argv[0].toUint16());
return TRUE_REG;
case 5: // restore game
warning("Unimplemented kMacPlatform32(%d): Restore game", argv[0].toUint16());
// Subops 3-5 are used for custom saving and restoring but they
// changed completely between each game that uses them.
//
// KQ7: 3-5 with no parameters
// Shivers: 3-5 with parameters
// Lighthouse: 3 with sub-subops: -1, 0, and 1 (TODO)
case 3:
if (argc == 1) {
return kMacKq7InitializeSave(s);
} else if (argc == 3) {
return kMacShiversInitializeSave(s, argc - 1, argv + 1);
}
break;
case 4:
if (argc == 1) {
return kMacKq7SaveGame(s);
} else if (argc == 4) {
return kMacShiversSaveGame(s, argc - 1, argv + 1);
}
break;
case 5:
if (argc == 1) {
return kMacKq7RestoreGame(s);
} else if (argc == 3) {
return kMacShiversRestoreGame(s, argc - 1, argv + 1);
}
break;
// TODO: Mother Goose save game handling
@ -605,18 +697,18 @@ reg_t kMacPlatform32(EngineState *s, int argc, reg_t *argv) {
case 9:
case 10:
case 11:
error("Unimplemented kMacPlatform32(%d) save game operation", argv[0].toUint16());
break;
// TODO: Phantasmagoria music volume adjustment [ 0-15 ]
case 12:
warning("Unimplemented kMacPlatform32(%d): Set volume: %d", argv[0].toUint16(), argv[1].toUint16());
break;
return s->r_acc;
default:
error("Unknown kMacPlatform32(%d)", argv[0].toUint16());
break;
}
error("Unknown kMacPlatform32(%d)", argv[0].toUint16());
return s->r_acc;
}
#endif

View file

@ -88,6 +88,9 @@ void EngineState::reset(bool isRestoring) {
_delayedRestoreGameId = -1;
_kq7MacSaveGameId = -1;
_kq7MacSaveGameDescription.clear();
executionStackBase = 0;
_executionStackPosChanged = false;
stack_base = 0;

View file

@ -136,6 +136,10 @@ public:
// see detection.cpp / SciEngine::loadGameState()
int _delayedRestoreGameId; // the saved game id, that it supposed to get restored (triggered by ScummVM menu)
// see kmisc.cpp / kMacPlatform32
int _kq7MacSaveGameId; // the saved game id to use when saving (might not exist yet)
Common::String _kq7MacSaveGameDescription; // description to use when saving game
uint _chosenQfGImportItem; // Remembers the item selected in QfG import rooms
bool _cursorWorkaroundActive; // Refer to GfxCursor::setPosition()

View file

@ -149,6 +149,7 @@ enum GlobalVar {
kGlobalVarPreviousRoomNo = 12,
kGlobalVarNewRoomNo = 13,
kGlobalVarScore = 15,
kGlobalVarVersion = 27,
kGlobalVarGK2MusicVolume = 76, // 0 to 127
kGlobalVarPhant2SecondaryVolume = 76, // 0 to 127
kGlobalVarFastCast = 84, // SCI16

View file

@ -446,6 +446,7 @@ const SciWorkaroundEntry uninitializedReadWorkarounds[] = {
{ GID_KQ7, 2450, 2450, 0, "maliciaComes", "handleEvent", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // when malicia appears at the southeast exit of the main chamber near the end of chapter 2
{ GID_KQ7, 5300, 5302, 0, "putOnMask", "handleEvent", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // in chapter 3, after using the mask on Valanice, click the jackalope hair in inventory - bug Trac#9759
{ GID_KQ7, 6060, 64964, 0, "DPath", "init", NULL, 1, 1, { WORKAROUND_FAKE, 0 } }, // after entering the harp crystal in chapter 5
{ GID_KQ7, -1, 64994, -1, "Game", "restore", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // when restoring from ScummVM launcher in Mac version
{ GID_LAURABOW, 37, 0, 0, "CB1", "doit", NULL, 1, 1, { WORKAROUND_FAKE, 0 } }, // when going up the stairs - bug #5084
{ GID_LAURABOW, -1, 967, 0, "myIcon", "cycle", NULL, 1, 1, { WORKAROUND_FAKE, 0 } }, // having any portrait conversation coming up - initial bug #4971
{ GID_LAURABOW2, -1, 24, 0, "gcWin", "open", NULL, 5, 5, { WORKAROUND_FAKE, 0xf } }, // is used as priority for game menu