Merge branch 'main' into feature/controller-editor

This commit is contained in:
MaxsTechReview authored and GitHub committed 2026-09-24 23:08:05 -04:00
commit 00e266d9b4
2 files changed
+48 -9

No files matched your search

@@ -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<String>, val exts: Set<String>)
private val specs = listOf(
Spec("rpcs3", "RPCS3", "PS3", "/opt/appimages/rpcs3.AppImage", listOf("ps3"), setOf("iso")),
@@ -748,7 +748,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 }
@@ -778,7 +778,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}") } }) }
}
}
}
@@ -997,6 +997,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,
)
@@ -1182,9 +1183,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)
}
}
}
}