public void CheckAndInjectFixPath(MethodDefinition method, ref int instri) { Mono.Collections.Generic.Collection <Instruction> instrs = method.Body.Instructions; Instruction instr = instrs[instri]; /* This hack at least is somewhat reasonable... * """Fix""" paths being passed to methods that heavily rely on proper paths. * This is for when MONO_IOMAP=all isn't enough because it doesn't affect native libs. * -ade */ if (instr.OpCode != OpCodes.Call && instr.OpCode != OpCodes.Callvirt && instr.OpCode != OpCodes.Newobj) { return; } MethodReference called = (MethodReference)instr.Operand; if (!FixPathsFor.Contains(called.GetFindableID()) && !FixPathsFor.Contains(called.GetFindableID(withType: false)) && !FixPathsFor.Contains(called.GetFindableID(simple: true)) && !FixPathsFor.Contains(called.GetFindableID(withType: false, simple: true)) ) { return; } MethodReference mr_Push = method.Module.ImportReference(StackOpHelper.m_Push); MethodReference mr_Pop = method.Module.ImportReference(StackOpHelper.m_Pop); MethodReference mr_FixPath = method.Module.ImportReference(m_FileSystemHelper_FixPath); ILProcessor il = method.Body.GetILProcessor(); // Go through all parameters in stack, store all passed parameters in StackOpHelper and handle accordingly. for (int i = called.Parameters.Count - 1; i > -1; --i) { if (called.Parameters[i].ParameterType.MetadataType == MetadataType.String) { // Before pushing to StackOpHelper, try to fix the path (we're assuming it's a path). il.InsertBefore(instrs[instri], il.Create(OpCodes.Call, mr_FixPath)); instri++; } GenericInstanceMethod push = new GenericInstanceMethod(mr_Push); push.GenericArguments.Add(called.Parameters[i].ParameterType); il.InsertBefore(instrs[instri], il.Create(OpCodes.Call, push)); instri++; } // Pop StackOpHelper. for (int i = 0; i < called.Parameters.Count; i++) { GenericInstanceMethod pop = new GenericInstanceMethod(mr_Pop); pop.GenericArguments.Add(called.Parameters[i].ParameterType); il.InsertBefore(instrs[instri], il.Create(OpCodes.Call, pop)); instri++; } }
public static void PatchAreaCompleteCtor(MethodDefinition method) { if (!method.HasBody) { return; } ParameterDefinition paramMeta = new ParameterDefinition("meta", ParameterAttributes.None, MonoModRule.Modder.FindType("Celeste.Mod.Meta.MapMetaCompleteScreen")); method.Parameters.Add(paramMeta); Mono.Collections.Generic.Collection <Instruction> instrs = method.Body.Instructions; ILProcessor il = method.Body.GetILProcessor(); for (int instri = 0; instri < instrs.Count; instri++) { Instruction instr = instrs[instri]; MethodReference calling = instr.Operand as MethodReference; string callingID = calling?.GetFindableID(); // The matching CompleteRenderer .ctor has been added manually, thus manually relink to it. if (instr.OpCode != OpCodes.Newobj || ( callingID != "System.Void Celeste.CompleteRenderer::.ctor(System.Xml.XmlElement,Monocle.Atlas,System.Single,System.Action)" )) { continue; } instr.Operand = calling.DeclaringType.Resolve().FindMethod("System.Void Celeste.CompleteRenderer::.ctor(System.Xml.XmlElement,Monocle.Atlas,System.Single,System.Action,Celeste.Mod.Meta.MapMetaCompleteScreen)"); instrs.Insert(instri, il.Create(OpCodes.Ldarg, paramMeta)); instri++; } }
public static void PatchLevelExitRoutine(MethodDefinition method) { FieldDefinition f_completeMeta = method.DeclaringType.FindField("completeMeta"); FieldDefinition f_this = null; // The level exit routine is stored in a compiler-generated method. foreach (TypeDefinition nest in method.DeclaringType.NestedTypes) { if (!nest.Name.StartsWith("<" + method.Name + ">d__")) { continue; } method = nest.FindMethod("System.Boolean MoveNext()") ?? method; f_this = method.DeclaringType.FindField("<>4__this"); break; } if (!method.HasBody) { return; } Mono.Collections.Generic.Collection <Instruction> instrs = method.Body.Instructions; ILProcessor il = method.Body.GetILProcessor(); for (int instri = 0; instri < instrs.Count; instri++) { Instruction instr = instrs[instri]; MethodReference calling = instr.Operand as MethodReference; string callingID = calling?.GetFindableID(); // The original AreaComplete .ctor has been modified to contain an extra parameter. // For safety, check against both signatures. if (instr.OpCode != OpCodes.Newobj || ( callingID != "System.Void Celeste.AreaComplete::.ctor(Celeste.Session,System.Xml.XmlElement,Monocle.Atlas,Celeste.HiresSnow)" && callingID != "System.Void Celeste.AreaComplete::.ctor(Celeste.Session,System.Xml.XmlElement,Monocle.Atlas,Celeste.HiresSnow,Celeste.Mod.Meta.MapMetaCompleteScreen)" )) { continue; } // For safety, replace the .ctor call if the new .ctor exists already. instr.Operand = calling.DeclaringType.Resolve().FindMethod("System.Void Celeste.AreaComplete::.ctor(Celeste.Session,System.Xml.XmlElement,Monocle.Atlas,Celeste.HiresSnow,Celeste.Mod.Meta.MapMetaCompleteScreen)") ?? instr.Operand; instrs.Insert(instri, il.Create(OpCodes.Ldarg_0)); instri++; if (f_this != null) { instrs.Insert(instri, il.Create(OpCodes.Ldfld, f_this)); instri++; } instrs.Insert(instri, il.Create(OpCodes.Ldfld, f_completeMeta)); instri++; } }
public static void ProxyFileCalls(MethodDefinition method, CustomAttribute attrib) { if (!method.HasBody) { return; } if (FileProxy == null) { FileProxy = MonoModRule.Modder.FindType("Celeste.Mod.Helpers.FileProxy")?.Resolve(); } if (FileProxy == null) { return; } foreach (Instruction instr in method.Body.Instructions) { // System.IO.File.* calls are always static calls. if (instr.OpCode != OpCodes.Call) { continue; } // We only want to replace System.IO.File.* calls. MethodReference calling = instr.Operand as MethodReference; if (calling?.DeclaringType?.FullName != "System.IO.File") { continue; } MethodDefinition replacement; if (!FileProxyCache.TryGetValue(calling.Name, out replacement)) { FileProxyCache[calling.GetFindableID(withType: false)] = replacement = FileProxy.FindMethod(calling.GetFindableID(withType: false)); } if (replacement == null) { continue; // We haven't got any replacement. } // Replace the called method with our replacement. instr.Operand = replacement; } }