diff --git a/CHANGELOG.md b/CHANGELOG.md index f1f9657..69bd318 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,26 @@ # Jackify Changelog +## v0.2.0.10 - Registry & Hashing Fixes +**Release Date:** 2025-01-04 + +### Engine Updates +- **jackify-engine 0.4.5**: Fixed archive extraction with backslashes (including pattern matching), data directory path configuration, and removed post-download .wabbajack hash validation. Engine now auto-refreshes OAuth tokens during long installations via `NEXUS_OAUTH_INFO` environment variable. + +### Critical Bug Fixes +- **InstallationThread Crash**: Fixed crash during installation with error "'InstallationThread' object has no attribute 'auth_service'". Premium detection diagnostics code assumed auth_service existed but it was never passed to the thread. Affects all users when Premium detection (including false positives) is triggered. +- **Install Start Hang**: Fixed missing `oauth_info` parameter that prevented modlist installs from starting (hung at "Starting modlist installation...") +- **OAuth Token Auto-Refresh**: Fixed OAuth tokens expiring during long modlist installations. Jackify now refreshes tokens with 15-minute buffer before passing to engine. Engine receives full OAuth state via `NEXUS_OAUTH_INFO` environment variable, enabling automatic token refresh during multi-hour downloads. Fixes "Token has expired" errors that occurred 60 minutes into installations. +- **ShowDotFiles Registry Format**: Fixed Wine registry format bug causing hidden files to remain hidden in prefixes. Python string escaping issue wrote single backslash instead of double backslash in `[Software\\Wine]` section header. Added auto-detection and fix for broken format from curated registry files. +- **Dotnet4 Registry Fixes**: Confirmed universal dotnet4.x registry fixes (`*mscoree=native` and `OnlyUseLatestCLR=1`) are applied in all three workflows (Install, Configure New, Configure Existing) across both CLI and GUI interfaces +- **Proton Path Configuration**: Fixed `proton_path` writing invalid "auto" string to config.json - now uses `null` instead, preventing jackify-engine from receiving invalid paths + +### Improvements +- **Wine Binary Detection**: Enhanced detection with recursive fallback search within Proton directory when expected paths don't exist (handles different Proton version structures) +- Added Jackify version logging at workflow start +- Fixed GUI log file rotation to only run in debug mode + +--- + ## v0.2.0.9 - Critical Configuration Fixes **Release Date:** 2025-12-31 diff --git a/jackify/__init__.py b/jackify/__init__.py index 61dfaa6..6511505 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.2.0.9" +__version__ = "0.2.0.10" diff --git a/jackify/backend/core/modlist_operations.py b/jackify/backend/core/modlist_operations.py index e5aac72..e43038d 100644 --- a/jackify/backend/core/modlist_operations.py +++ b/jackify/backend/core/modlist_operations.py @@ -34,7 +34,7 @@ def _get_user_proton_version(): # get_proton_path() returns the Install Proton path user_proton_path = config_handler.get_proton_path() - if user_proton_path == 'auto': + if not user_proton_path or user_proton_path == 'auto': # Use enhanced fallback logic with GE-Proton preference logging.info("User selected auto-detect, using GE-Proton → Experimental → Proton precedence") return WineUtils.select_best_proton() @@ -502,10 +502,11 @@ class ModlistInstallCLI: print("\n" + "-" * 28) print(f"{COLOR_INFO}Nexus Authentication: Using API Key (Legacy){COLOR_RESET}") - # Get valid token/key - api_key = auth_service.ensure_valid_auth() + # Get valid token/key and OAuth state for engine auto-refresh + api_key, oauth_info = auth_service.get_auth_for_engine() if api_key: self.context['nexus_api_key'] = api_key + self.context['nexus_oauth_info'] = oauth_info # For engine auto-refresh else: # Auth expired or invalid - prompt to set up print(f"\n{COLOR_WARNING}Your authentication has expired or is invalid.{COLOR_RESET}") @@ -538,9 +539,10 @@ class ModlistInstallCLI: if username: print(f"{COLOR_INFO}Authorized as: {username}{COLOR_RESET}") - api_key = auth_service.ensure_valid_auth() + api_key, oauth_info = auth_service.get_auth_for_engine() if api_key: self.context['nexus_api_key'] = api_key + self.context['nexus_oauth_info'] = oauth_info # For engine auto-refresh else: print(f"{COLOR_ERROR}Failed to retrieve auth token after authorization.{COLOR_RESET}") return None @@ -738,6 +740,7 @@ class ModlistInstallCLI: modlist_arg = self.context.get('modlist_value') or self.context.get('machineid') machineid = self.context.get('machineid') api_key = self.context.get('nexus_api_key') + oauth_info = self.context.get('nexus_oauth_info') # Path to the engine binary engine_path = get_jackify_engine_path() @@ -779,24 +782,37 @@ class ModlistInstallCLI: # Store original environment values to restore later original_env_values = { 'NEXUS_API_KEY': os.environ.get('NEXUS_API_KEY'), + 'NEXUS_OAUTH_INFO': os.environ.get('NEXUS_OAUTH_INFO'), 'DOTNET_SYSTEM_GLOBALIZATION_INVARIANT': os.environ.get('DOTNET_SYSTEM_GLOBALIZATION_INVARIANT') } try: # Temporarily modify current process's environment - if api_key: + # Prefer NEXUS_OAUTH_INFO (supports auto-refresh) over NEXUS_API_KEY (legacy) + if oauth_info: + os.environ['NEXUS_OAUTH_INFO'] = oauth_info + self.logger.debug(f"Set NEXUS_OAUTH_INFO for engine (supports auto-refresh)") + # Also set NEXUS_API_KEY for backward compatibility + if api_key: + os.environ['NEXUS_API_KEY'] = api_key + elif api_key: + # No OAuth info, use API key only (no auto-refresh support) os.environ['NEXUS_API_KEY'] = api_key - self.logger.debug(f"Temporarily set os.environ['NEXUS_API_KEY'] for engine call using session-provided key.") - elif 'NEXUS_API_KEY' in os.environ: # api_key is None/empty, but a system key might exist - self.logger.debug(f"Session API key not provided. Temporarily removing inherited NEXUS_API_KEY ('{'[REDACTED]' if os.environ.get('NEXUS_API_KEY') else 'None'}') from os.environ for engine call to ensure it is not used.") - del os.environ['NEXUS_API_KEY'] - # If api_key is None and NEXUS_API_KEY was not in os.environ, it remains unset, which is correct. + self.logger.debug(f"Set NEXUS_API_KEY for engine (no auto-refresh)") + else: + # No auth available, clear any inherited values + if 'NEXUS_API_KEY' in os.environ: + del os.environ['NEXUS_API_KEY'] + if 'NEXUS_OAUTH_INFO' in os.environ: + del os.environ['NEXUS_OAUTH_INFO'] + self.logger.debug(f"No Nexus auth available, cleared inherited env vars") os.environ['DOTNET_SYSTEM_GLOBALIZATION_INVARIANT'] = "1" self.logger.debug(f"Temporarily set os.environ['DOTNET_SYSTEM_GLOBALIZATION_INVARIANT'] = '1' for engine call.") self.logger.info("Environment prepared for jackify-engine install process by modifying os.environ.") self.logger.debug(f"NEXUS_API_KEY in os.environ (pre-call): {'[SET]' if os.environ.get('NEXUS_API_KEY') else '[NOT SET]'}") + self.logger.debug(f"NEXUS_OAUTH_INFO in os.environ (pre-call): {'[SET]' if os.environ.get('NEXUS_OAUTH_INFO') else '[NOT SET]'}") pretty_cmd = ' '.join([f'"{arg}"' if ' ' in arg else arg for arg in cmd]) print(f"{COLOR_INFO}Launching Jackify Install Engine with command:{COLOR_RESET} {pretty_cmd}") diff --git a/jackify/backend/handlers/config_handler.py b/jackify/backend/handlers/config_handler.py index 93dac26..0326401 100644 --- a/jackify/backend/handlers/config_handler.py +++ b/jackify/backend/handlers/config_handler.py @@ -58,6 +58,8 @@ class ConfigHandler: "use_winetricks_for_components": True, # DEPRECATED: Migrated to component_installation_method. Kept for backward compatibility. "component_installation_method": "winetricks", # "winetricks" (default) or "system_protontricks" "game_proton_path": None, # Proton version for game shortcuts (can be any Proton 9+), separate from install proton + "proton_path": None, # Install Proton path (for jackify-engine) - None means auto-detect + "proton_version": None, # Install Proton version name - None means auto-detect "steam_restart_strategy": "jackify", # "jackify" (default) or "nak_simple" "window_width": None, # Saved window width (None = use dynamic sizing) "window_height": None # Saved window height (None = use dynamic sizing) @@ -757,16 +759,20 @@ class ConfigHandler: Always reads fresh from disk. Returns: - str: Saved Install Proton path or 'auto' if not saved + str: Saved Install Proton path, or None if not set (indicates auto-detect mode) """ try: config = self._read_config_from_disk() - proton_path = config.get("proton_path", "auto") + proton_path = config.get("proton_path") + # Return None if missing/None/empty string - don't default to "auto" + if not proton_path: + logger.debug("proton_path not set in config - will use auto-detection") + return None logger.debug(f"Retrieved fresh install proton_path from config: {proton_path}") return proton_path except Exception as e: logger.error(f"Error retrieving install proton_path: {e}") - return "auto" + return None def get_game_proton_path(self): """ @@ -775,7 +781,7 @@ class ConfigHandler: Always reads fresh from disk. Returns: - str: Saved Game Proton path, Install Proton path, or 'auto' if not saved + str: Saved Game Proton path, Install Proton path, or None if not saved (indicates auto-detect mode) """ try: config = self._read_config_from_disk() @@ -783,8 +789,12 @@ class ConfigHandler: # If game proton not set or set to same_as_install, use install proton if not game_proton_path or game_proton_path == "same_as_install": - game_proton_path = config.get("proton_path", "auto") + game_proton_path = config.get("proton_path") # Returns None if not set + # Return None if missing/None/empty string + if not game_proton_path: + logger.debug("game_proton_path not set in config - will use auto-detection") + return None logger.debug(f"Retrieved fresh game proton_path from config: {game_proton_path}") return game_proton_path except Exception as e: @@ -821,15 +831,20 @@ class ConfigHandler: logger.info(f"Auto-detected Proton: {best_proton['name']} ({proton_type})") self.save_config() else: - # Fallback to auto-detect mode - self.settings["proton_path"] = "auto" - self.settings["proton_version"] = "auto" - logger.info("No compatible Proton versions found, using auto-detect mode") + # Set proton_path to None (will appear as null in JSON) so jackify-engine doesn't get invalid path + # Code will auto-detect on each run when proton_path is None + self.settings["proton_path"] = None + self.settings["proton_version"] = None + logger.warning("No compatible Proton versions found - proton_path set to null in config.json") + logger.info("Jackify will auto-detect Proton on each run until a valid version is found") self.save_config() except Exception as e: logger.error(f"Failed to auto-detect Proton: {e}") - self.settings["proton_path"] = "auto" - self.settings["proton_version"] = "auto" + # Set proton_path to None (will appear as null in JSON) + self.settings["proton_path"] = None + self.settings["proton_version"] = None + logger.warning("proton_path set to null in config.json due to auto-detection failure") + self.save_config() \ No newline at end of file diff --git a/jackify/backend/handlers/modlist_handler.py b/jackify/backend/handlers/modlist_handler.py index 7d6316f..fbfb16c 100644 --- a/jackify/backend/handlers/modlist_handler.py +++ b/jackify/backend/handlers/modlist_handler.py @@ -105,9 +105,9 @@ class ModlistHandler: verbose: Boolean indicating if verbose output is desired. filesystem_handler: Optional FileSystemHandler instance to use instead of creating a new one. """ - # Use standard logging (no file handler) + # Use standard logging (propagate to root logger so messages appear in logs) self.logger = logging.getLogger(__name__) - self.logger.propagate = False + self.logger.propagate = True self.steamdeck = steamdeck # DEBUG: Log ModlistHandler instantiation details for SD card path debugging @@ -746,15 +746,20 @@ class ModlistHandler: try: registry_success = self._apply_universal_dotnet_fixes() except Exception as e: - self.logger.error(f"CRITICAL: Registry fixes failed - modlist may have .NET compatibility issues: {e}") + error_msg = f"CRITICAL: Registry fixes failed - modlist may have .NET compatibility issues: {e}" + self.logger.error(error_msg) + if status_callback: + status_callback(f"{self._get_progress_timestamp()} ERROR: {error_msg}") registry_success = False if not registry_success: + failure_msg = "WARNING: Universal dotnet4.x registry fixes FAILED! This modlist may experience .NET Framework compatibility issues." self.logger.error("=" * 80) - self.logger.error("WARNING: Universal dotnet4.x registry fixes FAILED!") - self.logger.error("This modlist may experience .NET Framework compatibility issues.") + self.logger.error(failure_msg) self.logger.error("Consider manually setting mscoree=native in winecfg if problems occur.") self.logger.error("=" * 80) + if status_callback: + status_callback(f"{self._get_progress_timestamp()} {failure_msg}") # Continue but user should be aware of potential issues # Step 4.6: Enable dotfiles visibility for Wine prefix @@ -1596,20 +1601,21 @@ class ModlistHandler: except Exception as e: self.logger.warning(f"Wineserver shutdown failed (non-critical): {e}") - # Registry fix 1: Set mscoree=native DLL override + # Registry fix 1: Set *mscoree=native DLL override (asterisk for full override) # This tells Wine to use native .NET runtime instead of Wine's implementation - self.logger.debug("Setting mscoree=native DLL override...") + self.logger.debug("Setting *mscoree=native DLL override...") cmd1 = [ wine_binary, 'reg', 'add', 'HKEY_CURRENT_USER\\Software\\Wine\\DllOverrides', - '/v', 'mscoree', '/t', 'REG_SZ', '/d', 'native', '/f' + '/v', '*mscoree', '/t', 'REG_SZ', '/d', 'native', '/f' ] result1 = subprocess.run(cmd1, env=env, capture_output=True, text=True, errors='replace', timeout=30) + self.logger.info(f"*mscoree registry command result: returncode={result1.returncode}, stdout={result1.stdout[:200]}, stderr={result1.stderr[:200]}") if result1.returncode == 0: - self.logger.info("Successfully applied mscoree=native DLL override") + self.logger.info("Successfully applied *mscoree=native DLL override") else: - self.logger.warning(f"Failed to set mscoree DLL override: {result1.stderr}") + self.logger.error(f"Failed to set *mscoree DLL override: returncode={result1.returncode}, stderr={result1.stderr}") # Registry fix 2: Set OnlyUseLatestCLR=1 # This prevents .NET version conflicts by using the latest CLR @@ -1621,10 +1627,11 @@ class ModlistHandler: ] result2 = subprocess.run(cmd2, env=env, capture_output=True, text=True, errors='replace', timeout=30) + self.logger.info(f"OnlyUseLatestCLR registry command result: returncode={result2.returncode}, stdout={result2.stdout[:200]}, stderr={result2.stderr[:200]}") if result2.returncode == 0: self.logger.info("Successfully applied OnlyUseLatestCLR=1 registry entry") else: - self.logger.warning(f"Failed to set OnlyUseLatestCLR: {result2.stderr}") + self.logger.error(f"Failed to set OnlyUseLatestCLR: returncode={result2.returncode}, stderr={result2.stderr}") # Force wineserver to flush registry changes to disk if wineserver_binary: @@ -1639,17 +1646,17 @@ class ModlistHandler: self.logger.info("Verifying registry entries were applied and persisted...") verification_passed = True - # Verify mscoree=native + # Verify *mscoree=native verify_cmd1 = [ wine_binary, 'reg', 'query', 'HKEY_CURRENT_USER\\Software\\Wine\\DllOverrides', - '/v', 'mscoree' + '/v', '*mscoree' ] verify_result1 = subprocess.run(verify_cmd1, env=env, capture_output=True, text=True, errors='replace', timeout=30) if verify_result1.returncode == 0 and 'native' in verify_result1.stdout: - self.logger.info("VERIFIED: mscoree=native is set correctly") + self.logger.info("VERIFIED: *mscoree=native is set correctly") else: - self.logger.error(f"VERIFICATION FAILED: mscoree=native not found in registry. Query output: {verify_result1.stdout}") + self.logger.error(f"VERIFICATION FAILED: *mscoree=native not found in registry. Query output: {verify_result1.stdout}") verification_passed = False # Verify OnlyUseLatestCLR=1 @@ -1696,10 +1703,17 @@ class ModlistHandler: ] for wine_path in wine_candidates: - if wine_path.exists(): + if wine_path.exists() and wine_path.is_file(): self.logger.info(f"Using Wine binary from user's configured Proton: {wine_path}") return str(wine_path) + # Wine binary not found at expected paths - search recursively in Proton directory + self.logger.debug(f"Wine binary not found at expected paths in {proton_path}, searching recursively...") + wine_binary = self._search_wine_in_proton_directory(proton_path) + if wine_binary: + self.logger.info(f"Found Wine binary via recursive search in Proton directory: {wine_binary}") + return wine_binary + self.logger.warning(f"User's configured Proton path has no wine binary: {user_proton_path}") # Fallback: Try to use same Steam library detection as main Proton detection @@ -1719,4 +1733,42 @@ class ModlistHandler: self.logger.error(f"Error finding Wine binary: {e}") return None + def _search_wine_in_proton_directory(self, proton_path: Path) -> Optional[str]: + """ + Recursively search for wine binary within a Proton directory. + This handles cases where the directory structure might differ between Proton versions. + + Args: + proton_path: Path to the Proton directory to search + + Returns: + Path to wine binary if found, None otherwise + """ + try: + if not proton_path.exists() or not proton_path.is_dir(): + return None + + # Search for 'wine' executable (not 'wine64' or 'wine-preloader') + # Limit search depth to avoid scanning entire filesystem + max_depth = 5 + for root, dirs, files in os.walk(proton_path, followlinks=False): + # Calculate depth relative to proton_path + depth = len(Path(root).relative_to(proton_path).parts) + if depth > max_depth: + dirs.clear() # Don't descend further + continue + + # Check if 'wine' is in this directory + if 'wine' in files: + wine_path = Path(root) / 'wine' + # Verify it's actually an executable file + if wine_path.is_file() and os.access(wine_path, os.X_OK): + self.logger.debug(f"Found wine binary at: {wine_path}") + return str(wine_path) + + return None + except Exception as e: + self.logger.debug(f"Error during recursive wine search in {proton_path}: {e}") + return None + \ No newline at end of file diff --git a/jackify/backend/handlers/modlist_install_cli.py b/jackify/backend/handlers/modlist_install_cli.py index eff1ecb..cda9b6f 100644 --- a/jackify/backend/handlers/modlist_install_cli.py +++ b/jackify/backend/handlers/modlist_install_cli.py @@ -427,10 +427,11 @@ class ModlistInstallCLI: print("\n" + "-" * 28) print(f"{COLOR_INFO}Nexus Authentication: Using API Key (Legacy){COLOR_RESET}") - # Get valid token/key - api_key = auth_service.ensure_valid_auth() + # Get valid token/key and OAuth state for engine auto-refresh + api_key, oauth_info = auth_service.get_auth_for_engine() if api_key: self.context['nexus_api_key'] = api_key + self.context['nexus_oauth_info'] = oauth_info # For engine auto-refresh else: # Auth expired or invalid - prompt to set up print(f"\n{COLOR_WARNING}Your authentication has expired or is invalid.{COLOR_RESET}") @@ -463,9 +464,10 @@ class ModlistInstallCLI: if username: print(f"{COLOR_INFO}Authorized as: {username}{COLOR_RESET}") - api_key = auth_service.ensure_valid_auth() + api_key, oauth_info = auth_service.get_auth_for_engine() if api_key: self.context['nexus_api_key'] = api_key + self.context['nexus_oauth_info'] = oauth_info # For engine auto-refresh else: print(f"{COLOR_ERROR}Failed to retrieve auth token after authorization.{COLOR_RESET}") return None @@ -616,6 +618,7 @@ class ModlistInstallCLI: modlist_arg = self.context.get('modlist_value') or self.context.get('machineid') machineid = self.context.get('machineid') api_key = self.context['nexus_api_key'] + oauth_info = self.context.get('nexus_oauth_info') # Path to the engine binary engine_path = get_jackify_engine_path() @@ -675,24 +678,37 @@ class ModlistInstallCLI: # Store original environment values to restore later original_env_values = { 'NEXUS_API_KEY': os.environ.get('NEXUS_API_KEY'), + 'NEXUS_OAUTH_INFO': os.environ.get('NEXUS_OAUTH_INFO'), 'DOTNET_SYSTEM_GLOBALIZATION_INVARIANT': os.environ.get('DOTNET_SYSTEM_GLOBALIZATION_INVARIANT') } try: # Temporarily modify current process's environment - if api_key: + # Prefer NEXUS_OAUTH_INFO (supports auto-refresh) over NEXUS_API_KEY (legacy) + if oauth_info: + os.environ['NEXUS_OAUTH_INFO'] = oauth_info + self.logger.debug(f"Set NEXUS_OAUTH_INFO for engine (supports auto-refresh)") + # Also set NEXUS_API_KEY for backward compatibility + if api_key: + os.environ['NEXUS_API_KEY'] = api_key + elif api_key: + # No OAuth info, use API key only (no auto-refresh support) os.environ['NEXUS_API_KEY'] = api_key - self.logger.debug(f"Temporarily set os.environ['NEXUS_API_KEY'] for engine call using session-provided key.") - elif 'NEXUS_API_KEY' in os.environ: # api_key is None/empty, but a system key might exist - self.logger.debug(f"Session API key not provided. Temporarily removing inherited NEXUS_API_KEY ('{'[REDACTED]' if os.environ.get('NEXUS_API_KEY') else 'None'}') from os.environ for engine call to ensure it is not used.") - del os.environ['NEXUS_API_KEY'] - # If api_key is None and NEXUS_API_KEY was not in os.environ, it remains unset, which is correct. + self.logger.debug(f"Set NEXUS_API_KEY for engine (no auto-refresh)") + else: + # No auth available, clear any inherited values + if 'NEXUS_API_KEY' in os.environ: + del os.environ['NEXUS_API_KEY'] + if 'NEXUS_OAUTH_INFO' in os.environ: + del os.environ['NEXUS_OAUTH_INFO'] + self.logger.debug(f"No Nexus auth available, cleared inherited env vars") os.environ['DOTNET_SYSTEM_GLOBALIZATION_INVARIANT'] = "1" self.logger.debug(f"Temporarily set os.environ['DOTNET_SYSTEM_GLOBALIZATION_INVARIANT'] = '1' for engine call.") - + self.logger.info("Environment prepared for jackify-engine install process by modifying os.environ.") self.logger.debug(f"NEXUS_API_KEY in os.environ (pre-call): {'[SET]' if os.environ.get('NEXUS_API_KEY') else '[NOT SET]'}") + self.logger.debug(f"NEXUS_OAUTH_INFO in os.environ (pre-call): {'[SET]' if os.environ.get('NEXUS_OAUTH_INFO') else '[NOT SET]'}") pretty_cmd = ' '.join([f'"{arg}"' if ' ' in arg else arg for arg in cmd]) print(f"{COLOR_INFO}Launching Jackify Install Engine with command:{COLOR_RESET} {pretty_cmd}") diff --git a/jackify/backend/handlers/protontricks_handler.py b/jackify/backend/handlers/protontricks_handler.py index 20a5dbc..f0165d2 100644 --- a/jackify/backend/handlers/protontricks_handler.py +++ b/jackify/backend/handlers/protontricks_handler.py @@ -596,19 +596,29 @@ class ProtontricksHandler: try: if user_reg_path.exists(): content = user_reg_path.read_text(encoding='utf-8', errors='ignore') - if "ShowDotFiles" not in content: + # Check for CORRECT format with proper backslash escaping + has_correct_format = '[Software\\\\Wine]' in content and '"ShowDotFiles"="Y"' in content + has_broken_format = '[SoftwareWine]' in content and '"ShowDotFiles"="Y"' in content + + if has_broken_format and not has_correct_format: + # Fix the broken format by replacing the section header + logger.debug(f"Found broken ShowDotFiles format in {user_reg_path}, fixing...") + content = content.replace('[SoftwareWine]', '[Software\\\\Wine]') + user_reg_path.write_text(content, encoding='utf-8') + dotfiles_set_success = True + elif not has_correct_format: logger.debug(f"Adding ShowDotFiles entry to {user_reg_path}") with open(user_reg_path, 'a', encoding='utf-8') as f: - f.write('\n[Software\\Wine] 1603891765\n') + f.write('\n[Software\\\\Wine] 1603891765\n') f.write('"ShowDotFiles"="Y"\n') dotfiles_set_success = True # Count file write as success too else: - logger.debug("ShowDotFiles already present in user.reg") + logger.debug("ShowDotFiles already present in correct format in user.reg") dotfiles_set_success = True # Already there counts as success else: logger.warning(f"user.reg not found at {user_reg_path}, creating it.") with open(user_reg_path, 'w', encoding='utf-8') as f: - f.write('[Software\\Wine] 1603891765\n') + f.write('[Software\\\\Wine] 1603891765\n') f.write('"ShowDotFiles"="Y"\n') dotfiles_set_success = True # Creating file counts as success except Exception as e: diff --git a/jackify/backend/handlers/subprocess_utils.py b/jackify/backend/handlers/subprocess_utils.py index a3fec27..9c3d53b 100644 --- a/jackify/backend/handlers/subprocess_utils.py +++ b/jackify/backend/handlers/subprocess_utils.py @@ -41,8 +41,8 @@ def get_clean_subprocess_env(extra_env=None): """ Returns a copy of os.environ with bundled-runtime variables and other problematic entries removed. Optionally merges in extra_env dict. - Also ensures bundled tools (lz4, unzip, etc.) are in PATH when running as AppImage. - CRITICAL: Preserves system PATH to ensure system tools (like lz4) are available. + Also ensures bundled tools (lz4, cabextract, winetricks) are in PATH when running as AppImage. + CRITICAL: Preserves system PATH to ensure system utilities (wget, curl, unzip, xz, gzip, sha256sum) are available. """ from pathlib import Path @@ -73,7 +73,8 @@ def get_clean_subprocess_env(extra_env=None): path_parts.append(sys_path) # Add bundled tools directory to PATH if running as AppImage - # This ensures lz4, unzip, xz, etc. are available to subprocesses + # This ensures lz4, cabextract, and winetricks are available to subprocesses + # System utilities (wget, curl, unzip, xz, gzip, sha256sum) come from system PATH # Note: appdir was saved before env cleanup above tools_dir = None @@ -100,23 +101,23 @@ def get_clean_subprocess_env(extra_env=None): tools_dir = str(possible_dir) break - # Build final PATH: bundled tools first (if any), then original PATH with system paths + # Build final PATH: system PATH first, then bundled tools (lz4, cabextract, winetricks) + # System utilities (wget, curl, unzip, xz, gzip, sha256sum) are preferred from system final_path_parts = [] - if tools_dir and os.path.isdir(tools_dir): - # Prepend tools directory so bundled tools take precedence - # This is critical - bundled lz4 must come before system lz4 - final_path_parts.append(tools_dir) - # Add all other paths (preserving order, removing duplicates) - # Note: AppRun already sets PATH with tools directory, but we ensure it's first + # Add all other paths first (system utilities take precedence) seen = set() - if tools_dir: - seen.add(tools_dir) # Already added, don't add again for path_part in path_parts: if path_part and path_part not in seen: final_path_parts.append(path_part) seen.add(path_part) + # Then add bundled tools directory (for lz4, cabextract, winetricks) + if tools_dir and os.path.isdir(tools_dir) and tools_dir not in seen: + final_path_parts.append(tools_dir) + seen.add(tools_dir) + + env['PATH'] = ':'.join(final_path_parts) # Optionally restore LD_LIBRARY_PATH to system default if needed diff --git a/jackify/backend/handlers/winetricks_handler.py b/jackify/backend/handlers/winetricks_handler.py index e6e8c39..8f1b771 100644 --- a/jackify/backend/handlers/winetricks_handler.py +++ b/jackify/backend/handlers/winetricks_handler.py @@ -149,7 +149,7 @@ class WinetricksHandler: # If user selected a specific Proton, try that first wine_binary = None - if user_proton_path != 'auto': + if user_proton_path and user_proton_path != 'auto': # Check if user-selected Proton still exists if os.path.exists(user_proton_path): # Resolve symlinks to handle ~/.steam/steam -> ~/.local/share/Steam @@ -582,7 +582,7 @@ class WinetricksHandler: user_proton_path = config.get_proton_path() wine_binary = None - if user_proton_path != 'auto': + if user_proton_path and user_proton_path != 'auto': if os.path.exists(user_proton_path): resolved_proton_path = os.path.realpath(user_proton_path) valve_proton_wine = os.path.join(resolved_proton_path, 'dist', 'bin', 'wine') @@ -594,8 +594,8 @@ class WinetricksHandler: wine_binary = ge_proton_wine if not wine_binary: - if user_proton_path == 'auto': - self.logger.info("Auto-detecting Proton (user selected 'auto')") + if not user_proton_path or user_proton_path == 'auto': + self.logger.info("Auto-detecting Proton (user selected 'auto' or path not set)") best_proton = WineUtils.select_best_proton() if best_proton: wine_binary = WineUtils.find_proton_binary(best_proton['name']) @@ -811,7 +811,7 @@ class WinetricksHandler: # If user selected a specific Proton, try that first wine_binary = None - if user_proton_path != 'auto': + if user_proton_path and user_proton_path != 'auto': if os.path.exists(user_proton_path): resolved_proton_path = os.path.realpath(user_proton_path) valve_proton_wine = os.path.join(resolved_proton_path, 'dist', 'bin', 'wine') @@ -822,10 +822,10 @@ class WinetricksHandler: elif os.path.exists(ge_proton_wine): wine_binary = ge_proton_wine - # Only auto-detect if user explicitly chose 'auto' + # Only auto-detect if user explicitly chose 'auto' or path is not set if not wine_binary: - if user_proton_path == 'auto': - self.logger.info("Auto-detecting Proton (user selected 'auto')") + if not user_proton_path or user_proton_path == 'auto': + self.logger.info("Auto-detecting Proton (user selected 'auto' or path not set)") best_proton = WineUtils.select_best_proton() if best_proton: wine_binary = WineUtils.find_proton_binary(best_proton['name']) diff --git a/jackify/backend/services/automated_prefix_service.py b/jackify/backend/services/automated_prefix_service.py index a4ebcaf..9974640 100644 --- a/jackify/backend/services/automated_prefix_service.py +++ b/jackify/backend/services/automated_prefix_service.py @@ -71,7 +71,7 @@ class AutomatedPrefixService: config_handler = ConfigHandler() user_proton_path = config_handler.get_game_proton_path() - if user_proton_path == 'auto': + if not user_proton_path or user_proton_path == 'auto': # Use enhanced fallback logic with GE-Proton preference logger.info("User selected auto-detect, using GE-Proton → Experimental → Proton precedence") return WineUtils.select_best_proton() @@ -3095,20 +3095,20 @@ echo Prefix creation complete. env['WINEPREFIX'] = prefix_path env['WINEDEBUG'] = '-all' # Suppress Wine debug output - # Registry fix 1: Set mscoree=native DLL override + # Registry fix 1: Set *mscoree=native DLL override (asterisk for full override) # This tells Wine to use native .NET runtime instead of Wine's implementation - logger.debug("Setting mscoree=native DLL override...") + logger.debug("Setting *mscoree=native DLL override...") cmd1 = [ wine_binary, 'reg', 'add', 'HKEY_CURRENT_USER\\Software\\Wine\\DllOverrides', - '/v', 'mscoree', '/t', 'REG_SZ', '/d', 'native', '/f' + '/v', '*mscoree', '/t', 'REG_SZ', '/d', 'native', '/f' ] result1 = subprocess.run(cmd1, env=env, capture_output=True, text=True, errors='replace') if result1.returncode == 0: - logger.info("Successfully applied mscoree=native DLL override") + logger.info("Successfully applied *mscoree=native DLL override") else: - logger.warning(f"Failed to set mscoree DLL override: {result1.stderr}") + logger.warning(f"Failed to set *mscoree DLL override: {result1.stderr}") # Registry fix 2: Set OnlyUseLatestCLR=1 # This prevents .NET version conflicts by using the latest CLR @@ -3140,39 +3140,96 @@ echo Prefix creation complete. def _find_wine_binary_for_registry(self, modlist_compatdata_path: str) -> Optional[str]: """Find the appropriate Wine binary for registry operations""" try: - # Method 1: Try to detect from Steam's config or use Proton from compat data - # Look for wine binary in common Proton locations - proton_paths = [ - os.path.expanduser("~/.local/share/Steam/compatibilitytools.d"), - os.path.expanduser("~/.steam/steam/steamapps/common") - ] + from ..handlers.config_handler import ConfigHandler + from ..handlers.wine_utils import WineUtils + + # Method 1: Use the user's configured Proton version from settings + config_handler = ConfigHandler() + user_proton_path = config_handler.get_game_proton_path() - for base_path in proton_paths: - if os.path.exists(base_path): - for item in os.listdir(base_path): - if 'proton' in item.lower(): - wine_path = os.path.join(base_path, item, 'files', 'bin', 'wine') - if os.path.exists(wine_path): - logger.debug(f"Found Wine binary: {wine_path}") - return wine_path + if user_proton_path and user_proton_path != 'auto': + # User has selected a specific Proton version + proton_path = Path(user_proton_path).expanduser() - # Method 2: Fallback to system wine if available - try: - result = subprocess.run(['which', 'wine'], capture_output=True, text=True) - if result.returncode == 0: - wine_path = result.stdout.strip() - logger.debug(f"Using system Wine binary: {wine_path}") - return wine_path - except Exception: - pass + # Check for wine binary in both GE-Proton and Valve Proton structures + wine_candidates = [ + proton_path / "files" / "bin" / "wine", # GE-Proton structure + proton_path / "dist" / "bin" / "wine" # Valve Proton structure + ] - logger.error("No suitable Wine binary found for registry operations") + for wine_path in wine_candidates: + if wine_path.exists() and wine_path.is_file(): + logger.info(f"Using Wine binary from user's configured Proton: {wine_path}") + return str(wine_path) + + # Wine binary not found at expected paths - search recursively in Proton directory + logger.debug(f"Wine binary not found at expected paths in {proton_path}, searching recursively...") + wine_binary = self._search_wine_in_proton_directory(proton_path) + if wine_binary: + logger.info(f"Found Wine binary via recursive search in Proton directory: {wine_binary}") + return wine_binary + + logger.warning(f"User's configured Proton path has no wine binary: {user_proton_path}") + + # Method 2: Fallback to auto-detection using WineUtils + best_proton = WineUtils.select_best_proton() + if best_proton: + wine_binary = WineUtils.find_proton_binary(best_proton['name']) + if wine_binary: + logger.info(f"Using Wine binary from detected Proton: {wine_binary}") + return wine_binary + + # NEVER fall back to system wine - it will break Proton prefixes with architecture mismatches + logger.error("No suitable Proton Wine binary found for registry operations") return None except Exception as e: logger.error(f"Error finding Wine binary: {e}") return None + def _search_wine_in_proton_directory(self, proton_path: Path) -> Optional[str]: + """ + Recursively search for wine binary within a Proton directory. + This handles cases where the directory structure might differ between Proton versions. + + Args: + proton_path: Path to the Proton directory to search + + Returns: + Path to wine binary if found, None otherwise + """ + try: + if not proton_path.exists() or not proton_path.is_dir(): + return None + + # Search for 'wine' executable (not 'wine64' or 'wine-preloader') + # Limit search depth to avoid scanning entire filesystem + max_depth = 5 + for root, dirs, files in os.walk(proton_path, followlinks=False): + # Calculate depth relative to proton_path + try: + depth = len(Path(root).relative_to(proton_path).parts) + except ValueError: + # Path is not relative to proton_path (shouldn't happen, but be safe) + continue + + if depth > max_depth: + dirs.clear() # Don't descend further + continue + + # Check if 'wine' is in this directory + if 'wine' in files: + wine_path = Path(root) / 'wine' + # Verify it's actually an executable file + if wine_path.is_file() and os.access(wine_path, os.X_OK): + logger.debug(f"Found wine binary at: {wine_path}") + return str(wine_path) + + return None + except Exception as e: + logger.debug(f"Error during recursive wine search in {proton_path}: {e}") + return None + def _inject_game_registry_entries(self, modlist_compatdata_path: str): """Detect and inject FNV/Enderal game paths and apply universal dotnet4.x compatibility fixes""" system_reg_path = os.path.join(modlist_compatdata_path, "pfx", "system.reg") diff --git a/jackify/backend/services/modlist_service.py b/jackify/backend/services/modlist_service.py index d03c7cc..bddc769 100644 --- a/jackify/backend/services/modlist_service.py +++ b/jackify/backend/services/modlist_service.py @@ -276,7 +276,8 @@ class ModlistService: download_dir_str = str(actual_download_path) api_key = context['nexus_api_key'] - + oauth_info = context.get('nexus_oauth_info') + # Path to the engine binary (copied from working code) engine_path = get_jackify_engine_path() engine_dir = os.path.dirname(engine_path) @@ -302,16 +303,26 @@ class ModlistService: # Store original environment values (copied from working code) original_env_values = { 'NEXUS_API_KEY': os.environ.get('NEXUS_API_KEY'), + 'NEXUS_OAUTH_INFO': os.environ.get('NEXUS_OAUTH_INFO'), 'DOTNET_SYSTEM_GLOBALIZATION_INVARIANT': os.environ.get('DOTNET_SYSTEM_GLOBALIZATION_INVARIANT') } - + try: - # Environment setup (copied from working code) - if api_key: + # Environment setup - prefer NEXUS_OAUTH_INFO (supports auto-refresh) over NEXUS_API_KEY + if oauth_info: + os.environ['NEXUS_OAUTH_INFO'] = oauth_info + # Also set NEXUS_API_KEY for backward compatibility + if api_key: + os.environ['NEXUS_API_KEY'] = api_key + elif api_key: os.environ['NEXUS_API_KEY'] = api_key - elif 'NEXUS_API_KEY' in os.environ: - del os.environ['NEXUS_API_KEY'] - + else: + # No auth available, clear any inherited values + if 'NEXUS_API_KEY' in os.environ: + del os.environ['NEXUS_API_KEY'] + if 'NEXUS_OAUTH_INFO' in os.environ: + del os.environ['NEXUS_OAUTH_INFO'] + os.environ['DOTNET_SYSTEM_GLOBALIZATION_INVARIANT'] = "1" pretty_cmd = ' '.join([f'"{arg}"' if ' ' in arg else arg for arg in cmd]) diff --git a/jackify/backend/services/nexus_auth_service.py b/jackify/backend/services/nexus_auth_service.py index b53529d..1459a12 100644 --- a/jackify/backend/services/nexus_auth_service.py +++ b/jackify/backend/services/nexus_auth_service.py @@ -228,16 +228,65 @@ class NexusAuthService: return auth_token - def get_auth_for_engine(self) -> Optional[str]: + def get_auth_for_engine(self) -> Tuple[Optional[str], Optional[str]]: """ - Get authentication token for jackify-engine - Same as ensure_valid_auth() - engine uses NEXUS_API_KEY env var for both OAuth and API keys - (This matches upstream Wabbajack behavior) + Get authentication for jackify-engine with auto-refresh support + + Returns both NEXUS_API_KEY (for backward compat) and NEXUS_OAUTH_INFO (for auto-refresh). + When NEXUS_OAUTH_INFO is provided, the engine can automatically refresh expired tokens + during long installations. Returns: - Valid auth token to pass via NEXUS_API_KEY environment variable, or None + Tuple of (nexus_api_key, nexus_oauth_info_json) + - nexus_api_key: Access token or API key (for backward compat) + - nexus_oauth_info_json: Full OAuth state JSON (for auto-refresh) or None """ - return self.ensure_valid_auth() + import json + import time + + # Check if using OAuth and ensure token is fresh + if self.token_handler.has_token(): + # Refresh token if expired (15 minute buffer for long installs) + access_token = self._get_oauth_token() + if not access_token: + logger.warning("OAuth token refresh failed, cannot provide auth to engine") + return (None, None) + + # Load the refreshed token data + token_data = self.token_handler.load_token() + + if token_data: + oauth_data = token_data.get('oauth', {}) + + # Build NexusOAuthState JSON matching upstream Wabbajack format + # This allows engine to auto-refresh tokens during long installations + nexus_oauth_state = { + "oauth": { + "access_token": oauth_data.get('access_token'), + "token_type": oauth_data.get('token_type', 'Bearer'), + "expires_in": oauth_data.get('expires_in', 3600), + "refresh_token": oauth_data.get('refresh_token'), + "scope": oauth_data.get('scope', 'public openid profile'), + "created_at": oauth_data.get('created_at', int(time.time())), + "_received_at": token_data.get('_saved_at', int(time.time())) * 10000000 + 116444736000000000 # Convert Unix to Windows FILETIME + }, + "api_key": "" + } + + nexus_oauth_json = json.dumps(nexus_oauth_state) + access_token = oauth_data.get('access_token') + + logger.info("Providing OAuth state to engine for auto-refresh capability") + return (access_token, nexus_oauth_json) + + # Fall back to API key (no auto-refresh support) + api_key = self.api_key_service.get_saved_api_key() + if api_key: + logger.info("Using API key for engine (no auto-refresh)") + return (api_key, None) + + logger.warning("No authentication available for engine") + return (None, None) def clear_all_auth(self) -> bool: """ diff --git a/jackify/engine/TestBackslashFix.dll b/jackify/engine/TestBackslashFix.dll new file mode 100644 index 0000000..cb1f114 Binary files /dev/null and b/jackify/engine/TestBackslashFix.dll differ diff --git a/jackify/engine/Wabbajack.CLI.Builder.dll b/jackify/engine/Wabbajack.CLI.Builder.dll index 8416535..c6ec948 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 6fdbe66..13f9479 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 59ed9cc..19485e1 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 6bf85a3..5cbda37 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 21caded..107707e 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 5548374..84150e6 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 50e12ac..49dd9f5 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 dc1af8f..d88029e 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 f136897..14eeae4 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 43ece97..e5b931d 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 7a65cdb..c6710da 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 5b0bd38..d2e6f27 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 faea021..c556085 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 312ff41..2ac647d 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 0b127a1..5d95a4e 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 d754560..db3ff3e 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 9e9ac97..f12a053 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 abfa3e1..1f83c4d 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 a1759f5..e9f5ed7 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 f31aab6..70f4702 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 6073a3d..f5c6683 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 4e424fc..a3ef07e 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 20c6459..27b593c 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 8a0b49a..0a37da7 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 2d43bfe..0913006 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 be2b32a..edac537 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 f9f6a63..0103a83 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 04724dc..b13f573 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 b29dd77..0513090 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 b7d0e6b..cd6a8b7 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 ebb3c11..ce7160a 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 9b1925d..b1dc6ad 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 6361ed6..0a705c2 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 957c752..8757fd6 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 b70e2ca..fcfb9fa 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 c0703f1..abe79f4 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 be1d901..f4b42c4 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 897541a..6fb36cb 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 e52ec45..cc64132 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 7f65394..08753d0 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 7699157..55fc12a 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.4.4": { + "jackify-engine/0.4.5": { "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.4.4", - "Wabbajack.Downloaders.Bethesda": "0.4.4", - "Wabbajack.Downloaders.Dispatcher": "0.4.4", - "Wabbajack.Hashing.xxHash64": "0.4.4", - "Wabbajack.Networking.Discord": "0.4.4", - "Wabbajack.Networking.GitHub": "0.4.4", - "Wabbajack.Paths.IO": "0.4.4", - "Wabbajack.Server.Lib": "0.4.4", - "Wabbajack.Services.OSIntegrated": "0.4.4", - "Wabbajack.VFS": "0.4.4", + "Wabbajack.CLI.Builder": "0.4.5", + "Wabbajack.Downloaders.Bethesda": "0.4.5", + "Wabbajack.Downloaders.Dispatcher": "0.4.5", + "Wabbajack.Hashing.xxHash64": "0.4.5", + "Wabbajack.Networking.Discord": "0.4.5", + "Wabbajack.Networking.GitHub": "0.4.5", + "Wabbajack.Paths.IO": "0.4.5", + "Wabbajack.Server.Lib": "0.4.5", + "Wabbajack.Services.OSIntegrated": "0.4.5", + "Wabbajack.VFS": "0.4.5", "MegaApiClient": "1.0.0.0", "runtimepack.Microsoft.NETCore.App.Runtime.linux-x64": "8.0.22" }, @@ -1781,7 +1781,7 @@ } } }, - "Wabbajack.CLI.Builder/0.4.4": { + "Wabbajack.CLI.Builder/0.4.5": { "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.4.4" + "Wabbajack.Paths": "0.4.5" }, "runtime": { "Wabbajack.CLI.Builder.dll": {} } }, - "Wabbajack.Common/0.4.4": { + "Wabbajack.Common/0.4.5": { "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.4.4", - "Wabbajack.Networking.Http": "0.4.4", - "Wabbajack.Paths.IO": "0.4.4" + "Wabbajack.DTOs": "0.4.5", + "Wabbajack.Networking.Http": "0.4.5", + "Wabbajack.Paths.IO": "0.4.5" }, "runtime": { "Wabbajack.Common.dll": {} } }, - "Wabbajack.Compiler/0.4.4": { + "Wabbajack.Compiler/0.4.5": { "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.4.4", - "Wabbajack.Installer": "0.4.4", - "Wabbajack.VFS": "0.4.4", + "Wabbajack.Downloaders.Dispatcher": "0.4.5", + "Wabbajack.Installer": "0.4.5", + "Wabbajack.VFS": "0.4.5", "ini-parser-netstandard": "2.5.2" }, "runtime": { "Wabbajack.Compiler.dll": {} } }, - "Wabbajack.Compression.BSA/0.4.4": { + "Wabbajack.Compression.BSA/0.4.5": { "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.4.4", - "Wabbajack.DTOs": "0.4.4" + "Wabbajack.Common": "0.4.5", + "Wabbajack.DTOs": "0.4.5" }, "runtime": { "Wabbajack.Compression.BSA.dll": {} } }, - "Wabbajack.Compression.Zip/0.4.4": { + "Wabbajack.Compression.Zip/0.4.5": { "dependencies": { - "Wabbajack.IO.Async": "0.4.4" + "Wabbajack.IO.Async": "0.4.5" }, "runtime": { "Wabbajack.Compression.Zip.dll": {} } }, - "Wabbajack.Configuration/0.4.4": { + "Wabbajack.Configuration/0.4.5": { "runtime": { "Wabbajack.Configuration.dll": {} } }, - "Wabbajack.Downloaders.Bethesda/0.4.4": { + "Wabbajack.Downloaders.Bethesda/0.4.5": { "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.4.4", - "Wabbajack.Downloaders.Interfaces": "0.4.4", - "Wabbajack.Networking.BethesdaNet": "0.4.4" + "Wabbajack.Common": "0.4.5", + "Wabbajack.Downloaders.Interfaces": "0.4.5", + "Wabbajack.Networking.BethesdaNet": "0.4.5" }, "runtime": { "Wabbajack.Downloaders.Bethesda.dll": {} } }, - "Wabbajack.Downloaders.Dispatcher/0.4.4": { + "Wabbajack.Downloaders.Dispatcher/0.4.5": { "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.4.4", - "Wabbajack.Downloaders.GameFile": "0.4.4", - "Wabbajack.Downloaders.GoogleDrive": "0.4.4", - "Wabbajack.Downloaders.Http": "0.4.4", - "Wabbajack.Downloaders.IPS4OAuth2Downloader": "0.4.4", - "Wabbajack.Downloaders.Interfaces": "0.4.4", - "Wabbajack.Downloaders.Manual": "0.4.4", - "Wabbajack.Downloaders.MediaFire": "0.4.4", - "Wabbajack.Downloaders.Mega": "0.4.4", - "Wabbajack.Downloaders.ModDB": "0.4.4", - "Wabbajack.Downloaders.Nexus": "0.4.4", - "Wabbajack.Downloaders.VerificationCache": "0.4.4", - "Wabbajack.Downloaders.WabbajackCDN": "0.4.4", - "Wabbajack.Networking.WabbajackClientApi": "0.4.4" + "Wabbajack.Downloaders.Bethesda": "0.4.5", + "Wabbajack.Downloaders.GameFile": "0.4.5", + "Wabbajack.Downloaders.GoogleDrive": "0.4.5", + "Wabbajack.Downloaders.Http": "0.4.5", + "Wabbajack.Downloaders.IPS4OAuth2Downloader": "0.4.5", + "Wabbajack.Downloaders.Interfaces": "0.4.5", + "Wabbajack.Downloaders.Manual": "0.4.5", + "Wabbajack.Downloaders.MediaFire": "0.4.5", + "Wabbajack.Downloaders.Mega": "0.4.5", + "Wabbajack.Downloaders.ModDB": "0.4.5", + "Wabbajack.Downloaders.Nexus": "0.4.5", + "Wabbajack.Downloaders.VerificationCache": "0.4.5", + "Wabbajack.Downloaders.WabbajackCDN": "0.4.5", + "Wabbajack.Networking.WabbajackClientApi": "0.4.5" }, "runtime": { "Wabbajack.Downloaders.Dispatcher.dll": {} } }, - "Wabbajack.Downloaders.GameFile/0.4.4": { + "Wabbajack.Downloaders.GameFile/0.4.5": { "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.4.4", - "Wabbajack.VFS": "0.4.4" + "Wabbajack.Downloaders.Interfaces": "0.4.5", + "Wabbajack.VFS": "0.4.5" }, "runtime": { "Wabbajack.Downloaders.GameFile.dll": {} } }, - "Wabbajack.Downloaders.GoogleDrive/0.4.4": { + "Wabbajack.Downloaders.GoogleDrive/0.4.5": { "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.4.4", - "Wabbajack.DTOs": "0.4.4", - "Wabbajack.Downloaders.Interfaces": "0.4.4", - "Wabbajack.Networking.Http": "0.4.4", - "Wabbajack.Networking.Http.Interfaces": "0.4.4" + "Wabbajack.Common": "0.4.5", + "Wabbajack.DTOs": "0.4.5", + "Wabbajack.Downloaders.Interfaces": "0.4.5", + "Wabbajack.Networking.Http": "0.4.5", + "Wabbajack.Networking.Http.Interfaces": "0.4.5" }, "runtime": { "Wabbajack.Downloaders.GoogleDrive.dll": {} } }, - "Wabbajack.Downloaders.Http/0.4.4": { + "Wabbajack.Downloaders.Http/0.4.5": { "dependencies": { "Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.Logging.Abstractions": "9.0.1", - "Wabbajack.Common": "0.4.4", - "Wabbajack.DTOs": "0.4.4", - "Wabbajack.Downloaders.Interfaces": "0.4.4", - "Wabbajack.Networking.BethesdaNet": "0.4.4", - "Wabbajack.Networking.Http.Interfaces": "0.4.4", - "Wabbajack.Paths.IO": "0.4.4" + "Wabbajack.Common": "0.4.5", + "Wabbajack.DTOs": "0.4.5", + "Wabbajack.Downloaders.Interfaces": "0.4.5", + "Wabbajack.Networking.BethesdaNet": "0.4.5", + "Wabbajack.Networking.Http.Interfaces": "0.4.5", + "Wabbajack.Paths.IO": "0.4.5" }, "runtime": { "Wabbajack.Downloaders.Http.dll": {} } }, - "Wabbajack.Downloaders.Interfaces/0.4.4": { + "Wabbajack.Downloaders.Interfaces/0.4.5": { "dependencies": { "Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", - "Wabbajack.Compression.Zip": "0.4.4", - "Wabbajack.DTOs": "0.4.4", - "Wabbajack.Paths.IO": "0.4.4" + "Wabbajack.Compression.Zip": "0.4.5", + "Wabbajack.DTOs": "0.4.5", + "Wabbajack.Paths.IO": "0.4.5" }, "runtime": { "Wabbajack.Downloaders.Interfaces.dll": {} } }, - "Wabbajack.Downloaders.IPS4OAuth2Downloader/0.4.4": { + "Wabbajack.Downloaders.IPS4OAuth2Downloader/0.4.5": { "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.4.4", - "Wabbajack.Downloaders.Interfaces": "0.4.4", - "Wabbajack.Networking.Http": "0.4.4", - "Wabbajack.Networking.Http.Interfaces": "0.4.4" + "Wabbajack.Common": "0.4.5", + "Wabbajack.Downloaders.Interfaces": "0.4.5", + "Wabbajack.Networking.Http": "0.4.5", + "Wabbajack.Networking.Http.Interfaces": "0.4.5" }, "runtime": { "Wabbajack.Downloaders.IPS4OAuth2Downloader.dll": {} } }, - "Wabbajack.Downloaders.Manual/0.4.4": { + "Wabbajack.Downloaders.Manual/0.4.5": { "dependencies": { "Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.Logging.Abstractions": "9.0.1", - "Wabbajack.Common": "0.4.4", - "Wabbajack.Downloaders.Interfaces": "0.4.4" + "Wabbajack.Common": "0.4.5", + "Wabbajack.Downloaders.Interfaces": "0.4.5" }, "runtime": { "Wabbajack.Downloaders.Manual.dll": {} } }, - "Wabbajack.Downloaders.MediaFire/0.4.4": { + "Wabbajack.Downloaders.MediaFire/0.4.5": { "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.4.4", - "Wabbajack.Downloaders.Interfaces": "0.4.4", - "Wabbajack.Networking.Http.Interfaces": "0.4.4" + "Wabbajack.Common": "0.4.5", + "Wabbajack.Downloaders.Interfaces": "0.4.5", + "Wabbajack.Networking.Http.Interfaces": "0.4.5" }, "runtime": { "Wabbajack.Downloaders.MediaFire.dll": {} } }, - "Wabbajack.Downloaders.Mega/0.4.4": { + "Wabbajack.Downloaders.Mega/0.4.5": { "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.4.4", - "Wabbajack.Downloaders.Interfaces": "0.4.4", - "Wabbajack.Paths.IO": "0.4.4" + "Wabbajack.Common": "0.4.5", + "Wabbajack.Downloaders.Interfaces": "0.4.5", + "Wabbajack.Paths.IO": "0.4.5" }, "runtime": { "Wabbajack.Downloaders.Mega.dll": {} } }, - "Wabbajack.Downloaders.ModDB/0.4.4": { + "Wabbajack.Downloaders.ModDB/0.4.5": { "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.4.4", - "Wabbajack.Downloaders.Interfaces": "0.4.4", - "Wabbajack.Networking.Http": "0.4.4", - "Wabbajack.Networking.Http.Interfaces": "0.4.4" + "Wabbajack.Common": "0.4.5", + "Wabbajack.Downloaders.Interfaces": "0.4.5", + "Wabbajack.Networking.Http": "0.4.5", + "Wabbajack.Networking.Http.Interfaces": "0.4.5" }, "runtime": { "Wabbajack.Downloaders.ModDB.dll": {} } }, - "Wabbajack.Downloaders.Nexus/0.4.4": { + "Wabbajack.Downloaders.Nexus/0.4.5": { "dependencies": { "Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", - "Wabbajack.DTOs": "0.4.4", - "Wabbajack.Downloaders.Interfaces": "0.4.4", - "Wabbajack.Hashing.xxHash64": "0.4.4", - "Wabbajack.Networking.Http": "0.4.4", - "Wabbajack.Networking.Http.Interfaces": "0.4.4", - "Wabbajack.Networking.NexusApi": "0.4.4", - "Wabbajack.Paths": "0.4.4" + "Wabbajack.DTOs": "0.4.5", + "Wabbajack.Downloaders.Interfaces": "0.4.5", + "Wabbajack.Hashing.xxHash64": "0.4.5", + "Wabbajack.Networking.Http": "0.4.5", + "Wabbajack.Networking.Http.Interfaces": "0.4.5", + "Wabbajack.Networking.NexusApi": "0.4.5", + "Wabbajack.Paths": "0.4.5" }, "runtime": { "Wabbajack.Downloaders.Nexus.dll": {} } }, - "Wabbajack.Downloaders.VerificationCache/0.4.4": { + "Wabbajack.Downloaders.VerificationCache/0.4.5": { "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.4.4", - "Wabbajack.Paths.IO": "0.4.4" + "Wabbajack.DTOs": "0.4.5", + "Wabbajack.Paths.IO": "0.4.5" }, "runtime": { "Wabbajack.Downloaders.VerificationCache.dll": {} } }, - "Wabbajack.Downloaders.WabbajackCDN/0.4.4": { + "Wabbajack.Downloaders.WabbajackCDN/0.4.5": { "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.4.4", - "Wabbajack.Downloaders.Interfaces": "0.4.4", - "Wabbajack.Networking.Http": "0.4.4", - "Wabbajack.RateLimiter": "0.4.4" + "Wabbajack.Common": "0.4.5", + "Wabbajack.Downloaders.Interfaces": "0.4.5", + "Wabbajack.Networking.Http": "0.4.5", + "Wabbajack.RateLimiter": "0.4.5" }, "runtime": { "Wabbajack.Downloaders.WabbajackCDN.dll": {} } }, - "Wabbajack.DTOs/0.4.4": { + "Wabbajack.DTOs/0.4.5": { "dependencies": { "Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", - "Wabbajack.Hashing.xxHash64": "0.4.4", - "Wabbajack.Paths": "0.4.4" + "Wabbajack.Hashing.xxHash64": "0.4.5", + "Wabbajack.Paths": "0.4.5" }, "runtime": { "Wabbajack.DTOs.dll": {} } }, - "Wabbajack.FileExtractor/0.4.4": { + "Wabbajack.FileExtractor/0.4.5": { "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.4.4", - "Wabbajack.Compression.BSA": "0.4.4", - "Wabbajack.Hashing.PHash": "0.4.4", - "Wabbajack.Paths": "0.4.4" + "Wabbajack.Common": "0.4.5", + "Wabbajack.Compression.BSA": "0.4.5", + "Wabbajack.Hashing.PHash": "0.4.5", + "Wabbajack.Paths": "0.4.5" }, "runtime": { "Wabbajack.FileExtractor.dll": {} } }, - "Wabbajack.Hashing.PHash/0.4.4": { + "Wabbajack.Hashing.PHash/0.4.5": { "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.4.4", - "Wabbajack.DTOs": "0.4.4", - "Wabbajack.Paths": "0.4.4", - "Wabbajack.Paths.IO": "0.4.4" + "Wabbajack.Common": "0.4.5", + "Wabbajack.DTOs": "0.4.5", + "Wabbajack.Paths": "0.4.5", + "Wabbajack.Paths.IO": "0.4.5" }, "runtime": { "Wabbajack.Hashing.PHash.dll": {} } }, - "Wabbajack.Hashing.xxHash64/0.4.4": { + "Wabbajack.Hashing.xxHash64/0.4.5": { "dependencies": { - "Wabbajack.Paths": "0.4.4", - "Wabbajack.RateLimiter": "0.4.4" + "Wabbajack.Paths": "0.4.5", + "Wabbajack.RateLimiter": "0.4.5" }, "runtime": { "Wabbajack.Hashing.xxHash64.dll": {} } }, - "Wabbajack.Installer/0.4.4": { + "Wabbajack.Installer/0.4.5": { "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.4.4", - "Wabbajack.Downloaders.Dispatcher": "0.4.4", - "Wabbajack.Downloaders.GameFile": "0.4.4", - "Wabbajack.FileExtractor": "0.4.4", - "Wabbajack.Networking.WabbajackClientApi": "0.4.4", - "Wabbajack.Paths": "0.4.4", - "Wabbajack.Paths.IO": "0.4.4", - "Wabbajack.VFS": "0.4.4", + "Wabbajack.DTOs": "0.4.5", + "Wabbajack.Downloaders.Dispatcher": "0.4.5", + "Wabbajack.Downloaders.GameFile": "0.4.5", + "Wabbajack.FileExtractor": "0.4.5", + "Wabbajack.Networking.WabbajackClientApi": "0.4.5", + "Wabbajack.Paths": "0.4.5", + "Wabbajack.Paths.IO": "0.4.5", + "Wabbajack.VFS": "0.4.5", "ini-parser-netstandard": "2.5.2" }, "runtime": { "Wabbajack.Installer.dll": {} } }, - "Wabbajack.IO.Async/0.4.4": { + "Wabbajack.IO.Async/0.4.5": { "runtime": { "Wabbajack.IO.Async.dll": {} } }, - "Wabbajack.Networking.BethesdaNet/0.4.4": { + "Wabbajack.Networking.BethesdaNet/0.4.5": { "dependencies": { "Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", - "Wabbajack.DTOs": "0.4.4", - "Wabbajack.Networking.Http": "0.4.4", - "Wabbajack.Networking.Http.Interfaces": "0.4.4" + "Wabbajack.DTOs": "0.4.5", + "Wabbajack.Networking.Http": "0.4.5", + "Wabbajack.Networking.Http.Interfaces": "0.4.5" }, "runtime": { "Wabbajack.Networking.BethesdaNet.dll": {} } }, - "Wabbajack.Networking.Discord/0.4.4": { + "Wabbajack.Networking.Discord/0.4.5": { "dependencies": { "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.Logging.Abstractions": "9.0.1", - "Wabbajack.Networking.Http.Interfaces": "0.4.4" + "Wabbajack.Networking.Http.Interfaces": "0.4.5" }, "runtime": { "Wabbajack.Networking.Discord.dll": {} } }, - "Wabbajack.Networking.GitHub/0.4.4": { + "Wabbajack.Networking.GitHub/0.4.5": { "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.4.4", - "Wabbajack.Networking.Http.Interfaces": "0.4.4" + "Wabbajack.DTOs": "0.4.5", + "Wabbajack.Networking.Http.Interfaces": "0.4.5" }, "runtime": { "Wabbajack.Networking.GitHub.dll": {} } }, - "Wabbajack.Networking.Http/0.4.4": { + "Wabbajack.Networking.Http/0.4.5": { "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.4.4", - "Wabbajack.Downloaders.Interfaces": "0.4.4", - "Wabbajack.Hashing.xxHash64": "0.4.4", - "Wabbajack.Networking.Http.Interfaces": "0.4.4", - "Wabbajack.Paths": "0.4.4", - "Wabbajack.Paths.IO": "0.4.4" + "Wabbajack.Configuration": "0.4.5", + "Wabbajack.Downloaders.Interfaces": "0.4.5", + "Wabbajack.Hashing.xxHash64": "0.4.5", + "Wabbajack.Networking.Http.Interfaces": "0.4.5", + "Wabbajack.Paths": "0.4.5", + "Wabbajack.Paths.IO": "0.4.5" }, "runtime": { "Wabbajack.Networking.Http.dll": {} } }, - "Wabbajack.Networking.Http.Interfaces/0.4.4": { + "Wabbajack.Networking.Http.Interfaces/0.4.5": { "dependencies": { - "Wabbajack.Hashing.xxHash64": "0.4.4" + "Wabbajack.Hashing.xxHash64": "0.4.5" }, "runtime": { "Wabbajack.Networking.Http.Interfaces.dll": {} } }, - "Wabbajack.Networking.NexusApi/0.4.4": { + "Wabbajack.Networking.NexusApi/0.4.5": { "dependencies": { "Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.Logging.Abstractions": "9.0.1", - "Wabbajack.DTOs": "0.4.4", - "Wabbajack.Networking.Http": "0.4.4", - "Wabbajack.Networking.Http.Interfaces": "0.4.4", - "Wabbajack.Networking.WabbajackClientApi": "0.4.4" + "Wabbajack.DTOs": "0.4.5", + "Wabbajack.Networking.Http": "0.4.5", + "Wabbajack.Networking.Http.Interfaces": "0.4.5", + "Wabbajack.Networking.WabbajackClientApi": "0.4.5" }, "runtime": { "Wabbajack.Networking.NexusApi.dll": {} } }, - "Wabbajack.Networking.WabbajackClientApi/0.4.4": { + "Wabbajack.Networking.WabbajackClientApi/0.4.5": { "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.4.4", - "Wabbajack.DTOs": "0.4.4", - "Wabbajack.Paths.IO": "0.4.4", - "Wabbajack.VFS.Interfaces": "0.4.4", + "Wabbajack.Common": "0.4.5", + "Wabbajack.DTOs": "0.4.5", + "Wabbajack.Paths.IO": "0.4.5", + "Wabbajack.VFS.Interfaces": "0.4.5", "YamlDotNet": "16.3.0" }, "runtime": { "Wabbajack.Networking.WabbajackClientApi.dll": {} } }, - "Wabbajack.Paths/0.4.4": { + "Wabbajack.Paths/0.4.5": { "runtime": { "Wabbajack.Paths.dll": {} } }, - "Wabbajack.Paths.IO/0.4.4": { + "Wabbajack.Paths.IO/0.4.5": { "dependencies": { - "Wabbajack.Paths": "0.4.4", + "Wabbajack.Paths": "0.4.5", "shortid": "4.0.0" }, "runtime": { "Wabbajack.Paths.IO.dll": {} } }, - "Wabbajack.RateLimiter/0.4.4": { + "Wabbajack.RateLimiter/0.4.5": { "runtime": { "Wabbajack.RateLimiter.dll": {} } }, - "Wabbajack.Server.Lib/0.4.4": { + "Wabbajack.Server.Lib/0.4.5": { "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.4.4", - "Wabbajack.Networking.Http.Interfaces": "0.4.4", - "Wabbajack.Services.OSIntegrated": "0.4.4" + "Wabbajack.Common": "0.4.5", + "Wabbajack.Networking.Http.Interfaces": "0.4.5", + "Wabbajack.Services.OSIntegrated": "0.4.5" }, "runtime": { "Wabbajack.Server.Lib.dll": {} } }, - "Wabbajack.Services.OSIntegrated/0.4.4": { + "Wabbajack.Services.OSIntegrated/0.4.5": { "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.4.4", - "Wabbajack.Downloaders.Dispatcher": "0.4.4", - "Wabbajack.Installer": "0.4.4", - "Wabbajack.Networking.BethesdaNet": "0.4.4", - "Wabbajack.Networking.Discord": "0.4.4", - "Wabbajack.VFS": "0.4.4" + "Wabbajack.Compiler": "0.4.5", + "Wabbajack.Downloaders.Dispatcher": "0.4.5", + "Wabbajack.Installer": "0.4.5", + "Wabbajack.Networking.BethesdaNet": "0.4.5", + "Wabbajack.Networking.Discord": "0.4.5", + "Wabbajack.VFS": "0.4.5" }, "runtime": { "Wabbajack.Services.OSIntegrated.dll": {} } }, - "Wabbajack.VFS/0.4.4": { + "Wabbajack.VFS/0.4.5": { "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.4.4", - "Wabbajack.FileExtractor": "0.4.4", - "Wabbajack.Hashing.PHash": "0.4.4", - "Wabbajack.Hashing.xxHash64": "0.4.4", - "Wabbajack.Paths": "0.4.4", - "Wabbajack.Paths.IO": "0.4.4", - "Wabbajack.VFS.Interfaces": "0.4.4" + "Wabbajack.Common": "0.4.5", + "Wabbajack.FileExtractor": "0.4.5", + "Wabbajack.Hashing.PHash": "0.4.5", + "Wabbajack.Hashing.xxHash64": "0.4.5", + "Wabbajack.Paths": "0.4.5", + "Wabbajack.Paths.IO": "0.4.5", + "Wabbajack.VFS.Interfaces": "0.4.5" }, "runtime": { "Wabbajack.VFS.dll": {} } }, - "Wabbajack.VFS.Interfaces/0.4.4": { + "Wabbajack.VFS.Interfaces/0.4.5": { "dependencies": { "Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", - "Wabbajack.DTOs": "0.4.4", - "Wabbajack.Hashing.xxHash64": "0.4.4", - "Wabbajack.Paths": "0.4.4" + "Wabbajack.DTOs": "0.4.5", + "Wabbajack.Hashing.xxHash64": "0.4.5", + "Wabbajack.Paths": "0.4.5" }, "runtime": { "Wabbajack.VFS.Interfaces.dll": {} @@ -2332,7 +2332,7 @@ } }, "libraries": { - "jackify-engine/0.4.4": { + "jackify-engine/0.4.5": { "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.4.4": { + "Wabbajack.CLI.Builder/0.4.5": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Common/0.4.4": { + "Wabbajack.Common/0.4.5": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Compiler/0.4.4": { + "Wabbajack.Compiler/0.4.5": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Compression.BSA/0.4.4": { + "Wabbajack.Compression.BSA/0.4.5": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Compression.Zip/0.4.4": { + "Wabbajack.Compression.Zip/0.4.5": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Configuration/0.4.4": { + "Wabbajack.Configuration/0.4.5": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Downloaders.Bethesda/0.4.4": { + "Wabbajack.Downloaders.Bethesda/0.4.5": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Downloaders.Dispatcher/0.4.4": { + "Wabbajack.Downloaders.Dispatcher/0.4.5": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Downloaders.GameFile/0.4.4": { + "Wabbajack.Downloaders.GameFile/0.4.5": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Downloaders.GoogleDrive/0.4.4": { + "Wabbajack.Downloaders.GoogleDrive/0.4.5": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Downloaders.Http/0.4.4": { + "Wabbajack.Downloaders.Http/0.4.5": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Downloaders.Interfaces/0.4.4": { + "Wabbajack.Downloaders.Interfaces/0.4.5": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Downloaders.IPS4OAuth2Downloader/0.4.4": { + "Wabbajack.Downloaders.IPS4OAuth2Downloader/0.4.5": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Downloaders.Manual/0.4.4": { + "Wabbajack.Downloaders.Manual/0.4.5": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Downloaders.MediaFire/0.4.4": { + "Wabbajack.Downloaders.MediaFire/0.4.5": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Downloaders.Mega/0.4.4": { + "Wabbajack.Downloaders.Mega/0.4.5": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Downloaders.ModDB/0.4.4": { + "Wabbajack.Downloaders.ModDB/0.4.5": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Downloaders.Nexus/0.4.4": { + "Wabbajack.Downloaders.Nexus/0.4.5": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Downloaders.VerificationCache/0.4.4": { + "Wabbajack.Downloaders.VerificationCache/0.4.5": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Downloaders.WabbajackCDN/0.4.4": { + "Wabbajack.Downloaders.WabbajackCDN/0.4.5": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.DTOs/0.4.4": { + "Wabbajack.DTOs/0.4.5": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.FileExtractor/0.4.4": { + "Wabbajack.FileExtractor/0.4.5": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Hashing.PHash/0.4.4": { + "Wabbajack.Hashing.PHash/0.4.5": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Hashing.xxHash64/0.4.4": { + "Wabbajack.Hashing.xxHash64/0.4.5": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Installer/0.4.4": { + "Wabbajack.Installer/0.4.5": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.IO.Async/0.4.4": { + "Wabbajack.IO.Async/0.4.5": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Networking.BethesdaNet/0.4.4": { + "Wabbajack.Networking.BethesdaNet/0.4.5": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Networking.Discord/0.4.4": { + "Wabbajack.Networking.Discord/0.4.5": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Networking.GitHub/0.4.4": { + "Wabbajack.Networking.GitHub/0.4.5": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Networking.Http/0.4.4": { + "Wabbajack.Networking.Http/0.4.5": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Networking.Http.Interfaces/0.4.4": { + "Wabbajack.Networking.Http.Interfaces/0.4.5": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Networking.NexusApi/0.4.4": { + "Wabbajack.Networking.NexusApi/0.4.5": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Networking.WabbajackClientApi/0.4.4": { + "Wabbajack.Networking.WabbajackClientApi/0.4.5": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Paths/0.4.4": { + "Wabbajack.Paths/0.4.5": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Paths.IO/0.4.4": { + "Wabbajack.Paths.IO/0.4.5": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.RateLimiter/0.4.4": { + "Wabbajack.RateLimiter/0.4.5": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Server.Lib/0.4.4": { + "Wabbajack.Server.Lib/0.4.5": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.Services.OSIntegrated/0.4.4": { + "Wabbajack.Services.OSIntegrated/0.4.5": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.VFS/0.4.4": { + "Wabbajack.VFS/0.4.5": { "type": "project", "serviceable": false, "sha512": "" }, - "Wabbajack.VFS.Interfaces/0.4.4": { + "Wabbajack.VFS.Interfaces/0.4.5": { "type": "project", "serviceable": false, "sha512": "" diff --git a/jackify/engine/jackify-engine.dll b/jackify/engine/jackify-engine.dll index 7a39610..525e967 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 77089c8..0b5f0f2 100644 --- a/jackify/frontends/gui/main.py +++ b/jackify/frontends/gui/main.py @@ -1891,8 +1891,9 @@ def main(): # Initialize file logging on root logger so all modules inherit it from jackify.shared.logging import LoggingHandler logging_handler = LoggingHandler() - # Rotate log file before setting up new logger - logging_handler.rotate_log_for_logger('jackify_gui', 'jackify-gui.log') + # Only rotate log file when debug mode is enabled + if debug_mode: + logging_handler.rotate_log_for_logger('jackify_gui', 'jackify-gui.log') root_logger = logging_handler.setup_logger('', 'jackify-gui.log', is_general=True) # Empty name = root logger if debug_mode: diff --git a/jackify/frontends/gui/screens/configure_existing_modlist.py b/jackify/frontends/gui/screens/configure_existing_modlist.py index a7bfcf5..b8b487f 100644 --- a/jackify/frontends/gui/screens/configure_existing_modlist.py +++ b/jackify/frontends/gui/screens/configure_existing_modlist.py @@ -639,6 +639,8 @@ class ConfigureExistingModlistScreen(QWidget): # Start time tracking self._workflow_start_time = time.time() + from jackify import __version__ as jackify_version + self._safe_append_text(f"Jackify v{jackify_version}") self._safe_append_text("[Jackify] Starting post-install configuration...") # Create configuration thread using backend service diff --git a/jackify/frontends/gui/screens/configure_new_modlist.py b/jackify/frontends/gui/screens/configure_new_modlist.py index 44c2bb4..4c12602 100644 --- a/jackify/frontends/gui/screens/configure_new_modlist.py +++ b/jackify/frontends/gui/screens/configure_new_modlist.py @@ -919,8 +919,10 @@ class ConfigureNewModlistScreen(QWidget): self._enable_controls_after_operation() if success: self._safe_append_text("Steam restarted successfully.") - + # Start configuration immediately - the CLI will handle any manual steps + from jackify import __version__ as jackify_version + self._safe_append_text(f"Jackify v{jackify_version}") self._safe_append_text("Starting modlist configuration...") self.configure_modlist() else: @@ -950,6 +952,8 @@ class ConfigureNewModlistScreen(QWidget): def _start_automated_prefix_workflow(self, modlist_name, install_dir, mo2_exe_path, resolution): """Start the automated prefix workflow using AutomatedPrefixService in a background thread""" + from jackify import __version__ as jackify_version + self._safe_append_text(f"Jackify v{jackify_version}") self._safe_append_text(f"Initializing automated Steam setup for '{modlist_name}'...") self._safe_append_text("Starting automated Steam shortcut creation and configuration...") diff --git a/jackify/frontends/gui/screens/install_modlist.py b/jackify/frontends/gui/screens/install_modlist.py index 4de59d5..75a7060 100644 --- a/jackify/frontends/gui/screens/install_modlist.py +++ b/jackify/frontends/gui/screens/install_modlist.py @@ -1869,7 +1869,7 @@ class InstallModlistScreen(QWidget): downloads_dir = self.downloads_dir_edit.text().strip() # Get authentication token (OAuth or API key) with automatic refresh - api_key = self.auth_service.ensure_valid_auth() + api_key, oauth_info = self.auth_service.get_auth_for_engine() if not api_key: self._abort_with_message( "warning", @@ -2097,7 +2097,7 @@ class InstallModlistScreen(QWidget): return debug_print(f'DEBUG: Calling run_modlist_installer with modlist={modlist}, install_dir={install_dir}, downloads_dir={downloads_dir}, install_mode={install_mode}') - self.run_modlist_installer(modlist, install_dir, downloads_dir, api_key, install_mode) + self.run_modlist_installer(modlist, install_dir, downloads_dir, api_key, install_mode, oauth_info) except Exception as e: debug_print(f"DEBUG: Exception in validate_and_start_install: {e}") import traceback @@ -2108,7 +2108,7 @@ class InstallModlistScreen(QWidget): self.cancel_install_btn.setVisible(False) debug_print(f"DEBUG: Controls re-enabled in exception handler") - def run_modlist_installer(self, modlist, install_dir, downloads_dir, api_key, install_mode='online'): + def run_modlist_installer(self, modlist, install_dir, downloads_dir, api_key, install_mode='online', oauth_info=None): debug_print('DEBUG: run_modlist_installer called - USING THREADED BACKEND WRAPPER') # Rotate log file at start of each workflow run (keep 5 backups) @@ -2119,6 +2119,8 @@ class InstallModlistScreen(QWidget): # Clear console for fresh installation output self.console.clear() + from jackify import __version__ as jackify_version + self._safe_append_text(f"Jackify v{jackify_version}") self._safe_append_text("Starting modlist installation with custom progress handling...") # Update UI state for installation @@ -2136,7 +2138,7 @@ class InstallModlistScreen(QWidget): installation_finished = Signal(bool, str) premium_required_detected = Signal(str) - def __init__(self, modlist, install_dir, downloads_dir, api_key, modlist_name, install_mode='online', progress_state_manager=None): + def __init__(self, modlist, install_dir, downloads_dir, api_key, modlist_name, install_mode='online', progress_state_manager=None, auth_service=None, oauth_info=None): super().__init__() self.modlist = modlist self.install_dir = install_dir @@ -2148,6 +2150,8 @@ class InstallModlistScreen(QWidget): self.process_manager = None # R&D: Progress state manager for parsing self.progress_state_manager = progress_state_manager + self.auth_service = auth_service + self.oauth_info = oauth_info self._premium_signal_sent = False # Rolling buffer for Premium detection diagnostics self._engine_output_buffer = [] @@ -2196,7 +2200,10 @@ class InstallModlistScreen(QWidget): # Use clean subprocess environment to prevent AppImage variable inheritance from jackify.backend.handlers.subprocess_utils import get_clean_subprocess_env - env = get_clean_subprocess_env({'NEXUS_API_KEY': self.api_key}) + env_vars = {'NEXUS_API_KEY': self.api_key} + if self.oauth_info: + env_vars['NEXUS_OAUTH_INFO'] = self.oauth_info + env = get_clean_subprocess_env(env_vars) self.process_manager = ProcessManager(cmd, env=env, text=False) ansi_escape = re.compile(rb'\x1b\[[0-9;?]*[ -/]*[@-~]') buffer = b'' @@ -2451,7 +2458,9 @@ class InstallModlistScreen(QWidget): # After the InstallationThread class definition, add: self.install_thread = InstallationThread( modlist, install_dir, downloads_dir, api_key, self.modlist_name_edit.text().strip(), install_mode, - progress_state_manager=self.progress_state_manager # R&D: Pass progress state manager + progress_state_manager=self.progress_state_manager, # R&D: Pass progress state manager + auth_service=self.auth_service, # Fix Issue #127: Pass auth_service for Premium detection diagnostics + oauth_info=oauth_info # Pass OAuth state for auto-refresh ) self.install_thread.output_received.connect(self.on_installation_output) self.install_thread.progress_received.connect(self.on_installation_progress)