public void processFlags()
        {
            switch (MajorVersion)
            {
            default:
            case 2:
                //ID3v2.2 = ExtendedHeader Not Supported
                break;

            case 3:
                //ID3v2.3 = %x0000000 00000000 (x = CRCPresent)
                CRCPresent  = ((RawData[4] & 0x80) == 0x80);
                PaddingSize = ID3Base.expand(RawData[6], RawData[7], RawData[8], RawData[9]);

                if (CRCPresent)
                {
                    CRCData = ID3Base.expand(RawData[10], RawData[11], RawData[12], RawData[13]);
                }
                break;

            case 4:
                //ID3v2.4 = %0bcd0000 (b = TagIsUpdate, c = CRCPresent, d = TagRestrictions)
                int numOfFlags = (int)RawData[4];

                for (int i = 0; i < numOfFlags; ++i)
                {
                    TagIsUpdate     = ((RawData[5 + i] & 0x40) == 0x40);
                    CRCPresent      = ((RawData[5 + i] & 0x20) == 0x20);
                    TagRestrictions = ((RawData[5 + i] & 0x10) == 0x10);
                }

                int DataStartingPoint = 5 + numOfFlags;

                if (TagIsUpdate)
                {
                    //Flag Data Length is $00
                    DataStartingPoint++;
                }
                if (CRCPresent)
                {
                    //Flag Data Length is $05
                    DataStartingPoint++;

                    CRCData = ID3Base.syncsafe(RawData[DataStartingPoint],
                                               RawData[DataStartingPoint + 1],
                                               RawData[DataStartingPoint + 2],
                                               RawData[DataStartingPoint + 3],
                                               RawData[DataStartingPoint + 4]);

                    DataStartingPoint += 5;
                }
                if (TagRestrictions)
                {
                    //Flag data length is $01
                    DataStartingPoint++;

                    //Restrictions %ppqr rstt
                    //p - Tag Size Restrictions
                    byte p = (byte)(RawData[DataStartingPoint] & 0xC0);
                    switch (p)
                    {
                    case 0x00:         //0000 0000 [00] - No more than 128 frames and 1 MB total tag size.
                        break;

                    case 0x40:         //0100 0000 [01] - No more than 64 frames and 128 KB total tag size.
                        break;

                    case 0x80:         //1000 0000 [10] - No more than 32 frames and 40 KB total tag size.
                        break;

                    case 0xC0:         //1100 0000 [11] - No more than 32 frames and 4 KB total tag size.
                        break;
                    }

                    //q - Text encoding restrictions
                    byte q = (byte)(RawData[DataStartingPoint] & 0x20);
                    switch (q)
                    {
                    case 0x00:         //0000 0000 [0] - No Restrictions
                        break;

                    case 0x20:         //0010 0000 [1] - Strings are only encoded with ISO-8859-1 [ISO-8859-1] or UTF-8 [UTF-8].
                        break;
                    }

                    //r - Text fields size restrictions
                    byte r = (byte)(RawData[DataStartingPoint] & 0x18);
                    switch (r)
                    {
                    case 0x00:         //0000 0000 [00] - No restrictions
                        break;

                    case 0x08:         //0000 1000 [01] - No string is longer than 1024 characters.
                        break;

                    case 0x10:         //0001 0000 [10] - No string is longer than 128 characters.
                        break;

                    case 0x18:         //0001 1000 [11] - No string is longer than 30 characters.
                        break;
                    }

                    //s - Image encoding restrictions
                    byte s = (byte)(RawData[DataStartingPoint] & 0x04);
                    switch (s)
                    {
                    case 0x00:         //0000 0000 [0] - No restrictions
                        break;

                    case 0x04:         //0000 0100 [1] - Images are encoded only with PNG [PNG] or JPEG [JFIF].
                        break;
                    }

                    //t - Image size restrictions
                    byte t = (byte)(RawData[DataStartingPoint] & 0x03);
                    switch (t)
                    {
                    case 0x00:         //0000 0000 [00] - No restrictions
                        break;

                    case 0x01:         //0000 0001 [01] - All images are 256x256 pixels or smaller.
                        break;

                    case 0x02:         //0000 0010 [10] - All images are 64x64 pixels or smaller.
                        break;

                    case 0x03:         //0000 0011 [11] - All images are exactly 64x64 pixels, unless required otherwise.
                        break;
                    }
                }
                break;
            }
        }
        public ID3v2Tag(FileStream fs, long lngStartOffset)
        {
            //Store FileStreams current position
            long fsOriginalPosition = fs.Position;

            setDefaultValues();

            Regex ProperFrameNamePattern = new Regex("[A-Z0-9]");

            byte[] bytHeaderID3 = new byte[10];
            fs.Position = lngStartOffset;
            fs.Read(bytHeaderID3, 0, 10);

            //ID3v2.X should start with "ID3"
            if ((char)bytHeaderID3[0] == 'I' && (char)bytHeaderID3[1] == 'D' && (char)bytHeaderID3[2] == '3')
            {
                Position = lngStartOffset;

                //Major and Minor Versions should NOT be 0xFF
                MajorVersion = (bytHeaderID3[3] != 0xFF) ? (int)bytHeaderID3[3] : 0;
                MinorVersion = (bytHeaderID3[4] != 0xFF) ? (int)bytHeaderID3[4] : 0;

                ProcessFlags(bytHeaderID3[5]);

                /*
                 * ID3v2 Uses synchsafe integers, which skip the largest bit (farthest left) of each byte.
                 *
                 * See http://www.id3.org/id3v2.4.0-structure and http://id3lib.sourceforge.net/id3/id3v2.3.0.html#sec3.1
                 */
                //Console.WriteLine("ID3v2.{0:D}.{1:D} Exists", tag.MajorVersion, tag.MinorVersion);


                //Max size for tag size = 2^28 = 268435456 bytes = 256MB; int.MaxValue = 2147483647 bytes = 2048MB
                TagSize = (uint)ID3Base.syncsafe(bytHeaderID3[6], bytHeaderID3[7], bytHeaderID3[8], bytHeaderID3[9]); //(int)((bytHeaderID3[6] << 21) | (bytHeaderID3[7] << 14) | (bytHeaderID3[8] << 7) | bytHeaderID3[9]);

                //Console.WriteLine("Len (Bytes): {0:D}", (ulong)( (bytHeaderID3[6] << 21) | (bytHeaderID3[7] << 14) | (bytHeaderID3[8] << 7) | bytHeaderID3[9] ));
                //Console.WriteLine("NUMBER: {0:D}", (ulong)( (0x00<<21) | (0x00<<14) | (0x02<<7) | (0x01) ));
                //Console.WriteLine("OUTPUT: {0:X}, {1:X}, {2:X}, {3:X}", bytHeaderID3[6], bytHeaderID3[7], bytHeaderID3[8], bytHeaderID3[9]);

                Exists = true;

                //Experimental - Extended Header Implementation
                ExtendedHeader.setID3Version(MajorVersion);
                if (ExtendedHeader.Exists && MajorVersion > 2)
                {
                    byte[] EH_Size = new byte[4];
                    fs.Read(EH_Size, 0, 4);

                    if (MajorVersion == 3)
                    {
                        //Extended Header size, excluding itself; only 6 or 10 bytes (6 if no CRC Data, 10 if CRC Data [4 bytes for CRC])
                        ExtendedHeader.size = (int)ID3Base.expand(EH_Size[0], EH_Size[1], EH_Size[2], EH_Size[3]);
                        byte[] headerAndData = new byte[4 + ExtendedHeader.size];
                        fs.Position = lngStartOffset + 10;
                        fs.Read(headerAndData, 0, headerAndData.Length);
                        ExtendedHeader.setData(headerAndData);
                    }
                    else if (MajorVersion == 4)
                    {
                        //Whole Extended Header
                        ExtendedHeader.size = ID3Base.syncsafe(EH_Size[0], EH_Size[1], EH_Size[2], EH_Size[3]);
                        byte[] headerAndData = new byte[ExtendedHeader.size];
                        fs.Position = lngStartOffset + 10;
                        fs.Read(headerAndData, 0, headerAndData.Length);
                        ExtendedHeader.setData(headerAndData);
                    }
                }

                //ID3v2.2.X uses 3 letter identifiers versus the 4 letter identifiers in 2.3.X and 2.4.X
                if (!Compression)
                {
                    Console.WriteLine("ID3v2.{0:D} Detected...", MajorVersion);
                    int totalFrameSize = 0;

                    int AdditionalBytes = 10;
                    if (FooterExists)
                    {
                        AdditionalBytes = 20;
                    }
                    while (!PaddingExists && fs.Position < TagSize + AdditionalBytes) //(tag.TagSize + 10) == End Position (+10 for Original Header, +20 For Original Header and Footer [if present])
                    {
                        if (MajorVersion == 2)
                        {
                            byte[] TempHeader = new byte[6];
                            fs.Read(TempHeader, 0, 6);

                            if (TempHeader[0] == 0x00 && TempHeader[1] == 0x00 && TempHeader[2] == 0x00)
                            {
                                PaddingExists = true;
                            }
                            else
                            {
                                Console.WriteLine("HEADER[{0}{1}{2}]", (char)TempHeader[0], (char)TempHeader[1], (char)TempHeader[2]);

                                string FrameHeaderName = ((char)TempHeader[0]).ToString() +
                                                         ((char)TempHeader[1]).ToString() +
                                                         ((char)TempHeader[2]).ToString();

                                if (!ProperFrameNamePattern.IsMatch(FrameHeaderName))
                                {
                                    Console.WriteLine("Invalid ID3v2 Header");
                                    Valid = false;
                                    break;
                                }

                                if (Frames.ContainsKey(FrameHeaderName))
                                {
                                    Frames[FrameHeaderName].Add(new ID3v2Frame(FrameHeaderName, MajorVersion));
                                }
                                else
                                {
                                    List <ID3v2Frame> FrameList = new List <ID3v2Frame>();
                                    FrameList.Add(new ID3v2Frame(FrameHeaderName, MajorVersion));
                                    Frames.Add(FrameHeaderName, FrameList);
                                }

                                int        currentFrameIndex = Frames[FrameHeaderName].Count - 1;
                                ID3v2Frame currentFrame      = Frames[FrameHeaderName][currentFrameIndex];

                                currentFrame.getFrameFlags((byte)0x00, (byte)0x00, AllFramesUnsynced);

                                //Frame Size
                                currentFrame.FrameSize = (int)ID3Base.expand(TempHeader[3], TempHeader[4], TempHeader[5]); //(int)((TempHeader[3] << 16) | (TempHeader[4] << 8) | (TempHeader[5]));

                                totalFrameSize += currentFrame.FrameSize + 6;                                              //+6 for Frame Header

                                //Set FrameData
                                byte[] tempData = new byte[currentFrame.FrameSize];
                                fs.Read(tempData, 0, currentFrame.FrameSize);
                                currentFrame.getFrameData(tempData);
                            }
                        }
                        else
                        {
                            byte[] TempHeader = new byte[10];
                            fs.Read(TempHeader, 0, 10);

                            //All Tags must be 4 Characters (characters capital A-Z and 0-9; not null)
                            if (!FooterExists && (TempHeader[0] == 0x00 || TempHeader[1] == 0x00 || TempHeader[2] == 0x00 || TempHeader[3] == 0x00))
                            {
                                //Nothing Here but Padding; Skip to the end of the tag
                                //Footer and Padding are Mutually Exclusive
                                PaddingExists = true;
                            }
                            else
                            {
                                Console.WriteLine("HEADER[{0}{1}{2}{3}]", (char)TempHeader[0], (char)TempHeader[1], (char)TempHeader[2], (char)TempHeader[3]);

                                //Get the Frame Name
                                string FrameHeaderName = ((char)TempHeader[0]).ToString() +
                                                         ((char)TempHeader[1]).ToString() +
                                                         ((char)TempHeader[2]).ToString() +
                                                         ((char)TempHeader[3]).ToString();

                                if (!ProperFrameNamePattern.IsMatch(FrameHeaderName))
                                {
                                    Console.WriteLine("Invalid ID3v2 Header");
                                    Valid = false;
                                    break;
                                }

                                if (Frames.ContainsKey(FrameHeaderName))
                                {
                                    Frames[FrameHeaderName].Add(new ID3v2Frame(FrameHeaderName, MajorVersion));
                                }
                                else
                                {
                                    List <ID3v2Frame> FrameList = new List <ID3v2Frame>();
                                    FrameList.Add(new ID3v2Frame(FrameHeaderName, MajorVersion));
                                    Frames.Add(FrameHeaderName, FrameList);
                                }

                                //Keep Track of which count of the Frame we are working on (some frames may be in the tag more than once)
                                int        currentFrameIndex = Frames[FrameHeaderName].Count - 1;
                                ID3v2Frame currentFrame      = Frames[FrameHeaderName][currentFrameIndex];

                                currentFrame.getFrameFlags(TempHeader[8], TempHeader[9], AllFramesUnsynced);

                                //ID3v2.3 does not appear to use syncsafe numbers for frame sizes (2.4 does)
                                bool unsync = currentFrame.FF_Unsynchronisation;

                                if (MajorVersion > 3)
                                {
                                    currentFrame.FrameSize = ID3Base.syncsafe(TempHeader[4], TempHeader[5], TempHeader[6], TempHeader[7]);
                                }
                                else
                                {
                                    currentFrame.FrameSize = (int)ID3Base.expand(TempHeader[4], TempHeader[5], TempHeader[6], TempHeader[7]);//(int)((TempHeader[4] << 24) | (TempHeader[5] << 16) | (TempHeader[6] << 8) | (TempHeader[7]));
                                }

                                totalFrameSize += currentFrame.FrameSize + 10; //+10 for Frame Header

                                //Set FrameData
                                byte[] tempData = new byte[currentFrame.FrameSize];
                                fs.Read(tempData, 0, currentFrame.FrameSize);
                                currentFrame.getFrameData(tempData);
                            }
                        }
                    }
                    Console.WriteLine("Padding Exsists: {0}", PaddingExists.ToString());
                    Console.WriteLine("TagSize: {0:D}, TotalFrameSize: {1:D}", TagSize, totalFrameSize);
                    Console.WriteLine();
                }
            }

            ProcessFrames();

            fs.Position = fsOriginalPosition;
        }