using System; using System.Buffers; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Reflection; namespace PKHeX.Core; /// /// Wrapper for reading resources embedded in an assembly with some considerations for faster retrieval and case insensitivity. /// public sealed class EmbeddedResourceCache { private readonly Assembly Assembly; /// /// Remember the resource names for quick lookup. /// private readonly Dictionary ResourceNameMap; public EmbeddedResourceCache(Assembly assembly) { Assembly = assembly; ResourceNameMap = BuildLookup(Assembly.GetManifestResourceNames()); } private static Dictionary BuildLookup(ReadOnlySpan manifestNames) { var result = new Dictionary(manifestNames.Length); foreach (var resName in manifestNames) { var fileName = GetFileName(resName); result.Add(fileName, resName); } return result; } private static string GetFileName(string resName) { var period = resName.LastIndexOf('.'); if (resName.Length - period <= 5) period = resName.LastIndexOf('.', period - 1); var start = period + 1; System.Diagnostics.Debug.Assert(start != 0); // should have a period in the name // text file fetch excludes ".txt" (mixed case...); other extensions are used (all lowercase). return resName.EndsWith(".txt", StringComparison.Ordinal) ? resName[start..^4].ToLowerInvariant() : resName[start..]; } /// /// Fetches a string resource from this assembly. /// public bool TryGetStringResource(string name, [NotNullWhen(true)] out string? result) { if (!ResourceNameMap.TryGetValue(name.ToLowerInvariant(), out result)) return false; using var resource = Assembly.GetManifestResourceStream(result); if (resource is null) return false; using var reader = new StreamReader(resource); result = reader.ReadToEnd(); return true; } /// /// Fetches a byte array resource from this assembly. /// public bool TryGetBinaryResource(string name, [NotNullWhen(true)] out byte[]? result) { result = null; if (!ResourceNameMap.TryGetValue(name, out var resName)) return false; using var resource = Assembly.GetManifestResourceStream(resName); if (resource is null) return false; result = new byte[resource.Length]; resource.ReadExactly(result); return true; } /// /// Fetches a byte array resource from this assembly. /// public bool TryGetBinaryResource(string name, ArrayPool provider, [NotNullWhen(true)] out byte[]? result, out int length) { length = -1; result = null; if (!ResourceNameMap.TryGetValue(name, out var resName)) return false; using var resource = Assembly.GetManifestResourceStream(resName); if (resource is null) return false; result = provider.Rent(length = (int)resource.Length); resource.ReadExactly(result, 0, length); return true; } }