/* 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 3 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, see .
*
*/
#ifndef ENGINES_METAENGINE_H
#define ENGINES_METAENGINE_H
#include "common/scummsys.h"
#include "common/error.h"
#include "common/array.h"
#include "engines/achievements.h"
#include "engines/game.h"
#include "engines/savestate.h"
#include "base/plugins.h"
class Engine;
class OSystem;
namespace Common {
class Keymap;
class FSList;
class OutSaveFile;
class String;
typedef SeekableReadStream InSaveFile;
}
namespace Graphics {
struct Surface;
}
namespace GUI {
class GuiObject;
class OptionsContainerWidget;
}
/**
* @defgroup engines_metaengine Meta engine
* @ingroup engines
*
* @brief API for managing various functions of the meta engine.
*
* @{
*/
/**
* Per-game extra GUI options structure.
* Currently, this can only be used for options with checkboxes.
*/
struct ExtraGuiOption {
const char *label; /*!< Option label, e.g. "Fullscreen mode". */
const char *tooltip; /*!< Option tooltip shown when the mouse cursor hovers over it. */
const char *configOption; /*!< confMan key, e.g. "fullscreen". */
bool defaultState; /*!< Default state of the checkbox (checked or not). */
byte groupId; /*!< Id for the checkbox's group, or 0 for no group. */
byte groupLeaderId; /*!< When this checkbox is unchecked, disable all checkboxes in this group. One leader per group. */
};
/**
* debug channels structure
*/
struct DebugChannelDef {
uint32 channel; /*!< enum value, channel id, e.g. kDebugGlobalDetection */
const char *name; /*!< name of debug channel, e.g. "detection" */
const char *description; /*!< description of debug channel, e.g. "track scripts" */
};
/**
* delimiter of the array of DebugChannelDef
*/
#define DEBUG_CHANNEL_END {0, NULL, NULL}
/**
* Array of ExtraGuiOption structures.
*/
typedef Common::Array ExtraGuiOptions;
/**
* @todo Doc required
*/
enum { kSavegameFilePattern = -99 };
#define EXTENDED_SAVE_VERSION 4
/**
* Structure describing a savegame file.
*/
struct ExtendedSavegameHeader {
char id[6]; /*!< ID of the savegame file. */
uint8 version; /*!< Version of the savegame header. */
Common::String saveName; /*!< Name of the savegame. */
Common::String description; /*!< Description of the savegame, as entered by the user. */
uint32 date; /*!< Date of the savegame. */
uint16 time; /*!< Time of the savegame. */
uint32 playtime; /*!< Total play time until this savegame. */
Graphics::Surface *thumbnail; /*!< Screen content shown as a thumbnail for this savegame. */
bool isAutosave; /*!< Whether this savegame is an autosave. */
ExtendedSavegameHeader() {
memset(id, 0, 6);
version = 0;
date = 0;
time = 0;
playtime = 0;
thumbnail = nullptr;
isAutosave = false;
}
};
/**
* A meta engine factory for Engine instances with the
* added ability of listing and detecting supported games.
*
* Every engine "plugin" provides a hook to get an instance of a MetaEngineDetection
* subclass for that "engine plugin". For example, SCUMM provides ScummMetaEngineDetection.
* This is then in turn used by the frontend code to detect games, and other useful functionality.
*
* To instantiate actual Engine objects, see the class @ref MetaEngine.
*/
class MetaEngineDetection : public PluginObject {
public:
virtual ~MetaEngineDetection() {}
/** Get the engine ID. */
virtual const char *getName() const = 0;
/** Get the engine name. */
virtual const char *getEngineName() const = 0;
/** Return some copyright information about the original engine. */
virtual const char *getOriginalCopyright() const = 0;
/** Return a list of games supported by this engine. */
virtual PlainGameList getSupportedGames() const = 0;
/** Query the engine for a PlainGameDescriptor for the specified gameid, if any. */
virtual PlainGameDescriptor findGame(const char *gameId) const = 0;
/**
* Run the engine's game detector on the given list of files, and return a
* (possibly empty) list of games supported by the engine that were
* found among the given files.
*/
virtual DetectedGames detectGames(const Common::FSList &fslist, uint32 skipADFlags = 0, bool skipIncomplete = false) = 0;
/** Returns the number of bytes used for MD5-based detection, or 0 if not supported. */
virtual uint getMD5Bytes() const = 0;
/** Returns the number of game variants or -1 if unknown */
virtual int getGameVariantCount() const {
return -1;
}
/** Returns formatted data from game descriptor for dumping into a file */
virtual void dumpDetectionEntries() const = 0;
/**
* Return a list of engine specified debug channels
*
* If engine has no specified debug channels or not supported yet, then it will return NULL
*
* @return A list of engine specified debug channels
*/
virtual const DebugChannelDef *getDebugChannels() const {
return NULL;
}
};
/**
* A MetaEngine is another factory for Engine instances, and is very similar to MetaEngineDetection.
*
* This class, however, is made of of bridged functionalities that can be used to connect
* an actual Engine with a MetaEngine. Every engine "plugin" provides a hook to get an instance
* of the MetaEngine subclass for that "engine plugin.". For example, SCUMM provides a ScummMetaEngine.
* This is then in turn used for things like instantiating engine objects, listing savefiles,
* querying save metadata, etc.
*
* Since engine plugins can use external runtime libraries, these can live and build inside
* the engine, while a MetaEngine will always build into the executable to be able to detect code.
*/
class MetaEngine : public PluginObject {
protected:
/**
* Convert the current screen contents to a thumbnail. Can be overriden by individual
* engine meta engines to provide their own thumb, such as hiding any on-screen save
* dialog so that it won't appear in the thumbnail.
*/
virtual void getSavegameThumbnail(Graphics::Surface &thumb);
/**
* Finds the first empty save slot that can be used for this target
* @param target Name of a config manager target.
*
* @return The first empty save slot, or -1 if all are occupied.
*/
int findEmptySaveSlot(const char *target);
/**
* Return a list of extra GUI options for the specified target.
*
* If no target is specified, all of the available custom GUI options are
* returned for the plugin (used to set default values).
*
* Currently, this only supports options with checkboxes.
*
* The default implementation returns an empty list.
*
* @param target Name of a config manager target.
*
* @return A list of extra GUI options for an engine plugin and target.
*/
virtual const ExtraGuiOptions getExtraGuiOptions(const Common::String &target) const {
return ExtraGuiOptions();
}
public:
virtual ~MetaEngine() {}
/**
* Name of the engine plugin.
*
* Classes inheriting a MetaEngine must provide an engineID here,
* which can then be used to match an Engine with MetaEngine.
*
* For example, ScummMetaEngineDetection inherits MetaEngineDetection and provides a
* engineID of "scumm". ScummMetaEngine inherits MetaEngine and provides the name
* "Scumm". This way, an Engine can be easily matched with a MetaEngine.
*/
virtual const char *getName() const = 0;
/**
* Instantiate an engine instance based on the settings of
* the currently active ConfMan target.
*
* The MetaEngine queries the ConfMan singleton for data like the target,
* gameid, path etc.
*
* @param syst Pointer to the global OSystem object.
* @param engine Pointer to a pointer that the MetaEngine sets to
* the newly created Engine, or 0 in case of an error.
*
* @return A Common::Error describing the error that occurred, or kNoError.
*/
virtual Common::Error createInstance(OSystem *syst, Engine **engine) = 0;
/**
* Return a list of all save states associated with the given target.
*
* The returned list is guaranteed to be sorted by slot numbers. That
* means smaller slot numbers are always stored before bigger slot numbers.
*
* The caller must ensure that this (Meta)Engine is responsible
* for the specified target. This is done by using findGame on it respectively
* on the associated gameid from the relevant ConfMan entry, if present.
*
* The default implementation returns an empty list.
*
* @note MetaEngines must indicate that this function has been implemented
* via the kSupportsListSaves feature flag.
*
* @param target Name of a config manager target.
*
* @return A list of save state descriptors.
*/
virtual SaveStateList listSaves(const char *target) const;
/**
* Return a list of all save states associated with the given target.
*
* This is a wrapper around the basic listSaves virtual method, but it has
* some extra logic for autosave handling.
*
* @param target Name of a config manager target.
* @param saveMode If true, get the list for a save dialog.
* @return A list of save state descriptors.
*/
SaveStateList listSaves(const char *target, bool saveMode) const;
/**
* Return the slot number that is used for autosaves, or -1 for engines that
* don't support autosave.
*
* @note This should match the engine getAutosaveSlot() method.
*/
virtual int getAutosaveSlot() const {
return 0;
}
/**
* Return the maximum number of save slots that the engine supports.
*
* @note MetaEngines must indicate that this function has been implemented
* via the kSupportsListSaves feature flag.
*
* The default implementation limits the save slots to zero (0).
*
* @return Maximum save slot number supported.
*/
virtual int getMaximumSaveSlot() const {
// For games using the new save format, assume 99 slots by default
return hasFeature(kSavesUseExtendedFormat) ? 99 : 0;
}
/**
* Remove the specified save state.
*
* For most engines, this just means calling _saveFileMan->removeSaveFile().
* Engines that keep an index file will also update it accordingly.
*
* @note MetaEngines must indicate that this function has been implemented
* via the kSupportsDeleteSave feature flag.
*
* @param target Name of a config manager target.
* @param slot Slot number of the save state to be removed.
*/
virtual void removeSaveState(const char *target, int slot) const;
/**
* Return meta information from the specified save state.
*
* Depending on the MetaEngineFeatures set, this can include
* thumbnails, save date and time, play time.
*
* @param target Name of a config manager target.
* @param slot Slot number of the save state.
*/
virtual SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
/**
* Return the name of the save file for the given slot and optional target,
* or a pattern for matching filenames against.
*
* @param saveGameIdx Index of the save, or kSavegameFilePattern
* for returning a filename pattern.
* @param target Game target. If omitted, then the engine ID is used.
*/
virtual Common::String getSavegameFile(int saveGameIdx, const char *target = nullptr) const;
/**
* Return the pattern for save files.
*
* @param target Game target. If omitted, then the engine ID is used.
*/
Common::String getSavegameFilePattern(const char *target = nullptr) const {
return getSavegameFile(kSavegameFilePattern, target);
}
/**
* Return the keymap used by the target.
*/
virtual Common::Array initKeymaps(const char *target) const;
/**
* Register the default values for the settings that the engine uses into the
* configuration manager.
*
* @param target Name of a config manager target.
*/
virtual void registerDefaultSettings(const Common::String &target) const;
/**
* Return a GUI widget container for configuring the specified target options.
*
* Engines can build custom option dialogs from here.
*
* Engines that don't have an Engine tab in the Edit Game dialog, or that use
* ExtraGuiOptions in MetaEngineDetection can return nullptr.
*
* @param boss The widget or dialog that the returned widget is a child of.
* @param name The name that the returned widget must use.
* @param target Name of a config manager target.
*/
virtual GUI::OptionsContainerWidget *buildEngineOptionsWidget(GUI::GuiObject *boss, const Common::String &name, const Common::String &target) const;
/**
* MetaEngine feature flags.
*
* A feature in this context means an ability of the engine that can be
* either available or not.
*/
enum MetaEngineFeature {
/**
* List all save states for a given target that is supported.
*
* For the target to be supported, the listSaves() and
* getMaximumSaveSlot methods must be implemented.
* Used for --list-saves support, as well as the GMM load dialog.
*/
kSupportsListSaves,
/**
* Load from the launcher or command line (-x).
*/
kSupportsLoadingDuringStartup,
/**
* Delete saves from the launcher.
*
* This means that the engine implements the removeSaveState() method.
*/
kSupportsDeleteSave,
/**
* Feature meta information for save states.
*
* This means that the engine implements the querySaveMetaInfos method properly.
*
* Engines implementing meta information always must provide
* the following entries in the save state descriptor queried
* by querySaveMetaInfos:
* - is_deletable - indicates whether a given save is
* safe for deletion
* - is_write_protected - indicates whether a given save
* can be overwritten by the user.
* You do not need to set this, default value is 'false'.
*/
kSavesSupportMetaInfo,
/**
* Feature a thumbnail in savegames.
*
* This means that the engine includes a thumbnail in save states
* returned via querySaveMetaInfo.
* This flag can only be set when kSavesSupportMetaInfo is set.
*/
kSavesSupportThumbnail,
/**
* Feature @c save_date and @c save_time entries in the
* save state returned by querySaveMetaInfo.
*
* These values indicate the date and time when the savegame was created.
* This flag can only be set when kSavesSupportMetaInfo is set.
*/
kSavesSupportCreationDate,
/**
* Feature @c play_time entry in the save state returned by
* querySaveMetaInfo.
*
* This indicates how long the user played the game until the save.
* This flag can only be set when kSavesSupportMetaInfo is set.
*/
kSavesSupportPlayTime,
/**
* This feature is available if the engine's saves can be detected
* with:
* @code
* .###
* @endcode
* where ### corresponds to slot number.
*
* If that is not true, or if the engine uses some unusual way
* of detecting saves and slot numbers, this should be
* unavailable. In that case Save/Load dialog for the engine's
* games is locked during cloud saves sync.
*
* NOTE: This flag is used by cloud code, but also in
* MetaEngine::getSavegameFile(), for common save names.
*/
kSimpleSavesNames,
/**
* Use the default implementation of save header and thumbnail
* appended to the save.
*
* This flag requires the following flags to be set:
* - kSavesSupportMetaInfo
* - kSavesSupportThumbnail
* - kSavesSupportCreationDate
* - kSavesSupportPlayTime
*/
kSavesUseExtendedFormat
};
/**
* Return the achievements platform to use for the specified target.
*
* @param target Name of a config manager target.
*
* @return The achievements platform to use for an engine plugin and target.
*/
virtual Common::AchievementsPlatform getAchievementsPlatform(const Common::String &target) const;
/**
* Return a list of achievement descriptions for the specified target.
*
* @param target Name of a config manager target.
*
* @return A list of achievement descriptions for an engine plugin and target.
*/
virtual const Common::AchievementsInfo getAchievementsInfo(const Common::String &target) const;
/**
* Return the achievement descriptions.
*
* @note The default implementation returns @c nullptr
*
* @return a list of achievement descriptions for an engine.
*/
virtual const Common::AchievementDescriptionList* getAchievementDescriptionList() const {
return nullptr;
}
/**
* Determine whether the engine supports the specified MetaEngine feature.
*
* Used by e.g. the launcher to determine whether to enable the Load button.
*/
virtual bool hasFeature(MetaEngineFeature f) const;
/**
* Write the extended savegame header to the given savegame file.
*/
void appendExtendedSave(Common::OutSaveFile *saveFile, uint32 playtime, Common::String desc, bool isAutosave);
/**
* Write the extended savegame header to the given WriteStream.
*/
void appendExtendedSaveToStream(Common::WriteStream *saveFile, uint32 playtime, Common::String desc, bool isAutosave, uint32 offset = 0);
/**
* Copies an existing save file to the first empty slot which is not autosave
* @param target Name of a config manager target.
* @param slot Slot number of the save state.
*
* @return true if an empty slot was found and the save state was copied. false otherwise.
*/
bool copySaveFileToFreeSlot(const char *target, int slot);
/**
* Parse the extended savegame header to retrieve the SaveStateDescriptor information.
*/
static void parseSavegameHeader(ExtendedSavegameHeader *header, SaveStateDescriptor *desc);
/**
* Populate the given extended savegame header with dummy values.
*
* This is used when failing to read the header from a savegame file.
*/
static void fillDummyHeader(ExtendedSavegameHeader *header);
/**
* Decode the date from a savegame header into a calendar date. The month and day are both 1-based.
*/
static void decodeSavegameDate(const ExtendedSavegameHeader *header, uint16 &outYear, uint8 &outMonth, uint8 &outDay);
/**
* Decode the time from a savegame header into a wall clock time.
*/
static void decodeSavegameTime(const ExtendedSavegameHeader *header, uint8 &outHour, uint8 &outMinute);
/**
* Read the extended savegame header from the given savegame file.
*/
WARN_UNUSED_RESULT static bool readSavegameHeader(Common::InSaveFile *in, ExtendedSavegameHeader *header, bool skipThumbnail = true);
};
/**
* Singleton class that manages all engine plugins.
*/
class EngineManager : public Common::Singleton {
public:
/**
* Given a list of FSNodes in a given directory, detect a set of games contained within.
* @ param skipADFlags Ignore results which are flagged with the ADGF flags specified here (for mass add)
* @ param skipIncomplete Ignore incomplete file/md5/size matches (for mass add)
* Returns an empty list if none are found.
*/
DetectionResults detectGames(const Common::FSList &fslist, uint32 skipADFlags = 0, bool skipIncomplete = false);
/** Find a plugin by its engine ID. */
const Plugin *findPlugin(const Common::String &engineId) const;
/**
* Get the list of all plugins for the type specified.
*
* By default, it will get METAENGINES, for now.
* If usage of actual engines never occurs, the default arguments can be skipped,
* and always have it return PLUGIN_TYPE_ENGINE_DETECTION.
*/
const PluginList &getPlugins(const PluginType fetchPluginType = PLUGIN_TYPE_ENGINE_DETECTION) const;
/** Find a target. */
QualifiedGameDescriptor findTarget(const Common::String &target, const Plugin **plugin = NULL) const;
/**
* List games matching the specified criteria.
*
* If the engine ID is not specified, this scans all the plugins,
* loading them from the disk if necessary. This is a slow operation on
* some platforms and should not be used for the happy path.
*/
QualifiedGameList findGamesMatching(const Common::String &engineId, const Common::String &gameId) const;
/**
* Create a target from the supplied game descriptor.
*
* @return The created target name.
*/
Common::String createTargetForGame(const DetectedGame &game);
/** Upgrade a target to the current configuration format. */
void upgradeTargetIfNecessary(const Common::String &target) const;
/** Generate valid, non-repeated domainName for game*/
Common::String generateUniqueDomain(const Common::String gameId);
private:
/** Find a game across all loaded plugins. */
QualifiedGameList findGameInLoadedPlugins(const Common::String &gameId) const;
/** Use heuristics to complete a target lacking an engine ID. */
void upgradeTargetForEngineId(const Common::String &target) const;
};
/** Convenience shortcut for accessing the engine manager. */
#define EngineMan EngineManager::instance()
/** @} */
#endif