diff options
| author | 2023-03-22 11:09:12 -0600 | |
|---|---|---|
| committer | 2023-06-03 00:05:48 -0700 | |
| commit | ca4be4283d8eec9624019f4bca88f7d1aa467f01 (patch) | |
| tree | 6fc3424bb97d17315b1e63335020efc57a65b912 | |
| parent | android: vulkan_device: Disable VK_EXT_custom_border_color on Adreno. (diff) | |
| download | yuzu-ca4be4283d8eec9624019f4bca88f7d1aa467f01.tar.gz yuzu-ca4be4283d8eec9624019f4bca88f7d1aa467f01.tar.xz yuzu-ca4be4283d8eec9624019f4bca88f7d1aa467f01.zip | |
android: Implement amiibo reading from nfc tag
15 files changed, 327 insertions, 8 deletions
diff --git a/src/android/app/src/main/AndroidManifest.xml b/src/android/app/src/main/AndroidManifest.xml index c8f1696c5..a5c063d52 100644 --- a/src/android/app/src/main/AndroidManifest.xml +++ b/src/android/app/src/main/AndroidManifest.xml | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | 13 | ||
| 14 | <uses-permission android:name="android.permission.INTERNET" /> | 14 | <uses-permission android:name="android.permission.INTERNET" /> |
| 15 | <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> | 15 | <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> |
| 16 | <uses-permission android:name="android.permission.NFC" /> | ||
| 16 | 17 | ||
| 17 | <application | 18 | <application |
| 18 | android:name="org.yuzu.yuzu_emu.YuzuApplication" | 19 | android:name="org.yuzu.yuzu_emu.YuzuApplication" |
| @@ -48,7 +49,19 @@ | |||
| 48 | android:name="org.yuzu.yuzu_emu.activities.EmulationActivity" | 49 | android:name="org.yuzu.yuzu_emu.activities.EmulationActivity" |
| 49 | android:theme="@style/Theme.Yuzu.Main" | 50 | android:theme="@style/Theme.Yuzu.Main" |
| 50 | android:launchMode="singleTop" | 51 | android:launchMode="singleTop" |
| 51 | android:screenOrientation="userLandscape" /> | 52 | android:screenOrientation="userLandscape" |
| 53 | android:exported="true"> | ||
| 54 | |||
| 55 | <intent-filter> | ||
| 56 | <action android:name="android.nfc.action.TECH_DISCOVERED" /> | ||
| 57 | <category android:name="android.intent.category.DEFAULT" /> | ||
| 58 | <data android:mimeType="application/octet-stream" /> | ||
| 59 | </intent-filter> | ||
| 60 | |||
| 61 | <meta-data | ||
| 62 | android:name="android.nfc.action.TECH_DISCOVERED" | ||
| 63 | android:resource="@xml/nfc_tech_filter" /> | ||
| 64 | </activity> | ||
| 52 | 65 | ||
| 53 | <service android:name="org.yuzu.yuzu_emu.utils.ForegroundService"/> | 66 | <service android:name="org.yuzu.yuzu_emu.utils.ForegroundService"/> |
| 54 | 67 | ||
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.java b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.java index c056b7d6d..5def17f2b 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.java +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.java | |||
| @@ -124,6 +124,18 @@ public final class NativeLibrary { | |||
| 124 | float gyro_z, float accel_x, float accel_y, float accel_z); | 124 | float gyro_z, float accel_x, float accel_y, float accel_z); |
| 125 | 125 | ||
| 126 | /** | 126 | /** |
| 127 | * Signals and load a nfc tag | ||
| 128 | * | ||
| 129 | * @param data Byte array containing all the data from a nfc tag | ||
| 130 | */ | ||
| 131 | public static native boolean onReadNfcTag(byte[] data); | ||
| 132 | |||
| 133 | /** | ||
| 134 | * Removes current loaded nfc tag | ||
| 135 | */ | ||
| 136 | public static native boolean onRemoveNfcTag(); | ||
| 137 | |||
| 138 | /** | ||
| 127 | * Handles touch press events. | 139 | * Handles touch press events. |
| 128 | * | 140 | * |
| 129 | * @param finger_id The finger id corresponding to this event | 141 | * @param finger_id The finger id corresponding to this event |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt index 3589e7629..32d04ef31 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt | |||
| @@ -24,6 +24,7 @@ import org.yuzu.yuzu_emu.features.settings.model.Settings | |||
| 24 | import org.yuzu.yuzu_emu.fragments.EmulationFragment | 24 | import org.yuzu.yuzu_emu.fragments.EmulationFragment |
| 25 | import org.yuzu.yuzu_emu.model.Game | 25 | import org.yuzu.yuzu_emu.model.Game |
| 26 | import org.yuzu.yuzu_emu.utils.ControllerMappingHelper | 26 | import org.yuzu.yuzu_emu.utils.ControllerMappingHelper |
| 27 | import org.yuzu.yuzu_emu.utils.NfcReader | ||
| 27 | import org.yuzu.yuzu_emu.utils.SerializableHelper.parcelable | 28 | import org.yuzu.yuzu_emu.utils.SerializableHelper.parcelable |
| 28 | import org.yuzu.yuzu_emu.utils.ThemeHelper | 29 | import org.yuzu.yuzu_emu.utils.ThemeHelper |
| 29 | import kotlin.math.roundToInt | 30 | import kotlin.math.roundToInt |
| @@ -37,6 +38,7 @@ open class EmulationActivity : AppCompatActivity() { | |||
| 37 | var isActivityRecreated = false | 38 | var isActivityRecreated = false |
| 38 | private var menuVisible = false | 39 | private var menuVisible = false |
| 39 | private var emulationFragment: EmulationFragment? = null | 40 | private var emulationFragment: EmulationFragment? = null |
| 41 | private lateinit var nfcReader: NfcReader | ||
| 40 | 42 | ||
| 41 | private lateinit var game: Game | 43 | private lateinit var game: Game |
| 42 | 44 | ||
| @@ -76,6 +78,9 @@ open class EmulationActivity : AppCompatActivity() { | |||
| 76 | } | 78 | } |
| 77 | title = game.title | 79 | title = game.title |
| 78 | 80 | ||
| 81 | nfcReader = NfcReader(this) | ||
| 82 | nfcReader.initialize() | ||
| 83 | |||
| 79 | // Start a foreground service to prevent the app from getting killed in the background | 84 | // Start a foreground service to prevent the app from getting killed in the background |
| 80 | // TODO(bunnei): Disable notifications until we support app suspension. | 85 | // TODO(bunnei): Disable notifications until we support app suspension. |
| 81 | //foregroundService = new Intent(EmulationActivity.this, ForegroundService.class); | 86 | //foregroundService = new Intent(EmulationActivity.this, ForegroundService.class); |
| @@ -104,6 +109,21 @@ open class EmulationActivity : AppCompatActivity() { | |||
| 104 | } | 109 | } |
| 105 | return super.onKeyDown(keyCode, event) | 110 | return super.onKeyDown(keyCode, event) |
| 106 | } | 111 | } |
| 112 | override fun onResume() { | ||
| 113 | super.onResume() | ||
| 114 | nfcReader.startScanning() | ||
| 115 | } | ||
| 116 | |||
| 117 | override fun onPause() { | ||
| 118 | super.onPause() | ||
| 119 | nfcReader.stopScanning() | ||
| 120 | } | ||
| 121 | |||
| 122 | override fun onNewIntent(intent: Intent) { | ||
| 123 | super.onNewIntent(intent) | ||
| 124 | setIntent(intent) | ||
| 125 | nfcReader.onNewIntent(intent) | ||
| 126 | } | ||
| 107 | 127 | ||
| 108 | override fun onSaveInstanceState(outState: Bundle) { | 128 | override fun onSaveInstanceState(outState: Bundle) { |
| 109 | outState.putParcelable(EXTRA_SELECTED_GAME, game) | 129 | outState.putParcelable(EXTRA_SELECTED_GAME, game) |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt index b87125d1c..441c9da9c 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt | |||
| @@ -112,6 +112,7 @@ class MainActivity : AppCompatActivity(), MainView { | |||
| 112 | when (request) { | 112 | when (request) { |
| 113 | MainPresenter.REQUEST_ADD_DIRECTORY -> getGamesDirectory.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data) | 113 | MainPresenter.REQUEST_ADD_DIRECTORY -> getGamesDirectory.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data) |
| 114 | MainPresenter.REQUEST_INSTALL_KEYS -> getProdKey.launch(arrayOf("*/*")) | 114 | MainPresenter.REQUEST_INSTALL_KEYS -> getProdKey.launch(arrayOf("*/*")) |
| 115 | MainPresenter.REQUEST_INSTALL_AMIIBO_KEYS -> getAmiiboKey.launch(arrayOf("*/*")) | ||
| 115 | MainPresenter.REQUEST_SELECT_GPU_DRIVER -> { | 116 | MainPresenter.REQUEST_SELECT_GPU_DRIVER -> { |
| 116 | // Get the driver name for the dialog message. | 117 | // Get the driver name for the dialog message. |
| 117 | var driverName = GpuDriverHelper.customDriverName | 118 | var driverName = GpuDriverHelper.customDriverName |
| @@ -221,6 +222,37 @@ class MainActivity : AppCompatActivity(), MainView { | |||
| 221 | } | 222 | } |
| 222 | } | 223 | } |
| 223 | 224 | ||
| 225 | private val getAmiiboKey = | ||
| 226 | registerForActivityResult(ActivityResultContracts.OpenDocument()) { result -> | ||
| 227 | if (result == null) | ||
| 228 | return@registerForActivityResult | ||
| 229 | |||
| 230 | val takeFlags = | ||
| 231 | Intent.FLAG_GRANT_WRITE_URI_PERMISSION or Intent.FLAG_GRANT_READ_URI_PERMISSION | ||
| 232 | contentResolver.takePersistableUriPermission( | ||
| 233 | result, | ||
| 234 | takeFlags | ||
| 235 | ) | ||
| 236 | |||
| 237 | val dstPath = DirectoryInitialization.userDirectory + "/keys/" | ||
| 238 | if (FileUtil.copyUriToInternalStorage(this, result, dstPath, "key_retail.bin")) { | ||
| 239 | if (NativeLibrary.ReloadKeys()) { | ||
| 240 | Toast.makeText( | ||
| 241 | this, | ||
| 242 | R.string.install_keys_success, | ||
| 243 | Toast.LENGTH_SHORT | ||
| 244 | ).show() | ||
| 245 | refreshFragment() | ||
| 246 | } else { | ||
| 247 | Toast.makeText( | ||
| 248 | this, | ||
| 249 | R.string.install_amiibo_keys_failure, | ||
| 250 | Toast.LENGTH_LONG | ||
| 251 | ).show() | ||
| 252 | } | ||
| 253 | } | ||
| 254 | } | ||
| 255 | |||
| 224 | private val getDriver = | 256 | private val getDriver = |
| 225 | registerForActivityResult(ActivityResultContracts.OpenDocument()) { result -> | 257 | registerForActivityResult(ActivityResultContracts.OpenDocument()) { result -> |
| 226 | if (result == null) | 258 | if (result == null) |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainPresenter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainPresenter.kt index dbfda7be3..554542e05 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainPresenter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainPresenter.kt | |||
| @@ -36,6 +36,10 @@ class MainPresenter(private val view: MainView) { | |||
| 36 | launchFileListActivity(REQUEST_INSTALL_KEYS) | 36 | launchFileListActivity(REQUEST_INSTALL_KEYS) |
| 37 | return true | 37 | return true |
| 38 | } | 38 | } |
| 39 | R.id.button_install_amiibo_keys -> { | ||
| 40 | launchFileListActivity(REQUEST_INSTALL_AMIIBO_KEYS) | ||
| 41 | return true | ||
| 42 | } | ||
| 39 | R.id.button_select_gpu_driver -> { | 43 | R.id.button_select_gpu_driver -> { |
| 40 | launchFileListActivity(REQUEST_SELECT_GPU_DRIVER) | 44 | launchFileListActivity(REQUEST_SELECT_GPU_DRIVER) |
| 41 | return true | 45 | return true |
| @@ -64,6 +68,7 @@ class MainPresenter(private val view: MainView) { | |||
| 64 | companion object { | 68 | companion object { |
| 65 | const val REQUEST_ADD_DIRECTORY = 1 | 69 | const val REQUEST_ADD_DIRECTORY = 1 |
| 66 | const val REQUEST_INSTALL_KEYS = 2 | 70 | const val REQUEST_INSTALL_KEYS = 2 |
| 67 | const val REQUEST_SELECT_GPU_DRIVER = 3 | 71 | const val REQUEST_INSTALL_AMIIBO_KEYS = 3 |
| 72 | const val REQUEST_SELECT_GPU_DRIVER = 4 | ||
| 68 | } | 73 | } |
| 69 | } | 74 | } |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NfcReader.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NfcReader.kt new file mode 100644 index 000000000..1ce220d42 --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NfcReader.kt | |||
| @@ -0,0 +1,165 @@ | |||
| 1 | package org.yuzu.yuzu_emu.utils | ||
| 2 | |||
| 3 | import android.app.Activity | ||
| 4 | import android.app.PendingIntent | ||
| 5 | import android.content.Intent | ||
| 6 | import android.content.IntentFilter | ||
| 7 | import android.nfc.NfcAdapter | ||
| 8 | import android.nfc.Tag | ||
| 9 | import android.nfc.tech.NfcA | ||
| 10 | import android.os.Build | ||
| 11 | import android.os.Handler | ||
| 12 | import android.os.Looper | ||
| 13 | import org.yuzu.yuzu_emu.NativeLibrary | ||
| 14 | import java.io.IOException | ||
| 15 | |||
| 16 | class NfcReader(private val activity: Activity) { | ||
| 17 | private var nfcAdapter: NfcAdapter? = null | ||
| 18 | private var pendingIntent: PendingIntent? = null | ||
| 19 | |||
| 20 | fun initialize() { | ||
| 21 | nfcAdapter = NfcAdapter.getDefaultAdapter(activity) ?: return | ||
| 22 | |||
| 23 | pendingIntent = PendingIntent.getActivity( | ||
| 24 | activity, | ||
| 25 | 0, Intent(activity, activity.javaClass), | ||
| 26 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) | ||
| 27 | PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE | ||
| 28 | else PendingIntent.FLAG_UPDATE_CURRENT | ||
| 29 | ) | ||
| 30 | |||
| 31 | val tagDetected = IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED) | ||
| 32 | tagDetected.addCategory(Intent.CATEGORY_DEFAULT) | ||
| 33 | } | ||
| 34 | |||
| 35 | fun startScanning() { | ||
| 36 | nfcAdapter?.enableForegroundDispatch(activity, pendingIntent, null, null) | ||
| 37 | } | ||
| 38 | |||
| 39 | fun stopScanning() { | ||
| 40 | nfcAdapter?.disableForegroundDispatch(activity) | ||
| 41 | } | ||
| 42 | |||
| 43 | fun onNewIntent(intent: Intent) { | ||
| 44 | val action = intent.action | ||
| 45 | if (NfcAdapter.ACTION_TAG_DISCOVERED != action | ||
| 46 | && NfcAdapter.ACTION_TECH_DISCOVERED != action | ||
| 47 | && NfcAdapter.ACTION_NDEF_DISCOVERED != action | ||
| 48 | ) { | ||
| 49 | return | ||
| 50 | } | ||
| 51 | |||
| 52 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { | ||
| 53 | val tag = | ||
| 54 | intent.getParcelableExtra(NfcAdapter.EXTRA_TAG, Tag::class.java) ?: return | ||
| 55 | readTagData(tag) | ||
| 56 | return | ||
| 57 | } | ||
| 58 | |||
| 59 | val tag = | ||
| 60 | intent.getParcelableExtra<Tag>(NfcAdapter.EXTRA_TAG) ?: return | ||
| 61 | readTagData(tag) | ||
| 62 | } | ||
| 63 | |||
| 64 | private fun readTagData(tag: Tag) { | ||
| 65 | if (!tag.techList.contains("android.nfc.tech.NfcA")) { | ||
| 66 | return | ||
| 67 | } | ||
| 68 | |||
| 69 | val amiibo = NfcA.get(tag) ?: return | ||
| 70 | amiibo.connect() | ||
| 71 | |||
| 72 | val tagData = ntag215ReadAll(amiibo) ?: return | ||
| 73 | NativeLibrary.onReadNfcTag(tagData) | ||
| 74 | |||
| 75 | nfcAdapter?.ignore( | ||
| 76 | tag, | ||
| 77 | 1000, | ||
| 78 | { NativeLibrary.onRemoveNfcTag() }, | ||
| 79 | Handler(Looper.getMainLooper()) | ||
| 80 | ) | ||
| 81 | } | ||
| 82 | |||
| 83 | private fun ntag215ReadAll(amiibo: NfcA): ByteArray? { | ||
| 84 | val bufferSize = amiibo.maxTransceiveLength; | ||
| 85 | val tagSize = 0x21C | ||
| 86 | val pageSize = 4 | ||
| 87 | val lastPage = tagSize / pageSize - 1 | ||
| 88 | val tagData = ByteArray(tagSize) | ||
| 89 | |||
| 90 | // We need to read the ntag in steps otherwise we overflow the buffer | ||
| 91 | for (i in 0..tagSize step bufferSize - 1) { | ||
| 92 | val dataStart = i / pageSize | ||
| 93 | var dataEnd = (i + bufferSize) / pageSize | ||
| 94 | |||
| 95 | if (dataEnd > lastPage) { | ||
| 96 | dataEnd = lastPage | ||
| 97 | } | ||
| 98 | |||
| 99 | try { | ||
| 100 | val data = ntag215FastRead(amiibo, dataStart, dataEnd - 1) | ||
| 101 | System.arraycopy(data, 0, tagData, i, (dataEnd - dataStart) * pageSize) | ||
| 102 | } catch (e: IOException) { | ||
| 103 | return null; | ||
| 104 | } | ||
| 105 | } | ||
| 106 | return tagData | ||
| 107 | } | ||
| 108 | |||
| 109 | private fun ntag215Read(amiibo: NfcA, page: Int): ByteArray? { | ||
| 110 | return amiibo.transceive( | ||
| 111 | byteArrayOf( | ||
| 112 | 0x30.toByte(), | ||
| 113 | (page and 0xFF).toByte() | ||
| 114 | ) | ||
| 115 | ) | ||
| 116 | } | ||
| 117 | |||
| 118 | private fun ntag215FastRead(amiibo: NfcA, start: Int, end: Int): ByteArray? { | ||
| 119 | return amiibo.transceive( | ||
| 120 | byteArrayOf( | ||
| 121 | 0x3A.toByte(), | ||
| 122 | (start and 0xFF).toByte(), | ||
| 123 | (end and 0xFF).toByte() | ||
| 124 | ) | ||
| 125 | ) | ||
| 126 | } | ||
| 127 | |||
| 128 | private fun ntag215PWrite( | ||
| 129 | amiibo: NfcA, | ||
| 130 | page: Int, | ||
| 131 | data1: Int, | ||
| 132 | data2: Int, | ||
| 133 | data3: Int, | ||
| 134 | data4: Int | ||
| 135 | ): ByteArray? { | ||
| 136 | return amiibo.transceive( | ||
| 137 | byteArrayOf( | ||
| 138 | 0xA2.toByte(), | ||
| 139 | (page and 0xFF).toByte(), | ||
| 140 | (data1 and 0xFF).toByte(), | ||
| 141 | (data2 and 0xFF).toByte(), | ||
| 142 | (data3 and 0xFF).toByte(), | ||
| 143 | (data4 and 0xFF).toByte() | ||
| 144 | ) | ||
| 145 | ) | ||
| 146 | } | ||
| 147 | |||
| 148 | private fun ntag215PwdAuth( | ||
| 149 | amiibo: NfcA, | ||
| 150 | data1: Int, | ||
| 151 | data2: Int, | ||
| 152 | data3: Int, | ||
| 153 | data4: Int | ||
| 154 | ): ByteArray? { | ||
| 155 | return amiibo.transceive( | ||
| 156 | byteArrayOf( | ||
| 157 | 0x1B.toByte(), | ||
| 158 | (data1 and 0xFF).toByte(), | ||
| 159 | (data2 and 0xFF).toByte(), | ||
| 160 | (data3 and 0xFF).toByte(), | ||
| 161 | (data4 and 0xFF).toByte() | ||
| 162 | ) | ||
| 163 | ) | ||
| 164 | } | ||
| 165 | } | ||
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 index ad17cf129..2fdb68823 100644 --- a/src/android/app/src/main/jni/emu_window/emu_window.cpp +++ b/src/android/app/src/main/jni/emu_window/emu_window.cpp | |||
| @@ -2,6 +2,7 @@ | |||
| 2 | 2 | ||
| 3 | #include "common/logging/log.h" | 3 | #include "common/logging/log.h" |
| 4 | #include "input_common/drivers/touch_screen.h" | 4 | #include "input_common/drivers/touch_screen.h" |
| 5 | #include "input_common/drivers/virtual_amiibo.h" | ||
| 5 | #include "input_common/drivers/virtual_gamepad.h" | 6 | #include "input_common/drivers/virtual_gamepad.h" |
| 6 | #include "input_common/main.h" | 7 | #include "input_common/main.h" |
| 7 | #include "jni/emu_window/emu_window.h" | 8 | #include "jni/emu_window/emu_window.h" |
| @@ -36,7 +37,15 @@ void EmuWindow_Android::OnGamepadMotionEvent(int player_index, u64 delta_timesta | |||
| 36 | float gyro_y, float gyro_z, float accel_x, | 37 | float gyro_y, float gyro_z, float accel_x, |
| 37 | float accel_y, float accel_z) { | 38 | float accel_y, float accel_z) { |
| 38 | m_input_subsystem->GetVirtualGamepad()->SetMotionState( | 39 | m_input_subsystem->GetVirtualGamepad()->SetMotionState( |
| 39 | player_index, delta_timestamp, gyro_x, gyro_y, gyro_z, accel_x, accel_y, accel_z); | 40 | player_index, delta_timestamp, gyro_x, gyro_y, gyro_z, accel_x, accel_y, accel_z); |
| 41 | } | ||
| 42 | |||
| 43 | void EmuWindow_Android::OnReadNfcTag(std::span<u8> data) { | ||
| 44 | m_input_subsystem->GetVirtualAmiibo()->LoadAmiibo(data); | ||
| 45 | } | ||
| 46 | |||
| 47 | void EmuWindow_Android::OnRemoveNfcTag() { | ||
| 48 | m_input_subsystem->GetVirtualAmiibo()->CloseAmiibo(); | ||
| 40 | } | 49 | } |
| 41 | 50 | ||
| 42 | EmuWindow_Android::EmuWindow_Android(InputCommon::InputSubsystem* input_subsystem, | 51 | EmuWindow_Android::EmuWindow_Android(InputCommon::InputSubsystem* input_subsystem, |
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 index 1c1edf62c..2a0a72077 100644 --- a/src/android/app/src/main/jni/emu_window/emu_window.h +++ b/src/android/app/src/main/jni/emu_window/emu_window.h | |||
| @@ -1,6 +1,7 @@ | |||
| 1 | #pragma once | 1 | #pragma once |
| 2 | 2 | ||
| 3 | #include <memory> | 3 | #include <memory> |
| 4 | #include <span> | ||
| 4 | 5 | ||
| 5 | #include "core/frontend/emu_window.h" | 6 | #include "core/frontend/emu_window.h" |
| 6 | #include "core/frontend/graphics_context.h" | 7 | #include "core/frontend/graphics_context.h" |
| @@ -39,6 +40,8 @@ public: | |||
| 39 | void OnGamepadJoystickEvent(int player_index, int stick_id, float x, float y); | 40 | void OnGamepadJoystickEvent(int player_index, int stick_id, float x, float y); |
| 40 | void OnGamepadMotionEvent(int player_index, u64 delta_timestamp, float gyro_x, float gyro_y, | 41 | void OnGamepadMotionEvent(int player_index, u64 delta_timestamp, float gyro_x, float gyro_y, |
| 41 | float gyro_z, float accel_x, float accel_y, float accel_z); | 42 | float gyro_z, float accel_x, float accel_y, float accel_z); |
| 43 | void OnReadNfcTag(std::span<u8> data); | ||
| 44 | void OnRemoveNfcTag(); | ||
| 42 | void OnFrameDisplayed() override {} | 45 | void OnFrameDisplayed() override {} |
| 43 | 46 | ||
| 44 | std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override { | 47 | std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override { |
diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index 10603c8fa..86994f734 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp | |||
| @@ -451,6 +451,26 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadMotionEvent( | |||
| 451 | return static_cast<jboolean>(true); | 451 | return static_cast<jboolean>(true); |
| 452 | } | 452 | } |
| 453 | 453 | ||
| 454 | jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onReadNfcTag( | ||
| 455 | [[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jbyteArray j_data) { | ||
| 456 | jboolean isCopy{false}; | ||
| 457 | std::span<u8> data(reinterpret_cast<u8 *>(env->GetByteArrayElements(j_data, &isCopy)), | ||
| 458 | static_cast<size_t>(env->GetArrayLength(j_data))); | ||
| 459 | |||
| 460 | if (EmulationSession::GetInstance().IsRunning()) { | ||
| 461 | EmulationSession::GetInstance().Window().OnReadNfcTag(data); | ||
| 462 | } | ||
| 463 | return static_cast<jboolean>(true); | ||
| 464 | } | ||
| 465 | |||
| 466 | jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onRemoveNfcTag( | ||
| 467 | [[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz) { | ||
| 468 | if (EmulationSession::GetInstance().IsRunning()) { | ||
| 469 | EmulationSession::GetInstance().Window().OnRemoveNfcTag(); | ||
| 470 | } | ||
| 471 | return static_cast<jboolean>(true); | ||
| 472 | } | ||
| 473 | |||
| 454 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchPressed([[maybe_unused]] JNIEnv* env, | 474 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchPressed([[maybe_unused]] JNIEnv* env, |
| 455 | [[maybe_unused]] jclass clazz, jint id, | 475 | [[maybe_unused]] jclass clazz, jint id, |
| 456 | jfloat x, jfloat y) { | 476 | jfloat x, jfloat y) { |
diff --git a/src/android/app/src/main/jni/native.h b/src/android/app/src/main/jni/native.h index d30351c16..8336e525a 100644 --- a/src/android/app/src/main/jni/native.h +++ b/src/android/app/src/main/jni/native.h | |||
| @@ -32,7 +32,13 @@ JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadMoveEv | |||
| 32 | JNIEnv* env, jclass clazz, jstring j_device, jint axis, jfloat x, jfloat y); | 32 | JNIEnv* env, jclass clazz, jstring j_device, jint axis, jfloat x, jfloat y); |
| 33 | 33 | ||
| 34 | JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadAxisEvent( | 34 | JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadAxisEvent( |
| 35 | JNIEnv* env, jclass clazz, jstring j_device, jint axis_id, jfloat axis_val); | 35 | JNIEnv* env, jclass clazz, jstring j_device, jint axis_id, jfloat axis_val); |
| 36 | |||
| 37 | JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onReadNfcTag( | ||
| 38 | JNIEnv* env, jclass clazz, jbyteArray j_data); | ||
| 39 | |||
| 40 | JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onRemoveNfcTag( | ||
| 41 | JNIEnv* env, jclass clazz); | ||
| 36 | 42 | ||
| 37 | JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchEvent(JNIEnv* env, | 43 | JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchEvent(JNIEnv* env, |
| 38 | jclass clazz, | 44 | jclass clazz, |
diff --git a/src/android/app/src/main/res/menu/menu_game_grid.xml b/src/android/app/src/main/res/menu/menu_game_grid.xml index b834a444e..73046de0e 100644 --- a/src/android/app/src/main/res/menu/menu_game_grid.xml +++ b/src/android/app/src/main/res/menu/menu_game_grid.xml | |||
| @@ -23,6 +23,12 @@ | |||
| 23 | app:showAsAction="ifRoom" /> | 23 | app:showAsAction="ifRoom" /> |
| 24 | 24 | ||
| 25 | <item | 25 | <item |
| 26 | android:id="@+id/button_install_amiibo_keys" | ||
| 27 | android:icon="@drawable/ic_install" | ||
| 28 | android:title="@string/install_amiibo_keys" | ||
| 29 | app:showAsAction="ifRoom" /> | ||
| 30 | |||
| 31 | <item | ||
| 26 | android:id="@+id/button_select_gpu_driver" | 32 | android:id="@+id/button_select_gpu_driver" |
| 27 | android:icon="@drawable/ic_settings" | 33 | android:icon="@drawable/ic_settings" |
| 28 | android:title="@string/select_gpu_driver" | 34 | android:title="@string/select_gpu_driver" |
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index ea9290df5..29c1b1691 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml | |||
| @@ -52,8 +52,10 @@ | |||
| 52 | <!-- Add Directory Screen--> | 52 | <!-- Add Directory Screen--> |
| 53 | <string name="select_game_folder">Select game folder</string> | 53 | <string name="select_game_folder">Select game folder</string> |
| 54 | <string name="install_keys">Install keys</string> | 54 | <string name="install_keys">Install keys</string> |
| 55 | <string name="install_amiibo_keys">Install amiibo keys</string> | ||
| 55 | <string name="install_keys_success">Keys successfully installed</string> | 56 | <string name="install_keys_success">Keys successfully installed</string> |
| 56 | <string name="install_keys_failure">Keys file (prod.keys) is invalid</string> | 57 | <string name="install_keys_failure">Keys file (prod.keys) is invalid</string> |
| 58 | <string name="install_amiibo_keys_failure">Keys file (key_retail.bin) is invalid</string> | ||
| 57 | 59 | ||
| 58 | <!-- GPU driver installation --> | 60 | <!-- GPU driver installation --> |
| 59 | <string name="select_gpu_driver">Select GPU driver</string> | 61 | <string name="select_gpu_driver">Select GPU driver</string> |
diff --git a/src/android/app/src/main/res/xml/nfc_tech_filter.xml b/src/android/app/src/main/res/xml/nfc_tech_filter.xml new file mode 100644 index 000000000..eb4497446 --- /dev/null +++ b/src/android/app/src/main/res/xml/nfc_tech_filter.xml | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | <?xml version="1.0" encoding="utf-8"?> | ||
| 2 | <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> | ||
| 3 | <tech-list> | ||
| 4 | <tech>android.nfc.tech.NfcA</tech> | ||
| 5 | </tech-list> | ||
| 6 | </resources> | ||
diff --git a/src/input_common/drivers/virtual_amiibo.cpp b/src/input_common/drivers/virtual_amiibo.cpp index 304f4c70b..9505179c6 100644 --- a/src/input_common/drivers/virtual_amiibo.cpp +++ b/src/input_common/drivers/virtual_amiibo.cpp | |||
| @@ -73,10 +73,7 @@ VirtualAmiibo::State VirtualAmiibo::GetCurrentState() const { | |||
| 73 | VirtualAmiibo::Info VirtualAmiibo::LoadAmiibo(const std::string& filename) { | 73 | VirtualAmiibo::Info VirtualAmiibo::LoadAmiibo(const std::string& filename) { |
| 74 | const Common::FS::IOFile nfc_file{filename, Common::FS::FileAccessMode::Read, | 74 | const Common::FS::IOFile nfc_file{filename, Common::FS::FileAccessMode::Read, |
| 75 | Common::FS::FileType::BinaryFile}; | 75 | Common::FS::FileType::BinaryFile}; |
| 76 | 76 | std::vector<u8> data{}; | |
| 77 | if (state != State::WaitingForAmiibo) { | ||
| 78 | return Info::WrongDeviceState; | ||
| 79 | } | ||
| 80 | 77 | ||
| 81 | if (!nfc_file.IsOpen()) { | 78 | if (!nfc_file.IsOpen()) { |
| 82 | return Info::UnableToLoad; | 79 | return Info::UnableToLoad; |
| @@ -101,7 +98,28 @@ VirtualAmiibo::Info VirtualAmiibo::LoadAmiibo(const std::string& filename) { | |||
| 101 | } | 98 | } |
| 102 | 99 | ||
| 103 | file_path = filename; | 100 | file_path = filename; |
| 101 | return LoadAmiibo(data); | ||
| 102 | } | ||
| 103 | |||
| 104 | VirtualAmiibo::Info VirtualAmiibo::LoadAmiibo(std::span<u8> data) { | ||
| 105 | if (state != State::WaitingForAmiibo) { | ||
| 106 | return Info::WrongDeviceState; | ||
| 107 | } | ||
| 108 | |||
| 109 | switch (data.size_bytes()) { | ||
| 110 | case AmiiboSize: | ||
| 111 | case AmiiboSizeWithoutPassword: | ||
| 112 | nfc_data.resize(AmiiboSize); | ||
| 113 | break; | ||
| 114 | case MifareSize: | ||
| 115 | nfc_data.resize(MifareSize); | ||
| 116 | break; | ||
| 117 | default: | ||
| 118 | return Info::NotAnAmiibo; | ||
| 119 | } | ||
| 120 | |||
| 104 | state = State::AmiiboIsOpen; | 121 | state = State::AmiiboIsOpen; |
| 122 | memcpy(nfc_data.data(),data.data(),data.size_bytes()); | ||
| 105 | SetNfc(identifier, {Common::Input::NfcState::NewAmiibo, nfc_data}); | 123 | SetNfc(identifier, {Common::Input::NfcState::NewAmiibo, nfc_data}); |
| 106 | return Info::Success; | 124 | return Info::Success; |
| 107 | } | 125 | } |
diff --git a/src/input_common/drivers/virtual_amiibo.h b/src/input_common/drivers/virtual_amiibo.h index 488d00b31..34e97cd91 100644 --- a/src/input_common/drivers/virtual_amiibo.h +++ b/src/input_common/drivers/virtual_amiibo.h | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include <array> | 6 | #include <array> |
| 7 | #include <span> | ||
| 7 | #include <string> | 8 | #include <string> |
| 8 | #include <vector> | 9 | #include <vector> |
| 9 | 10 | ||
| @@ -47,6 +48,7 @@ public: | |||
| 47 | State GetCurrentState() const; | 48 | State GetCurrentState() const; |
| 48 | 49 | ||
| 49 | Info LoadAmiibo(const std::string& amiibo_file); | 50 | Info LoadAmiibo(const std::string& amiibo_file); |
| 51 | Info LoadAmiibo(std::span<u8> data); | ||
| 50 | Info ReloadAmiibo(); | 52 | Info ReloadAmiibo(); |
| 51 | Info CloseAmiibo(); | 53 | Info CloseAmiibo(); |
| 52 | 54 | ||