android: Implement amiibo reading from nfc tag
parent
b2aeb50229
commit
ca4be4283d
@ -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()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||||
|
<tech-list>
|
||||||
|
<tech>android.nfc.tech.NfcA</tech>
|
||||||
|
</tech-list>
|
||||||
|
</resources>
|
Loading…
Reference in New Issue