diff options
Diffstat (limited to '')
| -rw-r--r-- | src/core/file_sys/patch_manager.cpp | 5 | ||||
| -rw-r--r-- | src/core/file_sys/registered_cache.cpp | 15 | ||||
| -rw-r--r-- | src/core/file_sys/registered_cache.h | 8 | ||||
| -rw-r--r-- | src/core/hle/service/filesystem/fsp_srv.cpp | 6 | ||||
| -rw-r--r-- | src/yuzu/main.cpp | 73 | ||||
| -rw-r--r-- | src/yuzu/main.h | 6 |
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 | ||
| 169 | static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) { | 169 | static 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 | ||
| 34 | bool 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 | |||
| 38 | bool operator!=(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs) { | ||
| 39 | return !operator==(lhs, rhs); | ||
| 40 | } | ||
| 41 | |||
| 33 | static bool FollowsTwoDigitDirFormat(std::string_view name) { | 42 | static 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. |
| 51 | bool operator<(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs); | 51 | bool operator<(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs); |
| 52 | 52 | ||
| 53 | // std unique requires operator== to identify duplicates. | ||
| 54 | bool operator==(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs); | ||
| 55 | bool 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 | */ |
| 66 | class RegisteredCache { | 70 | class 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 | ||
| 103 | constexpr 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 | ||
| 825 | void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_path) { | 827 | void 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 | ||
| 1479 | boost::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 | |||
| 1462 | bool GMainWindow::ConfirmClose() { | 1515 | bool 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; | |||
| 29 | enum class GameListOpenTarget; | 30 | enum class GameListOpenTarget; |
| 30 | 31 | ||
| 31 | namespace FileSys { | 32 | namespace FileSys { |
| 33 | class RegisteredCacheUnion; | ||
| 32 | class VfsFilesystem; | 34 | class VfsFilesystem; |
| 33 | } | 35 | } // namespace FileSys |
| 34 | 36 | ||
| 35 | namespace Tegra { | 37 | namespace Tegra { |
| 36 | class DebugContext; | 38 | class DebugContext; |
| @@ -175,6 +177,8 @@ private slots: | |||
| 175 | void OnReinitializeKeys(ReinitializeKeyBehavior behavior); | 177 | void OnReinitializeKeys(ReinitializeKeyBehavior behavior); |
| 176 | 178 | ||
| 177 | private: | 179 | private: |
| 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; |