nca_metadata.cpp 4.4 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 "common/common_funcs.h"
  5. #include "common/swap.h"
  6. #include "content_archive.h"
  7. #include "core/file_sys/nca_metadata.h"
  8. namespace FileSys {
  9. CNMT::CNMT(VirtualFile file_) : file(std::move(file_)), header(std::make_unique<CNMTHeader>()) {
  10. if (file->ReadObject(header.get()) != sizeof(CNMTHeader))
  11. return;
  12. // If type is {Application, Update, AOC} has opt-header.
  13. if (static_cast<u8>(header->type) >= 0x80 && static_cast<u8>(header->type) <= 0x82) {
  14. opt_header = std::make_unique<OptionalHeader>();
  15. if (file->ReadObject(opt_header.get(), sizeof(CNMTHeader)) != sizeof(OptionalHeader)) {
  16. opt_header = nullptr;
  17. }
  18. }
  19. for (u16 i = 0; i < header->number_content_entries; ++i) {
  20. auto& next = content_records.emplace_back(ContentRecord{});
  21. if (file->ReadObject(&next, sizeof(CNMTHeader) + i * sizeof(ContentRecord) +
  22. header->table_offset) != sizeof(ContentRecord)) {
  23. content_records.erase(content_records.end() - 1);
  24. }
  25. }
  26. for (u16 i = 0; i < header->number_meta_entries; ++i) {
  27. auto& next = meta_records.emplace_back(MetaRecord{});
  28. if (file->ReadObject(&next, sizeof(CNMTHeader) + i * sizeof(MetaRecord) +
  29. header->table_offset) != sizeof(MetaRecord)) {
  30. meta_records.erase(meta_records.end() - 1);
  31. }
  32. }
  33. }
  34. CNMT::CNMT(CNMTHeader header, OptionalHeader opt_header, std::vector<ContentRecord> content_records,
  35. std::vector<MetaRecord> meta_records)
  36. : file(nullptr), header(std::make_unique<CNMTHeader>(std::move(header))),
  37. opt_header(std::make_unique<OptionalHeader>(std::move(opt_header))),
  38. content_records(std::move(content_records)), meta_records(std::move(meta_records)) {}
  39. u64 CNMT::GetTitleID() const {
  40. return header->title_id;
  41. }
  42. u32 CNMT::GetTitleVersion() const {
  43. return header->title_version;
  44. }
  45. TitleType CNMT::GetType() const {
  46. return header->type;
  47. }
  48. const std::vector<ContentRecord>& CNMT::GetContentRecords() const {
  49. return content_records;
  50. }
  51. const std::vector<MetaRecord>& CNMT::GetMetaRecords() const {
  52. return meta_records;
  53. }
  54. bool CNMT::UnionRecords(const CNMT& other) {
  55. bool change = false;
  56. for (const auto& rec : other.content_records) {
  57. const auto iter = std::find_if(
  58. content_records.begin(), content_records.end(),
  59. [rec](const ContentRecord& r) { return r.nca_id == rec.nca_id && r.type == rec.type; });
  60. if (iter == content_records.end()) {
  61. content_records.emplace_back(rec);
  62. ++header->number_content_entries;
  63. change = true;
  64. }
  65. }
  66. for (const auto& rec : other.meta_records) {
  67. const auto iter =
  68. std::find_if(meta_records.begin(), meta_records.end(), [rec](const MetaRecord& r) {
  69. return r.title_id == rec.title_id && r.title_version == rec.title_version &&
  70. r.type == rec.type;
  71. });
  72. if (iter == meta_records.end()) {
  73. meta_records.emplace_back(rec);
  74. ++header->number_meta_entries;
  75. change = true;
  76. }
  77. }
  78. return change;
  79. }
  80. std::vector<u8> CNMT::Serialize() const {
  81. if (header == nullptr)
  82. return {};
  83. std::vector<u8> out(sizeof(CNMTHeader));
  84. out.reserve(0x100); // Avoid resizing -- average size.
  85. memcpy(out.data(), header.get(), sizeof(CNMTHeader));
  86. if (opt_header != nullptr) {
  87. out.resize(out.size() + sizeof(OptionalHeader));
  88. memcpy(out.data() + sizeof(CNMTHeader), opt_header.get(), sizeof(OptionalHeader));
  89. }
  90. auto offset = header->table_offset;
  91. const auto dead_zone = offset + sizeof(CNMTHeader) - out.size();
  92. if (dead_zone > 0)
  93. out.resize(offset + sizeof(CNMTHeader));
  94. for (const auto& rec : content_records) {
  95. out.resize(out.size() + sizeof(ContentRecord));
  96. memcpy(out.data() + offset + sizeof(CNMTHeader), &rec, sizeof(ContentRecord));
  97. offset += sizeof(ContentRecord);
  98. }
  99. for (const auto& rec : meta_records) {
  100. out.resize(out.size() + sizeof(MetaRecord));
  101. memcpy(out.data() + offset + sizeof(CNMTHeader), &rec, sizeof(MetaRecord));
  102. offset += sizeof(MetaRecord);
  103. }
  104. return out;
  105. }
  106. } // namespace FileSys