/// <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();
}
}
}