summaryrefslogtreecommitdiff
path: root/meshmc/launcher/modplatform/modpacksch
diff options
context:
space:
mode:
Diffstat (limited to 'meshmc/launcher/modplatform/modpacksch')
-rw-r--r--meshmc/launcher/modplatform/modpacksch/FTBPackInstallTask.cpp280
-rw-r--r--meshmc/launcher/modplatform/modpacksch/FTBPackInstallTask.h88
-rw-r--r--meshmc/launcher/modplatform/modpacksch/FTBPackManifest.cpp205
-rw-r--r--meshmc/launcher/modplatform/modpacksch/FTBPackManifest.h154
4 files changed, 727 insertions, 0 deletions
diff --git a/meshmc/launcher/modplatform/modpacksch/FTBPackInstallTask.cpp b/meshmc/launcher/modplatform/modpacksch/FTBPackInstallTask.cpp
new file mode 100644
index 0000000000..6c54cd6bcb
--- /dev/null
+++ b/meshmc/launcher/modplatform/modpacksch/FTBPackInstallTask.cpp
@@ -0,0 +1,280 @@
+/* SPDX-FileCopyrightText: 2026 Project Tick
+ * SPDX-FileContributor: Project Tick
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ *
+ * MeshMC - A Custom Launcher for Minecraft
+ * Copyright (C) 2026 Project Tick
+ *
+ * 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 <https://www.gnu.org/licenses/>.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Copyright 2020-2021 Jamie Mansfield <jmansfield@cadixdev.org>
+ * Copyright 2020-2021 Petr Mrazek <peterix@gmail.com>
+ *
+ * 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 "FTBPackInstallTask.h"
+
+#include "FileSystem.h"
+#include "Json.h"
+#include "minecraft/MinecraftInstance.h"
+#include "minecraft/PackProfile.h"
+#include "net/ChecksumValidator.h"
+#include "settings/INISettingsObject.h"
+
+#include "BuildConfig.h"
+#include "Application.h"
+
+namespace ModpacksCH
+{
+
+ PackInstallTask::PackInstallTask(Modpack pack, QString version)
+ {
+ m_pack = pack;
+ m_version_name = version;
+ }
+
+ bool PackInstallTask::abort()
+ {
+ if (abortable) {
+ return jobPtr->abort();
+ }
+ return false;
+ }
+
+ void PackInstallTask::executeTask()
+ {
+ // Find pack version
+ bool found = false;
+ VersionInfo version;
+
+ for (auto vInfo : m_pack.versions) {
+ if (vInfo.name == m_version_name) {
+ found = true;
+ version = vInfo;
+ break;
+ }
+ }
+
+ if (!found) {
+ emitFailed(
+ tr("Failed to find pack version %1").arg(m_version_name));
+ return;
+ }
+
+ auto* netJob =
+ new NetJob("ModpacksCH::VersionFetch", APPLICATION->network());
+ auto searchUrl = QString(BuildConfig.MODPACKSCH_API_BASE_URL +
+ "public/modpack/%1/%2")
+ .arg(m_pack.id)
+ .arg(version.id);
+ netJob->addNetAction(
+ Net::Download::makeByteArray(QUrl(searchUrl), &response));
+ jobPtr = netJob;
+ jobPtr->start();
+
+ QObject::connect(netJob, &NetJob::succeeded, this,
+ &PackInstallTask::onDownloadSucceeded);
+ QObject::connect(netJob, &NetJob::failed, this,
+ &PackInstallTask::onDownloadFailed);
+ }
+
+ void PackInstallTask::onDownloadSucceeded()
+ {
+ QJsonParseError parse_error;
+ QJsonDocument doc = QJsonDocument::fromJson(response, &parse_error);
+ if (parse_error.error != QJsonParseError::NoError) {
+ qWarning() << "Error while parsing JSON response from FTB at "
+ << parse_error.offset
+ << " reason: " << parse_error.errorString();
+ qWarning() << response;
+ return;
+ }
+
+ auto obj = doc.object();
+
+ ModpacksCH::Version version;
+ try {
+ ModpacksCH::loadVersion(version, obj);
+ } catch (const JSONValidationError& e) {
+ emitFailed(tr("Could not understand pack manifest:\n") + e.cause());
+ jobPtr.reset();
+ return;
+ }
+ m_version = version;
+
+ downloadPack();
+ }
+
+ void PackInstallTask::onDownloadFailed(QString reason)
+ {
+ emitFailed(reason);
+ jobPtr.reset();
+ }
+
+ void PackInstallTask::downloadPack()
+ {
+ setStatus(tr("Downloading mods..."));
+
+ jobPtr = new NetJob(tr("Mod download"), APPLICATION->network());
+ for (auto file : m_version.files) {
+ if (file.serverOnly)
+ continue;
+ if (file.url.isEmpty()) {
+ qWarning() << "Skipping" << file.name
+ << "- no download URL available";
+ continue;
+ }
+
+ QFileInfo fileName(file.name);
+ auto cacheName = fileName.completeBaseName() + "-" + file.sha1 +
+ "." + fileName.suffix();
+
+ auto entry = APPLICATION->metacache()->resolveEntry(
+ "ModpacksCHPacks", cacheName);
+ entry->setStale(true);
+
+ auto relpath = FS::PathCombine("minecraft", file.path, file.name);
+ auto path = FS::PathCombine(m_stagingPath, relpath);
+
+ if (filesToCopy.contains(path)) {
+ qWarning() << "Ignoring" << file.url
+ << "as a file of that path is already downloading.";
+ continue;
+ }
+ qDebug() << "Will download" << file.url << "to" << path;
+ filesToCopy[path] = entry->getFullPath();
+
+ auto dl = Net::Download::makeCached(file.url, entry);
+ if (!file.sha1.isEmpty()) {
+ auto rawSha1 = QByteArray::fromHex(file.sha1.toLatin1());
+ dl->addValidator(new Net::ChecksumValidator(
+ QCryptographicHash::Sha1, rawSha1));
+ }
+ jobPtr->addNetAction(dl);
+ }
+
+ connect(jobPtr.get(), &NetJob::succeeded, this, [&]() {
+ abortable = false;
+ install();
+ jobPtr.reset();
+ });
+ connect(jobPtr.get(), &NetJob::failed, [&](QString reason) {
+ abortable = false;
+ emitFailed(reason);
+ jobPtr.reset();
+ });
+ connect(jobPtr.get(), &NetJob::progress,
+ [&](qint64 current, qint64 total) {
+ abortable = true;
+ setProgress(current, total);
+ });
+
+ jobPtr->start();
+ }
+
+ void PackInstallTask::install()
+ {
+ setStatus(tr("Copying modpack files"));
+
+ for (auto iter = filesToCopy.begin(); iter != filesToCopy.end();
+ iter++) {
+ auto& to = iter.key();
+ auto& from = iter.value();
+ FS::copy fileCopyOperation(from, to);
+ if (!fileCopyOperation()) {
+ qWarning() << "Failed to copy" << from << "to" << to;
+ emitFailed(tr("Failed to copy files"));
+ return;
+ }
+ }
+
+ setStatus(tr("Installing modpack"));
+
+ auto instanceConfigPath =
+ FS::PathCombine(m_stagingPath, "instance.cfg");
+ auto instanceSettings =
+ std::make_shared<INISettingsObject>(instanceConfigPath);
+ instanceSettings->suspendSave();
+ instanceSettings->registerSetting("InstanceType", "Legacy");
+ instanceSettings->set("InstanceType", "OneSix");
+
+ MinecraftInstance instance(m_globalSettings, instanceSettings,
+ m_stagingPath);
+ auto components = instance.getPackProfile();
+ components->buildingFromScratch();
+
+ for (auto target : m_version.targets) {
+ if (target.type == "game" && target.name == "minecraft") {
+ components->setComponentVersion("net.minecraft", target.version,
+ true);
+ break;
+ }
+ }
+
+ for (auto target : m_version.targets) {
+ if (target.type != "modloader")
+ continue;
+
+ if (target.name == "forge") {
+ components->setComponentVersion("net.minecraftforge",
+ target.version, true);
+ } else if (target.name == "neoforge") {
+ components->setComponentVersion("net.neoforged", target.version,
+ true);
+ } else if (target.name == "fabric") {
+ components->setComponentVersion("net.fabricmc.fabric-loader",
+ target.version, true);
+ } else if (target.name == "quilt-loader") {
+ components->setComponentVersion("org.quiltmc.quilt-loader",
+ target.version, true);
+ }
+ }
+
+ // install any jar mods
+ QDir jarModsDir(FS::PathCombine(m_stagingPath, "minecraft", "jarmods"));
+ if (jarModsDir.exists()) {
+ QStringList jarMods;
+
+ for (const auto& info :
+ jarModsDir.entryInfoList(QDir::NoDotAndDotDot | QDir::Files)) {
+ jarMods.push_back(info.absoluteFilePath());
+ }
+
+ components->installJarMods(jarMods);
+ }
+
+ components->saveNow();
+
+ instance.setName(m_instName);
+ instance.setIconKey(m_instIcon);
+ instanceSettings->resumeSave();
+
+ emitSucceeded();
+ }
+
+} // namespace ModpacksCH
diff --git a/meshmc/launcher/modplatform/modpacksch/FTBPackInstallTask.h b/meshmc/launcher/modplatform/modpacksch/FTBPackInstallTask.h
new file mode 100644
index 0000000000..bc18e1c2c0
--- /dev/null
+++ b/meshmc/launcher/modplatform/modpacksch/FTBPackInstallTask.h
@@ -0,0 +1,88 @@
+/* SPDX-FileCopyrightText: 2026 Project Tick
+ * SPDX-FileContributor: Project Tick
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ *
+ * MeshMC - A Custom Launcher for Minecraft
+ * Copyright (C) 2026 Project Tick
+ *
+ * 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 <https://www.gnu.org/licenses/>.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Copyright 2020-2021 Jamie Mansfield <jmansfield@cadixdev.org>
+ * Copyright 2020-2021 Petr Mrazek <peterix@gmail.com>
+ *
+ * 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 "FTBPackManifest.h"
+
+#include "InstanceTask.h"
+#include "net/NetJob.h"
+
+namespace ModpacksCH
+{
+
+ class PackInstallTask : public InstanceTask
+ {
+ Q_OBJECT
+
+ public:
+ explicit PackInstallTask(Modpack pack, QString version);
+ virtual ~PackInstallTask() {}
+
+ bool canAbort() const override
+ {
+ return true;
+ }
+ bool abort() override;
+
+ protected:
+ virtual void executeTask() override;
+
+ private slots:
+ void onDownloadSucceeded();
+ void onDownloadFailed(QString reason);
+
+ private:
+ void downloadPack();
+ void install();
+
+ private:
+ bool abortable = false;
+
+ NetJob::Ptr jobPtr;
+ QByteArray response;
+
+ Modpack m_pack;
+ QString m_version_name;
+ Version m_version;
+
+ QMap<QString, QString> filesToCopy;
+ };
+
+} // namespace ModpacksCH
diff --git a/meshmc/launcher/modplatform/modpacksch/FTBPackManifest.cpp b/meshmc/launcher/modplatform/modpacksch/FTBPackManifest.cpp
new file mode 100644
index 0000000000..afbf59736c
--- /dev/null
+++ b/meshmc/launcher/modplatform/modpacksch/FTBPackManifest.cpp
@@ -0,0 +1,205 @@
+/* SPDX-FileCopyrightText: 2026 Project Tick
+ * SPDX-FileContributor: Project Tick
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ *
+ * MeshMC - A Custom Launcher for Minecraft
+ * Copyright (C) 2026 Project Tick
+ *
+ * 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 <https://www.gnu.org/licenses/>.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Copyright 2020 Jamie Mansfield <jmansfield@cadixdev.org>
+ * Copyright 2020-2021 Petr Mrazek <peterix@gmail.com>
+ *
+ * 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 "FTBPackManifest.h"
+
+#include "Json.h"
+
+static void loadSpecs(ModpacksCH::Specs& s, QJsonObject& obj)
+{
+ s.id = Json::requireInteger(obj, "id");
+ s.minimum = Json::requireInteger(obj, "minimum");
+ s.recommended = Json::requireInteger(obj, "recommended");
+}
+
+static void loadTag(ModpacksCH::Tag& t, QJsonObject& obj)
+{
+ t.id = Json::requireInteger(obj, "id");
+ t.name = Json::requireString(obj, "name");
+}
+
+static void loadArt(ModpacksCH::Art& a, QJsonObject& obj)
+{
+ a.id = Json::requireInteger(obj, "id");
+ a.url = Json::requireString(obj, "url");
+ a.type = Json::requireString(obj, "type");
+ a.width = Json::requireInteger(obj, "width");
+ a.height = Json::requireInteger(obj, "height");
+ a.compressed = Json::requireBoolean(obj, "compressed");
+ a.sha1 = Json::requireString(obj, "sha1");
+ a.size = Json::requireInteger(obj, "size");
+ a.updated = Json::requireInteger(obj, "updated");
+}
+
+static void loadAuthor(ModpacksCH::Author& a, QJsonObject& obj)
+{
+ a.id = Json::requireInteger(obj, "id");
+ a.name = Json::requireString(obj, "name");
+ a.type = Json::requireString(obj, "type");
+ a.website = Json::requireString(obj, "website");
+ a.updated = Json::requireInteger(obj, "updated");
+}
+
+static void loadVersionInfo(ModpacksCH::VersionInfo& v, QJsonObject& obj)
+{
+ v.id = Json::requireInteger(obj, "id");
+ v.name = Json::requireString(obj, "name");
+ v.type = Json::requireString(obj, "type");
+ v.updated = Json::requireInteger(obj, "updated");
+ auto specs = Json::requireObject(obj, "specs");
+ loadSpecs(v.specs, specs);
+}
+
+void ModpacksCH::loadModpack(ModpacksCH::Modpack& m, QJsonObject& obj)
+{
+ m.id = Json::requireInteger(obj, "id");
+ m.name = Json::requireString(obj, "name");
+ m.synopsis = Json::requireString(obj, "synopsis");
+ m.description = Json::requireString(obj, "description");
+ m.type = Json::requireString(obj, "type");
+ m.featured = Json::requireBoolean(obj, "featured");
+ m.installs = Json::requireInteger(obj, "installs");
+ m.plays = Json::requireInteger(obj, "plays");
+ m.updated = Json::requireInteger(obj, "updated");
+ m.refreshed = Json::requireInteger(obj, "refreshed");
+ auto artArr = Json::requireArray(obj, "art");
+ for (QJsonValueRef artRaw : artArr) {
+ auto artObj = Json::requireObject(artRaw);
+ ModpacksCH::Art art;
+ loadArt(art, artObj);
+ m.art.append(art);
+ }
+ auto authorArr = Json::requireArray(obj, "authors");
+ for (QJsonValueRef authorRaw : authorArr) {
+ auto authorObj = Json::requireObject(authorRaw);
+ ModpacksCH::Author author;
+ loadAuthor(author, authorObj);
+ m.authors.append(author);
+ }
+ auto versionArr = Json::requireArray(obj, "versions");
+ for (QJsonValueRef versionRaw : versionArr) {
+ auto versionObj = Json::requireObject(versionRaw);
+ ModpacksCH::VersionInfo version;
+ loadVersionInfo(version, versionObj);
+ m.versions.append(version);
+ }
+ auto tagArr = Json::requireArray(obj, "tags");
+ for (QJsonValueRef tagRaw : tagArr) {
+ auto tagObj = Json::requireObject(tagRaw);
+ ModpacksCH::Tag tag;
+ loadTag(tag, tagObj);
+ m.tags.append(tag);
+ }
+ m.updated = Json::requireInteger(obj, "updated");
+}
+
+static void loadVersionTarget(ModpacksCH::VersionTarget& a, QJsonObject& obj)
+{
+ a.id = Json::requireInteger(obj, "id");
+ a.name = Json::requireString(obj, "name");
+ a.type = Json::requireString(obj, "type");
+ a.version = Json::requireString(obj, "version");
+ a.updated = Json::requireInteger(obj, "updated");
+}
+
+static void loadVersionFile(ModpacksCH::VersionFile& a, QJsonObject& obj)
+{
+ a.id = Json::requireInteger(obj, "id");
+ a.type = Json::requireString(obj, "type");
+ a.path = Json::requireString(obj, "path");
+ a.name = Json::requireString(obj, "name");
+ a.version = Json::requireString(obj, "version");
+ a.url = Json::ensureString(obj, "url", QString());
+ a.sha1 = Json::ensureString(obj, "sha1", QString());
+ a.size = Json::requireInteger(obj, "size");
+ a.clientOnly = Json::requireBoolean(obj, "clientonly");
+ a.serverOnly = Json::requireBoolean(obj, "serveronly");
+ a.optional = Json::requireBoolean(obj, "optional");
+ a.updated = Json::requireInteger(obj, "updated");
+ // Some files reference CurseForge mods with no direct download URL.
+ // Construct edge CDN URL from CurseForge file ID if available.
+ if (a.url.isEmpty() && obj.contains("curseforge")) {
+ auto cf = Json::requireObject(obj, "curseforge");
+ int cfFileId = Json::requireInteger(cf, "file");
+ // CurseForge edge CDN URL format: files/{first 4 digits}/{remaining
+ // digits}/{filename}
+ QString fileIdStr = QString::number(cfFileId);
+ QString prefix = fileIdStr.mid(0, 4);
+ QString suffix = fileIdStr.mid(4);
+ a.url = QString("https://edge.forgecdn.net/files/%1/%2/%3")
+ .arg(prefix, suffix, a.name);
+ qDebug() << "Constructed CurseForge CDN URL for" << a.name << ":"
+ << a.url;
+ }
+}
+
+void ModpacksCH::loadVersion(ModpacksCH::Version& m, QJsonObject& obj)
+{
+ m.id = Json::requireInteger(obj, "id");
+ m.parent = Json::requireInteger(obj, "parent");
+ m.name = Json::requireString(obj, "name");
+ m.type = Json::requireString(obj, "type");
+ m.installs = Json::requireInteger(obj, "installs");
+ m.plays = Json::requireInteger(obj, "plays");
+ m.updated = Json::requireInteger(obj, "updated");
+ m.refreshed = Json::requireInteger(obj, "refreshed");
+ auto specs = Json::requireObject(obj, "specs");
+ loadSpecs(m.specs, specs);
+ auto targetArr = Json::requireArray(obj, "targets");
+ for (QJsonValueRef targetRaw : targetArr) {
+ auto versionObj = Json::requireObject(targetRaw);
+ ModpacksCH::VersionTarget target;
+ loadVersionTarget(target, versionObj);
+ m.targets.append(target);
+ }
+ auto fileArr = Json::requireArray(obj, "files");
+ for (QJsonValueRef fileRaw : fileArr) {
+ auto fileObj = Json::requireObject(fileRaw);
+ ModpacksCH::VersionFile file;
+ loadVersionFile(file, fileObj);
+ m.files.append(file);
+ }
+}
+
+// static void loadVersionChangelog(ModpacksCH::VersionChangelog & m,
+// QJsonObject & obj)
+//{
+// m.content = Json::requireString(obj, "content");
+// m.updated = Json::requireInteger(obj, "updated");
+// }
diff --git a/meshmc/launcher/modplatform/modpacksch/FTBPackManifest.h b/meshmc/launcher/modplatform/modpacksch/FTBPackManifest.h
new file mode 100644
index 0000000000..3a0d7655a3
--- /dev/null
+++ b/meshmc/launcher/modplatform/modpacksch/FTBPackManifest.h
@@ -0,0 +1,154 @@
+/* SPDX-FileCopyrightText: 2026 Project Tick
+ * SPDX-FileContributor: Project Tick
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ *
+ * MeshMC - A Custom Launcher for Minecraft
+ * Copyright (C) 2026 Project Tick
+ *
+ * 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 <https://www.gnu.org/licenses/>.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Copyright 2020-2021 Jamie Mansfield <jmansfield@cadixdev.org>
+ * Copyright 2020 Petr Mrazek <peterix@gmail.com>
+ *
+ * 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 <QString>
+#include <QVector>
+#include <QUrl>
+#include <QJsonObject>
+#include <QMetaType>
+
+namespace ModpacksCH
+{
+
+ struct Specs {
+ int id;
+ int minimum;
+ int recommended;
+ };
+
+ struct Tag {
+ int id;
+ QString name;
+ };
+
+ struct Art {
+ int id;
+ QString url;
+ QString type;
+ int width;
+ int height;
+ bool compressed;
+ QString sha1;
+ int size;
+ int64_t updated;
+ };
+
+ struct Author {
+ int id;
+ QString name;
+ QString type;
+ QString website;
+ int64_t updated;
+ };
+
+ struct VersionInfo {
+ int id;
+ QString name;
+ QString type;
+ int64_t updated;
+ Specs specs;
+ };
+
+ struct Modpack {
+ int id;
+ QString name;
+ QString synopsis;
+ QString description;
+ QString type;
+ bool featured;
+ int installs;
+ int plays;
+ int64_t updated;
+ int64_t refreshed;
+ QVector<Art> art;
+ QVector<Author> authors;
+ QVector<VersionInfo> versions;
+ QVector<Tag> tags;
+ };
+
+ struct VersionTarget {
+ int id;
+ QString type;
+ QString name;
+ QString version;
+ int64_t updated;
+ };
+
+ struct VersionFile {
+ int id;
+ QString type;
+ QString path;
+ QString name;
+ QString version;
+ QString url;
+ QString sha1;
+ int size;
+ bool clientOnly;
+ bool serverOnly;
+ bool optional;
+ int64_t updated;
+ };
+
+ struct Version {
+ int id;
+ int parent;
+ QString name;
+ QString type;
+ int installs;
+ int plays;
+ int64_t updated;
+ int64_t refreshed;
+ Specs specs;
+ QVector<VersionTarget> targets;
+ QVector<VersionFile> files;
+ };
+
+ struct VersionChangelog {
+ QString content;
+ int64_t updated;
+ };
+
+ void loadModpack(Modpack& m, QJsonObject& obj);
+
+ void loadVersion(Version& m, QJsonObject& obj);
+} // namespace ModpacksCH
+
+Q_DECLARE_METATYPE(ModpacksCH::Modpack)