diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c7510e4..1dacf76 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -31,7 +31,7 @@ android:name=".MainActivity" android:exported="true" android:launchMode="singleTop" - android:configChanges="orientation|screenSize|screenLayout|smallestScreenSize|density" + android:configChanges="orientation|screenSize|screenLayout|smallestScreenSize|density|keyboard|keyboardHidden|navigation|uiMode" android:windowSoftInputMode="adjustResize"> diff --git a/app/src/main/java/com/lazy/emulate/data/CoverArtManager.kt b/app/src/main/java/com/lazy/emulate/data/CoverArtManager.kt index b68f12b..12983c3 100644 --- a/app/src/main/java/com/lazy/emulate/data/CoverArtManager.kt +++ b/app/src/main/java/com/lazy/emulate/data/CoverArtManager.kt @@ -31,13 +31,25 @@ class CoverArtManager(private val context: Context) { val dest = cacheFile(gameTitle, consoleType) - // Try the title as-is, with expanded regions, then fully cleaned + // Build the list of candidate filenames to try, in priority order: + // 1. Title as-is + // 2. Title with GoodTools-style region tags expanded into LibRetro forms + // (e.g. "(U)" -> "(USA)", "(USA, Europe)", "(World)") + // 3. Fully cleaned title (no parens at all) — rare hit + // 4. Cleaned title with each common LibRetro region tag appended — this catches + // cases like "Super Mario Bros. (U)" where LibRetro stores "(World)" but our + // region table didn't know that this specific game used the World tag. val namesToTry = buildList { add(gameTitle) val expanded = expandRegions(gameTitle) expanded.filter { it != gameTitle }.forEach { add(it) } val cleaned = cleanTitle(gameTitle) if (cleaned != gameTitle && cleaned !in expanded) add(cleaned) + // Append fallback region tags to the bare cleaned title + for (region in COMMON_LIBRETRO_REGIONS) { + val candidate = "$cleaned $region" + if (candidate !in this) add(candidate) + } } for (name in namesToTry) { @@ -45,10 +57,12 @@ class CoverArtManager(private val context: Context) { .replace("+", "%20") val url = "$BASE_URL/${consoleType.libretroThumbnailRepo}/master/Named_Boxarts/$encoded.png" if (downloadFile(url, dest)) { + Log.d(TAG, "Cover art hit for '$gameTitle' via '$name'") return dest.absolutePath } } + Log.d(TAG, "No cover art match for '$gameTitle' after ${namesToTry.size} attempts") return null } @@ -89,13 +103,14 @@ class CoverArtManager(private val context: Context) { private const val TAG = "CoverArtManager" private const val BASE_URL = "https://raw.githubusercontent.com/libretro-thumbnails" - // GoodNES (U) can mean (USA) or (USA, Europe) — try both + // GoodTools-style short region tags can map to several LibRetro long forms. + // Order matters — earlier entries are tried first. private val regionExpansions = mapOf( - "(U)" to listOf("(USA, Europe)", "(USA)"), - "(E)" to listOf("(Europe)", "(Europe, Australia)"), - "(J)" to listOf("(Japan)", "(Japan, USA)"), - "(UE)" to listOf("(USA, Europe)"), - "(JU)" to listOf("(Japan, USA)"), + "(U)" to listOf("(USA)", "(USA, Europe)", "(World)"), + "(E)" to listOf("(Europe)", "(Europe, Australia)", "(World)"), + "(J)" to listOf("(Japan)", "(Japan, USA)", "(World)"), + "(UE)" to listOf("(USA, Europe)", "(USA)", "(World)"), + "(JU)" to listOf("(Japan, USA)", "(USA)", "(Japan)"), "(W)" to listOf("(World)"), "(F)" to listOf("(France)"), "(G)" to listOf("(Germany)"), @@ -104,6 +119,19 @@ class CoverArtManager(private val context: Context) { "(Unl)" to listOf("(USA) (Unl)", "(Unl)"), ) + // Region tags to append to a cleaned (paren-stripped) title as a fallback for ROMs + // whose original filename had no region tag, an unrecognized region tag, or a tag + // that doesn't match what LibRetro stores for that specific game (e.g. SMB1 is + // "(World)" but most US dumps tag it "(U)"). + private val COMMON_LIBRETRO_REGIONS = listOf( + "(USA)", + "(World)", + "(USA, Europe)", + "(Europe)", + "(Japan)", + "(Japan, USA)", + ) + fun expandRegions(title: String): List { val stripped = title .replace(Regex("\\s*\\[[^]]*]"), "") // strip flags like [!], [b], [p1] diff --git a/app/src/main/java/com/lazy/emulate/ui/components/GameCard.kt b/app/src/main/java/com/lazy/emulate/ui/components/GameCard.kt index 8f3010e..ea79793 100644 --- a/app/src/main/java/com/lazy/emulate/ui/components/GameCard.kt +++ b/app/src/main/java/com/lazy/emulate/ui/components/GameCard.kt @@ -24,9 +24,12 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import coil.compose.AsyncImage +import coil.request.ImageRequest +import coil.size.Scale import com.lazy.emulate.data.model.Game import java.io.File @@ -55,8 +58,16 @@ fun GameCard( contentAlignment = Alignment.Center ) { if (game.coverArtPath != null) { + // Cap decode size — LibRetro thumbnail PNGs can be 1500x2000+, which + // burns ~12 MB of bitmap memory each. With 30 cards visible that's + // hundreds of MB and was OOM-ing the app during in-game input delivery. + // 384px is more than enough for a tile in a grid. AsyncImage( - model = File(game.coverArtPath), + model = ImageRequest.Builder(LocalContext.current) + .data(File(game.coverArtPath)) + .size(384) + .scale(Scale.FILL) + .build(), contentDescription = "${game.title} cover art", contentScale = ContentScale.Crop, modifier = Modifier.fillMaxSize()