PKHeX/PKHeX.Core/PKM/Util/EntityFileExtension.cs
Kurt 9792455f34 Refactor to use Context over Generation
Generation was always more weak; am I paranoid about potential VC3? maybe
Better indicates the move source for LGPE exclusive moves, etc.
2026-02-15 02:15:50 -06:00

112 lines
4.6 KiB
C#

using System;
using System.Collections.Generic;
namespace PKHeX.Core;
/// <summary>
/// Logic for interacting with Entity file extensions.
/// </summary>
public static class EntityFileExtension
{
// All side-game formats that don't follow the usual pk* format
private const string ExtensionSK2 = "sk2";
private const string ExtensionCK3 = "ck3";
private const string ExtensionXK3 = "xk3";
private const string ExtensionBK4 = "bk4";
private const string ExtensionRK4 = "rk4";
private const string ExtensionPB7 = "pb7";
private const string ExtensionPB8 = "pb8";
private const string ExtensionPA8 = "pa8";
private const string ExtensionPA9 = "pa9";
private const int CountExtra = 8;
/// <summary>
/// Valid file extensions that represent <see cref="PKM"/> data, without the leading '.'
/// </summary>
private static string[] Extensions => GetExtensions();
/// <summary>
/// Gets an array of valid <see cref="PKM"/> file extensions.
/// </summary>
/// <param name="maxGeneration">Maximum Generation to permit</param>
/// <returns>Valid <see cref="PKM"/> file extensions.</returns>
public static string[] GetExtensions(byte maxGeneration = Latest.Generation)
{
int min = maxGeneration is <= 2 or >= 7 ? 1 : 3;
int size = maxGeneration - min + 1 + CountExtra;
var result = new List<string>(size);
for (int i = min; i <= maxGeneration; i++)
result.Add($"pk{i}");
if (min < 3)
result.Add(ExtensionSK2); // stadium
if (maxGeneration >= 3)
{
result.Add(ExtensionCK3); // colosseum
result.Add(ExtensionXK3); // xd
}
if (maxGeneration >= 4)
{
result.Add(ExtensionBK4); // battle revolution
result.Add(ExtensionRK4); // My Pokemon Ranch
}
if (maxGeneration >= 7)
result.Add(ExtensionPB7); // let's go
if (maxGeneration >= 8)
result.Add(ExtensionPB8); // Brilliant Diamond & Shining Pearl
if (maxGeneration >= 8)
result.Add(ExtensionPA8); // Legends: Arceus
if (maxGeneration >= 9)
result.Add(ExtensionPA9); // Legends: Z-A
return [.. result];
}
/// <summary>
/// Roughly detects the PKM format from the file's extension.
/// </summary>
/// <param name="ext">File extension.</param>
/// <param name="prefer">Preference if not a valid extension, usually the highest acceptable format.</param>
/// <returns>Format hint that the file is.</returns>
public static EntityContext GetContextFromExtension(ReadOnlySpan<char> ext, EntityContext prefer = EntityContext.None)
{
if (ext.Length == 0)
return prefer;
static bool Is(ReadOnlySpan<char> ext, ReadOnlySpan<char> str) => ext.EndsWith(str, StringComparison.InvariantCultureIgnoreCase);
if (Is(ext, "a9")) return EntityContext.Gen9a;
if (Is(ext, "a8")) return EntityContext.Gen8a;
if (Is(ext, "b8")) return EntityContext.Gen8b;
if (Is(ext, "k8")) return EntityContext.Gen8;
if (Is(ext, "b7")) return EntityContext.Gen7b;
if (Is(ext, "k7")) return EntityContext.Gen7;
if (Is(ext, "k6")) return EntityContext.Gen6;
return (EntityContext)GetFormatFromExtension(ext[^1], prefer);
}
/// <summary>
/// Roughly detects the PKM format from the file's extension.
/// </summary>
/// <param name="last">Last character of the file's extension.</param>
/// <param name="prefer">Preference if not a valid extension, usually the highest acceptable format.</param>
/// <returns>Format hint that the file is.</returns>
private static int GetFormatFromExtension(char last, EntityContext prefer)
{
if (last is >= '1' and <= '9')
return last - '0';
return (int)prefer;
}
public static IReadOnlyList<string> Extensions7b => [ExtensionPB7];
public static IReadOnlyList<string> GetExtensionsAll() => Extensions;
public static IReadOnlyList<string> GetExtensionsHOME() => Extensions;
public static IReadOnlyList<string> GetExtensionsAtOrBelow(byte specific)
=> Array.FindAll(Extensions, f => IsAtOrBelow(f, specific));
public static IReadOnlyList<string> GetExtensionsAtOrBelow(byte specific, string exclude)
=> Array.FindAll(Extensions, f => IsAtOrBelow(f, specific) && !exclude.Contains(f));
private static bool IsAtOrBelow(ReadOnlySpan<char> ext, byte specific)
=> IsAtOrBelow(specific, ext[^1] - 0x30);
private static bool IsAtOrBelow(byte specific, int gen) => gen <= specific;
}