Sync from development - prepare for v0.1.7.1

This commit is contained in:
Omni
2025-11-11 20:04:32 +00:00
parent 9680814bbb
commit fe14e4ecfb
60 changed files with 622 additions and 344 deletions

View File

@@ -1,11 +1,33 @@
# Jackify Changelog # 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 ## v0.1.7 - TTW Automation & Bug Fixes
**Release Date:** November 1, 2025 **Release Date:** November 1, 2025
### Major Features ### Major Features
- **TTW (Tale of Two Wastelands) Installation and Automation** - **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 - Automated workflow for TTW installation and integration into FNV modlists, where possible
- Automatic detection of TTW-compatible modlists - Automatic detection of TTW-compatible modlists
- User prompt after modlist installation with option to install TTW - User prompt after modlist installation with option to install TTW

View File

@@ -77,6 +77,9 @@ Currently, there are two main functions that Jackify will perform at this stage
- **FUSE** (required for AppImage execution) - **FUSE** (required for AppImage execution)
- Pre-installed on most Linux distributions - Pre-installed on most Linux distributions
- If AppImage fails to run, install FUSE using your distribution's package manager - 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 ### Installation

View File

@@ -5,4 +5,4 @@ This package provides both CLI and GUI interfaces for managing
Wabbajack modlists natively on Linux systems. Wabbajack modlists natively on Linux systems.
""" """
__version__ = "0.1.7" __version__ = "0.1.7.1"

View File

@@ -100,7 +100,8 @@ class ConfigHandler:
libraryfolders_vdf_paths = [ libraryfolders_vdf_paths = [
os.path.expanduser("~/.steam/steam/config/libraryfolders.vdf"), os.path.expanduser("~/.steam/steam/config/libraryfolders.vdf"),
os.path.expanduser("~/.local/share/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: for vdf_path in libraryfolders_vdf_paths:

View File

@@ -784,7 +784,8 @@ class FileSystemHandler:
possible_vdf_paths = [ possible_vdf_paths = [
Path.home() / ".steam/steam/config/libraryfolders.vdf", Path.home() / ".steam/steam/config/libraryfolders.vdf",
Path.home() / ".local/share/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 libraryfolders_vdf_path: Optional[Path] = None

View File

@@ -650,6 +650,10 @@ class ModlistMenuHandler:
print("Modlist Install and Configuration complete!") print("Modlist Install and Configuration complete!")
print(f"• You should now be able to Launch '{context.get('name')}' through Steam") print(f"• You should now be able to Launch '{context.get('name')}' through Steam")
print("• Congratulations and enjoy the game!") 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") print("Detailed log available at: ~/Jackify/logs/Configure_New_Modlist_workflow.log")
# Only wait for input in CLI mode, not GUI mode # Only wait for input in CLI mode, not GUI mode
if not gui_mode: if not gui_mode:

View File

@@ -329,22 +329,18 @@ class ModlistHandler:
# On non-Steam Deck systems, /media mounts should use Z: drive, not D: drive # 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") is_on_sdcard_path = str(self.modlist_dir).startswith("/run/media") or str(self.modlist_dir).startswith("/media")
# DEBUG: Log SD card detection logic # Log SD card detection for debugging
self.logger.debug(f"[SD_CARD_DEBUG] SD card detection for instance id={id(self)}:") self.logger.debug(f"SD card detection: modlist_dir={self.modlist_dir}, is_sdcard_path={is_on_sdcard_path}, steamdeck={self.steamdeck}")
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}")
if is_on_sdcard_path and self.steamdeck: if is_on_sdcard_path and self.steamdeck:
self.modlist_sdcard = True self.modlist_sdcard = True
self.logger.info("Modlist appears to be on an SD card (Steam Deck).") 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: else:
self.modlist_sdcard = False 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: 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.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 # Find and set compatdata path now that we have appid
# Ensure PathHandler is available (should be initialized in __init__) # 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) success = self.winetricks_handler.install_wine_components(wineprefix, self.game_var_full, specific_components=components)
if success: if success:
self.logger.info("Wine component installation completed successfully") 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: else:
self.logger.error("Wine component installation failed") self.logger.error("Wine component installation failed")
print("Error: Failed to install necessary Wine components.") print("Error: Failed to install necessary Wine components.")
@@ -752,6 +750,19 @@ class ModlistHandler:
self.logger.error("=" * 80) self.logger.error("=" * 80)
# Continue but user should be aware of potential issues # 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 # Step 5: Ensure permissions of Modlist directory
if status_callback: if status_callback:
status_callback(f"{self._get_progress_timestamp()} Setting ownership and permissions for modlist directory") status_callback(f"{self._get_progress_timestamp()} Setting ownership and permissions for modlist directory")

View File

@@ -390,7 +390,7 @@ class PathHandler:
libraryfolders_vdf_paths = [ libraryfolders_vdf_paths = [
os.path.expanduser("~/.steam/steam/config/libraryfolders.vdf"), os.path.expanduser("~/.steam/steam/config/libraryfolders.vdf"),
os.path.expanduser("~/.local/share/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) # Simple backup mechanism (optional but good practice)
@@ -622,7 +622,9 @@ class PathHandler:
m = re.search(r'"path"\s*"([^"]+)"', line) m = re.search(r'"path"\s*"([^"]+)"', line)
if m: if m:
lib_path = Path(m.group(1)) 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: except Exception as e:
logger.error(f"[DEBUG] Failed to parse {vdf_path}: {e}") logger.error(f"[DEBUG] Failed to parse {vdf_path}: {e}")
logger.info(f"[DEBUG] All detected Steam libraries: {library_paths}") logger.info(f"[DEBUG] All detected Steam libraries: {library_paths}")
@@ -871,10 +873,9 @@ class PathHandler:
else: else:
found_stock = None found_stock = None
for folder in STOCK_GAME_FOLDERS: for folder in STOCK_GAME_FOLDERS:
folder_pattern = f"/{folder.replace(' ', '')}".lower() folder_pattern = f"/{folder}"
value_part_lower = value_part.replace(' ', '').lower() if folder_pattern in value_part:
if folder_pattern in value_part_lower: idx = value_part.index(folder_pattern)
idx = value_part_lower.index(folder_pattern)
rel_path = value_part[idx:].lstrip('/') rel_path = value_part[idx:].lstrip('/')
found_stock = folder found_stock = folder
break break

View File

@@ -120,20 +120,18 @@ class ProtontricksHandler:
result = subprocess.run( result = subprocess.run(
["flatpak", "list"], ["flatpak", "list"],
capture_output=True, stdout=subprocess.PIPE,
stderr=subprocess.DEVNULL, # Suppress stderr to avoid error messages when flatpak not installed
text=True, text=True,
check=True,
env=env # Use comprehensively cleaned environment 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") logger.info("Flatpak Protontricks is installed")
self.which_protontricks = 'flatpak' self.which_protontricks = 'flatpak'
flatpak_installed = True flatpak_installed = True
return True return True
except FileNotFoundError: except FileNotFoundError:
logger.warning("'flatpak' command not found. Cannot check for Flatpak Protontricks.") 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: except Exception as e:
logger.error(f"Unexpected error checking flatpak: {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) 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 ''}") self.logger.debug(f"Protontricks output: {result.stdout if result else ''}")
if result and result.returncode == 0: if result and result.returncode == 0:
self.logger.info("Wine Component installation command completed successfully.") 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 return True
else:
self.logger.error(f"Component verification failed (Attempt {attempt}/{max_attempts})")
# Continue to retry
else: 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"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 ''}") self.logger.error(f"Stdout: {result.stdout.strip() if result else ''}")
@@ -718,6 +723,65 @@ class ProtontricksHandler:
self.logger.error(f"Failed to install Wine components after {max_attempts} attempts.") self.logger.error(f"Failed to install Wine components after {max_attempts} attempts.")
return False 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): def _cleanup_wine_processes(self):
""" """
Internal method to clean up wine processes during component installation Internal method to clean up wine processes during component installation

View File

@@ -200,40 +200,55 @@ class WineUtils:
@staticmethod @staticmethod
def _get_sd_card_mounts(): def _get_sd_card_mounts():
""" """
Dynamically detect all current SD card mount points Detect SD card mount points using df.
Returns list of mount point paths Returns list of actual mount paths from /run/media (e.g., /run/media/deck/MicroSD).
""" """
try:
import subprocess import subprocess
import re
result = subprocess.run(['df', '-h'], capture_output=True, text=True, timeout=5) result = subprocess.run(['df', '-h'], capture_output=True, text=True, timeout=5)
sd_mounts = [] sd_mounts = []
for line in result.stdout.split('\n'): for line in result.stdout.split('\n'):
# Look for common SD card mount patterns if '/run/media' in line:
if '/run/media' in line or ('/mnt' in line and 'sdcard' in line.lower()):
parts = line.split() parts = line.split()
if len(parts) >= 6: # df output has 6+ columns if len(parts) >= 6:
mount_point = parts[-1] # Last column is mount point mount_point = parts[-1] # Last column is the mount point
if mount_point.startswith(('/run/media', '/mnt')): if mount_point.startswith('/run/media/'):
sd_mounts.append(mount_point) 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 return sd_mounts
except Exception:
# Fallback to common patterns if df fails
return ['/run/media/mmcblk0p1', '/run/media/deck']
@staticmethod @staticmethod
def _strip_sdcard_path(path): def _strip_sdcard_path(path):
""" """
Strip any detected SD card mount prefix from paths Strip SD card mount prefix from path.
Handles both /run/media/mmcblk0p1 and /run/media/deck/UUID patterns 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: # Pattern 1: /run/media/deck/UUID/... strip everything up to and including UUID
if path.startswith(mount): # This matches the bash: "${path#*/run/media/deck/*/*}"
# Strip the mount prefix and ensure proper leading slash deck_pattern = r'^/run/media/deck/[^/]+(/.*)?$'
relative_path = path[len(mount):].lstrip('/') match = re.match(deck_pattern, path)
return "/" + relative_path if relative_path else "/" 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 return path
@staticmethod @staticmethod

View File

@@ -349,10 +349,17 @@ class WinetricksHandler:
self.logger.debug(f"Winetricks output: {result.stdout}") self.logger.debug(f"Winetricks output: {result.stdout}")
if result.returncode == 0: if result.returncode == 0:
self.logger.info("Wine Component installation command completed successfully.") 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) # Set Windows 10 mode after component installation (matches legacy script timing)
self._set_windows_10_mode(wineprefix, env.get('WINE', '')) self._set_windows_10_mode(wineprefix, env.get('WINE', ''))
return True return True
else:
self.logger.error(f"Component verification failed (Attempt {attempt}/{max_attempts})")
# Continue to retry
else: else:
# Special handling for dotnet40 verification issue (mimics protontricks behavior) # Special handling for dotnet40 verification issue (mimics protontricks behavior)
if "dotnet40" in components_to_install and "ngen.exe not found" in result.stderr: if "dotnet40" in components_to_install and "ngen.exe not found" in result.stderr:
@@ -430,11 +437,36 @@ class WinetricksHandler:
if winetricks_failed: if winetricks_failed:
self.logger.error(f"Winetricks failed after {max_attempts} attempts.") 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: try:
protontricks_check = subprocess.run(['which', 'protontricks'], # Check if curl is available
capture_output=True, text=True, timeout=5) curl_check = subprocess.run(['which', 'curl'], capture_output=True, timeout=5)
if protontricks_check.returncode == 0: 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("=" * 80)
self.logger.warning("AUTOMATIC FALLBACK: Winetricks failed, attempting protontricks fallback...") self.logger.warning("AUTOMATIC FALLBACK: Winetricks failed, attempting protontricks fallback...")
self.logger.warning(f"Last winetricks error: {last_error_details}") self.logger.warning(f"Last winetricks error: {last_error_details}")
@@ -721,13 +753,6 @@ class WinetricksHandler:
if success: if success:
self.logger.info(f"Legacy .NET components {legacy_components} installed successfully with protontricks") 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 return True
else: else:
self.logger.error(f"Legacy .NET components {legacy_components} installation failed with protontricks") self.logger.error(f"Legacy .NET components {legacy_components} installation failed with protontricks")
@@ -844,11 +869,18 @@ class WinetricksHandler:
) )
if result.returncode == 0: if result.returncode == 0:
self.logger.info(f"Winetricks components installed successfully: {components}") 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) # Set Windows 10 mode after component installation (matches legacy script timing)
wine_binary = env.get('WINE', '') wine_binary = env.get('WINE', '')
self._set_windows_10_mode(env.get('WINEPREFIX', ''), wine_binary) self._set_windows_10_mode(env.get('WINEPREFIX', ''), wine_binary)
return True return True
else:
self.logger.error(f"Component verification failed (attempt {attempt})")
# Continue to retry
else: else:
self.logger.error(f"Winetricks failed (attempt {attempt}): {result.stderr.strip()}") 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}") self.logger.error(f"Error getting wine binary for prefix: {e}")
return "" 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): def _cleanup_wine_processes(self):
""" """
Internal method to clean up wine processes during component installation Internal method to clean up wine processes during component installation

View File

@@ -2905,9 +2905,20 @@ echo Prefix creation complete.
import os import os
from pathlib import Path from pathlib import Path
# Get Steam libraries from libraryfolders.vdf # Get Steam libraries from libraryfolders.vdf - check multiple possible locations
steam_config_path = Path.home() / ".steam/steam/config/libraryfolders.vdf" possible_config_paths = [
if not steam_config_path.exists(): 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 return None
steam_libraries = [] steam_libraries = []

View File

@@ -135,6 +135,9 @@ class NativeSteamOperationsService:
steam_locations = [ steam_locations = [
Path.home() / ".steam/steam", Path.home() / ".steam/steam",
Path.home() / ".local/share/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/.steam/steam",
Path.home() / ".var/app/com.valvesoftware.Steam/home/.local/share/Steam" Path.home() / ".var/app/com.valvesoftware.Steam/home/.local/share/Steam"
] ]
@@ -161,6 +164,9 @@ class NativeSteamOperationsService:
standard_locations = [ standard_locations = [
Path.home() / ".steam/steam/steamapps/compatdata", Path.home() / ".steam/steam/steamapps/compatdata",
Path.home() / ".local/share/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/.steam/steam/steamapps/compatdata",
Path.home() / ".var/app/com.valvesoftware.Steam/home/.local/share/Steam/steamapps/compatdata" Path.home() / ".var/app/com.valvesoftware.Steam/home/.local/share/Steam/steamapps/compatdata"
] ]

View File

@@ -128,19 +128,17 @@ class ProtontricksDetectionService:
env = handler._get_clean_subprocess_env() env = handler._get_clean_subprocess_env()
result = subprocess.run( result = subprocess.run(
["flatpak", "list"], ["flatpak", "list"],
capture_output=True, stdout=subprocess.PIPE,
stderr=subprocess.DEVNULL, # Suppress stderr to avoid error messages
text=True, text=True,
check=True,
env=env 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") logger.info("Flatpak Protontricks is installed")
handler.which_protontricks = 'flatpak' handler.which_protontricks = 'flatpak'
return True return True
except FileNotFoundError: except FileNotFoundError:
logger.warning("'flatpak' command not found. Cannot check for Flatpak Protontricks.") 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: except Exception as e:
logger.error(f"Unexpected error checking flatpak: {e}") logger.error(f"Unexpected error checking flatpak: {e}")

View File

@@ -5,6 +5,7 @@ import signal
import psutil import psutil
import logging import logging
import sys import sys
import shutil
from typing import Callable, Optional from typing import Callable, Optional
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -86,6 +87,25 @@ def is_steam_deck() -> bool:
logger.debug(f"Error detecting Steam Deck: {e}") logger.debug(f"Error detecting Steam Deck: {e}")
return False 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: def get_steam_processes() -> list:
"""Return a list of psutil.Process objects for running Steam processes.""" """Return a list of psutil.Process objects for running Steam processes."""
steam_procs = [] steam_procs = []
@@ -122,16 +142,37 @@ def start_steam() -> bool:
"""Attempt to start Steam using the exact methods from existing working logic.""" """Attempt to start Steam using the exact methods from existing working logic."""
env = _get_clean_subprocess_env() env = _get_clean_subprocess_env()
try: try:
# Try systemd user service (Steam Deck) # Try systemd user service (Steam Deck) - HIGHEST PRIORITY
if is_steam_deck(): if is_steam_deck():
subprocess.Popen(["systemctl", "--user", "restart", "app-steam@autostart.service"], env=env) subprocess.Popen(["systemctl", "--user", "restart", "app-steam@autostart.service"], env=env)
return True 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) # 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 = [ start_methods = [
{"name": "Popen", "cmd": ["steam", "-silent"], "kwargs": {"stdout": subprocess.DEVNULL, "stderr": subprocess.DEVNULL, "stdin": subprocess.DEVNULL, "start_new_session": True, "env": env}}, {"name": "Popen", "cmd": ["steam", "-silent"], "kwargs": {"env": env}},
{"name": "setsid", "cmd": ["setsid", "steam", "-silent"], "kwargs": {"stdout": subprocess.DEVNULL, "stderr": subprocess.DEVNULL, "stdin": subprocess.DEVNULL, "env": env}}, {"name": "setsid", "cmd": ["setsid", "steam", "-silent"], "kwargs": {"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": "nohup", "cmd": ["nohup", "steam", "-silent"], "kwargs": {"preexec_fn": os.setpgrp, "env": env}}
] ]
for method in start_methods: for method in start_methods:
@@ -175,7 +216,7 @@ def robust_steam_restart(progress_callback: Optional[Callable[[str], None]] = No
report("Shutting down Steam...") 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(): if is_steam_deck():
try: try:
report("Steam Deck detected - using systemctl shutdown...") report("Steam Deck detected - using systemctl shutdown...")
@@ -184,6 +225,15 @@ def robust_steam_restart(progress_callback: Optional[Callable[[str], None]] = No
time.sleep(2) time.sleep(2)
except Exception as e: except Exception as e:
logger.debug(f"systemctl stop failed on Steam Deck: {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) # All systems: Use pkill approach (proven 15/16 test success rate)
try: try:

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -7,7 +7,7 @@
"targets": { "targets": {
".NETCoreApp,Version=v8.0": {}, ".NETCoreApp,Version=v8.0": {},
".NETCoreApp,Version=v8.0/linux-x64": { ".NETCoreApp,Version=v8.0/linux-x64": {
"jackify-engine/0.3.17": { "jackify-engine/0.3.18": {
"dependencies": { "dependencies": {
"Markdig": "0.40.0", "Markdig": "0.40.0",
"Microsoft.Extensions.Configuration.Json": "9.0.1", "Microsoft.Extensions.Configuration.Json": "9.0.1",
@@ -22,16 +22,16 @@
"SixLabors.ImageSharp": "3.1.6", "SixLabors.ImageSharp": "3.1.6",
"System.CommandLine": "2.0.0-beta4.22272.1", "System.CommandLine": "2.0.0-beta4.22272.1",
"System.CommandLine.NamingConventionBinder": "2.0.0-beta4.22272.1", "System.CommandLine.NamingConventionBinder": "2.0.0-beta4.22272.1",
"Wabbajack.CLI.Builder": "0.3.17", "Wabbajack.CLI.Builder": "0.3.18",
"Wabbajack.Downloaders.Bethesda": "0.3.17", "Wabbajack.Downloaders.Bethesda": "0.3.18",
"Wabbajack.Downloaders.Dispatcher": "0.3.17", "Wabbajack.Downloaders.Dispatcher": "0.3.18",
"Wabbajack.Hashing.xxHash64": "0.3.17", "Wabbajack.Hashing.xxHash64": "0.3.18",
"Wabbajack.Networking.Discord": "0.3.17", "Wabbajack.Networking.Discord": "0.3.18",
"Wabbajack.Networking.GitHub": "0.3.17", "Wabbajack.Networking.GitHub": "0.3.18",
"Wabbajack.Paths.IO": "0.3.17", "Wabbajack.Paths.IO": "0.3.18",
"Wabbajack.Server.Lib": "0.3.17", "Wabbajack.Server.Lib": "0.3.18",
"Wabbajack.Services.OSIntegrated": "0.3.17", "Wabbajack.Services.OSIntegrated": "0.3.18",
"Wabbajack.VFS": "0.3.17", "Wabbajack.VFS": "0.3.18",
"MegaApiClient": "1.0.0.0", "MegaApiClient": "1.0.0.0",
"runtimepack.Microsoft.NETCore.App.Runtime.linux-x64": "8.0.19" "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": { "dependencies": {
"Microsoft.Extensions.Configuration.Json": "9.0.1", "Microsoft.Extensions.Configuration.Json": "9.0.1",
"Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection": "9.0.1",
@@ -1791,109 +1791,109 @@
"Microsoft.Extensions.Logging.Abstractions": "9.0.1", "Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"System.CommandLine": "2.0.0-beta4.22272.1", "System.CommandLine": "2.0.0-beta4.22272.1",
"System.CommandLine.NamingConventionBinder": "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": { "runtime": {
"Wabbajack.CLI.Builder.dll": {} "Wabbajack.CLI.Builder.dll": {}
} }
}, },
"Wabbajack.Common/0.3.17": { "Wabbajack.Common/0.3.18": {
"dependencies": { "dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1", "Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"System.Reactive": "6.0.1", "System.Reactive": "6.0.1",
"Wabbajack.DTOs": "0.3.17", "Wabbajack.DTOs": "0.3.18",
"Wabbajack.Networking.Http": "0.3.17", "Wabbajack.Networking.Http": "0.3.18",
"Wabbajack.Paths.IO": "0.3.17" "Wabbajack.Paths.IO": "0.3.18"
}, },
"runtime": { "runtime": {
"Wabbajack.Common.dll": {} "Wabbajack.Common.dll": {}
} }
}, },
"Wabbajack.Compiler/0.3.17": { "Wabbajack.Compiler/0.3.18": {
"dependencies": { "dependencies": {
"F23.StringSimilarity": "6.0.0", "F23.StringSimilarity": "6.0.0",
"Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Newtonsoft.Json": "13.0.3", "Newtonsoft.Json": "13.0.3",
"SixLabors.ImageSharp": "3.1.6", "SixLabors.ImageSharp": "3.1.6",
"Wabbajack.Downloaders.Dispatcher": "0.3.17", "Wabbajack.Downloaders.Dispatcher": "0.3.18",
"Wabbajack.Installer": "0.3.17", "Wabbajack.Installer": "0.3.18",
"Wabbajack.VFS": "0.3.17", "Wabbajack.VFS": "0.3.18",
"ini-parser-netstandard": "2.5.2" "ini-parser-netstandard": "2.5.2"
}, },
"runtime": { "runtime": {
"Wabbajack.Compiler.dll": {} "Wabbajack.Compiler.dll": {}
} }
}, },
"Wabbajack.Compression.BSA/0.3.17": { "Wabbajack.Compression.BSA/0.3.18": {
"dependencies": { "dependencies": {
"K4os.Compression.LZ4.Streams": "1.3.8", "K4os.Compression.LZ4.Streams": "1.3.8",
"Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"SharpZipLib": "1.4.2", "SharpZipLib": "1.4.2",
"Wabbajack.Common": "0.3.17", "Wabbajack.Common": "0.3.18",
"Wabbajack.DTOs": "0.3.17" "Wabbajack.DTOs": "0.3.18"
}, },
"runtime": { "runtime": {
"Wabbajack.Compression.BSA.dll": {} "Wabbajack.Compression.BSA.dll": {}
} }
}, },
"Wabbajack.Compression.Zip/0.3.17": { "Wabbajack.Compression.Zip/0.3.18": {
"dependencies": { "dependencies": {
"Wabbajack.IO.Async": "0.3.17" "Wabbajack.IO.Async": "0.3.18"
}, },
"runtime": { "runtime": {
"Wabbajack.Compression.Zip.dll": {} "Wabbajack.Compression.Zip.dll": {}
} }
}, },
"Wabbajack.Configuration/0.3.17": { "Wabbajack.Configuration/0.3.18": {
"runtime": { "runtime": {
"Wabbajack.Configuration.dll": {} "Wabbajack.Configuration.dll": {}
} }
}, },
"Wabbajack.Downloaders.Bethesda/0.3.17": { "Wabbajack.Downloaders.Bethesda/0.3.18": {
"dependencies": { "dependencies": {
"LibAES-CTR": "1.1.0", "LibAES-CTR": "1.1.0",
"Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"SharpZipLib": "1.4.2", "SharpZipLib": "1.4.2",
"Wabbajack.Common": "0.3.17", "Wabbajack.Common": "0.3.18",
"Wabbajack.Downloaders.Interfaces": "0.3.17", "Wabbajack.Downloaders.Interfaces": "0.3.18",
"Wabbajack.Networking.BethesdaNet": "0.3.17" "Wabbajack.Networking.BethesdaNet": "0.3.18"
}, },
"runtime": { "runtime": {
"Wabbajack.Downloaders.Bethesda.dll": {} "Wabbajack.Downloaders.Bethesda.dll": {}
} }
}, },
"Wabbajack.Downloaders.Dispatcher/0.3.17": { "Wabbajack.Downloaders.Dispatcher/0.3.18": {
"dependencies": { "dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1", "Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"Newtonsoft.Json": "13.0.3", "Newtonsoft.Json": "13.0.3",
"SixLabors.ImageSharp": "3.1.6", "SixLabors.ImageSharp": "3.1.6",
"Wabbajack.Downloaders.Bethesda": "0.3.17", "Wabbajack.Downloaders.Bethesda": "0.3.18",
"Wabbajack.Downloaders.GameFile": "0.3.17", "Wabbajack.Downloaders.GameFile": "0.3.18",
"Wabbajack.Downloaders.GoogleDrive": "0.3.17", "Wabbajack.Downloaders.GoogleDrive": "0.3.18",
"Wabbajack.Downloaders.Http": "0.3.17", "Wabbajack.Downloaders.Http": "0.3.18",
"Wabbajack.Downloaders.IPS4OAuth2Downloader": "0.3.17", "Wabbajack.Downloaders.IPS4OAuth2Downloader": "0.3.18",
"Wabbajack.Downloaders.Interfaces": "0.3.17", "Wabbajack.Downloaders.Interfaces": "0.3.18",
"Wabbajack.Downloaders.Manual": "0.3.17", "Wabbajack.Downloaders.Manual": "0.3.18",
"Wabbajack.Downloaders.MediaFire": "0.3.17", "Wabbajack.Downloaders.MediaFire": "0.3.18",
"Wabbajack.Downloaders.Mega": "0.3.17", "Wabbajack.Downloaders.Mega": "0.3.18",
"Wabbajack.Downloaders.ModDB": "0.3.17", "Wabbajack.Downloaders.ModDB": "0.3.18",
"Wabbajack.Downloaders.Nexus": "0.3.17", "Wabbajack.Downloaders.Nexus": "0.3.18",
"Wabbajack.Downloaders.VerificationCache": "0.3.17", "Wabbajack.Downloaders.VerificationCache": "0.3.18",
"Wabbajack.Downloaders.WabbajackCDN": "0.3.17", "Wabbajack.Downloaders.WabbajackCDN": "0.3.18",
"Wabbajack.Networking.WabbajackClientApi": "0.3.17" "Wabbajack.Networking.WabbajackClientApi": "0.3.18"
}, },
"runtime": { "runtime": {
"Wabbajack.Downloaders.Dispatcher.dll": {} "Wabbajack.Downloaders.Dispatcher.dll": {}
} }
}, },
"Wabbajack.Downloaders.GameFile/0.3.17": { "Wabbajack.Downloaders.GameFile/0.3.18": {
"dependencies": { "dependencies": {
"GameFinder.StoreHandlers.EADesktop": "4.5.0", "GameFinder.StoreHandlers.EADesktop": "4.5.0",
"GameFinder.StoreHandlers.EGS": "4.5.0", "GameFinder.StoreHandlers.EGS": "4.5.0",
@@ -1903,360 +1903,360 @@
"Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"SixLabors.ImageSharp": "3.1.6", "SixLabors.ImageSharp": "3.1.6",
"Wabbajack.Downloaders.Interfaces": "0.3.17", "Wabbajack.Downloaders.Interfaces": "0.3.18",
"Wabbajack.VFS": "0.3.17" "Wabbajack.VFS": "0.3.18"
}, },
"runtime": { "runtime": {
"Wabbajack.Downloaders.GameFile.dll": {} "Wabbajack.Downloaders.GameFile.dll": {}
} }
}, },
"Wabbajack.Downloaders.GoogleDrive/0.3.17": { "Wabbajack.Downloaders.GoogleDrive/0.3.18": {
"dependencies": { "dependencies": {
"HtmlAgilityPack": "1.11.72", "HtmlAgilityPack": "1.11.72",
"Microsoft.AspNetCore.Http.Extensions": "2.3.0", "Microsoft.AspNetCore.Http.Extensions": "2.3.0",
"Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1", "Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"Wabbajack.Common": "0.3.17", "Wabbajack.Common": "0.3.18",
"Wabbajack.DTOs": "0.3.17", "Wabbajack.DTOs": "0.3.18",
"Wabbajack.Downloaders.Interfaces": "0.3.17", "Wabbajack.Downloaders.Interfaces": "0.3.18",
"Wabbajack.Networking.Http": "0.3.17", "Wabbajack.Networking.Http": "0.3.18",
"Wabbajack.Networking.Http.Interfaces": "0.3.17" "Wabbajack.Networking.Http.Interfaces": "0.3.18"
}, },
"runtime": { "runtime": {
"Wabbajack.Downloaders.GoogleDrive.dll": {} "Wabbajack.Downloaders.GoogleDrive.dll": {}
} }
}, },
"Wabbajack.Downloaders.Http/0.3.17": { "Wabbajack.Downloaders.Http/0.3.18": {
"dependencies": { "dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1", "Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"Wabbajack.Common": "0.3.17", "Wabbajack.Common": "0.3.18",
"Wabbajack.DTOs": "0.3.17", "Wabbajack.DTOs": "0.3.18",
"Wabbajack.Downloaders.Interfaces": "0.3.17", "Wabbajack.Downloaders.Interfaces": "0.3.18",
"Wabbajack.Networking.BethesdaNet": "0.3.17", "Wabbajack.Networking.BethesdaNet": "0.3.18",
"Wabbajack.Networking.Http.Interfaces": "0.3.17", "Wabbajack.Networking.Http.Interfaces": "0.3.18",
"Wabbajack.Paths.IO": "0.3.17" "Wabbajack.Paths.IO": "0.3.18"
}, },
"runtime": { "runtime": {
"Wabbajack.Downloaders.Http.dll": {} "Wabbajack.Downloaders.Http.dll": {}
} }
}, },
"Wabbajack.Downloaders.Interfaces/0.3.17": { "Wabbajack.Downloaders.Interfaces/0.3.18": {
"dependencies": { "dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Wabbajack.Compression.Zip": "0.3.17", "Wabbajack.Compression.Zip": "0.3.18",
"Wabbajack.DTOs": "0.3.17", "Wabbajack.DTOs": "0.3.18",
"Wabbajack.Paths.IO": "0.3.17" "Wabbajack.Paths.IO": "0.3.18"
}, },
"runtime": { "runtime": {
"Wabbajack.Downloaders.Interfaces.dll": {} "Wabbajack.Downloaders.Interfaces.dll": {}
} }
}, },
"Wabbajack.Downloaders.IPS4OAuth2Downloader/0.3.17": { "Wabbajack.Downloaders.IPS4OAuth2Downloader/0.3.18": {
"dependencies": { "dependencies": {
"F23.StringSimilarity": "6.0.0", "F23.StringSimilarity": "6.0.0",
"Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1", "Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"Wabbajack.Common": "0.3.17", "Wabbajack.Common": "0.3.18",
"Wabbajack.Downloaders.Interfaces": "0.3.17", "Wabbajack.Downloaders.Interfaces": "0.3.18",
"Wabbajack.Networking.Http": "0.3.17", "Wabbajack.Networking.Http": "0.3.18",
"Wabbajack.Networking.Http.Interfaces": "0.3.17" "Wabbajack.Networking.Http.Interfaces": "0.3.18"
}, },
"runtime": { "runtime": {
"Wabbajack.Downloaders.IPS4OAuth2Downloader.dll": {} "Wabbajack.Downloaders.IPS4OAuth2Downloader.dll": {}
} }
}, },
"Wabbajack.Downloaders.Manual/0.3.17": { "Wabbajack.Downloaders.Manual/0.3.18": {
"dependencies": { "dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1", "Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"Wabbajack.Common": "0.3.17", "Wabbajack.Common": "0.3.18",
"Wabbajack.Downloaders.Interfaces": "0.3.17" "Wabbajack.Downloaders.Interfaces": "0.3.18"
}, },
"runtime": { "runtime": {
"Wabbajack.Downloaders.Manual.dll": {} "Wabbajack.Downloaders.Manual.dll": {}
} }
}, },
"Wabbajack.Downloaders.MediaFire/0.3.17": { "Wabbajack.Downloaders.MediaFire/0.3.18": {
"dependencies": { "dependencies": {
"HtmlAgilityPack": "1.11.72", "HtmlAgilityPack": "1.11.72",
"Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1", "Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"Wabbajack.Common": "0.3.17", "Wabbajack.Common": "0.3.18",
"Wabbajack.Downloaders.Interfaces": "0.3.17", "Wabbajack.Downloaders.Interfaces": "0.3.18",
"Wabbajack.Networking.Http.Interfaces": "0.3.17" "Wabbajack.Networking.Http.Interfaces": "0.3.18"
}, },
"runtime": { "runtime": {
"Wabbajack.Downloaders.MediaFire.dll": {} "Wabbajack.Downloaders.MediaFire.dll": {}
} }
}, },
"Wabbajack.Downloaders.Mega/0.3.17": { "Wabbajack.Downloaders.Mega/0.3.18": {
"dependencies": { "dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1", "Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"Newtonsoft.Json": "13.0.3", "Newtonsoft.Json": "13.0.3",
"Wabbajack.Common": "0.3.17", "Wabbajack.Common": "0.3.18",
"Wabbajack.Downloaders.Interfaces": "0.3.17", "Wabbajack.Downloaders.Interfaces": "0.3.18",
"Wabbajack.Paths.IO": "0.3.17" "Wabbajack.Paths.IO": "0.3.18"
}, },
"runtime": { "runtime": {
"Wabbajack.Downloaders.Mega.dll": {} "Wabbajack.Downloaders.Mega.dll": {}
} }
}, },
"Wabbajack.Downloaders.ModDB/0.3.17": { "Wabbajack.Downloaders.ModDB/0.3.18": {
"dependencies": { "dependencies": {
"HtmlAgilityPack": "1.11.72", "HtmlAgilityPack": "1.11.72",
"Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1", "Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"Newtonsoft.Json": "13.0.3", "Newtonsoft.Json": "13.0.3",
"Wabbajack.Common": "0.3.17", "Wabbajack.Common": "0.3.18",
"Wabbajack.Downloaders.Interfaces": "0.3.17", "Wabbajack.Downloaders.Interfaces": "0.3.18",
"Wabbajack.Networking.Http": "0.3.17", "Wabbajack.Networking.Http": "0.3.18",
"Wabbajack.Networking.Http.Interfaces": "0.3.17" "Wabbajack.Networking.Http.Interfaces": "0.3.18"
}, },
"runtime": { "runtime": {
"Wabbajack.Downloaders.ModDB.dll": {} "Wabbajack.Downloaders.ModDB.dll": {}
} }
}, },
"Wabbajack.Downloaders.Nexus/0.3.17": { "Wabbajack.Downloaders.Nexus/0.3.18": {
"dependencies": { "dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Wabbajack.DTOs": "0.3.17", "Wabbajack.DTOs": "0.3.18",
"Wabbajack.Downloaders.Interfaces": "0.3.17", "Wabbajack.Downloaders.Interfaces": "0.3.18",
"Wabbajack.Hashing.xxHash64": "0.3.17", "Wabbajack.Hashing.xxHash64": "0.3.18",
"Wabbajack.Networking.Http": "0.3.17", "Wabbajack.Networking.Http": "0.3.18",
"Wabbajack.Networking.Http.Interfaces": "0.3.17", "Wabbajack.Networking.Http.Interfaces": "0.3.18",
"Wabbajack.Networking.NexusApi": "0.3.17", "Wabbajack.Networking.NexusApi": "0.3.18",
"Wabbajack.Paths": "0.3.17" "Wabbajack.Paths": "0.3.18"
}, },
"runtime": { "runtime": {
"Wabbajack.Downloaders.Nexus.dll": {} "Wabbajack.Downloaders.Nexus.dll": {}
} }
}, },
"Wabbajack.Downloaders.VerificationCache/0.3.17": { "Wabbajack.Downloaders.VerificationCache/0.3.18": {
"dependencies": { "dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1", "Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"Stub.System.Data.SQLite.Core.NetStandard": "1.0.119", "Stub.System.Data.SQLite.Core.NetStandard": "1.0.119",
"Wabbajack.DTOs": "0.3.17", "Wabbajack.DTOs": "0.3.18",
"Wabbajack.Paths.IO": "0.3.17" "Wabbajack.Paths.IO": "0.3.18"
}, },
"runtime": { "runtime": {
"Wabbajack.Downloaders.VerificationCache.dll": {} "Wabbajack.Downloaders.VerificationCache.dll": {}
} }
}, },
"Wabbajack.Downloaders.WabbajackCDN/0.3.17": { "Wabbajack.Downloaders.WabbajackCDN/0.3.18": {
"dependencies": { "dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1", "Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"Microsoft.Toolkit.HighPerformance": "7.1.2", "Microsoft.Toolkit.HighPerformance": "7.1.2",
"Wabbajack.Common": "0.3.17", "Wabbajack.Common": "0.3.18",
"Wabbajack.Downloaders.Interfaces": "0.3.17", "Wabbajack.Downloaders.Interfaces": "0.3.18",
"Wabbajack.Networking.Http": "0.3.17", "Wabbajack.Networking.Http": "0.3.18",
"Wabbajack.RateLimiter": "0.3.17" "Wabbajack.RateLimiter": "0.3.18"
}, },
"runtime": { "runtime": {
"Wabbajack.Downloaders.WabbajackCDN.dll": {} "Wabbajack.Downloaders.WabbajackCDN.dll": {}
} }
}, },
"Wabbajack.DTOs/0.3.17": { "Wabbajack.DTOs/0.3.18": {
"dependencies": { "dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Wabbajack.Hashing.xxHash64": "0.3.17", "Wabbajack.Hashing.xxHash64": "0.3.18",
"Wabbajack.Paths": "0.3.17" "Wabbajack.Paths": "0.3.18"
}, },
"runtime": { "runtime": {
"Wabbajack.DTOs.dll": {} "Wabbajack.DTOs.dll": {}
} }
}, },
"Wabbajack.FileExtractor/0.3.17": { "Wabbajack.FileExtractor/0.3.18": {
"dependencies": { "dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1", "Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"OMODFramework": "3.0.1", "OMODFramework": "3.0.1",
"Wabbajack.Common": "0.3.17", "Wabbajack.Common": "0.3.18",
"Wabbajack.Compression.BSA": "0.3.17", "Wabbajack.Compression.BSA": "0.3.18",
"Wabbajack.Hashing.PHash": "0.3.17", "Wabbajack.Hashing.PHash": "0.3.18",
"Wabbajack.Paths": "0.3.17" "Wabbajack.Paths": "0.3.18"
}, },
"runtime": { "runtime": {
"Wabbajack.FileExtractor.dll": {} "Wabbajack.FileExtractor.dll": {}
} }
}, },
"Wabbajack.Hashing.PHash/0.3.17": { "Wabbajack.Hashing.PHash/0.3.18": {
"dependencies": { "dependencies": {
"BCnEncoder.Net.ImageSharp": "1.1.1", "BCnEncoder.Net.ImageSharp": "1.1.1",
"Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Shipwreck.Phash": "0.5.0", "Shipwreck.Phash": "0.5.0",
"SixLabors.ImageSharp": "3.1.6", "SixLabors.ImageSharp": "3.1.6",
"Wabbajack.Common": "0.3.17", "Wabbajack.Common": "0.3.18",
"Wabbajack.DTOs": "0.3.17", "Wabbajack.DTOs": "0.3.18",
"Wabbajack.Paths": "0.3.17", "Wabbajack.Paths": "0.3.18",
"Wabbajack.Paths.IO": "0.3.17" "Wabbajack.Paths.IO": "0.3.18"
}, },
"runtime": { "runtime": {
"Wabbajack.Hashing.PHash.dll": {} "Wabbajack.Hashing.PHash.dll": {}
} }
}, },
"Wabbajack.Hashing.xxHash64/0.3.17": { "Wabbajack.Hashing.xxHash64/0.3.18": {
"dependencies": { "dependencies": {
"Wabbajack.Paths": "0.3.17", "Wabbajack.Paths": "0.3.18",
"Wabbajack.RateLimiter": "0.3.17" "Wabbajack.RateLimiter": "0.3.18"
}, },
"runtime": { "runtime": {
"Wabbajack.Hashing.xxHash64.dll": {} "Wabbajack.Hashing.xxHash64.dll": {}
} }
}, },
"Wabbajack.Installer/0.3.17": { "Wabbajack.Installer/0.3.18": {
"dependencies": { "dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Newtonsoft.Json": "13.0.3", "Newtonsoft.Json": "13.0.3",
"Octopus.Octodiff": "2.0.548", "Octopus.Octodiff": "2.0.548",
"SixLabors.ImageSharp": "3.1.6", "SixLabors.ImageSharp": "3.1.6",
"Wabbajack.DTOs": "0.3.17", "Wabbajack.DTOs": "0.3.18",
"Wabbajack.Downloaders.Dispatcher": "0.3.17", "Wabbajack.Downloaders.Dispatcher": "0.3.18",
"Wabbajack.Downloaders.GameFile": "0.3.17", "Wabbajack.Downloaders.GameFile": "0.3.18",
"Wabbajack.FileExtractor": "0.3.17", "Wabbajack.FileExtractor": "0.3.18",
"Wabbajack.Networking.WabbajackClientApi": "0.3.17", "Wabbajack.Networking.WabbajackClientApi": "0.3.18",
"Wabbajack.Paths": "0.3.17", "Wabbajack.Paths": "0.3.18",
"Wabbajack.Paths.IO": "0.3.17", "Wabbajack.Paths.IO": "0.3.18",
"Wabbajack.VFS": "0.3.17", "Wabbajack.VFS": "0.3.18",
"ini-parser-netstandard": "2.5.2" "ini-parser-netstandard": "2.5.2"
}, },
"runtime": { "runtime": {
"Wabbajack.Installer.dll": {} "Wabbajack.Installer.dll": {}
} }
}, },
"Wabbajack.IO.Async/0.3.17": { "Wabbajack.IO.Async/0.3.18": {
"runtime": { "runtime": {
"Wabbajack.IO.Async.dll": {} "Wabbajack.IO.Async.dll": {}
} }
}, },
"Wabbajack.Networking.BethesdaNet/0.3.17": { "Wabbajack.Networking.BethesdaNet/0.3.18": {
"dependencies": { "dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Wabbajack.DTOs": "0.3.17", "Wabbajack.DTOs": "0.3.18",
"Wabbajack.Networking.Http": "0.3.17", "Wabbajack.Networking.Http": "0.3.18",
"Wabbajack.Networking.Http.Interfaces": "0.3.17" "Wabbajack.Networking.Http.Interfaces": "0.3.18"
}, },
"runtime": { "runtime": {
"Wabbajack.Networking.BethesdaNet.dll": {} "Wabbajack.Networking.BethesdaNet.dll": {}
} }
}, },
"Wabbajack.Networking.Discord/0.3.17": { "Wabbajack.Networking.Discord/0.3.18": {
"dependencies": { "dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.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": { "runtime": {
"Wabbajack.Networking.Discord.dll": {} "Wabbajack.Networking.Discord.dll": {}
} }
}, },
"Wabbajack.Networking.GitHub/0.3.17": { "Wabbajack.Networking.GitHub/0.3.18": {
"dependencies": { "dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1", "Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"Octokit": "14.0.0", "Octokit": "14.0.0",
"Wabbajack.DTOs": "0.3.17", "Wabbajack.DTOs": "0.3.18",
"Wabbajack.Networking.Http.Interfaces": "0.3.17" "Wabbajack.Networking.Http.Interfaces": "0.3.18"
}, },
"runtime": { "runtime": {
"Wabbajack.Networking.GitHub.dll": {} "Wabbajack.Networking.GitHub.dll": {}
} }
}, },
"Wabbajack.Networking.Http/0.3.17": { "Wabbajack.Networking.Http/0.3.18": {
"dependencies": { "dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Http": "9.0.1", "Microsoft.Extensions.Http": "9.0.1",
"Microsoft.Extensions.Logging": "9.0.1", "Microsoft.Extensions.Logging": "9.0.1",
"Wabbajack.Configuration": "0.3.17", "Wabbajack.Configuration": "0.3.18",
"Wabbajack.Downloaders.Interfaces": "0.3.17", "Wabbajack.Downloaders.Interfaces": "0.3.18",
"Wabbajack.Hashing.xxHash64": "0.3.17", "Wabbajack.Hashing.xxHash64": "0.3.18",
"Wabbajack.Networking.Http.Interfaces": "0.3.17", "Wabbajack.Networking.Http.Interfaces": "0.3.18",
"Wabbajack.Paths": "0.3.17", "Wabbajack.Paths": "0.3.18",
"Wabbajack.Paths.IO": "0.3.17" "Wabbajack.Paths.IO": "0.3.18"
}, },
"runtime": { "runtime": {
"Wabbajack.Networking.Http.dll": {} "Wabbajack.Networking.Http.dll": {}
} }
}, },
"Wabbajack.Networking.Http.Interfaces/0.3.17": { "Wabbajack.Networking.Http.Interfaces/0.3.18": {
"dependencies": { "dependencies": {
"Wabbajack.Hashing.xxHash64": "0.3.17" "Wabbajack.Hashing.xxHash64": "0.3.18"
}, },
"runtime": { "runtime": {
"Wabbajack.Networking.Http.Interfaces.dll": {} "Wabbajack.Networking.Http.Interfaces.dll": {}
} }
}, },
"Wabbajack.Networking.NexusApi/0.3.17": { "Wabbajack.Networking.NexusApi/0.3.18": {
"dependencies": { "dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1", "Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"Wabbajack.DTOs": "0.3.17", "Wabbajack.DTOs": "0.3.18",
"Wabbajack.Networking.Http": "0.3.17", "Wabbajack.Networking.Http": "0.3.18",
"Wabbajack.Networking.Http.Interfaces": "0.3.17", "Wabbajack.Networking.Http.Interfaces": "0.3.18",
"Wabbajack.Networking.WabbajackClientApi": "0.3.17" "Wabbajack.Networking.WabbajackClientApi": "0.3.18"
}, },
"runtime": { "runtime": {
"Wabbajack.Networking.NexusApi.dll": {} "Wabbajack.Networking.NexusApi.dll": {}
} }
}, },
"Wabbajack.Networking.WabbajackClientApi/0.3.17": { "Wabbajack.Networking.WabbajackClientApi/0.3.18": {
"dependencies": { "dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1", "Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"Octokit": "14.0.0", "Octokit": "14.0.0",
"Wabbajack.Common": "0.3.17", "Wabbajack.Common": "0.3.18",
"Wabbajack.DTOs": "0.3.17", "Wabbajack.DTOs": "0.3.18",
"Wabbajack.Paths.IO": "0.3.17", "Wabbajack.Paths.IO": "0.3.18",
"Wabbajack.VFS.Interfaces": "0.3.17", "Wabbajack.VFS.Interfaces": "0.3.18",
"YamlDotNet": "16.3.0" "YamlDotNet": "16.3.0"
}, },
"runtime": { "runtime": {
"Wabbajack.Networking.WabbajackClientApi.dll": {} "Wabbajack.Networking.WabbajackClientApi.dll": {}
} }
}, },
"Wabbajack.Paths/0.3.17": { "Wabbajack.Paths/0.3.18": {
"runtime": { "runtime": {
"Wabbajack.Paths.dll": {} "Wabbajack.Paths.dll": {}
} }
}, },
"Wabbajack.Paths.IO/0.3.17": { "Wabbajack.Paths.IO/0.3.18": {
"dependencies": { "dependencies": {
"Wabbajack.Paths": "0.3.17", "Wabbajack.Paths": "0.3.18",
"shortid": "4.0.0" "shortid": "4.0.0"
}, },
"runtime": { "runtime": {
"Wabbajack.Paths.IO.dll": {} "Wabbajack.Paths.IO.dll": {}
} }
}, },
"Wabbajack.RateLimiter/0.3.17": { "Wabbajack.RateLimiter/0.3.18": {
"runtime": { "runtime": {
"Wabbajack.RateLimiter.dll": {} "Wabbajack.RateLimiter.dll": {}
} }
}, },
"Wabbajack.Server.Lib/0.3.17": { "Wabbajack.Server.Lib/0.3.18": {
"dependencies": { "dependencies": {
"FluentFTP": "52.0.0", "FluentFTP": "52.0.0",
"Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection": "9.0.1",
@@ -2264,58 +2264,58 @@
"Nettle": "3.0.0", "Nettle": "3.0.0",
"Newtonsoft.Json": "13.0.3", "Newtonsoft.Json": "13.0.3",
"SixLabors.ImageSharp": "3.1.6", "SixLabors.ImageSharp": "3.1.6",
"Wabbajack.Common": "0.3.17", "Wabbajack.Common": "0.3.18",
"Wabbajack.Networking.Http.Interfaces": "0.3.17", "Wabbajack.Networking.Http.Interfaces": "0.3.18",
"Wabbajack.Services.OSIntegrated": "0.3.17" "Wabbajack.Services.OSIntegrated": "0.3.18"
}, },
"runtime": { "runtime": {
"Wabbajack.Server.Lib.dll": {} "Wabbajack.Server.Lib.dll": {}
} }
}, },
"Wabbajack.Services.OSIntegrated/0.3.17": { "Wabbajack.Services.OSIntegrated/0.3.18": {
"dependencies": { "dependencies": {
"DeviceId": "6.8.0", "DeviceId": "6.8.0",
"Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Newtonsoft.Json": "13.0.3", "Newtonsoft.Json": "13.0.3",
"SixLabors.ImageSharp": "3.1.6", "SixLabors.ImageSharp": "3.1.6",
"Wabbajack.Compiler": "0.3.17", "Wabbajack.Compiler": "0.3.18",
"Wabbajack.Downloaders.Dispatcher": "0.3.17", "Wabbajack.Downloaders.Dispatcher": "0.3.18",
"Wabbajack.Installer": "0.3.17", "Wabbajack.Installer": "0.3.18",
"Wabbajack.Networking.BethesdaNet": "0.3.17", "Wabbajack.Networking.BethesdaNet": "0.3.18",
"Wabbajack.Networking.Discord": "0.3.17", "Wabbajack.Networking.Discord": "0.3.18",
"Wabbajack.VFS": "0.3.17" "Wabbajack.VFS": "0.3.18"
}, },
"runtime": { "runtime": {
"Wabbajack.Services.OSIntegrated.dll": {} "Wabbajack.Services.OSIntegrated.dll": {}
} }
}, },
"Wabbajack.VFS/0.3.17": { "Wabbajack.VFS/0.3.18": {
"dependencies": { "dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1", "Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"SixLabors.ImageSharp": "3.1.6", "SixLabors.ImageSharp": "3.1.6",
"System.Data.SQLite.Core": "1.0.119", "System.Data.SQLite.Core": "1.0.119",
"Wabbajack.Common": "0.3.17", "Wabbajack.Common": "0.3.18",
"Wabbajack.FileExtractor": "0.3.17", "Wabbajack.FileExtractor": "0.3.18",
"Wabbajack.Hashing.PHash": "0.3.17", "Wabbajack.Hashing.PHash": "0.3.18",
"Wabbajack.Hashing.xxHash64": "0.3.17", "Wabbajack.Hashing.xxHash64": "0.3.18",
"Wabbajack.Paths": "0.3.17", "Wabbajack.Paths": "0.3.18",
"Wabbajack.Paths.IO": "0.3.17", "Wabbajack.Paths.IO": "0.3.18",
"Wabbajack.VFS.Interfaces": "0.3.17" "Wabbajack.VFS.Interfaces": "0.3.18"
}, },
"runtime": { "runtime": {
"Wabbajack.VFS.dll": {} "Wabbajack.VFS.dll": {}
} }
}, },
"Wabbajack.VFS.Interfaces/0.3.17": { "Wabbajack.VFS.Interfaces/0.3.18": {
"dependencies": { "dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Wabbajack.DTOs": "0.3.17", "Wabbajack.DTOs": "0.3.18",
"Wabbajack.Hashing.xxHash64": "0.3.17", "Wabbajack.Hashing.xxHash64": "0.3.18",
"Wabbajack.Paths": "0.3.17" "Wabbajack.Paths": "0.3.18"
}, },
"runtime": { "runtime": {
"Wabbajack.VFS.Interfaces.dll": {} "Wabbajack.VFS.Interfaces.dll": {}
@@ -2332,7 +2332,7 @@
} }
}, },
"libraries": { "libraries": {
"jackify-engine/0.3.17": { "jackify-engine/0.3.18": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
@@ -3021,202 +3021,202 @@
"path": "yamldotnet/16.3.0", "path": "yamldotnet/16.3.0",
"hashPath": "yamldotnet.16.3.0.nupkg.sha512" "hashPath": "yamldotnet.16.3.0.nupkg.sha512"
}, },
"Wabbajack.CLI.Builder/0.3.17": { "Wabbajack.CLI.Builder/0.3.18": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Common/0.3.17": { "Wabbajack.Common/0.3.18": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Compiler/0.3.17": { "Wabbajack.Compiler/0.3.18": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Compression.BSA/0.3.17": { "Wabbajack.Compression.BSA/0.3.18": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Compression.Zip/0.3.17": { "Wabbajack.Compression.Zip/0.3.18": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Configuration/0.3.17": { "Wabbajack.Configuration/0.3.18": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Downloaders.Bethesda/0.3.17": { "Wabbajack.Downloaders.Bethesda/0.3.18": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Downloaders.Dispatcher/0.3.17": { "Wabbajack.Downloaders.Dispatcher/0.3.18": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Downloaders.GameFile/0.3.17": { "Wabbajack.Downloaders.GameFile/0.3.18": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Downloaders.GoogleDrive/0.3.17": { "Wabbajack.Downloaders.GoogleDrive/0.3.18": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Downloaders.Http/0.3.17": { "Wabbajack.Downloaders.Http/0.3.18": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Downloaders.Interfaces/0.3.17": { "Wabbajack.Downloaders.Interfaces/0.3.18": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Downloaders.IPS4OAuth2Downloader/0.3.17": { "Wabbajack.Downloaders.IPS4OAuth2Downloader/0.3.18": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Downloaders.Manual/0.3.17": { "Wabbajack.Downloaders.Manual/0.3.18": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Downloaders.MediaFire/0.3.17": { "Wabbajack.Downloaders.MediaFire/0.3.18": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Downloaders.Mega/0.3.17": { "Wabbajack.Downloaders.Mega/0.3.18": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Downloaders.ModDB/0.3.17": { "Wabbajack.Downloaders.ModDB/0.3.18": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Downloaders.Nexus/0.3.17": { "Wabbajack.Downloaders.Nexus/0.3.18": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Downloaders.VerificationCache/0.3.17": { "Wabbajack.Downloaders.VerificationCache/0.3.18": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Downloaders.WabbajackCDN/0.3.17": { "Wabbajack.Downloaders.WabbajackCDN/0.3.18": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.DTOs/0.3.17": { "Wabbajack.DTOs/0.3.18": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.FileExtractor/0.3.17": { "Wabbajack.FileExtractor/0.3.18": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Hashing.PHash/0.3.17": { "Wabbajack.Hashing.PHash/0.3.18": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Hashing.xxHash64/0.3.17": { "Wabbajack.Hashing.xxHash64/0.3.18": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Installer/0.3.17": { "Wabbajack.Installer/0.3.18": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.IO.Async/0.3.17": { "Wabbajack.IO.Async/0.3.18": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Networking.BethesdaNet/0.3.17": { "Wabbajack.Networking.BethesdaNet/0.3.18": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Networking.Discord/0.3.17": { "Wabbajack.Networking.Discord/0.3.18": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Networking.GitHub/0.3.17": { "Wabbajack.Networking.GitHub/0.3.18": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Networking.Http/0.3.17": { "Wabbajack.Networking.Http/0.3.18": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Networking.Http.Interfaces/0.3.17": { "Wabbajack.Networking.Http.Interfaces/0.3.18": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Networking.NexusApi/0.3.17": { "Wabbajack.Networking.NexusApi/0.3.18": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Networking.WabbajackClientApi/0.3.17": { "Wabbajack.Networking.WabbajackClientApi/0.3.18": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Paths/0.3.17": { "Wabbajack.Paths/0.3.18": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Paths.IO/0.3.17": { "Wabbajack.Paths.IO/0.3.18": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.RateLimiter/0.3.17": { "Wabbajack.RateLimiter/0.3.18": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Server.Lib/0.3.17": { "Wabbajack.Server.Lib/0.3.18": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Services.OSIntegrated/0.3.17": { "Wabbajack.Services.OSIntegrated/0.3.18": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.VFS/0.3.17": { "Wabbajack.VFS/0.3.18": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.VFS.Interfaces/0.3.17": { "Wabbajack.VFS.Interfaces/0.3.18": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""

Binary file not shown.

BIN
jackify/engine/test.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 335 B

View File

@@ -195,6 +195,9 @@ Modlist Install and Configuration complete!:
• You should now be able to Launch '{self.modlist_name}' through Steam. • You should now be able to Launch '{self.modlist_name}' through Steam.
• Congratulations and enjoy the game! • 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""" Detailed log available at: ~/Jackify/logs/Configure_New_Modlist_workflow.log"""
return completion_text return completion_text

View File

@@ -40,12 +40,12 @@ class SuccessDialog(QDialog):
self.setWindowTitle("Success!") self.setWindowTitle("Success!")
self.setWindowModality(Qt.NonModal) self.setWindowModality(Qt.NonModal)
self.setAttribute(Qt.WA_ShowWithoutActivating, True) self.setAttribute(Qt.WA_ShowWithoutActivating, True)
self.setFixedSize(500, 420) self.setFixedSize(500, 500)
self.setWindowFlag(Qt.WindowDoesNotAcceptFocus, True) self.setWindowFlag(Qt.WindowDoesNotAcceptFocus, True)
self.setStyleSheet("QDialog { background: #181818; color: #fff; border-radius: 12px; }" ) self.setStyleSheet("QDialog { background: #181818; color: #fff; border-radius: 12px; }" )
layout = QVBoxLayout(self) layout = QVBoxLayout(self)
layout.setSpacing(0) layout.setSpacing(0)
layout.setContentsMargins(0, 0, 0, 0) layout.setContentsMargins(30, 30, 30, 30)
# --- Card background for content --- # --- Card background for content ---
card = QFrame(self) card = QFrame(self)
@@ -53,6 +53,7 @@ class SuccessDialog(QDialog):
card.setFrameShape(QFrame.StyledPanel) card.setFrameShape(QFrame.StyledPanel)
card.setFrameShadow(QFrame.Raised) card.setFrameShadow(QFrame.Raised)
card.setFixedWidth(440) card.setFixedWidth(440)
card.setMinimumHeight(380)
card_layout = QVBoxLayout(card) card_layout = QVBoxLayout(card)
card_layout.setSpacing(12) card_layout.setSpacing(12)
card_layout.setContentsMargins(28, 28, 28, 28) card_layout.setContentsMargins(28, 28, 28, 28)
@@ -65,23 +66,6 @@ class SuccessDialog(QDialog):
) )
card.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) 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) # Success title (less saturated green)
title_label = QLabel("Success!") title_label = QLabel("Success!")
title_label.setAlignment(Qt.AlignCenter) title_label.setAlignment(Qt.AlignCenter)
@@ -137,11 +121,12 @@ class SuccessDialog(QDialog):
next_steps_label = QLabel(next_steps_text) next_steps_label = QLabel(next_steps_text)
next_steps_label.setAlignment(Qt.AlignCenter) next_steps_label.setAlignment(Qt.AlignCenter)
next_steps_label.setWordWrap(True) next_steps_label.setWordWrap(True)
next_steps_label.setMinimumHeight(100)
next_steps_label.setStyleSheet( next_steps_label.setStyleSheet(
"QLabel { " "QLabel { "
" font-size: 13px; " " font-size: 13px; "
" color: #b0b0b0; " " color: #b0b0b0; "
" line-height: 1.2; " " line-height: 1.4; "
" padding: 6px; " " padding: 6px; "
" background-color: transparent; " " background-color: transparent; "
" border-radius: 6px; " " border-radius: 6px; "
@@ -237,10 +222,17 @@ class SuccessDialog(QDialog):
Formatted next steps string Formatted next steps string
""" """
game_display = self.game_name or self.modlist_name game_display = self.game_name or self.modlist_name
base_message = ""
if self.workflow_type == "tuxborn": 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: 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): def _update_countdown(self):
if self._countdown > 0: if self._countdown > 0: