Files
obsidian-vault/family/how-to/htpc-steam-nonsteam-shortcuts.md
T

9.4 KiB
Raw Blame History

Non-Steam Shortcuts через VDF

Главное правило

Steam должен быть выключен перед записью shortcuts.vdf. Если Steam работает — он перезапишет файл из памяти при следующем запуске.

Подводные камни native Linux shortcut'ов (Worms W.M.D case)

Проблема: Steam не видит /run/media/system/Data/

Steam, запущенный через gamescope-session-plus, работает в изолированном mount namespace. Путь /run/media/system/Data/ (btrfs-подраздел) может быть не виден Steam-процессу.

Симптом: нажимаешь Play → chdir в логах есть, exit code 0 мгновенно, игра не стартует. /tmp/worms_stderr.log не создаётся — значит run.sh даже не запустился.

Причина: Steam проверяет существование exe файла перед запуском native shortcut. Если файл не найден — steam-launch-wrapper завершается с кодом 0 без ошибки. Для Proton shortcut'ов эта проверка не делается — Steam передаёт путь внутрь Proton.

Решение: не указывать exe на /run/media/system/Data/.... Вместо этого:

  1. Создать symlink в /home/bazzite/:
    ln -sf /run/media/system/Data/Games/.../run.sh /home/bazzite/game_name.sh
    
  2. В VDF прописать exe = /home/bazzite/game_name.sh, StartDir = /home/bazzite/.
  3. /home/bazzite/ виден из любого mount namespace.

Проблема: run.sh глушит stderr

Многие native Linux игры (особенно старые MojoSetup-инсталляторы) имеют в run.sh:

exec ./binary 2>/dev/null "$@"

2>/dev/null скрывает все ошибки. Steam логи не показывают stderr дочернего процесса.

Решение: убрать 2>/dev/null из run.sh. Если нужно дебажить — перенаправить в файл:

exec ./binary 2>/tmp/game_stderr.log "$@"

Проблема: старая libmp3lame.so.0 без нужного символа

Старые native игры тащат свою libmp3lame.so.0 (2019-2020), в которой нет символа lame_encode_buffer_interleaved_int. Системная libsndfile.so.1 (Fedora 43) требует этот символ.

Симптом: symbol lookup error: /lib64/libsndfile.so.1: undefined symbol: lame_encode_buffer_interleaved_int

Причина: линковщик загружает игровую libmp3lame.so.0 из data/lib/ (старая, без символа), а libsndfile.so.1 из /lib64/ — и находит уже загруженную старую копию.

Решение: заменить игровую libmp3lame.so.0 на системную:

cp /lib64/libmp3lame.so.0 /run/media/system/Data/Games/.../data/lib/libmp3lame.so.0

Статус Worms W.M.D (native Linux)

  • Версия: MojoSetup-инсталлятор с rutracker (Worms W.M.D [239.583366] [Native])
  • Путь: /run/media/system/Data/Games/Worms W.M.D Native/Worms W.M.D/
  • Symlink: /home/bazzite/worms_wmd.sh → .../run.sh
  • Steam shortcut: entry 109, appid = -364361099 (unsigned: 3930606197)
  • LaunchOptions: %command%
  • Статус: запускается

Остановка Steam (Bazzite game session)

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 всё ещё активен — повторить.

Единственный правильный способ — vdf.binary_load

Использовать модуль vdf (установлен на HTPC через pip). Никогда не редактировать бинарный VDF через ручную сборку байтов или замену строк — это ломает файл.

import os, vdf, shutil, binascii, struct

vdf_path = os.path.expanduser('~/.steam/steam/userdata/147839491/config/shortcuts.vdf')

# 1. Бэкап
shutil.copy2(vdf_path, vdf_path + '.bak')

# 2. Загрузить существующий
with open(vdf_path, 'rb') as f:
    data = vdf.binary_load(f)

shortcuts = data.get('shortcuts', {})

# 3. Вычислить appid (алгоритм Steam)
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]

# 4. Создать entry
next_idx = str(len(shortcuts))
shortcuts[next_idx] = {
    'AppName': 'Game Name',
    'Exe': '/path/to/game.exe',
    'StartDir': '/path/to/',
    'LaunchOptions': '',
    'appid': compute_appid('Game Name', '/path/to/game.exe'),
    'IsHidden': 0,
    'AllowDesktopConfig': 1,
    'AllowOverlay': 1,
    'OpenVR': 0,
    'Devkit': 0,
    'DevkitGameID': '',
    'DevkitOverrideAppID': 0,
    'LastPlayTime': 0,
    'FlatpakAppID': '',
    'sortas': '',
    'tags': {},
}

# 5. Записать
data['shortcuts'] = shortcuts
with open(vdf_path, 'wb') as f:
    vdf.binary_dump(data, f)

CompatToolMapping (Proton) в config.vdf

После добавления shortcut нужно прописать Proton в текстовом config.vdf:

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. Смотреть там.

Бэкап

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 игры — сразу скачивай и wide (920×430, {appid}.png), а не только poster (600×900, {appid}p.png). Без wide постером игра выглядит как пустая белая заглушка в основном режиме библиотеки.

Naming convention

Файлы кладутся в ~/.steam/steam/userdata/147839491/config/grid/:

Размер Формат файла Назначение
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. Пример: -13217200742973247222.

Процедура (как делал Whale)

  1. Найти постеры на SteamGridDB — зайти на https://www.steamgriddb.com/game/<ID>, отфильтровать по 920×430.

    • Для новых игр: поискать <игра> site:steamgriddb.com
    • Wide grids часто меньше по количеству чем posters (600×900), их ищем в секции Grids → фильтр Dimensions: 920×430
  2. Получить прямую ссылку CDN — thumbnails на странице grid'a ведут на cdn2.steamgriddb.com/thumb/<hash>.jpg, а полный файл лежит по cdn2.steamgriddb.com/grid/<hash>.png.

  3. Скачать локально:

    curl -s -o /tmp/<appid>.png "https://cdn2.steamgriddb.com/grid/<hash>.png"
    
  4. Закинуть на HTPC через scp в /tmp/:

    scp /tmp/<appid>.png bazzite@192.168.1.86:/tmp/<appid>.png
    
  5. Скопировать в grid-директорию Steam:

    ssh bazzite@192.168.1.86
    cp /tmp/<appid>.png ~/.steam/steam/userdata/147839491/config/grid/<appid>.png
    
  6. Перезапустить Steam (Steam перечитывает grid-файлы свежими при старте, но часто подхватывает и без перезапуска):

    systemctl --user stop gamescope-session-plus@steam.service
    systemctl --user start gamescope-session-plus@steam.service
    

SteamGridDB ключ API

5f0a7cb3366dede2c10860a95795ce60 — для программного доступа.