Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of LoadingScreen v1.1.4
BepInEx/patchers/Bertogim-LoadingScreen/BepInEx.SplashScreen.Patcher.BepInEx5.dll
Decompiled 9 months agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Timers; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Mono.Cecil; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("BepInEx.SplashScreen.Patcher")] [assembly: AssemblyDescription("Splash screen that shows loading progress when a game patched with BepInEx is loading.")] [assembly: AssemblyCompany("https://github.com/BepInEx/BepInEx.SplashScreen")] [assembly: AssemblyProduct("BepInEx.SplashScreen")] [assembly: AssemblyCopyright("Copyright © 2023")] [assembly: ComVisible(false)] [assembly: Guid("449b9f51-bbe5-4d4a-8936-e0a3081d79cb")] [assembly: AssemblyVersion("2.2.1.0")] namespace BepInEx.SplashScreen; public static class BepInExSplashScreenPatcher { public const string Version = "1.0"; private static int _initialized; public static IEnumerable<string> TargetDLLs { get { Init(); return Enumerable.Empty<string>(); } } static BepInExSplashScreenPatcher() { Init(); } public static void Patch(AssemblyDefinition _) { Init(); } public static void Init() { if (Interlocked.Exchange(ref _initialized, 1) != 1) { SplashScreenController.SpawnSplash(); } } } internal class Metadata { public const string Version = "2.2.1"; } internal sealed class LoadingLogListener : ILogListener, IDisposable { [CompilerGenerated] private sealed class <DelayedCo>d__5 : IEnumerator<object>, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; private int <i>5__1; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <DelayedCo>d__5(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { switch (<>1__state) { default: return false; case 0: <>1__state = -1; <i>5__1 = 0; break; case 1: <>1__state = -1; <i>5__1++; break; } if (<i>5__1 < 1) { <>2__current = null; <>1__state = 1; return true; } SplashScreenController.KillSplash(); return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private bool _disposed; private LoadingLogListener() { } public static LoadingLogListener StartListening() { LoadingLogListener loadingLogListener = new LoadingLogListener(); Logger.Listeners.Add((ILogListener)(object)loadingLogListener); return loadingLogListener; } public void Dispose() { _disposed = true; } public void LogEvent(object sender, LogEventArgs eventArgs) { if (_disposed || !(eventArgs.Source.SourceName == "BepInEx") || eventArgs.Data == null) { return; } try { string text = (eventArgs.Data as string) ?? eventArgs.Data.ToString(); if (text == "Chainloader startup complete") { Dispose(); Traverse threadingHelper = Traverse.CreateWithType("BepInEx.ThreadingHelper").Property("Instance", (object[])null); Traverse val = threadingHelper.Method("StartSyncInvoke", new Type[1] { typeof(Action) }, (object[])null); if (val.MethodExists()) { val.GetValue(new object[1] { (Action)delegate { try { threadingHelper.Method("StartCoroutine", new Type[1] { typeof(IEnumerator) }, (object[])null).GetValue(new object[1] { DelayedCo() }); } catch (Exception ex2) { SplashScreenController.Logger.LogError((object)("Unexpected crash when trying to StartCoroutine: " + ex2)); SplashScreenController.KillSplash(); } } }); } else { Process currentProcess = Process.GetCurrentProcess(); System.Timers.Timer timer = new System.Timers.Timer(500.0); timer.Elapsed += delegate { try { timer.Stop(); currentProcess.Refresh(); if (currentProcess.Responding) { timer.Dispose(); SplashScreenController.KillSplash(); } else { SplashScreenController.Logger.LogDebug((object)"Process not responding, waiting..."); timer.Start(); } } catch (Exception ex) { SplashScreenController.Logger.LogError((object)("Unexpected crash in timer.Elapsed: " + ex)); SplashScreenController.KillSplash(); } }; timer.AutoReset = false; timer.Start(); } } else if (text.StartsWith("Failed to run [BepInEx.Chainloader]", StringComparison.Ordinal) || string.Equals(text, "Could not run preloader!", StringComparison.Ordinal)) { Dispose(); SplashScreenController.KillSplash(); } SplashScreenController.SendMessage(text); } catch (Exception arg) { SplashScreenController.Logger.LogError((object)string.Format("Crash in {0}, aborting. Exception: {1}", "LogEvent", arg)); SplashScreenController.KillSplash(); } } private static IEnumerator DelayedCo() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <DelayedCo>d__5(0); } } public static class SplashScreenController { internal static readonly ManualLogSource Logger = Logger.CreateLogSource("Splash"); private static readonly Queue _StatusQueue = Queue.Synchronized(new Queue(10, 2f)); private static LoadingLogListener _logListener; private static Process _guiProcess; public static void SpawnSplash() { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Expected O, but got Unknown //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Expected O, but got Unknown //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Expected O, but got Unknown //IL_01fe: Unknown result type (might be due to invalid IL or missing references) //IL_0208: Expected O, but got Unknown //IL_0246: Unknown result type (might be due to invalid IL or missing references) //IL_0250: Expected O, but got Unknown //IL_02d3: Unknown result type (might be due to invalid IL or missing references) //IL_02dd: Expected O, but got Unknown try { ConfigFile val = new ConfigFile(Path.Combine(Paths.ConfigPath, "Bertogim.LoadingScreen.cfg"), true); ConfigEntry<bool> val2 = val.Bind<bool>("1. LoadingScreen", "Enabled", true, "Display a loading window with information about game load progress on game start-up."); ConfigEntry<string> val3 = val.Bind<string>("2. Window", "WindowType", "FakeGame", new ConfigDescription("FakeGame = Makes a window with the same icon as the game, tries to mimic the game till it appears\nFixedWindow = A fixed loading screen on top of all windows, cant move or close and is not on the taskbar (Same behavior as v1.0.5 and less) (I wouldn't use this option since it can be annoying to have the loading screen stuck on top of everything).", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[2] { "FakeGame", "FixedWindow" }), new object[0])); ConfigEntry<int> val4 = val.Bind<int>("2. Window", "WindowWidth", 640, "The window width in pixels (Gets affected by windows screen scale config) \nHeight is automatically calculated by the image aspect ratio."); ConfigEntry<int> val5 = val.Bind<int>("2. Window", "ExtraWaitTime", 1, new ConfigDescription("Seconds extra to maintain the loading screen starting when the game window shows up.\nGood for big modpacks where the game window stays blank loading for a few seconds", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 60), new object[0])); ConfigEntry<string> setting2 = val.Bind<string>("2. Window", "TitleBarColor", "FFFFFF", "Hex color for the window's title bar (e.g. 1E90FF for DodgerBlue). Leave as FFFFFF for default behavior. Requires Windows 10 build 1809+"); ConfigEntry<string> setting3 = val.Bind<string>("2. Window", "BackgroundColor", "000000", "Hex color for the background (Custom images cover this)"); ConfigEntry<bool> val6 = val.Bind<bool>("2. Window", "RandomizeImage", false, "Whether to randomize the background image on each game start"); ConfigEntry<string> val7 = val.Bind<string>("2. Window", "ImagePath", "./Plugins/*/LoadingScreen/*.png", "Path to where to get the image or images for the background\n Relative to the BepInEx folder"); ConfigEntry<bool> val8 = val.Bind<bool>("2. Window", "ImagePathLogs", false, "Enable extra logging about image path finding"); ConfigEntry<string> setting4 = val.Bind<string>("3. Text", "TextColor", "FFFFFF", "Text color in hex format (e.g. FFFFFF for white)."); ConfigEntry<string> val9 = val.Bind<string>("3. Text", "TextFont", "Segoe UI", "Font name used for the loading text (e.g. Arial, Segoe UI, Consolas). Must match an installed system font.\nFor a list of default Windows fonts, visit: https://learn.microsoft.com/en-us/typography/fonts/windows_10_font_list"); ConfigEntry<string> setting5 = val.Bind<string>("3. Text", "TextBackgroundColor", "595959", "Hex color for background behind the text (e.g. 595959 for gray)"); ConfigEntry<bool> val10 = val.Bind<bool>("4. ProgressBar", "UseCustomProgressBar", true, "Whether to use a customizable progress bar (allows using the options below) instead of the Windows native one"); ConfigEntry<string> setting6 = val.Bind<string>("4. ProgressBar", "ProgressBarColor", "34b233", "Progress bar color, use a hex color (e.g. 34b233 for Wageningen Green)"); ConfigEntry<string> setting7 = val.Bind<string>("4. ProgressBar", "ProgressBarBackgroundColor", "FFFFFF", "Progress bar background color, use a hex color (e.g. FFFFFF for white)"); ConfigEntry<int> val11 = val.Bind<int>("4. ProgressBar", "ProgressBarBorderSize", 0, new ConfigDescription("Border thickness in pixels (0-4)", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 4), new object[0])); ConfigEntry<string> setting8 = val.Bind<string>("4. ProgressBar", "ProgressBarBorderColor", "FFFFFF", "Border color in hex format (e.g. FF0000)"); ConfigEntry<int> val12 = val.Bind<int>("4. ProgressBar", "ProgressBarSmoothness", 25, new ConfigDescription("Loading bar smoothness when changing (0-100)", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), new object[0])); ConfigEntry<string> val13 = val.Bind<string>("4. ProgressBar", "ProgressBarCurve", "EaseOut", new ConfigDescription("Animation curve used to interpolate the loading bar value smoothly over time.\nAvailable curves:\n- Linear: Constant speed\n- EaseIn: Starts slow, speeds up\n- EaseOut: Starts fast, slows down\n- EaseInOut: Slow start and end\n- SmootherStep: Smoothest transition\n- Exponential: Very slow start, accelerates\n- Elastic: Overshoots and bounces into place\n- Bounce: Bounces like a ball at the end\n- BackIn: Starts by going backward, then forward\n- BackOut: Overshoots slightly and comes back\n- Spring: Oscillates like a spring", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[11] { "Linear", "EaseIn", "EaseOut", "EaseInOut", "SmootherStep", "Exponential", "Elastic", "Bounce", "BackIn", "BackOut", "Spring" }), new object[0])); ConfigEntry<bool> val14 = val.Bind<bool>("5. Other", "GenerateStartupPluginLoadTimeInfo", false, "Generate information about how many time each plugin took to load (File will generate in BepInEx/Patchers/Bertogim-LoadingScreen/Debug)"); CheckValue(setting2); CheckValue(setting3); CheckValue(setting4); CheckValue(setting5); CheckValue(setting6); CheckValue(setting7); CheckValue(setting8); if (!val2.Value) { Logger.LogDebug((object)"Not showing splash because the Enabled setting is off"); return; } string text = Path.Combine(Path.GetDirectoryName(typeof(SplashScreenController).Assembly.Location) ?? Path.Combine(Paths.PatcherPluginPath, "1. LoadingScreen"), "LoadingScreen.GUI.exe"); if (!File.Exists(text)) { throw new FileNotFoundException("Executable not found or inaccessible at " + text); } Logger.Log((LogLevel)16, (object)("Starting GUI process: " + text)); ProcessStartInfo startInfo = new ProcessStartInfo(text, Process.GetCurrentProcess().Id.ToString()) { UseShellExecute = false, RedirectStandardInput = true, RedirectStandardOutput = true, RedirectStandardError = true, StandardOutputEncoding = Encoding.UTF8, StandardErrorEncoding = Encoding.UTF8 }; _guiProcess = Process.Start(startInfo); Thread thread = new Thread(CommunicationThread); thread.IsBackground = true; thread.Start(_guiProcess); _logListener = LoadingLogListener.StartListening(); } catch (Exception ex) { Logger.LogError((object)("Failed to start GUI: " + ex)); KillSplash(); } static void CheckValue(ConfigEntry<string> setting) { string text2 = RemoveHashtag(setting.Value); if (text2 != setting.Value) { setting.Value = text2; } } static string RemoveHashtag(string value) { if (!string.IsNullOrEmpty(value) && value.StartsWith("#")) { return value.Substring(1); } return value; } } internal static void SendMessage(string message) { _StatusQueue.Enqueue(message); } private static void CommunicationThread(object processArg) { try { Process process = (Process)processArg; process.Exited += delegate { KillSplash(); }; process.OutputDataReceived += delegate(object sender, DataReceivedEventArgs args) { if (args.Data != null) { Logger.Log((LogLevel)16, (object)("[GUI] " + args.Data.Replace('\t', '\n').TrimEnd(new char[1] { '\n' }))); } }; process.BeginOutputReadLine(); process.ErrorDataReceived += delegate(object sender, DataReceivedEventArgs args) { if (args.Data != null) { Logger.Log((LogLevel)2, (object)("[GUI] " + args.Data.Replace('\t', '\n').TrimEnd(new char[1] { '\n' }))); } }; process.BeginErrorReadLine(); process.StandardInput.AutoFlush = false; Logger.LogDebug((object)"Connected to the GUI process"); bool flag = false; while (!process.HasExited) { while (_StatusQueue.Count > 0 && process.StandardInput.BaseStream.CanWrite) { process.StandardInput.WriteLine(_StatusQueue.Dequeue()); flag = true; } if (flag) { flag = false; process.StandardInput.Flush(); } Thread.Sleep(150); } } catch (ThreadAbortException) { } catch (Exception arg) { Logger.LogError((object)string.Format("Crash in {0}, aborting. Exception: {1}", "CommunicationThread", arg)); } finally { KillSplash(); } } internal static void KillSplash() { try { _logListener?.Dispose(); _StatusQueue.Clear(); _StatusQueue.TrimToSize(); try { if (_guiProcess != null && !_guiProcess.HasExited) { Logger.LogDebug((object)"Closing GUI process"); _guiProcess.Kill(); } } catch (Exception) { } Logger.Dispose(); } catch (Exception value) { Console.WriteLine(value); } } }
BepInEx/patchers/Bertogim-LoadingScreen/LoadingScreen.GUI.exe
Decompiled 9 months ago#define DEBUG using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Drawing; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Threading; using System.Windows.Forms; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("BepInEx.SplashScreen.GUI")] [assembly: AssemblyDescription("Splash screen that shows loading progress when a game patched with BepInEx is loading.")] [assembly: AssemblyCompany("https://github.com/BepInEx/BepInEx.SplashScreen")] [assembly: AssemblyProduct("BepInEx.SplashScreen")] [assembly: AssemblyCopyright("Copyright © 2023")] [assembly: ComVisible(false)] [assembly: Guid("449b9f51-bbe5-4d4a-8936-e0a3081d79cb")] [assembly: AssemblyVersion("2.2.1.0")] public static class MathUtils { public static float Clamp(float value, float min, float max) { return (value < min) ? min : ((value > max) ? max : value); } public static int Clamp(int value, int min, int max) { return (value < min) ? min : ((value > max) ? max : value); } public static float Lerp(float a, float b, float t) { return a + (b - a) * Clamp(t, 0f, 1f); } } public enum ProgressBarCurve { Linear, EaseIn, EaseOut, EaseInOut, SmootherStep, Exponential, Elastic, Bounce, BackIn, BackOut, Spring } public class CustomProgressBar : ProgressBar { private Color _barColor; private Color _backgroundColor; private Color _borderColor; private int _borderSize; private int _smoothness; private ProgressBarCurve _curve; private ProgressBar _sourceProgressBar; private double _currentDisplayedValue; private double _velocity = 0.0; private Timer _animationTimer; private double _animationProgress = 1.0; private double _lastTarget; private const int PROGRESS_HEIGHT = 10; private double _animationStartValue; private double _animationTargetValue; private bool _isAnimating = false; public int Smoothness { get { return _smoothness; } set { _smoothness = value; } } public CustomProgressBar(ProgressBar source, Color barColor, Color backgroundColor, Color borderColor, int borderSize, int smoothness, ProgressBarCurve curve = ProgressBarCurve.SmootherStep) { //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Expected O, but got Unknown _sourceProgressBar = source; _barColor = barColor; _backgroundColor = backgroundColor; _borderColor = borderColor; _borderSize = Math.Max(0, borderSize); _smoothness = Math.Max(0, Math.Min(100, smoothness)); _curve = curve; _currentDisplayedValue = _sourceProgressBar.Value; _lastTarget = _currentDisplayedValue; ((Control)this).SetStyle((ControlStyles)131074, true); ((Control)this).Height = 10; ((Control)this).Dock = (DockStyle)2; ((ProgressBar)this).Style = (ProgressBarStyle)1; _animationTimer = new Timer { Interval = 16 }; _animationTimer.Tick += delegate { AnimateProgress(); }; _animationTimer.Start(); } private void AnimateProgress() { int num = MathUtils.Clamp(_sourceProgressBar.Value, _sourceProgressBar.Minimum, _sourceProgressBar.Maximum); if (_smoothness == 0) { _currentDisplayedValue = num; _isAnimating = false; } else { if (!_isAnimating || Math.Abs((double)num - _animationTargetValue) > 0.01) { _animationStartValue = _currentDisplayedValue; _animationTargetValue = num; _animationProgress = 0.0; _isAnimating = true; } if (_isAnimating) { double num2 = 1.0 + (double)_smoothness / 100.0 * 4.0; double num3 = (double)_animationTimer.Interval / 1000.0; _animationProgress += num3 / num2; if (_animationProgress >= 1.0) { _animationProgress = 1.0; _isAnimating = false; } double t = ApplyCurveToT(_animationProgress, _curve); _currentDisplayedValue = Lerp(_animationStartValue, _animationTargetValue, t); } } ((Control)this).Invalidate(); } protected override void OnPaint(PaintEventArgs e) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Expected O, but got Unknown //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Expected O, but got Unknown //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Expected O, but got Unknown Rectangle rectangle = new Rectangle(0, 0, ((Control)this).Width, ((Control)this).Height); Rectangle rectangle2 = rectangle; if (_borderSize > 0) { rectangle2.Inflate(-_borderSize, -_borderSize); } SolidBrush val = new SolidBrush(_backgroundColor); try { e.Graphics.FillRectangle((Brush)(object)val, rectangle2); } finally { ((IDisposable)val)?.Dispose(); } if (_borderSize > 0) { Pen val2 = new Pen(_borderColor, (float)_borderSize); try { Rectangle rectangle3 = new Rectangle(_borderSize / 2, _borderSize / 2, ((Control)this).Width - _borderSize, ((Control)this).Height - _borderSize); e.Graphics.DrawRectangle(val2, rectangle3); } finally { ((IDisposable)val2)?.Dispose(); } } if (!(_currentDisplayedValue > 0.0)) { return; } double num = (_currentDisplayedValue - (double)((ProgressBar)this).Minimum) / (double)(((ProgressBar)this).Maximum - ((ProgressBar)this).Minimum); int num2 = (int)((double)rectangle2.Width * num); if (num2 > 0) { Rectangle rectangle4 = new Rectangle(rectangle2.Left, rectangle2.Top, num2, rectangle2.Height); SolidBrush val3 = new SolidBrush(_barColor); try { e.Graphics.FillRectangle((Brush)(object)val3, rectangle4); } finally { ((IDisposable)val3)?.Dispose(); } } } protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) ((Control)this).SetBoundsCore(x, y, width, 10, specified); } private double ApplyCurveToT(double t, ProgressBarCurve curve) { t = Math.Max(0.0, Math.Min(1.0, t)); switch (curve) { case ProgressBarCurve.EaseIn: return t * t; case ProgressBarCurve.EaseOut: return 1.0 - (1.0 - t) * (1.0 - t); case ProgressBarCurve.EaseInOut: return (t < 0.5) ? (2.0 * t * t) : (1.0 - Math.Pow(-2.0 * t + 2.0, 2.0) / 2.0); case ProgressBarCurve.SmootherStep: return t * t * t * (t * (t * 6.0 - 15.0) + 10.0); case ProgressBarCurve.Exponential: return (t == 0.0) ? 0.0 : Math.Pow(2.0, 10.0 * (t - 1.0)); case ProgressBarCurve.Elastic: { if (t == 0.0 || t == 1.0) { return t; } double num3 = Math.PI * 2.0 / 3.0; return (0.0 - Math.Pow(2.0, 10.0 * t - 10.0)) * Math.Sin((t * 10.0 - 10.75) * num3); } case ProgressBarCurve.Bounce: if (t < 0.36363636363636365) { return 7.5625 * t * t; } if (t < 0.7272727272727273) { return 7.5625 * (t -= 0.5454545454545454) * t + 0.75; } if (t < 0.9090909090909091) { return 7.5625 * (t -= 0.8181818181818182) * t + 0.9375; } return 7.5625 * (t -= 21.0 / 22.0) * t + 63.0 / 64.0; case ProgressBarCurve.BackIn: { double num2 = 1.70158; return t * t * ((num2 + 1.0) * t - num2); } case ProgressBarCurve.BackOut: { double num = 1.70158; t -= 1.0; return t * t * ((num + 1.0) * t + num) + 1.0; } case ProgressBarCurve.Spring: return Math.Sin(t * Math.PI * (0.2 + 2.5 * t * t * t)) * Math.Pow(1.0 - t, 2.2) + t; default: return t; } } private double Lerp(double a, double b, double t) { return a + (b - a) * (double)MathUtils.Clamp((float)t, 0f, 1f); } } namespace BepInEx.SplashScreen; internal class IconManager { private struct SHFILEINFO { public IntPtr hIcon; public int iIcon; public uint dwAttributes; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] public string szDisplayName; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)] public string szTypeName; } private struct IMAGELISTDRAWPARAMS { public int cbSize; public IntPtr himl; public int i; public IntPtr hdcDst; public int x; public int y; public int cx; public int cy; public int xBitmap; public int yBitmap; public int rgbBk; public int rgbFg; public int fStyle; public int dwRop; public int fState; public int Frame; public int crEffect; } private struct IMAGEINFO { private readonly IntPtr hbmImage; private readonly IntPtr hbmMask; private readonly int Unused1; private readonly int Unused2; private readonly RECT rcImage; } [ComImport] [Guid("46EB5926-582E-4017-9FDF-E8998DAA0950")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] private interface IImageList { [PreserveSig] int Add(IntPtr hbmImage, IntPtr hbmMask, ref int pi); [PreserveSig] int ReplaceIcon(int i, IntPtr hicon, ref int pi); [PreserveSig] int SetOverlayImage(int iImage, int iOverlay); [PreserveSig] int Replace(int i, IntPtr hbmImage, IntPtr hbmMask); [PreserveSig] int AddMasked(IntPtr hbmImage, int crMask, ref int pi); [PreserveSig] int Draw(ref IMAGELISTDRAWPARAMS pimldp); [PreserveSig] int Remove(int i); [PreserveSig] int GetIcon(int i, int flags, ref IntPtr picon); [PreserveSig] int GetImageInfo(int i, ref IMAGEINFO pImageInfo); [PreserveSig] int Copy(int iDst, IImageList punkSrc, int iSrc, int uFlags); [PreserveSig] int Merge(int i1, IImageList punk2, int i2, int dx, int dy, ref Guid riid, ref IntPtr ppv); [PreserveSig] int Clone(ref Guid riid, ref IntPtr ppv); [PreserveSig] int GetImageRect(int i, ref RECT prc); [PreserveSig] int GetIconSize(ref int cx, ref int cy); [PreserveSig] int SetIconSize(int cx, int cy); [PreserveSig] int GetImageCount(ref int pi); [PreserveSig] int SetImageCount(int uNewCount); [PreserveSig] int SetBkColor(int clrBk, ref int pclr); [PreserveSig] int GetBkColor(ref int pclr); [PreserveSig] int BeginDrag(int iTrack, int dxHotspot, int dyHotspot); [PreserveSig] int EndDrag(); [PreserveSig] int DragEnter(IntPtr hwndLock, int x, int y); [PreserveSig] int DragLeave(IntPtr hwndLock); [PreserveSig] int DragMove(int x, int y); [PreserveSig] int SetDragCursorImage(ref IImageList punk, int iDrag, int dxHotspot, int dyHotspot); [PreserveSig] int DragShowNolock(int fShow); [PreserveSig] int GetDragImage(ref POINT ppt, ref POINT pptHotspot, ref Guid riid, ref IntPtr ppv); [PreserveSig] int GetItemFlags(int i, ref int dwFlags); [PreserveSig] int GetOverlayImage(int iOverlay, ref int piIndex); } private struct RECT { private readonly int _Left; private readonly int _Top; private readonly int _Right; private readonly int _Bottom; } private struct POINT { public int X; public int Y; public POINT(int x, int y) { X = x; Y = y; } public static implicit operator Point(POINT p) { return new Point(p.X, p.Y); } public static implicit operator POINT(Point p) { return new POINT(p.X, p.Y); } } private const int SHGFI_ICON = 256; private const int SHGFI_LARGEICON = 0; private const int SHGFI_SMALLICON = 1; private const int SHIL_EXTRALARGE = 2; private const int SHIL_JUMBO = 4; private const int WM_CLOSE = 16; [DllImport("shell32.dll", EntryPoint = "#727")] private static extern int SHGetImageList(int iImageList, ref Guid riid, out IImageList ppv); [DllImport("Shell32.dll")] private static extern int SHGetFileInfo(string pszPath, int dwFileAttributes, ref SHFILEINFO psfi, int cbFileInfo, uint uFlags); public static Icon GetLargeIcon(string fileName, bool jumbo, bool checkDisk) { SHFILEINFO psfi = default(SHFILEINFO); uint num = 16384u; if (!checkDisk) { num |= 0x10; } if (SHGetFileInfo(fileName, 128, ref psfi, Marshal.SizeOf((object)psfi), num) == 0) { throw new FileNotFoundException(); } int iIcon = psfi.iIcon; Guid riid = new Guid("46EB5926-582E-4017-9FDF-E8998DAA0950"); int iImageList = (jumbo ? 4 : 2); SHGetImageList(iImageList, ref riid, out var ppv); IntPtr picon = IntPtr.Zero; ppv.GetIcon(iIcon, 1, ref picon); return Icon.FromHandle(picon); } } public enum LoadEvent { None, PreloaderStart, PreloaderFinish, ChainloaderStart, ChainloaderFinish, LoadFinished } public class SplashScreen : Form { [ComImport] [Guid("ea1afb91-9e28-4b86-90e9-9e9f8a5eefaf")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] private interface ITaskbarList3 { void HrInit(); void AddTab(IntPtr hwnd); void DeleteTab(IntPtr hwnd); void ActivateTab(IntPtr hwnd); void SetActiveAlt(IntPtr hwnd); void MarkFullscreenWindow(IntPtr hwnd, [MarshalAs(UnmanagedType.Bool)] bool fFullscreen); void SetProgressValue(IntPtr hwnd, ulong ullCompleted, ulong ullTotal); void SetProgressState(IntPtr hwnd, TaskbarProgressState tbpFlags); } [ComImport] [Guid("56FDF344-FD6D-11d0-958A-006097C9A090")] [ClassInterface(ClassInterfaceType.None)] private class TaskbarInstance { } private enum TaskbarProgressState { NoProgress = 0, Indeterminate = 1, Normal = 2, Error = 4, Paused = 8 } private class DwmApi { public enum DWMWINDOWATTRIBUTE : uint { DWMWA_CAPTION_COLOR = 35u, DWMWA_USE_IMMERSIVE_DARK_MODE = 20u } [DllImport("dwmapi.dll")] public static extern int DwmSetWindowAttribute(IntPtr hwnd, DWMWINDOWATTRIBUTE attr, ref uint attrValue, int attrSize); [DllImport("dwmapi.dll")] public static extern int DwmSetWindowAttribute(IntPtr hwnd, DWMWINDOWATTRIBUTE attr, ref int attrValue, int attrSize); } private const uint WM_SETICON = 128u; private const int ICON_SMALL = 0; private const int ICON_BIG = 1; private const int GWL_EXSTYLE = -20; private const int WS_EX_APPWINDOW = 262144; private const string AppUserModelID = "BepInEx.SplashScreen"; private ITaskbarList3 _taskbar; private const string WorkingStr = "..."; private const string DoneStr = "...Done"; private string _gameLocation; private int _pluginPercentDone; private readonly Action<string, bool> _logAction; private readonly Process _gameProcess; public bool _closedByScript = false; private CustomProgressBar newProgressBar; public static bool imagePathLogs = bool.TryParse(GetBepInExConfigValue("2. Window", "ImagePathLogs", "false"), out var result) && result; private IContainer components = null; private ProgressBar progressBar1; private Label labelBot; private PictureBox pictureBox1; private CheckedListBox checkedListBox1; public static int SplashScreenExtraWaitTime => int.Parse(GetBepInExConfigValue("2. Window", "ExtraWaitTime", "0")); public static string SplashScreenWindowType => GetBepInExConfigValue("2. Window", "WindowType", "FakeGame"); public static int SplashScreenWindowWidth => int.Parse(GetBepInExConfigValue("2. Window", "WindowWidth", "640")); public static string BepInExRootPath { get { string text = Application.StartupPath; int num = 0; while (text != null && num < 5) { string text2 = Path.Combine(text, "BepInEx"); if (Directory.Exists(text2)) { return text2; } text = Directory.GetParent(text)?.FullName; num++; } return ""; } } protected override bool ShowWithoutActivation => true; private void SetExeIcon(Process gameProcess) { if (gameProcess == null || gameProcess.HasExited) { return; } try { string text = gameProcess.MainModule?.FileName; if (!string.IsNullOrEmpty(text)) { Icon val = (((Form)this).Icon = Icon.ExtractAssociatedIcon(text)); SendMessage(((Control)this).Handle, 128u, (IntPtr)0, val.Handle); SendMessage(((Control)this).Handle, 128u, (IntPtr)1, val.Handle); } } catch (Exception ex) { Debug.WriteLine("Failed to set game icon: " + ex.Message); ((Form)this).Icon = SystemIcons.Application; } } [DllImport("user32.dll")] private static extern IntPtr GetWindowLong(IntPtr hWnd, int nIndex); [DllImport("user32.dll", SetLastError = true)] private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong); [DllImport("shell32.dll")] private static extern void SetCurrentProcessExplicitAppUserModelID([MarshalAs(UnmanagedType.LPWStr)] string AppID); [DllImport("user32.dll", CharSet = CharSet.Auto)] private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam); public SplashScreen(Action<string, bool> logAction, Process gameProcess) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Expected O, but got Unknown _logAction = logAction; _gameProcess = gameProcess; InitializeComponent(); SetExeIcon(_gameProcess); ((Form)this).FormClosed += (FormClosedEventHandler)delegate { if (!_closedByScript && !_gameProcess.HasExited) { _logAction?.Invoke("Splash screen closed unexpectedly - terminating game process", arg2: true); _gameProcess.Kill(); } }; try { _taskbar = (ITaskbarList3)new TaskbarInstance(); _taskbar.HrInit(); } catch { _taskbar = null; } SetCurrentProcessExplicitAppUserModelID("BepInEx.SplashScreen"); progressBar1.Minimum = 0; progressBar1.Maximum = 100 + ((ObjectCollection)checkedListBox1.Items).Count * 10; progressBar1.Value = 0; ((Form)this).ShowInTaskbar = true; int num = GetWindowLong(((Control)this).Handle, -20).ToInt32(); SetWindowLong(((Control)this).Handle, -20, num | 0x40000); } private void UpdateProgress() { int num = checkedListBox1.CheckedItems.Count * 10 + _pluginPercentDone; progressBar1.Value = num; if (_taskbar != null) { _taskbar.SetProgressState(((Control)this).Handle, TaskbarProgressState.Normal); _taskbar.SetProgressValue(((Control)this).Handle, (ulong)num, (ulong)progressBar1.Maximum); } } public void ProcessEvent(LoadEvent e) { switch (e) { default: return; case LoadEvent.PreloaderStart: SetStatusMain("BepInEx patchers are being applied..."); _logAction?.Invoke("Detected preloader started", arg2: false); break; case LoadEvent.PreloaderFinish: checkedListBox1.SetItemChecked(0, true); SetStatusMain("Finished applying patchers."); SetStatusDetail("Plugins should start loading soon."); _logAction?.Invoke("Detected preloader finished", arg2: false); break; case LoadEvent.ChainloaderStart: SetStatusMain("BepInEx plugins are being loaded..."); _logAction?.Invoke("Detected chainloader started", arg2: false); break; case LoadEvent.ChainloaderFinish: _pluginPercentDone = 100; SetStatusMain("Finished loading plugins."); SetStatusDetail("Waiting for the game to start..."); _logAction?.Invoke("Detected chainloader finished", arg2: false); break; case LoadEvent.LoadFinished: { _logAction?.Invoke("Detected load finished", arg2: false); checkedListBox1.SetItemChecked(1, true); int value = checkedListBox1.CheckedItems.Count * 10 + _pluginPercentDone; double num = 5.0; double num2 = 60.0; double num3 = 25.0; double num4 = 0.0; if ((double)SplashScreenExtraWaitTime > num) { double val = ((double)SplashScreenExtraWaitTime - num) / (num2 - num); val = Math.Min(val, 1.0); num4 = val * num3; } newProgressBar.Smoothness = (int)(((double)SplashScreenExtraWaitTime + num4) * 10.0); _closedByScript = true; Thread.Sleep(10); progressBar1.Value = value; Thread.Sleep(SplashScreenExtraWaitTime * 1000 - 10); SafeClose(); return; } } UpdateProgress(); ((Control)checkedListBox1).Invalidate(); } public void SafeClose() { _closedByScript = true; ((Form)this).Close(); } public void SetStatusMain(string msg) { if (!string.IsNullOrEmpty(msg)) { ((Control)labelBot).Text = msg; } } public void SetStatusDetail(string msg) { ((Control)labelBot).Text = msg; } public void SetTitleBarColor(Color color) { uint attrValue = (uint)(color.R | (color.G << 8) | (color.B << 16)); IntPtr handle = ((Control)this).Handle; DwmApi.DwmSetWindowAttribute(handle, DwmApi.DWMWINDOWATTRIBUTE.DWMWA_CAPTION_COLOR, ref attrValue, 4); } public static string GetBepInExConfigValue(string section, string key, string defaultValue = "") { string text = Application.StartupPath; int num = 0; while (text != null && num < 5) { string text2 = Path.Combine(text, "BepInEx"); if (Directory.Exists(text2)) { string path = Path.Combine(text2, "config"); string path2 = Path.Combine(path, "Bertogim.LoadingScreen.cfg"); if (File.Exists(path2)) { string[] array = File.ReadAllLines(path2); bool flag = false; string[] array2 = array; foreach (string text3 in array2) { string text4 = text3.Trim(); if (text4.StartsWith("[" + section + "]")) { flag = true; continue; } if (text4.StartsWith("[") && flag) { break; } if (flag && text4.StartsWith(key)) { int num2 = text4.IndexOf('='); if (num2 > 0) { return text4.Substring(num2 + 1).Trim(); } } } } return defaultValue; } text = Directory.GetParent(text)?.FullName; num++; } return defaultValue; } public void CenterWindow() { ((Form)this).StartPosition = (FormStartPosition)0; int width = Screen.PrimaryScreen.Bounds.Width; int height = Screen.PrimaryScreen.Bounds.Height; int width2 = ((Control)this).Width; int height2 = ((Control)this).Height; ((Form)this).Location = new Point((width - width2) / 2, (height - height2) / 2); } private void ConfigureLayout(int fixedWidth, int scaledHeight, PictureBoxSizeMode sizeMode) { //IL_0326: Unknown result type (might be due to invalid IL or missing references) //IL_0330: Expected O, but got Unknown //IL_02bb: Unknown result type (might be due to invalid IL or missing references) //IL_0305: Unknown result type (might be due to invalid IL or missing references) //IL_030f: Expected O, but got Unknown string bepInExConfigValue = GetBepInExConfigValue("2. Window", "TitleBarColor", "#FFFFFF"); string bepInExConfigValue2 = GetBepInExConfigValue("2. Window", "BackgroundColor", "#000000"); string bepInExConfigValue3 = GetBepInExConfigValue("3. Text", "TextColor", "#FFFFFF"); string bepInExConfigValue4 = GetBepInExConfigValue("3. Text", "TextFont", "Segoe UI"); string bepInExConfigValue5 = GetBepInExConfigValue("3. Text", "TextBackgroundColor", "#595959"); bool flag = bool.Parse(GetBepInExConfigValue("4. ProgressBar", "UseCustomProgressBar", "true")); string bepInExConfigValue6 = GetBepInExConfigValue("4. ProgressBar", "ProgressBarColor", "#34b233"); string bepInExConfigValue7 = GetBepInExConfigValue("4. ProgressBar", "ProgressBarBackgroundColor", "#FFFFFF"); int borderSize = int.Parse(GetBepInExConfigValue("4. ProgressBar", "ProgressBarBorderSize", "0")); string bepInExConfigValue8 = GetBepInExConfigValue("4. ProgressBar", "ProgressBarBorderColor", "#FFFFFF"); int smoothness = int.Parse(GetBepInExConfigValue("4. ProgressBar", "ProgressBarSmoothness", "5")); string bepInExConfigValue9 = GetBepInExConfigValue("4. ProgressBar", "ProgressBarCurve", "Linear"); Color backColor = ParseColorHex(bepInExConfigValue2, Color.Black); Color backColor2 = ParseColorHex(bepInExConfigValue5, Color.FromArgb(89, 89, 89)); Color foreColor = ParseColorHex(bepInExConfigValue3, Color.White); Color color = ParseColorHex(bepInExConfigValue, Color.White); Color barColor = ParseColorHex(bepInExConfigValue6, Color.LimeGreen); Color backgroundColor = ParseColorHex(bepInExConfigValue7, Color.White); Color borderColor = ParseColorHex(bepInExConfigValue8, Color.White); if (color != Color.White) { SetTitleBarColor(color); } ProgressBarCurve curve; try { curve = (ProgressBarCurve)Enum.Parse(typeof(ProgressBarCurve), bepInExConfigValue9, ignoreCase: true); } catch { curve = ProgressBarCurve.Linear; } if (flag) { newProgressBar = new CustomProgressBar(progressBar1, barColor, backgroundColor, borderColor, borderSize, smoothness, curve); ((Control)newProgressBar).Name = "newProgressBar"; ((ProgressBar)newProgressBar).Minimum = progressBar1.Minimum; ((ProgressBar)newProgressBar).Maximum = progressBar1.Maximum; ((Control)this).Controls.Add((Control)(object)pictureBox1); ((Control)this).Controls.Add((Control)(object)newProgressBar); ((Control)this).Controls.Add((Control)(object)labelBot); ((Control)progressBar1).Visible = false; } int num = 30; int num2 = 10; int height = scaledHeight + num + num2; ((Form)this).ClientSize = new Size(fixedWidth, height); ((Control)pictureBox1).Size = new Size(fixedWidth, scaledHeight); ((Control)pictureBox1).Location = new Point(0, 0); pictureBox1.SizeMode = sizeMode; ((Control)pictureBox1).BackColor = backColor; ((Control)labelBot).Size = new Size(fixedWidth, num); ((Control)labelBot).Location = new Point(0, scaledHeight); try { ((Control)labelBot).Font = new Font(bepInExConfigValue4, 11f, (FontStyle)1); } catch { ((Control)labelBot).Font = new Font("Segoe UI", 11f, (FontStyle)1); } ((Control)labelBot).BackColor = backColor2; ((Control)labelBot).ForeColor = foreColor; labelBot.TextAlign = (ContentAlignment)32; ((Control)progressBar1).Size = new Size(fixedWidth, num2); ((Control)progressBar1).Location = new Point(0, scaledHeight + num); CenterWindow(); } private Color ParseColorHex(string hexColor, Color defaultColor) { try { if (!hexColor.StartsWith("#")) { hexColor = "#" + hexColor; } return ColorTranslator.FromHtml(hexColor); } catch { return defaultColor; } } public void SetIcon(Image fallbackIcon) { try { bool result; bool flag = bool.TryParse(GetBepInExConfigValue("2. Window", "RandomizeImage", "false"), out result) && result; string bepInExConfigValue = GetBepInExConfigValue("2. Window", "ImagePath", "./Plugins/*/LoadingScreen/*.png"); if (string.IsNullOrEmpty(BepInExRootPath)) { _logAction?.Invoke("Couldn't find BepInEx root directory, using fallback icon", arg2: true); UseFallbackIcon(fallbackIcon); return; } List<string> list = ResolveImagePaths(BepInExRootPath, bepInExConfigValue); if (list.Count == 0) { _logAction?.Invoke("No suitable loading image found, using fallback icon", arg2: false); UseFallbackIcon(fallbackIcon); } else { string text = (flag ? list[new Random().Next(list.Count)] : list[0]); _logAction?.Invoke("Using loading image: " + text, arg2: false); SetIconImage(text); } } catch (Exception ex) { _logAction?.Invoke("Error setting icon: " + ex.Message, arg2: true); UseFallbackIcon(fallbackIcon); } } private List<string> ResolveImagePaths(string rootPath, string relativePattern) { List<string> list = new List<string>(); relativePattern = relativePattern.Replace('/', Path.DirectorySeparatorChar); string[] parts = relativePattern.Split(new char[1] { Path.DirectorySeparatorChar }); ResolveRecursive(rootPath, parts, 0, list, rootPath); return list; } private void ResolveRecursive(string currentPath, string[] parts, int index, List<string> results, string rootPath) { if (index >= parts.Length) { return; } string text = parts[index]; if (imagePathLogs) { _logAction?.Invoke("Resolving part '" + text + "' in '" + currentPath + "'", arg2: false); } if (text.Contains("*") || text.Contains("?")) { if (index == parts.Length - 1) { try { string[] files = Directory.GetFiles(currentPath, text); foreach (string path in files) { string fullPath = Path.GetFullPath(path); if (fullPath.StartsWith(rootPath, StringComparison.OrdinalIgnoreCase)) { results.Add(fullPath); } else if (imagePathLogs) { _logAction?.Invoke("Skipped file outside BepInEx root: " + fullPath, arg2: false); } } return; } catch { return; } } try { string[] directories = Directory.GetDirectories(currentPath, text); foreach (string text2 in directories) { string fullPath2 = Path.GetFullPath(text2); if (fullPath2.StartsWith(rootPath, StringComparison.OrdinalIgnoreCase)) { ResolveRecursive(text2, parts, index + 1, results, rootPath); } else if (imagePathLogs) { _logAction?.Invoke("Skipped directory outside BepInEx root: " + fullPath2, arg2: false); } } return; } catch { return; } } string path2 = Path.Combine(currentPath, text); string fullPath3 = Path.GetFullPath(path2); if (fullPath3.StartsWith(rootPath, StringComparison.OrdinalIgnoreCase) && Directory.Exists(fullPath3)) { ResolveRecursive(fullPath3, parts, index + 1, results, rootPath); } else if (imagePathLogs) { _logAction?.Invoke("Skipped path outside BepInEx root or does not exist: " + fullPath3, arg2: false); } } public void SetIconImage(string imagePath) { Image val = Image.FromFile(imagePath); int splashScreenWindowWidth = SplashScreenWindowWidth; float num = (float)splashScreenWindowWidth / (float)val.Width; int scaledHeight = (int)((float)val.Height * num); ConfigureLayout(splashScreenWindowWidth, scaledHeight, (PictureBoxSizeMode)4); pictureBox1.Image = val; } private void UseFallbackIcon(Image icon) { if (icon != null) { int splashScreenWindowWidth = SplashScreenWindowWidth; float num = (float)splashScreenWindowWidth / 640f; int scaledHeight = (int)(360f * num); ConfigureLayout(splashScreenWindowWidth, scaledHeight, (PictureBoxSizeMode)3); pictureBox1.Image = icon; } } public void SetPluginProgress(int percentDone) { _pluginPercentDone = Math.Min(100, Math.Max(Math.Max(0, percentDone), _pluginPercentDone)); UpdateProgress(); } protected override void Dispose(bool disposing) { if (disposing && components != null) { components.Dispose(); } ((Form)this).Dispose(disposing); } private void InitializeComponent() { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected O, but got Unknown //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected O, but got Unknown //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Expected O, but got Unknown //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Expected O, but got Unknown //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Expected O, but got Unknown ComponentResourceManager componentResourceManager = new ComponentResourceManager(typeof(SplashScreen)); pictureBox1 = new PictureBox(); labelBot = new Label(); progressBar1 = new ProgressBar(); ((ISupportInitialize)pictureBox1).BeginInit(); ((Control)this).SuspendLayout(); ((Control)pictureBox1).Dock = (DockStyle)0; pictureBox1.SizeMode = (PictureBoxSizeMode)4; pictureBox1.TabIndex = 0; pictureBox1.TabStop = false; ((Control)pictureBox1).BackColor = Color.Black; ((Control)labelBot).AutoSize = false; ((Control)labelBot).BackColor = Color.FromArgb(160, 0, 0, 0); ((Control)labelBot).ForeColor = Color.White; ((Control)labelBot).Font = new Font("Segoe UI", 11f, (FontStyle)1); ((Control)labelBot).Dock = (DockStyle)2; labelBot.TextAlign = (ContentAlignment)32; ((Control)labelBot).Text = "Initializing..."; ((Control)progressBar1).Dock = (DockStyle)2; progressBar1.Style = (ProgressBarStyle)1; ((Control)progressBar1).ForeColor = Color.LimeGreen; progressBar1.Value = 0; checkedListBox1 = new CheckedListBox(); ((ListBox)checkedListBox1).BorderStyle = (BorderStyle)0; ((Control)checkedListBox1).CausesValidation = false; ((ListControl)checkedListBox1).FormattingEnabled = true; ((ObjectCollection)checkedListBox1.Items).AddRange(new object[2] { "Load and apply plugins", "Start the game" }); ((ListBox)checkedListBox1).SelectionMode = (SelectionMode)0; checkedListBox1.ThreeDCheckBoxes = true; ((ListBox)checkedListBox1).UseTabStops = false; ((Control)checkedListBox1).Visible = false; ((ContainerControl)this).AutoScaleMode = (AutoScaleMode)0; ((Control)this).Controls.Add((Control)(object)progressBar1); ((Control)this).Controls.Add((Control)(object)labelBot); ((Control)this).Controls.Add((Control)(object)pictureBox1); ((Control)this).Controls.Add((Control)(object)checkedListBox1); ((Form)this).FormBorderStyle = (FormBorderStyle)1; ((Form)this).MaximizeBox = false; ((Form)this).StartPosition = (FormStartPosition)1; ((Control)this).Name = "LoadingScreen"; ((Control)this).Text = "The game is loading..."; ((Form)this).ShowInTaskbar = true; ((ISupportInitialize)pictureBox1).EndInit(); ((Control)this).ResumeLayout(false); } } public static class Program { private static class NativeMethods { public struct RECT { public int Left; public int Top; public int Right; public int Bottom; } private static int GWL_STYLE = -16; private static int WS_BORDER = 8388608; private static int WS_DLGFRAME = 4194304; private static int WS_CAPTION = WS_BORDER | WS_DLGFRAME; private static int WS_SYSMENU = 524288; public const int GWL_HWNDPARENT = -8; [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool GetWindowRect(HandleRef hWnd, out RECT lpRect); [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)] public static extern IntPtr GetForegroundWindow(); [DllImport("user32.dll")] private static extern int GetWindowLong(IntPtr hWnd, int nIndex); public static bool IsBorderless(IntPtr windowPtr) { int windowLong = GetWindowLong(windowPtr, GWL_STYLE); return (windowLong & WS_CAPTION) == 0 && (windowLong & WS_SYSMENU) == 0; } [DllImport("user32.dll", EntryPoint = "SetWindowLongPtr")] private static extern IntPtr SetWindowLongPtr64(IntPtr hWnd, int nIndex, IntPtr dwNewLong); [DllImport("user32.dll", EntryPoint = "SetWindowLong")] private static extern IntPtr SetWindowLongPtr32(IntPtr hWnd, int nIndex, IntPtr dwNewLong); public static IntPtr SetWindowLongPtr(IntPtr hWnd, int nIndex, IntPtr dwNewLong) { if (IntPtr.Size == 8) { return SetWindowLongPtr64(hWnd, nIndex, dwNewLong); } return SetWindowLongPtr32(hWnd, nIndex, dwNewLong); } } private static SplashScreen _mainForm; private static readonly Timer _aliveTimerThreading = new Timer(OnAliveTimerElapsed, null, -1, -1); private static int _pluginCount = 10; private static int _pluginProcessedCount = 0; private static LoadEvent _lastLoadEvent = LoadEvent.None; private static readonly Dictionary<string, TimeSpan> _pluginLoadTimes = new Dictionary<string, TimeSpan>(); private static Stopwatch _pluginLoadStopwatch = new Stopwatch(); private static string _lastPluginName = string.Empty; public static string BepInExRootPath { get { string text = Application.StartupPath; int num = 0; while (text != null && num < 5) { string text2 = Path.Combine(text, "BepInEx"); if (Directory.Exists(text2)) { return text2; } text = Directory.GetParent(text)?.FullName; num++; } return ""; } } public static string SplashScreenWindowType => GetBepInExConfigValue("2. Window", "WindowType", "FakeGame"); public static string DebugGeneratePluginLoadTimeInfo => GetBepInExConfigValue("5. Other", "GenerateStartupPluginLoadTimeInfo", "false"); private static void ResetAliveTimer() { _aliveTimerThreading.Change(300000, -1); } private static void OnAliveTimerElapsed(object state) { try { Log("Stopped receiving log messages from the game, assuming preloader/chainloader has crashed or is stuck", error: true); } catch (Exception ex) { Debug.Fail(ex.ToString()); } _mainForm._closedByScript = true; Environment.Exit(3); } [STAThread] public static void Main(params string[] args) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Invalid comparison between Unknown and I4 try { Application.SetCompatibleTextRenderingDefault(false); Application.EnableVisualStyles(); if (args.Length == 0) { if ((int)MessageBox.Show("This is a splash screen that shows loading progress when a game patched with BepInEx is loading. It is automatically started and then updated by \"BepInEx.SplashScreen.Patcher.dll\" and can't be opened manually.\n\nIf you can't see a splash screen when the game is starting:\n1 - Make sure that \"LoadingScreen.GUI.exe\" and \"BepInEx.SplashScreen.Patcher.dll\" are both present inside the \"BepInEx\\patchers\" folder.\n2 - Check if the splash screen isn't disabled in \"BepInEx\\config\\Bertogim.LoadingScreen.cfg\".\n3 - Update BepInEx5 to latest version and make sure that it is running.\n4 - If the splash screen still does not appear, check the game log for any errors or exceptions. You can report issues on GitHub.\n\nDo you want to open the GitHub repository page of BepInEx.SplashScreen?", "BepInEx Loading Progress Splash Screen", (MessageBoxButtons)4, (MessageBoxIcon)48, (MessageBoxDefaultButton)256) == 6) { Process.Start("https://github.com/BepInEx/BepInEx.SplashScreen"); } return; } int processId = int.Parse(args.Last()); Process processById = Process.GetProcessById(processId); _mainForm = new SplashScreen(delegate(string message, bool error) { Log(message, error); }, processById); ((Control)_mainForm).Show(); BeginReadingInput(processById); try { ((Control)_mainForm).Text = processById.ProcessName ?? ""; if (processById.MainModule == null) { throw new FileNotFoundException("gameProcess.MainModule is null"); } string fileName = processById.MainModule.FileName; _mainForm.SetIcon((Image)(object)IconManager.GetLargeIcon(fileName, jumbo: true, checkDisk: true).ToBitmap()); BeginSnapPositionToGameWindow(processById); ResetAliveTimer(); } catch (Exception ex) { Log("Failed to get some info about the game process: " + ex, error: true); Debug.Fail(ex.ToString()); } Log("Splash screen window started successfully", error: false); Application.Run((Form)(object)_mainForm); } catch (Exception ex2) { Log("Failed to create window: " + ex2, error: true); Debug.Fail(ex2.ToString()); } } public static void Log(string message, bool error) { (error ? Console.Error : Console.Out).WriteLine(message.Replace("\t", " ").Replace('\n', '\t').Replace("\r", "")); } private static void BeginReadingInput(Process gameProcess) { Thread thread = new Thread(InputReadingThread); thread.IsBackground = true; thread.Start(gameProcess); } private static void InputReadingThread(object processArg) { try { Process process = (Process)processArg; using Stream stream = Console.OpenStandardInput(); using StreamReader streamReader = new StreamReader(stream); while (stream.CanRead && !process.HasExited) { ResetAliveTimer(); string text = streamReader.ReadLine(); if (text == null) { Thread.Sleep(100); } else { ProcessInputMessage(text); } } } catch (Exception ex) { Log(ex.ToString(), error: true); Debug.Fail(ex.ToString()); } Environment.Exit(0); } private static void RunEventsUpTo(LoadEvent targetEvent) { for (LoadEvent loadEvent = _lastLoadEvent; loadEvent < targetEvent; loadEvent++) { _mainForm.ProcessEvent(loadEvent + 1); _lastLoadEvent = targetEvent; } } public static string GetBepInExConfigValue(string section, string key, string defaultValue = "") { string text = Application.StartupPath; int num = 0; while (text != null && num < 5) { string text2 = Path.Combine(text, "BepInEx"); if (Directory.Exists(text2)) { string path = Path.Combine(text2, "config"); string path2 = Path.Combine(path, "Bertogim.LoadingScreen.cfg"); if (File.Exists(path2)) { string[] array = File.ReadAllLines(path2); bool flag = false; string[] array2 = array; foreach (string text3 in array2) { string text4 = text3.Trim(); if (text4.StartsWith("[" + section + "]")) { flag = true; continue; } if (text4.StartsWith("[") && flag) { break; } if (flag && text4.StartsWith(key)) { int num2 = text4.IndexOf('='); if (num2 > 0) { return text4.Substring(num2 + 1).Trim(); } } } } return defaultValue; } text = Directory.GetParent(text)?.FullName; num++; } return defaultValue; } private static string GenerateHtmlReport(Dictionary<string, TimeSpan> loadTimes) { TimeSpan timeSpan = loadTimes.Values.Aggregate(TimeSpan.Zero, (TimeSpan acc, TimeSpan ts) => acc.Add(ts)); string text = string.Join("\n", (from kv in loadTimes orderby kv.Value descending select $"<tr><td>{kv.Key}</td><td>{kv.Value.TotalSeconds:F3} s</td></tr>").ToArray()); string text2 = $"<tr style=\"font-weight: bold; background-color: #444;\"><td>TOTAL</td><td>{timeSpan.TotalSeconds:F3} s</td></tr>"; return "\r\n <!DOCTYPE html>\r\n <html>\r\n <head>\r\n <meta charset=\"UTF-8\">\r\n <title>Plugin Load Times</title>\r\n <style>\r\n body {\r\n font-family: Arial, sans-serif;\r\n background-color: #1e1e1e;\r\n color: #ffffff;\r\n padding: 20px;\r\n }\r\n table {\r\n width: 100%;\r\n border-collapse: collapse;\r\n background-color: #2d2d2d;\r\n }\r\n th, td {\r\n padding: 12px;\r\n text-align: left;\r\n border-bottom: 1px solid #444;\r\n }\r\n th {\r\n background-color: #3d3d3d;\r\n color: #ffffff;\r\n }\r\n tr {\r\n border-bottom: 1px solid #444;\r\n }\r\n body > table > tbody > tr:nth-child(1) {\r\n border: none;\r\n }\r\n tr:hover {\r\n background-color: #3d3d3d;\r\n }\r\n </style>\r\n </head>\r\n <body>\r\n <h2>Plugin Load Times</h2>\r\n <small>Load times can vary from startup to startup</small>\r\n <table>\r\n <tr>\r\n <th>Plugin</th>\r\n <th>Load Time</th>\r\n </tr>\r\n " + text + "\r\n " + text2 + "\r\n </table>\r\n </body>\r\n </html>"; } private static void PluginLoaded(string message) { if (string.Equals(DebugGeneratePluginLoadTimeInfo, "true", StringComparison.OrdinalIgnoreCase)) { if (!string.IsNullOrEmpty(_lastPluginName)) { _pluginLoadStopwatch.Stop(); _pluginLoadTimes[_lastPluginName] = _pluginLoadStopwatch.Elapsed; } _lastPluginName = message.Substring("Loading ".Length).Trim(); _pluginLoadStopwatch.Stop(); _pluginLoadStopwatch.Reset(); _pluginLoadStopwatch.Start(); } } private static void PluginsFinishedLoading() { if (string.Equals(DebugGeneratePluginLoadTimeInfo, "true", StringComparison.OrdinalIgnoreCase)) { if (!string.IsNullOrEmpty(_lastPluginName)) { _pluginLoadStopwatch.Stop(); _pluginLoadTimes[_lastPluginName] = _pluginLoadStopwatch.Elapsed; } string text = Path.Combine(Application.StartupPath, "Debug"); Directory.CreateDirectory(text); string path = Path.Combine(text, "PluginLoadTimes.txt"); string path2 = Path.Combine(text, "PluginLoadTimes.html"); TimeSpan timeSpan = _pluginLoadTimes.Values.Aggregate(TimeSpan.Zero, (TimeSpan acc, TimeSpan ts) => acc.Add(ts)); List<string> list = new List<string> { "Load times can vary from startup to startup", $"Total Load Time: {timeSpan.TotalSeconds:F3} s", "" }; list.AddRange(_pluginLoadTimes.Select((KeyValuePair<string, TimeSpan> kv) => $"{kv.Key}: {kv.Value.TotalMilliseconds:F2} ms").ToList()); File.WriteAllLines(path, list.ToArray()); string contents = GenerateHtmlReport(_pluginLoadTimes); File.WriteAllText(path2, contents); } } private static void ProcessInputMessage(string message) { try { switch (message) { case "Preloader started": RunEventsUpTo(LoadEvent.PreloaderStart); return; case "Preloader finished": RunEventsUpTo(LoadEvent.PreloaderFinish); return; case "Chainloader started": case "Chainloader initialized": RunEventsUpTo(LoadEvent.ChainloaderStart); return; case "Chainloader startup complete": RunEventsUpTo(LoadEvent.ChainloaderFinish); PluginsFinishedLoading(); RunEventsUpTo(LoadEvent.LoadFinished); return; } if (message.EndsWith(" patcher plugins loaded", StringComparison.Ordinal)) { RunEventsUpTo(LoadEvent.PreloaderStart); } else if (message.StartsWith("Patching ", StringComparison.Ordinal) || (message.StartsWith("Executing ", StringComparison.Ordinal) && message.EndsWith(" patch(es)", StringComparison.Ordinal))) { RunEventsUpTo(LoadEvent.PreloaderStart); _mainForm.SetStatusDetail(message); } else if (message.StartsWith("Loading ", StringComparison.Ordinal)) { RunEventsUpTo(LoadEvent.ChainloaderStart); _mainForm.SetStatusDetail(message); _pluginProcessedCount++; _mainForm.SetPluginProgress((int)Math.Round(100f * ((float)_pluginProcessedCount / (float)_pluginCount))); PluginLoaded(message); } else if (!message.StartsWith("Skipping ", StringComparison.Ordinal) && message.EndsWith(" plugins to load", StringComparison.Ordinal)) { RunEventsUpTo(LoadEvent.ChainloaderStart); _pluginCount = Math.Max(1, int.Parse(new string(message.TakeWhile(char.IsDigit).ToArray()))); } } catch (Exception arg) { Log($"Failed to process message \"{message}\": {arg}", error: true); } } private static void BeginSnapPositionToGameWindow(Process gameProcess) { Thread thread = new Thread(SnapPositionToGameWindowThread); thread.IsBackground = true; thread.Start(gameProcess); } private static void SnapPositionToGameWindowThread(object processArg) { //IL_0218: Unknown result type (might be due to invalid IL or missing references) //IL_021e: Invalid comparison between Unknown and I4 //IL_0233: Unknown result type (might be due to invalid IL or missing references) //IL_023d: Expected O, but got Unknown try { bool flag = false; Process process = (Process)processArg; while (!((Control)_mainForm).IsDisposed) { Thread.Sleep(100); if (!((Control)_mainForm).Visible && !flag) { continue; } IntPtr gameWindowHandle = process.MainWindowHandle; string mainWindowTitle = process.MainWindowTitle; if (gameWindowHandle == IntPtr.Zero || mainWindowTitle.StartsWith("BepInEx") || mainWindowTitle.StartsWith("Select BepInEx")) { if (SplashScreenWindowType == "FixedWindow") { ((Form)_mainForm).TopMost = true; } process.Refresh(); continue; } if (mainWindowTitle.EndsWith(" Configuration")) { ((Control)_mainForm).Visible = false; if (SplashScreenWindowType == "FixedWindow") { ((Form)_mainForm).TopMost = true; } flag = true; process.Refresh(); continue; } if (flag) { flag = false; ((Control)_mainForm).Visible = true; } if (!NativeMethods.GetWindowRect(new HandleRef(_mainForm, gameWindowHandle), out var lpRect)) { throw new InvalidOperationException("GetWindowRect failed :("); } IntPtr foregroundWindow = NativeMethods.GetForegroundWindow(); if (SplashScreenWindowType == "FixedWindow") { ((Form)_mainForm).TopMost = true; } if (default(NativeMethods.RECT).Equals(lpRect)) { continue; } int x = lpRect.Left + (lpRect.Right - lpRect.Left) / 2 - ((Control)_mainForm).Width / 2; int y = lpRect.Top + (lpRect.Bottom - lpRect.Top) / 2 - ((Control)_mainForm).Height / 2; Point point = new Point(x, y); if (((Form)_mainForm).Location != point) { ((Form)_mainForm).Location = point; } if ((int)((Form)_mainForm).FormBorderStyle <= 0) { continue; } ((Control)_mainForm).Invoke((Delegate)(MethodInvoker)delegate { ((Form)_mainForm).FormBorderStyle = (FormBorderStyle)0; ((Form)_mainForm).TopMost = false; ((Form)_mainForm).ShowInTaskbar = false; if (SplashScreenWindowType == "FakeGame") { NativeMethods.SetWindowLongPtr(((Control)_mainForm).Handle, -8, gameWindowHandle); } }); ((Control)_mainForm).PerformLayout(); } } catch (Exception) { Environment.Exit(1); } finally { Environment.Exit(0); } } } internal class Metadata { public const string Version = "2.2.1"; }