/// <summary> /// Creates a <see cref="Frame" /> object by reading it from /// raw ID3v2 frame data. /// </summary> /// <param name="data"> /// A <see cref="ByteVector" /> object containing a raw ID3v2 /// frame. /// </param> /// <param name="offset"> /// A <see cref="int" /> value reference specifying at what /// index in <paramref name="data" /> at which the frame /// begins. After reading, it contains the offset of the next /// frame to be read. /// </param> /// <param name="version"> /// A <see cref="byte" /> value specifying the ID3v2 version /// the frame in <paramref name="data"/> is encoded in. /// </param> /// <param name="alreadyUnsynched"> /// A <see cref="bool" /> value specifying whether the entire /// tag has already been unsynchronized. /// </param> /// <returns> /// A <see cref="Frame" /> object read from the data, or <see /// langword="null" /> if none is found. /// </returns> /// <exception cref="System.NotImplementedException"> /// The frame contained in the raw data could not be /// converted to ID3v2 or uses encryption or compression. /// </exception> public static Frame CreateFrame(ByteVector data, ref int offset, byte version, bool alreadyUnsynched) { int position = offset; FrameHeader header = new FrameHeader(data.Mid(position, (int)FrameHeader.Size(version)), version); offset += (int)(header.FrameSize + FrameHeader.Size( version)); if (header.FrameId == null) { throw new System.NotImplementedException(); } foreach (byte b in header.FrameId) { char c = (char)b; if ((c < 'A' || c > 'Z') && (c < '1' || c > '9')) { return(null); } } if (alreadyUnsynched) { // Mark the frame as not Unsynchronozed because the entire // tag has already been Unsynchronized header.Flags &= ~FrameFlags.Unsynchronisation; } // Windows Media Player may create zero byte frames. // Just send them off as unknown and delete them. if (header.FrameSize == 0) { header.Flags |= FrameFlags.TagAlterPreservation; return(new UnknownFrame(data, position, header, version)); } // TODO: Support Compression. if ((header.Flags & FrameFlags.Compression) != 0) { throw new System.NotImplementedException(); } // TODO: Support Encryption. if ((header.Flags & FrameFlags.Encryption) != 0) { throw new System.NotImplementedException(); } foreach (FrameCreator creator in frame_creators) { Frame frame = creator(data, position, header, version); if (frame != null) { return(frame); } } // This is where things get necissarily nasty. Here we // determine which Frame subclass (or if none is found // simply an Frame) based on the frame ID. Since there // are a lot of possibilities, that means a lot of if // blocks. // Text Identification (frames 4.2) if (header.FrameId == FrameType.TXXX) { return(new UserTextInformationFrame(data, position, header, version)); } if (header.FrameId [0] == (byte)'T') { return(new TextInformationFrame(data, position, header, version)); } // Unique File Identifier (frames 4.1) if (header.FrameId == FrameType.UFID) { return(new UniqueFileIdentifierFrame(data, position, header, version)); } // Music CD Identifier (frames 4.5) if (header.FrameId == FrameType.MCDI) { return(new MusicCdIdentifierFrame(data, position, header, version)); } // Unsynchronized Lyrics (frames 4.8) if (header.FrameId == FrameType.USLT) { return(new UnsynchronisedLyricsFrame(data, position, header, version)); } // Synchronized Lyrics (frames 4.9) if (header.FrameId == FrameType.SYLT) { return(new SynchronisedLyricsFrame(data, position, header, version)); } // Comments (frames 4.10) if (header.FrameId == FrameType.COMM) { return(new CommentsFrame(data, position, header, version)); } // Relative Volume Adjustment (frames 4.11) if (header.FrameId == FrameType.RVA2) { return(new RelativeVolumeFrame(data, position, header, version)); } // Attached Picture (frames 4.14) if (header.FrameId == FrameType.APIC) { return(new AttachedPictureFrame(data, position, header, version)); } // General Encapsulated Object (frames 4.15) if (header.FrameId == FrameType.GEOB) { return(new GeneralEncapsulatedObjectFrame(data, position, header, version)); } // Play Count (frames 4.16) if (header.FrameId == FrameType.PCNT) { return(new PlayCountFrame(data, position, header, version)); } // Play Count (frames 4.17) if (header.FrameId == FrameType.POPM) { return(new PopularimeterFrame(data, position, header, version)); } // Terms of Use (frames 4.22) if (header.FrameId == FrameType.USER) { return(new TermsOfUseFrame(data, position, header, version)); } // Private (frames 4.27) if (header.FrameId == FrameType.PRIV) { return(new PrivateFrame(data, position, header, version)); } return(new UnknownFrame(data, position, header, version)); }
/// <summary> /// Constructs and initializes a new instance of <see /// cref="Frame" /> with a specified header. /// </summary> /// <param name="header"> /// A <see cref="FrameHeader" /> value containing the header /// to use for the new instance. /// </param> protected Frame(FrameHeader header) { this.header = header; }
/// <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> /// Constructs and initializes a new instance of <see /// cref="CommentsFrame" /> by reading its raw data in a /// specified ID3v2 version. /// </summary> /// <param name="data"> /// A <see cref="ByteVector" /> object containing the raw /// representation of the new frame. /// </param> /// <param name="offset"> /// A <see cref="int" /> indicating at what offset in /// <paramref name="data" /> the frame actually begins. /// </param> /// <param name="header"> /// A <see cref="FrameHeader" /> containing the header of the /// frame found at <paramref name="offset" /> in the data. /// </param> /// <param name="version"> /// A <see cref="byte" /> indicating the ID3v2 version the /// raw frame is encoded in. /// </param> protected internal CommentsFrame(ByteVector data, int offset, FrameHeader header, byte version) : base(header) { SetData(data, offset, version, false); }