Decompiled source of NOXMFD v0.72.1

plugins/NOXMFD.dll

Decompiled 11 hours ago
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Mirage.Events;
using NuclearOption;
using NuclearOption.Networking;
using NuclearOption.SavedMission;
using Rewired;
using Steamworks;
using TMPro;
using Unity.Collections;
using UnityEngine;
using UnityEngine.Experimental.Rendering;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("NOXMFD")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("NO XMFD: live Nuclear Option telemetry (HUD + MFD)")]
[assembly: AssemblyFileVersion("0.72.1.0")]
[assembly: AssemblyInformationalVersion("0.72.1+0c8ea9c4d40b4e9ff13895798fdb919cf15c71c9")]
[assembly: AssemblyProduct("NOXMFD")]
[assembly: AssemblyTitle("NOXMFD")]
[assembly: AssemblyVersion("0.72.1.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 NOXMFD
{
	internal class AkfTracker
	{
		internal static AkfTracker? Active;

		private readonly AkfTrackerLogic<PersistentID> _logic = new AkfTrackerLogic<PersistentID>();

		private readonly KillLog _killLog = new KillLog();

		public IReadOnlyList<AkfKillEntry> AllFeed => _logic.AllFeed;

		public IReadOnlyList<AkfKillEntry> PlayerFeed => _logic.PlayerFeed;

		public int KillsAircraft => _logic.KillsAircraft;

		public int KillsShip => _logic.KillsShip;

		public int KillsVehicle => _logic.KillsVehicle;

		public int KillsBuilding => _logic.KillsBuilding;

		public int Rank => _logic.Rank;

		public float FundsGained => _logic.FundsGained;

		public float FundsSpent => _logic.FundsSpent;

		internal void RecordKill(PersistentID killerID, PersistentID killedID, KillType killedType)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: 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_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Invalid comparison between Unknown and I4
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: 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_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0154: Unknown result type (might be due to invalid IL or missing references)
			PersistentUnit val = default(PersistentUnit);
			if (!UnitRegistry.TryGetPersistentUnit(killedID, ref val) || val == null)
			{
				return;
			}
			PersistentUnit val2 = default(PersistentUnit);
			bool flag = UnitRegistry.TryGetPersistentUnit(killerID, ref val2) && val2 != null;
			string verb = KillTypeExtensions.GetVerb(killedType, flag);
			FactionHQ val3 = default(FactionHQ);
			GameManager.GetLocalHQ(ref val3);
			bool victimHostile = (Object)(object)val.GetHQ() != (Object)(object)val3;
			Aircraft val4 = default(Aircraft);
			GameManager.GetLocalAircraft(ref val4);
			bool killerIsPlayer = flag && (Object)(object)val4 != (Object)null && killerID == ((Unit)val4).persistentID;
			bool flag2 = (Object)(object)val4 != (Object)null && killedID == ((Unit)val4).persistentID;
			int num;
			if (!flag2 && (int)killedType == 3 && (Object)(object)val4 != (Object)null)
			{
				Unit unit = val.unit;
				Missile val5 = (Missile)(object)((unit is Missile) ? unit : null);
				if (val5 != null)
				{
					num = ((val5.ownerID == ((Unit)val4).persistentID) ? 1 : 0);
					goto IL_00bb;
				}
			}
			num = 0;
			goto IL_00bb;
			IL_00bb:
			bool victimIsPlayerOrdnance = (byte)num != 0;
			AkfKillEntry akfKillEntry = _logic.RecordKill(killerID, flag, (val2 != null) ? PlayerNameOverride.WithSteamName(val2.unitName) : null, val2 != null && (Object)(object)val2.GetHQ() != (Object)(object)val3, PlayerNameOverride.WithSteamName(val.unitName), victimHostile, ToKind(killedType), verb, Time.unscaledTime, killerIsPlayer, flag2, victimIsPlayerOrdnance);
			_killLog.Write(DateTime.Now, MissionManager.CurrentMission?.Name ?? string.Empty, akfKillEntry.Attacker, flag ? FactionName(val2) : null, akfKillEntry.Victim, FactionName(val), ToKind(killedType), akfKillEntry.Weapon);
		}

		internal void CloseLog()
		{
			_killLog.Dispose();
		}

		private static string? FactionName(PersistentUnit unit)
		{
			FactionHQ hQ = unit.GetHQ();
			if (!((Object)(object)hQ != (Object)null) || !((Object)(object)hQ.faction != (Object)null))
			{
				return null;
			}
			return hQ.faction.factionName;
		}

		internal void RecordWeaponHit(PersistentID dealerID, PersistentID missileID)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			PersistentUnit val = default(PersistentUnit);
			if (UnitRegistry.TryGetPersistentUnit(missileID, ref val) && val != null)
			{
				string weaponName = (((Object)(object)val.definition != (Object)null) ? val.definition.unitName : val.unitName);
				_logic.RecordWeaponHit(dealerID, weaponName, Time.unscaledTime);
			}
		}

		internal void TickFunds()
		{
			FactionHQ val = default(FactionHQ);
			bool flag = GameManager.GetLocalHQ(ref val) && (Object)(object)val != (Object)null;
			float current = (flag ? val.factionFunds : 0f);
			_logic.TickFunds(flag, current);
		}

		internal void TickRank()
		{
			Player val = default(Player);
			_logic.SetRank(GameManager.GetLocalPlayer<Player>(ref val) ? val.PlayerRank : 0);
		}

		private static AkfKillKind ToKind(KillType type)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Expected I4, but got Unknown
			return (int)type switch
			{
				0 => AkfKillKind.Aircraft, 
				4 => AkfKillKind.Ship, 
				1 => AkfKillKind.Vehicle, 
				2 => AkfKillKind.Building, 
				_ => AkfKillKind.Missile, 
			};
		}
	}
	internal enum AkfKillKind
	{
		Aircraft,
		Ship,
		Vehicle,
		Building,
		Missile
	}
	internal sealed class AkfTrackerLogic<TId> where TId : notnull
	{
		private readonly struct WeaponHit
		{
			public readonly string Name;

			public readonly float Time;

			public WeaponHit(string name, float time)
			{
				Name = name;
				Time = time;
			}
		}

		public const int MaxFeedLines = 50;

		public const float WeaponTtl = 5f;

		private readonly List<AkfKillEntry> _allFeed = new List<AkfKillEntry>();

		private readonly List<AkfKillEntry> _playerFeed = new List<AkfKillEntry>();

		private readonly Dictionary<TId, WeaponHit> _lastWeaponByAttacker = new Dictionary<TId, WeaponHit>();

		private int _killsAircraft;

		private int _killsShip;

		private int _killsVehicle;

		private int _killsBuilding;

		private int _rank;

		private float _fundsGained;

		private float _fundsSpent;

		private float _prevFunds;

		private bool _fundsInit;

		public IReadOnlyList<AkfKillEntry> AllFeed => _allFeed;

		public IReadOnlyList<AkfKillEntry> PlayerFeed => _playerFeed;

		public int KillsAircraft => _killsAircraft;

		public int KillsShip => _killsShip;

		public int KillsVehicle => _killsVehicle;

		public int KillsBuilding => _killsBuilding;

		public int Rank => _rank;

		public float FundsGained => _fundsGained;

		public float FundsSpent => _fundsSpent;

		public AkfKillEntry RecordKill(TId killerId, bool hasKiller, string? killerName, bool killerHostile, string victimName, bool victimHostile, AkfKillKind killedKind, string verb, float now, bool killerIsPlayer, bool victimIsPlayer, bool victimIsPlayerOrdnance)
		{
			string weapon = null;
			if (hasKiller && _lastWeaponByAttacker.TryGetValue(killerId, out var value) && now - value.Time <= 5f)
			{
				weapon = value.Name;
			}
			AkfKillEntry akfKillEntry = new AkfKillEntry
			{
				Attacker = (hasKiller ? killerName : null),
				AttackerHostile = (hasKiller && killerHostile),
				Victim = victimName,
				VictimHostile = victimHostile,
				Verb = verb,
				Weapon = weapon
			};
			AddCapped(_allFeed, akfKillEntry);
			if (killerIsPlayer)
			{
				AddCapped(_playerFeed, akfKillEntry);
				CountPlayerKill(killedKind);
				return akfKillEntry;
			}
			if (victimIsPlayer || victimIsPlayerOrdnance)
			{
				akfKillEntry.PlayerIsVictim = true;
				AddCapped(_playerFeed, akfKillEntry);
			}
			return akfKillEntry;
		}

		public void RecordWeaponHit(TId dealerId, string weaponName, float now)
		{
			if (!string.IsNullOrEmpty(weaponName))
			{
				_lastWeaponByAttacker[dealerId] = new WeaponHit(weaponName, now);
			}
		}

		public void TickFunds(bool hasFunds, float current)
		{
			if (!hasFunds)
			{
				_fundsInit = false;
				return;
			}
			if (_fundsInit)
			{
				float num = current - _prevFunds;
				if (num > 0f)
				{
					_fundsGained += num;
				}
				else if (num < 0f)
				{
					_fundsSpent += 0f - num;
				}
			}
			_prevFunds = current;
			_fundsInit = true;
		}

		public void SetRank(int rank)
		{
			_rank = rank;
		}

		private void CountPlayerKill(AkfKillKind kind)
		{
			switch (kind)
			{
			case AkfKillKind.Aircraft:
				_killsAircraft++;
				break;
			case AkfKillKind.Ship:
				_killsShip++;
				break;
			case AkfKillKind.Vehicle:
				_killsVehicle++;
				break;
			case AkfKillKind.Building:
				_killsBuilding++;
				break;
			}
		}

		private static void AddCapped(List<AkfKillEntry> list, AkfKillEntry entry)
		{
			list.Add(entry);
			if (list.Count > 50)
			{
				list.RemoveAt(0);
			}
		}
	}
	internal sealed class KillLog : IDisposable
	{
		internal static string Dir = string.Empty;

		internal static Func<bool> Enabled = () => false;

		internal static Action<string>? LogWarning;

		internal const string Header = "time,killer,killer_faction,victim,victim_faction,type,weapon";

		private StreamWriter? _writer;

		private bool _failed;

		internal void Write(DateTime localTime, string missionName, string? killer, string? killerFaction, string victim, string? victimFaction, AkfKillKind kind, string? weapon)
		{
			if (_failed || !Enabled())
			{
				return;
			}
			try
			{
				if (_writer == null)
				{
					_writer = Open(Path.Combine(Dir, FileName(missionName, localTime)));
				}
				_writer.WriteLine(Row(localTime, killer, killerFaction, victim, victimFaction, kind, weapon));
			}
			catch (Exception ex)
			{
				_failed = true;
				LogWarning?.Invoke("[NOXMFD] Kill log stopped for this mission: " + ex.Message);
			}
		}

		public void Dispose()
		{
			try
			{
				_writer?.Dispose();
			}
			catch (Exception ex)
			{
				LogWarning?.Invoke("[NOXMFD] Kill log close failed: " + ex.Message);
			}
			_writer = null;
		}

		private static StreamWriter Open(string path)
		{
			Directory.CreateDirectory(Path.GetDirectoryName(path));
			bool num = !File.Exists(path);
			StreamWriter streamWriter = new StreamWriter(path, append: true, new UTF8Encoding(encoderShouldEmitUTF8Identifier: true))
			{
				AutoFlush = true
			};
			if (num)
			{
				streamWriter.WriteLine("time,killer,killer_faction,victim,victim_faction,type,weapon");
			}
			return streamWriter;
		}

		internal static string FileName(string missionName, DateTime localTime)
		{
			StringBuilder stringBuilder = new StringBuilder("kills_").Append(localTime.ToString("yyyy-MM-dd_HH-mm-ss", CultureInfo.InvariantCulture));
			if (!string.IsNullOrWhiteSpace(missionName))
			{
				stringBuilder.Append('_');
				string text = missionName.Trim();
				foreach (char c in text)
				{
					stringBuilder.Append((char.IsLetterOrDigit(c) || c == '-') ? c : '_');
				}
			}
			return stringBuilder.Append(".csv").ToString();
		}

		internal static string Row(DateTime localTime, string? killer, string? killerFaction, string victim, string? victimFaction, AkfKillKind kind, string? weapon)
		{
			return string.Join(",", localTime.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture), Cell(killer), Cell(killerFaction), Cell(victim), Cell(victimFaction), kind.ToString().ToLowerInvariant(), Cell(weapon));
		}

		internal static string Cell(string? value)
		{
			if (string.IsNullOrEmpty(value))
			{
				return string.Empty;
			}
			string text = value;
			char c = text[0];
			if (c == '=' || c == '+' || c == '-' || c == '@' || c == '\t' || c == '\r')
			{
				text = "'" + text;
			}
			if (text.IndexOfAny(new char[4] { ',', '"', '\n', '\r' }) < 0)
			{
				return text;
			}
			return "\"" + text.Replace("\"", "\"\"") + "\"";
		}
	}
	internal class AssetCapture
	{
		private const int MapMaxDim = 4096;

		private const int MapJpegQuality = 85;

		internal const int IconsPerScan = 16;

		internal const string MissileIconKey = "__missilewarn";

		private readonly HashSet<string> _capturedIcons = new HashSet<string>();

		private readonly HashSet<string> _loggedPartLayouts = new HashSet<string>();

		private readonly HashSet<string> _capturedAirframes = new HashSet<string>();

		private readonly HashSet<string> _capturedWeaponIcons = new HashSet<string>();

		private bool _capturedFlareIcon;

		private bool _capturedJammerIcon;

		private bool _missileIconCaptured;

		private bool _mapCaptured;

		private bool _statusDisplayReadFailureLogged;

		private bool _failureIndicatorReadFailureLogged;

		private static FieldInfo? _sdStatusDisplaysField;

		private static FieldInfo? _sdBackgroundField;

		private static FieldInfo? _sdFailureIndicatorsField;

		private GameObject[] _failureIndicators = Array.Empty<GameObject>();

		private readonly HashSet<string> _capturedFrontal = new HashSet<string>();

		private readonly Dictionary<string, List<(string name, Image img)>> _frontalMarkers = new Dictionary<string, List<(string, Image)>>();

		private readonly HashSet<string> _loggedWeapons = new HashSet<string>();

		private readonly HashSet<string> _capturedTgtIcons = new HashSet<string>();

		private readonly HashSet<string> _capturedBdfShipIcons = new HashSet<string>();

		private readonly HashSet<string> _capturedBdfLogos = new HashSet<string>();

		private readonly HashSet<string> _capturedBuildingIcons = new HashSet<string>();

		private readonly HashSet<string> _capturedHudCatIcons = new HashSet<string>();

		private static readonly (int index, string key)[] HudCategoryIconSlots = new(int, string)[5]
		{
			(2, "AIRCRAFT"),
			(3, "MISSILES"),
			(4, "VEHICLES"),
			(5, "BUILDINGS"),
			(6, "SHIPS")
		};

		public GameObject[] FailureIndicators => _failureIndicators;

		public void TryCaptureAirframe(Aircraft ac)
		{
			//IL_0176: Unknown result type (might be due to invalid IL or missing references)
			//IL_017b: Unknown result type (might be due to invalid IL or missing references)
			//IL_018d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0192: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b2: 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_01bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0217: Unknown result type (might be due to invalid IL or missing references)
			//IL_021c: Unknown result type (might be due to invalid IL or missing references)
			//IL_022c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0231: Unknown result type (might be due to invalid IL or missing references)
			//IL_0251: Unknown result type (might be due to invalid IL or missing references)
			//IL_0260: Unknown result type (might be due to invalid IL or missing references)
			//IL_026f: Unknown result type (might be due to invalid IL or missing references)
			//IL_027e: Unknown result type (might be due to invalid IL or missing references)
			//IL_028d: Unknown result type (might be due to invalid IL or missing references)
			//IL_029c: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_02cb: Unknown result type (might be due to invalid IL or missing references)
			string text = (((Object)(object)ac.definition != (Object)null) ? ((UnitDefinition)ac.definition).unitName : null);
			if (string.IsNullOrEmpty(text) || _capturedAirframes.Contains(text))
			{
				return;
			}
			StatusDisplay val = Object.FindObjectOfType<StatusDisplay>(true);
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			if (_sdStatusDisplaysField == null)
			{
				_sdStatusDisplaysField = typeof(StatusDisplay).GetField("statusDisplays", BindingFlags.Instance | BindingFlags.NonPublic);
			}
			if (_sdBackgroundField == null)
			{
				_sdBackgroundField = typeof(StatusDisplay).GetField("aircraftBackground", BindingFlags.Instance | BindingFlags.NonPublic);
			}
			if (_sdFailureIndicatorsField == null)
			{
				_sdFailureIndicatorsField = typeof(StatusDisplay).GetField("failureIndicators", BindingFlags.Instance | BindingFlags.NonPublic);
			}
			if (_sdStatusDisplaysField == null || _sdBackgroundField == null)
			{
				ManualLogSource? log = Plugin.Log;
				if (log != null)
				{
					log.LogWarning((object)"[NOXMFD] AVN: StatusDisplay reflection fields not found — airframe capture disabled.");
				}
				_capturedAirframes.Add(text);
				return;
			}
			Image val2;
			IList list;
			try
			{
				object? value = _sdBackgroundField.GetValue(val);
				val2 = (Image)((value is Image) ? value : null);
				list = _sdStatusDisplaysField.GetValue(val) as IList;
			}
			catch (Exception arg)
			{
				if (!_statusDisplayReadFailureLogged)
				{
					_statusDisplayReadFailureLogged = true;
					ManualLogSource? log2 = Plugin.Log;
					if (log2 != null)
					{
						log2.LogWarning((object)$"[NOXMFD] AVN: StatusDisplay read failed; airframe capture will retry: {arg}");
					}
				}
				return;
			}
			if ((Object)(object)val2 == (Object)null || list == null)
			{
				return;
			}
			RectTransform rectTransform = ((Graphic)val2).rectTransform;
			if ((Object)(object)val2.sprite == (Object)null)
			{
				return;
			}
			Rect rect = rectTransform.rect;
			if (((Rect)(ref rect)).width <= 0.0001f)
			{
				return;
			}
			rect = rectTransform.rect;
			if (((Rect)(ref rect)).height <= 0.0001f)
			{
				return;
			}
			_capturedAirframes.Add(text);
			Vector3 lossyScale = ((Transform)rectTransform).lossyScale;
			Vector3 right = ((Transform)rectTransform).right;
			Vector3 up = ((Transform)rectTransform).up;
			Vector3 eulerAngles = ((Transform)rectTransform).eulerAngles;
			ManualLogSource? log3 = Plugin.Log;
			if (log3 != null)
			{
				string text2 = $"[NOXMFD] AVN bg lossyScale=({lossyScale.x:0.000},{lossyScale.y:0.000},{lossyScale.z:0.000})  ";
				rect = rectTransform.rect;
				object arg2 = ((Rect)(ref rect)).width;
				rect = rectTransform.rect;
				log3.LogInfo((object)(text2 + $"rectSize=({arg2:0.0},{((Rect)(ref rect)).height:0.0})  " + $"right=({right.x:0.00},{right.y:0.00},{right.z:0.00})  up=({up.x:0.00},{up.y:0.00},{up.z:0.00})  " + $"euler=({eulerAngles.x:0.0},{eulerAngles.y:0.0},{eulerAngles.z:0.0})"));
			}
			if ((Object)(object)val2.sprite != (Object)null)
			{
				string bgKey = text;
				SpriteCapture.Request(val2.sprite, SpriteCapture.Encoding.Png, synthAlpha: false, 0, 0, delegate(byte[]? bgPng)
				{
					if (bgPng != null)
					{
						TelemetryServer.SetAirframeImage(bgKey, "__bg", bgPng);
					}
				});
			}
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append("{\"type\":\"").Append(JsonLite.EscapeJson(text)).Append("\",\"parts\":[");
			int num = 0;
			int num2 = 0;
			for (int num3 = 0; num3 < list.Count; num3++)
			{
				object? obj = list[num3];
				PartStatusDisplay val3 = (PartStatusDisplay)((obj is PartStatusDisplay) ? obj : null);
				if (val3 == null || (Object)(object)val3.partImage == (Object)null)
				{
					continue;
				}
				Image partImage = val3.partImage;
				string text3 = (((Object)(object)((Component)partImage).gameObject != (Object)null) ? ((Object)((Component)partImage).gameObject).name : null);
				if (string.IsNullOrEmpty(text3) || !GetPartPlacement(((Graphic)partImage).rectTransform, rectTransform, out var cx, out var cy, out var w, out var h, out var rotZ, out var sx, out var sy) || (w >= 0.98f && h >= 0.98f && Mathf.Abs(cx - 0.5f) < 0.02f && Mathf.Abs(cy - 0.5f) < 0.02f))
				{
					continue;
				}
				if ((Object)(object)partImage.sprite != (Object)null)
				{
					string partKey = text;
					string partName = text3;
					SpriteCapture.Request(partImage.sprite, SpriteCapture.Encoding.Png, synthAlpha: false, 0, 0, delegate(byte[]? png)
					{
						if (png != null)
						{
							TelemetryServer.SetAirframeImage(partKey, partName, png);
						}
					});
				}
				if (num > 0)
				{
					stringBuilder.Append(',');
				}
				num++;
				if (sx < 0 || sy < 0)
				{
					num2++;
				}
				stringBuilder.Append('{').Append("\"n\":\"").Append(JsonLite.EscapeJson(text3))
					.Append("\",")
					.Append("\"cx\":")
					.Append(cx.ToString("0.00000", CultureInfo.InvariantCulture))
					.Append(',')
					.Append("\"cy\":")
					.Append(cy.ToString("0.00000", CultureInfo.InvariantCulture))
					.Append(',')
					.Append("\"w\":")
					.Append(w.ToString("0.00000", CultureInfo.InvariantCulture))
					.Append(',')
					.Append("\"h\":")
					.Append(h.ToString("0.00000", CultureInfo.InvariantCulture))
					.Append(',')
					.Append("\"r\":")
					.Append(rotZ.ToString("0.0", CultureInfo.InvariantCulture))
					.Append(',')
					.Append("\"sx\":")
					.Append(sx)
					.Append(',')
					.Append("\"sy\":")
					.Append(sy)
					.Append(',')
					.Append("\"rt\":")
					.Append(val3.redStatusThreshold.ToString("0.0", CultureInfo.InvariantCulture))
					.Append('}');
			}
			stringBuilder.Append("]}");
			TelemetryServer.SetAirframeLayout(text, stringBuilder.ToString());
			List<GameObject> list2 = new List<GameObject>();
			IList list3 = null;
			try
			{
				list3 = _sdFailureIndicatorsField?.GetValue(val) as IList;
			}
			catch (Exception arg3)
			{
				if (!_failureIndicatorReadFailureLogged)
				{
					_failureIndicatorReadFailureLogged = true;
					ManualLogSource? log4 = Plugin.Log;
					if (log4 != null)
					{
						log4.LogWarning((object)$"[NOXMFD] AVN: failure-indicator read failed: {arg3}");
					}
				}
			}
			if (list3 != null)
			{
				for (int num4 = 0; num4 < list3.Count; num4++)
				{
					object? obj2 = list3[num4];
					GameObject val4 = (GameObject)((obj2 is GameObject) ? obj2 : null);
					if ((Object)(object)val4 != (Object)null)
					{
						list2.Add(val4);
					}
				}
			}
			_failureIndicators = list2.ToArray();
			ManualLogSource? log5 = Plugin.Log;
			if (log5 != null)
			{
				log5.LogInfo((object)string.Format("[NOXMFD] Captured airframe silhouette '{0}' (bg + {1} parts, {2} flipped, {3} failure messages: {4}).", text, num, num2, _failureIndicators.Length, string.Join(", ", _failureIndicators.Select((GameObject g) => ((Object)g).name))));
			}
		}

		private static bool GetPartPlacement(RectTransform partRT, RectTransform bgRT, out float cx, out float cy, out float w, out float h, out float rotZ, out int sx, out int sy)
		{
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: 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_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: 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_0101: 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_012b: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: 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_0168: Unknown result type (might be due to invalid IL or missing references)
			//IL_017c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0181: Unknown result type (might be due to invalid IL or missing references)
			//IL_019b: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bb: 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)
			//IL_01ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0204: Unknown result type (might be due to invalid IL or missing references)
			cx = (cy = (w = (h = (rotZ = 0f))));
			sx = (sy = 1);
			if ((Object)(object)partRT == (Object)null || (Object)(object)bgRT == (Object)null)
			{
				return false;
			}
			Rect rect = bgRT.rect;
			if (((Rect)(ref rect)).width <= 0.0001f || ((Rect)(ref rect)).height <= 0.0001f)
			{
				return false;
			}
			Rect rect2 = partRT.rect;
			Vector3 val = ((Transform)partRT).TransformPoint(Vector2.op_Implicit(((Rect)(ref rect2)).center));
			Vector3 val2 = ((Transform)bgRT).InverseTransformPoint(val);
			cx = (val2.x - ((Rect)(ref rect)).xMin) / ((Rect)(ref rect)).width;
			cy = (val2.y - ((Rect)(ref rect)).yMin) / ((Rect)(ref rect)).height;
			cy = 1f - cy;
			if (((Transform)bgRT).right.x < 0f)
			{
				cx = 1f - cx;
			}
			if (((Transform)bgRT).up.y < 0f)
			{
				cy = 1f - cy;
			}
			float num = ((((Transform)bgRT).lossyScale.x == 0f) ? 1f : Mathf.Abs(((Transform)bgRT).lossyScale.x));
			float num2 = ((((Transform)bgRT).lossyScale.y == 0f) ? 1f : Mathf.Abs(((Transform)bgRT).lossyScale.y));
			float num3 = Mathf.Abs(((Transform)partRT).lossyScale.x);
			float num4 = Mathf.Abs(((Transform)partRT).lossyScale.y);
			rect2 = partRT.rect;
			w = ((Rect)(ref rect2)).width * (num3 / num) / ((Rect)(ref rect)).width;
			rect2 = partRT.rect;
			h = ((Rect)(ref rect2)).height * (num4 / num2) / ((Rect)(ref rect)).height;
			rotZ = ((Transform)partRT).localEulerAngles.z;
			if (rotZ > 180f)
			{
				rotZ -= 360f;
			}
			float x = ((Transform)partRT).lossyScale.x;
			float y = ((Transform)partRT).lossyScale.y;
			float x2 = ((Transform)bgRT).lossyScale.x;
			float y2 = ((Transform)bgRT).lossyScale.y;
			sx = ((x == 0f || x2 == 0f) ? 1 : ((!(x * x2 < 0f)) ? 1 : (-1)));
			sy = ((y == 0f || y2 == 0f) ? 1 : ((!(y * y2 < 0f)) ? 1 : (-1)));
			return true;
		}

		public void TryCaptureFrontalSilhouette(Aircraft ac)
		{
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			string text = (((Object)(object)ac.definition != (Object)null) ? ((UnitDefinition)ac.definition).unitName : null);
			if (string.IsNullOrEmpty(text) || _capturedFrontal.Contains(text))
			{
				return;
			}
			Image val = null;
			Image[] array = Resources.FindObjectsOfTypeAll<Image>();
			foreach (Image val2 in array)
			{
				if (!((Object)(object)val2 == (Object)null) && !(((Object)((Component)val2).gameObject).name != "frontProfile"))
				{
					Scene scene = ((Component)val2).gameObject.scene;
					if (((Scene)(ref scene)).name != null)
					{
						val = val2;
						break;
					}
				}
			}
			if ((Object)(object)val == (Object)null || (Object)(object)val.sprite == (Object)null)
			{
				return;
			}
			_capturedFrontal.Add(text);
			string bgKey = text;
			SpriteCapture.Request(val.sprite, SpriteCapture.Encoding.Png, synthAlpha: false, 0, 0, delegate(byte[]? png)
			{
				if (png != null)
				{
					TelemetryServer.SetAirframeImage(bgKey, "__front", png);
				}
			});
			RectTransform rectTransform = ((Graphic)val).rectTransform;
			List<(string, Image)> list = new List<(string, Image)>();
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append("{\"parts\":[");
			int num = 0;
			for (int num2 = 0; num2 < ((Transform)rectTransform).childCount; num2++)
			{
				Transform child = ((Transform)rectTransform).GetChild(num2);
				if (!((Object)child).name.StartsWith("hardpoint", StringComparison.OrdinalIgnoreCase))
				{
					continue;
				}
				Image component = ((Component)child).GetComponent<Image>();
				if (!((Object)(object)component == (Object)null) && GetPartPlacement(((Graphic)component).rectTransform, rectTransform, out var cx, out var cy, out var w, out var h, out var _, out var _, out var _))
				{
					list.Add((((Object)child).name, component));
					if (num > 0)
					{
						stringBuilder.Append(',');
					}
					num++;
					stringBuilder.Append('{').Append("\"n\":\"").Append(JsonLite.EscapeJson(((Object)child).name))
						.Append("\",")
						.Append("\"cx\":")
						.Append(cx.ToString("0.00000", CultureInfo.InvariantCulture))
						.Append(',')
						.Append("\"cy\":")
						.Append(cy.ToString("0.00000", CultureInfo.InvariantCulture))
						.Append(',')
						.Append("\"w\":")
						.Append(w.ToString("0.00000", CultureInfo.InvariantCulture))
						.Append(',')
						.Append("\"h\":")
						.Append(h.ToString("0.00000", CultureInfo.InvariantCulture))
						.Append('}');
				}
			}
			stringBuilder.Append("]}");
			_frontalMarkers[text] = list;
			TelemetryServer.SetAirframeLayout(text + "__front", stringBuilder.ToString());
		}

		public List<(string name, string state)> ReadFrontalMarkerStates(string? type)
		{
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: 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_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			List<(string, string)> list = new List<(string, string)>();
			if (string.IsNullOrEmpty(type) || !_frontalMarkers.TryGetValue(type, out List<(string, Image)> value))
			{
				return list;
			}
			foreach (var (item, val) in value)
			{
				if (!((Object)(object)val == (Object)null))
				{
					Color color = ((Graphic)val).color;
					string item2 = ((color.r == color.g && color.g == color.b) ? "empty" : ((color.g >= color.r) ? "armed" : "exhausted"));
					list.Add((item, item2));
				}
			}
			return list;
		}

		public void TryLogPartLayout(Aircraft ac)
		{
			string text = (((Object)(object)ac.definition != (Object)null) ? ((UnitDefinition)ac.definition).unitName : null);
			if (string.IsNullOrEmpty(text) || _loggedPartLayouts.Contains(text))
			{
				return;
			}
			List<UnitPart> partLookup = ((Unit)ac).partLookup;
			if (partLookup == null)
			{
				return;
			}
			_loggedPartLayouts.Add(text);
			ManualLogSource? log = Plugin.Log;
			if (log != null)
			{
				log.LogInfo((object)$"[NOXMFD] AVN parts for '{text}' (count={partLookup.Count}):");
			}
			for (int i = 0; i < partLookup.Count; i++)
			{
				UnitPart val = partLookup[i];
				if ((Object)(object)val == (Object)null)
				{
					ManualLogSource? log2 = Plugin.Log;
					if (log2 != null)
					{
						log2.LogInfo((object)$"  [{i}] <null>");
					}
					continue;
				}
				string text2 = (((Object)(object)((Component)val).gameObject != (Object)null) ? ((Object)((Component)val).gameObject).name : "<no-go>");
				ManualLogSource? log3 = Plugin.Log;
				if (log3 != null)
				{
					log3.LogInfo((object)$"  [{i}] {text2}  hp={val.hitPoints:0.#}  detached={val.IsDetached()}");
				}
			}
		}

		public void TryLogWeaponInfo(Aircraft ac)
		{
			List<WeaponStation> weaponStations = ((Unit)ac).weaponStations;
			if (weaponStations == null)
			{
				return;
			}
			foreach (WeaponStation item in weaponStations)
			{
				WeaponInfo val = item?.WeaponInfo;
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				string text = ((!string.IsNullOrEmpty(((Object)val).name)) ? ((Object)val).name : ((!string.IsNullOrEmpty(val.weaponName)) ? val.weaponName : (val.shortName ?? "?")));
				if (!_loggedWeapons.Contains(text))
				{
					_loggedWeapons.Add(text);
					ManualLogSource? log = Plugin.Log;
					if (log != null)
					{
						log.LogInfo((object)("[NOXMFD] Weapon '" + text + "' name=\"" + val.weaponName + "\" short=\"" + val.shortName + "\" " + $"flags[gun={val.gun} missile={val.missile} bomb={val.bomb} glideBomb={val.glideBomb} " + $"jammer={val.jammer} energy={val.energy} nuclear={val.nuclear} strategic={val.strategic} " + $"overHorizon={val.overHorizon} laserGuided={val.laserGuided} boresight={val.boresight} " + $"cargo={val.cargo} troops={val.troops} sling={val.sling} " + $"rearmGround={val.rearmGround} rearmShip={val.rearmShip} hideInDisplay={val.hideInDisplay}] " + $"role[antiSurface={val.effectiveness.antiSurface:0.##} antiAir={val.effectiveness.antiAir:0.##} " + $"antiMissile={val.effectiveness.antiMissile:0.##} antiRadar={val.effectiveness.antiRadar:0.##}] " + $"pK={val.pK:0.###} fireInterval={val.fireInterval:0.###} muzzleVelocity={val.muzzleVelocity:0.#} " + $"maxSpeed={val.maxSpeed:0.#} dragCoef={val.dragCoef:0.###} gravMult={val.gravMult:0.##} " + $"pierceDamage={val.pierceDamage:0.#} blastDamage={val.blastDamage:0.#} " + $"armorTierEffectiveness={val.armorTierEffectiveness:0.##} airburstHeight={val.airburstHeight:0.#} " + $"visibilityWhenFired={val.visibilityWhenFired:0.##} costPerRound={val.costPerRound:0.#} " + $"massPerRound={val.massPerRound:0.##} useWeaponDoors={val.useWeaponDoors} " + $"hasIcon={(Object)(object)val.weaponIcon != (Object)null}"));
					}
				}
			}
		}

		public void TryCaptureCmIcons(Aircraft ac)
		{
			if (!_capturedFlareIcon)
			{
				FlareEjector componentInChildren = ((Component)ac).GetComponentInChildren<FlareEjector>();
				if ((Object)(object)componentInChildren != (Object)null && (Object)(object)((Countermeasure)componentInChildren).displayImage != (Object)null)
				{
					_capturedFlareIcon = true;
					SpriteCapture.Request(((Countermeasure)componentInChildren).displayImage, SpriteCapture.Encoding.Png, synthAlpha: true, 0, 0, delegate(byte[]? png)
					{
						if (png != null)
						{
							TelemetryServer.SetCmIcon("flares", png);
						}
					});
				}
			}
			if (_capturedJammerIcon)
			{
				return;
			}
			RadarJammer componentInChildren2 = ((Component)ac).GetComponentInChildren<RadarJammer>();
			if (!((Object)(object)componentInChildren2 != (Object)null) || !((Object)(object)((Countermeasure)componentInChildren2).displayImage != (Object)null))
			{
				return;
			}
			_capturedJammerIcon = true;
			SpriteCapture.Request(((Countermeasure)componentInChildren2).displayImage, SpriteCapture.Encoding.Png, synthAlpha: true, 0, 0, delegate(byte[]? png)
			{
				if (png != null)
				{
					TelemetryServer.SetCmIcon("jammer", png);
				}
			});
		}

		public void TryCaptureVehicleTypeIcons()
		{
			Encyclopedia i = Encyclopedia.i;
			if (!((Object)(object)i == (Object)null))
			{
				CaptureTypeIcons(i.vehicleTypes, _capturedTgtIcons, (UnitType vt) => vt.typeName, (UnitType vt) => vt.typeSprite, TelemetryServer.SetTgtIcon);
			}
		}

		public void TryCaptureShipTypeIcons()
		{
			Encyclopedia i = Encyclopedia.i;
			if (!((Object)(object)i == (Object)null))
			{
				CaptureTypeIcons(i.shipTypes, _capturedBdfShipIcons, (UnitType st) => st.typeName, (UnitType st) => st.typeSprite, TelemetryServer.SetBdfIcon);
			}
		}

		public void TryCaptureFactionLogo(FactionHQ hq)
		{
			Faction val = (((Object)(object)hq != (Object)null) ? hq.faction : null);
			if ((Object)(object)val == (Object)null || string.IsNullOrEmpty(val.factionName))
			{
				return;
			}
			string name = val.factionName;
			if (_capturedBdfLogos.Contains(name))
			{
				return;
			}
			_capturedBdfLogos.Add(name);
			if ((Object)(object)val.factionColorLogo == (Object)null)
			{
				return;
			}
			SpriteCapture.Request(val.factionColorLogo, SpriteCapture.Encoding.Png, synthAlpha: true, 0, 0, delegate(byte[]? png)
			{
				if (png != null)
				{
					TelemetryServer.SetBdfIcon(name, png);
				}
			});
		}

		public void TryCaptureBuildingTypeIcons()
		{
			Encyclopedia i = Encyclopedia.i;
			if (!((Object)(object)i == (Object)null))
			{
				CaptureTypeIcons(i.buildingTypes, _capturedBuildingIcons, (UnitType bt) => bt.typeName, (UnitType bt) => bt.typeSprite, TelemetryServer.SetBuildingIcon);
			}
		}

		private static void CaptureTypeIcons<T>(IEnumerable<T> list, HashSet<string> captured, Func<T, string> nameOf, Func<T, Sprite> spriteOf, Action<string, byte[]> setIcon) where T : class
		{
			if (list == null)
			{
				return;
			}
			foreach (T item in list)
			{
				if (item == null)
				{
					continue;
				}
				string name = nameOf(item);
				if (string.IsNullOrEmpty(name) || captured.Contains(name))
				{
					continue;
				}
				captured.Add(name);
				Sprite val = spriteOf(item);
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				SpriteCapture.Request(val, SpriteCapture.Encoding.Png, synthAlpha: true, 0, 0, delegate(byte[]? png)
				{
					if (png != null)
					{
						setIcon(name, png);
					}
				});
			}
		}

		public void TryCaptureHudCategoryIcons()
		{
			HUDOptions i = SceneSingleton<HUDOptions>.i;
			if ((Object)(object)i == (Object)null || i.listCategories == null)
			{
				return;
			}
			(int, string)[] hudCategoryIconSlots = HudCategoryIconSlots;
			for (int j = 0; j < hudCategoryIconSlots.Length; j++)
			{
				(int, string) tuple = hudCategoryIconSlots[j];
				if (_capturedHudCatIcons.Contains(tuple.Item2) || tuple.Item1 >= i.listCategories.Count)
				{
					continue;
				}
				HUDOptions_Category val = i.listCategories[tuple.Item1];
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				Transform val2 = ((Component)val).transform.Find("TopContainer/Icon");
				Image val3 = (((Object)(object)val2 != (Object)null) ? ((Component)val2).GetComponent<Image>() : null);
				if ((Object)(object)val3 == (Object)null || (Object)(object)val3.sprite == (Object)null)
				{
					continue;
				}
				_capturedHudCatIcons.Add(tuple.Item2);
				string key = tuple.Item2;
				SpriteCapture.Request(val3.sprite, SpriteCapture.Encoding.Png, synthAlpha: true, 0, 0, delegate(byte[]? png)
				{
					if (png != null)
					{
						TelemetryServer.SetHudCategoryIcon(key, png);
					}
				});
			}
		}

		public void TryCaptureWeaponIcon(string name, Sprite icon)
		{
			if (string.IsNullOrEmpty(name) || _capturedWeaponIcons.Contains(name))
			{
				return;
			}
			_capturedWeaponIcons.Add(name);
			if ((Object)(object)icon == (Object)null)
			{
				return;
			}
			string weaponName = name;
			SpriteCapture.Request(icon, SpriteCapture.Encoding.Png, synthAlpha: true, 0, 0, delegate(byte[]? png)
			{
				if (png != null)
				{
					TelemetryServer.SetWeaponIcon(weaponName, png);
				}
			});
		}

		public void TryCaptureMap(MapSettings ms)
		{
			if (_mapCaptured)
			{
				return;
			}
			Sprite mapImage = ms.MapImage;
			if (!((Object)(object)mapImage == (Object)null))
			{
				_mapCaptured = true;
				Texture texture = (Texture)(object)mapImage.texture;
				int sw = (((Object)(object)texture != (Object)null) ? texture.width : 0);
				int sh = (((Object)(object)texture != (Object)null) ? texture.height : 0);
				if (!SpriteCapture.Request(mapImage, SpriteCapture.Encoding.Jpg, synthAlpha: false, 85, 4096, delegate(byte[]? jpg)
				{
					if (jpg != null)
					{
						TelemetryServer.SetMapImage(jpg);
						ManualLogSource? log = Plugin.Log;
						if (log != null)
						{
							log.LogInfo((object)$"[NOXMFD] Captured in-game map ({jpg.Length} bytes, JPEG; source {sw}x{sh}).");
						}
					}
					else
					{
						ManualLogSource? log2 = Plugin.Log;
						if (log2 != null)
						{
							log2.LogWarning((object)"[NOXMFD] Map capture failed; falling back to map file.");
						}
					}
				}))
				{
					_mapCaptured = false;
				}
			}
		}

		public void CaptureMissileWarningIcon()
		{
			if (_missileIconCaptured)
			{
				return;
			}
			try
			{
				GameAssets i = GameAssets.i;
				if ((Object)(object)i == (Object)null || (Object)(object)i.missileWarningSprite == (Object)null)
				{
					return;
				}
				_missileIconCaptured = true;
				SpriteCapture.Request(i.missileWarningSprite, SpriteCapture.Encoding.Png, synthAlpha: true, 0, 0, delegate(byte[]? png)
				{
					if (png != null)
					{
						TelemetryServer.SetIcon("__missilewarn", png);
					}
				});
			}
			catch
			{
			}
		}

		public bool TryCaptureIcon(UnitDefinition def)
		{
			if ((Object)(object)def == (Object)null || string.IsNullOrEmpty(def.unitName) || _capturedIcons.Contains(def.unitName))
			{
				return false;
			}
			_capturedIcons.Add(def.unitName);
			if ((Object)(object)def.mapIcon == (Object)null)
			{
				TelemetryServer.SetIcon(def.unitName, TelemetryServer.NoIconPng);
				return false;
			}
			string name = def.unitName;
			bool num = SpriteCapture.Request(def.mapIcon, SpriteCapture.Encoding.Png, synthAlpha: true, 0, 0, delegate(byte[]? png)
			{
				TelemetryServer.SetIcon(name, png ?? TelemetryServer.NoIconPng);
			});
			if (!num)
			{
				TelemetryServer.SetIcon(name, TelemetryServer.NoIconPng);
			}
			return num;
		}
	}
	internal static class SpriteCapture
	{
		internal enum Encoding
		{
			Png,
			Jpg
		}

		private const GraphicsFormat Fmt = (GraphicsFormat)4;

		public static bool Request(Sprite sprite, Encoding enc, bool synthAlpha, int quality, int maxDim, Action<byte[]?> onEncoded)
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_015d: Unknown result type (might be due to invalid IL or missing references)
			//IL_015f: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Expected O, but got Unknown
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_0154: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)sprite == (Object)null || (Object)(object)sprite.texture == (Object)null)
			{
				return false;
			}
			Texture2D texture = sprite.texture;
			Rect textureRect = sprite.textureRect;
			int num = Mathf.RoundToInt(((Rect)(ref textureRect)).width);
			int num2 = Mathf.RoundToInt(((Rect)(ref textureRect)).height);
			if (num <= 0 || num2 <= 0)
			{
				num = ((Texture)texture).width;
				num2 = ((Texture)texture).height;
				((Rect)(ref textureRect))..ctor(0f, 0f, (float)num, (float)num2);
			}
			var (tw, th) = TgpFeedSettings.FitWithinMaxDimension(num, num2, maxDim);
			Vector2 val = default(Vector2);
			((Vector2)(ref val))..ctor(((Rect)(ref textureRect)).width / (float)((Texture)texture).width, ((Rect)(ref textureRect)).height / (float)((Texture)texture).height);
			Vector2 val2 = default(Vector2);
			((Vector2)(ref val2))..ctor(((Rect)(ref textureRect)).x / (float)((Texture)texture).width, ((Rect)(ref textureRect)).y / (float)((Texture)texture).height);
			if (SystemInfo.supportsAsyncGPUReadback)
			{
				RenderTexture rt = new RenderTexture(tw, th, 0, (RenderTextureFormat)0, (RenderTextureReadWrite)2);
				rt.Create();
				Graphics.Blit((Texture)(object)texture, rt, val, val2);
				AsyncGPUReadback.Request((Texture)(object)rt, 0, (GraphicsFormat)4, (Action<AsyncGPUReadbackRequest>)delegate(AsyncGPUReadbackRequest req)
				{
					//IL_0000: Unknown result type (might be due to invalid IL or missing references)
					OnReadback(req, rt, tw, th, enc, synthAlpha, quality, onEncoded);
				});
				return true;
			}
			return RequestSync(texture, val, val2, tw, th, enc, synthAlpha, quality, onEncoded);
		}

		private static void OnReadback(AsyncGPUReadbackRequest req, RenderTexture rt, int w, int h, Encoding enc, bool synthAlpha, int quality, Action<byte[]?> onEncoded)
		{
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (((AsyncGPUReadbackRequest)(ref req)).hasError)
				{
					Deliver(onEncoded, null);
					return;
				}
				NativeArray<byte> data = ((AsyncGPUReadbackRequest)(ref req)).GetData<byte>(0);
				byte[] bytes = new byte[data.Length];
				data.CopyTo(bytes);
				Task.Run(delegate
				{
					EncodeAndDeliver(bytes, w, h, enc, synthAlpha, quality, onEncoded);
				});
			}
			catch (Exception ex)
			{
				ManualLogSource? log = Plugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("[NOXMFD] SpriteCapture readback failed: " + ex.Message));
				}
				Deliver(onEncoded, null);
			}
			finally
			{
				rt.Release();
				Object.Destroy((Object)(object)rt);
			}
		}

		private static bool RequestSync(Texture2D tex, Vector2 scale, Vector2 offset, int w, int h, Encoding enc, bool synthAlpha, int quality, Action<byte[]?> onEncoded)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: 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_002e: Expected O, but got Unknown
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			RenderTexture temporary = RenderTexture.GetTemporary(w, h, 0, (RenderTextureFormat)0, (RenderTextureReadWrite)2);
			Texture2D val = null;
			RenderTexture active = RenderTexture.active;
			try
			{
				Graphics.Blit((Texture)(object)tex, temporary, scale, offset);
				RenderTexture.active = temporary;
				val = new Texture2D(w, h, (TextureFormat)4, false);
				val.ReadPixels(new Rect(0f, 0f, (float)w, (float)h), 0, 0);
				val.Apply();
				EncodeAndDeliver(val.GetRawTextureData(), w, h, enc, synthAlpha, quality, onEncoded);
				return true;
			}
			catch (Exception ex)
			{
				ManualLogSource? log = Plugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("[NOXMFD] SpriteCapture (sync) failed: " + ex.Message));
				}
				Deliver(onEncoded, null);
				return false;
			}
			finally
			{
				RenderTexture.active = active;
				if ((Object)(object)val != (Object)null)
				{
					Object.Destroy((Object)(object)val);
				}
				RenderTexture.ReleaseTemporary(temporary);
			}
		}

		private static void EncodeAndDeliver(byte[] rgba, int w, int h, Encoding enc, bool synthAlpha, int quality, Action<byte[]?> onEncoded)
		{
			try
			{
				if (synthAlpha)
				{
					SynthesizeAlphaIfOpaque(rgba);
				}
				byte[] bytes = ((enc == Encoding.Jpg) ? ImageConversion.EncodeArrayToJPG((Array)rgba, (GraphicsFormat)4, (uint)w, (uint)h, 0u, quality) : ImageConversion.EncodeArrayToPNG((Array)rgba, (GraphicsFormat)4, (uint)w, (uint)h, 0u));
				Deliver(onEncoded, bytes);
			}
			catch (Exception ex)
			{
				ManualLogSource? log = Plugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("[NOXMFD] SpriteCapture encode failed: " + ex.Message));
				}
				Deliver(onEncoded, null);
			}
		}

		private static void Deliver(Action<byte[]?> onEncoded, byte[]? bytes)
		{
			try
			{
				onEncoded(bytes);
			}
			catch (Exception arg)
			{
				ManualLogSource? log = Plugin.Log;
				if (log != null)
				{
					log.LogWarning((object)$"[NOXMFD] SpriteCapture consumer failed: {arg}");
				}
			}
		}

		private static void SynthesizeAlphaIfOpaque(byte[] px)
		{
			byte b = byte.MaxValue;
			for (int i = 3; i < px.Length; i += 4)
			{
				if (px[i] < b)
				{
					b = px[i];
					if (b == 0)
					{
						return;
					}
				}
			}
			if (b >= 250)
			{
				for (int j = 0; j < px.Length; j += 4)
				{
					int num = px[j];
					int num2 = px[j + 1];
					int num3 = px[j + 2];
					px[j + 3] = (byte)(num * 77 + num2 * 150 + num3 * 29 >> 8);
				}
			}
		}
	}
	internal static class CmReflection
	{
		private static FieldInfo? _stationsField;

		private static MethodInfo? _getFirstMethod;

		private static bool _loggedStationsFailure;

		private static bool _loggedCountermeasureFailure;

		public static IList? GetStations(CountermeasureManager mgr)
		{
			if (_stationsField == null)
			{
				_stationsField = typeof(CountermeasureManager).GetField("countermeasureStations", BindingFlags.Instance | BindingFlags.NonPublic);
			}
			try
			{
				return _stationsField?.GetValue(mgr) as IList;
			}
			catch (Exception arg)
			{
				if (!_loggedStationsFailure)
				{
					_loggedStationsFailure = true;
					ManualLogSource? log = Plugin.Log;
					if (log != null)
					{
						log.LogWarning((object)$"[NOXMFD] countermeasure GetFirstCountermeasure invocation failed; CM integration is unavailable: {arg}");
					}
				}
				return null;
			}
		}

		public static Countermeasure? GetFirstCountermeasure(object station)
		{
			if (_getFirstMethod == null)
			{
				_getFirstMethod = station.GetType().GetMethod("GetFirstCountermeasure", BindingFlags.Instance | BindingFlags.Public);
			}
			try
			{
				object? obj = _getFirstMethod?.Invoke(station, null);
				return (Countermeasure?)((obj is Countermeasure) ? obj : null);
			}
			catch (Exception arg)
			{
				if (!_loggedCountermeasureFailure)
				{
					_loggedCountermeasureFailure = true;
					ManualLogSource? log = Plugin.Log;
					if (log != null)
					{
						log.LogWarning((object)$"[NOXMFD] countermeasure station read failed; CM integration is unavailable: {arg}");
					}
				}
				return null;
			}
		}
	}
	[Serializable]
	internal class CommandEnvelope
	{
		public string? cmd;

		public long id;

		public string? wname;

		public string? group;

		public int index;

		public bool on;

		public string? bind;

		public string? key;

		public string? cid;

		public int n;

		public float hz;

		public float x;

		public float y;

		public string peer;

		public string name;

		public string type;

		public string payload;

		public float wx;

		public float wz;

		public string? text;
	}
	internal static class CommandDispatcher
	{
		private static readonly Dictionary<string, Action<CommandEnvelope>> _handlers = new Dictionary<string, Action<CommandEnvelope>>(StringComparer.Ordinal)
		{
			{ "target.select", TargetSelect },
			{ "target.deselect", TargetDeselect },
			{ "weapon.select", WeaponSelect },
			{ "weapon.cycle", WeaponCycle },
			{ "weapon.deploy-cargo", WeaponDeployCargo },
			{ "cm.deploy", CountermeasureDeploy },
			{ "gear.set", GearSet },
			{ "tgt.set", TgtSet },
			{ "tgt.only", TgtOnly },
			{ "tgt.reset", TgtReset },
			{ "tgt.clear", TgtClear },
			{ "tgt.clear-datalink", TgtClearDatalink },
			{ "tgt.clear-stale", TgtClearStale },
			{ "tgt.sort", TgtSort },
			{ "tgt.laser", TgtLaser },
			{ "tgt.hud", TgtHud },
			{ "hud.set", HudSet },
			{ "hud.mode", HudMode },
			{ "declutter.set", DeclutterSet },
			{ "avn.toggle", AvnToggle },
			{ "avn.set", AvnSet },
			{ "diag.log", DiagLog },
			{
				"rates.set",
				delegate(CommandEnvelope e)
				{
					if (e.group == "tgp")
					{
						RatesConfig.SetTgpHz(e.hz);
					}
					else if (e.group == "contact")
					{
						RatesConfig.SetContactHz(e.hz);
					}
					else if (e.group == "tgpResolution" || e.group == "tgpQuality")
					{
						RatesConfig.SetTgpResolution(e.wname ?? "native");
					}
					else if (e.group == "tgpJpegQuality")
					{
						RatesConfig.SetTgpJpegQuality(e.wname ?? "mid");
					}
					else if (e.group == "tgpSuppressNative")
					{
						RatesConfig.SetTgpSuppressNative(e.wname == "on" || e.on);
					}
					else if (e.group == "mapShowPlayerNames")
					{
						RatesConfig.SetMapShowPlayerNames(e.wname == "on" || e.on);
					}
					else
					{
						RatesConfig.SetFastHz(e.hz);
					}
				}
			},
			{
				"master-arms.set",
				delegate(CommandEnvelope e)
				{
					ImmersionState.MasterArmsOn = e.on;
				}
			},
			{
				"power.set",
				delegate(CommandEnvelope e)
				{
					ImmersionState.PowerOn = e.on;
				}
			},
			{
				"sqd.create",
				delegate(CommandEnvelope e)
				{
					Squad.CreateSquad(e.name ?? string.Empty, e.index);
				}
			},
			{
				"sqd.invite",
				delegate(CommandEnvelope e)
				{
					if (TryPeer(e.peer, out var id))
					{
						Squad.Invite(id, e.name ?? string.Empty);
					}
				}
			},
			{
				"sqd.set-callsign",
				delegate(CommandEnvelope e)
				{
					Squad.SetCallsign(e.name ?? string.Empty, e.index);
				}
			},
			{
				"sqd.accept",
				delegate(CommandEnvelope e)
				{
					if (TryPeer(e.peer, out var id))
					{
						Squad.AcceptInvite(id);
					}
				}
			},
			{
				"sqd.decline",
				delegate(CommandEnvelope e)
				{
					if (TryPeer(e.peer, out var id))
					{
						Squad.DeclineInvite(id);
					}
				}
			},
			{
				"sqd.leave",
				delegate
				{
					Squad.Leave();
				}
			},
			{
				"sqd.relinquish",
				delegate(CommandEnvelope e)
				{
					Squad.RelinquishLeadership(TryPeer(e.peer, out var id) ? new ulong?(id) : ((ulong?)null));
				}
			},
			{
				"sqd.disband",
				delegate
				{
					Squad.Disband();
				}
			},
			{
				"sqd.kick",
				delegate(CommandEnvelope e)
				{
					if (TryPeer(e.peer, out var id))
					{
						Squad.Kick(id);
					}
				}
			},
			{
				"sqd.set-self-callsign",
				delegate(CommandEnvelope e)
				{
					Squad.SetSelfCallsign(e.name ?? string.Empty, e.index, e.n);
				}
			},
			{
				"sqd.clear-self-callsign",
				delegate
				{
					Squad.ClearSelfCallsign();
				}
			},
			{
				"sqd.send",
				delegate(CommandEnvelope e)
				{
					Squad.SendData(e.type, e.payload);
				}
			},
			{
				"td.cell",
				delegate(CommandEnvelope e)
				{
					if (Squad.IsLeader && TryPeer(e.peer, out var id) && Squad.AllMemberIds().Contains(id))
					{
						TdStore.ToggleCell((uint)e.id, id);
					}
				}
			},
			{
				"td.row",
				delegate(CommandEnvelope e)
				{
					if (Squad.IsLeader)
					{
						TdStore.ToggleRow((uint)e.id, Squad.AllMemberIds());
					}
				}
			},
			{
				"td.column",
				delegate(CommandEnvelope e)
				{
					if (Squad.IsLeader && TryPeer(e.peer, out var id) && Squad.AllMemberIds().Contains(id))
					{
						TdStore.ToggleColumn(id, ParseIds(e.text));
					}
				}
			},
			{
				"td.clear",
				delegate
				{
					if (Squad.IsLeader)
					{
						TdStore.ClearOwn();
					}
				}
			},
			{ "td.designate", TdDesignate },
			{
				"td.accept",
				delegate(CommandEnvelope e)
				{
					TdAccept(e.on);
				}
			},
			{
				"td.dismiss",
				delegate
				{
					TdStore.ClearDesignated();
				}
			},
			{
				"tgp.ir.set",
				delegate(CommandEnvelope e)
				{
					TgpManualControl.SetIR(e.on);
				}
			},
			{
				"tgp.view.set",
				delegate(CommandEnvelope e)
				{
					TgpSingleTargetView.SetStv(e.on);
				}
			},
			{
				"tgp.manual-toggle",
				delegate
				{
					TgpManualControl.Toggle();
				}
			},
			{
				"tgp.ir-toggle",
				delegate
				{
					TgpManualControl.ToggleIR();
				}
			},
			{
				"tgp.view-toggle",
				delegate
				{
					TgpSingleTargetView.ToggleStv();
				}
			},
			{
				"tgp.snap-headtracker",
				delegate
				{
					TgpManualControl.SnapToHeadTracker();
				}
			},
			{
				"tgp.fullscreen-toggle",
				delegate
				{
					TgpFullScreen.Toggle();
				}
			},
			{
				"tgp.fullscreen-hud-toggle",
				delegate
				{
					TgpFullScreen.ToggleHud();
				}
			},
			{
				"internal-mfd.poc-toggle",
				delegate
				{
					InternalMfdController.Toggle();
				}
			},
			{
				"units.toggle",
				delegate
				{
					Keybinds.ToggleUnits();
				}
			},
			{
				"hsd.set-view",
				delegate(CommandEnvelope e)
				{
					HsdViewState.Set(e.on, e.index);
				}
			},
			{
				"tgp.mark-steerpoint",
				delegate
				{
					TgpManualControl.MarkSteerPoint();
				}
			},
			{
				"tgp.point-track",
				delegate
				{
					TgpManualControl.TogglePointTrack();
				}
			},
			{
				"tgp.manual-reset",
				delegate
				{
					TgpManualControl.Reset();
				}
			},
			{
				"tgp.slew",
				delegate(CommandEnvelope e)
				{
					TgpManualControl.SlewTo(e.wx, e.wz);
				}
			},
			{
				"tgp.zoom.step",
				delegate(CommandEnvelope e)
				{
					if (TgpManualControl.ManualMode)
					{
						TgpManualControl.StepZoom(e.index);
					}
					else
					{
						TgpLockZoom.StepZoom(e.index);
					}
				}
			},
			{
				"tgp.zoom.set",
				delegate(CommandEnvelope e)
				{
					TgpManualControl.SetZoom(e.index, e.on);
				}
			},
			{
				"combat-mode.set",
				delegate(CommandEnvelope e)
				{
					string text = e.group;
					CombatMode combatMode = ((text == "aa") ? CombatMode.AirToAir : ((text == "ag") ? CombatMode.AirToGround : CombatMode.All));
					Keybinds.SetCombatMode(combatMode);
				}
			},
			{
				"keybind.set-key",
				delegate(CommandEnvelope e)
				{
					Log("set-key", e.bind, Keybinds.SetKeyBind(e.bind ?? string.Empty, e.key ?? string.Empty, e.on));
				}
			},
			{
				"keybind.arm-joy",
				delegate(CommandEnvelope e)
				{
					Log("arm-joy", e.bind, Keybinds.ArmJoyCapture(e.bind ?? string.Empty, e.on));
				}
			},
			{
				"keybind.resolve",
				delegate(CommandEnvelope e)
				{
					Log("resolve", e.bind, Keybinds.ResolveConflict(e.group ?? string.Empty));
				}
			},
			{
				"keybind.cancel-joy",
				delegate
				{
					Keybinds.CancelJoyCapture();
				}
			},
			{
				"keybind.clear-joy",
				delegate(CommandEnvelope e)
				{
					Log("clear-joy", e.bind, Keybinds.ClearJoyBind(e.bind ?? string.Empty));
				}
			},
			{
				"keybind.arm-axis",
				delegate(CommandEnvelope e)
				{
					Log("arm-axis", e.bind, Keybinds.ArmAxisCapture(e.bind ?? string.Empty));
				}
			},
			{
				"keybind.cancel-axis",
				delegate
				{
					Keybinds.CancelAxisCapture();
				}
			},
			{
				"keybind.clear-axis",
				delegate(CommandEnvelope e)
				{
					Log("clear-axis", e.bind, Keybinds.ClearAxisBind(e.bind ?? string.Empty));
				}
			},
			{
				"keybind.set-axis-invert",
				delegate(CommandEnvelope e)
				{
					Log("set-axis-invert", e.bind, Keybinds.SetAxisInvert(e.bind ?? string.Empty, e.on));
				}
			},
			{
				"keybind.set-bg-input",
				delegate(CommandEnvelope e)
				{
					Keybinds.SetBackgroundInput(e.on);
				}
			},
			{
				"keybind.set-radar-on-start",
				delegate(CommandEnvelope e)
				{
					ImmersionConfig.SetRadarOnOnStart(e.on);
				}
			},
			{
				"keybind.set-engine-on-start",
				delegate(CommandEnvelope e)
				{
					ImmersionConfig.SetEngineOnOnStart(e.on);
				}
			},
			{
				"keybind.set-master-arms-on-start",
				delegate(CommandEnvelope e)
				{
					ImmersionConfig.SetMasterArmsOnOnStart(e.on);
				}
			},
			{
				"keybind.set-power-on-start",
				delegate(CommandEnvelope e)
				{
					ImmersionConfig.SetPowerOnOnStart(e.on);
				}
			},
			{
				"keybind.set-hud-filters-on-combat-mode",
				delegate(CommandEnvelope e)
				{
					ImmersionConfig.SetHudFiltersOnCombatMode(e.on);
				}
			},
			{
				"soi.next",
				delegate
				{
					TelemetryServer.SoiCycle(1);
				}
			},
			{
				"soi.prev",
				delegate
				{
					TelemetryServer.SoiCycle(-1);
				}
			},
			{
				"soi.action",
				delegate(CommandEnvelope e)
				{
					TelemetryServer.SoiAction(e.wname ?? string.Empty);
				}
			},
			{ "map.action", MapAction },
			{
				"cursor.select",
				delegate
				{
					TelemetryServer.CursorSelect();
				}
			},
			{ "cursor.set", CursorSet },
			{ "fire.set", FireSet },
			{
				"soi.panes",
				delegate(CommandEnvelope e)
				{
					TelemetryServer.SetPaneCount(e.cid ?? string.Empty, e.n);
				}
			},
			{
				"soi.page",
				delegate(CommandEnvelope e)
				{
					TelemetryServer.ReportSoiPage(e.cid ?? string.Empty, e.n, e.wname ?? string.Empty);
				}
			},
			{
				"soi.include",
				delegate(CommandEnvelope e)
				{
					TelemetryServer.SetSoiIncluded(e.cid ?? string.Empty, e.n, e.on);
				}
			},
			{
				"wpt.create",
				delegate(CommandEnvelope e)
				{
					LogWpt("create", RouteStore.CreateRoute(e.wname ?? string.Empty) != null);
				}
			},
			{
				"wpt.rename",
				delegate(CommandEnvelope e)
				{
					LogWpt("rename", RouteStore.RenameRoute(e.bind ?? string.Empty, e.wname ?? string.Empty));
				}
			},
			{
				"wpt.delete",
				delegate(CommandEnvelope e)
				{
					LogWpt("delete", RouteStore.DeleteRoute(e.bind ?? string.Empty));
				}
			},
			{
				"wpt.set-active",
				delegate(CommandEnvelope e)
				{
					RouteStore.SetActiveRoute(e.bind ?? string.Empty);
				}
			},
			{
				"wpt.clear",
				delegate
				{
					RouteStore.ClearRoutes();
				}
			},
			{
				"wpt.reset-route",
				delegate(CommandEnvelope e)
				{
					LogWpt("reset-route", RouteStore.ResetRoute(e.bind ?? string.Empty));
				}
			},
			{
				"wpt.import",
				delegate(CommandEnvelope e)
				{
					LogWpt("import", RouteStore.ImportRoute(e.text ?? string.Empty));
				}
			},
			{
				"wpt.rename-waypoint",
				delegate(CommandEnvelope e)
				{
					LogWpt("rename-waypoint", RouteStore.RenameWaypoint(e.index, e.wname ?? string.Empty, e.bind));
				}
			},
			{
				"wpt.reorder-waypoint",
				delegate(CommandEnvelope e)
				{
					LogWpt("reorder-waypoint", RouteStore.ReorderWaypoint(e.index, e.n, e.bind));
				}
			},
			{
				"wpt.reset-waypoint",
				delegate(CommandEnvelope e)
				{
					LogWpt("reset-waypoint", RouteStore.ResetWaypoint(e.index));
				}
			},
			{
				"wpt.remove-waypoint",
				delegate(CommandEnvelope e)
				{
					LogWpt("remove-waypoint", RouteStore.RemoveWaypoint(e.index, e.bind));
				}
			},
			{
				"wpt.cycle-route",
				delegate(CommandEnvelope e)
				{
					RouteStore.CycleActiveRoute(e.index);
				}
			},
			{
				"wpt.step-waypoint",
				delegate(CommandEnvelope e)
				{
					RouteStore.StepWaypoint(e.index);
				}
			},
			{
				"wpt.step-navigation",
				delegate(CommandEnvelope e)
				{
					RouteStore.StepNavigation(e.index);
				}
			},
			{
				"wpt.add-waypoint",
				delegate(CommandEnvelope e)
				{
					RouteStore.AddWaypoint(e.wx, e.wz, e.wname ?? string.Empty);
				}
			},
			{
				"wpt.add-navigation-point",
				delegate(CommandEnvelope e)
				{
					RouteStore.AddNavigationPoint(e.wx, e.wz, e.wname ?? string.Empty);
				}
			},
			{
				"wpt.add-steerpoint",
				delegate(CommandEnvelope e)
				{
					RouteStore.AddSteerPoint(e.wx, e.wz, e.wname ?? string.Empty);
				}
			},
			{
				"wpt.rename-steerpoint",
				delegate(CommandEnvelope e)
				{
					LogWpt("rename-steerpoint", RouteStore.RenameSteerPoint(e.bind ?? string.Empty, e.wname ?? string.Empty));
				}
			},
			{
				"wpt.delete-steerpoint",
				delegate(CommandEnvelope e)
				{
					LogWpt("delete-steerpoint", RouteStore.DeleteSteerPoint(e.bind ?? string.Empty));
				}
			},
			{
				"wpt.set-active-steerpoint",
				delegate(CommandEnvelope e)
				{
					RouteStore.SetActiveSteerPoint(e.bind ?? string.Empty);
				}
			},
			{
				"wpt.cycle-steerpoint",
				delegate(CommandEnvelope e)
				{
					RouteStore.CycleSteerPoint(e.index);
				}
			},
			{
				"wpt.import-steerpoints",
				delegate(CommandEnvelope e)
				{
					LogWpt("import-steerpoints", RouteStore.ImportSteerPoints(e.text ?? string.Empty));
				}
			},
			{
				"wpt.accept-shared",
				delegate(CommandEnvelope e)
				{
					LogWpt("accept-shared", RouteStore.AcceptShared(e.bind));
				}
			},
			{
				"wpt.reject-shared",
				delegate(CommandEnvelope e)
				{
					LogWpt("reject-shared", RouteStore.RejectShared(e.bind));
				}
			},
			{
				"wpt.share",
				delegate(CommandEnvelope e)
				{
					LogWpt("share", RouteStore.ShareRoute(e.bind));
				}
			},
			{
				"wpt.accept-shared-steerpoint",
				delegate(CommandEnvelope e)
				{
					LogWpt("accept-shared-steerpoint", RouteStore.AcceptSharedSteerPoint(e.bind ?? string.Empty));
				}
			},
			{
				"wpt.reject-shared-steerpoint",
				delegate(CommandEnvelope e)
				{
					LogWpt("reject-shared-steerpoint", RouteStore.RejectSharedSteerPoint(e.bind ?? string.Empty));
				}
			},
			{
				"wpt.share-steerpoint",
				delegate(CommandEnvelope e)
				{
					LogWpt("share-steerpoint", RouteStore.ShareSteerPoint(e.bind ?? string.Empty));
				}
			},
			{
				"layout.save",
				delegate(CommandEnvelope e)
				{
					LogLayout("save", LayoutStore.SaveLayout(e.wname ?? string.Empty, e.group ?? string.Empty, e.text ?? string.Empty));
				}
			},
			{
				"layout.update",
				delegate(CommandEnvelope e)
				{
					LogLayout("update", LayoutStore.UpdateLayout(e.bind ?? string.Empty, e.wname ?? string.Empty, e.text ?? string.Empty));
				}
			},
			{
				"layout.rename",
				delegate(CommandEnvelope e)
				{
					LogLayout("rename", LayoutStore.RenameLayout(e.bind ?? string.Empty, e.wname ?? string.Empty));
				}
			},
			{
				"layout.delete",
				delegate(CommandEnvelope e)
				{
					LogLayout("delete", LayoutStore.DeleteLayout(e.bind ?? string.Empty));
				}
			},
			{
				"theme.create",
				delegate(CommandEnvelope e)
				{
					LogTheme("create", ThemeStore.Create(e.wname, e.bind));
				}
			},
			{
				"theme.rename",
				delegate(CommandEnvelope e)
				{
					LogTheme("rename", ThemeStore.Rename(e.bind, e.wname));
				}
			},
			{
				"theme.delete",
				delegate(CommandEnvelope e)
				{
					LogTheme("delete", ThemeStore.Delete(e.bind));
				}
			},
			{
				"theme.select",
				delegate(CommandEnvelope e)
				{
					LogTheme("select", ThemeStore.Select(e.bind));
				}
			},
			{
				"theme.set-color",
				delegate(CommandEnvelope e)
				{
					LogTheme("set-color", ThemeStore.SetColor(e.group, e.text));
				}
			},
			{
				"theme.reset-color",
				delegate(CommandEnvelope e)
				{
					LogTheme("reset-color", ThemeStore.ResetColor(e.group));
				}
			},
			{
				"theme.import",
				delegate(CommandEnvelope e)
				{
					LogTheme("import", ThemeStore.Import(e.text));
				}
			},
			{
				"theme.rescan",
				delegate
				{
					LogTheme("rescan", ThemeStore.Rescan());
				}
			},
			{
				"preset.save",
				delegate(CommandEnvelope e)
				{
					LogPreset("save", HudPresetStore.Save(e.wname ?? string.Empty, e.index));
				}
			},
			{
				"preset.rename",
				delegate(CommandEnvelope e)
				{
					LogPreset("rename", HudPresetStore.Rename(e.index, e.wname ?? string.Empty));
				}
			},
			{
				"preset.delete",
				delegate(CommandEnvelope e)
				{
					LogPreset("delete", HudPresetStore.Delete(e.index));
				}
			},
			{
				"preset.load",
				delegate(CommandEnvelope e)
				{
					LogPreset("load", HudPresetStore.LoadPreset(e.index));
				}
			},
			{
				"tgt-preset.save",
				delegate(CommandEnvelope e)
				{
					LogTgtPreset("save", TgtPresetStore.Save(e.wname ?? string.Empty, e.index));
				}
			},
			{
				"tgt-preset.rename",
				delegate(CommandEnvelope e)
				{
					LogTgtPreset("rename", TgtPresetStore.Rename(e.index, e.wname ?? string.Empty));
				}
			},
			{
				"tgt-preset.delete",
				delegate(CommandEnvelope e)
				{
					LogTgtPreset("delete", TgtPresetStore.Delete(e.index));
				}
			},
			{
				"tgt-preset.load",
				delegate(CommandEnvelope e)
				{
					LogTgtPreset("load", TgtPresetStore.LoadPreset(e.index));
				}
			}
		};

		private static void Log(string op, string? bind, bool ok)
		{
			if (!ok)
			{
				ManualLogSource? log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)("[NOXMFD] keybind." + op + " '" + bind + "': rejected."));
				}
			}
		}

		private static bool TryPeer(string s, out ulong id)
		{
			id = 0uL;
			if (string.IsNullOrWhiteSpace(s))
			{
				return false;
			}
			if (!ulong.TryParse(s.Trim(), NumberStyles.None, CultureInfo.InvariantCulture, out id) || id == 0L)
			{
				ManualLogSource? log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)("[NOXMFD] squadron: rejected peer id '" + s + "'."));
				}
				return false;
			}
			return true;
		}

		private static void LogWpt(string op, bool ok)
		{
			if (!ok)
			{
				ManualLogSource? log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)("[NOXMFD] wpt." + op + ": rejected."));
				}
			}
		}

		private static void LogLayout(string op, bool ok)
		{
			if (!ok)
			{
				ManualLogSource? log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)("[NOXMFD] layout." + op + ": rejected."));
				}
			}
		}

		private static void LogTheme(string op, bool ok)
		{
			if (!ok)
			{
				ManualLogSource? log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)("[NOXMFD] theme." + op + ": rejected."));
				}
			}
		}

		private static void LogPreset(string op, bool ok)
		{
			if (!ok)
			{
				ManualLogSource? log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)("[NOXMFD] preset." + op + ": rejected."));
				}
			}
		}

		private static void LogTgtPreset(string op, bool ok)
		{
			if (!ok)
			{
				ManualLogSource? log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)("[NOXMFD] tgt-preset." + op + ": rejected."));
				}
			}
		}

		public static bool IsKnown(string cmd)
		{
			if (cmd != null)
			{
				return _handlers.ContainsKey(cmd);
			}
			return false;
		}

		public static void Drain()
		{
			CommandEnvelope env;
			while (CommandEndpoint.TryDequeueCommand(out env))
			{
				if (env == null)
				{
					continue;
				}
				if (_handlers.TryGetValue(env.cmd ?? string.Empty, out Action<CommandEnvelope> value))
				{
					try
					{
						value(env);
					}
					catch (Exception arg)
					{
						ManualLogSource? log = Plugin.Log;
						if (log != null)
						{
							log.LogWarning((object)$"[NOXMFD] command '{env.cmd}' (group '{env.group}') threw: {arg}");
						}
					}
				}
				else
				{
					ManualLogSource? log2 = Plugin.Log;
					if (log2 != null)
					{
						log2.LogInfo((object)("[NOXMFD] unknown command '" + env.cmd + "' — dropped."));
					}
				}
			}
		}

		private static void TargetSelect(CommandEnvelope env)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			uint num = (uint)env.id;
			if (num == 0)
			{
				ManualLogSource? log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)"[NOXMFD] target.select: id=0 (missing/unparsed) — ignored.");
				}
				return;
			}
			Unit val = default(Unit);
			if (!UnitRegistry.TryGetUnit((PersistentID?)new PersistentID
			{
				Id = num
			}, ref val) || (Object)(object)val == (Object)null || val.disabled)
			{
				ManualLogSource? log2 = Plugin.Log;
				if (log2 != null)
				{
					log2.LogInfo((object)$"[NOXMFD] target.select id={num}: no live unit (stale) — ignored.");
				}
				return;
			}
			Aircraft val2 = default(Aircraft);
			GameManager.GetLocalAircraft(ref val2);
			if ((Object)(object)val2 == (Object)null || (Object)(object)((Unit)val2).NetworkHQ == (Object)null)
			{
				ManualLogSource? log3 = Plugin.Log;
				if (log3 != null)
				{
					log3.LogInfo((object)$"[NOXMFD] target.select id={num}: no local aircraft/HQ — ignored.");
				}
			}
			else if (!IsDetectedByPlayer(val2, val))
			{
				ManualLogSource? log4 = Plugin.Log;
				if (log4 != null)
				{
					log4.LogInfo((object)$"[NOXMFD] target.select id={num}: not visible to player — ignored.");
				}
			}
			else
			{
				TrySelectTarget(val, "target.select");
			}
		}

		internal static bool IsDetectedByPlayer(Aircraft ac, Unit unit)
		{
			GlobalPosition val = default(GlobalPosition);
			bool factionKnown = ((Unit)ac).NetworkHQ.TryGetKnownPosition(unit, ref val);
			TargetDetector radar = ((Unit)ac).radar;
			bool ownRadarDetected = ((radar is Radar) ? radar : null)?.detectedTargets.Contains(unit) ?? false;
			CombatHUD i = SceneSingleton<CombatHUD>.i;
			bool pictureJamActive = (Object)(object)i != (Object)null && i.jamAccumulation > 0f;
			return TargetSelectionPolicy.IsSelectable(factionKnown, ownRadarDetected, pictureJamActive);
		}

		internal static bool TrySelectTarget(Unit unit, string source)
		{
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)unit == (Object)null || unit.disabled)
			{
				return false;
			}
			Aircraft val = default(Aircraft);
			GameManager.GetLocalAircraft(ref val);
			if ((Object)(object)val == (Object)null || (Object)(object)val.weaponManager == (Object)null)
			{
				return false;
			}
			if ((object)unit == val)
			{
				return false;
			}
			string text = unit.definition?.unitName ?? "?";
			if ((int)DynamicMap.GetFactionMode(unit.NetworkHQ, false) == 0)
			{
				ManualLogSource? log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)$"[NOXMFD] {source} '{text}' (id={unit.persistentID.Id}): no-faction unit — ignored.");
				}
				return false;
			}
			TargetListSelector i = SceneSingleton<TargetListSelector>.i;
			if ((Object)(object)i != (Object)null && i.CheckExclusions(unit))
			{
				ManualLogSource? log2 = Plugin.Log;
				if (log2 != null)
				{
					log2.LogInfo((object)$"[NOXMFD] {source} '{text}' (id={unit.persistentID.Id}): excluded by TGT filters — ignored.");
				}
				return false;
			}
			WeaponManager weaponManager = val.weaponManager;
			if (weaponManager.CheckIsTarget(unit))
			{
				ManualLogSource? log3 = Plugin.Log;
				if (log3 != null)
				{
					log3.LogInfo((object)$"[NOXMFD] {source} '{text}' (id={unit.persistentID.Id}): already targeted — no-op.");
				}
				return false;
			}
			CombatHUD i2 = SceneSingleton<CombatHUD>.i;
			bool flag = (Object)(object)i2 != (Object)null && i2.aircraft == val && i2.MarkerExists(unit);
			if ((Object)(object)i2 != (Object)null && flag)
			{
				i2.SelectUnit(unit);
			}
			else
			{
				weaponManager.AddTargetList(unit);
			}
			ManualLogSource? log4 = Plugin.Log;
			if (log4 != null)
			{
				log4.LogInfo((object)$"[NOXMFD] {source} → '{text}' (id={unit.persistentID.Id}, viaHud={flag}).");
			}
			return true;
		}

		private static void TargetDeselect(CommandEnvelope env)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			uint num = (uint)env.id;
			if (num == 0)
			{
				ManualLogSource? log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)"[NOXMFD] target.deselect: id=0 (missing/unparsed) — ignored.");
				}
				return;
			}
			Unit val = default(Unit);
			if (!UnitRegistry.TryGetUnit((PersistentID?)new PersistentID
			{
				Id = num
			}, ref val) || (Object)(object)val == (Object)null)
			{
				ManualLogSource? log2 = Plugin.Log;
				if (log2 != null)
				{
					log2.LogInfo((object)$"[NOXMFD] target.deselect id={num}: no such unit — ignored.");
				}
				return;
			}
			Aircraft val2 = default(Aircraft);
			GameManager.GetLocalAircraft(ref val2);
			if ((Object)(object)val2 == (Object)null || (Object)(object)val2.weaponManager == (Object)null)
			{
				return;
			}
			WeaponManager weaponManager = val2.weaponManager;
			string arg = val.definition?.unitName ?? "?";
			if (!weaponManager.CheckIsTarget(val))
			{
				ManualLogSource? log3 = Plugin.Log;
				if (log3 != null)
				{
					log3.LogInfo((object)$"[NOXMFD] target.deselect '{arg}' (id={num}): not targeted — no-op.");
				}
				return;
			}
			if (TargetFocus.Id == num)
			{
				TargetFocus.Cycle(1, TelemetryReader.TargetIds(weaponManager.GetTargetList()));
			}
			CombatHUD i = SceneSingleton<CombatHUD>.i;
			bool flag = (Object)(object)i != (Object)null && i.aircraft == val2 && i.MarkerExists(val);
			if ((Object)(object)i != (Object)null && flag)
			{
				i.DeSelectUnit(val);
			}
			else
			{
				weaponManager.RemoveTargetList(val);
			}
			ManualLogSource? log4 = Plugin.Log;
			if (log4 != null)
			{
				log4.LogInfo((object)$"[NOXMFD] target.deselect ← '{arg}' (id={num}, viaHud={flag}).");
			}
		}

		private static void WeaponSelect(CommandEnvelope env)
		{
			string text = env.wname ?? string.Empty;
			if (string.IsNullOrEmpty(text))
			{
				ManualLogSource? log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)"[NOXMFD] weapon.select: empty name — ignored.");
				}
				return;
			}
			Aircraft val = default(Aircraft);
			GameManager.GetLocalAircraft(ref val);
			if ((Object)(object)val == (Object)null || (Object)(object)val.weaponManager == (Object)null || ((Unit)val).weaponStations == null)
			{
				return;
			}
			WeaponManager weaponManager = val.weaponManager;
			WeaponStation val2 = WeaponSelectors.FindStationByName(val, text);
			if (val2 == null)
			{
				ManualLogSource? log2 = Plugin.Log;
				if (log2 != null)
				{
					log2.LogInfo((object)("[NOXMFD] weapon.select '" + text + "': no matching station — ignored."));
				}
				return;
			}
			if (weaponManager.currentWeaponStation == val2)
			{
				ManualLogSource? log3 = Plugin.Log;
				if (log3 != null)
				{
					log3.LogInfo((object)("[NOXMFD] weapon.select '" + text + "': already selected — no-op."));
				}
				return;
			}
			WeaponSelectors.SelectStation(val, val2);
			ManualLogSource? log4 = Plugin.Log;
			if (log4 != null)
			{
				log4.LogInfo((object)$"[NOXMFD] weapon.select → '{text}' (station {val2.Number}).");
			}
		}

		private static bool LocalAircraft(string op, out Aircraft ac)
		{
			GameManager.GetLocalAircraft(ref ac);
			if ((Object)(object)ac == (Object)null || ((Unit)ac).disabled)
			{
				ManualLogSource? log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)("[NOXMFD] " + op + ": no local aircraft — ignored."));
				}
				return false;
			}
			return true;
		}

		private static void WeaponCycle(CommandEnvelope env)
		{
			if (!LocalAircraft("weapon.cycle", out Aircraft ac))
			{
				return;
			}
			switch (env.group)
			{
			case "guns":
				WeaponSelectors.CycleGun(ac);
				return;
			case "missiles":
				WeaponSelectors.CycleMissile(ac);
				return;
			case "bombs":
				WeaponSelectors.CycleBomb(ac);
				return;
			case "cargo":
				WeaponSelectors.CycleCargo(ac);
				return;
			}
			ManualLogSource? log = Plugin.Log;
			if (log != null)
			{
				log.LogInfo((object)("[NOXMFD] weapon.cycle: unknown group '" + env.group + "' — ignored."));
			}
		}

		private static void WeaponDeployCargo(CommandEnvelope env)
		{
			if (LocalAircraft("weapon.deploy-cargo", out Aircraft ac))
			{
				WeaponSelectors.DeployCargo(ac);
			}
		}

		private static void CountermeasureDeploy(CommandEnvelope env)
		{
			if (!LocalAircraft("cm.deploy", out Aircraft ac))
			{
				return;
			}
			string text = env.group;
			if (!(text == "flares"))
			{
				if (text == "jammer")
				{
					Keybinds.DriveCountermeasure(ac, 2);
					return;
				}
				ManualLogSource? log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)("[NOXMFD] cm.deploy: unknown group '" + env.group + "' — ignored."));
				}
			}
			else
			{
				Keybinds.DriveCountermeasure(ac, 1);
			}
		}

		private static void GearSet(CommandEnvelope env)
		{
			if (!LocalAircraft("gear.set", out Aircraft ac))
			{
				return;
			}
			string text = env.group;
			if (!(text == "up"))
			{
				if (text == "down")
				{
					Keybinds.DriveGear(ac, up: false, down: true);
					return;
				}
				ManualLogSource? log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)("[NOXMFD] gear.set: unknown group '" + env.group + "' — ignored."));
				}
			}
			else
			{
				Keybinds.DriveGear(ac, up: true, down: false);
			}
		}

		private static void CursorSet(CommandEnvelope env)
		{
			TelemetryServer.SetRemoteCursorState(ClampUnit(env.x), ClampUnit(env.y), env.on);
		}

		private static void FireSet(CommandEnvelope env)
		{
			TelemetryServer.SetRemoteFireState(env.group ?? string.Empty, env.on);
		}

		private static void MapAction(CommandEnvelope env)
		{
			string text = env.wname ?? string.Empty;
			TelemetryServer.MapAction(text);
			switch (text)
			{
			case "tgt-next":
				Keybinds.CycleTargetFocus(1);
				break;
			case "tgt-prev":
				Keybinds.CycleTargetFocus(-1);
				break;
			case "tgt-datalink":
				ClearDatalinkTargets();
				break;
			case "tgt-stale":
				ClearStaleTargets();
				break;
			}
		}

		private static float ClampUnit(float value)
		{
			if (value < -1f)
			{
				return -1f;
			}
			if (value > 1f)
			{
				return 1f;
			}
			return value;
		}

		private static List<TargetListSelector_ToggleButton>? TgtGroup(TargetListSelector sel, string? group)
		{
			return group switch
			{
				"faction" => sel.toggleFactionItems, 
				"category" => sel.toggleUnitTypesItems, 
				"vehicle" => sel.toggleVehicleTypesItems, 
				_ => null, 
			};
		}

		private static TargetListSelector_ToggleButton? TgtResolve(CommandEnvelope env, string op, out TargetListSelector sel)
		{
			sel = null;
			TargetListSelector i = SceneSingleton<TargetListSelector>.i;
			if ((Object)(object)i == (Object)null)
			{
				ManualLogSource? log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)("[NOXMFD] " + op + ": TargetListSelector absent — ignored."));
				}
				return null;
			}
			sel = i;
			List<TargetListSelector_ToggleButton> list = TgtGroup(sel, env.group);
			if (list == null)
			{
				ManualLogSource? log2 = Plugin.Log;
				if (log2 != null)
				{
					log2.LogInfo((object)("[NOXMFD] " + op + ": unknown group '" + env.group + "' — ignored."));
				}
				return null;
			}
			if (env.index < 0 || env.index >= list.Count)
			{
				ManualLogSource? log3 = Plugin.Log;
				if (log3 != null)
				{
					log3.LogInfo((object)$"[NOXMFD] {op}: index {env.index} out of range for '{env.group}' [{list.Count}] — ignored.");
				}
				return null;
			}
			TargetListSelector_ToggleButton val = list[env.index];
			if ((Object)(object)val == (Object)null)
			{
				ManualLogSource? log4 = Plugin.Log;
				if (log4 != null)
				{
					log4.LogInfo((object)$"[NOXMFD] {op}: null toggle at {env.group}[{env.index}] — ignored.");
				}
				return null;
			}
			return val;
		}

		private static void TgtSet(CommandEnvelope env)
		{
			TargetListSelector sel;
			TargetListSelector_ToggleButton val = TgtResolve(env, "tgt.set", out sel);
			if (!((Object)(object)val == (Object)null) && val.status != env.on)
			{
				val.Set(env.on);
				ManualLogSource? log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)$"[NOXMFD] tgt.set {env.group}[{env.index}] = {env.on}.");
				}
			}
		}

		private static void TgtOnly(CommandEnvelope env)
		{
			TargetListSelector sel;
			TargetListSelector_ToggleButton val = TgtResolve(env, "tgt.only", out sel);
			if (!((Object)(object)val == (Object)null))
			{
				sel.SetOnlyItem(val);
				ManualLogSource? log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)$"[NOXMFD] tgt.only {env.group}[{env.index}].");
				}
			}
		}

		private static void TgtReset(CommandEnvelope env)
		{
			TargetListSelector i = SceneSingleton<TargetListSelector>.i;
			if ((Object)(object)i == (Object)null)
			{
				ManualLogSource? log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)"[NOXMFD] tgt.reset: TargetListSelector absent — ignored.");
				}
				return;
			}
			i.ResetFilters();
			ManualLogSource? log2 = Plugin.Log;
			if (log2 != null)
			{
				log2.LogInfo((object)"[NOXMFD] tgt.reset — all filters on.");
			}
		}

		private static void TgtClear(CommandEnvelope env)
		{
			TargetListSelector i = SceneSingleton<TargetListSelector>.i;
			if ((Object)(object)i == (Object)null)
			{
				ManualLogSource? log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)"[NOXMFD] tgt.clear: TargetListSelector absent — ignored.");
				}
				return;
			}
			i.DeselectAll();
			ManualLogSource? log2 = Plugin.Log;
			if (log2 != null)
			{
				log2.LogInfo((object)"[NOXMFD] tgt.clear — deselected all targets.");
			}
		}

		private static bool IsDatalinkOnly(FactionHQ playerHQ, Unit unit)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)unit == (Object)null || (Object)(object)unit.NetworkHQ == (Object)(object)playerHQ)
			{
				return false;
			}
			TrackingInfo trackingData = playerHQ.GetTrackingData(unit.persistentID);
			return trackingData == null || !trackingData.Observed();
		}

		private static bool IsStale(FactionHQ playerHQ, Unit unit)
		{
			if ((Object)(object)unit == (Object)null || (Object)(object)unit.NetworkHQ == (Object)(object)playerHQ)
			{
				return false;
			}
			return !playerHQ.IsTargetPositionAccurate(unit, 20f);
		}

		private static void TgtClearBy(string op, Func<FactionHQ, Unit, bool> predicate)
		{
			TargetListSelector i = SceneSingleton<TargetListSelector>.i;
			if ((Object)(object)i == (Object)null)
			{
				ManualLogSource? log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)("[NOXMFD] " + op + ": TargetListSelector absent — ignored."));
				}
				return;
			}
			Aircraft val = default(Aircraft);
			GameManager.GetLocalAircraft(ref val);
			if ((Object)(object)val == (Object)null || (Object)(object)val.weaponManager == (Object)null || (Object)(object)((Unit)val).NetworkHQ == (Object)null)
			{
				return;
			}
			FactionHQ networkHQ = ((Unit)val).NetworkHQ;
			List<Unit> targetList = val.weaponManager.GetTargetList();
			if (targetList == null || targetList.Count == 0)
			{
				return;
			}
			int num = 0;
			foreach (Unit item in new List<Unit>(targetList))
			{
				if (predicate(networkHQ, item))
				{
					i.ForceDeselect(item);
					num++;
				}
			}
			ManualLogSource? log2 = Plugin.Log;
			if (log2 != null)
			{
				log2.LogInfo((object)$"[NOXMFD] {op} — deselected {num} target(s).");
			}
		}

		private static void TgtClearDatalink(CommandEnvelope env)
		{
			ClearDatalinkTargets();
		}

		private static void TgtClearStale(CommandEnvelope env)
		{
			ClearStaleTargets();
		}

		private static void TgtSort(CommandEnvelope env)
		{
			if (!TargetSort.Set(env.key ?? "", env.index))
			{
				ManualLogSource? log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)("[NOXMFD] tgt.sort: unknown key '" + env.key + "' — ignored."));
				}
			}
		}

		internal static void ClearDatalinkTargets()
		{
			TgtClearBy("tgt.clear-datalink", IsDatalinkOnly);
		}

		internal static void ClearStaleTargets()
		{
			TgtClearBy("tgt.clear-stale", IsStale);
		}

		private static void TdAccept(bool replace)
		{
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			if (TdStore.Designated.Count == 0)
			{
				ManualLogSource? log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)"[NOXMFD] td.accept: nothing pending — ignored.");
				}
				return;
			}
			if (replace)
			{
				TargetListSelector i = SceneSingleton<TargetListSelector>.i;
				if ((Object)(object)i == (Object)null)
				{
					ManualLogSource? log2 = Plugin.Log;
					if (log2 != null)
					{
						log2.LogInfo((object)"[NOXMFD] td.accept: TargetListSelector absent — ignored.");
					}
					return;
				}
				i.DeselectAll();
			}
			int num = 0;
			Unit val = default(Unit);
			foreach (TdStore.Row item in TdStore.Designated)
			{
				if (UnitRegistry.TryGetUnit((PersistentID?)new PersistentID
				{
					Id = item.Id
				}, ref val) && (Object)(object)val != (Object)null && !val.disabled && TrySelectTarget(val, "td.accept"))
				{
					num++;
				}
			}
			TdStore.AcceptDesignated();
			ManualLogSource? log3 = Plugin.Log;
			if (log3 != null)
			{
				log3.LogInfo((object)string.Format("[NOXMFD] td.accept ({0}) — selected {1} target(s).", replace ? "replace" : "add", num));
			}
		}

		private static List<uint> ParseIds(string? json)
		{
			List<uint> list = new List<uint>();
			if (JsonLite.Parse(json ?? "[]") is List<object> list2)
			{
				foreach (object item in list2)
				{
					if (item is double num && num > 0.0)
					{
						list.Add((uint)num);
					}
				}
			}
			return list;
		}

		private static void TdDesignate(CommandEnvelope e)
		{
			if (!Squad.IsLeader)
			{
				ManualLogSource? log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)"[NOXMFD] td.designate: not the squad leader — ignored.");
				}
				return;
			}
			if (!TryPeer(e.peer, out var id))
			{
				ManualLogSource? log2 = Plugin.Log;
				if (log2 != null)
				{
					log2.LogInfo((object)"[NOXMFD] td.designate: missing/unparsed peer — ignored.");
				}
				return;
			}
			List<uint> list = new List<uint>();
			if (JsonLite.Parse(e.text ?? "[]") is List<object> list2)
			{
				foreach (object item in list2)
				{
					if (item is Dictionary<string, object> dictionary && dictionary.TryGetValue("id", out var value) && value is double num)
					{
						list.Add((uint)num);
					}
				}
			}
			bool flag = Squad.SendDataTo(id, "td.designate", e.text ?? "[]");
			if (flag)
			{
				TdStore.MarkSent(id, list);
			}
			ManualLogSource? log3 = Plugin.Log;
			if (log3 != null)
			{
				log3.LogInfo((object)(flag ? $"[NOXMFD] td.designate → {id}: sent {list.Count} target(s)." : $"[NOXMFD] td.designate → {id}: not sent — not a current squad member, or the Steam send itself failed."));
			}
		}

		private static void TgtLaser(CommandEnvelope env)
		{
			TargetListSelector i = SceneSingleton<TargetListSelector>.i;
			if ((Object)(object)i == (Object)null || (Object)(object)i.toggleLaser == (Object)null)
			{
				ManualLogSource? log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)"[NOXMFD] tgt.laser: unavailable — ignored.");
				}
			}
			else if (i.toggleLaser.status != env.on)
			{
				i.toggleLaser.Set(env.on);
				ManualLogSource? log2 = Plugin.Log;
				if (log2 != null)
				{
					log2.LogInfo((object)$"[NOXMFD] tgt.laser = {env.on}.");
				}
			}
		}

		private static void TgtHud(CommandEnvelope env)
		{
			TargetListSelector i = SceneSingleton<TargetListSelector>.i;
			if ((Object)(object)i == (Object)null || (Object)(object)i.toggleFollowHUD == (Object)null)
			{
				ManualLogSource? log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)"[NOXMFD] tgt.hud: unavailable — ignored.");
				}
			}
			else if (i.toggleFollowHUD.status != env.on)
			{
				i.toggleFollowHUD.Set(env.on);
				ManualLogSource? log2 = Plugin.Log;
				if (log2 != null)
				{
					log2.LogInfo((object)$"[NOXMFD] tgt.hud = {env.on}.");
				}
			}
		}

		private static void HudSet(CommandEnvelope env)
		{
			HUDOptions i = SceneSingleton<HUDOptions>.i;
			if ((Object)(object)i == (Object)null)
			{
				ManualLogSource? log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)"[NOXMFD] hud.set: HUDOptions unavailable — ignored.");
				}
				return;
			}
			switch (env.group)
			{
			case "category":
			{
				if (!InList(i.listCategories, env.index, "hud.set category"))
				{
					return;
				}
				HUDOptions_Category val3 = i.listCategories[env.index];
				if ((Object)(object)val3 == (Object)null || val3.maximized == env.on)
				{
					return;
				}
				val3.Set(env.on);
				break;
			}
			case "vehicle":
			{
				if (!InList(i.listVehicleTypes, env.index, "hud.set vehicle"))
				{
					return;
				}
				HUDOptions_ToggleButton val = i.listVehicleTypes[env.index];
				if ((Object)(object)val == (Object)null || val.status == env.on)
				{
					return;
				}
				val.Set(env.on);
				break;
			}
			case "building":
			{
				if (!InList(i.listBuildingTypes, env.index, "hud.set building"))
				{
					return;
				}
				HUDOptions_ToggleButton val2 = i.listBuildingTypes[env.index];
				if ((Object)(object)val2 == (Object)null || val2.status == env.on)
				{
					return;
				}
				val2.Set(env.on);
				break;
			}
			default:
			{
				ManualLogSource? log2 = Plugin.Log;
				if (log2 != null)
				{
					log2.LogInfo((object)("[NOXMFD] hud.set: unknown group '" + env.group + "' — ignored."));
				}
				return;
			}
			}
			i.ApplyHUDSettings();
			HudCombatModeFilters.CaptureIfIdle();
			ManualLogSource? log3 = Plugin.Log;
			if (log3 != null)
			{
				log3.LogInfo((object)$"[NOXMFD] hud.set {env.group}[{env.index}] = {env.on}.");
			}
		}

		private static void HudMode(CommandEnvelope env)
		{
			HUDOptions i = SceneSingleton<HUDOptions>.i;
			if ((Object)(object)i == (Object)null)
			{
				ManualLogSource? log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)"[NOXMFD] hud.mode: HUDOptions unavailable — ignored.");
				}
			}
			else
			{
				if (!InList(i.listModes, env.index, "hud.mode"))
				{
					return;
				}
				HUDOptions_ToggleButton val = i.listModes[env.index];
				if ((Object)(object)val == (Object)null)
				{
					ManualLogSource? log2 = Plugin.Log;
					if (log2 != null)
					{
						log2.LogInfo((object)$"[NOXMFD] hud.mode[{env.index}] is null — ignored.");
					}
					return;
				}
				i.ToggleButtons(val);
				i.ApplyHUDSettings();
				HudCombatModeFilters.CaptureIfIdle();
				ManualLogSource? log3 = Plugin.Log;
				if (log3 != null)
				{
					log3.LogInfo((object)$"[NOXMFD] hud.mode = {env.index}.");
				}
			}
		}

		private static void DeclutterSet(CommandEnvelope env)
		{
			switch (env.group)
			{
			case "weapon":
				HudDeclutterConfig.SetHideWeaponAmmo(env.on);
				break;
			case "minimap":
				HudDeclutterConfig.SetHideMinimap(env.on);
				break;
			case "boxes":
				HudDeclutterConfig.SetHideTopBoxes(env.on);
				break;
			case "feed":
				HudDeclutterConfig.SetHideKillFeed(env.on);
				break;
			default:
			{
				ManualLogSource? log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)("[NOXMFD] declutter.set: unknown group '" + env.group + "' — ignored."));
				}
				return;
			}
			}
			ManualLogSource? log2 = Plugin.Log;
			if (log2 != null)
			{
				log2.LogInfo((object)$"[NOXMFD] declutter.set {env.group} hide={env.on}.");
			}
		}

		private static void AvnToggle(CommandEnvelope env)
		{
			Aircraft val = default(Aircraft);
			GameManager.GetLocalAircraft(ref val);
			if ((Object)(object)val == (Object)null || ((Unit)val).disabled)
			{
				ManualLogSource? log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)$"[NOXMFD] DIAG98 avn.toggle {env.group}: no live aircraft (null={(Object)(object)val == (Object)null}) — ignored.");
				}
				return;
			}
			ManualLogSource? log2 = Plugin.Log;
			if (log2 != null)
			{
				log2.LogInfo((object)$"[NOXMFD] DIAG98 avn.toggle {env.group} before: radarAlt={((Unit)val).radarAlt:0.00} {TelemetryReader.Diag98State(val)}");
			}
			switch (env.group)
			{
			case "gear":
				Keybinds.DriveGear(val, up: true, down: true);
				break;
			case "radar":
				val.CmdToggleRadar();
				break;
			case "guns":
			{
				WeaponManager weaponManager = val.weaponManager;
				if (weaponManager != null)
				{
					weaponManager.ToggleGunsLinked();
				}
				break;
			}
			case "eng":
				val.CmdToggleIgnition();
				break;
			case "assist":
				val.TogglePitchLimiter();
				break;
			case "nvg":
				NightVision.Toggle();
				break;
			case "lights":
				val.ToggleNavLights();
				break;
			case "turret":
			{
				CombatHUD i = SceneSingleton<CombatHUD>.i;
				if (i != null)
				{
					i.ToggleAutoControl();
				}
				break;
			}
			default:
			{
				ManualLogSource? log3 = Plugin.Log;
				if (log3 != null)
				{
					log3.LogInfo((object)("[NOXMFD] avn.toggle: unknown group '" + env.group + "' — ignored."));
				}
				return;
			}
			}
			ManualLogSource? log4 = Plugin.Log;
			if (log4 != null)
			{
				log4.LogInfo((object)("[NOXMFD] avn.toggle " + env.group + "."));
			}
			ManualLogSource? log5 = Plugin.Log;
			if (log5 != null)
			{
				log5.LogInfo((object)("[NOXMFD] DIAG98 avn.toggle " + env.group + " after: " + TelemetryReader.Diag98State(val)));
			}
		}

		private static void DiagLog(CommandEnvelope env)
		{
			string text = env.group ?? "";
			if (text.Length > 200)
			{
				text = text.Substring(0, 200);
			}
			ManualLogSource? log = Plugin.Log;
			if (log != null)
			{
				log.LogInfo((object)("[NOXMFD] DIAG98 web: " + text));
			}
		}

		private static void AvnSet(CommandEnvelope env)
		{
			if (!LocalAircraft("avn.set", out Aircraft ac))
			{
				return;
			}
			string text = env.group;
			if (!(text == "radar"))
			{
				if (!(text == "eng"))
				{
					ManualLogSource? log = Plugin.Log;
					if (log != null)
					{
						log.LogInfo((object)("[NOXMFD] avn.set: unknown group '" + env.group + "' — ignored."));
					}
					return;
				}
				Keybinds.SetEngine(ac, env.on);
			}
			else
			{
				Keybinds.SetRadar(ac, env.on);
			}
			ManualLogSource? log2 = Plugin.Log;
			if (log2 != null)
			{
				log2.LogInfo((object)$"[NOXMFD] avn.set {env.group} = {env.on}.");
			}
		}

		private static bool InList<T>(List<T> list, int index, string who)
		{
			if (list == null || index < 0 || index >= list.Count)
			{
				ManualLogSource? log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)$"[NOXMFD] {who}: index {index} out of range — ignored.");
				}
				return false;
			}
			return true;
		}
	}
	internal static class ConfigBackup
	{
		internal static Action<string>? LogWarning;

		private static readonly HashSet<string> _warnedPaths = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		internal static void BackupIfExists(string path)
		{
			try
			{
				if (File.Exists(path))
				{
					File.Copy(path, path + ".bak", overwrite: true);
				}
			}
			catch (IOException ex)
			{
				WarnOnce(path, ex);
			}
			catch (UnauthorizedAccessException ex2)
			{
				WarnOnce(path, ex2);
			}
		}

		private static void WarnOnce(string path, Exception ex)
		{
			lock (_warnedPaths)
			{
				if (!_warnedPaths.Add(path))
				{
					return;
				}
			}
			LogWarning?.Invoke("[NOXMFD] could not back up '" + path + "': " + ex.Message + "; saving will continue.");
		}
	}
	internal sealed class ConfigurationManagerAttributes
	{
		public bool? Browsable;
	}
	public static class Api
	{
		public delegate byte[]? AssetResolver(string relPath);

		public delegate void CommandHandler(string json);

		public const int ApiVersion = 7;

		public static int RouteRevision =>