From 3963506e92dbf13cd529078e1ec1f5a9b8ceeeeb Mon Sep 17 00:00:00 2001 From: danij Date: Thu, 7 Aug 2014 04:07:42 +0100 Subject: [PATCH] Refactor|IdTech1Converter: Moved HexDefs, MapInfoParser to mapinfotranslator.cpp --- .../include/mapinfotranslator.h | 76 - .../src/mapinfotranslator.cpp | 2011 +++++++++-------- 2 files changed, 1031 insertions(+), 1056 deletions(-) diff --git a/doomsday/plugins/idtech1converter/include/mapinfotranslator.h b/doomsday/plugins/idtech1converter/include/mapinfotranslator.h index 26890a37ef..e7ab518bef 100644 --- a/doomsday/plugins/idtech1converter/include/mapinfotranslator.h +++ b/doomsday/plugins/idtech1converter/include/mapinfotranslator.h @@ -23,85 +23,9 @@ #define IDTECH1CONVERTER_MAPINFOTRANSLATOR_H #include "idtech1converter.h" -#include -#include -#include namespace idtech1 { -class MapInfo : public de::Record -{ -public: - MapInfo(); - MapInfo &operator = (MapInfo const &other); - - void resetToDefaults(); -}; - -class EpisodeInfo : public de::Record -{ -public: - EpisodeInfo(); - EpisodeInfo &operator = (EpisodeInfo const &other); - - void resetToDefaults(); -}; - -/** - * Central database of definitions read from Hexen-derived definition formats. - * - * @note Ultimately the definitions this contains should instead have their sources - * translated into DED syntax and be made available from the main DED db instead. - */ -struct HexDefs -{ - typedef std::map EpisodeInfos; - EpisodeInfos episodeInfos; - typedef std::map MapInfos; - MapInfos mapInfos; - - void clear(); - - /** - * @param id Identifier of the episode to lookup info for. - * - * @return EpisodeInfo for the specified @a id; otherwise @c 0 (not found). - */ - EpisodeInfo *getEpisodeInfo(de::String id); - - /** - * @param mapUri Identifier of the map to lookup info for. - * - * @return MapInfo for the specified @a mapUri; otherwise @c 0 (not found). - */ - MapInfo *getMapInfo(de::Uri const &mapUri); -}; - -/** - * Parser for Hexen's MAPINFO definition lumps. - */ -class MapInfoParser -{ -public: - /// Base class for all parse-related errors. @ingroup errors - DENG2_ERROR(ParseError); - -public: - MapInfoParser(HexDefs &db); - - void parse(AutoStr const &buffer, de::String sourceFile); - - /** - * Clear any custom default MapInfo definition currently in use. MapInfos - * read after this is called will use the games' default definition as a - * basis (unless specified otherwise). - */ - void clearDefaultMap(); - -private: - DENG2_PRIVATE(d) -}; - /** * Hexen MAPINFO => DED translator. */ diff --git a/doomsday/plugins/idtech1converter/src/mapinfotranslator.cpp b/doomsday/plugins/idtech1converter/src/mapinfotranslator.cpp index 511b13fbc3..be3e81c27a 100644 --- a/doomsday/plugins/idtech1converter/src/mapinfotranslator.cpp +++ b/doomsday/plugins/idtech1converter/src/mapinfotranslator.cpp @@ -23,7 +23,10 @@ #include #include #include +#include #include +#include +#include #include "hexlex.h" using namespace de; @@ -31,58 +34,137 @@ using namespace de; namespace idtech1 { namespace internal { -static de::Uri composeMapUri(uint episode, uint map) -{ - de::String mapId; + class MapInfo : public de::Record + { + public: + MapInfo(); + MapInfo &operator = (MapInfo const &other); + + void resetToDefaults(); + }; + + class EpisodeInfo : public de::Record + { + public: + EpisodeInfo(); + EpisodeInfo &operator = (EpisodeInfo const &other); + + void resetToDefaults(); + }; + + /** + * Central database of definitions read from Hexen-derived definition formats. + * + * @note Ultimately the definitions this contains should instead have their sources + * translated into DED syntax and be made available from the main DED db instead. + */ + struct HexDefs + { + typedef std::map EpisodeInfos; + EpisodeInfos episodeInfos; + typedef std::map MapInfos; + MapInfos mapInfos; + + void clear() + { + episodeInfos.clear(); + mapInfos.clear(); + } + + /** + * @param id Identifier of the episode to lookup info for. + * + * @return EpisodeInfo for the specified @a id; otherwise @c 0 (not found). + */ + EpisodeInfo *getEpisodeInfo(String id) + { + if(!id.isEmpty()) + { + EpisodeInfos::iterator found = episodeInfos.find(id.toLower().toStdString()); + if(found != episodeInfos.end()) + { + return &found->second; + } + } + return 0; // Not found. + } + + /** + * @param mapUri Identifier of the map to lookup info for. + * + * @return MapInfo for the specified @a mapUri; otherwise @c 0 (not found). + */ + MapInfo *getMapInfo(de::Uri const &mapUri) + { + if(!mapUri.scheme().compareWithoutCase("Maps")) + { + MapInfos::iterator found = mapInfos.find(mapUri.path().toString().toLower().toStdString()); + if(found != mapInfos.end()) + { + return &found->second; + } + } + return 0; // Not found. + } + }; + + static inline String boolAsText(bool yes) + { + return yes? "true" : "false"; + } + + static de::Uri composeMapUri(uint episode, uint map) + { + de::String mapId; #if __JDOOM64__ - mapId = de::String("map%1").arg(map+1, 2, 10, QChar('0')); - DENG2_UNUSED(episode); -#elif __JDOOM__ - if(gameModeBits & GM_ANY_DOOM2) mapId = de::String("map%1").arg(map+1, 2, 10, QChar('0')); - else - mapId = de::String("e%1m%2").arg(episode+1).arg(map+1); + DENG2_UNUSED(episode); +#elif __JDOOM__ + if(gameModeBits & GM_ANY_DOOM2) + mapId = de::String("map%1").arg(map+1, 2, 10, QChar('0')); + else + mapId = de::String("e%1m%2").arg(episode+1).arg(map+1); #elif __JHERETIC__ - mapId = de::String("e%1m%2").arg(episode+1).arg(map+1); + mapId = de::String("e%1m%2").arg(episode+1).arg(map+1); #else - mapId = de::String("map%1").arg(map+1, 2, 10, QChar('0')); - DENG2_UNUSED(episode); + mapId = de::String("map%1").arg(map+1, 2, 10, QChar('0')); + DENG2_UNUSED(episode); #endif - return de::Uri("Maps", mapId); -} + return de::Uri("Maps", mapId); + } -static uint mapNumberFor(de::Uri const &mapUri) -{ - String path = mapUri.path(); - if(!path.isEmpty()) + static uint mapNumberFor(de::Uri const &mapUri) { + String path = mapUri.path(); + if(!path.isEmpty()) + { #if __JDOOM__ || __JHERETIC__ # if __JDOOM__ - if(gameModeBits & (GM_ANY_DOOM | ~GM_DOOM_CHEX)) + if(gameModeBits & (GM_ANY_DOOM | ~GM_DOOM_CHEX)) # endif - { - if(path.at(0).toLower() == 'e' && path.at(2).toLower() == 'm') { - return path.substr(3).toInt() - 1; + if(path.at(0).toLower() == 'e' && path.at(2).toLower() == 'm') + { + return path.substr(3).toInt() - 1; + } } - } #endif - if(path.beginsWith("map", Qt::CaseInsensitive)) - { - return path.substr(3).toInt() - 1; + if(path.beginsWith("map", Qt::CaseInsensitive)) + { + return path.substr(3).toInt() - 1; + } } + return 0; } - return 0; -} -static inline String defaultSkyMaterial() -{ + static inline String defaultSkyMaterial() + { #ifdef __JHEXEN__ - if(gameMode == hexen_demo || gameMode == hexen_betademo) - return "Textures:SKY2"; + if(gameMode == hexen_demo || gameMode == hexen_betademo) + return "Textures:SKY2"; #endif - return "Textures:SKY1"; -} + return "Textures:SKY1"; + } #define MUSIC_STARTUP "startup" #define MUSIC_ENDING1 "hall" @@ -91,1038 +173,1001 @@ static inline String defaultSkyMaterial() #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; - -MapInfo::MapInfo() : Record() -{ - resetToDefaults(); -} - -MapInfo &MapInfo::operator = (MapInfo const &other) -{ - static_cast(*this) = other; - return *this; -} + /** + * 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; -void MapInfo::resetToDefaults() -{ - // Add all expected fields with their default values. - addText ("map", "Maps:"); // URI. Unknown. - addNumber ("hub", 0); - addNumber ("warpTrans", 0); - addText ("nextMap", ""); // URI. None. (If scheme is "@wt" then the path is a warp trans number). - addText ("secretNextMap", ""); // URI. None. (If scheme is "@wt" then the path is a warp trans number). - addNumber ("cdTrack", 1); - addText ("title", "Untitled"); - addText ("sky1Material", defaultSkyMaterial()); - addText ("sky2Material", defaultSkyMaterial()); - addNumber ("sky1ScrollDelta", 0); - addNumber ("sky2ScrollDelta", 0); - addBoolean("doubleSky", false); - addBoolean("lightning", false); - addText ("fadeTable", "COLORMAP"); - addText ("songLump", "DEFSONG"); -} + //int cdTrack = track; + //Def_Set(DD_DEF_MUSIC, Def_Get(DD_DEF_MUSIC, musicId, 0), DD_CD_TRACK, &cdTrack); + } -EpisodeInfo::EpisodeInfo() : Record() -{ - resetToDefaults(); -} + /** + * Parser for Hexen's MAPINFO definition lumps. + */ + class MapInfoParser + { + public: + /// Base class for all parse-related errors. @ingroup errors + DENG2_ERROR(ParseError); + + MapInfoParser(HexDefs &db) : db(db), defaultMap(0) {} + ~MapInfoParser() { clearDefaultMap(); } + + /** + * Clear any custom default MapInfo definition currently in use. MapInfos + * read after this is called will use the games' default definition as a + * basis (unless specified otherwise). + */ + void clearDefaultMap() + { + delete defaultMap; defaultMap = 0; + } -EpisodeInfo &EpisodeInfo::operator = (EpisodeInfo const &other) -{ - static_cast(*this) = other; - return *this; -} + void parse(AutoStr const &buffer, de::String /*sourceFile*/) + { + LOG_AS("MapInfoParser"); -void EpisodeInfo::resetToDefaults() -{ - // Add all expected fields with their default values. - addText("startMap", "Maps:"); // URI. Unknown. - addText("title", "Untitled"); - addText("menuHelpInfo", ""); // None. - addText("menuImage", ""); // URI. None. - addText("menuShortcut", ""); // Key name. None. -} + // Nothing to parse? + if(Str_IsEmpty(&buffer)) + return; -DENG2_PIMPL(MapInfoParser) -{ - HexDefs &db; - HexLex lexer; - MapInfo *defaultMap; + lexer.parse(&buffer); + 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. + addDefaultMapIfNeeded(); + parseMap(defaultMap); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "adddefaultmap")) // ZDoom + { + // As per 'defaultmap' but additive. + addDefaultMapIfNeeded(false/*don't reset*/); + 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; + } - Instance(Public *i, HexDefs &db) - : Base(i) - , db (db) - , defaultMap(0) - {} + // Unexpected token encountered. + throw ParseError(String("Unexpected token '%1' on line #%2").arg(Str_Text(lexer.token())).arg(lexer.lineNumber())); + } + } - ~Instance() { clearDefaultMap(); } + private: + HexDefs &db; + HexLex lexer; + MapInfo *defaultMap; - void addDefaultMapIfNeeded(bool resetToDefaultsIfPresent = true) - { - if(!defaultMap) - { - defaultMap = new MapInfo; - } - else if(resetToDefaultsIfPresent) + void addDefaultMapIfNeeded(bool resetToDefaultsIfPresent = true) { - defaultMap->resetToDefaults(); + if(!defaultMap) + { + defaultMap = new MapInfo; + } + else if(resetToDefaultsIfPresent) + { + defaultMap->resetToDefaults(); + } } - } - - void clearDefaultMap() - { - delete defaultMap; defaultMap = 0; - } - void parseCluster() // ZDoom - { - LOG_WARNING("MAPINFO Cluster definitions are not supported."); + void parseCluster() // ZDoom + { + LOG_WARNING("MAPINFO Cluster definitions are not supported."); - /*int const clusterId = (int)*/lexer.readNumber(); + /*int const clusterId = (int)*/lexer.readNumber(); - // Process optional tokens. - while(lexer.readToken()) - { - if(!Str_CompareIgnoreCase(lexer.token(), "entertext")) + // Process optional tokens. + while(lexer.readToken()) { - String enterText = Str_Text(lexer.readString()); - - // Lookup the enter text from a Text definition? - if(!enterText.compareWithoutCase("lookup")) + if(!Str_CompareIgnoreCase(lexer.token(), "entertext")) { - enterText = Str_Text(lexer.readString()); - /*char *found = 0; - if(Def_Get(DD_DEF_TEXT, enterText.toUtf8().constData(), &found) >= 0) + String enterText = Str_Text(lexer.readString()); + + // Lookup the enter text from a Text definition? + if(!enterText.compareWithoutCase("lookup")) { - enterText = String(found); - }*/ + enterText = Str_Text(lexer.readString()); + /*char *found = 0; + if(Def_Get(DD_DEF_TEXT, enterText.toUtf8().constData(), &found) >= 0) + { + enterText = String(found); + }*/ + } + continue; } - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "exittext")) - { - String exitText = Str_Text(lexer.readString()); - - // Lookup the exit text from a Text definition? - if(!exitText.compareWithoutCase("lookup")) + if(!Str_CompareIgnoreCase(lexer.token(), "exittext")) { - exitText = Str_Text(lexer.readString()); - /*char *found = 0; - if(Def_Get(DD_DEF_TEXT, exitText.toUtf8().constData(), &found) >= 0) + String exitText = Str_Text(lexer.readString()); + + // Lookup the exit text from a Text definition? + if(!exitText.compareWithoutCase("lookup")) { - exitText = String(found); - }*/ + exitText = Str_Text(lexer.readString()); + /*char *found = 0; + if(Def_Get(DD_DEF_TEXT, exitText.toUtf8().constData(), &found) >= 0) + { + exitText = String(found); + }*/ + } + continue; } - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "music")) - { - lexer.readString(); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "flat")) - { - lexer.readString(); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "pic")) - { - lexer.readString(); - continue; + if(!Str_CompareIgnoreCase(lexer.token(), "music")) + { + lexer.readString(); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "flat")) + { + lexer.readString(); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "pic")) + { + lexer.readString(); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "hub")) + { + continue; + } + + lexer.unreadToken(); + break; } - if(!Str_CompareIgnoreCase(lexer.token(), "hub")) + } + + void parseEpisode() // ZDoom + { + LOG_WARNING("MAPINFO Episode definitions are not supported."); + de::Uri mapUri(Str_Text(lexer.readString()), RC_NULL); + if(mapUri.scheme().isEmpty()) mapUri.setScheme("Maps"); + + // Process optional tokens. + while(lexer.readToken()) { - continue; - } + if(!Str_CompareIgnoreCase(lexer.token(), "name")) + { + lexer.readString(); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "lookup")) + { + lexer.readString(); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "picname")) + { + lexer.readString(); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "key")) + { + lexer.readString(); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "remove")) + { + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "noskillmenu")) + { + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "optional")) + { + continue; + } - lexer.unreadToken(); - break; + lexer.unreadToken(); + break; + } } - } - - void parseEpisode() // ZDoom - { - LOG_WARNING("MAPINFO Episode definitions are not supported."); - de::Uri mapUri(Str_Text(lexer.readString()), RC_NULL); - if(mapUri.scheme().isEmpty()) mapUri.setScheme("Maps"); - // Process optional tokens. - while(lexer.readToken()) + /** + * @note EndGame definitions appear inside a Map definition and unlike all other definition + * block types are scoped with curly-braces. + * + * @param mapInfo MapInfo definition for which the EndGame subblock applies. + */ + void parseEndGame(MapInfo & /*mapInfo*/) // ZDoom { - if(!Str_CompareIgnoreCase(lexer.token(), "name")) + LOG_WARNING("MAPINFO Map.next[EndGame] definitions are not supported."); + + lexer.readToken(); + if(Str_CompareIgnoreCase(lexer.token(), "{")) + throw ParseError(String("Expected '{' but found '%1' on line #%2").arg(Str_Text(lexer.token())).arg(lexer.lineNumber())); + + while(lexer.readToken()) { - lexer.readString(); - continue; + if(!Str_CompareIgnoreCase(lexer.token(), "}")) + { + break; + } + if(!Str_CompareIgnoreCase(lexer.token(), "cast")) + { + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "hscroll")) + { + lexer.readString(); + lexer.readString(); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "music")) + { + lexer.readString(); + lexer.readNumber(); // Optional? + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "pic")) + { + lexer.readString(); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "vscroll")) + { + lexer.readString(); + lexer.readString(); + continue; + } + + lexer.unreadToken(); + break; } - if(!Str_CompareIgnoreCase(lexer.token(), "lookup")) + } + + /** + * @param isSecret @c true= this is the secret next map (from ZDoom). + */ + void parseMapNext(MapInfo &mapInfo, bool isSecret = false) + { + ddstring_s const *tok = lexer.readString(); + + // Perhaps a ZDoom EndGame directive? + if(!Str_CompareIgnoreCase(tok, "endpic")) { + LOG_WARNING("MAPINFO Map.next EndGame directives are not supported."); lexer.readString(); - continue; + return; } - if(!Str_CompareIgnoreCase(lexer.token(), "picname")) + if(!Str_CompareIgnoreCase(tok, "enddemon") || + !Str_CompareIgnoreCase(tok, "endgame1") || + !Str_CompareIgnoreCase(tok, "endgame2") || + !Str_CompareIgnoreCase(tok, "endgame3") || + !Str_CompareIgnoreCase(tok, "endgame4") || + !Str_CompareIgnoreCase(tok, "endgamec") || + !Str_CompareIgnoreCase(tok, "endgames") || + !Str_CompareIgnoreCase(tok, "endgamew")) { - lexer.readString(); - continue; + LOG_WARNING("MAPINFO Map.next EndGame directives are not supported."); + return; } - if(!Str_CompareIgnoreCase(lexer.token(), "key")) + if(!Str_CompareIgnoreCase(tok, "endgame")) { - lexer.readString(); - continue; + parseEndGame(mapInfo); + return; } - if(!Str_CompareIgnoreCase(lexer.token(), "remove")) + + de::Uri mapUri; + bool isNumber; + int mapNumber = String(Str_Text(tok)).toInt(&isNumber); // 1-based + if(!isNumber) { - continue; + mapUri = de::Uri(Str_Text(tok), RC_NULL); + if(mapUri.scheme().isEmpty()) mapUri.setScheme("Maps"); + mapInfo.set((isSecret? "secretNextMap" : "nextMap"), mapUri.compose()); } - if(!Str_CompareIgnoreCase(lexer.token(), "noskillmenu")) + else { - continue; + mapInfo.set((isSecret? "secretNextMap" : "nextMap"), String("@wt:%1").arg(mapNumber)); } - if(!Str_CompareIgnoreCase(lexer.token(), "optional")) - { - continue; - } - - lexer.unreadToken(); - break; } - } - - /** - * @note EndGame definitions appear inside a Map definition and unlike all other definition - * block types are scoped with curly-braces. - * - * @param mapInfo MapInfo definition for which the EndGame subblock applies. - */ - void parseEndGame(MapInfo & /*mapInfo*/) // ZDoom - { - LOG_WARNING("MAPINFO Map.next[EndGame] definitions are not supported."); - - lexer.readToken(); - if(Str_CompareIgnoreCase(lexer.token(), "{")) - throw ParseError(String("Expected '{' but found '%1' on line #%2").arg(Str_Text(lexer.token())).arg(lexer.lineNumber())); - while(lexer.readToken()) + /** + * @param info If non-zero parse the definition to this record. Otherwise the relevant + * MapInfo record will be located/created in the main database. + */ + void parseMap(MapInfo *info = 0) { - if(!Str_CompareIgnoreCase(lexer.token(), "}")) - { - break; - } - if(!Str_CompareIgnoreCase(lexer.token(), "cast")) - { - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "hscroll")) - { - lexer.readString(); - lexer.readString(); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "music")) - { - lexer.readString(); - lexer.readNumber(); // Optional? - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "pic")) - { - lexer.readString(); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "vscroll")) + if(!info) { - lexer.readString(); - lexer.readString(); - continue; - } + de::Uri mapUri; + String const mapRef = String(Str_Text(lexer.readString())); - lexer.unreadToken(); - break; - } - } + bool isNumber; + int mapNumber = mapRef.toInt(&isNumber); // 1-based + if(!isNumber) + { + mapUri = de::Uri(mapRef, RC_NULL); + if(mapUri.scheme().isEmpty()) mapUri.setScheme("Maps"); + } + else + { + if(mapNumber < 1) + { + throw ParseError(String("Invalid map number '%1' on line #%2").arg(mapNumber).arg(lexer.lineNumber())); + } + mapUri = composeMapUri(0, mapNumber - 1); + } - /** - * @param isSecret @c true= this is the secret next map (from ZDoom). - */ - void parseMapNext(MapInfo &mapInfo, bool isSecret = false) - { - ddstring_s const *tok = lexer.readString(); + // Lookup an existing map info from the database. + info = db.getMapInfo(mapUri); - // Perhaps a ZDoom EndGame directive? - if(!Str_CompareIgnoreCase(tok, "endpic")) - { - LOG_WARNING("MAPINFO Map.next EndGame directives are not supported."); - lexer.readString(); - return; - } - if(!Str_CompareIgnoreCase(tok, "enddemon") || - !Str_CompareIgnoreCase(tok, "endgame1") || - !Str_CompareIgnoreCase(tok, "endgame2") || - !Str_CompareIgnoreCase(tok, "endgame3") || - !Str_CompareIgnoreCase(tok, "endgame4") || - !Str_CompareIgnoreCase(tok, "endgamec") || - !Str_CompareIgnoreCase(tok, "endgames") || - !Str_CompareIgnoreCase(tok, "endgamew")) - { - LOG_WARNING("MAPINFO Map.next EndGame directives are not supported."); - return; - } - if(!Str_CompareIgnoreCase(tok, "endgame")) - { - parseEndGame(mapInfo); - return; - } + if(!info) + { + // A new map info. + info = &db.mapInfos[mapUri.path().asText().toLower().toUtf8().constData()]; - de::Uri mapUri; - bool isNumber; - int mapNumber = String(Str_Text(tok)).toInt(&isNumber); // 1-based - if(!isNumber) - { - mapUri = de::Uri(Str_Text(tok), RC_NULL); - if(mapUri.scheme().isEmpty()) mapUri.setScheme("Maps"); - mapInfo.set((isSecret? "secretNextMap" : "nextMap"), mapUri.compose()); - } - else - { - mapInfo.set((isSecret? "secretNextMap" : "nextMap"), String("@wt:%1").arg(mapNumber)); - } - } + // Initialize with custom default values? + if(defaultMap) + { + *info = *defaultMap; + } - /** - * @param info If non-zero parse the definition to this record. Otherwise the relevant - * MapInfo record will be located/created in the main database. - */ - void parseMap(MapInfo *info = 0) - { - if(!info) - { - de::Uri mapUri; - String const mapRef = String(Str_Text(lexer.readString())); + info->set("map", mapUri.compose()); - bool isNumber; - int mapNumber = mapRef.toInt(&isNumber); // 1-based - if(!isNumber) - { - mapUri = de::Uri(mapRef, RC_NULL); - if(mapUri.scheme().isEmpty()) mapUri.setScheme("Maps"); - } - else - { - if(mapNumber < 1) - { - throw ParseError(String("Invalid map number '%1' on line #%2").arg(mapNumber).arg(lexer.lineNumber())); + // Attempt to extract the "warp translation" number. + uint mapWarpNumber = mapNumberFor(mapUri); + info->set("warpTrans", mapWarpNumber != 0? mapWarpNumber + 1 : 0); } - mapUri = composeMapUri(0, mapNumber - 1); - } - // Lookup an existing map info from the database. - info = db.getMapInfo(mapUri); + // Map title must follow the number. + String title = Str_Text(lexer.readString()); - if(!info) - { - // A new map info. - info = &db.mapInfos[mapUri.path().asText().toLower().toUtf8().constData()]; - - // Initialize with custom default values? - if(defaultMap) + // Lookup the title from a Text definition? (ZDoom) + if(!title.compareWithoutCase("lookup")) { - *info = *defaultMap; + title = Str_Text(lexer.readString()); + /*char *found = 0; + if(Def_Get(DD_DEF_TEXT, title.toUtf8().constData(), &found) >= 0) + { + title = String(found); + }*/ } - - info->set("map", mapUri.compose()); - - // Attempt to extract the "warp translation" number. - uint mapWarpNumber = mapNumberFor(mapUri); - info->set("warpTrans", mapWarpNumber != 0? mapWarpNumber + 1 : 0); + info->set("title", title); } - // Map title must follow the number. - String title = Str_Text(lexer.readString()); - - // Lookup the title from a Text definition? (ZDoom) - if(!title.compareWithoutCase("lookup")) + // Process optional tokens. + while(lexer.readToken()) { - title = Str_Text(lexer.readString()); - /*char *found = 0; - if(Def_Get(DD_DEF_TEXT, title.toUtf8().constData(), &found) >= 0) + if(!Str_CompareIgnoreCase(lexer.token(), "allowcrouch")) // ZDoom { - title = String(found); - }*/ - } - info->set("title", title); - } - - // Process optional tokens. - while(lexer.readToken()) - { - if(!Str_CompareIgnoreCase(lexer.token(), "allowcrouch")) // ZDoom - { - LOG_WARNING("MAPINFO Map.allowCrouch is not supported."); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "allowjump")) // ZDoom - { - LOG_WARNING("MAPINFO Map.allowJump is not supported."); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "allowmonstertelefrags")) // ZDoom - { - LOG_WARNING("MAPINFO Map.allowMonsterTelefrags is not supported."); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "allowrespawn")) // ZDoom - { - LOG_WARNING("MAPINFO Map.allowRespawn is not supported."); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "aircontrol")) // ZDoom - { - LOG_WARNING("MAPINFO Map.airControl is not supported."); - lexer.readNumber(); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "airsupply")) // ZDoom - { - LOG_WARNING("MAPINFO Map.airSupply is not supported."); - lexer.readNumber(); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "autosequences")) // ZDoom - { - LOG_WARNING("MAPINFO Map.autosequences is not supported."); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "baronspecial")) // ZDoom - { - LOG_WARNING("MAPINFO Map.baronSpecial is not supported."); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "bordertexture")) // ZDoom - { - LOG_WARNING("MAPINFO Map.borderTexture is not supported."); - lexer.readString(); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "cdid")) // ZDoom - { - LOG_WARNING("MAPINFO Map.cdid is not supported."); - lexer.readString(); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "cdtrack")) - { - info->set("cdTrack", (int)lexer.readNumber()); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "checkswitchrange")) // ZDoom - { - LOG_WARNING("MAPINFO Map.checkSwitchRange is not supported."); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "clipmidtextures")) // ZDoom - { - LOG_WARNING("MAPINFO Map.clipMidtextures is not supported."); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "cluster")) - { - int const hubNum = (int)lexer.readNumber(); - if(hubNum < 1) + LOG_WARNING("MAPINFO Map.allowCrouch is not supported."); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "allowjump")) // ZDoom { - throw ParseError(String("Invalid 'cluster' (i.e., hub) number '%1' on line #%2").arg(Str_Text(lexer.token())).arg(lexer.lineNumber())); + LOG_WARNING("MAPINFO Map.allowJump is not supported."); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "allowmonstertelefrags")) // ZDoom + { + LOG_WARNING("MAPINFO Map.allowMonsterTelefrags is not supported."); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "allowrespawn")) // ZDoom + { + LOG_WARNING("MAPINFO Map.allowRespawn is not supported."); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "aircontrol")) // ZDoom + { + LOG_WARNING("MAPINFO Map.airControl is not supported."); + lexer.readNumber(); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "airsupply")) // ZDoom + { + LOG_WARNING("MAPINFO Map.airSupply is not supported."); + lexer.readNumber(); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "autosequences")) // ZDoom + { + LOG_WARNING("MAPINFO Map.autosequences is not supported."); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "baronspecial")) // ZDoom + { + LOG_WARNING("MAPINFO Map.baronSpecial is not supported."); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "bordertexture")) // ZDoom + { + LOG_WARNING("MAPINFO Map.borderTexture is not supported."); + lexer.readString(); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "cdid")) // ZDoom + { + LOG_WARNING("MAPINFO Map.cdid is not supported."); + lexer.readString(); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "cdtrack")) + { + info->set("cdTrack", (int)lexer.readNumber()); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "checkswitchrange")) // ZDoom + { + LOG_WARNING("MAPINFO Map.checkSwitchRange is not supported."); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "clipmidtextures")) // ZDoom + { + LOG_WARNING("MAPINFO Map.clipMidtextures is not supported."); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "cluster")) + { + int const hubNum = (int)lexer.readNumber(); + if(hubNum < 1) + { + throw ParseError(String("Invalid 'cluster' (i.e., hub) number '%1' on line #%2").arg(Str_Text(lexer.token())).arg(lexer.lineNumber())); + } + info->set("hub", hubNum); + continue; + } + if(String(Str_Text(lexer.token())).beginsWith("compat_", Qt::CaseInsensitive)) // ZDoom + { + LOG_WARNING("MAPINFO Map.%s is not supported.") << lexer.token(); + lexer.readNumber(); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "cyberdemonspecial")) // ZDoom + { + LOG_WARNING("MAPINFO Map.cyberdemonSpecial is not supported."); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "doublesky")) + { + info->set("doubleSky", true); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "enterpic")) // ZDoom + { + LOG_WARNING("MAPINFO Map.enterPic is not supported."); + lexer.readString(); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "evenlighting")) // ZDoom + { + LOG_WARNING("MAPINFO Map.evenlighting is not supported."); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "exitpic")) // ZDoom + { + LOG_WARNING("MAPINFO Map.exitPic is not supported."); + lexer.readString(); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "f1")) // ZDoom + { + LOG_WARNING("MAPINFO Map.f1 is not supported."); + lexer.readString(); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "fadetable")) + { + info->set("fadeTable", Str_Text(lexer.readString())); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "fade")) // ZDoom + { + LOG_WARNING("MAPINFO Map.fade is not supported."); + lexer.readString(); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "fallingdamage")) // ZDoom + { + LOG_WARNING("MAPINFO Map.fallingdamage is not supported."); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "filterstarts")) // ZDoom + { + LOG_WARNING("MAPINFO Map.filterStarts is not supported."); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "forceFallingDamage")) // ZDoom + { + LOG_WARNING("MAPINFO Map.forceFallingDamage is not supported."); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "forceNoSkyStretch")) // ZDoom + { + LOG_WARNING("MAPINFO Map.forceNoSkyStretch is not supported."); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "gravity")) // ZDoom + { + LOG_WARNING("MAPINFO Map.gravity is not supported."); + lexer.readNumber(); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "horizwallshade")) // ZDoom + { + LOG_WARNING("MAPINFO Map.horizwallShade is not supported."); + lexer.readNumber(); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "infiniteflightpowerup")) // ZDoom + { + LOG_WARNING("MAPINFO Map.infiniteFlightPowerup is not supported."); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "intermusic")) // ZDoom + { + LOG_WARNING("MAPINFO Map.interMusic is not supported."); + lexer.readString(); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "keepfullinventory")) // ZDoom + { + LOG_WARNING("MAPINFO Map.keepFullInventory is not supported."); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "laxmonsteractivation")) // ZDoom + { + LOG_WARNING("MAPINFO Map.laxMonsterActivation is not supported."); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "lightning")) + { + info->set("lightning", true); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "map07special")) // ZDoom + { + LOG_WARNING("MAPINFO Map.map07Special is not supported."); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "monsterfallingdamage")) // ZDoom + { + LOG_WARNING("MAPINFO Map.monsterFallingDamage is not supported."); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "missilesactivateimpactlines")) // ZDoom + { + LOG_WARNING("MAPINFO Map.missilesActivateImpactLines is not supported."); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "missileshootersactivateimpactlines")) // ZDoom + { + LOG_WARNING("MAPINFO Map.missileshootersActivateImpactLines is not supported."); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "music")) // ZDoom + { + LOG_WARNING("MAPINFO Map.music is not supported."); + lexer.readString(); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "next")) + { + parseMapNext(*info); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "noautosequences")) // ZDoom + { + LOG_WARNING("MAPINFO Map.noAutoSequences is not supported."); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "nocheckswitchrange")) // ZDoom + { + LOG_WARNING("MAPINFO Map.noCheckSwitchRange is not supported."); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "nocrouch")) // ZDoom + { + LOG_WARNING("MAPINFO Map.noCrouch is not supported."); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "nofallingdamage")) // ZDoom + { + LOG_WARNING("MAPINFO Map.noFallingDamage is not supported."); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "noinfighting")) // ZDoom + { + LOG_WARNING("MAPINFO Map.noInfighting is not supported."); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "nointermission")) // ZDoom + { + LOG_WARNING("MAPINFO Map.noIntermission is not supported."); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "noinventorybar")) // ZDoom + { + LOG_WARNING("MAPINFO Map.noInventorybar is not supported."); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "nojump")) // ZDoom + { + LOG_WARNING("MAPINFO Map.noJump is not supported."); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "normalinfighting")) // ZDoom + { + LOG_WARNING("MAPINFO Map.normalInfighting is not supported."); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "nosoundclipping")) // ZDoom + { + LOG_WARNING("MAPINFO Map.noSoundClipping is not supported."); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "oldfallingdamage")) // ZDoom + { + LOG_WARNING("MAPINFO Map.oldFallingDamage is not supported."); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "outsidefog")) // ZDoom + { + LOG_WARNING("MAPINFO Map.outsideFog is not supported."); + lexer.readString(); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "par")) // ZDoom + { + LOG_WARNING("MAPINFO Map.par is not supported."); + lexer.readNumber(); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "secretnext")) // ZDoom + { + parseMapNext(*info, true/*is-secret*/); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "sky1")) + { + info->set("sky1Material", lexer.readUri("Textures").compose()); + info->set("sky1ScrollDelta", lexer.readNumber() / 256.f); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "sky2")) + { + info->set("sky2Material", lexer.readUri("Textures").compose()); + info->set("sky2ScrollDelta", lexer.readNumber() / 256.f); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "skystretch")) // ZDoom + { + LOG_WARNING("MAPINFO Map.skyStretch is not supported."); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "specialaction_exitlevel")) // ZDoom + { + LOG_WARNING("MAPINFO Map.specialaction_exitlevel is not supported."); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "specialaction_killmonsters")) // ZDoom + { + LOG_WARNING("MAPINFO Map.specialaction_killmonsters is not supported."); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "specialaction_lowerfloor")) // ZDoom + { + LOG_WARNING("MAPINFO Map.specialaction_lowerfloor is not supported."); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "specialaction_opendoor")) // ZDoom + { + LOG_WARNING("MAPINFO Map.specialaction_opendoor is not supported."); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "spidermastermindspecial")) // ZDoom + { + LOG_WARNING("MAPINFO Map.spidermastermindSpecial is not supported."); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "smoothlighting")) // ZDoom + { + LOG_WARNING("MAPINFO Map.smoothlighting is not supported."); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "strictmonsteractivation")) // ZDoom + { + LOG_WARNING("MAPINFO Map.strictMonsterActivation is not supported."); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "strifefallingdamage")) // ZDoom + { + LOG_WARNING("MAPINFO Map.strifeFallingDamage is not supported."); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "teamdamage")) // ZDoom + { + LOG_WARNING("MAPINFO Map.teamDamage is not supported."); + lexer.readNumber(); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "teamplayoff")) // ZDoom + { + LOG_WARNING("MAPINFO Map.teamplayOff is not supported."); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "teamplayon")) // ZDoom + { + LOG_WARNING("MAPINFO Map.teamplayOn is not supported."); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "titlepatch")) // ZDoom + { + LOG_WARNING("MAPINFO Map.titlePatch is not supported."); + lexer.readString(); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "totalinfighting")) // ZDoom + { + LOG_WARNING("MAPINFO Map.totalInfighting is not supported."); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "translator")) // ZDoom + { + LOG_WARNING("MAPINFO Map.translator is not supported."); + lexer.readString(); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "unfreezesingleplayerconversations")) // ZDoom + { + LOG_WARNING("MAPINFO Map.unfreezeSingleplayerConversations is not supported."); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "vertwallshade")) // ZDoom + { + LOG_WARNING("MAPINFO Map.vertwallShade is not supported."); + lexer.readNumber(); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "warptrans") || + !Str_CompareIgnoreCase(lexer.token(), "levelnum") /* ZDoom */) + { + info->set("warpTrans", (int)lexer.readNumber()); + continue; } - info->set("hub", hubNum); - continue; - } - if(String(Str_Text(lexer.token())).beginsWith("compat_", Qt::CaseInsensitive)) // ZDoom - { - LOG_WARNING("MAPINFO Map.%s is not supported.") << lexer.token(); - lexer.readNumber(); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "cyberdemonspecial")) // ZDoom - { - LOG_WARNING("MAPINFO Map.cyberdemonSpecial is not supported."); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "doublesky")) - { - info->set("doubleSky", true); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "enterpic")) // ZDoom - { - LOG_WARNING("MAPINFO Map.enterPic is not supported."); - lexer.readString(); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "evenlighting")) // ZDoom - { - LOG_WARNING("MAPINFO Map.evenlighting is not supported."); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "exitpic")) // ZDoom - { - LOG_WARNING("MAPINFO Map.exitPic is not supported."); - lexer.readString(); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "f1")) // ZDoom - { - LOG_WARNING("MAPINFO Map.f1 is not supported."); - lexer.readString(); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "fadetable")) - { - info->set("fadeTable", Str_Text(lexer.readString())); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "fade")) // ZDoom - { - LOG_WARNING("MAPINFO Map.fade is not supported."); - lexer.readString(); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "fallingdamage")) // ZDoom - { - LOG_WARNING("MAPINFO Map.fallingdamage is not supported."); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "filterstarts")) // ZDoom - { - LOG_WARNING("MAPINFO Map.filterStarts is not supported."); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "forceFallingDamage")) // ZDoom - { - LOG_WARNING("MAPINFO Map.forceFallingDamage is not supported."); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "forceNoSkyStretch")) // ZDoom - { - LOG_WARNING("MAPINFO Map.forceNoSkyStretch is not supported."); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "gravity")) // ZDoom - { - LOG_WARNING("MAPINFO Map.gravity is not supported."); - lexer.readNumber(); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "horizwallshade")) // ZDoom - { - LOG_WARNING("MAPINFO Map.horizwallShade is not supported."); - lexer.readNumber(); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "infiniteflightpowerup")) // ZDoom - { - LOG_WARNING("MAPINFO Map.infiniteFlightPowerup is not supported."); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "intermusic")) // ZDoom - { - LOG_WARNING("MAPINFO Map.interMusic is not supported."); - lexer.readString(); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "keepfullinventory")) // ZDoom - { - LOG_WARNING("MAPINFO Map.keepFullInventory is not supported."); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "laxmonsteractivation")) // ZDoom - { - LOG_WARNING("MAPINFO Map.laxMonsterActivation is not supported."); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "lightning")) - { - info->set("lightning", true); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "map07special")) // ZDoom - { - LOG_WARNING("MAPINFO Map.map07Special is not supported."); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "monsterfallingdamage")) // ZDoom - { - LOG_WARNING("MAPINFO Map.monsterFallingDamage is not supported."); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "missilesactivateimpactlines")) // ZDoom - { - LOG_WARNING("MAPINFO Map.missilesActivateImpactLines is not supported."); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "missileshootersactivateimpactlines")) // ZDoom - { - LOG_WARNING("MAPINFO Map.missileshootersActivateImpactLines is not supported."); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "music")) // ZDoom - { - LOG_WARNING("MAPINFO Map.music is not supported."); - lexer.readString(); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "next")) - { - parseMapNext(*info); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "noautosequences")) // ZDoom - { - LOG_WARNING("MAPINFO Map.noAutoSequences is not supported."); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "nocheckswitchrange")) // ZDoom - { - LOG_WARNING("MAPINFO Map.noCheckSwitchRange is not supported."); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "nocrouch")) // ZDoom - { - LOG_WARNING("MAPINFO Map.noCrouch is not supported."); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "nofallingdamage")) // ZDoom - { - LOG_WARNING("MAPINFO Map.noFallingDamage is not supported."); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "noinfighting")) // ZDoom - { - LOG_WARNING("MAPINFO Map.noInfighting is not supported."); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "nointermission")) // ZDoom - { - LOG_WARNING("MAPINFO Map.noIntermission is not supported."); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "noinventorybar")) // ZDoom - { - LOG_WARNING("MAPINFO Map.noInventorybar is not supported."); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "nojump")) // ZDoom - { - LOG_WARNING("MAPINFO Map.noJump is not supported."); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "normalinfighting")) // ZDoom - { - LOG_WARNING("MAPINFO Map.normalInfighting is not supported."); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "nosoundclipping")) // ZDoom - { - LOG_WARNING("MAPINFO Map.noSoundClipping is not supported."); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "oldfallingdamage")) // ZDoom - { - LOG_WARNING("MAPINFO Map.oldFallingDamage is not supported."); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "outsidefog")) // ZDoom - { - LOG_WARNING("MAPINFO Map.outsideFog is not supported."); - lexer.readString(); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "par")) // ZDoom - { - LOG_WARNING("MAPINFO Map.par is not supported."); - lexer.readNumber(); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "secretnext")) // ZDoom - { - parseMapNext(*info, true/*is-secret*/); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "sky1")) - { - info->set("sky1Material", lexer.readUri("Textures").compose()); - info->set("sky1ScrollDelta", lexer.readNumber() / 256.f); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "sky2")) - { - info->set("sky2Material", lexer.readUri("Textures").compose()); - info->set("sky2ScrollDelta", lexer.readNumber() / 256.f); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "skystretch")) // ZDoom - { - LOG_WARNING("MAPINFO Map.skyStretch is not supported."); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "specialaction_exitlevel")) // ZDoom - { - LOG_WARNING("MAPINFO Map.specialaction_exitlevel is not supported."); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "specialaction_killmonsters")) // ZDoom - { - LOG_WARNING("MAPINFO Map.specialaction_killmonsters is not supported."); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "specialaction_lowerfloor")) // ZDoom - { - LOG_WARNING("MAPINFO Map.specialaction_lowerfloor is not supported."); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "specialaction_opendoor")) // ZDoom - { - LOG_WARNING("MAPINFO Map.specialaction_opendoor is not supported."); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "spidermastermindspecial")) // ZDoom - { - LOG_WARNING("MAPINFO Map.spidermastermindSpecial is not supported."); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "smoothlighting")) // ZDoom - { - LOG_WARNING("MAPINFO Map.smoothlighting is not supported."); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "strictmonsteractivation")) // ZDoom - { - LOG_WARNING("MAPINFO Map.strictMonsterActivation is not supported."); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "strifefallingdamage")) // ZDoom - { - LOG_WARNING("MAPINFO Map.strifeFallingDamage is not supported."); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "teamdamage")) // ZDoom - { - LOG_WARNING("MAPINFO Map.teamDamage is not supported."); - lexer.readNumber(); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "teamplayoff")) // ZDoom - { - LOG_WARNING("MAPINFO Map.teamplayOff is not supported."); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "teamplayon")) // ZDoom - { - LOG_WARNING("MAPINFO Map.teamplayOn is not supported."); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "titlepatch")) // ZDoom - { - LOG_WARNING("MAPINFO Map.titlePatch is not supported."); - lexer.readString(); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "totalinfighting")) // ZDoom - { - LOG_WARNING("MAPINFO Map.totalInfighting is not supported."); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "translator")) // ZDoom - { - LOG_WARNING("MAPINFO Map.translator is not supported."); - lexer.readString(); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "unfreezesingleplayerconversations")) // ZDoom - { - LOG_WARNING("MAPINFO Map.unfreezeSingleplayerConversations is not supported."); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "vertwallshade")) // ZDoom - { - LOG_WARNING("MAPINFO Map.vertwallShade is not supported."); - lexer.readNumber(); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "warptrans") || - !Str_CompareIgnoreCase(lexer.token(), "levelnum") /* ZDoom */) - { - info->set("warpTrans", (int)lexer.readNumber()); - continue; - } - lexer.unreadToken(); - break; + lexer.unreadToken(); + break; + } } - } - void parseSkill() // ZDoom - { - LOG_WARNING("MAPINFO Skill definitions are not supported."); + void parseSkill() // ZDoom + { + LOG_WARNING("MAPINFO Skill definitions are not supported."); - /*ddstring_s const *id =*/ lexer.readString(); + /*ddstring_s const *id =*/ lexer.readString(); - // Process optional tokens. - while(lexer.readToken()) - { - if(!Str_CompareIgnoreCase(lexer.token(), "acsreturn")) - { - lexer.readNumber(); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "aggressiveness")) - { - lexer.readNumber(); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "ammofactor")) - { - lexer.readNumber(); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "autousehealth")) - { - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "damagefactor")) - { - lexer.readNumber(); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "disablecheats")) - { - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "doubleammofactor")) - { - lexer.readNumber(); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "dropammofactor")) - { - lexer.readNumber(); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "easybossbrain")) - { - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "fastmonsters")) - { - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "key")) - { - lexer.readString(); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "mustconfirm")) - { - lexer.readString(); // Optional? - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "name")) - { - lexer.readString(); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "picname")) - { - lexer.readString(); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "playerclassname")) - { - lexer.readString(); - lexer.readString(); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "respawnlimit")) - { - lexer.readNumber(); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "respawntime")) - { - lexer.readNumber(); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "spawnfilter")) - { - lexer.readString(); - continue; - } - if(!Str_CompareIgnoreCase(lexer.token(), "textcolor")) + // Process optional tokens. + while(lexer.readToken()) { - lexer.readString(); - continue; - } + if(!Str_CompareIgnoreCase(lexer.token(), "acsreturn")) + { + lexer.readNumber(); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "aggressiveness")) + { + lexer.readNumber(); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "ammofactor")) + { + lexer.readNumber(); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "autousehealth")) + { + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "damagefactor")) + { + lexer.readNumber(); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "disablecheats")) + { + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "doubleammofactor")) + { + lexer.readNumber(); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "dropammofactor")) + { + lexer.readNumber(); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "easybossbrain")) + { + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "fastmonsters")) + { + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "key")) + { + lexer.readString(); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "mustconfirm")) + { + lexer.readString(); // Optional? + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "name")) + { + lexer.readString(); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "picname")) + { + lexer.readString(); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "playerclassname")) + { + lexer.readString(); + lexer.readString(); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "respawnlimit")) + { + lexer.readNumber(); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "respawntime")) + { + lexer.readNumber(); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "spawnfilter")) + { + lexer.readString(); + continue; + } + if(!Str_CompareIgnoreCase(lexer.token(), "textcolor")) + { + lexer.readString(); + continue; + } - lexer.unreadToken(); - break; + lexer.unreadToken(); + break; + } } - } -}; + }; -MapInfoParser::MapInfoParser(HexDefs &db) : d(new Instance(this, db)) -{} +} // namespace internal + +using namespace internal; -void MapInfoParser::clearDefaultMap() +MapInfo::MapInfo() : Record() { - d->clearDefaultMap(); + resetToDefaults(); } -void MapInfoParser::parse(AutoStr const &buffer, String /*sourceFile*/) +MapInfo &MapInfo::operator = (MapInfo const &other) { - LOG_AS("MapInfoParser"); - - // Nothing to parse? - if(Str_IsEmpty(&buffer)) - return; - - d->lexer.parse(&buffer); - while(d->lexer.readToken()) - { - 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())); - } + static_cast(*this) = other; + return *this; } -void HexDefs::clear() +void MapInfo::resetToDefaults() { - episodeInfos.clear(); - mapInfos.clear(); + // Add all expected fields with their default values. + addText ("map", "Maps:"); // URI. Unknown. + addNumber ("hub", 0); + addNumber ("warpTrans", 0); + addText ("nextMap", ""); // URI. None. (If scheme is "@wt" then the path is a warp trans number). + addText ("secretNextMap", ""); // URI. None. (If scheme is "@wt" then the path is a warp trans number). + addNumber ("cdTrack", 1); + addText ("title", "Untitled"); + addText ("sky1Material", defaultSkyMaterial()); + addText ("sky2Material", defaultSkyMaterial()); + addNumber ("sky1ScrollDelta", 0); + addNumber ("sky2ScrollDelta", 0); + addBoolean("doubleSky", false); + addBoolean("lightning", false); + addText ("fadeTable", "COLORMAP"); + addText ("songLump", "DEFSONG"); } -EpisodeInfo *HexDefs::getEpisodeInfo(String id) +EpisodeInfo::EpisodeInfo() : Record() { - if(!id.isEmpty()) - { - EpisodeInfos::iterator found = episodeInfos.find(id.toLower().toStdString()); - if(found != episodeInfos.end()) - { - return &found->second; - } - } - return 0; // Not found. + resetToDefaults(); } -MapInfo *HexDefs::getMapInfo(de::Uri const &mapUri) +EpisodeInfo &EpisodeInfo::operator = (EpisodeInfo const &other) { - if(!mapUri.scheme().compareWithoutCase("Maps")) - { - MapInfos::iterator found = mapInfos.find(mapUri.path().toString().toLower().toStdString()); - if(found != mapInfos.end()) - { - return &found->second; - } - } - return 0; // Not found. + static_cast(*this) = other; + return *this; } -static inline String boolAsText(bool yes) +void EpisodeInfo::resetToDefaults() { - return yes? "true" : "false"; + // Add all expected fields with their default values. + addText("startMap", "Maps:"); // URI. Unknown. + addText("title", "Untitled"); + addText("menuHelpInfo", ""); // None. + addText("menuImage", ""); // URI. None. + addText("menuShortcut", ""); // Key name. None. } DENG2_PIMPL_NOREF(MapInfoTranslator) @@ -1239,6 +1284,8 @@ void MapInfoTranslator::reset() void MapInfoTranslator::mergeFromFile(String sourceFile) { + LOG_AS("MapInfoTranslator"); + dd_bool sourceIsCustom; AutoStr *buffer = M_ReadFileIntoString(AutoStr_FromTextStd(sourceFile.toUtf8().constData()), &sourceIsCustom); if(!buffer || Str_IsEmpty(buffer)) return; @@ -1263,6 +1310,8 @@ void MapInfoTranslator::mergeFromFile(String sourceFile) String MapInfoTranslator::translate() { + LOG_AS("MapInfoTranslator"); + // Perform necessary preprocessing (must be done before translation). d->preprocess(); @@ -1415,6 +1464,8 @@ String MapInfoTranslator::translate() os << "\n}"; } + reset(); + return text; }