/// <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 void SerializeString(EndianWriter writer, string str, TagFieldAttribute valueInfo) { if (valueInfo == null || valueInfo.Length == 0) { throw new ArgumentException("Cannot serialize a string with no length set"); } var charSize = valueInfo.CharSet == Unicode ? 2 : 1; var byteCount = valueInfo.Length * charSize; var clampedLength = 0; if (str != null) { byte[] bytes = null; switch (valueInfo.CharSet) { case Ansi: bytes = Encoding.ASCII.GetBytes(str); break; case Unicode: if (Format == EndianFormat.LittleEndian) { bytes = Encoding.Unicode.GetBytes(str); } else { bytes = Encoding.BigEndianUnicode.GetBytes(str); } break; default: throw new NotSupportedException(valueInfo.CharSet.ToString()); } clampedLength = Math.Min(byteCount - charSize, bytes.Length); if (valueInfo.ForceNullTerminated && clampedLength == valueInfo.Length) { bytes[valueInfo.Length - 1] = 0; } writer.Write(bytes, 0, clampedLength); } for (var i = clampedLength; i < byteCount; 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> public string DeserializeString(EndianReader reader, TagFieldAttribute valueInfo) { if (valueInfo == null || valueInfo.Length == 0) { throw new ArgumentException("Cannot deserialize a string with no length set"); } switch (valueInfo.CharSet) { case CharSet.Ansi: case CharSet.Unicode: return(reader.ReadNullTerminatedString(valueInfo.Length, valueInfo.CharSet)); default: throw new NotSupportedException($"{valueInfo.CharSet}"); } }
/// <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 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="version"></param> /// <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="value">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(CacheVersion version, ISerializationContext context, MemoryStream tagStream, IDataBlock block, object value, TagFieldAttribute valueInfo, Type valueType) { if (valueInfo != null && valueInfo.Flags.HasFlag(TagFieldFlags.Pointer)) { SerializeIndirectValue(version, context, tagStream, block, value, valueType); } else if (valueType.IsEnum) { SerializePrimitiveValue(block.Writer, value, valueType.GetEnumUnderlyingType()); } else if (valueType == typeof(string)) { SerializeString(block.Writer, (string)value, valueInfo); } else if (valueType == typeof(Tag)) { SerializeTag(block, (Tag)value); } else if (valueType == typeof(CachedTagInstance)) { SerializeTagReference(context, block.Writer, (CachedTagInstance)value, valueInfo); } else if (valueType == typeof(CacheAddress)) { block.Writer.Write(((CacheAddress)value).Value); } else if (valueType == typeof(byte[])) { if (valueInfo.Flags.HasFlag(TagFieldFlags.Padding) || (value == null && valueInfo.Length > 0)) { block.Writer.Write(new byte[valueInfo.Length]); } else if (valueInfo.Length > 0) { block.Writer.Write((byte[])value); } else { SerializeDataReference(tagStream, block, (byte[])value, valueInfo); } } else if (valueType == typeof(RealRgbColor)) { SerializeColor(block, (RealRgbColor)value); } else if (valueType == typeof(RealArgbColor)) { SerializeColor(block, (RealArgbColor)value); } else if (valueType == typeof(ArgbColor)) { SerializeColor(block, (ArgbColor)value); } else if (valueType == typeof(ArgbColor)) { SerializeColor(block, (ArgbColor)value); } else if (valueType == typeof(RealEulerAngles2d)) { SerializeEulerAngles(block, (RealEulerAngles2d)value); } else if (valueType == typeof(RealEulerAngles3d)) { SerializeEulerAngles(block, (RealEulerAngles3d)value); } else if (valueType == typeof(Point2d)) { SerializePoint(block, (Point2d)value); } else if (valueType == typeof(Rectangle2d)) { SerializeRectangle(block, (Rectangle2d)value); } else if (valueType == typeof(RealPoint2d)) { SerializePoint(block, (RealPoint2d)value); } else if (valueType == typeof(RealPoint3d)) { SerializePoint(block, (RealPoint3d)value); } else if (valueType == typeof(RealVector2d)) { SerializeVector(block, (RealVector2d)value); } else if (valueType == typeof(RealVector3d)) { SerializeVector(block, (RealVector3d)value); } else if (valueType == typeof(RealQuaternion)) { SerializeVector(block, (RealQuaternion)value); } else if (valueType == typeof(RealPlane2d)) { SerializePlane(block, (RealPlane2d)value); } else if (valueType == typeof(RealPlane3d)) { SerializePlane(block, (RealPlane3d)value); } else if (valueType == typeof(RealMatrix4x3)) { SerializeMatrix(block, (RealMatrix4x3)value); } else if (valueType == typeof(StringId)) { block.Writer.Write(((StringId)value).Value); } else if (valueType == typeof(Angle)) { block.Writer.Write(((Angle)value).Radians); } else if (valueType == typeof(VertexShaderReference)) { block.Writer.Write(0); // TODO: fix (not used in halo online) } else if (valueType == typeof(PixelShaderReference)) { block.Writer.Write(0); // TODO: fix (not used in halo online) } else if (valueType.IsArray) { SerializeInlineArray(version, context, tagStream, block, (Array)value, valueInfo, valueType); } else if (valueType.IsGenericType && valueType.GetGenericTypeDefinition() == typeof(List <>)) { SerializeTagBlock(version, context, tagStream, block, value, valueType, valueInfo); } else if (valueType.IsGenericType && valueType.GetGenericTypeDefinition() == typeof(Bounds <>)) { SerializeRange(version, context, tagStream, block, value); } else { if (value == null) { value = Activator.CreateInstance(valueType); } SerializeStruct(context, tagStream, block, TagStructure.GetTagStructureInfo(valueType, version), value); } }
public override void SerializeTagData(ISerializationContext context, MemoryStream tagStream, IDataBlock block, TagData tagData, TagFieldAttribute valueInfo) { if (context.GetType() != typeof(ResourceDefinitionSerializationContext)) { throw new Exception($"Invalid context type given resource deserialization"); } if (block.GetType() != typeof(ResourceDefinitionSerializationContext.ResourceDefinitionDataBlock)) { throw new Exception($"Invalid block type given resource deserialization"); } var resourceBlock = block as ResourceDefinitionSerializationContext.ResourceDefinitionDataBlock; var resourceContext = context as ResourceDefinitionSerializationContext; var writer = block.Writer; if (tagData == null || tagData.Data == null || tagData.Data.Length == 0) { writer.Write(0); writer.Write(0); writer.Write(0); writer.Write(0); writer.Write(0); return; } CacheAddressType addressType = tagData.AddressType; int dataOffset = 0; // offset in the data stream to the start of the data uint blockOffset = (uint)writer.BaseStream.Position + 0xC; // offset to the address pointing to the above relative to the current block. uint size = 0; // stream where the byte[] in the data should be written to: var dataStream = (MemoryStream)resourceContext.GetWriter(addressType).BaseStream; var data = tagData.Data; if (data != null && data.Length > 0) { // Ensure the block is aligned correctly var align = Math.Max(DefaultResourceAlign, (valueInfo != null) ? valueInfo.Align : 0); StreamUtil.Align(dataStream, (int)align); // Write its data dataOffset = (int)dataStream.Position; size = (uint)data.Length; dataStream.Write(data, 0, data.Length); StreamUtil.Align(dataStream, DefaultResourceAlign); } var dataAddress = new CacheAddress(addressType, dataOffset); var dataFixup = new ResourceFixupLocation { BlockOffset = blockOffset, Address = dataAddress }; resourceBlock.FixupLocations.Add(dataFixup); // this fixup will need to be adjusted when we move the block // Write the reference data writer.Write(size); writer.Write(0); writer.Write(0); writer.Write(dataAddress.Value); writer.Write(0); }
/// <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> /// 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)); }
/// <summary> /// Serializes an inline array. /// </summary> /// <param name="version"></param> /// <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> /// <param name="valueType">The type of the value.</param> private void SerializeInlineArray(CacheVersion version, ISerializationContext context, MemoryStream tagStream, IDataBlock block, Array data, TagFieldAttribute valueInfo, Type valueType) { if (valueInfo == null || valueInfo.Length == 0) { throw new ArgumentException("Cannot serialize an inline array with no count set"); } var elementType = valueType.GetElementType(); if (data == null) { data = Array.CreateInstance(elementType, valueInfo.Length); } if (data == null || data.Length != valueInfo.Length) { throw new ArgumentException("Array length does not match the fixed count of " + valueInfo.Length); } // Serialize each element into the current block foreach (var element in data) { SerializeValue(version, context, tagStream, block, element, null, elementType); } }
/// <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> public CachedTagInstance DeserializeTagReference(EndianReader reader, ISerializationContext context, TagFieldAttribute valueInfo) { if (valueInfo == null || !valueInfo.Flags.HasFlag(TagFieldFlags.Short)) { reader.BaseStream.Position += (Version > CacheVersion.Halo2Vista ? 0xC : 0x4); // Skip the class name and zero bytes, it's not important } var result = context.GetTagByIndex(reader.ReadInt32()); if (result != null && valueInfo != null && valueInfo.ValidTags != null) { foreach (string tag in valueInfo.ValidTags) { if (!result.IsInGroup(tag)) { throw new Exception($"Invalid group for tag reference: {result.Group.Tag}"); } } } return(result); }
/// <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> public object DeserializeComplexValue(EndianReader 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.HasFlag(TagFieldFlags.Pointer)) { 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)); } if (valueType == typeof(Tag)) { return(new Tag(reader.ReadInt32())); } // TagInstance = Tag reference if (valueType == typeof(CachedTagInstance)) { return(DeserializeTagReference(reader, context, valueInfo)); } // ResourceAddress = Resource address if (valueType == typeof(CacheAddress)) { return(new CacheAddress(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[]) && valueInfo.Length == 0) { return(DeserializeDataReference(reader, context)); } // Color types if (valueType == typeof(RealRgbColor)) { return(new RealRgbColor(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle())); } else if (valueType == typeof(RealArgbColor)) { return(new RealArgbColor(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle())); } else if (valueType == typeof(ArgbColor)) { return(new ArgbColor(reader.ReadByte(), reader.ReadByte(), reader.ReadByte(), reader.ReadByte())); } if (valueType == typeof(Point2d)) { return(new Point2d(reader.ReadInt16(), reader.ReadInt16())); } if (valueType == typeof(Rectangle2d)) { return(new Rectangle2d(reader.ReadInt16(), reader.ReadInt16(), reader.ReadInt16(), reader.ReadInt16())); } if (valueType == typeof(RealEulerAngles2d)) { var i = Angle.FromRadians(reader.ReadSingle()); var j = Angle.FromRadians(reader.ReadSingle()); return(new RealEulerAngles2d(i, j)); } else if (valueType == typeof(RealEulerAngles3d)) { var i = Angle.FromRadians(reader.ReadSingle()); var j = Angle.FromRadians(reader.ReadSingle()); var k = Angle.FromRadians(reader.ReadSingle()); return(new RealEulerAngles3d(i, j, k)); } if (valueType == typeof(RealPoint2d)) { return(new RealPoint2d(reader.ReadSingle(), reader.ReadSingle())); } if (valueType == typeof(RealPoint3d)) { return(new RealPoint3d(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle())); } if (valueType == typeof(RealVector2d)) { return(new RealVector2d(reader.ReadSingle(), reader.ReadSingle())); } if (valueType == typeof(RealVector3d)) { return(new RealVector3d(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle())); } if (valueType == typeof(RealQuaternion)) { return(new RealQuaternion(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle())); } if (valueType == typeof(RealPlane2d)) { return(new RealPlane2d(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle())); } if (valueType == typeof(RealPlane3d)) { return(new RealPlane3d(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle())); } if (valueType == typeof(RealMatrix4x3)) { return(new RealMatrix4x3( reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle())); } // StringID if (valueType == typeof(StringId)) { return(new StringId(reader.ReadUInt32(), Version)); } // 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(Bounds <>)) { return(DeserializeRange(reader, context, valueType)); } if (valueType == typeof(VertexShaderReference)) { return(DeserializeVertexShaderReference(reader, context)); } if (valueType == typeof(PixelShaderReference)) { return(DeserializePixelShaderReference(reader, context)); } // Assume the value is a structure return(DeserializeStruct(reader, context, TagStructure.GetTagStructureInfo(valueType, Version))); }
/// <summary> /// Serializes a tag reference. /// </summary> /// <param name="context">The serialization context to use.</param> /// <param name="block">The block 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 void SerializeTagReference(ISerializationContext context, IDataBlock block, CachedTag referencedTag, TagFieldAttribute valueInfo) { if (referencedTag != null && referencedTag.Group == TagGroup.None) { referencedTag = null; } if (referencedTag != null && valueInfo != null && valueInfo.ValidTags != null) { foreach (string tag in valueInfo.ValidTags) { if (!referencedTag.IsInGroup(tag)) { throw new Exception($"Invalid group for tag reference: {referencedTag.Group.Tag}"); } } } block.AddTagReference(referencedTag, valueInfo == null ? false : valueInfo.Flags.HasFlag(Short)); if (valueInfo == null || !valueInfo.Flags.HasFlag(Short)) { block.Writer.Write((referencedTag != null) ? referencedTag.Group.Tag.Value : -1); block.Writer.Write(0); block.Writer.Write(0); } block.Writer.Write((referencedTag != null) ? referencedTag.Index : -1); }
/// <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> public Array DeserializeInlineArray(EndianReader reader, ISerializationContext context, TagFieldAttribute valueInfo, Type valueType) { // Create the array and read the elements in order var elementCount = valueInfo.Length; 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> /// Serializes a tag reference. /// </summary> /// <param name="context">The serialization context to use.</param> /// <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 void SerializeTagReference(ISerializationContext context, BinaryWriter writer, CachedTagInstance referencedTag, TagFieldAttribute valueInfo) { if ((referencedTag?.Index ?? 0) == -1) { referencedTag = context.GetTagByName(referencedTag.Group, referencedTag.Name); } if (referencedTag != null && valueInfo != null && valueInfo.ValidTags != null) { foreach (string tag in valueInfo.ValidTags) { if (!referencedTag.IsInGroup(tag)) { throw new Exception($"Invalid group for tag reference: {referencedTag.Group.Tag}"); } } } if (valueInfo == null || !valueInfo.Flags.HasFlag(TagFieldFlags.Short)) { 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 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 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.Align : 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 block. /// </summary> /// <param name="version"></param> /// <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(CacheVersion version, ISerializationContext context, MemoryStream tagStream, IDataBlock block, object list, Type listType, TagFieldAttribute valueInfo) { var writer = block.Writer; var count = 0; if (list != null) { // Use reflection to get the number of elements in the list var countProperty = listType.GetProperty("Count"); count = (int)countProperty.GetValue(list); } if (count == 0) { writer.Write(0); writer.Write(0); writer.Write(0); return; } var elementType = listType.GenericTypeArguments[0]; TagStructureAttribute structure; try { structure = TagStructure.GetTagStructureInfo(elementType, Version).Structure; } catch { structure = null; } // Serialize each value in the list to a data block var tagBlock = context.CreateBlock(); var enumerableList = (System.Collections.IEnumerable)list; foreach (var val in enumerableList) { SerializeValue(version, context, tagStream, tagBlock, val, null, elementType); } // Ensure the block is aligned correctly var align = Math.Max(DefaultBlockAlign, (valueInfo != null) ? valueInfo.Align : 0); StreamUtil.Align(tagStream, (int)align); // Finalize the block and write the tag block reference writer.Write(count); block.WritePointer(tagBlock.Finalize(tagStream), listType); writer.Write(0); }
/// <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> /// 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 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> /// 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> /// Serializes a value. /// </summary> /// <param name="version"></param> /// <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(CacheVersion version, 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(version, context, tagStream, block, val, valueInfo, valueType); } }
public object PreSerialize(TagFieldAttribute info, object obj) { return(obj); }
public override void SerializeTagBlock(CacheVersion version, ISerializationContext context, MemoryStream tagStream, IDataBlock block, object list, Type listType, TagFieldAttribute valueInfo) { if (context.GetType() != typeof(ResourceDefinitionSerializationContext)) { throw new Exception($"Invalid context type given resource deserialization"); } if (block.GetType() != typeof(ResourceDefinitionSerializationContext.ResourceDefinitionDataBlock)) { throw new Exception($"Invalid block type given resource deserialization"); } var resourceBlock = block as ResourceDefinitionSerializationContext.ResourceDefinitionDataBlock; var resourceContext = context as ResourceDefinitionSerializationContext; var writer = block.Writer; var count = 0; if (list != null) { // Use reflection to get the number of elements in the list var countProperty = listType.GetProperty("Count"); count = (int)countProperty.GetValue(list); } if (count == 0) { writer.Write(0); writer.Write(0); writer.Write(0); return; } var elementType = listType.GenericTypeArguments[0]; CacheAddressType addressType = (CacheAddressType)listType.GetField("AddressType").GetValue(list); // Serialize each value in the list to a data block var resourceBlock2 = (ResourceDefinitionSerializationContext.ResourceDefinitionDataBlock)resourceContext.CreateBlock(); resourceBlock2.BlockType = addressType; var addressTypeStream = (MemoryStream)resourceContext.GetWriter(addressType).BaseStream; var enumerableList = (System.Collections.IEnumerable)list; foreach (var val in enumerableList) { SerializeValue(version, resourceContext, tagStream, resourceBlock2, val, null, elementType); } // Ensure the block is aligned correctly var align = 0x10; StreamUtil.Align(resourceBlock2.Stream, align); // Finalize the block and write the tag block reference using a cache address var offset = resourceBlock2.Finalize(addressTypeStream); // offset of the data in the tagblock on the actual stream //var blockOffset = addressTypeStream.Position; // no need to fix that particular fixup later var address = new CacheAddress(addressType, (int)offset); var resourceFixup = new ResourceFixupLocation { Address = address, BlockOffset = (uint)writer.BaseStream.Position + 0x4 }; foreach (var fixup in resourceBlock2.FixupLocations) { fixup.BlockOffset += offset; resourceContext.FixupLocations.Add(fixup); } foreach (var location in resourceBlock2.InteropLocations) { location.Address = new CacheAddress(location.Address.Type, (int)(location.Address.Offset + offset)); resourceContext.InteropLocations.Add(location); } resourceBlock.FixupLocations.Add(resourceFixup); writer.Write(count); writer.Write(address.Value); // write address as 0, we use the fixups writer.Write(0); }