/// <summary> /// Read a double. /// </summary> /// <param name="reader"> /// The reader. /// </param> /// <param name="output"> /// The output. /// </param> private static void ReadDouble(AmfStreamReader reader, XmlWriter output = null) { double value = reader.ReadDouble(); if (output != null) { if (value <= MinInt29Value || value >= MaxInt29Value && Math.Abs(value - Math.Round(value)) < MinDoublePrecision) { if (value > Int32.MaxValue || value < Int32.MinValue) { output.WriteStartElement(AmfxContent.Double); output.WriteValue(value); } else { int integer = Convert.ToInt32(value); output.WriteStartElement(AmfxContent.Integer); output.WriteValue(integer); } } else { output.WriteStartElement(AmfxContent.Double); output.WriteValue(value); } output.WriteEndElement(); } }
/// <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> /// 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 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 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 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> /// 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> /// 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> /// 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> /// 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> /// 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); }
override public void Decode(Stream stream, XmlWriter output) { var reader = new AmfStreamReader(stream); var context = CreateDefaultContext(); ReadAmfValue(context, reader, output); }
/// <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 double. /// </summary> private static void ReadDouble(AmfStreamReader reader, XmlWriter output = null) { var value = reader.ReadDouble(); if (output != null) { output.WriteStartElement(AmfxContent.Double); output.WriteValue(value); output.WriteEndElement(); } }
/// <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> /// Read an integer. /// </summary> private static void ReadInteger(AmfStreamReader reader, XmlWriter output = null) { var value = ReadUint29(reader); if (output != null) { output.WriteStartElement(AmfxContent.Integer); output.WriteValue(value); output.WriteEndElement(); } }
/// <summary> /// Read AMF packet version. /// </summary> /// <exception cref="FormatException">Data has unknown format.</exception> private static AmfVersion ReadPacketVersion(AmfStreamReader reader) { try { //First two bytes contain message version number return((AmfVersion)reader.ReadUInt16()); } catch (Exception e) { throw new FormatException(Errors.AmfPacketReader_ReadPacketVersion_VersionReadError, e); } }
/// <summary> /// Read an XML document. /// </summary> /// <param name="reader"> /// The reader. /// </param> /// <param name="output"> /// The output. /// </param> /// <remarks> /// Type declaration: /// <c>xml-document-type = xml-document-marker UTF-8-long</c> /// </remarks> private static void ReadXml(AmfStreamReader reader, XmlWriter output = null) { string data = ReadLongString(reader); // XML is stored as a long string if (output != null) { byte[] rawData = Encoding.UTF8.GetBytes(data); string value = Convert.ToBase64String(rawData); output.WriteStartElement(AmfxContent.Xml); output.WriteValue(value); output.WriteEndElement(); } }
/// <summary> /// Read a specified number of bytes of a string. /// </summary> /// <param name="reader"> /// AMF reader. /// </param> /// <param name="length"> /// Number of bytes to read. /// </param> /// <returns> /// The <see cref="string"/>. /// </returns> private static string ReadUtf8(AmfStreamReader reader, uint length) { // Make sure that a null is never returned if (length == 0) { return(string.Empty); } byte[] data = reader.ReadBytes((int)length); // All strings are encoded in UTF-8) return(Encoding.UTF8.GetString(data)); }
/// <summary> /// Read a string. /// </summary> /// <param name="reader"> /// The reader. /// </param> /// <param name="output"> /// The output. /// </param> /// <remarks> /// Type declaration: /// <c>string-type = string-marker UTF-8</c> /// </remarks> /// <returns> /// The <see cref="string"/>. /// </returns> private static string ReadString(AmfStreamReader reader, XmlWriter output = null) { // First 16 bits represents string's (UTF-8) length in bytes ushort length = reader.ReadUInt16(); string value = ReadUtf8(reader, length); if (output != null) { output.WriteStartElement(AmfxContent.String); output.WriteValue(value); output.WriteEndElement(); } return(value); }
/// <summary> /// Read an integer. /// </summary> /// <param name="reader"> /// The reader. /// </param> /// <param name="output"> /// The output. /// </param> private static void ReadInteger(AmfStreamReader reader, XmlWriter output = null) { int value = ReadUint29(reader); const int mask = 1 << 28; // Integer sign mask value = -(value & mask) | value; if (output != null) { output.WriteStartElement(AmfxContent.Integer); output.WriteValue(value); output.WriteEndElement(); } }
/// <summary> /// Read a long string. /// </summary> /// <remarks> /// Type declaration: /// <c>long-string-type = long-string-marker UTF-8-long</c> /// </remarks> private static string ReadLongString(AmfStreamReader reader, XmlWriter output = null) { //First 32 bits represents long string's (UTF-8-long) length in bytes var length = reader.ReadUInt32(); var value = ReadUtf8(reader, length); if (output != null) { output.WriteStartElement(AmfxContent.String); output.WriteValue(value); output.WriteEndElement(); } return(value); }
/// <summary> /// Read a date. /// </summary> /// <param name="reader"> /// The reader. /// </param> /// <param name="output"> /// The output. /// </param> /// <remarks> /// Type declaration: /// <c> /// time-zone = S16 /// date-type = date-marker DOUBLE time-zone /// </c> /// </remarks> private static void ReadDate(AmfStreamReader reader, XmlWriter output = null) { // Dates are represented as an Unix time stamp, but in milliseconds double milliseconds = reader.ReadDouble(); // Value indicates a timezone, but it should not be used reader.ReadInt16(); if (output != null) { output.WriteStartElement(AmfxContent.Date); string result = milliseconds.ToString(); output.WriteValue(result); output.WriteEndElement(); } }
/// <summary> /// Read a specified number of bytes of a string. /// </summary> /// <param name="reader"> /// AMF reader. /// </param> /// <param name="length"> /// Number of bytes to read. /// </param> /// <returns> /// The <see cref="string"/>. /// </returns> private static string ReadUtf8(AmfStreamReader reader, int length) { if (length < 0) { throw new ArgumentException(Errors.Amf3Deserializer_ReadString_NegativeLength, "length"); } // Make sure that a null is never returned if (length == 0) { return(string.Empty); } byte[] data = reader.ReadBytes(length); // All strings are encoded in UTF-8 return(Encoding.UTF8.GetString(data)); }
/// <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 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> /// <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 a 29-bit unsigned integer. /// </summary> /// <param name="reader"> /// The reader. /// </param> /// <remarks> /// Up to 4 bytes are required to hold the value however the high bit /// of the first 3 bytes are used as flags to determine /// whether the next byte is part of the integer. /// <c> /// 0x00000000 - 0x0000007F : 0xxxxxxx /// 0x00000080 - 0x00003FFF : 1xxxxxxx 0xxxxxxx /// 0x00004000 - 0x001FFFFF : 1xxxxxxx 1xxxxxxx 0xxxxxxx /// 0x00200000 - 0x3FFFFFFF : 1xxxxxxx 1xxxxxxx 1xxxxxxx xxxxxxxx /// 0x40000000 - 0xFFFFFFFF : throw range exception /// </c> /// </remarks> /// <returns> /// The <see cref="int"/>. /// </returns> private static int ReadUint29(AmfStreamReader reader) { const byte mask = 0x7F; // 0111 1111 int octet = reader.ReadByte() & 0xFF; // 0xxxxxxx if (octet < 128) { return(octet); } int result = (octet & mask) << 7; octet = reader.ReadByte() & 0xFF; // 1xxxxxxx 0xxxxxxx if (octet < 128) { return(result | octet); } result = (result | (octet & mask)) << 7; octet = reader.ReadByte() & 0xFF; // 1xxxxxxx 1xxxxxxx 0xxxxxxx if (octet < 128) { return(result | octet); } result = (result | (octet & mask)) << 8; octet = reader.ReadByte() & 0xFF; // 1xxxxxxx 1xxxxxxx 1xxxxxxx xxxxxxxx return(result | octet); }
/// <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 } }