patch_manager.cpp 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. // Copyright 2018 yuzu emulator team
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include "core/file_sys/content_archive.h"
  5. #include "core/file_sys/control_metadata.h"
  6. #include "core/file_sys/patch_manager.h"
  7. #include "core/file_sys/registered_cache.h"
  8. #include "core/file_sys/romfs.h"
  9. #include "core/hle/service/filesystem/filesystem.h"
  10. #include "core/loader/loader.h"
  11. namespace FileSys {
  12. constexpr u64 SINGLE_BYTE_MODULUS = 0x100;
  13. std::string FormatTitleVersion(u32 version, TitleVersionFormat format) {
  14. std::array<u8, sizeof(u32)> bytes{};
  15. bytes[0] = version % SINGLE_BYTE_MODULUS;
  16. for (size_t i = 1; i < bytes.size(); ++i) {
  17. version /= SINGLE_BYTE_MODULUS;
  18. bytes[i] = version % SINGLE_BYTE_MODULUS;
  19. }
  20. if (format == TitleVersionFormat::FourElements)
  21. return fmt::format("v{}.{}.{}.{}", bytes[3], bytes[2], bytes[1], bytes[0]);
  22. return fmt::format("v{}.{}.{}", bytes[3], bytes[2], bytes[1]);
  23. }
  24. constexpr std::array<const char*, 1> PATCH_TYPE_NAMES{
  25. "Update",
  26. };
  27. std::string FormatPatchTypeName(PatchType type) {
  28. return PATCH_TYPE_NAMES.at(static_cast<size_t>(type));
  29. }
  30. PatchManager::PatchManager(u64 title_id) : title_id(title_id) {}
  31. VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
  32. LOG_INFO(Loader, "Patching ExeFS for title_id={:016X}", title_id);
  33. if (exefs == nullptr)
  34. return exefs;
  35. const auto installed = Service::FileSystem::GetUnionContents();
  36. // Game Updates
  37. const auto update_tid = GetUpdateTitleID(title_id);
  38. const auto update = installed->GetEntry(update_tid, ContentRecordType::Program);
  39. if (update != nullptr) {
  40. if (update->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS &&
  41. update->GetExeFS() != nullptr) {
  42. LOG_INFO(Loader, " ExeFS: Update ({}) applied successfully",
  43. FormatTitleVersion(installed->GetEntryVersion(update_tid).get_value_or(0)));
  44. exefs = update->GetExeFS();
  45. }
  46. }
  47. return exefs;
  48. }
  49. VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset,
  50. ContentRecordType type) const {
  51. LOG_INFO(Loader, "Patching RomFS for title_id={:016X}, type={:02X}", title_id,
  52. static_cast<u8>(type));
  53. if (romfs == nullptr)
  54. return romfs;
  55. const auto installed = Service::FileSystem::GetUnionContents();
  56. // Game Updates
  57. const auto update_tid = GetUpdateTitleID(title_id);
  58. const auto update = installed->GetEntryRaw(update_tid, type);
  59. if (update != nullptr) {
  60. const auto new_nca = std::make_shared<NCA>(update, romfs, ivfc_offset);
  61. if (new_nca->GetStatus() == Loader::ResultStatus::Success &&
  62. new_nca->GetRomFS() != nullptr) {
  63. LOG_INFO(Loader, " RomFS: Update ({}) applied successfully",
  64. FormatTitleVersion(installed->GetEntryVersion(update_tid).get_value_or(0)));
  65. romfs = new_nca->GetRomFS();
  66. }
  67. }
  68. return romfs;
  69. }
  70. std::map<PatchType, std::string> PatchManager::GetPatchVersionNames() const {
  71. std::map<PatchType, std::string> out;
  72. const auto installed = Service::FileSystem::GetUnionContents();
  73. const auto update_tid = GetUpdateTitleID(title_id);
  74. PatchManager update{update_tid};
  75. auto [nacp, discard_icon_file] = update.GetControlMetadata();
  76. if (nacp != nullptr) {
  77. out[PatchType::Update] = nacp->GetVersionString();
  78. } else {
  79. if (installed->HasEntry(update_tid, ContentRecordType::Program)) {
  80. const auto meta_ver = installed->GetEntryVersion(update_tid);
  81. if (meta_ver == boost::none || meta_ver.get() == 0) {
  82. out[PatchType::Update] = "";
  83. } else {
  84. out[PatchType::Update] =
  85. FormatTitleVersion(meta_ver.get(), TitleVersionFormat::ThreeElements);
  86. }
  87. }
  88. }
  89. return out;
  90. }
  91. std::pair<std::shared_ptr<NACP>, VirtualFile> PatchManager::GetControlMetadata() const {
  92. const auto& installed{Service::FileSystem::GetUnionContents()};
  93. const auto base_control_nca = installed->GetEntry(title_id, ContentRecordType::Control);
  94. if (base_control_nca == nullptr)
  95. return {};
  96. return ParseControlNCA(base_control_nca);
  97. }
  98. std::pair<std::shared_ptr<NACP>, VirtualFile> PatchManager::ParseControlNCA(
  99. const std::shared_ptr<NCA>& nca) const {
  100. const auto base_romfs = nca->GetRomFS();
  101. if (base_romfs == nullptr)
  102. return {};
  103. const auto romfs = PatchRomFS(base_romfs, nca->GetBaseIVFCOffset(), ContentRecordType::Control);
  104. if (romfs == nullptr)
  105. return {};
  106. const auto extracted = ExtractRomFS(romfs);
  107. if (extracted == nullptr)
  108. return {};
  109. auto nacp_file = extracted->GetFile("control.nacp");
  110. if (nacp_file == nullptr)
  111. nacp_file = extracted->GetFile("Control.nacp");
  112. const auto nacp = nacp_file == nullptr ? nullptr : std::make_shared<NACP>(nacp_file);
  113. VirtualFile icon_file;
  114. for (const auto& language : FileSys::LANGUAGE_NAMES) {
  115. icon_file = extracted->GetFile("icon_" + std::string(language) + ".dat");
  116. if (icon_file != nullptr)
  117. break;
  118. }
  119. return {nacp, icon_file};
  120. }
  121. } // namespace FileSys