Decompiled source of TheGarbageCollector v1.4.4

TheGarbageCollector.dll

Decompiled 4 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using BepInEx;
using Dungeonator;
using MonoMod.RuntimeDetour;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("TheGarbageCollector")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Apache Modding Inc.")]
[assembly: AssemblyProduct("TheGarbageCollector")]
[assembly: AssemblyCopyright("Copyright ©  2025")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("42932dec-6cbb-4428-aab7-35c7bee8110b")]
[assembly: AssemblyFileVersion("1.4.4.0")]
[assembly: AssemblyVersion("1.4.4.0")]
namespace TheGarbageCollector;

public class AudioResourceLoader
{
	public static uint LoadedSoundBankID = 0u;

	public static readonly string BankName = "GCSFX.bnk";

	public static bool InitAudio()
	{
		return LoadBankFromModProject("TheGarbageCollector." + BankName, out LoadedSoundBankID);
	}

	public static bool LoadBankFromModProject(string path, out uint bankid)
	{
		path = path.Replace("/", ".").Replace("\\", ".");
		if (!path.EndsWith(".bnk"))
		{
			path += ".bnk";
		}
		Assembly callingAssembly = Assembly.GetCallingAssembly();
		bankid = 0u;
		using (Stream stream = callingAssembly.GetManifestResourceStream(path))
		{
			if (stream != null)
			{
				string text = path.Substring(0, path.LastIndexOf('.'));
				string bankName = path;
				if (text.LastIndexOf('.') >= 0)
				{
					bankName = text.Substring(text.LastIndexOf('.') + 1);
				}
				return LoadSoundbankFromStream(stream, bankName, out bankid);
			}
		}
		return false;
	}

	private static bool LoadSoundbankFromStream(Stream stream, string bankName, out uint bankID)
	{
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		//IL_0028: Unknown result type (might be due to invalid IL or missing references)
		//IL_002d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0037: Unknown result type (might be due to invalid IL or missing references)
		//IL_0039: Invalid comparison between Unknown and I4
		byte[] array = StreamToByteArray(stream);
		IntPtr intPtr = Marshal.AllocHGlobal(array.Length);
		AKRESULT val = (AKRESULT)2;
		bankID = 0u;
		try
		{
			Marshal.Copy(array, 0, intPtr, array.Length);
			val = AkSoundEngine.LoadAndDecodeBankFromMemory(intPtr, (uint)array.Length, false, bankName, false, ref bankID);
		}
		finally
		{
			Marshal.FreeHGlobal(intPtr);
		}
		return (int)val == 1;
	}

	private static byte[] StreamToByteArray(Stream input)
	{
		byte[] array = new byte[16384];
		using MemoryStream memoryStream = new MemoryStream();
		int count;
		while ((count = input.Read(array, 0, array.Length)) > 0)
		{
			memoryStream.Write(array, 0, count);
		}
		return memoryStream.ToArray();
	}
}
public class GCFoyer : BraveBehaviour
{
	private enum State
	{
		PreFoyerCheck,
		EnableMod,
		Exit
	}

	private State m_State;

	public GCFoyer()
	{
		m_State = State.PreFoyerCheck;
	}

	public void Awake()
	{
	}

	public void Start()
	{
	}

	public void Update()
	{
		switch (m_State)
		{
		case State.PreFoyerCheck:
			if (!Foyer.DoIntroSequence || !Foyer.DoMainMenu)
			{
				m_State = State.EnableMod;
			}
			break;
		case State.EnableMod:
		{
			int @int = PlayerPrefs.GetInt(TheGarbageCollector.GarbageCollectorMemoryCap);
			if (@int >= 1024)
			{
				GC_Manager.MemoryGrowthAllowence = @int;
				if (@int >= 8196)
				{
					TheGarbageCollector.disableMonitor = true;
				}
			}
			else
			{
				GC_Manager.MemoryGrowthAllowence = TheGarbageCollector.GetBestMemoryCap;
				if (SystemInfo.systemMemorySize > 24576)
				{
					TheGarbageCollector.disableMonitor = true;
				}
			}
			if (PlayerPrefs.GetInt(TheGarbageCollector.GarbageCollectorToggleName) == 1)
			{
				TheGarbageCollector.DisableGC = false;
				break;
			}
			if (TheGarbageCollector.DisableGC)
			{
				ETGModConsole.Log((object)(TheGarbageCollector.ModNameInRed + "TheGarbageCollector is currently enabled. Use command 'garbagecolletor toggle' disable the mod."), false);
				if (GC_Manager.load_mono_gc())
				{
					TheGarbageCollector.ToggleHooksAndGC(state: true);
					if (SystemInfo.systemMemorySize < 8196)
					{
						ETGModConsole.Log((object)(TheGarbageCollector.ModNameInRed + "Warning: Your computer was detected as having 8GB or less ram. It is recommended only to enable this mod on machines with more then 8GB of ram!"), true);
					}
				}
			}
			else
			{
				ETGModConsole.Log((object)(TheGarbageCollector.ModNameInRed + "TheGarbageCollector is currently disabled. Use command 'garbagecolletor toggle' to enable TheGarbageCollector and disable Unity's GarbageCollector."), false);
			}
			m_State = State.Exit;
			break;
		}
		case State.Exit:
			if (Object.op_Implicit((Object)(object)((Component)this).gameObject))
			{
				Object.Destroy((Object)(object)((Component)this).gameObject);
			}
			break;
		}
	}

	protected override void OnDestroy()
	{
		((BraveBehaviour)this).OnDestroy();
	}
}
public class GCStats : MonoBehaviour
{
	public static GameObject GCStatsObject;

	public bool ShowGcData;

	public Vector3 UIPosition;

	public StringBuilder stringBuilder;

	private dfLabel m_label;

	private bool IsRedText;

	private bool WaitingForCollection;

	private Color32 WhiteColor;

	private Color32 RedColor;

	private float AllocationLimit;

	public static GCStats Instance
	{
		get
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Expected O, but got Unknown
			if (!Object.op_Implicit((Object)(object)GCStatsObject))
			{
				GCStatsObject = new GameObject("GCStats")
				{
					layer = 14
				};
				Object.DontDestroyOnLoad((Object)(object)GCStatsObject);
			}
			return GameObjectExtensions.GetOrAddComponent<GCStats>(GCStatsObject);
		}
	}

	public GCStats()
	{
		//IL_001d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0022: Unknown result type (might be due to invalid IL or missing references)
		//IL_0038: Unknown result type (might be due to invalid IL or missing references)
		//IL_003d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0057: Unknown result type (might be due to invalid IL or missing references)
		//IL_005c: Unknown result type (might be due to invalid IL or missing references)
		ShowGcData = false;
		UIPosition = new Vector3(-710f, 130f, 0f);
		RedColor = new Color32((byte)190, (byte)160, (byte)0, byte.MaxValue);
		WhiteColor = new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue);
		IsRedText = false;
		WaitingForCollection = false;
	}

	private void SetupLabel()
	{
		//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
		//IL_0103: Unknown result type (might be due to invalid IL or missing references)
		//IL_0138: Unknown result type (might be due to invalid IL or missing references)
		//IL_0151: Unknown result type (might be due to invalid IL or missing references)
		//IL_0156: Unknown result type (might be due to invalid IL or missing references)
		//IL_015b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0173: Unknown result type (might be due to invalid IL or missing references)
		//IL_0178: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a3: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
		//IL_01fd: Unknown result type (might be due to invalid IL or missing references)
		//IL_0202: Unknown result type (might be due to invalid IL or missing references)
		//IL_0214: Unknown result type (might be due to invalid IL or missing references)
		//IL_0219: Unknown result type (might be due to invalid IL or missing references)
		//IL_022e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0239: Unknown result type (might be due to invalid IL or missing references)
		//IL_023e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0245: Unknown result type (might be due to invalid IL or missing references)
		//IL_024c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0253: Unknown result type (might be due to invalid IL or missing references)
		//IL_025f: Expected O, but got Unknown
		//IL_027e: Unknown result type (might be due to invalid IL or missing references)
		GameUIRoot instance = GameUIRoot.Instance;
		if (Object.op_Implicit((Object)(object)((instance != null) ? instance.Manager : null)))
		{
			AssetBundle obj = ResourceManager.LoadAssetBundle("shared_auto_001");
			dfTiledSprite component = obj.LoadAsset<GameObject>("Weapon Skull Ammo FG").GetComponent<dfTiledSprite>();
			dfFont component2 = obj.LoadAsset<GameObject>("04b03_df40").GetComponent<dfFont>();
			dfLabel val = GameUIRoot.Instance.Manager.AddControl<dfLabel>();
			val.Atlas = ((dfSprite)component).Atlas;
			val.Font = (dfFontBase)(object)component2;
			((dfControl)val).Anchor = (dfAnchorStyle)6;
			((dfControl)val).IsEnabled = true;
			((dfControl)val).IsVisible = false;
			((dfControl)val).IsInteractive = true;
			((dfControl)val).Tooltip = string.Empty;
			((dfControl)val).Pivot = (dfPivotPoint)6;
			((dfControl)val).zindex = 29;
			((dfControl)val).Opacity = 0.5f;
			((dfControl)val).Color = Color32.op_Implicit(Color.white);
			((dfControl)val).DisabledColor = new Color32((byte)128, (byte)128, (byte)128, byte.MaxValue);
			((dfControl)val).Size = new Vector2(350f, 120f);
			((dfControl)val).MinimumSize = ((dfControl)val).Size;
			((dfControl)val).MaximumSize = new Vector2(400f, 240f);
			((dfControl)val).ClipChildren = false;
			((dfControl)val).InverseClipChildren = false;
			((dfControl)val).TabIndex = -1;
			((dfControl)val).CanFocus = false;
			((dfControl)val).AutoFocus = false;
			((dfControl)val).IsLocalized = false;
			((dfControl)val).HotZoneScale = Vector2.one;
			((dfControl)val).AllowSignalEvents = true;
			((dfControl)val).PrecludeUpdateCycle = false;
			val.PerCharacterOffset = Vector2.op_Implicit(Vector2.zero);
			val.PreventFontChanges = true;
			val.BackgroundSprite = string.Empty;
			val.BackgroundColor = Color32.op_Implicit(Color.white);
			val.AutoSize = true;
			val.AutoHeight = false;
			val.WordWrap = false;
			val.Text = "PLACEHOLDER";
			val.BottomColor = Color32.op_Implicit(Color.white);
			val.TextAlignment = (TextAlignment)0;
			val.VerticalAlignment = (dfVerticalAlignment)0;
			val.TextScale = 0.5f;
			val.TextScaleMode = (dfTextScaleMode)0;
			val.CharacterSpacing = 0;
			val.ColorizeSymbols = false;
			val.ProcessMarkup = false;
			val.Outline = false;
			val.OutlineSize = 0;
			val.ShowGradient = false;
			val.OutlineColor = Color32.op_Implicit(Color.black);
			val.Shadow = false;
			val.ShadowColor = Color32.op_Implicit(Color.black);
			val.ShadowOffset = new Vector2(1f, -1f);
			val.Padding = new RectOffset
			{
				left = 0,
				right = 0,
				top = 0,
				bottom = 0
			};
			val.TabSize = 48;
			val.MaintainJapaneseFont = false;
			val.MaintainKoreanFont = false;
			val.MaintainRussianFont = false;
			((dfControl)val).Position = UIPosition;
			m_label = val;
			component2 = null;
			component = null;
		}
	}

	private void Start()
	{
		stringBuilder = new StringBuilder(500);
		AllocationLimit = GC_Manager.MemoryGrowthAllowence;
	}

	protected void Update()
	{
		//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
		if (ShowGcData && GC_Manager.d_gc_disabled)
		{
			if (!Object.op_Implicit((Object)(object)m_label))
			{
				SetupLabel();
			}
			if (!Object.op_Implicit((Object)(object)m_label))
			{
				return;
			}
			if (!((dfControl)m_label).IsVisible)
			{
				((dfControl)m_label).IsVisible = true;
			}
			if (GC_Manager.Instance.AllocatedMemorySinceLastCollection != -1)
			{
				AllocationLimit = Mathf.Max(AllocationLimit, (float)GC_Manager.Instance.AllocatedMemorySinceLastCollection);
			}
			if ((float)GC_Manager.GetTotalMemoryAllocatedInMB >= AllocationLimit)
			{
				if (!WaitingForCollection)
				{
					WaitingForCollection = true;
				}
				if (!IsRedText)
				{
					((dfControl)m_label).Color = RedColor;
					IsRedText = true;
				}
			}
			else
			{
				if (WaitingForCollection)
				{
					WaitingForCollection = false;
				}
				if (IsRedText)
				{
					((dfControl)m_label).Color = WhiteColor;
					IsRedText = false;
				}
			}
			stringBuilder.Length = 0;
			stringBuilder.AppendFormat("Current Allocation: {0:0} MB / {1:0} MB\n", GC_Manager.GetTotalMemoryAllocatedInMB, AllocationLimit);
			stringBuilder.AppendFormat("Current Heap Size: {0:0} MB\n", ProfileUtils.GetMonoHeapSize() / 1024 / 1024);
			stringBuilder.AppendFormat("Time Since Last In Combat: {0: 00} sec\n", Time.realtimeSinceStartup - GC_Manager.Instance.LastInCombatTime);
			if (Time.realtimeSinceStartup - GC_Manager.Instance.LastUnpausedTime > 0.1f)
			{
				stringBuilder.AppendFormat("Time Spent In Pause Screen: {0: 00} sec\n", Time.realtimeSinceStartup - GC_Manager.Instance.LastUnpausedTime);
			}
			stringBuilder.AppendFormat("Time Since Last Collection: {0: 00} sec\n", Time.realtimeSinceStartup - GC_Manager.Instance.LastCollectionTime);
			stringBuilder.AppendFormat("Total Collections: {0}\n", ProfileUtils.GetMonoCollectionCount());
			if (WaitingForCollection)
			{
				stringBuilder.Append("Waiting to do a Collection...\n");
			}
			else if (TheGarbageCollector.disableMonitor)
			{
				stringBuilder.Append("GC Monitor is Disabled.\n");
			}
			m_label.Text = stringBuilder.ToString();
		}
		else if (!ShowGcData && GC_Manager.d_gc_disabled && Object.op_Implicit((Object)(object)m_label) && ((dfControl)m_label).IsVisible)
		{
			((dfControl)m_label).IsVisible = false;
		}
	}
}
public class GC_Manager : MonoBehaviour
{
	public static FieldInfo LastGcTime = typeof(BraveMemory).GetField("LastGcTime", BindingFlags.Static | BindingFlags.NonPublic);

	private static FieldInfo m_stringBuilder = typeof(HUDGC).GetField("stringBuilder", BindingFlags.Instance | BindingFlags.NonPublic);

	public static int offset_mono_gc_disable = 111376;

	public static int offset_mono_gc_enable = 111384;

	public static int offset_mono_gc_collect = 111300;

	public static int MemoryGrowthAllowence = 2048;

	public long AllocatedMemorySinceLastCollection;

	public static Action mono_gc_disable;

	public static Action mono_gc_enable;

	public static Action mono_gc_collect;

	public static bool d_gc_disabled = false;

	private static bool mono_gc_loaded = false;

	public bool turn_off_mono_gc;

	public bool CanDoCollectionNow;

	private bool DoManualCollection;

	public float PauseTimeTillManualCollection;

	public float OutOfCombatTimeTillManualCollection;

	public float LastUnpausedTime;

	public float LastInCombatTime;

	public float LastCollectionTime;

	private float AllocationLimit;

	private int[] dummy_object;

	public static GC_Manager Instance
	{
		get
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Expected O, but got Unknown
			GameObject gCManagerObject = TheGarbageCollector.GCManagerObject;
			if (!Object.op_Implicit((Object)(object)((gCManagerObject != null) ? gCManagerObject.GetComponent<GC_Manager>() : null)) && !Object.op_Implicit((Object)(object)TheGarbageCollector.GCManagerObject))
			{
				TheGarbageCollector.GCManagerObject = new GameObject("TheGarbageCollector");
				Object.DontDestroyOnLoad((Object)(object)TheGarbageCollector.GCManagerObject);
			}
			return GameObjectExtensions.GetOrAddComponent<GC_Manager>(TheGarbageCollector.GCManagerObject);
		}
	}

	public static long GetTotalMemoryAllocatedInMB => GC.GetTotalMemory(forceFullCollection: false) / 1024 / 1024;

	public GC_Manager()
	{
		turn_off_mono_gc = false;
		LastInCombatTime = 0f;
		LastUnpausedTime = 0f;
		DoManualCollection = false;
		CanDoCollectionNow = false;
		PauseTimeTillManualCollection = 120f;
		OutOfCombatTimeTillManualCollection = 300f;
		AllocatedMemorySinceLastCollection = -1L;
	}

	[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
	public static extern IntPtr GetModuleHandle(string lpModuleName);

	[DllImport("kernel32.dll", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
	public static extern IntPtr GetProcAddress(IntPtr hModule, string procName);

	public static bool load_mono_gc()
	{
		if (mono_gc_loaded)
		{
			return true;
		}
		IntPtr moduleHandle = GetModuleHandle("mono.dll");
		IntPtr intPtr = new IntPtr(moduleHandle.ToInt64() + offset_mono_gc_collect);
		IntPtr procAddress = GetProcAddress(moduleHandle, "mono_gc_collect");
		if (intPtr != procAddress)
		{
			ETGModConsole.Log((object)("[TheGarbageCollector] Cannot load GarbageCollector functions. Expected mono's collect at " + intPtr.ToInt64() + " Actual at " + intPtr.ToInt64() + " Module root " + moduleHandle.ToInt64()), true);
			return false;
		}
		mono_gc_enable = (Action)Marshal.GetDelegateForFunctionPointer(new IntPtr(moduleHandle.ToInt64() + offset_mono_gc_enable), typeof(Action));
		mono_gc_disable = (Action)Marshal.GetDelegateForFunctionPointer(new IntPtr(moduleHandle.ToInt64() + offset_mono_gc_disable), typeof(Action));
		mono_gc_collect = (Action)Marshal.GetDelegateForFunctionPointer(new IntPtr(moduleHandle.ToInt64() + offset_mono_gc_collect), typeof(Action));
		mono_gc_loaded = true;
		return true;
	}

	public void Disable()
	{
		mono_gc_enable();
		d_gc_disabled = false;
		turn_off_mono_gc = false;
	}

	public void Enable()
	{
		mono_gc_disable();
		d_gc_disabled = true;
		turn_off_mono_gc = true;
	}

	public void DoCollect()
	{
		if (d_gc_disabled)
		{
			Collect();
			return;
		}
		LastGcTime.SetValue(typeof(BraveMemory), Time.realtimeSinceStartup);
		GC.Collect();
	}

	private void Collect()
	{
		if (GCStats.Instance.ShowGcData && !Dungeon.IsGenerating && Object.op_Implicit((Object)(object)GameManager.Instance) && !GameManager.Instance.IsLoadingLevel && Object.op_Implicit((Object)(object)((Component)ETGModMainBehaviour.Instance).gameObject))
		{
			AkSoundEngine.PostEvent(TheGarbageCollector.ModSoundEventName, ((Component)ETGModMainBehaviour.Instance).gameObject);
		}
		int monoCollectionCount = ProfileUtils.GetMonoCollectionCount();
		mono_gc_enable();
		for (int i = 0; i < 100; i++)
		{
			dummy_object = new int[1];
			dummy_object[0] = 0;
		}
		if (ProfileUtils.GetMonoCollectionCount() == monoCollectionCount)
		{
			LastGcTime.SetValue(null, Time.realtimeSinceStartup);
			LastCollectionTime = Time.realtimeSinceStartup;
			GC.Collect();
		}
		mono_gc_disable();
		AllocatedMemorySinceLastCollection = GetTotalMemoryAllocatedInMB;
		if (GCStats.Instance.ShowGcData && !Dungeon.IsGenerating && Object.op_Implicit((Object)(object)GameManager.Instance) && !GameManager.Instance.IsLoadingLevel && GCStats.Instance.stringBuilder != null)
		{
			Debug.Log((object)"TheGarbageCollector Collection ran. Last GC Stats:");
			Debug.Log((object)GCStats.Instance.stringBuilder);
		}
	}

	private void Start()
	{
	}

	protected void Update()
	{
		if (!d_gc_disabled | Dungeon.IsGenerating | (Object.op_Implicit((Object)(object)GameManager.Instance) && GameManager.Instance.IsLoadingLevel))
		{
			return;
		}
		if (GameManager.Instance.IsPaused)
		{
			if (LastUnpausedTime > 0.1f && Time.realtimeSinceStartup - LastUnpausedTime > PauseTimeTillManualCollection)
			{
				DoManualCollection = true;
				LastUnpausedTime = Time.realtimeSinceStartup;
			}
		}
		else
		{
			DoManualCollection = false;
			LastUnpausedTime = Time.realtimeSinceStartup;
		}
		if (Object.op_Implicit((Object)(object)GameManager.Instance.PrimaryPlayer) && !GameManager.Instance.IsPaused)
		{
			if (GameManager.Instance.PrimaryPlayer.IsInCombat)
			{
				LastInCombatTime = Time.realtimeSinceStartup;
				DoManualCollection = false;
			}
			else if (LastInCombatTime > 0f && Time.realtimeSinceStartup - LastInCombatTime > OutOfCombatTimeTillManualCollection)
			{
				DoManualCollection = true;
				LastInCombatTime = Time.realtimeSinceStartup;
			}
			else
			{
				DoManualCollection = false;
			}
		}
		GameManager instance = GameManager.Instance;
		if (Object.op_Implicit((Object)(object)((instance != null) ? instance.PrimaryPlayer : null)) && GameManager.Instance.PrimaryPlayer.IsInCombat)
		{
			CanDoCollectionNow = false;
		}
		else
		{
			CanDoCollectionNow = true;
		}
		AllocationLimit = MemoryGrowthAllowence;
		if (AllocatedMemorySinceLastCollection != -1)
		{
			AllocationLimit = Mathf.Max(AllocationLimit, (float)AllocatedMemorySinceLastCollection);
		}
		if (DoManualCollection | (!TheGarbageCollector.disableMonitor && CanDoCollectionNow && (float)GetTotalMemoryAllocatedInMB >= AllocationLimit))
		{
			Collect();
			DoManualCollection = false;
			CanDoCollectionNow = false;
		}
	}
}
[BepInDependency(/*Could not decode attribute arguments.*/)]
[BepInPlugin("ApacheThunder.etg.TheGarbageCollector", "TheGarbageCollector", "1.4.4")]
public class TheGarbageCollector : BaseUnityPlugin
{
	public static bool DisableGC = true;

	public static bool disableMonitor = false;

	public const string GUID = "ApacheThunder.etg.TheGarbageCollector";

	public const string ModName = "TheGarbageCollector";

	public const string VERSION = "1.4.4";

	public static readonly string ConsoleCommandName = "garbagecollector";

	public static readonly string GarbageCollectorToggleName = "TheGarbageCollectorDisabled";

	public static readonly string GarbageCollectorMemoryCap = "TheGarbageCollectorMemCap";

	public static readonly string ModNameInRed = "<color=#00FF00>[TheGarbageCollector]</color> ";

	public static readonly string ModSoundEventName = "Play_TrashMan_01";

	public static string FolderPath;

	public static Hook BraveMemoryCollectHook;

	public static Hook clearLevelDataHook;

	public static Hook gameManagerHook;

	public static GameObject GCManagerObject;

	public static GameObject GCFoyerCheckerOBJ;

	public static int GetBestMemoryCap
	{
		get
		{
			if (SystemInfo.systemMemorySize > 24576)
			{
				return 8196;
			}
			if (SystemInfo.systemMemorySize > 12288)
			{
				return 5120;
			}
			if (SystemInfo.systemMemorySize > 8196)
			{
				return 3072;
			}
			return 2048;
		}
	}

	public void Start()
	{
		ETGModMainBehaviour.WaitForGameManagerStart((Action<GameManager>)GMStart);
	}

	public void GMStart(GameManager gameManager)
	{
		//IL_000b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0011: Invalid comparison between Unknown and I4
		FolderPath = ETGMod.FolderPath((BaseUnityPlugin)(object)this);
		if ((int)Application.platform != 2)
		{
			ETGModConsole.Log((object)(ModNameInRed + "ERROR: Linux/MacOS/Other version of the game detected!. This mod only supports the Windows version of ETG!"), false);
			return;
		}
		if (!AudioResourceLoader.InitAudio())
		{
			ETGModConsole.Log((object)(ModNameInRed + "WARNING: Failed to load custom sound bank!"), false);
		}
		ETGModConsole.Commands.AddGroup(ConsoleCommandName, (Action<string[]>)ConsoleInfo);
		ETGModConsole.Commands.GetGroup(ConsoleCommandName).AddUnit("toggle", (Action<string[]>)ToggleGCSetting);
		ETGModConsole.Commands.GetGroup(ConsoleCommandName).AddUnit("collect", (Action<string[]>)DoACollect);
		ETGModConsole.Commands.GetGroup(ConsoleCommandName).AddUnit("stats", (Action<string[]>)ToggleGCStats);
		ETGModConsole.Commands.GetGroup(ConsoleCommandName).AddUnit("setmemcap", (Action<string[]>)SetMemoryCap);
		CreateFoyerController();
	}

	public static void CreateFoyerController()
	{
		//IL_0024: Unknown result type (might be due to invalid IL or missing references)
		//IL_002e: Expected O, but got Unknown
		if (!Object.op_Implicit((Object)(object)GCFoyerCheckerOBJ))
		{
			GCFoyerCheckerOBJ = new GameObject("TheGarbageCollector Foyer Checker", new Type[1] { typeof(GCFoyer) });
		}
	}

	private void GameManager_Awake(Action<GameManager> orig, GameManager self)
	{
		orig(self);
		self.OnNewLevelFullyLoaded += OnLevelFullyLoaded;
	}

	public static void OnLevelFullyLoaded()
	{
		//IL_0057: Unknown result type (might be due to invalid IL or missing references)
		//IL_0061: Expected O, but got Unknown
		if (DisableGC && Object.op_Implicit((Object)(object)GC_Manager.Instance) && GC_Manager.d_gc_disabled)
		{
			if (gameManagerHook == null)
			{
				gameManagerHook = new Hook((MethodBase)typeof(GameManager).GetMethod("Awake", BindingFlags.Instance | BindingFlags.NonPublic), typeof(TheGarbageCollector).GetMethod("GameManager_Awake", BindingFlags.Instance | BindingFlags.NonPublic), (object)typeof(GameManager));
			}
			GC_Manager.Instance.DoCollect();
		}
	}

	private void ConsoleInfo(string[] consoleText)
	{
		if (ETGModConsole.Commands.GetGroup(ConsoleCommandName) == null || ETGModConsole.Commands.GetGroup(ConsoleCommandName).GetAllUnitNames() == null)
		{
			return;
		}
		List<string> list = new List<string>();
		string[] allUnitNames = ETGModConsole.Commands.GetGroup(ConsoleCommandName).GetAllUnitNames();
		foreach (string item in allUnitNames)
		{
			list.Add(item);
		}
		if (list.Count <= 0)
		{
			return;
		}
		if (!m_IsCommandValid(consoleText, string.Empty, string.Empty))
		{
			ETGModConsole.Log((object)"[TheGarbageCollector] No sub command specified! The following console commands are available for TheGarbageCollector:\n", false);
			{
				foreach (string item2 in list)
				{
					ETGModConsole.Log((object)("    " + item2 + "\n"), false);
				}
				return;
			}
		}
		if (list.Contains(consoleText[0].ToLower()))
		{
			return;
		}
		ETGModConsole.Log((object)"[TheGarbageCollector] Invalid sub-command! The following console commands are available for TheGarbageCollector:\n", false);
		foreach (string item3 in list)
		{
			ETGModConsole.Log((object)("    " + item3 + "\n"), false);
		}
	}

	private bool m_IsCommandValid(string[] CommandText, string validCommands, string sourceSubCommand)
	{
		if (CommandText == null)
		{
			if (!string.IsNullOrEmpty(validCommands) && !string.IsNullOrEmpty(sourceSubCommand))
			{
				ETGModConsole.Log((object)("[TheGarbageCollector] [" + sourceSubCommand + "] ERROR: Invalid console command specified! Valid Sub-Commands: \n" + validCommands), false);
			}
			return false;
		}
		if (CommandText.Length == 0)
		{
			if (!string.IsNullOrEmpty(validCommands) && !string.IsNullOrEmpty(sourceSubCommand))
			{
				ETGModConsole.Log((object)("[TheGarbageCollector] [" + sourceSubCommand + "] No sub-command specified. Valid Sub-Commands: \n" + validCommands), false);
			}
			return false;
		}
		if (string.IsNullOrEmpty(CommandText[0]))
		{
			if (!string.IsNullOrEmpty(validCommands) && !string.IsNullOrEmpty(sourceSubCommand))
			{
				ETGModConsole.Log((object)("[TheGarbageCollector] [" + sourceSubCommand + "] No sub-command specified. Valid Sub-Commands: \n" + validCommands), false);
			}
			return false;
		}
		if (CommandText.Length > 1)
		{
			if (!string.IsNullOrEmpty(validCommands) && !string.IsNullOrEmpty(sourceSubCommand))
			{
				ETGModConsole.Log((object)("[TheGarbageCollector] [" + sourceSubCommand + "] ERROR: Only one sub-command is accepted!. Valid Commands: \n" + validCommands), false);
			}
			return false;
		}
		return true;
	}

	private void ToggleGCSetting(string[] consoleText)
	{
		if (!Object.op_Implicit((Object)(object)Foyer.Instance) | Foyer.DoIntroSequence | Foyer.DoMainMenu)
		{
			ETGModConsole.Log((object)"[TheGarbageCollector] Warning: Please wait until the foyer is loaded before trying to enable/disable the mod!", false);
			return;
		}
		if (!DisableGC)
		{
			DisableGC = true;
			if (GC_Manager.load_mono_gc())
			{
				AkSoundEngine.PostEvent(ModSoundEventName, ((Component)ETGModMainBehaviour.Instance).gameObject);
				ToggleHooksAndGC(DisableGC);
				if (SystemInfo.systemMemorySize < 8196)
				{
					ETGModConsole.Log((object)"[TheGarbageCollector] Warning: Your computer was detected as having 8GB or less ram. It is recommended only to use this feature on machines with more then 8GB of ram!", false);
				}
				ETGModConsole.Log((object)"[TheGarbageCollector] TheGarbageCollector mod is now enabled.\nNow will only do collections during floor loads and if player is AFK or been in pause menu for more then 30 seconds!", false);
			}
			PlayerPrefs.SetInt(GarbageCollectorToggleName, 0);
		}
		else
		{
			DisableGC = false;
			ToggleHooksAndGC(DisableGC);
			ETGModConsole.Log((object)"[TheGarbageCollector] TheGarbageCollector mod is now disabled.\nUnity's GC will now run normally!", false);
			PlayerPrefs.SetInt(GarbageCollectorToggleName, 1);
		}
		PlayerPrefs.Save();
	}

	private void ToggleGCStats(string[] consoleText)
	{
		if (GCStats.Instance.ShowGcData)
		{
			GCStats.Instance.ShowGcData = false;
			ETGModConsole.Log((object)(ModNameInRed + "GC Stats disabled!"), false);
		}
		else
		{
			GCStats.Instance.ShowGcData = true;
			ETGModConsole.Log((object)(ModNameInRed + "GC Stats enabled!"), false);
		}
	}

	private void DoACollect(string[] consoleText)
	{
		ETGModConsole.Log((object)"TheGarbageCollector Doing a Collection", false);
		if (Object.op_Implicit((Object)(object)GameManager.Instance) && Object.op_Implicit((Object)(object)GameManager.Instance.PrimaryPlayer))
		{
			AkSoundEngine.PostEvent(ModSoundEventName, ((Component)GameManager.Instance.PrimaryPlayer).gameObject);
		}
		else
		{
			AkSoundEngine.PostEvent(ModSoundEventName, ((Component)ETGModMainBehaviour.Instance).gameObject);
		}
		if (DisableGC)
		{
			GC_Manager.Instance.DoCollect();
		}
		else
		{
			BraveMemory.DoCollect();
		}
	}

	private void SetMemoryCap(string[] consoleText)
	{
		if (consoleText != null && consoleText.Length == 1)
		{
			int num = int.Parse(consoleText[0]);
			bool flag = false;
			if (num < 1024)
			{
				num = GetBestMemoryCap;
				flag = true;
			}
			if (num >= 8196)
			{
				disableMonitor = true;
			}
			else
			{
				disableMonitor = false;
			}
			GC_Manager.MemoryGrowthAllowence = num;
			PlayerPrefs.SetInt(GarbageCollectorMemoryCap, num);
			PlayerPrefs.Save();
			if (flag)
			{
				ETGModConsole.Log((object)(ModNameInRed + "Memory allocation limit manually set. To allow this to be auto set again, set it to a value below 1024!"), false);
			}
			else
			{
				ETGModConsole.Log((object)(ModNameInRed + "Requested memory cap is below minimum allowed value.\nMemory cap is now set to Auto based on your current system memory spec."), false);
			}
		}
		else
		{
			ETGModConsole.Log((object)(ModNameInRed + "No memory value specified. Please specify a memory limit you want to use!"), false);
		}
	}

	public static void ToggleHooksAndGC(bool state)
	{
		//IL_0043: Unknown result type (might be due to invalid IL or missing references)
		//IL_004d: Expected O, but got Unknown
		//IL_008a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0094: Expected O, but got Unknown
		if (state)
		{
			GC_Manager.Instance.Enable();
			if (BraveMemoryCollectHook == null)
			{
				BraveMemoryCollectHook = new Hook((MethodBase)typeof(BraveMemory).GetMethod("DoCollect", BindingFlags.Static | BindingFlags.Public), typeof(TheGarbageCollector).GetMethod("DoCollect", BindingFlags.Static | BindingFlags.Public));
			}
			if (clearLevelDataHook == null)
			{
				clearLevelDataHook = new Hook((MethodBase)typeof(GameManager).GetMethod("ClearPerLevelData", BindingFlags.Instance | BindingFlags.Public), typeof(TheGarbageCollector).GetMethod("ClearPerLevelDataHook", BindingFlags.Instance | BindingFlags.Public), (object)typeof(GameManager));
			}
			GameManager.Instance.OnNewLevelFullyLoaded += OnLevelFullyLoaded;
			return;
		}
		GC_Manager.Instance.Disable();
		if (BraveMemoryCollectHook != null)
		{
			BraveMemoryCollectHook.Dispose();
			BraveMemoryCollectHook = null;
		}
		if (clearLevelDataHook != null)
		{
			clearLevelDataHook.Dispose();
			clearLevelDataHook = null;
		}
		if (gameManagerHook != null)
		{
			gameManagerHook.Dispose();
			gameManagerHook = null;
		}
		GameManager.Instance.OnNewLevelFullyLoaded -= OnLevelFullyLoaded;
	}

	public void ClearPerLevelDataHook(Action<GameManager> orig, GameManager self)
	{
		orig(self);
		if (DisableGC && GC_Manager.d_gc_disabled && GC_Manager.load_mono_gc())
		{
			GC_Manager.Instance.DoCollect();
		}
	}

	public static void DoCollect()
	{
		if (!GC_Manager.d_gc_disabled || GameManager.Instance.IsLoadingLevel)
		{
			if (GC_Manager.d_gc_disabled)
			{
				GC_Manager.Instance.DoCollect();
				return;
			}
			GC_Manager.LastGcTime.SetValue(typeof(BraveMemory), Time.realtimeSinceStartup);
			GC.Collect();
		}
	}
}