summaryrefslogtreecommitdiffstats
path: root/src/core/file_sys/registered_cache.h
blob: fe2cdc3d991d5184008fe5f7d18bfe744c960e5d (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.

#pragma once

#include <array>
#include <functional>
#include <map>
#include <memory>
#include <string>
#include <vector>
#include <boost/container/flat_map.hpp>
#include "common/common_types.h"
#include "core/file_sys/vfs.h"

namespace FileSys {
class CNMT;
class NCA;
class XCI;

enum class ContentRecordType : u8;
enum class TitleType : u8;

struct ContentRecord;

using NcaID = std::array<u8, 0x10>;
using RegisteredCacheParsingFunction = std::function<VirtualFile(const VirtualFile&, const NcaID&)>;
using VfsCopyFunction = std::function<bool(VirtualFile, VirtualFile)>;

enum class InstallResult {
    Success,
    ErrorAlreadyExists,
    ErrorCopyFailed,
    ErrorMetaFailed,
};

struct RegisteredCacheEntry {
    u64 title_id;
    ContentRecordType type;

    std::string DebugInfo() const;
};

// boost flat_map requires operator< for O(log(n)) lookups.
bool operator<(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs);

/*
 * A class that catalogues NCAs in the registered directory structure.
 * Nintendo's registered format follows this structure:
 *
 * Root
 *   | 000000XX <- XX is the ____ two digits of the NcaID
 *       | <hash>.nca <- hash is the NcaID (first half of SHA256 over entire file) (folder)
 *         | 00
 *         | 01 <- Actual content split along 4GB boundaries. (optional)
 *
 * (This impl also supports substituting the nca dir for an nca file, as that's more convenient when
 * 4GB splitting can be ignored.)
 */
class RegisteredCache {
public:
    // Parsing function defines the conversion from raw file to NCA. If there are other steps
    // besides creating the NCA from the file (e.g. NAX0 on SD Card), that should go in a custom
    // parsing function.
    explicit RegisteredCache(VirtualDir dir,
                             RegisteredCacheParsingFunction parsing_function =
                                 [](const VirtualFile& file, const NcaID& id) { return file; });
    ~RegisteredCache();

    void Refresh();

    bool HasEntry(u64 title_id, ContentRecordType type) const;
    bool HasEntry(RegisteredCacheEntry entry) const;

    VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const;
    VirtualFile GetEntryUnparsed(RegisteredCacheEntry entry) const;

    VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const;
    VirtualFile GetEntryRaw(RegisteredCacheEntry entry) const;

    std::shared_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const;
    std::shared_ptr<NCA> GetEntry(RegisteredCacheEntry entry) const;

    std::vector<RegisteredCacheEntry> ListEntries() const;
    // If a parameter is not boost::none, it will be filtered for from all entries.
    std::vector<RegisteredCacheEntry> ListEntriesFilter(
        boost::optional<TitleType> title_type = boost::none,
        boost::optional<ContentRecordType> record_type = boost::none,
        boost::optional<u64> title_id = boost::none) const;

    // Raw copies all the ncas from the xci to the csache. Does some quick checks to make sure there
    // is a meta NCA and all of them are accessible.
    InstallResult InstallEntry(std::shared_ptr<XCI> xci, bool overwrite_if_exists = false,
                               const VfsCopyFunction& copy = &VfsRawCopy);

    // Due to the fact that we must use Meta-type NCAs to determine the existance of files, this
    // poses quite a challenge. Instead of creating a new meta NCA for this file, yuzu will create a
    // dir inside the NAND called 'yuzu_meta' and store the raw CNMT there.
    // TODO(DarkLordZach): Author real meta-type NCAs and install those.
    InstallResult InstallEntry(std::shared_ptr<NCA> nca, TitleType type,
                               bool overwrite_if_exists = false,
                               const VfsCopyFunction& copy = &VfsRawCopy);

private:
    template <typename T>
    void IterateAllMetadata(std::vector<T>& out,
                            std::function<T(const CNMT&, const ContentRecord&)> proc,
                            std::function<bool(const CNMT&, const ContentRecord&)> filter) const;
    std::vector<NcaID> AccumulateFiles() const;
    void ProcessFiles(const std::vector<NcaID>& ids);
    void AccumulateYuzuMeta();
    boost::optional<NcaID> GetNcaIDFromMetadata(u64 title_id, ContentRecordType type) const;
    VirtualFile GetFileAtID(NcaID id) const;
    VirtualFile OpenFileOrDirectoryConcat(const VirtualDir& dir, std::string_view path) const;
    InstallResult RawInstallNCA(std::shared_ptr<NCA> nca, const VfsCopyFunction& copy,
                                bool overwrite_if_exists,
                                boost::optional<NcaID> override_id = boost::none);
    bool RawInstallYuzuMeta(const CNMT& cnmt);

    VirtualDir dir;
    RegisteredCacheParsingFunction parser;
    // maps tid -> NcaID of meta
    boost::container::flat_map<u64, NcaID> meta_id;
    // maps tid -> meta
    boost::container::flat_map<u64, CNMT> meta;
    // maps tid -> meta for CNMT in yuzu_meta
    boost::container::flat_map<u64, CNMT> yuzu_meta;
};

} // namespace FileSys