Add handling of mods manifests

Also add Polish translations for mod manager related strings.
pyro-refactor
MrSimbax 2020-07-22 16:18:18 +02:00
parent eac74c23ec
commit f57da76ae8
19 changed files with 468 additions and 84 deletions

View File

@ -189,7 +189,7 @@ elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
set(NORMAL_CXX_FLAGS "/wd\"4244\" /wd\"4309\" /wd\"4800\" /wd\"4996\" /wd\"4351\" /EHsc") # disable some useless warnings set(NORMAL_CXX_FLAGS "/wd\"4244\" /wd\"4309\" /wd\"4800\" /wd\"4996\" /wd\"4351\" /EHsc") # disable some useless warnings
if(MSVC_STATIC) if(MSVC_STATIC)
set(RELEASE_CXX_FLAGS "/MT /Ox") set(RELEASE_CXX_FLAGS "/MT /Ox")
set(DEBUG_CXX_FLAGS "/MTd /ZI") set(DEBUG_CXX_FLAGS "/MTd /Od /ZI")
else(MSVC_STATIC) else(MSVC_STATIC)
set(RELEASE_CXX_FLAGS "/MD /Ox") set(RELEASE_CXX_FLAGS "/MD /Ox")
set(DEBUG_CXX_FLAGS "/MDd /Od /ZI") set(DEBUG_CXX_FLAGS "/MDd /Od /ZI")

View File

@ -123,21 +123,6 @@ msgstr ""
msgid "2) Then press the key you want to use instead." msgid "2) Then press the key you want to use instead."
msgstr "" msgstr ""
msgid "Mods:"
msgstr ""
msgid "Information:"
msgstr ""
msgid "Description:"
msgstr ""
msgid "Enable\\Enable the selected mod"
msgstr ""
msgid "Disable\\Disable the selected mod"
msgstr ""
msgid "Face type:" msgid "Face type:"
msgstr "" msgstr ""
@ -315,6 +300,36 @@ msgstr ""
msgid "%s: %d pts" msgid "%s: %d pts"
msgstr "" msgstr ""
msgid "Mods:"
msgstr ""
msgid "Information:"
msgstr ""
msgid "Description:"
msgstr ""
msgid "Enable\\Enable the selected mod"
msgstr ""
msgid "Disable\\Disable the selected mod"
msgstr ""
msgid "Unknown author"
msgstr ""
msgid "by"
msgstr ""
msgid "Version"
msgstr ""
msgid "Website"
msgstr ""
msgid "No description."
msgstr ""
msgid "Code battle" msgid "Code battle"
msgstr "" msgstr ""

View File

@ -988,6 +988,10 @@ msgstr "Další objekt\\Vybere následující objekt"
msgid "No" msgid "No"
msgstr "Ne" msgstr "Ne"
#, fuzzy
msgid "No description."
msgstr "Rozlišení:"
msgid "No energy in the subsoil" msgid "No energy in the subsoil"
msgstr "Pod povrchem není zdroj energie" msgstr "Pod povrchem není zdroj energie"
@ -1735,6 +1739,10 @@ msgstr "Jednotka"
msgid "Unknown Object" msgid "Unknown Object"
msgstr "Neznámý objekt" msgstr "Neznámý objekt"
#, fuzzy
msgid "Unknown author"
msgstr "Neznámá funkce"
msgid "Unknown command" msgid "Unknown command"
msgstr "Neznámý příkaz" msgstr "Neznámý příkaz"
@ -1771,6 +1779,9 @@ msgstr "Proměnná nebyla nastavena"
msgid "Vault" msgid "Vault"
msgstr "Trezor" msgstr "Trezor"
msgid "Version"
msgstr ""
msgid "Vertical Synchronization\\Limits the number of frames per second to display frequency" msgid "Vertical Synchronization\\Limits the number of frames per second to display frequency"
msgstr "" msgstr ""
@ -1789,6 +1800,9 @@ msgstr "Vosa byla smrtelně raněna"
msgid "Waste" msgid "Waste"
msgstr "Odpad" msgstr "Odpad"
msgid "Website"
msgstr ""
msgid "Wheeled builder" msgid "Wheeled builder"
msgstr "" msgstr ""
@ -1975,6 +1989,9 @@ msgstr "\\Fialové vlajky"
msgid "\\Yellow flags" msgid "\\Yellow flags"
msgstr "\\Žluté vlajky" msgstr "\\Žluté vlajky"
msgid "by"
msgstr ""
msgid "colobot.info" msgid "colobot.info"
msgstr "colobot.info" msgstr "colobot.info"

View File

@ -1004,6 +1004,10 @@ msgstr "Nächstes auswählen\\Nächstes Objekt auswählen"
msgid "No" msgid "No"
msgstr "Nein" msgstr "Nein"
#, fuzzy
msgid "No description."
msgstr "Auflösung:"
msgid "No energy in the subsoil" msgid "No energy in the subsoil"
msgstr "Kein unterirdisches Energievorkommen" msgstr "Kein unterirdisches Energievorkommen"
@ -1752,6 +1756,10 @@ msgstr "Einheit"
msgid "Unknown Object" msgid "Unknown Object"
msgstr "Das Objekt existiert nicht" msgstr "Das Objekt existiert nicht"
#, fuzzy
msgid "Unknown author"
msgstr "Unbekannte Funktion"
msgid "Unknown command" msgid "Unknown command"
msgstr "Befehl unbekannt" msgstr "Befehl unbekannt"
@ -1788,6 +1796,9 @@ msgstr "Der Wert dieser Variable wurde nicht definiert"
msgid "Vault" msgid "Vault"
msgstr "Bunker" msgstr "Bunker"
msgid "Version"
msgstr ""
msgid "Vertical Synchronization\\Limits the number of frames per second to display frequency" msgid "Vertical Synchronization\\Limits the number of frames per second to display frequency"
msgstr "" msgstr ""
@ -1806,6 +1817,9 @@ msgstr "Wespe tödlich verwundet"
msgid "Waste" msgid "Waste"
msgstr "Abfall" msgstr "Abfall"
msgid "Website"
msgstr ""
msgid "Wheeled builder" msgid "Wheeled builder"
msgstr "" msgstr ""
@ -1990,6 +2004,9 @@ msgstr "\\Violette Fahne"
msgid "\\Yellow flags" msgid "\\Yellow flags"
msgstr "\\Gelbe Fahne" msgstr "\\Gelbe Fahne"
msgid "by"
msgstr ""
msgid "colobot.info" msgid "colobot.info"
msgstr "colobot.info" msgstr "colobot.info"

View File

@ -1006,6 +1006,10 @@ msgstr "Sélectionner l'objet suivant\\Sélectionner l'objet suivant"
msgid "No" msgid "No"
msgstr "Non" msgstr "Non"
#, fuzzy
msgid "No description."
msgstr "Résolutions :"
msgid "No energy in the subsoil" msgid "No energy in the subsoil"
msgstr "Pas d'énergie en sous-sol" msgstr "Pas d'énergie en sous-sol"
@ -1754,6 +1758,10 @@ msgstr "Unité"
msgid "Unknown Object" msgid "Unknown Object"
msgstr "Objet inconnu" msgstr "Objet inconnu"
#, fuzzy
msgid "Unknown author"
msgstr "Routine inconnue"
msgid "Unknown command" msgid "Unknown command"
msgstr "Commande inconnue" msgstr "Commande inconnue"
@ -1790,6 +1798,9 @@ msgstr "Variable non initialisée"
msgid "Vault" msgid "Vault"
msgstr "Coffre-fort" msgstr "Coffre-fort"
msgid "Version"
msgstr ""
msgid "Vertical Synchronization\\Limits the number of frames per second to display frequency" msgid "Vertical Synchronization\\Limits the number of frames per second to display frequency"
msgstr "Synchronisation verticale :\\Réduit la fréquence d'images par seconde à afficher." msgstr "Synchronisation verticale :\\Réduit la fréquence d'images par seconde à afficher."
@ -1808,6 +1819,9 @@ msgstr "Guêpe mortellement touchée"
msgid "Waste" msgid "Waste"
msgstr "Déchet" msgstr "Déchet"
msgid "Website"
msgstr ""
msgid "Wheeled builder" msgid "Wheeled builder"
msgstr "" msgstr ""
@ -1992,6 +2006,9 @@ msgstr "\\Drapeaux violets"
msgid "\\Yellow flags" msgid "\\Yellow flags"
msgstr "\\Drapeaux jaunes" msgstr "\\Drapeaux jaunes"
msgid "by"
msgstr ""
msgid "colobot.info" msgid "colobot.info"
msgstr "colobot.info" msgstr "colobot.info"

View File

@ -125,7 +125,7 @@ msgid "Apply changes\\Activates the changed settings"
msgstr "Zastosuj zmiany\\Aktywuje zmienione ustawienia" msgstr "Zastosuj zmiany\\Aktywuje zmienione ustawienia"
msgid "Apply\\Apply the current mod configuration" msgid "Apply\\Apply the current mod configuration"
msgstr "" msgstr "Zastosuj\\Zastosuj obecną konfigurację modów"
msgid "Appropriate constructor missing" msgid "Appropriate constructor missing"
msgstr "Brak odpowiedniego konstruktora" msgstr "Brak odpowiedniego konstruktora"
@ -481,9 +481,8 @@ msgstr "Kopalnia"
msgid "Descend\\Reduces the power of the jet" msgid "Descend\\Reduces the power of the jet"
msgstr "W dół\\Zmniejsza moc silnika" msgstr "W dół\\Zmniejsza moc silnika"
#, fuzzy
msgid "Description:" msgid "Description:"
msgstr "Rozdzielczość:" msgstr "Opis:"
msgid "Destroy" msgid "Destroy"
msgstr "Zniszcz" msgstr "Zniszcz"
@ -497,9 +496,8 @@ msgstr "Destroyer"
msgid "Device\\Driver and resolution settings" msgid "Device\\Driver and resolution settings"
msgstr "Urządzenie\\Ustawienia sterownika i rozdzielczości" msgstr "Urządzenie\\Ustawienia sterownika i rozdzielczości"
#, fuzzy
msgid "Disable\\Disable the selected mod" msgid "Disable\\Disable the selected mod"
msgstr "Usuń\\Usuwa zaznaczony plik" msgstr "Wyłącz\\Wyłącza zaznaczonego moda"
msgid "Dividing by zero" msgid "Dividing by zero"
msgstr "Dzielenie przez zero" msgstr "Dzielenie przez zero"
@ -518,7 +516,7 @@ msgid "Down (\\key gdown;)"
msgstr "Dół (\\key gdown;)" msgstr "Dół (\\key gdown;)"
msgid "Down\\Move the selected mod down so it's loaded later (mods may overwrite files from the mods above them)" msgid "Down\\Move the selected mod down so it's loaded later (mods may overwrite files from the mods above them)"
msgstr "" msgstr "W dół\\Przenieś zaznaczonego moda w dół, aby był załadowany później (mody mogą nadpisywać pliki modów wyżej)"
msgid "Drawer bot" msgid "Drawer bot"
msgstr "Robot rysownik" msgstr "Robot rysownik"
@ -547,9 +545,8 @@ msgstr "Jajo"
msgid "Empty character constant" msgid "Empty character constant"
msgstr "" msgstr ""
#, fuzzy
msgid "Enable\\Enable the selected mod" msgid "Enable\\Enable the selected mod"
msgstr "Wczytaj\\Wczytuje zaznaczoną misję" msgstr "Włącz\\Włącza zaznaczonego moda"
msgid "End of block missing" msgid "End of block missing"
msgstr "Brak końca bloku" msgstr "Brak końca bloku"
@ -778,7 +775,7 @@ msgid "Information exchange post"
msgstr "Stacja przekaźnikowa informacji" msgstr "Stacja przekaźnikowa informacji"
msgid "Information:" msgid "Information:"
msgstr "" msgstr "Informacje:"
msgid "Instruction \"break\" outside a loop" msgid "Instruction \"break\" outside a loop"
msgstr "Polecenie \"break\" na zewnątrz pętli" msgstr "Polecenie \"break\" na zewnątrz pętli"
@ -931,13 +928,13 @@ msgid "Missions\\Select mission"
msgstr "Misje\\Wybierz misję" msgstr "Misje\\Wybierz misję"
msgid "Mods" msgid "Mods"
msgstr "" msgstr "Mody"
msgid "Mods:" msgid "Mods:"
msgstr "" msgstr "Mody:"
msgid "Mods\\Mod manager" msgid "Mods\\Mod manager"
msgstr "" msgstr "Mody\\Zarządzanie modami"
msgid "Mouse inversion X\\Inversion of the scrolling direction on the X axis" msgid "Mouse inversion X\\Inversion of the scrolling direction on the X axis"
msgstr "Odwrócenie myszy X\\Odwrócenie kierunków przewijania w poziomie" msgstr "Odwrócenie myszy X\\Odwrócenie kierunków przewijania w poziomie"
@ -987,6 +984,9 @@ msgstr "Następny obiekt\\Zaznacza następny obiekt"
msgid "No" msgid "No"
msgstr "Nie" msgstr "Nie"
msgid "No description."
msgstr "Brak opisu."
msgid "No energy in the subsoil" msgid "No energy in the subsoil"
msgstr "Brak energii w ziemi" msgstr "Brak energii w ziemi"
@ -1108,7 +1108,7 @@ msgid "Open (Ctrl+O)"
msgstr "Otwórz (Ctrl+O)" msgstr "Otwórz (Ctrl+O)"
msgid "Open Directory\\Open the mods directory" msgid "Open Directory\\Open the mods directory"
msgstr "" msgstr "Otwórz katalog\\Otwórz katalog z modami"
msgid "Opening brace missing" msgid "Opening brace missing"
msgstr "Brak klamry otwierającej" msgstr "Brak klamry otwierającej"
@ -1321,7 +1321,7 @@ msgid "Reflections on the buttons \\Shiny buttons"
msgstr "Odbicia na przyciskach \\Świecące przyciski" msgstr "Odbicia na przyciskach \\Świecące przyciski"
msgid "Refresh\\Refresh the list of currently installed mods" msgid "Refresh\\Refresh the list of currently installed mods"
msgstr "" msgstr "Odśwież\\Odśwież listę obecnie zainstalowanych modów"
msgid "Remains of Apollo mission" msgid "Remains of Apollo mission"
msgstr "Pozostałości z misji Apollo" msgstr "Pozostałości z misji Apollo"
@ -1608,7 +1608,7 @@ msgid "The types of the two operands are incompatible"
msgstr "Niezgodne typy operatorów" msgstr "Niezgodne typy operatorów"
msgid "There are unsaved changes. Do you want to save them before leaving?" msgid "There are unsaved changes. Do you want to save them before leaving?"
msgstr "" msgstr "Są niezapisane zmiany. Czy chcesz je zapisać przed wyjściem?"
msgid "This class already exists" msgid "This class already exists"
msgstr "Taka klasa już istnieje" msgstr "Taka klasa już istnieje"
@ -1734,6 +1734,9 @@ msgstr "Jednostka"
msgid "Unknown Object" msgid "Unknown Object"
msgstr "Obiekt nieznany" msgstr "Obiekt nieznany"
msgid "Unknown author"
msgstr "Nieznany autor"
msgid "Unknown command" msgid "Unknown command"
msgstr "Nieznane polecenie" msgstr "Nieznane polecenie"
@ -1747,7 +1750,7 @@ msgid "Up (\\key gup;)"
msgstr "Góra (\\key gup;)" msgstr "Góra (\\key gup;)"
msgid "Up\\Move the selected mod up so it's loaded sooner (mods may overwrite files from the mods above them)" msgid "Up\\Move the selected mod up so it's loaded sooner (mods may overwrite files from the mods above them)"
msgstr "" msgstr "W górę\\Przenieś zaznaczonego moda w górę, aby był załadowany wcześniej (mody mogą nadpisywać pliki modów wyżej)"
msgid "Uranium deposit (site for derrick)" msgid "Uranium deposit (site for derrick)"
msgstr "Złoże uranu (miejsce na kopalnię)" msgstr "Złoże uranu (miejsce na kopalnię)"
@ -1770,6 +1773,9 @@ msgstr "Zmienna nie została zainicjalizowana"
msgid "Vault" msgid "Vault"
msgstr "Skrytka" msgstr "Skrytka"
msgid "Version"
msgstr "Wersja"
msgid "Vertical Synchronization\\Limits the number of frames per second to display frequency" msgid "Vertical Synchronization\\Limits the number of frames per second to display frequency"
msgstr "Synchronizacja pionowa\\Ogranicza ilość klatek na sekundę do wartości odświeżania ekranu" msgstr "Synchronizacja pionowa\\Ogranicza ilość klatek na sekundę do wartości odświeżania ekranu"
@ -1788,6 +1794,9 @@ msgstr "Osa śmiertelnie raniona"
msgid "Waste" msgid "Waste"
msgstr "Odpady" msgstr "Odpady"
msgid "Website"
msgstr "Strona internetowa"
msgid "Wheeled builder" msgid "Wheeled builder"
msgstr "" msgstr ""
@ -1822,7 +1831,7 @@ msgid "Withdraw shield (\\key action;)"
msgstr "Wyłącz osłonę (\\key action;)" msgstr "Wyłącz osłonę (\\key action;)"
msgid "Workshop\\Open the workshop to search for mods" msgid "Workshop\\Open the workshop to search for mods"
msgstr "" msgstr "Warsztat\\Otwórz warsztat, aby poszukać modów"
msgid "Worm" msgid "Worm"
msgstr "Robal" msgstr "Robal"
@ -1974,6 +1983,9 @@ msgstr "\\Fioletowe flagi"
msgid "\\Yellow flags" msgid "\\Yellow flags"
msgstr "\\Żółte flagi" msgstr "\\Żółte flagi"
msgid "by"
msgstr "autorstwa"
msgid "colobot.info" msgid "colobot.info"
msgstr "colobot.info" msgstr "colobot.info"

View File

@ -1001,6 +1001,10 @@ msgstr "Próximo objeto\\Selecionar o próximo objeto"
msgid "No" msgid "No"
msgstr "Não" msgstr "Não"
#, fuzzy
msgid "No description."
msgstr "Resolução:"
msgid "No energy in the subsoil" msgid "No energy in the subsoil"
msgstr "Nenhuma energia no subsolo" msgstr "Nenhuma energia no subsolo"
@ -1749,6 +1753,10 @@ msgstr "Unidade"
msgid "Unknown Object" msgid "Unknown Object"
msgstr "Objeto desconhecido" msgstr "Objeto desconhecido"
#, fuzzy
msgid "Unknown author"
msgstr "Função desconhecida"
msgid "Unknown command" msgid "Unknown command"
msgstr "Comando desconhecido" msgstr "Comando desconhecido"
@ -1785,6 +1793,9 @@ msgstr "Variável não inicializada"
msgid "Vault" msgid "Vault"
msgstr "Cofre" msgstr "Cofre"
msgid "Version"
msgstr ""
msgid "Vertical Synchronization\\Limits the number of frames per second to display frequency" msgid "Vertical Synchronization\\Limits the number of frames per second to display frequency"
msgstr "" msgstr ""
@ -1803,6 +1814,9 @@ msgstr "Vespa fatalmente ferida"
msgid "Waste" msgid "Waste"
msgstr "Desperdício" msgstr "Desperdício"
msgid "Website"
msgstr ""
msgid "Wheeled builder" msgid "Wheeled builder"
msgstr "" msgstr ""
@ -1987,6 +2001,9 @@ msgstr "\\Bandeiras violetas"
msgid "\\Yellow flags" msgid "\\Yellow flags"
msgstr "\\Bandeiras amarelas" msgstr "\\Bandeiras amarelas"
msgid "by"
msgstr ""
msgid "colobot.info" msgid "colobot.info"
msgstr "colobot.info" msgstr "colobot.info"

View File

@ -1012,6 +1012,10 @@ msgstr "Следующий объект\\Выбор следующего объ
msgid "No" msgid "No"
msgstr "Нет" msgstr "Нет"
#, fuzzy
msgid "No description."
msgstr "Разрешение:"
msgid "No energy in the subsoil" msgid "No energy in the subsoil"
msgstr "Под землей нет запасов энергии" msgstr "Под землей нет запасов энергии"
@ -1765,6 +1769,10 @@ msgstr "Юнит"
msgid "Unknown Object" msgid "Unknown Object"
msgstr "Неизвестный объект" msgstr "Неизвестный объект"
#, fuzzy
msgid "Unknown author"
msgstr "Неизвестная функция"
msgid "Unknown command" msgid "Unknown command"
msgstr "Неизвестная команда" msgstr "Неизвестная команда"
@ -1801,6 +1809,9 @@ msgstr "Переменная не инициализирована"
msgid "Vault" msgid "Vault"
msgstr "Хранилище" msgstr "Хранилище"
msgid "Version"
msgstr ""
msgid "Vertical Synchronization\\Limits the number of frames per second to display frequency" msgid "Vertical Synchronization\\Limits the number of frames per second to display frequency"
msgstr "" msgstr ""
@ -1819,6 +1830,9 @@ msgstr "Оса смертельно ранена"
msgid "Waste" msgid "Waste"
msgstr "Мусор" msgstr "Мусор"
msgid "Website"
msgstr ""
msgid "Wheeled builder" msgid "Wheeled builder"
msgstr "" msgstr ""
@ -2003,6 +2017,9 @@ msgstr "\\Фиолетовый флаг"
msgid "\\Yellow flags" msgid "\\Yellow flags"
msgstr "\\Желтый флаг" msgstr "\\Желтый флаг"
msgid "by"
msgstr ""
msgid "colobot.info" msgid "colobot.info"
msgstr "colobot.info" msgstr "colobot.info"

View File

@ -145,6 +145,8 @@ set(BASE_SOURCES
app/controller.h app/controller.h
app/input.cpp app/input.cpp
app/input.h app/input.h
app/moddata.cpp
app/moddata.h
app/modman.cpp app/modman.cpp
app/modman.h app/modman.h
app/pathman.cpp app/pathman.cpp

View File

@ -520,9 +520,7 @@ bool CApplication::Create()
GetLogger()->Warn("Config could not be loaded. Default values will be used!\n"); GetLogger()->Warn("Config could not be loaded. Default values will be used!\n");
} }
m_modManager->FindMods(); m_modManager->Init();
m_modManager->SaveMods();
m_modManager->UpdatePaths();
// Create the sound instance. // Create the sound instance.
#ifdef OPENAL_SOUND #ifdef OPENAL_SOUND

114
src/app/moddata.cpp Normal file
View File

@ -0,0 +1,114 @@
/*
* 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/resourcemanager.h"
#include "common/resources/inputstream.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, bool loaded = false);
std::string GetStringProperty(const pt::ptree& manifest, const std::string& key);
std::unordered_map<Language, std::string> GetLanguageStringProperty(const pt::ptree& manifest, const std::string& key);
ModData LoadModData(const std::string& path, bool loaded)
{
ModData modData{};
auto manifestOptional = LoadManifest(path);
if (!manifestOptional.has_value())
{
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, bool loaded)
{
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.has_value())
{
return prop.get();
}
return {};
}
std::unordered_map<Language, std::string> GetLanguageStringProperty(const pt::ptree& manifest, const std::string& key)
{
std::unordered_map<Language, std::string> ret;
auto prop = manifest.get_child_optional(key);
if (prop.has_value())
{
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;
}

36
src/app/moddata.h Normal file
View File

@ -0,0 +1,36 @@
/*
* 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 <unordered_map>
struct ModData
{
std::unordered_map<Language, std::string> displayName{};
std::string author{};
std::string version{};
std::string website{};
std::unordered_map<Language, std::string> summary{};
};
//! Loads the metadata for a mod in the given path.
ModData LoadModData(const std::string& path, bool loaded = false);

View File

@ -42,10 +42,8 @@ CModManager::CModManager(CApplication* app, CPathManager* pathManager)
{ {
} }
void CModManager::FindMods() void CModManager::Init()
{ {
m_mods.clear();
// Load names from the config file // Load names from the config file
std::vector<std::string> savedModNames; std::vector<std::string> savedModNames;
GetConfigFile().GetArrayProperty("Mods", "Names", savedModNames); GetConfigFile().GetArrayProperty("Mods", "Names", savedModNames);
@ -102,6 +100,29 @@ void CModManager::FindMods()
mod.path = newMod.second; mod.path = newMod.second;
m_mods.push_back(mod); m_mods.push_back(mod);
} }
SaveMods();
// Load the metadata for each mod
for (auto& mod : m_mods)
{
m_pathManager->AddMod(mod.path);
mod.data = LoadModData(mod.path);
m_pathManager->RemoveMod(mod.path);
}
UpdatePaths();
}
void CModManager::ReloadMods()
{
m_mods.clear();
m_pathManager->RemoveAllMods();
Init();
// Apply the configuration
ReloadResources();
} }
void CModManager::EnableMod(size_t i) void CModManager::EnableMod(size_t i)

View File

@ -19,13 +19,12 @@
#pragma once #pragma once
#include "ui/maindialog.h" #include "app/moddata.h"
#include "ui/screen/screen_setup.h"
#include <unordered_map> #include <unordered_map>
#include <boost/optional.hpp> #include <boost/optional.hpp>
class CApplication;
class CPathManager; class CPathManager;
struct Mod struct Mod
@ -33,7 +32,7 @@ struct Mod
std::string name{}; std::string name{};
std::string path{}; std::string path{};
bool enabled = false; bool enabled = false;
//TODO: add metadata for UI ModData data{};
}; };
/** /**
@ -43,20 +42,22 @@ struct Mod
* The order matters since the order in which files are loaded matters, * The order matters since the order in which files are loaded matters,
* because some files can be overwritten. * because some files can be overwritten.
* *
* The list can be kept in the config file with the \ref SaveMods function. * The changes in the list do not immediately apply
* * and will be lost after the \ref ReloadMods call if they were not
* The changes in the list do not immediately apply. * saved beforehand with \ref SaveMods.
* Separate calls to \ref UpdatePaths and \ref ReloadResources, probably in this order,
* need to be done for the changes to apply.
* *
* The changes are also lost to mods which are no longer found in the search paths.
*/ */
class CModManager class CModManager
{ {
public: public:
CModManager(CApplication* app, CPathManager* pathManager); CModManager(CApplication* app, CPathManager* pathManager);
//! Loads mods without resource reloading; should be called only once after creation
void Init();
//! Finds all the mods along with their metadata //! Finds all the mods along with their metadata
void FindMods(); void ReloadMods();
//! Removes a mod from the list of loaded mods //! Removes a mod from the list of loaded mods
void EnableMod(size_t i); void EnableMod(size_t i);
@ -70,15 +71,9 @@ public:
//! Moves the selected mod down in the list so that it's loaded later than others, returns the new index //! Moves the selected mod down in the list so that it's loaded later than others, returns the new index
size_t MoveDown(size_t i); size_t MoveDown(size_t i);
//! Reloads application resources so the enabled mods are applied
void ReloadResources();
//! Saves the current configuration of mods to the config file //! Saves the current configuration of mods to the config file
void SaveMods(); void SaveMods();
//! Updates the paths in Path Manager according to the current mod configuration
void UpdatePaths();
//! Number of mods loaded //! Number of mods loaded
size_t CountMods() const; size_t CountMods() const;
@ -88,10 +83,16 @@ public:
//! Returns the list of mods //! Returns the list of mods
const std::vector<Mod>& GetMods() const; const std::vector<Mod>& GetMods() const;
private:
//! Updates the paths in Path Manager according to the current mod configuration
void UpdatePaths();
//! Reloads application resources so the enabled mods are applied
void ReloadResources();
private: private:
CApplication* m_app; CApplication* m_app;
CPathManager* m_pathManager; CPathManager* m_pathManager;
//TODO: better data structure?
std::vector<Mod> m_mods; std::vector<Mod> m_mods;
}; };

View File

@ -71,14 +71,14 @@ void CPathManager::AddModSearchDir(const std::string &modSearchDirPath)
void CPathManager::AddMod(const std::string &modPath) void CPathManager::AddMod(const std::string &modPath)
{ {
GetLogger()->Info("Loading mod: '%s'\n", modPath.c_str()); GetLogger()->Debug("Adding mod: '%s'\n", modPath.c_str());
CResourceManager::AddLocation(modPath, true); CResourceManager::AddLocation(modPath, true);
m_mods.push_back(modPath); m_mods.push_back(modPath);
} }
void CPathManager::RemoveMod(const std::string &modPath) void CPathManager::RemoveMod(const std::string &modPath)
{ {
GetLogger()->Info("Unloading mod: '%s'\n", modPath.c_str()); GetLogger()->Debug("Removing mod: '%s'\n", modPath.c_str());
CResourceManager::RemoveLocation(modPath); CResourceManager::RemoveLocation(modPath);
auto it = std::find(m_mods.cbegin(), m_mods.cend(), modPath); auto it = std::find(m_mods.cbegin(), m_mods.cend(), modPath);
if (it != m_mods.cend()) if (it != m_mods.cend())

View File

@ -93,12 +93,6 @@ void InitializeRestext()
stringsText[RT_SETUP_KEY1] = TR("1) First click on the key you want to redefine."); stringsText[RT_SETUP_KEY1] = TR("1) First click on the key you want to redefine.");
stringsText[RT_SETUP_KEY2] = TR("2) Then press the key you want to use instead."); stringsText[RT_SETUP_KEY2] = TR("2) Then press the key you want to use instead.");
stringsText[RT_MOD_LIST] = TR("Mods:");
stringsText[RT_MOD_DETAILS] = TR("Information:");
stringsText[RT_MOD_SUMMARY] = TR("Description:");
stringsText[RT_MOD_ENABLE] = TR("Enable\\Enable the selected mod");
stringsText[RT_MOD_DISABLE] = TR("Disable\\Disable the selected mod");
stringsText[RT_PERSO_FACE] = TR("Face type:"); stringsText[RT_PERSO_FACE] = TR("Face type:");
stringsText[RT_PERSO_GLASSES] = TR("Eyeglasses:"); stringsText[RT_PERSO_GLASSES] = TR("Eyeglasses:");
stringsText[RT_PERSO_HAIR] = TR("Hair color:"); stringsText[RT_PERSO_HAIR] = TR("Hair color:");
@ -165,7 +159,16 @@ void InitializeRestext()
stringsText[RT_SCOREBOARD_RESULTS_TIME]= TR("Time: %s"); stringsText[RT_SCOREBOARD_RESULTS_TIME]= TR("Time: %s");
stringsText[RT_SCOREBOARD_RESULTS_LINE]= TR("%s: %d pts"); stringsText[RT_SCOREBOARD_RESULTS_LINE]= TR("%s: %d pts");
stringsText[RT_MOD_LIST] = TR("Mods:");
stringsText[RT_MOD_DETAILS] = TR("Information:");
stringsText[RT_MOD_SUMMARY] = TR("Description:");
stringsText[RT_MOD_ENABLE] = TR("Enable\\Enable the selected mod");
stringsText[RT_MOD_DISABLE] = TR("Disable\\Disable the selected mod");
stringsText[RT_MOD_UNKNOWN_AUTHOR] = TR("Unknown author");
stringsText[RT_MOD_AUTHOR_FIELD_NAME] = TR("by");
stringsText[RT_MOD_VERSION_FIELD_NAME] = TR("Version");
stringsText[RT_MOD_WEBSITE_FIELD_NAME] = TR("Website");
stringsText[RT_MOD_NO_SUMMARY] = TR("No description.");
stringsEvent[EVENT_LABEL_CODE_BATTLE] = TR("Code battle"); stringsEvent[EVENT_LABEL_CODE_BATTLE] = TR("Code battle");

View File

@ -153,11 +153,16 @@ enum ResTextType
RT_SCOREBOARD_RESULTS_TIME= 232, RT_SCOREBOARD_RESULTS_TIME= 232,
RT_SCOREBOARD_RESULTS_LINE= 233, RT_SCOREBOARD_RESULTS_LINE= 233,
RT_MOD_LIST = 234, RT_MOD_LIST = 234,
RT_MOD_DETAILS = 235, RT_MOD_DETAILS = 235,
RT_MOD_SUMMARY = 236, RT_MOD_SUMMARY = 236,
RT_MOD_ENABLE = 237, RT_MOD_ENABLE = 237,
RT_MOD_DISABLE = 238, RT_MOD_DISABLE = 238,
RT_MOD_UNKNOWN_AUTHOR = 239,
RT_MOD_AUTHOR_FIELD_NAME = 240,
RT_MOD_VERSION_FIELD_NAME = 241,
RT_MOD_WEBSITE_FIELD_NAME = 242,
RT_MOD_NO_SUMMARY = 243,
RT_MAX //! < number of values RT_MAX //! < number of values
}; };

View File

@ -32,6 +32,8 @@
#include "common/system/system.h" #include "common/system/system.h"
#include "level/robotmain.h"
#include "math/func.h" #include "math/func.h"
#include "ui/controls/button.h" #include "ui/controls/button.h"
@ -94,7 +96,7 @@ void CScreenModList::CreateInterface()
pl->SetTextAlign(Gfx::TEXT_ALIGN_LEFT); pl->SetTextAlign(Gfx::TEXT_ALIGN_LEFT);
pos.y = oy+sy*6.7f; pos.y = oy+sy*6.7f;
ddim.y = dim.y*4.5f; ddim.y = dim.y*4.6f;
ddim.x = dim.x*6.5f; ddim.x = dim.x*6.5f;
pli = pw->CreateList(pos, ddim, 0, EVENT_INTERFACE_MOD_LIST); pli = pw->CreateList(pos, ddim, 0, EVENT_INTERFACE_MOD_LIST);
pli->SetState(STATE_SHADOW); pli->SetState(STATE_SHADOW);
@ -112,10 +114,13 @@ void CScreenModList::CreateInterface()
pl->SetTextAlign(Gfx::TEXT_ALIGN_LEFT); pl->SetTextAlign(Gfx::TEXT_ALIGN_LEFT);
pos.y = oy+sy*6.7f; pos.y = oy+sy*6.7f;
ddim.y = dim.y*4.5f; ddim.y = dim.y*4.3f;
ddim.x = dim.x*6.5f; ddim.x = dim.x*6.5f;
pli = pw->CreateList(pos, ddim, 0, EVENT_INTERFACE_LIST); pe = pw->CreateEdit(pos, ddim, 0, EVENT_INTERFACE_MOD_DETAILS);
pli->SetState(STATE_SHADOW); pe->SetState(STATE_SHADOW);
pe->SetMaxChar(500);
pe->SetEditCap(false); // just to see
pe->SetHighlightCap(true);
UpdateModDetails(); UpdateModDetails();
@ -139,7 +144,7 @@ void CScreenModList::CreateInterface()
pe->SetState(STATE_SHADOW); pe->SetState(STATE_SHADOW);
pe->SetMaxChar(500); pe->SetMaxChar(500);
pe->SetEditCap(false); // just to see pe->SetEditCap(false); // just to see
pe->SetHighlightCap(false); pe->SetHighlightCap(true);
UpdateModSummary(); UpdateModSummary();
@ -337,14 +342,10 @@ void CScreenModList::ApplyChanges()
m_modManager->SaveMods(); m_modManager->SaveMods();
} }
m_modManager->FindMods(); m_modManager->ReloadMods();
m_modManager->SaveMods();
m_empty = (m_modManager->CountMods() == 0); m_empty = (m_modManager->CountMods() == 0);
m_modManager->UpdatePaths();
m_modManager->ReloadResources();
m_modSelectedIndex = Math::Clamp(m_modSelectedIndex, static_cast<size_t>(0), m_modManager->CountMods() - 1); m_modSelectedIndex = Math::Clamp(m_modSelectedIndex, static_cast<size_t>(0), m_modManager->CountMods() - 1);
} }
@ -372,7 +373,8 @@ void CScreenModList::UpdateModList()
for (size_t i = 0; i < mods.size(); ++i) for (size_t i = 0; i < mods.size(); ++i)
{ {
const auto& mod = mods[i]; const auto& mod = mods[i];
pl->SetItemName(i, mod.name); auto name = GetLanguageStringProperty(mod.data.displayName, mod.name);
pl->SetItemName(i, name);
pl->SetCheck(i, mod.enabled); pl->SetCheck(i, mod.enabled);
pl->SetEnable(i, true); pl->SetEnable(i, true);
} }
@ -383,12 +385,80 @@ void CScreenModList::UpdateModList()
void CScreenModList::UpdateModDetails() void CScreenModList::UpdateModDetails()
{ {
//TODO CWindow* pw = static_cast<CWindow*>(m_interface->SearchControl(EVENT_WINDOW5));
if (pw == nullptr) return;
CEdit* pe = static_cast<CEdit*>(pw->SearchControl(EVENT_INTERFACE_MOD_DETAILS));
if (pe == nullptr) return;
if (m_empty)
{
pe->SetText("No information");
return;
}
std::string details{};
const auto& mod = m_modManager->GetMod(m_modSelectedIndex);
const auto data = mod.data;
auto name = GetLanguageStringProperty(data.displayName, mod.name);
details += "\\b;" + name + '\n';
std::string authorFieldName;
GetResource(RES_TEXT, RT_MOD_AUTHOR_FIELD_NAME, authorFieldName);
details += "\\s;" + authorFieldName + " ";
if (!data.author.empty())
{
details += data.author;
}
else
{
std::string unknownAuthor;
GetResource(RES_TEXT, RT_MOD_UNKNOWN_AUTHOR, unknownAuthor);
details += unknownAuthor;
}
details += '\n';
details += '\n';
if (!data.version.empty())
{
std::string versionFieldName;
GetResource(RES_TEXT, RT_MOD_VERSION_FIELD_NAME, versionFieldName);
details += "\\t;" + versionFieldName + '\n' + data.version + '\n';
}
if (!data.website.empty())
{
std::string websiteFieldName;
GetResource(RES_TEXT, RT_MOD_WEBSITE_FIELD_NAME, websiteFieldName);
details += "\\t;" + websiteFieldName + '\n' + data.website + '\n';
}
pe->SetText(details);
} }
void CScreenModList::UpdateModSummary() void CScreenModList::UpdateModSummary()
{ {
//TODO CWindow* pw = static_cast<CWindow*>(m_interface->SearchControl(EVENT_WINDOW5));
if (pw == nullptr) return;
CEdit* pe = static_cast<CEdit*>(pw->SearchControl(EVENT_INTERFACE_MOD_SUMMARY));
if (pe == nullptr) return;
std::string noSummary;
GetResource(RES_TEXT, RT_MOD_NO_SUMMARY, noSummary);
if (m_empty)
{
pe->SetText(noSummary);
return;
}
const auto& mod = m_modManager->GetMod(m_modSelectedIndex);
pe->SetText(GetLanguageStringProperty(mod.data.summary, noSummary));
} }
void CScreenModList::UpdateEnableDisableButton() void CScreenModList::UpdateEnableDisableButton()
@ -487,4 +557,23 @@ void CScreenModList::UpdateUpDownButtons()
} }
} }
std::string CScreenModList::GetLanguageStringProperty(std::unordered_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 } // namespace Ui

View File

@ -21,11 +21,12 @@
#include "app/modman.h" #include "app/modman.h"
#include "ui/maindialog.h"
#include "ui/screen/screen.h" #include "ui/screen/screen.h"
namespace Ui namespace Ui
{ {
class CMainDialog;
/** /**
* \class CScreenModList * \class CScreenModList
@ -78,6 +79,8 @@ protected:
void UpdateApplyButton(); void UpdateApplyButton();
void UpdateUpDownButtons(); void UpdateUpDownButtons();
std::string GetLanguageStringProperty(std::unordered_map<Language, std::string> property, const std::string& fallback);
protected: protected:
Ui::CMainDialog* m_dialog; Ui::CMainDialog* m_dialog;