diff --git a/src/android/app/src/main/java/org/citra/citra_emu/NativeLibrary.kt b/src/android/app/src/main/java/org/citra/citra_emu/NativeLibrary.kt
index 1ef5c8d83..72035cae4 100644
--- a/src/android/app/src/main/java/org/citra/citra_emu/NativeLibrary.kt
+++ b/src/android/app/src/main/java/org/citra/citra_emu/NativeLibrary.kt
@@ -465,26 +465,51 @@ object NativeLibrary {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
emulationActivity = requireActivity() as EmulationActivity
- val result = requireArguments().getInt(RESULT_CODE)
- var captionString = getString(R.string.loader_error_invalid_format)
- if (result == CoreError.ErrorLoader_ErrorEncrypted.value) {
- captionString = getString(R.string.loader_error_encrypted)
- }
- if (result == CoreError.ErrorArticDisconnected.value) {
- captionString = getString(R.string.artic_base)
+ var coreError = CoreError.fromInt(requireArguments().getInt(RESULT_CODE))
+ val title: String
+ val message: String
+ when (coreError) {
+ CoreError.ErrorGetLoader, CoreError.ErrorLoader_ErrorInvalidFormat, CoreError.ErrorSystemMode -> {
+ title = getString(R.string.loader_error_invalid_format)
+ message = getString(R.string.loader_error_invalid_format_description)
+ }
+
+ CoreError.ErrorLoader_ErrorEncrypted -> {
+ title = getString(R.string.loader_error_encrypted)
+ message = getString(R.string.loader_error_encrypted_description)
+ }
+
+ CoreError.ErrorArticDisconnected -> {
+ title = getString(R.string.artic_base)
+ message = getString(R.string.artic_server_comm_error)
+ }
+
+ CoreError.ErrorN3DSApplication -> {
+ title = getString(R.string.loader_error_invalid_system_mode)
+ message = getString(R.string.loader_error_invalid_system_mode_description)
+ }
+
+ CoreError.ErrorLoader_ErrorPatches -> {
+ title = getString(R.string.loader_error_applying_patches)
+ message = getString(R.string.loader_error_applying_patches_description)
+ }
+
+ CoreError.ErrorLoader_ErrorPatchesInvalidTitle -> {
+ title = getString(R.string.loader_error_applying_patches)
+ message = getString(R.string.loader_error_patch_wrong_application)
+ }
+
+ else -> {
+ title = getString(R.string.loader_error_generic_title)
+ message = getString(R.string.loader_error_generic,
+ getString(coreError.stringRes), coreError.value)
+ }
}
val alert = MaterialAlertDialogBuilder(requireContext())
- .setTitle(captionString)
+ .setTitle(title)
.setMessage(
- Html.fromHtml(
- if (result == CoreError.ErrorArticDisconnected.value)
- getString(R.string.artic_server_comm_error)
- else if (result == CoreError.ErrorLoader_ErrorEncrypted.value)
- getString(R.string.loader_error_encrypted_desc)
- else
- getString(R.string.loader_error_generic,
- getString(CoreError.fromInt(result).stringRes), result),
+ Html.fromHtml(message,
Html.FROM_HTML_MODE_LEGACY
)
)
@@ -861,14 +886,16 @@ object NativeLibrary {
ErrorLoader_ErrorEncrypted(5, R.string.core_error_loader_encrypted),
ErrorLoader_ErrorInvalidFormat(6, R.string.core_error_loader_invalid_format),
ErrorLoader_ErrorGBATitle(7, R.string.core_error_loader_gba_title),
- ErrorSystemFiles(8, R.string.core_error_system_files),
- ErrorSavestate(9, R.string.core_error_savestate),
- ErrorArticDisconnected(10, R.string.core_error_artic_disconnected),
- ErrorN3DSApplication(11, R.string.core_error_n3ds_application),
- ErrorCoreExceptionRaised(12, R.string.core_error_core_exception_raised),
- ErrorMemoryExceptionRaised(13, R.string.core_error_memory_exception_raised),
- ShutdownRequested(14, R.string.core_error_shutdown_requested),
- ErrorUnknown(15, R.string.core_error_unknown);
+ ErrorLoader_ErrorPatches(8, R.string.core_error_loader_error_patches),
+ ErrorLoader_ErrorPatchesInvalidTitle(9, R.string.core_error_loader_patches_invalid_title),
+ ErrorSystemFiles(10, R.string.core_error_system_files),
+ ErrorSavestate(11, R.string.core_error_savestate),
+ ErrorArticDisconnected(12, R.string.core_error_artic_disconnected),
+ ErrorN3DSApplication(13, R.string.core_error_n3ds_application),
+ ErrorCoreExceptionRaised(14, R.string.core_error_core_exception_raised),
+ ErrorMemoryExceptionRaised(15, R.string.core_error_memory_exception_raised),
+ ShutdownRequested(16, R.string.core_error_shutdown_requested),
+ ErrorUnknown(17, R.string.core_error_unknown);
companion object {
fun fromInt(value: Int): CoreError {
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml
index 72e7eecf7..101eb4648 100644
--- a/src/android/app/src/main/res/values/strings.xml
+++ b/src/android/app/src/main/res/values/strings.xml
@@ -435,11 +435,19 @@
Layout
+ Error loading application
+ Invalid application format
+ Please make sure you are using one of the compatible file formats:- Cartridge images: .cci/.zcci/.3ds
- Installable archives: .cia/.zcia
- Homebrew titles: .3dsx/.z3dsx
- NCCH containers: .cxi/.zcxi/.app
- ELF files: .elf/.axf
]]>
+ Invalid system mode
+ New 3DS exclusive applications cannot be loaded without enabling the New 3DS mode.
+ Error applying patches
+ A generic error occurred while applying a patch to the application. Please check the log for more details.
+ Failed to apply a patch because it is designed for a different application. Please make sure you are using the patches for the right application, region and version.
Your ROM is Encrypted
- blog post for more information.]]>
- Invalid ROM format
+ blog post for more information.]]>
ROM file does not exist
No bootable game present!
+
An error occurred while loading ROM: \"%s (%d)\"
Success
Not initialized
@@ -449,6 +457,8 @@
Encrypted file
Corrupted file
File is GBA title
+ Error applying patches
+ Patches are for a different application
Missing system files
Savestate failed
Artic Base disconnected
diff --git a/src/citra_qt/citra_qt.cpp b/src/citra_qt/citra_qt.cpp
index 180591c9f..bda6d0816 100644
--- a/src/citra_qt/citra_qt.cpp
+++ b/src/citra_qt/citra_qt.cpp
@@ -1366,61 +1366,39 @@ bool GMainWindow::LoadROM(const QString& filename) {
system.Load(*render_window, filename.toStdString(), secondary_window)};
if (result != Core::System::ResultStatus::Success) {
+ QString invalid_format = tr("Invalid application format");
+ QString invalid_format_description =
+ tr("The application file format not supported.
Please make sure you are using one "
+ "of the compatible file formats:
- Cartridge images: "
+ ".cci/.zcci/.3ds
- Installable archives: "
+ ".cia/.zcia
- Homebrew titles: .3dsx/.z3dsx
- NCCH "
+ "containers: .cxi/.zcxi/.app
- ELF files: .elf/.axf
");
+
switch (result) {
case Core::System::ResultStatus::ErrorGetLoader:
LOG_CRITICAL(Frontend, "Failed to obtain loader for {}", filename.toStdString());
- QMessageBox::critical(
- this, tr("Invalid App Format"),
- tr("Your app format is not supported.
Please follow the guides to redump your "
- "game "
- "cartridges or "
- "installed "
- "titles."));
+ QMessageBox::critical(this, invalid_format, invalid_format_description);
break;
case Core::System::ResultStatus::ErrorSystemMode:
- LOG_CRITICAL(Frontend, "Failed to load App!");
- QMessageBox::critical(
- this, tr("App Corrupted"),
- tr("Your app is corrupted.
Please follow the guides to redump your "
- "game "
- "cartridges or "
- "installed "
- "titles."));
+ LOG_CRITICAL(Frontend, "Failed to load application!");
+ QMessageBox::critical(this, invalid_format, invalid_format_description);
break;
case Core::System::ResultStatus::ErrorLoader_ErrorEncrypted: {
- QMessageBox::critical(this, tr("App Encrypted"),
- tr("Your app is encrypted.
"
+ QMessageBox::critical(this, tr("Encrypted application"),
+ tr("Encrypted applications are not supported.
"
""
"Please check our blog for more info."));
break;
}
case Core::System::ResultStatus::ErrorLoader_ErrorInvalidFormat:
- QMessageBox::critical(
- this, tr("Invalid App Format"),
- tr("Your app format is not supported.
Please follow the guides to redump your "
- "game "
- "cartridges or "
- "installed "
- "titles."));
+ QMessageBox::critical(this, invalid_format, invalid_format_description);
break;
case Core::System::ResultStatus::ErrorLoader_ErrorGbaTitle:
- QMessageBox::critical(this, tr("Unsupported App"),
+ QMessageBox::critical(this, tr("Unsupported application"),
tr("GBA Virtual Console is not supported by Azahar."));
break;
@@ -1437,10 +1415,27 @@ bool GMainWindow::LoadROM(const QString& filename) {
tr("New 3DS exclusive applications cannot be loaded without "
"enabling the New 3DS mode."));
break;
+ case Core::System::ResultStatus::ErrorLoader:
+ QMessageBox::critical(this, tr("Generic load error"),
+ tr("An generic load error occurred while loading the "
+ "application.
Please check the log for more details."));
+ break;
+ case Core::System::ResultStatus::ErrorLoader_ErrorPatches:
+ QMessageBox::critical(this, tr("Error applying patches"),
+ tr("A generic error occurred while applying a patch to the "
+ "application.
Please check the log for more details."));
+ break;
+ case Core::System::ResultStatus::ErrorLoader_ErrorPatchesInvalidTitle:
+ QMessageBox::critical(
+ this, tr("Error applying patches"),
+ tr("Failed to apply a patch because it is designed for a different "
+ "application.
Please make sure you are using the patches for "
+ "the right application, region and version."));
+ break;
default:
QMessageBox::critical(
- this, tr("Error while loading App!"),
- tr("An unknown error occurred. Please see the log for more details."));
+ this, tr("Error while loading application"),
+ tr("An unknown error occurred.
Please see the log for more details."));
break;
}
return false;
@@ -1499,7 +1494,8 @@ void GMainWindow::BootGame(const QString& filename) {
auto loader = Loader::GetLoader(path);
u64 title_id{0};
- Loader::ResultStatus res = loader->ReadProgramId(title_id);
+ Loader::ResultStatus res =
+ loader ? loader->ReadProgramId(title_id) : Loader::ResultStatus::Error;
if (Loader::ResultStatus::Success == res) {
// Load per game settings
@@ -1512,7 +1508,7 @@ void GMainWindow::BootGame(const QString& filename) {
// Artic Server cannot accept a client multiple times, so multiple loaders are not
// possible. Instead register the app loader early and do not create it again on system load.
- if (!loader->SupportsMultipleInstancesForSameFile()) {
+ if (loader && !loader->SupportsMultipleInstancesForSameFile()) {
system.RegisterAppLoaderEarly(loader);
}
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 28af9f4f0..f9dfe1534 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -434,6 +434,10 @@ System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::st
return ResultStatus::ErrorLoader_ErrorInvalidFormat;
case Loader::ResultStatus::ErrorGbaTitle:
return ResultStatus::ErrorLoader_ErrorGbaTitle;
+ case Loader::ResultStatus::ErrorPatches:
+ return ResultStatus::ErrorLoader_ErrorPatches;
+ case Loader::ResultStatus::ErrorPatchesInvalidTitle:
+ return ResultStatus::ErrorLoader_ErrorPatchesInvalidTitle;
case Loader::ResultStatus::ErrorArtic:
return ResultStatus::ErrorArticDisconnected;
default:
diff --git a/src/core/core.h b/src/core/core.h
index 971bd7b32..c46a196b2 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -99,11 +99,13 @@ public:
/// invalid format
ErrorLoader_ErrorGbaTitle, ///< Error loading the specified application as it is GBA Virtual
///< Console
- ErrorSystemFiles, ///< Error in finding system files
- ErrorSavestate, ///< Error saving or loading
- ErrorArticDisconnected, ///< Error when artic base disconnects
- ErrorN3DSApplication, ///< Error launching New 3DS application in Old 3DS mode
- ErrorCoreExceptionRaised, ///< The CPU emulation raised an exception
+ ErrorLoader_ErrorPatches, ///< Generic error while loading patches for an application
+ ErrorLoader_ErrorPatchesInvalidTitle, ///< A patch was loaded for the incorrect application
+ ErrorSystemFiles, ///< Error in finding system files
+ ErrorSavestate, ///< Error saving or loading
+ ErrorArticDisconnected, ///< Error when artic base disconnects
+ ErrorN3DSApplication, ///< Error launching New 3DS application in Old 3DS mode
+ ErrorCoreExceptionRaised, ///< The CPU emulation raised an exception
ErrorMemoryExceptionRaised, ///< Unmmaped memory was accessed
ShutdownRequested, ///< Emulated program requested a system shutdown
ErrorUnknown ///< Any other error
diff --git a/src/core/file_sys/layered_fs.cpp b/src/core/file_sys/layered_fs.cpp
index 1cac97be4..93ce3e854 100644
--- a/src/core/file_sys/layered_fs.cpp
+++ b/src/core/file_sys/layered_fs.cpp
@@ -1,4 +1,4 @@
-// Copyright 2020 Citra Emulator Project
+// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@@ -247,14 +247,14 @@ void LayeredFS::LoadExtRelocations() {
std::vector buffer(file.relocation.size); // Original size
romfs->ReadFile(file.relocation.original_offset, buffer.size(), buffer.data());
- bool ret = false;
+ Loader::ResultStatus ret{};
if (extension == ".ips") {
ret = Patch::ApplyIpsPatch(patch, buffer);
} else {
ret = Patch::ApplyBpsPatch(patch, buffer);
}
- if (ret) {
+ if (ret == Loader::ResultStatus::Success) {
LOG_INFO(Service_FS, "LayeredFS patched file {}", file_path);
file.relocation.type = 2;
diff --git a/src/core/file_sys/layered_fs.h b/src/core/file_sys/layered_fs.h
index 69f6e5939..e51bd8f1c 100644
--- a/src/core/file_sys/layered_fs.h
+++ b/src/core/file_sys/layered_fs.h
@@ -1,4 +1,4 @@
-// Copyright 2020 Citra Emulator Project
+// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
diff --git a/src/core/file_sys/ncch_container.cpp b/src/core/file_sys/ncch_container.cpp
index 382c8943e..c51de40a9 100644
--- a/src/core/file_sys/ncch_container.cpp
+++ b/src/core/file_sys/ncch_container.cpp
@@ -518,7 +518,7 @@ Loader::ResultStatus NCCHContainer::LoadSectionExeFS(const char* name, std::vect
Loader::ResultStatus NCCHContainer::ApplyCodePatch(std::vector& code) const {
struct PatchLocation {
std::string path;
- bool (*patch_fn)(const std::vector& patch, std::vector& code);
+ Loader::ResultStatus (*patch_fn)(const std::vector& patch, std::vector& code);
};
const auto mods_path =
@@ -555,11 +555,12 @@ Loader::ResultStatus NCCHContainer::ApplyCodePatch(std::vector& code) const
std::vector patch(patch_file.GetSize());
if (patch_file.ReadBytes(patch.data(), patch.size()) != patch.size())
- return Loader::ResultStatus::Error;
+ return Loader::ResultStatus::ErrorPatches;
LOG_INFO(Service_FS, "File {} patching code.bin", info.path);
- if (!info.patch_fn(patch, code))
- return Loader::ResultStatus::Error;
+ auto patch_result = info.patch_fn(patch, code);
+ if (patch_result != Loader::ResultStatus::Success)
+ return patch_result;
return Loader::ResultStatus::Success;
}
diff --git a/src/core/file_sys/patch.cpp b/src/core/file_sys/patch.cpp
index fecc7fe05..83d3989eb 100644
--- a/src/core/file_sys/patch.cpp
+++ b/src/core/file_sys/patch.cpp
@@ -1,4 +1,4 @@
-// Copyright 2019 Citra Emulator Project
+// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@@ -14,21 +14,21 @@
namespace FileSys::Patch {
-bool ApplyIpsPatch(const std::vector& ips, std::vector& buffer) {
+Loader::ResultStatus ApplyIpsPatch(const std::vector& ips, std::vector& buffer) {
std::size_t cursor = 5;
std::size_t patch_length = ips.size() - 3;
std::string ips_header(ips.begin(), ips.begin() + 5);
if (ips_header != "PATCH") {
LOG_INFO(Service_FS, "Attempted to load invalid IPS");
- return false;
+ return Loader::ResultStatus::ErrorPatches;
}
while (cursor < patch_length) {
std::string eof_check(ips.begin() + cursor, ips.begin() + cursor + 3);
if (eof_check == "EOF")
- return false;
+ break;
std::size_t offset = ips[cursor] << 16 | ips[cursor + 1] << 8 | ips[cursor + 2];
std::size_t length = ips[cursor + 3] << 8 | ips[cursor + 4];
@@ -38,7 +38,7 @@ bool ApplyIpsPatch(const std::vector& ips, std::vector& buffer) {
length = ips[cursor + 5] << 8 | ips[cursor + 6];
if (buffer.size() < offset + length)
- return false;
+ return Loader::ResultStatus::ErrorPatches;
for (u32 i = 0; i < length; ++i)
buffer[offset + i] = ips[cursor + 7];
@@ -49,12 +49,12 @@ bool ApplyIpsPatch(const std::vector& ips, std::vector& buffer) {
}
if (buffer.size() < offset + length)
- return false;
+ return Loader::ResultStatus::ErrorPatches;
std::memcpy(&buffer[offset], &ips[cursor + 5], length);
cursor += length + 5;
}
- return true;
+ return Loader::ResultStatus::Success;
}
namespace Bps {
@@ -149,11 +149,11 @@ public:
PatchApplier(Stream source, Stream target, Stream patch)
: m_source{source}, m_target{target}, m_patch{patch} {}
- bool Apply() {
+ Loader::ResultStatus Apply() {
const auto magic = *m_patch.Read>();
if (std::string_view(magic.data(), magic.size()) != "BPS1") {
LOG_ERROR(Service_FS, "Invalid BPS magic");
- return false;
+ return Loader::ResultStatus::ErrorPatches;
}
const Bps::Number source_size = m_patch.ReadNumber();
@@ -161,7 +161,7 @@ public:
const Bps::Number metadata_size = m_patch.ReadNumber();
if (source_size > m_source.size() || target_size > m_target.size() || metadata_size != 0) {
LOG_ERROR(Service_FS, "Invalid sizes");
- return false;
+ return Loader::ResultStatus::ErrorPatches;
}
const std::size_t command_start_offset = m_patch.Tell();
@@ -173,22 +173,22 @@ public:
if (crc32(m_source.data(), source_size) != source_crc32) {
LOG_ERROR(Service_FS, "Unexpected source hash");
- return false;
+ return Loader::ResultStatus::ErrorPatchesInvalidTitle;
}
// Process all patch commands.
std::memset(m_target.data(), 0, m_target.size());
while (m_patch.Tell() < command_end_offset) {
if (!HandleCommand())
- return false;
+ return Loader::ResultStatus::ErrorPatches;
}
if (crc32(m_target.data(), target_size) != target_crc32) {
LOG_ERROR(Service_FS, "Unexpected target hash");
- return false;
+ return Loader::ResultStatus::ErrorPatches;
}
- return true;
+ return Loader::ResultStatus::Success;
}
private:
@@ -257,7 +257,7 @@ private:
} // namespace Bps
-bool ApplyBpsPatch(const std::vector& patch, std::vector& buffer) {
+Loader::ResultStatus ApplyBpsPatch(const std::vector& patch, std::vector& buffer) {
Bps::Stream patch_stream{patch.data(), patch.size()};
// Move the offset past the file format marker (i.e. "BPS1")
diff --git a/src/core/file_sys/patch.h b/src/core/file_sys/patch.h
index 9a8118475..6f8993384 100644
--- a/src/core/file_sys/patch.h
+++ b/src/core/file_sys/patch.h
@@ -1,4 +1,4 @@
-// Copyright 2019 Citra Emulator Project
+// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@@ -7,11 +7,12 @@
#include
#include "common/common_types.h"
+#include "core/loader/loader.h"
namespace FileSys::Patch {
-bool ApplyIpsPatch(const std::vector& patch, std::vector& buffer);
+Loader::ResultStatus ApplyIpsPatch(const std::vector& patch, std::vector& buffer);
-bool ApplyBpsPatch(const std::vector& patch, std::vector& buffer);
+Loader::ResultStatus ApplyBpsPatch(const std::vector& patch, std::vector& buffer);
} // namespace FileSys::Patch
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index 9e4c76122..678b48bf7 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -76,6 +76,8 @@ enum class ResultStatus {
ErrorGbaTitle,
ErrorArtic,
ErrorNotFound,
+ ErrorPatches,
+ ErrorPatchesInvalidTitle,
};
constexpr u32 MakeMagic(char a, char b, char c, char d) {