# Non-Steam Shortcuts через VDF ## Главное правило **Steam должен быть выключен** перед записью shortcuts.vdf. Если Steam работает — он перезапишет файл из памяти при следующем запуске. ## Остановка Steam (Bazzite game session) ```bash systemctl --user stop gamescope-session-plus@steam.service sleep 2 systemctl --user is-active gamescope-session-plus@steam.service # должен вернуть "inactive" # правишь VDF systemctl --user start gamescope-session-plus@steam.service ``` Если после stop Steam всё ещё активен — повторить. ## Процедура (один bash-скрипт) Один скрипт на HTPC: останавливает Steam → делает бэкап → редактирует VDF → запускает Steam. Использовать модуль `vdf` (установлен на HTPC через pip). **Никогда** не редактировать бинарный VDF через ручную сборку байтов или замену строк — это ломает файл. ```bash #!/bin/bash set -e # 1. Остановить Steam systemctl --user stop gamescope-session-plus@steam.service sleep 2 # 2. Бэкап cp ~/.steam/steam/userdata/147839491/config/shortcuts.vdf{,.bak} # 3. Редактировать VDF (скрипт лежит рядом) python3 /path/to/edit_vdf.py # 4. Запустить Steam systemctl --user start gamescope-session-plus@steam.service ``` Python-скрипт (`edit_vdf.py`): ```python import os, vdf, shutil, binascii, struct vdf_path = os.path.expanduser('~/.steam/steam/userdata/147839491/config/shortcuts.vdf') with open(vdf_path, 'rb') as f: data = vdf.binary_load(f) shortcuts = data.get('shortcuts', {}) def compute_appid(name, exe): key = exe + name top = binascii.crc32(key.encode('utf-8')) | 0x80000000 return struct.unpack('i', struct.pack('I', top))[0] next_idx = str(len(shortcuts)) shortcuts[next_idx] = { 'AppName': 'Game Name', 'Exe': '/home/bazzite/Emulation/tools/launchers/ryujinx.sh', 'StartDir': '/home/bazzite/Emulation/tools/launchers', 'LaunchOptions': '/home/bazzite/Emulation/roms/switch/animal_crossing.nsp', 'appid': compute_appid('Game Name', '/home/bazzite/Emulation/tools/launchers/ryujinx.sh'), 'IsHidden': 0, 'AllowDesktopConfig': 1, 'AllowOverlay': 1, 'OpenVR': 0, 'Devkit': 0, 'DevkitGameID': '', 'DevkitOverrideAppID': 0, 'LastPlayTime': 0, 'FlatpakAppID': '', 'sortas': '', 'tags': {}, } data['shortcuts'] = shortcuts with open(vdf_path, 'wb') as f: vdf.binary_dump(data, f) ``` Скрипты на HTPC загружать через scp, не через heredoc. ## CompatToolMapping (Proton) в config.vdf После добавления shortcut нужно прописать Proton в текстовом `config.vdf`: ```python import vdf CONFIG_PATH = os.path.expanduser('~/.local/share/Steam/config/config.vdf') with open(CONFIG_PATH, 'r') as f: config = vdf.load(f) compat_map = config['InstallConfigStore']['Software']['Valve']['Steam'].setdefault('CompatToolMapping', {}) compat_map[str(appid_signed)] = { 'name': 'GE-Proton10-34', # или 'proton_experimental' 'config': '', 'Priority': '250', } with open(CONFIG_PATH, 'w') as f: vdf.dump(config, f) ``` ## Полный скилл Вся процедура (shortcuts + Proton + artwork) описана в скилле `steam-emulator-shortcuts`. Смотреть там. ## Бэкап ```bash cp ~/.steam/steam/userdata/*/config/shortcuts.vdf{,.bak} ``` Автоматический бэкап `shortcuts.vdf` делается ежедневно скриптом `backup-saves.sh` на HTPC, версии хранятся в `/run/media/bazzite/Data/backups/saves/shortcuts/` (последние 30 копий). ## Перезагрузка Steam После записи VDF — перезапустить Steam, чтобы он подхватил изменения. ## Wide Steam Grid Posters — Порядок действий **Перед остановкой Steam** — сначала скачать все постеры. Так как Steam не нужен для скачивания. ### Постеры для всех форматов При добавлении новой не-Steam игры — сразу скачивай все 4 формата: | Размер | Формат файла | Назначение | |--------|-------------|------------| | 920×430 | `{unsigned_appid}.png` | Wide — основная карточка в библиотеке | | 600×900 | `{unsigned_appid}p.png` | Poster — вертикальный, при наведении | | 1920×620 | `{unsigned_appid}_hero.png` | Hero — фон на странице игры | | 1200×351 | `{unsigned_appid}_logo.png` | Логотип на hero | **Unsigned appid** = `signed_appid & 0xFFFFFFFF`. Пример: `-1321720074` → `2973247222`. → Подробно: [[htpc-steam-nonsteam-shortcuts]] ### Процедура (как делал Whale) 1. **Найти постеры на SteamGridDB** — зайти на `https://www.steamgriddb.com/game/`, отфильтровать по 920×430. - Для новых игр: поискать `<игра> site:steamgriddb.com` - Wide grids часто меньше по количеству чем posters (600×900), их ищем в секции Grids → фильтр `Dimensions: 920×430` 2. **Получить прямую ссылку CDN** — thumbnails на странице grid'a ведут на `cdn2.steamgriddb.com/thumb/.jpg`, а полный файл лежит по `cdn2.steamgriddb.com/grid/.png`. 3. **Скачать локально:** ```bash curl -s -o /tmp/.png "https://cdn2.steamgriddb.com/grid/.png" ``` 4. **Закинуть на HTPC** через scp в `/tmp/`: ```bash scp /tmp/.png bazzite@192.168.1.86:/tmp/.png ``` 5. **Скопировать в grid-директорию Steam:** ```bash ssh bazzite@192.168.1.86 cp /tmp/.png ~/.steam/steam/userdata/147839491/config/grid/.png ``` 6. **Перезапустить Steam** (Steam перечитывает grid-файлы свежими при старте, но часто подхватывает и без перезапуска): ```bash systemctl --user stop gamescope-session-plus@steam.service systemctl --user start gamescope-session-plus@steam.service ``` ### SteamGridDB ключ API `5f0a7cb3366dede2c10860a95795ce60` — для программного доступа.