diff --git a/src/common/settings.h b/src/common/settings.h
index 829759c94b2493d5b3f88a6d59712cdb7b4acdcf..98c3958c00521c5710f72c0c4606c41410badcd7 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -1,8 +1,6 @@
 // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
 // SPDX-License-Identifier: GPL-2.0-or-later
 
-// Modified by palfaiate on <2024/03/07>
-
 #pragma once
 
 #include <algorithm>
diff --git a/src/suyu/game_list.cpp b/src/suyu/game_list.cpp
index bda0d489bf041b4c2d63e270546fc87162bf9925..132ff30b0c2efe13a5e3f24898bac33d87c4ebd4 100644
--- a/src/suyu/game_list.cpp
+++ b/src/suyu/game_list.cpp
@@ -2,6 +2,7 @@
 // SPDX-License-Identifier: GPL-2.0-or-later
 
 // Modified by palfaiate on <2024/03/07>
+// Reverted palfaiate's changes on <2024/03/25> -Nine-Ball
 
 #include <QApplication>
 #include <QDir>
@@ -580,6 +581,9 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri
     remove_menu->addSeparator();
     QAction* remove_shader_cache = remove_menu->addAction(tr("Remove All Pipeline Caches"));
     QAction* remove_all_content = remove_menu->addAction(tr("Remove All Installed Contents"));
+    QMenu* dump_romfs_menu = context_menu.addMenu(tr("Dump RomFS"));
+    QAction* dump_romfs = dump_romfs_menu->addAction(tr("Dump RomFS"));
+    QAction* dump_romfs_sdmc = dump_romfs_menu->addAction(tr("Dump RomFS to SDMC"));
     QAction* verify_integrity = context_menu.addAction(tr("Verify Integrity"));
     QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard"));
     QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry"));
@@ -647,6 +651,12 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri
     connect(remove_cache_storage, &QAction::triggered, [this, program_id, path] {
         emit RemoveFileRequested(program_id, GameListRemoveTarget::CacheStorage, path);
     });
+    connect(dump_romfs, &QAction::triggered, [this, program_id, path]() {
+        emit DumpRomFSRequested(program_id, path, DumpRomFSTarget::Normal);
+    });
+    connect(dump_romfs_sdmc, &QAction::triggered, [this, program_id, path]() {
+        emit DumpRomFSRequested(program_id, path, DumpRomFSTarget::SDMC);
+    });
     connect(verify_integrity, &QAction::triggered,
             [this, path]() { emit VerifyIntegrityRequested(path); });
     connect(copy_tid, &QAction::triggered,
diff --git a/src/suyu/game_list.h b/src/suyu/game_list.h
index 7568e1b6e03d51e85ef9c02d34903765474b23f5..4adb6403ef135b629bf31631618d40726a3b6f37 100644
--- a/src/suyu/game_list.h
+++ b/src/suyu/game_list.h
@@ -2,6 +2,7 @@
 // SPDX-License-Identifier: GPL-2.0-or-later
 
 // Modified by palfaiate on <2024/03/07>
+// Reverted palfaiate's changes on <2024/03/25> -Nine-Ball
 
 #pragma once
 
@@ -52,6 +53,11 @@ enum class GameListRemoveTarget {
     CacheStorage,
 };
 
+enum class DumpRomFSTarget {
+    Normal,
+    SDMC,
+};
+
 enum class GameListShortcutTarget {
     Desktop,
     Applications,
@@ -113,6 +119,7 @@ signals:
     void RemoveFileRequested(u64 program_id, GameListRemoveTarget target,
                              const std::string& game_path);
     void RemovePlayTimeRequested(u64 program_id);
+    void DumpRomFSRequested(u64 program_id, const std::string& game_path, DumpRomFSTarget target);
     void VerifyIntegrityRequested(const std::string& game_path);
     void CopyTIDRequested(u64 program_id);
     void CreateShortcut(u64 program_id, const std::string& game_path,
diff --git a/src/suyu/main.cpp b/src/suyu/main.cpp
index a7a61f5cb358cfc340f3c206f4eb649164c6a617..baa98f2849b33d3ca0b8ae2c2ca4a9dd3e41cf35 100644
--- a/src/suyu/main.cpp
+++ b/src/suyu/main.cpp
@@ -1,8 +1,6 @@
 // SPDX-FileCopyrightText: 2014 Citra Emulator Project
 // SPDX-License-Identifier: GPL-2.0-or-later
 
-// Modified by palfaiate on <2024/03/07>
-
 #include <cinttypes>
 #include <clocale>
 #include <cmath>
@@ -55,6 +53,18 @@
 #include "suyu/multiplayer/state.h"
 #include "suyu/util/controller_navigation.h"
 
+// These are wrappers to avoid the calls to CreateDirectory and CreateFile because of the Windows
+static FileSys::VirtualDir VfsFilesystemCreateDirectoryWrapper(
+    const FileSys::VirtualFilesystem& vfs, const std::string& path, FileSys::OpenMode mode) {
+    return vfs->CreateDirectory(path, mode);
+}
+
+// Overloaded function, also removed by palafiate
+static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::VirtualDir& dir,
+                                                          const std::string& path) {
+    return dir->CreateFile(path);
+}
+
 #include <fmt/ostream.h>
 #include <glad/glad.h>
 
@@ -1465,6 +1475,7 @@ void GMainWindow::ConnectWidgetEvents() {
     connect(game_list, &GameList::RemoveFileRequested, this, &GMainWindow::OnGameListRemoveFile);
     connect(game_list, &GameList::RemovePlayTimeRequested, this,
             &GMainWindow::OnGameListRemovePlayTimeData);
+    connect(game_list, &GameList::DumpRomFSRequested, this, &GMainWindow::OnGameListDumpRomFS);
     connect(game_list, &GameList::VerifyIntegrityRequested, this,
             &GMainWindow::OnGameListVerifyIntegrity);
     connect(game_list, &GameList::CopyTIDRequested, this, &GMainWindow::OnGameListCopyTID);
@@ -2369,6 +2380,68 @@ void GMainWindow::OnTransferableShaderCacheOpenFile(u64 program_id) {
     QDesktopServices::openUrl(QUrl::fromLocalFile(qt_shader_cache_path));
 }
 
+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 (!QtRawCopy(file, out))
+                return false;
+        }
+    }
+
+    for (const auto& dir : src->GetSubdirectories()) {
+        const auto out = dest->CreateSubdirectory(dir->GetName());
+        if (!RomFSRawCopy(total_size, read_size, dialog, dir, out, full))
+            return false;
+    }
+
+    return true;
+}
+
 QString GMainWindow::GetGameListErrorRemoving(InstalledEntryType type) const {
     switch (type) {
     case InstalledEntryType::Game:
@@ -2610,6 +2683,121 @@ void GMainWindow::RemoveCacheStorage(u64 program_id) {
     Common::FS::RemoveDirRecursively(path);
 }
 
+void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_path,
+                                      DumpRomFSTarget target) {
+    const auto failed = [this] {
+        QMessageBox::warning(this, tr("RomFS Extraction Failed!"),
+                             tr("There was an error copying the RomFS files or the user "
+                                "cancelled the operation."));
+    };
+
+    const auto loader =
+        Loader::GetLoader(*system, vfs->OpenFile(game_path, FileSys::OpenMode::Read));
+    if (loader == nullptr) {
+        failed();
+        return;
+    }
+
+    FileSys::VirtualFile packed_update_raw{};
+    loader->ReadUpdateRaw(packed_update_raw);
+
+    const auto& installed = system->GetContentProvider();
+
+    u64 title_id{};
+    u8 raw_type{};
+    if (!SelectRomFSDumpTarget(installed, program_id, &title_id, &raw_type)) {
+        failed();
+        return;
+    }
+
+    const auto type = static_cast<FileSys::ContentRecordType>(raw_type);
+    const auto base_nca = installed.GetEntry(title_id, type);
+    if (!base_nca) {
+        failed();
+        return;
+    }
+
+    const FileSys::NCA update_nca{packed_update_raw, nullptr};
+    if (type != FileSys::ContentRecordType::Program ||
+        update_nca.GetStatus() != Loader::ResultStatus::ErrorMissingBKTRBaseRomFS ||
+        update_nca.GetTitleId() != FileSys::GetUpdateTitleID(title_id)) {
+        packed_update_raw = {};
+    }
+
+    const auto base_romfs = base_nca->GetRomFS();
+    const auto dump_dir =
+        target == DumpRomFSTarget::Normal
+            ? Common::FS::GetSuyuPath(Common::FS::SuyuPath::DumpDir)
+            : Common::FS::GetSuyuPath(Common::FS::SuyuPath::SDMCDir) / "atmosphere" / "contents";
+    const auto romfs_dir = fmt::format("{:016X}/romfs", title_id);
+
+    const auto path = Common::FS::PathToUTF8String(dump_dir / romfs_dir);
+
+    const FileSys::PatchManager pm{title_id, system->GetFileSystemController(), installed};
+    auto romfs = pm.PatchRomFS(base_nca.get(), base_romfs, type, packed_update_raw, false);
+
+    const auto out = VfsFilesystemCreateDirectoryWrapper(vfs, path, FileSys::OpenMode::ReadWrite);
+
+    if (out == nullptr) {
+        failed();
+        vfs->DeleteDirectory(path);
+        return;
+    }
+
+    bool ok = false;
+    const QStringList selections{tr("Full"), tr("Skeleton")};
+    const auto res = QInputDialog::getItem(
+        this, tr("Select RomFS Dump Mode"),
+        tr("Please select the how you would like the RomFS dumped.<br>Full will copy all of the "
+           "files into the new directory while <br>skeleton will only create the directory "
+           "structure."),
+        selections, 0, false, &ok);
+    if (!ok) {
+        failed();
+        vfs->DeleteDirectory(path);
+        return;
+    }
+
+    const auto extracted = FileSys::ExtractRomFS(romfs);
+    if (extracted == nullptr) {
+        failed();
+        return;
+    }
+
+    const auto full = res == selections.constFirst();
+
+    // 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!"),
+                             tr("There is not enough free space at %1 to extract the RomFS. Please "
+                                "free up space or select a different dump directory at "
+                                "Emulation > Configure > System > Filesystem > Dump Root")
+                                 .arg(QString::fromStdString(path)));
+        return;
+    }
+
+    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(romfs->GetSize(), read_size, progress, extracted, out, full)) {
+        progress.close();
+        QMessageBox::information(this, tr("RomFS Extraction Succeeded!"),
+                                 tr("The operation completed successfully."));
+        QDesktopServices::openUrl(QUrl::fromLocalFile(QString::fromStdString(path)));
+    } else {
+        progress.close();
+        failed();
+        vfs->DeleteDirectory(path);
+    }
+}
+
 void GMainWindow::OnGameListVerifyIntegrity(const std::string& game_path) {
     const auto NotImplemented = [this] {
         QMessageBox::warning(this, tr("Integrity verification couldn't be performed!"),
@@ -4680,6 +4868,66 @@ void GMainWindow::SetFirmwareVersion() {
     firmware_label->setToolTip(QString::fromStdString(display_title));
 }
 
+bool GMainWindow::SelectRomFSDumpTarget(const FileSys::ContentProvider& installed, u64 program_id,
+                                        u64* selected_title_id, u8* selected_content_record_type) {
+    using ContentInfo = std::tuple<u64, FileSys::TitleType, FileSys::ContentRecordType>;
+    boost::container::flat_set<ContentInfo> available_title_ids;
+
+    const auto RetrieveEntries = [&](FileSys::TitleType title_type,
+                                     FileSys::ContentRecordType record_type) {
+        const auto entries = installed.ListEntriesFilter(title_type, record_type);
+        for (const auto& entry : entries) {
+            if (FileSys::GetBaseTitleID(entry.title_id) == program_id &&
+                installed.GetEntry(entry)->GetStatus() == Loader::ResultStatus::Success) {
+                available_title_ids.insert({entry.title_id, title_type, record_type});
+            }
+        }
+    };
+
+    RetrieveEntries(FileSys::TitleType::Application, FileSys::ContentRecordType::Program);
+    RetrieveEntries(FileSys::TitleType::Application, FileSys::ContentRecordType::HtmlDocument);
+    RetrieveEntries(FileSys::TitleType::Application, FileSys::ContentRecordType::LegalInformation);
+    RetrieveEntries(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data);
+
+    if (available_title_ids.empty()) {
+        return false;
+    }
+
+    size_t title_index = 0;
+
+    if (available_title_ids.size() > 1) {
+        QStringList list;
+        for (auto& [title_id, title_type, record_type] : available_title_ids) {
+            const auto hex_title_id = QString::fromStdString(fmt::format("{:X}", title_id));
+            if (record_type == FileSys::ContentRecordType::Program) {
+                list.push_back(QStringLiteral("Program [%1]").arg(hex_title_id));
+            } else if (record_type == FileSys::ContentRecordType::HtmlDocument) {
+                list.push_back(QStringLiteral("HTML document [%1]").arg(hex_title_id));
+            } else if (record_type == FileSys::ContentRecordType::LegalInformation) {
+                list.push_back(QStringLiteral("Legal information [%1]").arg(hex_title_id));
+            } else {
+                list.push_back(
+                    QStringLiteral("DLC %1 [%2]").arg(title_id & 0x7FF).arg(hex_title_id));
+            }
+        }
+
+        bool ok;
+        const auto res = QInputDialog::getItem(
+            this, tr("Select RomFS Dump Target"),
+            tr("Please select which RomFS you would like to dump."), list, 0, false, &ok);
+        if (!ok) {
+            return false;
+        }
+
+        title_index = list.indexOf(res);
+    }
+
+    const auto& [title_id, title_type, record_type] = *available_title_ids.nth(title_index);
+    *selected_title_id = title_id;
+    *selected_content_record_type = static_cast<u8>(record_type);
+    return true;
+}
+
 bool GMainWindow::ConfirmClose() {
     if (emu_thread == nullptr ||
         UISettings::values.confirm_before_stopping.GetValue() == ConfirmStop::Ask_Never) {
diff --git a/src/suyu/main.h b/src/suyu/main.h
index e20950e238a0a3182a3319021980540d4faec8f5..b2b0f9afc4b7c8a9dcf5d5872d0d11cf2c7a70d8 100644
--- a/src/suyu/main.h
+++ b/src/suyu/main.h
@@ -51,6 +51,7 @@ class WaitTreeWidget;
 enum class GameListOpenTarget;
 enum class GameListRemoveTarget;
 enum class GameListShortcutTarget;
+enum class DumpRomFSTarget;
 enum class InstalledEntryType;
 class GameListPlaceholder;
 
@@ -347,6 +348,7 @@ private slots:
     void OnGameListRemoveFile(u64 program_id, GameListRemoveTarget target,
                               const std::string& game_path);
     void OnGameListRemovePlayTimeData(u64 program_id);
+    void OnGameListDumpRomFS(u64 program_id, const std::string& game_path, DumpRomFSTarget target);
     void OnGameListVerifyIntegrity(const std::string& game_path);
     void OnGameListCopyTID(u64 program_id);
     void OnGameListNavigateToGamedbEntry(u64 program_id,