diff options
-rw-r--r-- | engines/gob/gob.cpp | 1 | ||||
-rw-r--r-- | engines/gob/inter.h | 2 | ||||
-rw-r--r-- | engines/gob/inter_geisha.cpp | 97 | ||||
-rw-r--r-- | engines/gob/module.mk | 1 | ||||
-rw-r--r-- | engines/gob/save/saveload.h | 54 | ||||
-rw-r--r-- | engines/gob/save/saveload_geisha.cpp | 215 |
6 files changed, 359 insertions, 11 deletions
diff --git a/engines/gob/gob.cpp b/engines/gob/gob.cpp index 7bb7928406..51a117b7ec 100644 --- a/engines/gob/gob.cpp +++ b/engines/gob/gob.cpp @@ -422,6 +422,7 @@ bool GobEngine::initGameParts() { _map = new Map_v1(this); _goblin = new Goblin_v1(this); _scenery = new Scenery_v1(this); + _saveLoad = new SaveLoad_Geisha(this, _targetName.c_str()); break; case kGameTypeFascination: diff --git a/engines/gob/inter.h b/engines/gob/inter.h index 84180f407d..764c7bf246 100644 --- a/engines/gob/inter.h +++ b/engines/gob/inter.h @@ -348,6 +348,8 @@ protected: void oGeisha_goblinFunc(OpFuncParams ¶ms); void oGeisha_loadSound(OpFuncParams ¶ms); void oGeisha_checkData(OpFuncParams ¶ms); + void oGeisha_readData(OpFuncParams ¶ms); + void oGeisha_writeData(OpFuncParams ¶ms); void oGeisha_gamePenetration(OpGobParams ¶ms); void oGeisha_gameDiving(OpGobParams ¶ms); diff --git a/engines/gob/inter_geisha.cpp b/engines/gob/inter_geisha.cpp index 658f2346f4..e547285f86 100644 --- a/engines/gob/inter_geisha.cpp +++ b/engines/gob/inter_geisha.cpp @@ -21,6 +21,10 @@ */ #include "common/endian.h" +#include "common/str.h" +#include "common/translation.h" + +#include "gui/message.h" #include "gob/gob.h" #include "gob/inter.h" @@ -30,6 +34,7 @@ #include "gob/game.h" #include "gob/draw.h" #include "gob/video.h" +#include "gob/save/saveload.h" #include "gob/sound/sound.h" #include "gob/sound/sounddesc.h" @@ -54,6 +59,8 @@ void Inter_Geisha::setupOpcodesFunc() { OPCODEFUNC(0x25, oGeisha_goblinFunc); OPCODEFUNC(0x3A, oGeisha_loadSound); OPCODEFUNC(0x3F, oGeisha_checkData); + OPCODEFUNC(0x4D, oGeisha_readData); + OPCODEFUNC(0x4E, oGeisha_writeData); OPCODEGOB(0, oGeisha_gamePenetration); OPCODEGOB(1, oGeisha_gameDiving); @@ -114,20 +121,88 @@ int16 Inter_Geisha::loadSound(int16 slot) { } void Inter_Geisha::oGeisha_checkData(OpFuncParams ¶ms) { - const char *file = _vm->_game->_script->evalString(); - int16 varOff = _vm->_game->_script->readVarIndex(); + Common::String file = _vm->_game->_script->evalString(); + int16 varOff = _vm->_game->_script->readVarIndex(); + + file.toLowercase(); + if (file.hasSuffix(".0ot")) + file.setChar('t', file.size() - 3); + + bool exists = false; + + SaveLoad::SaveMode mode = _vm->_saveLoad->getSaveMode(file.c_str()); + if (mode == SaveLoad::kSaveModeNone) { + + exists = _vm->_dataIO->hasFile(file); + if (!exists) + warning("File \"%s\" not found", file.c_str()); + + } else if (mode == SaveLoad::kSaveModeSave) + exists = _vm->_saveLoad->getSize(file.c_str()) >= 0; + else if (mode == SaveLoad::kSaveModeExists) + exists = true; + + WRITE_VAR_OFFSET(varOff, exists ? 50 : (uint32)-1); +} + +void Inter_Geisha::oGeisha_readData(OpFuncParams ¶ms) { + const char *file = _vm->_game->_script->evalString(); + + uint16 dataVar = _vm->_game->_script->readVarIndex(); + + debugC(2, kDebugFileIO, "Read from file \"%s\" (%d)", file, dataVar); + + WRITE_VAR(1, 1); + + SaveLoad::SaveMode mode = _vm->_saveLoad->getSaveMode(file); + if (mode == SaveLoad::kSaveModeSave) { + + if (!_vm->_saveLoad->load(file, dataVar, 0, 0)) { + + GUI::MessageDialog dialog(_("Failed to load game state from file.")); + dialog.runModal(); + + } else + WRITE_VAR(1, 0); + + return; + + } else if (mode == SaveLoad::kSaveModeIgnore) { + WRITE_VAR(1, 0); + return; + } + + warning("Attempted to read from file \"%s\"", file); +} + +void Inter_Geisha::oGeisha_writeData(OpFuncParams ¶ms) { + const char *file = _vm->_game->_script->evalString(); + + int16 dataVar = _vm->_game->_script->readVarIndex(); + int32 size = _vm->_game->_script->readValExpr(); + + debugC(2, kDebugFileIO, "Write to file \"%s\" (%d, %d bytes)", file, dataVar, size); + + WRITE_VAR(1, 1); + + SaveLoad::SaveMode mode = _vm->_saveLoad->getSaveMode(file); + if (mode == SaveLoad::kSaveModeSave) { + + if (!_vm->_saveLoad->save(file, dataVar, size, 0)) { + + GUI::MessageDialog dialog(_("Failed to save game state to file.")); + dialog.runModal(); - Common::String fileName(file); + } else + WRITE_VAR(1, 0); - fileName.toLowercase(); - if (fileName.hasSuffix(".0ot")) - fileName.setChar('t', fileName.size() - 3); + } else if (mode == SaveLoad::kSaveModeIgnore) { + WRITE_VAR(1, 0); + return; + } else if (mode == SaveLoad::kSaveModeNone) + warning("Attempted to write to file \"%s\"", file); - if (!_vm->_dataIO->hasFile(fileName)) { - warning("File \"%s\" not found", fileName.c_str()); - WRITE_VAR_OFFSET(varOff, (uint32) -1); - } else - WRITE_VAR_OFFSET(varOff, 50); // "handle" between 50 and 128 = in archive + WRITE_VAR(1, 0); } void Inter_Geisha::oGeisha_gamePenetration(OpGobParams ¶ms) { diff --git a/engines/gob/module.mk b/engines/gob/module.mk index b85c387734..0300bcc3f0 100644 --- a/engines/gob/module.mk +++ b/engines/gob/module.mk @@ -77,6 +77,7 @@ MODULE_OBJS := \ save/saveload_v4.o \ save/saveload_v6.o \ save/saveload_v7.o \ + save/saveload_geisha.o \ save/saveload_fascin.o \ save/saveload_inca2.o \ save/saveload_playtoons.o \ diff --git a/engines/gob/save/saveload.h b/engines/gob/save/saveload.h index 4d51a5b51c..66b3482bac 100644 --- a/engines/gob/save/saveload.h +++ b/engines/gob/save/saveload.h @@ -71,6 +71,60 @@ protected: virtual const char *getDescription(const char *fileName) const; }; +/** Save/Load class for Geisha. */ +class SaveLoad_Geisha : public SaveLoad { +public: + SaveLoad_Geisha(GobEngine *vm, const char *targetName); + virtual ~SaveLoad_Geisha(); + + SaveMode getSaveMode(const char *fileName) const; + +protected: + static const uint32 kSlotCount = 7; + static const uint32 kSlotSize = 44; + + static const uint32 kSaveFileSize = kSlotCount * kSlotSize; + + struct SaveFile { + const char *sourceName; + SaveMode mode; + SaveHandler *handler; + const char *description; + }; + + /** Handles the save slots. */ + class GameHandler : public SaveHandler { + public: + GameHandler(GobEngine *vm, const Common::String &target); + ~GameHandler(); + + int32 getSize(); + bool load(int16 dataVar, int32 size, int32 offset); + bool save(int16 dataVar, int32 size, int32 offset); + + private: + /** Slot file construction. */ + class File : public SlotFileIndexed { + public: + File(GobEngine *vm, const Common::String &base); + ~File(); + + int getSlot(int32 offset) const; + int getSlotRemainder(int32 offset) const; + }; + + File _file; + }; + + static SaveFile _saveFiles[]; + + SaveHandler *getHandler(const char *fileName) const; + const char *getDescription(const char *fileName) const; + + const SaveFile *getSaveFile(const char *fileName) const; + SaveFile *getSaveFile(const char *fileName); +}; + /** Save/Load class for Gobliins 2, Ween: The Prophecy and Bargon Attack. */ class SaveLoad_v2 : public SaveLoad { public: diff --git a/engines/gob/save/saveload_geisha.cpp b/engines/gob/save/saveload_geisha.cpp new file mode 100644 index 0000000000..3414c12dda --- /dev/null +++ b/engines/gob/save/saveload_geisha.cpp @@ -0,0 +1,215 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "gob/save/saveload.h" +#include "gob/save/saveconverter.h" +#include "gob/inter.h" +#include "gob/variables.h" + +namespace Gob { + +SaveLoad_Geisha::SaveFile SaveLoad_Geisha::_saveFiles[] = { + {"save.inf", kSaveModeSave, 0, "savegame"} +}; + + +SaveLoad_Geisha::GameHandler::File::File(GobEngine *vm, const Common::String &base) : + SlotFileIndexed(vm, SaveLoad_Geisha::kSlotCount, base, "s") { + +} + +SaveLoad_Geisha::GameHandler::File::~File() { +} + +int SaveLoad_Geisha::GameHandler::File::getSlot(int32 offset) const { + return 0; +} + +int SaveLoad_Geisha::GameHandler::File::getSlotRemainder(int32 offset) const { + return 0; +} + + +SaveLoad_Geisha::GameHandler::GameHandler(GobEngine *vm, const Common::String &target) : + SaveHandler(vm), _file(vm, target) { + +} + +SaveLoad_Geisha::GameHandler::~GameHandler() { +} + +int32 SaveLoad_Geisha::GameHandler::getSize() { + if (_file.getSlotMax() == 0) + return -1; + + return SaveLoad_Geisha::kSaveFileSize; +} + +bool SaveLoad_Geisha::GameHandler::load(int16 dataVar, int32 size, int32 offset) { + if ((size != 0) || (offset != 0)) { + warning("Invalid loading procedure: %d, %d, %d", dataVar, size, offset); + return false; + } + + memset(_vm->_inter->_variables->getAddressOff8(dataVar), 0, SaveLoad_Geisha::kSaveFileSize); + + for (uint32 slot = 0; slot < SaveLoad_Geisha::kSlotCount; + slot++, dataVar += SaveLoad_Geisha::kSlotSize) { + + if (!_file.exists(slot)) + continue; + + Common::String slotFile = _file.build(slot); + if (slotFile.empty()) + return false; + + SaveReader reader(2, slot, slotFile); + if (!reader.load()) { + warning("Save slot %d contains corrupted save", slot); + continue; + } + + SavePartInfo info(20, (uint32) _vm->getGameType(), 0, + _vm->getEndianness(), _vm->_inter->_variables->getSize()); + SavePartVars vars(_vm, SaveLoad_Geisha::kSlotSize); + + if (!reader.readPart(0, &info) || !reader.readPart(1, &vars)) { + warning("Save slot %d contains corrupted save", slot); + continue; + } + + if (!vars.writeInto(dataVar, 0, SaveLoad_Geisha::kSlotSize)) { + warning("Save slot %d contains corrupted save", slot); + continue; + } + } + + return true; +} + +bool SaveLoad_Geisha::GameHandler::save(int16 dataVar, int32 size, int32 offset) { + if (((uint32)size != SaveLoad_Geisha::kSaveFileSize) || (offset != 0)) { + warning("Invalid saving procedure: %d, %d, %d", dataVar, size, offset); + return false; + } + + for (uint32 slot = 0; slot < SaveLoad_Geisha::kSlotCount; + slot++, dataVar += SaveLoad_Geisha::kSlotSize) { + + const byte *slotData = _vm->_inter->_variables->getAddressOff8(dataVar); + + // Check of the slot's data is empty + bool empty = true; + for (uint32 j = 0; j < SaveLoad_Geisha::kSlotSize; j++) { + if (slotData[j] != 0) { + empty = false; + break; + } + } + + // Don't save empty slots + if (empty) + continue; + + Common::String slotFile = _file.build(slot); + if (slotFile.empty()) + return false; + + SaveWriter writer(2, slot, slotFile); + + SavePartInfo info(20, (uint32) _vm->getGameType(), 0, + _vm->getEndianness(), _vm->_inter->_variables->getSize()); + SavePartVars vars(_vm, SaveLoad_Geisha::kSlotSize); + + info.setDesc(Common::String::format("Geisha, slot %d", slot).c_str()); + if (!vars.readFrom(dataVar, 0, SaveLoad_Geisha::kSlotSize)) + return false; + + if (!writer.writePart(0, &info)) + return false; + if (!writer.writePart(1, &vars)) + return false; + } + + return true; +} + + +SaveLoad_Geisha::SaveLoad_Geisha(GobEngine *vm, const char *targetName) : + SaveLoad(vm) { + + _saveFiles[0].handler = new GameHandler(vm, targetName); +} + +SaveLoad_Geisha::~SaveLoad_Geisha() { + for (int i = 0; i < ARRAYSIZE(_saveFiles); i++) + delete _saveFiles[i].handler; +} + +const SaveLoad_Geisha::SaveFile *SaveLoad_Geisha::getSaveFile(const char *fileName) const { + fileName = stripPath(fileName); + + for (int i = 0; i < ARRAYSIZE(_saveFiles); i++) + if (!scumm_stricmp(fileName, _saveFiles[i].sourceName)) + return &_saveFiles[i]; + + return 0; +} + +SaveLoad_Geisha::SaveFile *SaveLoad_Geisha::getSaveFile(const char *fileName) { + fileName = stripPath(fileName); + + for (int i = 0; i < ARRAYSIZE(_saveFiles); i++) + if (!scumm_stricmp(fileName, _saveFiles[i].sourceName)) + return &_saveFiles[i]; + + return 0; +} + +SaveHandler *SaveLoad_Geisha::getHandler(const char *fileName) const { + const SaveFile *saveFile = getSaveFile(fileName); + + if (saveFile) + return saveFile->handler; + + return 0; +} + +const char *SaveLoad_Geisha::getDescription(const char *fileName) const { + const SaveFile *saveFile = getSaveFile(fileName); + + if (saveFile) + return saveFile->description; + + return 0; +} + +SaveLoad::SaveMode SaveLoad_Geisha::getSaveMode(const char *fileName) const { + const SaveFile *saveFile = getSaveFile(fileName); + + if (saveFile) + return saveFile->mode; + + return kSaveModeNone; +} + +} // End of namespace Gob |