| 
									
										
										
										
											2018-08-25 19:03:45 -04:00
										 |  |  | // Copyright 2018 yuzu emulator team
 | 
					
						
							|  |  |  | // Licensed under GPLv2 or any later version
 | 
					
						
							|  |  |  | // Refer to the license.txt file included.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-30 21:07:17 -04:00
										 |  |  | #include <algorithm>
 | 
					
						
							| 
									
										
										
										
											2018-09-06 13:51:57 -04:00
										 |  |  | #include <array>
 | 
					
						
							|  |  |  | #include <cstddef>
 | 
					
						
							| 
									
										
										
										
											2018-10-02 17:03:34 -04:00
										 |  |  | #include <cstring>
 | 
					
						
							| 
									
										
										
										
											2018-09-06 13:51:57 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-29 22:15:16 -04:00
										 |  |  | #include "common/hex_util.h"
 | 
					
						
							| 
									
										
										
										
											2018-09-06 13:51:57 -04:00
										 |  |  | #include "common/logging/log.h"
 | 
					
						
							| 
									
										
										
										
											2018-09-04 17:01:40 -04:00
										 |  |  | #include "core/file_sys/content_archive.h"
 | 
					
						
							| 
									
										
										
										
											2018-09-03 18:57:52 -04:00
										 |  |  | #include "core/file_sys/control_metadata.h"
 | 
					
						
							| 
									
										
										
										
											2018-09-29 22:15:16 -04:00
										 |  |  | #include "core/file_sys/ips_layer.h"
 | 
					
						
							| 
									
										
										
										
											2018-08-25 19:03:45 -04:00
										 |  |  | #include "core/file_sys/patch_manager.h"
 | 
					
						
							|  |  |  | #include "core/file_sys/registered_cache.h"
 | 
					
						
							| 
									
										
										
										
											2018-09-04 17:01:40 -04:00
										 |  |  | #include "core/file_sys/romfs.h"
 | 
					
						
							| 
									
										
										
										
											2018-09-19 22:02:44 -04:00
										 |  |  | #include "core/file_sys/vfs_layered.h"
 | 
					
						
							| 
									
										
										
										
											2018-09-29 22:15:16 -04:00
										 |  |  | #include "core/file_sys/vfs_vector.h"
 | 
					
						
							| 
									
										
										
										
											2018-08-25 19:03:45 -04:00
										 |  |  | #include "core/hle/service/filesystem/filesystem.h"
 | 
					
						
							| 
									
										
										
										
											2018-09-04 17:01:40 -04:00
										 |  |  | #include "core/loader/loader.h"
 | 
					
						
							| 
									
										
										
										
											2018-08-25 19:03:45 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | namespace FileSys { | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-26 10:53:31 -04:00
										 |  |  | constexpr u64 SINGLE_BYTE_MODULUS = 0x100; | 
					
						
							| 
									
										
										
										
											2018-09-26 22:15:05 -04:00
										 |  |  | constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000; | 
					
						
							| 
									
										
										
										
											2018-08-26 10:53:31 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-29 22:15:16 -04:00
										 |  |  | struct NSOBuildHeader { | 
					
						
							|  |  |  |     u32_le magic; | 
					
						
							|  |  |  |     INSERT_PADDING_BYTES(0x3C); | 
					
						
							|  |  |  |     std::array<u8, 0x20> build_id; | 
					
						
							|  |  |  |     INSERT_PADDING_BYTES(0xA0); | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | static_assert(sizeof(NSOBuildHeader) == 0x100, "NSOBuildHeader has incorrect size."); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-26 10:53:31 -04:00
										 |  |  | std::string FormatTitleVersion(u32 version, TitleVersionFormat format) { | 
					
						
							|  |  |  |     std::array<u8, sizeof(u32)> bytes{}; | 
					
						
							|  |  |  |     bytes[0] = version % SINGLE_BYTE_MODULUS; | 
					
						
							| 
									
										
										
										
											2018-09-15 15:21:06 +02:00
										 |  |  |     for (std::size_t i = 1; i < bytes.size(); ++i) { | 
					
						
							| 
									
										
										
										
											2018-08-26 10:53:31 -04:00
										 |  |  |         version /= SINGLE_BYTE_MODULUS; | 
					
						
							|  |  |  |         bytes[i] = version % SINGLE_BYTE_MODULUS; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-08-25 19:03:45 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-26 10:53:31 -04:00
										 |  |  |     if (format == TitleVersionFormat::FourElements) | 
					
						
							|  |  |  |         return fmt::format("v{}.{}.{}.{}", bytes[3], bytes[2], bytes[1], bytes[0]); | 
					
						
							|  |  |  |     return fmt::format("v{}.{}.{}", bytes[3], bytes[2], bytes[1]); | 
					
						
							| 
									
										
										
										
											2018-08-25 19:03:45 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | PatchManager::PatchManager(u64 title_id) : title_id(title_id) {} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-19 19:19:05 -04:00
										 |  |  | PatchManager::~PatchManager() = default; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-25 19:03:45 -04:00
										 |  |  | VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const { | 
					
						
							| 
									
										
										
										
											2018-08-28 22:38:35 -04:00
										 |  |  |     LOG_INFO(Loader, "Patching ExeFS for title_id={:016X}", title_id); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-25 19:03:45 -04:00
										 |  |  |     if (exefs == nullptr) | 
					
						
							|  |  |  |         return exefs; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const auto installed = Service::FileSystem::GetUnionContents(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Game Updates
 | 
					
						
							|  |  |  |     const auto update_tid = GetUpdateTitleID(title_id); | 
					
						
							|  |  |  |     const auto update = installed->GetEntry(update_tid, ContentRecordType::Program); | 
					
						
							|  |  |  |     if (update != nullptr) { | 
					
						
							|  |  |  |         if (update->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS && | 
					
						
							| 
									
										
										
										
											2018-08-26 10:53:31 -04:00
										 |  |  |             update->GetExeFS() != nullptr) { | 
					
						
							| 
									
										
										
										
											2018-08-28 22:38:35 -04:00
										 |  |  |             LOG_INFO(Loader, "    ExeFS: Update ({}) applied successfully", | 
					
						
							|  |  |  |                      FormatTitleVersion(installed->GetEntryVersion(update_tid).get_value_or(0))); | 
					
						
							| 
									
										
										
										
											2018-08-25 19:03:45 -04:00
										 |  |  |             exefs = update->GetExeFS(); | 
					
						
							| 
									
										
										
										
											2018-08-26 10:53:31 -04:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2018-08-25 19:03:45 -04:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return exefs; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-02 17:03:34 -04:00
										 |  |  | static std::vector<VirtualFile> CollectIPSPatches(const std::vector<VirtualDir>& patch_dirs, | 
					
						
							|  |  |  |                                                   const std::string& build_id) { | 
					
						
							|  |  |  |     std::vector<VirtualFile> ips; | 
					
						
							|  |  |  |     ips.reserve(patch_dirs.size()); | 
					
						
							|  |  |  |     for (const auto& subdir : patch_dirs) { | 
					
						
							|  |  |  |         auto exefs_dir = subdir->GetSubdirectory("exefs"); | 
					
						
							|  |  |  |         if (exefs_dir != nullptr) { | 
					
						
							|  |  |  |             for (const auto& file : exefs_dir->GetFiles()) { | 
					
						
							|  |  |  |                 if (file->GetExtension() != "ips") | 
					
						
							|  |  |  |                     continue; | 
					
						
							|  |  |  |                 auto name = file->GetName(); | 
					
						
							|  |  |  |                 const auto p1 = name.substr(0, name.find('.')); | 
					
						
							|  |  |  |                 const auto this_build_id = p1.substr(0, p1.find_last_not_of('0') + 1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 if (build_id == this_build_id) | 
					
						
							|  |  |  |                     ips.push_back(file); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return ips; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-29 22:15:16 -04:00
										 |  |  | std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso) const { | 
					
						
							|  |  |  |     if (nso.size() < 0x100) | 
					
						
							|  |  |  |         return nso; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-02 17:03:34 -04:00
										 |  |  |     NSOBuildHeader header; | 
					
						
							| 
									
										
										
										
											2018-09-29 22:15:16 -04:00
										 |  |  |     std::memcpy(&header, nso.data(), sizeof(NSOBuildHeader)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (header.magic != Common::MakeMagic('N', 'S', 'O', '0')) | 
					
						
							|  |  |  |         return nso; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const auto build_id_raw = Common::HexArrayToString(header.build_id); | 
					
						
							|  |  |  |     const auto build_id = build_id_raw.substr(0, build_id_raw.find_last_not_of('0') + 1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     LOG_INFO(Loader, "Patching NSO for build_id={}", build_id); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id); | 
					
						
							|  |  |  |     auto patch_dirs = load_dir->GetSubdirectories(); | 
					
						
							|  |  |  |     std::sort(patch_dirs.begin(), patch_dirs.end(), | 
					
						
							|  |  |  |               [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); | 
					
						
							| 
									
										
										
										
											2018-10-02 17:03:34 -04:00
										 |  |  |     const auto ips = CollectIPSPatches(patch_dirs, build_id); | 
					
						
							| 
									
										
										
										
											2018-09-29 22:15:16 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     auto out = nso; | 
					
						
							|  |  |  |     for (const auto& ips_file : ips) { | 
					
						
							|  |  |  |         LOG_INFO(Loader, "    - Appling IPS patch from mod \"{}\"", | 
					
						
							|  |  |  |                  ips_file->GetContainingDirectory()->GetParentDirectory()->GetName()); | 
					
						
							|  |  |  |         const auto patched = PatchIPS(std::make_shared<VectorVfsFile>(out), ips_file); | 
					
						
							|  |  |  |         if (patched != nullptr) | 
					
						
							|  |  |  |             out = patched->ReadAllBytes(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (out.size() < 0x100) | 
					
						
							|  |  |  |         return nso; | 
					
						
							|  |  |  |     std::memcpy(out.data(), &header, sizeof(NSOBuildHeader)); | 
					
						
							|  |  |  |     return out; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool PatchManager::HasNSOPatch(const std::array<u8, 32>& build_id_) const { | 
					
						
							|  |  |  |     const auto build_id_raw = Common::HexArrayToString(build_id_); | 
					
						
							|  |  |  |     const auto build_id = build_id_raw.substr(0, build_id_raw.find_last_not_of('0') + 1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     LOG_INFO(Loader, "Querying NSO patch existence for build_id={}", build_id); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id); | 
					
						
							|  |  |  |     auto patch_dirs = load_dir->GetSubdirectories(); | 
					
						
							|  |  |  |     std::sort(patch_dirs.begin(), patch_dirs.end(), | 
					
						
							|  |  |  |               [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-02 17:03:34 -04:00
										 |  |  |     return !CollectIPSPatches(patch_dirs, build_id).empty(); | 
					
						
							| 
									
										
										
										
											2018-09-29 22:15:16 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-23 21:50:16 -04:00
										 |  |  | static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) { | 
					
						
							| 
									
										
										
										
											2018-09-19 22:02:44 -04:00
										 |  |  |     const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id); | 
					
						
							| 
									
										
										
										
											2018-09-25 20:09:20 -04:00
										 |  |  |     if (type != ContentRecordType::Program || load_dir == nullptr || load_dir->GetSize() <= 0) { | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     auto extracted = ExtractRomFS(romfs); | 
					
						
							|  |  |  |     if (extracted == nullptr) { | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     auto patch_dirs = load_dir->GetSubdirectories(); | 
					
						
							|  |  |  |     std::sort(patch_dirs.begin(), patch_dirs.end(), | 
					
						
							|  |  |  |               [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     std::vector<VirtualDir> layers; | 
					
						
							|  |  |  |     layers.reserve(patch_dirs.size() + 1); | 
					
						
							|  |  |  |     for (const auto& subdir : patch_dirs) { | 
					
						
							|  |  |  |         auto romfs_dir = subdir->GetSubdirectory("romfs"); | 
					
						
							|  |  |  |         if (romfs_dir != nullptr) | 
					
						
							|  |  |  |             layers.push_back(std::move(romfs_dir)); | 
					
						
							| 
									
										
										
										
											2018-09-19 22:02:44 -04:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-09-25 20:09:20 -04:00
										 |  |  |     layers.push_back(std::move(extracted)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     auto layered = LayeredVfsDirectory::MakeLayeredDirectory(std::move(layers)); | 
					
						
							|  |  |  |     if (layered == nullptr) { | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     auto packed = CreateRomFS(std::move(layered)); | 
					
						
							|  |  |  |     if (packed == nullptr) { | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     LOG_INFO(Loader, "    RomFS: LayeredFS patches applied successfully"); | 
					
						
							|  |  |  |     romfs = std::move(packed); | 
					
						
							| 
									
										
										
										
											2018-09-23 21:50:16 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-25 09:19:42 -04:00
										 |  |  | VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, ContentRecordType type, | 
					
						
							|  |  |  |                                      VirtualFile update_raw) const { | 
					
						
							| 
									
										
										
										
											2018-09-23 21:50:16 -04:00
										 |  |  |     LOG_INFO(Loader, "Patching RomFS for title_id={:016X}, type={:02X}", title_id, | 
					
						
							|  |  |  |              static_cast<u8>(type)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (romfs == nullptr) | 
					
						
							|  |  |  |         return romfs; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const auto installed = Service::FileSystem::GetUnionContents(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Game Updates
 | 
					
						
							|  |  |  |     const auto update_tid = GetUpdateTitleID(title_id); | 
					
						
							|  |  |  |     const auto update = installed->GetEntryRaw(update_tid, type); | 
					
						
							|  |  |  |     if (update != nullptr) { | 
					
						
							|  |  |  |         const auto new_nca = std::make_shared<NCA>(update, romfs, ivfc_offset); | 
					
						
							|  |  |  |         if (new_nca->GetStatus() == Loader::ResultStatus::Success && | 
					
						
							|  |  |  |             new_nca->GetRomFS() != nullptr) { | 
					
						
							|  |  |  |             LOG_INFO(Loader, "    RomFS: Update ({}) applied successfully", | 
					
						
							|  |  |  |                      FormatTitleVersion(installed->GetEntryVersion(update_tid).get_value_or(0))); | 
					
						
							|  |  |  |             romfs = new_nca->GetRomFS(); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2018-09-25 09:19:42 -04:00
										 |  |  |     } else if (update_raw != nullptr) { | 
					
						
							|  |  |  |         const auto new_nca = std::make_shared<NCA>(update, romfs, ivfc_offset); | 
					
						
							|  |  |  |         if (new_nca->GetStatus() == Loader::ResultStatus::Success && | 
					
						
							|  |  |  |             new_nca->GetRomFS() != nullptr) { | 
					
						
							| 
									
										
										
										
											2018-09-25 14:07:13 -04:00
										 |  |  |             LOG_INFO(Loader, "    RomFS: Update (PACKED) applied successfully"); | 
					
						
							| 
									
										
										
										
											2018-09-25 09:19:42 -04:00
										 |  |  |             romfs = new_nca->GetRomFS(); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2018-09-23 21:50:16 -04:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // LayeredFS
 | 
					
						
							|  |  |  |     ApplyLayeredFS(romfs, title_id, type); | 
					
						
							| 
									
										
										
										
											2018-09-19 22:02:44 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-25 19:03:45 -04:00
										 |  |  |     return romfs; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-02 17:03:34 -04:00
										 |  |  | static void AppendCommaIfNotEmpty(std::string& to, const std::string& with) { | 
					
						
							| 
									
										
										
										
											2018-09-29 22:14:01 -04:00
										 |  |  |     if (to.empty()) | 
					
						
							|  |  |  |         to += with; | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |         to += ", " + with; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static bool IsDirValidAndNonEmpty(const VirtualDir& dir) { | 
					
						
							|  |  |  |     return dir != nullptr && (!dir->GetFiles().empty() || !dir->GetSubdirectories().empty()); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-25 09:19:42 -04:00
										 |  |  | std::map<PatchType, std::string> PatchManager::GetPatchVersionNames(VirtualFile update_raw) const { | 
					
						
							| 
									
										
										
										
											2018-10-02 17:03:34 -04:00
										 |  |  |     std::map<std::string, std::string, std::less<>> out; | 
					
						
							| 
									
										
										
										
											2018-08-25 19:03:45 -04:00
										 |  |  |     const auto installed = Service::FileSystem::GetUnionContents(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-27 08:59:50 -04:00
										 |  |  |     // Game Updates
 | 
					
						
							| 
									
										
										
										
											2018-08-25 19:03:45 -04:00
										 |  |  |     const auto update_tid = GetUpdateTitleID(title_id); | 
					
						
							| 
									
										
										
										
											2018-09-03 18:57:52 -04:00
										 |  |  |     PatchManager update{update_tid}; | 
					
						
							|  |  |  |     auto [nacp, discard_icon_file] = update.GetControlMetadata(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (nacp != nullptr) { | 
					
						
							| 
									
										
										
										
											2018-10-02 17:03:34 -04:00
										 |  |  |         out.insert_or_assign("Update", nacp->GetVersionString()); | 
					
						
							| 
									
										
										
										
											2018-09-03 18:57:52 -04:00
										 |  |  |     } else { | 
					
						
							|  |  |  |         if (installed->HasEntry(update_tid, ContentRecordType::Program)) { | 
					
						
							|  |  |  |             const auto meta_ver = installed->GetEntryVersion(update_tid); | 
					
						
							|  |  |  |             if (meta_ver == boost::none || meta_ver.get() == 0) { | 
					
						
							| 
									
										
										
										
											2018-10-02 17:03:34 -04:00
										 |  |  |                 out.insert_or_assign("Update", ""); | 
					
						
							| 
									
										
										
										
											2018-09-03 18:57:52 -04:00
										 |  |  |             } else { | 
					
						
							| 
									
										
										
										
											2018-10-02 17:03:34 -04:00
										 |  |  |                 out.insert_or_assign( | 
					
						
							|  |  |  |                     "Update", | 
					
						
							|  |  |  |                     FormatTitleVersion(meta_ver.get(), TitleVersionFormat::ThreeElements)); | 
					
						
							| 
									
										
										
										
											2018-09-03 18:57:52 -04:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2018-09-25 09:19:42 -04:00
										 |  |  |         } else if (update_raw != nullptr) { | 
					
						
							| 
									
										
										
										
											2018-09-25 14:07:13 -04:00
										 |  |  |             out[PatchType::Update] = "PACKED"; | 
					
						
							| 
									
										
										
										
											2018-09-03 18:57:52 -04:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2018-08-26 10:53:31 -04:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-08-25 19:03:45 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-02 17:03:34 -04:00
										 |  |  |     // General Mods (LayeredFS and IPS)
 | 
					
						
							| 
									
										
										
										
											2018-09-29 22:14:01 -04:00
										 |  |  |     const auto mod_dir = Service::FileSystem::GetModificationLoadRoot(title_id); | 
					
						
							|  |  |  |     if (mod_dir != nullptr && mod_dir->GetSize() > 0) { | 
					
						
							|  |  |  |         for (const auto& mod : mod_dir->GetSubdirectories()) { | 
					
						
							|  |  |  |             std::string types; | 
					
						
							|  |  |  |             if (IsDirValidAndNonEmpty(mod->GetSubdirectory("exefs"))) | 
					
						
							|  |  |  |                 AppendCommaIfNotEmpty(types, "IPS"); | 
					
						
							|  |  |  |             if (IsDirValidAndNonEmpty(mod->GetSubdirectory("romfs"))) | 
					
						
							|  |  |  |                 AppendCommaIfNotEmpty(types, "LayeredFS"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (types.empty()) | 
					
						
							|  |  |  |                 continue; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             out.insert_or_assign(mod->GetName(), types); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-09-19 22:02:44 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-26 22:15:05 -04:00
										 |  |  |     // DLC
 | 
					
						
							|  |  |  |     const auto dlc_entries = installed->ListEntriesFilter(TitleType::AOC, ContentRecordType::Data); | 
					
						
							|  |  |  |     std::vector<RegisteredCacheEntry> dlc_match; | 
					
						
							|  |  |  |     dlc_match.reserve(dlc_entries.size()); | 
					
						
							|  |  |  |     std::copy_if(dlc_entries.begin(), dlc_entries.end(), std::back_inserter(dlc_match), | 
					
						
							|  |  |  |                  [this, &installed](const RegisteredCacheEntry& entry) { | 
					
						
							|  |  |  |                      return (entry.title_id & DLC_BASE_TITLE_ID_MASK) == title_id && | 
					
						
							|  |  |  |                             installed->GetEntry(entry)->GetStatus() == | 
					
						
							|  |  |  |                                 Loader::ResultStatus::Success; | 
					
						
							|  |  |  |                  }); | 
					
						
							|  |  |  |     if (!dlc_match.empty()) { | 
					
						
							|  |  |  |         // Ensure sorted so DLC IDs show in order.
 | 
					
						
							|  |  |  |         std::sort(dlc_match.begin(), dlc_match.end()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         std::string list; | 
					
						
							|  |  |  |         for (size_t i = 0; i < dlc_match.size() - 1; ++i) | 
					
						
							|  |  |  |             list += fmt::format("{}, ", dlc_match[i].title_id & 0x7FF); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         list += fmt::format("{}", dlc_match.back().title_id & 0x7FF); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-02 17:03:34 -04:00
										 |  |  |         out.insert_or_assign("DLC", std::move(list)); | 
					
						
							| 
									
										
										
										
											2018-09-26 22:15:05 -04:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-25 19:03:45 -04:00
										 |  |  |     return out; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-03 18:57:52 -04:00
										 |  |  | std::pair<std::shared_ptr<NACP>, VirtualFile> PatchManager::GetControlMetadata() const { | 
					
						
							|  |  |  |     const auto& installed{Service::FileSystem::GetUnionContents()}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const auto base_control_nca = installed->GetEntry(title_id, ContentRecordType::Control); | 
					
						
							|  |  |  |     if (base_control_nca == nullptr) | 
					
						
							|  |  |  |         return {}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return ParseControlNCA(base_control_nca); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | std::pair<std::shared_ptr<NACP>, VirtualFile> PatchManager::ParseControlNCA( | 
					
						
							|  |  |  |     const std::shared_ptr<NCA>& nca) const { | 
					
						
							|  |  |  |     const auto base_romfs = nca->GetRomFS(); | 
					
						
							|  |  |  |     if (base_romfs == nullptr) | 
					
						
							|  |  |  |         return {}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const auto romfs = PatchRomFS(base_romfs, nca->GetBaseIVFCOffset(), ContentRecordType::Control); | 
					
						
							|  |  |  |     if (romfs == nullptr) | 
					
						
							|  |  |  |         return {}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const auto extracted = ExtractRomFS(romfs); | 
					
						
							|  |  |  |     if (extracted == nullptr) | 
					
						
							|  |  |  |         return {}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     auto nacp_file = extracted->GetFile("control.nacp"); | 
					
						
							|  |  |  |     if (nacp_file == nullptr) | 
					
						
							|  |  |  |         nacp_file = extracted->GetFile("Control.nacp"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const auto nacp = nacp_file == nullptr ? nullptr : std::make_shared<NACP>(nacp_file); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     VirtualFile icon_file; | 
					
						
							|  |  |  |     for (const auto& language : FileSys::LANGUAGE_NAMES) { | 
					
						
							|  |  |  |         icon_file = extracted->GetFile("icon_" + std::string(language) + ".dat"); | 
					
						
							|  |  |  |         if (icon_file != nullptr) | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return {nacp, icon_file}; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2018-08-25 19:03:45 -04:00
										 |  |  | } // namespace FileSys
 |