// public virtual void WriteToFile(string fileName, ID3v2MajorVersion version, EncodingScheme encoding) // { // FileStream targetFileStream = null; // try // { // // First write all frame data to a memory buffer // // This way we catch all fatal exceptions before // // touching the file, so there's not a even a slight // // chance of corrupting the file. // MemoryStream memoryBuffer = new MemoryStream(); // WriteFrames(memoryBuffer, version, encoding); // int newSize = (int)memoryBuffer.Length; // if (newSize == 0) // { // throw new FatalException("No data to write in the tag."); // } // targetFileStream = File.Open(fileName, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite); // TagHeader oldHeader = TagHeader.FromStream(targetFileStream); // bool useIntermediate = false; // if (oldHeader == null) // { // // There is no attached tag in the file. We need to use an intermediate temporary file. // useIntermediate = true; // // Rewind the place in stream to the very beginning, so that when // // we copy the data from the original file to the intermediate file, // // we won't lose any data. // targetFileStream.Seek(0, SeekOrigin.Begin); // } // else // { // // File already has an ID3 v2 tag attached. // if (oldHeader.TagSize < newSize) // { // useIntermediate = true; // // Allow for 4KB of padding. // newSize += 4 * 1024; // // Move to the correct place in stream so when we copy data to // // the intermediate file, we won't lose any data. // // The +10 is to go skip the tag header, since it's not included // // in TagHeader.TagSize // targetFileStream.Seek(oldHeader.TagSize + 10, SeekOrigin.Begin); // } // else // { // // We should write exactly the same number of bytes back to the file. // // When writing the padding, compare memoryBuffer.Length to newSize to // // calculate the number of padding bytes required to write. // newSize = oldHeader.TagSize; // // Seek the beginning of the file. The tag header and frame information // // will be overwritten. // targetFileStream.Seek(0, SeekOrigin.Begin); // } // } // TagHeader newHeader = new TagHeader((byte)version, 0, TagHeaderFlags.None, newSize); // if (useIntermediate) // { // string intermediateFileName = GetTempIntermediateFileName(fileName); // using (FileStream intermediateStream = File.Create(intermediateFileName)) // { // newHeader.WriteToStream(intermediateStream); // // Write the frame data residing in memory buffer. // intermediateStream.Write(memoryBuffer.GetBuffer(), 0, (int)memoryBuffer.Length); // //Write any required paddings. // for (int i = (int)memoryBuffer.Length; i < newSize; i++) // { // intermediateStream.WriteByte(0); // } // // Copy the data from the original file to the intermediate file. // CopyFromStreamToStream(targetFileStream, intermediateStream); // // Close the stream of original and intermediate file streams. // targetFileStream.Close(); // intermediateStream.Close(); // // If control reaches this point, then everything went file, so // // should normally delete the original file and rename the intermediate file. // // But as a safety measure, for pre-release, alpha, and beta version, // // instead of removing the file, I decided to rename the old file to // // fileName+".old."+revisionNumber instead. The user can manually delete // // the these files after ensuring the integrity of the new files. //#if ACHAMENES_ID3_BACKUP_FILES_BEFORE_MODIFICATION // File.Move(fileName, GetBackupFileName(fileName)); //#else // File.Delete(fileName); //#endif // File.Move(intermediateFileName, fileName); // } // } // else // If using an intermediate file is not necessary. // { // // Similarly, we would normally just start writing to @stream here, // // but instead, we close it, make a backup copy of it, and then // // open it again, and write the tag information just to ensure nothing // // will be lost. // targetFileStream.Close(); //#if ACHAMENES_ID3_BACKUP_FILES_BEFORE_MODIFICATION // File.Copy(fileName, GetBackupFileName(fileName)); //#endif // targetFileStream = File.Open(fileName, FileMode.Open, FileAccess.Write, FileShare.Write); // // Write the header. // newHeader.WriteToStream(targetFileStream); // // Write frame data from memory buffer. // targetFileStream.Write(memoryBuffer.GetBuffer(), 0, (int)memoryBuffer.Length); // // Write any required padding. // for (int i = (int)memoryBuffer.Length; i < newSize; i++) // { // targetFileStream.WriteByte(0); // } // // And finally close the stream. // targetFileStream.Close(); // } // } // finally // { // if (targetFileStream != null) // { // targetFileStream.Close(); // } // } // } private ID3v2MajorVersion Read(Stream stream, FrameParserFactory frameParserFactory) { TagHeader header = TagHeader.FromStream(stream); if (header == null) { throw new FatalException("No ID3 v2 tag is attached to this file."); } if (!Enum.IsDefined(typeof(ID3v2MajorVersion), header.MajorVersion)) { throw new FatalException("Reading this major version of ID3 v2 is not supported."); } if (header.Flags != TagHeaderFlags.None) { throw new FatalException("Reading tags with with any set flags are not supported in this version."); } long startingPosition = stream.Position; while (stream.Position - startingPosition < header.TagSize) { long beginPosition = stream.Position; Frame frame = null; string frameID = ""; try { frame = FrameParser.Parse(stream, (ID3v2MajorVersion)header.MajorVersion, frameParserFactory, out frameID); if (frame == null) { break; } } catch (NonFatalException ex) { RaiseReadingWarning(ex, frameID); } if (beginPosition == stream.Position) // Probably stuck in an infinite loop because of corrupt data in file. Exit the loop. { break; } if (frame != null) { this.Frames.Add(frame); } } return((ID3v2MajorVersion)header.MajorVersion); }
/// <summary> /// Reads the ID3 v2 header from a stream. /// </summary> /// <param name="stream">The stream to read the header from.</param> /// <returns> /// Returns a TagHeader object with the information /// in the header, null if no ID3 v2 tag was available. /// </returns> /// <remarks> /// The method returns null if it can not find a valid ID3 v2 /// tag in the file. This means that in case of a corrupt /// ID3 v2 header, (e.g. invalid tag size), null is returned. /// /// This method can therefore be used to check a file /// for the existence of a valid ID3 v2 tag. /// /// NOTE: Valid in this context does not mean of compatible version /// or having compatible flags. /// </remarks> /// <exception cref="ArgumentNullException"> /// The passed stream was null. /// </exception> /// <exception cref="System.IO.IOException"> /// There was an IO exception while trying to read the header. /// </exception> /// <exception cref="NotSupportedException"> /// The stream does not support reading. /// </exception> /// <exception cref="ObjectDisposedException"> /// The passed stream was closed before the method was called. /// </exception> public static TagHeader FromStream(System.IO.Stream stream) { if (stream == null) { throw new ArgumentNullException("The parameter 'stream' can not be null."); } byte[] data = new byte[10]; int read = stream.Read(data, 0, 10); if (read != 10) { return(null); } if (System.Text.Encoding.GetEncoding("ISO-8859-1").GetString(data, 0, 3) != "ID3") { return(null); } int size = 0; if (data[6] > 127 || data[7] > 127 || data[8] > 127 || data[9] > 127) // invalid size { return(null); } checked { try { size = data[6] * (1 << 21) + data[7] * (1 << 14) + data[8] * (1 << 7) + data[9]; } catch (OverflowException) // Invalid size in stream { return(null); } } TagHeader header = new TagHeader(data[3], data[4], (TagHeaderFlags)data[5], size); return(header); }