示例#1
0
        /// <summary>
        /// Inline classes marked as [Patch], copying fields and replacing method implementations.
        /// As you can probably guess from the code, this is wholly incomplete and will certainly break and have to be
        /// extended in the future.
        /// </summary>
        public static void Patch(string modModulePath)
        {
            var baseModule = ModuleDefinition.ReadModule("PatchedTowerFall.exe");

            var modModule = ModuleDefinition.ReadModule(modModulePath);

            Func <TypeReference, bool> patchType = (type) => {
                if (type.Scope == modModule)
                {
                    return(type.Resolve().CustomAttributes.Any(attr => attr.AttributeType.FullName == "Patcher.PatchAttribute"));
                }
                return(false);
            };

            // baseModule won't recognize MemberReferences from modModule without Import(), so recursively translate them.
            // Furthermore, we have to redirect any references to members in [Patch] classes.
            Func <TypeReference, TypeReference> mapType = null;

            mapType = (modType) => {
                if (modType.IsGenericParameter)
                {
                    return(modType);
                }
                if (modType.IsArray)
                {
                    var type = mapType(((ArrayType)modType).ElementType);
                    return(new ArrayType(type));
                }
                if (patchType(modType))
                {
                    modType = modType.Resolve().BaseType;
                }
                return(baseModule.Import(modType));
            };
            Action <MethodReference, MethodReference> mapParams = (modMethod, method) => {
                foreach (var param in modMethod.Parameters)
                {
                    method.Parameters.Add(new ParameterDefinition(mapType(param.ParameterType)));
                }
            };
            Func <MethodReference, MethodReference> mapMethod = (modMethod) => {
                var method = new MethodReference(modMethod.Name, mapType(modMethod.ReturnType), mapType(modMethod.DeclaringType));
                method.HasThis = modMethod.HasThis;
                mapParams(modMethod, method);
                return(method as MethodReference);
            };
            Func <MethodDefinition, string, MethodDefinition> cloneMethod = (modMethod, prefix) => {
                var method = new MethodDefinition(prefix + modMethod.Name, modMethod.Attributes, mapType(modMethod.ReturnType));
                mapParams(modMethod, method);
                foreach (var modParam in modMethod.GenericParameters)
                {
                    var param = new GenericParameter(modParam.Owner);
                    method.GenericParameters.Add(param);
                }
                return(method);
            };

            MyMInput.PatchModule(baseModule);
            MyMainMenu.PatchModule(baseModule);
            MyMenuButtons.PatchModule(baseModule);
            MyMenuInput.PatchModule(baseModule);
            MyPlayerInput.PatchModule(baseModule);
            MyReadyBanner.PatchModule(baseModule);
            MyRoundLogic.PatchModule(baseModule);
            MySession.PatchModule(baseModule);
            MyTFCommands.PatchModule(baseModule);
            MyTFGame.PatchModule(baseModule);
            MyTeamSelectOverlay.PatchModule(baseModule);
            MyVariant.PatchModule(baseModule);
            MyVersusAwards.PatchModule(baseModule);
            MyVersusRoundResults.PatchModule(baseModule);
            CleanMyVersusMatchResults.CleanModule(baseModule);
            CleanMyVersusPlayerMatchResults.CleanModule(baseModule);
            MyAwardInfo.PatchModule(baseModule);
            VersusPlayerMatchResultsAssembly.PatchModule(baseModule);
            CleanMyVariantPerPlayer.CleanModule(baseModule);

            foreach (TypeDefinition modType in modModule.Types.SelectMany(CecilExtensions.AllNestedTypes))
            {
                if (patchType(modType))
                {
                    var type = baseModule.AllNestedTypes().Single(t => t.FullName == modType.BaseType.FullName);

                    // copy over fields including their custom attributes
                    foreach (var field in modType.Fields)
                    {
                        if (field.DeclaringType == modType)
                        {
                            var newField = new FieldDefinition(field.Name, field.Attributes, mapType(field.FieldType));
                            foreach (var attribute in field.CustomAttributes)
                            {
                                newField.CustomAttributes.Add(new CustomAttribute(mapMethod(attribute.Constructor), attribute.GetBlob()));
                            }
                            type.Fields.Add(newField);
                        }
                    }

                    // copy over or replace methods
                    foreach (var method in modType.Methods)
                    {
                        if (method.DeclaringType == modType)
                        {
                            var original = type.Methods.SingleOrDefault(m => m.Signature() == method.Signature());
                            MethodDefinition savedMethod = null;
                            if (original == null)
                            {
                                type.Methods.Add(original = cloneMethod(method, ""));
                            }
                            else
                            {
                                savedMethod      = cloneMethod(method, "$original_");
                                savedMethod.Body = original.Body;
                                savedMethod.IsRuntimeSpecialName = false;
                                type.Methods.Add(savedMethod);
                            }
                            original.Body = method.Body;

                            // redirect any references in the body
                            var proc       = method.Body.GetILProcessor();
                            var amendments = new List <Action>();
                            foreach (var instr in method.Body.Instructions)
                            {
                                if (instr.Operand is MethodReference)
                                {
                                    var callee = (MethodReference)instr.Operand;
                                    if (callee.Name == "CallRealBase")
                                    {
                                        MethodReference baseMethod;
                                        try {
                                            baseMethod = type.BaseType.Resolve().Methods.Single(m => m.Name == method.Name) as MethodReference;
                                        }
                                        catch
                                        {
                                            baseMethod = ((TypeDefinition)(type.BaseType)).BaseType.Resolve().Methods.Single(m => m.Name == method.Name) as MethodReference;
                                        }
                                        amendments.Add(() => proc.InsertBefore(instr, proc.Create(OpCodes.Ldarg_0)));
                                        amendments.Add(() => proc.Replace(instr, proc.Create(OpCodes.Call, baseMethod)));
                                    }
                                    else
                                    {
                                        callee = mapMethod((MethodReference)instr.Operand);
                                        if (callee.FullName == original.FullName)
                                        {
                                            // replace base calls with ones to $original
                                            instr.Operand = savedMethod;
                                        }
                                        else
                                        {
                                            instr.Operand = callee;
                                        }
                                    }
                                }
                                else if (instr.Operand is FieldReference)
                                {
                                    var field = (FieldReference)instr.Operand;
                                    instr.Operand = new FieldReference(field.Name, mapType(field.FieldType), mapType(field.DeclaringType));
                                }
                                else if (instr.Operand is TypeReference)
                                {
                                    instr.Operand = mapType((TypeReference)instr.Operand);
                                }
                            }
                            foreach (var var in method.Body.Variables)
                            {
                                var.VariableType = mapType(var.VariableType);
                            }
                            foreach (var amendment in amendments)
                            {
                                amendment();
                            }
                            method.Body = proc.Body;
                        }
                    }
                }
            }
            CleanMyVersusMatchResults.PatchModule(baseModule);
            baseModule.Write("TowerFall8Player.exe");
        }