public object Deserialize(BinaryReader reader)
		{
			if (reader.ReadInt32() != magic)
				throw new SerializationException("The data cannot be read by FastSerializer (unknown magic value)");
			
			DeserializationContext context = new DeserializationContext();
			context.Reader = reader;
			context.Objects = new object[reader.ReadInt32()];
			context.Types = new Type[reader.ReadInt32()];
			string[] assemblyNames = new string[reader.ReadInt32()];
			int fixedInstanceCount = reader.ReadInt32();
			
			if (fixedInstanceCount != 0) {
				if (this.FixedInstances == null || this.FixedInstances.Length != fixedInstanceCount)
					throw new SerializationException("Number of fixed instances doesn't match");
				for (int i = 0; i < fixedInstanceCount; i++) {
					context.Objects[i + 1] = this.FixedInstances[i];
				}
			}
			
			for (int i = 0; i < assemblyNames.Length; i++) {
				assemblyNames[i] = reader.ReadString();
			}
			int stringTypeID = -1;
			for (int i = 0; i < context.Types.Length; i++) {
				byte typeKind = reader.ReadByte();
				switch (typeKind) {
					case Type_ReferenceType:
					case Type_ValueType:
						int assemblyID;
						if (assemblyNames.Length <= ushort.MaxValue)
							assemblyID = reader.ReadUInt16();
						else
							assemblyID = reader.ReadInt32();
						string assemblyName = assemblyNames[assemblyID];
						string typeName = reader.ReadString();
						Type type;
						if (SerializationBinder != null) {
							type = SerializationBinder.BindToType(assemblyName, typeName);
						} else {
							type = Assembly.Load(assemblyName).GetType(typeName);
						}
						if (type == null)
							throw new SerializationException("Could not find '" + typeName + "' in '" + assemblyName + "'");
						if (typeKind == Type_ValueType && !type.IsValueType)
							throw new SerializationException("Expected '" + typeName + "' to be a value type, but it is reference type");
						if (typeKind == Type_ReferenceType && type.IsValueType)
							throw new SerializationException("Expected '" + typeName + "' to be a reference type, but it is value type");
						context.Types[i] = type;
						if (type == typeof(string))
							stringTypeID = i;
						break;
					case Type_SZArray:
						context.Types[i] = context.Types[context.ReadTypeID()].MakeArrayType();
						break;
					case Type_ParameterizedType:
						Type genericType = context.Types[context.ReadTypeID()];
						int typeParameterCount = genericType.GetGenericArguments().Length;
						Type[] typeArguments = new Type[typeParameterCount];
						for (int j = 0; j < typeArguments.Length; j++) {
							typeArguments[j] = context.Types[context.ReadTypeID()];
						}
						context.Types[i] = genericType.MakeGenericType(typeArguments);
						break;
					default:
						throw new SerializationException("Unknown type kind");
				}
			}
			context.DeserializeTypeDescriptions();
			int[] typeIDByObjectID = new int[context.Objects.Length];
			for (int i = 1 + fixedInstanceCount; i < context.Objects.Length; i++) {
				int typeID = context.ReadTypeID();
				
				object instance;
				if (typeID == stringTypeID) {
					instance = reader.ReadString();
				} else {
					Type type = context.Types[typeID];
					if (type.IsArray) {
						int length = reader.ReadInt32();
						instance = Array.CreateInstance(type.GetElementType(), length);
					} else {
						instance = FormatterServices.GetUninitializedObject(type);
					}
				}
				context.Objects[i] = instance;
				typeIDByObjectID[i] = typeID;
			}
			List<CustomDeserialization> customDeserializatons = new List<CustomDeserialization>();
			ObjectReader[] objectReaders = new ObjectReader[context.Types.Length]; // index: type ID
			for (int i = 1 + fixedInstanceCount; i < context.Objects.Length; i++) {
				object instance = context.Objects[i];
				int typeID = typeIDByObjectID[i];
				Log("0x{2:x6} Read #{0}: {1}", i, context.Types[typeID].Name, reader.BaseStream.Position);
				ISerializable serializable = instance as ISerializable;
				if (serializable != null) {
					Type type = context.Types[typeID];
					SerializationInfo info = new SerializationInfo(type, formatterConverter);
					int count = reader.ReadInt32();
					for (int j = 0; j < count; j++) {
						string name = reader.ReadString();
						object val = context.ReadObject();
						info.AddValue(name, val);
					}
					CustomDeserializationAction action = GetCustomDeserializationAction(type);
					customDeserializatons.Add(new CustomDeserialization(instance, info, action));
				} else {
					ObjectReader objectReader = objectReaders[typeID];
					if (objectReader == null) {
						objectReader = GetReader(context.Types[typeID]);
						objectReaders[typeID] = objectReader;
					}
					objectReader(context, instance);
				}
			}
			Log("File was read successfully, now running {0} custom deserializations...", customDeserializatons.Count);
			foreach (CustomDeserialization customDeserializaton in customDeserializatons) {
				customDeserializaton.Run(streamingContext);
			}
			for (int i = 1 + fixedInstanceCount; i < context.Objects.Length; i++) {
				IDeserializationCallback dc = context.Objects[i] as IDeserializationCallback;
				if (dc != null)
					dc.OnDeserialization(null);
			}
			
			return context.ReadObject();
		}