internal static MethodBase GetOriginalMethod(this HarmonyMethod attr) { try { switch (attr.methodType) { case MethodType.Normal: if (attr.methodName is null) { return(null); } return(AccessTools.DeclaredMethod(attr.declaringType, attr.methodName, attr.argumentTypes)); case MethodType.Getter: if (attr.methodName is null) { return(null); } return(AccessTools.DeclaredProperty(attr.declaringType, attr.methodName).GetGetMethod(true)); case MethodType.Setter: if (attr.methodName is null) { return(null); } return(AccessTools.DeclaredProperty(attr.declaringType, attr.methodName).GetSetMethod(true)); case MethodType.Constructor: return(AccessTools.DeclaredConstructor(attr.declaringType, attr.argumentTypes)); case MethodType.StaticConstructor: return(AccessTools.GetDeclaredConstructors(attr.declaringType) .Where(c => c.IsStatic) .FirstOrDefault()); case MethodType.Enumerator: if (attr.methodName is null) { return(null); } var method = AccessTools.DeclaredMethod(attr.declaringType, attr.methodName, attr.argumentTypes); return(AccessTools.EnumeratorMoveNext(method)); } } catch (AmbiguousMatchException ex) { throw new HarmonyException($"Ambiguous match for HarmonyMethod[{attr.Description()}]", ex.InnerException ?? ex); } return(null); }
List <MethodBase> GetBulkMethods() { var isPatchAll = containerType.GetCustomAttributes(true).Any(a => a.GetType().FullName == typeof(HarmonyPatchAll).FullName); if (isPatchAll) { var type = containerAttributes.declaringType; if (type == null) { throw new ArgumentException($"Using {typeof(HarmonyPatchAll).FullName} requires an additional attribute for specifying the Class/Type"); } var list = new List <MethodBase>(); list.AddRange(AccessTools.GetDeclaredConstructors(type).Cast <MethodBase>()); list.AddRange(AccessTools.GetDeclaredMethods(type).Cast <MethodBase>()); var props = AccessTools.GetDeclaredProperties(type); list.AddRange(props.Select(prop => prop.GetGetMethod(true)).Where(method => method != null).Cast <MethodBase>()); list.AddRange(props.Select(prop => prop.GetSetMethod(true)).Where(method => method != null).Cast <MethodBase>()); return(list); } string FailOnResult(IEnumerable <MethodBase> res) { if (res == null) { return("null"); } if (res.Any(m => m == null)) { return("some element was null"); } return(null); } var targetMethods = RunMethod <HarmonyTargetMethods, IEnumerable <MethodBase> >(null, null, FailOnResult); if (targetMethods != null) { return(targetMethods.ToList()); } var result = new List <MethodBase>(); var targetMethod = RunMethod <HarmonyTargetMethod, MethodBase>(null, null, method => method == null ? "null" : null); if (targetMethod != null) { result.Add(targetMethod); } return(result); }
MethodBase GetOriginalMethod() { var attr = containerAttributes; if (attr.declaringType == null) { return(null); } switch (attr.methodType) { case MethodType.Normal: if (attr.methodName == null) { return(null); } return(AccessTools.DeclaredMethod(attr.declaringType, attr.methodName, attr.argumentTypes)); case MethodType.Getter: if (attr.methodName == null) { return(null); } return(AccessTools.DeclaredProperty(attr.declaringType, attr.methodName).GetGetMethod(true)); case MethodType.Setter: if (attr.methodName == null) { return(null); } return(AccessTools.DeclaredProperty(attr.declaringType, attr.methodName).GetSetMethod(true)); case MethodType.Constructor: return(AccessTools.DeclaredConstructor(attr.declaringType, attr.argumentTypes)); case MethodType.StaticConstructor: return(AccessTools.GetDeclaredConstructors(attr.declaringType) .Where(c => c.IsStatic) .FirstOrDefault()); } return(null); }
List <MethodBase> GetBulkMethods() { var isPatchAll = containerType.GetCustomAttributes(true).Any(a => a.GetType().FullName == typeof(HarmonyPatchAll).FullName); if (isPatchAll) { var type = containerAttributes.declaringType; if (type is null) { throw new ArgumentException($"Using {typeof(HarmonyPatchAll).FullName} requires an additional attribute for specifying the Class/Type"); } var list = new List <MethodBase>(); list.AddRange(AccessTools.GetDeclaredConstructors(type).Cast <MethodBase>()); list.AddRange(AccessTools.GetDeclaredMethods(type).Cast <MethodBase>()); var props = AccessTools.GetDeclaredProperties(type); list.AddRange(props.Select(prop => prop.GetGetMethod(true)).Where(method => method is object).Cast <MethodBase>()); list.AddRange(props.Select(prop => prop.GetSetMethod(true)).Where(method => method is object).Cast <MethodBase>()); return(list); }
internal static MethodBase GetOriginalMethod(this HarmonyMethod attr) { switch (attr.methodType) { case MethodType.Normal: if (attr.methodName == null) { return(null); } return(AccessTools.DeclaredMethod(attr.declaringType, attr.methodName, attr.argumentTypes)); case MethodType.Getter: if (attr.methodName == null) { return(null); } return(AccessTools.DeclaredProperty(attr.declaringType, attr.methodName).GetGetMethod(true)); case MethodType.Setter: if (attr.methodName == null) { return(null); } return(AccessTools.DeclaredProperty(attr.declaringType, attr.methodName).GetSetMethod(true)); case MethodType.Constructor: return(AccessTools.DeclaredConstructor(attr.declaringType, attr.argumentTypes)); case MethodType.StaticConstructor: return(AccessTools.GetDeclaredConstructors(attr.declaringType) .Where(c => c.IsStatic) .FirstOrDefault()); } return(null); }
void PrepareType() { var mainPrepareResult = RunMethod <HarmonyPrepare, bool>(true); if (mainPrepareResult == false) { return; } var customOriginals = RunMethod <HarmonyTargetMethods, IEnumerable <MethodBase> >(null); if (customOriginals != null) { originals.Clear(); originals.AddRange(customOriginals); } else { var originalMethodType = containerAttributes.methodType; // MethodType default is Normal if (containerAttributes.methodType == null) { containerAttributes.methodType = MethodType.Normal; } var isPatchAll = container.GetCustomAttributes(true).Any(a => a.GetType().FullName == typeof(HarmonyPatchAll).FullName); if (isPatchAll) { var type = containerAttributes.declaringType; originals.AddRange(AccessTools.GetDeclaredConstructors(type).Cast <MethodBase>()); originals.AddRange(AccessTools.GetDeclaredMethods(type).Cast <MethodBase>()); var props = AccessTools.GetDeclaredProperties(type); originals.AddRange(props.Select(prop => prop.GetGetMethod(true)).Where(method => method != null).Cast <MethodBase>()); originals.AddRange(props.Select(prop => prop.GetSetMethod(true)).Where(method => method != null).Cast <MethodBase>()); } else { var original = RunMethod <HarmonyTargetMethod, MethodBase>(null); if (original == null) { original = GetOriginalMethod(); } if (original == null) { var info = "("; info += "declaringType=" + containerAttributes.declaringType + ", "; info += "methodName =" + containerAttributes.methodName + ", "; info += "methodType=" + originalMethodType + ", "; info += "argumentTypes=" + containerAttributes.argumentTypes.Description(); info += ")"; throw new ArgumentException("No target method specified for class " + container.FullName + " " + info); } originals.Add(original); } } PatchTools.GetPatches(container, out var prefixMethod, out var postfixMethod, out var transpilerMethod, out var finalizerMethod); if (prefix != null) { prefix.method = prefixMethod; } if (postfix != null) { postfix.method = postfixMethod; } if (transpiler != null) { transpiler.method = transpilerMethod; } if (finalizer != null) { finalizer.method = finalizerMethod; } if (prefixMethod != null) { if (prefixMethod.IsStatic == false) { throw new ArgumentException("Patch method " + prefixMethod.FullDescription() + " must be static"); } var prefixAttributes = HarmonyMethodExtensions.GetFromMethod(prefixMethod); containerAttributes.Merge(HarmonyMethod.Merge(prefixAttributes)).CopyTo(prefix); } if (postfixMethod != null) { if (postfixMethod.IsStatic == false) { throw new ArgumentException("Patch method " + postfixMethod.FullDescription() + " must be static"); } var postfixAttributes = HarmonyMethodExtensions.GetFromMethod(postfixMethod); containerAttributes.Merge(HarmonyMethod.Merge(postfixAttributes)).CopyTo(postfix); } if (transpilerMethod != null) { if (transpilerMethod.IsStatic == false) { throw new ArgumentException("Patch method " + transpilerMethod.FullDescription() + " must be static"); } var transpilerAttributes = HarmonyMethodExtensions.GetFromMethod(transpilerMethod); containerAttributes.Merge(HarmonyMethod.Merge(transpilerAttributes)).CopyTo(transpiler); } if (finalizerMethod != null) { if (finalizerMethod.IsStatic == false) { throw new ArgumentException("Patch method " + finalizerMethod.FullDescription() + " must be static"); } var finalizerAttributes = HarmonyMethodExtensions.GetFromMethod(finalizerMethod); containerAttributes.Merge(HarmonyMethod.Merge(finalizerAttributes)).CopyTo(finalizer); } }
/// <summary> /// Get the member specified by the <paramref name="attribute"/>. Throws if the member was not found. /// </summary> /// <exception cref="ArgumentException">Thrown if the member described in the <paramref name="attribute"/> couldn't be found.</exception> /// <exception cref="ArgumentNullException"><paramref name="attribute"/> is <see langword="null"/></exception> internal static MethodBase GetOriginalMethod(HarmonyMethod attribute) { if (attribute == null) { throw new ArgumentNullException(nameof(attribute)); } string GetPatchName() { return(attribute.method?.FullDescription() ?? "Unknown patch"); } MethodBase MakeFailure(string reason) { Logger.Log(Logger.LogChannel.Error, () => $"Failed to process patch {GetPatchName()} - {reason}"); return(null); } if (attribute.declaringType == null) { return(MakeFailure("declaringType cannot be null")); } switch (attribute.methodType) { case MethodType.Normal: { if (string.IsNullOrEmpty(attribute.methodName)) { return(MakeFailure("methodName can't be empty")); } if (attribute.methodName == ".ctor") { Logger.LogText(Logger.LogChannel.Warn, GetPatchName() + " - MethodType.Constructor should be used instead of setting methodName to .ctor"); goto case MethodType.Constructor; } if (attribute.methodName == ".cctor") { Logger.LogText(Logger.LogChannel.Warn, GetPatchName() + " - MethodType.StaticConstructor should be used instead of setting methodName to .cctor"); goto case MethodType.StaticConstructor; } if (attribute.methodName.StartsWith("get_") || attribute.methodName.StartsWith("set_")) { Logger.LogText(Logger.LogChannel.Warn, GetPatchName() + " - MethodType.Getter and MethodType.Setter should be used instead adding get_ and set_ to property names"); } var result = AccessTools.DeclaredMethod(attribute.declaringType, attribute.methodName, attribute.argumentTypes); if (result != null) { return(result); } result = AccessTools.Method(attribute.declaringType, attribute.methodName, attribute.argumentTypes); if (result != null) { Logger.LogText(Logger.LogChannel.Warn, GetPatchName() + $" - Could not find method {attribute.methodName} with {attribute.argumentTypes?.Length ?? 0} parameters in type {attribute.declaringType.FullDescription()}, but it was found in base class of this type {result.DeclaringType.FullDescription()}"); return(result); } return(MakeFailure($"Could not find method {attribute.methodName} with {attribute.argumentTypes.Description()} parameters in type {attribute.declaringType.FullDescription()}")); } case MethodType.Getter: { if (string.IsNullOrEmpty(attribute.methodName)) { return(MakeFailure("methodName can't be empty")); } var result = AccessTools.DeclaredProperty(attribute.declaringType, attribute.methodName); if (result != null) { var getter = result.GetGetMethod(true); if (getter == null) { return(MakeFailure($"Property {attribute.methodName} does not have a Getter")); } return(getter); } result = AccessTools.Property(attribute.declaringType, attribute.methodName); if (result != null) { Logger.LogText(Logger.LogChannel.Warn, GetPatchName() + $" - Could not find property {attribute.methodName} in type {attribute.declaringType.FullDescription()}, but it was found in base class of this type: {result.DeclaringType.FullDescription()}"); var getter = result.GetGetMethod(true); if (getter == null) { return(MakeFailure($"Property {attribute.methodName} does not have a Getter")); } return(getter); } return(MakeFailure($"Could not find property {attribute.methodName} in type {attribute.declaringType.FullDescription()}")); } case MethodType.Setter: { if (string.IsNullOrEmpty(attribute.methodName)) { return(MakeFailure("methodName can't be empty")); } var result = AccessTools.DeclaredProperty(attribute.declaringType, attribute.methodName); if (result != null) { var getter = result.GetSetMethod(true); if (getter == null) { return(MakeFailure($"Property {attribute.methodName} does not have a Setter")); } return(getter); } result = AccessTools.Property(attribute.declaringType, attribute.methodName); if (result != null) { Logger.LogText(Logger.LogChannel.Warn, GetPatchName() + $" - Could not find property {attribute.methodName} in type {attribute.declaringType.FullDescription()}, but it was found in base class of this type: {result.DeclaringType.FullDescription()}"); var getter = result.GetSetMethod(true); if (getter == null) { return(MakeFailure($"Property {attribute.methodName} does not have a Setter")); } return(getter); } return(MakeFailure($"Could not find property {attribute.methodName} in type {attribute.declaringType.FullDescription()}")); } case MethodType.Constructor: { var constructor = AccessTools.DeclaredConstructor(attribute.declaringType, attribute.argumentTypes); if (constructor != null) { return(constructor); } return(MakeFailure($"Could not find constructor with {attribute.argumentTypes.Description()} parameters in type {attribute.declaringType.FullDescription()}")); } case MethodType.StaticConstructor: { var constructor = AccessTools.GetDeclaredConstructors(attribute.declaringType).FirstOrDefault(c => c.IsStatic); if (constructor != null) { return(constructor); } return(MakeFailure($"Could not find static constructor in type {attribute.declaringType.FullDescription()}")); } default: throw new ArgumentOutOfRangeException(nameof(attribute.methodType), attribute.methodType, "Unknown method type"); } }
private void PrepareType() { var mainPrepareResult = RunMethod <HarmonyPrepare, bool>(true); if (mainPrepareResult == false) { return; } var originalMethodType = containerAttributes.methodType; // MethodType default is Normal if (containerAttributes.methodType == null) { containerAttributes.methodType = MethodType.Normal; } var reversePatchAttr = typeof(HarmonyReversePatch).FullName; var reversePatchMethods = container.GetMethods(AccessTools.all).Where(m => m.GetCustomAttributes(true).Any(a => a.GetType().FullName == reversePatchAttr)).ToList(); foreach (var reversePatchMethod in reversePatchMethods) { var attr = containerAttributes.Merge(new HarmonyMethod(reversePatchMethod)); var originalMethod = GetOriginalMethod(attr); var reversePatcher = instance.CreateReversePatcher(originalMethod, reversePatchMethod); reversePatcher.Patch(); } var customOriginals = RunMethod <HarmonyTargetMethods, IEnumerable <MethodBase> >(null); if (customOriginals != null) { originals.Clear(); originals.AddRange(customOriginals); } else { var isPatchAll = container.GetCustomAttributes(true).Any(a => a.GetType().FullName == typeof(HarmonyPatchAll).FullName); if (isPatchAll) { var type = containerAttributes.declaringType; originals.AddRange(AccessTools.GetDeclaredConstructors(type).Cast <MethodBase>()); originals.AddRange(AccessTools.GetDeclaredMethods(type).Cast <MethodBase>()); var props = AccessTools.GetDeclaredProperties(type); originals.AddRange(props.Select(prop => prop.GetGetMethod(true)).Where(method => method != null) .Cast <MethodBase>()); originals.AddRange(props.Select(prop => prop.GetSetMethod(true)).Where(method => method != null) .Cast <MethodBase>()); } else { var original = RunMethod <HarmonyTargetMethod, MethodBase>(null) ?? GetOriginalMethod(containerAttributes); if (original == null) { var info = "("; info += $"declaringType={containerAttributes.declaringType}, "; info += $"methodName ={containerAttributes.methodName}, "; info += $"methodType={originalMethodType}, "; info += $"argumentTypes={containerAttributes.argumentTypes.Description()}"; info += ")"; throw new ArgumentException( $"No target method specified for class {container.FullName} {info}"); } originals.Add(original); } } GetPatches(container, out var prefixMethod, out var postfixMethod, out var transpilerMethod, out var finalizerMethod); if (prefix != null) { prefix.method = prefixMethod; } if (postfix != null) { postfix.method = postfixMethod; } if (transpiler != null) { transpiler.method = transpilerMethod; } if (finalizer != null) { finalizer.method = finalizerMethod; } if (prefixMethod != null) { if (prefixMethod.IsStatic == false) { throw new ArgumentException($"Patch method {prefixMethod.GetID()} must be static"); } var prefixAttributes = HarmonyMethodExtensions.GetFromMethod(prefixMethod); containerAttributes.Merge(HarmonyMethod.Merge(prefixAttributes)).CopyTo(prefix); } if (postfixMethod != null) { if (postfixMethod.IsStatic == false) { throw new ArgumentException($"Patch method {postfixMethod.GetID()} must be static"); } var postfixAttributes = HarmonyMethodExtensions.GetFromMethod(postfixMethod); containerAttributes.Merge(HarmonyMethod.Merge(postfixAttributes)).CopyTo(postfix); } if (transpilerMethod != null) { if (transpilerMethod.IsStatic == false) { throw new ArgumentException($"Patch method {transpilerMethod.GetID()} must be static"); } var transpilerAttributes = HarmonyMethodExtensions.GetFromMethod(transpilerMethod); containerAttributes.Merge(HarmonyMethod.Merge(transpilerAttributes)).CopyTo(transpiler); } if (finalizerMethod != null) { if (finalizerMethod.IsStatic == false) { throw new ArgumentException($"Patch method {finalizerMethod.GetID()} must be static"); } var finalizerAttributes = HarmonyMethodExtensions.GetFromMethod(finalizerMethod); containerAttributes.Merge(HarmonyMethod.Merge(finalizerAttributes)).CopyTo(finalizer); } }