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 REPO SplashScreen v1.0.0
BepInEx/patchers/REPO.SplashScreen/BepInEx.SplashScreen.Patcher.BepInEx5.dll
Decompiled 6 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.IgnoreSymbolStoreSequencePoints)] [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("3.0.0.0")] namespace BepInEx.SplashScreen; public static class BepInExSplashScreenPatcher { public const string Version = "3.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() { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Expected O, but got Unknown //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Expected O, but got Unknown //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Expected O, but got Unknown if (Interlocked.Exchange(ref _initialized, 1) != 1) { string text = Path.Combine(Paths.ConfigPath, "BepInEx.SplashScreen.cfg"); BepInPlugin val = new BepInPlugin("BepInEx.SplashScreen.Patcher", "BepInEx.SplashScreen", "3.0"); ConfigFile config = new ConfigFile(text, false, val); ConfigFile coreConfig = (ConfigFile)AccessTools.Property(typeof(ConfigFile), "CoreConfig").GetValue(null, null); SplashScreenController.SpawnSplash(config, coreConfig); } } } internal class Metadata { public const string Version = "3.0"; } internal sealed class LoadingLogListener : ILogListener, IDisposable { 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() { for (int i = 0; i < 1; i++) { yield return null; } SplashScreenController.KillSplash(); } } 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(ConfigFile config, ConfigFile coreConfig) { try { if (config == null) { throw new ArgumentNullException("config"); } bool value = config.Bind<bool>("General", "Enabled", true, "Display a splash screen with information about game load progress on game start-up.").Value; bool value2 = config.Bind<bool>("General", "OnlyNoConsole", true, "Only display the splash screen if the logging console is turned off.").Value; ConfigEntry<bool> val = config.Bind<bool>("General", "RenameExe", true, "Automatically rename the splash .exe file to GameName.SplashScreen.GUI.dll to prevent Discord from mistaking the game as a different game."); if (!value) { Logger.LogDebug((object)"Not showing splash because the Enabled setting is off"); return; } ConfigEntry<bool> val2 = default(ConfigEntry<bool>); if (value2 && coreConfig.TryGetEntry<bool>("Logging.Console", "Enabled", ref val2) && val2.Value) { Logger.LogDebug((object)"Not showing splash because the console is enabled"); return; } Process currentProcess = Process.GetCurrentProcess(); string text = currentProcess.ProcessName + ".SplashScreen.GUI.exe"; string text2 = "BepInEx.SplashScreen.GUI.exe"; string location = typeof(SplashScreenController).Assembly.Location; string path = Path.GetDirectoryName(location) ?? Path.Combine(Paths.PatcherPluginPath, "BepInEx.SplashScreen"); string text3 = Path.Combine(path, val.Value ? text : text2); if (!File.Exists(text3)) { string text4 = Path.Combine(path, (!val.Value) ? text : text2); if (!File.Exists(text4)) { throw new FileNotFoundException("Executable not found or inaccessible at " + text3); } File.Move(text4, text3); } Logger.Log((LogLevel)32, (object)("Starting GUI process: " + text3)); ProcessStartInfo startInfo = new ProcessStartInfo(text3, currentProcess.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(); } } internal static void SendMessage(string message) { _StatusQueue.Enqueue(message); } private static void CommunicationThread(object processArg) { try { Thread.Sleep(100); Process process = (Process)processArg; process.Exited += delegate { KillSplash(); }; process.OutputDataReceived += delegate(object sender, DataReceivedEventArgs args) { if (args.Data != null) { Logger.Log((LogLevel)32, (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; try { Logger.LogDebug((object)"Connected to the GUI process"); } catch (InvalidOperationException) { Thread.Sleep(50); 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); } } }