loader: Add more descriptive errors
Full list of new errors and descriptions in core/loader/loader.h
This commit is contained in:
		| @@ -102,18 +102,8 @@ System::ResultStatus System::Load(EmuWindow& emu_window, const std::string& file | |||||||
|         LOG_CRITICAL(Core, "Failed to determine system mode (Error {})!", |         LOG_CRITICAL(Core, "Failed to determine system mode (Error {})!", | ||||||
|                      static_cast<int>(system_mode.second)); |                      static_cast<int>(system_mode.second)); | ||||||
|  |  | ||||||
|         switch (system_mode.second) { |         if (system_mode.second != Loader::ResultStatus::Success) | ||||||
|         case Loader::ResultStatus::ErrorMissingKeys: |  | ||||||
|             return ResultStatus::ErrorLoader_ErrorMissingKeys; |  | ||||||
|         case Loader::ResultStatus::ErrorDecrypting: |  | ||||||
|             return ResultStatus::ErrorLoader_ErrorDecrypting; |  | ||||||
|         case Loader::ResultStatus::ErrorInvalidFormat: |  | ||||||
|             return ResultStatus::ErrorLoader_ErrorInvalidFormat; |  | ||||||
|         case Loader::ResultStatus::ErrorUnsupportedArch: |  | ||||||
|             return ResultStatus::ErrorUnsupportedArch; |  | ||||||
|         default: |  | ||||||
|             return ResultStatus::ErrorSystemMode; |             return ResultStatus::ErrorSystemMode; | ||||||
|         } |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     ResultStatus init_result{Init(emu_window)}; |     ResultStatus init_result{Init(emu_window)}; | ||||||
| @@ -129,17 +119,9 @@ System::ResultStatus System::Load(EmuWindow& emu_window, const std::string& file | |||||||
|         LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", static_cast<int>(load_result)); |         LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", static_cast<int>(load_result)); | ||||||
|         System::Shutdown(); |         System::Shutdown(); | ||||||
|  |  | ||||||
|         switch (load_result) { |         if (load_result != Loader::ResultStatus::Success) { | ||||||
|         case Loader::ResultStatus::ErrorMissingKeys: |             return static_cast<ResultStatus>(static_cast<u32>(ResultStatus::ErrorLoader) + | ||||||
|             return ResultStatus::ErrorLoader_ErrorMissingKeys; |                                              static_cast<u32>(load_result)); | ||||||
|         case Loader::ResultStatus::ErrorDecrypting: |  | ||||||
|             return ResultStatus::ErrorLoader_ErrorDecrypting; |  | ||||||
|         case Loader::ResultStatus::ErrorInvalidFormat: |  | ||||||
|             return ResultStatus::ErrorLoader_ErrorInvalidFormat; |  | ||||||
|         case Loader::ResultStatus::ErrorUnsupportedArch: |  | ||||||
|             return ResultStatus::ErrorUnsupportedArch; |  | ||||||
|         default: |  | ||||||
|             return ResultStatus::ErrorLoader; |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     status = ResultStatus::Success; |     status = ResultStatus::Success; | ||||||
|   | |||||||
| @@ -49,21 +49,15 @@ public: | |||||||
|  |  | ||||||
|     /// Enumeration representing the return values of the System Initialize and Load process. |     /// Enumeration representing the return values of the System Initialize and Load process. | ||||||
|     enum class ResultStatus : u32 { |     enum class ResultStatus : u32 { | ||||||
|         Success,                      ///< Succeeded |         Success,             ///< Succeeded | ||||||
|         ErrorNotInitialized,          ///< Error trying to use core prior to initialization |         ErrorNotInitialized, ///< Error trying to use core prior to initialization | ||||||
|         ErrorGetLoader,               ///< Error finding the correct application loader |         ErrorGetLoader,      ///< Error finding the correct application loader | ||||||
|         ErrorSystemMode,              ///< Error determining the system mode |         ErrorSystemMode,     ///< Error determining the system mode | ||||||
|         ErrorLoader,                  ///< Error loading the specified application |         ErrorSystemFiles,    ///< Error in finding system files | ||||||
|         ErrorLoader_ErrorMissingKeys, ///< Error because the key/keys needed to run could not be |         ErrorSharedFont,     ///< Error in finding shared font | ||||||
|                                       ///< found. |         ErrorVideoCore,      ///< Error in the video core | ||||||
|         ErrorLoader_ErrorDecrypting,  ///< Error loading the specified application due to encryption |         ErrorUnknown,        ///< Any other error | ||||||
|         ErrorLoader_ErrorInvalidFormat, ///< Error loading the specified application due to an |         ErrorLoader,         ///< The base for loader errors (too many to repeat) | ||||||
|                                         /// invalid format |  | ||||||
|         ErrorSystemFiles,               ///< Error in finding system files |  | ||||||
|         ErrorSharedFont,                ///< Error in finding shared font |  | ||||||
|         ErrorVideoCore,                 ///< Error in the video core |  | ||||||
|         ErrorUnsupportedArch,           ///< Unsupported Architecture (32-Bit ROMs) |  | ||||||
|         ErrorUnknown                    ///< Any other error |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|   | |||||||
| @@ -12,14 +12,16 @@ | |||||||
|  |  | ||||||
| namespace FileSys { | namespace FileSys { | ||||||
|  |  | ||||||
|  | constexpr std::array<const char*, 0x4> partition_names = {"update", "normal", "secure", "logo"}; | ||||||
|  |  | ||||||
| XCI::XCI(VirtualFile file_) : file(std::move(file_)), partitions(0x4) { | XCI::XCI(VirtualFile file_) : file(std::move(file_)), partitions(0x4) { | ||||||
|     if (file->ReadObject(&header) != sizeof(GamecardHeader)) { |     if (file->ReadObject(&header) != sizeof(GamecardHeader)) { | ||||||
|         status = Loader::ResultStatus::ErrorInvalidFormat; |         status = Loader::ResultStatus::ErrorBadXCIHeader; | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (header.magic != Common::MakeMagic('H', 'E', 'A', 'D')) { |     if (header.magic != Common::MakeMagic('H', 'E', 'A', 'D')) { | ||||||
|         status = Loader::ResultStatus::ErrorInvalidFormat; |         status = Loader::ResultStatus::ErrorBadXCIHeader; | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -31,9 +33,6 @@ XCI::XCI(VirtualFile file_) : file(std::move(file_)), partitions(0x4) { | |||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     static constexpr std::array<const char*, 0x4> partition_names = {"update", "normal", "secure", |  | ||||||
|                                                                      "logo"}; |  | ||||||
|  |  | ||||||
|     for (XCIPartition partition : |     for (XCIPartition partition : | ||||||
|          {XCIPartition::Update, XCIPartition::Normal, XCIPartition::Secure, XCIPartition::Logo}) { |          {XCIPartition::Update, XCIPartition::Normal, XCIPartition::Secure, XCIPartition::Logo}) { | ||||||
|         auto raw = main_hfs.GetFile(partition_names[static_cast<size_t>(partition)]); |         auto raw = main_hfs.GetFile(partition_names[static_cast<size_t>(partition)]); | ||||||
| @@ -130,15 +129,21 @@ bool XCI::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { | |||||||
|  |  | ||||||
| Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) { | Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) { | ||||||
|     if (partitions[static_cast<size_t>(part)] == nullptr) { |     if (partitions[static_cast<size_t>(part)] == nullptr) { | ||||||
|         return Loader::ResultStatus::ErrorInvalidFormat; |         return Loader::ResultStatus::ErrorXCIMissingPartition; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     for (const VirtualFile& file : partitions[static_cast<size_t>(part)]->GetFiles()) { |     for (const VirtualFile& file : partitions[static_cast<size_t>(part)]->GetFiles()) { | ||||||
|         if (file->GetExtension() != "nca") |         if (file->GetExtension() != "nca") | ||||||
|             continue; |             continue; | ||||||
|         auto nca = std::make_shared<NCA>(file); |         auto nca = std::make_shared<NCA>(file); | ||||||
|         if (nca->GetStatus() == Loader::ResultStatus::Success) |         if (nca->GetStatus() == Loader::ResultStatus::Success) { | ||||||
|             ncas.push_back(std::move(nca)); |             ncas.push_back(std::move(nca)); | ||||||
|  |         } else { | ||||||
|  |             const u16 error_id = static_cast<u16>(nca->GetStatus()); | ||||||
|  |             LOG_CRITICAL(Loader, "Could not load NCA {}/{}, failed with error code {:04X} ({})", | ||||||
|  |                          partition_names[static_cast<size_t>(part)], nca->GetName(), error_id, | ||||||
|  |                          Loader::GetMessageForResultStatus(nca->GetStatus())); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return Loader::ResultStatus::Success; |     return Loader::ResultStatus::Success; | ||||||
|   | |||||||
| @@ -113,17 +113,27 @@ boost::optional<Core::Crypto::Key128> NCA::GetKeyAreaKey(NCASectionCryptoType ty | |||||||
|     return out; |     return out; | ||||||
| } | } | ||||||
|  |  | ||||||
| boost::optional<Core::Crypto::Key128> NCA::GetTitlekey() const { | boost::optional<Core::Crypto::Key128> NCA::GetTitlekey() { | ||||||
|     const auto master_key_id = GetCryptoRevision(); |     const auto master_key_id = GetCryptoRevision(); | ||||||
|  |  | ||||||
|     u128 rights_id{}; |     u128 rights_id{}; | ||||||
|     memcpy(rights_id.data(), header.rights_id.data(), 16); |     memcpy(rights_id.data(), header.rights_id.data(), 16); | ||||||
|     if (rights_id == u128{}) |     if (rights_id == u128{}) { | ||||||
|  |         status = Loader::ResultStatus::ErrorInvalidRightsID; | ||||||
|         return boost::none; |         return boost::none; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     auto titlekey = keys.GetKey(Core::Crypto::S128KeyType::Titlekey, rights_id[1], rights_id[0]); |     auto titlekey = keys.GetKey(Core::Crypto::S128KeyType::Titlekey, rights_id[1], rights_id[0]); | ||||||
|     if (titlekey == Core::Crypto::Key128{}) |     if (titlekey == Core::Crypto::Key128{}) { | ||||||
|  |         status = Loader::ResultStatus::ErrorMissingTitlekey; | ||||||
|         return boost::none; |         return boost::none; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (!keys.HasKey(Core::Crypto::S128KeyType::Titlekek, master_key_id)) { | ||||||
|  |         status = Loader::ResultStatus::ErrorMissingTitlekek; | ||||||
|  |         return boost::none; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     Core::Crypto::AESCipher<Core::Crypto::Key128> cipher( |     Core::Crypto::AESCipher<Core::Crypto::Key128> cipher( | ||||||
|         keys.GetKey(Core::Crypto::S128KeyType::Titlekek, master_key_id), Core::Crypto::Mode::ECB); |         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); |     cipher.Transcode(titlekey.data(), titlekey.size(), titlekey.data(), Core::Crypto::Op::Decrypt); | ||||||
| @@ -131,7 +141,7 @@ boost::optional<Core::Crypto::Key128> NCA::GetTitlekey() const { | |||||||
|     return titlekey; |     return titlekey; | ||||||
| } | } | ||||||
|  |  | ||||||
| VirtualFile NCA::Decrypt(NCASectionHeader s_header, VirtualFile in, u64 starting_offset) const { | VirtualFile NCA::Decrypt(NCASectionHeader s_header, VirtualFile in, u64 starting_offset) { | ||||||
|     if (!encrypted) |     if (!encrypted) | ||||||
|         return in; |         return in; | ||||||
|  |  | ||||||
| @@ -143,15 +153,22 @@ VirtualFile NCA::Decrypt(NCASectionHeader s_header, VirtualFile in, u64 starting | |||||||
|         LOG_DEBUG(Crypto, "called with mode=CTR, starting_offset={:016X}", starting_offset); |         LOG_DEBUG(Crypto, "called with mode=CTR, starting_offset={:016X}", starting_offset); | ||||||
|         { |         { | ||||||
|             boost::optional<Core::Crypto::Key128> key = boost::none; |             boost::optional<Core::Crypto::Key128> key = boost::none; | ||||||
|             if (std::find_if_not(header.rights_id.begin(), header.rights_id.end(), |             if (has_rights_id) { | ||||||
|                                  [](char c) { return c == 0; }) == header.rights_id.end()) { |                 status = Loader::ResultStatus::Success; | ||||||
|                 key = GetKeyAreaKey(NCASectionCryptoType::CTR); |  | ||||||
|             } else { |  | ||||||
|                 key = GetTitlekey(); |                 key = GetTitlekey(); | ||||||
|  |                 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; | ||||||
|  |                 } | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             if (key == boost::none) |  | ||||||
|                 return nullptr; |  | ||||||
|             auto out = std::make_shared<Core::Crypto::CTREncryptionLayer>( |             auto out = std::make_shared<Core::Crypto::CTREncryptionLayer>( | ||||||
|                 std::move(in), key.value(), starting_offset); |                 std::move(in), key.value(), starting_offset); | ||||||
|             std::vector<u8> iv(16); |             std::vector<u8> iv(16); | ||||||
| @@ -170,16 +187,31 @@ VirtualFile NCA::Decrypt(NCASectionHeader s_header, VirtualFile in, u64 starting | |||||||
| } | } | ||||||
|  |  | ||||||
| NCA::NCA(VirtualFile file_) : file(std::move(file_)) { | NCA::NCA(VirtualFile file_) : file(std::move(file_)) { | ||||||
|  |     status = Loader::ResultStatus::Success; | ||||||
|  |  | ||||||
|     if (file == nullptr) { |     if (file == nullptr) { | ||||||
|         status = Loader::ResultStatus::ErrorInvalidFormat; |         status = Loader::ResultStatus::ErrorNullFile; | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|     if (sizeof(NCAHeader) != file->ReadObject(&header)) |  | ||||||
|  |     if (sizeof(NCAHeader) != file->ReadObject(&header)) { | ||||||
|         LOG_ERROR(Loader, "File reader errored out during header read."); |         LOG_ERROR(Loader, "File reader errored out during header read."); | ||||||
|  |         status = Loader::ResultStatus::ErrorBadNCAHeader; | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     encrypted = false; |     encrypted = false; | ||||||
|  |  | ||||||
|     if (!IsValidNCA(header)) { |     if (!IsValidNCA(header)) { | ||||||
|  |         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; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         NCAHeader dec_header{}; |         NCAHeader dec_header{}; | ||||||
|         Core::Crypto::AESCipher<Core::Crypto::Key256> cipher( |         Core::Crypto::AESCipher<Core::Crypto::Key256> cipher( | ||||||
|             keys.GetKey(Core::Crypto::S256KeyType::Header), Core::Crypto::Mode::XTS); |             keys.GetKey(Core::Crypto::S256KeyType::Header), Core::Crypto::Mode::XTS); | ||||||
| @@ -189,14 +221,26 @@ NCA::NCA(VirtualFile file_) : file(std::move(file_)) { | |||||||
|             header = dec_header; |             header = dec_header; | ||||||
|             encrypted = true; |             encrypted = true; | ||||||
|         } else { |         } else { | ||||||
|  |             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; | ||||||
|  |             } | ||||||
|  |  | ||||||
|             if (!keys.HasKey(Core::Crypto::S256KeyType::Header)) |             if (!keys.HasKey(Core::Crypto::S256KeyType::Header)) | ||||||
|                 status = Loader::ResultStatus::ErrorMissingKeys; |                 status = Loader::ResultStatus::ErrorMissingHeaderKey; | ||||||
|             else |             else | ||||||
|                 status = Loader::ResultStatus::ErrorDecrypting; |                 status = Loader::ResultStatus::ErrorIncorrectHeaderKey; | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     has_rights_id = std::find_if_not(header.rights_id.begin(), header.rights_id.end(), | ||||||
|  |                                      [](char c) { return c == '\0'; }) != header.rights_id.end(); | ||||||
|  |  | ||||||
|     const std::ptrdiff_t number_sections = |     const std::ptrdiff_t number_sections = | ||||||
|         std::count_if(std::begin(header.section_tables), std::end(header.section_tables), |         std::count_if(std::begin(header.section_tables), std::end(header.section_tables), | ||||||
|                       [](NCASectionTableEntry entry) { return entry.media_offset > 0; }); |                       [](NCASectionTableEntry entry) { return entry.media_offset > 0; }); | ||||||
| @@ -229,7 +273,12 @@ NCA::NCA(VirtualFile file_) : file(std::move(file_)) { | |||||||
|                 files.push_back(std::move(dec)); |                 files.push_back(std::move(dec)); | ||||||
|                 romfs = files.back(); |                 romfs = files.back(); | ||||||
|             } else { |             } else { | ||||||
|                 status = Loader::ResultStatus::ErrorMissingKeys; |                 if (status != Loader::ResultStatus::Success) | ||||||
|  |                     return; | ||||||
|  |                 if (has_rights_id) | ||||||
|  |                     status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek; | ||||||
|  |                 else | ||||||
|  |                     status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey; | ||||||
|                 return; |                 return; | ||||||
|             } |             } | ||||||
|         } else if (section.raw.header.filesystem_type == NCASectionFilesystemType::PFS0) { |         } else if (section.raw.header.filesystem_type == NCASectionFilesystemType::PFS0) { | ||||||
| @@ -249,7 +298,12 @@ NCA::NCA(VirtualFile file_) : file(std::move(file_)) { | |||||||
|                         exefs = dirs.back(); |                         exefs = dirs.back(); | ||||||
|                 } |                 } | ||||||
|             } else { |             } else { | ||||||
|                 status = Loader::ResultStatus::ErrorMissingKeys; |                 if (status != Loader::ResultStatus::Success) | ||||||
|  |                     return; | ||||||
|  |                 if (has_rights_id) | ||||||
|  |                     status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek; | ||||||
|  |                 else | ||||||
|  |                     status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey; | ||||||
|                 return; |                 return; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -98,8 +98,8 @@ protected: | |||||||
| private: | private: | ||||||
|     u8 GetCryptoRevision() const; |     u8 GetCryptoRevision() const; | ||||||
|     boost::optional<Core::Crypto::Key128> GetKeyAreaKey(NCASectionCryptoType type) const; |     boost::optional<Core::Crypto::Key128> GetKeyAreaKey(NCASectionCryptoType type) const; | ||||||
|     boost::optional<Core::Crypto::Key128> GetTitlekey() const; |     boost::optional<Core::Crypto::Key128> GetTitlekey(); | ||||||
|     VirtualFile Decrypt(NCASectionHeader header, VirtualFile in, u64 starting_offset) const; |     VirtualFile Decrypt(NCASectionHeader header, VirtualFile in, u64 starting_offset); | ||||||
|  |  | ||||||
|     std::vector<VirtualDir> dirs; |     std::vector<VirtualDir> dirs; | ||||||
|     std::vector<VirtualFile> files; |     std::vector<VirtualFile> files; | ||||||
| @@ -109,6 +109,7 @@ private: | |||||||
|     VirtualFile file; |     VirtualFile file; | ||||||
|  |  | ||||||
|     NCAHeader header{}; |     NCAHeader header{}; | ||||||
|  |     bool has_rights_id{}; | ||||||
|  |  | ||||||
|     Loader::ResultStatus status{}; |     Loader::ResultStatus status{}; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -24,19 +24,19 @@ bool PartitionFilesystem::Header::HasValidMagicValue() const { | |||||||
| PartitionFilesystem::PartitionFilesystem(std::shared_ptr<VfsFile> file) { | PartitionFilesystem::PartitionFilesystem(std::shared_ptr<VfsFile> file) { | ||||||
|     // At least be as large as the header |     // At least be as large as the header | ||||||
|     if (file->GetSize() < sizeof(Header)) { |     if (file->GetSize() < sizeof(Header)) { | ||||||
|         status = Loader::ResultStatus::Error; |         status = Loader::ResultStatus::ErrorBadPFSHeader; | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // For cartridges, HFSs can get very large, so we need to calculate the size up to |     // For cartridges, HFSs can get very large, so we need to calculate the size up to | ||||||
|     // the actual content itself instead of just blindly reading in the entire file. |     // the actual content itself instead of just blindly reading in the entire file. | ||||||
|     if (sizeof(Header) != file->ReadObject(&pfs_header)) { |     if (sizeof(Header) != file->ReadObject(&pfs_header)) { | ||||||
|         status = Loader::ResultStatus::Error; |         status = Loader::ResultStatus::ErrorBadPFSHeader; | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (!pfs_header.HasValidMagicValue()) { |     if (!pfs_header.HasValidMagicValue()) { | ||||||
|         status = Loader::ResultStatus::ErrorInvalidFormat; |         status = Loader::ResultStatus::ErrorBadPFSHeader; | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -51,7 +51,7 @@ PartitionFilesystem::PartitionFilesystem(std::shared_ptr<VfsFile> file) { | |||||||
|     const size_t total_size = file_data.size(); |     const size_t total_size = file_data.size(); | ||||||
|  |  | ||||||
|     if (total_size != metadata_size) { |     if (total_size != metadata_size) { | ||||||
|         status = Loader::ResultStatus::Error; |         status = Loader::ResultStatus::ErrorIncorrectPFSFileSize; | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -12,26 +12,26 @@ namespace FileSys { | |||||||
| Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) { | Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) { | ||||||
|     size_t total_size = static_cast<size_t>(file->GetSize()); |     size_t total_size = static_cast<size_t>(file->GetSize()); | ||||||
|     if (total_size < sizeof(Header)) |     if (total_size < sizeof(Header)) | ||||||
|         return Loader::ResultStatus::Error; |         return Loader::ResultStatus::ErrorBadNPDMHeader; | ||||||
|  |  | ||||||
|     // TODO(DarkLordZach): Use ReadObject when Header/AcidHeader becomes trivially copyable. |     // TODO(DarkLordZach): Use ReadObject when Header/AcidHeader becomes trivially copyable. | ||||||
|     std::vector<u8> npdm_header_data = file->ReadBytes(sizeof(Header)); |     std::vector<u8> npdm_header_data = file->ReadBytes(sizeof(Header)); | ||||||
|     if (sizeof(Header) != npdm_header_data.size()) |     if (sizeof(Header) != npdm_header_data.size()) | ||||||
|         return Loader::ResultStatus::Error; |         return Loader::ResultStatus::ErrorBadNPDMHeader; | ||||||
|     std::memcpy(&npdm_header, npdm_header_data.data(), sizeof(Header)); |     std::memcpy(&npdm_header, npdm_header_data.data(), sizeof(Header)); | ||||||
|  |  | ||||||
|     std::vector<u8> acid_header_data = file->ReadBytes(sizeof(AcidHeader), npdm_header.acid_offset); |     std::vector<u8> acid_header_data = file->ReadBytes(sizeof(AcidHeader), npdm_header.acid_offset); | ||||||
|     if (sizeof(AcidHeader) != acid_header_data.size()) |     if (sizeof(AcidHeader) != acid_header_data.size()) | ||||||
|         return Loader::ResultStatus::Error; |         return Loader::ResultStatus::ErrorBadACIDHeader; | ||||||
|     std::memcpy(&acid_header, acid_header_data.data(), sizeof(AcidHeader)); |     std::memcpy(&acid_header, acid_header_data.data(), sizeof(AcidHeader)); | ||||||
|  |  | ||||||
|     if (sizeof(AciHeader) != file->ReadObject(&aci_header, npdm_header.aci_offset)) |     if (sizeof(AciHeader) != file->ReadObject(&aci_header, npdm_header.aci_offset)) | ||||||
|         return Loader::ResultStatus::Error; |         return Loader::ResultStatus::ErrorBadACIHeader; | ||||||
|  |  | ||||||
|     if (sizeof(FileAccessControl) != file->ReadObject(&acid_file_access, acid_header.fac_offset)) |     if (sizeof(FileAccessControl) != file->ReadObject(&acid_file_access, acid_header.fac_offset)) | ||||||
|         return Loader::ResultStatus::Error; |         return Loader::ResultStatus::ErrorBadFileAccessControl; | ||||||
|     if (sizeof(FileAccessHeader) != file->ReadObject(&aci_file_access, aci_header.fah_offset)) |     if (sizeof(FileAccessHeader) != file->ReadObject(&aci_file_access, aci_header.fah_offset)) | ||||||
|         return Loader::ResultStatus::Error; |         return Loader::ResultStatus::ErrorBadFileAccessHeader; | ||||||
|  |  | ||||||
|     return Loader::ResultStatus::Success; |     return Loader::ResultStatus::Success; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -83,13 +83,13 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load( | |||||||
|  |  | ||||||
|     if (dir == nullptr) { |     if (dir == nullptr) { | ||||||
|         if (file == nullptr) |         if (file == nullptr) | ||||||
|             return ResultStatus::ErrorInvalidFormat; |             return ResultStatus::ErrorNullFile; | ||||||
|         dir = file->GetContainingDirectory(); |         dir = file->GetContainingDirectory(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     const FileSys::VirtualFile npdm = dir->GetFile("main.npdm"); |     const FileSys::VirtualFile npdm = dir->GetFile("main.npdm"); | ||||||
|     if (npdm == nullptr) |     if (npdm == nullptr) | ||||||
|         return ResultStatus::ErrorInvalidFormat; |         return ResultStatus::ErrorMissingNPDM; | ||||||
|  |  | ||||||
|     ResultStatus result = metadata.Load(npdm); |     ResultStatus result = metadata.Load(npdm); | ||||||
|     if (result != ResultStatus::Success) { |     if (result != ResultStatus::Success) { | ||||||
| @@ -99,7 +99,7 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load( | |||||||
|  |  | ||||||
|     const FileSys::ProgramAddressSpaceType arch_bits{metadata.GetAddressSpaceType()}; |     const FileSys::ProgramAddressSpaceType arch_bits{metadata.GetAddressSpaceType()}; | ||||||
|     if (arch_bits == FileSys::ProgramAddressSpaceType::Is32Bit) { |     if (arch_bits == FileSys::ProgramAddressSpaceType::Is32Bit) { | ||||||
|         return ResultStatus::ErrorUnsupportedArch; |         return ResultStatus::Error32BitISA; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // Load NSO modules |     // Load NSO modules | ||||||
| @@ -143,28 +143,28 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load( | |||||||
|  |  | ||||||
| ResultStatus AppLoader_DeconstructedRomDirectory::ReadRomFS(FileSys::VirtualFile& dir) { | ResultStatus AppLoader_DeconstructedRomDirectory::ReadRomFS(FileSys::VirtualFile& dir) { | ||||||
|     if (romfs == nullptr) |     if (romfs == nullptr) | ||||||
|         return ResultStatus::ErrorNotUsed; |         return ResultStatus::ErrorNoRomFS; | ||||||
|     dir = romfs; |     dir = romfs; | ||||||
|     return ResultStatus::Success; |     return ResultStatus::Success; | ||||||
| } | } | ||||||
|  |  | ||||||
| ResultStatus AppLoader_DeconstructedRomDirectory::ReadIcon(std::vector<u8>& buffer) { | ResultStatus AppLoader_DeconstructedRomDirectory::ReadIcon(std::vector<u8>& buffer) { | ||||||
|     if (icon_data.empty()) |     if (icon_data.empty()) | ||||||
|         return ResultStatus::ErrorNotUsed; |         return ResultStatus::ErrorNoIcon; | ||||||
|     buffer = icon_data; |     buffer = icon_data; | ||||||
|     return ResultStatus::Success; |     return ResultStatus::Success; | ||||||
| } | } | ||||||
|  |  | ||||||
| ResultStatus AppLoader_DeconstructedRomDirectory::ReadProgramId(u64& out_program_id) { | ResultStatus AppLoader_DeconstructedRomDirectory::ReadProgramId(u64& out_program_id) { | ||||||
|     if (name.empty()) |     if (name.empty()) | ||||||
|         return ResultStatus::ErrorNotUsed; |         return ResultStatus::ErrorNoControl; | ||||||
|     out_program_id = title_id; |     out_program_id = title_id; | ||||||
|     return ResultStatus::Success; |     return ResultStatus::Success; | ||||||
| } | } | ||||||
|  |  | ||||||
| ResultStatus AppLoader_DeconstructedRomDirectory::ReadTitle(std::string& title) { | ResultStatus AppLoader_DeconstructedRomDirectory::ReadTitle(std::string& title) { | ||||||
|     if (name.empty()) |     if (name.empty()) | ||||||
|         return ResultStatus::ErrorNotUsed; |         return ResultStatus::ErrorNoControl; | ||||||
|     title = name; |     title = name; | ||||||
|     return ResultStatus::Success; |     return ResultStatus::Success; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -390,7 +390,7 @@ ResultStatus AppLoader_ELF::Load(Kernel::SharedPtr<Kernel::Process>& process) { | |||||||
|  |  | ||||||
|     std::vector<u8> buffer = file->ReadAllBytes(); |     std::vector<u8> buffer = file->ReadAllBytes(); | ||||||
|     if (buffer.size() != file->GetSize()) |     if (buffer.size() != file->GetSize()) | ||||||
|         return ResultStatus::Error; |         return ResultStatus::ErrorIncorrectELFFileSize; | ||||||
|  |  | ||||||
|     ElfReader elf_reader(&buffer[0]); |     ElfReader elf_reader(&buffer[0]); | ||||||
|     SharedPtr<CodeSet> codeset = elf_reader.LoadInto(Memory::PROCESS_IMAGE_VADDR); |     SharedPtr<CodeSet> codeset = elf_reader.LoadInto(Memory::PROCESS_IMAGE_VADDR); | ||||||
|   | |||||||
| @@ -86,6 +86,55 @@ std::string GetFileTypeString(FileType type) { | |||||||
|     return "unknown"; |     return "unknown"; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | constexpr std::array<const char*, 36> RESULT_MESSAGES{ | ||||||
|  |     "The operation completed successfully.", | ||||||
|  |     "The loader requested to load is already loaded.", | ||||||
|  |     "The operation is not implemented.", | ||||||
|  |     "The loader is not initialized properly.", | ||||||
|  |     "The NPDM file has a bad header.", | ||||||
|  |     "The NPDM has a bad ACID header.", | ||||||
|  |     "The NPDM has a bad ACI header,", | ||||||
|  |     "The NPDM file has a bad file access control.", | ||||||
|  |     "The NPDM has a bad file access header.", | ||||||
|  |     "The PFS/HFS partition has a bad header.", | ||||||
|  |     "The PFS/HFS partition has incorrect size as determined by the header.", | ||||||
|  |     "The NCA file has a bad header.", | ||||||
|  |     "The general keyfile could not be found.", | ||||||
|  |     "The NCA Header key could not be found.", | ||||||
|  |     "The NCA Header key is incorrect or the header is invalid.", | ||||||
|  |     "Support for NCA2-type NCAs is not implemented.", | ||||||
|  |     "Support for NCA0-type NCAs is not implemented.", | ||||||
|  |     "The titlekey for this Rights ID could not be found.", | ||||||
|  |     "The titlekek for this crypto revision could not be found.", | ||||||
|  |     "The Rights ID in the header is invalid.", | ||||||
|  |     "The key area key for this application type and crypto revision could not be found.", | ||||||
|  |     "The key area key is incorrect or the section header is invalid.", | ||||||
|  |     "The titlekey and/or titlekek is incorrect or the section header is invalid.", | ||||||
|  |     "The XCI file is missing a Program-type NCA.", | ||||||
|  |     "The NCA file is not an application.", | ||||||
|  |     "The ExeFS partition could not be found.", | ||||||
|  |     "The XCI file has a bad header.", | ||||||
|  |     "The XCI file is missing a partition.", | ||||||
|  |     "The file could not be found or does not exist.", | ||||||
|  |     "The game is missing a program metadata file (main.npdm).", | ||||||
|  |     "The game uses the currently-unimplemented 32-bit architecture.", | ||||||
|  |     "The RomFS could not be found.", | ||||||
|  |     "The ELF file has incorrect size as determined by the header.", | ||||||
|  |     "There was a general error loading the NRO into emulated memory.", | ||||||
|  |     "There is no icon available.", | ||||||
|  |     "There is no control data available.", | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | std::string GetMessageForResultStatus(ResultStatus status) { | ||||||
|  |     return GetMessageForResultStatus(static_cast<size_t>(status)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | std::string GetMessageForResultStatus(u16 status) { | ||||||
|  |     if (status >= 36) | ||||||
|  |         return ""; | ||||||
|  |     return RESULT_MESSAGES[status]; | ||||||
|  | } | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Get a loader for a file with a specific type |  * Get a loader for a file with a specific type | ||||||
|  * @param file The file to load |  * @param file The file to load | ||||||
|   | |||||||
| @@ -58,18 +58,46 @@ std::string GetFileTypeString(FileType type); | |||||||
| /// Return type for functions in Loader namespace | /// Return type for functions in Loader namespace | ||||||
| enum class ResultStatus { | enum class ResultStatus { | ||||||
|     Success, |     Success, | ||||||
|     Error, |  | ||||||
|     ErrorInvalidFormat, |  | ||||||
|     ErrorNotImplemented, |  | ||||||
|     ErrorNotLoaded, |  | ||||||
|     ErrorNotUsed, |  | ||||||
|     ErrorAlreadyLoaded, |     ErrorAlreadyLoaded, | ||||||
|     ErrorMemoryAllocationFailed, |     ErrorNotImplemented, | ||||||
|     ErrorMissingKeys, |     ErrorNotInitialized, | ||||||
|     ErrorDecrypting, |     ErrorBadNPDMHeader, | ||||||
|     ErrorUnsupportedArch, |     ErrorBadACIDHeader, | ||||||
|  |     ErrorBadACIHeader, | ||||||
|  |     ErrorBadFileAccessControl, | ||||||
|  |     ErrorBadFileAccessHeader, | ||||||
|  |     ErrorBadPFSHeader, | ||||||
|  |     ErrorIncorrectPFSFileSize, | ||||||
|  |     ErrorBadNCAHeader, | ||||||
|  |     ErrorMissingProductionKeyFile, | ||||||
|  |     ErrorMissingHeaderKey, | ||||||
|  |     ErrorIncorrectHeaderKey, | ||||||
|  |     ErrorNCA2, | ||||||
|  |     ErrorNCA0, | ||||||
|  |     ErrorMissingTitlekey, | ||||||
|  |     ErrorMissingTitlekek, | ||||||
|  |     ErrorInvalidRightsID, | ||||||
|  |     ErrorMissingKeyAreaKey, | ||||||
|  |     ErrorIncorrectKeyAreaKey, | ||||||
|  |     ErrorIncorrectTitlekeyOrTitlekek, | ||||||
|  |     ErrorXCIMissingProgramNCA, | ||||||
|  |     ErrorNCANotProgram, | ||||||
|  |     ErrorNoExeFS, | ||||||
|  |     ErrorBadXCIHeader, | ||||||
|  |     ErrorXCIMissingPartition, | ||||||
|  |     ErrorNullFile, | ||||||
|  |     ErrorMissingNPDM, | ||||||
|  |     Error32BitISA, | ||||||
|  |     ErrorNoRomFS, | ||||||
|  |     ErrorIncorrectELFFileSize, | ||||||
|  |     ErrorLoadingNRO, | ||||||
|  |     ErrorNoIcon, | ||||||
|  |     ErrorNoControl, | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | std::string GetMessageForResultStatus(ResultStatus status); | ||||||
|  | std::string GetMessageForResultStatus(u16 status); | ||||||
|  |  | ||||||
| /// Interface for loading an application | /// Interface for loading an application | ||||||
| class AppLoader : NonCopyable { | class AppLoader : NonCopyable { | ||||||
| public: | public: | ||||||
|   | |||||||
| @@ -46,12 +46,12 @@ ResultStatus AppLoader_NCA::Load(Kernel::SharedPtr<Kernel::Process>& process) { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (nca->GetType() != FileSys::NCAContentType::Program) |     if (nca->GetType() != FileSys::NCAContentType::Program) | ||||||
|         return ResultStatus::ErrorInvalidFormat; |         return ResultStatus::ErrorNCANotProgram; | ||||||
|  |  | ||||||
|     const auto exefs = nca->GetExeFS(); |     const auto exefs = nca->GetExeFS(); | ||||||
|  |  | ||||||
|     if (exefs == nullptr) |     if (exefs == nullptr) | ||||||
|         return ResultStatus::ErrorInvalidFormat; |         return ResultStatus::ErrorNoExeFS; | ||||||
|  |  | ||||||
|     directory_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(exefs); |     directory_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(exefs); | ||||||
|  |  | ||||||
| @@ -69,16 +69,16 @@ ResultStatus AppLoader_NCA::Load(Kernel::SharedPtr<Kernel::Process>& process) { | |||||||
|  |  | ||||||
| ResultStatus AppLoader_NCA::ReadRomFS(FileSys::VirtualFile& dir) { | ResultStatus AppLoader_NCA::ReadRomFS(FileSys::VirtualFile& dir) { | ||||||
|     if (nca == nullptr) |     if (nca == nullptr) | ||||||
|         return ResultStatus::ErrorNotLoaded; |         return ResultStatus::ErrorNotInitialized; | ||||||
|     if (nca->GetRomFS() == nullptr || nca->GetRomFS()->GetSize() == 0) |     if (nca->GetRomFS() == nullptr || nca->GetRomFS()->GetSize() == 0) | ||||||
|         return ResultStatus::ErrorNotUsed; |         return ResultStatus::ErrorNoRomFS; | ||||||
|     dir = nca->GetRomFS(); |     dir = nca->GetRomFS(); | ||||||
|     return ResultStatus::Success; |     return ResultStatus::Success; | ||||||
| } | } | ||||||
|  |  | ||||||
| ResultStatus AppLoader_NCA::ReadProgramId(u64& out_program_id) { | ResultStatus AppLoader_NCA::ReadProgramId(u64& out_program_id) { | ||||||
|     if (nca == nullptr || nca->GetStatus() != ResultStatus::Success) |     if (nca == nullptr || nca->GetStatus() != ResultStatus::Success) | ||||||
|         return ResultStatus::ErrorInvalidFormat; |         return ResultStatus::ErrorNotInitialized; | ||||||
|     out_program_id = nca->GetTitleId(); |     out_program_id = nca->GetTitleId(); | ||||||
|     return ResultStatus::Success; |     return ResultStatus::Success; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -182,7 +182,7 @@ ResultStatus AppLoader_NRO::Load(Kernel::SharedPtr<Kernel::Process>& process) { | |||||||
|     static constexpr VAddr base_addr{Memory::PROCESS_IMAGE_VADDR}; |     static constexpr VAddr base_addr{Memory::PROCESS_IMAGE_VADDR}; | ||||||
|  |  | ||||||
|     if (!LoadNro(file, base_addr)) { |     if (!LoadNro(file, base_addr)) { | ||||||
|         return ResultStatus::ErrorInvalidFormat; |         return ResultStatus::ErrorLoadingNRO; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     process->svc_access_mask.set(); |     process->svc_access_mask.set(); | ||||||
| @@ -197,7 +197,7 @@ ResultStatus AppLoader_NRO::Load(Kernel::SharedPtr<Kernel::Process>& process) { | |||||||
|  |  | ||||||
| ResultStatus AppLoader_NRO::ReadIcon(std::vector<u8>& buffer) { | ResultStatus AppLoader_NRO::ReadIcon(std::vector<u8>& buffer) { | ||||||
|     if (icon_data.empty()) { |     if (icon_data.empty()) { | ||||||
|         return ResultStatus::ErrorNotUsed; |         return ResultStatus::ErrorNoIcon; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     buffer = icon_data; |     buffer = icon_data; | ||||||
| @@ -206,7 +206,7 @@ ResultStatus AppLoader_NRO::ReadIcon(std::vector<u8>& buffer) { | |||||||
|  |  | ||||||
| ResultStatus AppLoader_NRO::ReadProgramId(u64& out_program_id) { | ResultStatus AppLoader_NRO::ReadProgramId(u64& out_program_id) { | ||||||
|     if (nacp == nullptr) { |     if (nacp == nullptr) { | ||||||
|         return ResultStatus::ErrorNotUsed; |         return ResultStatus::ErrorNoControl; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     out_program_id = nacp->GetTitleId(); |     out_program_id = nacp->GetTitleId(); | ||||||
| @@ -215,7 +215,7 @@ ResultStatus AppLoader_NRO::ReadProgramId(u64& out_program_id) { | |||||||
|  |  | ||||||
| ResultStatus AppLoader_NRO::ReadRomFS(FileSys::VirtualFile& dir) { | ResultStatus AppLoader_NRO::ReadRomFS(FileSys::VirtualFile& dir) { | ||||||
|     if (romfs == nullptr) { |     if (romfs == nullptr) { | ||||||
|         return ResultStatus::ErrorNotUsed; |         return ResultStatus::ErrorNoRomFS; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     dir = romfs; |     dir = romfs; | ||||||
| @@ -224,7 +224,7 @@ ResultStatus AppLoader_NRO::ReadRomFS(FileSys::VirtualFile& dir) { | |||||||
|  |  | ||||||
| ResultStatus AppLoader_NRO::ReadTitle(std::string& title) { | ResultStatus AppLoader_NRO::ReadTitle(std::string& title) { | ||||||
|     if (nacp == nullptr) { |     if (nacp == nullptr) { | ||||||
|         return ResultStatus::ErrorNotUsed; |         return ResultStatus::ErrorNoControl; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     title = nacp->GetApplicationName(); |     title = nacp->GetApplicationName(); | ||||||
|   | |||||||
| @@ -66,10 +66,13 @@ ResultStatus AppLoader_XCI::Load(Kernel::SharedPtr<Kernel::Process>& process) { | |||||||
|         return ResultStatus::ErrorAlreadyLoaded; |         return ResultStatus::ErrorAlreadyLoaded; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     if (xci->GetStatus() != ResultStatus::Success) | ||||||
|  |         return xci->GetStatus(); | ||||||
|  |  | ||||||
|     if (xci->GetNCAFileByType(FileSys::NCAContentType::Program) == nullptr) { |     if (xci->GetNCAFileByType(FileSys::NCAContentType::Program) == nullptr) { | ||||||
|         if (!Core::Crypto::KeyManager::KeyFileExists(false)) |         if (!Core::Crypto::KeyManager::KeyFileExists(false)) | ||||||
|             return ResultStatus::ErrorMissingKeys; |             return ResultStatus::ErrorMissingProductionKeyFile; | ||||||
|         return ResultStatus::ErrorDecrypting; |         return ResultStatus::ErrorXCIMissingProgramNCA; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     auto result = nca_loader->Load(process); |     auto result = nca_loader->Load(process); | ||||||
| @@ -91,14 +94,14 @@ ResultStatus AppLoader_XCI::ReadProgramId(u64& out_program_id) { | |||||||
|  |  | ||||||
| ResultStatus AppLoader_XCI::ReadIcon(std::vector<u8>& buffer) { | ResultStatus AppLoader_XCI::ReadIcon(std::vector<u8>& buffer) { | ||||||
|     if (icon_file == nullptr) |     if (icon_file == nullptr) | ||||||
|         return ResultStatus::ErrorInvalidFormat; |         return ResultStatus::ErrorNoControl; | ||||||
|     buffer = icon_file->ReadAllBytes(); |     buffer = icon_file->ReadAllBytes(); | ||||||
|     return ResultStatus::Success; |     return ResultStatus::Success; | ||||||
| } | } | ||||||
|  |  | ||||||
| ResultStatus AppLoader_XCI::ReadTitle(std::string& title) { | ResultStatus AppLoader_XCI::ReadTitle(std::string& title) { | ||||||
|     if (nacp_file == nullptr) |     if (nacp_file == nullptr) | ||||||
|         return ResultStatus::ErrorInvalidFormat; |         return ResultStatus::ErrorNoControl; | ||||||
|     title = nacp_file->GetApplicationName(); |     title = nacp_file->GetApplicationName(); | ||||||
|     return ResultStatus::Success; |     return ResultStatus::Success; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -453,10 +453,8 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign | |||||||
|             std::string name = " "; |             std::string name = " "; | ||||||
|             const auto res3 = loader->ReadTitle(name); |             const auto res3 = loader->ReadTitle(name); | ||||||
|  |  | ||||||
|             if ((res1 == Loader::ResultStatus::ErrorNotUsed || |             if (res1 != Loader::ResultStatus::Success && | ||||||
|                  res1 == Loader::ResultStatus::ErrorNotImplemented) && |                 res3 != Loader::ResultStatus::Success && | ||||||
|                 (res3 == Loader::ResultStatus::ErrorNotUsed || |  | ||||||
|                  res3 == Loader::ResultStatus::ErrorNotImplemented) && |  | ||||||
|                 res2 == Loader::ResultStatus::Success) { |                 res2 == Loader::ResultStatus::Success) { | ||||||
|                 // Use from metadata pool. |                 // Use from metadata pool. | ||||||
|                 if (nca_control_map.find(program_id) != nca_control_map.end()) { |                 if (nca_control_map.find(program_id) != nca_control_map.end()) { | ||||||
|   | |||||||
| @@ -424,67 +424,11 @@ bool GMainWindow::LoadROM(const QString& filename) { | |||||||
|             QMessageBox::critical(this, tr("Error while loading ROM!"), |             QMessageBox::critical(this, tr("Error while loading ROM!"), | ||||||
|                                   tr("The ROM format is not supported.")); |                                   tr("The ROM format is not supported.")); | ||||||
|             break; |             break; | ||||||
|         case Core::System::ResultStatus::ErrorUnsupportedArch: |  | ||||||
|             LOG_CRITICAL(Frontend, "Unsupported architecture detected!", filename.toStdString()); |  | ||||||
|             QMessageBox::critical(this, tr("Error while loading ROM!"), |  | ||||||
|                                   tr("The ROM uses currently unusable 32-bit architecture")); |  | ||||||
|             break; |  | ||||||
|         case Core::System::ResultStatus::ErrorSystemMode: |         case Core::System::ResultStatus::ErrorSystemMode: | ||||||
|             LOG_CRITICAL(Frontend, "Failed to load ROM!"); |             LOG_CRITICAL(Frontend, "Failed to load ROM!"); | ||||||
|             QMessageBox::critical(this, tr("Error while loading ROM!"), |             QMessageBox::critical(this, tr("Error while loading ROM!"), | ||||||
|                                   tr("Could not determine the system mode.")); |                                   tr("Could not determine the system mode.")); | ||||||
|             break; |             break; | ||||||
|  |  | ||||||
|         case Core::System::ResultStatus::ErrorLoader_ErrorMissingKeys: { |  | ||||||
|             const auto reg_found = Core::Crypto::KeyManager::KeyFileExists(false); |  | ||||||
|             const auto title_found = Core::Crypto::KeyManager::KeyFileExists(true); |  | ||||||
|  |  | ||||||
|             std::string file_text; |  | ||||||
|  |  | ||||||
|             if (!reg_found && !title_found) { |  | ||||||
|                 file_text = "A proper key file (prod.keys, dev.keys, or title.keys) could not be " |  | ||||||
|                             "found. You will need to dump your keys from your switch to continue."; |  | ||||||
|             } else if (reg_found && title_found) { |  | ||||||
|                 file_text = |  | ||||||
|                     "Both key files were found in your config directory, but the correct key could" |  | ||||||
|                     "not be found. You may be missing a titlekey or general key, depending on " |  | ||||||
|                     "the game."; |  | ||||||
|             } else if (reg_found) { |  | ||||||
|                 file_text = |  | ||||||
|                     "The regular keys file (prod.keys/dev.keys) was found in your config, but the " |  | ||||||
|                     "titlekeys file (title.keys) was not. You are either missing the correct " |  | ||||||
|                     "titlekey or missing a general key required to decrypt the game."; |  | ||||||
|             } else { |  | ||||||
|                 file_text = "The title keys file (title.keys) was found in your config, but " |  | ||||||
|                             "the regular keys file (prod.keys/dev.keys) was not. Unfortunately, " |  | ||||||
|                             "having the titlekey is not enough, you need additional general keys " |  | ||||||
|                             "to properly decrypt the game. You should double-check to make sure " |  | ||||||
|                             "your keys are correct."; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             QMessageBox::critical( |  | ||||||
|                 this, tr("Error while loading ROM!"), |  | ||||||
|                 tr(("The game you are trying to load is encrypted and the required keys to load " |  | ||||||
|                     "the game could not be found in your configuration. " + |  | ||||||
|                     file_text + " Please refer to the yuzu wiki for help.") |  | ||||||
|                        .c_str())); |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|         case Core::System::ResultStatus::ErrorLoader_ErrorDecrypting: { |  | ||||||
|             QMessageBox::critical( |  | ||||||
|                 this, tr("Error while loading ROM!"), |  | ||||||
|                 tr("There was a general error while decrypting the game. This means that the keys " |  | ||||||
|                    "necessary were found, but were either incorrect, the game itself was not a " |  | ||||||
|                    "valid game or the game uses an unhandled cryptographic scheme. Please double " |  | ||||||
|                    "check that you have the correct " |  | ||||||
|                    "keys.")); |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|         case Core::System::ResultStatus::ErrorLoader_ErrorInvalidFormat: |  | ||||||
|             QMessageBox::critical(this, tr("Error while loading ROM!"), |  | ||||||
|                                   tr("The ROM format is not supported.")); |  | ||||||
|             break; |  | ||||||
|  |  | ||||||
|         case Core::System::ResultStatus::ErrorVideoCore: |         case Core::System::ResultStatus::ErrorVideoCore: | ||||||
|             QMessageBox::critical( |             QMessageBox::critical( | ||||||
|                 this, tr("An error occurred initializing the video core."), |                 this, tr("An error occurred initializing the video core."), | ||||||
| @@ -499,9 +443,23 @@ bool GMainWindow::LoadROM(const QString& filename) { | |||||||
|             break; |             break; | ||||||
|  |  | ||||||
|         default: |         default: | ||||||
|             QMessageBox::critical( |             if (static_cast<u32>(result) > | ||||||
|                 this, tr("Error while loading ROM!"), |                 static_cast<u32>(Core::System::ResultStatus::ErrorLoader)) { | ||||||
|                 tr("An unknown error occurred. Please see the log for more details.")); |                 LOG_CRITICAL(Frontend, "Failed to load ROM!"); | ||||||
|  |                 const u16 loader_id = static_cast<u16>(Core::System::ResultStatus::ErrorLoader); | ||||||
|  |                 const u16 error_id = static_cast<u16>(result) - loader_id; | ||||||
|  |                 QMessageBox::critical( | ||||||
|  |                     this, tr("Error while loading ROM!"), | ||||||
|  |                     QString::fromStdString(fmt::format( | ||||||
|  |                         "While attempting to load the ROM requested, an error occured. Please " | ||||||
|  |                         "refer to the yuzu wiki for more information or the yuzu discord for " | ||||||
|  |                         "additional help.\n\nError Code: {:04X}-{:04X}\nError Description: {}", | ||||||
|  |                         loader_id, error_id, Loader::GetMessageForResultStatus(error_id)))); | ||||||
|  |             } else { | ||||||
|  |                 QMessageBox::critical( | ||||||
|  |                     this, tr("Error while loading ROM!"), | ||||||
|  |                     tr("An unknown error occurred. Please see the log for more details.")); | ||||||
|  |             } | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|         return false; |         return false; | ||||||
|   | |||||||
| @@ -174,19 +174,6 @@ int main(int argc, char** argv) { | |||||||
|     case Core::System::ResultStatus::ErrorLoader: |     case Core::System::ResultStatus::ErrorLoader: | ||||||
|         LOG_CRITICAL(Frontend, "Failed to load ROM!"); |         LOG_CRITICAL(Frontend, "Failed to load ROM!"); | ||||||
|         return -1; |         return -1; | ||||||
|     case Core::System::ResultStatus::ErrorLoader_ErrorMissingKeys: |  | ||||||
|         LOG_CRITICAL(Frontend, "The game you are trying to load is encrypted and the keys required " |  | ||||||
|                                "could not be found. Please refer to the yuzu wiki for help"); |  | ||||||
|         return -1; |  | ||||||
|     case Core::System::ResultStatus::ErrorLoader_ErrorDecrypting: |  | ||||||
|         LOG_CRITICAL(Frontend, "The game you are trying to load is encrypted and there was a " |  | ||||||
|                                "general error while decrypting. This could mean that the keys are " |  | ||||||
|                                "incorrect, game is invalid or game uses an unsupported method of " |  | ||||||
|                                "crypto. Please double-check your keys"); |  | ||||||
|         return -1; |  | ||||||
|     case Core::System::ResultStatus::ErrorLoader_ErrorInvalidFormat: |  | ||||||
|         LOG_CRITICAL(Frontend, "Error while loading ROM: The ROM format is not supported."); |  | ||||||
|         return -1; |  | ||||||
|     case Core::System::ResultStatus::ErrorNotInitialized: |     case Core::System::ResultStatus::ErrorNotInitialized: | ||||||
|         LOG_CRITICAL(Frontend, "CPUCore not initialized"); |         LOG_CRITICAL(Frontend, "CPUCore not initialized"); | ||||||
|         return -1; |         return -1; | ||||||
| @@ -198,6 +185,17 @@ int main(int argc, char** argv) { | |||||||
|         return -1; |         return -1; | ||||||
|     case Core::System::ResultStatus::Success: |     case Core::System::ResultStatus::Success: | ||||||
|         break; // Expected case |         break; // Expected case | ||||||
|  |     default: | ||||||
|  |         if (static_cast<u32>(load_result) > | ||||||
|  |             static_cast<u32>(Core::System::ResultStatus::ErrorLoader)) { | ||||||
|  |             const u16 loader_id = static_cast<u16>(Core::System::ResultStatus::ErrorLoader); | ||||||
|  |             const u16 error_id = static_cast<u16>(load_result) - loader_id; | ||||||
|  |             LOG_CRITICAL(Frontend, | ||||||
|  |                          "While attempting to load the ROM requested, an error occured. Please " | ||||||
|  |                          "refer to the yuzu wiki for more information or the yuzu discord for " | ||||||
|  |                          "additional help.\n\nError Code: {:04X}-{:04X}\nError Description: {}", | ||||||
|  |                          loader_id, error_id, Loader::GetMessageForResultStatus(error_id)); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     Core::Telemetry().AddField(Telemetry::FieldType::App, "Frontend", "SDL"); |     Core::Telemetry().AddField(Telemetry::FieldType::App, "Frontend", "SDL"); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user