summaryrefslogtreecommitdiffstats
path: root/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt
diff options
context:
space:
mode:
Diffstat (limited to 'src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt')
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt463
1 files changed, 463 insertions, 0 deletions
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt
new file mode 100644
index 000000000..725917ace
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt
@@ -0,0 +1,463 @@
+// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu
+
+import android.app.Dialog
+import android.content.DialogInterface
+import android.os.Bundle
+import android.text.Html
+import android.text.method.LinkMovementMethod
+import android.view.Surface
+import android.view.View
+import android.widget.TextView
+import androidx.fragment.app.DialogFragment
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import org.yuzu.yuzu_emu.YuzuApplication.Companion.appContext
+import org.yuzu.yuzu_emu.activities.EmulationActivity
+import org.yuzu.yuzu_emu.utils.DocumentsTree.Companion.isNativePath
+import org.yuzu.yuzu_emu.utils.FileUtil.getFileSize
+import org.yuzu.yuzu_emu.utils.FileUtil.openContentUri
+import org.yuzu.yuzu_emu.utils.Log.error
+import org.yuzu.yuzu_emu.utils.Log.verbose
+import org.yuzu.yuzu_emu.utils.Log.warning
+import org.yuzu.yuzu_emu.utils.SerializableHelper.serializable
+import java.lang.ref.WeakReference
+
+/**
+ * Class which contains methods that interact
+ * with the native side of the Yuzu code.
+ */
+object NativeLibrary {
+ /**
+ * Default controller id for each device
+ */
+ const val Player1Device = 0
+ const val Player2Device = 1
+ const val Player3Device = 2
+ const val Player4Device = 3
+ const val Player5Device = 4
+ const val Player6Device = 5
+ const val Player7Device = 6
+ const val Player8Device = 7
+ const val ConsoleDevice = 8
+
+ var sEmulationActivity = WeakReference<EmulationActivity?>(null)
+
+ init {
+ try {
+ System.loadLibrary("yuzu-android")
+ } catch (ex: UnsatisfiedLinkError) {
+ error("[NativeLibrary] $ex")
+ }
+ }
+
+ @JvmStatic
+ fun openContentUri(path: String?, openmode: String?): Int {
+ return if (isNativePath(path!!)) {
+ YuzuApplication.documentsTree!!.openContentUri(path, openmode)
+ } else openContentUri(appContext, path, openmode)
+ }
+
+ @JvmStatic
+ fun getSize(path: String?): Long {
+ return if (isNativePath(path!!)) {
+ YuzuApplication.documentsTree!!.getFileSize(path)
+ } else getFileSize(appContext, path)
+ }
+
+ /**
+ * Handles button press events for a gamepad.
+ *
+ * @param Device The input descriptor of the gamepad.
+ * @param Button Key code identifying which button was pressed.
+ * @param Action Mask identifying which action is happening (button pressed down, or button released).
+ * @return If we handled the button press.
+ */
+ external fun onGamePadButtonEvent(Device: Int, Button: Int, Action: Int): Boolean
+
+ /**
+ * Handles joystick movement events.
+ *
+ * @param Device The device ID of the gamepad.
+ * @param Axis The axis ID
+ * @param x_axis The value of the x-axis represented by the given ID.
+ * @param y_axis The value of the y-axis represented by the given ID.
+ */
+ external fun onGamePadJoystickEvent(
+ Device: Int,
+ Axis: Int,
+ x_axis: Float,
+ y_axis: Float
+ ): Boolean
+
+ /**
+ * Handles motion events.
+ *
+ * @param delta_timestamp The finger id corresponding to this event
+ * @param gyro_x,gyro_y,gyro_z The value of the accelerometer sensor.
+ * @param accel_x,accel_y,accel_z The value of the y-axis
+ */
+ external fun onGamePadMotionEvent(
+ Device: Int,
+ delta_timestamp: Long,
+ gyro_x: Float,
+ gyro_y: Float,
+ gyro_z: Float,
+ accel_x: Float,
+ accel_y: Float,
+ accel_z: Float
+ ): Boolean
+
+ /**
+ * Signals and load a nfc tag
+ *
+ * @param data Byte array containing all the data from a nfc tag
+ */
+ external fun onReadNfcTag(data: ByteArray?): Boolean
+
+ /**
+ * Removes current loaded nfc tag
+ */
+ external fun onRemoveNfcTag(): Boolean
+
+ /**
+ * Handles touch press events.
+ *
+ * @param finger_id The finger id corresponding to this event
+ * @param x_axis The value of the x-axis.
+ * @param y_axis The value of the y-axis.
+ */
+ external fun onTouchPressed(finger_id: Int, x_axis: Float, y_axis: Float)
+
+ /**
+ * Handles touch movement.
+ *
+ * @param x_axis The value of the instantaneous x-axis.
+ * @param y_axis The value of the instantaneous y-axis.
+ */
+ external fun onTouchMoved(finger_id: Int, x_axis: Float, y_axis: Float)
+
+ /**
+ * Handles touch release events.
+ *
+ * @param finger_id The finger id corresponding to this event
+ */
+ external fun onTouchReleased(finger_id: Int)
+
+ external fun reloadSettings()
+
+ external fun getUserSetting(gameID: String?, Section: String?, Key: String?): String?
+
+ external fun setUserSetting(gameID: String?, Section: String?, Key: String?, Value: String?)
+
+ external fun initGameIni(gameID: String?)
+
+ /**
+ * Gets the embedded icon within the given ROM.
+ *
+ * @param filename the file path to the ROM.
+ * @return a byte array containing the JPEG data for the icon.
+ */
+ external fun getIcon(filename: String): ByteArray
+
+ /**
+ * Gets the embedded title of the given ISO/ROM.
+ *
+ * @param filename The file path to the ISO/ROM.
+ * @return the embedded title of the ISO/ROM.
+ */
+ external fun getTitle(filename: String): String
+
+ external fun getDescription(filename: String): String
+
+ external fun getGameId(filename: String): String
+
+ external fun getRegions(filename: String): String
+
+ external fun getCompany(filename: String): String
+
+ external fun setAppDirectory(directory: String)
+
+ external fun initializeGpuDriver(
+ hookLibDir: String?,
+ customDriverDir: String?,
+ customDriverName: String?,
+ fileRedirectDir: String?
+ )
+
+ external fun reloadKeys(): Boolean
+
+ external fun initializeEmulation()
+
+ external fun defaultCPUCore(): Int
+
+ /**
+ * Begins emulation.
+ */
+ external fun run(path: String?)
+
+ /**
+ * Begins emulation from the specified savestate.
+ */
+ external fun run(path: String?, savestatePath: String?, deleteSavestate: Boolean)
+
+ // Surface Handling
+ external fun surfaceChanged(surf: Surface?)
+
+ external fun surfaceDestroyed()
+
+ external fun doFrame()
+
+ /**
+ * Unpauses emulation from a paused state.
+ */
+ external fun unPauseEmulation()
+
+ /**
+ * Pauses emulation.
+ */
+ external fun pauseEmulation()
+
+ /**
+ * Stops emulation.
+ */
+ external fun stopEmulation()
+
+ /**
+ * Resets the in-memory ROM metadata cache.
+ */
+ external fun resetRomMetadata()
+
+ /**
+ * Returns true if emulation is running (or is paused).
+ */
+ external fun isRunning(): Boolean
+
+ /**
+ * Returns the performance stats for the current game
+ */
+ external fun getPerfStats(): DoubleArray
+
+ /**
+ * Notifies the core emulation that the orientation has changed.
+ */
+ external fun notifyOrientationChange(layout_option: Int, rotation: Int)
+
+ enum class CoreError {
+ ErrorSystemFiles,
+ ErrorSavestate,
+ ErrorUnknown
+ }
+
+ private var coreErrorAlertResult = false
+ private val coreErrorAlertLock = Object()
+
+ class CoreErrorDialogFragment : DialogFragment() {
+ override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+ val title = requireArguments().serializable<String>("title")
+ val message = requireArguments().serializable<String>("message")
+
+ return MaterialAlertDialogBuilder(requireActivity())
+ .setTitle(title)
+ .setMessage(message)
+ .setPositiveButton(R.string.continue_button, null)
+ .setNegativeButton(R.string.abort_button) { _: DialogInterface?, _: Int ->
+ coreErrorAlertResult = false
+ synchronized(coreErrorAlertLock) { coreErrorAlertLock.notify() }
+ }
+ .create()
+ }
+
+ override fun onDismiss(dialog: DialogInterface) {
+ coreErrorAlertResult = true
+ synchronized(coreErrorAlertLock) { coreErrorAlertLock.notify() }
+ }
+
+ companion object {
+ fun newInstance(title: String?, message: String?): CoreErrorDialogFragment {
+ val frag = CoreErrorDialogFragment()
+ val args = Bundle()
+ args.putString("title", title)
+ args.putString("message", message)
+ frag.arguments = args
+ return frag
+ }
+ }
+ }
+
+ private fun onCoreErrorImpl(title: String, message: String) {
+ val emulationActivity = sEmulationActivity.get()
+ if (emulationActivity == null) {
+ error("[NativeLibrary] EmulationActivity not present")
+ return
+ }
+
+ val fragment = CoreErrorDialogFragment.newInstance(title, message)
+ fragment.show(emulationActivity.supportFragmentManager, "coreError")
+ }
+
+ /**
+ * Handles a core error.
+ *
+ * @return true: continue; false: abort
+ */
+ fun onCoreError(error: CoreError?, details: String): Boolean {
+ val emulationActivity = sEmulationActivity.get()
+ if (emulationActivity == null) {
+ error("[NativeLibrary] EmulationActivity not present")
+ return false
+ }
+
+ val title: String
+ val message: String
+ when (error) {
+ CoreError.ErrorSystemFiles -> {
+ title = emulationActivity.getString(R.string.system_archive_not_found)
+ message = emulationActivity.getString(
+ R.string.system_archive_not_found_message,
+ details.ifEmpty { emulationActivity.getString(R.string.system_archive_general) }
+ )
+ }
+ CoreError.ErrorSavestate -> {
+ title = emulationActivity.getString(R.string.save_load_error)
+ message = details
+ }
+ CoreError.ErrorUnknown -> {
+ title = emulationActivity.getString(R.string.fatal_error)
+ message = emulationActivity.getString(R.string.fatal_error_message)
+ }
+ else -> {
+ return true
+ }
+ }
+
+ // Show the AlertDialog on the main thread.
+ emulationActivity.runOnUiThread(Runnable { onCoreErrorImpl(title, message) })
+
+ // Wait for the lock to notify that it is complete.
+ synchronized(coreErrorAlertLock) { coreErrorAlertLock.wait() }
+
+ return coreErrorAlertResult
+ }
+
+ @JvmStatic
+ fun exitEmulationActivity(resultCode: Int) {
+ val Success = 0
+ val ErrorNotInitialized = 1
+ val ErrorGetLoader = 2
+ val ErrorSystemFiles = 3
+ val ErrorSharedFont = 4
+ val ErrorVideoCore = 5
+ val ErrorUnknown = 6
+ val ErrorLoader = 7
+
+ val captionId: Int
+ var descriptionId: Int
+ when (resultCode) {
+ ErrorVideoCore -> {
+ captionId = R.string.loader_error_video_core
+ descriptionId = R.string.loader_error_video_core_description
+ }
+ else -> {
+ captionId = R.string.loader_error_encrypted
+ descriptionId = R.string.loader_error_encrypted_roms_description
+ if (!reloadKeys()) {
+ descriptionId = R.string.loader_error_encrypted_keys_description
+ }
+ }
+ }
+
+ val emulationActivity = sEmulationActivity.get()
+ if (emulationActivity == null) {
+ warning("[NativeLibrary] EmulationActivity is null, can't exit.")
+ return
+ }
+
+ val builder = MaterialAlertDialogBuilder(emulationActivity)
+ .setTitle(captionId)
+ .setMessage(
+ Html.fromHtml(
+ emulationActivity.getString(descriptionId),
+ Html.FROM_HTML_MODE_LEGACY
+ )
+ )
+ .setPositiveButton(android.R.string.ok) { _: DialogInterface?, _: Int -> emulationActivity.finish() }
+ .setOnDismissListener { emulationActivity.finish() }
+ emulationActivity.runOnUiThread {
+ val alert = builder.create()
+ alert.show()
+ (alert.findViewById<View>(android.R.id.message) as TextView).movementMethod =
+ LinkMovementMethod.getInstance()
+ }
+ }
+
+ fun setEmulationActivity(emulationActivity: EmulationActivity?) {
+ verbose("[NativeLibrary] Registering EmulationActivity.")
+ sEmulationActivity = WeakReference(emulationActivity)
+ }
+
+ fun clearEmulationActivity() {
+ verbose("[NativeLibrary] Unregistering EmulationActivity.")
+ sEmulationActivity.clear()
+ }
+
+ /**
+ * Logs the Yuzu version, Android version and, CPU.
+ */
+ external fun logDeviceInfo()
+
+ /**
+ * Submits inline keyboard text. Called on input for buttons that result text.
+ * @param text Text to submit to the inline software keyboard implementation.
+ */
+ external fun submitInlineKeyboardText(text: String?)
+
+ /**
+ * Submits inline keyboard input. Used to indicate keys pressed that are not text.
+ * @param key_code Android Key Code associated with the keyboard input.
+ */
+ external fun submitInlineKeyboardInput(key_code: Int)
+
+ /**
+ * Button type for use in onTouchEvent
+ */
+ object ButtonType {
+ const val BUTTON_A = 0
+ const val BUTTON_B = 1
+ const val BUTTON_X = 2
+ const val BUTTON_Y = 3
+ const val STICK_L = 4
+ const val STICK_R = 5
+ const val TRIGGER_L = 6
+ const val TRIGGER_R = 7
+ const val TRIGGER_ZL = 8
+ const val TRIGGER_ZR = 9
+ const val BUTTON_PLUS = 10
+ const val BUTTON_MINUS = 11
+ const val DPAD_LEFT = 12
+ const val DPAD_UP = 13
+ const val DPAD_RIGHT = 14
+ const val DPAD_DOWN = 15
+ const val BUTTON_SL = 16
+ const val BUTTON_SR = 17
+ const val BUTTON_HOME = 18
+ const val BUTTON_CAPTURE = 19
+ }
+
+ /**
+ * Stick type for use in onTouchEvent
+ */
+ object StickType {
+ const val STICK_L = 0
+ const val STICK_R = 1
+ }
+
+ /**
+ * Button states
+ */
+ object ButtonState {
+ const val RELEASED = 0
+ const val PRESSED = 1
+ }
+}