Add RIFF container support.

This commit is contained in:
H. Utku Maden 2024-08-30 14:58:09 +03:00
parent ccf46a765c
commit f9792e2bcf
4 changed files with 128 additions and 0 deletions

@ -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<MediaType> _mediaTypes = new List<MediaType>();
public readonly Dictionary<string, MediaType> _extensions = new Dictionary<string, MediaType>();
public readonly Dictionary<int, MediaType> _magicValues = new Dictionary<int, MediaType>();
public IReadOnlyCollection<MediaType> MediaTypes { get; }
public RiffResolver()
{
MediaTypes = _mediaTypes.AsReadOnly();
List<MagicValueDatabaseEntry> 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<MagicValueMediaType> values) : this()
{
foreach (MagicValueMediaType value in values)
AddRiffType(value);
}
public bool TryResolve(Stream str, [NotNullWhen(true)] out MediaType? mediaType)
{
Span<byte> content = stackalloc byte[Unsafe.SizeOf<RiffChunk>()];
str.Read(content);
return TryResolve(content, out mediaType);
}
public bool TryResolve(ReadOnlySpan<byte> content, [NotNullWhen(true)] out MediaType? mediaType)
{
mediaType = null;
if (content.Length < Unsafe.SizeOf<RiffChunk>())
return false;
ref readonly RiffChunk chunk = ref MemoryMarshal.Cast<byte, RiffChunk>(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);
}
/// <summary>
/// Add a RIFF sub-magic value to this resolver.
/// </summary>
/// <param name="type"></param>
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<byte, int>(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;
}
}
}

@ -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"] }
]

@ -50,6 +50,7 @@ namespace ReMime
static MediaTypeResolver()
{
AddResolver(new RiffResolver(), 9997);
AddResolver(new MagicContentResolver(), 9998);
if (OperatingSystem.IsWindows())

@ -8,6 +8,7 @@
<ItemGroup>
<EmbeddedResource Include="ContentResolvers/database.jsonc" />
<EmbeddedResource Include="ContentResolvers/riff.jsonc" />
</ItemGroup>
</Project>