diff options
-rw-r--r-- | base/game.cpp | 29 | ||||
-rw-r--r-- | base/game.h | 39 | ||||
-rw-r--r-- | engines/kyra/detection.cpp | 32 | ||||
-rw-r--r-- | engines/metaengine.h | 23 | ||||
-rw-r--r-- | engines/scumm/detection.cpp | 24 | ||||
-rw-r--r-- | graphics/surface.cpp | 5 | ||||
-rw-r--r-- | graphics/surface.h | 19 | ||||
-rw-r--r-- | gui/launcher.cpp | 72 | ||||
-rw-r--r-- | gui/widget.cpp | 3 |
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) { |