Sync from development - prepare for v0.2.2.2

This commit is contained in:
Omni
2026-01-28 22:13:51 +00:00
parent 286d51e6a1
commit 98a9a4c7c6
52 changed files with 688 additions and 442 deletions

View File

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

View File

@@ -892,6 +892,7 @@ class PathHandler:
# Extract existing gamePath to use as source of truth for vanilla game location
existing_game_path = None
gamepath_drive_letter = None
gamepath_line_index = -1
for i, line in enumerate(lines):
if re.match(r'^\s*gamepath\s*=.*@ByteArray\(([^)]+)\)', line, re.IGNORECASE):
@@ -899,11 +900,16 @@ class PathHandler:
if match:
raw_path = match.group(1)
gamepath_line_index = i
# Extract drive letter from gamePath (Z: or D:)
if raw_path.startswith('Z:'):
gamepath_drive_letter = 'Z:'
elif raw_path.startswith('D:'):
gamepath_drive_letter = 'D:'
# Convert Windows path back to Linux path
if raw_path.startswith(('Z:', 'D:')):
linux_path = raw_path[2:].replace('\\\\', '/').replace('\\', '/')
existing_game_path = linux_path
logger.debug(f"Extracted existing gamePath: {existing_game_path}")
logger.debug(f"Extracted existing gamePath: {existing_game_path}, drive letter: {gamepath_drive_letter}")
break
# Special handling for gamePath in three-true scenario (engine_installed + steamdeck + sdcard)
@@ -957,19 +963,33 @@ class PathHandler:
logger.error(f"Malformed binary line: {line}")
continue
key_part, value_part = parts
exe_name = os.path.basename(value_part).lower()
# Clean up malformed paths (quotes in wrong places, etc.)
cleaned_value = PathHandler._clean_malformed_binary_path(value_part)
exe_name = os.path.basename(cleaned_value).lower()
# SELECTIVE FILTERING: Only process target executables (script extenders, etc.)
if exe_name not in TARGET_EXECUTABLES_LOWER:
logger.debug(f"Skipping non-target executable: {exe_name}")
continue
drive_prefix = "D:" if modlist_sdcard else "Z:"
rel_path = None
# --- BEGIN: FULL PARITY LOGIC ---
if 'steamapps' in value_part:
idx = value_part.index('steamapps')
subpath = value_part[idx:].lstrip('/')
if 'steamapps' in cleaned_value:
# Vanilla game path detected - always rebuild to ensure correct format
if not gamepath_drive_letter:
logger.warning(f"Vanilla game path detected but gamePath drive letter not found. Skipping binary path update for: {exe_name}")
logger.warning("This may indicate jackify-engine already configured paths correctly, or gamePath is malformed.")
continue
# Check if path is malformed (has quotes or wrong structure)
is_malformed = '"' in cleaned_value or cleaned_value != value_part.strip().strip('"')
# Extract subpath from cleaned value (includes exe name)
idx = cleaned_value.index('steamapps')
subpath = cleaned_value[idx:].lstrip('/')
# Find correct Steam library
correct_steam_lib = None
for lib in steam_libraries:
# Check if the actual game folder exists in this library
@@ -979,39 +999,62 @@ class PathHandler:
if not correct_steam_lib and steam_libraries:
correct_steam_lib = steam_libraries[0]
if correct_steam_lib:
# Always rebuild path using gamePath drive letter to ensure correct format
drive_prefix = gamepath_drive_letter
if is_malformed:
logger.info(f"Fixing malformed binary path for {exe_name}: {value_part.strip()}")
logger.debug(f"Vanilla game path detected: Using drive letter from gamePath: {drive_prefix}")
new_binary_path = f"{drive_prefix}/{correct_steam_lib}/{subpath}".replace('\\', '/').replace('//', '/')
else:
logger.error("Could not determine correct Steam library for vanilla game path.")
continue
else:
# For modlist-relative paths (Stock Game, mods, etc.), use modlist location
drive_prefix = "D:" if modlist_sdcard else "Z:"
found_stock = None
for folder in STOCK_GAME_FOLDERS:
folder_pattern = f"/{folder}"
if folder_pattern in value_part:
idx = value_part.index(folder_pattern)
rel_path = value_part[idx:].lstrip('/')
if folder_pattern in cleaned_value:
idx = cleaned_value.index(folder_pattern)
rel_path = cleaned_value[idx:].lstrip('/')
found_stock = folder
break
if not rel_path:
mods_pattern = "/mods/"
if mods_pattern in value_part:
idx = value_part.index(mods_pattern)
rel_path = value_part[idx:].lstrip('/')
if mods_pattern in cleaned_value:
idx = cleaned_value.index(mods_pattern)
rel_path = cleaned_value[idx:].lstrip('/')
else:
rel_path = exe_name
processed_modlist_path = PathHandler._strip_sdcard_path_prefix(modlist_dir_path) if modlist_sdcard else str(modlist_dir_path)
new_binary_path = f"{drive_prefix}/{processed_modlist_path}/{rel_path}".replace('\\', '/').replace('//', '/')
formatted_binary_path = PathHandler._format_binary_for_mo2(new_binary_path)
# Ensure no quotes in formatted path (binary paths should never have quotes)
if '"' in formatted_binary_path:
logger.warning(f"Formatted binary path still contains quotes, removing: {formatted_binary_path}")
formatted_binary_path = formatted_binary_path.replace('"', '')
new_binary_line = f"{index}{backslash_style}binary = {formatted_binary_path}"
logger.debug(f"Updating binary path: {line.strip()} -> {new_binary_line}")
lines[i] = new_binary_line + "\n"
logger.info(f"Updating binary path: {line.strip()} -> {new_binary_line}")
# Preserve original line ending - lines from readlines() should have newline, but ensure it
original_line = lines[i]
if original_line.endswith('\n'):
lines[i] = new_binary_line + '\n'
else:
lines[i] = new_binary_line + '\n'
binary_paths_updated += 1
binary_paths_by_index[index] = formatted_binary_path
for j, wd_line, index, backslash_style in working_dir_lines:
if index in binary_paths_by_index:
binary_path = binary_paths_by_index[index]
wd_path = os.path.dirname(binary_path)
drive_prefix = "D:" if modlist_sdcard else "Z:"
# Derive drive letter from binary path, not modlist location
if binary_path.startswith("D:"):
drive_prefix = "D:"
elif binary_path.startswith("Z:"):
drive_prefix = "Z:"
else:
# Fallback: use modlist location if binary path doesn't have drive letter
drive_prefix = "D:" if modlist_sdcard else "Z:"
if wd_path.startswith("D:") or wd_path.startswith("Z:"):
wd_path = wd_path[2:]
wd_path = drive_prefix + wd_path
@@ -1019,7 +1062,12 @@ class PathHandler:
key_part = f"{index}{backslash_style}workingDirectory"
new_wd_line = f"{key_part} = {formatted_wd_path}"
logger.debug(f"Updating working directory: {wd_line.strip()} -> {new_wd_line}")
lines[j] = new_wd_line + "\n"
# Preserve original line ending - ensure newline is present
original_wd_line = lines[j]
if original_wd_line.endswith('\n'):
lines[j] = new_wd_line + '\n'
else:
lines[j] = new_wd_line + '\n'
working_dirs_updated += 1
with open(modlist_ini_path, 'w', encoding='utf-8') as f:
f.writelines(lines)
@@ -1141,6 +1189,33 @@ class PathHandler:
path = re.sub(r'^([A-Z]:)\\+', r'\1\\', path)
return path
@staticmethod
def _clean_malformed_binary_path(value_part: str) -> str:
"""
Clean up malformed binary paths from engine (e.g., quotes in wrong places).
Example: "Z:/path/to/game"/exe.exe -> Z:/path/to/game/exe.exe
"""
cleaned = value_part.strip()
# Remove quotes if they wrap only part of the path (malformed)
if cleaned.startswith('"') and '"' in cleaned[1:]:
# Find the closing quote
quote_end = cleaned.find('"', 1)
if quote_end > 0:
# Check if there's content after the quote (malformed)
after_quote = cleaned[quote_end + 1:].strip()
if after_quote.startswith('/') or after_quote:
# Malformed: quotes wrap only part of path
# Remove quotes and join
path_part = cleaned[1:quote_end]
remaining = after_quote.lstrip('/')
cleaned = f"{path_part}/{remaining}" if remaining else path_part
logger.info(f"Cleaned malformed binary path: {value_part} -> {cleaned}")
# Remove any remaining quotes (handles fully quoted paths too)
cleaned = cleaned.strip('"')
# Normalize slashes
cleaned = cleaned.replace('\\', '/')
return cleaned
@staticmethod
def _format_binary_for_mo2(path: str) -> str:
import re

View File

@@ -367,6 +367,13 @@ class ProtontricksHandler:
**kwargs # Allow overriding defaults (like stderr=DEVNULL)
}
# Log full command for advanced users to reproduce manually (debug mode only)
cmd_str = ' '.join(map(str, cmd))
logger.debug("=" * 80)
logger.debug("PROTONTRICKS COMMAND (for manual reproduction):")
logger.debug(f" {cmd_str}")
logger.debug("=" * 80)
# Handle environment: if env was passed in kwargs, merge it with our clean env
# Otherwise create a clean env from scratch
if 'env' in kwargs and kwargs['env']:

View File

@@ -19,6 +19,15 @@ from .subprocess_utils import get_clean_subprocess_env
# Initialize logger
logger = logging.getLogger(__name__)
# Known Valve Proton App ID -> config.vdf internal name mapping
VALVE_PROTON_APPID_MAP = {
'2805730': 'proton_9',
'3658110': 'proton_10',
'1493710': 'proton_experimental',
'2180100': 'proton_hotfix',
'1887720': 'proton_8',
}
class WineUtils:
"""
@@ -849,6 +858,155 @@ class WineUtils:
# Return only existing paths
return [path for path in compat_paths if path.exists()]
@staticmethod
def _parse_compat_tool_name(proton_dir: Path) -> Optional[str]:
"""Parse the Steam internal name from a compatibilitytool.vdf file.
The key under compat_tools is what Steam uses in config.vdf CompatToolMapping."""
vdf_path = proton_dir / "compatibilitytool.vdf"
if not vdf_path.exists():
return None
try:
with open(vdf_path, 'r', encoding='utf-8', errors='ignore') as f:
content = f.read()
match = re.search(r'"compat_tools"\s*\{[^{]*"([^"]+)"\s*(?://[^\n]*)?\s*\{', content, re.DOTALL)
if match:
return match.group(1)
except Exception as e:
logger.warning(f"Failed to parse {vdf_path}: {e}")
return None
@staticmethod
def _find_valve_proton_appid(proton_dir_name: str) -> Optional[str]:
"""Find the Steam App ID for a Valve Proton by matching appmanifest installdir."""
steam_libs = WineUtils.get_steam_library_paths()
for lib_path in steam_libs:
steamapps_dir = lib_path.parent
for manifest in steamapps_dir.glob("appmanifest_*.acf"):
try:
with open(manifest, 'r', encoding='utf-8', errors='ignore') as f:
content = f.read()
installdir_match = re.search(r'"installdir"\s+"([^"]+)"', content)
appid_match = re.search(r'"appid"\s+"(\d+)"', content)
if installdir_match and appid_match:
if installdir_match.group(1) == proton_dir_name:
return appid_match.group(1)
except Exception:
continue
return None
@staticmethod
def resolve_steam_compat_name(proton_path) -> Optional[str]:
"""Resolve the correct Steam config.vdf internal name for a Proton installation.
For third-party Protons (GE, CachyOS, etc.): parses compatibilitytool.vdf
For Valve Protons: maps via App ID from appmanifest files.
Args:
proton_path: Path to the Proton directory (str or Path)
Returns:
Internal name for config.vdf CompatToolMapping, or None if unresolvable
"""
proton_path = Path(proton_path)
if not proton_path.is_dir():
logger.warning(f"Proton path not found: {proton_path}")
return None
# Third-party Proton: check for compatibilitytool.vdf
compat_name = WineUtils._parse_compat_tool_name(proton_path)
if compat_name:
logger.debug(f"Resolved compat name from vdf: {proton_path.name} -> {compat_name}")
return compat_name
# Valve Proton: look up App ID from appmanifest, then map
dir_name = proton_path.name
appid = WineUtils._find_valve_proton_appid(dir_name)
if appid and appid in VALVE_PROTON_APPID_MAP:
name = VALVE_PROTON_APPID_MAP[appid]
logger.debug(f"Resolved Valve Proton: {dir_name} (AppID {appid}) -> {name}")
return name
# Fallback for GE-Proton dirs without a vdf (shouldn't happen, but safe)
if dir_name.startswith('GE-Proton'):
return dir_name
logger.warning(f"Could not resolve Steam compat name for: {proton_path}")
return None
@staticmethod
def scan_thirdparty_proton_versions() -> List[Dict[str, any]]:
"""Scan for non-GE third-party Proton versions in compatibilitytools.d directories.
Discovers CachyOS, TKG, and other community builds by parsing compatibilitytool.vdf.
Returns:
List of dicts with version info, sorted by name
"""
logger.info("Scanning for third-party Proton versions...")
found_versions = []
seen_names = set()
compat_paths = WineUtils.get_compatibility_tool_paths()
if not compat_paths:
return []
for compat_path in compat_paths:
try:
for proton_dir in compat_path.iterdir():
if not proton_dir.is_dir():
continue
dir_name = proton_dir.name
# Skip GE-Proton (handled by scan_ge_proton_versions)
if dir_name.startswith("GE-Proton"):
continue
# Must have a wine binary to be a usable Proton
wine_bin = proton_dir / "files" / "bin" / "wine"
if not wine_bin.exists():
continue
# Must have a compatibilitytool.vdf (proves it's a Proton compat tool)
compat_name = WineUtils._parse_compat_tool_name(proton_dir)
if not compat_name:
continue
# Skip non-Proton tools (e.g., LegacyRuntime)
vdf_path = proton_dir / "compatibilitytool.vdf"
try:
with open(vdf_path, 'r', encoding='utf-8', errors='ignore') as f:
vdf_content = f.read()
if '"from_oslist" "linux"' in vdf_content:
continue
except Exception:
pass
# Skip Proton Hotfix
if 'hotfix' in compat_name.lower():
continue
if compat_name in seen_names:
continue
seen_names.add(compat_name)
found_versions.append({
'name': dir_name,
'path': proton_dir,
'wine_bin': wine_bin,
'priority': 175,
'type': 'ThirdParty-Proton',
'steam_compat_name': compat_name,
})
logger.debug(f"Found third-party Proton: {dir_name} (compat name: {compat_name})")
except Exception as e:
logger.warning(f"Error scanning {compat_path}: {e}")
logger.info(f"Found {len(found_versions)} third-party Proton version(s)")
return found_versions
@staticmethod
def scan_ge_proton_versions() -> List[Dict[str, any]]:
"""
@@ -895,6 +1053,7 @@ class WineUtils:
# Priority format: 200 (base) + major*10 + minor (e.g., 200 + 100 + 16 = 316)
priority = 200 + (major_ver * 10) + minor_ver
compat_name = WineUtils._parse_compat_tool_name(proton_dir) or dir_name
found_versions.append({
'name': dir_name,
'path': proton_dir,
@@ -902,7 +1061,8 @@ class WineUtils:
'priority': priority,
'major_version': major_ver,
'minor_version': minor_ver,
'type': 'GE-Proton'
'type': 'GE-Proton',
'steam_compat_name': compat_name,
})
logger.debug(f"Found {dir_name} at {proton_dir} (priority: {priority})")
else:
@@ -951,12 +1111,14 @@ class WineUtils:
wine_bin = proton_path / "files" / "bin" / "wine"
if wine_bin.exists() and wine_bin.is_file():
compat_name = WineUtils.resolve_steam_compat_name(proton_path)
found_versions.append({
'name': version_name,
'path': proton_path,
'wine_bin': wine_bin,
'priority': priority,
'type': 'Valve-Proton'
'type': 'Valve-Proton',
'steam_compat_name': compat_name,
})
logger.debug(f"Found {version_name} at {proton_path}")
@@ -998,6 +1160,10 @@ class WineUtils:
ge_versions = WineUtils.scan_ge_proton_versions()
all_versions.extend(ge_versions)
# Scan third-party Proton versions (CachyOS, TKG, etc.)
thirdparty_versions = WineUtils.scan_thirdparty_proton_versions()
all_versions.extend(thirdparty_versions)
# Scan Valve Proton versions
valve_versions = WineUtils.scan_valve_proton_versions()
all_versions.extend(valve_versions)
@@ -1025,6 +1191,7 @@ class WineUtils:
def select_best_proton() -> Optional[Dict[str, any]]:
"""
Select the best available Proton version (GE-Proton or Valve Proton) using unified precedence.
Excludes third-party builds (CachyOS, etc.) which may have compatibility issues.
Returns:
Dict with version info for the best Proton, or None if none found
@@ -1035,8 +1202,16 @@ class WineUtils:
logger.warning("No compatible Proton versions found")
return None
# Filter out third-party Protons - they may have compatibility issues with component installation
# Only include GE-Proton and Valve-Proton types
compatible_versions = [v for v in available_versions if v.get('type') in ('GE-Proton', 'Valve-Proton')]
if not compatible_versions:
logger.warning("No compatible Proton versions found (only third-party builds available)")
return None
# Return the highest priority version (first in sorted list)
best_version = available_versions[0]
best_version = compatible_versions[0]
logger.info(f"Selected best Proton version: {best_version['name']} ({best_version['type']})")
return best_version
@@ -1079,11 +1254,11 @@ class WineUtils:
if best_proton:
# Compatible Proton found
proton_type = best_proton.get('type', 'Unknown')
status_msg = f" Using {best_proton['name']} ({proton_type}) for this workflow"
status_msg = f"[OK] Using {best_proton['name']} ({proton_type}) for this workflow"
logger.info(f"Proton requirements satisfied: {best_proton['name']} ({proton_type})")
return True, status_msg, best_proton
else:
# No compatible Proton found
status_msg = " No compatible Proton version found (GE-Proton 10+, Proton 9+, 10, or Experimental required)"
status_msg = "[FAIL] No compatible Proton version found (GE-Proton 10+, Proton 9+, 10, or Experimental required)"
logger.warning("Proton requirements not met - no compatible version found")
return False, status_msg, None

View File

@@ -484,7 +484,16 @@ class WinetricksHandler:
# Build winetricks command - using --unattended for silent installation
cmd = [self.winetricks_path, '--unattended'] + components_to_install
self.logger.debug(f"Running: {' '.join(cmd)}")
# Log full command for advanced users to reproduce manually (debug mode only)
cmd_str = ' '.join(cmd)
self.logger.debug("=" * 80)
self.logger.debug("WINETRICKS COMMAND (for manual reproduction):")
self.logger.debug(f" {cmd_str}")
self.logger.debug("")
self.logger.debug("Environment variables required:")
self.logger.debug(f" WINEPREFIX={env.get('WINEPREFIX', 'NOT SET')}")
self.logger.debug(f" WINE={env.get('WINE', 'NOT SET')}")
self.logger.debug("=" * 80)
# Enhanced diagnostics for bundled winetricks
self.logger.debug("=== Winetricks Environment Diagnostics ===")
@@ -567,8 +576,31 @@ class WinetricksHandler:
self.logger.error("")
self.logger.error("STDERR:")
if result.stderr.strip():
# Filter out verbose winetricks "Executing..." messages - these are informational, not errors
error_lines = []
verbose_lines = []
for line in result.stderr.strip().split('\n'):
self.logger.error(f" {line}")
line_lower = line.lower().strip()
# Skip verbose informational messages
if (line_lower.startswith('executing ') or
(line_lower.startswith('grep: warning:') and 'stray' in line_lower) or
('warning; possible' in line_lower and 'extra bytes' in line_lower)):
# These are verbose info messages, log at debug level instead
verbose_lines.append(line)
else:
# Actual error/warning messages (including "returned status", "aborting", dbus errors, etc.)
error_lines.append(line)
if error_lines:
self.logger.error(" Actual errors/warnings:")
for line in error_lines:
self.logger.error(f" {line}")
if verbose_lines:
self.logger.debug(f" ({len(verbose_lines)} verbose 'Executing...' lines suppressed - see debug log for details)")
else:
self.logger.error(" (only verbose output, no actual errors)")
if verbose_lines:
self.logger.debug(f" ({len(verbose_lines)} verbose lines suppressed)")
else:
self.logger.error(" (empty)")
self.logger.error("=" * 80)

View File

@@ -41,60 +41,32 @@ class AutomatedPrefixService:
from jackify.shared.timing import get_timestamp
return get_timestamp()
def _get_user_proton_version(self, modlist_name: str = None):
"""Get user's preferred Proton version from config, with fallback to auto-detection
Args:
modlist_name: Optional modlist name for special handling (e.g., Lorerim)
"""
def _get_user_proton_version(self):
"""Get user's preferred Proton version from config, with fallback to auto-detection."""
try:
from jackify.backend.handlers.config_handler import ConfigHandler
from jackify.backend.handlers.wine_utils import WineUtils
# Check for Lorerim-specific Proton override first
modlist_normalized = modlist_name.lower().replace(" ", "") if modlist_name else ""
if modlist_normalized == 'lorerim':
lorerim_proton = self._get_lorerim_preferred_proton()
if lorerim_proton:
logger.info(f"Lorerim detected: Using {lorerim_proton} instead of user settings")
self._store_proton_override_notification("Lorerim", lorerim_proton)
return lorerim_proton
# Check for Lost Legacy-specific Proton override (needs Proton 9 for ENB compatibility)
if modlist_normalized == 'lostlegacy':
lostlegacy_proton = self._get_lorerim_preferred_proton() # Use same logic as Lorerim
if lostlegacy_proton:
logger.info(f"Lost Legacy detected: Using {lostlegacy_proton} instead of user settings (ENB compatibility)")
self._store_proton_override_notification("Lost Legacy", lostlegacy_proton)
return lostlegacy_proton
config_handler = ConfigHandler()
user_proton_path = config_handler.get_game_proton_path()
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()
logger.info("User selected auto-detect, using GE-Proton -> Experimental -> Proton precedence")
best = WineUtils.select_best_proton()
if best:
return best.get('steam_compat_name') or WineUtils.resolve_steam_compat_name(best['path'])
return "proton_experimental"
else:
# User has selected a specific Proton version
# Use the exact directory name for Steam config.vdf
try:
proton_version = os.path.basename(user_proton_path)
# GE-Proton uses exact directory name, Valve Proton needs lowercase conversion
if proton_version.startswith('GE-Proton'):
# Keep GE-Proton name exactly as-is
steam_proton_name = proton_version
else:
# Convert Valve Proton names to Steam's format
steam_proton_name = proton_version.lower().replace(' - ', '_').replace(' ', '_').replace('-', '_')
if not steam_proton_name.startswith('proton'):
steam_proton_name = f"proton_{steam_proton_name}"
steam_proton_name = WineUtils.resolve_steam_compat_name(user_proton_path)
if steam_proton_name:
logger.info(f"Using user-selected Proton: {steam_proton_name}")
return steam_proton_name
except Exception as e:
logger.warning(f"Invalid user Proton path '{user_proton_path}', falling back to auto: {e}")
return WineUtils.select_best_proton()
logger.warning(f"Could not resolve compat name for '{user_proton_path}', falling back to auto")
best = WineUtils.select_best_proton()
if best:
return best.get('steam_compat_name') or WineUtils.resolve_steam_compat_name(best['path'])
return "proton_experimental"
except Exception as e:
logger.error(f"Failed to get user Proton preference, using default: {e}")
@@ -148,8 +120,7 @@ class AutomatedPrefixService:
logger.warning(f"Could not generate STEAM_COMPAT_MOUNTS, using default: {e}")
launch_options = "%command%"
# Get user's preferred Proton version (with Lorerim-specific override)
proton_version = self._get_user_proton_version(shortcut_name)
proton_version = self._get_user_proton_version()
# Create shortcut with Proton using native service
success, app_id = steam_service.create_shortcut_with_proton(
@@ -1619,8 +1590,6 @@ echo Prefix creation complete.
if progress_callback:
progress_callback(f"{self._get_progress_timestamp()} Steam Configuration complete!")
# Show Proton override notification if applicable
self._show_proton_override_notification(progress_callback)
logger.info(" Simple automated prefix creation workflow completed successfully")
return True, prefix_path, actual_appid
@@ -1960,9 +1929,6 @@ echo Prefix creation complete.
progress_callback(f"{last_timestamp} Steam integration complete")
progress_callback("") # Blank line after Steam integration complete
# Show Proton override notification if applicable
self._show_proton_override_notification(progress_callback)
if progress_callback:
progress_callback("") # Extra blank line to span across Configuration Summary
progress_callback("") # And one more to create space before Prefix Configuration
@@ -2800,7 +2766,7 @@ echo Prefix creation complete.
platform_service = PlatformDetectionService.get_instance()
is_steamdeck_sdcard = (platform_service.is_steamdeck and
str(proton_path).startswith('/run/media/'))
timeout = 180 if is_steamdeck_sdcard else 60
timeout = 180 if is_steamdeck_sdcard else 120
if is_steamdeck_sdcard:
logger.info(f"Using extended timeout ({timeout}s) for Steam Deck SD card Proton installation")
@@ -3364,91 +3330,5 @@ echo Prefix creation complete.
if created_count > 0:
logger.info(f"Created {created_count} user directories for {game_dir_name}")
def _get_lorerim_preferred_proton(self):
"""Get Lorerim's preferred Proton 9 version with specific priority order"""
try:
from jackify.backend.handlers.wine_utils import WineUtils
# Get all available Proton versions
available_versions = WineUtils.scan_all_proton_versions()
if not available_versions:
logger.warning("No Proton versions found for Lorerim override")
return None
# Priority order for Lorerim:
# 1. GEProton9-27 (specific version)
# 2. Other GEProton-9 versions (latest first)
# 3. Valve Proton 9 (any version)
preferred_candidates = []
for version in available_versions:
version_name = version['name']
# Priority 1: GEProton9-27 specifically
if version_name == 'GE-Proton9-27':
logger.info(f"Lorerim: Found preferred GE-Proton9-27")
return version_name
# Priority 2: Other GE-Proton 9 versions
elif version_name.startswith('GE-Proton9-'):
preferred_candidates.append(('ge_proton_9', version_name, version))
# Priority 3: Valve Proton 9
elif 'Proton 9' in version_name:
preferred_candidates.append(('valve_proton_9', version_name, version))
# Return best candidate if any found
if preferred_candidates:
# Sort by priority (GE-Proton first, then by name for latest)
preferred_candidates.sort(key=lambda x: (x[0], x[1]), reverse=True)
best_candidate = preferred_candidates[0]
logger.info(f"Lorerim: Selected {best_candidate[1]} as best Proton 9 option")
return best_candidate[1]
logger.warning("Lorerim: No suitable Proton 9 versions found, will use user settings")
return None
except Exception as e:
logger.error(f"Error detecting Lorerim Proton preference: {e}")
return None
def _store_proton_override_notification(self, modlist_name: str, proton_version: str):
"""Store Proton override information for end-of-install notification"""
try:
# Store override info for later display
if not hasattr(self, '_proton_overrides'):
self._proton_overrides = []
self._proton_overrides.append({
'modlist': modlist_name,
'proton_version': proton_version,
'reason': f'{modlist_name} requires Proton 9 for optimal compatibility'
})
logger.debug(f"Stored Proton override notification: {modlist_name}{proton_version}")
except Exception as e:
logger.error(f"Failed to store Proton override notification: {e}")
def _show_proton_override_notification(self, progress_callback=None):
"""Display any Proton override notifications to the user"""
try:
if hasattr(self, '_proton_overrides') and self._proton_overrides:
for override in self._proton_overrides:
notification_msg = f"PROTON OVERRIDE: {override['modlist']} configured to use {override['proton_version']} for optimal compatibility"
if progress_callback:
progress_callback("")
progress_callback(f"{self._get_progress_timestamp()} {notification_msg}")
logger.info(notification_msg)
# Clear notifications after display
self._proton_overrides = []
except Exception as e:
logger.error(f"Failed to show Proton override notification: {e}")

View File

@@ -485,24 +485,23 @@ class NativeSteamService:
if proton_version is None:
try:
from jackify.backend.handlers.config_handler import ConfigHandler
from jackify.backend.handlers.wine_utils import WineUtils
config_handler = ConfigHandler()
game_proton_path = config_handler.get_game_proton_path()
if game_proton_path and game_proton_path != 'auto':
# User has selected Game Proton - use it
proton_version = os.path.basename(game_proton_path)
# Convert to Steam format
if not proton_version.startswith('GE-Proton'):
proton_version = proton_version.lower().replace(' - ', '_').replace(' ', '_').replace('-', '_')
if not proton_version.startswith('proton'):
proton_version = f"proton_{proton_version}"
logger.info(f"Using Game Proton from settings: {proton_version}")
else:
# Fallback to auto-detect if Game Proton not set
from jackify.backend.handlers.wine_utils import WineUtils
resolved = WineUtils.resolve_steam_compat_name(game_proton_path)
if resolved:
proton_version = resolved
logger.info(f"Using Game Proton from settings: {proton_version}")
else:
logger.warning(f"Could not resolve compat name for '{game_proton_path}', falling back to auto")
game_proton_path = None
if not game_proton_path or game_proton_path == 'auto':
best_proton = WineUtils.select_best_proton()
if best_proton:
proton_version = best_proton['name']
proton_version = best_proton.get('steam_compat_name') or WineUtils.resolve_steam_compat_name(best_proton['path'])
logger.info(f"Auto-detected Game Proton: {proton_version}")
else:
proton_version = "proton_experimental"

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -7,7 +7,7 @@
"targets": {
".NETCoreApp,Version=v8.0": {},
".NETCoreApp,Version=v8.0/linux-x64": {
"jackify-engine/0.4.6": {
"jackify-engine/0.4.7": {
"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.6",
"Wabbajack.Downloaders.Bethesda": "0.4.6",
"Wabbajack.Downloaders.Dispatcher": "0.4.6",
"Wabbajack.Hashing.xxHash64": "0.4.6",
"Wabbajack.Networking.Discord": "0.4.6",
"Wabbajack.Networking.GitHub": "0.4.6",
"Wabbajack.Paths.IO": "0.4.6",
"Wabbajack.Server.Lib": "0.4.6",
"Wabbajack.Services.OSIntegrated": "0.4.6",
"Wabbajack.VFS": "0.4.6",
"Wabbajack.CLI.Builder": "0.4.7",
"Wabbajack.Downloaders.Bethesda": "0.4.7",
"Wabbajack.Downloaders.Dispatcher": "0.4.7",
"Wabbajack.Hashing.xxHash64": "0.4.7",
"Wabbajack.Networking.Discord": "0.4.7",
"Wabbajack.Networking.GitHub": "0.4.7",
"Wabbajack.Paths.IO": "0.4.7",
"Wabbajack.Server.Lib": "0.4.7",
"Wabbajack.Services.OSIntegrated": "0.4.7",
"Wabbajack.VFS": "0.4.7",
"MegaApiClient": "1.0.0.0",
"runtimepack.Microsoft.NETCore.App.Runtime.linux-x64": "8.0.22"
},
@@ -1781,7 +1781,7 @@
}
}
},
"Wabbajack.CLI.Builder/0.4.6": {
"Wabbajack.CLI.Builder/0.4.7": {
"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.6"
"Wabbajack.Paths": "0.4.7"
},
"runtime": {
"Wabbajack.CLI.Builder.dll": {}
}
},
"Wabbajack.Common/0.4.6": {
"Wabbajack.Common/0.4.7": {
"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.6",
"Wabbajack.Networking.Http": "0.4.6",
"Wabbajack.Paths.IO": "0.4.6"
"Wabbajack.DTOs": "0.4.7",
"Wabbajack.Networking.Http": "0.4.7",
"Wabbajack.Paths.IO": "0.4.7"
},
"runtime": {
"Wabbajack.Common.dll": {}
}
},
"Wabbajack.Compiler/0.4.6": {
"Wabbajack.Compiler/0.4.7": {
"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.6",
"Wabbajack.Installer": "0.4.6",
"Wabbajack.VFS": "0.4.6",
"Wabbajack.Downloaders.Dispatcher": "0.4.7",
"Wabbajack.Installer": "0.4.7",
"Wabbajack.VFS": "0.4.7",
"ini-parser-netstandard": "2.5.2"
},
"runtime": {
"Wabbajack.Compiler.dll": {}
}
},
"Wabbajack.Compression.BSA/0.4.6": {
"Wabbajack.Compression.BSA/0.4.7": {
"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.6",
"Wabbajack.DTOs": "0.4.6"
"Wabbajack.Common": "0.4.7",
"Wabbajack.DTOs": "0.4.7"
},
"runtime": {
"Wabbajack.Compression.BSA.dll": {}
}
},
"Wabbajack.Compression.Zip/0.4.6": {
"Wabbajack.Compression.Zip/0.4.7": {
"dependencies": {
"Wabbajack.IO.Async": "0.4.6"
"Wabbajack.IO.Async": "0.4.7"
},
"runtime": {
"Wabbajack.Compression.Zip.dll": {}
}
},
"Wabbajack.Configuration/0.4.6": {
"Wabbajack.Configuration/0.4.7": {
"runtime": {
"Wabbajack.Configuration.dll": {}
}
},
"Wabbajack.Downloaders.Bethesda/0.4.6": {
"Wabbajack.Downloaders.Bethesda/0.4.7": {
"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.6",
"Wabbajack.Downloaders.Interfaces": "0.4.6",
"Wabbajack.Networking.BethesdaNet": "0.4.6"
"Wabbajack.Common": "0.4.7",
"Wabbajack.Downloaders.Interfaces": "0.4.7",
"Wabbajack.Networking.BethesdaNet": "0.4.7"
},
"runtime": {
"Wabbajack.Downloaders.Bethesda.dll": {}
}
},
"Wabbajack.Downloaders.Dispatcher/0.4.6": {
"Wabbajack.Downloaders.Dispatcher/0.4.7": {
"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.6",
"Wabbajack.Downloaders.GameFile": "0.4.6",
"Wabbajack.Downloaders.GoogleDrive": "0.4.6",
"Wabbajack.Downloaders.Http": "0.4.6",
"Wabbajack.Downloaders.IPS4OAuth2Downloader": "0.4.6",
"Wabbajack.Downloaders.Interfaces": "0.4.6",
"Wabbajack.Downloaders.Manual": "0.4.6",
"Wabbajack.Downloaders.MediaFire": "0.4.6",
"Wabbajack.Downloaders.Mega": "0.4.6",
"Wabbajack.Downloaders.ModDB": "0.4.6",
"Wabbajack.Downloaders.Nexus": "0.4.6",
"Wabbajack.Downloaders.VerificationCache": "0.4.6",
"Wabbajack.Downloaders.WabbajackCDN": "0.4.6",
"Wabbajack.Networking.WabbajackClientApi": "0.4.6"
"Wabbajack.Downloaders.Bethesda": "0.4.7",
"Wabbajack.Downloaders.GameFile": "0.4.7",
"Wabbajack.Downloaders.GoogleDrive": "0.4.7",
"Wabbajack.Downloaders.Http": "0.4.7",
"Wabbajack.Downloaders.IPS4OAuth2Downloader": "0.4.7",
"Wabbajack.Downloaders.Interfaces": "0.4.7",
"Wabbajack.Downloaders.Manual": "0.4.7",
"Wabbajack.Downloaders.MediaFire": "0.4.7",
"Wabbajack.Downloaders.Mega": "0.4.7",
"Wabbajack.Downloaders.ModDB": "0.4.7",
"Wabbajack.Downloaders.Nexus": "0.4.7",
"Wabbajack.Downloaders.VerificationCache": "0.4.7",
"Wabbajack.Downloaders.WabbajackCDN": "0.4.7",
"Wabbajack.Networking.WabbajackClientApi": "0.4.7"
},
"runtime": {
"Wabbajack.Downloaders.Dispatcher.dll": {}
}
},
"Wabbajack.Downloaders.GameFile/0.4.6": {
"Wabbajack.Downloaders.GameFile/0.4.7": {
"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.6",
"Wabbajack.VFS": "0.4.6"
"Wabbajack.Downloaders.Interfaces": "0.4.7",
"Wabbajack.VFS": "0.4.7"
},
"runtime": {
"Wabbajack.Downloaders.GameFile.dll": {}
}
},
"Wabbajack.Downloaders.GoogleDrive/0.4.6": {
"Wabbajack.Downloaders.GoogleDrive/0.4.7": {
"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.6",
"Wabbajack.DTOs": "0.4.6",
"Wabbajack.Downloaders.Interfaces": "0.4.6",
"Wabbajack.Networking.Http": "0.4.6",
"Wabbajack.Networking.Http.Interfaces": "0.4.6"
"Wabbajack.Common": "0.4.7",
"Wabbajack.DTOs": "0.4.7",
"Wabbajack.Downloaders.Interfaces": "0.4.7",
"Wabbajack.Networking.Http": "0.4.7",
"Wabbajack.Networking.Http.Interfaces": "0.4.7"
},
"runtime": {
"Wabbajack.Downloaders.GoogleDrive.dll": {}
}
},
"Wabbajack.Downloaders.Http/0.4.6": {
"Wabbajack.Downloaders.Http/0.4.7": {
"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.6",
"Wabbajack.DTOs": "0.4.6",
"Wabbajack.Downloaders.Interfaces": "0.4.6",
"Wabbajack.Networking.BethesdaNet": "0.4.6",
"Wabbajack.Networking.Http.Interfaces": "0.4.6",
"Wabbajack.Paths.IO": "0.4.6"
"Wabbajack.Common": "0.4.7",
"Wabbajack.DTOs": "0.4.7",
"Wabbajack.Downloaders.Interfaces": "0.4.7",
"Wabbajack.Networking.BethesdaNet": "0.4.7",
"Wabbajack.Networking.Http.Interfaces": "0.4.7",
"Wabbajack.Paths.IO": "0.4.7"
},
"runtime": {
"Wabbajack.Downloaders.Http.dll": {}
}
},
"Wabbajack.Downloaders.Interfaces/0.4.6": {
"Wabbajack.Downloaders.Interfaces/0.4.7": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Wabbajack.Compression.Zip": "0.4.6",
"Wabbajack.DTOs": "0.4.6",
"Wabbajack.Paths.IO": "0.4.6"
"Wabbajack.Compression.Zip": "0.4.7",
"Wabbajack.DTOs": "0.4.7",
"Wabbajack.Paths.IO": "0.4.7"
},
"runtime": {
"Wabbajack.Downloaders.Interfaces.dll": {}
}
},
"Wabbajack.Downloaders.IPS4OAuth2Downloader/0.4.6": {
"Wabbajack.Downloaders.IPS4OAuth2Downloader/0.4.7": {
"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.6",
"Wabbajack.Downloaders.Interfaces": "0.4.6",
"Wabbajack.Networking.Http": "0.4.6",
"Wabbajack.Networking.Http.Interfaces": "0.4.6"
"Wabbajack.Common": "0.4.7",
"Wabbajack.Downloaders.Interfaces": "0.4.7",
"Wabbajack.Networking.Http": "0.4.7",
"Wabbajack.Networking.Http.Interfaces": "0.4.7"
},
"runtime": {
"Wabbajack.Downloaders.IPS4OAuth2Downloader.dll": {}
}
},
"Wabbajack.Downloaders.Manual/0.4.6": {
"Wabbajack.Downloaders.Manual/0.4.7": {
"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.6",
"Wabbajack.Downloaders.Interfaces": "0.4.6"
"Wabbajack.Common": "0.4.7",
"Wabbajack.Downloaders.Interfaces": "0.4.7"
},
"runtime": {
"Wabbajack.Downloaders.Manual.dll": {}
}
},
"Wabbajack.Downloaders.MediaFire/0.4.6": {
"Wabbajack.Downloaders.MediaFire/0.4.7": {
"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.6",
"Wabbajack.Downloaders.Interfaces": "0.4.6",
"Wabbajack.Networking.Http.Interfaces": "0.4.6"
"Wabbajack.Common": "0.4.7",
"Wabbajack.Downloaders.Interfaces": "0.4.7",
"Wabbajack.Networking.Http.Interfaces": "0.4.7"
},
"runtime": {
"Wabbajack.Downloaders.MediaFire.dll": {}
}
},
"Wabbajack.Downloaders.Mega/0.4.6": {
"Wabbajack.Downloaders.Mega/0.4.7": {
"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.6",
"Wabbajack.Downloaders.Interfaces": "0.4.6",
"Wabbajack.Paths.IO": "0.4.6"
"Wabbajack.Common": "0.4.7",
"Wabbajack.Downloaders.Interfaces": "0.4.7",
"Wabbajack.Paths.IO": "0.4.7"
},
"runtime": {
"Wabbajack.Downloaders.Mega.dll": {}
}
},
"Wabbajack.Downloaders.ModDB/0.4.6": {
"Wabbajack.Downloaders.ModDB/0.4.7": {
"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.6",
"Wabbajack.Downloaders.Interfaces": "0.4.6",
"Wabbajack.Networking.Http": "0.4.6",
"Wabbajack.Networking.Http.Interfaces": "0.4.6"
"Wabbajack.Common": "0.4.7",
"Wabbajack.Downloaders.Interfaces": "0.4.7",
"Wabbajack.Networking.Http": "0.4.7",
"Wabbajack.Networking.Http.Interfaces": "0.4.7"
},
"runtime": {
"Wabbajack.Downloaders.ModDB.dll": {}
}
},
"Wabbajack.Downloaders.Nexus/0.4.6": {
"Wabbajack.Downloaders.Nexus/0.4.7": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Wabbajack.DTOs": "0.4.6",
"Wabbajack.Downloaders.Interfaces": "0.4.6",
"Wabbajack.Hashing.xxHash64": "0.4.6",
"Wabbajack.Networking.Http": "0.4.6",
"Wabbajack.Networking.Http.Interfaces": "0.4.6",
"Wabbajack.Networking.NexusApi": "0.4.6",
"Wabbajack.Paths": "0.4.6"
"Wabbajack.DTOs": "0.4.7",
"Wabbajack.Downloaders.Interfaces": "0.4.7",
"Wabbajack.Hashing.xxHash64": "0.4.7",
"Wabbajack.Networking.Http": "0.4.7",
"Wabbajack.Networking.Http.Interfaces": "0.4.7",
"Wabbajack.Networking.NexusApi": "0.4.7",
"Wabbajack.Paths": "0.4.7"
},
"runtime": {
"Wabbajack.Downloaders.Nexus.dll": {}
}
},
"Wabbajack.Downloaders.VerificationCache/0.4.6": {
"Wabbajack.Downloaders.VerificationCache/0.4.7": {
"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.6",
"Wabbajack.Paths.IO": "0.4.6"
"Wabbajack.DTOs": "0.4.7",
"Wabbajack.Paths.IO": "0.4.7"
},
"runtime": {
"Wabbajack.Downloaders.VerificationCache.dll": {}
}
},
"Wabbajack.Downloaders.WabbajackCDN/0.4.6": {
"Wabbajack.Downloaders.WabbajackCDN/0.4.7": {
"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.6",
"Wabbajack.Downloaders.Interfaces": "0.4.6",
"Wabbajack.Networking.Http": "0.4.6",
"Wabbajack.RateLimiter": "0.4.6"
"Wabbajack.Common": "0.4.7",
"Wabbajack.Downloaders.Interfaces": "0.4.7",
"Wabbajack.Networking.Http": "0.4.7",
"Wabbajack.RateLimiter": "0.4.7"
},
"runtime": {
"Wabbajack.Downloaders.WabbajackCDN.dll": {}
}
},
"Wabbajack.DTOs/0.4.6": {
"Wabbajack.DTOs/0.4.7": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Wabbajack.Hashing.xxHash64": "0.4.6",
"Wabbajack.Paths": "0.4.6"
"Wabbajack.Hashing.xxHash64": "0.4.7",
"Wabbajack.Paths": "0.4.7"
},
"runtime": {
"Wabbajack.DTOs.dll": {}
}
},
"Wabbajack.FileExtractor/0.4.6": {
"Wabbajack.FileExtractor/0.4.7": {
"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.6",
"Wabbajack.Compression.BSA": "0.4.6",
"Wabbajack.Hashing.PHash": "0.4.6",
"Wabbajack.Paths": "0.4.6"
"Wabbajack.Common": "0.4.7",
"Wabbajack.Compression.BSA": "0.4.7",
"Wabbajack.Hashing.PHash": "0.4.7",
"Wabbajack.Paths": "0.4.7"
},
"runtime": {
"Wabbajack.FileExtractor.dll": {}
}
},
"Wabbajack.Hashing.PHash/0.4.6": {
"Wabbajack.Hashing.PHash/0.4.7": {
"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.6",
"Wabbajack.DTOs": "0.4.6",
"Wabbajack.Paths": "0.4.6",
"Wabbajack.Paths.IO": "0.4.6"
"Wabbajack.Common": "0.4.7",
"Wabbajack.DTOs": "0.4.7",
"Wabbajack.Paths": "0.4.7",
"Wabbajack.Paths.IO": "0.4.7"
},
"runtime": {
"Wabbajack.Hashing.PHash.dll": {}
}
},
"Wabbajack.Hashing.xxHash64/0.4.6": {
"Wabbajack.Hashing.xxHash64/0.4.7": {
"dependencies": {
"Wabbajack.Paths": "0.4.6",
"Wabbajack.RateLimiter": "0.4.6"
"Wabbajack.Paths": "0.4.7",
"Wabbajack.RateLimiter": "0.4.7"
},
"runtime": {
"Wabbajack.Hashing.xxHash64.dll": {}
}
},
"Wabbajack.Installer/0.4.6": {
"Wabbajack.Installer/0.4.7": {
"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.6",
"Wabbajack.Downloaders.Dispatcher": "0.4.6",
"Wabbajack.Downloaders.GameFile": "0.4.6",
"Wabbajack.FileExtractor": "0.4.6",
"Wabbajack.Networking.WabbajackClientApi": "0.4.6",
"Wabbajack.Paths": "0.4.6",
"Wabbajack.Paths.IO": "0.4.6",
"Wabbajack.VFS": "0.4.6",
"Wabbajack.DTOs": "0.4.7",
"Wabbajack.Downloaders.Dispatcher": "0.4.7",
"Wabbajack.Downloaders.GameFile": "0.4.7",
"Wabbajack.FileExtractor": "0.4.7",
"Wabbajack.Networking.WabbajackClientApi": "0.4.7",
"Wabbajack.Paths": "0.4.7",
"Wabbajack.Paths.IO": "0.4.7",
"Wabbajack.VFS": "0.4.7",
"ini-parser-netstandard": "2.5.2"
},
"runtime": {
"Wabbajack.Installer.dll": {}
}
},
"Wabbajack.IO.Async/0.4.6": {
"Wabbajack.IO.Async/0.4.7": {
"runtime": {
"Wabbajack.IO.Async.dll": {}
}
},
"Wabbajack.Networking.BethesdaNet/0.4.6": {
"Wabbajack.Networking.BethesdaNet/0.4.7": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Wabbajack.DTOs": "0.4.6",
"Wabbajack.Networking.Http": "0.4.6",
"Wabbajack.Networking.Http.Interfaces": "0.4.6"
"Wabbajack.DTOs": "0.4.7",
"Wabbajack.Networking.Http": "0.4.7",
"Wabbajack.Networking.Http.Interfaces": "0.4.7"
},
"runtime": {
"Wabbajack.Networking.BethesdaNet.dll": {}
}
},
"Wabbajack.Networking.Discord/0.4.6": {
"Wabbajack.Networking.Discord/0.4.7": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"Wabbajack.Networking.Http.Interfaces": "0.4.6"
"Wabbajack.Networking.Http.Interfaces": "0.4.7"
},
"runtime": {
"Wabbajack.Networking.Discord.dll": {}
}
},
"Wabbajack.Networking.GitHub/0.4.6": {
"Wabbajack.Networking.GitHub/0.4.7": {
"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.6",
"Wabbajack.Networking.Http.Interfaces": "0.4.6"
"Wabbajack.DTOs": "0.4.7",
"Wabbajack.Networking.Http.Interfaces": "0.4.7"
},
"runtime": {
"Wabbajack.Networking.GitHub.dll": {}
}
},
"Wabbajack.Networking.Http/0.4.6": {
"Wabbajack.Networking.Http/0.4.7": {
"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.6",
"Wabbajack.Downloaders.Interfaces": "0.4.6",
"Wabbajack.Hashing.xxHash64": "0.4.6",
"Wabbajack.Networking.Http.Interfaces": "0.4.6",
"Wabbajack.Paths": "0.4.6",
"Wabbajack.Paths.IO": "0.4.6"
"Wabbajack.Configuration": "0.4.7",
"Wabbajack.Downloaders.Interfaces": "0.4.7",
"Wabbajack.Hashing.xxHash64": "0.4.7",
"Wabbajack.Networking.Http.Interfaces": "0.4.7",
"Wabbajack.Paths": "0.4.7",
"Wabbajack.Paths.IO": "0.4.7"
},
"runtime": {
"Wabbajack.Networking.Http.dll": {}
}
},
"Wabbajack.Networking.Http.Interfaces/0.4.6": {
"Wabbajack.Networking.Http.Interfaces/0.4.7": {
"dependencies": {
"Wabbajack.Hashing.xxHash64": "0.4.6"
"Wabbajack.Hashing.xxHash64": "0.4.7"
},
"runtime": {
"Wabbajack.Networking.Http.Interfaces.dll": {}
}
},
"Wabbajack.Networking.NexusApi/0.4.6": {
"Wabbajack.Networking.NexusApi/0.4.7": {
"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.6",
"Wabbajack.Networking.Http": "0.4.6",
"Wabbajack.Networking.Http.Interfaces": "0.4.6",
"Wabbajack.Networking.WabbajackClientApi": "0.4.6"
"Wabbajack.DTOs": "0.4.7",
"Wabbajack.Networking.Http": "0.4.7",
"Wabbajack.Networking.Http.Interfaces": "0.4.7",
"Wabbajack.Networking.WabbajackClientApi": "0.4.7"
},
"runtime": {
"Wabbajack.Networking.NexusApi.dll": {}
}
},
"Wabbajack.Networking.WabbajackClientApi/0.4.6": {
"Wabbajack.Networking.WabbajackClientApi/0.4.7": {
"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.6",
"Wabbajack.DTOs": "0.4.6",
"Wabbajack.Paths.IO": "0.4.6",
"Wabbajack.VFS.Interfaces": "0.4.6",
"Wabbajack.Common": "0.4.7",
"Wabbajack.DTOs": "0.4.7",
"Wabbajack.Paths.IO": "0.4.7",
"Wabbajack.VFS.Interfaces": "0.4.7",
"YamlDotNet": "16.3.0"
},
"runtime": {
"Wabbajack.Networking.WabbajackClientApi.dll": {}
}
},
"Wabbajack.Paths/0.4.6": {
"Wabbajack.Paths/0.4.7": {
"runtime": {
"Wabbajack.Paths.dll": {}
}
},
"Wabbajack.Paths.IO/0.4.6": {
"Wabbajack.Paths.IO/0.4.7": {
"dependencies": {
"Wabbajack.Paths": "0.4.6",
"Wabbajack.Paths": "0.4.7",
"shortid": "4.0.0"
},
"runtime": {
"Wabbajack.Paths.IO.dll": {}
}
},
"Wabbajack.RateLimiter/0.4.6": {
"Wabbajack.RateLimiter/0.4.7": {
"runtime": {
"Wabbajack.RateLimiter.dll": {}
}
},
"Wabbajack.Server.Lib/0.4.6": {
"Wabbajack.Server.Lib/0.4.7": {
"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.6",
"Wabbajack.Networking.Http.Interfaces": "0.4.6",
"Wabbajack.Services.OSIntegrated": "0.4.6"
"Wabbajack.Common": "0.4.7",
"Wabbajack.Networking.Http.Interfaces": "0.4.7",
"Wabbajack.Services.OSIntegrated": "0.4.7"
},
"runtime": {
"Wabbajack.Server.Lib.dll": {}
}
},
"Wabbajack.Services.OSIntegrated/0.4.6": {
"Wabbajack.Services.OSIntegrated/0.4.7": {
"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.6",
"Wabbajack.Downloaders.Dispatcher": "0.4.6",
"Wabbajack.Installer": "0.4.6",
"Wabbajack.Networking.BethesdaNet": "0.4.6",
"Wabbajack.Networking.Discord": "0.4.6",
"Wabbajack.VFS": "0.4.6"
"Wabbajack.Compiler": "0.4.7",
"Wabbajack.Downloaders.Dispatcher": "0.4.7",
"Wabbajack.Installer": "0.4.7",
"Wabbajack.Networking.BethesdaNet": "0.4.7",
"Wabbajack.Networking.Discord": "0.4.7",
"Wabbajack.VFS": "0.4.7"
},
"runtime": {
"Wabbajack.Services.OSIntegrated.dll": {}
}
},
"Wabbajack.VFS/0.4.6": {
"Wabbajack.VFS/0.4.7": {
"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.6",
"Wabbajack.FileExtractor": "0.4.6",
"Wabbajack.Hashing.PHash": "0.4.6",
"Wabbajack.Hashing.xxHash64": "0.4.6",
"Wabbajack.Paths": "0.4.6",
"Wabbajack.Paths.IO": "0.4.6",
"Wabbajack.VFS.Interfaces": "0.4.6"
"Wabbajack.Common": "0.4.7",
"Wabbajack.FileExtractor": "0.4.7",
"Wabbajack.Hashing.PHash": "0.4.7",
"Wabbajack.Hashing.xxHash64": "0.4.7",
"Wabbajack.Paths": "0.4.7",
"Wabbajack.Paths.IO": "0.4.7",
"Wabbajack.VFS.Interfaces": "0.4.7"
},
"runtime": {
"Wabbajack.VFS.dll": {}
}
},
"Wabbajack.VFS.Interfaces/0.4.6": {
"Wabbajack.VFS.Interfaces/0.4.7": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Wabbajack.DTOs": "0.4.6",
"Wabbajack.Hashing.xxHash64": "0.4.6",
"Wabbajack.Paths": "0.4.6"
"Wabbajack.DTOs": "0.4.7",
"Wabbajack.Hashing.xxHash64": "0.4.7",
"Wabbajack.Paths": "0.4.7"
},
"runtime": {
"Wabbajack.VFS.Interfaces.dll": {}
@@ -2332,7 +2332,7 @@
}
},
"libraries": {
"jackify-engine/0.4.6": {
"jackify-engine/0.4.7": {
"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.6": {
"Wabbajack.CLI.Builder/0.4.7": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Common/0.4.6": {
"Wabbajack.Common/0.4.7": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Compiler/0.4.6": {
"Wabbajack.Compiler/0.4.7": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Compression.BSA/0.4.6": {
"Wabbajack.Compression.BSA/0.4.7": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Compression.Zip/0.4.6": {
"Wabbajack.Compression.Zip/0.4.7": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Configuration/0.4.6": {
"Wabbajack.Configuration/0.4.7": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Downloaders.Bethesda/0.4.6": {
"Wabbajack.Downloaders.Bethesda/0.4.7": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Downloaders.Dispatcher/0.4.6": {
"Wabbajack.Downloaders.Dispatcher/0.4.7": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Downloaders.GameFile/0.4.6": {
"Wabbajack.Downloaders.GameFile/0.4.7": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Downloaders.GoogleDrive/0.4.6": {
"Wabbajack.Downloaders.GoogleDrive/0.4.7": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Downloaders.Http/0.4.6": {
"Wabbajack.Downloaders.Http/0.4.7": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Downloaders.Interfaces/0.4.6": {
"Wabbajack.Downloaders.Interfaces/0.4.7": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Downloaders.IPS4OAuth2Downloader/0.4.6": {
"Wabbajack.Downloaders.IPS4OAuth2Downloader/0.4.7": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Downloaders.Manual/0.4.6": {
"Wabbajack.Downloaders.Manual/0.4.7": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Downloaders.MediaFire/0.4.6": {
"Wabbajack.Downloaders.MediaFire/0.4.7": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Downloaders.Mega/0.4.6": {
"Wabbajack.Downloaders.Mega/0.4.7": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Downloaders.ModDB/0.4.6": {
"Wabbajack.Downloaders.ModDB/0.4.7": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Downloaders.Nexus/0.4.6": {
"Wabbajack.Downloaders.Nexus/0.4.7": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Downloaders.VerificationCache/0.4.6": {
"Wabbajack.Downloaders.VerificationCache/0.4.7": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Downloaders.WabbajackCDN/0.4.6": {
"Wabbajack.Downloaders.WabbajackCDN/0.4.7": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.DTOs/0.4.6": {
"Wabbajack.DTOs/0.4.7": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.FileExtractor/0.4.6": {
"Wabbajack.FileExtractor/0.4.7": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Hashing.PHash/0.4.6": {
"Wabbajack.Hashing.PHash/0.4.7": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Hashing.xxHash64/0.4.6": {
"Wabbajack.Hashing.xxHash64/0.4.7": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Installer/0.4.6": {
"Wabbajack.Installer/0.4.7": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.IO.Async/0.4.6": {
"Wabbajack.IO.Async/0.4.7": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Networking.BethesdaNet/0.4.6": {
"Wabbajack.Networking.BethesdaNet/0.4.7": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Networking.Discord/0.4.6": {
"Wabbajack.Networking.Discord/0.4.7": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Networking.GitHub/0.4.6": {
"Wabbajack.Networking.GitHub/0.4.7": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Networking.Http/0.4.6": {
"Wabbajack.Networking.Http/0.4.7": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Networking.Http.Interfaces/0.4.6": {
"Wabbajack.Networking.Http.Interfaces/0.4.7": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Networking.NexusApi/0.4.6": {
"Wabbajack.Networking.NexusApi/0.4.7": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Networking.WabbajackClientApi/0.4.6": {
"Wabbajack.Networking.WabbajackClientApi/0.4.7": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Paths/0.4.6": {
"Wabbajack.Paths/0.4.7": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Paths.IO/0.4.6": {
"Wabbajack.Paths.IO/0.4.7": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.RateLimiter/0.4.6": {
"Wabbajack.RateLimiter/0.4.7": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Server.Lib/0.4.6": {
"Wabbajack.Server.Lib/0.4.7": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Services.OSIntegrated/0.4.6": {
"Wabbajack.Services.OSIntegrated/0.4.7": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.VFS/0.4.6": {
"Wabbajack.VFS/0.4.7": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.VFS.Interfaces/0.4.6": {
"Wabbajack.VFS.Interfaces/0.4.7": {
"type": "project",
"serviceable": false,
"sha512": ""

Binary file not shown.

View File

@@ -550,29 +550,29 @@ class SettingsDialog(QDialog):
self.component_method_group = QButtonGroup()
component_method_layout = QVBoxLayout()
# Get current setting
current_method = self.config_handler.get('component_installation_method', 'system_protontricks')
# Get current setting (default to winetricks)
current_method = self.config_handler.get('component_installation_method', 'winetricks')
# Migrate old bundled_protontricks users to system_protontricks
if current_method == 'bundled_protontricks':
current_method = 'system_protontricks'
# Protontricks (default)
self.protontricks_radio = QRadioButton("Protontricks (Default)")
self.protontricks_radio.setChecked(current_method == 'system_protontricks')
self.protontricks_radio.setToolTip(
"Use system-installed protontricks (flatpak or native). Required for component installation."
)
self.component_method_group.addButton(self.protontricks_radio, 0)
component_method_layout.addWidget(self.protontricks_radio)
# Winetricks (alternative)
self.winetricks_radio = QRadioButton("Winetricks (Alternative)")
# Winetricks (default)
self.winetricks_radio = QRadioButton("Winetricks (Default)")
self.winetricks_radio.setChecked(current_method == 'winetricks')
self.winetricks_radio.setToolTip(
"Use bundled winetricks instead. May work when protontricks unavailable."
"Use bundled winetricks for component installation. Faster and more reliable."
)
self.component_method_group.addButton(self.winetricks_radio, 1)
self.component_method_group.addButton(self.winetricks_radio, 0)
component_method_layout.addWidget(self.winetricks_radio)
# Protontricks (alternative)
self.protontricks_radio = QRadioButton("Protontricks (Alternative)")
self.protontricks_radio.setChecked(current_method == 'system_protontricks')
self.protontricks_radio.setToolTip(
"Use system-installed protontricks (flatpak or native). Fallback option if winetricks fails."
)
self.component_method_group.addButton(self.protontricks_radio, 1)
component_method_layout.addWidget(self.protontricks_radio)
component_layout.addLayout(component_method_layout)
@@ -746,7 +746,8 @@ class SettingsDialog(QDialog):
else:
self.install_proton_dropdown.addItem("No Proton Versions Detected", "none")
# Filter for fast Proton versions only
# Filter to only known-compatible Protons for component installation
# Third-party builds (CachyOS, etc.) may have compatibility issues with Windows installers
fast_protons = []
slow_protons = []
@@ -754,30 +755,39 @@ class SettingsDialog(QDialog):
proton_name = proton.get('name', 'Unknown Proton')
proton_type = proton.get('type', 'Unknown')
is_fast_proton = False
# Only include known-compatible Proton types for Install Proton
# Exclude third-party builds that may have component installation issues
if proton_type not in ('GE-Proton', 'Valve-Proton'):
# Skip third-party Protons (CachyOS, etc.) - they may not work reliably for component installation
logger.debug(f"Skipping {proton_name} ({proton_type}) from Install Proton dropdown - third-party builds may have compatibility issues")
continue
# Fast Protons: Experimental, GE-Proton 10+
if proton_name == "Proton - Experimental":
is_fast_proton = True
elif proton_type == 'GE-Proton':
# For GE-Proton, check major_version field
major_version = proton.get('major_version', 0)
if major_version >= 10:
is_fast_proton = True
# Determine if this Proton is explicitly slow for texture processing
slow_warning = False
if is_fast_proton:
if proton_type == 'GE-Proton':
display_name = f"{proton_name} (GE)"
else:
display_name = proton_name
fast_protons.append((display_name, str(proton['path'])))
else:
# Slow Protons: Valve 9, 10 beta, older GE-Proton, etc.
if proton_type == 'GE-Proton':
display_name = f"{proton_name} (GE) (Slow texture processing)"
else:
display_name = f"{proton_name} (Slow texture processing)"
if proton_type == 'GE-Proton':
# Older GE (< 10) are known to be slower for heavy texture processing.
major_version = proton.get('major_version')
# Check if we have a valid major_version and it's < 10
if major_version is not None and isinstance(major_version, int) and major_version < 10:
slow_warning = True
# Also check name pattern as fallback (e.g., "GE-Proton9-27")
elif 'GE-Proton9' in proton_name or 'GE-Proton8' in proton_name:
slow_warning = True
display_name = f"{proton_name} (GE)"
elif proton_type == 'Valve-Proton':
# Valve Proton 9.x is slower for BC7/BC6H workloads; newer Valve Proton is fine.
display_name = proton_name
if proton_name.startswith("Proton 9") or "9.0" in proton_name:
slow_warning = True
# Add slow label if needed
if slow_warning:
display_name = f"{display_name} (Slow texture processing)"
slow_protons.append((display_name, str(proton['path'])))
else:
# Everything else (fast) goes above the separator
fast_protons.append((display_name, str(proton['path'])))
# Add fast Protons first
for display_name, path in fast_protons:
@@ -964,7 +974,7 @@ class SettingsDialog(QDialog):
# Save component installation method preference
if self.winetricks_radio.isChecked():
method = 'winetricks'
else: # protontricks_radio (default)
else: # protontricks_radio (alternative)
method = 'system_protontricks'
old_method = self.config_handler.get('component_installation_method', 'winetricks')

View File

@@ -6,7 +6,7 @@
# Name of this version of winetricks (YYYYMMDD)
# (This doesn't change often, use the sha256sum of the file when reporting problems)
WINETRICKS_VERSION=20250102-next
WINETRICKS_VERSION=20260125-next
# This is a UTF-8 file
# You should see an o with two dots over it here [ö]
@@ -4605,7 +4605,7 @@ winetricks_set_wineprefix()
_W_wineserver_binary_arch="$(winetricks_get_file_arch "${WINE_BINDIR}/wineserver")"
fi
fi
if [ -z "${_W_wineserver_binary_arch}" ]; then
if [ -z "${W_OPT_UNATTENDED}" ] && [ -z "${_W_wineserver_binary_arch}" ]; then
w_warn "Unknown file arch of ${WINESERVER_BIN}."
fi
@@ -4622,7 +4622,7 @@ winetricks_set_wineprefix()
_W_wine_binary_arch="$(winetricks_get_file_arch "${WINE_BINDIR}/wine")"
fi
fi
if [ -z "${_W_wine_binary_arch}" ]; then
if [ -z "${W_OPT_UNATTENDED}" ] && [ -z "${_W_wine_binary_arch}" ]; then
w_warn "Unknown file arch of ${WINE_BIN}."
fi
@@ -9893,6 +9893,56 @@ load_dotnetdesktop9()
#----------------------------------------------------------------
w_metadata dotnet10 dlls \
title="MS .NET Runtime 10.0 LTS" \
publisher="Microsoft" \
year="2025" \
media="download" \
file1="dotnet-runtime-10.0.0-win-x86.exe" \
installed_file1="${W_PROGRAMS_WIN}/dotnet/dotnet.exe"
load_dotnet10()
{
# Official version, see https://dotnet.microsoft.com/en-us/download/dotnet/10.0
w_download https://builds.dotnet.microsoft.com/dotnet/Runtime/10.0.0/dotnet-runtime-10.0.0-win-x86.exe 90bc5667c2a35c030a2e964e7083fe8fbdbc461377d27b4f0d9bf7b400d7b982
w_try_cd "${W_CACHE}"/"${W_PACKAGE}"
w_try "${WINE}" "${file1}" ${W_OPT_UNATTENDED:+/quiet}
if [ "${W_ARCH}" = "win64" ]; then
# Also install the 64-bit version
w_download https://builds.dotnet.microsoft.com/dotnet/Runtime/10.0.0/dotnet-runtime-10.0.0-win-x64.exe ca2dd25d477174767a55756d9ef92bbda0d3c7ef12cef284a542689b2ba52767
w_try "${WINE}" "dotnet-runtime-10.0.0-win-x64.exe" ${W_OPT_UNATTENDED:+/quiet}
fi
}
#----------------------------------------------------------------
w_metadata dotnetdesktop10 dlls \
title="MS .NET Desktop Runtime 10.0 LTS" \
publisher="Microsoft" \
year="2025" \
media="download" \
file1="windowsdesktop-runtime-10.0.0-win-x86.exe" \
installed_file1="${W_PROGRAMS_WIN}/dotnet/dotnet.exe"
load_dotnetdesktop10()
{
# Official version, see https://dotnet.microsoft.com/en-us/download/dotnet/10.0
w_download https://builds.dotnet.microsoft.com/dotnet/WindowsDesktop/10.0.0/windowsdesktop-runtime-10.0.0-win-x86.exe ac38c81fef78c565d6bfbddf49ac5bcca354616176c0108c5f0e23333c3d093a
w_try_cd "${W_CACHE}"/"${W_PACKAGE}"
w_try "${WINE}" "${file1}" ${W_OPT_UNATTENDED:+/quiet}
if [ "${W_ARCH}" = "win64" ]; then
# Also install the 64-bit version
w_download https://builds.dotnet.microsoft.com/dotnet/WindowsDesktop/10.0.0/windowsdesktop-runtime-10.0.0-win-x64.exe fc0494eaf529b15f74b10d920784cc618ff0845bebfdfd5d85d585e921157a5c
w_try "${WINE}" "windowsdesktop-runtime-10.0.0-win-x64.exe" ${W_OPT_UNATTENDED:+/quiet}
fi
}
#----------------------------------------------------------------
w_metadata dotnet_verifier dlls \
title="MS .NET Verifier" \
publisher="Microsoft" \
@@ -15467,8 +15517,10 @@ w_metadata dxwnd apps \
load_dxwnd()
{
# 2022/10/02 v2_05_88_build.rar a80ad1246493b3b34fba2131494052423ac298a39592d4e06a685568b829922e
w_download https://versaweb.dl.sourceforge.net/project/dxwnd/Latest%20build/v2_05_88_build.rar a80ad1246493b3b34fba2131494052423ac298a39592d4e06a685568b829922e
w_try_7z "${W_PROGRAMS_X86_UNIX}"/dxwnd "${W_CACHE}"/"${W_PACKAGE}"/"${file1}" -aoa
w_download https://downloads.sourceforge.net/project/dxwnd/Latest%20build/v2_05_88_build.rar a80ad1246493b3b34fba2131494052423ac298a39592d4e06a685568b829922e
w_try_mkdir "${W_PROGRAMS_X86_UNIX}"/dxwnd
w_try_cd "${W_PROGRAMS_X86_UNIX}"/dxwnd
w_try_unrar "${W_CACHE}/${W_PACKAGE}/${file1}"
}
#----------------------------------------------------------------