-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathshortcut_parser.py
More file actions
117 lines (89 loc) · 2.93 KB
/
shortcut_parser.py
File metadata and controls
117 lines (89 loc) · 2.93 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
from pathlib import Path
import struct
from subprocess import Popen, PIPE
import sys
def get_selection(input_list, prompt="") -> str:
""" Get selection from list """
lines = str(min(len(input_list), 8))
with Popen(
["fuzzel", "--dmenu", "-l", lines, "-p", prompt],
stdin=PIPE, stdout=PIPE, stderr=PIPE
) as fuzzel:
selection = fuzzel.communicate(
input=bytes("\n".join(input_list), 'utf-8'))[0]
if fuzzel.returncode != 0:
sys.exit(1)
return selection.decode().strip()
def find_shortcuts_vdf() -> Path:
bases = [
Path.home() / ".steam/steam/userdata",
Path.home() / ".local/share/Steam/userdata",
]
for base in bases:
if not base.exists():
continue
for userdir in base.iterdir():
path = userdir / "config/shortcuts.vdf"
if path.exists():
return path
raise FileNotFoundError("shortcuts.vdf not found")
def parse_shortcuts(path: Path) -> list[dict]:
"""
Parses the binary VDF format and extracts AppName and the
internal Steam AppID.
"""
with path.open("rb") as f:
data = f.read()
i = 0
entries = []
def read_cstring() -> str:
nonlocal i
try:
end = data.index(b"\x00", i)
s = data[i:end].decode("utf-8", "replace")
i = end + 1
return s
except ValueError:
return ""
while i < len(data):
if i >= len(data):
break
t = data[i]
i += 1
if t != 0x00: # 0x00 indicates the start of a shortcut object
continue
read_cstring() # Skip the index key (e.g., "0")
entry = {}
while i < len(data):
field_type = data[i]
i += 1
if field_type == 0x08: # End of current object
break
key = read_cstring()
if field_type == 0x01: # String type
value = read_cstring()
elif field_type == 0x02: # Integer type (The AppID is stored here)
value = struct.unpack_from("<I", data, i)[0]
i += 4
else:
continue
entry[key] = value
if entry:
entries.append(entry)
return entries
def generate_launch_id(vdf_appid: int) -> int:
"""
Converts the 32-bit AppID found in shortcuts.vdf into the
64-bit ID required for the steam://rungameid/ protocol.
"""
# Shift the 32-bit ID to the top and add the shortcut flag (0x02000000)
return (vdf_appid << 32) | 0x02000000
def main():
vdf_path = find_shortcuts_vdf()
shortcuts = parse_shortcuts(vdf_path)
games = {shortcut["AppName"]: shortcut["appid"] for shortcut in shortcuts}
selection = get_selection(list(games))
uri = f"steam://rungameid/{generate_launch_id(games[selection])}"
print(uri)
if __name__ == "__main__":
main()