using System; using System.IO; using System.Reflection; using System.Runtime.InteropServices; namespace SpaceEngineersBlueprintEditor.GameAssemblyLoader; /// /// Resolves Space Engineers managed and native dependencies directly from Bin64. /// public static class GameAssemblyLoader { private static readonly object SyncRoot = new object(); private static bool resolverRegistered; /// /// Gets the currently selected game root directory. /// public static string? GameRootPath { get; private set; } /// /// Gets the currently selected game Bin64 directory. /// public static string? GameBinPath { get; private set; } /// /// Detects the game, registers assembly resolution, and returns the validated game root. /// This method is safe to call more than once before game types are used. /// public static string? Initialize(string? preferredPath = null) { lock (SyncRoot) { if (!resolverRegistered) { AppDomain.CurrentDomain.AssemblyResolve += ResolveAssembly; resolverRegistered = true; } var gameRoot = GameInstallationLocator.FindGameRootPath(preferredPath); if (gameRoot is null) { return GameRootPath; } GameRootPath = gameRoot; GameBinPath = Path.Combine(gameRoot, "Bin64"); SetDllDirectory(GameBinPath); return GameRootPath; } } /// /// Initializes the loader or throws a descriptive error when the game is unavailable. /// public static string EnsureInitialized(string? preferredPath = null) { return Initialize(preferredPath) ?? throw new DirectoryNotFoundException( "Space Engineers was not found. Install the Steam game or select its installation directory in Settings."); } private static Assembly? ResolveAssembly(object? sender, ResolveEventArgs args) { var binPath = GameBinPath; if (string.IsNullOrWhiteSpace(binPath)) { return null; } AssemblyName requestedName; try { requestedName = new AssemblyName(args.Name); } catch (ArgumentException) { return null; } if (string.IsNullOrWhiteSpace(requestedName.Name)) { return null; } var candidate = Path.Combine(binPath!, requestedName.Name + ".dll"); if (!File.Exists(candidate)) { return null; } try { var candidateName = AssemblyName.GetAssemblyName(candidate); if (!string.Equals(candidateName.Name, requestedName.Name, StringComparison.OrdinalIgnoreCase)) { return null; } return Assembly.LoadFrom(candidate); } catch (BadImageFormatException) { return null; } catch (FileLoadException) { return null; } catch (FileNotFoundException) { return null; } } [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool SetDllDirectory(string lpPathName); }