3.4 KiB
3.4 KiB
Non-Steam Shortcuts через Binary VDF
Формат файла
Shortcuts хранятся в бинарном VDF:
~/.steam/steam/userdata/147839491/config/shortcuts.vdf
Это НЕ текстовый файл — редактировать через обычную замену строк нельзя, потому что:
- Каждое поле хранится с длиной строки
- Замена строки на другую длины ломает весь файл
- Steam после запуска может перезаписать сломанный файл, удалив все non-Steam игры
Безопасный способ — Python
import os, struct
vdf_path = os.path.expanduser('~/.steam/steam/userdata/147839491/config/shortcuts.vdf')
# 1. Всегда делать бэкап
import shutil
shutil.copy2(vdf_path, vdf_path + '.bak')
with open(vdf_path, 'rb') as f:
data = bytearray(f.read())
# Helper: string field
def sf(name, value):
nb = name.encode() + b'\x00'
vb = value.encode() + b'\x00'
return bytes([0x01, len(nb)]) + nb + bytes([len(vb)]) + vb
# Helper: int field
def zf(name, value):
nb = name.encode() + b'\x00'
vb = struct.pack('<I', value)
return bytes([0x02, len(nb)]) + nb + bytes([len(vb)]) + vb
# Build the entire entry with all fields Steam expects
entry = b''
# appid (int field, type 0x02)
appid_n = b'appid\x00'
appid_v = struct.pack('<I', APPID) # unsigned 32-bit int
entry += bytes([0x02, len(appid_n)]) + appid_n + bytes([len(appid_v)]) + appid_v
entry += sf('AppName', 'Name in Library')
entry += sf('Exe', '/path/to/game.exe')
entry += sf('StartDir', '/path/to/')
entry += sf('icon', '')
entry += sf('ShortcutPath', '')
entry += sf('LaunchOptions', '')
# Boolean fields (int, 0 or 1)
entry += zf('IsHidden', 0)
entry += zf('AllowDesktopConfig', 1)
entry += zf('AllowOverlay', 1)
entry += zf('OpenVR', 0)
entry += zf('Devkit', 0)
entry += sf('DevkitGameID', '')
entry += zf('DevkitOverrideAppID', 0)
entry += zf('LastPlayTime', 0)
entry += sf('FlatpakAppID', '')
entry += sf('sortas', '')
# tags array (must end with \x08\x08\x08)
tags_n = b'tags\x00'
entry += bytes([0x00, len(tags_n)]) + tags_n + b'\x08'
entry += b'\x08\x08\x08'
Добавление нового shortcut (к существующим)
- Найти начало и конец последней записи
- Вставить новую запись перед закрывающими байтами файла
- Записать файл целиком
Структура файла: \x00shortcuts\x00\x00 + \x30\x00 + [entry1] + [entry2] + ... + \x08\x08
Перезагрузка Steam без рестарта
DISPLAY=:0 steam 'steam://reloadshortcuts'
Важно: если Steam уже переписал сломанный файл — изменения не откатятся. Нужно восстанавливать из бэкапа, а потом форсить reload.
Важно
- Никогда не делать replace строк разной длины
- Всегда держать бэкап
- Если non-Steam игры пропали — восстановить из бэкапа, сделать
reloadshortcuts - AppID можно взять любой unsigned 32-bit (лучше > 2000000000 чтобы не конфликтовать с реальными Steam AppID)