HOWTO-Convert-Detection-Split

From ScummVM :: Wiki
Jump to navigation Jump to search

The Pull request 5829 aims to allow unloading of detection code before loading the engine code.

This implies some simple changes in the detection code as, currently, detection results are data stored alongside detection code and prevents this unloading.

If you started your engine before June 23, 2024, this guide is for you.

API changes

  • Detection is now split in two phases identifyGame, located in MetaEngineDetection and createInstance, located in MetaEngine.
  • AdvancedMetaEngineDetection is renamed to AdvancedMetaEngineDetectionBase and should not be used by the engines.
  • A new AdvancedMetaEngineDetection template class is created which extends AdvancedMetaEngineDetectionBase and depends on the game description object type.
  • It's the same for AdvancedMetaEngine which is renamed to AdvancedMetaEngineBase and it also should never be used by engines.
  • A new AdvancedMetaEngine template class is created and it also depends on the game description type.
  • The game descriptions structures are expected to provide serialization code to allow for dynamic allocation.
  • A new deleteInstance function has been added to MetaEngine allowing for extra cleanup. The default implementation deletes the engine object.
  • The getMetaEngineDetection, getMetaEngineFromEngine, getEngineFromMetaEngine functions has been deleted as they expect the detection plugin to be loaded alongside the engine code.

Changes for engines inheriting from AdvancedMetaEngineDetection and AdvancedMetaEngine

The AdvancedMetaEngineDetection and AdvancedMetaEngine classes are now templates which must be specialized while inheriting from them.

First the game descriptor structure type must be identified.

This can be checked in the call to the AdvancedMetaEngineDetection constructor:

XxxxMetaEngineDetection() : AdvancedMetaEngineDetection(Xxxx::gameDescriptions,
    sizeof(Xxxx::XxxxGameDescription),
    Xxxx::xxxxGames) {

The type to spot is the operand of the sizeof, here Xxxx::XxxxGameDescription.

There are three possibilities covered here:

  • the game descriptor structure is of type ADGameDescription (simple case)
  • the game descriptor structure is a custom type not making use of any pointers but only integers, enumerations, ... (almost as simple)
  • the game descriptor structure is a custom type with pointers to strings

If the game descriptor structure contains pointers to more complex data, this is not fully covered here but inspiration can be taken from AGS engine.

More help can be get from developers.

Common changes (applies to all AdvancedMetaEngine based engines)

The detection class declaration must be adapted:

class XxxxMetaEngineDetection : public AdvancedMetaEngineDetection<Xxxx::XxxxGameDescription> {

The type to fill in is the game descriptor structure type identified above.

The meta engine class declaration must also be adapted:

class XxxxMetaEngine : public AdvancedMetaEngine<Xxxx::XxxxGameDescription> {

Finally, the detection class constructor definition has to be changed as well:

XxxxMetaEngineDetection::XxxMetaEngineDetection() : AdvancedMetaEngineDetection(
       Xxxx::gameDescriptions, Xxxx::xyzzyGames) {

Note how the second argument is gone (the sizeof one).

Changes when the game descriptor is of type ADGameDescription

Nothing more has to be done

Changes when the game descriptor is of custom type without pointers

The game descriptor structure must be slightly modified to add ready-made serialization code.

The AD_GAME_DESCRIPTION_HELPERS(desc); stance must be added. The desc name must match the ADGameDescription field name.

struct XxxxGameDescription {
    AD_GAME_DESCRIPTION_HELPERS(desc);

    ADGameDescription desc;

    /* The following fields depend on the engine */
    int gameID;
    GameType gameType;
}

Changes when the game descriptor is of custom type with pointers to strings

In this case, two functions must be implemented.

struct XxxGameDescription {
    ADGameDescription desc;

    int32 gameID;
    const char *string1;
    GameType gameType;
    const char *string2;

    uint32 sizeBuffer() const {
        uint32 ret = desc.sizeBuffer();
        ret += ADDynamicDescription::strSizeBuffer(string1);
        ret += ADDynamicDescription::strSizeBuffer(string2);
        return ret;
    }

    void *toBuffer(void *buffer) {
        buffer = desc.toBuffer(buffer);
        buffer = ADDynamicDescription::strToBuffer(buffer, string1);
        buffer = ADDynamicDescription::strToBuffer(buffer, string2);
        return buffer;
    }
};

In the example above, the structure is composed of two pointers to strings string1 and string2.

They both need to be added to buffer size calculation using the ADDynamicDescription::strSizeBuffer helper function.

They also need to be serialized using the ADDynamicDescription::strToBuffer function.

The lines related to desc must be kept to allow storing of ADGameDescription data.

In case the engine redefines createInstance

If the redefinition is due to the use of upgradeTargetIfNecessary, this must be moved to a new identifyGame function override:

Common::Error identifyGame(DetectedGame &game, const void **descriptor) override {
    Engines::upgradeTargetIfNecessary(obsoleteGameIDsTable);
    return AdvancedMetaEngineDetection::identifyGame(game, descriptor);
}

For other cases, the code must be split according to if it depends on detection code or on engine code.

Changes for engines inheriting directly from MetaEngineDetection and MetaEngine

Inheriting directly from MetaEngine is deprecated and users should migrate to AdvancedMetaEngine.

For engines inheriting directly from MetaEngineDetection and MetaEngine, the only changes needed are to adapt the new function signature and add a identifyGame stub. The createInstance API change:

Common::Error XxxxMetaEngine::createInstance(OSystem *syst, Engine **engine,
       const DetectedGame &gameDescriptor, const void *metaEngineDescriptor) {

The new identifyGame function declaration inside XxxxMetaEngineDetection class:

Common::Error identifyGame(DetectedGame &game, const void **descriptor) override;

Its definition:

Common::Error XxxxMetaEngineDetection::identifyGame(DetectedGame &game, const void **descriptor) {
       *descriptor = nullptr;
       game = DetectedGame(getName(), findGame(ConfMan.get("gameid").c_str()));
       return game.gameId.empty() ? Common::kUnknownError : Common::kNoError;
}