using System.IO; using System.Linq; using System.Reflection; using Mono.Cecil; using Mono.Cecil.Cil; namespace COM3D2.CacheEditMenu.Patcher { public static class Patcher { public static readonly string[] TargetAssemblyNames = { "Assembly-CSharp.dll" }; public static readonly string SybarisPath = Path.GetDirectoryName(typeof(Patcher).Assembly.Location); private static void PatchHelper(MethodDefinition target, MethodReference prefix, MethodReference postix, params int[] ldargs) { var il = target.Body.GetILProcessor(); var resultVar = target.ReturnType.FullName != "System.Void" ? new VariableDefinition(target.Module.ImportReference(target.ReturnType)) : null; if(resultVar != null) target.Body.Variables.Add(resultVar); var ins = target.Body.Instructions[0]; if(resultVar != null) il.InsertBefore(ins, il.Create(OpCodes.Ldloca, resultVar)); foreach (var ldarg in ldargs) il.InsertBefore(ins, il.Create(OpCodes.Ldarg, ldarg)); il.InsertBefore(ins, il.Create(OpCodes.Call, target.Module.ImportReference(prefix))); il.InsertBefore(ins, il.Create(OpCodes.Brtrue, ins)); if(resultVar != null) il.InsertBefore(ins, il.Create(OpCodes.Ldloc, resultVar)); il.InsertBefore(ins, il.Create(OpCodes.Ret)); if (postix == null) return; ins = il.Create(OpCodes.Nop); foreach (var bodyInstruction in target.Body.Instructions.Where(bodyInstruction => bodyInstruction.OpCode == OpCodes.Ret).Skip(1)) { bodyInstruction.OpCode = OpCodes.Br; bodyInstruction.Operand = ins; } il.Append(ins); ins = il.Create(OpCodes.Ret); il.Append(ins); foreach (var ldarg in ldargs) il.InsertBefore(ins, il.Create(OpCodes.Ldarg, ldarg)); il.InsertBefore(ins, il.Create(OpCodes.Call, target.Module.ImportReference(postix))); } public static void Patch(AssemblyDefinition ad) { var hookAd = AssemblyDefinition.ReadAssembly( typeof(Patcher).Assembly.GetManifestResourceStream( "COM3D2.CacheEditMenu.Patcher.COM3D2.CacheEditMenu.dll")); var sceneEdit = ad.MainModule.GetType("SceneEdit"); var getMenuItemSetUp = sceneEdit.Methods.FirstOrDefault(m => m.Name == "GetMenuItemSetUP"); var initMenuItemScript = sceneEdit.Methods.FirstOrDefault(m => m.Name == "InitMenuItemScript"); // var importCm = ad.MainModule.GetType("ImportCM"); // var createTexture = importCm.Methods.FirstOrDefault(m => m.Name == "CreateTexture" && m.Parameters.Count == 1); var hookType = hookAd.MainModule.GetType("COM3D2.CacheEditMenu.Hooks"); var getMenuItemSetUpPrefix = hookType.Methods.FirstOrDefault(m => m.Name == "Prefix"); var getMenuItemSetUpPostfix = hookType.Methods.FirstOrDefault(m => m.Name == "Postfix"); var createTexturePrefix = hookType.Methods.FirstOrDefault(m => m.Name == "CreateTexturePrefix"); PatchHelper(getMenuItemSetUp, getMenuItemSetUpPrefix, getMenuItemSetUpPostfix, 0, 1); var il = initMenuItemScript.Body.GetILProcessor(); foreach (var ins in il.Body.Instructions.ToList()) { if (ins.OpCode == OpCodes.Call && ((MethodReference)ins.Operand).Name == "CreateTexture") il.InsertBefore(ins, il.Create(OpCodes.Call, ad.MainModule.ImportReference(createTexturePrefix))); } var buffer = new byte[4096]; using(var s = typeof(Patcher).Assembly.GetManifestResourceStream( "COM3D2.CacheEditMenu.Patcher.COM3D2.CacheEditMenu.dll")) using (var ms = new MemoryStream()) { int read; while ((read = s.Read(buffer, 0, buffer.Length)) > 0) ms.Write(buffer, 0, read); Assembly.Load(ms.ToArray()); } } } }