| 
									
										
										
										
											2018-07-18 21:07:11 -04:00
										 |  |  | // Copyright 2018 yuzu emulator team
 | 
					
						
							|  |  |  | // Licensed under GPLv2 or any later version
 | 
					
						
							|  |  |  | // Refer to the license.txt file included.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-18 23:12:14 -04:00
										 |  |  | #include <algorithm>
 | 
					
						
							|  |  |  | #include <utility>
 | 
					
						
							| 
									
										
										
										
											2018-07-29 20:47:33 -04:00
										 |  |  | #include <boost/optional.hpp>
 | 
					
						
							| 
									
										
										
										
											2018-07-18 21:07:11 -04:00
										 |  |  | #include "common/logging/log.h"
 | 
					
						
							| 
									
										
										
										
											2018-07-27 23:55:23 -04:00
										 |  |  | #include "core/crypto/aes_util.h"
 | 
					
						
							|  |  |  | #include "core/crypto/ctr_encryption_layer.h"
 | 
					
						
							| 
									
										
										
										
											2018-07-18 21:07:11 -04:00
										 |  |  | #include "core/file_sys/content_archive.h"
 | 
					
						
							| 
									
										
										
										
											2018-07-28 21:39:42 -04:00
										 |  |  | #include "core/file_sys/romfs.h"
 | 
					
						
							| 
									
										
										
										
											2018-07-18 21:07:11 -04:00
										 |  |  | #include "core/file_sys/vfs_offset.h"
 | 
					
						
							|  |  |  | #include "core/loader/loader.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | namespace FileSys { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Media offsets in headers are stored divided by 512. Mult. by this to get real offset.
 | 
					
						
							|  |  |  | constexpr u64 MEDIA_OFFSET_MULTIPLIER = 0x200; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | constexpr u64 SECTION_HEADER_SIZE = 0x200; | 
					
						
							|  |  |  | constexpr u64 SECTION_HEADER_OFFSET = 0x400; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | constexpr u32 IVFC_MAX_LEVEL = 6; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | enum class NCASectionFilesystemType : u8 { | 
					
						
							|  |  |  |     PFS0 = 0x2, | 
					
						
							|  |  |  |     ROMFS = 0x3, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct NCASectionHeaderBlock { | 
					
						
							|  |  |  |     INSERT_PADDING_BYTES(3); | 
					
						
							|  |  |  |     NCASectionFilesystemType filesystem_type; | 
					
						
							| 
									
										
										
										
											2018-07-27 23:55:23 -04:00
										 |  |  |     NCASectionCryptoType crypto_type; | 
					
						
							| 
									
										
										
										
											2018-07-18 21:07:11 -04:00
										 |  |  |     INSERT_PADDING_BYTES(3); | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | static_assert(sizeof(NCASectionHeaderBlock) == 0x8, "NCASectionHeaderBlock has incorrect size."); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-27 23:55:23 -04:00
										 |  |  | struct NCASectionRaw { | 
					
						
							|  |  |  |     NCASectionHeaderBlock header; | 
					
						
							|  |  |  |     std::array<u8, 0x138> block_data; | 
					
						
							|  |  |  |     std::array<u8, 0x8> section_ctr; | 
					
						
							|  |  |  |     INSERT_PADDING_BYTES(0xB8); | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | static_assert(sizeof(NCASectionRaw) == 0x200, "NCASectionRaw has incorrect size."); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-18 21:07:11 -04:00
										 |  |  | struct PFS0Superblock { | 
					
						
							|  |  |  |     NCASectionHeaderBlock header_block; | 
					
						
							|  |  |  |     std::array<u8, 0x20> hash; | 
					
						
							|  |  |  |     u32_le size; | 
					
						
							|  |  |  |     INSERT_PADDING_BYTES(4); | 
					
						
							|  |  |  |     u64_le hash_table_offset; | 
					
						
							|  |  |  |     u64_le hash_table_size; | 
					
						
							|  |  |  |     u64_le pfs0_header_offset; | 
					
						
							|  |  |  |     u64_le pfs0_size; | 
					
						
							| 
									
										
										
										
											2018-07-27 23:55:23 -04:00
										 |  |  |     INSERT_PADDING_BYTES(0x1B0); | 
					
						
							| 
									
										
										
										
											2018-07-18 21:07:11 -04:00
										 |  |  | }; | 
					
						
							|  |  |  | static_assert(sizeof(PFS0Superblock) == 0x200, "PFS0Superblock has incorrect size."); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct RomFSSuperblock { | 
					
						
							|  |  |  |     NCASectionHeaderBlock header_block; | 
					
						
							| 
									
										
										
										
											2018-07-27 18:14:03 -04:00
										 |  |  |     IVFCHeader ivfc; | 
					
						
							| 
									
										
										
										
											2018-07-27 23:55:23 -04:00
										 |  |  |     INSERT_PADDING_BYTES(0x118); | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | static_assert(sizeof(RomFSSuperblock) == 0x200, "RomFSSuperblock has incorrect size."); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | union NCASectionHeader { | 
					
						
							|  |  |  |     NCASectionRaw raw; | 
					
						
							|  |  |  |     PFS0Superblock pfs0; | 
					
						
							|  |  |  |     RomFSSuperblock romfs; | 
					
						
							| 
									
										
										
										
											2018-07-18 21:07:11 -04:00
										 |  |  | }; | 
					
						
							| 
									
										
										
										
											2018-07-27 23:55:23 -04:00
										 |  |  | static_assert(sizeof(NCASectionHeader) == 0x200, "NCASectionHeader has incorrect size."); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool IsValidNCA(const NCAHeader& header) { | 
					
						
							|  |  |  |     // TODO(DarkLordZach): Add NCA2/NCA0 support.
 | 
					
						
							|  |  |  |     return header.magic == Common::MakeMagic('N', 'C', 'A', '3'); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-04 14:57:21 -04:00
										 |  |  | u8 NCA::GetCryptoRevision() const { | 
					
						
							| 
									
										
										
										
											2018-07-27 23:55:23 -04:00
										 |  |  |     u8 master_key_id = header.crypto_type; | 
					
						
							|  |  |  |     if (header.crypto_type_2 > master_key_id) | 
					
						
							|  |  |  |         master_key_id = header.crypto_type_2; | 
					
						
							|  |  |  |     if (master_key_id > 0) | 
					
						
							|  |  |  |         --master_key_id; | 
					
						
							| 
									
										
										
										
											2018-08-04 14:57:21 -04:00
										 |  |  |     return master_key_id; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | boost::optional<Core::Crypto::Key128> NCA::GetKeyAreaKey(NCASectionCryptoType type) const { | 
					
						
							|  |  |  |     const auto master_key_id = GetCryptoRevision(); | 
					
						
							| 
									
										
										
										
											2018-07-27 23:55:23 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-29 20:47:33 -04:00
										 |  |  |     if (!keys.HasKey(Core::Crypto::S128KeyType::KeyArea, master_key_id, header.key_index)) | 
					
						
							|  |  |  |         return boost::none; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-27 23:55:23 -04:00
										 |  |  |     std::vector<u8> key_area(header.key_area.begin(), header.key_area.end()); | 
					
						
							| 
									
										
										
										
											2018-07-28 21:39:42 -04:00
										 |  |  |     Core::Crypto::AESCipher<Core::Crypto::Key128> cipher( | 
					
						
							|  |  |  |         keys.GetKey(Core::Crypto::S128KeyType::KeyArea, master_key_id, header.key_index), | 
					
						
							|  |  |  |         Core::Crypto::Mode::ECB); | 
					
						
							|  |  |  |     cipher.Transcode(key_area.data(), key_area.size(), key_area.data(), Core::Crypto::Op::Decrypt); | 
					
						
							| 
									
										
										
										
											2018-07-27 23:55:23 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-28 21:39:42 -04:00
										 |  |  |     Core::Crypto::Key128 out; | 
					
						
							| 
									
										
										
										
											2018-07-27 23:55:23 -04:00
										 |  |  |     if (type == NCASectionCryptoType::XTS) | 
					
						
							|  |  |  |         std::copy(key_area.begin(), key_area.begin() + 0x10, out.begin()); | 
					
						
							|  |  |  |     else if (type == NCASectionCryptoType::CTR) | 
					
						
							|  |  |  |         std::copy(key_area.begin() + 0x20, key_area.begin() + 0x30, out.begin()); | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |         LOG_CRITICAL(Crypto, "Called GetKeyAreaKey on invalid NCASectionCryptoType type={:02X}", | 
					
						
							|  |  |  |                      static_cast<u8>(type)); | 
					
						
							| 
									
										
										
										
											2018-07-28 21:39:42 -04:00
										 |  |  |     u128 out_128{}; | 
					
						
							|  |  |  |     memcpy(out_128.data(), out.data(), 16); | 
					
						
							| 
									
										
										
										
											2018-07-27 23:55:23 -04:00
										 |  |  |     LOG_DEBUG(Crypto, "called with crypto_rev={:02X}, kak_index={:02X}, key={:016X}{:016X}", | 
					
						
							|  |  |  |               master_key_id, header.key_index, out_128[1], out_128[0]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return out; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-09 21:06:44 -04:00
										 |  |  | boost::optional<Core::Crypto::Key128> NCA::GetTitlekey() { | 
					
						
							| 
									
										
										
										
											2018-08-04 14:57:21 -04:00
										 |  |  |     const auto master_key_id = GetCryptoRevision(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     u128 rights_id{}; | 
					
						
							|  |  |  |     memcpy(rights_id.data(), header.rights_id.data(), 16); | 
					
						
							| 
									
										
										
										
											2018-08-09 21:06:44 -04:00
										 |  |  |     if (rights_id == u128{}) { | 
					
						
							|  |  |  |         status = Loader::ResultStatus::ErrorInvalidRightsID; | 
					
						
							| 
									
										
										
										
											2018-08-04 14:57:21 -04:00
										 |  |  |         return boost::none; | 
					
						
							| 
									
										
										
										
											2018-08-09 21:06:44 -04:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-08-04 14:57:21 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     auto titlekey = keys.GetKey(Core::Crypto::S128KeyType::Titlekey, rights_id[1], rights_id[0]); | 
					
						
							| 
									
										
										
										
											2018-08-09 21:06:44 -04:00
										 |  |  |     if (titlekey == Core::Crypto::Key128{}) { | 
					
						
							|  |  |  |         status = Loader::ResultStatus::ErrorMissingTitlekey; | 
					
						
							|  |  |  |         return boost::none; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!keys.HasKey(Core::Crypto::S128KeyType::Titlekek, master_key_id)) { | 
					
						
							|  |  |  |         status = Loader::ResultStatus::ErrorMissingTitlekek; | 
					
						
							| 
									
										
										
										
											2018-08-04 14:57:21 -04:00
										 |  |  |         return boost::none; | 
					
						
							| 
									
										
										
										
											2018-08-09 21:06:44 -04:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-04 14:57:21 -04:00
										 |  |  |     Core::Crypto::AESCipher<Core::Crypto::Key128> cipher( | 
					
						
							|  |  |  |         keys.GetKey(Core::Crypto::S128KeyType::Titlekek, master_key_id), Core::Crypto::Mode::ECB); | 
					
						
							|  |  |  |     cipher.Transcode(titlekey.data(), titlekey.size(), titlekey.data(), Core::Crypto::Op::Decrypt); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return titlekey; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-09 21:06:44 -04:00
										 |  |  | VirtualFile NCA::Decrypt(NCASectionHeader s_header, VirtualFile in, u64 starting_offset) { | 
					
						
							| 
									
										
										
										
											2018-07-27 23:55:23 -04:00
										 |  |  |     if (!encrypted) | 
					
						
							|  |  |  |         return in; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-04 14:57:21 -04:00
										 |  |  |     switch (s_header.raw.header.crypto_type) { | 
					
						
							| 
									
										
										
										
											2018-07-27 23:55:23 -04:00
										 |  |  |     case NCASectionCryptoType::NONE: | 
					
						
							|  |  |  |         LOG_DEBUG(Crypto, "called with mode=NONE"); | 
					
						
							|  |  |  |         return in; | 
					
						
							|  |  |  |     case NCASectionCryptoType::CTR: | 
					
						
							|  |  |  |         LOG_DEBUG(Crypto, "called with mode=CTR, starting_offset={:016X}", starting_offset); | 
					
						
							|  |  |  |         { | 
					
						
							| 
									
										
										
										
											2018-08-04 14:57:21 -04:00
										 |  |  |             boost::optional<Core::Crypto::Key128> key = boost::none; | 
					
						
							| 
									
										
										
										
											2018-08-09 21:06:44 -04:00
										 |  |  |             if (has_rights_id) { | 
					
						
							|  |  |  |                 status = Loader::ResultStatus::Success; | 
					
						
							| 
									
										
										
										
											2018-08-04 14:57:21 -04:00
										 |  |  |                 key = GetTitlekey(); | 
					
						
							| 
									
										
										
										
											2018-08-09 21:06:44 -04:00
										 |  |  |                 if (key == boost::none) { | 
					
						
							|  |  |  |                     if (status == Loader::ResultStatus::Success) | 
					
						
							|  |  |  |                         status = Loader::ResultStatus::ErrorMissingTitlekey; | 
					
						
							|  |  |  |                     return nullptr; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |                 key = GetKeyAreaKey(NCASectionCryptoType::CTR); | 
					
						
							|  |  |  |                 if (key == boost::none) { | 
					
						
							|  |  |  |                     status = Loader::ResultStatus::ErrorMissingKeyAreaKey; | 
					
						
							|  |  |  |                     return nullptr; | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2018-08-04 14:57:21 -04:00
										 |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-28 21:39:42 -04:00
										 |  |  |             auto out = std::make_shared<Core::Crypto::CTREncryptionLayer>( | 
					
						
							| 
									
										
										
										
											2018-07-29 20:47:33 -04:00
										 |  |  |                 std::move(in), key.value(), starting_offset); | 
					
						
							| 
									
										
										
										
											2018-07-28 21:39:42 -04:00
										 |  |  |             std::vector<u8> iv(16); | 
					
						
							| 
									
										
										
										
											2018-07-27 23:55:23 -04:00
										 |  |  |             for (u8 i = 0; i < 8; ++i) | 
					
						
							| 
									
										
										
										
											2018-08-04 14:57:21 -04:00
										 |  |  |                 iv[i] = s_header.raw.section_ctr[0x8 - i - 1]; | 
					
						
							| 
									
										
										
										
											2018-07-27 23:55:23 -04:00
										 |  |  |             out->SetIV(iv); | 
					
						
							| 
									
										
										
										
											2018-07-29 20:47:33 -04:00
										 |  |  |             return std::static_pointer_cast<VfsFile>(out); | 
					
						
							| 
									
										
										
										
											2018-07-27 23:55:23 -04:00
										 |  |  |         } | 
					
						
							|  |  |  |     case NCASectionCryptoType::XTS: | 
					
						
							| 
									
										
										
										
											2018-08-04 14:57:21 -04:00
										 |  |  |         // TODO(DarkLordZach): Implement XTSEncryptionLayer.
 | 
					
						
							| 
									
										
										
										
											2018-07-27 23:55:23 -04:00
										 |  |  |     default: | 
					
						
							|  |  |  |         LOG_ERROR(Crypto, "called with unhandled crypto type={:02X}", | 
					
						
							| 
									
										
										
										
											2018-08-04 14:57:21 -04:00
										 |  |  |                   static_cast<u8>(s_header.raw.header.crypto_type)); | 
					
						
							| 
									
										
										
										
											2018-07-27 23:55:23 -04:00
										 |  |  |         return nullptr; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2018-07-18 21:07:11 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-18 23:12:14 -04:00
										 |  |  | NCA::NCA(VirtualFile file_) : file(std::move(file_)) { | 
					
						
							| 
									
										
										
										
											2018-08-09 21:06:44 -04:00
										 |  |  |     status = Loader::ResultStatus::Success; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-06 23:13:37 -04:00
										 |  |  |     if (file == nullptr) { | 
					
						
							| 
									
										
										
										
											2018-08-09 21:06:44 -04:00
										 |  |  |         status = Loader::ResultStatus::ErrorNullFile; | 
					
						
							| 
									
										
										
										
											2018-08-06 23:13:37 -04:00
										 |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-08-09 21:06:44 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (sizeof(NCAHeader) != file->ReadObject(&header)) { | 
					
						
							| 
									
										
										
										
											2018-07-27 23:55:23 -04:00
										 |  |  |         LOG_ERROR(Loader, "File reader errored out during header read."); | 
					
						
							| 
									
										
										
										
											2018-08-09 21:06:44 -04:00
										 |  |  |         status = Loader::ResultStatus::ErrorBadNCAHeader; | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-07-27 23:55:23 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     encrypted = false; | 
					
						
							| 
									
										
										
										
											2018-07-18 21:07:11 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (!IsValidNCA(header)) { | 
					
						
							| 
									
										
										
										
											2018-08-09 21:06:44 -04:00
										 |  |  |         if (header.magic == Common::MakeMagic('N', 'C', 'A', '2')) { | 
					
						
							|  |  |  |             status = Loader::ResultStatus::ErrorNCA2; | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (header.magic == Common::MakeMagic('N', 'C', 'A', '0')) { | 
					
						
							|  |  |  |             status = Loader::ResultStatus::ErrorNCA0; | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-27 23:55:23 -04:00
										 |  |  |         NCAHeader dec_header{}; | 
					
						
							| 
									
										
										
										
											2018-07-28 21:39:42 -04:00
										 |  |  |         Core::Crypto::AESCipher<Core::Crypto::Key256> cipher( | 
					
						
							|  |  |  |             keys.GetKey(Core::Crypto::S256KeyType::Header), Core::Crypto::Mode::XTS); | 
					
						
							|  |  |  |         cipher.XTSTranscode(&header, sizeof(NCAHeader), &dec_header, 0, 0x200, | 
					
						
							|  |  |  |                             Core::Crypto::Op::Decrypt); | 
					
						
							| 
									
										
										
										
											2018-07-27 23:55:23 -04:00
										 |  |  |         if (IsValidNCA(dec_header)) { | 
					
						
							|  |  |  |             header = dec_header; | 
					
						
							|  |  |  |             encrypted = true; | 
					
						
							|  |  |  |         } else { | 
					
						
							| 
									
										
										
										
											2018-08-09 21:06:44 -04:00
										 |  |  |             if (dec_header.magic == Common::MakeMagic('N', 'C', 'A', '2')) { | 
					
						
							|  |  |  |                 status = Loader::ResultStatus::ErrorNCA2; | 
					
						
							|  |  |  |                 return; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             if (dec_header.magic == Common::MakeMagic('N', 'C', 'A', '0')) { | 
					
						
							|  |  |  |                 status = Loader::ResultStatus::ErrorNCA0; | 
					
						
							|  |  |  |                 return; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-29 20:47:33 -04:00
										 |  |  |             if (!keys.HasKey(Core::Crypto::S256KeyType::Header)) | 
					
						
							| 
									
										
										
										
											2018-08-09 21:06:44 -04:00
										 |  |  |                 status = Loader::ResultStatus::ErrorMissingHeaderKey; | 
					
						
							| 
									
										
										
										
											2018-07-29 20:47:33 -04:00
										 |  |  |             else | 
					
						
							| 
									
										
										
										
											2018-08-09 21:06:44 -04:00
										 |  |  |                 status = Loader::ResultStatus::ErrorIncorrectHeaderKey; | 
					
						
							| 
									
										
										
										
											2018-07-27 23:55:23 -04:00
										 |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2018-07-18 21:07:11 -04:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-09 21:06:44 -04:00
										 |  |  |     has_rights_id = std::find_if_not(header.rights_id.begin(), header.rights_id.end(), | 
					
						
							|  |  |  |                                      [](char c) { return c == '\0'; }) != header.rights_id.end(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-27 23:55:23 -04:00
										 |  |  |     const std::ptrdiff_t number_sections = | 
					
						
							| 
									
										
										
										
											2018-07-18 21:07:11 -04:00
										 |  |  |         std::count_if(std::begin(header.section_tables), std::end(header.section_tables), | 
					
						
							|  |  |  |                       [](NCASectionTableEntry entry) { return entry.media_offset > 0; }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-27 23:55:23 -04:00
										 |  |  |     std::vector<NCASectionHeader> sections(number_sections); | 
					
						
							|  |  |  |     const auto length_sections = SECTION_HEADER_SIZE * number_sections; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (encrypted) { | 
					
						
							|  |  |  |         auto raw = file->ReadBytes(length_sections, SECTION_HEADER_OFFSET); | 
					
						
							| 
									
										
										
										
											2018-07-28 21:39:42 -04:00
										 |  |  |         Core::Crypto::AESCipher<Core::Crypto::Key256> cipher( | 
					
						
							|  |  |  |             keys.GetKey(Core::Crypto::S256KeyType::Header), Core::Crypto::Mode::XTS); | 
					
						
							| 
									
										
										
										
											2018-07-27 23:55:23 -04:00
										 |  |  |         cipher.XTSTranscode(raw.data(), length_sections, sections.data(), 2, SECTION_HEADER_SIZE, | 
					
						
							| 
									
										
										
										
											2018-07-28 21:39:42 -04:00
										 |  |  |                             Core::Crypto::Op::Decrypt); | 
					
						
							| 
									
										
										
										
											2018-07-27 23:55:23 -04:00
										 |  |  |     } else { | 
					
						
							|  |  |  |         file->ReadBytes(sections.data(), length_sections, SECTION_HEADER_OFFSET); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-18 21:07:11 -04:00
										 |  |  |     for (std::ptrdiff_t i = 0; i < number_sections; ++i) { | 
					
						
							| 
									
										
										
										
											2018-07-27 23:55:23 -04:00
										 |  |  |         auto section = sections[i]; | 
					
						
							| 
									
										
										
										
											2018-07-18 21:07:11 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-27 23:55:23 -04:00
										 |  |  |         if (section.raw.header.filesystem_type == NCASectionFilesystemType::ROMFS) { | 
					
						
							| 
									
										
										
										
											2018-07-18 21:07:11 -04:00
										 |  |  |             const size_t romfs_offset = | 
					
						
							|  |  |  |                 header.section_tables[i].media_offset * MEDIA_OFFSET_MULTIPLIER + | 
					
						
							| 
									
										
										
										
											2018-07-27 23:55:23 -04:00
										 |  |  |                 section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset; | 
					
						
							|  |  |  |             const size_t romfs_size = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].size; | 
					
						
							| 
									
										
										
										
											2018-07-31 12:27:14 -04:00
										 |  |  |             auto dec = | 
					
						
							| 
									
										
										
										
											2018-07-27 23:55:23 -04:00
										 |  |  |                 Decrypt(section, std::make_shared<OffsetVfsFile>(file, romfs_size, romfs_offset), | 
					
						
							| 
									
										
										
										
											2018-07-29 20:47:33 -04:00
										 |  |  |                         romfs_offset); | 
					
						
							|  |  |  |             if (dec != nullptr) { | 
					
						
							| 
									
										
										
										
											2018-07-31 12:27:14 -04:00
										 |  |  |                 files.push_back(std::move(dec)); | 
					
						
							| 
									
										
										
										
											2018-07-29 20:47:33 -04:00
										 |  |  |                 romfs = files.back(); | 
					
						
							| 
									
										
										
										
											2018-07-30 12:46:23 -04:00
										 |  |  |             } else { | 
					
						
							| 
									
										
										
										
											2018-08-09 21:06:44 -04:00
										 |  |  |                 if (status != Loader::ResultStatus::Success) | 
					
						
							|  |  |  |                     return; | 
					
						
							|  |  |  |                 if (has_rights_id) | 
					
						
							|  |  |  |                     status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek; | 
					
						
							|  |  |  |                 else | 
					
						
							|  |  |  |                     status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey; | 
					
						
							| 
									
										
										
										
											2018-07-30 12:46:23 -04:00
										 |  |  |                 return; | 
					
						
							| 
									
										
										
										
											2018-07-29 20:47:33 -04:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2018-07-27 23:55:23 -04:00
										 |  |  |         } else if (section.raw.header.filesystem_type == NCASectionFilesystemType::PFS0) { | 
					
						
							| 
									
										
										
										
											2018-07-18 21:07:11 -04:00
										 |  |  |             u64 offset = (static_cast<u64>(header.section_tables[i].media_offset) * | 
					
						
							|  |  |  |                           MEDIA_OFFSET_MULTIPLIER) + | 
					
						
							| 
									
										
										
										
											2018-07-27 23:55:23 -04:00
										 |  |  |                          section.pfs0.pfs0_header_offset; | 
					
						
							| 
									
										
										
										
											2018-07-18 21:07:11 -04:00
										 |  |  |             u64 size = MEDIA_OFFSET_MULTIPLIER * (header.section_tables[i].media_end_offset - | 
					
						
							|  |  |  |                                                   header.section_tables[i].media_offset); | 
					
						
							| 
									
										
										
										
											2018-07-31 12:27:14 -04:00
										 |  |  |             auto dec = | 
					
						
							| 
									
										
										
										
											2018-07-29 20:47:33 -04:00
										 |  |  |                 Decrypt(section, std::make_shared<OffsetVfsFile>(file, size, offset), offset); | 
					
						
							|  |  |  |             if (dec != nullptr) { | 
					
						
							| 
									
										
										
										
											2018-07-31 12:27:14 -04:00
										 |  |  |                 auto npfs = std::make_shared<PartitionFilesystem>(std::move(dec)); | 
					
						
							| 
									
										
										
										
											2018-07-29 20:47:33 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |                 if (npfs->GetStatus() == Loader::ResultStatus::Success) { | 
					
						
							| 
									
										
										
										
											2018-07-31 12:27:14 -04:00
										 |  |  |                     dirs.push_back(std::move(npfs)); | 
					
						
							| 
									
										
										
										
											2018-07-29 20:47:33 -04:00
										 |  |  |                     if (IsDirectoryExeFS(dirs.back())) | 
					
						
							|  |  |  |                         exefs = dirs.back(); | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2018-07-30 12:46:23 -04:00
										 |  |  |             } else { | 
					
						
							| 
									
										
										
										
											2018-08-09 21:06:44 -04:00
										 |  |  |                 if (status != Loader::ResultStatus::Success) | 
					
						
							|  |  |  |                     return; | 
					
						
							|  |  |  |                 if (has_rights_id) | 
					
						
							|  |  |  |                     status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek; | 
					
						
							|  |  |  |                 else | 
					
						
							|  |  |  |                     status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey; | 
					
						
							| 
									
										
										
										
											2018-07-30 12:46:23 -04:00
										 |  |  |                 return; | 
					
						
							| 
									
										
										
										
											2018-07-18 21:07:11 -04:00
										 |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     status = Loader::ResultStatus::Success; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Loader::ResultStatus NCA::GetStatus() const { | 
					
						
							|  |  |  |     return status; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | std::vector<std::shared_ptr<VfsFile>> NCA::GetFiles() const { | 
					
						
							|  |  |  |     if (status != Loader::ResultStatus::Success) | 
					
						
							|  |  |  |         return {}; | 
					
						
							|  |  |  |     return files; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | std::vector<std::shared_ptr<VfsDirectory>> NCA::GetSubdirectories() const { | 
					
						
							|  |  |  |     if (status != Loader::ResultStatus::Success) | 
					
						
							|  |  |  |         return {}; | 
					
						
							|  |  |  |     return dirs; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | std::string NCA::GetName() const { | 
					
						
							|  |  |  |     return file->GetName(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | std::shared_ptr<VfsDirectory> NCA::GetParentDirectory() const { | 
					
						
							|  |  |  |     return file->GetContainingDirectory(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | NCAContentType NCA::GetType() const { | 
					
						
							|  |  |  |     return header.content_type; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | u64 NCA::GetTitleId() const { | 
					
						
							|  |  |  |     if (status != Loader::ResultStatus::Success) | 
					
						
							|  |  |  |         return {}; | 
					
						
							|  |  |  |     return header.title_id; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | VirtualFile NCA::GetRomFS() const { | 
					
						
							|  |  |  |     return romfs; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | VirtualDir NCA::GetExeFS() const { | 
					
						
							|  |  |  |     return exefs; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-27 23:55:23 -04:00
										 |  |  | VirtualFile NCA::GetBaseFile() const { | 
					
						
							|  |  |  |     return file; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-18 21:07:11 -04:00
										 |  |  | bool NCA::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { | 
					
						
							|  |  |  |     return false; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | } // namespace FileSys
 |