summaryrefslogtreecommitdiffstats
path: root/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NfcReader.kt
diff options
context:
space:
mode:
Diffstat (limited to 'src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NfcReader.kt')
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NfcReader.kt165
1 files changed, 165 insertions, 0 deletions
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 @@
+package org.yuzu.yuzu_emu.utils
+
+import android.app.Activity
+import android.app.PendingIntent
+import android.content.Intent
+import android.content.IntentFilter
+import android.nfc.NfcAdapter
+import android.nfc.Tag
+import android.nfc.tech.NfcA
+import android.os.Build
+import android.os.Handler
+import android.os.Looper
+import org.yuzu.yuzu_emu.NativeLibrary
+import java.io.IOException
+
+class NfcReader(private val activity: Activity) {
+ private var nfcAdapter: NfcAdapter? = null
+ private var pendingIntent: PendingIntent? = null
+
+ fun initialize() {
+ nfcAdapter = NfcAdapter.getDefaultAdapter(activity) ?: return
+
+ pendingIntent = PendingIntent.getActivity(
+ activity,
+ 0, Intent(activity, activity.javaClass),
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
+ else PendingIntent.FLAG_UPDATE_CURRENT
+ )
+
+ val tagDetected = IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED)
+ tagDetected.addCategory(Intent.CATEGORY_DEFAULT)
+ }
+
+ fun startScanning() {
+ nfcAdapter?.enableForegroundDispatch(activity, pendingIntent, null, null)
+ }
+
+ fun stopScanning() {
+ nfcAdapter?.disableForegroundDispatch(activity)
+ }
+
+ fun onNewIntent(intent: Intent) {
+ val action = intent.action
+ if (NfcAdapter.ACTION_TAG_DISCOVERED != action
+ && NfcAdapter.ACTION_TECH_DISCOVERED != action
+ && NfcAdapter.ACTION_NDEF_DISCOVERED != action
+ ) {
+ return
+ }
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ val tag =
+ intent.getParcelableExtra(NfcAdapter.EXTRA_TAG, Tag::class.java) ?: return
+ readTagData(tag)
+ return
+ }
+
+ val tag =
+ intent.getParcelableExtra<Tag>(NfcAdapter.EXTRA_TAG) ?: return
+ readTagData(tag)
+ }
+
+ private fun readTagData(tag: Tag) {
+ if (!tag.techList.contains("android.nfc.tech.NfcA")) {
+ return
+ }
+
+ val amiibo = NfcA.get(tag) ?: return
+ amiibo.connect()
+
+ val tagData = ntag215ReadAll(amiibo) ?: return
+ NativeLibrary.onReadNfcTag(tagData)
+
+ nfcAdapter?.ignore(
+ tag,
+ 1000,
+ { NativeLibrary.onRemoveNfcTag() },
+ Handler(Looper.getMainLooper())
+ )
+ }
+
+ private fun ntag215ReadAll(amiibo: NfcA): ByteArray? {
+ val bufferSize = amiibo.maxTransceiveLength;
+ val tagSize = 0x21C
+ val pageSize = 4
+ val lastPage = tagSize / pageSize - 1
+ val tagData = ByteArray(tagSize)
+
+ // We need to read the ntag in steps otherwise we overflow the buffer
+ for (i in 0..tagSize step bufferSize - 1) {
+ val dataStart = i / pageSize
+ var dataEnd = (i + bufferSize) / pageSize
+
+ if (dataEnd > lastPage) {
+ dataEnd = lastPage
+ }
+
+ try {
+ val data = ntag215FastRead(amiibo, dataStart, dataEnd - 1)
+ System.arraycopy(data, 0, tagData, i, (dataEnd - dataStart) * pageSize)
+ } catch (e: IOException) {
+ return null;
+ }
+ }
+ return tagData
+ }
+
+ private fun ntag215Read(amiibo: NfcA, page: Int): ByteArray? {
+ return amiibo.transceive(
+ byteArrayOf(
+ 0x30.toByte(),
+ (page and 0xFF).toByte()
+ )
+ )
+ }
+
+ private fun ntag215FastRead(amiibo: NfcA, start: Int, end: Int): ByteArray? {
+ return amiibo.transceive(
+ byteArrayOf(
+ 0x3A.toByte(),
+ (start and 0xFF).toByte(),
+ (end and 0xFF).toByte()
+ )
+ )
+ }
+
+ private fun ntag215PWrite(
+ amiibo: NfcA,
+ page: Int,
+ data1: Int,
+ data2: Int,
+ data3: Int,
+ data4: Int
+ ): ByteArray? {
+ return amiibo.transceive(
+ byteArrayOf(
+ 0xA2.toByte(),
+ (page and 0xFF).toByte(),
+ (data1 and 0xFF).toByte(),
+ (data2 and 0xFF).toByte(),
+ (data3 and 0xFF).toByte(),
+ (data4 and 0xFF).toByte()
+ )
+ )
+ }
+
+ private fun ntag215PwdAuth(
+ amiibo: NfcA,
+ data1: Int,
+ data2: Int,
+ data3: Int,
+ data4: Int
+ ): ByteArray? {
+ return amiibo.transceive(
+ byteArrayOf(
+ 0x1B.toByte(),
+ (data1 and 0xFF).toByte(),
+ (data2 and 0xFF).toByte(),
+ (data3 and 0xFF).toByte(),
+ (data4 and 0xFF).toByte()
+ )
+ )
+ }
+}