From fa8e65f9e5c8b84282f91c880955256d13776b1f Mon Sep 17 00:00:00 2001 From: Kurt Date: Sat, 3 Jun 2023 18:52:05 -0700 Subject: [PATCH] Extract Characteristic calc to static class Reused logic, easier unit testing, better performance. Old method would do max of 6 properties (that each fetch 32bits and bitshift themselves); now we just fetch once and shift calc accordingly. --- PKHeX.Core/PKM/BK4.cs | 4 +- PKHeX.Core/PKM/EntityCharacteristic.cs | 76 ++++++++++++++++++++++++++ PKHeX.Core/PKM/HOME/PKH.cs | 18 +----- PKHeX.Core/PKM/PA8.cs | 17 +----- PKHeX.Core/PKM/PB7.cs | 2 +- PKHeX.Core/PKM/PK3.cs | 2 +- PKHeX.Core/PKM/PK4.cs | 2 +- PKHeX.Core/PKM/PK5.cs | 18 +----- PKHeX.Core/PKM/PK6.cs | 2 +- PKHeX.Core/PKM/PK7.cs | 2 +- PKHeX.Core/PKM/PK9.cs | 17 +----- PKHeX.Core/PKM/RK4.cs | 2 +- PKHeX.Core/PKM/Shared/G4PKM.cs | 19 +------ PKHeX.Core/PKM/Shared/G6PKM.cs | 18 +----- PKHeX.Core/PKM/Shared/G8PKM.cs | 17 +----- 15 files changed, 94 insertions(+), 122 deletions(-) create mode 100644 PKHeX.Core/PKM/EntityCharacteristic.cs diff --git a/PKHeX.Core/PKM/BK4.cs b/PKHeX.Core/PKM/BK4.cs index ce4d300db..32c10fe7d 100644 --- a/PKHeX.Core/PKM/BK4.cs +++ b/PKHeX.Core/PKM/BK4.cs @@ -133,7 +133,7 @@ public override uint EXP public override int Move2_PPUps { get => Data[0x35]; set => Data[0x35] = (byte)value; } public override int Move3_PPUps { get => Data[0x36]; set => Data[0x36] = (byte)value; } public override int Move4_PPUps { get => Data[0x37]; set => Data[0x37] = (byte)value; } - private uint IV32 { get => ReadUInt32BigEndian(Data.AsSpan(0x38)); set => WriteUInt32BigEndian(Data.AsSpan(0x38), value); } + protected internal override uint IV32 { get => ReadUInt32BigEndian(Data.AsSpan(0x38)); set => WriteUInt32BigEndian(Data.AsSpan(0x38), value); } public override int IV_SPD { get => (int)(IV32 >> 02) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 02)) | ((value > 31 ? 31u : (uint)value) << 02); } public override int IV_SPA { get => (int)(IV32 >> 07) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 07)) | ((value > 31 ? 31u : (uint)value) << 07); } public override int IV_SPE { get => (int)(IV32 >> 12) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 12)) | ((value > 31 ? 31u : (uint)value) << 12); } @@ -284,6 +284,8 @@ public override ushort Met_LocationDP public override int Stat_SPA { get; set; } public override int Stat_SPD { get; set; } + public override int Characteristic => EntityCharacteristic.GetCharacteristicInvertFields(PID, IV32); + // Methods protected override ushort CalculateChecksum() { diff --git a/PKHeX.Core/PKM/EntityCharacteristic.cs b/PKHeX.Core/PKM/EntityCharacteristic.cs new file mode 100644 index 000000000..f5fc992a8 --- /dev/null +++ b/PKHeX.Core/PKM/EntityCharacteristic.cs @@ -0,0 +1,76 @@ +using System; + +namespace PKHeX.Core; + +public static class EntityCharacteristic +{ + public static int GetCharacteristic(uint ec, uint iv32) + { + int index = (int)(ec % 6); + + int maxStatIndex = index; + var maxStatValue = 0u; + do + { + var value = iv32 >> (index * 5) & 0x1F; + if (value > maxStatValue) + { + maxStatIndex = index; + maxStatValue = value; + } + if (index != 5) + index++; + else + index = 0; + } while (maxStatIndex != index); + + return (maxStatIndex * 5) + ((int)maxStatValue % 5); + } + + public static int GetCharacteristic(uint ec, Span ivs) + { + int index = (int)(ec % 6); + + int maxStatIndex = index; + var maxStatValue = 0; + do + { + var value = ivs[index]; + if (value > maxStatValue) + { + maxStatIndex = index; + maxStatValue = value; + } + if (index != 5) + index++; + else + index = 0; + } while (maxStatIndex != index); + + return (maxStatIndex * 5) + (maxStatValue % 5); + } + + public static int GetCharacteristicInvertFields(uint ec, uint iv32) + { + int index = (int)(ec % 6); + + int maxStatIndex = index; + var maxStatValue = 0u; + do + { + // IVs are stored in reverse order, get the bits from the end of the IV value + var value = iv32 >> (27 - (index * 5)) & 0x1F; + if (value > maxStatValue) + { + maxStatIndex = index; + maxStatValue = value; + } + if (index != 5) + index++; + else + index = 0; + } while (maxStatIndex != index); + + return (maxStatIndex * 5) + ((int)maxStatValue % 5); + } +} diff --git a/PKHeX.Core/PKM/HOME/PKH.cs b/PKHeX.Core/PKM/HOME/PKH.cs index 105e75543..a1fa93041 100644 --- a/PKHeX.Core/PKM/HOME/PKH.cs +++ b/PKHeX.Core/PKM/HOME/PKH.cs @@ -188,23 +188,7 @@ private static byte[] DecryptHome(byte[] data) public override uint PSV => ((PID >> 16) ^ (PID & 0xFFFF)) >> 4; public override uint TSV => (uint)(TID16 ^ SID16) >> 4; - - public override int Characteristic - { - get - { - int pm6 = (int)(EncryptionConstant % 6); - int maxIV = MaximumIV; - int pm6stat = 0; - for (int i = 0; i < 6; i++) - { - pm6stat = (pm6 + i) % 6; - if (GetIV(pm6stat) == maxIV) - break; - } - return (pm6stat * 5) + (maxIV % 5); - } - } + public override int Characteristic => EntityCharacteristic.GetCharacteristic(EncryptionConstant, stackalloc int[] {IV_HP, IV_ATK, IV_DEF, IV_SPE, IV_SPA, IV_SPD}); #endregion diff --git a/PKHeX.Core/PKM/PA8.cs b/PKHeX.Core/PKM/PA8.cs index f98da93fa..4e970ce0a 100644 --- a/PKHeX.Core/PKM/PA8.cs +++ b/PKHeX.Core/PKM/PA8.cs @@ -83,22 +83,7 @@ public override int CurrentFriendship public override bool IsUntraded => Data[0xB8] == 0 && Data[0xB8 + 1] == 0 && Format == Generation; // immediately terminated HT_Name data (\0) // Complex Generated Attributes - public override int Characteristic - { - get - { - int pm6 = (int)(EncryptionConstant % 6); - int maxIV = MaximumIV; - int pm6stat = 0; - for (int i = 0; i < 6; i++) - { - pm6stat = (pm6 + i) % 6; - if (GetIV(pm6stat) == maxIV) - break; - } - return (pm6stat * 5) + (maxIV % 5); - } - } + public override int Characteristic => EntityCharacteristic.GetCharacteristic(EncryptionConstant, IV32); // Methods protected override byte[] Encrypt() diff --git a/PKHeX.Core/PKM/PB7.cs b/PKHeX.Core/PKM/PB7.cs index 195d2fa3d..74502c6f1 100644 --- a/PKHeX.Core/PKM/PB7.cs +++ b/PKHeX.Core/PKM/PB7.cs @@ -203,7 +203,7 @@ public override ushort RelearnMove4 // 0x72 Unused // 0x73 Unused - private uint IV32 { get => ReadUInt32LittleEndian(Data.AsSpan(0x74)); set => WriteUInt32LittleEndian(Data.AsSpan(0x74), value); } + protected override uint IV32 { get => ReadUInt32LittleEndian(Data.AsSpan(0x74)); set => WriteUInt32LittleEndian(Data.AsSpan(0x74), value); } public override int IV_HP { get => (int)(IV32 >> 00) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 00)) | ((value > 31 ? 31u : (uint)value) << 00); } public override int IV_ATK { get => (int)(IV32 >> 05) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 05)) | ((value > 31 ? 31u : (uint)value) << 05); } public override int IV_DEF { get => (int)(IV32 >> 10) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 10)) | ((value > 31 ? 31u : (uint)value) << 10); } diff --git a/PKHeX.Core/PKM/PK3.cs b/PKHeX.Core/PKM/PK3.cs index 838dd46e1..4180f0548 100644 --- a/PKHeX.Core/PKM/PK3.cs +++ b/PKHeX.Core/PKM/PK3.cs @@ -131,7 +131,7 @@ public override ushort Species public override int Ball { get => (Origins >> 11) & 0xF; set => Origins = (ushort)((Origins & ~0x7800) | ((value & 0xF) << 11)); } public override int OT_Gender { get => (Origins >> 15) & 1; set => Origins = (ushort)((Origins & ~(1 << 15)) | ((value & 1) << 15)); } - public uint IV32 { get => ReadUInt32LittleEndian(Data.AsSpan(0x48)); set => WriteUInt32LittleEndian(Data.AsSpan(0x48), value); } + private uint IV32 { get => ReadUInt32LittleEndian(Data.AsSpan(0x48)); set => WriteUInt32LittleEndian(Data.AsSpan(0x48), value); } public override int IV_HP { get => (int)(IV32 >> 00) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 00)) | ((value > 31 ? 31u : (uint)value) << 00); } public override int IV_ATK { get => (int)(IV32 >> 05) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 05)) | ((value > 31 ? 31u : (uint)value) << 05); } public override int IV_DEF { get => (int)(IV32 >> 10) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 10)) | ((value > 31 ? 31u : (uint)value) << 10); } diff --git a/PKHeX.Core/PKM/PK4.cs b/PKHeX.Core/PKM/PK4.cs index d9e97f87c..17d31e92d 100644 --- a/PKHeX.Core/PKM/PK4.cs +++ b/PKHeX.Core/PKM/PK4.cs @@ -113,7 +113,7 @@ private static byte[] DecryptParty(byte[] data) public override int Move2_PPUps { get => Data[0x35]; set => Data[0x35] = (byte)value; } public override int Move3_PPUps { get => Data[0x36]; set => Data[0x36] = (byte)value; } public override int Move4_PPUps { get => Data[0x37]; set => Data[0x37] = (byte)value; } - public uint IV32 { get => ReadUInt32LittleEndian(Data.AsSpan(0x38)); set => WriteUInt32LittleEndian(Data.AsSpan(0x38), value); } + protected internal override uint IV32 { get => ReadUInt32LittleEndian(Data.AsSpan(0x38)); set => WriteUInt32LittleEndian(Data.AsSpan(0x38), value); } public override int IV_HP { get => (int)(IV32 >> 00) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 00)) | ((value > 31 ? 31u : (uint)value) << 00); } public override int IV_ATK { get => (int)(IV32 >> 05) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 05)) | ((value > 31 ? 31u : (uint)value) << 05); } public override int IV_DEF { get => (int)(IV32 >> 10) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 10)) | ((value > 31 ? 31u : (uint)value) << 10); } diff --git a/PKHeX.Core/PKM/PK5.cs b/PKHeX.Core/PKM/PK5.cs index 7706aeb9c..2479c87ac 100644 --- a/PKHeX.Core/PKM/PK5.cs +++ b/PKHeX.Core/PKM/PK5.cs @@ -275,23 +275,7 @@ private static byte[] DecryptParty(byte[] data) // Generated Attributes public override uint PSV => ((PID >> 16) ^ (PID & 0xFFFF)) >> 3; public override uint TSV => (uint)(TID16 ^ SID16) >> 3; - - public override int Characteristic - { - get - { - int pm6 = (int)(PID % 6); // PID - int maxIV = MaximumIV; - int pm6stat = 0; - for (int i = 0; i < 6; i++) - { - pm6stat = (pm6 + i) % 6; - if (GetIV(pm6stat) == maxIV) - break; - } - return (pm6stat * 5) + (maxIV % 5); - } - } + public override int Characteristic => EntityCharacteristic.GetCharacteristic(PID, IV32); // Maximums public override ushort MaxMoveID => Legal.MaxMoveID_5; diff --git a/PKHeX.Core/PKM/PK6.cs b/PKHeX.Core/PKM/PK6.cs index 203a4f137..cbcdcd0e8 100644 --- a/PKHeX.Core/PKM/PK6.cs +++ b/PKHeX.Core/PKM/PK6.cs @@ -290,7 +290,7 @@ public override ushort RelearnMove4 public bool SecretSuperTrainingUnlocked { get => (Data[0x72] & 1) == 1; set => Data[0x72] = (byte)((Data[0x72] & ~1) | (value ? 1 : 0)); } public bool SecretSuperTrainingComplete { get => (Data[0x72] & 2) == 2; set => Data[0x72] = (byte)((Data[0x72] & ~2) | (value ? 2 : 0)); } // 0x73 Unused - private uint IV32 { get => ReadUInt32LittleEndian(Data.AsSpan(0x74)); set => WriteUInt32LittleEndian(Data.AsSpan(0x74), value); } + protected override uint IV32 { get => ReadUInt32LittleEndian(Data.AsSpan(0x74)); set => WriteUInt32LittleEndian(Data.AsSpan(0x74), value); } public override int IV_HP { get => (int)(IV32 >> 00) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 00)) | ((value > 31 ? 31u : (uint)value) << 00); } public override int IV_ATK { get => (int)(IV32 >> 05) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 05)) | ((value > 31 ? 31u : (uint)value) << 05); } public override int IV_DEF { get => (int)(IV32 >> 10) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 10)) | ((value > 31 ? 31u : (uint)value) << 10); } diff --git a/PKHeX.Core/PKM/PK7.cs b/PKHeX.Core/PKM/PK7.cs index ddfdd952c..e96637eda 100644 --- a/PKHeX.Core/PKM/PK7.cs +++ b/PKHeX.Core/PKM/PK7.cs @@ -314,7 +314,7 @@ public override ushort RelearnMove4 public bool SecretSuperTrainingUnlocked { get => (Data[0x72] & 1) == 1; set => Data[0x72] = (byte)((Data[0x72] & ~1) | (value ? 1 : 0)); } public bool SecretSuperTrainingComplete { get => (Data[0x72] & 2) == 2; set => Data[0x72] = (byte)((Data[0x72] & ~2) | (value ? 2 : 0)); } // 0x73 Unused - private uint IV32 { get => ReadUInt32LittleEndian(Data.AsSpan(0x74)); set => WriteUInt32LittleEndian(Data.AsSpan(0x74), value); } + protected override uint IV32 { get => ReadUInt32LittleEndian(Data.AsSpan(0x74)); set => WriteUInt32LittleEndian(Data.AsSpan(0x74), value); } public override int IV_HP { get => (int)(IV32 >> 00) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 00)) | ((value > 31 ? 31u : (uint)value) << 00); } public override int IV_ATK { get => (int)(IV32 >> 05) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 05)) | ((value > 31 ? 31u : (uint)value) << 05); } public override int IV_DEF { get => (int)(IV32 >> 10) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 10)) | ((value > 31 ? 31u : (uint)value) << 10); } diff --git a/PKHeX.Core/PKM/PK9.cs b/PKHeX.Core/PKM/PK9.cs index 471020bec..6f634a1bb 100644 --- a/PKHeX.Core/PKM/PK9.cs +++ b/PKHeX.Core/PKM/PK9.cs @@ -86,22 +86,7 @@ public override int CurrentFriendship public bool IsUnhatchedEgg => Version == 0 && IsEgg; // Complex Generated Attributes - public override int Characteristic - { - get - { - int pm6 = (int)(EncryptionConstant % 6); - int maxIV = MaximumIV; - int pm6stat = 0; - for (int i = 0; i < 6; i++) - { - pm6stat = (pm6 + i) % 6; - if (GetIV(pm6stat) == maxIV) - break; - } - return (pm6stat * 5) + (maxIV % 5); - } - } + public override int Characteristic => EntityCharacteristic.GetCharacteristic(EncryptionConstant, IV32); // Methods protected override byte[] Encrypt() diff --git a/PKHeX.Core/PKM/RK4.cs b/PKHeX.Core/PKM/RK4.cs index 1d00649d5..1e512caa5 100644 --- a/PKHeX.Core/PKM/RK4.cs +++ b/PKHeX.Core/PKM/RK4.cs @@ -116,7 +116,7 @@ private static byte[] Decrypt(byte[] data) public override int Move2_PPUps { get => Data[0x35]; set => Data[0x35] = (byte)value; } public override int Move3_PPUps { get => Data[0x36]; set => Data[0x36] = (byte)value; } public override int Move4_PPUps { get => Data[0x37]; set => Data[0x37] = (byte)value; } - public uint IV32 { get => ReadUInt32LittleEndian(Data.AsSpan(0x38)); set => WriteUInt32LittleEndian(Data.AsSpan(0x38), value); } + protected internal override uint IV32 { get => ReadUInt32LittleEndian(Data.AsSpan(0x38)); set => WriteUInt32LittleEndian(Data.AsSpan(0x38), value); } public override int IV_HP { get => (int)(IV32 >> 00) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 00)) | ((value > 31 ? 31u : (uint)value) << 00); } public override int IV_ATK { get => (int)(IV32 >> 05) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 05)) | ((value > 31 ? 31u : (uint)value) << 05); } public override int IV_DEF { get => (int)(IV32 >> 10) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 10)) | ((value > 31 ? 31u : (uint)value) << 10); } diff --git a/PKHeX.Core/PKM/Shared/G4PKM.cs b/PKHeX.Core/PKM/Shared/G4PKM.cs index 568a33bff..9d0b17369 100644 --- a/PKHeX.Core/PKM/Shared/G4PKM.cs +++ b/PKHeX.Core/PKM/Shared/G4PKM.cs @@ -25,23 +25,8 @@ public abstract class G4PKM : PKM, public sealed override uint TSV => (uint)(TID16 ^ SID16) >> 3; protected bool PtHGSS => Pt || HGSS; - - public sealed override int Characteristic - { - get - { - int pm6 = (int)(EncryptionConstant % 6); // PID - int maxIV = MaximumIV; - int pm6stat = 0; - for (int i = 0; i < 6; i++) - { - pm6stat = (pm6 + i) % 6; - if (GetIV(pm6stat) == maxIV) - break; - } - return (pm6stat * 5) + (maxIV % 5); - } - } + protected internal abstract uint IV32 { get; set; } + public override int Characteristic => EntityCharacteristic.GetCharacteristic(PID, IV32); public abstract ushort Sanity { get; set; } public abstract ushort Checksum { get; set; } diff --git a/PKHeX.Core/PKM/Shared/G6PKM.cs b/PKHeX.Core/PKM/Shared/G6PKM.cs index 381e9bcf6..71bfae194 100644 --- a/PKHeX.Core/PKM/Shared/G6PKM.cs +++ b/PKHeX.Core/PKM/Shared/G6PKM.cs @@ -48,22 +48,8 @@ public int OppositeFriendship public sealed override bool IsUntraded => Data[0x78] == 0 && Data[0x78 + 1] == 0 && Format == Generation; // immediately terminated HT_Name data (\0) // Complex Generated Attributes - public sealed override int Characteristic - { - get - { - int pm6 = (int)(EncryptionConstant % 6); - int maxIV = MaximumIV; - int pm6stat = 0; - for (int i = 0; i < 6; i++) - { - pm6stat = (pm6 + i) % 6; - if (GetIV(pm6stat) == maxIV) - break; - } - return (pm6stat * 5) + (maxIV % 5); - } - } + protected abstract uint IV32 { get; set; } + public override int Characteristic => EntityCharacteristic.GetCharacteristic(EncryptionConstant, IV32); // Methods protected sealed override byte[] Encrypt() diff --git a/PKHeX.Core/PKM/Shared/G8PKM.cs b/PKHeX.Core/PKM/Shared/G8PKM.cs index 1a6276d52..9454f8aaa 100644 --- a/PKHeX.Core/PKM/Shared/G8PKM.cs +++ b/PKHeX.Core/PKM/Shared/G8PKM.cs @@ -59,22 +59,7 @@ public override int CurrentFriendship public override bool IsUntraded => Data[0xA8] == 0 && Data[0xA8 + 1] == 0 && Format == Generation; // immediately terminated HT_Name data (\0) // Complex Generated Attributes - public override int Characteristic - { - get - { - int pm6 = (int)(EncryptionConstant % 6); - int maxIV = MaximumIV; - int pm6stat = 0; - for (int i = 0; i < 6; i++) - { - pm6stat = (pm6 + i) % 6; - if (GetIV(pm6stat) == maxIV) - break; - } - return (pm6stat * 5) + (maxIV % 5); - } - } + public override int Characteristic => EntityCharacteristic.GetCharacteristic(EncryptionConstant, IV32); // Methods protected override byte[] Encrypt()