/// <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> /// Adds a date to the internal object table. /// </summary> /// <param name="value">The value to add.</param> /// <returns>The index of the added value.</returns> private int AddDate(DateTime value) { if (!this.uniques.Contains(value)) { int index = this.objectTable.Count; byte[] buffer = BitConverter.GetBytes(value.ToUniversalTime().Subtract(ReferenceDate).TotalSeconds); if (BitConverter.IsLittleEndian) { Array.Reverse(buffer); } BinaryPlistItem item = new BinaryPlistItem(value); item.Marker.Add((byte)0x33); item.SetByteValue(buffer); this.objectTable.Add(item); this.objectTableSize += item.Size; this.uniques.SetIndex(value, index); return(index); } return(this.uniques.GetIndex(value)); }
/// <summary> /// Adds a float to the internal object table. /// </summary> /// <param name="value">The value to add.</param> /// <returns>The index of the added value.</returns> private int AddFloat(float value) { if (!this.uniques.Contains(value)) { int index = this.objectTable.Count; byte[] buffer = BitConverter.GetBytes(value); if (BitConverter.IsLittleEndian) { Array.Reverse(buffer); } BinaryPlistItem item = new BinaryPlistItem(value); item.Marker.Add((byte)((byte)0x20 | (byte)Math.Log(buffer.Length, 2))); item.SetByteValue(buffer); this.objectTable.Add(item); this.objectTableSize += item.Size; this.uniques.SetIndex(value, index); return(index); } return(this.uniques.GetIndex(value)); }
/// <summary> /// Adds an array to the internal object table. /// </summary> /// <param name="value">The value to add.</param> /// <returns>The index of the added value.</returns> private int AddArray(IEnumerable value) { int index = this.objectTable.Count; BinaryPlistArray array = new BinaryPlistArray(this.objectTable); BinaryPlistItem item = new BinaryPlistItem(array); item.IsArray = true; this.objectTable.Add(item); foreach (object obj in value) { array.ObjectReference.Add(this.AddObject(obj)); this.objectRefCount++; } if (array.ObjectReference.Count < 15) { item.Marker.Add((byte)((byte)0xA0 | (byte)array.ObjectReference.Count)); } else { item.Marker.Add((byte)0xAF); AddIntegerCount(item.Marker, array.ObjectReference.Count); } this.objectTableSize += item.Size; return(index); }
/// <summary> /// Writes an array item to the given <see cref="BinaryWriter"/>. /// </summary> /// <param name="writer">The <see cref="BinaryWriter"/> to write to.</param> /// <param name="value">The array item to write.</param> /// <returns>The number of bytes written.</returns> private int WriteArray(BinaryWriter writer, BinaryPlistItem value) { int size = value.Marker.Count; BinaryPlistArray array = (BinaryPlistArray)value.Value; writer.Write(value.Marker.ToArray()); foreach (int objectRef in array.ObjectReference) { size += WriteReferenceInteger(writer, objectRef, this.objectRefSize); } return(size); }
/// <summary> /// Adds a string to the internal object table. /// </summary> /// <param name="value">The value to add.</param> /// <returns>The index of the added value.</returns> private int AddString(string value) { if (!this.uniques.Contains(value)) { int index = this.objectTable.Count; bool ascii = value.IsAscii(); byte[] buffer; BinaryPlistItem item = new BinaryPlistItem(value); if (value.Length < 15) { item.Marker.Add((byte)((byte)(ascii ? 0x50 : 0x60) | (byte)value.Length)); } else { item.Marker.Add((byte)(ascii ? 0x5F : 0x6F)); AddIntegerCount(item.Marker, value.Length); } if (ascii) { buffer = Encoding.ASCII.GetBytes(value); } else { buffer = Encoding.Unicode.GetBytes(value); if (BitConverter.IsLittleEndian) { for (int i = 0; i < buffer.Length; i++) { byte l = buffer[i]; buffer[i] = buffer[++i]; buffer[i] = l; } } } item.SetByteValue(buffer); this.objectTable.Add(item); this.objectTableSize += item.Size; this.uniques.SetIndex(value, index); return(index); } return(this.uniques.GetIndex(value)); }
/// <summary> /// Adds an integer to the internal object table. /// </summary> /// <param name="value">The value to add.</param> /// <returns>The index of the added value.</returns> private int AddInteger(long value) { if (!this.uniques.Contains(value)) { int index = this.objectTable.Count; BinaryPlistItem item = new BinaryPlistItem(value); item.SetByteValue(GetIntegerBytes(value)); item.Marker.Add((byte)((byte)0x10 | (byte)Math.Log(item.ByteValue.Count, 2))); this.objectTable.Add(item); this.objectTableSize += item.Size; this.uniques.SetIndex(value, index); return(index); } return(this.uniques.GetIndex(value)); }
/// <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> /// Adds arbitrary data to the internal object table. /// </summary> /// <param name="value">The value to add.</param> /// <returns>The index of the added value.</returns> private int AddData(object value) { int index = this.objectTable.Count, count = 0, bufferIndex = 0; byte[] buffer = value as byte[]; if (buffer == null) { using (MemoryStream stream = new MemoryStream()) { BinaryFormatter formatter = new BinaryFormatter(); formatter.Serialize(stream, value); stream.Position = 0; buffer = new byte[stream.Length]; while (0 < (count = stream.Read(buffer, 0, buffer.Length - bufferIndex))) { bufferIndex += count; } } } BinaryPlistItem item = new BinaryPlistItem(value); item.SetByteValue(buffer); if (buffer.Length < 15) { item.Marker.Add((byte)((byte)0x40 | (byte)buffer.Length)); } else { item.Marker.Add(0x4F); AddIntegerCount(item.Marker, buffer.Length); } this.objectTable.Add(item); this.objectTableSize += item.Size; return(index); }
/// <summary> /// Adds a primitive to the internal object table. /// </summary> /// <param name="value">The value to add.</param> /// <returns>The index of the added value.</returns> private int AddPrimitive(bool?value) { if (!value.HasValue || !this.uniques.Contains(value.Value)) { int index = this.objectTable.Count; BinaryPlistItem item = new BinaryPlistItem(value); item.Marker.Add(value.HasValue ? (value.Value ? (byte)0x9 : (byte)0x8) : (byte)0); this.objectTable.Add(item); this.objectTableSize += item.Size; if (value.HasValue) { this.uniques.SetIndex(value.Value, index); } return(index); } return(this.uniques.GetIndex(value.Value)); }
/// <summary> /// Reads the object table from the given reader. /// </summary> /// <param name="reader">The reader to read the object table from.</param> private void ReadObjectTable(BinaryReader reader) { byte marker; bool? primitive; int size, intSize; long parsedInt; BinaryPlistItem item; for (int i = 0; i < this.objectCount; i++) { reader.BaseStream.Position = this.offsetTable[i]; marker = reader.ReadByte(); var hexMarker = marker.ToString("X2"); var firstHalf = marker >> 4; var bottomHalf = (marker & 0xf0) >> 4; var type = marker & 0xF0; var @caseHex = type.ToString("X2"); var hexFirstHalf = firstHalf.ToString("X2"); var hexBottomHalf = bottomHalf.ToString("X2"); // The first half of the byte is the base marker. switch (type) { case 0x00: if (ReadPrimitive(reader, reader.BaseStream.Position - 1, out primitive)) { this.objectTable.Add(new BinaryPlistItem(primitive)); } break; case 0x10: size = 1 << (marker & 0xf); parsedInt = ReadInteger(reader, reader.BaseStream.Position, size); if (size < 4) { this.objectTable.Add(new BinaryPlistItem((short)parsedInt)); } else if (size < 8) { this.objectTable.Add(new BinaryPlistItem((int)parsedInt)); } else { this.objectTable.Add(new BinaryPlistItem(parsedInt)); } break; case 0x20: size = 1 << (marker & 0xf); this.objectTable.Add(new BinaryPlistItem(ReadReal(reader, reader.BaseStream.Position, size))); break; case 0x30: size = marker & 0xf; this.objectTable.Add(new BinaryPlistItem(ReadDate(reader, reader.BaseStream.Position, 8))); break; case 0x40: size = marker & 0xf; if (size == 15) { intSize = 1 << (reader.ReadByte() & 0xf); size = (int)ReadInteger(reader, reader.BaseStream.Position, intSize); } this.objectTable.Add(new BinaryPlistItem(ReadData(reader, reader.BaseStream.Position, size))); break; case 0x50: size = marker & 0xf; if (size == 15) { intSize = 1 << (reader.ReadByte() & 0xf); size = (int)ReadInteger(reader, reader.BaseStream.Position, intSize); } this.objectTable.Add(new BinaryPlistItem(ReadAsciiString(reader, reader.BaseStream.Position, size))); break; case 0x60: size = marker & 0xf; if (size == 15) { intSize = 1 << (reader.ReadByte() & 0xf); size = (int)ReadInteger(reader, reader.BaseStream.Position, intSize); } this.objectTable.Add(new BinaryPlistItem(ReadUnicodeString(reader, reader.BaseStream.Position, size))); break; case 0x80: size = (marker & 0xf) + 1; this.objectTable.Add(new BinaryPlistItem(ReadUniqueId(reader, reader.BaseStream.Position, size))); break; case 0xA0: case 0xC0: size = marker & 0xf; if (size == 15) { intSize = 1 << (reader.ReadByte() & 0xf); size = (int)ReadInteger(reader, reader.BaseStream.Position, intSize); } item = new BinaryPlistItem(this.ReadArray(reader, reader.BaseStream.Position, size)); item.IsArray = true; this.objectTable.Add(item); break; case 0xD0: size = marker & 0xf; if (size == 15) { intSize = 1 << (reader.ReadByte() & 0xf); size = (int)ReadInteger(reader, reader.BaseStream.Position, intSize); } item = new BinaryPlistItem(this.ReadDictionary(reader, reader.BaseStream.Position, size)); item.IsDictionary = true; this.objectTable.Add(item); break; default: throw new InvalidOperationException("An invalid marker was found while reading the object table: " + marker.ToBinaryString()); } } }
/// <summary> /// Adds a date to the internal object table. /// </summary> /// <param name="value">The value to add.</param> /// <returns>The index of the added value.</returns> private int AddDate(DateTime value) { if (!this.uniques.Contains(value)) { int index = this.objectTable.Count; byte[] buffer = BitConverter.GetBytes(value.ToUniversalTime().Subtract(ReferenceDate).TotalSeconds); if (BitConverter.IsLittleEndian) { Array.Reverse(buffer); } BinaryPlistItem item = new BinaryPlistItem(value); item.Marker.Add((byte)0x33); item.SetByteValue(buffer); this.objectTable.Add(item); this.objectTableSize += item.Size; this.uniques.SetIndex(value, index); return index; } return this.uniques.GetIndex(value); }
/// <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> /// Adds a float to the internal object table. /// </summary> /// <param name="value">The value to add.</param> /// <returns>The index of the added value.</returns> private int AddFloat(float value) { if (!this.uniques.Contains(value)) { int index = this.objectTable.Count; byte[] buffer = BitConverter.GetBytes(value); if (BitConverter.IsLittleEndian) { Array.Reverse(buffer); } BinaryPlistItem item = new BinaryPlistItem(value); item.Marker.Add((byte)((byte)0x20 | (byte)Math.Log(buffer.Length, 2))); item.SetByteValue(buffer); this.objectTable.Add(item); this.objectTableSize += item.Size; this.uniques.SetIndex(value, index); return index; } return this.uniques.GetIndex(value); }
/// <summary> /// Adds arbitrary data to the internal object table. /// </summary> /// <param name="value">The value to add.</param> /// <returns>The index of the added value.</returns> private int AddData(object value) { int index = this.objectTable.Count, count = 0, bufferIndex = 0; byte[] buffer = value as byte[]; if (buffer == null) { using (MemoryStream stream = new MemoryStream()) { BinaryFormatter formatter = new BinaryFormatter(); formatter.Serialize(stream, value); stream.Position = 0; buffer = new byte[stream.Length]; while (0 < (count = stream.Read(buffer, 0, buffer.Length - bufferIndex))) { bufferIndex += count; } } } BinaryPlistItem item = new BinaryPlistItem(value); item.SetByteValue(buffer); if (buffer.Length < 15) { item.Marker.Add((byte)((byte)0x40 | (byte)buffer.Length)); } else { item.Marker.Add(0x4F); AddIntegerCount(item.Marker, buffer.Length); } this.objectTable.Add(item); this.objectTableSize += item.Size; return index; }
/// <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> /// Writes an array item to the given <see cref="BinaryWriter"/>. /// </summary> /// <param name="writer">The <see cref="BinaryWriter"/> to write to.</param> /// <param name="value">The array item to write.</param> /// <returns>The number of bytes written.</returns> private int WriteArray(BinaryWriter writer, BinaryPlistItem value) { int size = value.Marker.Count; BinaryPlistArray array = (BinaryPlistArray)value.Value; writer.Write(value.Marker.ToArray()); foreach (int objectRef in array.ObjectReference) { size += WriteReferenceInteger(writer, objectRef, this.objectRefSize); } return size; }
/// <summary> /// Adds a string to the internal object table. /// </summary> /// <param name="value">The value to add.</param> /// <returns>The index of the added value.</returns> private int AddString(string value) { if (!this.uniques.Contains(value)) { int index = this.objectTable.Count; bool ascii = value.IsAscii(); byte[] buffer; BinaryPlistItem item = new BinaryPlistItem(value); if (value.Length < 15) { item.Marker.Add((byte)((byte)(ascii ? 0x50 : 0x60) | (byte)value.Length)); } else { item.Marker.Add((byte)(ascii ? 0x5F : 0x6F)); AddIntegerCount(item.Marker, value.Length); } if (ascii) { buffer = Encoding.ASCII.GetBytes(value); } else { buffer = Encoding.Unicode.GetBytes(value); if (BitConverter.IsLittleEndian) { for (int i = 0; i < buffer.Length; i++) { byte l = buffer[i]; buffer[i] = buffer[++i]; buffer[i] = l; } } } item.SetByteValue(buffer); this.objectTable.Add(item); this.objectTableSize += item.Size; this.uniques.SetIndex(value, index); return index; } return this.uniques.GetIndex(value); }
/// <summary> /// Adds a primitive to the internal object table. /// </summary> /// <param name="value">The value to add.</param> /// <returns>The index of the added value.</returns> private int AddPrimitive(bool? value) { if (!value.HasValue || !this.uniques.Contains(value.Value)) { int index = this.objectTable.Count; BinaryPlistItem item = new BinaryPlistItem(value); item.Marker.Add(value.HasValue ? (value.Value ? (byte)0x9 : (byte)0x8) : (byte)0); this.objectTable.Add(item); this.objectTableSize += item.Size; if (value.HasValue) { this.uniques.SetIndex(value.Value, index); } return index; } return this.uniques.GetIndex(value.Value); }
/// <summary> /// Adds an integer to the internal object table. /// </summary> /// <param name="value">The value to add.</param> /// <returns>The index of the added value.</returns> private int AddInteger(long value) { if (!this.uniques.Contains(value)) { int index = this.objectTable.Count; BinaryPlistItem item = new BinaryPlistItem(value); item.SetByteValue(GetIntegerBytes(value)); item.Marker.Add((byte)((byte)0x10 | (byte)Math.Log(item.ByteValue.Count, 2))); this.objectTable.Add(item); this.objectTableSize += item.Size; this.uniques.SetIndex(value, index); return index; } return this.uniques.GetIndex(value); }
/// <summary> /// Reads the object table from the given reader. /// </summary> /// <param name="reader">The reader to read the object table from.</param> private void ReadObjectTable(BinaryReader reader) { byte marker; bool? primitive; int size, intSize; long parsedInt; BinaryPlistItem item; for (int i = 0; i < this.objectCount; i++) { reader.BaseStream.Position = this.offsetTable[i]; marker = reader.ReadByte(); // The first half of the byte is the base marker. switch ((marker & 0xf0) >> 4) { case 0: if (ReadPrimitive(reader, reader.BaseStream.Position - 1, out primitive)) { this.objectTable.Add(new BinaryPlistItem(primitive)); } break; case 1: size = 1 << (marker & 0xf); parsedInt = ReadInteger(reader, reader.BaseStream.Position, size); if (size < 4) { this.objectTable.Add(new BinaryPlistItem((short)parsedInt)); } else if (size < 8) { this.objectTable.Add(new BinaryPlistItem((int)parsedInt)); } else { this.objectTable.Add(new BinaryPlistItem(parsedInt)); } break; case 2: size = 1 << (marker & 0xf); this.objectTable.Add(new BinaryPlistItem(ReadReal(reader, reader.BaseStream.Position, size))); break; case 3: size = marker & 0xf; if (size == 3) { this.objectTable.Add(new BinaryPlistItem(ReadDate(reader, reader.BaseStream.Position, 8))); } else { throw new InvalidOperationException("Unsupported date size: " + size.ToBinaryString()); } break; case 4: size = marker & 0xf; if (size == 15) { intSize = 1 << (reader.ReadByte() & 0xf); size = (int)ReadInteger(reader, reader.BaseStream.Position, intSize); } this.objectTable.Add(new BinaryPlistItem(ReadData(reader, reader.BaseStream.Position, size))); break; case 5: size = marker & 0xf; if (size == 15) { intSize = 1 << (reader.ReadByte() & 0xf); size = (int)ReadInteger(reader, reader.BaseStream.Position, intSize); } this.objectTable.Add(new BinaryPlistItem(ReadAsciiString(reader, reader.BaseStream.Position, size))); break; case 6: size = marker & 0xf; if (size == 15) { intSize = 1 << (reader.ReadByte() & 0xf); size = (int)ReadInteger(reader, reader.BaseStream.Position, intSize); } this.objectTable.Add(new BinaryPlistItem(ReadUnicodeString(reader, reader.BaseStream.Position, size))); break; case 8: size = (marker & 0xf) + 1; this.objectTable.Add(new BinaryPlistItem(ReadUniqueId(reader, reader.BaseStream.Position, size))); break; case 10: case 12: size = marker & 0xf; if (size == 15) { intSize = 1 << (reader.ReadByte() & 0xf); size = (int)ReadInteger(reader, reader.BaseStream.Position, intSize); } item = new BinaryPlistItem(this.ReadArray(reader, reader.BaseStream.Position, size)); item.IsArray = true; this.objectTable.Add(item); break; case 13: size = marker & 0xf; if (size == 15) { intSize = 1 << (reader.ReadByte() & 0xf); size = (int)ReadInteger(reader, reader.BaseStream.Position, intSize); } item = new BinaryPlistItem(this.ReadDictionary(reader, reader.BaseStream.Position, size)); item.IsDictionary = true; this.objectTable.Add(item); break; default: throw new InvalidOperationException("An invalid marker was found while reading the object table: " + marker.ToBinaryString()); } } }
/// <summary> /// Adds an array to the internal object table. /// </summary> /// <param name="value">The value to add.</param> /// <returns>The index of the added value.</returns> private int AddArray(IEnumerable value) { int index = this.objectTable.Count; BinaryPlistArray array = new BinaryPlistArray(this.objectTable); BinaryPlistItem item = new BinaryPlistItem(array); item.IsArray = true; this.objectTable.Add(item); foreach (object obj in value) { array.ObjectReference.Add(this.AddObject(obj)); this.objectRefCount++; } if (array.ObjectReference.Count < 15) { item.Marker.Add((byte)((byte)0xA0 | (byte)array.ObjectReference.Count)); } else { item.Marker.Add((byte)0xAF); AddIntegerCount(item.Marker, array.ObjectReference.Count); } this.objectTableSize += item.Size; return index; }