Patch.cs 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. using System;
  2. using System.IO;
  3. using System.Linq;
  4. using System.Reflection;
  5. using System.Reflection.Emit;
  6. using System.Runtime.Serialization;
  7. using System.Runtime.Serialization.Formatters.Binary;
  8. namespace Harmony
  9. {
  10. public static class PatchInfoSerialization
  11. {
  12. class Binder : SerializationBinder
  13. {
  14. public override Type BindToType(string assemblyName, string typeName)
  15. {
  16. var types = new Type[] {
  17. typeof(PatchInfo),
  18. typeof(Patch[]),
  19. typeof(Patch)
  20. };
  21. foreach (var type in types)
  22. if (typeName == type.FullName)
  23. return type;
  24. var typeToDeserialize = Type.GetType(string.Format("{0}, {1}", typeName, assemblyName));
  25. return typeToDeserialize;
  26. }
  27. }
  28. public static byte[] Serialize(this PatchInfo patchInfo)
  29. {
  30. #pragma warning disable XS0001
  31. using (var streamMemory = new MemoryStream())
  32. {
  33. var formatter = new BinaryFormatter();
  34. formatter.Serialize(streamMemory, patchInfo);
  35. return streamMemory.GetBuffer();
  36. }
  37. #pragma warning restore XS0001
  38. }
  39. public static PatchInfo Deserialize(byte[] bytes)
  40. {
  41. var formatter = new BinaryFormatter { Binder = new Binder() };
  42. #pragma warning disable XS0001
  43. var streamMemory = new MemoryStream(bytes);
  44. #pragma warning restore XS0001
  45. return (PatchInfo)formatter.Deserialize(streamMemory);
  46. }
  47. // general sorting by (in that order): before, after, priority and index
  48. public static int PriorityComparer(object obj, int index, int priority, string[] before, string[] after)
  49. {
  50. var trv = Traverse.Create(obj);
  51. var theirOwner = trv.Field("owner").GetValue<string>();
  52. var theirPriority = trv.Field("priority").GetValue<int>();
  53. var theirIndex = trv.Field("index").GetValue<int>();
  54. if (before != null && Array.IndexOf(before, theirOwner) > -1)
  55. return -1;
  56. if (after != null && Array.IndexOf(after, theirOwner) > -1)
  57. return 1;
  58. if (priority != theirPriority)
  59. return -(priority.CompareTo(theirPriority));
  60. return index.CompareTo(theirIndex);
  61. }
  62. }
  63. [Serializable]
  64. public class PatchInfo
  65. {
  66. public Patch[] prefixes;
  67. public Patch[] postfixes;
  68. public Patch[] transpilers;
  69. public PatchInfo()
  70. {
  71. prefixes = new Patch[0];
  72. postfixes = new Patch[0];
  73. transpilers = new Patch[0];
  74. }
  75. public void AddPrefix(MethodInfo patch, string owner, int priority, string[] before, string[] after)
  76. {
  77. var l = prefixes.ToList();
  78. l.Add(new Patch(patch, prefixes.Count() + 1, owner, priority, before, after));
  79. prefixes = l.ToArray();
  80. }
  81. public void RemovePrefix(string owner)
  82. {
  83. if (owner == "*")
  84. {
  85. prefixes = new Patch[0];
  86. return;
  87. }
  88. prefixes = prefixes.Where(patch => patch.owner != owner).ToArray();
  89. }
  90. public void AddPostfix(MethodInfo patch, string owner, int priority, string[] before, string[] after)
  91. {
  92. var l = postfixes.ToList();
  93. l.Add(new Patch(patch, postfixes.Count() + 1, owner, priority, before, after));
  94. postfixes = l.ToArray();
  95. }
  96. public void RemovePostfix(string owner)
  97. {
  98. if (owner == "*")
  99. {
  100. postfixes = new Patch[0];
  101. return;
  102. }
  103. postfixes = postfixes.Where(patch => patch.owner != owner).ToArray();
  104. }
  105. public void AddTranspiler(MethodInfo patch, string owner, int priority, string[] before, string[] after)
  106. {
  107. var l = transpilers.ToList();
  108. l.Add(new Patch(patch, transpilers.Count() + 1, owner, priority, before, after));
  109. transpilers = l.ToArray();
  110. }
  111. public void RemoveTranspiler(string owner)
  112. {
  113. if (owner == "*")
  114. {
  115. transpilers = new Patch[0];
  116. return;
  117. }
  118. transpilers = transpilers.Where(patch => patch.owner != owner).ToArray();
  119. }
  120. public void RemovePatch(MethodInfo patch)
  121. {
  122. prefixes = prefixes.Where(p => p.patch != patch).ToArray();
  123. postfixes = postfixes.Where(p => p.patch != patch).ToArray();
  124. transpilers = transpilers.Where(p => p.patch != patch).ToArray();
  125. }
  126. }
  127. [Serializable]
  128. public class Patch : IComparable
  129. {
  130. readonly public int index;
  131. readonly public string owner;
  132. readonly public int priority;
  133. readonly public string[] before;
  134. readonly public string[] after;
  135. readonly public MethodInfo patch;
  136. public Patch(MethodInfo patch, int index, string owner, int priority, string[] before, string[] after)
  137. {
  138. if (patch is DynamicMethod) throw new Exception("Cannot directly reference dynamic method \"" + patch.FullDescription() + "\" in Harmony. Use a factory method instead that will return the dynamic method.");
  139. this.index = index;
  140. this.owner = owner;
  141. this.priority = priority;
  142. this.before = before;
  143. this.after = after;
  144. this.patch = patch;
  145. }
  146. public MethodInfo GetMethod(MethodBase original)
  147. {
  148. if (patch.ReturnType != typeof(DynamicMethod)) return patch;
  149. if (patch.IsStatic == false) return patch;
  150. var parameters = patch.GetParameters();
  151. if (parameters.Count() != 1) return patch;
  152. if (parameters[0].ParameterType != typeof(MethodBase)) return patch;
  153. // we have a DynamicMethod factory, let's use it
  154. return patch.Invoke(null, new object[] { original }) as DynamicMethod;
  155. }
  156. public override bool Equals(object obj)
  157. {
  158. return ((obj != null) && (obj is Patch) && (patch == ((Patch)obj).patch));
  159. }
  160. public int CompareTo(object obj)
  161. {
  162. return PatchInfoSerialization.PriorityComparer(obj, index, priority, before, after);
  163. }
  164. public override int GetHashCode()
  165. {
  166. return patch.GetHashCode();
  167. }
  168. }
  169. }