From 4bfd705fcc91365e61897e9aa157e56d8e846dc3 Mon Sep 17 00:00:00 2001 From: danij Date: Mon, 15 Oct 2012 02:31:04 +0100 Subject: [PATCH] Refactor: Cleanup of the GameCollection implementation Also added a proper NullGame object. --- doomsday/engine/portable/include/dd_games.h | 181 ++++------ doomsday/engine/portable/include/dd_main.h | 10 +- doomsday/engine/portable/include/game.h | 134 +++++-- doomsday/engine/portable/src/cl_main.c | 2 +- doomsday/engine/portable/src/con_config.c | 2 +- doomsday/engine/portable/src/con_data.c | 10 +- doomsday/engine/portable/src/dam_main.c | 2 +- doomsday/engine/portable/src/dd_games.cpp | 336 ++++++------------ doomsday/engine/portable/src/dd_help.c | 2 +- doomsday/engine/portable/src/dd_main.cpp | 134 ++++--- doomsday/engine/portable/src/dd_pinit.c | 2 +- doomsday/engine/portable/src/def_main.cpp | 4 +- doomsday/engine/portable/src/def_read.c | 2 +- doomsday/engine/portable/src/edit_bias.c | 2 +- .../engine/portable/src/finaleinterpreter.c | 2 +- doomsday/engine/portable/src/fs_main.cpp | 2 +- doomsday/engine/portable/src/game.cpp | 165 +++++++-- doomsday/engine/portable/src/p_data.cpp | 2 +- .../engine/portable/src/render/rend_console.c | 2 +- doomsday/engine/portable/src/sv_main.c | 4 +- .../engine/portable/src/sys_reslocator.cpp | 2 +- doomsday/engine/portable/src/ui_mpi.c | 4 +- doomsday/engine/portable/src/uri.c | 16 +- doomsday/engine/portable/src/zipfile.cpp | 8 +- 24 files changed, 544 insertions(+), 486 deletions(-) diff --git a/doomsday/engine/portable/include/dd_games.h b/doomsday/engine/portable/include/dd_games.h index 79d1aaaafb..5b8b6f2c72 100644 --- a/doomsday/engine/portable/include/dd_games.h +++ b/doomsday/engine/portable/include/dd_games.h @@ -37,28 +37,20 @@ struct gamedef_s; #include #include -/** - * @defgroup printGameFlags Print Game Flags. - */ -///@{ -#define PGF_BANNER 0x1 -#define PGF_STATUS 0x2 -#define PGF_LIST_STARTUP_RESOURCES 0x4 -#define PGF_LIST_OTHER_RESOURCES 0x8 - -#define PGF_EVERYTHING (PGF_BANNER|PGF_STATUS|PGF_LIST_STARTUP_RESOURCES|PGF_LIST_OTHER_RESOURCES) -///@} - namespace de { /** - * Games encapsulates a collection of de::Game instances and the logical - * operations which are performed upon it (such as searches and various - * index printing algorithms). + * Encapsulates a collection of de::Game instances and the logical operations + * which are performed upon it (such as searches and various index printing + * algorithms). */ - class Games + class GameCollection { public: + /// The requested game does not exist in the collection. @ingroup errors + DENG2_ERROR(NotFoundError); + + /// Used for returning the result of game searches. @see findAll() struct GameListItem { Game* game; @@ -74,10 +66,13 @@ namespace de }; typedef QList GameList; + typedef QList Games; + public: - Games(); - ~Games(); + GameCollection(); + ~GameCollection(); + /// Register the console commands, variables, etc..., of this module. static void consoleRegister(); /// @return The currently active Game instance. @@ -87,19 +82,13 @@ namespace de Game& nullGame() const; /// Change the currently active game. - Games& setCurrentGame(Game& game); + GameCollection& setCurrentGame(Game& game); /// @return @c true= @a game is the currently active game. inline bool isCurrentGame(Game const& game) const { return &game == ¤tGame(); } - /// @todo Implement a proper null-game object for this. - /// @return @c true= @a game is the special "null-game" object (not a real playable game). - inline bool isNullGame(Game const& game) const { - return &game == &nullGame(); - } - /// @return Total number of registered games. int count() const; @@ -112,68 +101,68 @@ namespace de */ gameid_t id(Game& game) const; - gameid_t gameIdForKey(char const* identityKey) const; - - /// @return Game associated with unique index @a idx else @c NULL. - Game* byIndex(int idx) const; + /** + * @return Game associated with @a identityKey. + * + * @throws NotFoundError if no game is associated with @a identityKey. + */ + Game& byIdentityKey(char const* identityKey) const; - /// @return Game associated with @a identityKey else @c NULL. - Game* byIdentityKey(char const* identityKey) const; + /** + * @return Game associated with @a gameId. + * + * @throws NotFoundError if no game is associated with @a gameId. + */ + Game& byId(gameid_t gameId) const; - /// @return Game associated with @a gameId else @c NULL. - Game* byId(gameid_t gameId) const; + /** + * Provides access to the games for efficient traversals. + */ + Games const& games() const; /** * Finds all games. * - * @param found Set of games that match the result. - * + * @param found List of found games. * @return Number of games found. */ int findAll(GameList& found); /** - * Add a new Game to this collection. - * - * @attention Assumes @a game is not already in the collection. + * Find the first playable game in this collection (in registration order). + * @return The found game else @c NULL. */ - Games& add(Game& game); - - /// @return The first playable game in the collection according to registration order. Game* firstPlayable() const; /** - * Try to locate all startup resources for @a game. - */ - Games& locateStartupResources(Game& game); - - /** - * Try to locate all startup resources for all registered games. + * Add a new Game to this collection. If @a game is already present in the + * collection this is no-op. + * + * @param game Game to be added. + * @return This collection. */ - Games& locateAllResources(); + GameCollection& add(Game& game); /** - * Print extended information about game @a info. - * @param info Game record to be printed. - * @param flags &ref printGameFlags + * Try to locate all startup resources for @a game. + * @return This collection. */ - void print(Game& game, int flags) const; + GameCollection& locateStartupResources(Game& game); /** - * Print a game mode banner with rulers. + * Try to locate all startup resources for all registered games. + * @return This collection. */ - static void printBanner(Game& game); + GameCollection& locateAllResources(); /** - * Print the list of resources for @a Game. + * @return Game associated with unique index @a idx. * - * @param game Game to list resources of. - * @param printStatus @c true= Include the current availability/load status - * of each resource. - * @param rflags Only consider resources whose @ref resourceFlags match - * this value. If @c <0 the flags are ignored. + * @deprecated Iterate games() instead. + * + * @throws NotFoundError if no game is associated with index @a idx. */ - static void printResources(Game& game, bool printStatus, int rflags); + Game& byIndex(int idx) const; private: struct Instance; @@ -189,90 +178,46 @@ extern "C" { * C wrapper API: */ -struct games_s; -typedef struct games_s Games; +struct gamecollection_s; +typedef struct gamecollection_s GameCollection; /// @return The currently active Game instance. -Game* Games_CurrentGame(Games* games); - -/// @return The special "null" Game instance. -Game* Games_NullGame(Games* games); +Game* GameCollection_CurrentGame(GameCollection* games); /// @return Total number of registered games. -int Games_Count(Games* games); +int GameCollection_Count(GameCollection* games); /// @return Number of games marked as currently playable. -int Games_NumPlayable(Games* games); +int GameCollection_NumPlayable(GameCollection* games); /** * @param game Game instance. * @return Unique identifier associated with @a game. */ -gameid_t Games_Id(Games* games, Game* game); +gameid_t GameCollection_Id(GameCollection* games, Game* game); /** - * @return Game associated with unique index @a idx else @c NULL. + * @return Game associated with @a gameId else @c NULL. */ -Game* Games_ByIndex(Games* games, int idx); +Game* GameCollection_ById(GameCollection* games, gameid_t gameId); /** * @return Game associated with @a identityKey else @c NULL. */ -Game* Games_ByIdentityKey(Games* games, char const* identityKey); +Game* GameCollection_ByIdentityKey(GameCollection* games, char const* identityKey); /** - * @return Game associated with @a gameId else @c NULL. - */ -Game* Games_ById(Games* games, gameid_t gameId); - -/** - * Is this the special "null-game" object (not a real playable game). - * @todo Implement a proper null-game object for this. + * @return Game associated with unique index @a idx else @c NULL. */ -boolean Games_IsNullObject(Games* games, Game const* game); +Game* GameCollection_ByIndex(GameCollection* games, int idx); /// @return The first playable game in the collection according to registration order. -Game* Games_FirstPlayable(Games* games); +Game* GameCollection_FirstPlayable(GameCollection* games); /** * Try to locate all startup resources for all registered games. */ -void Games_LocateAllResources(Games* games); - -/** - * @defgroup printGameFlags Print Game Flags. - */ -///@{ -#define PGF_BANNER 0x1 -#define PGF_STATUS 0x2 -#define PGF_LIST_STARTUP_RESOURCES 0x4 -#define PGF_LIST_OTHER_RESOURCES 0x8 - -#define PGF_EVERYTHING (PGF_BANNER|PGF_STATUS|PGF_LIST_STARTUP_RESOURCES|PGF_LIST_OTHER_RESOURCES) -///@} - -/** - * Print extended information about game @a info. - * @param info Game record to be printed. - * @param flags &see printGameFlags - */ -void Games_Print(Games* games, Game* game, int flags); - -/** - * Print a game mode banner with rulers. - */ -void Games_PrintBanner(Game* game); - -/** - * Print the list of resources for @a Game. - * - * @param game Game to list resources of. - * @param printStatus @c true= Include the current availability/load status - * of each resource. - * @param rflags Only consider resources whose @ref resourceFlags match - * this value. If @c <0 the flags are ignored. - */ -void Games_PrintResources(Game* game, boolean printStatus, int rflags); +void GameCollection_LocateAllResources(GameCollection* games); D_CMD(ListGames); diff --git a/doomsday/engine/portable/include/dd_main.h b/doomsday/engine/portable/include/dd_main.h index 0ddb9d370b..8fec9325e4 100644 --- a/doomsday/engine/portable/include/dd_main.h +++ b/doomsday/engine/portable/include/dd_main.h @@ -55,7 +55,8 @@ extern "C" { # define DEBUG_VERBOSE2_Message(code) #endif -struct games_s; +struct gamecollection_s; +struct game_s; extern int verbose; //extern FILE* outFile; // Output file for console messages. @@ -73,8 +74,11 @@ extern finaleid_t titleFinale; extern GETGAMEAPI GetGameAPI; #endif -/// @return The main Games collection. -struct games_s* App_Games(); +/// @return The Game collection. +struct gamecollection_s* App_GameCollection(); + +/// @return The current Game in the collection. +struct game_s* App_CurrentGame(); int DD_EarlyInit(void); void DD_FinishInitializationAfterWindowReady(void); diff --git a/doomsday/engine/portable/include/game.h b/doomsday/engine/portable/include/game.h index 632d4808d9..34b9813188 100644 --- a/doomsday/engine/portable/include/game.h +++ b/doomsday/engine/portable/include/game.h @@ -31,6 +31,18 @@ extern "C" { #endif +/** + * @defgroup printGameFlags Print Game Flags. + */ +///@{ +#define PGF_BANNER 0x1 +#define PGF_STATUS 0x2 +#define PGF_LIST_STARTUP_RESOURCES 0x4 +#define PGF_LIST_OTHER_RESOURCES 0x8 + +#define PGF_EVERYTHING (PGF_BANNER|PGF_STATUS|PGF_LIST_STARTUP_RESOURCES|PGF_LIST_OTHER_RESOURCES) +///@} + struct AbstractResource_s; struct gamedef_s; @@ -42,6 +54,8 @@ struct gamedef_s; namespace de { +class GameCollection; + /** * Game. Used to record top-level game configurations registered by * the loaded game plugin(s). @@ -61,25 +75,28 @@ class Game */ Game(char const* identityKey, ddstring_t const* dataPath, ddstring_t const* defsPath, char const* configDir, char const* title = "Unnamed", char const* author = "Unknown"); - ~Game(); + virtual ~Game(); + + /// @return Collection in which this game exists. + GameCollection& Game::collection() const; /// @return Unique plugin identifier attributed to that which registered this. - pluginid_t pluginId(); + pluginid_t pluginId() const; /// @return String containing the identity key. - ddstring_t const& identityKey(); + ddstring_t const& identityKey() const; /// @return String containing the default title. - ddstring_t const& title(); + ddstring_t const& title() const; /// @return String containing the default author. - ddstring_t const& author(); + ddstring_t const& author() const; /// @return String containing the name of the main config file. - ddstring_t const& mainConfig(); + ddstring_t const& mainConfig() const; /// @return String containing the name of the binding config file. - ddstring_t const& bindingConfig(); + ddstring_t const& bindingConfig() const; /** * @note Unless caller is the resource locator then you probably shouldn't be calling. @@ -87,7 +104,7 @@ class Game * * @return String containing the base data-class resource directory. */ - ddstring_t const& dataPath(); + ddstring_t const& dataPath() const; /** * @note Unless caller is the resource locator then you probably shouldn't be calling. @@ -95,7 +112,7 @@ class Game * * @return String containing the base defs-class resource directory. */ - ddstring_t const& defsPath(); + ddstring_t const& defsPath() const; /** * Change the identfier of the plugin associated with this. @@ -118,7 +135,7 @@ class Game */ bool isRequiredResource(char const* absolutePath); - bool allStartupResourcesFound(); + bool allStartupResourcesFound() const; /** * Retrieve a subset of the resource collection associated with this. @@ -126,7 +143,7 @@ class Game * @param rclass Class of resource to collect. * @return Vector of selected resource records. */ - struct AbstractResource_s* const* resources(resourceclass_t rclass, int* count); + struct AbstractResource_s* const* resources(resourceclass_t rclass, int* count) const; // Static members ------------------------------------------------------------------ @@ -137,11 +154,76 @@ class Game */ static Game* fromDef(GameDef const& def); + /** + * Print a game mode banner with rulers. + * + * @todo This has been moved here so that strings like the game title and author + * can be overridden (e.g., via DEHACKED). Make it so! + */ + static void printBanner(Game const& game); + + /** + * Print the list of resources for @a Game. + * + * @param game Game to list resources of. + * @param printStatus @c true= Include the current availability/load status + * of each resource. + * @param rflags Only consider resources whose @ref resourceFlags match + * this value. If @c <0 the flags are ignored. + */ + static void printResources(Game const& game, bool printStatus, int rflags); + + /** + * Print extended information about game @a info. + * + * @param info Game record to be printed. + * @param flags &ref printGameFlags + */ + static void print(Game const& game, int flags); + private: struct Instance; Instance* d; }; +/** + * The special "null" Game object. + */ +class NullGame : public Game +{ +public: + /// General exception for invalid action on a NULL object. @ingroup errors + DENG2_ERROR(NullObjectError); + +public: + NullGame(ddstring_t const* dataPath, ddstring_t const* defsPath); + + Game& addResource(resourceclass_t /*rclass*/, struct AbstractResource_s& /*record*/) { + throw NullObjectError("NullGame::addResource", "Invalid action on null-object"); + } + + bool isRequiredResource(char const* /*absolutePath*/) { + return false; // Never. + } + + bool allStartupResourcesFound() const { + return true; // Always. + } + + struct AbstractResource_s* const* resources(resourceclass_t /*rclass*/, int* /*count*/) const { + return 0; + } + + static Game* fromDef(GameDef const& /*def*/) { + throw NullObjectError("NullGame::fromDef", "Not valid for null-object"); + } +}; + +/// @return @c true= @a game is a "null-game" object (not a real playable game). +inline bool isNullGame(Game const& game) { + return !!dynamic_cast(&game); +} + } // namespace de extern "C" { @@ -158,33 +240,41 @@ Game* Game_New(char const* identityKey, ddstring_t const* dataPath, ddstring_t c void Game_Delete(Game* game); +boolean Game_IsNullObject(Game const* game); + struct game_s* Game_AddResource(Game* game, resourceclass_t rclass, struct AbstractResource_s* record); boolean Game_IsRequiredResource(Game* game, char const* absolutePath); -boolean Game_AllStartupResourcesFound(Game* game); +boolean Game_AllStartupResourcesFound(Game const* game); Game* Game_SetPluginId(Game* game, pluginid_t pluginId); -pluginid_t Game_PluginId(Game* game); +pluginid_t Game_PluginId(Game const* game); + +ddstring_t const* Game_IdentityKey(Game const* game); + +ddstring_t const* Game_Title(Game const* game); + +ddstring_t const* Game_Author(Game const* game); -ddstring_t const* Game_IdentityKey(Game* game); +ddstring_t const* Game_MainConfig(Game const* game); -ddstring_t const* Game_Title(Game* game); +ddstring_t const* Game_BindingConfig(Game const* game); -ddstring_t const* Game_Author(Game* game); +struct AbstractResource_s* const* Game_Resources(Game const* game, resourceclass_t rclass, int* count); -ddstring_t const* Game_MainConfig(Game* game); +ddstring_t const* Game_DataPath(Game const* game); -ddstring_t const* Game_BindingConfig(Game* game); +ddstring_t const* Game_DefsPath(Game const* game); -struct AbstractResource_s* const* Game_Resources(Game* game, resourceclass_t rclass, int* count); +Game* Game_FromDef(GameDef const* def); -ddstring_t const* Game_DataPath(Game* game); +void Game_PrintBanner(Game const* game); -ddstring_t const* Game_DefsPath(Game* game); +void Game_PrintResources(Game const* game, boolean printStatus, int rflags); -struct game_s* Game_FromDef(GameDef const* def); +void Game_Print(Game const* game, int flags); #ifdef __cplusplus } // extern "C" diff --git a/doomsday/engine/portable/src/cl_main.c b/doomsday/engine/portable/src/cl_main.c index 1fd3b6adb3..6b54d6d993 100644 --- a/doomsday/engine/portable/src/cl_main.c +++ b/doomsday/engine/portable/src/cl_main.c @@ -138,7 +138,7 @@ void Cl_SendHello(void) // The game mode is included in the hello packet. memset(buf, 0, sizeof(buf)); - strncpy(buf, Str_Text(Game_IdentityKey(Games_CurrentGame(App_Games()))), sizeof(buf) - 1); + strncpy(buf, Str_Text(Game_IdentityKey(App_CurrentGame())), sizeof(buf) - 1); #ifdef _DEBUG Con_Message("Cl_SendHello: game mode = %s\n", buf); diff --git a/doomsday/engine/portable/src/con_config.c b/doomsday/engine/portable/src/con_config.c index 7c815cf167..7b5ae8adc9 100644 --- a/doomsday/engine/portable/src/con_config.c +++ b/doomsday/engine/portable/src/con_config.c @@ -293,7 +293,7 @@ boolean Con_WriteState(const char* fileName, const char* bindingsFileName) */ void Con_SaveDefaults(void) { - Con_WriteState(cfgFile, (!isDedicated? Str_Text(Game_BindingConfig(Games_CurrentGame(App_Games()))) : 0)); + Con_WriteState(cfgFile, (!isDedicated? Str_Text(Game_BindingConfig(App_CurrentGame())) : 0)); } D_CMD(WriteConsole) diff --git a/doomsday/engine/portable/src/con_data.c b/doomsday/engine/portable/src/con_data.c index 5a90d7a87b..bc67ce7856 100644 --- a/doomsday/engine/portable/src/con_data.c +++ b/doomsday/engine/portable/src/con_data.c @@ -390,7 +390,7 @@ static void updateKnownWords(void) PathDirectory_Iterate2_Const(cvarDirectory, PCF_NO_BRANCH, NULL, PATHDIRECTORY_NOHASH, countVariable, &countCVarParams); // Build the known words table. - numKnownWords = numUniqueNamedCCmds + countCVarParams.count + numCAliases + Games_Count(App_Games()); + numKnownWords = numUniqueNamedCCmds + countCVarParams.count + numCAliases + GameCollection_Count(App_GameCollection()); len = sizeof(knownword_t) * numKnownWords; knownWords = realloc(knownWords, len); memset(knownWords, 0, len); @@ -430,10 +430,10 @@ static void updateKnownWords(void) } // Add games? - gameCount = Games_Count(App_Games()); + gameCount = GameCollection_Count(App_GameCollection()); for(i = 0; i < gameCount; ++i) { - Game* game = Games_ByIndex(App_Games(), i+1); + Game* game = GameCollection_ByIndex(App_GameCollection(), i); knownWords[c].type = WT_GAME; knownWords[c].data = game; @@ -1593,10 +1593,10 @@ static void printHelpAbout(const char* query) if(found == 0) // Perhaps a game? { - Game* game = Games_ByIdentityKey(App_Games(), query); + Game* game = GameCollection_ByIdentityKey(App_GameCollection(), query); if(game) { - Games_Print(App_Games(), game, PGF_EVERYTHING); + Game_Print(game, PGF_EVERYTHING); found = true; } } diff --git a/doomsday/engine/portable/src/dam_main.c b/doomsday/engine/portable/src/dam_main.c index f858c55415..1effe0873b 100644 --- a/doomsday/engine/portable/src/dam_main.c +++ b/doomsday/engine/portable/src/dam_main.c @@ -173,7 +173,7 @@ AutoStr* DAM_ComposeCacheDir(const char* sourcePath) if(!sourcePath || !sourcePath[0]) return NULL; - gameIdentityKey = Game_IdentityKey(Games_CurrentGame(App_Games())); + gameIdentityKey = Game_IdentityKey(App_CurrentGame()); mapPathIdentifier = calculateIdentifierForMapPath(sourcePath); Str_InitStd(&mapFileName); F_FileName(&mapFileName, sourcePath); diff --git a/doomsday/engine/portable/src/dd_games.cpp b/doomsday/engine/portable/src/dd_games.cpp index 38b841aab8..e08624496a 100644 --- a/doomsday/engine/portable/src/dd_games.cpp +++ b/doomsday/engine/portable/src/dd_games.cpp @@ -32,34 +32,28 @@ namespace de { static bool validateResource(AbstractResource* rec); -struct Games::Instance +struct GameCollection::Instance { - Games& self; + GameCollection& self; - /// Game collection. - Game** games; - int gamesCount; + /// The actual collection. + GameCollection::Games games; /// Currently active game (in this collection). - Game* theGame; + Game* currentGame; /// Special "null-game" object for this collection. - Game* nullGame; + NullGame* nullGame; - Instance(Games& d) - : self(d), games(0), gamesCount(0), theGame(0), nullGame(0) + Instance(GameCollection& d) + : self(d), games(), currentGame(0), nullGame(0) {} ~Instance() { - if(games) + DENG2_FOR_EACH(i, games, GameCollection::Games::const_iterator) { - for(int i = 0; i < gamesCount; ++i) - { - delete games[i]; - } - M_Free(games); games = 0; - gamesCount = 0; + delete *i; } if(nullGame) @@ -67,36 +61,11 @@ struct Games::Instance delete nullGame; nullGame = NULL; } - theGame = NULL; - } - - int index(Game const& game) - { - if(&game != nullGame) - { - for(int i = 0; i < gamesCount; ++i) - { - if(&game == games[i]) - return i+1; - } - } - return 0; - } - - Game* findByIdentityKey(char const* identityKey) - { - DENG_ASSERT(identityKey && identityKey[0]); - for(int i = 0; i < gamesCount; ++i) - { - Game* game = games[i]; - if(!stricmp(Str_Text(&game->identityKey()), identityKey)) - return game; - } - return NULL; // Not found. + currentGame = NULL; } }; -Games::Games() +GameCollection::GameCollection() { d = new Instance(*this); @@ -118,102 +87,119 @@ Games::Games() F_ExpandBasePath(&defsPath, &defsPath); F_AppendMissingSlash(&defsPath); - d->theGame = d->nullGame = new Game("null-game", &dataPath, &defsPath, "doomsday", 0, 0); + d->currentGame = d->nullGame = new NullGame(&dataPath, &defsPath); Str_Free(&defsPath); Str_Free(&dataPath); } -Games::~Games() +GameCollection::~GameCollection() { delete d; } -Game& Games::currentGame() const +Game& GameCollection::currentGame() const { - return *d->theGame; + return *d->currentGame; } -Game& Games::nullGame() const +Game& GameCollection::nullGame() const { return *d->nullGame; } -Games& Games::setCurrentGame(Game& game) +GameCollection& GameCollection::setCurrentGame(Game& game) { - d->theGame = &game; + // Ensure the specified game is actually in this collection (NullGame is implicitly). + DENG_ASSERT(isNullGame(game) || id(game) > 0); + d->currentGame = &game; return *this; } -int Games::numPlayable() const +int GameCollection::numPlayable() const { int count = 0; - for(int i = 0; i < d->gamesCount; ++i) + DENG2_FOR_EACH(i, d->games, Games::const_iterator) { - de::Game* game = d->games[i]; + Game* game = *i; if(!game->allStartupResourcesFound()) continue; ++count; } return count; } -Game* Games::firstPlayable() const +Game* GameCollection::firstPlayable() const { - for(int i = 0; i < d->gamesCount; ++i) + DENG2_FOR_EACH(i, d->games, Games::const_iterator) { - Game* game = d->games[i]; + Game* game = *i; if(game->allStartupResourcesFound()) return game; } return NULL; } -int Games::count() const +int GameCollection::count() const { - return d->gamesCount; + return d->games.count(); } -Game* Games::byIndex(int idx) const +gameid_t GameCollection::id(Game& game) const { - if(idx > 0 && idx <= d->gamesCount) - return d->games[idx-1]; - return NULL; + if(&game == d->nullGame) return 0; // Invalid id. + int idx = d->games.indexOf(&game); + if(idx < 0) throw NotFoundError("GameCollection::id", QString("Game %p is not part of this collection").arg(de::dintptr(&game))); + return gameid_t(idx+1); } -Game* Games::byIdentityKey(char const* identityKey) const +Game& GameCollection::byId(gameid_t gameId) const +{ + if(gameId <= 0 || gameId > d->games.count()) + throw NotFoundError("GameCollection::byId", QString("There is no Game with id %i").arg(gameId)); + return *d->games[gameId-1]; +} + +Game& GameCollection::byIdentityKey(char const* identityKey) const { if(identityKey && identityKey[0]) - return d->findByIdentityKey(identityKey); - return NULL; + { + DENG2_FOR_EACH(i, d->games, GameCollection::Games::const_iterator) + { + Game* game = *i; + if(!Str_CompareIgnoreCase(&game->identityKey(), identityKey)) + return *game; + } + } + throw NotFoundError("GameCollection::byIdentityKey", QString("There is no Game with identity key \"%s\"").arg(identityKey)); } -Game* Games::byId(gameid_t gameId) const +Game& GameCollection::byIndex(int idx) const { - if(gameId > 0 && gameId <= d->gamesCount) - return d->games[gameId-1]; - return NULL; // Not found. + if(idx < 0 || idx > d->games.count()) + throw NotFoundError("GameCollection::byIndex", QString("There is no Game at index %i").arg(idx)); + return *d->games[idx]; } -gameid_t Games::id(Game& game) const +GameCollection::Games const& GameCollection::games() const { - if(&game == d->nullGame) return 0; // Invalid id. - return (gameid_t) d->index(game); + return d->games; } -int Games::findAll(GameList& found) +int GameCollection::findAll(GameList& found) { int numFoundSoFar = found.count(); - for(int i = 0; i < d->gamesCount; ++i) + DENG2_FOR_EACH(i, d->games, Games::const_iterator) { - found.push_back(GameListItem(d->games[i])); + found.push_back(GameListItem(*i)); } return found.count() - numFoundSoFar; } -Games& Games::add(Game& game) +GameCollection& GameCollection::add(Game& game) { - d->games = (Game**) M_Realloc(d->games, sizeof(*d->games) * ++d->gamesCount); - if(!d->games) Con_Error("Games::add: Failed on allocation of %lu bytes enlarging Game list.", (unsigned long) (sizeof(*d->games) * d->gamesCount)); - d->games[d->gamesCount-1] = &game; + if(d->games.indexOf(&game) < 0) + { + d->games.push_back(&game); + } return *this; } @@ -287,7 +273,7 @@ static bool validateResource(AbstractResource* rec) validated = true; break; } - else if(recognizeZIP(Str_Text(path), (void*)AbstractResource_IdentityKeys(rec))) + if(recognizeZIP(Str_Text(path), (void*)AbstractResource_IdentityKeys(rec))) { validated = true; break; @@ -304,13 +290,13 @@ static bool validateResource(AbstractResource* rec) return validated; } -Games& Games::locateStartupResources(Game& game) +GameCollection& GameCollection::locateStartupResources(Game& game) { - Game* oldGame = d->theGame; - if(d->theGame != &game) + Game* oldGame = d->currentGame; + if(d->currentGame != &game) { /// @attention Kludge: Temporarily switch Game. - d->theGame = &game; + d->currentGame = &game; // Re-init the resource locator using the search paths of this Game. F_ResetAllResourceNamespaces(); } @@ -331,10 +317,10 @@ Games& Games::locateStartupResources(Game& game) } } - if(d->theGame != oldGame) + if(d->currentGame != oldGame) { // Kludge end - Restore the old Game. - d->theGame = oldGame; + d->currentGame = oldGame; // Re-init the resource locator using the search paths of this Game. F_ResetAllResourceNamespaces(); } @@ -343,111 +329,38 @@ Games& Games::locateStartupResources(Game& game) static int locateAllResourcesWorker(void* parameters) { - Games* games = (Games*) parameters; - for(int i = 0; i < games->count(); ++i) + GameCollection* gameCollection = (GameCollection*) parameters; + int n = 0; + DENG2_FOR_EACH(i, gameCollection->games(), GameCollection::Games::const_iterator) { - Game* game = games->byIndex(i+1); + Game* game = *i; VERBOSE( Con_Printf("Locating resources for \"%s\"...\n", Str_Text(&game->title())) ) - games->locateStartupResources(*game); - Con_SetProgress((i + 1) * 200 / games->count() - 1); + gameCollection->locateStartupResources(*game); + Con_SetProgress((n + 1) * 200 / gameCollection->count() - 1); - VERBOSE( games->print(*game, PGF_LIST_STARTUP_RESOURCES|PGF_STATUS) ) + VERBOSE( Game::print(*game, PGF_LIST_STARTUP_RESOURCES|PGF_STATUS) ) + ++n; } BusyMode_WorkerEnd(); return 0; } -Games& Games::locateAllResources() +GameCollection& GameCollection::locateAllResources() { BusyMode_RunNewTaskWithName(BUSYF_STARTUP | BUSYF_PROGRESS_BAR | (verbose? BUSYF_CONSOLE_OUTPUT : 0), locateAllResourcesWorker, (void*)this, "Locating game resources..."); return *this; } -/** - * @todo This has been moved here so that strings like the game title and author can - * be overridden (e.g., via DEHACKED). Make it so! - */ -void Games::printBanner(Game& game) -{ - Con_PrintRuler(); - Con_FPrintf(CPF_WHITE | CPF_CENTER, "%s\n", Str_Text(&game.title())); - Con_PrintRuler(); -} - -void Games::printResources(Game& game, bool printStatus, int rflags) -{ - size_t count = 0; - for(uint i = 0; i < RESOURCECLASS_COUNT; ++i) - { - AbstractResource* const* records = game.resources((resourceclass_t)i, 0); - if(!records) continue; - - for(AbstractResource* const* recordIt = records; *recordIt; recordIt++) - { - AbstractResource* rec = *recordIt; - - if(rflags >= 0 && (rflags & AbstractResource_ResourceFlags(rec))) - { - AbstractResource_Print(rec, printStatus); - count += 1; - } - } - } - - if(count == 0) - Con_Printf(" None\n"); -} - -void Games::print(Game& game, int flags) const -{ - if(isNullGame(game)) - flags &= ~PGF_BANNER; - -#if _DEBUG - Con_Printf("pluginid:%i data:\"%s\" defs:\"%s\"\n", int(game.pluginId()), - F_PrettyPath(Str_Text(&game.dataPath())), - F_PrettyPath(Str_Text(&game.defsPath()))); -#endif - - if(flags & PGF_BANNER) - printBanner(game); - - if(!(flags & PGF_BANNER)) - Con_Printf("Game: %s - ", Str_Text(&game.title())); - else - Con_Printf("Author: "); - Con_Printf("%s\n", Str_Text(&game.author())); - Con_Printf("IdentityKey: %s\n", Str_Text(&game.identityKey())); - - if(flags & PGF_LIST_STARTUP_RESOURCES) - { - Con_Printf("Startup resources:\n"); - printResources(game, (flags & PGF_STATUS) != 0, RF_STARTUP); - } - - if(flags & PGF_LIST_OTHER_RESOURCES) - { - Con_Printf("Other resources:\n"); - Con_Printf(" "); - printResources(game, /*(flags & PGF_STATUS) != 0*/false, 0); - } - - if(flags & PGF_STATUS) - Con_Printf("Status: %s\n", isCurrentGame(game)? "Loaded" : - game.allStartupResourcesFound()? "Complete/Playable" : - "Incomplete/Not playable"); -} - } // namespace de D_CMD(ListGames) { DENG_UNUSED(src); DENG_UNUSED(argc); DENG_UNUSED(argv); - de::Games* games = reinterpret_cast(App_Games()); + de::GameCollection* games = reinterpret_cast(App_GameCollection()); if(!games || !games->count()) { Con_Printf("No Registered Games.\n"); @@ -458,13 +371,13 @@ D_CMD(ListGames) Con_Printf("Key: '!'= Incomplete/Not playable '*'= Loaded\n"); Con_PrintRuler(); - de::Games::GameList found; + de::GameCollection::GameList found; games->findAll(found); // Sort so we get a nice alphabetical list. qSort(found.begin(), found.end()); int numCompleteGames = 0; - DENG2_FOR_EACH(i, found, de::Games::GameList::const_iterator) + DENG2_FOR_EACH(i, found, de::GameCollection::GameList::const_iterator) { de::Game* game = i->game; @@ -489,96 +402,81 @@ D_CMD(ListGames) */ #define TOINTERNAL(inst) \ - (inst) != 0? reinterpret_cast(inst) : NULL + (inst) != 0? reinterpret_cast(inst) : NULL #define TOINTERNAL_CONST(inst) \ - (inst) != 0? reinterpret_cast(inst) : NULL + (inst) != 0? reinterpret_cast(inst) : NULL #define SELF(inst) \ DENG2_ASSERT(inst); \ - de::Games* self = TOINTERNAL(inst) + de::GameCollection* self = TOINTERNAL(inst) #define SELF_CONST(inst) \ DENG2_ASSERT(inst); \ - de::Games const* self = TOINTERNAL_CONST(inst) + de::GameCollection const* self = TOINTERNAL_CONST(inst) -Game* Games_CurrentGame(Games* games) +Game* GameCollection_CurrentGame(GameCollection* games) { SELF(games); return reinterpret_cast(&self->currentGame()); } -Game* Games_NullGame(Games* games) -{ - SELF(games); - return reinterpret_cast(&self->nullGame()); -} - -int Games_NumPlayable(Games* games) +int GameCollection_NumPlayable(GameCollection* games) { SELF(games); return self->numPlayable(); } -Game* Games_FirstPlayable(Games* games) +Game* GameCollection_FirstPlayable(GameCollection* games) { SELF(games); return reinterpret_cast(self->firstPlayable()); } -int Games_Count(Games* games) +int GameCollection_Count(GameCollection* games) { SELF(games); return self->count(); } -Game* Games_ByIndex(Games* games, int idx) -{ - SELF(games); - return reinterpret_cast(self->byIndex(idx)); -} - -Game* Games_ByIdentityKey(Games* games, char const* identityKey) +Game* GameCollection_ByIndex(GameCollection* games, int idx) { SELF(games); - return reinterpret_cast(self->byIdentityKey(identityKey)); + try + { + return reinterpret_cast(&self->byIndex(idx)); + } + catch(de::GameCollection::NotFoundError) + {} // Ignore error. + return 0; // Not found. } -gameid_t Games_Id(Games* games, Game* game) +Game* GameCollection_ByIdentityKey(GameCollection* games, char const* identityKey) { SELF(games); - if(!game) return 0; // Invalid id. - return self->id(*reinterpret_cast(game)); + try + { + return reinterpret_cast(&self->byIdentityKey(identityKey)); + } + catch(de::GameCollection::NotFoundError) + {} // Ignore error. + return 0; // Not found. } -boolean Games_IsNullObject(Games* games, Game const* game) +gameid_t GameCollection_Id(GameCollection* games, Game* game) { SELF(games); - if(!game) return false; - return self->isNullGame(*reinterpret_cast(game)); + try + { + return self->id(*reinterpret_cast(game)); + } + catch(de::GameCollection::NotFoundError) + {} // Ignore error. + return 0; // Invalid id. } -void Games_LocateAllResources(Games* games) +void GameCollection_LocateAllResources(GameCollection* games) { SELF(games); self->locateAllResources(); } - -void Games_Print(Games* games, Game* game, int flags) -{ - if(!game) return; - SELF(games); - self->print(*reinterpret_cast(game), flags); -} - -void Games_PrintBanner(Game* game) -{ - if(!game) return; - de::Games::printBanner(*reinterpret_cast(game)); -} - -void Games_PrintResources(Game* game, boolean printStatus, int rflags) -{ - if(!game) return; - de::Games::printResources(*reinterpret_cast(game), CPP_BOOL(printStatus), rflags); -} diff --git a/doomsday/engine/portable/src/dd_help.c b/doomsday/engine/portable/src/dd_help.c index dcc93bf22d..37672648e6 100644 --- a/doomsday/engine/portable/src/dd_help.c +++ b/doomsday/engine/portable/src/dd_help.c @@ -326,7 +326,7 @@ void DD_ReadGameHelp(void) return; // Nothing to do. Str_Init(&helpFileName); - Str_Appendf(&helpFileName, "%sconhelp.txt", Str_Text(Game_DataPath(Games_CurrentGame(App_Games())))); + Str_Appendf(&helpFileName, "%sconhelp.txt", Str_Text(Game_DataPath(App_CurrentGame()))); F_ExpandBasePath(&helpFileName, &helpFileName); DH_ReadStrings(Str_Text(&helpFileName)); Str_Free(&helpFileName); diff --git a/doomsday/engine/portable/src/dd_main.cpp b/doomsday/engine/portable/src/dd_main.cpp index 09df0f0d5e..59802af60a 100644 --- a/doomsday/engine/portable/src/dd_main.cpp +++ b/doomsday/engine/portable/src/dd_main.cpp @@ -91,8 +91,8 @@ static size_t numSessionResourceFileList; extern GETGAMEAPI GetGameAPI; #endif -// The Games collection. -static de::Games* games; +// The Game collection. +static de::GameCollection* games; D_CMD(CheckForUpdates) { @@ -215,7 +215,7 @@ static void destroyPathList(ddstring_t*** list, size_t* listSize) boolean DD_GameLoaded(void) { DENG_ASSERT(games); - return !games->isNullGame(games->currentGame()); + return !isNullGame(games->currentGame()); } void DD_DestroyGames(void) @@ -450,7 +450,7 @@ static int DD_LoadGameStartupResourcesWorker(void* parameters) if(p->initiatedBusyMode) Con_SetProgress(50); - if(!games->isNullGame(games->currentGame())) + if(DD_GameLoaded()) { ddstring_t temp; @@ -530,7 +530,7 @@ static int DD_LoadAddonResourcesWorker(void* parameters) if(p->initiatedBusyMode) Con_SetProgress(50); - if(!games->isNullGame(games->currentGame())) + if(DD_GameLoaded()) { /** * Phase 3: Add real files from the Auto directory. @@ -579,7 +579,7 @@ static int DD_ActivateGameWorker(void* parameters) Con_SetProgress(50); // Now that resources have been located we can begin to initialize the game. - if(!games->isNullGame(games->currentGame()) && gx.PreInit) + if(!DD_GameLoaded() && gx.PreInit) gx.PreInit(games->id(games->currentGame())); if(p->initiatedBusyMode) @@ -608,7 +608,7 @@ static int DD_ActivateGameWorker(void* parameters) Str_Free(&tmp); } - if(!isDedicated && !games->isNullGame(games->currentGame())) + if(!isDedicated && DD_GameLoaded()) { // Apply default control bindings for this game. B_BindGameDefaults(); @@ -665,9 +665,14 @@ static int DD_ActivateGameWorker(void* parameters) return 0; } -struct games_s* App_Games() +struct gamecollection_s* App_GameCollection() +{ + return reinterpret_cast(games); +} + +struct game_s* App_CurrentGame() { - return reinterpret_cast(games); + return reinterpret_cast(&games->currentGame()); } static void populateGameInfo(GameInfo& info, de::Game& game) @@ -707,30 +712,27 @@ void DD_AddGameResource(gameid_t gameId, resourceclass_t rclass, int rflags, char const* _names, void* params) { DENG_ASSERT(games); - de::Game* game = games->byId(gameId); - AbstractResource* rec; - ddstring_t name; - ddstring_t str; - char const* p; - if(!game) - Con_Error("DD_AddGameResource: Error, unknown game id %i.", gameId); + de::Game& game = games->byId(gameId); + if(!VALID_RESOURCE_CLASS(rclass)) Con_Error("DD_AddGameResource: Unknown resource class %i.", (int)rclass); if(!_names || !_names[0] || !strcmp(_names, ";")) Con_Error("DD_AddGameResource: Invalid name argument."); + + AbstractResource* rec; if(0 == (rec = AbstractResource_New(rclass, rflags))) Con_Error("DD_AddGameResource: Unknown error occured during AbstractResource::Construct."); // Add a name list to the game record. - Str_Init(&str); + ddstring_t str; Str_InitStd(&str); Str_Set(&str, _names); // Ensure the name list has the required terminating semicolon. if(Str_RAt(&str, 0) != ';') Str_Append(&str, ";"); - p = Str_Text(&str); - Str_Init(&name); + char const* p = Str_Text(&str); + ddstring_t name; Str_Init(&name); while((p = Str_CopyDelim2(&name, p, ';', CDF_OMIT_DELIMITER))) { AbstractResource_AddName(rec, &name); @@ -763,7 +765,7 @@ void DD_AddGameResource(gameid_t gameId, resourceclass_t rclass, int rflags, default: break; } - game->addResource(rclass, *rec); + game.addResource(rclass, *rec); Str_Free(&str); } @@ -780,35 +782,39 @@ gameid_t DD_DefineGame(GameDef const* def) } // Game mode identity keys must be unique. Ensure that is the case. - if(Games_ByIdentityKey(App_Games(), def->identityKey)) + DENG_ASSERT(games); + try { + /*de::Game& game =*/ games->byIdentityKey(def->identityKey); #if _DEBUG Con_Message("Warning: DD_DefineGame: Failed adding game \"%s\", identity key '%s' already in use, ignoring.\n", def->defaultTitle, def->identityKey); #endif return 0; // Invalid id. } + catch(de::GameCollection::NotFoundError) + {} // Ignore the error. - // Add this game to our records. de::Game* game = de::Game::fromDef(*def); - if(game) - { - game->setPluginId(DD_PluginIdForActiveHook()); + if(!game) return 0; // Invalid def. - DENG_ASSERT(games); - games->add(*game); - return games->id(*game); - } - return 0; // Invalid id. + // Add this game to our records. + game->setPluginId(DD_PluginIdForActiveHook()); + games->add(*game); + return games->id(*game); } /// @note Part of the Doomsday public API. gameid_t DD_GameIdForKey(const char* identityKey) { DENG_ASSERT(games); - de::Game* game = games->byIdentityKey(identityKey); - if(game) return games->id(*game); - - DEBUG_Message(("Warning:DD_GameIdForKey: Game \"%s\" not defined.\n", identityKey)); + try + { + return games->id(games->byIdentityKey(identityKey)); + } + catch(de::GameCollection::NotFoundError) + { + DEBUG_Message(("Warning: DD_GameIdForKey: Game \"%s\" not defined.\n", identityKey)); + } return 0; // Invalid id. } @@ -932,7 +938,7 @@ bool DD_ChangeGame(de::Game& game, bool allowReload = false) Materials_Shutdown(); VERBOSE( - if(!games->isNullGame(game)) + if(!isNullGame(game)) { Con_Message("Selecting game '%s'...\n", Str_Text(&game.identityKey())); } @@ -1000,7 +1006,7 @@ bool DD_ChangeGame(de::Game& game, bool allowReload = false) p.initiatedBusyMode = !BusyMode_Active(); - if(!games->isNullGame(games->currentGame())) + if(DD_GameLoaded()) { // Tell the plugin it is being loaded. /// @todo Must this be done in the main thread? @@ -1010,7 +1016,7 @@ bool DD_ChangeGame(de::Game& game, bool allowReload = false) } /// @kludge Use more appropriate task names when unloading a game. - if(games->isNullGame(game)) + if(isNullGame(game)) { gameChangeTasks[0].name = "Unloading game..."; gameChangeTasks[3].name = "Switching to ringzero..."; @@ -1022,9 +1028,9 @@ bool DD_ChangeGame(de::Game& game, bool allowReload = false) // Process any GL-related tasks we couldn't while Busy. Rend_ParticleLoadExtraTextures(); - if(!games->isNullGame(games->currentGame())) + if(DD_GameLoaded()) { - de::Games::printBanner(games->currentGame()); + de::Game::printBanner(games->currentGame()); } else { @@ -1073,12 +1079,16 @@ de::Game* DD_AutoselectGame(void) if(CommandLine_CheckWith("-game", 1)) { char const* identityKey = CommandLine_Next(); - de::Game* game = games->byIdentityKey(identityKey); - - if(game && game->allStartupResourcesFound()) + try { - return game; + de::Game& game = games->byIdentityKey(identityKey); + if(game.allStartupResourcesFound()) + { + return &game; + } } + catch(de::GameCollection::NotFoundError) + {} // Ignore the error. } // If but one lonely game; select it. @@ -1108,7 +1118,7 @@ int DD_EarlyInit(void) Sys_InitWindowManager(); // Instantiate the Games collection. - games = new de::Games(); + games = new de::GameCollection(); return true; } @@ -2112,7 +2122,6 @@ D_CMD(Load) boolean didLoadGame = false, didLoadResource = false; ddstring_t foundPath, searchPath; - de::Game* game; int arg = 1; Str_Init(&searchPath); @@ -2134,18 +2143,18 @@ D_CMD(Load) } // Are we loading a game? - game = games->byIdentityKey(Str_Text(&searchPath)); - if(game) + try { - if(!game->allStartupResourcesFound()) + de::Game& game = games->byIdentityKey(Str_Text(&searchPath)); + if(!game.allStartupResourcesFound()) { Con_Message("Failed to locate all required startup resources:\n"); - de::Games::printResources(*game, true, RF_STARTUP); - Con_Message("%s (%s) cannot be loaded.\n", Str_Text(&game->title()), Str_Text(&game->identityKey())); + de::Game::printResources(game, true, RF_STARTUP); + Con_Message("%s (%s) cannot be loaded.\n", Str_Text(&game.title()), Str_Text(&game.identityKey())); Str_Free(&searchPath); return true; } - if(!DD_ChangeGame(*game)) + if(!DD_ChangeGame(game)) { Str_Free(&searchPath); return false; @@ -2153,6 +2162,8 @@ D_CMD(Load) didLoadGame = true; ++arg; } + catch(de::GameCollection::NotFoundError) + {} // Ignore the error. // Try the resource locator. Str_Init(&foundPath); @@ -2180,7 +2191,6 @@ D_CMD(Unload) boolean didUnloadFiles = false; ddstring_t searchPath; - de::Game* game; int i; // No arguments; unload the current game if loaded. @@ -2213,16 +2223,22 @@ D_CMD(Unload) } // Unload the current game if specified. - if(argc == 2 && (game = games->byIdentityKey(Str_Text(&searchPath))) != 0) + if(argc == 2) { - Str_Free(&searchPath); - if(DD_GameLoaded()) + try { - return DD_ChangeGame(games->nullGame()); - } + de::Game& game = games->byIdentityKey(Str_Text(&searchPath)); + Str_Free(&searchPath); + if(DD_GameLoaded()) + { + return DD_ChangeGame(games->nullGame()); + } - Con_Message("%s is not currently loaded.\n", Str_Text(&game->identityKey())); - return true; + Con_Message("%s is not currently loaded.\n", Str_Text(&game.identityKey())); + return true; + } + catch(de::GameCollection::NotFoundError) + {} // Ignore the error. } // Try the resource locator. diff --git a/doomsday/engine/portable/src/dd_pinit.c b/doomsday/engine/portable/src/dd_pinit.c index 3aeadee6d9..6f4729f186 100644 --- a/doomsday/engine/portable/src/dd_pinit.c +++ b/doomsday/engine/portable/src/dd_pinit.c @@ -65,7 +65,7 @@ void DD_ComposeMainWindowTitle(char* title) if(DD_GameLoaded() && gx.GetVariable) { sprintf(title, DOOMSDAY_NICENAME " " DOOMSDAY_VERSION_TEXT "%s - %s (%s %s)", - (isDedicated? " (Dedicated)" : ""), Str_Text(Game_Title(Games_CurrentGame(App_Games()))), + (isDedicated? " (Dedicated)" : ""), Str_Text(Game_Title(App_CurrentGame())), (char*) gx.GetVariable(DD_PLUGIN_NAME), (char*) gx.GetVariable(DD_PLUGIN_VERSION_SHORT)); } else diff --git a/doomsday/engine/portable/src/def_main.cpp b/doomsday/engine/portable/src/def_main.cpp index a92a26f053..66aa636a5d 100644 --- a/doomsday/engine/portable/src/def_main.cpp +++ b/doomsday/engine/portable/src/def_main.cpp @@ -857,7 +857,7 @@ static void readAllDefinitions(void) // Now any definition files required by the game on load. if(DD_GameLoaded()) { - AbstractResource* const* records = reinterpret_cast(App_Games())->currentGame().resources(RC_DEFINITION, 0); + AbstractResource* const* records = reinterpret_cast(App_CurrentGame())->resources(RC_DEFINITION, 0); AbstractResource* const* recordIt; if(records) @@ -885,7 +885,7 @@ static void readAllDefinitions(void) if(!CommandLine_Exists("-noauto")) { AutoStr* pattern = AutoStr_NewStd(); - Str_Appendf(pattern, "%sauto/*.ded", Str_Text(Game_DefsPath(Games_CurrentGame(App_Games())))); + Str_Appendf(pattern, "%sauto/*.ded", Str_Text(&reinterpret_cast(App_CurrentGame())->defsPath())); de::FS1::PathList found; if(App_FileSystem()->findAllPaths(Str_Text(pattern), 0, found)) diff --git a/doomsday/engine/portable/src/def_read.c b/doomsday/engine/portable/src/def_read.c index 91e74f1d20..6998800916 100644 --- a/doomsday/engine/portable/src/def_read.c +++ b/doomsday/engine/portable/src/def_read.c @@ -721,7 +721,7 @@ static boolean DED_CheckCondition(const char* cond, boolean expected) } else if(isalnum(cond[0])) { // A game mode. - value = !stricmp(cond, Str_Text(Game_IdentityKey(Games_CurrentGame(App_Games())))); + value = !stricmp(cond, Str_Text(Game_IdentityKey(App_CurrentGame()))); } return value == expected; diff --git a/doomsday/engine/portable/src/edit_bias.c b/doomsday/engine/portable/src/edit_bias.c index 39ff3cad9e..0f683b1cfe 100644 --- a/doomsday/engine/portable/src/edit_bias.c +++ b/doomsday/engine/portable/src/edit_bias.c @@ -379,7 +379,7 @@ static boolean SBE_Save(const char* name) // Since there can be quite a lot of these, make sure we'll skip // the ones that are definitely not suitable. - fprintf(file, "SkipIf Not %s\n", Str_Text(Game_IdentityKey(Games_CurrentGame(App_Games())))); + fprintf(file, "SkipIf Not %s\n", Str_Text(Game_IdentityKey(App_CurrentGame()))); s = SB_GetSource(0); { int i; diff --git a/doomsday/engine/portable/src/finaleinterpreter.c b/doomsday/engine/portable/src/finaleinterpreter.c index 1b41d5b889..c1416becfd 100644 --- a/doomsday/engine/portable/src/finaleinterpreter.c +++ b/doomsday/engine/portable/src/finaleinterpreter.c @@ -1462,7 +1462,7 @@ DEFFC(If) else if(!strnicmp(token, "mode:", 5)) { if(DD_GameLoaded()) - val = !stricmp(token + 5, Str_Text(Game_IdentityKey(Games_CurrentGame(App_Games())))); + val = !stricmp(token + 5, Str_Text(Game_IdentityKey(App_CurrentGame()))); else val = 0; } diff --git a/doomsday/engine/portable/src/fs_main.cpp b/doomsday/engine/portable/src/fs_main.cpp index 657fd20821..aae2e939d3 100644 --- a/doomsday/engine/portable/src/fs_main.cpp +++ b/doomsday/engine/portable/src/fs_main.cpp @@ -309,7 +309,7 @@ bool FS1::unloadFile(char const* path, bool permitRequired, bool quiet) if(found == d->loadedFiles.end()) return false; // Do not attempt to unload a resource required by the current game. - if(!permitRequired && Game_IsRequiredResource(Games_CurrentGame(App_Games()), path)) + if(!permitRequired && reinterpret_cast(App_CurrentGame())->isRequiredResource(path)) { if(!quiet) { diff --git a/doomsday/engine/portable/src/game.cpp b/doomsday/engine/portable/src/game.cpp index 5848966436..e4798ec790 100644 --- a/doomsday/engine/portable/src/game.cpp +++ b/doomsday/engine/portable/src/game.cpp @@ -138,6 +138,11 @@ Game::~Game() delete d; } +GameCollection& Game::collection() const +{ + return *reinterpret_cast(App_GameCollection()); +} + Game& Game::addResource(resourceclass_t rclass, AbstractResource& record) { if(!VALID_RESOURCE_CLASS(rclass)) @@ -181,7 +186,7 @@ bool Game::isRequiredResource(char const* absolutePath) return false; } -bool Game::allStartupResourcesFound() +bool Game::allStartupResourcesFound() const { for(uint i = 0; i < RESOURCECLASS_COUNT; ++i) { @@ -206,47 +211,47 @@ Game& Game::setPluginId(pluginid_t newId) return *this; } -pluginid_t Game::pluginId() +pluginid_t Game::pluginId() const { return d->pluginId; } -ddstring_t const& Game::identityKey() +ddstring_t const& Game::identityKey() const { return d->identityKey; } -ddstring_t const& Game::dataPath() +ddstring_t const& Game::dataPath() const { return d->dataPath; } -ddstring_t const& Game::defsPath() +ddstring_t const& Game::defsPath() const { return d->defsPath; } -ddstring_t const& Game::mainConfig() +ddstring_t const& Game::mainConfig() const { return d->mainConfig; } -ddstring_t const& Game::bindingConfig() +ddstring_t const& Game::bindingConfig() const { return d->bindingConfig; } -ddstring_t const& Game::title() +ddstring_t const& Game::title() const { return d->title; } -ddstring_t const& Game::author() +ddstring_t const& Game::author() const { return d->author; } -AbstractResource* const* Game::resources(resourceclass_t rclass, int* count) +AbstractResource* const* Game::resources(resourceclass_t rclass, int* count) const { if(!VALID_RESOURCE_CLASS(rclass)) { @@ -278,6 +283,82 @@ Game* Game::fromDef(GameDef const& def) def.defaultTitle, def.defaultAuthor); } +void Game::printBanner(Game const& game) +{ + Con_PrintRuler(); + Con_FPrintf(CPF_WHITE | CPF_CENTER, "%s\n", Str_Text(&game.title())); + Con_PrintRuler(); +} + +void Game::printResources(Game const& game, bool printStatus, int rflags) +{ + size_t count = 0; + for(uint i = 0; i < RESOURCECLASS_COUNT; ++i) + { + AbstractResource* const* records = game.resources((resourceclass_t)i, 0); + if(!records) continue; + + for(AbstractResource* const* recordIt = records; *recordIt; recordIt++) + { + AbstractResource* rec = *recordIt; + + if(rflags >= 0 && (rflags & AbstractResource_ResourceFlags(rec))) + { + AbstractResource_Print(rec, printStatus); + count += 1; + } + } + } + + if(count == 0) + Con_Printf(" None\n"); +} + +void Game::print(Game const& game, int flags) +{ + if(isNullGame(game)) + flags &= ~PGF_BANNER; + +#if _DEBUG + Con_Printf("pluginid:%i data:\"%s\" defs:\"%s\"\n", int(game.pluginId()), + F_PrettyPath(Str_Text(&game.dataPath())), + F_PrettyPath(Str_Text(&game.defsPath()))); +#endif + + if(flags & PGF_BANNER) + printBanner(game); + + if(!(flags & PGF_BANNER)) + Con_Printf("Game: %s - ", Str_Text(&game.title())); + else + Con_Printf("Author: "); + Con_Printf("%s\n", Str_Text(&game.author())); + Con_Printf("IdentityKey: %s\n", Str_Text(&game.identityKey())); + + if(flags & PGF_LIST_STARTUP_RESOURCES) + { + Con_Printf("Startup resources:\n"); + printResources(game, (flags & PGF_STATUS) != 0, RF_STARTUP); + } + + if(flags & PGF_LIST_OTHER_RESOURCES) + { + Con_Printf("Other resources:\n"); + Con_Printf(" "); + printResources(game, /*(flags & PGF_STATUS) != 0*/false, 0); + } + + if(flags & PGF_STATUS) + Con_Printf("Status: %s\n", + game.collection().isCurrentGame(game)? "Loaded" : + game.allStartupResourcesFound()? "Complete/Playable" : + "Incomplete/Not playable"); +} + +NullGame::NullGame(ddstring_t const* dataPath, ddstring_t const* defsPath) + : Game("null-game", dataPath, defsPath, "doomsday", "null-game", "null-game") +{} + } // namespace de /** @@ -313,6 +394,12 @@ void Game_Delete(struct game_s* game) } } +boolean Game_IsNullObject(Game const* game) +{ + if(!game) return false; + return de::isNullGame(*reinterpret_cast(game)); +} + struct game_s* Game_AddResource(struct game_s* game, resourceclass_t rclass, struct AbstractResource_s* record) { SELF(game); @@ -327,9 +414,9 @@ boolean Game_IsRequiredResource(struct game_s* game, char const* absolutePath) return self->isRequiredResource(absolutePath); } -boolean Game_AllStartupResourcesFound(struct game_s* game) +boolean Game_AllStartupResourcesFound(struct game_s const* game) { - SELF(game); + SELF_CONST(game); return self->allStartupResourcesFound(); } @@ -339,57 +426,57 @@ struct game_s* Game_SetPluginId(struct game_s* game, pluginid_t pluginId) return reinterpret_cast(&self->setPluginId(pluginId)); } -pluginid_t Game_PluginId(struct game_s* game) +pluginid_t Game_PluginId(struct game_s const* game) { - SELF(game); + SELF_CONST(game); return self->pluginId(); } -ddstring_t const* Game_IdentityKey(struct game_s* game) +ddstring_t const* Game_IdentityKey(struct game_s const* game) { - SELF(game); + SELF_CONST(game); return &self->identityKey(); } -ddstring_t const* Game_Title(struct game_s* game) +ddstring_t const* Game_Title(struct game_s const* game) { - SELF(game); + SELF_CONST(game); return &self->title(); } -ddstring_t const* Game_Author(struct game_s* game) +ddstring_t const* Game_Author(struct game_s const* game) { - SELF(game); + SELF_CONST(game); return &self->author(); } -ddstring_t const* Game_MainConfig(struct game_s* game) +ddstring_t const* Game_MainConfig(struct game_s const* game) { - SELF(game); + SELF_CONST(game); return &self->mainConfig(); } -ddstring_t const* Game_BindingConfig(struct game_s* game) +ddstring_t const* Game_BindingConfig(struct game_s const* game) { - SELF(game); + SELF_CONST(game); return &self->bindingConfig(); } -struct AbstractResource_s* const* Game_Resources(struct game_s* game, resourceclass_t rclass, int* count) +struct AbstractResource_s* const* Game_Resources(struct game_s const* game, resourceclass_t rclass, int* count) { - SELF(game); + SELF_CONST(game); return self->resources(rclass, count); } -ddstring_t const* Game_DataPath(struct game_s* game) +ddstring_t const* Game_DataPath(struct game_s const* game) { - SELF(game); + SELF_CONST(game); return &self->dataPath(); } -ddstring_t const* Game_DefsPath(struct game_s* game) +ddstring_t const* Game_DefsPath(struct game_s const* game) { - SELF(game); + SELF_CONST(game); return &self->defsPath(); } @@ -399,6 +486,24 @@ struct game_s* Game_FromDef(GameDef const* def) return reinterpret_cast(de::Game::fromDef(*def)); } +void Game_PrintBanner(Game const* game) +{ + if(!game) return; + de::Game::printBanner(*reinterpret_cast(game)); +} + +void Game_PrintResources(Game const* game, boolean printStatus, int rflags) +{ + if(!game) return; + de::Game::printResources(*reinterpret_cast(game), CPP_BOOL(printStatus), rflags); +} + +void Game_Print(Game const* game, int flags) +{ + if(!game) return; + de::Game::print(*reinterpret_cast(game), flags); +} + /// @todo Do this really belong here? Semantically, this appears misplaced. -ds void Game_Notify(int notification, void* param) { diff --git a/doomsday/engine/portable/src/p_data.cpp b/doomsday/engine/portable/src/p_data.cpp index 8eb413d952..16ed7f0ffa 100644 --- a/doomsday/engine/portable/src/p_data.cpp +++ b/doomsday/engine/portable/src/p_data.cpp @@ -80,7 +80,7 @@ const char* P_GenerateUniqueMapId(const char* mapID) Str_Init(&fileName); F_FileName(&fileName, F_LumpSourceFile(lumpNum)); qsnprintf(uid, 255, "%s|%s|%s|%s", mapID, Str_Text(&fileName), - (!F_LumpIsCustom(lumpNum) ? "iwad" : "pwad"), Str_Text(&reinterpret_cast(App_Games())->currentGame().identityKey())); + (!F_LumpIsCustom(lumpNum) ? "iwad" : "pwad"), Str_Text(&reinterpret_cast(App_CurrentGame())->identityKey())); strlwr(uid); Str_Free(&fileName); diff --git a/doomsday/engine/portable/src/render/rend_console.c b/doomsday/engine/portable/src/render/rend_console.c index 08c589752e..045fbd8ddf 100644 --- a/doomsday/engine/portable/src/render/rend_console.c +++ b/doomsday/engine/portable/src/render/rend_console.c @@ -287,7 +287,7 @@ void Rend_ConsoleUpdateTitle(void) if(DD_GameLoaded()) { dd_snprintf(secondaryTitleText, sizeof(secondaryTitleText)-1, "%s %s", (char*) gx.GetVariable(DD_PLUGIN_NAME), (char*) gx.GetVariable(DD_PLUGIN_VERSION_SHORT)); - strncpy(statusText, Str_Text(Game_Title(Games_CurrentGame(App_Games()))), sizeof(statusText) - 1); + strncpy(statusText, Str_Text(Game_Title(App_CurrentGame())), sizeof(statusText) - 1); return; } diff --git a/doomsday/engine/portable/src/sv_main.c b/doomsday/engine/portable/src/sv_main.c index cac3802a7b..b8ea789c8e 100644 --- a/doomsday/engine/portable/src/sv_main.c +++ b/doomsday/engine/portable/src/sv_main.c @@ -81,7 +81,7 @@ void Sv_GetInfo(serverinfo_t* info) // Let's figure out what we want to tell about ourselves. info->version = DOOMSDAY_VERSION; dd_snprintf(info->plugin, sizeof(info->plugin) - 1, "%s %s", (char*) gx.GetVariable(DD_PLUGIN_NAME), (char*) gx.GetVariable(DD_PLUGIN_VERSION_SHORT)); - strncpy(info->gameIdentityKey, Str_Text(Game_IdentityKey(Games_CurrentGame(App_Games()))), sizeof(info->gameIdentityKey) - 1); + strncpy(info->gameIdentityKey, Str_Text(Game_IdentityKey(App_CurrentGame())), sizeof(info->gameIdentityKey) - 1); strncpy(info->gameConfig, gx.GetVariable(DD_GAME_CONFIG), sizeof(info->gameConfig) - 1); strncpy(info->name, serverName, sizeof(info->name) - 1); strncpy(info->description, serverInfo, sizeof(info->description) - 1); @@ -398,7 +398,7 @@ void Sv_HandlePacket(void) { // Check the game mode (max 16 chars). Reader_Read(msgReader, buf, 16); - if(strnicmp(buf, Str_Text(Game_IdentityKey(Games_CurrentGame(App_Games()))), 16)) + if(strnicmp(buf, Str_Text(Game_IdentityKey(App_CurrentGame())), 16)) { Con_Printf(" Bad Game ID: %-.16s\n", buf); N_TerminateClient(from); diff --git a/doomsday/engine/portable/src/sys_reslocator.cpp b/doomsday/engine/portable/src/sys_reslocator.cpp index 886f779fa5..3ba7fffd75 100644 --- a/doomsday/engine/portable/src/sys_reslocator.cpp +++ b/doomsday/engine/portable/src/sys_reslocator.cpp @@ -1235,7 +1235,7 @@ boolean F_MapResourcePath(resourcenamespaceid_t rni, ddstring_t* path) if(nameLen <= pathLen && Str_At(path, nameLen) == '/' && !strnicmp(Str_Text(&info->name), Str_Text(path), nameLen)) { - Str_Prepend(path, Str_Text(&reinterpret_cast(App_Games())->currentGame().dataPath())); + Str_Prepend(path, Str_Text(&reinterpret_cast(App_CurrentGame())->dataPath())); return true; } } diff --git a/doomsday/engine/portable/src/ui_mpi.c b/doomsday/engine/portable/src/ui_mpi.c index 423e572924..e588ae7a78 100644 --- a/doomsday/engine/portable/src/ui_mpi.c +++ b/doomsday/engine/portable/src/ui_mpi.c @@ -485,11 +485,11 @@ void MPIUpdateServerList(void) N_MasterGet(i, &info); // Is this suitable? - if(info.version != DOOMSDAY_VERSION || stricmp(info.gameIdentityKey, Str_Text(Game_IdentityKey(Games_CurrentGame(App_Games())))) || !info.canJoin) + if(info.version != DOOMSDAY_VERSION || stricmp(info.gameIdentityKey, Str_Text(Game_IdentityKey(App_CurrentGame()))) || !info.canJoin) { Con_Message("Server %s filtered out:\n", info.name); Con_Message(" remote = %i, local = %i\n", info.version, DOOMSDAY_VERSION); - Con_Message(" remote = %s, local = %s\n", info.gameIdentityKey, Str_Text(Game_IdentityKey(Games_CurrentGame(App_Games())))); + Con_Message(" remote = %s, local = %s\n", info.gameIdentityKey, Str_Text(Game_IdentityKey(App_CurrentGame()))); Con_Message(" can join = %i\n", info.canJoin); continue; } diff --git a/doomsday/engine/portable/src/uri.c b/doomsday/engine/portable/src/uri.c index c89489f4cd..be8f1a188c 100644 --- a/doomsday/engine/portable/src/uri.c +++ b/doomsday/engine/portable/src/uri.c @@ -136,8 +136,8 @@ static boolean resolveUri(const Uri* uri, ddstring_t* dest) else if(!Str_CompareIgnoreCase(&part, "Game.DataPath")) { /// @note DataPath already has ending '/'. - Game* game = Games_CurrentGame(App_Games()); - if(Games_IsNullObject(App_Games(), game)) + Game* game = App_CurrentGame(); + if(Game_IsNullObject(game)) goto parseEnded; Str_PartAppend(dest, Str_Text(Game_DataPath(game)), 0, Str_Length(Game_DataPath(game))-1); @@ -145,16 +145,16 @@ static boolean resolveUri(const Uri* uri, ddstring_t* dest) else if(!Str_CompareIgnoreCase(&part, "Game.DefsPath")) { /// @note DefsPath already has ending '/'. - Game* game = Games_CurrentGame(App_Games()); - if(Games_IsNullObject(App_Games(), game)) + Game* game = App_CurrentGame(); + if(Game_IsNullObject(game)) goto parseEnded; Str_PartAppend(dest, Str_Text(Game_DefsPath(game)), 0, Str_Length(Game_DefsPath(game))-1); } else if(!Str_CompareIgnoreCase(&part, "Game.IdentityKey")) { - Game* game = Games_CurrentGame(App_Games()); - if(Games_IsNullObject(App_Games(), game)) + Game* game = App_CurrentGame(); + if(Game_IsNullObject(game)) goto parseEnded; Str_Append(dest, Str_Text(Game_IdentityKey(game))); @@ -320,7 +320,7 @@ const ddstring_t* Uri_ResolvedConst(const Uri* uri) } #ifndef LIBDENG_DISABLE_URI_RESOLVE_CACHING - if(uri->resolvedForGame && uri->resolvedForGame == (void*) Games_CurrentGame(App_Games())) + if(uri->resolvedForGame && uri->resolvedForGame == (void*) App_CurrentGame()) { // We can just return the previously prepared resolved URI. return &uri->resolved; @@ -332,7 +332,7 @@ const ddstring_t* Uri_ResolvedConst(const Uri* uri) // Keep a copy of this, we'll likely need it many, many times. if(resolveUri(uri, &modifiable->resolved)) { - modifiable->resolvedForGame = (void*) Games_CurrentGame(App_Games()); + modifiable->resolvedForGame = (void*) App_CurrentGame(); return &uri->resolved; } else diff --git a/doomsday/engine/portable/src/zipfile.cpp b/doomsday/engine/portable/src/zipfile.cpp index 97e89db8e7..732c314d4a 100644 --- a/doomsday/engine/portable/src/zipfile.cpp +++ b/doomsday/engine/portable/src/zipfile.cpp @@ -902,7 +902,7 @@ static void ApplyPathMappings(ddstring_t* dest, const ddstring_t* src) ddstring_t* out = (dest == src? Str_New() : dest); int dist; - Str_Appendf(out, "%sauto/", Str_Text(&reinterpret_cast(App_Games())->currentGame().defsPath())); + Str_Appendf(out, "%sauto/", Str_Text(&reinterpret_cast(App_CurrentGame())->defsPath())); dist = (Str_At(src, 1) == '/'? 2 : 1); Str_PartAppend(out, Str_Text(src), dist, Str_Length(src)-dist); @@ -921,7 +921,7 @@ static void ApplyPathMappings(ddstring_t* dest, const ddstring_t* src) int dist = 0; char* slash; - Str_Appendf(out, "%sauto/", Str_Text(&reinterpret_cast(App_Games())->currentGame().dataPath())); + Str_Appendf(out, "%sauto/", Str_Text(&reinterpret_cast(App_CurrentGame())->dataPath())); slash = strrchr(Str_Text(src), '/'); dist = slash - Str_Text(src); // Copy the path up to and including the last directory separator if present. @@ -986,10 +986,10 @@ static void ApplyPathMappings(ddstring_t* dest, const ddstring_t* src) switch(rclass) { case RC_PACKAGE: // Mapped to the Data directory. - Str_Appendf(&mapped, "%sauto/", Str_Text(&reinterpret_cast(App_Games())->currentGame().dataPath())); + Str_Appendf(&mapped, "%sauto/", Str_Text(&reinterpret_cast(App_CurrentGame())->dataPath())); break; case RC_DEFINITION: // Mapped to the Defs directory. - Str_Appendf(&mapped, "%sauto/", Str_Text(&reinterpret_cast(App_Games())->currentGame().defsPath())); + Str_Appendf(&mapped, "%sauto/", Str_Text(&reinterpret_cast(App_CurrentGame())->defsPath())); break; default: /* Not mapped */ break; }