Break up PKX into separate classes

Many years ago, PKX used to be a >4,000 line bloated file, which spun off multiple classes like CommonEdits and most of the early non-GUI PKM related logic. Now, it's just a stub to source the latest generation & personal table.

Separate files = more concise info, and more room to grow to do more advanced things.
Makes the IsPresent methods public (no longer internal).
This commit is contained in:
Kurt 2022-05-06 20:38:55 -07:00
parent 124bbf98ad
commit a57f40ae7d
37 changed files with 465 additions and 353 deletions

View file

@ -57,7 +57,7 @@ namespace PKHeX.Core
case PersonalInfo.RatioMagicMale: return 0;
}
if (!pk.IsGenderValid())
return PKX.GetGenderFromPIDAndRatio(pk.PID, gt);
return EntityGender.GetFromPIDAndRatio(pk.PID, gt);
return pk.Gender;
}

View file

@ -72,7 +72,7 @@ namespace PKHeX.Core
if (pk is PK5 pk5 && index == 2)
pk5.HiddenAbility = true;
else if (pk.Format <= 5)
pk.PID = PKX.GetRandomPID(Util.Rand, pk.Species, pk.Gender, pk.Version, pk.Nature, pk.Form, (uint)(index * 0x10001));
pk.PID = EntityPID.GetRandomPID(Util.Rand, pk.Species, pk.Gender, pk.Version, pk.Nature, pk.Form, (uint)(index * 0x10001));
pk.RefreshAbility(index);
}

View file

@ -0,0 +1,112 @@
using System;
using static PKHeX.Core.NatureAmpRequest;
namespace PKHeX.Core;
/// <summary>
/// Logic for mutating a nature to amplify certain stats.
/// </summary>
public static class NatureAmp
{
/// <summary>
/// Mutate the nature amp indexes to match the request
/// </summary>
/// <param name="type">Request type to modify the provided <see cref="statIndex"/></param>
/// <param name="statIndex">Stat Index to mutate</param>
/// <param name="currentNature">Current nature to derive the current amps from</param>
/// <returns>New nature value</returns>
public static int GetNewNature(this NatureAmpRequest type, int statIndex, int currentNature)
{
if (currentNature > (int)Nature.Quirky)
return -1;
var (up, dn) = GetNatureModification(currentNature);
return GetNewNature(type, statIndex, up, dn);
}
/// <inheritdoc cref="GetNewNature(PKHeX.Core.NatureAmpRequest,int,int)"/>
public static int GetNewNature(NatureAmpRequest type, int statIndex, int up, int dn)
{
//
switch (type)
{
case Increase when up != statIndex:
up = statIndex;
break;
case Decrease when dn != statIndex:
dn = statIndex;
break;
case Neutral when up != statIndex && dn != statIndex:
up = dn = statIndex;
break;
default:
return -1; // failure
}
return CreateNatureFromAmps(up, dn);
}
/// <summary>
/// Recombine the stat amps into a nature value.
/// </summary>
/// <param name="up">Increased stat</param>
/// <param name="dn">Decreased stat</param>
/// <returns>Nature</returns>
public static int CreateNatureFromAmps(int up, int dn)
{
if ((uint)up > 5 || (uint)dn > 5)
return -1;
return (up * 5) + dn;
}
/// <summary>
/// Decompose the nature to the two stat indexes that are modified
/// </summary>
public static (int up, int dn) GetNatureModification(int nature)
{
var up = (nature / 5) + 1;
var dn = (nature % 5) + 1;
return (up, dn);
}
/// <summary>
/// Checks if the nature is out of range or the stat amplifications are not neutral.
/// </summary>
/// <param name="nature">Nature</param>
/// <param name="up">Increased stat</param>
/// <param name="dn">Decreased stat</param>
/// <returns>True if nature modification values are equal or the Nature is out of range.</returns>
public static bool IsNeutralOrInvalid(int nature, int up, int dn)
{
return up == dn || nature >= 25; // invalid
}
/// <inheritdoc cref="IsNeutralOrInvalid(int, int, int)"/>
public static bool IsNeutralOrInvalid(int nature)
{
var (up, dn) = GetNatureModification(nature);
return IsNeutralOrInvalid(nature, up, dn);
}
/// <summary>
/// Updates stats according to the specified nature.
/// </summary>
/// <param name="stats">Current stats to amplify if appropriate</param>
/// <param name="nature">Nature</param>
public static void ModifyStatsForNature(Span<ushort> stats, int nature)
{
var (up, dn) = GetNatureModification(nature);
if (IsNeutralOrInvalid(nature, up, dn))
return;
stats[up] *= 11; stats[up] /= 10;
stats[dn] *= 9; stats[dn] /= 10;
}
}
public enum NatureAmpRequest
{
Neutral,
Increase,
Decrease,
}

View file

@ -23,7 +23,7 @@ namespace PKHeX.Core
public static void AddFromLocalFile(string file, ConcurrentBag<SlotCache> db, ITrainerInfo dest, ICollection<string> validExtensions)
{
var fi = new FileInfo(file);
if (!validExtensions.Contains(fi.Extension) || !PKX.IsPKM(fi.Length))
if (!validExtensions.Contains(fi.Extension) || !EntityDetection.IsSizePlausible(fi.Length))
return;
var data = File.ReadAllBytes(file);
@ -98,7 +98,7 @@ namespace PKHeX.Core
public static void AddFromLocalFile(string file, ICollection<SlotCache> db, ITrainerInfo dest, ICollection<string> validExtensions)
{
var fi = new FileInfo(file);
if (!validExtensions.Contains(fi.Extension) || !PKX.IsPKM(fi.Length))
if (!validExtensions.Contains(fi.Extension) || !EntityDetection.IsSizePlausible(fi.Length))
return;
var data = File.ReadAllBytes(file);

View file

@ -272,7 +272,7 @@ namespace PKHeX.Core
return true;
if (Species == (int) Core.Species.Azurill && Generation == 4 && Location == 233 && pkm.Gender == 0)
return PKX.GetGenderFromPIDAndRatio(pkm.PID, 0xBF) == 1;
return EntityGender.GetFromPIDAndRatio(pkm.PID, 0xBF) == 1;
return false;
}

View file

@ -17,7 +17,7 @@
protected override void SetPINGA(PKM pk, EncounterCriteria criteria)
{
int gender = criteria.GetGender(PKX.GetGenderFromPID(Species, PID), pk.PersonalInfo);
int gender = criteria.GetGender(EntityGender.GetFromPID(Species, PID), pk.PersonalInfo);
int nature = (int)Nature;
int ability = Ability.IsSingleValue(out var index) ? index : 0;

View file

@ -74,7 +74,7 @@ namespace PKHeX.Core
protected override void SetPINGA(PKM pk, EncounterCriteria criteria)
{
var pi = pk.PersonalInfo;
int gender = criteria.GetGender(PKX.GetGenderFromPID(Species, PID), pi);
int gender = criteria.GetGender(EntityGender.GetFromPID(Species, PID), pi);
int nature = (int)criteria.GetNature(Nature);
int ability = criteria.GetAbilityFromNumber(Ability);

View file

@ -31,7 +31,7 @@
protected override void SetPINGA(PKM pk, EncounterCriteria criteria)
{
var pi = pk.PersonalInfo;
int gender = criteria.GetGender(PKX.GetGenderFromPID(Species, PID), pi);
int gender = criteria.GetGender(EntityGender.GetFromPID(Species, PID), pi);
int nature = (int)criteria.GetNature(Nature);
int ability = criteria.GetAbilityFromNumber(Ability);

View file

@ -82,7 +82,7 @@ namespace PKHeX.Core
var pid = a << 16 | b;
// Process Conditions
if (PKX.GetUnownForm(pid) == form) // matches form, does it match nature?
if (EntityPID.GetUnownForm3(pid) == form) // matches form, does it match nature?
{
switch (VerifyPIDCriteria(pid, info))
{

View file

@ -530,7 +530,7 @@ namespace PKHeX.Core
return false;
const int AzurillGenderRatio = 0xBF;
var gender = PKX.GetGenderFromPIDAndRatio(pk.PID, AzurillGenderRatio);
var gender = EntityGender.GetFromPIDAndRatio(pk.PID, AzurillGenderRatio);
if (gender != 1)
return false;
@ -916,7 +916,7 @@ namespace PKHeX.Core
private static (int Species, int Gender) GetCuteCharmGenderSpecies(PKM pk, uint pid, int currentSpecies) => currentSpecies switch
{
// Nincada evo chain travels from M/F -> Genderless Shedinja
(int)Species.Shedinja => ((int)Species.Nincada, PKX.GetGenderFromPID((int)Species.Nincada, pid)),
(int)Species.Shedinja => ((int)Species.Nincada, EntityGender.GetFromPID((int)Species.Nincada, pid)),
// These evolved species cannot be encountered with cute charm.
// 100% fixed gender does not modify PID; override this with the encounter species for correct calculation.

View file

@ -69,14 +69,14 @@ namespace PKHeX.Core
var current = pkm.Gender;
if (current == 2) // shedinja, genderless
return true;
var gender = PKX.GetGenderFromPID(original, pkm.EncryptionConstant);
var gender = EntityGender.GetFromPID(original, pkm.EncryptionConstant);
return gender == current;
}
private static bool IsValidGenderMismatch(PKM pkm) => pkm.Species switch
{
// Shedinja evolution gender glitch, should match original Gender
(int) Species.Shedinja when pkm.Format == 4 => pkm.Gender == PKX.GetGenderFromPIDAndRatio(pkm.EncryptionConstant, 0x7F), // 50M-50F
(int) Species.Shedinja when pkm.Format == 4 => pkm.Gender == EntityGender.GetFromPIDAndRatio(pkm.EncryptionConstant, 0x7F), // 50M-50F
// Evolved from Azurill after transferring to keep gender
(int) Species.Marill or (int) Species.Azumarill when pkm.Format >= 6 => pkm.Gender == 1 && (pkm.EncryptionConstant & 0xFF) > 0x3F,

View file

@ -219,7 +219,7 @@ namespace PKHeX.Core
TID = TID,
SID = SID,
EXP = IsEgg ? Experience.GetEXP(5, PersonalInfo.EXPGrowth) : EXP,
Gender = PKX.GetGenderFromPID(Species, PID),
Gender = EntityGender.GetFromPID(Species, PID),
Form = Form,
// IsEgg = false, -- already false
OT_Friendship = 70,

View file

@ -13,7 +13,7 @@ namespace PKHeX.Core
/// <summary>
/// Valid file extensions that represent <see cref="PKM"/> data, without the leading '.'
/// </summary>
public static readonly string[] Extensions = PKX.GetPKMExtensions();
public static readonly string[] Extensions = EntityFileExtension.GetExtensions();
public abstract int SIZE_PARTY { get; }
public abstract int SIZE_STORED { get; }
public string Extension => GetType().Name.ToLowerInvariant();
@ -637,7 +637,7 @@ namespace PKHeX.Core
if (gen is not (3 or 4 or 5))
return gender == (gender & 1);
return gender == PKX.GetGenderFromPIDAndRatio(PID, gv);
return gender == EntityGender.GetFromPIDAndRatio(PID, gv);
}
/// <summary>
@ -737,7 +737,7 @@ namespace PKHeX.Core
LoadStats(stats, p, level);
// Account for nature
PKX.ModifyStatsForNature(stats, StatNature);
NatureAmp.ModifyStatsForNature(stats, StatNature);
}
private void LoadStats(Span<ushort> stats, PersonalInfo p, IHyperTrain t, int level)
@ -888,7 +888,7 @@ namespace PKHeX.Core
public virtual void SetShiny()
{
var rnd = Util.Rand;
do { PID = PKX.GetRandomPID(rnd, Species, Gender, Version, Nature, Form, PID); }
do { PID = EntityPID.GetRandomPID(rnd, Species, Gender, Version, Nature, Form, PID); }
while (!IsShiny);
if (Format >= 6 && (Gen3 || Gen4 || Gen5))
EncryptionConstant = PID;
@ -923,7 +923,7 @@ namespace PKHeX.Core
public void SetPIDGender(int gender)
{
var rnd = Util.Rand;
do PID = PKX.GetRandomPID(rnd, Species, gender, Version, Nature, Form, PID);
do PID = EntityPID.GetRandomPID(rnd, Species, gender, Version, Nature, Form, PID);
while (IsShiny);
if (Format >= 6 && (Gen3 || Gen4 || Gen5))
EncryptionConstant = PID;
@ -939,7 +939,7 @@ namespace PKHeX.Core
public void SetPIDNature(int nature)
{
var rnd = Util.Rand;
do PID = PKX.GetRandomPID(rnd, Species, Gender, Version, nature, Form, PID);
do PID = EntityPID.GetRandomPID(rnd, Species, Gender, Version, nature, Form, PID);
while (IsShiny);
if (Format >= 6 && (Gen3 || Gen4 || Gen5))
EncryptionConstant = PID;
@ -956,7 +956,7 @@ namespace PKHeX.Core
public void SetPIDUnown3(int form)
{
var rnd = Util.Rand;
do PID = rnd.Rand32(); while (PKX.GetUnownForm(PID) != form);
do PID = rnd.Rand32(); while (EntityPID.GetUnownForm3(PID) != form);
if (Format >= 6 && (Gen3 || Gen4 || Gen5))
EncryptionConstant = PID;
}

View file

@ -29,7 +29,7 @@
public sealed override uint EncryptionConstant { get => PID; set { } }
public sealed override int Nature { get => (int)(PID % 25); set { } }
public sealed override bool IsNicknamed { get => SpeciesName.IsNicknamed(Species, Nickname, Language, 3); set { } }
public sealed override int Gender { get => PKX.GetGenderFromPID(Species, PID); set { } }
public sealed override int Gender { get => EntityGender.GetFromPID(Species, PID); set { } }
public sealed override int Characteristic => -1;
public sealed override int CurrentFriendship { get => OT_Friendship; set => OT_Friendship = value; }
public sealed override int CurrentHandler { get => 0; set { } }
@ -39,13 +39,13 @@
public sealed override int Form
{
get => Species == (int)Core.Species.Unown ? PKX.GetUnownForm(PID) : 0;
get => Species == (int)Core.Species.Unown ? EntityPID.GetUnownForm3(PID) : 0;
set
{
if (Species != (int)Core.Species.Unown)
return;
var rnd = Util.Rand;
while (PKX.GetUnownForm(PID) != value)
while (EntityPID.GetUnownForm3(PID) != value)
PID = rnd.Rand32();
}
}

View file

@ -0,0 +1,56 @@
using System;
using System.Collections.Generic;
using static System.Buffers.Binary.BinaryPrimitives;
namespace PKHeX.Core;
public static class EntityDetection
{
private static readonly HashSet<int> Sizes = new()
{
PokeCrypto.SIZE_1JLIST, PokeCrypto.SIZE_1ULIST,
PokeCrypto.SIZE_2ULIST, PokeCrypto.SIZE_2JLIST, PokeCrypto.SIZE_2STADIUM,
PokeCrypto.SIZE_3STORED, PokeCrypto.SIZE_3PARTY,
PokeCrypto.SIZE_3CSTORED, PokeCrypto.SIZE_3XSTORED,
PokeCrypto.SIZE_4STORED, PokeCrypto.SIZE_4PARTY,
PokeCrypto.SIZE_5PARTY,
PokeCrypto.SIZE_6STORED, PokeCrypto.SIZE_6PARTY,
PokeCrypto.SIZE_8STORED, PokeCrypto.SIZE_8PARTY,
PokeCrypto.SIZE_8ASTORED, PokeCrypto.SIZE_8APARTY,
};
/// <summary>
/// Determines if the given length is valid for a <see cref="PKM"/>.
/// </summary>
/// <param name="len">Data length of the file/array.</param>
/// <returns>A <see cref="bool"/> indicating whether or not the length is valid for a <see cref="PKM"/>.</returns>
public static bool IsSizePlausible(long len) => Sizes.Contains((int)len);
public static bool IsPresentGB(ReadOnlySpan<byte> data) => data[0] != 0; // Species non-zero
public static bool IsPresentGC(ReadOnlySpan<byte> data) => ReadUInt16BigEndian(data) != 0; // Species non-zero
public static bool IsPresentGBA(ReadOnlySpan<byte> data) => (data[0x13] & 0xFB) == 2; // ignore egg flag, must be FlagHasSpecies.
public static bool IsPresent(ReadOnlySpan<byte> data)
{
if (ReadUInt32LittleEndian(data) != 0) // PID
return true;
ushort species = ReadUInt16LittleEndian(data[8..]);
return species != 0;
}
/// <summary>
/// Gets a function that can check a byte array (at an offset) to see if a <see cref="PKM"/> is possibly present.
/// </summary>
/// <param name="blank"></param>
/// <returns>Function that checks if a byte array (at an offset) has a <see cref="PKM"/> present</returns>
public static Func<byte[], bool> GetFuncIsPresent(PKM blank)
{
if (blank.Format >= 4)
return x => IsPresent(x);
if (blank.Format <= 2)
return x => IsPresentGB(x);
if (blank.Data.Length <= PokeCrypto.SIZE_3PARTY)
return x => IsPresentGBA(x);
return x => IsPresentGC(x);
}
}

View file

@ -0,0 +1,67 @@
using System.Collections.Generic;
namespace PKHeX.Core;
public static class EntityFileExtension
{
private const string ExtensionPB7 = "pb7";
private const string ExtensionPB8 = "pb8";
private const string ExtensionPA8 = "pa8";
public static IReadOnlyList<string> Extensions7b => new[] { ExtensionPB7 };
/// <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(int maxGeneration = PKX.Generation)
{
var result = new List<string>();
int min = maxGeneration is <= 2 or >= 7 ? 1 : 3;
for (int i = min; i <= maxGeneration; i++)
result.Add($"pk{i}");
if (maxGeneration >= 3)
{
result.Add("ck3"); // colosseum
result.Add("xk3"); // xd
}
if (maxGeneration >= 4)
result.Add("bk4"); // battle revolution
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
return result.ToArray();
}
/// <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 int GetFormatFromExtension(string ext, int prefer)
{
if (string.IsNullOrEmpty(ext))
return prefer;
return 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>
public static int GetFormatFromExtension(char last, int prefer)
{
if (last is >= '1' and <= '9')
return last - '0';
return last == 'x' ? 6 : prefer;
}
}

View file

@ -15,7 +15,7 @@ public static class EntityFormat
/// <returns>An integer indicating the generation of the PKM file, or -1 if the data is invalid.</returns>
public static EntityFormatDetected GetFormat(ReadOnlySpan<byte> data)
{
if (!PKX.IsPKM(data.Length))
if (!EntityDetection.IsSizePlausible(data.Length))
return None;
return GetFormatInternal(data);

View file

@ -0,0 +1,47 @@
namespace PKHeX.Core;
public static class EntityGender
{
/// <summary>
/// Translates a Gender string to Gender integer.
/// </summary>
/// <param name="s">Gender string</param>
/// <returns>Gender integer</returns>
public static int GetFromString(string s)
{
if (s.Length != 1)
return 2;
return GetFromChar(s[0]);
}
/// <summary>
/// Translates a Gender char to Gender integer.
/// </summary>
public static int GetFromChar(char c) => c switch
{
'♂' or 'M' => 0,
'♀' or 'F' => 1,
_ => 2,
};
/// <summary>
/// Gets the gender ID of the species based on the Personality ID.
/// </summary>
/// <param name="species">National Dex ID.</param>
/// <param name="pid">Personality ID.</param>
/// <returns>Gender ID (0/1/2)</returns>
/// <remarks>This method should only be used for Generations 3-5 origin.</remarks>
public static int GetFromPID(int species, uint pid)
{
int gt = PKX.Personal[species].Gender;
return GetFromPIDAndRatio(pid, gt);
}
public static int GetFromPIDAndRatio(uint pid, int gr) => gr switch
{
PersonalInfo.RatioMagicGenderless => 2,
PersonalInfo.RatioMagicFemale => 1,
PersonalInfo.RatioMagicMale => 0,
_ => (pid & 0xFF) < gr ? 1 : 0,
};
}

View file

@ -0,0 +1,79 @@
using System;
namespace PKHeX.Core;
public static class EntityPID
{
/// <summary>
/// Gets a random PID according to specifications.
/// </summary>
/// <param name="rnd">RNG to use</param>
/// <param name="species">National Dex ID</param>
/// <param name="gender">Current Gender</param>
/// <param name="origin">Origin Generation</param>
/// <param name="nature">Nature</param>
/// <param name="form">Form</param>
/// <param name="oldPID">Current PID</param>
/// <remarks>Used to retain ability bits.</remarks>
/// <returns>Rerolled PID.</returns>
public static uint GetRandomPID(Random rnd, int species, int gender, int origin, int nature, int form, uint oldPID)
{
// Gen6+ (and VC) PIDs do not tie PID to Nature/Gender/Ability
if (origin >= 24)
return rnd.Rand32();
// Below logic handles Gen3-5.
// No need to get form specific entry, as Gen3-5 do not have that feature.
int gt = PKX.Personal[species].Gender;
bool g34 = origin <= 15;
uint abilBitVal = g34 ? oldPID & 0x0000_0001 : oldPID & 0x0001_0000;
bool g3unown = origin <= 5 && species == (int)Species.Unown;
bool singleGender = PersonalInfo.IsSingleGender(gt); // single gender, skip gender check
while (true) // Loop until we find a suitable PID
{
uint pid = rnd.Rand32();
// Gen 3/4: Nature derived from PID
if (g34 && pid % 25 != nature)
continue;
// Gen 3 Unown: Letter/form derived from PID
if (g3unown)
{
var pidLetter = GetUnownForm3(pid);
if (pidLetter != form)
continue;
}
else if (g34)
{
if (abilBitVal != (pid & 0x0000_0001)) // keep ability bits
continue;
}
else
{
if (abilBitVal != (pid & 0x0001_0000)) // keep ability bits
continue;
}
if (singleGender) // Set Gender(less)
return pid; // PID can be anything
// Gen 3/4/5: Gender derived from PID
if (gender == EntityGender.GetFromPIDAndRatio(pid, gt))
return pid;
}
}
/// <summary>
/// Gets the Unown Forme ID from PID.
/// </summary>
/// <param name="pid">Personality ID</param>
/// <remarks>Should only be used for 3rd Generation origin specimens.</remarks>
/// <returns></returns>
public static int GetUnownForm3(uint pid)
{
var value = (pid & 0x3000000) >> 18 | (pid & 0x30000) >> 12 | (pid & 0x300) >> 6 | (pid & 0x3);
return (int)(value % 28);
}
}

View file

@ -1,275 +1,25 @@
using System;
using System.Collections.Generic;
using static System.Buffers.Binary.BinaryPrimitives;
namespace PKHeX.Core
namespace PKHeX.Core;
/// <summary>
/// Common logic for <see cref="PKM"/> data providing and manipulation.
/// </summary>
public static class PKX
{
internal static readonly PersonalTable Personal = PersonalTable.LA;
public const int Generation = 8;
/// <summary>
/// Common logic for <see cref="PKM"/> data providing and manipulation.
/// Reorders (in place) the input array of stats to have the Speed value last rather than before the SpA/SpD stats.
/// </summary>
public static class PKX
/// <param name="value">Input array to reorder</param>
/// <returns>Same array, reordered.</returns>
public static void ReorderSpeedLast(Span<int> value)
{
internal static readonly PersonalTable Personal = PersonalTable.LA;
public const int Generation = 8;
private static readonly HashSet<int> Sizes = new()
{
PokeCrypto.SIZE_1JLIST, PokeCrypto.SIZE_1ULIST,
PokeCrypto.SIZE_2ULIST, PokeCrypto.SIZE_2JLIST, PokeCrypto.SIZE_2STADIUM,
PokeCrypto.SIZE_3STORED, PokeCrypto.SIZE_3PARTY,
PokeCrypto.SIZE_3CSTORED, PokeCrypto.SIZE_3XSTORED,
PokeCrypto.SIZE_4STORED, PokeCrypto.SIZE_4PARTY,
PokeCrypto.SIZE_5PARTY,
PokeCrypto.SIZE_6STORED, PokeCrypto.SIZE_6PARTY,
PokeCrypto.SIZE_8STORED, PokeCrypto.SIZE_8PARTY,
PokeCrypto.SIZE_8ASTORED, PokeCrypto.SIZE_8APARTY,
};
/// <summary>
/// Determines if the given length is valid for a <see cref="PKM"/>.
/// </summary>
/// <param name="len">Data length of the file/array.</param>
/// <returns>A <see cref="bool"/> indicating whether or not the length is valid for a <see cref="PKM"/>.</returns>
public static bool IsPKM(long len) => Sizes.Contains((int)len);
/// <summary>
/// Translates a Gender string to Gender integer.
/// </summary>
/// <param name="s">Gender string</param>
/// <returns>Gender integer</returns>
public static int GetGenderFromString(string s)
{
if (s.Length != 1)
return 2;
return GetGenderFromChar(s[0]);
}
private static int GetGenderFromChar(char c) => c switch
{
'♂' or 'M' => 0,
'♀' or 'F' => 1,
_ => 2,
};
/// <summary>
/// Gets the nature modification values and checks if they are equal.
/// </summary>
/// <param name="nature">Nature</param>
/// <param name="incr">Increased stat</param>
/// <param name="decr">Decreased stat</param>
/// <returns>True if nature modification values are equal or the Nature is out of range.</returns>
public static bool GetNatureModification(int nature, out int incr, out int decr)
{
incr = (nature / 5) + 1;
decr = (nature % 5) + 1;
return incr == decr || nature >= 25; // invalid
}
/// <summary>
/// Updates stats according to the specified nature.
/// </summary>
/// <param name="stats">Current stats to amplify if appropriate</param>
/// <param name="nature">Nature</param>
public static void ModifyStatsForNature(Span<ushort> stats, int nature)
{
if (GetNatureModification(nature, out int incr, out int decr))
return;
stats[incr] *= 11; stats[incr] /= 10;
stats[decr] *= 9; stats[decr] /= 10;
}
/// <summary>
/// Gets a random PID according to specifications.
/// </summary>
/// <param name="rnd">RNG to use</param>
/// <param name="species">National Dex ID</param>
/// <param name="gender">Current Gender</param>
/// <param name="origin">Origin Generation</param>
/// <param name="nature">Nature</param>
/// <param name="form">Form</param>
/// <param name="oldPID">Current PID</param>
/// <remarks>Used to retain ability bits.</remarks>
/// <returns>Rerolled PID.</returns>
public static uint GetRandomPID(Random rnd, int species, int gender, int origin, int nature, int form, uint oldPID)
{
// Gen6+ (and VC) PIDs do not tie PID to Nature/Gender/Ability
if (origin >= 24)
return rnd.Rand32();
// Below logic handles Gen3-5.
int gt = Personal[species].Gender;
bool g34 = origin <= 15;
uint abilBitVal = g34 ? oldPID & 0x0000_0001 : oldPID & 0x0001_0000;
bool g3unown = origin <= 5 && species == (int)Species.Unown;
bool singleGender = PersonalInfo.IsSingleGender(gt); // single gender, skip gender check
while (true) // Loop until we find a suitable PID
{
uint pid = rnd.Rand32();
// Gen 3/4: Nature derived from PID
if (g34 && pid%25 != nature)
continue;
// Gen 3 Unown: Letter/form derived from PID
if (g3unown)
{
var pidLetter = GetUnownForm(pid);
if (pidLetter != form)
continue;
}
else if (g34)
{
if (abilBitVal != (pid & 0x0000_0001)) // keep ability bits
continue;
}
else
{
if (abilBitVal != (pid & 0x0001_0000)) // keep ability bits
continue;
}
if (singleGender) // Set Gender(less)
return pid; // PID can be anything
// Gen 3/4/5: Gender derived from PID
if (gender == GetGenderFromPIDAndRatio(pid, gt))
return pid;
}
}
/// <summary>
/// Gets the Unown Forme ID from PID.
/// </summary>
/// <param name="pid">Personality ID</param>
/// <remarks>Should only be used for 3rd Generation origin specimens.</remarks>
/// <returns></returns>
public static int GetUnownForm(uint pid)
{
var value = (pid & 0x3000000) >> 18 | (pid & 0x30000) >> 12 | (pid & 0x300) >> 6 | (pid & 0x3);
return (int)(value % 28);
}
/// <summary>
/// Gets the gender ID of the species based on the Personality ID.
/// </summary>
/// <param name="species">National Dex ID.</param>
/// <param name="pid">Personality ID.</param>
/// <returns>Gender ID (0/1/2)</returns>
/// <remarks>This method should only be used for Generations 3-5 origin.</remarks>
public static int GetGenderFromPID(int species, uint pid)
{
int gt = Personal[species].Gender;
return GetGenderFromPIDAndRatio(pid, gt);
}
public static int GetGenderFromPIDAndRatio(uint pid, int gr) => gr switch
{
PersonalInfo.RatioMagicGenderless => 2,
PersonalInfo.RatioMagicFemale => 1,
PersonalInfo.RatioMagicMale => 0,
_ => (pid & 0xFF) < gr ? 1 : 0,
};
internal const string ExtensionPB7 = "pb7";
internal const string ExtensionPB8 = "pb8";
internal const string ExtensionPA8 = "pa8";
/// <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[] GetPKMExtensions(int maxGeneration = Generation)
{
var result = new List<string>();
int min = maxGeneration is <= 2 or >= 7 ? 1 : 3;
for (int i = min; i <= maxGeneration; i++)
result.Add($"pk{i}");
if (maxGeneration >= 3)
{
result.Add("ck3"); // colosseum
result.Add("xk3"); // xd
}
if (maxGeneration >= 4)
result.Add("bk4"); // battle revolution
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
return result.ToArray();
}
/// <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 int GetPKMFormatFromExtension(string ext, int prefer)
{
if (string.IsNullOrEmpty(ext))
return prefer;
return GetPKMFormatFromExtension(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>
public static int GetPKMFormatFromExtension(char last, int prefer)
{
if (last is >= '1' and <= '9')
return last - '0';
return last == 'x' ? 6 : prefer;
}
internal static bool IsPKMPresentGB(ReadOnlySpan<byte> data) => data[0] != 0; // Species non-zero
internal static bool IsPKMPresentGC(ReadOnlySpan<byte> data) => ReadUInt16BigEndian(data) != 0; // Species non-zero
internal static bool IsPKMPresentGBA(ReadOnlySpan<byte> data) => (data[0x13] & 0xFB) == 2; // ignore egg flag, must be FlagHasSpecies.
internal static bool IsPKMPresent(ReadOnlySpan<byte> data)
{
if (ReadUInt32LittleEndian(data) != 0) // PID
return true;
ushort species = ReadUInt16LittleEndian(data[8..]);
return species != 0;
}
/// <summary>
/// Gets a function that can check a byte array (at an offset) to see if a <see cref="PKM"/> is possibly present.
/// </summary>
/// <param name="blank"></param>
/// <returns>Function that checks if a byte array (at an offset) has a <see cref="PKM"/> present</returns>
public static Func<byte[], bool> GetFuncIsPKMPresent(PKM blank)
{
if (blank.Format >= 4)
return x => IsPKMPresent(x);
if (blank.Format <= 2)
return x => IsPKMPresentGB(x);
if (blank.Data.Length <= PokeCrypto.SIZE_3PARTY)
return x => IsPKMPresentGBA(x);
return x => IsPKMPresentGC(x);
}
/// <summary>
/// Reorders (in place) the input array of stats to have the Speed value last rather than before the SpA/SpD stats.
/// </summary>
/// <param name="value">Input array to reorder</param>
/// <returns>Same array, reordered.</returns>
public static void ReorderSpeedLast(Span<int> value)
{
var spe = value[3];
value[3] = value[4];
value[4] = value[5];
value[5] = spe;
}
var spe = value[3];
value[3] = value[4];
value[4] = value[5];
value[5] = spe;
}
}

View file

@ -234,7 +234,7 @@ namespace PKHeX.Core
public override bool HasParty => true;
private int StringLength => Japanese ? GBPKML.StringLengthJapanese : GBPKML.StringLengthNotJapan;
public override bool IsPKMPresent(ReadOnlySpan<byte> data) => PKX.IsPKMPresentGB(data);
public override bool IsPKMPresent(ReadOnlySpan<byte> data) => EntityDetection.IsPresentGB(data);
// Checksums
protected override void SetChecksums() => Data[Offsets.ChecksumOfs] = GetRBYChecksum(Offsets.OT, Offsets.ChecksumOfs);

View file

@ -271,7 +271,7 @@ namespace PKHeX.Core
public override int MaxMoney => 999999;
public override int MaxCoins => 9999;
public override bool IsPKMPresent(ReadOnlySpan<byte> data) => PKX.IsPKMPresentGB(data);
public override bool IsPKMPresent(ReadOnlySpan<byte> data) => EntityDetection.IsPresentGB(data);
public int EventWorkCount => 0x100;
public int EventFlagCount => 2000;

View file

@ -178,7 +178,7 @@ namespace PKHeX.Core
public sealed override bool HasParty => true;
public sealed override bool IsPKMPresent(ReadOnlySpan<byte> data) => PKX.IsPKMPresentGBA(data);
public sealed override bool IsPKMPresent(ReadOnlySpan<byte> data) => EntityDetection.IsPresentGBA(data);
protected sealed override PKM GetPKM(byte[] data) => new PK3(data);
protected sealed override byte[] DecryptPKM(byte[] data) => PokeCrypto.DecryptArray3(data);
@ -479,7 +479,7 @@ namespace PKHeX.Core
public uint DexPIDUnown { get => ReadUInt32LittleEndian(Small.AsSpan(PokeDex + 0x4)); set => WriteUInt32LittleEndian(Small.AsSpan(PokeDex + 0x4), value); }
public uint DexPIDSpinda { get => ReadUInt32LittleEndian(Small.AsSpan(PokeDex + 0x8)); set => WriteUInt32LittleEndian(Small.AsSpan(PokeDex + 0x8), value); }
public int DexUnownForm => PKX.GetUnownForm(DexPIDUnown);
public int DexUnownForm => EntityPID.GetUnownForm3(DexPIDUnown);
public sealed override bool GetCaught(int species)
{

View file

@ -153,7 +153,7 @@ namespace PKHeX.Core
public override int MaxMoney => 9999999;
public override int BoxCount => 3;
public override bool IsPKMPresent(ReadOnlySpan<byte> data) => PKX.IsPKMPresentGC(data);
public override bool IsPKMPresent(ReadOnlySpan<byte> data) => EntityDetection.IsPresentGC(data);
private static byte[] EncryptColosseum(ReadOnlySpan<byte> input, Span<byte> digest)
{

View file

@ -124,7 +124,7 @@ namespace PKHeX.Core
public override int BoxCount => 50;
public override bool HasParty => false;
public override bool IsPKMPresent(ReadOnlySpan<byte> data) => PKX.IsPKMPresentGBA(data);
public override bool IsPKMPresent(ReadOnlySpan<byte> data) => EntityDetection.IsPresentGBA(data);
// Checksums
protected override void SetChecksums() => Blocks.SetChecksums(Data);

View file

@ -187,7 +187,7 @@ namespace PKHeX.Core
public override int BoxCount => 8;
public override bool IsPKMPresent(ReadOnlySpan<byte> data) => PKX.IsPKMPresentGC(data);
public override bool IsPKMPresent(ReadOnlySpan<byte> data) => EntityDetection.IsPresentGC(data);
// Checksums
protected override void SetChecksums()

View file

@ -10,7 +10,7 @@ namespace PKHeX.Core
{
protected internal override string ShortSummary => $"{OT} ({Version}) - {Blocks.Played.LastSavedTime}";
public override string Extension => ".bin";
public override IReadOnlyList<string> PKMExtensions => new[] {PKX.ExtensionPB7};
public override IReadOnlyList<string> PKMExtensions => EntityFileExtension.Extensions7b;
public override Type PKMType => typeof(PB7);
public override PKM BlankPKM => new PB7();

View file

@ -352,7 +352,7 @@ namespace PKHeX.Core
public abstract IReadOnlyList<ushort> HeldItems { get; }
protected virtual byte[] BoxBuffer => Data;
protected virtual byte[] PartyBuffer => Data;
public virtual bool IsPKMPresent(ReadOnlySpan<byte> data) => PKX.IsPKMPresent(data);
public virtual bool IsPKMPresent(ReadOnlySpan<byte> data) => EntityDetection.IsPresent(data);
public virtual PKM GetDecryptedPKM(byte[] data) => GetPKM(DecryptPKM(data));
public virtual PKM GetPartySlot(byte[] data, int offset) => GetDecryptedPKM(GetData(data, offset, SIZE_PARTY));
public virtual PKM GetStoredSlot(byte[] data, int offset) => GetDecryptedPKM(GetData(data, offset, SIZE_STORED));

View file

@ -174,7 +174,7 @@ namespace PKHeX.Core
public static IEnumerable<PKM> GetPKMsFromPaths(IEnumerable<string> files, int generation)
{
var result = files
.Where(file => PKX.IsPKM(new FileInfo(file).Length))
.Where(file => EntityDetection.IsSizePlausible(new FileInfo(file).Length))
.Select(File.ReadAllBytes)
.Select(data => EntityFormat.GetFromBytes(data, prefer: generation));

View file

@ -195,7 +195,7 @@ namespace PKHeX.Core
var di = new DirectoryInfo(templatePath);
string path = Path.Combine(templatePath, $"{di.Name}.{sav.PKMType.Name.ToLowerInvariant()}");
if (!File.Exists(path) || !PKX.IsPKM(new FileInfo(path).Length))
if (!File.Exists(path) || !EntityDetection.IsSizePlausible(new FileInfo(path).Length))
return LoadTemplateInternal(sav);
var pk = EntityFormat.GetFromBytes(File.ReadAllBytes(path), prefer: sav.Generation);

View file

@ -172,7 +172,7 @@ namespace PKHeX.Core
pk = null;
return false;
}
var format = PKX.GetPKMFormatFromExtension(ext, sav?.Generation ?? 6);
var format = EntityFileExtension.GetFormatFromExtension(ext, sav?.Generation ?? 6);
pk = EntityFormat.GetFromBytes(data, prefer: format);
return pk != null;
}
@ -192,7 +192,7 @@ namespace PKHeX.Core
return false;
}
var length = data.Length;
if (PKX.IsPKM(length / sav.SlotCount) || PKX.IsPKM(length / sav.BoxSlotCount))
if (EntityDetection.IsSizePlausible(length / sav.SlotCount) || EntityDetection.IsSizePlausible(length / sav.BoxSlotCount))
{
pkms = ArrayUtil.EnumerateSplit(data, length);
return true;
@ -253,7 +253,7 @@ namespace PKHeX.Core
return null;
if (fi.Length == GP1.SIZE && TryGetGP1(File.ReadAllBytes(file), out var gp1))
return gp1.ConvertToPB7(sav);
if (!PKX.IsPKM(fi.Length) && !MysteryGift.IsMysteryGift(fi.Length))
if (!EntityDetection.IsSizePlausible(fi.Length) && !MysteryGift.IsMysteryGift(fi.Length))
return null;
var data = File.ReadAllBytes(file);
var ext = fi.Extension;

View file

@ -385,7 +385,7 @@ namespace PKHeX.WinForms.Controls
private static string ReloadGender(string text, IReadOnlyList<string> genders)
{
var index = PKX.GetGenderFromString(text);
var index = EntityGender.GetFromString(text);
if (index >= 2)
return text;
return genders[index];
@ -605,7 +605,7 @@ namespace PKHeX.WinForms.Controls
}
Entity.Gender = gender;
if (PKX.GetGenderFromString(CB_Form.Text) < 2) // Gendered Forms
if (EntityGender.GetFromString(CB_Form.Text) < 2) // Gendered Forms
CB_Form.SelectedIndex = Math.Min(gender, CB_Form.Items.Count - 1);
UpdatePreviewSprite?.Invoke(UC_Gender, EventArgs.Empty);
@ -987,7 +987,7 @@ namespace PKHeX.WinForms.Controls
}
}
}
else if (CB_Form.Enabled && PKX.GetGenderFromString(CB_Form.Text) < 2)
else if (CB_Form.Enabled && EntityGender.GetFromString(CB_Form.Text) < 2)
{
if (CB_Form.Items.Count == 2) // actually M/F; Pumpkaboo formes in German are S,M,L,XL
{

View file

@ -21,8 +21,7 @@ namespace PKHeX.WinForms.Controls
MT_Base = new[] {TB_BaseHP, TB_BaseATK, TB_BaseDEF, TB_BaseSPE, TB_BaseSPA, TB_BaseSPD};
TB_BST.ResetForeColor();
TB_IVTotal.ForeColor = MT_EVs[0].ForeColor;
TB_EVTotal.ForeColor = MT_EVs[0].ForeColor;
TB_IVTotal.ForeColor = TB_EVTotal.ForeColor = MT_EVs[0].ForeColor;
}
public Color EVsInvalid { get; set; } = Color.Red;
@ -315,6 +314,9 @@ namespace PKHeX.WinForms.Controls
private void ClickStatLabel(object sender, MouseEventArgs e)
{
if (Entity.Format < 3)
return;
if (ModifierKeys == Keys.None)
return;
@ -322,22 +324,17 @@ namespace PKHeX.WinForms.Controls
if (index < 0)
return;
if (Entity.Format < 3)
var request = ModifierKeys switch
{
Keys.Control => NatureAmpRequest.Neutral,
Keys.Alt => NatureAmpRequest.Decrease,
_ => NatureAmpRequest.Increase,
};
var newNature = request.GetNewNature(index, Entity.StatNature);
if (newNature == -1)
return;
var current = Entity.StatNature;
var up = current / 5;
var dn = current % 5;
switch (ModifierKeys)
{
case Keys.Shift when up != index: up = index; break;
case Keys.Alt when dn != index: dn = index; break;
case Keys.Control when up != index && dn != index: up = dn = index; break;
default:
return;
}
var newNature = (up * 5) + dn;
MainEditor.ChangeNature(newNature);
}
@ -459,16 +456,19 @@ namespace PKHeX.WinForms.Controls
public string UpdateNatureModification(int nature)
{
// Reset Label Colors
for (var i = 1; i < L_Stats.Length; i++)
L_Stats[i].ResetForeColor();
foreach (var l in L_Stats)
l.ResetForeColor();
// Set Colored StatLabels only if Nature isn't Neutral
if (PKX.GetNatureModification(nature, out int incr, out int decr))
var (up, dn) = NatureAmp.GetNatureModification(nature);
if (NatureAmp.IsNeutralOrInvalid(nature, up, dn))
return "-/-";
L_Stats[incr].ForeColor = StatIncreased;
L_Stats[decr].ForeColor = StatDecreased;
return $"+{L_Stats[incr].Text} / -{L_Stats[decr].Text}".Replace(":", "");
var incr = L_Stats[up];
var decr = L_Stats[dn];
incr.ForeColor = StatIncreased;
decr.ForeColor = StatDecreased;
return $"+{incr.Text} / -{decr.Text}".Replace(":", "");
}
public void SetATKIVGender(int gender)
@ -565,12 +565,13 @@ namespace PKHeX.WinForms.Controls
{
int current = ganbaru.GetGV(i);
var max = ganbaru.GetMax(entity, i);
var tb = MT_GVs[i];
if (current > max)
MT_GVs[i].BackColor = EVsInvalid;
tb.BackColor = EVsInvalid;
else if (current == max)
MT_GVs[i].BackColor = StatHyperTrained;
tb.BackColor = StatHyperTrained;
else
MT_GVs[i].ResetBackColor();
tb.ResetBackColor();
}
public void ToggleInterface(PKM pk, int gen)
@ -589,14 +590,14 @@ namespace PKHeX.WinForms.Controls
Label_SPA.Visible = false;
Label_SPC.Visible = true;
TB_IVHP.Enabled = false;
SetEVMaskSize(Stat_HP.Size, "00000");
SetEVMaskSize(Stat_HP.Size, "00000", MT_EVs);
break;
case 2:
FLP_SpD.Visible = true;
Label_SPA.Visible = true;
Label_SPC.Visible = false;
TB_IVHP.Enabled = false;
SetEVMaskSize(Stat_HP.Size, "00000");
SetEVMaskSize(Stat_HP.Size, "00000", MT_EVs);
TB_EVSPD.Enabled = TB_IVSPD.Enabled = false;
break;
default:
@ -604,7 +605,7 @@ namespace PKHeX.WinForms.Controls
Label_SPA.Visible = true;
Label_SPC.Visible = false;
TB_IVHP.Enabled = true;
SetEVMaskSize(TB_EVTotal.Size, "000");
SetEVMaskSize(TB_EVTotal.Size, "000", MT_EVs);
TB_EVSPD.Enabled = TB_IVSPD.Enabled = true;
break;
}
@ -622,9 +623,9 @@ namespace PKHeX.WinForms.Controls
foreach (var mtb in MT_GVs)
mtb.Visible = showGV;
void SetEVMaskSize(Size s, string Mask)
static void SetEVMaskSize(Size s, string Mask, MaskedTextBox[] arr)
{
foreach (var ctrl in MT_EVs)
foreach (var ctrl in arr)
{
ctrl.Size = s;
ctrl.Mask = Mask;

View file

@ -289,7 +289,7 @@ namespace PKHeX.WinForms
private void TryProcess(string source, string destDir, IReadOnlyList<StringInstruction> metaFilters, IReadOnlyList<StringInstruction> pkFilters, IReadOnlyList<StringInstruction> instructions)
{
var fi = new FileInfo(source);
if (!PKX.IsPKM(fi.Length))
if (!EntityDetection.IsSizePlausible(fi.Length))
return;
byte[] data = File.ReadAllBytes(source);

View file

@ -215,13 +215,13 @@ namespace PKHeX.WinForms
TID = Convert.ToUInt16(TB_TID.Text),
SID = Convert.ToUInt16(TB_SID.Text),
Form = (uint)CB_Form.SelectedIndex,
Gender = (uint)PKX.GetGenderFromString(Label_Gender.Text) & 0x3,
Gender = (uint)EntityGender.GetFromString(Label_Gender.Text) & 0x3,
Level = Convert.ToUInt16(TB_Level.Text),
IsShiny = CHK_Shiny.Checked,
IsNicknamed = CHK_Nicknamed.Checked,
Nickname = TB_Nickname.Text,
OT_Name = TB_OT.Text,
OT_Gender = (uint)PKX.GetGenderFromString(Label_OTGender.Text) & 1,
OT_Gender = (uint)EntityGender.GetFromString(Label_OTGender.Text) & 1,
};
offset = index * 0x1B4;
@ -295,7 +295,7 @@ namespace PKHeX.WinForms
var species = WinFormsUtil.GetIndex(CB_Species);
var form = CB_Form.SelectedIndex & 0x1F;
var gender = PKX.GetGenderFromString(Label_Gender.Text);
var gender = EntityGender.GetFromString(Label_Gender.Text);
var item = WinFormsUtil.GetIndex(CB_HeldItem);
bpkx.Image = SpriteUtil.GetSprite(species, form, gender, 0, item, false, CHK_Shiny.Checked, 6);
@ -304,7 +304,7 @@ namespace PKHeX.WinForms
private void UpdateOTGender(object sender, EventArgs e)
{
var g = PKX.GetGenderFromString(Label_OTGender.Text);
var g = EntityGender.GetFromString(Label_OTGender.Text);
Label_OTGender.Text = gendersymbols[g ^ 1];
Write_Entry(this, EventArgs.Empty);
@ -317,7 +317,7 @@ namespace PKHeX.WinForms
var pi = SAV.Personal[species];
if (pi.IsDualGender)
{
var fg = PKX.GetGenderFromString(Label_Gender.Text);
var fg = EntityGender.GetFromString(Label_Gender.Text);
fg = (fg ^ 1) & 1;
Label_Gender.Text = gendersymbols[fg];
}
@ -328,14 +328,14 @@ namespace PKHeX.WinForms
return;
}
var g = PKX.GetGenderFromString(CB_Form.Text);
var g = EntityGender.GetFromString(CB_Form.Text);
if (g == 0 && Label_Gender.Text != gendersymbols[0])
CB_Form.SelectedIndex = 1;
else if (g == 1 && Label_Gender.Text != gendersymbols[1])
CB_Form.SelectedIndex = 0;
if (species == (int)Species.Pyroar)
CB_Form.SelectedIndex = PKX.GetGenderFromString(Label_Gender.Text);
CB_Form.SelectedIndex = EntityGender.GetFromString(Label_Gender.Text);
Write_Entry(this, EventArgs.Empty);
}

View file

@ -190,7 +190,7 @@ namespace PKHeX.WinForms
pkm.Ability = WinFormsUtil.GetIndex(CB_Ability);
pkm.AbilityNumber = CB_Ability.SelectedIndex << 1;
pkm.Nature = WinFormsUtil.GetIndex(CB_Nature);
pkm.Gender = PKX.GetGenderFromString(Label_Gender.Text);
pkm.Gender = EntityGender.GetFromString(Label_Gender.Text);
pkm.Form = CB_Form.SelectedIndex;
pkm.EV_HP = Math.Min(Convert.ToInt32(TB_HPEV.Text), 252);
pkm.EV_ATK = Math.Min(Convert.ToInt32(TB_ATKEV.Text), 252);
@ -347,7 +347,7 @@ namespace PKHeX.WinForms
// Set a sane gender
var gender = SAV.Personal[species].FixedGender;
if (gender == -1)
gender = PKX.GetGenderFromString(Label_Gender.Text);
gender = EntityGender.GetFromString(Label_Gender.Text);
SetGenderLabel(gender);
SetAbilityList();
@ -358,7 +358,7 @@ namespace PKHeX.WinForms
SetAbilityList();
// If form has a single gender, account for it.
if (PKX.GetGenderFromString(CB_Form.Text) < 2)
if (EntityGender.GetFromString(CB_Form.Text) < 2)
Label_Gender.Text = Main.GenderSymbols[CB_Form.SelectedIndex];
}
@ -369,7 +369,7 @@ namespace PKHeX.WinForms
var fg = pi.FixedGender;
if (fg == -1) // dual gender
{
fg = PKX.GetGenderFromString(Label_Gender.Text);
fg = EntityGender.GetFromString(Label_Gender.Text);
fg = (fg ^ 1) & 1;
}
Label_Gender.Text = Main.GenderSymbols[fg];

View file

@ -65,10 +65,10 @@ namespace PKHeX.Tests.Legality
{
var fi = new FileInfo(file);
fi.Should().NotBeNull($"the test file '{file}' should be a valid file");
PKX.IsPKM(fi.Length).Should().BeTrue($"the test file '{file}' should have a valid file length");
EntityDetection.IsSizePlausible(fi.Length).Should().BeTrue($"the test file '{file}' should have a valid file length");
var data = File.ReadAllBytes(file);
var format = PKX.GetPKMFormatFromExtension(file[^1], -1);
var format = EntityFileExtension.GetFormatFromExtension(file[^1], -1);
format.Should().BeLessOrEqualTo(PKX.Generation, "filename is expected to have a valid extension");
var dn = fi.DirectoryName ?? string.Empty;