Merge pull request #1409 from Trial97/import_zip

feat: refactored Instance ImportTask
This commit is contained in:
Alexandru Ionut Tripon 2024-06-07 00:11:37 +03:00 committed by GitHub
commit e961cab352
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 306 additions and 146 deletions

View File

@ -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)) {

View File

@ -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

View File

@ -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)

View File

@ -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;

View File

@ -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();

View File

@ -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,

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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 {};

View File

@ -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);
}
}

View File

@ -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...

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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:

View File

@ -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)