Decompiled source of Mercenary Skins Fix v1.2.0

plugins/MercSkinsFix/MercSkinsFix.dll

Decompiled 4 months ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HG;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using MonoMod.RuntimeDetour;
using R2API.Utils;
using RiskOfOptions;
using RiskOfOptions.Options;
using RoR2;
using UnityEngine;
using UnityEngine.AddressableAssets;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: NetworkCompatibility(/*Could not decode attribute arguments.*/)]
[assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")]
[assembly: AssemblyCompany("MercSkinsFix")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+2a7e882ffe00543beb2a5b0726b92ce89842d057")]
[assembly: AssemblyProduct("MercSkinsFix")]
[assembly: AssemblyTitle("MercSkinsFix")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace MercSkinsFix
{
	internal static class DelegateUtils
	{
		public static void Append<TDelegate>(ref TDelegate original, TDelegate append) where TDelegate : Delegate
		{
			original = (TDelegate)Delegate.Combine(original, append);
		}
	}
	internal static class Log
	{
		internal static ManualLogSource _logSource;

		internal static void Init(ManualLogSource logSource)
		{
			_logSource = logSource;
		}

		private static string getLogPrefix(string callerPath, string callerMemberName, int callerLineNumber)
		{
			int num = callerPath.LastIndexOf("MercSkinsFix\\");
			if (num >= 0)
			{
				callerPath = callerPath.Substring(num + "MercSkinsFix\\".Length);
			}
			return $"{callerPath}:{callerLineNumber} ({callerMemberName}) ";
		}

		internal static void Error(object data, [CallerFilePath] string callerPath = "", [CallerMemberName] string callerMemberName = "", [CallerLineNumber] int callerLineNumber = -1)
		{
			_logSource.LogError((object)(getLogPrefix(callerPath, callerMemberName, callerLineNumber) + data));
		}

		internal static void Error_NoCallerPrefix(object data)
		{
			_logSource.LogError(data);
		}

		internal static void Fatal(object data, [CallerFilePath] string callerPath = "", [CallerMemberName] string callerMemberName = "", [CallerLineNumber] int callerLineNumber = -1)
		{
			_logSource.LogFatal((object)(getLogPrefix(callerPath, callerMemberName, callerLineNumber) + data));
		}

		internal static void Fatal_NoCallerPrefix(object data)
		{
			_logSource.LogFatal(data);
		}

		internal static void Info(object data, [CallerFilePath] string callerPath = "", [CallerMemberName] string callerMemberName = "", [CallerLineNumber] int callerLineNumber = -1)
		{
			_logSource.LogInfo((object)(getLogPrefix(callerPath, callerMemberName, callerLineNumber) + data));
		}

		internal static void Info_NoCallerPrefix(object data)
		{
			_logSource.LogInfo(data);
		}

		internal static void Message(object data, [CallerFilePath] string callerPath = "", [CallerMemberName] string callerMemberName = "", [CallerLineNumber] int callerLineNumber = -1)
		{
			_logSource.LogMessage((object)(getLogPrefix(callerPath, callerMemberName, callerLineNumber) + data));
		}

		internal static void Message_NoCallerPrefix(object data)
		{
			_logSource.LogMessage(data);
		}

		internal static void Warning(object data, [CallerFilePath] string callerPath = "", [CallerMemberName] string callerMemberName = "", [CallerLineNumber] int callerLineNumber = -1)
		{
			_logSource.LogWarning((object)(getLogPrefix(callerPath, callerMemberName, callerLineNumber) + data));
		}

		internal static void Warning_NoCallerPrefix(object data)
		{
			_logSource.LogWarning(data);
		}
	}
	[BepInPlugin("Gorakh.MercSkinsFix", "MercSkinsFix", "1.2.0")]
	public class MercSkinsFixPlugin : BaseUnityPlugin
	{
		private delegate ScriptableObject orig_ScriptableObject_CreateInstance(Type type);

		public const string PluginGUID = "Gorakh.MercSkinsFix";

		public const string PluginAuthor = "Gorakh";

		public const string PluginName = "MercSkinsFix";

		public const string PluginVersion = "1.2.0";

		private static readonly Dictionary<Assembly, PluginInfo> _assemblyPluginLookup = new Dictionary<Assembly, PluginInfo>();

		private static readonly Dictionary<SkinDef, Assembly> _skinOwnerAssemblies = new Dictionary<SkinDef, Assembly>();

		private static readonly Dictionary<Assembly, DateTime?> _assemblyTimestampLookup = new Dictionary<Assembly, DateTime?>();

		private Hook _scriptableObjectCreateInstanceHook;

		internal static MercSkinsFixPlugin Instance { get; private set; }

		private void Awake()
		{
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Expected O, but got Unknown
			Stopwatch stopwatch = Stopwatch.StartNew();
			Log.Init(((BaseUnityPlugin)this).Logger);
			Instance = SingletonHelper.Assign<MercSkinsFixPlugin>(Instance, this);
			_scriptableObjectCreateInstanceHook = new Hook((MethodBase)SymbolExtensions.GetMethodInfo((Expression<Action>)(() => ScriptableObject.CreateInstance((Type)null))), (Delegate)new Func<orig_ScriptableObject_CreateInstance, Type, ScriptableObject>(ScriptableObject_CreateInstance));
			stopwatch.Stop();
			Log.Info_NoCallerPrefix($"Initialized in {stopwatch.Elapsed.TotalSeconds:F2} seconds");
			DelegateUtils.Append(ref RoR2Application.onLoad, onLoad);
		}

		private void OnDestroy()
		{
			Instance = SingletonHelper.Unassign<MercSkinsFixPlugin>(Instance, this);
			Hook scriptableObjectCreateInstanceHook = _scriptableObjectCreateInstanceHook;
			if (scriptableObjectCreateInstanceHook != null)
			{
				scriptableObjectCreateInstanceHook.Dispose();
			}
			_scriptableObjectCreateInstanceHook = null;
		}

		private static ScriptableObject ScriptableObject_CreateInstance(orig_ScriptableObject_CreateInstance orig, Type type)
		{
			ScriptableObject val = orig(type);
			SkinDef val2 = (SkinDef)(object)((val is SkinDef) ? val : null);
			if (val2 != null)
			{
				StackTrace stackTrace = new StackTrace();
				for (int i = 0; i < stackTrace.FrameCount; i++)
				{
					Assembly assembly = stackTrace.GetFrame(i)?.GetMethod()?.DeclaringType?.Assembly;
					if (assembly == null || assembly == Assembly.GetExecutingAssembly())
					{
						continue;
					}
					if (!_assemblyPluginLookup.TryGetValue(assembly, out var value))
					{
						value = null;
						foreach (PluginInfo value3 in Chainloader.PluginInfos.Values)
						{
							if (Object.op_Implicit((Object)(object)value3.Instance) && ((object)value3.Instance).GetType().Assembly == assembly)
							{
								value = value3;
								break;
							}
						}
						_assemblyPluginLookup.Add(assembly, value);
					}
					if (value == null)
					{
						continue;
					}
					if (!_assemblyTimestampLookup.ContainsKey(assembly))
					{
						DateTime? value2 = null;
						string location = assembly.Location;
						if (!string.IsNullOrEmpty(location) && File.Exists(location))
						{
							try
							{
								using FileStream fileStream = File.Open(location, FileMode.Open, FileAccess.Read, FileShare.Read);
								byte[] array = new byte[2048];
								int num = fileStream.Read(array, 0, 2048);
								if (num >= 60)
								{
									int num2 = BitConverter.ToInt32(array, 60) + 8;
									if (num >= num2 + 4)
									{
										int num3 = BitConverter.ToInt32(array, num2);
										DateTime dateTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(num3);
										if (dateTime > new DateTime(2000, 1, 1, 0, 0, 0, DateTimeKind.Utc) && dateTime < DateTime.UtcNow)
										{
											value2 = dateTime;
										}
									}
								}
							}
							catch (Exception arg)
							{
								Log.Error_NoCallerPrefix($"Failed to determine assembly date for {assembly.GetName()}: {arg}");
							}
						}
						_assemblyTimestampLookup.Add(assembly, value2);
					}
					_skinOwnerAssemblies.Add(val2, assembly);
					break;
				}
			}
			return val;
		}

		private void onLoad()
		{
			//IL_0013: 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_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_003f: 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)
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_04da: Unknown result type (might be due to invalid IL or missing references)
			//IL_04e1: Expected O, but got Unknown
			//IL_0512: Unknown result type (might be due to invalid IL or missing references)
			//IL_0521: Unknown result type (might be due to invalid IL or missing references)
			//IL_0250: Unknown result type (might be due to invalid IL or missing references)
			//IL_0255: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0413: Expected O, but got Unknown
			SkinDef[] array = (SkinDef[])(object)new SkinDef[3]
			{
				Addressables.LoadAssetAsync<SkinDef>((object)"RoR2/Base/Merc/skinMercDefault.asset").WaitForCompletion(),
				Addressables.LoadAssetAsync<SkinDef>((object)"RoR2/Base/Merc/skinMercAlt.asset").WaitForCompletion(),
				Addressables.LoadAssetAsync<SkinDef>((object)"RoR2/Base/Merc/skinMercAltPrisoner.asset").WaitForCompletion()
			};
			GameObject val = BodyCatalog.FindBodyPrefab("MercBody");
			GameObject gameObject = ((Component)val.GetComponent<ModelLocator>().modelTransform).gameObject;
			Renderer[] renderers = gameObject.GetComponentsInChildren<Renderer>(true);
			Renderer[] array2 = new Renderer[5];
			Transform obj = gameObject.transform.Find("MercArmature/ROOT/base/stomach/chest/PreDashEffect/ContractingEnergy");
			array2[0] = ((obj != null) ? ((Component)obj).GetComponent<Renderer>() : null);
			Transform obj2 = gameObject.transform.Find("MercArmature/ROOT/base/stomach/chest/PreDashEffect/FireEmission");
			array2[1] = ((obj2 != null) ? ((Component)obj2).GetComponent<Renderer>() : null);
			Transform obj3 = gameObject.transform.Find("MercArmature/ROOT/base/stomach/chest/PreDashEffect/BrightRing");
			array2[2] = ((obj3 != null) ? ((Component)obj3).GetComponent<Renderer>() : null);
			Transform obj4 = gameObject.transform.Find("MercMesh");
			array2[3] = ((obj4 != null) ? ((Component)obj4).GetComponent<Renderer>() : null);
			Transform obj5 = gameObject.transform.Find("MercSwordMesh");
			array2[4] = ((obj5 != null) ? ((Component)obj5).GetComponent<Renderer>() : null);
			Renderer[] preDevotionRenderersList = (Renderer[])(object)array2;
			SkinDef[] bodySkins = BodyCatalog.GetBodySkins(BodyCatalog.FindBodyIndex(val));
			Dictionary<string, HashSet<string>> dictionary = new Dictionary<string, HashSet<string>>();
			((BaseUnityPlugin)this).Config.SaveOnConfigSet = false;
			SkinDef[] array3 = bodySkins;
			foreach (SkinDef skin in array3)
			{
				if (Array.IndexOf(array, skin) >= 0)
				{
					continue;
				}
				Assembly valueSafe = GeneralExtensions.GetValueSafe<SkinDef, Assembly>(_skinOwnerAssemblies, skin);
				PluginInfo val2 = ((valueSafe != null) ? GeneralExtensions.GetValueSafe<Assembly, PluginInfo>(_assemblyPluginLookup, valueSafe) : null);
				string key = "Unknown";
				bool flag = false;
				if (val2 != null && valueSafe != null)
				{
					if (_assemblyTimestampLookup.TryGetValue(valueSafe, out var value) && value.HasValue)
					{
						DateTime dateTime = new DateTime(2024, 5, 20, 0, 0, 0, DateTimeKind.Utc);
						if (value.Value < dateTime)
						{
							flag = true;
						}
					}
					key = val2.Metadata.Name + " (" + Path.GetFileName(valueSafe.Location) + ")";
				}
				string text;
				if (!string.IsNullOrEmpty(skin.nameToken))
				{
					text = ((!string.IsNullOrEmpty(((Object)skin).name)) ? (Language.GetString(skin.nameToken, "en") + " (" + ((Object)skin).name + ")") : Language.GetString(skin.nameToken, "en"));
				}
				else if (string.IsNullOrEmpty(((Object)skin).name))
				{
					SkinIndex skinIndex = skin.skinIndex;
					text = ((object)(SkinIndex)(ref skinIndex)).ToString();
				}
				else
				{
					text = ((Object)skin).name;
				}
				string text2 = Regex.Replace(text, "<[^<>]+>", string.Empty);
				if (!string.IsNullOrEmpty(text2))
				{
					text = text2;
				}
				text = text.FilterConfigKey();
				if (string.IsNullOrEmpty(text))
				{
					Random random = new Random(skin.nameToken.GetHashCode());
					StringBuilder stringBuilder = StringBuilderPool.RentStringBuilder();
					byte[] array4 = new byte[16];
					random.NextBytes(array4);
					for (int j = 0; j < array4.Length; j++)
					{
						stringBuilder.Append(array4[j].ToString("X2"));
					}
					text = $"INVALID SKIN NAME {stringBuilder}";
					StringBuilderPool.ReturnStringBuilder(stringBuilder);
				}
				if (dictionary.TryGetValue(key, out var value2))
				{
					if (!value2.Add(text))
					{
						string arg = text;
						int num = 1;
						do
						{
							text = $"{arg} ({num})";
							num++;
						}
						while (!value2.Add(text));
					}
				}
				else
				{
					dictionary.Add(key, new HashSet<string> { text });
				}
				ConfigEntry<bool> obj6 = ((BaseUnityPlugin)this).Config.Bind<bool>(key.FilterConfigKey(), "Fix " + text, flag, "Apply the fix to this skin");
				ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(obj6), "Gorakh.MercSkinsFix", "Mercenary Skins Fix");
				if (obj6.Value)
				{
					setApplyToSkin(apply: true);
				}
				obj6.SettingChanged += delegate(object s, EventArgs e)
				{
					bool applyToSkin = (bool)((SettingChangedEventArgs)((e is SettingChangedEventArgs) ? e : null)).ChangedSetting.BoxedValue;
					setApplyToSkin(applyToSkin);
				};
				void setApplyToSkin(bool apply)
				{
					Renderer[] array5 = (apply ? renderers : preDevotionRenderersList);
					Renderer[] array6 = (apply ? preDevotionRenderersList : renderers);
					for (int k = 0; k < skin.rendererInfos.Length; k++)
					{
						ref RendererInfo reference = ref skin.rendererInfos[k];
						if (Object.op_Implicit((Object)(object)reference.renderer))
						{
							int num2 = Array.IndexOf(array5, reference.renderer);
							if (num2 >= 0 && num2 < array6.Length)
							{
								Log.Info($"Changing {((Object)skin).name} RendererInfo renderer {reference.renderer} -> {array6[num2]}", "E:\\Git\\RoR2\\MercSkinsFix\\MercSkinsFix\\MercSkinsFixPlugin.cs", "onLoad", 206);
								reference.renderer = array6[num2];
							}
						}
					}
					for (int l = 0; l < skin.meshReplacements.Length; l++)
					{
						ref MeshReplacement reference2 = ref skin.meshReplacements[l];
						if (Object.op_Implicit((Object)(object)reference2.renderer))
						{
							int num3 = Array.IndexOf(array5, reference2.renderer);
							if (num3 >= 0 && num3 < array6.Length)
							{
								Log.Info($"Changing {((Object)skin).name} MeshReplacement renderer {reference2.renderer} -> {array6[num3]}", "E:\\Git\\RoR2\\MercSkinsFix\\MercSkinsFix\\MercSkinsFixPlugin.cs", "onLoad", 220);
								reference2.renderer = array6[num3];
							}
						}
					}
					if (skin.runtimeSkin != null)
					{
						skin.runtimeSkin = null;
						skin.Bake();
					}
				}
			}
			((BaseUnityPlugin)this).Config.SaveOnConfigSet = false;
			((BaseUnityPlugin)this).Config.Save();
			ModSettingsManager.SetModDescription("Fix broken Mercenary skins after the Devotion update", "Gorakh.MercSkinsFix", "Mercenary Skins Fix");
			FileInfo fileInfo = null;
			DirectoryInfo directoryInfo = new DirectoryInfo(Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location));
			do
			{
				FileInfo[] files = directoryInfo.GetFiles("icon.png", SearchOption.TopDirectoryOnly);
				if (files != null && files.Length != 0)
				{
					fileInfo = files[0];
					break;
				}
				directoryInfo = directoryInfo.Parent;
			}
			while (directoryInfo != null && !string.Equals(directoryInfo.Name, "plugins", StringComparison.OrdinalIgnoreCase));
			if (fileInfo != null)
			{
				Texture2D val3 = new Texture2D(256, 256);
				if (ImageConversion.LoadImage(val3, File.ReadAllBytes(fileInfo.FullName)))
				{
					ModSettingsManager.SetModIcon(Sprite.Create(val3, new Rect(0f, 0f, (float)((Texture)val3).width, (float)((Texture)val3).height), new Vector2(0.5f, 0.5f)), "Gorakh.MercSkinsFix", "Mercenary Skins Fix");
				}
			}
			Hook scriptableObjectCreateInstanceHook = _scriptableObjectCreateInstanceHook;
			if (scriptableObjectCreateInstanceHook != null)
			{
				scriptableObjectCreateInstanceHook.Dispose();
			}
			_scriptableObjectCreateInstanceHook = null;
		}
	}
	public static class StringExtensions
	{
		private static readonly char[] _invalidConfigDefinitionChars = (char[])AccessTools.DeclaredField(typeof(ConfigDefinition), "_invalidConfigChars")?.GetValue(null);

		public static string FilterChars(this string str, char[] invalidChars)
		{
			if (str.IndexOfAny(invalidChars) == -1)
			{
				return str;
			}
			StringBuilder stringBuilder = StringBuilderPool.RentStringBuilder();
			foreach (char value in str)
			{
				if (Array.IndexOf(invalidChars, value) == -1)
				{
					stringBuilder.Append(value);
				}
			}
			string result = stringBuilder.ToString();
			StringBuilderPool.ReturnStringBuilder(stringBuilder);
			return result;
		}

		public static string FilterConfigKey(this string key)
		{
			if (_invalidConfigDefinitionChars == null)
			{
				Log.Error("_invalidConfigDefinitionChars is null", "E:\\Git\\RoR2\\MercSkinsFix\\MercSkinsFix\\StringExtensions.cs", "FilterConfigKey", 36);
				return key;
			}
			return key.FilterChars(_invalidConfigDefinitionChars);
		}
	}
}