예제 #1
0
파일: ID3v2Tag.cs 프로젝트: Livila/stuffa
        /// <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);
        }
예제 #2
0
        public void Read(TagReadingInfo tagReadingInfo, ref Stream stream)
        {
            // TODO: Some tags have the length INCLUDE the extra ten bytes of the tag header.
            // Handle this (don't corrupt MP3 on rewrite)

            m_TagVersion = tagReadingInfo.TagVersion;

            bool usesUnsynchronization = ((tagReadingInfo.TagVersionOptions & TagVersionOptions.Unsynchronized) == TagVersionOptions.Unsynchronized);

            if (tagReadingInfo.TagVersion == ID3v2TagVersion.ID3v23)
            {
                if (!usesUnsynchronization)
                {
                    m_FrameSize = stream.ReadInt32();
                }
                else
                {
                    m_FrameSize = ID3v2Utils.ReadInt32Unsynchronized(stream);
                }

                m_FrameSizeExcludingAdditions = m_FrameSize;

                byte byte0 = stream.Read1();
                byte byte1 = stream.Read1();

                // First byte
                IsTagAlterPreservation  = ((byte0 & 0x80) == 0x80);
                IsFileAlterPreservation = ((byte0 & 0x40) == 0x40);
                IsReadOnly = ((byte0 & 0x20) == 0x20);

                // Second byte
                IsCompressed = ((byte1 & 0x80) == 0x80);
                bool tmpIsEncrypted        = ((byte1 & 0x40) == 0x40);
                bool tmpIsGroupingIdentity = ((byte1 & 0x20) == 0x20);

                // Additional bytes

                // Compression
                if (IsCompressed)
                {
                    DecompressedSize = stream.ReadInt32();
                    m_FrameSizeExcludingAdditions -= 4;
                }
                else
                {
                    DecompressedSize = 0;
                }

                // Encryption
                if (tmpIsEncrypted)
                {
                    EncryptionMethod = stream.Read1();
                    m_FrameSizeExcludingAdditions -= 1;
                }
                else
                {
                    EncryptionMethod = null;
                }

                // Grouping Identity
                if (tmpIsGroupingIdentity)
                {
                    GroupingIdentity = stream.Read1();
                    m_FrameSizeExcludingAdditions -= 1;
                }
                else
                {
                    GroupingIdentity = null;
                }

                if (usesUnsynchronization)
                {
                    stream = ID3v2Utils.ReadUnsynchronizedStream(stream, m_FrameSize);
                }
            }
            else if (tagReadingInfo.TagVersion == ID3v2TagVersion.ID3v22)
            {
                if (!usesUnsynchronization)
                {
                    m_FrameSize = stream.ReadInt24();
                }
                else
                {
                    m_FrameSize = ID3v2Utils.ReadInt24Unsynchronized(stream);
                }

                if ((tagReadingInfo.TagVersionOptions & TagVersionOptions.AddOneByteToSize) == TagVersionOptions.AddOneByteToSize)
                {
                    m_FrameSize++;
                }
                m_FrameSizeExcludingAdditions = m_FrameSize;

                // These fields are not supported in ID3v2.2
                IsTagAlterPreservation  = false;
                IsFileAlterPreservation = false;
                IsReadOnly       = false;
                IsCompressed     = false;
                DecompressedSize = 0;
                EncryptionMethod = null;
                GroupingIdentity = null;

                if (usesUnsynchronization)
                {
                    stream = ID3v2Utils.ReadUnsynchronizedStream(stream, m_FrameSize);
                }
            }
            else if (tagReadingInfo.TagVersion == ID3v2TagVersion.ID3v24)
            {
                if ((tagReadingInfo.TagVersionOptions & TagVersionOptions.UseNonSyncSafeFrameSizeID3v24) == TagVersionOptions.UseNonSyncSafeFrameSizeID3v24)
                {
                    m_FrameSize = stream.ReadInt32();
                }
                else
                {
                    m_FrameSize = ID3v2Utils.ReadInt32SyncSafe(stream);
                }

                m_FrameSizeExcludingAdditions = m_FrameSize;

                byte byte0 = stream.Read1();
                byte byte1 = stream.Read1();

                bool hasDataLengthIndicator = ((byte1 & 0x01) == 0x01);
                usesUnsynchronization = ((byte1 & 0x03) == 0x03);
                if (hasDataLengthIndicator)
                {
                    m_FrameSizeExcludingAdditions -= 4;
                    stream.Seek(4, SeekOrigin.Current); // skip data length indicator
                }

                if (usesUnsynchronization)
                {
                    stream = ID3v2Utils.ReadUnsynchronizedStream(stream, m_FrameSize);
                }

                // TODO - finish parsing
            }

            if (IsCompressed)
            {
                stream           = ID3v2Utils.DecompressFrame(stream, FrameSizeExcludingAdditions);
                IsCompressed     = false;
                DecompressedSize = 0;
                m_FrameSizeExcludingAdditions = (int)stream.Length;
            }
        }