forked from chinosk/gkms-local
native: Drop C++17 deprecated codecvt and use JNI instead, add modern ICU lib for future android API 31
This commit is contained in:
parent
cbc571cf7a
commit
ca3fb1b05a
|
|
@ -68,4 +68,8 @@ target_link_libraries(${CMAKE_PROJECT_NAME}
|
||||||
log
|
log
|
||||||
fmt)
|
fmt)
|
||||||
|
|
||||||
|
if (ANDROID AND ANDROID_PLATFORM_LEVEL GREATER_EQUAL 31)
|
||||||
|
target_link_libraries(${CMAKE_PROJECT_NAME} icu)
|
||||||
|
endif()
|
||||||
|
|
||||||
target_compile_features(${CMAKE_PROJECT_NAME} PRIVATE cxx_std_23)
|
target_compile_features(${CMAKE_PROJECT_NAME} PRIVATE cxx_std_23)
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
#include "Misc.hpp"
|
#include "Misc.hpp"
|
||||||
|
|
||||||
#include <codecvt>
|
|
||||||
#include <locale>
|
|
||||||
#include "fmt/core.h"
|
#include "fmt/core.h"
|
||||||
|
|
||||||
#ifndef GKMS_WINDOWS
|
#ifndef GKMS_WINDOWS
|
||||||
#include <jni.h>
|
#include <jni.h>
|
||||||
|
#if defined(__ANDROID__) && __ANDROID_API__ >= 31
|
||||||
|
#include <unicode/ustring.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
extern JavaVM* g_javaVM;
|
extern JavaVM* g_javaVM;
|
||||||
#else
|
#else
|
||||||
|
|
@ -32,14 +33,136 @@ namespace GakumasLocal::Misc {
|
||||||
return utility::conversions::utf16_to_utf8(wstr);
|
return utility::conversions::utf16_to_utf8(wstr);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
|
namespace {
|
||||||
|
jclass GetStringClass(JNIEnv* env) {
|
||||||
|
static jclass stringClass = nullptr;
|
||||||
|
if (stringClass) return stringClass;
|
||||||
|
|
||||||
|
jclass localClass = env->FindClass("java/lang/String");
|
||||||
|
if (!localClass) return nullptr;
|
||||||
|
stringClass = reinterpret_cast<jclass>(env->NewGlobalRef(localClass));
|
||||||
|
env->DeleteLocalRef(localClass);
|
||||||
|
return stringClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
jstring GetUtf8CharsetName(JNIEnv* env) {
|
||||||
|
static jstring utf8Charset = nullptr;
|
||||||
|
if (utf8Charset) return utf8Charset;
|
||||||
|
|
||||||
|
jstring localUtf8 = env->NewStringUTF("UTF-8");
|
||||||
|
if (!localUtf8) return nullptr;
|
||||||
|
utf8Charset = reinterpret_cast<jstring>(env->NewGlobalRef(localUtf8));
|
||||||
|
env->DeleteLocalRef(localUtf8);
|
||||||
|
return utf8Charset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::u16string ToUTF16(const std::string_view& str) {
|
std::u16string ToUTF16(const std::string_view& str) {
|
||||||
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> utf16conv;
|
#if defined(__ANDROID__) && __ANDROID_API__ >= 31
|
||||||
return utf16conv.from_bytes(str.data(), str.data() + str.size());
|
UErrorCode status = U_ZERO_ERROR;
|
||||||
|
int32_t outLen = 0;
|
||||||
|
u_strFromUTF8(nullptr, 0, &outLen, str.data(), static_cast<int32_t>(str.size()), &status);
|
||||||
|
if (status != U_BUFFER_OVERFLOW_ERROR && U_FAILURE(status)) return {};
|
||||||
|
|
||||||
|
status = U_ZERO_ERROR;
|
||||||
|
std::u16string out(outLen, u'\0');
|
||||||
|
u_strFromUTF8(
|
||||||
|
reinterpret_cast<UChar*>(out.data()),
|
||||||
|
outLen,
|
||||||
|
&outLen,
|
||||||
|
str.data(),
|
||||||
|
static_cast<int32_t>(str.size()),
|
||||||
|
&status);
|
||||||
|
if (U_FAILURE(status)) return {};
|
||||||
|
out.resize(outLen);
|
||||||
|
return out;
|
||||||
|
#else
|
||||||
|
JNIEnv* env = GetJNIEnv();
|
||||||
|
if (!env) return {};
|
||||||
|
|
||||||
|
jclass stringClass = GetStringClass(env);
|
||||||
|
jstring utf8Charset = GetUtf8CharsetName(env);
|
||||||
|
if (!stringClass || !utf8Charset) return {};
|
||||||
|
|
||||||
|
jmethodID ctor = env->GetMethodID(stringClass, "<init>", "([BLjava/lang/String;)V");
|
||||||
|
if (!ctor) return {};
|
||||||
|
|
||||||
|
jbyteArray bytes = env->NewByteArray(static_cast<jsize>(str.size()));
|
||||||
|
if (!bytes) return {};
|
||||||
|
env->SetByteArrayRegion(bytes, 0, static_cast<jsize>(str.size()), reinterpret_cast<const jbyte*>(str.data()));
|
||||||
|
|
||||||
|
jstring jstr = reinterpret_cast<jstring>(env->NewObject(stringClass, ctor, bytes, utf8Charset));
|
||||||
|
env->DeleteLocalRef(bytes);
|
||||||
|
if (!jstr || env->ExceptionCheck()) {
|
||||||
|
env->ExceptionClear();
|
||||||
|
if (jstr) env->DeleteLocalRef(jstr);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const jsize len = env->GetStringLength(jstr);
|
||||||
|
const jchar* chars = env->GetStringChars(jstr, nullptr);
|
||||||
|
if (!chars) {
|
||||||
|
env->DeleteLocalRef(jstr);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::u16string out(reinterpret_cast<const char16_t*>(chars), reinterpret_cast<const char16_t*>(chars) + len);
|
||||||
|
env->ReleaseStringChars(jstr, chars);
|
||||||
|
env->DeleteLocalRef(jstr);
|
||||||
|
return out;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string ToUTF8(const std::u16string_view& str) {
|
std::string ToUTF8(const std::u16string_view& str) {
|
||||||
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> utf16conv;
|
#if defined(__ANDROID__) && __ANDROID_API__ >= 31
|
||||||
return utf16conv.to_bytes(str.data(), str.data() + str.size());
|
UErrorCode status = U_ZERO_ERROR;
|
||||||
|
int32_t outLen = 0;
|
||||||
|
u_strToUTF8(nullptr, 0, &outLen, reinterpret_cast<const UChar*>(str.data()), static_cast<int32_t>(str.size()), &status);
|
||||||
|
if (status != U_BUFFER_OVERFLOW_ERROR && U_FAILURE(status)) return {};
|
||||||
|
|
||||||
|
status = U_ZERO_ERROR;
|
||||||
|
std::string out(outLen, '\0');
|
||||||
|
u_strToUTF8(
|
||||||
|
out.data(),
|
||||||
|
outLen,
|
||||||
|
&outLen,
|
||||||
|
reinterpret_cast<const UChar*>(str.data()),
|
||||||
|
static_cast<int32_t>(str.size()),
|
||||||
|
&status);
|
||||||
|
if (U_FAILURE(status)) return {};
|
||||||
|
out.resize(outLen);
|
||||||
|
return out;
|
||||||
|
#else
|
||||||
|
JNIEnv* env = GetJNIEnv();
|
||||||
|
if (!env) return {};
|
||||||
|
|
||||||
|
jclass stringClass = GetStringClass(env);
|
||||||
|
jstring utf8Charset = GetUtf8CharsetName(env);
|
||||||
|
if (!stringClass || !utf8Charset) return {};
|
||||||
|
|
||||||
|
jstring jstr = env->NewString(reinterpret_cast<const jchar*>(str.data()), static_cast<jsize>(str.size()));
|
||||||
|
if (!jstr) return {};
|
||||||
|
|
||||||
|
jmethodID getBytes = env->GetMethodID(stringClass, "getBytes", "(Ljava/lang/String;)[B");
|
||||||
|
if (!getBytes) {
|
||||||
|
env->DeleteLocalRef(jstr);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
jbyteArray bytes = reinterpret_cast<jbyteArray>(env->CallObjectMethod(jstr, getBytes, utf8Charset));
|
||||||
|
env->DeleteLocalRef(jstr);
|
||||||
|
if (!bytes || env->ExceptionCheck()) {
|
||||||
|
env->ExceptionClear();
|
||||||
|
if (bytes) env->DeleteLocalRef(bytes);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const jsize len = env->GetArrayLength(bytes);
|
||||||
|
std::string out(static_cast<size_t>(len), '\0');
|
||||||
|
env->GetByteArrayRegion(bytes, 0, len, reinterpret_cast<jbyte*>(out.data()));
|
||||||
|
env->DeleteLocalRef(bytes);
|
||||||
|
return out;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue