mirror of
https://git.eden-emu.dev/eden-emu/eden
synced 2026-05-24 00:27:01 +02:00
[Android] Fix Amiibo bug (#2847)
Co-authored-by: Ribbit <ribbit@placeholder.com> Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2847 Reviewed-by: crueter <crueter@eden-emu.dev> Co-authored-by: Ribbit <ribbit@eden-emu.dev> Co-committed-by: Ribbit <ribbit@eden-emu.dev>
This commit is contained in:
parent
0be29d2947
commit
dc907616a9
1 changed files with 73 additions and 19 deletions
|
|
@ -56,6 +56,12 @@ import androidx.window.layout.WindowLayoutInfo
|
||||||
import com.google.android.material.color.MaterialColors
|
import com.google.android.material.color.MaterialColors
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import com.google.android.material.textview.MaterialTextView
|
import com.google.android.material.textview.MaterialTextView
|
||||||
|
import kotlinx.coroutines.CancellationException
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.ensureActive
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import org.yuzu.yuzu_emu.HomeNavigationDirections
|
import org.yuzu.yuzu_emu.HomeNavigationDirections
|
||||||
import org.yuzu.yuzu_emu.NativeLibrary
|
import org.yuzu.yuzu_emu.NativeLibrary
|
||||||
import org.yuzu.yuzu_emu.R
|
import org.yuzu.yuzu_emu.R
|
||||||
|
|
@ -85,12 +91,11 @@ import org.yuzu.yuzu_emu.utils.ViewUtils
|
||||||
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
|
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
|
||||||
import org.yuzu.yuzu_emu.utils.collect
|
import org.yuzu.yuzu_emu.utils.collect
|
||||||
import org.yuzu.yuzu_emu.utils.CustomSettingsHandler
|
import org.yuzu.yuzu_emu.utils.CustomSettingsHandler
|
||||||
import kotlinx.coroutines.launch
|
import java.io.ByteArrayOutputStream
|
||||||
import kotlinx.coroutines.withContext
|
import java.io.File
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlin.coroutines.coroutineContext
|
||||||
import kotlin.coroutines.resume
|
import kotlin.coroutines.resume
|
||||||
import kotlin.coroutines.suspendCoroutine
|
import kotlin.coroutines.suspendCoroutine
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||||
private lateinit var emulationState: EmulationState
|
private lateinit var emulationState: EmulationState
|
||||||
|
|
@ -125,6 +130,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||||
private var perfStatsRunnable: Runnable? = null
|
private var perfStatsRunnable: Runnable? = null
|
||||||
private var socRunnable: Runnable? = null
|
private var socRunnable: Runnable? = null
|
||||||
private var isAmiiboPickerOpen = false
|
private var isAmiiboPickerOpen = false
|
||||||
|
private var amiiboLoadJob: Job? = null
|
||||||
|
|
||||||
private val loadAmiiboLauncher =
|
private val loadAmiiboLauncher =
|
||||||
registerForActivityResult(ActivityResultContracts.OpenDocument()) { uri ->
|
registerForActivityResult(ActivityResultContracts.OpenDocument()) { uri ->
|
||||||
|
|
@ -136,26 +142,46 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||||
return@registerForActivityResult
|
return@registerForActivityResult
|
||||||
}
|
}
|
||||||
|
|
||||||
val data = try {
|
if (!NativeLibrary.isRunning()) {
|
||||||
requireContext().contentResolver.openInputStream(uri)?.use { it.readBytes() }
|
showAmiiboDialog(R.string.amiibo_wrong_state)
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.error("[EmulationFragment] Failed to read amiibo: ${e.message}")
|
|
||||||
showAmiiboDialog(R.string.amiibo_unknown_error)
|
|
||||||
return@registerForActivityResult
|
return@registerForActivityResult
|
||||||
}
|
}
|
||||||
|
|
||||||
val amiiboData = data ?: run {
|
amiiboLoadJob?.cancel()
|
||||||
showAmiiboDialog(R.string.amiibo_not_valid)
|
val owner = viewLifecycleOwner
|
||||||
return@registerForActivityResult
|
amiiboLoadJob = owner.lifecycleScope.launch {
|
||||||
}
|
val bytes = readAmiiboFile(uri)
|
||||||
|
|
||||||
if (amiiboData.isEmpty()) {
|
if (!isAdded) {
|
||||||
showAmiiboDialog(R.string.amiibo_not_valid)
|
return@launch
|
||||||
return@registerForActivityResult
|
}
|
||||||
}
|
|
||||||
|
|
||||||
val result = NativeLibrary.loadAmiibo(amiiboData)
|
if (bytes == null || bytes.isEmpty()) {
|
||||||
handleAmiiboLoadResult(result)
|
showAmiiboDialog(
|
||||||
|
if (bytes == null) R.string.amiibo_unknown_error else R.string.amiibo_not_valid
|
||||||
|
)
|
||||||
|
return@launch
|
||||||
|
}
|
||||||
|
|
||||||
|
val result = withContext(Dispatchers.IO) {
|
||||||
|
if (NativeLibrary.isRunning()) {
|
||||||
|
NativeLibrary.loadAmiibo(bytes)
|
||||||
|
} else {
|
||||||
|
AmiiboLoadResult.WrongDeviceState.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isAdded) {
|
||||||
|
return@launch
|
||||||
|
}
|
||||||
|
handleAmiiboLoadResult(result)
|
||||||
|
}.also { job ->
|
||||||
|
job.invokeOnCompletion {
|
||||||
|
if (amiiboLoadJob == job) {
|
||||||
|
amiiboLoadJob = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onAttach(context: Context) {
|
override fun onAttach(context: Context) {
|
||||||
|
|
@ -943,6 +969,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||||
|
|
||||||
when (AmiiboState.fromValue(NativeLibrary.getVirtualAmiiboState())) {
|
when (AmiiboState.fromValue(NativeLibrary.getVirtualAmiiboState())) {
|
||||||
AmiiboState.TagNearby -> {
|
AmiiboState.TagNearby -> {
|
||||||
|
amiiboLoadJob?.cancel()
|
||||||
NativeInput.onRemoveNfcTag()
|
NativeInput.onRemoveNfcTag()
|
||||||
showAmiiboDialog(R.string.amiibo_removed_message)
|
showAmiiboDialog(R.string.amiibo_removed_message)
|
||||||
}
|
}
|
||||||
|
|
@ -995,6 +1022,31 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private suspend fun readAmiiboFile(uri: Uri): ByteArray? =
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
val resolver = context?.contentResolver ?: return@withContext null
|
||||||
|
try {
|
||||||
|
resolver.openInputStream(uri)?.use { stream ->
|
||||||
|
val buffer = ByteArrayOutputStream()
|
||||||
|
val chunk = ByteArray(DEFAULT_BUFFER_SIZE)
|
||||||
|
while (true) {
|
||||||
|
coroutineContext.ensureActive()
|
||||||
|
val read = stream.read(chunk)
|
||||||
|
if (read == -1) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
buffer.write(chunk, 0, read)
|
||||||
|
}
|
||||||
|
buffer.toByteArray()
|
||||||
|
}
|
||||||
|
} catch (ce: CancellationException) {
|
||||||
|
throw ce
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.error("[EmulationFragment] Failed to read amiibo: ${e.message}")
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onPause() {
|
override fun onPause() {
|
||||||
if (this::emulationState.isInitialized) {
|
if (this::emulationState.isInitialized) {
|
||||||
if (emulationState.isRunning && emulationActivity?.isInPictureInPictureMode != true) {
|
if (emulationState.isRunning && emulationActivity?.isInPictureInPictureMode != true) {
|
||||||
|
|
@ -1007,6 +1059,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
|
amiiboLoadJob?.cancel()
|
||||||
|
amiiboLoadJob = null
|
||||||
_binding = null
|
_binding = null
|
||||||
isAmiiboPickerOpen = false
|
isAmiiboPickerOpen = false
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue