aboutsummaryrefslogtreecommitdiff
path: root/launcher
diff options
context:
space:
mode:
Diffstat (limited to 'launcher')
-rw-r--r--launcher/Application.cpp16
-rw-r--r--launcher/CMakeLists.txt13
-rw-r--r--launcher/DesktopServices.cpp24
-rw-r--r--launcher/DesktopServices.h13
-rw-r--r--launcher/minecraft/MinecraftInstance.cpp41
-rw-r--r--launcher/minecraft/MinecraftInstance.h2
-rw-r--r--launcher/minecraft/launch/DirectJavaLaunch.cpp166
-rw-r--r--launcher/minecraft/launch/DirectJavaLaunch.h58
-rw-r--r--launcher/minecraft/mod/tasks/LocalModParseTask.cpp9
-rw-r--r--launcher/modplatform/import_ftb/PackHelpers.cpp87
-rw-r--r--launcher/modplatform/import_ftb/PackHelpers.h55
-rw-r--r--launcher/modplatform/import_ftb/PackInstallTask.cpp99
-rw-r--r--launcher/modplatform/import_ftb/PackInstallTask.h49
-rw-r--r--launcher/settings/INIFile.cpp21
-rw-r--r--launcher/settings/INIFile.h1
-rw-r--r--launcher/ui/MainWindow.cpp14
-rw-r--r--launcher/ui/dialogs/AboutDialog.cpp2
-rw-r--r--launcher/ui/dialogs/ExportInstanceDialog.cpp2
-rw-r--r--launcher/ui/dialogs/ExportPackDialog.cpp2
-rw-r--r--launcher/ui/dialogs/NewInstanceDialog.cpp24
-rw-r--r--launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.cpp104
-rw-r--r--launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.h67
-rw-r--r--launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.ui28
-rw-r--r--launcher/ui/pages/modplatform/import_ftb/ListModel.cpp88
-rw-r--r--launcher/ui/pages/modplatform/import_ftb/ListModel.h46
25 files changed, 725 insertions, 306 deletions
diff --git a/launcher/Application.cpp b/launcher/Application.cpp
index aeea90f1..b538fd23 100644
--- a/launcher/Application.cpp
+++ b/launcher/Application.cpp
@@ -281,7 +281,16 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
}
else
{
- QDir foo(FS::PathCombine(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation), ".."));
+ QDir foo;
+ if (DesktopServices::isSnap())
+ {
+ foo = QDir(getenv("SNAP_USER_COMMON"));
+ }
+ else
+ {
+ foo = QDir(FS::PathCombine(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation), ".."));
+ }
+
dataPath = foo.absolutePath();
adjustedBy = "Persistent data path";
@@ -628,9 +637,6 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
m_settings->registerSetting("ShowGlobalGameTime", true);
m_settings->registerSetting("RecordGameTime", true);
- // Minecraft launch method
- m_settings->registerSetting("MCLaunchMethod", "LauncherPart");
-
// Minecraft mods
m_settings->registerSetting("ModMetadataDisabled", false);
@@ -704,7 +710,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
QUrl metaUrl(m_settings->get("MetaURLOverride").toString());
// get rid of invalid meta urls
- if (!metaUrl.isValid() || metaUrl.scheme() != "http" || metaUrl.scheme() != "https")
+ if (!metaUrl.isValid() || (metaUrl.scheme() != "http" && metaUrl.scheme() != "https"))
m_settings->reset("MetaURLOverride");
}
diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt
index 2d06dbf4..6b9ae4d5 100644
--- a/launcher/CMakeLists.txt
+++ b/launcher/CMakeLists.txt
@@ -262,8 +262,6 @@ set(MINECRAFT_SOURCES
minecraft/launch/CreateGameFolders.h
minecraft/launch/ModMinecraftJar.cpp
minecraft/launch/ModMinecraftJar.h
- minecraft/launch/DirectJavaLaunch.cpp
- minecraft/launch/DirectJavaLaunch.h
minecraft/launch/ExtractNatives.cpp
minecraft/launch/ExtractNatives.h
minecraft/launch/LauncherPartLaunch.cpp
@@ -501,6 +499,11 @@ set(FTB_SOURCES
modplatform/legacy_ftb/PrivatePackManager.cpp
modplatform/legacy_ftb/PackHelpers.h
+
+ modplatform/import_ftb/PackInstallTask.h
+ modplatform/import_ftb/PackInstallTask.cpp
+ modplatform/import_ftb/PackHelpers.h
+ modplatform/import_ftb/PackHelpers.cpp
)
set(FLAME_SOURCES
@@ -872,6 +875,11 @@ SET(LAUNCHER_SOURCES
ui/pages/modplatform/legacy_ftb/ListModel.h
ui/pages/modplatform/legacy_ftb/ListModel.cpp
+ ui/pages/modplatform/import_ftb/ImportFTBPage.cpp
+ ui/pages/modplatform/import_ftb/ImportFTBPage.h
+ ui/pages/modplatform/import_ftb/ListModel.h
+ ui/pages/modplatform/import_ftb/ListModel.cpp
+
ui/pages/modplatform/flame/FlameModel.cpp
ui/pages/modplatform/flame/FlameModel.h
ui/pages/modplatform/flame/FlamePage.cpp
@@ -1046,6 +1054,7 @@ qt_wrap_ui(LAUNCHER_UI
ui/pages/modplatform/ResourcePage.ui
ui/pages/modplatform/flame/FlamePage.ui
ui/pages/modplatform/legacy_ftb/Page.ui
+ ui/pages/modplatform/import_ftb/ImportFTBPage.ui
ui/pages/modplatform/ImportPage.ui
ui/pages/modplatform/modrinth/ModrinthPage.ui
ui/pages/modplatform/technic/TechnicPage.ui
diff --git a/launcher/DesktopServices.cpp b/launcher/DesktopServices.cpp
index 2984a1b4..81362f09 100644
--- a/launcher/DesktopServices.cpp
+++ b/launcher/DesktopServices.cpp
@@ -118,7 +118,7 @@ bool openDirectory(const QString &path, bool ensureExists)
return QDesktopServices::openUrl(QUrl::fromLocalFile(dir.absolutePath()));
};
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
- if(!isFlatpak())
+ if(!isSandbox())
{
return IndirectOpen(f);
}
@@ -139,7 +139,7 @@ bool openFile(const QString &path)
return QDesktopServices::openUrl(QUrl::fromLocalFile(path));
};
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
- if(!isFlatpak())
+ if(!isSandbox())
{
return IndirectOpen(f);
}
@@ -157,7 +157,7 @@ bool openFile(const QString &application, const QString &path, const QString &wo
qDebug() << "Opening file" << path << "using" << application;
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
// FIXME: the pid here is fake. So if something depends on it, it will likely misbehave
- if(!isFlatpak())
+ if(!isSandbox())
{
return IndirectOpen([&]()
{
@@ -177,7 +177,7 @@ bool run(const QString &application, const QStringList &args, const QString &wor
{
qDebug() << "Running" << application << "with args" << args.join(' ');
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
- if(!isFlatpak())
+ if(!isSandbox())
{
// FIXME: the pid here is fake. So if something depends on it, it will likely misbehave
return IndirectOpen([&]()
@@ -202,7 +202,7 @@ bool openUrl(const QUrl &url)
return QDesktopServices::openUrl(url);
};
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
- if(!isFlatpak())
+ if(!isSandbox())
{
return IndirectOpen(f);
}
@@ -224,4 +224,18 @@ bool isFlatpak()
#endif
}
+bool isSnap()
+{
+#ifdef Q_OS_LINUX
+ return getenv("SNAP");
+#else
+ return false;
+#endif
+}
+
+bool isSandbox()
+{
+ return isSnap() || isFlatpak();
+}
+
}
diff --git a/launcher/DesktopServices.h b/launcher/DesktopServices.h
index 21c9cae0..b1948cc2 100644
--- a/launcher/DesktopServices.h
+++ b/launcher/DesktopServices.h
@@ -34,5 +34,18 @@ namespace DesktopServices
*/
bool openUrl(const QUrl &url);
+ /**
+ * Determine whether the launcher is running in a Flatpak environment
+ */
bool isFlatpak();
+
+ /**
+ * Determine whether the launcher is running in a Snap environment
+ */
+ bool isSnap();
+
+ /**
+ * Determine whether the launcher is running in a sandboxed (Flatpak or Snap) environment
+ */
+ bool isSandbox();
}
diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp
index 3bcd4df8..07ed04f9 100644
--- a/launcher/minecraft/MinecraftInstance.cpp
+++ b/launcher/minecraft/MinecraftInstance.cpp
@@ -61,7 +61,6 @@
#include "launch/steps/QuitAfterGameStop.h"
#include "minecraft/launch/LauncherPartLaunch.h"
-#include "minecraft/launch/DirectJavaLaunch.h"
#include "minecraft/launch/ModMinecraftJar.h"
#include "minecraft/launch/ClaimAccount.h"
#include "minecraft/launch/ReconstructAssets.h"
@@ -167,10 +166,6 @@ void MinecraftInstance::loadSpecificSettings()
m_settings->registerOverride(global_settings->getSetting("MaxMemAlloc"), memorySetting);
m_settings->registerOverride(global_settings->getSetting("PermGen"), memorySetting);
- // Minecraft launch method
- auto launchMethodOverride = m_settings->registerSetting("OverrideMCLaunchMethod", false);
- m_settings->registerOverride(global_settings->getSetting("MCLaunchMethod"), launchMethodOverride);
-
// Native library workarounds
auto nativeLibraryWorkaroundsOverride = m_settings->registerSetting("OverrideNativeWorkarounds", false);
m_settings->registerOverride(global_settings->getSetting("UseNativeOpenAL"), nativeLibraryWorkaroundsOverride);
@@ -990,15 +985,6 @@ shared_qobject_ptr<LaunchTask> MinecraftInstance::createLaunchTask(AuthSessionPt
process->appendStep(makeShared<CheckJava>(pptr));
}
- // check launch method
- QStringList validMethods = {"LauncherPart", "DirectJava"};
- QString method = launchMethod();
- if(!validMethods.contains(method))
- {
- process->appendStep(makeShared<TextPrint>(pptr, "Selected launch method \"" + method + "\" is not valid.\n", MessageLevel::Fatal));
- return process;
- }
-
// create the .minecraft folder and server-resource-packs (workaround for Minecraft bug MCL-3732)
{
process->appendStep(makeShared<CreateGameFolders>(pptr));
@@ -1072,23 +1058,11 @@ shared_qobject_ptr<LaunchTask> MinecraftInstance::createLaunchTask(AuthSessionPt
{
// actually launch the game
- auto method = launchMethod();
- if(method == "LauncherPart")
- {
- auto step = makeShared<LauncherPartLaunch>(pptr);
- step->setWorkingDirectory(gameRoot());
- step->setAuthSession(session);
- step->setServerToJoin(serverToJoin);
- process->appendStep(step);
- }
- else if (method == "DirectJava")
- {
- auto step = makeShared<DirectJavaLaunch>(pptr);
- step->setWorkingDirectory(gameRoot());
- step->setAuthSession(session);
- step->setServerToJoin(serverToJoin);
- process->appendStep(step);
- }
+ auto step = makeShared<LauncherPartLaunch>(pptr);
+ step->setWorkingDirectory(gameRoot());
+ step->setAuthSession(session);
+ step->setServerToJoin(serverToJoin);
+ process->appendStep(step);
}
// run post-exit command if that's needed
@@ -1111,11 +1085,6 @@ shared_qobject_ptr<LaunchTask> MinecraftInstance::createLaunchTask(AuthSessionPt
return m_launchProcess;
}
-QString MinecraftInstance::launchMethod()
-{
- return settings()->get("MCLaunchMethod").toString();
-}
-
JavaVersion MinecraftInstance::getJavaVersion()
{
return JavaVersion(settings()->get("JavaVersion").toString());
diff --git a/launcher/minecraft/MinecraftInstance.h b/launcher/minecraft/MinecraftInstance.h
index 068b3008..4afcf04b 100644
--- a/launcher/minecraft/MinecraftInstance.h
+++ b/launcher/minecraft/MinecraftInstance.h
@@ -165,8 +165,6 @@ public:
protected:
QMap<QString, QString> createCensorFilterFromSession(AuthSessionPtr session);
- QStringList validLaunchMethods();
- QString launchMethod();
protected: // data
std::shared_ptr<PackProfile> m_components;
diff --git a/launcher/minecraft/launch/DirectJavaLaunch.cpp b/launcher/minecraft/launch/DirectJavaLaunch.cpp
deleted file mode 100644
index ca55cd2e..00000000
--- a/launcher/minecraft/launch/DirectJavaLaunch.cpp
+++ /dev/null
@@ -1,166 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * 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 "DirectJavaLaunch.h"
-
-#include <QStandardPaths>
-
-#include <launch/LaunchTask.h>
-#include <minecraft/MinecraftInstance.h>
-#include <FileSystem.h>
-#include <Commandline.h>
-
-#include "Application.h"
-
-#ifdef Q_OS_LINUX
-#include "gamemode_client.h"
-#endif
-
-DirectJavaLaunch::DirectJavaLaunch(LaunchTask *parent) : LaunchStep(parent)
-{
- connect(&m_process, &LoggedProcess::log, this, &DirectJavaLaunch::logLines);
- connect(&m_process, &LoggedProcess::stateChanged, this, &DirectJavaLaunch::on_state);
-}
-
-void DirectJavaLaunch::executeTask()
-{
- auto instance = m_parent->instance();
- std::shared_ptr<MinecraftInstance> minecraftInstance = std::dynamic_pointer_cast<MinecraftInstance>(instance);
- QStringList args = minecraftInstance->javaArguments();
-
- args.append("-Djava.library.path=" + minecraftInstance->getNativePath());
-
- auto classPathEntries = minecraftInstance->getClassPath();
- args.append("-cp");
- QString classpath;
-#ifdef Q_OS_WIN32
- classpath = classPathEntries.join(';');
-#else
- classpath = classPathEntries.join(':');
-#endif
- args.append(classpath);
- args.append(minecraftInstance->getMainClass());
-
- QString allArgs = args.join(", ");
- emit logLine("Java Arguments:\n[" + m_parent->censorPrivateInfo(allArgs) + "]\n\n", MessageLevel::Launcher);
-
- auto javaPath = FS::ResolveExecutable(instance->settings()->get("JavaPath").toString());
-
- m_process.setProcessEnvironment(instance->createLaunchEnvironment());
-
- // make detachable - this will keep the process running even if the object is destroyed
- m_process.setDetachable(true);
-
- auto mcArgs = minecraftInstance->processMinecraftArgs(m_session, m_serverToJoin);
- args.append(mcArgs);
-
- QString wrapperCommandStr = instance->getWrapperCommand().trimmed();
- if(!wrapperCommandStr.isEmpty())
- {
- auto wrapperArgs = Commandline::splitArgs(wrapperCommandStr);
- auto wrapperCommand = wrapperArgs.takeFirst();
- auto realWrapperCommand = QStandardPaths::findExecutable(wrapperCommand);
- if (realWrapperCommand.isEmpty())
- {
- const char *reason = QT_TR_NOOP("The wrapper command \"%1\" couldn't be found.");
- emit logLine(QString(reason).arg(wrapperCommand), MessageLevel::Fatal);
- emitFailed(tr(reason).arg(wrapperCommand));
- return;
- }
- emit logLine("Wrapper command is:\n" + wrapperCommandStr + "\n\n", MessageLevel::Launcher);
- args.prepend(javaPath);
- m_process.start(wrapperCommand, wrapperArgs + args);
- }
- else
- {
- m_process.start(javaPath, args);
- }
-
-#ifdef Q_OS_LINUX
- if (instance->settings()->get("EnableFeralGamemode").toBool() && APPLICATION->capabilities() & Application::SupportsGameMode)
- {
- auto pid = m_process.processId();
- if (pid)
- {
- gamemode_request_start_for(pid);
- }
- }
-#endif
-}
-
-void DirectJavaLaunch::on_state(LoggedProcess::State state)
-{
- switch(state)
- {
- case LoggedProcess::FailedToStart:
- {
- //: Error message displayed if instance can't start
- const char *reason = QT_TR_NOOP("Could not launch Minecraft!");
- emit logLine(reason, MessageLevel::Fatal);
- emitFailed(tr(reason));
- return;
- }
- case LoggedProcess::Aborted:
- case LoggedProcess::Crashed:
- {
- m_parent->setPid(-1);
- emitFailed(tr("Game crashed."));
- return;
- }
- case LoggedProcess::Finished:
- {
- m_parent->setPid(-1);
- // if the exit code wasn't 0, report this as a crash
- auto exitCode = m_process.exitCode();
- if(exitCode != 0)
- {
- emitFailed(tr("Game crashed."));
- return;
- }
- //FIXME: make this work again
- // m_postlaunchprocess.processEnvironment().insert("INST_EXITCODE", QString(exitCode));
- // run post-exit
- emitSucceeded();
- break;
- }
- case LoggedProcess::Running:
- emit logLine(QString("Minecraft process ID: %1\n\n").arg(m_process.processId()), MessageLevel::Launcher);
- m_parent->setPid(m_process.processId());
- m_parent->instance()->setLastLaunch();
- break;
- default:
- break;
- }
-}
-
-void DirectJavaLaunch::setWorkingDirectory(const QString &wd)
-{
- m_process.setWorkingDirectory(wd);
-}
-
-void DirectJavaLaunch::proceed()
-{
- // nil
-}
-
-bool DirectJavaLaunch::abort()
-{
- auto state = m_process.state();
- if (state == LoggedProcess::Running || state == LoggedProcess::Starting)
- {
- m_process.kill();
- }
- return true;
-}
-
diff --git a/launcher/minecraft/launch/DirectJavaLaunch.h b/launcher/minecraft/launch/DirectJavaLaunch.h
deleted file mode 100644
index 58b119b8..00000000
--- a/launcher/minecraft/launch/DirectJavaLaunch.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * 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 <launch/LaunchStep.h>
-#include <LoggedProcess.h>
-#include <minecraft/auth/AuthSession.h>
-
-#include "MinecraftServerTarget.h"
-
-class DirectJavaLaunch: public LaunchStep
-{
- Q_OBJECT
-public:
- explicit DirectJavaLaunch(LaunchTask *parent);
- virtual ~DirectJavaLaunch() {};
-
- virtual void executeTask();
- virtual bool abort();
- virtual void proceed();
- virtual bool canAbort() const
- {
- return true;
- }
- void setWorkingDirectory(const QString &wd);
- void setAuthSession(AuthSessionPtr session)
- {
- m_session = session;
- }
-
- void setServerToJoin(MinecraftServerTargetPtr serverToJoin)
- {
- m_serverToJoin = std::move(serverToJoin);
- }
-
-private slots:
- void on_state(LoggedProcess::State state);
-
-private:
- LoggedProcess m_process;
- QString m_command;
- AuthSessionPtr m_session;
- MinecraftServerTargetPtr m_serverToJoin;
-};
-
diff --git a/launcher/minecraft/mod/tasks/LocalModParseTask.cpp b/launcher/minecraft/mod/tasks/LocalModParseTask.cpp
index 264019f8..60389753 100644
--- a/launcher/minecraft/mod/tasks/LocalModParseTask.cpp
+++ b/launcher/minecraft/mod/tasks/LocalModParseTask.cpp
@@ -1,9 +1,9 @@
#include "LocalModParseTask.h"
+#include <qdcss.h>
#include <quazip/quazip.h>
#include <quazip/quazipfile.h>
#include <toml++/toml.h>
-#include <qdcss.h>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
@@ -369,12 +369,11 @@ ModDetails ReadQuiltModInfo(QByteArray contents)
details.icon_file = icon.toString();
}
}
-
}
return details;
}
-ModDetails ReadForgeInfo(QString fileName)
+ModDetails ReadForgeInfo(QByteArray contents)
{
ModDetails details;
// Read the data
@@ -382,7 +381,7 @@ ModDetails ReadForgeInfo(QString fileName)
details.mod_id = "Forge";
details.homeurl = "http://www.minecraftforge.net/forum/";
INIFile ini;
- if (!ini.loadFile(fileName))
+ if (!ini.loadFile(contents))
return details;
QString major = ini.get("forge.major.number", "0").toString();
@@ -554,7 +553,7 @@ bool processZIP(Mod& mod, ProcessingLevel level)
return false;
}
- details = ReadForgeInfo(file.getFileName());
+ details = ReadForgeInfo(file.readAll());
file.close();
zip.close();
diff --git a/launcher/modplatform/import_ftb/PackHelpers.cpp b/launcher/modplatform/import_ftb/PackHelpers.cpp
new file mode 100644
index 00000000..4a1bbef9
--- /dev/null
+++ b/launcher/modplatform/import_ftb/PackHelpers.cpp
@@ -0,0 +1,87 @@
+// 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 "modplatform/import_ftb/PackHelpers.h"
+
+#include <QIcon>
+#include <QString>
+#include <QVariant>
+
+#include "FileSystem.h"
+#include "Json.h"
+
+namespace FTBImportAPP {
+
+Modpack parseDirectory(QString path)
+{
+ Modpack modpack{ path };
+ auto instanceFile = QFileInfo(FS::PathCombine(path, "instance.json"));
+ if (!instanceFile.exists() || !instanceFile.isFile())
+ return {};
+ try {
+ auto doc = Json::requireDocument(instanceFile.absoluteFilePath(), "FTB_APP instance JSON file");
+ const auto root = doc.object();
+ modpack.uuid = Json::requireString(root, "uuid", "uuid");
+ modpack.id = Json::requireInteger(root, "id", "id");
+ modpack.versionId = Json::requireInteger(root, "versionId", "versionId");
+ modpack.name = Json::requireString(root, "name", "name");
+ modpack.version = Json::requireString(root, "version", "version");
+ modpack.mcVersion = Json::requireString(root, "mcVersion", "mcVersion");
+ modpack.jvmArgs = Json::ensureVariant(root, "jvmArgs", {}, "jvmArgs");
+ } catch (const Exception& e) {
+ qDebug() << "Couldn't load ftb instance json: " << e.cause();
+ return {};
+ }
+ auto versionsFile = QFileInfo(FS::PathCombine(path, "version.json"));
+ if (!versionsFile.exists() || !versionsFile.isFile())
+ return {};
+ try {
+ auto doc = Json::requireDocument(versionsFile.absoluteFilePath(), "FTB_APP version JSON file");
+ const auto root = doc.object();
+ auto targets = Json::requireArray(root, "targets", "targets");
+
+ for (auto target : targets) {
+ auto obj = Json::requireObject(target, "target");
+ auto name = Json::requireString(obj, "name", "name");
+ auto version = Json::requireString(obj, "version", "version");
+ if (name == "forge") {
+ modpack.loaderType = ResourceAPI::Forge;
+ modpack.version = version;
+ break;
+ } else if (name == "fabric") {
+ modpack.loaderType = ResourceAPI::Fabric;
+ modpack.version = version;
+ break;
+ } else if (name == "quilt") {
+ modpack.loaderType = ResourceAPI::Quilt;
+ modpack.version = version;
+ break;
+ }
+ }
+ } catch (const Exception& e) {
+ qDebug() << "Couldn't load ftb version json: " << e.cause();
+ return {};
+ }
+ auto iconFile = QFileInfo(FS::PathCombine(path, "folder.jpg"));
+ if (iconFile.exists() && iconFile.isFile()) {
+ modpack.icon = QIcon(iconFile.absoluteFilePath());
+ }
+ return modpack;
+}
+
+} // namespace FTBImportAPP
diff --git a/launcher/modplatform/import_ftb/PackHelpers.h b/launcher/modplatform/import_ftb/PackHelpers.h
new file mode 100644
index 00000000..8ea4f3fa
--- /dev/null
+++ b/launcher/modplatform/import_ftb/PackHelpers.h
@@ -0,0 +1,55 @@
+// 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/>.
+ */
+#pragma once
+
+#include <QIcon>
+#include <QList>
+#include <QMetaType>
+#include <QString>
+#include <QVariant>
+#include "modplatform/ResourceAPI.h"
+
+namespace FTBImportAPP {
+
+struct Modpack {
+ QString path;
+
+ // json data
+ QString uuid;
+ int id;
+ int versionId;
+ QString name;
+ QString version;
+ QString mcVersion;
+ // not needed for instance creation
+ QVariant jvmArgs;
+
+ std::optional<ResourceAPI::ModLoaderType> loaderType;
+ QString loaderVersion;
+
+ QIcon icon;
+};
+
+typedef QList<Modpack> ModpackList;
+
+Modpack parseDirectory(QString path);
+
+} // namespace FTBImportAPP
+
+// We need it for the proxy model
+Q_DECLARE_METATYPE(FTBImportAPP::Modpack)
diff --git a/launcher/modplatform/import_ftb/PackInstallTask.cpp b/launcher/modplatform/import_ftb/PackInstallTask.cpp
new file mode 100644
index 00000000..b5e424d1
--- /dev/null
+++ b/launcher/modplatform/import_ftb/PackInstallTask.cpp
@@ -0,0 +1,99 @@
+// 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 "PackInstallTask.h"
+
+#include <QtConcurrent>
+
+#include "BaseInstance.h"
+#include "FileSystem.h"
+#include "minecraft/MinecraftInstance.h"
+#include "minecraft/PackProfile.h"
+#include "modplatform/ResourceAPI.h"
+#include "modplatform/import_ftb/PackHelpers.h"
+#include "settings/INISettingsObject.h"
+
+namespace FTBImportAPP {
+
+void PackInstallTask::executeTask()
+{
+ setStatus(tr("Copying files..."));
+ setAbortable(false);
+ progress(1, 2);
+
+ m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [this] {
+ FS::copy folderCopy(m_pack.path, FS::PathCombine(m_stagingPath, ".minecraft"));
+ folderCopy.followSymlinks(true);
+ return folderCopy();
+ });
+ connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::finished, this, &PackInstallTask::copySettings);
+ connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::canceled, this, &PackInstallTask::emitAborted);
+ m_copyFutureWatcher.setFuture(m_copyFuture);
+}
+
+void PackInstallTask::copySettings()
+{
+ setStatus(tr("Copying settings..."));
+ progress(2, 2);
+ QString instanceConfigPath = FS::PathCombine(m_stagingPath, "instance.cfg");
+ auto instanceSettings = std::make_shared<INISettingsObject>(instanceConfigPath);
+ instanceSettings->suspendSave();
+ MinecraftInstance instance(m_globalSettings, instanceSettings, m_stagingPath);
+ instance.settings()->set("InstanceType", "OneSix");
+
+ if (m_pack.jvmArgs.isValid() && !m_pack.jvmArgs.toString().isEmpty()) {
+ instance.settings()->set("OverrideJavaArgs", true);
+ instance.settings()->set("JvmArgs", m_pack.jvmArgs.toString());
+ }
+
+ auto components = instance.getPackProfile();
+ components->buildingFromScratch();
+ components->setComponentVersion("net.minecraft", m_pack.mcVersion, true);
+
+ auto modloader = m_pack.loaderType;
+ if (modloader.has_value())
+ switch (modloader.value()) {
+ case ResourceAPI::Forge: {
+ components->setComponentVersion("net.minecraftforge", m_pack.version, true);
+ break;
+ }
+ case ResourceAPI::Fabric: {
+ components->setComponentVersion("net.fabricmc.fabric-loader", m_pack.version, true);
+ break;
+ }
+ case ResourceAPI::Quilt: {
+ components->setComponentVersion("org.quiltm