From 83cff10205c71351e2397ba901d4fcd077763ca0 Mon Sep 17 00:00:00 2001 From: Alexander <43010352+amarzot@users.noreply.github.com> Date: Wed, 10 Jan 2024 17:01:15 -0500 Subject: [PATCH 01/17] Update all paths to use pathlib Path objects --- openGOALModLauncher.py | 65 ++++++++---------- utils/launcherUtils.py | 151 ++++++++++++++++++++--------------------- 2 files changed, 102 insertions(+), 114 deletions(-) diff --git a/openGOALModLauncher.py b/openGOALModLauncher.py index dbb4861..99a20a3 100644 --- a/openGOALModLauncher.py +++ b/openGOALModLauncher.py @@ -31,6 +31,12 @@ from pathlib import Path import random + +dirs = AppDirs(roaming=True) + +# C:\Users\USERNAME\AppData\Roaming\OpenGOAL-Mods\ +ModFolderPATH = Path(dirs.user_data_dir) / "OpenGOAL-Mods" + sg.theme("DarkBlue3") @@ -45,11 +51,11 @@ def exitWithError(): # Folder where script is placed, It looks in this for the Exectuable if getattr(sys, "frozen", False): # If we are a pyinstaller exe get the path of this file, not python - LauncherDir = os.path.dirname(os.path.realpath(sys.executable)) + LauncherDir = Path(sys.executable).resolve().parent # Detect if a user has downloaded a release directly, if so point them to the autoupdater if ( - LauncherDir != os.getenv("APPDATA") + "\\OpenGOAL-UnofficalModLauncher" + LauncherDir != Path(dirs.user_data_dir) / "OpenGOAL-UnofficalModLauncher" and os.getlogin() != "NinjaPC" and os.environ["COMPUTERNAME"] != "DESKTOP-BBN1CMN" ): @@ -80,15 +86,10 @@ def exitWithError(): # if we are running the .py directly use this path LauncherDir = os.path.dirname(__file__) -installpath = str(LauncherDir + "\\resources\\") +installpath = Path(LauncherDir) / "resources" # intialize default variables so they are never null -dirs = AppDirs(roaming=True) - -# C:\Users\USERNAME\AppData\Roaming\OpenGOAL-Mods\ -ModFolderPATH = os.path.join(dirs.user_data_dir, "OpenGOAL-Mods", "") - # grab images from web # url to splash screen image @@ -131,8 +132,8 @@ def fetch_image(url): # Accept the URL parameter loadingimage = getPNGFromURL("https://raw.githubusercontent.com/OpenGOAL-Unofficial-Mods/OpenGoal-ModLauncher-dev/main/resources/modlauncher-loading-0.png") # make the modfolderpath if first install -if not os.path.exists(ModFolderPATH): - os.makedirs(ModFolderPATH) +if not ModFolderPATH.exists(): + ModFolderPATH.mkdir() table_headings = [ "id", @@ -210,8 +211,9 @@ def fetch_image(url): # Accept the URL parameter def getRefreshedTableData(sort_col_idx): # Load data from the local file if it exists - local_file_path = "resources/jak1_mods.json" - if os.path.exists(local_file_path): + local_file_path = Path("resources") / "jak1_mods.json" + if local_file_path.exists(): + # TODO: don't leak fd local_mods = json.loads(open(local_file_path, "r").read()) # Load data from the remote URL @@ -221,7 +223,7 @@ def getRefreshedTableData(sort_col_idx): # Initialize an empty dictionary to store the combined data mod_dict = {} - if os.path.exists(local_file_path): + if local_file_path.exists(): # Merge the remote and local data while removing duplicates mod_dict = {**remote_mods, **local_mods} else: @@ -243,30 +245,25 @@ def getRefreshedTableData(sort_col_idx): mod["install_date"] = "Not Installed" mod["access_date"] = "Not Installed" + mod_path = ModFolderPATH/mod_id + mod_gk_path = mod_path/"gk.exe" # determine local install/access datetime if mod_id in installed_mod_subfolders: mod[ "install_date" ] = f"{datetime.fromtimestamp(installed_mod_subfolders[mod_id]):%Y-%m-%d %H:%M}" - - if exists(f"{ModFolderPATH}/{mod_id}/gk.exe"): - gk_stat = os.stat(f"{ModFolderPATH}/{mod_id}/gk.exe") - mod[ - "access_date" - ] = f"{datetime.fromtimestamp(gk_stat.st_atime):%Y-%m-%d %H:%M}" elif mod_name in installed_mod_subfolders: # previous installation using mod_name (will migrate after this step) mod[ "install_date" ] = f"{datetime.fromtimestamp(installed_mod_subfolders[mod_name]):%Y-%m-%d %H:%M}" # migrate folder to use mod_id instead of mod_name - shutil.move(ModFolderPATH + "/" + mod_name, ModFolderPATH + "/" + mod_id) + (ModFolderPATH/mod_name).rename(mod_path) - if exists(f"{ModFolderPATH}/{mod_id}/gk.exe"): - gk_stat = os.stat(f"{ModFolderPATH}/{mod_id}/gk.exe") - mod[ - "access_date" - ] = f"{datetime.fromtimestamp(gk_stat.st_atime):%Y-%m-%d %H:%M}" + if mod_gk_path.exists(): + mod[ + "access_date" + ] = f"{datetime.fromtimestamp(mod_gk_path.stat().st_atime):%Y-%m-%d %H:%M}" mod["contributors"] = ", ".join(mod["contributors"]) mod["tags"].sort() @@ -846,7 +843,7 @@ def loading_screen_with_thread(thread): subfolders = [f.name for f in os.scandir(ModFolderPATH) if f.is_dir()] if tmpModSelected in subfolders: - dir = dirs.user_data_dir + "\\OpenGOAL-Mods\\" + tmpModSelected + dir = ModFolderPATH/tmpModSelected launcherUtils.openFolder(dir) else: sg.Popup("Selected mod is not installed", keep_on_top=True, icon=iconfile) @@ -858,10 +855,9 @@ def loading_screen_with_thread(thread): [linkType, tmpModURL] = githubUtils.identifyLinkType(tmpModURL) subfolders = [f.name for f in os.scandir(ModFolderPATH) if f.is_dir()] if tmpModSelected in subfolders: - dir = dirs.user_data_dir + "\\OpenGOAL-Mods\\" + tmpModSelected + dir = ModFolderPATH/tmpModSelected ans = sg.popup_ok_cancel( - "Confirm: re-extracting " - + dir, + f"Confirm: re-extracting {dir}", icon=iconfile, ) if ans == "OK": @@ -878,10 +874,9 @@ def loading_screen_with_thread(thread): [linkType, tmpModURL] = githubUtils.identifyLinkType(tmpModURL) subfolders = [f.name for f in os.scandir(ModFolderPATH) if f.is_dir()] if tmpModSelected in subfolders: - dir = dirs.user_data_dir + "\\OpenGOAL-Mods\\" + tmpModSelected + dir = ModFolderPATH/tmpModSelected ans = sg.popup_ok_cancel( - "Confirm: recompiling " - + dir, + f"Confirm: recompiling {dir}", icon=iconfile, ) if ans == "OK": @@ -895,8 +890,8 @@ def loading_screen_with_thread(thread): subfolders = [f.name for f in os.scandir(ModFolderPATH) if f.is_dir()] if tmpModSelected in subfolders: - dir = dirs.user_data_dir + "\\OpenGOAL-Mods\\" + tmpModSelected - ans = sg.popup_ok_cancel("Confirm: uninstalling " + dir, icon=iconfile) + dir = ModFolderPATH/tmpModSelected + ans = sg.popup_ok_cancel(f"Confirm: uninstalling {dir}", icon=iconfile) if ans == "OK": launcherUtils.try_remove_dir(dir) reset() @@ -940,7 +935,7 @@ def loading_screen_with_thread(thread): LATEST_TABLE_DATA = getRefreshedTableData(None) window["-MODTABLE-"].update(values=LATEST_TABLE_DATA) elif event == "-VIEWISOFOLDER-": - dir = dirs.user_data_dir + "\\OpenGOAL-Mods\\_iso_data" + dir = ModFolderPATH/"_iso_data" launcherUtils.ensure_jak_folders_exist() launcherUtils.openFolder(dir) elif event == "-JAKMODSWEB-": diff --git a/utils/launcherUtils.py b/utils/launcherUtils.py index 2bb6954..aa8e458 100644 --- a/utils/launcherUtils.py +++ b/utils/launcherUtils.py @@ -31,12 +31,6 @@ FILE_DATE_TO_CHECK = "gk.exe" UPDATE_FILE_EXTENTION = ".zip" -# Folder where script is placed, It looks in this for the Exectuable -if getattr(sys, "frozen", False): - LauncherDir = os.path.dirname(os.path.realpath(sys.executable)) -elif __file__: - LauncherDir = os.path.dirname(__file__) - ExecutableName = str( FILE_DATE_TO_CHECK ) # Executable we're checking the 'modified' time of @@ -46,8 +40,8 @@ FileIdent = "" # If we ever get to multiple .zip files in a release, include other identifying information from the name dirs = AppDirs(roaming=True) currentOS = platform.system() -ModFolderPATH = os.path.join(dirs.user_data_dir, "OpenGOAL-Mods", "") -AppdataPATH = dirs.user_data_dir +ModFolderPATH = Path(dirs.user_data_dir)/"OpenGOAL-Mods" +AppdataPATH = Path(dirs.user_data_dir) pbar = None @@ -125,7 +119,7 @@ def try_remove_dir(dir): def local_mod_image(MOD_ID): - path = ModFolderPATH + MOD_ID + "\\ModImage.png" + path = ModFolderPATH/MOD_ID/"ModImage.png" if exists(path): return path return None @@ -178,11 +172,15 @@ def link_files_by_extension(source_dir, destination_dir): makeFileSymlink(destination_path, file_path) def openFolder(path): - if not exists(dirs.user_data_dir + "\\OpenGOAL\\" + "mods\\data\\iso_data\\jak2"): - os.makedirs(dirs.user_data_dir + "\\OpenGOAL\\" + "mods\\data\\iso_data\\jak2") - FILEBROWSER_PATH = os.path.join(os.getenv("WINDIR"), "explorer.exe") + jak2_path = Path(dirs.user_data_dir)/"OpenGOAL"/"mods"/"data"/"iso_data"/"jak2" + if not jak2_path.exists(): + jak2_path.mkdir(parents=True) print(path) - subprocess.run([FILEBROWSER_PATH, path]) + if sys.platform == "win32": + os.startfile(path) + else: + opener = "open" if sys.platform == "darwin" else "xdg-open" + subprocess.call([opener, path]) def replaceText(path, search_text, replace_text): # Check if the file exists @@ -232,7 +230,7 @@ def divide_by_zero(): 1 / 0 def ensure_jak_folders_exist(): - directory = dirs.user_data_dir + "\OpenGOAL-Mods\_iso_data" + directory = Path(dirs.user_data_dir)/"OpenGOAL-Mods"/"_iso_data" jak1_path = os.path.join(directory, "jak1") jak2_path = os.path.join(directory, "jak2") @@ -270,13 +268,13 @@ def launch_local(MOD_ID, GAME): try_kill_process("goalc.exe") time.sleep(1) - InstallDir = ModFolderPATH + MOD_ID + InstallDir = ModFolderPATH/MOD_ID if GAME == "jak2": GKCOMMANDLINElist = [ - InstallDir + "\gk.exe", + InstallDir/"gk.exe", "--proj-path", - InstallDir + "\\data", + InstallDir/"data", "-v", "--game", "jak2", @@ -286,9 +284,9 @@ def launch_local(MOD_ID, GAME): ] else: # if GAME == "jak1": GKCOMMANDLINElist = [ - os.path.abspath(InstallDir + "\gk.exe"), # Using os.path.abspath to get the absolute path. + os.path.abspath(InstallDir/"gk.exe"), # Using os.path.abspath to get the absolute path. "--proj-path", - os.path.abspath(InstallDir + "\\data"), # Using absolute path for data folder too. + os.path.abspath(InstallDir/"data"), # Using absolute path for data folder too. "-boot", "-fakeiso", "-v", @@ -309,10 +307,10 @@ def download_and_unpack_mod(URL, MOD_ID, MOD_NAME, LINK_TYPE, InstallDir, Latest # download update from github # Create a new directory because it does not exist - try_remove_dir(InstallDir + "/temp") - if not os.path.exists(InstallDir + "/temp"): - print("Creating install dir: " + InstallDir) - os.makedirs(InstallDir + "/temp", exist_ok=True) + try_remove_dir(InstallDir/"temp") + if not os.path.exists(InstallDir/"temp"): + print(f"Creating install dir: {InstallDir}") + os.makedirs(InstallDir/"temp", exist_ok=True) response = requests.get(LatestRelAssetsURL) if response.history: @@ -331,103 +329,102 @@ def download_and_unpack_mod(URL, MOD_ID, MOD_NAME, LINK_TYPE, InstallDir, Latest print() print(str("File size is ") + str(file.length)) urllib.request.urlretrieve( - LatestRelAssetsURL, InstallDir + "/temp/updateDATA.zip", show_progress + LatestRelAssetsURL, InstallDir/"temp"/"updateDATA.zip", show_progress ) print("Done downloading") r = requests.head(LatestRelAssetsURL, allow_redirects=True) # delete any previous installation - print("Removing previous installation " + InstallDir) - try_remove_dir(InstallDir + "/data") - try_remove_dir(InstallDir + "/.github") - try_remove_dir(InstallDir + "/SND") - try_remove_file(InstallDir + "/gk.exe") - try_remove_file(InstallDir + "/goalc.exe") - try_remove_file(InstallDir + "/extractor.exe") + print(f"Removing previous installation {InstallDir}") + try_remove_dir(InstallDir/"data") + try_remove_dir(InstallDir/".github") + try_remove_dir(InstallDir/"SND") + try_remove_file(InstallDir/"gk.exe") + try_remove_file(InstallDir/"goalc.exe") + try_remove_file(InstallDir/"extractor.exe") #jak2hack - try_remove_file(InstallDir + "/decompiler.exe") + try_remove_file(InstallDir/"decompiler.exe") # extract mod zipped update print("Extracting update") - TempDir = InstallDir + "/temp" + TempDir = InstallDir/"temp" try: - with zipfile.ZipFile(TempDir + "/updateDATA.zip", "r") as zip_ref: + with zipfile.ZipFile(TempDir/"updateDATA.zip", "r") as zip_ref: zip_ref.extractall(TempDir) except BadZipFile as e: print("Error while extracting from zip: ", e) return # delete the mod zipped update archive - try_remove_file(TempDir + "/updateDATA.zip") + try_remove_file(TempDir/"updateDATA.zip") SubDir = TempDir if LINK_TYPE == githubUtils.LinkTypes.BRANCH or len(os.listdir(SubDir)) == 1: # for branches, the downloaded zip puts all files one directory down - SubDir = SubDir + "/" + os.listdir(SubDir)[0] + SubDir = SubDir/os.listdir(SubDir)[0] - print("Moving files from " + SubDir + " up to " + InstallDir) + print(f"Moving files from {SubDir} up to {InstallDir}") allfiles = os.listdir(SubDir) for f in allfiles: - shutil.move(SubDir + "/" + f, InstallDir + "/" + f) + shutil.move(SubDir/f, InstallDir/f) try_remove_dir(TempDir) #replace the settings and discord RPC texts automatically before we build the game. replaceText( - InstallDir + r"\data\goal_src\jak1\pc\pckernel.gc", + InstallDir/"data"/"goal_src"/"jak1"/"pc"/"pckernel.gc", "Playing Jak and Daxter: The Precursor Legacy", "Playing " + MOD_NAME, ) replaceText( - InstallDir + r"\data\goal_src\jak1\pc\pckernel.gc", + InstallDir/"data"/"goal_src"/"jak1"/"pc"/"pckernel.gc", "/pc-settings.gc", r"/" + MOD_ID + "-settings.gc", ) replaceText( - InstallDir + r"\data\goal_src\jak1\pc\pckernel-common.gc", + InstallDir/"data"/"goal_src"/"jak1"/"pc"/"pckernel-common.gc", "/pc-settings.gc", r"/" + MOD_ID + "-settings.gc", ) replaceText( - InstallDir + r"\data\goal_src\jak1\pc\pckernel-common.gc", + InstallDir/"data"/"goal_src"/"jak1"/"pc"/"pckernel-common.gc", "/pc-settings.gc", r"/" + MOD_ID + "-settings.gc", ) replaceText( - InstallDir + r"\data\decompiler\config\jak1_ntsc_black_label.jsonc", + InstallDir/"data"/"decompiler"/"config"/"jak1_ntsc_black_label.jsonc", "\"process_tpages\": true,", "\"process_tpages\": false,", ) replaceText( - InstallDir + r"\data\decompiler\config\jak1_pal.jsonc", + InstallDir/"data"/"decompiler"/"config"/"jak1_pal.jsonc", "\"process_tpages\": true,", - "\"process_tpages\": false,", ) def rebuild(URL, MOD_ID, MOD_NAME, LINK_TYPE, GAME, should_extract): - InstallDir = ModFolderPATH + MOD_ID - UniversalIsoPath = AppdataPATH + "\OpenGOAL-Mods\_iso_data" + InstallDir = ModFolderPATH/MOD_ID + UniversalIsoPath = AppdataPATH/"OpenGOAL-Mods"/"_iso_data" - print("Looking for some ISO data in " + UniversalIsoPath + "//" + GAME + "//") - found_universal_iso = exists(UniversalIsoPath +"//" + GAME + "//" + "Z6TAIL.DUP") + print(f"Looking for some ISO data in {UniversalIsoPath/GAME}") + found_universal_iso = exists(UniversalIsoPath/GAME/"Z6TAIL.DUP") #if ISO_DATA has content, store this path to pass to the extractor if found_universal_iso: print("We found ISO data from a previous mod installation! Lets use it!") - print("Found in " + UniversalIsoPath +"//" + GAME + "//" + "Z6TAIL.DUP") - iso_path = UniversalIsoPath + "\\" + GAME + print(f"Found in {UniversalIsoPath/GAME/'Z6TAIL.DUP'}") + iso_path = UniversalIsoPath/GAME - if not is_junction(InstallDir + "\\data\\iso_data"): + if not is_junction(InstallDir/"data"/"iso_data"): # we have iso extracted to universal folder already, just symlink it. otherwise we'll copy it there and symlink after extractor closes - try_remove_dir(InstallDir + "\\data\\iso_data/") - makeDirSymlink(InstallDir + "\\data\\iso_data\\", UniversalIsoPath) + try_remove_dir(InstallDir/"data"/"iso_data") + makeDirSymlink(InstallDir/"data"/"iso_data", UniversalIsoPath) else: print("We did not find " + GAME + " ISO data from a previous mod, lets ask for some!") # cleanup and remove a corrupted iso - if os.path.exists(UniversalIsoPath + "//" + GAME) and os.path.isdir(UniversalIsoPath) and not (exists((UniversalIsoPath + "//" + GAME + "//" + "Z6TAIL.DUP"))): + if os.path.exists(UniversalIsoPath/GAME) and os.path.isdir(UniversalIsoPath) and not (exists((UniversalIsoPath/GAME/"Z6TAIL.DUP"))): print("Removing corrupted iso destination...") - shutil.rmtree(UniversalIsoPath + "//" + GAME) + shutil.rmtree(UniversalIsoPath/GAME) ensure_jak_folders_exist() # prompt for their ISO and store its path @@ -454,7 +451,7 @@ def rebuild(URL, MOD_ID, MOD_NAME, LINK_TYPE, GAME, should_extract): #Extract and compile if GAME == "jak1": - extractor_command_list = [InstallDir + "\extractor.exe", "-f", iso_path, "-v", "-c"] + extractor_command_list = [InstallDir/"extractor.exe", "-f", iso_path, "-v", "-c"] if should_extract: extractor_command_list.append("-e") extractor_command_list.append("-d") @@ -468,7 +465,7 @@ def rebuild(URL, MOD_ID, MOD_NAME, LINK_TYPE, GAME, should_extract): return elif GAME == "jak2": - extractor_command_list = [InstallDir + "\extractor.exe", "-f", iso_path, "-v", "-c", "-g", "jak2"] + extractor_command_list = [InstallDir/"extractor.exe", "-f", iso_path, "-v", "-c", "-g", "jak2"] if should_extract: extractor_command_list.append("-e") extractor_command_list.append("-d") @@ -489,10 +486,10 @@ def rebuild(URL, MOD_ID, MOD_NAME, LINK_TYPE, GAME, should_extract): # move the extrated contents to the universal launchers directory for next time. if not found_universal_iso: ensure_jak_folders_exist() - moveDirContents(InstallDir + "\\data\\iso_data/" + GAME, UniversalIsoPath + "//" + GAME) + moveDirContents(InstallDir/"data"/"iso_data"/GAME, UniversalIsoPath/GAME) # replace iso_data with symlink - try_remove_dir(InstallDir + "\\data\\iso_data/") - makeDirSymlink(InstallDir + "\\data\\iso_data", UniversalIsoPath) + try_remove_dir(InstallDir/"data"/"iso_data") + makeDirSymlink(InstallDir/"data"/"iso_data", UniversalIsoPath) launch_local(MOD_ID, GAME) return @@ -513,8 +510,8 @@ def update_and_launch(URL, MOD_ID, MOD_NAME, LINK_TYPE, GAME): r = json.loads(json.dumps(requests.get(url=launchUrl, params=PARAMS).json())) # paths - InstallDir = ModFolderPATH + MOD_ID - UniversalIsoPath = AppdataPATH + "\OpenGOAL-Mods\_iso_data" + InstallDir = ModFolderPATH/MOD_ID + UniversalIsoPath = AppdataPATH/"OpenGOAL-Mods"/"_iso_data" ensure_jak_folders_exist() # store Latest Release and check our local date too. @@ -548,21 +545,20 @@ def update_and_launch(URL, MOD_ID, MOD_NAME, LINK_TYPE, GAME): # content_type = response.headers["content-type"] LastWrite = datetime(2020, 5, 17) - if exists(InstallDir + "/" + ExecutableName): - LastWrite = datetime.utcfromtimestamp( - pathlib.Path(InstallDir + "/" + ExecutableName).stat().st_mtime - ) + exe_path = InstallDir/ExecutableName + if exe_path.exists(): + LastWrite = datetime.utcfromtimestamp(exe_path.stat().st_mtime) # update checks Outdated = bool(LastWrite < LatestRel) - NotExtracted = bool(not (exists(UniversalIsoPath + "//" + GAME + "//" + "Z6TAIL.DUP"))) - NotCompiled = bool(not (exists(InstallDir + r"\data\out" + "//" + GAME + "//" + "fr3\GAME.fr3"))) + NotExtracted = bool(not (exists(UniversalIsoPath/GAME/"Z6TAIL.DUP"))) + NotCompiled = bool(not (exists(InstallDir/"data"/"out"/GAME/"fr3"/"GAME.fr3"))) needUpdate = bool(Outdated or NotExtracted or NotCompiled) print("Currently installed version created on: " + LastWrite.strftime('%Y-%m-%d %H:%M:%S')) print("Newest version created on: " + LatestRel.strftime('%Y-%m-%d %H:%M:%S')) if(NotExtracted): - print("Error! Iso data does not appear to be extracted to " + UniversalIsoPath +"//" + GAME + "//" + "Z6TAIL.DUP") + print(f"Error! Iso data does not appear to be extracted to {UniversalIsoPath/GAME/'Z6TAIL.DUP'}") print("Will ask user to provide ISO") if(NotCompiled): print("Error! The game is not compiled") @@ -573,18 +569,15 @@ def update_and_launch(URL, MOD_ID, MOD_NAME, LINK_TYPE, GAME): print("Is newest posted update older than what we have installed? " + str((LastWrite < LatestRel))) # attempt to migrate any old settings files from using MOD_NAME to MOD_ID - if exists(AppdataPATH + "\OpenGOAL" + "//" + GAME + "//" + "settings\\" + MOD_NAME + "-settings.gc"): + mod_name_settings_path = AppdataPATH/"OpenGOAL"/GAME/"settings"/(MOD_NAME+"-settings.gc") + if mod_name_settings_path.exists(): # just to be safe delete the migrated settings file if it already exists (shouldn't happen but prevents rename from failing below) - if exists(AppdataPATH + "\OpenGOAL" + "//" + GAME + "//" + "settings\\" + MOD_ID + "-settings.gc"): - os.remove( - AppdataPATH + "\OpenGOAL" + "//" + GAME + "//" + "settings\\" + MOD_ID + "-settings.gc" - ) + mod_id_settings_path = AppdataPATH/"OpenGOAL"/GAME/"settings"/(MOD_ID+"-settings.gc") + if mod_id_settings_path.exists(): + mod_id_settings_path.unlink() # rename settings file - os.rename( - AppdataPATH + "\OpenGOAL" + "//" + GAME + "//" + "settings\\" + MOD_NAME + "-settings.gc", - AppdataPATH + "\OpenGOAL" + "//" + GAME + "//" + "settings\\" + MOD_ID + "-settings.gc", - ) + mod_name_settings_path.rename(mod_id_settings_path) # force update to ensure we recompile with adjusted settings filename in pckernel.gc needUpdate = True From 08cc70eeeca60ff71ee7039f4d05286f3e28d39c Mon Sep 17 00:00:00 2001 From: Alexander <43010352+amarzot@users.noreply.github.com> Date: Sat, 13 Jan 2024 23:43:56 -0500 Subject: [PATCH 02/17] Update autoupdater.py too --- Launcher with autoupdater.py | 41 ++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/Launcher with autoupdater.py b/Launcher with autoupdater.py index 69a62cc..d2b4a10 100644 --- a/Launcher with autoupdater.py +++ b/Launcher with autoupdater.py @@ -8,8 +8,11 @@ from datetime import datetime from os.path import exists from pathlib import Path +from appdirs import AppDirs import subprocess -AppdataPATH = os.getenv('APPDATA') + "\\OpenGOAL-UnofficialModLauncher\\" + +dirs = AppDirs(roaming=True) +AppdataPATH = Path(dirs.user_data_dir)/"OpenGOAL-UnofficialModLauncher" def show_progress(block_num, block_size, total_size): if total_size > 0: @@ -45,8 +48,8 @@ def check_for_updates(): latest_release_assets_url = "https://github.com/OpenGOAL-Unofficial-Mods/OpenGoal-ModLauncher-dev/releases/download/v1.10fixoldpckernel/openGOALModLauncher.exe" last_write = datetime(2020, 5, 17) - if os.path.exists(AppdataPATH + "\\OpengoalModLauncher.exe"): - last_write = datetime.utcfromtimestamp(Path(AppdataPATH + "\\OpengoalModLauncher.exe").stat().st_mtime) + if (AppdataPATH/"OpengoalModLauncher.exe").exists(): + last_write = datetime.utcfromtimestamp((AppdataPATH/"OpengoalModLauncher.exe").stat().st_mtime) need_update = bool((last_write < latest_release)) @@ -63,8 +66,6 @@ def check_for_updates(): window['launch_button'].update(visible=True) def download_newest_mod(): - AppdataPATH = os.getenv('APPDATA') + "\\OpenGOAL-UnofficialModLauncher\\" - launch_url = "https://api.github.com/repos/OpenGOAL-Unofficial-Mods/OpenGoal-ModLauncher-dev/releases" response = requests.get(url=launch_url, params={'address': "yolo"}) @@ -81,35 +82,35 @@ def download_newest_mod(): latest_release_assets_url = "https://github.com/OpenGOAL-Unofficial-Mods/OpenGoal-ModLauncher-dev/releases/download/v1.10fixoldpckernel/openGOALModLauncher.exe" last_write = datetime(2020, 5, 17) - if os.path.exists(AppdataPATH + "\\OpengoalModLauncher.exe"): - last_write = datetime.utcfromtimestamp(Path(AppdataPATH + "\\OpengoalModLauncher.exe").stat().st_mtime) + if (AppdataPATH/"OpengoalModLauncher.exe").exists(): + last_write = datetime.utcfromtimestamp((AppdataPATH/"OpengoalModLauncher.exe").stat().st_mtime) need_update = bool((last_write < latest_release)) if need_update: window['update_status'].update("Starting Update...") - try_remove_dir(AppdataPATH + "/temp") - if not os.path.exists(AppdataPATH + "/temp"): - os.makedirs(AppdataPATH + "/temp") + try_remove_dir(AppdataPATH/"temp") + if not os.path.exists(AppdataPATH/"temp"): + os.makedirs(AppdataPATH/"temp") window['update_status'].update("Downloading update from " + latest_release_assets_url) file = urllib.request.urlopen(latest_release_assets_url) - urllib.request.urlretrieve(latest_release_assets_url, AppdataPATH + "/temp/OpengoalModLauncher.exe", show_progress) + urllib.request.urlretrieve(latest_release_assets_url, AppdataPATH/"/temp/OpengoalModLauncher.exe", show_progress) window['update_status'].update("Done downloading") - window['update_status'].update("Removing previous installation " + AppdataPATH) - try_remove_dir(AppdataPATH + "/data") - try_remove_file(AppdataPATH + "/gk.exe") - try_remove_file(AppdataPATH + "/goalc.exe") - try_remove_file(AppdataPATH + "/extractor.exe") + window['update_status'].update(f"Removing previous installation {AppdataPATH}") + try_remove_dir(AppdataPATH/"data") + try_remove_file(AppdataPATH/"gk.exe") + try_remove_file(AppdataPATH/"goalc.exe") + try_remove_file(AppdataPATH/"extractor.exe") window['update_status'].update("Extracting update") - temp_dir = AppdataPATH + "/temp" - try_remove_file(temp_dir + "/updateDATA.zip") + temp_dir = AppdataPATH/"temp" + try_remove_file(temp_dir/"updateDATA.zip") sub_dir = temp_dir all_files = os.listdir(sub_dir) for f in all_files: - shutil.move(sub_dir + "/" + f, AppdataPATH + "/" + f) + shutil.move(sub_dir/f, AppdataPATH/f) try_remove_dir(temp_dir) window['update_status'].update("Update complete") window['update_button'].update(visible=False) @@ -136,6 +137,6 @@ def download_newest_mod(): download_newest_mod() elif event == "launch_button": window.close() - subprocess.call([ AppdataPATH + "openGOALModLauncher.exe"]) + subprocess.call([ AppdataPATH/"openGOALModLauncher.exe"]) window.close() From 2012947808cb33de33a44aeddd82dfec9e760947 Mon Sep 17 00:00:00 2001 From: Steven Lares <13951178+StevenLares@users.noreply.github.com> Date: Tue, 20 Feb 2024 11:00:26 -0500 Subject: [PATCH 03/17] Adjusted one additional path to be pathlib, in prep for Linux compatibility --- Launcher with autoupdater.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Launcher with autoupdater.py b/Launcher with autoupdater.py index d2b4a10..89a5671 100644 --- a/Launcher with autoupdater.py +++ b/Launcher with autoupdater.py @@ -95,7 +95,7 @@ def download_newest_mod(): window['update_status'].update("Downloading update from " + latest_release_assets_url) file = urllib.request.urlopen(latest_release_assets_url) - urllib.request.urlretrieve(latest_release_assets_url, AppdataPATH/"/temp/OpengoalModLauncher.exe", show_progress) + urllib.request.urlretrieve(latest_release_assets_url, AppdataPATH/"temp"/"OpengoalModLauncher.exe", show_progress) window['update_status'].update("Done downloading") window['update_status'].update(f"Removing previous installation {AppdataPATH}") From 1f72e36083b460129a177de3434f1fe989cdfff9 Mon Sep 17 00:00:00 2001 From: Steven Lares <13951178+StevenLares@users.noreply.github.com> Date: Tue, 20 Feb 2024 12:09:05 -0500 Subject: [PATCH 04/17] Added a function to launcherUtils that returns the correct executable name corresponding to user's OS. This function is now used by other classes to store the executable names in member variables. Relevant code now refers to the member variables instead of the previously hardcoded exe names. In addition, I adjusted download links to point to executables (with the platform agnostic exe names) although that will require building the launcher for Linux on each release. --- Launcher with autoupdater.py | 29 +++++++++++------- openGOALModLauncher.py | 8 ++++- utils/launcherUtils.py | 58 ++++++++++++++++++++++-------------- 3 files changed, 61 insertions(+), 34 deletions(-) diff --git a/Launcher with autoupdater.py b/Launcher with autoupdater.py index 89a5671..893d629 100644 --- a/Launcher with autoupdater.py +++ b/Launcher with autoupdater.py @@ -10,10 +10,17 @@ from pathlib import Path from appdirs import AppDirs import subprocess +from utils import launcherUtils dirs = AppDirs(roaming=True) AppdataPATH = Path(dirs.user_data_dir)/"OpenGOAL-UnofficialModLauncher" +OpengoalModLauncher_exe = launcherUtils.get_exe("OpengoalModLauncher") +gk_exe = launcherUtils.get_exe("gk") +decompiler_exe = launcherUtils.get_exe("decompiler") +goalc_exe = launcherUtils.get_exe("goalc") +extractor_exe = launcherUtils.get_exe("extractor") + def show_progress(block_num, block_size, total_size): if total_size > 0: try: @@ -45,11 +52,11 @@ def check_for_updates(): else: print("WARNING: Failed to query GitHub API, you might be rate-limited. Using default fallback release instead.") latest_release = datetime(2023, 7, 23) - latest_release_assets_url = "https://github.com/OpenGOAL-Unofficial-Mods/OpenGoal-ModLauncher-dev/releases/download/v1.10fixoldpckernel/openGOALModLauncher.exe" + latest_release_assets_url = "https://github.com/OpenGOAL-Unofficial-Mods/OpenGoal-ModLauncher-dev/releases/download/v1.10fixoldpckernel/"+ OpengoalModLauncher_exe last_write = datetime(2020, 5, 17) - if (AppdataPATH/"OpengoalModLauncher.exe").exists(): - last_write = datetime.utcfromtimestamp((AppdataPATH/"OpengoalModLauncher.exe").stat().st_mtime) + if (AppdataPATH/OpengoalModLauncher_exe).exists(): + last_write = datetime.utcfromtimestamp((AppdataPATH/OpengoalModLauncher_exe).stat().st_mtime) need_update = bool((last_write < latest_release)) @@ -79,11 +86,11 @@ def download_newest_mod(): else: print("WARNING: Failed to query GitHub API, you might be rate-limited. Using default fallback release instead.") latest_release = datetime(2023, 7, 23) - latest_release_assets_url = "https://github.com/OpenGOAL-Unofficial-Mods/OpenGoal-ModLauncher-dev/releases/download/v1.10fixoldpckernel/openGOALModLauncher.exe" + latest_release_assets_url = "https://github.com/OpenGOAL-Unofficial-Mods/OpenGoal-ModLauncher-dev/releases/download/v1.10fixoldpckernel/"+ OpengoalModLauncher_exe last_write = datetime(2020, 5, 17) - if (AppdataPATH/"OpengoalModLauncher.exe").exists(): - last_write = datetime.utcfromtimestamp((AppdataPATH/"OpengoalModLauncher.exe").stat().st_mtime) + if (AppdataPATH/OpengoalModLauncher_exe).exists(): + last_write = datetime.utcfromtimestamp((AppdataPATH/OpengoalModLauncher_exe).stat().st_mtime) need_update = bool((last_write < latest_release)) @@ -95,14 +102,14 @@ def download_newest_mod(): window['update_status'].update("Downloading update from " + latest_release_assets_url) file = urllib.request.urlopen(latest_release_assets_url) - urllib.request.urlretrieve(latest_release_assets_url, AppdataPATH/"temp"/"OpengoalModLauncher.exe", show_progress) + urllib.request.urlretrieve(latest_release_assets_url, AppdataPATH/"temp"/OpengoalModLauncher_exe, show_progress) window['update_status'].update("Done downloading") window['update_status'].update(f"Removing previous installation {AppdataPATH}") try_remove_dir(AppdataPATH/"data") - try_remove_file(AppdataPATH/"gk.exe") - try_remove_file(AppdataPATH/"goalc.exe") - try_remove_file(AppdataPATH/"extractor.exe") + try_remove_file(AppdataPATH/gk_exe) + try_remove_file(AppdataPATH/goalc_exe) + try_remove_file(AppdataPATH/extractor_exe) window['update_status'].update("Extracting update") temp_dir = AppdataPATH/"temp" @@ -137,6 +144,6 @@ def download_newest_mod(): download_newest_mod() elif event == "launch_button": window.close() - subprocess.call([ AppdataPATH/"openGOALModLauncher.exe"]) + subprocess.call([AppdataPATH/OpengoalModLauncher_exe]) window.close() diff --git a/openGOALModLauncher.py b/openGOALModLauncher.py index 99a20a3..626a370 100644 --- a/openGOALModLauncher.py +++ b/openGOALModLauncher.py @@ -40,6 +40,12 @@ sg.theme("DarkBlue3") +# executables + +gk_exe = launcherUtils.get_exe("gk") + + + def openLauncherWebsite(): webbrowser.open("https://jakmods.dev") @@ -246,7 +252,7 @@ def getRefreshedTableData(sort_col_idx): mod["access_date"] = "Not Installed" mod_path = ModFolderPATH/mod_id - mod_gk_path = mod_path/"gk.exe" + mod_gk_path = mod_path / gk_exe # determine local install/access datetime if mod_id in installed_mod_subfolders: mod[ diff --git a/utils/launcherUtils.py b/utils/launcherUtils.py index aa8e458..c7d6630 100644 --- a/utils/launcherUtils.py +++ b/utils/launcherUtils.py @@ -28,15 +28,32 @@ import time import ctypes -FILE_DATE_TO_CHECK = "gk.exe" + +# This function expects the name of the executable without .exe at the end +# For windows systems, it appends ".exe" to the executable name and returns this value +# For linux and mac, it returns it as-is +# It is meant to be called at the beginning of a script, where the results are stored +# into a string variable. The string variable can then be used in followup logic +def get_exe(executable): + return executable + ".exe" if platform.system() == "Windows" else executable + + +gk_exe = get_exe("gk") +decompiler_exe = get_exe("decompiler") +goalc_exe = get_exe("goalc") +extractor_exe = get_exe("extractor") + + +FILE_DATE_TO_CHECK = gk_exe UPDATE_FILE_EXTENTION = ".zip" + # Executable we're checking the 'modified' time of ExecutableName = str( FILE_DATE_TO_CHECK -) # Executable we're checking the 'modified' time of +) # content_type of the .deb release is also "application\zip", so rely on file ext FileExt = str( UPDATE_FILE_EXTENTION -) # content_type of the .deb release is also "application\zip", so rely on file ext +) FileIdent = "" # If we ever get to multiple .zip files in a release, include other identifying information from the name dirs = AppDirs(roaming=True) currentOS = platform.system() @@ -50,8 +67,6 @@ - - def installedlist(PATH): print(PATH) scanDir = PATH @@ -244,8 +259,7 @@ def ensure_jak_folders_exist(): #check if we have decompiler in the path, if not check if we have a backup, if so use it, if not download a backup then use it def getDecompiler(path): - decompiler_exe = "decompiler.exe" - decompiler_url = "https://github.com/OpenGOAL-Mods/OG-Mod-Base/raw/main/out/build/Release/bin/decompiler.exe" + decompiler_url = "https://github.com/OpenGOAL-Mods/OG-Mod-Base/raw/main/out/build/Release/bin/" + decompiler_exe # Check if the decompiler exists in the provided path if os.path.exists(os.path.join(path, decompiler_exe)): @@ -264,17 +278,17 @@ def getDecompiler(path): def launch_local(MOD_ID, GAME): try: # Close Gk and goalc if they were open. - try_kill_process("gk.exe") - try_kill_process("goalc.exe") + try_kill_process(gk_exe) + try_kill_process(goalc_exe) time.sleep(1) InstallDir = ModFolderPATH/MOD_ID if GAME == "jak2": GKCOMMANDLINElist = [ - InstallDir/"gk.exe", + InstallDir / gk_exe, "--proj-path", - InstallDir/"data", + InstallDir /"data", "-v", "--game", "jak2", @@ -284,7 +298,7 @@ def launch_local(MOD_ID, GAME): ] else: # if GAME == "jak1": GKCOMMANDLINElist = [ - os.path.abspath(InstallDir/"gk.exe"), # Using os.path.abspath to get the absolute path. + os.path.abspath(InstallDir / gk_exe), # Using os.path.abspath to get the absolute path. "--proj-path", os.path.abspath(InstallDir/"data"), # Using absolute path for data folder too. "-boot", @@ -302,8 +316,8 @@ def download_and_unpack_mod(URL, MOD_ID, MOD_NAME, LINK_TYPE, InstallDir, Latest print("\nNeed to update") print("Starting Update...") # Close Gk and goalc if they were open. - try_kill_process("gk.exe") - try_kill_process("goalc.exe") + try_kill_process(gk_exe) + try_kill_process(goalc_exe) # download update from github # Create a new directory because it does not exist @@ -339,11 +353,11 @@ def download_and_unpack_mod(URL, MOD_ID, MOD_NAME, LINK_TYPE, InstallDir, Latest try_remove_dir(InstallDir/"data") try_remove_dir(InstallDir/".github") try_remove_dir(InstallDir/"SND") - try_remove_file(InstallDir/"gk.exe") - try_remove_file(InstallDir/"goalc.exe") - try_remove_file(InstallDir/"extractor.exe") + try_remove_file(InstallDir / gk_exe) + try_remove_file(InstallDir / goalc_exe) + try_remove_file(InstallDir / extractor_exe) #jak2hack - try_remove_file(InstallDir/"decompiler.exe") + try_remove_file(InstallDir / decompiler_exe) # extract mod zipped update print("Extracting update") @@ -445,13 +459,13 @@ def rebuild(URL, MOD_ID, MOD_NAME, LINK_TYPE, GAME, should_extract): return # Close Gk and goalc if they were open. - try_kill_process("gk.exe") - try_kill_process("goalc.exe") + try_kill_process(gk_exe) + try_kill_process(goalc_exe) print("Done update starting extractor This one can take a few moments! \n") #Extract and compile if GAME == "jak1": - extractor_command_list = [InstallDir/"extractor.exe", "-f", iso_path, "-v", "-c"] + extractor_command_list = [InstallDir / extractor_exe, "-f", iso_path, "-v", "-c"] if should_extract: extractor_command_list.append("-e") extractor_command_list.append("-d") @@ -465,7 +479,7 @@ def rebuild(URL, MOD_ID, MOD_NAME, LINK_TYPE, GAME, should_extract): return elif GAME == "jak2": - extractor_command_list = [InstallDir/"extractor.exe", "-f", iso_path, "-v", "-c", "-g", "jak2"] + extractor_command_list = [InstallDir / extractor_exe, "-f", iso_path, "-v", "-c", "-g", "jak2"] if should_extract: extractor_command_list.append("-e") extractor_command_list.append("-d") From a002281a53247b9c412b952130bfb98d9cb31648 Mon Sep 17 00:00:00 2001 From: Steven Lares <13951178+StevenLares@users.noreply.github.com> Date: Tue, 20 Feb 2024 14:18:31 -0500 Subject: [PATCH 05/17] Changed download links to point to the latest release, instead of an older release --- Launcher with autoupdater.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Launcher with autoupdater.py b/Launcher with autoupdater.py index 893d629..6820add 100644 --- a/Launcher with autoupdater.py +++ b/Launcher with autoupdater.py @@ -52,7 +52,7 @@ def check_for_updates(): else: print("WARNING: Failed to query GitHub API, you might be rate-limited. Using default fallback release instead.") latest_release = datetime(2023, 7, 23) - latest_release_assets_url = "https://github.com/OpenGOAL-Unofficial-Mods/OpenGoal-ModLauncher-dev/releases/download/v1.10fixoldpckernel/"+ OpengoalModLauncher_exe + latest_release_assets_url = "https://github.com/OpenGOAL-Unofficial-Mods/OpenGoal-ModLauncher-dev/releases/download/latest/"+ OpengoalModLauncher_exe last_write = datetime(2020, 5, 17) if (AppdataPATH/OpengoalModLauncher_exe).exists(): @@ -86,7 +86,7 @@ def download_newest_mod(): else: print("WARNING: Failed to query GitHub API, you might be rate-limited. Using default fallback release instead.") latest_release = datetime(2023, 7, 23) - latest_release_assets_url = "https://github.com/OpenGOAL-Unofficial-Mods/OpenGoal-ModLauncher-dev/releases/download/v1.10fixoldpckernel/"+ OpengoalModLauncher_exe + latest_release_assets_url = "https://github.com/OpenGOAL-Unofficial-Mods/OpenGoal-ModLauncher-dev/releases/download/latest/"+ OpengoalModLauncher_exe last_write = datetime(2020, 5, 17) if (AppdataPATH/OpengoalModLauncher_exe).exists(): From a1b767595709b5acddb3f4f4b2f344eef380c6fa Mon Sep 17 00:00:00 2001 From: Steven Lares <13951178+StevenLares@users.noreply.github.com> Date: Wed, 21 Feb 2024 16:11:19 -0500 Subject: [PATCH 06/17] Updated utils functions to include linux / unix commands. Also, I changed platform.system() to sys.platform for consistency with the rest of the code --- utils/launcherUtils.py | 368 ++++++++++++++++++++++------------------- 1 file changed, 199 insertions(+), 169 deletions(-) diff --git a/utils/launcherUtils.py b/utils/launcherUtils.py index c7d6630..7d5a8d7 100644 --- a/utils/launcherUtils.py +++ b/utils/launcherUtils.py @@ -22,7 +22,6 @@ import zipfile from zipfile import BadZipFile from appdirs import AppDirs -import platform import stat from pathlib import Path import time @@ -35,7 +34,7 @@ # It is meant to be called at the beginning of a script, where the results are stored # into a string variable. The string variable can then be used in followup logic def get_exe(executable): - return executable + ".exe" if platform.system() == "Windows" else executable + return executable + ".exe" if sys.platform == "win32" else executable gk_exe = get_exe("gk") @@ -43,11 +42,10 @@ def get_exe(executable): goalc_exe = get_exe("goalc") extractor_exe = get_exe("extractor") - FILE_DATE_TO_CHECK = gk_exe UPDATE_FILE_EXTENTION = ".zip" - # Executable we're checking the 'modified' time of +# Executable we're checking the 'modified' time of ExecutableName = str( FILE_DATE_TO_CHECK ) # content_type of the .deb release is also "application\zip", so rely on file ext @@ -56,17 +54,12 @@ def get_exe(executable): ) FileIdent = "" # If we ever get to multiple .zip files in a release, include other identifying information from the name dirs = AppDirs(roaming=True) -currentOS = platform.system() -ModFolderPATH = Path(dirs.user_data_dir)/"OpenGOAL-Mods" +ModFolderPATH = Path(dirs.user_data_dir) / "OpenGOAL-Mods" AppdataPATH = Path(dirs.user_data_dir) - pbar = None - - - def installedlist(PATH): print(PATH) scanDir = PATH @@ -98,33 +91,46 @@ def show_progress(block_num, block_size, total_size): def process_exists(process_name): - call = "TASKLIST", "/FI", "imagename eq %s" % process_name - try: - # use buildin check_output right away - output = subprocess.check_output(call).decode() - # check in last line for process name - last_line = output.strip().split("\r\n")[-1] - # because Fail message could be translated - return last_line.lower().startswith(process_name.lower()) - except: - return False + if sys.platform == "win32": + call = "TASKLIST", "/FI", "imagename eq %s" % process_name + try: + # use buildin check_output right away + output = subprocess.check_output(call).decode() + # check in last line for process name + last_line = output.strip().split("\r\n")[-1] + # because Fail message could be translated + return last_line.lower().startswith(process_name.lower()) + except: + return False + else: + call = ["pgrep", "--list-name", "^" + process_name + "$"] + try: + output = subprocess.check_output(call).decode() + return len(output) > 1 + except: + return False def try_kill_process(process_name): if process_exists(process_name): - os.system("taskkill /f /im " + process_name) + if sys.platform == "win32": + os.system("taskkill /f /im " + process_name) + else: + os.system("pkill " + "^" + process_name + "$") def try_remove_file(file): if exists(file): os.remove(file) + def is_junction(path: str) -> bool: try: return bool(os.readlink(path)) except OSError: return False + def try_remove_dir(dir): if exists(dir): print(f"found dir {dir}, attempting to remove") @@ -134,29 +140,36 @@ def try_remove_dir(dir): def local_mod_image(MOD_ID): - path = ModFolderPATH/MOD_ID/"ModImage.png" + path = ModFolderPATH / MOD_ID / "ModImage.png" if exists(path): return path return None - def moveDirContents(src, dest): - # moves all files from src to dest, without moving src dir itself - for f in os.listdir(src): - src_path = os.path.join(src, f) - dst_path = os.path.join(dest, f) - shutil.move(src_path, dst_path) + # moves all files from src to dest, without moving src dir itself + for f in os.listdir(src): + src_path = os.path.join(src, f) + dst_path = os.path.join(dest, f) + shutil.move(src_path, dst_path) + def makeDirSymlink(link, target): - subprocess.check_call('mklink /J "%s" "%s"' % (link, target), shell=True) + if sys.platform == "win32": + subprocess.check_call('mklink /J "%s" "%s"' % (link, target), shell=True) + else: + subprocess.check_call('ln -s "%s" "%s"' % (link, target), shell=True) def makeFileSymlink(link, target): # if ctypes.windll.shell32.IsUserAnAdmin(): # subprocess.check_call('mklink "%s" "%s"' % (link, target), shell=True) # else: - subprocess.check_call('mklink /H "%s" "%s"' % (link, target), shell=True) + if sys.platform == "win32": + subprocess.check_call('mklink /H "%s" "%s"' % (link, target), shell=True) + else: + subprocess.check_call('ln -s "%s" "%s"' % (link, target), shell=True) + def link_files_by_extension(source_dir, destination_dir): # Ensure the source directory exists @@ -183,11 +196,12 @@ def link_files_by_extension(source_dir, destination_dir): os.remove(destination_path) # Create a symbolic link from the source location to the destination location. - #print("making " + destination_path + "<-des source ->" + file_path) + # print("making " + destination_path + "<-des source ->" + file_path) makeFileSymlink(destination_path, file_path) + def openFolder(path): - jak2_path = Path(dirs.user_data_dir)/"OpenGOAL"/"mods"/"data"/"iso_data"/"jak2" + jak2_path = Path(dirs.user_data_dir) / "OpenGOAL" / "mods" / "data" / "iso_data" / "jak2" if not jak2_path.exists(): jak2_path.mkdir(parents=True) print(path) @@ -197,6 +211,7 @@ def openFolder(path): opener = "open" if sys.platform == "darwin" else "xdg-open" subprocess.call([opener, path]) + def replaceText(path, search_text, replace_text): # Check if the file exists if not os.path.isfile(path): @@ -216,11 +231,12 @@ def replaceText(path, search_text, replace_text): print(f"Text replaced successfully in file '{path}'.") + def ensure_dir(path): path.mkdir(parents=True, exist_ok=True) -def open_browser_link(): - url = "https://google.com" # Replace with the desired URL + +def open_browser_link(url): if sys.platform.startswith('linux'): subprocess.Popen(["xdg-open", url]) elif sys.platform.startswith('win'): @@ -230,10 +246,11 @@ def open_browser_link(): else: print("Unsupported platform") + def open_folder(path): folder_path = path # Replace with the desired folder path if not os.path.exists(path): - # Create the directory + # Create the directory try: os.makedirs(path) print(f"Directory '{path}' created successfully.") @@ -241,11 +258,13 @@ def open_folder(path): print(f"Error creating directory '{path}': {e}") os.startfile(folder_path) + def divide_by_zero(): 1 / 0 + def ensure_jak_folders_exist(): - directory = Path(dirs.user_data_dir)/"OpenGOAL-Mods"/"_iso_data" + directory = Path(dirs.user_data_dir) / "OpenGOAL-Mods" / "_iso_data" jak1_path = os.path.join(directory, "jak1") jak2_path = os.path.join(directory, "jak2") @@ -257,7 +276,8 @@ def ensure_jak_folders_exist(): os.makedirs(jak2_path) print(f"Created 'jak2' folder at {jak2_path}") -#check if we have decompiler in the path, if not check if we have a backup, if so use it, if not download a backup then use it + +# check if we have decompiler in the path, if not check if we have a backup, if so use it, if not download a backup then use it def getDecompiler(path): decompiler_url = "https://github.com/OpenGOAL-Mods/OG-Mod-Base/raw/main/out/build/Release/bin/" + decompiler_exe @@ -275,6 +295,8 @@ def getDecompiler(path): return + +# TODO def launch_local(MOD_ID, GAME): try: # Close Gk and goalc if they were open. @@ -282,37 +304,39 @@ def launch_local(MOD_ID, GAME): try_kill_process(goalc_exe) time.sleep(1) - InstallDir = ModFolderPATH/MOD_ID - + InstallDir = ModFolderPATH / MOD_ID + if GAME == "jak2": - GKCOMMANDLINElist = [ - InstallDir / gk_exe, - "--proj-path", - InstallDir /"data", - "-v", - "--game", - "jak2", - "--", - "-boot", - "-fakeiso" - ] - else: # if GAME == "jak1": - GKCOMMANDLINElist = [ - os.path.abspath(InstallDir / gk_exe), # Using os.path.abspath to get the absolute path. - "--proj-path", - os.path.abspath(InstallDir/"data"), # Using absolute path for data folder too. - "-boot", - "-fakeiso", - "-v", - ] - + GKCOMMANDLINElist = [ + InstallDir / gk_exe, + "--proj-path", + InstallDir / "data", + "-v", + "--game", + "jak2", + "--", + "-boot", + "-fakeiso" + ] + else: # if GAME == "jak1": + GKCOMMANDLINElist = [ + os.path.abspath(InstallDir / gk_exe), # Using os.path.abspath to get the absolute path. + "--proj-path", + os.path.abspath(InstallDir / "data"), # Using absolute path for data folder too. + "-boot", + "-fakeiso", + "-v", + ] + print("running: ", GKCOMMANDLINElist) subprocess.run(GKCOMMANDLINElist, shell=True, cwd=os.path.abspath(InstallDir)) except Exception as e: # Catch all exceptions and print the error message. return str(e) - + + +# TODO def download_and_unpack_mod(URL, MOD_ID, MOD_NAME, LINK_TYPE, InstallDir, LatestRelAssetsURL): - #start the actual update method if needUpdate is true + # start the actual update method if needUpdate is true print("\nNeed to update") print("Starting Update...") # Close Gk and goalc if they were open. @@ -321,11 +345,11 @@ def download_and_unpack_mod(URL, MOD_ID, MOD_NAME, LINK_TYPE, InstallDir, Latest # download update from github # Create a new directory because it does not exist - try_remove_dir(InstallDir/"temp") - if not os.path.exists(InstallDir/"temp"): + try_remove_dir(InstallDir / "temp") + if not os.path.exists(InstallDir / "temp"): print(f"Creating install dir: {InstallDir}") - os.makedirs(InstallDir/"temp", exist_ok=True) - + os.makedirs(InstallDir / "temp", exist_ok=True) + response = requests.get(LatestRelAssetsURL) if response.history: print("Request was redirected") @@ -343,104 +367,107 @@ def download_and_unpack_mod(URL, MOD_ID, MOD_NAME, LINK_TYPE, InstallDir, Latest print() print(str("File size is ") + str(file.length)) urllib.request.urlretrieve( - LatestRelAssetsURL, InstallDir/"temp"/"updateDATA.zip", show_progress + LatestRelAssetsURL, InstallDir / "temp" / "updateDATA.zip", show_progress ) print("Done downloading") r = requests.head(LatestRelAssetsURL, allow_redirects=True) # delete any previous installation print(f"Removing previous installation {InstallDir}") - try_remove_dir(InstallDir/"data") - try_remove_dir(InstallDir/".github") - try_remove_dir(InstallDir/"SND") + try_remove_dir(InstallDir / "data") + try_remove_dir(InstallDir / ".github") + try_remove_dir(InstallDir / "SND") try_remove_file(InstallDir / gk_exe) try_remove_file(InstallDir / goalc_exe) try_remove_file(InstallDir / extractor_exe) - #jak2hack + # jak2hack try_remove_file(InstallDir / decompiler_exe) # extract mod zipped update print("Extracting update") - TempDir = InstallDir/"temp" + TempDir = InstallDir / "temp" try: - with zipfile.ZipFile(TempDir/"updateDATA.zip", "r") as zip_ref: - zip_ref.extractall(TempDir) + with zipfile.ZipFile(TempDir / "updateDATA.zip", "r") as zip_ref: + zip_ref.extractall(TempDir) except BadZipFile as e: - print("Error while extracting from zip: ", e) - return + print("Error while extracting from zip: ", e) + return # delete the mod zipped update archive - try_remove_file(TempDir/"updateDATA.zip") + try_remove_file(TempDir / "updateDATA.zip") SubDir = TempDir if LINK_TYPE == githubUtils.LinkTypes.BRANCH or len(os.listdir(SubDir)) == 1: - # for branches, the downloaded zip puts all files one directory down - SubDir = SubDir/os.listdir(SubDir)[0] + # for branches, the downloaded zip puts all files one directory down + SubDir = SubDir / os.listdir(SubDir)[0] print(f"Moving files from {SubDir} up to {InstallDir}") allfiles = os.listdir(SubDir) for f in allfiles: - shutil.move(SubDir/f, InstallDir/f) + shutil.move(SubDir / f, InstallDir / f) try_remove_dir(TempDir) - #replace the settings and discord RPC texts automatically before we build the game. + # replace the settings and discord RPC texts automatically before we build the game. replaceText( - InstallDir/"data"/"goal_src"/"jak1"/"pc"/"pckernel.gc", - "Playing Jak and Daxter: The Precursor Legacy", - "Playing " + MOD_NAME, + InstallDir / "data" / "goal_src" / "jak1" / "pc" / "pckernel.gc", + "Playing Jak and Daxter: The Precursor Legacy", + "Playing " + MOD_NAME, ) replaceText( - InstallDir/"data"/"goal_src"/"jak1"/"pc"/"pckernel.gc", - "/pc-settings.gc", - r"/" + MOD_ID + "-settings.gc", + InstallDir / "data" / "goal_src" / "jak1" / "pc" / "pckernel.gc", + "/pc-settings.gc", + r"/" + MOD_ID + "-settings.gc", ) replaceText( - InstallDir/"data"/"goal_src"/"jak1"/"pc"/"pckernel-common.gc", - "/pc-settings.gc", - r"/" + MOD_ID + "-settings.gc", + InstallDir / "data" / "goal_src" / "jak1" / "pc" / "pckernel-common.gc", + "/pc-settings.gc", + r"/" + MOD_ID + "-settings.gc", ) replaceText( - InstallDir/"data"/"goal_src"/"jak1"/"pc"/"pckernel-common.gc", - "/pc-settings.gc", - r"/" + MOD_ID + "-settings.gc", + InstallDir / "data" / "goal_src" / "jak1" / "pc" / "pckernel-common.gc", + "/pc-settings.gc", + r"/" + MOD_ID + "-settings.gc", ) replaceText( - InstallDir/"data"/"decompiler"/"config"/"jak1_ntsc_black_label.jsonc", - "\"process_tpages\": true,", - "\"process_tpages\": false,", + InstallDir / "data" / "decompiler" / "config" / "jak1_ntsc_black_label.jsonc", + "\"process_tpages\": true,", + "\"process_tpages\": false,", ) replaceText( - InstallDir/"data"/"decompiler"/"config"/"jak1_pal.jsonc", - "\"process_tpages\": true,", - "\"process_tpages\": false,", + InstallDir / "data" / "decompiler" / "config" / "jak1_pal.jsonc", + "\"process_tpages\": true,", + "\"process_tpages\": false,", ) + +# TODO def rebuild(URL, MOD_ID, MOD_NAME, LINK_TYPE, GAME, should_extract): - InstallDir = ModFolderPATH/MOD_ID - UniversalIsoPath = AppdataPATH/"OpenGOAL-Mods"/"_iso_data" + InstallDir = ModFolderPATH / MOD_ID + UniversalIsoPath = AppdataPATH / "OpenGOAL-Mods" / "_iso_data" - print(f"Looking for some ISO data in {UniversalIsoPath/GAME}") - found_universal_iso = exists(UniversalIsoPath/GAME/"Z6TAIL.DUP") + print(f"Looking for some ISO data in {UniversalIsoPath / GAME}") + found_universal_iso = exists(UniversalIsoPath / GAME / "Z6TAIL.DUP") - #if ISO_DATA has content, store this path to pass to the extractor + # if ISO_DATA has content, store this path to pass to the extractor if found_universal_iso: print("We found ISO data from a previous mod installation! Lets use it!") - print(f"Found in {UniversalIsoPath/GAME/'Z6TAIL.DUP'}") - iso_path = UniversalIsoPath/GAME - - if not is_junction(InstallDir/"data"/"iso_data"): - # we have iso extracted to universal folder already, just symlink it. otherwise we'll copy it there and symlink after extractor closes - try_remove_dir(InstallDir/"data"/"iso_data") - makeDirSymlink(InstallDir/"data"/"iso_data", UniversalIsoPath) + print(f"Found in {UniversalIsoPath / GAME / 'Z6TAIL.DUP'}") + iso_path = UniversalIsoPath / GAME + + if not is_junction(InstallDir / "data" / "iso_data"): + # we have iso extracted to universal folder already, just symlink it. otherwise we'll copy it there and symlink after extractor closes + try_remove_dir(InstallDir / "data" / "iso_data") + makeDirSymlink(InstallDir / "data" / "iso_data", UniversalIsoPath) else: - print("We did not find " + GAME + " ISO data from a previous mod, lets ask for some!") - + print("We did not find " + GAME + " ISO data from a previous mod, lets ask for some!") + # cleanup and remove a corrupted iso - if os.path.exists(UniversalIsoPath/GAME) and os.path.isdir(UniversalIsoPath) and not (exists((UniversalIsoPath/GAME/"Z6TAIL.DUP"))): + if os.path.exists(UniversalIsoPath / GAME) and os.path.isdir(UniversalIsoPath) and not ( + exists((UniversalIsoPath / GAME / "Z6TAIL.DUP"))): print("Removing corrupted iso destination...") - shutil.rmtree(UniversalIsoPath/GAME) + shutil.rmtree(UniversalIsoPath / GAME) ensure_jak_folders_exist() - + # prompt for their ISO and store its path root = tk.Tk() prompt = "Please select your " + GAME + " ISO" @@ -451,8 +478,8 @@ def rebuild(URL, MOD_ID, MOD_NAME, LINK_TYPE, GAME, should_extract): iso_path = filedialog.askopenfilename(parent=root, title=prompt) root.destroy() if iso_path == "": - print("user closed popup") - return + print("user closed popup") + return if pathlib.Path(iso_path).is_file: if not (pathlib.Path(iso_path).suffix).lower() == ".iso": print("yo, this is not an ISO: " + (pathlib.Path(iso_path).suffix).lower()) @@ -462,22 +489,22 @@ def rebuild(URL, MOD_ID, MOD_NAME, LINK_TYPE, GAME, should_extract): try_kill_process(gk_exe) try_kill_process(goalc_exe) print("Done update starting extractor This one can take a few moments! \n") - - #Extract and compile + + # Extract and compile if GAME == "jak1": extractor_command_list = [InstallDir / extractor_exe, "-f", iso_path, "-v", "-c"] if should_extract: extractor_command_list.append("-e") extractor_command_list.append("-d") print(extractor_command_list) - extractor_result = subprocess.run(extractor_command_list, shell=True, cwd=os.path.abspath(InstallDir) ) + extractor_result = subprocess.run(extractor_command_list, shell=True, cwd=os.path.abspath(InstallDir)) if extractor_result.returncode == 0: print("done extracting!") else: print("Extractor error!") return - + elif GAME == "jak2": extractor_command_list = [InstallDir / extractor_exe, "-f", iso_path, "-v", "-c", "-g", "jak2"] if should_extract: @@ -500,14 +527,16 @@ def rebuild(URL, MOD_ID, MOD_NAME, LINK_TYPE, GAME, should_extract): # move the extrated contents to the universal launchers directory for next time. if not found_universal_iso: ensure_jak_folders_exist() - moveDirContents(InstallDir/"data"/"iso_data"/GAME, UniversalIsoPath/GAME) + moveDirContents(InstallDir / "data" / "iso_data" / GAME, UniversalIsoPath / GAME) # replace iso_data with symlink - try_remove_dir(InstallDir/"data"/"iso_data") - makeDirSymlink(InstallDir/"data"/"iso_data", UniversalIsoPath) - + try_remove_dir(InstallDir / "data" / "iso_data") + makeDirSymlink(InstallDir / "data" / "iso_data", UniversalIsoPath) + launch_local(MOD_ID, GAME) return + +# TODO def update_and_launch(URL, MOD_ID, MOD_NAME, LINK_TYPE, GAME): if URL is None: return @@ -516,7 +545,7 @@ def update_and_launch(URL, MOD_ID, MOD_NAME, LINK_TYPE, GAME): # Github API Call launchUrl = URL if LINK_TYPE == githubUtils.LinkTypes.BRANCH: - launchUrl = githubUtils.branchToApiURL(URL) + launchUrl = githubUtils.branchToApiURL(URL) LatestRelAssetsURL = "" print("\nlaunching from " + launchUrl) @@ -524,69 +553,70 @@ def update_and_launch(URL, MOD_ID, MOD_NAME, LINK_TYPE, GAME): r = json.loads(json.dumps(requests.get(url=launchUrl, params=PARAMS).json())) # paths - InstallDir = ModFolderPATH/MOD_ID - UniversalIsoPath = AppdataPATH/"OpenGOAL-Mods"/"_iso_data" + InstallDir = ModFolderPATH / MOD_ID + UniversalIsoPath = AppdataPATH / "OpenGOAL-Mods" / "_iso_data" ensure_jak_folders_exist() # store Latest Release and check our local date too. if LINK_TYPE == githubUtils.LinkTypes.BRANCH: - LatestRel = datetime.strptime( - r.get("commit") - .get("commit") - .get("author") - .get("date") - .replace("T", " ") - .replace("Z", ""), - "%Y-%m-%d %H:%M:%S", - ) - LatestRelAssetsURL = githubUtils.branchToArchiveURL(URL) + LatestRel = datetime.strptime( + r.get("commit") + .get("commit") + .get("author") + .get("date") + .replace("T", " ") + .replace("Z", ""), + "%Y-%m-%d %H:%M:%S", + ) + LatestRelAssetsURL = githubUtils.branchToArchiveURL(URL) elif LINK_TYPE == githubUtils.LinkTypes.RELEASE: - LatestRel = datetime.strptime( - r[0].get("published_at").replace("T", " ").replace("Z", ""), - "%Y-%m-%d %H:%M:%S", - ) - assets = json.loads(json.dumps(requests.get(url=r[0].get("assets_url"), params=PARAMS).json())) - for asset in assets: - # TODO: fork here based on sys.platform - if "linux" in asset.get("name") or "macos" in asset.get("name") or "json" in asset.get("name"): - print("Release asset " + asset.get("name") + " is not for windows - SKIPPING!") - else: - print("USING: Release asset " + asset.get("name") + "! Downloading from " + asset.get("browser_download_url")) - LatestRelAssetsURL = asset.get("browser_download_url") - break - - # response = requests.get(url=LatestRelAssetsURL, params=PARAMS) - # content_type = response.headers["content-type"] + LatestRel = datetime.strptime( + r[0].get("published_at").replace("T", " ").replace("Z", ""), + "%Y-%m-%d %H:%M:%S", + ) + assets = json.loads(json.dumps(requests.get(url=r[0].get("assets_url"), params=PARAMS).json())) + for asset in assets: + # TODO: fork here based on sys.platform + if "linux" in asset.get("name") or "macos" in asset.get("name") or "json" in asset.get("name"): + print("Release asset " + asset.get("name") + " is not for windows - SKIPPING!") + else: + print("USING: Release asset " + asset.get("name") + "! Downloading from " + asset.get( + "browser_download_url")) + LatestRelAssetsURL = asset.get("browser_download_url") + break + + # response = requests.get(url=LatestRelAssetsURL, params=PARAMS) + # content_type = response.headers["content-type"] LastWrite = datetime(2020, 5, 17) - exe_path = InstallDir/ExecutableName + exe_path = InstallDir / ExecutableName if exe_path.exists(): LastWrite = datetime.utcfromtimestamp(exe_path.stat().st_mtime) # update checks Outdated = bool(LastWrite < LatestRel) - NotExtracted = bool(not (exists(UniversalIsoPath/GAME/"Z6TAIL.DUP"))) - NotCompiled = bool(not (exists(InstallDir/"data"/"out"/GAME/"fr3"/"GAME.fr3"))) + NotExtracted = bool(not (exists(UniversalIsoPath / GAME / "Z6TAIL.DUP"))) + NotCompiled = bool(not (exists(InstallDir / "data" / "out" / GAME / "fr3" / "GAME.fr3"))) needUpdate = bool(Outdated or NotExtracted or NotCompiled) print("Currently installed version created on: " + LastWrite.strftime('%Y-%m-%d %H:%M:%S')) print("Newest version created on: " + LatestRel.strftime('%Y-%m-%d %H:%M:%S')) - if(NotExtracted): - print(f"Error! Iso data does not appear to be extracted to {UniversalIsoPath/GAME/'Z6TAIL.DUP'}") + if (NotExtracted): + print(f"Error! Iso data does not appear to be extracted to {UniversalIsoPath / GAME / 'Z6TAIL.DUP'}") print("Will ask user to provide ISO") - if(NotCompiled): + if (NotCompiled): print("Error! The game is not compiled") - if((LastWrite < LatestRel)): + if ((LastWrite < LatestRel)): print("Looks like we need to download a new update!") print(LastWrite) print(LatestRel) print("Is newest posted update older than what we have installed? " + str((LastWrite < LatestRel))) # attempt to migrate any old settings files from using MOD_NAME to MOD_ID - mod_name_settings_path = AppdataPATH/"OpenGOAL"/GAME/"settings"/(MOD_NAME+"-settings.gc") + mod_name_settings_path = AppdataPATH / "OpenGOAL" / GAME / "settings" / (MOD_NAME + "-settings.gc") if mod_name_settings_path.exists(): # just to be safe delete the migrated settings file if it already exists (shouldn't happen but prevents rename from failing below) - mod_id_settings_path = AppdataPATH/"OpenGOAL"/GAME/"settings"/(MOD_ID+"-settings.gc") + mod_id_settings_path = AppdataPATH / "OpenGOAL" / GAME / "settings" / (MOD_ID + "-settings.gc") if mod_id_settings_path.exists(): mod_id_settings_path.unlink() @@ -595,12 +625,12 @@ def update_and_launch(URL, MOD_ID, MOD_NAME, LINK_TYPE, GAME): # force update to ensure we recompile with adjusted settings filename in pckernel.gc needUpdate = True - + if needUpdate: download_and_unpack_mod(URL, MOD_ID, MOD_NAME, LINK_TYPE, InstallDir, LatestRelAssetsURL) rebuild(URL, MOD_ID, MOD_NAME, LINK_TYPE, GAME, True) else: - # dont need to update, close any open instances of the game and just launch it - print("Game is up to date!") - print("Launching now!") - launch_local(MOD_ID, GAME) + # dont need to update, close any open instances of the game and just launch it + print("Game is up to date!") + print("Launching now!") + launch_local(MOD_ID, GAME) From 025befd158d69a44288d790c5db7d7c7416f580d Mon Sep 17 00:00:00 2001 From: Steven Lares <13951178+StevenLares@users.noreply.github.com> Date: Wed, 21 Feb 2024 17:27:59 -0500 Subject: [PATCH 07/17] Removed lines that created a mod directory on the filesystem that was never used. launcherUtils.py now makes use of file path variables declared at the top, removes some file path hardcoding --- utils/launcherUtils.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/utils/launcherUtils.py b/utils/launcherUtils.py index 7d5a8d7..aae6590 100644 --- a/utils/launcherUtils.py +++ b/utils/launcherUtils.py @@ -53,9 +53,10 @@ def get_exe(executable): UPDATE_FILE_EXTENTION ) FileIdent = "" # If we ever get to multiple .zip files in a release, include other identifying information from the name -dirs = AppDirs(roaming=True) -ModFolderPATH = Path(dirs.user_data_dir) / "OpenGOAL-Mods" -AppdataPATH = Path(dirs.user_data_dir) + +AppdataPATH = Path(AppDirs(roaming=True).user_data_dir) +ModFolderPATH = AppdataPATH / "OpenGOAL-Mods" +ISO_PATH = ModFolderPATH / "_iso_data" pbar = None @@ -201,10 +202,6 @@ def link_files_by_extension(source_dir, destination_dir): def openFolder(path): - jak2_path = Path(dirs.user_data_dir) / "OpenGOAL" / "mods" / "data" / "iso_data" / "jak2" - if not jak2_path.exists(): - jak2_path.mkdir(parents=True) - print(path) if sys.platform == "win32": os.startfile(path) else: @@ -264,7 +261,7 @@ def divide_by_zero(): def ensure_jak_folders_exist(): - directory = Path(dirs.user_data_dir) / "OpenGOAL-Mods" / "_iso_data" + directory = ISO_PATH jak1_path = os.path.join(directory, "jak1") jak2_path = os.path.join(directory, "jak2") @@ -443,7 +440,7 @@ def download_and_unpack_mod(URL, MOD_ID, MOD_NAME, LINK_TYPE, InstallDir, Latest # TODO def rebuild(URL, MOD_ID, MOD_NAME, LINK_TYPE, GAME, should_extract): InstallDir = ModFolderPATH / MOD_ID - UniversalIsoPath = AppdataPATH / "OpenGOAL-Mods" / "_iso_data" + UniversalIsoPath = ISO_PATH print(f"Looking for some ISO data in {UniversalIsoPath / GAME}") found_universal_iso = exists(UniversalIsoPath / GAME / "Z6TAIL.DUP") @@ -554,7 +551,7 @@ def update_and_launch(URL, MOD_ID, MOD_NAME, LINK_TYPE, GAME): # paths InstallDir = ModFolderPATH / MOD_ID - UniversalIsoPath = AppdataPATH / "OpenGOAL-Mods" / "_iso_data" + UniversalIsoPath = ISO_PATH ensure_jak_folders_exist() # store Latest Release and check our local date too. From 75ff8efa46ace23a3d72cb122d342ade6e569f69 Mon Sep 17 00:00:00 2001 From: Steven Lares <13951178+StevenLares@users.noreply.github.com> Date: Thu, 22 Feb 2024 11:40:26 -0500 Subject: [PATCH 08/17] Fixed bug where the use of pathlib Path caused a crash with 'launcher with autoupdater.py' on both Linux and Windows --- Launcher with autoupdater.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Launcher with autoupdater.py b/Launcher with autoupdater.py index 6820add..3ff5a8f 100644 --- a/Launcher with autoupdater.py +++ b/Launcher with autoupdater.py @@ -144,6 +144,6 @@ def download_newest_mod(): download_newest_mod() elif event == "launch_button": window.close() - subprocess.call([AppdataPATH/OpengoalModLauncher_exe]) + subprocess.call([str(AppdataPATH/OpengoalModLauncher_exe)]) window.close() From ca63a7d57f5764b5ab287b26d68dc3e6e0f1cb4f Mon Sep 17 00:00:00 2001 From: Steven Lares <13951178+StevenLares@users.noreply.github.com> Date: Sat, 24 Feb 2024 15:16:21 -0500 Subject: [PATCH 09/17] Changed some os path functions to pathlib builtin functions. Also, moved temp_dir earlier to top of code block. It is used instead of hardcoding --- Launcher with autoupdater.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Launcher with autoupdater.py b/Launcher with autoupdater.py index 3ff5a8f..1c3311d 100644 --- a/Launcher with autoupdater.py +++ b/Launcher with autoupdater.py @@ -95,14 +95,16 @@ def download_newest_mod(): need_update = bool((last_write < latest_release)) if need_update: + temp_dir = AppdataPATH / "temp" + window['update_status'].update("Starting Update...") - try_remove_dir(AppdataPATH/"temp") - if not os.path.exists(AppdataPATH/"temp"): - os.makedirs(AppdataPATH/"temp") + try_remove_dir(temp_dir) + if not temp_dir.exists(): + temp_dir.mkdir() window['update_status'].update("Downloading update from " + latest_release_assets_url) file = urllib.request.urlopen(latest_release_assets_url) - urllib.request.urlretrieve(latest_release_assets_url, AppdataPATH/"temp"/OpengoalModLauncher_exe, show_progress) + urllib.request.urlretrieve(latest_release_assets_url, temp_dir / OpengoalModLauncher_exe, show_progress) window['update_status'].update("Done downloading") window['update_status'].update(f"Removing previous installation {AppdataPATH}") @@ -112,7 +114,7 @@ def download_newest_mod(): try_remove_file(AppdataPATH/extractor_exe) window['update_status'].update("Extracting update") - temp_dir = AppdataPATH/"temp" + try_remove_file(temp_dir/"updateDATA.zip") sub_dir = temp_dir all_files = os.listdir(sub_dir) From c44b2003bb1f065631cc76e1a02734c3b1768468 Mon Sep 17 00:00:00 2001 From: Steven Lares <13951178+StevenLares@users.noreply.github.com> Date: Sat, 24 Feb 2024 15:52:26 -0500 Subject: [PATCH 10/17] Renamed AppdataPATH to LauncherInstallPath --- Launcher with autoupdater.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/Launcher with autoupdater.py b/Launcher with autoupdater.py index 1c3311d..dad6ae5 100644 --- a/Launcher with autoupdater.py +++ b/Launcher with autoupdater.py @@ -13,7 +13,7 @@ from utils import launcherUtils dirs = AppDirs(roaming=True) -AppdataPATH = Path(dirs.user_data_dir)/"OpenGOAL-UnofficialModLauncher" +LauncherInstallPATH = Path(dirs.user_data_dir) / "OpenGOAL-UnofficialModLauncher" OpengoalModLauncher_exe = launcherUtils.get_exe("OpengoalModLauncher") gk_exe = launcherUtils.get_exe("gk") @@ -55,8 +55,8 @@ def check_for_updates(): latest_release_assets_url = "https://github.com/OpenGOAL-Unofficial-Mods/OpenGoal-ModLauncher-dev/releases/download/latest/"+ OpengoalModLauncher_exe last_write = datetime(2020, 5, 17) - if (AppdataPATH/OpengoalModLauncher_exe).exists(): - last_write = datetime.utcfromtimestamp((AppdataPATH/OpengoalModLauncher_exe).stat().st_mtime) + if (LauncherInstallPATH / OpengoalModLauncher_exe).exists(): + last_write = datetime.utcfromtimestamp((LauncherInstallPATH / OpengoalModLauncher_exe).stat().st_mtime) need_update = bool((last_write < latest_release)) @@ -89,13 +89,13 @@ def download_newest_mod(): latest_release_assets_url = "https://github.com/OpenGOAL-Unofficial-Mods/OpenGoal-ModLauncher-dev/releases/download/latest/"+ OpengoalModLauncher_exe last_write = datetime(2020, 5, 17) - if (AppdataPATH/OpengoalModLauncher_exe).exists(): - last_write = datetime.utcfromtimestamp((AppdataPATH/OpengoalModLauncher_exe).stat().st_mtime) + if (LauncherInstallPATH / OpengoalModLauncher_exe).exists(): + last_write = datetime.utcfromtimestamp((LauncherInstallPATH / OpengoalModLauncher_exe).stat().st_mtime) need_update = bool((last_write < latest_release)) if need_update: - temp_dir = AppdataPATH / "temp" + temp_dir = LauncherInstallPATH / "temp" window['update_status'].update("Starting Update...") try_remove_dir(temp_dir) @@ -107,11 +107,11 @@ def download_newest_mod(): urllib.request.urlretrieve(latest_release_assets_url, temp_dir / OpengoalModLauncher_exe, show_progress) window['update_status'].update("Done downloading") - window['update_status'].update(f"Removing previous installation {AppdataPATH}") - try_remove_dir(AppdataPATH/"data") - try_remove_file(AppdataPATH/gk_exe) - try_remove_file(AppdataPATH/goalc_exe) - try_remove_file(AppdataPATH/extractor_exe) + window['update_status'].update(f"Removing previous installation {LauncherInstallPATH}") + try_remove_dir(LauncherInstallPATH / "data") + try_remove_file(LauncherInstallPATH / gk_exe) + try_remove_file(LauncherInstallPATH / goalc_exe) + try_remove_file(LauncherInstallPATH / extractor_exe) window['update_status'].update("Extracting update") @@ -119,7 +119,7 @@ def download_newest_mod(): sub_dir = temp_dir all_files = os.listdir(sub_dir) for f in all_files: - shutil.move(sub_dir/f, AppdataPATH/f) + shutil.move(sub_dir / f, LauncherInstallPATH / f) try_remove_dir(temp_dir) window['update_status'].update("Update complete") window['update_button'].update(visible=False) @@ -146,6 +146,6 @@ def download_newest_mod(): download_newest_mod() elif event == "launch_button": window.close() - subprocess.call([str(AppdataPATH/OpengoalModLauncher_exe)]) + subprocess.call([str(LauncherInstallPATH / OpengoalModLauncher_exe)]) window.close() From 781524c6b0780e4e44ee7714f90f608f633f1b71 Mon Sep 17 00:00:00 2001 From: Steven Lares <13951178+StevenLares@users.noreply.github.com> Date: Sat, 24 Feb 2024 16:53:36 -0500 Subject: [PATCH 11/17] Moved redundant code regarding checking for opengoal mod launcher updates, found in both check_for_updates() and download_newest_mod(). Also, I broke up the code into several reusable functions that are each responsible for one task only. Finally, I added a TODO that describes a future code improvement needed for Linux compatibility, which should be easier to implement and debug with this new code structure --- Launcher with autoupdater.py | 65 ++++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 29 deletions(-) diff --git a/Launcher with autoupdater.py b/Launcher with autoupdater.py index dad6ae5..094d7a0 100644 --- a/Launcher with autoupdater.py +++ b/Launcher with autoupdater.py @@ -21,6 +21,7 @@ goalc_exe = launcherUtils.get_exe("goalc") extractor_exe = launcherUtils.get_exe("extractor") + def show_progress(block_num, block_size, total_size): if total_size > 0: try: @@ -28,17 +29,25 @@ def show_progress(block_num, block_size, total_size): except Exception as e: pass # Handle the exception if the window or element does not exist + def try_remove_file(file): if exists(file): os.remove(file) + def try_remove_dir(dir): if exists(dir): shutil.rmtree(dir) -def check_for_updates(): - +# TODO: +# The logic will need to be adjusted to use different criteria to return the latest executable +# with regards to the user's platform. +# Currently, it determines the executable using only the latest release at the launch_url +# (assuming no network error, otherwise it goes into the else statement) +# However, at the moment this leads to only the Windows exe being chosen and latest_release_assets_url gets +# assigned accordingly +def get_latest_release_info(): launch_url = "https://api.github.com/repos/OpenGOAL-Unofficial-Mods/OpenGoal-ModLauncher-dev/releases" response = requests.get(url=launch_url, params={'address': "yolo"}) @@ -52,18 +61,31 @@ def check_for_updates(): else: print("WARNING: Failed to query GitHub API, you might be rate-limited. Using default fallback release instead.") latest_release = datetime(2023, 7, 23) - latest_release_assets_url = "https://github.com/OpenGOAL-Unofficial-Mods/OpenGoal-ModLauncher-dev/releases/download/latest/"+ OpengoalModLauncher_exe + latest_release_assets_url = "https://github.com/OpenGOAL-Unofficial-Mods/OpenGoal-ModLauncher-dev/releases/download/latest/" + OpengoalModLauncher_exe + return latest_release_assets_url, latest_release + + +def get_latest_write_date(): last_write = datetime(2020, 5, 17) if (LauncherInstallPATH / OpengoalModLauncher_exe).exists(): last_write = datetime.utcfromtimestamp((LauncherInstallPATH / OpengoalModLauncher_exe).stat().st_mtime) + return last_write + + +def need_update(last_write, latest_release): + return bool((last_write < latest_release)) - need_update = bool((last_write < latest_release)) - window['installed_version'].update(f"Currently installed version created on: {last_write.strftime('%Y-%m-%d %H:%M:%S')}") +def check_for_updates(): + latest_release_assets_url, latest_release = get_latest_release_info() + last_write = get_latest_write_date() + + window['installed_version'].update( + f"Currently installed version created on: {last_write.strftime('%Y-%m-%d %H:%M:%S')}") window['newest_version'].update(f"Newest version created on: {latest_release.strftime('%Y-%m-%d %H:%M:%S')}") - if need_update: + if need_update(last_write, latest_release): window['update_status'].update("An update is available. Click 'Update' to install.") window['update_button'].update(visible=True) window['launch_button'].update(visible=False) @@ -72,29 +94,12 @@ def check_for_updates(): window['update_button'].update(visible=False) window['launch_button'].update(visible=True) -def download_newest_mod(): - launch_url = "https://api.github.com/repos/OpenGOAL-Unofficial-Mods/OpenGoal-ModLauncher-dev/releases" - response = requests.get(url=launch_url, params={'address': "yolo"}) - - if response is not None and response.status_code == 200: - r = json.loads(json.dumps(response.json())) - latest_release = datetime.strptime(r[0].get("published_at").replace("T", " ").replace("Z", ""), - '%Y-%m-%d %H:%M:%S') - latest_release_assets_url = (json.loads( - json.dumps(requests.get(url=r[0].get("assets_url"), params={'address': "yolo"}).json())))[0].get( - "browser_download_url") - else: - print("WARNING: Failed to query GitHub API, you might be rate-limited. Using default fallback release instead.") - latest_release = datetime(2023, 7, 23) - latest_release_assets_url = "https://github.com/OpenGOAL-Unofficial-Mods/OpenGoal-ModLauncher-dev/releases/download/latest/"+ OpengoalModLauncher_exe - - last_write = datetime(2020, 5, 17) - if (LauncherInstallPATH / OpengoalModLauncher_exe).exists(): - last_write = datetime.utcfromtimestamp((LauncherInstallPATH / OpengoalModLauncher_exe).stat().st_mtime) - need_update = bool((last_write < latest_release)) +def download_newest_mod(): + latest_release_assets_url, latest_release = get_latest_release_info() + last_write = get_latest_write_date() - if need_update: + if need_update(last_write, latest_release): temp_dir = LauncherInstallPATH / "temp" window['update_status'].update("Starting Update...") @@ -115,7 +120,7 @@ def download_newest_mod(): window['update_status'].update("Extracting update") - try_remove_file(temp_dir/"updateDATA.zip") + try_remove_file(temp_dir / "updateDATA.zip") sub_dir = temp_dir all_files = os.listdir(sub_dir) for f in all_files: @@ -125,13 +130,15 @@ def download_newest_mod(): window['update_button'].update(visible=False) window['launch_button'].update(visible=True) + layout = [ [sg.Text("OpenGOAL Mod Updater", font=("Helvetica", 16))], [sg.Text("Installed Version:", size=(20, 1)), sg.Text("", size=(20, 1), key='installed_version')], [sg.Text("Newest Version:", size=(20, 1)), sg.Text("", size=(20, 1), key='newest_version')], [sg.ProgressBar(100, orientation='h', size=(20, 20), key='progress_bar')], [sg.Text("", size=(40, 1), key='update_status')], - [sg.Button("Check for Updates"), sg.Button("Update", visible=False, key='update_button'), sg.Button("Launch", visible=False, key='launch_button'), sg.Button("Exit")] + [sg.Button("Check for Updates"), sg.Button("Update", visible=False, key='update_button'), + sg.Button("Launch", visible=False, key='launch_button'), sg.Button("Exit")] ] window = sg.Window("OpenGOAL Mod Updater", layout, finalize=True) From 2ffcc36501ead1b8792a159198e4f679f7fd7c91 Mon Sep 17 00:00:00 2001 From: Steven Lares <13951178+StevenLares@users.noreply.github.com> Date: Thu, 22 Feb 2024 15:14:17 -0500 Subject: [PATCH 12/17] Moved pysimplegui layout code to its own utility class, layoutUtils. layoutUtils has one public method, generate(), along with several private helper methods. My goals with this commit were to: Separate layout code from the main code. This improves readability for both sets of code. Break up the layout code into separate functions and variables so that it is easier to read and modify. Assemble the layout elements so that they resemble the actual UI, as it is suggested in the pysimplegui docs. Read the returns for side_panel(), main_panel(), and main_panel()'s variable button_panel_with_mod_info for this idea in action. --- openGOALModLauncher.py | 255 +---------------------------------------- utils/layoutUtils.py | 207 +++++++++++++++++++++++++++++++++ 2 files changed, 209 insertions(+), 253 deletions(-) create mode 100644 utils/layoutUtils.py diff --git a/openGOALModLauncher.py b/openGOALModLauncher.py index 626a370..025bf99 100644 --- a/openGOALModLauncher.py +++ b/openGOALModLauncher.py @@ -8,7 +8,7 @@ import threading from PIL import Image -from utils import launcherUtils, githubUtils +from utils import launcherUtils, githubUtils, layoutUtils import PySimpleGUI as sg import cloudscraper import io @@ -365,258 +365,7 @@ def getRefreshedTableData(sort_col_idx): LATEST_TABLE_DATA = [] # ----- Full layout ----- -layout = [ - [ - sg.Frame( - title="", - key="-SPLASHFRAME-", - border_width=0, # Set border_width to 0 - visible=True, - element_justification="center", - vertical_alignment="center", - layout=[ - [ - sg.Image( - key="-SPLASHIMAGE-", - source=githubUtils.resize_image(splashfile, 970, 607), - pad=(0, 0), # Set padding to 0 - expand_x=True, - expand_y=True, - ) - ] - ], - ) - ], - [ - sg.Frame( - title="", - key="-LOADINGFRAME-", - border_width=0, # Set border_width to 0 - visible=False, - element_justification="center", - vertical_alignment="center", - layout=[ - [ - sg.Image( - key="-LOADINGIMAGE-", - source=githubUtils.resize_image(loadingimage, 970, 607), - pad=(0, 0), # Set padding to 0 - expand_x=True, - expand_y=True, - ) - ] - ], - ) - ], - [ - sg.Frame( - title="", - key="-MAINFRAME-", - border_width=0, - visible=False, - layout=[ - [ - sg.Column( # nav sidebar - [ - [sg.Text("JAK 1", font=("Helvetica", 16, "bold"))], - [ - sg.Radio( - "Mods", - "filter", - font=("Helvetica", 12), - enable_events=True, - key="jak1/mods", - default=True, - ) - ], - [ - sg.Radio( - "Texture Packs", - "filter", - enable_events=True, - font=("Helvetica", 12), - key="jak1/tex", - ) - ], - [sg.Text("")], - [sg.Text("JAK 2", font=("Helvetica", 16, "bold"))], - [ - sg.Radio( - "Mods", - "filter", - font=("Helvetica", 12), - enable_events=True, - key="jak2/mods", - ) - ], - [ - sg.Radio( - "Texture Packs", - "filter", - font=("Helvetica", 12), - enable_events=True, - key="jak2/tex", - ) - ], - [sg.VPush()], - [ - sg.Btn( - button_text="View iso_data Folder", - key="-VIEWISOFOLDER-", - expand_x=True, - ) - ], - [ - sg.Btn( - button_text="jakmods.dev", - key="-JAKMODSWEB-", - expand_x=True, - ) - ], - ], - expand_y=True, - ), - sg.VerticalSeparator(), - sg.Column( - [ - [ - sg.Column( - [ - [ - sg.Text( - "", - key="-SELECTEDMODNAME-", - font=("Helvetica", 13), - metadata={"id": "", "url": ""}, - ) - ], - [ - sg.Text( - "", - key="-SELECTEDMODDESC-", - size=(45, 7), - ) - ], - [sg.Text("Tags:", key="-SELECTEDMODTAGS-")], - [ - sg.Text( - "Contributors:", - key="-SELECTEDMODCONTRIBUTORS-", - ) - ], - [sg.Text("")], - [ - sg.Btn( - button_text="Launch", - key="-LAUNCH-", - expand_x=True, - ), - sg.Btn( - button_text="Re-extract", - key="-REEXTRACT-", - expand_x=True, - ), - sg.Btn( - button_text="Recompile", - key="-RECOMPILE-", - expand_x=True, - ), - sg.Btn( - button_text="Uninstall", - key="-UNINSTALL-", - expand_x=True, - ), - ], - [ - sg.Btn( - button_text="View Folder", - key="-VIEWFOLDER-", - expand_x=True, - ), - sg.Btn( - button_text="Website", - key="-WEBSITE-", - expand_x=True, - metadata={"url": ""}, - ), - sg.Btn( - button_text="Video(s)", - key="-VIDEOS-", - expand_x=True, - metadata={"url": ""}, - ), - # sg.Btn( - # button_text="Photo(s)", - # key="-PHOTOS-", - # expand_x=True, - # metadata={"url": ""}, - # ), - ], - ], - size=(200, 300), - expand_x=True, - expand_y=True, - ), - sg.Frame( - title="", - element_justification="center", - vertical_alignment="center", - border_width=0, - layout=[ - [ - sg.Image( - key="-SELECTEDMODIMAGE-", expand_y=True - ) - ] - ], - size=(450, 300), - ), - ], - [sg.HorizontalSeparator()], - [ - sg.Text("Search"), - sg.Input( - expand_x=True, enable_events=True, key="-FILTER-" - ), - sg.Checkbox( - text="Show Installed", - default=True, - enable_events=True, - key="-SHOWINSTALLED-", - ), - sg.Checkbox( - text="Show Uninstalled", - default=True, - enable_events=True, - key="-SHOWUNINSTALLED-", - ), - ], - [ - sg.Table( - values=LATEST_TABLE_DATA, - headings=table_headings, - visible_column_map=col_vis, - col_widths=col_width, - auto_size_columns=False, - num_rows=15, - text_color="black", - background_color="lightblue", - alternating_row_color="white", - justification="left", - selected_row_colors="black on yellow", - key="-MODTABLE-", - expand_x=True, - expand_y=True, - enable_click_events=True, - ) - ], - ] - ), - ] - ], - ) - ], -] +layout = layoutUtils.generate(splashfile, loadingimage, LATEST_TABLE_DATA, table_headings, col_vis, col_width) window = sg.Window( "OpenGOAL Mod Launcher", layout, icon=iconfile, border_depth=0, finalize=True diff --git a/utils/layoutUtils.py b/utils/layoutUtils.py new file mode 100644 index 0000000..758a23e --- /dev/null +++ b/utils/layoutUtils.py @@ -0,0 +1,207 @@ +import PySimpleGUI as sg +from utils import githubUtils + + +def generate(splashfile, loadingimage, LATEST_TABLE_DATA, table_headings, col_vis, col_width): + return \ + [ + [ + sg.Frame( + title="", + key="-SPLASHFRAME-", + border_width=0, # Set border_width to 0 + visible=True, + element_justification="center", + vertical_alignment="center", + layout=[ + [ + sg.Image( + key="-SPLASHIMAGE-", + source=githubUtils.resize_image(splashfile, 970, 607), + pad=(0, 0), # Set padding to 0 + expand_x=True, + expand_y=True, + ) + ] + ], + ) + ], + [ + sg.Frame( + title="", + key="-LOADINGFRAME-", + border_width=0, # Set border_width to 0 + visible=False, + element_justification="center", + vertical_alignment="center", + layout=[ + [ + sg.Image( + key="-LOADINGIMAGE-", + source=githubUtils.resize_image(loadingimage, 970, 607), + pad=(0, 0), # Set padding to 0 + expand_x=True, + expand_y=True, + ) + ] + ], + ) + ], + [ + sg.Frame( + title="", + key="-MAINFRAME-", + border_width=0, + visible=False, + layout=[ + [ + __side_panel(), + sg.VerticalSeparator(), + __main_panel(LATEST_TABLE_DATA, table_headings, col_vis, col_width) + ] + ] + ) + ], + ] + + +def __side_panel(): + return sg.Column( # nav sidebar + [ + [sg.Text("JAK 1", font=("Helvetica", 16, "bold"))], + [__default_radio("Mods", "filter", "jak1/mods")], + [__radio("Texture Packs", "filter", "jak1/tex")], + + [sg.Text("")], + + [sg.Text("JAK 2", font=("Helvetica", 16, "bold"))], + [__radio("Mods", "filter", "jak2/mods")], + [__radio("Texture Packs", "filter", "jak2/tex")], + + [sg.VPush()], + + [__button("View iso_data Folder", "-VIEWISOFOLDER-")], + [__button("jakmods.dev", "-JAKMODSWEB-")], + ], + expand_y=True, + ) + + +def __main_panel(LATEST_TABLE_DATA, table_headings, col_vis, col_width): + install_filter = sg.Checkbox( + text="Show Installed", + default=True, + enable_events=True, + key="-SHOWINSTALLED-", + ) + + uninstall_filter = sg.Checkbox( + text="Show Uninstalled", + default=True, + enable_events=True, + key="-SHOWUNINSTALLED-", + ) + + search_bar = sg.Input( + expand_x=True, + enable_events=True, + key="-FILTER-" + ) + + mod_table = sg.Table( + values=LATEST_TABLE_DATA, + headings=table_headings, + visible_column_map=col_vis, + col_widths=col_width, + auto_size_columns=False, + num_rows=15, + text_color="black", + background_color="lightblue", + alternating_row_color="white", + justification="left", + selected_row_colors="black on yellow", + key="-MODTABLE-", + expand_x=True, + expand_y=True, + enable_click_events=True + ) + + mod_image = sg.Frame( + title="", + element_justification="center", + vertical_alignment="center", + border_width=0, + layout=[ + [ + sg.Image( + key="-SELECTEDMODIMAGE-", expand_y=True + ) + ] + ], + size=(450, 300), + ) + + button_panel_with_mod_info = sg.Column( + [ + [sg.Text("", key="-SELECTEDMODNAME-", font=("Helvetica", 13), metadata={"id": "", "url": ""})], + [sg.Text("", key="-SELECTEDMODDESC-", size=(45, 7))], + [sg.Text("Tags:", key="-SELECTEDMODTAGS-")], + [sg.Text("Contributors:", key="-SELECTEDMODCONTRIBUTORS-")], + [sg.Text("")], + [__button("Launch", "-LAUNCH-"), __button("Re-extract", "-REEXTRACT-"), + __button("Recompile", "-RECOMPILE-"), __button("Uninstall", "-UNINSTALL-")], + [__button("View Folder", "-VIEWFOLDER-"), __button_with_metadata("Website", "-WEBSITE-"), + __button_with_metadata("Video(s)", "-VIDEOS-")] + ], + size=(200, 300), + expand_x=True, + expand_y=True, + ) + + return sg.Column( + [ + [button_panel_with_mod_info, mod_image], + [sg.HorizontalSeparator()], + [sg.Text("Search"), search_bar, install_filter, uninstall_filter], + [mod_table] + + ] + ) + + +def __button(button_text, key): + return sg.Btn( + button_text=button_text, + key=key, + expand_x=True, + ) + + +def __button_with_metadata(button_text, key): + return sg.Btn( + button_text=button_text, + key=key, + expand_x=True, + metadata={"url": ""} + ) + + +def __radio(text, group_id, key): + return sg.Radio( + text=text, + group_id=group_id, + font=("Helvetica", 12), + enable_events=True, + key=key + ) + + +def __default_radio(text, group_id, key): + return sg.Radio( + text=text, + group_id=group_id, + font=("Helvetica", 12), + enable_events=True, + key=key, + default=True + ) From 39f7353acdbfa6fa4660e4c5723c07a6aba59c6f Mon Sep 17 00:00:00 2001 From: Steven Lares <13951178+StevenLares@users.noreply.github.com> Date: Wed, 28 Feb 2024 20:24:24 -0500 Subject: [PATCH 13/17] Increased Y size of column containing buttons and mod info. This is a temp fix to accomodate large screens with high resolutions. Therefore, I added a TODO on high level next steps as that is out of scope for this pull request --- utils/layoutUtils.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/utils/layoutUtils.py b/utils/layoutUtils.py index 758a23e..449998b 100644 --- a/utils/layoutUtils.py +++ b/utils/layoutUtils.py @@ -2,6 +2,11 @@ from utils import githubUtils +# TODO: Set sizes of columns, frames, and other elements +# based on the user's screen resolution and screen size. Using hard coded +# values can cause elements to not be displayed on large screens with +# high resolution + def generate(splashfile, loadingimage, LATEST_TABLE_DATA, table_headings, col_vis, col_width): return \ [ @@ -153,7 +158,10 @@ def __main_panel(LATEST_TABLE_DATA, table_headings, col_vis, col_width): [__button("View Folder", "-VIEWFOLDER-"), __button_with_metadata("Website", "-WEBSITE-"), __button_with_metadata("Video(s)", "-VIDEOS-")] ], - size=(200, 300), + # Increased y from 300 to 425, since higher display screens do not + # have room to render these buttons. This is a temp fix until + # a programmatic approach is taken to determine element sizes. + size=(200, 425), expand_x=True, expand_y=True, ) From f3938967be9070d7a384349f71c726d4e8fe409b Mon Sep 17 00:00:00 2001 From: Steven Lares <13951178+StevenLares@users.noreply.github.com> Date: Wed, 28 Feb 2024 20:55:19 -0500 Subject: [PATCH 14/17] Since Linux does not have a COMPUTERNAME variable in its environment, it results in a keyerror when the executable is run. I have added a default value in case the variable doesn't exist --- openGOALModLauncher.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openGOALModLauncher.py b/openGOALModLauncher.py index 025bf99..cc90914 100644 --- a/openGOALModLauncher.py +++ b/openGOALModLauncher.py @@ -63,7 +63,7 @@ def exitWithError(): if ( LauncherDir != Path(dirs.user_data_dir) / "OpenGOAL-UnofficalModLauncher" and os.getlogin() != "NinjaPC" - and os.environ["COMPUTERNAME"] != "DESKTOP-BBN1CMN" + and os.environ.get("COMPUTERNAME",) != "DESKTOP-BBN1CMN" ): # Creating the tkinter window root = tkinter.Tk() From 8b2c3a85adee85ac518f12fa440ccd2055f1eba9 Mon Sep 17 00:00:00 2001 From: Steven Lares <13951178+StevenLares@users.noreply.github.com> Date: Thu, 29 Feb 2024 12:28:26 -0500 Subject: [PATCH 15/17] Created bash scripts that build the launcher executables for Linux --- buildlaunchercoreEXE | 17 +++++++++++++++++ buildlauncherupdaterexe | 16 ++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100755 buildlaunchercoreEXE create mode 100755 buildlauncherupdaterexe diff --git a/buildlaunchercoreEXE b/buildlaunchercoreEXE new file mode 100755 index 0000000..377631b --- /dev/null +++ b/buildlaunchercoreEXE @@ -0,0 +1,17 @@ +#!/bin/bash + +# These three lines set the working path to this script's path. +# It is equivalent to %~dp0 used in Windows batch files +# https://stackoverflow.com/questions/207959/equivalent-of-dp0-retrieving-source-file-name-in-sh +cd `dirname $0` +mypath=`pwd` +cd "$mypath" + + +pyinstaller --onefile openGOALModLauncher.py --icon resources/appicon.ico + +mv "${mypath}/dist/openGOALModLauncher" "${mypath}" + +rm -rf "${mypath}/dist" +rm -rf "${mypath}/build" +rm "${mypath}/openGOALModLauncher.spec" diff --git a/buildlauncherupdaterexe b/buildlauncherupdaterexe new file mode 100755 index 0000000..90073b7 --- /dev/null +++ b/buildlauncherupdaterexe @@ -0,0 +1,16 @@ +#!/bin/bash + +# These three lines set the working path to this script's path. +# It is equivalent to %~dp0 used in Windows batch files +# https://stackoverflow.com/questions/207959/equivalent-of-dp0-retrieving-source-file-name-in-sh +cd `dirname $0` +mypath=`pwd` +cd "$mypath" + +# --hide-console is Windows only +pyinstaller --onefile "Launcher with autoupdater.py" --icon resources/appicon.ico + +mv "${mypath}/dist/Launcher with autoupdater" "${mypath}" + +rm -rf "${mypath}/dist" +rm -rf "${mypath}/build" From 9948fa9c662cf93883c4c19a6e6704f83efef945 Mon Sep 17 00:00:00 2001 From: Steven Lares <13951178+StevenLares@users.noreply.github.com> Date: Thu, 29 Feb 2024 14:20:20 -0500 Subject: [PATCH 16/17] Removed TODO's I added earlier in earlier commits, that do not need to be kept in --- utils/launcherUtils.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/utils/launcherUtils.py b/utils/launcherUtils.py index aae6590..5143614 100644 --- a/utils/launcherUtils.py +++ b/utils/launcherUtils.py @@ -293,7 +293,7 @@ def getDecompiler(path): return -# TODO + def launch_local(MOD_ID, GAME): try: # Close Gk and goalc if they were open. @@ -331,7 +331,7 @@ def launch_local(MOD_ID, GAME): return str(e) -# TODO + def download_and_unpack_mod(URL, MOD_ID, MOD_NAME, LINK_TYPE, InstallDir, LatestRelAssetsURL): # start the actual update method if needUpdate is true print("\nNeed to update") @@ -437,7 +437,7 @@ def download_and_unpack_mod(URL, MOD_ID, MOD_NAME, LINK_TYPE, InstallDir, Latest ) -# TODO + def rebuild(URL, MOD_ID, MOD_NAME, LINK_TYPE, GAME, should_extract): InstallDir = ModFolderPATH / MOD_ID UniversalIsoPath = ISO_PATH @@ -533,7 +533,7 @@ def rebuild(URL, MOD_ID, MOD_NAME, LINK_TYPE, GAME, should_extract): return -# TODO + def update_and_launch(URL, MOD_ID, MOD_NAME, LINK_TYPE, GAME): if URL is None: return From bab64d0cf681584c8bae0de0389c84b837c92857 Mon Sep 17 00:00:00 2001 From: Steven Lares <13951178+StevenLares@users.noreply.github.com> Date: Mon, 4 Mar 2024 10:42:34 -0500 Subject: [PATCH 17/17] Corrected order of Linux symlinks --- utils/launcherUtils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/utils/launcherUtils.py b/utils/launcherUtils.py index 5143614..1137e04 100644 --- a/utils/launcherUtils.py +++ b/utils/launcherUtils.py @@ -159,7 +159,7 @@ def makeDirSymlink(link, target): if sys.platform == "win32": subprocess.check_call('mklink /J "%s" "%s"' % (link, target), shell=True) else: - subprocess.check_call('ln -s "%s" "%s"' % (link, target), shell=True) + subprocess.check_call('ln -s "%s" "%s"' % (target, link), shell=True) def makeFileSymlink(link, target): @@ -169,7 +169,7 @@ def makeFileSymlink(link, target): if sys.platform == "win32": subprocess.check_call('mklink /H "%s" "%s"' % (link, target), shell=True) else: - subprocess.check_call('ln -s "%s" "%s"' % (link, target), shell=True) + subprocess.check_call('ln -s "%s" "%s"' % (target, link), shell=True) def link_files_by_extension(source_dir, destination_dir):