/// <summary> /// Read an object reference. /// </summary> /// <param name="context">AMF decoding context.</param> /// <param name="reader">AMF reader.</param> /// <param name="index">Reference index.</param> /// <param name="reference">Reference value.</param> /// <returns>Referenced object or <c>null</c> if value does not contain a reference.</returns> /// <exception cref="SerializationException">Invalid reference.</exception> private static bool ReadReference(AmfContext context, AmfStreamReader reader, out int index, out int reference) { reference = ReadUint29(reader); //The first bit is a flag with value 0 to imply that this is not an instance but a reference if ((reference & 0x1) == 0) { //The remaining 1 to 28 significant bits are used to encode an object reference index index = reference >> 1; if (context.References.Count <= index) throw new SerializationException("Invalid reference index: " + index); return true; } index = -1; return false; }
/// <summary> /// Read a strict array. /// </summary> /// <remarks> /// Type declaration: /// <c>array-count = U32 /// strict-array-type = array-count *(value-type)</c> /// </remarks> private void ReadStrictArray(AmfContext context, AmfStreamReader reader, XmlWriter output = null) { context.References.Track(); var length = reader.ReadUInt32(); if (output != null) { output.WriteStartElement(AmfxContent.Array); output.WriteAttributeString(AmfxContent.ArrayLength, length.ToString()); } for (var i = 0; i < length; i++) ReadAmfValue(context, reader, output); if (output != null) output.WriteEndElement(); }
/// <summary> /// Write an object. /// </summary> private void WriteObject(AmfContext context, AmfStreamWriter writer, XmlReader input) { context.References.Add(new AmfReference { AmfxType = AmfxContent.Object }); WriteTypeMarker(writer, Amf3TypeMarker.Object); AmfTypeTraits traits; var typeName = string.Empty; if (input.HasAttributes) typeName = input.GetAttribute(AmfxContent.ObjectType); #region Write traits input.Read(); //Send traits by value if (!input.IsEmptyElement) { traits = new AmfTypeTraits { TypeName = typeName }; context.TraitsReferences.Add(traits); var traitsReader = input.ReadSubtree(); traitsReader.MoveToContent(); traitsReader.ReadStartElement(); var members = new List<string>(); while (input.NodeType != XmlNodeType.EndElement) members.Add(traitsReader.ReadElementContentAsString()); traits.ClassMembers = members.ToArray(); //The first bit is a flag with value 1. //The second bit is a flag with value 1. //The third bit is a flag with value 0. var flag = 0x3; //00000011 if (traits.IsExternalizable) flag |= 0x4; //00000111 //The fourth bit is a flag specifying whether the type is dynamic. //A value of 0 implies not dynamic, a value of 1 implies dynamic. if (traits.IsDynamic) flag |= 0x8; //00001011 //The remaining 1 to 25 significant bits are used to encode the number //of sealed traits member names that follow after the class name. var count = traits.ClassMembers.Count(); flag |= count << 4; WriteUInt29(writer, flag); WriteUtf8(context, writer, traits.TypeName); //Write member names foreach (var member in traits.ClassMembers) WriteUtf8(context, writer, member); } //Send traits by reference else { var index = Convert.ToInt32(input.GetAttribute(AmfxContent.TraitsId), CultureInfo.InvariantCulture); traits = context.TraitsReferences[index]; var flag = index & UInt29Mask; //Truncate value to UInt29 //The first bit is a flag with value 1. //The second bit is a flag (representing whether a trait //reference follows) with value 0 to imply that this objects //traits are being sent by reference. The remaining 1 to 27 //significant bits are used to encode a trait reference index. flag = (flag << 2) | 0x1; WriteUInt29(writer, flag); } input.Read(); #endregion #region Write members for (var i = 0; i < traits.ClassMembers.Length; i++) { WriteAmfValue(context, input, writer); input.Read(); } #endregion }
/// <summary> /// Write an XML. /// </summary> private static void WriteXml(AmfContext context, AmfStreamWriter writer, XmlReader input) { context.References.Add(new AmfReference {AmfxType = AmfxContent.Xml}); WriteTypeMarker(writer, Amf3TypeMarker.Xml); input.Read(); var encoded = input.Value; input.Read(); var decoded = Encoding.UTF8.GetBytes(encoded); WriteUtf8(writer, decoded); }
/// <summary> /// Write a string. /// </summary> private static void WriteString(AmfContext context, AmfStreamWriter writer, XmlReader input) { WriteTypeMarker(writer, Amf3TypeMarker.String); string value; if (input.IsEmptyElement) { if (input.AttributeCount > 0) { var index = Convert.ToInt32(input.GetAttribute(AmfxContent.StringId), CultureInfo.InvariantCulture); WriteReference(writer, index); return; } value = string.Empty; } else { input.Read(); value = input.Value; input.Read(); } WriteUtf8(context, writer, value); }
/// <summary> /// Write a date. /// </summary> private static void WriteDate(AmfContext context, AmfStreamWriter writer, XmlReader input) { context.References.Add(new AmfReference { AmfxType = AmfxContent.Date }); WriteTypeMarker(writer, Amf3TypeMarker.Date); var milliseconds = input.ReadElementContentAsDouble(); //The first bit is a flag with value 1. //The remaining bits are not used. WriteUInt29(writer, 0 | 0x1); writer.Write(milliseconds); }
/// <summary> /// Read a string reference. /// </summary> /// <param name="context">AMF decoding context.</param> /// <param name="reader">AMF reader.</param> /// <param name="index">Reference index.</param> /// <param name="reference">Reference value.</param> /// <returns>Referenced string or <c>null</c> if value does not contain a reference.</returns> /// <exception cref="SerializationException">Invalid reference.</exception> private static string ReadStringReference(AmfContext context, AmfStreamReader reader, out int index, out int reference) { reference = ReadUint29(reader); //The first bit is a flag with value 0 if ((reference & 0x1) == 0) { //The remaining 1 to 28 significant bits are used to encode a string reference table index index = reference >> 1; if (context.StringReferences.Count <= index) throw new SerializationException("Invalid reference index: " + index); return context.StringReferences[index]; } index = -1; return null; }
/// <summary> /// Read an object. /// </summary> private void ReadObject(AmfContext context, AmfStreamReader reader, XmlWriter output = null) { var reference = reader.PeekChar(); int index; if ((reference & 0x1) == 0 && ReadReference(context, reader, out index, out reference)) { if (output != null) WriteReference(index, output); return; } context.References.Track(); int traitsindex, traitsreference; AmfTypeTraits traits; #region Read object's traits if ((traits = ReadTraitsReference(context, reader, out traitsindex, out traitsreference)) == null) { var isExternalizable = ((traitsreference & 0x4) == 4); var isDynamic = ((traitsreference & 0x8) == 8); var typeName = ReadString(context, reader); var count = (traitsreference >> 4); var classMembers = new string[count]; for (var i = 0; i < count; i++) classMembers[i] = ReadString(context, reader); traits = new AmfTypeTraits { IsDynamic = isDynamic, IsExternalizable = isExternalizable, TypeName = typeName, //No property names are included for types //that are externizable or dynamic ClassMembers = isDynamic || isExternalizable ? new string[0] : classMembers }; context.TraitsReferences.Add(traits); } #endregion #if DEBUG Debug.WriteLine(string.Format(Errors.Amf3Decoder_ReadObject_Debug_Name, traits.TypeName)); if (traits.IsDynamic) Debug.WriteLine(Errors.Amf3Decoder_ReadObject_Debug_Dynamic); else if (traits.IsExternalizable) Debug.WriteLine(Errors.Amf3Decoder_ReadObject_Debug_Externizable); else Debug.WriteLine(string.Format(Errors.Amf3Decoder_ReadObject_Debug_Members, traits.ClassMembers.Length)); #endif using (var ms = new MemoryStream()) { #region Reading object members var members = new List<string>(); var settings = new XmlWriterSettings { }; var buffer = XmlWriter.Create(ms, settings); buffer.WriteStartDocument(); buffer.WriteStartElement("buffer", AmfxContent.Namespace); #if DEBUG var memberPosition = 0; #endif //Read object's properties foreach (var classMember in traits.ClassMembers) { #if DEBUG Debug.WriteLine(string.Format(Errors.Amf3Decoder_ReadObject_Debug_ReadingField, memberPosition, classMember)); memberPosition++; #endif ReadAmfValue(context, reader, buffer); members.Add(classMember); } //Read dynamic properties too if (traits.IsDynamic) { #if DEBUG Debug.WriteLine(Errors.Amf3Decoder_ReadObject_Debug_ReadingDynamic); #endif var key = ReadString(context, reader); while (key != string.Empty) { #if DEBUG Debug.WriteLine(string.Format(Errors.Amf3Decoder_ReadObject_Debug_ReadingDynamicField, key)); #endif ReadAmfValue(context, reader, buffer); members.Add(key); key = ReadString(context, reader); } #if DEBUG Debug.WriteLine(Errors.Amf3Decoder_ReadObject_Debug_DynamicEnd); #endif } buffer.WriteEndElement(); buffer.WriteEndDocument(); buffer.Flush(); #endregion #if DEBUG Debug.WriteLine(Errors.Amf3Decoder_ReadObject_Debug_End); #endif #region Writing member values if (output != null) { output.WriteStartElement(AmfxContent.Object); if (traits.TypeName != AmfTypeTraits.BaseTypeAlias) output.WriteAttributeString(AmfxContent.ObjectType, traits.TypeName); output.WriteStartElement(AmfxContent.Traits); //Regular traits object //Always write traits for dynamic objects if (traitsindex == -1 || traits.IsDynamic) { if (traits.IsExternalizable) { output.WriteAttributeString(AmfxContent.TraitsExternizable, AmfxContent.True); } else { //Write object members foreach (var classMember in members) { output.WriteStartElement(AmfxContent.String); output.WriteValue(classMember); output.WriteEndElement(); } } } //Traits object reference else { output.WriteAttributeString(AmfxContent.TraitsId, traitsindex.ToString()); } output.WriteEndElement(); //End of traits //Write object members if (members.Count > 0) { ms.Position = 0; var bufferreader = XmlReader.Create(ms); bufferreader.Read(); bufferreader.ReadStartElement(); while (bufferreader.Depth >= 1) output.WriteNode(bufferreader, false); } output.WriteEndElement(); //End of object } #endregion } }
protected override void WriteAmfValue(AmfContext context, XmlReader input, AmfStreamWriter writer) { if (context.AmfVersion != AmfVersion.Amf0) throw new InvalidOperationException(string.Format(Errors.Amf0Decoder_ReadAmfValue_AmfVersionNotSupported, context.AmfVersion)); if (input == null) throw new ArgumentNullException("input"); if (context == null) throw new ArgumentNullException("context"); if (input.NodeType != XmlNodeType.Element) throw new XmlException(string.Format("Element node expected, {0} found.", input.NodeType)); #region Primitive values switch (input.Name) { case AmfxContent.Null: WriteNull(writer); return; case AmfxContent.True: WriteBoolean(writer, true); return; case AmfxContent.False: WriteBoolean(writer, false); return; } #endregion #region Complex values var reader = input.ReadSubtree(); reader.MoveToContent(); switch (reader.Name) { case AmfxContent.Integer: case AmfxContent.Double: WriteNumber(writer, reader); break; case AmfxContent.String: WriteString(writer, reader); break; case AmfxContent.Date: WriteDate(writer, reader); break; case AmfxContent.Xml: WriteXml(writer, reader); break; case AmfxContent.Reference: WriteReference(context, writer, reader); break; case AmfxContent.Array: WriteArray(context, writer, reader); break; case AmfxContent.Object: WriteObject(context, writer, reader); break; default: throw new NotSupportedException("Unexpected AMFX type: " + reader.Name); } #endregion }
/// <summary> /// Write an object. /// </summary> private void WriteObject(AmfContext context, AmfStreamWriter writer, XmlReader input) { context.References.Add(new AmfReference { AmfxType = AmfxContent.Object }); WriteTypeMarker(writer, Amf0TypeMarker.Object); var typeName = string.Empty; if (input.HasAttributes) typeName = input.GetAttribute(AmfxContent.ObjectType); #region Read traits var traits = new AmfTypeTraits { TypeName = typeName }; while (input.Read()) { if (input.NodeType != XmlNodeType.Element && input.Name != AmfxContent.Traits) continue; if (!input.IsEmptyElement) { var traitsReader = input.ReadSubtree(); traitsReader.MoveToContent(); traitsReader.ReadStartElement(); var members = new List<string>(); while (input.NodeType != XmlNodeType.EndElement) members.Add(traitsReader.ReadElementContentAsString()); traits.ClassMembers = members.ToArray(); } break; } #endregion #region Type name //Untyped object if(string.IsNullOrEmpty(traits.TypeName)) { WriteTypeMarker(writer, Amf0TypeMarker.Object); } //Strongly-typed object else { WriteTypeMarker(writer, Amf0TypeMarker.TypedObject); var typeNameData = Encoding.UTF8.GetBytes(traits.TypeName); writer.Write((ushort)typeNameData.Length); writer.Write(typeNameData); } #endregion #region Write members var i = 0; while (input.Read()) { if (input.NodeType != XmlNodeType.Element) continue; var memberName = traits.ClassMembers[i]; var memberReader = input.ReadSubtree(); memberReader.MoveToContent(); WriteUtf8(writer, memberName); WriteAmfValue(context, memberReader, writer); i++; } #endregion WriteUtf8(writer, string.Empty); WriteTypeMarker(writer, Amf0TypeMarker.ObjectEnd); }
/// <summary> /// Write an array. /// </summary> private void WriteArray(AmfContext context, AmfStreamWriter writer, XmlReader input) { context.References.Add(new AmfReference {AmfxType = AmfxContent.Array}); var length = Convert.ToUInt32(input.GetAttribute(AmfxContent.ArrayLength)); writer.Write(length); if (length == 0) return; input.MoveToContent(); while (input.Read()) { if (input.NodeType != XmlNodeType.Element) continue; for (var i = 0; i < length; i++) { var itemreader = input.ReadSubtree(); itemreader.MoveToContent(); WriteAmfValue(context, itemreader, writer); } } }
/// <summary> /// Write a reference value. /// </summary> private static void WriteReference(AmfContext context, AmfStreamWriter writer, XmlReader input) { var index = Convert.ToInt32(input.GetAttribute(AmfxContent.ReferenceId)); var proxy = context.References[index]; switch(proxy.AmfxType) { case AmfxContent.Array: case AmfxContent.Object: WriteTypeMarker(writer, Amf0TypeMarker.Reference); writer.Write((ushort)index); break; default: throw new InvalidOperationException(string.Format("AMFX type '{0}' cannot be send by reference.", proxy.AmfxType)); } }
/// <summary> /// Read AMF value from the current position. /// </summary> /// <param name="context">AMF context.</param> /// <param name="reader">AMF stream reader.</param> /// <param name="output">AMFX output writer.</param> /// <exception cref="NotSupportedException">AMF type is not supported.</exception> /// <exception cref="FormatException">Invalid data format.</exception> /// <exception cref="SerializationException">Error during deserialization.</exception> protected abstract void ReadAmfValue(AmfContext context, AmfStreamReader reader, XmlWriter output = null);
protected override void ReadAmfValue(AmfContext context, AmfStreamReader reader, XmlWriter output = null) { if (context.AmfVersion != AmfVersion.Amf0) throw new InvalidOperationException(string.Format(Errors.Amf0Decoder_ReadAmfValue_AmfVersionNotSupported, context.AmfVersion)); Amf0TypeMarker dataType; try { //Read a type marker byte dataType = (Amf0TypeMarker)reader.ReadByte(); } catch (Exception e) { throw new FormatException(Errors.Amf0Decoder_ReadAmfValue_TypeMarkerMissing, e); } //Special case if(dataType == Amf0TypeMarker.AvmPlusObject) { var newContext = new AmfContext(AmfVersion.Amf3); ReadAmfValue(newContext, reader, output); return; } ReadValue(context, reader, dataType, output); }
/// <summary> /// Read a value of a given type from current reader's position. /// </summary> /// <remarks> /// Current reader position must be just after a value type marker of a type to read. /// </remarks> /// <param name="context">AMF decoding context.</param> /// <param name="reader">AMF stream reader.</param> /// <param name="type">Type of the value to read.</param> /// <param name="output">AMFX output.</param> /// <exception cref="NotSupportedException">AMF type is not supported.</exception> /// <exception cref="FormatException">Unknown data format.</exception> /// <exception cref="SerializationException">Error during deserialization.</exception> private void ReadValue(AmfContext context, AmfStreamReader reader, Amf0TypeMarker type, XmlWriter output = null) { switch (type) { case Amf0TypeMarker.Null: case Amf0TypeMarker.Undefined: break; case Amf0TypeMarker.Boolean: reader.ReadBoolean(); break; case Amf0TypeMarker.Number: reader.ReadDouble(); break; case Amf0TypeMarker.String: ReadString(reader, output); break; case Amf0TypeMarker.LongString: ReadLongString(reader, output); break; case Amf0TypeMarker.Date: ReadDate(reader, output); break; case Amf0TypeMarker.XmlDocument: ReadXml(reader, output); break; case Amf0TypeMarker.Reference: ReadReference(context, reader, output); break; case Amf0TypeMarker.Object: ReadObject(context, reader, output); break; case Amf0TypeMarker.TypedObject: ReadObject(context, reader, output, true); break; case Amf0TypeMarker.EcmaArray: ReadEcmaArray(context, reader, output); break; case Amf0TypeMarker.StrictArray: ReadStrictArray(context, reader, output); break; case Amf0TypeMarker.MovieClip: case Amf0TypeMarker.RecordSet: case Amf0TypeMarker.Unsupported: throw new NotSupportedException(string.Format(Errors.Amf0Deserializer_ReadValue_UnsupportedType, type)); default: throw new FormatException(string.Format(Errors.Amf0Decoder_ReadValue_UnknownType, (byte)type)); } if (output != null) output.Flush(); }
/// <summary> /// Read an XML document. /// </summary> /// <remarks> /// Type declaration: /// <c>U29X-value = U29 (The first (low) bit is a flag with value 1. /// The remaining 1 to 28 significant bits are used to encode the byte-length /// of the UTF-8 encoded representation of the XML or XMLDocument). /// xml-doc-type = xml-doc-marker (U29O-ref | (U29X-value *(UTF8-char)))</c> /// </remarks> private static void ReadXml(AmfContext context, AmfStreamReader reader, XmlWriter output = null) { int index, reference; if (ReadReference(context, reader, out index, out reference)) { if (output != null) WriteReference(index, output); return; } context.References.Track(); //Get XML string length var length = (reference >> 1); var value = ReadUtf8(reader, length); if (output != null) { output.WriteStartElement(AmfxContent.Xml); output.WriteValue(value); output.WriteEndElement(); } }
/// <summary> /// Read an array. /// </summary> /// <remarks> /// Type declaration: /// <c>U29A-value = U29 (The first (low) bit is a flag with value 1. /// The remaining 1 to 28 significant bits are used to encode /// the count of the dense portion of the Array). /// assoc-value = UTF-8-vr value-type /// array-type = array-marker (U29O-ref | /// (U29A-value (UTF-8-empty | *(assoc-value) UTF-8-empty) *(value-type)))</c> /// </remarks> private void ReadArray(AmfContext context, AmfStreamReader reader, XmlWriter output = null) { int index, reference; if (ReadReference(context, reader, out index, out reference)) { if (output != null) WriteReference(index, output); return; } context.References.Track(); var length = reference >> 1; var key = ReadString(context, reader); if (output != null) { output.WriteStartElement(AmfxContent.Array); output.WriteAttributeString(AmfxContent.ArrayLength, length.ToString()); } //ECMA array if (key != string.Empty) { if (output != null) output.WriteAttributeString(AmfxContent.ArrayEcma, AmfxContent.True); //Read associative values do { if (output != null) { output.WriteStartElement(AmfxContent.ArrayItem); output.WriteAttributeString(AmfxContent.ArrayKey, key); } ReadAmfValue(context, reader, output); key = ReadString(context, reader); if (output != null) output.WriteEndElement(); } while (key != string.Empty); //Read array values for (var i = 0; i < length; i++) ReadAmfValue(context, reader, output); } //Regular array else { //Read array values for (var i = 0; i < length; i++) ReadAmfValue(context, reader, output); } if (output != null) output.WriteEndElement(); }
/// <summary> /// Write AMF value from the current position. /// </summary> /// <param name="context">AMF decoding context.</param> /// <param name="input">AMFX input reader.</param> /// <param name="writer">AMF stream writer.</param> /// <exception cref="NotSupportedException">AMF type is not supported.</exception> /// <exception cref="FormatException">Invalid data format.</exception> /// <exception cref="SerializationException">Error during serialization.</exception> protected abstract void WriteAmfValue(AmfContext context, XmlReader input, AmfStreamWriter writer);
/// <summary> /// Read a traits reference. /// </summary> /// <param name="context">AMF decoding context.</param> /// <param name="reader">AMF reader.</param> /// <param name="index">Reference index.</param> /// <param name="reference">Reference value.</param> /// <returns>Referenced traits object or <c>null</c> if value does not contain a reference.</returns> /// <exception cref="SerializationException">Invalid reference.</exception> private static AmfTypeTraits ReadTraitsReference(AmfContext context, AmfStreamReader reader, out int index, out int reference) { reference = ReadUint29(reader); //The first bit is a flag with value 1. The second bit is a flag with value 0 to imply //that this objects traits are being sent by reference if ((reference & 0x3) == 1) //x01 & x11 == x01 { //The remaining 1 to 27 significant bits are used to encode a trait reference index index = reference >> 2; if (context.TraitsReferences.Count <= index) throw new SerializationException("Invalid reference index: " + index); return context.TraitsReferences[index]; } index = -1; return null; }
protected override void ReadAmfValue(AmfContext context, AmfStreamReader reader, XmlWriter output = null) { //Work in a legacy context if (context.AmfVersion == AmfVersion.Amf0) { base.ReadAmfValue(context, reader, output); return; } Amf3TypeMarker dataType; try { //Read a type marker byte dataType = (Amf3TypeMarker)reader.ReadByte(); } catch (Exception e) { #if DEBUG Debug.WriteLine(string.Format(Errors.Amf3Decoder_ReadValue_InvalidMarker, reader.BaseStream.Position)); #endif throw new FormatException(string.Format(Errors.Amf3Decoder_ReadValue_TypeMarkerNotFound, reader.BaseStream.Position), e); } ReadValue(context, reader, dataType, output); #if DEBUG Debug.WriteLine(string.Format(Errors.Amf3Decoder_ReadValue_End, dataType, reader.BaseStream.Position)); #endif }
/// <summary> /// Write a byte array. /// </summary> private static void WriteByteArray(AmfContext context, AmfStreamWriter writer, XmlReader input) { context.References.Add(new AmfReference { AmfxType = AmfxContent.ByteArray }); WriteTypeMarker(writer, Amf3TypeMarker.ByteArray); input.Read(); var encoded = input.Value; input.Read(); var bytes = Convert.FromBase64String(encoded); //The first bit is a flag with value 1. //The remaining 1 to 28 significant bits are used //to encode the byte-length of the data var flag = (bytes.Length << 1) | 0x1; WriteUInt29(writer, flag); writer.Write(bytes); }
/// <summary> /// Read a value of a given type from current reader's position. /// </summary> /// <remarks> /// Current reader position must be just after a value type marker of a type to read. /// </remarks> /// <param name="context">AMF decoding context.</param> /// <param name="reader">AMF reader.</param> /// <param name="type">Type of the value to read.</param> /// <param name="output">AMFX output.</param> /// <exception cref="NotSupportedException">AMF type is not supported.</exception> /// <exception cref="FormatException">Unknown data format.</exception> /// <exception cref="SerializationException">Error during deserialization.</exception> private void ReadValue(AmfContext context, AmfStreamReader reader, Amf3TypeMarker type, XmlWriter output = null) { #if DEBUG Debug.WriteLine(string.Format(Errors.Amf3Decoder_ReadValue_Debug, type, reader.BaseStream.Position)); #endif switch (type) { case Amf3TypeMarker.Null: case Amf3TypeMarker.Undefined: WriteEmptyElement(type.ToAmfxName(), output); break; case Amf3TypeMarker.False: WriteEmptyElement(type.ToAmfxName(), output); break; case Amf3TypeMarker.True: WriteEmptyElement(type.ToAmfxName(), output); break; case Amf3TypeMarker.Integer: ReadInteger(reader, output); break; case Amf3TypeMarker.Double: ReadDouble(reader, output); break; case Amf3TypeMarker.String: ReadString(context, reader, output); break; case Amf3TypeMarker.Date: ReadDate(context, reader, output); break; case Amf3TypeMarker.ByteArray: ReadByteArray(context, reader, output); break; case Amf3TypeMarker.Xml: case Amf3TypeMarker.XmlDocument: ReadXml(context, reader, output); break; case Amf3TypeMarker.Array: ReadArray(context, reader, output); break; case Amf3TypeMarker.Object: ReadObject(context, reader, output); break; default: throw new NotSupportedException("Type '" + type + "' is not supported."); } if (output != null) output.Flush(); }
/// <summary> /// Write an integer. /// </summary> private static void WriteReference(AmfContext context, AmfStreamWriter writer, XmlReader input) { var index = Convert.ToInt32(input.GetAttribute(AmfxContent.ReferenceId), CultureInfo.InvariantCulture); var proxy = context.References[index]; switch(proxy.AmfxType) { case AmfxContent.Date: WriteTypeMarker(writer, Amf3TypeMarker.Date); break; case AmfxContent.Xml: WriteTypeMarker(writer, Amf3TypeMarker.Xml); break; case AmfxContent.Array: WriteTypeMarker(writer, Amf3TypeMarker.Array); break; case AmfxContent.ByteArray: WriteTypeMarker(writer, Amf3TypeMarker.ByteArray); break; case AmfxContent.Object: WriteTypeMarker(writer, Amf3TypeMarker.Object); break; default: throw new InvalidOperationException(string.Format("AMFX type '{0}' cannot be send by reference.", proxy.AmfxType)); } WriteReference(writer, index); }
/// <summary> /// Read a string. /// </summary> /// <remarks> /// Type declaration: /// <c>U29S-ref = U29 (The first (low) bit is a flag with value 0. The remaining 1 to 28 /// significant bits are used to encode a string reference table index (an integer)). /// U29S-value = U29 (The first (low) bit is a flag with value 1. The remaining 1 to 28 significant /// bits are used to encode the byte-length of the UTF-8 encoded representation of the string). /// UTF-8-empty = 0x01 (The UTF-8-vr empty string which is never sent by reference). /// UTF-8-vr = U29S-ref | (U29S-value *(UTF8-char)) /// string-type = string-marker UTF-8-vr</c> /// </remarks> private static string ReadString(AmfContext context, AmfStreamReader reader, XmlWriter output = null) { int index, reference; string cache; if ((cache = ReadStringReference(context, reader, out index, out reference)) != null) { if (output != null) { output.WriteStartElement(AmfxContent.String); output.WriteAttributeString(AmfxContent.StringId, index.ToString()); output.WriteEndElement(); } return cache; } //Get string length var length = (reference >> 1); var value = ReadUtf8(reader, length); if (value != string.Empty) context.StringReferences.Add(value); if (output != null) { output.WriteStartElement(AmfxContent.String); output.WriteValue(value); output.WriteEndElement(); } return value; }
/// <summary> /// Write UTF-8 string. /// </summary> private static void WriteUtf8(AmfContext context, AmfStreamWriter writer, string value) { if (value == null) value = string.Empty; //A special case if (value == string.Empty) { writer.Write((byte)0x01); return; } var index = context.StringReferences.IndexOf(value); if(index != -1) { WriteReference(writer, index); return; } context.StringReferences.Add(value); var decoded = Encoding.UTF8.GetBytes(value); WriteUtf8(writer, decoded); }
/// <summary> /// Read a date. /// </summary> /// <remarks> /// Type declaration: /// <c>U29D-value = U29 (The first (low) bit is a flag with value 1. /// The remaining bits are not used). /// date-time = DOUBLE (A 64-bit integer value transported as a double). /// date-type = date-marker (U29O-ref | (U29D-value date-time))</c> /// </remarks> private static void ReadDate(AmfContext context, AmfStreamReader reader, XmlWriter output = null) { int index, reference; if (ReadReference(context, reader, out index, out reference)) { if (output != null) WriteReference(index, output); return; } context.References.Track(); //Dates are represented as an Unix time stamp, but in milliseconds var milliseconds = reader.ReadDouble(); var value = milliseconds.ToString(); if (output != null) { output.WriteStartElement(AmfxContent.Date); output.WriteValue(value); output.WriteEndElement(); } }
/// <summary> /// Write an array. /// </summary> private void WriteArray(AmfContext context, AmfStreamWriter writer, XmlReader input) { context.References.Add(new AmfReference { AmfxType = AmfxContent.Array }); WriteTypeMarker(writer, Amf3TypeMarker.Array); var length = Convert.ToInt32(input.GetAttribute(AmfxContent.ArrayLength), CultureInfo.InvariantCulture); //The first bit is a flag with value 1. //The remaining 1 to 28 significant bits //are used to encode the count of the dense //portion of the Array. var size = (length << 1) | 0x1; WriteUInt29(writer, size); WriteUtf8(context, writer, string.Empty); //No associative values if (length == 0) return; input.Read(); for (var i = 0; i < length; i++) { WriteAmfValue(context, input, writer); input.Read(); } }
/// <summary> /// Read a byte array. /// </summary> /// <remarks> /// Type declaration: /// <c>U29B-value = U29 (The first (low) bit is a flag with value 1. /// The remaining 1 to 28 significant bits are used to encode the /// byte-length of the ByteArray). /// bytearray-type = bytearray-marker (U29O-ref | U29B-value *(U8))</c> /// </remarks> private static void ReadByteArray(AmfContext context, AmfStreamReader reader, XmlWriter output = null) { int index, reference; if (ReadReference(context, reader, out index, out reference)) { if (output != null) WriteReference(index, output); return; } context.References.Track(); //Get array length var length = (reference >> 1); var data = length == 0 ? new byte[] { } : reader.ReadBytes(length); var value = Convert.ToBase64String(data); if (output != null) { output.WriteStartElement(AmfxContent.ByteArray); output.WriteValue(value); output.WriteEndElement(); } }
protected override void WriteAmfValue(AmfContext context, XmlReader input, AmfStreamWriter writer) { if (input == null) throw new ArgumentNullException("input"); if (context == null) throw new ArgumentNullException("context"); if (input.NodeType != XmlNodeType.Element) throw new XmlException(string.Format("Element node expected, {0} found.", input.NodeType)); if (context.AmfVersion != AmfVersion.Amf3) { context = new AmfContext(AmfVersion.Amf3); writer.Write((byte)Amf0TypeMarker.AvmPlusObject); } #region Primitive values switch (input.Name) { case AmfxContent.Null: WriteTypeMarker(writer, Amf3TypeMarker.Null); return; case AmfxContent.True: WriteTypeMarker(writer, Amf3TypeMarker.True); return; case AmfxContent.False: WriteTypeMarker(writer, Amf3TypeMarker.False); return; } #endregion #region Complex values switch (input.Name) { case AmfxContent.Integer: WriteInteger(writer, input); break; case AmfxContent.Double: WriteDouble(writer, input); break; case AmfxContent.String: WriteString(context, writer, input); break; case AmfxContent.Reference: WriteReference(context, writer, input); break; case AmfxContent.Date: WriteDate(context, writer, input); break; case AmfxContent.Xml: WriteXml(context, writer, input); break; case AmfxContent.Array: WriteArray(context, writer, input); break; case AmfxContent.ByteArray: WriteByteArray(context, writer, input); break; case AmfxContent.Object: WriteObject(context, writer, input); break; default: throw new NotSupportedException("Unexpected AMFX type: " + input.Name); } #endregion }
/// <summary> /// Read object properties map. /// </summary> /// <remarks> /// Type declaration: /// <c>object-property = (UTF-8 value-type) | (UTF-8-empty object-end-marker)</c> /// </remarks> /// <exception cref="SerializationException"></exception> private IList<string> ReadPropertiesMap(AmfContext context, AmfStreamReader reader, XmlWriter output = null) { try { var result = new List<string>(); var property = ReadString(reader); //Read first property's name //An empty property name indicates that object's declaration ends here while (property != string.Empty) { result.Add(property); ReadAmfValue(context, reader, output); property = ReadString(reader); } //Last byte is always an "ObjectEnd" marker var marker = (Amf0TypeMarker)reader.ReadByte(); //Something goes wrong if (marker != Amf0TypeMarker.ObjectEnd) throw new FormatException(Errors.Amf0Decoder_ReadPropertiesMap_UnexpectedObjectEnd); return result; } catch (Exception e) { throw new SerializationException(Errors.Amf0Decoder_ReadPropertiesMap_UnableToDeserialize, e); } }