mirror of
https://github.com/kwsch/PKHeX
synced 2024-11-10 06:34:19 +00:00
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:
parent
f5c6510b82
commit
0674d72fae
26 changed files with 149 additions and 12 deletions
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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"/>.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
10
PKHeX.Core/PKM/Strings/Trash/ITrashIntrospection.cs
Normal file
10
PKHeX.Core/PKM/Strings/Trash/ITrashIntrospection.cs
Normal 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();
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue