diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/core/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/core/file_sys/patch_manager.cpp | 94 | ||||
| -rw-r--r-- | src/core/file_sys/patch_manager.h | 8 |
3 files changed, 104 insertions, 0 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 23fd6e920..a98dbb9ab 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -34,6 +34,8 @@ add_library(core STATIC | |||
| 34 | file_sys/errors.h | 34 | file_sys/errors.h |
| 35 | file_sys/fsmitm_romfsbuild.cpp | 35 | file_sys/fsmitm_romfsbuild.cpp |
| 36 | file_sys/fsmitm_romfsbuild.h | 36 | file_sys/fsmitm_romfsbuild.h |
| 37 | file_sys/ips_layer.cpp | ||
| 38 | file_sys/ips_layer.h | ||
| 37 | file_sys/mode.h | 39 | file_sys/mode.h |
| 38 | file_sys/nca_metadata.cpp | 40 | file_sys/nca_metadata.cpp |
| 39 | file_sys/nca_metadata.h | 41 | file_sys/nca_metadata.h |
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp index 10b4acc92..044e01ae9 100644 --- a/src/core/file_sys/patch_manager.cpp +++ b/src/core/file_sys/patch_manager.cpp | |||
| @@ -6,13 +6,16 @@ | |||
| 6 | #include <array> | 6 | #include <array> |
| 7 | #include <cstddef> | 7 | #include <cstddef> |
| 8 | 8 | ||
| 9 | #include "common/hex_util.h" | ||
| 9 | #include "common/logging/log.h" | 10 | #include "common/logging/log.h" |
| 10 | #include "core/file_sys/content_archive.h" | 11 | #include "core/file_sys/content_archive.h" |
| 11 | #include "core/file_sys/control_metadata.h" | 12 | #include "core/file_sys/control_metadata.h" |
| 13 | #include "core/file_sys/ips_layer.h" | ||
| 12 | #include "core/file_sys/patch_manager.h" | 14 | #include "core/file_sys/patch_manager.h" |
| 13 | #include "core/file_sys/registered_cache.h" | 15 | #include "core/file_sys/registered_cache.h" |
| 14 | #include "core/file_sys/romfs.h" | 16 | #include "core/file_sys/romfs.h" |
| 15 | #include "core/file_sys/vfs_layered.h" | 17 | #include "core/file_sys/vfs_layered.h" |
| 18 | #include "core/file_sys/vfs_vector.h" | ||
| 16 | #include "core/hle/service/filesystem/filesystem.h" | 19 | #include "core/hle/service/filesystem/filesystem.h" |
| 17 | #include "core/loader/loader.h" | 20 | #include "core/loader/loader.h" |
| 18 | 21 | ||
| @@ -21,6 +24,14 @@ namespace FileSys { | |||
| 21 | constexpr u64 SINGLE_BYTE_MODULUS = 0x100; | 24 | constexpr u64 SINGLE_BYTE_MODULUS = 0x100; |
| 22 | constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000; | 25 | constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000; |
| 23 | 26 | ||
| 27 | struct NSOBuildHeader { | ||
| 28 | u32_le magic; | ||
| 29 | INSERT_PADDING_BYTES(0x3C); | ||
| 30 | std::array<u8, 0x20> build_id; | ||
| 31 | INSERT_PADDING_BYTES(0xA0); | ||
| 32 | }; | ||
| 33 | static_assert(sizeof(NSOBuildHeader) == 0x100, "NSOBuildHeader has incorrect size."); | ||
| 34 | |||
| 24 | std::string FormatTitleVersion(u32 version, TitleVersionFormat format) { | 35 | std::string FormatTitleVersion(u32 version, TitleVersionFormat format) { |
| 25 | std::array<u8, sizeof(u32)> bytes{}; | 36 | std::array<u8, sizeof(u32)> bytes{}; |
| 26 | bytes[0] = version % SINGLE_BYTE_MODULUS; | 37 | bytes[0] = version % SINGLE_BYTE_MODULUS; |
| @@ -61,6 +72,89 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const { | |||
| 61 | return exefs; | 72 | return exefs; |
| 62 | } | 73 | } |
| 63 | 74 | ||
| 75 | std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso) const { | ||
| 76 | if (nso.size() < 0x100) | ||
| 77 | return nso; | ||
| 78 | |||
| 79 | NSOBuildHeader header{}; | ||
| 80 | std::memcpy(&header, nso.data(), sizeof(NSOBuildHeader)); | ||
| 81 | |||
| 82 | if (header.magic != Common::MakeMagic('N', 'S', 'O', '0')) | ||
| 83 | return nso; | ||
| 84 | |||
| 85 | const auto build_id_raw = Common::HexArrayToString(header.build_id); | ||
| 86 | const auto build_id = build_id_raw.substr(0, build_id_raw.find_last_not_of('0') + 1); | ||
| 87 | |||
| 88 | LOG_INFO(Loader, "Patching NSO for build_id={}", build_id); | ||
| 89 | |||
| 90 | const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id); | ||
| 91 | auto patch_dirs = load_dir->GetSubdirectories(); | ||
| 92 | std::sort(patch_dirs.begin(), patch_dirs.end(), | ||
| 93 | [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); | ||
| 94 | |||
| 95 | std::vector<VirtualFile> ips; | ||
| 96 | ips.reserve(patch_dirs.size() - 1); | ||
| 97 | for (const auto& subdir : patch_dirs) { | ||
| 98 | auto exefs_dir = subdir->GetSubdirectory("exefs"); | ||
| 99 | if (exefs_dir != nullptr) { | ||
| 100 | for (const auto& file : exefs_dir->GetFiles()) { | ||
| 101 | if (file->GetExtension() != "ips") | ||
| 102 | continue; | ||
| 103 | auto name = file->GetName(); | ||
| 104 | const auto p1 = name.substr(0, name.find_first_of('.')); | ||
| 105 | const auto this_build_id = p1.substr(0, p1.find_last_not_of('0') + 1); | ||
| 106 | |||
| 107 | if (build_id == this_build_id) | ||
| 108 | ips.push_back(file); | ||
| 109 | } | ||
| 110 | } | ||
| 111 | } | ||
| 112 | |||
| 113 | auto out = nso; | ||
| 114 | for (const auto& ips_file : ips) { | ||
| 115 | LOG_INFO(Loader, " - Appling IPS patch from mod \"{}\"", | ||
| 116 | ips_file->GetContainingDirectory()->GetParentDirectory()->GetName()); | ||
| 117 | const auto patched = PatchIPS(std::make_shared<VectorVfsFile>(out), ips_file); | ||
| 118 | if (patched != nullptr) | ||
| 119 | out = patched->ReadAllBytes(); | ||
| 120 | } | ||
| 121 | |||
| 122 | if (out.size() < 0x100) | ||
| 123 | return nso; | ||
| 124 | std::memcpy(out.data(), &header, sizeof(NSOBuildHeader)); | ||
| 125 | return out; | ||
| 126 | } | ||
| 127 | |||
| 128 | bool PatchManager::HasNSOPatch(const std::array<u8, 32>& build_id_) const { | ||
| 129 | const auto build_id_raw = Common::HexArrayToString(build_id_); | ||
| 130 | const auto build_id = build_id_raw.substr(0, build_id_raw.find_last_not_of('0') + 1); | ||
| 131 | |||
| 132 | LOG_INFO(Loader, "Querying NSO patch existence for build_id={}", build_id); | ||
| 133 | |||
| 134 | const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id); | ||
| 135 | auto patch_dirs = load_dir->GetSubdirectories(); | ||
| 136 | std::sort(patch_dirs.begin(), patch_dirs.end(), | ||
| 137 | [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); | ||
| 138 | |||
| 139 | for (const auto& subdir : patch_dirs) { | ||
| 140 | auto exefs_dir = subdir->GetSubdirectory("exefs"); | ||
| 141 | if (exefs_dir != nullptr) { | ||
| 142 | for (const auto& file : exefs_dir->GetFiles()) { | ||
| 143 | if (file->GetExtension() != "ips") | ||
| 144 | continue; | ||
| 145 | auto name = file->GetName(); | ||
| 146 | const auto p1 = name.substr(0, name.find_first_of('.')); | ||
| 147 | const auto this_build_id = p1.substr(0, p1.find_last_not_of('0') + 1); | ||
| 148 | |||
| 149 | if (build_id == this_build_id) | ||
| 150 | return true; | ||
| 151 | } | ||
| 152 | } | ||
| 153 | } | ||
| 154 | |||
| 155 | return false; | ||
| 156 | } | ||
| 157 | |||
| 64 | static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) { | 158 | static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) { |
| 65 | const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id); | 159 | const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id); |
| 66 | if (type != ContentRecordType::Program || load_dir == nullptr || load_dir->GetSize() <= 0) { | 160 | if (type != ContentRecordType::Program || load_dir == nullptr || load_dir->GetSize() <= 0) { |
diff --git a/src/core/file_sys/patch_manager.h b/src/core/file_sys/patch_manager.h index 7807515f9..254f7bfc9 100644 --- a/src/core/file_sys/patch_manager.h +++ b/src/core/file_sys/patch_manager.h | |||
| @@ -34,6 +34,14 @@ public: | |||
| 34 | // - Game Updates | 34 | // - Game Updates |
| 35 | VirtualDir PatchExeFS(VirtualDir exefs) const; | 35 | VirtualDir PatchExeFS(VirtualDir exefs) const; |
| 36 | 36 | ||
| 37 | // Currently tracked NSO patches: | ||
| 38 | // - IPS | ||
| 39 | std::vector<u8> PatchNSO(const std::vector<u8>& nso) const; | ||
| 40 | |||
| 41 | // Checks to see if PatchNSO() will have any effect given the NSO's build ID. | ||
| 42 | // Used to prevent expensive copies in NSO loader. | ||
| 43 | bool HasNSOPatch(const std::array<u8, 0x20>& build_id) const; | ||
| 44 | |||
| 37 | // Currently tracked RomFS patches: | 45 | // Currently tracked RomFS patches: |
| 38 | // - Game Updates | 46 | // - Game Updates |
| 39 | // - LayeredFS | 47 | // - LayeredFS |