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:]]> + 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:"); + 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) {