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.
This commit is contained in:
Kurt 2023-06-03 18:52:05 -07:00
parent e3d8d167be
commit fa8e65f9e5
15 changed files with 94 additions and 122 deletions

View file

@ -133,7 +133,7 @@ public sealed class BK4 : G4PKM
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 sealed class BK4 : G4PKM
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()
{

View file

@ -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<int> 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);
}
}

View file

@ -188,23 +188,7 @@ public sealed class PKH : PKM, IHandlerLanguage, IFormArgument, IHomeTrack, IBat
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

View file

@ -83,22 +83,7 @@ public sealed class PA8 : PKM, ISanityChecksum,
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()

View file

@ -203,7 +203,7 @@ public sealed class PB7 : G6PKM, IHyperTrain, IAwakened, IScaledSizeValue, IComb
// 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); }

View file

@ -131,7 +131,7 @@ public sealed class PK3 : G3PKM, ISanityChecksum
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); }

View file

@ -113,7 +113,7 @@ public sealed class PK4 : G4PKM
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); }

View file

@ -275,23 +275,7 @@ public sealed class PK5 : PKM, ISanityChecksum,
// 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;

View file

@ -290,7 +290,7 @@ public sealed class PK6 : G6PKM, IRibbonSetEvent3, IRibbonSetEvent4, IRibbonSetC
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); }

View file

@ -314,7 +314,7 @@ public sealed class PK7 : G6PKM, IRibbonSetEvent3, IRibbonSetEvent4, IRibbonSetC
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); }

View file

@ -86,22 +86,7 @@ public sealed class PK9 : PKM, ISanityChecksum, ITeraType, ITechRecord, IObedien
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()

View file

@ -116,7 +116,7 @@ public sealed class RK4 : G4PKM
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); }

View file

@ -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; }

View file

@ -48,22 +48,8 @@ public abstract class G6PKM : PKM, ISanityChecksum
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()

View file

@ -59,22 +59,7 @@ public abstract class G8PKM : PKM, ISanityChecksum,
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()