mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-14 13:50:13 +00:00
628 lines
21 KiB
C++
628 lines
21 KiB
C++
/* 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 <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#ifndef ENGINES_METAENGINE_H
|
|
#define ENGINES_METAENGINE_H
|
|
|
|
#include "common/scummsys.h"
|
|
#include "common/error.h"
|
|
#include "common/array.h"
|
|
#include "common/gui_options.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<ExtraGuiOption> 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;
|
|
|
|
/**
|
|
* The default version of this method will just parse the options string from
|
|
* the config manager. However it also allows the meta engine to post process
|
|
* result and add/remove other options as needed.
|
|
*
|
|
* @param optionsString Options string that from the config manager.
|
|
* @param domain Domain of the current target.
|
|
*
|
|
* @return The fully processed options string that is usable by the GUI.
|
|
*
|
|
*/
|
|
virtual Common::String parseAndCustomizeGuiOptions(const Common::String &optionsString, const Common::String &domain) const {
|
|
return parseGameGUIOptions(optionsString);
|
|
}
|
|
|
|
/**
|
|
* 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<Common::Keymap *> 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
|
|
* <target>.###
|
|
* @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 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);
|
|
/**
|
|
* 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<EngineManager> {
|
|
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
|