diff --git a/src/ImageSharp/Formats/Icon/IconImageFormatDetector.cs b/src/ImageSharp/Formats/Icon/IconImageFormatDetector.cs index aff8dfe0ac..4fceef3b8e 100644 --- a/src/ImageSharp/Formats/Icon/IconImageFormatDetector.cs +++ b/src/ImageSharp/Formats/Icon/IconImageFormatDetector.cs @@ -2,7 +2,6 @@ // Licensed under the Six Labors Split License. using System.Diagnostics.CodeAnalysis; -using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.Formats.Icon; @@ -12,22 +11,58 @@ namespace SixLabors.ImageSharp.Formats.Icon; public class IconImageFormatDetector : IImageFormatDetector { /// - public int HeaderSize { get; } = 4; + public int HeaderSize { get; } = IconDir.Size + IconDirEntry.Size; /// public bool TryDetectFormat(ReadOnlySpan header, [NotNullWhen(true)] out IImageFormat? format) { - switch (MemoryMarshal.Cast(header)[0]) + format = this.IsSupportedFileFormat(header) switch { - case Ico.IcoConstants.FileHeader: - format = Ico.IcoFormat.Instance; - return true; - case Cur.CurConstants.FileHeader: - format = Cur.CurFormat.Instance; - return true; - default: - format = default; - return false; + true => Ico.IcoFormat.Instance, + false => Cur.CurFormat.Instance, + null => default + }; + + return format is not null; + } + + private bool? IsSupportedFileFormat(ReadOnlySpan header) + { + // There are no magic bytes in the first few bytes of a tga file, + // so we try to figure out if its a valid tga by checking for valid tga header bytes. + if (header.Length < this.HeaderSize) + { + return null; + } + + IconDir dir = IconDir.Parse(header); + if (dir is not { Reserved: 0 } // Should be 0. + or not { Type: IconFileType.ICO or IconFileType.CUR } // Unknown Type. + or { Count: 0 }) // Should not be 0. + { + return null; + } + + IconDirEntry entry = IconDirEntry.Parse(header[IconDir.Size..]); + if (entry is not { Reserved: 0 } // Should be 0. + or { BytesInRes: 0 } // Should not be 0. + || entry.ImageOffset < IconDir.Size + (dir.Count * IconDirEntry.Size)) + { + return null; + } + + if (dir.Type is IconFileType.ICO) + { + if (entry is not { BitCount: 1 or 4 or 8 or 16 or 24 or 32 } or not { Planes: 0 or 1 }) + { + return null; + } + + return true; + } + else + { + return false; } } }