2022-03-03 02:05:13 +00:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
2020-01-24 07:16:09 +00:00
|
|
|
|
|
2022-03-03 02:05:13 +00:00
|
|
|
|
namespace PKHeX.Core;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// <see cref="SwishCrypto"/> block accessor, where blocks are ordered by ascending <see cref="SCBlock.Key"/>.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public abstract class SCBlockAccessor : ISaveBlockAccessor<SCBlock>
|
2020-01-24 07:16:09 +00:00
|
|
|
|
{
|
2022-03-03 02:05:13 +00:00
|
|
|
|
public abstract IReadOnlyList<SCBlock> BlockInfo { get; }
|
2020-01-24 07:16:09 +00:00
|
|
|
|
|
2022-03-03 02:05:13 +00:00
|
|
|
|
/// <summary> Checks if there is any <see cref="SCBlock"/> with the requested <see cref="key"/>. </summary>
|
|
|
|
|
public bool HasBlock(uint key) => FindIndex(BlockInfo, key) != -1;
|
2020-06-19 23:51:15 +00:00
|
|
|
|
|
2022-03-06 01:33:08 +00:00
|
|
|
|
#region Direct Block Accessing
|
2022-03-03 02:05:13 +00:00
|
|
|
|
/// <summary> Returns the <see cref="SCBlock"/> reference with the corresponding <see cref="key"/>. </summary>
|
|
|
|
|
public SCBlock GetBlock(uint key) => Find(BlockInfo, key);
|
2020-01-24 07:16:09 +00:00
|
|
|
|
|
2022-03-06 01:33:08 +00:00
|
|
|
|
/// <inheritdoc cref="SCBlock.GetValue"/>
|
|
|
|
|
public object GetBlockValue(uint key) => GetBlock(key).GetValue();
|
|
|
|
|
|
|
|
|
|
/// <inheritdoc cref="SCBlock.GetValue"/>
|
|
|
|
|
public T GetBlockValue<T>(uint key) where T : struct
|
|
|
|
|
{
|
|
|
|
|
var value = GetBlockValue(key);
|
|
|
|
|
if (value is T v)
|
|
|
|
|
return v;
|
|
|
|
|
throw new ArgumentException($"Incorrect type request! Expected {typeof(T).Name}, received {value.GetType().Name}", nameof(T));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <inheritdoc cref="SCBlock.SetValue"/>
|
|
|
|
|
public void SetBlockValue(uint key, object value) => GetBlock(key).SetValue(value);
|
|
|
|
|
#endregion
|
|
|
|
|
|
2022-03-03 02:05:13 +00:00
|
|
|
|
#region Ease of Use Overloads
|
|
|
|
|
/// <inheritdoc cref="GetBlock(string)"/>
|
|
|
|
|
/// <param name="name">Block name (un-hashed)</param>
|
|
|
|
|
public SCBlock GetBlock(string name) => GetBlock(Hash(name.AsSpan()));
|
2022-02-25 05:02:08 +00:00
|
|
|
|
|
2022-03-03 02:05:13 +00:00
|
|
|
|
/// <inheritdoc cref="GetBlock(string)"/>
|
|
|
|
|
public SCBlock GetBlock(ReadOnlySpan<char> name) => GetBlock(Hash(name));
|
|
|
|
|
private static uint Hash(ReadOnlySpan<char> name) => (uint)FnvHash.HashFnv1a_64(name);
|
|
|
|
|
|
|
|
|
|
/// <inheritdoc cref="GetBlock(string)"/>
|
|
|
|
|
public SCBlock GetBlock(ReadOnlySpan<byte> name) => GetBlock(Hash(name));
|
|
|
|
|
private static uint Hash(ReadOnlySpan<byte> name) => (uint)FnvHash.HashFnv1a_64(name);
|
|
|
|
|
#endregion
|
|
|
|
|
|
2022-03-06 01:33:08 +00:00
|
|
|
|
#region Safe Block Operations (no exceptions thrown)
|
2022-03-03 02:05:13 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Tries to grab the actual block, and returns a new dummy if the block does not exist.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="key">Block Key</param>
|
|
|
|
|
/// <returns>Block if exists, dummy if not. Dummy key will not match requested key.</returns>
|
|
|
|
|
public SCBlock GetBlockSafe(uint key) => FindOrDefault(BlockInfo, key);
|
2022-02-25 05:02:08 +00:00
|
|
|
|
|
2022-03-06 01:33:08 +00:00
|
|
|
|
/// <remarks> If the block does not exist, the method will return the default value. </remarks>
|
|
|
|
|
/// <inheritdoc cref="SCBlock.GetValue"/>
|
|
|
|
|
public T GetBlockValueSafe<T>(uint key) where T : struct
|
|
|
|
|
{
|
|
|
|
|
var index = FindIndex(BlockInfo, key);
|
|
|
|
|
if (index != -1)
|
|
|
|
|
BlockInfo[index].GetValue();
|
|
|
|
|
return default;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <remarks> If the block does not exist, the method will do nothing. </remarks>
|
|
|
|
|
/// <inheritdoc cref="SCBlock.SetValue"/>
|
|
|
|
|
public void SetBlockValueSafe(uint key, object value)
|
|
|
|
|
{
|
|
|
|
|
var index = FindIndex(BlockInfo, key);
|
|
|
|
|
if (index != -1)
|
|
|
|
|
BlockInfo[index].SetValue(value);
|
|
|
|
|
}
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region Block Fetching
|
2022-03-03 02:05:13 +00:00
|
|
|
|
private static SCBlock Find(IReadOnlyList<SCBlock> array, uint key)
|
|
|
|
|
{
|
|
|
|
|
var index = FindIndex(array, key);
|
|
|
|
|
if (index != -1)
|
|
|
|
|
return array[index];
|
|
|
|
|
throw new KeyNotFoundException(nameof(key));
|
|
|
|
|
}
|
2022-02-27 15:56:47 +00:00
|
|
|
|
|
2022-03-03 02:05:13 +00:00
|
|
|
|
private static SCBlock FindOrDefault(IReadOnlyList<SCBlock> array, uint key)
|
|
|
|
|
{
|
|
|
|
|
var index = FindIndex(array, key);
|
|
|
|
|
if (index != -1)
|
|
|
|
|
return array[index];
|
|
|
|
|
return new SCBlock(0, SCTypeCode.None);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Finds a specified <see cref="key"/> within the <see cref="array"/>.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <remarks>
|
|
|
|
|
/// Rather than storing a dictionary of keys, we can abuse the fact that the <see cref="BlockInfo"/> is stored in order of ascending block key.
|
|
|
|
|
/// <br></br>
|
|
|
|
|
/// Binary Search doesn't require extra memory like a Dictionary would; also, we usually only need to find a few blocks.
|
|
|
|
|
/// </remarks>
|
|
|
|
|
/// <param name="array">Index-able collection</param>
|
|
|
|
|
/// <param name="key"><see cref="SCBlock.Key"/> to find.</param>
|
|
|
|
|
/// <returns>Returns -1 if no match found.</returns>
|
|
|
|
|
private static int FindIndex(IReadOnlyList<SCBlock> array, uint key)
|
|
|
|
|
{
|
|
|
|
|
int min = 0;
|
|
|
|
|
int max = array.Count - 1;
|
|
|
|
|
do
|
2022-02-27 15:56:47 +00:00
|
|
|
|
{
|
2022-03-03 02:05:13 +00:00
|
|
|
|
int mid = min + ((max - min) >> 1);
|
|
|
|
|
var entry = array[mid];
|
|
|
|
|
var ek = entry.Key;
|
|
|
|
|
if (key == ek)
|
|
|
|
|
return mid;
|
2022-02-27 15:56:47 +00:00
|
|
|
|
|
2022-03-03 02:05:13 +00:00
|
|
|
|
if (key < ek)
|
|
|
|
|
max = mid - 1;
|
|
|
|
|
else
|
|
|
|
|
min = mid + 1;
|
|
|
|
|
} while (min <= max);
|
|
|
|
|
return -1;
|
2020-01-24 07:16:09 +00:00
|
|
|
|
}
|
2022-03-06 01:33:08 +00:00
|
|
|
|
#endregion
|
2021-01-07 07:30:30 +00:00
|
|
|
|
}
|