android: Implement amiibo reading from nfc tag
This commit is contained in:
		@@ -13,6 +13,7 @@
 | 
			
		||||
 | 
			
		||||
    <uses-permission android:name="android.permission.INTERNET" />
 | 
			
		||||
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
 | 
			
		||||
    <uses-permission android:name="android.permission.NFC" />
 | 
			
		||||
 | 
			
		||||
    <application
 | 
			
		||||
        android:name="org.yuzu.yuzu_emu.YuzuApplication"
 | 
			
		||||
@@ -48,7 +49,19 @@
 | 
			
		||||
            android:name="org.yuzu.yuzu_emu.activities.EmulationActivity"
 | 
			
		||||
            android:theme="@style/Theme.Yuzu.Main"
 | 
			
		||||
            android:launchMode="singleTop"
 | 
			
		||||
            android:screenOrientation="userLandscape" />
 | 
			
		||||
            android:screenOrientation="userLandscape"
 | 
			
		||||
            android:exported="true">
 | 
			
		||||
 | 
			
		||||
            <intent-filter>
 | 
			
		||||
                <action android:name="android.nfc.action.TECH_DISCOVERED" />
 | 
			
		||||
                <category android:name="android.intent.category.DEFAULT" />
 | 
			
		||||
                <data android:mimeType="application/octet-stream" />
 | 
			
		||||
            </intent-filter>
 | 
			
		||||
 | 
			
		||||
            <meta-data
 | 
			
		||||
                android:name="android.nfc.action.TECH_DISCOVERED"
 | 
			
		||||
                android:resource="@xml/nfc_tech_filter" />
 | 
			
		||||
        </activity>
 | 
			
		||||
 | 
			
		||||
        <service android:name="org.yuzu.yuzu_emu.utils.ForegroundService"/>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -123,6 +123,18 @@ public final class NativeLibrary {
 | 
			
		||||
    public static native boolean onGamePadMotionEvent(int Device, long delta_timestamp, float gyro_x, float gyro_y,
 | 
			
		||||
                                                      float gyro_z, float accel_x, float accel_y, float accel_z);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Signals and load a nfc tag
 | 
			
		||||
     *
 | 
			
		||||
     * @param data         Byte array containing all the data from a nfc tag
 | 
			
		||||
     */
 | 
			
		||||
    public static native boolean onReadNfcTag(byte[] data);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Removes current loaded nfc tag
 | 
			
		||||
     */
 | 
			
		||||
    public static native boolean onRemoveNfcTag();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Handles touch press events.
 | 
			
		||||
     *
 | 
			
		||||
 
 | 
			
		||||
@@ -24,6 +24,7 @@ import org.yuzu.yuzu_emu.features.settings.model.Settings
 | 
			
		||||
import org.yuzu.yuzu_emu.fragments.EmulationFragment
 | 
			
		||||
import org.yuzu.yuzu_emu.model.Game
 | 
			
		||||
import org.yuzu.yuzu_emu.utils.ControllerMappingHelper
 | 
			
		||||
import org.yuzu.yuzu_emu.utils.NfcReader
 | 
			
		||||
import org.yuzu.yuzu_emu.utils.SerializableHelper.parcelable
 | 
			
		||||
import org.yuzu.yuzu_emu.utils.ThemeHelper
 | 
			
		||||
import kotlin.math.roundToInt
 | 
			
		||||
@@ -37,6 +38,7 @@ open class EmulationActivity : AppCompatActivity() {
 | 
			
		||||
    var isActivityRecreated = false
 | 
			
		||||
    private var menuVisible = false
 | 
			
		||||
    private var emulationFragment: EmulationFragment? = null
 | 
			
		||||
    private lateinit var nfcReader: NfcReader
 | 
			
		||||
 | 
			
		||||
    private lateinit var game: Game
 | 
			
		||||
 | 
			
		||||
@@ -76,6 +78,9 @@ open class EmulationActivity : AppCompatActivity() {
 | 
			
		||||
        }
 | 
			
		||||
        title = game.title
 | 
			
		||||
 | 
			
		||||
        nfcReader = NfcReader(this)
 | 
			
		||||
        nfcReader.initialize()
 | 
			
		||||
 | 
			
		||||
        // Start a foreground service to prevent the app from getting killed in the background
 | 
			
		||||
        // TODO(bunnei): Disable notifications until we support app suspension.
 | 
			
		||||
        //foregroundService = new Intent(EmulationActivity.this, ForegroundService.class);
 | 
			
		||||
@@ -104,6 +109,21 @@ open class EmulationActivity : AppCompatActivity() {
 | 
			
		||||
        }
 | 
			
		||||
        return super.onKeyDown(keyCode, event)
 | 
			
		||||
    }
 | 
			
		||||
    override fun onResume() {
 | 
			
		||||
        super.onResume()
 | 
			
		||||
        nfcReader.startScanning()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onPause() {
 | 
			
		||||
        super.onPause()
 | 
			
		||||
        nfcReader.stopScanning()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onNewIntent(intent: Intent) {
 | 
			
		||||
        super.onNewIntent(intent)
 | 
			
		||||
        setIntent(intent)
 | 
			
		||||
        nfcReader.onNewIntent(intent)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onSaveInstanceState(outState: Bundle) {
 | 
			
		||||
        outState.putParcelable(EXTRA_SELECTED_GAME, game)
 | 
			
		||||
 
 | 
			
		||||
@@ -112,6 +112,7 @@ class MainActivity : AppCompatActivity(), MainView {
 | 
			
		||||
        when (request) {
 | 
			
		||||
            MainPresenter.REQUEST_ADD_DIRECTORY -> getGamesDirectory.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data)
 | 
			
		||||
            MainPresenter.REQUEST_INSTALL_KEYS -> getProdKey.launch(arrayOf("*/*"))
 | 
			
		||||
            MainPresenter.REQUEST_INSTALL_AMIIBO_KEYS -> getAmiiboKey.launch(arrayOf("*/*"))
 | 
			
		||||
            MainPresenter.REQUEST_SELECT_GPU_DRIVER -> {
 | 
			
		||||
                // Get the driver name for the dialog message.
 | 
			
		||||
                var driverName = GpuDriverHelper.customDriverName
 | 
			
		||||
@@ -221,6 +222,37 @@ class MainActivity : AppCompatActivity(), MainView {
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    private val getAmiiboKey =
 | 
			
		||||
        registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
 | 
			
		||||
            if (result == null)
 | 
			
		||||
                return@registerForActivityResult
 | 
			
		||||
 | 
			
		||||
            val takeFlags =
 | 
			
		||||
                Intent.FLAG_GRANT_WRITE_URI_PERMISSION or Intent.FLAG_GRANT_READ_URI_PERMISSION
 | 
			
		||||
            contentResolver.takePersistableUriPermission(
 | 
			
		||||
                result,
 | 
			
		||||
                takeFlags
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
            val dstPath = DirectoryInitialization.userDirectory + "/keys/"
 | 
			
		||||
            if (FileUtil.copyUriToInternalStorage(this, result, dstPath, "key_retail.bin")) {
 | 
			
		||||
                if (NativeLibrary.ReloadKeys()) {
 | 
			
		||||
                    Toast.makeText(
 | 
			
		||||
                        this,
 | 
			
		||||
                        R.string.install_keys_success,
 | 
			
		||||
                        Toast.LENGTH_SHORT
 | 
			
		||||
                    ).show()
 | 
			
		||||
                    refreshFragment()
 | 
			
		||||
                } else {
 | 
			
		||||
                    Toast.makeText(
 | 
			
		||||
                        this,
 | 
			
		||||
                        R.string.install_amiibo_keys_failure,
 | 
			
		||||
                        Toast.LENGTH_LONG
 | 
			
		||||
                    ).show()
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    private val getDriver =
 | 
			
		||||
        registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
 | 
			
		||||
            if (result == null)
 | 
			
		||||
 
 | 
			
		||||
@@ -36,6 +36,10 @@ class MainPresenter(private val view: MainView) {
 | 
			
		||||
                launchFileListActivity(REQUEST_INSTALL_KEYS)
 | 
			
		||||
                return true
 | 
			
		||||
            }
 | 
			
		||||
            R.id.button_install_amiibo_keys -> {
 | 
			
		||||
                launchFileListActivity(REQUEST_INSTALL_AMIIBO_KEYS)
 | 
			
		||||
                return true
 | 
			
		||||
            }
 | 
			
		||||
            R.id.button_select_gpu_driver -> {
 | 
			
		||||
                launchFileListActivity(REQUEST_SELECT_GPU_DRIVER)
 | 
			
		||||
                return true
 | 
			
		||||
@@ -64,6 +68,7 @@ class MainPresenter(private val view: MainView) {
 | 
			
		||||
    companion object {
 | 
			
		||||
        const val REQUEST_ADD_DIRECTORY = 1
 | 
			
		||||
        const val REQUEST_INSTALL_KEYS = 2
 | 
			
		||||
        const val REQUEST_SELECT_GPU_DRIVER = 3
 | 
			
		||||
        const val REQUEST_INSTALL_AMIIBO_KEYS = 3
 | 
			
		||||
        const val REQUEST_SELECT_GPU_DRIVER = 4
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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()
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -2,6 +2,7 @@
 | 
			
		||||
 | 
			
		||||
#include "common/logging/log.h"
 | 
			
		||||
#include "input_common/drivers/touch_screen.h"
 | 
			
		||||
#include "input_common/drivers/virtual_amiibo.h"
 | 
			
		||||
#include "input_common/drivers/virtual_gamepad.h"
 | 
			
		||||
#include "input_common/main.h"
 | 
			
		||||
#include "jni/emu_window/emu_window.h"
 | 
			
		||||
@@ -36,7 +37,15 @@ void EmuWindow_Android::OnGamepadMotionEvent(int player_index, u64 delta_timesta
 | 
			
		||||
                                             float gyro_y, float gyro_z, float accel_x,
 | 
			
		||||
                                             float accel_y, float accel_z) {
 | 
			
		||||
    m_input_subsystem->GetVirtualGamepad()->SetMotionState(
 | 
			
		||||
        player_index, delta_timestamp, gyro_x, gyro_y, gyro_z, accel_x, accel_y, accel_z);
 | 
			
		||||
            player_index, delta_timestamp, gyro_x, gyro_y, gyro_z, accel_x, accel_y, accel_z);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void EmuWindow_Android::OnReadNfcTag(std::span<u8> data) {
 | 
			
		||||
    m_input_subsystem->GetVirtualAmiibo()->LoadAmiibo(data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void EmuWindow_Android::OnRemoveNfcTag() {
 | 
			
		||||
    m_input_subsystem->GetVirtualAmiibo()->CloseAmiibo();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EmuWindow_Android::EmuWindow_Android(InputCommon::InputSubsystem* input_subsystem,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <span>
 | 
			
		||||
 | 
			
		||||
#include "core/frontend/emu_window.h"
 | 
			
		||||
#include "core/frontend/graphics_context.h"
 | 
			
		||||
@@ -39,6 +40,8 @@ public:
 | 
			
		||||
    void OnGamepadJoystickEvent(int player_index, int stick_id, float x, float y);
 | 
			
		||||
    void OnGamepadMotionEvent(int player_index, u64 delta_timestamp, float gyro_x, float gyro_y,
 | 
			
		||||
                              float gyro_z, float accel_x, float accel_y, float accel_z);
 | 
			
		||||
    void OnReadNfcTag(std::span<u8> data);
 | 
			
		||||
    void OnRemoveNfcTag();
 | 
			
		||||
    void OnFrameDisplayed() override {}
 | 
			
		||||
 | 
			
		||||
    std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override {
 | 
			
		||||
 
 | 
			
		||||
@@ -451,6 +451,26 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadMotionEvent(
 | 
			
		||||
    return static_cast<jboolean>(true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onReadNfcTag(
 | 
			
		||||
        [[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jbyteArray j_data) {
 | 
			
		||||
    jboolean isCopy{false};
 | 
			
		||||
    std::span<u8> data(reinterpret_cast<u8 *>(env->GetByteArrayElements(j_data, &isCopy)),
 | 
			
		||||
                       static_cast<size_t>(env->GetArrayLength(j_data)));
 | 
			
		||||
 | 
			
		||||
    if (EmulationSession::GetInstance().IsRunning()) {
 | 
			
		||||
        EmulationSession::GetInstance().Window().OnReadNfcTag(data);
 | 
			
		||||
    }
 | 
			
		||||
    return static_cast<jboolean>(true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onRemoveNfcTag(
 | 
			
		||||
        [[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz) {
 | 
			
		||||
    if (EmulationSession::GetInstance().IsRunning()) {
 | 
			
		||||
        EmulationSession::GetInstance().Window().OnRemoveNfcTag();
 | 
			
		||||
    }
 | 
			
		||||
    return static_cast<jboolean>(true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchPressed([[maybe_unused]] JNIEnv* env,
 | 
			
		||||
                                                          [[maybe_unused]] jclass clazz, jint id,
 | 
			
		||||
                                                          jfloat x, jfloat y) {
 | 
			
		||||
 
 | 
			
		||||
@@ -32,7 +32,13 @@ JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadMoveEv
 | 
			
		||||
    JNIEnv* env, jclass clazz, jstring j_device, jint axis, jfloat x, jfloat y);
 | 
			
		||||
 | 
			
		||||
JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadAxisEvent(
 | 
			
		||||
    JNIEnv* env, jclass clazz, jstring j_device, jint axis_id, jfloat axis_val);
 | 
			
		||||
        JNIEnv* env, jclass clazz, jstring j_device, jint axis_id, jfloat axis_val);
 | 
			
		||||
 | 
			
		||||
JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onReadNfcTag(
 | 
			
		||||
        JNIEnv* env, jclass clazz, jbyteArray j_data);
 | 
			
		||||
 | 
			
		||||
JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onRemoveNfcTag(
 | 
			
		||||
        JNIEnv* env, jclass clazz);
 | 
			
		||||
 | 
			
		||||
JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchEvent(JNIEnv* env,
 | 
			
		||||
                                                                              jclass clazz,
 | 
			
		||||
 
 | 
			
		||||
@@ -22,6 +22,12 @@
 | 
			
		||||
                android:title="@string/install_keys"
 | 
			
		||||
                app:showAsAction="ifRoom" />
 | 
			
		||||
 | 
			
		||||
            <item
 | 
			
		||||
                android:id="@+id/button_install_amiibo_keys"
 | 
			
		||||
                android:icon="@drawable/ic_install"
 | 
			
		||||
                android:title="@string/install_amiibo_keys"
 | 
			
		||||
                app:showAsAction="ifRoom" />
 | 
			
		||||
 | 
			
		||||
            <item
 | 
			
		||||
                android:id="@+id/button_select_gpu_driver"
 | 
			
		||||
                android:icon="@drawable/ic_settings"
 | 
			
		||||
 
 | 
			
		||||
@@ -52,8 +52,10 @@
 | 
			
		||||
    <!-- Add Directory Screen-->
 | 
			
		||||
    <string name="select_game_folder">Select game folder</string>
 | 
			
		||||
    <string name="install_keys">Install keys</string>
 | 
			
		||||
    <string name="install_amiibo_keys">Install amiibo keys</string>
 | 
			
		||||
    <string name="install_keys_success">Keys successfully installed</string>
 | 
			
		||||
    <string name="install_keys_failure">Keys file (prod.keys) is invalid</string>
 | 
			
		||||
    <string name="install_amiibo_keys_failure">Keys file (key_retail.bin) is invalid</string>
 | 
			
		||||
 | 
			
		||||
    <!-- GPU driver installation -->
 | 
			
		||||
    <string name="select_gpu_driver">Select GPU driver</string>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										6
									
								
								src/android/app/src/main/res/xml/nfc_tech_filter.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								src/android/app/src/main/res/xml/nfc_tech_filter.xml
									
									
									
									
									
										Normal file
									
								
							@@ -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>
 | 
			
		||||
@@ -73,10 +73,7 @@ VirtualAmiibo::State VirtualAmiibo::GetCurrentState() const {
 | 
			
		||||
VirtualAmiibo::Info VirtualAmiibo::LoadAmiibo(const std::string& filename) {
 | 
			
		||||
    const Common::FS::IOFile nfc_file{filename, Common::FS::FileAccessMode::Read,
 | 
			
		||||
                                      Common::FS::FileType::BinaryFile};
 | 
			
		||||
 | 
			
		||||
    if (state != State::WaitingForAmiibo) {
 | 
			
		||||
        return Info::WrongDeviceState;
 | 
			
		||||
    }
 | 
			
		||||
    std::vector<u8> data{};
 | 
			
		||||
 | 
			
		||||
    if (!nfc_file.IsOpen()) {
 | 
			
		||||
        return Info::UnableToLoad;
 | 
			
		||||
@@ -101,7 +98,28 @@ VirtualAmiibo::Info VirtualAmiibo::LoadAmiibo(const std::string& filename) {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    file_path = filename;
 | 
			
		||||
    return LoadAmiibo(data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
VirtualAmiibo::Info VirtualAmiibo::LoadAmiibo(std::span<u8> data) {
 | 
			
		||||
    if (state != State::WaitingForAmiibo) {
 | 
			
		||||
        return Info::WrongDeviceState;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    switch (data.size_bytes()) {
 | 
			
		||||
        case AmiiboSize:
 | 
			
		||||
        case AmiiboSizeWithoutPassword:
 | 
			
		||||
            nfc_data.resize(AmiiboSize);
 | 
			
		||||
            break;
 | 
			
		||||
        case MifareSize:
 | 
			
		||||
            nfc_data.resize(MifareSize);
 | 
			
		||||
            break;
 | 
			
		||||
        default:
 | 
			
		||||
            return Info::NotAnAmiibo;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    state = State::AmiiboIsOpen;
 | 
			
		||||
    memcpy(nfc_data.data(),data.data(),data.size_bytes());
 | 
			
		||||
    SetNfc(identifier, {Common::Input::NfcState::NewAmiibo, nfc_data});
 | 
			
		||||
    return Info::Success;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,7 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <array>
 | 
			
		||||
#include <span>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
@@ -47,6 +48,7 @@ public:
 | 
			
		||||
    State GetCurrentState() const;
 | 
			
		||||
 | 
			
		||||
    Info LoadAmiibo(const std::string& amiibo_file);
 | 
			
		||||
    Info LoadAmiibo(std::span<u8> data);
 | 
			
		||||
    Info ReloadAmiibo();
 | 
			
		||||
    Info CloseAmiibo();
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user