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.0", FrameworkDisplayName = ".NET Standard 2.0")]
[assembly: AssemblyCompany("BepInEx.GUI.Loader")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+bba91d0f08bc552e316d40748c77d3e95f9c4405")]
[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";
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 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");
}
}
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);
if (((ConfigEntry<bool>)typeof(BepInPlugin).Assembly.GetType("BepInEx.ConsoleManager", throwOnError: true).GetField("ConfigConsoleEnabled", BindingFlags.Static | BindingFlags.Public).GetValue(null)).Value)
{
Log.Info("BepInEx regular console is enabled, aborting launch.");
}
else if (Config.EnableBepInExGUIConfig.Value)
{
FindAndLaunchGUI();
}
else
{
Log.Info("Custom BepInEx.GUI is disabled in the config, aborting launch.");
}
}
private static string FindGUIExecutable()
{
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 + "\" \"" + GetLogOutputFilePath() + "\" \"" + Config.ConfigFilePath + "\" " + $"\"{Process.GetCurrentProcess().Id}\" " + $"\"{socketPort}\"";
return Process.Start(processStartInfo);
}
private static string GetLogOutputFilePath()
{
foreach (ILogListener listener in Logger.Listeners)
{
DiskLogListener val = (DiskLogListener)(object)((listener is DiskLogListener) ? listener : null);
if (val != null)
{
return val.FileFullPath;
}
}
return "";
}
}
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);
}
}
}
}