From e4f08a4644c5a59b8e02b3702eef436e52e2d994 Mon Sep 17 00:00:00 2001 From: Filippos Karapetis Date: Wed, 13 Jun 2012 22:55:27 +0300 Subject: SCI: Fix the loading screen and the loading functionality in Shivers Shivers uses extra special hardcoded save files together with the normal ones that are used to store slot names and spot descriptions. The scheme is a bit odd, and since the names of the extra save files are hardcoded, this scheme is problematic to use. We skip the creation of these files altogether and use virtual files instead, which means that the (broken) spot descriptions won't be visible next to each save description. This isn't a major issue for now, and it's left as a future TODO to implement this feature in a cleaner way, and not with extra save files. This scheme fixes the slot descriptions in the loading screen. Also, kCD(1) has been implemented, which fixes loading of the save states themselves --- engines/sci/engine/file.cpp | 8 ++++- engines/sci/engine/file.h | 1 + engines/sci/engine/kfile.cpp | 85 ++++++++++++++++++++++++++++++++++++-------- 3 files changed, 78 insertions(+), 16 deletions(-) diff --git a/engines/sci/engine/file.cpp b/engines/sci/engine/file.cpp index 66f54d67df..3470d23fe2 100644 --- a/engines/sci/engine/file.cpp +++ b/engines/sci/engine/file.cpp @@ -346,6 +346,12 @@ VirtualIndexFile::VirtualIndexFile(Common::String fileName) : _fileName(fileName delete inFile; } +VirtualIndexFile::VirtualIndexFile(uint32 initialSize) : _changed(false) { + _bufferSize = initialSize; + _buffer = new char[_bufferSize]; + _ptr = _buffer; +} + VirtualIndexFile::~VirtualIndexFile() { close(); @@ -430,7 +436,7 @@ bool VirtualIndexFile::seek(int32 offset, int whence) { } void VirtualIndexFile::close() { - if (_changed) { + if (_changed && !_fileName.empty()) { Common::WriteStream *outFile = g_sci->getSaveFileManager()->openForSaving(_fileName); outFile->write(_buffer, _bufferSize); delete outFile; diff --git a/engines/sci/engine/file.h b/engines/sci/engine/file.h index 896dcd2a42..1c8e302d15 100644 --- a/engines/sci/engine/file.h +++ b/engines/sci/engine/file.h @@ -115,6 +115,7 @@ private: class VirtualIndexFile { public: VirtualIndexFile(Common::String fileName); + VirtualIndexFile(uint32 initialSize); ~VirtualIndexFile(); uint32 read(char *buffer, uint32 size); diff --git a/engines/sci/engine/kfile.cpp b/engines/sci/engine/kfile.cpp index 445a019368..7a2f161829 100644 --- a/engines/sci/engine/kfile.cpp +++ b/engines/sci/engine/kfile.cpp @@ -199,6 +199,9 @@ reg_t kCD(EngineState *s, int argc, reg_t *argv) { case 0: // Return whether the contents of disc argv[1] is available. return TRUE_REG; + case 1: + // Return the current CD number + return make_reg(0, 1); default: warning("CD(%d)", argv[0].toUint16()); } @@ -219,21 +222,6 @@ reg_t kFileIO(EngineState *s, int argc, reg_t *argv) { reg_t kFileIOOpen(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(); @@ -261,6 +249,73 @@ reg_t kFileIOOpen(EngineState *s, int argc, reg_t *argv) { } debugC(kDebugLevelFile, "kFileIO(open): %s, 0x%x", name.c_str(), mode); +#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); + } + } + } + + // Shivers is trying to store savegame descriptions and current spots in + // separate .SG files, which are hardcoded in the scripts. + // Essentially, there is a normal save file, created by the executable + // and an extra hardcoded save file, created by the game scripts, probably + // because they didn't want to modify the save/load code to add the extra + // information. + // Each slot in the book then has two strings, the save description and a + // description of the current spot that the player is at. Currently, the + // spot strings are always empty (probably related to the unimplemented + // kString subop 14, which gets called right before this call). + // For now, we don't allow the creation of these files, which means that + // all the spot descriptions next to each slot description will be empty + // (they are empty anyway). Until a viable solution is found to handle these + // extra files and until the spot description strings are initialized + // correctly, we resort to virtual files in order to make the load screen + // useable. Without this code it is unusable, as the extra information is + // always saved to 0.SG for some reason, but on restore the correct file is + // used. Perhaps the virtual ID is not taken into account when saving. + // + // Future TODO: maintain spot descriptions and show them too, ideally without + // having to return to this logic of extra hardcoded files. + if (g_sci->getGameId() == GID_SHIVERS && name.hasSuffix(".SG")) { + if (mode == _K_FILE_MODE_OPEN_OR_CREATE || mode == _K_FILE_MODE_CREATE) { + // Game scripts are trying to create a file with the save + // description, stop them here + debugC(kDebugLevelFile, "Not creating unused file %s", name.c_str()); + return SIGNAL_REG; + } else if (mode == _K_FILE_MODE_OPEN_OR_FAIL) { + // Create a virtual file containing the save game description + // and slot number, as the game scripts expect. + int slotNumber; + sscanf(name.c_str(), "%d.SG", &slotNumber); + + Common::Array saves; + listSavegames(saves); + int savegameNr = findSavegame(saves, slotNumber - SAVEGAMEID_OFFICIALRANGE_START); + + if (!s->_virtualIndexFile) { + // Make the virtual file buffer big enough to avoid having it grow dynamically. + // 50 bytes should be more than enough. + s->_virtualIndexFile = new VirtualIndexFile(50); + } + + s->_virtualIndexFile->seek(0, SEEK_SET); + s->_virtualIndexFile->write(saves[savegameNr].name, strlen(saves[savegameNr].name)); + s->_virtualIndexFile->write("\0", 1); + s->_virtualIndexFile->write("\0", 1); // Spot description (empty) + s->_virtualIndexFile->seek(0, SEEK_SET); + return make_reg(0, VIRTUALFILE_HANDLE); + } + } +#endif + // 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 -- cgit v1.2.3