summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Zach Hilman2018-10-17 18:27:23 -0400
committerGravatar Zach Hilman2018-10-17 18:27:29 -0400
commit9d0fb0f81506429fa83923b04878a5ee2e8ff420 (patch)
tree3bdf4e57a7fa3aeb188838351ffe4f7f6326c047
parentregistered_cache: Deduplicate results of ListEntry and ListEntryFilter (diff)
downloadyuzu-9d0fb0f81506429fa83923b04878a5ee2e8ff420.tar.gz
yuzu-9d0fb0f81506429fa83923b04878a5ee2e8ff420.tar.xz
yuzu-9d0fb0f81506429fa83923b04878a5ee2e8ff420.zip
qt: Add support for dumping a DLC Data RomFS
Diffstat (limited to '')
-rw-r--r--src/core/file_sys/registered_cache.cpp4
-rw-r--r--src/core/file_sys/registered_cache.h1
-rw-r--r--src/yuzu/main.cpp73
-rw-r--r--src/yuzu/main.h6
4 files changed, 73 insertions, 11 deletions
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp
index d1dea5e82..29b100414 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -35,6 +35,10 @@ bool operator==(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs
35 return std::tie(lhs.title_id, lhs.type) == std::tie(rhs.title_id, rhs.type); 35 return std::tie(lhs.title_id, lhs.type) == std::tie(rhs.title_id, rhs.type);
36} 36}
37 37
38bool operator!=(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs) {
39 return !operator==(lhs, rhs);
40}
41
38static bool FollowsTwoDigitDirFormat(std::string_view name) { 42static bool FollowsTwoDigitDirFormat(std::string_view name) {
39 static const std::regex two_digit_regex("000000[0-9A-F]{2}", std::regex_constants::ECMAScript | 43 static const std::regex two_digit_regex("000000[0-9A-F]{2}", std::regex_constants::ECMAScript |
40 std::regex_constants::icase); 44 std::regex_constants::icase);
diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h
index aeb1c69ba..5beceffb3 100644
--- a/src/core/file_sys/registered_cache.h
+++ b/src/core/file_sys/registered_cache.h
@@ -52,6 +52,7 @@ bool operator<(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs)
52 52
53// std unique requires operator== to identify duplicates. 53// std unique requires operator== to identify duplicates.
54bool operator==(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs); 54bool operator==(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs);
55bool operator!=(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs);
55 56
56/* 57/*
57 * A class that catalogues NCAs in the registered directory structure. 58 * A class that catalogues NCAs in the registered directory structure.
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index bef9df00d..36c702195 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -100,6 +100,8 @@ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
100} 100}
101#endif 101#endif
102 102
103constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000;
104
103/** 105/**
104 * "Callouts" are one-time instructional messages shown to the user. In the config settings, there 106 * "Callouts" are one-time instructional messages shown to the user. In the config settings, there
105 * is a bitfield "callout_flags" options, used to track if a message has already been shown to the 107 * is a bitfield "callout_flags" options, used to track if a message has already been shown to the
@@ -823,14 +825,10 @@ static bool RomFSRawCopy(QProgressDialog& dialog, const FileSys::VirtualDir& src
823} 825}
824 826
825void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_path) { 827void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_path) {
826 const auto path = fmt::format("{}{:016X}/romfs", 828 const auto failed = [this] {
827 FileUtil::GetUserPath(FileUtil::UserPath::DumpDir), program_id);
828
829 const auto failed = [this, &path] {
830 QMessageBox::warning(this, tr("RomFS Extraction Failed!"), 829 QMessageBox::warning(this, tr("RomFS Extraction Failed!"),
831 tr("There was an error copying the RomFS files or the user " 830 tr("There was an error copying the RomFS files or the user "
832 "cancelled the operation.")); 831 "cancelled the operation."));
833 vfs->DeleteDirectory(path);
834 }; 832 };
835 833
836 const auto loader = Loader::GetLoader(vfs->OpenFile(game_path, FileSys::Mode::Read)); 834 const auto loader = Loader::GetLoader(vfs->OpenFile(game_path, FileSys::Mode::Read));
@@ -845,10 +843,24 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
845 return; 843 return;
846 } 844 }
847 845
848 const auto romfs = 846 const auto installed = Service::FileSystem::GetUnionContents();
849 loader->IsRomFSUpdatable() 847 auto romfs_title_id = SelectRomFSDumpTarget(*installed, program_id);
850 ? FileSys::PatchManager(program_id).PatchRomFS(file, loader->ReadRomFSIVFCOffset()) 848
851 : file; 849 if (!romfs_title_id) {
850 failed();
851 return;
852 }
853
854 const auto path = fmt::format(
855 "{}{:016X}/romfs", FileUtil::GetUserPath(FileUtil::UserPath::DumpDir), *romfs_title_id);
856
857 FileSys::VirtualFile romfs;
858
859 if (*romfs_title_id == program_id) {
860 romfs = file;
861 } else {
862 romfs = installed->GetEntry(*romfs_title_id, FileSys::ContentRecordType::Data)->GetRomFS();
863 }
852 864
853 const auto extracted = FileSys::ExtractRomFS(romfs, FileSys::RomFSExtractionType::Full); 865 const auto extracted = FileSys::ExtractRomFS(romfs, FileSys::RomFSExtractionType::Full);
854 if (extracted == nullptr) { 866 if (extracted == nullptr) {
@@ -860,6 +872,7 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
860 872
861 if (out == nullptr) { 873 if (out == nullptr) {
862 failed(); 874 failed();
875 vfs->DeleteDirectory(path);
863 return; 876 return;
864 } 877 }
865 878
@@ -870,8 +883,11 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
870 "files into the new directory while <br>skeleton will only create the directory " 883 "files into the new directory while <br>skeleton will only create the directory "
871 "structure."), 884 "structure."),
872 {"Full", "Skeleton"}, 0, false, &ok); 885 {"Full", "Skeleton"}, 0, false, &ok);
873 if (!ok) 886 if (!ok) {
874 failed(); 887 failed();
888 vfs->DeleteDirectory(path);
889 return;
890 }
875 891
876 const auto full = res == "Full"; 892 const auto full = res == "Full";
877 const auto entry_size = CalculateRomFSEntrySize(extracted, full); 893 const auto entry_size = CalculateRomFSEntrySize(extracted, full);
@@ -888,6 +904,7 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
888 } else { 904 } else {
889 progress.close(); 905 progress.close();
890 failed(); 906 failed();
907 vfs->DeleteDirectory(path);
891 } 908 }
892} 909}
893 910
@@ -1459,6 +1476,42 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
1459 } 1476 }
1460} 1477}
1461 1478
1479boost::optional<u64> GMainWindow::SelectRomFSDumpTarget(
1480 const FileSys::RegisteredCacheUnion& installed, u64 program_id) {
1481 const auto dlc_entries =
1482 installed.ListEntriesFilter(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data);
1483 std::vector<FileSys::RegisteredCacheEntry> dlc_match;
1484 dlc_match.reserve(dlc_entries.size());
1485 std::copy_if(dlc_entries.begin(), dlc_entries.end(), std::back_inserter(dlc_match),
1486 [&program_id, &installed](const FileSys::RegisteredCacheEntry& entry) {
1487 return (entry.title_id & DLC_BASE_TITLE_ID_MASK) == program_id &&
1488 installed.GetEntry(entry)->GetStatus() == Loader::ResultStatus::Success;
1489 });
1490
1491 std::vector<u64> romfs_tids;
1492 romfs_tids.push_back(program_id);
1493 for (const auto& entry : dlc_match)
1494 romfs_tids.push_back(entry.title_id);
1495
1496 if (romfs_tids.size() > 1) {
1497 QStringList list{"Base"};
1498 for (std::size_t i = 1; i < romfs_tids.size(); ++i)
1499 list.push_back(QStringLiteral("DLC %1").arg(romfs_tids[i] & 0x7FF));
1500
1501 bool ok;
1502 const auto res = QInputDialog::getItem(
1503 this, tr("Select RomFS Dump Target"),
1504 tr("Please select which RomFS you would like to dump."), list, 0, false, &ok);
1505 if (!ok) {
1506 return boost::none;
1507 }
1508
1509 return romfs_tids[list.indexOf(res)];
1510 }
1511
1512 return program_id;
1513}
1514
1462bool GMainWindow::ConfirmClose() { 1515bool GMainWindow::ConfirmClose() {
1463 if (emu_thread == nullptr || !UISettings::values.confirm_before_closing) 1516 if (emu_thread == nullptr || !UISettings::values.confirm_before_closing)
1464 return true; 1517 return true;
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 3663d6aed..c8cbc0ba8 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -10,6 +10,7 @@
10#include <QMainWindow> 10#include <QMainWindow>
11#include <QTimer> 11#include <QTimer>
12 12
13#include <boost/optional.hpp>
13#include "common/common_types.h" 14#include "common/common_types.h"
14#include "core/core.h" 15#include "core/core.h"
15#include "ui_main.h" 16#include "ui_main.h"
@@ -29,8 +30,9 @@ class WaitTreeWidget;
29enum class GameListOpenTarget; 30enum class GameListOpenTarget;
30 31
31namespace FileSys { 32namespace FileSys {
33class RegisteredCacheUnion;
32class VfsFilesystem; 34class VfsFilesystem;
33} 35} // namespace FileSys
34 36
35namespace Tegra { 37namespace Tegra {
36class DebugContext; 38class DebugContext;
@@ -175,6 +177,8 @@ private slots:
175 void OnReinitializeKeys(ReinitializeKeyBehavior behavior); 177 void OnReinitializeKeys(ReinitializeKeyBehavior behavior);
176 178
177private: 179private:
180 boost::optional<u64> SelectRomFSDumpTarget(const FileSys::RegisteredCacheUnion&,
181 u64 program_id);
178 void UpdateStatusBar(); 182 void UpdateStatusBar();
179 183
180 Ui::MainWindow ui; 184 Ui::MainWindow ui;