diff options
-rw-r--r-- | engines/mortevielle/detection.cpp | 27 | ||||
-rw-r--r-- | engines/mortevielle/disk.cpp | 157 | ||||
-rw-r--r-- | engines/mortevielle/module.mk | 2 | ||||
-rw-r--r-- | engines/mortevielle/mortevielle.cpp | 181 | ||||
-rw-r--r-- | engines/mortevielle/mortevielle.h | 9 | ||||
-rw-r--r-- | engines/mortevielle/prog.cpp | 105 | ||||
-rw-r--r-- | engines/mortevielle/prog.h | 2 | ||||
-rw-r--r-- | engines/mortevielle/saveload.cpp | 318 | ||||
-rw-r--r-- | engines/mortevielle/saveload.h (renamed from engines/mortevielle/disk.h) | 39 |
9 files changed, 563 insertions, 277 deletions
diff --git a/engines/mortevielle/detection.cpp b/engines/mortevielle/detection.cpp index ec0b01622a..35e5cada81 100644 --- a/engines/mortevielle/detection.cpp +++ b/engines/mortevielle/detection.cpp @@ -25,6 +25,7 @@ #include "mortevielle/mortevielle.h" #include "mortevielle/detection_tables.h" +#include "mortevielle/saveload.h" namespace Mortevielle { uint32 MortevielleEngine::getGameFlags() const { return _gameDescription->flags; } @@ -52,6 +53,9 @@ public: virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const; virtual bool hasFeature(MetaEngineFeature f) const; + virtual int getMaximumSaveSlot() const; + virtual SaveStateList listSaves(const char *target) const; + virtual SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const; }; bool MortevielleMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const { @@ -62,9 +66,30 @@ bool MortevielleMetaEngine::createInstance(OSystem *syst, Engine **engine, const } bool MortevielleMetaEngine::hasFeature(MetaEngineFeature f) const { - return false; + switch (f) { + case kSupportsListSaves: + case kSupportsDeleteSave: + case kSupportsLoadingDuringStartup: + case kSavesSupportMetaInfo: + case kSavesSupportThumbnail: + case kSavesSupportCreationDate: + return true; + default: + return false; + } +} + +int MortevielleMetaEngine::getMaximumSaveSlot() const { return 99; } + +SaveStateList MortevielleMetaEngine::listSaves(const char *target) const { + return Mortevielle::SavegameManager::listSaves(target); } +SaveStateDescriptor MortevielleMetaEngine::querySaveMetaInfos(const char *target, int slot) const { + return Mortevielle::SavegameManager::querySaveMetaInfos(slot); +} + + #if PLUGIN_ENABLED_DYNAMIC(MORTEVIELLE) REGISTER_PLUGIN_DYNAMIC(MORTEVIELLE, PLUGIN_TYPE_ENGINE, MortevielleMetaEngine); #else diff --git a/engines/mortevielle/disk.cpp b/engines/mortevielle/disk.cpp deleted file mode 100644 index 25bf7faca3..0000000000 --- a/engines/mortevielle/disk.cpp +++ /dev/null @@ -1,157 +0,0 @@ -/* 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. - * - */ - -/* - * This code is based on original Mortville Manor DOS source code - * Copyright (c) 1988-1989 Lankhor - */ - -#include "common/file.h" -#include "common/system.h" -#include "common/savefile.h" -#include "common/serializer.h" -#include "mortevielle/alert.h" -#include "mortevielle/disk.h" -#include "mortevielle/mor.h" -#include "mortevielle/mor2.h" -#include "mortevielle/mouse.h" -#include "mortevielle/ovd1.h" -#include "mortevielle/prog.h" -#include "mortevielle/var_mor.h" - -namespace Mortevielle { - -/** - * Ensure disk 1 data is available - */ -void dem1() { -/* Deprecated in ScummVM - int k; - - // -- demande de disk 1 -- //Translation: Ask for disk #1 - assign(f, "mort.005"); - //*$i-* - k = ioresult; - reset(f); - while (ioresult != 0) { - show_mouse(); - k = do_alert(al_mess, 1); - hide_mouse(); - reset(f); - } - close(f); - */ -} - -/** - * Handle saving or loading savegame data - */ -static void sync_save(Common::Serializer &sz) { - sz.syncAsSint16LE(s1.conf); - sz.syncBytes((byte *)&s1.pourc[0], 11); - sz.syncBytes((byte *)&s1.teauto[0], 43); - sz.syncBytes((byte *)&s1.sjer[0], 31); - sz.syncAsSint16LE(s1.mlieu); - sz.syncAsSint16LE(s1.iboul); - sz.syncAsSint16LE(s1.ibag); - sz.syncAsSint16LE(s1.icave); - sz.syncAsSint16LE(s1.ivier); - sz.syncAsSint16LE(s1.ipuit); - sz.syncAsSint16LE(s1.derobj); - sz.syncAsSint16LE(s1.iloic); - sz.syncAsSint16LE(s1.icryp); - sz.syncAsByte(s1.ipre); - sz.syncAsByte(s1.heure); - - sz.syncBytes(bufcha, 391); -} - -void takesav(int n) { - int i; - Common::String st; - - dem1(); - // -- Load the file 'sauve#n.mor' - Common::String saveName = Common::String::format("sav%d.mor", n); - - // Try loading first from the save area - Common::SeekableReadStream *stream = g_system->getSavefileManager()->openForLoading(saveName); - - // If not present, try loading from the program folder - Common::File f; - if (stream == NULL) { - if (!f.open(saveName)) - error("Unable to open save file '%s'", saveName); - - stream = f.readStream(f.size()); - f.close(); - } - - Common::Serializer sz(stream, NULL); - sync_save(sz); - - s = s1; - for (i = 0; i <= 389; i ++) tabdon[i + acha] = bufcha[i]; - - // Close the stream - delete stream; -} - -void ld_game(int n) { - hide_mouse(); - maivid(); - takesav(n); - /* -- disquette 2 -- */ //Translation: Floppy #2 - dem2(); - /* -- mises en place -- */ //Translation: Initialization - theure(); - dprog(); - antegame(); - show_mouse(); -} - -void sv_game(int n) { - Common::OutSaveFile *f; - int i; - - hide_mouse(); - tmaj3(); - dem1(); - /* -- sauvegarde du fichier 'sauve#n.mor' -- */ //Translation: save file 'sauve%d.mor' - for (i = 0; i <= 389; i ++) bufcha[i] = tabdon[i + acha]; - s1 = s; - if (s1.mlieu == 26) s1.mlieu = 15; - - Common::String saveName = Common::String::format("sav%d.mor", n); - f = g_system->getSavefileManager()->openForSaving(saveName); - - Common::Serializer sz(NULL, f); - sync_save(sz); - - f->finalize(); - delete f; - - dem2(); - show_mouse(); -} - -} // End of namespace Mortevielle diff --git a/engines/mortevielle/module.mk b/engines/mortevielle/module.mk index 2d16106e0c..28c6c7fc6d 100644 --- a/engines/mortevielle/module.mk +++ b/engines/mortevielle/module.mk @@ -6,7 +6,6 @@ MODULE_OBJS := \ asm.o \ boite.o \ detection.o \ - disk.o \ droite.o \ graphics.o \ keyboard.o \ @@ -22,6 +21,7 @@ MODULE_OBJS := \ parole2.o \ prog.o \ ques.o \ + saveload.o \ sound.o \ sprint.o \ taffich.o \ diff --git a/engines/mortevielle/mortevielle.cpp b/engines/mortevielle/mortevielle.cpp index 35d71c92aa..cbe2613716 100644 --- a/engines/mortevielle/mortevielle.cpp +++ b/engines/mortevielle/mortevielle.cpp @@ -21,6 +21,7 @@ */ #include "common/system.h" +#include "common/config-manager.h" #include "common/debug-channels.h" #include "engines/util.h" #include "engines/engine.h" @@ -28,16 +29,20 @@ #include "graphics/palette.h" #include "graphics/pixelformat.h" #include "mortevielle/mortevielle.h" +#include "mortevielle/actions.h" +#include "mortevielle/alert.h" #include "mortevielle/asm.h" -#include "mortevielle/disk.h" #include "mortevielle/keyboard.h" #include "mortevielle/level15.h" +#include "mortevielle/menu.h" #include "mortevielle/mor.h" #include "mortevielle/mor2.h" #include "mortevielle/mouse.h" #include "mortevielle/ovd1.h" #include "mortevielle/parole2.h" #include "mortevielle/prog.h" +#include "mortevielle/saveload.h" +#include "mortevielle/taffich.h" #include "mortevielle/var_mor.h" namespace Mortevielle { @@ -50,15 +55,58 @@ MortevielleEngine::MortevielleEngine(OSystem *system, const ADGameDescription *g g_vm = this; _lastGameFrame = 0; _mouseClick = false; + _inMainGameLoop = false; } MortevielleEngine::~MortevielleEngine() { } +/** + * Specifies whether the engine supports given features + */ bool MortevielleEngine::hasFeature(EngineFeature f) const { - return false; + return + (f == kSupportsRTL) || + (f == kSupportsLoadingDuringRuntime) || + (f == kSupportsSavingDuringRuntime); +} + +/** + * Return true if a game can currently be loaded + */ +bool MortevielleEngine::canLoadGameStateCurrently() { + // Saving is only allowed in the main game event loop + return _inMainGameLoop; } +/** + * Return true if a game can currently be saved + */ +bool MortevielleEngine::canSaveGameStateCurrently() { + // Loading is only allowed in the main game event loop + return _inMainGameLoop; +} + +/** + * Load in a savegame at the specified slot number + */ +Common::Error MortevielleEngine::loadGameState(int slot) { + return _savegameManager.loadGame(slot); +} + +/** + * Save the current game + */ +Common::Error MortevielleEngine::saveGameState(int slot, const Common::String &desc) { + if (slot == 0) + return Common::kWritingFailed; + + return _savegameManager.saveGame(slot, desc); +} + +/** + * Initialise the game state + */ Common::ErrorCode MortevielleEngine::initialise() { // Initialise graphics mode initGraphics(SCREEN_WIDTH, SCREEN_HEIGHT, true); @@ -361,12 +409,23 @@ Common::Error MortevielleEngine::run() { if (err != Common::kNoError) return err; - // Show the game introduction - showIntroduction(); + // Check for a savegame + int loadSlot = 0; + if (ConfMan.hasKey("save_slot")) { + int gameToLoad = ConfMan.getInt("save_slot"); + if (gameToLoad >= 1 && gameToLoad <= 999) + loadSlot = gameToLoad; + } + + if (loadSlot == 0) + // Show the game introduction + showIntroduction(); + // Either load the initial game state savegame, or the specified savegame number adzon(); - takesav(0); + _savegameManager.takesav(loadSlot); + // Run the main game loop mainGame(); return Common::kNoError; @@ -425,4 +484,116 @@ void MortevielleEngine::mainGame() { } while (!arret); } +/** + * This method handles repeatedly calling a sub-method to wait for a user action and then handling it + */ +void MortevielleEngine::tjouer() { + antegame(); + do { + tecran(); + CHECK_QUIT; + } while (!((arret) || (solu) || (perdu))); + if (solu) + tmaj1(); + else if (perdu) + tencore(); +} + +/** + * Waits for the user to select an action, and then handles it + */ +void MortevielleEngine::tecran() { + const char idem[] = "Idem"; + const int lim = 20000; + int temps = 0; + char inkey = '\0'; + bool oo, funct = 0; + + clsf3(); + oo = false; + ctrm = 0; + if (! iesc) { + draw_menu(); + imen = true; + temps = 0; + key = 0; + funct = false; + inkey = '.'; + + _inMainGameLoop = true; + do { + mdn(); + tinke(); + mov_mouse(funct, inkey); + CHECK_QUIT; + temps = temps + 1; + } while (!((choisi) || (temps > lim) || (funct) || (anyone))); + _inMainGameLoop = false; + + erase_menu(); + imen = false; + if ((inkey == '\1') || (inkey == '\3') || (inkey == '\5') || (inkey == '\7') || (inkey == '\11')) { + change_gd((uint)pred(int, ord(inkey)) >> 1); + return; + } + if (choisi && (msg[3] == sauve)) { + Common::String saveName = Common::String::format("Savegame #%d", msg[4] & 7); + g_vm->_savegameManager.saveGame(msg[4] & 7, saveName); + } + if (choisi && (msg[3] == charge)) + g_vm->_savegameManager.loadGame((msg[4] & 7) - 1); + if (inkey == '\103') { /* F9 */ + temps = do_alert(stpou, 1); + return; + } else if (inkey == '\77') { + if ((mnumo != no_choice) && ((msg[3] == action) || (msg[3] == saction))) { + msg[4] = mnumo; + ecr3(idem); + } else return; + } else if (inkey == '\104') { + if ((x != 0) && (y != 0)) num = 9999; + return; + } + } + if (inkey == '\73') { + arret = true; + tmaj3(); + } else { + if ((funct) && (inkey != '\77')) return; + if (temps > lim) { + repon(2, 141); + if (num == 9999) num = 0; + } else { + mnumo = msg[3]; + if ((msg[3] == action) || (msg[3] == saction)) mnumo = msg[4]; + if (! anyone) { + if ((fouil) || (obpart)) { + if (y_s < 12) return; + if ((msg[4] == sonder) || (msg[4] == soulever)) { + oo = true; + if ((msg[4] == soulever) || (obpart)) { + finfouil(); + caff = s.mlieu; + crep = 998; + } else tsuiv(); + mennor(); + } + } + } + do { + if (! oo) tsitu(); + if ((ctrm == 0) && (! perdu) && (! solu)) { + taffich(); + if (okdes) { + okdes = false; + dessin(0); + } + if ((! syn) || (col)) repon(2, crep); + } + } while (!(! syn)); + if (ctrm != 0) tctrm(); + } + } +} + } // End of namespace Mortevielle diff --git a/engines/mortevielle/mortevielle.h b/engines/mortevielle/mortevielle.h index 863cbdb95e..6dad5a0a79 100644 --- a/engines/mortevielle/mortevielle.h +++ b/engines/mortevielle/mortevielle.h @@ -33,6 +33,7 @@ #include "common/error.h" #include "graphics/surface.h" #include "mortevielle/graphics.h" +#include "mortevielle/saveload.h" #include "mortevielle/sound.h" namespace Mortevielle { @@ -56,6 +57,7 @@ private: uint32 _lastGameFrame; bool _mouseClick; Common::Point _mousePos; + bool _inMainGameLoop; Common::ErrorCode initialise(); Common::ErrorCode loadMortDat(); @@ -65,6 +67,8 @@ private: void initMouse(); void showIntroduction(); void mainGame(); + void tjouer(); + void tecran(); void divers(int np, bool b); public: ScreenSurface _screenSurface; @@ -72,10 +76,15 @@ public: GfxSurface _backgroundSurface; Common::RandomSource _randomSource; SoundManager _soundManager; + SavegameManager _savegameManager; public: MortevielleEngine(OSystem *system, const ADGameDescription *gameDesc); ~MortevielleEngine(); virtual bool hasFeature(EngineFeature f) const; + virtual bool canLoadGameStateCurrently(); + virtual bool canSaveGameStateCurrently(); + virtual Common::Error loadGameState(int slot); + virtual Common::Error saveGameState(int slot, const Common::String &desc); virtual Common::Error run(); uint32 getGameFlags() const; diff --git a/engines/mortevielle/prog.cpp b/engines/mortevielle/prog.cpp index 5f2fd3cb65..ba2c4ded91 100644 --- a/engines/mortevielle/prog.cpp +++ b/engines/mortevielle/prog.cpp @@ -219,100 +219,6 @@ L2: mennor(); } -void sv_game(int n); - -void ld_game(int n); - -void tecran() { - const char idem[] = "Idem"; - const int lim = 20000; - int temps = 0; - char inkey = '\0'; - bool oo, funct = 0; - - clsf3(); - oo = false; - ctrm = 0; - if (! iesc) { - draw_menu(); - imen = true; - temps = 0; - key = 0; - funct = false; - inkey = '.'; - - do { - mdn(); - tinke(); - mov_mouse(funct, inkey); - CHECK_QUIT; - temps = temps + 1; - } while (!((choisi) || (temps > lim) || (funct) || (anyone))); - - erase_menu(); - imen = false; - if ((inkey == '\1') || (inkey == '\3') || (inkey == '\5') || (inkey == '\7') || (inkey == '\11')) { - change_gd((uint)pred(int, ord(inkey)) >> 1); - return; - } - if (choisi && (msg[3] == sauve)) - sv_game(msg[4] & 7); - if (choisi && (msg[3] == charge)) - ld_game((msg[4] & 7) - 1); - if (inkey == '\103') { /* F9 */ - temps = do_alert(stpou, 1); - return; - } else if (inkey == '\77') { - if ((mnumo != no_choice) && ((msg[3] == action) || (msg[3] == saction))) { - msg[4] = mnumo; - ecr3(idem); - } else return; - } else if (inkey == '\104') { - if ((x != 0) && (y != 0)) num = 9999; - return; - } - } - if (inkey == '\73') { - arret = true; - tmaj3(); - } else { - if ((funct) && (inkey != '\77')) return; - if (temps > lim) { - repon(2, 141); - if (num == 9999) num = 0; - } else { - mnumo = msg[3]; - if ((msg[3] == action) || (msg[3] == saction)) mnumo = msg[4]; - if (! anyone) { - if ((fouil) || (obpart)) { - if (y_s < 12) return; - if ((msg[4] == sonder) || (msg[4] == soulever)) { - oo = true; - if ((msg[4] == soulever) || (obpart)) { - finfouil(); - caff = s.mlieu; - crep = 998; - } else tsuiv(); - mennor(); - } - } - } - do { - if (! oo) tsitu(); - if ((ctrm == 0) && (! perdu) && (! solu)) { - taffich(); - if (okdes) { - okdes = false; - dessin(0); - } - if ((! syn) || (col)) repon(2, crep); - } - } while (!(! syn)); - if (ctrm != 0) tctrm(); - } - } -} - /* NIVEAU 1 */ void theure() { @@ -326,15 +232,4 @@ void theure() { else min = 0; } - -void tjouer() { - antegame(); - do { - tecran(); - CHECK_QUIT; - } while (!((arret) || (solu) || (perdu))); - if (solu) tmaj1(); - else if (perdu) tencore(); -} - } // End of namespace Mortevielle diff --git a/engines/mortevielle/prog.h b/engines/mortevielle/prog.h index fbcd92f469..f51b2214b7 100644 --- a/engines/mortevielle/prog.h +++ b/engines/mortevielle/prog.h @@ -37,11 +37,9 @@ extern void antegame(); /* procedure PROGRAMME */ extern void tmaj3(); extern void tsitu(); -extern void tecran(); /* NIVEAU 1 */ extern void theure(); -extern void tjouer(); } // End of namespace Mortevielle #endif diff --git a/engines/mortevielle/saveload.cpp b/engines/mortevielle/saveload.cpp new file mode 100644 index 0000000000..d0cd603845 --- /dev/null +++ b/engines/mortevielle/saveload.cpp @@ -0,0 +1,318 @@ +/* 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. + * + */ + +/* + * This code is based on original Mortville Manor DOS source code + * Copyright (c) 1988-1989 Lankhor + */ + +#include "common/file.h" +#include "common/system.h" +#include "mortevielle/alert.h" +#include "mortevielle/mor.h" +#include "mortevielle/mor2.h" +#include "mortevielle/mortevielle.h" +#include "mortevielle/mouse.h" +#include "mortevielle/ovd1.h" +#include "mortevielle/prog.h" +#include "mortevielle/saveload.h" +#include "mortevielle/var_mor.h" + +namespace Mortevielle { + +static const char SAVEGAME_ID[4] = { 'M', 'O', 'R', 'T' }; + +Common::String SavegameManager::generateSaveName(int slotNumber) { + return Common::String::format("sav%d.mor", slotNumber); +} + +/** + * Handle saving or loading savegame data + */ +void SavegameManager::sync_save(Common::Serializer &sz) { + sz.syncAsSint16LE(s1.conf); + sz.syncBytes((byte *)&s1.pourc[0], 11); + sz.syncBytes((byte *)&s1.teauto[0], 43); + sz.syncBytes((byte *)&s1.sjer[0], 31); + sz.syncAsSint16LE(s1.mlieu); + sz.syncAsSint16LE(s1.iboul); + sz.syncAsSint16LE(s1.ibag); + sz.syncAsSint16LE(s1.icave); + sz.syncAsSint16LE(s1.ivier); + sz.syncAsSint16LE(s1.ipuit); + sz.syncAsSint16LE(s1.derobj); + sz.syncAsSint16LE(s1.iloic); + sz.syncAsSint16LE(s1.icryp); + sz.syncAsByte(s1.ipre); + sz.syncAsByte(s1.heure); + + sz.syncBytes(bufcha, 391); +} + +/** + * Inner code for loading a saved game + */ +void SavegameManager::takesav(int n) { + int i; + Common::String st; + + // -- Load the file + Common::String filename = generateSaveName(n); + + // Try loading first from the save area + Common::SeekableReadStream *stream = g_system->getSavefileManager()->openForLoading(filename); + + // If not present, try loading from the program folder + Common::File f; + if (stream == NULL) { + if (!f.open(filename)) + error("Unable to open save file '%s'", filename); + + stream = f.readStream(f.size()); + f.close(); + } + + // Check whether it's a ScummVM saved game + char buffer[4]; + stream->read(buffer, 4); + if (!strncmp(&buffer[0], &SAVEGAME_ID[0], 4)) { + // Yes, it is, so skip over the savegame header + SavegameHeader header; + readSavegameHeader(stream, header); + delete header.thumbnail; + } else { + stream->seek(0); + } + + // Read the game contents + Common::Serializer sz(stream, NULL); + sync_save(sz); + + s = s1; + for (i = 0; i <= 389; i ++) tabdon[i + acha] = bufcha[i]; + + // Close the stream + delete stream; +} + +/** + * Load a saved game + */ +Common::Error SavegameManager::loadGame(int n) { + hide_mouse(); + maivid(); + takesav(n); + + /* Initialization */ + theure(); + dprog(); + antegame(); + show_mouse(); + return Common::kNoError; +} + +/** + * Save the game + */ +Common::Error SavegameManager::saveGame(int n, const Common::String &saveName) { + Common::OutSaveFile *f; + int i; + + hide_mouse(); + tmaj3(); + + for (i = 0; i <= 389; i ++) + bufcha[i] = tabdon[i + acha]; + s1 = s; + if (s1.mlieu == 26) + s1.mlieu = 15; + + Common::String filename = generateSaveName(n); + f = g_system->getSavefileManager()->openForSaving(filename); + + // Write out the savegame header + f->write(&SAVEGAME_ID[0], 4); + + // Write out the header + SavegameHeader header; + writeSavegameHeader(f, saveName); + + // Write out the savegame contents + Common::Serializer sz(NULL, f); + sync_save(sz); + + // Close the save file + f->finalize(); + delete f; + + dem2(); + show_mouse(); + return Common::kNoError; +} + +void SavegameManager::writeSavegameHeader(Common::OutSaveFile *out, const Common::String &saveName) { + // Write out a savegame header + out->writeByte(SAVEGAME_VERSION); + + // Write savegame name + out->writeString(saveName); + out->writeByte(0); + + // Get the active palette + uint8 thumbPalette[256 * 3]; + g_system->getPaletteManager()->grabPalette(thumbPalette, 0, 256); + + // Create a thumbnail and save it + Graphics::Surface *thumb = new Graphics::Surface(); + Graphics::Surface s = g_vm->_screenSurface.lockArea(Common::Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)); + + ::createThumbnail(thumb, (const byte *)s.pixels, SCREEN_WIDTH, SCREEN_HEIGHT, thumbPalette); + Graphics::saveThumbnail(*out, *thumb); + thumb->free(); + delete thumb; + + // Write out the save date/time + TimeDate td; + g_system->getTimeAndDate(td); + out->writeSint16LE(td.tm_year + 1900); + out->writeSint16LE(td.tm_mon + 1); + out->writeSint16LE(td.tm_mday); + out->writeSint16LE(td.tm_hour); + out->writeSint16LE(td.tm_min); +} + +bool SavegameManager::readSavegameHeader(Common::InSaveFile *in, SavegameHeader &header) { + header.thumbnail = NULL; + + // Get the savegame version + header.version = in->readByte(); + + // Read in the save name + header.saveName.clear(); + char ch; + while ((ch = (char)in->readByte()) != '\0') + header.saveName += ch; + + // Get the thumbnail + header.thumbnail = Graphics::loadThumbnail(*in); + if (!header.thumbnail) + return false; + + // Read in save date/time + header.saveYear = in->readSint16LE(); + header.saveMonth = in->readSint16LE(); + header.saveDay = in->readSint16LE(); + header.saveHour = in->readSint16LE(); + header.saveMinutes = in->readSint16LE(); + + return true; +} + +SaveStateList SavegameManager::listSaves(const char *target) { + Common::String pattern = "sav*.mor"; + Common::StringArray files = g_system->getSavefileManager()->listSavefiles(pattern); + sort(files.begin(), files.end()); // Sort (hopefully ensuring we are sorted numerically..) + + SaveStateList saveList; + for (Common::StringArray::const_iterator file = files.begin(); file != files.end(); ++file) { + // Obtain the last 3 digits of the filename, since they correspond to the save slot + const Common::String &fname = *file; + int slotNumber = atoi(fname.c_str() + 3); + + Common::InSaveFile *in = g_system->getSavefileManager()->openForLoading(fname); + if (in) { + // There can be two types of savegames: original interpreter savegames, and ScummVM savegames. + // Original interpreter savegames are 497 bytes, and still need to be supported because the + // initial game state is stored as a savegame + bool validFlag = false; + Common::String saveDescription; + + char buffer[4]; + in->read(buffer, 4); + if (!strncmp(&buffer[0], &SAVEGAME_ID[0], 4)) { + // ScummVm savegame. Read in the header to get the savegame name + SavegameHeader header; + validFlag = readSavegameHeader(in, header); + + if (validFlag) { + delete header.thumbnail; + saveDescription = header.saveName; + } + } else if (file->size() == 497) { + // Form an appropriate savegame name + saveDescription = (slotNumber == 0) ? "Initial game state" : + Common::String::format("Savegame #%d", slotNumber); + validFlag = true; + } + + if (validFlag) + // Got a valid savegame + saveList.push_back(SaveStateDescriptor(slotNumber, saveDescription)); + + delete in; + } + } + + return saveList; +} + +SaveStateDescriptor SavegameManager::querySaveMetaInfos(int slot) { + Common::String fileName = Mortevielle::SavegameManager::generateSaveName(slot); + Common::InSaveFile *f = g_system->getSavefileManager()->openForLoading(fileName); + + if (f) { + // Check to see if it's a ScummVM savegame or not + char buffer[4]; + f->read(buffer, 4); + + bool hasHeader = !strncmp(&buffer[0], &SAVEGAME_ID[0], 4); + + if (!hasHeader) { + // Original savegame perhaps? + delete f; + + SaveStateDescriptor desc(slot, Common::String::format("Savegame #%d", slot)); + desc.setDeletableFlag(slot != 0); + desc.setWriteProtectedFlag(slot == 0); + return desc; + } else { + // Get the savegame header information + SavegameHeader header; + readSavegameHeader(f, header); + delete f; + + // Create the return descriptor + SaveStateDescriptor desc(slot, header.saveName); + desc.setDeletableFlag(true); + desc.setWriteProtectedFlag(false); + desc.setThumbnail(header.thumbnail); + desc.setSaveDate(header.saveYear, header.saveMonth, header.saveDay); + desc.setSaveTime(header.saveHour, header.saveMinutes); + + return desc; + } + } + + return SaveStateDescriptor(); +} + +} // End of namespace Mortevielle diff --git a/engines/mortevielle/disk.h b/engines/mortevielle/saveload.h index 06df8e029a..7f2edd8b53 100644 --- a/engines/mortevielle/disk.h +++ b/engines/mortevielle/saveload.h @@ -25,15 +25,42 @@ * Copyright (c) 1988-1989 Lankhor */ -#ifndef MORTEVIELLE_DISK_H -#define MORTEVIELLE_DISK_H +#ifndef MORTEVIELLE_SAVELOAD_H +#define MORTEVIELLE_SAVELOAD_H + +#include "common/savefile.h" +#include "common/serializer.h" +#include "graphics/palette.h" +#include "graphics/scaler.h" +#include "graphics/thumbnail.h" + +#define SAVEGAME_VERSION 1 namespace Mortevielle { -extern void dem1(); -extern void takesav(int n); -extern void ld_game(int n); -extern void sv_game(int n); +struct SavegameHeader { + uint8 version; + Common::String saveName; + Graphics::Surface *thumbnail; + int saveYear, saveMonth, saveDay; + int saveHour, saveMinutes; + int totalFrames; +}; + +class SavegameManager { +private: + void sync_save(Common::Serializer &sz); +public: + void takesav(int n); + Common::Error loadGame(int n); + Common::Error saveGame(int n, const Common::String &saveName); + + static void writeSavegameHeader(Common::OutSaveFile *out, const Common::String &saveName); + static bool readSavegameHeader(Common::InSaveFile *in, SavegameHeader &header); + static Common::String generateSaveName(int slotNumber); + static SaveStateList listSaves(const char *target); + static SaveStateDescriptor querySaveMetaInfos(int slot); +}; } // End of namespace Mortevielle #endif |