summaryrefslogtreecommitdiffstats
path: root/src/android/app/src/main/jni/native.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/android/app/src/main/jni/native.cpp')
-rw-r--r--src/android/app/src/main/jni/native.cpp342
1 files changed, 342 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..b343a1453
--- /dev/null
+++ b/src/android/app/src/main/jni/native.cpp
@@ -0,0 +1,342 @@
+#include <codecvt>
+#include <locale>
+#include <string>
+#include <string_view>
+
+#include <android/api-level.h>
+#include <android/native_window_jni.h>
+
+#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<EmuWindow_Android> emu_window;
+std::atomic<bool> stop_run{true};
+Core::System system_;
+
+std::string UTF16ToUTF8(std::u16string_view input) {
+ std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, 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<const char16_t*>(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<EmuWindow_Android>(&input_subsystem, s_surf);
+
+ system_.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
+ system_.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>());
+ 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<jboolean>(!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<jboolean>(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<jboolean>(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<jboolean>(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<int>(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"