mirror of
https://github.com/Droid-Deck/DroidDeck.git
synced 2026-11-12 03:21:08 +00:00
fix: add games picked inside a folder game as their own games (#1155)
Picking an exe anywhere under an added folder treated it as that game, so a folder added with "Add all games in this folder" (or a removed one) swallowed every game below it: picking another game's exe switched the folder game's target, or brought the removed folder game back under its old name and art. Only treat a pick as the same game when it sits in the folder root, in the same top-level subfolder as the game's current exe, or is another build of that exe, and prefer the deepest matching folder.
This commit is contained in:
1 parent
cee84fd12a
commit
57799bef5a
2 files changed
+70
-2
No files matched your search
@@ -204,8 +204,10 @@ object AddedGames {
|
||||
GamesFolderMigration.run(context)
|
||||
val path = canonical(exe)
|
||||
fun holds(f: File) = inside(path, canonical(f))
|
||||
AddedExes.list(context).map { File(it.folder) }.firstOrNull(::holds)?.let { return it }
|
||||
SessionPrefs.removedAddedGames(context).map { File(it) }.firstOrNull(::holds)?.let { return it }
|
||||
val entries = AddedExes.list(context)
|
||||
fun owns(f: File) = holds(f) && sameGame(f, currentExe(context, entries, f), exe)
|
||||
entries.map { File(it.folder) }.filter(::owns).maxByOrNull { canonical(it).length }?.let { return it }
|
||||
SessionPrefs.removedAddedGames(context).map { File(it) }.filter(::owns).maxByOrNull { canonical(it).length }?.let { return it }
|
||||
StoreInstallRoot.gameFolders(context).firstOrNull(::holds)?.let { return it }
|
||||
val dirs = listOfNotNull(
|
||||
GameStorage.effective(context)?.let { File(it.path, "steamapps/common") },
|
||||
@@ -220,6 +222,24 @@ object AddedGames {
|
||||
return null
|
||||
}
|
||||
|
||||
private fun currentExe(context: Context, entries: List<AddedExes.Entry>, folder: File): File? =
|
||||
SessionPrefs.addedGameExe(context, folder.path).takeIf { it.isNotEmpty() }?.let(::File)
|
||||
?: entries.firstOrNull { it.folder == folder.path }?.let { File(it.exe) }
|
||||
?: rank(folder).exes.firstOrNull()
|
||||
|
||||
private fun sameGame(folder: File, current: File?, pick: File): Boolean {
|
||||
val base = canonical(folder)
|
||||
if (current == null || pick.parentFile?.let(::canonical) == base) return true
|
||||
val known = canonical(current)
|
||||
if (!inside(known, base) || known.substringBeforeLast('/') == base) return true
|
||||
val picked = canonical(pick)
|
||||
fun top(p: String) = p.removePrefix("$base/").substringBefore('/')
|
||||
fun stem(p: String) = p.substringAfterLast('/').lowercase().removeSuffix(".exe").replace(EXE_BUILD, "").filter { it.isLetterOrDigit() }
|
||||
return top(known) == top(picked) || stem(known).let { it.isNotEmpty() && it == stem(picked) }
|
||||
}
|
||||
|
||||
private val EXE_BUILD = Regex("([-_. ](x64|x86|win64|win32|64|32|shipping))+$")
|
||||
|
||||
fun addExe(context: Context, exe: File): AddResult {
|
||||
val app = context.applicationContext
|
||||
if (!exe.isFile) return AddResult.NotFound
|
||||
|
||||
@@ -85,6 +85,54 @@ class AddedExesTest {
|
||||
assertEquals(listOf(File(games, "Example").path), AddedExes.list(app).map { it.folder })
|
||||
}
|
||||
|
||||
@Test fun aGameInsideAFolderAddedAsOneGameIsAddedOnItsOwn() {
|
||||
noGamesFolders()
|
||||
val held = onStorage("Download/ADM/Death Stranding/ds.exe")
|
||||
val pick = onStorage("Download/ADM/Pro Evolution Soccer 2017/PES2017.exe")
|
||||
val container = held.parentFile!!.parentFile!!
|
||||
AddedExes.put(app, AddedExes.Entry(container.path, held.path, picked = false))
|
||||
val added = AddedGames.addExe(app, pick)
|
||||
assertTrue(added is AddedGames.AddResult.Added)
|
||||
assertEquals(pick.parentFile!!.path, (added as AddedGames.AddResult.Added).game.folder.path)
|
||||
assertEquals(setOf(held.path, pick.path), AddedGames.scan(app).map { it.exe.path }.toSet())
|
||||
}
|
||||
|
||||
@Test fun aRemovedFolderGameDoesNotComeBackForAnotherGame() {
|
||||
noGamesFolders()
|
||||
val held = onStorage("Download/ADM/Death Stranding/ds.exe")
|
||||
val pick = onStorage("Download/ADM/Blur Game/Blur.exe")
|
||||
val container = held.parentFile!!.parentFile!!
|
||||
AddedExes.put(app, AddedExes.Entry(container.path, held.path, picked = false))
|
||||
SessionPrefs.setAddedGameName(app, container.path, "Death Stranding Directors Cut")
|
||||
AddedGames.remove(app, container)
|
||||
val added = AddedGames.addExe(app, pick)
|
||||
assertTrue(added is AddedGames.AddResult.Added)
|
||||
assertEquals("Blur Game", (added as AddedGames.AddResult.Added).game.name)
|
||||
assertTrue(container.path in SessionPrefs.removedAddedGames(app))
|
||||
assertEquals(listOf(pick.path), AddedGames.scan(app).map { it.exe.path })
|
||||
}
|
||||
|
||||
@Test fun anotherBuildOfTheSameExeInASiblingFolderSwitchesTheGame() {
|
||||
noGamesFolders()
|
||||
val held = onStorage("Games2/Oddworld/x64/Oddworld.exe")
|
||||
val other = onStorage("Games2/Oddworld/x86/Oddworld.exe")
|
||||
val folder = held.parentFile!!.parentFile!!
|
||||
AddedExes.put(app, AddedExes.Entry(folder.path, held.path, picked = false))
|
||||
val switched = AddedGames.addExe(app, other)
|
||||
assertTrue(switched is AddedGames.AddResult.Switched)
|
||||
assertEquals(folder.path, (switched as AddedGames.AddResult.Switched).game.folder.path)
|
||||
assertEquals(listOf(other.path), AddedGames.scan(app).map { it.exe.path })
|
||||
}
|
||||
|
||||
@Test fun aDifferentGameWithANumberedNameIsNotTakenAsAnotherBuild() {
|
||||
noGamesFolders()
|
||||
val held = onStorage("Games2/id/Doom/Doom.exe")
|
||||
val pick = onStorage("Games2/id/Doom64/Doom64.exe")
|
||||
AddedExes.put(app, AddedExes.Entry(held.parentFile!!.parentFile!!.path, held.path, picked = false))
|
||||
assertTrue(AddedGames.addExe(app, pick) is AddedGames.AddResult.Added)
|
||||
assertEquals(2, AddedGames.scan(app).size)
|
||||
}
|
||||
|
||||
@Test fun anExeTheSessionCannotSeeIsNotAdded() {
|
||||
noGamesFolders()
|
||||
val outside = exe(File(tmp.newFolder("elsewhere"), "Game/Game.exe"))
|
||||
|
||||
Reference in new issue
Block a user