mirror of
https://github.com/PrismLauncher/PrismLauncher.git
synced 2025-05-28 12:50:20 +02:00
Merge pull request #1409 from Trial97/import_zip
feat: refactored Instance ImportTask
This commit is contained in:
commit
e961cab352
@ -395,20 +395,15 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||
{
|
||||
static const QString baseLogFile = BuildConfig.LAUNCHER_NAME + "-%0.log";
|
||||
static const QString logBase = FS::PathCombine("logs", baseLogFile);
|
||||
auto moveFile = [](const QString& oldName, const QString& newName) {
|
||||
QFile::remove(newName);
|
||||
QFile::copy(oldName, newName);
|
||||
QFile::remove(oldName);
|
||||
};
|
||||
if (FS::ensureFolderPathExists("logs")) { // if this did not fail
|
||||
for (auto i = 0; i <= 4; i++)
|
||||
if (auto oldName = baseLogFile.arg(i);
|
||||
QFile::exists(oldName)) // do not pointlessly delete new files if the old ones are not there
|
||||
moveFile(oldName, logBase.arg(i));
|
||||
FS::move(oldName, logBase.arg(i));
|
||||
}
|
||||
|
||||
for (auto i = 4; i > 0; i--)
|
||||
moveFile(logBase.arg(i - 1), logBase.arg(i));
|
||||
FS::move(logBase.arg(i - 1), logBase.arg(i));
|
||||
|
||||
logFile = std::unique_ptr<QFile>(new QFile(logBase.arg(0)));
|
||||
if (!logFile->open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) {
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <QFile>
|
||||
|
||||
#include "BaseInstaller.h"
|
||||
#include "FileSystem.h"
|
||||
#include "minecraft/MinecraftInstance.h"
|
||||
|
||||
BaseInstaller::BaseInstaller() {}
|
||||
@ -42,7 +43,7 @@ bool BaseInstaller::add(MinecraftInstance* to)
|
||||
|
||||
bool BaseInstaller::remove(MinecraftInstance* from)
|
||||
{
|
||||
return QFile::remove(filename(from->instanceRoot()));
|
||||
return FS::deletePath(filename(from->instanceRoot()));
|
||||
}
|
||||
|
||||
QString BaseInstaller::filename(const QString& root) const
|
||||
|
@ -647,6 +647,19 @@ void ExternalLinkFileProcess::runLinkFile()
|
||||
qDebug() << "Process exited";
|
||||
}
|
||||
|
||||
bool moveByCopy(const QString& source, const QString& dest)
|
||||
{
|
||||
if (!copy(source, dest)()) { // copy
|
||||
qDebug() << "Copy of" << source << "to" << dest << "failed!";
|
||||
return false;
|
||||
}
|
||||
if (!deletePath(source)) { // remove original
|
||||
qDebug() << "Deletion of" << source << "failed!";
|
||||
return false;
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
bool move(const QString& source, const QString& dest)
|
||||
{
|
||||
std::error_code err;
|
||||
@ -654,13 +667,14 @@ bool move(const QString& source, const QString& dest)
|
||||
ensureFilePathExists(dest);
|
||||
fs::rename(StringUtils::toStdString(source), StringUtils::toStdString(dest), err);
|
||||
|
||||
if (err) {
|
||||
qWarning() << "Failed to move file:" << QString::fromStdString(err.message());
|
||||
qDebug() << "Source file:" << source;
|
||||
qDebug() << "Destination file:" << dest;
|
||||
if (err.value() != 0) {
|
||||
if (moveByCopy(source, dest))
|
||||
return true;
|
||||
qDebug() << "Move of" << source << "to" << dest << "failed!";
|
||||
qWarning() << "Failed to move file:" << QString::fromStdString(err.message()) << QString::number(err.value());
|
||||
return false;
|
||||
}
|
||||
|
||||
return err.value() == 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool deletePath(QString path)
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
#include "FileSystem.h"
|
||||
|
||||
void InstanceCreationTask::executeTask()
|
||||
{
|
||||
@ -45,7 +46,7 @@ void InstanceCreationTask::executeTask()
|
||||
if (!QFile::exists(path))
|
||||
continue;
|
||||
qDebug() << "Removing" << path;
|
||||
if (!QFile::remove(path)) {
|
||||
if (!FS::deletePath(path)) {
|
||||
qCritical() << "Couldn't remove the old conflicting files.";
|
||||
emitFailed(tr("Failed to remove old conflicting files."));
|
||||
return;
|
||||
|
@ -56,6 +56,7 @@
|
||||
|
||||
#include <QtConcurrentRun>
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
#include <quazip/quazipdir.h>
|
||||
|
||||
@ -68,15 +69,8 @@ bool InstanceImportTask::abort()
|
||||
if (!canAbort())
|
||||
return false;
|
||||
|
||||
if (m_filesNetJob)
|
||||
m_filesNetJob->abort();
|
||||
if (m_extractFuture.isRunning()) {
|
||||
// NOTE: The tasks created by QtConcurrent::run() can't actually get cancelled,
|
||||
// but we can use this call to check the state when the extraction finishes.
|
||||
m_extractFuture.cancel();
|
||||
m_extractFuture.waitForFinished();
|
||||
}
|
||||
|
||||
if (task)
|
||||
task->abort();
|
||||
return Task::abort();
|
||||
}
|
||||
|
||||
@ -89,7 +83,6 @@ void InstanceImportTask::executeTask()
|
||||
processZipPack();
|
||||
} else {
|
||||
setStatus(tr("Downloading modpack:\n%1").arg(m_sourceUrl.toString()));
|
||||
m_downloadRequired = true;
|
||||
|
||||
downloadFromUrl();
|
||||
}
|
||||
@ -97,115 +90,132 @@ void InstanceImportTask::executeTask()
|
||||
|
||||
void InstanceImportTask::downloadFromUrl()
|
||||
{
|
||||
const QString path = m_sourceUrl.host() + '/' + m_sourceUrl.path();
|
||||
const QString path(m_sourceUrl.host() + '/' + m_sourceUrl.path());
|
||||
|
||||
auto entry = APPLICATION->metacache()->resolveEntry("general", path);
|
||||
entry->setStale(true);
|
||||
m_filesNetJob.reset(new NetJob(tr("Modpack download"), APPLICATION->network()));
|
||||
m_filesNetJob->addNetAction(Net::ApiDownload::makeCached(m_sourceUrl, entry));
|
||||
m_archivePath = entry->getFullPath();
|
||||
|
||||
connect(m_filesNetJob.get(), &NetJob::succeeded, this, &InstanceImportTask::downloadSucceeded);
|
||||
connect(m_filesNetJob.get(), &NetJob::progress, this, &InstanceImportTask::downloadProgressChanged);
|
||||
connect(m_filesNetJob.get(), &NetJob::stepProgress, this, &InstanceImportTask::propagateStepProgress);
|
||||
connect(m_filesNetJob.get(), &NetJob::failed, this, &InstanceImportTask::downloadFailed);
|
||||
connect(m_filesNetJob.get(), &NetJob::aborted, this, &InstanceImportTask::downloadAborted);
|
||||
m_filesNetJob->start();
|
||||
auto filesNetJob = makeShared<NetJob>(tr("Modpack download"), APPLICATION->network());
|
||||
filesNetJob->addNetAction(Net::ApiDownload::makeCached(m_sourceUrl, entry));
|
||||
|
||||
connect(filesNetJob.get(), &NetJob::succeeded, this, &InstanceImportTask::processZipPack);
|
||||
connect(filesNetJob.get(), &NetJob::progress, this, &InstanceImportTask::setProgress);
|
||||
connect(filesNetJob.get(), &NetJob::stepProgress, this, &InstanceImportTask::propagateStepProgress);
|
||||
connect(filesNetJob.get(), &NetJob::failed, this, &InstanceImportTask::emitFailed);
|
||||
connect(filesNetJob.get(), &NetJob::aborted, this, &InstanceImportTask::emitAborted);
|
||||
task.reset(filesNetJob);
|
||||
filesNetJob->start();
|
||||
}
|
||||
|
||||
void InstanceImportTask::downloadSucceeded()
|
||||
QString InstanceImportTask::getRootFromZip(QuaZip* zip, const QString& root)
|
||||
{
|
||||
processZipPack();
|
||||
m_filesNetJob.reset();
|
||||
}
|
||||
if (!isRunning()) {
|
||||
return {};
|
||||
}
|
||||
QuaZipDir rootDir(zip, root);
|
||||
for (auto&& fileName : rootDir.entryList(QDir::Files)) {
|
||||
setDetails(fileName);
|
||||
if (fileName == "instance.cfg") {
|
||||
qDebug() << "MultiMC:" << true;
|
||||
m_modpackType = ModpackType::MultiMC;
|
||||
return root;
|
||||
}
|
||||
if (fileName == "manifest.json") {
|
||||
qDebug() << "Flame:" << true;
|
||||
m_modpackType = ModpackType::Flame;
|
||||
return root;
|
||||
}
|
||||
|
||||
void InstanceImportTask::downloadFailed(QString reason)
|
||||
{
|
||||
emitFailed(reason);
|
||||
m_filesNetJob.reset();
|
||||
}
|
||||
QCoreApplication::processEvents();
|
||||
}
|
||||
|
||||
void InstanceImportTask::downloadProgressChanged(qint64 current, qint64 total)
|
||||
{
|
||||
setProgress(current, total);
|
||||
}
|
||||
// Recurse the search to non-ignored subfolders
|
||||
for (auto&& fileName : rootDir.entryList(QDir::Dirs)) {
|
||||
if ("overrides/" == fileName)
|
||||
continue;
|
||||
|
||||
void InstanceImportTask::downloadAborted()
|
||||
{
|
||||
emitAborted();
|
||||
m_filesNetJob.reset();
|
||||
QString result = getRootFromZip(zip, root + fileName);
|
||||
if (!result.isEmpty())
|
||||
return result;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void InstanceImportTask::processZipPack()
|
||||
{
|
||||
setStatus(tr("Extracting modpack"));
|
||||
setStatus(tr("Attempting to determine instance type"));
|
||||
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)) {
|
||||
auto packZip = std::make_shared<QuaZip>(m_archivePath);
|
||||
if (!packZip->open(QuaZip::mdUnzip)) {
|
||||
emitFailed(tr("Unable to open supplied modpack zip file."));
|
||||
return;
|
||||
}
|
||||
|
||||
QuaZipDir packZipDir(m_packZip.get());
|
||||
QuaZipDir packZipDir(packZip.get());
|
||||
qDebug() << "Attempting to determine instance type";
|
||||
|
||||
// https://docs.modrinth.com/docs/modpacks/format_definition/#storage
|
||||
bool modrinthFound = packZipDir.exists("/modrinth.index.json");
|
||||
bool technicFound = packZipDir.exists("/bin/modpack.jar") || packZipDir.exists("/bin/version.json");
|
||||
QString root;
|
||||
|
||||
// NOTE: Prioritize modpack platforms that aren't searched for recursively.
|
||||
// Especially Flame has a very common filename for its manifest, which may appear inside overrides for example
|
||||
if (modrinthFound) {
|
||||
// https://docs.modrinth.com/docs/modpacks/format_definition/#storage
|
||||
if (packZipDir.exists("/modrinth.index.json")) {
|
||||
// process as Modrinth pack
|
||||
qDebug() << "Modrinth:" << modrinthFound;
|
||||
qDebug() << "Modrinth:" << true;
|
||||
m_modpackType = ModpackType::Modrinth;
|
||||
} else if (technicFound) {
|
||||
} else if (packZipDir.exists("/bin/modpack.jar") || packZipDir.exists("/bin/version.json")) {
|
||||
// process as Technic pack
|
||||
qDebug() << "Technic:" << technicFound;
|
||||
qDebug() << "Technic:" << true;
|
||||
extractDir.mkpath("minecraft");
|
||||
extractDir.cd("minecraft");
|
||||
m_modpackType = ModpackType::Technic;
|
||||
} else {
|
||||
QStringList paths_to_ignore{ "overrides/" };
|
||||
|
||||
if (QString mmcRoot = MMCZip::findFolderOfFileInZip(m_packZip.get(), "instance.cfg", paths_to_ignore); !mmcRoot.isNull()) {
|
||||
// process as MultiMC instance/pack
|
||||
qDebug() << "MultiMC:" << mmcRoot;
|
||||
root = mmcRoot;
|
||||
m_modpackType = ModpackType::MultiMC;
|
||||
} else if (QString flameRoot = MMCZip::findFolderOfFileInZip(m_packZip.get(), "manifest.json", paths_to_ignore);
|
||||
!flameRoot.isNull()) {
|
||||
// process as Flame pack
|
||||
qDebug() << "Flame:" << flameRoot;
|
||||
root = flameRoot;
|
||||
m_modpackType = ModpackType::Flame;
|
||||
}
|
||||
root = getRootFromZip(packZip.get());
|
||||
setDetails("");
|
||||
}
|
||||
if (m_modpackType == ModpackType::Unknown) {
|
||||
emitFailed(tr("Archive does not contain a recognized modpack type."));
|
||||
return;
|
||||
}
|
||||
setStatus(tr("Extracting modpack"));
|
||||
|
||||
// make sure we extract just the pack
|
||||
m_extractFuture =
|
||||
QtConcurrent::run(QThreadPool::globalInstance(), MMCZip::extractSubDir, m_packZip.get(), root, extractDir.absolutePath());
|
||||
connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::finished, this, &InstanceImportTask::extractFinished);
|
||||
m_extractFutureWatcher.setFuture(m_extractFuture);
|
||||
auto zipTask = makeShared<MMCZip::ExtractZipTask>(packZip, extractDir, root);
|
||||
|
||||
auto progressStep = std::make_shared<TaskStepProgress>();
|
||||
connect(zipTask.get(), &Task::finished, this, [this, progressStep] {
|
||||
progressStep->state = TaskStepState::Succeeded;
|
||||
stepProgress(*progressStep);
|
||||
});
|
||||
|
||||
connect(zipTask.get(), &Task::succeeded, this, &InstanceImportTask::extractFinished);
|
||||
connect(zipTask.get(), &Task::aborted, this, &InstanceImportTask::emitAborted);
|
||||
connect(zipTask.get(), &Task::failed, this, [this, progressStep](QString reason) {
|
||||
progressStep->state = TaskStepState::Failed;
|
||||
stepProgress(*progressStep);
|
||||
emitFailed(reason);
|
||||
});
|
||||
connect(zipTask.get(), &Task::stepProgress, this, &InstanceImportTask::propagateStepProgress);
|
||||
|
||||
connect(zipTask.get(), &Task::progress, this, [this, progressStep](qint64 current, qint64 total) {
|
||||
progressStep->update(current, total);
|
||||
stepProgress(*progressStep);
|
||||
});
|
||||
connect(zipTask.get(), &Task::status, this, [this, progressStep](QString status) {
|
||||
progressStep->status = status;
|
||||
stepProgress(*progressStep);
|
||||
});
|
||||
task.reset(zipTask);
|
||||
zipTask->start();
|
||||
}
|
||||
|
||||
void InstanceImportTask::extractFinished()
|
||||
{
|
||||
m_packZip.reset();
|
||||
|
||||
if (m_extractFuture.isCanceled())
|
||||
return;
|
||||
if (!m_extractFuture.result().has_value()) {
|
||||
emitFailed(tr("Failed to extract modpack"));
|
||||
return;
|
||||
}
|
||||
|
||||
QDir extractDir(m_stagingPath);
|
||||
|
||||
qDebug() << "Fixing permissions for extracted pack files...";
|
||||
@ -324,13 +334,15 @@ void InstanceImportTask::processMultiMC()
|
||||
m_instIcon = instance.iconKey();
|
||||
|
||||
auto importIconPath = IconUtils::findBestIconIn(instance.instanceRoot(), m_instIcon);
|
||||
if (importIconPath.isNull() || !QFile::exists(importIconPath))
|
||||
importIconPath = IconUtils::findBestIconIn(instance.instanceRoot(), "icon.png");
|
||||
if (!importIconPath.isNull() && QFile::exists(importIconPath)) {
|
||||
// import icon
|
||||
auto iconList = APPLICATION->icons();
|
||||
if (iconList->iconFileExists(m_instIcon)) {
|
||||
iconList->deleteIcon(m_instIcon);
|
||||
}
|
||||
iconList->installIcons({ importIconPath });
|
||||
iconList->installIcon(importIconPath, m_instIcon);
|
||||
}
|
||||
}
|
||||
emitSucceeded();
|
||||
|
@ -39,11 +39,8 @@
|
||||
#include <QFutureWatcher>
|
||||
#include <QUrl>
|
||||
#include "InstanceTask.h"
|
||||
#include "QObjectPtr.h"
|
||||
#include "modplatform/flame/PackManifest.h"
|
||||
#include "net/NetJob.h"
|
||||
#include "settings/SettingsObject.h"
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
class QuaZip;
|
||||
@ -54,35 +51,26 @@ class InstanceImportTask : public InstanceTask {
|
||||
explicit InstanceImportTask(const QUrl& sourceUrl, QWidget* parent = nullptr, QMap<QString, QString>&& extra_info = {});
|
||||
|
||||
bool abort() override;
|
||||
const QVector<Flame::File>& getBlockedFiles() const { return m_blockedMods; }
|
||||
|
||||
protected:
|
||||
//! Entry point for tasks.
|
||||
virtual void executeTask() override;
|
||||
|
||||
private:
|
||||
void processZipPack();
|
||||
void processMultiMC();
|
||||
void processTechnic();
|
||||
void processFlame();
|
||||
void processModrinth();
|
||||
QString getRootFromZip(QuaZip* zip, const QString& root = "");
|
||||
|
||||
private slots:
|
||||
void downloadSucceeded();
|
||||
void downloadFailed(QString reason);
|
||||
void downloadProgressChanged(qint64 current, qint64 total);
|
||||
void downloadAborted();
|
||||
void processZipPack();
|
||||
void extractFinished();
|
||||
|
||||
private: /* data */
|
||||
NetJob::Ptr m_filesNetJob;
|
||||
QUrl m_sourceUrl;
|
||||
QString m_archivePath;
|
||||
bool m_downloadRequired = false;
|
||||
std::unique_ptr<QuaZip> m_packZip;
|
||||
QFuture<std::optional<QStringList>> m_extractFuture;
|
||||
QFutureWatcher<std::optional<QStringList>> m_extractFutureWatcher;
|
||||
QVector<Flame::File> m_blockedMods;
|
||||
Task::Ptr task;
|
||||
enum class ModpackType {
|
||||
Unknown,
|
||||
MultiMC,
|
||||
|
@ -972,7 +972,6 @@ bool InstanceList::commitStagedInstance(const QString& path,
|
||||
if (groupName.isEmpty() && !groupName.isNull())
|
||||
groupName = QString();
|
||||
|
||||
QDir dir;
|
||||
QString instID;
|
||||
InstancePtr inst;
|
||||
|
||||
@ -996,7 +995,7 @@ bool InstanceList::commitStagedInstance(const QString& path,
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!dir.rename(path, destination)) {
|
||||
if (!FS::move(path, destination)) {
|
||||
qWarning() << "Failed to move" << path << "to" << destination;
|
||||
return false;
|
||||
}
|
||||
|
@ -42,6 +42,7 @@
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDebug>
|
||||
#include <QFileInfo>
|
||||
#include <QUrl>
|
||||
|
||||
#if defined(LAUNCHER_APPLICATION)
|
||||
@ -122,7 +123,7 @@ bool compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files,
|
||||
zip.setUtf8Enabled(true);
|
||||
QDir().mkpath(QFileInfo(fileCompressed).absolutePath());
|
||||
if (!zip.open(QuaZip::mdCreate)) {
|
||||
QFile::remove(fileCompressed);
|
||||
FS::deletePath(fileCompressed);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -130,7 +131,7 @@ bool compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files,
|
||||
|
||||
zip.close();
|
||||
if (zip.getZipError() != 0) {
|
||||
QFile::remove(fileCompressed);
|
||||
FS::deletePath(fileCompressed);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -144,7 +145,7 @@ bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<M
|
||||
QuaZip zipOut(targetJarPath);
|
||||
zipOut.setUtf8Enabled(true);
|
||||
if (!zipOut.open(QuaZip::mdCreate)) {
|
||||
QFile::remove(targetJarPath);
|
||||
FS::deletePath(targetJarPath);
|
||||
qCritical() << "Failed to open the minecraft.jar for modding";
|
||||
return false;
|
||||
}
|
||||
@ -162,7 +163,7 @@ bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<M
|
||||
if (mod->type() == ResourceType::ZIPFILE) {
|
||||
if (!mergeZipFiles(&zipOut, mod->fileinfo(), addedFiles)) {
|
||||
zipOut.close();
|
||||
QFile::remove(targetJarPath);
|
||||
FS::deletePath(targetJarPath);
|
||||
qCritical() << "Failed to add" << mod->fileinfo().fileName() << "to the jar.";
|
||||
return false;
|
||||
}
|
||||
@ -171,7 +172,7 @@ bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<M
|
||||
auto filename = mod->fileinfo();
|
||||
if (!JlCompress::compressFile(&zipOut, filename.absoluteFilePath(), filename.fileName())) {
|
||||
zipOut.close();
|
||||
QFile::remove(targetJarPath);
|
||||
FS::deletePath(targetJarPath);
|
||||
qCritical() << "Failed to add" << mod->fileinfo().fileName() << "to the jar.";
|
||||
return false;
|
||||
}
|
||||
@ -194,7 +195,7 @@ bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<M
|
||||
|
||||
if (!compressDirFiles(&zipOut, parent_dir, files)) {
|
||||
zipOut.close();
|
||||
QFile::remove(targetJarPath);
|
||||
FS::deletePath(targetJarPath);
|
||||
qCritical() << "Failed to add" << mod->fileinfo().fileName() << "to the jar.";
|
||||
return false;
|
||||
}
|
||||
@ -202,7 +203,7 @@ bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<M
|
||||
} else {
|
||||
// Make sure we do not continue launching when something is missing or undefined...
|
||||
zipOut.close();
|
||||
QFile::remove(targetJarPath);
|
||||
FS::deletePath(targetJarPath);
|
||||
qCritical() << "Failed to add unknown mod type" << mod->fileinfo().fileName() << "to the jar.";
|
||||
return false;
|
||||
}
|
||||
@ -210,7 +211,7 @@ bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<M
|
||||
|
||||
if (!mergeZipFiles(&zipOut, QFileInfo(sourceJarPath), addedFiles, [](const QString key) { return !key.contains("META-INF"); })) {
|
||||
zipOut.close();
|
||||
QFile::remove(targetJarPath);
|
||||
FS::deletePath(targetJarPath);
|
||||
qCritical() << "Failed to insert minecraft.jar contents.";
|
||||
return false;
|
||||
}
|
||||
@ -218,7 +219,7 @@ bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<M
|
||||
// Recompress the jar
|
||||
zipOut.close();
|
||||
if (zipOut.getZipError() != 0) {
|
||||
QFile::remove(targetJarPath);
|
||||
FS::deletePath(targetJarPath);
|
||||
qCritical() << "Failed to finalize minecraft.jar!";
|
||||
return false;
|
||||
}
|
||||
@ -332,9 +333,20 @@ std::optional<QStringList> extractSubDir(QuaZip* zip, const QString& subdir, con
|
||||
}
|
||||
|
||||
extracted.append(target_file_path);
|
||||
QFile::setPermissions(target_file_path,
|
||||
QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser | QFileDevice::Permission::ExeUser);
|
||||
auto fileInfo = QFileInfo(target_file_path);
|
||||
if (fileInfo.isFile()) {
|
||||
auto permissions = fileInfo.permissions();
|
||||
auto maxPermisions = QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser | QFileDevice::Permission::ExeUser |
|
||||
QFileDevice::Permission::ReadGroup | QFileDevice::Permission::ReadOther;
|
||||
auto minPermisions = QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser;
|
||||
|
||||
auto newPermisions = (permissions & maxPermisions) | minPermisions;
|
||||
if (newPermisions != permissions) {
|
||||
if (!QFile::setPermissions(target_file_path, newPermisions)) {
|
||||
qWarning() << (QObject::tr("Could not fix permissions for %1").arg(target_file_path));
|
||||
}
|
||||
}
|
||||
}
|
||||
qDebug() << "Extracted file" << relative_file_name << "to" << target_file_path;
|
||||
} while (zip->goToNextFile());
|
||||
|
||||
@ -492,10 +504,10 @@ auto ExportToZipTask::exportZip() -> ZipResult
|
||||
void ExportToZipTask::finish()
|
||||
{
|
||||
if (m_build_zip_future.isCanceled()) {
|
||||
QFile::remove(m_output_path);
|
||||
FS::deletePath(m_output_path);
|
||||
emitAborted();
|
||||
} else if (auto result = m_build_zip_future.result(); result.has_value()) {
|
||||
QFile::remove(m_output_path);
|
||||
FS::deletePath(m_output_path);
|
||||
emitFailed(result.value());
|
||||
} else {
|
||||
emitSucceeded();
|
||||
@ -512,6 +524,123 @@ bool ExportToZipTask::abort()
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
void ExtractZipTask::executeTask()
|
||||
{
|
||||
m_zip_future = QtConcurrent::run(QThreadPool::globalInstance(), [this]() { return extractZip(); });
|
||||
connect(&m_zip_watcher, &QFutureWatcher<ZipResult>::finished, this, &ExtractZipTask::finish);
|
||||
m_zip_watcher.setFuture(m_zip_future);
|
||||
}
|
||||
|
||||
auto ExtractZipTask::extractZip() -> ZipResult
|
||||
{
|
||||
auto target = m_output_dir.absolutePath();
|
||||
auto target_top_dir = QUrl::fromLocalFile(target);
|
||||
|
||||
QStringList extracted;
|
||||
|
||||
qDebug() << "Extracting subdir" << m_subdirectory << "from" << m_input->getZipName() << "to" << target;
|
||||
auto numEntries = m_input->getEntriesCount();
|
||||
if (numEntries < 0) {
|
||||
return ZipResult(tr("Failed to enumerate files in archive"));
|
||||
}
|
||||
if (numEntries == 0) {
|
||||
logWarning(tr("Extracting empty archives seems odd..."));
|
||||
return ZipResult();
|
||||
}
|
||||
if (!m_input->goToFirstFile()) {
|
||||
return ZipResult(tr("Failed to seek to first file in zip"));
|
||||
}
|
||||
|
||||
setStatus("Extracting files...");
|
||||
setProgress(0, numEntries);
|
||||
do {
|
||||
if (m_zip_future.isCanceled())
|
||||
return ZipResult();
|
||||
setProgress(m_progress + 1, m_progressTotal);
|
||||
QString file_name = m_input->getCurrentFileName();
|
||||
if (!file_name.startsWith(m_subdirectory))
|
||||
continue;
|
||||
|
||||
auto relative_file_name = QDir::fromNativeSeparators(file_name.remove(0, m_subdirectory.size()));
|
||||
auto original_name = relative_file_name;
|
||||
setStatus("Unziping: " + relative_file_name);
|
||||
|
||||
// Fix subdirs/files ending with a / getting transformed into absolute paths
|
||||
if (relative_file_name.startsWith('/'))
|
||||
relative_file_name = relative_file_name.mid(1);
|
||||
|
||||
// Fix weird "folders with a single file get squashed" thing
|
||||
QString sub_path;
|
||||
if (relative_file_name.contains('/') && !relative_file_name.endsWith('/')) {
|
||||
sub_path = relative_file_name.section('/', 0, -2) + '/';
|
||||
FS::ensureFolderPathExists(FS::PathCombine(target, sub_path));
|
||||
|
||||
relative_file_name = relative_file_name.split('/').last();
|
||||
}
|
||||
|
||||
QString target_file_path;
|
||||
if (relative_file_name.isEmpty()) {
|
||||
target_file_path = target + '/';
|
||||
} else {
|
||||
target_file_path = FS::PathCombine(target_top_dir.toLocalFile(), sub_path, relative_file_name);
|
||||
if (relative_file_name.endsWith('/') && !target_file_path.endsWith('/'))
|
||||
target_file_path += '/';
|
||||
}
|
||||
|
||||
if (!target_top_dir.isParentOf(QUrl::fromLocalFile(target_file_path))) {
|
||||
return ZipResult(tr("Extracting %1 was cancelled, because it was effectively outside of the target path %2")
|
||||
.arg(relative_file_name, target));
|
||||
}
|
||||
|
||||
if (!JlCompress::extractFile(m_input.get(), "", target_file_path)) {
|
||||
JlCompress::removeFile(extracted);
|
||||
return ZipResult(tr("Failed to extract file %1 to %2").arg(original_name, target_file_path));
|
||||
}
|
||||
|
||||
extracted.append(target_file_path);
|
||||
auto fileInfo = QFileInfo(target_file_path);
|
||||
if (fileInfo.isFile()) {
|
||||
auto permissions = fileInfo.permissions();
|
||||
auto maxPermisions = QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser | QFileDevice::Permission::ExeUser |
|
||||
QFileDevice::Permission::ReadGroup | QFileDevice::Permission::ReadOther;
|
||||
auto minPermisions = QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser;
|
||||
|
||||
auto newPermisions = (permissions & maxPermisions) | minPermisions;
|
||||
if (newPermisions != permissions) {
|
||||
if (!QFile::setPermissions(target_file_path, newPermisions)) {
|
||||
logWarning(tr("Could not fix permissions for %1").arg(target_file_path));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
qDebug() << "Extracted file" << relative_file_name << "to" << target_file_path;
|
||||
} while (m_input->goToNextFile());
|
||||
|
||||
return ZipResult();
|
||||
}
|
||||
|
||||
void ExtractZipTask::finish()
|
||||
{
|
||||
if (m_zip_future.isCanceled()) {
|
||||
emitAborted();
|
||||
} else if (auto result = m_zip_future.result(); result.has_value()) {
|
||||
emitFailed(result.value());
|
||||
} else {
|
||||
emitSucceeded();
|
||||
}
|
||||
}
|
||||
|
||||
bool ExtractZipTask::abort()
|
||||
{
|
||||
if (m_zip_future.isRunning()) {
|
||||
m_zip_future.cancel();
|
||||
// NOTE: Here we don't do `emitAborted()` because it will be done when `m_build_zip_future` actually cancels, which may not occur
|
||||
// immediately.
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
} // namespace MMCZip
|
||||
|
@ -205,5 +205,30 @@ class ExportToZipTask : public Task {
|
||||
QFuture<ZipResult> m_build_zip_future;
|
||||
QFutureWatcher<ZipResult> m_build_zip_watcher;
|
||||
};
|
||||
|
||||
class ExtractZipTask : public Task {
|
||||
public:
|
||||
ExtractZipTask(std::shared_ptr<QuaZip> input, QDir outputDir, QString subdirectory = "")
|
||||
: m_input(input), m_output_dir(outputDir), m_subdirectory(subdirectory)
|
||||
{}
|
||||
virtual ~ExtractZipTask() = default;
|
||||
|
||||
typedef std::optional<QString> ZipResult;
|
||||
|
||||
protected:
|
||||
virtual void executeTask() override;
|
||||
bool abort() override;
|
||||
|
||||
ZipResult extractZip();
|
||||
void finish();
|
||||
|
||||
private:
|
||||
std::shared_ptr<QuaZip> m_input;
|
||||
QDir m_output_dir;
|
||||
QString m_subdirectory;
|
||||
|
||||
QFuture<ZipResult> m_zip_future;
|
||||
QFutureWatcher<ZipResult> m_zip_watcher;
|
||||
};
|
||||
#endif
|
||||
} // namespace MMCZip
|
||||
|
@ -322,7 +322,7 @@ const MMCIcon* IconList::icon(const QString& key) const
|
||||
|
||||
bool IconList::deleteIcon(const QString& key)
|
||||
{
|
||||
return iconFileExists(key) && QFile::remove(icon(key)->getFilePath());
|
||||
return iconFileExists(key) && FS::deletePath(icon(key)->getFilePath());
|
||||
}
|
||||
|
||||
bool IconList::trashIcon(const QString& key)
|
||||
|
@ -52,8 +52,7 @@ QString findBestIconIn(const QString& folder, const QString& iconKey)
|
||||
while (it.hasNext()) {
|
||||
it.next();
|
||||
auto fileInfo = it.fileInfo();
|
||||
|
||||
if (fileInfo.completeBaseName() == iconKey && isIconSuffix(fileInfo.suffix()))
|
||||
if ((fileInfo.completeBaseName() == iconKey || fileInfo.fileName() == iconKey) && isIconSuffix(fileInfo.suffix()))
|
||||
return fileInfo.absoluteFilePath();
|
||||
}
|
||||
return {};
|
||||
|
@ -15,6 +15,7 @@
|
||||
|
||||
#include "BaseEntity.h"
|
||||
|
||||
#include "FileSystem.h"
|
||||
#include "Json.h"
|
||||
#include "net/ApiDownload.h"
|
||||
#include "net/HttpMetaCache.h"
|
||||
@ -83,8 +84,7 @@ bool Meta::BaseEntity::loadLocalFile()
|
||||
} catch (const Exception& e) {
|
||||
qDebug() << QString("Unable to parse file %1: %2").arg(fname, e.cause());
|
||||
// just make sure it's gone and we never consider it again.
|
||||
QFile::remove(fname);
|
||||
return false;
|
||||
return !FS::deletePath(fname);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -336,7 +336,7 @@ bool Component::revert()
|
||||
bool result = true;
|
||||
// just kill the file and reload
|
||||
if (QFile::exists(filename)) {
|
||||
result = QFile::remove(filename);
|
||||
result = FS::deletePath(filename);
|
||||
}
|
||||
if (result) {
|
||||
// file gone...
|
||||
|
@ -839,7 +839,7 @@ bool PackProfile::installCustomJar_internal(QString filepath)
|
||||
|
||||
QFileInfo jarInfo(finalPath);
|
||||
if (jarInfo.exists()) {
|
||||
if (!QFile::remove(finalPath)) {
|
||||
if (!FS::deletePath(finalPath)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -111,7 +111,7 @@ bool ResourceFolderModel::installResource(QString original_path)
|
||||
case ResourceType::ZIPFILE:
|
||||
case ResourceType::LITEMOD: {
|
||||
if (QFile::exists(new_path) || QFile::exists(new_path + QString(".disabled"))) {
|
||||
if (!QFile::remove(new_path)) {
|
||||
if (!FS::deletePath(new_path)) {
|
||||
qCritical() << "Cleaning up new location (" << new_path << ") was unsuccessful!";
|
||||
return false;
|
||||
}
|
||||
|
@ -282,7 +282,7 @@ void PackInstallTask::deleteExistingFiles()
|
||||
|
||||
// Delete the files
|
||||
for (const auto& item : filesToDelete) {
|
||||
QFile::remove(item);
|
||||
FS::deletePath(item);
|
||||
}
|
||||
}
|
||||
|
||||
@ -987,7 +987,7 @@ bool PackInstallTask::extractMods(const QMap<QString, VersionMod>& toExtract,
|
||||
// the copy from the Configs.zip
|
||||
QFileInfo fileInfo(to);
|
||||
if (fileInfo.exists()) {
|
||||
if (!QFile::remove(to)) {
|
||||
if (!FS::deletePath(to)) {
|
||||
qWarning() << "Failed to delete" << to;
|
||||
return false;
|
||||
}
|
||||
|
@ -322,7 +322,7 @@ bool FlameCreationTask::createInstance()
|
||||
// Keep index file in case we need it some other time (like when changing versions)
|
||||
QString new_index_place(FS::PathCombine(parent_folder, "manifest.json"));
|
||||
FS::ensureFilePathExists(new_index_place);
|
||||
QFile::rename(index_path, new_index_place);
|
||||
FS::move(index_path, new_index_place);
|
||||
|
||||
} catch (const JSONValidationError& e) {
|
||||
setError(tr("Could not understand pack manifest:\n") + e.cause());
|
||||
@ -336,7 +336,7 @@ bool FlameCreationTask::createInstance()
|
||||
Override::createOverrides("overrides", parent_folder, overridePath);
|
||||
|
||||
QString mcPath = FS::PathCombine(m_stagingPath, "minecraft");
|
||||
if (!QFile::rename(overridePath, mcPath)) {
|
||||
if (!FS::move(overridePath, mcPath)) {
|
||||
setError(tr("Could not rename the overrides folder:\n") + m_pack.overrides);
|
||||
return false;
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ void createOverrides(const QString& name, const QString& parent_folder, const QS
|
||||
{
|
||||
QString file_path(FS::PathCombine(parent_folder, name + ".txt"));
|
||||
if (QFile::exists(file_path))
|
||||
QFile::remove(file_path);
|
||||
FS::deletePath(file_path);
|
||||
|
||||
FS::ensureFilePathExists(file_path);
|
||||
|
||||
|
@ -137,7 +137,7 @@ void PackInstallTask::install()
|
||||
QDir unzipMcDir(m_stagingPath + "/unzip/minecraft");
|
||||
if (unzipMcDir.exists()) {
|
||||
// ok, found minecraft dir, move contents to instance dir
|
||||
if (!QDir().rename(m_stagingPath + "/unzip/minecraft", m_stagingPath + "/minecraft")) {
|
||||
if (!FS::move(m_stagingPath + "/unzip/minecraft", m_stagingPath + "/minecraft")) {
|
||||
emitFailed(tr("Failed to move unzipped Minecraft!"));
|
||||
return;
|
||||
}
|
||||
|
@ -173,7 +173,7 @@ bool ModrinthCreationTask::createInstance()
|
||||
// Keep index file in case we need it some other time (like when changing versions)
|
||||
QString new_index_place(FS::PathCombine(parent_folder, "modrinth.index.json"));
|
||||
FS::ensureFilePathExists(new_index_place);
|
||||
QFile::rename(index_path, new_index_place);
|
||||
FS::move(index_path, new_index_place);
|
||||
|
||||
auto mcPath = FS::PathCombine(m_stagingPath, m_root_path);
|
||||
|
||||
@ -183,7 +183,7 @@ bool ModrinthCreationTask::createInstance()
|
||||
Override::createOverrides("overrides", parent_folder, override_path);
|
||||
|
||||
// Apply the overrides
|
||||
if (!QFile::rename(override_path, mcPath)) {
|
||||
if (!FS::move(override_path, mcPath)) {
|
||||
setError(tr("Could not rename the overrides folder:\n") + "overrides");
|
||||
return false;
|
||||
}
|
||||
|
@ -40,6 +40,7 @@
|
||||
#include "ui_ImportPage.h"
|
||||
|
||||
#include <QFileDialog>
|
||||
#include <QMimeDatabase>
|
||||
#include <QValidator>
|
||||
#include <utility>
|
||||
|
||||
@ -51,6 +52,7 @@
|
||||
#include "Json.h"
|
||||
|
||||
#include "InstanceImportTask.h"
|
||||
#include "net/NetJob.h"
|
||||
|
||||
class UrlValidator : public QValidator {
|
||||
public:
|
||||
|
@ -352,15 +352,10 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar
|
||||
FS::ensureFolderPathExists(FS::PathCombine(m_dataPath, "logs"));
|
||||
static const QString baseLogFile = BuildConfig.LAUNCHER_NAME + "Updater" + (m_checkOnly ? "-CheckOnly" : "") + "-%0.log";
|
||||
static const QString logBase = FS::PathCombine(m_dataPath, "logs", baseLogFile);
|
||||
auto moveFile = [](const QString& oldName, const QString& newName) {
|
||||
QFile::remove(newName);
|
||||
QFile::copy(oldName, newName);
|
||||
QFile::remove(oldName);
|
||||
};
|
||||
|
||||
if (FS::ensureFolderPathExists("logs")) { // enough history to track both launches of the updater during a portable install
|
||||
moveFile(logBase.arg(1), logBase.arg(2));
|
||||
moveFile(logBase.arg(0), logBase.arg(1));
|
||||
FS::move(logBase.arg(1), logBase.arg(2));
|
||||
FS::move(logBase.arg(0), logBase.arg(1));
|
||||
}
|
||||
|
||||
logFile = std::unique_ptr<QFile>(new QFile(logBase.arg(0)));
|
||||
@ -924,7 +919,7 @@ bool PrismUpdaterApp::callAppImageUpdate()
|
||||
|
||||
void PrismUpdaterApp::clearUpdateLog()
|
||||
{
|
||||
QFile::remove(m_updateLogPath);
|
||||
FS::deletePath(m_updateLogPath);
|
||||
}
|
||||
|
||||
void PrismUpdaterApp::logUpdate(const QString& msg)
|
||||
|
Loading…
x
Reference in New Issue
Block a user