diff --git a/Libraries/SPTarkov.Reflection/CodeWrapper/Code.cs b/Libraries/SPTarkov.Reflection/CodeWrapper/Code.cs new file mode 100644 index 00000000..87c8d832 --- /dev/null +++ b/Libraries/SPTarkov.Reflection/CodeWrapper/Code.cs @@ -0,0 +1,46 @@ +using System.Reflection.Emit; + +namespace SPTarkov.Reflection.CodeWrapper; + +public class Code +{ + public OpCode OpCode { get; } + public Type? CallerType { get; } + public object? OperandTarget { get; } + public Type[]? Parameters { get; } + public bool HasOperand { get; } + + public Code(OpCode opCode) + { + OpCode = opCode; + HasOperand = false; + } + + public Code(OpCode opCode, object operandTarget) + { + OpCode = opCode; + OperandTarget = operandTarget; + HasOperand = true; + } + + public Code(OpCode opCode, Type callerType) + { + OpCode = opCode; + CallerType = callerType; + HasOperand = true; + } + + public Code(OpCode opCode, Type callerType, object operandTarget, Type[] parameters = null) + { + OpCode = opCode; + CallerType = callerType; + OperandTarget = operandTarget; + Parameters = parameters; + HasOperand = true; + } + + public virtual Label? GetLabel() + { + return null; + } +} diff --git a/Libraries/SPTarkov.Reflection/CodeWrapper/CodeGenerator.cs b/Libraries/SPTarkov.Reflection/CodeWrapper/CodeGenerator.cs new file mode 100644 index 00000000..2c68965d --- /dev/null +++ b/Libraries/SPTarkov.Reflection/CodeWrapper/CodeGenerator.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection.Emit; +using HarmonyLib; + +namespace SPTarkov.Reflection.CodeWrapper; + +/// +/// Helper class to generate IL code for transpilers +/// +public class CodeGenerator +{ + public static List GenerateInstructions(List codes) + { + var list = new List(); + + foreach (Code code in codes) + { + list.Add(ParseCode(code)); + } + + return list; + } + + private static CodeInstruction ParseCode(Code code) + { + if (!code.HasOperand) + { + return new CodeInstruction(code.OpCode) { labels = GetLabelList(code) }; + } + + if (code.OpCode == OpCodes.Ldfld || code.OpCode == OpCodes.Ldflda || code.OpCode == OpCodes.Stfld) + { + return new CodeInstruction(code.OpCode, AccessTools.Field(code.CallerType, code.OperandTarget as string)) { labels = GetLabelList(code) }; + } + + if (code.OpCode == OpCodes.Call || code.OpCode == OpCodes.Callvirt) + { + return new CodeInstruction(code.OpCode, AccessTools.Method(code.CallerType, code.OperandTarget as string, code.Parameters)) { labels = GetLabelList(code) }; + } + + if (code.OpCode == OpCodes.Box) + { + return new CodeInstruction(code.OpCode, code.CallerType) { labels = GetLabelList(code) }; + } + + if (code.OpCode == OpCodes.Br || code.OpCode == OpCodes.Brfalse || code.OpCode == OpCodes.Brtrue || code.OpCode == OpCodes.Brtrue_S + || code.OpCode == OpCodes.Brfalse_S || code.OpCode == OpCodes.Br_S) + { + return new CodeInstruction(code.OpCode, code.OperandTarget) { labels = GetLabelList(code) }; + } + + if (code.OpCode == OpCodes.Ldftn) + { + return new CodeInstruction(code.OpCode, AccessTools.Method(code.CallerType, code.OperandTarget as string, code.Parameters)) { labels = GetLabelList(code) }; + } + + if (code.OpCode == OpCodes.Newobj) + { + return new CodeInstruction(code.OpCode, code.CallerType.GetConstructors().FirstOrDefault(x => x.GetParameters().Length == code.Parameters.Length)) { labels = GetLabelList(code) }; + } + + throw new ArgumentException($"Code with OpCode {code.OpCode.ToString()} is not supported."); + } + + private static List