Make item dictionary implementations consistent

This commit is contained in:
Matt Nadareski
2024-10-19 22:39:23 -04:00
parent 97432a446a
commit 6a26a0d2fa
3 changed files with 182 additions and 221 deletions

View File

@@ -117,26 +117,26 @@ namespace SabreTools.Core.Tools
/// <summary> /// <summary>
/// Remove all chars that are considered path unsafe /// Remove all chars that are considered path unsafe
/// </summary> /// </summary>
public static string? RemovePathUnsafeCharacters(string? input) public static string RemovePathUnsafeCharacters(string? input)
{ {
if (string.IsNullOrEmpty(input)) if (string.IsNullOrEmpty(input))
return input; return string.Empty;
foreach (char invalid in Path.GetInvalidPathChars()) foreach (char invalid in Path.GetInvalidPathChars())
{ {
input = input!.Replace(invalid.ToString(), string.Empty); input = input!.Replace(invalid.ToString(), string.Empty);
} }
return input; return input!;
} }
/// <summary> /// <summary>
/// Remove all unicode-specific chars from a string /// Remove all unicode-specific chars from a string
/// </summary> /// </summary>
public static string? RemoveUnicodeCharacters(string? input) public static string RemoveUnicodeCharacters(string? input)
{ {
if (string.IsNullOrEmpty(input)) if (string.IsNullOrEmpty(input))
return input; return string.Empty;
return new string(input.Where(c => c <= 255).ToArray()); return new string(input.Where(c => c <= 255).ToArray());
} }

View File

@@ -45,9 +45,9 @@ namespace SabreTools.DatFiles
/// Internal dictionary for the class /// Internal dictionary for the class
/// </summary> /// </summary>
#if NET40_OR_GREATER || NETCOREAPP #if NET40_OR_GREATER || NETCOREAPP
private readonly ConcurrentDictionary<string, ConcurrentList<DatItem>?> items; private readonly ConcurrentDictionary<string, ConcurrentList<DatItem>?> items = [];
#else #else
private readonly Dictionary<string, ConcurrentList<DatItem>?> items; private readonly Dictionary<string, ConcurrentList<DatItem>?> items = [];
#endif #endif
/// <summary> /// <summary>
@@ -109,11 +109,6 @@ namespace SabreTools.DatFiles
{ {
bucketedBy = ItemKey.NULL; bucketedBy = ItemKey.NULL;
mergedBy = DedupeType.None; mergedBy = DedupeType.None;
#if NET40_OR_GREATER || NETCOREAPP
items = new ConcurrentDictionary<string, ConcurrentList<DatItem>?>();
#else
items = new Dictionary<string, ConcurrentList<DatItem>?>();
#endif
logger = new Logger(this); logger = new Logger(this);
} }
@@ -318,23 +313,29 @@ namespace SabreTools.DatFiles
List<string> keys = [.. items.Keys]; List<string> keys = [.. items.Keys];
foreach (string key in keys) foreach (string key in keys)
{ {
#if NET40_OR_GREATER || NETCOREAPP
// If the key doesn't exist, skip
if (!items.TryGetValue(key, out var value))
continue;
// If the value is null, remove
else if (value == null)
items.TryRemove(key, out _);
// If there are no non-blank items, remove
else if (!value!.Any(i => i != null && i is not Blank))
items.TryRemove(key, out _);
#else
// If the key doesn't exist, skip // If the key doesn't exist, skip
if (!items.ContainsKey(key)) if (!items.ContainsKey(key))
continue; continue;
// If the value is null, remove // If the value is null, remove
else if (items[key] == null) else if (items[key] == null)
#if NET40_OR_GREATER || NETCOREAPP
items.TryRemove(key, out _);
#else
items.Remove(key); items.Remove(key);
#endif
// If there are no non-blank items, remove // If there are no non-blank items, remove
else if (!items[key]!.Any(i => i != null && i is not Blank)) else if (!items[key]!.Any(i => i != null && i is not Blank))
#if NET40_OR_GREATER || NETCOREAPP
items.TryRemove(key, out _);
#else
items.Remove(key); items.Remove(key);
#endif #endif
} }
@@ -389,8 +390,13 @@ namespace SabreTools.DatFiles
// Explicit lock for some weird corner cases // Explicit lock for some weird corner cases
lock (key) lock (key)
{ {
#if NET40_OR_GREATER || NETCOREAPP
if (items.TryGetValue(key, out var list) && list != null)
return list.Contains(value);
#else
if (items.ContainsKey(key) && items[key] != null) if (items.ContainsKey(key) && items[key] != null)
return items[key]!.Contains(value); return items[key]!.Contains(value);
#endif
} }
return false; return false;
@@ -864,11 +870,7 @@ namespace SabreTools.DatFiles
try try
{ {
// First we want to get a mapping for all games to description // First we want to get a mapping for all games to description
#if NET40_OR_GREATER || NETCOREAPP var mapping = CreateMachineToDescriptionMapping();
ConcurrentDictionary<string, string> mapping = CreateMachineToDescriptionMapping();
#else
Dictionary<string, string> mapping = CreateMachineToDescriptionMapping();
#endif
// Now we loop through every item and update accordingly // Now we loop through every item and update accordingly
UpdateMachineNamesFromDescriptions(mapping); UpdateMachineNamesFromDescriptions(mapping);
@@ -1054,11 +1056,7 @@ namespace SabreTools.DatFiles
/// <summary> /// <summary>
/// Create machine to description mapping dictionary /// Create machine to description mapping dictionary
/// </summary> /// </summary>
#if NET40_OR_GREATER || NETCOREAPP private IDictionary<string, string> CreateMachineToDescriptionMapping()
private ConcurrentDictionary<string, string> CreateMachineToDescriptionMapping()
#else
private Dictionary<string, string> CreateMachineToDescriptionMapping()
#endif
{ {
#if NET40_OR_GREATER || NETCOREAPP #if NET40_OR_GREATER || NETCOREAPP
ConcurrentDictionary<string, string> mapping = new(); ConcurrentDictionary<string, string> mapping = new();
@@ -1105,7 +1103,7 @@ namespace SabreTools.DatFiles
/// Set internal names to match One Rom Per Game (ORPG) logic /// Set internal names to match One Rom Per Game (ORPG) logic
/// </summary> /// </summary>
/// <param name="datItem">DatItem to run logic on</param> /// <param name="datItem">DatItem to run logic on</param>
private void SetOneRomPerGame(DatItem datItem) private static void SetOneRomPerGame(DatItem datItem)
{ {
if (datItem.GetName() == null) if (datItem.GetName() == null)
return; return;
@@ -1133,11 +1131,7 @@ namespace SabreTools.DatFiles
/// <summary> /// <summary>
/// Update machine names from descriptions according to mappings /// Update machine names from descriptions according to mappings
/// </summary> /// </summary>
#if NET40_OR_GREATER || NETCOREAPP private void UpdateMachineNamesFromDescriptions(IDictionary<string, string> mapping)
private void UpdateMachineNamesFromDescriptions(ConcurrentDictionary<string, string> mapping)
#else
private void UpdateMachineNamesFromDescriptions(Dictionary<string, string> mapping)
#endif
{ {
#if NET452_OR_GREATER || NETCOREAPP #if NET452_OR_GREATER || NETCOREAPP
Parallel.ForEach(Keys, Globals.ParallelOptions, key => Parallel.ForEach(Keys, Globals.ParallelOptions, key =>
@@ -1200,33 +1194,28 @@ namespace SabreTools.DatFiles
List<string> games = [.. Keys.OrderBy(g => g)]; List<string> games = [.. Keys.OrderBy(g => g)];
foreach (string game in games) foreach (string game in games)
{ {
var items = this[game];
if (items == null)
continue;
// If the game has no items in it, we want to continue // If the game has no items in it, we want to continue
if (items.Count == 0) var items = this[game];
if (items == null || items.Count == 0)
continue; continue;
// Determine if the game has a parent or not // Get the machine
string? parent = null; var machine = items[0].GetFieldValue<Machine>(DatItem.MachineKey);
if (!string.IsNullOrEmpty(items[0].GetFieldValue<Machine>(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.RomOfKey))) if (machine == null)
parent = items[0].GetFieldValue<Machine>(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.RomOfKey); continue;
// If the parent doesnt exist, we want to continue // Get the bios parent
if (string.IsNullOrEmpty(parent)) string? romOf = machine.GetStringFieldValue(Models.Metadata.Machine.RomOfKey);
if (string.IsNullOrEmpty(romOf))
continue; continue;
// If the parent doesn't have any items, we want to continue // If the parent doesn't have any items, we want to continue
if (this[parent!]!.Count == 0) var parentItems = this[romOf];
if (parentItems == null || parentItems.Count == 0)
continue; continue;
// If the parent exists and has items, we copy the items from the parent to the current game // If the parent exists and has items, we copy the items from the parent to the current game
DatItem copyFrom = items[0]; DatItem copyFrom = items[0];
var parentItems = this[parent!];
if (parentItems == null)
continue;
foreach (DatItem item in parentItems) foreach (DatItem item in parentItems)
{ {
DatItem datItem = (DatItem)item.Clone(); DatItem datItem = (DatItem)item.Clone();
@@ -1397,27 +1386,28 @@ namespace SabreTools.DatFiles
if (items == null || items.Count == 0) if (items == null || items.Count == 0)
continue; continue;
// Determine if the game has a parent or not // Get the machine
string? parent = null; var machine = items[0].GetFieldValue<Machine>(DatItem.MachineKey);
if (!string.IsNullOrEmpty(items[0].GetFieldValue<Machine>(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.CloneOfKey))) if (machine == null)
parent = items[0].GetFieldValue<Machine>(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.CloneOfKey); continue;
// If the parent doesnt exist, we want to continue // Get the clone parent
if (string.IsNullOrEmpty(parent)) string? cloneOf = machine.GetStringFieldValue(Models.Metadata.Machine.CloneOfKey);
if (string.IsNullOrEmpty(cloneOf))
continue; continue;
// If the parent doesn't have any items, we want to continue // If the parent doesn't have any items, we want to continue
if (this[parent!]!.Count == 0) var parentItems = this[cloneOf];
if (parentItems == null || parentItems.Count == 0)
continue; continue;
// If the parent exists and has items, we copy the items from the parent to the current game // If the parent exists and has items, we copy the items from the parent to the current game
DatItem copyFrom = items[0]; DatItem copyFrom = items[0];
var parentItems = this[parent!]; foreach (DatItem item in parentItems)
foreach (DatItem item in parentItems!)
{ {
DatItem datItem = (DatItem)item.Clone(); DatItem datItem = (DatItem)item.Clone();
datItem.CopyMachineInformation(copyFrom); datItem.CopyMachineInformation(copyFrom);
if (!items.Any(i => i.GetName()?.ToLowerInvariant() == datItem.GetName()?.ToLowerInvariant()) if (!items.Any(i => string.Equals(i.GetName(), datItem.GetName(), StringComparison.OrdinalIgnoreCase))
&& !items.Contains(datItem)) && !items.Contains(datItem))
{ {
Add(game, datItem); Add(game, datItem);
@@ -1426,7 +1416,7 @@ namespace SabreTools.DatFiles
// Now we want to get the parent romof tag and put it in each of the items // Now we want to get the parent romof tag and put it in each of the items
items = this[game]; items = this[game];
string? romof = this[parent!]![0].GetFieldValue<Machine>(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.RomOfKey); string? romof = this[cloneOf]![0].GetFieldValue<Machine>(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.RomOfKey);
foreach (DatItem item in items!) foreach (DatItem item in items!)
{ {
item.GetFieldValue<Machine>(DatItem.MachineKey)!.SetFieldValue<string?>(Models.Metadata.Machine.RomOfKey, romof); item.GetFieldValue<Machine>(DatItem.MachineKey)!.SetFieldValue<string?>(Models.Metadata.Machine.RomOfKey, romof);
@@ -1449,26 +1439,30 @@ namespace SabreTools.DatFiles
if (items == null || items.Count == 0) if (items == null || items.Count == 0)
continue; continue;
// Determine if the game has a parent or not // Get the machine
string? parent = null; var machine = items[0].GetFieldValue<Machine>(DatItem.MachineKey);
if (!string.IsNullOrEmpty(items[0].GetFieldValue<Machine>(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.CloneOfKey))) if (machine == null)
parent = items[0].GetFieldValue<Machine>(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.CloneOfKey);
// If there is no parent, then we continue
if (string.IsNullOrEmpty(parent))
continue; continue;
// Get the clone parent
string? cloneOf = machine.GetStringFieldValue(Models.Metadata.Machine.CloneOfKey);
if (string.IsNullOrEmpty(cloneOf))
continue;
// Get the parent items
var parentItems = this[cloneOf];
// Otherwise, move the items from the current game to a subfolder of the parent game // Otherwise, move the items from the current game to a subfolder of the parent game
DatItem copyFrom; DatItem copyFrom;
if (this[parent!]!.Count == 0) if (parentItems == null || parentItems.Count == 0)
{ {
copyFrom = new Rom(); copyFrom = new Rom();
copyFrom.GetFieldValue<Machine>(DatItem.MachineKey)!.SetFieldValue<string?>(Models.Metadata.Machine.NameKey, parent); copyFrom.GetFieldValue<Machine>(DatItem.MachineKey)!.SetFieldValue<string?>(Models.Metadata.Machine.NameKey, cloneOf);
copyFrom.GetFieldValue<Machine>(DatItem.MachineKey)!.SetFieldValue<string?>(Models.Metadata.Machine.DescriptionKey, parent); copyFrom.GetFieldValue<Machine>(DatItem.MachineKey)!.SetFieldValue<string?>(Models.Metadata.Machine.DescriptionKey, cloneOf);
} }
else else
{ {
copyFrom = this[parent!]![0]; copyFrom = parentItems[0];
} }
items = this[game]; items = this[game];
@@ -1480,7 +1474,7 @@ namespace SabreTools.DatFiles
string? mergeTag = disk.GetStringFieldValue(Models.Metadata.Disk.MergeKey); string? mergeTag = disk.GetStringFieldValue(Models.Metadata.Disk.MergeKey);
// If the merge tag exists and the parent already contains it, skip // If the merge tag exists and the parent already contains it, skip
if (mergeTag != null && this[parent!]! if (mergeTag != null && this[cloneOf]!
.Where(i => i is Disk) .Where(i => i is Disk)
.Select(i => (i as Disk)!.GetName()).Contains(mergeTag)) .Select(i => (i as Disk)!.GetName()).Contains(mergeTag))
{ {
@@ -1488,19 +1482,19 @@ namespace SabreTools.DatFiles
} }
// If the merge tag exists but the parent doesn't contain it, add to parent // If the merge tag exists but the parent doesn't contain it, add to parent
else if (mergeTag != null && !this[parent!]! else if (mergeTag != null && !this[cloneOf]!
.Where(i => i is Disk) .Where(i => i is Disk)
.Select(i => (i as Disk)!.GetName()).Contains(mergeTag)) .Select(i => (i as Disk)!.GetName()).Contains(mergeTag))
{ {
disk.CopyMachineInformation(copyFrom); disk.CopyMachineInformation(copyFrom);
Add(parent!, disk); Add(cloneOf, disk);
} }
// If there is no merge tag, add to parent // If there is no merge tag, add to parent
else if (mergeTag == null) else if (mergeTag == null)
{ {
disk.CopyMachineInformation(copyFrom); disk.CopyMachineInformation(copyFrom);
Add(parent!, disk); Add(cloneOf, disk);
} }
} }
@@ -1508,7 +1502,7 @@ namespace SabreTools.DatFiles
else if (item is Rom rom) else if (item is Rom rom)
{ {
// If the merge tag exists and the parent already contains it, skip // If the merge tag exists and the parent already contains it, skip
if (rom.GetStringFieldValue(Models.Metadata.Rom.MergeKey) != null && this[parent!]! if (rom.GetStringFieldValue(Models.Metadata.Rom.MergeKey) != null && this[cloneOf]!
.Where(i => i is Rom).Select(i => (i as Rom)!.GetName()) .Where(i => i is Rom).Select(i => (i as Rom)!.GetName())
.Contains(rom.GetStringFieldValue(Models.Metadata.Rom.MergeKey))) .Contains(rom.GetStringFieldValue(Models.Metadata.Rom.MergeKey)))
{ {
@@ -1516,7 +1510,7 @@ namespace SabreTools.DatFiles
} }
// If the merge tag exists but the parent doesn't contain it, add to subfolder of parent // If the merge tag exists but the parent doesn't contain it, add to subfolder of parent
else if (rom.GetStringFieldValue(Models.Metadata.Rom.MergeKey) != null && !this[parent!]! else if (rom.GetStringFieldValue(Models.Metadata.Rom.MergeKey) != null && !this[cloneOf]!
.Where(i => i is Rom).Select(i => (i as Rom)!.GetName()) .Where(i => i is Rom).Select(i => (i as Rom)!.GetName())
.Contains(rom.GetStringFieldValue(Models.Metadata.Rom.MergeKey))) .Contains(rom.GetStringFieldValue(Models.Metadata.Rom.MergeKey)))
{ {
@@ -1524,28 +1518,28 @@ namespace SabreTools.DatFiles
rom.SetName($"{rom.GetFieldValue<Machine>(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.NameKey)}\\{rom.GetName()}"); rom.SetName($"{rom.GetFieldValue<Machine>(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.NameKey)}\\{rom.GetName()}");
rom.CopyMachineInformation(copyFrom); rom.CopyMachineInformation(copyFrom);
Add(parent!, rom); Add(cloneOf, rom);
} }
// If the parent doesn't already contain this item, add to subfolder of parent // If the parent doesn't already contain this item, add to subfolder of parent
else if (!this[parent!]!.Contains(item) || skipDedup) else if (!this[cloneOf]!.Contains(item) || skipDedup)
{ {
if (subfolder) if (subfolder)
rom.SetName($"{item.GetFieldValue<Machine>(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.NameKey)}\\{rom.GetName()}"); rom.SetName($"{item.GetFieldValue<Machine>(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.NameKey)}\\{rom.GetName()}");
rom.CopyMachineInformation(copyFrom); rom.CopyMachineInformation(copyFrom);
Add(parent!, rom); Add(cloneOf, rom);
} }
} }
// All other that would be missing to subfolder of parent // All other that would be missing to subfolder of parent
else if (!this[parent!]!.Contains(item)) else if (!this[cloneOf]!.Contains(item))
{ {
if (subfolder) if (subfolder)
item.SetName($"{item.GetFieldValue<Machine>(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.NameKey)}\\{item.GetName()}"); item.SetName($"{item.GetFieldValue<Machine>(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.NameKey)}\\{item.GetName()}");
item.CopyMachineInformation(copyFrom); item.CopyMachineInformation(copyFrom);
Add(parent!, item); Add(cloneOf, item);
} }
} }
@@ -1562,13 +1556,19 @@ namespace SabreTools.DatFiles
List<string> games = [.. Keys.OrderBy(g => g)]; List<string> games = [.. Keys.OrderBy(g => g)];
foreach (string game in games) foreach (string game in games)
{ {
// If the game has no items in it, we want to continue
var items = this[game]; var items = this[game];
if (items == null) if (items == null || items.Count == 0)
continue; continue;
if (items.Count > 0 // Get the machine
&& ((items[0].GetFieldValue<Machine>(DatItem.MachineKey)!.GetBoolFieldValue(Models.Metadata.Machine.IsBiosKey) == true) var machine = items[0].GetFieldValue<Machine>(DatItem.MachineKey);
|| (items[0].GetFieldValue<Machine>(DatItem.MachineKey)!.GetBoolFieldValue(Models.Metadata.Machine.IsDeviceKey) == true))) if (machine == null)
continue;
// Remove flagged items
if ((machine.GetBoolFieldValue(Models.Metadata.Machine.IsBiosKey) == true)
|| (machine.GetBoolFieldValue(Models.Metadata.Machine.IsDeviceKey) == true))
{ {
Remove(game); Remove(game);
} }
@@ -1590,28 +1590,26 @@ namespace SabreTools.DatFiles
if (items == null || items.Count == 0) if (items == null || items.Count == 0)
continue; continue;
// If the game (is/is not) a bios, we want to continue // Get the machine
if (bios ^ (items[0].GetFieldValue<Machine>(DatItem.MachineKey)!.GetBoolFieldValue(Models.Metadata.Machine.IsBiosKey) == true)) var machine = items[0].GetFieldValue<Machine>(DatItem.MachineKey);
if (machine == null)
continue; continue;
// Determine if the game has a parent or not // If the game (is/is not) a bios, we want to continue
string? parent = null; if (bios ^ (machine.GetBoolFieldValue(Models.Metadata.Machine.IsBiosKey) == true))
if (!string.IsNullOrEmpty(items[0].GetFieldValue<Machine>(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.RomOfKey))) continue;
parent = items[0].GetFieldValue<Machine>(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.RomOfKey);
// If the parent doesnt exist, we want to continue // Get the bios parent
if (string.IsNullOrEmpty(parent)) string? romOf = machine.GetStringFieldValue(Models.Metadata.Machine.RomOfKey);
if (string.IsNullOrEmpty(romOf))
continue; continue;
// If the parent doesn't have any items, we want to continue // If the parent doesn't have any items, we want to continue
if (this[parent!]!.Count == 0) var parentItems = this[romOf];
if (parentItems == null || parentItems.Count == 0)
continue; continue;
// If the parent exists and has items, we remove the items that are in the parent from the current game // If the parent exists and has items, we remove the items that are in the parent from the current game
var parentItems = this[parent!];
if (parentItems == null)
continue;
foreach (DatItem item in parentItems) foreach (DatItem item in parentItems)
{ {
DatItem datItem = (DatItem)item.Clone(); DatItem datItem = (DatItem)item.Clone();
@@ -1631,29 +1629,27 @@ namespace SabreTools.DatFiles
List<string> games = [.. Keys.OrderBy(g => g)]; List<string> games = [.. Keys.OrderBy(g => g)];
foreach (string game in games) foreach (string game in games)
{ {
var items = this[game];
if (items == null)
continue;
// If the game has no items in it, we want to continue // If the game has no items in it, we want to continue
if (items.Count == 0) var items = this[game];
if (items == null || items.Count == 0)
continue; continue;
// Determine if the game has a parent or not // Get the machine
string? parent = null; var machine = items[0].GetFieldValue<Machine>(DatItem.MachineKey);
if (!string.IsNullOrEmpty(items[0].GetFieldValue<Machine>(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.CloneOfKey))) if (machine == null)
parent = items[0].GetFieldValue<Machine>(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.CloneOfKey); continue;
// If the parent doesnt exist, we want to continue // Get the clone parent
if (string.IsNullOrEmpty(parent)) string? cloneOf = machine.GetStringFieldValue(Models.Metadata.Machine.CloneOfKey);
if (string.IsNullOrEmpty(cloneOf))
continue; continue;
// If the parent doesn't have any items, we want to continue // If the parent doesn't have any items, we want to continue
if (this[parent!] == null || this[parent!]!.Count == 0) var parentItems = this[cloneOf];
if (parentItems == null || parentItems.Count == 0)
continue; continue;
// If the parent exists and has items, we remove the parent items from the current game // If the parent exists and has items, we remove the parent items from the current game
var parentItems = this[parent!];
foreach (DatItem item in parentItems!) foreach (DatItem item in parentItems!)
{ {
DatItem datItem = (DatItem)item.Clone(); DatItem datItem = (DatItem)item.Clone();
@@ -1665,7 +1661,7 @@ namespace SabreTools.DatFiles
// Now we want to get the parent romof tag and put it in each of the remaining items // Now we want to get the parent romof tag and put it in each of the remaining items
items = this[game]; items = this[game];
string? romof = this[parent!]![0].GetFieldValue<Machine>(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.RomOfKey); string? romof = this[cloneOf!]![0].GetFieldValue<Machine>(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.RomOfKey);
foreach (DatItem item in items!) foreach (DatItem item in items!)
{ {
item.GetFieldValue<Machine>(DatItem.MachineKey)!.SetFieldValue<string?>(Models.Metadata.Machine.RomOfKey, romof); item.GetFieldValue<Machine>(DatItem.MachineKey)!.SetFieldValue<string?>(Models.Metadata.Machine.RomOfKey, romof);
@@ -1681,8 +1677,9 @@ namespace SabreTools.DatFiles
List<string> games = [.. Keys.OrderBy(g => g)]; List<string> games = [.. Keys.OrderBy(g => g)];
foreach (string game in games) foreach (string game in games)
{ {
// If the game has no items in it, we want to continue
var items = this[game]; var items = this[game];
if (items == null) if (items == null || items.Count == 0)
continue; continue;
foreach (DatItem item in items) foreach (DatItem item in items)

View File

@@ -51,7 +51,7 @@ namespace SabreTools.DatFiles
/// </summary> /// </summary>
[JsonIgnore, XmlIgnore] [JsonIgnore, XmlIgnore]
#if NET40_OR_GREATER || NETCOREAPP #if NET40_OR_GREATER || NETCOREAPP
private readonly ConcurrentDictionary<long, DatItem> _items = new ConcurrentDictionary<long, DatItem>(); private readonly ConcurrentDictionary<long, DatItem> _items = [];
#else #else
private readonly Dictionary<long, DatItem> _items = []; private readonly Dictionary<long, DatItem> _items = [];
#endif #endif
@@ -67,7 +67,7 @@ namespace SabreTools.DatFiles
/// </summary> /// </summary>
[JsonIgnore, XmlIgnore] [JsonIgnore, XmlIgnore]
#if NET40_OR_GREATER || NETCOREAPP #if NET40_OR_GREATER || NETCOREAPP
private readonly ConcurrentDictionary<long, Machine> _machines = new ConcurrentDictionary<long, Machine>(); private readonly ConcurrentDictionary<long, Machine> _machines = [];
#else #else
private readonly Dictionary<long, Machine> _machines = []; private readonly Dictionary<long, Machine> _machines = [];
#endif #endif
@@ -83,7 +83,7 @@ namespace SabreTools.DatFiles
/// </summary> /// </summary>
[JsonIgnore, XmlIgnore] [JsonIgnore, XmlIgnore]
#if NET40_OR_GREATER || NETCOREAPP #if NET40_OR_GREATER || NETCOREAPP
private readonly ConcurrentDictionary<long, Source> _sources = new ConcurrentDictionary<long, Source>(); private readonly ConcurrentDictionary<long, Source> _sources = [];
#else #else
private readonly Dictionary<long, Source> _sources = []; private readonly Dictionary<long, Source> _sources = [];
#endif #endif
@@ -99,7 +99,7 @@ namespace SabreTools.DatFiles
/// </summary> /// </summary>
[JsonIgnore, XmlIgnore] [JsonIgnore, XmlIgnore]
#if NET40_OR_GREATER || NETCOREAPP #if NET40_OR_GREATER || NETCOREAPP
private readonly ConcurrentDictionary<long, long> _itemToMachineMapping = new ConcurrentDictionary<long, long>(); private readonly ConcurrentDictionary<long, long> _itemToMachineMapping = [];
#else #else
private readonly Dictionary<long, long> _itemToMachineMapping = []; private readonly Dictionary<long, long> _itemToMachineMapping = [];
#endif #endif
@@ -109,7 +109,7 @@ namespace SabreTools.DatFiles
/// </summary> /// </summary>
[JsonIgnore, XmlIgnore] [JsonIgnore, XmlIgnore]
#if NET40_OR_GREATER || NETCOREAPP #if NET40_OR_GREATER || NETCOREAPP
private readonly ConcurrentDictionary<long, long> _itemToSourceMapping = new ConcurrentDictionary<long, long>(); private readonly ConcurrentDictionary<long, long> _itemToSourceMapping = [];
#else #else
private readonly Dictionary<long, long> _itemToSourceMapping = []; private readonly Dictionary<long, long> _itemToSourceMapping = [];
#endif #endif
@@ -119,7 +119,7 @@ namespace SabreTools.DatFiles
/// </summary> /// </summary>
[JsonIgnore, XmlIgnore] [JsonIgnore, XmlIgnore]
#if NET40_OR_GREATER || NETCOREAPP #if NET40_OR_GREATER || NETCOREAPP
private readonly ConcurrentDictionary<string, ConcurrentList<long>> _buckets = new ConcurrentDictionary<string, ConcurrentList<long>>(); private readonly ConcurrentDictionary<string, ConcurrentList<long>> _buckets = [];
#else #else
private readonly Dictionary<string, ConcurrentList<long>> _buckets = []; private readonly Dictionary<string, ConcurrentList<long>> _buckets = [];
#endif #endif
@@ -342,17 +342,20 @@ namespace SabreTools.DatFiles
/// <summary> /// <summary>
/// Get all item to machine mappings /// Get all item to machine mappings
/// </summary> /// </summary>
public (long, long)[] GetItemMachineMappings() => [.. _itemToMachineMapping.Select(kvp => (kvp.Key, kvp.Value))]; public (long, long)[] GetItemMachineMappings()
=> [.. _itemToMachineMapping.Select(kvp => (kvp.Key, kvp.Value))];
/// <summary> /// <summary>
/// Get all item to source mappings /// Get all item to source mappings
/// </summary> /// </summary>
public (long, long)[] GetItemSourceMappings() => [.. _itemToSourceMapping.Select(kvp => (kvp.Key, kvp.Value))]; public (long, long)[] GetItemSourceMappings()
=> [.. _itemToSourceMapping.Select(kvp => (kvp.Key, kvp.Value))];
/// <summary> /// <summary>
/// Get all items and their indicies /// Get all items and their indicies
/// </summary> /// </summary>
public (long, DatItem)[] GetItems() => [.. _items.Select(kvp => (kvp.Key, kvp.Value))]; public (long, DatItem)[] GetItems()
=> [.. _items.Select(kvp => (kvp.Key, kvp.Value))];
/// <summary> /// <summary>
/// Get the indices and items associated with a bucket name /// Get the indices and items associated with a bucket name
@@ -454,7 +457,8 @@ namespace SabreTools.DatFiles
/// <summary> /// <summary>
/// Get all machines and their indicies /// Get all machines and their indicies
/// </summary> /// </summary>
public (long, Machine)[] GetMachines() => [.. _machines.Select(kvp => (kvp.Key, kvp.Value))]; public (long, Machine)[] GetMachines()
=> [.. _machines.Select(kvp => (kvp.Key, kvp.Value))];
/// <summary> /// <summary>
/// Get a source based on the index /// Get a source based on the index
@@ -485,7 +489,8 @@ namespace SabreTools.DatFiles
/// <summary> /// <summary>
/// Get all sources and their indicies /// Get all sources and their indicies
/// </summary> /// </summary>
public (long, Source)[] GetSources() => [.. _sources.Select(kvp => (kvp.Key, kvp.Value))]; public (long, Source)[] GetSources()
=> [.. _sources.Select(kvp => (kvp.Key, kvp.Value))];
/// <summary> /// <summary>
/// Remove an item, returning if it could be removed /// Remove an item, returning if it could be removed
@@ -720,7 +725,7 @@ namespace SabreTools.DatFiles
return false; return false;
// Try to find duplicates // Try to find duplicates
return roms.Any(r => datItem.Equals(r.Item2)) == true; return roms.Any(r => datItem.Equals(r.Item2));
} }
/// <summary> /// <summary>
@@ -1148,14 +1153,14 @@ namespace SabreTools.DatFiles
return xType.AsEnumValue<ItemType>() - yType.AsEnumValue<ItemType>(); return xType.AsEnumValue<ItemType>() - yType.AsEnumValue<ItemType>();
// If directory names don't match // If directory names don't match
string? xDirectoryName = Path.GetDirectoryName(TextHelper.RemovePathUnsafeCharacters(x.Item2.GetName() ?? string.Empty)); string? xDirectoryName = Path.GetDirectoryName(TextHelper.RemovePathUnsafeCharacters(x.Item2.GetName()));
string? yDirectoryName = Path.GetDirectoryName(TextHelper.RemovePathUnsafeCharacters(y.Item2.GetName() ?? string.Empty)); string? yDirectoryName = Path.GetDirectoryName(TextHelper.RemovePathUnsafeCharacters(y.Item2.GetName()));
if (xDirectoryName != yDirectoryName) if (xDirectoryName != yDirectoryName)
return nc.Compare(xDirectoryName, yDirectoryName); return nc.Compare(xDirectoryName, yDirectoryName);
// If item names don't match // If item names don't match
string? xName = Path.GetFileName(TextHelper.RemovePathUnsafeCharacters(x.Item2.GetName() ?? string.Empty)); string? xName = Path.GetFileName(TextHelper.RemovePathUnsafeCharacters(x.Item2.GetName()));
string? yName = Path.GetFileName(TextHelper.RemovePathUnsafeCharacters(y.Item2.GetName() ?? string.Empty)); string? yName = Path.GetFileName(TextHelper.RemovePathUnsafeCharacters(y.Item2.GetName()));
if (xName != yName) if (xName != yName)
return nc.Compare(xName, yName); return nc.Compare(xName, yName);
@@ -1243,11 +1248,7 @@ namespace SabreTools.DatFiles
try try
{ {
// First we want to get a mapping for all games to description // First we want to get a mapping for all games to description
#if NET40_OR_GREATER || NETCOREAPP var mapping = CreateMachineToDescriptionMapping();
ConcurrentDictionary<string, string> mapping = CreateMachineToDescriptionMapping();
#else
Dictionary<string, string> mapping = CreateMachineToDescriptionMapping();
#endif
// Now we loop through every item and update accordingly // Now we loop through every item and update accordingly
UpdateMachineNamesFromDescriptions(mapping); UpdateMachineNamesFromDescriptions(mapping);
@@ -1443,11 +1444,7 @@ namespace SabreTools.DatFiles
/// <summary> /// <summary>
/// Create machine to description mapping dictionary /// Create machine to description mapping dictionary
/// </summary> /// </summary>
#if NET40_OR_GREATER || NETCOREAPP private IDictionary<string, string> CreateMachineToDescriptionMapping()
private ConcurrentDictionary<string, string> CreateMachineToDescriptionMapping()
#else
private Dictionary<string, string> CreateMachineToDescriptionMapping()
#endif
{ {
#if NET40_OR_GREATER || NETCOREAPP #if NET40_OR_GREATER || NETCOREAPP
ConcurrentDictionary<string, string> mapping = new(); ConcurrentDictionary<string, string> mapping = new();
@@ -1555,11 +1552,7 @@ namespace SabreTools.DatFiles
/// <summary> /// <summary>
/// Update machine names from descriptions according to mappings /// Update machine names from descriptions according to mappings
/// </summary> /// </summary>
#if NET40_OR_GREATER || NETCOREAPP private void UpdateMachineNamesFromDescriptions(IDictionary<string, string> mapping)
private void UpdateMachineNamesFromDescriptions(ConcurrentDictionary<string, string> mapping)
#else
private void UpdateMachineNamesFromDescriptions(Dictionary<string, string> mapping)
#endif
{ {
#if NET452_OR_GREATER || NETCOREAPP #if NET452_OR_GREATER || NETCOREAPP
Parallel.ForEach(SortedKeys, Globals.ParallelOptions, key => Parallel.ForEach(SortedKeys, Globals.ParallelOptions, key =>
@@ -1626,9 +1619,8 @@ namespace SabreTools.DatFiles
List<string> games = [.. SortedKeys]; List<string> games = [.. SortedKeys];
foreach (string game in games) foreach (string game in games)
{ {
// Get the items for this game
var items = GetItemsForBucket(game); var items = GetItemsForBucket(game);
// If the game has no items in it, we want to continue
if (items == null || items.Length == 0) if (items == null || items.Length == 0)
continue; continue;
@@ -1640,21 +1632,13 @@ namespace SabreTools.DatFiles
if (machine.Item2 == null) if (machine.Item2 == null)
continue; continue;
// Determine if the game has a parent or not // Get the bios parent
(long, string?) parent = (-1, null); string? romOf = machine.Item2.GetStringFieldValue(Models.Metadata.Machine.RomOfKey);
if (!string.IsNullOrEmpty(machine.Item2.GetStringFieldValue(Models.Metadata.Machine.RomOfKey))) if (string.IsNullOrEmpty(romOf))
{
string? romOf = machine.Item2.GetStringFieldValue(Models.Metadata.Machine.RomOfKey);
var romOfMachine = GetMachine(romOf);
parent = (romOfMachine.Item1, romOf);
}
// If the parent doesnt exist, we want to continue
if (string.IsNullOrEmpty(parent.Item2))
continue; continue;
// If the parent doesn't have any items, we want to continue // If the parent doesn't have any items, we want to continue
var parentItems = GetItemsForBucket(parent.Item2!); var parentItems = GetItemsForBucket(romOf);
if (parentItems == null || parentItems.Length == 0) if (parentItems == null || parentItems.Length == 0)
continue; continue;
@@ -1679,10 +1663,8 @@ namespace SabreTools.DatFiles
List<string> games = [.. SortedKeys]; List<string> games = [.. SortedKeys];
foreach (string game in games) foreach (string game in games)
{ {
// Get the items for this game // If the game has no items in it, we want to continue
var items = GetItemsForBucket(game); var items = GetItemsForBucket(game);
// If the machine doesn't have items, we continue
if (items == null || items.Length == 0) if (items == null || items.Length == 0)
continue; continue;
@@ -1857,21 +1839,13 @@ namespace SabreTools.DatFiles
if (machine.Item2 == null) if (machine.Item2 == null)
continue; continue;
// Determine if the game has a parent or not // Get the clone parent
(long, string?) parent = (-1, null); string? cloneOf = machine.Item2.GetStringFieldValue(Models.Metadata.Machine.CloneOfKey);
if (!string.IsNullOrEmpty(machine.Item2.GetStringFieldValue(Models.Metadata.Machine.CloneOfKey))) if (string.IsNullOrEmpty(cloneOf))
{
string? cloneOf = machine.Item2.GetStringFieldValue(Models.Metadata.Machine.CloneOfKey);
var cloneOfMachine = GetMachine(cloneOf);
parent = (cloneOfMachine.Item1, cloneOf);
}
// If there is no parent, then we continue
if (string.IsNullOrEmpty(parent.Item2))
continue; continue;
// If the parent doesn't have any items, we want to continue // If the parent doesn't have any items, we want to continue
var parentItems = GetItemsForBucket(parent.Item2!); var parentItems = GetItemsForBucket(cloneOf);
if (parentItems == null || parentItems.Length == 0) if (parentItems == null || parentItems.Length == 0)
continue; continue;
@@ -1887,7 +1861,7 @@ namespace SabreTools.DatFiles
} }
// Get the parent machine // Get the parent machine
var parentMachine = GetMachineForItem(GetItemsForBucket(parent.Item2!)![0].Item1); var parentMachine = GetMachineForItem(GetItemsForBucket(cloneOf)![0].Item1);
if (parentMachine.Item2 == null) if (parentMachine.Item2 == null)
continue; continue;
@@ -1925,24 +1899,21 @@ namespace SabreTools.DatFiles
if (machine.Item2 == null) if (machine.Item2 == null)
continue; continue;
// Determine if the game has a parent or not // Get the clone parent
(long, string?) parent = (-1, null); string? cloneOf = machine.Item2.GetStringFieldValue(Models.Metadata.Machine.CloneOfKey);
if (!string.IsNullOrEmpty(machine.Item2.GetStringFieldValue(Models.Metadata.Machine.CloneOfKey))) if (string.IsNullOrEmpty(cloneOf))
{ continue;
string? cloneOf = machine.Item2.GetStringFieldValue(Models.Metadata.Machine.CloneOfKey);
var cloneOfMachine = GetMachine(cloneOf);
parent = (cloneOfMachine.Item1, cloneOf);
}
// If there is no parent, then we continue // Get the clone parent machine
if (string.IsNullOrEmpty(parent.Item2)) var cloneOfMachine = GetMachine(cloneOf);
if (cloneOfMachine.Item2 == null)
continue; continue;
items = GetItemsForBucket(game); items = GetItemsForBucket(game);
foreach ((long, DatItem) item in items!) foreach ((long, DatItem) item in items!)
{ {
// Get the parent items and current machine name // Get the parent items and current machine name
var parentItems = GetItemsForBucket(parent.Item2!); var parentItems = GetItemsForBucket(cloneOf);
string? machineName = GetMachineForItem(item.Item1).Item2?.GetStringFieldValue(Models.Metadata.Machine.NameKey); string? machineName = GetMachineForItem(item.Item1).Item2?.GetStringFieldValue(Models.Metadata.Machine.NameKey);
// Special disk handling // Special disk handling
@@ -1965,15 +1936,15 @@ namespace SabreTools.DatFiles
.Select(i => (i.Item2 as Disk)!.GetName()) .Select(i => (i.Item2 as Disk)!.GetName())
.Contains(mergeTag)) .Contains(mergeTag))
{ {
_itemToMachineMapping[item.Item1] = parent.Item1; _itemToMachineMapping[item.Item1] = cloneOfMachine.Item1;
_buckets[parent.Item2!].Add(disk); _buckets[cloneOf].Add(disk);
} }
// If there is no merge tag, add to parent // If there is no merge tag, add to parent
else if (mergeTag == null) else if (mergeTag == null)
{ {
_itemToMachineMapping[item.Item1] = parent.Item1; _itemToMachineMapping[item.Item1] = cloneOfMachine.Item1;
_buckets[parent.Item2!].Add(disk); _buckets[cloneOf].Add(disk);
} }
} }
@@ -1998,8 +1969,8 @@ namespace SabreTools.DatFiles
if (subfolder) if (subfolder)
rom.SetName($"{machineName}\\{rom.GetName()}"); rom.SetName($"{machineName}\\{rom.GetName()}");
_itemToMachineMapping[item.Item1] = parent.Item1; _itemToMachineMapping[item.Item1] = cloneOfMachine.Item1;
_buckets[parent.Item2!].Add(rom); _buckets[cloneOf].Add(rom);
} }
// If the parent doesn't already contain this item, add to subfolder of parent // If the parent doesn't already contain this item, add to subfolder of parent
@@ -2008,8 +1979,8 @@ namespace SabreTools.DatFiles
if (subfolder) if (subfolder)
rom.SetName($"{machineName}\\{rom.GetName()}"); rom.SetName($"{machineName}\\{rom.GetName()}");
_itemToMachineMapping[item.Item1] = parent.Item1; _itemToMachineMapping[item.Item1] = cloneOfMachine.Item1;
_buckets[parent.Item2!].Add(rom); _buckets[cloneOf].Add(rom);
} }
} }
@@ -2019,8 +1990,8 @@ namespace SabreTools.DatFiles
if (subfolder) if (subfolder)
item.Item2.SetName($"{machineName}\\{item.Item2.GetName()}"); item.Item2.SetName($"{machineName}\\{item.Item2.GetName()}");
_itemToMachineMapping[item.Item1] = parent.Item1; _itemToMachineMapping[item.Item1] = cloneOfMachine.Item1;
_buckets[parent.Item2!].Add(item); _buckets[cloneOf].Add(item);
} }
} }
@@ -2041,14 +2012,17 @@ namespace SabreTools.DatFiles
List<string> games = [.. SortedKeys]; List<string> games = [.. SortedKeys];
foreach (string game in games) foreach (string game in games)
{ {
// If the game has no items in it, we want to continue
var items = GetItemsForBucket(game); var items = GetItemsForBucket(game);
if (items == null || items.Length == 0) if (items == null || items.Length == 0)
continue; continue;
// Get the machine
var machine = GetMachineForItem(items[0].Item1); var machine = GetMachineForItem(items[0].Item1);
if (machine.Item2 == null) if (machine.Item2 == null)
continue; continue;
// Remove flagged items
if ((machine.Item2.GetBoolFieldValue(Models.Metadata.Machine.IsBiosKey) == true) if ((machine.Item2.GetBoolFieldValue(Models.Metadata.Machine.IsBiosKey) == true)
|| (machine.Item2.GetBoolFieldValue(Models.Metadata.Machine.IsDeviceKey) == true)) || (machine.Item2.GetBoolFieldValue(Models.Metadata.Machine.IsDeviceKey) == true))
{ {
@@ -2084,17 +2058,13 @@ namespace SabreTools.DatFiles
if (bios ^ (machine.Item2.GetBoolFieldValue(Models.Metadata.Machine.IsBiosKey) == true)) if (bios ^ (machine.Item2.GetBoolFieldValue(Models.Metadata.Machine.IsBiosKey) == true))
continue; continue;
// Determine if the game has a parent or not // Get the bios parent
string? parent = null; string? romOf = machine.Item2.GetStringFieldValue(Models.Metadata.Machine.RomOfKey);
if (!string.IsNullOrEmpty(machine.Item2.GetStringFieldValue(Models.Metadata.Machine.RomOfKey))) if (string.IsNullOrEmpty(romOf))
parent = machine.Item2.GetStringFieldValue(Models.Metadata.Machine.RomOfKey);
// If the parent doesnt exist, we want to continue
if (string.IsNullOrEmpty(parent))
continue; continue;
// If the parent doesn't have any items, we want to continue // If the parent doesn't have any items, we want to continue
var parentItems = GetItemsForBucket(parent!); var parentItems = GetItemsForBucket(romOf);
if (parentItems == null || parentItems.Length == 0) if (parentItems == null || parentItems.Length == 0)
continue; continue;
@@ -2118,12 +2088,9 @@ namespace SabreTools.DatFiles
List<string> games = [.. SortedKeys]; List<string> games = [.. SortedKeys];
foreach (string game in games) foreach (string game in games)
{ {
var items = GetItemsForBucket(game);
if (items == null)
continue;
// If the game has no items in it, we want to continue // If the game has no items in it, we want to continue
if (items.Length == 0) var items = GetItemsForBucket(game);
if (items == null || items.Length == 0)
continue; continue;
// Get the machine for the first item // Get the machine for the first item
@@ -2131,17 +2098,13 @@ namespace SabreTools.DatFiles
if (machine.Item2 == null) if (machine.Item2 == null)
continue; continue;
// Determine if the game has a parent or not // Get the clone parent
string? parent = null; string? cloneOf = machine.Item2.GetStringFieldValue(Models.Metadata.Machine.CloneOfKey);
if (!string.IsNullOrEmpty(machine.Item2.GetStringFieldValue(Models.Metadata.Machine.CloneOfKey))) if (string.IsNullOrEmpty(cloneOf))
parent = machine.Item2.GetStringFieldValue(Models.Metadata.Machine.CloneOfKey);
// If the parent doesnt exist, we want to continue
if (string.IsNullOrEmpty(parent))
continue; continue;
// If the parent doesn't have any items, we want to continue // If the parent doesn't have any items, we want to continue
var parentItems = GetItemsForBucket(parent!); var parentItems = GetItemsForBucket(cloneOf);
if (parentItems == null || parentItems.Length == 0) if (parentItems == null || parentItems.Length == 0)
continue; continue;
@@ -2157,7 +2120,7 @@ namespace SabreTools.DatFiles
// Now we want to get the parent romof tag and put it in each of the remaining items // Now we want to get the parent romof tag and put it in each of the remaining items
items = GetItemsForBucket(game); items = GetItemsForBucket(game);
machine = GetMachineForItem(GetItemsForBucket(parent!)![0].Item1); machine = GetMachineForItem(GetItemsForBucket(cloneOf)![0].Item1);
if (machine.Item2 == null) if (machine.Item2 == null)
continue; continue;
@@ -2181,8 +2144,9 @@ namespace SabreTools.DatFiles
List<string> games = [.. SortedKeys]; List<string> games = [.. SortedKeys];
foreach (string game in games) foreach (string game in games)
{ {
// If the game has no items in it, we want to continue
var items = GetItemsForBucket(game); var items = GetItemsForBucket(game);
if (items == null) if (items == null || items.Length == 0)
continue; continue;
foreach ((long, DatItem) item in items) foreach ((long, DatItem) item in items)