aboutsummaryrefslogtreecommitdiff
path: root/api
diff options
context:
space:
mode:
Diffstat (limited to 'api')
-rw-r--r--api/logic/CMakeLists.txt10
-rw-r--r--api/logic/Env.cpp1
-rw-r--r--api/logic/InstanceImportTask.cpp40
-rw-r--r--api/logic/InstanceImportTask.h19
-rw-r--r--api/logic/MMCZip.cpp2
-rw-r--r--api/logic/MMCZip.h3
-rw-r--r--api/logic/modplatform/technic/SingleZipPackInstallTask.cpp129
-rw-r--r--api/logic/modplatform/technic/SingleZipPackInstallTask.h64
-rw-r--r--api/logic/modplatform/technic/SolderPackInstallTask.cpp194
-rw-r--r--api/logic/modplatform/technic/SolderPackInstallTask.h57
-rw-r--r--api/logic/modplatform/technic/TechnicPackProcessor.cpp201
-rw-r--r--api/logic/modplatform/technic/TechnicPackProcessor.h37
-rw-r--r--api/logic/net/NetJob.cpp2
-rw-r--r--api/logic/net/NetJob.h2
14 files changed, 753 insertions, 8 deletions
diff --git a/api/logic/CMakeLists.txt b/api/logic/CMakeLists.txt
index 6e9aec08..15916bb5 100644
--- a/api/logic/CMakeLists.txt
+++ b/api/logic/CMakeLists.txt
@@ -477,6 +477,15 @@ set(MODPACKSCH_SOURCES
modplatform/modpacksch/FTBPackManifest.cpp
)
+set(TECHNIC_SOURCES
+ modplatform/technic/SingleZipPackInstallTask.h
+ modplatform/technic/SingleZipPackInstallTask.cpp
+ modplatform/technic/SolderPackInstallTask.h
+ modplatform/technic/SolderPackInstallTask.cpp
+ modplatform/technic/TechnicPackProcessor.h
+ modplatform/technic/TechnicPackProcessor.cpp
+)
+
add_unit_test(Index
SOURCES meta/Index_test.cpp
LIBS MultiMC_logic
@@ -508,6 +517,7 @@ set(LOGIC_SOURCES
${FTB_SOURCES}
${FLAME_SOURCES}
${MODPACKSCH_SOURCES}
+ ${TECHNIC_SOURCES}
)
add_library(MultiMC_logic SHARED ${LOGIC_SOURCES})
diff --git a/api/logic/Env.cpp b/api/logic/Env.cpp
index 2043f982..e9eb67cb 100644
--- a/api/logic/Env.cpp
+++ b/api/logic/Env.cpp
@@ -98,6 +98,7 @@ void Env::initHttpMetaCache()
m_metacache->addBase("general", QDir("cache").absolutePath());
m_metacache->addBase("FTBPacks", QDir("cache/FTBPacks").absolutePath());
m_metacache->addBase("ModpacksCHPacks", QDir("cache/ModpacksCHPacks").absolutePath());
+ m_metacache->addBase("TechnicPacks", QDir("cache/TechnicPacks").absolutePath());
m_metacache->addBase("TwitchPacks", QDir("cache/TwitchPacks").absolutePath());
m_metacache->addBase("skins", QDir("accounts/skins").absolutePath());
m_metacache->addBase("root", QDir::currentPath());
diff --git a/api/logic/InstanceImportTask.cpp b/api/logic/InstanceImportTask.cpp
index e2187416..772149c4 100644
--- a/api/logic/InstanceImportTask.cpp
+++ b/api/logic/InstanceImportTask.cpp
@@ -1,3 +1,18 @@
+/* Copyright 2013-2020 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 "InstanceImportTask.h"
#include "BaseInstance.h"
#include "FileSystem.h"
@@ -15,6 +30,8 @@
#include "modplatform/flame/FileResolvingTask.h"
#include "modplatform/flame/PackManifest.h"
#include "Json.h"
+#include <quazipdir.h>
+#include "modplatform/technic/TechnicPackProcessor.h"
InstanceImportTask::InstanceImportTask(const QUrl sourceUrl)
{
@@ -23,8 +40,6 @@ InstanceImportTask::InstanceImportTask(const QUrl sourceUrl)
void InstanceImportTask::executeTask()
{
- InstancePtr newInstance;
-
if (m_sourceUrl.isLocalFile())
{
m_archivePath = m_sourceUrl.toLocalFile();
@@ -82,6 +97,7 @@ void InstanceImportTask::processZipPack()
QStringList blacklist = {"instance.cfg", "manifest.json"};
QString mmcFound = MMCZip::findFolderOfFileInZip(m_packZip.get(), "instance.cfg");
+ bool technicFound = QuaZipDir(m_packZip.get()).exists("/bin/modpack.jar") || QuaZipDir(m_packZip.get()).exists("/bin/version.json");
QString flameFound = MMCZip::findFolderOfFileInZip(m_packZip.get(), "manifest.json");
QString root;
if(!mmcFound.isNull())
@@ -91,6 +107,14 @@ void InstanceImportTask::processZipPack()
root = mmcFound;
m_modpackType = ModpackType::MultiMC;
}
+ else if (technicFound)
+ {
+ // process as Technic pack
+ qDebug() << "Technic:" << technicFound;
+ extractDir.mkpath(".minecraft");
+ extractDir.cd(".minecraft");
+ m_modpackType = ModpackType::Technic;
+ }
else if(!flameFound.isNull())
{
// process as Flame pack
@@ -98,7 +122,6 @@ void InstanceImportTask::processZipPack()
root = flameFound;
m_modpackType = ModpackType::Flame;
}
-
if(m_modpackType == ModpackType::Unknown)
{
emitFailed(tr("Archive does not contain a recognized modpack type."));
@@ -161,6 +184,9 @@ void InstanceImportTask::extractFinished()
case ModpackType::MultiMC:
processMultiMC();
return;
+ case ModpackType::Technic:
+ processTechnic();
+ return;
case ModpackType::Unknown:
emitFailed(tr("Archive does not contain a recognized modpack type."));
return;
@@ -371,6 +397,14 @@ void InstanceImportTask::processFlame()
m_modIdResolver->start();
}
+void InstanceImportTask::processTechnic()
+{
+ shared_qobject_ptr<Technic::TechnicPackProcessor> packProcessor = new Technic::TechnicPackProcessor();
+ connect(packProcessor.get(), &Technic::TechnicPackProcessor::succeeded, this, &InstanceImportTask::emitSucceeded);
+ connect(packProcessor.get(), &Technic::TechnicPackProcessor::failed, this, &InstanceImportTask::emitFailed);
+ packProcessor->run(m_globalSettings, m_instName, m_instIcon, m_stagingPath);
+}
+
void InstanceImportTask::processMultiMC()
{
// FIXME: copy from FolderInstanceProvider!!! FIX IT!!!
diff --git a/api/logic/InstanceImportTask.h b/api/logic/InstanceImportTask.h
index d326391b..1e19354b 100644
--- a/api/logic/InstanceImportTask.h
+++ b/api/logic/InstanceImportTask.h
@@ -1,3 +1,18 @@
+/* Copyright 2013-2020 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 "InstanceTask.h"
@@ -29,6 +44,7 @@ private:
void processZipPack();
void processMultiMC();
void processFlame();
+ void processTechnic();
private slots:
void downloadSucceeded();
@@ -49,6 +65,7 @@ private: /* data */
enum class ModpackType{
Unknown,
MultiMC,
- Flame
+ Flame,
+ Technic
} m_modpackType = ModpackType::Unknown;
};
diff --git a/api/logic/MMCZip.cpp b/api/logic/MMCZip.cpp
index 3afdbf5e..876d7328 100644
--- a/api/logic/MMCZip.cpp
+++ b/api/logic/MMCZip.cpp
@@ -1,4 +1,4 @@
-/* Copyright 2013-2019 MultiMC Contributors
+/* Copyright 2013-2020 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/api/logic/MMCZip.h b/api/logic/MMCZip.h
index 85ac7802..56d20fbe 100644
--- a/api/logic/MMCZip.h
+++ b/api/logic/MMCZip.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2019 MultiMC Contributors
+/* Copyright 2013-2020 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -67,5 +67,4 @@ namespace MMCZip
* \return The list of the full paths of the files extracted, empty on failure.
*/
QStringList MULTIMC_LOGIC_EXPORT extractDir(QString fileCompressed, QString dir);
-
}
diff --git a/api/logic/modplatform/technic/SingleZipPackInstallTask.cpp b/api/logic/modplatform/technic/SingleZipPackInstallTask.cpp
new file mode 100644
index 00000000..833ac0a2
--- /dev/null
+++ b/api/logic/modplatform/technic/SingleZipPackInstallTask.cpp
@@ -0,0 +1,129 @@
+/* Copyright 2013-2020 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 "SingleZipPackInstallTask.h"
+
+#include "Env.h"
+#include "MMCZip.h"
+#include "TechnicPackProcessor.h"
+
+#include <QtConcurrent>
+
+Technic::SingleZipPackInstallTask::SingleZipPackInstallTask(const QUrl &sourceUrl, const QString &minecraftVersion)
+{
+ m_sourceUrl = sourceUrl;
+ m_minecraftVersion = minecraftVersion;
+}
+
+void Technic::SingleZipPackInstallTask::executeTask()
+{
+ setStatus(tr("Downloading modpack:\n%1").arg(m_sourceUrl.toString()));
+
+ const QString path = m_sourceUrl.host() + '/' + m_sourceUrl.path();
+ auto entry = ENV.metacache()->resolveEntry("general", path);
+ entry->setStale(true);
+ m_filesNetJob.reset(new NetJob(tr("Modpack download")));
+ m_filesNetJob->addNetAction(Net::Download::makeCached(m_sourceUrl, entry));
+ m_archivePath = entry->getFullPath();
+ auto job = m_filesNetJob.get();
+ connect(job, &NetJob::succeeded, this, &Technic::SingleZipPackInstallTask::downloadSucceeded);
+ connect(job, &NetJob::progress, this, &Technic::SingleZipPackInstallTask::downloadProgressChanged);
+ connect(job, &NetJob::failed, this, &Technic::SingleZipPackInstallTask::downloadFailed);
+ m_filesNetJob->start();
+}
+
+void Technic::SingleZipPackInstallTask::downloadSucceeded()
+{
+ setStatus(tr("Extracting modpack"));
+ QDir extractDir(m_stagingPath);
+ qDebug() << "Attempting to create instance from" << m_archivePath;
+
+ // open the zip and find relevant files in it
+ m_packZip.reset(new QuaZip(m_archivePath));
+ if (!m_packZip->open(QuaZip::mdUnzip))
+ {
+ emitFailed(tr("Unable to open supplied modpack zip file."));
+ return;
+ }
+ m_extractFuture = QtConcurrent::run(QThreadPool::globalInstance(), MMCZip::extractSubDir, m_packZip.get(), QString(""), extractDir.absolutePath());
+ connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::finished, this, &Technic::SingleZipPackInstallTask::extractFinished);
+ connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::canceled, this, &Technic::SingleZipPackInstallTask::extractAborted);
+ m_extractFutureWatcher.setFuture(m_extractFuture);
+ m_filesNetJob.reset();
+}
+
+void Technic::SingleZipPackInstallTask::downloadFailed(QString reason)
+{
+ emitFailed(reason);
+ m_filesNetJob.reset();
+}
+
+void Technic::SingleZipPackInstallTask::downloadProgressChanged(qint64 current, qint64 total)
+{
+ setProgress(current / 2, total);
+}
+
+void Technic::SingleZipPackInstallTask::extractFinished()
+{
+ m_packZip.reset();
+ if (m_extractFuture.result().isEmpty())
+ {
+ emitFailed(tr("Failed to extract modpack"));
+ return;
+ }
+ QDir extractDir(m_stagingPath);
+
+ qDebug() << "Fixing permissions for extracted pack files...";
+ QDirIterator it(extractDir, QDirIterator::Subdirectories);
+ while (it.hasNext())
+ {
+ auto filepath = it.next();
+ QFileInfo file(filepath);
+ auto permissions = QFile::permissions(filepath);
+ auto origPermissions = permissions;
+ if (file.isDir())
+ {
+ // Folder +rwx for current user
+ permissions |= QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser | QFileDevice::Permission::ExeUser;
+ }
+ else
+ {
+ // File +rw for current user
+ permissions |= QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser;
+ }
+ if (origPermissions != permissions)
+ {
+ if (!QFile::setPermissions(filepath, permissions))
+ {
+ logWarning(tr("Could not fix permissions for %1").arg(filepath));
+ }
+ else
+ {
+ qDebug() << "Fixed" << filepath;
+ }
+ }
+ }
+
+ shared_qobject_ptr<Technic::TechnicPackProcessor> packProcessor = new Technic::TechnicPackProcessor();
+ connect(packProcessor.get(), &Technic::TechnicPackProcessor::succeeded, this, &Technic::SingleZipPackInstallTask::emitSucceeded);
+ connect(packProcessor.get(), &Technic::TechnicPackProcessor::failed, this, &Technic::SingleZipPackInstallTask::emitFailed);
+ packProcessor->run(m_globalSettings, m_instName, m_instIcon, m_stagingPath, m_minecraftVersion);
+}
+
+void Technic::SingleZipPackInstallTask::extractAborted()
+{
+ emitFailed(tr("Instance import has been aborted."));
+}
diff --git a/api/logic/modplatform/technic/SingleZipPackInstallTask.h b/api/logic/modplatform/technic/SingleZipPackInstallTask.h
new file mode 100644
index 00000000..929476bb
--- /dev/null
+++ b/api/logic/modplatform/technic/SingleZipPackInstallTask.h
@@ -0,0 +1,64 @@
+/* Copyright 2013-2020 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
+
+#ifndef TECHNIC_SINGLEZIPPACKINSTALLTASK_H
+#define TECHNIC_SINGLEZIPPACKINSTALLTASK_H
+
+#include "InstanceTask.h"
+#include "net/NetJob.h"
+#include "multimc_logic_export.h"
+
+#include "quazip.h"
+
+#include <QFutureWatcher>
+#include <QStringList>
+#include <QUrl>
+
+namespace Technic {
+
+class MULTIMC_LOGIC_EXPORT SingleZipPackInstallTask : public InstanceTask
+{
+ Q_OBJECT
+
+public:
+ SingleZipPackInstallTask(const QUrl &sourceUrl, const QString &minecraftVersion);
+
+protected:
+ void executeTask() override;
+
+
+private slots:
+ void downloadSucceeded();
+ void downloadFailed(QString reason);
+ void downloadProgressChanged(qint64 current, qint64 total);
+ void extractFinished();
+ void extractAborted();
+
+private:
+ QUrl m_sourceUrl;
+ QString m_minecraftVersion;
+ QString m_archivePath;
+ NetJobPtr m_filesNetJob;
+ std::unique_ptr<QuaZip> m_packZip;
+ QFuture<QStringList> m_extractFuture;
+ QFutureWatcher<QStringList> m_extractFutureWatcher;
+};
+
+} // namespace Technic
+
+#endif // TECHNIC_SINGLEZIPPACKINSTALLTASK_H
diff --git a/api/logic/modplatform/technic/SolderPackInstallTask.cpp b/api/logic/modplatform/technic/SolderPackInstallTask.cpp
new file mode 100644
index 00000000..cb440e84
--- /dev/null
+++ b/api/logic/modplatform/technic/SolderPackInstallTask.cpp
@@ -0,0 +1,194 @@
+/* Copyright 2013-2020 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 "SolderPackInstallTask.h"
+
+#include <FileSystem.h>
+#include <Json.h>
+#include <QtConcurrentRun>
+#include <MMCZip.h>
+#include "TechnicPackProcessor.h"
+
+Technic::SolderPackInstallTask::SolderPackInstallTask(const QUrl &sourceUrl, const QString &minecraftVersion)
+{
+ m_sourceUrl = sourceUrl;
+ m_minecraftVersion = minecraftVersion;
+}
+
+void Technic::SolderPackInstallTask::executeTask()
+{
+ setStatus(tr("Finding recommended version:\n%1").arg(m_sourceUrl.toString()));
+ m_filesNetJob.reset(new NetJob(tr("Finding recommended version")));
+ m_filesNetJob->addNetAction(Net::Download::makeByteArray(m_sourceUrl, &m_response));
+ auto job = m_filesNetJob.get();
+ connect(job, &NetJob::succeeded, this, &Technic::SolderPackInstallTask::versionSucceeded);
+ connect(job, &NetJob::failed, this, &Technic::SolderPackInstallTask::downloadFailed);
+ m_filesNetJob->start();
+}
+
+void Technic::SolderPackInstallTask::versionSucceeded()
+{
+ try
+ {
+ QJsonDocument doc = Json::requireDocument(m_response);
+ QJsonObject obj = Json::requireObject(doc);
+ QString version = Json::requireString(obj, "recommended", "__placeholder__");
+ m_sourceUrl = m_sourceUrl.toString() + '/' + version;
+ }
+ catch (const JSONValidationError &e)
+ {
+ emitFailed(e.cause());
+ m_filesNetJob.reset();
+ return;
+ }
+
+ setStatus(tr("Resolving modpack files:\n%1").arg(m_sourceUrl.toString()));
+ m_filesNetJob.reset(new NetJob(tr("Resolving modpack files")));
+ m_filesNetJob->addNetAction(Net::Download::makeByteArray(m_sourceUrl, &m_response));
+ auto job = m_filesNetJob.get();
+ connect(job, &NetJob::succeeded, this, &Technic::SolderPackInstallTask::fileListSucceeded);
+ connect(job, &NetJob::failed, this, &Technic::SolderPackInstallTask::downloadFailed);
+ m_filesNetJob->start();
+}
+
+void Technic::SolderPackInstallTask::fileListSucceeded()
+{
+ setStatus(tr("Downloading modpack:"));
+ QStringList modUrls;
+ try
+ {
+ QJsonDocument doc = Json::requireDocument(m_response);
+ QJsonObject obj = Json::requireObject(doc);
+ QString minecraftVersion = Json::ensureString(obj, "minecraft", QString(), "__placeholder__");
+ if (!minecraftVersion.isEmpty())
+ m_minecraftVersion = minecraftVersion;
+ QJsonArray mods = Json::requireArray(obj, "mods", "'mods'");
+ for (auto mod: mods)
+ {
+ QJsonObject modObject = Json::requireObject(mod);
+ modUrls.append(Json::requireString(modObject, "url", "'url'"));
+ }
+ }
+ catch (const JSONValidationError &e)
+ {
+ emitFailed(e.cause());
+ m_filesNetJob.reset();
+ return;
+ }
+ m_filesNetJob.reset(new NetJob(tr("Downloading modpack")));
+ int i = 0;
+ for (auto &modUrl: modUrls)
+ {
+ m_filesNetJob->addNetAction(Net::Download::makeFile(modUrl, m_outputDir.filePath(QString("%1").arg(i))));
+ i++;
+ }
+
+ m_modCount = modUrls.size();
+
+ connect(m_filesNetJob.get(), &NetJob::succeeded, this, &Technic::SolderPackInstallTask::downloadSucceeded);
+ connect(m_filesNetJob.get(), &NetJob::progress, this, &Technic::SolderPackInstallTask::downloadProgressChanged);
+ connect(m_filesNetJob.get(), &NetJob::failed, this, &Technic::SolderPackInstallTask::downloadFailed);
+ m_filesNetJob->start();
+}
+
+void Technic::SolderPackInstallTask::downloadSucceeded()
+{
+ setStatus(tr("Extracting modpack"));
+ m_filesNetJob.reset();
+ m_extractFuture = QtConcurrent::run([this]()
+ {
+ int i = 0;
+ QString extractDir = FS::PathCombine(m_stagingPath, ".minecraft");
+ FS::ensureFolderPathExists(extractDir);
+
+ while (m_modCount > i)
+ {
+ if (MMCZip::extractDir(m_outputDir.filePath(QString("%1").arg(i)), extractDir).isEmpty())
+ {
+ return false;
+ }
+ i++;
+ }
+ return true;
+ });
+ connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::finished, this, &Technic::SolderPackInstallTask::extractFinished);
+ connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::canceled, this, &Technic::SolderPackInstallTask::extractAborted);
+ m_extractFutureWatcher.setFuture(m_extractFuture);
+}
+
+void Technic::SolderPackInstallTask::downloadFailed(QString reason)
+{
+ emitFailed(reason);
+ m_filesNetJob.reset();
+}
+
+void Technic::SolderPackInstallTask::downloadProgressChanged(qint64 current, qint64 total)
+{
+ setProgress(current / 2, total);
+}
+
+void Technic::SolderPackInstallTask::extractFinished()
+{
+ if (!m_extractFuture.result())
+ {
+ emitFailed(tr("Failed to extract modpack"));
+ return;
+ }
+ QDir extractDir(m_stagingPath);
+
+ qDebug() << "Fixing permissions for extracted pack files...";
+ QDirIterator it(extractDir, QDirIterator::Subdirectories);
+ while (it.hasNext())
+ {
+ auto filepath = it.next();
+ QFileInfo file(filepath);
+ auto permissions = QFile::permissions(filepath);
+ auto origPermissions = permissions;
+ if(file.isDir())
+ {
+ // Folder +rwx for current user
+ permissions |= QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser | QFileDevice::Permission::ExeUser;
+ }
+ else
+ {
+ // File +rw for current user
+ permissions |= QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser;
+ }
+ if(origPermissions != permissions)
+ {
+ if(!QFile::setPermissions(filepath, permissions))
+ {
+ logWarning(tr("Could not fix permissions for %1").arg(filepath));
+ }
+ else
+ {
+ qDebug() << "Fixed" << filepath;
+ }
+ }
+ }
+
+ shared_qobject_ptr<Technic::TechnicPackProcessor> packProcessor = new Technic::TechnicPackProcessor();
+ connect(packProcessor.get(), &Technic::TechnicPackProcessor::succeeded, this, &Technic::SolderPackInstallTask::emitSucceeded);
+ connect(packProcessor.get(), &Technic::TechnicPackProcessor::failed, this, &Technic::SolderPackInstallTask::emitFailed);
+ packProcessor->run(m_globalSettings, m_instName, m_instIcon, m_stagingPath, m_minecraftVersion, true); // TODO: pass the minecraft version down
+}
+
+void Technic::SolderPackInstallTask::extractAborted()
+{
+ emitFailed(tr("Instance import has been aborted."));
+ return;
+}
+
diff --git a/api/logic/modplatform/technic/SolderPackInstallTask.h b/api/logic/modplatform/technic/SolderPackInstallTask.h
new file mode 100644
index 00000000..d3a1d0fd
--- /dev/null
+++ b/api/logic/modplatform/technic/SolderPackInstallTask.h
@@ -0,0 +1,57 @@
+/* Copyright 2013-2020 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 <InstanceTask.h>
+#include <net/NetJob.h>
+#include <tasks/Task.h>
+
+#include <QUrl>
+
+
+namespace Technic
+{
+ class MULTIMC_LOGIC_EXPORT SolderPackInstallTask : public InstanceTask
+ {
+ Q_OBJECT
+ public:
+ explicit SolderPackInstallTask(const QUrl &sourceUrl, const QString &minecraftVersion);
+
+ protected:
+ //! Entry point for tasks.
+ virtual void executeTask() override;
+
+ private slots:
+ void versionSucceeded();
+ void fileListSucceeded();
+ void downloadSucceeded();
+ void downloadFailed(QString reason);
+ void downloadProgressChanged(qint64 current, qint64 total);
+ void extractFinished();
+ void extractAborted();
+
+ private:
+ NetJobPtr m_filesNetJob;
+ QUrl m_sourceUrl;
+ QString m_minecraftVersion;
+ QByteArray m_response;
+ QTemporaryDir m_outputDir;
+ int m_modCount;
+ QFuture<bool> m_extractFuture;
+ QFutureWatcher<bool> m_extractFutureWatcher;
+ };
+}
diff --git a/api/logic/modplatform/technic/TechnicPackProcessor.cpp b/api/logic/modplatform/technic/TechnicPackProcessor.cpp
new file mode 100644
index 00000000..f986a529
--- /dev/null
+++ b/api/logic/modplatform/technic/TechnicPackProcessor.cpp
@@ -0,0 +1,201 @@
+/* Copyright 2020 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 "TechnicPackProcessor.h"
+
+#include <FileSystem.h>
+#include <Json.h>
+#include <minecraft/MinecraftInstance.h>
+#include <minecraft/PackProfile.h>
+#include <quazip.h>
+#include <quazipdir.h>
+#include <quazipfile.h>
+#include <settings/INISettingsObject.h>
+
+#include <memory>
+
+
+void Technic::TechnicPackProcessor::run(SettingsObjectPtr globalSettings, const QString &instName, const QString &instIcon, const QString &stagingPath, const QString &minecraftVersion, const bool isSolder)
+{
+ QString minecraftPath = FS::PathCombine(stagingPath, ".minecraft");
+ QString configPath = FS::PathCombine(stagingPath, "instance.cfg");
+ auto instanceSettings = std::make_shared<INISettingsObject>(configPath);
+ instanceSettings->registerSetting("InstanceType", "Legacy");
+ instanceSettings->set("InstanceType", "OneSix");
+ MinecraftInstance instance(globalSettings, instanceSettings, stagingPath);
+
+ instance.setName(instName);
+
+ if (instIcon != "default")
+ {
+ instance.setIconKey(instIcon);
+ }
+
+ auto components = instance.getPackProfile();
+ components->buildingFromScratch();
+
+ QByteArray data;
+
+ QString modpackJar = FS::PathCombine(minecraftPath, "bin", "modpack.jar");
+ QString versionJson = FS::PathCombine(minecraftPath, "bin", "version.json");
+ QString fmlMinecraftVersion;
+ if (QFile::exists(modpackJar))
+ {
+ QuaZip zipFile(modpackJar);
+ if (!zipFile.open(QuaZip::mdUnzip))
+ {
+ emit failed(tr("Unable to open \"bin/modpack.jar\" file!"));
+ return;
+ }
+ QuaZipDir zipFileRoot(&zipFile, "/");
+ if (zipFileRoot.exists("/version.json"))
+ {
+ if (zipFileRoot.exists("/fmlversion.properties"))
+ {
+ zipFile.setCurrentFile("fmlversion.properties");
+ QuaZipFile file(&zipFile);
+ if (!file.open(QIODevice::ReadOnly))
+ {
+ emit failed(tr("Unable to open \"fmlversion.properties\"!"));
+ return;
+ }
+ QByteArray fmlVersionData = file.readAll();
+ file.close();
+ INIFile iniFile;
+ iniFile.loadFile(fmlVersionData);
+ // If not present, this evaluates to a null string
+ fmlMinecraftVersion = iniFile["fmlbuild.mcversion"].toString();
+ }
+ zipFile.setCurrentFile("version.json", QuaZip::csSensitive);
+ QuaZipFile file(&zipFile);
+ if (!file.open(QIODevice::ReadOnly))
+ {
+ emit failed(tr("Unable to open \"version.json\"!"));
+ return;
+ }
+ data = file.readAll();
+ file.close();
+ }
+ else
+ {
+ if (minecraftVersion.isEmpty())
+ emit failed(tr("Could not find \"version.json\" inside \"bin/modpack.jar\", but minecraft version is unknown"));
+ components->setComponentVersion("net.minecraft", minecraftVersion, true);
+ components->installJarMods({modpackJar});
+
+ // Forge for 1.4.7 and for 1.5.2 require extra libraries.
+ // Figure out the forge version and add it as a component
+ // (the code still comes from the jar mod installed above)
+ if (zipFileRoot.exists("/forgeversion.properties"))
+ {
+ zipFile.setCurrentFile("forgeversion.properties", QuaZip::csSensitive);
+ QuaZipFile file(&zipFile);
+ if (!file.open(QIODevice::ReadOnly))
+ {
+ // Really shouldn't happen, but error handling shall not be forgotten
+ emit failed(tr("Unable to open \"forgeversion.properties\""));
+ return;
+ }
+ QByteArray forgeVersionData = file.readAll();
+ file.close();
+ INIFile iniFile;
+ iniFile.loadFile(forgeVersionData);
+ QString major, minor, revision, build;
+ major = iniFile["forge.major.number"].toString();
+ minor = iniFile["forge.minor.number"].toString();
+ revision = iniFile["forge.revision.number"].toString();
+ build = iniFile["forge.build.number"].toString();
+
+ if (major.isEmpty() || minor.isEmpty() || revision.isEmpty() || build.isEmpty())
+ {
+ emit failed(tr("Invalid \"forgeversion.properties\"!"));
+ return;
+ }
+
+ components->setComponentVersion("net.minecraftforge", major + '.' + minor + '.' + revision + '.' + build);
+ }
+
+ components->saveNow();
+ emit succeeded();
+ return;
+ }
+ }
+ else if (QFile::exists(versionJson))
+ {
+ QFile file(versionJson);
+ if (!file.open(QIODevice::ReadOnly))
+ {
+ emit failed(tr("Unable to open \"version.json\"!"));
+ return;
+ }
+ data = file.readAll();
+ file.close();
+ }
+ else
+ {
+ // This is the "Vanilla" modpack, excluded by the search code
+ emit failed(tr("Unable to find a \"version.json\"!"));
+ return;
+ }
+
+ try
+ {
+ QJsonDocument doc = Json::requireDocument(data);
+ QJsonObject root = Json::requireObject(doc, "version.json");
+ QString minecraftVersion = Json::ensureString(root, "inheritsFrom", QString(), "");
+ if (minecraftVersion.isEmpty())
+ {
+ if (fmlMinecraftVersion.isEmpty())
+ {
+ emit failed(tr("Could not understand \"version.json\":\ninheritsFrom is missing"));
+ return;
+ }
+ minecraftVersion = fmlMinecraftVersion;
+ }
+ components->setComponentVersion("net.minecraft", minecraftVersion, true);
+ for (auto library: Json::ensureArray(root, "libraries", {}))
+ {
+ if (!library.isObject())
+ {
+ continue;
+ }
+
+ auto libraryObject = Json::ensureObject(library, {}, "");
+ auto libraryName = Json::ensureString(libraryObject, "name", "", "");
+
+ if (libraryName.startsWith("net.minecraftforge:forge:") && libraryName.contains('-'))
+ {
+ components->setComponentVersion("net.minecraftforge", libraryName.section('-', 1));
+ }
+ else if (libraryName.startsWith("net.minecraftforge:minecraftforge:"))
+ {
+ components->setComponentVersion("net.minecraftforge", libraryName.section(':', 2));
+ }
+ else if (libraryName.startsWith("net.fabricmc:fabric-loader:"))
+ {
+ components->setComponentVersion("net.fabricmc.fabric-loader", libraryName.section(':', 2));
+ }
+ }
+ }
+ catch (const JSONValidationError &e)
+ {
+ emit failed(tr("Could not understand \"version.json\":\n") + e.cause());
+ return;
+ }
+
+ components->saveNow();
+ emit succeeded();
+}
diff --git a/api/logic/modplatform/technic/TechnicPackProcessor.h b/api/logic/modplatform/technic/TechnicPackProcessor.h
new file mode 100644
index 00000000..49d046a5
--- /dev/null
+++ b/api/logic/modplatform/technic/TechnicPackProcessor.h
@@ -0,0 +1,37 @@
+/* Copyright 2020 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 <QString>
+#include "settings/SettingsObject.h"
+
+
+namespace Technic
+{
+ // not exporting it, only used in SingleZipPackInstallTask, InstanceImportTask and SolderPackInstallTask
+ class TechnicPackProcessor : public QObject
+ {
+ Q_OBJECT
+
+ signals:
+ void succeeded();
+ void failed(QString reason);
+
+ public:
+ void run(SettingsObjectPtr globalSettings, const QString &instName, const QString &instIcon, const QString &stagingPath, const QString &minecraftVersion=QString(), const bool isSolder = false);
+ };
+}
diff --git a/api/logic/net/NetJob.cpp b/api/logic/net/NetJob.cpp
index 71e31736..7dfa16ca 100644
--- a/api/logic/net/NetJob.cpp
+++ b/api/logic/net/NetJob.cpp
@@ -214,3 +214,5 @@ bool NetJob::addNetAction(NetActionPtr action)
}
return true;
}
+
+NetJob::~NetJob() = default;
diff --git a/api/logic/net/NetJob.h b/api/logic/net/NetJob.h
index 0b56bdaa..daca419e 100644
--- a/api/logic/net/NetJob.h
+++ b/api/logic/net/NetJob.h
@@ -34,7 +34,7 @@ public:
{
setObjectName(job_name);
}
- virtual ~NetJob() {}
+ virtual ~NetJob();
bool addNetAction(NetActionPtr action);