summaryrefslogtreecommitdiff
path: root/src/core/file_sys
diff options
context:
space:
mode:
authorGravatar Zach Hilman2018-07-27 23:55:23 -0400
committerGravatar Zach Hilman2018-08-01 00:16:54 -0400
commitdf5b75694f5abde94ccf05fa6c7a557b1ba9079b (patch)
tree70f0cf96b1a9834360fb1c5d5547939693ecd577 /src/core/file_sys
parentMerge pull request #871 from bunnei/audio-config (diff)
downloadyuzu-df5b75694f5abde94ccf05fa6c7a557b1ba9079b.tar.gz
yuzu-df5b75694f5abde94ccf05fa6c7a557b1ba9079b.tar.xz
yuzu-df5b75694f5abde94ccf05fa6c7a557b1ba9079b.zip
Remove files that are not used
Diffstat (limited to 'src/core/file_sys')
-rw-r--r--src/core/file_sys/card_image.cpp150
-rw-r--r--src/core/file_sys/card_image.h93
-rw-r--r--src/core/file_sys/content_archive.cpp168
-rw-r--r--src/core/file_sys/content_archive.h26
-rw-r--r--src/core/file_sys/vfs.cpp21
-rw-r--r--src/core/file_sys/vfs.h3
6 files changed, 425 insertions, 36 deletions
diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp
new file mode 100644
index 000000000..5ff09a362
--- /dev/null
+++ b/src/core/file_sys/card_image.cpp
@@ -0,0 +1,150 @@
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 <array>
6#include <string>
7#include <core/loader/loader.h>
8#include "core/file_sys/card_image.h"
9#include "core/file_sys/partition_filesystem.h"
10#include "core/file_sys/vfs_offset.h"
11
12namespace FileSys {
13
14XCI::XCI(VirtualFile file_) : file(std::move(file_)), partitions(0x4) {
15 if (file->ReadObject(&header) != sizeof(GamecardHeader)) {
16 status = Loader::ResultStatus::ErrorInvalidFormat;
17 return;
18 }
19
20 if (header.magic != Common::MakeMagic('H', 'E', 'A', 'D')) {
21 status = Loader::ResultStatus::ErrorInvalidFormat;
22 return;
23 }
24
25 PartitionFilesystem main_hfs(
26 std::make_shared<OffsetVfsFile>(file, header.hfs_size, header.hfs_offset));
27
28 if (main_hfs.GetStatus() != Loader::ResultStatus::Success) {
29 status = main_hfs.GetStatus();
30 return;
31 }
32
33 const static std::array<std::string, 0x4> partition_names = {"update", "normal", "secure",
34 "logo"};
35
36 for (XCIPartition partition :
37 {XCIPartition::Update, XCIPartition::Normal, XCIPartition::Secure, XCIPartition::Logo}) {
38 auto raw = main_hfs.GetFile(partition_names[static_cast<size_t>(partition)]);
39 if (raw != nullptr)
40 partitions[static_cast<size_t>(partition)] = std::make_shared<PartitionFilesystem>(raw);
41 }
42
43 auto result = AddNCAFromPartition(XCIPartition::Secure);
44 if (result != Loader::ResultStatus::Success) {
45 status = result;
46 return;
47 }
48 result = AddNCAFromPartition(XCIPartition::Update);
49 if (result != Loader::ResultStatus::Success) {
50 status = result;
51 return;
52 }
53
54 result = AddNCAFromPartition(XCIPartition::Normal);
55 if (result != Loader::ResultStatus::Success) {
56 status = result;
57 return;
58 }
59
60 if (GetFormatVersion() >= 0x2) {
61 result = AddNCAFromPartition(XCIPartition::Logo);
62 if (result != Loader::ResultStatus::Success) {
63 status = result;
64 return;
65 }
66 }
67
68 status = Loader::ResultStatus::Success;
69}
70
71Loader::ResultStatus XCI::GetStatus() const {
72 return status;
73}
74
75VirtualDir XCI::GetPartition(XCIPartition partition) const {
76 return partitions[static_cast<size_t>(partition)];
77}
78
79VirtualDir XCI::GetSecurePartition() const {
80 return GetPartition(XCIPartition::Secure);
81}
82
83VirtualDir XCI::GetNormalPartition() const {
84 return GetPartition(XCIPartition::Normal);
85}
86
87VirtualDir XCI::GetUpdatePartition() const {
88 return GetPartition(XCIPartition::Update);
89}
90
91VirtualDir XCI::GetLogoPartition() const {
92 return GetPartition(XCIPartition::Logo);
93}
94
95std::shared_ptr<NCA> XCI::GetNCAByType(NCAContentType type) const {
96 for (const auto& nca : ncas) {
97 if (nca->GetType() == type)
98 return nca;
99 }
100
101 return nullptr;
102}
103
104VirtualFile XCI::GetNCAFileByType(NCAContentType type) const {
105 auto nca = GetNCAByType(type);
106 if (nca != nullptr)
107 return nca->GetBaseFile();
108 return nullptr;
109}
110
111std::vector<std::shared_ptr<VfsFile>> XCI::GetFiles() const {
112 return {};
113}
114
115std::vector<std::shared_ptr<VfsDirectory>> XCI::GetSubdirectories() const {
116 return std::vector<std::shared_ptr<VfsDirectory>>();
117}
118
119std::string XCI::GetName() const {
120 return file->GetName();
121}
122
123std::shared_ptr<VfsDirectory> XCI::GetParentDirectory() const {
124 return file->GetContainingDirectory();
125}
126
127bool XCI::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
128 return false;
129}
130
131Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) {
132 if (partitions[static_cast<size_t>(part)] == nullptr) {
133 return Loader::ResultStatus::ErrorInvalidFormat;
134 }
135
136 for (VirtualFile file : partitions[static_cast<size_t>(part)]->GetFiles()) {
137 if (file->GetExtension() != "nca")
138 continue;
139 auto nca = std::make_shared<NCA>(file);
140 if (nca->GetStatus() == Loader::ResultStatus::Success)
141 ncas.push_back(std::move(nca));
142 }
143
144 return Loader::ResultStatus::Success;
145}
146
147u8 XCI::GetFormatVersion() const {
148 return GetLogoPartition() == nullptr ? 0x1 : 0x2;
149}
150} // namespace FileSys
diff --git a/src/core/file_sys/card_image.h b/src/core/file_sys/card_image.h
new file mode 100644
index 000000000..b765c8bc1
--- /dev/null
+++ b/src/core/file_sys/card_image.h
@@ -0,0 +1,93 @@
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 <vector>
8#include "common/swap.h"
9#include "core/file_sys/content_archive.h"
10#include "core/file_sys/vfs.h"
11
12namespace FileSys {
13
14enum class GamecardSize : u8 {
15 S_1GB = 0xFA,
16 S_2GB = 0xF8,
17 S_4GB = 0xF0,
18 S_8GB = 0xE0,
19 S_16GB = 0xE1,
20 S_32GB = 0xE2,
21};
22
23struct GamecardInfo {
24 std::array<u8, 0x70> data;
25};
26static_assert(sizeof(GamecardInfo) == 0x70, "GamecardInfo has incorrect size.");
27
28struct GamecardHeader {
29 std::array<u8, 0x100> signature;
30 u32_le magic;
31 u32_le secure_area_start;
32 u32_le backup_area_start;
33 u8 kek_index;
34 GamecardSize size;
35 u8 header_version;
36 u8 flags;
37 u64_le package_id;
38 u64_le valid_data_end;
39 u128 info_iv;
40 u64_le hfs_offset;
41 u64_le hfs_size;
42 std::array<u8, 0x20> hfs_header_hash;
43 std::array<u8, 0x20> initial_data_hash;
44 u32_le secure_mode_flag;
45 u32_le title_key_flag;
46 u32_le key_flag;
47 u32_le normal_area_end;
48 GamecardInfo info;
49};
50static_assert(sizeof(GamecardHeader) == 0x200, "GamecardHeader has incorrect size.");
51
52enum class XCIPartition : u8 { Update, Normal, Secure, Logo };
53
54class XCI : public ReadOnlyVfsDirectory {
55public:
56 explicit XCI(VirtualFile file);
57
58 Loader::ResultStatus GetStatus() const;
59
60 u8 GetFormatVersion() const;
61
62 VirtualDir GetPartition(XCIPartition partition) const;
63 VirtualDir GetSecurePartition() const;
64 VirtualDir GetNormalPartition() const;
65 VirtualDir GetUpdatePartition() const;
66 VirtualDir GetLogoPartition() const;
67
68 std::shared_ptr<NCA> GetNCAByType(NCAContentType type) const;
69 VirtualFile GetNCAFileByType(NCAContentType type) const;
70
71 std::vector<std::shared_ptr<VfsFile>> GetFiles() const override;
72
73 std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override;
74
75 std::string GetName() const override;
76
77 std::shared_ptr<VfsDirectory> GetParentDirectory() const override;
78
79protected:
80 bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
81
82private:
83 Loader::ResultStatus AddNCAFromPartition(XCIPartition part);
84
85 VirtualFile file;
86 GamecardHeader header{};
87
88 Loader::ResultStatus status;
89
90 std::vector<VirtualDir> partitions;
91 std::vector<std::shared_ptr<NCA>> ncas;
92};
93} // namespace FileSys
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp
index 61cb0bbe3..add01974e 100644
--- a/src/core/file_sys/content_archive.cpp
+++ b/src/core/file_sys/content_archive.cpp
@@ -6,9 +6,12 @@
6#include <utility> 6#include <utility>
7 7
8#include "common/logging/log.h" 8#include "common/logging/log.h"
9#include "core/crypto/aes_util.h"
10#include "core/crypto/ctr_encryption_layer.h"
9#include "core/file_sys/content_archive.h" 11#include "core/file_sys/content_archive.h"
10#include "core/file_sys/vfs_offset.h" 12#include "core/file_sys/vfs_offset.h"
11#include "core/loader/loader.h" 13#include "core/loader/loader.h"
14#include "mbedtls/cipher.h"
12#include "romfs.h" 15#include "romfs.h"
13 16
14namespace FileSys { 17namespace FileSys {
@@ -29,11 +32,19 @@ enum class NCASectionFilesystemType : u8 {
29struct NCASectionHeaderBlock { 32struct NCASectionHeaderBlock {
30 INSERT_PADDING_BYTES(3); 33 INSERT_PADDING_BYTES(3);
31 NCASectionFilesystemType filesystem_type; 34 NCASectionFilesystemType filesystem_type;
32 u8 crypto_type; 35 NCASectionCryptoType crypto_type;
33 INSERT_PADDING_BYTES(3); 36 INSERT_PADDING_BYTES(3);
34}; 37};
35static_assert(sizeof(NCASectionHeaderBlock) == 0x8, "NCASectionHeaderBlock has incorrect size."); 38static_assert(sizeof(NCASectionHeaderBlock) == 0x8, "NCASectionHeaderBlock has incorrect size.");
36 39
40struct NCASectionRaw {
41 NCASectionHeaderBlock header;
42 std::array<u8, 0x138> block_data;
43 std::array<u8, 0x8> section_ctr;
44 INSERT_PADDING_BYTES(0xB8);
45};
46static_assert(sizeof(NCASectionRaw) == 0x200, "NCASectionRaw has incorrect size.");
47
37struct PFS0Superblock { 48struct PFS0Superblock {
38 NCASectionHeaderBlock header_block; 49 NCASectionHeaderBlock header_block;
39 std::array<u8, 0x20> hash; 50 std::array<u8, 0x20> hash;
@@ -43,62 +54,155 @@ struct PFS0Superblock {
43 u64_le hash_table_size; 54 u64_le hash_table_size;
44 u64_le pfs0_header_offset; 55 u64_le pfs0_header_offset;
45 u64_le pfs0_size; 56 u64_le pfs0_size;
46 INSERT_PADDING_BYTES(432); 57 INSERT_PADDING_BYTES(0x1B0);
47}; 58};
48static_assert(sizeof(PFS0Superblock) == 0x200, "PFS0Superblock has incorrect size."); 59static_assert(sizeof(PFS0Superblock) == 0x200, "PFS0Superblock has incorrect size.");
49 60
50struct RomFSSuperblock { 61struct RomFSSuperblock {
51 NCASectionHeaderBlock header_block; 62 NCASectionHeaderBlock header_block;
52 IVFCHeader ivfc; 63 IVFCHeader ivfc;
64 INSERT_PADDING_BYTES(0x118);
65};
66static_assert(sizeof(RomFSSuperblock) == 0x200, "RomFSSuperblock has incorrect size.");
67
68union NCASectionHeader {
69 NCASectionRaw raw;
70 PFS0Superblock pfs0;
71 RomFSSuperblock romfs;
53}; 72};
54static_assert(sizeof(RomFSSuperblock) == 0xE8, "RomFSSuperblock has incorrect size."); 73static_assert(sizeof(NCASectionHeader) == 0x200, "NCASectionHeader has incorrect size.");
74
75bool IsValidNCA(const NCAHeader& header) {
76 // TODO(DarkLordZach): Add NCA2/NCA0 support.
77 return header.magic == Common::MakeMagic('N', 'C', 'A', '3');
78}
79
80Crypto::Key128 NCA::GetKeyAreaKey(NCASectionCryptoType type) {
81 u8 master_key_id = header.crypto_type;
82 if (header.crypto_type_2 > master_key_id)
83 master_key_id = header.crypto_type_2;
84 if (master_key_id > 0)
85 --master_key_id;
86
87 std::vector<u8> key_area(header.key_area.begin(), header.key_area.end());
88 if (!Crypto::keys.ValidateKey(Crypto::S128KeyType::KEY_AREA, master_key_id, header.key_index)) {
89 status = Loader::ResultStatus::ErrorEncrypted;
90 return {};
91 }
92 Crypto::AESCipher<Crypto::Key128> cipher(
93 Crypto::keys.GetKey(Crypto::S128KeyType::KEY_AREA, master_key_id, header.key_index),
94 Crypto::Mode::ECB);
95 cipher.Transcode(key_area.data(), key_area.size(), key_area.data(), Crypto::Op::DECRYPT);
96
97 Crypto::Key128 out;
98 if (type == NCASectionCryptoType::XTS)
99 std::copy(key_area.begin(), key_area.begin() + 0x10, out.begin());
100 else if (type == NCASectionCryptoType::CTR)
101 std::copy(key_area.begin() + 0x20, key_area.begin() + 0x30, out.begin());
102 else
103 LOG_CRITICAL(Crypto, "Called GetKeyAreaKey on invalid NCASectionCryptoType type={:02X}",
104 static_cast<u8>(type));
105
106 u128 out_128 = *reinterpret_cast<u128*>(&out);
107 LOG_DEBUG(Crypto, "called with crypto_rev={:02X}, kak_index={:02X}, key={:016X}{:016X}",
108 master_key_id, header.key_index, out_128[1], out_128[0]);
109
110 return out;
111}
112
113VirtualFile NCA::Decrypt(NCASectionHeader header, VirtualFile in, u64 starting_offset) {
114 if (!encrypted)
115 return in;
116
117 switch (header.raw.header.crypto_type) {
118 case NCASectionCryptoType::NONE:
119 LOG_DEBUG(Crypto, "called with mode=NONE");
120 return in;
121 case NCASectionCryptoType::CTR:
122 LOG_DEBUG(Crypto, "called with mode=CTR, starting_offset={:016X}", starting_offset);
123 {
124 auto out = std::make_shared<Crypto::CTREncryptionLayer>(
125 std::move(in), GetKeyAreaKey(NCASectionCryptoType::CTR), starting_offset);
126 std::vector<u8> iv(16, 0);
127 for (u8 i = 0; i < 8; ++i)
128 iv[i] = header.raw.section_ctr[0x8 - i - 1];
129 out->SetIV(iv);
130 return out;
131 }
132 case NCASectionCryptoType::XTS:
133 // TODO(DarkLordZach): Implement XTSEncryptionLayer and title key encryption.
134 default:
135 LOG_ERROR(Crypto, "called with unhandled crypto type={:02X}",
136 static_cast<u8>(header.raw.header.crypto_type));
137 return nullptr;
138 }
139}
55 140
56NCA::NCA(VirtualFile file_) : file(std::move(file_)) { 141NCA::NCA(VirtualFile file_) : file(std::move(file_)) {
57 if (sizeof(NCAHeader) != file->ReadObject(&header)) 142 if (sizeof(NCAHeader) != file->ReadObject(&header))
58 LOG_CRITICAL(Loader, "File reader errored out during header read."); 143 LOG_ERROR(Loader, "File reader errored out during header read.");
144
145 encrypted = false;
59 146
60 if (!IsValidNCA(header)) { 147 if (!IsValidNCA(header)) {
61 status = Loader::ResultStatus::ErrorInvalidFormat; 148 NCAHeader dec_header{};
62 return; 149 if (!Crypto::keys.ValidateKey(Crypto::S256KeyType::HEADER)) {
150 status = Loader::ResultStatus::ErrorEncrypted;
151 return;
152 }
153 Crypto::AESCipher<Crypto::Key256> cipher(Crypto::keys.GetKey(Crypto::S256KeyType::HEADER),
154 Crypto::Mode::XTS);
155 cipher.XTSTranscode(&header, sizeof(NCAHeader), &dec_header, 0, 0x200, Crypto::Op::DECRYPT);
156 if (IsValidNCA(dec_header)) {
157 header = dec_header;
158 encrypted = true;
159 } else {
160 status = Loader::ResultStatus::ErrorInvalidFormat;
161 return;
162 }
63 } 163 }
64 164
65 std::ptrdiff_t number_sections = 165 const std::ptrdiff_t number_sections =
66 std::count_if(std::begin(header.section_tables), std::end(header.section_tables), 166 std::count_if(std::begin(header.section_tables), std::end(header.section_tables),
67 [](NCASectionTableEntry entry) { return entry.media_offset > 0; }); 167 [](NCASectionTableEntry entry) { return entry.media_offset > 0; });
68 168
169 std::vector<NCASectionHeader> sections(number_sections);
170 const auto length_sections = SECTION_HEADER_SIZE * number_sections;
171
172 if (encrypted) {
173 auto raw = file->ReadBytes(length_sections, SECTION_HEADER_OFFSET);
174 if (!Crypto::keys.ValidateKey(Crypto::S256KeyType::HEADER)) {
175 status = Loader::ResultStatus::ErrorEncrypted;
176 return;
177 }
178 Crypto::AESCipher<Crypto::Key256> cipher(Crypto::keys.GetKey(Crypto::S256KeyType::HEADER),
179 Crypto::Mode::XTS);
180 cipher.XTSTranscode(raw.data(), length_sections, sections.data(), 2, SECTION_HEADER_SIZE,
181 Crypto::Op::DECRYPT);
182 } else {
183 file->ReadBytes(sections.data(), length_sections, SECTION_HEADER_OFFSET);
184 }
185
69 for (std::ptrdiff_t i = 0; i < number_sections; ++i) { 186 for (std::ptrdiff_t i = 0; i < number_sections; ++i) {
70 // Seek to beginning of this section. 187 auto section = sections[i];
71 NCASectionHeaderBlock block{};
72 if (sizeof(NCASectionHeaderBlock) !=
73 file->ReadObject(&block, SECTION_HEADER_OFFSET + i * SECTION_HEADER_SIZE))
74 LOG_CRITICAL(Loader, "File reader errored out during header read.");
75
76 if (block.filesystem_type == NCASectionFilesystemType::ROMFS) {
77 RomFSSuperblock sb{};
78 if (sizeof(RomFSSuperblock) !=
79 file->ReadObject(&sb, SECTION_HEADER_OFFSET + i * SECTION_HEADER_SIZE))
80 LOG_CRITICAL(Loader, "File reader errored out during header read.");
81 188
189 if (section.raw.header.filesystem_type == NCASectionFilesystemType::ROMFS) {
82 const size_t romfs_offset = 190 const size_t romfs_offset =
83 header.section_tables[i].media_offset * MEDIA_OFFSET_MULTIPLIER + 191 header.section_tables[i].media_offset * MEDIA_OFFSET_MULTIPLIER +
84 sb.ivfc.levels[IVFC_MAX_LEVEL - 1].offset; 192 section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset;
85 const size_t romfs_size = sb.ivfc.levels[IVFC_MAX_LEVEL - 1].size; 193 const size_t romfs_size = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].size;
86 files.emplace_back(std::make_shared<OffsetVfsFile>(file, romfs_size, romfs_offset)); 194 files.emplace_back(
195 Decrypt(section, std::make_shared<OffsetVfsFile>(file, romfs_size, romfs_offset),
196 romfs_offset));
87 romfs = files.back(); 197 romfs = files.back();
88 } else if (block.filesystem_type == NCASectionFilesystemType::PFS0) { 198 } else if (section.raw.header.filesystem_type == NCASectionFilesystemType::PFS0) {
89 PFS0Superblock sb{};
90 // Seek back to beginning of this section.
91 if (sizeof(PFS0Superblock) !=
92 file->ReadObject(&sb, SECTION_HEADER_OFFSET + i * SECTION_HEADER_SIZE))
93 LOG_CRITICAL(Loader, "File reader errored out during header read.");
94
95 u64 offset = (static_cast<u64>(header.section_tables[i].media_offset) * 199 u64 offset = (static_cast<u64>(header.section_tables[i].media_offset) *
96 MEDIA_OFFSET_MULTIPLIER) + 200 MEDIA_OFFSET_MULTIPLIER) +
97 sb.pfs0_header_offset; 201 section.pfs0.pfs0_header_offset;
98 u64 size = MEDIA_OFFSET_MULTIPLIER * (header.section_tables[i].media_end_offset - 202 u64 size = MEDIA_OFFSET_MULTIPLIER * (header.section_tables[i].media_end_offset -
99 header.section_tables[i].media_offset); 203 header.section_tables[i].media_offset);
100 auto npfs = std::make_shared<PartitionFilesystem>( 204 auto npfs = std::make_shared<PartitionFilesystem>(
101 std::make_shared<OffsetVfsFile>(file, size, offset)); 205 Decrypt(section, std::make_shared<OffsetVfsFile>(file, size, offset), offset));
102 206
103 if (npfs->GetStatus() == Loader::ResultStatus::Success) { 207 if (npfs->GetStatus() == Loader::ResultStatus::Success) {
104 dirs.emplace_back(npfs); 208 dirs.emplace_back(npfs);
@@ -153,6 +257,10 @@ VirtualDir NCA::GetExeFS() const {
153 return exefs; 257 return exefs;
154} 258}
155 259
260VirtualFile NCA::GetBaseFile() const {
261 return file;
262}
263
156bool NCA::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { 264bool NCA::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
157 return false; 265 return false;
158} 266}
diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h
index 0b8b9db61..1e8d9c8ae 100644
--- a/src/core/file_sys/content_archive.h
+++ b/src/core/file_sys/content_archive.h
@@ -12,10 +12,10 @@
12#include "common/common_funcs.h" 12#include "common/common_funcs.h"
13#include "common/common_types.h" 13#include "common/common_types.h"
14#include "common/swap.h" 14#include "common/swap.h"
15#include "core/crypto/key_manager.h"
15#include "core/file_sys/partition_filesystem.h" 16#include "core/file_sys/partition_filesystem.h"
16 17
17namespace FileSys { 18namespace FileSys {
18
19enum class NCAContentType : u8 { 19enum class NCAContentType : u8 {
20 Program = 0, 20 Program = 0,
21 Meta = 1, 21 Meta = 1,
@@ -24,6 +24,13 @@ enum class NCAContentType : u8 {
24 Data = 4, 24 Data = 4,
25}; 25};
26 26
27enum class NCASectionCryptoType : u8 {
28 NONE = 1,
29 XTS = 2,
30 CTR = 3,
31 BKTR = 4,
32};
33
27struct NCASectionTableEntry { 34struct NCASectionTableEntry {
28 u32_le media_offset; 35 u32_le media_offset;
29 u32_le media_end_offset; 36 u32_le media_end_offset;
@@ -48,20 +55,19 @@ struct NCAHeader {
48 std::array<u8, 0x10> rights_id; 55 std::array<u8, 0x10> rights_id;
49 std::array<NCASectionTableEntry, 0x4> section_tables; 56 std::array<NCASectionTableEntry, 0x4> section_tables;
50 std::array<std::array<u8, 0x20>, 0x4> hash_tables; 57 std::array<std::array<u8, 0x20>, 0x4> hash_tables;
51 std::array<std::array<u8, 0x10>, 0x4> key_area; 58 std::array<u8, 0x40> key_area;
52 INSERT_PADDING_BYTES(0xC0); 59 INSERT_PADDING_BYTES(0xC0);
53}; 60};
54static_assert(sizeof(NCAHeader) == 0x400, "NCAHeader has incorrect size."); 61static_assert(sizeof(NCAHeader) == 0x400, "NCAHeader has incorrect size.");
55 62
63union NCASectionHeader;
64
56inline bool IsDirectoryExeFS(const std::shared_ptr<VfsDirectory>& pfs) { 65inline bool IsDirectoryExeFS(const std::shared_ptr<VfsDirectory>& pfs) {
57 // According to switchbrew, an exefs must only contain these two files: 66 // According to switchbrew, an exefs must only contain these two files:
58 return pfs->GetFile("main") != nullptr && pfs->GetFile("main.npdm") != nullptr; 67 return pfs->GetFile("main") != nullptr && pfs->GetFile("main.npdm") != nullptr;
59} 68}
60 69
61inline bool IsValidNCA(const NCAHeader& header) { 70bool IsValidNCA(const NCAHeader& header);
62 return header.magic == Common::MakeMagic('N', 'C', 'A', '2') ||
63 header.magic == Common::MakeMagic('N', 'C', 'A', '3');
64}
65 71
66// An implementation of VfsDirectory that represents a Nintendo Content Archive (NCA) conatiner. 72// An implementation of VfsDirectory that represents a Nintendo Content Archive (NCA) conatiner.
67// After construction, use GetStatus to determine if the file is valid and ready to be used. 73// After construction, use GetStatus to determine if the file is valid and ready to be used.
@@ -81,6 +87,8 @@ public:
81 VirtualFile GetRomFS() const; 87 VirtualFile GetRomFS() const;
82 VirtualDir GetExeFS() const; 88 VirtualDir GetExeFS() const;
83 89
90 VirtualFile GetBaseFile() const;
91
84protected: 92protected:
85 bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; 93 bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
86 94
@@ -95,6 +103,12 @@ private:
95 NCAHeader header{}; 103 NCAHeader header{};
96 104
97 Loader::ResultStatus status{}; 105 Loader::ResultStatus status{};
106
107 bool encrypted;
108
109 Crypto::Key128 GetKeyAreaKey(NCASectionCryptoType type);
110
111 VirtualFile Decrypt(NCASectionHeader header, VirtualFile in, size_t starting_offset);
98}; 112};
99 113
100} // namespace FileSys 114} // namespace FileSys
diff --git a/src/core/file_sys/vfs.cpp b/src/core/file_sys/vfs.cpp
index 84a6a7397..57d0aeb85 100644
--- a/src/core/file_sys/vfs.cpp
+++ b/src/core/file_sys/vfs.cpp
@@ -285,6 +285,27 @@ bool ReadOnlyVfsDirectory::Rename(std::string_view name) {
285 return false; 285 return false;
286} 286}
287 287
288bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, size_t block_size) {
289 if (file1->GetSize() != file2->GetSize())
290 return false;
291
292 std::vector<u8> f1_v(block_size);
293 std::vector<u8> f2_v(block_size);
294 for (size_t i = 0; i < file1->GetSize(); i += block_size) {
295 auto f1_vs = file1->Read(f1_v.data(), block_size, i);
296 auto f2_vs = file2->Read(f2_v.data(), block_size, i);
297
298 if (f1_vs != f2_vs)
299 return false;
300 for (size_t j = 0; j < f1_vs; ++j) {
301 if (f1_v[j] != f2_v[j])
302 return false;
303 }
304 }
305
306 return true;
307}
308
288bool VfsRawCopy(VirtualFile src, VirtualFile dest) { 309bool VfsRawCopy(VirtualFile src, VirtualFile dest) {
289 if (src == nullptr || dest == nullptr) 310 if (src == nullptr || dest == nullptr)
290 return false; 311 return false;
diff --git a/src/core/file_sys/vfs.h b/src/core/file_sys/vfs.h
index cf871edd6..fab9e2b45 100644
--- a/src/core/file_sys/vfs.h
+++ b/src/core/file_sys/vfs.h
@@ -245,6 +245,9 @@ struct ReadOnlyVfsDirectory : public VfsDirectory {
245 bool Rename(std::string_view name) override; 245 bool Rename(std::string_view name) override;
246}; 246};
247 247
248// Compare the two files, byte-for-byte, in increments specificed by block_size
249bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, size_t block_size = 0x200);
250
248// A method that copies the raw data between two different implementations of VirtualFile. If you 251// A method that copies the raw data between two different implementations of VirtualFile. If you
249// are using the same implementation, it is probably better to use the Copy method in the parent 252// are using the same implementation, it is probably better to use the Copy method in the parent
250// directory of src/dest. 253// directory of src/dest.