diff --git a/SabreTools.DatFiles.Test/DatFileTests.Removal.cs b/SabreTools.DatFiles.Test/DatFileTests.Removal.cs deleted file mode 100644 index 431239d0..00000000 --- a/SabreTools.DatFiles.Test/DatFileTests.Removal.cs +++ /dev/null @@ -1,33 +0,0 @@ -namespace SabreTools.DatFiles.Test -{ - public partial class DatFileTests - { - #region RemoveHeaderFields - - // TODO: Write RemoveHeaderFields tests - // - Empty list - // - Full list - - #endregion - - #region RemoveItemFields - - // TODO: Write RemoveItemFields tests - // - Both lists empty - // - Machine only - // - Item only - // - Nested - - #endregion - - #region RemoveItemFieldsDB - - // TODO: Write RemoveItemFieldsDB tests - // - Both lists empty - // - Machine only - // - Item only - // - Nested - - #endregion - } -} \ No newline at end of file diff --git a/SabreTools.DatFiles/DatFile.Removal.cs b/SabreTools.DatFiles/DatFile.Removal.cs deleted file mode 100644 index 9eaffdf0..00000000 --- a/SabreTools.DatFiles/DatFile.Removal.cs +++ /dev/null @@ -1,397 +0,0 @@ -using System.Collections.Generic; -#if NET40_OR_GREATER || NETCOREAPP -using System.Threading.Tasks; -#endif -using SabreTools.Core.Tools; -using SabreTools.DatItems; -using SabreTools.DatItems.Formats; - -namespace SabreTools.DatFiles -{ - public partial class DatFile - { - #region Removal - - /// - /// Remove header fields with given values - /// - public void RemoveHeaderFields(List fields) - { - // If we have an invalid input, return - if (fields.Count == 0) - return; - - foreach (var fieldName in fields) - { - bool removed = Header.RemoveField(fieldName); - _logger.Verbose($"Header field {fieldName} {(removed ? "removed" : "could not be removed")}"); - } - } - - /// - /// Apply removals to the item dictionary - /// - public void RemoveItemFields(List machineFields, Dictionary> itemFields) - { - // If we have an invalid input, return - if (machineFields.Count == 0 && itemFields.Count == 0) - return; - -#if NET452_OR_GREATER || NETCOREAPP - Parallel.ForEach(Items.Keys, Core.Globals.ParallelOptions, key => -#elif NET40_OR_GREATER - Parallel.ForEach(Items.Keys, key => -#else - foreach (var key in Items.Keys) -#endif - { - List? items = Items[key]; - if (items == null) -#if NET40_OR_GREATER || NETCOREAPP - return; -#else - continue; -#endif - - for (int j = 0; j < items.Count; j++) - { - RemoveFields(items[j], machineFields, itemFields); - } -#if NET40_OR_GREATER || NETCOREAPP - }); -#else - } -#endif - } - - /// - /// Apply removals to the item dictionary - /// - public void RemoveItemFieldsDB(List machineFields, Dictionary> itemFields) - { - // If we have an invalid input, return - if (machineFields.Count == 0 && itemFields.Count == 0) - return; - - // Handle machine removals -#if NET452_OR_GREATER || NETCOREAPP - Parallel.ForEach(ItemsDB.GetMachines(), Core.Globals.ParallelOptions, kvp => -#elif NET40_OR_GREATER - Parallel.ForEach(ItemsDB.GetMachines(), kvp => -#else - foreach (var kvp in ItemsDB.GetMachines()) -#endif - { - RemoveFields(kvp.Value, machineFields); -#if NET40_OR_GREATER || NETCOREAPP - }); -#else - } -#endif - - // Handle item removals -#if NET452_OR_GREATER || NETCOREAPP - Parallel.ForEach(ItemsDB.SortedKeys, Core.Globals.ParallelOptions, key => -#elif NET40_OR_GREATER - Parallel.ForEach(ItemsDB.SortedKeys, key => -#else - foreach (var key in ItemsDB.SortedKeys) -#endif - { - var items = ItemsDB.GetItemsForBucket(key); - if (items == null) -#if NET40_OR_GREATER || NETCOREAPP - return; -#else - continue; -#endif - - foreach (var item in items.Values) - { - RemoveFields(item, [], itemFields); - } -#if NET40_OR_GREATER || NETCOREAPP - }); -#else - } -#endif - } - - /// - /// Remove machine fields with given values - /// - private static void RemoveFields(Machine? machine, List fields) - { - // If we have an invalid input, return - if (machine == null || fields.Count == 0) - return; - - foreach (var fieldName in fields) - { - machine.RemoveField(fieldName); - } - } - - /// - /// Remove fields with given values - /// - /// DatItem to remove fields from - private static void RemoveFields(DatItem? datItem, List machineFields, Dictionary> itemFields) - { - if (datItem == null) - return; - - #region Common - - // Handle Machine fields - var machine = datItem.GetFieldValue(DatItem.MachineKey); - if (machineFields.Count > 0 && machine != null) - RemoveFields(machine, machineFields); - - // If there are no field names, return - if (itemFields == null || itemFields.Count == 0) - return; - - // If there are no field names for this type or generic, return - string? itemType = datItem.GetStringFieldValue(Models.Metadata.DatItem.TypeKey).AsEnumValue().AsStringValue(); - if (itemType == null || (!itemFields.ContainsKey(itemType) && !itemFields.ContainsKey("item"))) - return; - - // Get the combined list of fields to remove - var fields = new HashSet(); - if (itemFields.ContainsKey(itemType)) - fields.UnionWith(itemFields[itemType]); - if (itemFields.ContainsKey("item")) - fields.UnionWith(itemFields["item"]); - - // If the field specifically contains Name, set it separately - if (fields.Contains(Models.Metadata.Rom.NameKey)) - datItem.SetName(null); - - #endregion - - #region Item-Specific - - // Handle unnested removals first - foreach (var datItemField in fields) - { - datItem.RemoveField(datItemField); - } - - // Handle nested removals - switch (datItem) - { - case Adjuster adjuster: RemoveFields(adjuster, itemFields); break; - case Configuration configuration: RemoveFields(configuration, itemFields); break; - case ConfSetting confSetting: RemoveFields(confSetting, itemFields); break; - case Device device: RemoveFields(device, itemFields); break; - case DipSwitch dipSwitch: RemoveFields(dipSwitch, itemFields); break; - case DipValue dipValue: RemoveFields(dipValue, itemFields); break; - case Disk disk: RemoveFields(disk, itemFields); break; - case Input input: RemoveFields(input, itemFields); break; - case Part part: RemoveFields(part, itemFields); break; - case Port port: RemoveFields(port, itemFields); break; - case Rom rom: RemoveFields(rom, itemFields); break; - case Slot slot: RemoveFields(slot, itemFields); break; - } - - #endregion - } - - /// - /// Remove fields with given values - /// - /// Adjuster to remove fields from - private static void RemoveFields(Adjuster adjuster, Dictionary> fields) - { - var conditions = adjuster.GetFieldValue(Models.Metadata.Adjuster.ConditionKey) ?? []; - foreach (Condition subCondition in conditions) - { - RemoveFields(subCondition, [], fields); - } - } - - /// - /// Remove fields with given values - /// - /// Configuration to remove fields from - private static void RemoveFields(Configuration configuration, Dictionary> fields) - { - var conditions = configuration.GetFieldValue(Models.Metadata.Configuration.ConditionKey) ?? []; - foreach (Condition subCondition in conditions) - { - RemoveFields(subCondition, [], fields); - } - - var locations = configuration.GetFieldValue(Models.Metadata.Configuration.ConfLocationKey) ?? []; - foreach (ConfLocation subLocation in locations) - { - RemoveFields(subLocation, [], fields); - } - - var settings = configuration.GetFieldValue(Models.Metadata.Configuration.ConfSettingKey) ?? []; - foreach (ConfSetting subSetting in settings) - { - RemoveFields(subSetting as DatItem, [], fields); - } - } - - /// - /// Remove fields with given values - /// - /// ConfSetting to remove fields from - private static void RemoveFields(ConfSetting confsetting, Dictionary> fields) - { - var conditions = confsetting.GetFieldValue(Models.Metadata.ConfSetting.ConditionKey) ?? []; - foreach (Condition subCondition in conditions) - { - RemoveFields(subCondition, [], fields); - } - } - - /// - /// Remove fields with given values - /// - /// Device to remove fields from - private static void RemoveFields(Device device, Dictionary> fields) - { - var extensions = device.GetFieldValue(Models.Metadata.Device.ExtensionKey) ?? []; - foreach (Extension subExtension in extensions) - { - RemoveFields(subExtension, [], fields); - } - - var instances = device.GetFieldValue(Models.Metadata.Device.InstanceKey) ?? []; - foreach (Instance subInstance in instances) - { - RemoveFields(subInstance, [], fields); - } - } - - /// - /// Remove fields with given values - /// - /// DipSwitch to remove fields from - private static void RemoveFields(DipSwitch dipSwitch, Dictionary> fields) - { - var conditions = dipSwitch.GetFieldValue(Models.Metadata.DipSwitch.ConditionKey) ?? []; - foreach (Condition subCondition in conditions) - { - RemoveFields(subCondition, [], fields); - } - - var locations = dipSwitch.GetFieldValue(Models.Metadata.DipSwitch.DipLocationKey) ?? []; - foreach (DipLocation subLocation in locations) - { - RemoveFields(subLocation, [], fields); - } - - var dipValues = dipSwitch.GetFieldValue(Models.Metadata.DipSwitch.DipValueKey) ?? []; - foreach (DipValue subValue in dipValues) - { - RemoveFields(subValue as DatItem, [], fields); - } - - var part = dipSwitch.GetFieldValue(DipSwitch.PartKey); - if (part != null) - RemoveFields(part as DatItem, [], fields); - } - - /// - /// Remove fields with given values - /// - /// DipValue to remove fields from - private static void RemoveFields(DipValue dipValue, Dictionary> fields) - { - var conditions = dipValue.GetFieldValue(Models.Metadata.DipValue.ConditionKey) ?? []; - foreach (Condition subCondition in conditions) - { - RemoveFields(subCondition, [], fields); - } - } - - /// - /// Remove fields with given values - /// - /// Disk to remove fields from - private static void RemoveFields(Disk disk, Dictionary> fields) - { - var diskArea = disk.GetFieldValue(Disk.DiskAreaKey); - if (diskArea != null) - RemoveFields(diskArea as DatItem, [], fields); - - var part = disk.GetFieldValue(Disk.PartKey); - if (part != null) - RemoveFields(part as DatItem, [], fields); - } - - /// - /// Remove fields with given values - /// - /// Input to remove fields from - private static void RemoveFields(Input input, Dictionary> fields) - { - var controls = input.GetFieldValue(Models.Metadata.Input.ControlKey) ?? []; - foreach (Control subControl in controls) - { - RemoveFields(subControl, [], fields); - } - } - - /// - /// Remove fields with given values - /// - /// Part to remove fields from - private static void RemoveFields(Part part, Dictionary> fields) - { - var features = part.GetFieldValue(Models.Metadata.Part.FeatureKey) ?? []; - foreach (PartFeature subPartFeature in features) - { - RemoveFields(subPartFeature, [], fields); - } - } - - /// - /// Remove fields with given values - /// - /// Port to remove fields from - private static void RemoveFields(Port port, Dictionary> fields) - { - var analogs = port.GetFieldValue(Models.Metadata.Port.AnalogKey) ?? []; - foreach (Analog subAnalog in analogs) - { - RemoveFields(subAnalog, [], fields); - } - } - - /// - /// Remove fields with given values - /// - /// Rom to remove fields from - private static void RemoveFields(Rom rom, Dictionary> fields) - { - var dataArea = rom.GetFieldValue(Rom.DataAreaKey); - if (dataArea != null) - RemoveFields(dataArea as DatItem, [], fields); - - var part = rom.GetFieldValue(Rom.PartKey); - if (part != null) - RemoveFields(part as DatItem, [], fields); - } - - /// - /// Remove fields with given values - /// - /// Slot to remove fields from - private static void RemoveFields(Slot slot, Dictionary> fields) - { - var slotOptions = slot.GetFieldValue(Models.Metadata.Slot.SlotOptionKey) ?? []; - foreach (SlotOption subSlotOption in slotOptions) - { - RemoveFields(subSlotOption, [], fields); - } - } - - #endregion - } -} diff --git a/SabreTools.DatTools/Remover.cs b/SabreTools.DatTools/Remover.cs index d0d85dfc..3ed4a774 100644 --- a/SabreTools.DatTools/Remover.cs +++ b/SabreTools.DatTools/Remover.cs @@ -1,6 +1,9 @@ using System.Collections.Generic; using SabreTools.Core.Filter; +using SabreTools.Core.Tools; using SabreTools.DatFiles; +using SabreTools.DatItems; +using SabreTools.DatItems.Formats; using SabreTools.IO.Logging; namespace SabreTools.DatTools @@ -130,12 +133,396 @@ namespace SabreTools.DatTools public void ApplyRemovals(DatFile datFile) { InternalStopwatch watch = new("Applying removals to DAT"); - datFile.RemoveHeaderFields(HeaderFieldNames); - datFile.RemoveItemFields(MachineFieldNames, ItemFieldNames); - datFile.RemoveItemFieldsDB(MachineFieldNames, ItemFieldNames); + + RemoveHeaderFields(datFile.Header); + RemoveItemFields(datFile.Items); + RemoveItemFieldsDB(datFile.ItemsDB); + watch.Stop(); } + /// + /// Remove header fields with given values + /// + public void RemoveHeaderFields(DatHeader? datHeader) + { + // If we have an invalid input, return + if (datHeader == null || HeaderFieldNames.Count == 0) + return; + + foreach (var fieldName in HeaderFieldNames) + { + bool removed = datHeader.RemoveField(fieldName); + _logger.Verbose($"Header field {fieldName} {(removed ? "removed" : "could not be removed")}"); + } + } + + /// + /// Apply removals to the item dictionary + /// + /// TODO: Does this need to be multi-threaded? + public void RemoveItemFields(ItemDictionary? itemDictionary) + { + // If we have an invalid input, return + if (itemDictionary == null || (MachineFieldNames.Count == 0 && ItemFieldNames.Count == 0)) + return; + +#if NET452_OR_GREATER || NETCOREAPP + Parallel.ForEach(itemDictionary.Keys, Core.Globals.ParallelOptions, key => +#elif NET40_OR_GREATER + Parallel.ForEach(itemDictionary.Keys, key => +#else + foreach (var key in itemDictionary.Keys) +#endif + { + List? items = itemDictionary[key]; + if (items == null) +#if NET40_OR_GREATER || NETCOREAPP + return; +#else + continue; +#endif + + for (int j = 0; j < items.Count; j++) + { + RemoveFields(items[j]); + } +#if NET40_OR_GREATER || NETCOREAPP + }); +#else + } +#endif + } + + /// + /// Apply removals to the item dictionary + /// + /// TODO: Does this need to be multi-threaded? + public void RemoveItemFieldsDB(ItemDictionaryDB? itemDictionary) + { + // If we have an invalid input, return + if (itemDictionary == null || (MachineFieldNames.Count == 0 && ItemFieldNames.Count == 0)) + return; + + // Handle machine removals +#if NET452_OR_GREATER || NETCOREAPP + Parallel.ForEach(itemDictionary.GetMachines(), Core.Globals.ParallelOptions, kvp => +#elif NET40_OR_GREATER + Parallel.ForEach(itemDictionary.GetMachines(), kvp => +#else + foreach (var kvp in itemDictionary.GetMachines()) +#endif + { + RemoveFields(kvp.Value); +#if NET40_OR_GREATER || NETCOREAPP + }); +#else + } +#endif + + // Handle item removals +#if NET452_OR_GREATER || NETCOREAPP + Parallel.ForEach(ItemsDB.SortedKeys, Core.Globals.ParallelOptions, key => +#elif NET40_OR_GREATER + Parallel.ForEach(ItemsDB.SortedKeys, key => +#else + foreach (var key in itemDictionary.SortedKeys) +#endif + { + var items = itemDictionary.GetItemsForBucket(key); + if (items == null) +#if NET40_OR_GREATER || NETCOREAPP + return; +#else + continue; +#endif + + foreach (var item in items.Values) + { + RemoveFields(item); + } +#if NET40_OR_GREATER || NETCOREAPP + }); +#else + } +#endif + } + + /// + /// Remove machine fields with given values + /// + private void RemoveFields(Machine? machine) + { + // If we have an invalid input, return + if (machine == null || MachineFieldNames.Count == 0) + return; + + foreach (var fieldName in MachineFieldNames) + { + machine.RemoveField(fieldName); + } + } + + /// + /// Remove fields with given values + /// + /// DatItem to remove fields from + private void RemoveFields(DatItem? datItem) + { + if (datItem == null) + return; + + #region Common + + // Handle Machine fields + var machine = datItem.GetFieldValue(DatItem.MachineKey); + if (MachineFieldNames.Count > 0 && machine != null) + RemoveFields(machine); + + // If there are no field names, return + if (ItemFieldNames == null || ItemFieldNames.Count == 0) + return; + + // If there are no field names for this type or generic, return + string? itemType = datItem.GetStringFieldValue(Models.Metadata.DatItem.TypeKey).AsEnumValue().AsStringValue(); + if (itemType == null || (!ItemFieldNames.ContainsKey(itemType) && !ItemFieldNames.ContainsKey("item"))) + return; + + // Get the combined list of fields to remove + var fields = new HashSet(); + if (ItemFieldNames.ContainsKey(itemType)) + fields.UnionWith(ItemFieldNames[itemType]); + if (ItemFieldNames.ContainsKey("item")) + fields.UnionWith(ItemFieldNames["item"]); + + // If the field specifically contains Name, set it separately + if (fields.Contains(Models.Metadata.Rom.NameKey)) + datItem.SetName(null); + + #endregion + + #region Item-Specific + + // Handle unnested removals first + foreach (var datItemField in fields) + { + datItem.RemoveField(datItemField); + } + + // Handle nested removals + switch (datItem) + { + case Adjuster adjuster: RemoveNestedFields(adjuster); break; + case Configuration configuration: RemoveNestedFields(configuration); break; + case ConfSetting confSetting: RemoveNestedFields(confSetting); break; + case Device device: RemoveNestedFields(device); break; + case DipSwitch dipSwitch: RemoveNestedFields(dipSwitch); break; + case DipValue dipValue: RemoveNestedFields(dipValue); break; + case Disk disk: RemoveNestedFields(disk); break; + case Input input: RemoveNestedFields(input); break; + case Part part: RemoveNestedFields(part); break; + case Port port: RemoveNestedFields(port); break; + case Rom rom: RemoveNestedFields(rom); break; + case Slot slot: RemoveNestedFields(slot); break; + } + + #endregion + } + + /// + /// Remove fields with given values + /// + /// Adjuster to remove fields from + private void RemoveNestedFields(Adjuster adjuster) + { + var conditions = adjuster.GetFieldValue(Models.Metadata.Adjuster.ConditionKey) ?? []; + foreach (Condition subCondition in conditions) + { + RemoveFields(subCondition); + } + } + + /// + /// Remove fields with given values + /// + /// Configuration to remove fields from + private void RemoveNestedFields(Configuration configuration) + { + var conditions = configuration.GetFieldValue(Models.Metadata.Configuration.ConditionKey) ?? []; + foreach (Condition subCondition in conditions) + { + RemoveFields(subCondition); + } + + var locations = configuration.GetFieldValue(Models.Metadata.Configuration.ConfLocationKey) ?? []; + foreach (ConfLocation subLocation in locations) + { + RemoveFields(subLocation); + } + + var settings = configuration.GetFieldValue(Models.Metadata.Configuration.ConfSettingKey) ?? []; + foreach (ConfSetting subSetting in settings) + { + RemoveFields(subSetting); + } + } + + /// + /// Remove fields with given values + /// + /// ConfSetting to remove fields from + private void RemoveNestedFields(ConfSetting confsetting) + { + var conditions = confsetting.GetFieldValue(Models.Metadata.ConfSetting.ConditionKey) ?? []; + foreach (Condition subCondition in conditions) + { + RemoveFields(subCondition); + } + } + + /// + /// Remove fields with given values + /// + /// Device to remove fields from + private void RemoveNestedFields(Device device) + { + var extensions = device.GetFieldValue(Models.Metadata.Device.ExtensionKey) ?? []; + foreach (Extension subExtension in extensions) + { + RemoveFields(subExtension); + } + + var instances = device.GetFieldValue(Models.Metadata.Device.InstanceKey) ?? []; + foreach (Instance subInstance in instances) + { + RemoveFields(subInstance); + } + } + + /// + /// Remove fields with given values + /// + /// DipSwitch to remove fields from + private void RemoveNestedFields(DipSwitch dipSwitch) + { + var conditions = dipSwitch.GetFieldValue(Models.Metadata.DipSwitch.ConditionKey) ?? []; + foreach (Condition subCondition in conditions) + { + RemoveFields(subCondition); + } + + var locations = dipSwitch.GetFieldValue(Models.Metadata.DipSwitch.DipLocationKey) ?? []; + foreach (DipLocation subLocation in locations) + { + RemoveFields(subLocation); + } + + var dipValues = dipSwitch.GetFieldValue(Models.Metadata.DipSwitch.DipValueKey) ?? []; + foreach (DipValue subValue in dipValues) + { + RemoveFields(subValue); + } + + var part = dipSwitch.GetFieldValue(DipSwitch.PartKey); + if (part != null) + RemoveFields(part); + } + + /// + /// Remove fields with given values + /// + /// DipValue to remove fields from + private void RemoveNestedFields(DipValue dipValue) + { + var conditions = dipValue.GetFieldValue(Models.Metadata.DipValue.ConditionKey) ?? []; + foreach (Condition subCondition in conditions) + { + RemoveFields(subCondition); + } + } + + /// + /// Remove fields with given values + /// + /// Disk to remove fields from + private void RemoveNestedFields(Disk disk) + { + var diskArea = disk.GetFieldValue(Disk.DiskAreaKey); + if (diskArea != null) + RemoveFields(diskArea); + + var part = disk.GetFieldValue(Disk.PartKey); + if (part != null) + RemoveFields(part); + } + + /// + /// Remove fields with given values + /// + /// Input to remove fields from + private void RemoveNestedFields(Input input) + { + var controls = input.GetFieldValue(Models.Metadata.Input.ControlKey) ?? []; + foreach (Control subControl in controls) + { + RemoveFields(subControl); + } + } + + /// + /// Remove fields with given values + /// + /// Part to remove fields from + private void RemoveNestedFields(Part part) + { + var features = part.GetFieldValue(Models.Metadata.Part.FeatureKey) ?? []; + foreach (PartFeature subPartFeature in features) + { + RemoveFields(subPartFeature); + } + } + + /// + /// Remove fields with given values + /// + /// Port to remove fields from + private void RemoveNestedFields(Port port) + { + var analogs = port.GetFieldValue(Models.Metadata.Port.AnalogKey) ?? []; + foreach (Analog subAnalog in analogs) + { + RemoveFields(subAnalog); + } + } + + /// + /// Remove fields with given values + /// + /// Rom to remove fields from + private void RemoveNestedFields(Rom rom) + { + var dataArea = rom.GetFieldValue(Rom.DataAreaKey); + if (dataArea != null) + RemoveFields(dataArea); + + var part = rom.GetFieldValue(Rom.PartKey); + if (part != null) + RemoveFields(part); + } + + /// + /// Remove fields with given values + /// + /// Slot to remove fields from + private void RemoveNestedFields(Slot slot) + { + var slotOptions = slot.GetFieldValue(Models.Metadata.Slot.SlotOptionKey) ?? []; + foreach (SlotOption subSlotOption in slotOptions) + { + RemoveFields(subSlotOption); + } + } + #endregion } } \ No newline at end of file