Use level parser instead of JSON for manifest

pyro-refactor
MrSimbax 2020-07-22 21:40:13 +02:00
parent 62b770f7d3
commit 16795e0d49
8 changed files with 116 additions and 184 deletions

View File

@ -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

View File

@ -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 <boost/optional.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <istream>
namespace pt = boost::property_tree;
boost::optional<pt::ptree> LoadManifest(const std::string& path);
std::string GetStringProperty(const pt::ptree& manifest, const std::string& key);
std::map<Language, std::string> 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<pt::ptree> LoadManifest(const std::string& path)
{
try
{
auto inputStream = MakeUnique<CInputStream>("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<std::string>(key);
if (prop)
{
return prop.get();
}
return {};
}
std::map<Language, std::string> GetLanguageStringProperty(const pt::ptree& manifest, const std::string& key)
{
std::map<Language, std::string> 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;
}

View File

@ -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 <map>
struct ModData
{
std::map<Language, std::string> displayName{};
std::string author{};
std::string version{};
std::string website{};
std::map<Language, std::string> summary{};
};
//! Loads the metadata for a mod in the given path.
ModData LoadModData(const std::string& path);

View File

@ -29,6 +29,8 @@
#include "common/resources/resourcemanager.h"
#include "level/parser/parser.h"
#include <algorithm>
#include <map>
#include <boost/filesystem.hpp>
@ -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<Mod>& 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<std::string>(line->GetParam("major")->AsInt());
auto minor = boost::lexical_cast<std::string>(line->GetParam("minor")->AsInt());
auto patch = boost::lexical_cast<std::string>(line->GetParam("patch")->AsInt());
data.version = boost::algorithm::join(std::vector<std::string>{ 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());
}
}

View File

@ -19,14 +19,21 @@
#pragma once
#include "app/moddata.h"
#include <unordered_map>
#include <vector>
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;

View File

@ -41,6 +41,7 @@
#include <boost/algorithm/string/trim.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/regex.hpp>
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)
{

View File

@ -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<Language, std::string>& 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

View File

@ -81,8 +81,6 @@ protected:
void UpdateApplyButton();
void UpdateUpDownButtons();
std::string GetLanguageStringProperty(const std::map<Language, std::string>& property, const std::string& fallback);
protected:
Ui::CMainDialog* m_dialog;