diff --git a/.gitignore b/.gitignore index 3fd0341..9ffdb54 100644 --- a/.gitignore +++ b/.gitignore @@ -35,7 +35,7 @@ Thumbs.db docs/ testing/ -# PyInstaller build files (development only) +# Build files (development only) *.spec hook-*.py requirements-packaging.txt diff --git a/CHANGELOG.md b/CHANGELOG.md index 4eb9674..5c49730 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,26 @@ # Jackify Changelog +## v0.1.5.2 - Proton Configuration & Engine Updates +**Release Date:** September 30, 2025 + +### Critical Bug Fixes +- **Fixed Proton Version Selection**: Wine component installation now properly honors user-selected Proton version from Settings dialog + - Previously, changing from GE-Proton to Proton Experimental in settings would still use the old version for component installation + - Fixed ConfigHandler to reload fresh configuration from disk instead of using stale cache + - Updated all Proton path retrieval across codebase to use fresh-reading methods + +### Engine Updates +- **jackify-engine v0.3.16**: Updated to latest engine version with important reliability improvements + - **Sanity Check Fallback**: Added Proton 7z.exe fallback for case sensitivity extraction failures + - **Enhanced Error Messages**: Improved texconv/texdiag error messages to include original texture file names and conversion parameters + +### Technical Improvements +- Enhanced configuration system reliability for multi-instance scenarios +- Improved error diagnostics for texture processing operations +- Fix Qt platform plugin discovery in AppImage distribution for improved compatibility + +--- + ## v0.1.5.1 - Bug Fixes **Release Date:** September 28, 2025 diff --git a/jackify/__init__.py b/jackify/__init__.py index 30f3842..05b3f0b 100644 --- a/jackify/__init__.py +++ b/jackify/__init__.py @@ -5,4 +5,4 @@ This package provides both CLI and GUI interfaces for managing Wabbajack modlists natively on Linux systems. """ -__version__ = "0.1.5.1" +__version__ = "0.1.5.2" diff --git a/jackify/backend/core/modlist_operations.py b/jackify/backend/core/modlist_operations.py index 5e5108c..239ed44 100644 --- a/jackify/backend/core/modlist_operations.py +++ b/jackify/backend/core/modlist_operations.py @@ -30,7 +30,7 @@ def _get_user_proton_version(): from jackify.backend.handlers.wine_utils import WineUtils config_handler = ConfigHandler() - user_proton_path = config_handler.get('proton_path', 'auto') + user_proton_path = config_handler.get_proton_path() if user_proton_path == 'auto': # Use enhanced fallback logic with GE-Proton preference diff --git a/jackify/backend/handlers/config_handler.py b/jackify/backend/handlers/config_handler.py index d7aea67..75907af 100644 --- a/jackify/backend/handlers/config_handler.py +++ b/jackify/backend/handlers/config_handler.py @@ -496,6 +496,42 @@ class ConfigHandler: logger.error(f"Error saving modlist downloads base directory: {e}") return False + def get_proton_path(self): + """ + Retrieve the saved Proton path from configuration + Always reads fresh from disk to pick up changes from Settings dialog + + Returns: + str: Saved Proton path or 'auto' if not saved + """ + try: + # Reload config from disk to pick up changes from Settings dialog + self._load_config() + proton_path = self.settings.get("proton_path", "auto") + logger.debug(f"Retrieved fresh proton_path from config: {proton_path}") + return proton_path + except Exception as e: + logger.error(f"Error retrieving proton_path: {e}") + return "auto" + + def get_proton_version(self): + """ + Retrieve the saved Proton version from configuration + Always reads fresh from disk to pick up changes from Settings dialog + + Returns: + str: Saved Proton version or 'auto' if not saved + """ + try: + # Reload config from disk to pick up changes from Settings dialog + self._load_config() + proton_version = self.settings.get("proton_version", "auto") + logger.debug(f"Retrieved fresh proton_version from config: {proton_version}") + return proton_version + except Exception as e: + logger.error(f"Error retrieving proton_version: {e}") + return "auto" + def _auto_detect_proton(self): """Auto-detect and set best Proton version (includes GE-Proton and Valve Proton)""" try: diff --git a/jackify/backend/handlers/modlist_handler.py b/jackify/backend/handlers/modlist_handler.py index e683363..d687efb 100644 --- a/jackify/backend/handlers/modlist_handler.py +++ b/jackify/backend/handlers/modlist_handler.py @@ -692,10 +692,32 @@ class ModlistHandler: print("Error: Could not determine wine prefix location.") return False - if not self.winetricks_handler.install_wine_components(wineprefix, self.game_var_full, specific_components=components): - self.logger.error("Failed to install Wine components. Configuration aborted.") - print("Error: Failed to install necessary Wine components.") - return False # Abort on failure + # Try winetricks first (preferred method with current fix) + winetricks_success = False + try: + self.logger.info("Attempting Wine component installation using winetricks...") + winetricks_success = self.winetricks_handler.install_wine_components(wineprefix, self.game_var_full, specific_components=components) + if winetricks_success: + self.logger.info("Winetricks installation completed successfully") + except Exception as e: + self.logger.warning(f"Winetricks installation failed with exception: {e}") + winetricks_success = False + + # Fallback to protontricks if winetricks failed + if not winetricks_success: + self.logger.warning("Winetricks failed, falling back to protontricks for Wine component installation...") + try: + protontricks_success = self.protontricks_handler.install_wine_components(target_appid, self.game_var_full, specific_components=components) + if protontricks_success: + self.logger.info("Protontricks fallback installation completed successfully") + else: + self.logger.error("Both winetricks and protontricks failed to install Wine components.") + print("Error: Failed to install necessary Wine components using both winetricks and protontricks.") + return False + except Exception as e: + self.logger.error(f"Protontricks fallback also failed with exception: {e}") + print("Error: Failed to install necessary Wine components using both winetricks and protontricks.") + return False self.logger.info("Step 4: Installing Wine components... Done") # Step 5: Ensure permissions of Modlist directory diff --git a/jackify/backend/handlers/path_handler.py b/jackify/backend/handlers/path_handler.py index 06ed879..fb37a25 100644 --- a/jackify/backend/handlers/path_handler.py +++ b/jackify/backend/handlers/path_handler.py @@ -630,47 +630,64 @@ class PathHandler: # Moved _find_shortcuts_vdf here from ShortcutHandler def _find_shortcuts_vdf(self) -> Optional[str]: - """Helper to find the active shortcuts.vdf file for a user. + """Helper to find the active shortcuts.vdf file for the current Steam user. - Iterates through userdata directories and returns the path to the - first found shortcuts.vdf file. + Uses proper multi-user detection to find the correct Steam user instead + of just taking the first found user directory. Returns: Optional[str]: The full path to the shortcuts.vdf file, or None if not found. """ - # This implementation was moved from ShortcutHandler - userdata_base_paths = [ - os.path.expanduser("~/.steam/steam/userdata"), - os.path.expanduser("~/.local/share/Steam/userdata"), - os.path.expanduser("~/.var/app/com.valvesoftware.Steam/.local/share/Steam/userdata") - ] - found_vdf_path = None - for base_path in userdata_base_paths: - if not os.path.isdir(base_path): - logger.debug(f"Userdata base path not found or not a directory: {base_path}") - continue - logger.debug(f"Searching for user IDs in: {base_path}") - try: - for item in os.listdir(base_path): - user_path = os.path.join(base_path, item) - if os.path.isdir(user_path) and item.isdigit(): - logger.debug(f"Checking user directory: {user_path}") - config_path = os.path.join(user_path, "config") - shortcuts_file = os.path.join(config_path, "shortcuts.vdf") - if os.path.isfile(shortcuts_file): - logger.info(f"Found shortcuts.vdf at: {shortcuts_file}") - found_vdf_path = shortcuts_file - break # Found it for this base path - else: - logger.debug(f"shortcuts.vdf not found in {config_path}") - except OSError as e: - logger.warning(f"Could not access directory {base_path}: {e}") - continue # Try next base path - if found_vdf_path: - break # Found it in this base path - if not found_vdf_path: - logger.error("Could not find any shortcuts.vdf file in common Steam locations.") - return found_vdf_path + try: + # Use native Steam service for proper multi-user detection + from jackify.backend.services.native_steam_service import NativeSteamService + steam_service = NativeSteamService() + shortcuts_path = steam_service.get_shortcuts_vdf_path() + + if shortcuts_path: + logger.info(f"Found shortcuts.vdf using multi-user detection: {shortcuts_path}") + return str(shortcuts_path) + else: + logger.error("Could not determine shortcuts.vdf path using multi-user detection") + return None + + except Exception as e: + logger.error(f"Error using multi-user detection for shortcuts.vdf: {e}") + + # Fallback to legacy behavior if multi-user detection fails + logger.warning("Falling back to legacy shortcuts.vdf detection (first-found user)") + userdata_base_paths = [ + os.path.expanduser("~/.steam/steam/userdata"), + os.path.expanduser("~/.local/share/Steam/userdata"), + os.path.expanduser("~/.var/app/com.valvesoftware.Steam/.local/share/Steam/userdata") + ] + found_vdf_path = None + for base_path in userdata_base_paths: + if not os.path.isdir(base_path): + logger.debug(f"Userdata base path not found or not a directory: {base_path}") + continue + logger.debug(f"Searching for user IDs in: {base_path}") + try: + for item in os.listdir(base_path): + user_path = os.path.join(base_path, item) + if os.path.isdir(user_path) and item.isdigit(): + logger.debug(f"Checking user directory: {user_path}") + config_path = os.path.join(user_path, "config") + shortcuts_file = os.path.join(config_path, "shortcuts.vdf") + if os.path.isfile(shortcuts_file): + logger.info(f"Found shortcuts.vdf at: {shortcuts_file}") + found_vdf_path = shortcuts_file + break # Found it for this base path + else: + logger.debug(f"shortcuts.vdf not found in {config_path}") + except OSError as e: + logger.warning(f"Could not access directory {base_path}: {e}") + continue # Try next base path + if found_vdf_path: + break # Found it in this base path + if not found_vdf_path: + logger.error("Could not find any shortcuts.vdf file in common Steam locations.") + return found_vdf_path @staticmethod def find_game_install_paths(target_appids: Dict[str, str]) -> Dict[str, Path]: diff --git a/jackify/backend/handlers/validation_handler.py b/jackify/backend/handlers/validation_handler.py index 7c9ab3c..060ce44 100644 --- a/jackify/backend/handlers/validation_handler.py +++ b/jackify/backend/handlers/validation_handler.py @@ -222,15 +222,21 @@ class ValidationHandler: def validate_steam_shortcut(self, app_id: str) -> Tuple[bool, str]: """Validate a Steam shortcut.""" try: - # Check if shortcuts.vdf exists - shortcuts_path = Path.home() / '.steam' / 'steam' / 'userdata' / '75424832' / 'config' / 'shortcuts.vdf' + # Use native Steam service to get proper shortcuts.vdf path with multi-user support + from jackify.backend.services.native_steam_service import NativeSteamService + steam_service = NativeSteamService() + shortcuts_path = steam_service.get_shortcuts_vdf_path() + + if not shortcuts_path: + return False, "Could not determine shortcuts.vdf path (no active Steam user found)" + if not shortcuts_path.exists(): return False, "shortcuts.vdf not found" - + # Check if shortcuts.vdf is accessible if not os.access(shortcuts_path, os.R_OK | os.W_OK): return False, "shortcuts.vdf is not accessible" - + # Parse shortcuts.vdf using VDFHandler shortcuts_data = VDFHandler.load(str(shortcuts_path), binary=True) diff --git a/jackify/backend/handlers/wine_utils.py b/jackify/backend/handlers/wine_utils.py index 43a176e..6056657 100644 --- a/jackify/backend/handlers/wine_utils.py +++ b/jackify/backend/handlers/wine_utils.py @@ -709,7 +709,7 @@ class WineUtils: try: from .config_handler import ConfigHandler config = ConfigHandler() - fallback_path = config.get('proton_path', 'auto') + fallback_path = config.get_proton_path() if fallback_path != 'auto': fallback_wine_bin = Path(fallback_path) / "files/bin/wine" if fallback_wine_bin.is_file(): diff --git a/jackify/backend/handlers/winetricks_handler.py b/jackify/backend/handlers/winetricks_handler.py index a41e61f..a9fc7de 100644 --- a/jackify/backend/handlers/winetricks_handler.py +++ b/jackify/backend/handlers/winetricks_handler.py @@ -137,7 +137,7 @@ class WinetricksHandler: from ..handlers.wine_utils import WineUtils config = ConfigHandler() - user_proton_path = config.get('proton_path', 'auto') + user_proton_path = config.get_proton_path() # If user selected a specific Proton, try that first wine_binary = None @@ -181,6 +181,49 @@ class WinetricksHandler: env['WINE'] = str(wine_binary) self.logger.info(f"Using Proton wine binary for winetricks: {wine_binary}") + # CRITICAL: Set up protontricks-compatible environment + proton_dist_path = os.path.dirname(os.path.dirname(wine_binary)) # e.g., /path/to/proton/dist/bin/wine -> /path/to/proton/dist + self.logger.debug(f"Proton dist path: {proton_dist_path}") + + # Set WINEDLLPATH like protontricks does + env['WINEDLLPATH'] = f"{proton_dist_path}/lib64/wine:{proton_dist_path}/lib/wine" + + # Ensure Proton bin directory is first in PATH + env['PATH'] = f"{proton_dist_path}/bin:{env.get('PATH', '')}" + + # Set DLL overrides exactly like protontricks + dll_overrides = { + "beclient": "b,n", + "beclient_x64": "b,n", + "dxgi": "n", + "d3d9": "n", + "d3d10core": "n", + "d3d11": "n", + "d3d12": "n", + "d3d12core": "n", + "nvapi": "n", + "nvapi64": "n", + "nvofapi64": "n", + "nvcuda": "b" + } + + # Merge with existing overrides + existing_overrides = env.get('WINEDLLOVERRIDES', '') + if existing_overrides: + # Parse existing overrides + for override in existing_overrides.split(';'): + if '=' in override: + name, value = override.split('=', 1) + dll_overrides[name] = value + + env['WINEDLLOVERRIDES'] = ';'.join(f"{name}={setting}" for name, setting in dll_overrides.items()) + + # Set Wine defaults from protontricks + env['WINE_LARGE_ADDRESS_AWARE'] = '1' + env['DXVK_ENABLE_NVAPI'] = '1' + + self.logger.debug(f"Set protontricks environment: WINEDLLPATH={env['WINEDLLPATH']}") + except Exception as e: self.logger.error(f"Cannot run winetricks: Failed to get Proton wine binary: {e}") return False @@ -426,7 +469,8 @@ class WinetricksHandler: except Exception as e: self.logger.warning(f"Could not read winetricks.log: {e}") - self.logger.error(f"✗ {component} failed (attempt {attempt}): {result.stderr.strip()[:200]}") + self.logger.error(f"✗ {component} failed (attempt {attempt}): {result.stderr.strip()}") + self.logger.debug(f"Full stdout for {component}: {result.stdout.strip()}") except Exception as e: self.logger.error(f"Error installing {component} (attempt {attempt}): {e}") diff --git a/jackify/backend/services/automated_prefix_service.py b/jackify/backend/services/automated_prefix_service.py index d30ffb5..d1fa04e 100644 --- a/jackify/backend/services/automated_prefix_service.py +++ b/jackify/backend/services/automated_prefix_service.py @@ -46,7 +46,7 @@ class AutomatedPrefixService: from jackify.backend.handlers.wine_utils import WineUtils config_handler = ConfigHandler() - user_proton_path = config_handler.get('proton_path', 'auto') + user_proton_path = config_handler.get_proton_path() if user_proton_path == 'auto': # Use enhanced fallback logic with GE-Proton preference @@ -2705,7 +2705,7 @@ echo Prefix creation complete. from jackify.backend.handlers.wine_utils import WineUtils config = ConfigHandler() - user_proton_path = config.get('proton_path', 'auto') + user_proton_path = config.get_proton_path() # If user selected a specific Proton, try that first if user_proton_path != 'auto': diff --git a/jackify/engine/Wabbajack.CLI.Builder.dll b/jackify/engine/Wabbajack.CLI.Builder.dll index 069ed6e..5112bb1 100644 Binary files a/jackify/engine/Wabbajack.CLI.Builder.dll and b/jackify/engine/Wabbajack.CLI.Builder.dll differ diff --git a/jackify/engine/Wabbajack.Common.dll b/jackify/engine/Wabbajack.Common.dll index 351e404..56d10da 100644 Binary files a/jackify/engine/Wabbajack.Common.dll and b/jackify/engine/Wabbajack.Common.dll differ diff --git a/jackify/engine/Wabbajack.Compiler.dll b/jackify/engine/Wabbajack.Compiler.dll index 8c45d85..1364278 100644 Binary files a/jackify/engine/Wabbajack.Compiler.dll and b/jackify/engine/Wabbajack.Compiler.dll differ diff --git a/jackify/engine/Wabbajack.Compression.BSA.dll b/jackify/engine/Wabbajack.Compression.BSA.dll index 98205eb..987a82b 100644 Binary files a/jackify/engine/Wabbajack.Compression.BSA.dll and b/jackify/engine/Wabbajack.Compression.BSA.dll differ diff --git a/jackify/engine/Wabbajack.Compression.Zip.dll b/jackify/engine/Wabbajack.Compression.Zip.dll index c8a6b0c..837a02f 100644 Binary files a/jackify/engine/Wabbajack.Compression.Zip.dll and b/jackify/engine/Wabbajack.Compression.Zip.dll differ diff --git a/jackify/engine/Wabbajack.Configuration.dll b/jackify/engine/Wabbajack.Configuration.dll index 2031231..33fd275 100644 Binary files a/jackify/engine/Wabbajack.Configuration.dll and b/jackify/engine/Wabbajack.Configuration.dll differ diff --git a/jackify/engine/Wabbajack.DTOs.dll b/jackify/engine/Wabbajack.DTOs.dll index e5d7367..7491a1e 100644 Binary files a/jackify/engine/Wabbajack.DTOs.dll and b/jackify/engine/Wabbajack.DTOs.dll differ diff --git a/jackify/engine/Wabbajack.Downloaders.Bethesda.dll b/jackify/engine/Wabbajack.Downloaders.Bethesda.dll index 28df717..a9e4fc4 100644 Binary files a/jackify/engine/Wabbajack.Downloaders.Bethesda.dll and b/jackify/engine/Wabbajack.Downloaders.Bethesda.dll differ diff --git a/jackify/engine/Wabbajack.Downloaders.Dispatcher.dll b/jackify/engine/Wabbajack.Downloaders.Dispatcher.dll index 3416aed..a0d7fb7 100644 Binary files a/jackify/engine/Wabbajack.Downloaders.Dispatcher.dll and b/jackify/engine/Wabbajack.Downloaders.Dispatcher.dll differ diff --git a/jackify/engine/Wabbajack.Downloaders.GameFile.dll b/jackify/engine/Wabbajack.Downloaders.GameFile.dll index 2db7ded..1ba2866 100644 Binary files a/jackify/engine/Wabbajack.Downloaders.GameFile.dll and b/jackify/engine/Wabbajack.Downloaders.GameFile.dll differ diff --git a/jackify/engine/Wabbajack.Downloaders.GoogleDrive.dll b/jackify/engine/Wabbajack.Downloaders.GoogleDrive.dll index 766afb9..889d3f1 100644 Binary files a/jackify/engine/Wabbajack.Downloaders.GoogleDrive.dll and b/jackify/engine/Wabbajack.Downloaders.GoogleDrive.dll differ diff --git a/jackify/engine/Wabbajack.Downloaders.Http.dll b/jackify/engine/Wabbajack.Downloaders.Http.dll index ddbdb45..1f65475 100644 Binary files a/jackify/engine/Wabbajack.Downloaders.Http.dll and b/jackify/engine/Wabbajack.Downloaders.Http.dll differ diff --git a/jackify/engine/Wabbajack.Downloaders.IPS4OAuth2Downloader.dll b/jackify/engine/Wabbajack.Downloaders.IPS4OAuth2Downloader.dll index 17f8b9a..9178d1f 100644 Binary files a/jackify/engine/Wabbajack.Downloaders.IPS4OAuth2Downloader.dll and b/jackify/engine/Wabbajack.Downloaders.IPS4OAuth2Downloader.dll differ diff --git a/jackify/engine/Wabbajack.Downloaders.Interfaces.dll b/jackify/engine/Wabbajack.Downloaders.Interfaces.dll index 5a5680c..87f64cc 100644 Binary files a/jackify/engine/Wabbajack.Downloaders.Interfaces.dll and b/jackify/engine/Wabbajack.Downloaders.Interfaces.dll differ diff --git a/jackify/engine/Wabbajack.Downloaders.Manual.dll b/jackify/engine/Wabbajack.Downloaders.Manual.dll index 1816990..1bad653 100644 Binary files a/jackify/engine/Wabbajack.Downloaders.Manual.dll and b/jackify/engine/Wabbajack.Downloaders.Manual.dll differ diff --git a/jackify/engine/Wabbajack.Downloaders.MediaFire.dll b/jackify/engine/Wabbajack.Downloaders.MediaFire.dll index 4394662..6b4cd1d 100644 Binary files a/jackify/engine/Wabbajack.Downloaders.MediaFire.dll and b/jackify/engine/Wabbajack.Downloaders.MediaFire.dll differ diff --git a/jackify/engine/Wabbajack.Downloaders.Mega.dll b/jackify/engine/Wabbajack.Downloaders.Mega.dll index 9a079c2..662df05 100644 Binary files a/jackify/engine/Wabbajack.Downloaders.Mega.dll and b/jackify/engine/Wabbajack.Downloaders.Mega.dll differ diff --git a/jackify/engine/Wabbajack.Downloaders.ModDB.dll b/jackify/engine/Wabbajack.Downloaders.ModDB.dll index 8c42eb1..2e41174 100644 Binary files a/jackify/engine/Wabbajack.Downloaders.ModDB.dll and b/jackify/engine/Wabbajack.Downloaders.ModDB.dll differ diff --git a/jackify/engine/Wabbajack.Downloaders.Nexus.dll b/jackify/engine/Wabbajack.Downloaders.Nexus.dll index 6bf0cfd..5ce19c9 100644 Binary files a/jackify/engine/Wabbajack.Downloaders.Nexus.dll and b/jackify/engine/Wabbajack.Downloaders.Nexus.dll differ diff --git a/jackify/engine/Wabbajack.Downloaders.VerificationCache.dll b/jackify/engine/Wabbajack.Downloaders.VerificationCache.dll index 0df14ef..3d50f98 100644 Binary files a/jackify/engine/Wabbajack.Downloaders.VerificationCache.dll and b/jackify/engine/Wabbajack.Downloaders.VerificationCache.dll differ diff --git a/jackify/engine/Wabbajack.Downloaders.WabbajackCDN.dll b/jackify/engine/Wabbajack.Downloaders.WabbajackCDN.dll index 758d973..bf963b8 100644 Binary files a/jackify/engine/Wabbajack.Downloaders.WabbajackCDN.dll and b/jackify/engine/Wabbajack.Downloaders.WabbajackCDN.dll differ diff --git a/jackify/engine/Wabbajack.FileExtractor.dll b/jackify/engine/Wabbajack.FileExtractor.dll index 6797aa1..3046b42 100644 Binary files a/jackify/engine/Wabbajack.FileExtractor.dll and b/jackify/engine/Wabbajack.FileExtractor.dll differ diff --git a/jackify/engine/Wabbajack.Hashing.PHash.dll b/jackify/engine/Wabbajack.Hashing.PHash.dll index cc358fa..aa6ab99 100644 Binary files a/jackify/engine/Wabbajack.Hashing.PHash.dll and b/jackify/engine/Wabbajack.Hashing.PHash.dll differ diff --git a/jackify/engine/Wabbajack.Hashing.xxHash64.dll b/jackify/engine/Wabbajack.Hashing.xxHash64.dll index 5407d92..e344112 100644 Binary files a/jackify/engine/Wabbajack.Hashing.xxHash64.dll and b/jackify/engine/Wabbajack.Hashing.xxHash64.dll differ diff --git a/jackify/engine/Wabbajack.IO.Async.dll b/jackify/engine/Wabbajack.IO.Async.dll index 3b619be..25edc1a 100644 Binary files a/jackify/engine/Wabbajack.IO.Async.dll and b/jackify/engine/Wabbajack.IO.Async.dll differ diff --git a/jackify/engine/Wabbajack.Installer.dll b/jackify/engine/Wabbajack.Installer.dll index 7593881..2a5287b 100644 Binary files a/jackify/engine/Wabbajack.Installer.dll and b/jackify/engine/Wabbajack.Installer.dll differ diff --git a/jackify/engine/Wabbajack.Networking.BethesdaNet.dll b/jackify/engine/Wabbajack.Networking.BethesdaNet.dll index a4d69c9..04c5110 100644 Binary files a/jackify/engine/Wabbajack.Networking.BethesdaNet.dll and b/jackify/engine/Wabbajack.Networking.BethesdaNet.dll differ diff --git a/jackify/engine/Wabbajack.Networking.Discord.dll b/jackify/engine/Wabbajack.Networking.Discord.dll index ebbd796..aa845e9 100644 Binary files a/jackify/engine/Wabbajack.Networking.Discord.dll and b/jackify/engine/Wabbajack.Networking.Discord.dll differ diff --git a/jackify/engine/Wabbajack.Networking.GitHub.dll b/jackify/engine/Wabbajack.Networking.GitHub.dll index 6124a60..db21880 100644 Binary files a/jackify/engine/Wabbajack.Networking.GitHub.dll and b/jackify/engine/Wabbajack.Networking.GitHub.dll differ diff --git a/jackify/engine/Wabbajack.Networking.Http.Interfaces.dll b/jackify/engine/Wabbajack.Networking.Http.Interfaces.dll index e06e883..fc8283b 100644 Binary files a/jackify/engine/Wabbajack.Networking.Http.Interfaces.dll and b/jackify/engine/Wabbajack.Networking.Http.Interfaces.dll differ diff --git a/jackify/engine/Wabbajack.Networking.Http.dll b/jackify/engine/Wabbajack.Networking.Http.dll index c523dd2..0d9d5f2 100644 Binary files a/jackify/engine/Wabbajack.Networking.Http.dll and b/jackify/engine/Wabbajack.Networking.Http.dll differ diff --git a/jackify/engine/Wabbajack.Networking.NexusApi.dll b/jackify/engine/Wabbajack.Networking.NexusApi.dll index cf6260e..941c435 100644 Binary files a/jackify/engine/Wabbajack.Networking.NexusApi.dll and b/jackify/engine/Wabbajack.Networking.NexusApi.dll differ diff --git a/jackify/engine/Wabbajack.Networking.WabbajackClientApi.dll b/jackify/engine/Wabbajack.Networking.WabbajackClientApi.dll index 899578d..ddbf1cc 100644 Binary files a/jackify/engine/Wabbajack.Networking.WabbajackClientApi.dll and b/jackify/engine/Wabbajack.Networking.WabbajackClientApi.dll differ diff --git a/jackify/engine/Wabbajack.Paths.IO.dll b/jackify/engine/Wabbajack.Paths.IO.dll index 8091dc7..5ed41d6 100644 Binary files a/jackify/engine/Wabbajack.Paths.IO.dll and b/jackify/engine/Wabbajack.Paths.IO.dll differ diff --git a/jackify/engine/Wabbajack.Paths.dll b/jackify/engine/Wabbajack.Paths.dll index 28c8eb2..c38bd09 100644 Binary files a/jackify/engine/Wabbajack.Paths.dll and b/jackify/engine/Wabbajack.Paths.dll differ diff --git a/jackify/engine/Wabbajack.RateLimiter.dll b/jackify/engine/Wabbajack.RateLimiter.dll index 901749f..dc39d59 100644 Binary files a/jackify/engine/Wabbajack.RateLimiter.dll and b/jackify/engine/Wabbajack.RateLimiter.dll differ diff --git a/jackify/engine/Wabbajack.Server.Lib.dll b/jackify/engine/Wabbajack.Server.Lib.dll index 9f974de..4fcff1f 100644 Binary files a/jackify/engine/Wabbajack.Server.Lib.dll and b/jackify/engine/Wabbajack.Server.Lib.dll differ diff --git a/jackify/engine/Wabbajack.Services.OSIntegrated.dll b/jackify/engine/Wabbajack.Services.OSIntegrated.dll index b57cce1..992edd2 100644 Binary files a/jackify/engine/Wabbajack.Services.OSIntegrated.dll and b/jackify/engine/Wabbajack.Services.OSIntegrated.dll differ diff --git a/jackify/engine/Wabbajack.VFS.Interfaces.dll b/jackify/engine/Wabbajack.VFS.Interfaces.dll index 627f5f4..10b6646 100644 Binary files a/jackify/engine/Wabbajack.VFS.Interfaces.dll and b/jackify/engine/Wabbajack.VFS.Interfaces.dll differ diff --git a/jackify/engine/Wabbajack.VFS.dll b/jackify/engine/Wabbajack.VFS.dll index 42abcf4..2a68781 100644 Binary files a/jackify/engine/Wabbajack.VFS.dll and b/jackify/engine/Wabbajack.VFS.dll differ diff --git a/jackify/engine/jackify-engine.deps.json b/jackify/engine/jackify-engine.deps.json index 3a447af..d004f47 100644 --- a/jackify/engine/jackify-engine.deps.json +++ b/jackify/engine/jackify-engine.deps.json @@ -7,7 +7,7 @@ "targets": { ".NETCoreApp,Version=v8.0": {}, ".NETCoreApp,Version=v8.0/linux-x64": { - "jackify-engine/0.3.15": { + "jackify-engine/0.3.16": { "dependencies": { "Markdig": "0.40.0", "Microsoft.Extensions.Configuration.Json": "9.0.1", @@ -22,16 +22,16 @@ "SixLabors.ImageSharp": "3.1.6", "System.CommandLine": "2.0.0-beta4.22272.1", "System.CommandLine.NamingConventionBinder": "2.0.0-beta4.22272.1", - "Wabbajack.CLI.Builder": "0.3.15", - "Wabbajack.Downloaders.Bethesda": "0.3.15", - "Wabbajack.Downloaders.Dispatcher": "0.3.15", - "Wabbajack.Hashing.xxHash64": "0.3.15", - "Wabbajack.Networking.Discord": "0.3.15", - "Wabbajack.Networking.GitHub": "0.3.15", - "Wabbajack.Paths.IO": "0.3.15", - "Wabbajack.Server.Lib": "0.3.15", - "Wabbajack.Services.OSIntegrated": "0.3.15", - "Wabbajack.VFS": "0.3.15", + "Wabbajack.CLI.Builder": "0.3.16", + "Wabbajack.Downloaders.Bethesda": "0.3.16", + "Wabbajack.Downloaders.Dispatcher": "0.3.16", + "Wabbajack.Hashing.xxHash64": "0.3.16", + "Wabbajack.Networking.Discord": "0.3.16", + "Wabbajack.Networking.GitHub": "0.3.16", + "Wabbajack.Paths.IO": "0.3.16", + "Wabbajack.Server.Lib": "0.3.16", + "Wabbajack.Services.OSIntegrated": "0.3.16", + "Wabbajack.VFS": "0.3.16", "MegaApiClient": "1.0.0.0", "runtimepack.Microsoft.NETCore.App.Runtime.linux-x64": "8.0.19" }, @@ -1781,7 +1781,7 @@ } } }, - "Wabbajack.CLI.Builder/0.3.15": { + "Wabbajack.CLI.Builder/0.3.16": { "dependencies": { "Microsoft.Extensions.Configuration.Json": "9.0.1", "Microsoft.Extensions.DependencyInjection": "9.0.1", @@ -1791,109 +1791,109 @@ "Microsoft.Extensions.Logging.Abstractions": "9.0.1", "System.CommandLine": "2.0.0-beta4.22272.1", "System.CommandLine.NamingConventionBinder": "2.0.0-beta4.22272.1", - "Wabbajack.Paths": "0.3.15" + "Wabbajack.Paths": "0.3.16" }, "runtime": { "Wabbajack.CLI.Builder.dll": {} } }, - "Wabbajack.Common/0.3.15": { + "Wabbajack.Common/0.3.16": { "dependencies": { "Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.Logging.Abstractions": "9.0.1", "System.Reactive": "6.0.1", - "Wabbajack.DTOs": "0.3.15", - "Wabbajack.Networking.Http": "0.3.15", - "Wabbajack.Paths.IO": "0.3.15" + "Wabbajack.DTOs": "0.3.16", + "Wabbajack.Networking.Http": "0.3.16", + "Wabbajack.Paths.IO": "0.3.16" }, "runtime": { "Wabbajack.Common.dll": {} } }, - "Wabbajack.Compiler/0.3.15": { + "Wabbajack.Compiler/0.3.16": { "dependencies": { "F23.StringSimilarity": "6.0.0", "Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Newtonsoft.Json": "13.0.3", "SixLabors.ImageSharp": "3.1.6", - "Wabbajack.Downloaders.Dispatcher": "0.3.15", - "Wabbajack.Installer": "0.3.15", - "Wabbajack.VFS": "0.3.15", + "Wabbajack.Downloaders.Dispatcher": "0.3.16", + "Wabbajack.Installer": "0.3.16", + "Wabbajack.VFS": "0.3.16", "ini-parser-netstandard": "2.5.2" }, "runtime": { "Wabbajack.Compiler.dll": {} } }, - "Wabbajack.Compression.BSA/0.3.15": { + "Wabbajack.Compression.BSA/0.3.16": { "dependencies": { "K4os.Compression.LZ4.Streams": "1.3.8", "Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "SharpZipLib": "1.4.2", - "Wabbajack.Common": "0.3.15", - "Wabbajack.DTOs": "0.3.15" + "Wabbajack.Common": "0.3.16", + "Wabbajack.DTOs": "0.3.16" }, "runtime": { "Wabbajack.Compression.BSA.dll": {} } }, - "Wabbajack.Compression.Zip/0.3.15": { + "Wabbajack.Compression.Zip/0.3.16": { "dependencies": { - "Wabbajack.IO.Async": "0.3.15" + "Wabbajack.IO.Async": "0.3.16" }, "runtime": { "Wabbajack.Compression.Zip.dll": {} } }, - "Wabbajack.Configuration/0.3.15": { + "Wabbajack.Configuration/0.3.16": { "runtime": { "Wabbajack.Configuration.dll": {} } }, - "Wabbajack.Downloaders.Bethesda/0.3.15": { + "Wabbajack.Downloaders.Bethesda/0.3.16": { "dependencies": { "LibAES-CTR": "1.1.0", "Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "SharpZipLib": "1.4.2", - "Wabbajack.Common": "0.3.15", - "Wabbajack.Downloaders.Interfaces": "0.3.15", - "Wabbajack.Networking.BethesdaNet": "0.3.15" + "Wabbajack.Common": "0.3.16", + "Wabbajack.Downloaders.Interfaces": "0.3.16", + "Wabbajack.Networking.BethesdaNet": "0.3.16" }, "runtime": { "Wabbajack.Downloaders.Bethesda.dll": {} } }, - "Wabbajack.Downloaders.Dispatcher/0.3.15": { + "Wabbajack.Downloaders.Dispatcher/0.3.16": { "dependencies": { "Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.Logging.Abstractions": "9.0.1", "Newtonsoft.Json": "13.0.3", "SixLabors.ImageSharp": "3.1.6", - "Wabbajack.Downloaders.Bethesda": "0.3.15", - "Wabbajack.Downloaders.GameFile": "0.3.15", - "Wabbajack.Downloaders.GoogleDrive": "0.3.15", - "Wabbajack.Downloaders.Http": "0.3.15", - "Wabbajack.Downloaders.IPS4OAuth2Downloader": "0.3.15", - "Wabbajack.Downloaders.Interfaces": "0.3.15", - "Wabbajack.Downloaders.Manual": "0.3.15", - "Wabbajack.Downloaders.MediaFire": "0.3.15", - "Wabbajack.Downloaders.Mega": "0.3.15", - "Wabbajack.Downloaders.ModDB": "0.3.15", - "Wabbajack.Downloaders.Nexus": "0.3.15", - "Wabbajack.Downloaders.VerificationCache": "0.3.15", - "Wabbajack.Downloaders.WabbajackCDN": "0.3.15", - "Wabbajack.Networking.WabbajackClientApi": "0.3.15" + "Wabbajack.Downloaders.Bethesda": "0.3.16", + "Wabbajack.Downloaders.GameFile": "0.3.16", + "Wabbajack.Downloaders.GoogleDrive": "0.3.16", + "Wabbajack.Downloaders.Http": "0.3.16", + "Wabbajack.Downloaders.IPS4OAuth2Downloader": "0.3.16", + "Wabbajack.Downloaders.Interfaces": "0.3.16", + "Wabbajack.Downloaders.Manual": "0.3.16", + "Wabbajack.Downloaders.MediaFire": "0.3.16", + "Wabbajack.Downloaders.Mega": "0.3.16", + "Wabbajack.Downloaders.ModDB": "0.3.16", + "Wabbajack.Downloaders.Nexus": "0.3.16", + "Wabbajack.Downloaders.VerificationCache": "0.3.16", + "Wabbajack.Downloaders.WabbajackCDN": "0.3.16", + "Wabbajack.Networking.WabbajackClientApi": "0.3.16" }, "runtime": { "Wabbajack.Downloaders.Dispatcher.dll": {} } }, - "Wabbajack.Downloaders.GameFile/0.3.15": { + "Wabbajack.Downloaders.GameFile/0.3.16": { "dependencies": { "GameFinder.StoreHandlers.EADesktop": "4.5.0", "GameFinder.StoreHandlers.EGS": "4.5.0", @@ -1903,360 +1903,360 @@ "Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "SixLabors.ImageSharp": "3.1.6", - "Wabbajack.Downloaders.Interfaces": "0.3.15", - "Wabbajack.VFS": "0.3.15" + "Wabbajack.Downloaders.Interfaces": "0.3.16", + "Wabbajack.VFS": "0.3.16" }, "runtime": { "Wabbajack.Downloaders.GameFile.dll": {} } }, - "Wabbajack.Downloaders.GoogleDrive/0.3.15": { + "Wabbajack.Downloaders.GoogleDrive/0.3.16": { "dependencies": { "HtmlAgilityPack": "1.11.72", "Microsoft.AspNetCore.Http.Extensions": "2.3.0", "Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.Logging.Abstractions": "9.0.1", - "Wabbajack.Common": "0.3.15", - "Wabbajack.DTOs": "0.3.15", - "Wabbajack.Downloaders.Interfaces": "0.3.15", - "Wabbajack.Networking.Http": "0.3.15", - "Wabbajack.Networking.Http.Interfaces": "0.3.15" + "Wabbajack.Common": "0.3.16", + "Wabbajack.DTOs": "0.3.16", + "Wabbajack.Downloaders.Interfaces": "0.3.16", + "Wabbajack.Networking.Http": "0.3.16", + "Wabbajack.Networking.Http.Interfaces": "0.3.16" }, "runtime": { "Wabbajack.Downloaders.GoogleDrive.dll": {} } }, - "Wabbajack.Downloaders.Http/0.3.15": { + "Wabbajack.Downloaders.Http/0.3.16": { "dependencies": { "Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.Logging.Abstractions": "9.0.1", - "Wabbajack.Common": "0.3.15", - "Wabbajack.DTOs": "0.3.15", - "Wabbajack.Downloaders.Interfaces": "0.3.15", - "Wabbajack.Networking.BethesdaNet": "0.3.15", - "Wabbajack.Networking.Http.Interfaces": "0.3.15", - "Wabbajack.Paths.IO": "0.3.15" + "Wabbajack.Common": "0.3.16", + "Wabbajack.DTOs": "0.3.16", + "Wabbajack.Downloaders.Interfaces": "0.3.16", + "Wabbajack.Networking.BethesdaNet": "0.3.16", + "Wabbajack.Networking.Http.Interfaces": "0.3.16", + "Wabbajack.Paths.IO": "0.3.16" }, "runtime": { "Wabbajack.Downloaders.Http.dll": {} } }, - "Wabbajack.Downloaders.Interfaces/0.3.15": { + "Wabbajack.Downloaders.Interfaces/0.3.16": { "dependencies": { "Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", - "Wabbajack.Compression.Zip": "0.3.15", - "Wabbajack.DTOs": "0.3.15", - "Wabbajack.Paths.IO": "0.3.15" + "Wabbajack.Compression.Zip": "0.3.16", + "Wabbajack.DTOs": "0.3.16", + "Wabbajack.Paths.IO": "0.3.16" }, "runtime": { "Wabbajack.Downloaders.Interfaces.dll": {} } }, - "Wabbajack.Downloaders.IPS4OAuth2Downloader/0.3.15": { + "Wabbajack.Downloaders.IPS4OAuth2Downloader/0.3.16": { "dependencies": { "F23.StringSimilarity": "6.0.0", "Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.Logging.Abstractions": "9.0.1", - "Wabbajack.Common": "0.3.15", - "Wabbajack.Downloaders.Interfaces": "0.3.15", - "Wabbajack.Networking.Http": "0.3.15", - "Wabbajack.Networking.Http.Interfaces": "0.3.15" + "Wabbajack.Common": "0.3.16", + "Wabbajack.Downloaders.Interfaces": "0.3.16", + "Wabbajack.Networking.Http": "0.3.16", + "Wabbajack.Networking.Http.Interfaces": "0.3.16" }, "runtime": { "Wabbajack.Downloaders.IPS4OAuth2Downloader.dll": {} } }, - "Wabbajack.Downloaders.Manual/0.3.15": { + "Wabbajack.Downloaders.Manual/0.3.16": { "dependencies": { "Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.Logging.Abstractions": "9.0.1", - "Wabbajack.Common": "0.3.15", - "Wabbajack.Downloaders.Interfaces": "0.3.15" + "Wabbajack.Common": "0.3.16", + "Wabbajack.Downloaders.Interfaces": "0.3.16" }, "runtime": { "Wabbajack.Downloaders.Manual.dll": {} } }, - "Wabbajack.Downloaders.MediaFire/0.3.15": { + "Wabbajack.Downloaders.MediaFire/0.3.16": { "dependencies": { "HtmlAgilityPack": "1.11.72", "Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.Logging.Abstractions": "9.0.1", - "Wabbajack.Common": "0.3.15", - "Wabbajack.Downloaders.Interfaces": "0.3.15", - "Wabbajack.Networking.Http.Interfaces": "0.3.15" + "Wabbajack.Common": "0.3.16", + "Wabbajack.Downloaders.Interfaces": "0.3.16", + "Wabbajack.Networking.Http.Interfaces": "0.3.16" }, "runtime": { "Wabbajack.Downloaders.MediaFire.dll": {} } }, - "Wabbajack.Downloaders.Mega/0.3.15": { + "Wabbajack.Downloaders.Mega/0.3.16": { "dependencies": { "Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.Logging.Abstractions": "9.0.1", "Newtonsoft.Json": "13.0.3", - "Wabbajack.Common": "0.3.15", - "Wabbajack.Downloaders.Interfaces": "0.3.15", - "Wabbajack.Paths.IO": "0.3.15" + "Wabbajack.Common": "0.3.16", + "Wabbajack.Downloaders.Interfaces": "0.3.16", + "Wabbajack.Paths.IO": "0.3.16" }, "runtime": { "Wabbajack.Downloaders.Mega.dll": {} } }, - "Wabbajack.Downloaders.ModDB/0.3.15": { + "Wabbajack.Downloaders.ModDB/0.3.16": { "dependencies": { "HtmlAgilityPack": "1.11.72", "Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.Logging.Abstractions": "9.0.1", "Newtonsoft.Json": "13.0.3", - "Wabbajack.Common": "0.3.15", - "Wabbajack.Downloaders.Interfaces": "0.3.15", - "Wabbajack.Networking.Http": "0.3.15", - "Wabbajack.Networking.Http.Interfaces": "0.3.15" + "Wabbajack.Common": "0.3.16", + "Wabbajack.Downloaders.Interfaces": "0.3.16", + "Wabbajack.Networking.Http": "0.3.16", + "Wabbajack.Networking.Http.Interfaces": "0.3.16" }, "runtime": { "Wabbajack.Downloaders.ModDB.dll": {} } }, - "Wabbajack.Downloaders.Nexus/0.3.15": { + "Wabbajack.Downloaders.Nexus/0.3.16": { "dependencies": { "Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", - "Wabbajack.DTOs": "0.3.15", - "Wabbajack.Downloaders.Interfaces": "0.3.15", - "Wabbajack.Hashing.xxHash64": "0.3.15", - "Wabbajack.Networking.Http": "0.3.15", - "Wabbajack.Networking.Http.Interfaces": "0.3.15", - "Wabbajack.Networking.NexusApi": "0.3.15", - "Wabbajack.Paths": "0.3.15" + "Wabbajack.DTOs": "0.3.16", + "Wabbajack.Downloaders.Interfaces": "0.3.16", + "Wabbajack.Hashing.xxHash64": "0.3.16", + "Wabbajack.Networking.Http": "0.3.16", + "Wabbajack.Networking.Http.Interfaces": "0.3.16", + "Wabbajack.Networking.NexusApi": "0.3.16", + "Wabbajack.Paths": "0.3.16" }, "runtime": { "Wabbajack.Downloaders.Nexus.dll": {} } }, - "Wabbajack.Downloaders.VerificationCache/0.3.15": { + "Wabbajack.Downloaders.VerificationCache/0.3.16": { "dependencies": { "Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.Logging.Abstractions": "9.0.1", "Stub.System.Data.SQLite.Core.NetStandard": "1.0.119", - "Wabbajack.DTOs": "0.3.15", - "Wabbajack.Paths.IO": "0.3.15" + "Wabbajack.DTOs": "0.3.16", + "Wabbajack.Paths.IO": "0.3.16" }, "runtime": { "Wabbajack.Downloaders.VerificationCache.dll": {} } }, - "Wabbajack.Downloaders.WabbajackCDN/0.3.15": { + "Wabbajack.Downloaders.WabbajackCDN/0.3.16": { "dependencies": { "Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.Logging.Abstractions": "9.0.1", "Microsoft.Toolkit.HighPerformance": "7.1.2", - "Wabbajack.Common": "0.3.15", - "Wabbajack.Downloaders.Interfaces": "0.3.15", - "Wabbajack.Networking.Http": "0.3.15", - "Wabbajack.RateLimiter": "0.3.15" + "Wabbajack.Common": "0.3.16", + "Wabbajack.Downloaders.Interfaces": "0.3.16", + "Wabbajack.Networking.Http": "0.3.16", + "Wabbajack.RateLimiter": "0.3.16" }, "runtime": { "Wabbajack.Downloaders.WabbajackCDN.dll": {} } }, - "Wabbajack.DTOs/0.3.15": { + "Wabbajack.DTOs/0.3.16": { "dependencies": { "Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", - "Wabbajack.Hashing.xxHash64": "0.3.15", - "Wabbajack.Paths": "0.3.15" + "Wabbajack.Hashing.xxHash64": "0.3.16", + "Wabbajack.Paths": "0.3.16" }, "runtime": { "Wabbajack.DTOs.dll": {} } }, - "Wabbajack.FileExtractor/0.3.15": { + "Wabbajack.FileExtractor/0.3.16": { "dependencies": { "Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.Logging.Abstractions": "9.0.1", "OMODFramework": "3.0.1", - "Wabbajack.Common": "0.3.15", - "Wabbajack.Compression.BSA": "0.3.15", - "Wabbajack.Hashing.PHash": "0.3.15", - "Wabbajack.Paths": "0.3.15" + "Wabbajack.Common": "0.3.16", + "Wabbajack.Compression.BSA": "0.3.16", + "Wabbajack.Hashing.PHash": "0.3.16", + "Wabbajack.Paths": "0.3.16" }, "runtime": { "Wabbajack.FileExtractor.dll": {} } }, - "Wabbajack.Hashing.PHash/0.3.15": { + "Wabbajack.Hashing.PHash/0.3.16": { "dependencies": { "BCnEncoder.Net.ImageSharp": "1.1.1", "Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Shipwreck.Phash": "0.5.0", "SixLabors.ImageSharp": "3.1.6", - "Wabbajack.Common": "0.3.15", - "Wabbajack.DTOs": "0.3.15", - "Wabbajack.Paths": "0.3.15", - "Wabbajack.Paths.IO": "0.3.15" + "Wabbajack.Common": "0.3.16", + "Wabbajack.DTOs": "0.3.16", + "Wabbajack.Paths": "0.3.16", + "Wabbajack.Paths.IO": "0.3.16" }, "runtime": { "Wabbajack.Hashing.PHash.dll": {} } }, - "Wabbajack.Hashing.xxHash64/0.3.15": { + "Wabbajack.Hashing.xxHash64/0.3.16": { "dependencies": { - "Wabbajack.Paths": "0.3.15", - "Wabbajack.RateLimiter": "0.3.15" + "Wabbajack.Paths": "0.3.16", + "Wabbajack.RateLimiter": "0.3.16" }, "runtime": { "Wabbajack.Hashing.xxHash64.dll": {} } }, - "Wabbajack.Installer/0.3.15": { + "Wabbajack.Installer/0.3.16": { "dependencies": { "Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Newtonsoft.Json": "13.0.3", "Octopus.Octodiff": "2.0.548", "SixLabors.ImageSharp": "3.1.6", - "Wabbajack.DTOs": "0.3.15", - "Wabbajack.Downloaders.Dispatcher": "0.3.15", - "Wabbajack.Downloaders.GameFile": "0.3.15", - "Wabbajack.FileExtractor": "0.3.15", - "Wabbajack.Networking.WabbajackClientApi": "0.3.15", - "Wabbajack.Paths": "0.3.15", - "Wabbajack.Paths.IO": "0.3.15", - "Wabbajack.VFS": "0.3.15", + "Wabbajack.DTOs": "0.3.16", + "Wabbajack.Downloaders.Dispatcher": "0.3.16", + "Wabbajack.Downloaders.GameFile": "0.3.16", + "Wabbajack.FileExtractor": "0.3.16", + "Wabbajack.Networking.WabbajackClientApi": "0.3.16", + "Wabbajack.Paths": "0.3.16", + "Wabbajack.Paths.IO": "0.3.16", + "Wabbajack.VFS": "0.3.16", "ini-parser-netstandard": "2.5.2" }, "runtime": { "Wabbajack.Installer.dll": {} } }, - "Wabbajack.IO.Async/0.3.15": { + "Wabbajack.IO.Async/0.3.16": { "runtime": { "Wabbajack.IO.Async.dll": {} } }, - "Wabbajack.Networking.BethesdaNet/0.3.15": { + "Wabbajack.Networking.BethesdaNet/0.3.16": { "dependencies": { "Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", - "Wabbajack.DTOs": "0.3.15", - "Wabbajack.Networking.Http": "0.3.15", - "Wabbajack.Networking.Http.Interfaces": "0.3.15" + "Wabbajack.DTOs": "0.3.16", + "Wabbajack.Networking.Http": "0.3.16", + "Wabbajack.Networking.Http.Interfaces": "0.3.16" }, "runtime": { "Wabbajack.Networking.BethesdaNet.dll": {} } }, - "Wabbajack.Networking.Discord/0.3.15": { + "Wabbajack.Networking.Discord/0.3.16": { "dependencies": { "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.Logging.Abstractions": "9.0.1", - "Wabbajack.Networking.Http.Interfaces": "0.3.15" + "Wabbajack.Networking.Http.Interfaces": "0.3.16" }, "runtime": { "Wabbajack.Networking.Discord.dll": {} } }, - "Wabbajack.Networking.GitHub/0.3.15": { + "Wabbajack.Networking.GitHub/0.3.16": { "dependencies": { "Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.Logging.Abstractions": "9.0.1", "Octokit": "14.0.0", - "Wabbajack.DTOs": "0.3.15", - "Wabbajack.Networking.Http.Interfaces": "0.3.15" + "Wabbajack.DTOs": "0.3.16", + "Wabbajack.Networking.Http.Interfaces": "0.3.16" }, "runtime": { "Wabbajack.Networking.GitHub.dll": {} } }, - "Wabbajack.Networking.Http/0.3.15": { + "Wabbajack.Networking.Http/0.3.16": { "dependencies": { "Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.Http": "9.0.1", "Microsoft.Extensions.Logging": "9.0.1", - "Wabbajack.Configuration": "0.3.15", - "Wabbajack.Downloaders.Interfaces": "0.3.15", - "Wabbajack.Hashing.xxHash64": "0.3.15", - "Wabbajack.Networking.Http.Interfaces": "0.3.15", - "Wabbajack.Paths": "0.3.15", - "Wabbajack.Paths.IO": "0.3.15" + "Wabbajack.Configuration": "0.3.16", + "Wabbajack.Downloaders.Interfaces": "0.3.16", + "Wabbajack.Hashing.xxHash64": "0.3.16", + "Wabbajack.Networking.Http.Interfaces": "0.3.16", + "Wabbajack.Paths": "0.3.16", + "Wabbajack.Paths.IO": "0.3.16" }, "runtime": { "Wabbajack.Networking.Http.dll": {} } }, - "Wabbajack.Networking.Http.Interfaces/0.3.15": { + "Wabbajack.Networking.Http.Interfaces/0.3.16": { "dependencies": { - "Wabbajack.Hashing.xxHash64": "0.3.15" + "Wabbajack.Hashing.xxHash64": "0.3.16" }, "runtime": { "Wabbajack.Networking.Http.Interfaces.dll": {} } }, - "Wabbajack.Networking.NexusApi/0.3.15": { + "Wabbajack.Networking.NexusApi/0.3.16": { "dependencies": { "Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.Logging.Abstractions": "9.0.1", - "Wabbajack.DTOs": "0.3.15", - "Wabbajack.Networking.Http": "0.3.15", - "Wabbajack.Networking.Http.Interfaces": "0.3.15", - "Wabbajack.Networking.WabbajackClientApi": "0.3.15" + "Wabbajack.DTOs": "0.3.16", + "Wabbajack.Networking.Http": "0.3.16", + "Wabbajack.Networking.Http.Interfaces": "0.3.16", + "Wabbajack.Networking.WabbajackClientApi": "0.3.16" }, "runtime": { "Wabbajack.Networking.NexusApi.dll": {} } }, - "Wabbajack.Networking.WabbajackClientApi/0.3.15": { + "Wabbajack.Networking.WabbajackClientApi/0.3.16": { "dependencies": { "Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.Logging.Abstractions": "9.0.1", "Octokit": "14.0.0", - "Wabbajack.Common": "0.3.15", - "Wabbajack.DTOs": "0.3.15", - "Wabbajack.Paths.IO": "0.3.15", - "Wabbajack.VFS.Interfaces": "0.3.15", + "Wabbajack.Common": "0.3.16", + "Wabbajack.DTOs": "0.3.16", + "Wabbajack.Paths.IO": "0.3.16", + "Wabbajack.VFS.Interfaces": "0.3.16", "YamlDotNet": "16.3.0" }, "runtime": { "Wabbajack.Networking.WabbajackClientApi.dll": {} } }, - "Wabbajack.Paths/0.3.15": { + "Wabbajack.Paths/0.3.16": { "runtime": { "Wabbajack.Paths.dll": {} } }, - "Wabbajack.Paths.IO/0.3.15": { + "Wabbajack.Paths.IO/0.3.16": { "dependencies": { - "Wabbajack.Paths": "0.3.15", + "Wabbajack.Paths": "0.3.16", "shortid": "4.0.0" }, "runtime": { "Wabbajack.Paths.IO.dll": {} } }, - "Wabbajack.RateLimiter/0.3.15": { + "Wabbajack.RateLimiter/0.3.16": { "runtime": { "Wabbajack.RateLimiter.dll": {} } }, - "Wabbajack.Server.Lib/0.3.15": { + "Wabbajack.Server.Lib/0.3.16": { "dependencies": { "FluentFTP": "52.0.0", "Microsoft.Extensions.DependencyInjection": "9.0.1", @@ -2264,58 +2264,58 @@ "Nettle": "3.0.0", "Newtonsoft.Json": "13.0.3", "SixLabors.ImageSharp": "3.1.6", - "Wabbajack.Common": "0.3.15", - "Wabbajack.Networking.Http.Interfaces": "0.3.15", - "Wabbajack.Services.OSIntegrated": "0.3.15" + "Wabbajack.Common": "0.3.16", + "Wabbajack.Networking.Http.Interfaces": "0.3.16", + "Wabbajack.Services.OSIntegrated": "0.3.16" }, "runtime": { "Wabbajack.Server.Lib.dll": {} } }, - "Wabbajack.Services.OSIntegrated/0.3.15": { + "Wabbajack.Services.OSIntegrated/0.3.16": { "dependencies": { "DeviceId": "6.8.0", "Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Newtonsoft.Json": "13.0.3", "SixLabors.ImageSharp": "3.1.6", - "Wabbajack.Compiler": "0.3.15", - "Wabbajack.Downloaders.Dispatcher": "0.3.15", - "Wabbajack.Installer": "0.3.15", - "Wabbajack.Networking.BethesdaNet": "0.3.15", - "Wabbajack.Networking.Discord": "0.3.15", - "Wabbajack.VFS": "0.3.15" + "Wabbajack.Compiler": "0.3.16", + "Wabbajack.Downloaders.Dispatcher": "0.3.16", + "Wabbajack.Installer": "0.3.16", + "Wabbajack.Networking.BethesdaNet": "0.3.16", + "Wabbajack.Networking.Discord": "0.3.16", + "Wabbajack.VFS": "0.3.16" }, "runtime": { "Wabbajack.Services.OSIntegrated.dll": {} } }, - "Wabbajack.VFS/0.3.15": { + "Wabbajack.VFS/0.3.16": { "dependencies": { "Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.Logging.Abstractions": "9.0.1", "SixLabors.ImageSharp": "3.1.6", "System.Data.SQLite.Core": "1.0.119", - "Wabbajack.Common": "0.3.15", - "Wabbajack.FileExtractor": "0.3.15", - "Wabbajack.Hashing.PHash": "0.3.15", - "Wabbajack.Hashing.xxHash64": "0.3.15", - "Wabbajack.Paths": "0.3.15", - "Wabbajack.Paths.IO": "0.3.15", - "Wabbajack.VFS.Interfaces": "0.3.15" + "Wabbajack.Common": "0.3.16", + "Wabbajack.FileExtractor": "0.3.16", + "Wabbajack.Hashing.PHash": "0.3.16", + "Wabbajack.Hashing.xxHash64": "0.3.16", + "Wabbajack.Paths": "0.3.16", + "Wabbajack.Paths.IO": "0.3.16", + "Wabbajack.VFS.Interfaces": "0.3.16" }, "runtime": { "Wabbajack.VFS.dll": {} } }, - "Wabbajack.VFS.Interfaces/0.3.15": { + "Wabbajack.VFS.Interfaces/0.3.16": { "dependencies": { "Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", - "Wabbajack.DTOs": "0.3.15", - "Wabbajack.Hashing.xxHash64": "0.3.15", - "Wabbajack.Paths": "0.3.15" + "Wabbajack.DTOs": "0.3.16", + "Wabbajack.Hashing.xxHash64": "0.3.16", + "Wabbajack.Paths": "0.3.16" }, "runtime": { "Wabbajack.VFS.Interfaces.dll": {} @@ -2332,7 +2332,7 @@ } }, "libraries": { - "jackify-engine/0.3.15": { + "jackify-engine/0.3.16": { "type": "project", "serviceable": false, "sha512": "" @@ -3021,202 +3021,202 @@ "path": "yamldotnet/16.3.0", "hashPath": "yamldotnet.16.3.0.nupkg.sha512" }, - "Wabbajack.CLI.Builder/0.3.15": { + "Wabbajack.CLI.Builder/0.3.16": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Common/0.3.15": { + "Wabbajack.Common/0.3.16": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Compiler/0.3.15": { + "Wabbajack.Compiler/0.3.16": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Compression.BSA/0.3.15": { + "Wabbajack.Compression.BSA/0.3.16": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Compression.Zip/0.3.15": { + "Wabbajack.Compression.Zip/0.3.16": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Configuration/0.3.15": { + "Wabbajack.Configuration/0.3.16": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Downloaders.Bethesda/0.3.15": { + "Wabbajack.Downloaders.Bethesda/0.3.16": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Downloaders.Dispatcher/0.3.15": { + "Wabbajack.Downloaders.Dispatcher/0.3.16": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Downloaders.GameFile/0.3.15": { + "Wabbajack.Downloaders.GameFile/0.3.16": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Downloaders.GoogleDrive/0.3.15": { + "Wabbajack.Downloaders.GoogleDrive/0.3.16": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Downloaders.Http/0.3.15": { + "Wabbajack.Downloaders.Http/0.3.16": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Downloaders.Interfaces/0.3.15": { + "Wabbajack.Downloaders.Interfaces/0.3.16": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Downloaders.IPS4OAuth2Downloader/0.3.15": { + "Wabbajack.Downloaders.IPS4OAuth2Downloader/0.3.16": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Downloaders.Manual/0.3.15": { + "Wabbajack.Downloaders.Manual/0.3.16": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Downloaders.MediaFire/0.3.15": { + "Wabbajack.Downloaders.MediaFire/0.3.16": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Downloaders.Mega/0.3.15": { + "Wabbajack.Downloaders.Mega/0.3.16": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Downloaders.ModDB/0.3.15": { + "Wabbajack.Downloaders.ModDB/0.3.16": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Downloaders.Nexus/0.3.15": { + "Wabbajack.Downloaders.Nexus/0.3.16": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Downloaders.VerificationCache/0.3.15": { + "Wabbajack.Downloaders.VerificationCache/0.3.16": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Downloaders.WabbajackCDN/0.3.15": { + "Wabbajack.Downloaders.WabbajackCDN/0.3.16": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.DTOs/0.3.15": { + "Wabbajack.DTOs/0.3.16": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.FileExtractor/0.3.15": { + "Wabbajack.FileExtractor/0.3.16": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Hashing.PHash/0.3.15": { + "Wabbajack.Hashing.PHash/0.3.16": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Hashing.xxHash64/0.3.15": { + "Wabbajack.Hashing.xxHash64/0.3.16": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Installer/0.3.15": { + "Wabbajack.Installer/0.3.16": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.IO.Async/0.3.15": { + "Wabbajack.IO.Async/0.3.16": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Networking.BethesdaNet/0.3.15": { + "Wabbajack.Networking.BethesdaNet/0.3.16": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Networking.Discord/0.3.15": { + "Wabbajack.Networking.Discord/0.3.16": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Networking.GitHub/0.3.15": { + "Wabbajack.Networking.GitHub/0.3.16": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Networking.Http/0.3.15": { + "Wabbajack.Networking.Http/0.3.16": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Networking.Http.Interfaces/0.3.15": { + "Wabbajack.Networking.Http.Interfaces/0.3.16": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Networking.NexusApi/0.3.15": { + "Wabbajack.Networking.NexusApi/0.3.16": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Networking.WabbajackClientApi/0.3.15": { + "Wabbajack.Networking.WabbajackClientApi/0.3.16": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Paths/0.3.15": { + "Wabbajack.Paths/0.3.16": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Paths.IO/0.3.15": { + "Wabbajack.Paths.IO/0.3.16": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.RateLimiter/0.3.15": { + "Wabbajack.RateLimiter/0.3.16": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Server.Lib/0.3.15": { + "Wabbajack.Server.Lib/0.3.16": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Services.OSIntegrated/0.3.15": { + "Wabbajack.Services.OSIntegrated/0.3.16": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.VFS/0.3.15": { + "Wabbajack.VFS/0.3.16": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.VFS.Interfaces/0.3.15": { + "Wabbajack.VFS.Interfaces/0.3.16": { "type": "project", "serviceable": false, "sha512": "" diff --git a/jackify/engine/jackify-engine.dll b/jackify/engine/jackify-engine.dll index fee0c2c..8966d15 100644 Binary files a/jackify/engine/jackify-engine.dll and b/jackify/engine/jackify-engine.dll differ diff --git a/jackify/frontends/gui/main.py b/jackify/frontends/gui/main.py index a894061..f1b9d17 100644 --- a/jackify/frontends/gui/main.py +++ b/jackify/frontends/gui/main.py @@ -102,7 +102,7 @@ sys.path.insert(0, str(src_dir)) from PySide6.QtWidgets import ( QApplication, QMainWindow, QWidget, QLabel, QVBoxLayout, QPushButton, - QStackedWidget, QHBoxLayout, QDialog, QFormLayout, QLineEdit, QCheckBox, QSpinBox, QMessageBox, QGroupBox, QGridLayout, QFileDialog, QToolButton, QStyle, QComboBox + QStackedWidget, QHBoxLayout, QDialog, QFormLayout, QLineEdit, QCheckBox, QSpinBox, QMessageBox, QGroupBox, QGridLayout, QFileDialog, QToolButton, QStyle, QComboBox, QTabWidget ) from PySide6.QtCore import Qt, QEvent from PySide6.QtGui import QIcon @@ -167,221 +167,26 @@ class SettingsDialog(QDialog): self._original_debug_mode = self.config_handler.get('debug_mode', False) self.setWindowTitle("Settings") self.setModal(True) - self.setMinimumWidth(750) + self.setMinimumWidth(650) # Reduced width for Steam Deck compatibility + self.setMaximumWidth(800) # Maximum width to prevent excessive stretching self.setStyleSheet("QDialog { background-color: #232323; color: #eee; } QPushButton:hover { background-color: #333; }") + main_layout = QVBoxLayout() self.setLayout(main_layout) - # --- Resource Limits Section --- - resource_group = QGroupBox("Resource Limits") - resource_group.setStyleSheet("QGroupBox { border: 1px solid #555; border-radius: 6px; margin-top: 8px; padding: 8px; background: #23282d; } QGroupBox:title { subcontrol-origin: margin; left: 10px; padding: 0 3px 0 3px; font-weight: bold; color: #fff; }") - resource_layout = QGridLayout() - resource_group.setLayout(resource_layout) - resource_layout.setVerticalSpacing(4) - resource_layout.setHorizontalSpacing(8) - resource_layout.addWidget(self._bold_label("Resource"), 0, 0, 1, 1, Qt.AlignLeft) - resource_layout.addWidget(self._bold_label("Max Tasks"), 0, 1, 1, 1, Qt.AlignLeft) - self.resource_settings_path = os.path.expanduser("~/.config/jackify/resource_settings.json") - self.resource_settings = self._load_json(self.resource_settings_path) - self.resource_edits = {} - resource_row_index = 0 - for resource_row_index, (k, v) in enumerate(self.resource_settings.items(), start=1): - try: - # Create resource label with optional inline checkbox for File Extractor - if k == "File Extractor": - # Create horizontal layout for File Extractor with inline checkbox - resource_row = QHBoxLayout() - resource_label = QLabel(f"{k}:", parent=self) - resource_row.addWidget(resource_label) - resource_row.addSpacing(10) # Add some spacing - - multithreading_checkbox = QCheckBox("Multithreading (Experimental)") - multithreading_checkbox.setChecked(v.get('_7zzMultiThread', 'off') == 'on') - multithreading_checkbox.setToolTip("Enables multithreaded file extraction using 7-Zip. May improve extraction speed on multi-core systems but could be less stable.") - multithreading_checkbox.setStyleSheet("color: #fff;") - resource_row.addWidget(multithreading_checkbox) - resource_row.addStretch() # Push checkbox to the left - - # Add the horizontal layout to the grid - resource_layout.addLayout(resource_row, resource_row_index, 0) - else: - resource_layout.addWidget(QLabel(f"{k}:", parent=self), resource_row_index, 0, 1, 1, Qt.AlignLeft) - - max_tasks_spin = QSpinBox() - max_tasks_spin.setMinimum(1) - max_tasks_spin.setMaximum(128) - max_tasks_spin.setValue(v.get('MaxTasks', 16)) - max_tasks_spin.setToolTip("Maximum number of concurrent tasks for this resource.") - max_tasks_spin.setFixedWidth(160) - resource_layout.addWidget(max_tasks_spin, resource_row_index, 1) - - # Store the widgets (checkbox for File Extractor, None for others) - if k == "File Extractor": - self.resource_edits[k] = (multithreading_checkbox, max_tasks_spin) - else: - self.resource_edits[k] = (None, max_tasks_spin) - except Exception as e: - print(f"[ERROR] Failed to create widgets for resource '{k}': {e}") - continue - - # If no resources exist, show helpful message - if not self.resource_edits: - info_label = QLabel("Resource Limit settings will be generated once a modlist install action is performed") - info_label.setStyleSheet("color: #aaa; font-style: italic; padding: 20px; font-size: 11pt;") - info_label.setWordWrap(True) - info_label.setAlignment(Qt.AlignCenter) - info_label.setMinimumHeight(60) # Ensure enough height to prevent cutoff - resource_layout.addWidget(info_label, 1, 0, 3, 2) # Span more rows for better space - - # Bandwidth limiter row (only show if Downloads resource exists) - if "Downloads" in self.resource_settings: - downloads_throughput = self.resource_settings["Downloads"].get("MaxThroughput", 0) - - self.bandwidth_spin = QSpinBox() - self.bandwidth_spin.setMinimum(0) - self.bandwidth_spin.setMaximum(1000000) - self.bandwidth_spin.setValue(downloads_throughput) - self.bandwidth_spin.setSuffix(" KB/s") - self.bandwidth_spin.setFixedWidth(160) - self.bandwidth_spin.setToolTip("Set the maximum download speed for modlist downloads. 0 = unlimited.") - bandwidth_note = QLabel("(0 = unlimited)") - bandwidth_note.setStyleSheet("color: #aaa; font-size: 10pt;") - # Create horizontal layout for bandwidth row - bandwidth_row = QHBoxLayout() - bandwidth_row.addWidget(self.bandwidth_spin) - bandwidth_row.addWidget(bandwidth_note) - bandwidth_row.addStretch() # Push to the left - - resource_layout.addWidget(QLabel("Bandwidth Limit:", parent=self), resource_row_index+1, 0, 1, 1, Qt.AlignLeft) - resource_layout.addLayout(bandwidth_row, resource_row_index+1, 1) - else: - self.bandwidth_spin = None # No bandwidth UI if Downloads resource doesn't exist - main_layout.addWidget(resource_group) - main_layout.addSpacing(12) + # Create tab widget + self.tab_widget = QTabWidget() + self.tab_widget.setStyleSheet(""" + QTabWidget::pane { border: 1px solid #555; background: #232323; } + QTabBar::tab { background: #333; color: #eee; padding: 8px 16px; margin: 2px; } + QTabBar::tab:selected { background: #555; } + QTabBar::tab:hover { background: #444; } + """) + main_layout.addWidget(self.tab_widget) - # --- Debug & Diagnostics Section --- - debug_group = QGroupBox("Debug & Diagnostics") - debug_group.setStyleSheet("QGroupBox { border: 1px solid #555; border-radius: 6px; margin-top: 8px; padding: 8px; background: #23282d; } QGroupBox:title { subcontrol-origin: margin; left: 10px; padding: 0 3px 0 3px; font-weight: bold; color: #fff; }") - debug_layout = QVBoxLayout() - debug_group.setLayout(debug_layout) - self.debug_checkbox = QCheckBox("Enable debug mode (requires restart)") - # Load debug_mode from config - self.debug_checkbox.setChecked(self.config_handler.get('debug_mode', False)) - self.debug_checkbox.setToolTip("Enable verbose debug logging. Requires Jackify restart to take effect.") - self.debug_checkbox.setStyleSheet("color: #fff;") - debug_layout.addWidget(self.debug_checkbox) - main_layout.addWidget(debug_group) - main_layout.addSpacing(12) - - # --- Nexus API Key Section --- - api_group = QGroupBox("Nexus API Key") - api_group.setStyleSheet("QGroupBox { border: 1px solid #555; border-radius: 6px; margin-top: 8px; padding: 8px; background: #23282d; } QGroupBox:title { subcontrol-origin: margin; left: 10px; padding: 0 3px 0 3px; font-weight: bold; color: #fff; }") - api_layout = QHBoxLayout() - api_group.setLayout(api_layout) - self.api_key_edit = QLineEdit() - self.api_key_edit.setEchoMode(QLineEdit.Password) - api_key = self.config_handler.get_api_key() - if api_key: - self.api_key_edit.setText(api_key) - else: - self.api_key_edit.setText("") - self.api_key_edit.setToolTip("Your Nexus API Key (obfuscated by default, click Show to reveal)") - # Connect for immediate saving when text changes - self.api_key_edit.textChanged.connect(self._on_api_key_changed) - self.api_show_btn = QToolButton() - self.api_show_btn.setCheckable(True) - self.api_show_btn.setIcon(QIcon.fromTheme("view-visible")) - self.api_show_btn.setToolTip("Show or hide your API key") - self.api_show_btn.toggled.connect(self._toggle_api_key_visibility) - self.api_show_btn.setStyleSheet("") - clear_api_btn = QPushButton("Clear API Key") - clear_api_btn.clicked.connect(self._clear_api_key) - api_layout.addWidget(QLabel("Nexus API Key:")) - api_layout.addWidget(self.api_key_edit) - api_layout.addWidget(self.api_show_btn) - api_layout.addWidget(clear_api_btn) - main_layout.addWidget(api_group) - main_layout.addSpacing(12) - - # --- Proton Version Section --- - proton_group = QGroupBox("Proton Version") - proton_group.setStyleSheet("QGroupBox { border: 1px solid #555; border-radius: 6px; margin-top: 8px; padding: 8px; background: #23282d; } QGroupBox:title { subcontrol-origin: margin; left: 10px; padding: 0 3px 0 3px; font-weight: bold; color: #fff; }") - proton_layout = QHBoxLayout() - proton_group.setLayout(proton_layout) - - self.proton_dropdown = QComboBox() - self.proton_dropdown.setToolTip("Select Proton version for shortcut creation and texture processing") - self.proton_dropdown.setMinimumWidth(200) - - # Populate Proton dropdown - self._populate_proton_dropdown() - - # Refresh button for Proton detection - refresh_btn = QPushButton("↻") - refresh_btn.setFixedSize(30, 30) - refresh_btn.setToolTip("Refresh Proton version list") - refresh_btn.clicked.connect(self._refresh_proton_dropdown) - - proton_layout.addWidget(QLabel("Proton Version:")) - proton_layout.addWidget(self.proton_dropdown) - proton_layout.addWidget(refresh_btn) - proton_layout.addStretch() - - main_layout.addWidget(proton_group) - main_layout.addSpacing(12) - - # --- Directories & Paths Section --- - dir_group = QGroupBox("Directories & Paths") - dir_group.setStyleSheet("QGroupBox { border: 1px solid #555; border-radius: 6px; margin-top: 8px; padding: 8px; background: #23282d; } QGroupBox:title { subcontrol-origin: margin; left: 10px; padding: 0 3px 0 3px; font-weight: bold; color: #fff; }") - dir_layout = QFormLayout() - dir_group.setLayout(dir_layout) - self.install_dir_edit = QLineEdit(self.config_handler.get("modlist_install_base_dir", "")) - self.install_dir_edit.setToolTip("Default directory for modlist installations.") - self.install_dir_btn = QPushButton() - self.install_dir_btn.setIcon(QIcon.fromTheme("folder-open")) - self.install_dir_btn.setToolTip("Browse for directory") - self.install_dir_btn.setFixedWidth(32) - self.install_dir_btn.clicked.connect(lambda: self._pick_directory(self.install_dir_edit)) - install_dir_row = QHBoxLayout() - install_dir_row.addWidget(self.install_dir_edit) - install_dir_row.addWidget(self.install_dir_btn) - dir_layout.addRow(QLabel("Install Base Dir:"), install_dir_row) - self.download_dir_edit = QLineEdit(self.config_handler.get("modlist_downloads_base_dir", "")) - self.download_dir_edit.setToolTip("Default directory for modlist downloads.") - self.download_dir_btn = QPushButton() - self.download_dir_btn.setIcon(QIcon.fromTheme("folder-open")) - self.download_dir_btn.setToolTip("Browse for directory") - self.download_dir_btn.setFixedWidth(32) - self.download_dir_btn.clicked.connect(lambda: self._pick_directory(self.download_dir_edit)) - download_dir_row = QHBoxLayout() - download_dir_row.addWidget(self.download_dir_edit) - download_dir_row.addWidget(self.download_dir_btn) - dir_layout.addRow(QLabel("Downloads Base Dir:"), download_dir_row) - - # Jackify Data Directory - from jackify.shared.paths import get_jackify_data_dir - current_jackify_dir = str(get_jackify_data_dir()) - self.jackify_data_dir_edit = QLineEdit(current_jackify_dir) - self.jackify_data_dir_edit.setToolTip("Directory for Jackify data (logs, downloads, temp files). Default: ~/Jackify") - self.jackify_data_dir_btn = QPushButton() - self.jackify_data_dir_btn.setIcon(QIcon.fromTheme("folder-open")) - self.jackify_data_dir_btn.setToolTip("Browse for directory") - self.jackify_data_dir_btn.setFixedWidth(32) - self.jackify_data_dir_btn.clicked.connect(lambda: self._pick_directory(self.jackify_data_dir_edit)) - jackify_data_dir_row = QHBoxLayout() - jackify_data_dir_row.addWidget(self.jackify_data_dir_edit) - jackify_data_dir_row.addWidget(self.jackify_data_dir_btn) - - # Reset to default button - reset_jackify_dir_btn = QPushButton("Reset") - reset_jackify_dir_btn.setToolTip("Reset to default (~/ Jackify)") - reset_jackify_dir_btn.setFixedWidth(50) - reset_jackify_dir_btn.clicked.connect(lambda: self.jackify_data_dir_edit.setText(str(Path.home() / "Jackify"))) - jackify_data_dir_row.addWidget(reset_jackify_dir_btn) - - dir_layout.addRow(QLabel("Jackify Data Dir:"), jackify_data_dir_row) - main_layout.addWidget(dir_group) - main_layout.addSpacing(12) + # Create tabs + self._create_general_tab() + self._create_advanced_tab() # --- Save/Close/Help Buttons --- btn_layout = QHBoxLayout() @@ -396,36 +201,224 @@ class SettingsDialog(QDialog): close_btn.clicked.connect(self.reject) btn_layout.addWidget(save_btn) btn_layout.addWidget(close_btn) + + # Add error label for validation messages + self.error_label = QLabel("") + self.error_label.setStyleSheet("QLabel { color: #ff6b6b; }") + main_layout.addWidget(self.error_label) + main_layout.addSpacing(10) main_layout.addLayout(btn_layout) - # Set tab order for accessibility - # Get the first resource's widgets if any exist - if self.resource_edits: - first_resource_key = list(self.resource_edits.keys())[0] - first_multithreading, first_max_tasks = self.resource_edits[first_resource_key] - # Set tab order starting with the first max tasks spinner - self.setTabOrder(first_max_tasks, self.bandwidth_spin) - # Continue with bandwidth spinner regardless of resources - self.setTabOrder(self.bandwidth_spin, self.debug_checkbox) - self.setTabOrder(self.debug_checkbox, self.api_key_edit) - self.setTabOrder(self.api_key_edit, self.api_show_btn) - self.setTabOrder(self.api_show_btn, clear_api_btn) - self.setTabOrder(clear_api_btn, self.install_dir_edit) - self.setTabOrder(self.install_dir_edit, self.install_dir_btn) - self.setTabOrder(self.install_dir_btn, self.download_dir_edit) - self.setTabOrder(self.download_dir_edit, self.download_dir_btn) - self.setTabOrder(self.download_dir_btn, save_btn) - self.setTabOrder(save_btn, close_btn) - - self.error_label = QLabel("") - self.error_label.setStyleSheet("color: #f55; font-weight: bold;") - main_layout.insertWidget(0, self.error_label) except Exception as e: - print(f"[ERROR] Exception in SettingsDialog __init__: {e}") + print(f"[ERROR] Exception in SettingsDialog.__init__: {e}") import traceback traceback.print_exc() - raise + + def _create_general_tab(self): + """Create the General settings tab""" + general_tab = QWidget() + general_layout = QVBoxLayout(general_tab) + + # --- Directory Paths Section (moved to top as most essential) --- + dir_group = QGroupBox("Directory Paths") + dir_group.setStyleSheet("QGroupBox { border: 1px solid #555; border-radius: 6px; margin-top: 8px; padding: 8px; background: #23282d; } QGroupBox:title { subcontrol-origin: margin; left: 10px; padding: 0 3px 0 3px; font-weight: bold; color: #fff; }") + dir_layout = QFormLayout() + dir_group.setLayout(dir_layout) + self.install_dir_edit = QLineEdit(self.config_handler.get("modlist_install_base_dir", "")) + self.install_dir_edit.setToolTip("Default directory for modlist installations.") + self.install_dir_btn = QPushButton() + self.install_dir_btn.setIcon(QIcon.fromTheme("folder-open")) + self.install_dir_btn.setToolTip("Browse for directory") + self.install_dir_btn.setFixedWidth(32) + self.install_dir_btn.clicked.connect(lambda: self._pick_directory(self.install_dir_edit)) + install_dir_row = QHBoxLayout() + install_dir_row.addWidget(self.install_dir_edit) + install_dir_row.addWidget(self.install_dir_btn) + dir_layout.addRow(QLabel("Install Base Dir:"), install_dir_row) + self.download_dir_edit = QLineEdit(self.config_handler.get("modlist_downloads_base_dir", "")) + self.download_dir_edit.setToolTip("Default directory for modlist downloads.") + self.download_dir_btn = QPushButton() + self.download_dir_btn.setIcon(QIcon.fromTheme("folder-open")) + self.download_dir_btn.setToolTip("Browse for directory") + self.download_dir_btn.setFixedWidth(32) + self.download_dir_btn.clicked.connect(lambda: self._pick_directory(self.download_dir_edit)) + download_dir_row = QHBoxLayout() + download_dir_row.addWidget(self.download_dir_edit) + download_dir_row.addWidget(self.download_dir_btn) + dir_layout.addRow(QLabel("Downloads Base Dir:"), download_dir_row) + + # Jackify Data Directory + from jackify.shared.paths import get_jackify_data_dir + current_jackify_dir = str(get_jackify_data_dir()) + self.jackify_data_dir_edit = QLineEdit(current_jackify_dir) + self.jackify_data_dir_edit.setToolTip("Directory for Jackify data (logs, downloads, temp files). Default: ~/Jackify") + self.jackify_data_dir_btn = QPushButton() + self.jackify_data_dir_btn.setIcon(QIcon.fromTheme("folder-open")) + self.jackify_data_dir_btn.setToolTip("Browse for directory") + self.jackify_data_dir_btn.setFixedWidth(32) + self.jackify_data_dir_btn.clicked.connect(lambda: self._pick_directory(self.jackify_data_dir_edit)) + jackify_data_dir_row = QHBoxLayout() + jackify_data_dir_row.addWidget(self.jackify_data_dir_edit) + jackify_data_dir_row.addWidget(self.jackify_data_dir_btn) + + # Reset to default button + reset_jackify_dir_btn = QPushButton("Reset") + reset_jackify_dir_btn.setToolTip("Reset to default (~/ Jackify)") + reset_jackify_dir_btn.setFixedWidth(50) + reset_jackify_dir_btn.clicked.connect(lambda: self.jackify_data_dir_edit.setText(str(Path.home() / "Jackify"))) + jackify_data_dir_row.addWidget(reset_jackify_dir_btn) + + dir_layout.addRow(QLabel("Jackify Data Dir:"), jackify_data_dir_row) + general_layout.addWidget(dir_group) + general_layout.addSpacing(12) + + # --- Nexus API Key Section --- + api_group = QGroupBox("Nexus API Key") + api_group.setStyleSheet("QGroupBox { border: 1px solid #555; border-radius: 6px; margin-top: 8px; padding: 8px; background: #23282d; } QGroupBox:title { subcontrol-origin: margin; left: 10px; padding: 0 3px 0 3px; font-weight: bold; color: #fff; }") + api_layout = QHBoxLayout() + api_group.setLayout(api_layout) + self.api_key_edit = QLineEdit() + self.api_key_edit.setEchoMode(QLineEdit.Password) + api_key = self.config_handler.get_api_key() + if api_key: + self.api_key_edit.setText(api_key) + else: + self.api_key_edit.setText("") + self.api_key_edit.setToolTip("Your Nexus API Key (obfuscated by default, click Show to reveal)") + # Connect for immediate saving when text changes + self.api_key_edit.textChanged.connect(self._on_api_key_changed) + self.api_show_btn = QToolButton() + self.api_show_btn.setCheckable(True) + self.api_show_btn.setIcon(QIcon.fromTheme("view-visible")) + self.api_show_btn.setToolTip("Show or hide your API key") + self.api_show_btn.toggled.connect(self._toggle_api_key_visibility) + self.api_show_btn.setStyleSheet("") + clear_api_btn = QPushButton("Clear API Key") + clear_api_btn.clicked.connect(self._clear_api_key) + api_layout.addWidget(QLabel("Nexus API Key:")) + api_layout.addWidget(self.api_key_edit) + api_layout.addWidget(self.api_show_btn) + api_layout.addWidget(clear_api_btn) + general_layout.addWidget(api_group) + general_layout.addSpacing(12) + + # --- Default Proton Version Section --- + proton_group = QGroupBox("Default Proton Version") + proton_group.setStyleSheet("QGroupBox { border: 1px solid #555; border-radius: 6px; margin-top: 8px; padding: 8px; background: #23282d; } QGroupBox:title { subcontrol-origin: margin; left: 10px; padding: 0 3px 0 3px; font-weight: bold; color: #fff; }") + proton_layout = QHBoxLayout() + proton_group.setLayout(proton_layout) + + self.proton_dropdown = QComboBox() + self.proton_dropdown.setToolTip("Select default Proton version for shortcut creation and texture processing") + self.proton_dropdown.setMinimumWidth(200) + + # Populate Proton dropdown + self._populate_proton_dropdown() + + # Refresh button for Proton detection + refresh_btn = QPushButton("↻") + refresh_btn.setFixedSize(30, 30) + refresh_btn.setToolTip("Refresh Proton version list") + refresh_btn.clicked.connect(self._refresh_proton_dropdown) + + proton_layout.addWidget(QLabel("Proton Version:")) + proton_layout.addWidget(self.proton_dropdown) + proton_layout.addWidget(refresh_btn) + proton_layout.addStretch() + + general_layout.addWidget(proton_group) + general_layout.addSpacing(12) + + # --- Enable Debug Section (moved to bottom as advanced option) --- + debug_group = QGroupBox("Enable Debug") + debug_group.setStyleSheet("QGroupBox { border: 1px solid #555; border-radius: 6px; margin-top: 8px; padding: 8px; background: #23282d; } QGroupBox:title { subcontrol-origin: margin; left: 10px; padding: 0 3px 0 3px; font-weight: bold; color: #fff; }") + debug_layout = QVBoxLayout() + debug_group.setLayout(debug_layout) + self.debug_checkbox = QCheckBox("Enable debug mode (requires restart)") + # Load debug_mode from config + self.debug_checkbox.setChecked(self.config_handler.get('debug_mode', False)) + self.debug_checkbox.setToolTip("Enable verbose debug logging. Requires Jackify restart to take effect.") + self.debug_checkbox.setStyleSheet("color: #fff;") + debug_layout.addWidget(self.debug_checkbox) + general_layout.addWidget(debug_group) + general_layout.addStretch() # Add stretch to push content to top + + self.tab_widget.addTab(general_tab, "General") + + def _create_advanced_tab(self): + """Create the Advanced settings tab""" + advanced_tab = QWidget() + advanced_layout = QVBoxLayout(advanced_tab) + + resource_group = QGroupBox("Resource Limits") + resource_group.setStyleSheet("QGroupBox { border: 1px solid #555; border-radius: 6px; margin-top: 8px; padding: 8px; background: #23282d; } QGroupBox:title { subcontrol-origin: margin; left: 10px; padding: 0 3px 0 3px; font-weight: bold; color: #fff; }") + resource_layout = QGridLayout() + resource_group.setLayout(resource_layout) + resource_layout.setVerticalSpacing(4) + resource_layout.setHorizontalSpacing(8) + resource_layout.addWidget(self._bold_label("Resource"), 0, 0, 1, 1, Qt.AlignLeft) + resource_layout.addWidget(self._bold_label("Max Tasks"), 0, 1, 1, 1, Qt.AlignLeft) + self.resource_settings_path = os.path.expanduser("~/.config/jackify/resource_settings.json") + self.resource_settings = self._load_json(self.resource_settings_path) + self.resource_edits = {} + resource_row_index = 0 + for resource_row_index, (k, v) in enumerate(self.resource_settings.items(), start=1): + try: + # Create resource label + resource_layout.addWidget(QLabel(f"{k}:", parent=self), resource_row_index, 0, 1, 1, Qt.AlignLeft) + + max_tasks_spin = QSpinBox() + max_tasks_spin.setMinimum(1) + max_tasks_spin.setMaximum(128) + max_tasks_spin.setValue(v.get('MaxTasks', 16)) + max_tasks_spin.setToolTip("Maximum number of concurrent tasks for this resource.") + max_tasks_spin.setFixedWidth(160) + resource_layout.addWidget(max_tasks_spin, resource_row_index, 1) + + # Store the widgets + self.resource_edits[k] = (None, max_tasks_spin) + except Exception as e: + print(f"[ERROR] Failed to create widgets for resource '{k}': {e}") + continue + + # If no resources exist, show helpful message + if not self.resource_edits: + info_label = QLabel("Resource Limit settings will be generated once a modlist install action is performed") + info_label.setStyleSheet("color: #aaa; font-style: italic; padding: 20px; font-size: 11pt;") + info_label.setWordWrap(True) + info_label.setAlignment(Qt.AlignCenter) + info_label.setMinimumHeight(60) # Ensure enough height to prevent cutoff + resource_layout.addWidget(info_label, 1, 0, 3, 2) # Span more rows for better space + + # Bandwidth limiter row (only show if Downloads resource exists) + if "Downloads" in self.resource_settings: + downloads_throughput = self.resource_settings["Downloads"].get("MaxThroughput", 0) + + self.bandwidth_spin = QSpinBox() + self.bandwidth_spin.setMinimum(0) + self.bandwidth_spin.setMaximum(1000000) + self.bandwidth_spin.setValue(downloads_throughput) + self.bandwidth_spin.setSuffix(" KB/s") + self.bandwidth_spin.setFixedWidth(160) + self.bandwidth_spin.setToolTip("Set the maximum download speed for modlist downloads. 0 = unlimited.") + bandwidth_note = QLabel("(0 = unlimited)") + bandwidth_note.setStyleSheet("color: #aaa; font-size: 10pt;") + # Create horizontal layout for bandwidth row + bandwidth_row = QHBoxLayout() + bandwidth_row.addWidget(self.bandwidth_spin) + bandwidth_row.addWidget(bandwidth_note) + bandwidth_row.addStretch() # Push to the left + + resource_layout.addWidget(QLabel("Bandwidth Limit:", parent=self), resource_row_index+1, 0, 1, 1, Qt.AlignLeft) + resource_layout.addLayout(bandwidth_row, resource_row_index+1, 1) + else: + self.bandwidth_spin = None # No bandwidth UI if Downloads resource doesn't exist + + advanced_layout.addWidget(resource_group) + advanced_layout.addStretch() # Add stretch to push content to top + + self.tab_widget.addTab(advanced_tab, "Advanced") def _toggle_api_key_visibility(self, checked): # Always use the same eyeball icon, only change color when toggled @@ -572,13 +565,6 @@ class SettingsDialog(QDialog): for k, (multithreading_checkbox, max_tasks_spin) in self.resource_edits.items(): resource_data = self.resource_settings.get(k, {}) resource_data['MaxTasks'] = max_tasks_spin.value() - # Only add multithreading setting for File Extractor - if k == "File Extractor" and multithreading_checkbox: - if multithreading_checkbox.isChecked(): - resource_data['_7zzMultiThread'] = 'on' - else: - # Remove the setting if unchecked (don't add 'off') - resource_data.pop('_7zzMultiThread', None) self.resource_settings[k] = resource_data # Save bandwidth limit to Downloads resource MaxThroughput (only if bandwidth UI exists) diff --git a/jackify/frontends/gui/screens/install_modlist.py b/jackify/frontends/gui/screens/install_modlist.py index b6a000a..e6e674e 100644 --- a/jackify/frontends/gui/screens/install_modlist.py +++ b/jackify/frontends/gui/screens/install_modlist.py @@ -1757,6 +1757,10 @@ class InstallModlistScreen(QWidget): MessageService.critical(self, "Steam Restart Failed", "Failed to restart Steam automatically. Please restart Steam manually, then try again.") def start_automated_prefix_workflow(self): + # Ensure _current_resolution is always set before starting workflow + if not hasattr(self, '_current_resolution') or self._current_resolution is None: + resolution = self.resolution_combo.currentText() if hasattr(self, 'resolution_combo') else None + self._current_resolution = resolution.split()[0] if resolution and resolution != "Leave unchanged" else None """Start the automated prefix creation workflow""" try: # Disable controls during installation @@ -2382,7 +2386,7 @@ class InstallModlistScreen(QWidget): # This shouldn't happen since automated prefix creation is complete self.progress_update.emit(f"Unexpected manual steps callback for {modlist_name}") - # Call the service method for post-Steam configuration + # Call the service method for post-Steam configuration result = modlist_service.configure_modlist_post_steam( context=modlist_context, progress_callback=progress_callback, diff --git a/jackify/shared/validation.py b/jackify/shared/validation.py index 193d489..e3561d2 100644 --- a/jackify/shared/validation.py +++ b/jackify/shared/validation.py @@ -222,15 +222,21 @@ class ValidationHandler: def validate_steam_shortcut(self, app_id: str) -> Tuple[bool, str]: """Validate a Steam shortcut.""" try: - # Check if shortcuts.vdf exists - shortcuts_path = Path.home() / '.steam' / 'steam' / 'userdata' / '75424832' / 'config' / 'shortcuts.vdf' + # Use native Steam service to get proper shortcuts.vdf path with multi-user support + from jackify.backend.services.native_steam_service import NativeSteamService + steam_service = NativeSteamService() + shortcuts_path = steam_service.get_shortcuts_vdf_path() + + if not shortcuts_path: + return False, "Could not determine shortcuts.vdf path (no active Steam user found)" + if not shortcuts_path.exists(): return False, "shortcuts.vdf not found" - + # Check if shortcuts.vdf is accessible if not os.access(shortcuts_path, os.R_OK | os.W_OK): return False, "shortcuts.vdf is not accessible" - + # Parse shortcuts.vdf using VDFHandler shortcuts_data = VDFHandler.load(str(shortcuts_path), binary=True)