2023-01-22 04:02:33 +00:00
|
|
|
|
using System;
|
2018-07-27 02:34:27 +00:00
|
|
|
|
using static PKHeX.Core.LegalityCheckStrings;
|
2018-06-24 05:00:01 +00:00
|
|
|
|
|
2022-06-18 18:04:24 +00:00
|
|
|
|
namespace PKHeX.Core;
|
2018-06-24 05:00:01 +00:00
|
|
|
|
|
2022-06-18 18:04:24 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Verifies the <see cref="PKM.OT_Name"/>.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public sealed class TrainerNameVerifier : Verifier
|
|
|
|
|
{
|
|
|
|
|
protected override CheckIdentifier Identifier => CheckIdentifier.Trainer;
|
2018-06-24 05:00:01 +00:00
|
|
|
|
|
2022-06-18 18:04:24 +00:00
|
|
|
|
private static readonly string[] SuspiciousOTNames =
|
2023-12-04 04:13:20 +00:00
|
|
|
|
[
|
2022-06-18 18:04:24 +00:00
|
|
|
|
"PKHeX",
|
|
|
|
|
"PKHeX",
|
2023-12-04 04:13:20 +00:00
|
|
|
|
];
|
2018-06-24 05:00:01 +00:00
|
|
|
|
|
2022-06-18 18:04:24 +00:00
|
|
|
|
public override void Verify(LegalityAnalysis data)
|
|
|
|
|
{
|
|
|
|
|
var pk = data.Entity;
|
|
|
|
|
var enc = data.EncounterMatch;
|
|
|
|
|
if (!IsPlayerOriginalTrainer(enc))
|
|
|
|
|
return; // already verified
|
2018-06-24 05:00:01 +00:00
|
|
|
|
|
2022-06-18 18:04:24 +00:00
|
|
|
|
var ot = pk.OT_Name;
|
|
|
|
|
if (ot.Length == 0)
|
|
|
|
|
data.AddLine(GetInvalid(LOTShort));
|
2022-05-03 06:48:25 +00:00
|
|
|
|
|
2022-06-18 18:04:24 +00:00
|
|
|
|
if (IsOTNameSuspicious(ot))
|
|
|
|
|
{
|
|
|
|
|
data.AddLine(Get(LOTSuspicious, Severity.Fishy));
|
2018-06-24 05:00:01 +00:00
|
|
|
|
}
|
2018-07-27 02:34:27 +00:00
|
|
|
|
|
2022-06-18 18:04:24 +00:00
|
|
|
|
if (pk.VC)
|
2021-11-26 03:15:42 +00:00
|
|
|
|
{
|
2023-10-15 02:28:46 +00:00
|
|
|
|
VerifyOTGB(data);
|
2022-06-18 18:04:24 +00:00
|
|
|
|
}
|
|
|
|
|
else if (ot.Length > Legal.GetMaxLengthOT(data.Info.Generation, (LanguageID)pk.Language))
|
2018-10-27 23:06:06 +00:00
|
|
|
|
{
|
2022-06-18 18:04:24 +00:00
|
|
|
|
if (!IsEdgeCaseLength(pk, data.EncounterOriginal, ot))
|
|
|
|
|
data.AddLine(Get(LOTLong, Severity.Invalid));
|
|
|
|
|
}
|
2018-10-27 23:06:06 +00:00
|
|
|
|
|
2022-06-18 18:04:24 +00:00
|
|
|
|
if (ParseSettings.CheckWordFilter)
|
|
|
|
|
{
|
2023-01-22 04:02:33 +00:00
|
|
|
|
if (WordFilter.IsFiltered(ot, out var badPattern))
|
2023-07-08 04:12:19 +00:00
|
|
|
|
data.AddLine(GetInvalid($"Word Filter: {badPattern}"));
|
2022-06-18 18:04:24 +00:00
|
|
|
|
if (ContainsTooManyNumbers(ot, data.Info.Generation))
|
2023-07-08 04:12:19 +00:00
|
|
|
|
data.AddLine(GetInvalid("Word Filter: Too many numbers."));
|
2021-12-07 08:54:39 +00:00
|
|
|
|
|
2023-01-22 04:02:33 +00:00
|
|
|
|
if (WordFilter.IsFiltered(pk.HT_Name, out badPattern))
|
2023-07-08 04:12:19 +00:00
|
|
|
|
data.AddLine(GetInvalid($"Word Filter: {badPattern}"));
|
2018-10-27 23:06:06 +00:00
|
|
|
|
}
|
2022-06-18 18:04:24 +00:00
|
|
|
|
}
|
2018-10-27 23:06:06 +00:00
|
|
|
|
|
2022-06-18 18:04:24 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Checks if any player (human) was the original OT.
|
|
|
|
|
/// </summary>
|
|
|
|
|
internal static bool IsPlayerOriginalTrainer(IEncounterable enc) => enc switch
|
|
|
|
|
{
|
2023-08-12 23:01:16 +00:00
|
|
|
|
IFixedTrainer { IsFixedTrainer: true } => false,
|
2022-06-18 18:04:24 +00:00
|
|
|
|
MysteryGift { IsEgg: false } => false,
|
|
|
|
|
EncounterStatic5N => false,
|
|
|
|
|
_ => true,
|
|
|
|
|
};
|
|
|
|
|
|
2023-10-15 02:28:46 +00:00
|
|
|
|
public static bool IsEdgeCaseLength(PKM pk, IEncounterTemplate e, ReadOnlySpan<char> ot)
|
2022-06-18 18:04:24 +00:00
|
|
|
|
{
|
|
|
|
|
if (e.EggEncounter)
|
2018-06-24 05:00:01 +00:00
|
|
|
|
{
|
2023-10-15 02:28:46 +00:00
|
|
|
|
if (e is WC3 wc3 && pk.IsEgg && ot.SequenceEqual(wc3.OT_Name))
|
2022-06-18 18:04:24 +00:00
|
|
|
|
return true; // Fixed OT Mystery Gift Egg
|
|
|
|
|
bool eggEdge = pk.IsEgg ? pk.IsTradedEgg || pk.Format == 3 : pk.WasTradedEgg;
|
|
|
|
|
if (!eggEdge)
|
|
|
|
|
return false;
|
|
|
|
|
var len = Legal.GetMaxLengthOT(e.Generation, LanguageID.English); // max case
|
|
|
|
|
return ot.Length <= len;
|
|
|
|
|
}
|
2018-06-24 05:00:01 +00:00
|
|
|
|
|
2023-08-12 23:01:16 +00:00
|
|
|
|
if (e is IFixedTrainer { IsFixedTrainer: true })
|
2022-06-18 18:04:24 +00:00
|
|
|
|
return true; // already verified
|
2020-09-10 05:43:32 +00:00
|
|
|
|
|
2022-06-18 18:04:24 +00:00
|
|
|
|
if (e is MysteryGift mg && mg.OT_Name.Length == ot.Length)
|
|
|
|
|
return true; // Mattle Ho-Oh
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2018-06-24 05:00:01 +00:00
|
|
|
|
|
2023-10-15 02:28:46 +00:00
|
|
|
|
public void VerifyOTGB(LegalityAnalysis data)
|
2022-06-18 18:04:24 +00:00
|
|
|
|
{
|
|
|
|
|
var pk = data.Entity;
|
2023-10-15 02:28:46 +00:00
|
|
|
|
var enc = data.EncounterOriginal;
|
|
|
|
|
if (pk.OT_Gender == 1)
|
|
|
|
|
{
|
|
|
|
|
// Transferring from RBY->Gen7 won't have OT Gender in PK1, nor will PK1 originated encounters.
|
|
|
|
|
// GSC Trades already checked for OT Gender matching.
|
|
|
|
|
if (pk is { Format: > 2, VC1: true } || enc is { Generation: 1 } or EncounterGift2 { IsGift: true })
|
|
|
|
|
data.AddLine(GetInvalid(LG1OTGender));
|
|
|
|
|
}
|
2018-07-27 02:34:27 +00:00
|
|
|
|
|
2023-10-15 02:28:46 +00:00
|
|
|
|
if (enc is IFixedTrainer { IsFixedTrainer: true })
|
|
|
|
|
return; // already verified
|
|
|
|
|
|
|
|
|
|
string tr = pk.OT_Name;
|
2022-06-18 18:04:24 +00:00
|
|
|
|
if (tr.Length == 0)
|
2018-06-24 05:00:01 +00:00
|
|
|
|
{
|
2023-01-22 04:02:33 +00:00
|
|
|
|
if (pk is SK2 {TID16: 0, IsRental: true})
|
2018-06-24 05:00:01 +00:00
|
|
|
|
{
|
2022-06-18 18:04:24 +00:00
|
|
|
|
data.AddLine(Get(LOTShort, Severity.Fishy));
|
2018-06-24 05:00:01 +00:00
|
|
|
|
}
|
2022-06-18 18:04:24 +00:00
|
|
|
|
else
|
2018-06-24 05:00:01 +00:00
|
|
|
|
{
|
2022-06-18 18:04:24 +00:00
|
|
|
|
data.AddLine(GetInvalid(LOTShort));
|
|
|
|
|
return;
|
2018-06-24 05:00:01 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2023-10-15 02:28:46 +00:00
|
|
|
|
VerifyGBOTWithinBounds(data, tr);
|
2022-06-18 18:04:24 +00:00
|
|
|
|
}
|
2018-10-09 00:57:34 +00:00
|
|
|
|
|
2023-10-15 02:28:46 +00:00
|
|
|
|
private void VerifyGBOTWithinBounds(LegalityAnalysis data, ReadOnlySpan<char> str)
|
2022-06-18 18:04:24 +00:00
|
|
|
|
{
|
2023-10-15 02:28:46 +00:00
|
|
|
|
var pk = data.Entity;
|
|
|
|
|
if (pk.Japanese)
|
2022-06-18 18:04:24 +00:00
|
|
|
|
{
|
|
|
|
|
if (str.Length > 5)
|
|
|
|
|
data.AddLine(GetInvalid(LOTLong));
|
2023-10-15 02:28:46 +00:00
|
|
|
|
if (!StringConverter12.GetIsG1Japanese(str))
|
|
|
|
|
data.AddLine(GetInvalid(LG1CharOT));
|
2022-06-18 18:04:24 +00:00
|
|
|
|
}
|
2023-10-15 02:28:46 +00:00
|
|
|
|
else if (pk.Korean)
|
2022-06-18 18:04:24 +00:00
|
|
|
|
{
|
|
|
|
|
if (str.Length > 5)
|
|
|
|
|
data.AddLine(GetInvalid(LOTLong));
|
2023-10-15 02:28:46 +00:00
|
|
|
|
if (!StringConverter2KOR.GetIsG2Korean(str))
|
|
|
|
|
data.AddLine(GetInvalid(LG1CharOT));
|
2018-10-09 00:57:34 +00:00
|
|
|
|
}
|
2023-10-15 02:28:46 +00:00
|
|
|
|
else
|
2022-06-18 18:04:24 +00:00
|
|
|
|
{
|
2023-10-15 02:28:46 +00:00
|
|
|
|
if (str.Length > 7)
|
|
|
|
|
data.AddLine(GetInvalid(LOTLong));
|
|
|
|
|
if (!StringConverter12.GetIsG1English(str))
|
|
|
|
|
data.AddLine(GetInvalid(LG1CharOT));
|
2022-06-18 18:04:24 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-10-09 00:57:34 +00:00
|
|
|
|
|
2023-10-15 02:28:46 +00:00
|
|
|
|
private static bool IsOTNameSuspicious(ReadOnlySpan<char> name)
|
2022-06-18 18:04:24 +00:00
|
|
|
|
{
|
|
|
|
|
foreach (var s in SuspiciousOTNames)
|
2018-10-09 00:57:34 +00:00
|
|
|
|
{
|
2023-10-15 02:28:46 +00:00
|
|
|
|
if (name.StartsWith(s, StringComparison.OrdinalIgnoreCase))
|
2022-06-18 18:04:24 +00:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2018-10-09 00:57:34 +00:00
|
|
|
|
|
2023-10-15 02:28:46 +00:00
|
|
|
|
public static bool ContainsTooManyNumbers(ReadOnlySpan<char> str, int originalGeneration)
|
2022-06-18 18:04:24 +00:00
|
|
|
|
{
|
|
|
|
|
if (originalGeneration <= 3)
|
|
|
|
|
return false; // no limit from these generations
|
|
|
|
|
int max = originalGeneration < 6 ? 4 : 5;
|
|
|
|
|
if (str.Length <= max)
|
|
|
|
|
return false;
|
|
|
|
|
int count = GetNumberCount(str);
|
|
|
|
|
return count > max;
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-15 02:28:46 +00:00
|
|
|
|
private static int GetNumberCount(ReadOnlySpan<char> str)
|
2022-06-18 18:04:24 +00:00
|
|
|
|
{
|
|
|
|
|
static bool IsNumber(char c)
|
|
|
|
|
{
|
2023-10-15 02:28:46 +00:00
|
|
|
|
if (c >= '0')
|
2022-06-18 18:04:24 +00:00
|
|
|
|
return c <= '9';
|
|
|
|
|
return (uint)(c - '0') <= 9;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int ctr = 0;
|
|
|
|
|
foreach (var c in str)
|
|
|
|
|
{
|
|
|
|
|
if (IsNumber(c))
|
|
|
|
|
++ctr;
|
2018-10-09 00:57:34 +00:00
|
|
|
|
}
|
2022-06-18 18:04:24 +00:00
|
|
|
|
return ctr;
|
2018-06-24 05:00:01 +00:00
|
|
|
|
}
|
|
|
|
|
}
|