public static IEnumerable<Instruction> PushConvertedValue(this ValueNode node, ILContext context, FieldReference bpRef,
			IEnumerable<Instruction> pushServiceProvider, bool boxValueTypes, bool unboxValueTypes)
		{
			var module = context.Body.Method.Module;
			var targetTypeRef = GetBPReturnType(context, bpRef, node);

			TypeReference typeConverter;
			bpRef.HasTypeConverter(module, out typeConverter);

			return node.PushConvertedValue(context, targetTypeRef, typeConverter, pushServiceProvider, boxValueTypes,
				unboxValueTypes);
		}
		public static IEnumerable<Instruction> PushConvertedValue(this ValueNode node, ILContext context,
			TypeReference targetTypeRef, IEnumerable<ICustomAttributeProvider> attributeProviders,
			IEnumerable<Instruction> pushServiceProvider, bool boxValueTypes, bool unboxValueTypes)
		{
			TypeReference typeConverter = null;
			foreach (var attributeProvider in attributeProviders)
			{
				CustomAttribute typeConverterAttribute;
				if (
					(typeConverterAttribute =
						attributeProvider.CustomAttributes.FirstOrDefault(
							cad => TypeConverterAttribute.TypeConvertersType.Contains(cad.AttributeType.FullName))) != null)
				{
					typeConverter = typeConverterAttribute.ConstructorArguments[0].Value as TypeReference;
					break;
				}
			}
			return node.PushConvertedValue(context, targetTypeRef, typeConverter, pushServiceProvider, boxValueTypes,
				unboxValueTypes);
		}
		static void SetDataTemplate(IElementNode parentNode, ElementNode node, ILContext parentContext,
			IXmlLineInfo xmlLineInfo)
		{
			var parentVar = parentContext.Variables[parentNode];
			//Push the DataTemplate to the stack, for setting the template
			parentContext.IL.Emit(OpCodes.Ldloc, parentVar);

			//Create nested class
			//			.class nested private auto ansi sealed beforefieldinit '<Main>c__AnonStorey0'
			//			extends [mscorlib]System.Object

			var module = parentContext.Body.Method.Module;
			var anonType = new TypeDefinition(
				null,
				"<" + parentContext.Body.Method.Name + ">_anonXamlCDataTemplate_" + dtcount++,
				TypeAttributes.BeforeFieldInit |
				TypeAttributes.Sealed |
				TypeAttributes.NestedPrivate)
			{
				BaseType = module.TypeSystem.Object
			};

			parentContext.Body.Method.DeclaringType.NestedTypes.Add(anonType);
			var ctor = anonType.AddDefaultConstructor();

			var loadTemplate = new MethodDefinition("LoadDataTemplate",
				MethodAttributes.Assembly | MethodAttributes.HideBySig,
				module.TypeSystem.Object);
			anonType.Methods.Add(loadTemplate);

			var parentValues = new FieldDefinition("parentValues", FieldAttributes.Assembly, module.Import(typeof (object[])));
			anonType.Fields.Add(parentValues);

			TypeReference rootType = null;
			var vdefRoot = parentContext.Root as VariableDefinition;
			if (vdefRoot != null)
				rootType = vdefRoot.VariableType;
			var fdefRoot = parentContext.Root as FieldDefinition;
			if (fdefRoot != null)
				rootType = fdefRoot.FieldType;

			var root = new FieldDefinition("root", FieldAttributes.Assembly, rootType);
			anonType.Fields.Add(root);

			//Fill the loadTemplate Body
			var templateIl = loadTemplate.Body.GetILProcessor();
			templateIl.Emit(OpCodes.Nop);
			var templateContext = new ILContext(templateIl, loadTemplate.Body, parentValues)
			{
				Root = root
			};
			node.Accept(new CreateObjectVisitor(templateContext), null);
			node.Accept(new SetNamescopesAndRegisterNamesVisitor(templateContext), null);
			node.Accept(new SetFieldVisitor(templateContext), null);
			node.Accept(new SetResourcesVisitor(templateContext), null);
			node.Accept(new SetPropertiesVisitor(templateContext), null);
			templateIl.Emit(OpCodes.Ldloc, templateContext.Variables[node]);
			templateIl.Emit(OpCodes.Ret);

			//Instanciate nested class
			var parentIl = parentContext.IL;
			parentIl.Emit(OpCodes.Newobj, ctor);

			//Copy required local vars
			parentIl.Emit(OpCodes.Dup); //Duplicate the nestedclass instance
			parentIl.Append(node.PushParentObjectsArray(parentContext));
			parentIl.Emit(OpCodes.Stfld, parentValues);
			parentIl.Emit(OpCodes.Dup); //Duplicate the nestedclass instance
			if (parentContext.Root is VariableDefinition)
				parentIl.Emit(OpCodes.Ldloc, parentContext.Root as VariableDefinition);
			else if (parentContext.Root is FieldDefinition)
			{
				parentIl.Emit(OpCodes.Ldarg_0);
				parentIl.Emit(OpCodes.Ldfld, parentContext.Root as FieldDefinition);
			}
			else
				throw new InvalidProgramException();
			parentIl.Emit(OpCodes.Stfld, root);

			//SetDataTemplate
			parentIl.Emit(OpCodes.Ldftn, loadTemplate);
			var funcCtor =
				module.Import(typeof (Func<>))
					.MakeGenericInstanceType(module.TypeSystem.Object)
					.Resolve()
					.Methods.First(md => md.IsConstructor && md.Parameters.Count == 2)
					.MakeGeneric(module.TypeSystem.Object);
			parentIl.Emit(OpCodes.Newobj, module.Import(funcCtor));

			var propertySetter =
				module.Import(typeof (IDataTemplate)).Resolve().Properties.First(p => p.Name == "LoadTemplate").SetMethod;
			parentContext.IL.Emit(OpCodes.Callvirt, module.Import(propertySetter));
		}
		public static IEnumerable<Instruction> ProvideValue(VariableDefinitionReference vardefref, ILContext context,
			ModuleDefinition module, ElementNode node)
		{
			GenericInstanceType markupExtension;
			IList<TypeReference> genericArguments;
			if (vardefref.VariableDefinition.VariableType.FullName == "Xamarin.Forms.Xaml.ArrayExtension" &&
			    vardefref.VariableDefinition.VariableType.ImplementsGenericInterface("Xamarin.Forms.Xaml.IMarkupExtension`1",
				    out markupExtension, out genericArguments))
			{
				var markExt = markupExtension.Resolve();
				var provideValueInfo = markExt.Methods.First(md => md.Name == "ProvideValue");
				var provideValue = module.Import(provideValueInfo);
				provideValue =
					module.Import(provideValue.MakeGeneric(markupExtension.GenericArguments.Select(tr => module.Import(tr)).ToArray()));

				var typeNode = node.Properties[new XmlName("", "Type")];
				TypeReference arrayTypeRef;
				if (context.TypeExtensions.TryGetValue(typeNode, out arrayTypeRef))
					vardefref.VariableDefinition = new VariableDefinition(module.Import(arrayTypeRef.MakeArrayType()));
				else
					vardefref.VariableDefinition = new VariableDefinition(module.Import(genericArguments.First()));
				yield return Instruction.Create(OpCodes.Ldloc, context.Variables[node]);
				foreach (var instruction in node.PushServiceProvider(context))
					yield return instruction;
				yield return Instruction.Create(OpCodes.Callvirt, provideValue);

				if (arrayTypeRef != null)
					yield return Instruction.Create(OpCodes.Castclass, module.Import(arrayTypeRef.MakeArrayType()));
				yield return Instruction.Create(OpCodes.Stloc, vardefref.VariableDefinition);
			}
			else if (vardefref.VariableDefinition.VariableType.ImplementsGenericInterface("Xamarin.Forms.Xaml.IMarkupExtension`1",
				out markupExtension, out genericArguments))
			{
				var markExt = markupExtension.Resolve();
				var provideValueInfo = markExt.Methods.First(md => md.Name == "ProvideValue");
				var provideValue = module.Import(provideValueInfo);
				provideValue =
					module.Import(provideValue.MakeGeneric(markupExtension.GenericArguments.Select(tr => module.Import(tr)).ToArray()));

				vardefref.VariableDefinition = new VariableDefinition(module.Import(genericArguments.First()));
				yield return Instruction.Create(OpCodes.Ldloc, context.Variables[node]);
				foreach (var instruction in node.PushServiceProvider(context))
					yield return instruction;
				yield return Instruction.Create(OpCodes.Callvirt, provideValue);
				yield return Instruction.Create(OpCodes.Stloc, vardefref.VariableDefinition);
			}
			else if (context.Variables[node].VariableType.ImplementsInterface(module.Import(typeof (IMarkupExtension))))
			{
				var markExt = module.Import(typeof (IMarkupExtension)).Resolve();
				var provideValueInfo = markExt.Methods.First(md => md.Name == "ProvideValue");
				var provideValue = module.Import(provideValueInfo);

				vardefref.VariableDefinition = new VariableDefinition(module.TypeSystem.Object);
				yield return Instruction.Create(OpCodes.Ldloc, context.Variables[node]);
				foreach (var instruction in node.PushServiceProvider(context))
					yield return instruction;
				yield return Instruction.Create(OpCodes.Callvirt, provideValue);
				yield return Instruction.Create(OpCodes.Stloc, vardefref.VariableDefinition);
			}
			else if (context.Variables[node].VariableType.ImplementsInterface(module.Import(typeof (IValueProvider))))
			{
				var markExt = module.Import(typeof (IValueProvider)).Resolve();
				var provideValueInfo = markExt.Methods.First(md => md.Name == "ProvideValue");
				var provideValue = module.Import(provideValueInfo);

				vardefref.VariableDefinition = new VariableDefinition(module.TypeSystem.Object);
				yield return Instruction.Create(OpCodes.Ldloc, context.Variables[node]);
				foreach (var instruction in node.PushServiceProvider(context))
					yield return instruction;
				yield return Instruction.Create(OpCodes.Callvirt, provideValue);
				yield return Instruction.Create(OpCodes.Stloc, vardefref.VariableDefinition);
			}
		}
		public static void SetPropertyValue(VariableDefinition parent, XmlName propertyName, INode valueNode,
			ILContext context, IXmlLineInfo iXmlLineInfo)
		{
			var elementType = parent.VariableType;
			var localName = propertyName.LocalName;
			var module = context.Body.Method.Module;
			var br = Instruction.Create(OpCodes.Nop);
			TypeReference declaringTypeReference;
			var handled = false;

			//If it's an attached BP, update elementType and propertyName
			var attached = GetNameAndTypeRef(ref elementType, propertyName.NamespaceURI, ref localName, context, iXmlLineInfo);

			//If the target is an event, connect
			//			IL_0007:  ldloc.0 
			//			IL_0008:  ldarg.0 
			//			IL_0009:  ldftn instance void class Xamarin.Forms.Xaml.XamlcTests.MyPage::OnButtonClicked(object, class [mscorlib]System.EventArgs)
			//			IL_000f:  newobj instance void class [mscorlib]System.EventHandler::'.ctor'(object, native int)
			//			IL_0014:  callvirt instance void class [Xamarin.Forms.Core]Xamarin.Forms.Button::add_Clicked(class [mscorlib]System.EventHandler)

			var eventinfo = elementType.GetEvent(ed => ed.Name == localName);
			if (eventinfo != null)
			{
				var value = ((ValueNode)valueNode).Value;

				context.IL.Emit(OpCodes.Ldloc, parent);
				if (context.Root is VariableDefinition)
					context.IL.Emit(OpCodes.Ldloc, context.Root as VariableDefinition);
				else if (context.Root is FieldDefinition)
				{
					context.IL.Emit(OpCodes.Ldarg_0);
					context.IL.Emit(OpCodes.Ldfld, context.Root as FieldDefinition);
				}
				else
					throw new InvalidProgramException();
				var declaringType = context.Body.Method.DeclaringType;
				if (declaringType.IsNested)
					declaringType = declaringType.DeclaringType;
				var handler = declaringType.AllMethods().FirstOrDefault(md => md.Name == value as string);
				if (handler == null)
				{
					throw new XamlParseException(
						string.Format("EventHandler \"{0}\" not found in type \"{1}\"", value, context.Body.Method.DeclaringType.FullName),
						iXmlLineInfo);
				}
				context.IL.Emit(OpCodes.Ldftn, handler);
				//FIXME: eventually get the right ctor instead fo the First() one, just in case another one could exists (not even sure it's possible).
				var ctor = module.Import(eventinfo.EventType.Resolve().GetConstructors().First());
				ctor = ctor.ResolveGenericParameters(eventinfo.EventType, module);
				context.IL.Emit(OpCodes.Newobj, module.Import(ctor));
				context.IL.Emit(OpCodes.Callvirt, module.Import(eventinfo.AddMethod));
				return;
			}

			FieldReference bpRef = elementType.GetField(fd => fd.Name == localName + "Property" && fd.IsStatic && fd.IsPublic,
				out declaringTypeReference);
			if (bpRef != null)
			{
				bpRef = module.Import(bpRef.ResolveGenericParameters(declaringTypeReference));
				bpRef.FieldType = module.Import(bpRef.FieldType);
			}

			//If Value is DynamicResource, SetDynamicResource
			VariableDefinition varValue;
			if (bpRef != null && valueNode is IElementNode &&
			    context.Variables.TryGetValue(valueNode as IElementNode, out varValue) &&
			    varValue.VariableType.FullName == typeof (DynamicResource).FullName)
			{
				var setDynamicResource =
					module.Import(typeof (IDynamicResourceHandler)).Resolve().Methods.First(m => m.Name == "SetDynamicResource");
				var getKey = typeof (DynamicResource).GetProperty("Key").GetMethod;

				context.IL.Emit(OpCodes.Ldloc, parent);
				context.IL.Emit(OpCodes.Ldsfld, bpRef);
				context.IL.Emit(OpCodes.Ldloc, varValue);
				context.IL.Emit(OpCodes.Callvirt, module.Import(getKey));
				context.IL.Emit(OpCodes.Callvirt, module.Import(setDynamicResource));
				return;
			}

			//If Value is a BindingBase and target is a BP, SetBinding
			if (bpRef != null && valueNode is IElementNode &&
			    context.Variables.TryGetValue(valueNode as IElementNode, out varValue) &&
			    varValue.VariableType.InheritsFromOrImplements(module.Import(typeof (BindingBase))))
			{
				//TODO: check if parent is a BP
				var setBinding = typeof (BindableObject).GetMethod("SetBinding",
					new[] { typeof (BindableProperty), typeof (BindingBase) });

				context.IL.Emit(OpCodes.Ldloc, parent);
				context.IL.Emit(OpCodes.Ldsfld, bpRef);
				context.IL.Emit(OpCodes.Ldloc, varValue);
				context.IL.Emit(OpCodes.Callvirt, module.Import(setBinding));
				return;
			}

			//If it's a BP, SetValue ()
			//			IL_0007:  ldloc.0 
			//			IL_0008:  ldsfld class [Xamarin.Forms.Core]Xamarin.Forms.BindableProperty [Xamarin.Forms.Core]Xamarin.Forms.Label::TextProperty
			//			IL_000d:  ldstr "foo"
			//			IL_0012:  callvirt instance void class [Xamarin.Forms.Core]Xamarin.Forms.BindableObject::SetValue(class [Xamarin.Forms.Core]Xamarin.Forms.BindableProperty, object)
			if (bpRef != null)
			{
				//TODO: check if parent is a BP
				var setValue = typeof (BindableObject).GetMethod("SetValue", new[] { typeof (BindableProperty), typeof (object) });

				if (valueNode is ValueNode)
				{
					context.IL.Emit(OpCodes.Ldloc, parent);
					context.IL.Emit(OpCodes.Ldsfld, bpRef);
					context.IL.Append(((ValueNode)valueNode).PushConvertedValue(context, bpRef, valueNode.PushServiceProvider(context),
						true, false));
					context.IL.Emit(OpCodes.Callvirt, module.Import(setValue));
					return;
				}
				if (valueNode is IElementNode)
				{
					var getPropertyReturnType = module.Import(typeof (BindableProperty).GetProperty("ReturnType").GetGetMethod());
					//FIXME: this should check for inheritance too
					var isInstanceOfType = module.Import(typeof (Type).GetMethod("IsInstanceOfType", new[] { typeof (object) }));
					var brfalse = Instruction.Create(OpCodes.Nop);

					context.IL.Emit(OpCodes.Ldsfld, bpRef);
					context.IL.Emit(OpCodes.Call, getPropertyReturnType);
					context.IL.Emit(OpCodes.Ldloc, context.Variables[valueNode as IElementNode]);
					if (context.Variables[valueNode as IElementNode].VariableType.IsValueType)
						context.IL.Emit(OpCodes.Box, context.Variables[valueNode as IElementNode].VariableType);
					context.IL.Emit(OpCodes.Callvirt, isInstanceOfType);
					context.IL.Emit(OpCodes.Brfalse, brfalse);
					context.IL.Emit(OpCodes.Ldloc, parent);
					context.IL.Emit(OpCodes.Ldsfld, bpRef);
					context.IL.Emit(OpCodes.Ldloc, context.Variables[(IElementNode)valueNode]);
					if (context.Variables[valueNode as IElementNode].VariableType.IsValueType)
						context.IL.Emit(OpCodes.Box, context.Variables[valueNode as IElementNode].VariableType);
					context.IL.Emit(OpCodes.Callvirt, module.Import(setValue));
					context.IL.Emit(OpCodes.Br, br);
					context.IL.Append(brfalse);
					handled = true;
				}
			}

			//If it's a property, set it
			//			IL_0007:  ldloc.0 
			//			IL_0008:  ldstr "foo"
			//			IL_000d:  callvirt instance void class [Xamarin.Forms.Core]Xamarin.Forms.Label::set_Text(string)
			PropertyDefinition property = elementType.GetProperty(pd => pd.Name == localName, out declaringTypeReference);
			MethodDefinition propertySetter;
			if (property != null && (propertySetter = property.SetMethod) != null && propertySetter.IsPublic)
			{
				module.Import(elementType.Resolve());
				var propertySetterRef =
					module.Import(module.Import(propertySetter).ResolveGenericParameters(declaringTypeReference, module));
				propertySetterRef.ImportTypes(module);
				var propertyType = property.ResolveGenericPropertyType(declaringTypeReference);
				ValueNode vnode = null;

				if ((vnode = valueNode as ValueNode) != null)
				{
					context.IL.Emit(OpCodes.Ldloc, parent);
					context.IL.Append(vnode.PushConvertedValue(context,
						propertyType,
						new ICustomAttributeProvider[] { property, propertyType.Resolve() },
						valueNode.PushServiceProvider(context), false, true));
					context.IL.Emit(OpCodes.Callvirt, propertySetterRef);

					context.IL.Append(br);
					return;
				}
				if (valueNode is IElementNode)
				{
					var vardef = context.Variables[(ElementNode)valueNode];
					var implicitOperators =
						vardef.VariableType.GetMethods(md => md.IsPublic && md.IsStatic && md.IsSpecialName && md.Name == "op_Implicit",
							module).ToList();
					MethodReference implicitOperator = null;
					if (implicitOperators.Any())
					{
						foreach (var op in implicitOperators)
						{
							var cast = op.Item1;
							var opDeclTypeRef = op.Item2;
							var castDef = module.Import(cast).ResolveGenericParameters(opDeclTypeRef, module);
							var returnType = castDef.ReturnType;
							if (returnType.IsGenericParameter)
								returnType = ((GenericInstanceType)opDeclTypeRef).GenericArguments[((GenericParameter)returnType).Position];
							if (returnType.FullName == propertyType.FullName &&
							    cast.Parameters[0].ParameterType.Name == vardef.VariableType.Name)
							{
								implicitOperator = castDef;
								break;
							}
						}
					}

					//TODO replace latest check by a runtime type check
					if (implicitOperator != null || vardef.VariableType.InheritsFromOrImplements(propertyType) ||
					    propertyType.FullName == "System.Object" || vardef.VariableType.FullName == "System.Object")
					{
						context.IL.Emit(OpCodes.Ldloc, parent);
						context.IL.Emit(OpCodes.Ldloc, vardef);
						if (implicitOperator != null)
						{
							//						IL_000f:  call !0 class [Xamarin.Forms.Core]Xamarin.Forms.OnPlatform`1<bool>::op_Implicit(class [Xamarin.Forms.Core]Xamarin.Forms.OnPlatform`1<!0>)
							context.IL.Emit(OpCodes.Call, module.Import(implicitOperator));
						}
						else if (!vardef.VariableType.IsValueType && propertyType.IsValueType)
							context.IL.Emit(OpCodes.Unbox_Any, module.Import(propertyType));
						else if (vardef.VariableType.IsValueType && propertyType.FullName == "System.Object")
							context.IL.Emit(OpCodes.Box, vardef.VariableType);
						context.IL.Emit(OpCodes.Callvirt, propertySetterRef);
						context.IL.Append(br);
						return;
					}
					handled = true;
				}
			}

			//If it's an already initialized property, add to it
			MethodDefinition propertyGetter;
			//TODO: check if property is assigned
			if (property != null && (propertyGetter = property.GetMethod) != null && propertyGetter.IsPublic)
			{
				var propertyGetterRef = module.Import(propertyGetter);
				propertyGetterRef = module.Import(propertyGetterRef.ResolveGenericParameters(declaringTypeReference, module));
				var propertyType = propertyGetterRef.ReturnType.ResolveGenericParameters(declaringTypeReference);

				//TODO check md.Parameters[0] type
				var adderTuple =
					propertyType.GetMethods(md => md.Name == "Add" && md.Parameters.Count == 1, module).FirstOrDefault();
				if (adderTuple != null)
				{
					var adderRef = module.Import(adderTuple.Item1);
					adderRef = module.Import(adderRef.ResolveGenericParameters(adderTuple.Item2, module));

					if (valueNode is IElementNode)
					{
						context.IL.Emit(OpCodes.Ldloc, parent);
						context.IL.Emit(OpCodes.Callvirt, propertyGetterRef);
						context.IL.Emit(OpCodes.Ldloc, context.Variables[(ElementNode)valueNode]);
						context.IL.Emit(OpCodes.Callvirt, adderRef);
						if (adderRef.ReturnType.FullName != "System.Void")
							context.IL.Emit(OpCodes.Pop);
						context.IL.Append(br);
						return;
					}
				}
			}
			if (!handled)
			{
				throw new XamlParseException(string.Format("No property, bindable property, or event found for '{0}'", localName),
					iXmlLineInfo);
			}
			context.IL.Append(br);
		}
            private static void ApplyModHackfixes(MonoModder modder)
            {
                // See Coroutine.ForceDelayedSwap for more info.
                // Older mods are built with this delay in mind, except for hooks.
                MethodInfo coroutineWrapper =
                    (_Relinking?.Dependencies?.Find(dep => dep.Name == CoreModule.Instance.Metadata.Name)
                     ?.Version ?? new Version(0, 0, 0, 0)) < new Version(1, 2563, 0) ?
                    typeof(CoroutineDelayHackfixHelper).GetMethod("Wrap") : null;

                if (coroutineWrapper == null && _Relinking == null && !(
                        // Some mods require additional special care.
                        _Relinking.Name == "AdventureHelper" // Don't check the version for this mod as the hackfix is harmless.
                        ))
                {
                    return; // No hackfixes necessary.
                }
                void CrawlMethod(MethodDefinition method)
                {
                    string methodID = method.GetID();

                    if (coroutineWrapper != null && method.HasBody && method.ReturnType.FullName == "System.Collections.IEnumerator")
                    {
                        using (ILContext ctx = new ILContext(method)) {
                            ctx.Invoke(ctx => {
                                ILCursor c = new ILCursor(ctx);
                                while (c.TryGotoNext(i => i.MatchRet()))
                                {
                                    c.Next.OpCode  = OpCodes.Ldstr;
                                    c.Next.Operand = methodID;
                                    c.Index++;

                                    // Scan the nested IEnumerator type if it's already getting rid of the delay itself.
                                    c.Emit(
                                        method.GetCustomAttribute("System.Runtime.CompilerServices.IteratorStateMachineAttribute") is CustomAttribute ca_IteratorStateMachine &&
                                        ca_IteratorStateMachine.ConstructorArguments[0].Value is TypeReference iteratorType &&
                                        iteratorType.SafeResolve()?.FindMethod("MoveNext") is MethodDefinition iteratorMethod &&
                                        (iteratorMethod.Body?.Instructions.Any(i => i.Operand is MethodReference callee && callee.Name == "MoveNext") ?? false)
                                        ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0);

                                    c.Emit(OpCodes.Call, coroutineWrapper);
                                    c.Emit(OpCodes.Ret);
                                }
                            });
                        }
                    }

                    if (_ModHackfixNoAtlasFallback.Contains(methodID))
                    {
                        using (ILContext ctx = new ILContext(method)) {
                            ctx.Invoke(ctx => {
                                ILCursor c = new ILCursor(ctx);

                                c.Emit(OpCodes.Ldsfld, typeof(GFX).GetField("Game"));
                                c.Emit(OpCodes.Ldnull);
                                c.Emit(OpCodes.Callvirt, typeof(patch_Atlas).GetMethod("PushFallback"));

                                while (c.TryGotoNext(MoveType.AfterLabel, i => i.MatchRet()))
                                {
                                    c.Emit(OpCodes.Ldsfld, typeof(GFX).GetField("Game"));
                                    c.Emit(OpCodes.Callvirt, typeof(patch_Atlas).GetMethod("PopFallback"));
                                    c.Emit(OpCodes.Pop);
                                    c.Index++;
                                }
                            });
                        }
                    }
                }

                void CrawlType(TypeDefinition type)
                {
                    foreach (MethodDefinition method in type.Methods)
                    {
                        CrawlMethod(method);
                    }

                    foreach (TypeDefinition nested in type.NestedTypes)
                    {
                        CrawlType(nested);
                    }
                }

                foreach (TypeDefinition type in modder.Module.Types)
                {
                    CrawlType(type);
                }
            }
Beispiel #7
0
        // We're looking for these instructions and we want to delete all but the first one
        // IL_0114: stfld     int32 Terraria.Player::manaRegen
        //
        // IL_0119: ldarg.0
        // IL_011A: ldflda    valuetype [Microsoft.Xna.Framework]Microsoft.Xna.Framework.Vector2 Terraria.Entity::velocity
        // IL_011F: ldfld     float32 [Microsoft.Xna.Framework]Microsoft.Xna.Framework.Vector2::X
        // IL_0124: ldc.r4    0.0
        // IL_0129: bne.un.s  IL_013D
        //
        // IL_012B: ldarg.0
        // IL_012C: ldflda    valuetype [Microsoft.Xna.Framework]Microsoft.Xna.Framework.Vector2 Terraria.Entity::velocity
        // IL_0131: ldfld     float32 [Microsoft.Xna.Framework]Microsoft.Xna.Framework.Vector2::Y
        // IL_0136: ldc.r4    0.0
        // IL_013B: beq.s     IL_0150
        //
        // IL_013D: ldarg.0
        // IL_013E: ldfld     int32[] Terraria.Player::grappling
        // IL_0143: ldc.i4.0
        // IL_0144: ldelem.i4
        // IL_0145: ldc.i4.0
        // IL_0146: bge.s     IL_0150
        //
        // IL_0148: ldarg.0
        // IL_0149: ldfld     bool Terraria.Player::manaRegenBuff
        // IL_014E: brfalse.s IL_0165
        private void Player_UpdateManaRegen(ILContext ilContext)
        {
            ILCursor cursor = new ILCursor(ilContext);

            Func <Instruction, bool>[] ilToRemove =
            {
                instruction => instruction.MatchLdarg(0),
                instruction => instruction.MatchLdflda <Entity>("velocity"),
                instruction => instruction.MatchLdfld <Vector2>("X"),
                instruction => instruction.MatchLdcR4(0.0f),
                instruction => instruction.Match(OpCodes.Bne_Un_S),

                instruction => instruction.MatchLdarg(0),
                instruction => instruction.MatchLdflda <Entity>("velocity"),
                instruction => instruction.MatchLdfld <Vector2>("Y"),
                instruction => instruction.MatchLdcR4(0.0f),
                instruction => instruction.Match(OpCodes.Beq_S),

                instruction => instruction.MatchLdarg(0),
                instruction => instruction.MatchLdfld <Terraria.Player>("grappling"),
                instruction => instruction.MatchLdcI4(0),
                instruction => instruction.MatchLdelemI4(),
                instruction => instruction.MatchLdcI4(0),
                instruction => instruction.Match(OpCodes.Bge_S),

                instruction => instruction.MatchLdarg(0),
                instruction => instruction.MatchLdfld <Terraria.Player>("manaRegenBuff"),
                instruction => instruction.Match(OpCodes.Brfalse_S)
            };
            bool found = false;             //whether or not we have matched our 1 + 19 instructions

            int    attemptCount       = 0;  //used for logging
            string loggedInstructions = "";

            while (cursor.TryGotoNext(
                       // Look for when we store "manaRegen". This occurs just before our ilToRemove instructions
                       instruction => instruction.MatchStfld <Terraria.Player>("manaRegen")))
            {
                ILCursor backup = cursor.Clone();
                found = true;
                foreach (Func <Instruction, bool> il in ilToRemove)
                {
                    // Move cursor to next instruction and see if it doesn't match
                    cursor.GotoNext();
                    loggedInstructions += attemptCount + ": " + cursor.Next?.OpCode + "\n";

                    if (!il(cursor.Next))
                    {
                        found = false;
                        break;
                    }
                }
                if (!found)
                {
                    continue;
                }

                // We use the backup cursor which was at the original position just before the 19 instructions
                backup.GotoNext();
                // remove the 19 instructions
                for (int i = 0; i < ilToRemove.Length; i++)
                {
                    backup.Remove();
                }
                break;
            }

            if (!found)
            {
                throw new Exception("Instructions not found; unable to patch. Sorry!\n" + loggedInstructions);
            }
        }
Beispiel #8
0
        public IEnumerable <Instruction> ProvideValue(VariableDefinitionReference vardefref, ModuleDefinition module, BaseNode node, ILContext context)
        {
            INode sourceNode = null;

            ((IElementNode)node).Properties.TryGetValue(new XmlName("", "Source"), out sourceNode);
            if (sourceNode == null)
            {
                ((IElementNode)node).Properties.TryGetValue(new XmlName(XamlParser.XFUri, "Source"), out sourceNode);
            }

            INode styleNode = null;

            if (!((IElementNode)node).Properties.TryGetValue(new XmlName("", "Style"), out styleNode) &&
                !((IElementNode)node).Properties.TryGetValue(new XmlName(XamlParser.XFUri, "Style"), out styleNode) &&
                ((IElementNode)node).CollectionItems.Count == 1)
            {
                styleNode = ((IElementNode)node).CollectionItems[0];
            }

            if (sourceNode != null && styleNode != null)
            {
                throw new XamlParseException("StyleSheet can not have both a Source and a content", node);
            }

            if (sourceNode == null && styleNode == null)
            {
                throw new XamlParseException("StyleSheet require either a Source or a content", node);
            }

            if (styleNode != null && !(styleNode is ValueNode))
            {
                throw new XamlParseException("Style property or Content is not a string literal", node);
            }

            if (sourceNode != null && !(sourceNode is ValueNode))
            {
                throw new XamlParseException("Source property is not a string literal", node);
            }

            if (styleNode != null)
            {
                var style = (styleNode as ValueNode).Value as string;
                yield return(Create(Ldstr, style));

                yield return(Create(Call, module.ImportMethodReference(("System.Maui.Core", "System.Maui.StyleSheets", "StyleSheet"),
                                                                       methodName: "FromString",
                                                                       parameterTypes: new[] { ("mscorlib", "System", "String") },
Beispiel #9
0
        private static void GetVRLookInput(ILContext il)
        {
            ILCursor c = new ILCursor(il);

            //Remove target info from stack. Why is it pushed so early anyway?
            c.GotoNext(x =>
                       x.MatchLdflda(typeof(RoR2.CameraModes.CameraModeBase.CameraModeContext), "targetInfo")
                       );

            c.Index--;

            c.RemoveRange(2);

            //Fixing brtrue label
            c.GotoNext(x =>
                       x.MatchLdloca(6)
                       );

            ILLabel sussyLabel = c.IncomingLabels.First();

            c.Index++;
            sussyLabel.Target = c.Next;
            c.Index--;

            //Replacing input vectors
            c.Remove();
            c.Index++;
            c.RemoveRange(6);

            c.EmitDelegate <Func <Rewired.Player, Vector2> >((player) =>
            {
                return(ModConfig.InitialMotionControlsValue ? Vector2.zero : new Vector2(player.GetAxisRaw(ModConfig.SnapTurn.Value ? 26 : 2), ModConfig.TempLockedCameraPitchValue ? 0 : player.GetAxisRaw(3)));
            });
            c.Emit(OpCodes.Stloc_S, (byte)6);

            c.Remove();
            c.Index++;
            c.RemoveRange(6);

            c.EmitDelegate <Func <Rewired.Player, Vector2> >((player) =>
            {
                return(new Vector2(player.GetAxisRaw(16), ModConfig.TempLockedCameraPitchValue ? 0 : player.GetAxisRaw(17)));
            });
            c.Emit(OpCodes.Stloc_S, (byte)7);

            int startIndex = c.Index;

            //Removing aim assist
            c.GotoNext(x => x.MatchCall(typeof(RoR2.CameraModes.CameraModePlayerBasic), "PerformAimAssist"));

            c.Index -= 3;

            c.RemoveRange(4);

            //Adding snap turn code
            c.GotoNext(x => x.MatchLdflda(typeof(RoR2.CameraModes.CameraModeBase.CollectLookInputResult), "lookInput"));

            c.Index--;

            int snapTurnIndex = c.Index;

            c.Emit(OpCodes.Ldarg_3);
            c.Emit(OpCodes.Ldloc_S, (byte)6);
            c.Emit(OpCodes.Ldloc_S, (byte)7);
            c.EmitDelegate <Func <Vector2, Vector2, Vector2> >((mouseVector, joystickVector) =>
            {
                wasTurningLeft  = isTurningLeft;
                wasTurningRight = isTurningRight;

                isTurningLeft  = joystickVector.x < -0.8f;
                isTurningRight = joystickVector.x > 0.8f;

                if (!ModConfig.MotionControlsEnabled)
                {
                    isTurningLeft  = isTurningLeft || mouseVector.x < -0.8f;
                    isTurningRight = isTurningRight || mouseVector.x > 0.8f;
                }

                Vector2 result = Vector2.zero;

                if (justTurnedLeft)
                {
                    result.x = -ModConfig.SnapTurnAngle.Value;
                }
                else if (justTurnedRight)
                {
                    result.x = ModConfig.SnapTurnAngle.Value;
                }

                if ((isTurningLeft || isTurningRight) && timeSinceLastSnapTurn <= ModConfig.SnapTurnHoldDelay.Value)
                {
                    timeSinceLastSnapTurn += Time.deltaTime;
                }
                else
                {
                    timeSinceLastSnapTurn = 0;
                }

                return(result);
            });
            c.Emit(OpCodes.Stfld, typeof(RoR2.CameraModes.CameraModeBase.CollectLookInputResult).GetField("lookInput"));

            //Removing sensitivity modifications;
            var labels = il.GetIncomingLabels(c.Next);

            c.RemoveRange(24);

            foreach (var label in labels)
            {
                label.Target = c.Next;
            }

            //Adding jump after smooth turn code
            ILLabel endLabel = c.MarkLabel();

            c.Index = snapTurnIndex;

            c.Emit(OpCodes.Br_S, endLabel);

            //Adding snap turn condition and jump to snap turn
            ILLabel snapTurnLabel = c.MarkLabel();

            c.Index = startIndex;

            c.EmitDelegate <Func <bool> >(() => { return(ModConfig.SnapTurn.Value); });

            c.Emit(OpCodes.Brtrue_S, snapTurnLabel);
        }
        private void FixedUpdate_Il3(ILContext il)
        {
            BindingFlags allFlags                  = BindingFlags.Public | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
            FieldInfo    characterBody             = typeof(HuntressTracker).GetField("characterBody", allFlags);
            FieldInfo    trackingTarget            = typeof(HuntressTracker).GetField("trackingTarget", allFlags);
            FieldInfo    maxDistance               = typeof(HuntressTracker).GetField("maxTrackingDistance", allFlags);
            FieldInfo    trackerUpdateStopwatch    = typeof(HuntressTracker).GetField("trackerUpdateStopwatch", allFlags);
            FieldInfo    trackerUpdateFrequency    = typeof(HuntressTracker).GetField("trackerUpdateFrequency", allFlags);
            FieldInfo    indicator                 = typeof(HuntressTracker).GetField("indicator", allFlags);
            FieldInfo    indicator_targetTransform = typeof(RoR2.Indicator).GetField("targetTransform", allFlags);
            FieldInfo    hurtBox_group             = typeof(HurtBox).GetField(nameof(HurtBox.hurtBoxGroup), allFlags);
            FieldInfo    mainHurtBox               = typeof(HurtBoxGroup).GetField(nameof(HurtBoxGroup.mainHurtBox), allFlags);

            //MethodInfo fixedDeltaTime = typeof(Time).GetProperty( "fixedDeltaTime", allFlags ).GetGetMethod();
            MethodInfo object_implicit = typeof(UnityEngine.Object).GetMethod("op_Implicit", allFlags);


            HurtBox CastTarget(CharacterBody body, Single range)
            {
                var ray    = body.inputBank.GetAimRay();
                var myTeam = body.teamComponent.teamIndex;
                var hits   = Physics.SphereCastNonAlloc(ray, 0.75f, hitResultsBuffer, range, LayerIndex.entityPrecise.mask, QueryTriggerInteraction.UseGlobal);

                Single worldDist = Single.PositiveInfinity;

                if (hits > 0 && Physics.Raycast(ray, out RaycastHit hit, range, LayerIndex.world.mask, QueryTriggerInteraction.UseGlobal))
                {
                    worldDist = hit.distance + 1f;
                }

                HurtBox result       = null;
                Single  bestDistance = Single.PositiveInfinity;

                for (Int32 i = 0; i < hits; ++i)
                {
                    var res = hitResultsBuffer[i];

                    if (res.distance > bestDistance)
                    {
                        continue;
                    }
                    if (res.distance > worldDist)
                    {
                        continue;
                    }
                    var col = res.collider;
                    if (col is null)
                    {
                        continue;
                    }
                    var hb = col.GetComponent <HurtBox>();
                    if (hb is null)
                    {
                        continue;
                    }
                    var hc = hb.healthComponent;
                    if (hc is null)
                    {
                        continue;
                    }
                    if (hc == body.healthComponent)
                    {
                        continue;
                    }
                    if (!FriendlyFireManager.ShouldDirectHitProceed(hc, myTeam))
                    {
                        continue;
                    }

                    result       = hb;
                    bestDistance = res.distance;
                }
                return(result);
            }

            var cursor = new ILCursor(il);

            _ = cursor.Emit(OpCodes.Stfld, trackingTarget);
            _ = cursor.Emit(OpCodes.Ldarg_0);
            _ = cursor.Emit(OpCodes.Ldfld, indicator);
            _ = cursor.Emit(OpCodes.Ldarg_0);
            _ = cursor.Emit(OpCodes.Ldfld, trackingTarget);
            _ = cursor.Emit(OpCodes.Ldfld, hurtBox_group);
            _ = cursor.Emit(OpCodes.Ldfld, mainHurtBox);
            _ = cursor.Emit <Component>(OpCodes.Callvirt, "get_transform");
            _ = cursor.Emit(OpCodes.Stfld, indicator_targetTransform);
            _ = cursor.Emit(OpCodes.Ldarg_0);
            _ = cursor.Emit(OpCodes.Ldarg_0);
            _ = cursor.Emit(OpCodes.Ldfld, trackerUpdateStopwatch);
            _ = cursor.EmitDelegate <Func <Single> >(() => Time.fixedDeltaTime);
            _ = cursor.Emit(OpCodes.Add);
            _ = cursor.Emit(OpCodes.Stfld, trackerUpdateStopwatch);

            ILLabel normalPath = cursor.MarkLabel();

            _ = cursor.Emit(OpCodes.Pop);
            _ = cursor.Emit(OpCodes.Pop);
            ILLabel breakLabel = null;

            _            = cursor.GotoNext(MoveType.AfterLabel, x => x.MatchBltUn(out breakLabel));
            cursor.Index = 0;
            _            = cursor.Emit(OpCodes.Ldarg_0);
            _            = cursor.Emit(OpCodes.Ldarg_0);
            _            = cursor.Emit(OpCodes.Ldfld, characterBody);
            _            = cursor.Emit(OpCodes.Ldarg_0);
            _            = cursor.Emit(OpCodes.Ldfld, maxDistance);
            _            = cursor.EmitDelegate <Func <CharacterBody, Single, HurtBox> >(CastTarget);
            _            = cursor.Emit(OpCodes.Dup);
            _            = cursor.EmitDelegate <Func <HurtBox, Boolean> >((hb) => hb);
            _            = cursor.Emit(OpCodes.Brfalse, normalPath);
            _            = cursor.GotoLabel(normalPath, MoveType.Before);
            _            = cursor.Emit(OpCodes.Br, breakLabel);
        }
Beispiel #11
0
        private void RemoveSummonDamageBonus(ILContext il)
        {
            // FNA IL for this method seems to be different.
            if (Platform.IsWindows)
            {
                ILCursor c = new ILCursor(il);

                /* Match the base float damage of Fungal Clumps
                 * // Projectile.NewProjectile(player.Center.X, player.Center.Y, 0f, -1f, ModContent.ProjectileType<FungalClumpMinion>(), (int)(10f * player.MinionDamage()), 1f, player.whoAmI);
                 * IL_0047: ldarg.1
                 * IL_0048: callvirt instance valuetype [Microsoft.Xna.Framework]Microsoft.Xna.Framework.Vector2 [Terraria]Terraria.Entity::get_Center()
                 * IL_004d: ldfld float32 [Microsoft.Xna.Framework]Microsoft.Xna.Framework.Vector2::X
                 * IL_0052: ldarg.1
                 * IL_0053: callvirt instance valuetype [Microsoft.Xna.Framework]Microsoft.Xna.Framework.Vector2 [Terraria]Terraria.Entity::get_Center()
                 * IL_0058: ldfld float32 [Microsoft.Xna.Framework]Microsoft.Xna.Framework.Vector2::Y
                 * IL_005d: ldc.r4 0.0
                 * IL_0062: ldc.r4 -1
                 * IL_0067: call int32 [Terraria]Terraria.ModLoader.ModContent::ProjectileType<class CalamityMod.Projectiles.Summon.FungalClumpMinion>()
                 * IL_006c: ldc.r4 10
                 * IL_0071: ldarg.1
                 * IL_0072: call float32 CalamityMod.CalamityUtils::MinionDamage(class [Terraria]Terraria.Player)
                 * IL_0077: mul
                 * IL_0078: conv.i4
                 * IL_0079: ldc.r4 1
                 * IL_007e: ldarg.1
                 * IL_007f: ldfld int32 [Terraria]Terraria.Entity::whoAmI
                 * IL_0084: ldc.r4 0.0
                 * IL_0089: ldc.r4 0.0
                 * // {
                 * IL_008e: call int32 [Terraria]Terraria.Projectile::NewProjectile(float32, float32, float32, float32, int32, int32, float32, int32, float32, float32)
                 * // }
                 * IL_0093: pop
                 */
                if (!c.TryGotoNext(i => i.MatchLdcR4(10)))
                {
                    ModContent.GetInstance <CataclysmMod>().Logger.Warn("[IL] Unable to match ldc.r4 \"10\"!");
                    return;
                }

                c.Index++;

                // Replace with a regular int
                c.Emit(OpCodes.Pop);
                c.Emit(OpCodes.Ldc_I4, 10);

                c.Index++;

                /* Remove the IL code responsible for multiplying it based on summon damage
                 * IL_0071: ldarg.1
                 * IL_0072: call float32 CalamityMod.CalamityUtils::MinionDamage(class [Terraria]Terraria.Player)
                 * IL_0077: mul
                 * IL_0078: conv.i4
                 */
                c.RemoveRange(4);

                ModContent.GetInstance <CataclysmMod>().Logger.Info("[IL] Finished patching!");
            }
            else
            {
                ModContent.GetInstance <CataclysmMod>().Logger.Warn("[IL] Linux or Mac OS detected, skipping Funal Clump IL editing...");
            }
        }
Beispiel #12
0
        public void HookAdjustAngler(ILContext il)
        {
            var c = new ILCursor(il).Goto(0);

            // we force reset the angler quest if not on a server
            if (!c.TryGotoNext(i => i.MatchLdsfld(typeof(Main).GetField(nameof(Main.anglerWhoFinishedToday)))))
            {
                throw new Exception("Can't patch force reset");
            }
            if (!c.TryGotoNext(i => i.MatchCall(typeof(Lang).GetMethod(nameof(Lang.AnglerQuestChat)))))
            {
                throw new Exception("Can't patch force reset");
            }
            ILLabel label = il.DefineLabel();

            c.Emit(Brtrue_S, label);
            c.Emit(Ldloc_S, (byte)50);
            c.Index += 2;
            c.MarkLabel(label);
            if (!c.TryGotoNext(i => i.MatchCall(typeof(AchievementsHelper).GetMethod(nameof(AchievementsHelper.HandleAnglerService)))))
            {
                throw new Exception("Can't patch force reset");
            }
            c.Emit(Call, typeof(NoFishTimer).GetMethod(nameof(NoFishTimer.AnglerQuestSwap)));
            c.EmitDelegate <Action>(() => {
                if (!NoFishWorld.serverConfig.DisableAnglerTimer)
                {
                    Main.npcChatText = Lang.AnglerQuestChat(true);
                }
                else if (Main.netMode != 1)
                {
                    Main.npcChatText = Lang.AnglerQuestChat();
                }
            });

            // add reroll button
            c.Goto(0);
            if (!c.TryGotoNext(i => i.MatchLdcI4(NPCID.Angler)))
            {
                throw new Exception("Can't patch reroll button");
            }
            if (!c.TryGotoNext(i => i.MatchLdcI4(NPCID.Angler)))
            {
                throw new Exception("Can't patch reroll button");
            }
            c.Index += 2;

            label = il.DefineLabel();
            c.EmitDelegate <Func <bool> >(() => NoFishWorld.serverConfig.RerollPrice > 0);
            c.Emit(Brfalse_S, label);

            c.EmitDelegate <Func <string> >(() =>
            {
                int num13    = 0, num14 = 0, num15 = 0, num16 = 0;
                int num17    = NoFishWorld.serverConfig.RerollPrice;
                string text2 = "";

                if (num17 >= 1000000)
                {
                    num13  = num17 / 1000000;
                    num17 -= num13 * 1000000;
                }
                if (num17 >= 10000)
                {
                    num14  = num17 / 10000;
                    num17 -= num14 * 10000;
                }
                if (num17 >= 100)
                {
                    num15  = num17 / 100;
                    num17 -= num15 * 100;
                }
                if (num17 >= 1)
                {
                    num16 = num17;
                }

                if (num13 > 0)
                {
                    object obj5 = text2;
                    text2       = string.Concat(new object[]
                    {
                        obj5,
                        num13,
                        " ",
                        Language.GetText("LegacyInterface.15").Value,
                        " "
                    });
                }
                if (num14 > 0)
                {
                    object obj6 = text2;
                    text2       = string.Concat(new object[]
                    {
                        obj6,
                        num14,
                        " ",
                        Language.GetText("LegacyInterface.16").Value,
                        " "
                    });
                }
                if (num15 > 0)
                {
                    object obj7 = text2;
                    text2       = string.Concat(new object[]
                    {
                        obj7,
                        num15,
                        " ",
                        Language.GetText("LegacyInterface.17").Value,
                        " "
                    });
                }
                if (num16 > 0)
                {
                    object obj8 = text2;
                    text2       = string.Concat(new object[]
                    {
                        obj8,
                        num16,
                        " ",
                        Language.GetText("LegacyInterface.18").Value,
                        " "
                    });
                }

                text2 = text2.Substring(0, text2.Length - 1);
                return(Language.GetTextValue("Mods.NoFishTimer.Dialog.Reroll") + " (" + text2 + ")");
            });
            c.Emit(Stloc_S, (byte)10);

            c.EmitDelegate <Func <Color> >(() =>
            {
                int num13 = 0, num14 = 0, num15 = 0, num16 = 0;
                int num17 = NoFishWorld.serverConfig.RerollPrice;

                if (num17 >= 1000000)
                {
                    num13  = num17 / 1000000;
                    num17 -= num13 * 1000000;
                }
                if (num17 >= 10000)
                {
                    num14  = num17 / 10000;
                    num17 -= num14 * 10000;
                }
                if (num17 >= 100)
                {
                    num15  = num17 / 100;
                    num17 -= num15 * 100;
                }
                if (num17 >= 1)
                {
                    num16 = num17;
                }

                float num18 = Main.mouseTextColor / 255f;
                if (num13 > 0)
                {
                    return(new Color((byte)(220f * num18), (byte)(220f * num18), (byte)(198f * num18), Main.mouseTextColor));
                }
                else if (num14 > 0)
                {
                    return(new Color((byte)(224f * num18), (byte)(201f * num18), (byte)(92f * num18), Main.mouseTextColor));
                }
                else if (num15 > 0)
                {
                    return(new Color((byte)(181f * num18), (byte)(192f * num18), (byte)(193f * num18), Main.mouseTextColor));
                }
                return(new Color((byte)(246f * num18), (byte)(138f * num18), (byte)(96f * num18), Main.mouseTextColor));
            });
            c.Emit(Stloc_S, (byte)2);

            c.MarkLabel(label);
        }
Beispiel #13
0
        private void HookBeeType(ILContext il)
        {
            var c = new ILCursor(il);

            // Try to find where 566 is placed onto the stack
            if (!c.TryGotoNext(i => i.MatchLdcI4(566)))
            {
                return;                 // Patch unable to be applied
            }
            // Showcase different patch approaches
            if (implementation == 0)
            {
                // Move the cursor after 566 and onto the ret op.
                c.Index++;
                // Push the Player instance onto the stack
                c.Emit(Ldarg_0);
                // Call a delegate using the int and Player from the stack.
                c.EmitDelegate <Func <int, Player, int> >((returnValue, player) => {
                    // Regular c# code
                    if (player.GetModPlayer <ExamplePlayer>().strongBeesUpgrade&& Main.rand.NextBool(10) && Main.ProjectileUpdateLoopIndex == -1)
                    {
                        return(ProjectileID.Beenade);
                    }
                    return(returnValue);
                });
            }
            else if (implementation == 1)
            {
                // Make a label to use later
                var label = c.DefineLabel();
                // Push the Player instance onto the stack
                c.Emit(Ldarg_0);
                // Call a delegate popping the Player from the stack and pushing a bool
                c.EmitDelegate <Func <Player, bool> >(player => player.GetModPlayer <ExamplePlayer>().strongBeesUpgrade&& Main.rand.NextBool(10) && Main.ProjectileUpdateLoopIndex == -1);
                // if the bool on the stack is false, jump to label
                c.Emit(Brfalse, label);
                // Otherwise, push ProjectileID.Beenade and return
                c.Emit(Ldc_I4, ProjectileID.Beenade);
                c.Emit(Ret);
                // Set the label to the current cursor, which is still the instruction pushing 566 (which is followed by Ret)
                c.MarkLabel(label);
            }
            else
            {
                var label = c.DefineLabel();

                // Here we simply adapt the dnSpy output. This approach is tedious but easier if you don't understand IL instructions.
                c.Emit(Ldarg_0);
                // We need to make sure to pass in FieldInfo or MethodInfo into Call instructions.
                // Here we show how to retrieve a generic version of a MethodInfo
                c.Emit(Call, typeof(Player).GetMethod("GetModPlayer", new Type[] { }).MakeGenericMethod(typeof(ExamplePlayer)));
                // nameof helps avoid spelling mistakes
                c.Emit(Ldfld, typeof(ExamplePlayer).GetField(nameof(ExamplePlayer.strongBeesUpgrade)));
                c.Emit(Brfalse_S, label);
                c.Emit(Ldsfld, typeof(Main).GetField(nameof(Main.rand)));
                // Ldc_I4_S expects an int8, aka an sbyte. Failure to cast correctly will crash the game
                c.Emit(Ldc_I4_S, (sbyte)10);
                c.Emit(Call, typeof(Utils).GetMethod("NextBool", new Type[] { typeof(Terraria.Utilities.UnifiedRandom), typeof(int) }));
                c.Emit(Brfalse_S, label);
                // You may be tempted to write c.Emit(Ldsfld, Main.ProjectileUpdateLoopIndex);, this won't work and will simply use the value of the field at patch time. That will crash.
                c.Emit(Ldsfld, typeof(Main).GetField(nameof(Main.ProjectileUpdateLoopIndex)));
                c.Emit(Ldc_I4_M1);
                c.Emit(Bne_Un_S, label);
                c.Emit(Ldc_I4, ProjectileID.Beenade);
                c.Emit(Ret);
                // As Emit has been inserting and advancing the cursor index, we are still pointing at the 566 instruction.
                // All the branches in the dnspy output jumped to this instruction, so we set the label to this instruction.
                c.MarkLabel(label);
            }


            // change implementation every time the mod is reloaded
            implementation = (implementation + 1) % 3;
        }
Beispiel #14
0
#pragma warning disable IDE0051
        private static void Main_DrawPlayer(ILContext il)
        {
            ILCursor c = new ILCursor(il);

            ILHelper.CompleteLog(c, beforeEdit: true);

            /*   Edit:  Adding a specific conditional for item frames on the held item drawing
             *
             *   Main.itemTexture[type18],
             *   new Vector2((float)((int)(vector.X - Main.screenPosition.X)), (float)((int)(vector.Y - Main.screenPosition.Y))),
             *   new Rectangle?(new Rectangle(0, 0, Main.itemTexture[type18].Width, Main.itemTexture[type18].Height)),
             *      ^ Edit goes where this parameter is
             *
             *   IL_C098: sub
             *   IL_C099: conv.i4
             *   IL_C09A: conv.r4
             *   IL_C09B: newobj    instance void [Microsoft.Xna.Framework]Microsoft.Xna.Framework.Vector2::.ctor(float32, float32)
             *     === EDIT GOES HERE ===
             *   IL_C0A0: ldc.i4.0
             *   IL_C0A1: ldc.i4.0
             *   IL_C0A2: ldsfld    class [Microsoft.Xna.Framework.Graphics]Microsoft.Xna.Framework.Graphics.Texture2D[] Terraria.Main::itemTexture
             *   IL_C0A7: ldloc     V_299
             *   IL_C0AB: nop
             *   IL_C0AC: nop
             *   IL_C0AD: ldelem.ref
             *   IL_C0AE: callvirt  instance int32 [Microsoft.Xna.Framework.Graphics]Microsoft.Xna.Framework.Graphics.Texture2D::get_Width()
             *   IL_C0B3: ldsfld    class [Microsoft.Xna.Framework.Graphics]Microsoft.Xna.Framework.Graphics.Texture2D[] Terraria.Main::itemTexture
             *   IL_C0B8: ldloc     V_299
             *   IL_C0BC: nop
             *   IL_C0BD: nop
             *   IL_C0BE: ldelem.ref
             *   IL_C0BF: callvirt  instance int32 [Microsoft.Xna.Framework.Graphics]Microsoft.Xna.Framework.Graphics.Texture2D::get_Height()
             *   IL_C0C4: newobj    instance void [Microsoft.Xna.Framework]Microsoft.Xna.Framework.Rectangle::.ctor(int32, int32, int32, int32)
             *   IL_C0C9: newobj    instance void valuetype [mscorlib]System.Nullable`1<valuetype [Microsoft.Xna.Framework]Microsoft.Xna.Framework.Rectangle>::.ctor(!0)
             *   IL_C0CE: ldarg.1
             *     === BRANCH TO ABOVE STATEMENT IS ADDED ===
             */

            if (!c.TryGotoNext(MoveType.Before, i => i.MatchNewobj(ILHelper.Vector2_ctor_float_float),
                               i => i.MatchLdcI4(0),
                               i => i.MatchLdcI4(0),
                               i => i.MatchLdsfld(ILHelper.Main_itemTexture),
                               i => i.MatchLdloc(299),
                               i => i.MatchNop(),
                               i => i.MatchNop(),
                               i => i.MatchLdelemRef(),
                               i => i.MatchCallvirt(ILHelper.Texture2D_get_Width),
                               i => i.MatchLdsfld(ILHelper.Main_itemTexture),
                               i => i.MatchLdloc(299),
                               i => i.MatchNop(),
                               i => i.MatchNop(),
                               i => i.MatchLdelemRef(),
                               i => i.MatchCallvirt(ILHelper.Texture2D_get_Height),
                               i => i.MatchNewobj(ILHelper.Rectangle_ctor_int_int_int_int),
                               i => i.MatchNewobj(ILHelper.Nullable_Rectangle_ctor_Rectangle)))
            {
                goto bad_il;
            }

            c.Index++;
            //Put a branch target here for the "false" case
            ILLabel origCode = c.MarkLabel();

            //Go to after the nullable ctor, add a branch target there, then go back
            ILLabel atNullableRectangle_ctor = c.DefineLabel();

            c.Index += 15;
            c.MarkLabel(atNullableRectangle_ctor);
            c.Index -= 15;
            //Emit the things
            c.Emit(OpCodes.Ldloc, 299);
            c.EmitDelegate <Func <int, bool> >(itemType => itemType == ModContent.ItemType <TransportJunctionItem>());
            c.Emit(OpCodes.Brfalse, origCode);
            c.Emit(OpCodes.Ldarg_1);
            c.EmitDelegate <Func <Player, Rectangle> >(player => new Rectangle(player.HeldItem.placeStyle * 18, 0, 18, 18));
            c.Emit(OpCodes.Br, atNullableRectangle_ctor);

            ILHelper.UpdateInstructionOffsets(c);

            ILHelper.CompleteLog(c, beforeEdit: false);

            return;

bad_il:
            TechMod.Instance.Logger.Error("Could not fully edit method \"Terraria.Main.DrawPlayer(Player, Vector2, float, Vector2, [float])\"");
        }
        private void ProjectileAssignTarget(ILContext il)
        {
            var  c = new ILCursor(il);
            bool ILFound;

            ILFound = c.TryGotoNext(
                x => x.MatchLdloc(2),
                x => x.MatchCallOrCallvirt <UnityEngine.Object>("op_Implicit"));
            if (!ILFound)
            {
                ilFailed = true;
                KevinsAdditionsPlugin._logger.LogError("Failed to apply Seeking Stone IL patch (ProjectileAssignTarget), item will not work; target instructions not found");
                return;
            }

            c.Emit(OpCodes.Ldloc_0);
            c.Emit(OpCodes.Ldarg_1);
            c.Emit(OpCodes.Ldloc, 4);
            c.Emit(OpCodes.Ldloc, 5);
            c.EmitDelegate <Action <GameObject, FireProjectileInfo, ProjectileTargetComponent, ProjectileSimple> >((gameObject, fireProjectileInfo, targetComponent, projectileSimple) =>
            {
                var cpt = fireProjectileInfo.owner.GetComponent <SeekingStoneComponent>();
                if (!cpt || !projectileSimple)
                {
                    return;
                }
                if (fireProjectileInfo.target == null && cpt.cachedIcnt > 0 && cpt.target)
                {
                    if (!targetComponent)
                    {
                        targetComponent = gameObject.AddComponent <ProjectileTargetComponent>();
                    }
                    if (gameObject.GetComponent <ProjectileDirectionalTargetFinder>())
                    {
                        UnityEngine.Object.Destroy(gameObject.GetComponent <ProjectileDirectionalTargetFinder>());
                    }
                    if (gameObject.GetComponent <ProjectileSphereTargetFinder>())
                    {
                        UnityEngine.Object.Destroy(gameObject.GetComponent <ProjectileSphereTargetFinder>());
                    }
                    //This sets the gameObject's forward to be the aim direction so it's easier to work with

                    /*gameObject.transform.rotation = Util.QuaternionSafeLookRotation(cpt.body.GetComponent<InputBankTest>().aimDirection);
                     *
                     * gameObject.GetComponent<Rigidbody>().rotation = gameObject.transform.rotation;
                     * if(gameObject.GetComponent<Rigidbody>().angularVelocity != Vector3.zero)
                     * {
                     *  KevinsAdditionsPlugin._logger.LogError("Angular Velocity is not zero. Setting to zero");
                     *  gameObject.GetComponent<Rigidbody>().angularVelocity = Vector3.zero;
                     * }*/
                    projectileSimple.updateAfterFiring       = true;
                    MaintainTarget projectileFinderComponent = gameObject.AddComponent <MaintainTarget>();

                    var steerComponent = gameObject.GetComponent <ProjectileSteerTowardTarget>();
                    if (!steerComponent)
                    {
                        steerComponent = gameObject.AddComponent <ProjectileSteerTowardTarget>();
                    }
                    if (gameObject.GetComponent <Rigidbody>().useGravity)
                    {
                        steerComponent.yAxisOnly = true;
                        projectileFinderComponent.isAffectedByGravity = true;
                    }
                    steerComponent.rotationSpeed = 80f - 80f / (1f + 0.2f * cpt.cachedIcnt);
                    steerComponent.transform     = gameObject.transform;
                }
            });
        }
Beispiel #16
0
 public IEnumerable <Instruction> ProvideValue(VariableDefinitionReference vardefref, ModuleDefinition module, BaseNode node, ILContext context)
 {
     yield break;
 }
 private void CutsceneWarpTargetMirror(ILContext il)
 {
     this.CutsceneWarpTarget(il);
     this.CutsceneWarpMirrorFakeBSide(il);
 }
        public IEnumerable <Instruction> ProvideValue(VariableDefinitionReference vardefref, ModuleDefinition module, BaseNode node, ILContext context)
        {
            INode sourceNode = null;

            ((IElementNode)node).Properties.TryGetValue(new XmlName("", "Source"), out sourceNode);
            if (sourceNode == null)
            {
                ((IElementNode)node).Properties.TryGetValue(new XmlName(XamlParser.XFUri, "Source"), out sourceNode);
            }

            INode styleNode = null;

            if (!((IElementNode)node).Properties.TryGetValue(new XmlName("", "Style"), out styleNode) &&
                !((IElementNode)node).Properties.TryGetValue(new XmlName(XamlParser.XFUri, "Style"), out styleNode) &&
                ((IElementNode)node).CollectionItems.Count == 1)
            {
                styleNode = ((IElementNode)node).CollectionItems[0];
            }

            if (sourceNode != null && styleNode != null)
            {
                throw new XamlParseException($"StyleSheet can not have both a Source and a content", node);
            }

            if (sourceNode == null && styleNode == null)
            {
                throw new XamlParseException($"StyleSheet require either a Source or a content", node);
            }

            if (styleNode != null && !(styleNode is ValueNode))
            {
                throw new XamlParseException($"Style property or Content is not a string literal", node);
            }

            if (sourceNode != null && !(sourceNode is ValueNode))
            {
                throw new XamlParseException($"Source property is not a string literal", node);
            }

            if (styleNode != null)
            {
                var style = (styleNode as ValueNode).Value as string;
                yield return(Create(Ldstr, style));

                var fromString = module.ImportReference(typeof(StyleSheets.StyleSheet).GetMethods().FirstOrDefault(mi => mi.Name == nameof(StyleSheets.StyleSheet.FromString) && mi.GetParameters().Length == 1));
                yield return(Create(Call, module.ImportReference(fromString)));
            }
            else
            {
                string source   = (sourceNode as ValueNode)?.Value as string;
                INode  rootNode = node;
                while (!(rootNode is ILRootNode))
                {
                    rootNode = rootNode.Parent;
                }

                var rootTargetPath = RDSourceTypeConverter.GetPathForType(module, ((ILRootNode)rootNode).TypeReference);
                var uri            = new Uri(source, UriKind.Relative);

                var resourcePath = ResourceDictionary.RDSourceTypeConverter.GetResourcePath(uri, rootTargetPath);
                //fail early
                var resourceId = XamlCTask.GetResourceIdForPath(module, resourcePath);
                if (resourceId == null)
                {
                    throw new XamlParseException($"Resource '{source}' not found.", node);
                }

                var getTypeFromHandle = module.ImportReference(typeof(Type).GetMethod(nameof(Type.GetTypeFromHandle), new[] { typeof(RuntimeTypeHandle) }));
                var getAssembly       = module.ImportReference(typeof(Type).GetProperty(nameof(Type.Assembly)).GetGetMethod());
                yield return(Create(Ldtoken, module.ImportReference(((ILRootNode)rootNode).TypeReference)));

                yield return(Create(Call, module.ImportReference(getTypeFromHandle)));

                yield return(Create(Callvirt, module.ImportReference(getAssembly))); //assembly

                yield return(Create(Ldstr, resourceId));                             //resourceId

                foreach (var instruction in node.PushXmlLineInfo(context))
                {
                    yield return(instruction);                    //lineinfo
                }
                var fromAssemblyResource = module.ImportReference(typeof(StyleSheets.StyleSheet).GetMethods().FirstOrDefault(mi => mi.Name == nameof(StyleSheets.StyleSheet.FromAssemblyResource) && mi.GetParameters().Length == 3));
                yield return(Create(Call, module.ImportReference(fromAssemblyResource)));
            }

            //the variable is of type `object`. fix that
            var vardef = new VariableDefinition(module.ImportReference(typeof(StyleSheets.StyleSheet)));

            yield return(Create(Stloc, vardef));

            vardefref.VariableDefinition = vardef;
        }
Beispiel #19
0
		public static IEnumerable<Instruction> PushServiceProvider(this INode node, ILContext context)
		{
			var module = context.Body.Method.Module;

#if NOSERVICEPROVIDER
			yield return Instruction.Create (OpCodes.Ldnull);
			yield break;
			#endif

			var ctorinfo = typeof (XamlServiceProvider).GetConstructor(new Type[] { });
			var ctor = module.Import(ctorinfo);

			var addServiceInfo = typeof (XamlServiceProvider).GetMethod("Add", new[] { typeof (Type), typeof (object) });
			var addService = module.Import(addServiceInfo);

			var getTypeFromHandle =
				module.Import(typeof (Type).GetMethod("GetTypeFromHandle", new[] { typeof (RuntimeTypeHandle) }));
			var getAssembly = module.Import(typeof (Type).GetProperty("Assembly").GetMethod);

			yield return Instruction.Create(OpCodes.Newobj, ctor);

			//Add a SimpleValueTargetProvider
			var pushParentIl = node.PushParentObjectsArray(context).ToList();
			if (pushParentIl[pushParentIl.Count - 1].OpCode != OpCodes.Ldnull)
			{
				yield return Instruction.Create(OpCodes.Dup); //Keep the serviceProvider on the stack
				yield return Instruction.Create(OpCodes.Ldtoken, module.Import(typeof (IProvideValueTarget)));
				yield return Instruction.Create(OpCodes.Call, module.Import(getTypeFromHandle));

				foreach (var instruction in pushParentIl)
					yield return instruction;

				var targetProviderCtor =
					module.Import(typeof (SimpleValueTargetProvider).GetConstructor(new[] { typeof (object[]) }));
				yield return Instruction.Create(OpCodes.Newobj, targetProviderCtor);
				yield return Instruction.Create(OpCodes.Callvirt, addService);
			}

			//Add a NamescopeProvider
			if (context.Scopes.ContainsKey(node))
			{
				yield return Instruction.Create(OpCodes.Dup); //Dupicate the serviceProvider
				yield return Instruction.Create(OpCodes.Ldtoken, module.Import(typeof (INameScopeProvider)));
				yield return Instruction.Create(OpCodes.Call, module.Import(getTypeFromHandle));
				var namescopeProviderCtor = module.Import(typeof (NameScopeProvider).GetConstructor(new Type[] { }));
				yield return Instruction.Create(OpCodes.Newobj, namescopeProviderCtor);
				yield return Instruction.Create(OpCodes.Dup); //Duplicate the namescopeProvider
				var setNamescope = module.Import(typeof (NameScopeProvider).GetProperty("NameScope").GetSetMethod());

				yield return Instruction.Create(OpCodes.Ldloc, context.Scopes[node]);
				yield return Instruction.Create(OpCodes.Callvirt, setNamescope);
				yield return Instruction.Create(OpCodes.Callvirt, addService);
			}

			//Add a XamlTypeResolver
			if (node.NamespaceResolver != null)
			{
				yield return Instruction.Create(OpCodes.Dup); //Dupicate the serviceProvider
				yield return Instruction.Create(OpCodes.Ldtoken, module.Import(typeof (IXamlTypeResolver)));
				yield return Instruction.Create(OpCodes.Call, module.Import(getTypeFromHandle));
				var xmlNamespaceResolverCtor = module.Import(typeof (XmlNamespaceResolver).GetConstructor(new Type[] { }));
				var addNamespace = module.Import(typeof (XmlNamespaceResolver).GetMethod("Add"));
				yield return Instruction.Create(OpCodes.Newobj, xmlNamespaceResolverCtor);
				foreach (var kvp in node.NamespaceResolver.GetNamespacesInScope(XmlNamespaceScope.ExcludeXml))
				{
					yield return Instruction.Create(OpCodes.Dup); //dup the resolver
					yield return Instruction.Create(OpCodes.Ldstr, kvp.Key);
					yield return Instruction.Create(OpCodes.Ldstr, kvp.Value);
					yield return Instruction.Create(OpCodes.Callvirt, addNamespace);
				}
				yield return Instruction.Create(OpCodes.Ldtoken, context.Body.Method.DeclaringType);
				yield return Instruction.Create(OpCodes.Call, module.Import(getTypeFromHandle));
				yield return Instruction.Create(OpCodes.Callvirt, getAssembly);
				var xtr = module.Import(typeof (XamlTypeResolver)).Resolve();
				var xamlTypeResolverCtor = module.Import(xtr.Methods.First(md => md.IsConstructor && md.Parameters.Count == 2));
				yield return Instruction.Create(OpCodes.Newobj, xamlTypeResolverCtor);
				yield return Instruction.Create(OpCodes.Callvirt, addService);
			}

			if (node is IXmlLineInfo)
			{
				yield return Instruction.Create(OpCodes.Dup); //Dupicate the serviceProvider
				yield return Instruction.Create(OpCodes.Ldtoken, module.Import(typeof (IXmlLineInfoProvider)));
				yield return Instruction.Create(OpCodes.Call, module.Import(getTypeFromHandle));

				foreach (var instruction in node.PushXmlLineInfo(context))
					yield return instruction;

				var lip = module.Import(typeof (XmlLineInfoProvider)).Resolve();
				var lineInfoProviderCtor = module.Import(lip.Methods.First(md => md.IsConstructor && md.Parameters.Count == 1));
				yield return Instruction.Create(OpCodes.Newobj, lineInfoProviderCtor);
				yield return Instruction.Create(OpCodes.Callvirt, addService);
			}
		}
Beispiel #20
0
        public IEnumerable <Instruction> ProvideValue(VariableDefinitionReference vardefref, ModuleDefinition module, BaseNode node, ILContext context)
        {
            INode sourceNode = null;

            ((IElementNode)node).Properties.TryGetValue(new XmlName("", "Source"), out sourceNode);
            if (sourceNode == null)
            {
                ((IElementNode)node).Properties.TryGetValue(new XmlName(XamlParser.XFUri, "Source"), out sourceNode);
            }

            INode styleNode = null;

            if (!((IElementNode)node).Properties.TryGetValue(new XmlName("", "Style"), out styleNode) &&
                !((IElementNode)node).Properties.TryGetValue(new XmlName(XamlParser.XFUri, "Style"), out styleNode) &&
                ((IElementNode)node).CollectionItems.Count == 1)
            {
                styleNode = ((IElementNode)node).CollectionItems[0];
            }

            if (sourceNode != null && styleNode != null)
            {
                throw new BuildException(BuildExceptionCode.StyleSheetSourceOrContent, node, null);
            }

            if (sourceNode == null && styleNode == null)
            {
                throw new BuildException(BuildExceptionCode.StyleSheetNoSourceOrContent, node, null);
            }

            if (styleNode != null && !(styleNode is ValueNode))
            {
                throw new BuildException(BuildExceptionCode.StyleSheetStyleNotALiteral, node, null);
            }

            if (sourceNode != null && !(sourceNode is ValueNode))
            {
                throw new BuildException(BuildExceptionCode.StyleSheetSourceNotALiteral, node, null);
            }

            if (styleNode != null)
            {
                var style = (styleNode as ValueNode).Value as string;
                yield return(Create(Ldstr, style));

                yield return(Create(Call, module.ImportMethodReference(("Xamarin.Forms.Core", "Xamarin.Forms.StyleSheets", "StyleSheet"),
                                                                       methodName: "FromString",
                                                                       parameterTypes: new[] { ("mscorlib", "System", "String") },
Beispiel #21
0
        /// <summary>
        /// Change the following code sequence in Wiring.HitWireSingle
        /// num12 = Utils.SelectRandom(Main.rand, new short[5]
        /// {
        ///     359,
        ///     359,
        ///     359,
        ///     359,
        ///     360,
        /// });
        ///
        /// to
        ///
        /// var arr = new short[5]
        /// {
        ///     359,
        ///     359,
        ///     359,
        ///     359,
        ///     360,
        /// }
        /// arr = arr.ToList().Add(id).ToArray();
        /// num12 = Utils.SelectRandom(Main.rand, arr);
        ///
        /// </summary>
        /// <param name="il"></param>
        private void HookStatue(ILContext il)
        {
            // obtain a cursor positioned before the first instruction of the method
            // the cursor is used for navigating and modifying the il
            var c = new ILCursor(il);

            // the exact location for this hook is very complex to search for due to the hook instructions not being unique, and buried deep in control flow
            // switch statements are sometimes compiled to if-else chains, and debug builds litter the code with no-ops and redundant locals

            // in general you want to search using structure and function rather than numerical constants which may change across different versions or compile settings
            // using local variable indices is almost always a bad idea

            // we can search for
            // switch (*)
            //   case 56:
            //     Utils.SelectRandom *

            // in general you'd want to look for a specific switch variable, or perhaps the containing switch (type) { case 105:
            // but the generated IL is really variable and hard to match in this case

            // we'll just use the fact that there are no other switch statements with case 56, followed by a SelectRandom

            ILLabel[] targets = null;
            while (c.TryGotoNext(i => i.MatchSwitch(out targets)))
            {
                // some optimising compilers generate a sub so that all the switch cases start at 0
                // ldc.i4.s 51
                // sub
                // switch
                int offset = 0;
                if (c.Prev.MatchSub() && c.Prev.Previous.MatchLdcI4(out offset))
                {
                    ;
                }

                // get the label for case 56: if it exists
                int case56Index = 56 - offset;
                if (case56Index < 0 || case56Index >= targets.Length || !(targets[case56Index] is ILLabel target))
                {
                    continue;
                }

                // move the cursor to case 56:
                c.GotoLabel(target);
                // there's lots of extra checks we could add here to make sure we're at the right spot, such as not encountering any branching instructions
                c.GotoNext(i => i.MatchCall(typeof(Utils), nameof(Utils.SelectRandom)));

                // goto next positions us before the instruction we searched for, so we can insert our array modifying code right here
                c.EmitDelegate <Func <short[], short[]> >(arr =>
                {
                    // resize the array and add our custom snail
                    Array.Resize(ref arr, arr.Length + 1);
                    arr[arr.Length - 1] = (short)npc.type;
                    return(arr);
                });

                // hook applied successfully
                return;
            }

            // couldn't find the right place to insert
            throw new Exception("Hook location not found, switch(*) { case 56: ...");
        }
        public IEnumerable <Instruction> ConvertFromString(string value, ILContext context, BaseNode node)
        {
            var module = context.Body.Method.Module;
            var body   = context.Body;

            INode rootNode = node;

            while (!(rootNode is ILRootNode))
            {
                rootNode = rootNode.Parent;
            }

            var rdNode = node.Parent as IElementNode;

            var rootTargetPath = GetPathForType(module, ((ILRootNode)rootNode).TypeReference);
            var uri            = new Uri(value, UriKind.Relative);

            var resourceId = ResourceDictionary.RDSourceTypeConverter.GetResourceId(uri, rootTargetPath, s => GetResourceIdForPath(module, s));

            if (resourceId == null)
            {
                throw new XamlParseException($"Resource '{value}' not found.", node);
            }
            //abuse the converter, produce some side effect, but leave the stack untouched
            //public void SetAndLoadSource(Uri value, string resourceID, Assembly assembly, System.Xml.IXmlLineInfo lineInfo)
            yield return(Create(Ldloc, context.Variables[rdNode]));            //the resourcedictionary

            foreach (var instruction in (new UriTypeConverter()).ConvertFromString(value, context, node))
            {
                yield return(instruction);                //the Uri
            }
            //keep the Uri for later
            yield return(Create(Dup));

            var uriVarDef = new VariableDefinition(module.ImportReference(typeof(Uri)));

            body.Variables.Add(uriVarDef);
            yield return(Create(Stloc, uriVarDef));

            yield return(Create(Ldstr, resourceId));            //resourceId

            var getTypeFromHandle = module.ImportReference(typeof(Type).GetMethod("GetTypeFromHandle", new[] { typeof(RuntimeTypeHandle) }));
            var getAssembly       = module.ImportReference(typeof(Type).GetProperty("Assembly").GetGetMethod());

            yield return(Create(Ldtoken, module.ImportReference(((ILRootNode)rootNode).TypeReference)));

            yield return(Create(Call, module.ImportReference(getTypeFromHandle)));

            yield return(Create(Callvirt, module.ImportReference(getAssembly)));            //assembly

            foreach (var instruction in node.PushXmlLineInfo(context))
            {
                yield return(instruction);                //lineinfo
            }
            var setAndLoadSource = module.ImportReference(typeof(ResourceDictionary).GetMethod("SetAndLoadSource"));

            yield return(Create(Callvirt, module.ImportReference(setAndLoadSource)));

            //ldloc the stored uri as return value
            yield return(Create(Ldloc, uriVarDef));
        }
Beispiel #23
0
        private static void filterOutJumpThrusFromCollideChecks(ILContext il)
        {
            ILCursor cursor = new ILCursor(il);

            while (cursor.Next != null)
            {
                Instruction nextInstruction = cursor.Next;
                if (nextInstruction.OpCode == OpCodes.Call || nextInstruction.OpCode == OpCodes.Callvirt)
                {
                    switch ((nextInstruction.Operand as MethodReference)?.FullName ?? "")
                    {
                    case "T Monocle.Entity::CollideFirstOutside<Celeste.JumpThru>(Microsoft.Xna.Framework.Vector2)":
                        Logger.Log("SpringCollab2020/UpsideDownJumpThru", $"Patching CollideFirstOutside at {cursor.Index} in IL for {il.Method.Name}");
                        cursor.Index++;

                        // nullify if mod jumpthru.
                        cursor.EmitDelegate <Func <JumpThru, JumpThru> >(jumpThru => {
                            if (jumpThru?.GetType() == typeof(UpsideDownJumpThru))
                            {
                                return(null);
                            }
                            return(jumpThru);
                        });
                        break;

                    case "System.Boolean Monocle.Entity::CollideCheckOutside<Celeste.JumpThru>(Microsoft.Xna.Framework.Vector2)":
                        Logger.Log("SpringCollab2020/UpsideDownJumpThru", $"Patching CollideCheckOutside at {cursor.Index} in IL for {il.Method.Name}");

                        cursor.Remove();

                        // check if colliding with a jumpthru but not an upside-down jumpthru.
                        cursor.EmitDelegate <Func <Entity, Vector2, bool> >((self, at) => self.CollideCheckOutside <JumpThru>(at) && !self.CollideCheckOutside <UpsideDownJumpThru>(at));
                        break;

                    case "System.Boolean Monocle.Entity::CollideCheck<Celeste.JumpThru>()":
                        Logger.Log("SpringCollab2020/UpsideDownJumpThru", $"Patching CollideCheck at {cursor.Index} in IL for {il.Method.Name}");

                        // we want stack to be: CollideCheck result, this
                        cursor.Index++;
                        cursor.Emit(OpCodes.Ldarg_0);

                        // turn check to false if colliding with an upside-down jumpthru.
                        cursor.EmitDelegate <Func <bool, Player, bool> >((vanillaCheck, self) => vanillaCheck && !self.CollideCheck <UpsideDownJumpThru>());
                        break;

                    case "System.Collections.Generic.List`1<Monocle.Entity> Monocle.Tracker::GetEntities<Celeste.JumpThru>()":
                        Logger.Log("SpringCollab2020/UpsideDownJumpThru", $"Patching GetEntities at {cursor.Index} in IL for {il.Method.Name}");

                        cursor.Index++;

                        // remove all mod jumpthrus from the returned list.
                        cursor.EmitDelegate <Func <List <Entity>, List <Entity> > >(matches => {
                            for (int i = 0; i < matches.Count; i++)
                            {
                                if (matches[i].GetType() == typeof(UpsideDownJumpThru))
                                {
                                    matches.RemoveAt(i);
                                    i--;
                                }
                            }
                            return(matches);
                        });
                        break;
                    }
                }

                cursor.Index++;
            }
        }
Beispiel #24
0
        public IEnumerable <Instruction> ProvideValue(VariableDefinitionReference vardefref, ModuleDefinition module, BaseNode node, ILContext context)
        {
            INode valueNode = null;

            if (!((IElementNode)node).Properties.TryGetValue(new XmlName("", "Value"), out valueNode) &&
                !((IElementNode)node).Properties.TryGetValue(new XmlName(XamlParser.XFUri, "Value"), out valueNode) &&
                ((IElementNode)node).CollectionItems.Count == 1)
            {
                valueNode = ((IElementNode)node).CollectionItems[0];
            }

            var bpNode = ((ValueNode)((IElementNode)node).Properties[new XmlName("", "Property")]);
            var bpRef  = (new BindablePropertyConverter()).GetBindablePropertyFieldReference((string)bpNode.Value, module, bpNode);

            if (SetterValueIsCollection(bpRef, module, node, context))
            {
                yield break;
            }

            if (valueNode == null)
            {
                throw new XamlParseException("Missing Value for Setter", (IXmlLineInfo)node);
            }

            //if it's an elementNode, there's probably no need to convert it
            if (valueNode is IElementNode)
            {
                yield break;
            }

            var value      = ((string)((ValueNode)valueNode).Value);
            var setterType = ("Microsoft.Maui.Controls", "Microsoft.Maui.Controls", "Setter");

            //push the setter
            foreach (var instruction in vardefref.VariableDefinition.LoadAs(module.GetTypeDefinition(setterType), module))
            {
                yield return(instruction);
            }

            //push the value
            foreach (var instruction in ((ValueNode)valueNode).PushConvertedValue(context, bpRef, valueNode.PushServiceProvider(context, bpRef: bpRef), boxValueTypes: true, unboxValueTypes: false))
            {
                yield return(instruction);
            }

            //set the value
            yield return(Instruction.Create(OpCodes.Callvirt, module.ImportPropertySetterReference(setterType, propertyName: "Value")));
        }
Beispiel #25
0
        private static void OverrideAffixBlueDamage(ILContext il)
        {
            ILCursor c = new ILCursor(il);

            int bodyIndex = -1;

            bool found = c.TryGotoNext(
                x => x.MatchLdarg(2),
                x => x.MatchLdfld <DamageInfo>("attacker"),
                x => x.MatchCallOrCallvirt <GameObject>("GetComponent"),
                x => x.MatchStloc(out bodyIndex)
                );

            if (bodyIndex != -1)
            {
                found = c.TryGotoNext(
                    x => x.MatchLdsfld(AffixBlueDamageCoeffField)
                    );

                if (found)
                {
                    affixBlueDamageHook++;

                    Logger.Info("[EliteReworksCompat] - AffixBlueDamageCoeffField Index : " + c.Index);

                    c.Index += 1;

                    c.Emit(OpCodes.Ldloc, bodyIndex);
                    c.EmitDelegate <Func <float, CharacterBody, float> >((mult, body) =>
                    {
                        if (Configuration.AspectBlueEliteReworksDamage.Value && Configuration.AspectBlueBaseDamage.Value > 0f)
                        {
                            float count = Catalog.GetStackMagnitude(body, RoR2Content.Buffs.AffixBlue);

                            mult = Configuration.AspectBlueBaseDamage.Value + Configuration.AspectBlueStackDamage.Value * (count - 1f);
                            if (body.teamComponent.teamIndex != TeamIndex.Player)
                            {
                                mult *= Configuration.AspectBlueMonsterDamageMult.Value;
                            }
                        }

                        return(mult);
                    });
                }
                else
                {
                    Logger.Warn("[EliteReworksCompat] - OverrideAffixBlueDamage - Failed to find AffixBlueDamageCoefficientField");
                }

                found = c.TryGotoNext(
                    x => x.MatchLdsfld(AffixBlueDamageCoeffField)
                    );

                if (found)
                {
                    affixBlueDamageHook++;

                    Logger.Info("[EliteReworksCompat] - AffixBlueDamageCoeffField Index : " + c.Index);

                    c.Index += 1;

                    c.Emit(OpCodes.Ldloc, bodyIndex);
                    c.EmitDelegate <Func <float, CharacterBody, float> >((mult, body) =>
                    {
                        if (Configuration.AspectBlueEliteReworksDamage.Value && Configuration.AspectBlueBaseDamage.Value > 0f)
                        {
                            float count = Catalog.GetStackMagnitude(body, RoR2Content.Buffs.AffixBlue);

                            mult = Configuration.AspectBlueBaseDamage.Value + Configuration.AspectBlueStackDamage.Value * (count - 1f);
                            if (body.teamComponent.teamIndex != TeamIndex.Player)
                            {
                                mult *= Configuration.AspectBlueMonsterDamageMult.Value;
                            }
                        }

                        return(mult);
                    });
                }
                else
                {
                    Logger.Warn("[EliteReworksCompat] - OverrideAffixBlueDamage - Failed to find AffixBlueDamageCoefficientField");
                }
            }
            else
            {
                Logger.Warn("[EliteReworksCompat] - OverrideAffixBlueDamage - Failed to find CharacterBody Local Variable Index");
            }
        }
Beispiel #26
0
        static bool SetterValueIsCollection(FieldReference bindablePropertyReference, ModuleDefinition module, BaseNode node, ILContext context)
        {
            var items = (node as IElementNode)?.CollectionItems;

            if (items == null || items.Count <= 0)
            {
                return(false);
            }

            // Is this a generic type ?
            var generic = bindablePropertyReference.GetBindablePropertyType(node, module) as GenericInstanceType;

            // With a single generic argument?
            if (generic?.GenericArguments.Count != 1)
            {
                return(false);
            }

            // Is the generic argument assignable from this value?
            var genericType = generic.GenericArguments[0];

            if (!(items[0] is IElementNode firstItem))
            {
                return(false);
            }

            return(context.Variables[firstItem].VariableType.InheritsFromOrImplements(genericType));
        }
Beispiel #27
0
		public bool Compile()
		{
			LogLine(1, "Compiling Xaml");
			LogLine(1, "\nAssembly: {0}", Assembly);
			if (!string.IsNullOrEmpty(DependencyPaths))
				LogLine(1, "DependencyPaths: \t{0}", DependencyPaths);
			if (!string.IsNullOrEmpty(ReferencePath))
				LogLine(1, "ReferencePath: \t{0}", ReferencePath.Replace("//", "/"));
			LogLine(3, "DebugSymbols:\"{0}\"", DebugSymbols);
			var skipassembly = true; //change this to false to enable XamlC by default
			bool success = true;

			if (!File.Exists(Assembly))
			{
				LogLine(1, "Assembly file not found. Skipping XamlC.");
				return true;
			}

			var resolver = new XamlCAssemblyResolver();
			if (!string.IsNullOrEmpty(DependencyPaths))
			{
				foreach (var dep in DependencyPaths.Split(';'))
				{
					LogLine(3, "Adding searchpath {0}", dep);
					resolver.AddSearchDirectory(dep);
				}
			}

			if (!string.IsNullOrEmpty(ReferencePath))
			{
				var paths = ReferencePath.Replace("//", "/").Split(';');
				foreach (var p in paths)
				{
					var searchpath = Path.GetDirectoryName(p);
					LogLine(3, "Adding searchpath {0}", searchpath);
					resolver.AddSearchDirectory(searchpath);
					//					LogLine (3, "Referencing {0}", p);
					//					resolver.AddAssembly (p);
				}
			}

			var assemblyDefinition = AssemblyDefinition.ReadAssembly(Path.GetFullPath(Assembly), new ReaderParameters
			{
				AssemblyResolver = resolver,
				ReadSymbols = DebugSymbols
			});

			CustomAttribute xamlcAttr;
			if (assemblyDefinition.HasCustomAttributes &&
			    (xamlcAttr =
				    assemblyDefinition.CustomAttributes.FirstOrDefault(
					    ca => ca.AttributeType.FullName == "Xamarin.Forms.Xaml.XamlCompilationAttribute")) != null)
			{
				var options = (XamlCompilationOptions)xamlcAttr.ConstructorArguments[0].Value;
				if ((options & XamlCompilationOptions.Skip) == XamlCompilationOptions.Skip)
					skipassembly = true;
				if ((options & XamlCompilationOptions.Compile) == XamlCompilationOptions.Compile)
					skipassembly = false;
			}

			foreach (var module in assemblyDefinition.Modules)
			{
				var skipmodule = skipassembly;
				if (module.HasCustomAttributes &&
				    (xamlcAttr =
					    module.CustomAttributes.FirstOrDefault(
						    ca => ca.AttributeType.FullName == "Xamarin.Forms.Xaml.XamlCompilationAttribute")) != null)
				{
					var options = (XamlCompilationOptions)xamlcAttr.ConstructorArguments[0].Value;
					if ((options & XamlCompilationOptions.Skip) == XamlCompilationOptions.Skip)
						skipmodule = true;
					if ((options & XamlCompilationOptions.Compile) == XamlCompilationOptions.Compile)
						skipmodule = false;
				}

				LogLine(2, " Module: {0}", module.Name);
				var resourcesToPrune = new List<EmbeddedResource>();
				foreach (var resource in module.Resources.OfType<EmbeddedResource>())
				{
					Log(2, "  Resource: {0}... ", resource.Name);
					string classname;
					if (!resource.IsXaml(out classname))
					{
						LogLine(2, "skipped.");
						continue;
					}
					TypeDefinition typeDef = module.GetType(classname);
					if (typeDef == null)
					{
						LogLine(2, "no type found... skipped.");
						continue;
					}
					var skiptype = skipmodule;
					if (typeDef.HasCustomAttributes &&
					    (xamlcAttr =
						    typeDef.CustomAttributes.FirstOrDefault(
							    ca => ca.AttributeType.FullName == "Xamarin.Forms.Xaml.XamlCompilationAttribute")) != null)
					{
						var options = (XamlCompilationOptions)xamlcAttr.ConstructorArguments[0].Value;
						if ((options & XamlCompilationOptions.Skip) == XamlCompilationOptions.Skip)
							skiptype = true;
						if ((options & XamlCompilationOptions.Compile) == XamlCompilationOptions.Compile)
							skiptype = false;
					}
					if (skiptype)
					{
						LogLine(2, "Has XamlCompilationAttribute set to Skip and not Compile... skipped");
						continue;
					}

					var initComp = typeDef.Methods.FirstOrDefault(md => md.Name == "InitializeComponent");
					if (initComp == null)
					{
						LogLine(2, "no InitializeComponent found... skipped.");
						continue;
					}
					LogLine(2, "");

					Log(2, "   Parsing Xaml... ");
					var rootnode = ParseXaml(resource.GetResourceStream(), typeDef);
					if (rootnode == null)
					{
						LogLine(2, "failed.");
						continue;
					}
					LogLine(2, "done.");

					hasCompiledXamlResources = true;

					try
					{
						Log(2, "   Replacing {0}.InitializeComponent ()... ", typeDef.Name);
						var body = new MethodBody(initComp);
						var il = body.GetILProcessor();
						il.Emit(OpCodes.Nop);
						var visitorContext = new ILContext(il, body);

						rootnode.Accept(new XamlNodeVisitor((node, parent) => node.Parent = parent), null);
						rootnode.Accept(new ExpandMarkupsVisitor(visitorContext), null);
						rootnode.Accept(new CreateObjectVisitor(visitorContext), null);
						rootnode.Accept(new SetNamescopesAndRegisterNamesVisitor(visitorContext), null);
						rootnode.Accept(new SetFieldVisitor(visitorContext), null);
						rootnode.Accept(new SetResourcesVisitor(visitorContext), null);
						rootnode.Accept(new SetPropertiesVisitor(visitorContext, true), null);

						il.Emit(OpCodes.Ret);
						initComp.Body = body;
					}
					catch (XamlParseException xpe)
					{
						LogLine(2, "failed.");
						LogError(null, null, null, resource.Name, xpe.XmlInfo.LineNumber, xpe.XmlInfo.LinePosition, 0, 0, xpe.Message,
							xpe.HelpLink, xpe.Source);
						LogLine(4, xpe.StackTrace);
						success = false;
						continue;
					}
					catch (XmlException xe)
					{
						LogLine(2, "failed.");
						LogError(null, null, null, resource.Name, xe.LineNumber, xe.LinePosition, 0, 0, xe.Message, xe.HelpLink, xe.Source);
						LogLine(4, xe.StackTrace);
						success = false;
						continue;
					}
					catch (Exception e)
					{
						LogLine(2, "failed.");
						LogError(null, null, null, resource.Name, 0, 0, 0, 0, e.Message, e.HelpLink, e.Source);
						LogLine(4, e.StackTrace);
						success = false;
						continue;
					}
					LogLine(2, "done.");

					if (OptimizeIL)
					{
						Log(2, "   Optimizing IL... ");
						initComp.Body.OptimizeMacros();
						LogLine(2, "done");
					}

					if (OutputGeneratedILAsCode)
					{
						var filepath = Path.Combine(Path.GetDirectoryName(Assembly), typeDef.FullName + ".decompiled.cs");
						Log(2, "   Decompiling {0} into {1}...", typeDef.FullName, filepath);
						var decompilerContext = new DecompilerContext(module);
						using (var writer = new StreamWriter(filepath))
						{
							var output = new PlainTextOutput(writer);

							var codeDomBuilder = new AstBuilder(decompilerContext);
							codeDomBuilder.AddType(typeDef);
							codeDomBuilder.GenerateCode(output);
						}

						LogLine(2, "done");
					}
					resourcesToPrune.Add(resource);
				}
				if (!KeepXamlResources)
				{
					if (resourcesToPrune.Any())
						LogLine(2, "  Removing compiled xaml resources");
					foreach (var resource in resourcesToPrune)
					{
						Log(2, "   Removing {0}... ", resource.Name);
						module.Resources.Remove(resource);
						LogLine(2, "done");
					}
				}

				LogLine(2, "");
			}

			if (!hasCompiledXamlResources)
			{
				LogLine(1, "No compiled resources. Skipping writing assembly.");
				return success;
			}

			Log(1, "Writing the assembly... ");
			try
			{
				assemblyDefinition.Write(Assembly, new WriterParameters
				{
					WriteSymbols = DebugSymbols
				});
				LogLine(1, "done.");
			}
			catch (Exception e)
			{
				LogLine(1, "failed.");
				LogError(null, null, null, null, 0, 0, 0, 0, e.Message, e.HelpLink, e.Source);
				LogLine(4, e.StackTrace);
				success = false;
			}

			return success;
		}
Beispiel #28
0
 /// <summary>
 /// Creates an <see cref="ILCursor"/> with an <see cref="ILContext"/>.
 /// </summary>
 public static void CreateCursor(this ILContext context, out ILCursor c) => c = new ILCursor(context);
		public SetPropertiesVisitor(ILContext context, bool stopOnResourceDictionary = false)
		{
			Context = context;
			Module = context.Body.Method.Module;
			StopOnResourceDictionary = stopOnResourceDictionary;
		}
Beispiel #30
0
		static TypeReference GetBPReturnType(ILContext context, FieldReference bpRef, IXmlLineInfo lineInfo)
		{
			//Find a property with a matching name
			var name = bpRef.Name;
			if (!name.EndsWith("Property", StringComparison.Ordinal))
				return context.Body.Method.Module.TypeSystem.Object;
			name = name.Substring(0, name.Length - 8);

			//First, check for a property
			TypeReference declaringTypeRef;
			var property = bpRef.DeclaringType.GetProperty(pd => pd.Name == name, out declaringTypeRef);
			if (property != null)
				return property.PropertyType;

			//Then check for getter or setter (attached BPs)
			var getters =
				bpRef.DeclaringType.GetMethods(md => md.Name == "Get" + name && md.IsStatic, context.Body.Method.Module)
					.SingleOrDefault();
			if (getters != null)
				return getters.Item1.ReturnType;

			//throws
			throw new XamlParseException(
				string.Format(
					"Can not find a Property named \"{0}\" or a static method named \"Get{0}\" for BindableProperty \"{1}\"", name,
					bpRef.Name), lineInfo);
		}
		static bool GetNameAndTypeRef(ref TypeReference elementType, string namespaceURI, ref string localname,
			ILContext context, IXmlLineInfo lineInfo)
		{
			var dotIdx = localname.IndexOf('.');
			if (dotIdx > 0)
			{
				var typename = localname.Substring(0, dotIdx);
				localname = localname.Substring(dotIdx + 1);
				elementType = new XmlType(namespaceURI, typename, null).GetTypeReference(context.Body.Method.Module, lineInfo);
				return true;
			}
			return false;
		}
Beispiel #32
0
		public static IEnumerable<Instruction> PushParentObjectsArray(this INode node, ILContext context)
		{
			var module = context.Body.Method.Module;

			var nodes = new List<IElementNode>();
			INode n = node.Parent;
			while (n != null)
			{
				var en = n as IElementNode;
				if (en != null && context.Variables.ContainsKey(en))
					nodes.Add(en);
				n = n.Parent;
			}

			if (nodes.Count == 0 && context.ParentContextValues == null)
			{
				yield return Instruction.Create(OpCodes.Ldnull);
				yield break;
			}

			if (nodes.Count == 0)
			{
				yield return Instruction.Create(OpCodes.Ldarg_0);
				yield return Instruction.Create(OpCodes.Ldfld, context.ParentContextValues);
				yield break;
			}

			//Compute parent object length
			if (context.ParentContextValues != null)
			{
				yield return Instruction.Create(OpCodes.Ldarg_0);
				yield return Instruction.Create(OpCodes.Ldfld, context.ParentContextValues);
				yield return Instruction.Create(OpCodes.Ldlen);
				yield return Instruction.Create(OpCodes.Conv_I4);
			}
			else
				yield return Instruction.Create(OpCodes.Ldc_I4_0);
			var parentObjectLength = new VariableDefinition(module.TypeSystem.Int32);
			context.Body.Variables.Add(parentObjectLength);
			yield return Instruction.Create(OpCodes.Stloc, parentObjectLength);

			//Create the final array
			yield return Instruction.Create(OpCodes.Ldloc, parentObjectLength);
			yield return Instruction.Create(OpCodes.Ldc_I4, nodes.Count);
			yield return Instruction.Create(OpCodes.Add);
			yield return Instruction.Create(OpCodes.Newarr, module.TypeSystem.Object);
			var finalArray = new VariableDefinition(module.Import(typeof (object[])));
			context.Body.Variables.Add(finalArray);
			yield return Instruction.Create(OpCodes.Stloc, finalArray);

			//Copy original array to final
			if (context.ParentContextValues != null)
			{
				yield return Instruction.Create(OpCodes.Ldarg_0);
				yield return Instruction.Create(OpCodes.Ldfld, context.ParentContextValues); //sourceArray
				yield return Instruction.Create(OpCodes.Ldc_I4_0); //sourceIndex
				yield return Instruction.Create(OpCodes.Ldloc, finalArray); //destinationArray
				yield return Instruction.Create(OpCodes.Ldc_I4, nodes.Count); //destinationIndex
				yield return Instruction.Create(OpCodes.Ldloc, parentObjectLength); //length
				var arrayCopy =
					module.Import(typeof (Array))
						.Resolve()
						.Methods.First(
							md =>
								md.Name == "Copy" && md.Parameters.Count == 5 &&
								md.Parameters[1].ParameterType.FullName == module.TypeSystem.Int32.FullName);
				yield return Instruction.Create(OpCodes.Call, module.Import(arrayCopy));
			}

			//Add nodes to array
			yield return Instruction.Create(OpCodes.Ldloc, finalArray);
			if (nodes.Count > 0)
			{
				for (var i = 0; i < nodes.Count; i++)
				{
					var en = nodes[i];
					yield return Instruction.Create(OpCodes.Dup);
					yield return Instruction.Create(OpCodes.Ldc_I4, i);
					yield return Instruction.Create(OpCodes.Ldloc, context.Variables[en]);
					if (context.Variables[en].VariableType.IsValueType)
						yield return Instruction.Create(OpCodes.Box, module.Import(context.Variables[en].VariableType));
					yield return Instruction.Create(OpCodes.Stelem_Ref);
				}
			}
		}
Beispiel #33
0
        public static void HookMap(ILContext il)
        {
            var c = new ILCursor(il);

            #region size
            // Find Main.minimapWidth = 240;

            /*
             *  IL_0C0C: ldsfld int32   Terraria.Main::mapStyle
             *  IL_0C11: ldc.i4.1
             *  IL_0C12: bne.un         IL_0E16
             *  IL_0C17: ldc.i4         240
             */
            if (!c.TryGotoNext(MoveType.After,
                               i => i.MatchLdsfld(typeof(Main).GetField("mapStyle")),
                               i => i.MatchLdcI4(1),
                               i => i.MatchBneUn(out _),
                               i => i.MatchLdcI4(240)))
            {
                return;
            }

            // Change "Main.minimapWidth = 240;" to "Main.minimapWidth = 240 * minimapScale;"
            c.EmitDelegate <Func <int, int> >((returnvalue) =>
            {
                return((int)(240 * BetterZoom.minimapScale));
            });

            // Find Main.minimapHeight = 240;

            /*
             *   IL_0C1C: stsfld    int32 Terraria.Main::miniMapWidth
             *   IL_0C21: ldc.i4    240
             */
            if (!c.TryGotoNext(MoveType.After,
                               i => i.MatchStsfld(typeof(Main).GetField("miniMapWidth")),
                               i => i.MatchLdcI4(240)))
            {
                return;
            }

            // Change "Main.minimapHeight = 240;" to "Main.minimapHeight = 240 * minimapScale;"
            c.EmitDelegate <Func <int, int> >((returnvalue) =>
            {
                return((int)(240 * BetterZoom.minimapScale));
            });
            #endregion

            #region border
            // Go to IL_0E01 (L326: Main.spriteBatch.Draw(Main.minimapFrame2Texture, ..., ..., ..., ..., ..., 1f))
            // Draw call for the black background

            /*
             *
             *   IL_0DF2: ldc.r4     0.0
             *   IL_0DF7: ldloca.s   V_49
             *   IL_0DF9: initobj    [Microsoft.Xna.Framework]Microsoft.Xna.Framework.Vector2
             *   IL_0DFF: ldloc.s    V_49
             *   IL_0E01: ldc.r4     1
             *
             */
            if (!c.TryGotoNext(MoveType.After,
                               i => i.MatchLdcR4(0),
                               i => i.MatchLdloca(49),
                               i => i.MatchInitobj(typeof(Vector2)),
                               i => i.MatchLdloc(49),
                               i => i.MatchLdcR4(1)))
            {
                return;
            }

            // change scale parameter to minimapScale
            c.EmitDelegate <Func <float, float> >((returnvalue) =>
            {
                return(BetterZoom.minimapScale);
            });
            c.Index++;


            // Go to IL_21E9 (L683: Main.spriteBatch.Draw(Main.minimapFrameTexture, ..., ..., ..., ..., ..., ..., 1f))
            // Draw call for the minimap frame

            /*
             * IL_21DA: ldc.r4       0.0
             * IL_21DF: ldloca.s     V_49
             * IL_21E1: initobj      [Microsoft.Xna.Framework]Microsoft.Xna.Framework.Vector2
             * IL_21E7: ldloc.s      V_49
             * IL_21E9: ldc.r4       1
             */
            if (!c.TryGotoNext(MoveType.After,
                               i => i.MatchLdcR4(0),
                               i => i.MatchLdloca(49),
                               i => i.MatchInitobj(typeof(Vector2)),
                               i => i.MatchLdloc(49),
                               i => i.MatchLdcR4(1)))
            {
                return;
            }

            c.EmitDelegate <Func <float, float> >((returnvalue) =>
            {
                return(BetterZoom.minimapScale);
            });
            #endregion

            #region buttons

            // Go to IL_2201 (L686: float num88 = num57 + 148f + (float)(num87 * 26);)

            /*
             *  IL_2201: ldloc.s    num58 [84]
             *  IL_2203: ldc.r4     148
             *  IL_2208: add
             *  IL_2209: ldloc.s    num88 [121]
             *  IL_220B: ldc.i4.s   26
             *  IL_220D: mul
             *  IL_220E: conv.r4
             *  IL_220F: add
             *  IL_2210: stloc.s    num89 [122]
             */

            if (!c.TryGotoNext(MoveType.After,
                               i => i.MatchLdloc(84),
                               i => i.MatchLdcR4(148),
                               i => i.MatchAdd(),
                               i => i.MatchLdloc(121),
                               i => i.MatchLdcI4(26),
                               i => i.MatchMul(),
                               i => i.MatchConvR4(),
                               i => i.MatchAdd()))
            {
                return;
            }

            // Change button X position
            c.EmitDelegate <Func <float, float> >((returnvalue) =>
            {
                float newX   = Main.miniMapX - 6;
                float offset = returnvalue - (newX + 148f);
                return(newX + (148f * BetterZoom.minimapScale) + offset * BetterZoom.minimapScale);
            });

            // Go to IL_2212 (L687: float num89 = num58 + 234f;)

            /*
             *  IL_2212: ldloc.s    num59
             *  IL_2214: ldc.r4     234
             *  IL_2219: add
             *  IL_221A: stloc.s    num90
             */

            if (!c.TryGotoNext(MoveType.After,
                               i => i.MatchLdloc(85),
                               i => i.MatchLdcR4(234),
                               i => i.MatchAdd()))
            {
                return;
            }

            // Change button Y position
            c.EmitDelegate <Func <float, float> >((returnvalue) =>
            {
                float newY = Main.miniMapY - 6;
                return(newY + 234f * BetterZoom.minimapScale);
            });

            // Go to IL_2229 (L688: if((float)Main.mouseX < num88 + 22f)))

            /*
             *  IL_2229: ldsfld     int32 Terraria.Main::mouseX
             *  IL_222E: conv.r4
             *  IL_222F: ldloc.s    num89 [122]
             *  IL_2231: ldc.r4     22
             *  IL_2236: add
             */

            if (!c.TryGotoNext(MoveType.After,
                               i => i.MatchLdsfld(typeof(Main).GetField("mouseX")),
                               i => i.MatchConvR4(),
                               i => i.MatchLdloc(122),
                               i => i.MatchLdcR4(22)))
            {
                return;
            }

            // Change click hitbox X
            c.EmitDelegate <Func <float, float> >((returnvalue) =>
            {
                return(returnvalue * BetterZoom.minimapScale);
            });

            // Go to (L688: if((float)Main.mouseY < num89 + 22f)))

            /*
             *  IL_2249: ldsfld     int32 Terraria.Main::mouseY
             *  IL_224E: conv.r4
             *  IL_224F: ldloc.s    num90 [123]
             *  IL_2251: ldc.r4     22
             *  IL_2256: add
             */
            if (!c.TryGotoNext(MoveType.After,
                               i => i.MatchLdsfld(typeof(Main).GetField("mouseY")),
                               i => i.MatchConvR4(),
                               i => i.MatchLdloc(123),
                               i => i.MatchLdcR4(22)))
            {
                return;
            }

            // Change click hitbox Y
            c.EmitDelegate <Func <float, float> >((returnvalue) =>
            {
                return(returnvalue * BetterZoom.minimapScale);
            });

            // Go to IL_229D (L690: Main.spriteBatch.Draw(Main.miniMapButtonTexture[i], ..., ..., ..., ..., ..., 1f))

            /*
             *  IL_229D: ldc.r4     0.0
             *  IL_22A2: ldloca.s   V_49
             *  IL_22A4: initobj    [Microsoft.Xna.Framework]Microsoft.Xna.Framework.Vector2
             *  IL_22AA: ldloc.s    V_49
             *  IL_22AC: ldc.r4     1
             */
            if (!c.TryGotoNext(MoveType.After,
                               i => i.MatchLdcR4(0),
                               i => i.MatchLdloca(49),
                               i => i.MatchInitobj(typeof(Vector2)),
                               i => i.MatchLdloc(49),
                               i => i.MatchLdcR4(1)))
            {
                return;
            }

            // Change scale of Buttons
            c.EmitDelegate <Func <float, float> >((returnvalue) =>
            {
                return(BetterZoom.minimapScale);
            });


            #endregion
        }
Beispiel #34
0
        private static void modOuiChapterPanelSetStatsPosition(ILContext il)
        {
            ILCursor cursor = new ILCursor(il);

            // this is a tricky one... in lines like this:
            // this.strawberriesOffset = this.Approach(this.strawberriesOffset, new Vector2(120f, (float)(this.deaths.Visible ? -40 : 0)), !approach);
            // we want to catch the result of (float)(this.deaths.Visible ? -40 : 0) and transform it to shift the things up if the speed berry PB is there.
            while (cursor.TryGotoNext(MoveType.After, instr => instr.MatchConvR4()))
            {
                Logger.Log("CollabUtils2/SpeedBerryPBInChapterPanel", $"Modifying strawberry/death counter positioning at {cursor.Index} in CIL code for OuiChapterPanel.SetStatsPosition");
                cursor.EmitDelegate <Func <float, float> >(position => (speedBerryPBDisplay?.Visible ?? false) ? position - 40 : position);
            }

            cursor.Index = 0;

            // we will cross 2 occurrences when deathsOffset will be set: first time with the heart, second time without.
            // the only difference is the X offset, so put the code in common.
            bool hasHeart = true;

            while (cursor.TryGotoNext(MoveType.After, instr => instr.MatchStfld(typeof(OuiChapterPanel), "deathsOffset")))
            {
                Logger.Log("CollabUtils2/SpeedBerryPBInChapterPanel", $"Injecting speed berry PB position updating at {cursor.Index} in CIL code for OuiChapterPanel.SetStatsPosition (has heart = {hasHeart})");

                // bool approach
                cursor.Emit(OpCodes.Ldarg_1);
                // StrawberriesCounter strawberries
                cursor.Emit(OpCodes.Ldarg_0);
                cursor.Emit(OpCodes.Ldfld, typeof(OuiChapterPanel).GetField("strawberries", BindingFlags.NonPublic | BindingFlags.Instance));
                // DeathsCounter deaths
                cursor.Emit(OpCodes.Ldarg_0);
                cursor.Emit(OpCodes.Ldfld, typeof(OuiChapterPanel).GetField("deaths", BindingFlags.NonPublic | BindingFlags.Instance));
                // bool hasHeart
                cursor.Emit(hasHeart ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0);
                // function call
                cursor.EmitDelegate <Action <bool, StrawberriesCounter, DeathsCounter, bool> >((approach, strawberries, deaths, thisHasHeart) => {
                    int shift = 0;
                    if (strawberries.Visible)
                    {
                        shift += 40;
                    }
                    if (deaths.Visible)
                    {
                        shift += 40;
                    }
                    speedBerryPBOffset = SpeedBerryPBInChapterPanel.approach(speedBerryPBOffset, new Vector2(thisHasHeart ? 150f : 0f, shift), !approach);
                });

                hasHeart = false;
            }

            cursor.Index = 0;

            // have a bigger spacing between heart and text when the speed berry PB is displayed, because it is bigger than berry / death count.
            while (cursor.TryGotoNext(MoveType.After, instr => instr.MatchLdcR4(120f) || instr.MatchLdcR4(-120f)))
            {
                Logger.Log("CollabUtils2/SpeedBerryPBInChapterPanel", $"Modifying column spacing at {cursor.Index} in CIL code for OuiChapterPanel.SetStatsPosition");
                cursor.EmitDelegate <Func <float, float> >(orig => {
                    if (speedBerryPBDisplay?.Visible ?? false)
                    {
                        return(orig + 30f * Math.Sign(orig));
                    }
                    return(orig);
                });
            }
        }
Beispiel #35
0
		public static IEnumerable<Instruction> PushXmlLineInfo(this INode node, ILContext context)
		{
			var module = context.Body.Method.Module;

			var xmlLineInfo = node as IXmlLineInfo;
			if (xmlLineInfo == null)
			{
				yield return Instruction.Create(OpCodes.Ldnull);
				yield break;
			}
			MethodReference ctor;
			if (xmlLineInfo.HasLineInfo())
			{
				yield return Instruction.Create(OpCodes.Ldc_I4, xmlLineInfo.LineNumber);
				yield return Instruction.Create(OpCodes.Ldc_I4, xmlLineInfo.LinePosition);
				ctor = module.Import(typeof (XmlLineInfo).GetConstructor(new[] { typeof (int), typeof (int) }));
			}
			else
				ctor = module.Import(typeof (XmlLineInfo).GetConstructor(new Type[] { }));
			yield return Instruction.Create(OpCodes.Newobj, ctor);
		}
        private void DrawZoomOut(ILContext il)
        {
            var c = new ILCursor(il);

            if (!c.TryGotoNext(MoveType.After,
                               i => i.MatchSub(),
                               i => i.MatchLdcR4(16),
                               i => i.MatchDiv(),
                               i => i.MatchLdcR4(1),
                               i => i.MatchSub()))
            {
                return;
            }

            c.EmitDelegate <Func <float, float> >((returnvalue) =>
            {
                return((int)((Main.screenPosition.X - 1600) / 16f - 1f));
            });

            /*
             *  IL_00B5: add
             *  IL_00B6: ldc.r4    16
             *  IL_00BB: div
             *  IL_00BC: conv.i4
             *  IL_00BD: ldc.i4.2
             *  IL_00BE: add
             *  ---> here
             */
            if (!c.TryGotoNext(MoveType.After,
                               i => i.MatchAdd(),
                               i => i.MatchLdcR4(16),
                               i => i.MatchDiv(),
                               i => i.MatchConvI4(),
                               i => i.MatchLdcI4(2),
                               i => i.MatchAdd()))
            {
                return;
            }

            c.EmitDelegate <Func <int, int> >((returnvalue) =>
            {
                return((int)((Main.screenPosition.X + Main.screenWidth + 1600) / 16f + 2));
            });

            return;

            Vector2 zero = new Vector2((float)Main.offScreenRange, (float)Main.offScreenRange);

            //var c = new ILCursor(il);

            if (!c.TryGotoNext(MoveType.After,
                               i => i.MatchStloc(8)))
            {
                return;
            }

            c.Index++;

            c.Emit(OpCodes.Ldc_I4, (int)((Main.screenPosition.X - zero.X) / 16f - 1f));
            c.Emit(OpCodes.Stloc, 5);

            c.Emit(OpCodes.Ldc_I4, (int)((Main.screenPosition.X + (float)Main.screenWidth + zero.X) / 16f) + 2);
            c.Emit(OpCodes.Stloc, 6);

            c.Emit(OpCodes.Ldc_I4, (int)((Main.screenPosition.Y - zero.Y) / 16f - 1f));
            c.Emit(OpCodes.Stloc, 7);

            c.Emit(OpCodes.Ldc_I4, (int)((Main.screenPosition.Y + (float)Main.screenHeight + zero.Y) / 16f) + 5);
            c.Emit(OpCodes.Stloc, 8);
        }
        private void IL_ESPerformEquipmentAction(ILContext il)
        {
            ILCursor c = new ILCursor(il);

            //Insert a check for Embryo procs at the top of the function
            bool            boost = false;
            EmbryoComponent cpt   = null;

            c.Emit(OpCodes.Ldarg_0);
            c.EmitDelegate <Action <EquipmentSlot> >((slot) => {
                boost = Util.CheckRoll(GetCount(slot.characterBody) * procChance);
                cpt   = slot.characterBody?.GetComponentInChildren <EmbryoComponent>();
            });

            bool ILFound;

            ILLabel[] swarr = new ILLabel[] {};
            //Load switch case locations
            ILFound = c.TryGotoNext(
                x => x.MatchSwitch(out swarr));
            if (!ILFound)
            {
                ClassicItemsPlugin._logger.LogError("Failed to apply Beating Embryo IL patch (ALL EQUIPMENTS): couldn't find switch!");
                return;
            }

            //CommandMissile: double number of missiles fired in the same timespan
            if ((int)EquipmentIndex.CommandMissile >= swarr.Length)
            {
                ClassicItemsPlugin._logger.LogError("Failed to apply Beating Embryo IL patch: CommandMissile (PerformEquipmentAction); not in switch");
            }
            else if (subEnable[EquipmentIndex.CommandMissile])
            {
                //Find: default missile increment (+= (int)12)
                c.GotoLabel(swarr[(int)EquipmentIndex.CommandMissile]);
                ILFound = c.TryGotoNext(
                    x => x.MatchLdfld <EquipmentSlot>("remainingMissiles"),
                    x => x.OpCode == OpCodes.Ldc_I4_S,
                    x => x.MatchAdd());

                if (ILFound)
                {
                    c.Index += 2;
                    //Replace original increment number with a custom function to check for Embryo proc
                    //If proc happens, doubles number of missiles added and marks the total number of missiles added as boosted; otherwise returns original
                    c.EmitDelegate <Func <int, int> >((int origMissiles) => {
                        if (boost && cpt)
                        {
                            cpt.boostedMissiles += origMissiles * 2;
                        }
                        return((sbyte)(boost ? origMissiles * 2 : origMissiles));
                    });
                }
                else
                {
                    ClassicItemsPlugin._logger.LogError("Failed to apply Beating Embryo IL patch: CommandMissile (PerformEquipmentAction); target instructions not found");
                }
            }

            //Blackhole: double yoink radius
            if ((int)EquipmentIndex.CommandMissile >= swarr.Length)
            {
                ClassicItemsPlugin._logger.LogError("Failed to apply Beating Embryo IL patch: Blackhole; not in switch");
            }
            else if (subEnable[EquipmentIndex.Blackhole])
            {
                //Find: string "Prefabs/Projectiles/GravSphere", ldloc 15 (Vector3 position)
                c.GotoLabel(swarr[(int)EquipmentIndex.Blackhole]);
                ILFound = c.TryGotoNext(MoveType.After,
                                        x => x.MatchLdstr("Prefabs/Projectiles/GravSphere"),
                                        x => x.MatchCallOrCallvirt <Resources>("Load"));

                if (ILFound)
                {
                    //Insert a custom function to check for Embryo proc (captures GravSphere projectile prefab)
                    //If proc happens, radius of prefab's RadialForce component (GravSphere pull range) is doubled
                    c.EmitDelegate <Func <GameObject, GameObject> >((obj) => {
                        var newobj = UnityEngine.Object.Instantiate(obj);
                        if (boost)
                        {
                            newobj.GetComponent <RadialForce>().radius *= 2;
                        }
                        return(newobj);
                    });
                }
                else
                {
                    ClassicItemsPlugin._logger.LogError("Failed to apply Beating Embryo IL patch: Blackhole; target instructions not found");
                }
            }

            //CritOnUse: double duration
            if ((int)EquipmentIndex.CritOnUse >= swarr.Length)
            {
                ClassicItemsPlugin._logger.LogError("Failed to apply Beating Embryo IL patch: CritOnUse; not in switch");
            }
            else if (subEnable[EquipmentIndex.CritOnUse])
            {
                //Find: AddTimedBuff(BuffIndex.FullCrit, 8f)
                c.GotoLabel(swarr[(int)EquipmentIndex.CritOnUse]);
                ILFound = c.TryGotoNext(
                    x => x.MatchLdcI4((int)BuffIndex.FullCrit),
                    x => x.OpCode == OpCodes.Ldc_R4,
                    x => x.MatchCallOrCallvirt <CharacterBody>("AddTimedBuff"));

                if (ILFound)
                {
                    //Advance cursor to the found ldcR4 (time argument of AddTimedBuff)
                    c.Index += 2;
                    //Replace original buff time with a custom function to check for Embryo proc
                    //If proc happens, doubles the buff time; otherwise returns original
                    c.EmitDelegate <Func <float, float> >((origBuffTime) => {
                        if (cpt)
                        {
                            cpt.lastCOUBoosted = boost;
                        }
                        return(boost ? origBuffTime * 2 : origBuffTime);
                    });
                }
                else
                {
                    ClassicItemsPlugin._logger.LogError("Failed to apply Beating Embryo IL patch: CritOnUse; target instructions not found");
                }
            }

            //Gateway: double speed
            if ((int)EquipmentIndex.Gateway >= swarr.Length)
            {
                ClassicItemsPlugin._logger.LogError("Failed to apply Beating Embryo IL patch: Gateway; not in switch");
            }
            else if (subEnable[EquipmentIndex.Gateway])
            {
                //Find: start of Gateway label
                c.GotoLabel(swarr[(int)EquipmentIndex.Gateway]);

                //Insert a custom function to check boost
                //If proc happens, increments the player's boosted gateway counter; this will be spent once the gateway spawns
                c.EmitDelegate <Action>(() => {
                    if (boost && cpt)
                    {
                        cpt.boostedGates++;
                    }
                });
            }

            //Scanner: double duration
            if ((int)EquipmentIndex.Scanner >= swarr.Length)
            {
                ClassicItemsPlugin._logger.LogError("Failed to apply Beating Embryo IL patch: Scanner; not in switch");
            }
            else if (subEnable[EquipmentIndex.Scanner])
            {
                //Find: loading of prefab
                c.GotoLabel(swarr[(int)EquipmentIndex.Scanner]);
                ILFound = c.TryGotoNext(MoveType.After,
                                        x => x.MatchLdstr("Prefabs/NetworkedObjects/ChestScanner"),
                                        x => x.MatchCall <UnityEngine.Resources>("Load"));

                if (ILFound)
                {
                    //Insert a custom function to check boost
                    //If proc happens, replace the loaded prefab with a boosted copy (allows proper networking)
                    c.EmitDelegate <Func <GameObject, GameObject> >((origObj) => {
                        if (boost)
                        {
                            return(boostedScannerPrefab);
                        }
                        else
                        {
                            return(origObj);
                        }
                    });
                }
                else
                {
                    ClassicItemsPlugin._logger.LogError("Failed to apply Beating Embryo IL patch: Scanner; target instructions not found");
                }
            }

            //BFG: double impact damage
            if ((int)EquipmentIndex.BFG >= swarr.Length)
            {
                ClassicItemsPlugin._logger.LogError("Failed to apply Beating Embryo IL patch: BFG; not in switch");
            }
            else if (subEnable[EquipmentIndex.BFG])
            {
                //Find: loading of (int)2 into EquipmentSlot.bfgChargeTimer
                c.GotoLabel(swarr[(int)EquipmentIndex.BFG]);
                ILFound = c.TryGotoNext(MoveType.After,
                                        x => x.OpCode == OpCodes.Ldc_R4,
                                        x => x.MatchStfld <EquipmentSlot>("bfgChargeTimer"));

                if (ILFound)
                {
                    //Insert a custom function to check boost
                    //If proc happens, increments the player's boosted BFG shot counter; this will be spent once the BFG actually fires
                    c.EmitDelegate <Action>(() => {
                        if (boost && cpt)
                        {
                            cpt.boostedBFGs++;
                        }
                    });
                }
                else
                {
                    ClassicItemsPlugin._logger.LogError("Failed to apply Beating Embryo IL patch: BFG (PerformEquipmentAction); target instructions not found");
                }
            }

            //Jetpack: double duration
            if ((int)EquipmentIndex.Jetpack >= swarr.Length)
            {
                ClassicItemsPlugin._logger.LogError("Failed to apply Beating Embryo IL patch: Jetpack; not in switch");
            }
            else if (subEnable[EquipmentIndex.Jetpack])
            {
                //Find: start of Jetpack label
                c.GotoLabel(swarr[(int)EquipmentIndex.Jetpack]);

                //Insert a custom function to check boost
                //If proc happens, increments the player's boosted jetpack counter; this will be spent during the RPC duration reset
                c.EmitDelegate <Action>(() => {
                    if (boost && cpt)
                    {
                        cpt.boostedJetTime = 15f;
                    }
                });
            }

            //FireBallDash: double speed and damage
            if ((int)EquipmentIndex.FireBallDash >= swarr.Length)
            {
                ClassicItemsPlugin._logger.LogError("Failed to apply Beating Embryo IL patch: FireBallDash; not in switch");
            }
            else if (subEnable[EquipmentIndex.FireBallDash])
            {
                //Find: string "Prefabs/NetworkedObjects/FireballVehicle"
                //Then find: instantiation of the prefab
                c.GotoLabel(swarr[(int)EquipmentIndex.FireBallDash]);
                ILFound = c.TryGotoNext(
                    x => x.MatchLdstr("Prefabs/NetworkedObjects/FireballVehicle")) &&
                          c.TryGotoNext(
                    x => x.MatchCallOrCallvirt <UnityEngine.Object>("Instantiate"));

                if (ILFound)
                {
                    c.Index++;

                    //Insert a custom function to check boost (captures a dup of the instantiated FireballVehicle)
                    //If proc happens, doubles the instanced vehicle's target speed, acceleration, and blast and collision damage
                    c.Emit(OpCodes.Dup);
                    c.EmitDelegate <Action <GameObject> >((go) => {
                        if (boost)
                        {
                            go.GetComponent <FireballVehicle>().targetSpeed              *= 2f;
                            go.GetComponent <FireballVehicle>().acceleration             *= 2f;
                            go.GetComponent <FireballVehicle>().blastDamageCoefficient   *= 2f;
                            go.GetComponent <FireballVehicle>().overlapDamageCoefficient *= 2f;
                        }
                    });
                }
                else
                {
                    ClassicItemsPlugin._logger.LogError("Failed to apply Beating Embryo IL patch: FireBallDash; target instructions not found");
                }
            }

            //GainArmor: double duration
            if ((int)EquipmentIndex.GainArmor >= swarr.Length)
            {
                ClassicItemsPlugin._logger.LogError("Failed to apply Beating Embryo IL patch: GainArmor; not in switch");
            }
            else if (subEnable[EquipmentIndex.GainArmor])
            {
                //Find: AddTimedBuff(BuffIndex.ElephantArmorBoost, 5f)
                c.GotoLabel(swarr[(int)EquipmentIndex.GainArmor]);
                ILFound = c.TryGotoNext(
                    x => x.MatchLdcI4((int)BuffIndex.ElephantArmorBoost),
                    x => x.OpCode == OpCodes.Ldc_R4,
                    x => x.MatchCallvirt <CharacterBody>("AddTimedBuff"));

                if (ILFound)
                {
                    //Advance cursor to the found ldcR4 (time argument of AddTimedBuff)
                    c.Index += 2;

                    //Replace original buff time (5f) with a custom function to check for Embryo proc
                    //If proc happens, doubles the buff time to 10f; otherwise returns original
                    c.EmitDelegate <Func <float, float> >((origBuffTime) => {
                        return(boost?2 * origBuffTime:origBuffTime);
                    });
                }
                else
                {
                    ClassicItemsPlugin._logger.LogError("Failed to apply Beating Embryo IL patch: GainArmor; target instructions not found");
                }
            }

            //Cleanse: double projectile delete radius
            if ((int)EquipmentIndex.Cleanse >= swarr.Length)
            {
                ClassicItemsPlugin._logger.LogError("Failed to apply Beating Embryo IL patch: Cleanse; not in switch");
            }
            else if (subEnable[EquipmentIndex.Cleanse])
            {
                c.EmitDelegate <Action>(() => {
                    if (cpt)
                    {
                        cpt.lastCleanseBoosted = boost;
                    }
                });
            }

            //Recycle: double recycle count
            if ((int)EquipmentIndex.Recycle >= swarr.Length)
            {
                ClassicItemsPlugin._logger.LogError("Failed to apply Beating Embryo IL patch: Recycle; not in switch");
            }
            else if (subEnable[EquipmentIndex.Recycle])
            {
                c.GotoLabel(swarr[(int)EquipmentIndex.Recycle]);
                ILFound = c.TryGotoNext(
                    x => x.MatchLdloc(out _),
                    x => x.MatchLdcI4(1),
                    x => x.MatchCallOrCallvirt <GenericPickupController>("set_NetworkRecycled"));

                if (ILFound)
                {
                    c.Index++;
                    c.Emit(OpCodes.Dup);
                    c.Index++;
                    c.EmitDelegate <Func <GenericPickupController, bool, bool> >((pctrl, origRecyc) => {
                        if (boost && pctrl && pctrl.GetComponent <EmbryoRecycleFlag>() == null)
                        {
                            pctrl.gameObject.AddComponent <EmbryoRecycleFlag>();
                            return(false);
                        }
                        return(true);
                    });
                }
                else
                {
                    ClassicItemsPlugin._logger.LogError("Failed to apply Beating Embryo IL patch: Recycle; target instructions not found");
                }
            }

            //LifestealOnHit: double buff duration
            if ((int)EquipmentIndex.LifestealOnHit >= swarr.Length)
            {
                ClassicItemsPlugin._logger.LogError("Failed to apply Beating Embryo IL patch: LifestealOnHit; not in switch");
            }
            else if (subEnable[EquipmentIndex.LifestealOnHit])
            {
                c.GotoLabel(swarr[(int)EquipmentIndex.LifestealOnHit]);
                ILFound = c.TryGotoNext(MoveType.After,
                                        x => x.MatchLdcI4((int)BuffIndex.LifeSteal),
                                        x => x.MatchLdcR4(out _),
                                        x => x.MatchCallOrCallvirt <CharacterBody>("AddTimedBuff"));

                if (ILFound)
                {
                    c.Index--;
                    c.EmitDelegate <Func <float, float> >((origBuffTime) => {
                        return(boost?2 * origBuffTime:origBuffTime);
                    });
                }
                else
                {
                    ClassicItemsPlugin._logger.LogError("Failed to apply Beating Embryo IL patch: LifestealOnHit; target instructions not found");
                }
            }

            //TeamWarCry: double buff duration
            if ((int)EquipmentIndex.TeamWarCry >= swarr.Length)
            {
                ClassicItemsPlugin._logger.LogError("Failed to apply Beating Embryo IL patch: TeamWarCry; not in switch");
            }
            else if (subEnable[EquipmentIndex.TeamWarCry])
            {
                c.GotoLabel(swarr[(int)EquipmentIndex.TeamWarCry]);
                ILFound = c.TryGotoNext(MoveType.After,
                                        x => x.MatchLdcI4((int)BuffIndex.TeamWarCry),
                                        x => x.MatchLdcR4(out _),
                                        x => x.MatchCallOrCallvirt <CharacterBody>("AddTimedBuff"));

                if (ILFound)
                {
                    c.Index--;
                    c.EmitDelegate <Func <float, float> >((origBuffTime) => {
                        return(boost?2 * origBuffTime:origBuffTime);
                    });
                }
                else
                {
                    ClassicItemsPlugin._logger.LogError("Failed to apply Beating Embryo IL patch: TeamWarCry; target instructions not found (first buff time replacement)");
                }

                ILFound = c.TryGotoNext(MoveType.After,
                                        x => x.MatchLdcI4((int)BuffIndex.TeamWarCry),
                                        x => x.MatchLdcR4(out _),
                                        x => x.MatchCallOrCallvirt <CharacterBody>("AddTimedBuff"));

                if (ILFound)
                {
                    c.Index--;
                    c.EmitDelegate <Func <float, float> >((origBuffTime) => {
                        return(boost?2 * origBuffTime:origBuffTime);
                    });
                }
                else
                {
                    ClassicItemsPlugin._logger.LogError("Failed to apply Beating Embryo IL patch: TeamWarCry; target instructions not found (second buff time replacement)");
                }
            }
        }
        private void IL_ESFixedUpdate(ILContext il)
        {
            ILCursor c = new ILCursor(il);

            EmbryoComponent cpt = null;

            c.Emit(OpCodes.Ldarg_0);
            c.EmitDelegate <Action <EquipmentSlot> >((slot) => {
                cpt = slot.characterBody?.GetComponentInChildren <EmbryoComponent>();
            });

            bool ILFound;

            if (subEnable[EquipmentIndex.CommandMissile])
            {
                //Find: loading of 0.125f into EquipmentSlot.missileTimer
                ILFound = c.TryGotoNext(
                    x => x.OpCode == OpCodes.Ldc_R4,
                    x => x.MatchStfld <EquipmentSlot>("missileTimer"));

                if (ILFound)
                {
                    //Replace original missile cooldown (0.125f) with a custom function to check for Embryo-boosted missiles
                    //If boosts exist, halves the missile cooldown to 0.0625f and deducts a boost from CPD; otherwise returns original
                    c.Index++;
                    c.EmitDelegate <Func <float, float> >((origCooldown) => {
                        if (cpt && cpt.boostedMissiles > 0)
                        {
                            cpt.boostedMissiles--;
                            return(origCooldown / 2);
                        }
                        return(origCooldown);
                    });
                }
                else
                {
                    ClassicItemsPlugin._logger.LogError("Failed to apply Beating Embryo IL patch: CommandMissile (FixedUpdate)");
                }
            }

            if (subEnable[EquipmentIndex.BFG])
            {
                //Find: string "Prefabs/Projectiles/BeamSphere"
                //Then find: CharacterBody.get_damage * 2f (damage argument of FireProjectile)
                ILFound = c.TryGotoNext(
                    x => x.MatchLdstr("Prefabs/Projectiles/BeamSphere")) &&
                          c.TryGotoNext(
                    x => x.MatchCallvirt <CharacterBody>("get_damage"),
                    x => x.OpCode == OpCodes.Ldc_R4,
                    x => x.MatchMul());

                if (ILFound)
                {
                    //Advance cursor to found ldcR4
                    c.Index += 2;

                    //Replace original FireProjectile damage coefficient (2f) with a custom function to check for Embryo-boosted BFG shots
                    //If boosts exist, doubles the damage coefficient to 4f and deducts a boost from CPD; otherwise returns original
                    c.EmitDelegate <Func <float, float> >((origDamage) => {
                        if (cpt && cpt.boostedBFGs > 0)
                        {
                            cpt.boostedBFGs--;
                            return(origDamage * 2f);
                        }
                        return(origDamage);
                    });
                }
                else
                {
                    ClassicItemsPlugin._logger.LogError("Failed to apply Beating Embryo IL patch: BFG (FixedUpdate)");
                }
            }
        }
Beispiel #39
0
		public static IEnumerable<Instruction> PushConvertedValue(this ValueNode node, ILContext context,
			TypeReference targetTypeRef, TypeReference typeConverter, IEnumerable<Instruction> pushServiceProvider,
			bool boxValueTypes, bool unboxValueTypes)
		{
			var module = context.Body.Method.Module;
			var str = (string)node.Value;

			//If there's a [TypeConverter], use it
			if (typeConverter != null)
			{
				var isExtendedConverter = typeConverter.ImplementsInterface(module.Import(typeof (IExtendedTypeConverter)));
				var typeConverterCtor = typeConverter.Resolve().Methods.Single(md => md.IsConstructor && md.Parameters.Count == 0);
				var typeConverterCtorRef = module.Import(typeConverterCtor);
				var convertFromInvariantStringDefinition = isExtendedConverter
					? module.Import(typeof (IExtendedTypeConverter))
						.Resolve()
						.Methods.FirstOrDefault(md => md.Name == "ConvertFromInvariantString" && md.Parameters.Count == 2)
					: typeConverter.Resolve()
						.AllMethods()
						.FirstOrDefault(md => md.Name == "ConvertFromInvariantString" && md.Parameters.Count == 1);
				var convertFromInvariantStringReference = module.Import(convertFromInvariantStringDefinition);

				yield return Instruction.Create(OpCodes.Newobj, typeConverterCtorRef);
				yield return Instruction.Create(OpCodes.Ldstr, node.Value as string);

				if (isExtendedConverter)
				{
					foreach (var instruction in pushServiceProvider)
						yield return instruction;
				}

				yield return Instruction.Create(OpCodes.Callvirt, convertFromInvariantStringReference);

				if (targetTypeRef.IsValueType && unboxValueTypes)
					yield return Instruction.Create(OpCodes.Unbox_Any, module.Import(targetTypeRef));

				//ConvertFrom returns an object, no need to Box
				yield break;
			}
			var originalTypeRef = targetTypeRef;
			var isNullable = false;
			MethodReference nullableCtor = null;
			if (targetTypeRef.Resolve().FullName == "System.Nullable`1")
			{
				targetTypeRef = ((GenericInstanceType)targetTypeRef).GenericArguments[0];
				isNullable = true;
				nullableCtor = originalTypeRef.GetMethods(md => md.IsConstructor && md.Parameters.Count == 1, module).Single().Item1;
				nullableCtor = nullableCtor.MakeGeneric(targetTypeRef);
			}
			//Obvious Built-in conversions
			if (targetTypeRef.Resolve().BaseType != null && targetTypeRef.Resolve().BaseType.FullName == "System.Enum")
				yield return Instruction.Create(OpCodes.Ldc_I4, ParseEnum(targetTypeRef, str, node));
			else if (targetTypeRef.FullName == "System.Char")
				yield return Instruction.Create(OpCodes.Ldc_I4, Char.Parse(str));
			else if (targetTypeRef.FullName == "System.Byte")
				yield return Instruction.Create(OpCodes.Ldc_I4, Byte.Parse(str, CultureInfo.InvariantCulture));
			else if (targetTypeRef.FullName == "System.Int16")
				yield return Instruction.Create(OpCodes.Ldc_I4, Int16.Parse(str, CultureInfo.InvariantCulture));
			else if (targetTypeRef.FullName == "System.Int32")
				yield return Instruction.Create(OpCodes.Ldc_I4, Int32.Parse(str, CultureInfo.InvariantCulture));
			else if (targetTypeRef.FullName == "System.Int64")
				yield return Instruction.Create(OpCodes.Ldc_I8, Int64.Parse(str, CultureInfo.InvariantCulture));
			else if (targetTypeRef.FullName == "System.Single")
				yield return Instruction.Create(OpCodes.Ldc_R4, Single.Parse(str, CultureInfo.InvariantCulture));
			else if (targetTypeRef.FullName == "System.Double")
				yield return Instruction.Create(OpCodes.Ldc_R8, Double.Parse(str, CultureInfo.InvariantCulture));
			else if (targetTypeRef.FullName == "System.Boolean")
			{
				if (Boolean.Parse(str))
					yield return Instruction.Create(OpCodes.Ldc_I4_1);
				else
					yield return Instruction.Create(OpCodes.Ldc_I4_0);
			}
			else if (targetTypeRef.FullName == "System.TimeSpan")
			{
				var ts = TimeSpan.Parse(str, CultureInfo.InvariantCulture);
				var ticks = ts.Ticks;
				var timeSpanCtor =
					module.Import(typeof (TimeSpan))
						.Resolve()
						.Methods.FirstOrDefault(md => md.IsConstructor && md.Parameters.Count == 1);
				var timeSpanCtorRef = module.Import(timeSpanCtor);

				yield return Instruction.Create(OpCodes.Ldc_I8, ticks);
				yield return Instruction.Create(OpCodes.Newobj, timeSpanCtorRef);
			}
			else if (targetTypeRef.FullName == "System.DateTime")
			{
				var dt = DateTime.Parse(str, CultureInfo.InvariantCulture);
				var ticks = dt.Ticks;
				var dateTimeCtor =
					module.Import(typeof (DateTime))
						.Resolve()
						.Methods.FirstOrDefault(md => md.IsConstructor && md.Parameters.Count == 1);
				var dateTimeCtorRef = module.Import(dateTimeCtor);

				yield return Instruction.Create(OpCodes.Ldc_I8, ticks);
				yield return Instruction.Create(OpCodes.Newobj, dateTimeCtorRef);
			}
			else if (targetTypeRef.FullName == "System.String" && str.StartsWith("{}", StringComparison.Ordinal))
				yield return Instruction.Create(OpCodes.Ldstr, str.Substring(2));
			else if (targetTypeRef.FullName == "System.String")
				yield return Instruction.Create(OpCodes.Ldstr, str);
			else if (targetTypeRef.FullName == "System.Object")
				yield return Instruction.Create(OpCodes.Ldstr, str);
			else
				yield return Instruction.Create(OpCodes.Ldnull);

			if (isNullable)
				yield return Instruction.Create(OpCodes.Newobj, module.Import(nullableCtor));
			if (originalTypeRef.IsValueType && boxValueTypes)
				yield return Instruction.Create(OpCodes.Box, module.Import(originalTypeRef));
		}
		public SetResourcesVisitor(ILContext context)
		{
			Context = context;
			Module = context.Body.Method.Module;
		}