From e6d5dbb58ee894e44b7aa53064de0aec78795fef Mon Sep 17 00:00:00 2001 From: bunnei Date: Sat, 17 Dec 2022 23:27:18 -0800 Subject: [PATCH] android: Minimal JNI for yuzu. --- src/CMakeLists.txt | 5 + src/android/app/src/main/jni/CMakeLists.txt | 15 + .../src/main/jni/emu_window/emu_window.cpp | 58 +++ .../app/src/main/jni/emu_window/emu_window.h | 51 +++ src/android/app/src/main/jni/id_cache.cpp | 36 ++ src/android/app/src/main/jni/id_cache.h | 11 + src/android/app/src/main/jni/native.cpp | 342 ++++++++++++++++++ src/android/app/src/main/jni/native.h | 127 +++++++ 8 files changed, 645 insertions(+) create mode 100644 src/android/app/src/main/jni/CMakeLists.txt create mode 100644 src/android/app/src/main/jni/emu_window/emu_window.cpp create mode 100644 src/android/app/src/main/jni/emu_window/emu_window.h create mode 100644 src/android/app/src/main/jni/id_cache.cpp create mode 100644 src/android/app/src/main/jni/id_cache.h create mode 100644 src/android/app/src/main/jni/native.cpp create mode 100644 src/android/app/src/main/jni/native.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5e3a74c0f..55b113297 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -195,3 +195,8 @@ endif() if (ENABLE_WEB_SERVICE) add_subdirectory(web_service) endif() + +if (ANDROID) + add_subdirectory(android/app/src/main/jni) + target_include_directories(yuzu-android PRIVATE android/app/src/main) +endif() diff --git a/src/android/app/src/main/jni/CMakeLists.txt b/src/android/app/src/main/jni/CMakeLists.txt new file mode 100644 index 000000000..373c0e8bd --- /dev/null +++ b/src/android/app/src/main/jni/CMakeLists.txt @@ -0,0 +1,15 @@ +add_library(yuzu-android SHARED + emu_window/emu_window.cpp + emu_window/emu_window.h + id_cache.cpp + id_cache.h + native.cpp + native.h +) + +set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR}) + +target_link_libraries(yuzu-android PRIVATE audio_core common core input_common) +target_link_libraries(yuzu-android PRIVATE android camera2ndk EGL glad inih jnigraphics log) + +set(CPACK_PACKAGE_EXECUTABLES ${CPACK_PACKAGE_EXECUTABLES} yuzu-android) diff --git a/src/android/app/src/main/jni/emu_window/emu_window.cpp b/src/android/app/src/main/jni/emu_window/emu_window.cpp new file mode 100644 index 000000000..9062c0ae3 --- /dev/null +++ b/src/android/app/src/main/jni/emu_window/emu_window.cpp @@ -0,0 +1,58 @@ +#include + +#include "common/logging/log.h" +#include "input_common/drivers/touch_screen.h" +#include "input_common/drivers/virtual_gamepad.h" +#include "input_common/main.h" +#include "jni/emu_window/emu_window.h" + +void EmuWindow_Android::OnSurfaceChanged(ANativeWindow* surface) { + render_window = surface; +} + +bool EmuWindow_Android::OnTouchEvent(float x, float y, bool pressed) { + if (pressed) { + input_subsystem->GetTouchScreen()->TouchPressed(NormalizeX(x), NormalizeY(y), 0); + return true; + } + + input_subsystem->GetTouchScreen()->ReleaseAllTouch(); + return true; +} + +void EmuWindow_Android::OnTouchMoved(float x, float y) { + input_subsystem->GetTouchScreen()->TouchMoved(NormalizeX(x), NormalizeY(y), 0); +} + +void EmuWindow_Android::OnGamepadEvent(int button_id, bool pressed) { + input_subsystem->GetVirtualGamepad()->SetButtonState(0, button_id, pressed); +} + +void EmuWindow_Android::OnGamepadMoveEvent(float x, float y) { + input_subsystem->GetVirtualGamepad()->SetStickPosition( + 0, InputCommon::VirtualGamepad::VirtualStick::Left, x, y); +} + +EmuWindow_Android::EmuWindow_Android(InputCommon::InputSubsystem* input_subsystem_, + ANativeWindow* surface_) + : input_subsystem{input_subsystem_} { + LOG_INFO(Frontend, "initializing"); + + if (!surface_) { + LOG_CRITICAL(Frontend, "surface is nullptr"); + return; + } + + window_width = ANativeWindow_getWidth(surface_); + window_height = ANativeWindow_getHeight(surface_); + + host_window = surface_; + window_info.type = Core::Frontend::WindowSystemType::Android; + window_info.render_surface = reinterpret_cast(host_window); + + input_subsystem->Initialize(); +} + +EmuWindow_Android::~EmuWindow_Android() { + input_subsystem->Shutdown(); +} diff --git a/src/android/app/src/main/jni/emu_window/emu_window.h b/src/android/app/src/main/jni/emu_window/emu_window.h new file mode 100644 index 000000000..4af51c517 --- /dev/null +++ b/src/android/app/src/main/jni/emu_window/emu_window.h @@ -0,0 +1,51 @@ +#pragma once + +#include "core/frontend/emu_window.h" +#include "input_common/main.h" + +struct ANativeWindow; + +class SharedContext_Android : public Core::Frontend::GraphicsContext { +public: + SharedContext_Android() = default; + ~SharedContext_Android() = default; + void MakeCurrent() override {} + void DoneCurrent() override {} +}; + +class EmuWindow_Android : public Core::Frontend::EmuWindow { +public: + EmuWindow_Android(InputCommon::InputSubsystem* input_subsystem_, ANativeWindow* surface_); + ~EmuWindow_Android(); + + void OnSurfaceChanged(ANativeWindow* surface); + bool OnTouchEvent(float x, float y, bool pressed); + void OnTouchMoved(float x, float y); + void OnGamepadEvent(int button, bool pressed); + void OnGamepadMoveEvent(float x, float y); + void OnFrameDisplayed() override {} + + std::unique_ptr CreateSharedContext() const override { + return {std::make_unique()}; + } + bool IsShown() const override { + return true; + }; + +private: + float NormalizeX(float x) const { + return std::clamp(x / window_width, 0.f, 1.f); + } + + float NormalizeY(float y) const { + return std::clamp(y / window_height, 0.f, 1.f); + } + + InputCommon::InputSubsystem* input_subsystem{}; + + ANativeWindow* render_window{}; + ANativeWindow* host_window{}; + + float window_width{}; + float window_height{}; +}; diff --git a/src/android/app/src/main/jni/id_cache.cpp b/src/android/app/src/main/jni/id_cache.cpp new file mode 100644 index 000000000..2955122be --- /dev/null +++ b/src/android/app/src/main/jni/id_cache.cpp @@ -0,0 +1,36 @@ +#include "jni/id_cache.h" + +static JavaVM* s_java_vm; +static jclass s_native_library_class; +static jmethodID s_exit_emulation_activity; + +namespace IDCache { + +JNIEnv* GetEnvForThread() { + thread_local static struct OwnedEnv { + OwnedEnv() { + status = s_java_vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6); + if (status == JNI_EDETACHED) + s_java_vm->AttachCurrentThread(&env, nullptr); + } + + ~OwnedEnv() { + if (status == JNI_EDETACHED) + s_java_vm->DetachCurrentThread(); + } + + int status; + JNIEnv* env = nullptr; + } owned; + return owned.env; +} + +jclass GetNativeLibraryClass() { + return s_native_library_class; +} + +jmethodID GetExitEmulationActivity() { + return s_exit_emulation_activity; +} + +} // namespace IDCache diff --git a/src/android/app/src/main/jni/id_cache.h b/src/android/app/src/main/jni/id_cache.h new file mode 100644 index 000000000..2fe07169d --- /dev/null +++ b/src/android/app/src/main/jni/id_cache.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +namespace IDCache { + +JNIEnv* GetEnvForThread(); +jclass GetNativeLibraryClass(); +jmethodID GetExitEmulationActivity(); + +} // namespace IDCache diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp new file mode 100644 index 000000000..b343a1453 --- /dev/null +++ b/src/android/app/src/main/jni/native.cpp @@ -0,0 +1,342 @@ +#include +#include +#include +#include + +#include +#include + +#include "common/detached_tasks.h" +#include "common/logging/backend.h" +#include "common/logging/log.h" +#include "common/microprofile.h" +#include "common/scm_rev.h" +#include "common/scope_exit.h" +#include "common/settings.h" +#include "core/core.h" +#include "core/cpu_manager.h" +#include "core/file_sys/registered_cache.h" +#include "core/file_sys/vfs_real.h" +#include "core/hle/service/filesystem/filesystem.h" +#include "core/perf_stats.h" +#include "jni/emu_window/emu_window.h" +#include "jni/id_cache.h" +#include "video_core/rasterizer_interface.h" + +namespace { + +ANativeWindow* s_surf{}; +std::unique_ptr emu_window; +std::atomic stop_run{true}; +Core::System system_; + +std::string UTF16ToUTF8(std::u16string_view input) { + std::wstring_convert, char16_t> convert; + return convert.to_bytes(input.data(), input.data() + input.size()); +} + +std::string GetJString(JNIEnv* env, jstring jstr) { + if (!jstr) { + return {}; + } + + 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 = UTF16ToUTF8(string_view); + env->ReleaseStringChars(jstr, jchars); + + return converted_string; +} + +} // Anonymous namespace + +static Core::SystemResultStatus RunEmulation(const std::string& filepath) { + Common::Log::Initialize(); + Common::Log::SetColorConsoleBackendEnabled(true); + Common::Log::Start(); + Common::DetachedTasks detached_tasks; + + MicroProfileOnThreadCreate("EmuThread"); + SCOPE_EXIT({ MicroProfileShutdown(); }); + + LOG_INFO(Frontend, "starting"); + + if (filepath.empty()) { + LOG_CRITICAL(Frontend, "failed to load: filepath empty!"); + return Core::SystemResultStatus::ErrorLoader; + } + + system_.Initialize(); + system_.ApplySettings(); + + InputCommon::InputSubsystem input_subsystem{}; + + emu_window = std::make_unique(&input_subsystem, s_surf); + + system_.SetContentProvider(std::make_unique()); + system_.SetFilesystem(std::make_shared()); + system_.GetFileSystemController().CreateFactories(*system_.GetFilesystem()); + + const Core::SystemResultStatus load_result{system_.Load(*emu_window, filepath)}; + + if (load_result != Core::SystemResultStatus::Success) { + return load_result; + } + + system_.GPU().Start(); + system_.GetCpuManager().OnGpuReady(); + system_.RegisterExitCallback([&] { exit(0); }); + + void(system_.Run()); + + if (system_.DebuggerEnabled()) { + system_.InitializeDebugger(); + } + + stop_run = false; + while (!stop_run) { + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + + system_.DetachDebugger(); + void(system_.Pause()); + system_.ShutdownMainProcess(); + + detached_tasks.WaitForAllTasks(); + + return Core::SystemResultStatus::Success; +} + +extern "C" { + +void Java_org_citra_citra_1emu_NativeLibrary_SurfaceChanged(JNIEnv* env, + [[maybe_unused]] jclass clazz, + jobject surf) { + s_surf = ANativeWindow_fromSurface(env, surf); + + if (emu_window) { + emu_window->OnSurfaceChanged(s_surf); + } + + LOG_INFO(Frontend, "surface changed"); +} + +void Java_org_citra_citra_1emu_NativeLibrary_SurfaceDestroyed(JNIEnv* env, + [[maybe_unused]] jclass clazz) { + ANativeWindow_release(s_surf); + s_surf = nullptr; + if (emu_window) { + emu_window->OnSurfaceChanged(s_surf); + } +} + +void Java_org_citra_citra_1emu_NativeLibrary_DoFrame(JNIEnv* env, [[maybe_unused]] jclass clazz) {} + +void Java_org_citra_citra_1emu_NativeLibrary_NotifyOrientationChange(JNIEnv* env, + [[maybe_unused]] jclass clazz, + jint layout_option, + jint rotation) {} + +void Java_org_citra_citra_1emu_NativeLibrary_SetUserDirectory( + [[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, + [[maybe_unused]] jstring j_directory) {} + +void Java_org_citra_citra_1emu_NativeLibrary_UnPauseEmulation([[maybe_unused]] JNIEnv* env, + [[maybe_unused]] jclass clazz) {} + +void Java_org_citra_citra_1emu_NativeLibrary_PauseEmulation([[maybe_unused]] JNIEnv* env, + [[maybe_unused]] jclass clazz) {} + +void Java_org_citra_citra_1emu_NativeLibrary_StopEmulation([[maybe_unused]] JNIEnv* env, + [[maybe_unused]] jclass clazz) {} + +jboolean Java_org_citra_citra_1emu_NativeLibrary_IsRunning([[maybe_unused]] JNIEnv* env, + [[maybe_unused]] jclass clazz) { + return static_cast(!stop_run); +} + +jboolean Java_org_citra_citra_1emu_NativeLibrary_onGamePadEvent([[maybe_unused]] JNIEnv* env, + [[maybe_unused]] jclass clazz, + [[maybe_unused]] jstring j_device, + jint j_button, jint action) { + emu_window->OnGamepadEvent(j_button, action != 0); + return static_cast(true); +} + +jboolean Java_org_citra_citra_1emu_NativeLibrary_onGamePadMoveEvent([[maybe_unused]] JNIEnv* env, + [[maybe_unused]] jclass clazz, + jstring j_device, jint axis, + jfloat x, jfloat y) { + // Clamp joystick movement to supported minimum and maximum. + x = std::clamp(x, -1.f, 1.f); + y = std::clamp(-y, -1.f, 1.f); + + // Clamp the input to a circle. + float r = x * x + y * y; + if (r > 1.0f) { + r = std::sqrt(r); + x /= r; + y /= r; + } + emu_window->OnGamepadMoveEvent(x, y); + return static_cast(false); +} + +jboolean Java_org_citra_citra_1emu_NativeLibrary_onGamePadAxisEvent([[maybe_unused]] JNIEnv* env, + [[maybe_unused]] jclass clazz, + jstring j_device, jint axis_id, + jfloat axis_val) { + return {}; +} + +jboolean Java_org_citra_citra_1emu_NativeLibrary_onTouchEvent([[maybe_unused]] JNIEnv* env, + [[maybe_unused]] jclass clazz, + jfloat x, jfloat y, + jboolean pressed) { + return static_cast(emu_window->OnTouchEvent(x, y, pressed)); +} + +void Java_org_citra_citra_1emu_NativeLibrary_onTouchMoved([[maybe_unused]] JNIEnv* env, + [[maybe_unused]] jclass clazz, jfloat x, + jfloat y) { + emu_window->OnTouchMoved(x, y); +} + +jintArray Java_org_citra_citra_1emu_NativeLibrary_GetIcon([[maybe_unused]] JNIEnv* env, + [[maybe_unused]] jclass clazz, + [[maybe_unused]] jstring j_file) { + return {}; +} + +jstring Java_org_citra_citra_1emu_NativeLibrary_GetTitle([[maybe_unused]] JNIEnv* env, + [[maybe_unused]] jclass clazz, + [[maybe_unused]] jstring j_filename) { + return env->NewStringUTF(""); +} + +jstring Java_org_citra_citra_1emu_NativeLibrary_GetDescription([[maybe_unused]] JNIEnv* env, + [[maybe_unused]] jclass clazz, + jstring j_filename) { + return j_filename; +} + +jstring Java_org_citra_citra_1emu_NativeLibrary_GetGameId([[maybe_unused]] JNIEnv* env, + [[maybe_unused]] jclass clazz, + jstring j_filename) { + return j_filename; +} + +jstring Java_org_citra_citra_1emu_NativeLibrary_GetRegions([[maybe_unused]] JNIEnv* env, + [[maybe_unused]] jclass clazz, + [[maybe_unused]] jstring j_filename) { + return env->NewStringUTF(""); +} + +jstring Java_org_citra_citra_1emu_NativeLibrary_GetCompany([[maybe_unused]] JNIEnv* env, + [[maybe_unused]] jclass clazz, + [[maybe_unused]] jstring j_filename) { + return env->NewStringUTF(""); +} + +jstring Java_org_citra_citra_1emu_NativeLibrary_GetGitRevision([[maybe_unused]] JNIEnv* env, + [[maybe_unused]] jclass clazz) { + return {}; +} + +void Java_org_citra_citra_1emu_NativeLibrary_CreateConfigFile + [[maybe_unused]] (JNIEnv* env, [[maybe_unused]] jclass clazz) {} + +jint Java_org_citra_citra_1emu_NativeLibrary_DefaultCPUCore([[maybe_unused]] JNIEnv* env, + [[maybe_unused]] jclass clazz) { + return {}; +} + +void Java_org_citra_citra_1emu_NativeLibrary_Run__Ljava_lang_String_2Ljava_lang_String_2Z( + [[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, [[maybe_unused]] jstring j_file, + [[maybe_unused]] jstring j_savestate, [[maybe_unused]] jboolean j_delete_savestate) {} + +void Java_org_citra_citra_1emu_NativeLibrary_ReloadSettings([[maybe_unused]] JNIEnv* env, + [[maybe_unused]] jclass clazz) {} + +jstring Java_org_citra_citra_1emu_NativeLibrary_GetUserSetting([[maybe_unused]] JNIEnv* env, + [[maybe_unused]] jclass clazz, + jstring j_game_id, jstring j_section, + jstring j_key) { + std::string_view game_id = env->GetStringUTFChars(j_game_id, 0); + std::string_view section = env->GetStringUTFChars(j_section, 0); + std::string_view key = env->GetStringUTFChars(j_key, 0); + + env->ReleaseStringUTFChars(j_game_id, game_id.data()); + env->ReleaseStringUTFChars(j_section, section.data()); + env->ReleaseStringUTFChars(j_key, key.data()); + + return env->NewStringUTF(""); +} + +void Java_org_citra_citra_1emu_NativeLibrary_SetUserSetting([[maybe_unused]] JNIEnv* env, + [[maybe_unused]] jclass clazz, + jstring j_game_id, jstring j_section, + jstring j_key, jstring j_value) { + std::string_view game_id = env->GetStringUTFChars(j_game_id, 0); + std::string_view section = env->GetStringUTFChars(j_section, 0); + std::string_view key = env->GetStringUTFChars(j_key, 0); + std::string_view value = env->GetStringUTFChars(j_value, 0); + + env->ReleaseStringUTFChars(j_game_id, game_id.data()); + env->ReleaseStringUTFChars(j_section, section.data()); + env->ReleaseStringUTFChars(j_key, key.data()); + env->ReleaseStringUTFChars(j_value, value.data()); +} + +void Java_org_citra_citra_1emu_NativeLibrary_InitGameIni([[maybe_unused]] JNIEnv* env, + [[maybe_unused]] jclass clazz, + jstring j_game_id) { + std::string_view game_id = env->GetStringUTFChars(j_game_id, 0); + + env->ReleaseStringUTFChars(j_game_id, game_id.data()); +} + +jdoubleArray Java_org_citra_citra_1emu_NativeLibrary_GetPerfStats([[maybe_unused]] JNIEnv* env, + [[maybe_unused]] jclass clazz) { + jdoubleArray j_stats = env->NewDoubleArray(4); + + if (!stop_run && system_.IsPoweredOn()) { + const auto results = system_.GetAndResetPerfStats(); + + // Converting the structure into an array makes it easier to pass it to the frontend + double stats[4] = {results.system_fps, results.average_game_fps, results.frametime, + results.emulation_speed}; + + env->SetDoubleArrayRegion(j_stats, 0, 4, stats); + } + + return j_stats; +} + +void Java_org_citra_citra_1emu_utils_DirectoryInitialization_SetSysDirectory( + [[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jstring j_path) {} + +void Java_org_citra_citra_1emu_NativeLibrary_Run__Ljava_lang_String_2([[maybe_unused]] JNIEnv* env, + [[maybe_unused]] jclass clazz, + jstring j_path) { + const std::string path = GetJString(env, j_path); + + if (!stop_run) { + stop_run = true; + } + + const Core::SystemResultStatus result{RunEmulation(path)}; + if (result != Core::SystemResultStatus::Success) { + env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), + IDCache::GetExitEmulationActivity(), static_cast(result)); + } +} + +void Java_org_citra_citra_1emu_NativeLibrary_LogDeviceInfo([[maybe_unused]] JNIEnv* env, + [[maybe_unused]] jclass clazz) { + LOG_INFO(Frontend, "yuzu Version: {}-{}", Common::g_scm_branch, Common::g_scm_desc); + LOG_INFO(Frontend, "Host OS: Android API level {}", android_get_device_api_level()); +} + +} // extern "C" diff --git a/src/android/app/src/main/jni/native.h b/src/android/app/src/main/jni/native.h new file mode 100644 index 000000000..16c90e215 --- /dev/null +++ b/src/android/app/src/main/jni/native.h @@ -0,0 +1,127 @@ +#pragma once + +#include + +// Function calls from the Java side +#ifdef __cplusplus +extern "C" { +#endif + +JNIEXPORT void JNICALL Java_org_citra_citra_1emu_NativeLibrary_UnPauseEmulation(JNIEnv* env, + jclass clazz); + +JNIEXPORT void JNICALL Java_org_citra_citra_1emu_NativeLibrary_PauseEmulation(JNIEnv* env, + jclass clazz); + +JNIEXPORT void JNICALL Java_org_citra_citra_1emu_NativeLibrary_StopEmulation(JNIEnv* env, + jclass clazz); + +JNIEXPORT jboolean JNICALL Java_org_citra_citra_1emu_NativeLibrary_IsRunning(JNIEnv* env, + jclass clazz); + +JNIEXPORT jboolean JNICALL Java_org_citra_citra_1emu_NativeLibrary_onGamePadEvent( + JNIEnv* env, jclass clazz, jstring j_device, jint j_button, jint action); + +JNIEXPORT jboolean JNICALL Java_org_citra_citra_1emu_NativeLibrary_onGamePadMoveEvent( + JNIEnv* env, jclass clazz, jstring j_device, jint axis, jfloat x, jfloat y); + +JNIEXPORT jboolean JNICALL Java_org_citra_citra_1emu_NativeLibrary_onGamePadAxisEvent( + JNIEnv* env, jclass clazz, jstring j_device, jint axis_id, jfloat axis_val); + +JNIEXPORT jboolean JNICALL Java_org_citra_citra_1emu_NativeLibrary_onTouchEvent(JNIEnv* env, + jclass clazz, + jfloat x, jfloat y, + jboolean pressed); + +JNIEXPORT void JNICALL Java_org_citra_citra_1emu_NativeLibrary_onTouchMoved(JNIEnv* env, + jclass clazz, jfloat x, + jfloat y); + +JNIEXPORT jintArray JNICALL Java_org_citra_citra_1emu_NativeLibrary_GetIcon(JNIEnv* env, + jclass clazz, + jstring j_file); + +JNIEXPORT jstring JNICALL Java_org_citra_citra_1emu_NativeLibrary_GetTitle(JNIEnv* env, + jclass clazz, + jstring j_filename); + +JNIEXPORT jstring JNICALL Java_org_citra_citra_1emu_NativeLibrary_GetDescription( + JNIEnv* env, jclass clazz, jstring j_filename); + +JNIEXPORT jstring JNICALL Java_org_citra_citra_1emu_NativeLibrary_GetGameId(JNIEnv* env, + jclass clazz, + jstring j_filename); + +JNIEXPORT jstring JNICALL Java_org_citra_citra_1emu_NativeLibrary_GetRegions(JNIEnv* env, + jclass clazz, + jstring j_filename); + +JNIEXPORT jstring JNICALL Java_org_citra_citra_1emu_NativeLibrary_GetCompany(JNIEnv* env, + jclass clazz, + jstring j_filename); + +JNIEXPORT jstring JNICALL Java_org_citra_citra_1emu_NativeLibrary_GetGitRevision(JNIEnv* env, + jclass clazz); + +JNIEXPORT void JNICALL Java_org_citra_citra_1emu_NativeLibrary_SetUserDirectory( + JNIEnv* env, jclass clazz, jstring j_directory); + +JNIEXPORT void JNICALL Java_org_citra_citra_1emu_utils_DirectoryInitialization_SetSysDirectory( + JNIEnv* env, jclass clazz, jstring path_); + +JNIEXPORT void JNICALL Java_org_citra_citra_1emu_NativeLibrary_SetSysDirectory(JNIEnv* env, + jclass clazz, + jstring path); + +JNIEXPORT void JNICALL Java_org_citra_citra_1emu_NativeLibrary_CreateConfigFile(JNIEnv* env, + jclass clazz); + +JNIEXPORT jint JNICALL Java_org_citra_citra_1emu_NativeLibrary_DefaultCPUCore(JNIEnv* env, + jclass clazz); +JNIEXPORT void JNICALL Java_org_citra_citra_1emu_NativeLibrary_SetProfiling(JNIEnv* env, + jclass clazz, + jboolean enable); + +JNIEXPORT void JNICALL Java_org_citra_citra_1emu_NativeLibrary_WriteProfileResults(JNIEnv* env, + jclass clazz); + +JNIEXPORT void JNICALL Java_org_citra_citra_1emu_NativeLibrary_NotifyOrientationChange( + JNIEnv* env, jclass clazz, jint layout_option, jint rotation); + +JNIEXPORT void JNICALL Java_org_citra_citra_1emu_NativeLibrary_Run__Ljava_lang_String_2( + JNIEnv* env, jclass clazz, jstring j_path); + +JNIEXPORT void JNICALL +Java_org_citra_citra_1emu_NativeLibrary_Run__Ljava_lang_String_2Ljava_lang_String_2Z( + JNIEnv* env, jclass clazz, jstring j_file, jstring j_savestate, jboolean j_delete_savestate); + +JNIEXPORT void JNICALL Java_org_citra_citra_1emu_NativeLibrary_SurfaceChanged(JNIEnv* env, + jclass clazz, + jobject surf); + +JNIEXPORT void JNICALL Java_org_citra_citra_1emu_NativeLibrary_SurfaceDestroyed(JNIEnv* env, + jclass clazz); + +JNIEXPORT void JNICALL Java_org_citra_citra_1emu_NativeLibrary_InitGameIni(JNIEnv* env, + jclass clazz, + jstring j_game_id); + +JNIEXPORT void JNICALL Java_org_citra_citra_1emu_NativeLibrary_ReloadSettings(JNIEnv* env, + jclass clazz); + +JNIEXPORT void JNICALL Java_org_citra_citra_1emu_NativeLibrary_SetUserSetting( + JNIEnv* env, jclass clazz, jstring j_game_id, jstring j_section, jstring j_key, + jstring j_value); + +JNIEXPORT jstring JNICALL Java_org_citra_citra_1emu_NativeLibrary_GetUserSetting( + JNIEnv* env, jclass clazz, jstring game_id, jstring section, jstring key); + +JNIEXPORT jdoubleArray JNICALL Java_org_citra_citra_1emu_NativeLibrary_GetPerfStats(JNIEnv* env, + jclass clazz); + +JNIEXPORT void JNICALL Java_org_citra_citra_1emu_NativeLibrary_LogDeviceInfo(JNIEnv* env, + jclass clazz); + +#ifdef __cplusplus +} +#endif