From 6d8d7ebc66d9deb5adb4edb246590481b1236bbc Mon Sep 17 00:00:00 2001
From: Morph <39850852+Morph1984@users.noreply.github.com>
Date: Sun, 5 Jul 2020 09:29:39 -0400
Subject: [PATCH] Update the install and progress dialogs

- Remove the overwrite files checkbox, it will always overwrite
- The progressbar now reflects the progress in terms of data transferred.
---
 src/yuzu/install_dialog.cpp |  12 ++--
 src/yuzu/install_dialog.h   |   2 +-
 src/yuzu/main.cpp           | 118 ++++++++++++++++--------------------
 src/yuzu/main.h             |  13 ++--
 4 files changed, 65 insertions(+), 80 deletions(-)

diff --git a/src/yuzu/install_dialog.cpp b/src/yuzu/install_dialog.cpp
index 5f3b4c963..06b0b1874 100644
--- a/src/yuzu/install_dialog.cpp
+++ b/src/yuzu/install_dialog.cpp
@@ -22,7 +22,7 @@ InstallDialog::InstallDialog(QWidget* parent, const QStringList& files) : QDialo
         item->setCheckState(Qt::Checked);
     }
 
-    file_list->setMinimumWidth((file_list->sizeHintForColumn(0) * 10) / 9);
+    file_list->setMinimumWidth((file_list->sizeHintForColumn(0) * 11) / 10);
 
     vbox_layout = new QVBoxLayout;
 
@@ -30,8 +30,8 @@ InstallDialog::InstallDialog(QWidget* parent, const QStringList& files) : QDialo
 
     description = new QLabel(tr("Please confirm these are the files you wish to install."));
 
-    overwrite_files = new QCheckBox(tr("Overwrite Existing Files"));
-    overwrite_files->setCheckState(Qt::Unchecked);
+    update_description =
+        new QLabel(tr("Installing an Update or DLC will overwrite the previously installed one."));
 
     buttons = new QDialogButtonBox;
     buttons->addButton(QDialogButtonBox::Cancel);
@@ -40,10 +40,10 @@ InstallDialog::InstallDialog(QWidget* parent, const QStringList& files) : QDialo
     connect(buttons, &QDialogButtonBox::accepted, this, &InstallDialog::accept);
     connect(buttons, &QDialogButtonBox::rejected, this, &InstallDialog::reject);
 
-    hbox_layout->addWidget(overwrite_files);
     hbox_layout->addWidget(buttons);
 
     vbox_layout->addWidget(description);
+    vbox_layout->addWidget(update_description);
     vbox_layout->addWidget(file_list);
     vbox_layout->addLayout(hbox_layout);
 
@@ -67,10 +67,6 @@ QStringList InstallDialog::GetFiles() const {
     return files;
 }
 
-bool InstallDialog::ShouldOverwriteFiles() const {
-    return overwrite_files->isChecked();
-}
-
 int InstallDialog::GetMinimumWidth() const {
     return file_list->width();
 }
diff --git a/src/yuzu/install_dialog.h b/src/yuzu/install_dialog.h
index 55a458ba8..e4aba1b06 100644
--- a/src/yuzu/install_dialog.h
+++ b/src/yuzu/install_dialog.h
@@ -31,6 +31,6 @@ private:
     QHBoxLayout* hbox_layout;
 
     QLabel* description;
-    QCheckBox* overwrite_files;
+    QLabel* update_description;
     QDialogButtonBox* buttons;
 };
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 4539cbe0d..8a57e34c7 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -848,6 +848,9 @@ void GMainWindow::ConnectWidgetEvents() {
     connect(game_list, &GameList::OpenPerGameGeneralRequested, this,
             &GMainWindow::OnGameListOpenPerGameProperties);
 
+    connect(this, &GMainWindow::UpdateInstallProgress, this,
+            &GMainWindow::IncrementInstallProgress);
+
     connect(this, &GMainWindow::EmulationStarting, render_window,
             &GRenderWindow::OnEmulationStarting);
     connect(this, &GMainWindow::EmulationStopping, render_window,
@@ -1594,6 +1597,10 @@ void GMainWindow::OnMenuLoadFolder() {
     }
 }
 
+void GMainWindow::IncrementInstallProgress() {
+    install_progress->setValue(install_progress->value() + 1);
+}
+
 void GMainWindow::OnMenuInstallToNAND() {
     const QString file_filter =
         tr("Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive "
@@ -1613,28 +1620,35 @@ void GMainWindow::OnMenuInstallToNAND() {
     }
 
     const QStringList files = installDialog.GetFiles();
-    const bool overwrite_files = installDialog.ShouldOverwriteFiles();
 
-    int count = 0;
-    const int total_count = filenames.size();
+    int remaining = filenames.size();
+
+    // This would only overflow above 2^43 bytes (8.796 TB)
+    int total_size = 0;
+    for (const QString& file : files) {
+        total_size += static_cast<int>(QFile(file).size() / 0x1000);
+    }
+    if (total_size < 0) {
+        LOG_CRITICAL(Frontend, "Attempting to install too many files, aborting.");
+        return;
+    }
 
     QStringList new_files{};         // Newly installed files that do not yet exist in the NAND
     QStringList overwritten_files{}; // Files that overwrote those existing in the NAND
-    QStringList existing_files{}; // Files that were not installed as they already exist in the NAND
-    QStringList failed_files{};   // Files that failed to install due to errors
+    QStringList failed_files{};      // Files that failed to install due to errors
 
     ui.action_Install_File_NAND->setEnabled(false);
 
-    QProgressDialog install_progress(QStringLiteral(""), tr("Cancel"), 0, total_count, this);
-    install_progress.setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint &
-                                    ~Qt::WindowMaximizeButtonHint);
-    install_progress.setAutoClose(false);
-    install_progress.setFixedWidth(installDialog.GetMinimumWidth());
-    install_progress.show();
+    install_progress = new QProgressDialog(QStringLiteral(""), tr("Cancel"), 0, total_size, this);
+    install_progress->setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint &
+                                     ~Qt::WindowMaximizeButtonHint);
+    install_progress->setAttribute(Qt::WA_DeleteOnClose, true);
+    install_progress->setFixedWidth(installDialog.GetMinimumWidth() + 40);
+    install_progress->show();
 
     for (const QString& file : files) {
-        install_progress.setWindowTitle(tr("%n file(s) remaining", "", total_count - count));
-        install_progress.setLabelText(
+        install_progress->setWindowTitle(tr("%n file(s) remaining", "", remaining));
+        install_progress->setLabelText(
             tr("Installing file \"%1\"...").arg(QFileInfo(file).fileName()));
 
         QFuture<InstallResult> future;
@@ -1642,19 +1656,21 @@ void GMainWindow::OnMenuInstallToNAND() {
 
         if (file.endsWith(QStringLiteral("xci"), Qt::CaseInsensitive) ||
             file.endsWith(QStringLiteral("nsp"), Qt::CaseInsensitive)) {
-            future = QtConcurrent::run([this, &file, &overwrite_files, &install_progress] {
-                return InstallNSPXCI(file, overwrite_files, install_progress);
-            });
+
+            future = QtConcurrent::run([this, &file] { return InstallNSPXCI(file); });
 
             while (!future.isFinished()) {
                 QCoreApplication::processEvents();
             }
 
             result = future.result();
+
         } else {
-            result = InstallNCA(file, overwrite_files, install_progress);
+            result = InstallNCA(file);
         }
 
+        std::this_thread::sleep_for(std::chrono::milliseconds(10));
+
         switch (result) {
         case InstallResult::Success:
             new_files.append(QFileInfo(file).fileName());
@@ -1662,19 +1678,15 @@ void GMainWindow::OnMenuInstallToNAND() {
         case InstallResult::Overwrite:
             overwritten_files.append(QFileInfo(file).fileName());
             break;
-        case InstallResult::AlreadyExists:
-            existing_files.append(QFileInfo(file).fileName());
-            break;
         case InstallResult::Failure:
             failed_files.append(QFileInfo(file).fileName());
             break;
         }
 
-        install_progress.setValue(++count);
-        std::this_thread::sleep_for(std::chrono::milliseconds(10));
+        --remaining;
     }
 
-    install_progress.close();
+    install_progress->close();
 
     const QString install_results =
         (new_files.isEmpty() ? QStringLiteral("")
@@ -1682,9 +1694,6 @@ void GMainWindow::OnMenuInstallToNAND() {
         (overwritten_files.isEmpty()
              ? QStringLiteral("")
              : tr("%n file(s) were overwritten\n", "", overwritten_files.size())) +
-        (existing_files.isEmpty()
-             ? QStringLiteral("")
-             : tr("%n file(s) already exist in NAND\n", "", existing_files.size())) +
         (failed_files.isEmpty() ? QStringLiteral("")
                                 : tr("%n file(s) failed to install\n", "", failed_files.size()));
 
@@ -1695,11 +1704,9 @@ void GMainWindow::OnMenuInstallToNAND() {
     ui.action_Install_File_NAND->setEnabled(true);
 }
 
-InstallResult GMainWindow::InstallNSPXCI(const QString& filename, bool overwrite_files,
-                                         QProgressDialog& install_progress) {
-    const auto qt_raw_copy = [this, &install_progress](const FileSys::VirtualFile& src,
-                                                       const FileSys::VirtualFile& dest,
-                                                       std::size_t block_size) {
+InstallResult GMainWindow::InstallNSPXCI(const QString& filename) {
+    const auto qt_raw_copy = [this](const FileSys::VirtualFile& src,
+                                    const FileSys::VirtualFile& dest, std::size_t block_size) {
         if (src == nullptr || dest == nullptr) {
             return false;
         }
@@ -1710,11 +1717,13 @@ InstallResult GMainWindow::InstallNSPXCI(const QString& filename, bool overwrite
         std::array<u8, 0x1000> buffer{};
 
         for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) {
-            if (install_progress.wasCanceled()) {
+            if (install_progress->wasCanceled()) {
                 dest->Resize(0);
                 return false;
             }
 
+            emit UpdateInstallProgress();
+
             const auto read = src->Read(buffer.data(), buffer.size(), i);
             dest->Write(buffer.data(), read, i);
         }
@@ -1739,32 +1748,19 @@ InstallResult GMainWindow::InstallNSPXCI(const QString& filename, bool overwrite
     }
     const auto res =
         Core::System::GetInstance().GetFileSystemController().GetUserNANDContents()->InstallEntry(
-            *nsp, false, qt_raw_copy);
+            *nsp, true, qt_raw_copy);
     if (res == FileSys::InstallResult::Success) {
         return InstallResult::Success;
     } else if (res == FileSys::InstallResult::ErrorAlreadyExists) {
-        if (overwrite_files) {
-            const auto res2 = Core::System::GetInstance()
-                                  .GetFileSystemController()
-                                  .GetUserNANDContents()
-                                  ->InstallEntry(*nsp, true, qt_raw_copy);
-            if (res2 != FileSys::InstallResult::Success) {
-                return InstallResult::Failure;
-            }
-            return InstallResult::Overwrite;
-        } else {
-            return InstallResult::AlreadyExists;
-        }
+        return InstallResult::Overwrite;
     } else {
         return InstallResult::Failure;
     }
 }
 
-InstallResult GMainWindow::InstallNCA(const QString& filename, bool overwrite_files,
-                                      QProgressDialog& install_progress) {
-    const auto qt_raw_copy = [this, &install_progress](const FileSys::VirtualFile& src,
-                                                       const FileSys::VirtualFile& dest,
-                                                       std::size_t block_size) {
+InstallResult GMainWindow::InstallNCA(const QString& filename) {
+    const auto qt_raw_copy = [this](const FileSys::VirtualFile& src,
+                                    const FileSys::VirtualFile& dest, std::size_t block_size) {
         if (src == nullptr || dest == nullptr) {
             return false;
         }
@@ -1775,11 +1771,13 @@ InstallResult GMainWindow::InstallNCA(const QString& filename, bool overwrite_fi
         std::array<u8, 0x1000> buffer{};
 
         for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) {
-            if (install_progress.wasCanceled()) {
+            if (install_progress->wasCanceled()) {
                 dest->Resize(0);
                 return false;
             }
 
+            emit UpdateInstallProgress();
+
             const auto read = src->Read(buffer.data(), buffer.size(), i);
             dest->Write(buffer.data(), read, i);
         }
@@ -1830,30 +1828,18 @@ InstallResult GMainWindow::InstallNCA(const QString& filename, bool overwrite_fi
         res = Core::System::GetInstance()
                   .GetFileSystemController()
                   .GetUserNANDContents()
-                  ->InstallEntry(*nca, static_cast<FileSys::TitleType>(index), false, qt_raw_copy);
+                  ->InstallEntry(*nca, static_cast<FileSys::TitleType>(index), true, qt_raw_copy);
     } else {
         res = Core::System::GetInstance()
                   .GetFileSystemController()
                   .GetSystemNANDContents()
-                  ->InstallEntry(*nca, static_cast<FileSys::TitleType>(index), false, qt_raw_copy);
+                  ->InstallEntry(*nca, static_cast<FileSys::TitleType>(index), true, qt_raw_copy);
     }
 
     if (res == FileSys::InstallResult::Success) {
         return InstallResult::Success;
     } else if (res == FileSys::InstallResult::ErrorAlreadyExists) {
-        if (overwrite_files) {
-            const auto res2 =
-                Core::System::GetInstance()
-                    .GetFileSystemController()
-                    .GetUserNANDContents()
-                    ->InstallEntry(*nca, static_cast<FileSys::TitleType>(index), true, qt_raw_copy);
-            if (res2 != FileSys::InstallResult::Success) {
-                return InstallResult::Failure;
-            }
-            return InstallResult::Overwrite;
-        } else {
-            return InstallResult::AlreadyExists;
-        }
+        return InstallResult::Overwrite;
     } else {
         return InstallResult::Failure;
     }
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index deea8170d..adff65fb5 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -51,7 +51,6 @@ enum class EmulatedDirectoryTarget {
 enum class InstallResult {
     Success,
     Overwrite,
-    AlreadyExists,
     Failure,
 };
 
@@ -110,6 +109,8 @@ signals:
     // Signal that tells widgets to update icons to use the current theme
     void UpdateThemedIcons();
 
+    void UpdateInstallProgress();
+
     void ErrorDisplayFinished();
 
     void ProfileSelectorFinishedSelection(std::optional<Common::UUID> uuid);
@@ -206,6 +207,7 @@ private slots:
     void OnGameListOpenPerGameProperties(const std::string& file);
     void OnMenuLoadFile();
     void OnMenuLoadFolder();
+    void IncrementInstallProgress();
     void OnMenuInstallToNAND();
     void OnMenuRecentFile();
     void OnConfigure();
@@ -226,10 +228,8 @@ private slots:
 
 private:
     std::optional<u64> SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id);
-    InstallResult InstallNSPXCI(const QString& filename, bool overwrite_files,
-                                QProgressDialog& install_progress);
-    InstallResult InstallNCA(const QString& filename, bool overwrite_files,
-                             QProgressDialog& install_progress);
+    InstallResult InstallNSPXCI(const QString& filename);
+    InstallResult InstallNCA(const QString& filename);
     void UpdateWindowTitle(const std::string& title_name = {},
                            const std::string& title_version = {});
     void UpdateStatusBar();
@@ -284,6 +284,9 @@ private:
 
     HotkeyRegistry hotkey_registry;
 
+    // Install progress dialog
+    QProgressDialog* install_progress;
+
 protected:
     void dropEvent(QDropEvent* event) override;
     void dragEnterEvent(QDragEnterEvent* event) override;