summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/core/core.cpp26
-rw-r--r--src/core/core.h24
-rw-r--r--src/core/file_sys/card_image.cpp19
-rw-r--r--src/core/file_sys/content_archive.cpp86
-rw-r--r--src/core/file_sys/content_archive.h5
-rw-r--r--src/core/file_sys/partition_filesystem.cpp8
-rw-r--r--src/core/file_sys/program_metadata.cpp12
-rw-r--r--src/core/loader/deconstructed_rom_directory.cpp14
-rw-r--r--src/core/loader/elf.cpp2
-rw-r--r--src/core/loader/loader.cpp49
-rw-r--r--src/core/loader/loader.h46
-rw-r--r--src/core/loader/nca.cpp10
-rw-r--r--src/core/loader/nro.cpp10
-rw-r--r--src/core/loader/xci.cpp11
-rw-r--r--src/video_core/engines/shader_bytecode.h7
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp39
-rw-r--r--src/yuzu/game_list.cpp5
-rw-r--r--src/yuzu/main.cpp76
-rw-r--r--src/yuzu_cmd/yuzu.cpp24
19 files changed, 286 insertions, 187 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/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h
index 3e409c2e1..6cb7bea1c 100644
--- a/src/video_core/engines/shader_bytecode.h
+++ b/src/video_core/engines/shader_bytecode.h
@@ -597,6 +597,13 @@ public:
Unknown,
};
+ /// Returns whether an opcode has an execution predicate field or not (ie, whether it can be
+ /// conditionally executed).
+ static bool IsPredicatedInstruction(Id opcode) {
+ // TODO(Subv): Add the rest of unpredicated instructions.
+ return opcode != Id::SSY;
+ }
+
class Matcher {
public:
Matcher(const char* const name, u16 mask, u16 expected, OpCode::Id id, OpCode::Type type)
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index 32f06f409..8954deb81 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -141,6 +141,15 @@ private:
ExitMethod jmp = Scan(target, end, labels);
return exit_method = ParallelExit(no_jmp, jmp);
}
+ case OpCode::Id::SSY: {
+ // The SSY instruction uses a similar encoding as the BRA instruction.
+ ASSERT_MSG(instr.bra.constant_buffer == 0,
+ "Constant buffer SSY is not supported");
+ u32 target = offset + instr.bra.GetBranchTarget();
+ labels.insert(target);
+ // Continue scanning for an exit method.
+ break;
+ }
}
}
}
@@ -828,7 +837,11 @@ private:
ASSERT_MSG(instr.pred.full_pred != Pred::NeverExecute,
"NeverExecute predicate not implemented");
- if (instr.pred.pred_index != static_cast<u64>(Pred::UnusedIndex)) {
+ // Some instructions (like SSY) don't have a predicate field, they are always
+ // unconditionally executed.
+ bool can_be_predicated = OpCode::IsPredicatedInstruction(opcode->GetId());
+
+ if (can_be_predicated && instr.pred.pred_index != static_cast<u64>(Pred::UnusedIndex)) {
shader.AddLine("if (" +
GetPredicateCondition(instr.pred.pred_index, instr.negate_pred != 0) +
')');
@@ -1668,16 +1681,25 @@ private:
break;
}
case OpCode::Id::SSY: {
- // The SSY opcode tells the GPU where to re-converge divergent execution paths, we
- // can ignore this when generating GLSL code.
+ // The SSY opcode tells the GPU where to re-converge divergent execution paths, it
+ // sets the target of the jump that the SYNC instruction will make. The SSY opcode
+ // has a similar structure to the BRA opcode.
+ ASSERT_MSG(instr.bra.constant_buffer == 0, "Constant buffer SSY is not supported");
+
+ u32 target = offset + instr.bra.GetBranchTarget();
+ shader.AddLine("ssy_target = " + std::to_string(target) + "u;");
break;
}
- case OpCode::Id::SYNC:
+ case OpCode::Id::SYNC: {
+ // The SYNC opcode jumps to the address previously set by the SSY opcode
ASSERT(instr.flow.cond == Tegra::Shader::FlowCondition::Always);
+ shader.AddLine("{ jmp_to = ssy_target; break; }");
+ break;
+ }
case OpCode::Id::DEPBAR: {
- // TODO(Subv): Find out if we actually have to care about these instructions or if
+ // TODO(Subv): Find out if we actually have to care about this instruction or if
// the GLSL compiler takes care of that for us.
- LOG_WARNING(HW_GPU, "DEPBAR/SYNC instruction is stubbed");
+ LOG_WARNING(HW_GPU, "DEPBAR instruction is stubbed");
break;
}
default: {
@@ -1691,7 +1713,7 @@ private:
}
// Close the predicate condition scope.
- if (instr.pred.pred_index != static_cast<u64>(Pred::UnusedIndex)) {
+ if (can_be_predicated && instr.pred.pred_index != static_cast<u64>(Pred::UnusedIndex)) {
--shader.scope;
shader.AddLine('}');
}
@@ -1742,6 +1764,7 @@ private:
} else {
labels.insert(subroutine.begin);
shader.AddLine("uint jmp_to = " + std::to_string(subroutine.begin) + "u;");
+ shader.AddLine("uint ssy_target = 0u;");
shader.AddLine("while (true) {");
++shader.scope;
@@ -1757,7 +1780,7 @@ private:
u32 compile_end = CompileRange(label, next_label);
if (compile_end > next_label && compile_end != PROGRAM_END) {
// This happens only when there is a label inside a IF/LOOP block
- shader.AddLine("{ jmp_to = " + std::to_string(compile_end) + "u; break; }");
+ shader.AddLine(" jmp_to = " + std::to_string(compile_end) + "u; break; }");
labels.emplace(compile_end);
}
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index 1c738d2a4..85cb12594 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -453,10 +453,7 @@ 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");