diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ccef11..1b7bede 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,33 @@ # Jackify Changelog +## v0.1.7.1 - Wine Component Verification & Flatpak Steam Fixes +**Release Date:** November 11, 2025 + +### Critical Bug Fixes +- **FIXED: Wine Component Installation Verification** - Jackify now verifies components are actually installed before reporting success + +### Bug Fixes +- **Steam Deck SD Card Paths**: Fixed ModOrganizer.ini path corruption on SD card installs using regex-based stripping +- **Flatpak Steam Detection**: Fixed libraryfolders.vdf path detection for Flatpak Steam installations +- **Flatpak Steam Restart**: Steam restart service now properly detects and controls Flatpak Steam +- **Path Manipulation**: Fixed path corruption in Configure Existing/New Modlist (paths with spaces) + +### Improvements +- Added network diagnostics before winetricks fallback to protontricks +- Enhanced component installation logging with verification status +- Added GE-Proton 10-14 recommendation to success message (ENB compatibility note for Valve's Proton 10) + +### Engine Updates +- **jackify-engine 0.3.18**: Archive extraction fixes for Windows symlinks, bandwidth limiting fix, improved error messages + +--- + ## v0.1.7 - TTW Automation & Bug Fixes **Release Date:** November 1, 2025 ### Major Features - **TTW (Tale of Two Wastelands) Installation and Automation** - - TTW Installation function using Hoolamike application - https://github.com/Niedzwiedzw/hoolamike +laf - TTW Installation function using Hoolamike application - https://github.com/Niedzwiedzw/hoolamike - Automated workflow for TTW installation and integration into FNV modlists, where possible - Automatic detection of TTW-compatible modlists - User prompt after modlist installation with option to install TTW diff --git a/README.md b/README.md index fd92e3b..a6e3719 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,9 @@ Currently, there are two main functions that Jackify will perform at this stage - **FUSE** (required for AppImage execution) - Pre-installed on most Linux distributions - If AppImage fails to run, install FUSE using your distribution's package manager +- **Ubuntu/Debian only**: Qt platform plugin library + - `sudo apt install libxcb-cursor-dev` + - Required for Qt GUI to initialize properly ### Installation diff --git a/jackify/__init__.py b/jackify/__init__.py index 12979bc..130752b 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.7" +__version__ = "0.1.7.1" diff --git a/jackify/backend/handlers/config_handler.py b/jackify/backend/handlers/config_handler.py index 6e8279e..fba8633 100644 --- a/jackify/backend/handlers/config_handler.py +++ b/jackify/backend/handlers/config_handler.py @@ -100,7 +100,8 @@ class ConfigHandler: libraryfolders_vdf_paths = [ os.path.expanduser("~/.steam/steam/config/libraryfolders.vdf"), os.path.expanduser("~/.local/share/Steam/config/libraryfolders.vdf"), - os.path.expanduser("~/.steam/root/config/libraryfolders.vdf") + os.path.expanduser("~/.steam/root/config/libraryfolders.vdf"), + os.path.expanduser("~/.var/app/com.valvesoftware.Steam/.local/share/Steam/config/libraryfolders.vdf") # Flatpak ] for vdf_path in libraryfolders_vdf_paths: diff --git a/jackify/backend/handlers/filesystem_handler.py b/jackify/backend/handlers/filesystem_handler.py index f629d7d..8a91b91 100644 --- a/jackify/backend/handlers/filesystem_handler.py +++ b/jackify/backend/handlers/filesystem_handler.py @@ -784,7 +784,8 @@ class FileSystemHandler: possible_vdf_paths = [ Path.home() / ".steam/steam/config/libraryfolders.vdf", Path.home() / ".local/share/Steam/config/libraryfolders.vdf", - Path.home() / ".steam/root/config/libraryfolders.vdf" + Path.home() / ".steam/root/config/libraryfolders.vdf", + Path.home() / ".var/app/com.valvesoftware.Steam/.local/share/Steam/config/libraryfolders.vdf" # Flatpak ] libraryfolders_vdf_path: Optional[Path] = None diff --git a/jackify/backend/handlers/menu_handler.py b/jackify/backend/handlers/menu_handler.py index ef60133..0039f8c 100644 --- a/jackify/backend/handlers/menu_handler.py +++ b/jackify/backend/handlers/menu_handler.py @@ -650,6 +650,10 @@ class ModlistMenuHandler: print("Modlist Install and Configuration complete!") print(f"• You should now be able to Launch '{context.get('name')}' through Steam") print("• Congratulations and enjoy the game!") + print("") + print("NOTE: If you experience ENB issues, consider using GE-Proton 10-14 instead of") + print(" Valve's Proton 10 (known ENB compatibility issues in Valve's Proton 10).") + print("") print("Detailed log available at: ~/Jackify/logs/Configure_New_Modlist_workflow.log") # Only wait for input in CLI mode, not GUI mode if not gui_mode: diff --git a/jackify/backend/handlers/modlist_handler.py b/jackify/backend/handlers/modlist_handler.py index 1b82f83..e5bc448 100644 --- a/jackify/backend/handlers/modlist_handler.py +++ b/jackify/backend/handlers/modlist_handler.py @@ -329,22 +329,18 @@ class ModlistHandler: # On non-Steam Deck systems, /media mounts should use Z: drive, not D: drive is_on_sdcard_path = str(self.modlist_dir).startswith("/run/media") or str(self.modlist_dir).startswith("/media") - # DEBUG: Log SD card detection logic - self.logger.debug(f"[SD_CARD_DEBUG] SD card detection for instance id={id(self)}:") - self.logger.debug(f"[SD_CARD_DEBUG] modlist_dir: {self.modlist_dir}") - self.logger.debug(f"[SD_CARD_DEBUG] is_on_sdcard_path: {is_on_sdcard_path}") - self.logger.debug(f"[SD_CARD_DEBUG] self.steamdeck: {self.steamdeck}") + # Log SD card detection for debugging + self.logger.debug(f"SD card detection: modlist_dir={self.modlist_dir}, is_sdcard_path={is_on_sdcard_path}, steamdeck={self.steamdeck}") if is_on_sdcard_path and self.steamdeck: self.modlist_sdcard = True self.logger.info("Modlist appears to be on an SD card (Steam Deck).") - self.logger.debug(f"[SD_CARD_DEBUG] Set modlist_sdcard=True") + self.logger.debug(f"Set modlist_sdcard=True") else: self.modlist_sdcard = False - self.logger.debug(f"[SD_CARD_DEBUG] Set modlist_sdcard=False because: is_on_sdcard_path={is_on_sdcard_path} AND steamdeck={self.steamdeck}") + self.logger.debug(f"Set modlist_sdcard=False (is_on_sdcard_path={is_on_sdcard_path}, steamdeck={self.steamdeck})") if is_on_sdcard_path and not self.steamdeck: self.logger.info("Modlist on /media mount detected on non-Steam Deck system - using Z: drive mapping.") - self.logger.debug("[SD_CARD_DEBUG] This is the ROOT CAUSE - SD card path but steamdeck=False!") # Find and set compatdata path now that we have appid # Ensure PathHandler is available (should be initialized in __init__) @@ -722,6 +718,8 @@ class ModlistHandler: success = self.winetricks_handler.install_wine_components(wineprefix, self.game_var_full, specific_components=components) if success: self.logger.info("Wine component installation completed successfully") + if status_callback: + status_callback(f"{self._get_progress_timestamp()} Wine components verified and installed successfully") else: self.logger.error("Wine component installation failed") print("Error: Failed to install necessary Wine components.") @@ -752,6 +750,19 @@ class ModlistHandler: self.logger.error("=" * 80) # Continue but user should be aware of potential issues + # Step 4.6: Enable dotfiles visibility for Wine prefix + if status_callback: + status_callback(f"{self._get_progress_timestamp()} Enabling dotfiles visibility") + self.logger.info("Step 4.6: Enabling dotfiles visibility in Wine prefix...") + try: + if self.protontricks_handler.enable_dotfiles(self.appid): + self.logger.info("Dotfiles visibility enabled successfully") + else: + self.logger.warning("Failed to enable dotfiles visibility (non-critical, continuing)") + except Exception as e: + self.logger.warning(f"Error enabling dotfiles visibility: {e} (non-critical, continuing)") + self.logger.info("Step 4.6: Enabling dotfiles visibility... Done") + # Step 5: Ensure permissions of Modlist directory if status_callback: status_callback(f"{self._get_progress_timestamp()} Setting ownership and permissions for modlist directory") diff --git a/jackify/backend/handlers/path_handler.py b/jackify/backend/handlers/path_handler.py index 639b215..8d35b35 100644 --- a/jackify/backend/handlers/path_handler.py +++ b/jackify/backend/handlers/path_handler.py @@ -390,7 +390,7 @@ class PathHandler: libraryfolders_vdf_paths = [ os.path.expanduser("~/.steam/steam/config/libraryfolders.vdf"), os.path.expanduser("~/.local/share/Steam/config/libraryfolders.vdf"), - # Add other potential standard locations if necessary + os.path.expanduser("~/.var/app/com.valvesoftware.Steam/.local/share/Steam/config/libraryfolders.vdf"), # Flatpak ] # Simple backup mechanism (optional but good practice) @@ -622,7 +622,9 @@ class PathHandler: m = re.search(r'"path"\s*"([^"]+)"', line) if m: lib_path = Path(m.group(1)) - library_paths.add(lib_path) + # Resolve symlinks for consistency (mmcblk0p1 -> deck/UUID) + resolved_path = lib_path.resolve() + library_paths.add(resolved_path) except Exception as e: logger.error(f"[DEBUG] Failed to parse {vdf_path}: {e}") logger.info(f"[DEBUG] All detected Steam libraries: {library_paths}") @@ -871,10 +873,9 @@ class PathHandler: else: found_stock = None for folder in STOCK_GAME_FOLDERS: - folder_pattern = f"/{folder.replace(' ', '')}".lower() - value_part_lower = value_part.replace(' ', '').lower() - if folder_pattern in value_part_lower: - idx = value_part_lower.index(folder_pattern) + folder_pattern = f"/{folder}" + if folder_pattern in value_part: + idx = value_part.index(folder_pattern) rel_path = value_part[idx:].lstrip('/') found_stock = folder break diff --git a/jackify/backend/handlers/protontricks_handler.py b/jackify/backend/handlers/protontricks_handler.py index 397c292..8725ba5 100644 --- a/jackify/backend/handlers/protontricks_handler.py +++ b/jackify/backend/handlers/protontricks_handler.py @@ -117,23 +117,21 @@ class ProtontricksHandler: try: # PyInstaller fix: Comprehensive environment cleaning for subprocess calls env = self._get_clean_subprocess_env() - + result = subprocess.run( - ["flatpak", "list"], - capture_output=True, + ["flatpak", "list"], + stdout=subprocess.PIPE, + stderr=subprocess.DEVNULL, # Suppress stderr to avoid error messages when flatpak not installed text=True, - check=True, env=env # Use comprehensively cleaned environment ) - if "com.github.Matoking.protontricks" in result.stdout: + if result.returncode == 0 and "com.github.Matoking.protontricks" in result.stdout: logger.info("Flatpak Protontricks is installed") self.which_protontricks = 'flatpak' flatpak_installed = True return True except FileNotFoundError: logger.warning("'flatpak' command not found. Cannot check for Flatpak Protontricks.") - except subprocess.CalledProcessError as e: - logger.warning(f"Error checking flatpak list: {e}") except Exception as e: logger.error(f"Unexpected error checking flatpak: {e}") @@ -707,8 +705,15 @@ class ProtontricksHandler: result = self.run_protontricks("--no-bwrap", appid, "-q", *components_to_install, env=env, timeout=600) self.logger.debug(f"Protontricks output: {result.stdout if result else ''}") if result and result.returncode == 0: - self.logger.info("Wine Component installation command completed successfully.") - return True + self.logger.info("Wine Component installation command completed.") + + # Verify components were actually installed + if self._verify_components_installed(appid, components_to_install): + self.logger.info("Component verification successful - all components installed correctly.") + return True + else: + self.logger.error(f"Component verification failed (Attempt {attempt}/{max_attempts})") + # Continue to retry else: self.logger.error(f"Protontricks command failed (Attempt {attempt}/{max_attempts}). Return Code: {result.returncode if result else 'N/A'}") self.logger.error(f"Stdout: {result.stdout.strip() if result else ''}") @@ -718,14 +723,73 @@ class ProtontricksHandler: self.logger.error(f"Failed to install Wine components after {max_attempts} attempts.") return False + def _verify_components_installed(self, appid: str, components: List[str]) -> bool: + """ + Verify that Wine components were actually installed by querying protontricks. + + Args: + appid: Steam AppID + components: List of components that should be installed + + Returns: + bool: True if all critical components are verified, False otherwise + """ + try: + self.logger.info("Verifying installed components...") + + # Run protontricks list-installed to get actual installed components + result = self.run_protontricks("--no-bwrap", appid, "list-installed", timeout=30) + + if not result or result.returncode != 0: + self.logger.error("Failed to query installed components") + self.logger.debug(f"list-installed stderr: {result.stderr if result else 'N/A'}") + return False + + installed_output = result.stdout.lower() + self.logger.debug(f"Installed components output: {installed_output}") + + # Define critical components that MUST be installed + # These are the core components that determine success + critical_components = ["vcrun2022", "xact"] + + # Check for critical components + missing_critical = [] + for component in critical_components: + if component.lower() not in installed_output: + missing_critical.append(component) + + if missing_critical: + self.logger.error(f"CRITICAL: Missing essential components: {missing_critical}") + self.logger.error("Installation reported success but components are NOT installed") + return False + + # Check for requested components (warn but don't fail) + missing_requested = [] + for component in components: + # Handle settings like fontsmooth=rgb (just check the base component name) + base_component = component.split('=')[0].lower() + if base_component not in installed_output and component.lower() not in installed_output: + missing_requested.append(component) + + if missing_requested: + self.logger.warning(f"Some requested components may not be installed: {missing_requested}") + self.logger.warning("This may cause issues, but critical components are present") + + self.logger.info(f"Verification passed - critical components confirmed: {critical_components}") + return True + + except Exception as e: + self.logger.error(f"Error verifying components: {e}", exc_info=True) + return False + def _cleanup_wine_processes(self): """ Internal method to clean up wine processes during component installation """ try: - subprocess.run("pgrep -f 'win7|win10|ShowDotFiles|protontricks' | xargs -r kill -9", + subprocess.run("pgrep -f 'win7|win10|ShowDotFiles|protontricks' | xargs -r kill -9", shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) - subprocess.run("pkill -9 winetricks", + subprocess.run("pkill -9 winetricks", shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) except Exception as e: logger.error(f"Error cleaning up wine processes: {e}") diff --git a/jackify/backend/handlers/wine_utils.py b/jackify/backend/handlers/wine_utils.py index 92999a7..3cd883c 100644 --- a/jackify/backend/handlers/wine_utils.py +++ b/jackify/backend/handlers/wine_utils.py @@ -200,40 +200,55 @@ class WineUtils: @staticmethod def _get_sd_card_mounts(): """ - Dynamically detect all current SD card mount points - Returns list of mount point paths + Detect SD card mount points using df. + Returns list of actual mount paths from /run/media (e.g., /run/media/deck/MicroSD). """ - try: - import subprocess - result = subprocess.run(['df', '-h'], capture_output=True, text=True, timeout=5) - sd_mounts = [] - for line in result.stdout.split('\n'): - # Look for common SD card mount patterns - if '/run/media' in line or ('/mnt' in line and 'sdcard' in line.lower()): - parts = line.split() - if len(parts) >= 6: # df output has 6+ columns - mount_point = parts[-1] # Last column is mount point - if mount_point.startswith(('/run/media', '/mnt')): - sd_mounts.append(mount_point) - return sd_mounts - except Exception: - # Fallback to common patterns if df fails - return ['/run/media/mmcblk0p1', '/run/media/deck'] + import subprocess + import re + + result = subprocess.run(['df', '-h'], capture_output=True, text=True, timeout=5) + sd_mounts = [] + + for line in result.stdout.split('\n'): + if '/run/media' in line: + parts = line.split() + if len(parts) >= 6: + mount_point = parts[-1] # Last column is the mount point + if mount_point.startswith('/run/media/'): + sd_mounts.append(mount_point) + + # Sort by length (longest first) to match most specific paths first + sd_mounts.sort(key=len, reverse=True) + logger.debug(f"Detected SD card mounts from df: {sd_mounts}") + return sd_mounts @staticmethod def _strip_sdcard_path(path): """ - Strip any detected SD card mount prefix from paths - Handles both /run/media/mmcblk0p1 and /run/media/deck/UUID patterns + Strip SD card mount prefix from path. + Handles both /run/media/mmcblk0p1 and /run/media/deck/UUID patterns. + Pattern: /run/media/deck/UUID/Games/... becomes /Games/... + Pattern: /run/media/mmcblk0p1/Games/... becomes /Games/... """ - sd_mounts = WineUtils._get_sd_card_mounts() + import re - for mount in sd_mounts: - if path.startswith(mount): - # Strip the mount prefix and ensure proper leading slash - relative_path = path[len(mount):].lstrip('/') - return "/" + relative_path if relative_path else "/" + # Pattern 1: /run/media/deck/UUID/... strip everything up to and including UUID + # This matches the bash: "${path#*/run/media/deck/*/*}" + deck_pattern = r'^/run/media/deck/[^/]+(/.*)?$' + match = re.match(deck_pattern, path) + if match: + stripped = match.group(1) if match.group(1) else "/" + logger.debug(f"Stripped SD card path (deck pattern): {path} -> {stripped}") + return stripped + # Pattern 2: /run/media/mmcblk0p1/... strip /run/media/mmcblk0p1 + # This matches the bash: "${path#*mmcblk0p1}" + if path.startswith('/run/media/mmcblk0p1/'): + stripped = path.replace('/run/media/mmcblk0p1', '', 1) + logger.debug(f"Stripped SD card path (mmcblk pattern): {path} -> {stripped}") + return stripped + + # No SD card pattern matched return path @staticmethod diff --git a/jackify/backend/handlers/winetricks_handler.py b/jackify/backend/handlers/winetricks_handler.py index f2b108d..d0500bd 100644 --- a/jackify/backend/handlers/winetricks_handler.py +++ b/jackify/backend/handlers/winetricks_handler.py @@ -349,10 +349,17 @@ class WinetricksHandler: self.logger.debug(f"Winetricks output: {result.stdout}") if result.returncode == 0: - self.logger.info("Wine Component installation command completed successfully.") - # Set Windows 10 mode after component installation (matches legacy script timing) - self._set_windows_10_mode(wineprefix, env.get('WINE', '')) - return True + self.logger.info("Wine Component installation command completed.") + + # Verify components were actually installed + if self._verify_components_installed(wineprefix, components_to_install, env): + self.logger.info("Component verification successful - all components installed correctly.") + # Set Windows 10 mode after component installation (matches legacy script timing) + self._set_windows_10_mode(wineprefix, env.get('WINE', '')) + return True + else: + self.logger.error(f"Component verification failed (Attempt {attempt}/{max_attempts})") + # Continue to retry else: # Special handling for dotnet40 verification issue (mimics protontricks behavior) if "dotnet40" in components_to_install and "ngen.exe not found" in result.stderr: @@ -430,11 +437,36 @@ class WinetricksHandler: if winetricks_failed: self.logger.error(f"Winetricks failed after {max_attempts} attempts.") - # Check if protontricks is available for fallback + # Network diagnostics before fallback (non-fatal) + self.logger.warning("=" * 80) + self.logger.warning("NETWORK DIAGNOSTICS: Testing connectivity to component download sources...") try: - protontricks_check = subprocess.run(['which', 'protontricks'], - capture_output=True, text=True, timeout=5) - if protontricks_check.returncode == 0: + # Check if curl is available + curl_check = subprocess.run(['which', 'curl'], capture_output=True, timeout=5) + if curl_check.returncode == 0: + # Test Microsoft download servers (used by winetricks for .NET, VC runtimes, DirectX) + test_result = subprocess.run(['curl', '-I', '--max-time', '10', 'https://download.microsoft.com'], + capture_output=True, text=True, timeout=15) + if test_result.returncode == 0: + self.logger.warning("Can reach download.microsoft.com") + else: + self.logger.error("Cannot reach download.microsoft.com - network/DNS issue likely") + self.logger.error(f" Curl exit code: {test_result.returncode}") + if test_result.stderr: + self.logger.error(f" Curl error: {test_result.stderr.strip()}") + else: + self.logger.warning("curl not available, skipping network diagnostic test") + except Exception as e: + self.logger.warning(f"Network diagnostic test skipped: {e}") + self.logger.warning("=" * 80) + + # Check if protontricks is available for fallback using centralized handler + try: + from .protontricks_handler import ProtontricksHandler + protontricks_handler = ProtontricksHandler() + protontricks_available = protontricks_handler.is_available() + + if protontricks_available: self.logger.warning("=" * 80) self.logger.warning("AUTOMATIC FALLBACK: Winetricks failed, attempting protontricks fallback...") self.logger.warning(f"Last winetricks error: {last_error_details}") @@ -721,13 +753,6 @@ class WinetricksHandler: if success: self.logger.info(f"Legacy .NET components {legacy_components} installed successfully with protontricks") - - # Enable dotfiles and symlinks for the prefix - if protontricks_handler.enable_dotfiles(appid): - self.logger.info("Enabled dotfiles and symlinks support") - else: - self.logger.warning("Failed to enable dotfiles/symlinks (non-critical)") - return True else: self.logger.error(f"Legacy .NET components {legacy_components} installation failed with protontricks") @@ -844,11 +869,18 @@ class WinetricksHandler: ) if result.returncode == 0: - self.logger.info(f"Winetricks components installed successfully: {components}") - # Set Windows 10 mode after component installation (matches legacy script timing) - wine_binary = env.get('WINE', '') - self._set_windows_10_mode(env.get('WINEPREFIX', ''), wine_binary) - return True + self.logger.info(f"Winetricks components installation command completed.") + + # Verify components were actually installed + if self._verify_components_installed(wineprefix, components, env): + self.logger.info("Component verification successful - all components installed correctly.") + # Set Windows 10 mode after component installation (matches legacy script timing) + wine_binary = env.get('WINE', '') + self._set_windows_10_mode(env.get('WINEPREFIX', ''), wine_binary) + return True + else: + self.logger.error(f"Component verification failed (attempt {attempt})") + # Continue to retry else: self.logger.error(f"Winetricks failed (attempt {attempt}): {result.stderr.strip()}") @@ -992,6 +1024,70 @@ class WinetricksHandler: self.logger.error(f"Error getting wine binary for prefix: {e}") return "" + def _verify_components_installed(self, wineprefix: str, components: List[str], env: dict) -> bool: + """ + Verify that Wine components were actually installed by checking winetricks.log. + + Args: + wineprefix: Wine prefix path + components: List of components that should be installed + env: Environment variables (includes WINE path) + + Returns: + bool: True if all critical components are verified, False otherwise + """ + try: + self.logger.info("Verifying installed components...") + + # Check winetricks.log file for installed components + winetricks_log = os.path.join(wineprefix, 'winetricks.log') + + if not os.path.exists(winetricks_log): + self.logger.error(f"winetricks.log not found at {winetricks_log}") + return False + + try: + with open(winetricks_log, 'r', encoding='utf-8', errors='ignore') as f: + log_content = f.read().lower() + except Exception as e: + self.logger.error(f"Failed to read winetricks.log: {e}") + return False + + self.logger.debug(f"winetricks.log length: {len(log_content)} bytes") + + # Define critical components that MUST be installed + critical_components = ["vcrun2022", "xact"] + + # Check for critical components + missing_critical = [] + for component in critical_components: + if component.lower() not in log_content: + missing_critical.append(component) + + if missing_critical: + self.logger.error(f"CRITICAL: Missing essential components: {missing_critical}") + self.logger.error("Installation reported success but components are NOT in winetricks.log") + return False + + # Check for requested components (warn but don't fail) + missing_requested = [] + for component in components: + # Handle settings like fontsmooth=rgb (just check the base component name) + base_component = component.split('=')[0].lower() + if base_component not in log_content and component.lower() not in log_content: + missing_requested.append(component) + + if missing_requested: + self.logger.warning(f"Some requested components may not be installed: {missing_requested}") + self.logger.warning("This may cause issues, but critical components are present") + + self.logger.info(f"Verification passed - critical components confirmed: {critical_components}") + return True + + except Exception as e: + self.logger.error(f"Error verifying components: {e}", exc_info=True) + return False + def _cleanup_wine_processes(self): """ Internal method to clean up wine processes during component installation diff --git a/jackify/backend/services/automated_prefix_service.py b/jackify/backend/services/automated_prefix_service.py index 432fd50..13c79de 100644 --- a/jackify/backend/services/automated_prefix_service.py +++ b/jackify/backend/services/automated_prefix_service.py @@ -2904,10 +2904,21 @@ echo Prefix creation complete. """Find a Steam game installation path by AppID and common names""" import os from pathlib import Path - - # Get Steam libraries from libraryfolders.vdf - steam_config_path = Path.home() / ".steam/steam/config/libraryfolders.vdf" - if not steam_config_path.exists(): + + # Get Steam libraries from libraryfolders.vdf - check multiple possible locations + possible_config_paths = [ + Path.home() / ".steam/steam/config/libraryfolders.vdf", + Path.home() / ".local/share/Steam/config/libraryfolders.vdf", + Path.home() / ".var/app/com.valvesoftware.Steam/.local/share/Steam/config/libraryfolders.vdf" # Flatpak + ] + + steam_config_path = None + for path in possible_config_paths: + if path.exists(): + steam_config_path = path + break + + if not steam_config_path: return None steam_libraries = [] diff --git a/jackify/backend/services/native_steam_operations_service.py b/jackify/backend/services/native_steam_operations_service.py index 3b096e5..a14b6b9 100644 --- a/jackify/backend/services/native_steam_operations_service.py +++ b/jackify/backend/services/native_steam_operations_service.py @@ -135,6 +135,9 @@ class NativeSteamOperationsService: steam_locations = [ Path.home() / ".steam/steam", Path.home() / ".local/share/Steam", + # Flatpak Steam - direct data directory + Path.home() / ".var/app/com.valvesoftware.Steam/.local/share/Steam", + # Flatpak Steam - symlinked home paths Path.home() / ".var/app/com.valvesoftware.Steam/home/.steam/steam", Path.home() / ".var/app/com.valvesoftware.Steam/home/.local/share/Steam" ] @@ -161,6 +164,9 @@ class NativeSteamOperationsService: standard_locations = [ Path.home() / ".steam/steam/steamapps/compatdata", Path.home() / ".local/share/Steam/steamapps/compatdata", + # Flatpak Steam - direct data directory + Path.home() / ".var/app/com.valvesoftware.Steam/.local/share/Steam/steamapps/compatdata", + # Flatpak Steam - symlinked home paths Path.home() / ".var/app/com.valvesoftware.Steam/home/.steam/steam/steamapps/compatdata", Path.home() / ".var/app/com.valvesoftware.Steam/home/.local/share/Steam/steamapps/compatdata" ] diff --git a/jackify/backend/services/protontricks_detection_service.py b/jackify/backend/services/protontricks_detection_service.py index 54ad05a..c659630 100644 --- a/jackify/backend/services/protontricks_detection_service.py +++ b/jackify/backend/services/protontricks_detection_service.py @@ -127,20 +127,18 @@ class ProtontricksDetectionService: try: env = handler._get_clean_subprocess_env() result = subprocess.run( - ["flatpak", "list"], - capture_output=True, + ["flatpak", "list"], + stdout=subprocess.PIPE, + stderr=subprocess.DEVNULL, # Suppress stderr to avoid error messages text=True, - check=True, env=env ) - if "com.github.Matoking.protontricks" in result.stdout: + if result.returncode == 0 and "com.github.Matoking.protontricks" in result.stdout: logger.info("Flatpak Protontricks is installed") handler.which_protontricks = 'flatpak' return True except FileNotFoundError: logger.warning("'flatpak' command not found. Cannot check for Flatpak Protontricks.") - except subprocess.CalledProcessError as e: - logger.warning(f"Error checking flatpak list: {e}") except Exception as e: logger.error(f"Unexpected error checking flatpak: {e}") diff --git a/jackify/backend/services/steam_restart_service.py b/jackify/backend/services/steam_restart_service.py index daa6184..2815cc8 100644 --- a/jackify/backend/services/steam_restart_service.py +++ b/jackify/backend/services/steam_restart_service.py @@ -5,6 +5,7 @@ import signal import psutil import logging import sys +import shutil from typing import Callable, Optional logger = logging.getLogger(__name__) @@ -86,6 +87,25 @@ def is_steam_deck() -> bool: logger.debug(f"Error detecting Steam Deck: {e}") return False +def is_flatpak_steam() -> bool: + """Detect if Steam is installed as a Flatpak.""" + try: + # First check if flatpak command exists + if not shutil.which('flatpak'): + return False + + # Verify the app is actually installed (not just directory exists) + result = subprocess.run(['flatpak', 'list', '--app'], + stdout=subprocess.PIPE, + stderr=subprocess.DEVNULL, # Suppress stderr to avoid error messages + text=True, + timeout=5) + if result.returncode == 0 and 'com.valvesoftware.Steam' in result.stdout: + return True + except Exception as e: + logger.debug(f"Error detecting Flatpak Steam: {e}") + return False + def get_steam_processes() -> list: """Return a list of psutil.Process objects for running Steam processes.""" steam_procs = [] @@ -122,16 +142,37 @@ def start_steam() -> bool: """Attempt to start Steam using the exact methods from existing working logic.""" env = _get_clean_subprocess_env() try: - # Try systemd user service (Steam Deck) + # Try systemd user service (Steam Deck) - HIGHEST PRIORITY if is_steam_deck(): subprocess.Popen(["systemctl", "--user", "restart", "app-steam@autostart.service"], env=env) return True - + + # Check if Flatpak Steam (only if not Steam Deck) + if is_flatpak_steam(): + logger.info("Flatpak Steam detected - using flatpak run command") + try: + # Redirect flatpak's stderr to suppress "app not installed" errors on systems without flatpak Steam + # Steam's own stdout/stderr will still go through (flatpak forwards them) + subprocess.Popen(["flatpak", "run", "com.valvesoftware.Steam", "-silent"], + env=env, stderr=subprocess.DEVNULL) + time.sleep(5) + check_result = subprocess.run(['pgrep', '-f', 'steam'], capture_output=True, timeout=10, env=env) + if check_result.returncode == 0: + logger.info("Flatpak Steam process detected after start.") + return True + else: + logger.warning("Flatpak Steam process not detected after start attempt.") + return False + except Exception as e: + logger.error(f"Error starting Flatpak Steam: {e}") + return False + # Use startup methods with only -silent flag (no -minimized or -no-browser) + # Don't redirect stdout/stderr or use start_new_session to allow Steam to connect to display/tray start_methods = [ - {"name": "Popen", "cmd": ["steam", "-silent"], "kwargs": {"stdout": subprocess.DEVNULL, "stderr": subprocess.DEVNULL, "stdin": subprocess.DEVNULL, "start_new_session": True, "env": env}}, - {"name": "setsid", "cmd": ["setsid", "steam", "-silent"], "kwargs": {"stdout": subprocess.DEVNULL, "stderr": subprocess.DEVNULL, "stdin": subprocess.DEVNULL, "env": env}}, - {"name": "nohup", "cmd": ["nohup", "steam", "-silent"], "kwargs": {"stdout": subprocess.DEVNULL, "stderr": subprocess.DEVNULL, "stdin": subprocess.DEVNULL, "start_new_session": True, "preexec_fn": os.setpgrp, "env": env}} + {"name": "Popen", "cmd": ["steam", "-silent"], "kwargs": {"env": env}}, + {"name": "setsid", "cmd": ["setsid", "steam", "-silent"], "kwargs": {"env": env}}, + {"name": "nohup", "cmd": ["nohup", "steam", "-silent"], "kwargs": {"preexec_fn": os.setpgrp, "env": env}} ] for method in start_methods: @@ -174,17 +215,26 @@ def robust_steam_restart(progress_callback: Optional[Callable[[str], None]] = No progress_callback(msg) report("Shutting down Steam...") - - # Steam Deck: Use systemctl for shutdown (special handling) + + # Steam Deck: Use systemctl for shutdown (special handling) - HIGHEST PRIORITY if is_steam_deck(): try: report("Steam Deck detected - using systemctl shutdown...") - subprocess.run(['systemctl', '--user', 'stop', 'app-steam@autostart.service'], + subprocess.run(['systemctl', '--user', 'stop', 'app-steam@autostart.service'], timeout=15, check=False, capture_output=True, env=env) time.sleep(2) except Exception as e: logger.debug(f"systemctl stop failed on Steam Deck: {e}") - + # Flatpak Steam: Use flatpak kill command (only if not Steam Deck) + elif is_flatpak_steam(): + try: + report("Flatpak Steam detected - stopping via flatpak...") + subprocess.run(['flatpak', 'kill', 'com.valvesoftware.Steam'], + timeout=15, check=False, capture_output=True, stderr=subprocess.DEVNULL, env=env) + time.sleep(2) + except Exception as e: + logger.debug(f"flatpak kill failed: {e}") + # All systems: Use pkill approach (proven 15/16 test success rate) try: # Skip unreliable steam -shutdown, go straight to pkill diff --git a/jackify/engine/Wabbajack.CLI.Builder.dll b/jackify/engine/Wabbajack.CLI.Builder.dll index 2743521..d7435f3 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 feb94cb..c2f4464 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 9303d66..f9dbfed 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 e292420..112f499 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 c11d007..859dbb4 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 b7a2a37..afcea24 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 b70cab5..5d4ae75 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 eec2526..dce1061 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 47d3bf6..f65c6eb 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 94aca67..4b3c879 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 0462e3f..7ea486b 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 59b4f0f..2906f3b 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 803c951..f044496 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 6bc6cbb..e8385e9 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 f9e6a85..372337b 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 c43ff45..f3a8263 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 f627cbe..afb890a 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 979ddbe..3f8f66a 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 43d13be..bad258c 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 0e3b06d..6ee199b 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 8c2cfe0..e19beeb 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 4cb7a65..fbe3fc6 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 743b659..4ebff1a 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 c393de3..e8c965d 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 0c9d484..19cb8ee 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 5adc57e..8b15a9d 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 d56d852..bc25baa 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 7fa583c..4087477 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 0b9d9d1..4f4390f 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 5ef0b2f..400a085 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 2e686cc..d6af05b 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 59c82dd..bffb25f 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 3c62c9d..8430b9a 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 78eda6d..81e5017 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 c7b08fc..3cfbb3c 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 dc1d824..dd7089d 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 ff2427d..e3de313 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 7714c9a..bac8bd8 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 2047b87..b8dc353 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 11e1f4f..7143f05 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 ce64396..ea0f177 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.17": { + "jackify-engine/0.3.18": { "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.17", - "Wabbajack.Downloaders.Bethesda": "0.3.17", - "Wabbajack.Downloaders.Dispatcher": "0.3.17", - "Wabbajack.Hashing.xxHash64": "0.3.17", - "Wabbajack.Networking.Discord": "0.3.17", - "Wabbajack.Networking.GitHub": "0.3.17", - "Wabbajack.Paths.IO": "0.3.17", - "Wabbajack.Server.Lib": "0.3.17", - "Wabbajack.Services.OSIntegrated": "0.3.17", - "Wabbajack.VFS": "0.3.17", + "Wabbajack.CLI.Builder": "0.3.18", + "Wabbajack.Downloaders.Bethesda": "0.3.18", + "Wabbajack.Downloaders.Dispatcher": "0.3.18", + "Wabbajack.Hashing.xxHash64": "0.3.18", + "Wabbajack.Networking.Discord": "0.3.18", + "Wabbajack.Networking.GitHub": "0.3.18", + "Wabbajack.Paths.IO": "0.3.18", + "Wabbajack.Server.Lib": "0.3.18", + "Wabbajack.Services.OSIntegrated": "0.3.18", + "Wabbajack.VFS": "0.3.18", "MegaApiClient": "1.0.0.0", "runtimepack.Microsoft.NETCore.App.Runtime.linux-x64": "8.0.19" }, @@ -1781,7 +1781,7 @@ } } }, - "Wabbajack.CLI.Builder/0.3.17": { + "Wabbajack.CLI.Builder/0.3.18": { "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.17" + "Wabbajack.Paths": "0.3.18" }, "runtime": { "Wabbajack.CLI.Builder.dll": {} } }, - "Wabbajack.Common/0.3.17": { + "Wabbajack.Common/0.3.18": { "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.17", - "Wabbajack.Networking.Http": "0.3.17", - "Wabbajack.Paths.IO": "0.3.17" + "Wabbajack.DTOs": "0.3.18", + "Wabbajack.Networking.Http": "0.3.18", + "Wabbajack.Paths.IO": "0.3.18" }, "runtime": { "Wabbajack.Common.dll": {} } }, - "Wabbajack.Compiler/0.3.17": { + "Wabbajack.Compiler/0.3.18": { "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.17", - "Wabbajack.Installer": "0.3.17", - "Wabbajack.VFS": "0.3.17", + "Wabbajack.Downloaders.Dispatcher": "0.3.18", + "Wabbajack.Installer": "0.3.18", + "Wabbajack.VFS": "0.3.18", "ini-parser-netstandard": "2.5.2" }, "runtime": { "Wabbajack.Compiler.dll": {} } }, - "Wabbajack.Compression.BSA/0.3.17": { + "Wabbajack.Compression.BSA/0.3.18": { "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.17", - "Wabbajack.DTOs": "0.3.17" + "Wabbajack.Common": "0.3.18", + "Wabbajack.DTOs": "0.3.18" }, "runtime": { "Wabbajack.Compression.BSA.dll": {} } }, - "Wabbajack.Compression.Zip/0.3.17": { + "Wabbajack.Compression.Zip/0.3.18": { "dependencies": { - "Wabbajack.IO.Async": "0.3.17" + "Wabbajack.IO.Async": "0.3.18" }, "runtime": { "Wabbajack.Compression.Zip.dll": {} } }, - "Wabbajack.Configuration/0.3.17": { + "Wabbajack.Configuration/0.3.18": { "runtime": { "Wabbajack.Configuration.dll": {} } }, - "Wabbajack.Downloaders.Bethesda/0.3.17": { + "Wabbajack.Downloaders.Bethesda/0.3.18": { "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.17", - "Wabbajack.Downloaders.Interfaces": "0.3.17", - "Wabbajack.Networking.BethesdaNet": "0.3.17" + "Wabbajack.Common": "0.3.18", + "Wabbajack.Downloaders.Interfaces": "0.3.18", + "Wabbajack.Networking.BethesdaNet": "0.3.18" }, "runtime": { "Wabbajack.Downloaders.Bethesda.dll": {} } }, - "Wabbajack.Downloaders.Dispatcher/0.3.17": { + "Wabbajack.Downloaders.Dispatcher/0.3.18": { "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.17", - "Wabbajack.Downloaders.GameFile": "0.3.17", - "Wabbajack.Downloaders.GoogleDrive": "0.3.17", - "Wabbajack.Downloaders.Http": "0.3.17", - "Wabbajack.Downloaders.IPS4OAuth2Downloader": "0.3.17", - "Wabbajack.Downloaders.Interfaces": "0.3.17", - "Wabbajack.Downloaders.Manual": "0.3.17", - "Wabbajack.Downloaders.MediaFire": "0.3.17", - "Wabbajack.Downloaders.Mega": "0.3.17", - "Wabbajack.Downloaders.ModDB": "0.3.17", - "Wabbajack.Downloaders.Nexus": "0.3.17", - "Wabbajack.Downloaders.VerificationCache": "0.3.17", - "Wabbajack.Downloaders.WabbajackCDN": "0.3.17", - "Wabbajack.Networking.WabbajackClientApi": "0.3.17" + "Wabbajack.Downloaders.Bethesda": "0.3.18", + "Wabbajack.Downloaders.GameFile": "0.3.18", + "Wabbajack.Downloaders.GoogleDrive": "0.3.18", + "Wabbajack.Downloaders.Http": "0.3.18", + "Wabbajack.Downloaders.IPS4OAuth2Downloader": "0.3.18", + "Wabbajack.Downloaders.Interfaces": "0.3.18", + "Wabbajack.Downloaders.Manual": "0.3.18", + "Wabbajack.Downloaders.MediaFire": "0.3.18", + "Wabbajack.Downloaders.Mega": "0.3.18", + "Wabbajack.Downloaders.ModDB": "0.3.18", + "Wabbajack.Downloaders.Nexus": "0.3.18", + "Wabbajack.Downloaders.VerificationCache": "0.3.18", + "Wabbajack.Downloaders.WabbajackCDN": "0.3.18", + "Wabbajack.Networking.WabbajackClientApi": "0.3.18" }, "runtime": { "Wabbajack.Downloaders.Dispatcher.dll": {} } }, - "Wabbajack.Downloaders.GameFile/0.3.17": { + "Wabbajack.Downloaders.GameFile/0.3.18": { "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.17", - "Wabbajack.VFS": "0.3.17" + "Wabbajack.Downloaders.Interfaces": "0.3.18", + "Wabbajack.VFS": "0.3.18" }, "runtime": { "Wabbajack.Downloaders.GameFile.dll": {} } }, - "Wabbajack.Downloaders.GoogleDrive/0.3.17": { + "Wabbajack.Downloaders.GoogleDrive/0.3.18": { "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.17", - "Wabbajack.DTOs": "0.3.17", - "Wabbajack.Downloaders.Interfaces": "0.3.17", - "Wabbajack.Networking.Http": "0.3.17", - "Wabbajack.Networking.Http.Interfaces": "0.3.17" + "Wabbajack.Common": "0.3.18", + "Wabbajack.DTOs": "0.3.18", + "Wabbajack.Downloaders.Interfaces": "0.3.18", + "Wabbajack.Networking.Http": "0.3.18", + "Wabbajack.Networking.Http.Interfaces": "0.3.18" }, "runtime": { "Wabbajack.Downloaders.GoogleDrive.dll": {} } }, - "Wabbajack.Downloaders.Http/0.3.17": { + "Wabbajack.Downloaders.Http/0.3.18": { "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.17", - "Wabbajack.DTOs": "0.3.17", - "Wabbajack.Downloaders.Interfaces": "0.3.17", - "Wabbajack.Networking.BethesdaNet": "0.3.17", - "Wabbajack.Networking.Http.Interfaces": "0.3.17", - "Wabbajack.Paths.IO": "0.3.17" + "Wabbajack.Common": "0.3.18", + "Wabbajack.DTOs": "0.3.18", + "Wabbajack.Downloaders.Interfaces": "0.3.18", + "Wabbajack.Networking.BethesdaNet": "0.3.18", + "Wabbajack.Networking.Http.Interfaces": "0.3.18", + "Wabbajack.Paths.IO": "0.3.18" }, "runtime": { "Wabbajack.Downloaders.Http.dll": {} } }, - "Wabbajack.Downloaders.Interfaces/0.3.17": { + "Wabbajack.Downloaders.Interfaces/0.3.18": { "dependencies": { "Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", - "Wabbajack.Compression.Zip": "0.3.17", - "Wabbajack.DTOs": "0.3.17", - "Wabbajack.Paths.IO": "0.3.17" + "Wabbajack.Compression.Zip": "0.3.18", + "Wabbajack.DTOs": "0.3.18", + "Wabbajack.Paths.IO": "0.3.18" }, "runtime": { "Wabbajack.Downloaders.Interfaces.dll": {} } }, - "Wabbajack.Downloaders.IPS4OAuth2Downloader/0.3.17": { + "Wabbajack.Downloaders.IPS4OAuth2Downloader/0.3.18": { "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.17", - "Wabbajack.Downloaders.Interfaces": "0.3.17", - "Wabbajack.Networking.Http": "0.3.17", - "Wabbajack.Networking.Http.Interfaces": "0.3.17" + "Wabbajack.Common": "0.3.18", + "Wabbajack.Downloaders.Interfaces": "0.3.18", + "Wabbajack.Networking.Http": "0.3.18", + "Wabbajack.Networking.Http.Interfaces": "0.3.18" }, "runtime": { "Wabbajack.Downloaders.IPS4OAuth2Downloader.dll": {} } }, - "Wabbajack.Downloaders.Manual/0.3.17": { + "Wabbajack.Downloaders.Manual/0.3.18": { "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.17", - "Wabbajack.Downloaders.Interfaces": "0.3.17" + "Wabbajack.Common": "0.3.18", + "Wabbajack.Downloaders.Interfaces": "0.3.18" }, "runtime": { "Wabbajack.Downloaders.Manual.dll": {} } }, - "Wabbajack.Downloaders.MediaFire/0.3.17": { + "Wabbajack.Downloaders.MediaFire/0.3.18": { "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.17", - "Wabbajack.Downloaders.Interfaces": "0.3.17", - "Wabbajack.Networking.Http.Interfaces": "0.3.17" + "Wabbajack.Common": "0.3.18", + "Wabbajack.Downloaders.Interfaces": "0.3.18", + "Wabbajack.Networking.Http.Interfaces": "0.3.18" }, "runtime": { "Wabbajack.Downloaders.MediaFire.dll": {} } }, - "Wabbajack.Downloaders.Mega/0.3.17": { + "Wabbajack.Downloaders.Mega/0.3.18": { "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.17", - "Wabbajack.Downloaders.Interfaces": "0.3.17", - "Wabbajack.Paths.IO": "0.3.17" + "Wabbajack.Common": "0.3.18", + "Wabbajack.Downloaders.Interfaces": "0.3.18", + "Wabbajack.Paths.IO": "0.3.18" }, "runtime": { "Wabbajack.Downloaders.Mega.dll": {} } }, - "Wabbajack.Downloaders.ModDB/0.3.17": { + "Wabbajack.Downloaders.ModDB/0.3.18": { "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.17", - "Wabbajack.Downloaders.Interfaces": "0.3.17", - "Wabbajack.Networking.Http": "0.3.17", - "Wabbajack.Networking.Http.Interfaces": "0.3.17" + "Wabbajack.Common": "0.3.18", + "Wabbajack.Downloaders.Interfaces": "0.3.18", + "Wabbajack.Networking.Http": "0.3.18", + "Wabbajack.Networking.Http.Interfaces": "0.3.18" }, "runtime": { "Wabbajack.Downloaders.ModDB.dll": {} } }, - "Wabbajack.Downloaders.Nexus/0.3.17": { + "Wabbajack.Downloaders.Nexus/0.3.18": { "dependencies": { "Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", - "Wabbajack.DTOs": "0.3.17", - "Wabbajack.Downloaders.Interfaces": "0.3.17", - "Wabbajack.Hashing.xxHash64": "0.3.17", - "Wabbajack.Networking.Http": "0.3.17", - "Wabbajack.Networking.Http.Interfaces": "0.3.17", - "Wabbajack.Networking.NexusApi": "0.3.17", - "Wabbajack.Paths": "0.3.17" + "Wabbajack.DTOs": "0.3.18", + "Wabbajack.Downloaders.Interfaces": "0.3.18", + "Wabbajack.Hashing.xxHash64": "0.3.18", + "Wabbajack.Networking.Http": "0.3.18", + "Wabbajack.Networking.Http.Interfaces": "0.3.18", + "Wabbajack.Networking.NexusApi": "0.3.18", + "Wabbajack.Paths": "0.3.18" }, "runtime": { "Wabbajack.Downloaders.Nexus.dll": {} } }, - "Wabbajack.Downloaders.VerificationCache/0.3.17": { + "Wabbajack.Downloaders.VerificationCache/0.3.18": { "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.17", - "Wabbajack.Paths.IO": "0.3.17" + "Wabbajack.DTOs": "0.3.18", + "Wabbajack.Paths.IO": "0.3.18" }, "runtime": { "Wabbajack.Downloaders.VerificationCache.dll": {} } }, - "Wabbajack.Downloaders.WabbajackCDN/0.3.17": { + "Wabbajack.Downloaders.WabbajackCDN/0.3.18": { "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.17", - "Wabbajack.Downloaders.Interfaces": "0.3.17", - "Wabbajack.Networking.Http": "0.3.17", - "Wabbajack.RateLimiter": "0.3.17" + "Wabbajack.Common": "0.3.18", + "Wabbajack.Downloaders.Interfaces": "0.3.18", + "Wabbajack.Networking.Http": "0.3.18", + "Wabbajack.RateLimiter": "0.3.18" }, "runtime": { "Wabbajack.Downloaders.WabbajackCDN.dll": {} } }, - "Wabbajack.DTOs/0.3.17": { + "Wabbajack.DTOs/0.3.18": { "dependencies": { "Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", - "Wabbajack.Hashing.xxHash64": "0.3.17", - "Wabbajack.Paths": "0.3.17" + "Wabbajack.Hashing.xxHash64": "0.3.18", + "Wabbajack.Paths": "0.3.18" }, "runtime": { "Wabbajack.DTOs.dll": {} } }, - "Wabbajack.FileExtractor/0.3.17": { + "Wabbajack.FileExtractor/0.3.18": { "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.17", - "Wabbajack.Compression.BSA": "0.3.17", - "Wabbajack.Hashing.PHash": "0.3.17", - "Wabbajack.Paths": "0.3.17" + "Wabbajack.Common": "0.3.18", + "Wabbajack.Compression.BSA": "0.3.18", + "Wabbajack.Hashing.PHash": "0.3.18", + "Wabbajack.Paths": "0.3.18" }, "runtime": { "Wabbajack.FileExtractor.dll": {} } }, - "Wabbajack.Hashing.PHash/0.3.17": { + "Wabbajack.Hashing.PHash/0.3.18": { "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.17", - "Wabbajack.DTOs": "0.3.17", - "Wabbajack.Paths": "0.3.17", - "Wabbajack.Paths.IO": "0.3.17" + "Wabbajack.Common": "0.3.18", + "Wabbajack.DTOs": "0.3.18", + "Wabbajack.Paths": "0.3.18", + "Wabbajack.Paths.IO": "0.3.18" }, "runtime": { "Wabbajack.Hashing.PHash.dll": {} } }, - "Wabbajack.Hashing.xxHash64/0.3.17": { + "Wabbajack.Hashing.xxHash64/0.3.18": { "dependencies": { - "Wabbajack.Paths": "0.3.17", - "Wabbajack.RateLimiter": "0.3.17" + "Wabbajack.Paths": "0.3.18", + "Wabbajack.RateLimiter": "0.3.18" }, "runtime": { "Wabbajack.Hashing.xxHash64.dll": {} } }, - "Wabbajack.Installer/0.3.17": { + "Wabbajack.Installer/0.3.18": { "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.17", - "Wabbajack.Downloaders.Dispatcher": "0.3.17", - "Wabbajack.Downloaders.GameFile": "0.3.17", - "Wabbajack.FileExtractor": "0.3.17", - "Wabbajack.Networking.WabbajackClientApi": "0.3.17", - "Wabbajack.Paths": "0.3.17", - "Wabbajack.Paths.IO": "0.3.17", - "Wabbajack.VFS": "0.3.17", + "Wabbajack.DTOs": "0.3.18", + "Wabbajack.Downloaders.Dispatcher": "0.3.18", + "Wabbajack.Downloaders.GameFile": "0.3.18", + "Wabbajack.FileExtractor": "0.3.18", + "Wabbajack.Networking.WabbajackClientApi": "0.3.18", + "Wabbajack.Paths": "0.3.18", + "Wabbajack.Paths.IO": "0.3.18", + "Wabbajack.VFS": "0.3.18", "ini-parser-netstandard": "2.5.2" }, "runtime": { "Wabbajack.Installer.dll": {} } }, - "Wabbajack.IO.Async/0.3.17": { + "Wabbajack.IO.Async/0.3.18": { "runtime": { "Wabbajack.IO.Async.dll": {} } }, - "Wabbajack.Networking.BethesdaNet/0.3.17": { + "Wabbajack.Networking.BethesdaNet/0.3.18": { "dependencies": { "Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", - "Wabbajack.DTOs": "0.3.17", - "Wabbajack.Networking.Http": "0.3.17", - "Wabbajack.Networking.Http.Interfaces": "0.3.17" + "Wabbajack.DTOs": "0.3.18", + "Wabbajack.Networking.Http": "0.3.18", + "Wabbajack.Networking.Http.Interfaces": "0.3.18" }, "runtime": { "Wabbajack.Networking.BethesdaNet.dll": {} } }, - "Wabbajack.Networking.Discord/0.3.17": { + "Wabbajack.Networking.Discord/0.3.18": { "dependencies": { "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.Logging.Abstractions": "9.0.1", - "Wabbajack.Networking.Http.Interfaces": "0.3.17" + "Wabbajack.Networking.Http.Interfaces": "0.3.18" }, "runtime": { "Wabbajack.Networking.Discord.dll": {} } }, - "Wabbajack.Networking.GitHub/0.3.17": { + "Wabbajack.Networking.GitHub/0.3.18": { "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.17", - "Wabbajack.Networking.Http.Interfaces": "0.3.17" + "Wabbajack.DTOs": "0.3.18", + "Wabbajack.Networking.Http.Interfaces": "0.3.18" }, "runtime": { "Wabbajack.Networking.GitHub.dll": {} } }, - "Wabbajack.Networking.Http/0.3.17": { + "Wabbajack.Networking.Http/0.3.18": { "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.17", - "Wabbajack.Downloaders.Interfaces": "0.3.17", - "Wabbajack.Hashing.xxHash64": "0.3.17", - "Wabbajack.Networking.Http.Interfaces": "0.3.17", - "Wabbajack.Paths": "0.3.17", - "Wabbajack.Paths.IO": "0.3.17" + "Wabbajack.Configuration": "0.3.18", + "Wabbajack.Downloaders.Interfaces": "0.3.18", + "Wabbajack.Hashing.xxHash64": "0.3.18", + "Wabbajack.Networking.Http.Interfaces": "0.3.18", + "Wabbajack.Paths": "0.3.18", + "Wabbajack.Paths.IO": "0.3.18" }, "runtime": { "Wabbajack.Networking.Http.dll": {} } }, - "Wabbajack.Networking.Http.Interfaces/0.3.17": { + "Wabbajack.Networking.Http.Interfaces/0.3.18": { "dependencies": { - "Wabbajack.Hashing.xxHash64": "0.3.17" + "Wabbajack.Hashing.xxHash64": "0.3.18" }, "runtime": { "Wabbajack.Networking.Http.Interfaces.dll": {} } }, - "Wabbajack.Networking.NexusApi/0.3.17": { + "Wabbajack.Networking.NexusApi/0.3.18": { "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.17", - "Wabbajack.Networking.Http": "0.3.17", - "Wabbajack.Networking.Http.Interfaces": "0.3.17", - "Wabbajack.Networking.WabbajackClientApi": "0.3.17" + "Wabbajack.DTOs": "0.3.18", + "Wabbajack.Networking.Http": "0.3.18", + "Wabbajack.Networking.Http.Interfaces": "0.3.18", + "Wabbajack.Networking.WabbajackClientApi": "0.3.18" }, "runtime": { "Wabbajack.Networking.NexusApi.dll": {} } }, - "Wabbajack.Networking.WabbajackClientApi/0.3.17": { + "Wabbajack.Networking.WabbajackClientApi/0.3.18": { "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.17", - "Wabbajack.DTOs": "0.3.17", - "Wabbajack.Paths.IO": "0.3.17", - "Wabbajack.VFS.Interfaces": "0.3.17", + "Wabbajack.Common": "0.3.18", + "Wabbajack.DTOs": "0.3.18", + "Wabbajack.Paths.IO": "0.3.18", + "Wabbajack.VFS.Interfaces": "0.3.18", "YamlDotNet": "16.3.0" }, "runtime": { "Wabbajack.Networking.WabbajackClientApi.dll": {} } }, - "Wabbajack.Paths/0.3.17": { + "Wabbajack.Paths/0.3.18": { "runtime": { "Wabbajack.Paths.dll": {} } }, - "Wabbajack.Paths.IO/0.3.17": { + "Wabbajack.Paths.IO/0.3.18": { "dependencies": { - "Wabbajack.Paths": "0.3.17", + "Wabbajack.Paths": "0.3.18", "shortid": "4.0.0" }, "runtime": { "Wabbajack.Paths.IO.dll": {} } }, - "Wabbajack.RateLimiter/0.3.17": { + "Wabbajack.RateLimiter/0.3.18": { "runtime": { "Wabbajack.RateLimiter.dll": {} } }, - "Wabbajack.Server.Lib/0.3.17": { + "Wabbajack.Server.Lib/0.3.18": { "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.17", - "Wabbajack.Networking.Http.Interfaces": "0.3.17", - "Wabbajack.Services.OSIntegrated": "0.3.17" + "Wabbajack.Common": "0.3.18", + "Wabbajack.Networking.Http.Interfaces": "0.3.18", + "Wabbajack.Services.OSIntegrated": "0.3.18" }, "runtime": { "Wabbajack.Server.Lib.dll": {} } }, - "Wabbajack.Services.OSIntegrated/0.3.17": { + "Wabbajack.Services.OSIntegrated/0.3.18": { "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.17", - "Wabbajack.Downloaders.Dispatcher": "0.3.17", - "Wabbajack.Installer": "0.3.17", - "Wabbajack.Networking.BethesdaNet": "0.3.17", - "Wabbajack.Networking.Discord": "0.3.17", - "Wabbajack.VFS": "0.3.17" + "Wabbajack.Compiler": "0.3.18", + "Wabbajack.Downloaders.Dispatcher": "0.3.18", + "Wabbajack.Installer": "0.3.18", + "Wabbajack.Networking.BethesdaNet": "0.3.18", + "Wabbajack.Networking.Discord": "0.3.18", + "Wabbajack.VFS": "0.3.18" }, "runtime": { "Wabbajack.Services.OSIntegrated.dll": {} } }, - "Wabbajack.VFS/0.3.17": { + "Wabbajack.VFS/0.3.18": { "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.17", - "Wabbajack.FileExtractor": "0.3.17", - "Wabbajack.Hashing.PHash": "0.3.17", - "Wabbajack.Hashing.xxHash64": "0.3.17", - "Wabbajack.Paths": "0.3.17", - "Wabbajack.Paths.IO": "0.3.17", - "Wabbajack.VFS.Interfaces": "0.3.17" + "Wabbajack.Common": "0.3.18", + "Wabbajack.FileExtractor": "0.3.18", + "Wabbajack.Hashing.PHash": "0.3.18", + "Wabbajack.Hashing.xxHash64": "0.3.18", + "Wabbajack.Paths": "0.3.18", + "Wabbajack.Paths.IO": "0.3.18", + "Wabbajack.VFS.Interfaces": "0.3.18" }, "runtime": { "Wabbajack.VFS.dll": {} } }, - "Wabbajack.VFS.Interfaces/0.3.17": { + "Wabbajack.VFS.Interfaces/0.3.18": { "dependencies": { "Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", - "Wabbajack.DTOs": "0.3.17", - "Wabbajack.Hashing.xxHash64": "0.3.17", - "Wabbajack.Paths": "0.3.17" + "Wabbajack.DTOs": "0.3.18", + "Wabbajack.Hashing.xxHash64": "0.3.18", + "Wabbajack.Paths": "0.3.18" }, "runtime": { "Wabbajack.VFS.Interfaces.dll": {} @@ -2332,7 +2332,7 @@ } }, "libraries": { - "jackify-engine/0.3.17": { + "jackify-engine/0.3.18": { "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.17": { + "Wabbajack.CLI.Builder/0.3.18": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Common/0.3.17": { + "Wabbajack.Common/0.3.18": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Compiler/0.3.17": { + "Wabbajack.Compiler/0.3.18": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Compression.BSA/0.3.17": { + "Wabbajack.Compression.BSA/0.3.18": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Compression.Zip/0.3.17": { + "Wabbajack.Compression.Zip/0.3.18": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Configuration/0.3.17": { + "Wabbajack.Configuration/0.3.18": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Downloaders.Bethesda/0.3.17": { + "Wabbajack.Downloaders.Bethesda/0.3.18": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Downloaders.Dispatcher/0.3.17": { + "Wabbajack.Downloaders.Dispatcher/0.3.18": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Downloaders.GameFile/0.3.17": { + "Wabbajack.Downloaders.GameFile/0.3.18": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Downloaders.GoogleDrive/0.3.17": { + "Wabbajack.Downloaders.GoogleDrive/0.3.18": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Downloaders.Http/0.3.17": { + "Wabbajack.Downloaders.Http/0.3.18": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Downloaders.Interfaces/0.3.17": { + "Wabbajack.Downloaders.Interfaces/0.3.18": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Downloaders.IPS4OAuth2Downloader/0.3.17": { + "Wabbajack.Downloaders.IPS4OAuth2Downloader/0.3.18": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Downloaders.Manual/0.3.17": { + "Wabbajack.Downloaders.Manual/0.3.18": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Downloaders.MediaFire/0.3.17": { + "Wabbajack.Downloaders.MediaFire/0.3.18": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Downloaders.Mega/0.3.17": { + "Wabbajack.Downloaders.Mega/0.3.18": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Downloaders.ModDB/0.3.17": { + "Wabbajack.Downloaders.ModDB/0.3.18": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Downloaders.Nexus/0.3.17": { + "Wabbajack.Downloaders.Nexus/0.3.18": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Downloaders.VerificationCache/0.3.17": { + "Wabbajack.Downloaders.VerificationCache/0.3.18": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Downloaders.WabbajackCDN/0.3.17": { + "Wabbajack.Downloaders.WabbajackCDN/0.3.18": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.DTOs/0.3.17": { + "Wabbajack.DTOs/0.3.18": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.FileExtractor/0.3.17": { + "Wabbajack.FileExtractor/0.3.18": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Hashing.PHash/0.3.17": { + "Wabbajack.Hashing.PHash/0.3.18": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Hashing.xxHash64/0.3.17": { + "Wabbajack.Hashing.xxHash64/0.3.18": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Installer/0.3.17": { + "Wabbajack.Installer/0.3.18": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.IO.Async/0.3.17": { + "Wabbajack.IO.Async/0.3.18": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Networking.BethesdaNet/0.3.17": { + "Wabbajack.Networking.BethesdaNet/0.3.18": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Networking.Discord/0.3.17": { + "Wabbajack.Networking.Discord/0.3.18": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Networking.GitHub/0.3.17": { + "Wabbajack.Networking.GitHub/0.3.18": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Networking.Http/0.3.17": { + "Wabbajack.Networking.Http/0.3.18": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Networking.Http.Interfaces/0.3.17": { + "Wabbajack.Networking.Http.Interfaces/0.3.18": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Networking.NexusApi/0.3.17": { + "Wabbajack.Networking.NexusApi/0.3.18": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Networking.WabbajackClientApi/0.3.17": { + "Wabbajack.Networking.WabbajackClientApi/0.3.18": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Paths/0.3.17": { + "Wabbajack.Paths/0.3.18": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Paths.IO/0.3.17": { + "Wabbajack.Paths.IO/0.3.18": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.RateLimiter/0.3.17": { + "Wabbajack.RateLimiter/0.3.18": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Server.Lib/0.3.17": { + "Wabbajack.Server.Lib/0.3.18": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Services.OSIntegrated/0.3.17": { + "Wabbajack.Services.OSIntegrated/0.3.18": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.VFS/0.3.17": { + "Wabbajack.VFS/0.3.18": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.VFS.Interfaces/0.3.17": { + "Wabbajack.VFS.Interfaces/0.3.18": { "type": "project", "serviceable": false, "sha512": "" diff --git a/jackify/engine/jackify-engine.dll b/jackify/engine/jackify-engine.dll index bc0e2a9..f43b08d 100644 Binary files a/jackify/engine/jackify-engine.dll and b/jackify/engine/jackify-engine.dll differ diff --git a/jackify/engine/test.png b/jackify/engine/test.png new file mode 100644 index 0000000..661c481 Binary files /dev/null and b/jackify/engine/test.png differ diff --git a/jackify/frontends/gui/dialogs/completion_dialog.py b/jackify/frontends/gui/dialogs/completion_dialog.py index a4ecd01..8575528 100644 --- a/jackify/frontends/gui/dialogs/completion_dialog.py +++ b/jackify/frontends/gui/dialogs/completion_dialog.py @@ -183,7 +183,7 @@ class NextStepsDialog(QDialog): def _build_completion_text(self) -> str: """ Build the completion text matching the CLI version from menu_handler.py. - + Returns: Formatted completion text string """ @@ -195,6 +195,9 @@ Modlist Install and Configuration complete!: • You should now be able to Launch '{self.modlist_name}' through Steam. • Congratulations and enjoy the game! +NOTE: If you experience ENB issues, consider using GE-Proton 10-14 instead of +Valve's Proton 10 (known ENB compatibility issues in Valve's Proton 10). + Detailed log available at: ~/Jackify/logs/Configure_New_Modlist_workflow.log""" - + return completion_text \ No newline at end of file diff --git a/jackify/frontends/gui/dialogs/success_dialog.py b/jackify/frontends/gui/dialogs/success_dialog.py index 161db52..7e3be6c 100644 --- a/jackify/frontends/gui/dialogs/success_dialog.py +++ b/jackify/frontends/gui/dialogs/success_dialog.py @@ -40,12 +40,12 @@ class SuccessDialog(QDialog): self.setWindowTitle("Success!") self.setWindowModality(Qt.NonModal) self.setAttribute(Qt.WA_ShowWithoutActivating, True) - self.setFixedSize(500, 420) + self.setFixedSize(500, 500) self.setWindowFlag(Qt.WindowDoesNotAcceptFocus, True) self.setStyleSheet("QDialog { background: #181818; color: #fff; border-radius: 12px; }" ) layout = QVBoxLayout(self) layout.setSpacing(0) - layout.setContentsMargins(0, 0, 0, 0) + layout.setContentsMargins(30, 30, 30, 30) # --- Card background for content --- card = QFrame(self) @@ -53,6 +53,7 @@ class SuccessDialog(QDialog): card.setFrameShape(QFrame.StyledPanel) card.setFrameShadow(QFrame.Raised) card.setFixedWidth(440) + card.setMinimumHeight(380) card_layout = QVBoxLayout(card) card_layout.setSpacing(12) card_layout.setContentsMargins(28, 28, 28, 28) @@ -65,23 +66,6 @@ class SuccessDialog(QDialog): ) card.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) - # Trophy icon (smaller, more subtle) - trophy_label = QLabel() - trophy_label.setAlignment(Qt.AlignCenter) - trophy_icon_path = Path(__file__).parent.parent.parent.parent.parent / "Files" / "trophy.png" - if trophy_icon_path.exists(): - pixmap = QPixmap(str(trophy_icon_path)).scaled(36, 36, Qt.KeepAspectRatio, Qt.SmoothTransformation) - trophy_label.setPixmap(pixmap) - else: - trophy_label.setText("✓") - trophy_label.setStyleSheet( - "QLabel { " - " font-size: 28px; " - " margin-bottom: 4px; " - "}" - ) - card_layout.addWidget(trophy_label) - # Success title (less saturated green) title_label = QLabel("Success!") title_label.setAlignment(Qt.AlignCenter) @@ -137,11 +121,12 @@ class SuccessDialog(QDialog): next_steps_label = QLabel(next_steps_text) next_steps_label.setAlignment(Qt.AlignCenter) next_steps_label.setWordWrap(True) + next_steps_label.setMinimumHeight(100) next_steps_label.setStyleSheet( "QLabel { " " font-size: 13px; " " color: #b0b0b0; " - " line-height: 1.2; " + " line-height: 1.4; " " padding: 6px; " " background-color: transparent; " " border-radius: 6px; " @@ -232,15 +217,22 @@ class SuccessDialog(QDialog): def _build_next_steps(self) -> str: """ Build the next steps guidance based on workflow type. - + Returns: Formatted next steps string """ game_display = self.game_name or self.modlist_name + + base_message = "" if self.workflow_type == "tuxborn": - return f"You can now launch Tuxborn from Steam and enjoy your modded {game_display} experience!" + base_message = f"You can now launch Tuxborn from Steam and enjoy your modded {game_display} experience!" else: - return f"You can now launch {self.modlist_name} from Steam and enjoy your modded {game_display} experience!" + base_message = f"You can now launch {self.modlist_name} from Steam and enjoy your modded {game_display} experience!" + + # Add GE-Proton recommendation + proton_note = "\n\nNOTE: If you experience ENB issues, consider using GE-Proton 10-14 instead of Valve's Proton 10 (known ENB compatibility issues)." + + return base_message + proton_note def _update_countdown(self): if self._countdown > 0: