From 25de000982a6281e3efdd8db896c731c333e7b48 Mon Sep 17 00:00:00 2001 From: SulfurNitride Date: Sat, 16 May 2026 11:20:29 -0500 Subject: bg3: tolerate dangling Wine-prefix symlinks in metadata dirs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the BG3 metadata path (e.g. AppData/Local/Larian Studios) is a symlink left over from a previous prefix setup that points at a Steam compatdata path which no longer resolves, pathlib's mkdir(parents=True, exist_ok=True) raises FileExistsError — exist_ok only suppresses the error when the path is an actual directory, and a dangling symlink fails is_dir(). The launch crashed in bg3_file_mapper.create_mapping before any mods were deployed. Walk the path top-down on FileExistsError. For each component that's a dangling symlink, resolve its target via os.path.realpath and create the directory there so the symlink becomes valid and the original mkdir succeeds. Cross-prefix sharing (the reason these symlinks exist) is preserved. Co-Authored-By: Claude Opus 4.7 (1M context) --- libs/basic_games/games/baldursgate3/bg3_utils.py | 27 ++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) (limited to 'libs') diff --git a/libs/basic_games/games/baldursgate3/bg3_utils.py b/libs/basic_games/games/baldursgate3/bg3_utils.py index bbe5cda..23f0a8c 100644 --- a/libs/basic_games/games/baldursgate3/bg3_utils.py +++ b/libs/basic_games/games/baldursgate3/bg3_utils.py @@ -1,4 +1,5 @@ import functools +import os import shutil import typing from pathlib import Path @@ -262,8 +263,26 @@ class BG3Utils: def create_dir_if_needed(path: Path) -> Path: - if "." not in path.name[1:]: - path.mkdir(parents=True, exist_ok=True) - else: - path.parent.mkdir(parents=True, exist_ok=True) + target = path if "." not in path.name[1:] else path.parent + _mkdir_resolving_symlinks(target) return path + + +def _mkdir_resolving_symlinks(target: Path) -> None: + try: + target.mkdir(parents=True, exist_ok=True) + return + except FileExistsError: + pass + # A component along the chain exists but is not a directory — typically a + # dangling symlink left over from a Wine prefix that points at a Steam + # compatdata path that doesn't exist yet. Walk top-down, materialize each + # dangling-symlink target as a real directory, then retry. + for component in list(reversed(target.parents)) + [target]: + if component.is_dir(): + continue + if component.is_symlink(): + real = Path(os.path.realpath(component)) + real.mkdir(parents=True, exist_ok=True) + continue + component.mkdir(exist_ok=True) -- cgit v1.3.1