Add more xmldoc

This commit is contained in:
Kurt
2025-05-03 23:55:06 -05:00
parent ae3bb75fe6
commit 0e0d812d83
16 changed files with 218 additions and 56 deletions

View File

@@ -128,15 +128,9 @@ private static bool IsMunchlaxTree(ReadOnlySpan<byte> trees, ushort location)
58, // 20 Floaroma Meadow
];
/// <summary>
/// Checks if the Unown form is valid for the given <see cref="PKM"/>.
/// </summary>
public static bool IsUnownFormValid(PKM pk, byte form)
{
return pk.HGSS
? RuinsOfAlph4.IsFormValid(pk, form)
: SolaceonRuins4.IsFormValid(pk, form);
}
public static bool IsUnownFormValid(PKM pk, byte form, bool isRuinsOfAlph) => isRuinsOfAlph
? RuinsOfAlph4.IsFormValid(pk, form)
: SolaceonRuins4.IsFormValid(pk, form);
}
/// <summary>

View File

@@ -165,7 +165,7 @@ public EncounterMatchRating GetMatchRating(PKM pk)
}
if (IsDeferredWurmple(pk))
return EncounterMatchRating.PartialMatch;
if (pk.Species == (int)Core.Species.Unown && !EncounterArea4.IsUnownFormValid(pk, pk.Form))
if (Species == (int)Core.Species.Unown && !EncounterArea4.IsUnownFormValid(pk, pk.Form, Location is RuinsOfAlph4.Location))
return EncounterMatchRating.PartialMatch;
return EncounterMatchRating.Match;
}

View File

@@ -5,6 +5,10 @@ namespace PKHeX.Core;
/// <summary>
/// Indicates the source of a <see cref="Move"/> for a <see cref="PKM"/>.
/// </summary>
/// <remarks>
/// This enum is mostly a "lumping" enum to pivot searching logic.
/// See <see cref="LearnMethod"/> for the more-specific tracking enumeration.
/// </remarks>
[Flags]
public enum MoveSourceType
{

View File

@@ -2,8 +2,16 @@
namespace PKHeX.Core;
/// <summary>
/// Logic for <see cref="EntityContext.Gen4"/> Ruins of Alph Unown forms.
/// </summary>
public static class RuinsOfAlph4
{
/// <summary>
/// Met location for the Ruins of Alph.
/// </summary>
public const ushort Location = 209;
/// <summary>
/// Checks if the requested <see cref="form"/> is valid for the given seed.
/// </summary>

View File

@@ -2,8 +2,16 @@
namespace PKHeX.Core;
/// <summary>
/// Logic for <see cref="EntityContext.Gen4"/> Solaceon Ruins Unown forms.
/// </summary>
public static class SolaceonRuins4
{
/// <summary>
/// Met location for the Solaceon Ruins.
/// </summary>
public const ushort Location = 53;
/// <summary>
/// Checks if the requested <see cref="form"/> is valid for the given seed.
/// </summary>

View File

@@ -296,12 +296,12 @@ public ushort ShinyXor
public bool VC => VC1 || VC2;
public bool GG => LGPE || GO_LGPE;
public bool Gen9 => SV;
public bool Gen8 => Version is >= SW and <= SP || GO_HOME;
public bool Gen7 => Version is >= SN and <= UM || GG;
public bool Gen6 => Version is >= X and <= OR;
public bool Gen5 => Version is >= W and <= B2;
public bool Gen4 => Version is HG or SS or D or P or GameVersion.Pt;
public bool Gen3 => Version is (>= S and <= LG) or CXD;
public bool Gen8 => Version.IsGen8() || GO_HOME;
public bool Gen7 => Version.IsGen7();
public bool Gen6 => Version.IsGen6();
public bool Gen5 => Version.IsGen5();
public bool Gen4 => Version.IsGen4();
public bool Gen3 => Version.IsGen3();
public bool Gen2 => Version == GSC; // Fixed value set by the Gen2 PKM classes
public bool Gen1 => Version == RBY; // Fixed value set by the Gen1 PKM classes
public bool GenU => Generation <= 0;
@@ -312,7 +312,7 @@ public byte Generation
{
if (Gen9) return 9;
if (Gen8) return 8;
if (Gen7) return 7;
if (Gen7 || GG) return 7;
if (Gen6) return 6;
if (Gen5) return 5;
if (Gen4) return 4;

View File

@@ -2,9 +2,37 @@
namespace PKHeX.Core;
/// <summary>
/// Interface for converting between byte arrays and strings.
/// </summary>
/// <remarks>
/// Different implementations are used for different string encodings.
/// These vary across <see cref="EntityContext"/>.
/// </remarks>
public interface IStringConverter
{
/// <summary>
/// Reads a string from a byte array.
/// </summary>
/// <param name="data">Source data to decode.</param>
string GetString(ReadOnlySpan<byte> data);
/// <summary>
/// Loads a string character by character into the <see cref="text"/> array until a terminator is found or the end of the buffer is reached.
/// </summary>
/// <param name="data">Source data to decode.</param>
/// <param name="text">Resulting string buffer.</param>
/// <returns>Count of characters written to <see cref="text"/>.</returns>
int LoadString(ReadOnlySpan<byte> data, Span<char> text);
/// <summary>
/// Converts a string to a byte array.
/// </summary>
/// <param name="data">Destination data to store the encoded string.</param>
/// <param name="text">Source string to encode.</param>
/// <param name="length">Maximum length of the <see cref="text"/> to encode.</param>
/// <param name="option">Buffer conditioning option.</param>
/// <returns>Count of bytes written to <see cref="data"/>.</returns>
int SetString(Span<byte> data, ReadOnlySpan<char> text, int length, StringConverterOption option);
}

View File

@@ -2,6 +2,10 @@
namespace PKHeX.Core;
/// <summary>
/// Logic for converting a <see cref="string"/> for Generation 2.
/// </summary>
/// <remarks>Slight differences when compared to <seealso cref="StringConverter1"/>.</remarks>
public static class StringConverter2
{
public const byte TerminatorCode = StringConverter1.TerminatorCode;

View File

@@ -169,7 +169,7 @@ public static void TransferGlyphs78(Span<byte> str)
private static void TrimHalfSpaces(Span<char> u16)
{
// If a replacement is made, any leading or trailing halfwidth spaces are trimmed.
// If a replacement is made, any leading or trailing half-width spaces are trimmed.
// This allows nicknames/OT names that are the empty string or consist entirely of fullwidth spaces.
int length = u16.IndexOf((char)TerminatorNull);
if (length == -1)

View File

@@ -2,9 +2,34 @@
namespace PKHeX.Core;
/// <summary>
/// Interface for introspection of trash data.
/// </summary>
/// <remarks>
/// Implementation abstractions for accessing results from
/// <see cref="TrashBytesUTF16"/>,
/// <see cref="TrashBytesGB"/>,
/// <see cref="TrashBytes8"/>
/// depending on the type's string encoding.
/// </remarks>
public interface ITrashIntrospection
{
/// <summary>
/// Gets the index of the string terminator in the given data.
/// </summary>
/// <param name="data">The data to search.</param>
/// <returns>Character index of the string terminator.</returns>
int GetStringTerminatorIndex(ReadOnlySpan<byte> data);
/// <summary>
/// Gets the length of the string based on the terminator or end of the data.
/// </summary>
/// <param name="data">Span of data to check the length of.</param>
/// <returns>Count of characters in the string.</returns>
int GetStringLength(ReadOnlySpan<byte> data);
/// <summary>
/// Gets the amount of bytes per character in the string encoding.
/// </summary>
int GetBytesPerChar();
}

View File

@@ -24,7 +24,38 @@ public static class EntityCharacteristic
public static int GetCharacteristic(uint ec, uint iv32)
{
int index = (int)(ec % 6);
var (maxStatIndex, maxStatValue) = GetMaxStat(iv32, index);
return GetCharacteristic(maxStatIndex, maxStatValue);
}
/// <summary>
/// Gets the characteristic index of the given unpacked IVs.
/// </summary>
/// <param name="ec">Encryption Constant.</param>
/// <param name="ivs">Unpacked IVs.</param>
/// <returns>Characteristic index.</returns>
public static int GetCharacteristic(uint ec, Span<int> ivs)
{
int index = (int)(ec % 6);
var (maxStatIndex, maxStatValue) = GetMaxStat(ivs, index);
return GetCharacteristic(maxStatIndex, maxStatValue);
}
/// <summary>
/// Gets the characteristic index of the given IVs when the IVs are stored in reverse order (HP highest bits).
/// </summary>
/// <param name="ec">Encryption Constant.</param>
/// <param name="iv32">Lumped IVs with HP occupying the highest bits.</param>
/// <returns>Characteristic index.</returns>
public static int GetCharacteristicInvertFields(uint ec, uint iv32)
{
int index = (int)(ec % 6);
var (maxStatIndex, maxStatValue) = GetMaxStat32Invert(iv32, index);
return GetCharacteristic(maxStatIndex, maxStatValue);
}
private static (int MaxStatIndex, int MaxStatValue) GetMaxStat(uint iv32, int index)
{
// Get individual IVs from the lumped value.
// The IVs are stored in the following order: HP, Atk, Def, Spe, SpA, SpD
// Check all IVs, get the highest IV and its index. If there are multiple highest IVs, the first index checked is chosen.
@@ -44,19 +75,11 @@ public static int GetCharacteristic(uint ec, uint iv32)
index = 0;
} while (maxStatIndex != index);
return GetCharacteristic(maxStatIndex, (int)maxStatValue);
return (maxStatIndex, (int)maxStatValue);
}
/// <summary>
/// Gets the characteristic index of the given unpacked IVs.
/// </summary>
/// <param name="ec">Encryption Constant.</param>
/// <param name="ivs">Unpacked IVs.</param>
/// <returns>Characteristic index.</returns>
public static int GetCharacteristic(uint ec, Span<int> ivs)
private static (int MaxStatIndex, int MaxStatValue) GetMaxStat(Span<int> ivs, int index)
{
int index = (int)(ec % 6);
// Get individual IVs from the lumped value.
// The IVs are stored in the following order: HP, Atk, Def, Spe, SpA, SpD
// Check all IVs, get the highest IV and its index. If there are multiple highest IVs, the first index checked is chosen.
@@ -76,22 +99,12 @@ public static int GetCharacteristic(uint ec, Span<int> ivs)
index = 0;
} while (maxStatIndex != index);
return GetCharacteristic(maxStatIndex, maxStatValue);
return (maxStatIndex, maxStatValue);
}
/// <summary>
/// Gets the characteristic index of the given IVs in Big Endian format.
/// </summary>
/// <param name="ec">Encryption Constant.</param>
/// <param name="iv32">Lumped IVs in Big Endian format.</param>
/// <returns>Characteristic index.</returns>
public static int GetCharacteristicInvertFields(uint ec, uint iv32)
private static (int MaxStatIndex, int MaxStatValue) GetMaxStat32Invert(uint iv32, int index)
{
int index = (int)(ec % 6);
// Get individual IVs from the lumped value.
// The IVs are stored in the following order: SpD, SpA, Spe, Def, Atk, HP
// Check all IVs, get the highest IV and its index. If there are multiple highest IVs, the first index checked is chosen.
// Same as GetMaxStat, but for formats where the IVs are stored in reverse order.
int maxStatIndex = index;
var maxStatValue = 0u;
do
@@ -109,6 +122,6 @@ public static int GetCharacteristicInvertFields(uint ec, uint iv32)
index = 0;
} while (maxStatIndex != index);
return GetCharacteristic(maxStatIndex, (int)maxStatValue);
return (maxStatIndex, (int)maxStatValue);
}
}

View File

@@ -26,10 +26,28 @@ SIZE_5PARTY or
SIZE_8ASTORED or SIZE_8APARTY
;
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
/// <summary>
/// Checks the first byte of the span to see if the species is non-zero.
/// </summary>
public static bool IsPresentGB(ReadOnlySpan<byte> data) => data[0] != 0;
/// <summary>
/// Checks the first two bytes of the span to see if the species is non-zero.
/// </summary>
public static bool IsPresentGC(ReadOnlySpan<byte> data) => ReadUInt16BigEndian(data) != 0;
/// <summary>
/// Checks the flag status of the span to see if it has the <see cref="PK3.FlagHasSpecies"/> indicator.
/// </summary>
public static bool IsPresentGBA(ReadOnlySpan<byte> data) => (data[0x13] & 0xFB) == 2; // ignore egg flag, must be FlagHasSpecies.
public static bool IsPresentSAV4Ranch(ReadOnlySpan<byte> data) => ReadUInt32LittleEndian(data) != 0 && ReadUInt32BigEndian(data) != 0x28; // Species non-zero, ignore file end marker
/// <summary>
/// Checks if the species is non-zero and the entity PID is not the "file end" marker of the save file.
/// </summary>
/// <remarks>
/// Only useful when called from a <see cref="SAV4Ranch"/> file, not for a <see cref="PK4"/> entity dump.
/// </remarks>
public static bool IsPresentSAV4Ranch(ReadOnlySpan<byte> data) => IsPresent(data) && ReadUInt32BigEndian(data) != 0x28; // Species non-zero, ignore file end marker
public static bool IsPresent(ReadOnlySpan<byte> data)
{
@@ -40,18 +58,17 @@ public static bool IsPresent(ReadOnlySpan<byte> data)
}
/// <summary>
/// Gets a function that can check a byte array (at an offset) to see if a <see cref="PKM"/> is possibly present.
/// Gets a function that can check a span 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)
/// <returns>Function that checks if a span has a <see cref="PKM"/> present</returns>
public static Func<ReadOnlySpan<byte>, bool> GetFuncIsPresent(PKM blank) => blank switch
{
if (blank.Format >= 4)
return x => IsPresent(x);
if (blank.Format <= 2)
return x => IsPresentGB(x);
if (blank.Data.Length <= SIZE_3PARTY)
return x => IsPresentGBA(x);
return x => IsPresentGC(x);
}
{ Format: >= 4 } => IsPresent,
{ Format: <= 2 } => IsPresentGB,
// Gen3; ^above handles all other formats.
PK3 => IsPresentGBA,
_ => IsPresentGC,
};
}

View File

@@ -211,6 +211,12 @@ private static EntityFormatDetected IsFormatReally8b(PK8 pk)
}
}
/// <summary>
/// Enum representing the detected format of a Pokémon entity.
/// </summary>
/// <remarks>
/// Roughly correlated to derived <see cref="PKM"/> types, besides the "one-of" range of enum values.
/// </remarks>
public enum EntityFormatDetected
{
None,

View File

@@ -46,6 +46,13 @@ public static byte GetFromPID(ushort species, uint pid)
return GetFromPIDAndRatio(pid, gt);
}
/// <summary>
/// Gets the gender from the <see cref="pid"/> and <see cref="gr"/> values.
/// </summary>
/// <param name="pid">Personality ID.</param>
/// <param name="gr">Gender Ratio.</param>
/// <returns>Gender ID (0/1/2)</returns>
/// <remarks>This method should only be used for Generations 3-5 origin.</remarks>
public static byte GetFromPIDAndRatio(uint pid, byte gr) => gr switch
{
PersonalInfo.RatioMagicGenderless => Genderless,

View File

@@ -61,7 +61,21 @@ public interface IPersonalTable
/// <typeparam name="T">Specific type of <see cref="IPersonalInfo"/> the table contains.</typeparam>
public interface IPersonalTable<out T> where T : IPersonalInfo
{
/// <summary>
/// Gets the entry from the table based on absolute index, not species/form.
/// </summary>
/// <remarks>
/// Only use this if not requesting based on Form.
/// </remarks>
T this[int index] { get; }
/// <summary>
/// Gets the entry from the table based on species/form.
/// </summary>
T this[ushort species, byte form] { get; }
/// <summary>
/// Gets the entry from the table based on species/form.
/// </summary>
T GetFormEntry(ushort species, byte form);
}

View File

@@ -34,6 +34,11 @@ public static class SCTypeCodeExtensions
{
public static bool IsBoolean(this SCTypeCode type) => unchecked((uint)type - 1u) < 3;
/// <summary>
/// Gets the number of bytes occupied by a variable of a given type.
/// </summary>
/// <param name="type">Type of the value</param>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public static int GetTypeSize(this SCTypeCode type) => type switch
{
SCTypeCode.Bool3 => sizeof(bool),
@@ -54,6 +59,12 @@ public static class SCTypeCodeExtensions
_ => throw new ArgumentOutOfRangeException(nameof(type), type.ToString()),
};
/// <summary>
/// Gets the runtime <see cref="Type"/> of the value.
/// </summary>
/// <param name="type">Type of the value</param>
/// <remarks>If <see cref="SCTypeCode.Array"/>, use <see cref="GetTypeArray"/>.</remarks>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public static Type GetType(this SCTypeCode type) => type switch
{
SCTypeCode.Byte => typeof(byte),
@@ -72,6 +83,29 @@ public static class SCTypeCodeExtensions
_ => throw new ArgumentOutOfRangeException(nameof(type), type.ToString()),
};
/// <summary>
/// Gets the runtime <see cref="Type"/> of the array.
/// </summary>
/// <param name="type">Type of the array</param>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public static Type GetTypeArray(this SCTypeCode type) => type switch
{
SCTypeCode.Byte => typeof(byte[]),
SCTypeCode.UInt16 => typeof(ushort[]),
SCTypeCode.UInt32 => typeof(uint[]),
SCTypeCode.UInt64 => typeof(ulong[]),
SCTypeCode.SByte => typeof(sbyte[]),
SCTypeCode.Int16 => typeof(short[]),
SCTypeCode.Int32 => typeof(int[]),
SCTypeCode.Int64 => typeof(long[]),
SCTypeCode.Single => typeof(float[]),
SCTypeCode.Double => typeof(double[]),
_ => throw new ArgumentOutOfRangeException(nameof(type), type.ToString()),
};
public static object GetValue(this SCTypeCode type, ReadOnlySpan<byte> data)
{
// don't use a switch expression here, we want to box our underlying type rather than the last type (double)