Esempio n. 1
0
        /// <summary>
        /// Reads the raw data from a specified stream.
        /// </summary>
        /// <param name="stream">The stream to read from.</param>
        public void Read(Stream stream)
        {
            // Check for 'ID3' marker
            byte[] identifier = stream.Read(3);
            if (!(identifier[0] == 0x49 && identifier[1] == 0x44 && identifier[2] == 0x33))
            {
                return;
            }

            // Read the header
            _id3v2Header = new ID3v2Header(stream, false);

            TagReadingInfo tagReadingInfo = new TagReadingInfo(_id3v2Header.TagVersion);

            if (_id3v2Header.UsesUnsynchronization)
            {
                tagReadingInfo.TagVersionOptions = TagVersionOptions.Unsynchronized;
            }
            else
            {
                tagReadingInfo.TagVersionOptions = TagVersionOptions.None;
            }

            if (_id3v2Header.HasExtendedHeader)
            {
                _id3v2ExtendedHeader = new ID3v2ExtendedHeader(tagReadingInfo, stream);
            }

            int frameIDSize = (tagReadingInfo.TagVersion == ID3v2TagVersion.ID3v22 ? 3 : 4);
            int bytesRead;
            int readUntil;

            #region <<< ID3v2.4 - Guess if syncsafe frame size was used or not >>>

            if (_id3v2Header.TagVersion == ID3v2TagVersion.ID3v24)
            {
                bool isID3v24SyncSafe = true;

                bytesRead = 0;
                readUntil = _id3v2Header.TagSize - _id3v2ExtendedHeader.SizeIncludingSizeBytes - frameIDSize;
                long initialPosition = stream.Position;
                while (bytesRead < readUntil)
                {
                    byte[] frameIDBytes = stream.Read(frameIDSize);

                    // TODO: Noticed some tags contain 0x00 'E' 'N' as a FrameID.  Frame is well structured
                    // and other frames follow.  I believe the below (keep reading+looking) will cover this issue.

                    // If character is not a letter or number, padding reached, audio began,
                    // or otherwise the frame is not readable
                    if (frameIDBytes[0] < 0x30 || frameIDBytes[0] > 0x5A ||
                        frameIDBytes[1] < 0x30 || frameIDBytes[1] > 0x5A ||
                        frameIDBytes[2] < 0x30 || frameIDBytes[2] > 0x5A ||
                        frameIDBytes[3] < 0x30 || frameIDBytes[3] > 0x5A)
                    {
                        // TODO: Try to keep reading and look for a valid frame
                        if (frameIDBytes[0] != 0 && frameIDBytes[0] != 0xFF)
                        {
                            /*String msg = String.Format("Out of range FrameID - 0x{0:X}|0x{1:X}|0x{2:X}|0x{3:X}",
                             *                         tmpFrameIDBytes[0], tmpFrameIDBytes[1], tmpFrameIDBytes[2],
                             *                         tmpFrameIDBytes[3]);
                             * Trace.WriteLine(msg);*/
                        }

                        break;
                    }

                    int frameSize = stream.ReadInt32();
                    if (frameSize > 0xFF)
                    {
                        if ((frameSize & 0x80) == 0x80)
                        {
                            isID3v24SyncSafe = false; break;
                        }
                        if ((frameSize & 0x8000) == 0x8000)
                        {
                            isID3v24SyncSafe = false; break;
                        }
                        if ((frameSize & 0x800000) == 0x800000)
                        {
                            isID3v24SyncSafe = false; break;
                        }

                        if (bytesRead + frameSize + 10 == _id3v2Header.TagSize)
                        {
                            // Could give a false positive, but relatively unlikely (famous last words, huh?)
                            isID3v24SyncSafe = false;
                            break;
                        }
                        else
                        {
                            stream.Seek(-4, SeekOrigin.Current); // go back to read sync-safe version
                            int syncSafeFrameSize = ID3v2Utils.ReadInt32SyncSafe(stream);

                            long currentPosition = stream.Position;

                            bool isValidAtSyncSafe    = true;
                            bool isValidAtNonSyncSafe = true;

                            // TODO - if it's the last frame and there is padding, both would indicate false
                            // Use the one that returns some padding bytes opposed to bytes with non-zero values (could be frame data)

                            // If non sync-safe reads past the end of the tag, then it's sync safe
                            // Testing non-sync safe since it will always be bigger than the sync safe integer
                            if (currentPosition + frameSize + 2 >= readUntil)
                            {
                                isID3v24SyncSafe = true; break;
                            }

                            // Test non-sync safe
                            stream.Seek(currentPosition + frameSize + 2, SeekOrigin.Begin);
                            frameIDBytes = stream.Read(frameIDSize);
                            if (frameIDBytes[0] < 0x30 || frameIDBytes[0] > 0x5A ||
                                frameIDBytes[1] < 0x30 || frameIDBytes[1] > 0x5A ||
                                frameIDBytes[2] < 0x30 || frameIDBytes[2] > 0x5A ||
                                frameIDBytes[3] < 0x30 || frameIDBytes[3] > 0x5A)
                            {
                                isValidAtNonSyncSafe = false;
                            }

                            // Test sync-safe
                            stream.Seek(currentPosition + syncSafeFrameSize + 2, SeekOrigin.Begin);
                            frameIDBytes = stream.Read(frameIDSize);
                            if (frameIDBytes[0] < 0x30 || frameIDBytes[0] > 0x5A ||
                                frameIDBytes[1] < 0x30 || frameIDBytes[1] > 0x5A ||
                                frameIDBytes[2] < 0x30 || frameIDBytes[2] > 0x5A ||
                                frameIDBytes[3] < 0x30 || frameIDBytes[3] > 0x5A)
                            {
                                isValidAtSyncSafe = false;
                            }

                            // if they're equal, we'll just have to go with syncsafe, since that's the spec

                            if (isValidAtNonSyncSafe != isValidAtSyncSafe)
                            {
                                isID3v24SyncSafe = isValidAtSyncSafe;
                            }
                            break;
                        }
                    }

                    stream.Seek(frameSize + 2, SeekOrigin.Current);
                    bytesRead += frameSize + 10;
                }

                stream.Position = initialPosition;
                if (isID3v24SyncSafe == false)
                {
                    tagReadingInfo.TagVersionOptions |= TagVersionOptions.UseNonSyncSafeFrameSizeID3v24;
                }
            }
            else if (_id3v2Header.TagVersion == ID3v2TagVersion.ID3v22)
            {
                bool isID3v22CorrectSize = true;

                bytesRead = 0;
                readUntil = _id3v2Header.TagSize - _id3v2ExtendedHeader.SizeIncludingSizeBytes - frameIDSize;
                long initialPosition = stream.Position;

                stream.Read(frameIDSize);
                UnknownFrame unknownFrame = new UnknownFrame(null, tagReadingInfo, stream);
                bytesRead += unknownFrame.FrameHeader.FrameSizeTotal;
                if (bytesRead < readUntil)
                {
                    byte[] frameIDBytes = stream.Read(frameIDSize);

                    // TODO: Noticed some tags contain 0x00 'E' 'N' as a FrameID.  Frame is well structured
                    // and other frames follow.  I believe the below (keep reading+looking) will cover this issue.

                    // If character is not a letter or number, padding reached, audio began,
                    // or otherwise the frame is not readable
                    if (frameIDBytes[0] < 0x30 || frameIDBytes[0] > 0x5A)
                    {
                        if (frameIDBytes[1] >= 0x30 && frameIDBytes[1] <= 0x5A &&
                            frameIDBytes[2] >= 0x30 && frameIDBytes[2] <= 0x5A)
                        {
                            Trace.WriteLine("ID3v2.2 frame size off by 1 byte");
                            isID3v22CorrectSize = false;
                        }
                    }
                }

                stream.Position = initialPosition;
                if (isID3v22CorrectSize == false)
                {
                    tagReadingInfo.TagVersionOptions |= TagVersionOptions.AddOneByteToSize;
                }
            }

            #endregion <<< ID3v2.4 - Guess if syncsafe frame size was used or not >>>

            readUntil = _id3v2Header.TagSize - _id3v2ExtendedHeader.SizeIncludingSizeBytes - frameIDSize;
            Read(stream, _id3v2Header.TagVersion, tagReadingInfo, readUntil, frameIDSize);
        }
Esempio n. 2
0
 /// <summary>
 /// Initializes a new instance of the <see cref="ID3v2Tag"/> class.
 /// </summary>
 public ID3v2Tag()
 {
     _id3v2Header         = new ID3v2Header();
     _id3v2ExtendedHeader = new ID3v2ExtendedHeader();
 }