summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/core/file_sys/registered_cache.cpp2
-rw-r--r--src/core/file_sys/registered_cache.h2
-rw-r--r--src/yuzu/game_list.cpp9
-rw-r--r--src/yuzu/game_list.h7
-rw-r--r--src/yuzu/main.cpp145
-rw-r--r--src/yuzu/main.h2
6 files changed, 162 insertions, 5 deletions
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp
index dad7ae10b..0dda0b861 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -480,7 +480,7 @@ InstallResult RegisteredCache::RawInstallNCA(std::shared_ptr<NCA> nca, const Vfs
480 auto out = dir->CreateFileRelative(path); 480 auto out = dir->CreateFileRelative(path);
481 if (out == nullptr) 481 if (out == nullptr)
482 return InstallResult::ErrorCopyFailed; 482 return InstallResult::ErrorCopyFailed;
483 return copy(in, out) ? InstallResult::Success : InstallResult::ErrorCopyFailed; 483 return copy(in, out, 0x400000) ? InstallResult::Success : InstallResult::ErrorCopyFailed;
484} 484}
485 485
486bool RegisteredCache::RawInstallYuzuMeta(const CNMT& cnmt) { 486bool RegisteredCache::RawInstallYuzuMeta(const CNMT& cnmt) {
diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h
index f487b0cf0..c0cd59fc5 100644
--- a/src/core/file_sys/registered_cache.h
+++ b/src/core/file_sys/registered_cache.h
@@ -27,7 +27,7 @@ struct ContentRecord;
27 27
28using NcaID = std::array<u8, 0x10>; 28using NcaID = std::array<u8, 0x10>;
29using RegisteredCacheParsingFunction = std::function<VirtualFile(const VirtualFile&, const NcaID&)>; 29using RegisteredCacheParsingFunction = std::function<VirtualFile(const VirtualFile&, const NcaID&)>;
30using VfsCopyFunction = std::function<bool(VirtualFile, VirtualFile)>; 30using VfsCopyFunction = std::function<bool(const VirtualFile&, const VirtualFile&, size_t)>;
31 31
32enum class InstallResult { 32enum class InstallResult {
33 Success, 33 Success,
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index e8b2f720a..991ae10cd 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -318,9 +318,14 @@ void GameList::PopupContextMenu(const QPoint& menu_location) {
318 int row = item_model->itemFromIndex(item)->row(); 318 int row = item_model->itemFromIndex(item)->row();
319 QStandardItem* child_file = item_model->invisibleRootItem()->child(row, COLUMN_NAME); 319 QStandardItem* child_file = item_model->invisibleRootItem()->child(row, COLUMN_NAME);
320 u64 program_id = child_file->data(GameListItemPath::ProgramIdRole).toULongLong(); 320 u64 program_id = child_file->data(GameListItemPath::ProgramIdRole).toULongLong();
321 std::string path = child_file->data(GameListItemPath::FullPathRole).toString().toStdString();
321 322
322 QMenu context_menu; 323 QMenu context_menu;
323 QAction* open_save_location = context_menu.addAction(tr("Open Save Data Location")); 324 QAction* open_save_location = context_menu.addAction(tr("Open Save Data Location"));
325 QAction* open_lfs_location = context_menu.addAction(tr("Open Mod Data Location"));
326 context_menu.addSeparator();
327 QAction* dump_romfs = context_menu.addAction(tr("Dump RomFS"));
328 QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard"));
324 QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry")); 329 QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry"));
325 330
326 open_save_location->setEnabled(program_id != 0); 331 open_save_location->setEnabled(program_id != 0);
@@ -329,6 +334,10 @@ void GameList::PopupContextMenu(const QPoint& menu_location) {
329 334
330 connect(open_save_location, &QAction::triggered, 335 connect(open_save_location, &QAction::triggered,
331 [&]() { emit OpenFolderRequested(program_id, GameListOpenTarget::SaveData); }); 336 [&]() { emit OpenFolderRequested(program_id, GameListOpenTarget::SaveData); });
337 connect(open_lfs_location, &QAction::triggered,
338 [&]() { emit OpenFolderRequested(program_id, GameListOpenTarget::ModData); });
339 connect(dump_romfs, &QAction::triggered, [&]() { emit DumpRomFSRequested(program_id, path); });
340 connect(copy_tid, &QAction::triggered, [&]() { emit CopyTIDRequested(program_id); });
332 connect(navigate_to_gamedb_entry, &QAction::triggered, 341 connect(navigate_to_gamedb_entry, &QAction::triggered,
333 [&]() { emit NavigateToGamedbEntryRequested(program_id, compatibility_list); }); 342 [&]() { emit NavigateToGamedbEntryRequested(program_id, compatibility_list); });
334 343
diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h
index 2713e7b54..3bf51870e 100644
--- a/src/yuzu/game_list.h
+++ b/src/yuzu/game_list.h
@@ -28,7 +28,10 @@ namespace FileSys {
28class VfsFilesystem; 28class VfsFilesystem;
29} 29}
30 30
31enum class GameListOpenTarget { SaveData }; 31enum class GameListOpenTarget {
32 SaveData,
33 ModData,
34};
32 35
33class GameList : public QWidget { 36class GameList : public QWidget {
34 Q_OBJECT 37 Q_OBJECT
@@ -89,6 +92,8 @@ signals:
89 void GameChosen(QString game_path); 92 void GameChosen(QString game_path);
90 void ShouldCancelWorker(); 93 void ShouldCancelWorker();
91 void OpenFolderRequested(u64 program_id, GameListOpenTarget target); 94 void OpenFolderRequested(u64 program_id, GameListOpenTarget target);
95 void DumpRomFSRequested(u64 program_id, const std::string& game_path);
96 void CopyTIDRequested(u64 program_id);
92 void NavigateToGamedbEntryRequested(u64 program_id, 97 void NavigateToGamedbEntryRequested(u64 program_id,
93 const CompatibilityList& compatibility_list); 98 const CompatibilityList& compatibility_list);
94 99
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 45bb1d1d1..7cfe8a32f 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -7,6 +7,22 @@
7#include <memory> 7#include <memory>
8#include <thread> 8#include <thread>
9 9
10// VFS includes must be before glad as they will conflict with Windows file api, which uses defines.
11#include "core/file_sys/vfs.h"
12#include "core/file_sys/vfs_real.h"
13
14// These are wrappers to avoid the calls to CreateDirectory and CreateFile becuase of the Windows
15// defines.
16static FileSys::VirtualDir VfsFilesystemCreateDirectoryWrapper(
17 const FileSys::VirtualFilesystem& vfs, const std::string& path, FileSys::Mode mode) {
18 return vfs->CreateDirectory(path, mode);
19}
20
21static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::VirtualDir& dir,
22 const std::string& path) {
23 return dir->CreateFile(path);
24}
25
10#include <fmt/ostream.h> 26#include <fmt/ostream.h>
11#include <glad/glad.h> 27#include <glad/glad.h>
12 28
@@ -30,16 +46,18 @@
30#include "common/telemetry.h" 46#include "common/telemetry.h"
31#include "core/core.h" 47#include "core/core.h"
32#include "core/crypto/key_manager.h" 48#include "core/crypto/key_manager.h"
49#include "core/file_sys/bis_factory.h"
33#include "core/file_sys/card_image.h" 50#include "core/file_sys/card_image.h"
34#include "core/file_sys/content_archive.h" 51#include "core/file_sys/content_archive.h"
35#include "core/file_sys/control_metadata.h" 52#include "core/file_sys/control_metadata.h"
36#include "core/file_sys/patch_manager.h" 53#include "core/file_sys/patch_manager.h"
37#include "core/file_sys/registered_cache.h" 54#include "core/file_sys/registered_cache.h"
55#include "core/file_sys/romfs.h"
38#include "core/file_sys/savedata_factory.h" 56#include "core/file_sys/savedata_factory.h"
39#include "core/file_sys/submission_package.h" 57#include "core/file_sys/submission_package.h"
40#include "core/file_sys/vfs_real.h"
41#include "core/hle/kernel/process.h" 58#include "core/hle/kernel/process.h"
42#include "core/hle/service/filesystem/filesystem.h" 59#include "core/hle/service/filesystem/filesystem.h"
60#include "core/hle/service/filesystem/fsp_ldr.h"
43#include "core/loader/loader.h" 61#include "core/loader/loader.h"
44#include "core/perf_stats.h" 62#include "core/perf_stats.h"
45#include "core/settings.h" 63#include "core/settings.h"
@@ -362,6 +380,8 @@ void GMainWindow::RestoreUIState() {
362void GMainWindow::ConnectWidgetEvents() { 380void GMainWindow::ConnectWidgetEvents() {
363 connect(game_list, &GameList::GameChosen, this, &GMainWindow::OnGameListLoadFile); 381 connect(game_list, &GameList::GameChosen, this, &GMainWindow::OnGameListLoadFile);
364 connect(game_list, &GameList::OpenFolderRequested, this, &GMainWindow::OnGameListOpenFolder); 382 connect(game_list, &GameList::OpenFolderRequested, this, &GMainWindow::OnGameListOpenFolder);
383 connect(game_list, &GameList::DumpRomFSRequested, this, &GMainWindow::OnGameListDumpRomFS);
384 connect(game_list, &GameList::CopyTIDRequested, this, &GMainWindow::OnGameListCopyTID);
365 connect(game_list, &GameList::NavigateToGamedbEntryRequested, this, 385 connect(game_list, &GameList::NavigateToGamedbEntryRequested, this,
366 &GMainWindow::OnGameListNavigateToGamedbEntry); 386 &GMainWindow::OnGameListNavigateToGamedbEntry);
367 387
@@ -713,6 +733,12 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target
713 program_id, user_id, 0); 733 program_id, user_id, 0);
714 break; 734 break;
715 } 735 }
736 case GameListOpenTarget::ModData: {
737 open_target = "Mod Data";
738 const auto load_dir = FileUtil::GetUserPath(FileUtil::UserPath::LoadDir);
739 path = fmt::format("{}{:016X}", load_dir, program_id);
740 break;
741 }
716 default: 742 default:
717 UNIMPLEMENTED(); 743 UNIMPLEMENTED();
718 } 744 }
@@ -730,6 +756,120 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target
730 QDesktopServices::openUrl(QUrl::fromLocalFile(qpath)); 756 QDesktopServices::openUrl(QUrl::fromLocalFile(qpath));
731} 757}
732 758
759void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_path) {
760 const auto path = fmt::format("{}{:016X}/romfs",
761 FileUtil::GetUserPath(FileUtil::UserPath::DumpDir), program_id);
762
763 auto failed = [this, &path]() {
764 QMessageBox::warning(this, tr("RomFS Extraction Failed!"),
765 tr("There was an error copying the RomFS files or the user "
766 "cancelled the operation."));
767 vfs->DeleteDirectory(path);
768 };
769
770 const auto loader = Loader::GetLoader(vfs->OpenFile(game_path, FileSys::Mode::Read));
771 if (loader == nullptr) {
772 failed();
773 return;
774 }
775
776 FileSys::VirtualFile file;
777 if (loader->ReadRomFS(file) != Loader::ResultStatus::Success) {
778 failed();
779 return;
780 }
781
782 const auto romfs =
783 loader->IsRomFSUpdatable()
784 ? FileSys::PatchManager(program_id).PatchRomFS(file, loader->ReadRomFSIVFCOffset())
785 : file;
786
787 const auto extracted = FileSys::ExtractRomFS(romfs, false);
788 if (extracted == nullptr) {
789 failed();
790 return;
791 }
792
793 const auto out = VfsFilesystemCreateDirectoryWrapper(vfs, path, FileSys::Mode::ReadWrite);
794
795 if (out == nullptr) {
796 failed();
797 return;
798 }
799
800 bool ok;
801 const auto res = QInputDialog::getItem(
802 this, tr("Select RomFS Dump Mode"),
803 tr("Please select the how you would like the RomFS dumped.<br>Full will copy all of the "
804 "files into the new directory while <br>skeleton will only create the directory "
805 "structure."),
806 {"Full", "Skeleton"}, 0, false, &ok);
807 if (!ok)
808 failed();
809
810 const auto full = res == "Full";
811
812 const static std::function<size_t(const FileSys::VirtualDir&, bool)> calculate_entry_size =
813 [](const FileSys::VirtualDir& dir, bool full) {
814 size_t out = 0;
815 for (const auto& subdir : dir->GetSubdirectories())
816 out += 1 + calculate_entry_size(subdir, full);
817 return out + full ? dir->GetFiles().size() : 0;
818 };
819 const auto entry_size = calculate_entry_size(extracted, full);
820
821 QProgressDialog progress(tr("Extracting RomFS..."), tr("Cancel"), 0, entry_size, this);
822 progress.setWindowModality(Qt::WindowModal);
823 progress.setMinimumDuration(100);
824
825 const static std::function<bool(QProgressDialog&, const FileSys::VirtualDir&,
826 const FileSys::VirtualDir&, size_t, bool)>
827 qt_raw_copy = [](QProgressDialog& dialog, const FileSys::VirtualDir& src,
828 const FileSys::VirtualDir& dest, size_t block_size, bool full) {
829 if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable())
830 return false;
831 if (dialog.wasCanceled())
832 return false;
833
834 if (full) {
835 for (const auto& file : src->GetFiles()) {
836 const auto out = VfsDirectoryCreateFileWrapper(dest, file->GetName());
837 if (!FileSys::VfsRawCopy(file, out, block_size))
838 return false;
839 dialog.setValue(dialog.value() + 1);
840 if (dialog.wasCanceled())
841 return false;
842 }
843 }
844
845 for (const auto& dir : src->GetSubdirectories()) {
846 const auto out = dest->CreateSubdirectory(dir->GetName());
847 if (!qt_raw_copy(dialog, dir, out, block_size, full))
848 return false;
849 dialog.setValue(dialog.value() + 1);
850 if (dialog.wasCanceled())
851 return false;
852 }
853
854 return true;
855 };
856
857 if (qt_raw_copy(progress, extracted, out, 0x400000, full)) {
858 progress.close();
859 QMessageBox::information(this, tr("RomFS Extraction Succeeded!"),
860 tr("The operation completed successfully."));
861 QDesktopServices::openUrl(QUrl::fromLocalFile(QString::fromStdString(path)));
862 } else {
863 progress.close();
864 failed();
865 }
866}
867
868void GMainWindow::OnGameListCopyTID(u64 program_id) {
869 QClipboard* clipboard = QGuiApplication::clipboard();
870 clipboard->setText(QString::fromStdString(fmt::format("{:016X}", program_id)));
871}
872
733void GMainWindow::OnGameListNavigateToGamedbEntry(u64 program_id, 873void GMainWindow::OnGameListNavigateToGamedbEntry(u64 program_id,
734 const CompatibilityList& compatibility_list) { 874 const CompatibilityList& compatibility_list) {
735 const auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id); 875 const auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id);
@@ -790,7 +930,8 @@ void GMainWindow::OnMenuInstallToNAND() {
790 return; 930 return;
791 } 931 }
792 932
793 const auto qt_raw_copy = [this](FileSys::VirtualFile src, FileSys::VirtualFile dest) { 933 const auto qt_raw_copy = [this](const FileSys::VirtualFile& src,
934 const FileSys::VirtualFile& dest, size_t block_size) {
794 if (src == nullptr || dest == nullptr) 935 if (src == nullptr || dest == nullptr)
795 return false; 936 return false;
796 if (!dest->Resize(src->GetSize())) 937 if (!dest->Resize(src->GetSize()))
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 552e3e61c..8ee9242b1 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -138,6 +138,8 @@ private slots:
138 /// Called whenever a user selects a game in the game list widget. 138 /// Called whenever a user selects a game in the game list widget.
139 void OnGameListLoadFile(QString game_path); 139 void OnGameListLoadFile(QString game_path);
140 void OnGameListOpenFolder(u64 program_id, GameListOpenTarget target); 140 void OnGameListOpenFolder(u64 program_id, GameListOpenTarget target);
141 void OnGameListDumpRomFS(u64 program_id, const std::string& game_path);
142 void OnGameListCopyTID(u64 program_id);
141 void OnGameListNavigateToGamedbEntry(u64 program_id, 143 void OnGameListNavigateToGamedbEntry(u64 program_id,
142 const CompatibilityList& compatibility_list); 144 const CompatibilityList& compatibility_list);
143 void OnMenuLoadFile(); 145 void OnMenuLoadFile();