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 : IMediaContentResolver, IMagicValueResolver { public readonly List _mediaTypes = new List(); public readonly Dictionary _extensions = new Dictionary(); public readonly Dictionary _magicValues = new Dictionary(); public IReadOnlyCollection MediaTypes { get; } private RiffResolver() { MediaTypes = _mediaTypes.AsReadOnly(); IEnumerable entries; using (Stream str = typeof(MagicContentResolver).Assembly.GetManifestResourceStream("ReMime.ContentResolvers.riff.jsonc")!) { entries = MagicValueDatabaseEntry.GetEntries(str); } AddMagicValues(entries); } public void AddMagicValues(IEnumerable entries) { foreach (var entry in entries) { AddMagicValue(entry); } } 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 AddMagicValue(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); } } public static RiffResolver Instance { get; } = new RiffResolver(); [StructLayout(LayoutKind.Auto, Size = 12)] private struct RiffChunk { public int Riff; public int Size; public int FirstChunkType; public const int RiffValue = 1179011410; } } }