mirror of
https://github.com/Omni-guides/Jackify.git
synced 2026-06-08 00:17:58 +02:00
Sync from development - prepare for v0.3.0
This commit is contained in:
184
jackify/backend/handlers/path_handler_game.py
Normal file
184
jackify/backend/handlers/path_handler_game.py
Normal file
@@ -0,0 +1,184 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Game path and compatdata mixin for PathHandler.
|
||||
Extracted from path_handler for file-size and domain separation.
|
||||
"""
|
||||
|
||||
import os
|
||||
import re
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from typing import Optional, List, Dict
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class PathHandlerGameMixin:
|
||||
"""Mixin providing game install path and compatdata discovery."""
|
||||
|
||||
@classmethod
|
||||
def find_compat_data(cls, appid: str) -> Optional[Path]:
|
||||
"""Find the compatdata directory for a given AppID."""
|
||||
if not appid:
|
||||
logger.error(f"Invalid AppID provided for compatdata search: {appid}")
|
||||
return None
|
||||
appid_clean = appid.lstrip('-')
|
||||
if not appid_clean.isdigit():
|
||||
logger.error(f"Invalid AppID provided for compatdata search: {appid}")
|
||||
return None
|
||||
logger.debug(f"Searching for compatdata directory for AppID: {appid}")
|
||||
library_paths = cls.get_all_steam_library_paths()
|
||||
if library_paths:
|
||||
logger.debug(f"Checking compatdata in {len(library_paths)} Steam libraries")
|
||||
for library_path in library_paths:
|
||||
compatdata_base = library_path / "steamapps" / "compatdata"
|
||||
if not compatdata_base.is_dir():
|
||||
logger.debug(f"Compatdata directory does not exist: {compatdata_base}")
|
||||
continue
|
||||
potential_path = compatdata_base / appid
|
||||
if potential_path.is_dir():
|
||||
logger.info(f"Found compatdata directory: {potential_path}")
|
||||
return potential_path
|
||||
logger.debug(f"Compatdata for AppID {appid} not found in {compatdata_base}")
|
||||
is_flatpak_steam = any('.var/app/com.valvesoftware.Steam' in str(lib) for lib in library_paths) if library_paths else False
|
||||
if not library_paths or is_flatpak_steam:
|
||||
logger.debug("Checking fallback compatdata locations...")
|
||||
if is_flatpak_steam:
|
||||
fallback_locations = [
|
||||
Path.home() / ".var/app/com.valvesoftware.Steam/.local/share/Steam/steamapps/compatdata",
|
||||
Path.home() / ".var/app/com.valvesoftware.Steam/data/Steam/steamapps/compatdata",
|
||||
]
|
||||
else:
|
||||
fallback_locations = [
|
||||
Path.home() / ".local/share/Steam/steamapps/compatdata",
|
||||
Path.home() / ".steam/steam/steamapps/compatdata",
|
||||
]
|
||||
for compatdata_base in fallback_locations:
|
||||
if compatdata_base.is_dir():
|
||||
potential_path = compatdata_base / appid
|
||||
if potential_path.is_dir():
|
||||
logger.warning(f"Found compatdata directory in fallback location: {potential_path}")
|
||||
return potential_path
|
||||
logger.warning(f"Compatdata directory for AppID {appid} not found in any Steam library or fallback location.")
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def detect_stock_game_path(game_type: str, steam_library: Path) -> Optional[Path]:
|
||||
"""Detect the stock game path for a given game type and Steam library."""
|
||||
try:
|
||||
game_app_ids = {
|
||||
'skyrim': '489830', 'fallout4': '377160', 'fnv': '22380', 'oblivion': '22330'
|
||||
}
|
||||
if game_type not in game_app_ids:
|
||||
return None
|
||||
app_id = game_app_ids[game_type]
|
||||
game_path = steam_library / 'steamapps' / 'common'
|
||||
possible_names = {
|
||||
'skyrim': ['Skyrim Special Edition', 'Skyrim'],
|
||||
'fallout4': ['Fallout 4'],
|
||||
'fnv': ['Fallout New Vegas', 'FalloutNV'],
|
||||
'oblivion': ['Oblivion']
|
||||
}
|
||||
if game_type not in possible_names:
|
||||
return None
|
||||
for name in possible_names[game_type]:
|
||||
potential_path = game_path / name
|
||||
if potential_path.exists():
|
||||
return potential_path
|
||||
return None
|
||||
except Exception as e:
|
||||
logging.error(f"Error detecting stock game path: {e}")
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def find_game_install_paths(cls, target_appids: Dict[str, str]) -> Dict[str, Path]:
|
||||
"""Find installation paths for multiple specified games using Steam app IDs."""
|
||||
library_paths = cls.get_all_steam_library_paths()
|
||||
if not library_paths:
|
||||
logger.warning("Failed to find any Steam library paths")
|
||||
return {}
|
||||
results = {}
|
||||
for library_path in library_paths:
|
||||
common_dir = library_path / "steamapps" / "common"
|
||||
if not common_dir.is_dir():
|
||||
logger.debug(f"No 'steamapps/common' directory in library: {library_path}")
|
||||
continue
|
||||
try:
|
||||
game_dirs = [d for d in common_dir.iterdir() if d.is_dir()]
|
||||
except (PermissionError, OSError) as e:
|
||||
logger.warning(f"Cannot access directory {common_dir}: {e}")
|
||||
continue
|
||||
for game_name, app_id in target_appids.items():
|
||||
if game_name in results:
|
||||
continue
|
||||
appmanifest_path = library_path / "steamapps" / f"appmanifest_{app_id}.acf"
|
||||
if appmanifest_path.is_file():
|
||||
try:
|
||||
with open(appmanifest_path, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
match = re.search(r'"installdir"\s+"([^"]+)"', content)
|
||||
if match:
|
||||
install_dir_name = match.group(1)
|
||||
install_path = common_dir / install_dir_name
|
||||
if install_path.is_dir():
|
||||
results[game_name] = install_path
|
||||
logger.info(f"Found {game_name} at {install_path}")
|
||||
continue
|
||||
except Exception as e:
|
||||
logger.warning(f"Error reading appmanifest for {game_name}: {e}")
|
||||
return results
|
||||
|
||||
@classmethod
|
||||
def find_vanilla_game_paths(cls, game_names=None) -> Dict[str, Path]:
|
||||
"""For each known game, iterate all Steam libraries and look for the canonical game directory in steamapps/common."""
|
||||
GAME_DIR_NAMES = {
|
||||
"Skyrim Special Edition": ["Skyrim Special Edition"],
|
||||
"Fallout 4": ["Fallout 4"],
|
||||
"Fallout New Vegas": ["Fallout New Vegas"],
|
||||
"Oblivion": ["Oblivion"],
|
||||
"Fallout 3": ["Fallout 3", "Fallout 3 goty"]
|
||||
}
|
||||
if game_names is None:
|
||||
game_names = list(GAME_DIR_NAMES.keys())
|
||||
all_steam_libraries = cls.get_all_steam_library_paths()
|
||||
logger.info(f"[DEBUG] Detected Steam libraries: {all_steam_libraries}")
|
||||
found_games = {}
|
||||
for game in game_names:
|
||||
possible_names = GAME_DIR_NAMES.get(game, [game])
|
||||
for lib in all_steam_libraries:
|
||||
for name in possible_names:
|
||||
candidate = lib / "steamapps" / "common" / name
|
||||
logger.info(f"[DEBUG] Checking for vanilla game directory: {candidate}")
|
||||
if candidate.is_dir():
|
||||
found_games[game] = candidate
|
||||
logger.info(f"Found vanilla game directory for {game}: {candidate}")
|
||||
break
|
||||
if game in found_games:
|
||||
break
|
||||
return found_games
|
||||
|
||||
def _detect_stock_game_path(self) -> bool:
|
||||
"""Detects common Stock Game or Game Root directories within the modlist path. Expects self.logger, self.modlist_dir, self.stock_game_path."""
|
||||
self.logger.info("Step 7a: Detecting Stock Game/Game Root directory...")
|
||||
if not self.modlist_dir:
|
||||
self.logger.error("Modlist directory not set, cannot detect stock game path.")
|
||||
return False
|
||||
modlist_path = Path(self.modlist_dir)
|
||||
preferred_order = [
|
||||
"Stock Game", "STOCK GAME", "Skyrim Stock", "Stock Game Folder",
|
||||
"Stock Folder", Path("root/Skyrim Special Edition"), "Game Root"
|
||||
]
|
||||
found_path = None
|
||||
for name in preferred_order:
|
||||
potential_path = modlist_path / name
|
||||
if potential_path.is_dir():
|
||||
found_path = str(potential_path)
|
||||
self.logger.info(f"Found potential stock game directory: {found_path}")
|
||||
break
|
||||
if found_path:
|
||||
self.stock_game_path = found_path
|
||||
return True
|
||||
self.stock_game_path = None
|
||||
self.logger.info("No common Stock Game/Game Root directory found. Will assume vanilla game path is needed for some operations.")
|
||||
return True
|
||||
Reference in New Issue
Block a user