android: Implement amiibo reading from nfc tag
This commit is contained in:
parent
b2aeb50229
commit
ca4be4283d
15 changed files with 327 additions and 8 deletions
|
@ -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();
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue