mirror of
https://github.com/kwsch/PKHeX
synced 2024-11-26 22:10:21 +00:00
Convert checksum operations to span-based
Fix SAV.Data references in SAV_Misc4 to use the General block instead
This commit is contained in:
parent
014835ed14
commit
871f9b0627
17 changed files with 61 additions and 68 deletions
|
@ -50,19 +50,19 @@ namespace PKHeX.Core
|
|||
public sealed class BlockInfo6 : BlockInfo3DS
|
||||
{
|
||||
public BlockInfo6(int bo, uint id, int ofs, int len) : base(bo, id, ofs, len) { }
|
||||
protected override ushort GetChecksum(byte[] data) => Checksums.CRC16_CCITT(data, Offset, Length);
|
||||
protected override ushort GetChecksum(byte[] data) => Checksums.CRC16_CCITT(new ReadOnlySpan<byte>(data, Offset, Length));
|
||||
}
|
||||
|
||||
public sealed class BlockInfo7 : BlockInfo3DS
|
||||
{
|
||||
public BlockInfo7(int bo, uint id, int ofs, int len) : base(bo, id, ofs, len) { }
|
||||
protected override ushort GetChecksum(byte[] data) => Checksums.CRC16(data, Offset, Length);
|
||||
protected override ushort GetChecksum(byte[] data) => Checksums.CRC16Invert(new ReadOnlySpan<byte>(data, Offset, Length));
|
||||
}
|
||||
|
||||
public sealed class BlockInfo7b : BlockInfo3DS
|
||||
{
|
||||
public BlockInfo7b(int bo, uint id, int ofs, int len) : base(bo, id, ofs, len) { }
|
||||
protected override ushort GetChecksum(byte[] data) => Checksums.CRC16NoInvert(data, Offset, Length);
|
||||
protected override ushort GetChecksum(byte[] data) => Checksums.CRC16NoInvert(new ReadOnlySpan<byte>(data, Offset, Length));
|
||||
}
|
||||
|
||||
public static class BlockInfoBEEFUtil
|
||||
|
|
|
@ -18,7 +18,7 @@ namespace PKHeX.Core
|
|||
ChecksumMirror = chkMirror;
|
||||
}
|
||||
|
||||
private ushort GetChecksum(byte[] data) => Checksums.CRC16_CCITT(data, Offset, Length);
|
||||
private ushort GetChecksum(byte[] data) => Checksums.CRC16_CCITT(new ReadOnlySpan<byte>(data, Offset, Length));
|
||||
|
||||
protected override bool ChecksumValid(byte[] data)
|
||||
{
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
using System;
|
||||
|
||||
namespace PKHeX.Core
|
||||
{
|
||||
/// <summary>
|
||||
|
@ -6,15 +8,16 @@ namespace PKHeX.Core
|
|||
public sealed class BlockInfoRSBOX : BlockInfo
|
||||
{
|
||||
public readonly uint SaveCount;
|
||||
public readonly uint Checksum;
|
||||
public readonly uint OriginalChecksum;
|
||||
private const int ChecksumRegionSize = 0x1FF8;
|
||||
|
||||
public BlockInfoRSBOX(byte[] data, int offset)
|
||||
{
|
||||
Offset = offset;
|
||||
Length = 0x1FFC;
|
||||
Length = 4 + ChecksumRegionSize;
|
||||
|
||||
// Values stored in Big Endian format
|
||||
Checksum = BigEndian.ToUInt32(data, Offset + 0);
|
||||
OriginalChecksum = BigEndian.ToUInt32(data, Offset);
|
||||
ID = BigEndian.ToUInt32(data, Offset + 4);
|
||||
SaveCount = BigEndian.ToUInt32(data, Offset + 8);
|
||||
}
|
||||
|
@ -38,9 +41,8 @@ namespace PKHeX.Core
|
|||
private uint GetChecksum(byte[] data)
|
||||
{
|
||||
int start = Offset + 4;
|
||||
int end = start + Length - 4;
|
||||
|
||||
return Checksums.CheckSum16BigInvert(data, start, end);
|
||||
var span = new ReadOnlySpan<byte>(data, start, ChecksumRegionSize);
|
||||
return Checksums.CheckSum16BigInvert(span);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -75,7 +75,7 @@ namespace PKHeX.Core
|
|||
{
|
||||
var boxOfs = GetBoxOffset(i) - ListHeaderSize;
|
||||
var size = BoxSize - 2;
|
||||
var chk = Checksums.CheckSum16(Data, boxOfs, size);
|
||||
var chk = Checksums.CheckSum16(new ReadOnlySpan<byte>(Data, boxOfs, size));
|
||||
var actual = BigEndian.ToUInt16(Data, boxOfs + size);
|
||||
return chk == actual;
|
||||
}
|
||||
|
@ -84,7 +84,7 @@ namespace PKHeX.Core
|
|||
{
|
||||
var boxOfs = GetBoxOffset(i) - ListHeaderSize;
|
||||
var size = BoxSize - 2;
|
||||
var chk = Checksums.CheckSum16(Data, boxOfs, size);
|
||||
var chk = Checksums.CheckSum16(new ReadOnlySpan<byte>(Data, boxOfs, size));
|
||||
BigEndian.GetBytes(chk).CopyTo(Data, boxOfs + size);
|
||||
}
|
||||
|
||||
|
|
|
@ -60,7 +60,7 @@ namespace PKHeX.Core
|
|||
{
|
||||
var boxOfs = GetBoxOffset(i) - ListHeaderSize;
|
||||
const int size = BoxSizeJ - 2;
|
||||
var chk = Checksums.CheckSum16(Data, boxOfs, size);
|
||||
var chk = Checksums.CheckSum16(new ReadOnlySpan<byte>(Data, boxOfs, size));
|
||||
var actual = BigEndian.ToUInt16(Data, boxOfs + size);
|
||||
return chk == actual;
|
||||
}
|
||||
|
@ -69,7 +69,7 @@ namespace PKHeX.Core
|
|||
{
|
||||
var boxOfs = GetBoxOffset(i) - ListHeaderSize;
|
||||
const int size = BoxSizeJ - 2;
|
||||
var chk = Checksums.CheckSum16(Data, boxOfs, size);
|
||||
var chk = Checksums.CheckSum16(new ReadOnlySpan<byte>(Data, boxOfs, size));
|
||||
BigEndian.GetBytes(chk).CopyTo(Data, boxOfs + size);
|
||||
}
|
||||
|
||||
|
|
|
@ -73,7 +73,7 @@ namespace PKHeX.Core
|
|||
{
|
||||
var boxOfs = GetBoxOffset(i) - ListHeaderSizeBox;
|
||||
var size = BoxSize - 2;
|
||||
var chk = Checksums.CheckSum16(Data, boxOfs, size);
|
||||
var chk = Checksums.CheckSum16(new ReadOnlySpan<byte>(Data, boxOfs, size));
|
||||
var actual = BigEndian.ToUInt16(Data, boxOfs + size);
|
||||
return chk == actual;
|
||||
}
|
||||
|
@ -109,7 +109,7 @@ namespace PKHeX.Core
|
|||
{
|
||||
var boxOfs = GetBoxOffset(i) - ListHeaderSizeBox;
|
||||
var size = BoxSize - 2;
|
||||
var chk = Checksums.CheckSum16(Data, boxOfs, size);
|
||||
var chk = Checksums.CheckSum16(new ReadOnlySpan<byte>(Data, boxOfs, size));
|
||||
BigEndian.GetBytes(chk).CopyTo(Data, boxOfs + size);
|
||||
}
|
||||
|
||||
|
|
|
@ -104,7 +104,7 @@ namespace PKHeX.Core
|
|||
|
||||
// Checksums
|
||||
protected abstract int FooterSize { get; }
|
||||
private ushort CalcBlockChecksum(byte[] data) => Checksums.CRC16_CCITT(data, 0, data.Length - FooterSize);
|
||||
private ushort CalcBlockChecksum(byte[] data) => Checksums.CRC16_CCITT(new ReadOnlySpan<byte>(data, 0, data.Length - FooterSize));
|
||||
private static ushort GetBlockChecksumSaved(byte[] data) => BitConverter.ToUInt16(data, data.Length - 2);
|
||||
private bool GetBlockChecksumValid(byte[] data) => CalcBlockChecksum(data) == GetBlockChecksumSaved(data);
|
||||
|
||||
|
|
|
@ -175,7 +175,7 @@ namespace PKHeX.Core
|
|||
chkbytes.CopyTo(Data, footer + 2); // checksum
|
||||
chkbytes.CopyTo(Data, footer + 0x100); // second checksum
|
||||
dlcfooter.CopyTo(Data, footer + 0x102);
|
||||
ushort skinchkval = Checksums.CRC16_CCITT(Data, footer + 0x100, 4);
|
||||
ushort skinchkval = Checksums.CRC16_CCITT(new ReadOnlySpan<byte>(Data, footer + 0x100, 4));
|
||||
BitConverter.GetBytes(skinchkval).CopyTo(Data, footer + 0x112);
|
||||
|
||||
// Indicate in the save file that data is present
|
||||
|
|
|
@ -12,7 +12,7 @@ namespace PKHeX.Core
|
|||
public override PersonalTable Personal => PersonalTable.RS;
|
||||
public override IReadOnlyList<ushort> HeldItems => Legal.HeldItems_RS;
|
||||
protected override SaveFile CloneInternal() => new Bank3((byte[])Data.Clone());
|
||||
public override string PlayTimeString => Checksums.CRC16(Data, 0, Data.Length).ToString("X4");
|
||||
public override string PlayTimeString => Checksums.CRC16Invert(Data).ToString("X4");
|
||||
protected internal override string ShortSummary => PlayTimeString;
|
||||
public override string Extension => ".gst";
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ namespace PKHeX.Core
|
|||
public override PersonalTable Personal => PersonalTable.HGSS;
|
||||
public override IReadOnlyList<ushort> HeldItems => Legal.HeldItems_HGSS;
|
||||
protected override SaveFile CloneInternal() => new Bank4((byte[])Data.Clone());
|
||||
public override string PlayTimeString => Checksums.CRC16(Data, 0, Data.Length).ToString("X4");
|
||||
public override string PlayTimeString => Checksums.CRC16Invert(Data).ToString("X4");
|
||||
protected internal override string ShortSummary => PlayTimeString;
|
||||
public override string Extension => ".stk";
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ namespace PKHeX.Core
|
|||
|
||||
protected readonly int SlotsPerBox;
|
||||
|
||||
protected internal override string ShortSummary => $"{Checksums.CRC16(Data, Box, Data.Length - Box):X4}";
|
||||
protected internal override string ShortSummary => $"{Checksums.CRC16Invert(new ReadOnlySpan<byte>(Data, Box, Data.Length - Box)):X4}";
|
||||
public override string Extension => ".bin";
|
||||
public sealed override bool ChecksumsValid => true;
|
||||
public sealed override string ChecksumInfo => "No Info.";
|
||||
|
|
|
@ -22,7 +22,7 @@ namespace PKHeX.Core
|
|||
public override PersonalTable Personal => PersonalTable.Pt;
|
||||
public override IReadOnlyList<ushort> HeldItems => Legal.HeldItems_Pt;
|
||||
protected override SaveFile CloneInternal() => new SAV4Ranch((byte[])Data.Clone());
|
||||
public override string PlayTimeString => Checksums.CRC16(Data, 0, Data.Length).ToString("X4");
|
||||
public override string PlayTimeString => Checksums.CRC16Invert(Data).ToString("X4");
|
||||
protected internal override string ShortSummary => $"{OT} {PlayTimeString}";
|
||||
public override string Extension => ".bin";
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ namespace PKHeX.Core
|
|||
Checksum = GetCalculatedChecksum(); // [app,chk)
|
||||
}
|
||||
|
||||
private ushort GetCalculatedChecksum() => Checksums.CRC16_CCITT(Data, Offset + 0x200, 0xC48 - 4 - 0x200); // [app,chk)
|
||||
private ushort GetCalculatedChecksum() => Checksums.CRC16_CCITT(new ReadOnlySpan<byte>(Data, Offset + 0x200, 0xC48 - 4 - 0x200)); // [app,chk)
|
||||
|
||||
private int GetChecksumOffset() => Offset + 0xC48 - 4;
|
||||
|
||||
|
|
|
@ -89,7 +89,10 @@ namespace PKHeX.Core
|
|||
|
||||
pk7.EncryptedPartyData.CopyTo(data, 0x30); // Copy in pokemon data
|
||||
GetRawQR(pk7.Species, pk7.Form, pk7.IsShiny, pk7.Gender).CopyTo(data, 0x140);
|
||||
BitConverter.GetBytes(Checksums.CRC16(data, 0, 0x1A0)).CopyTo(data, 0x1A0);
|
||||
|
||||
var span = new ReadOnlySpan<byte>(data, 0, 0x1A0);
|
||||
var chk = Checksums.CRC16Invert(span);
|
||||
BitConverter.GetBytes(chk).CopyTo(data, 0x1A0);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,17 +9,14 @@ namespace PKHeX.Core
|
|||
{
|
||||
/// <summary>Calculates the CRC16-CCITT checksum over an input byte array.</summary>
|
||||
/// <param name="data">Input byte array</param>
|
||||
/// <param name="start">Starting point for checksum</param>
|
||||
/// <param name="length"></param>
|
||||
/// <returns>Checksum</returns>
|
||||
public static ushort CRC16_CCITT(byte[] data, int start, int length)
|
||||
public static ushort CRC16_CCITT(ReadOnlySpan<byte> data)
|
||||
{
|
||||
byte top = 0xFF;
|
||||
byte bot = 0xFF;
|
||||
int end = start + length;
|
||||
for (int i = start; i < end; i++)
|
||||
foreach (var b in data)
|
||||
{
|
||||
var x = data[i] ^ top;
|
||||
var x = b ^ top;
|
||||
x ^= (x >> 4);
|
||||
top = (byte)(bot ^ (x >> 3) ^ (x << 4));
|
||||
bot = (byte)(x ^ (x << 5));
|
||||
|
@ -27,11 +24,6 @@ namespace PKHeX.Core
|
|||
return (ushort)(top << 8 | bot);
|
||||
}
|
||||
|
||||
/// <summary>Calculates the CRC16-CCITT checksum over an input byte array.</summary>
|
||||
/// <param name="data">Input byte array</param>
|
||||
/// <returns>Checksum</returns>
|
||||
public static ushort CRC16_CCITT(byte[] data) => CRC16_CCITT(data, 0, data.Length);
|
||||
|
||||
private static readonly ushort[] crc16 =
|
||||
{
|
||||
0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
|
||||
|
@ -70,31 +62,23 @@ namespace PKHeX.Core
|
|||
|
||||
/// <summary>Calculates the 16bit checksum over an input byte array.</summary>
|
||||
/// <param name="data">Input byte array</param>
|
||||
/// <param name="start">Offset to start checksum at</param>
|
||||
/// <param name="length">Length of array to checksum</param>
|
||||
/// <param name="initial">Initial value for checksum</param>
|
||||
/// <returns>Checksum</returns>
|
||||
public static ushort CRC16(byte[] data, int start, int length, ushort initial)
|
||||
private static ushort CRC16(ReadOnlySpan<byte> data, ushort initial)
|
||||
{
|
||||
ushort chk = initial;
|
||||
for (var i = start; i < start + length; i++)
|
||||
chk = (ushort)(crc16[(data[i] ^ chk) & 0xFF] ^ chk >> 8);
|
||||
foreach (var b in data)
|
||||
chk = (ushort)(crc16[(b ^ chk) & 0xFF] ^ chk >> 8);
|
||||
return chk;
|
||||
}
|
||||
|
||||
/// <summary>Calculates the 16bit checksum over an input byte array.</summary>
|
||||
/// <param name="data">Input byte array</param>
|
||||
/// <param name="start">Offset to start checksum at</param>
|
||||
/// <param name="length">Length of array to checksum</param>
|
||||
/// <returns>Checksum</returns>
|
||||
public static ushort CRC16(byte[] data, int start, int length) => (ushort)~CRC16(data, start, length, unchecked((ushort)~0));
|
||||
public static ushort CRC16Invert(ReadOnlySpan<byte> data) => (ushort)~CRC16(data, unchecked((ushort)~0));
|
||||
|
||||
/// <summary>Calculates the 16bit checksum over an input byte array.</summary>
|
||||
/// <param name="data">Input byte array</param>
|
||||
/// <param name="start">Offset to start checksum at</param>
|
||||
/// <param name="length">Length of array to checksum</param>
|
||||
/// <returns>Checksum</returns>
|
||||
public static ushort CRC16NoInvert(byte[] data, int start, int length) => CRC16(data, start, length, 0);
|
||||
public static ushort CRC16NoInvert(ReadOnlySpan<byte> data) => CRC16(data, 0);
|
||||
|
||||
/// <summary>Calculates the 32bit checksum over an input byte array. Used in GBA save files.</summary>
|
||||
/// <param name="data">Input byte array</param>
|
||||
|
@ -112,15 +96,13 @@ namespace PKHeX.Core
|
|||
|
||||
/// <summary>Calculates the 16bit checksum over an input byte array. Used in N64 Stadium save files.</summary>
|
||||
/// <param name="data">Input byte array</param>
|
||||
/// <param name="start">Offset to start checksum at</param>
|
||||
/// <param name="length">Length of array to checksum</param>
|
||||
/// <param name="initial">Initial value for checksum</param>
|
||||
/// <returns>Checksum</returns>
|
||||
public static ushort CheckSum16(byte[] data, int start, int length, ushort initial = 0)
|
||||
public static ushort CheckSum16(ReadOnlySpan<byte> data, ushort initial = 0)
|
||||
{
|
||||
ushort acc = initial;
|
||||
for (int i = 0; i < length; i++)
|
||||
acc += data[start + i];
|
||||
foreach (byte b in data)
|
||||
acc += b;
|
||||
return acc;
|
||||
}
|
||||
|
||||
|
@ -132,15 +114,16 @@ namespace PKHeX.Core
|
|||
|
||||
/// <summary>Calculates the 32bit checksum over an input byte array. Used in GC R/S BOX.</summary>
|
||||
/// <param name="data">Input byte array</param>
|
||||
/// <param name="start">Offset to start checksum at</param>
|
||||
/// <param name="end">Exclusive end offset to finish the checksum at</param>
|
||||
/// <returns>Checksum</returns>
|
||||
public static uint CheckSum16BigInvert(byte[] data, int start, int end)
|
||||
public static uint CheckSum16BigInvert(ReadOnlySpan<byte> data)
|
||||
{
|
||||
ushort chk = 0; // initial value
|
||||
for (int i = start; i < end; i += 2)
|
||||
chk += BigEndian.ToUInt16(data, i);
|
||||
return (uint)(chk << 16 | (ushort)(0xF004 - chk));
|
||||
while ((data.Length & ~1) != 0)
|
||||
{
|
||||
chk += BigEndian.ToUInt16(data);
|
||||
data = data.Slice(2);
|
||||
}
|
||||
return (uint)(chk << 16 | (ushort)(0xF004u - chk));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -318,7 +318,8 @@ namespace PKHeX.Core
|
|||
|
||||
// Verify first checksum
|
||||
const int offset = 0x2000;
|
||||
var chk = Checksums.CheckSum16BigInvert(data, offset + 4, offset + 0x1FFC);
|
||||
var span = new ReadOnlySpan<byte>(data, offset + 4, 0x1FF8);
|
||||
var chk = Checksums.CheckSum16BigInvert(span);
|
||||
var actual = BigEndian.ToUInt32(data, offset);
|
||||
return chk == actual ? RSBOX : Invalid;
|
||||
}
|
||||
|
@ -417,11 +418,11 @@ namespace PKHeX.Core
|
|||
|
||||
// check the checksum block validity; nobody would normally modify this region
|
||||
ushort chk1 = BitConverter.ToUInt16(data, SIZE_G5BW - 0x100 + 0x8C + 0xE);
|
||||
ushort actual1 = Checksums.CRC16_CCITT(data, SIZE_G5BW - 0x100, 0x8C);
|
||||
ushort actual1 = Checksums.CRC16_CCITT(new ReadOnlySpan<byte>(data, SIZE_G5BW - 0x100, 0x8C));
|
||||
if (chk1 == actual1)
|
||||
return BW;
|
||||
ushort chk2 = BitConverter.ToUInt16(data, SIZE_G5B2W2 - 0x100 + 0x94 + 0xE);
|
||||
ushort actual2 = Checksums.CRC16_CCITT(data, SIZE_G5B2W2 - 0x100, 0x94);
|
||||
ushort actual2 = Checksums.CRC16_CCITT(new ReadOnlySpan<byte>(data, SIZE_G5B2W2 - 0x100, 0x94));
|
||||
if (chk2 == actual2)
|
||||
return B2W2;
|
||||
return Invalid;
|
||||
|
|
|
@ -472,13 +472,13 @@ namespace PKHeX.WinForms
|
|||
bool f = false;
|
||||
for (int i = 0; i < 2; i++, ofsHallStat += 0x14)
|
||||
{
|
||||
var h = BitConverter.ToInt32(SAV.Data, ofsHallStat);
|
||||
var h = BitConverter.ToInt32(SAV.General, ofsHallStat);
|
||||
if (h == -1) continue;
|
||||
for (int j = 0; j < 0x20; j++)
|
||||
{
|
||||
for (int k = 0, a = j + 0x20 << 12; k < 2; k++, a += 0x40000)
|
||||
{
|
||||
if (h != BitConverter.ToInt32(SAV.Data, a) || BitConverter.ToInt16(SAV.Data, a + 0xBA8) != 0xBA0)
|
||||
if (h != BitConverter.ToInt32(SAV.General, a) || BitConverter.ToInt16(SAV.General, a + 0xBA8) != 0xBA0)
|
||||
continue;
|
||||
|
||||
f = true;
|
||||
|
@ -507,7 +507,11 @@ namespace PKHeX.WinForms
|
|||
|
||||
// Fill List
|
||||
CB_Species.InitializeBinding();
|
||||
CB_Species.DataSource = new BindingSource(GameInfo.SpeciesDataSource.Skip(1).Where(id => id.Value <= SAV.MaxSpeciesID).ToList(), null);
|
||||
|
||||
var speciesList = GameInfo.SpeciesDataSource.ToList();
|
||||
speciesList.RemoveAt(0);
|
||||
speciesList.RemoveAll(z => z.Value > SAV.MaxSpeciesID);
|
||||
CB_Species.DataSource = new BindingSource(speciesList, null);
|
||||
|
||||
editing = false;
|
||||
CB_Stats1.SelectedIndex = 0;
|
||||
|
@ -525,7 +529,7 @@ namespace PKHeX.WinForms
|
|||
}
|
||||
|
||||
if (HallStatUpdated)
|
||||
BitConverter.GetBytes(Checksums.CRC16_CCITT(SAV.Data, ofsHallStat, 0xBAE)).CopyTo(SAV.Data, ofsHallStat + 0xBAE);
|
||||
BitConverter.GetBytes(Checksums.CRC16_CCITT(new ReadOnlySpan<byte>(SAV.General, ofsHallStat, 0xBAE))).CopyTo(SAV.General, ofsHallStat + 0xBAE);
|
||||
}
|
||||
|
||||
private void SetPrints()
|
||||
|
@ -746,7 +750,7 @@ namespace PKHeX.WinForms
|
|||
|
||||
if (ofsHallStat > 0)
|
||||
{
|
||||
ushort v = BitConverter.ToUInt16(SAV.Data, ofsHallStat + 4 + (0x3DE * CB_Stats2.SelectedIndex) + (species << 1));
|
||||
ushort v = BitConverter.ToUInt16(SAV.General, ofsHallStat + 4 + (0x3DE * CB_Stats2.SelectedIndex) + (species << 1));
|
||||
NUD_HallStreaks.Value = v > 9999 ? 9999 : v;
|
||||
}
|
||||
}
|
||||
|
@ -774,7 +778,7 @@ namespace PKHeX.WinForms
|
|||
{
|
||||
if (editing || ofsHallStat < 0)
|
||||
return;
|
||||
BitConverter.GetBytes((ushort)NUD_HallStreaks.Value).CopyTo(SAV.Data, ofsHallStat + 4 + (0x3DE * CB_Stats2.SelectedIndex) + (species << 1));
|
||||
BitConverter.GetBytes((ushort)NUD_HallStreaks.Value).CopyTo(SAV.General, ofsHallStat + 4 + (0x3DE * CB_Stats2.SelectedIndex) + (species << 1));
|
||||
HallStatUpdated = true;
|
||||
}
|
||||
#endregion
|
||||
|
|
Loading…
Reference in a new issue