Пример #1
0
        /// <summary>
        /// Gets the inject flags for this specified scenario. This will be based on the supplied target method and other variables.
        /// </summary>
        /// <param name="targetMethodDefinition">The target type to inspect and to determine what injectflags are required.</param>
        /// <param name="overrideFlags">If this value is specified, use this instead of a generated InjectFlags list.</param>
        /// <returns><see cref="Mono.Cecil.Inject.InjectFlags"/></returns>
        public InjectFlags GetInjectFlags(MethodDefinition targetMethodDefinition, InjectFlags?overrideFlags)
        {
            InjectFlags flags = default;

            // If target method and target type is not static, append PassInvokingInstance to the flags.
            if (!targetMethodDefinition.IsStatic && !Type.IsSealed)
            {
                flags = InjectFlags.PassInvokingInstance;
            }
            // If the target method has parameters, append PassParametersRef to the flags.
            if (targetMethodDefinition.Parameters.Count > 0)
            {
                flags |= InjectFlags.PassParametersRef;
            }
            // If the target method return type is *not* void, append ModifyReturn to the flags.
            if (Method.ReturnType.FullName != TargetAssembly.MainModule.TypeSystem.Void.FullName)
            {
                flags |= InjectFlags.ModifyReturn;
            }

            // Override the flags if 'overrideFlags' was provided.
            if (overrideFlags != InjectFlags.None)
            {
                flags = (InjectFlags)overrideFlags;
            }
            return(flags);
        }
Пример #2
0
        private static InjectionDefinition GetInjectionDefinition(ReturnData data, HookData hook)
        {
            InjectFlags flags = 0;

            if (!data.methodDefinition.IsStatic)
            {
                flags |= InjectFlags.PassInvokingInstance;
            }

            if (data.methodDefinition.HasParameters)
            {
                if (hook.Method.Parameters.Any((x) => x.IsIn))
                {
                    flags |= InjectFlags.PassParametersRef;
                }
                else
                {
                    flags |= InjectFlags.PassParametersVal;
                }
            }

            if (hook.Method.ReturnType != hook.Assembly.MainModule.TypeSystem.Void)
            {
                flags |= InjectFlags.ModifyReturn;
            }

            return(new InjectionDefinition(data.methodDefinition, hook.Method, flags));
        }
Пример #3
0
 public InjectMethodAttribute(Type injectionType, string injectionMethod, MethodInjectionInfo.MethodInjectionLocation injectionLocation = MethodInjectionInfo.MethodInjectionLocation.Top, InjectFlags injectFlags = InjectFlags.None, string injectionAssembly = null)
 {
     InjectionLocation       = injectionType;
     InjectionMethod         = injectionMethod;
     MethodInjectionLocation = injectionLocation;
     InjectFlags             = injectFlags;
     InjectionAssembly       = injectionAssembly;
 }
 /// <summary>
 ///     Finds a method that could be used as an injection method (hook) for this method and constructs an instance of
 ///     <see cref="InjectionDefinition" /> from it.
 /// </summary>
 /// <param name="target">This method that is used as a target.</param>
 /// <param name="injectionType">Type that contains the injection method (hook).</param>
 /// <param name="name">Name of the injection method (hook).</param>
 /// <param name="flags">
 ///     Injection flags that specify what values to pass to the injection method and how to inject it. This
 ///     method attempts to find the hook method that satisfies all the specified flags.
 /// </param>
 /// <param name="localsID">
 ///     An array of indicies of local variables to pass to the injection method. Used only if
 ///     <see cref="InjectFlags.PassLocals" /> is specified, otherwise ignored.
 /// </param>
 /// <param name="typeFields">
 ///     An array of class fields from the type the target lies in to pass to the injection method.
 ///     Used only if <see cref="InjectFlags.PassFields" /> is specified, otherwise ignored.
 /// </param>
 /// <returns>
 ///     An instance of <see cref="InjectionDefinition" />, if a suitable injection method is found from the given
 ///     type. Otherwise, null.
 /// </returns>
 public static InjectionDefinition GetInjector(this MethodDefinition target,
                                               TypeDefinition injectionType,
                                               string name,
                                               InjectFlags flags = InjectFlags.None,
                                               int[] localsID    = null,
                                               params FieldDefinition[] typeFields)
 {
     return(injectionType.GetInjectionMethod(name, target, flags, localsID, typeFields));
 }
 /// <summary>
 ///     Finds a method that could be used as an injection method (hook) for this method and constructs an instance of
 ///     <see cref="InjectionDefinition" /> from it.
 /// </summary>
 /// <param name="target">This method that is used as a target.</param>
 /// <param name="injectionType">Type that contains the injection method (hook).</param>
 /// <param name="name">Name of the injection method (hook).</param>
 /// <param name="flags">
 ///     Injection flags that specify what values to pass to the injection method and how to inject it. This
 ///     method attempts to find the hook method that satisfies all the specified flags.
 /// </param>
 /// <param name="localsID">
 ///     An array of indicies of local variables to pass to the injection method. Used only if
 ///     <see cref="InjectFlags.PassLocals" /> is specified, otherwise ignored.
 /// </param>
 /// <param name="typeFields">
 ///     An array of class fields from the type the target lies in to pass to the injection method.
 ///     Used only if <see cref="InjectFlags.PassFields" /> is specified, otherwise ignored.
 /// </param>
 /// <returns>
 ///     An instance of <see cref="InjectionDefinition" />, if a suitable injection method is found from the given
 ///     type. Otherwise, null.
 /// </returns>
 public static InjectionDefinition GetInjector(this MethodDefinition target,
                                               TypeDefinition injectionType,
                                               string name,
                                               InjectFlags flags = InjectFlags.None,
                                               int[] localsID = null,
                                               params FieldDefinition[] typeFields)
 {
     return injectionType.GetInjectionMethod(name, target, flags, localsID, typeFields);
 }
Пример #6
0
 public PatchAttribute(string sig, int offset = 0, object tag = null, InjectFlags flags = 0, bool before = false, int[] localsID = null, string depSig = null)
 {
     MethodSig    = sig;
     Offset       = offset;
     Tag          = tag;
     Flags        = (int)flags;
     After        = before;
     LocalIds     = localsID;
     DependentSig = depSig;
 }
 public static void InjectWith(this MethodDefinition method,
                               MethodDefinition injectionMethod,
                               int codeOffset,
                               int tag,
                               InjectFlags flags,
                               InjectDirection dir,
                               int[] localsID,
                               FieldDefinition[] typeFields)
 {
     method.InjectWith(injectionMethod, codeOffset, (object)tag, flags, dir, localsID, typeFields);
 }
Пример #8
0
 /// <summary>
 ///     Convert the injection flags into an instance of InjectValues.
 /// </summary>
 /// <param name="flags">Flags to convert.</param>
 public InjectValues(InjectFlags flags)
 {
     ModifyReturn         = flags.IsSet(InjectFlags.ModifyReturn);
     PassTag              = flags.IsSet(InjectFlags.PassTag);
     PassInvokingInstance = flags.IsSet(InjectFlags.PassInvokingInstance);
     PassFields           = flags.IsSet(InjectFlags.PassFields);
     PassLocals           = flags.IsSet(InjectFlags.PassLocals);
     ParameterType        = flags.IsSet(InjectFlags.PassParametersVal)
                     ? PassParametersType.ByValue
                     : (flags.IsSet(InjectFlags.PassParametersRef)
                        ? PassParametersType.ByReference : PassParametersType.None);
 }
 /// <summary>
 ///     Inject a hook call into this method.
 /// </summary>
 /// <param name="method">This method that is used as a target.</param>
 /// <param name="injectionMethod">The method the call of which to inject.</param>
 /// <param name="codeOffset">
 ///     The index of the instruction from which to start injecting. If positive, will count from the
 ///     beginning of the method. If negative, will count from the end. For instance, -1 is the method's last instruction
 ///     and 0 is the first.
 /// </param>
 /// <param name="tag">
 ///     If <see cref="InjectFlags.PassTag" /> is specified, the value of this parameter will be passed as a
 ///     parameter to the injection method.
 /// </param>
 /// <param name="flags">Injection flags that specify what values to pass to the injection method and how to inject it.</param>
 /// <param name="dir">The direction in which to insert the call: either above the start code or below it.</param>
 /// <param name="localsID">
 ///     An array of indicies of local variables to pass to the injection method. Used only if
 ///     <see cref="InjectFlags.PassLocals" /> is specified, otherwise ignored.
 /// </param>
 /// <param name="typeFields">
 ///     An array of class fields from the type the target lies in to pass to the injection method.
 ///     Used only if <see cref="InjectFlags.PassFields" /> is specified, otherwise ignored.
 /// </param>
 public static void InjectWith(this MethodDefinition method,
                               MethodDefinition injectionMethod,
                               int codeOffset = 0,
                               int tag = 0,
                               InjectFlags flags = InjectFlags.None,
                               InjectDirection dir = InjectDirection.Before,
                               int[] localsID = null,
                               FieldDefinition[] typeFields = null)
 {
     InjectionDefinition id = new InjectionDefinition(method, injectionMethod, flags, localsID, typeFields);
     id.Inject(codeOffset, tag, dir);
 }
        /// <summary>
        ///     Inject a hook call into this method.
        /// </summary>
        /// <param name="method">This method that is used as a target.</param>
        /// <param name="injectionMethod">The method the call of which to inject.</param>
        /// <param name="codeOffset">
        ///     The index of the instruction from which to start injecting. If positive, will count from the
        ///     beginning of the method. If negative, will count from the end. For instance, -1 is the method's last instruction
        ///     and 0 is the first.
        /// </param>
        /// <param name="tag">
        ///     If <see cref="InjectFlags.PassTag" /> is specified, the value of this parameter will be passed as a
        ///     parameter to the injection method.
        /// </param>
        /// <param name="flags">Injection flags that specify what values to pass to the injection method and how to inject it.</param>
        /// <param name="dir">The direction in which to insert the call: either above the start code or below it.</param>
        /// <param name="localsID">
        ///     An array of indicies of local variables to pass to the injection method. Used only if
        ///     <see cref="InjectFlags.PassLocals" /> is specified, otherwise ignored.
        /// </param>
        /// <param name="typeFields">
        ///     An array of class fields from the type the target lies in to pass to the injection method.
        ///     Used only if <see cref="InjectFlags.PassFields" /> is specified, otherwise ignored.
        /// </param>
        public static void InjectWith(this MethodDefinition method,
                                      MethodDefinition injectionMethod,
                                      int codeOffset               = 0,
                                      object tag                   = null,
                                      InjectFlags flags            = InjectFlags.None,
                                      InjectDirection dir          = InjectDirection.Before,
                                      int[] localsID               = null,
                                      FieldDefinition[] typeFields = null)
        {
            InjectionDefinition id = new InjectionDefinition(method, injectionMethod, flags, localsID, typeFields);

            id.Inject(codeOffset, tag, dir);
        }
 /// <summary>
 ///     Attempts to construct an instance of <see cref="InjectionDefinition" /> by linking the injection method with the
 ///     injection target (the method to be injected).
 ///     The way how the method is injected is specified by the injection flags. If the injection method does not match the
 ///     criteria set by the injection flags, an exception will be thrown.
 /// </summary>
 /// <param name="injectTarget">The method that will be injected.</param>
 /// <param name="injectMethod">The method which to inject.</param>
 /// <param name="flags">Injection flags that specify what values to pass to the injection method and how to inject it.</param>
 /// <param name="localVarIDs">
 ///     An array of indicies of local variables to pass to the injection method. Used only if
 ///     <see cref="InjectFlags.PassLocals" /> is specified, otherwise ignored.
 /// </param>
 /// <param name="memberReferences">
 ///     An array of class fields from the type the target lies in to pass to the injection
 ///     method. Used only if <see cref="InjectFlags.PassFields" /> is specified, otherwise ignored.
 /// </param>
 public InjectionDefinition(MethodDefinition injectTarget,
                            MethodDefinition injectMethod,
                            InjectFlags flags,
                            int[] localVarIDs = null,
                            params FieldDefinition[] memberReferences)
 {
     ParameterCount =
         VerifyInjectionDefinition(injectMethod, injectTarget, flags, localVarIDs, memberReferences);
     InjectMethod     = injectMethod;
     InjectTarget     = injectTarget;
     Flags            = flags;
     MemberReferences = memberReferences;
     LocalVarIDs      = localVarIDs;
 }
Пример #12
0
 /// <summary>
 ///     Searches for a method that can be used to inject into the specified target.
 /// </summary>
 /// <param name="type">This type in which the possible injection method lies.</param>
 /// <param name="name">Name of the injection method.</param>
 /// <param name="target">The target method which to inject.</param>
 /// <param name="flags">
 ///     Injection flags that specify what values to pass to the injection method and how to inject it. This
 ///     method attempts to find the hook method that satisfies all the specified flags.
 /// </param>
 /// <param name="localVarIDs">
 ///     An array of indicies of local variables to pass to the injection method. Used only if
 ///     <see cref="InjectFlags.PassLocals" /> is specified, otherwise ignored.
 /// </param>
 /// <param name="memberReferences">
 ///     An array of class fields from the type the target lies in to pass to the injection
 ///     method. Used only if <see cref="InjectFlags.PassFields" /> is specified, otherwise ignored.
 /// </param>
 /// <returns>
 ///     An instance of <see cref="InjectionDefinition" />, if a suitable injection method with the given name has been
 ///     found. Otherwise, null.
 /// </returns>
 public static InjectionDefinition GetInjectionMethod(this TypeDefinition type,
                                                      string name,
                                                      MethodDefinition target,
                                                      InjectFlags flags,
                                                      int[] localVarIDs = null,
                                                      params FieldDefinition[] memberReferences)
 {
     return(InjectionDefinition.FindInjectionDefinition(
                type,
                name,
                target,
                flags,
                localVarIDs,
                memberReferences));
 }
Пример #13
0
 /// <summary>
 ///     Convert the injection flags into an instance of InjectValues.
 /// </summary>
 /// <param name="flags">Flags to convert.</param>
 public InjectValues(InjectFlags flags)
 {
     ModifyReturn = flags.IsSet(InjectFlags.ModifyReturn);
     PassTag = flags.IsSet(InjectFlags.PassTag);
     PassInvokingInstance = flags.IsSet(InjectFlags.PassInvokingInstance);
     PassFields = flags.IsSet(InjectFlags.PassFields);
     PassLocals = flags.IsSet(InjectFlags.PassLocals);
     ParameterType = flags.IsSet(InjectFlags.PassParametersVal)
                     ? PassParametersType.ByValue
                     : (flags.IsSet(InjectFlags.PassParametersRef)
                        ? PassParametersType.ByReference : PassParametersType.None);
 }
        public static void Patch(AssemblyDefinition assembly)
        {
            AssemblyDefinition hookAssembly =
                AssemblyLoader.LoadAssembly(
                    Path.Combine(
                        Path.GetDirectoryName(Assembly.GetAssembly(typeof(MaidFiddlerPatcher)).Location),
                        @"..\Plugins\Managed\CM3D2.MaidFiddler.Hook.dll"));

            TypeDefinition gameMainType    = assembly.MainModule.GetType("GameMain");
            TypeDefinition maidParam       = assembly.MainModule.GetType("MaidParam");
            TypeDefinition maidType        = assembly.MainModule.GetType("Maid");
            TypeDefinition scheduleAPI     = assembly.MainModule.GetType("Schedule.ScheduleAPI");
            TypeDefinition daytimeTaskCtrl = assembly.MainModule.GetType("DaytimeTaskCtrl");
            TypeDefinition nightTaskCtrl   = assembly.MainModule.GetType("NightTaskCtrl");
            TypeDefinition playerParam     = assembly.MainModule.GetType("PlayerParam");
            TypeDefinition yotogiPlayMgr   = assembly.MainModule.GetType("YotogiPlayManager");
            TypeDefinition wf        = assembly.MainModule.GetType("wf");
            TypeDefinition status    = assembly.MainModule.GetType("param.Status");
            TypeDefinition skillData =
                assembly.MainModule.GetType("Yotogi").NestedTypes.FirstOrDefault(t => t.Name == "SkillData");
            TypeDefinition freeModeItemEveryday = assembly.MainModule.GetType("FreeModeItemEveryday");
            TypeDefinition freeModeItemVip      = assembly.MainModule.GetType("FreeModeItemVip");

            TypeDefinition hookType    = hookAssembly.MainModule.GetType("CM3D2.MaidFiddler.Hook.FiddlerHooks");
            TypeDefinition maidHooks   = hookAssembly.MainModule.GetType("CM3D2.MaidFiddler.Hook.MaidStatusChangeHooks");
            TypeDefinition playerHooks =
                hookAssembly.MainModule.GetType("CM3D2.MaidFiddler.Hook.PlayerStatusChangeHooks");
            TypeDefinition valueLimitHooks = hookAssembly.MainModule.GetType("CM3D2.MaidFiddler.Hook.ValueLimitHooks");

            gameMainType.GetMethod("Deserialize")
            .GetInjector(hookType, "OnSaveDeserialize", InjectFlags.PassParametersVal)
            .Inject(-1);

            // Maid hooks
            MethodDefinition statusChangeHook         = maidHooks.GetMethod(nameof(MaidStatusChangeHooks.OnStatusChanged));
            MethodDefinition statusChangeCallbackHook =
                maidHooks.GetMethod(nameof(MaidStatusChangeHooks.OnStatusChangedCallback));
            MethodDefinition propertyGetHook     = maidHooks.GetMethod(nameof(MaidStatusChangeHooks.OnNewPropertyGet));
            MethodDefinition statusChangeIDHook1 = maidHooks.GetMethod(
                nameof(MaidStatusChangeHooks.OnStatusChangedID),
                "System.Int32",
                "Maid&",
                "System.Int32");
            MethodDefinition statusChangeIDHook2 = maidHooks.GetMethod(
                nameof(MaidStatusChangeHooks.OnStatusChangedID),
                "System.Int32",
                "Maid&",
                "System.Int32",
                "System.Int32");
            MethodDefinition propertyRemovedHook  = maidHooks.GetMethod(nameof(MaidStatusChangeHooks.OnPropertyRemoved));
            MethodDefinition statusUpdateHook     = maidHooks.GetMethod(nameof(MaidStatusChangeHooks.OnStatusUpdate));
            MethodDefinition maidYotogiUpdateHook =
                maidHooks.GetMethod(nameof(MaidStatusChangeHooks.OnMaidClassAndYotogiUpdate));
            MethodDefinition classUpdateHook      = maidHooks.GetMethod(nameof(MaidStatusChangeHooks.OnClassTypeUpdate));
            MethodDefinition thumbnailChangedHook = maidHooks.GetMethod(
                nameof(MaidStatusChangeHooks.OnThumbnailChanged));
            MethodDefinition noonWorkEnableCheckHook =
                maidHooks.GetMethod(nameof(MaidStatusChangeHooks.OnNoonWorkEnableCheck));
            MethodDefinition nightWorkEnableCheckHook =
                maidHooks.GetMethod(nameof(MaidStatusChangeHooks.OnNightWorkEnableCheck));
            MethodDefinition reloadNoonWorkDataHook =
                maidHooks.GetMethod(nameof(MaidStatusChangeHooks.ReloadNoonWorkData));
            MethodDefinition reloadNightWorkDataHook =
                maidHooks.GetMethod(nameof(MaidStatusChangeHooks.ReloadNightWorkData));
            MethodDefinition featurePropensityUpdatedHook =
                maidHooks.GetMethod(nameof(MaidStatusChangeHooks.OnFeaturePropensityUpdated));
            MethodDefinition nightWorkVisCheckHook =
                maidHooks.GetMethod(nameof(MaidStatusChangeHooks.CheckNightWorkVisibility));
            MethodDefinition yotogiSkillVisCheckHook =
                maidHooks.GetMethod(nameof(MaidStatusChangeHooks.OnYotogiSkillVisibilityCheck));
            MethodDefinition postProcessFreeModeSceneHook =
                maidHooks.GetMethod(nameof(MaidStatusChangeHooks.PostProcessFreeModeScene));

            MethodDefinition onValueRoundInt1 = valueLimitHooks.GetMethod(
                nameof(ValueLimitHooks.OnValueRound),
                typeof(int).MakeByRefType(),
                typeof(int));
            MethodDefinition onValueRoundLong1 = valueLimitHooks.GetMethod(
                nameof(ValueLimitHooks.OnValueRound),
                typeof(long).MakeByRefType(),
                typeof(long));
            MethodDefinition onValueRoundInt3 = valueLimitHooks.GetMethod(
                nameof(ValueLimitHooks.OnValueRound),
                typeof(int).MakeByRefType(),
                typeof(int),
                typeof(int),
                typeof(int));
            MethodDefinition onValueRoundLong3 = valueLimitHooks.GetMethod(
                nameof(ValueLimitHooks.OnValueRound),
                typeof(long).MakeByRefType(),
                typeof(long),
                typeof(long),
                typeof(long));


            // Player hooks
            MethodDefinition playerStatChangeHook =
                playerHooks.GetMethod(nameof(PlayerStatusChangeHooks.OnPlayerStatChanged));

            const InjectFlags features1 = InjectFlags.PassTag | InjectFlags.PassFields | InjectFlags.ModifyReturn;
            const InjectFlags features2 = features1 | InjectFlags.PassParametersVal;
            const InjectFlags features3 = InjectFlags.PassFields | InjectFlags.PassTag;


            string[] typeNames = Enum.GetNames(typeof(MaidChangeType));

            for (int i = (int)MaidChangeType.Care; i <= (int)MaidChangeType.TotalEvaluation; i++)
            {
                maidParam.GetMethod($"Add{typeNames[i]}")
                .InjectWith(statusChangeHook, 0, i, features1, typeFields: new[] { maidParam.GetField("maid_") });

                maidParam.GetMethod($"Set{typeNames[i]}")
                .InjectWith(statusChangeHook, 0, i, features1, typeFields: new[] { maidParam.GetField("maid_") });
            }

            maidParam.GetMethod("SetSexual")?
            .InjectWith(
                statusChangeHook,
                -1,
                (int)MaidChangeType.Sexual,
                features1,
                typeFields: new[] { maidParam.GetField("maid_") });

            for (int i = (int)MaidChangeType.FirstName; i <= (int)MaidChangeType.Seikeiken; i++)
            {
                maidParam.GetMethod($"Set{typeNames[i]}")?
                .InjectWith(statusChangeHook, 0, i, features1, typeFields: new[] { maidParam.GetField("maid_") });
            }

            for (int i = (int)MaidChangeType.MaidClassExp; i <= (int)MaidChangeType.YotogiClassExp; i++)
            {
                maidParam.GetMethod($"Add{typeNames[i]}", typeof(int))
                .InjectWith(statusChangeHook, 0, i, features1, typeFields: new[] { maidParam.GetField("maid_") });
            }

            for (int i = (int)MaidChangeType.SkillPlayCount; i <= (int)MaidChangeType.WorkPlayCount; i++)
            {
                maidParam.GetMethod($"Add{typeNames[i]}")
                .InjectWith(
                    statusChangeIDHook1,
                    0,
                    i,
                    features2,
                    typeFields: new[] { maidParam.GetField("maid_") });
            }

            maidParam.GetMethod("UpdateProfileComment")
            .InjectWith(
                statusChangeHook,
                0,
                (int)MaidChangeType.Profile,
                features1,
                typeFields: new[] { maidParam.GetField("maid_") });

            maidParam.GetMethod($"Add{typeNames[(int) MaidChangeType.SkillExp]}")
            .InjectWith(
                statusChangeIDHook2,
                0,
                (int)MaidChangeType.SkillExp,
                InjectFlags.PassFields | InjectFlags.PassTag | InjectFlags.PassParametersVal,
                typeFields: new[] { maidParam.GetField("maid_") });

            maidParam.GetMethod($"Set{typeNames[(int) MaidChangeType.WorkLevel]}")
            .InjectWith(
                statusChangeIDHook2,
                0,
                (int)MaidChangeType.WorkLevel,
                features2,
                typeFields: new[] { maidParam.GetField("maid_") });

            PatchFuncEnumBool(MaidChangeType.Propensity, maidParam.GetMethod("SetPropensity"), statusUpdateHook);

            PatchFuncEnumBool(MaidChangeType.Feature, maidParam.GetMethod("SetFeature"), statusUpdateHook);

            for (int i = (int)MaidChangeType.NewGetSkill; i <= (int)MaidChangeType.NewGetWork; i++)
            {
                maidParam.GetMethod($"Set{typeNames[i]}")
                .InjectWith(
                    propertyGetHook,
                    0,
                    i,
                    features3 | InjectFlags.PassParametersVal,
                    typeFields: new[] { maidParam.GetField("maid_") });
            }

            for (int i = (int)MaidChangeType.Skill; i <= (int)MaidChangeType.Work; i++)
            {
                maidParam.GetMethod($"Remove{typeNames[i]}")
                .InjectWith(
                    propertyRemovedHook,
                    0,
                    i,
                    features3 | InjectFlags.PassParametersVal,
                    typeFields: new[] { maidParam.GetField("maid_") });
            }

            maidParam.GetMethod("UpdatetAcquisitionMaidClassType")
            .InjectWith(
                classUpdateHook,
                0,
                (int)MaidChangeType.MaidClassType,
                features3,
                typeFields: new[] { maidParam.GetField("maid_") });

            maidParam.GetMethod("UpdatetAcquisitionYotogiClassType")
            .InjectWith(
                classUpdateHook,
                0,
                (int)MaidChangeType.YotogiClassType,
                features3,
                typeFields: new[] { maidParam.GetField("maid_") });

            maidParam.GetMethod("UpdateMaidClassAndYotogiClassStatus")
            .InjectWith(
                maidYotogiUpdateHook,
                0,
                0,
                InjectFlags.PassFields,
                typeFields: new[] { maidParam.GetField("maid_") });

            PatchFuncEnum(
                MaidChangeType.MaidClassType,
                maidParam.GetMethods("AddMaidClassExp").FirstOrDefault(m => m.Parameters.Count == 2),
                statusChangeIDHook1);

            PatchFuncEnum(
                MaidChangeType.YotogiClassType,
                maidParam.GetMethods("AddYotogiClassExp").FirstOrDefault(m => m.Parameters.Count == 2),
                statusChangeIDHook1);

            maidType.GetMethod("ThumShot").InjectWith(thumbnailChangedHook, -1, 0, InjectFlags.PassInvokingInstance);
            maidType.GetMethod("SetThumIcon")?.InjectWith(thumbnailChangedHook, -1, 0, InjectFlags.PassInvokingInstance);

            scheduleAPI.GetMethod("EnableNoonWork")
            .InjectWith(
                noonWorkEnableCheckHook,
                0,
                0,
                InjectFlags.ModifyReturn | InjectFlags.PassParametersVal);

            scheduleAPI.GetMethod("EnableNightWork")
            .InjectWith(
                nightWorkEnableCheckHook,
                0,
                0,
                InjectFlags.ModifyReturn | InjectFlags.PassParametersVal);

            daytimeTaskCtrl.GetMethod("LoadData")
            .InjectWith(
                reloadNoonWorkDataHook,
                5,
                flags: InjectFlags.PassFields | InjectFlags.PassParametersVal,
                typeFields: new[] { daytimeTaskCtrl.GetField("m_scheduleApi") });

            nightTaskCtrl.GetMethod("LoadData")
            .InjectWith(
                reloadNightWorkDataHook,
                5,
                flags: InjectFlags.PassFields | InjectFlags.PassParametersVal,
                typeFields: new[] { nightTaskCtrl.GetField("m_scheduleApi") });

            scheduleAPI.GetMethod("VisibleNightWork")
            .InjectWith(
                nightWorkVisCheckHook,
                0,
                0,
                InjectFlags.ModifyReturn | InjectFlags.PassParametersVal);

            maidParam.GetMethod("UpdateFeatureAndPropensity")
            .InjectWith(
                featurePropensityUpdatedHook,
                -1,
                0,
                InjectFlags.PassFields | InjectFlags.PassParametersVal,
                typeFields: new[] { maidParam.GetField("maid_") });

            maidParam.GetMethod("SetFeature", "System.Collections.Generic.HashSet`1<param.Feature>")
            .InjectWith(
                statusChangeCallbackHook,
                -1,
                (int)MaidChangeType.FeatureHash,
                InjectFlags.PassTag | InjectFlags.PassFields,
                typeFields: new[] { maidParam.GetField("maid_") });

            maidParam.GetMethod("SetPropensity", "System.Collections.Generic.HashSet`1<param.Propensity>")
            .InjectWith(
                statusChangeCallbackHook,
                -1,
                (int)MaidChangeType.PropensityHash,
                InjectFlags.PassTag | InjectFlags.PassFields,
                typeFields: new[] { maidParam.GetField("maid_") });

            for (PlayerChangeType e = PlayerChangeType.Days; e <= PlayerChangeType.ShopUseMoney; e++)
            {
                string addMethod = $"Add{Enum.GetName(typeof (PlayerChangeType), e)}";
                string setMethod = $"Set{Enum.GetName(typeof (PlayerChangeType), e)}";
                playerParam.GetMethod(addMethod)
                .InjectWith(playerStatChangeHook, 0, (int)e, InjectFlags.PassTag | InjectFlags.ModifyReturn);

                playerParam.GetMethod(setMethod)
                .InjectWith(playerStatChangeHook, 0, (int)e, InjectFlags.PassTag | InjectFlags.ModifyReturn);
            }

            for (PlayerChangeType e = PlayerChangeType.BestSalonGrade; e <= PlayerChangeType.Name; e++)
            {
                string setMethod = $"Set{Enum.GetName(typeof (PlayerChangeType), e)}";

                playerParam.GetMethod(setMethod)
                .InjectWith(playerStatChangeHook, 0, (int)e, InjectFlags.PassTag | InjectFlags.ModifyReturn);
            }

            yotogiPlayMgr.GetMethod("UpdateCommand")
            .InjectWith(
                maidHooks.GetMethod(nameof(MaidStatusChangeHooks.OnUpdateCommand)),
                -1,
                0,
                InjectFlags.PassFields,
                typeFields:
                new[]
            {
                yotogiPlayMgr.GetField("player_state_"),
                yotogiPlayMgr.GetField("valid_command_dic_"),
                yotogiPlayMgr.GetField("command_factory_")
            });

            skillData.GetMethod("IsExecMaid")
            .InjectWith(yotogiSkillVisCheckHook, 0, 0, InjectFlags.PassTag | InjectFlags.ModifyReturn);

            skillData.GetMethod("IsExecStage")
            .InjectWith(yotogiSkillVisCheckHook, 0, 1, InjectFlags.PassTag | InjectFlags.ModifyReturn);

            wf.GetMethod("NumRound2")
            .InjectWith(onValueRoundInt1, 0, 0, InjectFlags.ModifyReturn | InjectFlags.PassParametersVal);

            wf.GetMethod("NumRound3")
            .InjectWith(onValueRoundInt1, 0, 0, InjectFlags.ModifyReturn | InjectFlags.PassParametersVal);

            wf.GetMethod("NumRound4", typeof(int))
            .InjectWith(onValueRoundInt1, 0, 0, InjectFlags.ModifyReturn | InjectFlags.PassParametersVal);

            wf.GetMethod("NumRound4", typeof(long))
            .InjectWith(onValueRoundLong1, 0, 0, InjectFlags.ModifyReturn | InjectFlags.PassParametersVal);

            wf.GetMethod("NumRound6")
            .InjectWith(onValueRoundLong1, 0, 0, InjectFlags.ModifyReturn | InjectFlags.PassParametersVal);

            wf.GetMethod("RoundMinMax", typeof(int), typeof(int), typeof(int))
            .InjectWith(onValueRoundInt3, 0, 0, InjectFlags.ModifyReturn | InjectFlags.PassParametersVal);

            wf.GetMethod("RoundMinMax", typeof(long), typeof(long), typeof(long))
            .InjectWith(onValueRoundLong3, 0, 0, InjectFlags.ModifyReturn | InjectFlags.PassParametersVal);

            freeModeItemEveryday?.GetMethod(".ctor")
            .InjectWith(
                postProcessFreeModeSceneHook,
                -1,
                0,
                InjectFlags.PassFields,
                typeFields: new[] { freeModeItemEveryday.GetField("is_enabled_") });

            freeModeItemVip?.GetMethod(".ctor")
            .InjectWith(
                postProcessFreeModeSceneHook,
                -1,
                0,
                InjectFlags.PassFields,
                typeFields: new[] { freeModeItemVip.GetField("is_enabled_") });

            maidParam.ChangeAccess("status_");

            playerParam.ChangeAccess("status_");

            status.ChangeAccess("kInitMaidPoint");

            SetCustomPatchedAttribute(assembly);
        }
Пример #15
0
        /// <summary>
        /// Injects like <see cref="Mono.Cecil.Inject.InjectionDefinition"/>, with less safety measures
        /// and injects a value from a local compiler class' field
        /// </summary>
        /// <returns>The modified self parameter.</returns>
        /// <param name="self">Self.</param>
        /// <param name="injectionMethod">The method to inject.</param>
        /// <param name="callLoc">The instruction position to inject at.</param>
        /// <param name="localClass">The Local class value whose field is to be injecting from.</param>
        /// <param name="fieldInClass">The field reference in local class.</param>
        /// <param name="f">The injection flags.</param>
        /// <param name="localsID">The other local value ids.</param>
        /// <param name="typeFields">The type fields.</param>
        public static MethodDefinition InjectWithLocalFieldParameter(
            this MethodDefinition self,
            MethodDefinition injectionMethod,
            int callLoc,
            int localClass,
            FieldDefinition fieldInClass,
            bool fieldByRef              = false,
            InjectDirection direction    = InjectDirection.Before,
            InjectFlags f                = InjectFlags.None,
            int[] localsID               = null,
            FieldDefinition[] typeFields = null,
            int token = 0
            )
        {
            var flags = f.ToValues();

            if (callLoc == -1)
            {
                throw new ArgumentOutOfRangeException(nameof(callLoc));
            }
            if (flags.PassLocals && localsID == null)
            {
                throw new ArgumentNullException(nameof(localsID));
            }
            if (flags.PassFields && typeFields == null)
            {
                throw new ArgumentNullException(nameof(typeFields));
            }

            var body   = self.Body;
            var il     = body.GetILProcessor();
            var isVoid = self.ReturnType.FullName == "System.Void";
            var inst   = body.Instructions[callLoc];
            var inst2  = inst;

            if (direction == InjectDirection.Before && callLoc != 0)
            {
                Instruction oldIns = ILUtils.CopyInstruction(inst);
                ILUtils.ReplaceInstruction(inst, il.Create(OpCodes.Nop));
                Instruction ins = body.Instructions[callLoc];
                il.InsertAfter(ins, oldIns);
                inst2 = body.Instructions[callLoc + 1];
            }
            else if (direction == InjectDirection.After)
            {
                il.InsertAfter(inst, il.Create(OpCodes.Nop));
                inst2 = body.Instructions[callLoc + 1];
            }

            VariableDefinition returnDef = null;

            if (flags.ModifyReturn && !isVoid)
            {
                body.InitLocals = true;
                returnDef       = new VariableDefinition(self.ReturnType);
                body.Variables.Add(returnDef);
            }
            if (flags.PassTag)
            {
                il.InsertBefore(inst, il.Create(OpCodes.Ldc_I4, token));
            }
            if (flags.PassInvokingInstance)
            {
                il.InsertBefore(inst, il.Create(OpCodes.Ldarg_0));
            }
            if (flags.ModifyReturn && !isVoid)
            {
                il.InsertBefore(inst, il.Create(OpCodes.Ldloca_S, returnDef));
            }
            if (flags.PassLocals)
            {
                foreach (int i in localsID)
                {
                    il.InsertBefore(inst, il.Create(OpCodes.Ldloca_S, (byte)i));
                }
            }
            if (flags.PassFields)
            {
                var memberRefs = typeFields.Select(t => t.Module.ImportReference(t));
                foreach (FieldReference t in memberRefs)
                {
                    il.InsertBefore(inst, il.Create(OpCodes.Ldarg_0));
                    il.InsertBefore(inst, il.Create(OpCodes.Ldflda, t));
                }
            }
            if (flags.PassParameters)
            {
                int prefixCount = Convert.ToInt32(flags.PassTag) +
                                  Convert.ToInt32(flags.PassInvokingInstance) +
                                  Convert.ToInt32(flags.ModifyReturn && !isVoid);
                int localsCount    = flags.PassLocals ? localsID.Length : 0;
                int memberRefCount = flags.PassFields ? typeFields.Length : 0;
                int paramCount     = flags.PassParameters ? self.Parameters.Count : 0;
                int parameters     = injectionMethod.Parameters.Count - prefixCount - localsCount - memberRefCount - 1;
                int icr            = Convert.ToInt32(!self.IsStatic);
                for (int i = 0; i < parameters; i++)
                {
                    il.InsertBefore(
                        inst,
                        il.Create(flags.PassParametersByRef ? OpCodes.Ldarga_S : OpCodes.Ldarg_S, (byte)(i + icr))
                        );
                }
            }
            il.InsertBefore(inst, il.Create(OpCodes.Ldloc_S, (byte)localClass));
            il.InsertBefore(inst, il.Create(fieldByRef ? OpCodes.Ldflda : OpCodes.Ldfld, fieldInClass));
            il.InsertBefore(inst, il.Create(OpCodes.Call, self.Module.ImportReference(injectionMethod)));
            if (flags.ModifyReturn)
            {
                il.InsertBefore(inst, il.Create(OpCodes.Brfalse_S, inst));
                if (!isVoid)
                {
                    il.InsertBefore(inst, il.Create(OpCodes.Ldloc_S, returnDef));
                }
                il.InsertBefore(inst, il.Create(OpCodes.Ret));
            }
            // If we don't use the return value of InjectMethod, pop it from the ES
            else if (injectionMethod.ReturnType.FullName != "System.Void")
            {
                il.InsertBefore(inst, il.Create(OpCodes.Pop));
            }
            if (direction == InjectDirection.After)
            {
                il.Remove(inst2);
            }
            return(self);
        }
Пример #16
0
        public override void Patch(PatcherArguments args)
        {
            TypeDefinition gameMainType    = args.Assembly.MainModule.GetType("GameMain");
            TypeDefinition maidParam       = args.Assembly.MainModule.GetType("MaidParam");
            TypeDefinition maidType        = args.Assembly.MainModule.GetType("Maid");
            TypeDefinition scheduleAPI     = args.Assembly.MainModule.GetType("Schedule.ScheduleAPI");
            TypeDefinition daytimeTaskCtrl = args.Assembly.MainModule.GetType("DaytimeTaskCtrl");
            TypeDefinition nightTaskCtrl   = args.Assembly.MainModule.GetType("NightTaskCtrl");
            TypeDefinition playerParam     = args.Assembly.MainModule.GetType("PlayerParam");
            TypeDefinition yotogiPlayMgr   = args.Assembly.MainModule.GetType("YotogiPlayManager");
            TypeDefinition wf        = args.Assembly.MainModule.GetType("wf");
            TypeDefinition status    = args.Assembly.MainModule.GetType("param.Status");
            TypeDefinition skillData =
                args.Assembly.MainModule.GetType("Yotogi").NestedTypes.FirstOrDefault(t => t.Name == "SkillData");
            TypeDefinition freeModeItemEveryday = args.Assembly.MainModule.GetType("FreeModeItemEveryday");
            TypeDefinition freeModeItemVip      = args.Assembly.MainModule.GetType("FreeModeItemVip");

            TypeDefinition hookType  = FiddlerAssembly.MainModule.GetType("CM3D2.MaidFiddler.Hook.FiddlerHooks");
            TypeDefinition maidHooks = FiddlerAssembly.MainModule.GetType(
                "CM3D2.MaidFiddler.Hook.MaidStatusChangeHooks");
            TypeDefinition playerHooks =
                FiddlerAssembly.MainModule.GetType("CM3D2.MaidFiddler.Hook.PlayerStatusChangeHooks");
            TypeDefinition valueLimitHooks = FiddlerAssembly.MainModule.GetType(
                "CM3D2.MaidFiddler.Hook.ValueLimitHooks");

            gameMainType.GetMethod("Deserialize")
            .GetInjector(hookType, "OnSaveDeserialize", InjectFlags.PassParametersVal)
            .Inject(-1);

            // Maid hooks
            MethodDefinition statusChangeHook         = maidHooks.GetMethod(nameof(MaidStatusChangeHooks.OnStatusChanged));
            MethodDefinition statusChangeCallbackHook =
                maidHooks.GetMethod(nameof(MaidStatusChangeHooks.OnStatusChangedCallback));
            MethodDefinition propertyGetHook     = maidHooks.GetMethod(nameof(MaidStatusChangeHooks.OnNewPropertyGet));
            MethodDefinition statusChangeIDHook1 = maidHooks.GetMethod(
                nameof(MaidStatusChangeHooks.OnStatusChangedID),
                typeof(int),
                typeof(Maid).MakeByRefType(),
                typeof(int));
            MethodDefinition statusChangeIDHook2 = maidHooks.GetMethod(
                nameof(MaidStatusChangeHooks.OnStatusChangedID),
                typeof(int),
                typeof(Maid).MakeByRefType(),
                typeof(int),
                typeof(int));
            MethodDefinition propertyRemovedHook  = maidHooks.GetMethod(nameof(MaidStatusChangeHooks.OnPropertyRemoved));
            MethodDefinition statusUpdateHook     = maidHooks.GetMethod(nameof(MaidStatusChangeHooks.OnStatusUpdate));
            MethodDefinition maidYotogiUpdateHook =
                maidHooks.GetMethod(nameof(MaidStatusChangeHooks.OnMaidClassAndYotogiUpdate));
            MethodDefinition classUpdateHook      = maidHooks.GetMethod(nameof(MaidStatusChangeHooks.OnClassTypeUpdate));
            MethodDefinition thumbnailChangedHook = maidHooks.GetMethod(
                nameof(MaidStatusChangeHooks.OnThumbnailChanged));
            MethodDefinition noonWorkEnableCheckHook =
                maidHooks.GetMethod(nameof(MaidStatusChangeHooks.OnNoonWorkEnableCheck));
            MethodDefinition nightWorkEnableCheckHook =
                maidHooks.GetMethod(nameof(MaidStatusChangeHooks.OnNightWorkEnableCheck));
            MethodDefinition reloadNoonWorkDataHook =
                maidHooks.GetMethod(nameof(MaidStatusChangeHooks.ReloadNoonWorkData));
            MethodDefinition reloadNightWorkDataHook =
                maidHooks.GetMethod(nameof(MaidStatusChangeHooks.ReloadNightWorkData));
            MethodDefinition featurePropensityUpdatedHook =
                maidHooks.GetMethod(nameof(MaidStatusChangeHooks.OnFeaturePropensityUpdated));
            MethodDefinition nightWorkVisCheckHook =
                maidHooks.GetMethod(nameof(MaidStatusChangeHooks.CheckNightWorkVisibility));
            MethodDefinition yotogiSkillVisCheckHook =
                maidHooks.GetMethod(nameof(MaidStatusChangeHooks.OnYotogiSkillVisibilityCheck));
            MethodDefinition postProcessFreeModeSceneHook =
                maidHooks.GetMethod(nameof(MaidStatusChangeHooks.PostProcessFreeModeScene));

            MethodDefinition onValueRoundInt1 = valueLimitHooks.GetMethod(
                nameof(ValueLimitHooks.OnValueRound),
                typeof(int).MakeByRefType(),
                typeof(int));
            MethodDefinition onValueRoundLong1 = valueLimitHooks.GetMethod(
                nameof(ValueLimitHooks.OnValueRound),
                typeof(long).MakeByRefType(),
                typeof(long));
            MethodDefinition onValueRoundInt3 = valueLimitHooks.GetMethod(
                nameof(ValueLimitHooks.OnValueRound),
                typeof(int).MakeByRefType(),
                typeof(int),
                typeof(int),
                typeof(int));
            MethodDefinition onValueRoundLong3 = valueLimitHooks.GetMethod(
                nameof(ValueLimitHooks.OnValueRound),
                typeof(long).MakeByRefType(),
                typeof(long),
                typeof(long),
                typeof(long));


            // Player hooks
            MethodDefinition playerStatChangeHook =
                playerHooks.GetMethod(nameof(PlayerStatusChangeHooks.OnPlayerStatChanged));

            const InjectFlags features1 = InjectFlags.PassTag | InjectFlags.PassFields | InjectFlags.ModifyReturn;
            const InjectFlags features2 = features1 | InjectFlags.PassParametersVal;
            const InjectFlags features3 = InjectFlags.PassFields | InjectFlags.PassTag;


            string[] typeNames = Enum.GetNames(typeof(MaidChangeType));

            Console.WriteLine("Patching basic Add/Set methods:");

            for (int i = (int)MaidChangeType.Care; i <= (int)MaidChangeType.TotalEvaluation; i++)
            {
                WritePreviousLine($"Add{typeNames[i]}");
                maidParam.GetMethod($"Add{typeNames[i]}")
                .InjectWith(statusChangeHook, 0, i, features1, typeFields: new[] { maidParam.GetField("maid_") });

                WritePreviousLine($"Set{typeNames[i]}");
                maidParam.GetMethod($"Set{typeNames[i]}")
                .InjectWith(statusChangeHook, 0, i, features1, typeFields: new[] { maidParam.GetField("maid_") });
            }

            MethodDefinition setSexual = maidParam.GetMethod("SetSexual");

            if (setSexual != null)
            {
                WritePreviousLine("SetSexual");
                maidParam.GetMethod("SetSexual")
                .InjectWith(
                    statusChangeHook,
                    -1,
                    (int)MaidChangeType.Sexual,
                    features1,
                    typeFields: new[] { maidParam.GetField("maid_") });
            }


            for (int i = (int)MaidChangeType.FirstName; i <= (int)MaidChangeType.Seikeiken; i++)
            {
                WritePreviousLine($"Set{typeNames[i]}");
                MethodDefinition setDefinition = maidParam.GetMethod($"Set{typeNames[i]}");
                if (setDefinition == null)
                {
                    Console.WriteLine("Method not found (probably older version of the game). Skipping...");
                    continue;
                }
                setDefinition.InjectWith(
                    statusChangeHook,
                    0,
                    i,
                    features1,
                    typeFields: new[] { maidParam.GetField("maid_") });
            }

            for (int i = (int)MaidChangeType.MaidClassExp; i <= (int)MaidChangeType.YotogiClassExp; i++)
            {
                WritePreviousLine($"Add{typeNames[i]}");
                maidParam.GetMethod($"Add{typeNames[i]}", typeof(int))
                .InjectWith(statusChangeHook, 0, i, features1, typeFields: new[] { maidParam.GetField("maid_") });
            }

            for (int i = (int)MaidChangeType.SkillPlayCount; i <= (int)MaidChangeType.WorkPlayCount; i++)
            {
                WritePreviousLine($"Add{typeNames[i]}");
                maidParam.GetMethod($"Add{typeNames[i]}")
                .InjectWith(
                    statusChangeIDHook1,
                    0,
                    i,
                    features2,
                    typeFields: new[] { maidParam.GetField("maid_") });
            }

            WritePreviousLine("UpdateProfileComment");
            maidParam.GetMethod("UpdateProfileComment")
            .InjectWith(
                statusChangeHook,
                0,
                (int)MaidChangeType.Profile,
                features1,
                typeFields: new[] { maidParam.GetField("maid_") });

            WritePreviousLine($"Add{typeNames[(int) MaidChangeType.SkillExp]}");
            maidParam.GetMethod($"Add{typeNames[(int) MaidChangeType.SkillExp]}")
            .InjectWith(
                statusChangeIDHook2,
                0,
                (int)MaidChangeType.SkillExp,
                InjectFlags.PassFields | InjectFlags.PassTag | InjectFlags.PassParametersVal,
                typeFields: new[] { maidParam.GetField("maid_") });

            WritePreviousLine($"Set{typeNames[(int) MaidChangeType.WorkLevel]}");
            maidParam.GetMethod($"Set{typeNames[(int) MaidChangeType.WorkLevel]}")
            .InjectWith(
                statusChangeIDHook2,
                0,
                (int)MaidChangeType.WorkLevel,
                features2,
                typeFields: new[] { maidParam.GetField("maid_") });

            WritePreviousLine("SetPropensity");
            PatchFuncEnumBool(MaidChangeType.Propensity, maidParam.GetMethod("SetPropensity"), statusUpdateHook);

            WritePreviousLine("SetFeature");
            PatchFuncEnumBool(MaidChangeType.Feature, maidParam.GetMethod("SetFeature"), statusUpdateHook);

            for (int i = (int)MaidChangeType.NewGetSkill; i <= (int)MaidChangeType.NewGetWork; i++)
            {
                WritePreviousLine($"Set{typeNames[i]}");
                maidParam.GetMethod($"Set{typeNames[i]}")
                .InjectWith(
                    propertyGetHook,
                    0,
                    i,
                    features3 | InjectFlags.PassParametersVal,
                    typeFields: new[] { maidParam.GetField("maid_") });
            }

            for (int i = (int)MaidChangeType.Skill; i <= (int)MaidChangeType.Work; i++)
            {
                WritePreviousLine($"Remove{typeNames[i]}");
                maidParam.GetMethod($"Remove{typeNames[i]}")
                .InjectWith(
                    propertyRemovedHook,
                    0,
                    i,
                    features3 | InjectFlags.PassParametersVal,
                    typeFields: new[] { maidParam.GetField("maid_") });
            }

            WritePreviousLine("UpdatetAcquisitionMaidClassType");
            maidParam.GetMethod("UpdatetAcquisitionMaidClassType")
            .InjectWith(
                classUpdateHook,
                0,
                (int)MaidChangeType.MaidClassType,
                features3,
                typeFields: new[] { maidParam.GetField("maid_") });

            WritePreviousLine("UpdatetAcquisitionYotogiClassType");
            maidParam.GetMethod("UpdatetAcquisitionYotogiClassType")
            .InjectWith(
                classUpdateHook,
                0,
                (int)MaidChangeType.YotogiClassType,
                features3,
                typeFields: new[] { maidParam.GetField("maid_") });

            WritePreviousLine("UpdateMaidClassAndYotogiClassStatus");
            maidParam.GetMethod("UpdateMaidClassAndYotogiClassStatus")
            .InjectWith(
                maidYotogiUpdateHook,
                0,
                0,
                InjectFlags.PassFields,
                typeFields: new[] { maidParam.GetField("maid_") });

            WritePreviousLine("AddMaidClassExp");
            PatchFuncEnum(
                MaidChangeType.MaidClassType,
                maidParam.GetMethods("AddMaidClassExp").FirstOrDefault(m => m.Parameters.Count == 2),
                statusChangeIDHook1);

            WritePreviousLine("AddYotogiClassExp");
            PatchFuncEnum(
                MaidChangeType.YotogiClassType,
                maidParam.GetMethods("AddYotogiClassExp").FirstOrDefault(m => m.Parameters.Count == 2),
                statusChangeIDHook1);

            WritePreviousLine("ThumShot");
            maidType.GetMethod("ThumShot").InjectWith(thumbnailChangedHook, -1, 0, InjectFlags.PassInvokingInstance);

            MethodDefinition setThumIcon = maidType.GetMethod("SetThumIcon");

            if (setThumIcon != null)
            {
                WritePreviousLine("SetThumIcon");
                setThumIcon.InjectWith(thumbnailChangedHook, -1, 0, InjectFlags.PassInvokingInstance);
            }
            WritePreviousLine("EnableNoonWork");
            scheduleAPI.GetMethod("EnableNoonWork")
            .InjectWith(
                noonWorkEnableCheckHook,
                0,
                0,
                InjectFlags.ModifyReturn | InjectFlags.PassParametersVal);

            WritePreviousLine("EnableNightWork");
            scheduleAPI.GetMethod("EnableNightWork")
            .InjectWith(
                nightWorkEnableCheckHook,
                0,
                0,
                InjectFlags.ModifyReturn | InjectFlags.PassParametersVal);

            WritePreviousLine("DaytimeTaskCtrl.LoadData");
            daytimeTaskCtrl.GetMethod("LoadData")
            .InjectWith(
                reloadNoonWorkDataHook,
                5,
                flags: InjectFlags.PassFields | InjectFlags.PassParametersVal,
                typeFields: new[] { daytimeTaskCtrl.GetField("m_scheduleApi") });

            WritePreviousLine("NightTaskCtrl.LoadData");
            nightTaskCtrl.GetMethod("LoadData")
            .InjectWith(
                reloadNightWorkDataHook,
                5,
                flags: InjectFlags.PassFields | InjectFlags.PassParametersVal,
                typeFields: new[] { nightTaskCtrl.GetField("m_scheduleApi") });

            WritePreviousLine("VisibleNightWork");
            scheduleAPI.GetMethod("VisibleNightWork")
            .InjectWith(
                nightWorkVisCheckHook,
                0,
                0,
                InjectFlags.ModifyReturn | InjectFlags.PassParametersVal);


            WritePreviousLine("UpdateFeatureAndPropensity");
            maidParam.GetMethod("UpdateFeatureAndPropensity")
            .InjectWith(
                featurePropensityUpdatedHook,
                -1,
                0,
                InjectFlags.PassFields | InjectFlags.PassParametersVal,
                typeFields: new[] { maidParam.GetField("maid_") });

            WritePreviousLine("SetFeature(HashSet)");
            maidParam.GetMethod("SetFeature", typeof(HashSet <>).MakeGenericType(typeof(Feature)))
            .InjectWith(
                statusChangeCallbackHook,
                -1,
                (int)MaidChangeType.FeatureHash,
                InjectFlags.PassTag | InjectFlags.PassFields,
                typeFields: new[] { maidParam.GetField("maid_") });

            WritePreviousLine("SetPropensity(HashSet)");
            maidParam.GetMethod("SetPropensity", typeof(HashSet <>).MakeGenericType(typeof(Propensity)))
            .InjectWith(
                statusChangeCallbackHook,
                -1,
                (int)MaidChangeType.PropensityHash,
                InjectFlags.PassTag | InjectFlags.PassFields,
                typeFields: new[] { maidParam.GetField("maid_") });

            for (PlayerChangeType e = PlayerChangeType.Days; e <= PlayerChangeType.ShopUseMoney; e++)
            {
                string addMethod = $"Add{Enum.GetName(typeof (PlayerChangeType), e)}";
                string setMethod = $"Set{Enum.GetName(typeof (PlayerChangeType), e)}";
                WritePreviousLine(addMethod);
                playerParam.GetMethod(addMethod)
                .InjectWith(playerStatChangeHook, 0, (int)e, InjectFlags.PassTag | InjectFlags.ModifyReturn);

                WritePreviousLine(setMethod);
                playerParam.GetMethod(setMethod)
                .InjectWith(playerStatChangeHook, 0, (int)e, InjectFlags.PassTag | InjectFlags.ModifyReturn);
            }

            for (PlayerChangeType e = PlayerChangeType.BestSalonGrade; e <= PlayerChangeType.Name; e++)
            {
                string setMethod = $"Set{Enum.GetName(typeof (PlayerChangeType), e)}";

                WritePreviousLine(setMethod);
                playerParam.GetMethod(setMethod)
                .InjectWith(playerStatChangeHook, 0, (int)e, InjectFlags.PassTag | InjectFlags.ModifyReturn);
            }

            WritePreviousLine("UpdateCommand");
            yotogiPlayMgr.GetMethod("UpdateCommand")
            .InjectWith(
                maidHooks.GetMethod(nameof(MaidStatusChangeHooks.OnUpdateCommand)),
                -1,
                0,
                InjectFlags.PassFields,
                typeFields:
                new[]
            {
                yotogiPlayMgr.GetField("player_state_"),
                yotogiPlayMgr.GetField("valid_command_dic_"),
                yotogiPlayMgr.GetField("command_factory_")
            });

            WritePreviousLine("IsExecMaid");
            skillData.GetMethod("IsExecMaid")
            .InjectWith(yotogiSkillVisCheckHook, 0, 0, InjectFlags.PassTag | InjectFlags.ModifyReturn);

            WritePreviousLine("IsExecStage");
            skillData.GetMethod("IsExecStage")
            .InjectWith(yotogiSkillVisCheckHook, 0, 1, InjectFlags.PassTag | InjectFlags.ModifyReturn);

            WritePreviousLine("NumRound2");
            wf.GetMethod("NumRound2")
            .InjectWith(onValueRoundInt1, 0, 0, InjectFlags.ModifyReturn | InjectFlags.PassParametersVal);

            WritePreviousLine("NumRound3");
            wf.GetMethod("NumRound3")
            .InjectWith(onValueRoundInt1, 0, 0, InjectFlags.ModifyReturn | InjectFlags.PassParametersVal);

            WritePreviousLine("NumRound4(int)");
            wf.GetMethod("NumRound4", typeof(int))
            .InjectWith(onValueRoundInt1, 0, 0, InjectFlags.ModifyReturn | InjectFlags.PassParametersVal);

            WritePreviousLine("NumRound4(long)");
            wf.GetMethod("NumRound4", typeof(long))
            .InjectWith(onValueRoundLong1, 0, 0, InjectFlags.ModifyReturn | InjectFlags.PassParametersVal);

            WritePreviousLine("NumRound6");
            wf.GetMethod("NumRound6")
            .InjectWith(onValueRoundLong1, 0, 0, InjectFlags.ModifyReturn | InjectFlags.PassParametersVal);

            WritePreviousLine("RoundMinMax(int)");
            wf.GetMethod("RoundMinMax", typeof(int), typeof(int), typeof(int))
            .InjectWith(onValueRoundInt3, 0, 0, InjectFlags.ModifyReturn | InjectFlags.PassParametersVal);

            WritePreviousLine("RoundMinMax(long)");
            wf.GetMethod("RoundMinMax", typeof(long), typeof(long), typeof(long))
            .InjectWith(onValueRoundLong3, 0, 0, InjectFlags.ModifyReturn | InjectFlags.PassParametersVal);

            if (freeModeItemEveryday != null)
            {
                WritePreviousLine("FreeModeItemEveryday.ctor");
                freeModeItemEveryday.GetMethod(".ctor")
                .InjectWith(
                    postProcessFreeModeSceneHook,
                    -1,
                    0,
                    InjectFlags.PassFields,
                    typeFields: new[] { freeModeItemEveryday.GetField("is_enabled_") });
            }

            if (freeModeItemVip != null)
            {
                WritePreviousLine("FreeModeItemVip.ctor");
                freeModeItemVip.GetMethod(".ctor")
                .InjectWith(
                    postProcessFreeModeSceneHook,
                    -1,
                    0,
                    InjectFlags.PassFields,
                    typeFields: new[] { freeModeItemVip.GetField("is_enabled_") });
            }

            Console.WriteLine("Done. Patching class members:\n");
            WritePreviousLine("MaidParam.status_");
            maidParam.ChangeAccess("status_");

            WritePreviousLine("MaidParam.status_");
            playerParam.ChangeAccess("status_");

            WritePreviousLine("param.Status.kInitMaidPoint");
            status.ChangeAccess("kInitMaidPoint");

            SetPatchedAttribute(args.Assembly, TAG);
            SetCustomPatchedAttribute(args.Assembly);
            Console.WriteLine("\nPatching complete.");
        }
Пример #17
0
        /// <summary>
        ///     Searches for a method that can be used to inject into the specified target.
        /// </summary>
        /// <param name="type">This type in which the possible injection method lies.</param>
        /// <param name="name">Name of the injection method.</param>
        /// <param name="target">The target method which to inject.</param>
        /// <param name="flags">
        ///     Injection flags that specify what values to pass to the injection method and how to inject it. This
        ///     method attempts to find the hook method that satisfies all the specified flags.
        /// </param>
        /// <param name="localVarIDs">
        ///     An array of indicies of local variables to pass to the injection method. Used only if
        ///     <see cref="InjectFlags.PassLocals" /> is specified, otherwise ignored.
        /// </param>
        /// <param name="memberReferences">
        ///     An array of class fields from the type the target lies in to pass to the injection
        ///     method. Used only if <see cref="InjectFlags.PassFields" /> is specified, otherwise ignored.
        /// </param>
        /// <returns>
        ///     An instance of <see cref="InjectionDefinition" />, if a suitable injection method with the given name has been
        ///     found. Otherwise, null.
        /// </returns>
        public static InjectionDefinition GetInjectionMethod(this TypeDefinition type,
                                                             string name,
                                                             MethodDefinition target,
                                                             InjectFlags flags,
                                                             int[] localVarIDs = null,
                                                             params FieldDefinition[] memberReferences)
        {
            Logger.LogLine(LogMask.GetInjectionMethod, "##### GET INJECTION METHOD BEGIN #####");
            Logger.LogLine(
                LogMask.GetInjectionMethod,
                $"Attempting to get a suitable injection method for {type.Name}.{target?.Name}");

            if (string.IsNullOrEmpty(name))
            {
                Logger.LogLine(LogMask.GetInjectionMethod, "No injection method name specified");
                return(null);
            }
            if (target == null || !target.HasBody)
            {
                Logger.LogLine(LogMask.GetInjectionMethod, "No target specified or the target has no definition");
                return(null);
            }

            InjectValues hFlags = flags.ToValues();

#if DEBUG
            Logger.LogLine(LogMask.GetInjectionMethod, "Patch parameters:");
            Logger.LogLine(LogMask.GetInjectionMethod, $"Pass tag: {hFlags.PassTag}");
            Logger.LogLine(LogMask.GetInjectionMethod, $"Modify return value: {hFlags.ModifyReturn}");
            Logger.LogLine(LogMask.GetInjectionMethod, $"Pass THIS: {hFlags.PassInvokingInstance}");
            Logger.LogLine(LogMask.GetInjectionMethod, $"Pass method locals: {hFlags.PassLocals}");
            Logger.LogLine(LogMask.GetInjectionMethod, $"Pass member fields: {hFlags.PassFields}");
            Logger.LogLine(LogMask.GetInjectionMethod, $"Pass member parameters: {hFlags.PassParameters}");
            if (hFlags.PassParameters)
            {
                Logger.LogLine(
                    LogMask.GetInjectionMethod,
                    $"Member parameters are passed by {(hFlags.PassParametersByRef ? "reference" : "value")}");
            }
#endif

            if (hFlags.PassInvokingInstance && target.IsStatic)
            {
                Logger.LogLine(
                    LogMask.GetInjectionMethod,
                    $"{nameof(hFlags.PassInvokingInstance)} is true, but target is static!");
                return(null);
            }
            if (hFlags.PassFields && (target.IsStatic || memberReferences == null || memberReferences.Length == 0))
            {
                Logger.LogLine(
                    LogMask.GetInjectionMethod,
                    $"{nameof(hFlags.PassFields)} is true, but target is either static or no member references were specified");
                return(null);
            }
            if (hFlags.PassLocals && (!target.Body.HasVariables || localVarIDs == null || localVarIDs.Length == 0))
            {
                Logger.LogLine(
                    LogMask.GetInjectionMethod,
                    $"{nameof(hFlags.PassLocals)} is true, but target either doesn't have any locals or no local IDs were specified");
                return(null);
            }

            bool isVoid = target.ReturnType.FullName == "System.Void";

            int offsetSelf       = Convert.ToInt32(hFlags.PassTag);
            int offsetReturn     = Convert.ToInt32(hFlags.PassTag) + Convert.ToInt32(hFlags.PassInvokingInstance);
            int customParamCount = Convert.ToInt32(hFlags.ModifyReturn && !isVoid) + offsetReturn;
            int memberRefCount   = hFlags.PassFields ? memberReferences.Length : 0;
            int localRefCount    = hFlags.PassLocals ? localVarIDs.Length : 0;
            int paramCount       = (hFlags.PassParameters ? target.Parameters.Count : 0) + customParamCount + memberRefCount
                                   + localRefCount;

            Logger.LogLine(LogMask.GetInjectionMethod, "Needed parameter count:");
            Logger.LogLine(LogMask.GetInjectionMethod, $"Custom parameters (tag + return): {customParamCount}");
            Logger.LogLine(LogMask.GetInjectionMethod, $"Member references: {memberRefCount}");
            Logger.LogLine(LogMask.GetInjectionMethod, $"Local references: {localRefCount}");
            Logger.LogLine(LogMask.GetInjectionMethod, $"Member parameters: {paramCount}");

            IEnumerable <TypeReference> v;
            TypeComparer comparer = new TypeComparer();

            MethodDefinition injection =
                type.Methods.FirstOrDefault(
                    m =>
                    m.Name == name && m.Parameters.Count == paramCount && m.IsStatic && m.IsPublic && m.HasBody &&
                    (!hFlags.PassTag || m.Parameters[0].ParameterType.FullName == "System.Int32") &&
                    (!hFlags.PassInvokingInstance ||
                     comparer.Equals(m.Parameters[offsetSelf].ParameterType, target.DeclaringType)) &&
                    (!hFlags.ModifyReturn ||
                     (m.ReturnType.FullName == "System.Boolean" &&
                      (isVoid ||
                       comparer.Equals(
                           m.Parameters[offsetReturn].ParameterType,
                           new ByReferenceType(target.ReturnType))))) &&
                    (!hFlags.PassLocals ||
                     localVarIDs.All(i => 0 <= i && i < target.Body.Variables.Count) &&
                     m.Parameters.Slice(customParamCount, localRefCount)
                     .Select((p, i) => new { param = p, index = i })
                     .All(
                         t =>
                         t.param.ParameterType.IsByReference &&
                         comparer.Equals(
                             t.param.ParameterType,
                             new ByReferenceType(target.Body.Variables[t.index].VariableType)))) &&
                    (!hFlags.PassFields ||
                     (v =
                          m.Parameters.Slice(customParamCount + localRefCount, memberRefCount).Select(p => p.ParameterType))
                     .All(p => p.IsByReference) &&
                     v.SequenceEqual(
                         memberReferences.Select(p => (TypeReference) new ByReferenceType(p.FieldType)),
                         comparer)) &&
                    (!hFlags.PassParameters ||
                     (m.HasGenericParameters == target.HasGenericParameters &&
                      (!m.HasGenericParameters || m.GenericParameters.Count <= target.GenericParameters.Count) &&
                      (!hFlags.PassParametersByRef ||
                       m.Parameters.Skip(customParamCount + localRefCount + memberRefCount)
                       .All(p => p.ParameterType.IsByReference)) &&
                      m.Parameters.Skip(customParamCount + localRefCount + memberRefCount)
                      .Select(p => p.ParameterType)
                      .SequenceEqual(
                          target.Parameters.Select(
                              p => (hFlags.PassParametersByRef ? new ByReferenceType(p.ParameterType) : p.ParameterType)),
                          comparer))));

            if (injection == null)
            {
                Logger.LogLine(LogMask.GetInjectionMethod, "Did not find any matching methods!");
                return(null);
            }
            Logger.LogLine(LogMask.GetInjectionMethod, "Found injection method.");
            Logger.LogLine(LogMask.GetInjectionMethod, "##### GET INJECTION METHOD END #####");
            return(new InjectionDefinition
            {
                InjectMethod = injection,
                InjectTarget = target,
                Flags = flags,
                MemberReferences = memberReferences,
                LocalVarIDs = localVarIDs,
                _PrefixCount = customParamCount,
                _MemeberRefCount = hFlags.PassFields ? memberReferences.Length : 0,
                _ParameterCount = hFlags.PassParameters ? target.Parameters.Count : 0
            });
        }
        /// <summary>
        ///     Attempts to construct an instance of <see cref="InjectionDefinition" /> by linking the injection method with the
        ///     injection target (the method to be injected).
        ///     The way how the method is injected is specified by the injection flags. If the injection method does not match the
        ///     criteria set by the injection flags, an exception will be thrown.
        /// </summary>
        /// <param name="injectTarget">The method that will be injected.</param>
        /// <param name="injectMethod">The method which to inject.</param>
        /// <param name="flags">Injection flags that specify what values to pass to the injection method and how to inject it.</param>
        /// <param name="localVarIDs">
        ///     An array of indicies of local variables to pass to the injection method. Used only if
        ///     <see cref="InjectFlags.PassLocals" /> is specified, otherwise ignored.
        /// </param>
        /// <param name="memberReferences">
        ///     An array of class fields from the type the target lies in to pass to the injection
        ///     method. Used only if <see cref="InjectFlags.PassFields" /> is specified, otherwise ignored.
        /// </param>
        public InjectionDefinition(MethodDefinition injectTarget,
                                   MethodDefinition injectMethod,
                                   InjectFlags flags,
                                   int[] localVarIDs = null,
                                   params FieldDefinition[] memberReferences)
        {
            Assert(
            injectMethod.IsStatic,
            $"{nameof(injectMethod)} must be static in order to be used as the injection.");
            Assert(injectMethod.IsPublic, $"{nameof(injectMethod)} must be public.");
            Assert(
            injectMethod.HasBody,
            $"{nameof(injectMethod)} must have a definition in order to be used as the injecton.");

            InjectValues hFlags = flags.ToValues();

            bool isVoid = injectTarget.ReturnType.FullName == "System.Void";

            Assert(
            !hFlags.PassLocals || localVarIDs != null,
            $"Supposed to pass local references, but {nameof(localVarIDs)} is empty");
            Assert(
            !hFlags.PassFields || memberReferences != null,
            $"Supposed to pass member fields, but {nameof(memberReferences)} is empty!");

            int prefixCount = Convert.ToInt32(hFlags.PassTag) + Convert.ToInt32(hFlags.PassInvokingInstance)
                              + Convert.ToInt32(hFlags.ModifyReturn && !isVoid);
            int localsCount = hFlags.PassLocals ? localVarIDs.Length : 0;
            int memberRefCount = (hFlags.PassFields ? memberReferences.Length : 0);
            int paramCount = (hFlags.PassParameters ? injectTarget.Parameters.Count : 0);
            int parameters = injectMethod.Parameters.Count - prefixCount - localsCount - memberRefCount;
            // int parameters = injectMethod.Parameters.Count - prefixCount - localsCount - memberRefCount;
            // PassParameters => 0 < parameters <= injectTarget.Patameters.Count
            // !PassParameters => parameters = 0
            Assert(
            (hFlags.PassParameters && 0 < parameters && parameters <= injectTarget.Parameters.Count
             || !hFlags.PassParameters && parameters == 0),
            //injectMethod.Parameters.Count == prefixCount + localsCount + memberRefCount + paramCount,
            $@"The injection method has a wrong number of parameters! Check that the provided target method, local variables, member references and injection flags add up to the right number of parameters.
Needed parameters: Prefix: {
            prefixCount}, Locals: {localsCount}, Members: {memberRefCount}, Parameters: {
            (hFlags.PassParameters ? "between 1 and " + paramCount : "0")}, TOTAL: {
            (hFlags.PassParameters ? $"between {prefixCount + localsCount + memberRefCount + 1} and " : "")}{
            prefixCount + localsCount + memberRefCount + paramCount}.
Injection has {injectMethod.Parameters.Count
            } parameters.");

            TypeComparer comparer = new TypeComparer();

            if (hFlags.PassTag)
            {
                Assert(
                injectMethod.Parameters[0].ParameterType.FullName == "System.Int32",
                "Supposed to pass a tag, but the provided tag must be of type System.Int32 (int).");
            }

            if (hFlags.PassInvokingInstance)
            {
                Assert(
                !injectTarget.IsStatic,
                "Supposed to pass invoking instance, but the target method is static and thus is not bound to any instances.");
                Assert(
                comparer.Equals(
                injectMethod.Parameters[Convert.ToInt32(hFlags.PassTag)].ParameterType,
                injectTarget.DeclaringType),
                "Supposed to pass invoking instance, but the type of the instance does not match with the type declared in the injection method.");
            }

            if (hFlags.ModifyReturn)
            {
                Assert(
                injectMethod.ReturnType.FullName == "System.Boolean",
                "The injection method must return a boolean in order to alter the return value.");
                Assert(
                isVoid
                || comparer.Equals(
                injectMethod.Parameters[Convert.ToInt32(hFlags.PassTag) + Convert.ToInt32(hFlags.PassInvokingInstance)]
                   .ParameterType,
                new ByReferenceType(injectTarget.ReturnType)),
                "Supposed to modify the return value, but the provided return type does not match with the return type of the target method! Also make sure the type is passed by reference (out/ref).");
            }

            if (hFlags.PassLocals)
            {
                Assert(injectTarget.Body.HasVariables, "The target method does not have any locals.");
                Assert(
                localVarIDs.Length != 0,
                "Supposed to pass method locals, but the IDs of the locals were not specified.");
                Assert(
                localVarIDs.All(i => 0 <= i && i < injectTarget.Body.Variables.Count),
                "Supposed to receive local references, but the provided local variable index/indices do not exist in the target method!");
                Assert(
                injectMethod.Parameters.Slice(prefixCount, localsCount)
                            .Select((p, i) => new {param = p, index = localVarIDs[i]})
                            .All(
                            t =>
                            t.param.ParameterType.IsByReference
                            && comparer.Equals(
                            t.param.ParameterType,
                            new ByReferenceType(injectTarget.Body.Variables[t.index].VariableType))),
                "Supposed to receive local references, but the types between injection method and target method mismatch. Also make sure they are passed by reference (ref/out).");
            }

            if (hFlags.PassFields)
            {
                Assert(!injectTarget.IsStatic, "Cannot pass member references if the injection method is static!");
                Assert(
                memberReferences.Length != 0,
                "Supposed to pass member references, but no members were specified.");
                Assert(
                memberReferences.All(
                m =>
                m.DeclaringType.FullName == injectTarget.DeclaringType.FullName
                && m.DeclaringType.BaseType.FullName == injectTarget.DeclaringType.BaseType.FullName),
                $"The provided member fields do not belong to {injectTarget.DeclaringType}");

                IEnumerable<TypeReference> paramRefs =
                injectMethod.Parameters.Slice(prefixCount + localsCount, memberRefCount).Select(p => p.ParameterType);
                IEnumerable<TypeReference> typeReferences = paramRefs as TypeReference[] ?? paramRefs.ToArray();

                Assert(
                typeReferences.All(p => p.IsByReference),
                "Supposed to pass class members, but the provided parameters in the injection method are not of a reference type (ref)!");
                Assert(
                typeReferences.SequenceEqual(
                memberReferences.Select(f => (TypeReference) new ByReferenceType(f.FieldType)),
                comparer),
                "Supposed to pass class members, but the existing members are of a different type than the ones specified in the injection method.");
            }

            if (hFlags.PassParameters)
            {
                Assert(
                injectMethod.HasGenericParameters == injectTarget.Parameters.Any(p => p.ParameterType.IsGenericParameter),
                "The injection and target methods have mismatching specification of generic parameters!");

                Assert(
                !injectMethod.HasGenericParameters
                || injectMethod.GenericParameters.Count <= injectTarget.GenericParameters.Count + injectTarget.DeclaringType.GenericParameters.Count,
                "The injection and target methods have a mismatching number of generic parameters! The injection method must have less or the same number of generic parameters as the target!");

                Assert(
                !hFlags.PassParametersByRef
                || injectMethod.Parameters.Skip(prefixCount + localsCount + memberRefCount)
                               .All(p => p.ParameterType.IsByReference),
                "Supposed to pass target method parameters by reference, but the provided parameters in the injection method are not of a reference type (ref).");

                Assert(
                injectMethod.Parameters.Skip(prefixCount + localsCount + memberRefCount)
                            .Select(p => p.ParameterType)
                            .SequenceEqual(
                            injectTarget.Parameters.Take(parameters)
                                        .Select(
                                        p =>
                                        (hFlags.PassParametersByRef
                                         ? new ByReferenceType(p.ParameterType) : p.ParameterType)),
                            comparer),
                "Supposed to pass target method parameters by reference, but the types specified in injection and target methods do not match.");
            }

            InjectMethod = injectMethod;
            InjectTarget = injectTarget;
            Flags = flags;
            MemberReferences = memberReferences;
            LocalVarIDs = localVarIDs;
            _PrefixCount = prefixCount;
            _MemeberRefCount = memberRefCount;
            _ParameterCount = parameters;
        }
        private static int VerifyInjectionDefinition(MethodDefinition injectMethod,
                                                     MethodDefinition injectTarget,
                                                     InjectFlags flags,
                                                     int[] localVarIDs = null,
                                                     params FieldDefinition[] memberReferences)
        {
            Assert(injectMethod.IsStatic,
                   $"{nameof(injectMethod)} must be static in order to be used as the injection.");
            Assert(injectMethod.IsPublic, $"{nameof(injectMethod)} must be public.");
            Assert(injectMethod.HasBody,
                   $"{nameof(injectMethod)} must have a definition in order to be used as the injecton.");

            InjectValues hFlags = flags.ToValues();

            bool isVoid = injectTarget.ReturnType.FullName == "System.Void";

            Assert(!hFlags.PassLocals || localVarIDs != null,
                   $"Supposed to pass local references, but {nameof(localVarIDs)} is empty");
            Assert(!hFlags.PassFields || memberReferences != null,
                   $"Supposed to pass member fields, but {nameof(memberReferences)} is empty!");

            int prefixCount = Convert.ToInt32(hFlags.PassTag)
                              + Convert.ToInt32(hFlags.PassInvokingInstance)
                              + Convert.ToInt32(hFlags.ModifyReturn && !isVoid);
            int localsCount    = hFlags.PassLocals ? localVarIDs.Length : 0;
            int memberRefCount = hFlags.PassFields ? memberReferences.Length : 0;
            int paramCount     = hFlags.PassParameters ? injectTarget.Parameters.Count : 0;
            int parameters     = injectMethod.Parameters.Count - prefixCount - localsCount - memberRefCount;

            Assert(hFlags.PassParameters && 0 < parameters && parameters <= injectTarget.Parameters.Count ||
                   !hFlags.PassParameters && parameters == 0,
                   $@"The injection method has a wrong number of parameters! Check that the provided target method, local variables, member references and injection flags add up to the right number of parameters.
Needed parameters: Prefix: {prefixCount}, Locals: {localsCount}, Members: {memberRefCount}, Parameters: {
                               (hFlags.PassParameters ? "between 1 and " + paramCount : "0")
                           }, TOTAL: {
                               (hFlags.PassParameters
                                        ? $"between {prefixCount + localsCount + memberRefCount + 1} and "
                                        : "")
                           }{prefixCount + localsCount + memberRefCount + paramCount}.
Injection has {injectMethod.Parameters.Count} parameters.");

            TypeComparer comparer = TypeComparer.Instance;

            if (hFlags.PassTag)
            {
                string typeName = Enum.GetName(typeof(InjectValues.PassTagType), hFlags.TagType);
                Assert(injectMethod.Parameters[0].ParameterType.FullName == $"System.{typeName}",
                       $"Supposed to pass a tag, but the provided tag must be of type System.{typeName}.");
            }

            if (hFlags.PassInvokingInstance)
            {
                Assert(!injectTarget.IsStatic,
                       "Supposed to pass invoking instance, but the target method is static and thus is not bound to any instances.");
                Assert(comparer.Equals(injectMethod.Parameters[Convert.ToInt32(hFlags.PassTag)].ParameterType,
                                       injectTarget.DeclaringType),
                       "Supposed to pass invoking instance, but the type of the instance does not match with the type declared in the injection method.");
            }

            if (hFlags.ModifyReturn)
            {
                Assert(injectMethod.ReturnType.FullName == "System.Boolean",
                       "The injection method must return a boolean in order to alter the return value.");
                Assert(isVoid ||
                       comparer.Equals(injectMethod
                                       .Parameters[Convert.ToInt32(hFlags.PassTag)
                                                   + Convert.ToInt32(hFlags.PassInvokingInstance)].ParameterType,
                                       new ByReferenceType(injectTarget.ReturnType)),
                       "Supposed to modify the return value, but the provided return type does not match with the return type of the target method! Also make sure the type is passed by reference (out/ref).");
            }

            if (hFlags.PassLocals)
            {
                Assert(injectTarget.Body.HasVariables, "The target method does not have any locals.");
                Assert(localVarIDs.Length != 0,
                       "Supposed to pass method locals, but the IDs of the locals were not specified.");
                Assert(localVarIDs.All(i => 0 <= i && i < injectTarget.Body.Variables.Count),
                       "Supposed to receive local references, but the provided local variable index/indices do not exist in the target method!");
                Assert(injectMethod.Parameters.Slice(prefixCount, localsCount).Select((p, i) => new
                {
                    param = p,
                    index = localVarIDs[i]
                }).All(t => t.param.ParameterType.IsByReference &&
                       comparer.Equals(t.param.ParameterType,
                                       new ByReferenceType(injectTarget.Body.Variables[t.index]
                                                           .VariableType))),
                       "Supposed to receive local references, but the types between injection method and target method mismatch. Also make sure they are passed by reference (ref/out).");
            }

            if (hFlags.PassFields)
            {
                Assert(!injectTarget.IsStatic, "Cannot pass member references if the injection method is static!");
                Assert(memberReferences.Length != 0,
                       "Supposed to pass member references, but no members were specified.");
                Assert(memberReferences.All(m => m.DeclaringType.FullName == injectTarget.DeclaringType.FullName &&
                                            m.DeclaringType.BaseType.FullName
                                            == injectTarget.DeclaringType.BaseType.FullName),
                       $"The provided member fields do not belong to {injectTarget.DeclaringType}");

                IEnumerable <TypeReference> paramRefs = injectMethod
                                                        .Parameters.Slice(prefixCount + localsCount, memberRefCount)
                                                        .Select(p => p.ParameterType);
                IEnumerable <TypeReference> typeReferences = paramRefs as TypeReference[] ?? paramRefs.ToArray();

                Assert(typeReferences.All(p => p.IsByReference),
                       "Supposed to pass class members, but the provided parameters in the injection method are not of a reference type (ref)!");
                Assert(typeReferences.SequenceEqual(memberReferences.Select(f => (TypeReference)
                                                                            new ByReferenceType(f.FieldType)),
                                                    comparer),
                       "Supposed to pass class members, but the existing members are of a different type than the ones specified in the injection method.");
            }

            if (hFlags.PassParameters)
            {
                Assert(injectMethod.HasGenericParameters
                       == injectTarget.Parameters.Any(p => p.ParameterType.IsGenericParameter),
                       "The injection and target methods have mismatching specification of generic parameters!");

                Assert(!injectMethod.HasGenericParameters ||
                       injectMethod.GenericParameters.Count
                       <= injectTarget.GenericParameters.Count + injectTarget.DeclaringType.GenericParameters.Count,
                       "The injection and target methods have a mismatching number of generic parameters! The injection method must have less or the same number of generic parameters as the target!");

                Assert(!hFlags.PassParametersByRef ||
                       injectMethod.Parameters.Skip(prefixCount + localsCount + memberRefCount)
                       .All(p => p.ParameterType.IsByReference),
                       "Supposed to pass target method parameters by reference, but the provided parameters in the injection method are not of a reference type (ref).");

                Assert(injectMethod.Parameters.Skip(prefixCount + localsCount + memberRefCount)
                       .Select(p => p.ParameterType)
                       .SequenceEqual(injectTarget
                                      .Parameters.Take(parameters)
                                      .Select(p => hFlags.PassParametersByRef
                                                                       ? new ByReferenceType(p.ParameterType)
                                                                       : p.ParameterType),
                                      comparer),
                       "Supposed to pass target method parameters by reference, but the types specified in injection and target methods do not match.");
            }

            return(parameters);
        }
        internal static InjectionDefinition FindInjectionDefinition(TypeDefinition type,
                                                                    string name,
                                                                    MethodDefinition target,
                                                                    InjectFlags flags,
                                                                    int[] localVarIDs = null,
                                                                    params FieldDefinition[] memberReferences)
        {
            Logger.LogLine(LogMask.GetInjectionMethod, "##### GET INJECTION METHOD BEGIN #####");
            Logger.LogLine(LogMask.GetInjectionMethod,
                           $"Attempting to get a suitable injection method for {type.Name}.{target?.Name}");

            if (string.IsNullOrEmpty(name))
            {
                Logger.LogLine(LogMask.GetInjectionMethod, "No injection method name specified");
                return(null);
            }

            if (target == null || !target.HasBody)
            {
                Logger.LogLine(LogMask.GetInjectionMethod, "No target specified or the target has no definition");
                return(null);
            }

            InjectValues hFlags = flags.ToValues();

#if DEBUG
            Logger.LogLine(LogMask.GetInjectionMethod, "Patch parameters:");
            Logger.LogLine(LogMask.GetInjectionMethod, $"Pass tag: {hFlags.PassTag}");
            Logger.LogLine(LogMask.GetInjectionMethod, $"Modify return value: {hFlags.ModifyReturn}");
            Logger.LogLine(LogMask.GetInjectionMethod, $"Pass THIS: {hFlags.PassInvokingInstance}");
            Logger.LogLine(LogMask.GetInjectionMethod, $"Pass method locals: {hFlags.PassLocals}");
            Logger.LogLine(LogMask.GetInjectionMethod, $"Pass member fields: {hFlags.PassFields}");
            Logger.LogLine(LogMask.GetInjectionMethod, $"Pass member parameters: {hFlags.PassParameters}");
            if (hFlags.PassParameters)
            {
                Logger.LogLine(
                    LogMask.GetInjectionMethod,
                    $"Member parameters are passed by {(hFlags.PassParametersByRef ? "reference" : "value")}");
            }
#endif

            if (hFlags.PassInvokingInstance && target.IsStatic)
            {
                Logger.LogLine(LogMask.GetInjectionMethod,
                               $"{nameof(hFlags.PassInvokingInstance)} is true, but target is static!");
                return(null);
            }

            if (hFlags.PassFields && (target.IsStatic || memberReferences == null || memberReferences.Length == 0))
            {
                Logger.LogLine(LogMask.GetInjectionMethod,
                               $"{nameof(hFlags.PassFields)} is true, but target is either static or no member references were specified");
                return(null);
            }

            if (hFlags.PassLocals && (!target.Body.HasVariables || localVarIDs == null || localVarIDs.Length == 0))
            {
                Logger.LogLine(LogMask.GetInjectionMethod,
                               $"{nameof(hFlags.PassLocals)} is true, but target either doesn't have any locals or no local IDs were specified");
                return(null);
            }

            int parameterCount = 0;

            MethodDefinition injection = type.Methods.FirstOrDefault(m =>
            {
                try
                {
                    parameterCount = VerifyInjectionDefinition(m, target, flags, localVarIDs, memberReferences);
                }
                catch (InjectionDefinitionException e)
                {
                    Logger.LogLine(LogMask.GetInjectionMethod, e.Message);
                    return(false);
                }

                return(true);
            });

            if (injection == null)
            {
                Logger.LogLine(LogMask.GetInjectionMethod, "Did not find any matching methods!");
                return(null);
            }

            Logger.LogLine(LogMask.GetInjectionMethod, "Found injection method.");
            Logger.LogLine(LogMask.GetInjectionMethod, "##### GET INJECTION METHOD END #####");
            return(new InjectionDefinition
            {
                InjectMethod = injection,
                InjectTarget = target,
                Flags = flags,
                MemberReferences = memberReferences,
                LocalVarIDs = localVarIDs,
                ParameterCount = parameterCount
            });
        }
Пример #21
0
 /// <summary>
 ///     Checks whether a certain flag has been set in the given flag.
 /// </summary>
 /// <param name="flags">Flag combination to check.</param>
 /// <param name="flag">Flag to check with.</param>
 /// <returns>True, if the specified flag is specified in the flag combination.</returns>
 public static bool IsSet(this InjectFlags flags, InjectFlags flag)
 {
     return(flag == (flags & flag));
 }
Пример #22
0
        public static void InjectCallToMethod(AssemblyDefinition _gameAdef, string _injectClass, string _injectMethod, int _entryNr, AssemblyDefinition _modAdef, string _modClass, string _modMethod, InjectFlags _flag)
        {
            TypeDefinition   gameClass  = _gameAdef.MainModule.GetType(_injectClass);
            MethodDefinition gameMethod = null;

            if (gameClass != null)
            {
                var i = 0;
                foreach (MethodDefinition method in gameClass.Methods)
                {
                    if (method.Name == _injectMethod)
                    {
                        if (i == _entryNr)
                        {
                            Console.WriteLine("Selected method to inject into: " + method.FullName);
                            gameMethod = method;
                            break;
                        }
                        else
                        {
                            i++;
                        }
                    }
                }
            }
            else
            {
                Console.WriteLine("Could not find game class " + _injectClass);
                return;
            }


            if (gameMethod == null)
            {
                Console.WriteLine("Could not find game method " + _injectMethod);
                return;
            }

            MethodDefinition modMethod = null;

            foreach (ModuleDefinition module in _modAdef.Modules)
            {
                foreach (TypeDefinition type in module.Types)
                {
                    foreach (MethodDefinition method in type.Methods)
                    {
                        //Console.WriteLine(method.Name);
                        if (method.Name == _modMethod)
                        {
                            modMethod = method;
                        }
                    }
                }
            }

            if (modMethod == null)
            {
                Console.WriteLine("Could not find mod method " + _modMethod);
                return;
            }

            try
            {
                InjectionDefinition injector = new InjectionDefinition(gameMethod, modMethod, _flag);
                injector.Inject(0, null, InjectDirection.Before);
                Console.WriteLine("Injection into " + gameMethod.ToString() + " OK ");
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }
        }
Пример #23
0
 /// <summary>
 ///     Converts the flag (combination) into an instance of <see cref="InjectValues" />.
 /// </summary>
 /// <param name="flags">Flags to convert.</param>
 /// <returns>The injection flags represented as an instance of <see cref="InjectValues" />.</returns>
 public static InjectValues ToValues(this InjectFlags flags)
 {
     return(new InjectValues(flags));
 }
Пример #24
0
 public AddMethodAttribute(Type type, string method, InjectFlags flags) : this(type, method)
 {
     Flags = flags;
 }
Пример #25
0
 /// <summary>
 ///     Checks whether a certain flag has been set in the given flag.
 /// </summary>
 /// <param name="flags">Flag combination to check.</param>
 /// <param name="flag">Flag to check with.</param>
 /// <returns>True, if the specified flag is specified in the flag combination.</returns>
 public static bool IsSet(this InjectFlags flags, InjectFlags flag)
 {
     return flag == (flags & flag);
 }