Exemple #1
0
        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);
        }
Exemple #3
0
        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);
        }
Exemple #4
0
        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);
            }
Exemple #5
0
        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);
        }
Exemple #6
0
        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);
            }
        }