예제 #1
0
        private void ReadTag_FLAC(Stream stream, InternalInfo info)
        {
            info.FileType = FileType.Flac;

            // Skip "fLaC" marker
            stream.Seek(4, SeekOrigin.Current);

            bool isLastMetaDataBlock;

            List <FlacMetaDataBlock> metaDataBlockList = new List <FlacMetaDataBlock>();

            do
            {
                int c = stream.ReadByte();
                isLastMetaDataBlock = (((c >> 7) & 0x01) == 1);

                int blocksize = stream.ReadInt24();

                FlacMetaDataBlockType blockType = (FlacMetaDataBlockType)(c & 0x07);

                if (blockType == FlacMetaDataBlockType.VorbisComment) // Vorbis comment
                {
                    info.OrigVorbisCommentSize = blocksize;
                    long mvcoffset = stream.Position - 4;
                    ReadTag_VorbisComment(stream);
                    stream.Seek(mvcoffset + 4, SeekOrigin.Begin);
                }

                FlacMetaDataBlock metaDataBlock = new FlacMetaDataBlock(blockType);
                metaDataBlock.SetBlockData(stream, blocksize);
                metaDataBlockList.Add(metaDataBlock);
            } while (isLastMetaDataBlock == false);

            info.MetaDataBlockList = metaDataBlockList;
            _metaDataBlockList     = metaDataBlockList;
        }
예제 #2
0
        private void WriteTagFlac(string path, InternalInfo targetFile)
        {
            // This will store the metadata blocks we're actually going to write
            List <FlacMetaDataBlock> myMetaDataBlocks = new List <FlacMetaDataBlock>();

            // Get byte array of new vorbis comment block
            byte[] newTagArray;
            using (MemoryStream newTag = new MemoryStream())
            {
                // Write vendor
                byte[] vendorBytes = Encoding.UTF8.GetBytes(_vendor);
                newTag.WriteInt32LittleEndian(vendorBytes.Length);
                newTag.Write(vendorBytes);

                // Remove dead items and replace commonly misnamed items
                foreach (NameValueItem item in new List <NameValueItem>(_items))
                {
                    if (string.IsNullOrEmpty(item.Value))
                    {
                        _items.Remove(item);
                    }
                    else if (string.Compare(item.Name, "YEAR", true) == 0)
                    {
                        if (string.IsNullOrEmpty(_items.GetValue("DATE")))
                        {
                            _items.SetValue("DATE", item.Value);
                        }
                        _items.Remove(item);
                    }
                }

                // Write item count
                newTag.WriteInt32LittleEndian(_items.Count);

                // Write items
                foreach (NameValueItem item in _items)
                {
                    if (string.IsNullOrEmpty(item.Value))
                    {
                        continue;
                    }

                    byte[] keyBytes   = Encoding.ASCII.GetBytes(item.Name);
                    byte[] valueBytes = Encoding.UTF8.GetBytes(item.Value);

                    newTag.WriteInt32LittleEndian(keyBytes.Length + 1 + valueBytes.Length);
                    newTag.Write(keyBytes);
                    newTag.WriteByte((byte)'=');
                    newTag.Write(valueBytes);
                }

                newTagArray = newTag.ToArray();
            }

            // 1. Get old size of metadata blocks.
            // 2. Find StreamInfo, SeekTable, and Padding blocks.
            //    These blocks should occur only once.  If not, an exception is thrown.  The padding
            //    block we don't really care about so no exception is thrown if it's duplicated.
            FlacMetaDataBlock paddingBlock    = null;
            FlacMetaDataBlock streamInfoBlock = null;
            FlacMetaDataBlock seekTableBlock  = null;
            long origMetaDataSize             = 0;

            foreach (FlacMetaDataBlock metaDataBlock in targetFile.MetaDataBlockList)
            {
                origMetaDataSize += 4; // Identifier + Size
                origMetaDataSize += metaDataBlock.Size;

                if (metaDataBlock.BlockType == FlacMetaDataBlockType.Padding)
                {
                    paddingBlock = metaDataBlock;
                }
                else if (metaDataBlock.BlockType == FlacMetaDataBlockType.StreamInfo)
                {
                    if (streamInfoBlock != null)
                    {
                        throw new InvalidDataException("Multiple stream info blocks");
                    }
                    streamInfoBlock = metaDataBlock;
                }
                else if (metaDataBlock.BlockType == FlacMetaDataBlockType.SeekTable)
                {
                    if (seekTableBlock != null)
                    {
                        throw new InvalidDataException("Multiple seek tables");
                    }
                    seekTableBlock = metaDataBlock;
                }
            }

            // No Padding block found, create one
            if (paddingBlock == null)
            {
                paddingBlock = new FlacMetaDataBlock(FlacMetaDataBlockType.Padding);
                paddingBlock.SetBlockDataZeroed(2000);
            }
            // Padding block found, adjust size
            else
            {
                // TODO: This is not entirely accurate, since we may be reading from one file
                // and writing to another.  The other blocks need to be accounted for, however for
                // same file read/write this works.  Not high priority.
                int adjustPadding = targetFile.OrigVorbisCommentSize - newTagArray.Length;
                int newSize       = paddingBlock.Size + adjustPadding;
                if (newSize < 10)
                {
                    paddingBlock.SetBlockDataZeroed(2000);
                }
                else
                {
                    paddingBlock.SetBlockDataZeroed(newSize);
                }
            }

            // Set Vorbis-Comment block data
            FlacMetaDataBlock vorbisCommentBlock = new FlacMetaDataBlock(FlacMetaDataBlockType.VorbisComment);

            vorbisCommentBlock.SetBlockData(newTagArray);

            // Create list of blocks to write
            myMetaDataBlocks.Add(streamInfoBlock); // StreamInfo MUST be first
            if (seekTableBlock != null)
            {
                myMetaDataBlocks.Add(seekTableBlock);
            }

            // Add other blocks we read from the original file.
            foreach (FlacMetaDataBlock metaDataBlock in _metaDataBlockList)
            {
                if (metaDataBlock.BlockType == FlacMetaDataBlockType.Application ||
                    metaDataBlock.BlockType == FlacMetaDataBlockType.CueSheet ||
                    metaDataBlock.BlockType == FlacMetaDataBlockType.Picture)
                {
                    myMetaDataBlocks.Add(metaDataBlock);
                }
            }

            // Add our new vorbis comment and padding blocks
            myMetaDataBlocks.Add(vorbisCommentBlock);
            myMetaDataBlocks.Add(paddingBlock);

            // Get new size of metadata blocks
            long newMetaDataSize = 0;

            foreach (FlacMetaDataBlock metaDataBlock in myMetaDataBlocks)
            {
                newMetaDataSize += 4; // Identifier + Size
                newMetaDataSize += metaDataBlock.Size;
            }

            // If the new metadata size is less than the original, increase the padding
            if (newMetaDataSize != origMetaDataSize)
            {
                int newPaddingSize = paddingBlock.Size + (int)(origMetaDataSize - newMetaDataSize);
                if (newPaddingSize > 0)
                {
                    paddingBlock.SetBlockDataZeroed(newPaddingSize);

                    // Get new size of metadata blocks
                    newMetaDataSize = 0;
                    foreach (FlacMetaDataBlock metaDataBlock in myMetaDataBlocks)
                    {
                        newMetaDataSize += 4; // Identifier + Size
                        newMetaDataSize += metaDataBlock.Size;
                    }
                }
            }

            string tempFilename = null;

            // no rewrite necessary
            if (newMetaDataSize == origMetaDataSize)
            {
                //
            }
            // rewrite necessary - grab a snickers.
            else if (newMetaDataSize > origMetaDataSize)
            {
                // rename

                tempFilename = PathUtils.GetTemporaryFileNameBasedOnFileName(path);
                File.Move(path, tempFilename);

                // open for read, open for write
                using (FileStream fsRead = File.Open(tempFilename, FileMode.Open, FileAccess.Read, FileShare.None))
                    using (FileStream fsWrite = File.Open(path, FileMode.CreateNew, FileAccess.Write, FileShare.None))
                    {
                        // copy ID3v2 tag.. technically there shouldn't be one, but we don't want to destroy data
                        int tmpID3v2TagSize = ID3v2.ID3v2Tag.GetTagSize(fsRead);
                        if (tmpID3v2TagSize != 0)
                        {
                            byte[] id3v2 = fsRead.Read(tmpID3v2TagSize);
                            fsWrite.Write(id3v2);
                        }

                        fsWrite.Write(FLAC_MARKER);
                        // create blankspace
                        byte[] blankSpace = new Byte[newMetaDataSize];
                        fsWrite.Write(blankSpace);

                        fsRead.Seek(4 + origMetaDataSize, SeekOrigin.Current);

                        byte[] buf       = new byte[32768];
                        int    bytesRead = fsRead.Read(buf, 0, 32768);
                        while (bytesRead != 0)
                        {
                            fsWrite.Write(buf, 0, bytesRead);
                            bytesRead = fsRead.Read(buf, 0, 32768);
                        }
                    }
            }
            // newMetaDataSize < origMetaDataSize is an error
            else
            {
                throw new Exception(String.Format("Internal Error: newMetaDataSize ({0}) < origMetaDataSize ({1})", newMetaDataSize, origMetaDataSize));
            }

            using (FileStream fs = File.Open(path, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
            {
                // skip fLaC marker and ID3v2 tag size
                int tmpID3v2TagSize = ID3v2.ID3v2Tag.GetTagSize(fs);
                fs.Position = tmpID3v2TagSize + 4;

                byte blockType;

                foreach (FlacMetaDataBlock metaDataBlock in myMetaDataBlocks)
                {
                    // always write padding last
                    if (metaDataBlock == paddingBlock)
                    {
                        continue;
                    }

                    blockType = (byte)metaDataBlock.BlockType;
                    fs.WriteByte(blockType);
                    fs.WriteInt24(metaDataBlock.Size);
                    fs.Write(metaDataBlock.BlockData);
                }

                // write padding, add stop bit to block type
                blockType = (byte)(paddingBlock.BlockType + 0x80);
                fs.WriteByte(blockType);
                fs.WriteInt24(paddingBlock.Size);
                fs.Write(paddingBlock.BlockData);
            }

            if (!string.IsNullOrEmpty(tempFilename))
            {
                File.Delete(tempFilename);
            }
        }