Пример #1
0
        public APEv2Tag(FileStream fs, ID3v1Tag id3v1, Lyrics3Tag lyrics3, bool afterAudioData)
        {
            Appended = false;
            Exists   = false;
            Valid    = false;

            long lngOffset = 32; //Size of footer tag of APEv2

            if (id3v1.Exists)
            {
                lngOffset += id3v1.TagSize; //128 Bytes for ID3v1
            }
            if (lyrics3.Exists)
            {
                lngOffset += lyrics3.TagSize;
            }

            RawFooter   = new byte[32];
            fs.Position = fs.Length - lngOffset;
            fs.Read(RawFooter, 0, 32);

            if (Encoding.ASCII.GetString(RawFooter, 0, 8) == "APETAGEX")
            {
                Exists   = true;
                Appended = true;

                //Version
                uint v = ID3Base.expand(RawFooter[11], RawFooter[10], RawFooter[9], RawFooter[8]);

                TagSize = ID3Base.expand(RawFooter[15], RawFooter[14], RawFooter[13], RawFooter[12]);

                uint numOfItems = ID3Base.expand(RawFooter[19], RawFooter[18], RawFooter[17], RawFooter[16]);

                //Detect Header
                fs.Position = fs.Length - (lngOffset + TagSize);
                RawHeader   = new byte[32];
                fs.Read(RawHeader, 0, 32);
                if (Encoding.ASCII.GetString(RawHeader, 0, 8) == "APETAGEX")
                {
                    //WERE GOOD
                    ProcessFlags();
                }
            }
        }
        public void ProcessFrame()
        {
            byte[] NewFrameData;

            if (FF_Unsynchronisation)
            {
                NewFrameData = ID3Base.UnSync(FrameData);
            }
            else
            {
                NewFrameData = FrameData;
            }

            //T-Type Frame (Text)
            if (FrameName.ToCharArray()[0] == 'T' && FrameName != "TXXX" && FrameName != "TXX")
            {
                if (NewFrameData.Length > 1)
                {
                    System.Text.Encoding enc = null;
                    bool useBOM = GetEncodingType(ref enc, 1);

                    if (useBOM)
                    {
                        Data = enc.GetString(NewFrameData, 1 + enc.GetPreamble().Length, NewFrameData.Length - (1 + enc.GetPreamble().Length));
                    }
                    else
                    {
                        Data = enc.GetString(NewFrameData, 1, NewFrameData.Length - 1);
                    }

                    Data = ((string)Data).Trim('\0').Trim();
                }
                else
                {
                    //No Frame Data, so blank.
                    Console.WriteLine("The Frame Above Had No Data");
                    Data = "";
                }
            }
            else if (FrameName == "TXXX" || FrameName == "TXX")
            {
                //TXXX, TXX Frame Goes Here (TODO)
            }

            //W-Type Frame (TODO: Needs more testing)
            if (FrameName.ToCharArray()[0] == 'W' && FrameName != "WXXX" && FrameName != "WXX")
            {
                Encoding enc = Encoding.ASCII;
                Data = enc.GetString(NewFrameData, 0, NewFrameData.Length);
            }
            else if (FrameName == "WXXX" || FrameName == "WXX")
            {
                //WXXX, WXX is always ISO-8859-1 Encoded
                Encoding enc = Encoding.GetEncoding("ISO-8859-1");
                Data = enc.GetString(NewFrameData, 0, NewFrameData.Length);
            }

            if (FrameName == "APIC" || FrameName == "PIC")
            {
                if (NewFrameData.Length > 1)
                {
                    int            DataPosition = 0;
                    ID3v2APICFrame apic         = new ID3v2APICFrame();

                    //Skip just Text Encoding
                    DataPosition++;

                    //Get MimeType (as Generic Text)
                    Encoding enc = Encoding.ASCII;
                    if (MajorVersion > 2)
                    {
                        int BeginMimeType = DataPosition;
                        while (NewFrameData[DataPosition] != 0x00)
                        {
                            DataPosition++;
                        }
                        apic.MIMEType = enc.GetString(NewFrameData, BeginMimeType, DataPosition - BeginMimeType);
                    }
                    else
                    {
                        apic.MIMEType = enc.GetString(NewFrameData, DataPosition, 3);
                        DataPosition += 2; //Should be Increment by 3, but next instruction is to increment by 1
                    }

                    //Get ImageType
                    DataPosition++;
                    apic.ImageType = NewFrameData[DataPosition];

                    //Get Description
                    DataPosition++;
                    bool useBOM = GetEncodingType(ref enc, DataPosition); //Determine what encoding style we need

                    int BeginDescription = DataPosition;
                    if (DataPosition + 1 < NewFrameData.Length && enc == Encoding.Unicode)  //Little Endian, Every Two Bytes (16bits)
                    {
                        while (!(NewFrameData[DataPosition] == 0x00 && NewFrameData[DataPosition + 1] == 0x00))
                        {
                            DataPosition += 2;
                        }

                        //Skip Past $00 00 at End
                        DataPosition += 2;
                    }
                    else
                    {
                        while (NewFrameData[DataPosition] != 0x00)
                        {
                            DataPosition++;
                        }

                        //Skip Past $00 at End
                        DataPosition++;
                    }

                    if (!useBOM)
                    {
                        apic.Description = enc.GetString(NewFrameData, BeginDescription, DataPosition - BeginDescription);
                    }
                    else
                    {
                        apic.Description = enc.GetString(NewFrameData, BeginDescription + enc.GetPreamble().Length, (DataPosition - (BeginDescription + enc.GetPreamble().Length)));
                    }
                    apic.Description = apic.Description.Trim('\0').Trim();

                    //Get Binary Data
                    MemoryStream ms = new MemoryStream(NewFrameData, DataPosition, NewFrameData.Length - DataPosition);
                    try
                    {
                        apic.Picture = Image.FromStream(ms);
                    }
                    catch (System.ArgumentException ex)
                    {
                        apic.Picture = null;
                        Console.WriteLine(ex.Message);
                    }

                    Data = apic;
                }
            }
        }
        public VBRHeader(FileStream fs, long startOffset)
        {
            fs.Position = startOffset;
            byte[] inputheader = new byte[8]; //4 for Xing or Info, 4 for flags
            fs.Read(inputheader, 0, 8);

            // If it's a variable bitrate MP3, the first 4 bytes will read 'Xing'
            // since they're the ones who added variable bitrate-edness to MP3s
            string HeaderType = Encoding.ASCII.GetString(inputheader, 0, 4);

            if (HeaderType == "Xing" || HeaderType == "Info") //(char)inputheader[0] == 'X' && (char)inputheader[1] == 'i' && (char)inputheader[2] == 'n' && (char)inputheader[3] == 'g')
            {
                VBRType  = HeaderType;
                Exists   = true;
                Position = startOffset;

                int flags = (int)ID3Base.expand(inputheader[4], inputheader[5], inputheader[6], inputheader[7]);//(int)(((inputheader[4] & 255) << 24) | ((inputheader[5] & 255) << 16) | ((inputheader[6] & 255) <<  8) | ((inputheader[7] & 255)));

                /**
                 * Flags:
                 * Frames
                 * Bytes
                 * TOC
                 * Scale
                 */

                framesFlag   = ((flags & 0x01) == 0x01);   // total bit stream frames from Xing header data
                bytesFlag    = ((flags & 0x02) == 0x02);   // total bit stream bytes from Xing header data
                tocFlag      = ((flags & 0x04) == 0x04);
                vbrScaleFlag = ((flags & 0x08) == 0x08);   // encoded vbr scale from Xing header data

                int byteCount = 0;
                if (framesFlag)
                {
                    byteCount += 4;
                }
                if (bytesFlag)
                {
                    byteCount += 4;
                }
                if (tocFlag)
                {
                    byteCount += 100;
                }
                if (vbrScaleFlag)
                {
                    byteCount += 4;
                }

                byte[] inputheaderData = new byte[byteCount];
                fs.Read(inputheaderData, 0, byteCount);


                int pos = 0;
                if (framesFlag)
                {
                    intVFrames = (int)ID3Base.expand(inputheaderData[pos], inputheaderData[pos + 1], inputheaderData[pos + 2], inputheaderData[pos + 3]);//(int)(((inputheader[8] & 255) << 24) | ((inputheader[9] & 255) << 16) | ((inputheader[10] & 255) <<  8) | ((inputheader[11] & 255)));
                    pos       += 4;
                }
                else
                {
                    intVFrames = -1;
                }

                if (bytesFlag)
                {
                    file_bytes = (int)ID3Base.expand(inputheaderData[pos], inputheaderData[pos + 1], inputheaderData[pos + 2], inputheaderData[pos + 3]);
                    pos       += 4;
                }

                if (tocFlag)
                {
                    TOC = new byte[100];
                    for (int i = 0; i < 100; ++i)
                    {
                        TOC[i] = inputheaderData[pos];
                        pos++;
                    }
                }

                int vbrScale = -1;
                if (vbrScaleFlag)
                {
                    vbrScale = (int)ID3Base.expand(inputheaderData[pos], inputheaderData[pos + 1], inputheaderData[pos + 2], inputheaderData[pos + 3]);
                    pos     += 4;
                }

                HeaderSize = pos + 8; //+8 for original 8 bytes ["Xing" + Flags]

                if (HeaderType == "Xing")
                {
                    //VBR Indeed
                    bVBR = true;

                    SeekPoint(100);
                }
                else
                {
                    //Header is 'Info' which means CBR
                    bVBR = false;
                }
            }
        }
        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;
            }
        }
Пример #5
0
        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;
        }