From 097a39dfda83b49fe6926296e5b485e3a2108d47 Mon Sep 17 00:00:00 2001 From: danij Date: Mon, 21 Jul 2014 15:46:57 +0100 Subject: [PATCH] libcommon: One MapInfoParser can now be used to read multiple definition sources Also applied PIMPL plus some cleanup. --- doomsday/plugins/common/include/mapinfo.h | 28 +- doomsday/plugins/common/src/mapinfo.cpp | 306 ++++++++++------------ doomsday/plugins/common/src/p_start.cpp | 94 +++++-- 3 files changed, 235 insertions(+), 193 deletions(-) diff --git a/doomsday/plugins/common/include/mapinfo.h b/doomsday/plugins/common/include/mapinfo.h index aa14e21487..14ac74f040 100644 --- a/doomsday/plugins/common/include/mapinfo.h +++ b/doomsday/plugins/common/include/mapinfo.h @@ -36,10 +36,34 @@ class MapInfo : public de::Record void resetToDefaults(); }; +// Central MapInfo database. +typedef std::map MapInfos; +extern MapInfos mapInfos; + /** - * Populate the MapInfo database by parsing the MAPINFO lump. + * Parser for Hexen's MAPINFO definition lumps. */ -void MapInfoParser(Str const *path); +class MapInfoParser +{ +public: + /// Base class for all parse-related errors. @ingroup errors + DENG2_ERROR(ParseError); + +public: + MapInfoParser(); + + void parse(AutoStr const &buffer, de::String sourceFile); + + /** + * Clear any custom default map definition currently in use. Map definitions + * read after this is called will use the games' default map definition as a + * basis (unless specified otherwise). + */ + void clearDefaultMap(); + +private: + DENG2_PRIVATE(d) +}; /** * @param mapUri Identifier of the map to lookup info for. Can be @c 0 in which diff --git a/doomsday/plugins/common/src/mapinfo.cpp b/doomsday/plugins/common/src/mapinfo.cpp index 161753cbe8..03da7938fd 100644 --- a/doomsday/plugins/common/src/mapinfo.cpp +++ b/doomsday/plugins/common/src/mapinfo.cpp @@ -30,11 +30,10 @@ using namespace de; -typedef std::map MapInfos; -static MapInfos mapInfos; - namespace common { +MapInfos mapInfos; + namespace internal { static inline String defaultSkyMaterial() @@ -46,6 +45,24 @@ static inline String defaultSkyMaterial() return "Textures:SKY1"; } +#define MUSIC_STARTUP "startup" +#define MUSIC_ENDING1 "hall" +#define MUSIC_ENDING2 "orb" +#define MUSIC_ENDING3 "chess" +#define MUSIC_INTERMISSION "hub" +#define MUSIC_TITLE "hexen" + +/** + * Update the Music definition @a musicId with the specified CD @a track number. + */ +static void setMusicCDTrack(char const *musicId, int track) +{ + LOG_RES_VERBOSE("setMusicCDTrack: musicId=%s, track=%i") << musicId << track; + + int cdTrack = track; + Def_Set(DD_DEF_MUSIC, Def_Get(DD_DEF_MUSIC, musicId, 0), DD_CD_TRACK, &cdTrack); +} + } // namespace internal using namespace internal; @@ -80,164 +97,37 @@ void MapInfo::resetToDefaults() addText ("songLump", "DEFSONG"); } -namespace internal { - -#define MUSIC_STARTUP "startup" -#define MUSIC_ENDING1 "hall" -#define MUSIC_ENDING2 "orb" -#define MUSIC_ENDING3 "chess" -#define MUSIC_INTERMISSION "hub" -#define MUSIC_TITLE "hexen" - -/** - * Update the Music definition @a musicId with the specified CD @a track number. - */ -static void setMusicCDTrack(char const *musicId, int track) -{ - LOG_RES_VERBOSE("setMusicCDTrack: musicId=%s, track=%i") << musicId << track; - - int cdTrack = track; - Def_Set(DD_DEF_MUSIC, Def_Get(DD_DEF_MUSIC, musicId, 0), DD_CD_TRACK, &cdTrack); -} - /** * @note In the future it is likely that a MAPINFO parser will only be necessary in order to * translate such content into Doomsday-native DED file(s) in a plugin. As such it would be * preferable if this could be done in one pass without the need for extra temporary storage. */ -class MapInfoParser +DENG2_PIMPL(MapInfoParser) { - AutoStr *source; + AutoStr const *buffer; HexLex lexer; MapInfo *defaultMap; -public: - /// Base class for all parse-related errors. @ingroup errors - DENG2_ERROR(ParseError); - - MapInfoParser(AutoStr *source = 0) - : source(source), defaultMap(0) - {} + Instance(Public *i) : Base(i), buffer(0), defaultMap(0) {} + ~Instance() { clearDefaultMap(); } - ~MapInfoParser() + void addDefaultMapIfNeeded(bool resetToDefaultsIfPresent = true) { - delete defaultMap; + if(!defaultMap) + { + defaultMap = new MapInfo; + } + else if(resetToDefaultsIfPresent) + { + defaultMap->resetToDefaults(); + } } - void parse() + void clearDefaultMap() { - ::mapInfos.clear(); - - // Nothing to parse? - if(!source || Str_IsEmpty(source)) - return; - delete defaultMap; defaultMap = 0; - - lexer.parse(source); - while(lexer.readToken()) - { - if(!Str_CompareIgnoreCase(lexer.token(), "cd_start_track")) - { - setMusicCDTrack(MUSIC_STARTUP, (int)lexer.readNumber()); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "cd_end1_track")) - { - setMusicCDTrack(MUSIC_ENDING1, (int)lexer.readNumber()); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "cd_end2_track")) - { - setMusicCDTrack(MUSIC_ENDING2, (int)lexer.readNumber()); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "cd_end3_track")) - { - setMusicCDTrack(MUSIC_ENDING3, (int)lexer.readNumber()); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "cd_intermission_track")) - { - setMusicCDTrack(MUSIC_INTERMISSION, (int)lexer.readNumber()); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "cd_title_track")) - { - setMusicCDTrack(MUSIC_TITLE, (int)lexer.readNumber()); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "clearepisodes")) // ZDoom - { - LOG_WARNING("MAPINFO ClearEpisodes directives are not supported."); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "clearskills")) // ZDoom - { - LOG_WARNING("MAPINFO ClearSkills directives are not supported."); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "clusterdef")) // ZDoom - { - parseCluster(); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "episode")) // ZDoom - { - parseEpisode(); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "map")) - { - parseMap(); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "defaultmap")) // ZDoom - { - // Custom default MapInfo definition to be used as the basis for subsequent defs. - if(!defaultMap) - { - defaultMap = new MapInfo; - } - else - { - defaultMap->resetToDefaults(); - } - - parseMap(defaultMap); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "adddefaultmap")) // ZDoom - { - // As per 'defaultmap' but additive. - if(!defaultMap) - { - defaultMap = new MapInfo; - } - parseMap(defaultMap); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "gamedefaults")) // ZDoom - { - // Custom default MapInfo definition which is seemingly only used by ZDoom itself - // as a way to get around their changes to/repurposing of the MAPINFO mechanism. - // We probably don't need to support this. -ds - MapInfo tempMap; - parseMap(&tempMap); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "skill")) // ZDoom - { - parseSkill(); - continue; - } - - // Unexpected token encountered. - throw ParseError(String("Unexpected token '%1' on line #%2").arg(Str_Text(lexer.token())).arg(lexer.lineNumber())); - } } -private: void parseCluster() // ZDoom { LOG_WARNING("MAPINFO Cluster definitions are not supported."); @@ -492,7 +382,7 @@ class MapInfoParser if(!info) { // A new map info. - info = &::mapInfos[mapUri.path().asText().toLower().toUtf8().constData()]; + info = &mapInfos[mapUri.path().asText().toLower().toUtf8().constData()]; // Initialize with custom default values? if(defaultMap) @@ -1044,39 +934,113 @@ class MapInfoParser } }; -} // namespace internal +MapInfoParser::MapInfoParser() +{} -void MapInfoParser(ddstring_s const *path) +void MapInfoParser::clearDefaultMap() { - AutoStr *source = M_ReadFileIntoString(path, 0); - if(!source || Str_IsEmpty(source)) - { - LOG_RES_WARNING("MapInfoParser: Failed to open definition/script file \"%s\" for reading") - << NativePath(Str_Text(path)).pretty(); - return; - } + d->clearDefaultMap(); +} - LOG_RES_VERBOSE("Parsing \"%s\"...") << NativePath(Str_Text(path)).pretty(); +void MapInfoParser::parse(AutoStr const &buffer, String /*sourceFile*/) +{ + LOG_AS("MapInfoParser"); - try - { - internal::MapInfoParser(source).parse(); - } - catch(internal::MapInfoParser::ParseError const &er) - { - LOG_RES_WARNING("Failed parsing \"%s\" as MAPINFO:\n%s") - << NativePath(Str_Text(path)).pretty() << er.asText(); - } + // Nothing to parse? + d->buffer = &buffer; + if(Str_IsEmpty(d->buffer)) + return; -#ifdef DENG_DEBUG - for(MapInfos::const_iterator i = mapInfos.begin(); i != mapInfos.end(); ++i) + d->lexer.parse(d->buffer); + while(d->lexer.readToken()) { - MapInfo const &info = i->second; - LOG_RES_MSG("MAPINFO %s { title: \"%s\" hub: %i map: %s warp: %i }") - << i->first.c_str() << info.gets("title") - << info.geti("hub") << info.gets("map") << info.geti("warpTrans"); + if(!Str_CompareIgnoreCase(d->lexer.token(), "cd_start_track")) + { + setMusicCDTrack(MUSIC_STARTUP, (int)d->lexer.readNumber()); + continue; + } + if(!Str_CompareIgnoreCase(d->lexer.token(), "cd_end1_track")) + { + setMusicCDTrack(MUSIC_ENDING1, (int)d->lexer.readNumber()); + continue; + } + if(!Str_CompareIgnoreCase(d->lexer.token(), "cd_end2_track")) + { + setMusicCDTrack(MUSIC_ENDING2, (int)d->lexer.readNumber()); + continue; + } + if(!Str_CompareIgnoreCase(d->lexer.token(), "cd_end3_track")) + { + setMusicCDTrack(MUSIC_ENDING3, (int)d->lexer.readNumber()); + continue; + } + if(!Str_CompareIgnoreCase(d->lexer.token(), "cd_intermission_track")) + { + setMusicCDTrack(MUSIC_INTERMISSION, (int)d->lexer.readNumber()); + continue; + } + if(!Str_CompareIgnoreCase(d->lexer.token(), "cd_title_track")) + { + setMusicCDTrack(MUSIC_TITLE, (int)d->lexer.readNumber()); + continue; + } + if(!Str_CompareIgnoreCase(d->lexer.token(), "clearepisodes")) // ZDoom + { + LOG_WARNING("MAPINFO ClearEpisodes directives are not supported."); + continue; + } + if(!Str_CompareIgnoreCase(d->lexer.token(), "clearskills")) // ZDoom + { + LOG_WARNING("MAPINFO ClearSkills directives are not supported."); + continue; + } + if(!Str_CompareIgnoreCase(d->lexer.token(), "clusterdef")) // ZDoom + { + d->parseCluster(); + continue; + } + if(!Str_CompareIgnoreCase(d->lexer.token(), "episode")) // ZDoom + { + d->parseEpisode(); + continue; + } + if(!Str_CompareIgnoreCase(d->lexer.token(), "map")) + { + d->parseMap(); + continue; + } + if(!Str_CompareIgnoreCase(d->lexer.token(), "defaultmap")) // ZDoom + { + // Custom default MapInfo definition to be used as the basis for subsequent defs. + d->addDefaultMapIfNeeded(); + d->parseMap(d->defaultMap); + continue; + } + if(!Str_CompareIgnoreCase(d->lexer.token(), "adddefaultmap")) // ZDoom + { + // As per 'defaultmap' but additive. + d->addDefaultMapIfNeeded(false/*don't reset*/); + d->parseMap(d->defaultMap); + continue; + } + if(!Str_CompareIgnoreCase(d->lexer.token(), "gamedefaults")) // ZDoom + { + // Custom default MapInfo definition which is seemingly only used by ZDoom itself + // as a way to get around their changes to/repurposing of the MAPINFO mechanism. + // We probably don't need to support this. -ds + MapInfo tempMap; + d->parseMap(&tempMap); + continue; + } + if(!Str_CompareIgnoreCase(d->lexer.token(), "skill")) // ZDoom + { + d->parseSkill(); + continue; + } + + // Unexpected token encountered. + throw ParseError(String("Unexpected token '%1' on line #%2").arg(Str_Text(d->lexer.token())).arg(d->lexer.lineNumber())); } -#endif } MapInfo *P_MapInfo(de::Uri const *mapUri) diff --git a/doomsday/plugins/common/src/p_start.cpp b/doomsday/plugins/common/src/p_start.cpp index b4e94b0990..ebc7382437 100644 --- a/doomsday/plugins/common/src/p_start.cpp +++ b/doomsday/plugins/common/src/p_start.cpp @@ -1,4 +1,4 @@ -/** @file p_start.cpp Common player (re)spawning logic. +/** @file p_start.cpp Common player (re)spawning logic. * * @authors Copyright © 2003-2013 Jaakko Keränen * @authors Copyright © 2006-2013 Daniel Swanson @@ -20,34 +20,35 @@ * 02110-1301 USA */ +#include "common.h" +#include "p_start.h" + #include #include #include - -#include "common.h" - -#include "p_tick.h" -#include "p_mapsetup.h" -#include "p_user.h" -#include "player.h" -#include "d_net.h" -#include "p_map.h" +#include #include "am_map.h" -#include "p_terraintype.h" -#include "g_common.h" +#include "d_net.h" +#include "dmu_lib.h" #include "gamesession.h" +#include "g_common.h" +#include "g_defs.h" +#include "hu_chat.h" +#include "hu_stuff.h" #include "mapinfo.h" -#include "p_start.h" #include "p_actor.h" -#include "p_switch.h" -#include "g_defs.h" #include "p_inventory.h" +#include "p_map.h" +#include "p_mapsetup.h" #include "p_mapspec.h" -#include "dmu_lib.h" -#include "hu_stuff.h" -#include "hu_chat.h" +#include "p_switch.h" +#include "p_terraintype.h" +#include "p_tick.h" +#include "p_user.h" +#include "player.h" #include "r_common.h" +using namespace de; using namespace common; #if __JDOOM__ || __JDOOM64__ || __JHERETIC__ @@ -185,15 +186,68 @@ void P_Init() P_Update(); } +/** + * Populate the MapInfo database by parsing the MAPINFO lump. + */ +#if __JHEXEN__ +static void readOneMapInfoDefinition(MapInfoParser &parser, AutoStr const &buffer, String sourceFile) +{ + LOG_RES_VERBOSE("Parsing \"%s\"...") << NativePath(sourceFile).pretty(); + try + { + parser.parse(buffer, sourceFile); + } + catch(MapInfoParser::ParseError const &er) + { + LOG_RES_WARNING("Failed parsing \"%s\" as MAPINFO:\n%s") + << NativePath(sourceFile).pretty() << er.asText(); + } +} + +static void readMapInfoDefinitions() +{ + // Clear the MapInfo database. + mapInfos.clear(); + + // Initialize a new parser. + MapInfoParser parser; + + // Read the primary MAPINFO (from the IWAD). + AutoStr *sourceFile = sc_FileScripts? Str_Appendf(AutoStr_New(), "%sMAPINFO.txt", sc_ScriptsDir) + : AutoStr_FromText("Lumps:MAPINFO"); + AutoStr *buffer = M_ReadFileIntoString(sourceFile, 0); + if(buffer && !Str_IsEmpty(buffer)) + { + readOneMapInfoDefinition(parser, *buffer, Str_Text(sourceFile)); + } + else + { + LOG_RES_WARNING("MapInfoParser: Failed to open definition/script file \"%s\" for reading") + << NativePath(Str_Text(sourceFile)).pretty(); + } + +#ifdef DENG_DEBUG + for(MapInfos::const_iterator i = mapInfos.begin(); i != mapInfos.end(); ++i) + { + MapInfo const &info = i->second; + LOG_RES_MSG("MAPINFO %s { title: \"%s\" hub: %i map: %s warp: %i }") + << i->first.c_str() << info.gets("title") + << info.geti("hub") << info.gets("map") << info.geti("warpTrans"); + } +#endif +} +#endif + void P_Update() { #if __JHERETIC__ || __JHEXEN__ || __JDOOM64__ P_InitInventory(); #endif + #if __JHEXEN__ - MapInfoParser(sc_FileScripts? Str_Appendf(AutoStr_New(), "%sMAPINFO.txt", sc_ScriptsDir) - : AutoStr_FromText("Lumps:MAPINFO")); + readMapInfoDefinitions(); #endif + P_InitSwitchList(); P_InitTerrainTypes();