// Copyright (c) 2022 bradson // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. /*using System.Linq; namespace FisheryLib; /// A PatchClassProcessor used to turn and Fishery attributes on a class/type into patches public class FisheryPatchClassProcessor : PatchClassProcessor { new readonly HarmonyMethod? containerAttributes; new readonly Dictionary? auxilaryMethods; new readonly List? patchMethods; new static readonly List auxilaryTypes = new() { typeof(HarmonyPrepare), typeof(HarmonyCleanup), typeof(HarmonyTargetMethod), typeof(HarmonyTargetMethods) }; /// Creates a patch class processor by pointing out a class. Similar to PatchAll() but without searching through all classes. This version supports attributes added by Fishery. /// The Harmony instance /// The class to process (need to have at least a [HarmonyPatch] attribute) public FisheryPatchClassProcessor(Harmony instance, Type type) : base(instance, type) { Guard.IsNotNull(instance, nameof(instance)); Guard.IsNotNull(type, nameof(type)); //this.instance = instance; //containerType = type; if (HarmonyMethodExtensions.GetFromType(type) is not { Count: not 0 } harmonyAttributes) return; containerAttributes = HarmonyMethod.Merge(harmonyAttributes); containerAttributes.methodType ??= MethodType.Normal; // MethodType default is Normal auxilaryMethods = new(); foreach (var auxType in auxilaryTypes) { var method = PatchTools.GetPatchMethod(containerType, auxType.FullName); if (method is object) auxilaryMethods[auxType] = method; } patchMethods = PatchTools.GetPatchMethods(containerType); foreach (var patchMethod in patchMethods) { var method = patchMethod.info.method; patchMethod.info = containerAttributes.Merge(patchMethod.info); patchMethod.info.method = method; } foreach (var attribute in type.GetCustomAttributes()) { if (attribute is InfixAttribute infixAttribute) { var infixMethod = new InfixMethod(null!, infixAttribute.InfixTarget); } } } public static List GetPatchMethods(Type type) { var attributePatches = new List(); var methodInfos = AccessTools.GetDeclaredMethods(type); for (var i = 0; i < methodInfos.Count; i++) { if (AttributePatch.Create(methodInfos[i]) is { } attributePatch) attributePatches.Add(attributePatch); } return attributePatches; } public static AttributePatch? CreateInfixAttributePatch(MethodInfo patch) { Guard.IsNotNull(patch, nameof(patch)); var customAttributes = patch.GetCustomAttributes(inherit: true); var patchType = GetInfixPatchType(patch.Name, customAttributes); if (patchType is null) return null; if (patchType != FisheryPatchType.ReversePatch && !patch.IsStatic) throw new ArgumentException($"Patch method {patch.FullDescription()} must be static"); var harmonyAttributes = new List(); foreach (var attr in customAttributes) { if (attr.GetType().BaseType.FullName == AttributePatch.harmonyAttributeName) { harmonyAttributes.Add( AccessTools.MakeDeepCopy( AccessTools.Field(attr.GetType(), nameof(HarmonyAttribute.info)).GetValue(attr))); } } var harmonyMethod = HarmonyMethod.Merge(harmonyAttributes); harmonyMethod.method = patch; return new AttributePatch { info = harmonyMethod, type = (HarmonyPatchType)patchType //<-------------------------------------------------------------------------------------------------------------------------------------------- }; } public static IEnumerable HarmonyFields { get; } = typeof(HarmonyMethod).GetFields(AccessTools.allDeclared).Select(f => f.Name).Where(s => s != "method"); public static HarmonyMethod Merge(List attributes) { var harmonyMethod = new HarmonyMethod(); if (attributes == null) { return harmonyMethod; } var resultTrv = Traverse.Create(harmonyMethod); attributes.ForEach(delegate (HarmonyMethod attribute) { var trv = Traverse.Create(attribute); foreach (var f in HarmonyFields) { var value = trv.Field(f).GetValue(); if (value != null && (f != "priority" || (int)value != -1)) { SetValue(resultTrv, f, value); } }; }); return harmonyMethod; } public static void SetValue(Traverse trv, string name, object val) { if (val != null) { var traverse = trv.Field(name); if (name is "methodType" or "reversePatchType") { val = Enum.ToObject(Nullable.GetUnderlyingType(traverse.GetValueType()), (int)val); } traverse.SetValue(val); } } public static FisheryPatchType? GetInfixPatchType(string methodName, object[] allAttributes) { var hashSet = new HashSet(); for (var i = 0; i < allAttributes.Length; i++) { var name = allAttributes[i].GetType().FullName; if (StartsWithAny(name, AttributePrefixes)) hashSet.Add(name.ToUpperInvariant()); } FisheryPatchType? result = null; var array = AttributePatch.allPatchTypes; for (var i = 0; i < array.Length; i++) { var value = array[i]; var text = value.ToString().ToUpperInvariant(); if (text == methodName.ToUpperInvariant() || hashSet.Overlaps(CombineWithAttributePrefixes(text))) { result = (FisheryPatchType)value; //<------------------------------------------------------------------------------------------------------------------------------------------ break; } } return result; } private static (string Namespace, string Prefix)[] AttributePrefixInfoTuples { get; } = new[] { (nameof(HarmonyLib), "Harmony"), (nameof(FisheryLib), "Fishery"), (nameof(FisheryLib), "Infix") }; private static string[] AttributePrefixes { get; } = Array.ConvertAll(AttributePrefixInfoTuples, t => t.Prefix); private static string[] AttributePrefixesWithNamespace { get; } = Array.ConvertAll(AttributePrefixInfoTuples, t => $"{t.Namespace}.{t.Prefix}"); private static IEnumerable CombineWithAttributePrefixes(string text) { foreach (var prefix in AttributePrefixesWithNamespace) yield return prefix + text; } private static bool StartsWithAny(string text, string[] values) { for (var i = 0; i < values.Length; i++) { if (text.StartsWith(values[i])) return true; } return false; } } public enum FisheryPatchType { All, Prefix, Postfix, Infix, Transpiler, Finalizer, ReversePatch }*/