/// <summary> /// Adds a dictionary to the internal object table. /// </summary> /// <param name="value">The value to add.</param> /// <returns>The index of the added value.</returns> private int AddDictionary(IDictionary value) { int index = this.objectTable.Count; BinaryPlistDictionary dict = new BinaryPlistDictionary(this.objectTable, value.Count); BinaryPlistItem item = new BinaryPlistItem(dict); item.IsDictionary = true; this.objectTable.Add(item); foreach (object key in value.Keys) { dict.KeyReference.Add(this.AddObject(key)); dict.ObjectReference.Add(this.AddObject(value[key])); this.objectRefCount += 2; } if (dict.KeyReference.Count < 15) { item.Marker.Add((byte)((byte)0xD0 | (byte)dict.KeyReference.Count)); } else { item.Marker.Add((byte)0xDF); AddIntegerCount(item.Marker, dict.KeyReference.Count); } this.objectTableSize += item.Size; return(index); }
/// <summary> /// Reads a dictionary value from the given reader, starting at the given index and of the given size. /// </summary> /// <param name="reader">The <see cref="BinaryReader"/> to read the dictionary value from.</param> /// <param name="index">The index in the stream the dictionary value starts at.</param> /// <param name="size">The number of items in the dictionary.</param> /// <returns>A dictionary value.</returns> private BinaryPlistDictionary ReadDictionary(BinaryReader reader, long index, int size) { BinaryPlistDictionary dictionary = new BinaryPlistDictionary(this.objectTable, size); int skip = size * this.objectRefSize; for (int i = 0; i < size; i++) { dictionary.KeyReference.Add((int)ReadInteger(reader, index + (i * this.objectRefSize), this.objectRefSize)); dictionary.ObjectReference.Add((int)ReadInteger(reader, skip + index + (i * this.objectRefSize), this.objectRefSize)); } return(dictionary); }
/// <summary> /// Writes a dictionary item to the given <see cref="BinaryWriter"/>. /// </summary> /// <param name="writer">The <see cref="BinaryWriter"/> to write to.</param> /// <param name="value">The dictionary item to write.</param> /// <returns>The number of bytes written.</returns> private int WriteDictionary(BinaryWriter writer, BinaryPlistItem value) { int size = value.Marker.Count; BinaryPlistDictionary dict = (BinaryPlistDictionary)value.Value; writer.Write(value.Marker.ToArray()); foreach (int keyRef in dict.KeyReference) { size += WriteReferenceInteger(writer, keyRef, this.objectRefSize); } foreach (int objectRef in dict.ObjectReference) { size += WriteReferenceInteger(writer, objectRef, this.objectRefSize); } return(size); }
/// <summary> /// Reads a binary plist from the given stream into an <see cref="IDictionary"/>. /// </summary> /// <param name="stream">The <see cref="Stream"/> to read.</param> /// <returns>The result plist <see cref="IDictionary"/>.</returns> public IDictionary ReadObject(Stream stream) { if (stream == null) { throw new ArgumentNullException("stream", "stream cannot be null."); } if (!stream.CanRead) { throw new ArgumentException("The stream must be readable.", "stream"); } Stream concreteStream = stream; bool disposeConcreteStream = false; if (!stream.CanSeek) { concreteStream = new MemoryStream(); byte[] buffer = new byte[4096]; int count = 0; while (0 < (count = stream.Read(buffer, 0, buffer.Length))) { concreteStream.Write(buffer, 0, count); } concreteStream.Position = 0; disposeConcreteStream = true; } try { Dictionary <object, object> dictionary = null; this.Reset(); // Header + trailer = 40. if (stream.Length > 40) { using (BinaryReader reader = new BinaryReader(concreteStream)) { // Read the header. stream.Position = 0; int bpli = reader.ReadInt32().ToBigEndianConditional(); int version = reader.ReadInt32().ToBigEndianConditional(); if (bpli != BinaryPlistWriter.HeaderMagicNumber || version != BinaryPlistWriter.HeaderVersionNumber) { throw new ArgumentException("The stream data does not start with required 'bplist00' header.", "stream"); } // Read the trailer. // The first six bytes of the first eight-byte block are unused, so offset by 26 instead of 32. stream.Position = stream.Length - 26; this.offsetIntSize = (int)reader.ReadByte(); this.objectRefSize = (int)reader.ReadByte(); this.objectCount = (int)reader.ReadInt64().ToBigEndianConditional(); this.topLevelObjectOffset = (int)reader.ReadInt64().ToBigEndianConditional(); this.offsetTableOffset = (int)reader.ReadInt64().ToBigEndianConditional(); int offsetTableSize = this.offsetIntSize * this.objectCount; // Ensure our sanity. if (this.offsetIntSize < 1 || this.offsetIntSize > 8 || this.objectRefSize < 1 || this.objectRefSize > 8 || this.offsetTableOffset < 8 || this.topLevelObjectOffset >= this.objectCount || offsetTableSize + this.offsetTableOffset + 32 > stream.Length) { throw new ArgumentException("The stream data contains an invalid trailer.", "stream"); } // Read the offset table and then the object table. this.ReadOffsetTable(reader); this.ReadObjectTable(reader); } } else { throw new ArgumentException("The stream is too short to be a valid binary plist.", "stream"); } BinaryPlistDictionary root = this.objectTable[this.topLevelObjectOffset].Value as BinaryPlistDictionary; if (root != null) { dictionary = root.ToDictionary(); } else { throw new InvalidOperationException("Unsupported root plist object: " + this.objectTable[this.topLevelObjectOffset].GetType() + ". A dictionary must be the root plist object."); } return(dictionary ?? new Dictionary <object, object>()); } finally { if (disposeConcreteStream && concreteStream != null) { concreteStream.Dispose(); } } }
/// <summary> /// Adds a dictionary to the internal object table. /// </summary> /// <param name="value">The value to add.</param> /// <returns>The index of the added value.</returns> private int AddDictionary(IDictionary value) { int index = this.objectTable.Count; BinaryPlistDictionary dict = new BinaryPlistDictionary(this.objectTable, value.Count); BinaryPlistItem item = new BinaryPlistItem(dict); item.IsDictionary = true; this.objectTable.Add(item); foreach (object key in value.Keys) { dict.KeyReference.Add(this.AddObject(key)); dict.ObjectReference.Add(this.AddObject(value[key])); this.objectRefCount += 2; } if (dict.KeyReference.Count < 15) { item.Marker.Add((byte)((byte)0xD0 | (byte)dict.KeyReference.Count)); } else { item.Marker.Add((byte)0xDF); AddIntegerCount(item.Marker, dict.KeyReference.Count); } this.objectTableSize += item.Size; return index; }