mirror of
https://github.com/Droid-Deck/DroidDeck.git
synced 2026-11-12 11:31:08 +00:00
fix: show when non-Steam games were last played, from Steam's shortcuts (#1551)
This commit is contained in:
1 parent
c9565bc0b9
commit
e62698e395
2 files changed
+75
-3
No files matched your search
@@ -204,9 +204,24 @@ object Library {
|
||||
(runCatching { g.folder.canonicalPath }.getOrDefault(g.folder.absolutePath) !in claimed &&
|
||||
AddedGames.guestPath(context, g.folder).let { it == null || it !in claimed })
|
||||
}
|
||||
return (steam + own.map { g -> addedGame(context, g, roots) }).distinctBy { it.gameId }
|
||||
val played = shortcutsLastPlayed(SessionPrefs.steamRoot(context, arch))
|
||||
return (steam + own.map { g -> addedGame(context, g, roots, played[g.appId] ?: 0L) }).distinctBy { it.gameId }
|
||||
}
|
||||
|
||||
/** Steam's LastPlayTime for each non-Steam shortcut, by its unsigned shortcut app id. */
|
||||
internal fun shortcutsLastPlayed(root: File): Map<Long, Long> = runCatching {
|
||||
val config = AddedGames.accountConfig(root) ?: return emptyMap()
|
||||
val bytes = File(config, "shortcuts.vdf").takeIf { it.isFile }?.readBytes() ?: return emptyMap()
|
||||
val vdf = SteamAppInfo.readMap(java.nio.ByteBuffer.wrap(bytes).order(java.nio.ByteOrder.LITTLE_ENDIAN), null)
|
||||
(vdf["shortcuts"] as? Map<*, *>)?.values?.filterIsInstance<Map<*, *>>()?.mapNotNull { s ->
|
||||
fun int(key: String) = s.entries.firstOrNull { (it.key as? String).equals(key, ignoreCase = true) }
|
||||
?.let { (it.value as? String)?.toLongOrNull() }?.and(0xFFFFFFFFL)
|
||||
val id = int("appid") ?: return@mapNotNull null
|
||||
val time = int("LastPlayTime")?.takeIf { it > 0L } ?: return@mapNotNull null
|
||||
id to time
|
||||
}?.toMap()
|
||||
}.getOrNull().orEmpty()
|
||||
|
||||
fun claimedFolders(steam: List<SteamGame>): Set<String> = steam.filter(::fromSteamClient).flatMap { g ->
|
||||
val dir = g.gameFiles ?: return@flatMap emptyList()
|
||||
listOfNotNull(
|
||||
@@ -215,11 +230,14 @@ object Library {
|
||||
)
|
||||
}.toSet()
|
||||
|
||||
fun addedGame(context: Context, g: AddedGames.Game, roots: List<File> = steamLibraries(context).map { it.first }): SteamGame =
|
||||
fun addedGame(
|
||||
context: Context, g: AddedGames.Game, roots: List<File> = steamLibraries(context).map { it.first },
|
||||
lastPlayed: Long = 0L,
|
||||
): SteamGame =
|
||||
AddedGameArt.resolve(context, g).let { art ->
|
||||
SteamGame(
|
||||
g.steamAppId ?: g.appId.toInt(), g.name, art.portrait ?: art.header, ADDED, g.gameId,
|
||||
hero = art.hero ?: art.header, gameFiles = g.folder,
|
||||
hero = art.hero ?: art.header, lastPlayed = lastPlayed, gameFiles = g.folder,
|
||||
protonPrefix = protonPrefix(context, g.steamAppId?.toLong() ?: g.appId, libraries = roots),
|
||||
icon = art.icon?.takeIf { it.extension.lowercase() != "ico" },
|
||||
source = g.source, storeId = g.storeId,
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.droiddeck.launcher.frontend
|
||||
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.File
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.ByteOrder
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TemporaryFolder
|
||||
import org.junit.runner.RunWith
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.annotation.Config
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
@Config(sdk = [33])
|
||||
class LibraryShortcutsLastPlayedTest {
|
||||
@get:Rule val tmp = TemporaryFolder()
|
||||
|
||||
private fun steamRoot(): File = tmp.newFolder("Steam").also { root ->
|
||||
File(root, "config").mkdirs()
|
||||
File(root, "config/loginusers.vdf").writeText(
|
||||
"\"users\"\n{\n\t\"76561198352563669\"\n\t{\n\t\t\"AccountName\"\t\t\"a\"\n\t\t\"MostRecent\"\t\t\"1\"\n\t}\n}\n")
|
||||
}
|
||||
|
||||
private class Vdf {
|
||||
val out = ByteArrayOutputStream()
|
||||
fun key(type: Int, k: String) { out.write(type); out.write(k.toByteArray()); out.write(0) }
|
||||
fun map(k: String, body: Vdf.() -> Unit) { key(0, k); body(); out.write(8) }
|
||||
fun str(k: String, v: String) { key(1, k); out.write(v.toByteArray()); out.write(0) }
|
||||
fun int(k: String, v: Long) { key(2, k); out.write(ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(v.toInt()).array()) }
|
||||
}
|
||||
|
||||
@Test fun readsEachShortcutsLastPlayTimeByUnsignedAppId() {
|
||||
val root = steamRoot()
|
||||
val vdf = Vdf().apply {
|
||||
map("shortcuts") {
|
||||
map("0") { int("appid", 3388195926L); str("AppName", "Rayman 2"); int("LastPlayTime", 1792931476L) }
|
||||
map("1") { int("appid", 2228102537L); str("AppName", "Do Not Disturb"); int("LastPlayTime", 0L) }
|
||||
map("2") { int("appid", 1234L); str("appname", "Old"); int("lastplaytime", 1700000000L) }
|
||||
}
|
||||
out.write(8)
|
||||
}
|
||||
File(root, "userdata/392297941/config").apply { mkdirs() }.resolve("shortcuts.vdf").writeBytes(vdf.out.toByteArray())
|
||||
assertEquals(mapOf(3388195926L to 1792931476L, 1234L to 1700000000L), Library.shortcutsLastPlayed(root))
|
||||
}
|
||||
|
||||
@Test fun missingOrBrokenShortcutsGiveNothing() {
|
||||
val root = steamRoot()
|
||||
assertEquals(emptyMap<Long, Long>(), Library.shortcutsLastPlayed(root))
|
||||
File(root, "userdata/392297941/config").apply { mkdirs() }.resolve("shortcuts.vdf").writeBytes(byteArrayOf(0, 's'.code.toByte()))
|
||||
assertEquals(emptyMap<Long, Long>(), Library.shortcutsLastPlayed(root))
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user