summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/android/app/src/main/AndroidManifest.xml15
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.java12
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt20
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt32
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainPresenter.kt7
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NfcReader.kt165
-rw-r--r--src/android/app/src/main/jni/emu_window/emu_window.cpp11
-rw-r--r--src/android/app/src/main/jni/emu_window/emu_window.h3
-rw-r--r--src/android/app/src/main/jni/native.cpp20
-rw-r--r--src/android/app/src/main/jni/native.h8
-rw-r--r--src/android/app/src/main/res/menu/menu_game_grid.xml6
-rw-r--r--src/android/app/src/main/res/values/strings.xml2
-rw-r--r--src/android/app/src/main/res/xml/nfc_tech_filter.xml6
-rw-r--r--src/input_common/drivers/virtual_amiibo.cpp26
-rw-r--r--src/input_common/drivers/virtual_amiibo.h2
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
24import org.yuzu.yuzu_emu.fragments.EmulationFragment 24import org.yuzu.yuzu_emu.fragments.EmulationFragment
25import org.yuzu.yuzu_emu.model.Game 25import org.yuzu.yuzu_emu.model.Game
26import org.yuzu.yuzu_emu.utils.ControllerMappingHelper 26import org.yuzu.yuzu_emu.utils.ControllerMappingHelper
27import org.yuzu.yuzu_emu.utils.NfcReader
27import org.yuzu.yuzu_emu.utils.SerializableHelper.parcelable 28import org.yuzu.yuzu_emu.utils.SerializableHelper.parcelable
28import org.yuzu.yuzu_emu.utils.ThemeHelper 29import org.yuzu.yuzu_emu.utils.ThemeHelper
29import kotlin.math.roundToInt 30import 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 @@
1package org.yuzu.yuzu_emu.utils
2
3import android.app.Activity
4import android.app.PendingIntent
5import android.content.Intent
6import android.content.IntentFilter
7import android.nfc.NfcAdapter
8import android.nfc.Tag
9import android.nfc.tech.NfcA
10import android.os.Build
11import android.os.Handler
12import android.os.Looper
13import org.yuzu.yuzu_emu.NativeLibrary
14import java.io.IOException
15
16class 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
43void EmuWindow_Android::OnReadNfcTag(std::span<u8> data) {
44 m_input_subsystem->GetVirtualAmiibo()->LoadAmiibo(data);
45}
46
47void EmuWindow_Android::OnRemoveNfcTag() {
48 m_input_subsystem->GetVirtualAmiibo()->CloseAmiibo();
40} 49}
41 50
42EmuWindow_Android::EmuWindow_Android(InputCommon::InputSubsystem* input_subsystem, 51EmuWindow_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
454jboolean 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
466jboolean 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
454void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchPressed([[maybe_unused]] JNIEnv* env, 474void 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
34JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadAxisEvent( 34JNIEXPORT 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
37JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onReadNfcTag(
38 JNIEnv* env, jclass clazz, jbyteArray j_data);
39
40JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onRemoveNfcTag(
41 JNIEnv* env, jclass clazz);
36 42
37JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchEvent(JNIEnv* env, 43JNIEXPORT 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 {
73VirtualAmiibo::Info VirtualAmiibo::LoadAmiibo(const std::string& filename) { 73VirtualAmiibo::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
104VirtualAmiibo::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