diff options
| author | SulfurNitride <SulfurNitride@users.noreply.github.com> | 2026-05-23 10:23:32 -0500 |
|---|---|---|
| committer | SulfurNitride <SulfurNitride@users.noreply.github.com> | 2026-05-23 10:23:32 -0500 |
| commit | de4e588e2d76a285381061fdfbd74a57f6593ffd (patch) | |
| tree | f3c9a222574173e8c81359b0d881a16f73b7f62b | |
| parent | 7f0ded9e4a6e11b5e0327dd8eb76dd5945b8693d (diff) | |
prefix: improve setup progress and failure logs
| -rw-r--r-- | src/src/prefixsetupdialog.cpp | 138 | ||||
| -rw-r--r-- | src/src/prefixsetupdialog.h | 10 | ||||
| -rw-r--r-- | src/src/prefixsetuprunner.cpp | 102 | ||||
| -rw-r--r-- | src/src/prefixsetuprunner.h | 9 |
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; |
