/// <summary> /// Patches the specified assembly. /// </summary> /// <param name="args">Information about the assembly to patch.</param> public override void Patch(PatcherArguments args) { // Step 1: Get type definitions for the target type (specified in the configs) and Hooks from appropriate assemblies. TypeDefinition targetTypeDefinition = args.Assembly.MainModule.GetType(targetType); TypeDefinition hooksType = hookAssembly.MainModule.GetType("ExMultiGame.Hook.Hooks"); // Step 2: Get method definition for the target method (specified in the configs) and hook method (PrintHelloWorld). MethodDefinition targetMethodDefinition = targetTypeDefinition.Methods.FirstOrDefault(m => m.Name == targetMethod); MethodDefinition printHelloWorldMethod = hooksType.Methods.FirstOrDefault(m => m.Name == "PrintHelloWorld"); // Step 2.1: Import PrintHelloWorld method into the module where the target type is. // This is needed to ensure that the target assembly will know where to find PrintHelloWorld method. MethodReference printHelloWorldReference = targetMethodDefinition.Module.Import(printHelloWorldMethod); // Step 3: Perform patching. // Here we get the first instruction from the target method and an instance of ILProcessor to simplify IL manipulation. Instruction startInstruction = targetMethodDefinition.Body.Instructions[0]; ILProcessor il = targetMethodDefinition.Body.GetILProcessor(); // Insert a call to PrintHelloWorld. Since we don't need to pass any parameters, we don't need to add any more instructions. il.InsertBefore(startInstruction, il.Create(OpCodes.Call, printHelloWorldReference)); // Step 4: Add pacher attribute. // Finally you must ALWAYS add the patcher tag to mark the asembly as patched. // This tag is used in CanPatch(PatcherArguments args) method above to check if the assembly requires patching. SetPatchedAttribute(args.Assembly, PATCHED_TAG); }
public override void Patch(PatcherArguments args) { // Step 1: Get type definitions for SceneLogo and Hooks from appropriate assemblies. TypeDefinition maidType = args.Assembly.MainModule.GetType("Maid"); TypeDefinition maidParamType = args.Assembly.MainModule.GetType("MaidParam"); TypeDefinition hooksType = hookAssembly.MainModule.GetType("CM3D2.ExHelloWorld.Hook.Hooks"); // Step 2: Get method definition for the target method (Start) and hook method (PrintHelloWorld). MethodDefinition crossFadeMethod = maidType.Methods.FirstOrDefault(m => m.Name == "CrossFade"); MethodDefinition setCurExciteMethod = maidParamType.Methods.FirstOrDefault(m => m.Name == "SetCurExcite"); MethodDefinition crossFadeHookMethod = hooksType.Methods.FirstOrDefault(m => m.Name == "CrossFadeHook"); MethodDefinition setCurExciteMaxMethod = hooksType.Methods.FirstOrDefault(m => m.Name == "SetCurExciteMax"); // Step 2.1: Import hook methods into the module where target types are. // This is needed to ensure that the target assembly (Assembly-CSharp) will know where to find the hook methods. MethodReference crossFadeHookReference = crossFadeMethod.Module.Import(crossFadeHookMethod); MethodReference setCurExciteMaxReference = setCurExciteMethod.Module.Import(setCurExciteMaxMethod); // Step 3.1: Perform patching (CrossFade). // Here we get the first instruction from CrossFade method and an instance of ILProcessor to simplify IL manipulation. Instruction startInstruction = crossFadeMethod.Body.Instructions[0]; ILProcessor il = crossFadeMethod.Body.GetILProcessor(); // Firstly, push method's parameters onto the evaluation stack. // Since CrossFade is not static ldarg.0 will push the value of "this" onto the evaluation stack. // ldarg.1-6 will load method's other parameters. il.InsertBefore(startInstruction, il.Create(OpCodes.Ldarg_0)); il.InsertBefore(startInstruction, il.Create(OpCodes.Ldarg_1)); il.InsertBefore(startInstruction, il.Create(OpCodes.Ldarg_2)); il.InsertBefore(startInstruction, il.Create(OpCodes.Ldarg_3)); il.InsertBefore(startInstruction, il.Create(OpCodes.Ldarg_S, 4)); il.InsertBefore(startInstruction, il.Create(OpCodes.Ldarg_S, 5)); il.InsertBefore(startInstruction, il.Create(OpCodes.Ldarg_S, 6)); // Finally, we insert the call to CrossFadeHook method il.InsertBefore(startInstruction, il.Create(OpCodes.Call, crossFadeHookReference)); // Step 3.2: Perform patching (SetCurExcite). // Here we get the last instruction (ret) from SetCurExcite method and an instance of ILProcessor to simplify IL manipulation. startInstruction = setCurExciteMethod.Body.Instructions[setCurExciteMethod.Body.Instructions.Count - 1]; il = setCurExciteMethod.Body.GetILProcessor(); // Get reference to the status_ field defined in MaidParam. Since the field is defined in the same assembly as SetCurExcite, there is no need to explicitly import the field. FieldReference statusField = maidParamType.Fields.FirstOrDefault(f => f.Name == "status_"); // Firstly we get the value of status_. Since it is an instance field, we first use ldarg.0 to get "this" and then ldfld to get the value of status_ for "this". il.InsertBefore(startInstruction, il.Create(OpCodes.Ldarg_0)); il.InsertBefore(startInstruction, il.Create(OpCodes.Ldfld, statusField)); // Finally we call SetCurExciteMax itself. il.InsertBefore(startInstruction, il.Create(OpCodes.Call, setCurExciteMaxReference)); // Step 4: Add pacher attribute. // Finally you must ALWAYS add the patcher tag to mark the asembly as patched. // This tag is used in CanPatch(PatcherArguments args) method above to check if the assembly requires patching. SetPatchedAttribute(args.Assembly, PATCHED_TAG); }
public override void Patch(PatcherArguments args) { var mod = args.Assembly.MainModule; var type = mod.GetType("GameUty"); var meth = type.Methods.First(def => def.Name == ".cctor"); meth.Body.Instructions.First(ins => ins.OpCode == OpCodes.Stsfld && ((FieldReference) ins.Operand).Name == "ModPriorityToModFolder") .Previous.OpCode = OpCodes.Ldc_I4_1; // Pop a true instead SetPatchedAttribute(args.Assembly, TOKEN); }
public override bool CanPatch(PatcherArguments args) { if (args.Assembly.Name.Name != "Assembly-CSharp") return false; if (GetPatchedAttributes(args.Assembly).Any(attribute => attribute.Info == TOKEN)) { Console.ForegroundColor = ConsoleColor.Yellow; Console.WriteLine("Assembly Already Patched"); Console.ForegroundColor = ConsoleColor.Gray; return false; } return true; }
public override void Patch( PatcherArguments args ) { var PluginLoader = _hookAssembly.MainModule.GetType( "XUnity.AutoTranslator.Plugin.Core.PluginLoader" ); var LoadThroughBootstrapper = PluginLoader.GetMethod( "LoadThroughBootstrapper" ); var entryClasses = args.Assembly.MainModule.GetTypes().Where( x => EntryClasses.Contains( x.Name ) ).ToList(); foreach( var entryClass in entryClasses ) { var staticCtor = entryClass.Methods.FirstOrDefault( x => x.IsStatic && x.IsConstructor ); if( staticCtor != null ) { var injecctor = new InjectionDefinition( staticCtor, LoadThroughBootstrapper, InjectFlags.None ); injecctor.Inject( startCode: 0, direction: InjectDirection.Before ); } } SetPatchedAttribute( args.Assembly, "XUnity.AutoTranslator.Plugin.Core" ); }
public override bool CanPatch(PatcherArguments args) { return((args.Assembly.Name.Name == _assemblyName) && !HasAttribute(this, args.Assembly, "XUnity.AutoTranslator.Plugin.Core")); }
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 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 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 skillExpAddedHook = maidHooks.GetMethod(nameof(MaidStatusChangeHooks.OnSkillExpAdded)); 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 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("Pathichg 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_")}); } for (int i = (int) MaidChangeType.FirstName; i <= (int) MaidChangeType.Seikeiken; i++) { WritePreviousLine($"Set{typeNames[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++) { 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( skillExpAddedHook, 0, 0, InjectFlags.PassFields | 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.GetMethod("AddMaidClassExp", typeof (MaidClassType), typeof (int)), statusChangeIDHook1); WritePreviousLine("AddYotogiClassExp"); PatchFuncEnum( MaidChangeType.YotogiClassType, maidParam.GetMethod("AddYotogiClassExp", typeof (YotogiClassType), typeof (int)), statusChangeIDHook1); WritePreviousLine("ThumShot"); maidType.GetMethod("ThumShot").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_")}); 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); WritePreviousLine(setMethod); playerParam.GetMethod(setMethod).InjectWith(playerStatChangeHook, 0, (int) e, InjectFlags.PassTag); } 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); } 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.ModifyReturn); WritePreviousLine("IsExecStage"); skillData.GetMethod("IsExecStage").InjectWith(yotogiSkillVisCheckHook, 0, 0, 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); 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); Console.WriteLine("\nPatching complete."); }
public override bool CanPatch(PatcherArguments args) { return args.Assembly.Name.Name == "Assembly-CSharp" && !HasAttribute(args.Assembly, TAG); }
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."); }
public override bool CanPatch(PatcherArguments args) { return(args.Assembly.Name.Name == "Assembly-CSharp" && !HasAttribute(args.Assembly, TAG)); }
/// <summary> /// Checks if the provided assembly should be patched with this patcher. /// </summary> /// <param name="args">Information about the assembly.</param> /// <returns>True, if the assembly specified in args should be patched with this patcher. Otherwise, false.</returns> public override bool CanPatch(PatcherArguments args) { // Check that the name of the assembly is the same as specified in the configuration and make sure the assembly wasn't already patched with this patcher. // The latter is done by looking at the assembly's attributes and finding the patched tag. return(args.Assembly.Name.Name == assembly && GetPatchedAttributes(args.Assembly).All(att => att.Info != PATCHED_TAG)); }
public override void Patch(PatcherArguments args) { //Debugger.Launch(); var aMod = args.Assembly.MainModule; var eMod = EngineDefinition.MainModule; var ttBody = aMod.GetType("TBody"); var mMulTex = ttBody.Methods.First(def => def.Name == "MulTexProc" && def.HasParameters); // Min var tInt = typeof(int); var mMin = aMod.Import(typeof(Math).GetMethods(BindingFlags.Public | BindingFlags.Static) .Where(info => info.Name == "Min") .First( info => info.GetParameters().Length == 2 && info.GetParameters()[0].ParameterType == tInt && info.GetParameters()[1].ParameterType == tInt)); // Vector Variables var tVector = eMod.GetType("UnityEngine.Vector3"); var tVectorRef = aMod.Import(tVector); var vVecPos = mMulTex.Body.Variables.First(def => def.VariableType.FullName == tVectorRef.FullName); var vVecScale = mMulTex.Body.Variables.Last(def => def.VariableType.FullName == tVectorRef.FullName); var vVecRatio = new VariableDefinition(tVectorRef); var mVectorCtor = aMod.Import(tVector.Methods.First(def => def.Name == ".ctor" && def.Parameters.Count == 3)); var mVectorScale = aMod.Import(tVector.Methods.First(def => def.Name == "Scale" && def.Parameters.All(def2 => def2.ParameterType.FullName == tVectorRef.FullName))); mMulTex.Body.Variables.Add(vVecRatio); // RenderTexture Methods var tRender = eMod.GetType("UnityEngine.RenderTexture"); var mGetWd = tRender.Methods.First(def => def.Name == "get_width"); var mGetHd = tRender.Methods.First(def => def.Name == "get_height"); var mGetActived = tRender.Methods.First(def => def.Name == "get_active"); var mSetActived = tRender.Methods.First(def => def.Name == "set_active"); var mGetW = aMod.Import(mGetWd); var mGetH = aMod.Import(mGetHd); var mGetActive = aMod.Import(mGetActived); var mSetActive = aMod.Import(mSetActived); // First Point var pointOne = mMulTex.Body.Instructions.First(ins => ins.OpCode == OpCodes.Call && ((MethodReference) ins.Operand).Name == mSetActive.Name).Next; // Second Point var pointTwo = mMulTex.Body.Instructions.First(ins => ins.OpCode == OpCodes.Call && ((MethodReference) ins.Operand).Name == "PushMatrix"); // Patching var ilp = mMulTex.Body.GetILProcessor(); // Ratio calculated by Min(width,height) / 1024 // Then matrix translation and scale XY are multiplied by it // This block Calculates the ratio, and insantiates a Vector3 with it as the xy components ilp.InsertBefore(pointOne, ilp.Create(OpCodes.Ldloca_S, vVecRatio)); // LDLOCA.S _vRATIO ilp.InsertBefore(pointOne, ilp.Create(OpCodes.Call, mGetActive)); //--- CALL [UnityEngine.RenderTexture].get_active ilp.InsertBefore(pointOne, ilp.Create(OpCodes.Callvirt, mGetW)); //---- CALLVIRT [UnityEngine.RenderTexture].get_width ilp.InsertBefore(pointOne, ilp.Create(OpCodes.Call, mGetActive)); //--- CALL [UnityEngine.RenderTexture].get_active ilp.InsertBefore(pointOne, ilp.Create(OpCodes.Callvirt, mGetH)); //---- CALLVIRT [UnityEngine.RenderTexture].get_height ilp.InsertBefore(pointOne, ilp.Create(OpCodes.Call, mMin)); //--------- CALL [System.Math].Min ilp.InsertBefore(pointOne, ilp.Create(OpCodes.Conv_R4)); //------------ CONV_R4 ilp.InsertBefore(pointOne, ilp.Create(OpCodes.Ldc_R4, 1024f)); //------ LDC.R4 1024f ilp.InsertBefore(pointOne, ilp.Create(OpCodes.Div)); //---------------- DIV ilp.InsertBefore(pointOne, ilp.Create(OpCodes.Dup)); //---------------- DUP ilp.InsertBefore(pointOne, ilp.Create(OpCodes.Ldc_R4, 1f)); //--------- LDC.R4 1f ilp.InsertBefore(pointOne, ilp.Create(OpCodes.Call, mVectorCtor)); //-- CALL [UnityEngine.Vector3].ctor // Before calling PushMatrix // Scales the translation and position vectors by the ratio vector calculated above ilp.InsertBefore(pointTwo, ilp.Create(OpCodes.Ldloc_S, vVecPos)); //--- LDLOC.S _vPOS ilp.InsertBefore(pointTwo, ilp.Create(OpCodes.Ldloc_S, vVecRatio)); //- LDLOC.S _vRATIO ilp.InsertBefore(pointTwo, ilp.Create(OpCodes.Call, mVectorScale)); //- CALL [UnityEngine.Vector3].Scale ilp.InsertBefore(pointTwo, ilp.Create(OpCodes.Stloc_S, vVecPos)); //--- STLOC.S _vPOS ilp.InsertBefore(pointTwo, ilp.Create(OpCodes.Ldloc_S, vVecScale)); //- LDLOC.S _vSCALE ilp.InsertBefore(pointTwo, ilp.Create(OpCodes.Ldloc_S, vVecRatio)); //- LDLOC.S _vRATIO ilp.InsertBefore(pointTwo, ilp.Create(OpCodes.Call, mVectorScale)); //- CALL [UnityEngine.Vector3].Scale ilp.InsertBefore(pointTwo, ilp.Create(OpCodes.Stloc_S, vVecScale)); //- STLOC.S _vSCALE SetPatchedAttribute(args.Assembly, TOKEN); }