Step 2-3 of game detection (game-presence-plan.md). The OS edges feeding
the pure seams from the previous commit.
- src/game/steam.rs: SteamProbe — reads the live RunningAppID and resolves
it to a name via appmanifest_<id>.acf (no binary appinfo.vdf). Pure parse
fns (parse_running_app_id / parse_library_paths / parse_app_name) over
file contents are unit-tested incl. current+legacy libraryfolders shapes,
escaped Windows paths, empty/missing names, and garbage. Roots discovered
across native/Flatpak/Snap (Linux) and the registry (Windows); libraries
and resolved names cached + mtime-invalidated so the 3s poll doesn't
rescan. File reads byte-capped.
- src/game/scan.rs: native running-process enumeration — /proc (exe symlink,
comm fallback) on Linux, Toolhelp on Windows — feeding the pure
match_processes. No sysinfo dep (D7).
- Cargo.toml: windows-sys as a direct Windows-only dep for the registry +
Toolhelp FFI. No NEW crate — it was already in the lockfile transitively
via cpal/rfd, so the audit surface is unchanged.
391 lib tests (+5). Linux: build + clippy --all-targets clean. Windows FFI
signatures verified against windows-sys 0.61 source (one *const vs *mut
lpReserved fixed) but NOT yet cross-compiled — defer to the post-UI Windows
build cycle.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Step 1 of the game-detection feature (game-presence-plan.md): all the
pure, I/O-free logic, tested first.
- src/game/mod.rs: DetectedGame + stable namespaced ids (steam:730 /
exe:hl2_linux, never the mutable name); ManualOverride; the priority
resolve() matcher (override -> Steam -> mapped process -> none); the
Debouncer (2-on/3-off, immediate bypass for manual override) that
stops a flapping detector re-announcing the ~48KB-avatar PeerState;
match_processes() over explicit user mappings with a launcher denylist
(never guesses a game from an arbitrary process).
- src/game/vdf.rs: a real recursive-descent KeyValues/VDF parser (not a
name-regex) for appmanifest/.acf, libraryfolders.vdf, registry.vdf —
depth-capped, escape-aware, never panics on malformed/truncated input.
- src/sanitize.rs: sanitize_game_label (64-char/256-byte cap, wider than
the 48-char name cap) sharing the bidi/zero-width cleaning.
- src/config.rs: additive game_presence_enabled (opt-in, default OFF),
game_backgrounds + game_process_map (BTreeMap, deterministic);
background_path generalized to hashed per-game files; explicit
legacy-config migration test (load() wipes on any deserialize error).
- src/background.rs: game_background_filename (FNV-1a hashed, fs-safe).
No wire/protocol change yet; no OS reads yet. 386 lib tests (+28).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>