diff --git a/ReMime/ContentResolvers/RiffResolver.cs b/ReMime/ContentResolvers/RiffResolver.cs new file mode 100644 index 0000000..ea949fe --- /dev/null +++ b/ReMime/ContentResolvers/RiffResolver.cs @@ -0,0 +1,105 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace ReMime.ContentResolvers +{ + public class RiffResolver : IMediaTypeResolver, IMediaContentResolver + { + public readonly List _mediaTypes = new List(); + public readonly Dictionary _extensions = new Dictionary(); + public readonly Dictionary _magicValues = new Dictionary(); + + public IReadOnlyCollection MediaTypes { get; } + + public RiffResolver() + { + MediaTypes = _mediaTypes.AsReadOnly(); + + List entries; + + using (Stream str = typeof(MagicContentResolver).Assembly.GetManifestResourceStream("ReMime.ContentResolvers.riff.jsonc")!) + { + entries = MagicValueDatabaseEntry.GetEntries(str); + } + + foreach (var entry in entries) + { + AddRiffType((MagicValueMediaType)entry); + } + } + + public RiffResolver(IEnumerable values) : this() + { + foreach (MagicValueMediaType value in values) + AddRiffType(value); + } + + public bool TryResolve(Stream str, [NotNullWhen(true)] out MediaType? mediaType) + { + Span content = stackalloc byte[Unsafe.SizeOf()]; + str.Read(content); + return TryResolve(content, out mediaType); + } + + public bool TryResolve(ReadOnlySpan content, [NotNullWhen(true)] out MediaType? mediaType) + { + mediaType = null; + + if (content.Length < Unsafe.SizeOf()) + return false; + + ref readonly RiffChunk chunk = ref MemoryMarshal.Cast(content)[0]; + + if (chunk.Riff != RiffChunk.RiffValue) + return false; + + return _magicValues.TryGetValue(chunk.FirstChunkType, out mediaType); + } + + public bool TryResolve(string extension, out MediaType? mediaType) + { + return _extensions.TryGetValue(extension, out mediaType); + } + + /// + /// Add a RIFF sub-magic value to this resolver. + /// + /// + public void AddRiffType(MagicValueMediaType type) + { + if (type.MagicValues.Length == 0) + throw new ArgumentException("Expected at least one media type."); + + _mediaTypes.Add(type.MediaType); + + foreach (string extension in type.Extensions) + { + _extensions.Add(extension, type.MediaType); + } + + foreach (MagicValue magic in type.MagicValues) + { + if (magic.Value.Length != 4) + continue; + + int i = MemoryMarshal.Cast(magic.Value)[0]; + + _magicValues.Add(i, type.MediaType); + } + } + + [StructLayout(LayoutKind.Auto, Size = 12)] + private struct RiffChunk + { + public int Riff; + public int Size; + public int FirstChunkType; + + public const int RiffValue = 1179011410; + } + } +} \ No newline at end of file diff --git a/ReMime/ContentResolvers/riff.jsonc b/ReMime/ContentResolvers/riff.jsonc new file mode 100644 index 0000000..1adaac3 --- /dev/null +++ b/ReMime/ContentResolvers/riff.jsonc @@ -0,0 +1,21 @@ +/* + * ReMime RIFF file types database. + * This is a self compiled list of magic values, file extensions and + * their media-types. Please contribute common file formats if you come + * across them. + * + * This file is only for RIFF based formats. Please do not add magic values + * for other file types here, as the magic values in this file are used + */ +[ + { "type": "video/avi", "magic": ["'AVI '"], "extensions": ["avi"]}, + { "type": "audio/wav", "magic": ["'WAVE'"], "extensions": ["wav"] }, + { "type": "image/vnd.microsoft.icon", "magic": ["'ACON'"], "extensions": ["ani"] }, + + // Whilst we speak about WEBP... + // My kindest f*ck you to Google for impeding standards like JPEG-XL + // for their own benefit. WEBP itself might be a fine file format + // but I do not support it if you maliciously stop implementing other + // file formats into your browsers and applications. + { "type": "image/webp", "magic": ["'WEBP'"], "extensions": ["webp"] } +] \ No newline at end of file diff --git a/ReMime/MediaTypeResolver.cs b/ReMime/MediaTypeResolver.cs index 3c4d907..e116737 100644 --- a/ReMime/MediaTypeResolver.cs +++ b/ReMime/MediaTypeResolver.cs @@ -50,6 +50,7 @@ namespace ReMime static MediaTypeResolver() { + AddResolver(new RiffResolver(), 9997); AddResolver(new MagicContentResolver(), 9998); if (OperatingSystem.IsWindows()) diff --git a/ReMime/ReMime.csproj b/ReMime/ReMime.csproj index 8fefd8e..5145f94 100644 --- a/ReMime/ReMime.csproj +++ b/ReMime/ReMime.csproj @@ -8,6 +8,7 @@ +