/// <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> /// 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); } }
/// <summary> /// Deserializes a null-terminated ASCII string. /// </summary> /// <param name="reader">The reader.</param> /// <param name="valueInfo">The value information.</param> /// <returns>The deserialized string.</returns> private static string DeserializeString(BinaryReader reader, TagFieldAttribute valueInfo) { if (valueInfo == null || valueInfo.Length == 0) { throw new ArgumentException("Cannot deserialize a string with no length set"); } // Keep reading until a null terminator is found var result = new StringBuilder(); var startPos = reader.BaseStream.Position; while (true) { var ch = reader.ReadByte(); if (ch == 0) { break; } result.Append((char)ch); } reader.BaseStream.Position = startPos + valueInfo.Length; return(result.ToString()); }
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); }
/// <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(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(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 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> /// Deserializes a complex value. /// </summary> /// <param name="reader">The reader.</param> /// <param name="context">The serialization context to use.</param> /// <param name="valueInfo">The value information. Can be <c>null</c>.</param> /// <param name="valueType">The type of the value to deserialize.</param> /// <returns>The deserialized value.</returns> private object DeserializeComplexValue(BinaryReader reader, ISerializationContext context, TagFieldAttribute valueInfo, Type valueType) { // Indirect objects // TODO: Remove ResourceReference hax, the Indirect flag wasn't available when I generated the tag structures if (valueInfo != null && ((valueInfo.Flags & TagFieldFlags.Indirect) != 0 || valueType == typeof(ResourceReference))) { return(DeserializeIndirectValue(reader, context, valueType)); } // enum = Enum type if (valueType.IsEnum) { return(DeserializePrimitiveValue(reader, valueType.GetEnumUnderlyingType())); } // string = ASCII string if (valueType == typeof(string)) { return(DeserializeString(reader, valueInfo)); } // TagInstance = Tag reference if (valueType == typeof(TagInstance)) { return(DeserializeTagReference(reader, context, valueInfo)); } // ResourceAddress = Resource address if (valueType == typeof(ResourceAddress)) { return(new ResourceAddress(reader.ReadUInt32())); } // Byte array = Data reference // TODO: Allow other types to be in data references, since sometimes they can point to a structure if (valueType == typeof(byte[])) { return(DeserializeDataReference(reader, context)); } // Euler Angles types if (valueType == typeof(Euler2)) { var i = Angle.FromRadians(reader.ReadSingle()); var j = Angle.FromRadians(reader.ReadSingle()); return(new Euler2(i, j)); } else if (valueType == typeof(Euler3)) { var i = Angle.FromRadians(reader.ReadSingle()); var j = Angle.FromRadians(reader.ReadSingle()); var k = Angle.FromRadians(reader.ReadSingle()); return(new Euler3(i, j, k)); } // Vector types if (valueType == typeof(Vector2)) { return(new Vector2(reader.ReadSingle(), reader.ReadSingle())); } if (valueType == typeof(Vector3)) { return(new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle())); } if (valueType == typeof(Vector4)) { return(new Vector4(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle())); } // StringID if (valueType == typeof(StringId)) { return(new StringId(reader.ReadUInt32())); } // Angle (radians) if (valueType == typeof(Angle)) { return(Angle.FromRadians(reader.ReadSingle())); } // Non-byte array = Inline array // TODO: Define more clearly in general what constitutes a data reference and what doesn't if (valueType.IsArray) { return(DeserializeInlineArray(reader, context, valueInfo, valueType)); } // List = Tag block if (valueType.IsGenericType && valueType.GetGenericTypeDefinition() == typeof(List <>)) { return(DeserializeTagBlock(reader, context, valueType)); } // Ranges if (valueType.IsGenericType && valueType.GetGenericTypeDefinition() == typeof(Range <>)) { return(DeserializeRange(reader, context, valueType)); } // Assume the value is a structure return(DeserializeStruct(reader, context, new TagStructureInfo(valueType, _version))); }
/// <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 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> /// 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(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(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.GroupTag.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; }
/// <summary> /// Deserializes an inline array. /// </summary> /// <param name="reader">The reader.</param> /// <param name="context">The serialization context to use.</param> /// <param name="valueInfo">The value information. Can be <c>null</c>.</param> /// <param name="valueType">The type of the value to deserialize.</param> /// <returns>The deserialized array.</returns> private Array DeserializeInlineArray(BinaryReader reader, ISerializationContext context, TagFieldAttribute valueInfo, Type valueType) { if (valueInfo == null || valueInfo.Count == 0) { throw new ArgumentException("Cannot deserialize an inline array with no count set"); } // Create the array and read the elements in order var elementCount = valueInfo.Count; var elementType = valueType.GetElementType(); var result = Array.CreateInstance(elementType, elementCount); for (var i = 0; i < elementCount; i++) { result.SetValue(DeserializeValue(reader, context, null, elementType), i); } return(result); }
/// <summary> /// Deserializes a tag reference. /// </summary> /// <param name="reader">The reader.</param> /// <param name="context">The serialization context to use.</param> /// <param name="valueInfo">The value information. Can be <c>null</c>.</param> /// <returns>The deserialized tag reference.</returns> private static TagInstance DeserializeTagReference(BinaryReader reader, ISerializationContext context, TagFieldAttribute valueInfo) { if (valueInfo == null || (valueInfo.Flags & TagFieldFlags.Short) == 0) { reader.BaseStream.Position += 0xC; // Skip the class name and zero bytes, it's not important } var index = reader.ReadInt32(); return(context.GetTagByIndex(index)); }
/// <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 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 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 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); }
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; }
/// <summary> /// Deserializes a value. /// </summary> /// <param name="reader">The reader.</param> /// <param name="context">The serialization context to use.</param> /// <param name="valueInfo">The value information. Can be <c>null</c>.</param> /// <param name="valueType">The type of the value to deserialize.</param> /// <returns>The deserialized value.</returns> private object DeserializeValue(BinaryReader reader, ISerializationContext context, TagFieldAttribute valueInfo, Type valueType) { if (valueType.IsPrimitive) { return(DeserializePrimitiveValue(reader, valueType)); } return(DeserializeComplexValue(reader, context, valueInfo, valueType)); }