From 705da08a0e052b25c71bc4e12f10e3335f9acb44 Mon Sep 17 00:00:00 2001 From: MaxsTechReview Date: Tue, 20 Oct 2026 23:24:29 -0400 Subject: [PATCH] fix: game files keep the full game name instead of cutting it at 40 characters (#1230) --- .../droiddeck/launcher/frontend/GameFiles.kt | 12 ++++++++++-- .../launcher/frontend/GameFilesTest.kt | 17 +++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/droiddeck/launcher/frontend/GameFiles.kt b/app/src/main/java/com/droiddeck/launcher/frontend/GameFiles.kt index 8e0b55def..63b5f6b4d 100644 --- a/app/src/main/java/com/droiddeck/launcher/frontend/GameFiles.kt +++ b/app/src/main/java/com/droiddeck/launcher/frontend/GameFiles.kt @@ -14,6 +14,7 @@ import java.io.InputStream object GameFiles { const val EXTENSION = "steam" private const val MAX_BYTES = 128 + private const val MAX_NAME_BYTES = 200 private const val INVENTORY = ".droiddeck-sync.json" private const val FORMAT = "droiddeck-game-files-v1" @@ -50,8 +51,15 @@ object GameFiles { fun filename(game: Library.SteamGame, disambiguate: Boolean = false): String { val sanitized = game.name.replace(Regex("[\\p{Cntrl}\\\\/:*?\"<>|]"), "_") .trim().trim('.').ifBlank { "Game" } - val points = sanitized.codePoints().limit(40).toArray() - val title = String(points, 0, points.size) + val points = sanitized.codePoints().toArray() + var end = 0 + var bytes = 0 + while (end < points.size) { + bytes += String(points, end, 1).toByteArray().size + if (bytes > MAX_NAME_BYTES) break + end++ + } + val title = String(points, 0, end).trimEnd { it.isWhitespace() || it == '.' }.ifBlank { "Game" } val suffix = if (disambiguate) " (${game.gameIdString})" else "" return "$title$suffix.$EXTENSION" } diff --git a/app/src/test/java/com/droiddeck/launcher/frontend/GameFilesTest.kt b/app/src/test/java/com/droiddeck/launcher/frontend/GameFilesTest.kt index d37689e29..bcae5c8fe 100644 --- a/app/src/test/java/com/droiddeck/launcher/frontend/GameFilesTest.kt +++ b/app/src/test/java/com/droiddeck/launcher/frontend/GameFilesTest.kt @@ -132,6 +132,23 @@ class GameFilesTest { } } + @Test fun longTitlesKeepTheirEndingAndStayApart() { + val folder = tmp.newFolder() + val first = game("32370", "STAR WARS™ Knights of the Old Republic™") + val second = game("208580", "STAR WARS™ Knights of the Old Republic™ II - The Sith Lords™") + assertEquals("STAR WARS™ Knights of the Old Republic™ II - The Sith Lords™.steam", GameFiles.filename(second)) + GameFiles.sync(app, folder, listOf(first, second)) + GameFiles.sync(app, folder, listOf(first, second)) + assertEquals("32370", File(folder, "STAR WARS™ Knights of the Old Republic™.steam").readText()) + assertEquals("208580", File(folder, "STAR WARS™ Knights of the Old Republic™ II - The Sith Lords™.steam").readText()) + assertEquals(2, folder.listFiles()!!.count { it.extension == GameFiles.EXTENSION }) + assertEquals("a".repeat(200) + ".steam", GameFiles.filename(game(name = "a".repeat(300)))) + assertEquals("a".repeat(199) + ".steam", GameFiles.filename(game(name = "a".repeat(199) + " b"))) + assertEquals("Game.steam", GameFiles.filename(game(name = " . "))) + assertEquals("Game.steam", GameFiles.filename(game(name = "Game . ."))) + assertEquals("a".repeat(197) + ".steam", GameFiles.filename(game(name = "a".repeat(197) + " . b"))) + } + @Test fun refusesSymlinksLeavingTheExportFolder() { val folder = tmp.newFolder() val outside = tmp.newFile()