Example #1
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 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);
            }
        }
Example #2
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}");
            }
        }
Example #3
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 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);
            }
        }
Example #4
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());
        }
Example #5
0
            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);
            }
Example #6
0
        /// <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);
            }
        }
Example #7
0
        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);
        }
Example #8
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));
        }
Example #9
0
 /// <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));
 }
Example #10
0
        /// <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);
            }
        }
Example #11
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>
        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);
        }
Example #12
0
        /// <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)));
        }
Example #13
0
        /// <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);
        }
Example #14
0
        /// <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);
        }
Example #15
0
        /// <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);
        }
Example #16
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 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);
        }
Example #17
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);
     }
 }
Example #18
0
        /// <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);
        }
Example #19
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);
 }
Example #20
0
        /// <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)));
        }
Example #21
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);
            }
        }
Example #22
0
        /// <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);
        }
Example #23
0
 /// <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);
     }
 }
Example #24
0
 public object PreSerialize(TagFieldAttribute info, object obj)
 {
     return(obj);
 }
Example #25
0
        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);
        }