From 68e35c17ee8be50e106bf927d7b570593dcde4ea Mon Sep 17 00:00:00 2001 From: alex47exe <17827464+alex47exe@users.noreply.github.com> Date: Mon, 21 Oct 2024 02:42:54 +0100 Subject: [PATCH] generate_emu_config, fix -rel argument for better customization, it is now split in two arguments: - rel_out: generate complete game config in _OUTPUT/appid folder, relative to the bat, sh or app calling generate_emu_config app - rel_raw: generate complete game config in the same folder that contains the bat, sh or app calling generate_emu_config app --- .../tools/au3/scripts/generate_interfaces.a3x | Bin 49430 -> 49430 bytes .../tools/au3/scripts/generate_interfaces.au3 | 4 +- .../external_components/ach_watcher_gen.py | 6 +- .../external_components/app_details.py | 4 +- .../external_components/cdx_gen.py | 4 +- .../external_components/rne_gen.py | 2 +- .../external_components/scx_gen.py | 27 +-- .../generate_emu_config.py | 179 +++++++++++------- .../achievements_gen.py | 4 +- 9 files changed, 140 insertions(+), 90 deletions(-) diff --git a/tools/generate_emu_config/_DEFAULT/0/steam_misc/tools/au3/scripts/generate_interfaces.a3x b/tools/generate_emu_config/_DEFAULT/0/steam_misc/tools/au3/scripts/generate_interfaces.a3x index d54c5e676b4a0075a43f0e1d0eee8e1a125e53c5..8b9f2d2aa0e8f47baaa0bcdec1ea7d6cd3f3e360 100644 GIT binary patch delta 2912 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 KGd#0}zmWmg+n-ke delta 2912 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 KGrY5fzmWkq0hOl! diff --git a/tools/generate_emu_config/_DEFAULT/0/steam_misc/tools/au3/scripts/generate_interfaces.au3 b/tools/generate_emu_config/_DEFAULT/0/steam_misc/tools/au3/scripts/generate_interfaces.au3 index 75c74534..3c954d49 100644 --- a/tools/generate_emu_config/_DEFAULT/0/steam_misc/tools/au3/scripts/generate_interfaces.au3 +++ b/tools/generate_emu_config/_DEFAULT/0/steam_misc/tools/au3/scripts/generate_interfaces.au3 @@ -107,10 +107,10 @@ FileClose($hFile) $interfaces_ini = @ScriptDir & '\steam_settings\steam_interfaces.ini' -$codex_ini = @ScriptDir & '\steam_misc\extra_cdx\CODEX\steam_emu.ini' +$codex_ini = @ScriptDir & '\steam_misc\extra_crk\CODEX\steam_emu.ini' _AddInterfaces($codex_ini) -$rune_ini = @ScriptDir & '\steam_misc\extra_cdx\RUNE\steam_emu.ini' +$rune_ini = @ScriptDir & '\steam_misc\extra_crk\RUNE\steam_emu.ini' _AddInterfaces($rune_ini) FileDelete($interfaces_ini) diff --git a/tools/generate_emu_config/external_components/ach_watcher_gen.py b/tools/generate_emu_config/external_components/ach_watcher_gen.py index b376b07d..efaf46cd 100644 --- a/tools/generate_emu_config/external_components/ach_watcher_gen.py +++ b/tools/generate_emu_config/external_components/ach_watcher_gen.py @@ -12,7 +12,7 @@ def __ClosestDictKey(targetKey : str, srcDict : dict[str, object] | set[str]) -> return None def __generate_ach_watcher_schema(lang: str, app_id: int, achs: list[dict]) -> list[dict]: - print(f"[ ] __ writing {lang} {app_id}.db to '.\\schema\\{lang}' folder") + print(f"[ ] __ writing {lang} {app_id}.db to \\{lang} folder") out_achs_list = [] for idx in range(len(achs)): ach = copy.deepcopy(achs[idx]) @@ -114,7 +114,7 @@ def generate_all_ach_watcher_schemas( #print("[X] Couldn't generate Achievement Watcher schemas, no achievements found") # move notification to main script return else: - print(f"[ ] Generating Achievement Watcher schemas...") + print(f"[ ] Generating & packing Achievement Watcher schemas...") #if app_exe: # print(f"[ ] __ Detected app exe: '{app_exe}'") # move notification to main script #else: @@ -163,7 +163,7 @@ def generate_all_ach_watcher_schemas( print(f"[ ] __ Assuming english is the only supported language") langs = ["english"] - print(f"[ ] __ schema = OUTPUT\\{appid}\\steam_misc\\achievement_watcher\\steam_cache\\schema") + print(f"[ ] __ = \\steam_misc\\extra_acw\\steam_cache\\schema") for lang in langs: out_schema_folder = os.path.join(ach_watcher_out_dir, lang) diff --git a/tools/generate_emu_config/external_components/app_details.py b/tools/generate_emu_config/external_components/app_details.py index 1367e3ee..fc55dae4 100644 --- a/tools/generate_emu_config/external_components/app_details.py +++ b/tools/generate_emu_config/external_components/app_details.py @@ -340,14 +340,14 @@ def download_app_details( time.sleep(0.1) if not app_details: - print(f"[?] No app details found - skip creating 'app_details.json'") + print(f"[?] No app details found - skip creating \\steam_misc\\app_info\\app_details.json") #if last_exception: # skip showing last_exception # print(f"[X] __ last error: {last_exception}") return with open(details_out_file, "wt", encoding='utf-8') as f: json.dump(app_details, f, ensure_ascii=False, indent=2) - print(f"[ ] Found app details --- writing to 'app_details.json'") # move it here to avoid showing both 'downloading' and 'cannot download' + print(f"[ ] Found app details --- writing to \\steam_misc\\app_info\\app_details.json") # move it here to avoid showing both 'downloading' and 'cannot download' if download_screenshots: __download_screenshots(base_out_dir, appid, app_details) diff --git a/tools/generate_emu_config/external_components/cdx_gen.py b/tools/generate_emu_config/external_components/cdx_gen.py index 9dadcff1..d65a47fc 100644 --- a/tools/generate_emu_config/external_components/cdx_gen.py +++ b/tools/generate_emu_config/external_components/cdx_gen.py @@ -17,7 +17,7 @@ __codex_ini = r'''### ### ßßßÛÛ²ÜÜÜÜÜÛ²ÛÛÛ²ßß ### ### -### Game data is stored at %SystemDrive%\\Users\\Public\\Documents\\Steam\\CODEX\\{cdx_id} +### Game data is stored at %SystemDrive%\Users\Public\Documents\Steam\CODEX\{cdx_id} ### [Settings] @@ -141,7 +141,7 @@ def generate_cdx_ini( os.makedirs(os.path.join(base_out_dir, "steam_misc\\extra_crk\\CODEX")) codex_ini_path = os.path.join(base_out_dir, "steam_misc\\extra_crk\\CODEX\\steam_emu.ini") - print(f"[ ] Generating CODEX config --- writing 'steam_emu.ini'") + print(f"[ ] Generating CODEX config --- writing \\steam_misc\\extra_crk\\CODEX\\steam_emu.ini") print(f"[ ] __ if to be used, make sure it has the correct interface versions") dlc_list = [f"{d[0]}={d[1]}" for d in dlc] diff --git a/tools/generate_emu_config/external_components/rne_gen.py b/tools/generate_emu_config/external_components/rne_gen.py index 7bae7f15..b1cf5079 100644 --- a/tools/generate_emu_config/external_components/rne_gen.py +++ b/tools/generate_emu_config/external_components/rne_gen.py @@ -132,7 +132,7 @@ def generate_rne_ini( os.makedirs(os.path.join(base_out_dir, "steam_misc\\extra_crk\\RUNE")) rune_ini_path = os.path.join(base_out_dir, "steam_misc\\extra_crk\\RUNE\\steam_emu.ini") - print(f"[ ] Generating RUNE config --- writing 'steam_emu.ini'") + print(f"[ ] Generating RUNE config --- writing \\steam_misc\\extra_crk\\RUNE\\steam_emu.ini") print(f"[ ] __ if to be used, make sure it has the correct interface versions") dlc_list = [f"{d[0]}={d[1]}" for d in dlc] diff --git a/tools/generate_emu_config/external_components/scx_gen.py b/tools/generate_emu_config/external_components/scx_gen.py index 32e127a8..62d85420 100644 --- a/tools/generate_emu_config/external_components/scx_gen.py +++ b/tools/generate_emu_config/external_components/scx_gen.py @@ -95,10 +95,13 @@ def download_scx(base_out_dir : str, appid : int): _game_found=True + print(f"[ ] Searching SCX content...") + for line in app_scx_line: if 'Game not found' in line: shutil.rmtree(os.path.join(base_out_dir, 'steam_misc\\app_scx')) _game_found=False + print(f"[?] __ no SCX content found --- nothing downloaded to \\steam_misc\\app_scx folder") break if ('class="tracking-wider font-league-gothic"' in line) and ('Animation<' in line: @@ -753,7 +756,7 @@ def download_scx(base_out_dir : str, appid : int): if _animated_avatars != line_series_name_safe: if _animated_avatars_series == line_series_count: # this fixes duplicating message for last found 'section' in html source, after finding a new 'series' - print(f"[ ] __ {line_series_name_safe} --- downloading animated avatars...") + print(f"[ ] __ found '{line_series_name_safe}' animated avatars...") _animated_avatars = line_series_name_safe if '>Animation<' in line: @@ -809,7 +812,7 @@ def download_scx(base_out_dir : str, appid : int): if _profiles != line_series_name_safe: if _profiles_series == line_series_count: # this fixes duplicating message for last found 'section' in html source, after finding a new 'series' - print(f"[ ] __ {line_series_name_safe} --- downloading profiles...") + print(f"[ ] __ found '{line_series_name_safe}' profiles...") _profiles = line_series_name_safe if 'class="sm:h-[166px] md:h-[146px] lg:h-[126px] xl:h-[138px] 2xl:h-[148px]"' in line: diff --git a/tools/generate_emu_config/generate_emu_config.py b/tools/generate_emu_config/generate_emu_config.py index 834fde73..f8c62d37 100644 --- a/tools/generate_emu_config/generate_emu_config.py +++ b/tools/generate_emu_config/generate_emu_config.py @@ -301,7 +301,7 @@ def get_stats_schema(client, game_id, owner_id): return client.wait_msg(EMsg.ClientGetUserStatsResponse, timeout=5) def download_achievement_images(game_id : int, image_names : set[str], output_folder : str): - print(f"[ ] Found {len(image_names)} achievements images --- downloading to '.\\steam_settings\\img' folder") + print(f"[ ] Found {len(image_names)} achievements images --- downloading to \\steam_settings\\img folder") q : queue.Queue[str] = queue.Queue() @@ -356,7 +356,7 @@ def generate_achievement_stats(client, game_id : int, output_directory, backup_d break if stats_schema_found is None: # no achievement found - print(f"[?] No achievements found - skip creating 'achievements.json'") + print(f"[?] No achievements found - skip creating \\steam_settings\\achievements.json") return [] achievement_images_dir = os.path.join(output_directory, "img") @@ -371,9 +371,9 @@ def generate_achievement_stats(client, game_id : int, output_directory, backup_d ) = achievements_gen.generate_stats_achievements(stats_schema_found.body.schema, output_directory) if len(achievements) != 1: - print(f"[ ] Found {len(achievements)} achievements --- writing to 'achievements.json'") + print(f"[ ] Found {len(achievements)} achievements --- writing to \\steam_settings\\achievements.json") else: - print(f"[ ] Found {len(achievements)} achievement --- writing to 'achievements.json'") + print(f"[ ] Found {len(achievements)} achievement --- writing to \\steam_settings\\achievements.json") #print(f"[ ] Writing 'UserGameStatsSchema_{game_id}.bin'") @@ -592,16 +592,19 @@ def help(): print(" -rne: generate .ini file for RUNE Steam emu for each app") print(" -acw: generate schemas of all possible languages for Achievement Watcher") print(" -skip_ach: skip downloading & generating achievements and their images") - print(" -skip_con: skip downloading & generating controller configuration files") - print(" -skip_inv: skip downloading & generating inventory data (items.json & default_items.json)") - print(" -clr: delete any folder/file with the same name as the output before generating any data") - print(" -rel: generate temp files/folders, and expect input files, relative to the current working directory") + print(" -skip_con: skip downloading & generating controller configuration files (action sets txt files)") + print(" -skip_inv: skip downloading & generating inventory data ('items.json' & 'default_items.json')") print(" -anon: login as an anonymous account, these have very limited access and cannot get all app details") - print(" -name: save the output of each app in a folder with the same name as the app, unsafe characters are discarded") - print("\nAll switches are optional except app id, at least 1 app id must be provided") + print(" -name: save the complete game config in a folder with the same name as the app (unsafe characters are discarded)") + print(" -rel_out: generate complete game config in _OUTPUT/appid folder, relative to the bat, sh or app calling generate_emu_config app") + print(" -rel_raw: generate complete game config in the same folder that contains the bat, sh or app calling generate_emu_config app") + print(" -clr: clear output folder before generating the complete game config") + print(" do note that it will not work when '-rel_raw' argument is used too") + print("\nAll switches are optional except appid, at least 1 appid must be provided") print("\nAutomate the login prompt:") - print(" * You can create a file called 'my_login.txt' beside the script, then add your username on the first line") - print(" and your password on the second line.") + print(" * You can create a file called 'my_login.txt' beside the script, then add your:") + print(" USERNAME on the first line") + print(" PASSWORD on the second line") print(" * You can set these 2 environment variables (will override 'my_login.txt'):") print(" GSE_CFG_USERNAME") print(" GSE_CFG_PASSWORD") @@ -664,8 +667,12 @@ def main(): CLEANUP_BEFORE_GENERATING = True elif f'{appid}'.lower() == '-anon': ANON_LOGIN = True - elif f'{appid}'.lower() == '-rel': + elif f'{appid}'.lower() == '-rel_out': RELATIVE_DIR = True + RELATIVE_set = 'out' + elif f'{appid}'.lower() == '-rel_raw': + RELATIVE_DIR = True + RELATIVE_set = 'raw' elif f'{appid}'.lower() == '-skip_ach': SKIP_ACH = True elif f'{appid}'.lower() == '-skip_con': @@ -698,13 +705,13 @@ def main(): sys.exit(1) client = SteamClient() - # login_tmp_folder = os.path.join(get_exe_dir(RELATIVE_DIR), "login_temp") + # login_tmp_folder = os.path.join(get_exe_dir(False), "login_temp") # replaced 'RELATIVE_DIR with 'False' to always look for or create login_temp in generate_emu_config folder # if not os.path.exists(login_tmp_folder): # os.makedirs(login_tmp_folder) # client.set_credential_location(login_tmp_folder) # first read the 'my_login.txt' file - my_login_file = os.path.join(get_exe_dir(RELATIVE_DIR), "my_login.txt") + my_login_file = os.path.join(get_exe_dir(False), "my_login.txt") # replaced 'RELATIVE_DIR with 'False' to always look for or create my_login.txt in generate_emu_config folder if not ANON_LOGIN and os.path.isfile(my_login_file): filedata = [''] with open(my_login_file, "r", encoding="utf-8") as f: @@ -743,7 +750,7 @@ def main(): top_own.top_owners() # read and prepend top_owners_ids.txt - top_owners_file = os.path.join(get_exe_dir(RELATIVE_DIR), "top_owners_ids.txt") + top_owners_file = os.path.join(get_exe_dir(False), "top_owners_ids.txt") # replaced 'RELATIVE_DIR with 'False' to always look for or create top_owners_ids.txt in generate_emu_config folder if os.path.isfile(top_owners_file): filedata = [''] with open(top_owners_file, "r", encoding="utf-8") as f: @@ -757,6 +764,17 @@ def main(): if not ANON_LOGIN: TOP_OWNER_IDS.insert(0, client.steam_id.as_64) + user_name = '' + user_repl = '' + if platform.system() == "Windows": # Windows + user_name = os.getenv("USERNAME") + user_repl = r'%username%' + elif platform.system() == "Linux": # Linux + user_name = os.getenv("USER") + user_repl = r'$user' + + username = os.getenv("USERNAME") or os.getenv("USER") + for appid in appids: print(" ") @@ -772,10 +790,8 @@ def main(): print(f"*** ABORTED config for app id {appid} ***") print(" ") break - game_info : dict = raw["apps"][appid] - print(f"[ ] Found product info --- writing to 'app_product_info.json'") - + game_info : dict = raw["apps"][appid] game_info_common : dict = game_info.get("common", {}) app_name = game_info_common.get("name", "") app_name_on_disk = f"{appid}" @@ -791,33 +807,64 @@ def main(): app_name = f"Unknown_Steam_app_{appid}" # we need this for later use in the Achievement Watcher print(f"[X] Cannot find app name on Steam store") - #root_backup_dir = os.path.join(get_exe_dir(RELATIVE_DIR), "BACKUP") + #root_backup_dir = os.path.join(get_exe_dir(False), "_BACKUP") # replaced 'RELATIVE_DIR with 'False' to always look for or create _BACKUP in generate_emu_config folder #backup_dir = os.path.join(root_backup_dir, f"{appid}") #if not os.path.exists(backup_dir): # os.makedirs(backup_dir) - root_def_dir = "_DEFAULT" - root_out_dir = "_OUTPUT" - base_out_dir = os.path.join(root_out_dir, app_name_on_disk) + root_def_dir_RELATIVE = os.path.join(get_exe_dir(True), "_DEFAULT") # _DEFAULT folder relative to external bat, sh or app calling generate_emu_config exe; with get_exe_dir(False) is only relative to generate_emu_config exe + root_out_dir_RELATIVE = os.path.join(get_exe_dir(True), "_OUTPUT") # _OUTPUT folder relative to external bat, sh or app calling generate_emu_config exe; with get_exe_dir(False) is only relative to generate_emu_config exe + + if RELATIVE_DIR: + if RELATIVE_set == 'out': + root_out_dir = os.path.join(get_exe_dir(True), "_OUTPUT") + base_out_dir = os.path.join(root_out_dir, app_name_on_disk) + CLEANUP_override = 0 + elif RELATIVE_set == 'raw': + root_out_dir = os.path.join(get_exe_dir(True)) + base_out_dir = os.path.join(get_exe_dir(True)) + CLEANUP_override = 1 + + if os.path.exists(root_def_dir_RELATIVE) and os.path.isdir(root_def_dir_RELATIVE): + if os.listdir(root_def_dir_RELATIVE): + root_def_dir = os.path.join(get_exe_dir(True), "_DEFAULT") + else: + root_def_dir = os.path.join(get_exe_dir(False), "_DEFAULT") + else: + root_def_dir = os.path.join(get_exe_dir(False), "_DEFAULT") + else: + root_out_dir = os.path.join(get_exe_dir(False), "_OUTPUT") + base_out_dir = os.path.join(root_out_dir, app_name_on_disk) + CLEANUP_override = 0 + + root_def_dir = os.path.join(get_exe_dir(False), "_DEFAULT") + emu_settings_dir = os.path.join(base_out_dir, "steam_settings") info_out_dir = os.path.join(base_out_dir, "steam_misc\\app_info") + print(f"[ ] DEF_DIR = {root_def_dir.replace(user_name, user_repl, 1)}") + if RELATIVE_DIR and (RELATIVE_set == 'raw'): + print(f"[ ] OUT_DIR = {os.getcwd().replace(user_name, user_repl, 1)}") + else: + print(f"[ ] OUT_DIR = {base_out_dir.replace(user_name, user_repl, 1)}") + if CLEANUP_BEFORE_GENERATING: - print(f"[ ] Cleaning '{base_out_dir}' folder...") - base_dir_path = pathlib.Path(base_out_dir) - if base_dir_path.is_file(): - base_dir_path.unlink() - time.sleep(0.05) - elif base_dir_path.is_dir(): - shutil.rmtree(base_dir_path) - time.sleep(0.05) - - while base_dir_path.exists(): - time.sleep(0.05) + if CLEANUP_override == 0: + print(f"[ ] Cleaning folder...") + base_dir_path = pathlib.Path(base_out_dir) + if base_dir_path.is_file(): + base_dir_path.unlink() + time.sleep(0.05) + elif base_dir_path.is_dir(): + shutil.rmtree(base_dir_path) + time.sleep(0.05) + while base_dir_path.exists(): + time.sleep(0.05) root_backup_dir = os.path.join(base_out_dir, "steam_misc\\app_backup") #backup_dir = os.path.join(root_backup_dir, f"{appid}") backup_dir = root_backup_dir #use different structure for 'backup' dir + if not os.path.exists(backup_dir): os.makedirs(backup_dir) @@ -830,27 +877,28 @@ def main(): #with open(os.path.join(info_out_dir, "app_widget.url"), mode='w', newline='\r\n') as f: #f.write(f"[InternetShortcut]\nURL=https://store.steampowered.com/widget/{appid}/") - if DEFAULT_PRESET == True: - print(f"[ ] Copying preset emu configs to '{base_out_dir}' folder") - shutil.copytree(os.path.join(root_def_dir, str(0)), base_out_dir, dirs_exist_ok=True) # copy from default emu dir - print(f"[ ] __ default emu config from '{os.path.join(root_def_dir, str(0))}' folder") - shutil.copytree(os.path.join(root_def_dir, str(DEFAULT_PRESET_NO)), base_out_dir, dirs_exist_ok=True) # copy from preset emu dir - print(f"[ ] __ preset emu config from '{os.path.join(root_def_dir, str(DEFAULT_PRESET_NO))}' folder") - if os.path.exists(os.path.join(root_def_dir, str(appid))): - shutil.copytree(os.path.join(root_def_dir, str(appid)), base_out_dir, dirs_exist_ok=True) # copy from preset app dir - print(f"[ ] __ app emu config from '{os.path.join(root_def_dir, str(appid))}' folder") + print(f"[ ] Copying preset emu configs...") + shutil.copytree(os.path.join(root_def_dir, str(0)), base_out_dir, dirs_exist_ok=True) # copy from default emu dir + print(f"[ ] __ default emu config from \\{str(0)} folder") + shutil.copytree(os.path.join(root_def_dir, str(DEFAULT_PRESET_NO)), base_out_dir, dirs_exist_ok=True) # copy from preset emu dir + print(f"[ ] __ preset emu config from \\{str(DEFAULT_PRESET_NO)} folder") + if os.path.exists(os.path.join(root_def_dir, str(appid))): + shutil.copytree(os.path.join(root_def_dir, str(appid)), base_out_dir, dirs_exist_ok=True) # copy from preset app dir + print(f"[ ] __ app emu config from \\{str(appid)} folder") with open(os.path.join(emu_settings_dir, "steam_appid.txt"), 'w') as f: f.write(str(appid)) #print(f"[ ] Writing 'steam_appid.txt'") + print(f"[ ] Found product info --- writing to \\steam_misc\\app_info\\app_product_info.json") + with open(os.path.join(info_out_dir, "app_product_info.json"), "wt", encoding='utf-8') as f: json.dump(game_info, f, ensure_ascii=False, indent=2) #print(f"[ ] Writing 'app_product_info.json'") with open(os.path.join(backup_dir, "product_info.json"), "wt", encoding='utf-8') as f: json.dump(game_info, f, ensure_ascii=False, indent=2) - #print(f"[ ] Writing 'app_product_info.json'") + #print(f"[ ] Writing 'product_info.json'") app_details.download_app_details( base_out_dir, info_out_dir, @@ -897,11 +945,11 @@ def main(): f.write(f'{lang}\n') #print(f"[ ] Writing 'supported_languages.txt'") if len(languages) == 1: - print(f"[ ] Found {len(languages)} supported language --- writing to 'supported_languages.txt'") + print(f"[ ] Found {len(languages)} supported language --- writing to \\steam_settings\\supported_languages.txt") else: - print(f"[ ] Found {len(languages)} supported languages --- writing to 'supported_languages.txt'") + print(f"[ ] Found {len(languages)} supported languages --- writing to \\steam_settings\\supported_languages.txt") else: - print(f"[?] No supported languages found - skip creating 'supported_languages.txt'") + print(f"[?] No supported languages found - skip creating \\steam_settings\\supported_languages.txt") ReplaceStringInFile(os.path.join(emu_settings_dir, "configs.app.ini"), 'This is another example DLC name', '# 56789=', '56789=') # make sure we write DLCs after '# 56789=This is another example DLC name' @@ -935,11 +983,11 @@ def main(): dlc_config_list.append((dlc, dlc_name)) if len(dlc_list) == 1: - print(f"[ ] Found {len(dlc_config_list)} DLC --- writing to 'configs.app.ini'") + print(f"[ ] Found {len(dlc_config_list)} DLC --- writing to \\steam_settings\\configs.app.ini") else: - print(f"[ ] Found {len(dlc_config_list)} DLCs --- writing to 'configs.app.ini'") + print(f"[ ] Found {len(dlc_config_list)} DLCs --- writing to \\steam_settings\\configs.app.ini") else: - print(f"[?] No DLCs found - skip writing to 'configs.app.ini'") + print(f"[?] No DLCs found - skip writing to \\steam_settings\\configs.app.ini") if not dlc_raw == {}: with open(os.path.join(info_out_dir, "dlc_product_info.json"), "wt", encoding='utf-8') as f: @@ -1021,25 +1069,25 @@ def main(): f.write(f"{game_depot}\n") #print(f"[ ] Writing 'depots.txt'") if len(all_depots) == 1: - print(f"[ ] Found {len(all_depots)} depot --- writing to 'depots.txt'") + print(f"[ ] Found {len(all_depots)} depot --- writing to \\steam_settings\\depots.txt") else: - print(f"[ ] Found {len(all_depots)} depots --- writing to 'depots.txt'") + print(f"[ ] Found {len(all_depots)} depots --- writing to \\steam_settings\\depots.txt") else: - print(f"[?] No depots found - skip creating 'depots.txt'") + print(f"[?] No depots found - skip creating \\steam_settings\\depots.txt") if len(all_branches) >= 1: with open(os.path.join(emu_settings_dir, "branches.json"), "wt", encoding='utf-8') as f: json.dump(all_branches, f, ensure_ascii=False, indent=2) if len(all_branches) == 1: - print(f"[ ] Found {len(all_branches)} branch --- writing to 'branches.json'") + print(f"[ ] Found {len(all_branches)} branch --- writing to \\steam_settings\\branches.json") else: - print(f"[ ] Found {len(all_branches)} branches --- writing to 'branches.json'") + print(f"[ ] Found {len(all_branches)} branches --- writing to \\steam_settings\\branches.json") if "public" in game_info["depots"]["branches"]: if "buildid" in game_info["depots"]["branches"]["public"]: buildid = game_info["depots"]["branches"]["public"]["buildid"] print(f"[ ] __ default branch name: public, latest build id: {buildid}") else: - print(f"[?] No branches found - skip creating 'branches.json'") + print(f"[?] No branches found - skip creating \\steam_settings\\branches.json") # read some keys from 'configs.user.ini' cfg_user = ConfigObj(os.path.join(emu_settings_dir, "configs.user.ini"), encoding='utf-8') @@ -1047,11 +1095,10 @@ def main(): cfg_user_account_steamid = cfg_user["user::general"]["account_steamid"] cfg_user_language = cfg_user["user::general"]["language"] - config_found = 0 # needed to skip showing "[ ] Found controller configs --- generating action sets..." again if already shown once + config_found = 0 # needed to show 'No controller configs found...' if value remains 0 config_generated = 0 # used to avoid overwriting supported config by unsupported one - config_generated_not_sup = 0 # used to avoid overwriting prefered unsupported config if no supported config present downloading_ctrl_vdf = 0 # needed to remove possible duplicate 'Found controller configs...' - valid_id = 0 # needed to skip showing "[ ] Found controller configs --- generating action sets..." if no valid is found in either "steamcontrollerconfigdetails" or "steamcontrollertouchconfigdetails" + valid_id = 0 # needed to skip showing "Found controller configs..." if no valid is found in either "steamcontrollerconfigdetails" or "steamcontrollertouchconfigdetails" if "config" in game_info: if not SKIP_CONTROLLER and "steamcontrollerconfigdetails" in game_info["config"]: controller_details = game_info["config"]["steamcontrollerconfigdetails"] @@ -1107,8 +1154,8 @@ def main(): elif (controller_type in ["controller_ps4", "controller_ps5", "controller_steamcontroller_gordon", "controller_neptune", "controller_switch_pro"] and (("default" in enabled_branches) or ("public" in enabled_branches))): config_found=1 - #print(f"[X] __ controller type '{controller_type}' is not supported ... converting .vdf to action sets") - print(f"[X] __ parsing '{controller_type}' vdf - not supported, backup purposes only") + #print(f"[ ] __ controller type '{controller_type}' is not supported ... converting .vdf to action sets") + print(f"[ ] __ parsing '{controller_type}' vdf - not supported, backup purposes only") parse_controller_vdf.generate_controller_config(out_vdf.decode('utf-8'), os.path.join(os.path.join(backup_dir, 'controller\\' + f'{controller_type}' + '_' + f'{id}'), "action_set")) if not SKIP_CONTROLLER and "steamcontrollertouchconfigdetails" in game_info["config"]: @@ -1138,8 +1185,8 @@ def main(): if out_vdf is not None: if (controller_type in ["controller_mobile_touch"] and (("default" in enabled_branches) or ("public" in enabled_branches))): config_found = 1 - #print(f"[X] __ controller type '{controller_type}' is not supported ... converting .vdf to action sets") - print(f"[X] __ parsing '{controller_type}' vdf - not supported, backup purposes only") + #print(f"[ ] __ controller type '{controller_type}' is not supported ... converting .vdf to action sets") + print(f"[ ] __ parsing '{controller_type}' vdf - not supported, backup purposes only") parse_controller_vdf.generate_controller_config(out_vdf.decode('utf-8'), os.path.join(os.path.join(backup_dir, 'controller\\' + f'{controller_type}' + '_' + f'{id}'), "action_set")) ''' # NOTE zip the parent 'app_backup' folder instead of only the child 'controller' folder @@ -1245,9 +1292,9 @@ def main(): raw_inventory = json.dumps(inventory, indent=4) if len(inventory) != 1: - print(f"[ ] Found {len(inventory)} inventory items --- writing to 'items.json' and 'default_items.json'") + print(f"[ ] Found {len(inventory)} inventory items --- writing to \\steam_settings\\items.json & default_items.json") else: - print(f"[ ] Found {len(inventory)} inventory item --- writing to 'items.json' and 'default_items.json'") + print(f"[ ] Found {len(inventory)} inventory item --- writing to \\steam_settings\\items.json & default_items.json") with open(os.path.join(backup_dir, f"InventoryItems_{appid}.json"), "w") as f: f.write(raw_inventory) @@ -1275,7 +1322,7 @@ def main(): #print(f"[ ] __ writing 'default_items.json'") else: - print(f"[?] No inventory items found - skip creating 'items.json' and 'default_items.json'") + print(f"[?] No inventory items found - skip creating \\steam_settings\\items.json & default_items.json") if app_exe: if app_mode_new != "": diff --git a/tools/generate_emu_config/stats_schema_achievement_gen/achievements_gen.py b/tools/generate_emu_config/stats_schema_achievement_gen/achievements_gen.py index 16e7f855..b55713d2 100644 --- a/tools/generate_emu_config/stats_schema_achievement_gen/achievements_gen.py +++ b/tools/generate_emu_config/stats_schema_achievement_gen/achievements_gen.py @@ -122,9 +122,9 @@ def generate_stats_achievements( if output_stats: with open(os.path.join(config_directory, "stats.txt"), 'wt', encoding='utf-8') as f: f.writelines(output_stats) - print(f"[ ] Found {len(output_stats)} stats --- writing to 'stats.txt'") + print(f"[ ] Found {len(output_stats)} stats --- writing to \\steam_settings\\stats.txt'") else: - print(f"[?] No stats found - skip creating 'stats.txt'") + print(f"[?] No stats found - skip creating \\steam_settings\\stats.txt") return (achievements_out, stats_out, copy_default_unlocked_img, copy_default_locked_img)