aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSulfurNitride <SulfurNitride@users.noreply.github.com>2026-05-23 10:23:32 -0500
committerSulfurNitride <SulfurNitride@users.noreply.github.com>2026-05-23 10:23:32 -0500
commitde4e588e2d76a285381061fdfbd74a57f6593ffd (patch)
treef3c9a222574173e8c81359b0d881a16f73b7f62b
parent7f0ded9e4a6e11b5e0327dd8eb76dd5945b8693d (diff)
prefix: improve setup progress and failure logs
-rw-r--r--src/src/prefixsetupdialog.cpp138
-rw-r--r--src/src/prefixsetupdialog.h10
-rw-r--r--src/src/prefixsetuprunner.cpp102
-rw-r--r--src/src/prefixsetuprunner.h9
4 files changed, 217 insertions, 42 deletions
diff --git a/src/src/prefixsetupdialog.cpp b/src/src/prefixsetupdialog.cpp
index 781284f..2d7730f 100644
--- a/src/src/prefixsetupdialog.cpp
+++ b/src/src/prefixsetupdialog.cpp
@@ -3,7 +3,10 @@
#include "fluorineconfig.h"
#include "fluorinepaths.h"
+#include <QDateTime>
#include <QDesktopServices>
+#include <QDir>
+#include <QFile>
#include <QHBoxLayout>
#include <QLabel>
#include <QMessageBox>
@@ -15,6 +18,24 @@
#include <log.h>
#include <utility.h>
+namespace
+{
+QString formatByteCount(qint64 bytes)
+{
+ static const char* units[] = {"B", "KiB", "MiB", "GiB"};
+ double value = static_cast<double>(bytes);
+ int unit = 0;
+ while (value >= 1024.0 && unit < 3) {
+ value /= 1024.0;
+ ++unit;
+ }
+
+ return unit == 0
+ ? QStringLiteral("%1 %2").arg(bytes).arg(units[unit])
+ : QStringLiteral("%1 %2").arg(value, 0, 'f', 1).arg(units[unit]);
+}
+}
+
// ============================================================================
// Construction / destruction
// ============================================================================
@@ -51,6 +72,12 @@ PrefixSetupDialog::PrefixSetupDialog(const QString& prefixPath,
this, &PrefixSetupDialog::onStepFinished, Qt::QueuedConnection);
connect(m_runner, &PrefixSetupRunner::logMessage,
this, &PrefixSetupDialog::onLogMessage, Qt::QueuedConnection);
+ connect(m_runner, &PrefixSetupRunner::downloadStarted,
+ this, &PrefixSetupDialog::onDownloadStarted, Qt::QueuedConnection);
+ connect(m_runner, &PrefixSetupRunner::downloadProgress,
+ this, &PrefixSetupDialog::onDownloadProgress, Qt::QueuedConnection);
+ connect(m_runner, &PrefixSetupRunner::downloadFinished,
+ this, &PrefixSetupDialog::onDownloadFinished, Qt::QueuedConnection);
connect(m_runner, &PrefixSetupRunner::progressChanged,
this, &PrefixSetupDialog::onProgressChanged, Qt::QueuedConnection);
connect(m_runner, &PrefixSetupRunner::finished,
@@ -111,6 +138,17 @@ void PrefixSetupDialog::buildUI()
m_progressBar->setTextVisible(true);
mainLayout->addWidget(m_progressBar);
+ m_downloadLabel = new QLabel(this);
+ m_downloadLabel->setVisible(false);
+ mainLayout->addWidget(m_downloadLabel);
+
+ m_downloadProgressBar = new QProgressBar(this);
+ m_downloadProgressBar->setRange(0, 100);
+ m_downloadProgressBar->setValue(0);
+ m_downloadProgressBar->setTextVisible(true);
+ m_downloadProgressBar->setVisible(false);
+ mainLayout->addWidget(m_downloadProgressBar);
+
// Button row.
auto* buttonLayout = new QHBoxLayout;
buttonLayout->addStretch();
@@ -124,7 +162,12 @@ void PrefixSetupDialog::buildUI()
connect(m_retryBtn, &QPushButton::clicked, this, &PrefixSetupDialog::onRetryFailed);
buttonLayout->addWidget(m_retryBtn);
- m_deleteBtn = new QPushButton(tr("Delete Prefix && Show Log"), this);
+ m_showLogBtn = new QPushButton(tr("Show Log"), this);
+ m_showLogBtn->setVisible(false);
+ connect(m_showLogBtn, &QPushButton::clicked, this, &PrefixSetupDialog::onShowLog);
+ buttonLayout->addWidget(m_showLogBtn);
+
+ m_deleteBtn = new QPushButton(tr("Delete Prefix"), this);
m_deleteBtn->setVisible(false);
connect(m_deleteBtn, &QPushButton::clicked, this, &PrefixSetupDialog::onDeleteAndClose);
buttonLayout->addWidget(m_deleteBtn);
@@ -151,10 +194,35 @@ void PrefixSetupDialog::updateButtons()
{
m_cancelBtn->setVisible(m_running);
m_retryBtn->setVisible(!m_running && !m_allSucceeded);
+ m_showLogBtn->setVisible(!m_running && !m_allSucceeded);
m_deleteBtn->setVisible(!m_running && !m_allSucceeded);
m_closeBtn->setVisible(!m_running);
}
+QString PrefixSetupDialog::writeSetupLog() const
+{
+ const QString logsPath = fluorineDataDir() + "/logs";
+ QDir().mkpath(logsPath);
+
+ const QString timestamp =
+ QDateTime::currentDateTime().toString(QStringLiteral("yyyyMMdd-hhmmss"));
+ const QString logPath =
+ QStringLiteral("%1/prefix-setup-%2.log").arg(logsPath, timestamp);
+
+ QFile file(logPath);
+ if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
+ return {};
+
+ file.write(m_logView->toPlainText().toUtf8());
+ file.write("\n");
+ file.close();
+
+ QFile::remove(logsPath + "/prefix-setup-latest.log");
+ QFile::copy(logPath, logsPath + "/prefix-setup-latest.log");
+
+ return logPath;
+}
+
// ============================================================================
// Auto-start on show
// ============================================================================
@@ -219,6 +287,54 @@ void PrefixSetupDialog::onLogMessage(const QString& text)
if (sb) sb->setValue(sb->maximum());
}
+void PrefixSetupDialog::onDownloadStarted(const QString& name)
+{
+ m_downloadLabel->setText(tr("Downloading %1...").arg(name));
+ m_downloadLabel->setVisible(true);
+ m_downloadProgressBar->setRange(0, 0);
+ m_downloadProgressBar->setValue(0);
+ m_downloadProgressBar->setFormat(tr("Starting..."));
+ m_downloadProgressBar->setVisible(true);
+}
+
+void PrefixSetupDialog::onDownloadProgress(const QString& name,
+ qint64 bytesReceived,
+ qint64 bytesTotal,
+ double bytesPerSecond)
+{
+ const QString speed =
+ bytesPerSecond > 0.0
+ ? tr("%1/s").arg(formatByteCount(static_cast<qint64>(bytesPerSecond)))
+ : tr("calculating...");
+
+ if (bytesTotal > 0) {
+ const int percent =
+ static_cast<int>((bytesReceived * 100.0) / static_cast<double>(bytesTotal));
+ m_downloadProgressBar->setRange(0, 100);
+ m_downloadProgressBar->setValue(percent);
+ m_downloadProgressBar->setFormat(
+ tr("%1% - %2 / %3 - %4")
+ .arg(percent)
+ .arg(formatByteCount(bytesReceived),
+ formatByteCount(bytesTotal),
+ speed));
+ } else {
+ m_downloadProgressBar->setRange(0, 0);
+ m_downloadProgressBar->setFormat(
+ tr("%1 downloaded - %2").arg(formatByteCount(bytesReceived), speed));
+ }
+
+ m_downloadLabel->setText(tr("Downloading %1...").arg(name));
+}
+
+void PrefixSetupDialog::onDownloadFinished()
+{
+ m_downloadProgressBar->setVisible(false);
+ m_downloadProgressBar->setRange(0, 100);
+ m_downloadProgressBar->setValue(0);
+ m_downloadLabel->setVisible(false);
+}
+
void PrefixSetupDialog::onProgressChanged(float progress)
{
m_progressBar->setValue(static_cast<int>(progress * 100.0f));
@@ -277,12 +393,26 @@ void PrefixSetupDialog::onRetryFailed()
QMetaObject::invokeMethod(m_runner, "retryFailed", Qt::QueuedConnection);
}
+void PrefixSetupDialog::onShowLog()
+{
+ const QString logPath = writeSetupLog();
+ if (logPath.isEmpty()) {
+ QMessageBox::warning(this, tr("Show Log"),
+ tr("Could not write the setup log."));
+ return;
+ }
+
+ if (!QDesktopServices::openUrl(QUrl::fromLocalFile(logPath))) {
+ QMessageBox::information(this, tr("Setup Log"),
+ tr("Setup log written to:\n%1").arg(logPath));
+ }
+}
+
void PrefixSetupDialog::onDeleteAndClose()
{
const auto answer = QMessageBox::warning(
this, tr("Delete Prefix"),
tr("This will delete the Wine prefix at:\n%1\n\n"
- "The log folder will be opened so you can share the log.\n\n"
"Continue?").arg(m_prefixPath),
QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
@@ -294,10 +424,6 @@ void PrefixSetupDialog::onDeleteAndClose()
cfg.prefix_path = m_prefixPath;
cfg.destroyPrefix();
- // Open the logs folder.
- const QString logsPath = fluorineDataDir() + "/logs";
- QDesktopServices::openUrl(QUrl::fromLocalFile(logsPath));
-
reject();
}
diff --git a/src/src/prefixsetupdialog.h b/src/src/prefixsetupdialog.h
index 3c23ff1..3285c43 100644
--- a/src/src/prefixsetupdialog.h
+++ b/src/src/prefixsetupdialog.h
@@ -4,6 +4,7 @@
#include "prefixsetuprunner.h"
#include <QDialog>
+#include <QLabel>
#include <QListWidget>
#include <QProgressBar>
#include <QPushButton>
@@ -37,11 +38,16 @@ private slots:
void onStepStarted(int index);
void onStepFinished(int index, bool success, const QString& error);
void onLogMessage(const QString& text);
+ void onDownloadStarted(const QString& name);
+ void onDownloadProgress(const QString& name, qint64 bytesReceived,
+ qint64 bytesTotal, double bytesPerSecond);
+ void onDownloadFinished();
void onProgressChanged(float progress);
void onFinished(bool allSucceeded);
void onCancel();
void onRetryFailed();
+ void onShowLog();
void onDeleteAndClose();
void onClose();
@@ -49,6 +55,7 @@ private:
void buildUI();
void populateStepList();
void updateButtons();
+ QString writeSetupLog() const;
// -- state -----------------------------------------------------------------
QString m_prefixPath;
@@ -66,9 +73,12 @@ private:
QListWidget* m_stepList = nullptr;
QTextEdit* m_logView = nullptr;
QProgressBar* m_progressBar = nullptr;
+ QLabel* m_downloadLabel = nullptr;
+ QProgressBar* m_downloadProgressBar = nullptr;
QLabel* m_statusLabel = nullptr;
QPushButton* m_cancelBtn = nullptr;
QPushButton* m_retryBtn = nullptr;
+ QPushButton* m_showLogBtn = nullptr;
QPushButton* m_deleteBtn = nullptr;
QPushButton* m_closeBtn = nullptr;
};
diff --git a/src/src/prefixsetuprunner.cpp b/src/src/prefixsetuprunner.cpp
index 4684628..2233160 100644
--- a/src/src/prefixsetuprunner.cpp
+++ b/src/src/prefixsetuprunner.cpp
@@ -9,6 +9,7 @@
#include <QCoreApplication>
#include <QDir>
+#include <QElapsedTimer>
#include <QEventLoop>
#include <QFile>
#include <QFileInfo>
@@ -675,9 +676,11 @@ bool PrefixSetupRunner::runStep(int index)
{
m_steps[index].status = SetupStep::Running;
m_steps[index].errorMessage.clear();
+ m_currentStepIndex = index;
emit stepStarted(index);
const bool ok = m_stepFunctions[index]();
+ m_currentStepIndex = -1;
m_steps[index].status = ok ? SetupStep::Succeeded : SetupStep::Failed;
if (!ok && m_steps[index].errorMessage.isEmpty())
@@ -940,7 +943,7 @@ bool PrefixSetupRunner::stepProtonInit()
{
const QString protonScript = findProtonScript();
if (protonScript.isEmpty()) {
- m_steps.last().errorMessage = "Proton wrapper script not found";
+ currentStep().errorMessage = "Proton wrapper script not found";
return false;
}
@@ -1007,14 +1010,14 @@ bool PrefixSetupRunner::stepProtonInit()
const QString missing =
(end > start) ? QString::fromUtf8(out.mid(start, end - start))
: QStringLiteral("default_pfx/drive_c/...");
- m_steps.last().errorMessage =
+ currentStep().errorMessage =
QStringLiteral(
"Proton install is incomplete: missing '%1'. "
"Reinstall or verify your Proton build (e.g. GE-Proton) — "
"this is not a Fluorine bug.")
.arg(missing);
} else {
- m_steps.last().errorMessage =
+ currentStep().errorMessage =
QStringLiteral("proton wineboot failed (exit code %1)").arg(rc);
}
return false;
@@ -1024,7 +1027,7 @@ bool PrefixSetupRunner::stepProtonInit()
QThread::sleep(2);
if (!QDir(m_prefixPath).exists()) {
- m_steps.last().errorMessage = "Prefix directory not created after wineboot";
+ currentStep().errorMessage = "Prefix directory not created after wineboot";
return false;
}
@@ -1130,7 +1133,7 @@ bool PrefixSetupRunner::extractFromRedist(const QString& redistPath,
int rc = runHostProcess(cabextractBin,
{"-d", tmpDir, "-L", "-F", cabFilter, redistPath});
if (rc != 0) {
- m_steps.last().errorMessage =
+ currentStep().errorMessage =
QStringLiteral("cabextract failed (filter: %1, exit %2)").arg(cabFilter).arg(rc);
return false;
}
@@ -1138,7 +1141,7 @@ bool PrefixSetupRunner::extractFromRedist(const QString& redistPath,
// Stage 2: extract DLL(s) from each inner cab.
const QStringList cabs = QDir(tmpDir).entryList({"*.cab"}, QDir::Files);
if (cabs.isEmpty()) {
- m_steps.last().errorMessage =
+ currentStep().errorMessage =
QStringLiteral("No inner cab found for filter: %1").arg(cabFilter);
return false;
}
@@ -1147,7 +1150,7 @@ bool PrefixSetupRunner::extractFromRedist(const QString& redistPath,
rc = runHostProcess(cabextractBin,
{"-d", destDir, "-L", "-F", dllFilter, tmpDir + "/" + cab});
if (rc != 0) {
- m_steps.last().errorMessage =
+ currentStep().errorMessage =
QStringLiteral("cabextract inner failed (filter: %1, exit %2)").arg(dllFilter).arg(rc);
return false;
}
@@ -1186,7 +1189,7 @@ bool PrefixSetupRunner::stepD3DCompiler47()
const QString dest = dllDir32 + "/d3dcompiler_47.dll";
QFile::remove(dest);
if (!QFile::copy(cached, dest)) {
- m_steps.last().errorMessage = "Failed to copy d3dcompiler_47.dll to syswow64";
+ currentStep().errorMessage = "Failed to copy d3dcompiler_47.dll to syswow64";
return false;
}
}
@@ -1202,7 +1205,7 @@ bool PrefixSetupRunner::stepD3DCompiler47()
const QString dest = dllDir64 + "/d3dcompiler_47.dll";
QFile::remove(dest);
if (!QFile::copy(cached, dest)) {
- m_steps.last().errorMessage = "Failed to copy d3dcompiler_47.dll to system32";
+ currentStep().errorMessage = "Failed to copy d3dcompiler_47.dll to system32";
return false;
}
}
@@ -1358,14 +1361,14 @@ bool PrefixSetupRunner::stepVcrun2022()
int rc = runHostProcess(cabextractBin,
{"--directory=" + tmpDir + "/win32", x86Path, "-F", "a10"});
if (rc != 0) {
- m_steps.last().errorMessage =
+ currentStep().errorMessage =
QStringLiteral("vc_redist.x86.exe cab extraction failed (exit code %1)").arg(rc);
return false;
}
rc = runHostProcess(cabextractBin,
{"--directory=" + dllDir32, tmpDir + "/win32/a10", "-F", "msvcp140.dll"});
if (rc != 0) {
- m_steps.last().errorMessage =
+ currentStep().errorMessage =
QStringLiteral("msvcp140.dll x86 extraction failed (exit code %1)").arg(rc);
return false;
}
@@ -1377,7 +1380,7 @@ bool PrefixSetupRunner::stepVcrun2022()
rc = runProcess(m_wineBin, {x86Path, "/install", "/quiet", "/norestart"}, env);
if (!isMicrosoftInstallerSuccess(rc)) {
- m_steps.last().errorMessage =
+ currentStep().errorMessage =
QStringLiteral("vc_redist.x86.exe failed (exit code %1, SHA256 %2)")
.arg(rc)
.arg(fileSha256(x86Path));
@@ -1405,14 +1408,14 @@ bool PrefixSetupRunner::stepVcrun2022()
rc = runHostProcess(cabextractBin,
{"--directory=" + tmpDir + "/win64", x64Path, "-F", "a12"});
if (rc != 0) {
- m_steps.last().errorMessage =
+ currentStep().errorMessage =
QStringLiteral("vc_redist.x64.exe cab extraction failed (exit code %1)").arg(rc);
return false;
}
rc = runHostProcess(cabextractBin,
{"--directory=" + dllDir64, tmpDir + "/win64/a12", "-F", "msvcp140.dll"});
if (rc != 0) {
- m_steps.last().errorMessage =
+ currentStep().errorMessage =
QStringLiteral("msvcp140.dll x64 extraction failed (exit code %1)").arg(rc);
return false;
}
@@ -1420,7 +1423,7 @@ bool PrefixSetupRunner::stepVcrun2022()
emit logMessage("Running vc_redist.x64.exe...");
rc = runProcess(m_wineBin, {x64Path, "/install", "/quiet", "/norestart"}, env);
if (!isMicrosoftInstallerSuccess(rc)) {
- m_steps.last().errorMessage =
+ currentStep().errorMessage =
QStringLiteral("vc_redist.x64.exe failed (exit code %1, SHA256 %2)")
.arg(rc)
.arg(fileSha256(x64Path));
@@ -1502,7 +1505,7 @@ bool PrefixSetupRunner::stepDotNetInstallPair(const QString& url32, const QStrin
emit logMessage(QStringLiteral("Installing %1 (32-bit)...").arg(name));
const int rc = runProcess(m_wineBin, {path, "/install", "/quiet", "/norestart"}, env);
if (!isMicrosoftInstallerSuccess(rc)) {
- m_steps.last().errorMessage =
+ currentStep().errorMessage =
QStringLiteral("%1 x86 installer failed (exit code %2)").arg(name).arg(rc);
return false;
} else if (rc != 0) {
@@ -1533,7 +1536,7 @@ bool PrefixSetupRunner::stepDotNetInstallPair(const QString& url32, const QStrin
emit logMessage(QStringLiteral("Installing %1 (64-bit)...").arg(name));
const int rc = runProcess(m_wineBin, {path, "/install", "/quiet", "/norestart"}, env);
if (!isMicrosoftInstallerSuccess(rc)) {
- m_steps.last().errorMessage =
+ currentStep().errorMessage =
QStringLiteral("%1 x64 installer failed (exit code %2)").arg(name).arg(rc);
return false;
} else if (rc != 0) {
@@ -1578,7 +1581,7 @@ bool PrefixSetupRunner::stepDotNetInstall(const QString& url, const QString& nam
env);
if (!isMicrosoftInstallerSuccess(rc)) {
- m_steps.last().errorMessage =
+ currentStep().errorMessage =
QStringLiteral("%1 installer failed (exit code %2)").arg(name).arg(rc);
return false;
} else if (rc != 0) {
@@ -1642,7 +1645,7 @@ bool PrefixSetupRunner::stepGameDetection()
const QString regFile = tmpDir + "/game_registry.reg";
QFile f(regFile);
if (!f.open(QIODevice::WriteOnly | QIODevice::Text)) {
- m_steps.last().errorMessage = "Failed to write game registry file";
+ currentStep().errorMessage = "Failed to write game registry file";
return false;
}
f.write(regContent.toUtf8());
@@ -1657,7 +1660,7 @@ bool PrefixSetupRunner::stepGameDetection()
QFile::remove(regFile);
if (rc != 0) {
- m_steps.last().errorMessage =
+ currentStep().errorMessage =
QStringLiteral("wine regedit failed (exit code %1)").arg(rc);
return false;
}
@@ -1676,7 +1679,7 @@ bool PrefixSetupRunner::stepWineRegistry()
const QString regFile = tmpDir + "/wine_settings.reg";
QFile f(regFile);
if (!f.open(QIODevice::WriteOnly | QIODevice::Text)) {
- m_steps.last().errorMessage = "Failed to write registry file";
+ currentStep().errorMessage = "Failed to write registry file";
return false;
}
f.write(WINE_SETTINGS_REG);
@@ -1690,7 +1693,7 @@ bool PrefixSetupRunner::stepWineRegistry()
QFile::remove(regFile);
if (rc != 0) {
- m_steps.last().errorMessage =
+ currentStep().errorMessage =
QStringLiteral("wine regedit failed (exit code %1)").arg(rc);
return false;
}
@@ -1719,7 +1722,7 @@ bool PrefixSetupRunner::stepWin11Mode()
const int rc = runProcess("/bin/sh", {"-c", shellCmd}, env);
if (rc != 0) {
- m_steps.last().errorMessage =
+ currentStep().errorMessage =
QStringLiteral("winetricks win11 failed (exit code %1)").arg(rc);
return false;
}
@@ -1753,7 +1756,8 @@ static void setExecPermissions(const QString& path)
QFileDevice::ReadOther | QFileDevice::ExeOther);
}
-bool PrefixSetupRunner::downloadFile(const QString& url, const QString& destPath)
+bool PrefixSetupRunner::downloadFile(const QString& url, const QString& destPath,
+ const QString& displayName)
{
// Use Qt networking — no dependency on host curl.
QNetworkAccessManager nam;
@@ -1762,14 +1766,33 @@ bool PrefixSetupRunner::downloadFile(const QString& url, const QString& destPath
QNetworkRequest::NoLessSafeRedirectPolicy);
request.setHeader(QNetworkRequest::UserAgentHeader, "Fluorine-Manager");
+ const QString shownName =
+ displayName.isEmpty() ? QFileInfo(destPath).fileName() : displayName;
+ QElapsedTimer timer;
+ timer.start();
+
QNetworkReply* reply = nam.get(request);
QEventLoop loop;
+ emit downloadStarted(shownName);
+ QObject::connect(reply, &QNetworkReply::downloadProgress,
+ this,
+ [this, shownName, &timer](qint64 received, qint64 total) {
+ const qint64 elapsedMs = timer.elapsed();
+ const double bytesPerSecond =
+ elapsedMs > 0
+ ? static_cast<double>(received) * 1000.0 /
+ static_cast<double>(elapsedMs)
+ : 0.0;
+ emit downloadProgress(shownName, received, total,
+ bytesPerSecond);
+ });
QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
loop.exec();
if (reply->error() != QNetworkReply::NoError) {
emit logMessage(QStringLiteral("Download failed: %1").arg(reply->errorString()));
reply->deleteLater();
+ emit downloadFinished();
return false;
}
@@ -1777,12 +1800,14 @@ bool PrefixSetupRunner::downloadFile(const QString& url, const QString& destPath
if (!file.open(QIODevice::WriteOnly)) {
emit logMessage(QStringLiteral("Failed to write: %1").arg(destPath));
reply->deleteLater();
+ emit downloadFinished();
return false;
}
file.write(reply->readAll());
file.close();
reply->deleteLater();
+ emit downloadFinished();
return true;
}
@@ -1823,9 +1848,9 @@ bool PrefixSetupRunner::downloadRuntimeInstaller(const QString& url,
const QString partPath = destPath + ".part";
QFile::remove(partPath);
- if (!downloadFile(url, partPath)) {
+ if (!downloadFile(url, partPath, displayName)) {
QFile::remove(partPath);
- m_steps.last().errorMessage =
+ currentStep().errorMessage =
QStringLiteral("Failed to download %1").arg(displayName);
return false;
}
@@ -1839,7 +1864,7 @@ bool PrefixSetupRunner::downloadRuntimeInstaller(const QString& url,
QFile::remove(destPath);
if (!QFile::rename(partPath, destPath)) {
QFile::remove(partPath);
- m_steps.last().errorMessage =
+ currentStep().errorMessage =
QStringLiteral("Failed to cache %1").arg(displayName);
return false;
}
@@ -1863,20 +1888,20 @@ bool PrefixSetupRunner::validateRuntimeInstaller(const QString& path,
QFile file(path);
if (!file.open(QIODevice::ReadOnly)) {
- m_steps.last().errorMessage =
+ currentStep().errorMessage =
QStringLiteral("Failed to read %1").arg(displayName);
return false;
}
if (file.size() < 1024 * 1024) {
- m_steps.last().errorMessage =
+ currentStep().errorMessage =
QStringLiteral("%1 is too small to be a valid installer").arg(displayName);
return false;
}
const QByteArray magic = file.read(2);
if (magic != "MZ") {
- m_steps.last().errorMessage =
+ currentStep().errorMessage =
QStringLiteral("%1 is not a Windows PE installer").arg(displayName);
return false;
}
@@ -1884,7 +1909,7 @@ bool PrefixSetupRunner::validateRuntimeInstaller(const QString& path,
const QString sha = fileSha256(path);
if (sha.isEmpty()) {
- m_steps.last().errorMessage =
+ currentStep().errorMessage =
QStringLiteral("Failed to hash %1").arg(displayName);
return false;
}
@@ -1930,13 +1955,13 @@ bool PrefixSetupRunner::downloadAndVerify(const QString& url, const QString& des
const QString& expectedSha256)
{
if (!downloadFile(url, destPath)) {
- m_steps.last().errorMessage = QStringLiteral("Failed to download %1").arg(url);
+ currentStep().errorMessage = QStringLiteral("Failed to download %1").arg(url);
return false;
}
if (!verifySha256(destPath, expectedSha256)) {
QFile::remove(destPath);
- m_steps.last().errorMessage =
+ currentStep().errorMessage =
QStringLiteral("SHA256 mismatch for %1").arg(QUrl(url).fileName());
return false;
}
@@ -2250,7 +2275,7 @@ bool PrefixSetupRunner::applyDllOverrides(const QStringList& native,
const QString regFile = tmpDir + "/dependency_overrides.reg";
QFile f(regFile);
if (!f.open(QIODevice::WriteOnly | QIODevice::Text)) {
- m_steps.last().errorMessage = "Failed to write dependency override registry";
+ currentStep().errorMessage = "Failed to write dependency override registry";
return false;
}
f.write(regContent.toUtf8());
@@ -2263,7 +2288,7 @@ bool PrefixSetupRunner::applyDllOverrides(const QStringList& native,
const int rc = runProcess(m_wineBin, {"regedit", regFile}, env);
QFile::remove(regFile);
if (rc != 0) {
- m_steps.last().errorMessage =
+ currentStep().errorMessage =
QStringLiteral("dependency override import failed (exit code %1)").arg(rc);
return false;
}
@@ -2291,3 +2316,10 @@ bool PrefixSetupRunner::isMicrosoftInstallerSuccess(int exitCode)
{
return exitCode == 0 || exitCode == 105 || exitCode == 194 || exitCode == 236;
}
+
+SetupStep& PrefixSetupRunner::currentStep()
+{
+ if (m_currentStepIndex >= 0 && m_currentStepIndex < m_steps.size())
+ return m_steps[m_currentStepIndex];
+ return m_steps.last();
+}
diff --git a/src/src/prefixsetuprunner.h b/src/src/prefixsetuprunner.h
index 9f3f0c8..3eda26c 100644
--- a/src/src/prefixsetuprunner.h
+++ b/src/src/prefixsetuprunner.h
@@ -45,6 +45,10 @@ signals:
void stepStarted(int index);
void stepFinished(int index, bool success, const QString& error);
void logMessage(const QString& text);
+ void downloadStarted(const QString& name);
+ void downloadProgress(const QString& name, qint64 bytesReceived,
+ qint64 bytesTotal, double bytesPerSecond);
+ void downloadFinished();
void progressChanged(float progress); ///< 0.0 – 1.0
void finished(bool allSucceeded);
@@ -115,7 +119,8 @@ private:
const QString& dllFilter, const QString& destDir);
// -- tool management -------------------------------------------------------
- bool downloadFile(const QString& url, const QString& destPath);
+ bool downloadFile(const QString& url, const QString& destPath,
+ const QString& displayName = {});
bool downloadAndVerify(const QString& url, const QString& destPath,
const QString& expectedSha256);
bool downloadRuntimeInstaller(const QString& url, const QString& destPath,
@@ -146,6 +151,7 @@ private:
const QStringList& native,
const QStringList& nativeBuiltin);
static bool isMicrosoftInstallerSuccess(int exitCode);
+ SetupStep& currentStep();
/// Kill any wineboot/wineserver/pv-adverb processes still bound to
/// m_prefixPath from a previous aborted run. Safe to call when none exist.
@@ -156,6 +162,7 @@ private:
QString m_protonPath;
uint32_t m_appId;
QAtomicInt m_cancelled{0};
+ int m_currentStepIndex = -1;
QString m_wineBin;
QString m_wineserverBin;