[Scummvm-cvs-logs] scummvm master -> 045613af95b84b6a57526125da35b949d0f431ea

bluegr md5 at scummvm.org
Wed Jun 13 11:56:31 CEST 2012


This automated email contains information about 1 new commit which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
045613af95 SCI: Shuffle the kernel functions inside kfile.cpp


Commit: 045613af95b84b6a57526125da35b949d0f431ea
    https://github.com/scummvm/scummvm/commit/045613af95b84b6a57526125da35b949d0f431ea
Author: Filippos Karapetis (md5 at scummvm.org)
Date: 2012-06-13T02:55:07-07:00

Commit Message:
SCI: Shuffle the kernel functions inside kfile.cpp

This puts them in the order that they are defined in the kernel tables

Changed paths:
    engines/sci/engine/kfile.cpp



diff --git a/engines/sci/engine/kfile.cpp b/engines/sci/engine/kfile.cpp
index 0523329..445a019 100644
--- a/engines/sci/engine/kfile.cpp
+++ b/engines/sci/engine/kfile.cpp
@@ -152,18 +152,6 @@ reg_t kDeviceInfo(EngineState *s, int argc, reg_t *argv) {
 	return s->r_acc;
 }
 
-reg_t kGetSaveDir(EngineState *s, int argc, reg_t *argv) {
-#ifdef ENABLE_SCI32
-	// SCI32 uses a parameter here. It is used to modify a string, stored in a
-	// global variable, so that game scripts store the save directory. We
-	// don't really set a save game directory, thus not setting the string to
-	// anything is the correct thing to do here.
-	//if (argc > 0)
-	//	warning("kGetSaveDir called with %d parameter(s): %04x:%04x", argc, PRINT_REG(argv[0]));
-#endif
-		return s->_segMan->getSaveDirPtr();
-}
-
 reg_t kCheckFreeSpace(EngineState *s, int argc, reg_t *argv) {
 	if (argc > 1) {
 		// SCI1.1/SCI32
@@ -194,292 +182,187 @@ reg_t kCheckFreeSpace(EngineState *s, int argc, reg_t *argv) {
 	return make_reg(0, 1);
 }
 
-reg_t kCheckSaveGame(EngineState *s, int argc, reg_t *argv) {
-	Common::String game_id = s->_segMan->getString(argv[0]);
-	uint16 virtualId = argv[1].toUint16();
-
-	debug(3, "kCheckSaveGame(%s, %d)", game_id.c_str(), virtualId);
+reg_t kValidPath(EngineState *s, int argc, reg_t *argv) {
+	Common::String path = s->_segMan->getString(argv[0]);
 
-	Common::Array<SavegameDesc> saves;
-	listSavegames(saves);
+	debug(3, "kValidPath(%s) -> %d", path.c_str(), s->r_acc.offset);
 
-	// we allow 0 (happens in QfG2 when trying to restore from an empty saved game list) and return false in that case
-	if (virtualId == 0)
-		return NULL_REG;
+	// Always return true
+	return make_reg(0, 1);
+}
 
-	// Find saved-game
-	if ((virtualId < SAVEGAMEID_OFFICIALRANGE_START) || (virtualId > SAVEGAMEID_OFFICIALRANGE_END))
-		error("kCheckSaveGame: called with invalid savegameId");
-	uint savegameId = virtualId - SAVEGAMEID_OFFICIALRANGE_START;
-	int savegameNr = findSavegame(saves, savegameId);
-	if (savegameNr == -1)
-		return NULL_REG;
+#ifdef ENABLE_SCI32
 
-	// Check for compatible savegame version
-	int ver = saves[savegameNr].version;
-	if (ver < MINIMUM_SAVEGAME_VERSION || ver > CURRENT_SAVEGAME_VERSION)
-		return NULL_REG;
+reg_t kCD(EngineState *s, int argc, reg_t *argv) {
+	// TODO: Stub
+	switch (argv[0].toUint16()) {
+	case 0:
+		// Return whether the contents of disc argv[1] is available.
+		return TRUE_REG;
+	default:
+		warning("CD(%d)", argv[0].toUint16());
+	}
 
-	// Otherwise we assume the savegame is OK
-	return TRUE_REG;
+	return NULL_REG;
 }
 
-reg_t kGetSaveFiles(EngineState *s, int argc, reg_t *argv) {
-	Common::String game_id = s->_segMan->getString(argv[0]);
-
-	debug(3, "kGetSaveFiles(%s)", game_id.c_str());
+#endif
 
-	// Scripts ask for current save files, we can assume that if afterwards they ask us to create a new slot they really
-	//  mean new slot instead of overwriting the old one
-	s->_lastSaveVirtualId = SAVEGAMEID_OFFICIALRANGE_START;
+// ---- FileIO operations -----------------------------------------------------
 
-	Common::Array<SavegameDesc> saves;
-	listSavegames(saves);
-	uint totalSaves = MIN<uint>(saves.size(), MAX_SAVEGAME_NR);
+reg_t kFileIO(EngineState *s, int argc, reg_t *argv) {
+	if (!s)
+		return make_reg(0, getSciVersion());
+	error("not supposed to call this");
+}
 
-	reg_t *slot = s->_segMan->derefRegPtr(argv[2], totalSaves);
+reg_t kFileIOOpen(EngineState *s, int argc, reg_t *argv) {
+	Common::String name = s->_segMan->getString(argv[0]);
 
-	if (!slot) {
-		warning("kGetSaveFiles: %04X:%04X invalid or too small to hold slot data", PRINT_REG(argv[2]));
-		totalSaves = 0;
+#ifdef ENABLE_SCI32
+	if (name == PHANTASMAGORIA_SAVEGAME_INDEX) {
+		if (s->_virtualIndexFile) {
+			return make_reg(0, VIRTUALFILE_HANDLE);
+		} else {
+			Common::String englishName = g_sci->getSciLanguageString(name, K_LANG_ENGLISH);
+			Common::String wrappedName = g_sci->wrapFilename(englishName);
+			if (!g_sci->getSaveFileManager()->listSavefiles(wrappedName).empty()) {
+				s->_virtualIndexFile = new VirtualIndexFile(wrappedName);
+				return make_reg(0, VIRTUALFILE_HANDLE);
+			}
+		}
 	}
+#endif
 
-	const uint bufSize = (totalSaves * SCI_MAX_SAVENAME_LENGTH) + 1;
-	char *saveNames = new char[bufSize];
-	char *saveNamePtr = saveNames;
+	// SCI32 can call K_FILEIO_OPEN with only one argument. It seems to
+	// just be checking if it exists.
+	int mode = (argc < 2) ? (int)_K_FILE_MODE_OPEN_OR_FAIL : argv[1].toUint16();
+	bool unwrapFilename = true;
 
-	for (uint i = 0; i < totalSaves; i++) {
-		*slot++ = make_reg(0, saves[i].id + SAVEGAMEID_OFFICIALRANGE_START); // Store the virtual savegame ID ffs. see above
-		strcpy(saveNamePtr, saves[i].name);
-		saveNamePtr += SCI_MAX_SAVENAME_LENGTH;
+	// SQ4 floppy prepends /\ to the filenames
+	if (name.hasPrefix("/\\")) {
+		name.deleteChar(0);
+		name.deleteChar(0);
 	}
 
-	*saveNamePtr = 0; // Terminate list
+	// SQ4 floppy attempts to update the savegame index file sq4sg.dir when
+	// deleting saved games. We don't use an index file for saving or loading,
+	// so just stop the game from modifying the file here in order to avoid
+	// having it saved in the ScummVM save directory.
+	if (name == "sq4sg.dir") {
+		debugC(kDebugLevelFile, "Not opening unused file sq4sg.dir");
+		return SIGNAL_REG;
+	}
 
-	s->_segMan->memcpy(argv[1], (byte *)saveNames, bufSize);
-	delete[] saveNames;
+	if (name.empty()) {
+		// Happens many times during KQ1 (e.g. when typing something)
+		debugC(kDebugLevelFile, "Attempted to open a file with an empty filename");
+		return SIGNAL_REG;
+	}
+	debugC(kDebugLevelFile, "kFileIO(open): %s, 0x%x", name.c_str(), mode);
 
-	return make_reg(0, totalSaves);
+	// QFG import rooms get a virtual filelisting instead of an actual one
+	if (g_sci->inQfGImportRoom()) {
+		// We need to find out what the user actually selected, "savedHeroes" is
+		// already destroyed when we get here. That's why we need to remember
+		// selection via kDrawControl.
+		name = s->_dirseeker.getVirtualFilename(s->_chosenQfGImportItem);
+		unwrapFilename = false;
+	}
+
+	return file_open(s, name, mode, unwrapFilename);
 }
 
-reg_t kSaveGame(EngineState *s, int argc, reg_t *argv) {
-	Common::String game_id;
- 	int16 virtualId = argv[1].toSint16();
-	int16 savegameId = -1;
-	Common::String game_description;
-	Common::String version;
+reg_t kFileIOClose(EngineState *s, int argc, reg_t *argv) {
+	debugC(kDebugLevelFile, "kFileIO(close): %d", argv[0].toUint16());
 
-	if (argc > 3)
-		version = s->_segMan->getString(argv[3]);
+	if (argv[0] == SIGNAL_REG)
+		return s->r_acc;
+	
+	uint16 handle = argv[0].toUint16();
 
-	// We check here, we don't want to delete a users save in case we are within a kernel function
-	if (s->executionStackBase) {
-		warning("kSaveGame - won't save from within kernel function");
-		return NULL_REG;
+#ifdef ENABLE_SCI32
+	if (handle == VIRTUALFILE_HANDLE) {
+		s->_virtualIndexFile->close();
+		return SIGNAL_REG;
 	}
+#endif
 
-	if (argv[0].isNull()) {
-		// Direct call, from a patched Game::save
-		if ((argv[1] != SIGNAL_REG) || (!argv[2].isNull()))
-			error("kSaveGame: assumed patched call isn't accurate");
-
-		// we are supposed to show a dialog for the user and let him choose where to save
-		g_sci->_soundCmd->pauseAll(true); // pause music
-		GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Save game:"), _("Save"), true);
-		savegameId = dialog->runModalWithCurrentTarget();
-		game_description = dialog->getResultString();
-		if (game_description.empty()) {
-			// create our own description for the saved game, the user didnt enter it
-			game_description = dialog->createDefaultSaveDescription(savegameId);
-		}
-		delete dialog;
-		g_sci->_soundCmd->pauseAll(false); // unpause music ( we can't have it paused during save)
-		if (savegameId < 0)
-			return NULL_REG;
-
-	} else {
-		// Real call from script
-		game_id = s->_segMan->getString(argv[0]);
-		if (argv[2].isNull())
-			error("kSaveGame: called with description being NULL");
-		game_description = s->_segMan->getString(argv[2]);
-
-		debug(3, "kSaveGame(%s,%d,%s,%s)", game_id.c_str(), virtualId, game_description.c_str(), version.c_str());
+	FileHandle *f = getFileFromHandle(s, handle);
+	if (f) {
+		f->close();
+		if (getSciVersion() <= SCI_VERSION_0_LATE)
+			return s->r_acc;	// SCI0 semantics: no value returned
+		return SIGNAL_REG;
+	}
 
-		Common::Array<SavegameDesc> saves;
-		listSavegames(saves);
+	if (getSciVersion() <= SCI_VERSION_0_LATE)
+		return s->r_acc;	// SCI0 semantics: no value returned
+	return NULL_REG;
+}
 
-		if ((virtualId >= SAVEGAMEID_OFFICIALRANGE_START) && (virtualId <= SAVEGAMEID_OFFICIALRANGE_END)) {
-			// savegameId is an actual Id, so search for it just to make sure
-			savegameId = virtualId - SAVEGAMEID_OFFICIALRANGE_START;
-			if (findSavegame(saves, savegameId) == -1)
-				return NULL_REG;
-		} else if (virtualId < SAVEGAMEID_OFFICIALRANGE_START) {
-			// virtualId is low, we assume that scripts expect us to create new slot
-			if (virtualId == s->_lastSaveVirtualId) {
-				// if last virtual id is the same as this one, we assume that caller wants to overwrite last save
-				savegameId = s->_lastSaveNewId;
-			} else {
-				uint savegameNr;
-				// savegameId is in lower range, scripts expect us to create a new slot
-				for (savegameId = 0; savegameId < SAVEGAMEID_OFFICIALRANGE_START; savegameId++) {
-					for (savegameNr = 0; savegameNr < saves.size(); savegameNr++) {
-						if (savegameId == saves[savegameNr].id)
-							break;
-					}
-					if (savegameNr == saves.size())
-						break;
-				}
-				if (savegameId == SAVEGAMEID_OFFICIALRANGE_START)
-					error("kSavegame: no more savegame slots available");
-			}
-		} else {
-			error("kSaveGame: invalid savegameId used");
-		}
+reg_t kFileIOReadRaw(EngineState *s, int argc, reg_t *argv) {
+	uint16 handle = argv[0].toUint16();
+	uint16 size = argv[2].toUint16();
+	int bytesRead = 0;
+	char *buf = new char[size];
+	debugC(kDebugLevelFile, "kFileIO(readRaw): %d, %d", handle, size);
 
-		// Save in case caller wants to overwrite last newly created save
-		s->_lastSaveVirtualId = virtualId;
-		s->_lastSaveNewId = savegameId;
+#ifdef ENABLE_SCI32
+	if (handle == VIRTUALFILE_HANDLE) {
+		bytesRead = s->_virtualIndexFile->read(buf, size);
+	} else {
+#endif
+		FileHandle *f = getFileFromHandle(s, handle);
+		if (f)
+			bytesRead = f->_in->read(buf, size);
+#ifdef ENABLE_SCI32
 	}
+#endif
 
-	s->r_acc = NULL_REG;
+	// TODO: What happens if less bytes are read than what has
+	// been requested? (i.e. if bytesRead is non-zero, but still
+	// less than size)
+	if (bytesRead > 0)
+		s->_segMan->memcpy(argv[1], (const byte*)buf, size);
 
-	Common::String filename = g_sci->getSavegameName(savegameId);
-	Common::SaveFileManager *saveFileMan = g_sci->getSaveFileManager();
-	Common::OutSaveFile *out;
+	delete[] buf;
+	return make_reg(0, bytesRead);
+}
 
-	out = saveFileMan->openForSaving(filename);
-	if (!out) {
-		warning("Error opening savegame \"%s\" for writing", filename.c_str());
-	} else {
-		if (!gamestate_save(s, out, game_description, version)) {
-			warning("Saving the game failed");
-		} else {
-			s->r_acc = TRUE_REG; // save successful
-		}
-
-		out->finalize();
-		if (out->err()) {
-			warning("Writing the savegame failed");
-			s->r_acc = NULL_REG; // write failure
-		}
-		delete out;
-	}
-
-	return s->r_acc;
-}
-
-reg_t kRestoreGame(EngineState *s, int argc, reg_t *argv) {
-	Common::String game_id = !argv[0].isNull() ? s->_segMan->getString(argv[0]) : "";
-	int16 savegameId = argv[1].toSint16();
-	bool pausedMusic = false;
-
-	debug(3, "kRestoreGame(%s,%d)", game_id.c_str(), savegameId);
-
-	if (argv[0].isNull()) {
-		// Direct call, either from launcher or from a patched Game::restore
-		if (savegameId == -1) {
-			// we are supposed to show a dialog for the user and let him choose a saved game
-			g_sci->_soundCmd->pauseAll(true); // pause music
-			GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Restore game:"), _("Restore"), false);
-			savegameId = dialog->runModalWithCurrentTarget();
-			delete dialog;
-			if (savegameId < 0) {
-				g_sci->_soundCmd->pauseAll(false); // unpause music
-				return s->r_acc;
-			}
-			pausedMusic = true;
-		}
-		// don't adjust ID of the saved game, it's already correct
-	} else {
-		if (argv[2].isNull())
-			error("kRestoreGame: called with parameter 2 being NULL");
-		// Real call from script, we need to adjust ID
-		if ((savegameId < SAVEGAMEID_OFFICIALRANGE_START) || (savegameId > SAVEGAMEID_OFFICIALRANGE_END)) {
-			warning("Savegame ID %d is not allowed", savegameId);
-			return TRUE_REG;
-		}
-		savegameId -= SAVEGAMEID_OFFICIALRANGE_START;
-	}
-
-	s->r_acc = NULL_REG; // signals success
+reg_t kFileIOWriteRaw(EngineState *s, int argc, reg_t *argv) {
+	uint16 handle = argv[0].toUint16();
+	uint16 size = argv[2].toUint16();
+	char *buf = new char[size];
+	bool success = false;
+	s->_segMan->memcpy((byte *)buf, argv[1], size);
+	debugC(kDebugLevelFile, "kFileIO(writeRaw): %d, %d", handle, size);
 
-	Common::Array<SavegameDesc> saves;
-	listSavegames(saves);
-	if (findSavegame(saves, savegameId) == -1) {
-		s->r_acc = TRUE_REG;
-		warning("Savegame ID %d not found", savegameId);
+#ifdef ENABLE_SCI32
+	if (handle == VIRTUALFILE_HANDLE) {
+		s->_virtualIndexFile->write(buf, size);
+		success = true;
 	} else {
-		Common::SaveFileManager *saveFileMan = g_sci->getSaveFileManager();
-		Common::String filename = g_sci->getSavegameName(savegameId);
-		Common::SeekableReadStream *in;
-
-		in = saveFileMan->openForLoading(filename);
-		if (in) {
-			// found a savegame file
-
-			gamestate_restore(s, in);
-			delete in;
-
-			if (g_sci->getGameId() == GID_MOTHERGOOSE256) {
-				// WORKAROUND: Mother Goose SCI1/SCI1.1 does some weird things for
-				//  saving a previously restored game.
-				// We set the current savedgame-id directly and remove the script
-				//  code concerning this via script patch.
-				s->variables[VAR_GLOBAL][0xB3].offset = SAVEGAMEID_OFFICIALRANGE_START + savegameId;
-			}
-		} else {
-			s->r_acc = TRUE_REG;
-			warning("Savegame #%d not found", savegameId);
+#endif
+		FileHandle *f = getFileFromHandle(s, handle);
+		if (f) {
+			f->_out->write(buf, size);
+			success = true;
 		}
+#ifdef ENABLE_SCI32
 	}
+#endif
 
-	if (!s->r_acc.isNull()) {
-		// no success?
-		if (pausedMusic)
-			g_sci->_soundCmd->pauseAll(false); // unpause music
-	}
-
-	return s->r_acc;
-}
-
-reg_t kValidPath(EngineState *s, int argc, reg_t *argv) {
-	Common::String path = s->_segMan->getString(argv[0]);
-
-	debug(3, "kValidPath(%s) -> %d", path.c_str(), s->r_acc.offset);
-
-	// Always return true
-	return make_reg(0, 1);
-}
-
-reg_t kFileIO(EngineState *s, int argc, reg_t *argv) {
-	if (!s)
-		return make_reg(0, getSciVersion());
-	error("not supposed to call this");
+	delete[] buf;
+	if (success)
+		return NULL_REG;
+	return make_reg(0, 6); // DOS - invalid handle
 }
 
-reg_t kFileIOOpen(EngineState *s, int argc, reg_t *argv) {
+reg_t kFileIOUnlink(EngineState *s, int argc, reg_t *argv) {
 	Common::String name = s->_segMan->getString(argv[0]);
-
-#ifdef ENABLE_SCI32
-	if (name == PHANTASMAGORIA_SAVEGAME_INDEX) {
-		if (s->_virtualIndexFile) {
-			return make_reg(0, VIRTUALFILE_HANDLE);
-		} else {
-			Common::String englishName = g_sci->getSciLanguageString(name, K_LANG_ENGLISH);
-			Common::String wrappedName = g_sci->wrapFilename(englishName);
-			if (!g_sci->getSaveFileManager()->listSavefiles(wrappedName).empty()) {
-				s->_virtualIndexFile = new VirtualIndexFile(wrappedName);
-				return make_reg(0, VIRTUALFILE_HANDLE);
-			}
-		}
-	}
-#endif
-
-	// SCI32 can call K_FILEIO_OPEN with only one argument. It seems to
-	// just be checking if it exists.
-	int mode = (argc < 2) ? (int)_K_FILE_MODE_OPEN_OR_FAIL : argv[1].toUint16();
-	bool unwrapFilename = true;
+	Common::SaveFileManager *saveFileMan = g_sci->getSaveFileManager();
+	bool result;
 
 	// SQ4 floppy prepends /\ to the filenames
 	if (name.hasPrefix("/\\")) {
@@ -487,407 +370,546 @@ reg_t kFileIOOpen(EngineState *s, int argc, reg_t *argv) {
 		name.deleteChar(0);
 	}
 
-	// SQ4 floppy attempts to update the savegame index file sq4sg.dir when
-	// deleting saved games. We don't use an index file for saving or loading,
-	// so just stop the game from modifying the file here in order to avoid
-	// having it saved in the ScummVM save directory.
-	if (name == "sq4sg.dir") {
-		debugC(kDebugLevelFile, "Not opening unused file sq4sg.dir");
-		return SIGNAL_REG;
-	}
-
-	if (name.empty()) {
-		// Happens many times during KQ1 (e.g. when typing something)
-		debugC(kDebugLevelFile, "Attempted to open a file with an empty filename");
-		return SIGNAL_REG;
-	}
-	debugC(kDebugLevelFile, "kFileIO(open): %s, 0x%x", name.c_str(), mode);
+	// Special case for SQ4 floppy: This game has hardcoded names for all of
+	// its savegames, and they are all named "sq4sg.xxx", where xxx is the
+	// slot. We just take the slot number here, and delete the appropriate
+	// save game.
+	if (name.hasPrefix("sq4sg.")) {
+		// Special handling for SQ4... get the slot number and construct the
+		// save game name.
+		int slotNum = atoi(name.c_str() + name.size() - 3);
+		Common::Array<SavegameDesc> saves;
+		listSavegames(saves);
+		int savedir_nr = saves[slotNum].id;
+		name = g_sci->getSavegameName(savedir_nr);
+		result = saveFileMan->removeSavefile(name);
+	} else if (getSciVersion() >= SCI_VERSION_2) {
+		// The file name may be already wrapped, so check both cases
+		result = saveFileMan->removeSavefile(name);
+		if (!result) {
+			const Common::String wrappedName = g_sci->wrapFilename(name);
+			result = saveFileMan->removeSavefile(wrappedName);
+		}
 
-	// QFG import rooms get a virtual filelisting instead of an actual one
-	if (g_sci->inQfGImportRoom()) {
-		// We need to find out what the user actually selected, "savedHeroes" is
-		// already destroyed when we get here. That's why we need to remember
-		// selection via kDrawControl.
-		name = s->_dirseeker.getVirtualFilename(s->_chosenQfGImportItem);
-		unwrapFilename = false;
+#ifdef ENABLE_SCI32
+		if (name == PHANTASMAGORIA_SAVEGAME_INDEX) {
+			delete s->_virtualIndexFile;
+			s->_virtualIndexFile = 0;
+		}
+#endif
+	} else {
+		const Common::String wrappedName = g_sci->wrapFilename(name);
+		result = saveFileMan->removeSavefile(wrappedName);
 	}
 
-	return file_open(s, name, mode, unwrapFilename);
+	debugC(kDebugLevelFile, "kFileIO(unlink): %s", name.c_str());
+	if (result)
+		return NULL_REG;
+	return make_reg(0, 2); // DOS - file not found error code
 }
 
-reg_t kFileIOClose(EngineState *s, int argc, reg_t *argv) {
-	debugC(kDebugLevelFile, "kFileIO(close): %d", argv[0].toUint16());
+reg_t kFileIOReadString(EngineState *s, int argc, reg_t *argv) {
+	uint16 maxsize = argv[1].toUint16();
+	char *buf = new char[maxsize];
+	uint16 handle = argv[2].toUint16();
+	debugC(kDebugLevelFile, "kFileIO(readString): %d, %d", handle, maxsize);
+	uint32 bytesRead;
 
-	if (argv[0] == SIGNAL_REG)
-		return s->r_acc;
-	
-	uint16 handle = argv[0].toUint16();
+#ifdef ENABLE_SCI32
+	if (handle == VIRTUALFILE_HANDLE)
+		bytesRead = s->_virtualIndexFile->readLine(buf, maxsize);
+	else
+#endif
+		bytesRead = fgets_wrapper(s, buf, maxsize, handle);
+
+	s->_segMan->memcpy(argv[0], (const byte*)buf, maxsize);
+	delete[] buf;
+	return bytesRead ? argv[0] : NULL_REG;
+}
+
+reg_t kFileIOWriteString(EngineState *s, int argc, reg_t *argv) {
+	int handle = argv[0].toUint16();
+	Common::String str = s->_segMan->getString(argv[1]);
+	debugC(kDebugLevelFile, "kFileIO(writeString): %d", handle);
 
 #ifdef ENABLE_SCI32
 	if (handle == VIRTUALFILE_HANDLE) {
-		s->_virtualIndexFile->close();
-		return SIGNAL_REG;
+		s->_virtualIndexFile->write(str.c_str(), str.size());
+		return NULL_REG;
 	}
 #endif
 
 	FileHandle *f = getFileFromHandle(s, handle);
+
 	if (f) {
-		f->close();
+		f->_out->write(str.c_str(), str.size());
 		if (getSciVersion() <= SCI_VERSION_0_LATE)
 			return s->r_acc;	// SCI0 semantics: no value returned
-		return SIGNAL_REG;
+		return NULL_REG;
 	}
 
 	if (getSciVersion() <= SCI_VERSION_0_LATE)
 		return s->r_acc;	// SCI0 semantics: no value returned
-	return NULL_REG;
+	return make_reg(0, 6); // DOS - invalid handle
 }
 
-reg_t kFileIOReadRaw(EngineState *s, int argc, reg_t *argv) {
+reg_t kFileIOSeek(EngineState *s, int argc, reg_t *argv) {
 	uint16 handle = argv[0].toUint16();
-	uint16 size = argv[2].toUint16();
-	int bytesRead = 0;
-	char *buf = new char[size];
-	debugC(kDebugLevelFile, "kFileIO(readRaw): %d, %d", handle, size);
+	uint16 offset = ABS<int16>(argv[1].toSint16());	// can be negative
+	uint16 whence = argv[2].toUint16();
+	debugC(kDebugLevelFile, "kFileIO(seek): %d, %d, %d", handle, offset, whence);
 
 #ifdef ENABLE_SCI32
-	if (handle == VIRTUALFILE_HANDLE) {
-		bytesRead = s->_virtualIndexFile->read(buf, size);
-	} else {
+	if (handle == VIRTUALFILE_HANDLE)
+		return make_reg(0, s->_virtualIndexFile->seek(offset, whence));
 #endif
-		FileHandle *f = getFileFromHandle(s, handle);
-		if (f)
-			bytesRead = f->_in->read(buf, size);
-#ifdef ENABLE_SCI32
+
+	FileHandle *f = getFileFromHandle(s, handle);
+
+	if (f && f->_in) {
+		// Backward seeking isn't supported in zip file streams, thus adapt the
+		// parameters accordingly if games ask for such a seek mode. A known
+		// case where this is requested is the save file manager in Phantasmagoria
+		if (whence == SEEK_END) {
+			whence = SEEK_SET;
+			offset = f->_in->size() - offset;
+		}
+
+		return make_reg(0, f->_in->seek(offset, whence));
+	} else if (f && f->_out) {
+		error("kFileIOSeek: Unsupported seek operation on a writeable stream (offset: %d, whence: %d)", offset, whence);
 	}
-#endif
 
-	// TODO: What happens if less bytes are read than what has
-	// been requested? (i.e. if bytesRead is non-zero, but still
-	// less than size)
-	if (bytesRead > 0)
-		s->_segMan->memcpy(argv[1], (const byte*)buf, size);
+	return SIGNAL_REG;
+}
 
-	delete[] buf;
-	return make_reg(0, bytesRead);
+reg_t kFileIOFindFirst(EngineState *s, int argc, reg_t *argv) {
+	Common::String mask = s->_segMan->getString(argv[0]);
+	reg_t buf = argv[1];
+	int attr = argv[2].toUint16(); // We won't use this, Win32 might, though...
+	debugC(kDebugLevelFile, "kFileIO(findFirst): %s, 0x%x", mask.c_str(), attr);
+
+	// We remove ".*". mask will get prefixed, so we will return all additional files for that gameid
+	if (mask == "*.*")
+		mask = "*";
+	return s->_dirseeker.firstFile(mask, buf, s->_segMan);
 }
 
-reg_t kFileIOWriteRaw(EngineState *s, int argc, reg_t *argv) {
-	uint16 handle = argv[0].toUint16();
-	uint16 size = argv[2].toUint16();
-	char *buf = new char[size];
-	bool success = false;
-	s->_segMan->memcpy((byte *)buf, argv[1], size);
-	debugC(kDebugLevelFile, "kFileIO(writeRaw): %d, %d", handle, size);
+reg_t kFileIOFindNext(EngineState *s, int argc, reg_t *argv) {
+	debugC(kDebugLevelFile, "kFileIO(findNext)");
+	return s->_dirseeker.nextFile(s->_segMan);
+}
+
+reg_t kFileIOExists(EngineState *s, int argc, reg_t *argv) {
+	Common::String name = s->_segMan->getString(argv[0]);
 
 #ifdef ENABLE_SCI32
-	if (handle == VIRTUALFILE_HANDLE) {
-		s->_virtualIndexFile->write(buf, size);
-		success = true;
-	} else {
+	// Cache the file existence result for the Phantasmagoria
+	// save index file, as the game scripts keep checking for
+	// its existence.
+	if (name == PHANTASMAGORIA_SAVEGAME_INDEX && s->_virtualIndexFile)
+		return TRUE_REG;
 #endif
-		FileHandle *f = getFileFromHandle(s, handle);
-		if (f) {
-			f->_out->write(buf, size);
-			success = true;
-		}
-#ifdef ENABLE_SCI32
+
+	bool exists = false;
+
+	// Check for regular file
+	exists = Common::File::exists(name);
+
+	// Check for a savegame with the name
+	Common::SaveFileManager *saveFileMan = g_sci->getSaveFileManager();
+	if (!exists)
+		exists = !saveFileMan->listSavefiles(name).empty();
+
+	// Try searching for the file prepending "target-"
+	const Common::String wrappedName = g_sci->wrapFilename(name);
+	if (!exists) {
+		exists = !saveFileMan->listSavefiles(wrappedName).empty();
 	}
-#endif
 
-	delete[] buf;
-	if (success)
-		return NULL_REG;
-	return make_reg(0, 6); // DOS - invalid handle
-}
+	// SCI2+ debug mode
+	if (DebugMan.isDebugChannelEnabled(kDebugLevelDebugMode)) {
+		if (!exists && name == "1.scr")		// PQ4
+			exists = true;
+		if (!exists && name == "18.scr")	// QFG4
+			exists = true;
+		if (!exists && name == "99.scr")	// GK1, KQ7
+			exists = true;
+		if (!exists && name == "classes")	// GK2, SQ6, LSL7
+			exists = true;
+	}
 
-reg_t kFileIOReadString(EngineState *s, int argc, reg_t *argv) {
-	uint16 maxsize = argv[1].toUint16();
-	char *buf = new char[maxsize];
-	uint16 handle = argv[2].toUint16();
-	debugC(kDebugLevelFile, "kFileIO(readString): %d, %d", handle, maxsize);
-	uint32 bytesRead;
+	// Special case for non-English versions of LSL5: The English version of
+	// LSL5 calls kFileIO(), case K_FILEIO_OPEN for reading to check if
+	// memory.drv exists (which is where the game's password is stored). If
+	// it's not found, it calls kFileIO() again, case K_FILEIO_OPEN for
+	// writing and creates a new file. Non-English versions call kFileIO(),
+	// case K_FILEIO_FILE_EXISTS instead, and fail if memory.drv can't be
+	// found. We create a default memory.drv file with no password, so that
+	// the game can continue.
+	if (!exists && name == "memory.drv") {
+		// Create a new file, and write the bytes for the empty password
+		// string inside
+		byte defaultContent[] = { 0xE9, 0xE9, 0xEB, 0xE1, 0x0D, 0x0A, 0x31, 0x30, 0x30, 0x30 };
+		Common::WriteStream *outFile = saveFileMan->openForSaving(wrappedName);
+		for (int i = 0; i < 10; i++)
+			outFile->writeByte(defaultContent[i]);
+		outFile->finalize();
+		exists = !outFile->err();	// check whether we managed to create the file.
+		delete outFile;
+	}
 
-#ifdef ENABLE_SCI32
-	if (handle == VIRTUALFILE_HANDLE)
-		bytesRead = s->_virtualIndexFile->readLine(buf, maxsize);
-	else
-#endif
-		bytesRead = fgets_wrapper(s, buf, maxsize, handle);
+	// Special case for KQ6 Mac: The game checks for two video files to see
+	// if they exist before it plays them. Since we support multiple naming
+	// schemes for resource fork files, we also need to support that here in
+	// case someone has a "HalfDome.bin" file, etc. 
+	if (!exists && g_sci->getGameId() == GID_KQ6 && g_sci->getPlatform() == Common::kPlatformMacintosh &&
+			(name == "HalfDome" || name == "Kq6Movie"))
+		exists = Common::MacResManager::exists(name);
 
-	s->_segMan->memcpy(argv[0], (const byte*)buf, maxsize);
-	delete[] buf;
-	return bytesRead ? argv[0] : NULL_REG;
+	debugC(kDebugLevelFile, "kFileIO(fileExists) %s -> %d", name.c_str(), exists);
+	return make_reg(0, exists);
 }
 
-reg_t kFileIOWriteString(EngineState *s, int argc, reg_t *argv) {
-	int handle = argv[0].toUint16();
-	Common::String str = s->_segMan->getString(argv[1]);
-	debugC(kDebugLevelFile, "kFileIO(writeString): %d", handle);
+reg_t kFileIORename(EngineState *s, int argc, reg_t *argv) {
+	Common::String oldName = s->_segMan->getString(argv[0]);
+	Common::String newName = s->_segMan->getString(argv[1]);
+
+	// SCI1.1 returns 0 on success and a DOS error code on fail. SCI32
+	// returns -1 on fail. We just return -1 for all versions.
+	if (g_sci->getSaveFileManager()->renameSavefile(oldName, newName))
+		return NULL_REG;
+	else
+		return SIGNAL_REG;
+}
 
 #ifdef ENABLE_SCI32
-	if (handle == VIRTUALFILE_HANDLE) {
-		s->_virtualIndexFile->write(str.c_str(), str.size());
+reg_t kFileIOReadByte(EngineState *s, int argc, reg_t *argv) {
+	// Read the byte into the low byte of the accumulator
+	FileHandle *f = getFileFromHandle(s, argv[0].toUint16());
+	if (!f)
 		return NULL_REG;
-	}
-#endif
+	return make_reg(0, (s->r_acc.toUint16() & 0xff00) | f->_in->readByte());
+}
 
-	FileHandle *f = getFileFromHandle(s, handle);
+reg_t kFileIOWriteByte(EngineState *s, int argc, reg_t *argv) {
+	FileHandle *f = getFileFromHandle(s, argv[0].toUint16());
+	if (f)
+		f->_out->writeByte(argv[1].toUint16() & 0xff);
+	return s->r_acc; // FIXME: does this really not return anything?
+}
 
-	if (f) {
-		f->_out->write(str.c_str(), str.size());
-		if (getSciVersion() <= SCI_VERSION_0_LATE)
-			return s->r_acc;	// SCI0 semantics: no value returned
+reg_t kFileIOReadWord(EngineState *s, int argc, reg_t *argv) {
+	FileHandle *f = getFileFromHandle(s, argv[0].toUint16());
+	if (!f)
 		return NULL_REG;
-	}
+	return make_reg(0, f->_in->readUint16LE());
+}
 
-	if (getSciVersion() <= SCI_VERSION_0_LATE)
-		return s->r_acc;	// SCI0 semantics: no value returned
-	return make_reg(0, 6); // DOS - invalid handle
+reg_t kFileIOWriteWord(EngineState *s, int argc, reg_t *argv) {
+	FileHandle *f = getFileFromHandle(s, argv[0].toUint16());
+	if (f)
+		f->_out->writeUint16LE(argv[1].toUint16());
+	return s->r_acc; // FIXME: does this really not return anything?
 }
 
-reg_t kFileIOUnlink(EngineState *s, int argc, reg_t *argv) {
-	Common::String name = s->_segMan->getString(argv[0]);
-	Common::SaveFileManager *saveFileMan = g_sci->getSaveFileManager();
-	bool result;
+reg_t kFileIOCreateSaveSlot(EngineState *s, int argc, reg_t *argv) {
+	// Used in Shivers when the user enters his name on the guest book
+	// in the beginning to start the game.
 
-	// SQ4 floppy prepends /\ to the filenames
-	if (name.hasPrefix("/\\")) {
-		name.deleteChar(0);
-		name.deleteChar(0);
-	}
+	// Creates a new save slot, and returns if the operation was successful
 
-	// Special case for SQ4 floppy: This game has hardcoded names for all of
-	// its savegames, and they are all named "sq4sg.xxx", where xxx is the
-	// slot. We just take the slot number here, and delete the appropriate
-	// save game.
-	if (name.hasPrefix("sq4sg.")) {
-		// Special handling for SQ4... get the slot number and construct the
-		// save game name.
-		int slotNum = atoi(name.c_str() + name.size() - 3);
-		Common::Array<SavegameDesc> saves;
-		listSavegames(saves);
-		int savedir_nr = saves[slotNum].id;
-		name = g_sci->getSavegameName(savedir_nr);
-		result = saveFileMan->removeSavefile(name);
-	} else if (getSciVersion() >= SCI_VERSION_2) {
-		// The file name may be already wrapped, so check both cases
-		result = saveFileMan->removeSavefile(name);
-		if (!result) {
-			const Common::String wrappedName = g_sci->wrapFilename(name);
-			result = saveFileMan->removeSavefile(wrappedName);
-		}
+	// Argument 0 denotes the save slot as a negative integer, 2 means "0"
+	// Argument 1 is a string, with the file name, obtained from kSave(5).
+	// The interpreter checks if it can be written to (by checking for free
+	// disk space and write permissions)
 
-#ifdef ENABLE_SCI32
-		if (name == PHANTASMAGORIA_SAVEGAME_INDEX) {
-			delete s->_virtualIndexFile;
-			s->_virtualIndexFile = 0;
-		}
-#endif
-	} else {
-		const Common::String wrappedName = g_sci->wrapFilename(name);
-		result = saveFileMan->removeSavefile(wrappedName);
-	}
+	// We don't really use or need any of this...
 
-	debugC(kDebugLevelFile, "kFileIO(unlink): %s", name.c_str());
-	if (result)
-		return NULL_REG;
-	return make_reg(0, 2); // DOS - file not found error code
+	uint16 saveSlot = argv[0].toUint16();
+	char* fileName = s->_segMan->lookupString(argv[1])->getRawData();
+	warning("kFileIOCreateSaveSlot(%d, '%s')", saveSlot, fileName);
+
+	return TRUE_REG;	// slot creation was successful
 }
 
-reg_t kFileIOSeek(EngineState *s, int argc, reg_t *argv) {
-	uint16 handle = argv[0].toUint16();
-	uint16 offset = ABS<int16>(argv[1].toSint16());	// can be negative
-	uint16 whence = argv[2].toUint16();
-	debugC(kDebugLevelFile, "kFileIO(seek): %d, %d, %d", handle, offset, whence);
+#endif
+
+// ---- Save operations -------------------------------------------------------
 
 #ifdef ENABLE_SCI32
-	if (handle == VIRTUALFILE_HANDLE)
-		return make_reg(0, s->_virtualIndexFile->seek(offset, whence));
+
+reg_t kSave(EngineState *s, int argc, reg_t *argv) {
+	if (!s)
+		return make_reg(0, getSciVersion());
+	error("not supposed to call this");
+}
+
 #endif
 
-	FileHandle *f = getFileFromHandle(s, handle);
+reg_t kSaveGame(EngineState *s, int argc, reg_t *argv) {
+	Common::String game_id;
+ 	int16 virtualId = argv[1].toSint16();
+	int16 savegameId = -1;
+	Common::String game_description;
+	Common::String version;
 
-	if (f && f->_in) {
-		// Backward seeking isn't supported in zip file streams, thus adapt the
-		// parameters accordingly if games ask for such a seek mode. A known
-		// case where this is requested is the save file manager in Phantasmagoria
-		if (whence == SEEK_END) {
-			whence = SEEK_SET;
-			offset = f->_in->size() - offset;
+	if (argc > 3)
+		version = s->_segMan->getString(argv[3]);
+
+	// We check here, we don't want to delete a users save in case we are within a kernel function
+	if (s->executionStackBase) {
+		warning("kSaveGame - won't save from within kernel function");
+		return NULL_REG;
+	}
+
+	if (argv[0].isNull()) {
+		// Direct call, from a patched Game::save
+		if ((argv[1] != SIGNAL_REG) || (!argv[2].isNull()))
+			error("kSaveGame: assumed patched call isn't accurate");
+
+		// we are supposed to show a dialog for the user and let him choose where to save
+		g_sci->_soundCmd->pauseAll(true); // pause music
+		GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Save game:"), _("Save"), true);
+		savegameId = dialog->runModalWithCurrentTarget();
+		game_description = dialog->getResultString();
+		if (game_description.empty()) {
+			// create our own description for the saved game, the user didnt enter it
+			game_description = dialog->createDefaultSaveDescription(savegameId);
 		}
+		delete dialog;
+		g_sci->_soundCmd->pauseAll(false); // unpause music ( we can't have it paused during save)
+		if (savegameId < 0)
+			return NULL_REG;
 
-		return make_reg(0, f->_in->seek(offset, whence));
-	} else if (f && f->_out) {
-		error("kFileIOSeek: Unsupported seek operation on a writeable stream (offset: %d, whence: %d)", offset, whence);
+	} else {
+		// Real call from script
+		game_id = s->_segMan->getString(argv[0]);
+		if (argv[2].isNull())
+			error("kSaveGame: called with description being NULL");
+		game_description = s->_segMan->getString(argv[2]);
+
+		debug(3, "kSaveGame(%s,%d,%s,%s)", game_id.c_str(), virtualId, game_description.c_str(), version.c_str());
+
+		Common::Array<SavegameDesc> saves;
+		listSavegames(saves);
+
+		if ((virtualId >= SAVEGAMEID_OFFICIALRANGE_START) && (virtualId <= SAVEGAMEID_OFFICIALRANGE_END)) {
+			// savegameId is an actual Id, so search for it just to make sure
+			savegameId = virtualId - SAVEGAMEID_OFFICIALRANGE_START;
+			if (findSavegame(saves, savegameId) == -1)
+				return NULL_REG;
+		} else if (virtualId < SAVEGAMEID_OFFICIALRANGE_START) {
+			// virtualId is low, we assume that scripts expect us to create new slot
+			if (virtualId == s->_lastSaveVirtualId) {
+				// if last virtual id is the same as this one, we assume that caller wants to overwrite last save
+				savegameId = s->_lastSaveNewId;
+			} else {
+				uint savegameNr;
+				// savegameId is in lower range, scripts expect us to create a new slot
+				for (savegameId = 0; savegameId < SAVEGAMEID_OFFICIALRANGE_START; savegameId++) {
+					for (savegameNr = 0; savegameNr < saves.size(); savegameNr++) {
+						if (savegameId == saves[savegameNr].id)
+							break;
+					}
+					if (savegameNr == saves.size())
+						break;
+				}
+				if (savegameId == SAVEGAMEID_OFFICIALRANGE_START)
+					error("kSavegame: no more savegame slots available");
+			}
+		} else {
+			error("kSaveGame: invalid savegameId used");
+		}
+
+		// Save in case caller wants to overwrite last newly created save
+		s->_lastSaveVirtualId = virtualId;
+		s->_lastSaveNewId = savegameId;
 	}
 
-	return SIGNAL_REG;
-}
+	s->r_acc = NULL_REG;
 
-reg_t kFileIOFindFirst(EngineState *s, int argc, reg_t *argv) {
-	Common::String mask = s->_segMan->getString(argv[0]);
-	reg_t buf = argv[1];
-	int attr = argv[2].toUint16(); // We won't use this, Win32 might, though...
-	debugC(kDebugLevelFile, "kFileIO(findFirst): %s, 0x%x", mask.c_str(), attr);
+	Common::String filename = g_sci->getSavegameName(savegameId);
+	Common::SaveFileManager *saveFileMan = g_sci->getSaveFileManager();
+	Common::OutSaveFile *out;
+
+	out = saveFileMan->openForSaving(filename);
+	if (!out) {
+		warning("Error opening savegame \"%s\" for writing", filename.c_str());
+	} else {
+		if (!gamestate_save(s, out, game_description, version)) {
+			warning("Saving the game failed");
+		} else {
+			s->r_acc = TRUE_REG; // save successful
+		}
+
+		out->finalize();
+		if (out->err()) {
+			warning("Writing the savegame failed");
+			s->r_acc = NULL_REG; // write failure
+		}
+		delete out;
+	}
 
-	// We remove ".*". mask will get prefixed, so we will return all additional files for that gameid
-	if (mask == "*.*")
-		mask = "*";
-	return s->_dirseeker.firstFile(mask, buf, s->_segMan);
+	return s->r_acc;
 }
 
-reg_t kFileIOFindNext(EngineState *s, int argc, reg_t *argv) {
-	debugC(kDebugLevelFile, "kFileIO(findNext)");
-	return s->_dirseeker.nextFile(s->_segMan);
-}
+reg_t kRestoreGame(EngineState *s, int argc, reg_t *argv) {
+	Common::String game_id = !argv[0].isNull() ? s->_segMan->getString(argv[0]) : "";
+	int16 savegameId = argv[1].toSint16();
+	bool pausedMusic = false;
 
-reg_t kFileIOExists(EngineState *s, int argc, reg_t *argv) {
-	Common::String name = s->_segMan->getString(argv[0]);
+	debug(3, "kRestoreGame(%s,%d)", game_id.c_str(), savegameId);
 
-#ifdef ENABLE_SCI32
-	// Cache the file existence result for the Phantasmagoria
-	// save index file, as the game scripts keep checking for
-	// its existence.
-	if (name == PHANTASMAGORIA_SAVEGAME_INDEX && s->_virtualIndexFile)
-		return TRUE_REG;
-#endif
+	if (argv[0].isNull()) {
+		// Direct call, either from launcher or from a patched Game::restore
+		if (savegameId == -1) {
+			// we are supposed to show a dialog for the user and let him choose a saved game
+			g_sci->_soundCmd->pauseAll(true); // pause music
+			GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Restore game:"), _("Restore"), false);
+			savegameId = dialog->runModalWithCurrentTarget();
+			delete dialog;
+			if (savegameId < 0) {
+				g_sci->_soundCmd->pauseAll(false); // unpause music
+				return s->r_acc;
+			}
+			pausedMusic = true;
+		}
+		// don't adjust ID of the saved game, it's already correct
+	} else {
+		if (argv[2].isNull())
+			error("kRestoreGame: called with parameter 2 being NULL");
+		// Real call from script, we need to adjust ID
+		if ((savegameId < SAVEGAMEID_OFFICIALRANGE_START) || (savegameId > SAVEGAMEID_OFFICIALRANGE_END)) {
+			warning("Savegame ID %d is not allowed", savegameId);
+			return TRUE_REG;
+		}
+		savegameId -= SAVEGAMEID_OFFICIALRANGE_START;
+	}
 
-	bool exists = false;
+	s->r_acc = NULL_REG; // signals success
 
-	// Check for regular file
-	exists = Common::File::exists(name);
+	Common::Array<SavegameDesc> saves;
+	listSavegames(saves);
+	if (findSavegame(saves, savegameId) == -1) {
+		s->r_acc = TRUE_REG;
+		warning("Savegame ID %d not found", savegameId);
+	} else {
+		Common::SaveFileManager *saveFileMan = g_sci->getSaveFileManager();
+		Common::String filename = g_sci->getSavegameName(savegameId);
+		Common::SeekableReadStream *in;
 
-	// Check for a savegame with the name
-	Common::SaveFileManager *saveFileMan = g_sci->getSaveFileManager();
-	if (!exists)
-		exists = !saveFileMan->listSavefiles(name).empty();
+		in = saveFileMan->openForLoading(filename);
+		if (in) {
+			// found a savegame file
 
-	// Try searching for the file prepending "target-"
-	const Common::String wrappedName = g_sci->wrapFilename(name);
-	if (!exists) {
-		exists = !saveFileMan->listSavefiles(wrappedName).empty();
-	}
+			gamestate_restore(s, in);
+			delete in;
 
-	// SCI2+ debug mode
-	if (DebugMan.isDebugChannelEnabled(kDebugLevelDebugMode)) {
-		if (!exists && name == "1.scr")		// PQ4
-			exists = true;
-		if (!exists && name == "18.scr")	// QFG4
-			exists = true;
-		if (!exists && name == "99.scr")	// GK1, KQ7
-			exists = true;
-		if (!exists && name == "classes")	// GK2, SQ6, LSL7
-			exists = true;
+			if (g_sci->getGameId() == GID_MOTHERGOOSE256) {
+				// WORKAROUND: Mother Goose SCI1/SCI1.1 does some weird things for
+				//  saving a previously restored game.
+				// We set the current savedgame-id directly and remove the script
+				//  code concerning this via script patch.
+				s->variables[VAR_GLOBAL][0xB3].offset = SAVEGAMEID_OFFICIALRANGE_START + savegameId;
+			}
+		} else {
+			s->r_acc = TRUE_REG;
+			warning("Savegame #%d not found", savegameId);
+		}
 	}
 
-	// Special case for non-English versions of LSL5: The English version of
-	// LSL5 calls kFileIO(), case K_FILEIO_OPEN for reading to check if
-	// memory.drv exists (which is where the game's password is stored). If
-	// it's not found, it calls kFileIO() again, case K_FILEIO_OPEN for
-	// writing and creates a new file. Non-English versions call kFileIO(),
-	// case K_FILEIO_FILE_EXISTS instead, and fail if memory.drv can't be
-	// found. We create a default memory.drv file with no password, so that
-	// the game can continue.
-	if (!exists && name == "memory.drv") {
-		// Create a new file, and write the bytes for the empty password
-		// string inside
-		byte defaultContent[] = { 0xE9, 0xE9, 0xEB, 0xE1, 0x0D, 0x0A, 0x31, 0x30, 0x30, 0x30 };
-		Common::WriteStream *outFile = saveFileMan->openForSaving(wrappedName);
-		for (int i = 0; i < 10; i++)
-			outFile->writeByte(defaultContent[i]);
-		outFile->finalize();
-		exists = !outFile->err();	// check whether we managed to create the file.
-		delete outFile;
+	if (!s->r_acc.isNull()) {
+		// no success?
+		if (pausedMusic)
+			g_sci->_soundCmd->pauseAll(false); // unpause music
 	}
 
-	// Special case for KQ6 Mac: The game checks for two video files to see
-	// if they exist before it plays them. Since we support multiple naming
-	// schemes for resource fork files, we also need to support that here in
-	// case someone has a "HalfDome.bin" file, etc. 
-	if (!exists && g_sci->getGameId() == GID_KQ6 && g_sci->getPlatform() == Common::kPlatformMacintosh &&
-			(name == "HalfDome" || name == "Kq6Movie"))
-		exists = Common::MacResManager::exists(name);
+	return s->r_acc;
+}
 
-	debugC(kDebugLevelFile, "kFileIO(fileExists) %s -> %d", name.c_str(), exists);
-	return make_reg(0, exists);
+reg_t kGetSaveDir(EngineState *s, int argc, reg_t *argv) {
+#ifdef ENABLE_SCI32
+	// SCI32 uses a parameter here. It is used to modify a string, stored in a
+	// global variable, so that game scripts store the save directory. We
+	// don't really set a save game directory, thus not setting the string to
+	// anything is the correct thing to do here.
+	//if (argc > 0)
+	//	warning("kGetSaveDir called with %d parameter(s): %04x:%04x", argc, PRINT_REG(argv[0]));
+#endif
+		return s->_segMan->getSaveDirPtr();
 }
 
-reg_t kFileIORename(EngineState *s, int argc, reg_t *argv) {
-	Common::String oldName = s->_segMan->getString(argv[0]);
-	Common::String newName = s->_segMan->getString(argv[1]);
+reg_t kCheckSaveGame(EngineState *s, int argc, reg_t *argv) {
+	Common::String game_id = s->_segMan->getString(argv[0]);
+	uint16 virtualId = argv[1].toUint16();
 
-	// SCI1.1 returns 0 on success and a DOS error code on fail. SCI32
-	// returns -1 on fail. We just return -1 for all versions.
-	if (g_sci->getSaveFileManager()->renameSavefile(oldName, newName))
-		return NULL_REG;
-	else
-		return SIGNAL_REG;
-}
+	debug(3, "kCheckSaveGame(%s, %d)", game_id.c_str(), virtualId);
 
-#ifdef ENABLE_SCI32
-reg_t kFileIOReadByte(EngineState *s, int argc, reg_t *argv) {
-	// Read the byte into the low byte of the accumulator
-	FileHandle *f = getFileFromHandle(s, argv[0].toUint16());
-	if (!f)
+	Common::Array<SavegameDesc> saves;
+	listSavegames(saves);
+
+	// we allow 0 (happens in QfG2 when trying to restore from an empty saved game list) and return false in that case
+	if (virtualId == 0)
 		return NULL_REG;
-	return make_reg(0, (s->r_acc.toUint16() & 0xff00) | f->_in->readByte());
-}
 
-reg_t kFileIOWriteByte(EngineState *s, int argc, reg_t *argv) {
-	FileHandle *f = getFileFromHandle(s, argv[0].toUint16());
-	if (f)
-		f->_out->writeByte(argv[1].toUint16() & 0xff);
-	return s->r_acc; // FIXME: does this really not return anything?
-}
+	// Find saved-game
+	if ((virtualId < SAVEGAMEID_OFFICIALRANGE_START) || (virtualId > SAVEGAMEID_OFFICIALRANGE_END))
+		error("kCheckSaveGame: called with invalid savegameId");
+	uint savegameId = virtualId - SAVEGAMEID_OFFICIALRANGE_START;
+	int savegameNr = findSavegame(saves, savegameId);
+	if (savegameNr == -1)
+		return NULL_REG;
 
-reg_t kFileIOReadWord(EngineState *s, int argc, reg_t *argv) {
-	FileHandle *f = getFileFromHandle(s, argv[0].toUint16());
-	if (!f)
+	// Check for compatible savegame version
+	int ver = saves[savegameNr].version;
+	if (ver < MINIMUM_SAVEGAME_VERSION || ver > CURRENT_SAVEGAME_VERSION)
 		return NULL_REG;
-	return make_reg(0, f->_in->readUint16LE());
-}
 
-reg_t kFileIOWriteWord(EngineState *s, int argc, reg_t *argv) {
-	FileHandle *f = getFileFromHandle(s, argv[0].toUint16());
-	if (f)
-		f->_out->writeUint16LE(argv[1].toUint16());
-	return s->r_acc; // FIXME: does this really not return anything?
+	// Otherwise we assume the savegame is OK
+	return TRUE_REG;
 }
 
-reg_t kFileIOCreateSaveSlot(EngineState *s, int argc, reg_t *argv) {
-	// Used in Shivers when the user enters his name on the guest book
-	// in the beginning to start the game.
+reg_t kGetSaveFiles(EngineState *s, int argc, reg_t *argv) {
+	Common::String game_id = s->_segMan->getString(argv[0]);
 
-	// Creates a new save slot, and returns if the operation was successful
+	debug(3, "kGetSaveFiles(%s)", game_id.c_str());
 
-	// Argument 0 denotes the save slot as a negative integer, 2 means "0"
-	// Argument 1 is a string, with the file name, obtained from kSave(5).
-	// The interpreter checks if it can be written to (by checking for free
-	// disk space and write permissions)
+	// Scripts ask for current save files, we can assume that if afterwards they ask us to create a new slot they really
+	//  mean new slot instead of overwriting the old one
+	s->_lastSaveVirtualId = SAVEGAMEID_OFFICIALRANGE_START;
 
-	// We don't really use or need any of this...
+	Common::Array<SavegameDesc> saves;
+	listSavegames(saves);
+	uint totalSaves = MIN<uint>(saves.size(), MAX_SAVEGAME_NR);
 
-	uint16 saveSlot = argv[0].toUint16();
-	char* fileName = s->_segMan->lookupString(argv[1])->getRawData();
-	warning("kFileIOCreateSaveSlot(%d, '%s')", saveSlot, fileName);
+	reg_t *slot = s->_segMan->derefRegPtr(argv[2], totalSaves);
 
-	return TRUE_REG;	// slot creation was successful
-}
+	if (!slot) {
+		warning("kGetSaveFiles: %04X:%04X invalid or too small to hold slot data", PRINT_REG(argv[2]));
+		totalSaves = 0;
+	}
 
-reg_t kCD(EngineState *s, int argc, reg_t *argv) {
-	// TODO: Stub
-	switch (argv[0].toUint16()) {
-	case 0:
-		// Return whether the contents of disc argv[1] is available.
-		return TRUE_REG;
-	default:
-		warning("CD(%d)", argv[0].toUint16());
+	const uint bufSize = (totalSaves * SCI_MAX_SAVENAME_LENGTH) + 1;
+	char *saveNames = new char[bufSize];
+	char *saveNamePtr = saveNames;
+
+	for (uint i = 0; i < totalSaves; i++) {
+		*slot++ = make_reg(0, saves[i].id + SAVEGAMEID_OFFICIALRANGE_START); // Store the virtual savegame ID ffs. see above
+		strcpy(saveNamePtr, saves[i].name);
+		saveNamePtr += SCI_MAX_SAVENAME_LENGTH;
 	}
 
-	return NULL_REG;
+	*saveNamePtr = 0; // Terminate list
+
+	s->_segMan->memcpy(argv[1], (byte *)saveNames, bufSize);
+	delete[] saveNames;
+
+	return make_reg(0, totalSaves);
 }
 
+#ifdef ENABLE_SCI32
+
 reg_t kMakeSaveCatName(EngineState *s, int argc, reg_t *argv) {
 	// Normally, this creates the name of the save catalogue/directory to save into.
 	// First parameter is the string to save the result into. Second is a string
@@ -928,12 +950,6 @@ reg_t kAutoSave(EngineState *s, int argc, reg_t *argv) {
 	return s->r_acc;
 }
 
-reg_t kSave(EngineState *s, int argc, reg_t *argv) {
-	if (!s)
-		return make_reg(0, getSciVersion());
-	error("not supposed to call this");
-}
-
 #endif
 
 } // End of namespace Sci






More information about the Scummvm-git-logs mailing list