def func(name, appid, compat_tool_name=None, library_dir=None, add_prefix=True, required_tool_app=None): if not library_dir: steamapps_dir = steam_dir / "steamapps" else: steamapps_dir = library_dir / "steamapps" install_path = steamapps_dir / "common" / name install_path.mkdir(parents=True) if required_tool_app: (install_path / "toolmanifest.vdf").write_text( vdf.dumps({ "manifest": { "require_tool_appid": required_tool_app.appid } })) (steamapps_dir / "appmanifest_{}.acf".format(appid)).write_text( vdf.dumps({ "AppState": { "appid": str(appid), "name": name, "installdir": name } })) # Add Wine prefix if add_prefix: (steamapps_dir / "compatdata" / str(appid) / "pfx").mkdir(parents=True) (steamapps_dir / "compatdata" / str(appid) / "pfx.lock").touch() # Set the preferred Proton installation for the app if provided if compat_tool_name: steam_config = vdf.loads(steam_config_path.read_text()) steam_config["InstallConfigStore"]["Software"]["Valve"]["Steam"][ "CompatToolMapping"][str(appid)] = { "name": compat_tool_name, "config": "", "Priority": "250" } steam_config_path.write_text(vdf.dumps(steam_config)) steam_app = SteamApp(name=name, appid=appid, install_path=str(steamapps_dir / "common" / name), prefix_path=str(steamapps_dir / "compatdata" / str(appid) / "pfx")) if required_tool_app: # In actual code, `required_tool_app` attribute is populated later # when we have retrieved all Steam apps and can find the # corresponding app using its app ID steam_app.required_tool_app = required_tool_app return steam_app
def test_dump_params_invalid(self): # pretty/escaped only accept bool with self.assertRaises(TypeError): vdf.dump({'a': 1}, StringIO(), pretty=1) with self.assertRaises(TypeError): vdf.dumps({'a': 1}, pretty=1) with self.assertRaises(TypeError): vdf.dump({'a': 1}, StringIO(), escaped=1) with self.assertRaises(TypeError): vdf.dumps({'a': 1}, escaped=1)
def write_databases_config(self): """ write database config databases.cfg """ sourcemod_cfg = self._config.sourcemod configured_dbs = sourcemod_cfg.get("databases") if not configured_dbs: self._log.info("no databases configured") return True database_config_path = os.path.join( self._root_dir, "csgo/addons/sourcemod/configs/databases.cfg" ) db_config_file = open(database_config_path, "r") self._log.info( "loading databases configuration '%s' ..." % database_config_path ) db_config = vdf.load(db_config_file, mapper=OrderedDict) db_config_file.close() for database_name, database_config in configured_dbs.iteritems(): db_config["Databases"][database_name] = database_config self._log.info( "writing databases config '%s' ..." % database_config_path ) with open(database_config_path, "w") as config_file: config_file.write(vdf.dumps(db_config, pretty=True)) return True
def write_plugin_updater(self, filename=None): self.create_plugin_updater() data = vdf.dumps(self.updater, pretty=True) if filename is None: filename = self.parent.getpath( ["updater-data", "update-%s.txt" % self.name]) self.parent.write_file(filename=filename, data=data)
def keyvalues_rpc_notification(method: str, **kwargs): ''' Performs a KeyValues RPC notification; no return value is received. ''' with socket.create_connection(RPC_CONNECTION) as caller: call_dict = {"rpc_version": "2.0", "method": method, "params": kwargs} caller.send(vdf.dumps({'keyvalues_rpc': call_dict}).encode('utf8'))
def test_get_steam_lib_paths_adjust_flatpak_steam_path( self, steam_dir, steam_library_factory, home_dir): """ Retrieve Steam library folders and ensure that the "~/.local/share/Steam" path is adjusted in the library folder configuration file if Steam is installed using Flatpak. Regression test for flathub/com.github.Matoking.protontricks#10 """ flatpak_steam_dir = \ home_dir / ".var/app/com.valvesoftware.Steam/data/Steam" flatpak_steam_dir.mkdir(parents=True) steam_dir.rename(str(flatpak_steam_dir)) shutil.rmtree(str(home_dir / ".local")) libraryfolders_data = { "libraryfolders": { "0": { "path": str(home_dir / ".local/share/Steam") } } } (flatpak_steam_dir / "steamapps/libraryfolders.vdf").write_text( vdf.dumps(libraryfolders_data), "utf-8") library_paths = get_steam_lib_paths( home_dir / ".var/app/com.valvesoftware.Steam/data/Steam") # The only library folder should point under "~/.var/app", even if the # path in the configuration file is "~/.local/share/Steam". # This needs to be done to adjust for the different path in the # Steam Flatpak sandbox. assert len(library_paths) == 1 assert str(library_paths[0]) == str(flatpak_steam_dir)
def test_dumps_pretty_output(self): tests = [ [ { '1': '1' }, '"1" "1"\n', ], [ { '1': { '2': '2' } }, '"1"\n{\n\t"2" "2"\n}\n', ], [ { '1': { '2': { '3': '3' } } }, '"1"\n{\n\t"2"\n\t{\n\t\t"3" "3"\n\t}\n}\n', ], ] for test, expected in tests: self.assertEqual(vdf.dumps(test, pretty=True), expected)
def test_get_steam_lib_paths_duplicate_paths(self, steam_dir, steam_library_factory): """ Retrive Steam library folders and ensure duplicate paths (eg. an existing path OR a symlink that resolves to an existing path) are removed from the returned list. Regression test for #118 """ library_dir = steam_library_factory("TestLibrary_A") # Create a symlink from TestLibrary_B to TestLibrary_A (library_dir.parent / "TestLibrary_B").symlink_to(library_dir) # Add the duplicate library folder vdf_data = vdf.loads( (steam_dir / "steamapps" / "libraryfolders.vdf").read_text()) vdf_data["LibraryFolders"]["2"] = str(library_dir.parent / "TestLibrary_B") (steam_dir / "steamapps" / "libraryfolders.vdf").write_text( vdf.dumps(vdf_data)) library_paths = get_steam_lib_paths(steam_dir) # Only two paths should be returned assert len(library_paths) == 2 assert steam_dir in library_paths assert library_dir in library_paths
def func(name, new_struct=False): library_dir = Path(str(tmp_path)) / "mnt" / name library_dir.mkdir(parents=True) # Update libraryfolders.vdf with the new library folder libraryfolders_config = vdf.loads( steam_libraryfolders_path.read_text()) # Each new library adds a new entry into the config file with the # field name that starts from 1 and increases with each new library # folder. # Newer Steam releases stores the library entry in a dict, while # older releases just store the full path as the field value library_id = len(libraryfolders_config["LibraryFolders"].keys()) - 1 if new_struct: libraryfolders_config["LibraryFolders"][str(library_id)] = { "path": str(library_dir), "label": "", "mounted": "1" } else: libraryfolders_config["LibraryFolders"][str(library_id)] = \ str(library_dir) steam_libraryfolders_path.write_text(vdf.dumps(libraryfolders_config)) return library_dir
def func( name, appid, compat_tool_name, is_default_proton=True, library_dir=None, required_tool_app=None): steam_app = steam_app_factory( name=name, appid=appid, library_dir=library_dir, required_tool_app=required_tool_app ) shutil.rmtree(str(Path(steam_app.prefix_path).parent)) steam_app.prefix_path = None install_path = Path(steam_app.install_path) (install_path / "proton").touch() (install_path / "dist" / "bin").mkdir(parents=True) (install_path / "dist" / "bin" / "wine").touch() (install_path / "dist" / "bin" / "wineserver").touch() # Update config if is_default_proton: steam_config = vdf.loads(steam_config_path.read_text()) steam_config["InstallConfigStore"]["Software"]["Valve"]["Steam"][ "CompatToolMapping"]["0"] = { "name": compat_tool_name, "config": "", "Priority": "250" } steam_config_path.write_text(vdf.dumps(steam_config)) # Add the Proton installation to the appinfo.vdf, which contains # a manifest of all official Proton installations appinfo_factory( proton_app=steam_app, compat_tool_name=compat_tool_name ) return steam_app
def func(name, compat_tool_dir=None): if not compat_tool_dir: compat_tool_dir = \ steam_dir.parent / "root" / "compatibilitytools.d" / name else: compat_tool_dir = compat_tool_dir / name compat_tool_dir.mkdir(parents=True, exist_ok=True) (compat_tool_dir / "proton").touch() (compat_tool_dir / "proton").chmod(0o744) (compat_tool_dir / "dist" / "bin").mkdir(parents=True) (compat_tool_dir / "dist" / "bin" / "wine").touch() (compat_tool_dir / "dist" / "bin" / "wineserver").touch() (compat_tool_dir / "compatibilitytool.vdf").write_text( vdf.dumps({ "compatibilitytools": { "compat_tools": { name: { "install_path": ".", "display_name": name, "from_oslist": "windows", "to_oslist": "linux" } } } }) ) return SteamApp( name=name, install_path=str(compat_tool_dir) )
def do_map(path = None, filename = None): pprint(path) pprint(filename) if filename is None: filename = os.path.join(base_path, "maps", "src", "comacchio_d.vmf") d = vdf.parse(open(filename), mapper=vdf.VDFDict) print(d) print(vdf.dumps(d, pretty=True))
def do_map(path=None, filename=None): pprint(path) pprint(filename) if filename is None: filename = os.path.join(base_path, "maps", "src", "comacchio_d.vmf") d = vdf.parse(open(filename), mapper=vdf.VDFDict) print(d) print(vdf.dumps(d, pretty=True))
def func(name, appid, compat_tool_name=None, library_dir=None, add_prefix=True): if not library_dir: steamapps_dir = steam_dir / "steamapps" else: steamapps_dir = library_dir / "steamapps" (steamapps_dir / "common" / name).mkdir(parents=True) (steamapps_dir / "appmanifest_{}.acf".format(appid)).write_text( vdf.dumps({ "AppState": { "appid": str(appid), "name": name, "installdir": name } })) # Add Wine prefix if add_prefix: (steamapps_dir / "compatdata" / str(appid) / "pfx").mkdir(parents=True) (steamapps_dir / "compatdata" / str(appid) / "pfx.lock").touch() # Set the preferred Proton installation for the app if provided if compat_tool_name: steam_config = vdf.loads(steam_config_path.read_text()) steam_config["InstallConfigStore"]["Software"]["Valve"]["Steam"][ "CompatToolMapping"][str(appid)] = { "name": compat_tool_name, "config": "", "Priority": "250" } steam_config_path.write_text(vdf.dumps(steam_config)) return SteamApp(name=name, appid=appid, install_path=str(steamapps_dir / "common" / name), prefix_path=str(steamapps_dir / "compatdata" / str(appid) / "pfx"))
def set_last_user(self, username): reg_file = os.path.expanduser('~/.steam/registry.vdf') reg_data = vdf.loads(read_file(reg_file)) steam_config = subkey_lookup(reg_data, r'Registry\HKCU\Software\Valve\Steam') steam_config['AutoLoginUser'] = username steam_config['RememberPassword'] = '******' reg_data = vdf.dumps(reg_data, pretty=True) with open(reg_file, 'wt') as f: f.write(reg_data)
def dump(self, data=None, type=None): """Dump VDFDict as specified type""" if data is None: data = self.get_data() if type == "yaml": data = yaml.safe_dump(json.loads(self.dump(data=data, type="json")), default_flow_style=False) elif type == "json": data = json.dumps(data, indent=4) else: data = vdf.dumps(data, pretty=True) return data
def test_routine_dump_writing(self): class CustomDict(dict): pass for mapper in (dict, CustomDict): src = mapper({"asd": "123"}) expected = vdf.dumps(src) vdf.dump(src, self.f) self.f.seek(0) self.assertEqual(expected, self.f.read())
def __call__(self, handle): # language = pycountry.languages.get(alpha_2=self.language) # file_base = copy.deepcopy(vdfTranslationBase) file_base = vdf.loads(vdf.dumps(vdfTranslationBase)) # copy structure # file_base['lang']['Language'] = language.name file_base['lang']['Language'] = self.language.capitalize() for unit in self.units: file_base['lang']['Tokens'][unit.key] = unit.text file_content = vdf.dump(file_base, handle, pretty=True)
def create_new_file(cls, filename, language, base): if base: store = cls.load(base) cls.untranslate_store(store, language) elif cls.new_translation is None: raise ValueError('Not supported') else: store = cls.load(vdf.dumps(cls.new_translation)) # Copy translation obj with codecs.open(filename, 'w', store.codec if base else defaultCodec) as handle: # vdf.dump(store, handle, pretty=True) VDFSerializer(store.units, language)(handle)
def steam_libraryfolders_path(steam_dir): """ Fake libraryfolders.vdf file at ~/.steam/steam/steamapps/libraryfolders.vdf """ LIBRARYFOLDERS_DEFAULT = { "LibraryFolders": { # These fields are completely meaningless as far as Protontricks # is concerned "TimeNextStatsReport": "281946123974", "ContentStatsID": "23157498213759321679" } } (steam_dir / "steamapps" / "libraryfolders.vdf").write_text( vdf.dumps(LIBRARYFOLDERS_DEFAULT)) return steam_dir / "steamapps" / "libraryfolders.vdf"
def func(name, steamid64=None): if not steamid64: steamid64 = random.randint((2**32), (2**64) - 1) steam_users.append({"name": name, "steamid64": steamid64}) loginusers_path = steam_dir / "config" / "loginusers.vdf" data = {"users": {}} for i, user in enumerate(steam_users): data["users"][str(user["steamid64"])] = { "AccountName": user["name"], # This ensures the newest Steam user is assumed to be logged-in "Timestamp": i } loginusers_path.write_text(vdf.dumps(data)) return steamid64
def test_dumps_pretty_output(self): tests = [ [ {'1': '1'}, '"1" "1"\n', ], [ {'1': {'2': '2'}}, '"1"\n{\n\t"2" "2"\n}\n', ], [ {'1': {'2': {'3': '3'}}}, '"1"\n{\n\t"2"\n\t{\n\t\t"3" "3"\n\t}\n}\n', ], ] for test, expected in tests: self.assertEqual(vdf.dumps(test, pretty=True), expected)
def func(name): library_dir = Path(str(tmp_path)) / "mnt" / name library_dir.mkdir(parents=True) # Update libraryfolders.vdf with the new library folder libraryfolders_config = vdf.loads( steam_libraryfolders_path.read_text()) # Each new library adds a new entry into the config file with the # field name that starts from 1 and increases with each new library # folder. library_id = len(libraryfolders_config["LibraryFolders"].keys()) - 1 libraryfolders_config["LibraryFolders"][str(library_id)] = \ str(library_dir) steam_libraryfolders_path.write_text(vdf.dumps(libraryfolders_config)) return library_dir
def func(name): tool_dir = steam_dir.parent / "root" / "compatibilitytools.d" / name tool_dir.mkdir(parents=True) (tool_dir / "proton").touch() (tool_dir / "proton").chmod(0o744) (tool_dir / "compatibilitytool.vdf").write_text( vdf.dumps({ "compatibilitytools": { "compat_tools": { name: { "install_path": ".", "display_name": name, "from_oslist": "windows", "to_oslist": "linux" } } } })) return SteamApp(name=name, install_path=str(tool_dir))
def steam_config_path(steam_dir): """ Fake Steam config file at ~/.steam/steam/config/config.vdf """ CONFIG_DEFAULT = { "InstallConfigStore": { "Software": { "Valve": { "Steam": { "ToolMapping": {}, "CompatToolMapping": {} } } } } } (steam_dir / "config" / "config.vdf").write_text(vdf.dumps(CONFIG_DEFAULT)) yield steam_dir / "config" / "config.vdf"
def func(name, appid, runtime_dir_name, library_dir=None): runtime_app = steam_app_factory( name=name, appid=appid, library_dir=library_dir, add_prefix=False ) install_path = Path(runtime_app.install_path) runtime_root_path = install_path / runtime_dir_name / "files" (runtime_root_path / "i686-pc-linux-gnu" / "lib").mkdir(parents=True) (runtime_root_path / "x86_64-pc-linux-gnu" / "lib").mkdir(parents=True) (install_path / "run.sh").touch() (install_path / "toolmanifest.vdf").write_text( vdf.dumps({ "manifest": {"commandline": "/run.sh --"} }) ) return runtime_app
def test_steam_app_userconfig_name(self, steam_app_factory): """ Try creating a SteamApp from an older version of the app manifest which contains the application name in a different field See GitHub issue #103 for details """ steam_app = steam_app_factory(name="Fake game", appid=10) appmanifest_path = \ Path(steam_app.install_path).parent.parent / "appmanifest_10.acf" data = vdf.loads(appmanifest_path.read_text()) # Older installations store the name in `userconfig/name` instead del data["AppState"]["name"] data["AppState"]["userconfig"] = {"name": "Fake game"} appmanifest_path.write_text(vdf.dumps(data)) app = SteamApp.from_appmanifest(path=appmanifest_path, steam_lib_paths=[]) assert app.name == "Fake game"
def keyvalues_rpc_call(method: str, **kwargs): ''' Performs a KeyValues RPC call. ''' with socket.create_connection(RPC_CONNECTION) as caller: call_dict = { "rpc_version": "2.0", "method": method, "id": "string_identifier", "params": kwargs } caller.send(vdf.dumps({'keyvalues_rpc': call_dict}).encode('utf8')) response = vdf.loads( caller.recv(4096).decode('utf8')).get('keyvalues_rpc') error = response.get('error') if error: raise KVRPCException({ 'message': error.get('message'), 'code': error.get('code') }) result = response.get('result') return dict(result) if result else None
def write_plugin_updater(self,filename=None): self.create_plugin_updater() data = vdf.dumps(self.updater, pretty=True) if filename is None: filename = self.parent.getpath(["updater-data", "update-%s.txt" % self.name]) self.parent.write_file(filename=filename,data=data)
def copy(self, obj=None): """Create a copy of a VDFDict object.""" if obj is None: obj = self.get_data() return vdf.loads(vdf.dumps(obj, pretty=True), mapper=vdf.VDFDict)
def dump_vdf(self, data): """Dump VDF content to console.""" print(vdf.dumps(data, pretty=True))
def get_new_file_content(cls): sample_text = vdf.dumps(cls.new_translation, pretty=True) sample_bytes = codecs.encode(sample_text, defaultCodec) return sample_bytes
def run(self): global screenshotDownloadList,cur_ss while True: threadlock.acquire() if len(screenshotDownloadList)==0: threadlock.release() return else: curKey=list(screenshotDownloadList.keys())[0] ssInfo = screenshotDownloadList[curKey].pop() if len(screenshotDownloadList[curKey])==0: screenshotDownloadList.pop(curKey) threadlock.release() while(True): try: filepage = steamSession.request("get","https://steamcommunity.com/sharedfiles/filedetails/?id="+str(ssInfo.FileID)+"&l=schinese",verify=False,timeout=MAX_TIMEOUT) except: continue if filepage.ok==True: break filepage_html = etree.HTML(filepage.text) fileurl= filepage_html.xpath("//div[@class=\"actualmediactn\"]/a")[0].get("href") description_page=filepage_html.xpath("//div[@class=\"screenshotDescription\"]") description="" if len(description_page)!=0: description=description_page[0].text #判断本地是否已有这个截图 match_result = gethscreenshotRe.search(fileurl) hscreenshot="0" if match_result!=None: hscreenshot=match_result.group(1) if existedScreenshot.__contains__(hscreenshot): cur_ss+=1 continue dataPage=filepage_html.xpath("//div[@class=\"detailsStatsContainerRight\"]")[0] match_result=getresRe.search(dataPage[2].text) width = match_result.group(1) height=match_result.group(2) dateStr = dataPage[1].text if dateStr.find("年")==-1: dateStr=str(time.localtime(time.time()).tm_year) +"年"+dateStr match_result = gettimeRe.search(dateStr) year=int(match_result.group(1)) month=int(match_result.group(2)) day=int(match_result.group(3)) ismorning=match_result.group(4)=="上" hour=int(match_result.group(5))+(12 if ismorning==False else 0) minute=int(match_result.group(5)) while True: try: screenshotData = steamSession.request("get",fileurl,verify=False,timeout=MAX_DOWNLOADTIME) except: continue if screenshotData.ok==True: break if syncMode==False: with open(syncFolder+"\\"+hscreenshot+".jpg","wb+") as f: f.write(screenshotData.content) cur_ss+=1 else: timestr = str(year)+str(month)+str(day)+str(hour)+str(minute) ss_index=1 while os.path.exists(syncFolder+"\\"+curKey+"\\screenshots\\"+timestr+"00_"+str(ss_index)+".jpg"): ss_index+=1 os.makedirs(syncFolder+"\\"+curKey+"\\screenshots",exist_ok=True) screenshotFilename=syncFolder+"\\"+curKey+"\\screenshots\\"+timestr+"00_"+str(ss_index)+".jpg" with open(screenshotFilename,"wb+") as f: f.write(screenshotData.content) if CREATE_THUMBNAILS==True: ppath=syncFolder+"\\"+curKey+"\\screenshots\\thumbnails" os.makedirs(ppath,exist_ok=True) img = Image.open(screenshotFilename) img1=img.resize((200,int(200.0/img.size[0]*img.size[1])),Image.BILINEAR) img1.save(syncFolder+"\\"+curKey+"\\screenshots\\thumbnails\\"+timestr+"00_"+str(ss_index)+".jpg") del img,img1 threadlock.acquire() if os.path.exists(syncFolder+"\\..\\screenshots.vdf"): fp=open(syncFolder+"\\..\\screenshots.vdf","r",encoding="utf8") screenshotCfg=vdf.load(fp) fp.close() if ScreenshotsKey not in screenshotCfg: screenshotCfg={ScreenshotsKey:{}} if curKey not in screenshotCfg[ScreenshotsKey]: screenshotCfg[ScreenshotsKey][curKey]={} targetIndex=len(screenshotCfg[ScreenshotsKey][curKey].keys()) screenshotCfg[ScreenshotsKey][curKey][str(targetIndex)]={ "type":"1", "filename":curKey+ "/screenshots/"+timestr+"00_"+str(ss_index)+".jpg", "thumbnail":curKey+ "/screenshots/thumbnails/"+timestr+"00_"+str(ss_index)+".jpg", "vrfilename":"", "imported":"1", "spoiler":"1", "width":width, "height":height, "gameid" :curKey, "creation":str(int(time.mktime(time.strptime(str(year)+ "/"+str(month)+"/"+str(day)+"/"+str(hour)+"/"+str(minute), "%Y/%m/%d/%H/%M")))), "caption":description, "Permissions":"8", "hscreenshot" :hscreenshot, } if ssInfo.IsSpoiler==False: del screenshotCfg[ScreenshotsKey][curKey][str(targetIndex)]["spoiler"] with open(syncFolder+"\\..\\screenshots.vdf","w+",encoding="utf8") as f: f.write(vdf.dumps(screenshotCfg)) cur_ss+=1 threadlock.release()
def save(self) -> None: """Save the file""" conf = vdf.dumps(self.config_data, pretty=True) ensure_directory_for_file(self.path) with open(self.path, 'w') as file: file.write(conf)