SCI32: Implement ScummVM save/load
This commit is contained in:
parent
b5d0fffb8b
commit
1eb6d90eec
3 changed files with 122 additions and 56 deletions
|
@ -351,8 +351,8 @@ static const SciKernelMapSubEntry kPalCycle_subops[] = {
|
||||||
|
|
||||||
// version, subId, function-mapping, signature, workarounds
|
// version, subId, function-mapping, signature, workarounds
|
||||||
static const SciKernelMapSubEntry kSave_subops[] = {
|
static const SciKernelMapSubEntry kSave_subops[] = {
|
||||||
{ SIG_SCI32, 0, MAP_CALL(SaveGame32), "rir[r0]", NULL },
|
{ SIG_SCI32, 0, MAP_CALL(SaveGame32), "[r0]i[r0][r0]", NULL },
|
||||||
{ SIG_SCI32, 1, MAP_CALL(RestoreGame32), "ri[r0]", NULL },
|
{ SIG_SCI32, 1, MAP_CALL(RestoreGame32), "[r0]i[r0]", NULL },
|
||||||
// System script 64994 in several SCI2.1mid games (KQ7 2.00b, Phant1,
|
// System script 64994 in several SCI2.1mid games (KQ7 2.00b, Phant1,
|
||||||
// PQ:SWAT, SQ6, Torin) calls GetSaveDir with an extra unused argument, and
|
// 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
|
// 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(RespondsTo), SIG_EVERYWHERE, ".i", NULL, NULL },
|
||||||
{ MAP_CALL(RestartGame), SIG_EVERYWHERE, "", NULL, NULL },
|
{ MAP_CALL(RestartGame), SIG_EVERYWHERE, "", NULL, NULL },
|
||||||
#ifdef ENABLE_SCI32
|
#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
|
#endif
|
||||||
{ MAP_CALL(RestoreGame), SIG_EVERYWHERE, "[r0]i[r0]", NULL, NULL },
|
{ MAP_CALL(RestoreGame), SIG_EVERYWHERE, "[r0]i[r0]", NULL, NULL },
|
||||||
{ MAP_CALL(Said), SIG_EVERYWHERE, "[r0]", NULL, NULL },
|
{ MAP_CALL(Said), SIG_EVERYWHERE, "[r0]", NULL, NULL },
|
||||||
#ifdef ENABLE_SCI32
|
#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
|
#endif
|
||||||
{ MAP_CALL(SaveGame), SIG_SCI16, SIGFOR_ALL, "[r0]i[r0](r0)", NULL, NULL },
|
{ MAP_CALL(SaveGame), SIG_SCI16, SIGFOR_ALL, "[r0]i[r0](r0)", NULL, NULL },
|
||||||
{ MAP_CALL(ScriptID), SIG_EVERYWHERE, "[io](i)", NULL, NULL },
|
{ MAP_CALL(ScriptID), SIG_EVERYWHERE, "[io](i)", NULL, NULL },
|
||||||
|
|
|
@ -1077,10 +1077,32 @@ reg_t kGetSaveFiles(EngineState *s, int argc, reg_t *argv) {
|
||||||
|
|
||||||
#ifdef ENABLE_SCI32
|
#ifdef ENABLE_SCI32
|
||||||
reg_t kSaveGame32(EngineState *s, int argc, reg_t *argv) {
|
reg_t kSaveGame32(EngineState *s, int argc, reg_t *argv) {
|
||||||
const Common::String gameName = s->_segMan->getString(argv[0]);
|
Common::String gameName = "";
|
||||||
int16 saveNo = argv[1].toSint16();
|
int16 saveNo;
|
||||||
const Common::String saveDescription = s->_segMan->getString(argv[2]);
|
Common::String saveDescription;
|
||||||
const Common::String gameVersion = argv[3].isNull() ? "" : s->_segMan->getString(argv[3]);
|
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
|
// Auto-save system used by Torin and LSL7
|
||||||
if (gameName == "Autosave" || gameName == "Autosv") {
|
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) {
|
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();
|
int16 saveNo = argv[1].toSint16();
|
||||||
const Common::String gameVersion = argv[2].isNull() ? "" : s->_segMan->getString(argv[2]);
|
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 (gameName == "Autosave" || gameName == "Autosv") {
|
||||||
if (saveNo == 0) {
|
if (saveNo == 0) {
|
||||||
// Autosave slot 0 is the autosave
|
// Autosave slot 0 is the autosave
|
||||||
|
|
|
@ -496,61 +496,86 @@ static byte patchGameRestoreSave[] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
// SCI2 version: Same as above, but the second parameter to callk is a word
|
// SCI2 version: Same as above, but the second parameter to callk is a word
|
||||||
static byte patchGameRestoreSaveSci2[] = {
|
// and third parameter is a string reference
|
||||||
0x39, 0x03, // pushi 03
|
static byte patchGameRestoreSci2[] = {
|
||||||
0x76, // push0
|
0x39, 0x03, // pushi 03
|
||||||
0x38, 0xff, 0xff, // pushi -1
|
0x76, // push0 (game name)
|
||||||
0x76, // push0
|
0x38, 0xff, 0xff, // pushi -1 (save number)
|
||||||
0x43, 0xff, 0x06, 0x00, // callk kRestoreGame/kSaveGame (will get changed afterwards)
|
0x89, 0x1b, // lsg global[27] (game version)
|
||||||
0x48, // ret
|
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 patchGameSaveSci2[] = {
|
||||||
static byte patchGameRestoreSaveSci21[] = {
|
0x39, 0x04, // pushi 04
|
||||||
0x39, 0x04, // pushi 04
|
0x76, // push0 (game name)
|
||||||
0x76, // push0 // 0: save, 1: restore (will get changed afterwards)
|
0x38, 0xff, 0xff, // pushi -1 (save number)
|
||||||
0x76, // push0
|
0x76, // push0 (save description)
|
||||||
0x38, 0xff, 0xff, // pushi -1
|
0x89, 0x1b, // lsg global[27] (game version)
|
||||||
0x76, // push0
|
0x43, 0xff, 0x08, 0x00, // callk kSaveGame (0xFF will be overwritten by patcher)
|
||||||
0x43, 0xff, 0x08, 0x00, // callk kSave (will get changed afterwards)
|
0x48, // ret
|
||||||
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) {
|
static void patchGameSaveRestoreCode(SegManager *segMan, reg_t methodAddress, byte id) {
|
||||||
Script *script = segMan->getScript(methodAddress.getSegment());
|
Script *script = segMan->getScript(methodAddress.getSegment());
|
||||||
byte *patchPtr = const_cast<byte *>(script->getBuf(methodAddress.getOffset()));
|
byte *patchPtr = const_cast<byte *>(script->getBuf(methodAddress.getOffset()));
|
||||||
|
|
||||||
if (getSciVersion() <= SCI_VERSION_1_1) {
|
memcpy(patchPtr, patchGameRestoreSave, sizeof(patchGameRestoreSave));
|
||||||
memcpy(patchPtr, patchGameRestoreSave, sizeof(patchGameRestoreSave));
|
|
||||||
} else { // SCI2+
|
|
||||||
memcpy(patchPtr, patchGameRestoreSaveSci2, sizeof(patchGameRestoreSaveSci2));
|
|
||||||
|
|
||||||
if (g_sci->isBE()) {
|
|
||||||
// LE -> BE
|
|
||||||
patchPtr[9] = 0x00;
|
|
||||||
patchPtr[10] = 0x06;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
patchPtr[8] = id;
|
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());
|
Script *script = segMan->getScript(methodAddress.getSegment());
|
||||||
byte *patchPtr = const_cast<byte *>(script->getBuf(methodAddress.getOffset()));
|
byte *patchPtr = const_cast<byte *>(script->getBuf(methodAddress.getOffset()));
|
||||||
memcpy(patchPtr, patchGameRestoreSaveSci21, sizeof(patchGameRestoreSaveSci21));
|
int kcallOffset;
|
||||||
|
|
||||||
if (doRestore)
|
if (getSciVersion() < SCI_VERSION_2_1_MIDDLE) {
|
||||||
patchPtr[2] = 0x78; // push1
|
if (doRestore) {
|
||||||
|
memcpy(patchPtr, patchGameRestoreSci2, sizeof(patchGameRestoreSci2));
|
||||||
if (g_sci->isBE()) {
|
kcallOffset = 9;
|
||||||
// LE -> BE
|
} else {
|
||||||
patchPtr[10] = 0x00;
|
memcpy(patchPtr, patchGameSaveSci2, sizeof(patchGameSaveSci2));
|
||||||
patchPtr[11] = 0x08;
|
kcallOffset = 10;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (doRestore) {
|
||||||
|
memcpy(patchPtr, patchGameRestoreSci21, sizeof(patchGameRestoreSci21));
|
||||||
|
kcallOffset = 10;
|
||||||
|
} else {
|
||||||
|
memcpy(patchPtr, patchGameSaveSci21, sizeof(patchGameSaveSci21));
|
||||||
|
kcallOffset = 11;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
patchPtr[9] = id;
|
patchPtr[kcallOffset] = id;
|
||||||
|
if (g_sci->isBE()) {
|
||||||
|
SWAP(patchPtr[kcallOffset + 1], patchPtr[kcallOffset + 2]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void SciEngine::patchGameSaveRestore() {
|
void SciEngine::patchGameSaveRestore() {
|
||||||
SegManager *segMan = _gamestate->_segMan;
|
SegManager *segMan = _gamestate->_segMan;
|
||||||
|
@ -595,17 +620,21 @@ void SciEngine::patchGameSaveRestore() {
|
||||||
uint16 selectorId = gameSuperObject->getFuncSelector(methodNr);
|
uint16 selectorId = gameSuperObject->getFuncSelector(methodNr);
|
||||||
Common::String methodName = _kernel->getSelectorName(selectorId);
|
Common::String methodName = _kernel->getSelectorName(selectorId);
|
||||||
if (methodName == "restore") {
|
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);
|
patchGameSaveRestoreCode(segMan, gameSuperObject->getFunction(methodNr), kernelIdRestore);
|
||||||
else
|
|
||||||
patchGameSaveRestoreCodeSci21(segMan, gameSuperObject->getFunction(methodNr), kernelIdRestore, true);
|
|
||||||
}
|
}
|
||||||
else if (methodName == "save") {
|
else if (methodName == "save") {
|
||||||
if (_gameId != GID_FAIRYTALES) { // Fairy Tales saves automatically without a dialog
|
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);
|
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);
|
Common::String methodName = _kernel->getSelectorName(selectorId);
|
||||||
if (methodName == "save") {
|
if (methodName == "save") {
|
||||||
if (_gameId != GID_FAIRYTALES) { // Fairy Tales saves automatically without a dialog
|
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);
|
patchGameSaveRestoreCode(segMan, patchObjectSave->getFunction(methodNr), kernelIdSave);
|
||||||
else
|
|
||||||
patchGameSaveRestoreCodeSci21(segMan, patchObjectSave->getFunction(methodNr), kernelIdSave, false);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue