static FrameBase ReadFrame(BinaryReader reader, string frameId, FrameFlags flags, uint frameSize) { #if NETSTANDARD2_0 var frameDataBuffer = reader.ReadBytes((int)frameSize); #else // Use heap allocations for frames > 256kB (usually pictures) var frameDataBuffer = frameSize < 0x40000 ? stackalloc byte[(int)frameSize] : new byte[(int)frameSize]; reader.Read(frameDataBuffer); #endif return(FrameFactory.Build(frameId, flags, frameDataBuffer)); }
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(); } }