diff options
Diffstat (limited to '')
-rw-r--r-- | src/android/app/src/main/jni/native.cpp | 774 |
1 files changed, 774 insertions, 0 deletions
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..b87e04b3d --- /dev/null +++ b/src/android/app/src/main/jni/native.cpp @@ -0,0 +1,774 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <codecvt> +#include <locale> +#include <string> +#include <string_view> +#include <dlfcn.h> + +#ifdef ARCHITECTURE_arm64 +#include <adrenotools/driver.h> +#endif + +#include <android/api-level.h> +#include <android/native_window_jni.h> + +#include "common/detached_tasks.h" +#include "common/dynamic_library.h" +#include "common/fs/path_util.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 "common/string_util.h" +#include "core/core.h" +#include "core/cpu_manager.h" +#include "core/crypto/key_manager.h" +#include "core/file_sys/registered_cache.h" +#include "core/file_sys/vfs_real.h" +#include "core/frontend/applets/cabinet.h" +#include "core/frontend/applets/controller.h" +#include "core/frontend/applets/error.h" +#include "core/frontend/applets/general_frontend.h" +#include "core/frontend/applets/mii_edit.h" +#include "core/frontend/applets/profile_select.h" +#include "core/frontend/applets/software_keyboard.h" +#include "core/frontend/applets/web_browser.h" +#include "core/hid/emulated_controller.h" +#include "core/hid/hid_core.h" +#include "core/hid/hid_types.h" +#include "core/hle/service/acc/profile_manager.h" +#include "core/hle/service/am/applet_ae.h" +#include "core/hle/service/am/applet_oe.h" +#include "core/hle/service/am/applets/applets.h" +#include "core/hle/service/filesystem/filesystem.h" +#include "core/loader/loader.h" +#include "core/perf_stats.h" +#include "jni/android_common/android_common.h" +#include "jni/applets/software_keyboard.h" +#include "jni/config.h" +#include "jni/emu_window/emu_window.h" +#include "jni/id_cache.h" +#include "video_core/rasterizer_interface.h" +#include "video_core/renderer_base.h" + +namespace { + +class EmulationSession final { +public: + EmulationSession() { + m_vfs = std::make_shared<FileSys::RealVfsFilesystem>(); + } + + ~EmulationSession() = default; + + static EmulationSession& GetInstance() { + return s_instance; + } + + const Core::System& System() const { + return m_system; + } + + Core::System& System() { + return m_system; + } + + const EmuWindow_Android& Window() const { + return *m_window; + } + + EmuWindow_Android& Window() { + return *m_window; + } + + ANativeWindow* NativeWindow() const { + return m_native_window; + } + + void SetNativeWindow(ANativeWindow* native_window) { + m_native_window = native_window; + } + + u32 ScreenRotation() const { + return m_screen_rotation; + } + + void SetScreenRotation(u32 screen_rotation) { + m_screen_rotation = screen_rotation; + } + + void InitializeGpuDriver(const std::string& hook_lib_dir, const std::string& custom_driver_dir, + const std::string& custom_driver_name, + const std::string& file_redirect_dir) { +#ifdef ARCHITECTURE_arm64 + void* handle{}; + const char* file_redirect_dir_{}; + int featureFlags{}; + + // Enable driver file redirection when renderer debugging is enabled. + if (Settings::values.renderer_debug && file_redirect_dir.size()) { + featureFlags |= ADRENOTOOLS_DRIVER_FILE_REDIRECT; + file_redirect_dir_ = file_redirect_dir.c_str(); + } + + // Try to load a custom driver. + if (custom_driver_name.size()) { + handle = adrenotools_open_libvulkan( + RTLD_NOW, featureFlags | ADRENOTOOLS_DRIVER_CUSTOM, nullptr, hook_lib_dir.c_str(), + custom_driver_dir.c_str(), custom_driver_name.c_str(), file_redirect_dir_, nullptr); + } + + // Try to load the system driver. + if (!handle) { + handle = + adrenotools_open_libvulkan(RTLD_NOW, featureFlags, nullptr, hook_lib_dir.c_str(), + nullptr, nullptr, file_redirect_dir_, nullptr); + } + + m_vulkan_library = std::make_shared<Common::DynamicLibrary>(handle); +#endif + } + + bool IsRunning() const { + std::scoped_lock lock(m_mutex); + return m_is_running; + } + + const Core::PerfStatsResults& PerfStats() const { + std::scoped_lock m_perf_stats_lock(m_perf_stats_mutex); + return m_perf_stats; + } + + void SurfaceChanged() { + if (!IsRunning()) { + return; + } + m_window->OnSurfaceChanged(m_native_window); + m_system.Renderer().NotifySurfaceChanged(); + } + + Core::SystemResultStatus InitializeEmulation(const std::string& filepath) { + std::scoped_lock lock(m_mutex); + + // Loads the configuration. + Config{}; + + // Create the render window. + m_window = std::make_unique<EmuWindow_Android>(&m_input_subsystem, m_native_window, + m_vulkan_library); + + // Initialize system. + auto android_keyboard = std::make_unique<SoftwareKeyboard::AndroidKeyboard>(); + m_software_keyboard = android_keyboard.get(); + m_system.SetShuttingDown(false); + m_system.ApplySettings(); + m_system.HIDCore().ReloadInputDevices(); + m_system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>()); + m_system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>()); + m_system.SetAppletFrontendSet({ + nullptr, // Amiibo Settings + nullptr, // Controller Selector + nullptr, // Error Display + nullptr, // Mii Editor + nullptr, // Parental Controls + nullptr, // Photo Viewer + nullptr, // Profile Selector + std::move(android_keyboard), // Software Keyboard + nullptr, // Web Browser + }); + m_system.GetFileSystemController().CreateFactories(*m_system.GetFilesystem()); + + // Initialize account manager + m_profile_manager = std::make_unique<Service::Account::ProfileManager>(); + + // Load the ROM. + m_load_result = m_system.Load(EmulationSession::GetInstance().Window(), filepath); + if (m_load_result != Core::SystemResultStatus::Success) { + return m_load_result; + } + + // Complete initialization. + m_system.GPU().Start(); + m_system.GetCpuManager().OnGpuReady(); + m_system.RegisterExitCallback([&] { HaltEmulation(); }); + + return Core::SystemResultStatus::Success; + } + + void ShutdownEmulation() { + std::scoped_lock lock(m_mutex); + + m_is_running = false; + + // Unload user input. + m_system.HIDCore().UnloadInputDevices(); + + // Shutdown the main emulated process + if (m_load_result == Core::SystemResultStatus::Success) { + m_system.DetachDebugger(); + m_system.ShutdownMainProcess(); + m_detached_tasks.WaitForAllTasks(); + m_load_result = Core::SystemResultStatus::ErrorNotInitialized; + } + + // Tear down the render window. + m_window.reset(); + } + + void PauseEmulation() { + std::scoped_lock lock(m_mutex); + m_system.Pause(); + } + + void UnPauseEmulation() { + std::scoped_lock lock(m_mutex); + m_system.Run(); + } + + void HaltEmulation() { + std::scoped_lock lock(m_mutex); + m_is_running = false; + m_cv.notify_one(); + } + + void RunEmulation() { + { + std::scoped_lock lock(m_mutex); + m_is_running = true; + } + + // Load the disk shader cache. + if (Settings::values.use_disk_shader_cache.GetValue()) { + LoadDiskCacheProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); + m_system.Renderer().ReadRasterizer()->LoadDiskResources( + m_system.GetApplicationProcessProgramID(), std::stop_token{}, + LoadDiskCacheProgress); + LoadDiskCacheProgress(VideoCore::LoadCallbackStage::Complete, 0, 0); + } + + void(m_system.Run()); + + if (m_system.DebuggerEnabled()) { + m_system.InitializeDebugger(); + } + + while (true) { + { + std::unique_lock lock(m_mutex); + if (m_cv.wait_for(lock, std::chrono::milliseconds(800), + [&]() { return !m_is_running; })) { + // Emulation halted. + break; + } + } + { + // Refresh performance stats. + std::scoped_lock m_perf_stats_lock(m_perf_stats_mutex); + m_perf_stats = m_system.GetAndResetPerfStats(); + } + } + } + + std::string GetRomTitle(const std::string& path) { + return GetRomMetadata(path).title; + } + + std::vector<u8> GetRomIcon(const std::string& path) { + return GetRomMetadata(path).icon; + } + + void ResetRomMetadata() { + m_rom_metadata_cache.clear(); + } + + bool IsHandheldOnly() { + const auto npad_style_set = m_system.HIDCore().GetSupportedStyleTag(); + + if (npad_style_set.fullkey == 1) { + return false; + } + + if (npad_style_set.handheld == 0) { + return false; + } + + return !Settings::values.use_docked_mode.GetValue(); + } + + void SetDeviceType(int index, int type) { + auto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index); + controller->SetNpadStyleIndex(static_cast<Core::HID::NpadStyleIndex>(type)); + } + + void OnGamepadConnectEvent(int index) { + auto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index); + + // Ensure that player1 is configured correctly and handheld disconnected + if (controller->GetNpadIdType() == Core::HID::NpadIdType::Player1) { + auto handheld = + m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld); + + if (controller->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::Handheld) { + handheld->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController); + controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController); + handheld->Disconnect(); + } + } + + // Ensure that handheld is configured correctly and player 1 disconnected + if (controller->GetNpadIdType() == Core::HID::NpadIdType::Handheld) { + auto player1 = m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); + + if (controller->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::Handheld) { + player1->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld); + controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld); + player1->Disconnect(); + } + } + + if (!controller->IsConnected()) { + controller->Connect(); + } + } + + void OnGamepadDisconnectEvent(int index) { + auto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index); + controller->Disconnect(); + } + + SoftwareKeyboard::AndroidKeyboard* SoftwareKeyboard() { + return m_software_keyboard; + } + +private: + struct RomMetadata { + std::string title; + std::vector<u8> icon; + }; + + RomMetadata GetRomMetadata(const std::string& path) { + if (auto search = m_rom_metadata_cache.find(path); search != m_rom_metadata_cache.end()) { + return search->second; + } + + return CacheRomMetadata(path); + } + + RomMetadata CacheRomMetadata(const std::string& path) { + const auto file = Core::GetGameFileFromPath(m_vfs, path); + const auto loader = Loader::GetLoader(EmulationSession::GetInstance().System(), file, 0, 0); + + RomMetadata entry; + loader->ReadTitle(entry.title); + loader->ReadIcon(entry.icon); + + m_rom_metadata_cache[path] = entry; + + return entry; + } + +private: + static void LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress, int max) { + JNIEnv* env = IDCache::GetEnvForThread(); + env->CallStaticVoidMethod(IDCache::GetDiskCacheProgressClass(), + IDCache::GetDiskCacheLoadProgress(), static_cast<jint>(stage), + static_cast<jint>(progress), static_cast<jint>(max)); + } + +private: + static EmulationSession s_instance; + + // Frontend management + std::unordered_map<std::string, RomMetadata> m_rom_metadata_cache; + + // Window management + std::unique_ptr<EmuWindow_Android> m_window; + ANativeWindow* m_native_window{}; + u32 m_screen_rotation{}; + + // Core emulation + Core::System m_system; + InputCommon::InputSubsystem m_input_subsystem; + Common::DetachedTasks m_detached_tasks; + Core::PerfStatsResults m_perf_stats{}; + std::shared_ptr<FileSys::RealVfsFilesystem> m_vfs; + Core::SystemResultStatus m_load_result{Core::SystemResultStatus::ErrorNotInitialized}; + bool m_is_running{}; + SoftwareKeyboard::AndroidKeyboard* m_software_keyboard{}; + std::unique_ptr<Service::Account::ProfileManager> m_profile_manager; + + // GPU driver parameters + std::shared_ptr<Common::DynamicLibrary> m_vulkan_library; + + // Synchronization + std::condition_variable_any m_cv; + mutable std::mutex m_perf_stats_mutex; + mutable std::mutex m_mutex; +}; + +/*static*/ EmulationSession EmulationSession::s_instance; + +} // Anonymous namespace + +u32 GetAndroidScreenRotation() { + return EmulationSession::GetInstance().ScreenRotation(); +} + +static Core::SystemResultStatus RunEmulation(const std::string& filepath) { + Common::Log::Initialize(); + Common::Log::SetColorConsoleBackendEnabled(true); + Common::Log::Start(); + + MicroProfileOnThreadCreate("EmuThread"); + SCOPE_EXIT({ MicroProfileShutdown(); }); + + LOG_INFO(Frontend, "starting"); + + if (filepath.empty()) { + LOG_CRITICAL(Frontend, "failed to load: filepath empty!"); + return Core::SystemResultStatus::ErrorLoader; + } + + SCOPE_EXIT({ EmulationSession::GetInstance().ShutdownEmulation(); }); + + const auto result = EmulationSession::GetInstance().InitializeEmulation(filepath); + if (result != Core::SystemResultStatus::Success) { + return result; + } + + EmulationSession::GetInstance().RunEmulation(); + + return Core::SystemResultStatus::Success; +} + +extern "C" { + +void Java_org_yuzu_yuzu_1emu_NativeLibrary_surfaceChanged(JNIEnv* env, + [[maybe_unused]] jclass clazz, + jobject surf) { + EmulationSession::GetInstance().SetNativeWindow(ANativeWindow_fromSurface(env, surf)); + EmulationSession::GetInstance().SurfaceChanged(); +} + +void Java_org_yuzu_yuzu_1emu_NativeLibrary_surfaceDestroyed(JNIEnv* env, + [[maybe_unused]] jclass clazz) { + ANativeWindow_release(EmulationSession::GetInstance().NativeWindow()); + EmulationSession::GetInstance().SetNativeWindow(nullptr); + EmulationSession::GetInstance().SurfaceChanged(); +} + +void Java_org_yuzu_yuzu_1emu_NativeLibrary_notifyOrientationChange(JNIEnv* env, + [[maybe_unused]] jclass clazz, + jint layout_option, + jint rotation) { + return EmulationSession::GetInstance().SetScreenRotation(static_cast<u32>(rotation)); +} + +void Java_org_yuzu_yuzu_1emu_NativeLibrary_setAppDirectory(JNIEnv* env, + [[maybe_unused]] jclass clazz, + jstring j_directory) { + Common::FS::SetAppDirectory(GetJString(env, j_directory)); +} + +void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeGpuDriver( + JNIEnv* env, [[maybe_unused]] jclass clazz, jstring hook_lib_dir, jstring custom_driver_dir, + jstring custom_driver_name, jstring file_redirect_dir) { + EmulationSession::GetInstance().InitializeGpuDriver( + GetJString(env, hook_lib_dir), GetJString(env, custom_driver_dir), + GetJString(env, custom_driver_name), GetJString(env, file_redirect_dir)); +} + +jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_reloadKeys(JNIEnv* env, + [[maybe_unused]] jclass clazz) { + Core::Crypto::KeyManager::Instance().ReloadKeys(); + return static_cast<jboolean>(Core::Crypto::KeyManager::Instance().AreKeysLoaded()); +} + +void Java_org_yuzu_yuzu_1emu_NativeLibrary_unPauseEmulation([[maybe_unused]] JNIEnv* env, + [[maybe_unused]] jclass clazz) { + EmulationSession::GetInstance().UnPauseEmulation(); +} + +void Java_org_yuzu_yuzu_1emu_NativeLibrary_pauseEmulation([[maybe_unused]] JNIEnv* env, + [[maybe_unused]] jclass clazz) { + EmulationSession::GetInstance().PauseEmulation(); +} + +void Java_org_yuzu_yuzu_1emu_NativeLibrary_stopEmulation([[maybe_unused]] JNIEnv* env, + [[maybe_unused]] jclass clazz) { + EmulationSession::GetInstance().HaltEmulation(); +} + +void Java_org_yuzu_yuzu_1emu_NativeLibrary_resetRomMetadata([[maybe_unused]] JNIEnv* env, + [[maybe_unused]] jclass clazz) { + EmulationSession::GetInstance().ResetRomMetadata(); +} + +jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isRunning([[maybe_unused]] JNIEnv* env, + [[maybe_unused]] jclass clazz) { + return static_cast<jboolean>(EmulationSession::GetInstance().IsRunning()); +} + +jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isHandheldOnly([[maybe_unused]] JNIEnv* env, + [[maybe_unused]] jclass clazz) { + return EmulationSession::GetInstance().IsHandheldOnly(); +} + +jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_setDeviceType([[maybe_unused]] JNIEnv* env, + [[maybe_unused]] jclass clazz, + jint j_device, jint j_type) { + if (EmulationSession::GetInstance().IsRunning()) { + EmulationSession::GetInstance().SetDeviceType(j_device, j_type); + } + return static_cast<jboolean>(true); +} + +jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadConnectEvent([[maybe_unused]] JNIEnv* env, + [[maybe_unused]] jclass clazz, + jint j_device) { + if (EmulationSession::GetInstance().IsRunning()) { + EmulationSession::GetInstance().OnGamepadConnectEvent(j_device); + } + return static_cast<jboolean>(true); +} + +jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadDisconnectEvent( + [[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jint j_device) { + if (EmulationSession::GetInstance().IsRunning()) { + EmulationSession::GetInstance().OnGamepadDisconnectEvent(j_device); + } + return static_cast<jboolean>(true); +} +jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadButtonEvent([[maybe_unused]] JNIEnv* env, + [[maybe_unused]] jclass clazz, + [[maybe_unused]] jint j_device, + jint j_button, jint action) { + if (EmulationSession::GetInstance().IsRunning()) { + // Ensure gamepad is connected + EmulationSession::GetInstance().OnGamepadConnectEvent(j_device); + EmulationSession::GetInstance().Window().OnGamepadButtonEvent(j_device, j_button, + action != 0); + } + return static_cast<jboolean>(true); +} + +jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadJoystickEvent([[maybe_unused]] JNIEnv* env, + [[maybe_unused]] jclass clazz, + jint j_device, jint stick_id, + jfloat x, jfloat y) { + if (EmulationSession::GetInstance().IsRunning()) { + EmulationSession::GetInstance().Window().OnGamepadJoystickEvent(j_device, stick_id, x, y); + } + return static_cast<jboolean>(true); +} + +jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadMotionEvent( + [[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jint j_device, + jlong delta_timestamp, jfloat gyro_x, jfloat gyro_y, jfloat gyro_z, jfloat accel_x, + jfloat accel_y, jfloat accel_z) { + if (EmulationSession::GetInstance().IsRunning()) { + EmulationSession::GetInstance().Window().OnGamepadMotionEvent( + j_device, delta_timestamp, gyro_x, gyro_y, gyro_z, accel_x, accel_y, accel_z); + } + return static_cast<jboolean>(true); +} + +jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onReadNfcTag([[maybe_unused]] JNIEnv* env, + [[maybe_unused]] jclass clazz, + jbyteArray j_data) { + jboolean isCopy{false}; + std::span<u8> data(reinterpret_cast<u8*>(env->GetByteArrayElements(j_data, &isCopy)), + static_cast<size_t>(env->GetArrayLength(j_data))); + + if (EmulationSession::GetInstance().IsRunning()) { + EmulationSession::GetInstance().Window().OnReadNfcTag(data); + } + return static_cast<jboolean>(true); +} + +jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onRemoveNfcTag([[maybe_unused]] JNIEnv* env, + [[maybe_unused]] jclass clazz) { + if (EmulationSession::GetInstance().IsRunning()) { + EmulationSession::GetInstance().Window().OnRemoveNfcTag(); + } + return static_cast<jboolean>(true); +} + +void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchPressed([[maybe_unused]] JNIEnv* env, + [[maybe_unused]] jclass clazz, jint id, + jfloat x, jfloat y) { + if (EmulationSession::GetInstance().IsRunning()) { + EmulationSession::GetInstance().Window().OnTouchPressed(id, x, y); + } +} + +void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchMoved([[maybe_unused]] JNIEnv* env, + [[maybe_unused]] jclass clazz, jint id, + jfloat x, jfloat y) { + if (EmulationSession::GetInstance().IsRunning()) { + EmulationSession::GetInstance().Window().OnTouchMoved(id, x, y); + } +} + +void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchReleased([[maybe_unused]] JNIEnv* env, + [[maybe_unused]] jclass clazz, jint id) { + if (EmulationSession::GetInstance().IsRunning()) { + EmulationSession::GetInstance().Window().OnTouchReleased(id); + } +} + +jbyteArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getIcon([[maybe_unused]] JNIEnv* env, + [[maybe_unused]] jclass clazz, + [[maybe_unused]] jstring j_filename) { + auto icon_data = EmulationSession::GetInstance().GetRomIcon(GetJString(env, j_filename)); + jbyteArray icon = env->NewByteArray(static_cast<jsize>(icon_data.size())); + env->SetByteArrayRegion(icon, 0, env->GetArrayLength(icon), + reinterpret_cast<jbyte*>(icon_data.data())); + return icon; +} + +jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getTitle([[maybe_unused]] JNIEnv* env, + [[maybe_unused]] jclass clazz, + [[maybe_unused]] jstring j_filename) { + auto title = EmulationSession::GetInstance().GetRomTitle(GetJString(env, j_filename)); + return env->NewStringUTF(title.c_str()); +} + +jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getDescription([[maybe_unused]] JNIEnv* env, + [[maybe_unused]] jclass clazz, + jstring j_filename) { + return j_filename; +} + +jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getGameId([[maybe_unused]] JNIEnv* env, + [[maybe_unused]] jclass clazz, + jstring j_filename) { + return j_filename; +} + +jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getRegions([[maybe_unused]] JNIEnv* env, + [[maybe_unused]] jclass clazz, + [[maybe_unused]] jstring j_filename) { + return env->NewStringUTF(""); +} + +jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getCompany([[maybe_unused]] JNIEnv* env, + [[maybe_unused]] jclass clazz, + [[maybe_unused]] jstring j_filename) { + return env->NewStringUTF(""); +} + +void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeEmulation + [[maybe_unused]] (JNIEnv* env, [[maybe_unused]] jclass clazz) { + // Create the default config.ini. + Config{}; + // Initialize the emulated system. + EmulationSession::GetInstance().System().Initialize(); +} + +jint Java_org_yuzu_yuzu_1emu_NativeLibrary_defaultCPUCore([[maybe_unused]] JNIEnv* env, + [[maybe_unused]] jclass clazz) { + return {}; +} + +void Java_org_yuzu_yuzu_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_yuzu_yuzu_1emu_NativeLibrary_reloadSettings([[maybe_unused]] JNIEnv* env, + [[maybe_unused]] jclass clazz) { + Config{}; +} + +jstring Java_org_yuzu_yuzu_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_yuzu_yuzu_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_yuzu_yuzu_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_yuzu_yuzu_1emu_NativeLibrary_getPerfStats([[maybe_unused]] JNIEnv* env, + [[maybe_unused]] jclass clazz) { + jdoubleArray j_stats = env->NewDoubleArray(4); + + if (EmulationSession::GetInstance().IsRunning()) { + const auto results = EmulationSession::GetInstance().PerfStats(); + + // 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_yuzu_yuzu_1emu_utils_DirectoryInitialization_setSysDirectory( + [[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jstring j_path) {} + +void Java_org_yuzu_yuzu_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); + + const Core::SystemResultStatus result{RunEmulation(path)}; + if (result != Core::SystemResultStatus::Success) { + env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), + IDCache::GetExitEmulationActivity(), static_cast<int>(result)); + } +} + +void Java_org_yuzu_yuzu_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()); +} + +void Java_org_yuzu_yuzu_1emu_NativeLibrary_submitInlineKeyboardText(JNIEnv* env, jclass clazz, + jstring j_text) { + const std::u16string input = Common::UTF8ToUTF16(GetJString(env, j_text)); + EmulationSession::GetInstance().SoftwareKeyboard()->SubmitInlineKeyboardText(input); +} + +void Java_org_yuzu_yuzu_1emu_NativeLibrary_submitInlineKeyboardInput(JNIEnv* env, jclass clazz, + jint j_key_code) { + EmulationSession::GetInstance().SoftwareKeyboard()->SubmitInlineKeyboardInput(j_key_code); +} + +} // extern "C" |