commit 2b746b2ecb6f641b5c7dffdb067abfa42b346767 Author: Doloro1978 Date: Wed Aug 5 20:27:01 2026 +0100 everything diff --git a/.direnv/flake-profile b/.direnv/flake-profile new file mode 120000 index 0000000..0c05709 --- /dev/null +++ b/.direnv/flake-profile @@ -0,0 +1 @@ +flake-profile-1-link \ No newline at end of file diff --git a/.direnv/flake-profile-1-link b/.direnv/flake-profile-1-link new file mode 120000 index 0000000..01cc8ad --- /dev/null +++ b/.direnv/flake-profile-1-link @@ -0,0 +1 @@ +/nix/store/xx9iiifsq1l2l6rf90i8nx6c03srwhkf-nix-shell-env \ No newline at end of file diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..3550a30 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3b462cb --- /dev/null +++ b/.gitignore @@ -0,0 +1,23 @@ +node_modules + +# Output +.output +.vercel +.netlify +.wrangler +/.svelte-kit +/build + +# OS +.DS_Store +Thumbs.db + +# Env +.env +.env.* +!.env.example +!.env.test + +# Vite +vite.config.js.timestamp-* +vite.config.ts.timestamp-* diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..b6f27f1 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +engine-strict=true diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..7d74fe2 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,9 @@ +# Package Managers +package-lock.json +pnpm-lock.yaml +yarn.lock +bun.lock +bun.lockb + +# Miscellaneous +/static/ diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..936fb6d --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["svelte.svelte-vscode", "dbaeumer.vscode-eslint", "esbenp.prettier-vscode"] +} diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..21702ea --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,180 @@ +# AGENTS.md + +## What this is + +SvelteKit 5 (runes mode) static site that assembles Steam launch commands from +composable "program classes" (gamescope, MangoHud, Proton, obs-gamecapture, +gamemode). User flips toggles in a tabbed UI; the app renders one flat launch +command string like: + +``` +gamescope -f -- %command% mangohud waitforexecandrun %command% +``` + +Rendered by adapter-static (`prerender = true` in `src/routes/+page.ts`). + +## Core model (`src/lib/`) + +- `meow.ts` — the engine. `ArgEvaluatable` interface (`enabled()`, `envs()`, + `binary()`, `prefix()`, `suffix()`, `priority()`, `arguments()`); `ArgEval` + evaluates components: filter `enabled()`, sort by `priority()` (lowest = + outermost wrapper), envs as `VAR=VAL` space-joined, each program rendered + `prefix binary args suffix` (empty parts dropped), args sorted by `prio` and + rendered `arg val` (bare `arg` when val empty), then `%command%` appended. +- `programs.ts` — data types + `Program` class implementing `ArgEvaluatable` + from a `ProgramConfig`. Key fields: `enabled`, `name`, `binary`, `prefix`, + `suffix`, `priority`, `envs`, `args`, `options` (toggleable sub-flags), + `envSeparators` (per-var merge separators), option `conflicts` (see below). +- `presets.ts` — re-export index only; real factories live per-program in + `src/lib/preset/` (see file map). +- `preset/helpers.ts` — builders shared by presets: `envToggle(label, env, +val, category?)`, `group(program, labels)` (pairwise conflict keys). + (Mutually-exclusive flag choices like `-F` filter use ONE option with an + `args[].values` dropdown — do NOT reintroduce one-option-per-value.) + +### Semantics to keep in mind + +- Env vars with the same name merge into ONE var: values dedupe, joined by the + var's separator from `envSeparators` (default `,`). +- `conflicts` keys are `'programname'` (program-level) or + `'program:Option label'` (option-level). Only enabled vs enabled conflict; + conflicts are displayed as a non-blocking warning banner. +- Env merge destructuring: `var` is a reserved word in TS — use + `[varName, vals]` in destructures. +- Args are space-separated (`-W 1920`), never `arg=val`. + +## File map + +| file | role | +| ---------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | +| `src/lib/meow.ts` | evaluation engine (see above) | +| `src/lib/programs.ts` | types + `Program` adapter + `findConflicts()` | +| `src/lib/presets.ts` | re-exports the eight preset factories | +| `src/lib/preset/helpers.ts` | shared option builders | +| `src/lib/preset/gamescope.ts` | gamescope args (fullscreen, filter/scale dropdowns, HDR, VR, …) | +| `src/lib/preset/proton-ge.ts` | GloriousEggroll Proton env toggles | +| `src/lib/preset/proton-cachyos.ts` | CachyOS Proton env toggles (largest set) | +| `src/lib/preset/obs.ts` | obs-gamecapture CLI | +| `src/lib/preset/gamemode.ts` | `gamemoderun` wrapper (priority 5 = outermost) | +| `src/lib/preset/mangohud.ts` | MANGOHUD env options + `MANGOHUD_CONFIG` separator merge | +| `src/lib/preset/mesa.ts` | Mesa Vulkan layers env toggles (anti-lag, overlay layer, fps-monitor) | +| `src/lib/preset/prime.ts` | `prime-run` wrapper for NVIDIA PRIME offload | +| `src/lib/ProgramForm.svelte` | per-program editor: enabled checkbox, options list, Advanced `
` (name/binary/prefix/suffix/priority, env vars, args, env separators) | +| `src/lib/decode.ts` | `applyCommand(cmd, programs)` — enable options/programs a pasted command references (only ever enables, never disables) | +| `src/routes/+page.svelte` | tabs + `pre.result` command preview + conflict banner; localStorage persistence; decode textarea | +| `launch_args_env_vars.md` | reference: gamescope args, Proton env-var tables, OBS, gamemode | +| `steam-tinker-launcher.md` | reference: STL v14 docs (gamescope CLI catalog §3.1–3.6, mangohud, proton, dxvk, wine) | + +## Conventions + +- New programs are preset factory functions in `src/lib/preset/.ts` + returning a `ProgramConfig`, re-exported from `presets.ts`, imported into + `+page.svelte`'s `programs` array. +- Every option carries a `category: string`; ProgramForm groups options under + category headings (keep categories consistent within a preset). +- Every option should also carry a `desc: string`; it appears as a hover + tooltip (1 s delay, CSS-only) next to the option row. +- Default new presets to `enabled: false` so the default output stays stable. +- Preset files ≤ ~140 lines; split if they grow (that's why the directory + exists). Shared builders go in `helpers.ts`. +- `+page.svelte` uses PURE TAB indentation — never build edit oldText with + spaces. +- Mutual exclusivity is expressed via `conflicts` (options) or `group()` + (helpers), never via extra app state. +- `user-select: none` on tabs/labels to stop double-click selection; inputs and + `pre.result` stay selectable. + +## Adding options & programs (agent playbook) + +Every task in this repo ends with the verification loop below; a change is not +done until all three pass. + +### Add an option to an existing program + +1. Edit `src/lib/preset/.ts` — the program's factory, inside + `options: [...]`. Mine the flag/env catalogs from `launch_args_env_vars.md` + and `steam-tinker-launcher.md`. +2. Pick the builder (from `./helpers`): + + - Env toggle (checkbox sets `VAR=1`): + `envToggle('Label', 'ENV_VAR', '1', 'Category')` — pass a custom value as + the 3rd arg for non-`1` values. + - Flag with a value (gets an editable input in the UI while enabled): + + ```ts + { label: 'Mouse sensitivity', enabled: false, args: [{ arg: '-s', val: '1.0', prio: 0 }], category: 'Window' } + ``` + + - Flag with a fixed value set (gets a ` + Enabled + + +
+
+

Options

+ {#if cats.length > 1} +
+ + {#each cats as c (c)} + + {/each} +
+ {/if} + {#each visibleOpts as opt (opt.label)} +
+ + {#if opt.desc}{opt.desc}{/if} + {#if opt.enabled} + {#each opt.args ?? [] as arg (arg.arg)} + {#if arg.values || arg.val || latched.includes(argKey(opt, arg))} + {#if arg.values} + + {:else} + latch(opt, arg)} + /> + {/if} + {/if} + {/each} + {/if} +
+ {/each} +
+ +
+ Advanced: name, binary, env vars & raw args + +
+ + + + + +
+ +
+

Env vars

+ {#each config.envs ?? [] as env, i (i)} +
+ + + +
+ {/each} + +
+ +
+

Env separators

+ {#each config.envSeparators ?? [] as s, i (i)} +
+ + + +
+ {/each} + +
+ +
+

Args

+ {#each config.args ?? [] as a, i (i)} +
+ + + + +
+ {/each} + +
+
+
+ + diff --git a/src/lib/assets/favicon.svg b/src/lib/assets/favicon.svg new file mode 100644 index 0000000..cc5dc66 --- /dev/null +++ b/src/lib/assets/favicon.svg @@ -0,0 +1 @@ +svelte-logo \ No newline at end of file diff --git a/src/lib/decode.ts b/src/lib/decode.ts new file mode 100644 index 0000000..7fcacc3 --- /dev/null +++ b/src/lib/decode.ts @@ -0,0 +1,157 @@ +import type { ProgramConfig } from './programs'; + +/** Tokens that carry no option meaning. */ +const IGNORED = new Set(['%command%', 'env', '--']); + +/** gamescope long aliases our presets store as shorts. */ +const ALIASES: Record = { + '--fullscreen': '-f', + '--borderless': '-b', + '--nested-unfocused-refresh': '-o' +}; + +/** Split a shell-ish command, keeping quoted tokens together. */ +export function tokenize(cmd: string): string[] { + const out: string[] = []; + let cur = ''; + let quote: '"' | "'" | null = null; + for (let i = 0; i < cmd.length; i++) { + const ch = cmd[i]; + if (quote) { + if (ch === quote) quote = null; + else cur += ch; + } else if (ch === '"' || ch === "'") { + quote = ch; + } else if (ch === '\\') { + cur += cmd[++i] ?? ''; + } else if (/\s/.test(ch)) { + if (cur) { + out.push(cur); + cur = ''; + } + } else { + cur += ch; + } + } + if (cur) out.push(cur); + return out; +} + +/** + * Enable the options (and their programs) that a pasted launch command + * references. Only ever ENABLES — nothing is disabled or reverted. Returns + * the tokens that matched nothing. + */ +export function applyCommand(cmd: string, programs: ProgramConfig[]): string[] { + const tokens = tokenize(cmd).map((t) => ALIASES[t] ?? t); + const used = new Set(); + const unmatched: string[] = []; + + const enable = (p: ProgramConfig, o: { enabled: boolean }): void => { + p.enabled = true; + o.enabled = true; + }; + + // Pass 1: VAR=VAL env tokens + for (let i = 0; i < tokens.length; i++) { + const t = tokens[i]; + const eq = t.indexOf('='); + if (eq <= 0) continue; + const varName = t.slice(0, eq); + const raw = t.slice(eq + 1); + const vals = raw.split(','); + let hit = false; + for (const p of programs) { + for (const o of p.options ?? []) { + for (const e of o.envs ?? []) { + if (e.var === varName && (e.val === raw || vals.includes(e.val))) { + enable(p, o); + hit = true; + } + } + } + } + if (hit) used.add(i); + else unmatched.push(t); + } + + // Pass 2: program prefixes/binaries, then option args + const matched = (i: number): void => { + used.add(i); + const idx = unmatched.indexOf(tokens[i]); + if (idx !== -1) unmatched.splice(idx, 1); + }; + for (let i = 0; i < tokens.length; i++) { + const t = tokens[i]; + if (used.has(i) || IGNORED.has(t)) continue; + let hit = false; + for (const p of programs) { + if (p.prefix === t || p.binary === t) { + p.enabled = true; + hit = true; + break; + } + } + if (!hit) { + // option-contributed prefix/suffix (e.g. FPS BOMB fix); compare quote-stripped + for (const p of programs) { + for (const o of p.options ?? []) { + if ( + (o.prefix ?? '').replaceAll('"', '') === t || + (o.suffix ?? '').replaceAll('"', '') === t + ) { + enable(p, o); + hit = true; + break; + } + } + if (hit) break; + } + } + if (hit) { + matched(i); + continue; + } + // exact arg+value pair (e.g. '-F fsr' → the fsr filter option) + for (const p of programs) { + for (const o of p.options ?? []) { + for (const a of o.args ?? []) { + if (a.arg === t && a.val && tokens[i + 1] === a.val) { + enable(p, o); + used.add(i + 1); + hit = true; + break; + } + } + if (hit) break; + } + if (hit) break; + } + if (hit) { + matched(i); + continue; + } + // bare flag, or value arg consuming the next token + for (const p of programs) { + for (const o of p.options ?? []) { + for (const a of o.args ?? []) { + if (a.arg === t) { + enable(p, o); + if (a.val && i + 1 < tokens.length && !used.has(i + 1) && !IGNORED.has(tokens[i + 1])) { + a.val = tokens[i + 1]; + used.add(i + 1); + } + hit = true; + break; + } + } + if (hit) break; + } + if (hit) break; + } + if (hit) matched(i); + else unmatched.push(t); + } + + return unmatched; +} diff --git a/src/lib/index.ts b/src/lib/index.ts new file mode 100644 index 0000000..856f2b6 --- /dev/null +++ b/src/lib/index.ts @@ -0,0 +1 @@ +// place files you want to import through the `$lib` alias in this folder. diff --git a/src/lib/meow.ts b/src/lib/meow.ts new file mode 100644 index 0000000..7be6d9f --- /dev/null +++ b/src/lib/meow.ts @@ -0,0 +1,62 @@ +export interface ArgEvaluatable { + enabled(): boolean; + envs(): EnvVar[]; + binary(): string; + prefix(): string; + suffix(): string; + priority(): number; + arguments(): Arguments[]; +} + +export type EnvVar = { + var: string; + val: string; +}; + +export type Arguments = { + arg: string; + val: string; + prio: number; + /** Fixed value set — renders a dropdown in the UI instead of a text input. */ + values?: string[]; + /** JS regex source the value input must match (HTML pattern attr); empty/cleared always valid. */ + pattern?: string; +}; + +function compareArgPrio(a: Arguments, b: Arguments) { + return a.prio - b.prio; +} + +export class ArgEval { + Components: ArgEvaluatable[]; + + constructor(components: ArgEvaluatable[]) { + this.Components = components; + } + + public eval(): string { + const active = this.Components.filter((c) => c.enabled()); + + const env = active.flatMap((c) => c.envs().map((e) => e.var + '=' + e.val)).join(' '); + + const progs = [...active] + .sort((a, b) => a.priority() - b.priority()) + .map((c) => + [ + c.prefix(), + c.binary(), + ...c + .arguments() + .sort(compareArgPrio) + .flatMap((a) => (a.val ? `${a.arg} ${a.val}` : a.arg)), + c.suffix() + ] + .filter((s) => s !== '') + .join(' ') + ) + .filter((s) => s !== '') + .join(' '); + + return [env, progs, '%command%'].filter((s) => s !== '').join(' '); + } +} diff --git a/src/lib/preset/gamemode.ts b/src/lib/preset/gamemode.ts new file mode 100644 index 0000000..016715e --- /dev/null +++ b/src/lib/preset/gamemode.ts @@ -0,0 +1,14 @@ +import type { ProgramConfig } from '../programs'; +export function gameMode(): ProgramConfig { + return { + name: 'gamemode', + enabled: false, + binary: '', + prefix: 'gamemoderun', + suffix: '', + priority: 5, + envs: [], + args: [], + options: [] + }; +} diff --git a/src/lib/preset/gamescope.ts b/src/lib/preset/gamescope.ts new file mode 100644 index 0000000..a0ed4a1 --- /dev/null +++ b/src/lib/preset/gamescope.ts @@ -0,0 +1,358 @@ +import type { ProgramConfig } from '../programs'; +import { NUM, NUM_DEC } from './helpers'; +export function gamescope(): ProgramConfig { + return { + name: 'gamescope', + enabled: true, + binary: 'gamescope', + prefix: '', + suffix: '--', + priority: 10, + envs: [], + args: [], + options: [ + // Window + { + label: 'Fullscreen', + enabled: true, + args: [{ arg: '-f', val: '', prio: 0 }], + category: 'Window', + desc: 'Run the game fullscreen instead of embedded in a window.' + }, + { + label: 'Borderless', + enabled: false, + args: [{ arg: '-b', val: '', prio: 0 }], + category: 'Window', + desc: 'Borderless fullscreen window (native resolution, no decorations).' + }, + { + label: 'Steam integration', + enabled: false, + args: [{ arg: '-e', val: '', prio: 0 }], + category: 'Window', + desc: 'Enable Steam integration (gamepad navigation in the overlay).' + }, + { + label: 'Force windows fullscreen', + enabled: false, + args: [{ arg: '--force-windows-fullscreen', val: '', prio: 0 }], + category: 'Window', + desc: 'Use the old Windows-style fullscreen path for the game.' + }, + { + label: 'Force grab cursor', + enabled: false, + args: [{ arg: '--force-grab-cursor', val: '', prio: 0 }], + category: 'Window', + desc: 'Force-grab the cursor inside the window (for games that do not request it).' + }, + { + label: 'Force grab keyboard', + enabled: false, + args: [{ arg: '-g', val: '', prio: 0 }], + category: 'Window', + desc: 'Force-grab keyboard input; the escape key toggles the grab.' + }, + { + label: 'Mouse sensitivity', + enabled: false, + args: [{ arg: '-s', val: '1.0', prio: 0, pattern: NUM_DEC }], + category: 'Window', + desc: 'Mouse sensitivity multiplier for the game (1.0 = unchanged).' + }, + { + label: 'Backend', + enabled: false, + args: [ + { + arg: '--backend', + val: 'wayland', + prio: 0, + values: ['auto', 'drm', 'sdl', 'openvr', 'headless', 'wayland'] + } + ], + category: 'Window', + desc: 'Output backend: drm = fullscreen on the display, sdl = window, wayland = nested Wayland compositor, headless = no output.' + }, + { + label: 'Force orientation', + enabled: false, + args: [ + { + arg: '--force-orientation', + val: 'left', + prio: 0, + values: ['left', 'right', 'upside_down'] + } + ], + category: 'Window', + desc: 'Force a specific screen orientation for the session.' + }, + { + label: 'Generate DRM mode', + enabled: false, + args: [ + { + arg: '--generate-drm-mode', + val: 'cvt', + prio: 0, + values: ['cvt', 'fixed'] + } + ], + category: 'Advanced', + desc: 'Generate a custom DRM mode (cvt) instead of using the native one.' + }, + // Resolution + { + label: 'Output width', + enabled: false, + args: [{ arg: '-W', val: '1920', prio: 0, pattern: NUM }], + category: 'Resolution', + desc: 'Output (display) resolution width in pixels.' + }, + { + label: 'Output height', + enabled: false, + args: [{ arg: '-H', val: '1080', prio: 0, pattern: NUM }], + category: 'Resolution', + desc: 'Output (display) resolution height in pixels.' + }, + { + label: 'Game width', + enabled: false, + args: [{ arg: '-w', val: '1920', prio: 0, pattern: NUM }], + category: 'Resolution', + desc: 'Internal game resolution width; scaled to the output.' + }, + { + label: 'Game height', + enabled: false, + args: [{ arg: '-h', val: '1080', prio: 0, pattern: NUM }], + category: 'Resolution', + desc: 'Internal game resolution height; scaled to the output.' + }, + // Framerate + { + label: 'Limit to 60 fps', + enabled: false, + args: [{ arg: '-r', val: '60', prio: 0, pattern: NUM }], + category: 'Framerate', + desc: 'Cap the game frame rate to the given FPS (nested refresh rate).' + }, + { + label: 'Unfocused FPS limit', + enabled: false, + args: [{ arg: '-o', val: '30', prio: 0, pattern: NUM }], + category: 'Framerate', + desc: 'Frame rate limit while the window is unfocused (does not work on the Wayland backend).' + }, + // Filter + { + label: 'Filter', + enabled: false, + args: [ + { + arg: '-F', + val: 'fsr', + prio: 0, + values: ['none', 'linear', 'nearest', 'fsr', 'nis', 'pixel'] + } + ], + category: 'Filter', + desc: 'Filter/upscaling algorithm applied to the game frame.' + }, + { + label: 'Sharpness', + enabled: false, + args: [{ arg: '--sharpness', val: '20', prio: 0, pattern: NUM }], + category: 'Filter', + desc: 'FSR sharpness: 0 = sharpest, 20 = softest.' + }, + // Scale + { + label: 'Scale', + enabled: false, + args: [ + { + arg: '-S', + val: 'integer', + prio: 0, + values: ['none', 'auto', 'integer', 'fit', 'fill', 'stretch'] + } + ], + category: 'Scale', + desc: 'Scaling mode used to fit the game to the output.' + }, + { + label: 'Max scale factor', + enabled: false, + args: [{ arg: '-m', val: '2', prio: 0, pattern: NUM }], + category: 'Scale', + desc: 'Maximum scale factor (1 = disable scaling up).' + }, + // HDR + { + label: 'HDR', + enabled: false, + args: [{ arg: '--hdr-enabled', val: '', prio: 0 }], + category: 'HDR', + desc: 'Enable HDR output (requires an HDR-capable display and game).' + }, + { + label: 'HDR wide gamut for SDR', + enabled: false, + args: [{ arg: '--hdr-wide-gammut-for-sdr', val: '', prio: 0 }], + category: 'HDR', + desc: 'Widen the gamut of SDR content under HDR output.' + }, + { + label: 'HDR SDR content nits', + enabled: false, + args: [{ arg: '--hdr-sdr-content-nits', val: '400', prio: 0, pattern: NUM }], + category: 'HDR', + desc: 'Brightness assumed for SDR content under HDR output (nits).' + }, + { + label: 'HDR inverse tone mapping', + enabled: false, + args: [{ arg: '--hdr-itm-enable', val: '', prio: 0 }], + category: 'HDR', + desc: 'Apply inverse tone mapping to SDR content (SDR → HDR).' + }, + { + label: 'HDR ITM SDR nits', + enabled: false, + args: [{ arg: '--hdr-itm-sdr-nits', val: '100', prio: 0, pattern: NUM }], + category: 'HDR', + desc: 'Assumed SDR brightness (nits) used by inverse tone mapping.' + }, + { + label: 'HDR ITM target nits', + enabled: false, + args: [{ arg: '--hdr-itm-target-nits', val: '1000', prio: 0, pattern: NUM }], + category: 'HDR', + desc: 'Target HDR brightness (nits) for inverse tone mapping.' + }, + // VR + { + label: 'OpenVR', + enabled: false, + args: [{ arg: '--openvr', val: '', prio: 0 }], + category: 'VR', + desc: 'Run as the VR compositor (headset output).' + }, + { + label: 'VR overlay show immediately', + enabled: false, + args: [{ arg: '--vr-overlay-show-immediately', val: '', prio: 0 }], + category: 'VR', + desc: 'Show the VR overlay immediately instead of waiting for the game.' + }, + { + label: 'VR overlay control bar', + enabled: false, + args: [{ arg: '--vr-overlay-enable-control-bar', val: '', prio: 0 }], + category: 'VR', + desc: 'Add a control bar to the VR overlay.' + }, + { + label: 'VR overlay control bar keyboard', + enabled: false, + args: [{ arg: '--vr-overlay-enable-control-bar-keyboard', val: '', prio: 0 }], + category: 'VR', + desc: 'Add a keyboard button to the VR overlay control bar.' + }, + { + label: 'VR overlay control bar close', + enabled: false, + args: [{ arg: '--vr-overlay-enable-control-bar-close', val: '', prio: 0 }], + category: 'VR', + desc: 'Add a close button to the VR overlay control bar.' + }, + { + label: 'VR overlay modal', + enabled: false, + args: [{ arg: '--vr-overlay-modal', val: '', prio: 0 }], + category: 'VR', + desc: 'Make the VR overlay modal (blocks input to the game).' + }, + { + label: 'VR scroll speed', + enabled: false, + args: [{ arg: '--vr-scrolls-speed', val: '8.0', prio: 0, pattern: NUM_DEC }], + category: 'VR', + desc: 'Scroll speed for scrolling views in the VR overlay.' + }, + // Compositor + { + label: 'Immediate flips', + enabled: false, + args: [{ arg: '--immediate-flips', val: '', prio: 0 }], + category: 'Compositor', + desc: 'Present frames immediately without waiting for vblank (can cause tearing).' + }, + { + label: 'Adaptive sync', + enabled: false, + args: [{ arg: '--adaptive-sync', val: '', prio: 0 }], + category: 'Compositor', + desc: 'Enable adaptive sync (VRR) when the display supports it.' + }, + { + label: 'Force composition', + enabled: false, + args: [{ arg: '--force-composition', val: '', prio: 0 }], + category: 'Compositor', + desc: 'Force the compositor to always recomposite frames.' + }, + { + label: 'Expose Wayland', + enabled: false, + args: [{ arg: '--expose-wayland', val: '', prio: 0 }], + category: 'Compositor', + desc: 'Expose the game as a Wayland surface (e.g. for screen capture).' + }, + // Advanced + { + label: 'Debug HUD', + enabled: false, + args: [{ arg: '--debug-hud', val: '', prio: 0 }], + category: 'Advanced', + desc: "Show gamescope's internal debug HUD (frametime, latency)." + }, + { + label: 'Realtime scheduling', + enabled: false, + args: [{ arg: '--rt', val: '', prio: 0 }], + category: 'Advanced', + desc: 'Give gamescope realtime scheduling priority.' + }, + { + label: 'Headless', + enabled: false, + args: [{ arg: '--headless', val: '', prio: 0 }], + category: 'Advanced', + desc: 'Run without creating an output (no window).' + }, + // Overlay + { + label: 'MangoHud overlay', + enabled: false, + args: [{ arg: '--mangoapp', val: '', prio: 0 }], + conflicts: ['mangohud'], + category: 'Overlay', + desc: "Start MangoHud's app (--mangoapp) inside the game — conflicts with the MangoHud program." + }, + // Command + { + label: 'FPS BOMB fix', + enabled: false, + prefix: 'LD_PRELOAD=', + suffix: 'LD_PRELOAD="$LD_PRELOAD"', + category: 'Command', + desc: 'Clear Steam preloaded libs for gamescope, restore them for the game (fixes the Steam FPS-bomb stutter).' + } + ] + }; +} diff --git a/src/lib/preset/helpers.ts b/src/lib/preset/helpers.ts new file mode 100644 index 0000000..f25df30 --- /dev/null +++ b/src/lib/preset/helpers.ts @@ -0,0 +1,37 @@ +import type { ProgramOption } from '../programs'; + +/** Positive integer, e.g. resolutions, FPS limits. */ +export const NUM = '^\\d+$'; +/** Signed decimal, e.g. sensitivities, scales. */ +export const NUM_DEC = '^-?\\d+(\\.\\d+)?$'; + +/** Human-readable descriptions for the exported patterns (shown in banner errors). */ +export const PATTERN_HINTS: Record = { + [NUM]: 'a whole number (e.g. 1920, 60)', + [NUM_DEC]: 'a number (e.g. 1.0, 8.5, -2.5)' +}; + +export function envToggle( + label: string, + env: string, + val = '1', + category?: string, + desc?: string +): ProgramOption { + return { + label, + enabled: false, + envs: [{ var: env, val }], + ...(category && { category }), + ...(desc && { desc }) + }; +} + +/** Pairwise 'program:label' conflict keys for a mutually-exclusive option group. */ +export function group(program: string, labels: string[]): Record { + const out: Record = {}; + for (const l of labels) { + out[l] = labels.filter((x) => x !== l).map((x) => `${program}:${x}`); + } + return out; +} diff --git a/src/lib/preset/mangohud.ts b/src/lib/preset/mangohud.ts new file mode 100644 index 0000000..2f7cbf7 --- /dev/null +++ b/src/lib/preset/mangohud.ts @@ -0,0 +1,37 @@ +import type { ProgramConfig } from '../programs'; +export function mangoHud(): ProgramConfig { + return { + name: 'mangohud', + enabled: true, + binary: '', + prefix: '', + suffix: '', + priority: 30, + envs: [], + args: [], + envSeparators: [{ var: 'MANGOHUD_CONFIG', sep: ',' }], + options: [ + { + label: 'Enable overlay', + enabled: true, + envs: [{ var: 'MANGOHUD', val: '1' }], + category: 'Overlay', + desc: 'Enable the MangoHud overlay (MANGOHUD=1).' + }, + { + label: 'Limit to 60 fps', + enabled: false, + envs: [{ var: 'MANGOHUD_CONFIG', val: 'fps_limit=60' }], + category: 'Overlay', + desc: 'Cap the frame rate to 60 FPS via MangoHud (fps_limit).' + }, + { + label: 'Show FPS', + enabled: false, + envs: [{ var: 'MANGOHUD_CONFIG', val: 'fps' }], + category: 'Overlay', + desc: 'Show the FPS counter in the overlay.' + } + ] + }; +} diff --git a/src/lib/preset/mesa.ts b/src/lib/preset/mesa.ts new file mode 100644 index 0000000..f955e13 --- /dev/null +++ b/src/lib/preset/mesa.ts @@ -0,0 +1,38 @@ +import type { ProgramConfig } from '../programs'; +import { envToggle } from './helpers'; +export function mesaLayers(): ProgramConfig { + return { + name: 'mesa-layers', + enabled: false, + binary: '', + prefix: '', + suffix: '', + priority: 40, + envs: [], + args: [], + options: [ + // Layers + envToggle( + 'Anti-lag layer', + 'ENABLE_LAYER_MESA_ANTI_LAG', + '1', + 'Layers', + 'AMD Anti-Lag via the Mesa layer (RADV, Mesa 25.3+, needs distro mesa-git).' + ), + envToggle( + 'Vulkan overlay layer', + 'VK_LOADER_LAYERS_ENABLE', + 'VK_LAYER_MESA_overlay', + 'Layers', + "Enable Mesa's Vulkan overlay layer (FPS/frametime HUD)." + ), + envToggle( + 'FPS monitor layer', + 'ENABLE_LAYER_MESA_FPS_MONITOR', + '1', + 'Layers', + "Enable Mesa's FPS monitor layer (CachyOS mesa-git)." + ) + ] + }; +} diff --git a/src/lib/preset/obs.ts b/src/lib/preset/obs.ts new file mode 100644 index 0000000..bc462b6 --- /dev/null +++ b/src/lib/preset/obs.ts @@ -0,0 +1,84 @@ +import type { ProgramConfig } from '../programs'; +import { envToggle, NUM } from './helpers'; +export function obsGameCapture(): ProgramConfig { + return { + name: 'obs-gamecapture', + enabled: false, + binary: 'obs-gamecapture', + prefix: '', + suffix: '--', + priority: 10, + envs: [], + args: [], + options: [ + // Video + { + label: 'Capture width', + enabled: false, + args: [{ arg: '--width', val: '1920', prio: 0, pattern: NUM }], + category: 'Video', + desc: 'Captured frame width (0 = follow the game window).' + }, + { + label: 'Capture height', + enabled: false, + args: [{ arg: '--height', val: '1080', prio: 0, pattern: NUM }], + category: 'Video', + desc: 'Captured frame height (0 = follow the game window).' + }, + { + label: 'Capture frame rate', + enabled: false, + args: [{ arg: '--rate', val: '60', prio: 0, pattern: NUM }], + category: 'Video', + desc: 'Capture frame rate.' + }, + // Audio + { + label: 'No sound', + enabled: false, + args: [{ arg: '--no-sound', val: '', prio: 0 }], + category: 'Audio', + desc: "Do not capture the game's audio." + }, + // Capture + { + label: 'Force game capture', + enabled: false, + envs: [{ var: 'OBS_FORCE_GAMECAPTURE', val: '1' }], + conflicts: ['obs-gamecapture:Disable game capture detection'], + category: 'Capture', + desc: 'Force game-capture mode (instead of desktop capture).' + }, + { + label: 'Disable game capture detection', + enabled: false, + envs: [{ var: 'OBS_NO_GAMECAPTURE', val: '1' }], + conflicts: ['obs-gamecapture:Force game capture'], + category: 'Capture', + desc: 'Disable automatic game-capture detection.' + }, + envToggle( + 'Disable XLIB capture', + 'OBS_NO_XLIB', + '1', + 'Capture', + 'Disable the X11 XLIB capture path.' + ), + envToggle( + 'Disable XSHM capture', + 'OBS_NO_XSHM', + '1', + 'Capture', + 'Disable the X11 shared-memory capture path.' + ), + envToggle( + 'Disable VK capture', + 'OBS_NO_VKCAPTURE', + '1', + 'Capture', + 'Disable the Vulkan capture path.' + ) + ] + }; +} diff --git a/src/lib/preset/prime.ts b/src/lib/preset/prime.ts new file mode 100644 index 0000000..3f8c206 --- /dev/null +++ b/src/lib/preset/prime.ts @@ -0,0 +1,14 @@ +import type { ProgramConfig } from '../programs'; +export function prime(): ProgramConfig { + return { + name: 'prime', + enabled: false, + binary: '', + prefix: 'prime-run', + suffix: '', + priority: 6, + envs: [], + args: [], + options: [] + }; +} diff --git a/src/lib/preset/proton-cachyos.ts b/src/lib/preset/proton-cachyos.ts new file mode 100644 index 0000000..ea4b398 --- /dev/null +++ b/src/lib/preset/proton-cachyos.ts @@ -0,0 +1,411 @@ +import type { ProgramConfig } from '../programs'; +import { envToggle, group } from './helpers'; + +const cachyNv = group('proton-cachyos', [ + 'NVIDIA libs', + 'NVIDIA libs (64-bit only)', + 'nvcuda.dll only', + 'nvencodeapi.dll only', + 'nvml.dll only', + 'nvoptix.dll only' +]); + +export function protonCachyOS(): ProgramConfig { + return { + name: 'proton-cachyos', + enabled: false, + binary: '', + prefix: '', + suffix: '', + priority: 20, + envs: [], + args: [], + options: [ + // Debug + envToggle( + 'Debug log', + 'PROTON_LOG', + '1', + 'Debug', + "Write the Proton/dxvk/wine debug log to the game's prefix directory." + ), + envToggle( + 'Wait for debugger', + 'PROTON_WAIT_ATTACH', + '1', + 'Debug', + 'Pause the game and wait for a debugger to attach.' + ), + envToggle( + 'DXVK HUD (fps)', + 'DXVK_HUD', + 'fps', + 'Debug', + "Show DXVK's HUD with FPS and frame timing (values: fps, full, devinfo, …)." + ), + // Performance + envToggle( + 'Disable fsync', + 'PROTON_NO_FSYNC', + '1', + 'Performance', + 'Disable fsync (fast sync via futexes).' + ), + envToggle( + 'Force large address aware', + 'PROTON_FORCE_LARGE_ADDRESS_AWARE', + '1', + 'Performance', + 'Force 32-bit games to see a large address space.' + ), + envToggle( + 'Delay memory freeing', + 'PROTON_HEAP_DELAY_FREE', + '1', + 'Performance', + 'Delay freeing memory to improve performance in some games.' + ), + envToggle( + 'Enable seccomp-bpf', + 'PROTON_USE_SECCOMP', + '1', + 'Performance', + 'Enable seccomp-bpf sandboxing inside the wine process.' + ), + envToggle( + 'dxvk-low-latency', + 'PROTON_DXVK_LOWLATENCY', + '1', + 'Performance', + 'Enable DXVK low-latency mode (NVIDIA Reflex integration).' + ), + envToggle( + 'Vulkan Reflex', + 'DXVK_NVAPI_VKREFLEX', + '1', + 'Performance', + 'Enable Vulkan Reflex (NVIDIA).' + ), + envToggle( + 'Low latency layer', + 'LOW_LATENCY_LAYER', + '1', + 'Performance', + 'Enable the low-latency layer (driver-level latency reduction).' + ), + // Graphics + envToggle( + 'wined3d (OpenGL)', + 'PROTON_USE_WINED3D', + '1', + 'Graphics', + 'Use wined3d (OpenGL) instead of DXVK for D3D9/11.' + ), + envToggle( + 'Disable D3D11', + 'PROTON_NO_D3D11', + '1', + 'Graphics', + 'Disable DXVK for D3D11 (fall back to wined3d).' + ), + envToggle( + 'Disable D3D10/DXGI', + 'PROTON_NO_D3D10', + '1', + 'Graphics', + 'Disable DXVK for D3D10 (fall back to wined3d).' + ), + envToggle( + 'DXVK d3d8', + 'PROTON_DXVK_D3D8', + '1', + 'Graphics', + "Use DXVK's d3d8 implementation for DirectX 8 games." + ), + envToggle( + 'Disable NVAPI', + 'PROTON_DISABLE_NVAPI', + '1', + 'Graphics', + 'Disable NVIDIA NVAPI support.' + ), + envToggle( + 'Fix miscolored video', + 'WINE_DO_NOT_CREATE_DXGI_DEVICE_MANAGER', + '1', + 'Graphics', + 'Fix wrong colors in videos (disable the DXGI device manager).' + ), + envToggle( + 'Disable Vulkan OPWR', + 'WINE_DISABLE_VULKAN_OPWR', + '1', + 'Graphics', + 'Disable Vulkan one-present-wait renderer workarounds.' + ), + envToggle( + 'Hide NVIDIA GPUs as AMD', + 'PROTON_HIDE_NVIDIA_GPU', + '1', + 'Graphics', + 'Hide NVIDIA GPUs from the game (reports AMD instead).' + ), + envToggle( + 'Integer scaling', + 'WINE_FULLSCREEN_INTEGER_SCALING', + '1', + 'Graphics', + 'Integer scaling for fullscreen games.' + ), + envToggle( + 'Force FNA D3D11', + 'FNA3D_FORCE_DRIVER', + 'D3D11', + 'Graphics', + 'Force FNA games (FNA3D) to use the D3D11 backend.' + ), + envToggle( + 'D9VK for d3d9', + 'PROTON_USE_D9VK', + '1', + 'Graphics', + 'Use D9VK (DX9→Vulkan) instead of wined3d for D3D9.' + ), + envToggle( + 'dxvk-sarek', + 'PROTON_DXVK_SAREK', + '1', + 'Graphics', + 'Use the dxvk-sarek fork (custom shader workarounds).' + ), + envToggle( + 'D7VK for DX7 and lower', + 'PROTON_D7VK_DDRAW', + '1', + 'Graphics', + 'Use D7VK for DirectX 7 and older games.' + ), + // Compatibility + envToggle( + 'Create S: drive', + 'PROTON_SET_GAME_DRIVE', + '1', + 'Compatibility', + "Create an S: drive in the prefix (maps to the game's install dir)." + ), + envToggle( + 'Limit GL extension string', + 'PROTON_OLD_GL_STRING', + '1', + 'Compatibility', + 'Hide GL extensions newer than the game expects.' + ), + envToggle( + 'KDE windowing hacks', + 'WINE_USE_KWIN_HACKS', + '1', + 'Compatibility', + 'Enable KDE-specific windowing fixes.' + ), + envToggle( + 'Per-game shader cache', + 'PROTON_LOCAL_SHADER_CACHE', + '1', + 'Compatibility', + 'Use a per-game shader cache directory.' + ), + // NVIDIA + { + label: 'NVIDIA libs', + enabled: false, + envs: [{ var: 'PROTON_NVIDIA_LIBS', val: '1' }], + conflicts: cachyNv['NVIDIA libs'], + category: 'NVIDIA', + desc: 'Include NVIDIA proprietary libs in the prefix (for DLSS/Reflex).' + }, + { + label: 'NVIDIA libs (64-bit only)', + enabled: false, + envs: [{ var: 'PROTON_NVIDIA_LIBS_NO_32BIT', val: '1' }], + conflicts: cachyNv['NVIDIA libs (64-bit only)'], + category: 'NVIDIA', + desc: 'Include only 64-bit NVIDIA libs (smaller prefix).' + }, + { + label: 'nvcuda.dll only', + enabled: false, + envs: [{ var: 'PROTON_NVIDIA_NVCUDA', val: '1' }], + conflicts: cachyNv['nvcuda.dll only'], + category: 'NVIDIA', + desc: 'Include only nvcuda.dll from the NVIDIA libs.' + }, + { + label: 'nvencodeapi.dll only', + enabled: false, + envs: [{ var: 'PROTON_NVIDIA_NVENC', val: '1' }], + conflicts: cachyNv['nvencodeapi.dll only'], + category: 'NVIDIA', + desc: 'Include only nvencodeapi.dll (NVENC encoding).' + }, + { + label: 'nvml.dll only', + enabled: false, + envs: [{ var: 'PROTON_NVIDIA_NVML', val: '1' }], + conflicts: cachyNv['nvml.dll only'], + category: 'NVIDIA', + desc: 'Include only nvml.dll (GPU monitoring).' + }, + { + label: 'nvoptix.dll only', + enabled: false, + envs: [{ var: 'PROTON_NVIDIA_NVOPTIX', val: '1' }], + conflicts: cachyNv['nvoptix.dll only'], + category: 'NVIDIA', + desc: 'Include only nvoptix.dll (ray tracing / OptiX).' + }, + // Upscaling + envToggle( + 'FSR4 upscaling', + 'PROTON_FSR4_UPGRADE', + '1', + 'Upscaling', + 'Upgrade FSR3 games to FSR4 (RDNA4).' + ), + envToggle( + 'FSR4 indicator', + 'PROTON_FSR4_INDICATOR', + '1', + 'Upscaling', + 'Show an FSR4 upscaling indicator in-game.' + ), + envToggle( + 'MLFG upgrade', + 'PROTON_MLFG_UPGRADE', + '1', + 'Upscaling', + 'Upgrade to MLFG (multi-frame generation).' + ), + envToggle( + 'DLSS upscaling', + 'PROTON_DLSS_UPGRADE', + '1', + 'Upscaling', + 'Upgrade DLSS2 games to the latest DLSS.' + ), + envToggle( + 'DLSS indicator', + 'PROTON_DLSS_INDICATOR', + '1', + 'Upscaling', + 'Show a DLSS upscaling indicator in-game.' + ), + envToggle( + 'XESS upscaling', + 'PROTON_XESS_UPGRADE', + '1', + 'Upscaling', + 'Upgrade games to XESS (Intel XeSS).' + ), + envToggle( + 'FFX III upscaling', + 'PROTON_FFX3_UPGRADE', + '1', + 'Upscaling', + 'Upgrade to AMD FidelityFX 3 (upscaling).' + ), + envToggle( + 'FFX IV upscaling', + 'PROTON_FFX4_UPGRADE', + '1', + 'Upscaling', + 'Upgrade to AMD FidelityFX 4.' + ), + envToggle( + 'OptiScaler', + 'PROTON_USE_OPTISCALER', + '1', + 'Upscaling', + 'Enable OptiScaler (adds FSR/XeSS/RT to non-supported games).' + ), + // Input + envToggle( + 'Enable Xalia', + 'PROTON_USE_XALIA', + '1', + 'Input', + 'Enable Xalia for gamepad/keyboard input from the desktop.' + ), + envToggle('SDL input', 'PROTON_PREFER_SDL', '1', 'Input', 'Prefer SDL for input.'), + envToggle( + 'Disable Steam Input', + 'PROTON_NO_STEAMINPUT', + '1', + 'Input', + 'Disable Steam Input translation.' + ), + // Display + envToggle( + 'Disable WM decorations', + 'PROTON_NO_WM_DECORATION', + '1', + 'Display', + 'Disable window manager decorations on game windows.' + ), + envToggle( + 'Wayland driver', + 'PROTON_ENABLE_WAYLAND', + '1', + 'Display', + 'Use the native Wayland wine driver (experimental).' + ), + // Audio + envToggle( + 'PipeWire audio', + 'PROTON_USE_PIPEWIRE', + '1', + 'Audio', + 'Use PipeWire instead of PulseAudio for audio.' + ), + // HDR + { + label: 'HDR', + enabled: false, + envs: [{ var: 'DXVK_HDR', val: '1' }], + conflicts: ['proton-cachyos:Disable HDR'], + category: 'HDR', + desc: 'Enable HDR support in DXVK (requires an HDR display).' + }, + { + label: 'Disable HDR', + enabled: false, + envs: [{ var: 'DXVK_NO_HDR', val: '1' }], + conflicts: ['proton-cachyos:HDR'], + category: 'HDR', + desc: 'Disable HDR support in DXVK.' + }, + // Media + envToggle( + 'Media converter', + 'PROTON_ENABLE_MEDIACONV', + '1', + 'Media', + 'Enable the media converter for game videos (mfplat).' + ), + envToggle( + 'Force GST media', + 'PROTON_MEDIA_FORCE_GST', + '1', + 'Media', + 'Force the GStreamer media backend for videos.' + ), + // Integration + envToggle( + 'Discord Rich Presence bridge', + 'PROTON_DISCORD_BRIDGE', + '1', + 'Integration', + "Bridge the game's Discord Rich Presence to the desktop client." + ) + ] + }; +} diff --git a/src/lib/preset/proton-ge.ts b/src/lib/preset/proton-ge.ts new file mode 100644 index 0000000..fb59660 --- /dev/null +++ b/src/lib/preset/proton-ge.ts @@ -0,0 +1,258 @@ +import type { ProgramConfig } from '../programs'; +import { envToggle } from './helpers'; +export function protonGE(): ProgramConfig { + return { + name: 'proton-ge', + enabled: false, + binary: '', + prefix: '', + suffix: '', + priority: 20, + envs: [], + args: [], + options: [ + // Debug + envToggle( + 'Debug log', + 'PROTON_LOG', + '1', + 'Debug', + "Write the Proton/dxvk/wine debug log to the game's prefix directory." + ), + envToggle( + 'Dump debug commands', + 'PROTON_DUMP_DEBUG_COMMANDS', + '1', + 'Debug', + 'Dump debug commands to stderr for debugging.' + ), + envToggle( + 'DXVK HUD (fps)', + 'DXVK_HUD', + 'fps', + 'Debug', + "Show DXVK's HUD with FPS and frame timing (values: fps, full, devinfo, …)." + ), + // Performance + envToggle( + 'Disable esync', + 'PROTON_NO_ESYNC', + '1', + 'Performance', + 'Disable esync (eventfd-based synchronization).' + ), + envToggle( + 'Disable fsync', + 'PROTON_NO_FSYNC', + '1', + 'Performance', + 'Disable fsync (fast sync via futexes).' + ), + envToggle( + 'Disable ntsync', + 'PROTON_NO_NTSYNC', + '1', + 'Performance', + 'Disable ntsync (new NT synchronization).' + ), + envToggle( + 'Force large address aware', + 'PROTON_FORCE_LARGE_ADDRESS_AWARE', + '1', + 'Performance', + 'Force 32-bit games to see a large address space.' + ), + envToggle( + 'Delay memory freeing', + 'PROTON_HEAP_DELAY_FREE', + '1', + 'Performance', + 'Delay freeing memory to improve performance in some games.' + ), + envToggle( + 'Enable seccomp-bpf', + 'PROTON_USE_SECCOMP', + '1', + 'Performance', + 'Enable seccomp-bpf sandboxing inside the wine process.' + ), + envToggle( + 'Disable write watches', + 'PROTON_NO_WRITE_WATCH', + '1', + 'Performance', + 'Disable write-watch-based memory tracking.' + ), + envToggle( + 'WoW64', + 'PROTON_USE_WOW64', + '1', + 'Performance', + 'Run 32-bit Windows apps under WoW64 (new 64-bit-only prefix layout).' + ), + // Graphics + envToggle( + 'wined3d (OpenGL)', + 'PROTON_USE_WINED3D', + '1', + 'Graphics', + 'Use wined3d (OpenGL) instead of DXVK for D3D9/11.' + ), + envToggle( + 'Disable D3D12', + 'PROTON_NO_D3D12', + '1', + 'Graphics', + 'Disable the D3D12→Vulkan layer (vkd3d-proton).' + ), + envToggle( + 'Disable D3D11', + 'PROTON_NO_D3D11', + '1', + 'Graphics', + 'Disable DXVK for D3D11 (fall back to wined3d).' + ), + envToggle( + 'Disable D3D10', + 'PROTON_NO_D3D10', + '1', + 'Graphics', + 'Disable DXVK for D3D10 (fall back to wined3d).' + ), + envToggle( + 'Disable D3D9', + 'PROTON_NO_D3D9', + '1', + 'Graphics', + 'Disable DXVK for D3D9 (fall back to wined3d).' + ), + envToggle( + 'Enable NVAPI', + 'PROTON_ENABLE_NVAPI', + '1', + 'Graphics', + 'Enable NVIDIA NVAPI support (DLSS, Reflex, ray tracing) — NVIDIA GPUs.' + ), + envToggle( + 'FSR in fullscreen', + 'WINE_FULLSCREEN_FSR', + '1', + 'Graphics', + 'Enable FSR upscaling for fullscreen exclusive games (wine).' + ), + { + label: 'FSR sharpening', + enabled: false, + envs: [{ var: 'WINE_FULLSCREEN_FSR_STRENGTH', val: '2' }], + category: 'Graphics', + desc: 'FSR sharpening strength (2 = default).' + }, + envToggle( + 'Fix miscolored video', + 'WINE_DO_NOT_CREATE_DXGI_DEVICE_MANAGER', + '1', + 'Graphics', + 'Fix wrong colors in videos (disable the DXGI device manager).' + ), + // Compatibility + envToggle( + 'Limit GL extension string', + 'PROTON_OLD_GL_STRING', + '1', + 'Compatibility', + 'Hide GL extensions newer than the game expects.' + ), + envToggle( + 'Copy prefix for -steamdeck', + 'COPYPREFIX', + '1', + 'Compatibility', + 'Copy the prefix instead of hardlinking (for -steamdeck).' + ), + // Input + { + label: 'Enable Xalia', + enabled: false, + envs: [{ var: 'PROTON_USE_XALIA', val: '1' }], + conflicts: ['proton-ge:Disable Xalia'], + category: 'Input', + desc: 'Enable Xalia for gamepad/keyboard input from the desktop.' + }, + { + label: 'Disable Xalia', + enabled: false, + envs: [{ var: 'PROTON_USE_XALIA', val: '0' }], + conflicts: ['proton-ge:Enable Xalia'], + category: 'Input', + desc: 'Disable Xalia input handling.' + }, + envToggle('SDL input', 'PROTON_USE_SDL', '1', 'Input', 'Use SDL for input instead of X11.'), + // Display + envToggle( + 'Wayland driver', + 'PROTON_USE_WAYLAND', + '1', + 'Display', + 'Use the native Wayland wine driver (experimental).' + ), + // Upscaling + envToggle( + 'FSR4 upscaling', + 'PROTON_FSR4_UPGRADE', + '1', + 'Upscaling', + 'Upgrade FSR3 games to FSR4 (RDNA4; use "FSR4 for RDNA3" on RDNA3 GPUs).' + ), + envToggle( + 'FSR4 indicator', + 'PROTON_FSR4_INDICATOR', + '1', + 'Upscaling', + 'Show an FSR4 upscaling indicator in-game.' + ), + envToggle( + 'FSR4 for RDNA3', + 'PROTON_FSR4_RDNA3_UPGRADE', + '1', + 'Upscaling', + 'Enable FSR4 on RDNA3 GPUs.' + ), + envToggle( + 'FSR3 upscaling', + 'PROTON_FSR3_UPGRADE', + '1', + 'Upscaling', + 'Upgrade FSR2 games to FSR3.' + ), + envToggle( + 'DLSS upscaling', + 'PROTON_DLSS_UPGRADE', + '1', + 'Upscaling', + 'Upgrade DLSS2 games to the latest DLSS.' + ), + envToggle( + 'DLSS indicator', + 'PROTON_DLSS_INDICATOR', + '1', + 'Upscaling', + 'Show a DLSS upscaling indicator in-game.' + ), + envToggle( + 'XESS upscaling', + 'PROTON_XESS_UPGRADE', + '1', + 'Upscaling', + 'Upgrade games to XESS (Intel XeSS).' + ), + // Media + envToggle( + 'Media converter', + 'PROTON_ENABLE_MEDIACONV', + '1', + 'Media', + 'Enable the media converter for game videos (mfplat).' + ) + ] + }; +} diff --git a/src/lib/presets.ts b/src/lib/presets.ts new file mode 100644 index 0000000..b8b9eb9 --- /dev/null +++ b/src/lib/presets.ts @@ -0,0 +1,8 @@ +export { gamescope } from './preset/gamescope'; +export { protonGE } from './preset/proton-ge'; +export { protonCachyOS } from './preset/proton-cachyos'; +export { obsGameCapture } from './preset/obs'; +export { gameMode } from './preset/gamemode'; +export { mangoHud } from './preset/mangohud'; +export { mesaLayers } from './preset/mesa'; +export { prime } from './preset/prime'; diff --git a/src/lib/programs.ts b/src/lib/programs.ts new file mode 100644 index 0000000..38b3d86 --- /dev/null +++ b/src/lib/programs.ts @@ -0,0 +1,140 @@ +import type { ArgEvaluatable, Arguments, EnvVar } from './meow'; +import { PATTERN_HINTS } from './preset/helpers'; + +export type ProgramOption = { + label: string; + enabled: boolean; + prefix?: string; + suffix?: string; + envs?: EnvVar[]; + args?: Arguments[]; + /** UI grouping heading for the options list. */ + category?: string; + /** Hover tooltip text (1s delay). */ + desc?: string; + /** Keys this option conflicts with: a program name, or 'program:option label'. */ + conflicts?: string[]; +}; + +export type ProgramConfig = { + name: string; + enabled: boolean; + binary: string; + prefix?: string; + suffix?: string; + priority?: number; + envs?: EnvVar[]; + args?: Arguments[]; + options?: ProgramOption[]; + /** Per-env-var join separator for duplicate values; default ','. */ + envSeparators?: { var: string; sep: string }[]; +}; + +/** + * Config-driven program — the base class every Steam launch program + * (gamescope, proton, gamemoderun, ...) builds on. Configure one through the UI. + */ +export class Program implements ArgEvaluatable { + constructor(private cfg: ProgramConfig) {} + + enabled(): boolean { + return this.cfg.enabled; + } + + private opts(): ProgramOption[] { + return (this.cfg.options ?? []).filter((o) => o.enabled); + } + + binary(): string { + return this.cfg.binary; + } + prefix(): string { + return [this.cfg.prefix, ...this.opts().flatMap((o) => (o.prefix ? [o.prefix] : []))] + .filter(Boolean) + .join(' '); + } + suffix(): string { + return [this.cfg.suffix, ...this.opts().flatMap((o) => (o.suffix ? [o.suffix] : []))] + .filter(Boolean) + .join(' '); + } + priority(): number { + return this.cfg.priority ?? 0; + } + envs(): EnvVar[] { + const all = [...(this.cfg.envs ?? []), ...this.opts().flatMap((o) => o.envs ?? [])]; + const seps = new Map((this.cfg.envSeparators ?? []).map((s) => [s.var, s.sep])); + const byVar = new Map>(); + for (const e of all) { + byVar.set(e.var, (byVar.get(e.var) ?? new Set()).add(e.val)); + } + return [...byVar].map(([varName, vals]) => ({ + var: varName, + val: [...vals].join(seps.get(varName) ?? ',') + })); + } + arguments(): Arguments[] { + return [...(this.cfg.args ?? []), ...this.opts().flatMap((o) => o.args ?? [])]; + } +} + +export type Conflict = { + from: string; + with: string; +}; + +/** Enabled-vs-enabled conflicts: option `conflicts` keys matched against enabled programs and options. */ +export function findConflicts(programs: ProgramConfig[]): Conflict[] { + const enabled: { key: string; desc: string }[] = []; + for (const p of programs) { + if (!p.enabled) continue; + enabled.push({ key: p.name, desc: `${p.name} (program)` }); + for (const o of p.options ?? []) { + if (o.enabled) + enabled.push({ key: `${p.name}:${o.label}`, desc: `${p.name}: ${o.label} (arg)` }); + } + } + + const descByKey = new Map(enabled.map((e) => [e.key, e.desc])); + const seen = new Set(); + const conflicts: Conflict[] = []; + + for (const p of programs) { + if (!p.enabled) continue; + for (const o of p.options ?? []) { + if (!o.enabled) continue; + for (const c of o.conflicts ?? []) { + const withDesc = descByKey.get(c); + if (!withDesc) continue; + const from = `${p.name}: ${o.label}`; + const pair = [from, withDesc].sort().join('\u0000'); + if (seen.has(pair)) continue; + seen.add(pair); + conflicts.push({ from, with: withDesc }); + } + } + } + + return conflicts; +} + +/** Values of enabled options (and program args) that violate their arg `pattern`, as banner messages. */ +export function findInvalidArgs(programs: ProgramConfig[]): string[] { + const out: string[] = []; + const hint = (p: string) => PATTERN_HINTS[p] ?? `something matching ${p}`; + for (const p of programs) { + if (!p.enabled) continue; + for (const a of p.args ?? []) { + if (a.pattern && a.val && !new RegExp(a.pattern).test(a.val)) + out.push(`${p.name}: ${a.arg} value must be ${hint(a.pattern)}`); + } + for (const o of p.options ?? []) { + if (!o.enabled) continue; + for (const a of o.args ?? []) { + if (a.pattern && a.val && !new RegExp(a.pattern).test(a.val)) + out.push(`${p.name}: ${o.label} value must be ${hint(a.pattern)}`); + } + } + } + return out; +} diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte new file mode 100644 index 0000000..9cebde5 --- /dev/null +++ b/src/routes/+layout.svelte @@ -0,0 +1,11 @@ + + + + + + +{@render children()} diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte new file mode 100644 index 0000000..00cd81c --- /dev/null +++ b/src/routes/+page.svelte @@ -0,0 +1,224 @@ + + +

Steam Launch Args

+ +
+
{result}
+ +
+ +
+ {#each programs as p, i (i)} + + {/each} +
+ +{#if programs[active]} + +{/if} + +{#if conflicts.length > 0 || invalid.length > 0} +
    + {#each conflicts as c (c.from + c.with)} +
  • ⚠ {c.from} conflicts with {c.with}
  • + {/each} + {#each invalid as m (m)} +
  • ⚠ {m}
  • + {/each} +
+{/if} + +
+ Decode a command into options + +
+ + {#if decodeUnmatched.length > 0} + Unrecognized: {decodeUnmatched.join(' ')} + {/if} +
+
+ + diff --git a/src/routes/+page.ts b/src/routes/+page.ts new file mode 100644 index 0000000..189f71e --- /dev/null +++ b/src/routes/+page.ts @@ -0,0 +1 @@ +export const prerender = true; diff --git a/static/robots.txt b/static/robots.txt new file mode 100644 index 0000000..b6dd667 --- /dev/null +++ b/static/robots.txt @@ -0,0 +1,3 @@ +# allow crawling everything by default +User-agent: * +Disallow: diff --git a/steam-tinker-launcher.md b/steam-tinker-launcher.md new file mode 100644 index 0000000..12f15d1 --- /dev/null +++ b/steam-tinker-launcher.md @@ -0,0 +1,658 @@ +# Steam Tinker Launch — Env Vars & Program Arguments Reference + +Source: `sonic2kk/steamtinkerlaunch` (single bash script, ~27,000 lines, v14.0.20250602). + +## 1. Environment Variables Set by STL + +### 1.1 Game Launch Time (most important) + +| Variable | Set When | Value | +| ---------------------------------- | ------------------------------------------------- | ----------------------------------------- | +| `MANGOHUD` | `USEMANGOHUD=1` + `MAHUVAR=1` | `1` | +| `MANGOHUD_CONFIG` | `USEMANGOHUD=1` + `MAHUARGS` non-empty | `$MAHUARGS` | +| `MANGOHUD_CONFIGFILE` | `USEMANGOHUD=1` + `MHIC` set | `$MHIC` | +| `LD_PRELOAD` | `USEMANGOHUD=1` + `LDPMAHU=1` | `$LD_PRELOAD $MAHUBIN` | +| `DXVK_FRAME_RATE` | `USEDXVKFRL=1` | `$DXVK_FPSLIMIT` | +| `DXVK_HDR` | `DXVKHDR=1` | `1` | +| `DXVK_CONFIG_FILE` | `DXVKCFG`/`DXDLSSCFG` set | `$STLDXVKCFG` or `$DXDLSSCFG` | +| `PROTON_ENABLE_NVAPI` | `USENVAPI=1` | `1` | +| `PROTON_HIDE_NVIDIA_GPU` | `USENVAPI=1` | `0` | +| `WINEDEBUG` | `USEWINEDEBUG` set | `$STLWINEDEBUG` | +| `WINEDLLOVERRIDES` | `USEWINEDLLOVERRIDES` set | `$WINEDLLOVERRIDES;$STLWINEDLLOVERRIDES` | +| `WINEESYNC` | `ENABLE_WINESYNC=1` | `0` | +| `WINEFSYNC` | `ENABLE_WINESYNC=1` | `0` | +| `WINEFSYNC_FUTEX2` | `ENABLE_WINESYNC=1` | `0` | +| `__NV_PRIME_RENDER_OFFLOAD` | `USEPRIMERUN=1` | `1` | +| `__VK_LAYER_NV_optimus` | `USEPRIMERUN=1` | `NVIDIA_only` | +| `__GLX_VENDOR_LIBRARY_NAME` | `USEPRIMERUN=1` → `nvidia` / `USEZINK=1` → `mesa` | `nvidia` or `mesa` | +| `VK_ICD_FILENAMES` | `USEPRIMERUN=1` | `/usr/share/vulkan/icd.d/nvidia_icd.json` | +| `MESA_LOADER_DRIVER_OVERRIDE` | `USEZINK=1` | `zink` | +| `GALLIUM_DRIVER` | `USEZINK=1` | `zink` | +| `SDL_VIDEODRIVER` | `SDLUSEWAYLAND=1` | `wayland` | +| `ENABLE_GAMESCOPE_WSI` | `USEGAMESCOPE=1` + `USEGAMESCOPEWSI=1` | `1` | +| `RADV_PERFTEST` | `STLRAD_PFTST` set | `$STLRAD_PFTST` | +| `ENABLE_` | `VULKANPOSTPROCESSOR` set (e.g. `ENABLE_FSR=1`) | `1` | +| `LC_ALL` | Always | `$LANG` | +| `PULSE_LATENCY_MSEC` | `STL_PULSE_LATENCY_MSEC` set | `$STL_PULSE_LATENCY_MSEC` | +| `DOTNET_ROOT` | Vortex mod manager | `$VTX_DOTNET_ROOT` | +| `DOTNET_BUNDLE_EXTRACT_BASE_DIR` | Vortex mod manager | `$VTX_DOTNET_ROOT` | +| `ONSTEAMDECK` | Detected Steam Deck | `1` | +| `STEAM_COMPAT_CLIENT_INSTALL_PATH` | `$SROOT` set | `$SROOT` | +| `STEAM_COMPAT_INSTALL_PATH` | `$MEGAMEDIR` / `$EFD` / `$STLDLDIR` | depending on game path | +| `STEAM_COMPAT_DATA_PATH` | `$STL_COMPAT_DATA_PATH` / `$DESTCODA` | wine prefix | +| `STEAM_COMPAT_APP_ID` | `$DPRS` / `$EUNID` | app id | +| `SteamAppId` | Same as above | `$STEAM_COMPAT_APP_ID` | +| `STEAM_COMPAT_TOOL_PATHS` | Custom SLR / tool paths | colon-joined paths | +| `STEAM_COMPAT_LIBRARY_PATHS` | Set before game launch | | +| `STEAM_COMPAT_MOUNTS` | Set before game launch | | +| `VRPGWINXIS` | Game window XID | `$GAMEWINXID` | + +### 1.2 VKD3D Configuration + +| Variable | Set When | Value | +| -------------------------- | ---------------------------------------- | ------------------------------- | +| `VKD3D_CONFIG` | `USERAYTRACING=1` or `$STL_VKD3D_CONFIG` | `dxr11` or `$STL_VKD3D_CONFIG` | +| `VKD3D_DEBUG` | `$STL_VKD3D_DEBUG` set | `$STL_VKD3D_DEBUG` | +| `VKD3D_SHADER_DEBUG` | `$STL_VKD3D_SHADER_DEBUG` set | `$STL_VKD3D_SHADER_DEBUG` | +| `VKD3D_LOG_FILE` | `$STL_VKD3D_LOG_FILE` set | `$STL_VKD3D_LOG_FILE` | +| `VKD3D_VULKAN_DEVICE` | `$STL_VKD3D_VULKAN_DEVICE` set | `$STL_VKD3D_VULKAN_DEVICE` | +| `VKD3D_FILTER_DEVICE_NAME` | `$STL_VKD3D_FILTER_DEVICE_NAME` set | `$STL_VKD3D_FILTER_DEVICE_NAME` | +| `VKD3D_DISABLE_EXTENSIONS` | `$STL_VKD3D_DISABLE_EXTENSIONS` set | `$STL_VKD3D_DISABLE_EXTENSIONS` | +| `VKD3D_TEST_DEBUG` | `$STL_VKD3D_TEST_DEBUG` set | `$STL_VKD3D_TEST_DEBUG` | +| `VKD3D_TEST_FILTER` | `$STL_VKD3D_TEST_FILTER` set | `$STL_VKD3D_TEST_FILTER` | +| `VKD3D_TEST_EXCLUDE` | `$STL_VKD3D_TEST_EXCLUDE` set | `$STL_VKD3D_TEST_EXCLUDE` | +| `VKD3D_TEST_PLATFORM` | `$STL_VKD3D_TEST_PLATFORM` set | `$STL_VKD3D_TEST_PLATFORM` | +| `VKD3D_TEST_BUG` | `$STL_VKD3D_TEST_BUG` set | `$STL_VKD3D_TEST_BUG` | +| `VKD3D_PROFILE_PATH` | `$STL_VKD3D_PROFILE_PATH` set | `$STL_VKD3D_PROFILE_PATH` | + +### 1.3 Wine Configuration + +| Variable | Set When | Value | +| ------------ | -------------- | ------------------------- | +| `WINE` | `extWine64Run` | `$RUNWINE` | +| `WINEARCH` | `extWine64Run` | `win64` or `$RUNWINEARCH` | +| `WINEPREFIX` | `extWine64Run` | `$GPFX` or `$GWFX` | + +## 2. Program Arguments Built + +### 2.1 Command Chain (FINALSTARTCMD) + +The final launch command is built by `buildCustomCmdLaunch`: + +``` +FINALSTARTCMD = [ GMR?, GSC?, GAMESCOPEARGSARR?, OBSC?, MAHUBIN?, --dlsym? ] + [SLRCMD?] + [EXTSTARTCMD] + [PROTSTARTCMD] + [GAMESTARTCMD] + [GAMEARGSARR] +``` + +### 2.2 Components + +| Component | Set By | Value | +| ------------------------------ | ---------------------------------------------------------------- | --------------------------------- | +| `GMR` (GameMode) | `USEGAMEMODERUN=1` | `$(command -v gamemoderun)` | +| `GSC` (GameScope) | `USEGAMESCOPE=1` | `$(command -v gamescope)` | +| `GAMESCOPEARGSARR` | `gameScopeArgs "$GAMESCOPE_ARGS"` | parsed args from `GAMESCOPE_ARGS` | +| `OBSC` (OBS capture) | `USEOBSCAP=1` | `$(command -v obs-gamecapture)` | +| `MAHUBIN` (MangoHud) | `USEMANGOHUD=1` + `LDPMAHU=0` | `$(command -v mangohud)` | +| `--dlsym` | `MAHUDLSYM=1` | literal flag appended to MangoHud | +| `SLRCMD` (Steam Linux Runtime) | `setSLRReap` | SLR command array | +| `EXTSTARTCMD` | `setBoxtronCmd`/`setRobertaCmd`/`setLuxtorpedaCmd`/`setLinuxCmd` | game-specific command | + +### 2.3 Proton Command (`PROTSTARTCMD`) + +Built by `setProtonCmd`: + +``` +PROTSTARTCMD = ( "$RUNPROTON" "$WFEAR" ) + GAMESTARTCMD +``` + +Where: + +- `$RUNPROTON` = path to proton binary +- `$WFEAR` = `waitforexitandrun` + +### 2.4 Wine Command + +Built by `setWineCmd`: + +``` +WINE=/bin/wine WINEARCH=win64 WINEDEBUG= WINEPREFIX= +``` + +### 2.5 Game Command (`GAMESTARTCMD`) + +- Windows games: `$EFD/$GAMEEXE` (game exe path) +- Native games: `$GAMERUN` or user-specified binary +- Custom program: `$CUSTOMCMD` (with optional `$WICO` for Windows binaries) +- Luxtorpeda: `"$LUXTORPEDACMD" "$LUXTORPEDAARGS" "$LUXEXE"` +- Boxtron: `"$BOXTRONCMD" "$BOXTRONARGS" "$BOXTRONEXE"` +- Roberta: `"$ROBERTACMD" "$ROBERTAARGS" "$ROBERTAEXE"` + +### 2.6 Game Arguments (`GAMEARGSARR`) + +- `GAMEARGS` from user config +- `HARDARGS` from user config (hardcoded overrides) +- `SORTGARGS=1` → prepend Steam-provided args (`SLOARGS`) +- `GAMEARGS` can be set to `"none"` (disabled) +- `-dx12` appended when `USERAYTRACING=1` + +## 3. GameScope Arguments + +Parsed from `GAMESCOPE_ARGS` config. Examples: + +- `--width 1280 --height 720` +- `--fs` (fullscreen) +- `--expose-wayland` +- `--immersive` + +Always ends with `--` (required by GameScope). + +## 4. MangoHud + +| Setting | Variable | Notes | +| -------------- | --------------- | ------------------------------- | +| Enable | `USEMANGOHUD=1` | | +| Config string | `MAHUARGS` | Passed to `MANGOHUD_CONFIG` | +| Config file | `MHIC` | Passed to `MANGOHUD_CONFIGFILE` | +| Var mode | `MAHUVAR=1` | Sets `MANGOHUD=1` only | +| Binary preload | `LDPMAHU=1` | Sets `LD_PRELOAD=$MAHUBIN` | +| Dlsym mode | `MAHUDLSYM=1` | Appends `--dlsym` to MangoHud | + +## 5. DXVK + +| Setting | Variable | Notes | +| ----------- | --------------------- | -------------------------------------------------------- | +| HDR | `DXVKHDR=1` | Sets `DXVK_HDR=1` | +| Frame limit | `USEDXVKFRL=1` | Sets `DXVK_FRAME_RATE=$DXVK_FPSLIMIT` | +| Config file | `DXVKCFG`/`DXDLSSCFG` | Sets `DXVK_CONFIG_FILE` | +| NVAPI | `USENVAPI=1` | Sets `PROTON_ENABLE_NVAPI=1`, `PROTON_HIDE_NVIDIA_GPU=0` | + +## 6. Vulkan Post-Processors + +| Processor | Env Var | +| --------- | ------------------- | +| FSR | `ENABLE_FSR=1` | +| DLSS | `USEDLSS=1` | +| VKBasalt | `ENABLE_VKBASALT=1` | + +## 7. GameMode + +| Setting | Variable | Notes | +| ------- | ------------------ | -------------------------------- | +| Enable | `USEGAMEMODERUN=1` | Prepends `gamemoderun` to launch | + +## 8. Custom Program (CustomCMD) + +| Setting | Variable | Notes | +| -------------- | ---------------------- | ------------------------------------------- | +| Enable | `CUSTOMCMD` set | User-specified executable | +| Force Wine | `CUSTOMCMDFORCEWIN=1` | Force Wine/Proton wrapper | +| External progs | `EXTPROGS_CUSTOMCMD=1` | Apply MangoHud/GameScope to custom program | +| Fork | `FORK_CUSTOMCMD=1` | Fork process in background | +| Only custom | `ONLY_CUSTOMCMD=1` | Skip game, run only custom command | +| Wait | `WAITFORCUSTOMCMD=N` | Wait N seconds for custom command to start | +| Use SLR | `CUSTOMCMD_USESLR=1` | Use Steam Linux Runtime with custom command | + +## 9. Conty (Containerized Wine) + +| Setting | Variable | Notes | +| ------------ | ------------ | --------------------------- | +| Enable | `USECONTY=1` | | +| Binary | `RUNCONTY` | `$CONTYDLDIR/$CONTY` | +| Pick Conty | `PICKCONT` | user-specified Conty binary | +| Custom Conty | `CUSTCONTY` | user-specified Conty binary | + +Launches as: `"$RUNCONTY" "${RUNCMD[@]}"` + +## 10. Mod Organizer 2 (MO2) + +| Setting | Variable | Notes | +| -------- | --------- | ------------------------------- | +| Mode | `MO2MODE` | `disabled` / `enabled` / `auto` | +| MO2 exe | `GMO2EXE` | Path to MO2 executable | +| MO2 path | `MO2PATH` | Path to MO2 installation | + +Launches MO2 with: `STEAM_COMPAT_CLIENT_INSTALL_PATH=... STEAM_COMPAT_DATA_PATH=... $RUNPROTON run ${MO}.exe -i $MYINST $1` + +## 11. Vortex Mod Manager + +| Setting | Variable | Notes | +| ------------ | ----------------- | ------------------------------------------------------- | +| Enable | `USEVORTEX=1` | | +| Setup | `VORTEXSETUP` | | +| Download dir | `VORTEXDLDIR` | | +| .NET root | `VTX_DOTNET_ROOT` | Sets `DOTNET_ROOT` and `DOTNET_BUNDLE_EXTRACT_BASE_DIR` | +| VPath | `VSPATH` | `$VORTEXDLDIR/$VORTEXSETUP` | + +## 12. Shader Management + +| Setting | Variable | Notes | +| ---------- | -------------- | ------------------------------ | +| Shader dir | `SHADDESTDIR` | Default shader destination | +| Repos | `SHADERREPOS` | Shader repository URLs | +| Update | `SHADERUPDATE` | `1` = update shaders on launch | + +## 13. Network/Internet Blocking + +| Setting | Variable | Notes | +| -------------- | ----------------- | --------------------------------- | +| Block internet | `BLOCKINTERNET=1` | Uses `unshare -n -r` to namespace | +| `BLOCKCMD` | `unshare` | | +| `BLOCKARGS` | `-n -r` | Network + user namespace | + +## 14. Wine Debug + +| Setting | Variable | Notes | +| -------------- | --------------------- | ------------------------ | +| Debug channels | `STLWINEDEBUG` | e.g. `+relay,+timestamp` | +| DLL overrides | `STLWINEDLLOVERRIDES` | e.g. `dxvk=n:d3d11` | + +## 15. Game Window Management + +| Setting | Variable | Notes | +| ------------ | ------------- | ------------------------- | +| Window name | `GAMEWINDOW` | X11 window name to target | +| Window XID | `GAMEWINXID` | X11 window XID | +| `VRPGWINXIS` | `$GAMEWINXID` | Used for VR games | + +## 16. Steam Deck Specific + +| Setting | Variable | Value | +| ----------- | --------------------- | ----- | +| Detected | `ONSTEAMDECK` | `1` | +| Was update | `STEAMDECKWASUPDATE` | `0` | +| Did install | `STEAMDECKDIDINSTALL` | `1` | +| Steam Run | `STEAMDECKSTEAMRUN` | `0` | + +## 17. Game Files + +| Setting | Variable | Notes | +| --------- | ------------ | ------------------------- | +| Game dir | `$EFD` | Executable file directory | +| Game exe | `$GAMEEXE` | Game executable name | +| Game path | `$GP` | Absolute game path | +| Mega dir | `$MEGAMEDIR` | Game install directory | + +## 18. Proton + +| Setting | Variable | Notes | +| --------------- | ----------------------- | --------------------------- | +| Use proton | `USEPROTON` | Proton version string | +| Proton bin | `$PROTBIN` | Path to proton binary | +| Compat data | `$STL_COMPAT_DATA_PATH` | Wine prefix path | +| PFX | `$GPFX` | `$STL_COMPAT_DATA_PATH/pfx` | +| Run proton | `RUNPROTON` | Full proton command | +| Run wine server | `RUNWINESERVER` | `run_wine_server` path | +| Proton version | `PROTONVERSION` | Version string | + +## 19. Winetricks + +| Setting | Variable | Notes | +| -------------- | ---------------- | -------------------------- | +| Run winetricks | `RUNWINETRICKS` | Winetricks commands to run | +| Winetricks bin | `$SYSWINETRICKS` | `winetricks` path | + +## 20. GDB Debugging + +| Setting | Variable | Notes | +| ---------- | ------------ | --------------- | +| GDB run | `GDBGAMERUN` | GDB script path | +| GDB script | `GDBRUN` | GDB script | +| Run GDB | `RUN_GDB=1` | Enable GDB | + +## 21. VR + +| Setting | Variable | Notes | +| -------------- | -------------- | --------------------------- | +| VR init | `VRINITLOCK` | VR initialization lock file | +| VR init result | `VRINITRESULT` | VR init result file | + +## 22. One-Time Run + +| Setting | Variable | Notes | +| ------------ | ------------ | ------------------- | +| One-time run | `USEONERT=1` | | +| Custom path | `OPCUSTPATH` | Custom program path | + +## 23. Standalone Proton + +| Setting | Variable | Notes | +| ----------------- | ----------------------- | ---------------------- | +| Use standalone | `USESTANDALONEPROTON=1` | | +| Standalone proton | `$STLPROTDIR` | STL-managed proton dir | + +## 24. GameScope Args + +| Setting | Variable | Notes | +| -------------- | ---------------- | ------------------------------ | +| GameScope args | `GAMESCOPE_ARGS` | Space-separated args | +| MangoApp | `USEMANGOAPP=1` | Uses MangoHud App (Steam Deck) | + +## 25. DXVK HUD + +| Setting | Variable | Notes | +| -------- | ---------- | -------------------- | +| DXVK HUD | `DXVK_HUD` | HUD elements to show | + +## 26. Game Scope + +| Setting | Variable | Notes | +| ------------- | ------------------- | --------------------------- | +| GameScope | `USEGAMESCOPE=1` | Enable GameScope | +| GameScope WSI | `USEGAMESCOPEWSI=1` | Enable GameScope WSI | +| GameScope fix | `FIXGAMESCOPE=1` | Fix GameScope on Steam Deck | + +## 27. OBS Capture + +| Setting | Variable | Notes | +| ----------- | ------------- | ----------------------- | +| OBS capture | `USEOBSCAP=1` | Enable OBS game capture | + +## 28. Vulkan Post Processor + +| Setting | Variable | Notes | +| --------- | --------------------- | ------------------- | +| Vulkan PP | `VULKANPOSTPROCESSOR` | Post-processor name | + +## 29. SDL Wayland + +| Setting | Variable | Notes | +| ----------- | ----------------- | ------------------------ | +| SDL Wayland | `SDLUSEWAYLAND=1` | Force SDL to use Wayland | + +## 30. Wine DPI Scaling + +| Setting | Variable | Notes | +| -------- | --------- | ----------------- | +| Wine DPI | `WINEDPI` | DPI scaling value | + +## 3. GameScope CLI Arguments + +Parsed from `GAMESCOPE_ARGS` config string. Always ends with `--`. + +### 3.1 General Options + +| Flag | Description | Type | +| ---------------------------- | ---------------------------------------------- | ---------------------- | +| `-W ` | Show resolution width | number | +| `-H ` | Show resolution height | number | +| `-w ` | Internal resolution width (what games see) | number | +| `-h ` | Internal resolution height | number | +| `-r ` | Focused frame rate limit | 30/60/90/120/unlimited | +| `-o ` | Unfocused frame rate limit | 30/60/90/120/unlimited | +| `-f` | Fullscreen | bool | +| `-b` | Borderless window | bool | +| `-e` | Steam integration (fixes peripheral bugs) | bool | +| `--force-windows-fullscreen` | Force windows fullscreen inside nested display | bool | +| `--force-grab-cursor` | Keep cursor inside GameScope nested display | bool | +| `-g` | Force grab keyboard | bool | +| `--force-orientation ` | Orientation (left/right/normal/upsidedown) | dropdown | +| `--cursor ` | Custom cursor image | file picker | +| `-s ` | Mouse sensitivity | number (default 1.0) | + +### 3.2 Filtering & Scaling Options + +**New filter mode (GameScope ≥3.11):** + +| Flag | Description | Type | +| ------------- | ----------------------------------------------- | -------- | +| `-F ` | Filter (none/linear/nearest/fsr/nis/pixel) | dropdown | +| `-S ` | Scale mode (none/auto/integer/fit/fill/stretch) | dropdown | + +**Legacy filter mode (older GameScope):** + +| Flag | Description | Type | +| ----------------------- | ------------------------------------------- | ------ | +| `-i` | Integer scaling | bool | +| `-n` | Nearest neighbor filtering | bool | +| `-U ` | FSR 1.0 upscaling | bool | +| `-Y` | NVIDIA Image Scaling v1.0.3 | bool | +| `--fsr-sharpness ` | FSR sharpness (0-20, default 2) | number | +| `-m ` | Max scale factor (Steam Deck defaults to 2) | number | + +### 3.3 HDR Options + +| Flag | Description | Type | +| --------------------------- | --------------------------------------- | ------ | +| `--hdr-enabled` | Enable HDR | bool | +| `--hdr-wide-gammut-for-sdr` | Wide gammut for SDR | bool | +| `--hdr-sdr-content-nits` | SDR content nits (default 400) | number | +| `--hdr-itm-enable` | Inverse tone mapping enabled | bool | +| `--hdr-itm-sdr-nits` | ITM SDR nits (0-1000, default 100) | number | +| `--hdr-itm-target-nits` | ITM target nits (0-10000, default 1000) | number | + +### 3.4 VR Options + +| Flag | Description | Type | +| ------------------------------------------ | -------------------------------------- | ----------- | +| `--openvr` | Enable OpenVR | bool | +| `--vr-overlay-explicit-name ` | SteamVR explicit name | text | +| `--vr-overlay-default-name ` | SteamVR default name | text | +| `--vr-overlay-key ` | SteamVR overlay key string | text | +| `--vr-overlay-icon ` | SteamVR overlay icon | file picker | +| `--vr-overlay-show-immediately` | Show VR overlay immediately | bool | +| `--vr-overlay-enable-control-bar` | Enable SteamVR control bar | bool | +| `--vr-overlay-enable-control-bar-keyboard` | Enable keyboard button on control bar | bool | +| `--vr-overlay-enable-control-bar-close` | Enable close button on control bar | bool | +| `--vr-scrolls-speed ` | VR trackpad scroll speed (default 8.0) | number | +| `--vr-overlay-modal` | Show SteamVR overlay as modal | bool | + +### 3.5 Embedded & Display Options + +| Flag | Description | Type | +| ----------------------------- | ------------------------------------------------------------------------- | -------- | +| `--default-touch-mode ` | Default touch action (hover/leftclick/rightclick/middleclick/passthrough) | dropdown | +| `--immediate-flips` | Enable immediate flips (tearing allowed) | bool | +| `--adaptive-sync` | Enable adaptive sync / VRR | bool | +| `-O ` | Preferred GameScope output(s) | text | +| `--generate-drm-mode ` | DRM mode generation (default/cvt/fixed) | dropdown | + +### 3.6 Advanced Options + +| Flag | Description | Type | +| --------------------------- | ---------------------------------------------- | ----------- | +| `--stats-path ` | Path to write GameScope statistics | file picker | +| `--backend ` | Backend (auto/sdl/wayland/drm/headless/openvr) | dropdown | +| `-C ` | Cursor hide delay in milliseconds | number | +| `--force-composition` | Disable direct scan-out | bool | +| `--debug-hud` | Draw debug HUD | bool | +| `--hdr-debug-force-support` | Force HDR support flag | bool | +| `--hdr-debug-force-output` | Force HDR output on display | bool | +| `--prefer-vk-device` | Prefer Vulkan device for compositing | bool | +| `--expose-wayland` | Expose Wayland | bool | +| `--rt` | Realtime scheduling | bool | +| `--headless` | Headless mode | bool | + +## 4. MangoHud CLI Arguments + +### 4.1 Launch Command + +``` +$mangohud [--dlsym] +``` + +### 4.2 Configuration + +| Setting | Variable | Description | +| ----------- | -------- | ----------------------------------------------------------- | +| `MAHUARGS` | Config | MangoHud config string (passed as `MANGOHUD_CONFIG`) | +| `MHIC` | Config | MangoHud config file path (passed as `MANGOHUD_CONFIGFILE`) | +| `MAHUVAR` | Config | If 1, sets `MANGOHUD=1` instead of binary | +| `LDPMAHU` | Config | If 1, uses `LD_PRELOAD` instead of binary wrapper | +| `MAHUDLSYM` | Config | If 1, appends `--dlsym` to mangohud | + +## 5. Proton/Wine CLI Arguments + +### 5.1 Proton Command + +``` +$RUNPROTON run [--verb] +``` + +Where `--verb` is the Steam runtime flag, `run` is the Proton subcommand. + +### 5.2 Wine Command + +``` +WINE=/bin/wine WINEARCH=win64 WINEDEBUG= WINEPREFIX= +``` + +### 5.3 Proton Environment Variables + +| Variable | Description | +| ---------------------------------- | ------------------------- | +| `PROTON_LOG` | Enable Proton logging | +| `PROTON_LOG_DIR` | Proton log directory | +| `PROTON_DEBUG_DIR` | Proton debug directory | +| `PROTON_DUMP_DEBUG_COMMANDS` | Dump debug commands | +| `PROTON_FORCE_LARGE_ADDRESS_AWARE` | Force large address aware | +| `PROTON_NO_D3D10` | Disable D3D10 | +| `PROTON_NO_D3D11` | Disable D3D11 | +| `PROTON_NO_ESYNC` | Disable esync | +| `PROTON_NO_FSYNC` | Disable fsync | +| `PROTON_ENABLE_NVAPI` | Enable NVAPI | +| `PROTON_HIDE_NVIDIA_GPU` | Hide NVIDIA GPU | +| `PROTON_USE_WINED3D` | Use wined3d | +| `NETOPTS` | Network options | + +## 6. DXVK CLI Arguments + +### 6.1 DXVK HUD Options (comma-separated) + +``` +DXVK_HUD= +``` + +| Option | Description | +| ------------- | ----------------- | +| `1` | Frame count | +| `devinfo` | Device info | +| `fps` | Frames per second | +| `frametimes` | Frame times | +| `submissions` | Submission count | +| `drawcalls` | Draw call count | +| `pipelines` | Pipeline count | +| `memory` | Memory usage | +| `gpuload` | GPU load | +| `version` | DXVK version | +| `api` | API version | +| `compiler` | Shader compiler | +| `samplers` | Sampler count | +| `full` | Full HUD | + +Additional DXVK HUD options: + +- `scale=` — HUD scale factor + +### 6.2 DXVK Configuration + +| Variable | Description | +| ------------------ | ----------------------------------------- | +| `DXVK_HUD` | HUD options (see above) | +| `DXVK_LOG_LEVEL` | Log level (info/error/perf/debug/verbose) | +| `DXVK_CONFIG_FILE` | Path to DXVK config file | +| `DXVK_FRAME_RATE` | FPS limit | +| `DXVK_HDR` | HDR enable | + +## 7. VKD3D CLI Arguments + +| Variable | Description | +| -------------------------- | ------------------------------------------- | +| `VKD3D_CONFIG` | Config string (e.g. `dxr11` for raytracing) | +| `VKD3D_DEBUG` | Debug level | +| `VKD3D_SHADER_DEBUG` | Shader debug level | +| `VKD3D_LOG_FILE` | Log file path | +| `VKD3D_VULKAN_DEVICE` | Vulkan device to use | +| `VKD3D_FILTER_DEVICE_NAME` | Filter devices by name | +| `VKD3D_DISABLE_EXTENSIONS` | Disable Vulkan extensions | +| `VKD3D_TEST_DEBUG` | Test debug level | +| `VKD3D_TEST_FILTER` | Test filter | +| `VKD3D_TEST_EXCLUDE` | Test exclude | +| `VKD3D_TEST_PLATFORM` | Test platform | +| `VKD3D_TEST_BUG` | Test bug | +| `VKD3D_PROFILE_PATH` | Profile path | + +## 8. Wine Environment Variables + +| Variable | Description | +| --------------------------------- | --------------------------- | +| `WINE_FULLSCREEN_FSR` | FSR in Wine (1=enabled) | +| `USEFWS` | FSR in Wine (1=enabled) | +| `WINE_FULLSCREEN_FSR_STRENGTH` | FSR strength | +| `WINE_FULLSCREEN_FSR_MODE` | FSR mode | +| `WINE_FULLSCREEN_FSR_CUSTOM_MODE` | Custom FSR mode | +| `WINE_FULLSCREEN_INTEGER_SCALING` | Integer scaling (1=enabled) | +| `WINEDLLOVERRIDES` | DLL overrides | +| `WINEDEBUG` | Debug channels | +| `WINEESYNC` | Esync (0=disabled) | +| `WINEFSYNC` | Fsync (0=disabled) | +| `WINEFSYNC_FUTEX2` | Fsync futex2 (0=disabled) | + +## 31. Raytracing + +| Setting | Variable | Notes | +| ---------- | ----------------- | ------------------------------------------------------- | +| Raytracing | `USERAYTRACING=1` | Sets `VKD3D_CONFIG=dxr11`, appends `-dx12` to game args | + +## 32. PrimeRun (NVIDIA Prime Render Offload) + +| Setting | Variable | Notes | +| ------- | --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Enable | `USEPRIMERUN=1` | Sets `__NV_PRIME_RENDER_OFFLOAD=1`, `__VK_LAYER_NV_optimus=NVIDIA_only`, `__GLX_VENDOR_LIBRARY_NAME=nvidia`, `VK_ICD_FILENAMES=/usr/share/vulkan/icd.d/nvidia_icd.json` | + +## 33. Zink (Vulkan-to-Gallium) + +| Setting | Variable | Notes | +| ------- | ----------- | ------------------------------------------------------------------------------------------------ | +| Enable | `USEZINK=1` | Sets `__GLX_VENDOR_LIBRARY_NAME=mesa`, `MESA_LOADER_DRIVER_OVERRIDE=zink`, `GALLIUM_DRIVER=zink` | + +## 34. WineSync + +| Setting | Variable | Notes | +| ------- | ------------------- | ------------------------------------------------------- | +| Enable | `ENABLE_WINESYNC=1` | Sets `WINEESYNC=0`, `WINEFSYNC=0`, `WINEFSYNC_FUTEX2=0` | + +## 35. X64dbg + +| Setting | Variable | Notes | +| --------------- | -------------------------- | --------------------------- | +| Run X64dbg | `RUN_X64DBG=1` | Enable x64dbg | +| Attach on start | `X64DBG_ATTACHONSTARTUP=1` | Attach x64dbg on game start | + +## 36. Dependencies + +| Setting | Variable | Notes | +| -------- | ------------ | --------------------- | +| Run deps | `RUN_DEPS=1` | Install dependencies | +| Dep exe | `DEPSEXE` | Dependency executable | + +## 37. Boxtron + +| Setting | Variable | Notes | +| ------- | -------------- | ------------------------------ | +| Enable | `USEBOXTRON=1` | | +| Args | `BOXTRONARGS` | `--wait-before-run` by default | +| Binary | `BOXTRONCMD` | Boxtron path | +| Exe | `BOXTRONEXE` | Boxtron exe | + +## 38. Roberta + +| Setting | Variable | Notes | +| ------- | -------------- | ------------------------------ | +| Enable | `USEROBERTA=1` | | +| Args | `ROBERTAARGS` | `--wait-before-run` by default | +| Binary | `ROBERTACMD` | Roberta path | +| Exe | `ROBERTAEXE` | Roberta exe | + +## 39. Luxtorpeda + +| Setting | Variable | Notes | +| ------- | ----------------- | --------------- | +| Enable | `USELUXTORPEDA=1` | | +| Args | `LUXTORPEDAARGS` | Luxtorpeda args | +| Binary | `LUXTORPEDACMD` | Luxtorpeda path | +| Exe | `LUXEXE` | Luxtorpeda exe | + +## 40. Game Mode + +| Setting | Variable | Notes | +| --------- | ------------------ | --------------- | +| Game Mode | `USEGAMEMODERUN=1` | Enable GameMode | diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..2c2ed3c --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,20 @@ +{ + "extends": "./.svelte-kit/tsconfig.json", + "compilerOptions": { + "rewriteRelativeImportExtensions": true, + "allowJs": true, + "checkJs": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "sourceMap": true, + "strict": true, + "moduleResolution": "bundler" + } + // Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias + // except $lib which is handled by https://svelte.dev/docs/kit/configuration#files + // + // To make changes to top-level options such as include and exclude, we recommend extending + // the generated config; see https://svelte.dev/docs/kit/configuration#typescript +} diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..58f05a1 --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,16 @@ +import adapter from '@sveltejs/adapter-static'; +import { sveltekit } from '@sveltejs/kit/vite'; +import { defineConfig } from 'vite'; + +export default defineConfig({ + plugins: [ + sveltekit({ + compilerOptions: { + // Force runes mode for the project, except for libraries. Can be removed in svelte 6. + runes: ({ filename }) => + filename.split(/[/\\]/).includes('node_modules') ? undefined : true + }, + adapter: adapter() + }) + ] +});