/// <summary> /// Serializes a data reference composed of raw bytes. /// </summary> /// <param name="tagStream">The stream to write completed blocks of tag data to.</param> /// <param name="block">The temporary block to write incomplete tag data to.</param> /// <param name="data">The data.</param> /// <param name="valueInfo">Information about the value. Can be <c>null</c>.</param> private static void SerializeDataReference(MemoryStream tagStream, IDataBlock block, byte[] data, TagFieldAttribute valueInfo) { var writer = block.Writer; uint offset = 0; uint size = 0; if (data != null && data.Length > 0) { // Ensure the block is aligned correctly var align = Math.Max(DefaultBlockAlign, (valueInfo != null) ? valueInfo.DataAlign : 0); StreamUtil.Align(tagStream, (int)align); // Write its data offset = (uint)tagStream.Position; size = (uint)data.Length; tagStream.Write(data, 0, data.Length); StreamUtil.Align(tagStream, DefaultBlockAlign); } // Write the reference data writer.Write(size); writer.Write(0); writer.Write(0); if (size > 0) block.WritePointer(offset, typeof(byte[])); else writer.Write(0); writer.Write(0); }
/// <summary> /// Determines whether a field exists in the given CacheVersion. Defines a priority : Version, Gen, Min/Max. /// </summary> /// <param name="attr"></param> /// <param name="compare"></param> /// <returns></returns> public static bool AttributeInCacheVersion(TagFieldAttribute attr, CacheVersion compare) { if (attr.Version != CacheVersion.Unknown) { // has a specific version specified. return(attr.Version == compare); } else if (attr.Gen != CacheGeneration.Unknown) { // Has a generation specified return(IsInGen(attr.Gen, compare)); } else if (attr.MinVersion != CacheVersion.Unknown || attr.MaxVersion != CacheVersion.Unknown) { // Has a min or a max or both specified. return(IsBetween(compare, attr.MinVersion, attr.MaxVersion)); } else { // has no version attribute therefore it's valid in all cache versions return(true); } }
/// <summary> /// Determines whether a field exists in the given CacheVersion. Defines a priority : Version, Gen, Min/Max. /// </summary> /// <param name="attr"></param> /// <param name="compare"></param> /// <returns></returns> public static bool AttributeInCacheVersion(TagFieldAttribute attr, CacheVersion compare) { if (attr.Version != CacheVersion.Unknown) { if (attr.Version != compare) { return(false); } } if (attr.Gen != CacheGeneration.Unknown) { if (!IsInGen(attr.Gen, compare)) { return(false); } } if (attr.Platform != CachePlatform.All) { if (!IsInPlatform(attr.Platform, compare)) { return(false); } } if (attr.MinVersion != CacheVersion.Unknown || attr.MaxVersion != CacheVersion.Unknown) { if (!IsBetween(compare, attr.MinVersion, attr.MaxVersion)) { return(false); } } return(true); }
/// <summary> /// Serializes a value. /// </summary> /// <param name="context">The serialization context to use.</param> /// <param name="tagStream">The stream to write completed blocks of tag data to.</param> /// <param name="block">The temporary block to write incomplete tag data to.</param> /// <param name="val">The value.</param> /// <param name="valueInfo">Information about the value. Can be <c>null</c>.</param> /// <param name="valueType">Type of the value.</param> private void SerializeValue(ISerializationContext context, MemoryStream tagStream, IDataBlock block, object val, TagFieldAttribute valueInfo, Type valueType) { // Call the data block's PreSerialize callback to determine if the value should be mutated val = block.PreSerialize(valueInfo, val); if (val != null) valueType = val.GetType(); // TODO: Fix hax if (valueType.IsPrimitive) SerializePrimitiveValue(block.Writer, val, valueType); else SerializeComplexValue(context, tagStream, block, val, valueInfo, valueType); }
/// <summary> /// Serializes a tag block. /// </summary> /// <param name="context">The serialization context to use.</param> /// <param name="tagStream">The stream to write completed blocks of tag data to.</param> /// <param name="block">The temporary block to write incomplete tag data to.</param> /// <param name="list">The list of values in the tag block.</param> /// <param name="listType">Type of the list.</param> /// <param name="valueInfo">Information about the value. Can be <c>null</c>.</param> private void SerializeTagBlock(ISerializationContext context, MemoryStream tagStream, IDataBlock block, object list, Type listType, TagFieldAttribute valueInfo) { var writer = block.Writer; var listCount = 0; if (list != null) { // Use reflection to get the number of elements in the list var countProperty = listType.GetProperty("Count"); listCount = (int)countProperty.GetValue(list); } if (listCount == 0) { writer.Write(0); writer.Write(0); writer.Write(0); return; } // Serialize each value in the list to a data block var tagBlock = context.CreateBlock(); var enumerableList = (System.Collections.IEnumerable)list; var valueType = listType.GenericTypeArguments[0]; foreach (var val in enumerableList) SerializeValue(context, tagStream, tagBlock, val, null, valueType); // Ensure the block is aligned correctly var align = Math.Max(DefaultBlockAlign, (valueInfo != null) ? valueInfo.DataAlign : 0); StreamUtil.Align(tagStream, (int)align); // Finalize the block and write the tag block reference writer.Write(listCount); block.WritePointer(tagBlock.Finalize(tagStream), listType); writer.Write(0); }
/// <summary> /// Serializes an inline array. /// </summary> /// <param name="context">The serialization context to use.</param> /// <param name="tagStream">The stream to write completed blocks of tag data to.</param> /// <param name="block">The temporary block to write incomplete tag data to.</param> /// <param name="data">The array.</param> /// <param name="valueInfo">Information about the value. Can be <c>null</c>.</param> private void SerializeInlineArray(ISerializationContext context, MemoryStream tagStream, IDataBlock block, Array data, TagFieldAttribute valueInfo) { if (valueInfo == null || valueInfo.Count == 0) throw new ArgumentException("Cannot serialize an inline array with no count set"); if (data == null || data.Length != valueInfo.Count) throw new ArgumentException("Array length does not match the fixed count of " + valueInfo.Count); // Serialize each element into the current block var elementType = data.GetType().GetElementType(); foreach (var elem in data) SerializeValue(context, tagStream, block, elem, null, elementType); }
/// <summary> /// Serializes a complex value. /// </summary> /// <param name="context">The serialization context to use.</param> /// <param name="tagStream">The stream to write completed blocks of tag data to.</param> /// <param name="block">The temporary block to write incomplete tag data to.</param> /// <param name="val">The value.</param> /// <param name="valueInfo">Information about the value. Can be <c>null</c>.</param> /// <param name="valueType">Type of the value.</param> private void SerializeComplexValue(ISerializationContext context, MemoryStream tagStream, IDataBlock block, object val, TagFieldAttribute valueInfo, Type valueType) { if (valueInfo != null && ((valueInfo.Flags & TagFieldFlags.Indirect) != 0 || valueType == typeof(ResourceReference))) SerializeIndirectValue(context, tagStream, block, val, valueType); else if (valueType.IsEnum) SerializePrimitiveValue(block.Writer, val, valueType.GetEnumUnderlyingType()); else if (valueType == typeof(string)) SerializeString(block.Writer, (string)val, valueInfo); else if (valueType == typeof(Tag)) SerializeTag(block, (Tag)val); else if (valueType == typeof(TagInstance)) SerializeTagReference(block.Writer, (TagInstance)val, valueInfo); else if (valueType == typeof(ResourceAddress)) block.Writer.Write(((ResourceAddress)val).Value); else if (valueType == typeof(byte[])) SerializeDataReference(tagStream, block, (byte[])val, valueInfo); else if (valueType == typeof(Euler2)) SerializeEulerAngles(block, (Euler2)val); else if (valueType == typeof(Euler3)) SerializeEulerAngles(block, (Euler3)val); else if (valueType == typeof(Vector2)) SerializeVector(block, (Vector2)val); else if (valueType == typeof(Vector3)) SerializeVector(block, (Vector3)val); else if (valueType == typeof(Vector4)) SerializeVector(block, (Vector4)val); else if (valueType == typeof(RealQuaternion)) SerializeRealQuaternion(block, (RealQuaternion)val); else if (valueType == typeof(Matrix4x3)) SerializeMatrix(block, (Matrix4x3)val); else if (valueType == typeof(StringID)) block.Writer.Write(((StringID)val).Value); else if (valueType == typeof(Angle)) block.Writer.Write(((Angle)val).Radians); else if (valueType.IsArray) SerializeInlineArray(context, tagStream, block, (Array)val, valueInfo); else if (valueType.IsGenericType && valueType.GetGenericTypeDefinition() == typeof(List<>)) SerializeTagBlock(context, tagStream, block, val, valueType, valueInfo); else if (valueType.IsGenericType && valueType.GetGenericTypeDefinition() == typeof(Range<>)) SerializeRange(block, val); else SerializeStruct(context, tagStream, block, new TagStructureInfo(val.GetType(), _version), val); }
/// <summary> /// Serializes a tag reference. /// </summary> /// <param name="writer">The writer to write to.</param> /// <param name="referencedTag">The referenced tag.</param> /// <param name="valueInfo">Information about the value. Can be <c>null</c>.</param> private static void SerializeTagReference(BinaryWriter writer, TagInstance referencedTag, TagFieldAttribute valueInfo) { // Write the reference out if (valueInfo == null || (valueInfo.Flags & TagFieldFlags.Short) == 0) { writer.Write((referencedTag != null) ? referencedTag.Group.Tag.Value : -1); writer.Write(0); writer.Write(0); } writer.Write((referencedTag != null) ? referencedTag.Index : -1); }
/// <summary> /// Serializes a string. /// </summary> /// <param name="writer">The writer to write to.</param> /// <param name="str">The string to serialize.</param> /// <param name="valueInfo">Information about the value.</param> private static void SerializeString(BinaryWriter writer, string str, TagFieldAttribute valueInfo) { if (valueInfo == null || valueInfo.Length == 0) throw new ArgumentException("Cannot serialize a string with no length set"); var clampedLength = 0; if (str != null) { var bytes = Encoding.ASCII.GetBytes(str); clampedLength = Math.Min(valueInfo.Length - 1, bytes.Length); writer.Write(bytes, 0, clampedLength); } for (var i = clampedLength; i < valueInfo.Length; i++) writer.Write((byte)0); }
public object PreSerialize(TagFieldAttribute info, object obj) { if (obj == null) return null; // Get the object type and make sure it's supported var type = obj.GetType(); if (type == typeof(ResourceDataReference) || (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(D3DPointer<>))) throw new InvalidOperationException(type + " cannot be serialized as tag data"); // HACK: If the object is a ResourceReference, fix the Owner property var resource = obj as ResourceReference; if (resource != null) resource.Owner = _context.Tag; if (type == typeof(TagInstance)) { // Object is a tag reference - add it as a dependency var referencedTag = obj as TagInstance; if (referencedTag != null && referencedTag != _context.Tag) _context._data.Dependencies.Add(referencedTag.Index); } return obj; }
public object PreSerialize(TagFieldAttribute info, object obj) { if (obj == null) return null; // When serializing a resource address, just add a fixup for it and serialize a null pointer if (obj is ResourceAddress) { _fixups.Add(MakeDefinitionFixup((ResourceAddress)obj)); return 0U; } var type = obj.GetType(); if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(D3DPointer<>)) { // Add a D3D fixup for D3DPointers based on the type of object being pointed to var d3dType = GetD3DObjectType(type.GenericTypeArguments[0]); _d3dFixups.Add(MakeD3DFixup((uint)Stream.Position, d3dType)); } return obj; }
public object PreSerialize(TagFieldAttribute info, object obj) { return(obj); }