BUTR/Bannerlord.BLSE

View on GitHub
src/Bannerlord.BLSE.Shared/NoExceptions/ProgramEx.cs

Summary

Maintainability
A
0 mins
Test Coverage
using Bannerlord.BUTR.Shared.Helpers;
using Bannerlord.LauncherEx;

using HarmonyLib;
using HarmonyLib.BUTR.Extensions;

using System;
using System.Linq;
using System.Reflection;

using TaleWorlds.Library;
using TaleWorlds.MountAndBlade.Launcher.Library;
using TaleWorlds.TwoDimension.Standalone;
using TaleWorlds.TwoDimension.Standalone.Native.Windows;

namespace Bannerlord.BLSE.Shared.NoExceptions;

public sealed class ProgramEx
{
    private record LauncherExContext(GraphicsForm GraphicsForm, StandaloneUIDomain StandaloneUIDomain, WindowsFrameworkEx WindowsFramework)
    {
        public static LauncherExContext Create()
        {
            var resourceDepot = new ResourceDepot();
            resourceDepot.AddLocation(BasePath.Name, $"{ModuleInfoHelper.ModulesFolder}/Native/LauncherGUI/");
            resourceDepot.CollectResources();
            resourceDepot.StartWatchingChangesInDepot();

            var graphicsForm = new GraphicsForm(1154, 701, resourceDepot, true, true, true, "M&B II: Bannerlord");
            var standaloneUIDomain = new StandaloneUIDomain(graphicsForm, resourceDepot);
            var windowsFrameworkEx = new WindowsFrameworkEx { ThreadConfig = WindowsFrameworkThreadConfig.NoThread };

            return new LauncherExContext(graphicsForm, standaloneUIDomain, windowsFrameworkEx);
        }

        public void Initialize()
        {
            WindowsFramework.Initialize(new FrameworkDomain[] { StandaloneUIDomain });
            WindowsFramework.RegisterMessageCommunicator(GraphicsForm);
            WindowsFramework.Start();
        }

        public void DeInitialize()
        {
            WindowsFramework.UnRegisterMessageCommunicator(GraphicsForm);
            GraphicsForm.Destroy();
            WindowsFramework.Stop();
        }
    }
    private record StartGameData
    {
        public bool ShouldStart { get; init; }
        public string[] Args { get; init; } = Array.Empty<string>();
    }

    private const string StarterExecutable = "Bannerlord.exe";
    private static StartGameData _gameData = new();
    private static LauncherExContext? _context;

    public static void Main(string[] args)
    {
#if v110 || v111
        TaleWorlds.Library.Common.SetInvariantCulture();
#endif
        Common.PlatformFileHelper = new PlatformFileHelperPC("Mount and Blade II Bannerlord");
        Debug.DebugManager = new LauncherDebugManager();
        AppDomain.CurrentDomain.AssemblyResolve += OnAssemblyResolve;

        new Harmony("Bannerlord.BLSE.Shared.LauncherExEx").TryPatch(
            AccessTools2.Method(typeof(TaleWorlds.MountAndBlade.Launcher.Library.Program), nameof(TaleWorlds.MountAndBlade.Launcher.Library.Program.StartGame)),
            AccessTools2.Method(typeof(ProgramEx), nameof(StartGamePrefix)));

        _gameData = _gameData with { Args = args.ToArray() };

        LauncherPlatform.Initialize();
        _context = LauncherExContext.Create();
        _context.Initialize();
        LauncherPlatform.Destroy();

        AppDomain.CurrentDomain.AssemblyResolve -= OnAssemblyResolve;

        if (_gameData.ShouldStart)
            TaleWorlds.Starter.Library.Program.Main(_gameData.Args.ToArray());
    }

    public static bool StartGamePrefix()
    {
        if (_context is null) return true;

        _gameData = _gameData with
        {
            Args = _gameData.Args.Concat(new[] { _context.StandaloneUIDomain.AdditionalArgs }).ToArray(),
            ShouldStart = true,
        };
        AuxFinalize();
        return false;
    }

    private static void AuxFinalize()
    {
        if (_context is not null)
        {
            _context.DeInitialize();
            _context = null;
        }

        var launcherDebugManager = Debug.DebugManager as LauncherDebugManager;
        launcherDebugManager?.OnFinalize();

        User32.SetForegroundWindow(Kernel32.GetConsoleWindow());

        Manager.Disable();
    }

    private static Assembly? OnAssemblyResolve(object sender, ResolveEventArgs args) => args.Name.Contains("ManagedStarter") ? Assembly.LoadFrom(StarterExecutable) : null;
}