HOWTO-Engines
Introduction
This page is meant as a mini-HOWTO which roughly outlines the steps needed to add a new engine to ScummVM. It does not tell you how to create an engine for a given game; rather it is meant to tell a developer how to properly "hook" into ScummVM.
I will assume that you are at least roughly familiar with ScummVM, and have a fresh checkout of our Subversion repository. Note that it's strongly adviced to base your work on the current development version of ScummVM, and not on a release version. This will ease integration of your work.
Overview
Essentially, you will have to implement a subclass of the Engine class. Our Doxygen documentation is your friend and should hopefully explain enough about this, see here: http://scummvm.org/docs/doxygen/html/class Engine.php.
You also must hook yourself into the regular ScummVM main build system. Actually, some ports use custom build system, but their maintainers will usually add your new engine once it has been added to ScummVM.
Finally, you need to make ScummVM aware of your new engine by updating a couple source files (see below).
Steps
In the following I assume your engine is called "quux".
- Add a new directory engines/quux/
- Add engines/quux/module.mk (take a look at module.mk files of 2-3 existing engines to understand the content).
- Add engines/quux/quux.h and engines/quux/quux.cpp; this will contain your Engine subclass (or at least parts of it). It will also contain the plugin interface code (more on that in the next section).
- Modify engines/module.mk by adding your engine. It should be clear what to do by looking at what is done for the other engines there.
- Modify configure; you'll have to add your engine in multiple places. Again, just check out what is done for the existing engines.
- Modify base/plugins.cpp; in particular, you have to add your engine to the list in PluginManager::loadPlugins.
That's it. The difficult part is of course writing the Engine subclass. More on that in the next section!
Important note: Use a C++ namespace for all your work, e.g. "namespace Quux" in this case.
Subclassing Engine
TODO: We should probably give some sample code, maybe even provide a full (empty) Engine demo class. Maybe even provide a real mini engine project somewhere on our site which demonstrates using events, drawing, etc. ? Not sure whether this would be worth the effort, though.
TODO: At the very least, describe the plugin interface: I.e. which functions *must* be implemented, and what they are supposed to do. Once again, sample code would be nice.
Example: engines/quux/quux.h
#ifndef QUUX_H #define QUUX_H #include "base/engine.h" namespace Quux { // our engine debug levels enum { kQuuxDebugExample = 1, kQuuxDebugExample2 = 2 // next must be 4, because the debug levels must be able to be combined with OR // and again be extracted with AND and for example 3 could be then kQuuxDebugExample or // kQuuxDebugExample2, that should be only used then a call should be printed when one of both // is specified }; class QuuxEngine : public Engine { public: QuuxEngine(OSystem *syst); ~QuuxEngine(); virtual int init(GameDetector &detector); virtual int go(); }; } // End of namespace Quux #endif
Example: engines/quux/quux.cpp
#include "common/stdafx.h" #include "backends/fs/fs.h" #include "base/gameDetector.h" #include "base/plugins.h" #include "quux/quux.h" static const GameSettings quux_setting[] = { { "quux", "Quux the Example Module", 0 }, { "quuxcd", "Quux the Example Module (CD version)", 0 }, { 0, 0, 0 } }; GameList Engine_QUUX_gameList() { GameList games; const GameSettings *g = quux_setting; while (g->gameid) { games.push_back(*g); g++; } return games; } DetectedGameList Engine_QUUX_detectGames(const FSList &fslist) { DetectedGameList detectedGames; // Iterate over all files in the given directory for (FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) { if (!file->isDirectory()) { const char *gameName = file->displayName().c_str(); if (0 == scumm_stricmp("README", gameName)) { // You could check the contents of the file now if you need to. detectedGames.push_back(quux_setting[0]); break; } } } return detectedGames; } Engine *Engine_QUUX_create(GameDetector *detector, OSystem *syst) { // At this point you may want to perform a sanity check on the // values in 'detecetor'. return new Quux::QuuxEngine(syst); } REGISTER_PLUGIN(QUUX, "Quux the Example Module") namespace Quux { QuuxEngine::QuuxEngine(OSystem *syst) : Engine(syst) { // Put your engine in a sane state, but do nothing big yet; // in particular, do not load data from files; rather, if you // need to do such things, do them from init(). // Do not initialize graphics here // However this is the place to specify all default directories File::addDefaultDirectory(_gameDataPath + "sound/"); // Here is the right place to set up the engine specific debug levels Common::addSpecialDebugLevel(kQuuxDebugExample, "example", "this is just an example for a engine specific debug level"); Common::addSpecialDebugLevel(kQuuxDebugExample2, "example2", "also an example"); printf("QuuxEngine::QuuxEngine\n"); } QuuxEngine::~QuuxEngine() { // Dispose your resources here printf("QuuxEngine::~QuuxEngine\n"); // Remove all of our debug levels here Common::clearAllSpecialDebugLevels(); } int QuuxEngine::init(GameDetector &detector) { // Initialize graphics using following template // You have to use transactions _system->beginGFXTransaction(); // This is handled by base Engine class and processes command // line parameters _vm->initCommonGFX(detector); // Specify dimensions of game graphics window _system->initSize(width, height); _system->endGFXTransaction(); // Additional setup. printf("QuuxEngine::init\n"); return 0; } int QuuxEngine::go() { // Your main even loop should be (invoked from) here. printf("QuuxEngine::go: Hello, World!\n"); // This test will show up if -d1 and --debugflags=example are specified on the commandline debugC(1, kQuuxDebugExample, "Example debug call"); // This test will show up if --debugflags=example or --debugflags=example2 or both of them and -d3 are specified on the commandline debugC(3, kQuuxDebugExample | kQuuxDebugExample2, "Example debug call two"); return 0; } } // End of namespace Quux
Example: engines/quux/module.mk
MODULE := engines/quux MODULE_OBJS := \ quux.o MODULE_DIRS += \ engines/quux # This module can be built as a plugin ifdef BUILD_PLUGINS PLUGIN := 1 endif # Include common rules include $(srcdir)/common.rules