Decompiled source of Ship Customizer v0.3.0

VoidCrewShipCustomizer.dll

Decompiled 16 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using CG;
using CG.Client;
using CG.Client.Utils;
using CG.Game.Configuration;
using CG.Game.SpaceObjects.Controllers;
using CG.Objects;
using CG.Profile;
using CG.Ship.Hull;
using CG.Ship.Modules;
using CG.Ship.Object;
using CG.Space;
using CG.UI.MainMenu;
using Client.Player.Interactions;
using ExitGames.Client.Photon;
using Gameplay.Chat;
using Gameplay.CompositeWeapons;
using Gameplay.Hub;
using HarmonyLib;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Photon.Pun;
using Photon.Realtime;
using ResourceAssets;
using UI;
using UI.Core;
using UI.Core.Audio;
using UI.PowerTerminal;
using UI.ShipLoadoutTerminal;
using UnityEngine;
using UnityEngine.Localization;
using UnityEngine.UIElements;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("Void Crew Ship Customizer")]
[assembly: AssemblyDescription("BepInEx plugin that adds a Custom ship loadout to Void Crew's ship loadout terminal")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("CaptainChunck")]
[assembly: AssemblyProduct("VoidCrewShipCustomizer")]
[assembly: AssemblyCopyright("Copyright © CaptainChunck 2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("6c0b5e1a-3d2f-4b8e-9c47-5a1e0f2d7b93")]
[assembly: AssemblyFileVersion("0.3.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("0.3.0.0")]
namespace VoidCrewShipCustomizer;

internal enum OptionKind
{
	Module,
	Weapon,
	Item,
	WeaponCrate
}
internal sealed class Option
{
	public string Code;

	public OptionKind Kind;

	public GUIDUnion Guid;

	public GUIDUnion PrefabGuid;

	public string Name;

	public ContextInfo Context;

	public Sprite Icon;

	public RarityType Rarity;

	public string Category;

	public int CategoryOrder;

	public bool HasSize;

	public BuildSize Size;

	public bool SingleInstance;

	public Type ModuleType;
}
internal sealed class SocketInfo
{
	public int Index;

	public BuildSize Size;

	public Vector3[] Cells = (Vector3[])(object)new Vector3[0];

	public readonly List<int> Overlaps = new List<int>();

	public int Group;
}
internal sealed class ShelfInfo
{
	public int CoreIndex;

	public GUIDUnion ModuleGuid;

	public string Name;

	public List<CarryablesSocket> Slots = new List<CarryablesSocket>();

	public readonly Dictionary<string, bool> AcceptCache = new Dictionary<string, bool>();
}
internal sealed class HullInfo
{
	public GUIDUnion ShipGuid;

	public string Name;

	public ShipLoadoutDataDef BasePreset;

	public readonly List<ShipLoadoutDataDef> Presets = new List<ShipLoadoutDataDef>();

	public readonly List<ShipLoadoutDataDef> RegularPresets = new List<ShipLoadoutDataDef>();

	public readonly List<SocketInfo> Sockets = new List<SocketInfo>();

	public readonly List<ShelfInfo> Shelves = new List<ShelfInfo>();

	public int GroupCount;
}
internal static class Catalog
{
	private static readonly Dictionary<GUIDUnion, HullInfo> Hulls = new Dictionary<GUIDUnion, HullInfo>();

	private static readonly HashSet<GUIDUnion> FailedHulls = new HashSet<GUIDUnion>();

	private static readonly Dictionary<string, Option> ByCode = new Dictionary<string, Option>();

	private static readonly Dictionary<GUIDUnion, JObject> PresetContents = new Dictionary<GUIDUnion, JObject>();

	private static List<Option> _slotOptions;

	private static List<Option> _itemOptions;

	private static List<Option> _homunculusOptions;

	private static bool _presetContentsRead;

	private static GUIDUnion _weaponCrate;

	private static bool _weaponCrateRead;

	private static readonly FieldInfo CompareMethodField = AccessTools.Field(typeof(CsObjectReference), "compareMethod");

	private static readonly FieldInfo PrefabRuleField = AccessTools.Field(typeof(CsObjectReference), "orbitObjectRef");

	public static List<Option> SlotOptions
	{
		get
		{
			if (_slotOptions != null)
			{
				return _slotOptions;
			}
			Stopwatch stopwatch = Stopwatch.StartNew();
			List<Option> list = new List<Option>();
			AddWeaponOptions(list);
			AddModuleOptions(list);
			_slotOptions = (from o in list
				orderby o.CategoryOrder, ((int)o.Rarity != 0) ? ((int)o.Rarity) : 0
				select o).ThenBy<Option, string>((Option o) => o.Name, StringComparer.OrdinalIgnoreCase).ToList();
			foreach (Option slotOption in _slotOptions)
			{
				ByCode[slotOption.Code] = slotOption;
			}
			Log.Info($"Found {_slotOptions.Count((Option o) => o.Kind == OptionKind.Weapon)} weapons and {_slotOptions.Count((Option o) => o.Kind == OptionKind.Module)} modules that fit in hardpoints ({stopwatch.ElapsedMilliseconds} ms).");
			return _slotOptions;
		}
	}

	public static List<Option> ItemOptions
	{
		get
		{
			if (_itemOptions != null)
			{
				return _itemOptions;
			}
			Stopwatch stopwatch = Stopwatch.StartNew();
			List<Option> list = new List<Option>();
			AddItemOptions(list);
			_itemOptions = (from o in list
				orderby o.CategoryOrder, ((int)o.Rarity != 0) ? ((int)o.Rarity) : 0
				select o).ThenBy<Option, string>((Option o) => o.Name, StringComparer.OrdinalIgnoreCase).ToList();
			foreach (Option itemOption in _itemOptions)
			{
				ByCode[itemOption.Code] = itemOption;
			}
			Log.Info($"Found {_itemOptions.Count} items for the storage racks ({stopwatch.ElapsedMilliseconds} ms).");
			return _itemOptions;
		}
	}

	public static List<Option> HomunculusOptions
	{
		get
		{
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			if (_homunculusOptions != null)
			{
				return _homunculusOptions;
			}
			HashSet<GUIDUnion> hashSet = new HashSet<GUIDUnion>();
			List<Option> list = new List<Option>();
			foreach (ShipLoadoutDataDef assetDescription in ((ResourceAssetContainer<ShipLoadoutDataContainer, ShipLoadoutData, ShipLoadoutDataDef>)(object)ResourceAssetContainer<ShipLoadoutDataContainer, ShipLoadoutData, ShipLoadoutDataDef>.Instance).AssetDescriptions)
			{
				try
				{
					CarryableRef homunculusOverride = ((ResourceAssetDef<ShipLoadoutData>)(object)assetDescription).Asset.ShipLoadout.HomunculusOverride;
					if (!((ResourceAssetRef)(object)homunculusOverride == (ResourceAssetRef)null) && !((ResourceAssetRef)homunculusOverride).IsNull && hashSet.Add(((ResourceAssetRef)homunculusOverride).AssetGuid))
					{
						Option option = ItemOption(((ResourceAssetRef)homunculusOverride).AssetGuid, "HOMUNCULUS", 0);
						if (option != null)
						{
							list.Add(option);
						}
					}
				}
				catch (Exception ex)
				{
					Log.Verbose("Skipped a loadout's homunculus: " + ex.Message);
				}
			}
			_homunculusOptions = list.OrderBy<Option, string>((Option o) => o.Name, StringComparer.OrdinalIgnoreCase).ToList();
			foreach (Option homunculusOption in _homunculusOptions)
			{
				ByCode[homunculusOption.Code] = homunculusOption;
			}
			return _homunculusOptions;
		}
	}

	public static GUIDUnion WeaponCrateGuid
	{
		get
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			if (_weaponCrateRead)
			{
				return _weaponCrate;
			}
			_weaponCrateRead = true;
			try
			{
				_weaponCrate = ((ResourceAssetDef<CarryableObject>)(object)ResourceAssetContainer<CarryableContainer, CarryableObject, CarryableDef>.Instance.WeaponBuildBox).AssetGuid;
			}
			catch (Exception ex)
			{
				Log.Warn("Could not find the game's weapon crate, so weapons can't be put on storage racks: " + ex.Message);
			}
			return _weaponCrate;
		}
	}

	public static List<GUIDUnion> ShipGuids()
	{
		//IL_0033: Unknown result type (might be due to invalid IL or missing references)
		List<GUIDUnion> list = new List<GUIDUnion>();
		foreach (MonoBehaviourRef ship in ResourceAssetContainer<ShipLoadoutDataContainer, ShipLoadoutData, ShipLoadoutDataDef>.Instance.Ships)
		{
			if ((ResourceAssetRef)(object)ship != (ResourceAssetRef)null && !((ResourceAssetRef)ship).IsNull)
			{
				list.Add(((ResourceAssetRef)ship).AssetGuid);
			}
		}
		return list;
	}

	public unsafe static bool TryGetHull(GUIDUnion shipGuid, out HullInfo hull)
	{
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
		//IL_001e: 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)
		//IL_003c: Unknown result type (might be due to invalid IL or missing references)
		if (Hulls.TryGetValue(shipGuid, out hull))
		{
			return true;
		}
		if (((GUIDUnion)(ref shipGuid)).IsNull || FailedHulls.Contains(shipGuid))
		{
			return false;
		}
		try
		{
			Stopwatch stopwatch = Stopwatch.StartNew();
			hull = BuildHull(shipGuid);
			Hulls[shipGuid] = hull;
			Log.Info($"Read the {hull.Name}: {hull.Sockets.Count} hardpoint positions in {hull.GroupCount} hardpoints, {hull.Shelves.Count} storage racks, {hull.Presets.Count} loadouts ({stopwatch.ElapsedMilliseconds} ms).");
			return true;
		}
		catch (Exception ex)
		{
			FailedHulls.Add(shipGuid);
			GUIDUnion val = shipGuid;
			Log.Error("Could not read ship " + ((object)(*(GUIDUnion*)(&val))/*cast due to .constrained prefix*/).ToString() + ", so it gets no CUSTOM loadout: " + ex);
			hull = null;
			return false;
		}
	}

	private static HullInfo BuildHull(GUIDUnion shipGuid)
	{
		//IL_000c: Unknown result type (might be due to invalid IL or missing references)
		//IL_000d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0013: Unknown result type (might be due to invalid IL or missing references)
		//IL_0077: Unknown result type (might be due to invalid IL or missing references)
		//IL_007c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0272: Unknown result type (might be due to invalid IL or missing references)
		//IL_0277: Unknown result type (might be due to invalid IL or missing references)
		//IL_0327: Unknown result type (might be due to invalid IL or missing references)
		//IL_032c: Unknown result type (might be due to invalid IL or missing references)
		HullInfo hullInfo = new HullInfo
		{
			ShipGuid = shipGuid,
			Name = HeaderOf(ContextOf(shipGuid), "SHIP")
		};
		List<ShipLoadoutDataDef> list = new List<ShipLoadoutDataDef>();
		foreach (ShipLoadoutDataDef assetDescription in ((ResourceAssetContainer<ShipLoadoutDataContainer, ShipLoadoutData, ShipLoadoutDataDef>)(object)ResourceAssetContainer<ShipLoadoutDataContainer, ShipLoadoutData, ShipLoadoutDataDef>.Instance).AssetDescriptions)
		{
			try
			{
				if (assetDescription != null && !((ResourceAssetDef<ShipLoadoutData>)(object)assetDescription).IsNull && (Object)(object)((ResourceAssetDef<ShipLoadoutData>)(object)assetDescription).Asset != (Object)null && ((ResourceAssetDef<ShipLoadoutData>)(object)assetDescription).Asset.ShipLoadout != null && assetDescription.ShipGuid == shipGuid)
				{
					list.Add(assetDescription);
				}
			}
			catch (Exception ex)
			{
				Log.Verbose("Skipped a loadout while reading " + hullInfo.Name + ": " + ex.Message);
			}
		}
		if (list.Count == 0)
		{
			throw new InvalidOperationException("the game has no loadouts for this ship");
		}
		ShipLoadoutDataContainer instance = ResourceAssetContainer<ShipLoadoutDataContainer, ShipLoadoutData, ShipLoadoutDataDef>.Instance;
		List<ShipLoadoutDataDef> listed = new List<ShipLoadoutDataDef>();
		try
		{
			listed.AddRange(instance.GetUnlockedShipDefinitionsFromHighestRankPlayer());
			listed.AddRange(instance.GetLockedShipDefinitionsFromHighestRankPlayer());
		}
		catch (Exception ex2)
		{
			Log.Warn("Could not read which loadouts the terminal lists: " + ex2.Message);
		}
		hullInfo.RegularPresets.AddRange(list.Where(listed.Contains).OrderBy(RankRequirement));
		hullInfo.Presets.AddRange(hullInfo.RegularPresets);
		hullInfo.Presets.AddRange(list.Where((ShipLoadoutDataDef p) => !listed.Contains(p)));
		ShipLoadoutDataDef val = null;
		try
		{
			val = ((DefaultableResourceAssetContainer<ShipLoadoutDataContainer, ShipLoadoutData, ShipLoadoutDataDef>)(object)instance).GetDefault();
		}
		catch
		{
		}
		hullInfo.BasePreset = (ShipLoadoutDataDef)((val != null && list.Contains(val)) ? ((object)val) : ((object)(hullInfo.RegularPresets.FirstOrDefault() ?? list[0])));
		MonoBehaviour asset = ((ResourceAssetRef<MonoBehaviourContainer, MonoBehaviour, MonoBehaviourDef>)(object)((ResourceAssetDef<ShipLoadoutData>)(object)hullInfo.BasePreset).Asset.ShipLoadout.ReferenceShip).Asset;
		if ((Object)(object)asset == (Object)null)
		{
			throw new InvalidOperationException("the ship prefab could not be loaded");
		}
		BuildSocketController component = ((Component)asset).GetComponent<BuildSocketController>();
		List<BuildSocket> list2 = (((Object)(object)component != (Object)null && component.Sockets != null && component.Sockets.Count > 0) ? component.Sockets : new List<BuildSocket>(((Component)asset).GetComponentsInChildren<BuildSocket>(true)));
		for (int num = 0; num < list2.Count; num++)
		{
			SocketInfo socketInfo = new SocketInfo
			{
				Index = num
			};
			if ((Object)(object)list2[num] != (Object)null)
			{
				socketInfo.Size = list2[num].Size;
				socketInfo.Cells = CellsOf(list2[num]);
			}
			hullInfo.Sockets.Add(socketInfo);
		}
		hullInfo.GroupCount = Hardpoints.LinkOverlaps(hullInfo.Sockets);
		PlayerControlledShip component2 = ((Component)asset).GetComponent<PlayerControlledShip>();
		CellModule[] array = (((Object)(object)component2 != (Object)null) ? component2.CoreSystems : null);
		if (array != null)
		{
			for (int num2 = 0; num2 < array.Length; num2++)
			{
				CarryablesShelf val2 = (((Object)(object)array[num2] != (Object)null) ? ((Component)array[num2]).GetComponent<CarryablesShelf>() : null);
				if (!((Object)(object)val2 == (Object)null))
				{
					ShelfInfo shelfInfo = new ShelfInfo
					{
						CoreIndex = num2,
						ModuleGuid = ((AbstractCloneStarObject)val2).assetGuid
					};
					shelfInfo.Slots.AddRange(((CellModule)val2).CarryablesSockets.Where((CarryablesSocket s) => (Object)(object)s != (Object)null));
					shelfInfo.Name = "STORAGE RACK " + (hullInfo.Shelves.Count + 1);
					hullInfo.Shelves.Add(shelfInfo);
				}
			}
		}
		return hullInfo;
	}

	private static Vector3[] CellsOf(BuildSocket socket)
	{
		//IL_0008: 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_0014: Unknown result type (might be due to invalid IL or missing references)
		//IL_001a: Unknown result type (might be due to invalid IL or missing references)
		Transform transform = ((Component)socket).transform;
		return Hardpoints.CellsFor(socket.Size, transform.position, transform.right, transform.forward);
	}

	private static int RankRequirement(ShipLoadoutDataDef def)
	{
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_001d: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			UnlockItemDef assetDefById = ((ResourceAssetContainer<UnlockContainer, Object, UnlockItemDef>)(object)ResourceAssetContainer<UnlockContainer, Object, UnlockItemDef>.Instance).GetAssetDefById(((ResourceAssetDef<ShipLoadoutData>)(object)def).AssetGuid, false);
			return (assetDefById != null) ? assetDefById.UnlockOptions.RankRequirement : int.MaxValue;
		}
		catch
		{
			return int.MaxValue;
		}
	}

	public static bool TryGetOption(string code, out Option option)
	{
		//IL_0088: Unknown result type (might be due to invalid IL or missing references)
		//IL_009f: Unknown result type (might be due to invalid IL or missing references)
		option = null;
		if (string.IsNullOrEmpty(code))
		{
			return false;
		}
		if (ByCode.TryGetValue(code, out option))
		{
			return true;
		}
		if (code.StartsWith("m:") || code.StartsWith("w:"))
		{
			_ = SlotOptions;
		}
		else
		{
			_ = ItemOptions;
		}
		if (ByCode.TryGetValue(code, out option))
		{
			return true;
		}
		if (code.Length > 2 && GUIDUnion.IsValidString(code.Substring(2)))
		{
			GUIDUnion guid = default(GUIDUnion);
			((GUIDUnion)(ref guid))..ctor(code.Substring(2));
			if (code.StartsWith("m:"))
			{
				option = RawModuleOption(guid);
			}
			else if (code.StartsWith("i:"))
			{
				option = ItemOption(guid, "OTHER", 99);
			}
			if (option != null)
			{
				ByCode[code] = option;
			}
		}
		return option != null;
	}

	public static bool CanSpawn(GUIDUnion guid)
	{
		//IL_000e: Unknown result type (might be due to invalid IL or missing references)
		if (!((GUIDUnion)(ref guid)).IsNull)
		{
			return ((ResourceAssetContainer<CloneStarObjectContainer, AbstractCloneStarObject, CloneStarObjectDef>)(object)ResourceAssetContainer<CloneStarObjectContainer, AbstractCloneStarObject, CloneStarObjectDef>.Instance).HasItem(guid);
		}
		return false;
	}

	private static Option RawModuleOption(GUIDUnion guid)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_000a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0035: 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)
		//IL_003c: Unknown result type (might be due to invalid IL or missing references)
		//IL_003d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0073: Unknown result type (might be due to invalid IL or missing references)
		//IL_0074: Unknown result type (might be due to invalid IL or missing references)
		//IL_0079: Unknown result type (might be due to invalid IL or missing references)
		//IL_0097: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
		if (!CanSpawn(guid))
		{
			return null;
		}
		ContextInfo val = ContextOf(guid);
		Option option = new Option
		{
			Code = "m:" + ((GUIDUnion)(ref guid)).AsHex(),
			Kind = OptionKind.Module,
			Guid = guid,
			PrefabGuid = guid,
			Name = HeaderOf(val, "Module"),
			Context = val,
			Icon = (((Object)(object)val != (Object)null) ? val.Icon : null),
			Rarity = RarityOf(guid),
			Category = "OTHER",
			CategoryOrder = 9
		};
		try
		{
			CellModule val2 = ((ResourceAssetDef<CellModule>)(object)((ResourceAssetContainer<ModuleContainer, CellModule, ModuleDef>)(object)ResourceAssetContainer<ModuleContainer, CellModule, ModuleDef>.Instance).GetAssetDefById(guid, false))?.Asset;
			if ((Object)(object)val2 != (Object)null && TryGetSize(val2.BuildingConstraints, out var size))
			{
				option.HasSize = true;
				option.Size = size;
				option.SingleInstance = val2.BuildingConstraints.AllowOnlySingleInstance;
				option.ModuleType = ((object)val2).GetType();
			}
		}
		catch (Exception ex)
		{
			Log.Verbose("Could not read the size of module " + option.Name + ": " + ex.Message);
		}
		return option;
	}

	private static void AddWeaponOptions(List<Option> list)
	{
		//IL_007a: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
		//IL_0107: Unknown result type (might be due to invalid IL or missing references)
		//IL_010c: Unknown result type (might be due to invalid IL or missing references)
		//IL_011d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0122: Unknown result type (might be due to invalid IL or missing references)
		//IL_0151: Unknown result type (might be due to invalid IL or missing references)
		//IL_0156: Unknown result type (might be due to invalid IL or missing references)
		//IL_015b: Unknown result type (might be due to invalid IL or missing references)
		//IL_017b: Unknown result type (might be due to invalid IL or missing references)
		//IL_017d: Unknown result type (might be due to invalid IL or missing references)
		foreach (CompositeWeaponDataDef assetDescription in ((ResourceAssetContainer<CompositeWeaponDataContainer, CompositeWeaponData, CompositeWeaponDataDef>)(object)ResourceAssetContainer<CompositeWeaponDataContainer, CompositeWeaponData, CompositeWeaponDataDef>.Instance).AssetDescriptions)
		{
			try
			{
				if (assetDescription == null || ((ResourceAssetDef<CompositeWeaponData>)(object)assetDescription).IsNull)
				{
					continue;
				}
				CompositeWeaponData asset = ((ResourceAssetDef<CompositeWeaponData>)(object)assetDescription).Asset;
				if ((Object)(object)asset == (Object)null || asset.Data == null || (ResourceAssetRef)(object)asset.Data.CoreElement == (ResourceAssetRef)null || ((ResourceAssetRef)asset.Data.CoreElement).IsNull || !CanSpawn(((ResourceAssetRef)asset.Data.CoreElement).AssetGuid))
				{
					continue;
				}
				CompositeWeaponModule asset2 = ((ResourceAssetRef<WeaponCoreContainer, CompositeWeaponModule, WeaponCoreDef>)(object)asset.Data.CoreElement).Asset;
				if (!((Object)(object)asset2 == (Object)null) && TryGetSize(((CellModule)asset2).BuildingConstraints, out var size))
				{
					IResourceAssetContextInfo contextInfo = ((PurchasableResourceAssetDef<CompositeWeaponData>)(object)assetDescription).ContextInfo;
					ContextInfo val = (ContextInfo)(object)((contextInfo is ContextInfo) ? contextInfo : null);
					if (!IsMissing(val))
					{
						Option option = new Option();
						GUIDUnion assetGuid = ((ResourceAssetDef<CompositeWeaponData>)(object)assetDescription).AssetGuid;
						option.Code = "w:" + ((GUIDUnion)(ref assetGuid)).AsHex();
						option.Kind = OptionKind.Weapon;
						option.Guid = ((ResourceAssetDef<CompositeWeaponData>)(object)assetDescription).AssetGuid;
						option.PrefabGuid = ((ResourceAssetRef)asset.Data.CoreElement).AssetGuid;
						option.Name = HeaderOf(val, ((Object)asset).name);
						option.Context = val;
						option.Icon = val.Icon;
						option.Rarity = RarityOf(((ResourceAssetDef<CompositeWeaponData>)(object)assetDescription).AssetGuid);
						option.Category = CategoryName((ECategory)1);
						option.CategoryOrder = 1;
						option.HasSize = true;
						option.Size = size;
						option.SingleInstance = ((CellModule)asset2).BuildingConstraints.AllowOnlySingleInstance;
						option.ModuleType = ((object)asset2).GetType();
						list.Add(option);
					}
				}
			}
			catch (Exception ex)
			{
				Log.Verbose("Skipped weapon " + ((assetDescription != null) ? ((ResourceAssetDef<CompositeWeaponData>)(object)assetDescription).Path : "?") + ": " + ex.Message);
			}
		}
	}

	private static void AddModuleOptions(List<Option> list)
	{
		//IL_0029: Unknown result type (might be due to invalid IL or missing references)
		//IL_0031: Unknown result type (might be due to invalid IL or missing references)
		//IL_0082: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00db: Unknown result type (might be due to invalid IL or missing references)
		//IL_010a: Unknown result type (might be due to invalid IL or missing references)
		//IL_010f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0114: Unknown result type (might be due to invalid IL or missing references)
		//IL_011b: Unknown result type (might be due to invalid IL or missing references)
		//IL_012c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0136: Expected I4, but got Unknown
		//IL_013e: Unknown result type (might be due to invalid IL or missing references)
		//IL_013f: Unknown result type (might be due to invalid IL or missing references)
		foreach (ModuleDef assetDescription in ((ResourceAssetContainer<ModuleContainer, CellModule, ModuleDef>)(object)ResourceAssetContainer<ModuleContainer, CellModule, ModuleDef>.Instance).AssetDescriptions)
		{
			try
			{
				if (assetDescription == null || ((ResourceAssetDef<CellModule>)(object)assetDescription).IsNull || (int)assetDescription.Category == 0 || !CanSpawn(((ResourceAssetDef<CellModule>)(object)assetDescription).AssetGuid))
				{
					continue;
				}
				CellModule asset = ((ResourceAssetDef<CellModule>)(object)assetDescription).Asset;
				if (!((Object)(object)asset == (Object)null) && !(asset is CompositeWeaponModule) && !((Object)(object)((Component)asset).GetComponent<CarryablesShelf>() != (Object)null) && TryGetSize(asset.BuildingConstraints, out var size))
				{
					ContextInfo val = ContextOf(((ResourceAssetDef<CellModule>)(object)assetDescription).AssetGuid);
					if (!IsMissing(val))
					{
						Option option = new Option();
						GUIDUnion assetGuid = ((ResourceAssetDef<CellModule>)(object)assetDescription).AssetGuid;
						option.Code = "m:" + ((GUIDUnion)(ref assetGuid)).AsHex();
						option.Kind = OptionKind.Module;
						option.Guid = ((ResourceAssetDef<CellModule>)(object)assetDescription).AssetGuid;
						option.PrefabGuid = ((ResourceAssetDef<CellModule>)(object)assetDescription).AssetGuid;
						option.Name = HeaderOf(val, ((Object)asset).name);
						option.Context = val;
						option.Icon = val.Icon;
						option.Rarity = RarityOf(((ResourceAssetDef<CellModule>)(object)assetDescription).AssetGuid);
						option.Category = CategoryName(assetDescription.Category);
						option.CategoryOrder = (int)assetDescription.Category;
						option.HasSize = true;
						option.Size = size;
						option.SingleInstance = asset.BuildingConstraints.AllowOnlySingleInstance;
						option.ModuleType = ((object)asset).GetType();
						list.Add(option);
					}
				}
			}
			catch (Exception ex)
			{
				Log.Verbose("Skipped module " + ((assetDescription != null) ? ((ResourceAssetDef<CellModule>)(object)assetDescription).Path : "?") + ": " + ex.Message);
			}
		}
	}

	private static void AddItemOptions(List<Option> list)
	{
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		//IL_000a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0172: Unknown result type (might be due to invalid IL or missing references)
		//IL_0177: Unknown result type (might be due to invalid IL or missing references)
		//IL_017d: Unknown result type (might be due to invalid IL or missing references)
		//IL_017e: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ad: Unknown result type (might be due to invalid IL or missing references)
		//IL_012f: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
		//IL_002f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0034: Unknown result type (might be due to invalid IL or missing references)
		//IL_003f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0046: Invalid comparison between Unknown and I4
		//IL_0057: Unknown result type (might be due to invalid IL or missing references)
		//IL_005d: 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)
		CarryableContainer instance = ResourceAssetContainer<CarryableContainer, CarryableObject, CarryableDef>.Instance;
		GUIDUnion weaponCrateGuid = WeaponCrateGuid;
		foreach (CarryableDef assetDescription in ((ResourceAssetContainer<CarryableContainer, CarryableObject, CarryableDef>)(object)instance).AssetDescriptions)
		{
			try
			{
				if (assetDescription != null && !((ResourceAssetDef<CarryableObject>)(object)assetDescription).IsNull && !(((ResourceAssetDef<CarryableObject>)(object)assetDescription).AssetGuid == weaponCrateGuid) && ((int)((PurchasableResourceAssetDef<CarryableObject>)(object)assetDescription).Category != 65 || Settings.ShowQuestItems.Value))
				{
					Option option = ItemOption(((ResourceAssetDef<CarryableObject>)(object)assetDescription).AssetGuid, ItemCategoryName(((PurchasableResourceAssetDef<CarryableObject>)(object)assetDescription).Category), ItemCategoryOrder(((PurchasableResourceAssetDef<CarryableObject>)(object)assetDescription).Category));
					if (option != null)
					{
						list.Add(option);
					}
				}
			}
			catch (Exception ex)
			{
				Log.Verbose("Skipped item " + ((assetDescription != null) ? ((ResourceAssetDef<CarryableObject>)(object)assetDescription).Path : "?") + ": " + ex.Message);
			}
		}
		if (((GUIDUnion)(ref weaponCrateGuid)).IsNull || !CanSpawn(weaponCrateGuid))
		{
			return;
		}
		foreach (Option item in SlotOptions.Where((Option o) => o.Kind == OptionKind.Weapon))
		{
			Sprite icon = item.Icon;
			try
			{
				icon = ((ResourceAssetContainer<CompositeWeaponDataContainer, CompositeWeaponData, CompositeWeaponDataDef>)(object)ResourceAssetContainer<CompositeWeaponDataContainer, CompositeWeaponData, CompositeWeaponDataDef>.Instance).GetAssetDefById(item.Guid, true).BuildBoxIcon;
			}
			catch
			{
			}
			list.Add(new Option
			{
				Code = "c:" + ((GUIDUnion)(ref item.Guid)).AsHex(),
				Kind = OptionKind.WeaponCrate,
				Guid = item.Guid,
				PrefabGuid = weaponCrateGuid,
				Name = item.Name,
				Context = item.Context,
				Icon = icon,
				Rarity = item.Rarity,
				Category = "WEAPON CRATES",
				CategoryOrder = 5
			});
		}
	}

	private static Option ItemOption(GUIDUnion guid, string category, int order)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_000a: Unknown result type (might be due to invalid IL or missing references)
		//IL_003f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0040: Unknown result type (might be due to invalid IL or missing references)
		//IL_0046: Unknown result type (might be due to invalid IL or missing references)
		//IL_0047: 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_0072: Unknown result type (might be due to invalid IL or missing references)
		//IL_0077: Unknown result type (might be due to invalid IL or missing references)
		if (!CanSpawn(guid))
		{
			return null;
		}
		ContextInfo val = ContextOf(guid);
		if (IsMissing(val))
		{
			return null;
		}
		return new Option
		{
			Code = "i:" + ((GUIDUnion)(ref guid)).AsHex(),
			Kind = OptionKind.Item,
			Guid = guid,
			PrefabGuid = guid,
			Name = HeaderOf(val, "Item"),
			Context = val,
			Icon = val.Icon,
			Rarity = RarityOf(guid),
			Category = category,
			CategoryOrder = order
		};
	}

	private static string ItemCategoryName(PurchasableItemSubCategory category)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0003: Invalid comparison between Unknown and I4
		//IL_0023: Unknown result type (might be due to invalid IL or missing references)
		//IL_0026: Invalid comparison between Unknown and I4
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		//IL_0008: Unknown result type (might be due to invalid IL or missing references)
		//IL_000a: Invalid comparison between Unknown and I4
		//IL_0028: Unknown result type (might be due to invalid IL or missing references)
		//IL_002b: Invalid comparison between Unknown and I4
		//IL_000c: Unknown result type (might be due to invalid IL or missing references)
		//IL_000f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0021: Expected I4, but got Unknown
		//IL_002d: 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)
		//IL_0032: Invalid comparison between Unknown and I4
		if ((int)category <= 35)
		{
			if (category - 17 <= 3)
			{
				return "ANIMUS CRATES";
			}
			switch (category - 33)
			{
			case 0:
				return "AMMO & POWER";
			case 1:
				return "SUPPORT";
			case 2:
				return "MATERIALS";
			}
		}
		else
		{
			if ((int)category == 49)
			{
				return "WEAPON MODS";
			}
			if ((int)category == 50)
			{
				return "RELICS";
			}
			if (category - 65 <= 1)
			{
				return "SPECIAL";
			}
		}
		return "OTHER";
	}

	private static int ItemCategoryOrder(PurchasableItemSubCategory category)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		return ItemCategoryName(category) switch
		{
			"AMMO & POWER" => 1, 
			"SUPPORT" => 2, 
			"MATERIALS" => 3, 
			"ANIMUS CRATES" => 4, 
			"WEAPON MODS" => 6, 
			"RELICS" => 7, 
			"SPECIAL" => 8, 
			_ => 9, 
		};
	}

	private unsafe static string CategoryName(ECategory category)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			string text = ECategoryExtension.ToStringLocalized(category);
			if (!string.IsNullOrEmpty(text))
			{
				return text.ToUpperInvariant();
			}
		}
		catch
		{
		}
		return ((object)(*(ECategory*)(&category))/*cast due to .constrained prefix*/).ToString().ToUpperInvariant();
	}

	public static bool ShelfAccepts(ShelfInfo shelf, Option item)
	{
		//IL_009e: 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_0064: Unknown result type (might be due to invalid IL or missing references)
		if (shelf.Slots.Count == 0)
		{
			return true;
		}
		string key = ((item.Kind == OptionKind.WeaponCrate) ? "crate" : item.Code);
		if (shelf.AcceptCache.TryGetValue(key, out var value))
		{
			return value;
		}
		value = true;
		try
		{
			CarryablesSocket val = shelf.Slots[0];
			bool flag = false;
			if (val.rejectedList != null)
			{
				CsObjectReference[] rejectedList = val.rejectedList;
				for (int i = 0; i < rejectedList.Length; i++)
				{
					bool? flag2 = QuickMatch(rejectedList[i], item.PrefabGuid);
					if (flag2 == true)
					{
						value = false;
					}
					if (!flag2.HasValue)
					{
						flag = true;
					}
				}
			}
			if (value)
			{
				bool? flag3 = QuickMatch(val.accepted, item.PrefabGuid);
				if (flag3 == false)
				{
					value = false;
				}
				if (!flag3.HasValue)
				{
					flag = true;
				}
			}
			if (value && flag)
			{
				ISelfReferencingResourceAsset asset = (ISelfReferencingResourceAsset)(object)((ResourceAssetDef<CarryableObject>)(object)((ResourceAssetContainer<CarryableContainer, CarryableObject, CarryableDef>)(object)ResourceAssetContainer<CarryableContainer, CarryableObject, CarryableDef>.Instance).GetAssetDefById(item.PrefabGuid, false))?.Asset;
				if (asset != null)
				{
					if (val.rejectedList != null && val.rejectedList.Any((CsObjectReference r) => r != null && r.IsInstanceOf(asset)))
					{
						value = false;
					}
					else if (val.accepted != null)
					{
						value = val.accepted.IsInstanceOf(asset);
					}
				}
			}
		}
		catch (Exception ex)
		{
			Log.Verbose("Could not check whether " + shelf.Name + " takes " + item.Name + ": " + ex.Message);
			value = true;
		}
		shelf.AcceptCache[key] = value;
		return value;
	}

	private static bool? QuickMatch(CsObjectReference rule, GUIDUnion item)
	{
		//IL_002c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0031: Unknown result type (might be due to invalid IL or missing references)
		//IL_0032: Unknown result type (might be due to invalid IL or missing references)
		//IL_0035: Unknown result type (might be due to invalid IL or missing references)
		//IL_0037: Invalid comparison between Unknown and I4
		//IL_0039: Unknown result type (might be due to invalid IL or missing references)
		//IL_003b: Invalid comparison between Unknown and I4
		//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)
		if (rule == null)
		{
			return false;
		}
		if (CompareMethodField == null)
		{
			return null;
		}
		Comparison val = (Comparison)CompareMethodField.GetValue(rule);
		if ((int)val != 0)
		{
			if ((int)val != 2)
			{
				if ((int)val == 5)
				{
					return true;
				}
				return null;
			}
			ResourceAssetRef val2 = (ResourceAssetRef)((PrefabRuleField != null) ? /*isinst with value type is only supported in some contexts*/: null);
			return val2 != (ResourceAssetRef)null && !val2.IsNull && val2.AssetGuid == item;
		}
		return false;
	}

	public static Option OptionFor(ShipSocketEntry entry)
	{
		//IL_0026: Unknown result type (might be due to invalid IL or missing references)
		//IL_002b: Unknown result type (might be due to invalid IL or missing references)
		if (entry == null || (ResourceAssetRef)(object)entry.Module == (ResourceAssetRef)null || ((ResourceAssetRef)entry.Module).IsNull)
		{
			return null;
		}
		GUIDUnion val = WeaponGuidOf(entry.ExtraData);
		if (!((GUIDUnion)(ref val)).IsNull && TryGetOption("w:" + ((GUIDUnion)(ref val)).AsHex(), out var option))
		{
			return option;
		}
		if (TryGetOption("m:" + ((GUIDUnion)(ref ((ResourceAssetRef)entry.Module).AssetGuid)).AsHex(), out option))
		{
			return option;
		}
		return null;
	}

	public static string CodeFor(ShipAssetRef item)
	{
		//IL_0025: Unknown result type (might be due to invalid IL or missing references)
		//IL_002a: Unknown result type (might be due to invalid IL or missing references)
		//IL_004c: 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)
		if (item == null || item.AssetRef == (ResourceAssetRef)null || item.IsNull)
		{
			return "";
		}
		GUIDUnion val = WeaponGuidOf(item.ExtraData);
		if (!((GUIDUnion)(ref val)).IsNull)
		{
			return "c:" + ((GUIDUnion)(ref val)).AsHex();
		}
		GUIDUnion assetGuid = item.AssetGuid;
		return "i:" + ((GUIDUnion)(ref assetGuid)).AsHex();
	}

	public static GUIDUnion WeaponGuidOf(JObject extraData)
	{
		//IL_0012: Unknown result type (might be due to invalid IL or missing references)
		//IL_002a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0038: Unknown result type (might be due to invalid IL or missing references)
		//IL_0032: Unknown result type (might be due to invalid IL or missing references)
		JToken val = default(JToken);
		if (extraData == null || !extraData.TryGetValue("AssetGuid", ref val))
		{
			return GUIDUnion.Empty();
		}
		GUIDUnion val2 = default(GUIDUnion);
		((GUIDUnion)(ref val2))..ctor(((object)val).ToString());
		if (!((ResourceAssetContainer<CompositeWeaponDataContainer, CompositeWeaponData, CompositeWeaponDataDef>)(object)ResourceAssetContainer<CompositeWeaponDataContainer, CompositeWeaponData, CompositeWeaponDataDef>.Instance).HasItem(val2))
		{
			return GUIDUnion.Empty();
		}
		return val2;
	}

	public static bool TryGetPresetContents(GUIDUnion moduleOrWeapon, out JObject entry)
	{
		//IL_0159: 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)
		//IL_0082: Unknown result type (might be due to invalid IL or missing references)
		//IL_0093: Unknown result type (might be due to invalid IL or missing references)
		//IL_0098: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
		if (!_presetContentsRead)
		{
			_presetContentsRead = true;
			foreach (ShipLoadoutDataDef assetDescription in ((ResourceAssetContainer<ShipLoadoutDataContainer, ShipLoadoutData, ShipLoadoutDataDef>)(object)ResourceAssetContainer<ShipLoadoutDataContainer, ShipLoadoutData, ShipLoadoutDataDef>.Instance).AssetDescriptions)
			{
				try
				{
					foreach (ShipSocketEntry socket in ((ResourceAssetDef<ShipLoadoutData>)(object)assetDescription).Asset.ShipLoadout.Sockets)
					{
						if (socket != null && !((ResourceAssetRef)(object)socket.Module == (ResourceAssetRef)null) && !((ResourceAssetRef)socket.Module).IsNull)
						{
							GUIDUnion key = WeaponGuidOf(socket.ExtraData);
							if (((GUIDUnion)(ref key)).IsNull)
							{
								key = ((ResourceAssetRef)socket.Module).AssetGuid;
							}
							bool flag = socket.Upgrades != null && socket.Upgrades.Any((ShipAssetRef u) => u != null && !u.IsNull);
							if (!PresetContents.TryGetValue(key, out var value) || (flag && !HasContents(value)))
							{
								PresetContents[key] = JObject.FromObject((object)socket);
							}
						}
					}
				}
				catch (Exception ex)
				{
					Log.Verbose("Skipped a loadout while reading module contents: " + ex.Message);
				}
			}
		}
		return PresetContents.TryGetValue(moduleOrWeapon, out entry);
	}

	private static bool HasContents(JObject entry)
	{
		JToken obj = entry["Upgrades"];
		return ((IEnumerable<JToken>)((obj is JArray) ? obj : null))?.Any(delegate(JToken u)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			if (u is JObject && u[(object)"AssetRef"] is JObject)
			{
				GUIDUnion val = new GUIDUnion(((object)((JObject)u[(object)"AssetRef"])["AssetGuid"])?.ToString());
				return !((GUIDUnion)(ref val)).IsNull;
			}
			return false;
		}) ?? false;
	}

	public static ContextInfo ContextOf(GUIDUnion guid)
	{
		//IL_0010: Unknown result type (might be due to invalid IL or missing references)
		if (((GUIDUnion)(ref guid)).IsNull)
		{
			return null;
		}
		IResourceAssetContextInfo context = ResourceAssetContainer<CloneStarObjectContainer, AbstractCloneStarObject, CloneStarObjectDef>.Instance.GetContext(guid);
		return (ContextInfo)(object)((context is ContextInfo) ? context : null);
	}

	public static string HeaderOf(ContextInfo context, string fallback)
	{
		try
		{
			string text = (((Object)(object)context != (Object)null) ? context.HeaderText : null);
			return string.IsNullOrEmpty(text) ? fallback : text;
		}
		catch
		{
			return fallback;
		}
	}

	private static bool IsMissing(ContextInfo context)
	{
		if ((Object)(object)context == (Object)null)
		{
			return true;
		}
		try
		{
			return (Object)(object)context == (Object)(object)DataTable<DefaultAssetTable>.Instance.MissingContextInfo;
		}
		catch
		{
			return false;
		}
	}

	public static RarityType RarityOf(GUIDUnion guid)
	{
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_000b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0010: Unknown result type (might be due to invalid IL or missing references)
		//IL_0013: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			return ResourceAssetContainer<UnlockContainer, Object, UnlockItemDef>.Instance.GetRarity(guid);
		}
		catch
		{
			return (RarityType)0;
		}
	}

	private static bool TryGetSize(BuildingConstraints constraints, out BuildSize size)
	{
		//IL_001c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0022: Expected I4, but got Unknown
		size = (BuildSize)0;
		if (constraints == null)
		{
			return false;
		}
		try
		{
			size = (BuildSize)(int)BuildSizeExtensions.GetSize(constraints.Width, constraints.Height, constraints.Depth);
			return true;
		}
		catch
		{
			return false;
		}
	}

	public static string SizeName(BuildSize size)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0003: Unknown result type (might be due to invalid IL or missing references)
		//IL_0005: Invalid comparison between Unknown and I4
		if ((int)size != 0)
		{
			if ((int)size == 1)
			{
				return "2x1";
			}
			return "2x2";
		}
		return "1x1";
	}
}
internal static class CustomLoadouts
{
	private sealed class Entry
	{
		public HullInfo Hull;

		public GUIDUnion Guid;

		public ShipLoadoutDataDef Def;

		public ShipLoadoutData Data;
	}

	public const string Header = "CUSTOM";

	private static readonly Dictionary<GUIDUnion, Entry> ByShip = new Dictionary<GUIDUnion, Entry>();

	private static readonly Dictionary<GUIDUnion, Entry> ByGuid = new Dictionary<GUIDUnion, Entry>();

	private static readonly FieldInfo HeaderTextField = AccessTools.Field(typeof(ContextInfo), "headerTextDefaultable");

	private static readonly FieldInfo BodyTextField = AccessTools.Field(typeof(ContextInfo), "bodyTextDefaultable");

	public static bool Available;

	public static GUIDUnion ActiveShip = GUIDUnion.Empty();

	public static HullConfig RemoteConfig;

	public static GUIDUnion ResumeShip = GUIDUnion.Empty();

	public static bool IsActive => !((GUIDUnion)(ref ActiveShip)).IsNull;

	public static bool IsCustom(ShipLoadoutDataDef def)
	{
		//IL_0009: Unknown result type (might be due to invalid IL or missing references)
		if (def != null)
		{
			return ByGuid.ContainsKey(((ResourceAssetDef<ShipLoadoutData>)(object)def).AssetGuid);
		}
		return false;
	}

	public static bool TryGetHullOfCustomGuid(GUIDUnion guid, out HullInfo hull)
	{
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		hull = (ByGuid.TryGetValue(guid, out var value) ? value.Hull : null);
		return hull != null;
	}

	public static bool TryGetHull(ShipLoadoutDataDef customDef, out HullInfo hull)
	{
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		hull = null;
		if (customDef != null)
		{
			return TryGetHullOfCustomGuid(((ResourceAssetDef<ShipLoadoutData>)(object)customDef).AssetGuid, out hull);
		}
		return false;
	}

	public static bool TryGetDef(GUIDUnion shipGuid, out ShipLoadoutDataDef def)
	{
		//IL_001a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0024: Unknown result type (might be due to invalid IL or missing references)
		//IL_006b: 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)
		def = null;
		if (!Available || ((GUIDUnion)(ref shipGuid)).IsNull)
		{
			return false;
		}
		if (!ByShip.TryGetValue(shipGuid, out var value))
		{
			if (!Catalog.TryGetHull(shipGuid, out var hull))
			{
				return false;
			}
			try
			{
				value = Create(hull);
			}
			catch (Exception ex)
			{
				Log.Error("Could not create the CUSTOM loadout for the " + hull.Name + ": " + ex);
				return false;
			}
			ByShip[shipGuid] = value;
			ByGuid[value.Guid] = value;
		}
		def = value.Def;
		return true;
	}

	private static Entry Create(HullInfo hull)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_000b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0031: Unknown result type (might be due to invalid IL or missing references)
		//IL_004e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0064: Unknown result type (might be due to invalid IL or missing references)
		//IL_006a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0070: Expected O, but got Unknown
		//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
		//IL_005b: Unknown result type (might be due to invalid IL or missing references)
		GUIDUnion val = CustomGuidFor(hull.ShipGuid);
		ShipLoadoutData val2 = ScriptableObject.CreateInstance<ShipLoadoutData>();
		((Object)val2).name = "ShipCustomizer " + hull.Name;
		((Object)val2).hideFlags = (HideFlags)32;
		((SelfReferencingScriptableObject)val2).ContainerGuid = val;
		val2.ShipLoadout = Build(hull, ConfigToShow(hull));
		if (!RuntimeAssetsRegister.Instance.HasAsset(val))
		{
			RuntimeAssetsRegister.Instance.RegisterAsset(val, (Object)(object)val2, (SessionModificationEffect)0, (RuntimeAssetInfo)null);
		}
		ShipLoadoutDataDef val3 = new ShipLoadoutDataDef(val, "");
		((ResourceAssetDef<ShipLoadoutData>)(object)val3).Ref.IsRuntime = true;
		val3.LoadoutContextInfo = MakeContext(hull);
		Log.Info("Added a CUSTOM loadout for the " + hull.Name + " (shown to other players as " + LoadoutStore.PresetName(hull.BasePreset) + ").");
		return new Entry
		{
			Hull = hull,
			Guid = val,
			Def = val3,
			Data = val2
		};
	}

	private static GUIDUnion CustomGuidFor(GUIDUnion ship)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_000c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0018: Unknown result type (might be due to invalid IL or missing references)
		//IL_0024: 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)
		return new GUIDUnion(ship.m_Value0 ^ 0x50494853, ship.m_Value1 ^ 0x54535543, ship.m_Value2 ^ 0x5A494D4F, ship.m_Value3 ^ 0x21215245);
	}

	private static ShipLoadoutContextInfo MakeContext(HullInfo hull)
	{
		//IL_004e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0058: Expected O, but got Unknown
		//IL_006d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0077: Expected O, but got Unknown
		ShipLoadoutContextInfo val = Object.Instantiate<ShipLoadoutContextInfo>(hull.BasePreset.LoadoutContextInfo);
		((Object)val).name = "ShipCustomizer CUSTOM " + hull.Name;
		((Object)val).hideFlags = (HideFlags)32;
		string text = "<b>Custom Loadout</b>\n<i>Click a hardpoint on the ship, or use HARDPOINTS, to fit any module or weapon.</i>\nMissions flown with it are marked modded and earn no XP or rewards.";
		if (HeaderTextField != null)
		{
			HeaderTextField.SetValue(val, (object?)new DefaultableLocalizedString("CUSTOM", (LocalizedString)null));
		}
		if (BodyTextField != null)
		{
			BodyTextField.SetValue(val, (object?)new DefaultableLocalizedString(text, (LocalizedString)null));
		}
		return val;
	}

	public static HullConfig ConfigToShow(HullInfo hull)
	{
		//IL_000e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0014: Unknown result type (might be due to invalid IL or missing references)
		if (!PhotonNetwork.IsMasterClient && RemoteConfig != null && ActiveShip == hull.ShipGuid)
		{
			return RemoteConfig;
		}
		return LoadoutStore.Get(hull);
	}

	public static bool CanEdit(HullInfo hull)
	{
		//IL_000e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0014: Unknown result type (might be due to invalid IL or missing references)
		if (!PhotonNetwork.IsMasterClient && RemoteConfig != null)
		{
			return ActiveShip != hull.ShipGuid;
		}
		return true;
	}

	public static void Refresh(HullInfo hull)
	{
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		if (!ByShip.TryGetValue(hull.ShipGuid, out var value))
		{
			return;
		}
		try
		{
			value.Data.ShipLoadout = Build(hull, ConfigToShow(hull));
		}
		catch (Exception e)
		{
			Log.Failure("Rebuilding the CUSTOM loadout of the " + hull.Name, e);
		}
	}

	public static void RefreshAll()
	{
		foreach (Entry value in ByShip.Values)
		{
			Refresh(value.Hull);
		}
	}

	public unsafe static ShipLoadout Build(HullInfo hull, HullConfig config)
	{
		//IL_009b: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
		//IL_0132: Unknown result type (might be due to invalid IL or missing references)
		//IL_023d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0321: Unknown result type (might be due to invalid IL or missing references)
		//IL_032b: Expected O, but got Unknown
		//IL_024c: 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_0263: Unknown result type (might be due to invalid IL or missing references)
		//IL_026b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0271: Unknown result type (might be due to invalid IL or missing references)
		//IL_027b: Expected O, but got Unknown
		//IL_0276: Unknown result type (might be due to invalid IL or missing references)
		//IL_0280: Expected O, but got Unknown
		//IL_0280: Unknown result type (might be due to invalid IL or missing references)
		//IL_0281: Unknown result type (might be due to invalid IL or missing references)
		//IL_028b: Expected O, but got Unknown
		//IL_028b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0299: Expected O, but got Unknown
		//IL_0348: Unknown result type (might be due to invalid IL or missing references)
		//IL_0356: Unknown result type (might be due to invalid IL or missing references)
		//IL_035b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0365: Expected O, but got Unknown
		ShipLoadout val = ShipLoadout.FromJObject(((ResourceAssetDef<ShipLoadoutData>)(object)hull.BasePreset).Asset.ShipLoadout.AsJObject());
		val.Sockets = new List<ShipSocketEntry>();
		HashSet<int> hashSet = new HashSet<int>();
		foreach (KeyValuePair<int, string> slot in config.Slots)
		{
			if (slot.Key < 0 || slot.Key >= hull.Sockets.Count)
			{
				continue;
			}
			Option option;
			if (hull.Sockets[slot.Key].Overlaps.Any(hashSet.Contains))
			{
				GUIDUnion shipGuid = hull.ShipGuid;
				Log.WarnOnce("overlap " + ((object)(*(GUIDUnion*)(&shipGuid))/*cast due to .constrained prefix*/).ToString() + slot.Key, "Hardpoint position " + slot.Key + " of the " + hull.Name + " overlaps a module that is already fitted, so it is left empty.");
			}
			else if (!Catalog.TryGetOption(slot.Value, out option) || (option.Kind != OptionKind.Module && option.Kind != OptionKind.Weapon) || !Catalog.CanSpawn(option.PrefabGuid))
			{
				Log.WarnOnce("unknown " + slot.Value, "The CUSTOM " + hull.Name + " has a module the game no longer has (" + slot.Value + "); that hardpoint is left empty.");
			}
			else
			{
				ShipSocketEntry val2 = SocketEntryFor(option, slot.Key);
				if (val2 != null)
				{
					val.Sockets.Add(val2);
					hashSet.Add(slot.Key);
				}
			}
		}
		foreach (ShelfInfo shelf in hull.Shelves)
		{
			config.Storage.TryGetValue(shelf.CoreIndex, out var value);
			ShipSocketEntry val3 = ((IEnumerable<ShipSocketEntry>)val.CoreSystemSockets).FirstOrDefault((Func<ShipSocketEntry, bool>)((ShipSocketEntry c) => c != null && c.SocketId == shelf.CoreIndex));
			if (val3 == null)
			{
				if (!Catalog.CanSpawn(shelf.ModuleGuid))
				{
					continue;
				}
				val3 = new ShipSocketEntry
				{
					SocketId = shelf.CoreIndex,
					Module = new GameObjectRef(new ResourceAssetRef(shelf.ModuleGuid, false)),
					BuildBox = new CarryableRef(),
					Upgrades = (ShipAssetRef[])(object)new ShipAssetRef[0]
				};
				val.CoreSystemSockets.Add(val3);
			}
			ShipAssetRef[] array = (ShipAssetRef[])(object)new ShipAssetRef[shelf.Slots.Count];
			for (int num = 0; num < array.Length; num++)
			{
				array[num] = ItemFor((value != null && num < value.Count) ? value[num] : "", hull);
			}
			val3.Upgrades = array;
		}
		val.HomunculusOverride = new CarryableRef();
		if (!string.IsNullOrEmpty(config.Homunculus) && Catalog.TryGetOption(config.Homunculus, out var option2) && Catalog.CanSpawn(option2.Guid))
		{
			val.HomunculusOverride = new CarryableRef(option2.Guid);
		}
		return val;
	}

	private static ShipSocketEntry SocketEntryFor(Option option, int socketIndex)
	{
		//IL_000f: Unknown result type (might be due to invalid IL or missing references)
		//IL_005b: 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_006b: Expected O, but got Unknown
		//IL_0066: Unknown result type (might be due to invalid IL or missing references)
		//IL_0070: Expected O, but got Unknown
		//IL_0071: Unknown result type (might be due to invalid IL or missing references)
		//IL_007b: Expected O, but got Unknown
		//IL_004c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0052: Expected O, but got Unknown
		//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
		ShipSocketEntry val = null;
		if (Settings.CopyPresetModuleContents.Value && Catalog.TryGetPresetContents(option.Guid, out var entry))
		{
			try
			{
				val = ((JToken)entry).ToObject<ShipSocketEntry>();
			}
			catch (Exception ex)
			{
				Log.Verbose("Could not copy the contents of " + option.Name + " from the game's loadouts: " + ex.Message);
			}
		}
		if (val == null)
		{
			val = new ShipSocketEntry();
		}
		val.SocketId = socketIndex;
		val.Module = new GameObjectRef(new ResourceAssetRef(option.PrefabGuid, false));
		val.BuildBox = new CarryableRef();
		if (val.Upgrades == null)
		{
			val.Upgrades = (ShipAssetRef[])(object)new ShipAssetRef[0];
		}
		val.Upgrades = val.Upgrades.Select((ShipAssetRef u) => (ShipAssetRef)((u == null || u.IsNull || !Catalog.CanSpawn(u.AssetGuid)) ? ((object)new ShipAssetRef()) : ((object)u))).ToArray();
		if (option.Kind == OptionKind.Weapon)
		{
			val.ExtraData = WeaponData(option.Guid);
		}
		return val;
	}

	private static ShipAssetRef ItemFor(string code, HullInfo hull)
	{
		//IL_0008: Unknown result type (might be due to invalid IL or missing references)
		//IL_000e: Expected O, but got Unknown
		//IL_0058: Unknown result type (might be due to invalid IL or missing references)
		//IL_005e: Expected O, but got Unknown
		//IL_0068: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b9: Expected O, but got Unknown
		//IL_00a7: 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_0081: Unknown result type (might be due to invalid IL or missing references)
		//IL_008b: Expected O, but got Unknown
		//IL_0086: Unknown result type (might be due to invalid IL or missing references)
		//IL_008b: Unknown result type (might be due to invalid IL or missing references)
		//IL_008d: Unknown result type (might be due to invalid IL or missing references)
		//IL_009d: Expected O, but got Unknown
		//IL_0074: Unknown result type (might be due to invalid IL or missing references)
		//IL_007a: Expected O, but got Unknown
		//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ca: Expected O, but got Unknown
		//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cb: Expected O, but got Unknown
		if (!string.IsNullOrEmpty(code))
		{
			if (!Catalog.TryGetOption(code, out var option))
			{
				Log.WarnOnce("unknown " + code, "The CUSTOM " + hull.Name + " has a storage item the game no longer has (" + code + "); that rack slot is left empty.");
				return new ShipAssetRef();
			}
			if (option.Kind == OptionKind.WeaponCrate)
			{
				if (Catalog.CanSpawn(option.PrefabGuid))
				{
					return new ShipAssetRef(new ResourceAssetRef(option.PrefabGuid, false))
					{
						ExtraData = WeaponData(option.Guid)
					};
				}
				return new ShipAssetRef();
			}
			if (option.Kind == OptionKind.Item && Catalog.CanSpawn(option.Guid))
			{
				return new ShipAssetRef(new ResourceAssetRef(option.Guid, false));
			}
			return new ShipAssetRef();
		}
		return new ShipAssetRef();
	}

	private static JObject WeaponData(GUIDUnion weapon)
	{
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		//IL_001b: Expected O, but got Unknown
		return JObject.FromObject((object)new CompositeWeaponDataRef(((ResourceAssetDef<CompositeWeaponData>)(object)((ResourceAssetContainer<CompositeWeaponDataContainer, CompositeWeaponData, CompositeWeaponDataDef>)(object)ResourceAssetContainer<CompositeWeaponDataContainer, CompositeWeaponData, CompositeWeaponDataDef>.Instance).GetAssetDefById(weapon, true)).Ref));
	}

	public static bool TryMapToCustom(ShipLoadoutDataDef selected, out ShipLoadoutDataDef custom)
	{
		//IL_001c: Unknown result type (might be due to invalid IL or missing references)
		//IL_003a: Unknown result type (might be due to invalid IL or missing references)
		//IL_004a: Unknown result type (might be due to invalid IL or missing references)
		custom = null;
		if (selected == null || !IsActive || IsCustom(selected))
		{
			return false;
		}
		if (!ByShip.TryGetValue(ActiveShip, out var value) || value.Hull.BasePreset == null)
		{
			return false;
		}
		if (((ResourceAssetDef<ShipLoadoutData>)(object)selected).AssetGuid != ((ResourceAssetDef<ShipLoadoutData>)(object)value.Hull.BasePreset).AssetGuid)
		{
			return false;
		}
		custom = value.Def;
		return true;
	}

	public static void ActivateAsHost(HullInfo hull)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		//IL_0016: Unknown result type (might be due to invalid IL or missing references)
		bool num = ActiveShip != hull.ShipGuid;
		ActiveShip = hull.ShipGuid;
		RemoteConfig = null;
		Refresh(hull);
		RoomSync.PublishCustom(hull, LoadoutStore.Get(hull));
		ModdingUtils.RegisterSessionMod();
		if (num)
		{
			Log.Info("The room's loadout is now your CUSTOM " + hull.Name + " (" + Describe(LoadoutStore.Get(hull)) + ").");
		}
	}

	public static void DeactivateAsHost()
	{
		//IL_0008: Unknown result type (might be due to invalid IL or missing references)
		//IL_0024: Unknown result type (might be due to invalid IL or missing references)
		//IL_0029: Unknown result type (might be due to invalid IL or missing references)
		if (IsActive)
		{
			HullInfo hull;
			string text = (Catalog.TryGetHull(ActiveShip, out hull) ? hull.Name : "ship");
			ActiveShip = GUIDUnion.Empty();
			RoomSync.ClearCustom();
			Log.Info("The room's loadout is no longer the CUSTOM " + text + ".");
		}
	}

	public static void OnEdited(HullInfo hull)
	{
		//IL_000d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0013: Unknown result type (might be due to invalid IL or missing references)
		Refresh(hull);
		if (PhotonNetwork.IsMasterClient && ActiveShip == hull.ShipGuid)
		{
			RoomSync.PublishCustom(hull, LoadoutStore.Get(hull));
		}
	}

	public static void OnMissionStarting(GameSession session, PreservedGameSession preserved)
	{
		//IL_0046: Unknown result type (might be due to invalid IL or missing references)
		//IL_0193: Unknown result type (might be due to invalid IL or missing references)
		//IL_019f: Unknown result type (might be due to invalid IL or missing references)
		//IL_019a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0063: Unknown result type (might be due to invalid IL or missing references)
		//IL_006e: Unknown result type (might be due to invalid IL or missing references)
		if (!PhotonNetwork.IsMasterClient || session == null)
		{
			return;
		}
		bool flag = false;
		HullInfo hull;
		if (preserved != null)
		{
			flag = LoadoutStore.WasCustomRun(preserved.GameSessionID);
			if (flag)
			{
				Log.Info("Continuing a saved run that was started with a CUSTOM loadout; progression stays paused.");
			}
		}
		else if (IsActive && session.ToLoadShipData != null && Catalog.TryGetHull(ActiveShip, out hull) && hull.BasePreset != null && session.ToLoadShipGuid == ((ResourceAssetDef<ShipLoadoutData>)(object)hull.BasePreset).AssetGuid)
		{
			List<string> list = RoomSync.PlayersWithoutMod(giveJoinersTime: false);
			if (list.Count > 0)
			{
				Notice.Show("This mission starts with " + LoadoutStore.PresetName(hull.BasePreset) + " instead of CUSTOM: " + string.Join(", ", list.ToArray()) + " doesn't have Ship Customizer, and nobody may earn progression in a CUSTOM run.");
			}
			else if (!EnsureRoomMarkedModded())
			{
				Notice.Show("This mission starts with " + LoadoutStore.PresetName(hull.BasePreset) + " instead of CUSTOM: the room is not marked [mod_session] yet.");
			}
			else
			{
				HullConfig config = LoadoutStore.Get(hull);
				session.ToLoadShipData = Build(hull, config);
				flag = true;
				Log.Info("Starting the mission with your CUSTOM " + hull.Name + ": " + Describe(config) + ".");
			}
		}
		if (flag && preserved != null)
		{
			List<string> list2 = RoomSync.PlayersWithoutMod(giveJoinersTime: false);
			if (list2.Count > 0)
			{
				Log.Warn("Continuing a saved CUSTOM run while " + string.Join(", ", list2.ToArray()) + " doesn't have Ship Customizer; their progression can't be paused.");
			}
		}
		ResumeShip = ((flag && preserved == null) ? ActiveShip : GUIDUnion.Empty());
		ProgressionGuard.SetHostRun(flag);
		if (flag)
		{
			ModdingUtils.RegisterSessionMod();
			RoomSync.CloseForCustomMission();
		}
	}

	public static bool EnsureRoomMarkedModded()
	{
		if (ModdingUtils.IsCurrentSessionModded())
		{
			return true;
		}
		ModdingUtils.RegisterSessionMod();
		return ModdingUtils.IsCurrentSessionModded();
	}

	public static void OnHubLoaded(HubShipManager manager)
	{
		//IL_001d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0022: Unknown result type (might be due to invalid IL or missing references)
		//IL_0023: Unknown result type (might be due to invalid IL or missing references)
		//IL_0028: Unknown result type (might be due to invalid IL or missing references)
		//IL_002d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0038: Unknown result type (might be due to invalid IL or missing references)
		//IL_0043: 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_005c: 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)
		if (PhotonNetwork.IsMasterClient && !((GUIDUnion)(ref ResumeShip)).IsNull && !((Object)(object)manager == (Object)null))
		{
			GUIDUnion resumeShip = ResumeShip;
			ResumeShip = GUIDUnion.Empty();
			if (Catalog.TryGetHull(resumeShip, out var hull) && !(manager.SelectedShipGuid != ((ResourceAssetDef<ShipLoadoutData>)(object)hull.BasePreset).AssetGuid) && TryGetDef(resumeShip, out var def))
			{
				GUIDUnion assetGuid = ((ResourceAssetDef<ShipLoadoutData>)(object)def).AssetGuid;
				manager.SelectShip(((GUIDUnion)(ref assetGuid)).AsIntArray());
			}
		}
	}

	public static void OnRoomChanged()
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		//IL_0010: Unknown result type (might be due to invalid IL or missing references)
		//IL_0015: Unknown result type (might be due to invalid IL or missing references)
		ActiveShip = GUIDUnion.Empty();
		RemoteConfig = null;
		ResumeShip = GUIDUnion.Empty();
		RefreshAll();
	}

	public static string Describe(HullConfig config)
	{
		List<string> list = new List<string>();
		foreach (string value in config.Slots.Values)
		{
			list.Add(Catalog.TryGetOption(value, out var option) ? option.Name : value);
		}
		return ((list.Count == 0) ? "no modules" : string.Join(", ", list.ToArray())) + "; " + config.StoredCount + " items in storage";
	}
}
internal sealed class CustomizerRuntime : MonoBehaviour
{
	private const float PollSeconds = 0.25f;

	private float _nextPoll;

	public static bool GameReady;

	private void Start()
	{
		if (!CustomLoadouts.Available)
		{
			return;
		}
		try
		{
			ModdingUtils.RegisterSessionMod();
			Log.Info("Rooms you host are marked [mod_session] (the game's tag for mods that change the session), as Void Crew's modding guidelines ask.");
		}
		catch (Exception e)
		{
			Log.Failure("Marking hosted rooms as [mod_session]", e);
		}
	}

	private void Update()
	{
		if (!GameReady || Time.unscaledTime < _nextPoll)
		{
			return;
		}
		_nextPoll = Time.unscaledTime + 0.25f;
		try
		{
			RoomSync.Poll();
		}
		catch (Exception e)
		{
			Log.Failure("Reading the room's ship customizer info", e);
		}
		try
		{
			ProgressionGuard.Poll();
		}
		catch (Exception e2)
		{
			Log.Failure("Updating the progression pause", e2);
		}
	}
}
internal static class GameDataReport
{
	private const int MaxLayoutLines = 600;

	public unsafe static void Write(VisualElement terminalRoot)
	{
		//IL_0093: Unknown result type (might be due to invalid IL or missing references)
		//IL_0098: Unknown result type (might be due to invalid IL or missing references)
		//IL_009a: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
		//IL_015c: Unknown result type (might be due to invalid IL or missing references)
		//IL_016b: Unknown result type (might be due to invalid IL or missing references)
		//IL_022a: Unknown result type (might be due to invalid IL or missing references)
		string text = Path.Combine(LoadoutStore.Folder, "game-data.txt");
		try
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine("Void Crew Ship Customizer 0.3.0 game-data report");
			stringBuilder.AppendLine("Written " + DateTime.Now.ToString("yyyy-MM-dd HH:mm") + " | game " + Application.version + " | Unity " + Application.unityVersion);
			stringBuilder.AppendLine();
			stringBuilder.AppendLine("== SHIPS ==");
			foreach (GUIDUnion item in Catalog.ShipGuids())
			{
				if (!Catalog.TryGetHull(item, out var hull))
				{
					GUIDUnion val = item;
					stringBuilder.AppendLine("(could not read ship " + ((object)(*(GUIDUnion*)(&val))/*cast due to .constrained prefix*/).ToString() + ")");
				}
				else
				{
					WriteHull(stringBuilder, hull);
				}
			}
			stringBuilder.AppendLine("== WEAPONS AND MODULES (" + Catalog.SlotOptions.Count + ") ==");
			foreach (Option slotOption in Catalog.SlotOptions)
			{
				stringBuilder.AppendLine(string.Format("  {0,-9} {1,-4} {2,-10} {3}  {4}{5}", slotOption.Category, slotOption.HasSize ? Catalog.SizeName(slotOption.Size) : "?", Ui.RarityName(slotOption.Rarity), slotOption.Name, slotOption.Code, slotOption.SingleInstance ? "  (one per ship)" : ""));
			}
			stringBuilder.AppendLine();
			stringBuilder.AppendLine("== STORAGE ITEMS (" + Catalog.ItemOptions.Count + ") ==");
			foreach (Option itemOption in Catalog.ItemOptions)
			{
				stringBuilder.AppendLine($"  {itemOption.Category,-14} {Ui.RarityName(itemOption.Rarity),-10} {itemOption.Name}  {itemOption.Code}");
			}
			stringBuilder.AppendLine();
			stringBuilder.AppendLine("== HOMUNCULUS VARIANTS IN GAME LOADOUTS ==");
			foreach (Option homunculusOption in Catalog.HomunculusOptions)
			{
				stringBuilder.AppendLine("  " + homunculusOption.Name + "  " + homunculusOption.Code);
			}
			stringBuilder.AppendLine();
			stringBuilder.AppendLine("== TERMINAL SCREEN LAYOUT ==");
			if (terminalRoot != null)
			{
				int lines = 0;
				WriteTree(stringBuilder, terminalRoot, 0, ref lines);
			}
			else
			{
				stringBuilder.AppendLine("  (terminal not available)");
			}
			Directory.CreateDirectory(LoadoutStore.Folder);
			File.WriteAllText(text, stringBuilder.ToString());
			Log.Info("Wrote the game-data report to " + text + ".");
		}
		catch (Exception e)
		{
			Log.Failure("Writing the game-data report to " + text, e);
		}
	}

	private unsafe static void WriteHull(StringBuilder text, HullInfo hull)
	{
		//IL_000d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0012: Unknown result type (might be due to invalid IL or missing references)
		//IL_005d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0062: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ae: Unknown result type (might be due to invalid IL or missing references)
		//IL_02c9: Unknown result type (might be due to invalid IL or missing references)
		//IL_0615: Unknown result type (might be due to invalid IL or missing references)
		//IL_056a: Unknown result type (might be due to invalid IL or missing references)
		//IL_056f: Unknown result type (might be due to invalid IL or missing references)
		string name = hull.Name;
		GUIDUnion shipGuid = hull.ShipGuid;
		text.AppendLine(name + "  [" + ((object)(*(GUIDUnion*)(&shipGuid))/*cast due to .constrained prefix*/).ToString() + "]");
		text.AppendLine("  CUSTOM is built on, and shown to other players as: " + LoadoutStore.PresetName(hull.BasePreset) + "  [" + ((object)((ResourceAssetDef<ShipLoadoutData>)(object)hull.BasePreset).AssetGuid/*cast due to .constrained prefix*/).ToString() + "]");
		text.AppendLine("  Loadouts (the terminal's list first, lowest rank first; then challenge and other loadouts):");
		foreach (ShipLoadoutDataDef preset in hull.Presets)
		{
			text.AppendLine("    " + (hull.RegularPresets.Contains(preset) ? "" : "(not listed) ") + LoadoutStore.PresetName(preset) + "  [" + ((object)((ResourceAssetDef<ShipLoadoutData>)(object)preset).AssetGuid/*cast due to .constrained prefix*/).ToString() + "]");
		}
		text.AppendLine("  Hardpoint positions (" + hull.Sockets.Count + " in " + hull.GroupCount + " hardpoints):");
		foreach (SocketInfo socket in hull.Sockets)
		{
			text.AppendLine(string.Format("    #{0,-2} {1}  hardpoint {2}  overlaps [{3}]  cells {4}", socket.Index, Catalog.SizeName(socket.Size), socket.Group + 1, string.Join(",", socket.Overlaps.Select((int i) => i.ToString()).ToArray()), string.Join(" ", socket.Cells.Select((Vector3 c) => "(" + c.x.ToString("0.#") + "," + c.y.ToString("0.#") + "," + c.z.ToString("0.#") + ")").ToArray())));
		}
		text.AppendLine("  Storage racks:");
		foreach (ShelfInfo shelf in hull.Shelves)
		{
			CarryablesSocket val = shelf.Slots.FirstOrDefault();
			text.AppendLine(string.Format("    {0}: core system #{1} [{2}] {3} slots; accepts {4}; rejects {5}", shelf.Name, shelf.CoreIndex, shelf.ModuleGuid, shelf.Slots.Count, ((Object)(object)val != (Object)null) ? Describe(val.accepted) : "?", ((Object)(object)val != (Object)null && val.rejectedList != null && val.rejectedList.Length != 0) ? string.Join(", ", val.rejectedList.Select(Describe).ToArray()) : "nothing"));
		}
		text.AppendLine("  Game loadouts' contents:");
		foreach (ShipLoadoutDataDef preset2 in hull.Presets)
		{
			try
			{
				ShipLoadout shipLoadout = ((ResourceAssetDef<ShipLoadoutData>)(object)preset2).Asset.ShipLoadout;
				text.AppendLine("    " + LoadoutStore.PresetName(preset2) + ":");
				foreach (ShipSocketEntry socket2 in shipLoadout.Sockets)
				{
					if (socket2 != null && !((ResourceAssetRef)(object)socket2.Module == (ResourceAssetRef)null) && !((ResourceAssetRef)socket2.Module).IsNull)
					{
						Option option = Catalog.OptionFor(socket2);
						int num = ((socket2.Upgrades != null) ? socket2.Upgrades.Count((ShipAssetRef u) => u != null && !u.IsNull) : 0);
						text.AppendLine(string.Format("      hardpoint #{0}: {1} ({2}){3}", socket2.SocketId, (option != null) ? option.Name : "?", (option != null) ? option.Code : ((object)Unsafe.As<GUIDUnion, GUIDUnion>(ref ((ResourceAssetRef)socket2.Module).AssetGuid)/*cast due to .constrained prefix*/).ToString(), (num > 0) ? (", stocked with " + string.Join(", ", (from u in socket2.Upgrades
							where u != null && !u.IsNull
							select NameOfItem(Catalog.CodeFor(u))).ToArray())) : ""));
						if ((ResourceAssetRef)(object)socket2.BuildBox != (ResourceAssetRef)null && !((ResourceAssetRef)socket2.BuildBox).IsNull)
						{
							string text2 = socket2.SocketId.ToString();
							shipGuid = ((ResourceAssetRef)socket2.BuildBox).AssetGuid;
							text.AppendLine("      hardpoint #" + text2 + " also has a crate: " + ((object)(*(GUIDUnion*)(&shipGuid))/*cast due to .constrained prefix*/).ToString());
						}
					}
				}
				foreach (ShipSocketEntry coreSystemSocket in shipLoadout.CoreSystemSockets)
				{
					if (coreSystemSocket.Upgrades != null && !coreSystemSocket.Upgrades.All((ShipAssetRef u) => u == null || u.IsNull))
					{
						text.AppendLine(string.Format("      core system #{0} ({1}): {2}", coreSystemSocket.SocketId, Catalog.HeaderOf(Catalog.ContextOf(((ResourceAssetRef)coreSystemSocket.Module).AssetGuid), ((object)Unsafe.As<GUIDUnion, GUIDUnion>(ref ((ResourceAssetRef)coreSystemSocket.Module).AssetGuid)/*cast due to .constrained prefix*/).ToString()), string.Join(", ", coreSystemSocket.Upgrades.Select((ShipAssetRef u) => (u != null && !u.IsNull) ? NameOfItem(Catalog.CodeFor(u)) : "-").ToArray())));
					}
				}
				if ((ResourceAssetRef)(object)shipLoadout.HomunculusOverride != (ResourceAssetRef)null && !((ResourceAssetRef)shipLoadout.HomunculusOverride).IsNull)
				{
					text.AppendLine("      homunculus: " + NameOfItem("i:" + ((GUIDUnion)(ref ((ResourceAssetRef)shipLoadout.HomunculusOverride).AssetGuid)).AsHex()));
				}
				if (shipLoadout.AdditionalAssets != null && shipLoadout.AdditionalAssets.Count > 0)
				{
					text.AppendLine("      extra loose items: " + shipLoadout.AdditionalAssets.Count);
				}
			}
			catch (Exception ex)
			{
				text.AppendLine("      (could not read: " + ex.Message + ")");
			}
		}
		text.AppendLine();
	}

	private static string NameOfItem(string code)
	{
		if (!Catalog.TryGetOption(code, out var option))
		{
			return code;
		}
		return option.Name;
	}

	private static string Describe(CsObjectReference reference)
	{
		if (reference == null)
		{
			return "?";
		}
		try
		{
			Traverse val = Traverse.Create((object)reference);
			object value = val.Field("compareMethod").GetValue();
			string text = "";
			if (value != null && value.ToString() == "Type")
			{
				object value2 = val.Field("type").Property("Type", (object[])null).GetValue();
				text = " " + ((value2 != null) ? value2.ToString() : "?");
			}
			else if (value != null && value.ToString() == "Prefab")
			{
				object value3 = val.Field("orbitObjectRef").GetValue();
				ResourceAssetRef val2 = (ResourceAssetRef)((value3 is ResourceAssetRef) ? value3 : null);
				text = " " + ((val2 != (ResourceAssetRef)null) ? NameOfItem("i:" + ((GUIDUnion)(ref val2.AssetGuid)).AsHex()) : "?");
			}
			return ((value != null) ? value.ToString() : "?") + text;
		}
		catch (Exception ex)
		{
			return "? (" + ex.Message + ")";
		}
	}

	private static void WriteTree(StringBuilder text, VisualElement element, int depth, ref int lines)
	{
		//IL_0029: Unknown result type (might be due to invalid IL or missing references)
		//IL_002e: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c6: Invalid comparison between Unknown and I4
		if (lines >= 600 || element == null || (element.name != null && element.name.StartsWith("ShipCustomizer")))
		{
			return;
		}
		Rect worldBound = element.worldBound;
		string text2 = string.Join(" ", element.GetClasses().ToArray());
		text.AppendLine(string.Format("{0}{1} \"{2}\" [{3}] x{4:0} y{5:0} w{6:0} h{7:0}{8}", new string(' ', 2 + depth * 2), ((object)element).GetType().Name, element.name, text2, ((Rect)(ref worldBound)).x, ((Rect)(ref worldBound)).y, ((Rect)(ref worldBound)).width, ((Rect)(ref worldBound)).height, ((int)element.resolvedStyle.display == 1) ? " hidden" : ""));
		lines++;
		if (depth >= 12)
		{
			return;
		}
		foreach (VisualElement item in element.Children())
		{
			WriteTree(text, item, depth + 1, ref lines);
		}
	}
}
internal static class Hardpoints
{
	public const float CellSize = 4f;

	public static Vector3[] CellsFor(BuildSize size, Vector3 position, Vector3 right, Vector3 forward)
	{
		//IL_0000: 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_0052: Unknown result type (might be due to invalid IL or missing references)
		//IL_005e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0063: 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)
		//IL_0075: Unknown result type (might be due to invalid IL or missing references)
		//IL_007a: Unknown result type (might be due to invalid IL or missing references)
		//IL_007f: Unknown result type (might be due to invalid IL or missing references)
		int num = default(int);
		int num2 = default(int);
		int num3 = default(int);
		BuildSizeExtensions.GetDimensions(size, ref num, ref num2, ref num3);
		float num4 = (float)(num / 2) - 0.5f * (float)(1 - num % 2);
		float num5 = (float)(num3 / 2) - 0.5f * (float)(1 - num3 % 2);
		Vector3[] array = (Vector3[])(object)new Vector3[num * num3];
		int num6 = 0;
		for (int i = 0; i < num3; i++)
		{
			for (int j = 0; j < num; j++)
			{
				array[num6++] = position + right * (((float)j - num4) * 4f) + forward * (((float)i - num5) * 4f);
			}
		}
		return array;
	}

	public static int LinkOverlaps(List<SocketInfo> sockets)
	{
		foreach (SocketInfo socket in sockets)
		{
			socket.Overlaps.Clear();
			socket.Group = -1;
		}
		for (int i = 0; i < sockets.Count; i++)
		{
			for (int j = i + 1; j < sockets.Count; j++)
			{
				if (CellsOverlap(sockets[i], sockets[j]))
				{
					sockets[i].Overlaps.Add(j);
					sockets[j].Overlaps.Add(i);
				}
			}
		}
		int num = 0;
		foreach (SocketInfo socket2 in sockets)
		{
			if (socket2.Group >= 0)
			{
				continue;
			}
			Stack<SocketInfo> stack = new Stack<SocketInfo>();
			socket2.Group = num;
			stack.Push(socket2);
			while (stack.Count > 0)
			{
				foreach (int overlap in stack.Pop().Overlaps)
				{
					if (sockets[overlap].Group < 0)
					{
						sockets[overlap].Group = num;
						stack.Push(sockets[overlap]);
					}
				}
			}
			num++;
		}
		return num;
	}

	private static bool CellsOverlap(SocketInfo a, SocketInfo b)
	{
		//IL_000d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0012: Unknown result type (might be due to invalid IL or missing references)
		//IL_0022: Unknown result type (might be due to invalid IL or missing references)
		//IL_0027: Unknown result type (might be due to invalid IL or missing references)
		//IL_0029: Unknown result type (might be due to invalid IL or missing references)
		//IL_002a: Unknown result type (might be due to invalid IL or missing references)
		//IL_002c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0031: Unknown result type (might be due to invalid IL or missing references)
		Vector3[] cells = a.Cells;
		foreach (Vector3 val in cells)
		{
			Vector3[] cells2 = b.Cells;
			foreach (Vector3 val2 in cells2)
			{
				Vector3 val3 = val - val2;
				if (((Vector3)(ref val3)).sqrMagnitude < 4f)
				{
					return true;
				}
			}
		}
		return false;
	}

	public static HashSet<int> OfferedEmpty(List<SocketInfo> sockets, ICollection<int> fitted, int drawnCount)
	{
		HashSet<int> hashSet = new HashSet<int>();
		foreach (SocketInfo item in from s in sockets
			orderby (int)s.Size descending, s.Index
			select s)
		{
			if (item.Index < drawnCount && !fitted.Contains(item.Index) && !item.Overlaps.Any(fitted.Contains) && !item.Overlaps.Any(hashSet.Contains))
			{
				hashSet.Add(item.Index);
			}
		}
		return hashSet;
	}

	public static SocketInfo TargetFor(BuildSize size, SocketInfo clicked, List<SocketInfo> sockets, ICollection<int> fitted, int drawnCount)
	{
		//IL_000e: Unknown result type (might be due to invalid IL or missing references)
		//IL_000f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0024: Unknown result type (might be due to invalid IL or missing references)
		//IL_002a: Unknown result type (might be due to invalid IL or missing references)
		if (clicked.Size == size)
		{
			return clicked;
		}
		return (from i in clicked.Overlaps
			select sockets[i] into s
			where s.Size == size && s.Index < drawnCount
			orderby fitted.Count((int k) => k == s.Index || s.Overlaps.Contains(k)), s.Index
			select s).FirstOrDefault();
	}

	public static List<int> Displaced(SocketInfo target, ICollection<int> fitted)
	{
		return fitted.Where((int k) => k == target.Index || target.Overlaps.Contains(k)).ToList();
	}
}
internal sealed class HullConfig
{
	public SortedDictionary<int, string> Slots = new SortedDictionary<int, string>();

	public SortedDictionary<int, List<string>> Storage = new SortedDictionary<int, List<string>>();

	public string Homunculus = "";

	public string CopiedFrom = "";

	public int FittedCount => Slots.Count;

	public int StoredCount => Storage.Values.Sum((List<string> rack) => rack.Count((string code) => !string.IsNullOrEmpty(code)));

	public HullConfig Clone()
	{
		return JsonConvert.DeserializeObject<HullConfig>(JsonConvert.SerializeObject((object)this));
	}
}
internal static class LoadoutStore
{
	private sealed class StoreFile
	{
		public int Version = 1;

		public Dictionary<string, HullConfig> Ships = new Dictionary<string, HullConfig>();

		public List<string> CustomRuns = new List<string>();
	}

	private const int MaxRememberedRuns = 50;

	private static StoreFile _file;

	public static string Folder => Path.Combine(Application.persistentDataPath, "ShipCustomizer");

	private static string FilePath => Path.Combine(Folder, "custom-loadouts.json");

	private static StoreFile File
	{
		get
		{
			if (_file != null)
			{
				return _file;
			}
			try
			{
				if (System.IO.File.Exists(FilePath))
				{
					_file = JsonConvert.DeserializeObject<StoreFile>(System.IO.File.ReadAllText(FilePath));
					Log.Info("Loaded your Custom loadouts from " + FilePath + ".");
				}
			}
			catch (Exception ex)
			{
				string text = FilePath + ".broken";
				Log.Error("Could not read " + FilePath + ", so the Custom loadouts start fresh. The old file is kept as " + text + ". Details: " + ex.Message);
				try
				{
					System.IO.File.Copy(FilePath, text, overwrite: true);
				}
				catch
				{
				}
			}
			if (_file == null)
			{
				_file = new StoreFile();
			}
			if (_file.Ships == null)
			{
				_file.Ships = new Dictionary<string, HullConfig>();
			}
			if (_file.CustomRuns == null)
			{
				_file.CustomRuns = new List<string>();
			}
			return _file;
		}
	}

	public static HullConfig Get(HullInfo hull)
	{
		if (File.Ships.TryGetValue(((GUIDUnion)(ref hull.ShipGuid)).AsHex(), out var value) && value != null)
		{
			if (value.Slots == null)
			{
				value.Slots = new SortedDictionary<int, string>();
			}
			if (value.Storage == null)
			{
				value.Storage = new SortedDictionary<int, List<string>>();
			}
			if (value.Homunculus == null)
			{
				value.Homunculus = "";
			}
			return value;
		}
		value = FromGameLoadout(hull, hull.BasePreset);
		File.Ships[((GUIDUnion)(ref hull.ShipGuid)).AsHex()] = value;
		return value;
	}

	public static void Set(HullInfo hull, HullConfig config)
	{
		File.Ships[((GUIDUnion)(ref hull.ShipGuid)).AsHex()] = config;
		Save();
	}

	public static void Save()
	{
		try
		{
			Directory.CreateDirectory(Folder);
			string text = FilePath + ".tmp";
			System.IO.File.WriteAllText(text, JsonConvert.SerializeObject((object)File, (Formatting)1));
			if (System.IO.File.Exists(FilePath))
			{
				System.IO.File.Delete(FilePath);
			}
			System.IO.File.Move(text, FilePath);
		}
		catch (Exception e)
		{
			Log.Failure("Saving the Custom loadouts to " + FilePath, e);
		}
	}

	public static HullConfig FromGameLoadout(HullInfo hull, ShipLoadoutDataDef preset)
	{
		HullConfig hullConfig = new HullConfig
		{
			CopiedFrom = PresetName(preset)
		};
		ShipLoadout shipLoadout = ((ResourceAssetDef<ShipLoadoutData>)(object)preset).Asset.ShipLoadout;
		foreach (ShipSocketEntry socket in shipLoadout.Sockets)
		{
			if (socket != null && !((ResourceAssetRef)(object)socket.Module == (ResourceAssetRef)null) && !((ResourceAssetRef)socket.Module).IsNull && socket.SocketId >= 0 && socket.SocketId < hull.Sockets.Count)
			{
				Option option = Catalog.OptionFor(socket);
				if (option != null)
				{
					hullConfig.Slots[socket.SocketId] = option.Code;
					continue;
				}
				Log.Warn("The " + hullConfig.CopiedFrom + " loadout has a module the customizer doesn't know (" + ((object)socket)?.ToString() + "); it is left out of the copy.");
			}
		}
		foreach (ShelfInfo shelf in hull.Shelves)
		{
			ShipSocketEntry val = ((IEnumerable<ShipSocketEntry>)shipLoadout.CoreSystemSockets).FirstOrDefault((Func<ShipSocketEntry, bool>)((ShipSocketEntry c) => c != null && c.SocketId == shelf.CoreIndex));
			List<string> list = new List<string>();
			for (int num = 0; num < shelf.Slots.Count; num++)
			{
				list.Add((val != null && val.Upgrades != null && num < val.Upgrades.Length) ? Catalog.CodeFor(val.Upgrades[num]) : "");
			}
			hullConfig.Storage[shelf.CoreIndex] = list;
		}
		CarryableRef homunculusOverride = shipLoadout.HomunculusOverride;
		hullConfig.Homunculus = (((ResourceAssetRef)(object)homunculusOverride != (ResourceAssetRef)null && !((ResourceAssetRef)homunculusOverride).IsNull) ? ("i:" + ((GUIDUnion)(ref ((ResourceAssetRef)homunculusOverride).AssetGuid)).AsHex()) : "");
		return hullConfig;
	}

	public static string PresetName(ShipLoadoutDataDef preset)
	{
		try
		{
			string text = (((Object)(object)preset.LoadoutContextInfo != (Object)null) ? ((ContextInfo)preset.LoadoutContextInfo).HeaderText : null);
			if (!string.IsNullOrEmpty(text))
			{
				return text;
			}
		}
		catch
		{
		}
		try
		{
			return ((Object)(object)((ResourceAssetDef<ShipLoadoutData>)(object)preset).Asset != (Object)null) ? ((Object)((ResourceAssetDef<ShipLoadoutData>)(object)preset).Asset).name : "loadout";
		}
		catch
		{
			return "loadout";
		}
	}

	public static void RememberCustomRun(string gameSessionId)
	{
		if (!string.IsNullOrEmpty(gameSessionId) && !File.CustomRuns.Contains(gameSessionId))
		{
			File.CustomRuns.Add(gameSessionId);
			while (File.CustomRuns.Count > 50)
			{
				File.CustomRuns.RemoveAt(0);
			}
			Save();
		}
	}

	public static bool WasCustomRun(string gameSessionId)
	{
		if (!string.IsNullOrEmpty(gameSessionId))
		{
			return File.CustomRuns.Contains(gameSessionId);
		}
		return false;
	}
}
internal static class Log
{
	private static ManualLogSource _source;

	private static readonly HashSet<string> OnceKeys = new HashSet<string>();

	private static readonly Dictionary<string, int> FailureCounts = new Dictionary<string, int>();

	public static void Init(ManualLogSource source)
	{
		_source = source;
	}

	public static void Info(string message)
	{
		_source.LogInfo((object)message);
	}

	public static void Warn(string message)
	{
		_source.LogWarning((object)message);
	}

	public static void Error(string message)
	{
		_source.LogError((object)message);
	}

	public static void Verbose(string message)
	{
		if (Settings.VerboseLogging != null && Settings.VerboseLogging.Value)
		{
			_source.LogInfo((object)("[verbose] " + message));
		}
	}

	public static void InfoOnce(string key, string message)
	{
		if (OnceKeys.Add(key))
		{
			Info(message);
		}
	}

	public static void WarnOnce(string key, string message)
	{
		if (OnceKeys.Add(key))
		{
			Warn(message);
		}
	}

	public static void Failure(string operation, Exception e)
	{
		FailureCounts.TryGetValue(operation, out var value);
		value = (FailureCounts[operation] = value + 1);
		switch (value)
		{
		case 1:
			Error(operation + " failed: " + e);
			break;
		default:
			if (value % 1000 != 0)
			{
				break;
			}
			goto case 10;
		case 10:
			Error(operation + " has now failed " + value + " times. Latest: " + e.GetType().Name + ": " + e.Message);
			break;
		}
	}
}
internal static class Notice
{
	private const string Sender = "Ship Customizer";

	public static void Show(string message)
	{
		//IL_001e: Unknown result type (might be due to invalid IL or missing references)
		Log.Info(message);
		try
		{
			if ((Object)(object)TextChat.Instance != (Object)null)
			{
				TextChat.Instance.AddLog(new LogEntry("Ship Customizer", message));
			}
		}
		catch (Exception ex)
		{
			Log.Verbose("Could not show a notice in the chat: " + ex.Message);
		}
		TerminalUi.ShowToast(message);
	}
}
internal static class Patches
{
	private static Harmony _harmony;

	private static FieldRef<HubShipManager, ShipLoadoutDataDef> _currentShip;

	private static FieldInfo _selectionChanged;

	public static void Apply(Harmony harmony)
	{
		_harmony = harmony;
		bool num = Patch("HubShipManager", "SelectShip", "SelectShipPrefix", null) & Patch("HubShipManager", "get_CurrentShipSelected", null, "CurrentShipSelectedPostfix") & Patch("CG.GameLoopStateMachine.GameStates.GSIngame", "TransitionTo", "TransitionToPrefix", null) & Patch("UI.ShipLoadoutTerminal.LoadoutList", "SetSource", "SetSourcePrefix", null) & Patch("UI.ShipLoadoutTerminal.LoadoutList", "Sort", "SortPrefix", null) & Patch("UI.ShipLoadoutTerminal.LoadoutList", "BindItem", "BindItemPrefix", null) & Patch("UI.ShipLoadoutTerminal.ModuleLayout", "Show", null, "ModuleLayoutShowPostfix") & Patch("UI.ShipLoadoutTerminal.ShipLoadoutTerminal", "Display", null, "DisplayPostfix") & Patch("CG.Profile.PlayerRewardsCollection", "AddItem", "BlockRewardsPrefix", null);
		_currentShip = AccessTools.FieldRefAccess<HubShipManager, ShipLoadoutDataDef>("_currentShipSelected");
		_selectionChanged = AccessTools.Field(typeof(HubShipManager), "ShipSelectionChanged");
		if (_currentShip != null && _selectionChanged != null)
		{
			Patch("HubShipManager", "NotifyShipSelected", "NotifyShipSelectedPrefix", null);
		}
		else
		{
			Log.Warn("Could not find HubShipManager's selection fields; the terminal will show the stand-in loadout as chosen instead of CUSTOM.");
		}
		Patch("HubShipManager", "Start", null, "HubStartPostfix");
		Patch("Gameplay.Hub.HubQuestManager", "SelectPreservedSessionQuest", "SelectSavedRunPrefix", null);
		Patch("UI.ShipLoadoutTerminal.ShipLoadoutTerminal", "OnDestroy", "TerminalDestroyPrefix", null);
		Patch("UI.ShipLoadoutTerminal.ShipLoadoutTerminal", "OnLeaveUI", null, "TerminalLeavePostfix");
		Patch("CG.Cloud.CloudRewardsProfileSync", "AddItem", "BlockRewardsPrefix", null);
		Patch("CG.Profile.PlayerAchievements", "SetAchievementComplete", "BlockAchievementsPrefix", null);
		Patch("SteamPlayerAchievementsSync", "SetAchievementComplete", "BlockAchievementsPrefix", null);
		Patch("CG.Cloud.CloudPlayerAchievementsSync", "SetAchievementComplete", "BlockAchievementsPrefix", null);
		Patch("CG.Profile.PlayerProfileData", "AddQuestProgress", "BlockQuestProgressPrefix", null);
		Patch("CG.Cloud.CloudPlayerProfileDataSync", "AddQuestProgress", "BlockQuestProgressPrefix", null);
		Patch("CG.Profile.PhotonPlayerDataSync", "AddQuestProgress", "BlockQuestProgressPrefix", null);
		CustomLoadouts.Available = num;
		if (num)
		{
			Log.Info("All game patches are in; the CUSTOM loadout is available at the ship loadout terminal.");
		}
		else
		{
			Log.Error("Some patches the CUSTOM loadout needs could not be applied (see above), so it is turned off. The game itself is unaffected.");
		}
	}

	private static bool Patch(string typeName, string methodName, string prefix, string postfix)
	{
		//IL_0061: Unknown result type (might be due to invalid IL or missing references)
		//IL_006b: Expected O, but got Unknown
		//IL_0082: Unknown result type (might be due to invalid IL or missing references)
		//IL_008c: Expected O, but got Unknown
		try
		{
			Type type = AccessTools.TypeByName(typeName);
			if (type == null)
			{
				throw new MissingMemberException("type " + typeName + " not found");
			}
			MethodBase methodBase = AccessTools.Method(type, methodName, (Type[])null, (Type[])null);
			if (methodBase == null)
			{
				throw new MissingMethodException(typeName, methodName);
			}
			PatchProcessor val = _harmony.CreateProcessor(methodBase);
			if (prefix != null)
			{
				val.AddPrefix(new HarmonyMethod(AccessTools.Method(typeof(Patches), prefix, (Type[])null, (Type[])null)));
			}
			if (postfix != null)
			{
				val.AddPostfix(new HarmonyMethod(AccessTools.Method(typeof(Patches), postfix, (Type[])null, (Type[])null)));
			}
			val.Patch();
			Log.Verbose("Patched " + typeName + "." + methodName + ".");
			return true;
		}
		catch (Exception ex)
		{
			Log.Error("Could not patch " + typeName + "." + methodName + " (the game may have changed): " + ex.Message);
			return false;
		}
	}

	private static bool SelectShipPrefix(HubShipManager __instance, ref int[] guid)
	{
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_0093: Unknown result type (might be due to invalid IL or missing references)
		//IL_0098: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			if (CustomLoadouts.TryGetHullOfCustomGuid(new GUIDUnion(guid), out var hull))
			{
				if (!CustomLoadouts.Available)
				{
					return false;
				}
				if (!PhotonNetwork.IsMasterClient)
				{
					TerminalUi.Toast("Only the host can choose the CUSTOM loadout.");
					return false;
				}
				List<string> list = RoomSync.PlayersWithoutMod(giveJoinersTime: false);
				if (list.Count > 0)
				{
					Notice.Show("Everyone in the room needs Ship Customizer for CUSTOM. Not yet: " + string.Join(", ", list.ToArray()) + ". (Just joined? Try again in a few seconds.)");
					return false;
				}
				if (!CustomLoadouts.EnsureRoomMarkedModded())
				{
					TerminalUi.Toast("Marking the room [mod_session] first; choose CUSTOM again in a moment.");
					return false;
				}
				CustomLoadouts.ActivateAsHost(hull);
				GUIDUnion assetGuid = ((ResourceAssetDef<ShipLoadoutData>)(object)hull.BasePreset).AssetGuid;
				guid = ((GUIDUnion)(ref assetGuid)).AsIntArray();
				return true;
			}
			if (PhotonNetwork.IsMasterClient)
			{
				CustomLoadouts.DeactivateAsHost();
			}
		}
		catch (Exception e)
		{
			Log.Failure("Choosing a ship loadout", e);
		}
		return true;
	}

	private static bool NotifyShipSelectedPrefix(HubShipManager __instance, int[] guid)
	{
		//IL_0008: Unknown result type (might be due to invalid IL or missing references)
		//IL_001f: 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_0018: Unknown result type (might be due to invalid IL or missing references)
		//IL_001d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0033: Unknown result type (might be due to invalid IL or missing references)
		ShipLoadoutDataDef obj;
		try
		{
			GUIDUnion assetGuid = default(GUIDUnion);
			((GUIDUnion)(ref assetGuid))..ctor(guid);
			if (CustomLoadouts.TryGetHullOfCustomGuid(assetGuid, out var hull))
			{
				assetGuid = ((ResourceAssetDef<ShipLoadoutData>)(object)hull.BasePreset).AssetGuid;
			}
			__instance.SelectedShipGuid = assetGuid;
			ShipLoadoutDataDef val = (((GUIDUnion)(ref assetGuid)).IsNull ? null : ((ResourceAssetContainer<ShipLoadoutDataContainer, ShipLoadoutData, ShipLoadoutDataDef>)(object)ResourceAssetContainer<ShipLoadoutDataContainer, ShipLoadoutData, ShipLoadoutDataDef>.Instance).GetAssetDefById(assetGuid, true));
			_currentShip.Invoke(__instance) = val;
			obj = (CustomLoadouts.TryMapToCustom(val, out var custom) ? custom : val);
		}
		catch (Exception e)
		{
			Log.Failure("Showing the chosen ship loadout", e);
			return true;
		}
		if (_selectionChanged.GetValue(__instance) is Action<ShipLoadoutDataDef> action)
		{
			action(obj);
		}
		return false;
	}

	private static void CurrentShipSelectedPostfix(ref ShipLoadoutDataDef __result)
	{
		try
		{
			if (__result != null && CustomLoadouts.TryMapToCustom(__result, out var custom))
			{
				__result = custom;
			}
		}
		catch (Exception e)
		{
			Log.Failure("Reading the chosen ship loadout", e);
		}
	}

	private static void TransitionToPrefix(GameSession gameSession, PreservedGameSession preservedGameSession)
	{
		try
		{
			CustomizerRuntime.GameReady = true;
			CustomLoadouts.OnMissionStarting(gameSession, preservedGameSession);
		}
		catch (Exception e)
		{
			Log.Failure("Swapping in the CUSTOM ship at mission start (the mission starts with the stand-in loadout instead)", e);
		}
	}

	priv