Use level parser instead of JSON for manifest
parent
62b770f7d3
commit
16795e0d49
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Loading…
Reference in New Issue