From 44e4cfb785a3e9a657be4b8bcbbbe01294937ec2 Mon Sep 17 00:00:00 2001 From: xXJSONDeruloXx Date: Thu, 24 Sep 2026 22:52:37 -0400 Subject: [PATCH] Fix Steam library capsule artwork --- .../droiddeck/launcher/frontend/Library.kt | 44 +++++++++++++++++-- .../droiddeck/launcher/ui/FrontEndScreen.kt | 13 +++--- 2 files changed, 48 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/com/droiddeck/launcher/frontend/Library.kt b/app/src/main/java/com/droiddeck/launcher/frontend/Library.kt index e255d314b..500daeea3 100644 --- a/app/src/main/java/com/droiddeck/launcher/frontend/Library.kt +++ b/app/src/main/java/com/droiddeck/launcher/frontend/Library.kt @@ -26,7 +26,35 @@ object Library { } /** The client's own tools and runtimes live in steamapps beside the games; they are not titles. */ - private val NOT_GAMES = setOf(228980, 1493710, 3127680, 4183110, 4427310, 4185400) + private val NOT_GAMES = setOf( + 858280, // Proton 3.7 + 961940, // Proton 3.16 + 993090, // Lossless Scaling + 1054830, // Proton 4.2 + 1070560, // Steam Linux Runtime 1.0 + 1113280, // Proton 4.11 + 1245040, // Proton 5.0 + 1391110, // Steam Linux Runtime 2.0 + 1420170, // Proton 5.13 + 1493710, // Proton Experimental + 1580130, // Proton 6.3 + 1628350, // Steam Linux Runtime 3.0 + 1887720, // Proton 7 + 2180100, // Proton Hotfix + 228980, // Steamworks Common Redistributables + 2348590, // Proton 8 + 2805730, // Proton 9 + 3029110, // Lepton + 3127680, // FEX + 3658110, // Proton 10 + 4183110, // Steam Linux Runtime 4.0 + 4185400, // Steam Linux Runtime 4.0 for ARM64 + 4427310, // Proton Experimental for ARM64 + 4628710, // Proton 11 / Proton Next + 4628740, // Proton 11 for ARM64 + 4690330, // Legacy Steam Runtime + ) + private val STEAM_CAPSULES = listOf("library_capsule.jpg", "library_600x900.jpg") private val NAME = Regex("^\\s*\"name\"\\s*\"([^\"]*)\"", RegexOption.MULTILINE) private val STATE = Regex("^\\s*\"StateFlags\"\\s*\"(\\d+)\"", RegexOption.MULTILINE) @@ -49,15 +77,23 @@ object Library { val flags = STATE.find(text)?.groupValues?.get(1)?.toIntOrNull() ?: 0 // StateFlags 4 = fully installed; anything else is downloading, updating or broken. if (name.isEmpty() || flags and 4 == 0) return@forEach - val dir = File(cache, appId.toString()) - val art = listOf("library_600x900.jpg", "logo.png", "library_header.jpg", "header.jpg") - .map { File(dir, it) }.firstOrNull { it.isFile } + val art = steamCapsule(cache, appId) out[appId] = SteamGame(appId, name, art, label) } } return out.values.toList() } + /** Steam stores current library capsules inside hash-named folders under the app's cache dir. */ + private fun steamCapsule(cache: File, appId: Int): File? { + val appDir = File(cache, appId.toString()) + val dirs = listOf(appDir) + appDir.listFiles() + .orEmpty().filter { it.isDirectory }.sortedBy { it.name } + return STEAM_CAPSULES.asSequence() + .flatMap { name -> dirs.asSequence().map { File(it, name) } } + .firstOrNull { it.isFile && it.length() > 0L } + } + private class Spec(val id: String, val name: String, val system: String, val program: String, val folders: List, val exts: Set) private val specs = listOf( Spec("rpcs3", "RPCS3", "PS3", "/opt/appimages/rpcs3.AppImage", listOf("ps3"), setOf("iso")), diff --git a/app/src/main/java/com/droiddeck/launcher/ui/FrontEndScreen.kt b/app/src/main/java/com/droiddeck/launcher/ui/FrontEndScreen.kt index 5c73281b7..9091ff364 100644 --- a/app/src/main/java/com/droiddeck/launcher/ui/FrontEndScreen.kt +++ b/app/src/main/java/com/droiddeck/launcher/ui/FrontEndScreen.kt @@ -745,7 +745,7 @@ private fun Content( } Rise(4) { SectionTitle("Installed", "${s.steamGames.size} game${if (s.steamGames.size == 1) "" else "s"}") } if (s.steamGames.isEmpty()) Rise(5) { Note("No games installed.") } - else Rise(5, Modifier.weight(1f).fillMaxWidth()) { ArtGrid(s.steamGames.map { g -> Tile(g.name, g.library, g.art, "steam:${g.appId}", null) { onSelect("app:${g.appId}") } }) } + else Rise(5, Modifier.weight(1f).fillMaxWidth()) { ArtGrid(s.steamGames.map { g -> Tile(g.name, g.library, g.art, "steam:${g.appId}", null, showFooter = false) { onSelect("app:${g.appId}") } }) } } selected.startsWith("app:") -> { val g = s.steamGames.firstOrNull { "app:${it.appId}" == selected } @@ -775,7 +775,7 @@ private fun Content( val others = s.steamGames.filter { it !== g } if (others.isNotEmpty()) { Rise(3) { SectionTitle("More from the library", null) } - Rise(4, Modifier.weight(1f).fillMaxWidth()) { ArtGrid(others.map { x -> Tile(x.name, x.library, x.art, "steam:${x.appId}", null) { onSelect("app:${x.appId}") } }) } + Rise(4, Modifier.weight(1f).fillMaxWidth()) { ArtGrid(others.map { x -> Tile(x.name, x.library, x.art, "steam:${x.appId}", null, showFooter = false) { onSelect("app:${x.appId}") } }) } } } } @@ -990,6 +990,7 @@ private fun SetupPanel(s: FrontEndState, a: FrontEndActions) { private class Tile( val title: String, val sub: String?, val art: File?, val key: String, val iconRes: Int? = null, val dim: Boolean = false, val iconBitmap: Bitmap? = null, + val showFooter: Boolean = true, val onClick: () -> Unit, ) @@ -1175,9 +1176,11 @@ private fun GameTile(t: Tile, wide: Boolean, square: Boolean, src: MutableIntera ) { Text("›", fontSize = if (square) 16.sp else 20.sp, color = Color.White) } } } - Column(modifier = Modifier.padding(horizontal = 6.dp, vertical = 5.dp)) { - Text(t.title, fontSize = 10.sp, fontWeight = FontWeight.SemiBold, color = colors.onBackground, maxLines = 1, overflow = TextOverflow.Ellipsis) - if (t.sub != null) Text(t.sub, fontSize = 8.sp, color = colors.onSurfaceVariant, maxLines = 1, overflow = TextOverflow.Ellipsis) + if (t.showFooter) { + Column(modifier = Modifier.padding(horizontal = 6.dp, vertical = 5.dp)) { + Text(t.title, fontSize = 10.sp, fontWeight = FontWeight.SemiBold, color = colors.onBackground, maxLines = 1, overflow = TextOverflow.Ellipsis) + if (t.sub != null) Text(t.sub, fontSize = 8.sp, color = colors.onSurfaceVariant, maxLines = 1, overflow = TextOverflow.Ellipsis) + } } } }