Add ITrashIntrospection, impl on PKM classes

new api allows for checking for trash byte metadata, makes it much easier to write a verifier now
This commit is contained in:
Kurt 2024-05-17 15:58:49 -05:00
parent f5c6510b82
commit 0674d72fae
26 changed files with 149 additions and 12 deletions

View file

@ -319,4 +319,9 @@ public sealed class BK4 : G4PKM
=> StringConverter4.LoadString(data, destBuffer);
public override int SetString(Span<byte> destBuffer, ReadOnlySpan<char> value, int maxLength, StringConverterOption option)
=> StringConverter4.SetString(destBuffer, value, maxLength, Language, option);
public override int GetStringTerminatorIndex(ReadOnlySpan<byte> data)
=> TrashBytes.GetTerminatorIndex(data, StringConverter4.Terminator);
public override int GetStringLength(ReadOnlySpan<byte> data)
=> TrashBytes.GetStringLength(data, StringConverter4.Terminator);
public override int GetBytesPerChar() => 2;
}

View file

@ -237,4 +237,9 @@ public sealed class CK3(byte[] Data) : G3PKM(Data), IShadowCapture
=> StringConverter3GC.LoadString(data, destBuffer);
public override int SetString(Span<byte> destBuffer, ReadOnlySpan<char> value, int maxLength, StringConverterOption option)
=> StringConverter3GC.SetString(destBuffer, value, maxLength, option);
public override int GetStringTerminatorIndex(ReadOnlySpan<byte> data)
=> TrashBytes.GetTerminatorIndex(data, StringConverter3GC.TerminatorBigEndian);
public override int GetStringLength(ReadOnlySpan<byte> data)
=> TrashBytes.GetStringLength(data, StringConverter3GC.TerminatorBigEndian);
public override int GetBytesPerChar() => 2;
}

View file

@ -432,6 +432,11 @@ public sealed class PKH : PKM, IHandlerLanguage, IFormArgument, IHomeTrack, IBat
=> StringConverter8.LoadString(data, destBuffer);
public override int SetString(Span<byte> destBuffer, ReadOnlySpan<char> value, int maxLength, StringConverterOption option)
=> StringConverter8.SetString(destBuffer, value, maxLength, option);
public override int GetStringTerminatorIndex(ReadOnlySpan<byte> data)
=> TrashBytes.GetTerminatorIndex(data);
public override int GetStringLength(ReadOnlySpan<byte> data)
=> TrashBytes.GetStringLength(data);
public override int GetBytesPerChar() => 2;
/// <summary>
/// Revises the Handler details of a <see cref="PKM"/> to match the current <see cref="ITrainerInfo"/>.

View file

@ -738,4 +738,9 @@ public sealed class PA8 : PKM, ISanityChecksum,
=> StringConverter8.LoadString(data, destBuffer);
public override int SetString(Span<byte> destBuffer, ReadOnlySpan<char> value, int maxLength, StringConverterOption option)
=> StringConverter8.SetString(destBuffer, value, maxLength, option);
public override int GetStringTerminatorIndex(ReadOnlySpan<byte> data)
=> TrashBytes.GetTerminatorIndex(data);
public override int GetStringLength(ReadOnlySpan<byte> data)
=> TrashBytes.GetStringLength(data);
public override int GetBytesPerChar() => 2;
}

View file

@ -593,4 +593,9 @@ public sealed class PB7 : G6PKM, IHyperTrain, IAwakened, IScaledSizeValue, IComb
=> StringConverter8.LoadString(data, destBuffer);
public override int SetString(Span<byte> destBuffer, ReadOnlySpan<char> value, int maxLength, StringConverterOption option)
=> StringConverter8.SetString(destBuffer, value, maxLength, option);
public override int GetStringTerminatorIndex(ReadOnlySpan<byte> data)
=> TrashBytes.GetTerminatorIndex(data);
public override int GetStringLength(ReadOnlySpan<byte> data)
=> TrashBytes.GetStringLength(data);
public override int GetBytesPerChar() => 2;
}

View file

@ -141,4 +141,9 @@ public sealed class PB8 : G8PKM
=> StringConverter8.LoadString(data, destBuffer);
public override int SetString(Span<byte> destBuffer, ReadOnlySpan<char> value, int maxLength, StringConverterOption option)
=> StringConverter8.SetString(destBuffer, value, maxLength, option);
public override int GetStringTerminatorIndex(ReadOnlySpan<byte> data)
=> TrashBytes.GetTerminatorIndex(data);
public override int GetStringLength(ReadOnlySpan<byte> data)
=> TrashBytes.GetStringLength(data);
public override int GetBytesPerChar() => 2;
}

View file

@ -254,6 +254,11 @@ public sealed class PK1 : GBPKML, IPersonalType
=> StringConverter1.LoadString(data, destBuffer, Japanese);
public override int SetString(Span<byte> destBuffer, ReadOnlySpan<char> value, int maxLength, StringConverterOption option)
=> StringConverter1.SetString(destBuffer, value, maxLength, Japanese, option);
public override int GetStringTerminatorIndex(ReadOnlySpan<byte> data)
=> TrashBytes.GetTerminatorIndex(data, StringConverter4.Terminator);
public override int GetStringLength(ReadOnlySpan<byte> data)
=> TrashBytes.GetStringLength(data, StringConverter4.Terminator);
public override int GetBytesPerChar() => 2;
/// <summary>
/// Gets a checksum over all the entity's data using a single list to wrap all components.

View file

@ -279,6 +279,11 @@ public sealed class PK2 : GBPKML, ICaughtData2
return StringConverter2KOR.SetString(destBuffer, value, maxLength, option);
return StringConverter2.SetString(destBuffer, value, maxLength, Language, option);
}
public override int GetStringTerminatorIndex(ReadOnlySpan<byte> data)
=> Korean ? StringConverter2KOR.GetTerminatorIndex(data) : TrashBytesGB.GetTerminatorIndex(data);
public override int GetStringLength(ReadOnlySpan<byte> data)
=> Korean ? StringConverter2KOR.GetStringLength(data) : TrashBytesGB.GetStringLength(data);
public override int GetBytesPerChar() => 1;
/// <summary>
/// Gets a checksum over all the entity's data using a single list to wrap all components.

View file

@ -362,4 +362,9 @@ public sealed class PK3 : G3PKM, ISanityChecksum
=> StringConverter3.LoadString(data, destBuffer, Language);
public override int SetString(Span<byte> destBuffer, ReadOnlySpan<char> value, int maxLength, StringConverterOption option)
=> StringConverter3.SetString(destBuffer, value, maxLength, Language, option);
public override int GetStringTerminatorIndex(ReadOnlySpan<byte> data)
=> TrashBytes8.GetTerminatorIndex(data);
public override int GetStringLength(ReadOnlySpan<byte> data)
=> TrashBytes8.GetStringLength(data);
public override int GetBytesPerChar() => 1;
}

View file

@ -395,4 +395,9 @@ public sealed class PK4 : G4PKM
=> StringConverter4.LoadString(data, destBuffer);
public override int SetString(Span<byte> destBuffer, ReadOnlySpan<char> value, int maxLength, StringConverterOption option)
=> StringConverter4.SetString(destBuffer, value, maxLength, Language, option);
public override int GetStringTerminatorIndex(ReadOnlySpan<byte> data)
=> TrashBytes.GetTerminatorIndex(data, StringConverter4.Terminator);
public override int GetStringLength(ReadOnlySpan<byte> data)
=> TrashBytes.GetStringLength(data, StringConverter4.Terminator);
public override int GetBytesPerChar() => 2;
}

View file

@ -572,6 +572,11 @@ public sealed class PK5 : PKM, ISanityChecksum,
=> StringConverter5.LoadString(data, destBuffer);
public override int SetString(Span<byte> destBuffer, ReadOnlySpan<char> value, int maxLength, StringConverterOption option)
=> StringConverter5.SetString(destBuffer, value, maxLength, Language, option);
public override int GetStringTerminatorIndex(ReadOnlySpan<byte> data)
=> TrashBytes.GetTerminatorIndex(data, StringConverter5.Terminator);
public override int GetStringLength(ReadOnlySpan<byte> data)
=> TrashBytes.GetStringLength(data, StringConverter5.Terminator);
public override int GetBytesPerChar() => 2;
/// <inheritdoc cref="G4PKM.CheckKoreanNidoranDPPt"/>
/// <remarks> Gen4->Gen5 chars transfer without resetting the name. Still relevant even as PK5. </remarks>

View file

@ -531,4 +531,9 @@ public sealed class PK6 : G6PKM, IRibbonSetEvent3, IRibbonSetEvent4, IRibbonSetC
=> StringConverter6.LoadString(data, destBuffer);
public override int SetString(Span<byte> destBuffer, ReadOnlySpan<char> value, int maxLength, StringConverterOption option)
=> StringConverter6.SetString(destBuffer, value, maxLength, Language, option);
public override int GetStringTerminatorIndex(ReadOnlySpan<byte> data)
=> TrashBytes.GetTerminatorIndex(data);
public override int GetStringLength(ReadOnlySpan<byte> data)
=> TrashBytes.GetStringLength(data);
public override int GetBytesPerChar() => 2;
}

View file

@ -556,6 +556,11 @@ public sealed class PK7 : G6PKM, IRibbonSetEvent3, IRibbonSetEvent4, IRibbonSetC
=> StringConverter7.LoadString(data, destBuffer);
public override int SetString(Span<byte> destBuffer, ReadOnlySpan<char> value, int maxLength, StringConverterOption option)
=> StringConverter7.SetString(destBuffer, value, maxLength, Language);
public override int GetStringTerminatorIndex(ReadOnlySpan<byte> data)
=> TrashBytes.GetTerminatorIndex(data);
public override int GetStringLength(ReadOnlySpan<byte> data)
=> TrashBytes.GetStringLength(data);
public override int GetBytesPerChar() => 2;
}
public enum ResortEventState : byte

View file

@ -124,4 +124,9 @@ public sealed class PK8 : G8PKM, IHandlerUpdate
=> StringConverter8.LoadString(data, destBuffer);
public override int SetString(Span<byte> destBuffer, ReadOnlySpan<char> value, int maxLength, StringConverterOption option)
=> StringConverter8.SetString(destBuffer, value, maxLength, option);
public override int GetStringTerminatorIndex(ReadOnlySpan<byte> data)
=> TrashBytes.GetTerminatorIndex(data);
public override int GetStringLength(ReadOnlySpan<byte> data)
=> TrashBytes.GetStringLength(data);
public override int GetBytesPerChar() => 2;
}

View file

@ -677,4 +677,9 @@ public sealed class PK9 : PKM, ISanityChecksum, ITeraType, ITechRecord, IObedien
=> StringConverter8.LoadString(data, destBuffer);
public override int SetString(Span<byte> destBuffer, ReadOnlySpan<char> value, int maxLength, StringConverterOption option)
=> StringConverter8.SetString(destBuffer, value, maxLength, option);
public override int GetStringTerminatorIndex(ReadOnlySpan<byte> data)
=> TrashBytes.GetTerminatorIndex(data);
public override int GetStringLength(ReadOnlySpan<byte> data)
=> TrashBytes.GetStringLength(data);
public override int GetBytesPerChar() => 2;
}

View file

@ -11,7 +11,7 @@ namespace PKHeX.Core;
/// Object representing a <see cref="PKM"/>'s data and derived properties.
/// </summary>
[DynamicallyAccessedMembers(PublicProperties | NonPublicProperties | PublicParameterlessConstructor)]
public abstract class PKM : ISpeciesForm, ITrainerID32, IGeneration, IShiny, ILangNick, IGameValueLimit, INature, IFatefulEncounter, IStringConverter
public abstract class PKM : ISpeciesForm, ITrainerID32, IGeneration, IShiny, ILangNick, IGameValueLimit, INature, IFatefulEncounter, IStringConverter, ITrashIntrospection
{
/// <summary>
/// Valid file extensions that represent <see cref="PKM"/> data, without the leading '.'
@ -151,6 +151,9 @@ public abstract class PKM : ISpeciesForm, ITrainerID32, IGeneration, IShiny, ILa
public abstract string GetString(ReadOnlySpan<byte> data);
public abstract int LoadString(ReadOnlySpan<byte> data, Span<char> text);
public abstract int SetString(Span<byte> data, ReadOnlySpan<char> text, int length, StringConverterOption option);
public abstract int GetStringTerminatorIndex(ReadOnlySpan<byte> data);
public abstract int GetStringLength(ReadOnlySpan<byte> data);
public abstract int GetBytesPerChar();
/// <summary>
/// The date the Pokémon was met.

View file

@ -357,4 +357,9 @@ public sealed class RK4 : G4PKM
=> StringConverter4.LoadString(data, destBuffer);
public override int SetString(Span<byte> destBuffer, ReadOnlySpan<char> value, int maxLength, StringConverterOption option)
=> StringConverter4.SetString(destBuffer, value, maxLength, Language, option);
public override int GetStringTerminatorIndex(ReadOnlySpan<byte> data)
=> TrashBytes.GetTerminatorIndex(data, StringConverter4.Terminator);
public override int GetStringLength(ReadOnlySpan<byte> data)
=> TrashBytes.GetStringLength(data, StringConverter4.Terminator);
public override int GetBytesPerChar() => 2;
}

View file

@ -221,4 +221,9 @@ public sealed class SK2 : GBPKM, ICaughtData2
=> StringConverter2.LoadString(data, destBuffer, Language);
public override int SetString(Span<byte> destBuffer, ReadOnlySpan<char> value, int maxLength, StringConverterOption option)
=> StringConverter2.SetString(destBuffer, value, maxLength, Language, option);
public override int GetStringTerminatorIndex(ReadOnlySpan<byte> data)
=> TrashBytesGB.GetTerminatorIndex(data);
public override int GetStringLength(ReadOnlySpan<byte> data)
=> TrashBytesGB.GetStringLength(data);
public override int GetBytesPerChar() => 1;
}

View file

@ -76,6 +76,40 @@ public static class StringConverter2KOR
return ctr;
}
/// <summary>
/// Gets the count of characters in the data.
/// </summary>
public static int GetStringLength(ReadOnlySpan<byte> data)
{
int length = 0;
for (var i = 0; i < data.Length; i++)
{
byte value = data[i];
if (value is TableInvalid or TerminatorCode)
break;
if (value <= TableMax)
i++; // Korean char takes 2 bytes
length++;
}
return length;
}
/// <summary>
/// Gets the index of the first terminator in the data.
/// </summary>
public static int GetTerminatorIndex(ReadOnlySpan<byte> data)
{
for (var i = 0; i < data.Length; i++)
{
byte value = data[i];
if (value is TableInvalid or TerminatorCode)
return i;
if (value <= TableMax)
i++; // Korean char takes 2 bytes
}
return -1;
}
/// <summary>
/// Converts a string to Generation 1 encoded data.
/// </summary>

View file

@ -8,7 +8,7 @@ namespace PKHeX.Core;
/// </summary>
public static class StringConverter3GC
{
private const char TerminatorBigEndian = (char)0; // GC
public const char TerminatorBigEndian = (char)0; // GC
/// <summary>Converts Big Endian encoded data to decoded string.</summary>
/// <param name="data">Encoded data</param>

View file

@ -8,7 +8,7 @@ namespace PKHeX.Core;
/// </summary>
public static class StringConverter5
{
private const char TerminatorFFFF = (char)0xFFFF;
public const char Terminator = (char)0xFFFF;
/// <summary>Converts Generation 5 encoded data to decoded string.</summary>
/// <param name="data">Encoded data</param>
@ -31,7 +31,7 @@ public static class StringConverter5
for (; i < data.Length; i += 2)
{
var value = ReadUInt16LittleEndian(data[i..]);
if (value == TerminatorFFFF)
if (value == Terminator)
break;
result[ctr++] = (char)value;
}
@ -68,7 +68,7 @@ public static class StringConverter5
int count = value.Length * 2;
if (count == destBuffer.Length)
return count;
WriteUInt16LittleEndian(destBuffer[count..], TerminatorFFFF);
WriteUInt16LittleEndian(destBuffer[count..], Terminator);
return count + 2;
}
}

View file

@ -0,0 +1,10 @@
using System;
namespace PKHeX.Core;
public interface ITrashIntrospection
{
int GetStringTerminatorIndex(ReadOnlySpan<byte> data);
int GetStringLength(ReadOnlySpan<byte> data);
int GetBytesPerChar();
}

View file

@ -16,7 +16,7 @@ public static class TrashBytes
/// <returns>Decoded index (char) of the terminator, or max length if not found.</returns>
public static int GetStringLength(ReadOnlySpan<byte> buffer, ushort terminator = 0)
{
int index = FindTerminatorIndex(buffer, terminator);
int index = GetTerminatorIndex(buffer, terminator);
return index == -1 ? buffer.Length / 2 : index;
}
@ -27,7 +27,7 @@ public static class TrashBytes
/// <param name="terminator">Terminator character to search for.</param>
/// <returns>Decoded index (char) of the terminator, or -1 if not found.</returns>
/// <remarks>When used on a raw string, returns the computed length of the string, assuming a terminator is present.</remarks>
public static int FindTerminatorIndex(ReadOnlySpan<byte> buffer, ushort terminator = 0)
public static int GetTerminatorIndex(ReadOnlySpan<byte> buffer, ushort terminator = 0)
{
var u16 = MemoryMarshal.Cast<byte, ushort>(buffer);
return u16.IndexOf(terminator);

View file

@ -10,7 +10,7 @@ public static class TrashBytes8
/// <inheritdoc cref="TrashBytes.GetStringLength"/>
public static int GetStringLength(ReadOnlySpan<byte> buffer)
{
int index = FindTerminatorIndex(buffer);
int index = GetTerminatorIndex(buffer);
return index == -1 ? buffer.Length : index;
}
@ -19,6 +19,6 @@ public static class TrashBytes8
/// </summary>
/// <param name="buffer">Backing buffer of the string.</param>
/// <returns>Index of the terminator, or -1 if not found.</returns>
public static int FindTerminatorIndex(ReadOnlySpan<byte> buffer)
public static int GetTerminatorIndex(ReadOnlySpan<byte> buffer)
=> buffer.IndexOf<byte>(0xFF);
}

View file

@ -11,15 +11,15 @@ public static class TrashBytesGB
/// <inheritdoc cref="TrashBytes.GetStringLength"/>
public static int GetStringLength(ReadOnlySpan<byte> buffer)
{
int index = FindTerminatorIndex(buffer);
int index = GetTerminatorIndex(buffer);
return index == -1 ? buffer.Length : index;
}
/// <summary>
/// Returns a 16-bit aligned index of the terminator.
/// Returns a 8-bit aligned index of the terminator.
/// </summary>
/// <param name="buffer">Backing buffer of the string.</param>
/// <returns>Index of the terminator, or -1 if not found.</returns>
public static int FindTerminatorIndex(ReadOnlySpan<byte> buffer)
public static int GetTerminatorIndex(ReadOnlySpan<byte> buffer)
=> buffer.IndexOfAny(TerminatorZero, TerminatorCode);
}

View file

@ -241,4 +241,9 @@ public sealed class XK3 : G3PKM, IShadowCapture
=> StringConverter3GC.LoadString(data, destBuffer);
public override int SetString(Span<byte> destBuffer, ReadOnlySpan<char> value, int maxLength, StringConverterOption option)
=> StringConverter3GC.SetString(destBuffer, value, maxLength, option);
public override int GetStringTerminatorIndex(ReadOnlySpan<byte> data)
=> TrashBytes.GetTerminatorIndex(data, StringConverter3GC.TerminatorBigEndian);
public override int GetStringLength(ReadOnlySpan<byte> data)
=> TrashBytes.GetStringLength(data, StringConverter3GC.TerminatorBigEndian);
public override int GetBytesPerChar() => 2;
}