card_image.cpp 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  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 <string>
  6. #include <fmt/ostream.h>
  7. #include "common/logging/log.h"
  8. #include "core/file_sys/card_image.h"
  9. #include "core/file_sys/content_archive.h"
  10. #include "core/file_sys/nca_metadata.h"
  11. #include "core/file_sys/partition_filesystem.h"
  12. #include "core/file_sys/submission_package.h"
  13. #include "core/file_sys/vfs_offset.h"
  14. #include "core/loader/loader.h"
  15. namespace FileSys {
  16. constexpr std::array<const char*, 0x4> partition_names = {"update", "normal", "secure", "logo"};
  17. XCI::XCI(VirtualFile file_)
  18. : file(std::move(file_)), program_nca_status{Loader::ResultStatus::ErrorXCIMissingProgramNCA},
  19. partitions(0x4) {
  20. if (file->ReadObject(&header) != sizeof(GamecardHeader)) {
  21. status = Loader::ResultStatus::ErrorBadXCIHeader;
  22. return;
  23. }
  24. if (header.magic != Common::MakeMagic('H', 'E', 'A', 'D')) {
  25. status = Loader::ResultStatus::ErrorBadXCIHeader;
  26. return;
  27. }
  28. PartitionFilesystem main_hfs(
  29. std::make_shared<OffsetVfsFile>(file, header.hfs_size, header.hfs_offset));
  30. if (main_hfs.GetStatus() != Loader::ResultStatus::Success) {
  31. status = main_hfs.GetStatus();
  32. return;
  33. }
  34. for (XCIPartition partition :
  35. {XCIPartition::Update, XCIPartition::Normal, XCIPartition::Secure, XCIPartition::Logo}) {
  36. auto raw = main_hfs.GetFile(partition_names[static_cast<std::size_t>(partition)]);
  37. if (raw != nullptr)
  38. partitions[static_cast<std::size_t>(partition)] =
  39. std::make_shared<PartitionFilesystem>(raw);
  40. }
  41. secure_partition = std::make_shared<NSP>(
  42. main_hfs.GetFile(partition_names[static_cast<std::size_t>(XCIPartition::Secure)]));
  43. const auto secure_ncas = secure_partition->GetNCAsCollapsed();
  44. std::copy(secure_ncas.begin(), secure_ncas.end(), std::back_inserter(ncas));
  45. program =
  46. secure_partition->GetNCA(secure_partition->GetProgramTitleID(), ContentRecordType::Program);
  47. program_nca_status = secure_partition->GetProgramStatus(secure_partition->GetProgramTitleID());
  48. if (program_nca_status == Loader::ResultStatus::ErrorNSPMissingProgramNCA)
  49. program_nca_status = Loader::ResultStatus::ErrorXCIMissingProgramNCA;
  50. auto result = AddNCAFromPartition(XCIPartition::Update);
  51. if (result != Loader::ResultStatus::Success) {
  52. status = result;
  53. return;
  54. }
  55. result = AddNCAFromPartition(XCIPartition::Normal);
  56. if (result != Loader::ResultStatus::Success) {
  57. status = result;
  58. return;
  59. }
  60. if (GetFormatVersion() >= 0x2) {
  61. result = AddNCAFromPartition(XCIPartition::Logo);
  62. if (result != Loader::ResultStatus::Success) {
  63. status = result;
  64. return;
  65. }
  66. }
  67. status = Loader::ResultStatus::Success;
  68. }
  69. XCI::~XCI() = default;
  70. Loader::ResultStatus XCI::GetStatus() const {
  71. return status;
  72. }
  73. Loader::ResultStatus XCI::GetProgramNCAStatus() const {
  74. return program_nca_status;
  75. }
  76. VirtualDir XCI::GetPartition(XCIPartition partition) const {
  77. return partitions[static_cast<std::size_t>(partition)];
  78. }
  79. std::shared_ptr<NSP> XCI::GetSecurePartitionNSP() const {
  80. return secure_partition;
  81. }
  82. VirtualDir XCI::GetSecurePartition() const {
  83. return GetPartition(XCIPartition::Secure);
  84. }
  85. VirtualDir XCI::GetNormalPartition() const {
  86. return GetPartition(XCIPartition::Normal);
  87. }
  88. VirtualDir XCI::GetUpdatePartition() const {
  89. return GetPartition(XCIPartition::Update);
  90. }
  91. VirtualDir XCI::GetLogoPartition() const {
  92. return GetPartition(XCIPartition::Logo);
  93. }
  94. u64 XCI::GetProgramTitleID() const {
  95. return secure_partition->GetProgramTitleID();
  96. }
  97. bool XCI::HasProgramNCA() const {
  98. return program != nullptr;
  99. }
  100. VirtualFile XCI::GetProgramNCAFile() const {
  101. if (!HasProgramNCA()) {
  102. return nullptr;
  103. }
  104. return program->GetBaseFile();
  105. }
  106. const std::vector<std::shared_ptr<NCA>>& XCI::GetNCAs() const {
  107. return ncas;
  108. }
  109. std::shared_ptr<NCA> XCI::GetNCAByType(NCAContentType type) const {
  110. const auto iter =
  111. std::find_if(ncas.begin(), ncas.end(),
  112. [type](const std::shared_ptr<NCA>& nca) { return nca->GetType() == type; });
  113. return iter == ncas.end() ? nullptr : *iter;
  114. }
  115. VirtualFile XCI::GetNCAFileByType(NCAContentType type) const {
  116. auto nca = GetNCAByType(type);
  117. if (nca != nullptr)
  118. return nca->GetBaseFile();
  119. return nullptr;
  120. }
  121. std::vector<VirtualFile> XCI::GetFiles() const {
  122. return {};
  123. }
  124. std::vector<VirtualDir> XCI::GetSubdirectories() const {
  125. return {};
  126. }
  127. std::string XCI::GetName() const {
  128. return file->GetName();
  129. }
  130. VirtualDir XCI::GetParentDirectory() const {
  131. return file->GetContainingDirectory();
  132. }
  133. Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) {
  134. if (partitions[static_cast<std::size_t>(part)] == nullptr) {
  135. return Loader::ResultStatus::ErrorXCIMissingPartition;
  136. }
  137. for (const VirtualFile& file : partitions[static_cast<std::size_t>(part)]->GetFiles()) {
  138. if (file->GetExtension() != "nca")
  139. continue;
  140. auto nca = std::make_shared<NCA>(file, nullptr, 0, keys);
  141. // TODO(DarkLordZach): Add proper Rev1+ Support
  142. if (nca->IsUpdate())
  143. continue;
  144. if (nca->GetType() == NCAContentType::Program) {
  145. program_nca_status = nca->GetStatus();
  146. }
  147. if (nca->GetStatus() == Loader::ResultStatus::Success) {
  148. ncas.push_back(std::move(nca));
  149. } else {
  150. const u16 error_id = static_cast<u16>(nca->GetStatus());
  151. LOG_CRITICAL(Loader, "Could not load NCA {}/{}, failed with error code {:04X} ({})",
  152. partition_names[static_cast<std::size_t>(part)], nca->GetName(), error_id,
  153. nca->GetStatus());
  154. }
  155. }
  156. return Loader::ResultStatus::Success;
  157. }
  158. u8 XCI::GetFormatVersion() const {
  159. return GetLogoPartition() == nullptr ? 0x1 : 0x2;
  160. }
  161. } // namespace FileSys