diff --git a/SabreTools.Metadata.DatFiles/ItemDatabase.cs b/SabreTools.Metadata.DatFiles/ItemDatabase.cs
index 9b950906..0ba6889a 100644
--- a/SabreTools.Metadata.DatFiles/ItemDatabase.cs
+++ b/SabreTools.Metadata.DatFiles/ItemDatabase.cs
@@ -44,6 +44,108 @@ namespace SabreTools.Metadata.DatFiles
{
#region Private Classes
+ ///
+ /// Represents a table with a string key and a list of indexes as a value
+ ///
+ private class GroupingTable
+ {
+ #region Private Fields
+
+#if NET40_OR_GREATER || NETCOREAPP || NETSTANDARD2_0_OR_GREATER
+ private readonly ConcurrentDictionary> _groupings = [];
+#else
+ private readonly Dictionary> _groupings = [];
+#endif
+
+ #endregion
+
+ #region Properties
+
+ ///
+ /// Indicates what the grouping keys represent
+ ///
+ public ItemKey GroupedBy { get; set; } = ItemKey.NULL;
+
+ ///
+ /// All grouping keys
+ ///
+ public string[] Keys => [.. _groupings.Keys];
+
+ #endregion
+
+ #region Accessors
+
+ ///
+ /// Add an index to a grouping
+ ///
+ public void Add(string key, long index)
+ {
+ lock (key)
+ {
+#if NET40_OR_GREATER || NETCOREAPP || NETSTANDARD2_0_OR_GREATER
+ // If the key is missing from the dictionary, add it
+ _groupings.GetOrAdd(key, []);
+ if (!_groupings.TryGetValue(key, out var grouping) || grouping is null)
+ return;
+
+ grouping.Add(index);
+#else
+ // If the key is missing from the dictionary, add it
+ if (!_groupings.ContainsKey(key))
+ _groupings[key] = [];
+
+ _groupings[key].Add(index);
+#endif
+ }
+ }
+
+ ///
+ /// Remove all groupings
+ ///
+ public void Clear() => _groupings.Clear();
+
+ ///
+ /// Try to set a grouping by key and list of values
+ ///
+ /// This overwrites anything existing
+ public bool TryAdd(string key, List value)
+ {
+#if NET40_OR_GREATER || NETCOREAPP || NETSTANDARD2_0_OR_GREATER
+ return _groupings.TryAdd(key, value);
+#else
+ _groupings[key] = value;
+ return true;
+#endif
+ }
+
+ ///
+ /// Try to get a value by key, returning success
+ ///
+ public bool TryGet(string key, out List? value)
+ => _groupings.TryGetValue(key, out value);
+
+ ///
+ /// Try to remove a value by key, returning success
+ ///
+ public bool TryRemove(string key, out List? value)
+ {
+#if NET40_OR_GREATER || NETCOREAPP || NETSTANDARD2_0_OR_GREATER
+ return _groupings.TryRemove(key, out value);
+#else
+ if (!_groupings.ContainsKey(key))
+ {
+ value = default;
+ return false;
+ }
+
+ value = _groupings[key];
+ return _groupings.Remove(key);
+#endif
+ }
+
+ #endregion
+ }
+
///
/// Represents a table with an incremental index as the key
///
@@ -156,7 +258,7 @@ namespace SabreTools.Metadata.DatFiles
#if NET40_OR_GREATER || NETCOREAPP || NETSTANDARD2_0_OR_GREATER
});
#else
- }
+ }
#endif
}
@@ -181,7 +283,7 @@ namespace SabreTools.Metadata.DatFiles
=> _table.TryGetValue(index, out value);
///
- /// Try to get a value by index, returning success
+ /// Try to remove a value by index, returning success
///
public bool TryRemove(long index, out T? value)
{
@@ -251,16 +353,7 @@ namespace SabreTools.Metadata.DatFiles
/// Internal dictionary representing the current buckets
///
[JsonIgnore, XmlIgnore]
-#if NET40_OR_GREATER || NETCOREAPP || NETSTANDARD2_0_OR_GREATER
- private readonly ConcurrentDictionary> _buckets = [];
-#else
- private readonly Dictionary> _buckets = [];
-#endif
-
- ///
- /// Current bucketed by value
- ///
- private ItemKey _bucketedBy = ItemKey.NULL;
+ private readonly GroupingTable _buckets = new();
///
/// Logging object
@@ -446,7 +539,7 @@ namespace SabreTools.Metadata.DatFiles
if (bucketName is null)
return [];
- if (!_buckets.TryGetValue(bucketName, out var itemIds))
+ if (!_buckets.TryGet(bucketName, out var itemIds) || itemIds is null)
return [];
var datItems = new Dictionary();
@@ -509,16 +602,7 @@ namespace SabreTools.Metadata.DatFiles
/// Key in the dictionary to remove
public bool RemoveBucket(string key)
{
-#if NET40_OR_GREATER || NETCOREAPP || NETSTANDARD2_0_OR_GREATER
bool removed = _buckets.TryRemove(key, out var list);
-#else
- if (!_buckets.ContainsKey(key))
- return false;
-
- bool removed = true;
- var list = _buckets[key];
- _buckets.Remove(key);
-#endif
if (list is null)
return removed;
@@ -585,7 +669,7 @@ namespace SabreTools.Metadata.DatFiles
DatStatistics.AddItemStatistics(item);
// Add the item to the default bucket
- PerformItemBucketing(new KeyValuePair(index, item), _bucketedBy, lower: true, norename: true);
+ PerformItemBucketing(new KeyValuePair(index, item), _buckets.GroupedBy, lower: true, norename: true);
// Return the used index
return index;
@@ -605,7 +689,7 @@ namespace SabreTools.Metadata.DatFiles
public void BucketBy(ItemKey bucketBy, bool lower = true, bool norename = true)
{
// If the sorted type isn't the same, we want to sort the dictionary accordingly
- if (_bucketedBy != bucketBy && bucketBy != ItemKey.NULL)
+ if (_buckets.GroupedBy != bucketBy && bucketBy != ItemKey.NULL)
{
_logger.User($"Organizing roms by {bucketBy}");
PerformBucketing(bucketBy, lower, norename);
@@ -715,7 +799,7 @@ namespace SabreTools.Metadata.DatFiles
_ = SortAndGetKey(datItem, sorted);
var machine = GetMachine(datItem.Value.MachineIndex);
var source = GetSource(datItem.Value.SourceIndex);
- string key = datItem.Value.GetKey(_bucketedBy, machine.Value, source.Value);
+ string key = datItem.Value.GetKey(_buckets.GroupedBy, machine.Value, source.Value);
// If the key doesn't exist, return the empty list
var items = GetItemsForBucket(key);
@@ -758,7 +842,7 @@ namespace SabreTools.Metadata.DatFiles
_ = SortAndGetKey(datItem, sorted);
var machine = GetMachine(datItem.Value.MachineIndex);
var source = GetSource(datItem.Value.SourceIndex);
- string key = datItem.Value.GetKey(_bucketedBy, machine.Value, source.Value);
+ string key = datItem.Value.GetKey(_buckets.GroupedBy, machine.Value, source.Value);
// If the key doesn't exist
var roms = GetItemsForBucket(key);
@@ -967,7 +1051,7 @@ namespace SabreTools.Metadata.DatFiles
private void PerformBucketing(ItemKey bucketBy, bool lower, bool norename)
{
// Reset the bucketing values
- _bucketedBy = bucketBy;
+ _buckets.GroupedBy = bucketBy;
_buckets.Clear();
// Get the current list of item indicies
@@ -1005,25 +1089,7 @@ namespace SabreTools.Metadata.DatFiles
private void PerformItemBucketing(KeyValuePair datItem, ItemKey bucketBy, bool lower, bool norename)
{
string? bucketKey = GetBucketKey(datItem, bucketBy, lower, norename);
- lock (bucketKey)
- {
- // If the key is missing from the dictionary, add it
-#if NET40_OR_GREATER || NETCOREAPP || NETSTANDARD2_0_OR_GREATER
- _buckets.GetOrAdd(bucketKey, []);
-#else
- if (!_buckets.ContainsKey(bucketKey))
- _buckets[bucketKey] = [];
-#endif
-
-#if NET40_OR_GREATER || NETCOREAPP || NETSTANDARD2_0_OR_GREATER
- if (!_buckets.TryGetValue(bucketKey, out var bucket) || bucket is null)
- return;
-
- bucket.Add(datItem.Key);
-#else
- _buckets[bucketKey].Add(datItem.Key);
-#endif
- }
+ _buckets.Add(bucketKey, datItem.Key);
}
///
@@ -1040,20 +1106,11 @@ namespace SabreTools.Metadata.DatFiles
for (int i = 0; i < bucketKeys.Length; i++)
#endif
{
-#if NET452_OR_GREATER || NETCOREAPP || NETSTANDARD2_0_OR_GREATER
- _buckets.TryGetValue(bucketKeys[i], out var itemIndices);
-#else
- var itemIndices = _buckets[bucketKeys[i]];
-#endif
+ _buckets.TryGet(bucketKeys[i], out var itemIndices);
if (itemIndices is null || itemIndices.Count == 0)
{
-#if NET40_OR_GREATER || NETCOREAPP || NETSTANDARD2_0_OR_GREATER
_buckets.TryRemove(bucketKeys[i], out _);
return;
-#else
- _buckets.Remove(bucketKeys[i]);
- continue;
-#endif
}
var datItems = itemIndices
@@ -1062,11 +1119,10 @@ namespace SabreTools.Metadata.DatFiles
Sort(ref datItems, norename);
-#if NET40_OR_GREATER || NETCOREAPP || NETSTANDARD2_0_OR_GREATER
_buckets.TryAdd(bucketKeys[i], datItems.ConvertAll(kvp => kvp.Key));
+#if NET40_OR_GREATER || NETCOREAPP || NETSTANDARD2_0_OR_GREATER
});
#else
- _buckets[bucketKeys[i]] = datItems.ConvertAll(kvp => kvp.Key);
}
#endif
}
@@ -1145,7 +1201,7 @@ namespace SabreTools.Metadata.DatFiles
BucketBy(GetBestAvailable());
// Now that we have the sorted type, we get the proper key
- return GetBucketKey(datItem, _bucketedBy, lower: true, norename: true);
+ return GetBucketKey(datItem, _buckets.GroupedBy, lower: true, norename: true);
}
#endregion