aboutsummaryrefslogtreecommitdiff
path: root/engines
diff options
context:
space:
mode:
Diffstat (limited to 'engines')
-rw-r--r--engines/hugo/detection.cpp127
-rw-r--r--engines/hugo/file.cpp103
-rw-r--r--engines/hugo/file.h4
-rw-r--r--engines/hugo/game.h2
-rw-r--r--engines/hugo/global.h3
-rw-r--r--engines/hugo/hugo.cpp11
-rw-r--r--engines/hugo/hugo.h44
-rw-r--r--engines/hugo/parser.cpp6
-rw-r--r--engines/hugo/parser_v1d.cpp10
-rw-r--r--engines/hugo/parser_v1w.cpp4
-rw-r--r--engines/hugo/parser_v2d.cpp10
-rw-r--r--engines/hugo/parser_v3d.cpp10
12 files changed, 263 insertions, 71 deletions
diff --git a/engines/hugo/detection.cpp b/engines/hugo/detection.cpp
index a4bc5a4c1c..05fe984615 100644
--- a/engines/hugo/detection.cpp
+++ b/engines/hugo/detection.cpp
@@ -24,6 +24,10 @@
*/
#include "engines/advancedDetector.h"
+#include "common/system.h"
+#include "common/savefile.h"
+#include "graphics/thumbnail.h"
+#include "graphics/surface.h"
#include "hugo/hugo.h"
@@ -38,6 +42,11 @@ uint32 HugoEngine::getFeatures() const {
return _gameDescription->desc.flags;
}
+const char *HugoEngine::getGameId() const {
+ return _gameDescription->desc.gameid;
+}
+
+
static const PlainGameDescriptor hugoGames[] = {
// Games
{"hugo1", "Hugo 1: Hugo's House of Horrors"},
@@ -162,8 +171,12 @@ public:
}
bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *gd) const;
-
bool hasFeature(MetaEngineFeature f) const;
+
+ int getMaximumSaveSlot() const;
+ SaveStateList listSaves(const char *target) const;
+ SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
+ void removeSaveState(const char *target, int slot) const;
};
bool HugoMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *gd) const {
@@ -175,9 +188,117 @@ bool HugoMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGame
}
bool HugoMetaEngine::hasFeature(MetaEngineFeature f) const {
- return false;
+ return
+ (f == kSupportsListSaves) ||
+ (f == kSupportsLoadingDuringStartup) ||
+ (f == kSupportsDeleteSave) ||
+ (f == kSavesSupportMetaInfo) ||
+ (f == kSavesSupportThumbnail) ||
+ (f == kSavesSupportCreationDate);
+}
+
+int HugoMetaEngine::getMaximumSaveSlot() const { return 99; }
+
+SaveStateList HugoMetaEngine::listSaves(const char *target) const {
+ Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
+ Common::StringArray filenames;
+ Common::String pattern = target;
+ pattern += "-??.SAV";
+
+ filenames = saveFileMan->listSavefiles(pattern);
+ sort(filenames.begin(), filenames.end()); // Sort (hopefully ensuring we are sorted numerically..)
+
+ SaveStateList saveList;
+ int slotNum = 0;
+ for (Common::StringArray::const_iterator filename = filenames.begin(); filename != filenames.end(); ++filename) {
+ // Obtain the last 3 digits of the filename, since they correspond to the save slot
+ slotNum = atoi(filename->c_str() + filename->size() - 3);
+
+ if (slotNum >= 0 && slotNum <= getMaximumSaveSlot()) {
+ Common::InSaveFile *file = saveFileMan->openForLoading(*filename);
+ if (file) {
+ int saveVersion = file->readByte();
+
+ if (saveVersion != kSavegameVersion) {
+ warning("Savegame of incompatible version");
+ delete file;
+ continue;
+ }
+
+ // read name
+ uint16 nameSize = file->readUint16BE();
+ if (nameSize >= 255) {
+ delete file;
+ continue;
+ }
+ char name[256];
+ file->read(name, nameSize);
+ name[nameSize] = 0;
+
+ saveList.push_back(SaveStateDescriptor(slotNum, name));
+ delete file;
+ }
+ }
+ }
+
+ return saveList;
}
+SaveStateDescriptor HugoMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
+ Common::String fileName = Common::String::format("%s-%02d.SAV", target, slot);
+ Common::InSaveFile *file = g_system->getSavefileManager()->openForLoading(fileName);
+
+ if (file) {
+ int saveVersion = file->readByte();
+
+ if (saveVersion != kSavegameVersion) {
+ warning("Savegame of incompatible version");
+ delete file;
+ return SaveStateDescriptor();
+ }
+
+ uint32 saveNameLength = file->readUint16BE();
+ char saveName[256];
+ file->read(saveName, saveNameLength);
+ saveName[saveNameLength] = 0;
+
+ SaveStateDescriptor desc(slot, saveName);
+
+ Graphics::Surface *thumbnail = new Graphics::Surface();
+ assert(thumbnail);
+ if (!Graphics::loadThumbnail(*file, *thumbnail)) {
+ delete thumbnail;
+ thumbnail = 0;
+ }
+ desc.setThumbnail(thumbnail);
+
+ desc.setDeletableFlag(true);
+ desc.setWriteProtectedFlag(false);
+
+ uint32 saveDate = file->readUint32BE();
+ uint16 saveTime = file->readUint16BE();
+
+ int day = (saveDate >> 24) & 0xFF;
+ int month = (saveDate >> 16) & 0xFF;
+ int year = saveDate & 0xFFFF;
+
+ desc.setSaveDate(year, month, day);
+
+ int hour = (saveTime >> 8) & 0xFF;
+ int minutes = saveTime & 0xFF;
+
+ desc.setSaveTime(hour, minutes);
+
+ delete file;
+ return desc;
+ }
+ return SaveStateDescriptor();
+}
+
+void HugoMetaEngine::removeSaveState(const char *target, int slot) const {
+ Common::String fileName = Common::String::format("%s-%02d.SAV", target, slot);
+ g_system->getSavefileManager()->removeSavefile(fileName);
+}
} // End of namespace Hugo
#if PLUGIN_ENABLED_DYNAMIC(HUGO)
@@ -195,7 +316,7 @@ void HugoEngine::initGame(const HugoGameDescription *gd) {
_gameVariant = _gameType - 1 + ((_platform == Common::kPlatformWindows) ? 0 : 3);
// Generate filename
- _saveFilename = _targetName + "-%d.SAV";
+ _saveFilename = _targetName + "-%02d.SAV";
}
} // End of namespace Hugo
diff --git a/engines/hugo/file.cpp b/engines/hugo/file.cpp
index 78bcc46e85..e34679fce3 100644
--- a/engines/hugo/file.cpp
+++ b/engines/hugo/file.cpp
@@ -32,6 +32,9 @@
#include "common/system.h"
#include "common/savefile.h"
+#include "common/config-manager.h"
+#include "graphics/thumbnail.h"
+#include "gui/saveload.h"
#include "hugo/hugo.h"
#include "hugo/file.h"
@@ -299,22 +302,61 @@ bool FileManager::fileExists(char *filename) {
/**
* Save game to supplied slot
*/
-void FileManager::saveGame(int16 slot, const char *descrip) {
+bool FileManager::saveGame(int16 slot, Common::String descrip) {
debugC(1, kDebugFile, "saveGame(%d, %s)", slot, descrip);
- // Get full path of saved game file - note test for INITFILE
- Common::String path = Common::String::format(_vm->_saveFilename.c_str(), slot);
- Common::WriteStream *out = _vm->getSaveFileManager()->openForSaving(path);
+ const EnginePlugin *plugin = NULL;
+ int16 savegameId;
+ Common::String savegameDescription;
+ EngineMan.findGame(_vm->getGameId(), &plugin);
+
+ if (slot == -1) {
+ GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser("Save game:", "Save");
+ dialog->setSaveMode(true);
+ savegameId = dialog->runModal(plugin, ConfMan.getActiveDomainName());
+ savegameDescription = dialog->getResultString();
+ delete dialog;
+ } else {
+ savegameId = slot;
+ if (!descrip.empty()) {
+ savegameDescription = descrip;
+ } else {
+ savegameDescription = Common::String::format("Quick save #%d", slot);
+ }
+ }
+
+ if (savegameId < 0) // dialog aborted
+ return false;
+
+ Common::String savegameFile = Common::String::format(_vm->_saveFilename.c_str(), savegameId);
+ Common::SaveFileManager *saveMan = g_system->getSavefileManager();
+ Common::OutSaveFile *out = saveMan->openForSaving(savegameFile);
+
if (!out) {
- warning("Can't create file '%s', game not saved", path.c_str());
- return;
+ warning("Can't create file '%s', game not saved", savegameFile.c_str());
+ return false;
}
// Write version. We can't restore from obsolete versions
out->writeByte(kSavegameVersion);
- // Save description of saved game
- out->write(descrip, DESCRIPLEN);
+ if (savegameDescription == "") {
+ savegameDescription = "Untitled savegame";
+ }
+
+ out->writeSint16BE(savegameDescription.size() + 1);
+ out->write(savegameDescription.c_str(), savegameDescription.size() + 1);
+
+ Graphics::saveThumbnail(*out);
+
+ TimeDate curTime;
+ _vm->_system->getTimeAndDate(curTime);
+
+ uint32 saveDate = (curTime.tm_mday & 0xFF) << 24 | ((curTime.tm_mon + 1) & 0xFF) << 16 | ((curTime.tm_year + 1900) & 0xFFFF);
+ uint16 saveTime = (curTime.tm_hour & 0xFF) << 8 | ((curTime.tm_min) & 0xFF);
+
+ out->writeUint32BE(saveDate);
+ out->writeUint16BE(saveTime);
_vm->_object->saveObjects(out);
@@ -365,35 +407,57 @@ void FileManager::saveGame(int16 slot, const char *descrip) {
out->finalize();
delete out;
+
+ return true;
}
/**
* Restore game from supplied slot number
*/
-void FileManager::restoreGame(int16 slot) {
+bool FileManager::restoreGame(int16 slot) {
debugC(1, kDebugFile, "restoreGame(%d)", slot);
- // Initialize new-game status
- _vm->initStatus();
+ const EnginePlugin *plugin = NULL;
+ int16 savegameId;
+ EngineMan.findGame(_vm->getGameId(), &plugin);
- // Get full path of saved game file - note test for INITFILE
- Common::String path; // Full path of saved game
+ if (slot == -1) {
+ GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser("Restore game:", "Restore");
+ dialog->setSaveMode(false);
+ savegameId = dialog->runModal(plugin, ConfMan.getActiveDomainName());
+ delete dialog;
+ } else {
+ savegameId = slot;
+ }
+
+ if (savegameId < 0) // dialog aborted
+ return false;
- path = Common::String::format(_vm->_saveFilename.c_str(), slot);
+ Common::String savegameFile = Common::String::format(_vm->_saveFilename.c_str(), savegameId);
+ Common::SaveFileManager *saveMan = g_system->getSavefileManager();
+ Common::InSaveFile *in = saveMan->openForLoading(savegameFile);
- Common::SeekableReadStream *in = _vm->getSaveFileManager()->openForLoading(path);
if (!in)
- return;
+ return false;
+
+ // Initialize new-game status
+ _vm->initStatus();
// Check version, can't restore from different versions
int saveVersion = in->readByte();
if (saveVersion != kSavegameVersion) {
- error("Savegame of incompatible version");
- return;
+ warning("Savegame of incompatible version");
+ delete in;
+ return false;
}
// Skip over description
- in->seek(DESCRIPLEN, SEEK_CUR);
+ int32 saveGameNameSize = in->readSint16BE();
+ in->skip(saveGameNameSize);
+
+ Graphics::skipThumbnail(*in);
+
+ in->skip(6); // Skip date & time
// If hero image is currently swapped, swap it back before restore
if (_vm->_heroImage != HERO)
@@ -446,6 +510,7 @@ void FileManager::restoreGame(int16 slot) {
_maze.firstScreenIndex = in->readByte();
delete in;
+ return true;
}
/**
diff --git a/engines/hugo/file.h b/engines/hugo/file.h
index 1f231d0604..4dfd7167c0 100644
--- a/engines/hugo/file.h
+++ b/engines/hugo/file.h
@@ -63,8 +63,8 @@ public:
void readImage(int objNum, object_t *objPtr);
void readUIFImages();
void readUIFItem(int16 id, byte *buf);
- void restoreGame(int16 slot);
- void saveGame(int16 slot, const char *descrip);
+ bool restoreGame(int16 slot);
+ bool saveGame(int16 slot, Common::String descrip);
virtual void openDatabaseFiles() = 0;
virtual void closeDatabaseFiles() = 0;
diff --git a/engines/hugo/game.h b/engines/hugo/game.h
index a4f450bfbf..3c88c4cafa 100644
--- a/engines/hugo/game.h
+++ b/engines/hugo/game.h
@@ -836,7 +836,6 @@ struct status_t { // Game status (not saved)
go_t go_for; // Purpose of an automatic route
int16 go_id; // Index of exit of object walking to
fpath_t path; // Alternate path for saved files
- int16 saveSlot; // Current slot to save/restore game
int16 song; // Current song
int16 cx, cy; // Cursor position (dib coords)
@@ -847,6 +846,7 @@ struct status_t { // Game status (not saved)
// bool mmtimeFl; // Multimedia timer supported
// int16 screenWidth; // Desktop screen width
// uint32 saveTick; // Time of last save in ticks
+// int16 saveSlot; // Current slot to save/restore game
};
struct config_t { // User's config (saved)
diff --git a/engines/hugo/global.h b/engines/hugo/global.h
index 43a1e39b8f..d44b5cb44f 100644
--- a/engines/hugo/global.h
+++ b/engines/hugo/global.h
@@ -47,7 +47,4 @@ namespace Hugo {
// User interface database (Windows Only)
// This file contains, between others, the bitmaps of the fonts used in the application
#define UIF_FILE "uif.dat"
-
-static const int kSavegameVersion = 1;
-
} // End of namespace Hugo
diff --git a/engines/hugo/hugo.cpp b/engines/hugo/hugo.cpp
index 06cd7db62b..c3e4658e8b 100644
--- a/engines/hugo/hugo.cpp
+++ b/engines/hugo/hugo.cpp
@@ -69,6 +69,7 @@ HugoEngine::HugoEngine(OSystem *syst, const HugoGameDescription *gd) : Engine(sy
_backgroundObjectsSize(0), _screenActsSize(0), _usesSize(0)
{
+ _system = syst;
DebugMan.addDebugChannel(kDebugSchedule, "Schedule", "Script Schedule debug level");
DebugMan.addDebugChannel(kDebugEngine, "Engine", "Engine debug level");
DebugMan.addDebugChannel(kDebugDisplay, "Display", "Display debug level");
@@ -915,7 +916,6 @@ void HugoEngine::initStatus() {
_status.helpFl = false; // Not calling WinHelp()
_status.doQuitFl = false;
_status.path[0] = 0; // Path to write files
- _status.saveSlot = 0; // Slot to save/restore game
// Initialize every start of new game
_status.tick = 0; // Tick count
@@ -934,6 +934,7 @@ void HugoEngine::initStatus() {
// _status.mmtime = false; // Multimedia timer support
// _status.screenWidth = 0; // Desktop screen width
// _status.saveTick = 0; // Time of last save
+// _status.saveSlot = 0; // Slot to save/restore game
}
/**
@@ -1256,4 +1257,12 @@ void HugoEngine::endGame() {
_status.viewState = V_EXIT;
}
+bool HugoEngine::canLoadGameStateCurrently() {
+ return true;
+}
+
+bool HugoEngine::canSaveGameStateCurrently() {
+ return (_status.viewState == V_PLAY);
+}
+
} // End of namespace Hugo
diff --git a/engines/hugo/hugo.h b/engines/hugo/hugo.h
index d7dd767b6b..903dd5a7bc 100644
--- a/engines/hugo/hugo.h
+++ b/engines/hugo/hugo.h
@@ -32,6 +32,7 @@
// This include is here temporarily while the engine is being refactored.
#include "hugo/game.h"
+#include "hugo/file.h"
#define HUGO_DAT_VER_MAJ 0 // 1 byte
#define HUGO_DAT_VER_MIN 30 // 1 byte
@@ -59,6 +60,8 @@ class RandomSource;
*/
namespace Hugo {
+static const int kSavegameVersion = 2;
+
enum GameType {
kGameTypeNone = 0,
kGameTypeHugo1,
@@ -114,6 +117,8 @@ public:
HugoEngine(OSystem *syst, const HugoGameDescription *gd);
~HugoEngine();
+ OSystem *_system;
+
byte _numVariant;
byte _gameVariant;
byte _maxInvent;
@@ -172,6 +177,7 @@ public:
const HugoGameDescription *_gameDescription;
uint32 getFeatures() const;
+ const char *HugoEngine::getGameId() const;
GameType getGameType() const;
Common::Platform getPlatform() const;
@@ -183,17 +189,17 @@ public:
return *s_Engine;
}
- void initGame(const HugoGameDescription *gd);
- void initGamePart(const HugoGameDescription *gd);
+ bool canLoadGameStateCurrently();
+ bool canSaveGameStateCurrently();
bool loadHugoDat();
- int getMouseX() const {
- return _mouseX;
- }
- int getMouseY() const {
- return _mouseY;
- }
+ char *useBG(char *name);
+ int deltaX(int x1, int x2, int vx, int y);
+ int deltaY(int x1, int x2, int vy, int y);
+
+ void initGame(const HugoGameDescription *gd);
+ void initGamePart(const HugoGameDescription *gd);
void boundaryCollision(object_t *obj);
void clearBoundary(int x1, int x2, int y);
void endGame();
@@ -204,10 +210,12 @@ public:
void shutdown();
void storeBoundary(int x1, int x2, int y);
- char *useBG(char *name);
-
- int deltaX(int x1, int x2, int vx, int y);
- int deltaY(int x1, int x2, int vy, int y);
+ int getMouseX() const {
+ return _mouseX;
+ }
+ int getMouseY() const {
+ return _mouseY;
+ }
overlay_t &getBoundaryOverlay() {
return _boundary;
@@ -242,6 +250,18 @@ public:
byte getIntroSize() {
return _introXSize;
}
+ Common::Error saveGameState(int slot, const char *desc) {
+
+ return (_file->saveGame(slot, desc) ? Common::kWritingFailed : Common::kNoError);
+ }
+
+ Common::Error loadGameState(int slot) {
+ return (_file->restoreGame(slot) ? Common::kReadingFailed : Common::kNoError);
+ }
+
+ bool hasFeature(EngineFeature f) const {
+ return (f == kSupportsRTL) || (f == kSupportsLoadingDuringRuntime) || (f == kSupportsSavingDuringRuntime);
+ }
FileManager *_file;
Scheduler *_scheduler;
diff --git a/engines/hugo/parser.cpp b/engines/hugo/parser.cpp
index f23427e690..19d496dfd4 100644
--- a/engines/hugo/parser.cpp
+++ b/engines/hugo/parser.cpp
@@ -100,13 +100,11 @@ void Parser::keyHandler(uint16 nChar, uint16 nFlags) {
gameStatus.recallFl = true;
break;
case Common::KEYCODE_F4: // Save game
- // TODO: Add a proper screen to select saveslot
if (gameStatus.viewState == V_PLAY)
- _vm->_file->saveGame(gameStatus.saveSlot, "Current game");
+ _vm->_file->saveGame(-1, Common::String());
break;
case Common::KEYCODE_F5: // Restore game
- // TODO: Add a proper screen to specify saveslot and description
- _vm->_file->restoreGame(gameStatus.saveSlot);
+ _vm->_file->restoreGame(-1);
_vm->_scheduler->restoreScreen(*_vm->_screen_p);
gameStatus.viewState = V_PLAY;
break;
diff --git a/engines/hugo/parser_v1d.cpp b/engines/hugo/parser_v1d.cpp
index 9514f88178..9aab521a18 100644
--- a/engines/hugo/parser_v1d.cpp
+++ b/engines/hugo/parser_v1d.cpp
@@ -320,18 +320,12 @@ void Parser_v1d::lineHandler() {
if (gameStatus.gameOverFl)
Utils::gameOverMsg();
else
-// _vm->_file->saveOrRestore(true);
- warning("STUB: saveOrRestore()");
- // HACK: Currently use Win code
- _vm->_file->saveGame(gameStatus.saveSlot, "Current game");
+ _vm->_file->saveGame(-1, Common::String());
return;
}
if (!strcmp("restore", _line)) {
-// _vm->_file->saveOrRestore(false);
- warning("STUB: saveOrRestore()");
- // HACK: Currently use Win code
- _vm->_file->restoreGame(gameStatus.saveSlot);
+ _vm->_file->restoreGame(-1);
_vm->_scheduler->restoreScreen(*_vm->_screen_p);
gameStatus.viewState = V_PLAY;
return;
diff --git a/engines/hugo/parser_v1w.cpp b/engines/hugo/parser_v1w.cpp
index 4bfdd281ba..236d24b61b 100644
--- a/engines/hugo/parser_v1w.cpp
+++ b/engines/hugo/parser_v1w.cpp
@@ -369,12 +369,12 @@ void Parser_v1w::lineHandler() {
// SAVE/RESTORE
if (!strcmp("save", _line) && gameStatus.viewState == V_PLAY) {
- _vm->_file->saveGame(gameStatus.saveSlot, "Current game");
+ _vm->_file->saveGame(-1, Common::String());
return;
}
if (!strcmp("restore", _line) && (gameStatus.viewState == V_PLAY || gameStatus.viewState == V_IDLE)) {
- _vm->_file->restoreGame(gameStatus.saveSlot);
+ _vm->_file->restoreGame(-1);
_vm->_scheduler->restoreScreen(*_vm->_screen_p);
gameStatus.viewState = V_PLAY;
return;
diff --git a/engines/hugo/parser_v2d.cpp b/engines/hugo/parser_v2d.cpp
index 802f69056b..a0b0db6234 100644
--- a/engines/hugo/parser_v2d.cpp
+++ b/engines/hugo/parser_v2d.cpp
@@ -75,19 +75,13 @@ void Parser_v2d::lineHandler() {
if (gameStatus.gameOverFl)
Utils::gameOverMsg();
else
-// _vm->_file->saveOrRestore(true);
- warning("STUB: saveOrRestore()");
- // HACK: Currently use Win code
- _vm->_file->saveGame(gameStatus.saveSlot, "Current game");
+ _vm->_file->saveGame(-1, Common::String());
return;
}
if (!strcmp("restore", _line)) {
_config.soundFl = false;
-// _vm->_file->saveOrRestore(false);
- warning("STUB: saveOrRestore()");
- // HACK: Currently use Win code
- _vm->_file->restoreGame(gameStatus.saveSlot);
+ _vm->_file->restoreGame(-1);
_vm->_scheduler->restoreScreen(*_vm->_screen_p);
gameStatus.viewState = V_PLAY;
return;
diff --git a/engines/hugo/parser_v3d.cpp b/engines/hugo/parser_v3d.cpp
index 54c3e830ce..ca78307a44 100644
--- a/engines/hugo/parser_v3d.cpp
+++ b/engines/hugo/parser_v3d.cpp
@@ -127,19 +127,13 @@ void Parser_v3d::lineHandler() {
if (gameStatus.gameOverFl)
Utils::gameOverMsg();
else
-// _vm->_file->saveOrRestore(true);
- warning("STUB: saveOrRestore()");
- // HACK: Currently use Win code
- _vm->_file->saveGame(gameStatus.saveSlot, "Current game");
+ _vm->_file->saveGame(-1, Common::String());
return;
}
if (!strcmp("restore", _line)) {
_config.soundFl = false;
-// _vm->_file->saveOrRestore(false);
- warning("STUB: saveOrRestore()");
- // HACK: Currently use Win code
- _vm->_file->restoreGame(gameStatus.saveSlot);
+ _vm->_file->restoreGame(-1);
_vm->_scheduler->restoreScreen(*_vm->_screen_p);
gameStatus.viewState = V_PLAY;
return;