Upload from upload_mods.ps1
This commit is contained in:
19
Scripts/Utilities/Modular/IModuleProcessor.cs
Normal file
19
Scripts/Utilities/Modular/IModuleProcessor.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using KFCommonUtilityLib.Scripts.StaticManagers;
|
||||
using Mono.Cecil.Cil;
|
||||
using Mono.Cecil;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace KFCommonUtilityLib
|
||||
{
|
||||
public interface IModuleProcessor
|
||||
{
|
||||
void InitModules(ModuleManipulator manipulator, Type targetType, Type baseType, params Type[] moduleTypes);
|
||||
bool BuildConstructor(ModuleManipulator manipulator, MethodDefinition mtddef_ctor);
|
||||
Type GetModuleTypeByName(string name);
|
||||
bool MatchSpecialArgs(ParameterDefinition par, MethodDefinition mtddef_target, MethodPatchInfo mtdpinf_derived, int moduleIndex, List<Instruction> list_inst_pars, ILProcessor il);
|
||||
}
|
||||
}
|
||||
80
Scripts/Utilities/Modular/ItemActionDataModuleProcessor.cs
Normal file
80
Scripts/Utilities/Modular/ItemActionDataModuleProcessor.cs
Normal file
@@ -0,0 +1,80 @@
|
||||
using Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace KFCommonUtilityLib
|
||||
{
|
||||
public class ItemActionDataModuleProcessor : IModuleProcessor
|
||||
{
|
||||
TypeDefinition typedef_newAction;
|
||||
Type[] arr_type_actions;
|
||||
FieldDefinition[] arr_flddef_actions;
|
||||
bool[] arr_hasdata;
|
||||
FieldDefinition[] arr_flddef_actiondatas;
|
||||
public ItemActionDataModuleProcessor(TypeDefinition typedef_newAction, Type[] arr_type_actions, FieldDefinition[] arr_flddef_actions, bool[] arr_hasdata, out FieldDefinition[] arr_flddef_actiondatas)
|
||||
{
|
||||
this.typedef_newAction = typedef_newAction;
|
||||
this.arr_type_actions = arr_type_actions;
|
||||
this.arr_flddef_actions = arr_flddef_actions;
|
||||
this.arr_hasdata = arr_hasdata;
|
||||
this.arr_flddef_actiondatas = new FieldDefinition[arr_type_actions.Length];
|
||||
arr_flddef_actiondatas = this.arr_flddef_actiondatas;
|
||||
}
|
||||
|
||||
public bool BuildConstructor(ModuleManipulator manipulator, MethodDefinition mtddef_ctor)
|
||||
{
|
||||
mtddef_ctor.Parameters.Add(new ParameterDefinition("_inventoryData", Mono.Cecil.ParameterAttributes.None, manipulator.module.ImportReference(typeof(ItemInventoryData))));
|
||||
mtddef_ctor.Parameters.Add(new ParameterDefinition("_indexInEntityOfAction", Mono.Cecil.ParameterAttributes.None, manipulator.module.TypeSystem.Int32));
|
||||
FieldReference fldref_invdata_item = manipulator.module.ImportReference(typeof(ItemInventoryData).GetField(nameof(ItemInventoryData.item)));
|
||||
FieldReference fldref_item_actions = manipulator.module.ImportReference(typeof(ItemClass).GetField(nameof(ItemClass.Actions)));
|
||||
var il = mtddef_ctor.Body.GetILProcessor();
|
||||
il.Append(il.Create(OpCodes.Ldarg_0));
|
||||
il.Append(il.Create(OpCodes.Ldarg_1));
|
||||
il.Append(il.Create(OpCodes.Ldarg_2));
|
||||
il.Append(il.Create(OpCodes.Call, manipulator.module.ImportReference(manipulator.targetType.GetConstructor(new Type[] { typeof(ItemInventoryData), typeof(int) }))));
|
||||
il.Append(il.Create(OpCodes.Nop));
|
||||
for (int i = 0, j = 0; i < arr_type_actions.Length; i++)
|
||||
{
|
||||
if (!arr_hasdata[i])
|
||||
{
|
||||
arr_flddef_actiondatas[i] = null;
|
||||
continue;
|
||||
}
|
||||
arr_flddef_actiondatas[i] = manipulator.arr_flddef_modules[j];
|
||||
il.Append(il.Create(OpCodes.Ldarg_0));
|
||||
il.Append(il.Create(OpCodes.Ldarg_1));
|
||||
il.Append(il.Create(OpCodes.Ldarg_2));
|
||||
il.Emit(OpCodes.Ldarg_1);
|
||||
il.Emit(OpCodes.Ldfld, fldref_invdata_item);
|
||||
il.Emit(OpCodes.Ldfld, fldref_item_actions);
|
||||
il.Emit(OpCodes.Ldarg_2);
|
||||
il.Emit(OpCodes.Ldelem_Ref);
|
||||
il.Emit(OpCodes.Castclass, typedef_newAction);
|
||||
il.Emit(OpCodes.Ldfld, arr_flddef_actions[i]);
|
||||
Log.Out($"data module {j} {manipulator.moduleTypes[j].FullName} action module {i} {arr_type_actions[i].FullName}");
|
||||
il.Append(il.Create(OpCodes.Newobj, manipulator.module.ImportReference(manipulator.moduleTypes[j].GetConstructor(new Type[] { typeof(ItemInventoryData), typeof(int), arr_type_actions[i] }))));
|
||||
il.Append(il.Create(OpCodes.Stfld, manipulator.arr_flddef_modules[j]));
|
||||
il.Append(il.Create(OpCodes.Nop));
|
||||
j++;
|
||||
}
|
||||
il.Append(il.Create(OpCodes.Ret));
|
||||
return true;
|
||||
}
|
||||
|
||||
public Type GetModuleTypeByName(string name)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void InitModules(ModuleManipulator manipulator, Type targetType, Type baseType, params Type[] moduleTypes)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public bool MatchSpecialArgs(ParameterDefinition par, MethodDefinition mtddef_target, MethodPatchInfo mtdpinf_derived, int moduleIndex, List<Instruction> list_inst_pars, ILProcessor il)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
104
Scripts/Utilities/Modular/ItemActionModuleManager.cs
Normal file
104
Scripts/Utilities/Modular/ItemActionModuleManager.cs
Normal file
@@ -0,0 +1,104 @@
|
||||
using KFCommonUtilityLib.Scripts.Attributes;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using UniLinq;
|
||||
|
||||
namespace KFCommonUtilityLib
|
||||
{
|
||||
//public static class AssemblyLocator
|
||||
//{
|
||||
// private static Dictionary<string, Assembly> assemblies;
|
||||
|
||||
// public static void Init()
|
||||
// {
|
||||
// assemblies = new Dictionary<string, Assembly>();
|
||||
// foreach (var assembly in ModManager.GetLoadedAssemblies())
|
||||
// {
|
||||
// assemblies.Add(assembly.FullName, assembly);
|
||||
// }
|
||||
// AppDomain.CurrentDomain.AssemblyLoad += new AssemblyLoadEventHandler(CurrentDomain_AssemblyLoad);
|
||||
// AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
|
||||
// }
|
||||
|
||||
// private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
|
||||
// {
|
||||
// assemblies.TryGetValue(args.Name, out Assembly assembly);
|
||||
// if (assembly != null)
|
||||
// Log.Out($"RESOLVING ASSEMBLY {assembly.FullName}");
|
||||
// else
|
||||
// Log.Error($"RESOLVING ASSEMBBLY {args.Name} FAILED!");
|
||||
// return assembly;
|
||||
// }
|
||||
|
||||
// private static void CurrentDomain_AssemblyLoad(object sender, AssemblyLoadEventArgs args)
|
||||
// {
|
||||
// Assembly assembly = args.LoadedAssembly;
|
||||
// assemblies[assembly.FullName] = assembly;
|
||||
// Log.Out($"LOADING ASSEMBLY {assembly.FullName}");
|
||||
// }
|
||||
//}
|
||||
|
||||
public static class ItemActionModuleManager
|
||||
{
|
||||
private static readonly Dictionary<string, List<(string typename, int indexOfAction)>> dict_replacement_mapping = new Dictionary<string, List<(string typename, int indexOfAction)>>();
|
||||
|
||||
internal static void Init()
|
||||
{
|
||||
ModuleManagers.OnAssemblyCreated += static () => dict_replacement_mapping.Clear();
|
||||
ModuleManagers.OnAssemblyLoaded += static () =>
|
||||
{
|
||||
//replace item actions
|
||||
foreach (var pair in dict_replacement_mapping)
|
||||
{
|
||||
ItemClass item = ItemClass.GetItemClass(pair.Key, true);
|
||||
foreach ((string typename, int indexOfAction) in pair.Value)
|
||||
if (ModuleManagers.TryFindType(typename, out Type itemActionType))
|
||||
{
|
||||
//Log.Out($"Replace ItemAction {item.Actions[indexOfAction].GetType().FullName} with {itemActionType.FullName}");
|
||||
ItemAction itemActionPrev = item.Actions[indexOfAction];
|
||||
item.Actions[indexOfAction] = (ItemAction)Activator.CreateInstance(itemActionType);
|
||||
item.Actions[indexOfAction].ActionIndex = indexOfAction;
|
||||
item.Actions[indexOfAction].item = item;
|
||||
item.Actions[indexOfAction].ExecutionRequirements = itemActionPrev.ExecutionRequirements;
|
||||
item.Actions[indexOfAction].ReadFrom(itemActionPrev.Properties);
|
||||
}
|
||||
}
|
||||
dict_replacement_mapping.Clear();
|
||||
};
|
||||
}
|
||||
|
||||
internal static void CheckItem(ItemClass item)
|
||||
{
|
||||
if (!ModuleManagers.Inited)
|
||||
{
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < item.Actions.Length; i++)
|
||||
{
|
||||
ItemAction itemAction = item.Actions[i];
|
||||
if (itemAction != null && itemAction.Properties.Values.TryGetValue("ItemActionModules", out string str_modules))
|
||||
{
|
||||
try
|
||||
{
|
||||
if (ModuleManagers.PatchType<ItemActionModuleProcessor>(itemAction.GetType(), typeof(ItemAction), str_modules, out string typename))
|
||||
{
|
||||
if (!dict_replacement_mapping.TryGetValue(item.Name, out var list))
|
||||
{
|
||||
list = new List<(string typename, int indexOfAction)>();
|
||||
dict_replacement_mapping.Add(item.Name, list);
|
||||
}
|
||||
list.Add((typename, i));
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
Log.Error($"Error parsing ItemActionModules for {item.Name} action{i}:\n{e}");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
157
Scripts/Utilities/Modular/ItemActionModuleProcessor.cs
Normal file
157
Scripts/Utilities/Modular/ItemActionModuleProcessor.cs
Normal file
@@ -0,0 +1,157 @@
|
||||
using Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UniLinq;
|
||||
using System.Reflection;
|
||||
using UnityEngine.Scripting;
|
||||
using FieldAttributes = Mono.Cecil.FieldAttributes;
|
||||
using MethodAttributes = Mono.Cecil.MethodAttributes;
|
||||
using TypeAttributes = Mono.Cecil.TypeAttributes;
|
||||
using KFCommonUtilityLib.Scripts.Attributes;
|
||||
using Mono.Cecil.Rocks;
|
||||
|
||||
namespace KFCommonUtilityLib
|
||||
{
|
||||
public class ItemActionModuleProcessor : IModuleProcessor
|
||||
{
|
||||
TypeDefinition typedef_newActionData;
|
||||
FieldDefinition[] arr_flddef_data;
|
||||
|
||||
public Type GetModuleTypeByName(string name)
|
||||
{
|
||||
return ReflectionHelpers.GetTypeWithPrefix("ActionModule", name);
|
||||
}
|
||||
|
||||
public bool BuildConstructor(ModuleManipulator manipulator, MethodDefinition mtddef_ctor)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public void InitModules(ModuleManipulator manipulator, Type targetType, Type baseType, params Type[] moduleTypes)
|
||||
{
|
||||
ModuleDefinition module = manipulator.module;
|
||||
//Find ItemActionData subtype
|
||||
MethodInfo mtdinf_create_data = null;
|
||||
{
|
||||
Type type_itemActionBase = targetType;
|
||||
while (baseType.IsAssignableFrom(type_itemActionBase))
|
||||
{
|
||||
mtdinf_create_data = type_itemActionBase.GetMethod(nameof(ItemAction.CreateModifierData), BindingFlags.Public | BindingFlags.Instance);
|
||||
if (mtdinf_create_data != null)
|
||||
break;
|
||||
mtdinf_create_data = mtdinf_create_data.GetBaseDefinition();
|
||||
}
|
||||
}
|
||||
|
||||
//ACTION MODULE DATA TYPES
|
||||
var arr_type_data = moduleTypes.Select(m => m.GetCustomAttribute<ActionDataTargetAttribute>()?.DataType).ToArray();
|
||||
//Create new ItemActionData
|
||||
//Find CreateModifierData
|
||||
MethodDefinition mtddef_create_data = module.ImportReference(mtdinf_create_data).Resolve();
|
||||
//ItemActionData subtype is the return type of CreateModifierData
|
||||
TypeReference typeref_actiondata = ((MethodReference)mtddef_create_data.Body.Instructions[mtddef_create_data.Body.Instructions.Count - 2].Operand).DeclaringType;
|
||||
//Get type by assembly qualified name since it might be from mod assembly
|
||||
Type type_itemActionData = Type.GetType(Assembly.CreateQualifiedName(typeref_actiondata.Module.Assembly.Name.Name, typeref_actiondata.FullName));
|
||||
MethodReference mtdref_data_ctor;
|
||||
if (ModuleManagers.PatchType(type_itemActionData, typeof(ItemActionData), arr_type_data.Where(t => t != null).ToArray(), new ItemActionDataModuleProcessor(manipulator.typedef_newTarget, moduleTypes, manipulator.arr_flddef_modules, arr_type_data.Select(t => t != null).ToArray(), out arr_flddef_data), out var str_data_type_name) && ModuleManagers.TryFindInCur(str_data_type_name, out typedef_newActionData))
|
||||
{
|
||||
module.Types.Remove(typedef_newActionData);
|
||||
manipulator.typedef_newTarget.NestedTypes.Add(typedef_newActionData);
|
||||
mtdref_data_ctor = typedef_newActionData.GetConstructors().FirstOrDefault();
|
||||
}
|
||||
else
|
||||
{
|
||||
mtdref_data_ctor = module.ImportReference(type_itemActionData.GetConstructor(new Type[] { typeof(ItemInventoryData), typeof(int) }));
|
||||
}
|
||||
|
||||
//typedef_newActionData = new TypeDefinition(null, ModuleUtils.CreateTypeName(type_itemActionData, arr_type_data), TypeAttributes.AnsiClass | TypeAttributes.BeforeFieldInit | TypeAttributes.NestedPublic | TypeAttributes.Sealed, module.ImportReference(typeref_actiondata));
|
||||
//typedef_newActionData.CustomAttributes.Add(new CustomAttribute(module.ImportReference(typeof(PreserveAttribute).GetConstructor(Array.Empty<Type>()))));
|
||||
//manipulator.typedef_newTarget.NestedTypes.Add(typedef_newActionData);
|
||||
|
||||
////Create ItemActionData field
|
||||
//arr_flddef_data = new FieldDefinition[moduleTypes.Length];
|
||||
//for (int i = 0; i < moduleTypes.Length; i++)
|
||||
//{
|
||||
// if (arr_type_data[i] != null)
|
||||
// {
|
||||
// Type type_data = arr_type_data[i];
|
||||
// manipulator.MakeContainerFor(typedef_newActionData, type_data, out var flddef_data);
|
||||
// arr_flddef_data[i] = flddef_data;
|
||||
// }
|
||||
//}
|
||||
|
||||
////Create ItemActionData constructor
|
||||
//MethodDefinition mtddef_ctor_data = new MethodDefinition(".ctor", MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, module.TypeSystem.Void);
|
||||
//mtddef_ctor_data.Parameters.Add(new ParameterDefinition("_inventoryData", Mono.Cecil.ParameterAttributes.None, module.ImportReference(typeof(ItemInventoryData))));
|
||||
//mtddef_ctor_data.Parameters.Add(new ParameterDefinition("_indexInEntityOfAction", Mono.Cecil.ParameterAttributes.None, module.TypeSystem.Int32));
|
||||
//FieldReference fldref_invdata_item = module.ImportReference(typeof(ItemInventoryData).GetField(nameof(ItemInventoryData.item)));
|
||||
//FieldReference fldref_item_actions = module.ImportReference(typeof(ItemClass).GetField(nameof(ItemClass.Actions)));
|
||||
//var il = mtddef_ctor_data.Body.GetILProcessor();
|
||||
//il.Append(il.Create(OpCodes.Ldarg_0));
|
||||
//il.Append(il.Create(OpCodes.Ldarg_1));
|
||||
//il.Append(il.Create(OpCodes.Ldarg_2));
|
||||
//il.Append(il.Create(OpCodes.Call, module.ImportReference(type_itemActionData.GetConstructor(new Type[] { typeof(ItemInventoryData), typeof(int) }))));
|
||||
//il.Append(il.Create(OpCodes.Nop));
|
||||
//for (int i = 0; i < arr_flddef_data.Length; i++)
|
||||
//{
|
||||
// if (arr_type_data[i] == null)
|
||||
// continue;
|
||||
// il.Append(il.Create(OpCodes.Ldarg_0));
|
||||
// il.Append(il.Create(OpCodes.Ldarg_1));
|
||||
// il.Append(il.Create(OpCodes.Ldarg_2));
|
||||
// il.Emit(OpCodes.Ldarg_1);
|
||||
// il.Emit(OpCodes.Ldfld, fldref_invdata_item);
|
||||
// il.Emit(OpCodes.Ldfld, fldref_item_actions);
|
||||
// il.Emit(OpCodes.Ldarg_2);
|
||||
// il.Emit(OpCodes.Ldelem_Ref);
|
||||
// il.Emit(OpCodes.Castclass, manipulator.typedef_newTarget);
|
||||
// il.Emit(OpCodes.Ldfld, manipulator.arr_flddef_modules[i]);
|
||||
// il.Append(il.Create(OpCodes.Newobj, module.ImportReference(arr_type_data[i].GetConstructor(new Type[] { typeof(ItemInventoryData), typeof(int), moduleTypes[i] }))));
|
||||
// il.Append(il.Create(OpCodes.Stfld, arr_flddef_data[i]));
|
||||
// il.Append(il.Create(OpCodes.Nop));
|
||||
//}
|
||||
//il.Append(il.Create(OpCodes.Ret));
|
||||
//typedef_newActionData.Methods.Add(mtddef_ctor_data);
|
||||
|
||||
//Create ItemAction.CreateModifierData override
|
||||
MethodDefinition mtddef_create_modifier_data = new MethodDefinition(nameof(ItemAction.CreateModifierData), MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig | MethodAttributes.ReuseSlot, module.ImportReference(typeof(ItemActionData)));
|
||||
mtddef_create_modifier_data.Parameters.Add(new ParameterDefinition("_invData", Mono.Cecil.ParameterAttributes.None, module.ImportReference(typeof(ItemInventoryData))));
|
||||
mtddef_create_modifier_data.Parameters.Add(new ParameterDefinition("_indexInEntityOfAction", Mono.Cecil.ParameterAttributes.None, module.TypeSystem.Int32));
|
||||
var il = mtddef_create_modifier_data.Body.GetILProcessor();
|
||||
il.Emit(OpCodes.Ldarg_1);
|
||||
il.Emit(OpCodes.Ldarg_2);
|
||||
il.Emit(OpCodes.Newobj, mtdref_data_ctor);
|
||||
il.Emit(OpCodes.Ret);
|
||||
manipulator.typedef_newTarget.Methods.Add(mtddef_create_modifier_data);
|
||||
}
|
||||
|
||||
public bool MatchSpecialArgs(ParameterDefinition par, MethodDefinition mtddef_target, MethodPatchInfo mtdpinf_derived, int moduleIndex, List<Instruction> list_inst_pars, ILProcessor il)
|
||||
{
|
||||
switch (par.Name)
|
||||
{
|
||||
//load injected data instance
|
||||
case "__customData":
|
||||
var flddef_data = arr_flddef_data[moduleIndex];
|
||||
if (flddef_data == null)
|
||||
throw new ArgumentNullException($"No Injected ItemActionData in {mtddef_target.DeclaringType.FullName}! module index {moduleIndex}");
|
||||
int index = -1;
|
||||
for (int j = 0; j < mtdpinf_derived.Method.Parameters.Count; j++)
|
||||
{
|
||||
if (mtdpinf_derived.Method.Parameters[j].ParameterType.Name == "ItemActionData")
|
||||
{
|
||||
index = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (index < 0)
|
||||
throw new ArgumentException($"ItemActionData is not present in target method! Patch method: {mtddef_target.DeclaringType.FullName}.{mtddef_target.Name}");
|
||||
list_inst_pars.Add(il.Create(OpCodes.Ldarg_S, mtdpinf_derived.Method.Parameters[index]));
|
||||
list_inst_pars.Add(il.Create(OpCodes.Castclass, typedef_newActionData));
|
||||
list_inst_pars.Add(il.Create(par.ParameterType.IsByReference ? OpCodes.Ldflda : OpCodes.Ldfld, flddef_data));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
104
Scripts/Utilities/Modular/ItemClassModuleManager.cs
Normal file
104
Scripts/Utilities/Modular/ItemClassModuleManager.cs
Normal file
@@ -0,0 +1,104 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace KFCommonUtilityLib
|
||||
{
|
||||
public static class ItemClassModuleManager
|
||||
{
|
||||
private static readonly Dictionary<string, string> dict_classtypes = new Dictionary<string, string>();
|
||||
|
||||
internal static void Init()
|
||||
{
|
||||
ModuleManagers.OnAssemblyCreated += static () => dict_classtypes.Clear();
|
||||
ModuleManagers.OnAssemblyLoaded += static () =>
|
||||
{
|
||||
foreach (var pair in dict_classtypes)
|
||||
{
|
||||
if (ModuleManagers.TryFindType(pair.Value, out Type classType))
|
||||
{
|
||||
var item = ItemClass.GetItemClass(pair.Key);
|
||||
if (item != null)
|
||||
{
|
||||
var itemNew = (ItemClass)Activator.CreateInstance(classType);
|
||||
item.PreInitCopyTo(itemNew);
|
||||
if (item is ItemClassModifier mod)
|
||||
{
|
||||
mod.PreInitCopyToModifier((ItemClassModifier)itemNew);
|
||||
}
|
||||
itemNew.Init();
|
||||
ItemClass.itemNames.RemoveAt(ItemClass.itemNames.Count - 1);
|
||||
ItemClass.list[itemNew.Id] = itemNew;
|
||||
}
|
||||
}
|
||||
}
|
||||
dict_classtypes.Clear();
|
||||
};
|
||||
}
|
||||
|
||||
internal static void CheckItem(ItemClass item)
|
||||
{
|
||||
if (!ModuleManagers.Inited)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (item != null && item.Properties.Values.TryGetValue("ItemClassModules", out string str_modules))
|
||||
{
|
||||
if (ModuleManagers.PatchType<ItemClassModuleProcessor>(item.GetType(), typeof(ItemClass), str_modules, out string typename))
|
||||
{
|
||||
dict_classtypes[item.Name] = typename;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void PreInitCopyTo(this ItemClass from, ItemClass to)
|
||||
{
|
||||
to.Actions = from.Actions;
|
||||
foreach (var action in to.Actions)
|
||||
{
|
||||
if (action != null)
|
||||
{
|
||||
action.item = to;
|
||||
}
|
||||
}
|
||||
to.SetName(from.Name);
|
||||
to.pId = from.pId;
|
||||
to.Properties = from.Properties;
|
||||
to.Effects = from.Effects;
|
||||
to.setLocalizedItemName(from.localizedName);
|
||||
to.Stacknumber = from.Stacknumber;
|
||||
to.SetCanHold(from.bCanHold);
|
||||
to.SetCanDrop(from.bCanDrop);
|
||||
to.MadeOfMaterial = from.MadeOfMaterial;
|
||||
to.MeshFile = from.MeshFile;
|
||||
to.StickyOffset = from.StickyOffset;
|
||||
to.StickyColliderRadius = from.StickyColliderRadius;
|
||||
to.StickyColliderUp = from.StickyColliderUp;
|
||||
to.StickyColliderLength = from.StickyColliderLength;
|
||||
to.StickyMaterial = from.StickyMaterial;
|
||||
to.ImageEffectOnActive = from.ImageEffectOnActive;
|
||||
to.Active = from.Active;
|
||||
to.IsSticky = from.IsSticky;
|
||||
to.DropMeshFile = from.DropMeshFile;
|
||||
to.HandMeshFile = from.HandMeshFile;
|
||||
to.HoldType = from.HoldType;
|
||||
to.RepairTools = from.RepairTools;
|
||||
to.RepairAmount = from.RepairAmount;
|
||||
to.RepairTime = from.RepairTime;
|
||||
to.MaxUseTimes = from.MaxUseTimes;
|
||||
to.MaxUseTimesBreaksAfter = from.MaxUseTimesBreaksAfter;
|
||||
to.EconomicValue = from.EconomicValue;
|
||||
to.Preview = from.Preview;
|
||||
}
|
||||
|
||||
private static void PreInitCopyToModifier(this ItemClassModifier from, ItemClassModifier to)
|
||||
{
|
||||
to.CosmeticInstallChance = from.CosmeticInstallChance;
|
||||
to.PropertyOverrides = from.PropertyOverrides;
|
||||
to.InstallableTags = from.InstallableTags;
|
||||
to.DisallowedTags = from.DisallowedTags;
|
||||
to.ItemTags = from.ItemTags;
|
||||
to.Type = from.Type;
|
||||
}
|
||||
}
|
||||
}
|
||||
33
Scripts/Utilities/Modular/ItemClassModuleProcessor.cs
Normal file
33
Scripts/Utilities/Modular/ItemClassModuleProcessor.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace KFCommonUtilityLib
|
||||
{
|
||||
public struct ItemClassModuleProcessor : IModuleProcessor
|
||||
{
|
||||
|
||||
public Type GetModuleTypeByName(string name)
|
||||
{
|
||||
return ReflectionHelpers.GetTypeWithPrefix("ItemModule", name);
|
||||
}
|
||||
public bool BuildConstructor(ModuleManipulator manipulator, MethodDefinition mtddef_ctor)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public void InitModules(ModuleManipulator manipulator, Type targetType, Type baseType, params Type[] moduleTypes)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public bool MatchSpecialArgs(ParameterDefinition par, MethodDefinition mtddef_target, MethodPatchInfo mtdpinf_derived, int moduleIndex, List<Instruction> list_inst_pars, ILProcessor il)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
262
Scripts/Utilities/Modular/ModuleManagers.cs
Normal file
262
Scripts/Utilities/Modular/ModuleManagers.cs
Normal file
@@ -0,0 +1,262 @@
|
||||
using HarmonyLib;
|
||||
using KFCommonUtilityLib.Scripts.Attributes;
|
||||
using Mono.Cecil;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Security.Permissions;
|
||||
using UniLinq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace KFCommonUtilityLib
|
||||
{
|
||||
public static class ModuleManagers
|
||||
{
|
||||
private static class ModuleExtensions<T>
|
||||
{
|
||||
public readonly static List<Type> extensions = new List<Type>();
|
||||
}
|
||||
public static AssemblyDefinition WorkingAssembly { get; private set; } = null;
|
||||
public static event Action OnAssemblyCreated;
|
||||
public static event Action OnAssemblyLoaded;
|
||||
public static bool Inited { get; private set; }
|
||||
private static bool extensionScanned;
|
||||
private static readonly HashSet<string> list_registered_path = new HashSet<string>();
|
||||
private static readonly List<Assembly> list_created = new List<Assembly>();
|
||||
private static DefaultAssemblyResolver resolver;
|
||||
private static ModuleAttributes moduleAttributes;
|
||||
private static ModuleCharacteristics moduleCharacteristics;
|
||||
private static MethodInfo mtdinf = AccessTools.Method(typeof(ModuleManagers), nameof(ModuleManagers.AddModuleExtension));
|
||||
|
||||
public static void InitModuleExtensions()
|
||||
{
|
||||
if (extensionScanned)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var assemblies = ModManager.GetLoadedAssemblies();
|
||||
|
||||
foreach (var assembly in assemblies)
|
||||
{
|
||||
foreach (var type in assembly.GetTypes())
|
||||
{
|
||||
var attr = type.GetCustomAttribute<TypeTargetExtensionAttribute>();
|
||||
if (attr != null)
|
||||
{
|
||||
if ((bool)mtdinf.MakeGenericMethod(attr.ModuleType).Invoke(null, new object[] { type }))
|
||||
{
|
||||
Log.Out($"Found Module Extension {type.FullName}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
extensionScanned = true;
|
||||
}
|
||||
|
||||
public static bool AddModuleExtension<T>(Type extType)
|
||||
{
|
||||
if (typeof(T).GetCustomAttribute<TypeTargetAttribute>() == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ModuleExtensions<T>.extensions.Contains(extType))
|
||||
ModuleExtensions<T>.extensions.Add(extType);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static Type[] GetModuleExtensions<T>()
|
||||
{
|
||||
if (typeof(T).GetCustomAttribute<TypeTargetAttribute>() == null)
|
||||
{
|
||||
return Array.Empty<Type>();
|
||||
}
|
||||
|
||||
return ModuleExtensions<T>.extensions.ToArray();
|
||||
}
|
||||
|
||||
public static void AddAssemblySearchPath(string path)
|
||||
{
|
||||
if (Directory.Exists(path))
|
||||
{
|
||||
list_registered_path.Add(path);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void ClearOutputFolder()
|
||||
{
|
||||
Mod self = ModManager.GetMod("CommonUtilityLib");
|
||||
string path = Path.Combine(self.Path, "AssemblyOutput");
|
||||
if (Directory.Exists(path))
|
||||
Array.ForEach(Directory.GetFiles(path), File.Delete);
|
||||
else
|
||||
Directory.CreateDirectory(path);
|
||||
}
|
||||
|
||||
internal static void InitNew()
|
||||
{
|
||||
if (Inited)
|
||||
{
|
||||
return;
|
||||
}
|
||||
InitModuleExtensions();
|
||||
WorkingAssembly?.Dispose();
|
||||
if (resolver == null)
|
||||
{
|
||||
resolver = new DefaultAssemblyResolver();
|
||||
resolver.AddSearchDirectory(Path.Combine(Application.dataPath, "Managed"));
|
||||
|
||||
foreach (var mod in ModManager.GetLoadedMods())
|
||||
{
|
||||
resolver.AddSearchDirectory(mod.Path);
|
||||
}
|
||||
|
||||
foreach (var path in list_registered_path)
|
||||
{
|
||||
resolver.AddSearchDirectory(path);
|
||||
}
|
||||
|
||||
AssemblyDefinition assdef_main = AssemblyDefinition.ReadAssembly($"{Application.dataPath}/Managed/Assembly-CSharp.dll", new ReaderParameters() { AssemblyResolver = resolver });
|
||||
moduleAttributes = assdef_main.MainModule.Attributes;
|
||||
moduleCharacteristics = assdef_main.MainModule.Characteristics;
|
||||
Log.Out("Reading Attributes from assembly: " + assdef_main.FullName);
|
||||
}
|
||||
string assname = "RuntimeAssembled" + DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
||||
WorkingAssembly = AssemblyDefinition.CreateAssembly(new AssemblyNameDefinition(assname,
|
||||
new Version(0, 0, 0, 0)),
|
||||
assname + ".dll",
|
||||
new ModuleParameters()
|
||||
{
|
||||
Kind = ModuleKind.Dll,
|
||||
AssemblyResolver = resolver,
|
||||
Architecture = TargetArchitecture.I386,
|
||||
Runtime = TargetRuntime.Net_4_0,
|
||||
});
|
||||
WorkingAssembly.MainModule.Attributes = moduleAttributes;
|
||||
WorkingAssembly.MainModule.Characteristics = moduleCharacteristics;
|
||||
//write security attributes so that calling non-public patch methods from this assembly is allowed
|
||||
Mono.Cecil.SecurityAttribute sattr_permission = new Mono.Cecil.SecurityAttribute(WorkingAssembly.MainModule.ImportReference(typeof(SecurityPermissionAttribute)));
|
||||
Mono.Cecil.CustomAttributeNamedArgument caarg_SkipVerification = new Mono.Cecil.CustomAttributeNamedArgument(nameof(SecurityPermissionAttribute.SkipVerification), new CustomAttributeArgument(WorkingAssembly.MainModule.TypeSystem.Boolean, true));
|
||||
sattr_permission.Properties.Add(caarg_SkipVerification);
|
||||
SecurityDeclaration sdec = new SecurityDeclaration(Mono.Cecil.SecurityAction.RequestMinimum);
|
||||
sdec.SecurityAttributes.Add(sattr_permission);
|
||||
WorkingAssembly.SecurityDeclarations.Add(sdec);
|
||||
OnAssemblyCreated?.Invoke();
|
||||
Inited = true;
|
||||
Log.Out("======Init New======");
|
||||
}
|
||||
|
||||
public static bool PatchType<T>(Type targetType, Type baseType, string moduleNames, out string typename) where T : IModuleProcessor, new()
|
||||
{
|
||||
Type[] moduleTypes = moduleNames.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(s => new T().GetModuleTypeByName(s.Trim()))
|
||||
.Where(t => t.GetCustomAttribute<TypeTargetAttribute>().BaseType.IsAssignableFrom(targetType)).ToArray();
|
||||
return PatchType(targetType, baseType, moduleTypes, new T(), out typename);
|
||||
}
|
||||
|
||||
public static bool PatchType<T>(Type targetType, Type baseType, Type[] moduleTypes, out string typename) where T : IModuleProcessor, new()
|
||||
{
|
||||
return PatchType(targetType, baseType, moduleTypes, new T(), out typename);
|
||||
}
|
||||
|
||||
public static bool PatchType<T>(Type targetType, Type baseType, Type[] moduleTypes, T processor, out string typename) where T : IModuleProcessor
|
||||
{
|
||||
if (moduleTypes.Length == 0)
|
||||
{
|
||||
typename = string.Empty;
|
||||
return false;
|
||||
}
|
||||
typename = ModuleUtils.CreateTypeName(targetType, moduleTypes);
|
||||
//Log.Out(typename);
|
||||
if (!ModuleManagers.TryFindType(typename, out _) && !ModuleManagers.TryFindInCur(typename, out _))
|
||||
_ = new ModuleManipulator(ModuleManagers.WorkingAssembly, processor, targetType, baseType, moduleTypes);
|
||||
return true;
|
||||
}
|
||||
|
||||
internal static void FinishAndLoad()
|
||||
{
|
||||
if (!Inited)
|
||||
{
|
||||
return;
|
||||
}
|
||||
//output assembly
|
||||
Mod self = ModManager.GetMod("CommonUtilityLib");
|
||||
if (self == null)
|
||||
{
|
||||
Log.Warning("Failed to get mod!");
|
||||
self = ModManager.GetModForAssembly(typeof(ItemActionModuleManager).Assembly);
|
||||
}
|
||||
if (self != null && WorkingAssembly != null)
|
||||
{
|
||||
if (WorkingAssembly.MainModule.Types.Count > 1)
|
||||
{
|
||||
Log.Out("Assembly is valid!");
|
||||
using (MemoryStream ms = new MemoryStream())
|
||||
{
|
||||
try
|
||||
{
|
||||
WorkingAssembly.Write(ms);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
new ConsoleCmdShutdown().Execute(new List<string>(), new CommandSenderInfo());
|
||||
}
|
||||
DirectoryInfo dirInfo = Directory.CreateDirectory(Path.Combine(self.Path, "AssemblyOutput"));
|
||||
string filename = Path.Combine(dirInfo.FullName, WorkingAssembly.Name.Name + ".dll");
|
||||
Log.Out("Output Assembly: " + filename);
|
||||
using (FileStream fs = new FileStream(filename, FileMode.OpenOrCreate, FileAccess.Write))
|
||||
{
|
||||
ms.WriteTo(fs);
|
||||
}
|
||||
Assembly newAssembly = Assembly.LoadFile(filename);
|
||||
list_created.Add(newAssembly);
|
||||
}
|
||||
}
|
||||
|
||||
Log.Out("======Finish and Load======");
|
||||
Inited = false;
|
||||
OnAssemblyLoaded?.Invoke();
|
||||
Cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
//cleanup
|
||||
internal static void Cleanup()
|
||||
{
|
||||
Inited = false;
|
||||
WorkingAssembly?.Dispose();
|
||||
WorkingAssembly = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if type is already generated in previous assemblies.
|
||||
/// </summary>
|
||||
/// <param name="name">Full type name.</param>
|
||||
/// <param name="type">The retrieved type, null if not found.</param>
|
||||
/// <returns>true if found.</returns>
|
||||
public static bool TryFindType(string name, out Type type)
|
||||
{
|
||||
type = null;
|
||||
foreach (var assembly in list_created)
|
||||
{
|
||||
type = assembly.GetType(name, false);
|
||||
if (type != null)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if type is already generated in current working assembly definition.
|
||||
/// </summary>
|
||||
/// <param name="name">Full type name.</param>
|
||||
/// <param name="typedef">The retrieved type definition, null if not found.</param>
|
||||
/// <returns>true if found.</returns>
|
||||
public static bool TryFindInCur(string name, out TypeDefinition typedef)
|
||||
{
|
||||
typedef = WorkingAssembly?.MainModule.GetType(name);
|
||||
return typedef != null;
|
||||
}
|
||||
}
|
||||
}
|
||||
646
Scripts/Utilities/Modular/ModuleManipulator.cs
Normal file
646
Scripts/Utilities/Modular/ModuleManipulator.cs
Normal file
@@ -0,0 +1,646 @@
|
||||
using HarmonyLib.Public.Patching;
|
||||
using HarmonyLib;
|
||||
using KFCommonUtilityLib.Scripts.Attributes;
|
||||
using Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
using MonoMod.Cil;
|
||||
using MonoMod.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UniLinq;
|
||||
using System.Reflection;
|
||||
using KFCommonUtilityLib.Harmony;
|
||||
using UnityEngine.Scripting;
|
||||
using TypeAttributes = Mono.Cecil.TypeAttributes;
|
||||
using MethodAttributes = Mono.Cecil.MethodAttributes;
|
||||
using FieldAttributes = Mono.Cecil.FieldAttributes;
|
||||
using Mono.Cecil.Rocks;
|
||||
|
||||
namespace KFCommonUtilityLib
|
||||
{
|
||||
public interface IModuleContainerFor<out T> where T : class
|
||||
{
|
||||
T Instance { get; }
|
||||
}
|
||||
|
||||
public class TranspilerTarget
|
||||
{
|
||||
public TranspilerTarget(Type type_action, MethodInfo mtdinf_original, PatchInfo patchinf_harmony)
|
||||
{
|
||||
this.type_action = type_action;
|
||||
this.mtdinf_original = mtdinf_original;
|
||||
this.patchinf_harmony = patchinf_harmony;
|
||||
}
|
||||
|
||||
public Type type_action;
|
||||
public MethodInfo mtdinf_original;
|
||||
public PatchInfo patchinf_harmony;
|
||||
}
|
||||
|
||||
public class MethodPatchInfo
|
||||
{
|
||||
public readonly MethodDefinition Method;
|
||||
public Instruction PrefixBegin;
|
||||
public Instruction PostfixBegin;
|
||||
public Instruction PostfixEnd;
|
||||
|
||||
public MethodPatchInfo(MethodDefinition mtddef, Instruction postfixEnd, Instruction prefixBegin)
|
||||
{
|
||||
Method = mtddef;
|
||||
PostfixEnd = postfixEnd;
|
||||
PrefixBegin = prefixBegin;
|
||||
}
|
||||
}
|
||||
|
||||
internal struct MethodOverrideInfo
|
||||
{
|
||||
public MethodInfo mtdinf_target;
|
||||
public MethodInfo mtdinf_base;
|
||||
public MethodReference mtdref_base;
|
||||
public Type prefType;
|
||||
|
||||
public MethodOverrideInfo(MethodInfo mtdinf_target, MethodInfo mtdinf_base, MethodReference mtddef_base, Type prefType)
|
||||
{
|
||||
this.mtdinf_target = mtdinf_target;
|
||||
this.mtdinf_base = mtdinf_base;
|
||||
this.mtdref_base = mtddef_base;
|
||||
this.prefType = prefType;
|
||||
}
|
||||
}
|
||||
|
||||
public class ModuleManipulator
|
||||
{
|
||||
public ModuleDefinition module;
|
||||
public IModuleProcessor processor;
|
||||
public Type targetType;
|
||||
public Type baseType;
|
||||
public Type[] moduleTypes;
|
||||
public Type[][] moduleExtensionTypes;
|
||||
public TypeDefinition typedef_newTarget;
|
||||
public TypeReference typeref_interface;
|
||||
public FieldDefinition[] arr_flddef_modules;
|
||||
private static MethodInfo mtdinf = AccessTools.Method(typeof(ModuleManagers), nameof(ModuleManagers.GetModuleExtensions));
|
||||
|
||||
public ModuleManipulator(AssemblyDefinition workingAssembly, IModuleProcessor processor, Type targetType, Type baseType, params Type[] moduleTypes)
|
||||
{
|
||||
module = workingAssembly.MainModule;
|
||||
this.processor = processor;
|
||||
this.targetType = targetType;
|
||||
this.baseType = baseType;
|
||||
this.moduleTypes = moduleTypes;
|
||||
moduleExtensionTypes = moduleTypes.Select(t => (Type[])mtdinf.MakeGenericMethod(t).Invoke(null, null)).ToArray();
|
||||
Patch();
|
||||
}
|
||||
|
||||
private void Patch()
|
||||
{
|
||||
typeref_interface = module.ImportReference(typeof(IModuleContainerFor<>));
|
||||
//Create new override type
|
||||
TypeReference typeref_target = module.ImportReference(targetType);
|
||||
typedef_newTarget = new TypeDefinition(null, ModuleUtils.CreateTypeName(targetType, moduleTypes), TypeAttributes.AnsiClass | TypeAttributes.BeforeFieldInit | TypeAttributes.Public | TypeAttributes.Sealed, typeref_target);
|
||||
typedef_newTarget.CustomAttributes.Add(new CustomAttribute(module.ImportReference(typeof(PreserveAttribute).GetConstructor(Array.Empty<Type>()))));
|
||||
module.Types.Add(typedef_newTarget);
|
||||
|
||||
//Create fields
|
||||
arr_flddef_modules = new FieldDefinition[moduleTypes.Length];
|
||||
for (int i = 0; i < moduleTypes.Length; i++)
|
||||
{
|
||||
//Create ItemAction field
|
||||
Type type_module = moduleTypes[i];
|
||||
MakeContainerFor(typedef_newTarget, type_module, out var flddef_module);
|
||||
arr_flddef_modules[i] = flddef_module;
|
||||
}
|
||||
|
||||
//Create ItemAction constructor
|
||||
MethodDefinition mtddef_ctor = new MethodDefinition(".ctor", MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, module.TypeSystem.Void);
|
||||
if (processor == null || !processor.BuildConstructor(this, mtddef_ctor))
|
||||
{
|
||||
var il = mtddef_ctor.Body.GetILProcessor();
|
||||
il.Append(il.Create(OpCodes.Ldarg_0));
|
||||
il.Append(il.Create(OpCodes.Call, module.ImportReference(targetType.GetConstructor(Array.Empty<Type>()))));
|
||||
il.Append(il.Create(OpCodes.Nop));
|
||||
for (int i = 0; i < arr_flddef_modules.Length; i++)
|
||||
{
|
||||
il.Append(il.Create(OpCodes.Ldarg_0));
|
||||
il.Append(il.Create(OpCodes.Newobj, module.ImportReference(moduleTypes[i].GetConstructor(Array.Empty<Type>()))));
|
||||
il.Append(il.Create(OpCodes.Stfld, arr_flddef_modules[i]));
|
||||
il.Append(il.Create(OpCodes.Nop));
|
||||
}
|
||||
il.Append(il.Create(OpCodes.Ret));
|
||||
}
|
||||
typedef_newTarget.Methods.Add(mtddef_ctor);
|
||||
|
||||
processor?.InitModules(this, targetType, baseType, moduleTypes);
|
||||
|
||||
//<derived method name, method patch info>
|
||||
Dictionary<string, MethodPatchInfo> dict_overrides = new Dictionary<string, MethodPatchInfo>();
|
||||
//<derived method name, transpiler stub methods in inheritance order>
|
||||
//TODO: USE TREE INSTEAD OF LIST
|
||||
Dictionary<string, List<TranspilerTarget>> dict_transpilers = new Dictionary<string, List<TranspilerTarget>>();
|
||||
//<derived method name, <module type name, local variable>>
|
||||
Dictionary<string, Dictionary<string, VariableDefinition>> dict_all_states = new Dictionary<string, Dictionary<string, VariableDefinition>>();
|
||||
|
||||
//Get all transpilers
|
||||
for (int i = 0; i < moduleTypes.Length; i++)
|
||||
{
|
||||
Type moduleType = moduleTypes[i];
|
||||
const BindingFlags searchFlags = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
|
||||
foreach (var mtd in moduleType.GetMethods(searchFlags).Concat(moduleExtensionTypes[i].SelectMany(t => t.GetMethods(searchFlags))))
|
||||
{
|
||||
var attr = mtd.GetCustomAttribute<MethodTargetTranspilerAttribute>();
|
||||
foreach (var hp in mtd.GetCustomAttributes<HarmonyPatch>())
|
||||
{
|
||||
//make sure the transpiler has a target method to apply, otherwise skip it
|
||||
if (attr != null && hp != null && hp.info.declaringType != null)
|
||||
{
|
||||
var hm = hp.info;
|
||||
hm.methodType = hm.methodType ?? MethodType.Normal;
|
||||
var mtdinf_target = hm.GetOriginalMethod() as MethodInfo;
|
||||
if (mtdinf_target == null || mtdinf_target.IsAbstract || !mtdinf_target.IsVirtual)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
string id = hm.GetTargetMethodIdentifier();
|
||||
if (!dict_transpilers.TryGetValue(id, out var list))
|
||||
{
|
||||
dict_transpilers[id] = (list = new List<TranspilerTarget>());
|
||||
Type nextType = targetType;
|
||||
TranspilerTarget curNode = null;
|
||||
var hm_next = hm.Clone();
|
||||
while (hm.declaringType.IsAssignableFrom(nextType))
|
||||
{
|
||||
hm_next.declaringType = nextType;
|
||||
var mtdinfo_cur = hm_next.GetOriginalMethod() as MethodInfo;
|
||||
if (mtdinfo_cur != null)
|
||||
{
|
||||
var patchinf_harmony = mtdinfo_cur.ToPatchInfoDontAdd().Copy();
|
||||
curNode = new TranspilerTarget(mtdinfo_cur.DeclaringType, mtdinfo_cur, patchinf_harmony);
|
||||
list.Add(curNode);
|
||||
}
|
||||
nextType = nextType.BaseType;
|
||||
}
|
||||
|
||||
if (curNode != null)
|
||||
{
|
||||
curNode.patchinf_harmony.AddTranspilers(CommonUtilityLibInit.HarmonyInstance.Id, new HarmonyMethod(mtd));
|
||||
Log.Out($"Adding transpiler {mtd.FullDescription()}\nCurrent transpilers:\n{string.Join('\n', curNode.patchinf_harmony.transpilers.Select(p => p.PatchMethod.FullDescription()))}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bool childFound = false;
|
||||
foreach (var node in ((IEnumerable<TranspilerTarget>)list).Reverse())
|
||||
{
|
||||
if (node.type_action.Equals(hm.declaringType))
|
||||
{
|
||||
childFound = true;
|
||||
node.patchinf_harmony.AddTranspilers(CommonUtilityLibInit.HarmonyInstance.Id, mtd);
|
||||
Log.Out($"Adding transpiler {mtd.FullDescription()}\nCurrent transpilers:\n{string.Join('\n', node.patchinf_harmony.transpilers.Select(p => p.PatchMethod.FullDescription()))}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!childFound)
|
||||
{
|
||||
Type nextType = list[list.Count - 1].type_action.BaseType;
|
||||
TranspilerTarget curNode = null;
|
||||
var hm_next = hm.Clone();
|
||||
while (hm.declaringType.IsAssignableFrom(nextType))
|
||||
{
|
||||
hm_next.declaringType = nextType;
|
||||
var mtdinfo_cur = hm_next.GetOriginalMethod() as MethodInfo;
|
||||
if (mtdinfo_cur != null)
|
||||
{
|
||||
var patchinf_harmony = mtdinfo_cur.ToPatchInfoDontAdd().Copy();
|
||||
curNode = new TranspilerTarget(mtdinfo_cur.DeclaringType, mtdinfo_cur, patchinf_harmony);
|
||||
list.Add(curNode);
|
||||
}
|
||||
nextType = nextType.BaseType;
|
||||
}
|
||||
|
||||
if (curNode != null)
|
||||
{
|
||||
curNode.patchinf_harmony.AddTranspilers(CommonUtilityLibInit.HarmonyInstance.Id, new HarmonyMethod(mtd));
|
||||
Log.Out($"Adding transpiler {mtd.FullDescription()}\nCurrent transpilers:\n{string.Join('\n', curNode.patchinf_harmony.transpilers.Select(p => p.PatchMethod.FullDescription()))}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//apply transpilers and replace method calls on base methods with patched ones
|
||||
Dictionary<string, MethodDefinition> dict_replacers = new Dictionary<string, MethodDefinition>();
|
||||
foreach (var pair in dict_transpilers)
|
||||
{
|
||||
List<TranspilerTarget> list = pair.Value;
|
||||
|
||||
//the top copy to call in the override method
|
||||
MethodDefinition mtddef_override_copy = null;
|
||||
MethodReference mtdref_override_base = null;
|
||||
for (int i = list.Count - 1; i >= 0; i--)
|
||||
{
|
||||
TranspilerTarget curNode = list[i];
|
||||
MethodPatcher patcher = curNode.mtdinf_original.GetMethodPatcher();
|
||||
DynamicMethodDefinition dmd = patcher.CopyOriginal();
|
||||
ILContext context = new ILContext(dmd.Definition);
|
||||
HarmonyManipulator.Manipulate(curNode.mtdinf_original, curNode.patchinf_harmony, context);
|
||||
var mtdref_original = module.ImportReference(curNode.mtdinf_original);
|
||||
var mtddef_copy = mtdref_original.Resolve().CloneToModuleAsStatic(context.Body, module.ImportReference(curNode.type_action), module);
|
||||
dmd.Dispose();
|
||||
context.Dispose();
|
||||
if (mtddef_override_copy != null && mtdref_override_base != null)
|
||||
{
|
||||
//replace calls to the base
|
||||
foreach (var ins in mtddef_copy.Body.Instructions)
|
||||
{
|
||||
if (ins.OpCode == OpCodes.Call && ((MethodReference)ins.Operand).FullName.Equals(mtdref_override_base.FullName))
|
||||
{
|
||||
Log.Out($"replacing call to {mtdref_override_base.FullName} to {mtddef_override_copy.FullName}");
|
||||
ins.Operand = mtddef_override_copy;
|
||||
}
|
||||
}
|
||||
}
|
||||
//add patched copy to the class
|
||||
typedef_newTarget.Methods.Add(mtddef_copy);
|
||||
//the iteration is reversed so make sure we grab the latest method
|
||||
mtddef_override_copy = mtddef_copy;
|
||||
mtdref_override_base = mtdref_original;
|
||||
}
|
||||
//create the method override that calls the patched copy
|
||||
if (mtddef_override_copy != null && mtdref_override_base != null)
|
||||
{
|
||||
GetOrCreateOverride(dict_overrides, pair.Key, mtdref_override_base, mtddef_override_copy);
|
||||
}
|
||||
}
|
||||
|
||||
//Apply Postfixes first so that Prefixes can jump to the right instruction
|
||||
for (int i = 0; i < moduleTypes.Length; i++)
|
||||
{
|
||||
Type moduleType = moduleTypes[i];
|
||||
Dictionary<string, MethodOverrideInfo> dict_targets = GetMethodOverrideTargets<MethodTargetPostfixAttribute>(i);
|
||||
string moduleID = ModuleUtils.CreateFieldName(moduleType);
|
||||
foreach (var pair in dict_targets)
|
||||
{
|
||||
MethodDefinition mtddef_root = module.ImportReference(pair.Value.mtdinf_base.GetBaseDefinition()).Resolve();
|
||||
MethodDefinition mtddef_target = module.ImportReference(pair.Value.mtdinf_target).Resolve();
|
||||
MethodPatchInfo mtdpinf_derived = GetOrCreateOverride(dict_overrides, pair.Key, pair.Value.mtdref_base);
|
||||
MethodDefinition mtddef_derived = mtdpinf_derived.Method;
|
||||
|
||||
if (!dict_all_states.TryGetValue(pair.Key, out var dict_states))
|
||||
{
|
||||
dict_states = new Dictionary<string, VariableDefinition>();
|
||||
dict_all_states.Add(pair.Key, dict_states);
|
||||
}
|
||||
var list_inst_pars = MatchArguments(mtddef_root, mtdpinf_derived, mtddef_target, i, true, dict_states, moduleID);
|
||||
//insert invocation
|
||||
var il = mtddef_derived.Body.GetILProcessor();
|
||||
foreach (var ins in list_inst_pars)
|
||||
{
|
||||
il.InsertBefore(mtdpinf_derived.PostfixEnd, ins);
|
||||
}
|
||||
il.InsertBefore(mtdpinf_derived.PostfixEnd, il.Create(OpCodes.Call, module.ImportReference(mtddef_target)));
|
||||
if (mtdpinf_derived.PostfixBegin == null)
|
||||
mtdpinf_derived.PostfixBegin = list_inst_pars[0];
|
||||
}
|
||||
}
|
||||
|
||||
//Apply Prefixes
|
||||
for (int i = moduleTypes.Length - 1; i >= 0; i--)
|
||||
{
|
||||
Type moduleType = moduleTypes[i];
|
||||
Dictionary<string, MethodOverrideInfo> dict_targets = GetMethodOverrideTargets<MethodTargetPrefixAttribute>(i);
|
||||
string moduleID = ModuleUtils.CreateFieldName(moduleType);
|
||||
foreach (var pair in dict_targets)
|
||||
{
|
||||
MethodDefinition mtddef_root = module.ImportReference(pair.Value.mtdinf_base.GetBaseDefinition()).Resolve();
|
||||
MethodDefinition mtddef_target = module.ImportReference(pair.Value.mtdinf_target).Resolve();
|
||||
MethodPatchInfo mtdpinf_derived = GetOrCreateOverride(dict_overrides, pair.Key, pair.Value.mtdref_base);
|
||||
MethodDefinition mtddef_derived = mtdpinf_derived.Method;
|
||||
dict_all_states.TryGetValue(pair.Key, out var dict_states);
|
||||
var list_inst_pars = MatchArguments(mtddef_root, mtdpinf_derived, mtddef_target, i, false, dict_states, moduleID);
|
||||
//insert invocation
|
||||
var il = mtdpinf_derived.Method.Body.GetILProcessor();
|
||||
Instruction ins_insert = mtdpinf_derived.PrefixBegin;
|
||||
foreach (var ins in list_inst_pars)
|
||||
{
|
||||
il.InsertBefore(ins_insert, ins);
|
||||
}
|
||||
il.InsertBefore(ins_insert, il.Create(OpCodes.Call, module.ImportReference(mtddef_target)));
|
||||
il.InsertBefore(ins_insert, il.Create(OpCodes.Brfalse_S, mtdpinf_derived.PostfixBegin ?? mtdpinf_derived.PostfixEnd));
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var pair in dict_all_states)
|
||||
{
|
||||
var dict_states = pair.Value;
|
||||
if (dict_states.Count > 0)
|
||||
{
|
||||
Log.Error($"__state variable count does not match in prefixes and postfixes for {pair.Key}! check following modules:\n" + string.Join("\n", dict_states.Keys));
|
||||
throw new Exception();
|
||||
}
|
||||
}
|
||||
|
||||
//Add all overrides to new type
|
||||
foreach (var mtd in dict_overrides.Values)
|
||||
{
|
||||
typedef_newTarget.Methods.Add(mtd.Method);
|
||||
|
||||
//Log.Out($"Add method override to new action: {mtd.Method.Name}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="targetType"></param>
|
||||
/// <param name="moduleType"></param>
|
||||
/// <param name="module"></param>
|
||||
/// <returns></returns>
|
||||
private Dictionary<string, MethodOverrideInfo> GetMethodOverrideTargets<T>(int moduleIndex) where T : Attribute, IMethodTarget
|
||||
{
|
||||
Type moduleType = moduleTypes[moduleIndex];
|
||||
Dictionary<string, MethodOverrideInfo> dict_overrides = new Dictionary<string, MethodOverrideInfo>();
|
||||
const BindingFlags searchFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
|
||||
const BindingFlags extensionFlags = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
|
||||
foreach (var mtd in moduleType.GetMethods(searchFlags).Concat(moduleExtensionTypes[moduleIndex].SelectMany(t => t.GetMethods(extensionFlags))))
|
||||
{
|
||||
if (mtd.GetCustomAttribute<T>() != null)
|
||||
{
|
||||
foreach (HarmonyPatch hp in mtd.GetCustomAttributes<HarmonyPatch>())
|
||||
{
|
||||
if (hp != null && (hp.info.declaringType == null || hp.info.declaringType.IsAssignableFrom(targetType)))
|
||||
{
|
||||
var hm = hp.info;
|
||||
hm.methodType = hm.methodType ?? MethodType.Normal;
|
||||
var hmclone = hm.Clone();
|
||||
hmclone.declaringType = targetType;
|
||||
string id = hm.GetTargetMethodIdentifier();
|
||||
MethodInfo mtdinf_base = hmclone.GetBaseMethod() as MethodInfo;
|
||||
if (mtdinf_base == null || !mtdinf_base.IsVirtual || mtdinf_base.IsFinal)
|
||||
{
|
||||
Log.Error($"Method not found: {moduleType.FullName} on {hmclone.methodName}\n{hmclone.ToString()}");
|
||||
continue;
|
||||
}
|
||||
|
||||
MethodReference mtdref_base = module.ImportReference(mtdinf_base);
|
||||
//Find preferred patch
|
||||
if (dict_overrides.TryGetValue(id, out var info))
|
||||
{
|
||||
if (hm.declaringType == null)
|
||||
continue;
|
||||
//cur action type is sub or same class of cur preferred type
|
||||
//cur preferred type is sub class of previous preferred type
|
||||
//means cur preferred type is closer to the action type in inheritance hierachy than the previous one
|
||||
if (hm.declaringType.IsAssignableFrom(targetType) && (info.prefType == null || hm.declaringType.IsSubclassOf(info.prefType)))
|
||||
{
|
||||
dict_overrides[id] = new MethodOverrideInfo(mtd, mtdinf_base, mtdref_base, hm.declaringType);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dict_overrides[id] = new MethodOverrideInfo(mtd, mtdinf_base, mtdref_base, hm.declaringType);
|
||||
}
|
||||
//Log.Out($"Add method override: {id} for {mtdref_base.FullName}/{mtdinf_base.Name}, action type: {itemActionType.Name}");
|
||||
}
|
||||
else
|
||||
{
|
||||
//Log.Out($"No override target found or preferred type not match on {mtd.Name}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return dict_overrides;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get or create override MethodDefinition of mtdref_base.
|
||||
/// </summary>
|
||||
/// <param name="dict_overrides"></param>
|
||||
/// <param name="id"></param>
|
||||
/// <param name="mtdref_base"></param>
|
||||
/// <param name="module"></param>
|
||||
/// <returns></returns>
|
||||
private MethodPatchInfo GetOrCreateOverride(Dictionary<string, MethodPatchInfo> dict_overrides, string id, MethodReference mtdref_base, MethodDefinition mtddef_base_override = null)
|
||||
{
|
||||
//if (mtddef_base.FullName == "CreateModifierData")
|
||||
// throw new MethodAccessException($"YOU SHOULD NOT MANUALLY MODIFY CreateModifierData!");
|
||||
if (dict_overrides.TryGetValue(id, out var mtdpinf_derived))
|
||||
{
|
||||
return mtdpinf_derived;
|
||||
}
|
||||
//when overriding, retain attributes of base but make sure to remove the 'new' keyword which presents if you are overriding the root method
|
||||
MethodDefinition mtddef_base = mtdref_base.Resolve();
|
||||
MethodDefinition mtddef_derived = new MethodDefinition(mtddef_base.Name, (mtddef_base.Attributes | MethodAttributes.Virtual | MethodAttributes.HideBySig | MethodAttributes.ReuseSlot) & ~MethodAttributes.NewSlot, module.ImportReference(mtddef_base.ReturnType));
|
||||
|
||||
//Log.Out($"Create method override: {id} for {mtdref_base.FullName}");
|
||||
foreach (var par in mtddef_base_override?.Parameters?.Skip(1) ?? mtddef_base.Parameters)
|
||||
{
|
||||
ParameterDefinition pardef = new ParameterDefinition(par.Name, par.Attributes, module.ImportReference(par.ParameterType));
|
||||
if (par.HasConstant)
|
||||
pardef.Constant = par.Constant;
|
||||
mtddef_derived.Parameters.Add(pardef);
|
||||
}
|
||||
mtddef_derived.Body.Variables.Clear();
|
||||
mtddef_derived.Body.InitLocals = true;
|
||||
mtddef_derived.Body.Variables.Add(new VariableDefinition(module.TypeSystem.Boolean));
|
||||
bool hasReturnVal = mtddef_derived.ReturnType.MetadataType != MetadataType.Void;
|
||||
if (hasReturnVal)
|
||||
{
|
||||
mtddef_derived.Body.Variables.Add(new VariableDefinition(module.ImportReference(mtddef_base.ReturnType)));
|
||||
}
|
||||
var il = mtddef_derived.Body.GetILProcessor();
|
||||
if (hasReturnVal)
|
||||
{
|
||||
il.Emit(OpCodes.Ldloca_S, mtddef_derived.Body.Variables[1]);
|
||||
il.Emit(OpCodes.Initobj, module.ImportReference(mtddef_derived.ReturnType));
|
||||
}
|
||||
il.Emit(OpCodes.Ldc_I4_0);
|
||||
il.Emit(OpCodes.Stloc_S, mtddef_derived.Body.Variables[0]);
|
||||
Instruction prefixBegin = il.Create(OpCodes.Ldc_I4_1);
|
||||
il.Append(prefixBegin);
|
||||
il.Emit(OpCodes.Stloc_S, mtddef_derived.Body.Variables[0]);
|
||||
il.Emit(OpCodes.Ldarg_0);
|
||||
for (int i = 0; i < mtddef_derived.Parameters.Count; i++)
|
||||
{
|
||||
var par = mtddef_derived.Parameters[i];
|
||||
il.Emit(par.ParameterType.IsByReference ? OpCodes.Ldarga_S : OpCodes.Ldarg_S, par);
|
||||
}
|
||||
il.Emit(OpCodes.Call, mtddef_base_override ?? module.ImportReference(mtdref_base));
|
||||
if (hasReturnVal)
|
||||
{
|
||||
il.Emit(OpCodes.Stloc_S, mtddef_derived.Body.Variables[1]);
|
||||
il.Emit(OpCodes.Ldloc_S, mtddef_derived.Body.Variables[1]);
|
||||
}
|
||||
il.Emit(OpCodes.Ret);
|
||||
mtdpinf_derived = new MethodPatchInfo(mtddef_derived, mtddef_derived.Body.Instructions[mtddef_derived.Body.Instructions.Count - (hasReturnVal ? 2 : 1)], prefixBegin);
|
||||
dict_overrides.Add(id, mtdpinf_derived);
|
||||
return mtdpinf_derived;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a List<Instruction> that loads all arguments required to call the method onto stack.
|
||||
/// </summary>
|
||||
/// <param name="mtddef_root">The root definition of this method.</param>
|
||||
/// <param name="mtdpinf_derived">The override method.</param>
|
||||
/// <param name="mtddef_target">The patch method to be called.</param>
|
||||
/// <param name="flddef_module">The injected module field.</param>
|
||||
/// <param name="flddef_data">The injected data field.</param>
|
||||
/// <param name="module">The assembly's main module.</param>
|
||||
/// <param name="itemActionType">The base ItemAction type.</param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="MissingFieldException"></exception>
|
||||
/// <exception cref="ArgumentNullException"></exception>
|
||||
/// <exception cref="ArgumentException"></exception>
|
||||
private List<Instruction> MatchArguments(MethodDefinition mtddef_root, MethodPatchInfo mtdpinf_derived, MethodDefinition mtddef_target, int moduleIndex, bool isPostfix, Dictionary<string, VariableDefinition> dict_states, string moduleID)
|
||||
{
|
||||
FieldDefinition flddef_module = arr_flddef_modules[moduleIndex];
|
||||
var mtddef_derived = mtdpinf_derived.Method;
|
||||
var il = mtddef_derived.Body.GetILProcessor();
|
||||
//Match parameters
|
||||
List<Instruction> list_inst_pars = new List<Instruction>();
|
||||
list_inst_pars.Add(il.Create(OpCodes.Ldarg_0));
|
||||
list_inst_pars.Add(il.Create(OpCodes.Ldfld, flddef_module));
|
||||
foreach (var par in mtddef_target.IsStatic ? mtddef_target.Parameters.Skip(1) : mtddef_target.Parameters)
|
||||
{
|
||||
if (par.Name.StartsWith("___"))
|
||||
{
|
||||
//___ means non public fields
|
||||
string str_fldname = par.Name.Substring(3);
|
||||
FieldDefinition flddef_target = module.ImportReference(targetType.GetField(str_fldname, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)).Resolve();
|
||||
if (flddef_target == null)
|
||||
throw new MissingFieldException($"Field with name \"{str_fldname}\" not found! Patch method: {mtddef_target.DeclaringType.FullName}.{mtddef_target.Name}");
|
||||
if (flddef_target.IsStatic)
|
||||
{
|
||||
list_inst_pars.Add(il.Create((par.ParameterType.IsByReference) ? OpCodes.Ldsflda : OpCodes.Ldsfld, module.ImportReference(flddef_target)));
|
||||
}
|
||||
else
|
||||
{
|
||||
list_inst_pars.Add(il.Create(OpCodes.Ldarg_0));
|
||||
list_inst_pars.Add(il.Create(par.ParameterType.IsByReference ? OpCodes.Ldflda : OpCodes.Ldfld, module.ImportReference(flddef_target)));
|
||||
}
|
||||
}
|
||||
else if (!MatchSpecialParameters(par, mtddef_target, mtdpinf_derived, moduleIndex, list_inst_pars, il, isPostfix, dict_states, moduleID))
|
||||
{
|
||||
//match param by name
|
||||
int index = -1;
|
||||
for (int j = 0; j < mtddef_root.Parameters.Count; j++)
|
||||
{
|
||||
if (mtddef_root.Parameters[j].Name == par.Name)
|
||||
{
|
||||
index = mtddef_root.Parameters[j].Index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (index < 0)
|
||||
throw new ArgumentException($"Parameter \"{par.Name}\" not found! Patch method: {mtddef_target.DeclaringType.FullName}.{mtddef_target.Name}");
|
||||
try
|
||||
{
|
||||
//Log.Out($"Match Parameter {par.Name} to {mtddef_derived.Parameters[index].Name}/{mtddef_root.Parameters[index].Name} index: {index}");
|
||||
|
||||
}
|
||||
catch (ArgumentOutOfRangeException e)
|
||||
{
|
||||
Log.Error($"index {index} parameter {par.Name}" +
|
||||
$"root pars: {{{string.Join(",", mtddef_root.Parameters.Select(p => p.Name + "/" + p.Index).ToArray())}}}" +
|
||||
$"derived pars: {{{string.Join(",", mtddef_derived.Parameters.Select(p => p.Name + "/" + p.Index).ToArray())}}}");
|
||||
throw e;
|
||||
}
|
||||
if (!mtddef_derived.Parameters[index].ParameterType.IsByReference)
|
||||
{
|
||||
list_inst_pars.Add(il.Create(par.ParameterType.IsByReference ? OpCodes.Ldarga_S : OpCodes.Ldarg_S, mtddef_derived.Parameters[index]));
|
||||
}
|
||||
else
|
||||
{
|
||||
list_inst_pars.Add(il.Create(OpCodes.Ldarg_S, mtddef_derived.Parameters[index]));
|
||||
if (!par.ParameterType.IsByReference)
|
||||
{
|
||||
list_inst_pars.Add(il.Create(OpCodes.Ldind_Ref));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return list_inst_pars;
|
||||
}
|
||||
|
||||
private bool MatchSpecialParameters(ParameterDefinition par, MethodDefinition mtddef_target, MethodPatchInfo mtdpinf_derived, int moduleIndex, List<Instruction> list_inst_pars, ILProcessor il, bool isPostfix, Dictionary<string, VariableDefinition> dict_states, string moduleID)
|
||||
{
|
||||
MethodDefinition mtddef_derived = mtdpinf_derived.Method;
|
||||
switch (par.Name)
|
||||
{
|
||||
//load ItemAction instance
|
||||
case "__instance":
|
||||
list_inst_pars.Add(il.Create(OpCodes.Ldarg_0));
|
||||
break;
|
||||
//load return value
|
||||
case "__result":
|
||||
list_inst_pars.Add(il.Create(par.ParameterType.IsByReference ? OpCodes.Ldloca_S : OpCodes.Ldloc_S, mtddef_derived.Body.Variables[1]));
|
||||
break;
|
||||
//for postfix only, indicates whether original method is executed
|
||||
case "__runOriginal":
|
||||
if (par.ParameterType.IsByReference)
|
||||
throw new ArgumentException($"__runOriginal is readonly! Patch method: {mtddef_target.DeclaringType.FullName}.{mtddef_target.Name}");
|
||||
list_inst_pars.Add(il.Create(OpCodes.Ldloc_S, mtddef_derived.Body.Variables[0]));
|
||||
break;
|
||||
case "__state":
|
||||
if (dict_states == null)
|
||||
{
|
||||
throw new ArgumentNullException($"__state is found in prefix but no matching postfix exists! Patch method: {mtddef_target.DeclaringType.FullName}.{mtddef_target.Name}");
|
||||
}
|
||||
if (!isPostfix && !dict_states.TryGetValue(moduleID, out var vardef))
|
||||
{
|
||||
throw new KeyNotFoundException($"__state is found in prefix but not found in corresponding postfix! Patch method: {mtddef_target.DeclaringType.FullName}.{mtddef_target.Name}");
|
||||
}
|
||||
if (par.IsOut && isPostfix)
|
||||
{
|
||||
throw new ArgumentException($"__state is marked as out parameter in postfix! Patch method: {mtddef_target.DeclaringType.FullName}.{mtddef_target.Name}");
|
||||
}
|
||||
if (!par.IsOut && !isPostfix)
|
||||
{
|
||||
throw new ArgumentException($"__state is not marked as out in prefix! Patch method: {mtddef_target.DeclaringType.FullName}.{mtddef_target.Name}");
|
||||
}
|
||||
if (isPostfix)
|
||||
{
|
||||
vardef = new VariableDefinition(module.ImportReference(par.ParameterType));
|
||||
mtddef_derived.Body.Variables.Add(vardef);
|
||||
dict_states.Add(moduleID, vardef);
|
||||
var ins = mtddef_derived.Body.Instructions[0];
|
||||
il.InsertBefore(ins, il.Create(OpCodes.Ldloca_S, vardef));
|
||||
il.InsertBefore(ins, il.Create(OpCodes.Initobj, module.ImportReference(par.ParameterType)));
|
||||
list_inst_pars.Add(il.Create(OpCodes.Ldloc_S, vardef));
|
||||
}
|
||||
else
|
||||
{
|
||||
vardef = dict_states[moduleID];
|
||||
dict_states.Remove(moduleID);
|
||||
list_inst_pars.Add(il.Create(OpCodes.Ldloca_S, vardef));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return processor != null ? processor.MatchSpecialArgs(par, mtddef_target, mtdpinf_derived, moduleIndex, list_inst_pars, il) : false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void MakeContainerFor(TypeDefinition typedef_container, Type type_module, out FieldDefinition flddef_module)
|
||||
{
|
||||
var typeref_module = module.ImportReference(type_module);
|
||||
flddef_module = new FieldDefinition(ModuleUtils.CreateFieldName(type_module), FieldAttributes.Public, typeref_module);
|
||||
typedef_container.Fields.Add(flddef_module);
|
||||
typedef_container.Interfaces.Add(new InterfaceImplementation(typeref_interface.MakeGenericInstanceType(typeref_module)));
|
||||
PropertyDefinition propdef_instance = new PropertyDefinition("Instance", Mono.Cecil.PropertyAttributes.None, typeref_module);
|
||||
MethodDefinition mtddef_instance_getter = new MethodDefinition("get_Instance", MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual | MethodAttributes.Final, typeref_module);
|
||||
mtddef_instance_getter.Overrides.Add(module.ImportReference(AccessTools.Method(typeof(IModuleContainerFor<>).MakeGenericType(type_module), "get_Instance")));
|
||||
typedef_container.Methods.Add(mtddef_instance_getter);
|
||||
mtddef_instance_getter.Body = new Mono.Cecil.Cil.MethodBody(mtddef_instance_getter);
|
||||
var generator = mtddef_instance_getter.Body.GetILProcessor();
|
||||
generator.Emit(OpCodes.Ldarg_0);
|
||||
generator.Emit(OpCodes.Ldfld, flddef_module);
|
||||
generator.Emit(OpCodes.Ret);
|
||||
propdef_instance.GetMethod = mtddef_instance_getter;
|
||||
typedef_container.Properties.Add(propdef_instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
67
Scripts/Utilities/Modular/ModuleUtils.cs
Normal file
67
Scripts/Utilities/Modular/ModuleUtils.cs
Normal file
@@ -0,0 +1,67 @@
|
||||
using HarmonyLib;
|
||||
using Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
using Mono.Cecil.Rocks;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace KFCommonUtilityLib
|
||||
{
|
||||
public static class ModuleUtils
|
||||
{
|
||||
public static string CreateFieldName(Type moduleType)
|
||||
{
|
||||
return (moduleType.FullName + "_" + moduleType.Assembly.GetName().Name).ReplaceInvalidChar();
|
||||
}
|
||||
|
||||
public static string CreateFieldName(TypeReference moduleType)
|
||||
{
|
||||
return (moduleType.FullName + "_" + moduleType.Module.Assembly.Name.Name).ReplaceInvalidChar();
|
||||
}
|
||||
|
||||
public static string CreateTypeName(Type itemActionType, params Type[] moduleTypes)
|
||||
{
|
||||
string typeName = itemActionType.FullName + "_" + itemActionType.Assembly.GetName().Name;
|
||||
foreach (Type type in moduleTypes)
|
||||
{
|
||||
if (type != null)
|
||||
typeName += "__" + type.FullName + "_" + type.Assembly.GetName().Name;
|
||||
}
|
||||
typeName = typeName.ReplaceInvalidChar();
|
||||
return typeName;
|
||||
}
|
||||
|
||||
public static string CreateTypeName(TypeReference itemActionType, params TypeReference[] moduleTypes)
|
||||
{
|
||||
string typeName = itemActionType.FullName + "_" + itemActionType.Module.Assembly.Name.Name;
|
||||
foreach (TypeReference type in moduleTypes)
|
||||
{
|
||||
if (type != null)
|
||||
typeName += "__" + type.FullName + "_" + type.Module.Assembly.Name.Name;
|
||||
}
|
||||
typeName = typeName.ReplaceInvalidChar();
|
||||
return typeName;
|
||||
}
|
||||
|
||||
private static string ReplaceInvalidChar(this string self)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < self.Length; i++)
|
||||
{
|
||||
char c = self[i];
|
||||
if (!char.IsLetterOrDigit(c) && c != '_')
|
||||
{
|
||||
sb.Append('_');
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append(c);
|
||||
}
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user