From ec3bef7b4c21931918f3a84ad79a53d31b02aeaf Mon Sep 17 00:00:00 2001
From: Zach Hilman <zachhilman@gmail.com>
Date: Thu, 9 Aug 2018 21:06:44 -0400
Subject: [PATCH] loader: Add more descriptive errors

Full list of new errors and descriptions in core/loader/loader.h
---
 src/core/core.cpp                             | 26 +-----
 src/core/core.h                               | 24 ++----
 src/core/file_sys/card_image.cpp              | 19 ++--
 src/core/file_sys/content_archive.cpp         | 86 +++++++++++++++----
 src/core/file_sys/content_archive.h           |  5 +-
 src/core/file_sys/partition_filesystem.cpp    |  8 +-
 src/core/file_sys/program_metadata.cpp        | 12 +--
 .../loader/deconstructed_rom_directory.cpp    | 14 +--
 src/core/loader/elf.cpp                       |  2 +-
 src/core/loader/loader.cpp                    | 49 +++++++++++
 src/core/loader/loader.h                      | 46 ++++++++--
 src/core/loader/nca.cpp                       | 10 +--
 src/core/loader/nro.cpp                       | 10 +--
 src/core/loader/xci.cpp                       | 11 ++-
 src/yuzu/game_list.cpp                        |  6 +-
 src/yuzu/main.cpp                             | 76 ++++------------
 src/yuzu_cmd/yuzu.cpp                         | 24 +++---
 17 files changed, 249 insertions(+), 179 deletions(-)

diff --git a/src/core/core.cpp b/src/core/core.cpp
index 69c45c026..6b8004eb2 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -102,18 +102,8 @@ 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)};
@@ -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;
diff --git a/src/core/core.h b/src/core/core.h
index 7cf7ea4e1..2944b09cd 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -49,21 +49,15 @@ public:
 
     /// Enumeration representing the return values of the System Initialize and Load process.
     enum class ResultStatus : u32 {
-        Success,                      ///< Succeeded
-        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
+        Success,             ///< Succeeded
+        ErrorNotInitialized, ///< Error trying to use core prior to initialization
+        ErrorGetLoader,      ///< Error finding the correct application loader
+        ErrorSystemMode,     ///< Error determining the system mode
+        ErrorSystemFiles,    ///< Error in finding system files
+        ErrorSharedFont,     ///< Error in finding shared font
+        ErrorVideoCore,      ///< Error in the video core
+        ErrorUnknown,        ///< Any other error
+        ErrorLoader,         ///< The base for loader errors (too many to repeat)
     };
 
     /**
diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp
index e897d9913..a4823353e 100644
--- a/src/core/file_sys/card_image.cpp
+++ b/src/core/file_sys/card_image.cpp
@@ -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;
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp
index d3007d981..47afcad9b 100644
--- a/src/core/file_sys/content_archive.cpp
+++ b/src/core/file_sys/content_archive.cpp
@@ -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;
             }
         }
diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h
index 5cfd5031a..b82e65ad5 100644
--- a/src/core/file_sys/content_archive.h
+++ b/src/core/file_sys/content_archive.h
@@ -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{};
 
diff --git a/src/core/file_sys/partition_filesystem.cpp b/src/core/file_sys/partition_filesystem.cpp
index 47e032b19..c377edc9c 100644
--- a/src/core/file_sys/partition_filesystem.cpp
+++ b/src/core/file_sys/partition_filesystem.cpp
@@ -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;
     }
 
diff --git a/src/core/file_sys/program_metadata.cpp b/src/core/file_sys/program_metadata.cpp
index 63d4b6e4f..279f987d4 100644
--- a/src/core/file_sys/program_metadata.cpp
+++ b/src/core/file_sys/program_metadata.cpp
@@ -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;
 }
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp
index 915d525b0..de05f21d8 100644
--- a/src/core/loader/deconstructed_rom_directory.cpp
+++ b/src/core/loader/deconstructed_rom_directory.cpp
@@ -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;
 }
diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp
index a7133f5a6..401cad3ab 100644
--- a/src/core/loader/elf.cpp
+++ b/src/core/loader/elf.cpp
@@ -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);
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp
index a288654df..2f5bfc67c 100644
--- a/src/core/loader/loader.cpp
+++ b/src/core/loader/loader.cpp
@@ -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
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index 6a9e5a68b..cfdadbee3 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -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:
diff --git a/src/core/loader/nca.cpp b/src/core/loader/nca.cpp
index 46f5cd393..8498cc94b 100644
--- a/src/core/loader/nca.cpp
+++ b/src/core/loader/nca.cpp
@@ -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;
 }
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp
index dc053cdad..908d91eab 100644
--- a/src/core/loader/nro.cpp
+++ b/src/core/loader/nro.cpp
@@ -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();
diff --git a/src/core/loader/xci.cpp b/src/core/loader/xci.cpp
index d3fe24419..5d67fb186 100644
--- a/src/core/loader/xci.cpp
+++ b/src/core/loader/xci.cpp
@@ -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;
 }
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index 1c738d2a4..d0455cbca 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -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()) {
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 67e3c6549..94fb8ae6a 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -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:
-            QMessageBox::critical(
-                this, tr("Error while loading ROM!"),
-                tr("An unknown error occurred. Please see the log for more details."));
+            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;
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index 0605c92e3..e44a98311 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -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");