import xml.etree.ElementTree as ET import os from dataclasses import dataclass from typing import List, Optional from pathlib import Path @dataclass class SourcedFile: layer_name: str subfolder: Optional[str] files: List[str] def read_psc_file(psc_file: Path) -> List[SourcedFile]: tree = ET.parse(psc_file) root = tree.getroot() # PSC exports may use a default XML namespace. for element in root.iter(): element.tag = element.tag.rsplit("}", 1)[-1] layers = root.findall(".//Layer") layer_info_list: List[SourcedFile] = [] for layer in layers: layer_name = layer.attrib.get("Name") subfolder = layer.attrib.get("SubFolder") # Scripts-Filenames scripts = layer.find("Scripts") script_names = [] if scripts is not None: for filename in scripts.findall("Filename"): name = filename.attrib.get("Name") if name: script_names.append(name) layer_info_list.append( SourcedFile(layer_name=layer_name, subfolder=subfolder, files=script_names) ) return layer_info_list def get_all_psc_files(root_path: Path) -> list[Path]: return sorted(root_path.rglob("*.psc"), key=lambda path: str(path).casefold()) def psc_script_files(psc_file: Path) -> list[Path]: """Resolve layer script paths relative to the PSC, preserving load order.""" def expanded(value): return Path(os.path.expandvars(value).replace("\\", "/")) paths = [] for layer in read_psc_file(psc_file): folder = layer.subfolder or "." base = psc_file.parent / expanded(os.environ.get(folder, folder)) for name in layer.files: filename = expanded(name) if not filename.suffix: filename = filename.with_suffix(".tcl") path = (base / filename).resolve() if path.suffix.lower() == ".tcl": paths.append(path) return paths