mirror of
https://github.com/Vita3K/Vita3K.git
synced 2026-07-11 01:34:23 +02:00
gui-qt: don't crash when deleting an archive locked by another process (#3956)
* gui-qt: don't crash on install file delete failures when you install a .zip, .vpk, .pkg, or firmware .pup and tick the matching "delete after install" option, vita3k tries to remove the installer file from disk. the archive and pkg dialogs use the throwing fs::remove overload, which raises a filesystem_error when the file is locked by another process. since the call runs inside a qt slot, the exception escapes into the qt event loop and the whole emulator goes down with it. the firmware dialog uses QFile::remove instead, which returns a bool that the code was ignoring, so a locked firmware file silently stuck around with no feedback to the user. leaving the installer open in something like 7-zip while installing is enough to trigger either case. this swaps fs::remove for the non-throwing error_code overload in the archive and pkg dialogs, and checks the bool from QFile::remove in the firmware dialog. anything that could not be removed gets shown in a warning so the user knows what stuck around, and the install itself still finishes normally. * gui-qt: route firmware drops through the install dialog dropping a .pup onto the main window used to run install_pup inline with a bare QProgressDialog and its own success/failure handling, bypassing FirmwareInstallDialog entirely. that path duplicated the progress UI and never offered the "delete firmware file after install?" option that the menu-triggered flow exposes. route the drop through FirmwareInstallDialog instead, matching the existing .pkg and .vpk drop routing right next to it. this adds a constructor that takes a pre-supplied firmware path so the dialog skips its file picker when invoked from a drop. dropped firmware installs now share the threaded progress UI, the warning when the file is locked, and the optional cleanup checkbox with the menu trigger.
This commit is contained in:
@@ -52,6 +52,7 @@ class FirmwareInstallDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit FirmwareInstallDialog(EmuEnvState &emuenv, QWidget *parent = nullptr);
|
||||
FirmwareInstallDialog(EmuEnvState &emuenv, const QString &path, QWidget *parent = nullptr);
|
||||
|
||||
Q_SIGNALS:
|
||||
void install_complete();
|
||||
|
||||
@@ -431,9 +431,20 @@ void ArchiveInstallDialog::run_install(const std::vector<fs::path> &archives) {
|
||||
[this, del_check, results]() {
|
||||
if (del_check->isChecked()) {
|
||||
std::set<fs::path> deleted;
|
||||
QStringList failed;
|
||||
for (const auto &r : results) {
|
||||
if (!r.archive_full_path.empty() && deleted.insert(r.archive_full_path).second)
|
||||
fs::remove(r.archive_full_path);
|
||||
if (r.archive_full_path.empty() || !deleted.insert(r.archive_full_path).second)
|
||||
continue;
|
||||
// fs::remove throws and crashes the app when the archive is locked so use the non throwing overload
|
||||
boost::system::error_code error;
|
||||
fs::remove(r.archive_full_path, error);
|
||||
if (error)
|
||||
failed.push_back(r.archive_name);
|
||||
}
|
||||
|
||||
if (!failed.isEmpty()) {
|
||||
QMessageBox::warning(this, tr("Could Not Delete Archives"),
|
||||
tr("The following archive files could not be deleted. They may still be open in another program:\n\n%1").arg(failed.join(QStringLiteral("\n"))));
|
||||
}
|
||||
}
|
||||
Q_EMIT install_complete(results);
|
||||
|
||||
@@ -60,6 +60,20 @@ FirmwareInstallDialog::FirmwareInstallDialog(EmuEnvState &emuenv, QWidget *paren
|
||||
run_install(path);
|
||||
}
|
||||
|
||||
FirmwareInstallDialog::FirmwareInstallDialog(EmuEnvState &emuenv, const QString &path, QWidget *parent)
|
||||
: QDialog(parent)
|
||||
, m_emuenv(emuenv) {
|
||||
setWindowTitle(tr("Install Firmware"));
|
||||
setModal(true);
|
||||
|
||||
if (path.isEmpty()) {
|
||||
QMetaObject::invokeMethod(this, &QDialog::reject, Qt::QueuedConnection);
|
||||
return;
|
||||
}
|
||||
|
||||
run_install(path);
|
||||
}
|
||||
|
||||
void FirmwareInstallDialog::run_install(const QString &path) {
|
||||
auto *main_layout = new QVBoxLayout(this);
|
||||
|
||||
@@ -122,8 +136,11 @@ void FirmwareInstallDialog::run_install(const QString &path) {
|
||||
layout()->addWidget(buttons);
|
||||
|
||||
connect(buttons, &QDialogButtonBox::accepted, this, [this, del_check, path]() {
|
||||
if (del_check->isChecked())
|
||||
QFile::remove(path);
|
||||
// QFile::remove returns false silently when the firmware file is locked, so surface that to the user
|
||||
if (del_check->isChecked() && !QFile::remove(path)) {
|
||||
QMessageBox::warning(this, tr("Could Not Delete Firmware"),
|
||||
tr("The firmware file could not be deleted. It may still be open in another program:\n\n%1").arg(path));
|
||||
}
|
||||
Q_EMIT install_complete();
|
||||
accept();
|
||||
});
|
||||
|
||||
@@ -93,7 +93,6 @@
|
||||
#include <QMimeData>
|
||||
#include <QOpenGLContext>
|
||||
#include <QPainter>
|
||||
#include <QProgressDialog>
|
||||
#include <QPushButton>
|
||||
#include <QSlider>
|
||||
#include <QStandardPaths>
|
||||
@@ -715,31 +714,11 @@ void MainWindow::handle_drop(const QString &path) {
|
||||
const std::string ext = string_utils::tolower(drop_path.extension().string());
|
||||
|
||||
if (ext == ".pup") {
|
||||
QProgressDialog prog(tr("Installing firmware\u2026"),
|
||||
/*cancel=*/QString(), 0, 100, this);
|
||||
prog.setWindowTitle(tr("Install Firmware"));
|
||||
prog.setWindowModality(Qt::WindowModal);
|
||||
prog.setMinimumDuration(0);
|
||||
prog.setValue(0);
|
||||
|
||||
const std::string fw_version = install_pup(
|
||||
emuenv.pref_path,
|
||||
drop_path,
|
||||
[&prog](uint32_t pct) {
|
||||
prog.setValue(static_cast<int>(pct));
|
||||
QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
|
||||
});
|
||||
|
||||
prog.setValue(100);
|
||||
|
||||
if (!fw_version.empty()) {
|
||||
LOG_INFO("Firmware {} installed successfully via drop!", fw_version);
|
||||
on_install_finished();
|
||||
} else {
|
||||
QMessageBox::critical(this, tr("Install Failed"),
|
||||
tr("Failed to install the dropped firmware file.\n"
|
||||
"Check the log for details."));
|
||||
}
|
||||
auto *dlg = new FirmwareInstallDialog(emuenv, path, this);
|
||||
connect(dlg, &FirmwareInstallDialog::install_complete,
|
||||
this, &MainWindow::on_install_finished);
|
||||
connect(dlg, &QDialog::finished, dlg, &QObject::deleteLater);
|
||||
dlg->exec();
|
||||
|
||||
} else if (ext == ".vpk" || ext == ".zip") {
|
||||
auto *dlg = new ArchiveInstallDialog(emuenv, { drop_path }, this);
|
||||
|
||||
@@ -322,10 +322,25 @@ void PkgInstallDialog::run_install(const fs::path &pkg_path,
|
||||
|
||||
connect(buttons, &QDialogButtonBox::accepted, this,
|
||||
[this, del_pkg, del_bin, pkg_path, title_id]() {
|
||||
if (del_pkg->isChecked())
|
||||
fs::remove(pkg_path);
|
||||
if (del_bin->isChecked() && !m_license_path.empty())
|
||||
fs::remove(m_license_path);
|
||||
QStringList failed;
|
||||
// fs::remove throws and crashes the app when the file is locked so use the non throwing overload
|
||||
if (del_pkg->isChecked()) {
|
||||
boost::system::error_code error;
|
||||
fs::remove(pkg_path, error);
|
||||
if (error)
|
||||
failed.push_back(QString::fromStdString(pkg_path.filename().string()));
|
||||
}
|
||||
if (del_bin->isChecked() && !m_license_path.empty()) {
|
||||
boost::system::error_code error;
|
||||
fs::remove(m_license_path, error);
|
||||
if (error)
|
||||
failed.push_back(QString::fromStdString(m_license_path.filename().string()));
|
||||
}
|
||||
|
||||
if (!failed.isEmpty()) {
|
||||
QMessageBox::warning(this, tr("Could Not Delete Files"),
|
||||
tr("The following files could not be deleted. They may still be open in another program:\n\n%1").arg(failed.join(QStringLiteral("\n"))));
|
||||
}
|
||||
|
||||
Q_EMIT install_complete(title_id);
|
||||
accept();
|
||||
|
||||
Reference in New Issue
Block a user