Files
DroidDeck/docs/development/steam-game-imports.md

5.7 KiB

Steam game imports

Second Library still registers the selected Steam library. It now also scans Windows executables in its immediate game subfolders and steamapps/common, using the same importer as Added Games. Existing Steam manifests are preserved.

droiddeck-steam-games watch runs beside the client. It identifies a game by an existing manifest's install directory, a steam_appid.txt beside the game or selected executable, or a unique normalized title from Steam's store search with an explicit country. A steam_appid.txt naming 480 (Spacewar, the Steamworks SDK sample that Steam emulators and multiplayer fixes use in place of the game) identifies nothing; the launcher's artwork matching ignores it too. Matching also accepts the terminal Windows Edition and INTERGRADE suffixes; multiple matches still remain unresolved. Artwork's fuzzy matching is deliberately not used for identity. Ambiguous or unresolved titles remain non-Steam games.

Ownership comes from the running native ARM64 client's BIsSubscribedApp, through versioned SteamClient020 / SteamUser021 / STEAMAPPS_INTERFACE_VERSION008 interfaces. The helper gets the account from that connection, not from a public profile. No API key, password, profile visibility change, or CEF debugging port is needed. Each probe runs in a bounded subprocess so library failures cannot crash the importer or client.

The resulting snapshot is stored under that account's userdata/<id>/config and refreshed every 30 seconds while logged in. Title lookups retry every five minutes and when the source changes, so an early network failure does not end the watcher. Changed status and probe failures are written to the session log. Startup and post-exit import use the last successful snapshot for the selected account. The client reads manifests and shortcuts only at start, so when a snapshot confirms owned titles that are not yet imported, the watcher restarts the client once (not while a game runs or a download is in progress) and the importer runs between the exit and the next start. An unavailable client or offline first launch falls back to shortcuts; existing snapshots remain usable offline. Steam still enforces the current license at launch.

For a confirmed game without a manifest, the importer links the source folder into the internal Steam library and writes an installed manifest with Universe=1 and StateFlags=4. Steam ignores manifests without the universe field. A manifest without installed depots makes Steam verify every file, so the importer carries the build another client recorded: WinNative's .winnative-steam/steamapps manifest beside the game folder, or a completed download's depot manifest IDs from .DepotDownloader/depot.config or, when that file is gone, the <depot>_<gid>.manifest names in .DepotDownloader/completed. Steam then only downloads what changed since that build. Bare manifests from earlier imports are upgraded while Steam has not touched them. Without either record Steam still verifies. WinNative's manifest also identifies the folder without a store lookup. Steam updates and uninstall operate on the linked game files, so imported folders must be writable. Existing Steam manifests and directories are preserved. Only the exact minimal manifests generated by the earlier importer are migrated, with the matching source symlink still present. If registration fails, the game remains a shortcut. Once Steam takes over an install, manage it in Steam; forgetting an Added Games folder only removes its shortcuts.

An import is undone (its symlink and manifest, never the folder) when the folder was imported as Spacewar, or when a snapshot of the folder's current files names another app. The folder is then imported as that app if the account owns it, else it is a shortcut again. A failed title lookup undoes nothing. The launcher likewise drops artwork it fetched for the app the files named before and fetches it again; pictures the user chose are kept.

The account's .droiddeck-routes.json maps shortcut IDs to real Steam app IDs so the Android launcher uses the same title and Proton prefix as Steam. Other accounts do not inherit those routes. Managed shortcuts are replaced; user-created shortcuts and unreadable shortcut files are preserved. Both routes use the existing ARM64 Proton registration.

Launcher artwork

The installed Steam game scan lists each game's appcache/librarycache/<appid> directory once for its cover, background, icon and logo. Filename preference is checked before directory preference: the app folder, then hash folders sorted by name. Hash-named fallback icons are still taken only from the app folder in its listing order. The directory snapshot is not kept between scans, so the next scan sees new artwork folders.

Validation

Run python3 -m unittest discover -s tools/tests -p 'test_*.py' and Android unit tests. Tests cover identity and edition matching, network recovery, account isolation, owned/unowned/unknown routing, legacy manifest migration, manifest preservation, filesystem collisions, shortcut preservation, and the versioned IPC call/cleanup contract. On 2026-09-28, the ARM64 Steam client (1790545198) confirmed ownership and installed status through ISteamApps for Final Fantasy VII Rebirth (2909400), Final Fantasy VII Remake Intergrade (1462040), and Final Fantasy XV (637650), with all three install paths resolving to the original second-library folders. The old managed shortcuts were removed. This verifies library registration, not complete game launches. Unowned fallback and account isolation are covered by automated tests.

References: Steam Apps API, [Valve's versioned interfaces](https://github.com/ValveSoftware/Proton/tree/proton_11.0/lsteamcli ent/steamworks_sdk_154).