using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Threading;
using BepInEx.Configuration;
using BepInEx.Logging;
using Microsoft.CodeAnalysis;
using Mono.Cecil;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("BepInEx.GUI.Loader")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+739e8e02dc687f121737a12a073f0d42c36b66e9")]
[assembly: AssemblyProduct("BepInEx.GUI.Loader")]
[assembly: AssemblyTitle("BepInEx.GUI.Loader")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
internal sealed class RefSafetyRulesAttribute : Attribute
{
public readonly int Version;
public RefSafetyRulesAttribute(int P_0)
{
Version = P_0;
}
}
}
namespace BepInEx.GUI.Loader
{
public class CloseProcessOnChainloaderDone : ILogListener, IDisposable
{
private bool _disposed;
private Process _process;
public CloseProcessOnChainloaderDone(Process process)
{
_process = process;
}
public void Dispose()
{
_disposed = true;
}
public void LogEvent(object sender, LogEventArgs eventArgs)
{
//IL_0021: Unknown result type (might be due to invalid IL or missing references)
//IL_0026: Unknown result type (might be due to invalid IL or missing references)
if (!_disposed && eventArgs.Data.ToString() == "Chainloader startup complete")
{
LogLevel level = eventArgs.Level;
if (((object)(LogLevel)(ref level)).Equals((object)(LogLevel)8) && Config.CloseWindowWhenGameLoadedConfig.Value)
{
Log.Message("Closing BepInEx.GUI");
KillBepInExGUIProcess();
}
}
}
private void KillBepInExGUIProcess()
{
try
{
_process.Kill();
}
catch (Exception ex)
{
Log.Error($"Error while trying to kill BepInEx GUI Process: {ex}");
Log.Error(ex);
}
finally
{
SendLogToClientSocket.Instance.Dispose();
Dispose();
}
}
}
internal static class Config
{
internal const string FileName = "BepInEx.GUI.cfg";
internal const string EnableBepInExGUIConfigKey = "Enable BepInEx GUI";
internal const string EnableBepInExGUIConfigDescription = "Enable the custom BepInEx GUI";
internal const string CloseWindowWhenGameLoadedConfigKey = "Close Window When Game Loaded";
internal const string CloseWindowWhenGameLoadedConfigDescription = "Close the graphic user interface window when the game is loaded";
internal const string CloseWindowWhenGameClosesConfigKey = "Close Window When Game Closes";
internal const string CloseWindowWhenGameClosesConfigDescription = "Close the graphic user interface window when the game closes";
private const string Disclaimer = "\nDO NOT TOUCH OR CHANGE UNLESS YOU KNOW WHAT YOU ARE DOING";
internal const string AutherNameConfigKey = "Auther Name";
internal const string AutherNameConfigDescription = "To Change The Auther Name For Finding the Executable\nDO NOT TOUCH OR CHANGE UNLESS YOU KNOW WHAT YOU ARE DOING";
internal const string ThunderstoreModNameConfigKey = "Thunderstore Mod Name";
internal const string ThunderstoreModNameConfigDescription = "To Change The Thunderstore Mod Name For Finding the Executable\nDO NOT TOUCH OR CHANGE UNLESS YOU KNOW WHAT YOU ARE DOING";
internal static string ConfigFilePath { get; private set; }
private static ConfigFile File { get; set; }
internal static ConfigEntry<bool> EnableBepInExGUIConfig { get; private set; }
internal static ConfigEntry<bool> CloseWindowWhenGameLoadedConfig { get; private set; }
internal static ConfigEntry<bool> CloseWindowWhenGameClosesConfig { get; private set; }
internal static ConfigEntry<string> AutherNameConfig { get; private set; }
internal static ConfigEntry<string> ThunderstoreModNameConfig { get; private set; }
internal static void Init(string folderFullPath)
{
//IL_0016: Unknown result type (might be due to invalid IL or missing references)
//IL_0020: Expected O, but got Unknown
ConfigFilePath = Path.Combine(folderFullPath, "BepInEx.GUI.cfg");
File = new ConfigFile(ConfigFilePath, true);
EnableBepInExGUIConfig = File.Bind<bool>("Settings", "Enable BepInEx GUI", true, "Enable the custom BepInEx GUI");
CloseWindowWhenGameLoadedConfig = File.Bind<bool>("Settings", "Close Window When Game Loaded", false, "Close the graphic user interface window when the game is loaded");
CloseWindowWhenGameClosesConfig = File.Bind<bool>("Settings", "Close Window When Game Closes", true, "Close the graphic user interface window when the game closes");
AutherNameConfig = File.Bind<string>("Auther name", "Auther Name", "CatsArmy", "To Change The Auther Name For Finding the Executable\nDO NOT TOUCH OR CHANGE UNLESS YOU KNOW WHAT YOU ARE DOING");
ThunderstoreModNameConfig = File.Bind<string>("Thunderstore mod name", "Thunderstore Mod Name", "BepInEx_GUI", "To Change The Thunderstore Mod Name For Finding the Executable\nDO NOT TOUCH OR CHANGE UNLESS YOU KNOW WHAT YOU ARE DOING");
}
}
internal static class EntryPoint
{
public static IEnumerable<string> TargetDLLs { get; } = Array.Empty<string>();
public static void Patch(AssemblyDefinition _)
{
}
public static void Initialize()
{
Log.Init();
try
{
InitializeInternal();
}
catch (Exception ex)
{
Log.Error($"Failed to initialize : ({ex.GetType()}) {ex.Message}{Environment.NewLine}{ex}");
}
}
private static void InitializeInternal()
{
Config.Init(Paths.ConfigPath);
ConfigEntry<bool> val = (ConfigEntry<bool>)typeof(BepInPlugin).Assembly.GetType("BepInEx.ConsoleManager", throwOnError: true).GetField("ConfigConsoleEnabled", BindingFlags.Static | BindingFlags.Public).GetValue(null);
if (!Config.EnableBepInExGUIConfig.Value)
{
Log.Info("Custom BepInEx.GUI is disabled in the config, aborting launch.");
}
else if (val.Value)
{
val.Value = false;
Log.Warning("Disabled old console restart game for changes to take effect");
}
else
{
FindAndLaunchGUI();
}
}
private static string FindGUIExecutable()
{
string value = Config.ThunderstoreModNameConfig.Value;
string value2 = Config.AutherNameConfig.Value;
string text = Paths.PatcherPluginPath + "\\" + value2 + "-" + value + "\\bepinex_gui.exe";
if (Path.GetFileName(text) == "bepinex_gui.exe" && FileVersionInfo.GetVersionInfo(text).FileMajorPart == 3)
{
Log.Info("Found bepinex_gui v3 executable in " + text);
return text;
}
return SearchForGuiExecuteable();
}
public static string SearchForGuiExecuteable()
{
string[] files = Directory.GetFiles(Paths.PatcherPluginPath, "*", SearchOption.AllDirectories);
foreach (string text in files)
{
if (Path.GetFileName(text) == "bepinex_gui.exe" && FileVersionInfo.GetVersionInfo(text).FileMajorPart == 3)
{
Log.Info("Found bepinex_gui v3 executable in " + text);
return text;
}
}
return null;
}
private static void FindAndLaunchGUI()
{
Log.Info("Finding and launching GUI");
string text = FindGUIExecutable();
if (text != null)
{
int num = FindFreePort();
Process process = LaunchGUI(text, num);
if (process != null)
{
Logger.Listeners.Add((ILogListener)(object)new SendLogToClientSocket(num));
Logger.Listeners.Add((ILogListener)(object)new CloseProcessOnChainloaderDone(process));
}
else
{
Log.Info("LaunchGUI failed");
}
}
else
{
Log.Info("bepinex_gui executable not found.");
}
}
private static int FindFreePort()
{
int num = 0;
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
try
{
IPEndPoint localEP = new IPEndPoint(IPAddress.Any, 0);
socket.Bind(localEP);
localEP = (IPEndPoint)socket.LocalEndPoint;
return localEP.Port;
}
finally
{
socket.Close();
}
}
private static Process LaunchGUI(string executablePath, int socketPort)
{
ProcessStartInfo processStartInfo = new ProcessStartInfo();
processStartInfo.FileName = executablePath;
processStartInfo.WorkingDirectory = Path.GetDirectoryName(executablePath);
processStartInfo.Arguments = $"\"{typeof(Paths).Assembly.GetName().Version}\" " + "\"" + Paths.ProcessName + "\" \"" + Paths.GameRootPath + "\" \"" + Paths.BepInExRootPath + "\\LogOutput.log\" \"" + Config.ConfigFilePath + "\" " + $"\"{Process.GetCurrentProcess().Id}\" " + $"\"{socketPort}\"";
return Process.Start(processStartInfo);
}
}
internal static class Log
{
private static ManualLogSource _logSource;
internal static void Init()
{
_logSource = Logger.CreateLogSource("BepInEx.GUI.Loader");
}
internal static void Debug(object data)
{
_logSource.LogDebug(data);
}
internal static void Error(object data)
{
_logSource.LogError(data);
}
internal static void Fatal(object data)
{
_logSource.LogFatal(data);
}
internal static void Info(object data)
{
_logSource.LogInfo(data);
}
internal static void Message(object data)
{
_logSource.LogMessage(data);
}
internal static void Warning(object data)
{
_logSource.LogWarning(data);
}
}
internal struct LogPacket
{
internal byte[] Bytes;
internal unsafe LogPacket(LogEventArgs log)
{
//IL_0047: Unknown result type (might be due to invalid IL or missing references)
//IL_004d: Expected I4, but got Unknown
byte[] bytes = Encoding.UTF8.GetBytes(((object)log).ToString());
int num = bytes.Length;
Bytes = new byte[8 + num];
fixed (byte* ptr = Bytes)
{
*(int*)ptr = num;
*(int*)(ptr + 4) = (int)log.Level;
Marshal.Copy(bytes, 0, (IntPtr)(ptr + 8), num);
}
}
}
internal class SendLogToClientSocket : ILogListener, IDisposable
{
private int _freePort;
private readonly Thread _thread;
private readonly object _queueLock = new object();
private readonly Queue<LogEventArgs> _logQueue = new Queue<LogEventArgs>();
private bool _isDisposed;
private bool _gotFirstLog;
internal static SendLogToClientSocket Instance { get; private set; }
internal SendLogToClientSocket(int freePort)
{
Instance = this;
_freePort = freePort;
_thread = new Thread((ThreadStart)delegate
{
TcpListener tcpListener = new TcpListener(IPAddress.Parse("127.0.0.1"), _freePort);
tcpListener.Start();
while (true)
{
Log.Info("[SendLogToClient] Accepting Socket.");
Socket clientSocket = tcpListener.AcceptSocket();
if (_isDisposed)
{
break;
}
SendPacketsToClientUntilConnectionIsClosed(clientSocket);
}
});
_thread.Start();
}
private void SendPacketsToClientUntilConnectionIsClosed(Socket clientSocket)
{
while (!_isDisposed)
{
while (_logQueue.Count > 0)
{
LogEventArgs log;
lock (_queueLock)
{
log = _logQueue.Peek();
}
LogPacket logPacket = new LogPacket(log);
try
{
clientSocket.Send(logPacket.Bytes);
}
catch (Exception arg)
{
Log.Error($"Error while trying to send log to socket: {arg}{Environment.NewLine}Disconnecting socket.");
return;
}
lock (_queueLock)
{
_logQueue.Dequeue();
}
}
Thread.Sleep(17);
}
}
public void Dispose()
{
_isDisposed = true;
}
internal void StoreLog(LogEventArgs eventArgs)
{
lock (_queueLock)
{
_logQueue.Enqueue(eventArgs);
}
}
public void LogEvent(object sender, LogEventArgs eventArgs)
{
//IL_001b: Unknown result type (might be due to invalid IL or missing references)
//IL_0021: Invalid comparison between Unknown and I4
if (_isDisposed || eventArgs.Data == null)
{
return;
}
if (!_gotFirstLog)
{
if ((int)eventArgs.Level == 8 && eventArgs.Source.SourceName == "BepInEx" && eventArgs.Data.ToString().StartsWith("BepInEx"))
{
_gotFirstLog = true;
}
}
else
{
StoreLog(eventArgs);
}
}
}
}