mirror of
https://github.com/kwsch/PKHeX
synced 2024-11-10 06:34:19 +00:00
Add Trash Byte verification for Switch formats (#4283)
* Extract logic, finish impl for switch era * Extract trash checks to static class * Reduce some allocations in OT name compares
This commit is contained in:
parent
ca2bd3baf4
commit
c10c2a0dc2
40 changed files with 435 additions and 127 deletions
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using static PKHeX.Core.LanguageID;
|
||||
using static PKHeX.Core.EncounterGift2.TrainerType;
|
||||
|
||||
|
@ -227,8 +228,6 @@ public sealed record EncounterGift2
|
|||
private bool IsTrainerNameValid(PKM pk) => Trainer switch
|
||||
{
|
||||
Recipient => true,
|
||||
GiftStadiumJPN => pk.OriginalTrainerName == StadiumJPN,
|
||||
GiftStadiumENG => pk.OriginalTrainerName == StadiumENG,
|
||||
GiftStadiumINT => pk.OriginalTrainerName switch
|
||||
{
|
||||
StadiumGER => true,
|
||||
|
@ -237,10 +236,26 @@ public sealed record EncounterGift2
|
|||
StadiumSPA => true,
|
||||
_ => false,
|
||||
},
|
||||
PokemonCenterNewYork => IsTrainerPCNY(pk.OriginalTrainerName),
|
||||
GiftStadiumJPN => IsTrainerName(pk, StadiumJPN),
|
||||
GiftStadiumENG => IsTrainerName(pk, StadiumENG),
|
||||
PokemonCenterNewYork => IsTrainerPCNY(pk),
|
||||
_ => true,
|
||||
};
|
||||
|
||||
private static bool IsTrainerPCNY(PKM pk)
|
||||
{
|
||||
Span<char> ot = stackalloc char[pk.MaxStringLengthTrainer];
|
||||
int len = pk.LoadString(pk.OriginalTrainerTrash, ot);
|
||||
return IsTrainerPCNY(ot[..len]);
|
||||
}
|
||||
|
||||
private static bool IsTrainerName(PKM pk, [ConstantExpected] string name)
|
||||
{
|
||||
Span<char> ot = stackalloc char[pk.MaxStringLengthTrainer];
|
||||
int len = pk.LoadString(pk.OriginalTrainerTrash, ot);
|
||||
return ot[..len].SequenceEqual(name);
|
||||
}
|
||||
|
||||
private bool IsTrainerIDValid(ITrainerID16 pk) => Trainer switch
|
||||
{
|
||||
Recipient => true,
|
||||
|
|
|
@ -48,6 +48,9 @@ public abstract record EncounterStatic8Nest<T>(GameVersion Version)
|
|||
|
||||
public PK8 ConvertToPKM(ITrainerInfo tr) => ConvertToPKM(tr, EncounterCriteria.Unrestricted);
|
||||
|
||||
protected virtual void SetTrainerName(ReadOnlySpan<char> name, PK8 pk) =>
|
||||
pk.SetString(pk.OriginalTrainerTrash, name, name.Length, StringConverterOption.None);
|
||||
|
||||
public PK8 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
|
||||
{
|
||||
var version = this.GetCompatibleVersion(tr.Version);
|
||||
|
@ -67,7 +70,6 @@ public abstract record EncounterStatic8Nest<T>(GameVersion Version)
|
|||
Version = version,
|
||||
Language = lang,
|
||||
OriginalTrainerGender = tr.Gender,
|
||||
OriginalTrainerName = tr.OT,
|
||||
OriginalTrainerFriendship = pi.BaseFriendship,
|
||||
|
||||
Nickname = SpeciesName.GetSpeciesNameGeneration(Species, lang, Generation),
|
||||
|
@ -75,6 +77,7 @@ public abstract record EncounterStatic8Nest<T>(GameVersion Version)
|
|||
DynamaxLevel = DynamaxLevel,
|
||||
CanGigantamax = CanGigantamax,
|
||||
};
|
||||
SetTrainerName(tr.OT, pk);
|
||||
|
||||
SetPINGA(pk, criteria, pi);
|
||||
|
||||
|
|
|
@ -41,6 +41,16 @@ public sealed record EncounterStatic8U : EncounterStatic8Nest<EncounterStatic8U>
|
|||
}
|
||||
protected override ushort GetLocation() => Location;
|
||||
|
||||
protected override void SetTrainerName(ReadOnlySpan<char> name, PK8 pk)
|
||||
{
|
||||
if (ShouldHaveScientistTrash)
|
||||
{
|
||||
var scientist = GetScientistName(pk.Language);
|
||||
pk.SetString(pk.OriginalTrainerTrash, scientist, scientist.Length, StringConverterOption.None);
|
||||
}
|
||||
base.SetTrainerName(name, pk);
|
||||
}
|
||||
|
||||
// no downleveling, unlike all other raids
|
||||
protected override bool IsMatchLevel(PKM pk) => pk.MetLevel == Level;
|
||||
protected override bool IsMatchLocation(PKM pk) => Location == pk.MetLocation;
|
||||
|
@ -50,31 +60,6 @@ public sealed record EncounterStatic8U : EncounterStatic8Nest<EncounterStatic8U>
|
|||
public bool ShouldHaveScientistTrash => !SpeciesCategory.IsLegendary(Species)
|
||||
&& !SpeciesCategory.IsSubLegendary(Species);
|
||||
|
||||
protected override void FinishCorrelation(PK8 pk, ulong seed)
|
||||
{
|
||||
if (!ShouldHaveScientistTrash)
|
||||
return;
|
||||
|
||||
ApplyTrashBytes(pk);
|
||||
}
|
||||
|
||||
public void ApplyTrashBytes(PKM pk)
|
||||
{
|
||||
// Normally we would apply the trash before applying the OT, but we already did.
|
||||
// Just add in the expected trash after the OT.
|
||||
var ot = pk.OriginalTrainerTrash;
|
||||
var language = pk.Language;
|
||||
var scientist = GetScientistName(language);
|
||||
StringConverter8.ApplyTrashBytes(ot, scientist);
|
||||
}
|
||||
|
||||
public static TrashMatch HasScientistTrash(PKM pk)
|
||||
{
|
||||
var language = pk.Language;
|
||||
var name = GetScientistName(language);
|
||||
return StringConverter8.GetTrashState(pk.OriginalTrainerTrash, name);
|
||||
}
|
||||
|
||||
public static ReadOnlySpan<char> GetScientistName(int language) => language switch
|
||||
{
|
||||
(int)LanguageID.Japanese => "けんきゅういん",
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
namespace PKHeX.Core;
|
||||
|
||||
public interface ITrashUnderlaySpecies
|
||||
{
|
||||
bool IsTrashUnderlaySpecies(PKM pk);
|
||||
}
|
|
@ -57,6 +57,7 @@ public static class LegalityCheckStrings
|
|||
|
||||
public static string L_XOT { get; set; } = "OT";
|
||||
public static string L_XHT { get; set; } = "HT";
|
||||
public static string L_XNickname { get; set; } = "Nickname";
|
||||
public static string L_XKorean { get; set; } = "Korean";
|
||||
public static string L_XKoreanNon { get; set; } = "Non-Korean";
|
||||
public static string L_XEnigmaBerry_0 { get; set; } = "{0} Berry";
|
||||
|
@ -499,6 +500,14 @@ public static class LegalityCheckStrings
|
|||
public static string LTransferPIDECXor { get; set; } = "Encryption Constant matches shinyxored PID.";
|
||||
public static string LTransferTrackerMissing { get; set; } = "Pokémon HOME Transfer Tracker is missing.";
|
||||
public static string LTransferTrackerShouldBeZero { get; set; } = "Pokémon HOME Transfer Tracker should be 0.";
|
||||
|
||||
public static string LTrashBytesExpected_0 { get; set; } = "Expected Trash Bytes: {0}";
|
||||
public static string LTrashBytesExpected { get; set; } = "Expected Trash Bytes.";
|
||||
public static string LTrashBytesMismatchInitial { get; set; } = "Expected initial trash bytes to match the encounter.";
|
||||
public static string LTrashBytesMissingTerminator { get; set; } = "Final terminator missing.";
|
||||
public static string LTrashBytesShouldBeEmpty { get; set; } = "Trash Bytes should be cleared.";
|
||||
public static string LTrashBytesUnexpected { get; set; } = "Unexpected Trash Bytes.";
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
|
|
|
@ -327,6 +327,7 @@ public sealed class LegalityAnalysis
|
|||
HyperTraining.Verify(this);
|
||||
MiscValues.VerifyVersionEvolution(this);
|
||||
|
||||
Trash.Verify(this);
|
||||
if (format < 8)
|
||||
return;
|
||||
|
||||
|
|
|
@ -35,4 +35,5 @@ internal static class LegalityAnalyzers
|
|||
public static readonly MarkVerifier Mark = new();
|
||||
public static readonly LegendsArceusVerifier Arceus = new();
|
||||
public static readonly AwakenedValueVerifier Awakening = new();
|
||||
public static readonly TrashByteVerifier Trash = new();
|
||||
}
|
||||
|
|
|
@ -86,7 +86,7 @@ public sealed class TrainerNameVerifier : Verifier
|
|||
if (e is IFixedTrainer { IsFixedTrainer: true })
|
||||
return true; // already verified
|
||||
|
||||
if (e is MysteryGift mg && mg.OriginalTrainerName.Length == ot.Length)
|
||||
if (e is WC3 { Species: (int)Species.HoOh } mattle && pk is CK3 && mattle.OriginalTrainerName.Length == ot.Length)
|
||||
return true; // Mattle Ho-Oh
|
||||
return false;
|
||||
}
|
||||
|
|
165
PKHeX.Core/Legality/Verifiers/TrashByteVerifier.cs
Normal file
165
PKHeX.Core/Legality/Verifiers/TrashByteVerifier.cs
Normal file
|
@ -0,0 +1,165 @@
|
|||
using System;
|
||||
using static PKHeX.Core.LegalityCheckStrings;
|
||||
using static PKHeX.Core.StringSource;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Verifies the trash bytes of various strings.
|
||||
/// </summary>
|
||||
public sealed class TrashByteVerifier : Verifier
|
||||
{
|
||||
protected override CheckIdentifier Identifier => CheckIdentifier.TrashBytes;
|
||||
|
||||
private static string Format(StringSource s) => s switch
|
||||
{
|
||||
Nickname => L_XNickname,
|
||||
OriginalTrainer => L_XOT,
|
||||
HandlingTrainer => L_XHT,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(s)),
|
||||
};
|
||||
|
||||
private static string Format(StringSource s, string msg) => string.Format(L_F0_1, Format(s), msg);
|
||||
|
||||
public override void Verify(LegalityAnalysis data)
|
||||
{
|
||||
var pk = data.Entity;
|
||||
if (pk.Format >= 8 || pk.Context == EntityContext.Gen7b)
|
||||
{
|
||||
VerifyTrashBytesHOME(data, pk);
|
||||
}
|
||||
else if (pk.Format == 4)
|
||||
{
|
||||
var enc = data.EncounterMatch;
|
||||
if (enc is PCD pcd)
|
||||
VerifyTrashBytesPCD(data, pk, pcd);
|
||||
else if (enc.Generation == 3)
|
||||
VerifyTrashBytesPalPark(data, pk);
|
||||
}
|
||||
}
|
||||
|
||||
private void VerifyTrashBytesPalPark(LegalityAnalysis data, PKM pk)
|
||||
{
|
||||
if (pk.Japanese)
|
||||
{
|
||||
// Trash bytes should be zero.
|
||||
if (!TrashBytesUTF16.IsTrashEmpty(pk.OriginalTrainerTrash))
|
||||
data.AddLine(GetInvalid(Format(Nickname, LTrashBytesShouldBeEmpty)));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Should have trash bytes from the transfer process.
|
||||
if (TrashBytesUTF16.IsTrashEmpty(pk.OriginalTrainerTrash))
|
||||
data.AddLine(GetInvalid(Format(Nickname, LTrashBytesExpected)));
|
||||
}
|
||||
}
|
||||
|
||||
private void VerifyTrashBytesPCD(LegalityAnalysis data, PKM pk, PCD pcd)
|
||||
{
|
||||
var enc = pcd.Gift.PK;
|
||||
var ot = enc.OriginalTrainerTrash;
|
||||
if (!ot.SequenceEqual(pk.OriginalTrainerTrash))
|
||||
data.AddLine(GetInvalid(Format(OriginalTrainer, LTrashBytesMismatchInitial)));
|
||||
|
||||
if (pcd.Species != pk.Species)
|
||||
return; // Evolved, trash bytes are rewritten.
|
||||
|
||||
var nick = enc.NicknameTrash;
|
||||
if (!nick.SequenceEqual(pk.NicknameTrash))
|
||||
data.AddLine(GetInvalid(Format(Nickname, LTrashBytesMismatchInitial)));
|
||||
}
|
||||
|
||||
private void VerifyTrashBytesHOME(LegalityAnalysis data, PKM pk)
|
||||
{
|
||||
if (!TrashBytesUTF16.IsFinalTerminatorPresent(pk.NicknameTrash))
|
||||
data.AddLine(GetInvalid(Format(Nickname, LTrashBytesMissingTerminator)));
|
||||
if (!TrashBytesUTF16.IsFinalTerminatorPresent(pk.OriginalTrainerTrash))
|
||||
data.AddLine(GetInvalid(Format(OriginalTrainer, LTrashBytesMissingTerminator)));
|
||||
if (!TrashBytesUTF16.IsFinalTerminatorPresent(pk.HandlingTrainerTrash))
|
||||
data.AddLine(GetInvalid(Format(HandlingTrainer, LTrashBytesMissingTerminator)));
|
||||
|
||||
if (pk.IsEgg)
|
||||
{
|
||||
if (!pk.IsTradedEgg || pk.SWSH)
|
||||
VerifyTrashEmpty(data, pk.HandlingTrainerTrash, HandlingTrainer);
|
||||
else
|
||||
VerifyTrashNotEmpty(data, pk.HandlingTrainerTrash, HandlingTrainer);
|
||||
VerifyTrashNone(data, pk.OriginalTrainerTrash, OriginalTrainer);
|
||||
|
||||
// Species name is overwritten by "Egg"
|
||||
var origName = SpeciesName.GetSpeciesName(pk.Species, pk.Language);
|
||||
VerifyTrashSpecific(data, pk.NicknameTrash, origName, Nickname);
|
||||
return;
|
||||
}
|
||||
|
||||
VerifyTrashNickname(data, pk.NicknameTrash);
|
||||
var enc = data.Info.EncounterMatch;
|
||||
if (enc is EncounterEgg && pk.WasTradedEgg)
|
||||
{
|
||||
// Allow Traded eggs to have a single layer of OT trash bytes.
|
||||
VerifyTrashSingle(data, pk.OriginalTrainerTrash, OriginalTrainer);
|
||||
if (!pk.SWSH) // SW/SH does not update the HT data.
|
||||
VerifyTrashNotEmpty(data, pk.HandlingTrainerTrash, HandlingTrainer);
|
||||
}
|
||||
else if (enc is EncounterStatic8U { ShouldHaveScientistTrash: true })
|
||||
{
|
||||
var under = EncounterStatic8U.GetScientistName(pk.Language);
|
||||
VerifyTrashSpecific(data, pk.OriginalTrainerTrash, under, OriginalTrainer);
|
||||
}
|
||||
else
|
||||
{
|
||||
VerifyTrashNone(data, pk.OriginalTrainerTrash, OriginalTrainer);
|
||||
}
|
||||
}
|
||||
|
||||
private void VerifyTrashNickname(LegalityAnalysis data, ReadOnlySpan<byte> span)
|
||||
{
|
||||
var pk = data.Entity;
|
||||
if (pk.IsNicknamed)
|
||||
{
|
||||
var origName = SpeciesName.GetSpeciesName(pk.Species, pk.Language);
|
||||
VerifyTrashSpecific(data, span, origName, Nickname, Severity.Fishy);
|
||||
}
|
||||
else
|
||||
{
|
||||
VerifyTrashNone(data, span, Nickname, Severity.Fishy);
|
||||
}
|
||||
}
|
||||
|
||||
private void VerifyTrashSingle(LegalityAnalysis data, ReadOnlySpan<byte> span, StringSource s)
|
||||
{
|
||||
var result = TrashBytesUTF16.IsTrashSingleOrNone(span);
|
||||
if (result.IsInvalid())
|
||||
data.AddLine(GetInvalid(Format(s, LTrashBytesShouldBeEmpty)));
|
||||
}
|
||||
|
||||
private void VerifyTrashSpecific(LegalityAnalysis data, ReadOnlySpan<byte> span, ReadOnlySpan<char> under, StringSource s,
|
||||
Severity severity = Severity.Invalid)
|
||||
{
|
||||
var result = TrashBytesUTF16.IsTrashSpecific(span, under);
|
||||
if (result.IsInvalid())
|
||||
data.AddLine(Get(Format(s, string.Format(LTrashBytesExpected_0, under.ToString())), severity));
|
||||
}
|
||||
|
||||
private void VerifyTrashNone(LegalityAnalysis data, ReadOnlySpan<byte> span, StringSource s,
|
||||
Severity severity = Severity.Invalid)
|
||||
{
|
||||
var result = TrashBytesUTF16.IsTrashNone(span);
|
||||
if (result.IsInvalid())
|
||||
data.AddLine(Get(Format(s, LTrashBytesShouldBeEmpty), severity));
|
||||
}
|
||||
|
||||
private void VerifyTrashNotEmpty(LegalityAnalysis data, ReadOnlySpan<byte> span, StringSource s)
|
||||
{
|
||||
if (!TrashBytesUTF16.IsTrashNotEmpty(span))
|
||||
data.AddLine(GetInvalid(Format(s, LTrashBytesExpected)));
|
||||
}
|
||||
|
||||
private void VerifyTrashEmpty(LegalityAnalysis data, ReadOnlySpan<byte> span, StringSource s)
|
||||
{
|
||||
if (!TrashBytesUTF16.IsTrashEmpty(span))
|
||||
data.AddLine(GetInvalid(Format(s, LTrashBytesShouldBeEmpty)));
|
||||
}
|
||||
}
|
||||
|
||||
public enum StringSource : byte { Nickname, OriginalTrainer, HandlingTrainer }
|
|
@ -538,7 +538,7 @@ public sealed class WC6(byte[] Data) : DataMysteryGift(Data), IRibbonSetEvent3,
|
|||
}
|
||||
if (OTGender != pk.OriginalTrainerGender) return false;
|
||||
}
|
||||
if (!string.IsNullOrEmpty(OriginalTrainerName) && OriginalTrainerName != pk.OriginalTrainerName) return false;
|
||||
if (IsOriginalTrainerNameSet && OriginalTrainerName != pk.OriginalTrainerName) return false;
|
||||
if (PIDType == ShinyType6.FixedValue && pk.PID != PID) return false;
|
||||
if (!Shiny.IsValid(pk)) return false;
|
||||
if (OriginGame != 0 && (GameVersion)OriginGame != pk.Version) return false;
|
||||
|
|
|
@ -577,7 +577,7 @@ public sealed class WC7(byte[] Data) : DataMysteryGift(Data), IRibbonSetEvent3,
|
|||
if (TID16 != pk.TID16) return false;
|
||||
if (OTGender != pk.OriginalTrainerGender) return false;
|
||||
}
|
||||
if (!string.IsNullOrEmpty(OriginalTrainerName) && OriginalTrainerName != pk.OriginalTrainerName) return false;
|
||||
if (IsOriginalTrainerNameSet && OriginalTrainerName != pk.OriginalTrainerName) return false;
|
||||
if (OriginGame != 0 && (GameVersion)OriginGame != pk.Version) return false;
|
||||
if (EncryptionConstant != 0 && EncryptionConstant != pk.EncryptionConstant) return false;
|
||||
if (Language != 0 && Language != pk.Language) return false;
|
||||
|
|
|
@ -8,7 +8,7 @@ namespace PKHeX.Core;
|
|||
/// Generation 8 Mystery Gift Template File
|
||||
/// </summary>
|
||||
public sealed class WC8(byte[] Data) : DataMysteryGift(Data), ILangNick, INature, IGigantamax, IDynamaxLevel, IRibbonIndex, IMemoryOT, ILangNicknamedTemplate, IRelearn, IEncounterServerDate, IRestrictVersion,
|
||||
IRibbonSetEvent3, IRibbonSetEvent4, IRibbonSetCommon3, IRibbonSetCommon4, IRibbonSetCommon6, IRibbonSetCommon7, IRibbonSetCommon8, IRibbonSetMark8
|
||||
IRibbonSetEvent3, IRibbonSetEvent4, IRibbonSetCommon3, IRibbonSetCommon4, IRibbonSetCommon6, IRibbonSetCommon7, IRibbonSetCommon8, IRibbonSetMark8, ITrashUnderlaySpecies
|
||||
{
|
||||
public WC8() : this(new byte[Size]) { }
|
||||
|
||||
|
@ -391,6 +391,7 @@ public sealed class WC8(byte[] Data) : DataMysteryGift(Data), ILangNick, INature
|
|||
public bool IsHOMEGift => CardID >= 9000;
|
||||
|
||||
public bool CanHandleOT(int language) => !GetHasOT(language);
|
||||
public bool IsTrashUnderlaySpecies(PKM pk) => GetIsNicknamed(pk.Language);
|
||||
|
||||
public override GameVersion Version => OriginGame != 0 ? (GameVersion)OriginGame : GameVersion.SWSH;
|
||||
|
||||
|
@ -403,9 +404,9 @@ public sealed class WC8(byte[] Data) : DataMysteryGift(Data), ILangNick, INature
|
|||
byte currentLevel = Level > 0 ? Level : (byte)(1 + rnd.Next(100));
|
||||
var metLevel = MetLevel > 0 ? MetLevel : currentLevel;
|
||||
var pi = PersonalTable.SWSH.GetFormEntry(Species, Form);
|
||||
var language = tr.Language;
|
||||
bool hasOT = GetHasOT(language);
|
||||
var version = OriginGame != 0 ? (GameVersion)OriginGame : this.GetCompatibleVersion(tr.Version);
|
||||
var language = (int)Core.Language.GetSafeLanguage(Generation, (LanguageID)tr.Language, version);
|
||||
bool hasOT = GetHasOT(language);
|
||||
|
||||
var pk = new PK8
|
||||
{
|
||||
|
@ -494,8 +495,9 @@ public sealed class WC8(byte[] Data) : DataMysteryGift(Data), ILangNick, INature
|
|||
|
||||
var nickname_language = GetLanguage(language);
|
||||
pk.Language = nickname_language != 0 ? nickname_language : tr.Language;
|
||||
pk.IsNicknamed = GetIsNicknamed(language);
|
||||
pk.Nickname = pk.IsNicknamed ? GetNickname(language) : SpeciesName.GetSpeciesNameGeneration(Species, pk.Language, Generation);
|
||||
pk.Nickname = SpeciesName.GetSpeciesNameGeneration(Species, pk.Language, Generation);
|
||||
if (GetIsNicknamed(language))
|
||||
pk.Nickname = GetNickname(language);
|
||||
|
||||
for (var i = 0; i < RibbonBytesCount; i++)
|
||||
{
|
||||
|
|
|
@ -320,8 +320,8 @@ public sealed class BK4 : G4PKM
|
|||
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);
|
||||
=> TrashBytesUTF16.GetTerminatorIndex(data, StringConverter4.Terminator);
|
||||
public override int GetStringLength(ReadOnlySpan<byte> data)
|
||||
=> TrashBytes.GetStringLength(data, StringConverter4.Terminator);
|
||||
=> TrashBytesUTF16.GetStringLength(data, StringConverter4.Terminator);
|
||||
public override int GetBytesPerChar() => 2;
|
||||
}
|
||||
|
|
|
@ -249,8 +249,8 @@ public sealed class CK3(byte[] Data) : G3PKM(Data), IShadowCapture
|
|||
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);
|
||||
=> TrashBytesUTF16.GetTerminatorIndex(data, StringConverter3GC.TerminatorBigEndian);
|
||||
public override int GetStringLength(ReadOnlySpan<byte> data)
|
||||
=> TrashBytes.GetStringLength(data, StringConverter3GC.TerminatorBigEndian);
|
||||
=> TrashBytesUTF16.GetStringLength(data, StringConverter3GC.TerminatorBigEndian);
|
||||
public override int GetBytesPerChar() => 2;
|
||||
}
|
||||
|
|
|
@ -433,9 +433,9 @@ public sealed class PKH : PKM, IHandlerLanguage, IFormArgument, IHomeTrack, IBat
|
|||
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);
|
||||
=> TrashBytesUTF16.GetTerminatorIndex(data);
|
||||
public override int GetStringLength(ReadOnlySpan<byte> data)
|
||||
=> TrashBytes.GetStringLength(data);
|
||||
=> TrashBytesUTF16.GetStringLength(data);
|
||||
public override int GetBytesPerChar() => 2;
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -587,7 +587,10 @@ public sealed class PA8 : PKM, ISanityChecksum,
|
|||
return false;
|
||||
if (tr.Gender != OriginalTrainerGender)
|
||||
return false;
|
||||
return tr.OT == OriginalTrainerName;
|
||||
|
||||
Span<char> ot = stackalloc char[MaxStringLengthTrainer];
|
||||
int len = LoadString(OriginalTrainerTrash, ot);
|
||||
return ot[..len].SequenceEqual(tr.OT);
|
||||
}
|
||||
|
||||
public void UpdateHandler(ITrainerInfo tr)
|
||||
|
@ -739,8 +742,8 @@ public sealed class PA8 : PKM, ISanityChecksum,
|
|||
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);
|
||||
=> TrashBytesUTF16.GetTerminatorIndex(data);
|
||||
public override int GetStringLength(ReadOnlySpan<byte> data)
|
||||
=> TrashBytes.GetStringLength(data);
|
||||
=> TrashBytesUTF16.GetStringLength(data);
|
||||
public override int GetBytesPerChar() => 2;
|
||||
}
|
||||
|
|
|
@ -594,8 +594,8 @@ public sealed class PB7 : G6PKM, IHyperTrain, IAwakened, IScaledSizeValue, IComb
|
|||
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);
|
||||
=> TrashBytesUTF16.GetTerminatorIndex(data);
|
||||
public override int GetStringLength(ReadOnlySpan<byte> data)
|
||||
=> TrashBytes.GetStringLength(data);
|
||||
=> TrashBytesUTF16.GetStringLength(data);
|
||||
public override int GetBytesPerChar() => 2;
|
||||
}
|
||||
|
|
|
@ -52,7 +52,10 @@ public sealed class PB8 : G8PKM
|
|||
return false;
|
||||
if (tr.Gender != OriginalTrainerGender)
|
||||
return false;
|
||||
return tr.OT == OriginalTrainerName;
|
||||
|
||||
Span<char> ot = stackalloc char[MaxStringLengthTrainer];
|
||||
int len = LoadString(OriginalTrainerTrash, ot);
|
||||
return ot[..len].SequenceEqual(tr.OT);
|
||||
}
|
||||
|
||||
public void UpdateHandler(ITrainerInfo tr)
|
||||
|
@ -142,8 +145,8 @@ public sealed class PB8 : G8PKM
|
|||
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);
|
||||
=> TrashBytesUTF16.GetTerminatorIndex(data);
|
||||
public override int GetStringLength(ReadOnlySpan<byte> data)
|
||||
=> TrashBytes.GetStringLength(data);
|
||||
=> TrashBytesUTF16.GetStringLength(data);
|
||||
public override int GetBytesPerChar() => 2;
|
||||
}
|
||||
|
|
|
@ -255,9 +255,9 @@ public sealed class PK1 : GBPKML, IPersonalType
|
|||
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);
|
||||
=> TrashBytesUTF16.GetTerminatorIndex(data, StringConverter4.Terminator);
|
||||
public override int GetStringLength(ReadOnlySpan<byte> data)
|
||||
=> TrashBytes.GetStringLength(data, StringConverter4.Terminator);
|
||||
=> TrashBytesUTF16.GetStringLength(data, StringConverter4.Terminator);
|
||||
public override int GetBytesPerChar() => 2;
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -396,8 +396,8 @@ public sealed class PK4 : G4PKM
|
|||
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);
|
||||
=> TrashBytesUTF16.GetTerminatorIndex(data, StringConverter4.Terminator);
|
||||
public override int GetStringLength(ReadOnlySpan<byte> data)
|
||||
=> TrashBytes.GetStringLength(data, StringConverter4.Terminator);
|
||||
=> TrashBytesUTF16.GetStringLength(data, StringConverter4.Terminator);
|
||||
public override int GetBytesPerChar() => 2;
|
||||
}
|
||||
|
|
|
@ -318,7 +318,10 @@ public sealed class PK5 : PKM, ISanityChecksum,
|
|||
return false;
|
||||
if (tr.Gender != OriginalTrainerGender)
|
||||
return false;
|
||||
return tr.OT == OriginalTrainerName;
|
||||
|
||||
Span<char> ot = stackalloc char[MaxStringLengthTrainer];
|
||||
int len = LoadString(OriginalTrainerTrash, ot);
|
||||
return ot[..len].SequenceEqual(tr.OT);
|
||||
}
|
||||
|
||||
public void UpdateHandler(ITrainerInfo tr)
|
||||
|
@ -573,9 +576,9 @@ public sealed class PK5 : PKM, ISanityChecksum,
|
|||
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);
|
||||
=> TrashBytesUTF16.GetTerminatorIndex(data, StringConverter5.Terminator);
|
||||
public override int GetStringLength(ReadOnlySpan<byte> data)
|
||||
=> TrashBytes.GetStringLength(data, StringConverter5.Terminator);
|
||||
=> TrashBytesUTF16.GetStringLength(data, StringConverter5.Terminator);
|
||||
public override int GetBytesPerChar() => 2;
|
||||
|
||||
/// <inheritdoc cref="G4PKM.CheckKoreanNidoranDPPt"/>
|
||||
|
|
|
@ -532,8 +532,8 @@ public sealed class PK6 : G6PKM, IRibbonSetEvent3, IRibbonSetEvent4, IRibbonSetC
|
|||
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);
|
||||
=> TrashBytesUTF16.GetTerminatorIndex(data);
|
||||
public override int GetStringLength(ReadOnlySpan<byte> data)
|
||||
=> TrashBytes.GetStringLength(data);
|
||||
=> TrashBytesUTF16.GetStringLength(data);
|
||||
public override int GetBytesPerChar() => 2;
|
||||
}
|
||||
|
|
|
@ -557,9 +557,9 @@ public sealed class PK7 : G6PKM, IRibbonSetEvent3, IRibbonSetEvent4, IRibbonSetC
|
|||
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);
|
||||
=> TrashBytesUTF16.GetTerminatorIndex(data);
|
||||
public override int GetStringLength(ReadOnlySpan<byte> data)
|
||||
=> TrashBytes.GetStringLength(data);
|
||||
=> TrashBytesUTF16.GetStringLength(data);
|
||||
public override int GetBytesPerChar() => 2;
|
||||
}
|
||||
|
||||
|
|
|
@ -43,7 +43,10 @@ public sealed class PK8 : G8PKM, IHandlerUpdate
|
|||
return false;
|
||||
if (tr.Gender != OriginalTrainerGender)
|
||||
return false;
|
||||
return tr.OT == OriginalTrainerName;
|
||||
|
||||
Span<char> ot = stackalloc char[MaxStringLengthTrainer];
|
||||
int len = LoadString(OriginalTrainerTrash, ot);
|
||||
return ot[..len].SequenceEqual(tr.OT);
|
||||
}
|
||||
|
||||
public void UpdateHandler(ITrainerInfo tr)
|
||||
|
@ -125,8 +128,8 @@ public sealed class PK8 : G8PKM, IHandlerUpdate
|
|||
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);
|
||||
=> TrashBytesUTF16.GetTerminatorIndex(data);
|
||||
public override int GetStringLength(ReadOnlySpan<byte> data)
|
||||
=> TrashBytes.GetStringLength(data);
|
||||
=> TrashBytesUTF16.GetStringLength(data);
|
||||
public override int GetBytesPerChar() => 2;
|
||||
}
|
||||
|
|
|
@ -566,7 +566,10 @@ public sealed class PK9 : PKM, ISanityChecksum, ITeraType, ITechRecord, IObedien
|
|||
return false;
|
||||
if (tr.Language != Language)
|
||||
return false;
|
||||
return tr.OT == OriginalTrainerName;
|
||||
|
||||
Span<char> ot = stackalloc char[MaxStringLengthTrainer];
|
||||
int len = LoadString(OriginalTrainerTrash, ot);
|
||||
return ot[..len].SequenceEqual(tr.OT);
|
||||
}
|
||||
|
||||
public void UpdateHandler(ITrainerInfo tr)
|
||||
|
@ -678,8 +681,8 @@ public sealed class PK9 : PKM, ISanityChecksum, ITeraType, ITechRecord, IObedien
|
|||
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);
|
||||
=> TrashBytesUTF16.GetTerminatorIndex(data);
|
||||
public override int GetStringLength(ReadOnlySpan<byte> data)
|
||||
=> TrashBytes.GetStringLength(data);
|
||||
=> TrashBytesUTF16.GetStringLength(data);
|
||||
public override int GetBytesPerChar() => 2;
|
||||
}
|
||||
|
|
|
@ -358,8 +358,8 @@ public sealed class RK4 : G4PKM
|
|||
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);
|
||||
=> TrashBytesUTF16.GetTerminatorIndex(data, StringConverter4.Terminator);
|
||||
public override int GetStringLength(ReadOnlySpan<byte> data)
|
||||
=> TrashBytes.GetStringLength(data, StringConverter4.Terminator);
|
||||
=> TrashBytesUTF16.GetStringLength(data, StringConverter4.Terminator);
|
||||
public override int GetBytesPerChar() => 2;
|
||||
}
|
||||
|
|
|
@ -290,7 +290,10 @@ public abstract class G4PKM : PKM, IHandlerUpdate,
|
|||
return false;
|
||||
if (tr.Gender != OriginalTrainerGender)
|
||||
return false;
|
||||
return tr.OT == OriginalTrainerName;
|
||||
|
||||
Span<char> ot = stackalloc char[MaxStringLengthTrainer];
|
||||
int len = LoadString(OriginalTrainerTrash, ot);
|
||||
return ot[..len].SequenceEqual(tr.OT);
|
||||
}
|
||||
|
||||
public void UpdateHandler(ITrainerInfo tr)
|
||||
|
|
|
@ -89,7 +89,10 @@ public abstract class G6PKM : PKM, ISanityChecksum, IHandlerUpdate
|
|||
return false;
|
||||
if (tr.Gender != OriginalTrainerGender)
|
||||
return false;
|
||||
return tr.OT == OriginalTrainerName;
|
||||
|
||||
Span<char> ot = stackalloc char[MaxStringLengthTrainer];
|
||||
int len = LoadString(OriginalTrainerTrash, ot);
|
||||
return ot[..len].SequenceEqual(tr.OT);
|
||||
}
|
||||
|
||||
public void UpdateHandler(ITrainerInfo tr)
|
||||
|
|
|
@ -62,7 +62,10 @@ public abstract class GBPKM : PKM
|
|||
get
|
||||
{
|
||||
var spName = SpeciesName.GetSpeciesNameGeneration(Species, GuessedLanguage(), Format);
|
||||
return Nickname != spName;
|
||||
|
||||
Span<char> nickname = stackalloc char[TrashCharCountNickname];
|
||||
int len = LoadString(NicknameTrash, nickname);
|
||||
return !nickname[..len].SequenceEqual(spName);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -85,9 +85,9 @@ public static class StringConverter8
|
|||
/// <returns>Indication of the under string's presence.</returns>
|
||||
public static TrashMatch ApplyTrashBytes(Span<byte> top, ReadOnlySpan<char> under)
|
||||
{
|
||||
var index = TrashBytes.GetStringLength(top);
|
||||
var index = TrashBytesUTF16.GetStringLength(top);
|
||||
if (index == -1)
|
||||
return TrashMatch.Skipped;
|
||||
return TrashMatch.TooLongToTell;
|
||||
index++; // hop over the terminator
|
||||
if (index >= under.Length) // Overlapping
|
||||
return TrashMatch.TooLongToTell;
|
||||
|
@ -107,9 +107,9 @@ public static class StringConverter8
|
|||
public static TrashMatch GetTrashState(ReadOnlySpan<byte> top, ReadOnlySpan<char> under)
|
||||
{
|
||||
if (under.Length == 0)
|
||||
return TrashMatch.Skipped;
|
||||
return TrashMatch.TooLongToTell;
|
||||
|
||||
var index = TrashBytes.GetStringLength(top);
|
||||
var index = TrashBytesUTF16.GetStringLength(top);
|
||||
if ((uint)index >= under.Length)
|
||||
return TrashMatch.TooLongToTell;
|
||||
index++; // hop over the terminator
|
||||
|
@ -177,7 +177,7 @@ public static class StringConverter8
|
|||
|
||||
var region = u16[..length];
|
||||
char seek = ' ';
|
||||
if (BitConverter.IsLittleEndian)
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
seek = (char)ReverseEndianness(' ');
|
||||
|
||||
var trim = region.Trim(seek);
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// 16-bit encoded string utility
|
||||
/// </summary>
|
||||
public static class TrashBytes
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the length of the string based on the terminator.
|
||||
/// </summary>
|
||||
/// <param name="buffer">Buffer to check the length of.</param>
|
||||
/// <param name="terminator">String terminator to search for.</param>
|
||||
/// <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 = GetTerminatorIndex(buffer, terminator);
|
||||
return index == -1 ? buffer.Length / 2 : index;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a 16-bit aligned index of the terminator.
|
||||
/// </summary>
|
||||
/// <param name="buffer">Backing buffer of the string.</param>
|
||||
/// <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 GetTerminatorIndex(ReadOnlySpan<byte> buffer, ushort terminator = 0)
|
||||
{
|
||||
var u16 = MemoryMarshal.Cast<byte, ushort>(buffer);
|
||||
return u16.IndexOf(terminator);
|
||||
}
|
||||
}
|
|
@ -7,7 +7,7 @@ namespace PKHeX.Core;
|
|||
/// </summary>
|
||||
public static class TrashBytes8
|
||||
{
|
||||
/// <inheritdoc cref="TrashBytes.GetStringLength"/>
|
||||
/// <inheritdoc cref="TrashBytesUTF16.GetStringLength"/>
|
||||
public static int GetStringLength(ReadOnlySpan<byte> buffer)
|
||||
{
|
||||
int index = GetTerminatorIndex(buffer);
|
||||
|
|
|
@ -8,7 +8,7 @@ namespace PKHeX.Core;
|
|||
/// </summary>
|
||||
public static class TrashBytesGB
|
||||
{
|
||||
/// <inheritdoc cref="TrashBytes.GetStringLength"/>
|
||||
/// <inheritdoc cref="TrashBytesUTF16.GetStringLength"/>
|
||||
public static int GetStringLength(ReadOnlySpan<byte> buffer)
|
||||
{
|
||||
int index = GetTerminatorIndex(buffer);
|
||||
|
|
114
PKHeX.Core/PKM/Strings/Trash/TrashBytesUTF16.cs
Normal file
114
PKHeX.Core/PKM/Strings/Trash/TrashBytesUTF16.cs
Normal file
|
@ -0,0 +1,114 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using static System.Buffers.Binary.BinaryPrimitives;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// 16-bit encoded string utility
|
||||
/// </summary>
|
||||
public static class TrashBytesUTF16
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the length of the string based on the terminator.
|
||||
/// </summary>
|
||||
/// <param name="buffer">Buffer to check the length of.</param>
|
||||
/// <param name="terminator">String terminator to search for.</param>
|
||||
/// <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 = GetTerminatorIndex(buffer, terminator);
|
||||
return index == -1 ? buffer.Length / 2 : index;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a 16-bit aligned index of the terminator.
|
||||
/// </summary>
|
||||
/// <param name="buffer">Backing buffer of the string.</param>
|
||||
/// <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 GetTerminatorIndex(ReadOnlySpan<byte> buffer, ushort terminator = 0)
|
||||
{
|
||||
var u16 = MemoryMarshal.Cast<byte, ushort>(buffer);
|
||||
return u16.IndexOf(terminator);
|
||||
}
|
||||
|
||||
public static TrashMatch IsUnderlayerPresent(ReadOnlySpan<char> under, ReadOnlySpan<byte> data, int charsUsed)
|
||||
{
|
||||
var input = MemoryMarshal.Cast<byte, char>(data);
|
||||
return IsUnderlayerPresent(under, input, charsUsed);
|
||||
}
|
||||
|
||||
public static TrashMatch IsUnderlayerPresent(ReadOnlySpan<char> under, ReadOnlySpan<char> input, int charsUsed)
|
||||
{
|
||||
if (charsUsed >= under.Length)
|
||||
return TrashMatch.TooLongToTell;
|
||||
|
||||
for (int i = charsUsed; i < under.Length; i++)
|
||||
{
|
||||
var c = input[i];
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
c = (char)ReverseEndianness(c);
|
||||
if (c == under[i])
|
||||
continue;
|
||||
return TrashMatch.NotPresent;
|
||||
}
|
||||
return TrashMatch.Present;
|
||||
}
|
||||
|
||||
public static bool IsTrashNotEmpty(ReadOnlySpan<byte> span) => span.ContainsAnyExcept<byte>(0) || span.Length == 0;
|
||||
public static bool IsTrashEmpty(ReadOnlySpan<byte> span) => !span.ContainsAnyExcept<byte>(0) || span.Length == 0;
|
||||
|
||||
public static bool IsFinalTerminatorPresent(ReadOnlySpan<byte> buffer, byte terminator = 0)
|
||||
=> buffer[^1] == terminator && buffer[^2] == terminator;
|
||||
|
||||
private const int BytesPerChar = 2;
|
||||
|
||||
public static TrashMatch IsTrashNone(ReadOnlySpan<byte> span)
|
||||
{
|
||||
var charsUsed = GetTerminatorIndex(span) + 1;
|
||||
var start = charsUsed * BytesPerChar;
|
||||
if ((uint)start >= span.Length)
|
||||
return TrashMatch.TooLongToTell;
|
||||
|
||||
var remain = span[start..];
|
||||
if (!IsTrashEmpty(remain))
|
||||
return TrashMatch.NotEmpty;
|
||||
return TrashMatch.PresentNone;
|
||||
}
|
||||
|
||||
public static TrashMatch IsTrashSingleOrNone(ReadOnlySpan<byte> span)
|
||||
{
|
||||
var charsUsed = GetTerminatorIndex(span) + 1;
|
||||
var start = charsUsed * BytesPerChar;
|
||||
if ((uint)start >= span.Length)
|
||||
return TrashMatch.TooLongToTell;
|
||||
|
||||
var remain = span[start..];
|
||||
var end = GetTerminatorIndex(span) + 1;
|
||||
start = end * BytesPerChar;
|
||||
if ((uint)start < remain.Length && !IsTrashEmpty(remain[start..]))
|
||||
return TrashMatch.NotEmpty;
|
||||
|
||||
return end == 1 ? TrashMatch.PresentNone : TrashMatch.PresentSingle;
|
||||
}
|
||||
|
||||
public static TrashMatch IsTrashSpecific(ReadOnlySpan<byte> span, ReadOnlySpan<char> under)
|
||||
{
|
||||
var charsUsed = GetTerminatorIndex(span) + 1;
|
||||
var start = charsUsed * BytesPerChar;
|
||||
if (start >= span.Length)
|
||||
return TrashMatch.TooLongToTell;
|
||||
|
||||
var check = IsUnderlayerPresent(under, span, charsUsed);
|
||||
if (check.IsInvalid())
|
||||
return TrashMatch.NotPresent;
|
||||
|
||||
start = Math.Max(start, under.Length * BytesPerChar);
|
||||
if ((uint)start < span.Length && !IsTrashEmpty(span[start..]))
|
||||
return TrashMatch.NotEmpty;
|
||||
|
||||
return TrashMatch.Present;
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
namespace PKHeX.Core;
|
||||
namespace PKHeX.Core;
|
||||
|
||||
public enum TrashMatch
|
||||
{
|
||||
|
@ -7,10 +7,7 @@ public enum TrashMatch
|
|||
/// </summary>
|
||||
NotPresent,
|
||||
|
||||
/// <summary>
|
||||
/// Expected under-layer of trash was found.
|
||||
/// </summary>
|
||||
Present,
|
||||
NotEmpty,
|
||||
|
||||
/// <summary>
|
||||
/// Displayed string is too long, with all bytes covering the initial trash.
|
||||
|
@ -18,7 +15,19 @@ public enum TrashMatch
|
|||
TooLongToTell,
|
||||
|
||||
/// <summary>
|
||||
/// Ignored due to other issues that would be flagged by other checks.
|
||||
/// Expected under-layer of trash was found.
|
||||
/// </summary>
|
||||
Skipped,
|
||||
Present,
|
||||
|
||||
PresentNone,
|
||||
|
||||
PresentSingle,
|
||||
|
||||
PresentMulti,
|
||||
}
|
||||
|
||||
public static class TrashMatchExtensions
|
||||
{
|
||||
public static bool IsPresent(this TrashMatch match) => match >= TrashMatch.Present;
|
||||
public static bool IsInvalid(this TrashMatch match) => match < TrashMatch.TooLongToTell;
|
||||
}
|
||||
|
|
|
@ -253,8 +253,8 @@ public sealed class XK3 : G3PKM, IShadowCapture
|
|||
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);
|
||||
=> TrashBytesUTF16.GetTerminatorIndex(data, StringConverter3GC.TerminatorBigEndian);
|
||||
public override int GetStringLength(ReadOnlySpan<byte> data)
|
||||
=> TrashBytes.GetStringLength(data, StringConverter3GC.TerminatorBigEndian);
|
||||
=> TrashBytesUTF16.GetStringLength(data, StringConverter3GC.TerminatorBigEndian);
|
||||
public override int GetBytesPerChar() => 2;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
using System;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
|
@ -68,7 +70,11 @@ public static class TrainerInfoExtensions
|
|||
{
|
||||
if (tr.ID32 != pk.ID32)
|
||||
return false;
|
||||
if (tr.OT != pk.OriginalTrainerName)
|
||||
|
||||
Span<char> ot = stackalloc char[pk.MaxStringLengthTrainer];
|
||||
int len = pk.LoadString(pk.OriginalTrainerTrash, ot);
|
||||
ot = ot[..len];
|
||||
if (!ot.SequenceEqual(tr.OT))
|
||||
return false;
|
||||
|
||||
if (pk.Format == 3)
|
||||
|
@ -104,7 +110,10 @@ public static class TrainerInfoExtensions
|
|||
else { return false; }
|
||||
}
|
||||
|
||||
if (tr.OT != pk.OriginalTrainerName)
|
||||
Span<char> ot = stackalloc char[pk.MaxStringLengthTrainer];
|
||||
int len = pk.LoadString(pk.OriginalTrainerTrash, ot);
|
||||
ot = ot[..len];
|
||||
if (!ot.SequenceEqual(tr.OT))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Windows.Forms;
|
||||
|
||||
|
|
Binary file not shown.
Binary file not shown.
Loading…
Reference in a new issue