/// <summary> /// Populates the current instance with the contents of the /// raw ID3v2 frame. /// </summary> /// <param name="data"> /// A <see cref="ByteVector" /> object containing the raw /// extended header structure. /// </param> /// <param name="version"> /// A <see cref="byte" /> value indicating the ID3v2 version. /// </param> protected void Parse(ByteVector data, byte version) { if (data == null) { throw new ArgumentNullException("data"); } size = (version == 3 ? 4u : 0u) + SynchData.ToUInt(data.Mid(0, 4)); }
/// <summary> /// Renders the current instance as a raw ID3v2 header. /// </summary> /// <returns> /// A <see cref="ByteVector" /> object containing the /// rendered header. /// </returns> public ByteVector Render() { ByteVector v = new ByteVector(); v.Add(FileIdentifier); v.Add(MajorVersion); v.Add(RevisionNumber); v.Add((byte)flags); v.Add(SynchData.FromUInt(TagSize)); return(v); }
/// <summary> /// Constructs and initializes a new instance of <see /// cref="Footer" /> by reading it from raw footer data. /// </summary> /// <param name="data"> /// A <see cref="ByteVector" /> object containing the raw /// data to build the new instance from. /// </param> /// <exception cref="ArgumentNullException"> /// <paramref name="data" /> is <see langword="null" />. /// </exception> /// <exception cref="CorruptFileException"> /// <paramref name="data" /> is smaller than <see /// cref="Size" />, does not begin with <see /// cref="FileIdentifier" />, contains invalid flag data, /// or contains invalid size data. /// </exception> public Footer(ByteVector data) { if (data == null) { throw new ArgumentNullException("data"); } if (data.Count < Size) { throw new CorruptFileException( "Provided data is smaller than object size."); } if (!data.StartsWith(FileIdentifier)) { throw new CorruptFileException( "Provided data does not start with the file identifier"); } major_version = data[3]; revision_number = data[4]; flags = (HeaderFlags)data[5]; if (major_version == 2 && ((int)flags & 127) != 0) { throw new CorruptFileException( "Invalid flags set on version 2 tag."); } if (major_version == 3 && ((int)flags & 15) != 0) { throw new CorruptFileException( "Invalid flags set on version 3 tag."); } if (major_version == 4 && ((int)flags & 7) != 0) { throw new CorruptFileException( "Invalid flags set on version 4 tag."); } for (int i = 6; i < 10; i++) { if (data[i] >= 128) { throw new CorruptFileException( "One of the bytes in the header was greater than the allowed 128."); } } tag_size = SynchData.ToUInt(data.Mid(6, 4)); }
/// <summary> /// Renders the current instance, encoded in a specified /// ID3v2 version. /// </summary> /// <param name="version"> /// A <see cref="byte" /> value specifying the version of /// ID3v2 to use when encoding the current instance. /// </param> /// <returns> /// A <see cref="ByteVector" /> object containing the /// rendered version of the current instance. /// </returns> /// <exception cref="NotImplementedException"> /// The version specified in the current instance is /// unsupported. /// </exception> public ByteVector Render(byte version) { ByteVector data = new ByteVector(); ByteVector id = ConvertId(frame_id, version, true); if (id == null) { throw new NotImplementedException(); } switch (version) { case 2: data.Add(id); data.Add(ByteVector.FromUInt(frame_size) .Mid(1, 3)); return(data); case 3: ushort new_flags = (ushort)( (((ushort)flags << 1) & 0xE000) | (((ushort)flags << 4) & 0x00C0) | (((ushort)flags >> 1) & 0x0020)); data.Add(id); data.Add(ByteVector.FromUInt(frame_size)); data.Add(ByteVector.FromUShort(new_flags)); return(data); case 4: data.Add(id); data.Add(SynchData.FromUInt(frame_size)); data.Add(ByteVector.FromUShort((ushort)flags)); return(data); default: throw new NotImplementedException( "Unsupported tag version."); } }
/// <summary> /// Constructs and initializes a new instance of <see /// cref="FrameHeader" /> by reading it from raw header data /// of a specified version. /// </summary> /// <param name="data"> /// A <see cref="ByteVector" /> object containing the raw /// data to build the new instance from. /// </param> /// <param name="version"> /// A <see cref="byte" /> value containing the ID3v2 version /// with which the data in <paramref name="data" /> was /// encoded. /// </param> /// <remarks> /// If the data size is smaller than the size of a full /// header, the data is just treated as a frame identifier /// and the remaining values are zeroed. /// </remarks> /// <exception cref="ArgumentNullException"> /// <paramref name="data" /> is <see langword="null" />. /// </exception> /// <exception cref="CorruptFileException"> /// <paramref name="data" /> is smaller than the size of a /// frame identifier or <paramref name="version" /> is less /// than 2 or more than 4. /// </exception> public FrameHeader(ByteVector data, byte version) { if (data == null) { throw new ArgumentNullException("data"); } flags = 0; frame_size = 0; if (version < 2 || version > 4) { throw new CorruptFileException( "Unsupported tag version."); } if (data.Count < (version == 2 ? 3 : 4)) { throw new CorruptFileException( "Data must contain at least a frame ID."); } switch (version) { case 2: // Set the frame ID -- the first three bytes frame_id = ConvertId(data.Mid(0, 3), version, false); // If the full header information was not passed // in, do not continue to the steps to parse the // frame size and flags. if (data.Count < 6) { return; } frame_size = data.Mid(3, 3).ToUInt(); return; case 3: // Set the frame ID -- the first four bytes frame_id = ConvertId(data.Mid(0, 4), version, false); // If the full header information was not passed // in, do not continue to the steps to parse the // frame size and flags. if (data.Count < 10) { return; } // Store the flags internally as version 2.4. frame_size = data.Mid(4, 4).ToUInt(); flags = (FrameFlags)( ((data[8] << 7) & 0x7000) | ((data[9] >> 4) & 0x000C) | ((data[9] << 1) & 0x0040)); return; case 4: // Set the frame ID -- the first four bytes frame_id = new ReadOnlyByteVector( data.Mid(0, 4)); // If the full header information was not passed // in, do not continue to the steps to parse the // frame size and flags. if (data.Count < 10) { return; } frame_size = SynchData.ToUInt(data.Mid(4, 4)); flags = (FrameFlags)data.Mid(8, 2).ToUShort(); return; default: throw new CorruptFileException( "Unsupported tag version."); } }
/// <summary> /// Extracts the field data from the raw data portion of an /// ID3v2 frame. /// </summary> /// <param name="frameData"> /// A <see cref="ByteVector" /> object containing fraw frame /// data. /// </param> /// <param name="offset"> /// A <see cref="int" /> value containing the index at which /// the data is contained. /// </param> /// <param name="version"> /// A <see cref="byte" /> value containing the ID3v2 version /// of the data. /// </param> /// <returns> /// A <see cref="ByteVector" /> object containing the /// extracted field data. /// </returns> /// <remarks> /// This method is necessary for extracting extra data /// prepended to the frame such as the grouping ID. /// </remarks> /// <exception cref="ArgumentNullException"> /// <paramref name="frameData" /> is <see langword="null" />. /// </exception> protected ByteVector FieldData(ByteVector frameData, int offset, byte version) { if (frameData == null) { throw new ArgumentNullException("frameData"); } int data_offset = offset + (int)FrameHeader.Size(version); int data_length = (int)Size; if ((Flags & (FrameFlags.Compression | FrameFlags.DataLengthIndicator)) != 0) { data_offset += 4; data_length -= 4; } if ((Flags & FrameFlags.GroupingIdentity) != 0) { if (frameData.Count >= data_offset) { throw new TagLib.CorruptFileException( "Frame data incomplete."); } group_id = frameData[data_offset++]; data_length--; } if ((Flags & FrameFlags.Encryption) != 0) { if (frameData.Count >= data_offset) { throw new TagLib.CorruptFileException( "Frame data incomplete."); } encryption_id = frameData[data_offset++]; data_length--; } data_length = Math.Min(data_length, frameData.Count - data_offset); if (data_length < 0) { throw new CorruptFileException( "Frame size less than zero."); } ByteVector data = frameData.Mid(data_offset, data_length); if ((Flags & FrameFlags.Unsynchronisation) != 0) { int before_length = data.Count; SynchData.ResynchByteVector(data); data_length -= (data.Count - before_length); } // FIXME: Implement encryption. if ((Flags & FrameFlags.Encryption) != 0) { throw new NotImplementedException(); } // FIXME: Implement compression. if ((Flags & FrameFlags.Compression) != 0) { throw new NotImplementedException(); } /* * if(d->header->compression()) { * ByteVector data(frameDataLength); * uLongf uLongTmp = frameDataLength; * ::uncompress((Bytef *) data.data(), * (uLongf *) &uLongTmp, * (Bytef *) frameData.data() + frameDataOffset, * size()); * return data; * } */ return(data); }
/// <summary> /// Renders the current instance, encoded in a specified /// ID3v2 version. /// </summary> /// <param name="version"> /// A <see cref="byte" /> value specifying the version of /// ID3v2 to use when encoding the current instance. /// </param> /// <returns> /// A <see cref="ByteVector" /> object containing the /// rendered version of the current instance. /// </returns> /// <exception cref="NotImplementedException"> /// The current instance uses some feature that cannot be /// implemented in the specified ID3v2 version, or uses a /// feature, such as encryption or compression, which is not /// yet implemented in the library. /// </exception> public virtual ByteVector Render(byte version) { // Remove flags that are not supported by older versions // of ID3v2. if (version < 4) { Flags &= ~(FrameFlags.DataLengthIndicator | FrameFlags.Unsynchronisation); } if (version < 3) { Flags &= ~(FrameFlags.Compression | FrameFlags.Encryption | FrameFlags.FileAlterPreservation | FrameFlags.GroupingIdentity | FrameFlags.ReadOnly | FrameFlags.TagAlterPreservation); } ByteVector field_data = RenderFields(version); // If we don't have any content, don't render anything. // This will cause the frame to not be rendered. if (field_data.Count == 0) { return(new ByteVector()); } ByteVector front_data = new ByteVector(); if ((Flags & (FrameFlags.Compression | FrameFlags.DataLengthIndicator)) != 0) { front_data.Add(ByteVector.FromUInt((uint) field_data.Count)); } if ((Flags & FrameFlags.GroupingIdentity) != 0) { front_data.Add(group_id); } if ((Flags & FrameFlags.Encryption) != 0) { front_data.Add(encryption_id); } // FIXME: Implement compression. if ((Flags & FrameFlags.Compression) != 0) { throw new NotImplementedException( "Compression not yet supported"); } // FIXME: Implement encryption. if ((Flags & FrameFlags.Encryption) != 0) { throw new NotImplementedException( "Encryption not yet supported"); } if ((Flags & FrameFlags.Unsynchronisation) != 0) { SynchData.UnsynchByteVector(field_data); } if (front_data.Count > 0) { field_data.Insert(0, front_data); } header.FrameSize = (uint)field_data.Count; ByteVector header_data = header.Render(version); header_data.Add(field_data); return(header_data); }