mirror of
https://github.com/kwsch/PKHeX
synced 2024-11-23 12:33:06 +00:00
Move BinaryCodedDecimal conversions to separate class
Add unit tests, clarify names
This commit is contained in:
parent
50b15cd740
commit
3ac1882151
4 changed files with 127 additions and 80 deletions
|
@ -267,8 +267,8 @@ namespace PKHeX.Core
|
|||
|
||||
public int PikaBeachScore
|
||||
{
|
||||
get => BigEndian.BCDToInt32_LE(Data, Offsets.PikaBeachScore, 2);
|
||||
set => SetData(BigEndian.Int32ToBCD_LE(Math.Min(9999, value), 2), Offsets.PikaBeachScore);
|
||||
get => BinaryCodedDecimal.ToInt32LE(Data, Offsets.PikaBeachScore, 2);
|
||||
set => BinaryCodedDecimal.WriteBytesLE(Data.AsSpan(Offsets.PikaBeachScore, 2), Math.Min(9999, value));
|
||||
}
|
||||
|
||||
public override string PlayTimeString => !PlayedMaximum ? base.PlayTimeString : $"{base.PlayTimeString} {Checksums.CRC16_CCITT(Data):X4}";
|
||||
|
@ -353,21 +353,21 @@ namespace PKHeX.Core
|
|||
|
||||
public override uint Money
|
||||
{
|
||||
get => (uint)BigEndian.BCDToInt32(Data, Offsets.Money, 3);
|
||||
get => (uint)BinaryCodedDecimal.ToInt32BE(Data, Offsets.Money, 3);
|
||||
set
|
||||
{
|
||||
value = (uint)Math.Min(value, MaxMoney);
|
||||
BigEndian.Int32ToBCD((int)value, 3).CopyTo(Data, Offsets.Money);
|
||||
BinaryCodedDecimal.WriteBytesBE(Data.AsSpan(Offsets.Money, 3), (int)value);
|
||||
}
|
||||
}
|
||||
|
||||
public uint Coin
|
||||
{
|
||||
get => (uint)BigEndian.BCDToInt32(Data, Offsets.Coin, 2);
|
||||
get => (uint)BinaryCodedDecimal.ToInt32BE(Data, Offsets.Coin, 2);
|
||||
set
|
||||
{
|
||||
value = (ushort)Math.Min(value, MaxCoins);
|
||||
BigEndian.Int32ToBCD((int)value, 2).CopyTo(Data, Offsets.Coin);
|
||||
BinaryCodedDecimal.WriteBytesBE(Data.AsSpan(Offsets.Coin, 2), (int)value);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -128,79 +128,5 @@ namespace PKHeX.Core
|
|||
data[2 + i] = tmp1;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a 32-bit signed integer converted from bytes in a Binary Coded Decimal format byte array.
|
||||
/// </summary>
|
||||
/// <param name="input">Input byte array to read from.</param>
|
||||
/// <param name="offset">Offset to start reading at.</param>
|
||||
/// <param name="length">Length of array to read.</param>
|
||||
public static int BCDToInt32(byte[] input, int offset, int length)
|
||||
{
|
||||
int result = 0;
|
||||
for (int i = offset; i < offset + length; i++)
|
||||
{
|
||||
byte p = input[i];
|
||||
result *= 100;
|
||||
result += 10 * (p >> 4);
|
||||
result += p & 0xf;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the specified 32-bit signed integer value as an array of Binary Coded Decimal format bytes.
|
||||
/// </summary>
|
||||
/// <param name="input">32-bit signed integer to convert.</param>
|
||||
/// <param name="size">Desired size of returned array.</param>
|
||||
public static byte[] Int32ToBCD(int input, int size)
|
||||
{
|
||||
byte[] result = new byte[size];
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
int p = input%100;
|
||||
input /= 100;
|
||||
result[size - i - 1] = (byte)(p/10 << 4 | p%10);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a 16-bit signed integer converted from bytes in a Binary Coded Decimal format byte array.
|
||||
/// </summary>
|
||||
/// <remarks>Little Endian instead of Big Endian</remarks>
|
||||
/// <param name="input">Input byte array to read from.</param>
|
||||
/// <param name="offset">Offset to start reading at.</param>
|
||||
/// <param name="length">Length of array to read.</param>
|
||||
public static int BCDToInt32_LE(byte[] input, int offset, int length)
|
||||
{
|
||||
int result = 0;
|
||||
for (int i = offset + length - 1; i >= offset; i--)
|
||||
{
|
||||
byte p = input[i];
|
||||
result *= 100;
|
||||
result += 10 * (p >> 4);
|
||||
result += p & 0xf;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the specified 32-bit signed integer value as an array of Binary Coded Decimal format bytes.
|
||||
/// </summary>
|
||||
/// <remarks>Little Endian instead of Big Endian</remarks>
|
||||
/// <param name="input">32-bit signed integer to convert.</param>
|
||||
/// <param name="size">Desired size of returned array.</param>
|
||||
public static byte[] Int32ToBCD_LE(int input, int size)
|
||||
{
|
||||
byte[] result = new byte[size];
|
||||
for (int i = size - 1; i >= 0; i--)
|
||||
{
|
||||
int p = input % 100;
|
||||
input /= 100;
|
||||
result[size - i - 1] = (byte)(p / 10 << 4 | p % 10);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
96
PKHeX.Core/Util/BinaryCodedDecimal.cs
Normal file
96
PKHeX.Core/Util/BinaryCodedDecimal.cs
Normal file
|
@ -0,0 +1,96 @@
|
|||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace PKHeX.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// 4-bit decimal encoding used by some Generation 1 save file values.
|
||||
/// </summary>
|
||||
public static class BinaryCodedDecimal
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns a 32-bit signed integer converted from bytes in a Binary Coded Decimal format byte array.
|
||||
/// </summary>
|
||||
/// <param name="input">Input byte array to read from.</param>
|
||||
/// <param name="offset">Offset to start reading at.</param>
|
||||
/// <param name="length">Length of array to read.</param>
|
||||
public static int ToInt32BE(byte[] input, int offset, int length)
|
||||
{
|
||||
var span = new ReadOnlySpan<byte>(input, offset, length);
|
||||
return ToInt32BE(span);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="ToInt32BE(byte[],int,int)"/>
|
||||
public static int ToInt32BE(ReadOnlySpan<byte> input)
|
||||
{
|
||||
int result = 0;
|
||||
foreach (var b in input)
|
||||
PushDigits(ref result, b);
|
||||
return result;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void PushDigits(ref int result, byte b) => result = (result * 100) + (10 * (b >> 4)) + (b & 0xf);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the specified 32-bit signed integer value as an array of Binary Coded Decimal format bytes.
|
||||
/// </summary>
|
||||
/// <param name="value">32-bit signed integer to convert.</param>
|
||||
/// <param name="size">Desired size of returned array.</param>
|
||||
public static byte[] GetBytesBE(int value, int size)
|
||||
{
|
||||
byte[] data = new byte[size];
|
||||
WriteBytesBE(data, value);
|
||||
return data;
|
||||
}
|
||||
|
||||
public static void WriteBytesBE(Span<byte> data, int value)
|
||||
{
|
||||
for (int i = 0; i < data.Length; i++)
|
||||
{
|
||||
int p = value % 100;
|
||||
value /= 100;
|
||||
data[^(1+i)] = (byte) (p / 10 << 4 | p % 10);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="ToInt32BE(byte[],int,int)"/>
|
||||
/// <remarks>Big Endian instead of Little Endian</remarks>
|
||||
public static int ToInt32LE(byte[] data, int offset, int length)
|
||||
{
|
||||
var span = new ReadOnlySpan<byte>(data, offset, length);
|
||||
return ToInt32LE(span);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="ToInt32BE(byte[],int,int)"/>
|
||||
public static int ToInt32LE(ReadOnlySpan<byte> input)
|
||||
{
|
||||
int result = 0;
|
||||
for (int i = input.Length - 1; i >= 0; i--)
|
||||
PushDigits(ref result, input[i]);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="GetBytesBE"/>
|
||||
/// <remarks>Little Endian instead of Big Endian</remarks>
|
||||
public static byte[] GetBytesLE(int value, int size)
|
||||
{
|
||||
byte[] data = new byte[size];
|
||||
WriteBytesLE(data, value);
|
||||
return data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the <see cref="value"/> to the <see cref="data"/> buffer.
|
||||
/// </summary>
|
||||
public static void WriteBytesLE(Span<byte> data, int value)
|
||||
{
|
||||
for (int i = 0; i < data.Length; i++)
|
||||
{
|
||||
int p = value % 100;
|
||||
value /= 100;
|
||||
data[i] = (byte) (p / 10 << 4 | p % 10);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using FluentAssertions;
|
||||
using Xunit;
|
||||
|
||||
|
@ -47,5 +48,29 @@ namespace PKHeX.Tests.Util
|
|||
var remake = Core.Util.GetHexStringFromBytes(convert, 0, convert.Length);
|
||||
remake.Should().Be(v);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0x12345678, 12345678)]
|
||||
public void CheckConvertBCD_Little(uint raw, int expect)
|
||||
{
|
||||
var data = BitConverter.GetBytes(raw);
|
||||
var result = Core.BinaryCodedDecimal.ToInt32LE(data);
|
||||
result.Should().Be(expect);
|
||||
|
||||
var newData = Core.BinaryCodedDecimal.GetBytesLE(result, 4);
|
||||
data.SequenceEqual(newData).Should().BeTrue();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0x78563412, 12345678)]
|
||||
public void CheckConvertBCD_Big(uint raw, int expect)
|
||||
{
|
||||
var data = BitConverter.GetBytes(raw);
|
||||
var result = Core.BinaryCodedDecimal.ToInt32BE(data);
|
||||
result.Should().Be(expect);
|
||||
|
||||
var newData = Core.BinaryCodedDecimal.GetBytesBE(result, 4);
|
||||
data.SequenceEqual(newData).Should().BeTrue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue