Separate scblock ctor's

No longer need to set byte[] twice
Add value-setter checks for bad mutations (size change, bool1<->bool2 only)
This commit is contained in:
Kurt 2021-06-03 12:35:39 -07:00
parent 8c970ec991
commit 066aedfc21
7 changed files with 76 additions and 40 deletions

View file

@ -25,7 +25,7 @@ namespace PKHeX.Core
{ {
try { return GetBlock(key); } try { return GetBlock(key); }
#pragma warning disable CA1031 // Do not catch general exception types #pragma warning disable CA1031 // Do not catch general exception types
catch (KeyNotFoundException) { return new SCBlock(0); } catch (KeyNotFoundException) { return new SCBlock(0, SCTypeCode.None); }
#pragma warning restore CA1031 // Do not catch general exception types #pragma warning restore CA1031 // Do not catch general exception types
} }

View file

@ -17,19 +17,60 @@ namespace PKHeX.Core
/// <summary> /// <summary>
/// What kind of block is it? /// What kind of block is it?
/// </summary> /// </summary>
public SCTypeCode Type { get; set; } public SCTypeCode Type { get; private set; }
/// <summary> /// <summary>
/// For <see cref="SCTypeCode.Array"/>: What kind of array is it? /// For <see cref="SCTypeCode.Array"/>: What kind of array is it?
/// </summary> /// </summary>
public SCTypeCode SubType { get; private set; } public readonly SCTypeCode SubType;
/// <summary> /// <summary>
/// Decrypted data for this block. /// Decrypted data for this block.
/// </summary> /// </summary>
public byte[] Data = Array.Empty<byte>(); public readonly byte[] Data;
internal SCBlock(uint key) => Key = key; /// <summary>
/// Changes the block's Boolean type. Will throw if the old / new <see cref="Type"/> is not boolean.
/// </summary>
/// <param name="value">New boolean type to set.</param>
/// <remarks>Will throw if the requested block state changes are incorrect.</remarks>
public void ChangeBooleanType(SCTypeCode value)
{
if (Type is not (SCTypeCode.Bool1 or SCTypeCode.Bool2) || value is not (SCTypeCode.Bool1 or SCTypeCode.Bool2))
throw new InvalidOperationException($"Cannot change {Type} to {value}.");
Type = value;
}
/// <summary>
/// Replaces the current <see cref="Data"/> with a same-sized array <see cref="value"/>.
/// </summary>
/// <param name="value">New array to load (copy from).</param>
/// <remarks>Will throw if the requested block state changes are incorrect.</remarks>
public void ChangeData(ReadOnlySpan<byte> value)
{
if (value.Length != Data.Length)
throw new InvalidOperationException($"Cannot change size of {Type} block from {Data.Length} to {value.Length}.");
value.CopyTo(Data);
}
internal SCBlock(uint key, SCTypeCode type) : this(key, type, Array.Empty<byte>())
{
}
internal SCBlock(uint key, SCTypeCode type, byte[] arr)
{
Key = key;
Type = type;
Data = arr;
}
internal SCBlock(uint key, byte[] arr, SCTypeCode subType)
{
Key = key;
Type = SCTypeCode.Array;
Data = arr;
SubType = subType;
}
public bool HasValue() => Type > SCTypeCode.Array; public bool HasValue() => Type > SCTypeCode.Array;
public object GetValue() => Type.GetValue(Data); public object GetValue() => Type.GetValue(Data);
@ -37,9 +78,11 @@ namespace PKHeX.Core
public SCBlock Clone() public SCBlock Clone()
{ {
var block = (SCBlock)MemberwiseClone(); if (Data.Length == 0)
block.Data = (byte[])Data.Clone(); return new SCBlock(Key, Type);
return block; if (SubType == 0)
return new SCBlock(Key, Type, (byte[]) Data.Clone());
return new SCBlock(Key, (byte[])Data.Clone(), SubType);
} }
/// <summary> /// <summary>
@ -77,20 +120,20 @@ namespace PKHeX.Core
// Create block, parse its key. // Create block, parse its key.
var key = BitConverter.ToUInt32(data, offset); var key = BitConverter.ToUInt32(data, offset);
offset += 4; offset += 4;
var block = new SCBlock(key);
var xk = new SCXorShift32(key); var xk = new SCXorShift32(key);
// Parse the block's type // Parse the block's type
block.Type = (SCTypeCode)(data[offset++] ^ xk.Next()); //var block =
var type = (SCTypeCode)(data[offset++] ^ xk.Next());
switch (block.Type) switch (type)
{ {
case SCTypeCode.Bool1: case SCTypeCode.Bool1:
case SCTypeCode.Bool2: case SCTypeCode.Bool2:
case SCTypeCode.Bool3: case SCTypeCode.Bool3:
// Block types are empty, and have no extra data. // Block types are empty, and have no extra data.
Debug.Assert(block.Type != SCTypeCode.Bool3); // invalid type, haven't seen it used yet Debug.Assert(type != SCTypeCode.Bool3); // invalid type, haven't seen it used yet
break; return new SCBlock(key, type);
case SCTypeCode.Object: // Cast raw bytes to Object case SCTypeCode.Object: // Cast raw bytes to Object
{ {
@ -99,39 +142,35 @@ namespace PKHeX.Core
var arr = new byte[num_bytes]; var arr = new byte[num_bytes];
for (int i = 0; i < arr.Length; i++) for (int i = 0; i < arr.Length; i++)
arr[i] = (byte)(data[offset++] ^ xk.Next()); arr[i] = (byte)(data[offset++] ^ xk.Next());
block.Data = arr;
break; return new SCBlock(key, type, arr);
} }
case SCTypeCode.Array: // Cast raw bytes to SubType[] case SCTypeCode.Array: // Cast raw bytes to SubType[]
{ {
var num_entries = BitConverter.ToUInt32(data, offset) ^ xk.Next32(); var num_entries = BitConverter.ToUInt32(data, offset) ^ xk.Next32();
offset += 4; offset += 4;
block.SubType = (SCTypeCode)(data[offset++] ^ xk.Next()); var sub = (SCTypeCode)(data[offset++] ^ xk.Next());
var num_bytes = num_entries * block.SubType.GetTypeSize(); var num_bytes = num_entries * sub.GetTypeSize();
var arr = new byte[num_bytes]; var arr = new byte[num_bytes];
for (int i = 0; i < arr.Length; i++) for (int i = 0; i < arr.Length; i++)
arr[i] = (byte)(data[offset++] ^ xk.Next()); arr[i] = (byte)(data[offset++] ^ xk.Next());
block.Data = arr;
#if DEBUG #if DEBUG
Debug.Assert(block.SubType > SCTypeCode.Array || Array.TrueForAll(block.Data, z => z <= 1)); Debug.Assert(sub > SCTypeCode.Array || Array.TrueForAll(arr, z => z <= 1));
#endif #endif
break; return new SCBlock(key, arr, sub);
} }
default: // Single Value Storage default: // Single Value Storage
{ {
var num_bytes = block.Type.GetTypeSize(); var num_bytes = type.GetTypeSize();
var arr = new byte[num_bytes]; var arr = new byte[num_bytes];
for (int i = 0; i < arr.Length; i++) for (int i = 0; i < arr.Length; i++)
arr[i] = (byte)(data[offset++] ^ xk.Next()); arr[i] = (byte)(data[offset++] ^ xk.Next());
block.Data = arr; return new SCBlock(key, type, arr);
break;
} }
} }
return block;
} }
} }
} }

View file

@ -99,7 +99,7 @@ namespace PKHeX.Core
} }
var data = File.ReadAllBytes(f); var data = File.ReadAllBytes(f);
data.CopyTo(block.Data, 0); block.ChangeData(data);
} }
#pragma warning disable CA1031 // Do not catch general exception types #pragma warning disable CA1031 // Do not catch general exception types
catch catch

View file

@ -60,7 +60,6 @@
key ^= key << 13; key ^= key << 13;
} }
#if !NET5 #if !NET5
/// <summary> /// <summary>
/// Count of bits set in value /// Count of bits set in value

View file

@ -169,7 +169,8 @@ namespace PKHeX.Core
{ {
if (value.Length != 1) if (value.Length != 1)
return; return;
Blocks.GetBlock(SaveBlockAccessor8SWSH.KSecretBoxUnlocked).Type = (SCTypeCode)(value[0] & 1) + 1; var block = Blocks.GetBlock(SaveBlockAccessor8SWSH.KSecretBoxUnlocked);
block.ChangeBooleanType((SCTypeCode)(value[0] & 1) + 1);
} }
} }

View file

@ -12,7 +12,7 @@ namespace PKHeX.Core
for (int i = 0; i < blocks.Length; i++) for (int i = 0; i < blocks.Length; i++)
{ {
int index = i * 2; int index = i * 2;
blocks[i] = new SCBlock(arr[index]) { Data = new byte[(int)arr[index + 1]] }; blocks[i] = new SCBlock(arr[index], SCTypeCode.None, new byte[(int)arr[index + 1]]);
} }
return blocks; return blocks;

View file

@ -31,16 +31,13 @@ namespace PKHeX.WinForms
CB_Key.InitializeBinding(); CB_Key.InitializeBinding();
CB_Key.DataSource = Metadata.GetSortedBlockKeyList().ToArray(); CB_Key.DataSource = Metadata.GetSortedBlockKeyList().ToArray();
ComboItem[] boolToggle =
{
new(nameof(SCTypeCode.Bool1), (int)SCTypeCode.Bool1),
new(nameof(SCTypeCode.Bool2), (int)SCTypeCode.Bool2),
new(nameof(SCTypeCode.Bool3), (int)SCTypeCode.Bool3),
};
CB_TypeToggle.InitializeBinding(); CB_TypeToggle.InitializeBinding();
CB_TypeToggle.DataSource = boolToggle; CB_TypeToggle.DataSource = new[]
{
CB_TypeToggle.SelectedIndexChanged += (o, args) => CB_TypeToggle_SelectedIndexChanged(CB_TypeToggle, args); new ComboItem(nameof(SCTypeCode.Bool1), (int)SCTypeCode.Bool1),
new ComboItem(nameof(SCTypeCode.Bool2), (int)SCTypeCode.Bool2),
};
CB_TypeToggle.SelectedIndexChanged += CB_TypeToggle_SelectedIndexChanged;
CB_Key.SelectedIndex = 0; CB_Key.SelectedIndex = 0;
} }
@ -109,14 +106,14 @@ namespace PKHeX.WinForms
PG_BlockView.Visible = false; PG_BlockView.Visible = false;
} }
private void CB_TypeToggle_SelectedIndexChanged(object sender, EventArgs e) private void CB_TypeToggle_SelectedIndexChanged(object? sender, EventArgs e)
{ {
var block = CurrentBlock; var block = CurrentBlock;
var cType = block.Type; var cType = block.Type;
var cValue = (SCTypeCode)WinFormsUtil.GetIndex(CB_TypeToggle); var cValue = (SCTypeCode)WinFormsUtil.GetIndex(CB_TypeToggle);
if (cType == cValue) if (cType == cValue)
return; return;
block.Type = cValue; block.ChangeBooleanType(cValue);
UpdateBlockSummaryControls(); UpdateBlockSummaryControls();
} }