From 47d79c9977027e8f66abc74832c433d7623c02be Mon Sep 17 00:00:00 2001 From: krzys-h Date: Wed, 5 Aug 2015 19:27:26 +0200 Subject: [PATCH] Added #Include support in level files --- src/common/pathman.cpp | 28 ------------ src/common/pathman.h | 3 -- src/object/level/parser.cpp | 61 ++++++++++++++++++++++++++- src/object/level/parser.h | 18 ++++++++ src/object/level/parserexceptions.cpp | 4 +- src/object/level/parserline.cpp | 13 ++++++ src/object/level/parserline.h | 3 ++ src/object/level/parserparam.cpp | 5 +-- src/object/robotmain.cpp | 2 +- src/script/scriptfunc.cpp | 5 ++- src/ui/edit.cpp | 10 ++--- 11 files changed, 107 insertions(+), 45 deletions(-) diff --git a/src/common/pathman.cpp b/src/common/pathman.cpp index 01d7834d..c6eaedee 100644 --- a/src/common/pathman.cpp +++ b/src/common/pathman.cpp @@ -169,31 +169,3 @@ void CPathManager::LoadModsFromDir(const std::string &dir) GetLogger()->Warn("Unable to load mods from directory '%s': %s\n", dir.c_str(), e.what()); } } - -std::string CPathManager::InjectLevelDir(std::string path, const std::string& defaultDir) -{ - std::string newPath = path; - std::string lvlDir = CLevelParser::BuildScenePath(CRobotMain::GetInstancePointer()->GetLevelCategory(), CRobotMain::GetInstancePointer()->GetLevelChap(), CRobotMain::GetInstancePointer()->GetLevelRank(), false); - boost::replace_all(newPath, "%lvl%", lvlDir); - std::string chapDir = CLevelParser::BuildScenePath(CRobotMain::GetInstancePointer()->GetLevelCategory(), CRobotMain::GetInstancePointer()->GetLevelChap(), 0, false); - boost::replace_all(newPath, "%chap%", chapDir); - std::string catDir = CLevelParser::BuildCategoryPath(CRobotMain::GetInstancePointer()->GetLevelCategory()); - boost::replace_all(newPath, "%cat%", catDir); - if(newPath == path && !path.empty()) - { - newPath = defaultDir + (!defaultDir.empty() ? "/" : "") + newPath; - } - - std::string langPath = newPath; - std::string langStr(1, CApplication::GetInstancePointer()->GetLanguageChar()); - boost::replace_all(langPath, "%lng%", langStr); - if(CResourceManager::Exists(langPath)) - return langPath; - - // Fallback to English if file doesn't exist - boost::replace_all(newPath, "%lng%", "E"); - if(CResourceManager::Exists(newPath)) - return newPath; - - return langPath; // Return current language file if none of the files exist -} diff --git a/src/common/pathman.h b/src/common/pathman.h index a17d931e..7cf505a4 100644 --- a/src/common/pathman.h +++ b/src/common/pathman.h @@ -52,9 +52,6 @@ public: //! Loads configured paths void InitPaths(); - //! Does the %lvl%, %chap%, %cat% and %lng% replacements (with fallback to English when translations are not available) - static std::string InjectLevelDir(std::string path, const std::string& defaultDir = ""); - private: //! Loads all mods from given directory void LoadModsFromDir(const std::string &dir); diff --git a/src/object/level/parser.cpp b/src/object/level/parser.cpp index ee627a45..527d2d4d 100644 --- a/src/object/level/parser.cpp +++ b/src/object/level/parser.cpp @@ -22,6 +22,7 @@ #include "app/app.h" #include "common/make_unique.h" +#include "common/stringutils.h" #include "common/resources/inputstream.h" #include "common/resources/outputstream.h" @@ -44,16 +45,28 @@ CLevelParser::CLevelParser() { m_filename = ""; + + m_pathCat = ""; + m_pathChap = ""; + m_pathLvl = ""; } CLevelParser::CLevelParser(std::string filename) { m_filename = filename; + + m_pathCat = ""; + m_pathChap = ""; + m_pathLvl = ""; } CLevelParser::CLevelParser(std::string category, int chapter, int rank) { m_filename = BuildScenePath(category, chapter, rank); + + m_pathCat = BuildCategoryPath(category); + m_pathChap = BuildScenePath(category, chapter, 0, false); + m_pathLvl = BuildScenePath(category, chapter, rank, false); } CLevelParser::CLevelParser(LevelCategory category, int chapter, int rank) @@ -187,6 +200,7 @@ void CLevelParser::Load() continue; auto parserLine = MakeUnique(lineNumber, command); + parserLine->SetLevel(this); if (command.length() > 2 && command[command.length() - 2] == '.') { @@ -265,7 +279,27 @@ void CLevelParser::Load() boost::algorithm::trim(line); } - AddLine(std::move(parserLine)); + if (parserLine->GetCommand().length() > 1 && parserLine->GetCommand()[0] == '#') + { + std::string cmd = parserLine->GetCommand().substr(1, std::string::npos); + if(cmd == "Include") + { + std::unique_ptr includeParser = MakeUnique(parserLine->GetParam("file")->AsPath("")); + includeParser->Load(); + for(CLevelParserLineUPtr& line : includeParser->m_lines) + { + AddLine(std::move(line)); + } + } + else + { + throw CLevelParserException("Unknown preprocessor command '#" + cmd + "' (in " + m_filename + ":" + StrUtils::ToString(lineNumber) + ")"); + } + } + else + { + AddLine(std::move(parserLine)); + } } file.close(); @@ -286,6 +320,31 @@ void CLevelParser::Save() file.close(); } +std::string CLevelParser::InjectLevelPaths(const std::string& path, const std::string& defaultDir) +{ + std::string newPath = path; + if(!m_pathLvl.empty() ) boost::replace_all(newPath, "%lvl%", m_pathLvl); + if(!m_pathChap.empty()) boost::replace_all(newPath, "%chap%", m_pathChap); + if(!m_pathCat.empty() ) boost::replace_all(newPath, "%cat%", m_pathCat); + if(newPath == path && !path.empty()) + { + newPath = defaultDir + (!defaultDir.empty() ? "/" : "") + newPath; + } + + std::string langPath = newPath; + std::string langStr(1, CApplication::GetInstancePointer()->GetLanguageChar()); + boost::replace_all(langPath, "%lng%", langStr); + if(CResourceManager::Exists(langPath)) + return langPath; + + // Fallback to English if file doesn't exist + boost::replace_all(newPath, "%lng%", "E"); + if(CResourceManager::Exists(newPath)) + return newPath; + + return langPath; // Return current language file if none of the files exist +} + const std::string& CLevelParser::GetFilename() { return m_filename; diff --git a/src/object/level/parser.h b/src/object/level/parser.h index 7d5a2fc5..9694c36b 100644 --- a/src/object/level/parser.h +++ b/src/object/level/parser.h @@ -26,6 +26,10 @@ #include "object/level_category.h" +#include "common/make_unique.h" + +#include "object/robotmain.h" + #include "object/level/parserexceptions.h" #include "object/level/parserline.h" #include "object/level/parserparam.h" @@ -65,6 +69,9 @@ public: //! Save file void Save(); + //! Inject %something% paths + std::string InjectLevelPaths(const std::string& path, const std::string& defaultDir = ""); + //! Get filename const std::string& GetFilename(); @@ -83,4 +90,15 @@ public: private: std::string m_filename; std::vector m_lines; + + std::string m_pathCat; + std::string m_pathChap; + std::string m_pathLvl; }; + +inline std::string InjectLevelPathsForCurrentLevel(const std::string& path, const std::string& defaultDir = "") +{ + CRobotMain* main = CRobotMain::GetInstancePointer(); + auto levelParser = MakeUnique(main->GetLevelCategory(), main->GetLevelChap(), main->GetLevelRank()); + return levelParser->InjectLevelPaths(path, "ai"); +} diff --git a/src/object/level/parserexceptions.cpp b/src/object/level/parserexceptions.cpp index 1869a774..d7f3f09b 100644 --- a/src/object/level/parserexceptions.cpp +++ b/src/object/level/parserexceptions.cpp @@ -28,7 +28,7 @@ std::string FormatMissingParamError(CLevelParserParam* thisParam) NOEXCEPT { auto paramName = thisParam->GetName(); auto lineNumber = boost::lexical_cast(thisParam->GetLine()->GetLineNumber()); - auto fileName = thisParam->GetLine()->GetLevel()->GetFilename(); + auto fileName = thisParam->GetLine()->GetLevelFilename(); return "Missing required param '" + paramName + "' (in " + fileName + ":" + lineNumber + ")"; } @@ -42,7 +42,7 @@ std::string FormatBadParamError(CLevelParserParam* thisParam, std::string reques auto paramName = thisParam->GetName(); auto paramValue = thisParam->GetValue(); auto lineNumber = boost::lexical_cast(thisParam->GetLine()->GetLineNumber()); - auto fileName = thisParam->GetLine()->GetLevel()->GetFilename(); + auto fileName = thisParam->GetLine()->GetLevelFilename(); return "Unable to parse '" + paramValue + "' as " + requestedType + " (param '" + paramName + "' in " + fileName + ":" + lineNumber + ")"; } diff --git a/src/object/level/parserline.cpp b/src/object/level/parserline.cpp index cdf11cea..6b97b898 100644 --- a/src/object/level/parserline.cpp +++ b/src/object/level/parserline.cpp @@ -27,12 +27,14 @@ CLevelParserLine::CLevelParserLine(std::string command) { m_command = command; + m_levelFilename = ""; m_lineNumber = 0; } CLevelParserLine::CLevelParserLine(int lineNumber, std::string command) { m_command = command; + m_levelFilename = ""; m_lineNumber = lineNumber; } @@ -49,6 +51,17 @@ CLevelParser* CLevelParserLine::GetLevel() void CLevelParserLine::SetLevel(CLevelParser* level) { m_level = level; + + // Only on the first call - this makes sure the level name doesn't change if the file is loaded using #Include + if (m_levelFilename.empty()) + { + m_levelFilename = m_level->GetFilename(); + } +} + +const std::string& CLevelParserLine::GetLevelFilename() +{ + return m_levelFilename; } std::string CLevelParserLine::GetCommand() diff --git a/src/object/level/parserline.h b/src/object/level/parserline.h index c5cadc0c..9478fcd5 100644 --- a/src/object/level/parserline.h +++ b/src/object/level/parserline.h @@ -48,6 +48,8 @@ public: //! Set CLevelParser this line is part of void SetLevel(CLevelParser* level); + const std::string& GetLevelFilename(); + std::string GetCommand(); void SetCommand(std::string command); @@ -58,6 +60,7 @@ public: private: CLevelParser* m_level; + std::string m_levelFilename; int m_lineNumber; std::string m_command; std::map m_params; diff --git a/src/object/level/parserparam.cpp b/src/object/level/parserparam.cpp index b61367f5..585a86b7 100644 --- a/src/object/level/parserparam.cpp +++ b/src/object/level/parserparam.cpp @@ -23,7 +23,6 @@ #include "common/logger.h" #include "common/make_unique.h" -#include "common/pathman.h" #include "common/resources/resourcemanager.h" @@ -227,7 +226,7 @@ std::string CLevelParserParam::ToPath(std::string path, const std::string defaul if (defaultDir == "" && path.find("%lvl%") != std::string::npos) throw CLevelParserException("TODO: Param "+m_name+" does not yet support %lvl%! :("); - return CPathManager::InjectLevelDir(path, defaultDir); + return GetLine()->GetLevel()->InjectLevelPaths(path, defaultDir); } std::string CLevelParserParam::AsPath(const std::string defaultDir) @@ -241,7 +240,7 @@ std::string CLevelParserParam::AsPath(const std::string defaultDir) std::string CLevelParserParam::AsPath(const std::string defaultDir, std::string def) { if (m_empty) - return CPathManager::InjectLevelDir(def, defaultDir); + return GetLine()->GetLevel()->InjectLevelPaths(def, defaultDir); return ToPath(AsString(def), defaultDir); } diff --git a/src/object/robotmain.cpp b/src/object/robotmain.cpp index cfc8d69d..83a74857 100644 --- a/src/object/robotmain.cpp +++ b/src/object/robotmain.cpp @@ -3816,7 +3816,7 @@ void CRobotMain::CreateScene(bool soluce, bool fixScene, bool resetObject) if (!m_sceneReadPath.empty()) continue; // ignore errors when loading saved game (TODO: don't report ones that are just not loaded when loading saved game) if (resetObject) continue; // ignore when reseting just objects (TODO: see above) - throw CLevelParserException("Unknown command: '" + line->GetCommand() + "' in " + line->GetLevel()->GetFilename() + ":" + boost::lexical_cast(line->GetLineNumber())); + throw CLevelParserException("Unknown command: '" + line->GetCommand() + "' in " + line->GetLevelFilename() + ":" + boost::lexical_cast(line->GetLineNumber())); } if (m_sceneReadPath.empty()) diff --git a/src/script/scriptfunc.cpp b/src/script/scriptfunc.cpp index 655201b9..9fd30065 100644 --- a/src/script/scriptfunc.cpp +++ b/src/script/scriptfunc.cpp @@ -25,7 +25,6 @@ #include "common/logger.h" #include "common/make_unique.h" -#include "common/pathman.h" #include "common/resources/inputstream.h" #include "common/resources/outputstream.h" @@ -46,6 +45,8 @@ #include "object/auto/autobase.h" #include "object/auto/autofactory.h" +#include "object/level/parser.h" + #include "object/motion/motionvehicle.h" #include "object/subclass/exchange_post.h" @@ -1614,7 +1615,7 @@ bool CScriptFunctions::rProduce(CBotVar* var, CBotVar* result, int& exception, v if (name[0] != 0) { - std::string name2 = CPathManager::InjectLevelDir(name, "ai"); + std::string name2 = InjectLevelPathsForCurrentLevel(name, "ai"); if (object->Implements(ObjectInterfaceType::Programmable)) { CBrain* brain = dynamic_cast(object)->GetBrain(); diff --git a/src/ui/edit.cpp b/src/ui/edit.cpp index fe4468a7..a3f1d357 100644 --- a/src/ui/edit.cpp +++ b/src/ui/edit.cpp @@ -27,11 +27,12 @@ #include "common/logger.h" #include "common/misc.h" -#include "common/pathman.h" #include "common/resources/inputstream.h" #include "common/resources/outputstream.h" +#include "object/level/parser.h" + #include "object/robotmain.h" #include "ui/scroll.h" @@ -788,7 +789,7 @@ void CEdit::HyperJump(std::string name, std::string marker) sMarker = marker; filename = name + std::string(".txt"); - filename = CPathManager::InjectLevelDir(filename, "help/%lng%"); + filename = InjectLevelPathsForCurrentLevel(filename, "help/%lng%"); boost::replace_all(filename, "\\", "/"); //TODO: Fix this in files if ( ReadText(filename) ) @@ -1144,7 +1145,7 @@ void CEdit::DrawImage(Math::Point pos, std::string name, float width, std::string filename; filename = name + ".png"; - filename = CPathManager::InjectLevelDir(filename, "icons"); + filename = InjectLevelPathsForCurrentLevel(filename, "icons"); boost::replace_all(filename, "\\", "/"); //TODO: Fix this in files m_engine->SetTexture(filename); @@ -1434,7 +1435,7 @@ void CEdit::LoadImage(std::string name) { std::string filename; filename = name + ".png"; - filename = CPathManager::InjectLevelDir(filename, "icons"); + filename = InjectLevelPathsForCurrentLevel(filename, "icons"); boost::replace_all(filename, "\\", "/"); //TODO: Fix this in files m_engine->LoadTexture(filename); } @@ -3221,4 +3222,3 @@ void CEdit::UpdateScroll() } } -