Decompiled source of ProfileSwitcher v1.0.1

BepInEx/plugins/ProfileSwitcher.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("Sparroh")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.1.0")]
[assembly: AssemblyInformationalVersion("1.0.1")]
[assembly: AssemblyProduct("ProfileSwitcher")]
[assembly: AssemblyTitle("ProfileSwitcher")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.1.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
[BepInPlugin("sparroh.profileswitcher", "ProfileSwitcher", "1.0.1")]
[MycoMod(/*Could not decode attribute arguments.*/)]
public class ProfileSwitcherPlugin : BaseUnityPlugin
{
	public const string PluginGUID = "sparroh.profileswitcher";

	public const string PluginName = "ProfileSwitcher";

	public const string PluginVersion = "1.0.1";

	internal static ManualLogSource Log;

	private Harmony _harmony;

	private void Awake()
	{
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		//IL_001b: Expected O, but got Unknown
		Log = ((BaseUnityPlugin)this).Logger;
		_harmony = new Harmony("sparroh.profileswitcher");
		_harmony.PatchAll(typeof(StartMenuProfilePatches));
		Log.LogInfo((object)"ProfileSwitcher v1.0.1 loaded — profiles can be swapped from the main menu without restarting.");
	}

	private void OnDestroy()
	{
		Harmony harmony = _harmony;
		if (harmony != null)
		{
			harmony.UnpatchSelf();
		}
	}
}
internal static class ProfileReloader
{
	private static readonly FieldInfo InstanceField = AccessTools.Field(typeof(PlayerData), "_instance");

	private static readonly FieldInfo SavePathField = AccessTools.Field(typeof(PlayerData), "savePath");

	private static readonly FieldInfo BinaryFormatterField = AccessTools.Field(typeof(PlayerData), "binaryFormatter");

	private static readonly FieldInfo ProfileIndexField = AccessTools.Field(typeof(ProfileConfig), "profileIndex");

	private static readonly MethodInfo TryLoadSaveMethod = AccessTools.Method(typeof(PlayerData), "TryLoadSave", (Type[])null, (Type[])null);

	private static readonly MethodInfo OnCreatedMethod = AccessTools.Method(typeof(PlayerData), "OnCreated", (Type[])null, (Type[])null);

	private static readonly MethodInfo InitializeMethod = AccessTools.Method(typeof(PlayerData), "Initialize", (Type[])null, (Type[])null);

	private static readonly MethodInfo PlayerLevelMethod = AccessTools.Method(typeof(PlayerData), "GetPlayerLevel", Type.EmptyTypes, (Type[])null);

	private static readonly FieldInfo InWorldUpgradesField = AccessTools.Field(typeof(PlayerData), "inWorldUpgrades");

	private static readonly FieldInfo FloatingInstancesField = AccessTools.Field(typeof(PlayerData), "floatingInstances");

	private static readonly FieldInfo RecentUnchosenField = AccessTools.Field(typeof(PlayerData), "recentUnchosenUpgrades");

	private static readonly FieldInfo DamagePropertiesField = AccessTools.Field(typeof(PlayerData), "damageProperties");

	private static readonly FieldInfo KillPropertiesField = AccessTools.Field(typeof(PlayerData), "killProperties");

	private static readonly FieldInfo MissionPropertiesField = AccessTools.Field(typeof(PlayerData), "missionProperties");

	private static readonly FieldInfo FixedUpdatePropertiesField = AccessTools.Field(typeof(PlayerData), "fixedUpdateProperties");

	private static readonly FieldInfo DiePropertiesField = AccessTools.Field(typeof(PlayerData), "dieProperties");

	private static readonly FieldInfo OuroPropertiesField = AccessTools.Field(typeof(PlayerData), "ouroProperties");

	private static readonly FieldInfo ActiveDefaultDirectiveField = AccessTools.Field(typeof(PlayerData), "activeDefaultDirective");

	private static readonly FieldInfo TaskObjectiveField = AccessTools.Field(typeof(PlayerData), "taskObjective");

	private static readonly FieldInfo ProfileNameField = AccessTools.Field(typeof(PlayerData), "profile");

	private static readonly FieldInfo DataField = AccessTools.Field(typeof(PlayerData), "data");

	private static readonly FieldInfo InvalidUpgradesField = AccessTools.Field(typeof(PlayerData), "invalidUpgrades");

	private static readonly FieldInfo DiscoveredModifiersField = AccessTools.Field(typeof(PlayerData), "discoveredModifiers");

	private static readonly MethodInfo MergeUpgradeListsMethod = AccessTools.Method(typeof(PlayerData), "MergeUpgradeLists", (Type[])null, (Type[])null);

	private static readonly MethodInfo GiveMissingGunTokensMethod = AccessTools.Method(typeof(PlayerData), "GiveMissingGunTokens", (Type[])null, (Type[])null);

	private static readonly MethodInfo QueryUpgradesMethod = AccessTools.Method(typeof(PlayerData), "QueryUpgrades", (Type[])null, (Type[])null);

	private static readonly FieldInfo OnResourcesUpdatedField = AccessTools.DeclaredField(typeof(PlayerData), "OnResourcesUpdated");

	private static readonly FieldInfo OnXPAddedField = AccessTools.DeclaredField(typeof(PlayerData), "OnXPAdded");

	private static readonly FieldInfo OnUpgradeCollectedOrDestroyedField = AccessTools.DeclaredField(typeof(PlayerData), "OnUpgradeCollectedOrDestroyed");

	internal static int GetCurrentProfileIndex()
	{
		ProfileConfig instance = ProfileConfig.Instance;
		if (instance == null || ProfileIndexField == null)
		{
			return 0;
		}
		return (int)ProfileIndexField.GetValue(instance);
	}

	public static bool TrySwapProfile(int newIndex, out string error)
	{
		//IL_018d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0194: Expected O, but got Unknown
		error = null;
		if (InstanceField == null || SavePathField == null || TryLoadSaveMethod == null || InitializeMethod == null || ProfileIndexField == null)
		{
			error = "Could not access required PlayerData internals. The game may have updated.";
			return false;
		}
		ProfileConfig instance = ProfileConfig.Instance;
		if (instance == null)
		{
			error = "Profile configuration is missing.";
			return false;
		}
		int num = (int)ProfileIndexField.GetValue(instance);
		PlayerData instance2 = PlayerData.Instance;
		string text = SavePathField.GetValue(null) as string;
		try
		{
			UpdateProfileLevelMetadata(num);
			PlayerData.SaveData(false);
			SaveProfileConfig();
			instance.SetProfile(newIndex);
			SaveProfileConfig();
			string text2 = BuildSavePathForCurrentProfile();
			SavePathField.SetValue(null, text2);
			string text3 = text2 + "/PlayerData.saxon";
			object[] array = new object[2] { text3, null };
			int num2 = (int)TryLoadSaveMethod.Invoke(null, array);
			object obj = array[1];
			PlayerData val = (PlayerData)((obj is PlayerData) ? obj : null);
			if (num2 != 0 || val == null)
			{
				string path = text2 + "/Backups";
				if (Directory.Exists(path))
				{
					string[] files = Directory.GetFiles(path);
					Array.Sort(files);
					for (int num3 = files.Length - 1; num3 >= 0; num3--)
					{
						array[0] = files[num3];
						array[1] = null;
						num2 = (int)TryLoadSaveMethod.Invoke(null, array);
						object obj2 = array[1];
						val = (PlayerData)((obj2 is PlayerData) ? obj2 : null);
						if (num2 == 0 && val != null)
						{
							break;
						}
					}
				}
			}
			if (num2 != 0 || val == null)
			{
				ProfileSwitcherPlugin.Log.LogInfo((object)$"No existing save for profile {newIndex}; creating a new PlayerData instance.");
				val = new PlayerData();
				OnCreatedMethod?.Invoke(val, null);
			}
			ClearStaticRuntimeCaches();
			RemovePlayerDataEventHandlers(instance2);
			ResetProcessLifetimeCounters();
			InstanceField.SetValue(null, val);
			int gameLaunchCount = val.gameLaunchCount;
			InitializeMethod.Invoke(val, null);
			val.gameLaunchCount = gameLaunchCount;
			RebindGearAndFinalize(val);
			ProfileNameField?.SetValue(val, GetSteamBranchFolderName());
			UpdateProfileLevelMetadata(newIndex);
			SaveProfileConfig();
			return true;
		}
		catch (Exception ex)
		{
			error = ex.Message;
			ProfileSwitcherPlugin.Log.LogError((object)ex);
			try
			{
				if (instance2 != null)
				{
					InstanceField.SetValue(null, instance2);
				}
				if (text != null)
				{
					SavePathField.SetValue(null, text);
				}
				instance.SetProfile(num);
				SaveProfileConfig();
			}
			catch (Exception arg)
			{
				ProfileSwitcherPlugin.Log.LogError((object)$"Rollback after failed swap also failed: {arg}");
			}
			return false;
		}
	}

	private static void UpdateProfileLevelMetadata(int index)
	{
		try
		{
			if (index >= 1)
			{
				int level = 1;
				if (PlayerLevelMethod != null && PlayerData.Instance != null)
				{
					level = (int)PlayerLevelMethod.Invoke(null, null);
				}
				ProfileConfig.Instance.GetProfile(index).Level = level;
			}
		}
		catch (Exception ex)
		{
			ProfileSwitcherPlugin.Log.LogDebug((object)("Could not update profile level metadata: " + ex.Message));
		}
	}

	private static string GetSteamBranchFolderName()
	{
		string text = default(string);
		if (SteamManager.Initialized && Online.GetProfileName(ref text) && !string.IsNullOrEmpty(text))
		{
			return text;
		}
		return "default";
	}

	private static string BuildSavePathForCurrentProfile()
	{
		//IL_001a: Unknown result type (might be due to invalid IL or missing references)
		string text = Application.persistentDataPath + "/Profiles/" + GetSteamBranchFolderName();
		string name = ProfileConfig.Instance.CurrentProfile.Name;
		if (!string.IsNullOrEmpty(name))
		{
			return text + "/" + name;
		}
		return text;
	}

	private static void SaveProfileConfig()
	{
		try
		{
			if (ProfileConfig.Instance == null || string.IsNullOrEmpty(ProfileConfig.SavePath))
			{
				return;
			}
			BinaryFormatter binaryFormatter = (BinaryFormatterField?.GetValue(null) as BinaryFormatter) ?? new BinaryFormatter();
			string directoryName = Path.GetDirectoryName(ProfileConfig.SavePath);
			if (!string.IsNullOrEmpty(directoryName))
			{
				Directory.CreateDirectory(directoryName);
			}
			using FileStream serializationStream = new FileStream(ProfileConfig.SavePath, FileMode.Create);
			binaryFormatter.Serialize(serializationStream, ProfileConfig.Instance);
		}
		catch (Exception ex)
		{
			ProfileSwitcherPlugin.Log.LogWarning((object)("Failed to save Profiles.poop: " + ex.Message));
		}
	}

	private static void ClearStaticRuntimeCaches()
	{
		ClearListField(InWorldUpgradesField);
		ClearDictionaryField(FloatingInstancesField);
		ClearListField(RecentUnchosenField);
		ClearListField(DamagePropertiesField);
		ClearListField(KillPropertiesField);
		ClearListField(MissionPropertiesField);
		ClearListField(FixedUpdatePropertiesField);
		ClearListField(DiePropertiesField);
		ClearListField(OuroPropertiesField);
		ActiveDefaultDirectiveField?.SetValue(null, null);
		TaskObjectiveField?.SetValue(null, null);
	}

	private static void RemovePlayerDataEventHandlers(PlayerData oldInstance)
	{
		if (oldInstance != null)
		{
			RemoveHandlersTargeting(OnResourcesUpdatedField, oldInstance);
			RemoveHandlersTargeting(OnXPAddedField, oldInstance);
			RemoveHandlersTargeting(OnUpgradeCollectedOrDestroyedField, oldInstance);
		}
	}

	private static void RemoveHandlersTargeting(FieldInfo eventField, object oldTarget)
	{
		if (eventField == null)
		{
			return;
		}
		try
		{
			if (!(eventField.GetValue(null) is Delegate obj) || (object)obj == null)
			{
				return;
			}
			Delegate obj2 = null;
			Delegate[] invocationList = obj.GetInvocationList();
			foreach (Delegate obj3 in invocationList)
			{
				if (obj3.Target != oldTarget)
				{
					obj2 = (((object)obj2 == null) ? obj3 : Delegate.Combine(obj2, obj3));
				}
			}
			eventField.SetValue(null, obj2);
		}
		catch (Exception ex)
		{
			ProfileSwitcherPlugin.Log.LogDebug((object)("Could not prune event field " + eventField.Name + ": " + ex.Message));
		}
	}

	private static void ResetProcessLifetimeCounters()
	{
		PlayerData.goobers = 0;
		PlayerData.hoops = 0;
		PlayerData.tempSaxitosPunchedCounter = 0;
	}

	private static void ClearListField(FieldInfo field)
	{
		if (!(field == null) && field.GetValue(null) is IList list)
		{
			list.Clear();
		}
	}

	private static void ClearDictionaryField(FieldInfo field)
	{
		if (!(field == null) && field.GetValue(null) is IDictionary dictionary)
		{
			dictionary.Clear();
		}
	}

	private static void RebindGearAndFinalize(PlayerData instance)
	{
		if ((Object)(object)Global.Instance == (Object)null)
		{
			ProfileSwitcherPlugin.Log.LogWarning((object)"Global.Instance is null during profile rebind; gear refs may be incomplete until next scene.");
			return;
		}
		for (int i = 0; i < Global.Instance.AllGear.Length; i++)
		{
			AddGear(Global.Instance.AllGear[i]);
		}
		for (int j = 0; j < Global.Instance.Characters.Length; j++)
		{
			AddGear((IUpgradable)(object)Global.Instance.Characters[j]);
		}
		AddGear((IUpgradable)(object)Global.Instance);
		RepairInvalidAndNullUpgrades(instance);
		try
		{
			MergeUpgradeListsMethod?.Invoke(instance, null);
		}
		catch (Exception ex)
		{
			ProfileSwitcherPlugin.Log.LogDebug((object)("MergeUpgradeLists: " + ex.Message));
		}
		List<int> list = null;
		foreach (KeyValuePair<int, GearData> item in instance.collectedGear)
		{
			if (item.Value.Gear == null)
			{
				if (list == null)
				{
					list = new List<int>();
				}
				list.Add(item.Key);
			}
			else
			{
				try
				{
					item.Value.DeepCleanup();
				}
				catch (Exception ex2)
				{
					ProfileSwitcherPlugin.Log.LogDebug((object)("DeepCleanup: " + ex2.Message));
				}
			}
		}
		if (list != null)
		{
			for (int k = 0; k < list.Count; k++)
			{
				instance.collectedGear.Remove(list[k]);
			}
		}
		foreach (KeyValuePair<int, GearData> item2 in instance.collectedGear)
		{
			try
			{
				IUpgradable gear = item2.Value.Gear;
				if (gear != null)
				{
					gear.InitializeOnLoad();
				}
				item2.Value.RestoreSavedUpgrades(true);
			}
			catch (Exception ex3)
			{
				ProfileSwitcherPlugin.Log.LogDebug((object)("InitializeOnLoad/RestoreSavedUpgrades: " + ex3.Message));
			}
		}
		try
		{
			if (instance.GetFlag("mxp") == 0)
			{
				instance.SetFlag("mxp", 1);
				foreach (KeyValuePair<int, GearData> item3 in instance.collectedGear)
				{
					try
					{
						item3.Value.GiveMissedXP();
					}
					catch (Exception ex4)
					{
						ProfileSwitcherPlugin.Log.LogDebug((object)("GiveMissedXP: " + ex4.Message));
					}
				}
			}
			if (instance.GetFlag("freeupgrades") <= 0 && QueryUpgradesMethod != null)
			{
				instance.SetFlag("freeupgrades", 1);
				QueryUpgradesMethod.Invoke(null, new object[1] { (Action<UpgradeInstance>)delegate(UpgradeInstance upgrade)
				{
					try
					{
						upgrade.UnlockIfFree();
					}
					catch (Exception ex7)
					{
						ProfileSwitcherPlugin.Log.LogDebug((object)("UnlockIfFree: " + ex7.Message));
					}
				} });
			}
		}
		catch (Exception ex5)
		{
			ProfileSwitcherPlugin.Log.LogDebug((object)("OnAwake one-shot fixes skipped: " + ex5.Message));
		}
		ScrubInvalidDiscoveredModifiers(instance);
		try
		{
			GiveMissingGunTokensMethod?.Invoke(null, null);
		}
		catch (Exception ex6)
		{
			ProfileSwitcherPlugin.Log.LogDebug((object)("GiveMissingGunTokens: " + ex6.Message));
		}
		void AddGear(IUpgradable val)
		{
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Expected O, but got Unknown
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Invalid comparison between Unknown and I4
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Invalid comparison between Unknown and I4
			if (!((Object)(object)((val != null) ? val.Info : null) == (Object)null))
			{
				if (instance.collectedGear.TryGetValue(val.Info.ID, out var value))
				{
					value.Gear = val;
					if ((int)val.Info.UnlockState >= 1 && !value.IsCollected)
					{
						value.Collect();
					}
					if ((int)val.Info.UnlockState == 2 && !value.IsUnlocked)
					{
						value.Unlock();
					}
				}
				else
				{
					instance.collectedGear.Add(val.Info.ID, new GearData(val, val.Info.UnlockState));
				}
			}
		}
	}

	private static void RepairInvalidAndNullUpgrades(PlayerData instance)
	{
		if (DataField == null || InvalidUpgradesField == null)
		{
			return;
		}
		try
		{
			IDictionary dictionary = DataField.GetValue(instance) as IDictionary;
			IList list = InvalidUpgradesField.GetValue(instance) as IList;
			if (dictionary == null || list == null)
			{
				return;
			}
			Type type = null;
			if (InvalidUpgradesField.FieldType.IsGenericType)
			{
				type = InvalidUpgradesField.FieldType.GetGenericArguments()[0];
			}
			FieldInfo fieldInfo = type?.GetField("info", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			FieldInfo fieldInfo2 = type?.GetField("gearID", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (fieldInfo == null || fieldInfo2 == null)
			{
				return;
			}
			PropertyInfo property = typeof(UpgradeInfo).GetProperty("Upgrade", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			PropertyInfo property2 = typeof(UpgradeInfo).GetProperty("SourceID", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			for (int num = list.Count - 1; num >= 0; num--)
			{
				object obj = list[num];
				if (obj == null)
				{
					list.RemoveAt(num);
				}
				else
				{
					object value = fieldInfo.GetValue(obj);
					if (value == null)
					{
						list.RemoveAt(num);
					}
					else
					{
						object? obj2 = property?.GetValue(value);
						int num2 = (int)fieldInfo2.GetValue(obj);
						if (obj2 != null && dictionary.Contains(num2) && dictionary[num2] is IList list2)
						{
							object obj3 = property2?.GetValue(value);
							bool flag = false;
							for (int i = 0; i < list2.Count; i++)
							{
								object obj4 = list2[i];
								if (obj4 != null)
								{
									object obj5 = property2?.GetValue(obj4);
									if (obj3 != null && obj5 != null && obj3.Equals(obj5))
									{
										flag = true;
										break;
									}
								}
							}
							if (!flag)
							{
								list2.Add(value);
							}
							list.RemoveAt(num);
						}
					}
				}
			}
			ArrayList arrayList = new ArrayList();
			foreach (object key in dictionary.Keys)
			{
				arrayList.Add(key);
			}
			FieldInfo field = typeof(UpgradeID).GetField("Mod", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (object item in arrayList)
			{
				if (!(dictionary[item] is IList list3))
				{
					continue;
				}
				for (int num3 = list3.Count - 1; num3 >= 0; num3--)
				{
					object obj6 = list3[num3];
					if (obj6 == null)
					{
						list3.RemoveAt(num3);
					}
					else if (property?.GetValue(obj6) == null)
					{
						object obj7 = property2?.GetValue(obj6);
						string value2 = null;
						if (obj7 != null && field != null)
						{
							value2 = field.GetValue(obj7) as string;
						}
						if (!string.IsNullOrEmpty(value2) && type != null)
						{
							for (int num4 = list.Count - 1; num4 >= 0; num4--)
							{
								object obj8 = list[num4];
								if (obj8 != null && object.Equals(fieldInfo2.GetValue(obj8), item))
								{
									object value3 = fieldInfo.GetValue(obj8);
									object obj9 = ((value3 == null) ? null : property2?.GetValue(value3));
									if (obj7 != null && obj9 != null && obj7.Equals(obj9))
									{
										list.RemoveAt(num4);
									}
								}
							}
							object obj10 = Activator.CreateInstance(type);
							fieldInfo.SetValue(obj10, obj6);
							fieldInfo2.SetValue(obj10, item);
							list.Add(obj10);
						}
						list3.RemoveAt(num3);
					}
				}
			}
		}
		catch (Exception ex)
		{
			ProfileSwitcherPlugin.Log.LogDebug((object)("RepairInvalidAndNullUpgrades: " + ex.Message));
		}
	}

	private static void ScrubInvalidDiscoveredModifiers(PlayerData instance)
	{
		if (DiscoveredModifiersField == null)
		{
			return;
		}
		try
		{
			if (!(DiscoveredModifiersField.GetValue(instance) is IDictionary { Count: not 0 } dictionary))
			{
				return;
			}
			MethodInfo method = typeof(UpgradeID).GetMethod("IsValid", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			object obj = Activator.CreateInstance(typeof(UpgradeID));
			bool flag = false;
			foreach (object key in dictionary.Keys)
			{
				if (key == null || key.Equals(obj))
				{
					flag = true;
					break;
				}
				if (method != null && !(bool)method.Invoke(key, null))
				{
					flag = true;
					break;
				}
			}
			if (!flag)
			{
				return;
			}
			IDictionary dictionary2;
			try
			{
				Type type = AccessTools.TypeByName("UpgradeIDComparer");
				object obj2 = ((!(type != null)) ? null : AccessTools.Field(type, "Instance")?.GetValue(null));
				dictionary2 = ((obj2 != null) ? ((IDictionary)Activator.CreateInstance(dictionary.GetType(), obj2)) : ((IDictionary)Activator.CreateInstance(dictionary.GetType())));
			}
			catch
			{
				dictionary2 = (IDictionary)Activator.CreateInstance(dictionary.GetType());
			}
			foreach (DictionaryEntry item in dictionary)
			{
				if (item.Key != null && !item.Key.Equals(obj) && (!(method != null) || (bool)method.Invoke(item.Key, null)))
				{
					dictionary2.Add(item.Key, item.Value);
				}
			}
			DiscoveredModifiersField.SetValue(instance, dictionary2);
		}
		catch (Exception ex)
		{
			ProfileSwitcherPlugin.Log.LogDebug((object)("ScrubInvalidDiscoveredModifiers: " + ex.Message));
		}
	}
}
[HarmonyPatch(typeof(StartMenu))]
internal static class StartMenuProfilePatches
{
	private static readonly FieldInfo ProfileSelectField = AccessTools.Field(typeof(StartMenu), "profileSelect");

	private static readonly FieldInfo ProfileTextField = AccessTools.Field(typeof(StartMenu), "profileText");

	private static readonly FieldInfo ProfileRestartTextField = AccessTools.Field(typeof(StartMenu), "profileRestartText");

	private static readonly FieldInfo PatchNotesBlinkerField = AccessTools.Field(typeof(StartMenu), "patchNotesBlinker");

	private static bool _isSwapping;

	[HarmonyPatch("SelectProfile")]
	[HarmonyPrefix]
	private static bool SelectProfile_Prefix(StartMenu __instance, int index)
	{
		if (_isSwapping)
		{
			return false;
		}
		try
		{
			if (!CanSwapSafely(out var reason))
			{
				ProfileSwitcherPlugin.Log.LogWarning((object)("Profile swap blocked: " + reason));
				ShowInfo("Profile Swap", reason);
				ApplyVanillaSelectionUi(__instance, index, showRestartPrompt: true);
				return false;
			}
			int currentProfileIndex = ProfileReloader.GetCurrentProfileIndex();
			if (index == currentProfileIndex)
			{
				ApplyVanillaSelectionUi(__instance, index, showRestartPrompt: false);
				return false;
			}
			_isSwapping = true;
			string error;
			bool num = ProfileReloader.TrySwapProfile(index, out error);
			_isSwapping = false;
			if (!num)
			{
				ProfileSwitcherPlugin.Log.LogError((object)("Profile swap failed: " + error));
				ShowInfo("Profile Swap Failed", error ?? "Unknown error while loading the selected profile.");
				ApplyVanillaSelectionUi(__instance, ProfileReloader.GetCurrentProfileIndex(), showRestartPrompt: true);
				return false;
			}
			ApplyVanillaSelectionUi(__instance, index, showRestartPrompt: false);
			RefreshMenuAfterSwap(__instance);
			ProfileSwitcherPlugin.Log.LogInfo((object)$"Swapped to profile index {index} without restart.");
			return false;
		}
		catch (Exception arg)
		{
			_isSwapping = false;
			ProfileSwitcherPlugin.Log.LogError((object)$"Unhandled profile swap exception: {arg}");
			ShowInfo("Profile Swap Failed", "An unexpected error occurred. The game may need a restart.");
			return false;
		}
	}

	private static bool CanSwapSafely(out string reason)
	{
		if ((Object)(object)GameManager.Instance != (Object)null)
		{
			reason = "Leave the hub/mission and return to the main menu before swapping profiles.";
			return false;
		}
		if (JoinFriendsWindow.JoiningInProgress)
		{
			reason = "Cannot swap profiles while joining a lobby.";
			return false;
		}
		if ((Object)(object)StartMenu.Instance == (Object)null)
		{
			reason = "Main menu is not active.";
			return false;
		}
		reason = null;
		return true;
	}

	private static void ApplyVanillaSelectionUi(StartMenu menu, int index, bool showRestartPrompt)
	{
		ProfileConfig.Instance.SetProfile(index);
		object? obj = ProfileSelectField?.GetValue(menu);
		object? obj2 = ((obj is GameObject) ? obj : null);
		if (obj2 != null)
		{
			((GameObject)obj2).SetActive(false);
		}
		object? obj3 = ProfileRestartTextField?.GetValue(menu);
		TextMeshProUGUI val = (TextMeshProUGUI)((obj3 is TextMeshProUGUI) ? obj3 : null);
		if ((Object)(object)val != (Object)null)
		{
			((Component)val).gameObject.SetActive(showRestartPrompt);
		}
		object? obj4 = ProfileTextField?.GetValue(menu);
		TextMeshProUGUI val2 = (TextMeshProUGUI)((obj4 is TextMeshProUGUI) ? obj4 : null);
		if ((Object)(object)val2 != (Object)null)
		{
			((TMP_Text)val2).text = ((index == 0) ? "default" : ProfileConfig.Instance.GetProfile(index).Name);
			((Component)val2).gameObject.SetActive(true);
		}
	}

	private static void RefreshMenuAfterSwap(StartMenu menu)
	{
		try
		{
			object? obj = PatchNotesBlinkerField?.GetValue(menu);
			Component val = (Component)((obj is Component) ? obj : null);
			if ((Object)(object)val != (Object)null)
			{
				val.gameObject.SetActive(Global.Version != PlayerData.Instance.lastViewedPatchNotes);
			}
		}
		catch (Exception ex)
		{
			ProfileSwitcherPlugin.Log.LogDebug((object)("Patch notes blinker refresh skipped: " + ex.Message));
		}
	}

	private static void ShowInfo(string title, string body)
	{
		//IL_0048: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			if (!((Object)(object)StartMenu.Instance == (Object)null) && !((Object)(object)Global.Instance == (Object)null) && !((Object)(object)Global.Instance.InfoWindow == (Object)null))
			{
				((InfoWindow)((Window)StartMenu.Instance).WindowSystem.OpenWindowFromPrefab((Window)(object)Global.Instance.InfoWindow, false)).SetInfo(title, body);
			}
		}
		catch (Exception ex)
		{
			ProfileSwitcherPlugin.Log.LogDebug((object)("Could not show info window: " + ex.Message));
		}
	}
}
namespace ProfileSwitcher
{
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "ProfileSwitcher";

		public const string PLUGIN_NAME = "ProfileSwitcher";

		public const string PLUGIN_VERSION = "1.0.1";
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}