internal void Deserialize(Stream stream) { using (var reader = new TagReader(stream)) Size = reader.ReadUInt32SyncSafe(); if (Size < 6) { throw new AudioInvalidException("Corrupt id3 extended header."); } // Seek past it for now stream.Seek((int)Size, SeekOrigin.Current); }
internal void Deserialize(Stream stream) { using (var reader = new TagReader(stream)) { // Read the tag identifier #if NETSTANDARD2_0 var idTag = reader.ReadBytes(3); #else Span <byte> idTag = stackalloc byte[3]; reader.Read(idTag); #endif if (_id3.AsSpan().SequenceCompareTo(idTag) != 0) { throw new TagNotFoundException("ID3v2 tag identifier was not found."); } // Get the id3v2 version byte Version = reader.ReadByte(); if (Version == 0xFF) { throw new AudioInvalidException("Corrupt header: invalid ID3v2 version."); } // Get the id3v2 revision byte if (reader.ReadByte() == 0xFF) { throw new AudioInvalidException("Corrupt header: invalid ID3v2 revision."); } // Parse the flag byte var id3Flags = (byte)(0xF0 & reader.ReadByte()); Unsynchronisation = (id3Flags & 0b1000_0000) > 0; HasExtendedHeader = (id3Flags & 0b0100_0000) > 0; _hasFooter = (id3Flags & 0b0001_0000) > 0; // Get the id3v2 size, swap and un-sync the integer TagSize = reader.ReadUInt32SyncSafe(); if (TagSize == 0) { throw new AudioInvalidException("Corrupt header: tag size can't be zero."); } } }
internal static TagModel Deserialize(Stream stream) { var tagModel = new TagModel(); tagModel.Header.Deserialize(stream); if (tagModel.Header.Version != 3 & tagModel.Header.Version != 4) { throw new AudioUnsupportedException("ID3v2 Version " + tagModel.Header.Version + " is not supported."); } var id3TagSize = tagModel.Header.TagSize; MemoryStream?memory = null; try { if (tagModel.Header.Unsynchronisation) { memory = new(new byte[stream.Length]); id3TagSize -= Sync.Unsafe(stream, memory, id3TagSize); stream = memory; if (id3TagSize <= 0) { throw new AudioInvalidException("Data is missing after the header."); } } uint rawSize; // Seek past the extended header if (tagModel.Header.HasExtendedHeader) { tagModel.ExtendedHeader.Deserialize(stream); rawSize = id3TagSize - 4 - tagModel.ExtendedHeader.Size; if (id3TagSize <= 0) { throw new AudioInvalidException("Data is missing after the extended header."); } } else { rawSize = id3TagSize; } // Tags should have at least one frame if (rawSize <= 0) { throw new AudioInvalidException("No frames are present in the tag."); } uint index = 0; #if NETSTANDARD2_0 var frameIdBuffer = new byte[4]; #else Span <byte> frameIdBuffer = stackalloc byte[4]; #endif while (index < rawSize) { // Read one byte first, looking for padding #if NETSTANDARD2_0 stream.Read(frameIdBuffer, 0, 1); #else stream.Read(frameIdBuffer.Slice(0, 1)); #endif // We reached the padding if (frameIdBuffer[0] == 0) { tagModel.Header.PaddingSize = rawSize - index; stream.Seek(tagModel.Header.PaddingSize - 1, SeekOrigin.Current); break; } // 10 is the minimum size of a valid frame if (index + 10 > rawSize) { throw new AudioInvalidException("Tag is corrupt: incomplete frame."); } // Read the rest of the frame ID #if NETSTANDARD2_0 stream.Read(frameIdBuffer, 1, 3); #else stream.Read(frameIdBuffer.Slice(1, 3)); #endif index += 4; using (var reader = new TagReader(stream)) { var frameSize = tagModel.Header.Version == 4 ? reader.ReadUInt32SyncSafe() : reader.ReadUInt32BigEndian(); index += 4; // The size of the frame can't be larger than the available space if (frameSize > rawSize - index) { throw new AudioInvalidException( "A frame is corrupt: can't be larger than the available space remaining."); } var flags = new FrameFlags(reader.ReadUInt16BigEndian(), tagModel.Header.Version); index += 2; tagModel.Frames.Add(ReadFrame(reader, #if NETSTANDARD2_0 Encoding.ASCII.GetString(frameIdBuffer, 0, 4), flags, frameSize)); #else Encoding.ASCII.GetString(frameIdBuffer), flags, frameSize)); #endif index += frameSize; } } return(tagModel); } finally { memory?.Close(); } }