private void init(BinaryWriter w) { w.Write((ulong)10); // 1st size descriptor; position 0 w.Write((byte)2); // Counter; position 8 w.Write((uint)5); // 1st sub-size descriptor; position 9 w.Write(new byte[5] { 1, 2, 3, 4, 5 }); // Data chunk 1; position 13 w.Write((uint)5); // 2nd sub-size descriptor; position 18 w.Write(new byte[5] { 6, 7, 8, 9, 10 }); // Data chunk 2; position 22 structureHelper.AddZone(13, 5, "zone1"); structureHelper.AddSize(0, (ulong)10, "zone1"); structureHelper.AddCounter(8, (byte)2, "zone1"); structureHelper.AddSize(9, (uint)5, "zone1"); structureHelper.AddZone(22, 5, "zone2"); structureHelper.AddSize(0, (ulong)10, "zone2"); structureHelper.AddCounter(8, (byte)2, "zone2"); structureHelper.AddSize(18, (uint)5, "zone2"); structureHelper.AddZone(27, 0, "zone3"); structureHelper.AddSize(0, (ulong)10, "zone3"); structureHelper.AddCounter(8, (byte)2, "zone3"); }
public bool Write(BinaryReader r, BinaryWriter w, TagData tag) { bool result = true; // Constraint-check on non-supported values if (FieldCodeFixedLength > 0) { if (tag.Pictures != null) { foreach (PictureInfo picInfo in tag.Pictures) { if (PictureInfo.PIC_TYPE.Unsupported.Equals(picInfo.PicType) && (picInfo.TagType.Equals(getImplementedTagType()))) { if ((-1 == picInfo.NativePicCode) && (Utils.ProtectValue(picInfo.NativePicCodeStr).Length != FieldCodeFixedLength)) { throw new NotSupportedException("Field code fixed length is " + FieldCodeFixedLength + "; detected field '" + Utils.ProtectValue(picInfo.NativePicCodeStr) + "' is " + Utils.ProtectValue(picInfo.NativePicCodeStr).Length + " characters long and cannot be written"); } } } } foreach (MetaFieldInfo fieldInfo in tag.AdditionalFields) { if (fieldInfo.TagType.Equals(getImplementedTagType()) || MetaDataIOFactory.TAG_ANY == fieldInfo.TagType) { string fieldCode = Utils.ProtectValue(fieldInfo.NativeFieldCode); if (fieldCode.Length != FieldCodeFixedLength && !fieldCode.Contains("----")) // "----" = exception for MP4 extended fields (e.g. ----:com.apple.iTunes:CONDUCTOR) { throw new NotSupportedException("Field code fixed length is " + FieldCodeFixedLength + "; detected field '" + fieldCode + "' is " + fieldCode.Length + " characters long and cannot be written"); } } } } structureHelper.Clear(); tagData.Pictures.Clear(); // Read all the fields in the existing tag (including unsupported fields) ReadTagParams readTagParams = new ReadTagParams(true, true); readTagParams.PrepareForWriting = true; if (embedder != null && embedder.HasEmbeddedID3v2 > 0) { readTagParams.offset = embedder.HasEmbeddedID3v2; } this.read(r, readTagParams); if (embedder != null && getImplementedTagType() == MetaDataIOFactory.TAG_ID3V2) { structureHelper.Clear(); structureHelper.AddZone(embedder.Id3v2Zone); } // Give engine something to work with if the tag is really empty if (!tagExists && 0 == Zones.Count) { structureHelper.AddZone(0, 0); } TagData dataToWrite; dataToWrite = tagData; dataToWrite.IntegrateValues(tag); // Merge existing information + new tag information dataToWrite.Cleanup(); FileSurgeon surgeon = new FileSurgeon(structureHelper, embedder, getImplementedTagType(), getDefaultTagOffset()); result = surgeon.RewriteZones(w, new FileSurgeon.WriteDelegate(writeAdapter), Zones, dataToWrite, tagExists); // Update tag information without calling Read /* TODO - this implementation is too risky : * - if one of the writing operations fails, data is updated as if everything went right * - any picture slot with a markForDeletion flag is recorded as-is in the tag */ tagData = dataToWrite; return(result); }
// ---------- SUPPORT METHODS private bool readWAV(Stream source, ReadTagParams readTagParams) { bool result = true; uint riffChunkSize; long riffChunkSizePos; byte[] data = new byte[4]; source.Seek(0, SeekOrigin.Begin); // Read header source.Read(data, 0, 4); string str = Utils.Latin1Encoding.GetString(data); if (str.Equals(HEADER_RIFF)) { _isLittleEndian = true; } else if (str.Equals(HEADER_RIFX)) { _isLittleEndian = false; } else { return(false); } // Force creation of FileStructureHelper with detected endianness structureHelper = new FileStructureHelper(isLittleEndian); id3v2StructureHelper = new FileStructureHelper(isLittleEndian); riffChunkSizePos = source.Position; source.Read(data, 0, 4); if (isLittleEndian) { riffChunkSize = StreamUtils.DecodeUInt32(data); } else { riffChunkSize = StreamUtils.DecodeBEUInt32(data); } // Format code source.Read(data, 0, 4); str = Utils.Latin1Encoding.GetString(data); if (!str.Equals(FORMAT_WAVE)) { return(false); } string subChunkId; uint chunkSize; long chunkDataPos; bool foundSample = false; bool foundBext = false; bool foundInfo = false; bool foundIXml = false; // Sub-chunks loop while (source.Position < riffChunkSize + 8) { // Chunk ID source.Read(data, 0, 4); if (0 == data[0]) // Sometimes data segment ends with a parasite null byte { source.Seek(-3, SeekOrigin.Current); source.Read(data, 0, 4); } subChunkId = Utils.Latin1Encoding.GetString(data); // Chunk size source.Read(data, 0, 4); if (isLittleEndian) { chunkSize = StreamUtils.DecodeUInt32(data); } else { chunkSize = StreamUtils.DecodeBEUInt32(data); } chunkDataPos = source.Position; if (subChunkId.Equals(CHUNK_FORMAT)) { source.Read(data, 0, 2); if (isLittleEndian) { formatId = StreamUtils.DecodeUInt16(data); } else { formatId = StreamUtils.DecodeBEUInt16(data); } source.Read(data, 0, 2); if (isLittleEndian) { channelsArrangement = ChannelsArrangements.GuessFromChannelNumber(StreamUtils.DecodeUInt16(data)); } else { channelsArrangement = ChannelsArrangements.GuessFromChannelNumber(StreamUtils.DecodeBEUInt16(data)); } source.Read(data, 0, 4); if (isLittleEndian) { sampleRate = StreamUtils.DecodeUInt32(data); } else { sampleRate = StreamUtils.DecodeBEUInt32(data); } source.Read(data, 0, 4); if (isLittleEndian) { bytesPerSecond = StreamUtils.DecodeUInt32(data); } else { bytesPerSecond = StreamUtils.DecodeBEUInt32(data); } source.Seek(2, SeekOrigin.Current); // BlockAlign source.Read(data, 0, 2); if (isLittleEndian) { bitsPerSample = StreamUtils.DecodeUInt16(data); } else { bitsPerSample = StreamUtils.DecodeBEUInt16(data); } } else if (subChunkId.Equals(CHUNK_DATA)) { headerSize = riffChunkSize - chunkSize; } else if (subChunkId.Equals(CHUNK_FACT)) { source.Read(data, 0, 4); if (isLittleEndian) { sampleNumber = StreamUtils.DecodeInt32(data); } else { sampleNumber = StreamUtils.DecodeBEInt32(data); } } else if (subChunkId.Equals(CHUNK_SAMPLE)) { structureHelper.AddZone(source.Position - 8, (int)(chunkSize + 8), subChunkId); structureHelper.AddSize(riffChunkSizePos, riffChunkSize, subChunkId); foundSample = true; tagExists = true; SampleTag.FromStream(source, this, readTagParams); } else if (subChunkId.Equals(CHUNK_BEXT)) { structureHelper.AddZone(source.Position - 8, (int)(chunkSize + 8), subChunkId); structureHelper.AddSize(riffChunkSizePos, riffChunkSize, subChunkId); foundBext = true; tagExists = true; BextTag.FromStream(source, this, readTagParams); } else if (subChunkId.Equals(CHUNK_INFO)) { // Purpose of the list should be INFO source.Read(data, 0, 4); string purpose = Utils.Latin1Encoding.GetString(data, 0, 4); if (purpose.Equals(InfoTag.PURPOSE_INFO)) { structureHelper.AddZone(source.Position - 12, (int)(chunkSize + 8), subChunkId); structureHelper.AddSize(riffChunkSizePos, riffChunkSize, subChunkId); foundInfo = true; tagExists = true; InfoTag.FromStream(source, this, readTagParams, chunkSize); } } else if (subChunkId.Equals(CHUNK_IXML)) { structureHelper.AddZone(source.Position - 8, (int)(chunkSize + 8), subChunkId); structureHelper.AddSize(riffChunkSizePos, riffChunkSize, subChunkId); foundIXml = true; tagExists = true; IXmlTag.FromStream(source, this, readTagParams, chunkSize); } else if (subChunkId.Equals(CHUNK_ID3)) { id3v2Offset = source.Position; // Zone is already added by Id3v2.Read id3v2StructureHelper.AddZone(id3v2Offset - 8, (int)(chunkSize + 8), subChunkId); id3v2StructureHelper.AddSize(riffChunkSizePos, riffChunkSize, subChunkId); } source.Seek(chunkDataPos + chunkSize, SeekOrigin.Begin); } // Add zone placeholders for future tag writing if (readTagParams.PrepareForWriting) { if (!foundSample) { structureHelper.AddZone(source.Position, 0, CHUNK_SAMPLE); structureHelper.AddSize(riffChunkSizePos, riffChunkSize, CHUNK_SAMPLE); } if (!foundBext) { structureHelper.AddZone(source.Position, 0, CHUNK_BEXT); structureHelper.AddSize(riffChunkSizePos, riffChunkSize, CHUNK_BEXT); } if (!foundInfo) { structureHelper.AddZone(source.Position, 0, CHUNK_INFO); structureHelper.AddSize(riffChunkSizePos, riffChunkSize, CHUNK_INFO); } if (!foundIXml) { structureHelper.AddZone(source.Position, 0, CHUNK_IXML); structureHelper.AddSize(riffChunkSizePos, riffChunkSize, CHUNK_IXML); } } // ID3 zone should be set as the very last one for Windows to be able to read the LIST INFO zone properly if (-1 == id3v2Offset) { id3v2Offset = 0; // Switch status to "tried to read, but nothing found" if (readTagParams.PrepareForWriting) { id3v2StructureHelper.AddZone(source.Position, 0, CHUNK_ID3); id3v2StructureHelper.AddSize(riffChunkSizePos, riffChunkSize, CHUNK_ID3); } } return(result); }
public bool Read(BinaryReader source, AudioDataManager.SizeInfo sizeInfo, MetaDataIO.ReadTagParams readTagParams) { this.sizeInfo = sizeInfo; bool result = false; resetData(); source.BaseStream.Seek(0, SeekOrigin.Begin); if (DSD_ID.Equals(Utils.Latin1Encoding.GetString(source.ReadBytes(4)))) { source.BaseStream.Seek(16, SeekOrigin.Current); // Chunk size and file size id3v2Offset = source.ReadInt64(); if (FMT_ID.Equals(Utils.Latin1Encoding.GetString(source.ReadBytes(4)))) { source.BaseStream.Seek(8, SeekOrigin.Current); // Chunk size formatVersion = source.ReadInt32(); if (formatVersion > 1) { LogDelegator.GetLogDelegate()(Log.LV_ERROR, "DSF format version " + formatVersion + " not supported"); return(result); } isValid = true; source.BaseStream.Seek(8, SeekOrigin.Current); // Format ID (4), Channel type (4) uint channels = source.ReadUInt32(); switch (channels) { case 1: channelsArrangement = MONO; break; case 2: channelsArrangement = STEREO; break; case 3: channelsArrangement = ISO_3_0_0; break; case 4: channelsArrangement = QUAD; break; case 5: channelsArrangement = LRCLFE; break; case 6: channelsArrangement = ISO_3_2_0; break; case 7: channelsArrangement = ISO_3_2_1; break; default: channelsArrangement = UNKNOWN; break; } sampleRate = source.ReadUInt32(); bits = source.ReadUInt32(); ulong sampleCount = source.ReadUInt64(); duration = (double)sampleCount * 1000.0 / sampleRate; bitrate = Math.Round(((double)(sizeInfo.FileSize - source.BaseStream.Position)) * 8 / duration); //time to calculate average bitrate result = true; } // Load tag if exists if (id3v2Offset > 0) { if (readTagParams.PrepareForWriting) { id3v2StructureHelper.AddZone(id3v2Offset, (int)(source.BaseStream.Length - id3v2Offset)); id3v2StructureHelper.AddSize(12, source.BaseStream.Length); id3v2StructureHelper.AddIndex(20, id3v2Offset); } } else { id3v2Offset = 0; // Switch status to "tried to read, but nothing found" if (readTagParams.PrepareForWriting) { // Add EOF zone for future tag writing id3v2StructureHelper.AddZone(source.BaseStream.Length, 0); id3v2StructureHelper.AddSize(12, source.BaseStream.Length); id3v2StructureHelper.AddIndex(20, source.BaseStream.Length); } } } return(result); }
protected override bool read(BinaryReader source, MetaDataIO.ReadTagParams readTagParams) { bool result = false; long position; resetData(); source.BaseStream.Seek(0, SeekOrigin.Begin); if (AIFF_CONTAINER_ID.Equals(Utils.Latin1Encoding.GetString(source.ReadBytes(4)))) { // Container chunk size long containerChunkPos = source.BaseStream.Position; int containerChunkSize = StreamUtils.DecodeBEInt32(source.ReadBytes(4)); if (containerChunkPos + containerChunkSize + 4 != source.BaseStream.Length) { LogDelegator.GetLogDelegate()(Log.LV_WARNING, "Header size is incoherent with file size"); } // Form type format = Utils.Latin1Encoding.GetString(source.ReadBytes(4)); if (format.Equals(FORMTYPE_AIFF) || format.Equals(FORMTYPE_AIFC)) { isValid = true; StringBuilder commentStr = new StringBuilder(""); long soundChunkPosition = 0; long soundChunkSize = 0; // Header size included bool nameFound = false; bool authorFound = false; bool copyrightFound = false; bool commentsFound = false; long limit = Math.Min(containerChunkPos + containerChunkSize + 4, source.BaseStream.Length); int annotationIndex = 0; int commentIndex = 0; while (source.BaseStream.Position < limit) { ChunkHeader header = seekNextChunkHeader(source, limit); position = source.BaseStream.Position; if (header.ID.Equals(CHUNKTYPE_COMMON)) { short channels = StreamUtils.DecodeBEInt16(source.ReadBytes(2)); switch (channels) { case 1: channelsArrangement = MONO; break; case 2: channelsArrangement = STEREO; break; case 3: channelsArrangement = ISO_3_0_0; break; case 4: channelsArrangement = ISO_2_2_0; break; // Specs actually allow both 2/2.0 and LRCS case 6: channelsArrangement = LRLcRcCS; break; default: channelsArrangement = UNKNOWN; break; } numSampleFrames = StreamUtils.DecodeBEUInt32(source.ReadBytes(4)); sampleSize = (uint)StreamUtils.DecodeBEInt16(source.ReadBytes(2)); // This sample size is for uncompressed data only byte[] byteArray = source.ReadBytes(10); Array.Reverse(byteArray); double aSampleRate = StreamUtils.ExtendedToDouble(byteArray); if (format.Equals(FORMTYPE_AIFC)) { compression = Utils.Latin1Encoding.GetString(source.ReadBytes(4)); } else // AIFF <=> no compression { compression = COMPRESSION_NONE; } if (aSampleRate > 0) { sampleRate = (int)Math.Round(aSampleRate); duration = (double)numSampleFrames * 1000.0 / sampleRate; if (!compression.Equals(COMPRESSION_NONE)) // Sample size is specific to selected compression method { if (compression.ToLower().Equals("fl32")) { sampleSize = 32; } else if (compression.ToLower().Equals("fl64")) { sampleSize = 64; } else if (compression.ToLower().Equals("alaw")) { sampleSize = 8; } else if (compression.ToLower().Equals("ulaw")) { sampleSize = 8; } } if (duration > 0) { bitrate = sampleSize * numSampleFrames * channelsArrangement.NbChannels / duration; } } } else if (header.ID.Equals(CHUNKTYPE_SOUND)) { soundChunkPosition = source.BaseStream.Position - 8; soundChunkSize = header.Size + 8; } else if (header.ID.Equals(CHUNKTYPE_NAME) || header.ID.Equals(CHUNKTYPE_AUTHOR) || header.ID.Equals(CHUNKTYPE_COPYRIGHT)) { structureHelper.AddZone(source.BaseStream.Position - 8, header.Size + 8, header.ID); structureHelper.AddSize(containerChunkPos, containerChunkSize, header.ID); tagExists = true; if (header.ID.Equals(CHUNKTYPE_NAME)) { nameFound = true; } if (header.ID.Equals(CHUNKTYPE_AUTHOR)) { authorFound = true; } if (header.ID.Equals(CHUNKTYPE_COPYRIGHT)) { copyrightFound = true; } SetMetaField(header.ID, Utils.Latin1Encoding.GetString(source.ReadBytes(header.Size)), readTagParams.ReadAllMetaFrames); } else if (header.ID.Equals(CHUNKTYPE_ANNOTATION)) { annotationIndex++; structureHelper.AddZone(source.BaseStream.Position - 8, header.Size + 8, header.ID + annotationIndex); structureHelper.AddSize(containerChunkPos, containerChunkSize, header.ID + annotationIndex); if (commentStr.Length > 0) { commentStr.Append(Settings.InternalValueSeparator); } commentStr.Append(Utils.Latin1Encoding.GetString(source.ReadBytes(header.Size))); tagExists = true; } else if (header.ID.Equals(CHUNKTYPE_COMMENTS)) { commentIndex++; structureHelper.AddZone(source.BaseStream.Position - 8, header.Size + 8, header.ID + commentIndex); structureHelper.AddSize(containerChunkPos, containerChunkSize, header.ID + commentIndex); tagExists = true; commentsFound = true; ushort numComs = StreamUtils.DecodeBEUInt16(source.ReadBytes(2)); for (int i = 0; i < numComs; i++) { CommentData cmtData = new CommentData(); cmtData.Timestamp = StreamUtils.DecodeBEUInt32(source.ReadBytes(4)); cmtData.MarkerId = StreamUtils.DecodeBEInt16(source.ReadBytes(2)); // Comments length ushort comLength = StreamUtils.DecodeBEUInt16(source.ReadBytes(2)); MetaFieldInfo comment = new MetaFieldInfo(getImplementedTagType(), header.ID + commentIndex); comment.Value = Utils.Latin1Encoding.GetString(source.ReadBytes(comLength)); comment.SpecificData = cmtData; tagData.AdditionalFields.Add(comment); // Only read general purpose comments, not those linked to a marker if (0 == cmtData.MarkerId) { if (commentStr.Length > 0) { commentStr.Append(Settings.InternalValueSeparator); } commentStr.Append(comment.Value); } } } else if (header.ID.Equals(CHUNKTYPE_ID3TAG)) { id3v2Offset = source.BaseStream.Position; // Zone is already added by Id3v2.Read id3v2StructureHelper.AddZone(id3v2Offset - 8, header.Size + 8, CHUNKTYPE_ID3TAG); id3v2StructureHelper.AddSize(containerChunkPos, containerChunkSize, CHUNKTYPE_ID3TAG); } source.BaseStream.Position = position + header.Size; if (header.ID.Equals(CHUNKTYPE_SOUND) && header.Size % 2 > 0) { source.BaseStream.Position += 1; // Sound chunk size must be even } } tagData.IntegrateValue(TagData.TAG_FIELD_COMMENT, commentStr.ToString().Replace("\0", " ").Trim()); if (-1 == id3v2Offset) { id3v2Offset = 0; // Switch status to "tried to read, but nothing found" if (readTagParams.PrepareForWriting) { id3v2StructureHelper.AddZone(soundChunkPosition + soundChunkSize, 0, CHUNKTYPE_ID3TAG); id3v2StructureHelper.AddSize(containerChunkPos, containerChunkSize, CHUNKTYPE_ID3TAG); } } // Add zone placeholders for future tag writing if (readTagParams.PrepareForWriting) { if (!nameFound) { structureHelper.AddZone(soundChunkPosition, 0, CHUNKTYPE_NAME); structureHelper.AddSize(containerChunkPos, containerChunkSize, CHUNKTYPE_NAME); } if (!authorFound) { structureHelper.AddZone(soundChunkPosition, 0, CHUNKTYPE_AUTHOR); structureHelper.AddSize(containerChunkPos, containerChunkSize, CHUNKTYPE_AUTHOR); } if (!copyrightFound) { structureHelper.AddZone(soundChunkPosition, 0, CHUNKTYPE_COPYRIGHT); structureHelper.AddSize(containerChunkPos, containerChunkSize, CHUNKTYPE_COPYRIGHT); } if (!commentsFound) { structureHelper.AddZone(soundChunkPosition, 0, CHUNKTYPE_COMMENTS); structureHelper.AddSize(containerChunkPos, containerChunkSize, CHUNKTYPE_COMMENTS); } } result = true; } } return(result); }
public Boolean Read(BinaryReader source, SizeInfo sizeInfo, MetaDataIO.ReadTagParams readTagParams) { this.sizeInfo = sizeInfo; var result = false; resetData(); source.BaseStream.Seek(0, SeekOrigin.Begin); if (DSD_ID.Equals(Utils.Latin1Encoding.GetString(source.ReadBytes(4)))) { source.BaseStream.Seek(16, SeekOrigin.Current); // Chunk size and file size id3v2Offset = source.ReadInt64(); if (FMT_ID.Equals(Utils.Latin1Encoding.GetString(source.ReadBytes(4)))) { source.BaseStream.Seek(8, SeekOrigin.Current); // Chunk size formatVersion = source.ReadInt32(); if (formatVersion > 1) { LogDelegator.GetLogDelegate()(Log.LV_ERROR, "DSF format version " + formatVersion + " not supported"); return(result); } isValid = true; source.BaseStream.Seek(8, SeekOrigin.Current); // Format ID (4), Channel type (4) channels = source.ReadUInt32(); sampleRate = source.ReadUInt32(); bits = source.ReadUInt32(); var sampleCount = source.ReadUInt64(); duration = (Double)sampleCount * 1000.0 / sampleRate; bitrate = Math.Round(((Double)(sizeInfo.FileSize - source.BaseStream.Position)) * 8 / duration); //time to calculate average bitrate result = true; } // Load tag if exists if (id3v2Offset > 0) { if (readTagParams.PrepareForWriting) { id3v2StructureHelper.AddZone(id3v2Offset, (Int32)(source.BaseStream.Length - id3v2Offset)); id3v2StructureHelper.AddSize(12, source.BaseStream.Length); id3v2StructureHelper.AddIndex(20, id3v2Offset); } } else { id3v2Offset = 0; // Switch status to "tried to read, but nothing found" if (readTagParams.PrepareForWriting) { // Add EOF zone for future tag writing id3v2StructureHelper.AddZone(source.BaseStream.Length, 0); id3v2StructureHelper.AddSize(12, source.BaseStream.Length); id3v2StructureHelper.AddIndex(20, source.BaseStream.Length); } } } return(result); }
public bool Write(BinaryReader r, BinaryWriter w, TagData tag) { long oldTagSize; long newTagSize; long cumulativeDelta = 0; bool result = true; // Constraint-check on non-supported values if (FieldCodeFixedLength > 0) { if (tag.Pictures != null) { foreach (PictureInfo picInfo in tag.Pictures) { if (PictureInfo.PIC_TYPE.Unsupported.Equals(picInfo.PicType) && (picInfo.TagType.Equals(getImplementedTagType()))) { if ((-1 == picInfo.NativePicCode) && (Utils.ProtectValue(picInfo.NativePicCodeStr).Length != FieldCodeFixedLength)) { throw new NotSupportedException("Field code fixed length is " + FieldCodeFixedLength + "; detected field '" + Utils.ProtectValue(picInfo.NativePicCodeStr) + "' is " + Utils.ProtectValue(picInfo.NativePicCodeStr).Length + " characters long and cannot be written"); } } } } foreach (MetaFieldInfo fieldInfo in tag.AdditionalFields) { if (fieldInfo.TagType.Equals(getImplementedTagType()) || MetaDataIOFactory.TAG_ANY == fieldInfo.TagType) { if (Utils.ProtectValue(fieldInfo.NativeFieldCode).Length != FieldCodeFixedLength) { throw new NotSupportedException("Field code fixed length is " + FieldCodeFixedLength + "; detected field '" + Utils.ProtectValue(fieldInfo.NativeFieldCode) + "' is " + Utils.ProtectValue(fieldInfo.NativeFieldCode).Length + " characters long and cannot be written"); } } } } structureHelper.Clear(); tagData.Pictures.Clear(); // Read all the fields in the existing tag (including unsupported fields) ReadTagParams readTagParams = new ReadTagParams(true, true); readTagParams.PrepareForWriting = true; if (embedder != null && embedder.HasEmbeddedID3v2 > 0) { readTagParams.offset = embedder.HasEmbeddedID3v2; } this.read(r, readTagParams); if (embedder != null && getImplementedTagType() == MetaDataIOFactory.TAG_ID3V2) { structureHelper.Clear(); structureHelper.AddZone(embedder.Id3v2Zone); } // Give engine something to work with if the tag is really empty if (!tagExists && 0 == Zones.Count) { structureHelper.AddZone(0, 0); } TagData dataToWrite; dataToWrite = tagData; dataToWrite.IntegrateValues(tag); // Merge existing information + new tag information foreach (Zone zone in Zones) { oldTagSize = zone.Size; bool isTagWritten; // Write new tag to a MemoryStream using (MemoryStream s = new MemoryStream(zone.Size)) using (BinaryWriter msw = new BinaryWriter(s, Settings.DefaultTextEncoding)) { if (write(dataToWrite, msw, zone.Name) > 0) { isTagWritten = true; newTagSize = s.Length; if (embedder != null && getImplementedTagType() == MetaDataIOFactory.TAG_ID3V2 && embedder.ID3v2EmbeddingHeaderSize > 0) { StreamUtils.LengthenStream(s, 0, embedder.ID3v2EmbeddingHeaderSize); s.Position = 0; embedder.WriteID3v2EmbeddingHeader(msw, newTagSize); newTagSize = s.Length; } } else { isTagWritten = false; newTagSize = zone.CoreSignature.Length; } // -- Adjust tag slot to new size in file -- long tagBeginOffset, tagEndOffset; if (tagExists && zone.Size > zone.CoreSignature.Length) // An existing tag has been reprocessed { tagBeginOffset = zone.Offset + cumulativeDelta; tagEndOffset = tagBeginOffset + zone.Size; } else // A brand new tag has been added to the file { if (embedder != null && getImplementedTagType() == MetaDataIOFactory.TAG_ID3V2) { tagBeginOffset = embedder.Id3v2Zone.Offset; } else { switch (getDefaultTagOffset()) { case TO_EOF: tagBeginOffset = r.BaseStream.Length; break; case TO_BOF: tagBeginOffset = 0; break; case TO_BUILTIN: tagBeginOffset = zone.Offset + cumulativeDelta; break; default: tagBeginOffset = -1; break; } } tagEndOffset = tagBeginOffset + zone.Size; } // Need to build a larger file if (newTagSize > zone.Size) { StreamUtils.LengthenStream(w.BaseStream, tagEndOffset, (uint)(newTagSize - zone.Size)); } else if (newTagSize < zone.Size) // Need to reduce file size { StreamUtils.ShortenStream(w.BaseStream, tagEndOffset, (uint)(zone.Size - newTagSize)); } // Copy tag contents to the new slot r.BaseStream.Seek(tagBeginOffset, SeekOrigin.Begin); s.Seek(0, SeekOrigin.Begin); if (isTagWritten) { StreamUtils.CopyStream(s, w.BaseStream); } else { if (zone.CoreSignature.Length > 0) { msw.Write(zone.CoreSignature); } } int delta = (int)(newTagSize - oldTagSize); cumulativeDelta += delta; // Edit wrapping size markers and frame counters if needed if (delta != 0 && (MetaDataIOFactory.TAG_NATIVE == getImplementedTagType() || (embedder != null && getImplementedTagType() == MetaDataIOFactory.TAG_ID3V2))) { int action; if (oldTagSize == zone.CoreSignature.Length && isTagWritten) { action = ACTION_ADD; } else if (newTagSize == zone.CoreSignature.Length && !isTagWritten) { action = ACTION_DELETE; } else { action = ACTION_EDIT; } result = structureHelper.RewriteHeaders(w, delta, action, zone.Name); } } } // Loop through zones // Update tag information without calling Read /* TODO - this implementation is too risky : * - if one of the writing operations fails, data is updated as if everything went right * - any picture slot with a markForDeletion flag is recorded as-is in the tag */ tagData = dataToWrite; return(result); }