From b87bc5d35157cc70c0284cc521d6d4a9ec4f636b Mon Sep 17 00:00:00 2001
From: zhupengfei <zhupf321@gmail.com>
Date: Sun, 9 Feb 2020 21:01:56 +0800
Subject: [PATCH] citra_qt: Add 'Dump RomFS' menu action

A progress dialog will be displayed. However no progress is reported and the user also cannot cancel it.
---
 src/citra_qt/game_list.cpp |  4 ++++
 src/citra_qt/game_list.h   |  1 +
 src/citra_qt/main.cpp      | 41 ++++++++++++++++++++++++++++++++++++++
 src/citra_qt/main.h        |  1 +
 4 files changed, 47 insertions(+)

diff --git a/src/citra_qt/game_list.cpp b/src/citra_qt/game_list.cpp
index 4ba7e99d6..8a5df5172 100644
--- a/src/citra_qt/game_list.cpp
+++ b/src/citra_qt/game_list.cpp
@@ -469,6 +469,7 @@ void GameList::AddGamePopup(QMenu& context_menu, const QString& path, u64 progra
     QAction* open_texture_load_location =
         context_menu.addAction(tr("Open Custom Texture Location"));
     QAction* open_mods_location = context_menu.addAction(tr("Open Mods Location"));
+    QAction* dump_romfs = context_menu.addAction(tr("Dump RomFS"));
     QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry"));
 
     const bool is_application =
@@ -499,6 +500,7 @@ void GameList::AddGamePopup(QMenu& context_menu, const QString& path, u64 progra
     open_texture_dump_location->setVisible(is_application);
     open_texture_load_location->setVisible(is_application);
     open_mods_location->setVisible(is_application);
+    dump_romfs->setVisible(is_application);
 
     navigate_to_gamedb_entry->setVisible(it != compatibility_list.end());
 
@@ -535,6 +537,8 @@ void GameList::AddGamePopup(QMenu& context_menu, const QString& path, u64 progra
             emit OpenFolderRequested(program_id, GameListOpenTarget::MODS);
         }
     });
+    connect(dump_romfs, &QAction::triggered,
+            [this, path, program_id] { emit DumpRomFSRequested(path, program_id); });
     connect(navigate_to_gamedb_entry, &QAction::triggered, [this, program_id]() {
         emit NavigateToGamedbEntryRequested(program_id, compatibility_list);
     });
diff --git a/src/citra_qt/game_list.h b/src/citra_qt/game_list.h
index 635fbb39b..334089037 100644
--- a/src/citra_qt/game_list.h
+++ b/src/citra_qt/game_list.h
@@ -82,6 +82,7 @@ signals:
     void OpenFolderRequested(u64 program_id, GameListOpenTarget target);
     void NavigateToGamedbEntryRequested(u64 program_id,
                                         const CompatibilityList& compatibility_list);
+    void DumpRomFSRequested(QString game_path, u64 program_id);
     void OpenDirectory(const QString& directory);
     void AddDirectory();
     void ShowList(bool show);
diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp
index cbb29e6d7..8534007f6 100644
--- a/src/citra_qt/main.cpp
+++ b/src/citra_qt/main.cpp
@@ -568,6 +568,7 @@ void GMainWindow::ConnectWidgetEvents() {
     connect(game_list, &GameList::OpenFolderRequested, this, &GMainWindow::OnGameListOpenFolder);
     connect(game_list, &GameList::NavigateToGamedbEntryRequested, this,
             &GMainWindow::OnGameListNavigateToGamedbEntry);
+    connect(game_list, &GameList::DumpRomFSRequested, this, &GMainWindow::OnGameListDumpRomFS);
     connect(game_list, &GameList::AddDirectory, this, &GMainWindow::OnGameListAddDirectory);
     connect(game_list_placeholder, &GameListPlaceholder::AddDirectory, this,
             &GMainWindow::OnGameListAddDirectory);
@@ -1177,6 +1178,46 @@ void GMainWindow::OnGameListNavigateToGamedbEntry(u64 program_id,
     QDesktopServices::openUrl(QUrl("https://citra-emu.org/game/" + directory));
 }
 
+void GMainWindow::OnGameListDumpRomFS(QString game_path, u64 program_id) {
+    auto* dialog = new QProgressDialog(tr("Dumping..."), tr("Cancel"), 0, 0, this);
+    dialog->setWindowModality(Qt::WindowModal);
+    dialog->setWindowFlags(dialog->windowFlags() &
+                           ~(Qt::WindowCloseButtonHint | Qt::WindowContextHelpButtonHint));
+    dialog->setCancelButton(nullptr);
+    dialog->setMinimumDuration(0);
+    dialog->setValue(0);
+
+    const auto base_path = fmt::format(
+        "{}romfs/{:016X}", FileUtil::GetUserPath(FileUtil::UserPath::DumpDir), program_id);
+    const auto update_path =
+        fmt::format("{}romfs/{:016X}", FileUtil::GetUserPath(FileUtil::UserPath::DumpDir),
+                    program_id | 0x0004000e00000000);
+    using FutureWatcher = QFutureWatcher<std::pair<Loader::ResultStatus, Loader::ResultStatus>>;
+    auto* future_watcher = new FutureWatcher(this);
+    connect(future_watcher, &FutureWatcher::finished,
+            [this, program_id, dialog, base_path, update_path, future_watcher] {
+                dialog->hide();
+                const auto& [base, update] = future_watcher->result();
+                if (base != Loader::ResultStatus::Success) {
+                    QMessageBox::critical(
+                        this, tr("Citra"),
+                        tr("Could not dump base RomFS.\nRefer to the log for details."));
+                    return;
+                }
+                QDesktopServices::openUrl(QUrl::fromLocalFile(QString::fromStdString(base_path)));
+                if (update == Loader::ResultStatus::Success) {
+                    QDesktopServices::openUrl(
+                        QUrl::fromLocalFile(QString::fromStdString(update_path)));
+                }
+            });
+
+    auto future = QtConcurrent::run([game_path, base_path, update_path] {
+        std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(game_path.toStdString());
+        return std::make_pair(loader->DumpRomFS(base_path), loader->DumpUpdateRomFS(update_path));
+    });
+    future_watcher->setFuture(future);
+}
+
 void GMainWindow::OnGameListOpenDirectory(const QString& directory) {
     QString path;
     if (directory == QStringLiteral("INSTALLED")) {
diff --git a/src/citra_qt/main.h b/src/citra_qt/main.h
index 91d7eed1b..75a1bbb3d 100644
--- a/src/citra_qt/main.h
+++ b/src/citra_qt/main.h
@@ -169,6 +169,7 @@ private slots:
     void OnGameListOpenFolder(u64 program_id, GameListOpenTarget target);
     void OnGameListNavigateToGamedbEntry(u64 program_id,
                                          const CompatibilityList& compatibility_list);
+    void OnGameListDumpRomFS(QString game_path, u64 program_id);
     void OnGameListOpenDirectory(const QString& directory);
     void OnGameListAddDirectory();
     void OnGameListShowList(bool show);