aboutsummaryrefslogtreecommitdiff
path: root/src/android
diff options
context:
space:
mode:
Diffstat (limited to 'src/android')
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt23
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameIconUtils.kt65
2 files changed, 82 insertions, 6 deletions
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt
index 64e035afbe..411b764e36 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt
@@ -31,6 +31,7 @@ object GameHelper {
fun getGames(): List<Game> {
val games = mutableListOf<Game>()
+ val gamesByProgramId = mutableMapOf<String, Game>()
val context = YuzuApplication.appContext
preferences = PreferenceManager.getDefaultSharedPreferences(context)
@@ -63,6 +64,7 @@ object GameHelper {
addGamesRecursive(
games,
+ gamesByProgramId,
FileUtil.listFiles(gameDirUri),
scanDepth,
mountedContainerUris
@@ -136,6 +138,7 @@ object GameHelper {
private fun addGamesRecursive(
games: MutableList<Game>,
+ gamesByProgramId: MutableMap<String, Game>,
files: Array<MinimalDocumentFile>,
depth: Int,
mountedContainerUris: MutableSet<String>
@@ -148,6 +151,7 @@ object GameHelper {
if (it.isDirectory) {
addGamesRecursive(
games,
+ gamesByProgramId,
FileUtil.listFiles(it.uri),
depth - 1,
mountedContainerUris
@@ -156,8 +160,9 @@ object GameHelper {
val extension = FileUtil.getExtension(it.uri).lowercase()
val filePath = it.uri.toString()
- if (externalContentExtensions.contains(extension) &&
- mountedContainerUris.add(filePath)) {
+ val mountedContainer = externalContentExtensions.contains(extension) &&
+ mountedContainerUris.add(filePath)
+ if (mountedContainer) {
NativeLibrary.addGameFolderFileToFilesystemProvider(filePath)
}
@@ -165,6 +170,20 @@ object GameHelper {
val game = getGame(it.uri, true, false)
if (game != null) {
games.add(game)
+ if (game.programId != "0") {
+ gamesByProgramId[game.programId] = game
+ }
+ } else if (mountedContainer) {
+ GameMetadata.getProgramId(filePath).toLongOrNull()?.let { programId ->
+ gamesByProgramId[(programId and 0x800L.inv()).toString()]
+ }?.let { existingGame ->
+ NativeLibrary.getPatchesForFile(existingGame.path, existingGame.programId)
+ existingGame.version = GameMetadata.getVersion(
+ existingGame.path,
+ true
+ )
+ GameIconUtils.refreshGameIcon(existingGame)
+ }
}
}
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameIconUtils.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameIconUtils.kt
index d05020560a..efe68a4ecf 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameIconUtils.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameIconUtils.kt
@@ -1,3 +1,6 @@
+// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -24,6 +27,15 @@ import coil.request.Options
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.model.Game
+import java.util.Collections
+import java.util.WeakHashMap
+
+private val gameIconHashes = Collections.synchronizedMap(mutableMapOf<String, Int>())
+private val gameIconTargets = Collections.synchronizedMap(WeakHashMap<ImageView, GameIconTarget>())
+
+private fun Game.iconCacheKey(): String = "$path|$version"
+
+private data class GameIconTarget(val game: Game, var iconHash: Int? = null)
class GameIconFetcher(
private val game: Game,
@@ -31,14 +43,15 @@ class GameIconFetcher(
) : Fetcher {
override suspend fun fetch(): FetchResult {
return DrawableResult(
- drawable = decodeGameIcon(game.path)!!.toDrawable(options.context.resources),
+ drawable = decodeGameIcon(game)!!.toDrawable(options.context.resources),
isSampled = false,
dataSource = DataSource.DISK
)
}
- private fun decodeGameIcon(uri: String): Bitmap? {
- val data = GameMetadata.getIcon(uri)
+ private fun decodeGameIcon(game: Game): Bitmap? {
+ val data = GameMetadata.getIcon(game.path)
+ gameIconHashes[game.iconCacheKey()] = data.contentHashCode()
return BitmapFactory.decodeByteArray(
data,
0,
@@ -54,7 +67,7 @@ class GameIconFetcher(
}
class GameIconKeyer : Keyer<Game> {
- override fun key(data: Game, options: Options): String = data.path
+ override fun key(data: Game, options: Options): String = data.iconCacheKey()
}
object GameIconUtils {
@@ -71,14 +84,58 @@ object GameIconUtils {
.build()
fun loadGameIcon(game: Game, imageView: ImageView) {
+ gameIconTargets[imageView] = GameIconTarget(game)
val request = ImageRequest.Builder(YuzuApplication.appContext)
.data(game)
.target(imageView)
.error(R.drawable.default_icon)
+ .listener(
+ onSuccess = { _, _ ->
+ val target = gameIconTargets[imageView]
+ if (target?.game?.iconCacheKey() == game.iconCacheKey()) {
+ gameIconHashes[game.iconCacheKey()]?.let {
+ target.iconHash = it
+ }
+ }
+ },
+ onError = { _, _ ->
+ gameIconTargets[imageView]?.iconHash = null
+ }
+ )
.build()
imageLoader.enqueue(request)
}
+ fun refreshGameIcon(game: Game) {
+ val targets = synchronized(gameIconTargets) {
+ gameIconTargets
+ .filterValues { it.game.path == game.path && it.game.programId == game.programId }
+ .keys
+ .toList()
+ }
+ if (targets.isEmpty()) {
+ return
+ }
+
+ val iconHash = GameMetadata.getIcon(game.path).contentHashCode()
+ val targetsToRefresh = targets.filter { gameIconTargets[it]?.iconHash != iconHash }
+ if (targetsToRefresh.isEmpty()) {
+ return
+ }
+
+ imageLoader.memoryCache?.remove(MemoryCache.Key(game.iconCacheKey()))
+ targetsToRefresh.forEach { imageView ->
+ imageView.post {
+ val target = gameIconTargets[imageView] ?: return@post
+ if (target.game.path == game.path && target.game.programId == game.programId) {
+ if (target.iconHash != iconHash) {
+ loadGameIcon(game, imageView)
+ }
+ }
+ }
+ }
+ }
+
suspend fun getGameIcon(lifecycleOwner: LifecycleOwner, game: Game): Bitmap {
val request = ImageRequest.Builder(YuzuApplication.appContext)
.data(game)