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.INTERNET" /> | ||||||
|     <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> |     <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> | ||||||
|  |     <uses-permission android:name="android.permission.NFC" /> | ||||||
|  |  | ||||||
|     <application |     <application | ||||||
|         android:name="org.yuzu.yuzu_emu.YuzuApplication" |         android:name="org.yuzu.yuzu_emu.YuzuApplication" | ||||||
| @@ -48,7 +49,19 @@ | |||||||
|             android:name="org.yuzu.yuzu_emu.activities.EmulationActivity" |             android:name="org.yuzu.yuzu_emu.activities.EmulationActivity" | ||||||
|             android:theme="@style/Theme.Yuzu.Main" |             android:theme="@style/Theme.Yuzu.Main" | ||||||
|             android:launchMode="singleTop" |             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"/> |         <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, |     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); |                                                       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. |      * 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.fragments.EmulationFragment | ||||||
| import org.yuzu.yuzu_emu.model.Game | import org.yuzu.yuzu_emu.model.Game | ||||||
| import org.yuzu.yuzu_emu.utils.ControllerMappingHelper | 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.SerializableHelper.parcelable | ||||||
| import org.yuzu.yuzu_emu.utils.ThemeHelper | import org.yuzu.yuzu_emu.utils.ThemeHelper | ||||||
| import kotlin.math.roundToInt | import kotlin.math.roundToInt | ||||||
| @@ -37,6 +38,7 @@ open class EmulationActivity : AppCompatActivity() { | |||||||
|     var isActivityRecreated = false |     var isActivityRecreated = false | ||||||
|     private var menuVisible = false |     private var menuVisible = false | ||||||
|     private var emulationFragment: EmulationFragment? = null |     private var emulationFragment: EmulationFragment? = null | ||||||
|  |     private lateinit var nfcReader: NfcReader | ||||||
|  |  | ||||||
|     private lateinit var game: Game |     private lateinit var game: Game | ||||||
|  |  | ||||||
| @@ -76,6 +78,9 @@ open class EmulationActivity : AppCompatActivity() { | |||||||
|         } |         } | ||||||
|         title = game.title |         title = game.title | ||||||
|  |  | ||||||
|  |         nfcReader = NfcReader(this) | ||||||
|  |         nfcReader.initialize() | ||||||
|  |  | ||||||
|         // Start a foreground service to prevent the app from getting killed in the background |         // Start a foreground service to prevent the app from getting killed in the background | ||||||
|         // TODO(bunnei): Disable notifications until we support app suspension. |         // TODO(bunnei): Disable notifications until we support app suspension. | ||||||
|         //foregroundService = new Intent(EmulationActivity.this, ForegroundService.class); |         //foregroundService = new Intent(EmulationActivity.this, ForegroundService.class); | ||||||
| @@ -104,6 +109,21 @@ open class EmulationActivity : AppCompatActivity() { | |||||||
|         } |         } | ||||||
|         return super.onKeyDown(keyCode, event) |         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) { |     override fun onSaveInstanceState(outState: Bundle) { | ||||||
|         outState.putParcelable(EXTRA_SELECTED_GAME, game) |         outState.putParcelable(EXTRA_SELECTED_GAME, game) | ||||||
|   | |||||||
| @@ -112,6 +112,7 @@ class MainActivity : AppCompatActivity(), MainView { | |||||||
|         when (request) { |         when (request) { | ||||||
|             MainPresenter.REQUEST_ADD_DIRECTORY -> getGamesDirectory.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data) |             MainPresenter.REQUEST_ADD_DIRECTORY -> getGamesDirectory.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data) | ||||||
|             MainPresenter.REQUEST_INSTALL_KEYS -> getProdKey.launch(arrayOf("*/*")) |             MainPresenter.REQUEST_INSTALL_KEYS -> getProdKey.launch(arrayOf("*/*")) | ||||||
|  |             MainPresenter.REQUEST_INSTALL_AMIIBO_KEYS -> getAmiiboKey.launch(arrayOf("*/*")) | ||||||
|             MainPresenter.REQUEST_SELECT_GPU_DRIVER -> { |             MainPresenter.REQUEST_SELECT_GPU_DRIVER -> { | ||||||
|                 // Get the driver name for the dialog message. |                 // Get the driver name for the dialog message. | ||||||
|                 var driverName = GpuDriverHelper.customDriverName |                 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 = |     private val getDriver = | ||||||
|         registerForActivityResult(ActivityResultContracts.OpenDocument()) { result -> |         registerForActivityResult(ActivityResultContracts.OpenDocument()) { result -> | ||||||
|             if (result == null) |             if (result == null) | ||||||
|   | |||||||
| @@ -36,6 +36,10 @@ class MainPresenter(private val view: MainView) { | |||||||
|                 launchFileListActivity(REQUEST_INSTALL_KEYS) |                 launchFileListActivity(REQUEST_INSTALL_KEYS) | ||||||
|                 return true |                 return true | ||||||
|             } |             } | ||||||
|  |             R.id.button_install_amiibo_keys -> { | ||||||
|  |                 launchFileListActivity(REQUEST_INSTALL_AMIIBO_KEYS) | ||||||
|  |                 return true | ||||||
|  |             } | ||||||
|             R.id.button_select_gpu_driver -> { |             R.id.button_select_gpu_driver -> { | ||||||
|                 launchFileListActivity(REQUEST_SELECT_GPU_DRIVER) |                 launchFileListActivity(REQUEST_SELECT_GPU_DRIVER) | ||||||
|                 return true |                 return true | ||||||
| @@ -64,6 +68,7 @@ class MainPresenter(private val view: MainView) { | |||||||
|     companion object { |     companion object { | ||||||
|         const val REQUEST_ADD_DIRECTORY = 1 |         const val REQUEST_ADD_DIRECTORY = 1 | ||||||
|         const val REQUEST_INSTALL_KEYS = 2 |         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 "common/logging/log.h" | ||||||
| #include "input_common/drivers/touch_screen.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/drivers/virtual_gamepad.h" | ||||||
| #include "input_common/main.h" | #include "input_common/main.h" | ||||||
| #include "jni/emu_window/emu_window.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 gyro_y, float gyro_z, float accel_x, | ||||||
|                                              float accel_y, float accel_z) { |                                              float accel_y, float accel_z) { | ||||||
|     m_input_subsystem->GetVirtualGamepad()->SetMotionState( |     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, | EmuWindow_Android::EmuWindow_Android(InputCommon::InputSubsystem* input_subsystem, | ||||||
|   | |||||||
| @@ -1,6 +1,7 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include <memory> | #include <memory> | ||||||
|  | #include <span> | ||||||
|  |  | ||||||
| #include "core/frontend/emu_window.h" | #include "core/frontend/emu_window.h" | ||||||
| #include "core/frontend/graphics_context.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 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, |     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); |                               float gyro_z, float accel_x, float accel_y, float accel_z); | ||||||
|  |     void OnReadNfcTag(std::span<u8> data); | ||||||
|  |     void OnRemoveNfcTag(); | ||||||
|     void OnFrameDisplayed() override {} |     void OnFrameDisplayed() override {} | ||||||
|  |  | ||||||
|     std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const 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); |     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, | void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchPressed([[maybe_unused]] JNIEnv* env, | ||||||
|                                                           [[maybe_unused]] jclass clazz, jint id, |                                                           [[maybe_unused]] jclass clazz, jint id, | ||||||
|                                                           jfloat x, jfloat y) { |                                                           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); |     JNIEnv* env, jclass clazz, jstring j_device, jint axis, jfloat x, jfloat y); | ||||||
|  |  | ||||||
| JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadAxisEvent( | 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, | JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchEvent(JNIEnv* env, | ||||||
|                                                                               jclass clazz, |                                                                               jclass clazz, | ||||||
|   | |||||||
| @@ -22,6 +22,12 @@ | |||||||
|                 android:title="@string/install_keys" |                 android:title="@string/install_keys" | ||||||
|                 app:showAsAction="ifRoom" /> |                 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 |             <item | ||||||
|                 android:id="@+id/button_select_gpu_driver" |                 android:id="@+id/button_select_gpu_driver" | ||||||
|                 android:icon="@drawable/ic_settings" |                 android:icon="@drawable/ic_settings" | ||||||
|   | |||||||
| @@ -52,8 +52,10 @@ | |||||||
|     <!-- Add Directory Screen--> |     <!-- Add Directory Screen--> | ||||||
|     <string name="select_game_folder">Select game folder</string> |     <string name="select_game_folder">Select game folder</string> | ||||||
|     <string name="install_keys">Install keys</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_success">Keys successfully installed</string> | ||||||
|     <string name="install_keys_failure">Keys file (prod.keys) is invalid</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 --> |     <!-- GPU driver installation --> | ||||||
|     <string name="select_gpu_driver">Select GPU driver</string> |     <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) { | VirtualAmiibo::Info VirtualAmiibo::LoadAmiibo(const std::string& filename) { | ||||||
|     const Common::FS::IOFile nfc_file{filename, Common::FS::FileAccessMode::Read, |     const Common::FS::IOFile nfc_file{filename, Common::FS::FileAccessMode::Read, | ||||||
|                                       Common::FS::FileType::BinaryFile}; |                                       Common::FS::FileType::BinaryFile}; | ||||||
|  |     std::vector<u8> data{}; | ||||||
|     if (state != State::WaitingForAmiibo) { |  | ||||||
|         return Info::WrongDeviceState; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (!nfc_file.IsOpen()) { |     if (!nfc_file.IsOpen()) { | ||||||
|         return Info::UnableToLoad; |         return Info::UnableToLoad; | ||||||
| @@ -101,7 +98,28 @@ VirtualAmiibo::Info VirtualAmiibo::LoadAmiibo(const std::string& filename) { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     file_path = 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; |     state = State::AmiiboIsOpen; | ||||||
|  |     memcpy(nfc_data.data(),data.data(),data.size_bytes()); | ||||||
|     SetNfc(identifier, {Common::Input::NfcState::NewAmiibo, nfc_data}); |     SetNfc(identifier, {Common::Input::NfcState::NewAmiibo, nfc_data}); | ||||||
|     return Info::Success; |     return Info::Success; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -4,6 +4,7 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include <array> | #include <array> | ||||||
|  | #include <span> | ||||||
| #include <string> | #include <string> | ||||||
| #include <vector> | #include <vector> | ||||||
|  |  | ||||||
| @@ -47,6 +48,7 @@ public: | |||||||
|     State GetCurrentState() const; |     State GetCurrentState() const; | ||||||
|  |  | ||||||
|     Info LoadAmiibo(const std::string& amiibo_file); |     Info LoadAmiibo(const std::string& amiibo_file); | ||||||
|  |     Info LoadAmiibo(std::span<u8> data); | ||||||
|     Info ReloadAmiibo(); |     Info ReloadAmiibo(); | ||||||
|     Info CloseAmiibo(); |     Info CloseAmiibo(); | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user