/// <summary>Initializes a new instance of the <see cref="ID3v2Header"/> class.</summary> /// <param name="version">The version.</param> /// <param name="revision">The revision.</param> /// <param name="flags">The flags.</param> /// <param name="size">The size.</param> /// <exception cref="NotSupportedException"></exception> public ID3v2Header(byte version, byte revision, ID3v2HeaderFlags flags, int size) { Version = version; m_Revision = revision; Flags = flags; m_BodySize = size; switch (Version) { case 2: Flags = (ID3v2HeaderFlags)((int)Flags & 0xC0); throw new NotImplementedException("Missing ID3v2.2 implementation."); case 3: Flags = (ID3v2HeaderFlags)((int)Flags & 0xE0); break; case 4: Flags = (ID3v2HeaderFlags)((int)Flags & 0xF0); break; default: throw new NotSupportedException(string.Format("ID3v2.{0} is not supported!", Version)); } m_Data = new byte[10]; m_Data[0] = (byte)'I'; m_Data[1] = (byte)'D'; m_Data[2] = (byte)'3'; m_Data[3] = Version; m_Data[4] = 0; m_Data[5] = (byte)((int)Flags & 0xF0); ID3v2EnUnsync.Int32(m_BodySize, m_Data, 6); }
private void ReadHeaderFlags() { _HeaderFlags = (ID3v2HeaderFlags)TStream.ReadByte(TStream.FS); if ((_HeaderFlags & ID3v2HeaderFlags.Experimental) == ID3v2HeaderFlags.Experimental) { _Errors.Add(new ID3Exception("This file contain Experimental ID3", ExceptionLevels.Warning)); } }
/// <summary> /// Internally parses the current data and loads all fields. /// </summary> protected void ParseData() { if ((m_Data[0] != (byte)'3') || (m_Data[1] != (byte)'D') || (m_Data[2] != (byte)'I')) { throw new InvalidDataException(string.Format("Missing ID3 identifier!")); } m_Version = m_Data[3]; m_Revision = m_Data[4]; m_Flags = CheckFlags(m_Data[5]); m_BodySize = ID3v2DeUnsync.Int32(m_Data, 6); }
/// <summary>Retrieves the tag as byte array.</summary> /// <returns></returns> public byte[] ToArray() { var buffer = new FifoBuffer(); ID3v2HeaderFlags flags = ID3v2HeaderFlags.None; /* we do not like extended headers so we won't write them * if (m_ExtendedHeader != null) * { * buffer.Enqueue(m_ExtendedHeader.Data); * flags |= ID3v2HeaderFlags.ExtendedHeader; * } */ foreach (ID3v2Frame frame in frames) { buffer.Enqueue(frame.RawData); } int bodySize = buffer.Length; // no one likes footers so we won't write them var header = new ID3v2Header(Header.Version, Header.Revision, flags, bodySize); buffer.Prepend(header.Data); return(buffer.ToArray()); }
private static Dictionary <ID3Tag, object> ParseID3v2(byte[] data) { // TODO: add support for ID3v2.2.0 shorter frame IDs Dictionary <ID3Tag, object> tags = new Dictionary <ID3Tag, object>(); if (data.Length < 21) // there must be at least one header (10 bytes) containing one frame (at least 10 byte header + 1 byte) { return(tags); } using (MemoryStream stream = new MemoryStream(data)) { byte[] header = new byte[10]; if (stream.Read(header, 0, header.Length) != header.Length) { throw new MetaParseException("Unable to read full ID3v2 tag header"); } if (header[0] != 'I' || header[1] != 'D' || header[2] != '3') { return(tags); } byte versionMajor = header[3]; byte versionMinor = header[4]; string version = $"ID3v2.{versionMajor}.{versionMinor}"; if (versionMajor != 3 && versionMajor != 4) { throw new NotImplementedException($"Only ID3v2.3.0 and ID3v2.4.0 are supported. Detected version: {version}"); } tags[ID3Tag.Version] = version; ID3v2HeaderFlags flags = (ID3v2HeaderFlags)header[5]; int size = BitConverter.ToInt32(header.Skip(6).Take(4).Reverse().ToArray(), 0); if (flags.HasFlag(ID3v2HeaderFlags.ExtendedHeader)) { byte[] extendedHeader = new byte[10]; if (stream.Read(extendedHeader, 0, extendedHeader.Length) != extendedHeader.Length) { throw new MetaParseException("Unable to read full ID3v2 extended header"); } int extendedSize = BitConverter.ToInt32(extendedHeader.Take(4).Reverse().ToArray(), 0); ID3v2ExtendedFlags extendedFlags = (ID3v2ExtendedFlags)((extendedHeader[4] << 8) | extendedHeader[5]); int sizeOfPadding = BitConverter.ToInt32(extendedHeader.Skip(6).Take(4).Reverse().ToArray(), 0); Trace.WriteLine($"[{tags[ID3Tag.Version]}] Ignoring extended header"); stream.Position += extendedSize; } while (stream.Position < size - 10) { byte[] frameHeader = new byte[10]; if (stream.Read(frameHeader, 0, frameHeader.Length) != frameHeader.Length) { throw new MetaParseException("Unable to read full ID3v2 frame header"); } string frameID = Encoding.ASCII.GetString(frameHeader, 0, 4); if (frameID == "\0\0\0\0") { break; // TODO: done? } int frameSize = BitConverter.ToInt32(frameHeader.Skip(4).Take(4).Reverse().ToArray(), 0); ID3v2FrameFlags frameFlags = (ID3v2FrameFlags)((frameHeader[8] << 8) | frameHeader[9]); switch (frameID) { case "TALB": tags[ID3Tag.Album] = ParseTextualFrame(stream, frameSize); break; case "TIT2": tags[ID3Tag.Title] = ParseTextualFrame(stream, frameSize); break; case "TPE2": tags[ID3Tag.Artist] = ParseTextualFrame(stream, frameSize); break; case "TYER": tags[ID3Tag.Year] = Int32.Parse(ParseTextualFrame(stream, frameSize)); break; case "TRCK": string track = ParseTextualFrame(stream, frameSize); if (track.Contains('/')) { tags[ID3Tag.Track] = Int32.Parse(track.Split('/')[0]); // "<current>/<total>" } else { tags[ID3Tag.Track] = Int32.Parse(track); } break; default: Trace.WriteLine($"[{tags[ID3Tag.Version]}] Ignoring frame ID: \"{frameID}\""); stream.Position += frameSize; break; } } } return(tags); }