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