aboutsummaryrefslogtreecommitdiff
path: root/launcher
diff options
context:
space:
mode:
Diffstat (limited to 'launcher')
-rw-r--r--launcher/Application.cpp14
-rw-r--r--launcher/CMakeLists.txt6
-rw-r--r--launcher/FileSystem.cpp60
-rw-r--r--launcher/minecraft/PackProfile.cpp3
-rw-r--r--launcher/minecraft/mod/Mod.cpp4
-rw-r--r--launcher/minecraft/mod/Mod.h2
-rw-r--r--launcher/minecraft/mod/ModFolderModel.cpp6
-rw-r--r--launcher/minecraft/mod/Resource.cpp8
-rw-r--r--launcher/minecraft/mod/Resource.h2
-rw-r--r--launcher/minecraft/mod/ResourceFolderModel.cpp2
-rw-r--r--launcher/minecraft/mod/tasks/LocalResourceParse.cpp9
-rw-r--r--launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp2
-rw-r--r--launcher/modplatform/EnsureMetadataTask.cpp6
-rw-r--r--launcher/modplatform/flame/FileResolvingTask.cpp23
-rw-r--r--launcher/modplatform/flame/FlameAPI.h2
-rw-r--r--launcher/modplatform/flame/FlameInstanceCreationTask.cpp8
-rw-r--r--launcher/modplatform/flame/PackManifest.cpp9
-rw-r--r--launcher/modplatform/helpers/ExportToModList.cpp200
-rw-r--r--launcher/modplatform/helpers/ExportToModList.h33
-rw-r--r--launcher/modplatform/legacy_ftb/PackInstallTask.cpp86
-rw-r--r--launcher/modplatform/legacy_ftb/PackInstallTask.h27
-rw-r--r--launcher/modplatform/modrinth/ModrinthAPI.h6
-rw-r--r--launcher/translations/TranslationsModel.cpp30
-rw-r--r--launcher/translations/TranslationsModel.h29
-rw-r--r--launcher/ui/MainWindow.cpp51
-rw-r--r--launcher/ui/MainWindow.h1
-rw-r--r--launcher/ui/MainWindow.ui10
-rw-r--r--launcher/ui/dialogs/ExportToModListDialog.cpp223
-rw-r--r--launcher/ui/dialogs/ExportToModListDialog.h55
-rw-r--r--launcher/ui/dialogs/ExportToModListDialog.ui240
-rw-r--r--launcher/ui/dialogs/ResourceDownloadDialog.cpp9
-rw-r--r--launcher/ui/pages/instance/ManagedPackPage.cpp25
-rw-r--r--launcher/ui/pages/modplatform/ResourcePage.cpp1
-rw-r--r--launcher/ui/widgets/LanguageSelectionWidget.cpp28
-rw-r--r--launcher/ui/widgets/LanguageSelectionWidget.h25
35 files changed, 1056 insertions, 189 deletions
diff --git a/launcher/Application.cpp b/launcher/Application.cpp
index a80af39e..814741dd 100644
--- a/launcher/Application.cpp
+++ b/launcher/Application.cpp
@@ -433,7 +433,11 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
}
// seach root path
if(!foundLoggingRules) {
+#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
+ logRulesPath = FS::PathCombine(m_rootPath, "share", BuildConfig.LAUNCHER_NAME, logRulesFile);
+#else
logRulesPath = FS::PathCombine(m_rootPath, logRulesFile);
+#endif
qDebug() << "Testing" << logRulesPath << "...";
foundLoggingRules = QFile::exists(logRulesPath);
}
@@ -568,6 +572,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
// Language
m_settings->registerSetting("Language", QString());
+ m_settings->registerSetting("UseSystemLocale", false);
// Console
m_settings->registerSetting("ShowConsole", false);
@@ -918,12 +923,7 @@ bool Application::createSetupWizard()
}
return false;
}();
- bool languageRequired = [&]()
- {
- if (settings()->get("Language").toString().isEmpty())
- return true;
- return false;
- }();
+ bool languageRequired = settings()->get("Language").toString().isEmpty();
bool pasteInterventionRequired = settings()->get("PastebinURL") != "";
bool themeInterventionRequired = settings()->get("ApplicationTheme") == "";
bool wizardRequired = javaRequired || languageRequired || pasteInterventionRequired || themeInterventionRequired;
@@ -1577,7 +1577,7 @@ QString Application::getJarPath(QString jarFile)
{
QStringList potentialPaths = {
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
- FS::PathCombine(m_rootPath, "share/" + BuildConfig.LAUNCHER_APP_BINARY_NAME),
+ FS::PathCombine(m_rootPath, "share", BuildConfig.LAUNCHER_NAME),
#endif
FS::PathCombine(m_rootPath, "jars"),
FS::PathCombine(applicationDirPath(), "jars"),
diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt
index 203ab986..2d06dbf4 100644
--- a/launcher/CMakeLists.txt
+++ b/launcher/CMakeLists.txt
@@ -487,6 +487,9 @@ set(API_SOURCES
modplatform/helpers/HashUtils.cpp
modplatform/helpers/OverrideUtils.h
modplatform/helpers/OverrideUtils.cpp
+
+ modplatform/helpers/ExportToModList.h
+ modplatform/helpers/ExportToModList.cpp
)
set(FTB_SOURCES
@@ -913,6 +916,8 @@ SET(LAUNCHER_SOURCES
ui/dialogs/ExportInstanceDialog.h
ui/dialogs/ExportPackDialog.cpp
ui/dialogs/ExportPackDialog.h
+ ui/dialogs/ExportToModListDialog.cpp
+ ui/dialogs/ExportToModListDialog.h
ui/dialogs/IconPickerDialog.cpp
ui/dialogs/IconPickerDialog.h
ui/dialogs/ImportResourceDialog.cpp
@@ -1060,6 +1065,7 @@ qt_wrap_ui(LAUNCHER_UI
ui/dialogs/SkinUploadDialog.ui
ui/dialogs/ExportInstanceDialog.ui
ui/dialogs/ExportPackDialog.ui
+ ui/dialogs/ExportToModListDialog.ui
ui/dialogs/IconPickerDialog.ui
ui/dialogs/ImportResourceDialog.ui
ui/dialogs/MSALoginDialog.ui
diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp
index 812d45eb..4538702f 100644
--- a/launcher/FileSystem.cpp
+++ b/launcher/FileSystem.cpp
@@ -778,9 +778,43 @@ bool createShortcut(QString destination, QString target, QStringList args, QStri
destination = PathCombine(getDesktopDir(), RemoveInvalidFilenameChars(name));
}
#if defined(Q_OS_MACOS)
- destination += ".command";
+ // Create the Application
+ QDir applicationDirectory = QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation) + "/" + BuildConfig.LAUNCHER_NAME + " Instances/";
- QFile f(destination);
+ if (!applicationDirectory.mkpath(".")) {
+ qWarning() << "Couldn't create application directory";
+ return false;
+ }
+
+ QDir application = applicationDirectory.path() + "/" + name + ".app/";
+
+ if (application.exists()) {
+ qWarning() << "Application already exists!";
+ return false;
+ }
+
+ if (!application.mkpath(".")) {
+ qWarning() << "Couldn't create application";
+ return false;
+ }
+
+ QDir content = application.path() + "/Contents/";
+ QDir resources = content.path() + "/Resources/";
+ QDir binaryDir = content.path() + "/MacOS/";
+ QFile info = content.path() + "/Info.plist";
+
+ if (!(content.mkpath(".") && resources.mkpath(".") && binaryDir.mkpath("."))) {
+ qWarning() << "Couldn't create directories within application";
+ return false;
+ }
+ info.open(QIODevice::WriteOnly | QIODevice::Text);
+
+ QFile(icon).rename(resources.path() + "/Icon.icns");
+
+ // Create the Command file
+ QString exec = binaryDir.path() + "/Run.command";
+
+ QFile f(exec);
f.open(QIODevice::WriteOnly | QIODevice::Text);
QTextStream stream(&f);
@@ -797,6 +831,28 @@ bool createShortcut(QString destination, QString target, QStringList args, QStri
f.setPermissions(f.permissions() | QFileDevice::ExeOwner | QFileDevice::ExeGroup | QFileDevice::ExeOther);
+ // Generate the Info.plist
+ QTextStream infoStream(&info);
+ infoStream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?> \n"
+ "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" "
+ "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">"
+ "<plist version=\"1.0\">\n"
+ "<dict>\n"
+ " <key>CFBundleExecutable</key>\n"
+ " <string>Run.command</string>\n" // The path to the executable
+ " <key>CFBundleIconFile</key>\n"
+ " <string>Icon.icns</string>\n"
+ " <key>CFBundleName</key>\n"
+ " <string>" << name << "</string>\n" // Name of the application
+ " <key>CFBundlePackageType</key>\n"
+ " <string>APPL</string>\n"
+ " <key>CFBundleShortVersionString</key>\n"
+ " <string>1.0</string>\n"
+ " <key>CFBundleVersion</key>\n"
+ " <string>1.0</string>\n"
+ "</dict>\n"
+ "</plist>";
+
return true;
#elif defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
if (!destination.endsWith(".desktop")) // in case of isFlatpak destination is already populated
diff --git a/launcher/minecraft/PackProfile.cpp b/launcher/minecraft/PackProfile.cpp
index aff05dbc..e8fd2157 100644
--- a/launcher/minecraft/PackProfile.cpp
+++ b/launcher/minecraft/PackProfile.cpp
@@ -65,7 +65,8 @@
static const QMap<QString, ResourceAPI::ModLoaderType> modloaderMapping{
{"net.minecraftforge", ResourceAPI::Forge},
{"net.fabricmc.fabric-loader", ResourceAPI::Fabric},
- {"org.quiltmc.quilt-loader", ResourceAPI::Quilt}
+ {"org.quiltmc.quilt-loader", ResourceAPI::Quilt},
+ {"com.mumfrey.liteloader", ResourceAPI::LiteLoader}
};
PackProfile::PackProfile(MinecraftInstance * instance)
diff --git a/launcher/minecraft/mod/Mod.cpp b/launcher/minecraft/mod/Mod.cpp
index fa1a0253..880dacb1 100644
--- a/launcher/minecraft/mod/Mod.cpp
+++ b/launcher/minecraft/mod/Mod.cpp
@@ -126,7 +126,7 @@ bool Mod::applyFilter(QRegularExpression filter) const
return Resource::applyFilter(filter);
}
-auto Mod::destroy(QDir& index_dir, bool preserve_metadata) -> bool
+auto Mod::destroy(QDir& index_dir, bool preserve_metadata, bool attempt_trash) -> bool
{
if (!preserve_metadata) {
qDebug() << QString("Destroying metadata for '%1' on purpose").arg(name());
@@ -139,7 +139,7 @@ auto Mod::destroy(QDir& index_dir, bool preserve_metadata) -> bool
}
}
- return Resource::destroy();
+ return Resource::destroy(attempt_trash);
}
auto Mod::details() const -> const ModDetails&
diff --git a/launcher/minecraft/mod/Mod.h b/launcher/minecraft/mod/Mod.h
index d6272f4d..b67bd465 100644
--- a/launcher/minecraft/mod/Mod.h
+++ b/launcher/minecraft/mod/Mod.h
@@ -93,7 +93,7 @@ public:
[[nodiscard]] bool applyFilter(QRegularExpression filter) const override;
// Delete all the files of this mod
- auto destroy(QDir& index_dir, bool preserve_metadata = false) -> bool;
+ auto destroy(QDir& index_dir, bool preserve_metadata = false, bool attempt_trash = true) -> bool;
void finishResolvingWithDetails(ModDetails&& details);
diff --git a/launcher/minecraft/mod/ModFolderModel.cpp b/launcher/minecraft/mod/ModFolderModel.cpp
index d4ff29e6..51383edf 100644
--- a/launcher/minecraft/mod/ModFolderModel.cpp
+++ b/launcher/minecraft/mod/ModFolderModel.cpp
@@ -199,10 +199,10 @@ Task* ModFolderModel::createParseTask(Resource& resource)
bool ModFolderModel::uninstallMod(const QString& filename, bool preserve_metadata)
{
- for(auto mod : allMods()){
- if(mod->fileinfo().fileName() == filename){
+ for(auto mod : allMods()) {
+ if(mod->fileinfo().fileName() == filename) {
auto index_dir = indexDir();
- mod->destroy(index_dir, preserve_metadata);
+ mod->destroy(index_dir, preserve_metadata, false);
update();
diff --git a/launcher/minecraft/mod/Resource.cpp b/launcher/minecraft/mod/Resource.cpp
index e5077260..098a617f 100644
--- a/launcher/minecraft/mod/Resource.cpp
+++ b/launcher/minecraft/mod/Resource.cpp
@@ -148,14 +148,10 @@ bool Resource::enable(EnableAction action)
return true;
}
-bool Resource::destroy()
+bool Resource::destroy(bool attemptTrash)
{
m_type = ResourceType::UNKNOWN;
-
- if (FS::trash(m_file_info.filePath()))
- return true;
-
- return FS::deletePath(m_file_info.filePath());
+ return (attemptTrash && FS::trash(m_file_info.filePath())) || FS::deletePath(m_file_info.filePath());
}
bool Resource::isSymLinkUnder(const QString& instPath) const
diff --git a/launcher/minecraft/mod/Resource.h b/launcher/minecraft/mod/Resource.h
index a5e9ae91..94f3160c 100644
--- a/launcher/minecraft/mod/Resource.h
+++ b/launcher/minecraft/mod/Resource.h
@@ -92,7 +92,7 @@ class Resource : public QObject {
}
// Delete all files of this resource.
- bool destroy();
+ bool destroy(bool attemptTrash = true);
[[nodiscard]] auto isSymLink() const -> bool { return m_file_info.isSymLink(); }
diff --git a/launcher/minecraft/mod/ResourceFolderModel.cpp b/launcher/minecraft/mod/ResourceFolderModel.cpp
index c2c0d178..39a61067 100644
--- a/launcher/minecraft/mod/ResourceFolderModel.cpp
+++ b/launcher/minecraft/mod/ResourceFolderModel.cpp
@@ -157,7 +157,7 @@ bool ResourceFolderModel::uninstallResource(QString file_name)
{
for (auto& resource : m_resources) {
if (resource->fileinfo().fileName() == file_name) {
- auto res = resource->destroy();
+ auto res = resource->destroy(false);
update();
diff --git a/launcher/minecraft/mod/tasks/LocalResourceParse.cpp b/launcher/minecraft/mod/tasks/LocalResourceParse.cpp
index 4d760df2..0894049c 100644
--- a/launcher/minecraft/mod/tasks/LocalResourceParse.cpp
+++ b/launcher/minecraft/mod/tasks/LocalResourceParse.cpp
@@ -44,7 +44,11 @@ static const QMap<PackedResourceType, QString> s_packed_type_names = {
namespace ResourceUtils {
PackedResourceType identify(QFileInfo file){
if (file.exists() && file.isFile()) {
- if (ResourcePackUtils::validate(file)) {
+ if (ModUtils::validate(file)) {
+ // mods can contain resource and data packs so they must be tested first
+ qDebug() << file.fileName() << "is a mod";
+ return PackedResourceType::Mod;
+ } else if (ResourcePackUtils::validate(file)) {
qDebug() << file.fileName() << "is a resource pack";
return PackedResourceType::ResourcePack;
} else if (TexturePackUtils::validate(file)) {
@@ -53,9 +57,6 @@ PackedResourceType identify(QFileInfo file){
} else if (DataPackUtils::validate(file)) {
qDebug() << file.fileName() << "is a data pack";
return PackedResourceType::DataPack;
- } else if (ModUtils::validate(file)) {
- qDebug() << file.fileName() << "is a mod";
- return PackedResourceType::Mod;
} else if (WorldSaveUtils::validate(file)) {
qDebug() << file.fileName() << "is a world save";
return PackedResourceType::WorldSave;
diff --git a/launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp b/launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp
index 3677a1dc..ef353c70 100644
--- a/launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp
+++ b/launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp
@@ -103,7 +103,7 @@ void ModFolderLoadTask::executeTask()
while (iter.hasNext()) {
auto mod = iter.next().value();
if (mod->status() == ModStatus::NotInstalled) {
- mod->destroy(m_index_dir, false);
+ mod->destroy(m_index_dir, false, false);
iter.remove();
}
}
diff --git a/launcher/modplatform/EnsureMetadataTask.cpp b/launcher/modplatform/EnsureMetadataTask.cpp
index 93b5ce76..c3eadd06 100644
--- a/launcher/modplatform/EnsureMetadataTask.cpp
+++ b/launcher/modplatform/EnsureMetadataTask.cpp
@@ -145,7 +145,8 @@ void EnsureMetadataTask::executeTask()
connect(project_task.get(), &Task::finished, this, [=] {
invalidade_leftover();
project_task->deleteLater();
- m_current_task = nullptr;
+ if (m_current_task)
+ m_current_task.reset();
});
m_current_task = project_task;
@@ -154,7 +155,8 @@ void EnsureMetadataTask::executeTask()
connect(version_task.get(), &Task::finished, [=] {
version_task->deleteLater();
- m_current_task = nullptr;
+ if (m_current_task)
+ m_current_task.reset();
});
if (m_mods.size() > 1)
diff --git a/launcher/modplatform/flame/FileResolvingTask.cpp b/launcher/modplatform/flame/FileResolvingTask.cpp
index ce7a6055..34bd401d 100644
--- a/launcher/modplatform/flame/FileResolvingTask.cpp
+++ b/launcher/modplatform/flame/FileResolvingTask.cpp
@@ -21,6 +21,10 @@ bool Flame::FileResolvingTask::abort()
void Flame::FileResolvingTask::executeTask()
{
+ if (m_toProcess.files.isEmpty()) { // no file to resolve so leave it empty and emit success immediately
+ emitSucceeded();
+ return;
+ }
setStatus(tr("Resolving mod IDs..."));
setProgress(0, 3);
m_dljob.reset(new NetJob("Mod id resolver", m_network));
@@ -128,12 +132,13 @@ void Flame::FileResolvingTask::netJobFinished()
m_checkJob->start();
}
-void Flame::FileResolvingTask::modrinthCheckFinished() {
+void Flame::FileResolvingTask::modrinthCheckFinished()
+{
setProgress(2, 3);
qDebug() << "Finished with blocked mods : " << blockedProjects.size();
for (auto it = blockedProjects.keyBegin(); it != blockedProjects.keyEnd(); it++) {
- auto &out = *it;
+ auto& out = *it;
auto bytes = blockedProjects[out];
if (!out->resolved) {
continue;
@@ -153,15 +158,13 @@ void Flame::FileResolvingTask::modrinthCheckFinished() {
out->resolved = false;
}
}
- //copy to an output list and filter out projects found on modrinth
+ // copy to an output list and filter out projects found on modrinth
auto block = std::make_shared<QList<File*>>();
auto it = blockedProjects.keys();
- std::copy_if(it.begin(), it.end(), std::back_inserter(*block), [](File *f) {
- return !f->resolved;
- });
- //Display not found mods early
+ std::copy_if(it.begin(), it.end(), std::back_inserter(*block), [](File* f) { return !f->resolved; });
+ // Display not found mods early
if (!block->empty()) {
- //blocked mods found, we need the slug for displaying.... we need another job :D !
+ // blocked mods found, we need the slug for displaying.... we need another job :D !
m_slugJob.reset(new NetJob("Slug Job", m_network));
int index = 0;
for (auto mod : *block) {
@@ -173,8 +176,8 @@ void Flame::FileResolvingTask::modrinthCheckFinished() {
QObject::connect(dl.get(), &Net::Download::succeeded, [block, index, output]() {
auto mod = block->at(index); // use the shared_ptr so it is captured and only freed when we are done
auto json = QJsonDocument::fromJson(*output);
- auto base = Json::requireString(Json::requireObject(Json::requireObject(Json::requireObject(json),"data"),"links"),
- "websiteUrl");
+ auto base =
+ Json::requireString(Json::requireObject(Json::requireObject(Json::requireObject(json), "data"), "links"), "websiteUrl");
auto link = QString("%1/download/%2").arg(base, QString::number(mod->fileId));
mod->websiteUrl = link;
});
diff --git a/launcher/modplatform/flame/FlameAPI.h b/launcher/modplatform/flame/FlameAPI.h
index 0a6dc78f..49bc316f 100644
--- a/launcher/modplatform/flame/FlameAPI.h
+++ b/launcher/modplatform/flame/FlameAPI.h
@@ -23,6 +23,8 @@ class FlameAPI : public NetworkResourceAPI {
[[nodiscard]] auto getSortingMethods() const -> QList<ResourceAPI::SortingMethod> override;
+ static inline auto validateModLoaders(ModLoaderTypes loaders) -> bool { return loaders & (Forge | Fabric | Quilt); }
+
private:
static int getClassId(ModPlatform::ResourceType type)
{
diff --git a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp
index e7641d64..b57db288 100644
--- a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp
+++ b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp
@@ -563,6 +563,8 @@ void FlameCreationTask::validateZIPResouces()
if (FS::move(localPath, destPath)) {
return destPath;
}
+ } else {
+ qDebug() << "Target folder of" << fileName << "is correct at" << targetFolder;
}
return localPath;
};
@@ -584,6 +586,9 @@ void FlameCreationTask::validateZIPResouces()
QString worldPath;
switch (type) {
+ case PackedResourceType::Mod :
+ validatePath(fileName, targetFolder, "mods");
+ break;
case PackedResourceType::ResourcePack :
validatePath(fileName, targetFolder, "resourcepacks");
break;
@@ -593,9 +598,6 @@ void FlameCreationTask::validateZIPResouces()
case PackedResourceType::DataPack :
validatePath(fileName, targetFolder, "datapacks");
break;
- case PackedResourceType::Mod :
- validatePath(fileName, targetFolder, "mods");
- break;
case PackedResourceType::ShaderPack :
// in theroy flame API can't do this but who knows, that *may* change ?
// better to handle it if it *does* occure in the future
diff --git a/launcher/modplatform/flame/PackManifest.cpp b/launcher/modplatform/flame/PackManifest.cpp
index 22008297..ee4d0766 100644
--- a/launcher/modplatform/flame/PackManifest.cpp
+++ b/launcher/modplatform/flame/PackManifest.cpp
@@ -76,13 +76,8 @@ bool Flame::File::parseFromObject(const QJsonObject& obj, bool throw_on_blocked
// It is also optional
type = File::Type::SingleFile;
- if (fileName.endsWith(".zip")) {
- // this is probably a resource pack
- targetFolder = "resourcepacks";
- } else {
- // this is probably a mod, dunno what else could modpacks download
- targetFolder = "mods";
- }
+ targetFolder = "mods";
+
// get the hash
hash = QString();
auto hashes = Json::ensureArray(obj, "hashes");
diff --git a/launcher/modplatform/helpers/ExportToModList.cpp b/launcher/modplatform/helpers/ExportToModList.cpp
new file mode 100644
index 00000000..1f01c4a8
--- /dev/null
+++ b/launcher/modplatform/helpers/ExportToModList.cpp
@@ -0,0 +1,200 @@
+// SPDX-License-Identifier: GPL-3.0-only
+/*
+ * Prism Launcher - Minecraft Launcher
+ * Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
+ *
+ * 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, version 3.
+ *
+ * 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 <https://www.gnu.org/licenses/>.
+ */
+#include "ExportToModList.h"
+#include <QJsonArray>
+#include <QJsonDocument>
+#include <QJsonObject>
+
+namespace ExportToModList {
+QString toHTML(QList<Mod*> mods, OptionalData extraData)
+{
+ QStringList lines;
+ for (auto mod : mods) {
+ auto meta = mod->metadata();
+ auto modName = mod->name().toHtmlEscaped();
+ if (extraData & Url) {
+ auto url = mod->metaurl().toHtmlEscaped();
+ if (!url.isEmpty())
+ modName = QString("<a href=\"%1\">%2</a>").arg(url, modName);
+ }
+ auto line = modName;
+ if (extraData & Version) {
+ auto ver = mod->version();
+ if (ver.isEmpty() && meta != nullptr)
+ ver = meta->version().toString();
+ if (!ver.isEmpty())
+ line += QString(" [%1]").arg(ver.toHtmlEscaped());
+ }
+ if (extraData & Authors && !mod->authors().isEmpty())
+ line += " by " + mod->authors().join(", ").toHtmlEscaped();
+ lines.append(QString("<li>%1</li>").arg(line));
+ }
+ return QString("<html><body><ul>\n\t%1\n</ul></body></html>").arg(lines.join("\n\t"));
+}
+
+QString toMarkdown(QList<Mod*> mods, OptionalData extraData)
+{
+ QStringList lines;
+ for (auto mod : mods) {
+ auto meta = mod->metadata();
+ auto modName = mod->name();
+ if (extraData & Url) {
+ auto url = mod->metaurl();
+ if (!url.isEmpty())
+ modName = QString("[%1](%2)").arg(modName, url);
+ }
+ auto line = modName;
+ if (extraData & Version) {
+ auto ver = mod->version();
+ if (ver.isEmpty() && meta != nullptr)
+ ver = meta->version().toString();
+ if (!ver.isEmpty())
+ line += QString(" [%1]").arg(ver);
+ }
+ if (extraData & Authors && !mod->authors().isEmpty())
+ line += " by " + mod->authors().join(", ");
+ lines << "- " + line;
+ }
+ return lines.join("\n");
+}
+
+QString toPlainTXT(QList<Mod*> mods, OptionalData extraData)
+{
+ QStringList lines;
+ for (auto mod : mods) {
+ auto meta = mod->metadata();
+ auto modName = mod->name();
+
+ auto line = modName;
+ if (extraData & Url) {
+ auto url = mod->metaurl();
+ if (!url.isEmpty())
+ line += QString(" (%1)").arg(url);
+ }
+ if (extraData & Version) {
+ auto ver = mod->version();
+ if (ver.isEmpty() && meta != nullptr)
+ ver = meta->version().toString();
+ if (!ver.isEmpty())
+ line += QString(" [%1]").arg(ver);
+ }
+ if (extraData & Authors && !mod->authors().isEmpty())
+ line += " by " + mod->authors().join(", ");
+ lines << line;
+ }
+ return lines.join("\n");
+}
+
+QString toJSON(QList<Mod*> mods, OptionalData extraData)
+{
+ QJsonArray lines;
+ for (auto mod : mods) {
+ auto meta = mod->metadata();
+ auto modName = mod->name();
+ QJsonObject line;
+ line["name"] = modName;
+ if (extraData & Url) {
+ auto url = mod->metaurl();
+ if (!url.isEmpty())
+ line["url"] = url;
+ }
+ if (extraData & Version) {
+ auto ver = mod->version();
+ if (ver.isEmpty() && meta != nullptr)
+ ver = meta->version().toString();
+ if (!ver.isEmpty())
+ line["version"] = ver;
+ }
+ if (extraData & Authors && !mod->authors().isEmpty())
+ line["authors"] = QJsonArray::fromStringList(mod->authors());
+ lines << line;
+ }
+ QJsonDocument doc;
+ doc.setArray(lines);
+ return doc.toJson();
+}
+
+QString toCSV(QList<Mod*> mods, OptionalData extraData)
+{
+ QStringList lines;
+ for (auto mod : mods) {
+ QStringList data;
+ auto meta = mod->metadata();
+ auto modName = mod->name();
+
+ data << modName;
+ if (extraData & Url)
+ data << mod->metaurl();
+ if (extraData & Version) {
+ auto ver = mod->version();
+ if (ver.isEmpty() && meta != nullptr)
+ ver = meta->version().toString();
+ data << ver;
+ }
+ if (extraData & Authors) {
+ QString authors;
+ if (mod->authors().length() == 1)
+ authors = mod->authors().back();
+ else if (mod->authors().length() > 1)
+ authors = QString("\"%1\"").arg(mod->authors().join(","));
+ data << authors;
+ }
+ lines << data.join(",");
+ }
+ return lines.join("\n");
+}
+
+QString exportToModList(QList<Mod*> mods, Formats format, OptionalData extraData)
+{
+ switch (format) {
+ case HTML:
+ return toHTML(mods, extraData);
+ case MARKDOWN:
+ return toMarkdown(mods, extraData);
+ case PLAINTXT:
+ return toPlainTXT(mods, extraData);
+ case JSON:
+ return toJSON(mods, extraData);
+ case CSV:
+ return to