/// <summary> /// Populates the current instance by reading in a raw APEv2 /// item. /// </summary> /// <param name="data"> /// A <see cref="ByteVector" /> object containing the item to /// read. /// </param> /// <param name="offset"> /// A <see cref="int" /> value specifying the offset in /// <paramref name="data" /> at which the item data begins. /// </param> /// <exception cref="ArgumentNullException"> /// <paramref name="data" /> is <see langword="null" />. /// </exception> /// <exception cref="ArgumentOutOfRangeException"> /// <paramref name="offset" /> is less than zero. /// </exception> /// <exception cref="CorruptFileException"> /// A complete item could not be read. /// </exception> protected void Parse(ByteVector data, int offset) { if (data == null) { throw new ArgumentNullException(nameof(data)); } if (offset < 0) { throw new ArgumentOutOfRangeException(nameof(offset)); } // 11 bytes is the minimum size for an APE item if (data.Count < offset + 11) { throw new CorruptFileException( "Not enough data for APE Item"); } var value_length = data.Mid(offset, 4) .ToUInt(false); var flags = data.Mid(offset + 4, 4) .ToUInt(false); ReadOnly = (flags & 1) == 1; Type = (ItemType)((flags >> 1) & 3); var pos = data.Find(ByteVector.TextDelimiter( StringType.UTF8), offset + 8); Key = data.ToString(StringType.UTF8, offset + 8, pos - offset - 8); if (value_length > data.Count - pos - 1) { throw new CorruptFileException( "Invalid data length."); } Size = pos + 1 + (int)value_length - offset; if (Type == ItemType.Binary) { this.data = new ReadOnlyByteVector( data.Mid(pos + 1, (int)value_length)); } else { text = data.Mid(pos + 1, (int)value_length) .ToStrings( StringType.UTF8, 0); } }