loader: Add more descriptive errors
Full list of new errors and descriptions in core/loader/loader.h
This commit is contained in:
		| @@ -102,19 +102,9 @@ System::ResultStatus System::Load(EmuWindow& emu_window, const std::string& file | ||||
|         LOG_CRITICAL(Core, "Failed to determine system mode (Error {})!", | ||||
|                      static_cast<int>(system_mode.second)); | ||||
|  | ||||
|         switch (system_mode.second) { | ||||
|         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: | ||||
|         if (system_mode.second != Loader::ResultStatus::Success) | ||||
|             return ResultStatus::ErrorSystemMode; | ||||
|     } | ||||
|     } | ||||
|  | ||||
|     ResultStatus init_result{Init(emu_window)}; | ||||
|     if (init_result != ResultStatus::Success) { | ||||
| @@ -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)); | ||||
|         System::Shutdown(); | ||||
|  | ||||
|         switch (load_result) { | ||||
|         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::ErrorLoader; | ||||
|         if (load_result != Loader::ResultStatus::Success) { | ||||
|             return static_cast<ResultStatus>(static_cast<u32>(ResultStatus::ErrorLoader) + | ||||
|                                              static_cast<u32>(load_result)); | ||||
|         } | ||||
|     } | ||||
|     status = ResultStatus::Success; | ||||
|   | ||||
| @@ -53,17 +53,11 @@ public: | ||||
|         ErrorNotInitialized, ///< Error trying to use core prior to initialization | ||||
|         ErrorGetLoader,      ///< Error finding the correct application loader | ||||
|         ErrorSystemMode,     ///< Error determining the system mode | ||||
|         ErrorLoader,                  ///< Error loading the specified application | ||||
|         ErrorLoader_ErrorMissingKeys, ///< Error because the key/keys needed to run could not be | ||||
|                                       ///< found. | ||||
|         ErrorLoader_ErrorDecrypting,  ///< Error loading the specified application due to encryption | ||||
|         ErrorLoader_ErrorInvalidFormat, ///< Error loading the specified application due to an | ||||
|                                         /// 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 | ||||
|         ErrorUnknown,        ///< Any other error | ||||
|         ErrorLoader,         ///< The base for loader errors (too many to repeat) | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|   | ||||
| @@ -12,14 +12,16 @@ | ||||
|  | ||||
| namespace FileSys { | ||||
|  | ||||
| constexpr std::array<const char*, 0x4> partition_names = {"update", "normal", "secure", "logo"}; | ||||
|  | ||||
| XCI::XCI(VirtualFile file_) : file(std::move(file_)), partitions(0x4) { | ||||
|     if (file->ReadObject(&header) != sizeof(GamecardHeader)) { | ||||
|         status = Loader::ResultStatus::ErrorInvalidFormat; | ||||
|         status = Loader::ResultStatus::ErrorBadXCIHeader; | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (header.magic != Common::MakeMagic('H', 'E', 'A', 'D')) { | ||||
|         status = Loader::ResultStatus::ErrorInvalidFormat; | ||||
|         status = Loader::ResultStatus::ErrorBadXCIHeader; | ||||
|         return; | ||||
|     } | ||||
|  | ||||
| @@ -31,9 +33,6 @@ XCI::XCI(VirtualFile file_) : file(std::move(file_)), partitions(0x4) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     static constexpr std::array<const char*, 0x4> partition_names = {"update", "normal", "secure", | ||||
|                                                                      "logo"}; | ||||
|  | ||||
|     for (XCIPartition partition : | ||||
|          {XCIPartition::Update, XCIPartition::Normal, XCIPartition::Secure, XCIPartition::Logo}) { | ||||
|         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) { | ||||
|     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()) { | ||||
|         if (file->GetExtension() != "nca") | ||||
|             continue; | ||||
|         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)); | ||||
|         } 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; | ||||
|   | ||||
| @@ -113,17 +113,27 @@ boost::optional<Core::Crypto::Key128> NCA::GetKeyAreaKey(NCASectionCryptoType ty | ||||
|     return out; | ||||
| } | ||||
|  | ||||
| boost::optional<Core::Crypto::Key128> NCA::GetTitlekey() const { | ||||
| boost::optional<Core::Crypto::Key128> NCA::GetTitlekey() { | ||||
|     const auto master_key_id = GetCryptoRevision(); | ||||
|  | ||||
|     u128 rights_id{}; | ||||
|     memcpy(rights_id.data(), header.rights_id.data(), 16); | ||||
|     if (rights_id == u128{}) | ||||
|     if (rights_id == u128{}) { | ||||
|         status = Loader::ResultStatus::ErrorInvalidRightsID; | ||||
|         return boost::none; | ||||
|     } | ||||
|  | ||||
|     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; | ||||
|     } | ||||
|  | ||||
|     if (!keys.HasKey(Core::Crypto::S128KeyType::Titlekek, master_key_id)) { | ||||
|         status = Loader::ResultStatus::ErrorMissingTitlekek; | ||||
|         return boost::none; | ||||
|     } | ||||
|  | ||||
|     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); | ||||
| @@ -131,7 +141,7 @@ boost::optional<Core::Crypto::Key128> NCA::GetTitlekey() const { | ||||
|     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) | ||||
|         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); | ||||
|         { | ||||
|             boost::optional<Core::Crypto::Key128> key = boost::none; | ||||
|             if (std::find_if_not(header.rights_id.begin(), header.rights_id.end(), | ||||
|                                  [](char c) { return c == 0; }) == header.rights_id.end()) { | ||||
|                 key = GetKeyAreaKey(NCASectionCryptoType::CTR); | ||||
|             } else { | ||||
|             if (has_rights_id) { | ||||
|                 status = Loader::ResultStatus::Success; | ||||
|                 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>( | ||||
|                 std::move(in), key.value(), starting_offset); | ||||
|             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_)) { | ||||
|     status = Loader::ResultStatus::Success; | ||||
|  | ||||
|     if (file == nullptr) { | ||||
|         status = Loader::ResultStatus::ErrorInvalidFormat; | ||||
|         status = Loader::ResultStatus::ErrorNullFile; | ||||
|         return; | ||||
|     } | ||||
|     if (sizeof(NCAHeader) != file->ReadObject(&header)) | ||||
|  | ||||
|     if (sizeof(NCAHeader) != file->ReadObject(&header)) { | ||||
|         LOG_ERROR(Loader, "File reader errored out during header read."); | ||||
|         status = Loader::ResultStatus::ErrorBadNCAHeader; | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     encrypted = false; | ||||
|  | ||||
|     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{}; | ||||
|         Core::Crypto::AESCipher<Core::Crypto::Key256> cipher( | ||||
|             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; | ||||
|             encrypted = true; | ||||
|         } 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)) | ||||
|                 status = Loader::ResultStatus::ErrorMissingKeys; | ||||
|                 status = Loader::ResultStatus::ErrorMissingHeaderKey; | ||||
|             else | ||||
|                 status = Loader::ResultStatus::ErrorDecrypting; | ||||
|                 status = Loader::ResultStatus::ErrorIncorrectHeaderKey; | ||||
|             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 = | ||||
|         std::count_if(std::begin(header.section_tables), std::end(header.section_tables), | ||||
|                       [](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)); | ||||
|                 romfs = files.back(); | ||||
|             } 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; | ||||
|             } | ||||
|         } else if (section.raw.header.filesystem_type == NCASectionFilesystemType::PFS0) { | ||||
| @@ -249,7 +298,12 @@ NCA::NCA(VirtualFile file_) : file(std::move(file_)) { | ||||
|                         exefs = dirs.back(); | ||||
|                 } | ||||
|             } 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; | ||||
|             } | ||||
|         } | ||||
|   | ||||
| @@ -98,8 +98,8 @@ protected: | ||||
| private: | ||||
|     u8 GetCryptoRevision() const; | ||||
|     boost::optional<Core::Crypto::Key128> GetKeyAreaKey(NCASectionCryptoType type) const; | ||||
|     boost::optional<Core::Crypto::Key128> GetTitlekey() const; | ||||
|     VirtualFile Decrypt(NCASectionHeader header, VirtualFile in, u64 starting_offset) const; | ||||
|     boost::optional<Core::Crypto::Key128> GetTitlekey(); | ||||
|     VirtualFile Decrypt(NCASectionHeader header, VirtualFile in, u64 starting_offset); | ||||
|  | ||||
|     std::vector<VirtualDir> dirs; | ||||
|     std::vector<VirtualFile> files; | ||||
| @@ -109,6 +109,7 @@ private: | ||||
|     VirtualFile file; | ||||
|  | ||||
|     NCAHeader header{}; | ||||
|     bool has_rights_id{}; | ||||
|  | ||||
|     Loader::ResultStatus status{}; | ||||
|  | ||||
|   | ||||
| @@ -24,19 +24,19 @@ bool PartitionFilesystem::Header::HasValidMagicValue() const { | ||||
| PartitionFilesystem::PartitionFilesystem(std::shared_ptr<VfsFile> file) { | ||||
|     // At least be as large as the header | ||||
|     if (file->GetSize() < sizeof(Header)) { | ||||
|         status = Loader::ResultStatus::Error; | ||||
|         status = Loader::ResultStatus::ErrorBadPFSHeader; | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     // 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. | ||||
|     if (sizeof(Header) != file->ReadObject(&pfs_header)) { | ||||
|         status = Loader::ResultStatus::Error; | ||||
|         status = Loader::ResultStatus::ErrorBadPFSHeader; | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (!pfs_header.HasValidMagicValue()) { | ||||
|         status = Loader::ResultStatus::ErrorInvalidFormat; | ||||
|         status = Loader::ResultStatus::ErrorBadPFSHeader; | ||||
|         return; | ||||
|     } | ||||
|  | ||||
| @@ -51,7 +51,7 @@ PartitionFilesystem::PartitionFilesystem(std::shared_ptr<VfsFile> file) { | ||||
|     const size_t total_size = file_data.size(); | ||||
|  | ||||
|     if (total_size != metadata_size) { | ||||
|         status = Loader::ResultStatus::Error; | ||||
|         status = Loader::ResultStatus::ErrorIncorrectPFSFileSize; | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -12,26 +12,26 @@ namespace FileSys { | ||||
| Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) { | ||||
|     size_t total_size = static_cast<size_t>(file->GetSize()); | ||||
|     if (total_size < sizeof(Header)) | ||||
|         return Loader::ResultStatus::Error; | ||||
|         return Loader::ResultStatus::ErrorBadNPDMHeader; | ||||
|  | ||||
|     // TODO(DarkLordZach): Use ReadObject when Header/AcidHeader becomes trivially copyable. | ||||
|     std::vector<u8> npdm_header_data = file->ReadBytes(sizeof(Header)); | ||||
|     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::vector<u8> acid_header_data = file->ReadBytes(sizeof(AcidHeader), npdm_header.acid_offset); | ||||
|     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)); | ||||
|  | ||||
|     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)) | ||||
|         return Loader::ResultStatus::Error; | ||||
|         return Loader::ResultStatus::ErrorBadFileAccessControl; | ||||
|     if (sizeof(FileAccessHeader) != file->ReadObject(&aci_file_access, aci_header.fah_offset)) | ||||
|         return Loader::ResultStatus::Error; | ||||
|         return Loader::ResultStatus::ErrorBadFileAccessHeader; | ||||
|  | ||||
|     return Loader::ResultStatus::Success; | ||||
| } | ||||
|   | ||||
| @@ -83,13 +83,13 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load( | ||||
|  | ||||
|     if (dir == nullptr) { | ||||
|         if (file == nullptr) | ||||
|             return ResultStatus::ErrorInvalidFormat; | ||||
|             return ResultStatus::ErrorNullFile; | ||||
|         dir = file->GetContainingDirectory(); | ||||
|     } | ||||
|  | ||||
|     const FileSys::VirtualFile npdm = dir->GetFile("main.npdm"); | ||||
|     if (npdm == nullptr) | ||||
|         return ResultStatus::ErrorInvalidFormat; | ||||
|         return ResultStatus::ErrorMissingNPDM; | ||||
|  | ||||
|     ResultStatus result = metadata.Load(npdm); | ||||
|     if (result != ResultStatus::Success) { | ||||
| @@ -99,7 +99,7 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load( | ||||
|  | ||||
|     const FileSys::ProgramAddressSpaceType arch_bits{metadata.GetAddressSpaceType()}; | ||||
|     if (arch_bits == FileSys::ProgramAddressSpaceType::Is32Bit) { | ||||
|         return ResultStatus::ErrorUnsupportedArch; | ||||
|         return ResultStatus::Error32BitISA; | ||||
|     } | ||||
|  | ||||
|     // Load NSO modules | ||||
| @@ -143,28 +143,28 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load( | ||||
|  | ||||
| ResultStatus AppLoader_DeconstructedRomDirectory::ReadRomFS(FileSys::VirtualFile& dir) { | ||||
|     if (romfs == nullptr) | ||||
|         return ResultStatus::ErrorNotUsed; | ||||
|         return ResultStatus::ErrorNoRomFS; | ||||
|     dir = romfs; | ||||
|     return ResultStatus::Success; | ||||
| } | ||||
|  | ||||
| ResultStatus AppLoader_DeconstructedRomDirectory::ReadIcon(std::vector<u8>& buffer) { | ||||
|     if (icon_data.empty()) | ||||
|         return ResultStatus::ErrorNotUsed; | ||||
|         return ResultStatus::ErrorNoIcon; | ||||
|     buffer = icon_data; | ||||
|     return ResultStatus::Success; | ||||
| } | ||||
|  | ||||
| ResultStatus AppLoader_DeconstructedRomDirectory::ReadProgramId(u64& out_program_id) { | ||||
|     if (name.empty()) | ||||
|         return ResultStatus::ErrorNotUsed; | ||||
|         return ResultStatus::ErrorNoControl; | ||||
|     out_program_id = title_id; | ||||
|     return ResultStatus::Success; | ||||
| } | ||||
|  | ||||
| ResultStatus AppLoader_DeconstructedRomDirectory::ReadTitle(std::string& title) { | ||||
|     if (name.empty()) | ||||
|         return ResultStatus::ErrorNotUsed; | ||||
|         return ResultStatus::ErrorNoControl; | ||||
|     title = name; | ||||
|     return ResultStatus::Success; | ||||
| } | ||||
|   | ||||
| @@ -390,7 +390,7 @@ ResultStatus AppLoader_ELF::Load(Kernel::SharedPtr<Kernel::Process>& process) { | ||||
|  | ||||
|     std::vector<u8> buffer = file->ReadAllBytes(); | ||||
|     if (buffer.size() != file->GetSize()) | ||||
|         return ResultStatus::Error; | ||||
|         return ResultStatus::ErrorIncorrectELFFileSize; | ||||
|  | ||||
|     ElfReader elf_reader(&buffer[0]); | ||||
|     SharedPtr<CodeSet> codeset = elf_reader.LoadInto(Memory::PROCESS_IMAGE_VADDR); | ||||
|   | ||||
| @@ -86,6 +86,55 @@ std::string GetFileTypeString(FileType type) { | ||||
|     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 | ||||
|  * @param file The file to load | ||||
|   | ||||
| @@ -58,18 +58,46 @@ std::string GetFileTypeString(FileType type); | ||||
| /// Return type for functions in Loader namespace | ||||
| enum class ResultStatus { | ||||
|     Success, | ||||
|     Error, | ||||
|     ErrorInvalidFormat, | ||||
|     ErrorNotImplemented, | ||||
|     ErrorNotLoaded, | ||||
|     ErrorNotUsed, | ||||
|     ErrorAlreadyLoaded, | ||||
|     ErrorMemoryAllocationFailed, | ||||
|     ErrorMissingKeys, | ||||
|     ErrorDecrypting, | ||||
|     ErrorUnsupportedArch, | ||||
|     ErrorNotImplemented, | ||||
|     ErrorNotInitialized, | ||||
|     ErrorBadNPDMHeader, | ||||
|     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 | ||||
| class AppLoader : NonCopyable { | ||||
| public: | ||||
|   | ||||
| @@ -46,12 +46,12 @@ ResultStatus AppLoader_NCA::Load(Kernel::SharedPtr<Kernel::Process>& process) { | ||||
|     } | ||||
|  | ||||
|     if (nca->GetType() != FileSys::NCAContentType::Program) | ||||
|         return ResultStatus::ErrorInvalidFormat; | ||||
|         return ResultStatus::ErrorNCANotProgram; | ||||
|  | ||||
|     const auto exefs = nca->GetExeFS(); | ||||
|  | ||||
|     if (exefs == nullptr) | ||||
|         return ResultStatus::ErrorInvalidFormat; | ||||
|         return ResultStatus::ErrorNoExeFS; | ||||
|  | ||||
|     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) { | ||||
|     if (nca == nullptr) | ||||
|         return ResultStatus::ErrorNotLoaded; | ||||
|         return ResultStatus::ErrorNotInitialized; | ||||
|     if (nca->GetRomFS() == nullptr || nca->GetRomFS()->GetSize() == 0) | ||||
|         return ResultStatus::ErrorNotUsed; | ||||
|         return ResultStatus::ErrorNoRomFS; | ||||
|     dir = nca->GetRomFS(); | ||||
|     return ResultStatus::Success; | ||||
| } | ||||
|  | ||||
| ResultStatus AppLoader_NCA::ReadProgramId(u64& out_program_id) { | ||||
|     if (nca == nullptr || nca->GetStatus() != ResultStatus::Success) | ||||
|         return ResultStatus::ErrorInvalidFormat; | ||||
|         return ResultStatus::ErrorNotInitialized; | ||||
|     out_program_id = nca->GetTitleId(); | ||||
|     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}; | ||||
|  | ||||
|     if (!LoadNro(file, base_addr)) { | ||||
|         return ResultStatus::ErrorInvalidFormat; | ||||
|         return ResultStatus::ErrorLoadingNRO; | ||||
|     } | ||||
|  | ||||
|     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) { | ||||
|     if (icon_data.empty()) { | ||||
|         return ResultStatus::ErrorNotUsed; | ||||
|         return ResultStatus::ErrorNoIcon; | ||||
|     } | ||||
|  | ||||
|     buffer = icon_data; | ||||
| @@ -206,7 +206,7 @@ ResultStatus AppLoader_NRO::ReadIcon(std::vector<u8>& buffer) { | ||||
|  | ||||
| ResultStatus AppLoader_NRO::ReadProgramId(u64& out_program_id) { | ||||
|     if (nacp == nullptr) { | ||||
|         return ResultStatus::ErrorNotUsed; | ||||
|         return ResultStatus::ErrorNoControl; | ||||
|     } | ||||
|  | ||||
|     out_program_id = nacp->GetTitleId(); | ||||
| @@ -215,7 +215,7 @@ ResultStatus AppLoader_NRO::ReadProgramId(u64& out_program_id) { | ||||
|  | ||||
| ResultStatus AppLoader_NRO::ReadRomFS(FileSys::VirtualFile& dir) { | ||||
|     if (romfs == nullptr) { | ||||
|         return ResultStatus::ErrorNotUsed; | ||||
|         return ResultStatus::ErrorNoRomFS; | ||||
|     } | ||||
|  | ||||
|     dir = romfs; | ||||
| @@ -224,7 +224,7 @@ ResultStatus AppLoader_NRO::ReadRomFS(FileSys::VirtualFile& dir) { | ||||
|  | ||||
| ResultStatus AppLoader_NRO::ReadTitle(std::string& title) { | ||||
|     if (nacp == nullptr) { | ||||
|         return ResultStatus::ErrorNotUsed; | ||||
|         return ResultStatus::ErrorNoControl; | ||||
|     } | ||||
|  | ||||
|     title = nacp->GetApplicationName(); | ||||
|   | ||||
| @@ -66,10 +66,13 @@ ResultStatus AppLoader_XCI::Load(Kernel::SharedPtr<Kernel::Process>& process) { | ||||
|         return ResultStatus::ErrorAlreadyLoaded; | ||||
|     } | ||||
|  | ||||
|     if (xci->GetStatus() != ResultStatus::Success) | ||||
|         return xci->GetStatus(); | ||||
|  | ||||
|     if (xci->GetNCAFileByType(FileSys::NCAContentType::Program) == nullptr) { | ||||
|         if (!Core::Crypto::KeyManager::KeyFileExists(false)) | ||||
|             return ResultStatus::ErrorMissingKeys; | ||||
|         return ResultStatus::ErrorDecrypting; | ||||
|             return ResultStatus::ErrorMissingProductionKeyFile; | ||||
|         return ResultStatus::ErrorXCIMissingProgramNCA; | ||||
|     } | ||||
|  | ||||
|     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) { | ||||
|     if (icon_file == nullptr) | ||||
|         return ResultStatus::ErrorInvalidFormat; | ||||
|         return ResultStatus::ErrorNoControl; | ||||
|     buffer = icon_file->ReadAllBytes(); | ||||
|     return ResultStatus::Success; | ||||
| } | ||||
|  | ||||
| ResultStatus AppLoader_XCI::ReadTitle(std::string& title) { | ||||
|     if (nacp_file == nullptr) | ||||
|         return ResultStatus::ErrorInvalidFormat; | ||||
|         return ResultStatus::ErrorNoControl; | ||||
|     title = nacp_file->GetApplicationName(); | ||||
|     return ResultStatus::Success; | ||||
| } | ||||
|   | ||||
| @@ -453,10 +453,8 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign | ||||
|             std::string name = " "; | ||||
|             const auto res3 = loader->ReadTitle(name); | ||||
|  | ||||
|             if ((res1 == Loader::ResultStatus::ErrorNotUsed || | ||||
|                  res1 == Loader::ResultStatus::ErrorNotImplemented) && | ||||
|                 (res3 == Loader::ResultStatus::ErrorNotUsed || | ||||
|                  res3 == Loader::ResultStatus::ErrorNotImplemented) && | ||||
|             if (res1 != Loader::ResultStatus::Success && | ||||
|                 res3 != Loader::ResultStatus::Success && | ||||
|                 res2 == Loader::ResultStatus::Success) { | ||||
|                 // Use from metadata pool. | ||||
|                 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!"), | ||||
|                                   tr("The ROM format is not supported.")); | ||||
|             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: | ||||
|             LOG_CRITICAL(Frontend, "Failed to load ROM!"); | ||||
|             QMessageBox::critical(this, tr("Error while loading ROM!"), | ||||
|                                   tr("Could not determine the system mode.")); | ||||
|             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: | ||||
|             QMessageBox::critical( | ||||
|                 this, tr("An error occurred initializing the video core."), | ||||
| @@ -499,9 +443,23 @@ bool GMainWindow::LoadROM(const QString& filename) { | ||||
|             break; | ||||
|  | ||||
|         default: | ||||
|             if (static_cast<u32>(result) > | ||||
|                 static_cast<u32>(Core::System::ResultStatus::ErrorLoader)) { | ||||
|                 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; | ||||
|         } | ||||
|         return false; | ||||
|   | ||||
| @@ -174,19 +174,6 @@ int main(int argc, char** argv) { | ||||
|     case Core::System::ResultStatus::ErrorLoader: | ||||
|         LOG_CRITICAL(Frontend, "Failed to load ROM!"); | ||||
|         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: | ||||
|         LOG_CRITICAL(Frontend, "CPUCore not initialized"); | ||||
|         return -1; | ||||
| @@ -198,6 +185,17 @@ int main(int argc, char** argv) { | ||||
|         return -1; | ||||
|     case Core::System::ResultStatus::Success: | ||||
|         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"); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user