From 16795e0d49ba8b65f6bc7d6e74dc33c27561f365 Mon Sep 17 00:00:00 2001 From: MrSimbax Date: Wed, 22 Jul 2020 21:40:13 +0200 Subject: [PATCH] Use level parser instead of JSON for manifest --- src/CMakeLists.txt | 2 - src/app/moddata.cpp | 114 ------------------------------ src/app/moddata.h | 36 ---------- src/app/modman.cpp | 75 +++++++++++++++++++- src/app/modman.h | 14 +++- src/level/parser/parser.cpp | 24 +++++-- src/ui/screen/screen_mod_list.cpp | 33 +++------ src/ui/screen/screen_mod_list.h | 2 - 8 files changed, 116 insertions(+), 184 deletions(-) delete mode 100644 src/app/moddata.cpp delete mode 100644 src/app/moddata.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f00e77a6..df752615 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -145,8 +145,6 @@ set(BASE_SOURCES app/controller.h app/input.cpp app/input.h - app/moddata.cpp - app/moddata.h app/modman.cpp app/modman.h app/pathman.cpp diff --git a/src/app/moddata.cpp b/src/app/moddata.cpp deleted file mode 100644 index 43ed4368..00000000 --- a/src/app/moddata.cpp +++ /dev/null @@ -1,114 +0,0 @@ -/* - * This file is part of the Colobot: Gold Edition source code - * Copyright (C) 2001-2020, Daniel Roux, EPSITEC SA & TerranovaTeam - * http://epsitec.ch; http://colobot.info; http://github.com/colobot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://gnu.org/licenses - */ - -#include "app/moddata.h" - -#include "common/logger.h" -#include "common/make_unique.h" - -#include "common/resources/inputstream.h" -#include "common/resources/resourcemanager.h" - -#include -#include -#include - -namespace pt = boost::property_tree; - -boost::optional LoadManifest(const std::string& path); -std::string GetStringProperty(const pt::ptree& manifest, const std::string& key); -std::map GetLanguageStringProperty(const pt::ptree& manifest, const std::string& key); - -ModData LoadModData(const std::string& path) -{ - ModData modData{}; - - auto manifestOptional = LoadManifest(path); - if (!manifestOptional) - { - return modData; - } - auto manifest = manifestOptional.get(); - - modData.displayName = GetLanguageStringProperty(manifest, "DisplayName"); - modData.version = GetStringProperty(manifest, "Version"); - modData.author = GetStringProperty(manifest, "Author"); - modData.website = GetStringProperty(manifest, "Website"); - modData.summary = GetLanguageStringProperty(manifest, "Summary"); - - return modData; -} - -boost::optional LoadManifest(const std::string& path) -{ - try - { - auto inputStream = MakeUnique("manifest.json"); - if (!inputStream->is_open()) - { - GetLogger()->Error("Error on parsing the manifest file %s: failed to open file\n"); - return {}; - } - - pt::ptree manifest{}; - boost::property_tree::json_parser::read_json(*inputStream, manifest); - - return manifest; - } - catch (std::exception& e) - { - GetLogger()->Warn("Error on parsing the manifest file %s: %s\n", path.c_str(), e.what()); - return {}; - } - return {}; -} - -std::string GetStringProperty(const pt::ptree& manifest, const std::string& key) -{ - auto prop = manifest.get_optional(key); - if (prop) - { - return prop.get(); - } - return {}; -} - -std::map GetLanguageStringProperty(const pt::ptree& manifest, const std::string& key) -{ - std::map ret; - auto prop = manifest.get_child_optional(key); - if (prop) - { - for (const auto& child : prop.get()) - { - Language language = LANGUAGE_ENGLISH; - std::string strLanguage = child.first.data(); - if (!ParseLanguage(strLanguage, language)) - { - GetLogger()->Warn("Error on parsing the manifest file: %s language %s is not a valid language\n", key.c_str(), strLanguage.c_str()); - continue; - } - else - { - ret.insert(std::make_pair(language, child.second.data())); - } - } - } - return ret; -} diff --git a/src/app/moddata.h b/src/app/moddata.h deleted file mode 100644 index 25484e8e..00000000 --- a/src/app/moddata.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * This file is part of the Colobot: Gold Edition source code - * Copyright (C) 2001-2020, Daniel Roux, EPSITEC SA & TerranovaTeam - * http://epsitec.ch; http://colobot.info; http://github.com/colobot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://gnu.org/licenses - */ - -#pragma once - -#include "common/language.h" - -#include - -struct ModData -{ - std::map displayName{}; - std::string author{}; - std::string version{}; - std::string website{}; - std::map summary{}; -}; - -//! Loads the metadata for a mod in the given path. -ModData LoadModData(const std::string& path); diff --git a/src/app/modman.cpp b/src/app/modman.cpp index a8dcf7d3..d129271f 100644 --- a/src/app/modman.cpp +++ b/src/app/modman.cpp @@ -29,6 +29,8 @@ #include "common/resources/resourcemanager.h" +#include "level/parser/parser.h" + #include #include #include @@ -107,7 +109,7 @@ void CModManager::Init() for (auto& mod : m_mods) { m_pathManager->AddMod(mod.path); - mod.data = LoadModData(mod.path); + LoadModData(mod); m_pathManager->RemoveMod(mod.path); } @@ -207,3 +209,74 @@ const std::vector& CModManager::GetMods() const { return m_mods; } + +void CModManager::LoadModData(Mod& mod) +{ + auto& data = mod.data; + + data.displayName = mod.name; + + try + { + CLevelParser levelParser("manifest.txt"); + if (levelParser.Exists()) + { + levelParser.Load(); + + CLevelParserLine* line = nullptr; + + // DisplayName + line = levelParser.GetIfDefined("DisplayName"); + if (line != nullptr && line->GetParam("text")->IsDefined()) + { + data.displayName = line->GetParam("text")->AsString(); + } + + // Author + line = levelParser.GetIfDefined("Author"); + if (line != nullptr && line->GetParam("text")->IsDefined()) + { + data.author = line->GetParam("text")->AsString(); + } + + // Version + line = levelParser.GetIfDefined("Version"); + if (line != nullptr) + { + if (line->GetParam("text")->IsDefined()) + { + data.version = line->GetParam("text")->AsString(); + } + else if (line->GetParam("major")->IsDefined() && line->GetParam("minor")->IsDefined() && line->GetParam("patch")->IsDefined()) + { + auto major = boost::lexical_cast(line->GetParam("major")->AsInt()); + auto minor = boost::lexical_cast(line->GetParam("minor")->AsInt()); + auto patch = boost::lexical_cast(line->GetParam("patch")->AsInt()); + data.version = boost::algorithm::join(std::vector{ major, minor, patch }, ","); + } + } + + // Website + line = levelParser.GetIfDefined("Website"); + if (line != nullptr && line->GetParam("text")->IsDefined()) + { + data.website = line->GetParam("text")->AsString(); + } + + // Summary + line = levelParser.GetIfDefined("Summary"); + if (line != nullptr && line->GetParam("text")->IsDefined()) + { + data.summary = line->GetParam("text")->AsString(); + } + } + else + { + GetLogger()->Warn("No manifest file for mod %s\n", mod.name.c_str()); + } + } + catch (CLevelParserException& e) + { + GetLogger()->Warn("Failed parsing manifest for mod %s: %s\n", mod.name.c_str(), e.what()); + } +} diff --git a/src/app/modman.h b/src/app/modman.h index bd66117e..57c54524 100644 --- a/src/app/modman.h +++ b/src/app/modman.h @@ -19,14 +19,21 @@ #pragma once -#include "app/moddata.h" - #include #include class CApplication; class CPathManager; +struct ModData +{ + std::string displayName{}; + std::string author{}; + std::string version{}; + std::string website{}; + std::string summary{}; +}; + struct Mod { std::string name{}; @@ -90,6 +97,9 @@ private: //! Reloads application resources so the enabled mods are applied void ReloadResources(); + //! Load mod data into mod + void LoadModData(Mod& mod); + private: CApplication* m_app; CPathManager* m_pathManager; diff --git a/src/level/parser/parser.cpp b/src/level/parser/parser.cpp index e8f871bd..1bf78a24 100644 --- a/src/level/parser/parser.cpp +++ b/src/level/parser/parser.cpp @@ -41,6 +41,7 @@ #include #include #include +#include CLevelParser::CLevelParser() { @@ -172,13 +173,28 @@ void CLevelParser::Load() boost::replace_all(line, "\t", " "); // replace tab by space // ignore comments - std::size_t comment = line.find("//"); - if (comment != std::string::npos) - line = line.substr(0, comment); + size_t pos = 0; + std::string linesuffix = line; + boost::regex commentRegex{ R"(("[^"]*")|('[^']*')|(//.*$))" }; + boost::smatch matches; + while (boost::regex_search(linesuffix, matches, commentRegex)) + { + if (matches[3].matched) + { + pos += std::distance(linesuffix.cbegin(), matches.prefix().second); + line = line.substr(0, pos); + linesuffix = ""; + } + else + { + pos += std::distance(linesuffix.cbegin(), matches.suffix().first); + linesuffix = matches.suffix().str(); + } + } boost::algorithm::trim(line); - std::size_t pos = line.find_first_of(" \t\n"); + pos = line.find_first_of(" \t\n"); std::string command = line.substr(0, pos); if (pos != std::string::npos) { diff --git a/src/ui/screen/screen_mod_list.cpp b/src/ui/screen/screen_mod_list.cpp index 95d42c57..8717ed57 100644 --- a/src/ui/screen/screen_mod_list.cpp +++ b/src/ui/screen/screen_mod_list.cpp @@ -380,7 +380,7 @@ void CScreenModList::UpdateModList() for (size_t i = 0; i < mods.size(); ++i) { const auto& mod = mods[i]; - auto name = GetLanguageStringProperty(mod.data.displayName, mod.name); + auto name = mod.data.displayName; pl->SetItemName(i, name); pl->SetCheck(i, mod.enabled); pl->SetEnable(i, true); @@ -409,8 +409,7 @@ void CScreenModList::UpdateModDetails() const auto& mod = m_modManager->GetMod(m_modSelectedIndex); const auto data = mod.data; - auto name = GetLanguageStringProperty(data.displayName, mod.name); - details += "\\b;" + name + '\n'; + details += "\\b;" + data.displayName + '\n'; std::string authorFieldName; GetResource(RES_TEXT, RT_MOD_AUTHOR_FIELD_NAME, authorFieldName); @@ -465,7 +464,14 @@ void CScreenModList::UpdateModSummary() const auto& mod = m_modManager->GetMod(m_modSelectedIndex); - pe->SetText(GetLanguageStringProperty(mod.data.summary, noSummary)); + if (!mod.data.summary.empty()) + { + pe->SetText(mod.data.summary); + } + else + { + pe->SetText(noSummary); + } } void CScreenModList::UpdateEnableDisableButton() @@ -564,23 +570,4 @@ void CScreenModList::UpdateUpDownButtons() } } -std::string CScreenModList::GetLanguageStringProperty(const std::map& property, const std::string& fallback) -{ - std::string ret{}; - const auto language = m_app->GetLanguage(); - if (property.count(language) > 0) - { - ret = property.at(language); - } - else if (property.count(LANGUAGE_ENGLISH) > 0) - { - ret = property.at(LANGUAGE_ENGLISH); - } - else - { - ret = fallback; - } - return ret; -} - } // namespace Ui diff --git a/src/ui/screen/screen_mod_list.h b/src/ui/screen/screen_mod_list.h index 0da3a7c3..639fd775 100644 --- a/src/ui/screen/screen_mod_list.h +++ b/src/ui/screen/screen_mod_list.h @@ -81,8 +81,6 @@ protected: void UpdateApplyButton(); void UpdateUpDownButtons(); - std::string GetLanguageStringProperty(const std::map& property, const std::string& fallback); - protected: Ui::CMainDialog* m_dialog;