Decompiled source of DSP Planner Export v1.10.0

BepInEx/plugins/DSPPlannerExport/DSPPlannerExport.dll

Decompiled a month ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Net;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Threading;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("DSPPlannerExport")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.9.0.0")]
[assembly: AssemblyInformationalVersion("1.9.0+50e1294f5ffc09055a235f4a19442cc3386625f1")]
[assembly: AssemblyProduct("DSPPlannerExport")]
[assembly: AssemblyTitle("DSPPlannerExport")]
[assembly: AssemblyVersion("1.9.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[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 DSPPlannerExport
{
	[BepInPlugin("dev.ski.dspplannerexport", "DSP Planner Export", "1.10.0")]
	public class Plugin : BaseUnityPlugin
	{
		private enum DeficitMode
		{
			Sustained,
			Instant,
			Window
		}

		private enum Corner
		{
			TopLeft,
			TopRight,
			BottomLeft,
			BottomRight
		}

		private struct Snap
		{
			public long tick;

			public int[] ids;

			public long[] p;

			public long[] c;
		}

		private struct DefRow
		{
			public int id;

			public string name;

			public float prod;

			public float cons;

			public float net;

			public int streak;

			public bool flagged;
		}

		private struct Chain
		{
			public string kind;

			public int[] ids;

			public Chain(string k, int[] i)
			{
				kind = k;
				ids = i;
			}
		}

		private struct Advice
		{
			public int id;

			public string text;
		}

		public const string GUID = "dev.ski.dspplannerexport";

		public const string NAME = "DSP Planner Export";

		public const string VERSION = "1.10.0";

		private const int Port = 8765;

		private HttpListener _listener;

		private Thread _thread;

		private Harmony _harmony;

		private volatile bool _running;

		private volatile string _snapshot = "{\"version\":1,\"running\":false,\"states\":[]}";

		private volatile string _rates = "{\"version\":1,\"running\":false,\"gameTick\":0,\"items\":[]}";

		private volatile string _protos;

		private volatile string _techs;

		private volatile bool _locReady;

		private volatile string _deficits = "{\"version\":1,\"running\":false,\"items\":[]}";

		private volatile string _research = "{\"version\":1,\"running\":false}";

		private volatile string _power = "{\"version\":1,\"running\":false,\"planets\":[]}";

		private int _frame;

		private ConfigEntry<bool> _cfgEnabled;

		private ConfigEntry<KeyboardShortcut> _cfgToggle;

		private ConfigEntry<DeficitMode> _cfgMode;

		private ConfigEntry<int> _cfgSustainSeconds;

		private ConfigEntry<int> _cfgWindowSeconds;

		private ConfigEntry<float> _cfgMinMagnitude;

		private ConfigEntry<int> _cfgMaxRows;

		private ConfigEntry<float> _cfgScale;

		private ConfigEntry<Corner> _cfgCorner;

		private ConfigEntry<bool> _cfgShowPending;

		private ConfigEntry<float> _cfgHudX;

		private ConfigEntry<float> _cfgHudY;

		private ConfigEntry<bool> _cfgAdvisor;

		private ConfigEntry<bool> _cfgAdvBelts;

		private ConfigEntry<bool> _cfgAdvSorters;

		private ConfigEntry<bool> _cfgAdvMachines;

		private ConfigEntry<bool> _cfgAdvProlif;

		private ConfigEntry<KeyboardShortcut> _cfgAdvAckKey;

		private ConfigEntry<string> _cfgAdvAcked;

		private FileSystemWatcher _cfgWatcher;

		private string[] _itemName;

		private float[] _negSeconds;

		private readonly List<Snap> _snaps = new List<Snap>();

		private volatile DefRow[] _hudRows = new DefRow[0];

		private static readonly Chain[] UpgradeChains = new Chain[7]
		{
			new Chain("Belts", new int[3] { 2001, 2002, 2003 }),
			new Chain("Sorters", new int[4] { 2011, 2012, 2013, 2014 }),
			new Chain("Machines", new int[3] { 2303, 2304, 2305 }),
			new Chain("Machines", new int[2] { 2302, 2315 }),
			new Chain("Machines", new int[2] { 2309, 2317 }),
			new Chain("Machines", new int[2] { 2301, 2316 }),
			new Chain("Proliferator", new int[3] { 1141, 1142, 1143 })
		};

		private static readonly Advice[] _noAdvice = new Advice[0];

		private volatile Advice[] _advice = _noAdvice;

		private volatile bool _inGame;

		private bool _hudVisible = true;

		private bool _dragging;

		private float _hudX;

		private float _hudY;

		private Vector2 _dragOffset;

		private GUIStyle _titleStyle;

		private GUIStyle _rowStyle;

		private Texture2D _px;

		private void Awake()
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_026c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0332: Unknown result type (might be due to invalid IL or missing references)
			//IL_033c: Expected O, but got Unknown
			_cfgEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("HUD", "Enabled", true, "Show the in-game live-deficit HUD overlay.");
			_cfgToggle = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("HUD", "ToggleKey", new KeyboardShortcut((KeyCode)289, Array.Empty<KeyCode>()), "Hotkey to show/hide the HUD at runtime.");
			_cfgMode = ((BaseUnityPlugin)this).Config.Bind<DeficitMode>("Deficit", "Mode", DeficitMode.Window, "How a deficit is flagged. Window = net averaged over the last WindowSeconds (recommended — buffer churn averages out, only real shortages show). Sustained = net-negative held for SustainSeconds. Instant = net-negative in the latest 1s sample (most detail, spammy).");
			_cfgSustainSeconds = ((BaseUnityPlugin)this).Config.Bind<int>("Deficit", "SustainSeconds", 5, "Sustained mode: consecutive ~1s samples in deficit before a flag.");
			_cfgWindowSeconds = ((BaseUnityPlugin)this).Config.Bind<int>("Deficit", "WindowSeconds", 60, "Window mode: average net production over this many seconds (e.g. 30, 60, 120, 300, 600, 900, 1800, 3600). Longer = calmer, only structural deficits show. Capped at 3600 (1h).");
			_cfgMinMagnitude = ((BaseUnityPlugin)this).Config.Bind<float>("Deficit", "MinMagnitude", 1f, "Ignore deficits smaller than this many items/min (noise floor).");
			_cfgMaxRows = ((BaseUnityPlugin)this).Config.Bind<int>("HUD", "MaxRows", 12, "Maximum deficit rows drawn in the HUD.");
			_cfgScale = ((BaseUnityPlugin)this).Config.Bind<float>("HUD", "Scale", 1f, "HUD size multiplier.");
			_cfgCorner = ((BaseUnityPlugin)this).Config.Bind<Corner>("HUD", "Corner", Corner.TopRight, "Screen corner the HUD anchors to (used until you drag the HUD).");
			_cfgShowPending = ((BaseUnityPlugin)this).Config.Bind<bool>("HUD", "ShowPending", true, "Also list items dipping but not yet sustained (dimmed, with their streak) so the HUD matches the planner card. Off = show only flagged deficits.");
			_cfgHudX = ((BaseUnityPlugin)this).Config.Bind<float>("HUD", "HudX", -1f, "HUD pixel X of the top-left corner. -1 = anchor to Corner. Set by dragging the HUD in-game.");
			_cfgHudY = ((BaseUnityPlugin)this).Config.Bind<float>("HUD", "HudY", -1f, "HUD pixel Y of the top-left corner. -1 = anchor to Corner. Set by dragging the HUD in-game.");
			_cfgAdvisor = ((BaseUnityPlugin)this).Config.Bind<bool>("Advisor", "Enabled", true, "Show 'researched upgrade available' tips in the HUD when a higher building tier unlocks (e.g. Mk.II belts). Mirrors the planner's \ud83d\udca1 Advisor.");
			_cfgAdvBelts = ((BaseUnityPlugin)this).Config.Bind<bool>("Advisor", "Belts", true, "Advise when a higher Conveyor Belt tier is researched.");
			_cfgAdvSorters = ((BaseUnityPlugin)this).Config.Bind<bool>("Advisor", "Sorters", true, "Advise when a higher Sorter tier is researched.");
			_cfgAdvMachines = ((BaseUnityPlugin)this).Config.Bind<bool>("Advisor", "Machines", true, "Advise when a higher Assembler / Smelter / Chemical Plant / Mining Machine tier is researched.");
			_cfgAdvProlif = ((BaseUnityPlugin)this).Config.Bind<bool>("Advisor", "Proliferator", true, "Advise when a higher Proliferator Mk is researched.");
			_cfgAdvAckKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Advisor", "DismissKey", new KeyboardShortcut((KeyCode)290, Array.Empty<KeyCode>()), "Hotkey to dismiss the current advisor tips (each reappears only when a still-newer tier unlocks).");
			_cfgAdvAcked = ((BaseUnityPlugin)this).Config.Bind<string>("Advisor", "Acknowledged", "", "Internal: comma-separated item ids already dismissed. Managed by DismissKey.");
			_hudVisible = _cfgEnabled.Value;
			try
			{
				string directoryName = Path.GetDirectoryName(((BaseUnityPlugin)this).Config.ConfigFilePath);
				string fileName = Path.GetFileName(((BaseUnityPlugin)this).Config.ConfigFilePath);
				_cfgWatcher = new FileSystemWatcher(directoryName, fileName)
				{
					NotifyFilter = (NotifyFilters.Size | NotifyFilters.LastWrite),
					EnableRaisingEvents = true
				};
				_cfgWatcher.Changed += delegate
				{
					try
					{
						((BaseUnityPlugin)this).Config.Reload();
					}
					catch
					{
					}
				};
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("DSP Planner Export: config watcher failed (edit the cfg + restart to apply). " + ex.Message));
			}
			try
			{
				_harmony = new Harmony("dev.ski.dspplannerexport");
				_harmony.PatchAll(typeof(ProductionTally));
			}
			catch (Exception ex2)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("DSP Planner Export: stats patch failed (live /rates disabled). " + ex2.Message));
			}
			try
			{
				_listener = new HttpListener();
				_listener.Prefixes.Add($"http://localhost:{8765}/");
				_listener.Prefixes.Add($"http://127.0.0.1:{8765}/");
				_listener.Start();
				_running = true;
				_thread = new Thread(ServeLoop)
				{
					IsBackground = true,
					Name = "DSPPlannerExport"
				};
				_thread.Start();
				((BaseUnityPlugin)this).Logger.LogInfo((object)$"DSP Planner Export: serving http://localhost:{8765}/state, /protos, /techs, /rates, /deficits, /research, /power, /config");
			}
			catch (Exception ex3)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("DSP Planner Export: could not start HTTP listener. On Windows you may need to run the game as admin once, or pick another port. " + ex3));
			}
		}

		private void Update()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			KeyboardShortcut value = _cfgToggle.Value;
			if (((KeyboardShortcut)(ref value)).IsDown())
			{
				_hudVisible = !_hudVisible;
			}
			value = _cfgAdvAckKey.Value;
			if (((KeyboardShortcut)(ref value)).IsDown())
			{
				AcknowledgeAdvice();
			}
			if (++_frame % 60 != 0)
			{
				return;
			}
			if (!_locReady && (Object)(object)GameMain.instance != (Object)null && GameMain.data != null && !DSPGame.IsMenuDemo)
			{
				_locReady = true;
				_protos = null;
				_techs = null;
			}
			try
			{
				_snapshot = BuildJson();
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("DSP Planner Export: snapshot error " + ex.Message));
			}
			try
			{
				_rates = BuildRates();
			}
			catch (Exception ex2)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("DSP Planner Export: rates error " + ex2.Message));
			}
			try
			{
				ComputeDeficits();
			}
			catch (Exception ex3)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("DSP Planner Export: deficit error " + ex3.Message));
			}
			try
			{
				_research = BuildResearch();
			}
			catch (Exception ex4)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("DSP Planner Export: research error " + ex4.Message));
			}
			try
			{
				_power = BuildPower();
			}
			catch (Exception ex5)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("DSP Planner Export: power error " + ex5.Message));
			}
			try
			{
				ComputeAdvice();
			}
			catch (Exception ex6)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("DSP Planner Export: advice error " + ex6.Message));
			}
		}

		private bool AdvKindEnabled(string kind)
		{
			return kind switch
			{
				"Belts" => _cfgAdvBelts.Value, 
				"Sorters" => _cfgAdvSorters.Value, 
				"Machines" => _cfgAdvMachines.Value, 
				"Proliferator" => _cfgAdvProlif.Value, 
				_ => true, 
			};
		}

		private HashSet<int> AckedSet()
		{
			HashSet<int> hashSet = new HashSet<int>();
			string value = _cfgAdvAcked.Value;
			if (!string.IsNullOrEmpty(value))
			{
				string[] array = value.Split(new char[1] { ',' });
				for (int i = 0; i < array.Length; i++)
				{
					if (int.TryParse(array[i].Trim(), out var result))
					{
						hashSet.Add(result);
					}
				}
			}
			return hashSet;
		}

		private void ComputeAdvice()
		{
			if (!_cfgAdvisor.Value || (Object)(object)GameMain.instance == (Object)null || GameMain.history == null || DSPGame.IsMenuDemo)
			{
				_advice = _noAdvice;
				return;
			}
			HashSet<int> hashSet = AckedSet();
			List<Advice> list = new List<Advice>();
			Chain[] upgradeChains = UpgradeChains;
			for (int i = 0; i < upgradeChains.Length; i++)
			{
				Chain chain = upgradeChains[i];
				if (!AdvKindEnabled(chain.kind))
				{
					continue;
				}
				int num = -1;
				for (int j = 0; j < chain.ids.Length; j++)
				{
					if (GameMain.history.ItemUnlocked(chain.ids[j]))
					{
						num = j;
					}
				}
				if (num > 0)
				{
					int num2 = chain.ids[num];
					if (!hashSet.Contains(num2))
					{
						ItemProto val = ((ProtoSet<ItemProto>)(object)LDB.items).Select(num2);
						list.Add(new Advice
						{
							id = num2,
							text = ((val != null) ? ((Proto)val).name : ("item " + num2))
						});
					}
				}
			}
			_advice = list.ToArray();
		}

		private void AcknowledgeAdvice()
		{
			Advice[] advice = _advice;
			if (advice.Length != 0)
			{
				HashSet<int> hashSet = AckedSet();
				Advice[] array = advice;
				for (int i = 0; i < array.Length; i++)
				{
					Advice advice2 = array[i];
					hashSet.Add(advice2.id);
				}
				_cfgAdvAcked.Value = string.Join(",", hashSet);
				_advice = _noAdvice;
			}
		}

		private void ServeLoop()
		{
			while (_running)
			{
				HttpListenerContext ctx;
				try
				{
					ctx = _listener.GetContext();
				}
				catch
				{
					break;
				}
				try
				{
					HttpListenerResponse response = ctx.Response;
					response.AddHeader("Access-Control-Allow-Origin", "*");
					response.AddHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
					response.AddHeader("Access-Control-Allow-Headers", "Content-Type");
					response.AddHeader("Cache-Control", "no-store");
					if (ctx.Request.HttpMethod == "OPTIONS")
					{
						response.StatusCode = 204;
						response.OutputStream.Close();
						continue;
					}
					string absolutePath = ctx.Request.Url.AbsolutePath;
					if (absolutePath.EndsWith("/events"))
					{
						Thread thread = new Thread((ThreadStart)delegate
						{
							ServeEvents(ctx);
						});
						thread.IsBackground = true;
						thread.Name = "DSPPlannerExportSSE";
						thread.Start();
						continue;
					}
					response.ContentType = "application/json";
					string s;
					if (absolutePath.EndsWith("/protos"))
					{
						s = GetStaticProtos();
					}
					else if (absolutePath.EndsWith("/techs"))
					{
						s = GetStaticTechs();
					}
					else if (absolutePath.EndsWith("/rates"))
					{
						s = _rates;
					}
					else if (absolutePath.EndsWith("/deficits"))
					{
						s = _deficits;
					}
					else if (absolutePath.EndsWith("/research"))
					{
						s = _research;
					}
					else if (absolutePath.EndsWith("/power"))
					{
						s = _power;
					}
					else if (absolutePath.EndsWith("/config"))
					{
						if (ctx.Request.HttpMethod == "POST")
						{
							string form;
							using (StreamReader streamReader = new StreamReader(ctx.Request.InputStream, Encoding.UTF8))
							{
								form = streamReader.ReadToEnd();
							}
							try
							{
								ApplyConfig(form);
							}
							catch (Exception ex)
							{
								((BaseUnityPlugin)this).Logger.LogWarning((object)("DSP Planner Export: config apply error " + ex.Message));
							}
						}
						s = BuildConfig();
					}
					else
					{
						s = _snapshot;
					}
					byte[] bytes = Encoding.UTF8.GetBytes(s);
					response.ContentLength64 = bytes.Length;
					response.OutputStream.Write(bytes, 0, bytes.Length);
					response.OutputStream.Close();
				}
				catch (Exception ex2)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)("DSP Planner Export: request error " + ex2.Message));
				}
			}
		}

		private void ServeEvents(HttpListenerContext ctx)
		{
			HttpListenerResponse response = ctx.Response;
			Stream outputStream = response.OutputStream;
			try
			{
				response.ContentType = "text/event-stream";
				response.SendChunked = true;
				string text = null;
				string text2 = null;
				while (_running)
				{
					string snapshot = _snapshot;
					string rates = _rates;
					StringBuilder stringBuilder = new StringBuilder();
					if (snapshot != text)
					{
						stringBuilder.Append("event: state\ndata: ").Append(snapshot).Append("\n\n");
						text = snapshot;
					}
					if (rates != text2)
					{
						stringBuilder.Append("event: rates\ndata: ").Append(rates).Append("\n\n");
						text2 = rates;
					}
					if (stringBuilder.Length == 0)
					{
						stringBuilder.Append(": keepalive\n\n");
					}
					byte[] bytes = Encoding.UTF8.GetBytes(stringBuilder.ToString());
					outputStream.Write(bytes, 0, bytes.Length);
					outputStream.Flush();
					Thread.Sleep(1000);
				}
			}
			catch
			{
			}
			try
			{
				outputStream.Close();
			}
			catch
			{
			}
		}

		private static string BuildJson()
		{
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			StringBuilder stringBuilder = new StringBuilder(4096);
			bool flag = (Object)(object)GameMain.instance != (Object)null && GameMain.history != null && !DSPGame.IsMenuDemo;
			stringBuilder.Append("{\"version\":1,\"running\":").Append(flag ? "true" : "false").Append(",\"states\":[");
			if (flag)
			{
				Dictionary<int, TechState> techStates = GameMain.history.techStates;
				if (techStates != null)
				{
					bool flag2 = true;
					foreach (KeyValuePair<int, TechState> item in techStates)
					{
						TechState value = item.Value;
						if (value.unlocked || value.curLevel > 0)
						{
							if (!flag2)
							{
								stringBuilder.Append(',');
							}
							flag2 = false;
							stringBuilder.Append("{\"id\":").Append(item.Key).Append(",\"level\":")
								.Append(value.curLevel)
								.Append(",\"max\":")
								.Append(value.maxLevel)
								.Append(",\"unlocked\":")
								.Append(value.unlocked ? "true" : "false")
								.Append('}');
						}
					}
				}
			}
			stringBuilder.Append("]}");
			return stringBuilder.ToString();
		}

		private static string BuildResearch()
		{
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			StringBuilder stringBuilder = new StringBuilder(512);
			bool flag = (Object)(object)GameMain.instance != (Object)null && GameMain.history != null && !DSPGame.IsMenuDemo;
			stringBuilder.Append("{\"version\":1,\"running\":").Append(flag ? "true" : "false");
			if (flag)
			{
				GameHistoryData history = GameMain.history;
				int currentTech = history.currentTech;
				stringBuilder.Append(",\"current\":").Append(currentTech);
				if (currentTech > 0 && history.techStates != null && history.techStates.ContainsKey(currentTech))
				{
					TechState val = history.techStates[currentTech];
					stringBuilder.Append(",\"hashUploaded\":").Append(val.hashUploaded).Append(",\"hashNeeded\":")
						.Append(val.hashNeeded)
						.Append(",\"level\":")
						.Append(val.curLevel);
				}
				stringBuilder.Append(",\"queue\":[");
				int[] techQueue = history.techQueue;
				if (techQueue != null)
				{
					int techQueueLength = history.techQueueLength;
					int num = 0;
					for (int i = 0; i < techQueueLength && i < techQueue.Length; i++)
					{
						if (techQueue[i] > 0)
						{
							if (num++ > 0)
							{
								stringBuilder.Append(',');
							}
							stringBuilder.Append(techQueue[i]);
						}
					}
				}
				stringBuilder.Append(']');
			}
			stringBuilder.Append('}');
			return stringBuilder.ToString();
		}

		private static string BuildPower()
		{
			StringBuilder stringBuilder = new StringBuilder(2048);
			bool flag = (Object)(object)GameMain.instance != (Object)null && GameMain.data != null && !DSPGame.IsMenuDemo;
			stringBuilder.Append("{\"version\":1,\"running\":").Append(flag ? "true" : "false").Append(",\"planets\":[");
			if (flag)
			{
				PlanetFactory[] factories = GameMain.data.factories;
				bool flag2 = true;
				for (int i = 0; i < GameMain.data.factoryCount; i++)
				{
					PlanetFactory val = factories[i];
					if (val == null || val.powerSystem == null || val.planet == null)
					{
						continue;
					}
					long num = 0L;
					long num2 = 0L;
					long num3 = 0L;
					PowerSystem powerSystem = val.powerSystem;
					for (int j = 1; j < powerSystem.netCursor; j++)
					{
						PowerNetwork val2 = powerSystem.netPool[j];
						if (val2 != null)
						{
							num += val2.energyCapacity;
							num2 += val2.energyServed;
							num3 += val2.energyStored;
						}
					}
					if (!flag2)
					{
						stringBuilder.Append(',');
					}
					flag2 = false;
					stringBuilder.Append("{\"planet\":").Append(val.planet.id).Append(",\"name\":\"")
						.Append(JsonEscape(val.planet.displayName ?? ""))
						.Append("\",\"capacity\":")
						.Append(num)
						.Append(",\"consumption\":")
						.Append(num2)
						.Append(",\"stored\":")
						.Append(num3)
						.Append('}');
				}
			}
			stringBuilder.Append("]}");
			return stringBuilder.ToString();
		}

		private string GetStaticProtos()
		{
			if (_protos != null)
			{
				return _protos;
			}
			string text;
			try
			{
				text = BuildProtos();
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("protos error " + ex.Message));
				return "{\"items\":[]}";
			}
			if (_locReady)
			{
				_protos = text;
			}
			return text;
		}

		private string GetStaticTechs()
		{
			if (_techs != null)
			{
				return _techs;
			}
			string text;
			try
			{
				text = BuildTechs();
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("techs error " + ex.Message));
				return "{\"version\":1,\"techs\":[]}";
			}
			if (_locReady)
			{
				_techs = text;
			}
			return text;
		}

		private static string BuildProtos()
		{
			//IL_00fe: 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_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01dd: Unknown result type (might be due to invalid IL or missing references)
			StringBuilder stringBuilder = new StringBuilder(65536);
			stringBuilder.Append("{\"version\":2,\"items\":[");
			bool flag = true;
			ItemProto[] dataArray = ((ProtoSet<ItemProto>)(object)LDB.items).dataArray;
			foreach (ItemProto val in dataArray)
			{
				if (val == null || ((Proto)val).ID <= 0)
				{
					continue;
				}
				if (!flag)
				{
					stringBuilder.Append(',');
				}
				flag = false;
				stringBuilder.Append("{\"id\":").Append(((Proto)val).ID).Append(",\"name\":\"")
					.Append(JsonEscape(((Proto)val).name))
					.Append('"')
					.Append(",\"grid\":")
					.Append(val.GridIndex);
				if (val.ModelIndex <= 0)
				{
					stringBuilder.Append('}');
					continue;
				}
				stringBuilder.Append(",\"model\":").Append(val.ModelIndex);
				try
				{
					PrefabDesc val2 = ((ProtoSet<ModelProto>)(object)LDB.models).Select(val.ModelIndex)?.prefabDesc;
					if (val2 != null)
					{
						Vector2 blueprintBoxSize = val2.blueprintBoxSize;
						if (blueprintBoxSize.x > 0f && blueprintBoxSize.y > 0f)
						{
							stringBuilder.Append(",\"w\":").Append((int)blueprintBoxSize.x).Append(",\"h\":")
								.Append((int)blueprintBoxSize.y);
						}
						float y = val2.lapJoint.y;
						if (y > 0f)
						{
							stringBuilder.Append(",\"zstep\":").Append(y.ToString("0.###", CultureInfo.InvariantCulture));
						}
						Pose[] slotPoses = val2.slotPoses;
						if (slotPoses != null && slotPoses.Length != 0)
						{
							int[] array = new int[4];
							Pose[] array2 = slotPoses;
							for (int j = 0; j < array2.Length; j++)
							{
								Vector3 position = array2[j].position;
								int num = ((Math.Abs(position.x) >= Math.Abs(position.z)) ? ((position.x >= 0f) ? 1 : 3) : ((!(position.z >= 0f)) ? 2 : 0));
								array[num]++;
							}
							stringBuilder.Append(",\"slots\":[").Append(array[0]).Append(',')
								.Append(array[1])
								.Append(',')
								.Append(array[2])
								.Append(',')
								.Append(array[3])
								.Append(']');
						}
					}
				}
				catch
				{
				}
				stringBuilder.Append('}');
			}
			stringBuilder.Append("]}");
			return stringBuilder.ToString();
		}

		private static string BuildTechs()
		{
			StringBuilder stringBuilder = new StringBuilder(65536);
			stringBuilder.Append("{\"version\":1,\"techs\":[");
			bool flag = true;
			TechProto[] dataArray = ((ProtoSet<TechProto>)(object)LDB.techs).dataArray;
			foreach (TechProto val in dataArray)
			{
				if (val == null || ((Proto)val).ID <= 0)
				{
					continue;
				}
				if (!flag)
				{
					stringBuilder.Append(',');
				}
				flag = false;
				long hashNeeded;
				try
				{
					hashNeeded = val.GetHashNeeded(val.Level);
				}
				catch
				{
					hashNeeded = val.HashNeeded;
				}
				stringBuilder.Append("{\"id\":").Append(((Proto)val).ID).Append(",\"name\":\"")
					.Append(JsonEscape(((Proto)val).name))
					.Append('"')
					.Append(",\"level\":")
					.Append(val.Level)
					.Append(",\"max\":")
					.Append(val.MaxLevel)
					.Append(",\"hash\":")
					.Append(hashNeeded);
				if (val.MaxLevel > val.Level)
				{
					stringBuilder.Append(",\"hashByLevel\":[");
					int num = Math.Min(val.MaxLevel, val.Level + 49);
					for (int j = val.Level; j <= num; j++)
					{
						if (j > val.Level)
						{
							stringBuilder.Append(',');
						}
						long hashNeeded2;
						try
						{
							hashNeeded2 = val.GetHashNeeded(j);
						}
						catch
						{
							hashNeeded2 = val.HashNeeded;
						}
						stringBuilder.Append(hashNeeded2);
					}
					stringBuilder.Append(']');
				}
				stringBuilder.Append(",\"pre\":");
				AppendIntArray(stringBuilder, val.PreTechs);
				stringBuilder.Append(",\"preImplicit\":");
				AppendIntArray(stringBuilder, val.PreTechsImplicit);
				stringBuilder.Append(",\"items\":");
				AppendIntArray(stringBuilder, val.Items);
				stringBuilder.Append(",\"points\":");
				AppendIntArray(stringBuilder, val.ItemPoints);
				stringBuilder.Append(",\"preItem\":");
				AppendIntArray(stringBuilder, val.PreItem);
				stringBuilder.Append('}');
			}
			stringBuilder.Append("]}");
			return stringBuilder.ToString();
		}

		private static void AppendIntArray(StringBuilder sb, int[] a)
		{
			sb.Append('[');
			if (a != null)
			{
				for (int i = 0; i < a.Length; i++)
				{
					if (i > 0)
					{
						sb.Append(',');
					}
					sb.Append(a[i]);
				}
			}
			sb.Append(']');
		}

		private static string BuildRates()
		{
			bool flag = (Object)(object)GameMain.instance != (Object)null && GameMain.data != null && !DSPGame.IsMenuDemo;
			StringBuilder stringBuilder = new StringBuilder(16384);
			stringBuilder.Append("{\"version\":1,\"running\":").Append(flag ? "true" : "false").Append(",\"gameTick\":")
				.Append(flag ? GameMain.gameTick : 0)
				.Append(",\"items\":[");
			if (flag)
			{
				bool flag2 = true;
				for (int i = 0; i < ProductionTally.Produced.Length; i++)
				{
					long num = ProductionTally.Produced[i];
					long num2 = ProductionTally.Consumed[i];
					if (num != 0L || num2 != 0L)
					{
						if (!flag2)
						{
							stringBuilder.Append(',');
						}
						flag2 = false;
						stringBuilder.Append("{\"id\":").Append(i).Append(",\"p\":")
							.Append(num)
							.Append(",\"c\":")
							.Append(num2)
							.Append('}');
					}
				}
			}
			stringBuilder.Append("]}");
			return stringBuilder.ToString();
		}

		private void ComputeDeficits()
		{
			if (!(_inGame = (Object)(object)GameMain.instance != (Object)null && GameMain.data != null && !DSPGame.IsMenuDemo))
			{
				_deficits = "{\"version\":2,\"running\":false,\"items\":[]}";
				_hudRows = new DefRow[0];
				_snaps.Clear();
				return;
			}
			int num = ProductionTally.Produced.Length;
			if (_itemName == null)
			{
				_itemName = new string[num];
				ItemProto[] dataArray = ((ProtoSet<ItemProto>)(object)LDB.items).dataArray;
				foreach (ItemProto val in dataArray)
				{
					if (val != null && ((Proto)val).ID > 0 && ((Proto)val).ID < num)
					{
						_itemName[((Proto)val).ID] = ((Proto)val).name;
					}
				}
			}
			if (_negSeconds == null)
			{
				_negSeconds = new float[num];
			}
			long[] produced = ProductionTally.Produced;
			long[] consumed = ProductionTally.Consumed;
			long gameTick = GameMain.gameTick;
			if (_snaps.Count > 0 && gameTick <= _snaps[_snaps.Count - 1].tick)
			{
				_snaps.Clear();
			}
			DeficitMode value = _cfgMode.Value;
			int num2 = Math.Max(5, Math.Min(3600, _cfgWindowSeconds.Value));
			int num3 = -1;
			if (_snaps.Count > 0)
			{
				if (value == DeficitMode.Window)
				{
					long num4 = gameTick - (long)num2 * 60L;
					num3 = 0;
					for (int j = 0; j < _snaps.Count && _snaps[j].tick <= num4; j++)
					{
						num3 = j;
					}
				}
				else
				{
					num3 = _snaps.Count - 1;
				}
			}
			if (num3 < 0)
			{
				PushSnap(gameTick, produced, consumed, num);
				return;
			}
			Snap snap = _snaps[num3];
			long num5 = gameTick - snap.tick;
			if (num5 <= 0)
			{
				PushSnap(gameTick, produced, consumed, num);
				return;
			}
			float num6 = (float)num5 / 60f;
			Dictionary<int, long> dictionary = new Dictionary<int, long>(snap.ids.Length);
			Dictionary<int, long> dictionary2 = new Dictionary<int, long>(snap.ids.Length);
			for (int k = 0; k < snap.ids.Length; k++)
			{
				dictionary[snap.ids[k]] = snap.p[k];
				dictionary2[snap.ids[k]] = snap.c[k];
			}
			float value2 = _cfgMinMagnitude.Value;
			int num7 = Math.Max(1, _cfgSustainSeconds.Value);
			List<DefRow> list = new List<DefRow>();
			for (int l = 0; l < num; l++)
			{
				long num8 = produced[l];
				long num9 = consumed[l];
				long value3;
				long num10 = num8 - (dictionary.TryGetValue(l, out value3) ? value3 : 0);
				long value4;
				long num11 = num9 - (dictionary2.TryGetValue(l, out value4) ? value4 : 0);
				if (num10 == 0L && num11 == 0L)
				{
					_negSeconds[l] = 0f;
					continue;
				}
				float num12 = (float)((double)num10 / (double)num5 * 3600.0);
				float num13 = (float)((double)num11 / (double)num5 * 3600.0);
				float num14 = num12 - num13;
				if (num14 >= 0f - value2)
				{
					_negSeconds[l] = 0f;
					continue;
				}
				bool flagged;
				int streak;
				if (value == DeficitMode.Sustained)
				{
					_negSeconds[l] += 1f;
					flagged = _negSeconds[l] >= (float)num7;
					streak = (int)Math.Round(_negSeconds[l]);
				}
				else
				{
					flagged = true;
					streak = (int)Math.Round(num6);
				}
				list.Add(new DefRow
				{
					id = l,
					name = (_itemName[l] ?? ("#" + l)),
					prod = num12,
					cons = num13,
					net = num14,
					streak = streak,
					flagged = flagged
				});
			}
			PushSnap(gameTick, produced, consumed, num);
			long num15 = ((value == DeficitMode.Window) ? num2 : 2) + 120;
			long num16 = gameTick - num15 * 60;
			while (_snaps.Count > 1 && _snaps[0].tick < num16)
			{
				_snaps.RemoveAt(0);
			}
			list.Sort((DefRow x, DefRow y) => x.net.CompareTo(y.net));
			_hudRows = list.ToArray();
			_deficits = BuildDeficitsJson(gameTick, num5, list);
		}

		private void PushSnap(long tick, long[] P, long[] C, int len)
		{
			int num = 0;
			for (int i = 0; i < len; i++)
			{
				if (P[i] != 0L || C[i] != 0L)
				{
					num++;
				}
			}
			int[] array = new int[num];
			long[] array2 = new long[num];
			long[] array3 = new long[num];
			int num2 = 0;
			for (int j = 0; j < len; j++)
			{
				if (P[j] != 0L || C[j] != 0L)
				{
					array[num2] = j;
					array2[num2] = P[j];
					array3[num2] = C[j];
					num2++;
				}
			}
			_snaps.Add(new Snap
			{
				tick = tick,
				ids = array,
				p = array2,
				c = array3
			});
		}

		private string BuildDeficitsJson(long tick, long dt, List<DefRow> rows)
		{
			StringBuilder stringBuilder = new StringBuilder(2048);
			stringBuilder.Append("{\"version\":2,\"running\":true,\"gameTick\":").Append(tick).Append(",\"windowTicks\":")
				.Append(dt)
				.Append(",\"windowSec\":")
				.Append(Fmt((float)dt / 60f))
				.Append(",\"mode\":\"")
				.Append(_cfgMode.Value.ToString().ToLowerInvariant())
				.Append('"')
				.Append(",\"sustainSeconds\":")
				.Append(_cfgSustainSeconds.Value)
				.Append(",\"requestedWindowSec\":")
				.Append(Math.Max(5, Math.Min(3600, _cfgWindowSeconds.Value)))
				.Append(",\"minMagnitude\":")
				.Append(Fmt(_cfgMinMagnitude.Value))
				.Append(",\"items\":[");
			int num = Math.Min(rows.Count, 50);
			for (int i = 0; i < num; i++)
			{
				DefRow defRow = rows[i];
				if (i > 0)
				{
					stringBuilder.Append(',');
				}
				stringBuilder.Append("{\"id\":").Append(defRow.id).Append(",\"name\":\"")
					.Append(JsonEscape(defRow.name))
					.Append('"')
					.Append(",\"produce\":")
					.Append(Fmt(defRow.prod))
					.Append(",\"consume\":")
					.Append(Fmt(defRow.cons))
					.Append(",\"net\":")
					.Append(Fmt(defRow.net))
					.Append(",\"streak\":")
					.Append(defRow.streak)
					.Append(",\"flagged\":")
					.Append(defRow.flagged ? "true" : "false")
					.Append('}');
			}
			stringBuilder.Append("]}");
			return stringBuilder.ToString();
		}

		private static string Fmt(float v)
		{
			return v.ToString("0.#", CultureInfo.InvariantCulture);
		}

		private string BuildConfig()
		{
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			StringBuilder stringBuilder = new StringBuilder(256);
			stringBuilder.Append("{\"version\":2").Append(",\"mode\":\"").Append(_cfgMode.Value.ToString())
				.Append('"')
				.Append(",\"sustainSeconds\":")
				.Append(_cfgSustainSeconds.Value)
				.Append(",\"windowSeconds\":")
				.Append(_cfgWindowSeconds.Value)
				.Append(",\"minMagnitude\":")
				.Append(Fmt(_cfgMinMagnitude.Value))
				.Append(",\"hudEnabled\":")
				.Append(_cfgEnabled.Value ? "true" : "false")
				.Append(",\"toggleKey\":\"")
				.Append(JsonEscape(((object)_cfgToggle.Value/*cast due to .constrained prefix*/).ToString()))
				.Append('"')
				.Append(",\"showPending\":")
				.Append(_cfgShowPending.Value ? "true" : "false")
				.Append(",\"corner\":\"")
				.Append(_cfgCorner.Value.ToString())
				.Append('"')
				.Append(",\"scale\":")
				.Append(Fmt(_cfgScale.Value))
				.Append(",\"maxRows\":")
				.Append(_cfgMaxRows.Value)
				.Append(",\"hudX\":")
				.Append(Fmt(_cfgHudX.Value))
				.Append(",\"hudY\":")
				.Append(Fmt(_cfgHudY.Value))
				.Append(",\"dragged\":")
				.Append((_cfgHudX.Value >= 0f && _cfgHudY.Value >= 0f) ? "true" : "false")
				.Append('}');
			return stringBuilder.ToString();
		}

		private void ApplyConfig(string form)
		{
			if (string.IsNullOrEmpty(form))
			{
				return;
			}
			CultureInfo invariantCulture = CultureInfo.InvariantCulture;
			string[] array = form.Split(new char[1] { '&' });
			foreach (string text in array)
			{
				int num = text.IndexOf('=');
				if (num <= 0)
				{
					continue;
				}
				string text2 = text.Substring(0, num);
				string text3 = Uri.UnescapeDataString(text.Substring(num + 1).Replace('+', ' ')).Trim();
				try
				{
					if (text2 == null)
					{
						continue;
					}
					switch (text2.Length)
					{
					case 4:
						switch (text2[3])
						{
						case 'e':
						{
							if (text2 == "mode" && Enum.TryParse<DeficitMode>(text3, ignoreCase: true, out var result4))
							{
								_cfgMode.Value = result4;
							}
							break;
						}
						case 'X':
						{
							if (text2 == "hudX" && float.TryParse(text3, NumberStyles.Float, invariantCulture, out var result3))
							{
								_cfgHudX.Value = result3;
							}
							break;
						}
						case 'Y':
						{
							if (text2 == "hudY" && float.TryParse(text3, NumberStyles.Float, invariantCulture, out var result2))
							{
								_cfgHudY.Value = result2;
							}
							break;
						}
						}
						break;
					case 14:
					{
						if (text2 == "sustainSeconds" && int.TryParse(text3, out var result8))
						{
							_cfgSustainSeconds.Value = Math.Max(1, result8);
						}
						break;
					}
					case 13:
					{
						if (text2 == "windowSeconds" && int.TryParse(text3, out var result5))
						{
							_cfgWindowSeconds.Value = Math.Max(5, Math.Min(3600, result5));
						}
						break;
					}
					case 12:
					{
						if (text2 == "minMagnitude" && float.TryParse(text3, NumberStyles.Float, invariantCulture, out var result))
						{
							_cfgMinMagnitude.Value = Math.Max(0f, result);
						}
						break;
					}
					case 7:
					{
						if (text2 == "maxRows" && int.TryParse(text3, out var result9))
						{
							_cfgMaxRows.Value = Math.Max(1, result9);
						}
						break;
					}
					case 5:
					{
						if (text2 == "scale" && float.TryParse(text3, NumberStyles.Float, invariantCulture, out var result7))
						{
							_cfgScale.Value = Mathf.Clamp(result7, 0.5f, 3f);
						}
						break;
					}
					case 6:
					{
						if (text2 == "corner" && Enum.TryParse<Corner>(text3, ignoreCase: true, out var result6))
						{
							_cfgCorner.Value = result6;
						}
						break;
					}
					case 10:
						if (text2 == "hudEnabled")
						{
							_cfgEnabled.Value = text3 == "true" || text3 == "1";
						}
						break;
					case 11:
						if (text2 == "showPending")
						{
							_cfgShowPending.Value = text3 == "true" || text3 == "1";
						}
						break;
					case 8:
					case 9:
						break;
					}
				}
				catch
				{
				}
			}
		}

		private void OnGUI()
		{
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Expected O, but got Unknown
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Expected O, but got Unknown
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Expected O, but got Unknown
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_048e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0493: Unknown result type (might be due to invalid IL or missing references)
			//IL_04a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_04bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0330: Unknown result type (might be due to invalid IL or missing references)
			//IL_04ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_04e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0511: Unknown result type (might be due to invalid IL or missing references)
			//IL_0521: Unknown result type (might be due to invalid IL or missing references)
			//IL_03aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b0: Invalid comparison between Unknown and I4
			//IL_0348: Unknown result type (might be due to invalid IL or missing references)
			//IL_034d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0353: Unknown result type (might be due to invalid IL or missing references)
			//IL_044c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0452: Invalid comparison between Unknown and I4
			//IL_03b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_03fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0379: Unknown result type (might be due to invalid IL or missing references)
			//IL_0382: Unknown result type (might be due to invalid IL or missing references)
			//IL_0387: Unknown result type (might be due to invalid IL or missing references)
			//IL_038c: Unknown result type (might be due to invalid IL or missing references)
			//IL_07da: Unknown result type (might be due to invalid IL or missing references)
			//IL_0713: Unknown result type (might be due to invalid IL or missing references)
			//IL_0718: Unknown result type (might be due to invalid IL or missing references)
			//IL_071c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0721: Unknown result type (might be due to invalid IL or missing references)
			//IL_0754: Unknown result type (might be due to invalid IL or missing references)
			//IL_0573: Unknown result type (might be due to invalid IL or missing references)
			//IL_0456: Unknown result type (might be due to invalid IL or missing references)
			//IL_045c: Invalid comparison between Unknown and I4
			//IL_07b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_05e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_05cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_06d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_06cc: Unknown result type (might be due to invalid IL or missing references)
			if (!_hudVisible || !_inGame)
			{
				return;
			}
			bool value = _cfgEnabled.Value;
			Advice[] array = (_cfgAdvisor.Value ? _advice : _noAdvice);
			if (!value && array.Length == 0)
			{
				return;
			}
			DefRow[] array2 = (value ? _hudRows : new DefRow[0]);
			bool value2 = _cfgShowPending.Value;
			int num = 0;
			int num2 = 0;
			for (int i = 0; i < array2.Length; i++)
			{
				if (array2[i].flagged)
				{
					num2++;
					num++;
				}
				else if (value2)
				{
					num++;
				}
			}
			if (_titleStyle == null)
			{
				_titleStyle = new GUIStyle(GUI.skin.label)
				{
					fontStyle = (FontStyle)1,
					alignment = (TextAnchor)3
				};
				_rowStyle = new GUIStyle(GUI.skin.label)
				{
					alignment = (TextAnchor)3
				};
			}
			if ((Object)(object)_px == (Object)null)
			{
				_px = new Texture2D(1, 1);
				_px.SetPixel(0, 0, Color.white);
				_px.Apply();
				((Object)_px).hideFlags = (HideFlags)61;
			}
			float num3 = Mathf.Clamp(_cfgScale.Value, 0.5f, 3f);
			int num4 = Mathf.RoundToInt(14f * num3);
			_titleStyle.fontSize = num4;
			_rowStyle.fontSize = num4;
			float num5 = 10f * num3;
			float num6 = 4f * num3;
			float rowH = num4 + 8;
			float num7 = 300f * num3;
			float num8 = 14f * num3;
			int num9 = Math.Max(1, _cfgMaxRows.Value);
			int num10 = (value ? (1 + ((num <= 0) ? 1 : Math.Min(num, num9))) : 0);
			int num11 = ((array.Length != 0) ? (array.Length + 1) : 0);
			float num12 = num5 * 2f + rowH * (float)Math.Max(1, num10 + num11);
			Corner value3 = _cfgCorner.Value;
			bool num13 = value3 == Corner.TopLeft || value3 == Corner.BottomLeft;
			bool flag = value3 == Corner.TopLeft || value3 == Corner.TopRight;
			float num14 = (num13 ? num8 : ((float)Screen.width - num7 - num8));
			float num15 = (flag ? num8 : ((float)Screen.height - num12 - num8));
			float num16 = (_dragging ? _hudX : ((_cfgHudX.Value >= 0f && _cfgHudY.Value >= 0f) ? _cfgHudX.Value : num14));
			float num17 = (_dragging ? _hudY : ((_cfgHudX.Value >= 0f && _cfgHudY.Value >= 0f) ? _cfgHudY.Value : num15));
			num16 = Mathf.Clamp(num16, 0f, Math.Max(0f, (float)Screen.width - num7));
			num17 = Mathf.Clamp(num17, 0f, Math.Max(0f, (float)Screen.height - num12));
			Event current = Event.current;
			if (current != null)
			{
				if ((int)current.type == 0 && current.button == 0)
				{
					Rect val = new Rect(num16, num17, num7, num12);
					if (((Rect)(ref val)).Contains(current.mousePosition))
					{
						_dragging = true;
						_hudX = num16;
						_hudY = num17;
						_dragOffset = current.mousePosition - new Vector2(num16, num17);
						current.Use();
						goto IL_048e;
					}
				}
				if (_dragging && (int)current.type == 3)
				{
					num16 = (_hudX = Mathf.Clamp(current.mousePosition.x - _dragOffset.x, 0f, Math.Max(0f, (float)Screen.width - num7)));
					num17 = (_hudY = Mathf.Clamp(current.mousePosition.y - _dragOffset.y, 0f, Math.Max(0f, (float)Screen.height - num12)));
					current.Use();
				}
				else if (_dragging && ((int)current.type == 1 || (int)current.rawType == 1))
				{
					_dragging = false;
					_cfgHudX.Value = _hudX;
					_cfgHudY.Value = _hudY;
					current.Use();
				}
			}
			goto IL_048e;
			IL_048e:
			Color color = GUI.color;
			GUI.color = new Color(0.04f, 0.05f, 0.07f, 0.93f);
			GUI.DrawTexture(new Rect(num16, num17, num7, num12), (Texture)(object)_px);
			GUI.color = ((num2 > 0) ? new Color(1f, 0.45f, 0.35f, 0.95f) : new Color(0.55f, 0.6f, 0.7f, 0.9f));
			GUI.DrawTexture(new Rect(num16, num17, num6, num12), (Texture)(object)_px);
			GUI.color = color;
			float tx = num16 + num5 + num6;
			float tw = num7 - num5 * 2f - num6;
			float num18 = num17 + num5;
			if (value)
			{
				if (num == 0)
				{
					L(num18, "✓ No deficits", new Color(0.55f, 0.95f, 0.6f), _titleStyle);
					num18 += rowH;
				}
				else
				{
					L(num18, (num2 > 0) ? ("▲ Live deficits (" + num2 + ")") : "Dipping — none sustained", (num2 > 0) ? new Color(1f, 0.78f, 0.32f) : new Color(0.92f, 0.94f, 1f), _titleStyle);
					num18 += rowH;
					Color val2 = default(Color);
					((Color)(ref val2))..ctor(1f, 0.5f, 0.42f);
					Color val3 = default(Color);
					((Color)(ref val3))..ctor(0.9f, 0.92f, 0.97f);
					int num19 = 0;
					for (int j = 0; j < array2.Length; j++)
					{
						if (num19 >= num9)
						{
							break;
						}
						DefRow defRow = array2[j];
						if (defRow.flagged || value2)
						{
							string text = defRow.name + "   " + defRow.net.ToString("0.0", CultureInfo.InvariantCulture) + "/min" + (defRow.flagged ? "" : ("  (" + defRow.streak + "s)"));
							L(num18, text, defRow.flagged ? val2 : val3, _rowStyle);
							num18 += rowH;
							num19++;
						}
					}
				}
			}
			if (array.Length != 0)
			{
				KeyboardShortcut value4 = _cfgAdvAckKey.Value;
				string text2 = ((object)((KeyboardShortcut)(ref value4)).MainKey/*cast due to .constrained prefix*/).ToString();
				L(num18, "Upgrades available (" + text2 + " to dismiss)", new Color(1f, 0.85f, 0.4f), _titleStyle);
				num18 += rowH;
				Color col = default(Color);
				((Color)(ref col))..ctor(0.75f, 0.95f, 1f);
				Advice[] array3 = array;
				for (int k = 0; k < array3.Length; k++)
				{
					Advice advice = array3[k];
					L(num18, "+ " + advice.text + " researched", col, _rowStyle);
					num18 += rowH;
				}
			}
			GUI.color = color;
			void L(float y, string text3, Color textColor, GUIStyle st)
			{
				//IL_001a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0046: Unknown result type (might be due to invalid IL or missing references)
				//IL_0058: Unknown result type (might be due to invalid IL or missing references)
				//IL_0074: Unknown result type (might be due to invalid IL or missing references)
				st.normal.textColor = new Color(0f, 0f, 0f, 0.9f);
				GUI.Label(new Rect(tx + 1.5f, y + 1.5f, tw, rowH), text3, st);
				st.normal.textColor = textColor;
				GUI.Label(new Rect(tx, y, tw, rowH), text3, st);
			}
		}

		private static string JsonEscape(string s)
		{
			if (string.IsNullOrEmpty(s))
			{
				return "";
			}
			StringBuilder stringBuilder = new StringBuilder(s.Length + 8);
			foreach (char c in s)
			{
				if (c == '"' || c == '\\')
				{
					stringBuilder.Append('\\').Append(c);
				}
				else if (c < ' ')
				{
					StringBuilder stringBuilder2 = stringBuilder.Append("\\u");
					int num = c;
					stringBuilder2.Append(num.ToString("x4"));
				}
				else
				{
					stringBuilder.Append(c);
				}
			}
			return stringBuilder.ToString();
		}

		private void OnDestroy()
		{
			_running = false;
			try
			{
				_cfgWatcher?.Dispose();
			}
			catch
			{
			}
			try
			{
				Harmony harmony = _harmony;
				if (harmony != null)
				{
					harmony.UnpatchSelf();
				}
			}
			catch
			{
			}
			try
			{
				_listener?.Stop();
			}
			catch
			{
			}
			try
			{
				_listener?.Close();
			}
			catch
			{
			}
		}
	}
	[HarmonyPatch(typeof(FactoryProductionStat), "GameTick")]
	internal static class ProductionTally
	{
		internal static readonly long[] Produced = new long[12000];

		internal static readonly long[] Consumed = new long[12000];

		private static void Prefix(FactoryProductionStat __instance)
		{
			int[] productRegister = __instance.productRegister;
			int[] consumeRegister = __instance.consumeRegister;
			if (productRegister == null || consumeRegister == null)
			{
				return;
			}
			int num = Math.Min(Math.Min(productRegister.Length, consumeRegister.Length), Produced.Length);
			for (int i = 0; i < num; i++)
			{
				if (productRegister[i] != 0)
				{
					Produced[i] += productRegister[i];
				}
				if (consumeRegister[i] != 0)
				{
					Consumed[i] += consumeRegister[i];
				}
			}
		}
	}
}