def test_simple(self): pairs = [ ('a', 'test'), ('a2', b'\xd0\xb0\xd0\xb1\xd0\xb2\xd0\xb3'.decode('utf-8')), ('bb', 1), ('bb2', -500), ('ccc', 1.0), ('dddd', vdf.POINTER(1234)), ('fffff', vdf.COLOR(1234)), ('gggggg', vdf.UINT_64(1234)), ('hhhhhhh', vdf.INT_64(-1234)), ] data = OrderedDict(pairs) data['level1-1'] = OrderedDict(pairs) data['level1-1']['level2-1'] = OrderedDict(pairs) data['level1-1']['level2-2'] = OrderedDict(pairs) data['level1-2'] = OrderedDict(pairs) result = vdf.binary_loads(vdf.binary_dumps(data), mapper=OrderedDict) self.assertEqual(data, result) result = vdf.binary_loads(vdf.binary_dumps(data, alt_format=True), mapper=OrderedDict, alt_format=True) self.assertEqual(data, result) result = vdf.vbkv_loads(vdf.vbkv_dumps(data), mapper=OrderedDict) self.assertEqual(data, result)
def test_simple(self): pairs = [ ('a', 'test'), ('a2', b'\xff\xfe0\x041\x042\x043\x04'.decode('utf-16')), ('bb', 1), ('bb2', -500), ('ccc', 1.0), ('dddd', vdf.POINTER(1234)), ('fffff', vdf.COLOR(1234)), ('gggggg', vdf.UINT_64(1234)), ('hhhhhhh', vdf.INT_64(-1234)), ] data = OrderedDict(pairs) data['level1-1'] = OrderedDict(pairs) data['level1-1']['level2-1'] = OrderedDict(pairs) data['level1-1']['level2-2'] = OrderedDict(pairs) data['level1-2'] = OrderedDict(pairs) result = vdf.binary_loads(vdf.binary_dumps(data), mapper=OrderedDict) self.assertEqual(data, result) result = vdf.binary_loads(vdf.binary_dumps(data, alt_format=True), mapper=OrderedDict, alt_format=True) self.assertEqual(data, result) result = vdf.vbkv_loads(vdf.vbkv_dumps(data), mapper=OrderedDict) self.assertEqual(data, result)
def test_dumps_empty(self): self.assertEqual(vdf.binary_dumps({}), b'') buf = BytesIO() vdf.binary_dump({}, buf) self.assertEqual(buf.getvalue(), b'')
def save(self) -> None: """Save current file with current shortcuts data""" out = {} out['shortcuts'] = self.new_data utils.ensure_directory_for_file(self.path) with open(self.path, 'wb') as ss_file: ss_file.write(vdf.binary_dumps(out))
def func(install_dir, name, steamid64=None, appid_in_vdf=False): if not steamid64: steamid64 = steam_user # Update shortcuts.vdf first steamid3 = int(steamid64) & 0xffffffff shortcuts_by_user[steamid3].append({ "install_dir": install_dir, "name": name }) shortcut_path = ( steam_dir / "userdata" / str(steamid3) / "config" / "shortcuts.vdf" ) shortcut_path.parent.mkdir(parents=True, exist_ok=True) data = {"shortcuts": {}} for shortcut_data in shortcuts_by_user[steamid3]: install_dir_ = shortcut_data["install_dir"] name_ = shortcut_data["name"] entry = { "AppName": name_, "StartDir": install_dir_, "exe": str(Path(install_dir_) / name_) } # Derive the shortcut ID crc_data = b"".join([ entry["exe"].encode("utf-8"), entry["AppName"].encode("utf-8") ]) result = zlib.crc32(crc_data) & 0xffffffff result = result | 0x80000000 shortcut_id = (result << 32) | 0x02000000 if appid_in_vdf: # Store the app ID in `shortcuts.vdf`. This is similar # in behavior to newer Steam releases. entry["appid"] = ~(result ^ 0xffffffff) data["shortcuts"][str(shortcut_id)] = entry shortcut_path.write_bytes(vdf.binary_dumps(data)) appid = get_appid_from_shortcut( target=str(Path(install_dir) / name), name=name ) # Create the fake prefix (steam_dir / "steamapps" / "compatdata" / str(appid) / "pfx").mkdir( parents=True) (steam_dir / "steamapps" / "compatdata" / str(appid) / "pfx.lock").touch() return shortcut_id
def func(proton_app, compat_tool_name): compat_tools.append({ "appid": proton_app.appid, "compat_tool_name": compat_tool_name }) # Add the header section content = struct.pack( APPINFO_STRUCT_HEADER, b"'DV\x07", # Magic number 1 # Universe, protontricks ignores this ) # Serialize the Proton manifest VDF section, which contains # information about different Proton installations appid = 123500 infostate = 2 last_updated = 2 access_token = 2 change_number = 2 sha_hash = b"0"*20 compat_tool_entries = {} for compat_tool in compat_tools: compat_tool_entries[compat_tool["compat_tool_name"]] = { # The name implies it could be a list, but in practice # it has been a string. Do the same thing here. "aliases": compat_tool["compat_tool_name"], "appid": compat_tool["appid"] } binary_vdf = vdf.binary_dumps({ "appinfo": { "extended": { "compat_tools": compat_tool_entries } } }) entry_size = len(binary_vdf) + 40 # Add the only VDF binary section content += struct.pack( APPINFO_STRUCT_SECTION, appid, entry_size, infostate, last_updated, access_token, sha_hash, change_number ) content += binary_vdf # Add the EOF section content += b"ffff" (steam_dir / "appcache" / "appinfo.vdf").write_bytes(content)
def create_backup_of_shortcuts(config, user, dry_run=False): def _create_directory_if_needed(directory): if os.path.exists(directory): return logger.debug("Creating directory: %s" % directory) os.makedirs(directory) backup_dir = backup_directory(config) if backup_dir is None: logger.info( "No backups directory specified, so not backing up shortcuts.vdf before overwriting. See config.txt for more info" ) return _create_directory_if_needed(backup_dir) if not os.path.isdir(backup_dir): logger.warning( "Backup directory path is something other than a directory. Skipping backups" ) return backup_path = shortcuts_backup_path(backup_dir, user) # Make sure the user-specific backups dir exists _create_directory_if_needed(os.path.dirname(backup_path)) formatted_obj = { 'shortcuts': {str(i): item for i, item in enumerate(shortcuts.get_shortcuts(user))} } byte_str = vdf.binary_dumps(formatted_obj) with open(backup_path, 'wb') as fp: fp.write(byte_str)
def test_dumps_value_invalid_type(self): with self.assertRaises(TypeError): vdf.binary_dumps({'': None})
def add_games_to_shortcut_file(steam_path, steamid, games, skip_backup, use_executable_path): if use_executable_path: print() print("⚠ ⚠ WARNING: ⚠ ⚠") print( "Using the path to the executable instead of the Epic Games Launcher URI" ) print("You may experience issues with online games (eg GTAV!)") print() shortcut_file_path = os.path.join(steam_path, "userdata", steamid, "config", "shortcuts.vdf") if not os.path.exists(shortcut_file_path): print(f"Could not find shortcuts file at `{shortcut_file_path}`") print( "Make a shortcut in Steam (Library ➡ ➕ Add Game ➡ Add a Non-Steam Game...) first. Aborting." ) exit(-2) # read in the shortcuts file with open(shortcut_file_path, "rb") as sf: shortcuts = vdf.binary_load(sf) # Make a set that contains the path of every shortcut installed. If a path is already in the # shortuts file, we won't add another one (ie the path is what makes a shortcut unique) all_paths = set() for k, v in shortcuts["shortcuts"].items(): all_paths.add(v["Exe"]) # the shortcuts "list" is actually a dict of "index": value # find the last one so we can add on to the end added = 0 last_index = int(max(shortcuts["shortcuts"].keys())) for game in games: shortcut = game.executable_path if use_executable_path else game.uri if shortcut in all_paths: print( f"Not creating shortcut for `{game.display_name}` since it already has one" ) continue last_index += 1 shortcuts["shortcuts"][str(last_index)] = to_shortcut( game, use_executable_path) added += 1 print(f"Added {added} new games") if added == 0: print(f"No need to update `shortcuts.vdf`") return if skip_backup: print("Not backing up `shortcuts.vdf` since you enjoy danger") os.remove(shortcut_file_path) else: timestamp = time.strftime("%Y%m%d-%H%M%S") new_filename = shortcut_file_path + f"-{timestamp}.bak" print(f"Backing up `shortcuts.vdf` to `{new_filename}`") os.rename(shortcut_file_path, new_filename) new_bytes = vdf.binary_dumps(shortcuts) with open(shortcut_file_path, "wb") as shortcut_file: shortcut_file.write(new_bytes) print("Updated `shortcuts.vdf` successfully!") print() print("➡ Restart Steam!")
def set_shortcuts(user_context, shortcuts): # make the list of shortcuts under a nested "shortcuts" key with each index formatted_obj = {'shortcuts': {str(i): item for i, item in enumerate(shortcuts)}} byte_str = vdf.binary_dumps(formatted_obj) with open(paths.shortcuts_path(user_context), 'wb') as fp: fp.write(byte_str)
def test_dumps_params_invalid(self): with self.assertRaises(TypeError): vdf.binary_dumps([]) with self.assertRaises(TypeError): vdf.binary_dumps(b'aaaa')
def test_dumps_unicode(self): self.assertEqual(vdf.binary_dumps({u('a'): u('b')}), b'\x01a\x00b\x00\x08')
def test_dumps_key_invalid_type(self): with self.assertRaises(TypeError): vdf.binary_dumps({1: 1}) with self.assertRaises(TypeError): vdf.binary_dumps({None: 1})
def test_dumps_unicode_alternative(self): self.assertEqual(vdf.binary_dumps({u('a'): u('b')}, alt_format=True), b'\x01a\x00b\x00\x0b')
def test_dumps_empty(self): self.assertEqual(vdf.binary_dumps({}), b'')
def add_games_to_shortcut_file(steam_path, steamid, games, skip_backup, use_uri): """Add the given games to the shortcut file Args: steam_path (string): location of the root of the steam installation steamid (string): numeric steamid to add shortcuts to games ([GameDefinition]): games to add skip_backup (bool): if we shouldn't back up the file use_uri ([type]): if we should use the EGS uri, or the path to the executable Returns: (([string], integer), string): First element of tuple is a tuple of an array of "results" to display and the number of games added, The second is an error text if something went wrong """ if use_uri: print() print("⚠ ⚠ NOTICE: ⚠ ⚠") print("Using a URI instead of executable path") print("You may experience issues with game streaming") print() else: print() print("⚠ ⚠ NOTICE: ⚠ ⚠") print( "Using the path to the executable instead of the Epic Games Launcher URI" ) print("You may experience issues with online games (eg GTAV!)") print() shortcut_file_path = os.path.join(steam_path, "userdata", steamid, "config", "shortcuts.vdf") if not os.path.exists(shortcut_file_path): message = f"Could not find shortcuts file at `{shortcut_file_path}` \n Make a shortcut in Steam (Library ➡ ➕ Add Game ➡ Add a Non-Steam Game...) first. Aborting." print(message) return None, message # read in the shortcuts file with open(shortcut_file_path, "rb") as sf: shortcuts = vdf.binary_load(sf) # Make a set that contains the path of every shortcut installed. If a path is already in the # shortuts file, we won't add another one (ie the path is what makes a shortcut unique) all_paths = set() for k, v in shortcuts["shortcuts"].items(): exe_key = "Exe" if exe_key not in v: exe_key = "exe" if exe_key not in v: print( "Warning: Entry in shortcuts.vdf has no `Exe` field! Is this a malformed entry?" ) print(v) continue all_paths.add(v[exe_key]) # the shortcuts "list" is actually a dict of "index": value # find the last one so we can add on to the end added = 0 all_indexes = shortcuts["shortcuts"].keys() if len(all_indexes) == 0: last_index = 0 else: last_index = max(int(idx) for idx in all_indexes) game_results = [] for game in games: shortcut = game.uri if use_uri else game.executable_path if shortcut in all_paths: msg = f"{game.display_name}: Not creating shortcut since it already has one" print(msg) game_results.append(msg) continue last_index += 1 shortcuts["shortcuts"][str(last_index)] = to_shortcut(game, use_uri) added += 1 print(f"Added {added} new games") if added == 0: msg = f"No need to update `shortcuts.vdf` - nothing new to add" print(msg) return None, msg if skip_backup: print("Not backing up `shortcuts.vdf` since you enjoy danger") os.remove(shortcut_file_path) else: timestamp = time.strftime("%Y%m%d-%H%M%S") new_filename = shortcut_file_path + f"-{timestamp}.bak" print(f"Backing up `shortcuts.vdf` to `{new_filename}`") os.rename(shortcut_file_path, new_filename) new_bytes = vdf.binary_dumps(shortcuts) with open(shortcut_file_path, "wb") as shortcut_file: shortcut_file.write(new_bytes) print("Updated `shortcuts.vdf` successfully!") print() print("➡ Restart Steam!") return (game_results, added), None
def test_dumps_key_invalid_type(self): with self.assertRaises(TypeError): vdf.binary_dumps({1:1}) with self.assertRaises(TypeError): vdf.binary_dumps({None:1})