mirror of
https://github.com/azahar-emu/azahar.git
synced 2026-06-06 02:33:44 -04:00
ui: Made rom loading errors more clear and user friendly (#2097)
Some checks are pending
citra-build / source (push) Waiting to run
citra-build / linux-x86_64 (appimage) (push) Waiting to run
citra-build / linux-x86_64 (appimage-wayland) (push) Waiting to run
citra-build / linux-x86_64 (gcc-nopch) (push) Waiting to run
citra-build / linux-arm64 (clang) (push) Waiting to run
citra-build / linux-arm64 (gcc-nopch) (push) Waiting to run
citra-build / macos (push) Waiting to run
citra-build / windows (msvc) (push) Waiting to run
citra-build / windows (msys2) (push) Waiting to run
citra-build / android (googleplay) (push) Waiting to run
citra-build / android (vanilla) (push) Waiting to run
citra-build / docker (push) Waiting to run
citra-format / clang-format (push) Waiting to run
citra-libretro / android (push) Waiting to run
citra-libretro / linux (push) Waiting to run
citra-libretro / windows (push) Waiting to run
citra-libretro / macos (arm64) (push) Waiting to run
citra-libretro / macos (x86_64) (push) Waiting to run
citra-libretro / ios (push) Waiting to run
citra-libretro / tvos (push) Waiting to run
citra-transifex / transifex (push) Waiting to run
Some checks are pending
citra-build / source (push) Waiting to run
citra-build / linux-x86_64 (appimage) (push) Waiting to run
citra-build / linux-x86_64 (appimage-wayland) (push) Waiting to run
citra-build / linux-x86_64 (gcc-nopch) (push) Waiting to run
citra-build / linux-arm64 (clang) (push) Waiting to run
citra-build / linux-arm64 (gcc-nopch) (push) Waiting to run
citra-build / macos (push) Waiting to run
citra-build / windows (msvc) (push) Waiting to run
citra-build / windows (msys2) (push) Waiting to run
citra-build / android (googleplay) (push) Waiting to run
citra-build / android (vanilla) (push) Waiting to run
citra-build / docker (push) Waiting to run
citra-format / clang-format (push) Waiting to run
citra-libretro / android (push) Waiting to run
citra-libretro / linux (push) Waiting to run
citra-libretro / windows (push) Waiting to run
citra-libretro / macos (arm64) (push) Waiting to run
citra-libretro / macos (x86_64) (push) Waiting to run
citra-libretro / ios (push) Waiting to run
citra-libretro / tvos (push) Waiting to run
citra-transifex / transifex (push) Waiting to run
This commit is contained in:
parent
b540725090
commit
921ea178b9
11 changed files with 141 additions and 98 deletions
|
|
@ -465,26 +465,51 @@ object NativeLibrary {
|
||||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
emulationActivity = requireActivity() as EmulationActivity
|
emulationActivity = requireActivity() as EmulationActivity
|
||||||
|
|
||||||
val result = requireArguments().getInt(RESULT_CODE)
|
var coreError = CoreError.fromInt(requireArguments().getInt(RESULT_CODE))
|
||||||
var captionString = getString(R.string.loader_error_invalid_format)
|
val title: String
|
||||||
if (result == CoreError.ErrorLoader_ErrorEncrypted.value) {
|
val message: String
|
||||||
captionString = getString(R.string.loader_error_encrypted)
|
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)
|
||||||
}
|
}
|
||||||
if (result == CoreError.ErrorArticDisconnected.value) {
|
|
||||||
captionString = getString(R.string.artic_base)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val alert = MaterialAlertDialogBuilder(requireContext())
|
val alert = MaterialAlertDialogBuilder(requireContext())
|
||||||
.setTitle(captionString)
|
.setTitle(title)
|
||||||
.setMessage(
|
.setMessage(
|
||||||
Html.fromHtml(
|
Html.fromHtml(message,
|
||||||
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.FROM_HTML_MODE_LEGACY
|
Html.FROM_HTML_MODE_LEGACY
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
@ -861,14 +886,16 @@ object NativeLibrary {
|
||||||
ErrorLoader_ErrorEncrypted(5, R.string.core_error_loader_encrypted),
|
ErrorLoader_ErrorEncrypted(5, R.string.core_error_loader_encrypted),
|
||||||
ErrorLoader_ErrorInvalidFormat(6, R.string.core_error_loader_invalid_format),
|
ErrorLoader_ErrorInvalidFormat(6, R.string.core_error_loader_invalid_format),
|
||||||
ErrorLoader_ErrorGBATitle(7, R.string.core_error_loader_gba_title),
|
ErrorLoader_ErrorGBATitle(7, R.string.core_error_loader_gba_title),
|
||||||
ErrorSystemFiles(8, R.string.core_error_system_files),
|
ErrorLoader_ErrorPatches(8, R.string.core_error_loader_error_patches),
|
||||||
ErrorSavestate(9, R.string.core_error_savestate),
|
ErrorLoader_ErrorPatchesInvalidTitle(9, R.string.core_error_loader_patches_invalid_title),
|
||||||
ErrorArticDisconnected(10, R.string.core_error_artic_disconnected),
|
ErrorSystemFiles(10, R.string.core_error_system_files),
|
||||||
ErrorN3DSApplication(11, R.string.core_error_n3ds_application),
|
ErrorSavestate(11, R.string.core_error_savestate),
|
||||||
ErrorCoreExceptionRaised(12, R.string.core_error_core_exception_raised),
|
ErrorArticDisconnected(12, R.string.core_error_artic_disconnected),
|
||||||
ErrorMemoryExceptionRaised(13, R.string.core_error_memory_exception_raised),
|
ErrorN3DSApplication(13, R.string.core_error_n3ds_application),
|
||||||
ShutdownRequested(14, R.string.core_error_shutdown_requested),
|
ErrorCoreExceptionRaised(14, R.string.core_error_core_exception_raised),
|
||||||
ErrorUnknown(15, R.string.core_error_unknown);
|
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 {
|
companion object {
|
||||||
fun fromInt(value: Int): CoreError {
|
fun fromInt(value: Int): CoreError {
|
||||||
|
|
|
||||||
|
|
@ -435,11 +435,19 @@
|
||||||
<string name="preferences_layout">Layout</string>
|
<string name="preferences_layout">Layout</string>
|
||||||
|
|
||||||
<!-- ROM loading errors -->
|
<!-- ROM loading errors -->
|
||||||
|
<string name="loader_error_generic_title">Error loading application</string>
|
||||||
|
<string name="loader_error_invalid_format">Invalid application format</string>
|
||||||
|
<string name="loader_error_invalid_format_description"><![CDATA[The application file format not supported.<br>Please make sure you are using one of the compatible file formats:<ul><li>Cartridge images: <b>.cci/.zcci/.3ds</b></li><li>Installable archives: <b>.cia/.zcia</b></li><li>Homebrew titles: <b>.3dsx/.z3dsx</b></li><li>NCCH containers: <b>.cxi/.zcxi/.app</b></li><li>ELF files: <b>.elf/.axf</b></li></ul>]]></string>
|
||||||
|
<string name="loader_error_invalid_system_mode">Invalid system mode</string>
|
||||||
|
<string name="loader_error_invalid_system_mode_description">New 3DS exclusive applications cannot be loaded without enabling the New 3DS mode.</string>
|
||||||
|
<string name="loader_error_applying_patches">Error applying patches</string>
|
||||||
|
<string name="loader_error_applying_patches_description">A generic error occurred while applying a patch to the application. Please check the log for more details.</string>
|
||||||
|
<string name="loader_error_patch_wrong_application">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.</string>
|
||||||
<string name="loader_error_encrypted">Your ROM is Encrypted</string>
|
<string name="loader_error_encrypted">Your ROM is Encrypted</string>
|
||||||
<string name="loader_error_encrypted_desc"><![CDATA[Azahar does not support encrypted ROMS. Read our <a href="https://azahar-emu.org/blog/game-loading-changes/">blog post</a> for more information.]]></string>
|
<string name="loader_error_encrypted_description"><![CDATA[Azahar does not support encrypted applications. Read our <a href="https://azahar-emu.org/blog/game-loading-changes/">blog post</a> for more information.]]></string>
|
||||||
<string name="loader_error_invalid_format">Invalid ROM format</string>
|
|
||||||
<string name="loader_error_file_not_found">ROM file does not exist</string>
|
<string name="loader_error_file_not_found">ROM file does not exist</string>
|
||||||
<string name="no_game_present">No bootable game present!</string>
|
<string name="no_game_present">No bootable game present!</string>
|
||||||
|
|
||||||
<string name="loader_error_generic">An error occurred while loading ROM: \"%s (%d)\"</string>
|
<string name="loader_error_generic">An error occurred while loading ROM: \"%s (%d)\"</string>
|
||||||
<string name="core_error_success">Success</string>
|
<string name="core_error_success">Success</string>
|
||||||
<string name="core_error_not_initialized">Not initialized</string>
|
<string name="core_error_not_initialized">Not initialized</string>
|
||||||
|
|
@ -449,6 +457,8 @@
|
||||||
<string name="core_error_loader_encrypted">Encrypted file</string>
|
<string name="core_error_loader_encrypted">Encrypted file</string>
|
||||||
<string name="core_error_loader_invalid_format">Corrupted file</string>
|
<string name="core_error_loader_invalid_format">Corrupted file</string>
|
||||||
<string name="core_error_loader_gba_title">File is GBA title</string>
|
<string name="core_error_loader_gba_title">File is GBA title</string>
|
||||||
|
<string name="core_error_loader_error_patches">Error applying patches</string>
|
||||||
|
<string name="core_error_loader_patches_invalid_title">Patches are for a different application</string>
|
||||||
<string name="core_error_system_files">Missing system files</string>
|
<string name="core_error_system_files">Missing system files</string>
|
||||||
<string name="core_error_savestate">Savestate failed</string>
|
<string name="core_error_savestate">Savestate failed</string>
|
||||||
<string name="core_error_artic_disconnected">Artic Base disconnected</string>
|
<string name="core_error_artic_disconnected">Artic Base disconnected</string>
|
||||||
|
|
|
||||||
|
|
@ -1366,61 +1366,39 @@ bool GMainWindow::LoadROM(const QString& filename) {
|
||||||
system.Load(*render_window, filename.toStdString(), secondary_window)};
|
system.Load(*render_window, filename.toStdString(), secondary_window)};
|
||||||
|
|
||||||
if (result != Core::System::ResultStatus::Success) {
|
if (result != Core::System::ResultStatus::Success) {
|
||||||
|
QString invalid_format = tr("Invalid application format");
|
||||||
|
QString invalid_format_description =
|
||||||
|
tr("The application file format not supported.<br>Please make sure you are using one "
|
||||||
|
"of the compatible file formats:<ul><li>Cartridge images: "
|
||||||
|
"<b>.cci/.zcci/.3ds</b></li><li>Installable archives: "
|
||||||
|
"<b>.cia/.zcia</b></li><li>Homebrew titles: <b>.3dsx/.z3dsx</b></li><li>NCCH "
|
||||||
|
"containers: <b>.cxi/.zcxi/.app</b></li><li>ELF files: <b>.elf/.axf</b></li></ul>");
|
||||||
|
|
||||||
switch (result) {
|
switch (result) {
|
||||||
case Core::System::ResultStatus::ErrorGetLoader:
|
case Core::System::ResultStatus::ErrorGetLoader:
|
||||||
LOG_CRITICAL(Frontend, "Failed to obtain loader for {}", filename.toStdString());
|
LOG_CRITICAL(Frontend, "Failed to obtain loader for {}", filename.toStdString());
|
||||||
QMessageBox::critical(
|
QMessageBox::critical(this, invalid_format, invalid_format_description);
|
||||||
this, tr("Invalid App Format"),
|
|
||||||
tr("Your app format is not supported.<br/>Please follow the guides to redump your "
|
|
||||||
"<a "
|
|
||||||
"href='https://web.archive.org/web/20240304210021/https://citra-emu.org/wiki/"
|
|
||||||
"dumping-game-cartridges/'>game "
|
|
||||||
"cartridges</a> or "
|
|
||||||
"<a "
|
|
||||||
"href='https://web.archive.org/web/20240304210011/https://citra-emu.org/wiki/"
|
|
||||||
"dumping-installed-titles/'>installed "
|
|
||||||
"titles</a>."));
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Core::System::ResultStatus::ErrorSystemMode:
|
case Core::System::ResultStatus::ErrorSystemMode:
|
||||||
LOG_CRITICAL(Frontend, "Failed to load App!");
|
LOG_CRITICAL(Frontend, "Failed to load application!");
|
||||||
QMessageBox::critical(
|
QMessageBox::critical(this, invalid_format, invalid_format_description);
|
||||||
this, tr("App Corrupted"),
|
|
||||||
tr("Your app is corrupted. <br/>Please follow the guides to redump your "
|
|
||||||
"<a "
|
|
||||||
"href='https://web.archive.org/web/20240304210021/https://citra-emu.org/wiki/"
|
|
||||||
"dumping-game-cartridges/'>game "
|
|
||||||
"cartridges</a> or "
|
|
||||||
"<a "
|
|
||||||
"href='https://web.archive.org/web/20240304210011/https://citra-emu.org/wiki/"
|
|
||||||
"dumping-installed-titles/'>installed "
|
|
||||||
"titles</a>."));
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Core::System::ResultStatus::ErrorLoader_ErrorEncrypted: {
|
case Core::System::ResultStatus::ErrorLoader_ErrorEncrypted: {
|
||||||
QMessageBox::critical(this, tr("App Encrypted"),
|
QMessageBox::critical(this, tr("Encrypted application"),
|
||||||
tr("Your app is encrypted. <br/>"
|
tr("Encrypted applications are not supported.<br/>"
|
||||||
"<a "
|
"<a "
|
||||||
"href='https://azahar-emu.org/blog/game-loading-changes/'>"
|
"href='https://azahar-emu.org/blog/game-loading-changes/'>"
|
||||||
"Please check our blog for more info.</a>"));
|
"Please check our blog for more info.</a>"));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Core::System::ResultStatus::ErrorLoader_ErrorInvalidFormat:
|
case Core::System::ResultStatus::ErrorLoader_ErrorInvalidFormat:
|
||||||
QMessageBox::critical(
|
QMessageBox::critical(this, invalid_format, invalid_format_description);
|
||||||
this, tr("Invalid App Format"),
|
|
||||||
tr("Your app format is not supported.<br/>Please follow the guides to redump your "
|
|
||||||
"<a "
|
|
||||||
"href='https://web.archive.org/web/20240304210021/https://citra-emu.org/wiki/"
|
|
||||||
"dumping-game-cartridges/'>game "
|
|
||||||
"cartridges</a> or "
|
|
||||||
"<a "
|
|
||||||
"href='https://web.archive.org/web/20240304210011/https://citra-emu.org/wiki/"
|
|
||||||
"dumping-installed-titles/'>installed "
|
|
||||||
"titles</a>."));
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Core::System::ResultStatus::ErrorLoader_ErrorGbaTitle:
|
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."));
|
tr("GBA Virtual Console is not supported by Azahar."));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
@ -1437,10 +1415,27 @@ bool GMainWindow::LoadROM(const QString& filename) {
|
||||||
tr("New 3DS exclusive applications cannot be loaded without "
|
tr("New 3DS exclusive applications cannot be loaded without "
|
||||||
"enabling the New 3DS mode."));
|
"enabling the New 3DS mode."));
|
||||||
break;
|
break;
|
||||||
|
case Core::System::ResultStatus::ErrorLoader:
|
||||||
|
QMessageBox::critical(this, tr("Generic load error"),
|
||||||
|
tr("An generic load error occurred while loading the "
|
||||||
|
"application.<br/>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.<br/>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.<br/>Please make sure you are using the patches for "
|
||||||
|
"the right application, region and version."));
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
QMessageBox::critical(
|
QMessageBox::critical(
|
||||||
this, tr("Error while loading App!"),
|
this, tr("Error while loading application"),
|
||||||
tr("An unknown error occurred. Please see the log for more details."));
|
tr("An unknown error occurred.<br/>Please see the log for more details."));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -1499,7 +1494,8 @@ void GMainWindow::BootGame(const QString& filename) {
|
||||||
auto loader = Loader::GetLoader(path);
|
auto loader = Loader::GetLoader(path);
|
||||||
|
|
||||||
u64 title_id{0};
|
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) {
|
if (Loader::ResultStatus::Success == res) {
|
||||||
// Load per game settings
|
// 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
|
// 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.
|
// 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);
|
system.RegisterAppLoaderEarly(loader);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -434,6 +434,10 @@ System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::st
|
||||||
return ResultStatus::ErrorLoader_ErrorInvalidFormat;
|
return ResultStatus::ErrorLoader_ErrorInvalidFormat;
|
||||||
case Loader::ResultStatus::ErrorGbaTitle:
|
case Loader::ResultStatus::ErrorGbaTitle:
|
||||||
return ResultStatus::ErrorLoader_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:
|
case Loader::ResultStatus::ErrorArtic:
|
||||||
return ResultStatus::ErrorArticDisconnected;
|
return ResultStatus::ErrorArticDisconnected;
|
||||||
default:
|
default:
|
||||||
|
|
|
||||||
|
|
@ -99,6 +99,8 @@ public:
|
||||||
/// invalid format
|
/// invalid format
|
||||||
ErrorLoader_ErrorGbaTitle, ///< Error loading the specified application as it is GBA Virtual
|
ErrorLoader_ErrorGbaTitle, ///< Error loading the specified application as it is GBA Virtual
|
||||||
///< Console
|
///< Console
|
||||||
|
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
|
ErrorSystemFiles, ///< Error in finding system files
|
||||||
ErrorSavestate, ///< Error saving or loading
|
ErrorSavestate, ///< Error saving or loading
|
||||||
ErrorArticDisconnected, ///< Error when artic base disconnects
|
ErrorArticDisconnected, ///< Error when artic base disconnects
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2020 Citra Emulator Project
|
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
|
@ -247,14 +247,14 @@ void LayeredFS::LoadExtRelocations() {
|
||||||
std::vector<u8> buffer(file.relocation.size); // Original size
|
std::vector<u8> buffer(file.relocation.size); // Original size
|
||||||
romfs->ReadFile(file.relocation.original_offset, buffer.size(), buffer.data());
|
romfs->ReadFile(file.relocation.original_offset, buffer.size(), buffer.data());
|
||||||
|
|
||||||
bool ret = false;
|
Loader::ResultStatus ret{};
|
||||||
if (extension == ".ips") {
|
if (extension == ".ips") {
|
||||||
ret = Patch::ApplyIpsPatch(patch, buffer);
|
ret = Patch::ApplyIpsPatch(patch, buffer);
|
||||||
} else {
|
} else {
|
||||||
ret = Patch::ApplyBpsPatch(patch, buffer);
|
ret = Patch::ApplyBpsPatch(patch, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ret) {
|
if (ret == Loader::ResultStatus::Success) {
|
||||||
LOG_INFO(Service_FS, "LayeredFS patched file {}", file_path);
|
LOG_INFO(Service_FS, "LayeredFS patched file {}", file_path);
|
||||||
|
|
||||||
file.relocation.type = 2;
|
file.relocation.type = 2;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2020 Citra Emulator Project
|
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -518,7 +518,7 @@ Loader::ResultStatus NCCHContainer::LoadSectionExeFS(const char* name, std::vect
|
||||||
Loader::ResultStatus NCCHContainer::ApplyCodePatch(std::vector<u8>& code) const {
|
Loader::ResultStatus NCCHContainer::ApplyCodePatch(std::vector<u8>& code) const {
|
||||||
struct PatchLocation {
|
struct PatchLocation {
|
||||||
std::string path;
|
std::string path;
|
||||||
bool (*patch_fn)(const std::vector<u8>& patch, std::vector<u8>& code);
|
Loader::ResultStatus (*patch_fn)(const std::vector<u8>& patch, std::vector<u8>& code);
|
||||||
};
|
};
|
||||||
|
|
||||||
const auto mods_path =
|
const auto mods_path =
|
||||||
|
|
@ -555,11 +555,12 @@ Loader::ResultStatus NCCHContainer::ApplyCodePatch(std::vector<u8>& code) const
|
||||||
|
|
||||||
std::vector<u8> patch(patch_file.GetSize());
|
std::vector<u8> patch(patch_file.GetSize());
|
||||||
if (patch_file.ReadBytes(patch.data(), patch.size()) != patch.size())
|
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);
|
LOG_INFO(Service_FS, "File {} patching code.bin", info.path);
|
||||||
if (!info.patch_fn(patch, code))
|
auto patch_result = info.patch_fn(patch, code);
|
||||||
return Loader::ResultStatus::Error;
|
if (patch_result != Loader::ResultStatus::Success)
|
||||||
|
return patch_result;
|
||||||
|
|
||||||
return Loader::ResultStatus::Success;
|
return Loader::ResultStatus::Success;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2019 Citra Emulator Project
|
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
|
@ -14,21 +14,21 @@
|
||||||
|
|
||||||
namespace FileSys::Patch {
|
namespace FileSys::Patch {
|
||||||
|
|
||||||
bool ApplyIpsPatch(const std::vector<u8>& ips, std::vector<u8>& buffer) {
|
Loader::ResultStatus ApplyIpsPatch(const std::vector<u8>& ips, std::vector<u8>& buffer) {
|
||||||
std::size_t cursor = 5;
|
std::size_t cursor = 5;
|
||||||
std::size_t patch_length = ips.size() - 3;
|
std::size_t patch_length = ips.size() - 3;
|
||||||
std::string ips_header(ips.begin(), ips.begin() + 5);
|
std::string ips_header(ips.begin(), ips.begin() + 5);
|
||||||
|
|
||||||
if (ips_header != "PATCH") {
|
if (ips_header != "PATCH") {
|
||||||
LOG_INFO(Service_FS, "Attempted to load invalid IPS");
|
LOG_INFO(Service_FS, "Attempted to load invalid IPS");
|
||||||
return false;
|
return Loader::ResultStatus::ErrorPatches;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (cursor < patch_length) {
|
while (cursor < patch_length) {
|
||||||
std::string eof_check(ips.begin() + cursor, ips.begin() + cursor + 3);
|
std::string eof_check(ips.begin() + cursor, ips.begin() + cursor + 3);
|
||||||
|
|
||||||
if (eof_check == "EOF")
|
if (eof_check == "EOF")
|
||||||
return false;
|
break;
|
||||||
|
|
||||||
std::size_t offset = ips[cursor] << 16 | ips[cursor + 1] << 8 | ips[cursor + 2];
|
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];
|
std::size_t length = ips[cursor + 3] << 8 | ips[cursor + 4];
|
||||||
|
|
@ -38,7 +38,7 @@ bool ApplyIpsPatch(const std::vector<u8>& ips, std::vector<u8>& buffer) {
|
||||||
length = ips[cursor + 5] << 8 | ips[cursor + 6];
|
length = ips[cursor + 5] << 8 | ips[cursor + 6];
|
||||||
|
|
||||||
if (buffer.size() < offset + length)
|
if (buffer.size() < offset + length)
|
||||||
return false;
|
return Loader::ResultStatus::ErrorPatches;
|
||||||
|
|
||||||
for (u32 i = 0; i < length; ++i)
|
for (u32 i = 0; i < length; ++i)
|
||||||
buffer[offset + i] = ips[cursor + 7];
|
buffer[offset + i] = ips[cursor + 7];
|
||||||
|
|
@ -49,12 +49,12 @@ bool ApplyIpsPatch(const std::vector<u8>& ips, std::vector<u8>& buffer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (buffer.size() < offset + length)
|
if (buffer.size() < offset + length)
|
||||||
return false;
|
return Loader::ResultStatus::ErrorPatches;
|
||||||
|
|
||||||
std::memcpy(&buffer[offset], &ips[cursor + 5], length);
|
std::memcpy(&buffer[offset], &ips[cursor + 5], length);
|
||||||
cursor += length + 5;
|
cursor += length + 5;
|
||||||
}
|
}
|
||||||
return true;
|
return Loader::ResultStatus::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Bps {
|
namespace Bps {
|
||||||
|
|
@ -149,11 +149,11 @@ public:
|
||||||
PatchApplier(Stream<const u8> source, Stream<u8> target, Stream<const u8> patch)
|
PatchApplier(Stream<const u8> source, Stream<u8> target, Stream<const u8> patch)
|
||||||
: m_source{source}, m_target{target}, m_patch{patch} {}
|
: m_source{source}, m_target{target}, m_patch{patch} {}
|
||||||
|
|
||||||
bool Apply() {
|
Loader::ResultStatus Apply() {
|
||||||
const auto magic = *m_patch.Read<std::array<char, MagicSize>>();
|
const auto magic = *m_patch.Read<std::array<char, MagicSize>>();
|
||||||
if (std::string_view(magic.data(), magic.size()) != "BPS1") {
|
if (std::string_view(magic.data(), magic.size()) != "BPS1") {
|
||||||
LOG_ERROR(Service_FS, "Invalid BPS magic");
|
LOG_ERROR(Service_FS, "Invalid BPS magic");
|
||||||
return false;
|
return Loader::ResultStatus::ErrorPatches;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Bps::Number source_size = m_patch.ReadNumber();
|
const Bps::Number source_size = m_patch.ReadNumber();
|
||||||
|
|
@ -161,7 +161,7 @@ public:
|
||||||
const Bps::Number metadata_size = m_patch.ReadNumber();
|
const Bps::Number metadata_size = m_patch.ReadNumber();
|
||||||
if (source_size > m_source.size() || target_size > m_target.size() || metadata_size != 0) {
|
if (source_size > m_source.size() || target_size > m_target.size() || metadata_size != 0) {
|
||||||
LOG_ERROR(Service_FS, "Invalid sizes");
|
LOG_ERROR(Service_FS, "Invalid sizes");
|
||||||
return false;
|
return Loader::ResultStatus::ErrorPatches;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::size_t command_start_offset = m_patch.Tell();
|
const std::size_t command_start_offset = m_patch.Tell();
|
||||||
|
|
@ -173,22 +173,22 @@ public:
|
||||||
|
|
||||||
if (crc32(m_source.data(), source_size) != source_crc32) {
|
if (crc32(m_source.data(), source_size) != source_crc32) {
|
||||||
LOG_ERROR(Service_FS, "Unexpected source hash");
|
LOG_ERROR(Service_FS, "Unexpected source hash");
|
||||||
return false;
|
return Loader::ResultStatus::ErrorPatchesInvalidTitle;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process all patch commands.
|
// Process all patch commands.
|
||||||
std::memset(m_target.data(), 0, m_target.size());
|
std::memset(m_target.data(), 0, m_target.size());
|
||||||
while (m_patch.Tell() < command_end_offset) {
|
while (m_patch.Tell() < command_end_offset) {
|
||||||
if (!HandleCommand())
|
if (!HandleCommand())
|
||||||
return false;
|
return Loader::ResultStatus::ErrorPatches;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (crc32(m_target.data(), target_size) != target_crc32) {
|
if (crc32(m_target.data(), target_size) != target_crc32) {
|
||||||
LOG_ERROR(Service_FS, "Unexpected target hash");
|
LOG_ERROR(Service_FS, "Unexpected target hash");
|
||||||
return false;
|
return Loader::ResultStatus::ErrorPatches;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return Loader::ResultStatus::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
@ -257,7 +257,7 @@ private:
|
||||||
|
|
||||||
} // namespace Bps
|
} // namespace Bps
|
||||||
|
|
||||||
bool ApplyBpsPatch(const std::vector<u8>& patch, std::vector<u8>& buffer) {
|
Loader::ResultStatus ApplyBpsPatch(const std::vector<u8>& patch, std::vector<u8>& buffer) {
|
||||||
Bps::Stream patch_stream{patch.data(), patch.size()};
|
Bps::Stream patch_stream{patch.data(), patch.size()};
|
||||||
|
|
||||||
// Move the offset past the file format marker (i.e. "BPS1")
|
// Move the offset past the file format marker (i.e. "BPS1")
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2019 Citra Emulator Project
|
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
|
@ -7,11 +7,12 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
#include "core/loader/loader.h"
|
||||||
|
|
||||||
namespace FileSys::Patch {
|
namespace FileSys::Patch {
|
||||||
|
|
||||||
bool ApplyIpsPatch(const std::vector<u8>& patch, std::vector<u8>& buffer);
|
Loader::ResultStatus ApplyIpsPatch(const std::vector<u8>& patch, std::vector<u8>& buffer);
|
||||||
|
|
||||||
bool ApplyBpsPatch(const std::vector<u8>& patch, std::vector<u8>& buffer);
|
Loader::ResultStatus ApplyBpsPatch(const std::vector<u8>& patch, std::vector<u8>& buffer);
|
||||||
|
|
||||||
} // namespace FileSys::Patch
|
} // namespace FileSys::Patch
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,8 @@ enum class ResultStatus {
|
||||||
ErrorGbaTitle,
|
ErrorGbaTitle,
|
||||||
ErrorArtic,
|
ErrorArtic,
|
||||||
ErrorNotFound,
|
ErrorNotFound,
|
||||||
|
ErrorPatches,
|
||||||
|
ErrorPatchesInvalidTitle,
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr u32 MakeMagic(char a, char b, char c, char d) {
|
constexpr u32 MakeMagic(char a, char b, char c, char d) {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue