/// <summary> /// Write an integer. /// </summary> /// <param name="context"> /// The context. /// </param> /// <param name="writer"> /// The writer. /// </param> /// <param name="input"> /// The input. /// </param> private static void WriteReference(AmfContext context, AmfStreamWriter writer, XmlReader input) { int index = Convert.ToInt32(input.GetAttribute(AmfxContent.ReferenceId)); AmfReference 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 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 = reader.ReadAMF3IntegerData(); //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 object properties map. /// </summary> /// <param name="context"> /// The context. /// </param> /// <param name="reader"> /// The reader. /// </param> /// <param name="output"> /// The output. /// </param> /// <remarks> /// Type declaration: /// <c>object-property = (UTF-8 value-type) | (UTF-8-empty object-end-marker)</c> /// </remarks> /// <exception cref="SerializationException"> /// </exception> /// <returns> /// The <see cref="IList"/>. /// </returns> private IList <string> ReadPropertiesMap(AmfContext context, AmfStreamReader reader, XmlWriter output = null) { try { var result = new List <string>(); string 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); this.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); } }
/// <summary> /// Write UTF-8 string. /// </summary> /// <param name="context"> /// The context. /// </param> /// <param name="writer"> /// The writer. /// </param> /// <param name="value"> /// The value. /// </param> 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; } int index = context.StringReferences.IndexOf(value); if (index != -1) { WriteReference(writer, index); return; } context.StringReferences.Add(value); byte[] decoded = Encoding.UTF8.GetBytes(value); WriteUtf8(writer, decoded); }
/// <summary> /// The encode. /// </summary> /// <param name="stream"> /// The stream. /// </param> /// <param name="input"> /// The input. /// </param> public override void Encode(Stream stream, XmlReader input) { var writer = new AmfStreamWriter(stream); AmfContext context = this.CreateDefaultContext(); this.WriteAmfValue(context, input, writer); }
/// <summary> /// The read amf value. /// </summary> /// <param name="context"> /// The context. /// </param> /// <param name="reader"> /// The reader. /// </param> /// <param name="output"> /// The output. /// </param> /// <exception cref="FormatException"> /// </exception> 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(Errors.Amf3Decoder_ReadValue_InvalidMarker, reader.BaseStream.Position); #endif throw new FormatException( string.Format(Errors.Amf3Decoder_ReadValue_TypeMarkerNotFound, reader.BaseStream.Position), e); } this.ReadValue(context, reader, dataType, output); #if DEBUG Debug.WriteLine(Errors.Amf3Decoder_ReadValue_End, dataType, reader.BaseStream.Position); #endif }
/// <summary> /// Write an array. /// </summary> /// <param name="context"> /// The context. /// </param> /// <param name="writer"> /// The writer. /// </param> /// <param name="input"> /// The input. /// </param> private void WriteArray(AmfContext context, AmfStreamWriter writer, XmlReader input) { context.References.Add(new AmfReference { AmfxType = AmfxContent.Array }); WriteTypeMarker(writer, Amf3TypeMarker.Array); int length = Convert.ToInt32(input.GetAttribute(AmfxContent.ArrayLength)); // 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. int size = (length << 1) | 0x1; WriteUInt29(writer, size); WriteUtf8(context, writer, string.Empty); // No associative values if (length == 0) { return; } input.Read(); for (int i = 0; i < length; i++) { this.WriteAmfValue(context, input, writer); input.Read(); } }
/// <summary> /// The decode. /// </summary> /// <param name="stream"> /// The stream. /// </param> /// <param name="output"> /// The output. /// </param> public override void Decode(Stream stream, XmlWriter output) { var reader = new AmfStreamReader(stream); AmfContext context = this.CreateDefaultContext(); this.ReadAmfValue(context, reader, output); }
/// <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); }
/// <summary> /// Read an ECMA array. /// </summary> /// <param name="context"> /// The context. /// </param> /// <param name="reader"> /// The reader. /// </param> /// <param name="output"> /// The output. /// </param> /// <remarks> /// Type declaration: /// <c> /// associative-count = U32 /// ecma-array-type = associative-count *(object-property) /// </c> /// </remarks> private void ReadEcmaArray(AmfContext context, AmfStreamReader reader, XmlWriter output = null) { context.References.Track(); reader.ReadUInt32(); // Read properties count this.ReadObject(context, reader, output); }
/// <summary> /// Read a date. /// </summary> /// <param name="context"> /// The context. /// </param> /// <param name="reader"> /// The reader. /// </param> /// <param name="output"> /// The output. /// </param> /// <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 double milliseconds = reader.ReadDouble(); string value = milliseconds.ToString(); if (output != null) { output.WriteStartElement(AmfxContent.Date); output.WriteValue(value); output.WriteEndElement(); } }
/// <summary> /// Read a byte array. /// </summary> /// <param name="context"> /// The context. /// </param> /// <param name="reader"> /// The reader. /// </param> /// <param name="output"> /// The output. /// </param> /// <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 int length = reference >> 1; byte[] data = length == 0 ? new byte[] { } : reader.ReadBytes(length); string value = Convert.ToBase64String(data); if (output != null) { output.WriteStartElement(AmfxContent.ByteArray); output.WriteValue(value); output.WriteEndElement(); } }
/// <summary> /// Read an XML document. /// </summary> /// <param name="context"> /// The context. /// </param> /// <param name="reader"> /// The reader. /// </param> /// <param name="output"> /// The output. /// </param> /// <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 int length = reference >> 1; string value = ReadUtf8(reader, length); if (output != null) { output.WriteStartElement(AmfxContent.Xml); output.WriteValue(value); output.WriteEndElement(); } }
/// <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> /// 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 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> /// The read amf value. /// </summary> /// <param name="context"> /// The context. /// </param> /// <param name="reader"> /// The reader. /// </param> /// <param name="output"> /// The output. /// </param> /// <exception cref="InvalidOperationException"> /// </exception> /// <exception cref="FormatException"> /// </exception> 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); this.ReadAmfValue(newContext, reader, output); return; } this.ReadValue(context, reader, dataType, output); }
/// <summary> /// Read an object reference. /// </summary> /// <param name="context"> /// The context. /// </param> /// <param name="reader"> /// The reader. /// </param> /// <param name="output"> /// The output. /// </param> /// <remarks> /// Type declaration: /// <c>reference-type = reference-marker U16</c> /// </remarks> private static void ReadReference(AmfContext context, AmfStreamReader reader, XmlWriter output = null) { ushort index = reader.ReadUInt16(); if (context.References.Count <= index) { throw new SerializationException(string.Format(Errors.Amf0Decoder_ReadReference_BadIndex, index)); } WriteReference(index, output); }
/// <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); var encoded = input.ReadString(); var decoded = Encoding.UTF8.GetBytes(encoded); WriteUtf8(writer, decoded); }
/// <summary> /// Write a date. /// </summary> /// <param name="context"> /// The context. /// </param> /// <param name="writer"> /// The writer. /// </param> /// <param name="input"> /// The input. /// </param> private static void WriteDate(AmfContext context, AmfStreamWriter writer, XmlReader input) { context.References.Add(new AmfReference { AmfxType = AmfxContent.Date }); WriteTypeMarker(writer, Amf3TypeMarker.Date); double milliseconds = Convert.ToDouble(input.ReadString()); // The first bit is a flag with value 1. // The remaining bits are not used. WriteUInt29(writer, 0 | 0x1); writer.Write(milliseconds); }
/// <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> /// 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); var encoded = input.ReadString(); 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 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> /// The read packet body. /// </summary> /// <param name="stream"> /// The stream. /// </param> /// <returns> /// The <see cref="AmfMessageDescriptor"/>. /// </returns> /// <exception cref="FormatException"> /// </exception> public override sealed AmfMessageDescriptor ReadPacketBody(Stream stream) { var reader = new AmfStreamReader(stream); AmfContext context = this.CreateDefaultContext(); try { var descriptor = new AmfMessageDescriptor { Target = ReadString(reader), Response = ReadString(reader) }; reader.ReadInt32(); // Message length context.ResetReferences(); return(descriptor); } catch (Exception e) { throw new FormatException(Errors.Amf0Deserializer_ReadPacketMessages_InvalidFormat, e); } }
/// <summary> /// Read a strict array. /// </summary> /// <param name="context"> /// The context. /// </param> /// <param name="reader"> /// The reader. /// </param> /// <param name="output"> /// The output. /// </param> /// <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(); uint length = reader.ReadUInt32(); if (output != null) { output.WriteStartElement(AmfxContent.Array); output.WriteAttributeString(AmfxContent.ArrayLength, length.ToString()); } for (int i = 0; i < length; i++) { this.ReadAmfValue(context, reader, output); } if (output != null) { output.WriteEndElement(); } }
/// <summary> /// The read packet header. /// </summary> /// <param name="stream"> /// The stream. /// </param> /// <returns> /// The <see cref="AmfHeaderDescriptor"/>. /// </returns> /// <exception cref="FormatException"> /// </exception> public override sealed AmfHeaderDescriptor ReadPacketHeader(Stream stream) { var reader = new AmfStreamReader(stream); AmfContext context = this.CreateDefaultContext(); try { var descriptor = new AmfHeaderDescriptor { Name = ReadString(reader), MustUnderstand = reader.ReadBoolean() }; reader.ReadInt32(); // Header length context.ResetReferences(); return(descriptor); } catch (Exception e) { throw new FormatException(Errors.Amf0Deserializer_ReadPacketHeaders_InvalidFormat, e); } }
/// <summary> /// Read an object. /// </summary> /// <param name="context"> /// The context. /// </param> /// <param name="reader"> /// The reader. /// </param> /// <param name="output"> /// The output. /// </param> private void ReadObject(AmfContext context, AmfStreamReader reader, XmlWriter output = null) { int 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; if ((traits = ReadTraitsReference(context, reader, out traitsindex, out traitsreference)) == null) { bool isExternalizable = (traitsreference & 0x4) == 4; bool isDynamic = (traitsreference & 0x8) == 8; string typeName = ReadString(context, reader); int count = traitsreference >> 4; var classMembers = new string[count]; for (int 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); } #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(Errors.Amf3Decoder_ReadObject_Debug_Members, traits.ClassMembers.Length); } #endif using (var ms = new MemoryStream()) { var members = new List <string>(); var buffer = new XmlTextWriter(ms, Encoding.UTF8); buffer.WriteStartElement("buffer"); buffer.WriteAttributeString("xmlns", AmfxContent.Namespace); #if DEBUG int memberPosition = 0; #endif // Read object's properties foreach (string classMember in traits.ClassMembers) { #if DEBUG Debug.WriteLine(Errors.Amf3Decoder_ReadObject_Debug_ReadingField, memberPosition, classMember); memberPosition++; #endif this.ReadAmfValue(context, reader, buffer); members.Add(classMember); } // Read dynamic properties too if (traits.IsDynamic) { #if DEBUG Debug.WriteLine(Errors.Amf3Decoder_ReadObject_Debug_ReadingDynamic); #endif string key = ReadString(context, reader); while (key != string.Empty) { #if DEBUG Debug.WriteLine(string.Format(Errors.Amf3Decoder_ReadObject_Debug_ReadingDynamicField, key)); #endif this.ReadAmfValue(context, reader, buffer); members.Add(key); key = ReadString(context, reader); } #if DEBUG Debug.WriteLine(Errors.Amf3Decoder_ReadObject_Debug_DynamicEnd); #endif } buffer.WriteEndElement(); buffer.Flush(); #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 (string 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 = new XmlTextReader(ms); bufferreader.Read(); bufferreader.ReadStartElement(); while (bufferreader.Depth >= 1) { output.WriteNode(bufferreader, false); } } output.WriteEndElement(); // End of object } #endregion } }
/// <summary> /// Read an array. /// </summary> /// <param name="context"> /// The context. /// </param> /// <param name="reader"> /// The reader. /// </param> /// <param name="output"> /// The output. /// </param> /// <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(); int length = reference >> 1; string 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); } this.ReadAmfValue(context, reader, output); key = ReadString(context, reader); if (output != null) { output.WriteEndElement(); } }while (key != string.Empty); // Read array values for (int i = 0; i < length; i++) { this.ReadAmfValue(context, reader, output); } } // Regular array else { // Read array values for (int i = 0; i < length; i++) { this.ReadAmfValue(context, reader, output); } } if (output != null) { output.WriteEndElement(); } }
/// <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(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: this.ReadArray(context, reader, output); break; case Amf3TypeMarker.Object: this.ReadObject(context, reader, output); break; default: throw new NotSupportedException("Type '" + type + "' is not supported."); } if (output != null) { output.Flush(); } }
/// <summary> /// Read a string. /// </summary> /// <param name="context"> /// The context. /// </param> /// <param name="reader"> /// The reader. /// </param> /// <param name="output"> /// The output. /// </param> /// <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> /// <returns> /// The <see cref="string"/>. /// </returns> 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.WriteValue(cache); // output.WriteStartElement(AmfxContent.String); // output.WriteAttributeString(AmfxContent.StringId, index.ToString()); output.WriteEndElement(); } return(cache); } // Get string length int length2 = reference >> 1; string value = ReadUtf8(reader, length2); if (value != string.Empty) { context.StringReferences.Add(value); } if (output != null) { output.WriteStartElement(AmfxContent.String); output.WriteValue(value); output.WriteEndElement(); } return(value); int handle = reader.ReadAMF3IntegerData(); bool inline = (handle & 1) != 0; handle = handle >> 1; if (inline) { int length = handle; if (length == 0) { return(string.Empty); } string str = reader.ReadUTF(length); context.StringReferences.Add(str); if (output != null) { output.WriteStartElement(AmfxContent.String); output.WriteValue(str); output.WriteEndElement(); } return(str); } if (output != null) { output.WriteStartElement(AmfxContent.String); output.WriteAttributeString(AmfxContent.StringId, handle.ToString()); output.WriteEndElement(); } return(context.StringReferences[handle]); }