mirror of
https://git.eden-emu.dev/eden-emu/eden
synced 2026-04-10 03:18:55 +02:00
[android,addons] after a crash, launch button will wait for reloadGames to complete, and system will initialize after global config for proper firmware mounting (#3803)
This fixes two problems: 1. After a crash, it was possible to launch a game before external content gets mounted. Now the button will wait for it to complete. 2. Directory initialization was init system before init globalconfig, so after a crash firmware was not being remounted (have you ever noticed fw version = N/A in device overlay, after fiddling with applets?) (this had been fixed in 3755, which was not thoroughly tested by cocoon dev) Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3803 Reviewed-by: Lizzie <lizzie@eden-emu.dev> Reviewed-by: CamilleLaVey <camillelavey99@gmail.com> Co-authored-by: xbzk <xbzk@eden-emu.dev> Co-committed-by: xbzk <xbzk@eden-emu.dev>
This commit is contained in:
parent
c0fbb2526d
commit
81a344f3db
4 changed files with 128 additions and 53 deletions
|
|
@ -36,6 +36,7 @@ import org.yuzu.yuzu_emu.databinding.FragmentGamePropertiesBinding
|
||||||
import org.yuzu.yuzu_emu.features.DocumentProvider
|
import org.yuzu.yuzu_emu.features.DocumentProvider
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.Settings
|
import org.yuzu.yuzu_emu.features.settings.model.Settings
|
||||||
import org.yuzu.yuzu_emu.features.settings.ui.SettingsSubscreen
|
import org.yuzu.yuzu_emu.features.settings.ui.SettingsSubscreen
|
||||||
|
import org.yuzu.yuzu_emu.model.AddonViewModel
|
||||||
import org.yuzu.yuzu_emu.model.DriverViewModel
|
import org.yuzu.yuzu_emu.model.DriverViewModel
|
||||||
import org.yuzu.yuzu_emu.model.GameProperty
|
import org.yuzu.yuzu_emu.model.GameProperty
|
||||||
import org.yuzu.yuzu_emu.model.GamesViewModel
|
import org.yuzu.yuzu_emu.model.GamesViewModel
|
||||||
|
|
@ -46,6 +47,7 @@ import org.yuzu.yuzu_emu.model.SubmenuProperty
|
||||||
import org.yuzu.yuzu_emu.model.TaskState
|
import org.yuzu.yuzu_emu.model.TaskState
|
||||||
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
|
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
|
||||||
import org.yuzu.yuzu_emu.utils.FileUtil
|
import org.yuzu.yuzu_emu.utils.FileUtil
|
||||||
|
import org.yuzu.yuzu_emu.utils.GameHelper
|
||||||
import org.yuzu.yuzu_emu.utils.GameIconUtils
|
import org.yuzu.yuzu_emu.utils.GameIconUtils
|
||||||
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
|
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
|
||||||
import org.yuzu.yuzu_emu.utils.MemoryUtil
|
import org.yuzu.yuzu_emu.utils.MemoryUtil
|
||||||
|
|
@ -61,6 +63,7 @@ class GamePropertiesFragment : Fragment() {
|
||||||
|
|
||||||
private val homeViewModel: HomeViewModel by activityViewModels()
|
private val homeViewModel: HomeViewModel by activityViewModels()
|
||||||
private val gamesViewModel: GamesViewModel by activityViewModels()
|
private val gamesViewModel: GamesViewModel by activityViewModels()
|
||||||
|
private val addonViewModel: AddonViewModel by activityViewModels()
|
||||||
private val driverViewModel: DriverViewModel by activityViewModels()
|
private val driverViewModel: DriverViewModel by activityViewModels()
|
||||||
|
|
||||||
private val args by navArgs<GamePropertiesFragmentArgs>()
|
private val args by navArgs<GamePropertiesFragmentArgs>()
|
||||||
|
|
@ -118,6 +121,20 @@ class GamePropertiesFragment : Fragment() {
|
||||||
.show(childFragmentManager, LaunchGameDialogFragment.TAG)
|
.show(childFragmentManager, LaunchGameDialogFragment.TAG)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (GameHelper.cachedGameList.isEmpty()) {
|
||||||
|
binding.buttonStart.isEnabled = false
|
||||||
|
viewLifecycleOwner.lifecycleScope.launch {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
GameHelper.restoreContentForGame(args.game)
|
||||||
|
}
|
||||||
|
if (_binding == null) {
|
||||||
|
return@launch
|
||||||
|
}
|
||||||
|
addonViewModel.onAddonsViewStarted(args.game)
|
||||||
|
binding.buttonStart.isEnabled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
reloadList()
|
reloadList()
|
||||||
|
|
||||||
homeViewModel.openImportSaves.collect(
|
homeViewModel.openImportSaves.collect(
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,7 @@ class GamesViewModel : ViewModel() {
|
||||||
|
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
|
try {
|
||||||
if (firstStartup) {
|
if (firstStartup) {
|
||||||
// Retrieve list of cached games
|
// Retrieve list of cached games
|
||||||
val storedGames =
|
val storedGames =
|
||||||
|
|
@ -130,13 +131,15 @@ class GamesViewModel : ViewModel() {
|
||||||
}
|
}
|
||||||
|
|
||||||
setGames(GameHelper.getGames())
|
setGames(GameHelper.getGames())
|
||||||
reloading.set(false)
|
|
||||||
_isReloading.value = false
|
|
||||||
_shouldScrollAfterReload.value = true
|
_shouldScrollAfterReload.value = true
|
||||||
|
|
||||||
if (directoriesChanged) {
|
if (directoriesChanged) {
|
||||||
setShouldSwapData(true)
|
setShouldSwapData(true)
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
reloading.set(false)
|
||||||
|
_isReloading.value = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,8 +23,8 @@ object DirectoryInitialization {
|
||||||
fun start() {
|
fun start() {
|
||||||
if (!areDirectoriesReady) {
|
if (!areDirectoriesReady) {
|
||||||
initializeInternalStorage()
|
initializeInternalStorage()
|
||||||
NativeLibrary.initializeSystem(false)
|
|
||||||
NativeConfig.initializeGlobalConfig()
|
NativeConfig.initializeGlobalConfig()
|
||||||
|
NativeLibrary.initializeSystem(false)
|
||||||
NativeLibrary.reloadProfiles()
|
NativeLibrary.reloadProfiles()
|
||||||
migrateSettings()
|
migrateSettings()
|
||||||
areDirectoriesReady = true
|
areDirectoriesReady = true
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,11 @@ package org.yuzu.yuzu_emu.utils
|
||||||
|
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import android.provider.DocumentsContract
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import kotlinx.serialization.encodeToString
|
import kotlinx.serialization.encodeToString
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
|
import java.io.File
|
||||||
import org.yuzu.yuzu_emu.NativeLibrary
|
import org.yuzu.yuzu_emu.NativeLibrary
|
||||||
import org.yuzu.yuzu_emu.YuzuApplication
|
import org.yuzu.yuzu_emu.YuzuApplication
|
||||||
import org.yuzu.yuzu_emu.model.Game
|
import org.yuzu.yuzu_emu.model.Game
|
||||||
|
|
@ -49,29 +51,8 @@ object GameHelper {
|
||||||
// Remove previous filesystem provider information so we can get up to date version info
|
// Remove previous filesystem provider information so we can get up to date version info
|
||||||
NativeLibrary.clearFilesystemProvider()
|
NativeLibrary.clearFilesystemProvider()
|
||||||
|
|
||||||
// Scan External Content directories and register all NSP/XCI files
|
|
||||||
val externalContentDirs = NativeConfig.getExternalContentDirs()
|
|
||||||
val uniqueExternalContentDirs = linkedSetOf<String>()
|
|
||||||
externalContentDirs.forEach { externalDir ->
|
|
||||||
if (externalDir.isNotEmpty()) {
|
|
||||||
uniqueExternalContentDirs.add(externalDir)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val mountedContainerUris = mutableSetOf<String>()
|
val mountedContainerUris = mutableSetOf<String>()
|
||||||
for (externalDir in uniqueExternalContentDirs) {
|
mountExternalContentDirectories(mountedContainerUris)
|
||||||
if (externalDir.isNotEmpty()) {
|
|
||||||
val externalDirUri = externalDir.toUri()
|
|
||||||
if (FileUtil.isTreeUriValid(externalDirUri)) {
|
|
||||||
scanContentContainersRecursive(FileUtil.listFiles(externalDirUri), 3) {
|
|
||||||
val containerUri = it.uri.toString()
|
|
||||||
if (mountedContainerUris.add(containerUri)) {
|
|
||||||
NativeLibrary.addFileToFilesystemProvider(containerUri)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val badDirs = mutableListOf<Int>()
|
val badDirs = mutableListOf<Int>()
|
||||||
gameDirs.forEachIndexed { index: Int, gameDir: GameDir ->
|
gameDirs.forEachIndexed { index: Int, gameDir: GameDir ->
|
||||||
|
|
@ -115,6 +96,15 @@ object GameHelper {
|
||||||
return games.toList()
|
return games.toList()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun restoreContentForGame(game: Game) {
|
||||||
|
NativeLibrary.reloadKeys()
|
||||||
|
|
||||||
|
val mountedContainerUris = mutableSetOf<String>()
|
||||||
|
mountExternalContentDirectories(mountedContainerUris)
|
||||||
|
mountGameFolderContent(Uri.parse(game.path), mountedContainerUris)
|
||||||
|
NativeLibrary.addFileToFilesystemProvider(game.path)
|
||||||
|
}
|
||||||
|
|
||||||
// File extensions considered as external content, buuut should
|
// File extensions considered as external content, buuut should
|
||||||
// be done better imo.
|
// be done better imo.
|
||||||
private val externalContentExtensions = setOf("nsp", "xci")
|
private val externalContentExtensions = setOf("nsp", "xci")
|
||||||
|
|
@ -181,6 +171,71 @@ object GameHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun mountExternalContentDirectories(mountedContainerUris: MutableSet<String>) {
|
||||||
|
val uniqueExternalContentDirs = linkedSetOf<String>()
|
||||||
|
NativeConfig.getExternalContentDirs().forEach { externalDir ->
|
||||||
|
if (externalDir.isNotEmpty()) {
|
||||||
|
uniqueExternalContentDirs.add(externalDir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (externalDir in uniqueExternalContentDirs) {
|
||||||
|
val externalDirUri = externalDir.toUri()
|
||||||
|
if (FileUtil.isTreeUriValid(externalDirUri)) {
|
||||||
|
scanContentContainersRecursive(FileUtil.listFiles(externalDirUri), 3) {
|
||||||
|
val containerUri = it.uri.toString()
|
||||||
|
if (mountedContainerUris.add(containerUri)) {
|
||||||
|
NativeLibrary.addFileToFilesystemProvider(containerUri)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun mountGameFolderContent(gameUri: Uri, mountedContainerUris: MutableSet<String>) {
|
||||||
|
if (gameUri.scheme == "content") {
|
||||||
|
val parentUri = getParentDocumentUri(gameUri) ?: return
|
||||||
|
scanContentContainersRecursive(FileUtil.listFiles(parentUri), 1) {
|
||||||
|
val containerUri = it.uri.toString()
|
||||||
|
if (mountedContainerUris.add(containerUri)) {
|
||||||
|
NativeLibrary.addGameFolderFileToFilesystemProvider(containerUri)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val gameFile = File(gameUri.path ?: gameUri.toString())
|
||||||
|
val parentDir = gameFile.parentFile ?: return
|
||||||
|
parentDir.listFiles()?.forEach { sibling ->
|
||||||
|
if (!sibling.isFile) {
|
||||||
|
return@forEach
|
||||||
|
}
|
||||||
|
|
||||||
|
val extension = sibling.extension.lowercase()
|
||||||
|
if (externalContentExtensions.contains(extension)) {
|
||||||
|
val containerUri = Uri.fromFile(sibling).toString()
|
||||||
|
if (mountedContainerUris.add(containerUri)) {
|
||||||
|
NativeLibrary.addGameFolderFileToFilesystemProvider(containerUri)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getParentDocumentUri(uri: Uri): Uri? {
|
||||||
|
return try {
|
||||||
|
val documentId = DocumentsContract.getDocumentId(uri)
|
||||||
|
val separatorIndex = documentId.lastIndexOf('/')
|
||||||
|
if (separatorIndex == -1) {
|
||||||
|
null
|
||||||
|
} else {
|
||||||
|
val parentDocumentId = documentId.substring(0, separatorIndex)
|
||||||
|
DocumentsContract.buildDocumentUriUsingTree(uri, parentDocumentId)
|
||||||
|
}
|
||||||
|
} catch (_: Exception) {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun getGame(
|
fun getGame(
|
||||||
uri: Uri,
|
uri: Uri,
|
||||||
addedToLibrary: Boolean,
|
addedToLibrary: Boolean,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue