From f1843c6d8ab6be71cbd53b247c224574fc0fc71e Mon Sep 17 00:00:00 2001 From: William Hubbard <75867393+Markil3@users.noreply.github.com> Date: Tue, 14 Sep 2021 21:21:42 +0000 Subject: [PATCH] Create loadSteamInfo --- loadSteamInfo | 498 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 498 insertions(+) create mode 100644 loadSteamInfo diff --git a/loadSteamInfo b/loadSteamInfo new file mode 100644 index 0000000..6e50e93 --- /dev/null +++ b/loadSteamInfo @@ -0,0 +1,498 @@ +#!/usr/bin/env python +from steamctl.clients import CachingSteamClient +import sys +import json +import requests +from pathlib import Path +from argparse import Namespace +from io import StringIO + +steamClient = False + +class App: + appId = 0 + name = "" + executable = "" + start = "" + icon = "" + shortcut = "" + options = "" + hidden = False + desktopConfig = False + overlay = False + vr = False + devkit = False + devkitId = "" + playTime = 0 + tags = "null" + + def __init__(self, appId): + self.appId = appId + + def __repr__(self): + return "App %s (%s at %s)" % (self.appId, self.name, self.executable) + + def __hash__(self): + return self.appId + + def __eq__(self, other): + if self.appId != other.appId: + return False + if self.name != other.name: + return False + if self.executable != other.executable: + return False + if self.start != other.start: + return False + if self.icon != other.icon: + return False + if self.shortcut != other.shortcut: + return False + if self.options != other.options: + return False + if self.hidden != other.hidden: + return False + if self.desktopConfig != other.desktopConfig: + return False + if self.overlay != other.overlay: + return False + if self.vr != other.vr: + return False + if self.devkit != other.devkit: + return False + if self.devkitId != other.devkitId: + return False + if self.playTime != other.playTime: + return False + if self.tags != other.tags: + return False + return True + +def parseShortcuts(shortcutFile): + data = [] + with open(shortcutFile, 'rb') as shortcutData: + rawData = shortcutData.read() + i = 0 + buffer = bytearray() + app = None + while i < len(rawData): + buffer.append(rawData[i]) + try: + buffCont = buffer.decode() + except UnicodeDecodeError: + #print(rawData[:i]) + #print("Clearing buffer %s after %d" % (buffer[:-1].decode(), buffer[-1])) + buffer.clear() + buffCont = "" + found = True + if buffCont.endswith("shortcuts"): + buffer.clear() + i += 2 + while rawData[i] != 2: + buffer.append(rawData[i]) + i += 1 + data.append({ + "shortcuts": buffer[:-1].decode() + }) + if buffCont.endswith("appid"): + buffer.clear() + i += 2 + # Some app names do have a 1 for one of their bits + while rawData[i + 1:i + 8] != bytearray("AppName", "utf-8"): + buffer.append(rawData[i]) + i += 1 + app = App(int.from_bytes(buffer, "little")) + data.append(app) + elif buffCont.endswith("AppName"): + buffer.clear() + i += 2 + while rawData[i + 1:i + 4] != bytearray("Exe", "utf-8"): + buffer.append(rawData[i]) + i += 1 + app.name = buffer[:-1].decode() + elif buffCont.endswith("Exe"): + buffer.clear() + i += 2 + while rawData[i + 1:i + 9] != bytearray("StartDir", "utf-8"): + buffer.append(rawData[i]) + i += 1 + app.executable = buffer[:-1].decode() + elif buffCont.endswith("StartDir"): + buffer.clear() + i += 2 + while rawData[i] != 0: + buffer.append(rawData[i]) + i += 1 + app.start = buffer.decode() + elif buffCont.endswith("icon"): + buffer.clear() + i += 2 + while rawData[i + 1:i + 13] != bytearray("ShortcutPath", "utf-8"): + buffer.append(rawData[i]) + i += 1 + app.icon = buffer[:-1].decode() + elif buffCont.endswith("ShortcutPath"): + buffer.clear() + i += 2 + while rawData[i + 1:i + 14] != bytearray("LaunchOptions", "utf-8"): + buffer.append(rawData[i]) + i += 1 + app.shortcut = buffer[:-1].decode() + elif buffCont.endswith("LaunchOptions"): + buffer.clear() + i += 2 + while rawData[i + 1:i + 9] != bytearray("IsHidden", "utf-8"): + buffer.append(rawData[i]) + i += 1 + app.options = buffer[:-1].decode() + elif buffCont.endswith("IsHidden"): + buffer.clear() + i += 2 + while len(buffer) < 3: + buffer.append(rawData[i]) + i += 1 + app.hidden = int.from_bytes(buffer, "little") + elif buffCont.endswith("AllowDesktopConfig"): + buffer.clear() + i += 2 + while len(buffer) < 3: + buffer.append(rawData[i]) + i += 1 + app.desktopConfig = int.from_bytes(buffer, "little") + elif buffCont.endswith("AllowOverlay"): + buffer.clear() + i += 2 + while len(buffer) < 3: + buffer.append(rawData[i]) + i += 1 + app.overlay = int.from_bytes(buffer, "little") + elif buffCont.endswith("OpenVR"): + buffer.clear() + i += 2 + while len(buffer) < 3: + buffer.append(rawData[i]) + i += 1 + app.vr = int.from_bytes(buffer, "little") + elif buffCont.endswith("Devkit") and type(app.devkit) == bool: + buffer.clear() + i += 2 + while len(buffer) < 3: + buffer.append(rawData[i]) + i += 1 + app.devkit = int.from_bytes(buffer, "little") + elif buffCont.endswith("DevkitGameID"): + buffer.clear() + i += 2 + while rawData[i + 1:i + 13] != bytearray("LastPlayTime", "utf-8"): + buffer.append(rawData[i]) + i += 1 + app.devkitId = buffer[:-1].decode() + elif buffCont.endswith("LastPlayTime"): + buffer.clear() + i += 2 + while len(buffer) < 4: + buffer.append(rawData[i]) + i += 1 + app.playTime = int.from_bytes(buffer, "little") + elif buffCont.endswith("tags"): + buffer.clear() + i += 2 + while i < len(rawData) and rawData[i] != 2: + buffer.append(rawData[i]) + i += 1 + if i < len(rawData) and rawData[i] == 2: + buffer = buffer[:-1] + app.tags = buffer.decode() + else: + found = False + #print('\r' + buffCont, end='') + if found: + #try: + #print(": %s" % buffer.decode()) + #except UnicodeDecodeError: + #print(": %s" % int.from_bytes(buffer, "little")) + buffer.clear() + i += 1 + return data + +def writeShortcuts(apps, shortcutFile): + encoding = "utf-8" + data = bytearray([0x00]) + for app in apps: + if type(app) == dict: + data += bytearray("shortcuts", encoding) + data.append(0x00) + data += bytearray(apps[0]["shortcuts"], encoding) + else: + data.append(0x00) + data.append(0x02) + data += bytearray("appid", encoding) + data.append(0x00) + data += int(app.appId).to_bytes(4, "little") + data.append(0x01) + data += bytearray("AppName", encoding) + data.append(0x00) + data += bytearray(app.name, encoding) + data.append(0x00) + data.append(0x01) + data += bytearray("Exe", encoding) + data.append(0x00) + data += bytearray(app.executable, encoding) + data.append(0x00) + data.append(0x01) + data += bytearray("StartDir", encoding) + data.append(0x00) + data += bytearray(app.start, encoding) + data.append(0x00) + data.append(0x01) + data += bytearray("icon", encoding) + data.append(0x00) + data += bytearray(app.icon, encoding) + data.append(0x00) + data.append(0x01) + data += bytearray("ShortcutPath", encoding) + data.append(0x00) + data += bytearray(app.shortcut, encoding) + data.append(0x00) + data.append(0x01) + data += bytearray("LaunchOptions", encoding) + data.append(0x00) + data += bytearray(app.options, encoding) + data.append(0x00) + data.append(0x02) + data += bytearray("IsHidden", encoding) + data.append(0x00) + data += int(app.hidden).to_bytes(3, "little") + data.append(0x00) + data.append(0x02) + data += bytearray("AllowDesktopConfig", encoding) + data.append(0x00) + data += int(app.desktopConfig).to_bytes(3, "little") + data.append(0x00) + data.append(0x02) + data += bytearray("AllowOverlay", encoding) + data.append(0x00) + data += int(app.overlay).to_bytes(3, "little") + data.append(0x00) + data.append(0x02) + data += bytearray("OpenVR", encoding) + data.append(0x00) + data += int(app.vr).to_bytes(3, "little") + data.append(0x00) + data.append(0x02) + data += bytearray("Devkit", encoding) + data.append(0x00) + data += int(app.devkit).to_bytes(3, "little") + data.append(0x00) + data.append(0x01) + data += bytearray("DevkitGameID", encoding) + data.append(0x00) + data += bytearray(app.devkitId, encoding) + data.append(0x00) + data.append(0x02) + data += bytearray("LastPlayTime", encoding) + data.append(0x00) + data += int(app.playTime).to_bytes(4, "little") + data.append(0x00) + data += bytearray("tags", encoding) + data.append(0x00) + data += bytearray(app.tags, encoding) + with open(shortcutFile, 'wb') as stream: + stream.write(data) + +def getSteamArgs(): + global steamClient + if not steamClient: + args = Namespace() + args.user = None + args.anonymous = True + args.skip_licenses = True + steamClient = CachingSteamClient() + print("Logging into steam") + steamClient.login_from_args(args) + return steamClient + +def getHeader(code): + return "https://cdn.cloudflare.steamstatic.com/steam/apps/%s/header.jpg" % code + +def getLibraryTall(code): + return "https://cdn.cloudflare.steamstatic.com/steam/apps/%s/library_600x900.jpg" % code + +def getBackground(code): + return "https://cdn.cloudflare.steamstatic.com/steam/apps/%s/library_hero.jpg" % code + +def getLogo(code): + return "https://cdn.cloudflare.steamstatic.com/steam/apps/%s/logo.png" % code + +def getGameInfo(code): + fromContent = requests.get(fromURL, allow_redirects=True) + return "https://cdn.cloudflare.steamstatic.com/steam/apps/%s/logo.png" % code + +def copyContent(name, content, byte=True): + userdata = Path(Path.home(), ".steam", "root", "userdata") + for user in userdata.iterdir(): + toFile = Path(user, name) + print("Writing to %s" % (toFile)) + mode = 'w' + if byte: + mode += 'b' + with open(toFile, mode) as stream: + stream.write(content) + +def copyFile(fromURL, toCode): + fromURLComp = fromURL.split('/') + fromName = fromURLComp[6] + fromCode = fromURLComp[5] + userdata = Path(Path.home(), ".steam", "root", "userdata") + + # Gets the final file name + if fromName == "library_hero.jpg": + toName = "_hero.jpg" + elif fromName == "library_600x900.jpg": + toName = "p.jpg" + elif fromName == "header.jpg": + toName = ".jpg" + else: + toName = "_" + fromName + toName = toCode + toName + + print("Copying %s (%s) to %s" % (fromName, fromCode, toName)) + fromContent = requests.get(fromURL, allow_redirects=True) + copyContent(Path("config", "grid", toName), fromContent.content) + +def copyContents(ids): + client = getSteamArgs() + fromCodes = [] + toCodes = [] + reverse = {} + for id in ids: + fromCode = id[0] + toCode = id[1] + reverse[toCode] = fromCode + fromCodes.append(fromCode) + toCodes.append(toCode) + copyFile(getHeader(str(fromCode)), str(toCode)) + copyFile(getLibraryTall(str(fromCode)), str(toCode)) + copyFile(getBackground(str(fromCode)), str(toCode)) + copyFile(getLogo(str(fromCode)), str(toCode)) + data = client.get_product_info(apps=fromCodes) + i = 0 + for i in range(len(ids)): + logo = data["apps"][ids[i][0]]["common"]["library_assets"]["logo_position"] + logo2 = { + "nversion": 1, + "logoPosition": { + "pinnedPosition": logo["pinned_position"], + "nWidthPct": logo["width_pct"], + "nHeightPct": logo["height_pct"] + } + } + copyContent(Path("config", "grid", str(ids[i][1]) + ".json"), json.dumps(logo2), byte=False) + + logo2 = [[ + "achievements", { + "version":2, + "data": { + "vecHighlight": [], + "vecUnachieved": [], + "vecAchievedHidden": [], + "nTotal": 0, + "nAchieved": 0 + } + } + ], [ + "customimage", { + "version": 1, + "data": logo2 + } + ] + ] + copyContent(Path("config", "librarycache", str(ids[i][1]) + ".json"), json.dumps(logo2), byte=False) + + userdata = Path(Path.home(), ".steam", "root", "userdata") + for user in userdata.iterdir(): + shortcutData = parseShortcuts(Path(user, "config", "shortcuts.vdf")) + for app in shortcutData: + if type(app) != dict: + if app.appId in toCodes: + print("Setting shortcut data for %s", app.name) + app.name = data["apps"][reverse[app.appId]]["common"]["name"] + app.icon = str(Path(user, "config", "grid", str(app.appId) + ".jpg")) + print("Setting %s to %s" % (app.name, app.icon)) + writeShortcuts(shortcutData, Path(user, "config", "shortcuts.vdf")) + + +if __name__ == "__main__": + keys = [] + ids = [] + ops = [] + for term in sys.argv[1:]: + if not term.startswith('-'): + keys.append(term) + if term.isnumeric(): + if len(ids) > 0 and type(ids[-1]) == int: + ids[-1] = (ids[-1], int(term)) + else: + ids.append(int(term)) + else: + ids = None + else: + if term[1] == '-': + ops.append(term[2:]) + else: + for op in term[1:]: + ops.append(op) + if len(keys) < 2 and not "a" in ops: + print("%s [OPTIONS] ( )..." % __file__) + print("%s [OPTIONS] ..." % __file__) + exit(1) + if ids: + if type(ids[-1]) == int: + print("Must have an even number of fromID - toId pairs") + exit(2) + + copyContents(ids) + else: + tempApps = {} + apps = [] + userdata = Path(Path.home(), ".steam", "root", "userdata") + for user in userdata.iterdir(): + shortcutData = parseShortcuts(Path(user, "config", "shortcuts.vdf")) + for app in shortcutData: + if type(app) != dict: + if "a" in ops: + apps.append(app) + else: + for term in keys: + if term.lower() in app.name.lower(): + if not app in tempApps: + tempApps[app] = 1 + else: + tempApps[app] += 1 + if not "a" in ops: + for app in tempApps: + if tempApps[app] / len(keys) >= 0.7: + apps.append(app) + if len(apps) == 0: + if "v" in ops: + print("No matches found") + exit(0) + elif len(apps) == 1 and not "v" in ops and not "l" in ops: + print(apps[0].appId) + else: + for app in apps: + print("%d - %s" % (app.appId, app.name)) + if "l" in ops: + print("\tExecutable: %s" % app.executable) + print("\tRuntime Directory: %s" % app.start) + print("\tIcon: %s" % app.icon) + print("\tShortcut: %s" % app.shortcut) + print("\tRuntime Options: %s" % app.options) + print("\tIs Hidden: %s" % bool(app.hidden)) + print("\tDesktop Configuration: %s" % bool(app.desktopConfig)) + print("\tOverlay Enabled: %s" % bool(app.overlay)) + print("\tIs VR: %s" % bool(app.vr)) + print("\tDevkit: %d" % app.devkit) + print("\tDevkit ID: %s" % app.devkitId) + print("\tDate of last Play: %s" % app.playTime) + print("\tTags: %s" % app.tags)