summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/common/alignment.h12
-rw-r--r--src/core/core.cpp3
-rw-r--r--src/core/crypto/partition_data_manager.cpp3
-rw-r--r--src/core/file_sys/card_image.cpp10
-rw-r--r--src/core/file_sys/card_image.h2
-rw-r--r--src/core/file_sys/content_archive.cpp531
-rw-r--r--src/core/file_sys/content_archive.h19
-rw-r--r--src/core/hle/kernel/svc.cpp24
-rw-r--r--src/core/hle/service/am/am.cpp36
-rw-r--r--src/core/hle/service/am/am.h4
-rw-r--r--src/core/hle/service/mm/mm_u.cpp50
-rw-r--r--src/core/loader/xci.cpp3
-rw-r--r--src/video_core/engines/maxwell_3d.h5
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp6
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.h3
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp1
16 files changed, 421 insertions, 291 deletions
diff --git a/src/common/alignment.h b/src/common/alignment.h
index 225770fab..d94a2291f 100644
--- a/src/common/alignment.h
+++ b/src/common/alignment.h
@@ -19,4 +19,16 @@ constexpr T AlignDown(T value, std::size_t size) {
19 return static_cast<T>(value - value % size); 19 return static_cast<T>(value - value % size);
20} 20}
21 21
22template <typename T>
23constexpr bool Is4KBAligned(T value) {
24 static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
25 return (value & 0xFFF) == 0;
26}
27
28template <typename T>
29constexpr bool IsWordAligned(T value) {
30 static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
31 return (value & 0b11) == 0;
32}
33
22} // namespace Common 34} // namespace Common
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 3c57a62ec..7cb86ed92 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -375,8 +375,7 @@ const Kernel::Process* System::CurrentProcess() const {
375} 375}
376 376
377ARM_Interface& System::ArmInterface(std::size_t core_index) { 377ARM_Interface& System::ArmInterface(std::size_t core_index) {
378 ASSERT(core_index < NUM_CPU_CORES); 378 return CpuCore(core_index).ArmInterface();
379 return impl->cpu_cores[core_index]->ArmInterface();
380} 379}
381 380
382Cpu& System::CpuCore(std::size_t core_index) { 381Cpu& System::CpuCore(std::size_t core_index) {
diff --git a/src/core/crypto/partition_data_manager.cpp b/src/core/crypto/partition_data_manager.cpp
index 25cee1f3a..ed0775444 100644
--- a/src/core/crypto/partition_data_manager.cpp
+++ b/src/core/crypto/partition_data_manager.cpp
@@ -516,7 +516,8 @@ void PartitionDataManager::DecryptPackage2(const std::array<Key128, 0x20>& packa
516 out.insert(out.end(), rodata.begin(), rodata.end()); 516 out.insert(out.end(), rodata.begin(), rodata.end());
517 out.insert(out.end(), data.begin(), data.end()); 517 out.insert(out.end(), data.begin(), data.end());
518 518
519 offset += sizeof(KIPHeader) + out.size(); 519 offset += sizeof(KIPHeader) + kip.sections[0].size_compressed +
520 kip.sections[1].size_compressed + kip.sections[2].size_compressed;
520 521
521 if (name == "FS") 522 if (name == "FS")
522 package2_fs[static_cast<size_t>(type)] = std::move(out); 523 package2_fs[static_cast<size_t>(type)] = std::move(out);
diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp
index 8f5142a07..ecdd7505b 100644
--- a/src/core/file_sys/card_image.cpp
+++ b/src/core/file_sys/card_image.cpp
@@ -122,14 +122,16 @@ u64 XCI::GetProgramTitleID() const {
122 return secure_partition->GetProgramTitleID(); 122 return secure_partition->GetProgramTitleID();
123} 123}
124 124
125std::shared_ptr<NCA> XCI::GetProgramNCA() const { 125bool XCI::HasProgramNCA() const {
126 return program; 126 return program != nullptr;
127} 127}
128 128
129VirtualFile XCI::GetProgramNCAFile() const { 129VirtualFile XCI::GetProgramNCAFile() const {
130 if (GetProgramNCA() == nullptr) 130 if (!HasProgramNCA()) {
131 return nullptr; 131 return nullptr;
132 return GetProgramNCA()->GetBaseFile(); 132 }
133
134 return program->GetBaseFile();
133} 135}
134 136
135const std::vector<std::shared_ptr<NCA>>& XCI::GetNCAs() const { 137const std::vector<std::shared_ptr<NCA>>& XCI::GetNCAs() const {
diff --git a/src/core/file_sys/card_image.h b/src/core/file_sys/card_image.h
index ce514dfa0..48cbef666 100644
--- a/src/core/file_sys/card_image.h
+++ b/src/core/file_sys/card_image.h
@@ -80,7 +80,7 @@ public:
80 80
81 u64 GetProgramTitleID() const; 81 u64 GetProgramTitleID() const;
82 82
83 std::shared_ptr<NCA> GetProgramNCA() const; 83 bool HasProgramNCA() const;
84 VirtualFile GetProgramNCAFile() const; 84 VirtualFile GetProgramNCAFile() const;
85 const std::vector<std::shared_ptr<NCA>>& GetNCAs() const; 85 const std::vector<std::shared_ptr<NCA>>& GetNCAs() const;
86 std::shared_ptr<NCA> GetNCAByType(NCAContentType type) const; 86 std::shared_ptr<NCA> GetNCAByType(NCAContentType type) const;
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp
index 6dcec7816..6c356d85d 100644
--- a/src/core/file_sys/content_archive.cpp
+++ b/src/core/file_sys/content_archive.cpp
@@ -97,11 +97,288 @@ union NCASectionHeader {
97}; 97};
98static_assert(sizeof(NCASectionHeader) == 0x200, "NCASectionHeader has incorrect size."); 98static_assert(sizeof(NCASectionHeader) == 0x200, "NCASectionHeader has incorrect size.");
99 99
100bool IsValidNCA(const NCAHeader& header) { 100static bool IsValidNCA(const NCAHeader& header) {
101 // TODO(DarkLordZach): Add NCA2/NCA0 support. 101 // TODO(DarkLordZach): Add NCA2/NCA0 support.
102 return header.magic == Common::MakeMagic('N', 'C', 'A', '3'); 102 return header.magic == Common::MakeMagic('N', 'C', 'A', '3');
103} 103}
104 104
105NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_offset)
106 : file(std::move(file_)), bktr_base_romfs(std::move(bktr_base_romfs_)) {
107 if (file == nullptr) {
108 status = Loader::ResultStatus::ErrorNullFile;
109 return;
110 }
111
112 if (sizeof(NCAHeader) != file->ReadObject(&header)) {
113 LOG_ERROR(Loader, "File reader errored out during header read.");
114 status = Loader::ResultStatus::ErrorBadNCAHeader;
115 return;
116 }
117
118 if (!HandlePotentialHeaderDecryption()) {
119 return;
120 }
121
122 has_rights_id = std::any_of(header.rights_id.begin(), header.rights_id.end(),
123 [](char c) { return c != '\0'; });
124
125 const std::vector<NCASectionHeader> sections = ReadSectionHeaders();
126 is_update = std::any_of(sections.begin(), sections.end(), [](const NCASectionHeader& header) {
127 return header.raw.header.crypto_type == NCASectionCryptoType::BKTR;
128 });
129
130 if (!ReadSections(sections, bktr_base_ivfc_offset)) {
131 return;
132 }
133
134 status = Loader::ResultStatus::Success;
135}
136
137NCA::~NCA() = default;
138
139bool NCA::CheckSupportedNCA(const NCAHeader& nca_header) {
140 if (nca_header.magic == Common::MakeMagic('N', 'C', 'A', '2')) {
141 status = Loader::ResultStatus::ErrorNCA2;
142 return false;
143 }
144
145 if (nca_header.magic == Common::MakeMagic('N', 'C', 'A', '0')) {
146 status = Loader::ResultStatus::ErrorNCA0;
147 return false;
148 }
149
150 return true;
151}
152
153bool NCA::HandlePotentialHeaderDecryption() {
154 if (IsValidNCA(header)) {
155 return true;
156 }
157
158 if (!CheckSupportedNCA(header)) {
159 return false;
160 }
161
162 NCAHeader dec_header{};
163 Core::Crypto::AESCipher<Core::Crypto::Key256> cipher(
164 keys.GetKey(Core::Crypto::S256KeyType::Header), Core::Crypto::Mode::XTS);
165 cipher.XTSTranscode(&header, sizeof(NCAHeader), &dec_header, 0, 0x200,
166 Core::Crypto::Op::Decrypt);
167 if (IsValidNCA(dec_header)) {
168 header = dec_header;
169 encrypted = true;
170 } else {
171 if (!CheckSupportedNCA(dec_header)) {
172 return false;
173 }
174
175 if (keys.HasKey(Core::Crypto::S256KeyType::Header)) {
176 status = Loader::ResultStatus::ErrorIncorrectHeaderKey;
177 } else {
178 status = Loader::ResultStatus::ErrorMissingHeaderKey;
179 }
180 return false;
181 }
182
183 return true;
184}
185
186std::vector<NCASectionHeader> NCA::ReadSectionHeaders() const {
187 const std::ptrdiff_t number_sections =
188 std::count_if(std::begin(header.section_tables), std::end(header.section_tables),
189 [](NCASectionTableEntry entry) { return entry.media_offset > 0; });
190
191 std::vector<NCASectionHeader> sections(number_sections);
192 const auto length_sections = SECTION_HEADER_SIZE * number_sections;
193
194 if (encrypted) {
195 auto raw = file->ReadBytes(length_sections, SECTION_HEADER_OFFSET);
196 Core::Crypto::AESCipher<Core::Crypto::Key256> cipher(
197 keys.GetKey(Core::Crypto::S256KeyType::Header), Core::Crypto::Mode::XTS);
198 cipher.XTSTranscode(raw.data(), length_sections, sections.data(), 2, SECTION_HEADER_SIZE,
199 Core::Crypto::Op::Decrypt);
200 } else {
201 file->ReadBytes(sections.data(), length_sections, SECTION_HEADER_OFFSET);
202 }
203
204 return sections;
205}
206
207bool NCA::ReadSections(const std::vector<NCASectionHeader>& sections, u64 bktr_base_ivfc_offset) {
208 for (std::size_t i = 0; i < sections.size(); ++i) {
209 const auto& section = sections[i];
210
211 if (section.raw.header.filesystem_type == NCASectionFilesystemType::ROMFS) {
212 if (!ReadRomFSSection(section, header.section_tables[i], bktr_base_ivfc_offset)) {
213 return false;
214 }
215 } else if (section.raw.header.filesystem_type == NCASectionFilesystemType::PFS0) {
216 if (!ReadPFS0Section(section, header.section_tables[i])) {
217 return false;
218 }
219 }
220 }
221
222 return true;
223}
224
225bool NCA::ReadRomFSSection(const NCASectionHeader& section, const NCASectionTableEntry& entry,
226 u64 bktr_base_ivfc_offset) {
227 const std::size_t base_offset = entry.media_offset * MEDIA_OFFSET_MULTIPLIER;
228 ivfc_offset = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset;
229 const std::size_t romfs_offset = base_offset + ivfc_offset;
230 const std::size_t romfs_size = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].size;
231 auto raw = std::make_shared<OffsetVfsFile>(file, romfs_size, romfs_offset);
232 auto dec = Decrypt(section, raw, romfs_offset);
233
234 if (dec == nullptr) {
235 if (status != Loader::ResultStatus::Success)
236 return false;
237 if (has_rights_id)
238 status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek;
239 else
240 status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey;
241 return false;
242 }
243
244 if (section.raw.header.crypto_type == NCASectionCryptoType::BKTR) {
245 if (section.bktr.relocation.magic != Common::MakeMagic('B', 'K', 'T', 'R') ||
246 section.bktr.subsection.magic != Common::MakeMagic('B', 'K', 'T', 'R')) {
247 status = Loader::ResultStatus::ErrorBadBKTRHeader;
248 return false;
249 }
250
251 if (section.bktr.relocation.offset + section.bktr.relocation.size !=
252 section.bktr.subsection.offset) {
253 status = Loader::ResultStatus::ErrorBKTRSubsectionNotAfterRelocation;
254 return false;
255 }
256
257 const u64 size = MEDIA_OFFSET_MULTIPLIER * (entry.media_end_offset - entry.media_offset);
258 if (section.bktr.subsection.offset + section.bktr.subsection.size != size) {
259 status = Loader::ResultStatus::ErrorBKTRSubsectionNotAtEnd;
260 return false;
261 }
262
263 const u64 offset = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset;
264 RelocationBlock relocation_block{};
265 if (dec->ReadObject(&relocation_block, section.bktr.relocation.offset - offset) !=
266 sizeof(RelocationBlock)) {
267 status = Loader::ResultStatus::ErrorBadRelocationBlock;
268 return false;
269 }
270 SubsectionBlock subsection_block{};
271 if (dec->ReadObject(&subsection_block, section.bktr.subsection.offset - offset) !=
272 sizeof(RelocationBlock)) {
273 status = Loader::ResultStatus::ErrorBadSubsectionBlock;
274 return false;
275 }
276
277 std::vector<RelocationBucketRaw> relocation_buckets_raw(
278 (section.bktr.relocation.size - sizeof(RelocationBlock)) / sizeof(RelocationBucketRaw));
279 if (dec->ReadBytes(relocation_buckets_raw.data(),
280 section.bktr.relocation.size - sizeof(RelocationBlock),
281 section.bktr.relocation.offset + sizeof(RelocationBlock) - offset) !=
282 section.bktr.relocation.size - sizeof(RelocationBlock)) {
283 status = Loader::ResultStatus::ErrorBadRelocationBuckets;
284 return false;
285 }
286
287 std::vector<SubsectionBucketRaw> subsection_buckets_raw(
288 (section.bktr.subsection.size - sizeof(SubsectionBlock)) / sizeof(SubsectionBucketRaw));
289 if (dec->ReadBytes(subsection_buckets_raw.data(),
290 section.bktr.subsection.size - sizeof(SubsectionBlock),
291 section.bktr.subsection.offset + sizeof(SubsectionBlock) - offset) !=
292 section.bktr.subsection.size - sizeof(SubsectionBlock)) {
293 status = Loader::ResultStatus::ErrorBadSubsectionBuckets;
294 return false;
295 }
296
297 std::vector<RelocationBucket> relocation_buckets(relocation_buckets_raw.size());
298 std::transform(relocation_buckets_raw.begin(), relocation_buckets_raw.end(),
299 relocation_buckets.begin(), &ConvertRelocationBucketRaw);
300 std::vector<SubsectionBucket> subsection_buckets(subsection_buckets_raw.size());
301 std::transform(subsection_buckets_raw.begin(), subsection_buckets_raw.end(),
302 subsection_buckets.begin(), &ConvertSubsectionBucketRaw);
303
304 u32 ctr_low;
305 std::memcpy(&ctr_low, section.raw.section_ctr.data(), sizeof(ctr_low));
306 subsection_buckets.back().entries.push_back({section.bktr.relocation.offset, {0}, ctr_low});
307 subsection_buckets.back().entries.push_back({size, {0}, 0});
308
309 boost::optional<Core::Crypto::Key128> key = boost::none;
310 if (encrypted) {
311 if (has_rights_id) {
312 status = Loader::ResultStatus::Success;
313 key = GetTitlekey();
314 if (key == boost::none) {
315 status = Loader::ResultStatus::ErrorMissingTitlekey;
316 return false;
317 }
318 } else {
319 key = GetKeyAreaKey(NCASectionCryptoType::BKTR);
320 if (key == boost::none) {
321 status = Loader::ResultStatus::ErrorMissingKeyAreaKey;
322 return false;
323 }
324 }
325 }
326
327 if (bktr_base_romfs == nullptr) {
328 status = Loader::ResultStatus::ErrorMissingBKTRBaseRomFS;
329 return false;
330 }
331
332 auto bktr = std::make_shared<BKTR>(
333 bktr_base_romfs, std::make_shared<OffsetVfsFile>(file, romfs_size, base_offset),
334 relocation_block, relocation_buckets, subsection_block, subsection_buckets, encrypted,
335 encrypted ? key.get() : Core::Crypto::Key128{}, base_offset, bktr_base_ivfc_offset,
336 section.raw.section_ctr);
337
338 // BKTR applies to entire IVFC, so make an offset version to level 6
339 files.push_back(std::make_shared<OffsetVfsFile>(
340 bktr, romfs_size, section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset));
341 } else {
342 files.push_back(std::move(dec));
343 }
344
345 romfs = files.back();
346 return true;
347}
348
349bool NCA::ReadPFS0Section(const NCASectionHeader& section, const NCASectionTableEntry& entry) {
350 const u64 offset = (static_cast<u64>(entry.media_offset) * MEDIA_OFFSET_MULTIPLIER) +
351 section.pfs0.pfs0_header_offset;
352 const u64 size = MEDIA_OFFSET_MULTIPLIER * (entry.media_end_offset - entry.media_offset);
353
354 auto dec = Decrypt(section, std::make_shared<OffsetVfsFile>(file, size, offset), offset);
355 if (dec != nullptr) {
356 auto npfs = std::make_shared<PartitionFilesystem>(std::move(dec));
357
358 if (npfs->GetStatus() == Loader::ResultStatus::Success) {
359 dirs.push_back(std::move(npfs));
360 if (IsDirectoryExeFS(dirs.back()))
361 exefs = dirs.back();
362 } else {
363 if (has_rights_id)
364 status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek;
365 else
366 status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey;
367 return false;
368 }
369 } else {
370 if (status != Loader::ResultStatus::Success)
371 return false;
372 if (has_rights_id)
373 status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek;
374 else
375 status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey;
376 return false;
377 }
378
379 return true;
380}
381
105u8 NCA::GetCryptoRevision() const { 382u8 NCA::GetCryptoRevision() const {
106 u8 master_key_id = header.crypto_type; 383 u8 master_key_id = header.crypto_type;
107 if (header.crypto_type_2 > master_key_id) 384 if (header.crypto_type_2 > master_key_id)
@@ -167,7 +444,7 @@ boost::optional<Core::Crypto::Key128> NCA::GetTitlekey() {
167 return titlekey; 444 return titlekey;
168} 445}
169 446
170VirtualFile NCA::Decrypt(NCASectionHeader s_header, VirtualFile in, u64 starting_offset) { 447VirtualFile NCA::Decrypt(const NCASectionHeader& s_header, VirtualFile in, u64 starting_offset) {
171 if (!encrypted) 448 if (!encrypted)
172 return in; 449 return in;
173 450
@@ -215,256 +492,6 @@ VirtualFile NCA::Decrypt(NCASectionHeader s_header, VirtualFile in, u64 starting
215 } 492 }
216} 493}
217 494
218NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_offset)
219 : file(std::move(file_)),
220 bktr_base_romfs(bktr_base_romfs_ ? std::move(bktr_base_romfs_) : nullptr) {
221 status = Loader::ResultStatus::Success;
222
223 if (file == nullptr) {
224 status = Loader::ResultStatus::ErrorNullFile;
225 return;
226 }
227
228 if (sizeof(NCAHeader) != file->ReadObject(&header)) {
229 LOG_ERROR(Loader, "File reader errored out during header read.");
230 status = Loader::ResultStatus::ErrorBadNCAHeader;
231 return;
232 }
233
234 encrypted = false;
235
236 if (!IsValidNCA(header)) {
237 if (header.magic == Common::MakeMagic('N', 'C', 'A', '2')) {
238 status = Loader::ResultStatus::ErrorNCA2;
239 return;
240 }
241 if (header.magic == Common::MakeMagic('N', 'C', 'A', '0')) {
242 status = Loader::ResultStatus::ErrorNCA0;
243 return;
244 }
245
246 NCAHeader dec_header{};
247 Core::Crypto::AESCipher<Core::Crypto::Key256> cipher(
248 keys.GetKey(Core::Crypto::S256KeyType::Header), Core::Crypto::Mode::XTS);
249 cipher.XTSTranscode(&header, sizeof(NCAHeader), &dec_header, 0, 0x200,
250 Core::Crypto::Op::Decrypt);
251 if (IsValidNCA(dec_header)) {
252 header = dec_header;
253 encrypted = true;
254 } else {
255 if (dec_header.magic == Common::MakeMagic('N', 'C', 'A', '2')) {
256 status = Loader::ResultStatus::ErrorNCA2;
257 return;
258 }
259 if (dec_header.magic == Common::MakeMagic('N', 'C', 'A', '0')) {
260 status = Loader::ResultStatus::ErrorNCA0;
261 return;
262 }
263
264 if (!keys.HasKey(Core::Crypto::S256KeyType::Header))
265 status = Loader::ResultStatus::ErrorMissingHeaderKey;
266 else
267 status = Loader::ResultStatus::ErrorIncorrectHeaderKey;
268 return;
269 }
270 }
271
272 has_rights_id = std::find_if_not(header.rights_id.begin(), header.rights_id.end(),
273 [](char c) { return c == '\0'; }) != header.rights_id.end();
274
275 const std::ptrdiff_t number_sections =
276 std::count_if(std::begin(header.section_tables), std::end(header.section_tables),
277 [](NCASectionTableEntry entry) { return entry.media_offset > 0; });
278
279 std::vector<NCASectionHeader> sections(number_sections);
280 const auto length_sections = SECTION_HEADER_SIZE * number_sections;
281
282 if (encrypted) {
283 auto raw = file->ReadBytes(length_sections, SECTION_HEADER_OFFSET);
284 Core::Crypto::AESCipher<Core::Crypto::Key256> cipher(
285 keys.GetKey(Core::Crypto::S256KeyType::Header), Core::Crypto::Mode::XTS);
286 cipher.XTSTranscode(raw.data(), length_sections, sections.data(), 2, SECTION_HEADER_SIZE,
287 Core::Crypto::Op::Decrypt);
288 } else {
289 file->ReadBytes(sections.data(), length_sections, SECTION_HEADER_OFFSET);
290 }
291
292 is_update = std::find_if(sections.begin(), sections.end(), [](const NCASectionHeader& header) {
293 return header.raw.header.crypto_type == NCASectionCryptoType::BKTR;
294 }) != sections.end();
295 ivfc_offset = 0;
296
297 for (std::ptrdiff_t i = 0; i < number_sections; ++i) {
298 auto section = sections[i];
299
300 if (section.raw.header.filesystem_type == NCASectionFilesystemType::ROMFS) {
301 const std::size_t base_offset =
302 header.section_tables[i].media_offset * MEDIA_OFFSET_MULTIPLIER;
303 ivfc_offset = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset;
304 const std::size_t romfs_offset = base_offset + ivfc_offset;
305 const std::size_t romfs_size = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].size;
306 auto raw = std::make_shared<OffsetVfsFile>(file, romfs_size, romfs_offset);
307 auto dec = Decrypt(section, raw, romfs_offset);
308
309 if (dec == nullptr) {
310 if (status != Loader::ResultStatus::Success)
311 return;
312 if (has_rights_id)
313 status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek;
314 else
315 status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey;
316 return;
317 }
318
319 if (section.raw.header.crypto_type == NCASectionCryptoType::BKTR) {
320 if (section.bktr.relocation.magic != Common::MakeMagic('B', 'K', 'T', 'R') ||
321 section.bktr.subsection.magic != Common::MakeMagic('B', 'K', 'T', 'R')) {
322 status = Loader::ResultStatus::ErrorBadBKTRHeader;
323 return;
324 }
325
326 if (section.bktr.relocation.offset + section.bktr.relocation.size !=
327 section.bktr.subsection.offset) {
328 status = Loader::ResultStatus::ErrorBKTRSubsectionNotAfterRelocation;
329 return;
330 }
331
332 const u64 size =
333 MEDIA_OFFSET_MULTIPLIER * (header.section_tables[i].media_end_offset -
334 header.section_tables[i].media_offset);
335 if (section.bktr.subsection.offset + section.bktr.subsection.size != size) {
336 status = Loader::ResultStatus::ErrorBKTRSubsectionNotAtEnd;
337 return;
338 }
339
340 const u64 offset = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset;
341 RelocationBlock relocation_block{};
342 if (dec->ReadObject(&relocation_block, section.bktr.relocation.offset - offset) !=
343 sizeof(RelocationBlock)) {
344 status = Loader::ResultStatus::ErrorBadRelocationBlock;
345 return;
346 }
347 SubsectionBlock subsection_block{};
348 if (dec->ReadObject(&subsection_block, section.bktr.subsection.offset - offset) !=
349 sizeof(RelocationBlock)) {
350 status = Loader::ResultStatus::ErrorBadSubsectionBlock;
351 return;
352 }
353
354 std::vector<RelocationBucketRaw> relocation_buckets_raw(
355 (section.bktr.relocation.size - sizeof(RelocationBlock)) /
356 sizeof(RelocationBucketRaw));
357 if (dec->ReadBytes(relocation_buckets_raw.data(),
358 section.bktr.relocation.size - sizeof(RelocationBlock),
359 section.bktr.relocation.offset + sizeof(RelocationBlock) -
360 offset) !=
361 section.bktr.relocation.size - sizeof(RelocationBlock)) {
362 status = Loader::ResultStatus::ErrorBadRelocationBuckets;
363 return;
364 }
365
366 std::vector<SubsectionBucketRaw> subsection_buckets_raw(
367 (section.bktr.subsection.size - sizeof(SubsectionBlock)) /
368 sizeof(SubsectionBucketRaw));
369 if (dec->ReadBytes(subsection_buckets_raw.data(),
370 section.bktr.subsection.size - sizeof(SubsectionBlock),
371 section.bktr.subsection.offset + sizeof(SubsectionBlock) -
372 offset) !=
373 section.bktr.subsection.size - sizeof(SubsectionBlock)) {
374 status = Loader::ResultStatus::ErrorBadSubsectionBuckets;
375 return;
376 }
377
378 std::vector<RelocationBucket> relocation_buckets(relocation_buckets_raw.size());
379 std::transform(relocation_buckets_raw.begin(), relocation_buckets_raw.end(),
380 relocation_buckets.begin(), &ConvertRelocationBucketRaw);
381 std::vector<SubsectionBucket> subsection_buckets(subsection_buckets_raw.size());
382 std::transform(subsection_buckets_raw.begin(), subsection_buckets_raw.end(),
383 subsection_buckets.begin(), &ConvertSubsectionBucketRaw);
384
385 u32 ctr_low;
386 std::memcpy(&ctr_low, section.raw.section_ctr.data(), sizeof(ctr_low));
387 subsection_buckets.back().entries.push_back(
388 {section.bktr.relocation.offset, {0}, ctr_low});
389 subsection_buckets.back().entries.push_back({size, {0}, 0});
390
391 boost::optional<Core::Crypto::Key128> key = boost::none;
392 if (encrypted) {
393 if (has_rights_id) {
394 status = Loader::ResultStatus::Success;
395 key = GetTitlekey();
396 if (key == boost::none) {
397 status = Loader::ResultStatus::ErrorMissingTitlekey;
398 return;
399 }
400 } else {
401 key = GetKeyAreaKey(NCASectionCryptoType::BKTR);
402 if (key == boost::none) {
403 status = Loader::ResultStatus::ErrorMissingKeyAreaKey;
404 return;
405 }
406 }
407 }
408
409 if (bktr_base_romfs == nullptr) {
410 status = Loader::ResultStatus::ErrorMissingBKTRBaseRomFS;
411 return;
412 }
413
414 auto bktr = std::make_shared<BKTR>(
415 bktr_base_romfs, std::make_shared<OffsetVfsFile>(file, romfs_size, base_offset),
416 relocation_block, relocation_buckets, subsection_block, subsection_buckets,
417 encrypted, encrypted ? key.get() : Core::Crypto::Key128{}, base_offset,
418 bktr_base_ivfc_offset, section.raw.section_ctr);
419
420 // BKTR applies to entire IVFC, so make an offset version to level 6
421
422 files.push_back(std::make_shared<OffsetVfsFile>(
423 bktr, romfs_size, section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset));
424 romfs = files.back();
425 } else {
426 files.push_back(std::move(dec));
427 romfs = files.back();
428 }
429 } else if (section.raw.header.filesystem_type == NCASectionFilesystemType::PFS0) {
430 u64 offset = (static_cast<u64>(header.section_tables[i].media_offset) *
431 MEDIA_OFFSET_MULTIPLIER) +
432 section.pfs0.pfs0_header_offset;
433 u64 size = MEDIA_OFFSET_MULTIPLIER * (header.section_tables[i].media_end_offset -
434 header.section_tables[i].media_offset);
435 auto dec =
436 Decrypt(section, std::make_shared<OffsetVfsFile>(file, size, offset), offset);
437 if (dec != nullptr) {
438 auto npfs = std::make_shared<PartitionFilesystem>(std::move(dec));
439
440 if (npfs->GetStatus() == Loader::ResultStatus::Success) {
441 dirs.push_back(std::move(npfs));
442 if (IsDirectoryExeFS(dirs.back()))
443 exefs = dirs.back();
444 } else {
445 if (has_rights_id)
446 status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek;
447 else
448 status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey;
449 return;
450 }
451 } else {
452 if (status != Loader::ResultStatus::Success)
453 return;
454 if (has_rights_id)
455 status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek;
456 else
457 status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey;
458 return;
459 }
460 }
461 }
462
463 status = Loader::ResultStatus::Success;
464}
465
466NCA::~NCA() = default;
467
468Loader::ResultStatus NCA::GetStatus() const { 495Loader::ResultStatus NCA::GetStatus() const {
469 return status; 496 return status;
470} 497}
diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h
index f9f66cae9..1c903cd3f 100644
--- a/src/core/file_sys/content_archive.h
+++ b/src/core/file_sys/content_archive.h
@@ -73,8 +73,6 @@ inline bool IsDirectoryExeFS(const std::shared_ptr<VfsDirectory>& pfs) {
73 return pfs->GetFile("main") != nullptr && pfs->GetFile("main.npdm") != nullptr; 73 return pfs->GetFile("main") != nullptr && pfs->GetFile("main.npdm") != nullptr;
74} 74}
75 75
76bool IsValidNCA(const NCAHeader& header);
77
78// An implementation of VfsDirectory that represents a Nintendo Content Archive (NCA) conatiner. 76// An implementation of VfsDirectory that represents a Nintendo Content Archive (NCA) conatiner.
79// After construction, use GetStatus to determine if the file is valid and ready to be used. 77// After construction, use GetStatus to determine if the file is valid and ready to be used.
80class NCA : public ReadOnlyVfsDirectory { 78class NCA : public ReadOnlyVfsDirectory {
@@ -106,10 +104,19 @@ protected:
106 bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; 104 bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
107 105
108private: 106private:
107 bool CheckSupportedNCA(const NCAHeader& header);
108 bool HandlePotentialHeaderDecryption();
109
110 std::vector<NCASectionHeader> ReadSectionHeaders() const;
111 bool ReadSections(const std::vector<NCASectionHeader>& sections, u64 bktr_base_ivfc_offset);
112 bool ReadRomFSSection(const NCASectionHeader& section, const NCASectionTableEntry& entry,
113 u64 bktr_base_ivfc_offset);
114 bool ReadPFS0Section(const NCASectionHeader& section, const NCASectionTableEntry& entry);
115
109 u8 GetCryptoRevision() const; 116 u8 GetCryptoRevision() const;
110 boost::optional<Core::Crypto::Key128> GetKeyAreaKey(NCASectionCryptoType type) const; 117 boost::optional<Core::Crypto::Key128> GetKeyAreaKey(NCASectionCryptoType type) const;
111 boost::optional<Core::Crypto::Key128> GetTitlekey(); 118 boost::optional<Core::Crypto::Key128> GetTitlekey();
112 VirtualFile Decrypt(NCASectionHeader header, VirtualFile in, u64 starting_offset); 119 VirtualFile Decrypt(const NCASectionHeader& header, VirtualFile in, u64 starting_offset);
113 120
114 std::vector<VirtualDir> dirs; 121 std::vector<VirtualDir> dirs;
115 std::vector<VirtualFile> files; 122 std::vector<VirtualFile> files;
@@ -118,15 +125,15 @@ private:
118 VirtualDir exefs = nullptr; 125 VirtualDir exefs = nullptr;
119 VirtualFile file; 126 VirtualFile file;
120 VirtualFile bktr_base_romfs; 127 VirtualFile bktr_base_romfs;
121 u64 ivfc_offset; 128 u64 ivfc_offset = 0;
122 129
123 NCAHeader header{}; 130 NCAHeader header{};
124 bool has_rights_id{}; 131 bool has_rights_id{};
125 132
126 Loader::ResultStatus status{}; 133 Loader::ResultStatus status{};
127 134
128 bool encrypted; 135 bool encrypted = false;
129 bool is_update; 136 bool is_update = false;
130 137
131 Core::Crypto::KeyManager keys; 138 Core::Crypto::KeyManager keys;
132}; 139};
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index d08b84bde..d3c9d50b5 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -8,6 +8,7 @@
8#include <mutex> 8#include <mutex>
9#include <vector> 9#include <vector>
10 10
11#include "common/alignment.h"
11#include "common/assert.h" 12#include "common/assert.h"
12#include "common/logging/log.h" 13#include "common/logging/log.h"
13#include "common/microprofile.h" 14#include "common/microprofile.h"
@@ -36,9 +37,6 @@
36 37
37namespace Kernel { 38namespace Kernel {
38namespace { 39namespace {
39constexpr bool Is4KBAligned(VAddr address) {
40 return (address & 0xFFF) == 0;
41}
42 40
43// Checks if address + size is greater than the given address 41// Checks if address + size is greater than the given address
44// This can return false if the size causes an overflow of a 64-bit type 42// This can return false if the size causes an overflow of a 64-bit type
@@ -69,11 +67,11 @@ bool IsInsideNewMapRegion(const VMManager& vm, VAddr address, u64 size) {
69// in the same order. 67// in the same order.
70ResultCode MapUnmapMemorySanityChecks(const VMManager& vm_manager, VAddr dst_addr, VAddr src_addr, 68ResultCode MapUnmapMemorySanityChecks(const VMManager& vm_manager, VAddr dst_addr, VAddr src_addr,
71 u64 size) { 69 u64 size) {
72 if (!Is4KBAligned(dst_addr) || !Is4KBAligned(src_addr)) { 70 if (!Common::Is4KBAligned(dst_addr) || !Common::Is4KBAligned(src_addr)) {
73 return ERR_INVALID_ADDRESS; 71 return ERR_INVALID_ADDRESS;
74 } 72 }
75 73
76 if (size == 0 || !Is4KBAligned(size)) { 74 if (size == 0 || !Common::Is4KBAligned(size)) {
77 return ERR_INVALID_SIZE; 75 return ERR_INVALID_SIZE;
78 } 76 }
79 77
@@ -352,6 +350,10 @@ static ResultCode ArbitrateLock(Handle holding_thread_handle, VAddr mutex_addr,
352 return ERR_INVALID_ADDRESS_STATE; 350 return ERR_INVALID_ADDRESS_STATE;
353 } 351 }
354 352
353 if (!Common::IsWordAligned(mutex_addr)) {
354 return ERR_INVALID_ADDRESS;
355 }
356
355 auto& handle_table = Core::System::GetInstance().Kernel().HandleTable(); 357 auto& handle_table = Core::System::GetInstance().Kernel().HandleTable();
356 return Mutex::TryAcquire(handle_table, mutex_addr, holding_thread_handle, 358 return Mutex::TryAcquire(handle_table, mutex_addr, holding_thread_handle,
357 requesting_thread_handle); 359 requesting_thread_handle);
@@ -365,6 +367,10 @@ static ResultCode ArbitrateUnlock(VAddr mutex_addr) {
365 return ERR_INVALID_ADDRESS_STATE; 367 return ERR_INVALID_ADDRESS_STATE;
366 } 368 }
367 369
370 if (!Common::IsWordAligned(mutex_addr)) {
371 return ERR_INVALID_ADDRESS;
372 }
373
368 return Mutex::Release(mutex_addr); 374 return Mutex::Release(mutex_addr);
369} 375}
370 376
@@ -570,11 +576,11 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s
570 "called, shared_memory_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}", 576 "called, shared_memory_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}",
571 shared_memory_handle, addr, size, permissions); 577 shared_memory_handle, addr, size, permissions);
572 578
573 if (!Is4KBAligned(addr)) { 579 if (!Common::Is4KBAligned(addr)) {
574 return ERR_INVALID_ADDRESS; 580 return ERR_INVALID_ADDRESS;
575 } 581 }
576 582
577 if (size == 0 || !Is4KBAligned(size)) { 583 if (size == 0 || !Common::Is4KBAligned(size)) {
578 return ERR_INVALID_SIZE; 584 return ERR_INVALID_SIZE;
579 } 585 }
580 586
@@ -599,11 +605,11 @@ static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64
599 LOG_WARNING(Kernel_SVC, "called, shared_memory_handle=0x{:08X}, addr=0x{:X}, size=0x{:X}", 605 LOG_WARNING(Kernel_SVC, "called, shared_memory_handle=0x{:08X}, addr=0x{:X}, size=0x{:X}",
600 shared_memory_handle, addr, size); 606 shared_memory_handle, addr, size);
601 607
602 if (!Is4KBAligned(addr)) { 608 if (!Common::Is4KBAligned(addr)) {
603 return ERR_INVALID_ADDRESS; 609 return ERR_INVALID_ADDRESS;
604 } 610 }
605 611
606 if (size == 0 || !Is4KBAligned(size)) { 612 if (size == 0 || !Common::Is4KBAligned(size)) {
607 return ERR_INVALID_SIZE; 613 return ERR_INVALID_SIZE;
608 } 614 }
609 615
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 69bfce1c1..4d1f83170 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -638,10 +638,12 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF
638 {24, nullptr, "GetLaunchStorageInfoForDebug"}, 638 {24, nullptr, "GetLaunchStorageInfoForDebug"},
639 {25, nullptr, "ExtendSaveData"}, 639 {25, nullptr, "ExtendSaveData"},
640 {26, nullptr, "GetSaveDataSize"}, 640 {26, nullptr, "GetSaveDataSize"},
641 {30, nullptr, "BeginBlockingHomeButtonShortAndLongPressed"}, 641 {30, &IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed,
642 {31, nullptr, "EndBlockingHomeButtonShortAndLongPressed"}, 642 "BeginBlockingHomeButtonShortAndLongPressed"},
643 {32, nullptr, "BeginBlockingHomeButton"}, 643 {31, &IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed,
644 {33, nullptr, "EndBlockingHomeButton"}, 644 "EndBlockingHomeButtonShortAndLongPressed"},
645 {32, &IApplicationFunctions::BeginBlockingHomeButton, "BeginBlockingHomeButton"},
646 {33, &IApplicationFunctions::EndBlockingHomeButton, "EndBlockingHomeButton"},
645 {40, &IApplicationFunctions::NotifyRunning, "NotifyRunning"}, 647 {40, &IApplicationFunctions::NotifyRunning, "NotifyRunning"},
646 {50, &IApplicationFunctions::GetPseudoDeviceId, "GetPseudoDeviceId"}, 648 {50, &IApplicationFunctions::GetPseudoDeviceId, "GetPseudoDeviceId"},
647 {60, nullptr, "SetMediaPlaybackStateForApplication"}, 649 {60, nullptr, "SetMediaPlaybackStateForApplication"},
@@ -669,6 +671,32 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF
669 671
670IApplicationFunctions::~IApplicationFunctions() = default; 672IApplicationFunctions::~IApplicationFunctions() = default;
671 673
674void IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed(
675 Kernel::HLERequestContext& ctx) {
676 IPC::ResponseBuilder rb{ctx, 2};
677 rb.Push(RESULT_SUCCESS);
678 LOG_WARNING(Service_AM, "(STUBBED) called");
679}
680
681void IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed(
682 Kernel::HLERequestContext& ctx) {
683 IPC::ResponseBuilder rb{ctx, 2};
684 rb.Push(RESULT_SUCCESS);
685 LOG_WARNING(Service_AM, "(STUBBED) called");
686}
687
688void IApplicationFunctions::BeginBlockingHomeButton(Kernel::HLERequestContext& ctx) {
689 IPC::ResponseBuilder rb{ctx, 2};
690 rb.Push(RESULT_SUCCESS);
691 LOG_WARNING(Service_AM, "(STUBBED) called");
692}
693
694void IApplicationFunctions::EndBlockingHomeButton(Kernel::HLERequestContext& ctx) {
695 IPC::ResponseBuilder rb{ctx, 2};
696 rb.Push(RESULT_SUCCESS);
697 LOG_WARNING(Service_AM, "(STUBBED) called");
698}
699
672void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) { 700void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) {
673 constexpr std::array<u8, 0x88> data{{ 701 constexpr std::array<u8, 0x88> data{{
674 0xca, 0x97, 0x94, 0xc7, // Magic 702 0xca, 0x97, 0x94, 0xc7, // Magic
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index b39b0d838..095f94851 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -154,6 +154,10 @@ private:
154 void SetGamePlayRecordingState(Kernel::HLERequestContext& ctx); 154 void SetGamePlayRecordingState(Kernel::HLERequestContext& ctx);
155 void NotifyRunning(Kernel::HLERequestContext& ctx); 155 void NotifyRunning(Kernel::HLERequestContext& ctx);
156 void GetPseudoDeviceId(Kernel::HLERequestContext& ctx); 156 void GetPseudoDeviceId(Kernel::HLERequestContext& ctx);
157 void BeginBlockingHomeButtonShortAndLongPressed(Kernel::HLERequestContext& ctx);
158 void EndBlockingHomeButtonShortAndLongPressed(Kernel::HLERequestContext& ctx);
159 void BeginBlockingHomeButton(Kernel::HLERequestContext& ctx);
160 void EndBlockingHomeButton(Kernel::HLERequestContext& ctx);
157}; 161};
158 162
159class IHomeMenuFunctions final : public ServiceFramework<IHomeMenuFunctions> { 163class IHomeMenuFunctions final : public ServiceFramework<IHomeMenuFunctions> {
diff --git a/src/core/hle/service/mm/mm_u.cpp b/src/core/hle/service/mm/mm_u.cpp
index 7b91bb258..e1f17a926 100644
--- a/src/core/hle/service/mm/mm_u.cpp
+++ b/src/core/hle/service/mm/mm_u.cpp
@@ -14,14 +14,14 @@ public:
14 explicit MM_U() : ServiceFramework{"mm:u"} { 14 explicit MM_U() : ServiceFramework{"mm:u"} {
15 // clang-format off 15 // clang-format off
16 static const FunctionInfo functions[] = { 16 static const FunctionInfo functions[] = {
17 {0, &MM_U::Initialize, "InitializeOld"}, 17 {0, &MM_U::Initialize, "Initialize"},
18 {1, &MM_U::Finalize, "FinalizeOld"}, 18 {1, &MM_U::Finalize, "Finalize"},
19 {2, &MM_U::SetAndWait, "SetAndWaitOld"}, 19 {2, &MM_U::SetAndWait, "SetAndWait"},
20 {3, &MM_U::Get, "GetOld"}, 20 {3, &MM_U::Get, "Get"},
21 {4, &MM_U::Initialize, "Initialize"}, 21 {4, &MM_U::InitializeWithId, "InitializeWithId"},
22 {5, &MM_U::Finalize, "Finalize"}, 22 {5, &MM_U::FinalizeWithId, "FinalizeWithId"},
23 {6, &MM_U::SetAndWait, "SetAndWait"}, 23 {6, &MM_U::SetAndWaitWithId, "SetAndWaitWithId"},
24 {7, &MM_U::Get, "Get"}, 24 {7, &MM_U::GetWithId, "GetWithId"},
25 }; 25 };
26 // clang-format on 26 // clang-format on
27 27
@@ -59,9 +59,43 @@ private:
59 rb.Push(current); 59 rb.Push(current);
60 } 60 }
61 61
62 void InitializeWithId(Kernel::HLERequestContext& ctx) {
63 LOG_WARNING(Service_MM, "(STUBBED) called");
64 IPC::ResponseBuilder rb{ctx, 3};
65 rb.Push(RESULT_SUCCESS);
66 rb.Push<u32>(id); // Any non zero value
67 }
68
69 void FinalizeWithId(Kernel::HLERequestContext& ctx) {
70 LOG_WARNING(Service_MM, "(STUBBED) called");
71 IPC::ResponseBuilder rb{ctx, 2};
72 rb.Push(RESULT_SUCCESS);
73 }
74
75 void SetAndWaitWithId(Kernel::HLERequestContext& ctx) {
76 IPC::RequestParser rp{ctx};
77 u32 input_id = rp.Pop<u32>();
78 min = rp.Pop<u32>();
79 max = rp.Pop<u32>();
80 current = min;
81
82 LOG_WARNING(Service_MM, "(STUBBED) called, input_id=0x{:X}, min=0x{:X}, max=0x{:X}",
83 input_id, min, max);
84 IPC::ResponseBuilder rb{ctx, 2};
85 rb.Push(RESULT_SUCCESS);
86 }
87
88 void GetWithId(Kernel::HLERequestContext& ctx) {
89 LOG_WARNING(Service_MM, "(STUBBED) called");
90 IPC::ResponseBuilder rb{ctx, 3};
91 rb.Push(RESULT_SUCCESS);
92 rb.Push(current);
93 }
94
62 u32 min{0}; 95 u32 min{0};
63 u32 max{0}; 96 u32 max{0};
64 u32 current{0}; 97 u32 current{0};
98 u32 id{1};
65}; 99};
66 100
67void InstallInterfaces(SM::ServiceManager& service_manager) { 101void InstallInterfaces(SM::ServiceManager& service_manager) {
diff --git a/src/core/loader/xci.cpp b/src/core/loader/xci.cpp
index 7a619acb4..461607c95 100644
--- a/src/core/loader/xci.cpp
+++ b/src/core/loader/xci.cpp
@@ -59,8 +59,7 @@ ResultStatus AppLoader_XCI::Load(Kernel::Process& process) {
59 if (xci->GetProgramNCAStatus() != ResultStatus::Success) 59 if (xci->GetProgramNCAStatus() != ResultStatus::Success)
60 return xci->GetProgramNCAStatus(); 60 return xci->GetProgramNCAStatus();
61 61
62 const auto nca = xci->GetProgramNCA(); 62 if (!xci->HasProgramNCA() && !Core::Crypto::KeyManager::KeyFileExists(false))
63 if (nca == nullptr && !Core::Crypto::KeyManager::KeyFileExists(false))
64 return ResultStatus::ErrorMissingProductionKeyFile; 63 return ResultStatus::ErrorMissingProductionKeyFile;
65 64
66 const auto result = nca_loader->Load(process); 65 const auto result = nca_loader->Load(process);
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index c8d1b6478..c8af1c6b6 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -448,7 +448,10 @@ public:
448 BitField<8, 3, u32> block_depth; 448 BitField<8, 3, u32> block_depth;
449 BitField<12, 1, InvMemoryLayout> type; 449 BitField<12, 1, InvMemoryLayout> type;
450 } memory_layout; 450 } memory_layout;
451 u32 array_mode; 451 union {
452 BitField<0, 16, u32> array_mode;
453 BitField<16, 1, u32> volume;
454 };
452 u32 layer_stride; 455 u32 layer_stride;
453 u32 base_layer; 456 u32 base_layer;
454 INSERT_PADDING_WORDS(7); 457 INSERT_PADDING_WORDS(7);
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
index 1cb77aaf2..9c8925383 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -155,6 +155,7 @@ void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) {
155 params.rt.index = static_cast<u32>(index); 155 params.rt.index = static_cast<u32>(index);
156 params.rt.array_mode = config.array_mode; 156 params.rt.array_mode = config.array_mode;
157 params.rt.layer_stride = config.layer_stride; 157 params.rt.layer_stride = config.layer_stride;
158 params.rt.volume = config.volume;
158 params.rt.base_layer = config.base_layer; 159 params.rt.base_layer = config.base_layer;
159 160
160 params.InitCacheParameters(config.Address()); 161 params.InitCacheParameters(config.Address());
@@ -1122,8 +1123,8 @@ Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params, bool pres
1122 } else if (preserve_contents) { 1123 } else if (preserve_contents) {
1123 // If surface parameters changed and we care about keeping the previous data, recreate 1124 // If surface parameters changed and we care about keeping the previous data, recreate
1124 // the surface from the old one 1125 // the surface from the old one
1125 Unregister(surface);
1126 Surface new_surface{RecreateSurface(surface, params)}; 1126 Surface new_surface{RecreateSurface(surface, params)};
1127 Unregister(surface);
1127 Register(new_surface); 1128 Register(new_surface);
1128 return new_surface; 1129 return new_surface;
1129 } else { 1130 } else {
@@ -1220,6 +1221,9 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface,
1220 CopySurface(old_surface, new_surface, copy_pbo.handle); 1221 CopySurface(old_surface, new_surface, copy_pbo.handle);
1221 } 1222 }
1222 break; 1223 break;
1224 case SurfaceParams::SurfaceTarget::Texture3D:
1225 AccurateCopySurface(old_surface, new_surface);
1226 break;
1223 case SurfaceParams::SurfaceTarget::TextureCubemap: { 1227 case SurfaceParams::SurfaceTarget::TextureCubemap: {
1224 if (old_params.rt.array_mode != 1) { 1228 if (old_params.rt.array_mode != 1) {
1225 // TODO(bunnei): This is used by Breath of the Wild, I'm not sure how to implement this 1229 // TODO(bunnei): This is used by Breath of the Wild, I'm not sure how to implement this
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
index 7c1cb72d0..0dd0d90a3 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
@@ -132,6 +132,8 @@ struct SurfaceParams {
132 case Tegra::Texture::TextureType::Texture2D: 132 case Tegra::Texture::TextureType::Texture2D:
133 case Tegra::Texture::TextureType::Texture2DNoMipmap: 133 case Tegra::Texture::TextureType::Texture2DNoMipmap:
134 return SurfaceTarget::Texture2D; 134 return SurfaceTarget::Texture2D;
135 case Tegra::Texture::TextureType::Texture3D:
136 return SurfaceTarget::Texture3D;
135 case Tegra::Texture::TextureType::TextureCubemap: 137 case Tegra::Texture::TextureType::TextureCubemap:
136 return SurfaceTarget::TextureCubemap; 138 return SurfaceTarget::TextureCubemap;
137 case Tegra::Texture::TextureType::Texture1DArray: 139 case Tegra::Texture::TextureType::Texture1DArray:
@@ -791,6 +793,7 @@ struct SurfaceParams {
791 struct { 793 struct {
792 u32 index; 794 u32 index;
793 u32 array_mode; 795 u32 array_mode;
796 u32 volume;
794 u32 layer_stride; 797 u32 layer_stride;
795 u32 base_layer; 798 u32 base_layer;
796 } rt; 799 } rt;
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index 55c33c3a9..f4340a017 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -1142,6 +1142,7 @@ private:
1142 case Tegra::Shader::TextureType::Texture2D: { 1142 case Tegra::Shader::TextureType::Texture2D: {
1143 return 2; 1143 return 2;
1144 } 1144 }
1145 case Tegra::Shader::TextureType::Texture3D:
1145 case Tegra::Shader::TextureType::TextureCube: { 1146 case Tegra::Shader::TextureType::TextureCube: {
1146 return 3; 1147 return 3;
1147 } 1148 }