forked from ShuriZma/suyu
1
0
Fork 0

Merge pull request #970 from DarkLordZach/loader-errors

loader: Add more descriptive errors
This commit is contained in:
bunnei 2018-08-11 19:25:30 -04:00 committed by GitHub
commit bc286c169f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 248 additions and 179 deletions

View File

@ -102,19 +102,9 @@ 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)};
if (init_result != ResultStatus::Success) { 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)); 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;

View File

@ -53,17 +53,11 @@ public:
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
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 ErrorSystemFiles, ///< Error in finding system files
ErrorSharedFont, ///< Error in finding shared font ErrorSharedFont, ///< Error in finding shared font
ErrorVideoCore, ///< Error in the video core 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)
}; };
/** /**

View File

@ -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;

View File

@ -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;
} }
} }

View File

@ -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{};

View File

@ -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;
} }

View File

@ -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;
} }

View File

@ -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;
} }

View File

@ -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);

View File

@ -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

View File

@ -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:

View File

@ -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;
} }

View File

@ -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();

View File

@ -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;
} }

View File

@ -453,10 +453,7 @@ 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 && res3 != Loader::ResultStatus::Success &&
res1 == Loader::ResultStatus::ErrorNotImplemented) &&
(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()) {

View File

@ -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:
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( QMessageBox::critical(
this, tr("Error while loading ROM!"), this, tr("Error while loading ROM!"),
tr("An unknown error occurred. Please see the log for more details.")); tr("An unknown error occurred. Please see the log for more details."));
}
break; break;
} }
return false; return false;

View File

@ -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");