From aed38527010e7b155af469d0521aa07e7fd7d12e Mon Sep 17 00:00:00 2001 From: johndoe123 Date: Fri, 4 Dec 2015 23:20:22 +0100 Subject: ILLUSIONS: Implement save/load functionality - Only works in Duckman at the moment - Only via the ScummVM main menu for now, save/load via the game's menu to be implemented --- engines/illusions/detection.cpp | 9 -- engines/illusions/duckman/gamestate_duckman.cpp | 50 +++++++ engines/illusions/duckman/gamestate_duckman.h | 44 ++++++ engines/illusions/duckman/illusions_duckman.cpp | 86 ++++++----- engines/illusions/duckman/illusions_duckman.h | 5 +- .../illusions/duckman/scriptopcodes_duckman.cpp | 12 +- engines/illusions/gamestate.cpp | 75 ++++++++++ engines/illusions/gamestate.h | 53 +++++++ engines/illusions/illusions.cpp | 5 + engines/illusions/illusions.h | 14 +- engines/illusions/module.mk | 3 + engines/illusions/resources/scriptresource.cpp | 40 ++++- engines/illusions/resources/scriptresource.h | 7 + engines/illusions/saveload.cpp | 162 +++++++++++++++++++++ engines/illusions/screen.cpp | 13 +- 15 files changed, 511 insertions(+), 67 deletions(-) create mode 100644 engines/illusions/duckman/gamestate_duckman.cpp create mode 100644 engines/illusions/duckman/gamestate_duckman.h create mode 100644 engines/illusions/gamestate.cpp create mode 100644 engines/illusions/gamestate.h create mode 100644 engines/illusions/saveload.cpp diff --git a/engines/illusions/detection.cpp b/engines/illusions/detection.cpp index 53e7a4b1e5..a0e24886cc 100644 --- a/engines/illusions/detection.cpp +++ b/engines/illusions/detection.cpp @@ -95,29 +95,22 @@ public: virtual bool hasFeature(MetaEngineFeature f) const; virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const; -#if 0 virtual int getMaximumSaveSlot() const; virtual SaveStateList listSaves(const char *target) const; SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const; virtual void removeSaveState(const char *target, int slot) const; -#endif }; bool IllusionsMetaEngine::hasFeature(MetaEngineFeature f) const { return - false; - /* (f == kSupportsListSaves) || (f == kSupportsDeleteSave) || (f == kSupportsLoadingDuringStartup) || (f == kSavesSupportMetaInfo) || (f == kSavesSupportThumbnail) || (f == kSavesSupportCreationDate); - */ } -#if 0 - void IllusionsMetaEngine::removeSaveState(const char *target, int slot) const { Common::String fileName = Common::String::format("%s.%03d", target, slot); g_system->getSavefileManager()->removeSavefile(fileName); @@ -175,8 +168,6 @@ SaveStateDescriptor IllusionsMetaEngine::querySaveMetaInfos(const char *target, return SaveStateDescriptor(); } -#endif - bool IllusionsMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const { const Illusions::IllusionsGameDescription *gd = (const Illusions::IllusionsGameDescription *)desc; if (gd) { diff --git a/engines/illusions/duckman/gamestate_duckman.cpp b/engines/illusions/duckman/gamestate_duckman.cpp new file mode 100644 index 0000000000..8f4939f9dc --- /dev/null +++ b/engines/illusions/duckman/gamestate_duckman.cpp @@ -0,0 +1,50 @@ +/* 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 "illusions/duckman/gamestate_duckman.h" +#include "illusions/duckman/illusions_duckman.h" +#include "illusions/resources/scriptresource.h" + +namespace Illusions { + +Duckman_GameState::Duckman_GameState(IllusionsEngine_Duckman *vm) + : _vm(vm) { +} + +uint32 Duckman_GameState::calcWriteBufferSizeInternal() { + return + _vm->_scriptResource->_properties.getSize() + + _vm->_scriptResource->_blockCounters.getSize(); +} + +bool Duckman_GameState::readStateInternal(Common::ReadStream *in) { + return + _vm->_scriptResource->_properties.readFromStream(in) && + _vm->_scriptResource->_blockCounters.readFromStream(in); +} + +void Duckman_GameState::writeStateInternal(Common::WriteStream *out) { + _vm->_scriptResource->_properties.writeToStream(out); + _vm->_scriptResource->_blockCounters.writeToStream(out); +} + +} // End of namespace Illusions diff --git a/engines/illusions/duckman/gamestate_duckman.h b/engines/illusions/duckman/gamestate_duckman.h new file mode 100644 index 0000000000..36024c8d8e --- /dev/null +++ b/engines/illusions/duckman/gamestate_duckman.h @@ -0,0 +1,44 @@ +/* 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. + * + */ + +#ifndef ILLUSIONS_DUCKMAN_GAMESTATE_DUCKMAN_H +#define ILLUSIONS_DUCKMAN_GAMESTATE_DUCKMAN_H + +#include "illusions/gamestate.h" + +namespace Illusions { + +class IllusionsEngine_Duckman; + +class Duckman_GameState : public GameState { +public: + Duckman_GameState(IllusionsEngine_Duckman *vm); +protected: + IllusionsEngine_Duckman *_vm; + uint32 calcWriteBufferSizeInternal(); + bool readStateInternal(Common::ReadStream *in); + void writeStateInternal(Common::WriteStream *out); +}; + +} // End of namespace Illusions + +#endif // ILLUSIONS_DUCKMAN_GAMESTATE_DUCKMAN_H diff --git a/engines/illusions/duckman/illusions_duckman.cpp b/engines/illusions/duckman/illusions_duckman.cpp index ebfcdca4bd..c9233ae6d1 100644 --- a/engines/illusions/duckman/illusions_duckman.cpp +++ b/engines/illusions/duckman/illusions_duckman.cpp @@ -23,6 +23,7 @@ #include "illusions/duckman/illusions_duckman.h" #include "illusions/duckman/duckman_dialog.h" #include "illusions/duckman/duckman_specialcode.h" +#include "illusions/duckman/gamestate_duckman.h" #include "illusions/duckman/menusystem_duckman.h" #include "illusions/duckman/scriptopcodes_duckman.h" #include "illusions/actor.h" @@ -114,6 +115,7 @@ Common::Error IllusionsEngine_Duckman::run() { _updateFunctions = new UpdateFunctions(); _soundMan = new SoundMan(this); _menuSystem = new DuckmanMenuSystem(this); + _gameState = new Duckman_GameState(this); _fader = new Fader(); @@ -153,10 +155,7 @@ Common::Error IllusionsEngine_Duckman::run() { _resSys->loadResource(0x120001, 0x00010001, 0); _resSys->loadResource(0x120002, 0x00010001, 0); _resSys->loadResource(0x120003, 0x00010001, 0); - _resSys->loadResource(0x000D0001, 0x00010001, 0); - startScriptThread(0x00020004, 0); - _doScriptThreadInit = true; #if 0 //DEBUG @@ -174,13 +173,28 @@ Common::Error IllusionsEngine_Duckman::run() { _scriptResource->_blockCounters.set(238, 1); #endif -#if 1 +#if 0 // DEBUG Map / special code 0016001A _scriptResource->_properties.set(0x000E0017, true); _scriptResource->_properties.set(0x000E0022, false); #endif + if (ConfMan.hasKey("save_slot")) { + _doScriptThreadInit = true; + // Load global resources manually, usually done by the game script + enterScene(0x00010003, 0); + loadGameState(ConfMan.getInt("save_slot")); + } else { + startScriptThread(0x00020004, 0); + _doScriptThreadInit = true; + } + while (!shouldQuit()) { + if (_resumeFromSavegameRequested) { + activateSavegame(0); + resumeFromSavegame(0); + _resumeFromSavegameRequested = false; + } runUpdateFunctions(); _system->updateScreen(); updateEvents(); @@ -195,6 +209,7 @@ Common::Error IllusionsEngine_Duckman::run() { delete _fader; + delete _gameState; delete _menuSystem; delete _soundMan; delete _updateFunctions; @@ -218,12 +233,9 @@ Common::Error IllusionsEngine_Duckman::run() { bool IllusionsEngine_Duckman::hasFeature(EngineFeature f) const { return - false; - /* (f == kSupportsRTL) || (f == kSupportsLoadingDuringRuntime) || (f == kSupportsSavingDuringRuntime); - */ } void IllusionsEngine_Duckman::initInput() { @@ -793,7 +805,7 @@ bool IllusionsEngine_Duckman::changeScene(uint32 sceneId, uint32 threadId, uint3 _controls->destroyControls(); _resSys->unloadSceneResources(0x10003, 0x10001); if (enterScene(sceneId, threadId)) { - // TODO GameStates_writeStates(sceneId, threadId); + _gameState->writeState(sceneId, threadId); return true; } return false; @@ -1075,23 +1087,24 @@ uint32 IllusionsEngine_Duckman::runTriggerCause(uint32 verbId, uint32 objectId2, if (!getTriggerCause(verbId, objectId2, objectId, triggerThreadId)) return 0; - bool flag = false; + // TODO Extract sound effect playing to method + bool soundWasPlayed = false; if (_scriptResource->_properties.get(0x000E003C)) { if (verbId == 7 && objectId == 0x40003) { playSoundEffect(7); - flag = true; + soundWasPlayed = true; } else if (objectId == 0x40003) { playSoundEffect(14); - flag = true; + soundWasPlayed = true; } else if (verbId == 3) { playSoundEffect(16); - flag = true; + soundWasPlayed = true; } else if (verbId == 2) { - flag = true; + soundWasPlayed = true; } } - if (!flag) { + if (!soundWasPlayed) { if (objectId == 0x40003) { playSoundEffect(14); } else if ((verbId == 1 || verbId == 2) && _scriptResource->getMainActorObjectId() == objectId) { @@ -1120,34 +1133,33 @@ uint32 IllusionsEngine_Duckman::runTriggerCause(uint32 verbId, uint32 objectId2, return tempThreadId; } -bool IllusionsEngine_Duckman::loadSavegame(int16 slotNum, uint32 callingThreadId) { -#if 0 - // TODO - bool success = _gameStates->load(slotNum); - if (success) { - _vm->_screen->setDisplayOn(false); - uint32 currSceneId = getCurrentScene(); - if (currSceneId != 0x10003) - dumpCurrSceneFiles(currSceneId, callerThreadId); - reset(); - stopMidi(); - clearMidiPlayList(); - _gameStates->readStates(); - pushActiveScene(0x10000); - } - _gameStates->freeGameStateReadBuffer(); +bool IllusionsEngine_Duckman::loadSavegameFromScript(int16 slotNum, uint32 callingThreadId) { + const char *fileName = getSavegameFilename(slotNum); + bool success = loadgame(fileName); + if (success) + activateSavegame(callingThreadId); + _gameState->deleteReadStream(); return success; -#endif - return true; } -bool IllusionsEngine_Duckman::saveSavegame(int16 slotNum, uint32 callingThreadId) { -#if 0 +bool IllusionsEngine_Duckman::saveSavegameFromScript(int16 slotNum, uint32 callingThreadId) { // TODO - bool success = _gameStates->save(slotNum); + const char *fileName = getSavegameFilename(slotNum); + bool success = false;//savegame(fileName, _savegameDescription.c_str()); return success; -#endif - return true; +} + +void IllusionsEngine_Duckman::activateSavegame(uint32 callingThreadId) { + // TODO _screen->setDisplayOn(false); + uint32 currSceneId = getCurrentScene(); + if (currSceneId != 0x10003) + dumpCurrSceneFiles(currSceneId, callingThreadId); + reset(); + // TODO stopMidi(); + // TODO clearMidiPlayList(); + _gameState->readState(_savegameSceneId, _savegameThreadId); + pushActiveScene(0x10000); + _gameState->deleteReadStream(); } } // End of namespace Illusions diff --git a/engines/illusions/duckman/illusions_duckman.h b/engines/illusions/duckman/illusions_duckman.h index 87520d3a6a..1825123e5c 100644 --- a/engines/illusions/duckman/illusions_duckman.h +++ b/engines/illusions/duckman/illusions_duckman.h @@ -187,8 +187,9 @@ public: bool getTriggerCause(uint32 verbId, uint32 objectId2, uint32 objectId, uint32 &outThreadId); uint32 runTriggerCause(uint32 verbId, uint32 objectId2, uint32 objectId); - bool loadSavegame(int16 slotNum, uint32 callingThreadId); - bool saveSavegame(int16 slotNum, uint32 callingThreadId); + bool loadSavegameFromScript(int16 slotNum, uint32 callingThreadId); + bool saveSavegameFromScript(int16 slotNum, uint32 callingThreadId); + void activateSavegame(uint32 callingThreadId); }; diff --git a/engines/illusions/duckman/scriptopcodes_duckman.cpp b/engines/illusions/duckman/scriptopcodes_duckman.cpp index a62a78b46b..1456cfc0ee 100644 --- a/engines/illusions/duckman/scriptopcodes_duckman.cpp +++ b/engines/illusions/duckman/scriptopcodes_duckman.cpp @@ -259,7 +259,7 @@ void ScriptOpcodes_Duckman::opUnloadResourcesBySceneId(ScriptThread *scriptThrea //static uint dsceneId = 0, dthreadId = 0; //static uint dsceneId = 0x00010008, dthreadId = 0x00020029;//Beginning in Jac -//static uint dsceneId = 0x0001000A, dthreadId = 0x00020043;//Home front +static uint dsceneId = 0x0001000A, dthreadId = 0x00020043;//Home front //static uint dsceneId = 0x0001000E, dthreadId = 0x0002007C; //static uint dsceneId = 0x00010012, dthreadId = 0x0002009D;//Paramount //static uint dsceneId = 0x00010020, dthreadId = 0x00020112;//Xmas @@ -276,7 +276,7 @@ void ScriptOpcodes_Duckman::opUnloadResourcesBySceneId(ScriptThread *scriptThrea //static uint dsceneId = 0x10002, dthreadId = 0x20001;//Debug menu, not supported //static uint dsceneId = 0x10044, dthreadId = 0x000202B8; // Starship Enterprise //static uint dsceneId = 0x00010039, dthreadId = 0x00020089; // Map -static uint dsceneId = 0x00010052, dthreadId = 0x00020347; // Credits +//static uint dsceneId = 0x00010052, dthreadId = 0x00020347; // Credits void ScriptOpcodes_Duckman::opChangeScene(ScriptThread *scriptThread, OpCall &opCall) { ARG_SKIP(2); @@ -348,12 +348,12 @@ void ScriptOpcodes_Duckman::opLeaveScene24(ScriptThread *scriptThread, OpCall &o void ScriptOpcodes_Duckman::opEnterDebugger(ScriptThread *scriptThread, OpCall &opCall) { // Used for debugging purposes in the original engine // This is not supported and only reachable by code not implemented here! - error("ScriptOpcodes_Duckman::opEnterDebugger() Debugger function called"); + //error("ScriptOpcodes_Duckman::opEnterDebugger() Debugger function called"); } void ScriptOpcodes_Duckman::opLeaveDebugger(ScriptThread *scriptThread, OpCall &opCall) { // See opEnterDebugger - error("ScriptOpcodes_Duckman::opLeaveDebugger() Debugger function called"); + //error("ScriptOpcodes_Duckman::opLeaveDebugger() Debugger function called"); } void ScriptOpcodes_Duckman::opDumpCurrentSceneFiles(ScriptThread *scriptThread, OpCall &opCall) { @@ -690,7 +690,7 @@ void ScriptOpcodes_Duckman::opLoadGame(ScriptThread *scriptThread, OpCall &opCal ARG_SKIP(2); ARG_INT16(bankNum) ARG_INT16(slotNum) - bool success = _vm->loadSavegame(slotNum, opCall._callerThreadId); + bool success = _vm->loadSavegameFromScript(slotNum, opCall._callerThreadId); _vm->_stack->push(success ? 1 : 0); } @@ -698,7 +698,7 @@ void ScriptOpcodes_Duckman::opSaveGame(ScriptThread *scriptThread, OpCall &opCal ARG_SKIP(2); ARG_INT16(bankNum) ARG_INT16(slotNum) - bool success = _vm->saveSavegame(slotNum, opCall._callerThreadId); + bool success = _vm->saveSavegameFromScript(slotNum, opCall._callerThreadId); _vm->_stack->push(success ? 1 : 0); } diff --git a/engines/illusions/gamestate.cpp b/engines/illusions/gamestate.cpp new file mode 100644 index 0000000000..5f6e17cc76 --- /dev/null +++ b/engines/illusions/gamestate.cpp @@ -0,0 +1,75 @@ +/* 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 "illusions/gamestate.h" + +namespace Illusions { + +GameState::GameState() + : _writeBufferSize(0), _writeBuffer(0), _readStream(0) { +} + +GameState::~GameState() { + free(_writeBuffer); +} + +bool GameState::readState(uint32 &sceneId, uint32 &threadId) { + sceneId = _readStream->readUint32LE(); + threadId = _readStream->readUint32LE(); + return readStateInternal(_readStream); +} + +void GameState::writeState(uint32 sceneId, uint32 threadId) { + Common::WriteStream *writeStream = newWriteStream(); + writeStream->writeUint32LE(sceneId); + writeStream->writeUint32LE(threadId); + writeStateInternal(writeStream); +} + +void GameState::read(Common::ReadStream *in) { + uint32 size = in->readUint32LE(); + _readStream = in->readStream(size); +} + +void GameState::write(Common::WriteStream *out) { + out->writeUint32LE(_writeBufferSize); + out->write(_writeBuffer, _writeBufferSize); +} + +void GameState::deleteReadStream() { + delete _readStream; + _readStream = 0; +} + +Common::WriteStream *GameState::newWriteStream() { + if (!_writeBufferSize == 0 || !_writeBuffer) { + _writeBufferSize = calcWriteBufferSize(); + _writeBuffer = (byte*)malloc(_writeBufferSize); + } + return new Common::MemoryWriteStream(_writeBuffer, _writeBufferSize); +} + +uint32 GameState::calcWriteBufferSize() { + return calcWriteBufferSizeInternal() + 4 + 4; +} + +} // End of namespace Illusions diff --git a/engines/illusions/gamestate.h b/engines/illusions/gamestate.h new file mode 100644 index 0000000000..5f279ff0c3 --- /dev/null +++ b/engines/illusions/gamestate.h @@ -0,0 +1,53 @@ +/* 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. + * + */ + +#ifndef ILLUSIONS_GAMESTATE_H +#define ILLUSIONS_GAMESTATE_H + +#include "common/file.h" +#include "common/memstream.h" + +namespace Illusions { + +class GameState { +public: + GameState(); + virtual ~GameState(); + bool readState(uint32 &sceneId, uint32 &threadId); + void writeState(uint32 sceneId, uint32 threadId); + void read(Common::ReadStream *in); + void write(Common::WriteStream *out); + void deleteReadStream(); +protected: + uint32 _writeBufferSize; + byte *_writeBuffer; + Common::SeekableReadStream *_readStream; + Common::WriteStream *newWriteStream(); + uint32 calcWriteBufferSize(); + virtual uint32 calcWriteBufferSizeInternal() = 0; + virtual bool readStateInternal(Common::ReadStream *in) = 0; + virtual void writeStateInternal(Common::WriteStream *out) = 0; +}; + +} // End of namespace Illusions + +#endif // ILLUSIONS_GAMESTATE_H diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp index c4af5b5449..e22ef0c316 100644 --- a/engines/illusions/illusions.cpp +++ b/engines/illusions/illusions.cpp @@ -66,6 +66,11 @@ IllusionsEngine::IllusionsEngine(OSystem *syst, const IllusionsGameDescription * _rerunThreads = false; + _isSaveAllowed = true; // TODO + _resumeFromSavegameRequested = false; + _savegameSceneId = 0; + _savegameThreadId = 0; + Engine::syncSoundSettings(); } diff --git a/engines/illusions/illusions.h b/engines/illusions/illusions.h index 9914936add..784277f872 100644 --- a/engines/illusions/illusions.h +++ b/engines/illusions/illusions.h @@ -76,6 +76,7 @@ class SpecialCode; class TalkInstanceList; class ThreadList; class UpdateFunctions; +class GameState; enum { kGameIdBBDOU = 1, @@ -102,6 +103,7 @@ public: ResourceSystem *_resSys; BaseResourceReader *_resReader; UpdateFunctions *_updateFunctions; + GameState *_gameState; void updateEvents(); @@ -132,7 +134,8 @@ public: uint32 _resGetTime; bool _unpauseControlActorFlag; uint32 _lastUpdateTime; - + + int _resumeFromSavegameRequested; uint32 _savegameSceneId; uint32 _savegameThreadId; @@ -201,8 +204,6 @@ public: uint32 value8, uint32 valueC, uint32 value10) = 0; virtual void resumeFromSavegame(uint32 callingThreadId) = 0; -#if 0 - // Savegame API enum kReadSaveHeaderError { @@ -229,15 +230,14 @@ public: bool canSaveGameStateCurrently() { return _isSaveAllowed; } Common::Error loadGameState(int slot); Common::Error saveGameState(int slot, const Common::String &description); - void savegame(const char *filename, const char *description); - void loadgame(const char *filename); + Common::Error removeGameState(int slot); + bool savegame(const char *filename, const char *description); + bool loadgame(const char *filename); const char *getSavegameFilename(int num); bool existsSavegame(int num); static Common::String getSavegameFilename(const Common::String &target, int num); static kReadSaveHeaderError readSaveHeader(Common::SeekableReadStream *in, bool loadThumbnail, SaveHeader &header); -#endif - }; } // End of namespace Illusions diff --git a/engines/illusions/module.mk b/engines/illusions/module.mk index a5aa86d8cc..ad61eee5c8 100644 --- a/engines/illusions/module.mk +++ b/engines/illusions/module.mk @@ -17,6 +17,7 @@ MODULE_OBJS := \ duckman/duckman_inventory.o \ duckman/duckman_screenshakereffects.o \ duckman/duckman_specialcode.o \ + duckman/gamestate_duckman.o \ duckman/illusions_duckman.o \ duckman/menusystem_duckman.o \ duckman/propertytimers.o \ @@ -24,6 +25,7 @@ MODULE_OBJS := \ fileresourcereader.o \ fixedpoint.o \ gamarchive.o \ + gamestate.o \ gamresourcereader.o \ graphics.o \ illusions.o \ @@ -39,6 +41,7 @@ MODULE_OBJS := \ resources/soundresource.o \ resources/talkresource.o \ resourcesystem.o \ + saveload.o \ screen.o \ screentext.o \ scriptstack.o \ diff --git a/engines/illusions/resources/scriptresource.cpp b/engines/illusions/resources/scriptresource.cpp index 3038a8694d..5472d28658 100644 --- a/engines/illusions/resources/scriptresource.cpp +++ b/engines/illusions/resources/scriptresource.cpp @@ -50,8 +50,8 @@ void Properties::init(uint count, byte *properties) { } void Properties::clear() { - uint size = (_count >> 3) + 1; - for (uint i = 0; i < size; ++i) + uint32 size = getSize(); + for (uint32 i = 0; i < size; ++i) _properties[i] = 0; } @@ -72,6 +72,24 @@ void Properties::set(uint32 propertyId, bool value) { _properties[index] &= ~mask; } +uint32 Properties::getSize() { + return (_count >> 3) + 1; +} + +void Properties::writeToStream(Common::WriteStream *out) { + const uint32 size = getSize(); + out->writeUint32LE(size); + out->write(_properties, size); +} + +bool Properties::readFromStream(Common::ReadStream *in) { + uint32 size = in->readUint32LE(); + if (size != getSize()) + return false; + in->read(_properties, size); + return true; +} + void Properties::getProperyPos(uint32 propertyId, uint &index, byte &mask) { propertyId &= 0xFFFF; index = propertyId >> 3; @@ -113,6 +131,24 @@ void BlockCounters::setC0(uint index, byte value) { _blockCounters[index - 1] = oldValue | (value & 0xC0); } +uint32 BlockCounters::getSize() { + return _count; +} + +void BlockCounters::writeToStream(Common::WriteStream *out) { + const uint32 size = getSize(); + out->writeUint32LE(size); + out->write(_blockCounters, size); +} + +bool BlockCounters::readFromStream(Common::ReadStream *in) { + uint32 size = in->readUint32LE(); + if (size != getSize()) + return false; + in->read(_blockCounters, size); + return true; +} + // TriggerCause void TriggerCause::load(Common::SeekableReadStream &stream) { diff --git a/engines/illusions/resources/scriptresource.h b/engines/illusions/resources/scriptresource.h index 6debcb2efb..f185319f67 100644 --- a/engines/illusions/resources/scriptresource.h +++ b/engines/illusions/resources/scriptresource.h @@ -24,6 +24,7 @@ #define ILLUSIONS_SCRIPTRESOURCE_H #include "illusions/resourcesystem.h" +#include "common/file.h" namespace Illusions { @@ -46,6 +47,9 @@ public: void clear(); bool get(uint32 propertyId); void set(uint32 propertyId, bool value); + uint32 getSize(); + void writeToStream(Common::WriteStream *out); + bool readFromStream(Common::ReadStream *in); public: uint _count; byte *_properties; @@ -61,6 +65,9 @@ public: void set(uint index, byte value); byte getC0(uint index); void setC0(uint index, byte value); + uint32 getSize(); + void writeToStream(Common::WriteStream *out); + bool readFromStream(Common::ReadStream *in); public: uint _count; byte *_blockCounters; diff --git a/engines/illusions/saveload.cpp b/engines/illusions/saveload.cpp new file mode 100644 index 0000000000..751f6a600e --- /dev/null +++ b/engines/illusions/saveload.cpp @@ -0,0 +1,162 @@ +/* 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 "common/savefile.h" + +#include "graphics/thumbnail.h" + +#include "illusions/illusions.h" +#include "illusions/gamestate.h" + +namespace Illusions { + +#define ILLUSIONS_SAVEGAME_VERSION 0 + +IllusionsEngine::kReadSaveHeaderError IllusionsEngine::readSaveHeader(Common::SeekableReadStream *in, bool loadThumbnail, SaveHeader &header) { + + header.version = in->readUint32LE(); + if (header.version > ILLUSIONS_SAVEGAME_VERSION) + return kRSHEInvalidVersion; + + byte descriptionLen = in->readByte(); + header.description = ""; + while (descriptionLen--) + header.description += (char)in->readByte(); + + if (loadThumbnail) { + header.thumbnail = Graphics::loadThumbnail(*in); + } else { + Graphics::skipThumbnail(*in); + } + + // Not used yet, reserved for future usage + header.gameID = in->readByte(); + header.flags = in->readUint32LE(); + + header.saveDate = in->readUint32LE(); + header.saveTime = in->readUint32LE(); + header.playTime = in->readUint32LE(); + + return ((in->eos() || in->err()) ? kRSHEIoError : kRSHENoError); +} + +bool IllusionsEngine::savegame(const char *filename, const char *description) { + + Common::OutSaveFile *out; + if (!(out = g_system->getSavefileManager()->openForSaving(filename))) { + warning("Can't create file '%s', game not saved", filename); + return false; + } + + TimeDate curTime; + g_system->getTimeAndDate(curTime); + + // Header start + out->writeUint32LE(ILLUSIONS_SAVEGAME_VERSION); + + byte descriptionLen = strlen(description); + out->writeByte(descriptionLen); + out->write(description, descriptionLen); + + // TODO Probably pre-generate the thumbnail before the internal menu system is + // called to have a thumbnail without the menu system itself on it. + // Use the automatic thumbnail generation only when the ScummVM save dialog is used. + Graphics::saveThumbnail(*out); + + // Not used yet, reserved for future usage + out->writeByte(0); + out->writeUint32LE(0); + uint32 saveDate = ((curTime.tm_mday & 0xFF) << 24) | (((curTime.tm_mon + 1) & 0xFF) << 16) | ((curTime.tm_year + 1900) & 0xFFFF); + uint32 saveTime = ((curTime.tm_hour & 0xFF) << 16) | (((curTime.tm_min) & 0xFF) << 8) | ((curTime.tm_sec) & 0xFF); + uint32 playTime = g_engine->getTotalPlayTime() / 1000; + out->writeUint32LE(saveDate); + out->writeUint32LE(saveTime); + out->writeUint32LE(playTime); + // Header end + + _gameState->write(out); + + out->finalize(); + delete out; + return true; +} + +bool IllusionsEngine::loadgame(const char *filename) { + Common::InSaveFile *in; + if (!(in = g_system->getSavefileManager()->openForLoading(filename))) { + warning("Can't open file '%s', game not loaded", filename); + return false; + } + + SaveHeader header; + + kReadSaveHeaderError errorCode = readSaveHeader(in, false, header); + + if (errorCode != kRSHENoError) { + warning("Error loading savegame '%s'", filename); + delete in; + return false; + } + + g_engine->setTotalPlayTime(header.playTime * 1000); + + _gameState->read(in); + + delete in; + return true; +} + +Common::Error IllusionsEngine::loadGameState(int slot) { + _resumeFromSavegameRequested = false; + const char *fileName = getSavegameFilename(slot); + if (!loadgame(fileName)) + return Common::kReadingFailed; + _resumeFromSavegameRequested = true; + return Common::kNoError; +} + +Common::Error IllusionsEngine::saveGameState(int slot, const Common::String &description) { + const char *fileName = getSavegameFilename(slot); + if (!savegame(fileName, description.c_str())) + return Common::kWritingFailed; + return Common::kNoError; +} + +Common::Error IllusionsEngine::removeGameState(int slot) { + Common::SaveFileManager *saveFileMan = g_system->getSavefileManager(); + Common::String filename = Illusions::IllusionsEngine::getSavegameFilename(_targetName, slot); + saveFileMan->removeSavefile(filename.c_str()); + return Common::kNoError; +} + +const char *IllusionsEngine::getSavegameFilename(int num) { + static Common::String filename; + filename = getSavegameFilename(_targetName, num); + return filename.c_str(); +} + +Common::String IllusionsEngine::getSavegameFilename(const Common::String &target, int num) { + assert(num >= 0 && num <= 999); + return Common::String::format("%s.%03d", target.c_str(), num); +} + +} // End of namespace Illusions diff --git a/engines/illusions/screen.cpp b/engines/illusions/screen.cpp index b9506f63d4..a4a3d27430 100644 --- a/engines/illusions/screen.cpp +++ b/engines/illusions/screen.cpp @@ -635,10 +635,10 @@ void Screen::drawSurface82(Common::Rect &dstRect, Graphics::Surface *surface, Co const int srcWidth = srcRect.width(), srcHeight = srcRect.height(); const int errYStart = srcHeight / dstHeight; const int errYIncr = srcHeight % dstHeight; - const int midY = dstHeight / 2; +// const int midY = dstHeight / 2; const int errXStart = srcWidth / dstWidth; const int errXIncr = srcWidth % dstWidth; - const int midX = dstWidth / 2; +// const int midX = dstWidth / 2; int h = dstHeight, errY = 0, skipY, srcY = srcRect.top; byte *dst = (byte*)_backSurface->getBasePtr(dstRect.left, dstRect.top); skipY = (dstHeight < srcHeight) ? 0 : dstHeight / (2*srcHeight) + 1; @@ -807,6 +807,8 @@ void Screen::drawSurface20(Common::Rect &dstRect, Graphics::Surface *surface, Co //debug("Screen::drawSurface20"); } +//#define TEST_SMOOTHING +#ifdef TEST_SMOOTHING static uint16 average(const uint16 a, const uint16 b) { byte r1, g1, b1, r2, g2, b2; g_system->getScreenFormat().colorToRGB(a, r1, g1, b1); @@ -814,6 +816,7 @@ static uint16 average(const uint16 a, const uint16 b) { // return g_system->getScreenFormat().RGBToColor((r1 + r1 + r2) / 3, (g1 + g1 + g2) / 3, (b1 + b1 + b2) / 3); return g_system->getScreenFormat().RGBToColor((r1 + r2) / 2, (g1 + g2) / 2, (b1 + b2) / 2); } +#endif void Screen::drawSurface21(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect) { // Scaled @@ -821,10 +824,10 @@ void Screen::drawSurface21(Common::Rect &dstRect, Graphics::Surface *surface, Co const int srcWidth = srcRect.width(), srcHeight = srcRect.height(); const int errYStart = srcHeight / dstHeight; const int errYIncr = srcHeight % dstHeight; - const int midY = dstHeight / 2; +// const int midY = dstHeight / 2; const int errXStart = srcWidth / dstWidth; const int errXIncr = srcWidth % dstWidth; - const int midX = dstWidth / 2; +// const int midX = dstWidth / 2; int h = dstHeight, errY = 0, skipY, srcY = srcRect.top; byte *dst = (byte*)_backSurface->getBasePtr(dstRect.left, dstRect.top); skipY = (dstHeight < srcHeight) ? 0 : dstHeight / (2*srcHeight) + 1; @@ -838,12 +841,14 @@ void Screen::drawSurface21(Common::Rect &dstRect, Graphics::Surface *surface, Co while (w-- > 0) { uint16 pixel = READ_LE_UINT16(src); if (pixel != _colorKey1) { +#ifdef TEST_SMOOTHING if (errX >= midX) { uint16 npixel = READ_LE_UINT16(src + 2); if (npixel == _colorKey1) npixel = READ_LE_UINT16(dstRow); pixel = average(pixel, npixel); } +#endif WRITE_LE_UINT16(dstRow, pixel); } dstRow += 2; -- cgit v1.2.3