summaryrefslogtreecommitdiffstats
path: root/src/core/file_sys/nca_metadata.cpp
blob: 4492444449d285d7c8789913152a8e4dbdb55a25 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.

#include <cstring>
#include "common/common_funcs.h"
#include "common/logging/log.h"
#include "common/swap.h"
#include "content_archive.h"
#include "core/file_sys/nca_metadata.h"

namespace FileSys {

bool operator>=(TitleType lhs, TitleType rhs) {
    return static_cast<size_t>(lhs) >= static_cast<size_t>(rhs);
}

bool operator<=(TitleType lhs, TitleType rhs) {
    return static_cast<size_t>(lhs) <= static_cast<size_t>(rhs);
}

CNMT::CNMT(VirtualFile file) {
    if (file->ReadObject(&header) != sizeof(CNMTHeader))
        return;

    // If type is {Application, Update, AOC} has opt-header.
    if (header.type >= TitleType::Application && header.type <= TitleType::AOC) {
        if (file->ReadObject(&opt_header, sizeof(CNMTHeader)) != sizeof(OptionalHeader)) {
            LOG_WARNING(Loader, "Failed to read optional header.");
        }
    }

    for (u16 i = 0; i < header.number_content_entries; ++i) {
        auto& next = content_records.emplace_back(ContentRecord{});
        if (file->ReadObject(&next, sizeof(CNMTHeader) + i * sizeof(ContentRecord) +
                                        header.table_offset) != sizeof(ContentRecord)) {
            content_records.erase(content_records.end() - 1);
        }
    }

    for (u16 i = 0; i < header.number_meta_entries; ++i) {
        auto& next = meta_records.emplace_back(MetaRecord{});
        if (file->ReadObject(&next, sizeof(CNMTHeader) + i * sizeof(MetaRecord) +
                                        header.table_offset) != sizeof(MetaRecord)) {
            meta_records.erase(meta_records.end() - 1);
        }
    }
}

CNMT::CNMT(CNMTHeader header, OptionalHeader opt_header, std::vector<ContentRecord> content_records,
           std::vector<MetaRecord> meta_records)
    : header(std::move(header)), opt_header(std::move(opt_header)),
      content_records(std::move(content_records)), meta_records(std::move(meta_records)) {}

u64 CNMT::GetTitleID() const {
    return header.title_id;
}

u32 CNMT::GetTitleVersion() const {
    return header.title_version;
}

TitleType CNMT::GetType() const {
    return header.type;
}

const std::vector<ContentRecord>& CNMT::GetContentRecords() const {
    return content_records;
}

const std::vector<MetaRecord>& CNMT::GetMetaRecords() const {
    return meta_records;
}

bool CNMT::UnionRecords(const CNMT& other) {
    bool change = false;
    for (const auto& rec : other.content_records) {
        const auto iter = std::find_if(content_records.begin(), content_records.end(),
                                       [&rec](const ContentRecord& r) {
                                           return r.nca_id == rec.nca_id && r.type == rec.type;
                                       });
        if (iter == content_records.end()) {
            content_records.emplace_back(rec);
            ++header.number_content_entries;
            change = true;
        }
    }
    for (const auto& rec : other.meta_records) {
        const auto iter =
            std::find_if(meta_records.begin(), meta_records.end(), [&rec](const MetaRecord& r) {
                return r.title_id == rec.title_id && r.title_version == rec.title_version &&
                       r.type == rec.type;
            });
        if (iter == meta_records.end()) {
            meta_records.emplace_back(rec);
            ++header.number_meta_entries;
            change = true;
        }
    }
    return change;
}

std::vector<u8> CNMT::Serialize() const {
    const bool has_opt_header =
        header.type >= TitleType::Application && header.type <= TitleType::AOC;
    const auto dead_zone = header.table_offset + sizeof(CNMTHeader);
    std::vector<u8> out(
        std::max(sizeof(CNMTHeader) + (has_opt_header ? sizeof(OptionalHeader) : 0), dead_zone) +
        content_records.size() * sizeof(ContentRecord) + meta_records.size() * sizeof(MetaRecord));
    memcpy(out.data(), &header, sizeof(CNMTHeader));

    // Optional Header
    if (has_opt_header) {
        memcpy(out.data() + sizeof(CNMTHeader), &opt_header, sizeof(OptionalHeader));
    }

    auto offset = header.table_offset;

    for (const auto& rec : content_records) {
        memcpy(out.data() + offset + sizeof(CNMTHeader), &rec, sizeof(ContentRecord));
        offset += sizeof(ContentRecord);
    }

    for (const auto& rec : meta_records) {
        memcpy(out.data() + offset + sizeof(CNMTHeader), &rec, sizeof(MetaRecord));
        offset += sizeof(MetaRecord);
    }

    return out;
}
} // namespace FileSys