/// <summary> /// Decode an AMF packet into an AMFX format. /// </summary> /// <exception cref="FormatException">Error during decoding.</exception> public void Decode(Stream stream, XmlWriter output) { if (stream == null) throw new ArgumentNullException("stream"); if (!stream.CanRead) throw new ArgumentException(Errors.AmfPacketReader_Read_StreamClosed, "stream"); if (output == null) throw new ArgumentNullException("output"); try { var amfStreamReader = new AmfStreamReader(stream); var version = ReadPacketVersion(amfStreamReader); var decoder = CreateDecoder(version, _options); output.WriteStartDocument(); output.WriteStartElement(AmfxContent.AmfxDocument, AmfxContent.Namespace); output.WriteAttributeString(AmfxContent.VersionAttribute, version.ToAmfxName()); output.Flush(); //Read headers var headerCount = ReadDataCount(amfStreamReader); for (var i = 0; i < headerCount; i++) { var header = decoder.ReadPacketHeader(stream); output.WriteStartElement(AmfxContent.PacketHeader); output.WriteAttributeString(AmfxContent.PacketHeaderName, header.Name); output.WriteAttributeString(AmfxContent.PacketHeaderMustUnderstand, header.MustUnderstand.ToString()); decoder.Decode(stream, output); output.WriteEndElement(); output.Flush(); } //Read messages var messageCount = ReadDataCount(amfStreamReader); for (var i = 0; i < messageCount; i++) { var body = decoder.ReadPacketBody(stream); output.WriteStartElement(AmfxContent.PacketBody); output.WriteAttributeString(AmfxContent.PacketBodyTarget, body.Target); output.WriteAttributeString(AmfxContent.PacketBodyResponse, body.Response); decoder.Decode(stream, output); output.WriteEndElement(); output.Flush(); } output.WriteEndElement(); output.WriteEndDocument(); output.Flush(); } catch (Exception e) { output.Flush(); throw new FormatException(Errors.AmfPacketReader_DecodingError, 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; }
public override sealed AmfHeaderDescriptor ReadPacketHeader(Stream stream) { var reader = new AmfStreamReader(stream); var context = 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); } }
public override sealed AmfMessageDescriptor ReadPacketBody(Stream stream) { var reader = new AmfStreamReader(stream); var context = 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 XML document. /// </summary> /// <remarks> /// Type declaration: /// <c>xml-document-type = xml-document-marker UTF-8-long</c> /// </remarks> private static void ReadXml(AmfStreamReader reader, XmlWriter output = null) { var data = ReadLongString(reader); //XML is stored as a long string if (output != null) { var rawData = Encoding.UTF8.GetBytes(data); var value = Convert.ToBase64String(rawData); 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(); }
override public void Decode(Stream stream, XmlWriter output) { var reader = new AmfStreamReader(stream); var context = CreateDefaultContext(); ReadAmfValue(context, reader, output); }
/// <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> /// Read a double. /// </summary> private static void ReadDouble(AmfStreamReader reader, XmlWriter output = null) { var value = reader.ReadDouble(); if (output != null) { if (value <= MinInt29Value || value >= MaxInt29Value && Math.Abs(value - Math.Round(value)) < MinDoublePrecision) { var integer = Convert.ToInt32(value); output.WriteStartElement(AmfxContent.Integer); output.WriteValue(integer); } else { output.WriteStartElement(AmfxContent.Double); output.WriteValue(value); } 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(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> /// 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 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> /// 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); } }
/// <summary> /// Read an object. /// </summary> /// <remarks> /// Type declaration: /// <c>object-property = (UTF-8 value-type) | (UTF-8-empty object-end-marker) /// anonymous-object-type = object-marker *(object-property)</c> /// </remarks> private void ReadObject(AmfContext context, AmfStreamReader reader, XmlWriter output = null, bool isTyped = false) { context.References.Track(); if (output != null) { output.WriteStartElement(AmfxContent.Object); //Read properties using (var ms = new MemoryStream()) { var buffer = XmlWriter.Create(ms); buffer.WriteStartElement("buffer"); buffer.WriteAttributeString("xmlns", AmfxContent.Namespace); var members = ReadPropertiesMap(context, reader); buffer.WriteEndElement(); buffer.Flush(); if (isTyped) { var typeName = ReadString(reader); output.WriteAttributeString(AmfxContent.ObjectType, typeName); } output.WriteStartElement(AmfxContent.Traits); //Write traits foreach (var classMember in members) { output.WriteStartElement(AmfxContent.String); output.WriteValue(classMember); output.WriteEndElement(); } //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); } buffer.WriteEndElement(); //End of traits } output.WriteEndElement(); //End of object } else { //Just read the object's properties ReadPropertiesMap(context ,reader); if(isTyped) ReadString(reader); } }
/// <summary> /// Read an ECMA array. /// </summary> /// <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 ReadObject(context, reader, output); }
/// <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> 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; var data = reader.ReadBytes(length); //All strings are encoded in UTF-8 return Encoding.UTF8.GetString(data, 0, data.Length); }
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 }
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 an integer. /// </summary> private static void ReadInteger(AmfStreamReader reader, XmlWriter output = null) { var 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 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);
/// <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 number of following headers/messages. /// </summary> private static uint ReadDataCount(AmfStreamReader reader) { return reader.ReadUInt16(); }
/// <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(); } }
/// <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> /// <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 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 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 } }
/// <summary> /// Read a 29-bit unsigned integer. /// </summary> /// <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> private static int ReadUint29(AmfStreamReader reader) { const byte mask = 0x7F; //0111 1111 var octet = reader.ReadByte() & 0xFF; //0xxxxxxx if (octet < 128) return octet; var 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 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 a specified number of bytes of a string. /// </summary> /// <param name="reader">AMF reader.</param> /// <param name="length">Number of bytes to read.</param> private static string ReadUtf8(AmfStreamReader reader, uint length) { //Make sure that a null is never returned if (length == 0) return string.Empty; var data = reader.ReadBytes((int)length); //All strings are encoded in UTF-8) return Encoding.UTF8.GetString(data, 0, data.Length); }