diff --git a/src/android/app/src/main/java/org/citra/citra_android/NativeLibrary.java b/src/android/app/src/main/java/org/citra/citra_android/NativeLibrary.java
index 043f935cb..b18b282ed 100644
--- a/src/android/app/src/main/java/org/citra/citra_android/NativeLibrary.java
+++ b/src/android/app/src/main/java/org/citra/citra_android/NativeLibrary.java
@@ -8,11 +8,13 @@ package org.citra.citra_android;
import android.app.AlertDialog;
import android.content.res.Configuration;
-import android.preference.PreferenceManager;
+import android.text.Html;
+import android.text.method.LinkMovementMethod;
import android.view.Surface;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.FrameLayout;
+import android.widget.TextView;
import org.citra.citra_android.activities.EmulationActivity;
import org.citra.citra_android.utils.EmulationMenuSettings;
@@ -444,6 +446,46 @@ public final class NativeLibrary {
return alertPromptButton;
}
+ public static void exitEmulationActivity(int resultCode) {
+ final int Success = 0;
+ final int ErrorNotInitialized = 1;
+ final int ErrorGetLoader = 2;
+ final int ErrorSystemMode = 3;
+ final int ErrorLoader = 4;
+ final int ErrorLoader_ErrorEncrypted = 5;
+ final int ErrorLoader_ErrorInvalidFormat = 6;
+ final int ErrorSystemFiles = 7;
+ final int ErrorVideoCore = 8;
+ final int ErrorVideoCore_ErrorGenericDrivers = 9;
+ final int ErrorVideoCore_ErrorBelowGL33 = 10;
+ final int ShutdownRequested = 11;
+ final int ErrorUnknown = 12;
+
+ final EmulationActivity emulationActivity = sEmulationActivity.get();
+ if (emulationActivity == null) {
+ Log.warning("[NativeLibrary] EmulationActivity is null, can't exit.");
+ return;
+ }
+
+ int captionId = R.string.loader_error_invalid_format;
+ if (resultCode == ErrorLoader_ErrorEncrypted) {
+ captionId = R.string.loader_error_encrypted;
+ }
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(emulationActivity)
+ .setTitle(captionId)
+ .setMessage(Html.fromHtml("Please follow the guides to redump your game cartidges or installed titles."))
+ .setPositiveButton(android.R.string.ok, (dialog, whichButton) -> emulationActivity.exitWithAnimation())
+ .setOnDismissListener(dialogInterface -> emulationActivity.exitWithAnimation());
+ emulationActivity.runOnUiThread(() -> {
+ AlertDialog alert = builder.create();
+ alert.show();
+ ((TextView) alert.findViewById(android.R.id.message)).setMovementMethod(LinkMovementMethod.getInstance());
+ });
+
+ return;
+ }
+
public static void setEmulationActivity(EmulationActivity emulationActivity) {
Log.verbose("[NativeLibrary] Registering EmulationActivity.");
sEmulationActivity = new WeakReference<>(emulationActivity);
diff --git a/src/android/app/src/main/java/org/citra/citra_android/model/GameDatabase.java b/src/android/app/src/main/java/org/citra/citra_android/model/GameDatabase.java
index b249c312e..2e32f87ea 100644
--- a/src/android/app/src/main/java/org/citra/citra_android/model/GameDatabase.java
+++ b/src/android/app/src/main/java/org/citra/citra_android/model/GameDatabase.java
@@ -140,7 +140,7 @@ public final class GameDatabase extends SQLiteOpenHelper {
null); // Order of folders is irrelevant.
Set allowedExtensions = new HashSet(Arrays.asList(
- ".3ds", ".3dsx", ".elf", ".axf", ".cci", ".cxi", ".app"));
+ ".3ds", ".3dsx", ".elf", ".axf", ".cci", ".cxi", ".cia", ".app", ".rar", ".zip", ".7z", ".torrent", ".tar", ".gz"));
// Possibly overly defensive, but ensures that moveToNext() does not skip a row.
folderCursor.moveToPosition(-1);
diff --git a/src/android/app/src/main/jni/id_cache.cpp b/src/android/app/src/main/jni/id_cache.cpp
index 78d04d339..36d48ca27 100644
--- a/src/android/app/src/main/jni/id_cache.cpp
+++ b/src/android/app/src/main/jni/id_cache.cpp
@@ -21,6 +21,7 @@ static jmethodID s_display_alert_prompt;
static jmethodID s_alert_prompt_button;
static jmethodID s_is_portrait_mode;
static jmethodID s_landscape_screen_layout;
+static jmethodID s_exit_emulation_activity;
namespace IDCache {
@@ -67,6 +68,10 @@ jmethodID GetLandscapeScreenLayout() {
return s_landscape_screen_layout;
}
+jmethodID GetExitEmulationActivity() {
+ return s_exit_emulation_activity;
+}
+
} // namespace IDCache
#ifdef __cplusplus
@@ -103,6 +108,8 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
s_is_portrait_mode = env->GetStaticMethodID(s_native_library_class, "isPortraitMode", "()Z");
s_landscape_screen_layout =
env->GetStaticMethodID(s_native_library_class, "landscapeScreenLayout", "()I");
+ s_exit_emulation_activity =
+ env->GetStaticMethodID(s_native_library_class, "exitEmulationActivity", "(I)V");
return JNI_VERSION;
}
diff --git a/src/android/app/src/main/jni/id_cache.h b/src/android/app/src/main/jni/id_cache.h
index 9d6bba545..ac9f24d97 100644
--- a/src/android/app/src/main/jni/id_cache.h
+++ b/src/android/app/src/main/jni/id_cache.h
@@ -15,5 +15,6 @@ jmethodID GetDisplayAlertPrompt();
jmethodID GetAlertPromptButton();
jmethodID GetIsPortraitMode();
jmethodID GetLandscapeScreenLayout();
+jmethodID GetExitEmulationActivity();
} // namespace IDCache
diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp
index b83306753..18b568d25 100644
--- a/src/android/app/src/main/jni/native.cpp
+++ b/src/android/app/src/main/jni/native.cpp
@@ -109,7 +109,7 @@ public:
void ShowError(const std::string& error) override {}
};
-static int RunCitra(const std::string& filepath) {
+static Core::System::ResultStatus RunCitra(const std::string& filepath) {
// Citra core only supports a single running instance
std::lock_guard lock(running_mutex);
@@ -120,7 +120,7 @@ static int RunCitra(const std::string& filepath) {
if (filepath.empty()) {
LOG_CRITICAL(Frontend, "Failed to load ROM: No ROM specified");
- return -1;
+ return Core::System::ResultStatus::ErrorLoader;
}
Core::System& system{Core::System::GetInstance()};
@@ -143,33 +143,8 @@ static int RunCitra(const std::string& filepath) {
SCOPE_EXIT({ window.reset(); });
const Core::System::ResultStatus load_result{system.Load(*window, filepath)};
- switch (load_result) {
- case Core::System::ResultStatus::ErrorGetLoader:
- LOG_CRITICAL(Frontend, "Failed to obtain loader for {}!", filepath);
- return -1;
- case Core::System::ResultStatus::ErrorLoader:
- LOG_CRITICAL(Frontend, "Failed to load ROM!");
- return -1;
- case Core::System::ResultStatus::ErrorLoader_ErrorEncrypted:
- LOG_CRITICAL(Frontend, "The game that you are trying to load must be decrypted before "
- "being used with Citra. \n\n For more information on dumping and "
- "decrypting games, please refer to: "
- "https://citra-emu.org/wiki/dumping-game-cartridges/");
- return -1;
- case Core::System::ResultStatus::ErrorLoader_ErrorInvalidFormat:
- LOG_CRITICAL(Frontend, "Error while loading ROM: The ROM format is not supported.");
- return -1;
- case Core::System::ResultStatus::ErrorNotInitialized:
- LOG_CRITICAL(Frontend, "Core not initialized");
- return -1;
- case Core::System::ResultStatus::ErrorSystemMode:
- LOG_CRITICAL(Frontend, "Failed to determine system mode!");
- return -1;
- case Core::System::ResultStatus::ErrorVideoCore:
- LOG_CRITICAL(Frontend, "VideoCore not initialized");
- return -1;
- case Core::System::ResultStatus::Success:
- break; // Expected case
+ if (load_result != Core::System::ResultStatus::Success) {
+ return load_result;
}
auto& telemetry_session = Core::System::GetInstance().TelemetrySession();
@@ -192,7 +167,7 @@ static int RunCitra(const std::string& filepath) {
}
}
- return {};
+ return Core::System::ResultStatus::Success;
}
void Java_org_citra_citra_1android_NativeLibrary_SurfaceChanged(JNIEnv* env, jobject obj,
@@ -483,5 +458,10 @@ void Java_org_citra_citra_1android_NativeLibrary_Run__Ljava_lang_String_2(JNIEnv
is_running = false;
running_cv.notify_all();
}
- RunCitra(path);
+
+ const Core::System::ResultStatus result{RunCitra(path)};
+ if (result != Core::System::ResultStatus::Success) {
+ env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(),
+ IDCache::GetExitEmulationActivity(), static_cast(result));
+ }
}
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml
index 08eb3f1a1..9934e7048 100644
--- a/src/android/app/src/main/res/values/strings.xml
+++ b/src/android/app/src/main/res/values/strings.xml
@@ -103,6 +103,10 @@
Graphics
Audio
+
+ Your ROM is encrypted
+ Invalid ROM format
+
Take Screenshot
Exit
diff --git a/src/core/core.cpp b/src/core/core.cpp
index d6a284810..464d0f950 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -518,15 +518,17 @@ void System::RegisterImageInterface(std::shared_ptr im
void System::Shutdown(bool is_deserializing) {
// Log last frame performance stats
- const auto perf_results = GetAndResetPerfStats();
- telemetry_session->AddField(Telemetry::FieldType::Performance, "Shutdown_EmulationSpeed",
- perf_results.emulation_speed * 100.0);
- telemetry_session->AddField(Telemetry::FieldType::Performance, "Shutdown_Framerate",
- perf_results.game_fps);
- telemetry_session->AddField(Telemetry::FieldType::Performance, "Shutdown_Frametime",
- perf_results.frametime * 1000.0);
- telemetry_session->AddField(Telemetry::FieldType::Performance, "Mean_Frametime_MS",
- perf_stats->GetMeanFrametime());
+ if (telemetry_session) {
+ const auto perf_results = GetAndResetPerfStats();
+ telemetry_session->AddField(Telemetry::FieldType::Performance, "Shutdown_EmulationSpeed",
+ perf_results.emulation_speed * 100.0);
+ telemetry_session->AddField(Telemetry::FieldType::Performance, "Shutdown_Framerate",
+ perf_results.game_fps);
+ telemetry_session->AddField(Telemetry::FieldType::Performance, "Shutdown_Frametime",
+ perf_results.frametime * 1000.0);
+ telemetry_session->AddField(Telemetry::FieldType::Performance, "Mean_Frametime_MS",
+ perf_stats->GetMeanFrametime());
+ }
// Shutdown emulation session
VideoCore::Shutdown();
@@ -546,7 +548,7 @@ void System::Shutdown(bool is_deserializing) {
cpu_cores.clear();
timing.reset();
- if (video_dumper->IsDumping()) {
+ if (video_dumper && video_dumper->IsDumping()) {
video_dumper->StopDumping();
}