summaryrefslogtreecommitdiff
path: root/src/core/file_sys
diff options
context:
space:
mode:
authorGravatar Zach Hilman2018-08-09 20:51:52 -0400
committerGravatar Zach Hilman2018-08-11 22:50:48 -0400
commit9aab7871222ca86bdf817cc6c96956b25aa76674 (patch)
tree0995ea9a3d33b191529272983e4abf6281c29d8f /src/core/file_sys
parentcard_image: Add accessor for all NCAs in XCI (diff)
downloadyuzu-9aab7871222ca86bdf817cc6c96956b25aa76674.tar.gz
yuzu-9aab7871222ca86bdf817cc6c96956b25aa76674.tar.xz
yuzu-9aab7871222ca86bdf817cc6c96956b25aa76674.zip
file_sys: Add support for parsing NCA metadata (CNMT)
Diffstat (limited to 'src/core/file_sys')
-rw-r--r--src/core/file_sys/nca_metadata.cpp125
-rw-r--r--src/core/file_sys/nca_metadata.h105
2 files changed, 230 insertions, 0 deletions
diff --git a/src/core/file_sys/nca_metadata.cpp b/src/core/file_sys/nca_metadata.cpp
new file mode 100644
index 000000000..fa06897b7
--- /dev/null
+++ b/src/core/file_sys/nca_metadata.cpp
@@ -0,0 +1,125 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/common_funcs.h"
6#include "common/swap.h"
7#include "content_archive.h"
8#include "core/file_sys/nca_metadata.h"
9
10namespace FileSys {
11
12CNMT::CNMT(VirtualFile file_) : file(std::move(file_)), header(std::make_unique<CNMTHeader>()) {
13 if (file->ReadObject(header.get()) != sizeof(CNMTHeader))
14 return;
15
16 // If type is {Application, Update, AOC} has opt-header.
17 if (static_cast<u8>(header->type) >= 0x80 && static_cast<u8>(header->type) <= 0x82) {
18 opt_header = std::make_unique<OptionalHeader>();
19 if (file->ReadObject(opt_header.get(), sizeof(CNMTHeader)) != sizeof(OptionalHeader)) {
20 opt_header = nullptr;
21 }
22 }
23
24 for (u16 i = 0; i < header->number_content_entries; ++i) {
25 auto& next = content_records.emplace_back(ContentRecord{});
26 if (file->ReadObject(&next, sizeof(CNMTHeader) + i * sizeof(ContentRecord) +
27 header->table_offset) != sizeof(ContentRecord)) {
28 content_records.erase(content_records.end() - 1);
29 }
30 }
31
32 for (u16 i = 0; i < header->number_meta_entries; ++i) {
33 auto& next = meta_records.emplace_back(MetaRecord{});
34 if (file->ReadObject(&next, sizeof(CNMTHeader) + i * sizeof(MetaRecord) +
35 header->table_offset) != sizeof(MetaRecord)) {
36 meta_records.erase(meta_records.end() - 1);
37 }
38 }
39}
40
41CNMT::CNMT(CNMTHeader header, OptionalHeader opt_header, std::vector<ContentRecord> content_records,
42 std::vector<MetaRecord> meta_records)
43 : file(nullptr), header(std::make_unique<CNMTHeader>(std::move(header))),
44 opt_header(std::make_unique<OptionalHeader>(std::move(opt_header))),
45 content_records(std::move(content_records)), meta_records(std::move(meta_records)) {}
46
47u64 CNMT::GetTitleID() const {
48 return header->title_id;
49}
50
51u32 CNMT::GetTitleVersion() const {
52 return header->title_version;
53}
54
55TitleType CNMT::GetType() const {
56 return header->type;
57}
58
59const std::vector<ContentRecord>& CNMT::GetContentRecords() const {
60 return content_records;
61}
62
63const std::vector<MetaRecord>& CNMT::GetMetaRecords() const {
64 return meta_records;
65}
66
67bool CNMT::UnionRecords(const CNMT& other) {
68 bool change = false;
69 for (const auto& rec : other.content_records) {
70 const auto iter = std::find_if(
71 content_records.begin(), content_records.end(),
72 [rec](const ContentRecord& r) { return r.nca_id == rec.nca_id && r.type == rec.type; });
73 if (iter == content_records.end()) {
74 content_records.emplace_back(rec);
75 ++header->number_content_entries;
76 change = true;
77 }
78 }
79 for (const auto& rec : other.meta_records) {
80 const auto iter =
81 std::find_if(meta_records.begin(), meta_records.end(), [rec](const MetaRecord& r) {
82 return r.title_id == rec.title_id && r.title_version == rec.title_version &&
83 r.type == rec.type;
84 });
85 if (iter == meta_records.end()) {
86 meta_records.emplace_back(rec);
87 ++header->number_meta_entries;
88 change = true;
89 }
90 }
91 return change;
92}
93
94std::vector<u8> CNMT::Serialize() const {
95 if (header == nullptr)
96 return {};
97 std::vector<u8> out(sizeof(CNMTHeader));
98 out.reserve(0x100); // Avoid resizing -- average size.
99 memcpy(out.data(), header.get(), sizeof(CNMTHeader));
100 if (opt_header != nullptr) {
101 out.resize(out.size() + sizeof(OptionalHeader));
102 memcpy(out.data() + sizeof(CNMTHeader), opt_header.get(), sizeof(OptionalHeader));
103 }
104
105 auto offset = header->table_offset;
106
107 const auto dead_zone = offset + sizeof(CNMTHeader) - out.size();
108 if (dead_zone > 0)
109 out.resize(offset + sizeof(CNMTHeader));
110
111 for (const auto& rec : content_records) {
112 out.resize(out.size() + sizeof(ContentRecord));
113 memcpy(out.data() + offset + sizeof(CNMTHeader), &rec, sizeof(ContentRecord));
114 offset += sizeof(ContentRecord);
115 }
116
117 for (const auto& rec : meta_records) {
118 out.resize(out.size() + sizeof(MetaRecord));
119 memcpy(out.data() + offset + sizeof(CNMTHeader), &rec, sizeof(MetaRecord));
120 offset += sizeof(MetaRecord);
121 }
122
123 return out;
124}
125} // namespace FileSys
diff --git a/src/core/file_sys/nca_metadata.h b/src/core/file_sys/nca_metadata.h
new file mode 100644
index 000000000..7b0725f36
--- /dev/null
+++ b/src/core/file_sys/nca_metadata.h
@@ -0,0 +1,105 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include "core/file_sys/vfs.h"
9
10namespace FileSys {
11class CNMT;
12
13struct CNMTHeader;
14struct OptionalHeader;
15
16enum class TitleType : u8 {
17 SystemProgram = 0x01,
18 SystemDataArchive = 0x02,
19 SystemUpdate = 0x03,
20 FirmwarePackageA = 0x04,
21 FirmwarePackageB = 0x05,
22 Application = 0x80,
23 Update = 0x81,
24 AOC = 0x82,
25 DeltaTitle = 0x83,
26};
27
28enum class ContentRecordType : u8 {
29 Meta = 0,
30 Program = 1,
31 Data = 2,
32 Control = 3,
33 Manual = 4,
34 Legal = 5,
35 Patch = 6,
36};
37
38struct ContentRecord {
39 std::array<u8, 0x20> hash;
40 std::array<u8, 0x10> nca_id;
41 std::array<u8, 0x6> size;
42 ContentRecordType type;
43 INSERT_PADDING_BYTES(1);
44};
45static_assert(sizeof(ContentRecord) == 0x38, "ContentRecord has incorrect size.");
46
47constexpr ContentRecord EMPTY_META_CONTENT_RECORD{{}, {}, {}, ContentRecordType::Meta, {}};
48
49struct MetaRecord {
50 u64_le title_id;
51 u32_le title_version;
52 TitleType type;
53 u8 install_byte;
54 INSERT_PADDING_BYTES(2);
55};
56static_assert(sizeof(MetaRecord) == 0x10, "MetaRecord has incorrect size.");
57
58struct OptionalHeader {
59 u64_le title_id;
60 u64_le minimum_version;
61};
62static_assert(sizeof(OptionalHeader) == 0x10, "OptionalHeader has incorrect size.");
63
64struct CNMTHeader {
65 u64_le title_id;
66 u32_le title_version;
67 TitleType type;
68 INSERT_PADDING_BYTES(1);
69 u16_le table_offset;
70 u16_le number_content_entries;
71 u16_le number_meta_entries;
72 INSERT_PADDING_BYTES(12);
73};
74static_assert(sizeof(CNMTHeader) == 0x20, "CNMTHeader has incorrect size.");
75
76// A class representing the format used by NCA metadata files, typically named {}.cnmt.nca or
77// meta0.ncd. These describe which NCA's belong with which titles in the registered cache.
78class CNMT {
79public:
80 explicit CNMT(VirtualFile file);
81 CNMT(CNMTHeader header, OptionalHeader opt_header, std::vector<ContentRecord> content_records,
82 std::vector<MetaRecord> meta_records);
83
84 u64 GetTitleID() const;
85 u32 GetTitleVersion() const;
86 TitleType GetType() const;
87
88 const std::vector<ContentRecord>& GetContentRecords() const;
89 const std::vector<MetaRecord>& GetMetaRecords() const;
90
91 bool UnionRecords(const CNMT& other);
92 std::vector<u8> Serialize() const;
93
94private:
95 VirtualFile file;
96 std::unique_ptr<CNMTHeader> header;
97 std::unique_ptr<OptionalHeader> opt_header;
98 std::vector<ContentRecord> content_records;
99 std::vector<MetaRecord> meta_records;
100
101 // TODO(DarkLordZach): According to switchbrew, for Patch-type there is additional data
102 // after the table. This is not documented, unfortunately.
103};
104
105} // namespace FileSys