using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Cecil.Mdb;
using Mono.Cecil.Pdb;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;

namespace QNetWeaver
{
	internal class Weaver
	{
		public static void ResetRecursionCount() => s_RecursionCount = 0;

		public static bool CanBeResolved(TypeReference parent)
		{
			while (parent != null)
			{
				bool result;
				if (parent.Scope.Name == "Windows")
				{
					result = false;
				}
				else
				{
					if (!(parent.Scope.Name == "mscorlib"))
					{
						try
						{
							parent = parent.Resolve().BaseType;
						}
						catch
						{
							return false;
						}

						continue;
					}

					var typeDefinition = parent.Resolve();
					result = (typeDefinition != null);
				}

				return result;
			}

			return true;
		}

		public static bool IsArrayType(TypeReference variable) => (!variable.IsArray || !((ArrayType)variable).ElementType.IsArray) && (!variable.IsArray || ((ArrayType)variable).Rank <= 1);

		public static void DLog(TypeDefinition td, string fmt, params object[] args)
		{
			if (m_DebugFlag)
			{
				Console.WriteLine("[" + td.Name + "] " + string.Format(fmt, args));
			}
		}

		public static int GetSyncVarStart(string className)
		{
			int result;
			if (lists.numSyncVars.ContainsKey(className))
			{
				var num = lists.numSyncVars[className];
				result = num;
			}
			else
			{
				result = 0;
			}

			return result;
		}

		public static void SetNumSyncVars(string className, int num) => lists.numSyncVars[className] = num;

		public static MethodReference GetWriteFunc(TypeReference variable)
		{
			MethodReference result;
			if (s_RecursionCount++ > 128)
			{
				Log.Error("GetWriteFunc recursion depth exceeded for " + variable.Name + ". Check for self-referencing member variables.");
				fail = true;
				result = null;
			}
			else
			{
				if (lists.writeFuncs.ContainsKey(variable.FullName))
				{
					var methodReference = lists.writeFuncs[variable.FullName];
					if (methodReference.Parameters[0].ParameterType.IsArray == variable.IsArray)
					{
						return methodReference;
					}
				}

				if (variable.IsByReference)
				{
					Log.Error("GetWriteFunc variable.IsByReference error.");
					result = null;
				}
				else
				{
					MethodDefinition methodDefinition;
					if (variable.IsArray)
					{
						var elementType = variable.GetElementType();
						var writeFunc = GetWriteFunc(elementType);
						if (writeFunc == null)
						{
							return null;
						}

						methodDefinition = GenerateArrayWriteFunc(variable, writeFunc);
					}
					else
					{
						if (variable.Resolve().IsEnum)
						{
							return NetworkWriterWriteInt32;
						}

						methodDefinition = GenerateWriterFunction(variable);
					}

					if (methodDefinition == null)
					{
						result = null;
					}
					else
					{
						RegisterWriteFunc(variable.FullName, methodDefinition);
						result = methodDefinition;
					}
				}
			}

			return result;
		}

		public static void RegisterWriteFunc(string name, MethodDefinition newWriterFunc)
		{
			lists.writeFuncs[name] = newWriterFunc;
			lists.generatedWriteFunctions.Add(newWriterFunc);
			ConfirmGeneratedCodeClass(scriptDef.MainModule);
			lists.generateContainerClass.Methods.Add(newWriterFunc);
		}

		public static MethodReference GetReadByReferenceFunc(TypeReference variable)
		{
			MethodReference result;
			if (lists.readByReferenceFuncs.ContainsKey(variable.FullName))
			{
				result = lists.readByReferenceFuncs[variable.FullName];
			}
			else
			{
				result = null;
			}

			return result;
		}

		public static MethodReference GetReadFunc(TypeReference variable)
		{
			if (lists.readFuncs.ContainsKey(variable.FullName))
			{
				var methodReference = lists.readFuncs[variable.FullName];
				if (methodReference.ReturnType.IsArray == variable.IsArray)
				{
					return methodReference;
				}
			}

			var typeDefinition = variable.Resolve();
			MethodReference result;
			if (typeDefinition == null)
			{
				Log.Error("GetReadFunc unsupported type " + variable.FullName);
				result = null;
			}
			else if (variable.IsByReference)
			{
				Log.Error("GetReadFunc variable.IsByReference error.");
				result = null;
			}
			else
			{
				MethodDefinition methodDefinition;
				if (variable.IsArray)
				{
					var elementType = variable.GetElementType();
					var readFunc = GetReadFunc(elementType);
					if (readFunc == null)
					{
						return null;
					}

					methodDefinition = GenerateArrayReadFunc(variable, readFunc);
				}
				else
				{
					if (typeDefinition.IsEnum)
					{
						return NetworkReaderReadInt32;
					}

					methodDefinition = GenerateReadFunction(variable);
				}

				if (methodDefinition == null)
				{
					Log.Error("GetReadFunc unable to generate function for:" + variable.FullName);
					result = null;
				}
				else
				{
					RegisterReadFunc(variable.FullName, methodDefinition);
					result = methodDefinition;
				}
			}

			return result;
		}

		public static void RegisterReadByReferenceFunc(string name, MethodDefinition newReaderFunc)
		{
			lists.readByReferenceFuncs[name] = newReaderFunc;
			lists.generatedReadFunctions.Add(newReaderFunc);
			ConfirmGeneratedCodeClass(scriptDef.MainModule);
			lists.generateContainerClass.Methods.Add(newReaderFunc);
		}

		public static void RegisterReadFunc(string name, MethodDefinition newReaderFunc)
		{
			lists.readFuncs[name] = newReaderFunc;
			lists.generatedReadFunctions.Add(newReaderFunc);
			ConfirmGeneratedCodeClass(scriptDef.MainModule);
			lists.generateContainerClass.Methods.Add(newReaderFunc);
		}

		private static MethodDefinition GenerateArrayReadFunc(TypeReference variable, MethodReference elementReadFunc)
		{
			MethodDefinition result;
			if (!IsArrayType(variable))
			{
				Log.Error(variable.FullName + " is an unsupported array type. Jagged and multidimensional arrays are not supported");
				result = null;
			}
			else
			{
				var text = "_ReadArray" + variable.GetElementType().Name + "_";
				if (variable.DeclaringType != null)
				{
					text += variable.DeclaringType.Name;
				}
				else
				{
					text += "None";
				}

				var methodDefinition = new MethodDefinition(text, MethodAttributes.FamANDAssem | MethodAttributes.Family | MethodAttributes.Static | MethodAttributes.HideBySig, variable);
				methodDefinition.Parameters.Add(new ParameterDefinition("reader", ParameterAttributes.None, scriptDef.MainModule.ImportReference(NetworkReaderType)));
				methodDefinition.Body.Variables.Add(new VariableDefinition(int32Type));
				methodDefinition.Body.Variables.Add(new VariableDefinition(variable));
				methodDefinition.Body.Variables.Add(new VariableDefinition(int32Type));
				methodDefinition.Body.InitLocals = true;
				var ilprocessor = methodDefinition.Body.GetILProcessor();
				ilprocessor.Append(ilprocessor.Create(OpCodes.Ldarg_0));
				ilprocessor.Append(ilprocessor.Create(OpCodes.Call, NetworkReadUInt16));
				ilprocessor.Append(ilprocessor.Create(OpCodes.Stloc_0));
				ilprocessor.Append(ilprocessor.Create(OpCodes.Ldloc_0));
				var instruction = ilprocessor.Create(OpCodes.Nop);
				ilprocessor.Append(ilprocessor.Create(OpCodes.Brtrue, instruction));
				ilprocessor.Append(ilprocessor.Create(OpCodes.Ldc_I4_0));
				ilprocessor.Append(ilprocessor.Create(OpCodes.Newarr, variable.GetElementType()));
				ilprocessor.Append(ilprocessor.Create(OpCodes.Ret));
				ilprocessor.Append(instruction);
				ilprocessor.Append(ilprocessor.Create(OpCodes.Ldloc_0));
				ilprocessor.Append(ilprocessor.Create(OpCodes.Newarr, variable.GetElementType()));
				ilprocessor.Append(ilprocessor.Create(OpCodes.Stloc_1));
				ilprocessor.Append(ilprocessor.Create(OpCodes.Ldc_I4_0));
				ilprocessor.Append(ilprocessor.Create(OpCodes.Stloc_2));
				var instruction2 = ilprocessor.Create(OpCodes.Nop);
				ilprocessor.Append(ilprocessor.Create(OpCodes.Br, instruction2));
				var instruction3 = ilprocessor.Create(OpCodes.Nop);
				ilprocessor.Append(instruction3);
				ilprocessor.Append(ilprocessor.Create(OpCodes.Ldloc_1));
				ilprocessor.Append(ilprocessor.Create(OpCodes.Ldloc_2));
				ilprocessor.Append(ilprocessor.Create(OpCodes.Ldelema, variable.GetElementType()));
				ilprocessor.Append(ilprocessor.Create(OpCodes.Ldarg_0));
				ilprocessor.Append(ilprocessor.Create(OpCodes.Call, elementReadFunc));
				ilprocessor.Append(ilprocessor.Create(OpCodes.Stobj, variable.GetElementType()));
				ilprocessor.Append(ilprocessor.Create(OpCodes.Ldloc_2));
				ilprocessor.Append(ilprocessor.Create(OpCodes.Ldc_I4_1));
				ilprocessor.Append(ilprocessor.Create(OpCodes.Add));
				ilprocessor.Append(ilprocessor.Create(OpCodes.Stloc_2));
				ilprocessor.Append(instruction2);
				ilprocessor.Append(ilprocessor.Create(OpCodes.Ldloc_2));
				ilprocessor.Append(ilprocessor.Create(OpCodes.Ldloc_0));
				ilprocessor.Append(ilprocessor.Create(OpCodes.Blt, instruction3));
				ilprocessor.Append(ilprocessor.Create(OpCodes.Ldloc_1));
				ilprocessor.Append(ilprocessor.Create(OpCodes.Ret));
				result = methodDefinition;
			}

			return result;
		}

		private static MethodDefinition GenerateArrayWriteFunc(TypeReference variable, MethodReference elementWriteFunc)
		{
			MethodDefinition result;
			if (!IsArrayType(variable))
			{
				Log.Error(variable.FullName + " is an unsupported array type. Jagged and multidimensional arrays are not supported");
				result = null;
			}
			else
			{
				var text = "_WriteArray" + variable.GetElementType().Name + "_";
				if (variable.DeclaringType != null)
				{
					text += variable.DeclaringType.Name;
				}
				else
				{
					text += "None";
				}

				var methodDefinition = new MethodDefinition(text, MethodAttributes.FamANDAssem | MethodAttributes.Family | MethodAttributes.Static | MethodAttributes.HideBySig, voidType);
				methodDefinition.Parameters.Add(new ParameterDefinition("writer", ParameterAttributes.None, scriptDef.MainModule.ImportReference(NetworkWriterType)));
				methodDefinition.Parameters.Add(new ParameterDefinition("value", ParameterAttributes.None, scriptDef.MainModule.ImportReference(variable)));
				methodDefinition.Body.Variables.Add(new VariableDefinition(uint16Type));
				methodDefinition.Body.Variables.Add(new VariableDefinition(uint16Type));
				methodDefinition.Body.InitLocals = true;
				var ilprocessor = methodDefinition.Body.GetILProcessor();
				var instruction = ilprocessor.Create(OpCodes.Nop);
				ilprocessor.Append(ilprocessor.Create(OpCodes.Ldarg_1));
				ilprocessor.Append(ilprocessor.Create(OpCodes.Brtrue, instruction));
				ilprocessor.Append(ilprocessor.Create(OpCodes.Ldarg_0));
				ilprocessor.Append(ilprocessor.Create(OpCodes.Ldc_I4_0));
				ilprocessor.Append(ilprocessor.Create(OpCodes.Call, NetworkWriteUInt16));
				ilprocessor.Append(ilprocessor.Create(OpCodes.Ret));
				ilprocessor.Append(instruction);
				ilprocessor.Append(ilprocessor.Create(OpCodes.Ldarg_1));
				ilprocessor.Append(ilprocessor.Create(OpCodes.Ldlen));
				ilprocessor.Append(ilprocessor.Create(OpCodes.Conv_I4));
				ilprocessor.Append(ilprocessor.Create(OpCodes.Conv_U2));
				ilprocessor.Append(ilprocessor.Create(OpCodes.Stloc_0));
				ilprocessor.Append(ilprocessor.Create(OpCodes.Ldarg_0));
				ilprocessor.Append(ilprocessor.Create(OpCodes.Ldloc_0));
				ilprocessor.Append(ilprocessor.Create(OpCodes.Call, NetworkWriteUInt16));
				ilprocessor.Append(ilprocessor.Create(OpCodes.Ldc_I4_0));
				ilprocessor.Append(ilprocessor.Create(OpCodes.Stloc_1));
				var instruction2 = ilprocessor.Create(OpCodes.Nop);
				ilprocessor.Append(ilprocessor.Create(OpCodes.Br, instruction2));
				var instruction3 = ilprocessor.Create(OpCodes.Nop);
				ilprocessor.Append(instruction3);
				ilprocessor.Append(ilprocessor.Create(OpCodes.Ldarg_0));
				ilprocessor.Append(ilprocessor.Create(OpCodes.Ldarg_1));
				ilprocessor.Append(ilprocessor.Create(OpCodes.Ldloc_1));
				ilprocessor.Append(ilprocessor.Create(OpCodes.Ldelema, variable.GetElementType()));
				ilprocessor.Append(ilprocessor.Create(OpCodes.Ldobj, variable.GetElementType()));
				ilprocessor.Append(ilprocessor.Create(OpCodes.Call, elementWriteFunc));
				ilprocessor.Append(ilprocessor.Create(OpCodes.Ldloc_1));
				ilprocessor.Append(ilprocessor.Create(OpCodes.Ldc_I4_1));
				ilprocessor.Append(ilprocessor.Create(OpCodes.Add));
				ilprocessor.Append(ilprocessor.Create(OpCodes.Conv_U2));
				ilprocessor.Append(ilprocessor.Create(OpCodes.Stloc_1));
				ilprocessor.Append(instruction2);
				ilprocessor.Append(ilprocessor.Create(OpCodes.Ldloc_1));
				ilprocessor.Append(ilprocessor.Create(OpCodes.Ldarg_1));
				ilprocessor.Append(ilprocessor.Create(OpCodes.Ldlen));
				ilprocessor.Append(ilprocessor.Create(OpCodes.Conv_I4));
				ilprocessor.Append(ilprocessor.Create(OpCodes.Blt, instruction3));
				ilprocessor.Append(ilprocessor.Create(OpCodes.Ret));
				result = methodDefinition;
			}

			return result;
		}

		private static MethodDefinition GenerateWriterFunction(TypeReference variable)
		{
			MethodDefinition result;
			if (!IsValidTypeToGenerate(variable.Resolve()))
			{
				result = null;
			}
			else
			{
				var text = "_Write" + variable.Name + "_";
				if (variable.DeclaringType != null)
				{
					text += variable.DeclaringType.Name;
				}
				else
				{
					text += "None";
				}

				var methodDefinition = new MethodDefinition(text, MethodAttributes.FamANDAssem | MethodAttributes.Family | MethodAttributes.Static | MethodAttributes.HideBySig, voidType);
				methodDefinition.Parameters.Add(new ParameterDefinition("writer", ParameterAttributes.None, scriptDef.MainModule.ImportReference(NetworkWriterType)));
				methodDefinition.Parameters.Add(new ParameterDefinition("value", ParameterAttributes.None, scriptDef.MainModule.ImportReference(variable)));
				var ilprocessor = methodDefinition.Body.GetILProcessor();
				var num = 0U;
				foreach (var fieldDefinition in variable.Resolve().Fields)
				{
					if (!fieldDefinition.IsStatic && !fieldDefinition.IsPrivate)
					{
						if (fieldDefinition.FieldType.Resolve().HasGenericParameters)
						{
							fail = true;
							Log.Error(string.Concat(new object[]
							{
								"WriteReadFunc for ",
								fieldDefinition.Name,
								" [",
								fieldDefinition.FieldType,
								"/",
								fieldDefinition.FieldType.FullName,
								"]. Cannot have generic parameters."
							}));
							return null;
						}

						if (fieldDefinition.FieldType.Resolve().IsInterface)
						{
							fail = true;
							Log.Error(string.Concat(new object[]
							{
								"WriteReadFunc for ",
								fieldDefinition.Name,
								" [",
								fieldDefinition.FieldType,
								"/",
								fieldDefinition.FieldType.FullName,
								"]. Cannot be an interface."
							}));
							return null;
						}

						var writeFunc = GetWriteFunc(fieldDefinition.FieldType);
						if (writeFunc == null)
						{
							Log.Error(string.Concat(new object[]
							{
								"WriteReadFunc for ",
								fieldDefinition.Name,
								" type ",
								fieldDefinition.FieldType,
								" no supported"
							}));
							fail = true;
							return null;
						}

						num += 1U;
						ilprocessor.Append(ilprocessor.Create(OpCodes.Ldarg_0));
						ilprocessor.Append(ilprocessor.Create(OpCodes.Ldarg_1));
						ilprocessor.Append(ilprocessor.Create(OpCodes.Ldfld, fieldDefinition));
						ilprocessor.Append(ilprocessor.Create(OpCodes.Call, writeFunc));
					}
				}

				if (num == 0U)
				{
					Log.Warning("The class / struct " + variable.Name + " has no public or non-static fields to serialize");
				}

				ilprocessor.Append(ilprocessor.Create(OpCodes.Ret));
				result = methodDefinition;
			}

			return result;
		}

		private static MethodDefinition GenerateReadFunction(TypeReference variable)
		{
			MethodDefinition result;
			if (!IsValidTypeToGenerate(variable.Resolve()))
			{
				result = null;
			}
			else
			{
				var text = "_Read" + variable.Name + "_";
				if (variable.DeclaringType != null)
				{
					text += variable.DeclaringType.Name;
				}
				else
				{
					text += "None";
				}

				var methodDefinition = new MethodDefinition(text, MethodAttributes.FamANDAssem | MethodAttributes.Family | MethodAttributes.Static | MethodAttributes.HideBySig, variable);
				methodDefinition.Body.Variables.Add(new VariableDefinition(variable));
				methodDefinition.Body.InitLocals = true;
				methodDefinition.Parameters.Add(new ParameterDefinition("reader", ParameterAttributes.None, scriptDef.MainModule.ImportReference(NetworkReaderType)));
				var ilprocessor = methodDefinition.Body.GetILProcessor();
				if (variable.IsValueType)
				{
					ilprocessor.Append(ilprocessor.Create(OpCodes.Ldloca, 0));
					ilprocessor.Append(ilprocessor.Create(OpCodes.Initobj, variable));
				}
				else
				{
					var methodDefinition2 = ResolveDefaultPublicCtor(variable);
					if (methodDefinition2 == null)
					{
						Log.Error("The class " + variable.Name + " has no default constructor or it's private, aborting.");
						return null;
					}

					ilprocessor.Append(ilprocessor.Create(OpCodes.Newobj, methodDefinition2));
					ilprocessor.Append(ilprocessor.Create(OpCodes.Stloc_0));
				}

				var num = 0U;
				foreach (var fieldDefinition in variable.Resolve().Fields)
				{
					if (!fieldDefinition.IsStatic && !fieldDefinition.IsPrivate)
					{
						if (variable.IsValueType)
						{
							ilprocessor.Append(ilprocessor.Create(OpCodes.Ldloca, 0));
						}
						else
						{
							ilprocessor.Append(ilprocessor.Create(OpCodes.Ldloc, 0));
						}

						var readFunc = GetReadFunc(fieldDefinition.FieldType);
						if (readFunc == null)
						{
							Log.Error(string.Concat(new object[]
							{
								"GetReadFunc for ",
								fieldDefinition.Name,
								" type ",
								fieldDefinition.FieldType,
								" no supported"
							}));
							fail = true;
							return null;
						}

						ilprocessor.Append(ilprocessor.Create(OpCodes.Ldarg_0));
						ilprocessor.Append(ilprocessor.Create(OpCodes.Call, readFunc));
						ilprocessor.Append(ilprocessor.Create(OpCodes.Stfld, fieldDefinition));
						num += 1U;
					}
				}

				if (num == 0U)
				{
					Log.Warning("The class / struct " + variable.Name + " has no public or non-static fields to serialize");
				}

				ilprocessor.Append(ilprocessor.Create(OpCodes.Ldloc_0));
				ilprocessor.Append(ilprocessor.Create(OpCodes.Ret));
				result = methodDefinition;
			}

			return result;
		}

		private static Instruction GetEventLoadInstruction(ModuleDefinition moduleDef, TypeDefinition td, MethodDefinition md, int iCount, FieldReference foundEventField)
		{
			while (iCount > 0)
			{
				iCount--;
				var instruction = md.Body.Instructions[iCount];
				if (instruction.OpCode == OpCodes.Ldfld)
				{
					if (instruction.Operand == foundEventField)
					{
						DLog(td, "    " + instruction.Operand, new object[0]);
						return instruction;
					}
				}
			}

			return null;
		}

		private static void ProcessInstructionMethod(ModuleDefinition moduleDef, TypeDefinition td, MethodDefinition md, Instruction instr, MethodReference opMethodRef, int iCount)
		{
			if (opMethodRef.Name == "Invoke")
			{
				var flag = false;
				while (iCount > 0 && !flag)
				{
					iCount--;
					var instruction = md.Body.Instructions[iCount];
					if (instruction.OpCode == OpCodes.Ldfld)
					{
						var fieldReference = instruction.Operand as FieldReference;
						for (var i = 0; i < lists.replacedEvents.Count; i++)
						{
							var eventDefinition = lists.replacedEvents[i];
							if (eventDefinition.Name == fieldReference.Name)
							{
								instr.Operand = lists.replacementEvents[i];
								instruction.OpCode = OpCodes.Nop;
								flag = true;
								break;
							}
						}
					}
				}
			}
			else if (lists.replacementMethodNames.Contains(opMethodRef.FullName))
			{
				for (var j = 0; j < lists.replacedMethods.Count; j++)
				{
					var methodDefinition = lists.replacedMethods[j];
					if (opMethodRef.FullName == methodDefinition.FullName)
					{
						instr.Operand = lists.replacementMethods[j];
						break;
					}
				}
			}
		}

		private static void ConfirmGeneratedCodeClass(ModuleDefinition moduleDef)
		{
			if (lists.generateContainerClass == null)
			{
				lists.generateContainerClass = new TypeDefinition("Unity", "GeneratedNetworkCode", TypeAttributes.Public | TypeAttributes.AutoClass | TypeAttributes.BeforeFieldInit, objectType);
				var methodDefinition = new MethodDefinition(".ctor", MethodAttributes.FamANDAssem | MethodAttributes.Family | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, voidType);
				methodDefinition.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_0));
				methodDefinition.Body.Instructions.Add(Instruction.Create(OpCodes.Call, ResolveMethod(objectType, ".ctor")));
				methodDefinition.Body.Instructions.Add(Instruction.Create(OpCodes.Ret));
				lists.generateContainerClass.Methods.Add(methodDefinition);
			}
		}

		private static void ProcessInstructionField(TypeDefinition td, MethodDefinition md, Instruction i, FieldDefinition opField)
		{
			if (!(md.Name == ".ctor") && !(md.Name == "OnDeserialize"))
			{
				for (var j = 0; j < lists.replacedFields.Count; j++)
				{
					var fieldDefinition = lists.replacedFields[j];
					if (opField == fieldDefinition)
					{
						i.OpCode = OpCodes.Call;
						i.Operand = lists.replacementProperties[j];
						break;
					}
				}
			}
		}

		private static void ProcessInstruction(ModuleDefinition moduleDef, TypeDefinition td, MethodDefinition md, Instruction i, int iCount)
		{
			if (i.OpCode == OpCodes.Call || i.OpCode == OpCodes.Callvirt)
			{
				var methodReference = i.Operand as MethodReference;
				if (methodReference != null)
				{
					ProcessInstructionMethod(moduleDef, td, md, i, methodReference, iCount);
				}
			}

			if (i.OpCode == OpCodes.Stfld)
			{
				var fieldDefinition = i.Operand as FieldDefinition;
				if (fieldDefinition != null)
				{
					ProcessInstructionField(td, md, i, fieldDefinition);
				}
			}
		}

		private static void InjectGuardParameters(MethodDefinition md, ILProcessor worker, Instruction top)
		{
			var num = (!md.Resolve().IsStatic) ? 1 : 0;
			for (var i = 0; i < md.Parameters.Count; i++)
			{
				var parameterDefinition = md.Parameters[i];
				if (parameterDefinition.IsOut)
				{
					var elementType = parameterDefinition.ParameterType.GetElementType();
					if (elementType.IsPrimitive)
					{
						worker.InsertBefore(top, worker.Create(OpCodes.Ldarg, i + num));
						worker.InsertBefore(top, worker.Create(OpCodes.Ldc_I4_0));
						worker.InsertBefore(top, worker.Create(OpCodes.Stind_I4));
					}
					else
					{
						md.Body.Variables.Add(new VariableDefinition(elementType));
						md.Body.InitLocals = true;
						worker.InsertBefore(top, worker.Create(OpCodes.Ldarg, i + num));
						worker.InsertBefore(top, worker.Create(OpCodes.Ldloca_S, (byte)(md.Body.Variables.Count - 1)));
						worker.InsertBefore(top, worker.Create(OpCodes.Initobj, elementType));
						worker.InsertBefore(top, worker.Create(OpCodes.Ldloc, md.Body.Variables.Count - 1));
						worker.InsertBefore(top, worker.Create(OpCodes.Stobj, elementType));
					}
				}
			}
		}

		private static void InjectGuardReturnValue(MethodDefinition md, ILProcessor worker, Instruction top)
		{
			if (md.ReturnType.FullName != voidType.FullName)
			{
				if (md.ReturnType.IsPrimitive)
				{
					worker.InsertBefore(top, worker.Create(OpCodes.Ldc_I4_0));
				}
				else
				{
					md.Body.Variables.Add(new VariableDefinition(md.ReturnType));
					md.Body.InitLocals = true;
					worker.InsertBefore(top, worker.Create(OpCodes.Ldloca_S, (byte)(md.Body.Variables.Count - 1)));
					worker.InsertBefore(top, worker.Create(OpCodes.Initobj, md.ReturnType));
					worker.InsertBefore(top, worker.Create(OpCodes.Ldloc, md.Body.Variables.Count - 1));
				}
			}
		}

		private static void InjectServerGuard(ModuleDefinition moduleDef, TypeDefinition td, MethodDefinition md, bool logWarning)
		{
			if (!IsNetworkBehaviour(td))
			{
				Log.Error("[Server] guard on non-NetworkBehaviour script at [" + md.FullName + "]");
			}
			else
			{
				var ilprocessor = md.Body.GetILProcessor();
				var instruction = md.Body.Instructions[0];
				ilprocessor.InsertBefore(instruction, ilprocessor.Create(OpCodes.Call, NetworkServerGetActive));
				ilprocessor.InsertBefore(instruction, ilprocessor.Create(OpCodes.Brtrue, instruction));
				if (logWarning)
				{
					ilprocessor.InsertBefore(instruction, ilprocessor.Create(OpCodes.Ldstr, "[Server] function '" + md.FullName + "' called on client"));
					ilprocessor.InsertBefore(instruction, ilprocessor.Create(OpCodes.Call, logWarningReference));
				}

				InjectGuardParameters(md, ilprocessor, instruction);
				InjectGuardReturnValue(md, ilprocessor, instruction);
				ilprocessor.InsertBefore(instruction, ilprocessor.Create(OpCodes.Ret));
			}
		}

		private static void InjectClientGuard(ModuleDefinition moduleDef, TypeDefinition td, MethodDefinition md, bool logWarning)
		{
			if (!IsNetworkBehaviour(td))
			{
				Log.Error("[Client] guard on non-NetworkBehaviour script at [" + md.FullName + "]");
			}
			else
			{
				var ilprocessor = md.Body.GetILProcessor();
				var instruction = md.Body.Instructions[0];
				ilprocessor.InsertBefore(instruction, ilprocessor.Create(OpCodes.Call, NetworkClientGetActive));
				ilprocessor.InsertBefore(instruction, ilprocessor.Create(OpCodes.Brtrue, instruction));
				if (logWarning)
				{
					ilprocessor.InsertBefore(instruction, ilprocessor.Create(OpCodes.Ldstr, "[Client] function '" + md.FullName + "' called on server"));
					ilprocessor.InsertBefore(instruction, ilprocessor.Create(OpCodes.Call, logWarningReference));
				}

				InjectGuardParameters(md, ilprocessor, instruction);
				InjectGuardReturnValue(md, ilprocessor, instruction);
				ilprocessor.InsertBefore(instruction, ilprocessor.Create(OpCodes.Ret));
			}
		}

		private static void ProcessSiteMethod(ModuleDefinition moduleDef, TypeDefinition td, MethodDefinition md)
		{
			if (!(md.Name == ".cctor") && !(md.Name == "OnUnserializeVars"))
			{
				var text = md.Name.Substring(0, Math.Min(md.Name.Length, 4));
				if (!(text == "UNet"))
				{
					text = md.Name.Substring(0, Math.Min(md.Name.Length, 7));
					if (!(text == "CallCmd"))
					{
						text = md.Name.Substring(0, Math.Min(md.Name.Length, 9));
						if (!(text == "InvokeCmd") && !(text == "InvokeRpc") && !(text == "InvokeSyn"))
						{
							if (md.Body != null && md.Body.Instructions != null)
							{
								foreach (var customAttribute in md.CustomAttributes)
								{
									if (customAttribute.Constructor.DeclaringType.ToString() == "UnityEngine.Networking.ServerAttribute")
									{
										InjectServerGuard(moduleDef, td, md, true);
									}
									else if (customAttribute.Constructor.DeclaringType.ToString() == "UnityEngine.Networking.ServerCallbackAttribute")
									{
										InjectServerGuard(moduleDef, td, md, false);
									}
									else if (customAttribute.Constructor.DeclaringType.ToString() == "UnityEngine.Networking.ClientAttribute")
									{
										InjectClientGuard(moduleDef, td, md, true);
									}
									else if (customAttribute.Constructor.DeclaringType.ToString() == "UnityEngine.Networking.ClientCallbackAttribute")
									{
										InjectClientGuard(moduleDef, td, md, false);
									}
								}

								var num = 0;
								foreach (var i in md.Body.Instructions)
								{
									ProcessInstruction(moduleDef, td, md, i, num);
									num++;
								}
							}
						}
					}
				}
			}
		}

		private static void ProcessSiteClass(ModuleDefinition moduleDef, TypeDefinition td)
		{
			foreach (var md in td.Methods)
			{
				ProcessSiteMethod(moduleDef, td, md);
			}

			foreach (var td2 in td.NestedTypes)
			{
				ProcessSiteClass(moduleDef, td2);
			}
		}

		private static void ProcessSitesModule(ModuleDefinition moduleDef)
		{
			var now = DateTime.Now;
			foreach (var typeDefinition in moduleDef.Types)
			{
				if (typeDefinition.IsClass)
				{
					ProcessSiteClass(moduleDef, typeDefinition);
				}
			}

			if (lists.generateContainerClass != null)
			{
				moduleDef.Types.Add(lists.generateContainerClass);
				scriptDef.MainModule.ImportReference(lists.generateContainerClass);
				foreach (var method in lists.generatedReadFunctions)
				{
					scriptDef.MainModule.ImportReference(method);
				}

				foreach (var method2 in lists.generatedWriteFunctions)
				{
					scriptDef.MainModule.ImportReference(method2);
				}
			}

			Console.WriteLine(string.Concat(new object[]
			{
				"  ProcessSitesModule ",
				moduleDef.Name,
				" elapsed time:",
				DateTime.Now - now
			}));
		}

		private static void ProcessPropertySites() => ProcessSitesModule(scriptDef.MainModule);

		private static bool ProcessMessageType(TypeDefinition td)
		{
			var messageClassProcessor = new MessageClassProcessor(td);
			messageClassProcessor.Process();
			return true;
		}

		private static bool ProcessSyncListStructType(TypeDefinition td)
		{
			var syncListStructProcessor = new SyncListStructProcessor(td);
			syncListStructProcessor.Process();
			return true;
		}

		private static void ProcessMonoBehaviourType(TypeDefinition td)
		{
			var monoBehaviourProcessor = new MonoBehaviourProcessor(td);
			monoBehaviourProcessor.Process();
		}

		private static bool ProcessNetworkBehaviourType(TypeDefinition td)
		{
			foreach (var methodDefinition in td.Resolve().Methods)
			{
				if (methodDefinition.Name == "UNetVersion")
				{
					DLog(td, " Already processed", new object[0]);
					return false;
				}
			}

			DLog(td, "Found NetworkBehaviour " + td.FullName, new object[0]);
			var networkBehaviourProcessor = new NetworkBehaviourProcessor(td);
			networkBehaviourProcessor.Process();
			return true;
		}

		public static MethodReference ResolveMethod(TypeReference t, string name)
		{
			MethodReference result;
			if (t == null)
			{
				Log.Error("Type missing for " + name);
				fail = true;
				result = null;
			}
			else
			{
				foreach (var methodDefinition in t.Resolve().Methods)
				{
					if (methodDefinition.Name == name)
					{
						return scriptDef.MainModule.ImportReference(methodDefinition);
					}
				}

				Log.Error($"ResolveMethod failed - Couldn't find {name} in {t.Name}");
				foreach (var methodDefinition2 in t.Resolve().Methods)
				{
					Log.Error("- has method " + methodDefinition2.Name);
				}

				fail = true;
				result = null;
			}

			return result;
		}

		private static MethodReference ResolveMethodWithArg(TypeReference t, string name, TypeReference argType)
		{
			foreach (var methodDefinition in t.Resolve().Methods)
			{
				if (methodDefinition.Name == name)
				{
					if (methodDefinition.Parameters.Count == 1)
					{
						if (methodDefinition.Parameters[0].ParameterType.FullName == argType.FullName)
						{
							return scriptDef.MainModule.ImportReference(methodDefinition);
						}
					}
				}
			}

			Log.Error(string.Concat(new object[]
			{
				"ResolveMethodWithArg failed ",
				t.Name,
				"::",
				name,
				" ",
				argType
			}));
			fail = true;
			return null;
		}

		private static MethodDefinition ResolveDefaultPublicCtor(TypeReference variable)
		{
			foreach (var methodDefinition in variable.Resolve().Methods)
			{
				if (methodDefinition.Name == ".ctor" && methodDefinition.Resolve().IsPublic && methodDefinition.Parameters.Count == 0)
				{
					return methodDefinition;
				}
			}

			return null;
		}

		private static GenericInstanceMethod ResolveMethodGeneric(TypeReference t, string name, TypeReference genericType)
		{
			foreach (var methodDefinition in t.Resolve().Methods)
			{
				if (methodDefinition.Name == name)
				{
					if (methodDefinition.Parameters.Count == 0)
					{
						if (methodDefinition.GenericParameters.Count == 1)
						{
							var method = scriptDef.MainModule.ImportReference(methodDefinition);
							var genericInstanceMethod = new GenericInstanceMethod(method);
							genericInstanceMethod.GenericArguments.Add(genericType);
							if (genericInstanceMethod.GenericArguments[0].FullName == genericType.FullName)
							{
								return genericInstanceMethod;
							}
						}
					}
				}
			}

			Log.Error(string.Concat(new object[]
			{
				"ResolveMethodGeneric failed ",
				t.Name,
				"::",
				name,
				" ",
				genericType
			}));
			fail = true;
			return null;
		}

		public static FieldReference ResolveField(TypeReference t, string name)
		{
			foreach (var fieldDefinition in t.Resolve().Fields)
			{
				if (fieldDefinition.Name == name)
				{
					return scriptDef.MainModule.ImportReference(fieldDefinition);
				}
			}

			return null;
		}

		public static MethodReference ResolveProperty(TypeReference t, string name)
		{
			foreach (var propertyDefinition in t.Resolve().Properties)
			{
				if (propertyDefinition.Name == name)
				{
					return scriptDef.MainModule.ImportReference(propertyDefinition.GetMethod);
				}
			}

			Log.Error($"ResolveProperty failed - Couldn't find {name} in {t.Name}");
			return null;
		}

		private static void SetupUnityTypes()
		{
			vector2Type = UnityAssemblyDefinition.MainModule.GetType("UnityEngine.Vector2");
			if (vector2Type == null)
			{
				Log.Error("Vector2Type is null!");
			}

			vector3Type = UnityAssemblyDefinition.MainModule.GetType("UnityEngine.Vector3");
			vector4Type = UnityAssemblyDefinition.MainModule.GetType("UnityEngine.Vector4");
			colorType = UnityAssemblyDefinition.MainModule.GetType("UnityEngine.Color");
			color32Type = UnityAssemblyDefinition.MainModule.GetType("UnityEngine.Color32");
			quaternionType = UnityAssemblyDefinition.MainModule.GetType("UnityEngine.Quaternion");
			rectType = UnityAssemblyDefinition.MainModule.GetType("UnityEngine.Rect");
			planeType = UnityAssemblyDefinition.MainModule.GetType("UnityEngine.Plane");
			rayType = UnityAssemblyDefinition.MainModule.GetType("UnityEngine.Ray");
			matrixType = UnityAssemblyDefinition.MainModule.GetType("UnityEngine.Matrix4x4");
			gameObjectType = UnityAssemblyDefinition.MainModule.GetType("UnityEngine.GameObject");
			if (gameObjectType == null)
			{
				Log.Error("GameObjectType is null!");
			}

			transformType = UnityAssemblyDefinition.MainModule.GetType("UnityEngine.Transform");
			unityObjectType = UnityAssemblyDefinition.MainModule.GetType("UnityEngine.Object");

			hashType = UNetAssemblyDefinition.MainModule.GetType("UnityEngine.Networking.NetworkHash128");

			NetworkClientType = QNetAssemblyDefinition.MainModule.GetType("QuantumUNET.QNetworkClient");
			NetworkServerType = QNetAssemblyDefinition.MainModule.GetType("QuantumUNET.QNetworkServer");
			NetworkCRCType = QNetAssemblyDefinition.MainModule.GetType("QuantumUNET.QNetworkCRC");

			SyncVarType = UNetAssemblyDefinition.MainModule.GetType("UnityEngine.Networking.SyncVarAttribute");

			CommandType = UNetAssemblyDefinition.MainModule.GetType("UnityEngine.Networking.CommandAttribute");

			ClientRpcType = UNetAssemblyDefinition.MainModule.GetType("UnityEngine.Networking.ClientRpcAttribute");

			TargetRpcType = UNetAssemblyDefinition.MainModule.GetType("UnityEngine.Networking.TargetRpcAttribute");

			SyncEventType = UNetAssemblyDefinition.MainModule.GetType("UnityEngine.Networking.SyncEventAttribute");

			SyncListType = UNetAssemblyDefinition.MainModule.GetType("UnityEngine.Networking.SyncList`1");

			NetworkSettingsType = UNetAssemblyDefinition.MainModule.GetType("UnityEngine.Networking.NetworkSettingsAttribute");

			SyncListFloatType = UNetAssemblyDefinition.MainModule.GetType("UnityEngine.Networking.SyncListFloat");

			SyncListIntType = UNetAssemblyDefinition.MainModule.GetType("UnityEngine.Networking.SyncListInt");

			SyncListUIntType = UNetAssemblyDefinition.MainModule.GetType("UnityEngine.Networking.SyncListUInt");

			SyncListBoolType = UNetAssemblyDefinition.MainModule.GetType("UnityEngine.Networking.SyncListBool");

			SyncListStringType = UNetAssemblyDefinition.MainModule.GetType("UnityEngine.Networking.SyncListString");

		}

		private static void SetupCorLib()
		{
			var name = AssemblyNameReference.Parse("mscorlib");
			var parameters = new ReaderParameters
			{
				AssemblyResolver = scriptDef.MainModule.AssemblyResolver
			};
			corLib = scriptDef.MainModule.AssemblyResolver.Resolve(name, parameters).MainModule;
		}

		private static TypeReference ImportCorLibType(string fullName)
		{
			var type = corLib.GetType(fullName) ?? Enumerable.First<ExportedType>(corLib.ExportedTypes, (ExportedType t) => t.FullName == fullName).Resolve();
			return scriptDef.MainModule.ImportReference(type);
		}

		private static void SetupTargetTypes()
		{
			SetupCorLib();
			voidType = ImportCorLibType("System.Void");
			singleType = ImportCorLibType("System.Single");
			doubleType = ImportCorLibType("System.Double");
			decimalType = ImportCorLibType("System.Decimal");
			boolType = ImportCorLibType("System.Boolean");
			stringType = ImportCorLibType("System.String");
			int64Type = ImportCorLibType("System.Int64");
			uint64Type = ImportCorLibType("System.UInt64");
			int32Type = ImportCorLibType("System.Int32");
			uint32Type = ImportCorLibType("System.UInt32");
			int16Type = ImportCorLibType("System.Int16");
			uint16Type = ImportCorLibType("System.UInt16");
			byteType = ImportCorLibType("System.Byte");
			sbyteType = ImportCorLibType("System.SByte");
			charType = ImportCorLibType("System.Char");
			objectType = ImportCorLibType("System.Object");
			valueTypeType = ImportCorLibType("System.ValueType");
			typeType = ImportCorLibType("System.Type");
			IEnumeratorType = ImportCorLibType("System.Collections.IEnumerator");
			MemoryStreamType = ImportCorLibType("System.IO.MemoryStream");

			NetworkReaderType = QNetAssemblyDefinition.MainModule.GetType("QuantumUNET.Transport.QNetworkReader");

			NetworkReaderDef = NetworkReaderType.Resolve();
			NetworkReaderCtor = ResolveMethod(NetworkReaderDef, ".ctor");

			NetworkWriterType = QNetAssemblyDefinition.MainModule.GetType("QuantumUNET.Transport.QNetworkWriter");

			NetworkWriterDef = NetworkWriterType.Resolve();
			NetworkWriterCtor = ResolveMethod(NetworkWriterDef, ".ctor");
			MemoryStreamCtor = ResolveMethod(MemoryStreamType, ".ctor");

			NetworkInstanceIdType = UNetAssemblyDefinition.MainModule.GetType("UnityEngine.Networking.NetworkInstanceId");
			NetworkSceneIdType = UNetAssemblyDefinition.MainModule.GetType("UnityEngine.Networking.NetworkSceneId");
			NetworkInstanceIdType = UNetAssemblyDefinition.MainModule.GetType("UnityEngine.Networking.NetworkInstanceId");
			NetworkSceneIdType = UNetAssemblyDefinition.MainModule.GetType("UnityEngine.Networking.NetworkSceneId");

			NetworkServerGetActive = ResolveMethod(NetworkServerType, "get_active");
			NetworkServerGetLocalClientActive = ResolveMethod(NetworkServerType, "get_localClientActive");
			NetworkClientGetActive = ResolveMethod(NetworkClientType, "get_active");
			NetworkReaderReadInt32 = ResolveMethod(NetworkReaderType, "ReadInt32");
			NetworkWriterWriteInt32 = ResolveMethodWithArg(NetworkWriterType, "Write", int32Type);
			NetworkWriterWriteInt16 = ResolveMethodWithArg(NetworkWriterType, "Write", int16Type);
			NetworkReaderReadPacked32 = ResolveMethod(NetworkReaderType, "ReadPackedUInt32");
			NetworkReaderReadPacked64 = ResolveMethod(NetworkReaderType, "ReadPackedUInt64");
			NetworkReaderReadByte = ResolveMethod(NetworkReaderType, "ReadByte");
			NetworkWriterWritePacked32 = ResolveMethod(NetworkWriterType, "WritePackedUInt32");
			NetworkWriterWritePacked64 = ResolveMethod(NetworkWriterType, "WritePackedUInt64");
			NetworkWriterWriteNetworkInstanceId = ResolveMethodWithArg(NetworkWriterType, "Write", NetworkInstanceIdType);
			NetworkWriterWriteNetworkSceneId = ResolveMethodWithArg(NetworkWriterType, "Write", NetworkSceneIdType);
			NetworkWriterStartMessage = ResolveMethod(NetworkWriterType, "StartMessage");
			NetworkWriterFinishMessage = ResolveMethod(NetworkWriterType, "FinishMessage");
			NetworkReaderReadNetworkInstanceId = ResolveMethod(NetworkReaderType, "ReadNetworkId");
			NetworkReaderReadNetworkSceneId = ResolveMethod(NetworkReaderType, "ReadSceneId");
			NetworkInstanceIsEmpty = ResolveMethod(NetworkInstanceIdType, "IsEmpty");
			NetworkReadUInt16 = ResolveMethod(NetworkReaderType, "ReadUInt16");
			NetworkWriteUInt16 = ResolveMethodWithArg(NetworkWriterType, "Write", uint16Type);

			CmdDelegateReference = QNetAssemblyDefinition.MainModule.GetType("QuantumUNET.QNetworkBehaviour/CmdDelegate");

			CmdDelegateConstructor = ResolveMethod(CmdDelegateReference, ".ctor");
			Console.WriteLine("gameobject");
			scriptDef.MainModule.ImportReference(gameObjectType);
			Console.WriteLine("transform");
			scriptDef.MainModule.ImportReference(transformType);

			TypeReference type = QNetAssemblyDefinition.MainModule.GetType("QuantumUNET.Components.QNetworkIdentity");

			Console.WriteLine("type");
			NetworkIdentityType = scriptDef.MainModule.ImportReference(type);
			Console.WriteLine("networkinstanceidtype");
			NetworkInstanceIdType = scriptDef.MainModule.ImportReference(NetworkInstanceIdType);
			SyncListFloatReadType = ResolveMethod(SyncListFloatType, "ReadReference");
			SyncListIntReadType = ResolveMethod(SyncListIntType, "ReadReference");
			SyncListUIntReadType = ResolveMethod(SyncListUIntType, "ReadReference");
			SyncListBoolReadType = ResolveMethod(SyncListBoolType, "ReadReference");
			SyncListStringReadType = ResolveMethod(SyncListStringType, "ReadReference");
			SyncListFloatWriteType = ResolveMethod(SyncListFloatType, "WriteInstance");
			SyncListIntWriteType = ResolveMethod(SyncListIntType, "WriteInstance");
			SyncListUIntWriteType = ResolveMethod(SyncListUIntType, "WriteInstance");
			SyncListBoolWriteType = ResolveMethod(SyncListBoolType, "WriteInstance");
			SyncListStringWriteType = ResolveMethod(SyncListStringType, "WriteInstance");

			NetworkBehaviourType = QNetAssemblyDefinition.MainModule.GetType("QuantumUNET.QNetworkBehaviour");

			Console.WriteLine("networkbehaviourtype");
			NetworkBehaviourType2 = scriptDef.MainModule.ImportReference(NetworkBehaviourType);

			NetworkConnectionType = QNetAssemblyDefinition.MainModule.GetType("QuantumUNET.QNetworkConnection");

			MonoBehaviourType = UnityAssemblyDefinition.MainModule.GetType("UnityEngine.MonoBehaviour");
			ScriptableObjectType = UnityAssemblyDefinition.MainModule.GetType("UnityEngine.ScriptableObject");

			NetworkConnectionType = QNetAssemblyDefinition.MainModule.GetType("QuantumUNET.QNetworkConnection");

			NetworkConnectionType = scriptDef.MainModule.ImportReference(NetworkConnectionType);

			ULocalConnectionToServerType = QNetAssemblyDefinition.MainModule.GetType("QuantumUNET.QULocalConnectionToServer");

			ULocalConnectionToServerType = scriptDef.MainModule.ImportReference(ULocalConnectionToServerType);

			ULocalConnectionToClientType = QNetAssemblyDefinition.MainModule.GetType("QuantumUNET.QULocalConnectionToClient");

			ULocalConnectionToClientType = scriptDef.MainModule.ImportReference(ULocalConnectionToClientType);

			MessageBaseType = QNetAssemblyDefinition.MainModule.GetType("QuantumUNET.Messages.QMessageBase");
			SyncListStructType = UNetAssemblyDefinition.MainModule.GetType("UnityEngine.Networking.SyncListStruct`1");

			NetworkBehaviourDirtyBitsReference = ResolveProperty(NetworkBehaviourType, "SyncVarDirtyBits");
			NetworkBehaviourOnSerialize = ResolveMethod(NetworkBehaviourType, "OnSerialize");
			NetworkBehaviourClearAllDirtyBits = ResolveMethod(NetworkBehaviourType, "ClearAllDirtyBits");
			NetworkBehaviourGetNetworkChannel = ResolveMethod(NetworkBehaviourType, "GetNetworkChannel");
			NetworkBehaviourClientSendUpdateVars = ResolveMethod(NetworkBehaviourType, "ClientSendUpdateVars");
			NetworkConnectionSendWriter = ResolveMethod(NetworkConnectionType, "SendWriter");
			ComponentType = UnityAssemblyDefinition.MainModule.GetType("UnityEngine.Component");

			ClientSceneType = QNetAssemblyDefinition.MainModule.GetType("QuantumUNET.QClientScene");

			FindLocalObjectReference = ResolveMethod(ClientSceneType, "FindLocalObject");
			RegisterBehaviourReference = ResolveMethod(NetworkCRCType, "RegisterBehaviour");
			ReadyConnectionReference = ResolveMethod(ClientSceneType, "get_readyConnection");
			getComponentReference = ResolveMethodGeneric(ComponentType, "GetComponent", NetworkIdentityType);
			getUNetIdReference = ResolveMethod(type, "get_NetId");
			gameObjectInequality = ResolveMethod(unityObjectType, "op_Inequality");
			UBehaviourIsServer = ResolveMethod(NetworkBehaviourType, "get_IsServer");
			getPlayerIdReference = ResolveMethod(NetworkBehaviourType, "get_PlayerControllerId");
			setSyncVarReference = ResolveMethod(NetworkBehaviourType, "SetSyncVar");
			setSyncVarHookGuard = ResolveMethod(NetworkBehaviourType, "set_SyncVarHookGuard");
			getSyncVarHookGuard = ResolveMethod(NetworkBehaviourType, "get_SyncVarHookGuard");
			setSyncVarGameObjectReference = ResolveMethod(NetworkBehaviourType, "SetSyncVarGameObject");
			registerCommandDelegateReference = ResolveMethod(NetworkBehaviourType, "RegisterCommandDelegate");
			registerRpcDelegateReference = ResolveMethod(NetworkBehaviourType, "RegisterRpcDelegate");
			registerEventDelegateReference = ResolveMethod(NetworkBehaviourType, "RegisterEventDelegate");
			registerSyncListDelegateReference = ResolveMethod(NetworkBehaviourType, "RegisterSyncListDelegate");
			getTypeReference = ResolveMethod(objectType, "GetType");
			getTypeFromHandleReference = ResolveMethod(typeType, "GetTypeFromHandle");
			logErrorReference = ResolveMethod(UnityAssemblyDefinition.MainModule.GetType("UnityEngine.Debug"), "LogError");
			logWarningReference = ResolveMethod(UnityAssemblyDefinition.MainModule.GetType("UnityEngine.Debug"), "LogWarning");
			sendCommandInternal = ResolveMethod(NetworkBehaviourType, "SendCommandInternal");
			sendRpcInternal = ResolveMethod(NetworkBehaviourType, "SendRPCInternal");
			sendTargetRpcInternal = ResolveMethod(NetworkBehaviourType, "SendTargetRPCInternal");
			sendEventInternal = ResolveMethod(NetworkBehaviourType, "SendEventInternal");
			Console.WriteLine("synclisttype");
			SyncListType = scriptDef.MainModule.ImportReference(SyncListType);
			SyncListInitBehaviourReference = ResolveMethod(SyncListType, "InitializeBehaviour");
			SyncListInitHandleMsg = ResolveMethod(SyncListType, "HandleMsg");
			SyncListClear = ResolveMethod(SyncListType, "Clear");
		}

		private static void SetupReadFunctions()
		{
			var weaverLists = lists;
			var dictionary = new Dictionary<string, MethodReference>
			{
				{ singleType.FullName, ResolveMethod(NetworkReaderType, "ReadSingle") },
				{ doubleType.FullName, ResolveMethod(NetworkReaderType, "ReadDouble") },
				{ boolType.FullName, ResolveMethod(NetworkReaderType, "ReadBoolean") },
				{ stringType.FullName, ResolveMethod(NetworkReaderType, "ReadString") },
				{ int64Type.FullName, NetworkReaderReadPacked64 },
				{ uint64Type.FullName, NetworkReaderReadPacked64 },
				{ int32Type.FullName, NetworkReaderReadPacked32 },
				{ uint32Type.FullName, NetworkReaderReadPacked32 },
				{ int16Type.FullName, NetworkReaderReadPacked32 },
				{ uint16Type.FullName, NetworkReaderReadPacked32 },
				{ byteType.FullName, NetworkReaderReadPacked32 },
				{ sbyteType.FullName, NetworkReaderReadPacked32 },
				{ charType.FullName, NetworkReaderReadPacked32 },
				{ decimalType.FullName, ResolveMethod(NetworkReaderType, "ReadDecimal") },
				{ vector2Type.FullName, ResolveMethod(NetworkReaderType, "ReadVector2") },
				{ vector3Type.FullName, ResolveMethod(NetworkReaderType, "ReadVector3") },
				{ vector4Type.FullName, ResolveMethod(NetworkReaderType, "ReadVector4") },
				{ colorType.FullName, ResolveMethod(NetworkReaderType, "ReadColor") },
				{ color32Type.FullName, ResolveMethod(NetworkReaderType, "ReadColor32") },
				{ quaternionType.FullName, ResolveMethod(NetworkReaderType, "ReadQuaternion") },
				{ rectType.FullName, ResolveMethod(NetworkReaderType, "ReadRect") },
				{ planeType.FullName, ResolveMethod(NetworkReaderType, "ReadPlane") },
				{ rayType.FullName, ResolveMethod(NetworkReaderType, "ReadRay") },
				{ matrixType.FullName, ResolveMethod(NetworkReaderType, "ReadMatrix4x4") },
				{ hashType.FullName, ResolveMethod(NetworkReaderType, "ReadNetworkHash128") },
				{ gameObjectType.FullName, ResolveMethod(NetworkReaderType, "ReadGameObject") },
				{ NetworkIdentityType.FullName, ResolveMethod(NetworkReaderType, "ReadNetworkIdentity") },
				{ NetworkInstanceIdType.FullName, NetworkReaderReadNetworkInstanceId },
				{ NetworkSceneIdType.FullName, NetworkReaderReadNetworkSceneId },
				{ transformType.FullName, ResolveMethod(NetworkReaderType, "ReadTransform") },
				{ "System.Byte[]", ResolveMethod(NetworkReaderType, "ReadBytesAndSize") }
			};
			weaverLists.readFuncs = dictionary;
			var weaverLists2 = lists;
			dictionary = new Dictionary<string, MethodReference>
			{
				{ SyncListFloatType.FullName, SyncListFloatReadType },
				{ SyncListIntType.FullName, SyncListIntReadType },
				{ SyncListUIntType.FullName, SyncListUIntReadType },
				{ SyncListBoolType.FullName, SyncListBoolReadType },
				{ SyncListStringType.FullName, SyncListStringReadType }
			};
			weaverLists2.readByReferenceFuncs = dictionary;
		}

		private static void SetupWriteFunctions()
		{
			var weaverLists = lists;
			var dictionary = new Dictionary<string, MethodReference>
			{
				{ singleType.FullName, ResolveMethodWithArg(NetworkWriterType, "Write", singleType) },
				{ doubleType.FullName, ResolveMethodWithArg(NetworkWriterType, "Write", doubleType) },
				{ boolType.FullName, ResolveMethodWithArg(NetworkWriterType, "Write", boolType) },
				{ stringType.FullName, ResolveMethodWithArg(NetworkWriterType, "Write", stringType) },
				{ int64Type.FullName, NetworkWriterWritePacked64 },
				{ uint64Type.FullName, NetworkWriterWritePacked64 },
				{ int32Type.FullName, NetworkWriterWritePacked32 },
				{ uint32Type.FullName, NetworkWriterWritePacked32 },
				{ int16Type.FullName, NetworkWriterWritePacked32 },
				{ uint16Type.FullName, NetworkWriterWritePacked32 },
				{ byteType.FullName, NetworkWriterWritePacked32 },
				{ sbyteType.FullName, NetworkWriterWritePacked32 },
				{ charType.FullName, NetworkWriterWritePacked32 },
				{ decimalType.FullName, ResolveMethodWithArg(NetworkWriterType, "Write", decimalType) },
				{ vector2Type.FullName, ResolveMethodWithArg(NetworkWriterType, "Write", vector2Type) },
				{ vector3Type.FullName, ResolveMethodWithArg(NetworkWriterType, "Write", vector3Type) },
				{ vector4Type.FullName, ResolveMethodWithArg(NetworkWriterType, "Write", vector4Type) },
				{ colorType.FullName, ResolveMethodWithArg(NetworkWriterType, "Write", colorType) },
				{ color32Type.FullName, ResolveMethodWithArg(NetworkWriterType, "Write", color32Type) },
				{ quaternionType.FullName, ResolveMethodWithArg(NetworkWriterType, "Write", quaternionType) },
				{ rectType.FullName, ResolveMethodWithArg(NetworkWriterType, "Write", rectType) },
				{ planeType.FullName, ResolveMethodWithArg(NetworkWriterType, "Write", planeType) },
				{ rayType.FullName, ResolveMethodWithArg(NetworkWriterType, "Write", rayType) },
				{ matrixType.FullName, ResolveMethodWithArg(NetworkWriterType, "Write", matrixType) },
				{ hashType.FullName, ResolveMethodWithArg(NetworkWriterType, "Write", hashType) },
				{ gameObjectType.FullName, ResolveMethodWithArg(NetworkWriterType, "Write", gameObjectType) },
				{ NetworkIdentityType.FullName, ResolveMethodWithArg(NetworkWriterType, "Write", NetworkIdentityType) },
				{ NetworkInstanceIdType.FullName, NetworkWriterWriteNetworkInstanceId },
				{ NetworkSceneIdType.FullName, NetworkWriterWriteNetworkSceneId },
				{ transformType.FullName, ResolveMethodWithArg(NetworkWriterType, "Write", transformType) },
				{ "System.Byte[]", ResolveMethod(NetworkWriterType, "WriteBytesFull") },
				{ SyncListFloatType.FullName, SyncListFloatWriteType },
				{ SyncListIntType.FullName, SyncListIntWriteType },
				{ SyncListUIntType.FullName, SyncListUIntWriteType },
				{ SyncListBoolType.FullName, SyncListBoolWriteType },
				{ SyncListStringType.FullName, SyncListStringWriteType }
			};
			weaverLists.writeFuncs = dictionary;
		}

		private static bool IsNetworkBehaviour(TypeDefinition td)
		{
			bool result;
			if (!td.IsClass)
			{
				result = false;
			}
			else
			{
				var baseType = td.BaseType;
				while (baseType != null)
				{
					if (baseType.FullName == NetworkBehaviourType.FullName)
					{
						return true;
					}

					try
					{
						baseType = baseType.Resolve().BaseType;
					}
					catch (AssemblyResolutionException)
					{
						break;
					}
				}

				result = false;
			}

			return result;
		}

		public static bool IsDerivedFrom(TypeDefinition td, TypeReference baseClass)
		{
			bool result;
			if (!td.IsClass)
			{
				result = false;
			}
			else
			{
				var baseType = td.BaseType;
				while (baseType != null)
				{
					var text = baseType.FullName;
					var num = text.IndexOf('<');
					if (num != -1)
					{
						text = text.Substring(0, num);
					}

					if (text == baseClass.FullName)
					{
						return true;
					}

					try
					{
						baseType = baseType.Resolve().BaseType;
					}
					catch (AssemblyResolutionException)
					{
						break;
					}
				}

				result = false;
			}

			return result;
		}

		public static bool IsValidTypeToGenerate(TypeDefinition variable)
		{
			var name = scriptDef.MainModule.Name;
			bool result;
			if (variable.Module.Name != name)
			{
				Log.Error(string.Concat(new string[]
				{
					"parameter [",
					variable.Name,
					"] is of the type [",
					variable.FullName,
					"] is not a valid type, please make sure to use a valid type."
				}));
				fail = true;
				fail = true;
				result = false;
			}
			else
			{
				result = true;
			}

			return result;
		}

		private static void CheckMonoBehaviour(TypeDefinition td)
		{
			if (IsDerivedFrom(td, MonoBehaviourType))
			{
				ProcessMonoBehaviourType(td);
			}
		}

		private static bool CheckNetworkBehaviour(TypeDefinition td)
		{
			bool result;
			if (!td.IsClass)
			{
				result = false;
			}
			else if (!IsNetworkBehaviour(td))
			{
				CheckMonoBehaviour(td);
				result = false;
			}
			else
			{
				var list = new List<TypeDefinition>();
				var typeDefinition = td;
				while (typeDefinition != null)
				{
					if (typeDefinition.FullName == NetworkBehaviourType.FullName)
					{
						break;
					}

					try
					{
						list.Insert(0, typeDefinition);
						typeDefinition = typeDefinition.BaseType.Resolve();
					}
					catch (AssemblyResolutionException)
					{
						break;
					}
				}

				var flag = false;
				foreach (var td2 in list)
				{
					flag |= ProcessNetworkBehaviourType(td2);
				}

				result = flag;
			}

			return result;
		}

		private static bool CheckMessageBase(TypeDefinition td)
		{
			bool result;
			if (!td.IsClass)
			{
				result = false;
			}
			else
			{
				var flag = false;
				var baseType = td.BaseType;
				while (baseType != null)
				{
					if (baseType.FullName == MessageBaseType.FullName)
					{
						flag |= ProcessMessageType(td);
						break;
					}

					try
					{
						baseType = baseType.Resolve().BaseType;
					}
					catch (AssemblyResolutionException)
					{
						break;
					}
				}

				foreach (var td2 in td.NestedTypes)
				{
					flag |= CheckMessageBase(td2);
				}

				result = flag;
			}

			return result;
		}

		private static bool CheckSyncListStruct(TypeDefinition td)
		{
			bool result;
			if (!td.IsClass)
			{
				result = false;
			}
			else
			{
				var flag = false;
				var baseType = td.BaseType;
				while (baseType != null)
				{
					if (baseType.FullName.Contains("SyncListStruct"))
					{
						flag |= ProcessSyncListStructType(td);
						break;
					}

					try
					{
						baseType = baseType.Resolve().BaseType;
					}
					catch (AssemblyResolutionException)
					{
						break;
					}
				}

				foreach (var td2 in td.NestedTypes)
				{
					flag |= CheckSyncListStruct(td2);
				}

				result = flag;
			}

			return result;
		}

		private static bool Weave(string assName, IEnumerable<string> dependencies, IAssemblyResolver assemblyResolver, string unityEngineDLLPath, string unityUNetDLLPath, string outputDir)
		{
			var readerParameters = Helpers.ReaderParameters(assName, dependencies, assemblyResolver, unityEngineDLLPath, unityUNetDLLPath);
			scriptDef = AssemblyDefinition.ReadAssembly(assName, readerParameters);
			SetupTargetTypes();
			SetupReadFunctions();
			SetupWriteFunctions();
			var mainModule = scriptDef.MainModule;
			Console.WriteLine("Script Module: {0}", mainModule.Name);
			var flag = false;
			for (var i = 0; i < 2; i++)
			{
				var stopwatch = Stopwatch.StartNew();
				foreach (var typeDefinition in mainModule.Types)
				{
					if (typeDefinition.IsClass && CanBeResolved(typeDefinition.BaseType))
					{
						try
						{
							if (i == 0)
							{
								flag |= CheckSyncListStruct(typeDefinition);
							}
							else
							{
								flag |= CheckNetworkBehaviour(typeDefinition);
								flag |= CheckMessageBase(typeDefinition);
							}
						}
						catch (Exception ex)
						{
							if (scriptDef.MainModule.SymbolReader != null)
							{
								scriptDef.MainModule.SymbolReader.Dispose();
							}

							fail = true;
							throw ex;
						}
					}

					if (fail)
					{
						if (scriptDef.MainModule.SymbolReader != null)
						{
							scriptDef.MainModule.SymbolReader.Dispose();
						}

						return false;
					}
				}

				stopwatch.Stop();
				Console.WriteLine(string.Concat(new object[]
				{
					"Pass: ",
					i,
					" took ",
					stopwatch.ElapsedMilliseconds,
					" milliseconds"
				}));
			}

			if (flag)
			{
				foreach (var methodDefinition in lists.replacedMethods)
				{
					lists.replacementMethodNames.Add(methodDefinition.FullName);
				}

				try
				{
					ProcessPropertySites();
				}
				catch (Exception ex2)
				{
					Log.Error("ProcessPropertySites exception: " + ex2);
					if (scriptDef.MainModule.SymbolReader != null)
					{
						scriptDef.MainModule.SymbolReader.Dispose();
					}

					return false;
				}

				if (fail)
				{
					if (scriptDef.MainModule.SymbolReader != null)
					{
						scriptDef.MainModule.SymbolReader.Dispose();
					}

					return false;
				}

				var fileName = Helpers.DestinationFileFor(outputDir, assName);
				var writerParameters = Helpers.GetWriterParameters(readerParameters);
				if (writerParameters.SymbolWriterProvider is PdbWriterProvider)
				{
					writerParameters.SymbolWriterProvider = new MdbWriterProvider();
					var text = Path.ChangeExtension(assName, ".pdb");
					File.Delete(text);
				}

				scriptDef.Write(fileName, writerParameters);
			}

			if (scriptDef.MainModule.SymbolReader != null)
			{
				scriptDef.MainModule.SymbolReader.Dispose();
			}

			return true;
		}

		public static bool WeaveAssemblies(string assembly, IEnumerable<string> dependencies, IAssemblyResolver assemblyResolver, string outputDir, string unityEngineDLLPath, string unityQNetDLLPath, string unityUNetDLLPath)
		{
			fail = false;
			lists = new WeaverLists();
			Console.WriteLine($"load unity engine from {unityEngineDLLPath}");
			UnityAssemblyDefinition = AssemblyDefinition.ReadAssembly(unityEngineDLLPath);
			Console.WriteLine($"load qnet from {unityQNetDLLPath}");
			QNetAssemblyDefinition = AssemblyDefinition.ReadAssembly(unityQNetDLLPath);
			Console.WriteLine($"load unet from {unityUNetDLLPath}");
			UNetAssemblyDefinition = AssemblyDefinition.ReadAssembly(unityUNetDLLPath);
			SetupUnityTypes();
			try
			{
				if (!Weave(assembly, dependencies, assemblyResolver, unityEngineDLLPath, unityQNetDLLPath, outputDir))
				{
					return false;
				}
			}
			catch (Exception ex)
			{
				Log.Error("Exception :" + ex);
				return false;
			}

			corLib = null;
			return true;
		}

		public static TypeReference NetworkBehaviourType;

		public static TypeReference NetworkBehaviourType2;

		public static TypeReference MonoBehaviourType;

		public static TypeReference ScriptableObjectType;

		public static TypeReference NetworkConnectionType;

		public static TypeReference ULocalConnectionToServerType;

		public static TypeReference ULocalConnectionToClientType;

		public static TypeReference MessageBaseType;

		public static TypeReference SyncListStructType;

		public static MethodReference NetworkBehaviourDirtyBitsReference;

		public static MethodReference NetworkBehaviourOnSerialize;

		public static MethodReference NetworkBehaviourClearAllDirtyBits;

		public static MethodReference NetworkBehaviourGetNetworkChannel;

		public static MethodReference NetworkConnectionSendWriter;

		public static MethodReference NetworkBehaviourClientSendUpdateVars;

		public static TypeReference NetworkClientType;

		public static TypeReference NetworkServerType;

		public static TypeReference NetworkCRCType;

		public static TypeReference NetworkReaderType;

		public static TypeDefinition NetworkReaderDef;

		public static TypeReference NetworkWriterType;

		public static TypeDefinition NetworkWriterDef;

		public static MethodReference NetworkWriterCtor;

		public static MethodReference NetworkReaderCtor;

		public static TypeReference MemoryStreamType;

		public static MethodReference MemoryStreamCtor;

		public static MethodReference getComponentReference;

		public static MethodReference getUNetIdReference;

		public static MethodReference getPlayerIdReference;

		public static TypeReference NetworkIdentityType;

		public static TypeReference NetworkInstanceIdType;

		public static TypeReference NetworkSceneIdType;

		public static TypeReference IEnumeratorType;

		public static TypeReference ClientSceneType;

		public static MethodReference FindLocalObjectReference;

		public static MethodReference RegisterBehaviourReference;

		public static MethodReference ReadyConnectionReference;

		public static TypeReference ComponentType;

		public static TypeReference CmdDelegateReference;

		public static MethodReference CmdDelegateConstructor;

		public static MethodReference NetworkReaderReadInt32;

		public static MethodReference NetworkWriterWriteInt32;

		public static MethodReference NetworkWriterWriteInt16;

		public static MethodReference NetworkServerGetActive;

		public static MethodReference NetworkServerGetLocalClientActive;

		public static MethodReference NetworkClientGetActive;

		public static MethodReference UBehaviourIsServer;

		public static MethodReference NetworkReaderReadPacked32;

		public static MethodReference NetworkReaderReadPacked64;

		public static MethodReference NetworkReaderReadByte;

		public static MethodReference NetworkWriterWritePacked32;

		public static MethodReference NetworkWriterWritePacked64;

		public static MethodReference NetworkWriterWriteNetworkInstanceId;

		public static MethodReference NetworkWriterWriteNetworkSceneId;

		public static MethodReference NetworkWriterStartMessage;

		public static MethodReference NetworkWriterFinishMessage;

		public static MethodReference NetworkReaderReadNetworkInstanceId;

		public static MethodReference NetworkReaderReadNetworkSceneId;

		public static MethodReference NetworkInstanceIsEmpty;

		public static MethodReference NetworkReadUInt16;

		public static MethodReference NetworkWriteUInt16;

		public static TypeReference SyncVarType;

		public static TypeReference CommandType;

		public static TypeReference ClientRpcType;

		public static TypeReference TargetRpcType;

		public static TypeReference SyncEventType;

		public static TypeReference SyncListType;

		public static MethodReference SyncListInitBehaviourReference;

		public static MethodReference SyncListInitHandleMsg;

		public static MethodReference SyncListClear;

		public static TypeReference NetworkSettingsType;

		public static TypeReference SyncListFloatType;

		public static TypeReference SyncListIntType;

		public static TypeReference SyncListUIntType;

		public static TypeReference SyncListBoolType;

		public static TypeReference SyncListStringType;

		public static MethodReference SyncListFloatReadType;

		public static MethodReference SyncListIntReadType;

		public static MethodReference SyncListUIntReadType;

		public static MethodReference SyncListStringReadType;

		public static MethodReference SyncListBoolReadType;

		public static MethodReference SyncListFloatWriteType;

		public static MethodReference SyncListIntWriteType;

		public static MethodReference SyncListUIntWriteType;

		public static MethodReference SyncListBoolWriteType;

		public static MethodReference SyncListStringWriteType;

		public static TypeReference voidType;

		public static TypeReference singleType;

		public static TypeReference doubleType;

		public static TypeReference decimalType;

		public static TypeReference boolType;

		public static TypeReference stringType;

		public static TypeReference int64Type;

		public static TypeReference uint64Type;

		public static TypeReference int32Type;

		public static TypeReference uint32Type;

		public static TypeReference int16Type;

		public static TypeReference uint16Type;

		public static TypeReference byteType;

		public static TypeReference sbyteType;

		public static TypeReference charType;

		public static TypeReference objectType;

		public static TypeReference valueTypeType;

		public static TypeReference vector2Type;

		public static TypeReference vector3Type;

		public static TypeReference vector4Type;

		public static TypeReference colorType;

		public static TypeReference color32Type;

		public static TypeReference quaternionType;

		public static TypeReference rectType;

		public static TypeReference rayType;

		public static TypeReference planeType;

		public static TypeReference matrixType;

		public static TypeReference hashType;

		public static TypeReference typeType;

		public static TypeReference gameObjectType;

		public static TypeReference transformType;

		public static TypeReference unityObjectType;

		public static MethodReference gameObjectInequality;

		public static MethodReference setSyncVarReference;

		public static MethodReference setSyncVarHookGuard;

		public static MethodReference getSyncVarHookGuard;

		public static MethodReference setSyncVarGameObjectReference;

		public static MethodReference registerCommandDelegateReference;

		public static MethodReference registerRpcDelegateReference;

		public static MethodReference registerEventDelegateReference;

		public static MethodReference registerSyncListDelegateReference;

		public static MethodReference getTypeReference;

		public static MethodReference getTypeFromHandleReference;

		public static MethodReference logErrorReference;

		public static MethodReference logWarningReference;

		public static MethodReference sendCommandInternal;

		public static MethodReference sendRpcInternal;

		public static MethodReference sendTargetRpcInternal;

		public static MethodReference sendEventInternal;

		public static WeaverLists lists;

		public static AssemblyDefinition scriptDef;

		public static ModuleDefinition corLib;

		public static AssemblyDefinition UnityAssemblyDefinition;

		public static AssemblyDefinition QNetAssemblyDefinition;

		public static AssemblyDefinition UNetAssemblyDefinition;

		private static bool m_DebugFlag = true;

		public static bool fail;

		public static bool generateLogErrors = false;

		private const int MaxRecursionCount = 128;

		private static int s_RecursionCount;
	}
}
