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++;
            }
        }
Beispiel #2
0
        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++;
            }
        }
Beispiel #3
0
        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++;
            }
        }
Beispiel #4
0
        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;
            }
        }