aboutsummaryrefslogtreecommitdiff
path: root/launcher/modplatform
diff options
context:
space:
mode:
Diffstat (limited to 'launcher/modplatform')
-rw-r--r--launcher/modplatform/ModAPI.h15
-rw-r--r--launcher/modplatform/flame/FileResolvingTask.cpp16
-rw-r--r--launcher/modplatform/flame/FlameAPI.h17
-rw-r--r--launcher/modplatform/flame/FlameModIndex.cpp9
-rw-r--r--launcher/modplatform/flame/FlamePackIndex.cpp10
-rw-r--r--launcher/modplatform/flame/FlamePackIndex.h1
-rw-r--r--launcher/modplatform/flame/PackManifest.cpp15
-rw-r--r--launcher/modplatform/modrinth/ModrinthAPI.h75
-rw-r--r--launcher/modplatform/modrinth/ModrinthPackIndex.cpp17
-rw-r--r--launcher/modplatform/modrinth/ModrinthPackIndex.h17
-rw-r--r--launcher/modplatform/modrinth/ModrinthPackManifest.cpp156
-rw-r--r--launcher/modplatform/modrinth/ModrinthPackManifest.h107
-rw-r--r--launcher/modplatform/technic/SolderPackManifest.cpp2
13 files changed, 408 insertions, 49 deletions
diff --git a/launcher/modplatform/ModAPI.h b/launcher/modplatform/ModAPI.h
index 8e6cd45c..4230df0b 100644
--- a/launcher/modplatform/ModAPI.h
+++ b/launcher/modplatform/ModAPI.h
@@ -16,14 +16,21 @@ class ModAPI {
public:
virtual ~ModAPI() = default;
- // https://docs.curseforge.com/?http#tocS_ModLoaderType
- enum ModLoaderType { Unspecified = 0, Forge = 1, Cauldron = 2, LiteLoader = 3, Fabric = 4, Quilt = 5 };
+ enum ModLoaderType {
+ Unspecified = 0,
+ Forge = 1 << 0,
+ Cauldron = 1 << 1,
+ LiteLoader = 1 << 2,
+ Fabric = 1 << 3,
+ Quilt = 1 << 4
+ };
+ Q_DECLARE_FLAGS(ModLoaderTypes, ModLoaderType)
struct SearchArgs {
int offset;
QString search;
QString sorting;
- ModLoaderType mod_loader;
+ ModLoaderTypes loaders;
std::list<Version> versions;
};
@@ -33,7 +40,7 @@ class ModAPI {
struct VersionSearchArgs {
QString addonId;
std::list<Version> mcVersions;
- ModLoaderType loader;
+ ModLoaderTypes loaders;
};
virtual void getVersions(CallerType* caller, VersionSearchArgs&& args) const = 0;
diff --git a/launcher/modplatform/flame/FileResolvingTask.cpp b/launcher/modplatform/flame/FileResolvingTask.cpp
index 95924a68..0deb99c4 100644
--- a/launcher/modplatform/flame/FileResolvingTask.cpp
+++ b/launcher/modplatform/flame/FileResolvingTask.cpp
@@ -31,7 +31,21 @@ void Flame::FileResolvingTask::netJobFinished()
for (auto& bytes : results) {
auto& out = m_toProcess.files[index];
try {
- failed &= (!out.parseFromBytes(bytes));
+ bool fail = (!out.parseFromBytes(bytes));
+ if(fail){
+ //failed :( probably disabled mod, try to add to the list
+ auto doc = Json::requireDocument(bytes);
+ if (!doc.isObject()) {
+ throw JSONValidationError(QString("data is not an object? that's not supposed to happen"));
+ }
+ auto obj = Json::ensureObject(doc.object(), "data");
+ //FIXME : HACK, MAY NOT WORK FOR LONG
+ out.url = QUrl(QString("https://media.forgecdn.net/files/%1/%2/%3")
+ .arg(QString::number(QString::number(out.fileId).leftRef(4).toInt())
+ ,QString::number(QString::number(out.fileId).rightRef(3).toInt())
+ ,QUrl::toPercentEncoding(out.fileName)), QUrl::TolerantMode);
+ }
+ failed &= fail;
} catch (const JSONValidationError& e) {
qCritical() << "Resolving of" << out.projectId << out.fileId << "failed because of a parsing error:";
qCritical() << e.cause();
diff --git a/launcher/modplatform/flame/FlameAPI.h b/launcher/modplatform/flame/FlameAPI.h
index 61628e60..8bb33d47 100644
--- a/launcher/modplatform/flame/FlameAPI.h
+++ b/launcher/modplatform/flame/FlameAPI.h
@@ -37,14 +37,14 @@ class FlameAPI : public NetworkModAPI {
.arg(args.offset)
.arg(args.search)
.arg(getSortFieldInt(args.sorting))
- .arg(getMappedModLoader(args.mod_loader))
+ .arg(getMappedModLoader(args.loaders))
.arg(gameVersionStr);
};
inline auto getVersionsURL(VersionSearchArgs& args) const -> QString override
{
QString gameVersionQuery = args.mcVersions.size() == 1 ? QString("gameVersion=%1&").arg(args.mcVersions.front().toString()) : "";
- QString modLoaderQuery = QString("modLoaderType=%1&").arg(getMappedModLoader(args.loader));
+ QString modLoaderQuery = QString("modLoaderType=%1&").arg(getMappedModLoader(args.loaders));
return QString("https://api.curseforge.com/v1/mods/%1/files?pageSize=10000&%2%3")
.arg(args.addonId)
@@ -53,11 +53,16 @@ class FlameAPI : public NetworkModAPI {
};
public:
- static auto getMappedModLoader(const ModLoaderType type) -> const ModLoaderType
+ static auto getMappedModLoader(const ModLoaderTypes loaders) -> const int
{
+ // https://docs.curseforge.com/?http#tocS_ModLoaderType
+ if (loaders & Forge)
+ return 1;
+ if (loaders & Fabric)
+ return 4;
// TODO: remove this once Quilt drops official Fabric support
- if (type == Quilt) // NOTE: Most if not all Fabric mods should work *currently*
- return Fabric;
- return type;
+ if (loaders & Quilt) // NOTE: Most if not all Fabric mods should work *currently*
+ return 4; // Quilt would probably be 5
+ return 0;
}
};
diff --git a/launcher/modplatform/flame/FlameModIndex.cpp b/launcher/modplatform/flame/FlameModIndex.cpp
index ba0824cf..9846b156 100644
--- a/launcher/modplatform/flame/FlameModIndex.cpp
+++ b/launcher/modplatform/flame/FlameModIndex.cpp
@@ -56,8 +56,15 @@ void FlameMod::loadIndexedPackVersions(ModPlatform::IndexedPack& pack,
file.fileId = Json::requireInteger(obj, "id");
file.date = Json::requireString(obj, "fileDate");
file.version = Json::requireString(obj, "displayName");
- file.downloadUrl = Json::requireString(obj, "downloadUrl");
file.fileName = Json::requireString(obj, "fileName");
+ file.downloadUrl = Json::ensureString(obj, "downloadUrl", "");
+ if(file.downloadUrl.isEmpty()){
+ //FIXME : HACK, MAY NOT WORK FOR LONG
+ file.downloadUrl = QString("https://media.forgecdn.net/files/%1/%2/%3")
+ .arg(QString::number(QString::number(file.fileId.toInt()).leftRef(4).toInt())
+ ,QString::number(QString::number(file.fileId.toInt()).rightRef(3).toInt())
+ ,QUrl::toPercentEncoding(file.fileName));
+ }
unsortedVersions.append(file);
}
diff --git a/launcher/modplatform/flame/FlamePackIndex.cpp b/launcher/modplatform/flame/FlamePackIndex.cpp
index ac24c647..6d48a3bf 100644
--- a/launcher/modplatform/flame/FlamePackIndex.cpp
+++ b/launcher/modplatform/flame/FlamePackIndex.cpp
@@ -65,7 +65,15 @@ void Flame::loadIndexedPackVersions(Flame::IndexedPack& pack, QJsonArray& arr)
// pick the latest version supported
file.mcVersion = versionArray[0].toString();
file.version = Json::requireString(version, "displayName");
- file.downloadUrl = Json::requireString(version, "downloadUrl");
+ file.fileName = Json::requireString(version, "fileName");
+ file.downloadUrl = Json::ensureString(version, "downloadUrl");
+ if(file.downloadUrl.isEmpty()){
+ //FIXME : HACK, MAY NOT WORK FOR LONG
+ file.downloadUrl = QString("https://media.forgecdn.net/files/%1/%2/%3")
+ .arg(QString::number(QString::number(file.fileId).leftRef(4).toInt())
+ ,QString::number(QString::number(file.fileId).rightRef(3).toInt())
+ ,QUrl::toPercentEncoding(file.fileName));
+ }
unsortedVersions.append(file);
}
diff --git a/launcher/modplatform/flame/FlamePackIndex.h b/launcher/modplatform/flame/FlamePackIndex.h
index 7ffa29c3..a8bb15be 100644
--- a/launcher/modplatform/flame/FlamePackIndex.h
+++ b/launcher/modplatform/flame/FlamePackIndex.h
@@ -18,6 +18,7 @@ struct IndexedVersion {
QString version;
QString mcVersion;
QString downloadUrl;
+ QString fileName;
};
struct IndexedPack
diff --git a/launcher/modplatform/flame/PackManifest.cpp b/launcher/modplatform/flame/PackManifest.cpp
index e4f90c1a..3217a756 100644
--- a/launcher/modplatform/flame/PackManifest.cpp
+++ b/launcher/modplatform/flame/PackManifest.cpp
@@ -71,11 +71,6 @@ bool Flame::File::parseFromBytes(const QByteArray& bytes)
fileName = Json::requireString(obj, "fileName");
- QString rawUrl = Json::requireString(obj, "downloadUrl");
- url = QUrl(rawUrl, QUrl::TolerantMode);
- if (!url.isValid()) {
- throw JSONValidationError(QString("Invalid URL: %1").arg(rawUrl));
- }
// This is a piece of a Flame project JSON pulled out into the file metadata (here) for convenience
// It is also optional
type = File::Type::SingleFile;
@@ -87,7 +82,17 @@ bool Flame::File::parseFromBytes(const QByteArray& bytes)
// this is probably a mod, dunno what else could modpacks download
targetFolder = "mods";
}
+ QString rawUrl = Json::ensureString(obj, "downloadUrl");
+ if(rawUrl.isEmpty()){
+ //either there somehow is an emtpy string as a link, or it's null either way it's invalid
+ //soft failing
+ return false;
+ }
+ url = QUrl(rawUrl, QUrl::TolerantMode);
+ if (!url.isValid()) {
+ throw JSONValidationError(QString("Invalid URL: %1").arg(rawUrl));
+ }
resolved = true;
return true;
}
diff --git a/launcher/modplatform/modrinth/ModrinthAPI.h b/launcher/modplatform/modrinth/ModrinthAPI.h
index 86852c94..79bc5175 100644
--- a/launcher/modplatform/modrinth/ModrinthAPI.h
+++ b/launcher/modplatform/modrinth/ModrinthAPI.h
@@ -1,5 +1,24 @@
+// SPDX-License-Identifier: GPL-3.0-only
+/*
+ * PolyMC - Minecraft Launcher
+ * Copyright (c) 2022 flowln <flowlnlnln@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/>.
+ */
+
#pragma once
+#include "BuildConfig.h"
#include "modplatform/ModAPI.h"
#include "modplatform/helpers/NetworkModAPI.h"
@@ -9,30 +28,25 @@ class ModrinthAPI : public NetworkModAPI {
public:
inline auto getAuthorURL(const QString& name) const -> QString { return "https://modrinth.com/user/" + name; };
- static auto getModLoaderStrings(ModLoaderType type) -> const QStringList
+ static auto getModLoaderStrings(const ModLoaderTypes types) -> const QStringList
{
QStringList l;
- switch (type)
+ for (auto loader : {Forge, Fabric, Quilt})
{
- case Unspecified:
- for (auto loader : {Forge, Fabric, Quilt})
- {
- l << ModAPI::getModLoaderString(loader);
- }
- break;
-
- case Quilt:
- l << ModAPI::getModLoaderString(Fabric);
- default:
- l << ModAPI::getModLoaderString(type);
+ if ((types & loader) || types == Unspecified)
+ {
+ l << ModAPI::getModLoaderString(loader);
+ }
}
+ if ((types & Quilt) && (~types & Fabric)) // Add Fabric if Quilt is in use, if Fabric isn't already there
+ l << ModAPI::getModLoaderString(Fabric);
return l;
}
- static auto getModLoaderFilters(ModLoaderType type) -> const QString
+ static auto getModLoaderFilters(ModLoaderTypes types) -> const QString
{
QStringList l;
- for (auto loader : getModLoaderStrings(type))
+ for (auto loader : getModLoaderStrings(types))
{
l << QString("\"categories:%1\"").arg(loader);
}
@@ -42,33 +56,34 @@ class ModrinthAPI : public NetworkModAPI {
private:
inline auto getModSearchURL(SearchArgs& args) const -> QString override
{
- if (!validateModLoader(args.mod_loader)) {
+ if (!validateModLoaders(args.loaders)) {
qWarning() << "Modrinth only have Forge and Fabric-compatible mods!";
return "";
}
- return QString(
- "https://api.modrinth.com/v2/search?"
- "offset=%1&"
- "limit=25&"
- "query=%2&"
- "index=%3&"
- "facets=[[%4],%5[\"project_type:mod\"]]")
+ return QString(BuildConfig.MODRINTH_PROD_URL +
+ "/search?"
+ "offset=%1&"
+ "limit=25&"
+ "query=%2&"
+ "index=%3&"
+ "facets=[[%4],%5[\"project_type:mod\"]]")
.arg(args.offset)
.arg(args.search)
.arg(args.sorting)
- .arg(getModLoaderFilters(args.mod_loader))
+ .arg(getModLoaderFilters(args.loaders))
.arg(getGameVersionsArray(args.versions));
};
inline auto getVersionsURL(VersionSearchArgs& args) const -> QString override
{
- return QString("https://api.modrinth.com/v2/project/%1/version?"
- "game_versions=[%2]"
- "loaders=[\"%3\"]")
+ return QString(BuildConfig.MODRINTH_PROD_URL +
+ "/project/%1/version?"
+ "game_versions=[%2]"
+ "loaders=[\"%3\"]")
.arg(args.addonId)
.arg(getGameVersionsString(args.mcVersions))
- .arg(getModLoaderStrings(args.loader).join("\",\""));
+ .arg(getModLoaderStrings(args.loaders).join("\",\""));
};
auto getGameVersionsArray(std::list<Version> mcVersions) const -> QString
@@ -81,9 +96,9 @@ class ModrinthAPI : public NetworkModAPI {
return s.isEmpty() ? QString() : QString("[%1],").arg(s);
}
- inline auto validateModLoader(ModLoaderType modLoader) const -> bool
+ inline auto validateModLoaders(ModLoaderTypes loaders) const -> bool
{
- return modLoader == Unspecified || modLoader == Forge || modLoader == Fabric || modLoader == Quilt;
+ return (loaders == Unspecified) || (loaders & (Forge | Fabric | Quilt));
}
};
diff --git a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp
index a3c2f166..f7fa9864 100644
--- a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp
+++ b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp
@@ -1,3 +1,20 @@
+// SPDX-License-Identifier: GPL-3.0-only
+/*
+ * PolyMC - Minecraft Launcher
+ *
+ * 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 "ModrinthPackIndex.h"
#include "ModrinthAPI.h"
diff --git a/launcher/modplatform/modrinth/ModrinthPackIndex.h b/launcher/modplatform/modrinth/ModrinthPackIndex.h
index fd17847a..7f306f25 100644
--- a/launcher/modplatform/modrinth/ModrinthPackIndex.h
+++ b/launcher/modplatform/modrinth/ModrinthPackIndex.h
@@ -1,3 +1,20 @@
+// SPDX-License-Identifier: GPL-3.0-only
+/*
+ * PolyMC - Minecraft Launcher
+ *
+ * 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/>.
+ */
+
#pragma once
#include "modplatform/ModIndex.h"
diff --git a/launcher/modplatform/modrinth/ModrinthPackManifest.cpp b/launcher/modplatform/modrinth/ModrinthPackManifest.cpp
new file mode 100644
index 00000000..f1ad39ce
--- /dev/null
+++ b/launcher/modplatform/modrinth/ModrinthPackManifest.cpp
@@ -0,0 +1,156 @@
+// SPDX-License-Identifier: GPL-3.0-only
+/*
+ * PolyMC - Minecraft Launcher
+ * Copyright (c) 2022 flowln <flowlnlnln@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/>.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Copyright 2013-2021 MultiMC Contributors
+ * Copyright 2022 kb1000
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ModrinthPackManifest.h"
+#include "Json.h"
+
+#include "modplatform/modrinth/ModrinthAPI.h"
+
+#include "minecraft/MinecraftInstance.h"
+#include "minecraft/PackProfile.h"
+
+static ModrinthAPI api;
+
+namespace Modrinth {
+
+void loadIndexedPack(Modpack& pack, QJsonObject& obj)
+{
+ pack.id = Json::ensureString(obj, "project_id");
+
+ pack.name = Json::ensureString(obj, "title");
+ pack.description = Json::ensureString(obj, "description");
+ auto temp_author_name = Json::ensureString(obj, "author");
+ pack.author = std::make_tuple(temp_author_name, api.getAuthorURL(temp_author_name));
+ pack.iconName = QString("modrinth_%1").arg(Json::ensureString(obj, "slug"));
+ pack.iconUrl = Json::ensureString(obj, "icon_url");
+}
+
+void loadIndexedInfo(Modpack& pack, QJsonObject& obj)
+{
+ pack.extra.body = Json::ensureString(obj, "body");
+ pack.extra.projectUrl = QString("https://modrinth.com/modpack/%1").arg(Json::ensureString(obj, "slug"));
+ pack.extra.sourceUrl = Json::ensureString(obj, "source_url");
+ pack.extra.wikiUrl = Json::ensureString(obj, "wiki_url");
+
+ pack.extraInfoLoaded = true;
+}
+
+void loadIndexedVersions(Modpack& pack, QJsonDocument& doc)
+{
+ QVector<ModpackVersion> unsortedVersions;
+
+ auto arr = Json::requireArray(doc);
+
+ for (auto versionIter : arr) {
+ auto obj = Json::requireObject(versionIter);
+ auto file = loadIndexedVersion(obj);
+
+ if(!file.id.isEmpty()) // Heuristic to check if the returned value is valid
+ unsortedVersions.append(file);
+ }
+ auto orderSortPredicate = [](const ModpackVersion& a, const ModpackVersion& b) -> bool {
+ // dates are in RFC 3339 format
+ return a.date > b.date;
+ };
+
+ std::sort(unsortedVersions.begin(), unsortedVersions.end(), orderSortPredicate);
+
+ pack.versions.swap(unsortedVersions);
+
+ pack.versionsLoaded = true;
+}
+
+auto validateDownloadUrl(QUrl url) -> bool
+{
+ auto domain = url.host();
+ if(domain == "cdn.modrinth.com")
+ return true;
+ if(domain == "edge.forgecdn.net")
+ return true;
+ if(domain == "media.forgecdn.net")
+ return true;
+ if(domain == "github.com")
+ return true;
+ if(domain == "raw.githubusercontent.com")
+ return true;
+
+ return false;
+}
+
+auto loadIndexedVersion(QJsonObject &obj) -> ModpackVersion
+{
+ ModpackVersion file;
+
+ file.name = Json::requireString(obj, "name");
+ file.version = Json::requireString(obj, "version_number");
+
+ file.id = Json::requireString(obj, "id");
+ file.project_id = Json::requireString(obj, "project_id");
+
+ file.date = Json::requireString(obj, "date_published");
+
+ auto files = Json::requireArray(obj, "files");
+
+
+ for (auto file_iter : files) {
+ File indexed_file;
+ auto parent = Json::requireObject(file_iter);
+ auto is_primary = Json::ensureBoolean(parent, "primary", false);
+ if (!is_primary) {
+ auto filename = Json::ensureString(parent, "filename");
+ // Checking suffix here is fine because it's the response from Modrinth,
+ // so one would assume it will always be in English.
+ if(!filename.endsWith("mrpack") && !filename.endsWith("zip"))
+ continue;
+ }
+
+ auto url = Json::requireString(parent, "url");
+
+ if(!validateDownloadUrl(url))
+ continue;
+
+ file.download_url = url;
+ if(is_primary)
+ break;
+ }
+
+ if(file.download_url.isEmpty())
+ return {};
+
+ return file;
+}
+
+} // namespace Modrinth
diff --git a/launcher/modplatform/modrinth/ModrinthPackManifest.h b/launcher/modplatform/modrinth/ModrinthPackManifest.h
new file mode 100644
index 00000000..e5fc9a70
--- /dev/null
+++ b/launcher/modplatform/modrinth/ModrinthPackManifest.h
@@ -0,0 +1,107 @@
+// SPDX-License-Identifier: GPL-3.0-only
+/*
+ * PolyMC - Minecraft Launcher
+ * Copyright (c) 2022 flowln <flowlnlnln@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/>.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Copyright 2013-2021 MultiMC Contributors
+ * Copyright 2022 kb1000
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <QMetaType>
+
+#include <QByteArray>
+#include <QCryptographicHash>
+#include <QString>
+#include <QUrl>
+#include <QVector>
+
+class MinecraftInstance;
+
+namespace Modrinth {
+
+struct File
+{
+ QString path;
+
+ QCryptographicHash::Algorithm hashAlgorithm;
+ QByteArray hash;
+ // TODO: should this support multiple download URLs, like the JSON does?
+ QUrl download;
+};
+
+struct ModpackExtra {
+ QString body;
+
+ QString projectUrl;
+ QString sourceUrl;
+ QString wikiUrl;
+};
+
+struct ModpackVersion {
+ QString name;
+ QString version;
+
+ QString id;
+ QString project_id;
+
+ QString date;
+
+ QString download_url;
+};
+
+struct Modpack {
+ QString id;
+
+ QString name;
+ QString description;
+ std::tuple<QString, QUrl> author;
+ QString iconName;
+ QUrl iconUrl;
+
+ bool versionsLoaded = false;
+ bool extraInfoLoaded = false;
+
+ ModpackExtra extra;
+ QVector<ModpackVersion> versions;
+};
+
+void loadIndexedPack(Modpack&, QJsonObject&);
+void loadIndexedInfo(Modpack&, QJsonObject&);
+void loadIndexedVersions(Modpack&, QJsonDocument&);
+auto loadIndexedVersion(QJsonObject&) -> ModpackVersion;
+
+auto validateDownloadUrl(QUrl) -> bool;
+
+}
+
+Q_DECLARE_METATYPE(Modrinth::Modpack)
+Q_DECLARE_METATYPE(Modrinth::ModpackVersion)
diff --git a/launcher/modplatform/technic/SolderPackManifest.cpp b/launcher/modplatform/technic/SolderPackManifest.cpp
index 16fe0b0e..e52a7ec0 100644
--- a/launcher/modplatform/technic/SolderPackManifest.cpp
+++ b/launcher/modplatform/technic/SolderPackManifest.cpp
@@ -37,7 +37,7 @@ void loadPack(Pack& v, QJsonObject& obj)
static void loadPackBuildMod(PackBuildMod& b, QJsonObject& obj)
{
b.name = Json::requireString(obj, "name");
- b.version = Json::requireString(obj, "version");
+ b.version = Json::ensureString(obj, "version", "");
b.md5 = Json::requireString(obj, "md5");
b.url = Json::requireString(obj, "url");
}