From ab4c0939765887a30d2c76053595712210361cce Mon Sep 17 00:00:00 2001 From: echosys Date: Thu, 3 Oct 2024 12:08:28 +0000 Subject: [PATCH] Fix Android crash caused by incorrect type in progress dialog callbacks (#58) Bug discovered via an incomplete fix in Sudachi. Some Progress Dialog callbacks pass the wrong type (Double instead of Long) from C++ to Java code causing a crash at runtime. To fix this a new function is implemented to convert to a Java Long and that is used instead of the function that converts to a Double. Reviewed-on: http://vub63vv26q6v27xzv2dtcd25xumubshogm67yrpaz2rculqxs7jlfqad.onion/torzu-emu/torzu/pulls/58 Co-authored-by: echosys Co-committed-by: echosys --- src/android/app/src/main/jni/native.cpp | 12 ++++++------ src/common/android/android_common.cpp | 8 ++++++++ src/common/android/android_common.h | 3 +++ src/common/android/id_cache.cpp | 22 ++++++++++++++++++++++ src/common/android/id_cache.h | 4 ++++ 5 files changed, 43 insertions(+), 6 deletions(-) diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index ebf38b34ff..849d8367cd 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp @@ -459,8 +459,8 @@ int Java_org_yuzu_yuzu_1emu_NativeLibrary_installFileToNand(JNIEnv* env, jobject jlambdaClass, "invoke", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); const auto callback = [env, jcallback, jlambdaInvokeMethod](size_t max, size_t progress) { auto jwasCancelled = env->CallObjectMethod(jcallback, jlambdaInvokeMethod, - Common::Android::ToJDouble(env, max), - Common::Android::ToJDouble(env, progress)); + Common::Android::ToJLong(env, max), + Common::Android::ToJLong(env, progress)); return Common::Android::GetJBoolean(env, jwasCancelled); }; @@ -791,8 +791,8 @@ jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_verifyInstalledContents(JNIEn jlambdaClass, "invoke", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); const auto callback = [env, jcallback, jlambdaInvokeMethod](size_t max, size_t progress) { auto jwasCancelled = env->CallObjectMethod(jcallback, jlambdaInvokeMethod, - Common::Android::ToJDouble(env, max), - Common::Android::ToJDouble(env, progress)); + Common::Android::ToJLong(env, max), + Common::Android::ToJLong(env, progress)); return Common::Android::GetJBoolean(env, jwasCancelled); }; @@ -814,8 +814,8 @@ jint Java_org_yuzu_yuzu_1emu_NativeLibrary_verifyGameContents(JNIEnv* env, jobje jlambdaClass, "invoke", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); const auto callback = [env, jcallback, jlambdaInvokeMethod](size_t max, size_t progress) { auto jwasCancelled = env->CallObjectMethod(jcallback, jlambdaInvokeMethod, - Common::Android::ToJDouble(env, max), - Common::Android::ToJDouble(env, progress)); + Common::Android::ToJLong(env, max), + Common::Android::ToJLong(env, progress)); return Common::Android::GetJBoolean(env, jwasCancelled); }; auto& session = EmulationSession::GetInstance(); diff --git a/src/common/android/android_common.cpp b/src/common/android/android_common.cpp index e79005658d..8913413c01 100644 --- a/src/common/android/android_common.cpp +++ b/src/common/android/android_common.cpp @@ -54,6 +54,14 @@ jobject ToJInteger(JNIEnv* env, s32 value) { return env->NewObject(GetIntegerClass(), GetIntegerConstructor(), value); } +s64 GetJLong(JNIEnv* env, jobject jlong) { + return env->GetLongField(jlong, GetIntegerValueField()); +} + +jobject ToJLong(JNIEnv* env, s64 value) { + return env->NewObject(GetLongClass(), GetLongConstructor(), value); +} + bool GetJBoolean(JNIEnv* env, jobject jboolean) { return env->GetBooleanField(jboolean, GetBooleanValueField()); } diff --git a/src/common/android/android_common.h b/src/common/android/android_common.h index d0ccb4ec2f..67e9b9e138 100644 --- a/src/common/android/android_common.h +++ b/src/common/android/android_common.h @@ -20,6 +20,9 @@ jobject ToJDouble(JNIEnv* env, double value); s32 GetJInteger(JNIEnv* env, jobject jinteger); jobject ToJInteger(JNIEnv* env, s32 value); +s64 GetJLong(JNIEnv* env, jobject jlong); +jobject ToJLong(JNIEnv* env, s64 value); + bool GetJBoolean(JNIEnv* env, jobject jboolean); jobject ToJBoolean(JNIEnv* env, bool value); diff --git a/src/common/android/id_cache.cpp b/src/common/android/id_cache.cpp index 1145cbdf28..94bb5fab63 100644 --- a/src/common/android/id_cache.cpp +++ b/src/common/android/id_cache.cpp @@ -61,6 +61,10 @@ static jclass s_integer_class; static jmethodID s_integer_constructor; static jfieldID s_integer_value_field; +static jclass s_long_class; +static jmethodID s_long_constructor; +static jfieldID s_long_value_field; + static jclass s_boolean_class; static jmethodID s_boolean_constructor; static jfieldID s_boolean_value_field; @@ -288,6 +292,18 @@ jfieldID GetIntegerValueField() { return s_integer_value_field; } +jclass GetLongClass() { + return s_long_class; +} + +jmethodID GetLongConstructor() { + return s_long_constructor; +} + +jfieldID GetLongValueField() { + return s_long_value_field; +} + jclass GetBooleanClass() { return s_boolean_class; } @@ -493,6 +509,12 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) { s_integer_value_field = env->GetFieldID(int_class, "value", "I"); env->DeleteLocalRef(int_class); + const jclass long_class = env->FindClass("java/lang/Long"); + s_long_class = reinterpret_cast(env->NewGlobalRef(long_class)); + s_long_constructor = env->GetMethodID(long_class, "", "(J)V"); + s_long_value_field = env->GetFieldID(long_class, "value", "J"); + env->DeleteLocalRef(long_class); + const jclass boolean_class = env->FindClass("java/lang/Boolean"); s_boolean_class = reinterpret_cast(env->NewGlobalRef(boolean_class)); s_boolean_constructor = env->GetMethodID(boolean_class, "", "(Z)V"); diff --git a/src/common/android/id_cache.h b/src/common/android/id_cache.h index cd2844dcc6..b4da9b77a5 100644 --- a/src/common/android/id_cache.h +++ b/src/common/android/id_cache.h @@ -81,6 +81,10 @@ jclass GetIntegerClass(); jmethodID GetIntegerConstructor(); jfieldID GetIntegerValueField(); +jclass GetLongClass(); +jmethodID GetLongConstructor(); +jfieldID GetLongValueField(); + jclass GetBooleanClass(); jmethodID GetBooleanConstructor(); jfieldID GetBooleanValueField();