patch_manager.cpp 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. // Copyright 2018 yuzu emulator team
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include <array>
  5. #include <cstddef>
  6. #include "common/logging/log.h"
  7. #include "core/file_sys/content_archive.h"
  8. #include "core/file_sys/control_metadata.h"
  9. #include "core/file_sys/patch_manager.h"
  10. #include "core/file_sys/registered_cache.h"
  11. #include "core/file_sys/romfs.h"
  12. #include "core/file_sys/vfs_layered.h"
  13. #include "core/hle/service/filesystem/filesystem.h"
  14. #include "core/loader/loader.h"
  15. namespace FileSys {
  16. constexpr u64 SINGLE_BYTE_MODULUS = 0x100;
  17. std::string FormatTitleVersion(u32 version, TitleVersionFormat format) {
  18. std::array<u8, sizeof(u32)> bytes{};
  19. bytes[0] = version % SINGLE_BYTE_MODULUS;
  20. for (std::size_t i = 1; i < bytes.size(); ++i) {
  21. version /= SINGLE_BYTE_MODULUS;
  22. bytes[i] = version % SINGLE_BYTE_MODULUS;
  23. }
  24. if (format == TitleVersionFormat::FourElements)
  25. return fmt::format("v{}.{}.{}.{}", bytes[3], bytes[2], bytes[1], bytes[0]);
  26. return fmt::format("v{}.{}.{}", bytes[3], bytes[2], bytes[1]);
  27. }
  28. constexpr std::array<const char*, 2> PATCH_TYPE_NAMES{
  29. "Update",
  30. "LayeredFS",
  31. };
  32. std::string FormatPatchTypeName(PatchType type) {
  33. return PATCH_TYPE_NAMES.at(static_cast<std::size_t>(type));
  34. }
  35. PatchManager::PatchManager(u64 title_id) : title_id(title_id) {}
  36. PatchManager::~PatchManager() = default;
  37. VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
  38. LOG_INFO(Loader, "Patching ExeFS for title_id={:016X}", title_id);
  39. if (exefs == nullptr)
  40. return exefs;
  41. const auto installed = Service::FileSystem::GetUnionContents();
  42. // Game Updates
  43. const auto update_tid = GetUpdateTitleID(title_id);
  44. const auto update = installed->GetEntry(update_tid, ContentRecordType::Program);
  45. if (update != nullptr) {
  46. if (update->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS &&
  47. update->GetExeFS() != nullptr) {
  48. LOG_INFO(Loader, " ExeFS: Update ({}) applied successfully",
  49. FormatTitleVersion(installed->GetEntryVersion(update_tid).get_value_or(0)));
  50. exefs = update->GetExeFS();
  51. }
  52. }
  53. return exefs;
  54. }
  55. static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) {
  56. const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
  57. if (type == ContentRecordType::Program && load_dir != nullptr && load_dir->GetSize() > 0) {
  58. auto extracted = ExtractRomFS(romfs);
  59. if (extracted != nullptr) {
  60. auto patch_dirs = load_dir->GetSubdirectories();
  61. std::sort(patch_dirs.begin(), patch_dirs.end(),
  62. [](const VirtualDir& l, const VirtualDir& r) {
  63. return l->GetName() < r->GetName();
  64. });
  65. std::vector<VirtualDir> layers;
  66. layers.reserve(patch_dirs.size() + 1);
  67. for (const auto& subdir : patch_dirs) {
  68. auto romfs_dir = subdir->GetSubdirectory("romfs");
  69. if (romfs_dir != nullptr)
  70. layers.push_back(std::move(romfs_dir));
  71. }
  72. layers.push_back(std::move(extracted));
  73. auto layered = LayeredVfsDirectory::MakeLayeredDirectory(std::move(layers));
  74. if (layered != nullptr) {
  75. auto packed = CreateRomFS(std::move(layered));
  76. if (packed != nullptr) {
  77. LOG_INFO(Loader, " RomFS: LayeredFS patches applied successfully");
  78. romfs = std::move(packed);
  79. }
  80. }
  81. }
  82. }
  83. }
  84. VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset,
  85. ContentRecordType type) const {
  86. LOG_INFO(Loader, "Patching RomFS for title_id={:016X}, type={:02X}", title_id,
  87. static_cast<u8>(type));
  88. if (romfs == nullptr)
  89. return romfs;
  90. const auto installed = Service::FileSystem::GetUnionContents();
  91. // Game Updates
  92. const auto update_tid = GetUpdateTitleID(title_id);
  93. const auto update = installed->GetEntryRaw(update_tid, type);
  94. if (update != nullptr) {
  95. const auto new_nca = std::make_shared<NCA>(update, romfs, ivfc_offset);
  96. if (new_nca->GetStatus() == Loader::ResultStatus::Success &&
  97. new_nca->GetRomFS() != nullptr) {
  98. LOG_INFO(Loader, " RomFS: Update ({}) applied successfully",
  99. FormatTitleVersion(installed->GetEntryVersion(update_tid).get_value_or(0)));
  100. romfs = new_nca->GetRomFS();
  101. }
  102. }
  103. // LayeredFS
  104. ApplyLayeredFS(romfs, title_id, type);
  105. return romfs;
  106. }
  107. std::map<PatchType, std::string> PatchManager::GetPatchVersionNames() const {
  108. std::map<PatchType, std::string> out;
  109. const auto installed = Service::FileSystem::GetUnionContents();
  110. const auto update_tid = GetUpdateTitleID(title_id);
  111. PatchManager update{update_tid};
  112. auto [nacp, discard_icon_file] = update.GetControlMetadata();
  113. if (nacp != nullptr) {
  114. out[PatchType::Update] = nacp->GetVersionString();
  115. } else {
  116. if (installed->HasEntry(update_tid, ContentRecordType::Program)) {
  117. const auto meta_ver = installed->GetEntryVersion(update_tid);
  118. if (meta_ver == boost::none || meta_ver.get() == 0) {
  119. out[PatchType::Update] = "";
  120. } else {
  121. out[PatchType::Update] =
  122. FormatTitleVersion(meta_ver.get(), TitleVersionFormat::ThreeElements);
  123. }
  124. }
  125. }
  126. const auto lfs_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
  127. if (lfs_dir != nullptr && lfs_dir->GetSize() > 0)
  128. out.insert_or_assign(PatchType::LayeredFS, "");
  129. return out;
  130. }
  131. std::pair<std::shared_ptr<NACP>, VirtualFile> PatchManager::GetControlMetadata() const {
  132. const auto& installed{Service::FileSystem::GetUnionContents()};
  133. const auto base_control_nca = installed->GetEntry(title_id, ContentRecordType::Control);
  134. if (base_control_nca == nullptr)
  135. return {};
  136. return ParseControlNCA(base_control_nca);
  137. }
  138. std::pair<std::shared_ptr<NACP>, VirtualFile> PatchManager::ParseControlNCA(
  139. const std::shared_ptr<NCA>& nca) const {
  140. const auto base_romfs = nca->GetRomFS();
  141. if (base_romfs == nullptr)
  142. return {};
  143. const auto romfs = PatchRomFS(base_romfs, nca->GetBaseIVFCOffset(), ContentRecordType::Control);
  144. if (romfs == nullptr)
  145. return {};
  146. const auto extracted = ExtractRomFS(romfs);
  147. if (extracted == nullptr)
  148. return {};
  149. auto nacp_file = extracted->GetFile("control.nacp");
  150. if (nacp_file == nullptr)
  151. nacp_file = extracted->GetFile("Control.nacp");
  152. const auto nacp = nacp_file == nullptr ? nullptr : std::make_shared<NACP>(nacp_file);
  153. VirtualFile icon_file;
  154. for (const auto& language : FileSys::LANGUAGE_NAMES) {
  155. icon_file = extracted->GetFile("icon_" + std::string(language) + ".dat");
  156. if (icon_file != nullptr)
  157. break;
  158. }
  159. return {nacp, icon_file};
  160. }
  161. } // namespace FileSys