summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/core/file_sys/patch_manager.cpp5
-rw-r--r--src/core/file_sys/registered_cache.cpp15
-rw-r--r--src/core/file_sys/registered_cache.h8
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.cpp6
-rw-r--r--src/yuzu/main.cpp73
-rw-r--r--src/yuzu/main.h6
6 files changed, 97 insertions, 16 deletions
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
index 0117cb0bf..1f4928562 100644
--- a/src/core/file_sys/patch_manager.cpp
+++ b/src/core/file_sys/patch_manager.cpp
@@ -168,7 +168,8 @@ bool PatchManager::HasNSOPatch(const std::array<u8, 32>& build_id_) const {
168 168
169static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) { 169static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) {
170 const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id); 170 const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
171 if (type != ContentRecordType::Program || load_dir == nullptr || load_dir->GetSize() <= 0) { 171 if ((type != ContentRecordType::Program && type != ContentRecordType::Data) ||
172 load_dir == nullptr || load_dir->GetSize() <= 0) {
172 return; 173 return;
173 } 174 }
174 175
@@ -218,7 +219,7 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, Content
218 title_id, static_cast<u8>(type)) 219 title_id, static_cast<u8>(type))
219 .c_str(); 220 .c_str();
220 221
221 if (type == ContentRecordType::Program) 222 if (type == ContentRecordType::Program || type == ContentRecordType::Data)
222 LOG_INFO(Loader, log_string); 223 LOG_INFO(Loader, log_string);
223 else 224 else
224 LOG_DEBUG(Loader, log_string); 225 LOG_DEBUG(Loader, log_string);
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp
index 1febb398e..29b100414 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -2,6 +2,7 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm>
5#include <regex> 6#include <regex>
6#include <mbedtls/sha256.h> 7#include <mbedtls/sha256.h>
7#include "common/assert.h" 8#include "common/assert.h"
@@ -30,6 +31,14 @@ bool operator<(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs)
30 return (lhs.title_id < rhs.title_id) || (lhs.title_id == rhs.title_id && lhs.type < rhs.type); 31 return (lhs.title_id < rhs.title_id) || (lhs.title_id == rhs.title_id && lhs.type < rhs.type);
31} 32}
32 33
34bool operator==(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs) {
35 return std::tie(lhs.title_id, lhs.type) == std::tie(rhs.title_id, rhs.type);
36}
37
38bool operator!=(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs) {
39 return !operator==(lhs, rhs);
40}
41
33static bool FollowsTwoDigitDirFormat(std::string_view name) { 42static bool FollowsTwoDigitDirFormat(std::string_view name) {
34 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 |
35 std::regex_constants::icase); 44 std::regex_constants::icase);
@@ -593,6 +602,9 @@ std::vector<RegisteredCacheEntry> RegisteredCacheUnion::ListEntries() const {
593 }, 602 },
594 [](const CNMT& c, const ContentRecord& r) { return true; }); 603 [](const CNMT& c, const ContentRecord& r) { return true; });
595 } 604 }
605
606 std::sort(out.begin(), out.end());
607 out.erase(std::unique(out.begin(), out.end()), out.end());
596 return out; 608 return out;
597} 609}
598 610
@@ -616,6 +628,9 @@ std::vector<RegisteredCacheEntry> RegisteredCacheUnion::ListEntriesFilter(
616 return true; 628 return true;
617 }); 629 });
618 } 630 }
631
632 std::sort(out.begin(), out.end());
633 out.erase(std::unique(out.begin(), out.end()), out.end());
619 return out; 634 return out;
620} 635}
621} // namespace FileSys 636} // namespace FileSys
diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h
index 5ddacba47..5beceffb3 100644
--- a/src/core/file_sys/registered_cache.h
+++ b/src/core/file_sys/registered_cache.h
@@ -50,6 +50,10 @@ constexpr u64 GetUpdateTitleID(u64 base_title_id) {
50// boost flat_map requires operator< for O(log(n)) lookups. 50// boost flat_map requires operator< for O(log(n)) lookups.
51bool operator<(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs); 51bool operator<(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs);
52 52
53// std unique requires operator== to identify duplicates.
54bool operator==(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs);
55bool operator!=(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs);
56
53/* 57/*
54 * A class that catalogues NCAs in the registered directory structure. 58 * A class that catalogues NCAs in the registered directory structure.
55 * Nintendo's registered format follows this structure: 59 * Nintendo's registered format follows this structure:
@@ -60,8 +64,8 @@ bool operator<(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs)
60 * | 00 64 * | 00
61 * | 01 <- Actual content split along 4GB boundaries. (optional) 65 * | 01 <- Actual content split along 4GB boundaries. (optional)
62 * 66 *
63 * (This impl also supports substituting the nca dir for an nca file, as that's more convenient when 67 * (This impl also supports substituting the nca dir for an nca file, as that's more convenient
64 * 4GB splitting can be ignored.) 68 * when 4GB splitting can be ignored.)
65 */ 69 */
66class RegisteredCache { 70class RegisteredCache {
67 friend class RegisteredCacheUnion; 71 friend class RegisteredCacheUnion;
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index d5dced429..c87721c39 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -17,6 +17,7 @@
17#include "core/file_sys/errors.h" 17#include "core/file_sys/errors.h"
18#include "core/file_sys/mode.h" 18#include "core/file_sys/mode.h"
19#include "core/file_sys/nca_metadata.h" 19#include "core/file_sys/nca_metadata.h"
20#include "core/file_sys/patch_manager.h"
20#include "core/file_sys/savedata_factory.h" 21#include "core/file_sys/savedata_factory.h"
21#include "core/file_sys/vfs.h" 22#include "core/file_sys/vfs.h"
22#include "core/hle/ipc_helpers.h" 23#include "core/hle/ipc_helpers.h"
@@ -630,6 +631,7 @@ void FSP_SRV::OpenDataStorageByDataId(Kernel::HLERequestContext& ctx) {
630 static_cast<u8>(storage_id), unknown, title_id); 631 static_cast<u8>(storage_id), unknown, title_id);
631 632
632 auto data = OpenRomFS(title_id, storage_id, FileSys::ContentRecordType::Data); 633 auto data = OpenRomFS(title_id, storage_id, FileSys::ContentRecordType::Data);
634
633 if (data.Failed()) { 635 if (data.Failed()) {
634 // TODO(DarkLordZach): Find the right error code to use here 636 // TODO(DarkLordZach): Find the right error code to use here
635 LOG_ERROR(Service_FS, 637 LOG_ERROR(Service_FS,
@@ -640,7 +642,9 @@ void FSP_SRV::OpenDataStorageByDataId(Kernel::HLERequestContext& ctx) {
640 return; 642 return;
641 } 643 }
642 644
643 IStorage storage(std::move(data.Unwrap())); 645 FileSys::PatchManager pm{title_id};
646
647 IStorage storage(pm.PatchRomFS(std::move(data.Unwrap()), 0, FileSys::ContentRecordType::Data));
644 648
645 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 649 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
646 rb.Push(RESULT_SUCCESS); 650 rb.Push(RESULT_SUCCESS);
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;