Skip to content

Commit cdd3839

Browse files
authored
Update README.md
1 parent 5a24453 commit cdd3839

1 file changed

Lines changed: 88 additions & 31 deletions

File tree

README.md

Lines changed: 88 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Hacker Launcher
22

3-
**v1.0** — A game launcher for running Windows games on Linux with Proton, Wine, Steam, Flatpak, and Native runners.
3+
**v1.0** — A game launcher for running Windows games on Linux with Proton, Wine, Steam, Flatpak, and Native runners — plus Android apps via Waydroid.
44

55
Built with **Rust + Tauri 2** on the backend and **SolidJS + TypeScript** on the frontend.
66

@@ -9,25 +9,35 @@ Built with **Rust + Tauri 2** on the backend and **SolidJS + TypeScript** on the
99
## Features
1010

1111
### Games
12-
- Add games with Native / Wine / Proton / Flatpak / Steam runners
12+
- Add games with Native / Wine / Proton / Flatpak / Steam / Android runners
1313
- Per-game DXVK, Esync, Fsync, DXVK-Async overrides (with global defaults in Settings)
1414
- Custom per-game environment variables
1515
- Custom icon/cover art per game, shown in both List and Grid library views
1616
- Tags and favorites, with filtering in the library
1717
- List view or cover-art Grid view (switchable in Settings, takes effect after restart)
18-
- Drag & drop a `.exe` anywhere onto the window to add it as a game
18+
- Drag & drop a `.exe` or `.apk` anywhere onto the window to add it as a game
1919
- Live "Running" status with a Stop button, and automatic playtime tracking
2020
- Rotating per-run logs (last 10 runs kept per game) with a log viewer
2121
- Keyboard shortcuts: **Enter** launches/stops the selected game, **Delete** removes it
2222
- Quote-aware launch options parsing (so `--config="C:\Path With Spaces\x.ini"` works correctly)
2323
- Optional shared Wine prefix across multiple games, in addition to the default per-game prefix
2424
- Steam library auto-import: scans default, Flatpak, and additional Steam Library Folder
2525
locations — including external/removable drives and unregistered `SteamLibrary` folders — and
26-
lets you review and correct the guessed executable before importing
27-
- ProtonDB compatibility lookup by Steam App ID (cached for 15 minutes per App ID)
26+
lets you review and correct the guessed executable before importing. The exe guess uses a
27+
multi-signal scoring heuristic (name similarity to the game's title, Unreal/Unity "-Shipping"
28+
naming, install-folder depth, known non-game subfolders), not just "pick the largest file"
29+
- ProtonDB compatibility lookup by Steam App ID (in-memory cache for 15 minutes, plus a
30+
disk-persisted cache so a lookup that already succeeded once still works offline afterwards).
31+
A built-in Steam store search lets you find an App ID by name for games with none on record
2832
- Backup/restore: export games + settings to a JSON file, and import it back later either as a
2933
full restore or merged in as new games only
3034

35+
### Android
36+
- Add an `.apk` as a game (runner "Android"), running under [Waydroid](https://docs.waydro.id)
37+
- Package name auto-detected from the APK via `aapt`/`aapt2` (editable if detection fails)
38+
- One-click install into the Waydroid container with live streaming progress
39+
- Launch/stop by package ID, independent of the `.exe`-based runners' Wine/Proton pipeline
40+
3141
### Proton / Wine tooling
3242
- Install Proton-GE, official Valve Proton (stable/experimental), or a custom `tar.gz`/folder
3343
- Paginated GitHub release listing (not just the first ~30), with an in-memory cache to avoid
@@ -41,23 +51,29 @@ Built with **Rust + Tauri 2** on the backend and **SolidJS + TypeScript** on the
4151
your installed `winetricks list-all`)
4252
- **Dependency scanner**: looks for bundled redistributable installers (`vcredist`, `dotnetfx`,
4353
`dxsetup`, …) next to a game's executable and offers to run them directly or via the matching
44-
Winetricks verb
54+
Winetricks verb. Also flags dependencies that already look installed in the target prefix (via
55+
known marker DLLs/paths), so you're not nudged to reinstall something that's already there
4556
- Live streaming output for both Winetricks and dependency-installer runs — long operations like
4657
`dotnet48` show real progress instead of an unresponsive-looking wait
4758
- **Prefix locking**: launching a game, running Winetricks, and running a dependency installer all
4859
claim an exclusive lock on the Wine prefix they touch. A second attempt against the same prefix
4960
(e.g. starting a game that shares a prefix with one already running, or running Winetricks while
50-
that happens) is refused with a clear error instead of risking registry corruption
61+
that happens) is refused with a clear error instead of risking registry corruption — this now
62+
also detects prefixes already in use by processes *outside* the launcher (e.g. a `wine` command
63+
run manually in a terminal, or a second launcher instance), not just other launcher-initiated ops
5164
- Gamescope integration (adaptive-sync, resolution, FPS cap, Big Picture) via the `--gamescope`
5265
launch option
5366

5467
### Controllers
5568
- Lists gamepads currently visible to the kernel (`/proc/bus/input/devices`)
5669
- **SDL_GAMECONTROLLERCONFIG mapping wizard**: pick a connected controller, press each button/stick
5770
in turn, and the wizard builds the mapping string for you — reading raw Linux joystick events
58-
directly, so you don't need to know button/axis numbers by heart. The controller's SDL-style GUID
59-
is auto-derived from its USB/Bluetooth identity in sysfs. Usable standalone (copies to clipboard)
60-
or launched straight from a game's Configure dialog (fills the field directly)
71+
directly, so you don't need to know button/axis numbers by heart. D-Pads reported by the kernel
72+
as a "hat" (rather than four separate buttons) are detected via the same `JSIOCGAXMAP` ioctl
73+
SDL2/`jstest` use, and encoded as SDL's `hH.MASK` syntax rather than a plain axis. The
74+
controller's SDL-style GUID is auto-derived from its USB/Bluetooth identity in sysfs. Usable
75+
standalone (copies to clipboard) or launched straight from a game's Configure dialog (fills the
76+
field directly)
6177
- Per-game "Disable Steam Input" toggle and raw `SDL_GAMECONTROLLERCONFIG` override
6278

6379
### UI
@@ -74,6 +90,9 @@ Built with **Rust + Tauri 2** on the backend and **SolidJS + TypeScript** on the
7490
catalog browser shell out to your installed copy)
7591
- `gamescope` — optional, for Gamescope integration
7692
- `steam` or `flatpak` — for the Steam runner and Steam library import
93+
- `waydroid` — for the Android runner (see [docs.waydro.id](https://docs.waydro.id) for setup);
94+
`aapt` or `aapt2` (from your distro's Android SDK build-tools package) — optional, used to
95+
auto-detect an APK's package name; without it you can still enter the package ID manually
7796
- Proton versions are downloaded and managed by the launcher itself
7897

7998
### Build dependencies
@@ -108,13 +127,10 @@ npm run tauri dev
108127

109128
# Production build
110129
npm run tauri build
111-
# Binary will be in: src-tauri/target/release/hacker-launcher
130+
# Packaged AppImage/.deb/.rpm will be in: src-tauri/target/release/bundle/
131+
# (plain binary also at: src-tauri/target/release/hacker-launcher)
112132
```
113133

114-
> **Note:** `bundle.active` is currently `false` in `tauri.conf.json`, so `tauri build` produces a
115-
> plain binary rather than a packaged AppImage/`.deb`/`.rpm`. Flip it on and configure the `bundle`
116-
> section if you want installable packages.
117-
118134
## Architecture
119135

120136
```
@@ -135,9 +151,17 @@ source-code/
135151
├── config_manager.rs Settings persistence
136152
├── game_manager.rs Game CRUD, process launch/tracking/playtime, log rotation
137153
├── proton_manager.rs GitHub release listing/cache, install/extract, checksums
138-
├── tools.rs Steam import, Winetricks, dependency scan, ProtonDB, controllers,
139-
│ backup/restore
140-
└── prefix_lock.rs Cross-cutting Wine-prefix exclusivity lock
154+
├── android_manager.rs Waydroid: APK package-name extraction, install, launch, running-check, stop
155+
├── prefix_lock.rs Cross-cutting Wine-prefix exclusivity lock
156+
└── tools/ Loosely-related features, one submodule per area:
157+
├── mod.rs Re-exports each submodule's public API as `tools::*`
158+
├── process_util.rs Shared "stream a child process's output live" helper
159+
├── steam_import.rs Library scan, appinfo.vdf launch-config read, exe-scoring fallback
160+
├── winetricks.rs Verb catalog + running winetricks against a prefix
161+
├── dependency_scan.rs Bundled-installer detection + already-installed heuristic
162+
├── controllers.rs Controller listing + SDL mapping wizard input capture
163+
├── protondb.rs ProtonDB lookup with in-memory + disk cache
164+
└── backup.rs Export/import of games + settings
141165
```
142166

143167
## Data locations
@@ -156,17 +180,50 @@ All data is stored in `~/.hackeros/Hacker-Launcher/`:
156180

157181
Being upfront about what these features actually are, rather than overselling them:
158182

159-
- **Steam library import** and the **dependency scanner** are filename/heuristic-based (largest
160-
non-utility `.exe` in a folder; known installer filename patterns). They're right most of the
161-
time, wrong occasionally — the import dialog lets you fix the guessed executable before
162-
committing, and the dependency scanner can't tell whether something is *already* installed in the
163-
prefix, only that an installer for it exists on disk.
164-
- **ProtonDB lookup** and **Steam library import** both need network/Steam-client data respectively;
165-
neither works offline or for games with no corresponding Steam App ID.
183+
- **Steam library import**'s exe guess now tries Steam's own local launch-config cache first
184+
(`appcache/appinfo.vdf` — an undocumented, community-reverse-engineered binary format, parsed
185+
defensively with validation on the result), so it's reading Steam's real answer rather than
186+
guessing whenever that succeeds. When it doesn't (no local cache entry, or it doesn't parse — e.g.
187+
a future Steam client revision changes the format), it falls back to the filename/scoring
188+
heuristic as before. The import dialog tags each game with which one actually happened ("steam
189+
metadata" vs "heuristic scan") and always lets you correct the path either way before committing.
190+
**ProtonDB lookup** and **Steam library import** both need network/Steam-client data respectively
191+
to do a *fresh* lookup — ProtonDB results are now cached to disk so a prior successful lookup is
192+
still available offline, but a first-time lookup with no connection still fails, and neither works
193+
for games with no corresponding Steam App ID (the "Find by name" search only helps if the game
194+
*has* a Steam store entry, just not one on record locally).
195+
- The **dependency scanner**'s "already installed" check combines two signals, both still
196+
heuristics rather than a real registry/component query (Wine doesn't expose one): winetricks' own
197+
install log (`<prefix>/winetricks.log`, checked first — a real record of what winetricks
198+
successfully ran against that exact prefix, not an inference) and, as a fallback, known marker
199+
DLLs/paths a given component would leave behind. The log-based check only knows about verbs
200+
installed *via* winetricks; the marker-file check only covers a fixed table of common verbs —
201+
anything installed some other way and not in that table still always shows as "not confirmed
202+
installed".
166203
- The **controller mapping wizard** reads raw Linux joystick events directly rather than depending
167-
on SDL2 itself — it's a best-effort helper, and D-Pads reported as a "hat" (rather than four
168-
separate buttons) may need a manual touch-up to the generated string.
169-
- **Prefix locking** prevents concurrent *launcher-initiated* operations on the same prefix; it
170-
can't stop something outside the launcher from touching the same prefix at the same time.
171-
- No automated tests or CI yet, and the launcher itself has no auto-update mechanism (only Proton
172-
versions are update-checked).
204+
on SDL2 itself. D-Pads reported as a "hat" are detected and mapped correctly via the same
205+
`JSIOCGAXMAP` ioctl SDL2 uses, but this ioctl can fail silently on unusual devices/permissions, in
206+
which case the wizard falls back to treating every axis as plain analog — it's still a best-effort
207+
helper overall, so double-check the generated string.
208+
- **Prefix locking** detects Wine prefixes already in use by processes outside the launcher via two
209+
combined `/proc` checks: a matching `WINEPREFIX` environment variable, and (to also catch
210+
processes that reach a prefix without ever setting that variable themselves) any open file
211+
descriptor pointing inside the prefix directory. Both are still limited to processes readable by
212+
the current user (same user, or root) — a process running as a different user touching the same
213+
prefix on a shared machine is invisible to either check — and both are inherently racy against a
214+
process that hasn't opened anything inside the prefix yet at the moment of the check.
215+
- **Android/.apk support** runs entirely inside a separate [Waydroid](https://docs.waydro.id)
216+
container rather than as a process this launcher directly owns, so it doesn't get quite the same
217+
guarantees as the Wine/Proton runners. Playtime tracking now polls the container (`waydroid shell
218+
-- pidof <package>`, roughly every 4 seconds with a 2-strike debounce) to detect the app's actual
219+
exit rather than only reacting to the Stop button, so a game closed from inside Android itself
220+
still gets recorded correctly — but it's still polling-based (exit is noticed with a delay of up
221+
to ~8 seconds, not instantly) and, like `pidof` generally, assumes the app's main process is named
222+
after its package ID, which can miss/misreport for apps that spawn background services under a
223+
different name. Stop also calls `waydroid app stop`, which isn't supported on every Waydroid
224+
version. Package-name auto-detection needs `aapt`/`aapt2` installed separately (Waydroid itself
225+
doesn't provide it) — without it, the package ID can still be entered by hand.
226+
- No automated tests or CI pipeline yet — some of the trickier logic above (the binary VDF parsing,
227+
the winetricks-log check) does have unit tests alongside it in the relevant module now, but
228+
there's no CI running them automatically and no coverage of the Tauri command layer or frontend.
229+
The launcher itself also has no auto-update mechanism (only Proton versions are update-checked).

0 commit comments

Comments
 (0)