From 5af96eab954241fb28b80188e50b34d2d2d50def Mon Sep 17 00:00:00 2001 From: Kurt Date: Sun, 12 May 2024 23:40:50 -0500 Subject: [PATCH] Extract switch-entity HT update logic, fix Closes #4227 SlotWrite no longer revises the data incorrectly if ot&gender mismatch, clear memories and set new values can retain wrong values by having ot&gender shared between games --- PKHeX.Core/Legality/Bulk/HandlerChecker.cs | 7 +++- .../Legality/Verifiers/HistoryVerifier.cs | 6 ++- PKHeX.Core/PKM/HOME/PKH.cs | 39 +++++++++++++++++++ PKHeX.Core/PKM/PA8.cs | 12 +----- PKHeX.Core/PKM/PB7.cs | 9 ++++- PKHeX.Core/PKM/PB8.cs | 13 +------ PKHeX.Core/PKM/PK6.cs | 15 +++---- PKHeX.Core/PKM/PK7.cs | 16 ++++---- PKHeX.Core/PKM/PK8.cs | 13 +------ PKHeX.Core/PKM/PK9.cs | 14 +------ PKHeX.WinForms/Subforms/PKM Editors/Text.cs | 23 ++++++----- 11 files changed, 91 insertions(+), 76 deletions(-) diff --git a/PKHeX.Core/Legality/Bulk/HandlerChecker.cs b/PKHeX.Core/Legality/Bulk/HandlerChecker.cs index 9fd1367eb..7fed3341f 100644 --- a/PKHeX.Core/Legality/Bulk/HandlerChecker.cs +++ b/PKHeX.Core/Legality/Bulk/HandlerChecker.cs @@ -1,3 +1,4 @@ +using System; using static PKHeX.Core.CheckIdentifier; namespace PKHeX.Core.Bulk; @@ -35,7 +36,11 @@ private static void Verify(BulkAnalysis input, SlotCache cs) if (flag != 1) return; - if (pk.HandlingTrainerName != tr.OT) + Span ht = stackalloc char[pk.TrashCharCountTrainer]; + var len = pk.LoadString(pk.HandlingTrainerTrash, ht); + ht = ht[..len]; + + if (!ht.SequenceEqual(tr.OT)) input.AddLine(cs, LegalityCheckStrings.LTransferHTMismatchName, Trainer); if (pk is IHandlerLanguage h && h.HandlingTrainerLanguage != tr.Language) input.AddLine(cs, LegalityCheckStrings.LTransferHTMismatchLanguage, Trainer); diff --git a/PKHeX.Core/Legality/Verifiers/HistoryVerifier.cs b/PKHeX.Core/Legality/Verifiers/HistoryVerifier.cs index 14d36b7d1..761d14652 100644 --- a/PKHeX.Core/Legality/Verifiers/HistoryVerifier.cs +++ b/PKHeX.Core/Legality/Verifiers/HistoryVerifier.cs @@ -70,7 +70,11 @@ private void VerifyHandlerState(LegalityAnalysis data, bool neverOT) if (flag == 1) { - if (pk.HandlingTrainerName != tr.OT) + Span ht = stackalloc char[pk.TrashCharCountTrainer]; + var len = pk.LoadString(pk.HandlingTrainerTrash, ht); + ht = ht[..len]; + + if (!ht.SequenceEqual(tr.OT)) data.AddLine(GetInvalid(LTransferHTMismatchName)); if (pk is IHandlerLanguage h && h.HandlingTrainerLanguage != tr.Language) data.AddLine(Get(LTransferHTMismatchLanguage, Severity.Fishy)); diff --git a/PKHeX.Core/PKM/HOME/PKH.cs b/PKHeX.Core/PKM/HOME/PKH.cs index f23eab44f..17c1226a2 100644 --- a/PKHeX.Core/PKM/HOME/PKH.cs +++ b/PKHeX.Core/PKM/HOME/PKH.cs @@ -432,4 +432,43 @@ public override int LoadString(ReadOnlySpan data, Span destBuffer) => StringConverter8.LoadString(data, destBuffer); public override int SetString(Span destBuffer, ReadOnlySpan value, int maxLength, StringConverterOption option) => StringConverter8.SetString(destBuffer, value, maxLength, option); + + /// + /// Revises the Handler details of a to match the current . + /// + /// Logic used starting in SW/SH games. + public static void UpdateHandler(T pk, ITrainerInfo tr) + where T : PKM, IHandlerLanguage, IMemoryHT + { + pk.CurrentHandler = 1; + + var gender = tr.Gender; + var other = tr.OT; + if (IsHandlerSame(pk, other, gender)) + return; + + // Clear the old Handler details and write our new ones. + pk.ClearMemoriesHT(); + pk.HandlingTrainerName = other; + pk.HandlingTrainerLanguage = (byte)tr.Language; + pk.HandlingTrainerGender = gender; + pk.HandlingTrainerFriendship = pk.PersonalInfo.BaseFriendship; + // Memories are deferred to the game. SW/SH does not immediately set memories. + } + + private static bool IsHandlerSame(T pk, ReadOnlySpan newHT, byte newGender) + where T : PKM, IHandlerLanguage + { + if (newGender != pk.HandlingTrainerGender) + return false; + + // Does not check Language or Version for equality! Can result in mismatches or empty (0) language value. + // Check the trainer string for equality (most expensive of all last). + + Span exist = stackalloc char[pk.TrashCharCountTrainer]; + var len = pk.LoadString(pk.HandlingTrainerTrash, exist); + exist = exist[..len]; + + return exist.SequenceEqual(newHT); + } } diff --git a/PKHeX.Core/PKM/PA8.cs b/PKHeX.Core/PKM/PA8.cs index 75deb58cf..ba6f2cac0 100644 --- a/PKHeX.Core/PKM/PA8.cs +++ b/PKHeX.Core/PKM/PA8.cs @@ -628,17 +628,7 @@ private bool TradeOT(ITrainerInfo tr) return true; } - private void TradeHT(ITrainerInfo tr) - { - if (HandlingTrainerName != tr.OT) - { - HandlingTrainerFriendship = PersonalInfo.BaseFriendship; - HandlingTrainerName = tr.OT; - } - CurrentHandler = 1; - HandlingTrainerGender = tr.Gender; - HandlingTrainerLanguage = (byte)tr.Language; - } + private void TradeHT(ITrainerInfo tr) => PKH.UpdateHandler(this, tr); // Maximums public override ushort MaxMoveID => Legal.MaxMoveID_8a; diff --git a/PKHeX.Core/PKM/PB7.cs b/PKHeX.Core/PKM/PB7.cs index 5a49358ac..a78959556 100644 --- a/PKHeX.Core/PKM/PB7.cs +++ b/PKHeX.Core/PKM/PB7.cs @@ -346,10 +346,15 @@ protected override bool TradeOT(ITrainerInfo tr) protected override void TradeHT(ITrainerInfo tr) { - if (HandlingTrainerName != tr.OT) + Span ht = stackalloc char[TrashCharCountTrainer]; + var len = LoadString(HandlingTrainerTrash, ht); + ht = ht[..len]; + + var other = tr.OT; + if (!ht.SequenceEqual(other)) { + HandlingTrainerName = other; HandlingTrainerFriendship = CurrentFriendship; // copy friendship instead of resetting (don't alter CP) - HandlingTrainerName = tr.OT; } CurrentHandler = 1; HandlingTrainerGender = tr.Gender; diff --git a/PKHeX.Core/PKM/PB8.cs b/PKHeX.Core/PKM/PB8.cs index 87491b229..b0d6012ae 100644 --- a/PKHeX.Core/PKM/PB8.cs +++ b/PKHeX.Core/PKM/PB8.cs @@ -121,18 +121,7 @@ private bool TradeOT(ITrainerInfo tr) return true; } - private void TradeHT(ITrainerInfo tr) - { - if (HandlingTrainerName != tr.OT) - { - HandlingTrainerFriendship = PersonalInfo.BaseFriendship; - HandlingTrainerName = tr.OT; - } - CurrentHandler = 1; - HandlingTrainerGender = tr.Gender; - HandlingTrainerLanguage = (byte)tr.Language; - //this.SetTradeMemoryHT8(); - } + private void TradeHT(ITrainerInfo tr) => PKH.UpdateHandler(this, tr); // Maximums public override ushort MaxMoveID => Legal.MaxMoveID_8b; diff --git a/PKHeX.Core/PKM/PK6.cs b/PKHeX.Core/PKM/PK6.cs index ace2a4f5f..99ffa6ae6 100644 --- a/PKHeX.Core/PKM/PK6.cs +++ b/PKHeX.Core/PKM/PK6.cs @@ -428,17 +428,18 @@ protected override bool TradeOT(ITrainerInfo tr) protected override void TradeHT(ITrainerInfo tr) { - if (tr.OT != HandlingTrainerName || tr.Gender != HandlingTrainerGender || (Geo1_Country == 0 && Geo1_Region == 0 && !IsUntradedEvent6)) - { - if (tr is IRegionOrigin o) - this.TradeGeoLocation(o.Country, o.Region); - } + Span ht = stackalloc char[TrashCharCountTrainer]; + var len = LoadString(HandlingTrainerTrash, ht); + ht = ht[..len]; - if (tr.OT != HandlingTrainerName) + var other = tr.OT; + if (!ht.SequenceEqual(other) || tr.Gender != HandlingTrainerGender || (Geo1_Country == 0 && Geo1_Region == 0 && !IsUntradedEvent6)) { + HandlingTrainerName = other; HandlingTrainerFriendship = PersonalInfo.BaseFriendship; HandlingTrainerAffection = 0; - HandlingTrainerName = tr.OT; + if (tr is IRegionOrigin o) + this.TradeGeoLocation(o.Country, o.Region); } CurrentHandler = 1; HandlingTrainerGender = tr.Gender; diff --git a/PKHeX.Core/PKM/PK7.cs b/PKHeX.Core/PKM/PK7.cs index ffada70e2..551291eb5 100644 --- a/PKHeX.Core/PKM/PK7.cs +++ b/PKHeX.Core/PKM/PK7.cs @@ -486,17 +486,19 @@ protected override bool TradeOT(ITrainerInfo tr) protected override void TradeHT(ITrainerInfo tr) { - if (tr.OT != HandlingTrainerName || tr.Gender != HandlingTrainerGender || (Geo1_Country == 0 && Geo1_Region == 0 && !IsUntradedEvent6)) - { - // No geolocations are set ingame -- except for bank transfers. Don't emulate bank transfers - // this.TradeGeoLocation(tr.Country, tr.SubRegion); - } + // No geolocations are set in-game -- except for bank transfers. Don't emulate bank transfers + // this.TradeGeoLocation(tr.Country, tr.SubRegion); - if (HandlingTrainerName != tr.OT) + Span ht = stackalloc char[TrashCharCountTrainer]; + var len = LoadString(HandlingTrainerTrash, ht); + ht = ht[..len]; + + var other = tr.OT; + if (!ht.SequenceEqual(other)) { + HandlingTrainerName = other; HandlingTrainerFriendship = PersonalInfo.BaseFriendship; HandlingTrainerAffection = 0; - HandlingTrainerName = tr.OT; } CurrentHandler = 1; HandlingTrainerGender = tr.Gender; diff --git a/PKHeX.Core/PKM/PK8.cs b/PKHeX.Core/PKM/PK8.cs index 82e3f0501..798e11ec6 100644 --- a/PKHeX.Core/PKM/PK8.cs +++ b/PKHeX.Core/PKM/PK8.cs @@ -103,18 +103,7 @@ private bool TradeOT(ITrainerInfo tr) return true; } - private void TradeHT(ITrainerInfo tr) - { - if (HandlingTrainerName != tr.OT) - { - HandlingTrainerFriendship = 50; - HandlingTrainerName = tr.OT; - } - CurrentHandler = 1; - HandlingTrainerGender = tr.Gender; - HandlingTrainerLanguage = (byte)tr.Language; - this.SetTradeMemoryHT8(); - } + private void TradeHT(ITrainerInfo tr) => PKH.UpdateHandler(this, tr); // Maximums public override ushort MaxMoveID => Legal.MaxMoveID_8; diff --git a/PKHeX.Core/PKM/PK9.cs b/PKHeX.Core/PKM/PK9.cs index 1ea466d5b..83044d7a9 100644 --- a/PKHeX.Core/PKM/PK9.cs +++ b/PKHeX.Core/PKM/PK9.cs @@ -661,19 +661,7 @@ private bool TradeOT(ITrainerInfo tr) return true; } - private void TradeHT(ITrainerInfo tr) - { - if (HandlingTrainerName != tr.OT) - { - HandlingTrainerFriendship = 50; - HandlingTrainerName = tr.OT; - } - CurrentHandler = 1; - HandlingTrainerGender = tr.Gender; - if (HandlingTrainerLanguage == 0) - this.ClearMemoriesHT(); - HandlingTrainerLanguage = (byte)tr.Language; - } + private void TradeHT(ITrainerInfo tr) => PKH.UpdateHandler(this, tr); // Maximums public override ushort MaxMoveID => Legal.MaxMoveID_9; diff --git a/PKHeX.WinForms/Subforms/PKM Editors/Text.cs b/PKHeX.WinForms/Subforms/PKM Editors/Text.cs index 0978e255e..b81256a4f 100644 --- a/PKHeX.WinForms/Subforms/PKM Editors/Text.cs +++ b/PKHeX.WinForms/Subforms/PKM Editors/Text.cs @@ -10,6 +10,13 @@ namespace PKHeX.WinForms; public partial class TrashEditor : Form { private readonly IStringConverter Converter; + private readonly ToolTip Tip = new() { InitialDelay = 200, IsBalloon = false, AutoPopDelay = 32_767 }; + private readonly List Bytes = []; + public string FinalString { get; private set; } + public byte[] FinalBytes { get; private set; } + + private readonly byte[] Raw; + private bool editing; public TrashEditor(TextBoxBase TB_NN, IStringConverter sav, byte generation) : this(TB_NN, [], sav, generation) { } @@ -60,12 +67,6 @@ public TrashEditor(TextBoxBase TB_NN, Span raw, IStringConverter converter }; } - private readonly ToolTip Tip = new() { InitialDelay = 200, IsBalloon = false, AutoPopDelay = 32_767 }; - private readonly List Bytes = []; - public string FinalString; - public byte[] FinalBytes; - private readonly byte[] Raw; - private bool editing; private void B_Cancel_Click(object sender, EventArgs e) => Close(); private void B_Save_Click(object sender, EventArgs e) @@ -153,8 +154,10 @@ private void UpdateString(object sender, EventArgs e) return; editing = true; // build bytes - byte[] data = SetString(TB_Text.Text); - Array.Copy(data, Raw, Math.Min(data.Length, Raw.Length)); + ReadOnlySpan data = SetString(TB_Text.Text); + if (data.Length > Raw.Length) + data = data[..Raw.Length]; + data.CopyTo(Raw); for (int i = 0; i < Raw.Length; i++) Bytes[i].Value = Raw[i]; editing = false; @@ -163,8 +166,8 @@ private void UpdateString(object sender, EventArgs e) private void B_ApplyTrash_Click(object sender, EventArgs e) { string text = GetTrashString(); - byte[] data = SetString(text); - byte[] current = SetString(TB_Text.Text); + ReadOnlySpan data = SetString(text); + ReadOnlySpan current = SetString(TB_Text.Text); if (data.Length <= current.Length) { WinFormsUtil.Alert("Trash byte layer is hidden by current text.",