Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions PRIVACY.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Seaglass is made and published by ApolloF, in the Netherlands. Contact: me@apoll

## What Seaglass reads on your PC

To find your games, Seaglass reads the libraries of the launchers installed on your PC (such as Steam, Epic, GOG Galaxy, EA, Ubisoft, Battle.net and Xbox), the folders you add, and achievement files games and launchers keep locally. It records playtime for games you start. All of this stays on your PC.
To find your games, Seaglass reads the libraries of the launchers installed on your PC (such as Steam, Epic, GOG Galaxy, EA, Ubisoft, Battle.net and Xbox), the settings and game lists of the emulators installed on your PC (RPCS3, Dolphin, Cemu) and the game folders they point at, the folders you add, and achievement files games and launchers keep locally. It records playtime for games you start. All of this stays on your PC.

Seaglass also reads which Steam account is signed in on this PC, from Steam's local files, and the Windows machine ID (only as a hash, to name this PC's files in the profile described under With Syncer).

Expand Down Expand Up @@ -57,7 +57,7 @@ If you use [Syncer](https://apps.apollof.nl/syncer/), Seaglass talks to it on yo

Settings, your library, the profile, the log and crash logs stay in `%APPDATA%\Seaglass`. The log records events such as the titles of games started, play times and errors. Cached art, achievement data, the game database, update downloads and the data of the WebView2 component that draws Seaglass's window stay in `%LOCALAPPDATA%\Seaglass`. WebView2 is a Microsoft component; its own data handling follows Microsoft's and Windows' privacy settings, not Seaglass. Seaglass's interface loads no remote code (it has a strict Content Security Policy).

Outside those folders Seaglass writes only when you turn the feature on: a value under `HKCU\Software\Microsoft\Windows\CurrentVersion\Run` for *Start with Windows*, and Seaglass shortcuts in Steam's `shortcuts.vdf` for the Steam Input route for controllers. With HidHide installed, Seaglass adds your controller to HidHide's block list while a game runs and adds itself to HidHide's allow list; it notes the controllers it hid in `%APPDATA%\Seaglass\hidden-controllers.json` and takes exactly those off again when the game ends. It reads, but doesn't change, the store launchers' own files and registry entries.
Outside those folders Seaglass writes only when you turn the feature on: a value under `HKCU\Software\Microsoft\Windows\CurrentVersion\Run` for *Start with Windows*, and Seaglass shortcuts in Steam's `shortcuts.vdf` for the Steam Input route for controllers. With HidHide installed, Seaglass adds your controller to HidHide's block list while a game runs and adds itself to HidHide's allow list; it notes the controllers it hid in `%APPDATA%\Seaglass\hidden-controllers.json` and takes exactly those off again when the game ends. It reads, but doesn't change, the store launchers' and emulators' own files and registry entries.

*Copy diagnostics* (or `Seaglass.exe --diagnostics`, which writes a file to your desktop) creates a report only when you ask for it. It holds Seaglass, Windows and WebView2 versions, your settings (including game folders), library counts, the last crash and the end of the log. It holds no keys or tokens, and your user folder is shortened to `%USERPROFILE%`. It goes only where you paste or send it.

Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,10 @@ go test ./... # backend tests
cd frontend && npm run check # type-check the interface
cd frontend && npm run dev:mock # interface in a browser with made-up games
WL_REAL_SCAN=1 go test -run RealScan -v ./internal/scan # scan this PC and print what was found
WL_REAL_EMU=1 go test -run RealFind -v ./internal/emulated # list the emulators and console games on this PC
```

The backend lives in `internal/`: `scan` (sources, external-copy detection, executable picking), `identify` (Ludusavi matching), `library` (the game library), `settings`, `meta` (metadata and art), `pad` (controllers through SDL3), `launch` (game sessions: hooks, process tracking, playtime), `syncer` (client for Syncer's launcher API), `owned` (owned games from Steam, GOG and Epic accounts), `update` (updates from GitHub releases), `sqlite` (read-only SQLite reader for GOG Galaxy), `steaminput` (Steam shortcuts for the Steam Input route), `app` (services the interface calls, windows and tray), plus small helpers (`lnk`, `platform`, `logx`). The installer is `build/windows/nsis/project.nsi`; signing is described in [docs/SIGNING.md](docs/SIGNING.md). Steam and game-database parsing comes from [gamekit](https://github.com/ApolloF/gamekit). The Svelte 5 interface is in `frontend/src`: `desktop/` for desktop mode, `bigpicture/` for the controller layouts, `overlay/` for the in-game overlay.
The backend lives in `internal/`: `scan` (sources, external-copy detection, executable picking), `identify` (Ludusavi matching), `library` (the game library), `settings`, `meta` (metadata and art), `pad` (controllers through SDL3), `launch` (game sessions: hooks, process tracking, playtime), `syncer` (client for Syncer's launcher API), `owned` (owned games from Steam, GOG and Epic accounts), `emulated` (console games on RPCS3, Dolphin and Cemu), `update` (updates from GitHub releases), `sqlite` (read-only SQLite reader for GOG Galaxy), `steaminput` (Steam shortcuts for the Steam Input route), `app` (services the interface calls, windows and tray), plus small helpers (`lnk`, `platform`, `logx`). The installer is `build/windows/nsis/project.nsi`; signing is described in [docs/SIGNING.md](docs/SIGNING.md). Steam and game-database parsing comes from [gamekit](https://github.com/ApolloF/gamekit). The Svelte 5 interface is in `frontend/src`: `desktop/` for desktop mode, `bigpicture/` for the controller layouts, `overlay/` for the in-game overlay.

The DLSS Updater add-on host lives on the [`feature/dlss-addon`](https://github.com/ApolloF/Seaglass/tree/feature/dlss-addon) branch; [docs/dlss-addon.md](https://github.com/ApolloF/Seaglass/blob/feature/dlss-addon/docs/dlss-addon.md) there has ideas for bringing it back.

Expand Down
15 changes: 15 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,21 @@ What each version of Seaglass (WaterLauncher until 1.4) changed and why, newest

Alongside Seaglass: [gamekit](https://github.com/ApolloF/gamekit) v0.1.0, Syncer 0.15.0 and DLSS Updater 1.4.1.

## 1.13.0 (unreleased): emulated games

Console games played on emulators are a new library next to the PC games, so Seaglass can be the one launcher at the desk and on a stream. Nothing about PC games changes: console games get keys of their own (`emu:` and the file the emulator boots), so they never meet a PC game's folder.

- **New `internal/emulated`**: finds RPCS3, Dolphin and Cemu (an `Emulators`, `Emulation` or `Emu` folder on any fixed drive, also under `Games`, Program Files, `%LOCALAPPDATA%\Programs`, Scoop) and the games their own files list: RPCS3's `config\games.yml`, `dev_hdd0\game` and `dev_hdd0\disc`, and the folders next to its listed games (a second copy of a disc it forgot); Dolphin's `ISOPath`s from `Dolphin.ini`, walked like Dolphin does; Cemu's `GamePaths`, `title_list_cache.xml` and games installed to `mlc01`. Another emulator (RetroArch, Ryujinx, PCSX2) is one more `emulator` value in that package.
- **Titles** come from the game or the emulator, never the file name when there's better: `PARAM.SFO`, Dolphin's GameTDB list (`Sys\wiitdb-en.txt`) by disc id, Cemu's game list and `meta.xml`, then the disc's own name, then the folder (when the game is alone in it), then the file. The platform is the source label (PS3, GameCube, Wii, Wii U).
- **Copies**: two files with the same serial, or the same name and size, are one game; a local copy beats one on a network drive (Cemu's list pointed at the server, the local copy is what plays). Different copies that are both there (two regions' discs) keep both, with the serial after the title.
- **Bounded and untrusted**: every file is read with a size limit (`PARAM.SFO` 64 KB, `games.yml` 1 MB, title list 8 MB, …) and the parsers are fuzzed; paths from the emulators' files are cleaned, and device paths refused. The walk stops after 4 s or 60,000 entries, goes at most 5 folders down, skips folders with programs in them (PC games). Nothing on a network drive (mapped or `\\server\share`) is ever opened, since one whose server is away can hang for minutes: a game Cemu's list names there still shows, from the list alone; RPCS3 games there don't. On this PC: 7 games in about 20 ms.
- **The PC games never wait**: the search for console games runs beside the scan (`app.consoleSearch`, one at a time, 15 s deadline). When it isn't done as the PC games are, they're shown first and the console games come in after (`library.ApplyScanOf`); console games a search may have missed are left as they were instead of turning not installed: all of them after a cancel or a panic, only those in the game folders it didn't get through when the walk ran out of time or entries (`emulated.WalkError`). Launch options refuse quit arguments for the game's own program (Quit game would never run them) and arguments with control or format characters, so the confirmation shows exactly what runs. Turning the setting on or off rescans.
- **Launching**: RPCS3 `--no-gui --fullscreen`, Dolphin `-b -C Main.Display.Fullscreen=True -e`, Cemu `-f -g`. While a console game runs, only processes from the emulator's folder that started after Play are the game (`launch.Plan.FolderOnly`), so a boot script, a launcher that stays in the tray and an emulator that was open already don't keep the session going. A boot script that's slow to start the emulator (a figure portal's controller first) is waited for up to 15 minutes while it runs. *Ask who plays* asks for console games too. Auto never hands a console game an Xbox controller (the emulator reads the DualSense itself); saves steps and Syncer registration skip them; a running emulator isn't taken for one of its games.
- **Launch options** (any game): the program (picked in Windows' file picker), the arguments it starts with, and arguments that make it close the game again (`quitArgs`). Nothing is saved before the person confirms the exact command lines in a Windows dialog, which the interface can't answer for them; Cancel drops a program picked in the form. Changing only the arguments keeps the program and its folder check (`userArgs`, not `userExe`). Quit game runs the program with the quit arguments only when it isn't the game itself (a script or launcher, not the emulator), and ends the game after 20 s if it's still running. This starts a console game through a launcher or script (a figure portal's boot script, say). Scans keep them; *Start with <emulator>* goes back.
- **Art**: console games skip the Steam store and PCGamingWiki (a PC game of the same name isn't them). SteamGridDB is searched by the title without the serial, and the pictures the game came with fill the rest: a PS3 disc's 1920×1080 `PIC1.PNG` is the backdrop (and the hero and cover when there's no other), `ICON0.PNG` the icon, Dolphin's downloaded covers the cover. Local files are only read as PNG or JPEG, within the download limits.
- **Playtime** is kept per console and serial (`console:ps3:bles01689`), never under a PC game's title.
- **Settings**: *Add console games from emulators* (`detectEmulators`, on, per PC like the emulators), and a *Consoles* library that can be hidden (`hiddenSources: emulated`). A Seaglass older than 1.13 drops `emulated` from the hidden sources it syncs, so the synced settings carry `hideConsoles` too; when another PC's settings don't have it, the console games stay hidden or shown as they were.

## 1.12.0 (2026-10-29): big picture for game streaming

For streaming to another screen (Sunshine, Moonlight), Seaglass can open big picture from the command line without touching the settings, so desktop use stays as it was.
Expand Down
6 changes: 4 additions & 2 deletions docs/ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ These need the maintainer's accounts or decisions; nothing else can do them.
- **Theme packs**: user themes as token sets only (no JavaScript), on top of the per-layout CSS tokens.
- **Memory while playing**: the goal was a Go core under 50 MB with the interface closed. 1.1 got it from 79 MB to 62 MB; a bare Wails v3 app is 46 MB here, so what's left of ours is ~16 MB, nearly all the game database index. An interned or on-disk index would be the next step.
- **Wails**: v3 is still beta; keep the pin, and when upgrading recheck the single-instance mutex name, event payloads and bindings.
- **More emulators**: RetroArch (its playlists), Ryujinx, PCSX2, DuckStation and PPSSPP, each one `emulator` value in `internal/emulated`. Also: RPCS3's and Cemu's own playtime and trophies, and Dolphin's compressed GCZ images (only named by file today).
- **Syncer** (in Syncer's repo): use `gamekit/steam.Accounts`, and let games the launcher registers feed Syncer's discovery (for now they only help `gameStatus`).

## Goals
Expand All @@ -28,7 +29,7 @@ These need the maintainer's accounts or decisions; nothing else can do them.
- Syncer integration: save status per game, sync before launch, back up after exit, conflicts, and each person's playtime, achievements and settings following them between PCs.
- Pretty, responsive, secure and light: the interface unloads while you play.

**Not planned:** downloading or modifying games, or anything that bypasses copy protection (Seaglass only manages what's installed). Also not planned for now: emulators and ROMs, macOS or Linux, importing from Playnite.
**Not planned:** downloading or modifying games, or anything that bypasses copy protection (Seaglass only manages what's installed). Also not planned for now: macOS or Linux, importing from Playnite. Console games on emulators are in (1.13), for the emulators and games already on the PC: Seaglass doesn't download, set up or configure emulators, BIOS files, keys or games.

## Decisions

Expand All @@ -41,6 +42,7 @@ These need the maintainer's accounts or decisions; nothing else can do them.
| Shared code | `github.com/ApolloF/gamekit` (public, MIT): VDF (text and binary), Steam (folder, libraries, installed apps, account, Authenticode, tamper and emulator checks) and the Ludusavi manifest parser. Store detection (Epic, GOG, Xbox, …) stays in each app: Syncer only needs names, Seaglass needs launch details. |
| Syncer | Stays a separate app with a local API (named pipe, current user only). Needs Syncer 0.11+; see [syncer-api.md](syncer-api.md) |
| Store games not installed | Optional, **off by default** |
| Console games | Emulators found on the PC (RPCS3, Dolphin, Cemu) and the games their own settings point at; **on by default**, per PC. Started through the emulator full screen; the emulator handles the controller. Keys of their own (`emu:` + the booted file), so PC games are never touched |
| Desktop theme | Follows the Windows light or dark setting (Mica). Big picture is always dark. |
| Releases | Signed with the release key outside GitHub ([RELEASING.md](RELEASING.md)); the updater installs only signed releases |
| License | AGPL-3.0 (MIT until 1.4) |
Expand All @@ -50,7 +52,7 @@ These need the maintainer's accounts or decisions; nothing else can do them.
- **WebView:** strict CSP with no remote scripts. Navigation away from the app is blocked, devtools and the context menu are off in release, only bound services are exposed, and every value from the UI is validated in Go (paths must resolve inside known roots).
- **Network:** allowlisted hosts, HTTPS, size limits, and images re-encoded. The only executables ever downloaded are Seaglass's own update from GitHub Releases (checked against the signed `SHA256SUMS`) and Syncer's installer (checked against the SHA-256 GitHub computed on upload, never the same or an older version, and, while Syncer's releases are unsigned, only after a Yes and with the installer's own windows).
- **Secrets:** the SteamGridDB key, Steam Web API key, Epic token and GOG token are stored with DPAPI and never logged.
- **Parsers:** VDF, ini, JSON, lnk and ACF files, and emulators' achievement files, are treated as untrusted, with size limits and fuzz tests.
- **Parsers:** VDF, ini, JSON, lnk and ACF files, emulators' achievement files, and console emulators' settings and game files (PARAM.SFO, disc headers, games.yml, Dolphin.ini, Cemu's XML) are treated as untrusted, with size limits and fuzz tests; walks through their game folders are bounded in time, entries and depth.
- **Privileges:** Seaglass never elevates itself, and nothing is ever injected into games. The Syncer pipe is current-user only.

## Performance budget (Ryzen 7 7840HS, 16 GB)
Expand Down
4 changes: 4 additions & 0 deletions docs/releases/v1.13.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
**New**

- **Console games from your emulators.** Seaglass now finds RPCS3 (PS3), Dolphin (GameCube and Wii) and Cemu (Wii U) and the games they can play, and lists them next to your PC games, in desktop mode and every big picture layout. They get their real names, the console as their label, art where SteamGridDB or the game itself has some, and playtime like any other game. Play starts the emulator full screen, and you're back in Seaglass when it closes. The emulator handles your controller itself. Two copies of the same game (two regions) show their serial so you can tell them apart. Turn it off in Settings → Library, or hide the *Consoles* library.
- **Launch options.** In a game's menu, *Launch options…* sets the arguments its program starts with, and optionally the ones that make it close the game again. With *Choose program…* you can start a game through a script or another launcher.
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,15 @@ export function OpenFolder(id: number): $CancellablePromise<void> {
return $Call.ByID(1976215147, id);
}

/**
* PickProgram lets the user pick a program for the game's launch options
* without saving it: SetLaunch takes it when they save. "" when they
* cancel.
*/
export function PickProgram(id: number): $CancellablePromise<string> {
return $Call.ByID(3276071030, id);
}

/**
* RefreshMetadata fetches a game's metadata and art again.
*/
Expand Down Expand Up @@ -108,6 +117,15 @@ export function Rescan(): $CancellablePromise<void> {
return $Call.ByID(3200454951);
}

/**
* ResetLaunch goes back to starting the game the way the scan found
* (a console game: its emulator), forgetting the chosen program and
* arguments.
*/
export function ResetLaunch(id: number): $CancellablePromise<library$0.Game> {
return $Call.ByID(2299542789, id);
}

/**
* ScanState reports what the scanner is doing.
*/
Expand Down Expand Up @@ -151,6 +169,19 @@ export function SetHidden(id: number, on: boolean): $CancellablePromise<library$
return $Call.ByID(775406597, id, on);
}

/**
* SetLaunch sets the program that starts the game (exe: the one it has,
* or the one PickProgram just picked), its command-line arguments, and
* the ones that make it close the game again (quitArgs, "" for none:
* then Seaglass ends the game itself). Scans keep them. The person
* confirms the exact command lines in a Windows dialog first. A console
* game can be started through a launcher or script this way (a figure
* portal's boot script, say).
*/
export function SetLaunch(id: number, exe: string, args: string, quitArgs: string): $CancellablePromise<library$0.Game> {
return $Call.ByID(844615898, id, exe, args, quitArgs);
}

/**
* SetMatch says which game this is: a Steam app and its name. The choice
* is kept across scans, and metadata is fetched for it.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// This file is automatically generated. DO NOT EDIT

export type {
Console,
Game,
Meta
} from "./models.js";
Loading