aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--base/game.cpp29
-rw-r--r--base/game.h39
-rw-r--r--engines/kyra/detection.cpp32
-rw-r--r--engines/metaengine.h23
-rw-r--r--engines/scumm/detection.cpp24
-rw-r--r--graphics/surface.cpp5
-rw-r--r--graphics/surface.h19
-rw-r--r--gui/launcher.cpp72
-rw-r--r--gui/widget.cpp3
9 files changed, 170 insertions, 76 deletions
diff --git a/base/game.cpp b/base/game.cpp
index e65c891dc7..30fd5fc850 100644
--- a/base/game.cpp
+++ b/base/game.cpp
@@ -69,9 +69,30 @@ void GameDescriptor::updateDesc(const char *extra) {
}
void SaveStateDescriptor::setThumbnail(Graphics::Surface *t) {
- if (_thumbnail && _thumbnail != t) {
- _thumbnail->free();
- delete _thumbnail;
+ if (_thumbnail.get() == t)
+ return;
+
+ _thumbnail = Common::SharedPtr<Graphics::Surface>(t, Graphics::SharedPtrSurfaceDeleter());
+}
+
+bool SaveStateDescriptor::getBool(const Common::String &key) const {
+ if (contains(key)) {
+ Common::String value = getVal(key);
+ if (value.equalsIgnoreCase("true") ||
+ value.equalsIgnoreCase("yes") ||
+ value.equals("1"))
+ return true;
+ if (value.equalsIgnoreCase("false") ||
+ value.equalsIgnoreCase("no") ||
+ value.equals("0"))
+ return false;
+ error("SaveStateDescriptor: %s '%s' has unknown value '%s' for boolean '%s'",
+ save_slot().c_str(), description().c_str(), value.c_str(), key.c_str());
}
- _thumbnail = t;
+ return false;
}
+
+void SaveStateDescriptor::setDeletableFlag(bool state) {
+ setVal("is_deletable", state ? "true" : "false");
+}
+
diff --git a/base/game.h b/base/game.h
index d81f2afb8a..6f9030c56f 100644
--- a/base/game.h
+++ b/base/game.h
@@ -29,6 +29,7 @@
#include "common/str.h"
#include "common/array.h"
#include "common/hash-str.h"
+#include "common/ptr.h"
namespace Graphics {
struct Surface;
@@ -119,15 +120,16 @@ public:
*/
class SaveStateDescriptor : public Common::StringMap {
protected:
- Graphics::Surface *_thumbnail; // can be NULL
+ Common::SharedPtr<Graphics::Surface> _thumbnail; // can be 0
+
public:
- SaveStateDescriptor() : _thumbnail(0) {
+ SaveStateDescriptor() : _thumbnail() {
setVal("save_slot", "-1"); // FIXME: default to 0 (first slot) or to -1 (invalid slot) ?
setVal("description", "");
setVal("filename", "");
}
- SaveStateDescriptor(int s, const Common::String &d, const Common::String &f) : _thumbnail(0) {
+ SaveStateDescriptor(int s, const Common::String &d, const Common::String &f) : _thumbnail() {
char buf[16];
sprintf(buf, "%d", s);
setVal("save_slot", buf);
@@ -135,16 +137,12 @@ public:
setVal("filename", f);
}
- SaveStateDescriptor(const Common::String &s, const Common::String &d, const Common::String &f) : _thumbnail(0) {
+ SaveStateDescriptor(const Common::String &s, const Common::String &d, const Common::String &f) : _thumbnail() {
setVal("save_slot", s);
setVal("description", d);
setVal("filename", f);
}
- ~SaveStateDescriptor() {
- setThumbnail(0);
- }
-
/** The saveslot id, as it would be passed to the "-x" command line switch. */
Common::String &save_slot() { return getVal("save_slot"); }
@@ -163,19 +161,30 @@ public:
/** The filename of the savestate, for use with the SaveFileManager API (read-only variant). */
const Common::String &filename() const { return getVal("filename"); }
+ /** Optional entries only included when querying via MetaEngine::querySaveMetaInfo */
+
+ /**
+ * Returns the value of a given key as boolean.
+ * It accepts 'true', 'yes' and '1' for true and
+ * 'false', 'no' and '0' for false.
+ * (FIXME:) On unknown value it errors out ScummVM.
+ * On unknown key it returns false as default.
+ */
+ bool getBool(const Common::String &key) const;
+
+ /**
+ * Sets the 'is_deletable' key, which indicates, if the
+ * given savestate is safe for deletion.
+ */
+ void setDeletableFlag(bool state);
+
/**
* Return a thumbnail graphics surface representing the savestate visually
* This is usually a scaled down version of the game graphics. The size
* should be either 160x100 or 160x120 pixels, depending on the aspect
* ratio of the game. If another ratio is required, contact the core team.
- *
- * TODO: it is probably a bad idea to read this for *all* games at once,
- * at least on low-end devices. So this info should probably normally only
- * be included optionally. I.e. only upon a query for a specific savegame...
- * To this end, add a getFullSaveStateInfo(target, slot) to the plugin API.
*/
- const Graphics::Surface *getThumbnail() const { return _thumbnail; }
-
+ const Graphics::Surface *getThumbnail() const { return _thumbnail.get(); }
void setThumbnail(Graphics::Surface *t);
};
diff --git a/engines/kyra/detection.cpp b/engines/kyra/detection.cpp
index c630e61b05..7a377471c1 100644
--- a/engines/kyra/detection.cpp
+++ b/engines/kyra/detection.cpp
@@ -1068,7 +1068,7 @@ public:
bool createInstance(OSystem *syst, Engine **engine, const Common::ADGameDescription *desc) const;
SaveStateList listSaves(const char *target) const;
void removeSaveState(const char *target, int slot) const;
- Graphics::Surface *loadThumbnailFromSlot(const char *target, int slot) const;
+ SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
};
bool KyraMetaEngine::hasFeature(MetaEngineFeature f) const {
@@ -1077,6 +1077,7 @@ bool KyraMetaEngine::hasFeature(MetaEngineFeature f) const {
(f == kSupportsListSaves) ||
(f == kSupportsDirectLoad) ||
(f == kSupportsDeleteSave) ||
+ (f == kSupportsMetaInfos) ||
(f == kSupportsThumbnails);
}
@@ -1137,10 +1138,7 @@ SaveStateList KyraMetaEngine::listSaves(const char *target) const {
// Obtain the last 3 digits of the filename, since they correspond to the save slot
int slotNum = atoi(file->c_str() + file->size() - 3);
- // HACK: Until we have a way to check whether a save is deletable in our launcher delete savegame dialog.
- // We do not list slot 0 here, since it's for restarting the game and it should never be deleted.
- // The downside of it is of course we can not load it via the menu and it isn't listed via --list-saves.
- if (slotNum > 0 && slotNum <= 999) {
+ if (slotNum >= 0 && slotNum <= 999) {
Common::InSaveFile *in = saveFileMan->openForLoading(file->c_str());
if (in) {
if (Kyra::KyraEngine_v1::readSaveHeader(in, false, header) == Kyra::KyraEngine_v1::kRSHENoError) {
@@ -1194,16 +1192,28 @@ void KyraMetaEngine::removeSaveState(const char *target, int slot) const {
}
-Graphics::Surface *KyraMetaEngine::loadThumbnailFromSlot(const char *target, int slot) const {
+SaveStateDescriptor KyraMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
Common::String filename = Kyra::KyraEngine_v1::getSavegameFilename(target, slot);
-
Common::InSaveFile *in = g_system->getSavefileManager()->openForLoading(filename.c_str());
- Kyra::KyraEngine_v1::SaveHeader header;
- if (in && Kyra::KyraEngine_v1::readSaveHeader(in, true, header) == Kyra::KyraEngine_v1::kRSHENoError)
- return header.thumbnail;
+ if (in) {
+ Kyra::KyraEngine_v1::SaveHeader header;
+ Kyra::KyraEngine_v1::kReadSaveHeaderError error;
+
+ error = Kyra::KyraEngine_v1::readSaveHeader(in, true, header);
+ delete in;
+
+ if (error == Kyra::KyraEngine_v1::kRSHENoError) {
+ SaveStateDescriptor desc(slot, header.description, filename);
+
+ desc.setDeletableFlag(slot != 0);
+ desc.setThumbnail(header.thumbnail);
+
+ return desc;
+ }
+ }
- return 0;
+ return SaveStateDescriptor();
}
#if PLUGIN_ENABLED_DYNAMIC(KYRA)
diff --git a/engines/metaengine.h b/engines/metaengine.h
index f9a6c42cbc..525afe5fa3 100644
--- a/engines/metaengine.h
+++ b/engines/metaengine.h
@@ -107,18 +107,16 @@ public:
virtual void removeSaveState(const char *target, int slot) const {};
/**
- * Loads a thumbnail from the specified save state.
+ * Returns meta infos from the specified save state.
*
- * This can return '0' to indicate that no thumbnail was found.
- * If it returns a valid Graphics::Surface object, it must be the same
- * format as the overlay.
+ * Depending on the MetaEngineFeatures set this can include
+ * thumbnails, save date / time, play time.
*
* @param target name of a config manager target
* @param slot slot number of the save state
*/
- virtual Graphics::Surface *loadThumbnailFromSlot(const char *target, int slot) const { return 0; }
+ virtual SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const { return SaveStateDescriptor(); }
-
/** @name MetaEngineFeature flags */
//@{
@@ -146,10 +144,17 @@ public:
kSupportsDeleteSave = 3,
/**
- * Features a thumbnail in savegames (i.e. implements the
- * loadThumbnailFromSlot method)
+ * Features meta infos for savestates (i.e. implements the
+ * querySaveMetaInfos method properly)
+ */
+ kSupportsMetaInfos = 4,
+
+ /**
+ * Features a thumbnail in savegames (i.e. includes a thumbnail
+ * in savestates returned via querySaveMetaInfo). This flag may
+ * only be set when 'kSupportsMetaInfos' is set.
*/
- kSupportsThumbnails = 4
+ kSupportsThumbnails = 5
};
/**
diff --git a/engines/scumm/detection.cpp b/engines/scumm/detection.cpp
index ce991a8997..34775ab575 100644
--- a/engines/scumm/detection.cpp
+++ b/engines/scumm/detection.cpp
@@ -683,7 +683,7 @@ public:
virtual SaveStateList listSaves(const char *target) const;
virtual void removeSaveState(const char *target, int slot) const;
- virtual Graphics::Surface *loadThumbnailFromSlot(const char *target, int slot) const;
+ virtual SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
};
bool ScummMetaEngine::hasFeature(MetaEngineFeature f) const {
@@ -692,6 +692,7 @@ bool ScummMetaEngine::hasFeature(MetaEngineFeature f) const {
(f == kSupportsListSaves) ||
(f == kSupportsDirectLoad) ||
(f == kSupportsDeleteSave) ||
+ (f == kSupportsMetaInfos) ||
(f == kSupportsThumbnails);
}
@@ -983,8 +984,25 @@ void ScummMetaEngine::removeSaveState(const char *target, int slot) const {
g_system->getSavefileManager()->removeSavefile(filename.c_str());
}
-Graphics::Surface *ScummMetaEngine::loadThumbnailFromSlot(const char *target, int slot) const {
- return ScummEngine::loadThumbnailFromSlot(target, slot);
+SaveStateDescriptor ScummMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
+ Common::String filename = ScummEngine::makeSavegameName(target, slot, false);
+ Common::InSaveFile *in = g_system->getSavefileManager()->openForLoading(filename.c_str());
+
+ if (!in)
+ return SaveStateDescriptor();
+
+ Common::String saveDesc;
+ Scumm::getSavegameName(in, saveDesc, 0); // FIXME: heversion?!?
+ delete in;
+
+ // TODO: Cleanup
+ Graphics::Surface *thumbnail = ScummEngine::loadThumbnailFromSlot(target, slot);
+
+ SaveStateDescriptor desc(slot, saveDesc, filename);
+ desc.setDeletableFlag(true);
+ desc.setThumbnail(thumbnail);
+
+ return desc;
}
#if PLUGIN_ENABLED_DYNAMIC(SCUMM)
diff --git a/graphics/surface.cpp b/graphics/surface.cpp
index a9f3e75886..263a4fd23b 100644
--- a/graphics/surface.cpp
+++ b/graphics/surface.cpp
@@ -66,6 +66,11 @@ void Surface::free() {
bytesPerPixel = 0;
}
+void Surface::copyFrom(const Surface &surf) {
+ create(surf.w, surf.h, surf.bytesPerPixel);
+ memcpy(pixels, surf.pixels, h * pitch);
+}
+
void Surface::hLine(int x, int y, int x2, uint32 color) {
// Clipping
if (y < 0 || y >= h)
diff --git a/graphics/surface.h b/graphics/surface.h
index ff1ddda695..747bda9a26 100644
--- a/graphics/surface.h
+++ b/graphics/surface.h
@@ -30,7 +30,6 @@
namespace Graphics {
-
/**
* An arbitrary graphics surface, which can be the target (or source) of blit
* operations, font rendering, etc.
@@ -67,6 +66,12 @@ struct Surface {
*/
void free();
+ /**
+ * Copies data from another Surface, this calls *free* on the current surface, to assure
+ * it being clean.
+ */
+ void copyFrom(const Surface &surf);
+
void drawLine(int x0, int y0, int x1, int y1, uint32 color);
void hLine(int x, int y, int x2, uint32 color);
void vLine(int x, int y, int y2, uint32 color);
@@ -76,6 +81,18 @@ struct Surface {
void move(int dx, int dy, int height);
};
+/**
+ * For safe deletion of surface with SharedPtr.
+ * The deleter assures Surface::free is called on
+ * deletion.
+ */
+struct SharedPtrSurfaceDeleter {
+ void operator()(Surface *ptr) {
+ ptr->free();
+ delete ptr;
+ }
+};
+
} // End of namespace Graphics
diff --git a/gui/launcher.cpp b/gui/launcher.cpp
index 3674da5a97..1d50f21998 100644
--- a/gui/launcher.cpp
+++ b/gui/launcher.cpp
@@ -477,7 +477,6 @@ class SaveLoadChooser : public GUI::Dialog {
typedef Common::String String;
typedef Common::StringList StringList;
protected:
- bool _delSupport;
GUI::ListWidget *_list;
GUI::ButtonWidget *_chooseButton;
GUI::ButtonWidget *_deleteButton;
@@ -485,13 +484,16 @@ protected:
GUI::ContainerWidget *_container;
const EnginePlugin *_plugin;
+ bool _delSupport;
+ bool _metaInfoSupport;
+ bool _thumbnailSupport;
String _target;
SaveStateList _saveList;
uint8 _fillR, _fillG, _fillB;
void updateSaveList();
- void updateInfos(bool redraw);
+ void updateSelection(bool redraw);
public:
SaveLoadChooser(const String &title, const String &buttonLabel);
~SaveLoadChooser();
@@ -528,6 +530,8 @@ SaveLoadChooser::SaveLoadChooser(const String &title, const String &buttonLabel)
_deleteButton = new GUI::ButtonWidget(this, "scummsaveload_delete", "Delete", kDelCmd, 0);
_deleteButton->setEnabled(false);
+
+ _delSupport = _metaInfoSupport = _thumbnailSupport = false;
}
SaveLoadChooser::~SaveLoadChooser() {
@@ -540,6 +544,8 @@ int SaveLoadChooser::runModal(const EnginePlugin *plugin, const String &target)
_plugin = plugin;
_target = target;
_delSupport = (*_plugin)->hasFeature(MetaEngine::kSupportsDeleteSave);
+ _metaInfoSupport = (*_plugin)->hasFeature(MetaEngine::kSupportsMetaInfos);
+ _thumbnailSupport = _metaInfoSupport && (*_plugin)->hasFeature(MetaEngine::kSupportsThumbnails);
reflowLayout();
updateSaveList();
@@ -566,16 +572,7 @@ void SaveLoadChooser::handleCommand(CommandSender *sender, uint32 cmd, uint32 da
close();
break;
case GUI::kListSelectionChangedCmd: {
- if (_gfxWidget)
- updateInfos(true);
-
- // Disable these buttons if nothing is selected, or if an empty
- // list item is selected.
- _chooseButton->setEnabled(selItem >= 0 && (!_list->getSelectedString().empty()));
- _chooseButton->draw();
- // Delete will always be disabled if the engine doesn't support it.
- _deleteButton->setEnabled(_delSupport && (selItem >= 0) && (!_list->getSelectedString().empty()));
- _deleteButton->draw();
+ updateSelection(true);
} break;
case kDelCmd:
if (selItem >= 0 && _delSupport) {
@@ -588,10 +585,7 @@ void SaveLoadChooser::handleCommand(CommandSender *sender, uint32 cmd, uint32 da
_list->setSelected(-1);
updateSaveList();
-
- // Disable these buttons again after deleteing a selection
- _chooseButton->setEnabled(false);
- _deleteButton->setEnabled(false);
+ updateSelection(true);
}
}
break;
@@ -603,7 +597,7 @@ void SaveLoadChooser::handleCommand(CommandSender *sender, uint32 cmd, uint32 da
}
void SaveLoadChooser::reflowLayout() {
- if (g_gui.evaluator()->getVar("scummsaveload_extinfo.visible") == 1 && (_plugin && (*_plugin)->hasFeature(MetaEngine::kSupportsThumbnails))) {
+ if (g_gui.evaluator()->getVar("scummsaveload_extinfo.visible") == 1 && _thumbnailSupport) {
int thumbX = g_gui.evaluator()->getVar("scummsaveload_thumbnail.x");
int thumbY = g_gui.evaluator()->getVar("scummsaveload_thumbnail.y");
int hPad = g_gui.evaluator()->getVar("scummsaveload_thumbnail.hPad");
@@ -621,7 +615,7 @@ void SaveLoadChooser::reflowLayout() {
_fillR = g_gui.evaluator()->getVar("scummsaveload_thumbnail.fillR");
_fillG = g_gui.evaluator()->getVar("scummsaveload_thumbnail.fillG");
_fillB = g_gui.evaluator()->getVar("scummsaveload_thumbnail.fillB");
- updateInfos(false);
+ updateSelection(false);
} else {
_container->setFlags(GUI::WIDGET_INVISIBLE);
_gfxWidget->setFlags(GUI::WIDGET_INVISIBLE);
@@ -630,23 +624,39 @@ void SaveLoadChooser::reflowLayout() {
Dialog::reflowLayout();
}
-void SaveLoadChooser::updateInfos(bool redraw) {
+void SaveLoadChooser::updateSelection(bool redraw) {
int selItem = _list->getSelected();
- Graphics::Surface *thumb = 0;
- if (selItem >= 0 && !_list->getSelectedString().empty())
- thumb = (*_plugin)->loadThumbnailFromSlot(_target.c_str(), atoi(_saveList[selItem].save_slot().c_str()));
-
- if (thumb) {
- _gfxWidget->setGfx(thumb);
- _gfxWidget->useAlpha(256);
- thumb->free();
- delete thumb;
- } else {
- _gfxWidget->setGfx(-1, -1, _fillR, _fillG, _fillB);
+
+ bool isDeletable = _delSupport;
+
+ if (selItem >= 0 && !_list->getSelectedString().empty() && _metaInfoSupport) {
+ SaveStateDescriptor desc = (*_plugin)->querySaveMetaInfos(_target.c_str(), atoi(_saveList[selItem].save_slot().c_str()));
+
+ isDeletable = desc.getBool("is_deletable") && _delSupport;
+
+ if (_thumbnailSupport) {
+ const Graphics::Surface *thumb = desc.getThumbnail();
+ if (thumb) {
+ _gfxWidget->setGfx(thumb);
+ _gfxWidget->useAlpha(256);
+ } else {
+ _gfxWidget->setGfx(-1, -1, _fillR, _fillG, _fillB);
+ }
+ }
}
- if (redraw)
+
+ // Disable these buttons if nothing is selected, or if an empty
+ // list item is selected.
+ _chooseButton->setEnabled(selItem >= 0 && (!_list->getSelectedString().empty()));
+ // Delete will always be disabled if the engine doesn't support it.
+ _deleteButton->setEnabled(isDeletable && (selItem >= 0) && (!_list->getSelectedString().empty()));
+
+ if (redraw) {
_gfxWidget->draw();
+ _chooseButton->draw();
+ _deleteButton->draw();
+ }
}
void SaveLoadChooser::close() {
diff --git a/gui/widget.cpp b/gui/widget.cpp
index e9afc30301..31f76eac6f 100644
--- a/gui/widget.cpp
+++ b/gui/widget.cpp
@@ -363,8 +363,7 @@ void GraphicsWidget::setGfx(const Graphics::Surface *gfx) {
return;
// TODO: add conversion to OverlayColor
- _gfx.create(gfx->w, gfx->h, gfx->bytesPerPixel);
- memcpy(_gfx.pixels, gfx->pixels, gfx->h * gfx->pitch);
+ _gfx.copyFrom(*gfx);
}
void GraphicsWidget::setGfx(int w, int h, int r, int g, int b) {