From 8a9d4936d48b4134c559fa62be5605388a55a036 Mon Sep 17 00:00:00 2001
From: Liam <byteslice@airmail.cc>
Date: Sat, 2 Sep 2023 19:19:36 -0400
Subject: [PATCH] qt: measure romfs dump completion by bytes read

---
 src/yuzu/main.cpp | 76 +++++++++++++++++++++++++++++++----------------
 1 file changed, 50 insertions(+), 26 deletions(-)

diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index f2e6c03f0d..4e435c7e2a 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -2275,40 +2275,62 @@ void GMainWindow::OnTransferableShaderCacheOpenFile(u64 program_id) {
     QDesktopServices::openUrl(QUrl::fromLocalFile(qt_shader_cache_path));
 }
 
-static std::size_t CalculateRomFSEntrySize(const FileSys::VirtualDir& dir, bool full) {
-    std::size_t out = 0;
-
-    for (const auto& subdir : dir->GetSubdirectories()) {
-        out += 1 + CalculateRomFSEntrySize(subdir, full);
-    }
-
-    return out + (full ? dir->GetFiles().size() : 0);
-}
-
-static bool RomFSRawCopy(QProgressDialog& dialog, const FileSys::VirtualDir& src,
-                         const FileSys::VirtualDir& dest, std::size_t block_size, bool full) {
+static bool RomFSRawCopy(size_t total_size, size_t& read_size, QProgressDialog& dialog,
+                         const FileSys::VirtualDir& src, const FileSys::VirtualDir& dest,
+                         bool full) {
     if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable())
         return false;
     if (dialog.wasCanceled())
         return false;
 
+    std::vector<u8> buffer(CopyBufferSize);
+    auto last_timestamp = std::chrono::steady_clock::now();
+
+    const auto QtRawCopy = [&](const FileSys::VirtualFile& src_file,
+                               const FileSys::VirtualFile& dest_file) {
+        if (src_file == nullptr || dest_file == nullptr) {
+            return false;
+        }
+        if (!dest_file->Resize(src_file->GetSize())) {
+            return false;
+        }
+
+        for (std::size_t i = 0; i < src_file->GetSize(); i += buffer.size()) {
+            if (dialog.wasCanceled()) {
+                dest_file->Resize(0);
+                return false;
+            }
+
+            using namespace std::literals::chrono_literals;
+            const auto new_timestamp = std::chrono::steady_clock::now();
+
+            if ((new_timestamp - last_timestamp) > 33ms) {
+                last_timestamp = new_timestamp;
+                dialog.setValue(
+                    static_cast<int>(std::min(read_size, total_size) * 100 / total_size));
+                QCoreApplication::processEvents();
+            }
+
+            const auto read = src_file->Read(buffer.data(), buffer.size(), i);
+            dest_file->Write(buffer.data(), read, i);
+
+            read_size += read;
+        }
+
+        return true;
+    };
+
     if (full) {
         for (const auto& file : src->GetFiles()) {
             const auto out = VfsDirectoryCreateFileWrapper(dest, file->GetName());
-            if (!FileSys::VfsRawCopy(file, out, block_size))
-                return false;
-            dialog.setValue(dialog.value() + 1);
-            if (dialog.wasCanceled())
+            if (!QtRawCopy(file, out))
                 return false;
         }
     }
 
     for (const auto& dir : src->GetSubdirectories()) {
         const auto out = dest->CreateSubdirectory(dir->GetName());
-        if (!RomFSRawCopy(dialog, dir, out, block_size, full))
-            return false;
-        dialog.setValue(dialog.value() + 1);
-        if (dialog.wasCanceled())
+        if (!RomFSRawCopy(total_size, read_size, dialog, dir, out, full))
             return false;
     }
 
@@ -2653,10 +2675,9 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
     }
 
     const auto full = res == selections.constFirst();
-    const auto entry_size = CalculateRomFSEntrySize(extracted, full);
 
-    // The minimum required space is the size of the extracted RomFS + 1 GiB
-    const auto minimum_free_space = extracted->GetSize() + 0x40000000;
+    // The expected required space is the size of the RomFS + 1 GiB
+    const auto minimum_free_space = romfs->GetSize() + 0x40000000;
 
     if (full && Common::FS::GetFreeSpaceSize(path) < minimum_free_space) {
         QMessageBox::warning(this, tr("RomFS Extraction Failed!"),
@@ -2667,12 +2688,15 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
         return;
     }
 
-    QProgressDialog progress(tr("Extracting RomFS..."), tr("Cancel"), 0,
-                             static_cast<s32>(entry_size), this);
+    QProgressDialog progress(tr("Extracting RomFS..."), tr("Cancel"), 0, 100, this);
     progress.setWindowModality(Qt::WindowModal);
     progress.setMinimumDuration(100);
+    progress.setAutoClose(false);
+    progress.setAutoReset(false);
+
+    size_t read_size = 0;
 
-    if (RomFSRawCopy(progress, extracted, out, 0x400000, full)) {
+    if (RomFSRawCopy(romfs->GetSize(), read_size, progress, extracted, out, full)) {
         progress.close();
         QMessageBox::information(this, tr("RomFS Extraction Succeeded!"),
                                  tr("The operation completed successfully."));
-- 
GitLab