nca_metadata.cpp 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. // Copyright 2018 yuzu emulator team
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include <cstring>
  5. #include "common/common_types.h"
  6. #include "common/logging/log.h"
  7. #include "common/swap.h"
  8. #include "core/file_sys/nca_metadata.h"
  9. #include "core/file_sys/vfs.h"
  10. namespace FileSys {
  11. CNMT::CNMT(VirtualFile file) {
  12. if (file->ReadObject(&header) != sizeof(CNMTHeader))
  13. return;
  14. // If type is {Application, Update, AOC} has opt-header.
  15. if (header.type >= TitleType::Application && header.type <= TitleType::AOC) {
  16. if (file->ReadObject(&opt_header, sizeof(CNMTHeader)) != sizeof(OptionalHeader)) {
  17. LOG_WARNING(Loader, "Failed to read optional header.");
  18. }
  19. }
  20. for (u16 i = 0; i < header.number_content_entries; ++i) {
  21. auto& next = content_records.emplace_back(ContentRecord{});
  22. if (file->ReadObject(&next, sizeof(CNMTHeader) + i * sizeof(ContentRecord) +
  23. header.table_offset) != sizeof(ContentRecord)) {
  24. content_records.erase(content_records.end() - 1);
  25. }
  26. }
  27. for (u16 i = 0; i < header.number_meta_entries; ++i) {
  28. auto& next = meta_records.emplace_back(MetaRecord{});
  29. if (file->ReadObject(&next, sizeof(CNMTHeader) + i * sizeof(MetaRecord) +
  30. header.table_offset) != sizeof(MetaRecord)) {
  31. meta_records.erase(meta_records.end() - 1);
  32. }
  33. }
  34. }
  35. CNMT::CNMT(CNMTHeader header, OptionalHeader opt_header, std::vector<ContentRecord> content_records,
  36. std::vector<MetaRecord> meta_records)
  37. : header(std::move(header)), opt_header(std::move(opt_header)),
  38. content_records(std::move(content_records)), meta_records(std::move(meta_records)) {}
  39. CNMT::~CNMT() = default;
  40. u64 CNMT::GetTitleID() const {
  41. return header.title_id;
  42. }
  43. u32 CNMT::GetTitleVersion() const {
  44. return header.title_version;
  45. }
  46. TitleType CNMT::GetType() const {
  47. return header.type;
  48. }
  49. const std::vector<ContentRecord>& CNMT::GetContentRecords() const {
  50. return content_records;
  51. }
  52. const std::vector<MetaRecord>& CNMT::GetMetaRecords() const {
  53. return meta_records;
  54. }
  55. bool CNMT::UnionRecords(const CNMT& other) {
  56. bool change = false;
  57. for (const auto& rec : other.content_records) {
  58. const auto iter = std::find_if(content_records.begin(), content_records.end(),
  59. [&rec](const ContentRecord& r) {
  60. return r.nca_id == rec.nca_id && r.type == rec.type;
  61. });
  62. if (iter == content_records.end()) {
  63. content_records.emplace_back(rec);
  64. ++header.number_content_entries;
  65. change = true;
  66. }
  67. }
  68. for (const auto& rec : other.meta_records) {
  69. const auto iter =
  70. std::find_if(meta_records.begin(), meta_records.end(), [&rec](const MetaRecord& r) {
  71. return r.title_id == rec.title_id && r.title_version == rec.title_version &&
  72. r.type == rec.type;
  73. });
  74. if (iter == meta_records.end()) {
  75. meta_records.emplace_back(rec);
  76. ++header.number_meta_entries;
  77. change = true;
  78. }
  79. }
  80. return change;
  81. }
  82. std::vector<u8> CNMT::Serialize() const {
  83. const bool has_opt_header =
  84. header.type >= TitleType::Application && header.type <= TitleType::AOC;
  85. const auto dead_zone = header.table_offset + sizeof(CNMTHeader);
  86. std::vector<u8> out(
  87. std::max(sizeof(CNMTHeader) + (has_opt_header ? sizeof(OptionalHeader) : 0), dead_zone) +
  88. content_records.size() * sizeof(ContentRecord) + meta_records.size() * sizeof(MetaRecord));
  89. memcpy(out.data(), &header, sizeof(CNMTHeader));
  90. // Optional Header
  91. if (has_opt_header) {
  92. memcpy(out.data() + sizeof(CNMTHeader), &opt_header, sizeof(OptionalHeader));
  93. }
  94. u64_le offset = header.table_offset;
  95. for (const auto& rec : content_records) {
  96. memcpy(out.data() + offset + sizeof(CNMTHeader), &rec, sizeof(ContentRecord));
  97. offset += sizeof(ContentRecord);
  98. }
  99. for (const auto& rec : meta_records) {
  100. memcpy(out.data() + offset + sizeof(CNMTHeader), &rec, sizeof(MetaRecord));
  101. offset += sizeof(MetaRecord);
  102. }
  103. return out;
  104. }
  105. } // namespace FileSys