Handle known enumerable types better

This commit is contained in:
Matt Nadareski
2024-11-12 21:12:06 -05:00
parent 4b3955af77
commit a4da7f3657
19 changed files with 227 additions and 178 deletions

View File

@@ -1,6 +1,5 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using Microsoft.Data.Sqlite; using Microsoft.Data.Sqlite;
using SabreTools.Help; using SabreTools.Help;
using SabreTools.IO; using SabreTools.IO;
@@ -33,7 +32,7 @@ namespace RombaSharp.Features
logger.Error("This feature is not yet implemented: import"); logger.Error("This feature is not yet implemented: import");
// Ensure the inputs // Ensure the inputs
var files = PathTool.GetFilesOnly(Inputs).Select(p => p.CurrentPath); var files = PathTool.GetFilesOnly(Inputs).ConvertAll(p => p.CurrentPath);
Inputs.Clear(); Inputs.Clear();
Inputs.AddRange(files); Inputs.AddRange(files);

View File

@@ -1,6 +1,5 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using SabreTools.Help; using SabreTools.Help;
using SabreTools.IO; using SabreTools.IO;
@@ -43,7 +42,7 @@ namespace RombaSharp.Features
logger.Error("This feature is not yet implemented: merge"); logger.Error("This feature is not yet implemented: merge");
// Verify that the inputs are valid directories // Verify that the inputs are valid directories
var dirs = PathTool.GetDirectoriesOnly(Inputs).Select(p => p.CurrentPath); var dirs = PathTool.GetDirectoriesOnly(Inputs).ConvertAll(p => p.CurrentPath);
Inputs.Clear(); Inputs.Clear();
Inputs.AddRange(dirs); Inputs.AddRange(dirs);

View File

@@ -33,8 +33,8 @@ namespace SabreTools.Core
// Enumerable types // Enumerable types
byte[] bytArr => bytArr.Clone(), byte[] bytArr => bytArr.Clone(),
string[] strArr => strArr.Clone(), string[] strArr => strArr.Clone(),
DictionaryBase[] dbArr => dbArr.Select(Clone).ToArray(), DictionaryBase[] dbArr => Array.ConvertAll(dbArr, Clone),
ICloneable[] clArr => clArr.Select(cl => cl.Clone()).ToArray(), ICloneable[] clArr => Array.ConvertAll(clArr, cl => cl.Clone()),
// Everything else just copies // Everything else just copies
_ => value, _ => value,

View File

@@ -1,5 +1,4 @@
using System; using System;
using System.Linq;
using SabreTools.Core.Tools; using SabreTools.Core.Tools;
using SabreTools.Models.Metadata; using SabreTools.Models.Metadata;
@@ -62,7 +61,7 @@ namespace SabreTools.Core.Filter
return false; return false;
// Get the value that matches the field name provided // Get the value that matches the field name provided
string? realField = constants.FirstOrDefault(c => string.Equals(c, fieldName, StringComparison.OrdinalIgnoreCase)); string? realField = Array.Find(constants, c => string.Equals(c, fieldName, StringComparison.OrdinalIgnoreCase));
if (realField == null) if (realField == null)
return false; return false;

View File

@@ -1,5 +1,4 @@
using System; using System;
using System.Linq;
using SabreTools.Core.Tools; using SabreTools.Core.Tools;
using SabreTools.Models.Metadata; using SabreTools.Models.Metadata;
@@ -110,7 +109,7 @@ namespace SabreTools.Core.Filter
// Get if there's a match to the constant // Get if there's a match to the constant
string localFieldName = fieldName; string localFieldName = fieldName;
string? constantMatch = constants.FirstOrDefault(c => string.Equals(c, localFieldName, StringComparison.OrdinalIgnoreCase)); string? constantMatch = Array.Find(constants, c => string.Equals(c, localFieldName, StringComparison.OrdinalIgnoreCase));
if (constantMatch == null) if (constantMatch == null)
return false; return false;
@@ -132,7 +131,7 @@ namespace SabreTools.Core.Filter
// Get if there's a match to the constant // Get if there's a match to the constant
string localFieldName = fieldName; string localFieldName = fieldName;
string? constantMatch = constants.FirstOrDefault(c => string.Equals(c, localFieldName, StringComparison.OrdinalIgnoreCase)); string? constantMatch = Array.Find(constants, c => string.Equals(c, localFieldName, StringComparison.OrdinalIgnoreCase));
if (constantMatch == null) if (constantMatch == null)
return false; return false;
@@ -156,7 +155,7 @@ namespace SabreTools.Core.Filter
// If we get any matches // If we get any matches
string localFieldName = fieldName; string localFieldName = fieldName;
string? matchedType = itemTypes.FirstOrDefault(t => DatItemContainsField(t, localFieldName)); string? matchedType = Array.Find(itemTypes, t => DatItemContainsField(t, localFieldName));
if (matchedType != null) if (matchedType != null)
{ {
// Check for a matching field // Check for a matching field
@@ -208,7 +207,7 @@ namespace SabreTools.Core.Filter
// Get if there's a match to the constant // Get if there's a match to the constant
string localFieldName = fieldName; string localFieldName = fieldName;
string? constantMatch = constants.FirstOrDefault(c => string.Equals(c, localFieldName, StringComparison.OrdinalIgnoreCase)); string? constantMatch = Array.Find(constants, c => string.Equals(c, localFieldName, StringComparison.OrdinalIgnoreCase));
return constantMatch; return constantMatch;
} }

View File

@@ -1,5 +1,4 @@
using System; using System;
using System.Linq;
namespace SabreTools.Core.Tools namespace SabreTools.Core.Tools
{ {
@@ -32,7 +31,7 @@ namespace SabreTools.Core.Tools
return null; return null;
// Get the enum value info from the array, if possible // Get the enum value info from the array, if possible
var enumValueMemberInfo = memberInfos.FirstOrDefault(m => m.DeclaringType == enumType); var enumValueMemberInfo = Array.Find(memberInfos, m => m.DeclaringType == enumType);
if (enumValueMemberInfo == null) if (enumValueMemberInfo == null)
return null; return null;
@@ -42,7 +41,7 @@ namespace SabreTools.Core.Tools
return null; return null;
// Return the first attribute, if possible // Return the first attribute, if possible
return (MappingAttribute?)attributes.FirstOrDefault(); return (MappingAttribute?)attributes[0];
} }
} }
} }

View File

@@ -50,19 +50,23 @@ namespace SabreTools.Core.Tools
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{ {
// If not all types can be loaded, use the ones that could be // If not all types can be loaded, use the ones that could be
List<Type> assemblyTypes = []; Type?[] assemblyTypes = [];
try try
{ {
assemblyTypes = [.. assembly.GetTypes()]; assemblyTypes = assembly.GetTypes();
} }
catch (ReflectionTypeLoadException rtle) catch (ReflectionTypeLoadException rtle)
{ {
assemblyTypes = [.. rtle.Types.Where(t => t != null)]; assemblyTypes = Array.FindAll(rtle.Types ?? [], t => t != null);
} }
// Loop through all types // Loop through all types
foreach (Type type in assemblyTypes) foreach (Type? type in assemblyTypes)
{ {
// If the type is invalid
if (type == null)
continue;
// If the type isn't a class or doesn't implement the interface // If the type isn't a class or doesn't implement the interface
if (!type.IsClass || !typeof(DatItem).IsAssignableFrom(type)) if (!type.IsClass || !typeof(DatItem).IsAssignableFrom(type))
continue; continue;
@@ -89,19 +93,23 @@ namespace SabreTools.Core.Tools
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{ {
// If not all types can be loaded, use the ones that could be // If not all types can be loaded, use the ones that could be
List<Type> assemblyTypes = []; Type?[] assemblyTypes = [];
try try
{ {
assemblyTypes = [.. assembly.GetTypes()]; assemblyTypes = assembly.GetTypes();
} }
catch (ReflectionTypeLoadException rtle) catch (ReflectionTypeLoadException rtle)
{ {
assemblyTypes = [.. rtle.Types.Where(t => t != null)]; assemblyTypes = Array.FindAll(rtle.Types ?? [], t => t != null);
} }
// Loop through all types // Loop through all types
foreach (Type type in assemblyTypes) foreach (Type? type in assemblyTypes)
{ {
// If the type is invalid
if (type == null)
continue;
// If the type isn't a class or doesn't implement the interface // If the type isn't a class or doesn't implement the interface
if (!type.IsClass || !typeof(DatItem).IsAssignableFrom(type)) if (!type.IsClass || !typeof(DatItem).IsAssignableFrom(type))
continue; continue;

View File

@@ -754,7 +754,7 @@ namespace SabreTools.DatFiles
foreach (DatItem datItem in datItems) foreach (DatItem datItem in datItems)
{ {
ItemType itemType = datItem.GetStringFieldValue(Models.Metadata.DatItem.TypeKey).AsEnumValue<ItemType>(); ItemType itemType = datItem.GetStringFieldValue(Models.Metadata.DatItem.TypeKey).AsEnumValue<ItemType>();
if (GetSupportedTypes().Contains(itemType)) if (Array.Exists(GetSupportedTypes(), t => t == itemType))
return true; return true;
} }
@@ -776,7 +776,7 @@ namespace SabreTools.DatFiles
foreach ((long, DatItem) datItem in datItems) foreach ((long, DatItem) datItem in datItems)
{ {
ItemType itemType = datItem.Item2.GetStringFieldValue(Models.Metadata.DatItem.TypeKey).AsEnumValue<ItemType>(); ItemType itemType = datItem.Item2.GetStringFieldValue(Models.Metadata.DatItem.TypeKey).AsEnumValue<ItemType>();
if (GetSupportedTypes().Contains(itemType)) if (Array.Exists(GetSupportedTypes(), t => t == itemType))
return true; return true;
} }

View File

@@ -478,7 +478,7 @@ namespace SabreTools.DatFiles.Formats
continue; continue;
// Resolve the names in the block // Resolve the names in the block
items = [.. DatItem.ResolveNamesDB(items.ToList())]; items = [.. DatItem.ResolveNamesDB([.. items])];
for (int index = 0; index < items.Length; index++) for (int index = 0; index < items.Length; index++)
{ {

View File

@@ -1,5 +1,5 @@
using System.Collections.Generic; using System;
using System.Linq; using System.Collections.Generic;
using SabreTools.DatItems; using SabreTools.DatItems;
using SabreTools.DatItems.Formats; using SabreTools.DatItems.Formats;
@@ -122,9 +122,9 @@ namespace SabreTools.DatFiles.Formats
if (dipSwitch.ValuesSpecified) if (dipSwitch.ValuesSpecified)
{ {
var dipValues = dipSwitch.GetFieldValue<DipValue[]?>(Models.Metadata.DipSwitch.DipValueKey); var dipValues = dipSwitch.GetFieldValue<DipValue[]?>(Models.Metadata.DipSwitch.DipValueKey);
if (dipValues!.Any(dv => string.IsNullOrEmpty(dv.GetName()))) if (Array.Find(dipValues!, dv => string.IsNullOrEmpty(dv.GetName())) != null)
missingFields.Add(Models.Metadata.DipValue.NameKey); missingFields.Add(Models.Metadata.DipValue.NameKey);
if (dipValues!.Any(dv => string.IsNullOrEmpty(dv.GetStringFieldValue(Models.Metadata.DipValue.ValueKey)))) if (Array.Find(dipValues!, dv => string.IsNullOrEmpty(dv.GetStringFieldValue(Models.Metadata.DipValue.ValueKey))) != null)
missingFields.Add(Models.Metadata.DipValue.ValueKey); missingFields.Add(Models.Metadata.DipValue.ValueKey);
} }

View File

@@ -322,7 +322,7 @@ namespace SabreTools.DatFiles
items.TryRemove(key, out _); items.TryRemove(key, out _);
// If there are no non-blank items, remove // If there are no non-blank items, remove
else if (!value!.Any(i => i != null && i is not Blank)) else if (value!.FindIndex(i => i != null && i is not Blank) == -1)
items.TryRemove(key, out _); items.TryRemove(key, out _);
#else #else
// If the key doesn't exist, skip // If the key doesn't exist, skip
@@ -334,7 +334,7 @@ namespace SabreTools.DatFiles
items.Remove(key); items.Remove(key);
// 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]!.FindIndex(i => i != null && i is not Blank) == -1)
items.Remove(key); items.Remove(key);
#endif #endif
} }
@@ -348,8 +348,12 @@ namespace SabreTools.DatFiles
List<string> keys = [.. items.Keys]; List<string> keys = [.. items.Keys];
foreach (string key in keys) foreach (string key in keys)
{ {
// Skip invalid item lists
List<DatItem>? oldItemList = items[key]; List<DatItem>? oldItemList = items[key];
List<DatItem>? newItemList = oldItemList?.Where(i => i.GetBoolFieldValue(DatItem.RemoveKey) != true)?.ToList(); if (oldItemList == null)
return;
List<DatItem> newItemList = oldItemList.FindAll(i => i.GetBoolFieldValue(DatItem.RemoveKey) != true);
Remove(key); Remove(key);
AddRange(key, newItemList); AddRange(key, newItemList);
@@ -630,7 +634,7 @@ namespace SabreTools.DatFiles
if (roms == null) if (roms == null)
return false; return false;
return roms.Any(r => datItem.Equals(r)); return roms.FindIndex(r => datItem.Equals(r)) > -1;
} }
/// <summary> /// <summary>
@@ -747,7 +751,7 @@ namespace SabreTools.DatFiles
#endif #endif
{ {
// Get the possibly unsorted list // Get the possibly unsorted list
List<DatItem>? sortedlist = this[key]?.ToList(); List<DatItem>? sortedlist = this[key];
if (sortedlist == null) if (sortedlist == null)
#if NET40_OR_GREATER || NETCOREAPP #if NET40_OR_GREATER || NETCOREAPP
return; return;
@@ -953,7 +957,7 @@ namespace SabreTools.DatFiles
string? machine = default; string? machine = default;
foreach (string region in regionList) foreach (string region in regionList)
{ {
machine = parents[key].FirstOrDefault(m => Regex.IsMatch(m, @"\(.*" + region + @".*\)", RegexOptions.IgnoreCase)); machine = parents[key].Find(m => Regex.IsMatch(m, @"\(.*" + region + @".*\)", RegexOptions.IgnoreCase));
if (machine != default) if (machine != default)
break; break;
} }
@@ -1190,7 +1194,9 @@ namespace SabreTools.DatFiles
/// </summary> /// </summary>
public void AddRomsFromBios() public void AddRomsFromBios()
{ {
List<string> games = [.. Keys.OrderBy(g => g)]; List<string> games = [.. Keys];
games.Sort();
foreach (string game in games) foreach (string game in games)
{ {
// If the game has no items in it, we want to continue // If the game has no items in it, we want to continue
@@ -1219,7 +1225,7 @@ namespace SabreTools.DatFiles
{ {
DatItem datItem = (DatItem)item.Clone(); DatItem datItem = (DatItem)item.Clone();
datItem.CopyMachineInformation(copyFrom); datItem.CopyMachineInformation(copyFrom);
if (!items.Any(i => i.GetName() == datItem.GetName()) && !items.Contains(datItem)) if (items.FindIndex(i => i.GetName() == datItem.GetName()) == -1 && !items.Contains(datItem))
Add(game, datItem); Add(game, datItem);
} }
} }
@@ -1233,7 +1239,9 @@ namespace SabreTools.DatFiles
public bool AddRomsFromDevices(bool dev, bool useSlotOptions) public bool AddRomsFromDevices(bool dev, bool useSlotOptions)
{ {
bool foundnew = false; bool foundnew = false;
List<string> machines = [.. Keys.OrderBy(g => g)]; List<string> machines = [.. Keys];
machines.Sort();
foreach (string machine in machines) foreach (string machine in machines)
{ {
// If the machine doesn't have items, we continue // If the machine doesn't have items, we continue
@@ -1266,7 +1274,7 @@ namespace SabreTools.DatFiles
if (deviceReferences.Count > 0) if (deviceReferences.Count > 0)
{ {
// Loop through all names and check the corresponding machines // Loop through all names and check the corresponding machines
List<string> newDeviceReferences = []; var newDeviceReferences = new HashSet<string>();
foreach (string? deviceReference in deviceReferences) foreach (string? deviceReference in deviceReferences)
{ {
// If the machine doesn't exist then we continue // If the machine doesn't exist then we continue
@@ -1278,7 +1286,7 @@ namespace SabreTools.DatFiles
if (devItems == null) if (devItems == null)
continue; continue;
newDeviceReferences.AddRange(devItems newDeviceReferences.IntersectWith(devItems
.Where(i => i is DeviceRef) .Where(i => i is DeviceRef)
.Select(i => (i as DeviceRef)!.GetName()!)); .Select(i => (i as DeviceRef)!.GetName()!));
@@ -1301,7 +1309,7 @@ namespace SabreTools.DatFiles
} }
// Now that every device reference is accounted for, add the new list of device references, if they don't already exist // Now that every device reference is accounted for, add the new list of device references, if they don't already exist
foreach (string deviceReference in newDeviceReferences.Distinct()) foreach (string deviceReference in newDeviceReferences)
{ {
if (!deviceReferences.Contains(deviceReference)) if (!deviceReferences.Contains(deviceReference))
{ {
@@ -1316,7 +1324,7 @@ namespace SabreTools.DatFiles
if (useSlotOptions && slotOptions.Count > 0) if (useSlotOptions && slotOptions.Count > 0)
{ {
// Loop through all names and check the corresponding machines // Loop through all names and check the corresponding machines
List<string> newSlotOptions = []; var newSlotOptions = new HashSet<string>();
foreach (string? slotOption in slotOptions) foreach (string? slotOption in slotOptions)
{ {
// If the machine doesn't exist then we continue // If the machine doesn't exist then we continue
@@ -1328,7 +1336,7 @@ namespace SabreTools.DatFiles
if (slotItems == null) if (slotItems == null)
continue; continue;
newSlotOptions.AddRange(slotItems newSlotOptions.IntersectWith(slotItems
.Where(i => i is Slot) .Where(i => i is Slot)
.Where(s => (s as Slot)!.SlotOptionsSpecified) .Where(s => (s as Slot)!.SlotOptionsSpecified)
.SelectMany(s => (s as Slot)!.GetFieldValue<SlotOption[]?>(Models.Metadata.Slot.SlotOptionKey)!) .SelectMany(s => (s as Slot)!.GetFieldValue<SlotOption[]?>(Models.Metadata.Slot.SlotOptionKey)!)
@@ -1353,7 +1361,7 @@ namespace SabreTools.DatFiles
} }
// Now that every device is accounted for, add the new list of slot options, if they don't already exist // Now that every device is accounted for, add the new list of slot options, if they don't already exist
foreach (string slotOption in newSlotOptions.Distinct()) foreach (string slotOption in newSlotOptions)
{ {
if (!slotOptions.Contains(slotOption)) if (!slotOptions.Contains(slotOption))
{ {
@@ -1377,7 +1385,9 @@ namespace SabreTools.DatFiles
/// </summary> /// </summary>
public void AddRomsFromParent() public void AddRomsFromParent()
{ {
List<string> games = [.. Keys.OrderBy(g => g)]; List<string> games = [.. Keys];
games.Sort();
foreach (string game in games) foreach (string game in games)
{ {
// If the game has no items in it, we want to continue // If the game has no items in it, we want to continue
@@ -1406,7 +1416,7 @@ namespace SabreTools.DatFiles
{ {
DatItem datItem = (DatItem)item.Clone(); DatItem datItem = (DatItem)item.Clone();
datItem.CopyMachineInformation(copyFrom); datItem.CopyMachineInformation(copyFrom);
if (!items.Any(i => string.Equals(i.GetName(), datItem.GetName(), StringComparison.OrdinalIgnoreCase)) if (items.FindIndex(i => string.Equals(i.GetName(), datItem.GetName(), StringComparison.OrdinalIgnoreCase)) == -1
&& !items.Contains(datItem)) && !items.Contains(datItem))
{ {
Add(game, datItem); Add(game, datItem);
@@ -1430,7 +1440,9 @@ namespace SabreTools.DatFiles
/// <param name="skipDedup">True to skip checking for duplicate ROMs in parent, false otherwise</param> /// <param name="skipDedup">True to skip checking for duplicate ROMs in parent, false otherwise</param>
public void AddRomsFromChildren(bool subfolder, bool skipDedup) public void AddRomsFromChildren(bool subfolder, bool skipDedup)
{ {
List<string> games = [.. Keys.OrderBy(g => g)]; List<string> games = [.. Keys];
games.Sort();
foreach (string game in games) foreach (string game in games)
{ {
// If the game has no items in it, we want to continue // If the game has no items in it, we want to continue
@@ -1552,7 +1564,9 @@ namespace SabreTools.DatFiles
/// </summary> /// </summary>
public void RemoveBiosAndDeviceSets() public void RemoveBiosAndDeviceSets()
{ {
List<string> games = [.. Keys.OrderBy(g => g)]; List<string> games = [.. Keys];
games.Sort();
foreach (string game in games) foreach (string game in games)
{ {
// If the game has no items in it, we want to continue // If the game has no items in it, we want to continue
@@ -1581,7 +1595,9 @@ namespace SabreTools.DatFiles
public void RemoveBiosRomsFromChild(bool bios) public void RemoveBiosRomsFromChild(bool bios)
{ {
// Loop through the romof tags // Loop through the romof tags
List<string> games = [.. Keys.OrderBy(g => g)]; List<string> games = [.. Keys];
games.Sort();
foreach (string game in games) foreach (string game in games)
{ {
// If the game has no items in it, we want to continue // If the game has no items in it, we want to continue
@@ -1625,7 +1641,9 @@ namespace SabreTools.DatFiles
/// </summary> /// </summary>
public void RemoveRomsFromChild() public void RemoveRomsFromChild()
{ {
List<string> games = [.. Keys.OrderBy(g => g)]; List<string> games = [.. Keys];
games.Sort();
foreach (string game in games) foreach (string game in games)
{ {
// If the game has no items in it, we want to continue // If the game has no items in it, we want to continue
@@ -1673,7 +1691,9 @@ namespace SabreTools.DatFiles
/// </summary> /// </summary>
public void RemoveTagsFromChild() public void RemoveTagsFromChild()
{ {
List<string> games = [.. Keys.OrderBy(g => g)]; List<string> games = [.. Keys];
games.Sort();
foreach (string game in games) foreach (string game in games)
{ {
// If the game has no items in it, we want to continue // If the game has no items in it, we want to continue

View File

@@ -286,7 +286,7 @@ namespace SabreTools.DatFiles
/// </summary> /// </summary>
public void ClearEmpty() public void ClearEmpty()
{ {
var keys = SortedKeys.Where(k => k != null).ToList(); var keys = Array.FindAll(SortedKeys, k => k != null);
foreach (string key in keys) foreach (string key in keys)
{ {
// If the key doesn't exist, skip // If the key doesn't exist, skip
@@ -302,7 +302,7 @@ namespace SabreTools.DatFiles
#endif #endif
// If there are no non-blank items, remove // If there are no non-blank items, remove
else if (!_buckets[key]!.Any(i => GetItem(i) != null && GetItem(i) is not Blank)) else if (!_buckets[key].Any(i => GetItem(i) != null && GetItem(i) is not Blank))
#if NET40_OR_GREATER || NETCOREAPP #if NET40_OR_GREATER || NETCOREAPP
_buckets.TryRemove(key, out _); _buckets.TryRemove(key, out _);
#else #else
@@ -724,7 +724,7 @@ namespace SabreTools.DatFiles
return false; return false;
// Try to find duplicates // Try to find duplicates
return roms.Any(r => datItem.Equals(r.Item2)); return Array.Exists(roms, r => datItem.Equals(r.Item2));
} }
/// <summary> /// <summary>
@@ -1064,9 +1064,8 @@ namespace SabreTools.DatFiles
#endif #endif
var datItems = itemIndices var datItems = itemIndices
.Where(i => _items.ContainsKey(i)) .FindAll(i => _items.ContainsKey(i))
.Select(i => (i, _items[i])) .ConvertAll(i => (i, _items[i]));
.ToList();
Sort(ref datItems, false); Sort(ref datItems, false);
@@ -1074,7 +1073,7 @@ namespace SabreTools.DatFiles
if (dedupeType == DedupeType.Full || (dedupeType == DedupeType.Game && bucketBy == ItemKey.Machine)) if (dedupeType == DedupeType.Full || (dedupeType == DedupeType.Game && bucketBy == ItemKey.Machine))
datItems = Deduplicate(datItems); datItems = Deduplicate(datItems);
_buckets[bucketKeys[i]] = datItems.Select(m => m.Item1).ToList(); _buckets[bucketKeys[i]] = datItems.ConvertAll(m => m.i);
#if NET40_OR_GREATER || NETCOREAPP #if NET40_OR_GREATER || NETCOREAPP
}); });
#else #else
@@ -1338,7 +1337,7 @@ namespace SabreTools.DatFiles
string? machine = default; string? machine = default;
foreach (string region in regionList) foreach (string region in regionList)
{ {
machine = parents[key].FirstOrDefault(m => Regex.IsMatch(m, @"\(.*" + region + @".*\)", RegexOptions.IgnoreCase)); machine = parents[key].Find(m => Regex.IsMatch(m, @"\(.*" + region + @".*\)", RegexOptions.IgnoreCase));
if (machine != default) if (machine != default)
break; break;
} }
@@ -1432,7 +1431,7 @@ namespace SabreTools.DatFiles
items[j] = item; items[j] = item;
} }
_buckets[key] = items.Select(i => i.Item1).ToList(); _buckets[key] = [.. Array.ConvertAll(items, i => i.Item1)];
#if NET40_OR_GREATER || NETCOREAPP #if NET40_OR_GREATER || NETCOREAPP
}); });
#else #else
@@ -1511,7 +1510,7 @@ namespace SabreTools.DatFiles
} }
// Set the value in the key to the new set // Set the value in the key to the new set
_buckets[bucketName] = newItems.Select(i => i.Item1).ToList(); _buckets[bucketName] = newItems.ConvertAll(i => i.Item1);
} }
/// <summary> /// <summary>
@@ -1598,7 +1597,7 @@ namespace SabreTools.DatFiles
} }
// Replace the old list of roms with the new one // Replace the old list of roms with the new one
_buckets[key] = newItems.Select(i => i.Item1).ToList(); _buckets[key] = newItems.ConvertAll(i => i.Item1);
#if NET40_OR_GREATER || NETCOREAPP #if NET40_OR_GREATER || NETCOREAPP
}); });
#else #else
@@ -1645,11 +1644,14 @@ namespace SabreTools.DatFiles
foreach ((long, DatItem) item in parentItems) foreach ((long, DatItem) item in parentItems)
{ {
DatItem datItem = (item.Item2.Clone() as DatItem)!; DatItem datItem = (item.Item2.Clone() as DatItem)!;
if (!items.Any(i => i.Item2.GetName() == datItem.GetName()) && !items.Any(i => i.Item2 == datItem)) if (Array.FindIndex(items, i => i.Item2.GetName() == datItem.GetName()) > -1
&& Array.FindIndex(items, i => i.Item2 == datItem) > -1)
{
AddItem(datItem, machine.Item1, source.Item1); AddItem(datItem, machine.Item1, source.Item1);
} }
} }
} }
}
/// <summary> /// <summary>
/// Use device_ref and optionally slotoption tags to add roms to the children /// Use device_ref and optionally slotoption tags to add roms to the children
@@ -1701,7 +1703,7 @@ namespace SabreTools.DatFiles
if (deviceReferences.Count > 0) if (deviceReferences.Count > 0)
{ {
// Loop through all names and check the corresponding machines // Loop through all names and check the corresponding machines
List<string> newDeviceReferences = []; var newDeviceReferences = new HashSet<string>();
foreach (string? deviceReference in deviceReferences) foreach (string? deviceReference in deviceReferences)
{ {
// If the device reference is invalid // If the device reference is invalid
@@ -1714,7 +1716,7 @@ namespace SabreTools.DatFiles
continue; continue;
// Add to the list of new device reference names // Add to the list of new device reference names
newDeviceReferences.AddRange(devItems newDeviceReferences.IntersectWith(devItems
.Where(i => i.Item2 is DeviceRef) .Where(i => i.Item2 is DeviceRef)
.Select(i => (i.Item2 as DeviceRef)!.GetName()!)); .Select(i => (i.Item2 as DeviceRef)!.GetName()!));
@@ -1741,7 +1743,7 @@ namespace SabreTools.DatFiles
} }
// Now that every device reference is accounted for, add the new list of device references, if they don't already exist // Now that every device reference is accounted for, add the new list of device references, if they don't already exist
foreach (string deviceReference in newDeviceReferences.Distinct()) foreach (string deviceReference in newDeviceReferences)
{ {
if (!deviceReferences.Contains(deviceReference)) if (!deviceReferences.Contains(deviceReference))
{ {
@@ -1756,7 +1758,7 @@ namespace SabreTools.DatFiles
if (useSlotOptions && slotOptions.Count > 0) if (useSlotOptions && slotOptions.Count > 0)
{ {
// Loop through all names and check the corresponding machines // Loop through all names and check the corresponding machines
List<string> newSlotOptions = []; var newSlotOptions = new HashSet<string>();
foreach (string? slotOption in slotOptions) foreach (string? slotOption in slotOptions)
{ {
// If the slot option is invalid // If the slot option is invalid
@@ -1769,7 +1771,7 @@ namespace SabreTools.DatFiles
continue; continue;
// Add to the list of new slot option names // Add to the list of new slot option names
newSlotOptions.AddRange(slotItems newSlotOptions.IntersectWith(slotItems
.Where(i => i.Item2 is Slot) .Where(i => i.Item2 is Slot)
.Where(s => (s.Item2 as Slot)!.SlotOptionsSpecified) .Where(s => (s.Item2 as Slot)!.SlotOptionsSpecified)
.SelectMany(s => (s.Item2 as Slot)!.GetFieldValue<SlotOption[]?>(Models.Metadata.Slot.SlotOptionKey)!) .SelectMany(s => (s.Item2 as Slot)!.GetFieldValue<SlotOption[]?>(Models.Metadata.Slot.SlotOptionKey)!)
@@ -1798,7 +1800,7 @@ namespace SabreTools.DatFiles
} }
// Now that every device is accounted for, add the new list of slot options, if they don't already exist // Now that every device is accounted for, add the new list of slot options, if they don't already exist
foreach (string slotOption in newSlotOptions.Distinct()) foreach (string slotOption in newSlotOptions)
{ {
if (!slotOptions.Contains(slotOption)) if (!slotOptions.Contains(slotOption))
{ {
@@ -1852,8 +1854,8 @@ namespace SabreTools.DatFiles
foreach (var item in parentItems) foreach (var item in parentItems)
{ {
DatItem datItem = (DatItem)item.Item2.Clone(); DatItem datItem = (DatItem)item.Item2.Clone();
if (!items.Any(i => i.Item2.GetName()?.ToLowerInvariant() == datItem.GetName()?.ToLowerInvariant()) if (Array.FindIndex(items, i => i.Item2.GetName()?.ToLowerInvariant() == datItem.GetName()?.ToLowerInvariant()) > -1
&& !items.Any(i => i.Item2 == datItem)) && Array.FindIndex(items, i => i.Item2 == datItem) > -1)
{ {
AddItem(datItem, machine.Item1, source.Item1); AddItem(datItem, machine.Item1, source.Item1);
} }
@@ -1913,6 +1915,9 @@ namespace SabreTools.DatFiles
{ {
// Get the parent items and current machine name // Get the parent items and current machine name
var parentItems = GetItemsForBucket(cloneOf!); var parentItems = GetItemsForBucket(cloneOf!);
if (parentItems == null)
continue;
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
@@ -1921,7 +1926,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 && parentItems! if (mergeTag != null && parentItems
.Where(i => i.Item2 is Disk) .Where(i => i.Item2 is Disk)
.Select(i => (i.Item2 as Disk)!.GetName()) .Select(i => (i.Item2 as Disk)!.GetName())
.Contains(mergeTag)) .Contains(mergeTag))
@@ -1930,7 +1935,7 @@ 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 && !parentItems! else if (mergeTag != null && !parentItems
.Where(i => i.Item2 is Disk) .Where(i => i.Item2 is Disk)
.Select(i => (i.Item2 as Disk)!.GetName()) .Select(i => (i.Item2 as Disk)!.GetName())
.Contains(mergeTag)) .Contains(mergeTag))
@@ -1951,7 +1956,7 @@ namespace SabreTools.DatFiles
else if (item.Item2 is Rom rom) else if (item.Item2 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 && parentItems! if (rom.GetStringFieldValue(Models.Metadata.Rom.MergeKey) != null && parentItems
.Where(i => i.Item2 is Rom) .Where(i => i.Item2 is Rom)
.Select(i => (i.Item2 as Rom)!.GetName()) .Select(i => (i.Item2 as Rom)!.GetName())
.Contains(rom.GetStringFieldValue(Models.Metadata.Rom.MergeKey))) .Contains(rom.GetStringFieldValue(Models.Metadata.Rom.MergeKey)))
@@ -1960,7 +1965,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 && !parentItems! else if (rom.GetStringFieldValue(Models.Metadata.Rom.MergeKey) != null && !parentItems
.Where(i => i.Item2 is Rom) .Where(i => i.Item2 is Rom)
.Select(i => (i.Item2 as Rom)!.GetName()) .Select(i => (i.Item2 as Rom)!.GetName())
.Contains(rom.GetStringFieldValue(Models.Metadata.Rom.MergeKey))) .Contains(rom.GetStringFieldValue(Models.Metadata.Rom.MergeKey)))
@@ -1973,7 +1978,7 @@ namespace SabreTools.DatFiles
} }
// 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 (!parentItems!.Contains(item) || skipDedup) else if (Array.IndexOf(parentItems, item) == -1 || skipDedup)
{ {
if (subfolder) if (subfolder)
rom.SetName($"{machineName}\\{rom.GetName()}"); rom.SetName($"{machineName}\\{rom.GetName()}");
@@ -1984,7 +1989,7 @@ namespace SabreTools.DatFiles
} }
// All other that would be missing to subfolder of parent // All other that would be missing to subfolder of parent
else if (!parentItems!.Contains(item)) else if (Array.IndexOf(parentItems, item) == -1)
{ {
if (subfolder) if (subfolder)
item.Item2.SetName($"{machineName}\\{item.Item2.GetName()}"); item.Item2.SetName($"{machineName}\\{item.Item2.GetName()}");
@@ -2070,8 +2075,8 @@ namespace SabreTools.DatFiles
// 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
foreach ((long, DatItem) item in parentItems) foreach ((long, DatItem) item in parentItems)
{ {
var matchedIndices = items.Where(i => i.Item2 == item.Item2).Select(i => i.Item1); var matchedItems = Array.FindAll(items, i => i.Item2 == item.Item2);
foreach (long index in matchedIndices) foreach ((long index, _) in matchedItems)
{ {
RemoveItem(index); RemoveItem(index);
} }
@@ -2110,8 +2115,8 @@ namespace SabreTools.DatFiles
// 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
foreach ((long, DatItem) item in parentItems) foreach ((long, DatItem) item in parentItems)
{ {
var matchedIndices = items.Where(i => i.Item2 == item.Item2).Select(i => i.Item1); var matchedItems = Array.FindAll(items, i => i.Item2 == item.Item2);
foreach (long index in matchedIndices) foreach ((long index, _) in matchedItems)
{ {
RemoveItem(index); RemoveItem(index);
} }

View File

@@ -1,7 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using System.Reflection; using System.Reflection;
using System.Xml.Serialization; using System.Xml.Serialization;
using Newtonsoft.Json; using Newtonsoft.Json;
@@ -1022,9 +1021,8 @@ namespace SabreTools.DatItems
/// <returns>Clone of the DatItem</returns> /// <returns>Clone of the DatItem</returns>
public override object Clone() public override object Clone()
{ {
var concrete = Assembly.GetExecutingAssembly() var concrete = Array.Find(Assembly.GetExecutingAssembly().GetTypes(),
.GetTypes() t => !t.IsAbstract && t.IsClass && t.BaseType == typeof(DatItem<T>));
.FirstOrDefault(t => !t.IsAbstract && t.IsClass && t.BaseType == typeof(DatItem<T>));
var clone = Activator.CreateInstance(concrete!); var clone = Activator.CreateInstance(concrete!);
(clone as DatItem<T>)!._internal = _internal?.Clone() as T ?? Activator.CreateInstance<T>(); (clone as DatItem<T>)!._internal = _internal?.Clone() as T ?? Activator.CreateInstance<T>();

View File

@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@@ -532,7 +533,7 @@ namespace SabreTools.DatTools
/// <param name="inputs">List of inputs to write out from</param> /// <param name="inputs">List of inputs to write out from</param>
public static DatFile DiffDuplicates(DatFile datFile, List<string> inputs) public static DatFile DiffDuplicates(DatFile datFile, List<string> inputs)
{ {
List<ParentablePath> paths = inputs.Select(i => new ParentablePath(i)).ToList(); List<ParentablePath> paths = inputs.ConvertAll(i => new ParentablePath(i));
return DiffDuplicates(datFile, paths); return DiffDuplicates(datFile, paths);
//return DiffDuplicatesDB(datFile, paths); //return DiffDuplicatesDB(datFile, paths);
} }
@@ -737,7 +738,7 @@ namespace SabreTools.DatTools
/// <param name="inputs">List of inputs to write out from</param> /// <param name="inputs">List of inputs to write out from</param>
public static List<DatFile> DiffIndividuals(DatFile datFile, List<string> inputs) public static List<DatFile> DiffIndividuals(DatFile datFile, List<string> inputs)
{ {
List<ParentablePath> paths = inputs.Select(i => new ParentablePath(i)).ToList(); List<ParentablePath> paths = inputs.ConvertAll(i => new ParentablePath(i));
return DiffIndividuals(datFile, paths); return DiffIndividuals(datFile, paths);
//return DiffIndividualsDB(datFile, paths); //return DiffIndividualsDB(datFile, paths);
} }
@@ -967,7 +968,7 @@ namespace SabreTools.DatTools
/// <param name="inputs">List of inputs to write out from</param> /// <param name="inputs">List of inputs to write out from</param>
public static DatFile DiffNoDuplicates(DatFile datFile, List<string> inputs) public static DatFile DiffNoDuplicates(DatFile datFile, List<string> inputs)
{ {
List<ParentablePath> paths = inputs.Select(i => new ParentablePath(i)).ToList(); List<ParentablePath> paths = inputs.ConvertAll(i => new ParentablePath(i));
return DiffNoDuplicates(datFile, paths); return DiffNoDuplicates(datFile, paths);
//return DiffNoDuplicatesDB(datFile, paths); //return DiffNoDuplicatesDB(datFile, paths);
} }
@@ -1171,7 +1172,7 @@ namespace SabreTools.DatTools
/// <returns>List of DatHeader objects representing headers</returns> /// <returns>List of DatHeader objects representing headers</returns>
public static List<DatHeader> PopulateUserData(DatFile datFile, List<string> inputs) public static List<DatHeader> PopulateUserData(DatFile datFile, List<string> inputs)
{ {
List<ParentablePath> paths = inputs.Select(i => new ParentablePath(i)).ToList(); List<ParentablePath> paths = inputs.ConvertAll(i => new ParentablePath(i));
return PopulateUserData(datFile, paths); return PopulateUserData(datFile, paths);
} }
@@ -1216,7 +1217,7 @@ namespace SabreTools.DatTools
watch.Stop(); watch.Stop();
return [.. datFiles.Select(d => d.Header)]; return [.. Array.ConvertAll(datFiles, d => d.Header)];
} }
/// <summary> /// <summary>

View File

@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@@ -46,10 +47,10 @@ namespace SabreTools.DatTools
InternalStopwatch watch = new($"Splitting DAT by extension"); InternalStopwatch watch = new($"Splitting DAT by extension");
// Make sure all of the extensions don't have a dot at the beginning // Make sure all of the extensions don't have a dot at the beginning
var newExtA = extA.Select(s => s.TrimStart('.').ToLowerInvariant()).ToArray(); var newExtA = extA.ConvertAll(s => s.TrimStart('.').ToLowerInvariant()).ToArray();
string newExtAString = string.Join(",", newExtA); string newExtAString = string.Join(",", newExtA);
var newExtB = extB.Select(s => s.TrimStart('.').ToLowerInvariant()).ToArray(); var newExtB = extB.ConvertAll(s => s.TrimStart('.').ToLowerInvariant()).ToArray();
string newExtBString = string.Join(",", newExtB); string newExtBString = string.Join(",", newExtB);
// Set all of the appropriate outputs for each of the subsets // Set all of the appropriate outputs for each of the subsets
@@ -82,11 +83,11 @@ namespace SabreTools.DatTools
foreach (DatItem item in items) foreach (DatItem item in items)
{ {
if (newExtA.Contains((item.GetName() ?? string.Empty).GetNormalizedExtension())) if (Array.IndexOf(newExtA, (item.GetName() ?? string.Empty).GetNormalizedExtension()) > -1)
{ {
extADat.Items.Add(key, item); extADat.Items.Add(key, item);
} }
else if (newExtB.Contains((item.GetName() ?? string.Empty).GetNormalizedExtension())) if (Array.IndexOf(newExtB, (item.GetName() ?? string.Empty).GetNormalizedExtension()) > -1)
{ {
extBDat.Items.Add(key, item); extBDat.Items.Add(key, item);
} }
@@ -123,10 +124,10 @@ namespace SabreTools.DatTools
InternalStopwatch watch = new($"Splitting DAT by extension"); InternalStopwatch watch = new($"Splitting DAT by extension");
// Make sure all of the extensions don't have a dot at the beginning // Make sure all of the extensions don't have a dot at the beginning
var newExtA = extA.Select(s => s.TrimStart('.').ToLowerInvariant()).ToArray(); var newExtA = extA.ConvertAll(s => s.TrimStart('.').ToLowerInvariant()).ToArray();
string newExtAString = string.Join(",", newExtA); string newExtAString = string.Join(",", newExtA);
var newExtB = extB.Select(s => s.TrimStart('.').ToLowerInvariant()).ToArray(); var newExtB = extB.ConvertAll(s => s.TrimStart('.').ToLowerInvariant()).ToArray();
string newExtBString = string.Join(",", newExtB); string newExtBString = string.Join(",", newExtB);
// Set all of the appropriate outputs for each of the subsets // Set all of the appropriate outputs for each of the subsets

View File

@@ -1,7 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using SabreTools.Core.Tools; using SabreTools.Core.Tools;
using SabreTools.FileTypes.Archives; using SabreTools.FileTypes.Archives;
using SabreTools.IO; using SabreTools.IO;
@@ -181,7 +180,7 @@ namespace SabreTools.FileTypes
List<string> files = PathTool.GetFilesOrdered(Filename); List<string> files = PathTool.GetFilesOrdered(Filename);
// Now sort through to find the first file that matches // Now sort through to find the first file that matches
string? match = files.Where(s => s.EndsWith(entryName)).FirstOrDefault(); string? match = files.Find(s => s.EndsWith(entryName));
// If we had a file, copy that over to the new name // If we had a file, copy that over to the new name
if (!string.IsNullOrEmpty(match)) if (!string.IsNullOrEmpty(match))
@@ -220,7 +219,7 @@ namespace SabreTools.FileTypes
List<string> files = PathTool.GetFilesOrdered(Filename); List<string> files = PathTool.GetFilesOrdered(Filename);
// Now sort through to find the first file that matches // Now sort through to find the first file that matches
string? match = files.Where(s => s.EndsWith(entryName)).FirstOrDefault(); string? match = files.Find(s => s.EndsWith(entryName));
// If we had a file, open and return the stream // If we had a file, open and return the stream
if (!string.IsNullOrEmpty(match)) if (!string.IsNullOrEmpty(match))

View File

@@ -116,7 +116,7 @@ namespace SabreTools.Help
/// <returns>True if the flag was found, false otherwise</returns> /// <returns>True if the flag was found, false otherwise</returns>
public bool ContainsFlag(string name) public bool ContainsFlag(string name)
{ {
return Flags.Any(f => f == name || f.TrimStart('-') == name); return Flags.Exists(f => f == name || f.TrimStart('-') == name);
} }
/// <summary> /// <summary>
@@ -126,7 +126,7 @@ namespace SabreTools.Help
/// <returns>True if the flag was found, false otherwise</returns> /// <returns>True if the flag was found, false otherwise</returns>
public bool StartsWith(char c) public bool StartsWith(char c)
{ {
return Flags.Any(f => f.TrimStart('-').ToLowerInvariant()[0] == c); return Flags.Exists(f => f.TrimStart('-').ToLowerInvariant()[0] == c);
} }
#endregion #endregion
@@ -202,7 +202,7 @@ namespace SabreTools.Help
for (int i = 0; i < split.Length; i++) for (int i = 0; i < split.Length; i++)
{ {
// If we have a newline character, reset the line and continue // If we have a newline character, reset the line and continue
if (split[i].Contains('\n')) if (split[i].Contains("\n"))
{ {
string[] subsplit = split[i].Replace("\r", string.Empty).Split('\n'); string[] subsplit = split[i].Replace("\r", string.Empty).Split('\n');
for (int j = 0; j < subsplit.Length - 1; j++) for (int j = 0; j < subsplit.Length - 1; j++)
@@ -339,7 +339,7 @@ namespace SabreTools.Help
for (int i = 0; i < split.Length; i++) for (int i = 0; i < split.Length; i++)
{ {
// If we have a newline character, reset the line and continue // If we have a newline character, reset the line and continue
if (split[i].Contains('\n')) if (split[i].Contains("\n"))
{ {
string[] subsplit = split[i].Replace("\r", string.Empty).Split('\n'); string[] subsplit = split[i].Replace("\r", string.Empty).Split('\n');
for (int j = 0; j < subsplit.Length - 1; j++) for (int j = 0; j < subsplit.Length - 1; j++)
@@ -404,12 +404,15 @@ namespace SabreTools.Help
{ {
bool valid = false; bool valid = false;
// Pre-split the input for efficiency
string[] splitInput = input.Split('=');
// Determine what we should be looking for // Determine what we should be looking for
switch (_featureType) switch (_featureType)
{ {
// If we have a flag, make sure it doesn't have an equal sign in it // If we have a flag, make sure it doesn't have an equal sign in it
case ParameterType.Flag: case ParameterType.Flag:
valid = !input.Contains('=') && Flags.Contains(input); valid = !input.Contains("=") && Flags.Contains(input);
if (valid) if (valid)
{ {
_value = true; _value = true;
@@ -425,10 +428,10 @@ namespace SabreTools.Help
// If we have an Int32, try to parse it if at all possible // If we have an Int32, try to parse it if at all possible
case ParameterType.Int32: case ParameterType.Int32:
valid = input.Contains('=') && Flags.Contains(input.Split('=')[0]); valid = input.Contains("=") && Flags.Contains(splitInput[0]);
if (valid) if (valid)
{ {
if (!Int32.TryParse(input.Split('=')[1], out int value)) if (!Int32.TryParse(splitInput[1], out int value))
value = Int32.MinValue; value = Int32.MinValue;
_value = value; _value = value;
@@ -444,10 +447,10 @@ namespace SabreTools.Help
// If we have an Int32, try to parse it if at all possible // If we have an Int32, try to parse it if at all possible
case ParameterType.Int64: case ParameterType.Int64:
valid = input.Contains('=') && Flags.Contains(input.Split('=')[0]); valid = input.Contains("=") && Flags.Contains(splitInput[0]);
if (valid) if (valid)
{ {
if (!Int64.TryParse(input.Split('=')[1], out long value)) if (!Int64.TryParse(splitInput[1], out long value))
value = Int64.MinValue; value = Int64.MinValue;
_value = value; _value = value;
@@ -463,20 +466,20 @@ namespace SabreTools.Help
// If we have an input, make sure it has an equals sign in it // If we have an input, make sure it has an equals sign in it
case ParameterType.List: case ParameterType.List:
valid = input.Contains('=') && Flags.Contains(input.Split('=')[0]); valid = input.Contains("=") && Flags.Contains(splitInput[0]);
if (valid) if (valid)
{ {
_value ??= new List<string>(); _value ??= new List<string>();
(_value as List<string>)?.Add(string.Join("=", input.Split('=').Skip(1).ToArray())); (_value as List<string>)?.Add(string.Join("=", splitInput, 1, splitInput.Length - 1));
} }
break; break;
case ParameterType.String: case ParameterType.String:
valid = input.Contains('=') && Flags.Contains(input.Split('=')[0]); valid = input.Contains("=") && Flags.Contains(input.Split('=')[0]);
if (valid) if (valid)
{ {
_value = string.Join("=", input.Split('=').Skip(1).ToArray()); _value = string.Join("=", splitInput, 1, splitInput.Length - 1);
// If we've already found this feature before // If we've already found this feature before
if (_foundOnce && !ignore) if (_foundOnce && !ignore)

View File

@@ -1,6 +1,5 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
namespace SabreTools.Help namespace SabreTools.Help
{ {
@@ -84,7 +83,20 @@ namespace SabreTools.Help
/// <returns>Feature name</returns> /// <returns>Feature name</returns>
public string GetFeatureName(string name) public string GetFeatureName(string name)
{ {
return _features.Keys.FirstOrDefault(f => _features[f]?.ValidateInput(name, exact: true, ignore: true) ?? false) ?? string.Empty; foreach (var key in _features.Keys)
{
// Skip invalid features
var feature = _features[key];
if (feature == null)
continue;
// If validation passes
if (feature.ValidateInput(name, exact: true, ignore: true))
return key;
}
// No feature could be found
return string.Empty;
} }
/// <summary> /// <summary>
@@ -236,7 +248,20 @@ namespace SabreTools.Help
/// <returns>True if the feature was found, false otherwise</returns> /// <returns>True if the feature was found, false otherwise</returns>
public bool TopLevelFlag(string flag) public bool TopLevelFlag(string flag)
{ {
return _features.Keys.Any(f => _features[f]?.ValidateInput(flag, exact: true) ?? false); foreach (var key in _features.Keys)
{
// Skip invalid features
var feature = _features[key];
if (feature == null)
continue;
// If validation passes
if (feature.ValidateInput(flag, exact: true))
return true;
}
// No feature could be found
return false;
} }
/// <summary> /// <summary>

View File

@@ -1,7 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using SabreTools.Core.Filter; using SabreTools.Core.Filter;
using SabreTools.Core.Tools; using SabreTools.Core.Tools;
@@ -118,7 +117,7 @@ Reset the internal state: reset();";
} }
// Now run the command // Now run the command
logger.User($"Attempting to invoke {command.Name} with {(command.Arguments.Count == 0 ? "no arguments" : "the following argument(s): " + string.Join(", ", command.Arguments))}"); logger.User($"Attempting to invoke {command.Name} with {(command.Arguments.Length == 0 ? "no arguments" : "the following argument(s): " + string.Join(", ", command.Arguments))}");
command.Process(batchState); command.Process(batchState);
} }
} }
@@ -136,14 +135,14 @@ Reset the internal state: reset();";
private abstract class BatchCommand private abstract class BatchCommand
{ {
public string? Name { get; set; } public string? Name { get; set; }
public List<string> Arguments { get; private set; } = []; public string[] Arguments { get; private set; } = [];
/// <summary> /// <summary>
/// Default constructor for setting arguments /// Default constructor for setting arguments
/// </summary> /// </summary>
public BatchCommand(List<string> arguments) public BatchCommand(string[] args)
{ {
Arguments = arguments; Arguments = args;
} }
/// <summary> /// <summary>
@@ -166,13 +165,8 @@ Reset the internal state: reset();";
// Otherwise, get the name and arguments // Otherwise, get the name and arguments
string commandName = match.Groups[1].Value; string commandName = match.Groups[1].Value;
List<string> arguments = match string[] arguments = match.Groups[2].Value.Split(',');
.Groups[2] arguments = Array.ConvertAll(arguments, s => s.Trim().Trim('"').Trim());
.Value
.Split(',')
.Select(s => s.Trim().Trim('"').Trim())
.Where(s => !string.IsNullOrWhiteSpace(s)) // TODO: This may interfere with header value replacement
.ToList();
return commandName.ToLowerInvariant() switch return commandName.ToLowerInvariant() switch
{ {
@@ -219,7 +213,7 @@ Reset the internal state: reset();";
private class DescriptionAsNameCommand : BatchCommand private class DescriptionAsNameCommand : BatchCommand
{ {
/// <inheritdoc/> /// <inheritdoc/>
public DescriptionAsNameCommand(List<string> arguments) : base(arguments) { } public DescriptionAsNameCommand(string[] args) : base(args) { }
/// <inheritdoc/> /// <inheritdoc/>
public override string Usage() public override string Usage()
@@ -230,9 +224,9 @@ Reset the internal state: reset();";
/// <inheritdoc/> /// <inheritdoc/>
public override (bool, string?) ValidateArguments() public override (bool, string?) ValidateArguments()
{ {
if (Arguments.Count != 0) if (Arguments.Length != 0)
{ {
string message = $"Invoked {Name} and expected no arguments, but {Arguments.Count} arguments were provided"; string message = $"Invoked {Name} and expected no arguments, but {Arguments.Length} arguments were provided";
return (false, message); return (false, message);
} }
@@ -253,7 +247,7 @@ Reset the internal state: reset();";
private class DFDCommand : BatchCommand private class DFDCommand : BatchCommand
{ {
/// <inheritdoc/> /// <inheritdoc/>
public DFDCommand(List<string> arguments) : base(arguments) { } public DFDCommand(string[] args) : base(args) { }
/// <inheritdoc/> /// <inheritdoc/>
public override string Usage() public override string Usage()
@@ -264,7 +258,7 @@ Reset the internal state: reset();";
/// <inheritdoc/> /// <inheritdoc/>
public override (bool, string?) ValidateArguments() public override (bool, string?) ValidateArguments()
{ {
if (Arguments.Count == 0) if (Arguments.Length == 0)
{ {
string message = $"Invoked {Name} but no arguments were provided"; string message = $"Invoked {Name} but no arguments were provided";
return (false, message); return (false, message);
@@ -299,7 +293,7 @@ Reset the internal state: reset();";
private class ExtraCommand : BatchCommand private class ExtraCommand : BatchCommand
{ {
/// <inheritdoc/> /// <inheritdoc/>
public ExtraCommand(List<string> arguments) : base(arguments) { } public ExtraCommand(string[] args) : base(args) { }
/// <inheritdoc/> /// <inheritdoc/>
public override string Usage() public override string Usage()
@@ -310,9 +304,9 @@ Reset the internal state: reset();";
/// <inheritdoc/> /// <inheritdoc/>
public override (bool, string?) ValidateArguments() public override (bool, string?) ValidateArguments()
{ {
if (Arguments.Count != 2) if (Arguments.Length != 2)
{ {
string message = $"Invoked {Name} and expected 2 arguments, but {Arguments.Count} arguments were provided"; string message = $"Invoked {Name} and expected 2 arguments, but {Arguments.Length} arguments were provided";
return (false, message); return (false, message);
} }
@@ -363,7 +357,7 @@ Reset the internal state: reset();";
private class FilterCommand : BatchCommand private class FilterCommand : BatchCommand
{ {
/// <inheritdoc/> /// <inheritdoc/>
public FilterCommand(List<string> arguments) : base(arguments) { } public FilterCommand(string[] args) : base(args) { }
/// <inheritdoc/> /// <inheritdoc/>
public override string Usage() public override string Usage()
@@ -374,19 +368,19 @@ Reset the internal state: reset();";
/// <inheritdoc/> /// <inheritdoc/>
public override (bool, string?) ValidateArguments() public override (bool, string?) ValidateArguments()
{ {
if (Arguments.Count < 2 || Arguments.Count > 4) if (Arguments.Length < 2 || Arguments.Length > 4)
{ {
string message = $"Invoked {Name} and expected between 2-4 arguments, but {Arguments.Count} arguments were provided"; string message = $"Invoked {Name} and expected between 2-4 arguments, but {Arguments.Length} arguments were provided";
return (false, message); return (false, message);
} }
// Read in the individual arguments // Read in the individual arguments
string itemFieldString = Arguments[0]; string itemFieldString = Arguments[0];
bool? filterRemove = false; bool? filterRemove = false;
if (Arguments.Count >= 3) if (Arguments.Length >= 3)
filterRemove = Arguments[2].AsYesNo(); filterRemove = Arguments[2].AsYesNo();
bool? filterPerMachine = false; bool? filterPerMachine = false;
if (Arguments.Count >= 4) if (Arguments.Length >= 4)
filterPerMachine = Arguments[3].AsYesNo(); filterPerMachine = Arguments[3].AsYesNo();
// If we had an invalid input, log and continue // If we had an invalid input, log and continue
@@ -420,11 +414,11 @@ Reset the internal state: reset();";
string filterField = Arguments[0]; string filterField = Arguments[0];
string filterValue = Arguments[1]; string filterValue = Arguments[1];
bool? filterRemove = false; bool? filterRemove = false;
if (Arguments.Count >= 3) if (Arguments.Length >= 3)
filterRemove = Arguments[2].AsYesNo(); filterRemove = Arguments[2].AsYesNo();
// TODO: Add back this functionality // TODO: Add back this functionality
bool? filterPerMachine = false; bool? filterPerMachine = false;
if (Arguments.Count >= 4) if (Arguments.Length >= 4)
filterPerMachine = Arguments[3].AsYesNo(); filterPerMachine = Arguments[3].AsYesNo();
// Build the filter statement // Build the filter statement
@@ -451,7 +445,7 @@ Reset the internal state: reset();";
private class FormatCommand : BatchCommand private class FormatCommand : BatchCommand
{ {
/// <inheritdoc/> /// <inheritdoc/>
public FormatCommand(List<string> arguments) : base(arguments) { } public FormatCommand(string[] args) : base(args) { }
/// <inheritdoc/> /// <inheritdoc/>
public override string Usage() public override string Usage()
@@ -462,7 +456,7 @@ Reset the internal state: reset();";
/// <inheritdoc/> /// <inheritdoc/>
public override (bool, string?) ValidateArguments() public override (bool, string?) ValidateArguments()
{ {
if (Arguments.Count == 0) if (Arguments.Length == 0)
{ {
string message = $"Invoked {Name} but no arguments were provided"; string message = $"Invoked {Name} but no arguments were provided";
return (false, message); return (false, message);
@@ -505,7 +499,7 @@ Reset the internal state: reset();";
private class InputCommand : BatchCommand private class InputCommand : BatchCommand
{ {
/// <inheritdoc/> /// <inheritdoc/>
public InputCommand(List<string> arguments) : base(arguments) { } public InputCommand(string[] args) : base(args) { }
/// <inheritdoc/> /// <inheritdoc/>
public override string Usage() public override string Usage()
@@ -516,7 +510,7 @@ Reset the internal state: reset();";
/// <inheritdoc/> /// <inheritdoc/>
public override (bool, string?) ValidateArguments() public override (bool, string?) ValidateArguments()
{ {
if (Arguments.Count == 0) if (Arguments.Length == 0)
{ {
string message = $"Invoked {Name} but no arguments were provided"; string message = $"Invoked {Name} but no arguments were provided";
return (false, message); return (false, message);
@@ -529,7 +523,7 @@ Reset the internal state: reset();";
public override void Process(BatchState batchState) public override void Process(BatchState batchState)
{ {
// Get only files from inputs // Get only files from inputs
List<ParentablePath> datFilePaths = PathTool.GetFilesOnly(Arguments); List<ParentablePath> datFilePaths = PathTool.GetFilesOnly([.. Arguments]);
// Assume there could be multiple // Assume there could be multiple
foreach (ParentablePath datFilePath in datFilePaths) foreach (ParentablePath datFilePath in datFilePaths)
@@ -545,7 +539,7 @@ Reset the internal state: reset();";
private class MergeCommand : BatchCommand private class MergeCommand : BatchCommand
{ {
/// <inheritdoc/> /// <inheritdoc/>
public MergeCommand(List<string> arguments) : base(arguments) { } public MergeCommand(string[] args) : base(args) { }
/// <inheritdoc/> /// <inheritdoc/>
public override string Usage() public override string Usage()
@@ -556,9 +550,9 @@ Reset the internal state: reset();";
/// <inheritdoc/> /// <inheritdoc/>
public override (bool, string?) ValidateArguments() public override (bool, string?) ValidateArguments()
{ {
if (Arguments.Count != 1) if (Arguments.Length != 1)
{ {
string message = $"Invoked {Name} and expected 1 argument, but {Arguments.Count} arguments were provided"; string message = $"Invoked {Name} and expected 1 argument, but {Arguments.Length} arguments were provided";
return (false, message); return (false, message);
} }
@@ -593,7 +587,7 @@ Reset the internal state: reset();";
private class OneGamePerRegionCommand : BatchCommand private class OneGamePerRegionCommand : BatchCommand
{ {
/// <inheritdoc/> /// <inheritdoc/>
public OneGamePerRegionCommand(List<string> arguments) : base(arguments) { } public OneGamePerRegionCommand(string[] args) : base(args) { }
/// <inheritdoc/> /// <inheritdoc/>
public override string Usage() public override string Usage()
@@ -604,7 +598,7 @@ Reset the internal state: reset();";
/// <inheritdoc/> /// <inheritdoc/>
public override (bool, string?) ValidateArguments() public override (bool, string?) ValidateArguments()
{ {
if (Arguments.Count == 0) if (Arguments.Length == 0)
{ {
string message = $"Invoked {Name} but no arguments were provided"; string message = $"Invoked {Name} but no arguments were provided";
return (false, message); return (false, message);
@@ -616,8 +610,8 @@ Reset the internal state: reset();";
/// <inheritdoc/> /// <inheritdoc/>
public override void Process(BatchState batchState) public override void Process(BatchState batchState)
{ {
batchState.DatFile.Items.SetOneGamePerRegion(Arguments); batchState.DatFile.Items.SetOneGamePerRegion([.. Arguments]);
batchState.DatFile.ItemsDB.SetOneGamePerRegion(Arguments); batchState.DatFile.ItemsDB.SetOneGamePerRegion([.. Arguments]);
} }
} }
@@ -627,7 +621,7 @@ Reset the internal state: reset();";
private class OneRomPerGameCommand : BatchCommand private class OneRomPerGameCommand : BatchCommand
{ {
/// <inheritdoc/> /// <inheritdoc/>
public OneRomPerGameCommand(List<string> arguments) : base(arguments) { } public OneRomPerGameCommand(string[] args) : base(args) { }
/// <inheritdoc/> /// <inheritdoc/>
public override string Usage() public override string Usage()
@@ -638,9 +632,9 @@ Reset the internal state: reset();";
/// <inheritdoc/> /// <inheritdoc/>
public override (bool, string?) ValidateArguments() public override (bool, string?) ValidateArguments()
{ {
if (Arguments.Count == 0) if (Arguments.Length == 0)
{ {
string message = $"Invoked {Name} and expected no arguments, but {Arguments.Count} arguments were provided"; string message = $"Invoked {Name} and expected no arguments, but {Arguments.Length} arguments were provided";
return (false, message); return (false, message);
} }
@@ -661,7 +655,7 @@ Reset the internal state: reset();";
private class OutputCommand : BatchCommand private class OutputCommand : BatchCommand
{ {
/// <inheritdoc/> /// <inheritdoc/>
public OutputCommand(List<string> arguments) : base(arguments) { } public OutputCommand(string[] args) : base(args) { }
/// <inheritdoc/> /// <inheritdoc/>
public override string Usage() public override string Usage()
@@ -672,9 +666,9 @@ Reset the internal state: reset();";
/// <inheritdoc/> /// <inheritdoc/>
public override (bool, string?) ValidateArguments() public override (bool, string?) ValidateArguments()
{ {
if (Arguments.Count != 1) if (Arguments.Length != 1)
{ {
string message = $"Invoked {Name} and expected exactly 1 argument, but {Arguments.Count} arguments were provided"; string message = $"Invoked {Name} and expected exactly 1 argument, but {Arguments.Length} arguments were provided";
return (false, message); return (false, message);
} }
@@ -694,7 +688,7 @@ Reset the internal state: reset();";
private class RemoveCommand : BatchCommand private class RemoveCommand : BatchCommand
{ {
/// <inheritdoc/> /// <inheritdoc/>
public RemoveCommand(List<string> arguments) : base(arguments) { } public RemoveCommand(string[] args) : base(args) { }
/// <inheritdoc/> /// <inheritdoc/>
public override string Usage() public override string Usage()
@@ -705,7 +699,7 @@ Reset the internal state: reset();";
/// <inheritdoc/> /// <inheritdoc/>
public override (bool, string?) ValidateArguments() public override (bool, string?) ValidateArguments()
{ {
if (Arguments.Count == 0) if (Arguments.Length == 0)
{ {
string message = $"Invoked {Name} but no arguments were provided"; string message = $"Invoked {Name} but no arguments were provided";
return (false, message); return (false, message);
@@ -718,7 +712,7 @@ Reset the internal state: reset();";
public override void Process(BatchState batchState) public override void Process(BatchState batchState)
{ {
var remover = new Remover(); var remover = new Remover();
remover.PopulateExclusionsFromList(Arguments); remover.PopulateExclusionsFromList([.. Arguments]);
remover.ApplyRemovals(batchState.DatFile); remover.ApplyRemovals(batchState.DatFile);
} }
} }
@@ -729,7 +723,7 @@ Reset the internal state: reset();";
private class ResetCommand : BatchCommand private class ResetCommand : BatchCommand
{ {
/// <inheritdoc/> /// <inheritdoc/>
public ResetCommand(List<string> arguments) : base(arguments) { } public ResetCommand(string[] args) : base(args) { }
/// <inheritdoc/> /// <inheritdoc/>
public override string Usage() public override string Usage()
@@ -740,9 +734,9 @@ Reset the internal state: reset();";
/// <inheritdoc/> /// <inheritdoc/>
public override (bool, string?) ValidateArguments() public override (bool, string?) ValidateArguments()
{ {
if (Arguments.Count != 0) if (Arguments.Length != 0)
{ {
string message = $"Invoked {Name} and expected no arguments, but {Arguments.Count} arguments were provided"; string message = $"Invoked {Name} and expected no arguments, but {Arguments.Length} arguments were provided";
return (false, message); return (false, message);
} }
@@ -762,7 +756,7 @@ Reset the internal state: reset();";
private class SceneDateStripCommand : BatchCommand private class SceneDateStripCommand : BatchCommand
{ {
/// <inheritdoc/> /// <inheritdoc/>
public SceneDateStripCommand(List<string> arguments) : base(arguments) { } public SceneDateStripCommand(string[] args) : base(args) { }
/// <inheritdoc/> /// <inheritdoc/>
public override string Usage() public override string Usage()
@@ -773,9 +767,9 @@ Reset the internal state: reset();";
/// <inheritdoc/> /// <inheritdoc/>
public override (bool, string?) ValidateArguments() public override (bool, string?) ValidateArguments()
{ {
if (Arguments.Count != 0) if (Arguments.Length != 0)
{ {
string message = $"Invoked {Name} and expected no arguments, but {Arguments.Count} arguments were provided"; string message = $"Invoked {Name} and expected no arguments, but {Arguments.Length} arguments were provided";
return (false, message); return (false, message);
} }
@@ -796,7 +790,7 @@ Reset the internal state: reset();";
private class SetCommand : BatchCommand private class SetCommand : BatchCommand
{ {
/// <inheritdoc/> /// <inheritdoc/>
public SetCommand(List<string> arguments) : base(arguments) { } public SetCommand(string[] args) : base(args) { }
/// <inheritdoc/> /// <inheritdoc/>
public override string Usage() public override string Usage()
@@ -807,7 +801,7 @@ Reset the internal state: reset();";
/// <inheritdoc/> /// <inheritdoc/>
public override (bool, string?) ValidateArguments() public override (bool, string?) ValidateArguments()
{ {
if (Arguments.Count != 2) if (Arguments.Length != 2)
{ {
string message = $"Invoked {Name} but no arguments were provided"; string message = $"Invoked {Name} but no arguments were provided";
return (false, message); return (false, message);
@@ -853,7 +847,7 @@ Reset the internal state: reset();";
private class WriteCommand : BatchCommand private class WriteCommand : BatchCommand
{ {
/// <inheritdoc/> /// <inheritdoc/>
public WriteCommand(List<string> arguments) : base(arguments) { } public WriteCommand(string[] args) : base(args) { }
/// <inheritdoc/> /// <inheritdoc/>
public override string Usage() public override string Usage()
@@ -864,15 +858,15 @@ Reset the internal state: reset();";
/// <inheritdoc/> /// <inheritdoc/>
public override (bool, string?) ValidateArguments() public override (bool, string?) ValidateArguments()
{ {
if (Arguments.Count > 1) if (Arguments.Length > 1)
{ {
string message = $"Invoked {Name} and expected 0-1 arguments, but {Arguments.Count} arguments were provided"; string message = $"Invoked {Name} and expected 0-1 arguments, but {Arguments.Length} arguments were provided";
return (false, message); return (false, message);
} }
// Get overwrite value, if possible // Get overwrite value, if possible
bool? overwrite = true; bool? overwrite = true;
if (Arguments.Count == 1) if (Arguments.Length == 1)
overwrite = Arguments[0].AsYesNo(); overwrite = Arguments[0].AsYesNo();
// If we had an invalid input, log and continue // If we had an invalid input, log and continue
@@ -890,7 +884,7 @@ Reset the internal state: reset();";
{ {
// Get overwrite value, if possible // Get overwrite value, if possible
bool overwrite = true; bool overwrite = true;
if (Arguments.Count == 1) if (Arguments.Length == 1)
overwrite = Arguments[0].AsYesNo() ?? true; overwrite = Arguments[0].AsYesNo() ?? true;
// Write out the dat with the current state // Write out the dat with the current state