Example #1
0
        /// <summary>
        /// Load header from the stream.
        /// </summary>
        /// <param name="stream">Stream to load header</param>
        public void Deserialize(Stream stream)
        {
            BinaryReader reader = new BinaryReader(stream);

            byte[] idTag = new byte[3];

            // Read the tag identifier
            reader.Read(idTag, 0, 3);
            // Compare the readed tag
            if (Memory.Compare(_id3, idTag, 3) == false)
            {
                throw new TagNotFoundException("ID3v2 tag identifier was not found");
            }
            // Get the id3v2 version byte
            _id3Version = reader.ReadByte();
            if (_id3Version == 0xff)
            {
                throw new InvalidTagException("Corrupt header, invalid ID3v2 version.");
            }
            // Get the id3v2 revision byte
            _id3Revision = reader.ReadByte();
            if (_id3Revision == 0xff)
            {
                throw new InvalidTagException("Corrupt header, invalid ID3v2 revision.");
            }
            // Get the id3v2 flag byte, only read what I understand
            _id3Flags = (byte)(0xf0 & reader.ReadByte());
            // Get the id3v2 size, swap and unsync the integer
            _id3RawSize = Swap.UInt32(Sync.UnsafeBigEndian(reader.ReadUInt32()));
            if (_id3RawSize == 0)
            {
                throw new InvalidTagException("Corrupt header, tag size can't be zero.");
            }
        }
        /// <summary>
        /// Create a frame depending on the tag form its binary representation.
        /// </summary>
        /// <param name="frameId">type of frame</param>
        /// <param name="flags">frame flags</param>
        /// <param name="frame">binary frame</param>
        /// <returns>Frame of tag type</returns>
        public FrameBase Build(string frameId, ushort flags, byte[] buffer)
        {
            // Build a frame
            var frame = FrameFactory.Build(frameId);

            SetFlags(frame, flags);

            uint   index  = 0;
            uint   size   = (uint)buffer.Length;
            Stream stream = new MemoryStream(buffer, false);
            var    reader = new BinaryReader(stream);

            if (GetGrouping(flags) == true)
            {
                frame.Group = reader.ReadByte();
                index++;
            }
            if (frame.Compression == true)
            {
                switch (Version)
                {
                case 3:
                {
                    size = Swap.UInt32(reader.ReadUInt32());
                    break;
                }

                case 4:
                {
                    size = Swap.UInt32(Sync.UnsafeBigEndian(reader.ReadUInt32()));
                    break;
                }

                default:
                {
                    throw new NotImplementedException("ID3v2 Version " + Version + " is not supported.");
                }
                }
                index  = 0;
                stream = new InflaterInputStream(stream);
            }
            if (frame.Encryption == true)
            {
                throw new NotImplementedException("Encryption is not implemented, consequently it is not supported.");
            }
            if (frame.Unsynchronisation == true)
            {
                var memoryStream = new MemoryStream();
                size  = Sync.Unsafe(stream, memoryStream, size);
                index = 0;
                memoryStream.Seek(0, SeekOrigin.Begin);
                stream = memoryStream;
            }
            byte[] frameBuffer = new byte[size - index];
            stream.Read(frameBuffer, 0, (int)(size - index));
            frame.Parse(frameBuffer);
            return(frame);
        }
Example #3
0
        /// <summary>
        /// Save corresponding footer into the stream.
        /// </summary>
        /// <param name="stream">Stream to save header</param>
        public void SerializeFooter(Stream stream)
        {
            // open a writer on the underlying stream but don't use 'using'
            // or Dispose will close the underlying stream at the end
            BinaryWriter writer = new BinaryWriter(stream);

            //TODO: Validate version and revision we support
            writer.Write(_3di);         // ID3v2/file footer identifier; ID3 backwards.
            writer.Write(_id3Version);  // ID3v2 version, e.g. 3 or 4
            writer.Write(_id3Revision); // ID3v2 revision, e.g. 0
            writer.Write(_id3Flags);    // ID3v2 flags
            writer.Write(Swap.UInt32(Sync.Safe(_id3RawSize)));
        }
Example #4
0
        /// <summary>
        /// Load the ID3 extended header from a stream
        /// </summary>
        /// <param name="stream">Binary stream containing a ID3 extended header</param>
        public void Deserialize(Stream stream)
        {
            BinaryReader reader = new BinaryReader(stream);

            _size = Swap.UInt32(Sync.UnsafeBigEndian(reader.ReadUInt32()));
            if (_size < 6)
            {
                throw new InvalidFrameException("Corrupt id3 extended header.");
            }

            // TODO: implement the extended header, copy for now since it's optional
            _extendedHeader = new Byte[_size];
            stream.Read(_extendedHeader, 0, (int)_size);
        }
Example #5
0
        /// <summary>
        /// Load the ID3 extended header from a stream
        /// </summary>
        /// <param name="stream">Binary stream containing a ID3 extended header</param>
        public void Deserialize([NotNull] Stream stream)
        {
            if (stream == null)
            {
                throw new ArgumentNullException("stream");
            }

            using (var reader = new BinaryReader(stream, Encoding.UTF8, true))
                Size = Swap.UInt32(Sync.UnsafeBigEndian(reader.ReadUInt32()));
            if (Size < 6)
            {
                throw new InvalidFrameException("Corrupt id3 extended header.");
            }

            // TODO: implement the extended header, copy for now since it's optional
            _extendedHeader = new byte[Size];
            stream.Read(_extendedHeader, 0, (int)Size);
        }
        /// <summary>
        /// Build a binary data frame form the frame object.
        /// </summary>
        /// <param name="frame">ID3 Frame</param>
        /// <returns>binary frame representation</returns>
        public byte[] Make(FrameBase frame, out ushort flags)
        {
            flags = GetFlags(frame);
            var buffer = frame.Make();

            var memoryStream = new MemoryStream();
            var writer       = new BinaryWriter(memoryStream);

            if (frame.Group.HasValue == true)
            {
                writer.Write((byte)frame.Group);
            }

            if (frame.Compression == true)
            {
                switch (Version)
                {
                case 3:
                {
                    writer.Write(Swap.Int32(buffer.Length));
                    break;
                }

                case 4:
                {
                    writer.Write(Sync.UnsafeBigEndian(Swap.UInt32((uint)buffer.Length)));
                    break;
                }

                default:
                {
                    throw new NotImplementedException("ID3v2 Version " + Version + " is not supported.");
                }
                }
                var buf      = new byte[2048];
                var deflater = new Deflater(Deflater.BEST_COMPRESSION);
                deflater.SetInput(buffer, 0, buffer.Length);
                deflater.Finish();
                while (!deflater.IsNeedingInput)
                {
                    int len = deflater.Deflate(buf, 0, buf.Length);
                    if (len <= 0)
                    {
                        break;
                    }
                    memoryStream.Write(buf, 0, len);
                }

                if (!deflater.IsNeedingInput)
                {
                    //TODO: Skip and remove invalid frames.
                    throw new InvalidFrameException("Can't decompress frame '" + frame.FrameId + "' missing data");
                }
            }
            else
            {
                memoryStream.Write(buffer, 0, buffer.Length);
            }

            if (frame.Encryption == true)
            {
                //TODO: Encryption
                throw new NotImplementedException("Encryption is not implemented, consequently it is not supported.");
            }

            if (frame.Unsynchronisation == true)
            {
                MemoryStream synchStream = new MemoryStream();
                Sync.Unsafe(memoryStream, synchStream, (uint)memoryStream.Position);
                memoryStream = synchStream;
            }
            return(memoryStream.ToArray());
        }
Example #7
0
        internal FrameBase Build([NotNull] string frameId, ushort flags, [NotNull] byte[] buffer)
        {
            // Build a frame
            var frame = FrameFactory.Build(frameId);

            SetFlags(frame, flags);

            var    index          = 0;
            var    size           = (uint)buffer.Length;
            Stream stream         = new MemoryStream(buffer, false);
            var    streamsToClose = new List <Stream>(3)
            {
                stream
            };

            try
            {
                using (var reader = new BinaryReader(stream, Encoding.UTF8, false))
                {
                    if (GetGrouping(flags))
                    {
                        frame.Group = reader.ReadByte();
                        index++;
                    }

                    if (frame.Compression)
                    {
                        switch (_version)
                        {
                        case 3:
                            size = Swap.UInt32(reader.ReadUInt32());
                            break;

                        case 4:
                            size = Swap.UInt32(Sync.UnsafeBigEndian(reader.ReadUInt32()));
                            break;

                        default:
                            throw new NotImplementedException($"ID3v2 Version {_version} is not supported.");
                        }
                        index  = 0;
                        stream = new InflaterInputStream(stream);
                        streamsToClose.Add(stream);
                    }

                    if (frame.Encryption)
                    {
                        throw new NotImplementedException(
                                  "Encryption is not implemented, consequently it is not supported.");
                    }

                    if (frame.Unsynchronisation)
                    {
                        var memoryStream = new MemoryStream();
                        streamsToClose.Add(memoryStream);
                        size  = Sync.Unsafe(stream, memoryStream, size);
                        index = 0;
                        memoryStream.Seek(0, SeekOrigin.Begin);
                        stream = memoryStream;
                    }

                    var frameBuffer = new byte[size - index];
                    stream.Read(frameBuffer, 0, (int)(size - index));
                    frame.Parse(frameBuffer);
                    return(frame);
                }
            }
            finally
            {
                foreach (var streamToClose in streamsToClose)
                {
                    streamToClose.Close();
                }
            }
        }
Example #8
0
        internal byte[] Make([NotNull] FrameBase frame, out ushort flags)
        {
            flags = GetFlags(frame);
            var buffer = frame.Make();

            var memoryStream   = new MemoryStream();
            var streamsToClose = new List <Stream>(2)
            {
                memoryStream
            };

            try
            {
                using (var writer = new BinaryWriter(memoryStream, Encoding.UTF8, true))
                {
                    if (frame.Group.HasValue)
                    {
                        writer.Write((byte)frame.Group);
                    }

                    if (frame.Compression)
                    {
                        switch (_version)
                        {
                        case 3:
                            writer.Write(Swap.Int32(buffer.Length));
                            break;

                        case 4:
                            writer.Write(Sync.UnsafeBigEndian(Swap.UInt32((uint)buffer.Length)));
                            break;

                        default:
                            throw new NotImplementedException($"ID3v2 Version {_version} is not supported.");
                        }

                        var buf      = new byte[2048];
                        var deflater = new Deflater(Deflater.BEST_COMPRESSION);
                        deflater.SetInput(buffer, 0, buffer.Length);
                        deflater.Finish();

                        while (!deflater.IsNeedingInput)
                        {
                            var len = deflater.Deflate(buf, 0, buf.Length);
                            if (len <= 0)
                            {
                                break;
                            }
                            memoryStream.Write(buf, 0, len);
                        }

                        //TODO: Skip and remove invalid frames.
                        if (!deflater.IsNeedingInput)
                        {
                            throw new InvalidFrameException($"Can't decompress frame '{frame.FrameId}' missing data");
                        }
                    }
                    else
                    {
                        memoryStream.Write(buffer, 0, buffer.Length);
                    }

                    //TODO: Encryption
                    if (frame.Encryption)
                    {
                        throw new NotImplementedException(
                                  "Encryption is not implemented, consequently it is not supported.");
                    }

                    if (frame.Unsynchronisation)
                    {
                        var synchStream = new MemoryStream();
                        streamsToClose.Add(synchStream);
                        Sync.Unsafe(memoryStream, synchStream, (uint)memoryStream.Position);
                        memoryStream = synchStream;
                    }

                    return(memoryStream.ToArray());
                }
            }
            finally
            {
                foreach (var streamToClose in streamsToClose)
                {
                    streamToClose.Close();
                }
            }
        }
Example #9
0
        /// <summary>
        /// Load the ID3v2 frames to a binary stream
        /// </summary>
        /// <param name="stream">Bynary stream holding the ID3 Tag</param>
        /// <returns>Model keeping the ID3 Tag structure</returns>
        public static FrameModel Deserialize(Stream stream)
        {
            FrameModel frameModel = new FrameModel();

            frameModel.Header.Deserialize(stream); // load the ID3v2 header
            if (frameModel.Header.Version != 3 & frameModel.Header.Version != 4)
            {
                throw new NotImplementedException("ID3v2 Version " + frameModel.Header.Version + " is not supported.");
            }

            uint id3TagSize = frameModel.Header.TagSize;

            if (frameModel.Header.Unsync == true)
            {
                MemoryStream memory = new MemoryStream();
                id3TagSize -= Sync.Unsafe(stream, memory, id3TagSize);
                stream      = memory; // This is now the stream
                if (id3TagSize <= 0)
                {
                    throw new InvalidTagException("Data is missing after the header.");
                }
            }
            uint rawSize;

            // load the extended header
            if (frameModel.Header.ExtendedHeader == true)
            {
                frameModel.ExtendedHeader.Deserialize(stream);
                rawSize = id3TagSize - frameModel.ExtendedHeader.Size;
                if (id3TagSize <= 0)
                {
                    throw new InvalidTagException("Data is missing after the extended header.");
                }
            }
            else
            {
                rawSize = id3TagSize;
            }

            // Read the frames
            if (rawSize <= 0)
            {
                throw new InvalidTagException("No frames are present in the Tag, there must be at least one present.");
            }

            BinaryReader reader = new BinaryReader(stream);
            // Load the tag frames
            uint        index       = 0;
            FrameHelper frameHelper = new FrameHelper(frameModel.Header);

            // repeat while there is at least one complete frame available, 10 is the minimum size of a valid frame
            // but what happens when there's only, say, 5 bytes of padding?
            // we need to read a single byte to inspect for padding, then if not, read a whole tag.
            while (index < rawSize)
            {
                byte[] frameId = new byte[4];
                reader.Read(frameId, 0, 1);
                if (frameId[0] == 0)
                {
                    // We reached the padding area between the frames and the end of the tag,
                    // signified by a zero byte where the frame name should be.

                    // we could double check we actually know what's going on
                    // and check the padding goes exactly to the end of the id3 tag
                    // but in fact it doesn't give us any benefit.
                    //
                    // one of the following cases must apply:
                    //
                    // 1) if the id3 tag specifies more bytes than the frames use up,
                    //    and that space is exactly filled with zeros to the first audio frame,
                    //    it complies with the standard and everything is happy.
                    //
                    // 2) if the id3 tag specifies more bytes than the frames use up,
                    //    and that space isn't completely filled with zeros,
                    //    we assume the software that generated the tag
                    //    forgot to zero-fill it properly.
                    //
                    // 3) if the zero padding extends past the start of the id3 tag,
                    //    we assume the audio payload starts with skippable stuff too.
                    //
                    // 4) if the audio payload doesn't start with a valid mpeg audio frame header,
                    //    (vbr headers have valid mpeg audio frame headers)
                    //    we assume there's a tag in a format we don't recognise.
                    //    It still has to comply with the mpeg sync rules,
                    //    so we will have to use that to find the start of the audio.
                    //
                    // in all cases, we read the specified length of the id3 tag
                    // and let the higher-level processing inspect the audio payload
                    // to decide what is audio, what is extra padding,
                    // and what is unrecognised (non-id3) tags.

                    // how much does the tag size say should be left?
                    frameModel.Header.PaddingSize = rawSize - index;

                    //// advance the stream past any zero bytes,
                    //// and verify the real measured size against that specified in the tag
                    //uint observed = SeekEndOfPadding(src) + 1;
                    //if( frameModel.Header.PaddingSize != observed )
                    //    throw new InvalidPaddingException(observed, frameModel.Header.PaddingSize);

                    // advance the stream to the specified end of the tag
                    // this skips any non-zero rubbish in the padding without looking at it.
                    stream.Seek(frameModel.Header.PaddingSize - 1, SeekOrigin.Current);

                    break;
                }
                if (index + 10 > rawSize)
                {
                    // 10 is the minimum size of a valid frame;
                    // we read one already, if less than 9 chars left it's an error.
                    throw new InvalidTagException("Tag is corrupt, must be formed of complete frames.");
                }
                // read another 3 chars
                reader.Read(frameId, 1, 3);
                index += 4; // have read 4 bytes
                //TODO: Validate key valid ranges
                uint frameSize = Swap.UInt32(reader.ReadUInt32());
                index += 4; // have read 4 bytes
                // ID3v4 now has syncsafe sizes
                if (frameModel.Header.Version == 4)
                {
                    Sync.Unsafe(frameSize);
                }
                // The size of the frame can't be larger than the avaliable space
                if (frameSize > rawSize - index)
                {
                    throw new InvalidFrameException("A frame is corrupt, it can't be larger than the avaliable space remaining.");
                }

                ushort flags = Swap.UInt16(reader.ReadUInt16());
                index += 2; // read 2 bytes
                byte[] frameData = new byte[frameSize];
                reader.Read(frameData, 0, (int)frameSize);
                index += frameSize; // read more bytes
                frameModel.Add(frameHelper.Build(UTF8Encoding.UTF8.GetString(frameId, 0, 4), flags, frameData));
            }
            return(frameModel);
        }
Example #10
0
        ///// <summary>
        ///// find the first non-zero character in the stream
        ///// then back up one, so it will be the first char that gets read
        ///// and the stream position is pointing to the start of the non-zero (e.g. audio) data
        ///// </summary>
        ///// <param name="stream"></param>
        ///// <returns></returns>
        //static private uint SeekEndOfPadding( Stream stream )
        //{
        //    uint index = 0;
        //    while( stream.ReadByte() == 0 )
        //    {
        //        ++index;
        //    }
        //    stream.Seek(-1, SeekOrigin.Current);
        //    return index;
        //}

        /// <summary>
        /// Save the ID3v2 frames to a binary stream
        /// </summary>
        /// <param name="frameModel">Model keeping the ID3 Tag structure</param>
        /// <param name="stream">Stream keeping the ID3 Tag</param>
        public static void Serialize(FrameModel frameModel, Stream stream)
        {
            if (frameModel.Count <= 0)
            {
                throw new InvalidTagException("Can't serialize a ID3v2 tag without any frames, there must be at least one present.");
            }

            MemoryStream memory = new MemoryStream();
            BinaryWriter writer = new BinaryWriter(memory);

            FrameHelper frameHelper = new FrameHelper(frameModel.Header);

            // Write the frames in binary format
            foreach (IFrame frameBase in frameModel)
            {
                //TODO: Do validations on tag name correctness
                byte[] frameId = new byte[4];
                UTF8Encoding.UTF8.GetBytes(frameBase.FrameId, 0, 4, frameId, 0);
                writer.Write(frameId); // Write the 4 byte text tag
                byte[] frame     = frameHelper.Make(frameBase);
                uint   frameSize = (uint)frame.Length;

                if (frameModel.Header.Version == 4)
                {
                    Sync.Safe(frameSize);
                }

                writer.Write(Swap.UInt32(frameSize));
                writer.Write(Swap.UInt16(frameBase.Flags));
                writer.Write(frame);
            }

            uint id3TagSize = (uint)memory.Position;

            // Skip the header 10 bytes for now, we will come back and write the Header
            // with the correct size once have the tag size + padding
            stream.Seek(10, SeekOrigin.Begin);

            // TODO: Add extended header handling
            if (frameModel.Header.Unsync == true)
            {
                id3TagSize += Sync.Safe(memory, stream, id3TagSize);
            }
            else
            {
                memory.WriteTo(stream);
            }

            // update the TagSize stored in the tagModel
            frameModel.Header.TagSize = id3TagSize;

            // next write the padding of zeros, if any
            if (frameModel.Header.Padding)
            {
                byte[] bytes = new byte[frameModel.Header.PaddingSize];
                stream.Write(bytes, 0, (int)frameModel.Header.PaddingSize);
            }

            // next write the footer, if any
            if (frameModel.Header.Footer)
            {
                frameModel.Header.SerializeFooter(stream);
            }

            // Now seek back to the start and write the header
            long position = stream.Position;

            stream.Seek(0, SeekOrigin.Begin);
            frameModel.Header.Serialize(stream);

            // reset position to the end of the tag
            stream.Position = position;
        }
Example #11
0
        /// <summary>
        /// Save the ID3v2 frames to a binary stream
        /// </summary>
        /// <param name="frameModel">Model keeping the ID3 Tag structure</param>
        /// <param name="stream">Stream keeping the ID3 Tag</param>
        public static void Serialize([NotNull] TagModel frameModel, [NotNull] Stream stream)
        {
            if (frameModel.Count <= 0)
            {
                throw new InvalidTagException("Can't serialize a ID3v2 tag without any frames, there must be at least one present.");
            }

            using (var memory = new MemoryStream())
                using (var writer = new BinaryWriter(memory, Encoding.UTF8, true))
                {
                    var frameHelper = new FrameHelper(frameModel.Header);

                    // Write the frames in binary format
                    foreach (var frame in frameModel)
                    {
                        //TODO: Do validations on tag name correctness
                        var frameId = new byte[4];
                        Encoding.UTF8.GetBytes(frame.FrameId, 0, 4, frameId, 0);
                        writer.Write(frameId); // Write the 4 byte text tag
                        var buffer    = frameHelper.Make(frame, out var flags);
                        var frameSize = (uint)buffer.Length;

                        if (frameModel.Header.Version == 4)
                        {
                            frameSize = Sync.Safe(frameSize);
                        }

                        writer.Write(Swap.UInt32(frameSize));
                        writer.Write(Swap.UInt16(flags));
                        writer.Write(buffer);
                    }

                    var id3TagSize = (uint)memory.Position;

                    // Skip the header 10 bytes for now, we will come back and write the Header
                    // with the correct size once have the tag size + padding
                    stream.Seek(10, SeekOrigin.Begin);

                    // TODO: Add extended header handling
                    if (frameModel.Header.Unsync)
                    {
                        id3TagSize += Sync.Safe(memory, stream, id3TagSize);
                    }
                    else
                    {
                        memory.WriteTo(stream);
                    }

                    // update the TagSize stored in the tagModel
                    frameModel.Header.TagSize = id3TagSize;

                    // If padding + tag size is too big, shrink the padding (rather than throwing an exception)
                    frameModel.Header.PaddingSize =
                        Math.Min(frameModel.Header.PaddingSize, 0x10000000 - id3TagSize);

                    // next write the padding of zeros, if any
                    if (frameModel.Header.Padding)
                    {
                        for (var i = 0; i < frameModel.Header.PaddingSize; i++)
                        {
                            stream.WriteByte(0);
                        }
                    }

                    // next write the footer, if any
                    if (frameModel.Header.Footer)
                    {
                        frameModel.Header.SerializeFooter(stream);
                    }

                    // Now seek back to the start and write the header
                    var position = stream.Position;
                    stream.Seek(0, SeekOrigin.Begin);
                    frameModel.Header.Serialize(stream);

                    // reset position to the end of the tag
                    stream.Position = position;
                }
        }
Example #12
0
        /// <summary>
        /// Create a frame depending on the tag form its binary representation.
        /// </summary>
        /// <param name="frameId">type of frame</param>
        /// <param name="flags">frame flags</param>
        /// <param name="frame">binary frame</param>
        /// <returns>Frame of tag type</returns>
        public IFrame Build(string frameId, ushort options, byte[] frame)
        {
            // Build a frame
            IFrame frameBase = FrameFactory.Build(frameId);

            frameBase.Flags = options;

            flagHandler.Flags = options;
            uint         index  = 0;
            uint         size   = (uint)frame.Length;
            Stream       stream = new MemoryStream(frame, false);
            BinaryReader reader = new BinaryReader(stream);

            if (flagHandler.Grouping == true)
            {
                //TODO: Implement grouping when watching grass grow stops beeing intresting.
                /* The byte readed here is the group, we are skiping it for now*/
                reader.ReadByte();
                index++;
            }
            if (flagHandler.Compression == true)
            {
                switch (flagHandler.Version)
                {
                case 3:
                {
                    size = Swap.UInt32(reader.ReadUInt32());
                    break;
                }

                case 4:
                {
                    size = Swap.UInt32(Sync.UnsafeBigEndian(reader.ReadUInt32()));
                    break;
                }

                default:
                {
                    throw new NotImplementedException("ID3v2 Version " + flagHandler.Version + " is not supported.");
                }
                }
                index  = 0;
                stream = new InflaterInputStream(stream);
            }
            if (flagHandler.Encryption == true)
            {
                //TODO: Encryption
                throw new NotImplementedException("Encryption is not implemented, consequently it is not supported.");
            }
            if (flagHandler.Unsynchronisation == true)
            {
                Stream memStream = new MemoryStream();
                size   = Sync.Unsafe(stream, memStream, size);
                index  = 0;
                stream = memStream;
            }
            byte[] frameBuffer = new byte[size - index];
            stream.Read(frameBuffer, 0, (int)(size - index));
            frameBase.Parse(frameBuffer);
            return(frameBase);
        }
Example #13
0
        /// <summary>
        /// Build a binary data frame form the frame object.
        /// </summary>
        /// <param name="frameBase">ID3 Frame</param>
        /// <returns>binary frame representation</returns>
        public byte[] Make(IFrame frameBase)
        {
            flagHandler.Flags = frameBase.Flags;
            byte[] frame = frameBase.Make();

            MemoryStream memoryStream = new MemoryStream();
            BinaryWriter writer       = new BinaryWriter(memoryStream);

            if (flagHandler.Grouping == true)
            {
                //TODO: Do grouping some place in the universe
                byte _group = 0;
                writer.Write(_group);
            }
            if (flagHandler.Compression == true)
            {
                switch (flagHandler.Version)
                {
                case 3:
                {
                    writer.Write(Swap.Int32(frame.Length));
                    break;
                }

                case 4:
                {
                    writer.Write(Sync.UnsafeBigEndian(Swap.UInt32((uint)frame.Length)));
                    break;
                }

                default:
                {
                    throw new NotImplementedException("ID3v2 Version " + flagHandler.Version + " is not supported.");
                }
                }
                byte[]   buf      = new byte[2048];
                Deflater deflater = new Deflater(Deflater.BEST_COMPRESSION);
                deflater.SetInput(frame, 0, frame.Length);
                deflater.Finish();
                while (!deflater.IsNeedingInput)
                {
                    int len = deflater.Deflate(buf, 0, buf.Length);
                    if (len <= 0)
                    {
                        break;
                    }
                    memoryStream.Write(buf, 0, len);
                }

                if (!deflater.IsNeedingInput)
                {
                    //TODO: Skip and remove invalid frames.
                    throw new InvalidFrameException("Can't decompress frame '" + frameBase.FrameId + "' missing data");
                }
            }
            else
            {
                memoryStream.Write(frame, 0, frame.Length);
            }

            if (flagHandler.Encryption == true)
            {
                //TODO: Encryption
                throw new NotImplementedException("Encryption is not implemented, consequently it is not supported.");
            }

            if (flagHandler.Unsynchronisation == true)
            {
                MemoryStream synchStream = new MemoryStream();
                Sync.Unsafe(memoryStream, synchStream, (uint)memoryStream.Position);
                memoryStream = synchStream;
            }
            return(memoryStream.ToArray());
        }