Decompiled source of UnityHotReload v1.0.1

UnityHotReload.dll

Decompiled 3 months 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.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Logging;
using Microsoft.CodeAnalysis;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Collections.Generic;
using MonoMod.Cil;
using MonoMod.RuntimeDetour;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")]
[assembly: AssemblyCompany("UnityHotReload")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+bcfa4c369b6149b8781209a4d6a848e88aad5aca")]
[assembly: AssemblyProduct("UnityHotReload")]
[assembly: AssemblyTitle("UnityHotReload")]
[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 UnityHotReloadNS
{
	internal static class Log
	{
		private static ManualLogSource _logSource;

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

		internal static void Debug(object data)
		{
			_logSource.LogDebug(data);
		}

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

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

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

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

		internal static void Warning(object data)
		{
			_logSource.LogWarning(data);
		}
	}
	public static class UnityHotReload
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static Manipulator <0>__GatherOriginalReferences;

			public static Manipulator <1>__GatherInstructionsFromNewMethodAndMakeThemReferenceOriginalAssembly;
		}

		private const BindingFlags allFlags = (BindingFlags)(-1);

		internal static int CompilationCount = 0;

		private static List<ILHook> _activeILHooks = new List<ILHook>();

		private static Assembly OriginalAss = null;

		private static Dictionary<string, TypeReference> OriginalAssTypeRefs = new Dictionary<string, TypeReference>();

		private static Dictionary<string, MethodReference> OriginalAssMethodRefs = new Dictionary<string, MethodReference>();

		private static Dictionary<string, FieldReference> OriginalAssFieldRefs = new Dictionary<string, FieldReference>();

		private static Dictionary<string, CallSite> OriginalAssCallSites = new Dictionary<string, CallSite>();

		public static void LoadNewAssemblyVersion(Assembly originalAss, string pathOfTheNewAssembly)
		{
			LoadNewAssemblyVersionInternal(originalAss, pathOfTheNewAssembly);
		}

		private static void LoadNewAssemblyVersionInternal(Assembly originalAss, string path)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Expected O, but got Unknown
			//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_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Expected O, but got Unknown
			//IL_0162: Unknown result type (might be due to invalid IL or missing references)
			//IL_016c: Expected O, but got Unknown
			//IL_0157: 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_0162: Expected O, but got Unknown
			//IL_0193: Unknown result type (might be due to invalid IL or missing references)
			//IL_019d: Expected O, but got Unknown
			//IL_0188: Unknown result type (might be due to invalid IL or missing references)
			//IL_018d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0193: Expected O, but got Unknown
			//IL_01bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c6: Expected O, but got Unknown
			//IL_01c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cb: Expected O, but got Unknown
			OriginalAss = originalAss;
			DefaultAssemblyResolver val = new DefaultAssemblyResolver();
			((BaseAssemblyResolver)val).AddSearchDirectory(Paths.ManagedPath);
			((BaseAssemblyResolver)val).AddSearchDirectory(Paths.BepInExAssemblyDirectory);
			Log.Debug(path);
			AssemblyDefinition val2 = AssemblyDefinition.ReadAssembly(path, new ReaderParameters
			{
				AssemblyResolver = (IAssemblyResolver)(object)val,
				ReadSymbols = true
			});
			try
			{
				((AssemblyNameReference)val2.Name).Name = $"{originalAss.GetName().Name}_HotReload_{++CompilationCount}";
				Assembly assembly;
				using (MemoryStream memoryStream = new MemoryStream())
				{
					val2.Write((Stream)memoryStream);
					assembly = Assembly.Load(memoryStream.ToArray());
				}
				Log.Debug("originalAss: " + originalAss.GetName().Name);
				Log.Debug("newAss: " + assembly.GetName().Name);
				foreach (Type item in GetTypesSafe(assembly))
				{
					MethodInfo[] methods = item.GetMethods((BindingFlags)(-1));
					foreach (MethodInfo method in methods)
					{
						Type type = originalAss.GetType(item.FullName);
						MethodInfo methodInfo = GetMethodResolveAmbiguous(method, type);
						if (methodInfo == null)
						{
							methodInfo = method;
						}
						try
						{
							List<ILHook> activeILHooks = _activeILHooks;
							MethodInfo methodInfo2 = methodInfo;
							object obj = <>O.<0>__GatherOriginalReferences;
							if (obj == null)
							{
								Manipulator val3 = GatherOriginalReferences;
								<>O.<0>__GatherOriginalReferences = val3;
								obj = (object)val3;
							}
							activeILHooks.Add(new ILHook((MethodBase)methodInfo2, (Manipulator)obj));
							List<ILHook> activeILHooks2 = _activeILHooks;
							MethodInfo methodInfo3 = method;
							object obj2 = <>O.<1>__GatherInstructionsFromNewMethodAndMakeThemReferenceOriginalAssembly;
							if (obj2 == null)
							{
								Manipulator val4 = GatherInstructionsFromNewMethodAndMakeThemReferenceOriginalAssembly;
								<>O.<1>__GatherInstructionsFromNewMethodAndMakeThemReferenceOriginalAssembly = val4;
								obj2 = (object)val4;
							}
							activeILHooks2.Add(new ILHook((MethodBase)methodInfo3, (Manipulator)obj2));
							if (!(methodInfo != method))
							{
								continue;
							}
							_activeILHooks.Add(new ILHook((MethodBase)methodInfo, (Manipulator)delegate(ILContext il)
							{
								//IL_0001: Unknown result type (might be due to invalid IL or missing references)
								//IL_0007: Expected O, but got Unknown
								//IL_000c: 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_0042: Unknown result type (might be due to invalid IL or missing references)
								ILCursor val5 = new ILCursor(il);
								for (int j = 0; j < ((MethodReference)il.Method).Parameters.Count; j++)
								{
									val5.Emit(OpCodes.Ldarg, j);
								}
								val5.Emit(OpCodes.Call, (MethodBase)method);
								val5.Emit(OpCodes.Ret);
							}));
						}
						catch (Exception arg)
						{
							Log.Error($"Failed to hot reload method: {item.FullName}.{method.Name}\n{arg}");
						}
					}
				}
			}
			finally
			{
				((IDisposable)val2)?.Dispose();
			}
		}

		private static MethodInfo GetMethodResolveAmbiguous(MethodReference methodRef, Type originalType)
		{
			List<MethodInfo> list = (from m in originalType.GetMethods((BindingFlags)(-1))
				where m.Name == ((MemberReference)methodRef).Name
				select m).ToList();
			if (list.Count == 1)
			{
				return list[0];
			}
			if (list.Count > 1)
			{
				foreach (MethodInfo item in list)
				{
					ParameterInfo[] parameters = item.GetParameters();
					Collection<ParameterDefinition> parameters2 = methodRef.Parameters;
					if (parameters.Length != parameters2.Count)
					{
						continue;
					}
					bool flag = true;
					for (int i = 0; i < parameters.Length; i++)
					{
						if (parameters[i].ParameterType.FullName != ((MemberReference)((ParameterReference)parameters2[i]).ParameterType).FullName)
						{
							flag = false;
							break;
						}
					}
					if (flag)
					{
						Log.Debug("Resolved ambiguous method: " + originalType.FullName + "." + ((MemberReference)methodRef).Name);
						return item;
					}
				}
			}
			return null;
		}

		private static MethodInfo GetMethodResolveAmbiguous(MethodInfo method, Type originalType)
		{
			List<MethodInfo> list = (from m in originalType.GetMethods((BindingFlags)(-1))
				where m.Name == method.Name
				select m).ToList();
			if (list.Count == 1)
			{
				return list[0];
			}
			if (list.Count > 1)
			{
				foreach (MethodInfo item in list)
				{
					ParameterInfo[] parameters = item.GetParameters();
					ParameterInfo[] parameters2 = method.GetParameters();
					if (parameters.Length != parameters2.Length)
					{
						continue;
					}
					bool flag = true;
					for (int i = 0; i < parameters.Length; i++)
					{
						if (parameters[i].ParameterType.ToString() != parameters2[i].ParameterType.ToString())
						{
							flag = false;
							break;
						}
					}
					if (flag)
					{
						Log.Debug("Resolved ambiguous method: " + originalType.FullName + "." + method.Name);
						return item;
					}
				}
			}
			return null;
		}

		private static IEnumerable<Type> GetTypesSafe(Assembly ass)
		{
			try
			{
				return ass.GetTypes();
			}
			catch (ReflectionTypeLoadException ex)
			{
				StringBuilder stringBuilder = new StringBuilder();
				stringBuilder.AppendLine("\r\n-- LoaderExceptions --");
				Exception[] loaderExceptions = ex.LoaderExceptions;
				foreach (Exception ex2 in loaderExceptions)
				{
					stringBuilder.AppendLine(ex2.ToString());
				}
				stringBuilder.AppendLine("\r\n-- StackTrace --");
				stringBuilder.AppendLine(ex.StackTrace);
				Log.Error(stringBuilder.ToString());
				return ex.Types.Where((Type x) => x != null);
			}
		}

		private static void GatherOriginalReferences(ILContext il)
		{
			//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)
			Enumerator<Instruction> enumerator = il.Instrs.GetEnumerator();
			try
			{
				while (enumerator.MoveNext())
				{
					Instruction current = enumerator.Current;
					object operand = current.Operand;
					TypeReference val = (TypeReference)((operand is TypeReference) ? operand : null);
					if (val != null)
					{
						if (!OriginalAssTypeRefs.ContainsKey(((MemberReference)val).FullName))
						{
							OriginalAssTypeRefs.Add(((MemberReference)val).FullName, val);
						}
						continue;
					}
					object operand2 = current.Operand;
					MethodReference val2 = (MethodReference)((operand2 is MethodReference) ? operand2 : null);
					if (val2 != null)
					{
						if (!OriginalAssMethodRefs.ContainsKey(((MemberReference)val2).FullName))
						{
							OriginalAssMethodRefs.Add(((MemberReference)val2).FullName, val2);
						}
						continue;
					}
					object operand3 = current.Operand;
					FieldReference val3 = (FieldReference)((operand3 is FieldReference) ? operand3 : null);
					if (val3 != null)
					{
						if (!OriginalAssFieldRefs.ContainsKey(((MemberReference)val3).FullName))
						{
							OriginalAssFieldRefs.Add(((MemberReference)val3).FullName, val3);
						}
						continue;
					}
					object operand4 = current.Operand;
					CallSite val4 = (CallSite)((operand4 is CallSite) ? operand4 : null);
					if (val4 != null && !OriginalAssCallSites.ContainsKey(val4.FullName))
					{
						OriginalAssCallSites.Add(val4.FullName, val4);
					}
				}
			}
			finally
			{
				((IDisposable)enumerator).Dispose();
			}
		}

		private static void GatherInstructionsFromNewMethodAndMakeThemReferenceOriginalAssembly(ILContext il)
		{
			//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)
			Enumerator<Instruction> enumerator = il.Instrs.GetEnumerator();
			try
			{
				while (enumerator.MoveNext())
				{
					Instruction current = enumerator.Current;
					object operand = current.Operand;
					object operand2 = current.Operand;
					TypeReference val = (TypeReference)((operand2 is TypeReference) ? operand2 : null);
					if (val != null)
					{
						if (!OriginalAssTypeRefs.ContainsKey(((MemberReference)val).FullName))
						{
							Type type = OriginalAss.GetType(((MemberReference)val).FullName);
							if (type != null)
							{
								Log.Debug("Adding missing type ref: " + ((MemberReference)val).FullName + " / " + ((ModuleReference)((MemberReference)il.Method).Module).Name);
								OriginalAssTypeRefs.Add(((MemberReference)val).FullName, il.Import(type));
							}
						}
						if (OriginalAssTypeRefs.TryGetValue(((MemberReference)val).FullName, out var value))
						{
							operand = value;
						}
					}
					else
					{
						object operand3 = current.Operand;
						MethodReference val2 = (MethodReference)((operand3 is MethodReference) ? operand3 : null);
						if (val2 != null)
						{
							if (!OriginalAssMethodRefs.ContainsKey(((MemberReference)val2).FullName))
							{
								Type type2 = OriginalAss.GetType(((MemberReference)((MemberReference)val2).DeclaringType).FullName);
								if (type2 != null)
								{
									MethodInfo methodResolveAmbiguous = GetMethodResolveAmbiguous(val2, type2);
									if (methodResolveAmbiguous != null)
									{
										Log.Debug("Adding missing method ref: " + ((MemberReference)val2).FullName + " / " + ((ModuleReference)((MemberReference)il.Method).Module).Name);
										OriginalAssMethodRefs.Add(((MemberReference)val2).FullName, il.Import((MethodBase)methodResolveAmbiguous));
									}
								}
							}
							if (OriginalAssMethodRefs.TryGetValue(((MemberReference)val2).FullName, out var value2))
							{
								operand = value2;
							}
						}
						else
						{
							object operand4 = current.Operand;
							FieldReference val3 = (FieldReference)((operand4 is FieldReference) ? operand4 : null);
							if (val3 != null)
							{
								if (!OriginalAssFieldRefs.ContainsKey(((MemberReference)val3).FullName))
								{
									FieldInfo fieldInfo = OriginalAss.GetType(((MemberReference)((MemberReference)val3).DeclaringType).FullName)?.GetField(((MemberReference)val3).Name, (BindingFlags)(-1));
									if (fieldInfo != null)
									{
										Log.Debug("Adding missing field ref: " + ((MemberReference)val3).FullName + " / " + ((ModuleReference)((MemberReference)il.Method).Module).Name);
										OriginalAssFieldRefs.Add(((MemberReference)val3).FullName, il.Import(fieldInfo));
									}
								}
								if (OriginalAssFieldRefs.TryGetValue(((MemberReference)val3).FullName, out var value3))
								{
									operand = value3;
								}
							}
							else
							{
								object operand5 = current.Operand;
								CallSite val4 = (CallSite)((operand5 is CallSite) ? operand5 : null);
								if (val4 != null && OriginalAssCallSites.TryGetValue(val4.FullName, out var value4))
								{
									operand = value4;
								}
							}
						}
					}
					current.Operand = operand;
				}
			}
			finally
			{
				((IDisposable)enumerator).Dispose();
			}
		}
	}
	[BepInPlugin("iDeathHD.UnityHotReload", "UnityHotReload", "1.0.1")]
	public class UnityHotReloadPlugin : BaseUnityPlugin
	{
		public const string PluginGUID = "iDeathHD.UnityHotReload";

		public const string PluginAuthor = "iDeathHD";

		public const string PluginName = "UnityHotReload";

		public const string PluginVersion = "1.0.1";

		public void Awake()
		{
			Log.Init(((BaseUnityPlugin)this).Logger);
		}
	}
}