From e65ca8b90764402bd51c8108835e6ce1a3a9dbcd Mon Sep 17 00:00:00 2001 From: JosJuice Date: Wed, 3 Aug 2022 15:11:13 +0200 Subject: [PATCH 1/3] android: Deduplicate GetJString --- src/android/app/src/main/jni/CMakeLists.txt | 2 ++ .../jni/android_common/android_common.cpp | 20 +++++++++++++++++++ .../main/jni/android_common/android_common.h | 11 ++++++++++ .../app/src/main/jni/applets/swkbd.cpp | 14 +++---------- src/android/app/src/main/jni/native.cpp | 12 +---------- 5 files changed, 37 insertions(+), 22 deletions(-) create mode 100644 src/android/app/src/main/jni/android_common/android_common.cpp create mode 100644 src/android/app/src/main/jni/android_common/android_common.h diff --git a/src/android/app/src/main/jni/CMakeLists.txt b/src/android/app/src/main/jni/CMakeLists.txt index 77b95130d..443b39d57 100644 --- a/src/android/app/src/main/jni/CMakeLists.txt +++ b/src/android/app/src/main/jni/CMakeLists.txt @@ -1,4 +1,6 @@ add_library(citra-android SHARED + android_common/android_common.cpp + android_common/android_common.h applets/mii_selector.cpp applets/mii_selector.h applets/swkbd.cpp diff --git a/src/android/app/src/main/jni/android_common/android_common.cpp b/src/android/app/src/main/jni/android_common/android_common.cpp new file mode 100644 index 000000000..b00d9a171 --- /dev/null +++ b/src/android/app/src/main/jni/android_common/android_common.cpp @@ -0,0 +1,20 @@ +// Copyright 2022 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "jni/android_common/android_common.h" + +#include + +#include + +std::string GetJString(JNIEnv *env, jstring jstr) { + if (!jstr) { + return {}; + } + + const char *s = env->GetStringUTFChars(jstr, nullptr); + std::string result = s; + env->ReleaseStringUTFChars(jstr, s); + return result; +} diff --git a/src/android/app/src/main/jni/android_common/android_common.h b/src/android/app/src/main/jni/android_common/android_common.h new file mode 100644 index 000000000..50b4c8d22 --- /dev/null +++ b/src/android/app/src/main/jni/android_common/android_common.h @@ -0,0 +1,11 @@ +// Copyright 2022 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include + +#include + +std::string GetJString(JNIEnv* env, jstring jstr); diff --git a/src/android/app/src/main/jni/applets/swkbd.cpp b/src/android/app/src/main/jni/applets/swkbd.cpp index 062d307a6..f5babae9d 100644 --- a/src/android/app/src/main/jni/applets/swkbd.cpp +++ b/src/android/app/src/main/jni/applets/swkbd.cpp @@ -3,22 +3,14 @@ // Refer to the license.txt file included. #include + #include + #include "core/core.h" +#include "jni/android_common/android_common.h" #include "jni/applets/swkbd.h" #include "jni/id_cache.h" -static std::string GetJString(JNIEnv* env, jstring jstr) { - if (!jstr) { - return {}; - } - - const char* s = env->GetStringUTFChars(jstr, nullptr); - std::string result = s; - env->ReleaseStringUTFChars(jstr, s); - return result; -} - static jclass s_software_keyboard_class; static jclass s_keyboard_config_class; static jclass s_keyboard_data_class; diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index 6dffc1225..ecae76fe2 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp @@ -27,6 +27,7 @@ #include "core/hle/service/nfc/nfc.h" #include "core/savestate.h" #include "core/settings.h" +#include "jni/android_common/android_common.h" #include "jni/applets/mii_selector.h" #include "jni/applets/swkbd.h" #include "jni/camera/ndk_camera.h" @@ -58,17 +59,6 @@ std::condition_variable running_cv; } // Anonymous namespace -static std::string GetJString(JNIEnv* env, jstring jstr) { - if (!jstr) { - return {}; - } - - const char* s = env->GetStringUTFChars(jstr, nullptr); - std::string result = s; - env->ReleaseStringUTFChars(jstr, s); - return result; -} - static bool DisplayAlertMessage(const char* caption, const char* text, bool yes_no) { JNIEnv* env = IDCache::GetEnvForThread(); From 87677be92187b457f94f2d304d378834bfcd6be6 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Wed, 3 Aug 2022 15:36:36 +0200 Subject: [PATCH 2/3] android: Create utility function for converting strings to Java --- .../app/src/main/jni/android_common/android_common.cpp | 4 ++++ .../app/src/main/jni/android_common/android_common.h | 1 + src/android/app/src/main/jni/applets/mii_selector.cpp | 5 +++-- src/android/app/src/main/jni/applets/swkbd.cpp | 9 ++++----- src/android/app/src/main/jni/native.cpp | 8 ++++---- 5 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/android/app/src/main/jni/android_common/android_common.cpp b/src/android/app/src/main/jni/android_common/android_common.cpp index b00d9a171..0fa028944 100644 --- a/src/android/app/src/main/jni/android_common/android_common.cpp +++ b/src/android/app/src/main/jni/android_common/android_common.cpp @@ -18,3 +18,7 @@ std::string GetJString(JNIEnv *env, jstring jstr) { env->ReleaseStringUTFChars(jstr, s); return result; } + +jstring ToJString(JNIEnv* env, const std::string& str) { + return env->NewStringUTF(str.c_str()); +} diff --git a/src/android/app/src/main/jni/android_common/android_common.h b/src/android/app/src/main/jni/android_common/android_common.h index 50b4c8d22..ff55bee75 100644 --- a/src/android/app/src/main/jni/android_common/android_common.h +++ b/src/android/app/src/main/jni/android_common/android_common.h @@ -9,3 +9,4 @@ #include std::string GetJString(JNIEnv* env, jstring jstr); +jstring ToJString(JNIEnv* env, const std::string& str); diff --git a/src/android/app/src/main/jni/applets/mii_selector.cpp b/src/android/app/src/main/jni/applets/mii_selector.cpp index 0e8e79238..2009a84ec 100644 --- a/src/android/app/src/main/jni/applets/mii_selector.cpp +++ b/src/android/app/src/main/jni/applets/mii_selector.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include "common/string_util.h" +#include "jni/android_common/android_common.h" #include "jni/applets/mii_selector.h" #include "jni/id_cache.h" @@ -26,7 +27,7 @@ void AndroidMiiSelector::Setup(const Frontend::MiiSelectorConfig& config) { static_cast(config.enable_cancel_button)); env->SetObjectField(java_config, env->GetFieldID(s_mii_selector_config_class, "title", "Ljava/lang/String;"), - env->NewStringUTF(config.title.c_str())); + ToJString(env, config.title)); env->SetLongField( java_config, env->GetFieldID(s_mii_selector_config_class, "initially_selected_mii_index", "J"), @@ -39,7 +40,7 @@ void AndroidMiiSelector::Setup(const Frontend::MiiSelectorConfig& config) { env->NewObjectArray(static_cast(miis.size()), string_class, nullptr); for (std::size_t i = 0; i < miis.size(); ++i) { const auto name = Common::UTF16BufferToUTF8(miis[i].mii_name); - env->SetObjectArrayElement(array, static_cast(i), env->NewStringUTF(name.c_str())); + env->SetObjectArrayElement(array, static_cast(i), ToJString(env, name)); } env->SetObjectField( java_config, diff --git a/src/android/app/src/main/jni/applets/swkbd.cpp b/src/android/app/src/main/jni/applets/swkbd.cpp index f5babae9d..e373c9e27 100644 --- a/src/android/app/src/main/jni/applets/swkbd.cpp +++ b/src/android/app/src/main/jni/applets/swkbd.cpp @@ -31,15 +31,15 @@ static jobject ToJavaKeyboardConfig(const Frontend::KeyboardConfig& config) { static_cast(config.multiline_mode)); env->SetObjectField(object, env->GetFieldID(s_keyboard_config_class, "hint_text", "Ljava/lang/String;"), - env->NewStringUTF(config.hint_text.c_str())); + ToJString(env, config.hint_text)); const jclass string_class = reinterpret_cast(env->FindClass("java/lang/String")); const jobjectArray array = env->NewObjectArray(static_cast(config.button_text.size()), string_class, - env->NewStringUTF(config.button_text[0].c_str())); + ToJString(env, config.button_text[0])); for (std::size_t i = 1; i < config.button_text.size(); ++i) { env->SetObjectArrayElement(array, static_cast(i), - env->NewStringUTF(config.button_text[i].c_str())); + ToJString(env, config.button_text[i])); } env->SetObjectField( object, env->GetFieldID(s_keyboard_config_class, "button_text", "[Ljava/lang/String;"), @@ -70,8 +70,7 @@ void AndroidKeyboard::Execute(const Frontend::KeyboardConfig& config) { void AndroidKeyboard::ShowError(const std::string& error) { JNIEnv* env = IDCache::GetEnvForThread(); - env->CallStaticVoidMethod(s_software_keyboard_class, s_swkbd_show_error, - env->NewStringUTF(error.c_str())); + env->CallStaticVoidMethod(s_software_keyboard_class, s_swkbd_show_error, ToJString(env, error)); } void InitJNI(JNIEnv* env) { diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index ecae76fe2..e876ecb3e 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp @@ -64,8 +64,8 @@ static bool DisplayAlertMessage(const char* caption, const char* text, bool yes_ // Execute the Java method. jboolean result = env->CallStaticBooleanMethod( - IDCache::GetNativeLibraryClass(), IDCache::GetDisplayAlertMsg(), env->NewStringUTF(caption), - env->NewStringUTF(text), yes_no ? JNI_TRUE : JNI_FALSE); + IDCache::GetNativeLibraryClass(), IDCache::GetDisplayAlertMsg(), ToJString(env, caption), + ToJString(env, text), yes_no ? JNI_TRUE : JNI_FALSE); return result != JNI_FALSE; } @@ -74,8 +74,8 @@ static std::string DisplayAlertPrompt(const char* caption, const char* text, int JNIEnv* env = IDCache::GetEnvForThread(); jstring value = reinterpret_cast(env->CallStaticObjectMethod( - IDCache::GetNativeLibraryClass(), IDCache::GetDisplayAlertPrompt(), - env->NewStringUTF(caption), env->NewStringUTF(text), buttonConfig)); + IDCache::GetNativeLibraryClass(), IDCache::GetDisplayAlertPrompt(), ToJString(env, caption), + ToJString(env, text), buttonConfig)); return GetJString(env, value); } From ce07ef182134283178d220901e14d3de30e053b8 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Wed, 3 Aug 2022 15:44:51 +0200 Subject: [PATCH 3/3] android: Use correct encoding when converting strings The JNI functions that have "UTF" their name use "modified UTF-8" rather than the standard UTF-8 that Citra uses, at least according to Oracle's documentation, so it is incorrect for us to use them. This change fixes the problem by converting between UTF-8 and UTF-16 manually instead of letting JNI do it for us. --- .../jni/android_common/android_common.cpp | 22 +++++++++++++------ .../main/jni/android_common/android_common.h | 2 +- src/common/string_util.cpp | 10 +++++---- src/common/string_util.h | 5 +++-- 4 files changed, 25 insertions(+), 14 deletions(-) diff --git a/src/android/app/src/main/jni/android_common/android_common.cpp b/src/android/app/src/main/jni/android_common/android_common.cpp index 0fa028944..dedd33151 100644 --- a/src/android/app/src/main/jni/android_common/android_common.cpp +++ b/src/android/app/src/main/jni/android_common/android_common.cpp @@ -5,20 +5,28 @@ #include "jni/android_common/android_common.h" #include +#include #include -std::string GetJString(JNIEnv *env, jstring jstr) { +#include "common/string_util.h" + +std::string GetJString(JNIEnv* env, jstring jstr) { if (!jstr) { return {}; } - const char *s = env->GetStringUTFChars(jstr, nullptr); - std::string result = s; - env->ReleaseStringUTFChars(jstr, s); - return result; + const jchar* jchars = env->GetStringChars(jstr, nullptr); + const jsize length = env->GetStringLength(jstr); + const std::u16string_view string_view(reinterpret_cast(jchars), length); + const std::string converted_string = Common::UTF16ToUTF8(string_view); + env->ReleaseStringChars(jstr, jchars); + + return converted_string; } -jstring ToJString(JNIEnv* env, const std::string& str) { - return env->NewStringUTF(str.c_str()); +jstring ToJString(JNIEnv* env, std::string_view str) { + const std::u16string converted_string = Common::UTF8ToUTF16(str); + return env->NewString(reinterpret_cast(converted_string.data()), + static_cast(converted_string.size())); } diff --git a/src/android/app/src/main/jni/android_common/android_common.h b/src/android/app/src/main/jni/android_common/android_common.h index ff55bee75..a09f7bc45 100644 --- a/src/android/app/src/main/jni/android_common/android_common.h +++ b/src/android/app/src/main/jni/android_common/android_common.h @@ -9,4 +9,4 @@ #include std::string GetJString(JNIEnv* env, jstring jstr); -jstring ToJString(JNIEnv* env, const std::string& str); +jstring ToJString(JNIEnv* env, std::string_view str); diff --git a/src/common/string_util.cpp b/src/common/string_util.cpp index 395cf1d38..47aaddddb 100644 --- a/src/common/string_util.cpp +++ b/src/common/string_util.cpp @@ -8,6 +8,8 @@ #include #include #include +#include +#include #include "common/common_paths.h" #include "common/logging/log.h" #include "common/string_util.h" @@ -135,14 +137,14 @@ std::string ReplaceAll(std::string result, const std::string& src, const std::st return result; } -std::string UTF16ToUTF8(const std::u16string& input) { +std::string UTF16ToUTF8(std::u16string_view input) { std::wstring_convert, char16_t> convert; - return convert.to_bytes(input); + return convert.to_bytes(input.data(), input.data() + input.size()); } -std::u16string UTF8ToUTF16(const std::string& input) { +std::u16string UTF8ToUTF16(std::string_view input) { std::wstring_convert, char16_t> convert; - return convert.from_bytes(input); + return convert.from_bytes(input.data(), input.data() + input.size()); } #ifdef _WIN32 diff --git a/src/common/string_util.h b/src/common/string_util.h index 2c454bb7b..d9edbf803 100644 --- a/src/common/string_util.h +++ b/src/common/string_util.h @@ -7,6 +7,7 @@ #include #include #include +#include #include #include "common/common_types.h" #include "common/swap.h" @@ -37,8 +38,8 @@ void BuildCompleteFilename(std::string& _CompleteFilename, const std::string& _P [[nodiscard]] std::string ReplaceAll(std::string result, const std::string& src, const std::string& dest); -[[nodiscard]] std::string UTF16ToUTF8(const std::u16string& input); -[[nodiscard]] std::u16string UTF8ToUTF16(const std::string& input); +[[nodiscard]] std::string UTF16ToUTF8(std::u16string_view input); +[[nodiscard]] std::u16string UTF8ToUTF16(std::string_view input); #ifdef _WIN32 [[nodiscard]] std::string UTF16ToUTF8(const std::wstring& input);