nca_patch.cpp 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. // Copyright 2018 yuzu emulator team
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include <algorithm>
  5. #include <cstddef>
  6. #include <cstring>
  7. #include "common/assert.h"
  8. #include "core/crypto/aes_util.h"
  9. #include "core/file_sys/nca_patch.h"
  10. namespace FileSys {
  11. BKTR::BKTR(VirtualFile base_romfs_, VirtualFile bktr_romfs_, RelocationBlock relocation_,
  12. std::vector<RelocationBucket> relocation_buckets_, SubsectionBlock subsection_,
  13. std::vector<SubsectionBucket> subsection_buckets_, bool is_encrypted_,
  14. Core::Crypto::Key128 key_, u64 base_offset_, u64 ivfc_offset_,
  15. std::array<u8, 8> section_ctr_)
  16. : relocation(relocation_), relocation_buckets(std::move(relocation_buckets_)),
  17. subsection(subsection_), subsection_buckets(std::move(subsection_buckets_)),
  18. base_romfs(std::move(base_romfs_)), bktr_romfs(std::move(bktr_romfs_)),
  19. encrypted(is_encrypted_), key(key_), base_offset(base_offset_), ivfc_offset(ivfc_offset_),
  20. section_ctr(section_ctr_) {
  21. for (std::size_t i = 0; i < relocation.number_buckets - 1; ++i) {
  22. relocation_buckets[i].entries.push_back({relocation.base_offsets[i + 1], 0, 0});
  23. }
  24. for (std::size_t i = 0; i < subsection.number_buckets - 1; ++i) {
  25. subsection_buckets[i].entries.push_back({subsection_buckets[i + 1].entries[0].address_patch,
  26. {0},
  27. subsection_buckets[i + 1].entries[0].ctr});
  28. }
  29. relocation_buckets.back().entries.push_back({relocation.size, 0, 0});
  30. }
  31. BKTR::~BKTR() = default;
  32. std::size_t BKTR::Read(u8* data, std::size_t length, std::size_t offset) const {
  33. // Read out of bounds.
  34. if (offset >= relocation.size)
  35. return 0;
  36. const auto relocation = GetRelocationEntry(offset);
  37. const auto section_offset = offset - relocation.address_patch + relocation.address_source;
  38. const auto bktr_read = relocation.from_patch;
  39. const auto next_relocation = GetNextRelocationEntry(offset);
  40. if (offset + length > next_relocation.address_patch) {
  41. const u64 partition = next_relocation.address_patch - offset;
  42. return Read(data, partition, offset) +
  43. Read(data + partition, length - partition, offset + partition);
  44. }
  45. if (!bktr_read) {
  46. ASSERT_MSG(section_offset >= ivfc_offset, "Offset calculation negative.");
  47. return base_romfs->Read(data, length, section_offset - ivfc_offset);
  48. }
  49. if (!encrypted) {
  50. return bktr_romfs->Read(data, length, section_offset);
  51. }
  52. const auto subsection = GetSubsectionEntry(section_offset);
  53. Core::Crypto::AESCipher<Core::Crypto::Key128> cipher(key, Core::Crypto::Mode::CTR);
  54. // Calculate AES IV
  55. std::vector<u8> iv(16);
  56. auto subsection_ctr = subsection.ctr;
  57. auto offset_iv = section_offset + base_offset;
  58. for (std::size_t i = 0; i < section_ctr.size(); ++i)
  59. iv[i] = section_ctr[0x8 - i - 1];
  60. offset_iv >>= 4;
  61. for (std::size_t i = 0; i < sizeof(u64); ++i) {
  62. iv[0xF - i] = static_cast<u8>(offset_iv & 0xFF);
  63. offset_iv >>= 8;
  64. }
  65. for (std::size_t i = 0; i < sizeof(u32); ++i) {
  66. iv[0x7 - i] = static_cast<u8>(subsection_ctr & 0xFF);
  67. subsection_ctr >>= 8;
  68. }
  69. cipher.SetIV(iv);
  70. const auto next_subsection = GetNextSubsectionEntry(section_offset);
  71. if (section_offset + length > next_subsection.address_patch) {
  72. const u64 partition = next_subsection.address_patch - section_offset;
  73. return Read(data, partition, offset) +
  74. Read(data + partition, length - partition, offset + partition);
  75. }
  76. const auto block_offset = section_offset & 0xF;
  77. if (block_offset != 0) {
  78. auto block = bktr_romfs->ReadBytes(0x10, section_offset & ~0xF);
  79. cipher.Transcode(block.data(), block.size(), block.data(), Core::Crypto::Op::Decrypt);
  80. if (length + block_offset < 0x10) {
  81. std::memcpy(data, block.data() + block_offset, std::min(length, block.size()));
  82. return std::min(length, block.size());
  83. }
  84. const auto read = 0x10 - block_offset;
  85. std::memcpy(data, block.data() + block_offset, read);
  86. return read + Read(data + read, length - read, offset + read);
  87. }
  88. const auto raw_read = bktr_romfs->Read(data, length, section_offset);
  89. cipher.Transcode(data, raw_read, data, Core::Crypto::Op::Decrypt);
  90. return raw_read;
  91. }
  92. template <bool Subsection, typename BlockType, typename BucketType>
  93. std::pair<std::size_t, std::size_t> BKTR::SearchBucketEntry(u64 offset, BlockType block,
  94. BucketType buckets) const {
  95. if constexpr (Subsection) {
  96. const auto last_bucket = buckets[block.number_buckets - 1];
  97. if (offset >= last_bucket.entries[last_bucket.number_entries].address_patch)
  98. return {block.number_buckets - 1, last_bucket.number_entries};
  99. } else {
  100. ASSERT_MSG(offset <= block.size, "Offset is out of bounds in BKTR relocation block.");
  101. }
  102. std::size_t bucket_id = std::count_if(
  103. block.base_offsets.begin() + 1, block.base_offsets.begin() + block.number_buckets,
  104. [&offset](u64 base_offset) { return base_offset <= offset; });
  105. const auto bucket = buckets[bucket_id];
  106. if (bucket.number_entries == 1)
  107. return {bucket_id, 0};
  108. std::size_t low = 0;
  109. std::size_t mid = 0;
  110. std::size_t high = bucket.number_entries - 1;
  111. while (low <= high) {
  112. mid = (low + high) / 2;
  113. if (bucket.entries[mid].address_patch > offset) {
  114. high = mid - 1;
  115. } else {
  116. if (mid == bucket.number_entries - 1 ||
  117. bucket.entries[mid + 1].address_patch > offset) {
  118. return {bucket_id, mid};
  119. }
  120. low = mid + 1;
  121. }
  122. }
  123. UNREACHABLE_MSG("Offset could not be found in BKTR block.");
  124. }
  125. RelocationEntry BKTR::GetRelocationEntry(u64 offset) const {
  126. const auto res = SearchBucketEntry<false>(offset, relocation, relocation_buckets);
  127. return relocation_buckets[res.first].entries[res.second];
  128. }
  129. RelocationEntry BKTR::GetNextRelocationEntry(u64 offset) const {
  130. const auto res = SearchBucketEntry<false>(offset, relocation, relocation_buckets);
  131. const auto bucket = relocation_buckets[res.first];
  132. if (res.second + 1 < bucket.entries.size())
  133. return bucket.entries[res.second + 1];
  134. return relocation_buckets[res.first + 1].entries[0];
  135. }
  136. SubsectionEntry BKTR::GetSubsectionEntry(u64 offset) const {
  137. const auto res = SearchBucketEntry<true>(offset, subsection, subsection_buckets);
  138. return subsection_buckets[res.first].entries[res.second];
  139. }
  140. SubsectionEntry BKTR::GetNextSubsectionEntry(u64 offset) const {
  141. const auto res = SearchBucketEntry<true>(offset, subsection, subsection_buckets);
  142. const auto bucket = subsection_buckets[res.first];
  143. if (res.second + 1 < bucket.entries.size())
  144. return bucket.entries[res.second + 1];
  145. return subsection_buckets[res.first + 1].entries[0];
  146. }
  147. std::string BKTR::GetName() const {
  148. return base_romfs->GetName();
  149. }
  150. std::size_t BKTR::GetSize() const {
  151. return relocation.size;
  152. }
  153. bool BKTR::Resize(std::size_t new_size) {
  154. return false;
  155. }
  156. std::shared_ptr<VfsDirectory> BKTR::GetContainingDirectory() const {
  157. return base_romfs->GetContainingDirectory();
  158. }
  159. bool BKTR::IsWritable() const {
  160. return false;
  161. }
  162. bool BKTR::IsReadable() const {
  163. return true;
  164. }
  165. std::size_t BKTR::Write(const u8* data, std::size_t length, std::size_t offset) {
  166. return 0;
  167. }
  168. bool BKTR::Rename(std::string_view name) {
  169. return base_romfs->Rename(name);
  170. }
  171. } // namespace FileSys