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

@@ -1,5 +1,21 @@
# Jackify Changelog
## v0.2.2.2 - ModOrganizer.ini Path Fixes for SD Card Installations
**Release Date:** TBD (Testing in progress)
### Bug Fixes
- **ModOrganizer.ini Path Mangling**: Fixed incorrect drive letter assignment when modlist is on SD card but vanilla game is on internal storage. Now uses gamePath drive letter as source of truth for vanilla game paths.
- **Proton Config Name Mismatch (Issues #150, #151)**: Fixed incorrect Proton names written to Steam config.vdf CompatToolMapping. Naive string conversion produced wrong names (e.g., `proton_9.0_(beta)` instead of `proton_9`). Now resolves correct internal names from `compatibilitytool.vdf` (third-party) or App ID mapping (Valve Proton). CachyOS and other community Proton builds in `compatibilitytools.d/` are now detected and selectable.
- **Removed Lorerim/Lost Legacy Proton Override**: No longer forces Proton 9 for specific modlists. ENB compatibility warnings are handled by the success dialog instead.
### Engine Updates
- **jackify-engine 0.4.7**: Fixed incorrect quoting/escaping of MO2 `customExecutables` by writing clean, unquoted Proton `Z:\...` paths in `ModOrganizer.ini`. This eliminates engine-side quote corruption that previously triggered SD card path mangling issues.
### Loggging Improvement
- **Debug External Command Logging**: When debug mode is enabled, Jackify now logs the full `protontricks` command line before execution, making it easier for advanced users to reproduce and troubleshoot Wine/Proton issues by running the same command manually.
---
## v0.2.2.1 - TTW Installer Pinning and Configure New Modlist CLI Fix
**Release Date:** 2026-01-24

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,30 +550,30 @@ 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)
advanced_layout.addWidget(component_group)
@@ -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}"
}
#----------------------------------------------------------------