Decompiled source of CosmeticResync Fixed v1.1.0

plugins/CosmeticResync_Fixed.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Logging;
using CosmeticResync.Patches;
using HarmonyLib;
using Newtonsoft.Json;
using UnityEngine;
using Zorro.Core;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("CosmeticResync_Fixed")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("CosmeticResync_Fixed")]
[assembly: AssemblyCopyright("Copyright © 1101trash 2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("eb3c5a88-48ff-431d-9961-15eee8223f66")]
[assembly: AssemblyFileVersion("1.1.0")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.1.0.0")]
[module: UnverifiableCode]
namespace CosmeticResync
{
	[BepInPlugin("1101trash.CosmeticResync_Fixed", "CosmeticResync_Fixed", "1.1.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class CosmeticResync : BaseUnityPlugin
	{
		internal const string Name = "CosmeticResync_Fixed";

		internal const string Author = "1101trash";

		internal const string ID = "1101trash.CosmeticResync_Fixed";

		internal const string Version = "1.1.0";

		internal static CosmeticResync Instance;

		internal static ManualLogSource Logger;

		public void Awake()
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Expected O, but got Unknown
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Expected O, but got Unknown
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: Expected O, but got Unknown
			Instance = this;
			Logger = ((BaseUnityPlugin)this).Logger;
			Harmony val = new Harmony("1101trash.CosmeticResync_Fixed");
			Logger.LogInfo((object)"Applying Harmony patches");
			val.PatchAll(Assembly.GetExecutingAssembly());
			if (Chainloader.PluginInfos.TryGetValue("com.github.Spider-XD-Gaming.Multi-Cosmetic", out var value))
			{
				MultiCosmeticPatch.MultiCosmetic = value.Instance;
				if (MultiCosmeticPatch.GetCurrentDataField())
				{
					Type type = ((object)value.Instance).GetType();
					MethodInfo methodInfo = AccessTools.Method(type, "dataFromString", (Type[])null, (Type[])null);
					if (methodInfo != null)
					{
						val.Patch((MethodBase)methodInfo, new HarmonyMethod(AccessTools.Method(typeof(MultiCosmeticPatch), "PrefixMCDataFromString", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					}
					else
					{
						Logger.LogError((object)"Failed to find Multi_Cosmetic.Plugin.dataFromString()");
					}
					methodInfo = AccessTools.Method(type, "UpdateCustomPropertiesValue", (Type[])null, (Type[])null);
					if (methodInfo != null)
					{
						val.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(AccessTools.Method(typeof(MultiCosmeticPatch), "PostfixUpdateCustomPropertiesValue", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					}
					else
					{
						Logger.LogError((object)"Failed to find Multi_Cosmetic.Plugin.UpdateCustomPropertiesValue()");
					}
				}
			}
			int num = 0;
			foreach (MethodBase patchedMethod in val.GetPatchedMethods())
			{
				Logger.LogInfo((object)("Patched " + patchedMethod.DeclaringType.FullName + "." + patchedMethod.Name));
				num++;
			}
			Logger.LogInfo((object)(num + " patches applied"));
		}
	}
}
namespace CosmeticResync.Patches
{
	[HarmonyPatch(typeof(CharacterCustomization))]
	internal static class CosmeticPatch
	{
		[HarmonyPrefix]
		[HarmonyPatch("TryGetCosmeticsFromSteam")]
		private static bool PrefixTryGetCosmeticsFromSteam(ref CharacterCustomization __instance)
		{
			Customization instance = Singleton<Customization>.Instance;
			if (!Object.op_Implicit((Object)(object)instance))
			{
				CosmeticResync.Logger.LogError((object)"Customization not initialized? Cannot Resync cosmetics");
				return true;
			}
			int cosmeticFromPrefsOrSteam = GetCosmeticFromPrefsOrSteam("CosmeticResync_Skin", (STEAMSTATTYPE)7, instance.skins);
			if (cosmeticFromPrefsOrSteam > 0)
			{
				CharacterCustomization.SetCharacterSkinColor(cosmeticFromPrefsOrSteam);
			}
			else
			{
				__instance.SetRandomSkinColor();
			}
			int cosmeticFromPrefsOrSteam2 = GetCosmeticFromPrefsOrSteam("CosmeticResync_Eyes", (STEAMSTATTYPE)8, instance.eyes);
			if (cosmeticFromPrefsOrSteam2 > 0)
			{
				CharacterCustomization.SetCharacterEyes(cosmeticFromPrefsOrSteam2);
			}
			else
			{
				__instance.SetRandomEyes();
			}
			int cosmeticFromPrefsOrSteam3 = GetCosmeticFromPrefsOrSteam("CosmeticResync_Mouth", (STEAMSTATTYPE)9, instance.mouths);
			if (cosmeticFromPrefsOrSteam3 > 0)
			{
				CharacterCustomization.SetCharacterMouth(cosmeticFromPrefsOrSteam3);
			}
			else
			{
				__instance.SetRandomMouth();
			}
			int cosmeticFromPrefsOrSteam4 = GetCosmeticFromPrefsOrSteam("CosmeticResync_Accessory", (STEAMSTATTYPE)10, instance.accessories);
			if (cosmeticFromPrefsOrSteam4 > 0)
			{
				CharacterCustomization.SetCharacterAccessory(cosmeticFromPrefsOrSteam4);
			}
			else
			{
				__instance.SetRandomAccessory();
			}
			int cosmeticFromPrefsOrSteam5 = GetCosmeticFromPrefsOrSteam("CosmeticResync_Outfit", (STEAMSTATTYPE)11, instance.fits);
			if (cosmeticFromPrefsOrSteam5 > 0)
			{
				CharacterCustomization.SetCharacterOutfit(cosmeticFromPrefsOrSteam5);
			}
			else
			{
				__instance.SetRandomOutfit();
			}
			int cosmeticFromPrefsOrSteam6 = GetCosmeticFromPrefsOrSteam("CosmeticResync_Hat", (STEAMSTATTYPE)12, instance.hats);
			if (cosmeticFromPrefsOrSteam6 > 0)
			{
				CharacterCustomization.SetCharacterHat(cosmeticFromPrefsOrSteam6);
			}
			else
			{
				__instance.SetRandomHat();
			}
			int cosmeticFromPrefsOrSteam7 = GetCosmeticFromPrefsOrSteam("CosmeticResync_Sash", (STEAMSTATTYPE)17, instance.sashes);
			if (cosmeticFromPrefsOrSteam7 > 0)
			{
				CharacterCustomization.SetCharacterSash(cosmeticFromPrefsOrSteam7);
			}
			else
			{
				CharacterCustomization.SetCharacterSash(0);
			}
			return false;
		}

		[HarmonyPostfix]
		[HarmonyPatch("SetCharacterSkinColor")]
		private static void PostfixSetCharacterSkinColor(int index)
		{
			Customization instance = Singleton<Customization>.Instance;
			if (!Object.op_Implicit((Object)(object)instance))
			{
				CosmeticResync.Logger.LogError((object)"Customization not initialized? Cannot save skin color");
			}
			else
			{
				ValidateAndSaveCosmetic(index, "CosmeticResync_Skin", instance.skins);
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch("SetCharacterEyes")]
		private static void PostfixSetCharacterEyes(int index)
		{
			Customization instance = Singleton<Customization>.Instance;
			if (!Object.op_Implicit((Object)(object)instance))
			{
				CosmeticResync.Logger.LogError((object)"Customization not initialized? Cannot save eyes");
			}
			else
			{
				ValidateAndSaveCosmetic(index, "CosmeticResync_Eyes", instance.eyes);
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch("SetCharacterMouth")]
		private static void PostfixSetCharacterMouth(int index)
		{
			Customization instance = Singleton<Customization>.Instance;
			if (!Object.op_Implicit((Object)(object)instance))
			{
				CosmeticResync.Logger.LogError((object)"Customization not initialized? Cannot save mouth");
			}
			else
			{
				ValidateAndSaveCosmetic(index, "CosmeticResync_Mouth", instance.mouths);
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch("SetCharacterAccessory")]
		private static void PostfixSetCharacterAccessory(int index)
		{
			Customization instance = Singleton<Customization>.Instance;
			if (!Object.op_Implicit((Object)(object)instance))
			{
				CosmeticResync.Logger.LogError((object)"Customization not initialized? Cannot save accessory name");
			}
			else
			{
				ValidateAndSaveCosmetic(index, "CosmeticResync_Accessory", instance.accessories);
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch("SetCharacterOutfit")]
		private static void PostfixSetCharacterOutfit(int index)
		{
			Customization instance = Singleton<Customization>.Instance;
			if (!Object.op_Implicit((Object)(object)instance))
			{
				CosmeticResync.Logger.LogError((object)"Customization not initialized? Cannot save outfit name");
			}
			else
			{
				ValidateAndSaveCosmetic(index, "CosmeticResync_Outfit", instance.fits);
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch("SetCharacterHat")]
		private static void PostfixSetCharacterHat(int index)
		{
			Customization instance = Singleton<Customization>.Instance;
			if (!Object.op_Implicit((Object)(object)instance))
			{
				CosmeticResync.Logger.LogError((object)"Customization not initialized? Cannot save hat name");
			}
			else
			{
				ValidateAndSaveCosmetic(index, "CosmeticResync_Hat", instance.hats);
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch("SetCharacterSash")]
		private static void PostfixSetCharacterSash(int index)
		{
			Customization instance = Singleton<Customization>.Instance;
			if (!Object.op_Implicit((Object)(object)instance))
			{
				CosmeticResync.Logger.LogError((object)"Customization not initialized? Cannot save sash name");
			}
			else
			{
				ValidateAndSaveCosmetic(index, "CosmeticResync_Sash", instance.sashes);
			}
		}

		private static int GetCosmeticFromPrefsOrSteam(string prefKey, STEAMSTATTYPE steamStatType, CustomizationOption[] options)
		{
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			int num = -1;
			if (PlayerPrefs.HasKey(prefKey))
			{
				string text = PlayerPrefs.GetString(prefKey);
				if (!string.IsNullOrWhiteSpace(text))
				{
					for (int i = 0; i < options.Length; i++)
					{
						if (((Object)options[i]).name == text)
						{
							num = i;
							break;
						}
					}
					if (num != -1)
					{
						Debug.Log((object)$"[CosmeticResync] Detected {steamStatType} {text} as #{num}");
					}
					else
					{
						CosmeticResync.Logger.LogWarning((object)$"{steamStatType} {text} is no longer available, picking a random option");
					}
				}
			}
			else if (Singleton<AchievementManager>.Instance.GetSteamStatInt(steamStatType, ref num))
			{
				if (num >= options.Length)
				{
					num = -1;
				}
			}
			else
			{
				num = -1;
			}
			return num;
		}

		private static void ValidateAndSaveCosmetic(int index, string prefKey, CustomizationOption[] options)
		{
			if (index < 0 || index >= options.Length)
			{
				CosmeticResync.Logger.LogError((object)$"Customization index #{index} invalid? Cannot save customization for {prefKey}");
			}
			else
			{
				PlayerPrefs.SetString(prefKey, ((Object)options[index]).name);
			}
		}
	}
	[HarmonyPatch]
	internal static class MultiCosmeticPatch
	{
		private enum RestoreResult
		{
			Restored,
			Retry,
			NoSavedData,
			InvalidData
		}

		internal static BaseUnityPlugin MultiCosmetic;

		private static FieldInfo _currentData;

		private static bool _restoreQueued;

		private static bool _restorePending;

		private static bool _pendingSaveLogged;

		internal static bool GetCurrentDataField()
		{
			if ((Object)(object)MultiCosmetic == (Object)null)
			{
				CosmeticResync.Logger.LogError((object)"Missing MultiCosmetic instance");
				return false;
			}
			FieldInfo fieldInfo = AccessTools.Field(((object)MultiCosmetic).GetType(), "currentData");
			if (fieldInfo == null)
			{
				CosmeticResync.Logger.LogError((object)"MultiCosmetic missing currentData field");
				return false;
			}
			if (fieldInfo.FieldType != typeof(int[][]))
			{
				CosmeticResync.Logger.LogError((object)"MultiCosmetic.currentData is no longer an int[][]");
				return false;
			}
			_currentData = fieldInfo;
			_restorePending = PlayerPrefs.HasKey("CosmeticResync_MultiCosmetic");
			return true;
		}

		internal static bool PrefixMCDataFromString()
		{
			if ((Object)(object)MultiCosmetic == (Object)null)
			{
				CosmeticResync.Logger.LogError((object)"Missing MultiCosmetic instance, cannot resync MultiCosmetic");
				return true;
			}
			if (_currentData == null)
			{
				CosmeticResync.Logger.LogError((object)"MultiCosmetic.currentData field not found, cannot Resync Multicosmetic");
				return true;
			}
			string[] missingAccessories;
			string[] missingHats;
			return TryRestoreSavedCosmetics(out missingAccessories, out missingHats) != RestoreResult.Restored;
		}

		internal static void PostfixUpdateCustomPropertiesValue()
		{
			if ((Object)(object)MultiCosmetic == (Object)null)
			{
				CosmeticResync.Logger.LogError((object)"Missing MultiCosmetic instance? Cannot save MultiCosmetic names");
				return;
			}
			if (_currentData == null)
			{
				CosmeticResync.Logger.LogError((object)"MultiCosmetic.currentData field not found, cannot save MultiCosmetic names");
				return;
			}
			if (_restorePending)
			{
				if (!_pendingSaveLogged)
				{
					CosmeticResync.Logger.LogInfo((object)"Skipping MultiCosmetic save while saved cosmetic names are awaiting restoration");
					_pendingSaveLogged = true;
				}
				return;
			}
			Customization instance = Singleton<Customization>.Instance;
			if (!Object.op_Implicit((Object)(object)instance))
			{
				CosmeticResync.Logger.LogError((object)"Customization not initialized? Cannot save MultiCosmetic names");
				return;
			}
			int[][] array = (int[][])_currentData.GetValue(MultiCosmetic);
			string[] names;
			string[] names2;
			if (array == null)
			{
				CosmeticResync.Logger.LogError((object)"MultiCosmetic.currentData is null");
			}
			else if (array.Length != 2)
			{
				CosmeticResync.Logger.LogError((object)$"Expected MultiCosmetic.Plugin.currentData to contain 2 int[], not {array.Length}");
			}
			else if (array[0] == null || array[1] == null)
			{
				CosmeticResync.Logger.LogError((object)"MultiCosmetic.currentData contains a null array");
			}
			else if (TryGetCosmeticNames(instance.accessories, array[0], "Accessory", out names) && TryGetCosmeticNames(instance.hats, array[1], "Hat", out names2))
			{
				string text = JsonConvert.SerializeObject((object)new string[2][] { names, names2 });
				PlayerPrefs.SetString("CosmeticResync_MultiCosmetic", text);
				PlayerPrefs.Save();
				_pendingSaveLogged = false;
				CosmeticResync.Logger.LogInfo((object)$"Saved {names.Length} accessories and {names2.Length} hats");
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(PassportManager), "Awake")]
		internal static void Awake(PassportManager __instance)
		{
			Customization component = ((Component)__instance).GetComponent<Customization>();
			if (Object.op_Implicit((Object)(object)component))
			{
				Singleton<Customization>._instance = component;
				CosmeticResync.Logger.LogInfo((object)"Manually updating MultiCosmetic");
				if (!_restoreQueued)
				{
					_restoreQueued = true;
					CosmeticResync.Logger.LogInfo((object)"Queued MultiCosmetic restore; waiting for cosmetic options to become available");
					((MonoBehaviour)CosmeticResync.Instance).StartCoroutine(RestoreWhenReady());
				}
			}
			else
			{
				CosmeticResync.Logger.LogError((object)"PassportManager not with Customization? Cannot resync MultiCosmetic");
			}
		}

		private static IEnumerator RestoreWhenReady()
		{
			string[] missingAccessories = new string[0];
			string[] missingHats = new string[0];
			int attempt = 0;
			while (attempt < 30)
			{
				switch (TryRestoreSavedCosmetics(out missingAccessories, out missingHats))
				{
				case RestoreResult.NoSavedData:
					CosmeticResync.Logger.LogInfo((object)"No saved MultiCosmetic names found; keeping MultiCosmetic's current data");
					_restoreQueued = false;
					yield break;
				default:
					_restoreQueued = false;
					_pendingSaveLogged = false;
					yield break;
				case RestoreResult.Retry:
					yield return (object)new WaitForSecondsRealtime(1f);
					attempt++;
					break;
				}
			}
			string[] array = missingAccessories;
			foreach (string text in array)
			{
				CosmeticResync.Logger.LogWarning((object)("Accessory " + text + " is still unavailable after waiting for cosmetic registration"));
			}
			array = missingHats;
			foreach (string text2 in array)
			{
				CosmeticResync.Logger.LogWarning((object)("Hat " + text2 + " is still unavailable after waiting for cosmetic registration"));
			}
			if (missingAccessories.Length == 0 && missingHats.Length == 0)
			{
				CosmeticResync.Logger.LogError((object)"Customization was not ready in time; could not resync MultiCosmetic");
			}
			else
			{
				CosmeticResync.Logger.LogError((object)"Could not resync every saved cosmetic after 30 seconds; preserving saved names and skipping MultiCosmetic saves");
			}
			_restoreQueued = false;
		}

		private static RestoreResult TryRestoreSavedCosmetics(out string[] missingAccessories, out string[] missingHats)
		{
			missingAccessories = new string[0];
			missingHats = new string[0];
			if ((Object)(object)MultiCosmetic == (Object)null || _currentData == null)
			{
				CosmeticResync.Logger.LogError((object)"MultiCosmetic is not ready; cannot restore saved cosmetics");
				return RestoreResult.InvalidData;
			}
			if (!PlayerPrefs.HasKey("CosmeticResync_MultiCosmetic"))
			{
				_restorePending = false;
				return RestoreResult.NoSavedData;
			}
			string text = PlayerPrefs.GetString("CosmeticResync_MultiCosmetic");
			if (string.IsNullOrWhiteSpace(text))
			{
				CosmeticResync.Logger.LogWarning((object)"CosmeticResync_MultiCosmetic has no data, nothing to resync");
				_restorePending = false;
				return RestoreResult.InvalidData;
			}
			try
			{
				string[][] array = JsonConvert.DeserializeObject<string[][]>(text);
				if (array == null || array.Length != 2 || array[0] == null || array[1] == null)
				{
					CosmeticResync.Logger.LogWarning((object)"CosmeticResync_MultiCosmetic is malformed; expected two name arrays");
					_restorePending = false;
					return RestoreResult.InvalidData;
				}
				Customization instance = Singleton<Customization>.Instance;
				if (!Object.op_Implicit((Object)(object)instance))
				{
					return RestoreResult.Retry;
				}
				int[] indices;
				bool flag = TryGetCosmeticIndices(instance.accessories, array[0], out indices, out missingAccessories);
				int[] indices2;
				bool flag2 = TryGetCosmeticIndices(instance.hats, array[1], out indices2, out missingHats);
				if (!flag || !flag2)
				{
					return RestoreResult.Retry;
				}
				_currentData.SetValue(MultiCosmetic, new int[2][] { indices, indices2 });
				_restorePending = false;
				CosmeticResync.Logger.LogInfo((object)$"Loaded {indices.Length} accessories and {indices2.Length} hats");
				return RestoreResult.Restored;
			}
			catch (JsonException)
			{
				CosmeticResync.Logger.LogWarning((object)"Failed to deserialize CosmeticResync_MultiCosmetic");
				_restorePending = false;
				return RestoreResult.InvalidData;
			}
		}

		private static bool TryGetCosmeticIndices(CustomizationOption[] options, string[] names, out int[] indices, out string[] missingNames)
		{
			Dictionary<string, int> dictionary = new Dictionary<string, int>();
			if (options != null)
			{
				for (int i = 0; i < options.Length; i++)
				{
					CustomizationOption val = options[i];
					if ((Object)(object)val != (Object)null && !string.IsNullOrEmpty(((Object)val).name))
					{
						dictionary[((Object)val).name] = i;
					}
				}
			}
			List<int> list = new List<int>();
			List<string> list2 = new List<string>();
			foreach (string text in names)
			{
				if (!string.IsNullOrEmpty(text) && dictionary.TryGetValue(text, out var value))
				{
					list.Add(value);
				}
				else
				{
					list2.Add(text ?? "<null>");
				}
			}
			indices = list.ToArray();
			missingNames = list2.ToArray();
			return missingNames.Length == 0;
		}

		private static bool TryGetCosmeticNames(CustomizationOption[] options, int[] indices, string cosmeticType, out string[] names)
		{
			List<string> list = new List<string>();
			names = new string[0];
			if (options == null || indices == null)
			{
				CosmeticResync.Logger.LogError((object)(cosmeticType + " options or indices are null; cannot save MultiCosmetic names"));
				return false;
			}
			int num = options.Length;
			foreach (int num2 in indices)
			{
				if (num2 >= 0 && num2 < num && (Object)(object)options[num2] != (Object)null && !string.IsNullOrEmpty(((Object)options[num2]).name))
				{
					list.Add(((Object)options[num2]).name);
					continue;
				}
				CosmeticResync.Logger.LogError((object)$"{cosmeticType} index #{num2} cannot be resolved; preserving previously saved MultiCosmetic names");
				return false;
			}
			names = list.ToArray();
			return true;
		}
	}
}