diff --git a/SabreTools.DatFiles/DatFile.Removal.cs b/SabreTools.DatFiles/DatFile.Removal.cs new file mode 100644 index 00000000..4bcc6bae --- /dev/null +++ b/SabreTools.DatFiles/DatFile.Removal.cs @@ -0,0 +1,413 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using SabreTools.Core; +using SabreTools.Core.Tools; +using SabreTools.DatItems; +using SabreTools.DatItems.Formats; + +namespace SabreTools.DatFiles +{ + public partial class DatFile + { + #region Removal + + /// + /// Remove fields indicated by the three input lists + /// + public void ApplyRemovals(List headerFieldNames, List machineFieldNames, Dictionary> itemFieldNames) + { + // Remove DatHeader fields + if (headerFieldNames.Count > 0) + RemoveHeaderFields(headerFieldNames); + + // Remove DatItem and Machine fields + if (machineFieldNames.Count > 0 || itemFieldNames.Count > 0) + { + ApplyRemovalsItemDictionary(machineFieldNames, itemFieldNames); + ApplyRemovalsItemDictionaryDB(machineFieldNames, itemFieldNames); + } + } + + /// + /// Apply removals to the item dictionary + /// + private void ApplyRemovalsItemDictionary(List machineFieldNames, Dictionary> itemFieldNames) + { +#if NET452_OR_GREATER || NETCOREAPP + Parallel.ForEach(Items.Keys, Globals.ParallelOptions, key => +#elif NET40_OR_GREATER + Parallel.ForEach(Items.Keys, key => +#else + foreach (var key in Items.Keys) +#endif + { + ConcurrentList? 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], machineFieldNames, itemFieldNames); + } +#if NET40_OR_GREATER || NETCOREAPP + }); +#else + } +#endif + } + + /// + /// Apply removals to the item dictionary + /// + private void ApplyRemovalsItemDictionaryDB(List machineFieldNames, Dictionary> itemFieldNames) + { +#if NET452_OR_GREATER || NETCOREAPP + Parallel.ForEach(ItemsDB.SortedKeys, Globals.ParallelOptions, key => +#elif NET40_OR_GREATER + Parallel.ForEach(ItemsDB.SortedKeys, key => +#else + foreach (var key in ItemsDB.SortedKeys) +#endif + { + var items = ItemsDB.GetDatItemsForBucket(key); + if (items == null) +#if NET40_OR_GREATER || NETCOREAPP + return; +#else + continue; +#endif + + for (int j = 0; j < items.Length; j++) + { + RemoveFields(items[j].Item2, machineFieldNames, itemFieldNames); + } +#if NET40_OR_GREATER || NETCOREAPP + }); +#else + } +#endif + } + + /// + /// Remove fields with given values + /// + private void RemoveHeaderFields(List headerFieldNames) + { + // If we have an invalid input, return + if (Header == null || !headerFieldNames.Any()) + return; + + foreach (var fieldName in headerFieldNames) + { + Header.RemoveField(fieldName); + } + } + + /// + /// Remove fields with given values + /// + private static void RemoveFields(Machine? machine, List machineFieldNames) + { + // If we have an invalid input, return + if (machine == null || !machineFieldNames.Any()) + return; + + foreach (var fieldName in machineFieldNames) + { + machine.RemoveField(fieldName); + } + } + + /// + /// Remove fields with given values + /// + /// DatItem to remove fields from + private static void RemoveFields(DatItem? datItem, List machineFieldNames, Dictionary> itemFieldNames) + { + if (datItem == null) + return; + + #region Common + + // Handle Machine fields + if (machineFieldNames.Any() && datItem.GetFieldValue(DatItem.MachineKey) != null) + RemoveFields(datItem.GetFieldValue(DatItem.MachineKey), machineFieldNames); + + // If there are no field names, return + if (itemFieldNames == null || !itemFieldNames.Any()) + 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 fieldNames = new List(); + if (itemFieldNames.ContainsKey(itemType)) + fieldNames.AddRange(itemFieldNames[itemType]); + if (itemFieldNames.ContainsKey("item")) + fieldNames.AddRange(itemFieldNames["item"]); + fieldNames = fieldNames.Distinct().ToList(); + + // If the field specifically contains Name, set it separately + if (fieldNames.Contains(Models.Metadata.Rom.NameKey)) + datItem.SetName(null); + + #endregion + + #region Item-Specific + + // Handle unnested removals first + foreach (var datItemField in fieldNames) + { + datItem.RemoveField(datItemField); + } + + // Handle nested removals + switch (datItem) + { + case Adjuster adjuster: RemoveFields(adjuster, itemFieldNames); break; + case Configuration configuration: RemoveFields(configuration, itemFieldNames); break; + case ConfSetting confSetting: RemoveFields(confSetting, itemFieldNames); break; + case Device device: RemoveFields(device, itemFieldNames); break; + case DipSwitch dipSwitch: RemoveFields(dipSwitch, itemFieldNames); break; + case DipValue dipValue: RemoveFields(dipValue, itemFieldNames); break; + case Disk disk: RemoveFields(disk, itemFieldNames); break; + case Input input: RemoveFields(input, itemFieldNames); break; + case Part part: RemoveFields(part, itemFieldNames); break; + case Port port: RemoveFields(port, itemFieldNames); break; + case Rom rom: RemoveFields(rom, itemFieldNames); break; + case Slot slot: RemoveFields(slot, itemFieldNames); break; + } + + #endregion + } + + /// + /// Remove fields with given values + /// + /// Adjuster to remove fields from + private static void RemoveFields(Adjuster adjuster, Dictionary> itemFieldNames) + { + if (!adjuster.ConditionsSpecified) + return; + + foreach (Condition subCondition in adjuster.GetFieldValue(Models.Metadata.Adjuster.ConditionKey)!) + { + RemoveFields(subCondition, [], itemFieldNames); + } + } + + /// + /// Remove fields with given values + /// + /// Configuration to remove fields from + private static void RemoveFields(Configuration configuration, Dictionary> itemFieldNames) + { + if (configuration.ConditionsSpecified) + { + foreach (Condition subCondition in configuration.GetFieldValue(Models.Metadata.Configuration.ConditionKey)!) + { + RemoveFields(subCondition, [], itemFieldNames); + } + } + + if (configuration.LocationsSpecified) + { + foreach (ConfLocation subLocation in configuration.GetFieldValue(Models.Metadata.Configuration.ConfLocationKey)!) + { + RemoveFields(subLocation, [], itemFieldNames); + } + } + + if (configuration.SettingsSpecified) + { + foreach (ConfSetting subSetting in configuration.GetFieldValue(Models.Metadata.Configuration.ConfSettingKey)!) + { + RemoveFields(subSetting as DatItem, [], itemFieldNames); + } + } + } + + /// + /// Remove fields with given values + /// + /// ConfSetting to remove fields from + private static void RemoveFields(ConfSetting confsetting, Dictionary> itemFieldNames) + { + if (confsetting.ConditionsSpecified) + { + foreach (Condition subCondition in confsetting.GetFieldValue(Models.Metadata.ConfSetting.ConditionKey)!) + { + RemoveFields(subCondition, [], itemFieldNames); + } + } + } + + /// + /// Remove fields with given values + /// + /// Device to remove fields from + private static void RemoveFields(Device device, Dictionary> itemFieldNames) + { + if (device.ExtensionsSpecified) + { + foreach (Extension subExtension in device.GetFieldValue(Models.Metadata.Device.ExtensionKey)!) + { + RemoveFields(subExtension, [], itemFieldNames); + } + } + + if (device.InstancesSpecified) + { + foreach (Instance subInstance in device.GetFieldValue(Models.Metadata.Device.InstanceKey)!) + { + RemoveFields(subInstance, [], itemFieldNames); + } + } + } + + /// + /// Remove fields with given values + /// + /// DipSwitch to remove fields from + private static void RemoveFields(DipSwitch dipSwitch, Dictionary> itemFieldNames) + { + if (dipSwitch.ConditionsSpecified) + { + foreach (Condition subCondition in dipSwitch.GetFieldValue(Models.Metadata.DipSwitch.ConditionKey)!) + { + RemoveFields(subCondition, [], itemFieldNames); + } + } + + if (dipSwitch.LocationsSpecified) + { + foreach (DipLocation subLocation in dipSwitch.GetFieldValue(Models.Metadata.DipSwitch.DipLocationKey)!) + { + RemoveFields(subLocation, [], itemFieldNames); + } + } + + if (dipSwitch.ValuesSpecified) + { + foreach (DipValue subValue in dipSwitch.GetFieldValue(Models.Metadata.DipSwitch.DipValueKey)!) + { + RemoveFields(subValue as DatItem, [], itemFieldNames); + } + } + + if (dipSwitch.PartSpecified) + RemoveFields(dipSwitch.GetFieldValue(DipSwitch.PartKey)! as DatItem, [], itemFieldNames); + } + + /// + /// Remove fields with given values + /// + /// DipValue to remove fields from + private static void RemoveFields(DipValue dipValue, Dictionary> itemFieldNames) + { + if (dipValue.ConditionsSpecified) + { + foreach (Condition subCondition in dipValue.GetFieldValue(Models.Metadata.DipValue.ConditionKey)!) + { + RemoveFields(subCondition, [], itemFieldNames); + } + } + } + + /// + /// Remove fields with given values + /// + /// Disk to remove fields from + private static void RemoveFields(Disk disk, Dictionary> itemFieldNames) + { + if (disk.DiskAreaSpecified) + RemoveFields(disk.GetFieldValue(Disk.DiskAreaKey)! as DatItem, [], itemFieldNames); + + if (disk.PartSpecified) + RemoveFields(disk.GetFieldValue(Disk.PartKey)! as DatItem, [], itemFieldNames); + } + + /// + /// Remove fields with given values + /// + /// Input to remove fields from + private static void RemoveFields(Input input, Dictionary> itemFieldNames) + { + if (input.ControlsSpecified) + { + foreach (Control subControl in input.GetFieldValue(Models.Metadata.Input.ControlKey)!) + { + RemoveFields(subControl, [], itemFieldNames); + } + } + } + + /// + /// Remove fields with given values + /// + /// Part to remove fields from + private static void RemoveFields(Part part, Dictionary> itemFieldNames) + { + if (part.FeaturesSpecified) + { + foreach (PartFeature subPartFeature in part.GetFieldValue(Models.Metadata.Part.FeatureKey)!) + { + RemoveFields(subPartFeature, [], itemFieldNames); + } + } + } + + /// + /// Remove fields with given values + /// + /// Port to remove fields from + private static void RemoveFields(Port port, Dictionary> itemFieldNames) + { + if (port.AnalogsSpecified) + { + foreach (Analog subAnalog in port.GetFieldValue(Models.Metadata.Port.AnalogKey)!) + { + RemoveFields(subAnalog, [], itemFieldNames); + } + } + } + + /// + /// Remove fields with given values + /// + /// Rom to remove fields from + private static void RemoveFields(Rom rom, Dictionary> itemFieldNames) + { + if (rom.DataAreaSpecified) + RemoveFields(rom.GetFieldValue(Rom.DataAreaKey)!, [], itemFieldNames); + + if (rom.PartSpecified) + RemoveFields(rom.GetFieldValue(Rom.PartKey)! as DatItem, [], itemFieldNames); + } + + /// + /// Remove fields with given values + /// + /// Slot to remove fields from + private static void RemoveFields(Slot slot, Dictionary> itemFieldNames) + { + if (slot.SlotOptionsSpecified) + { + foreach (SlotOption subSlotOption in slot.GetFieldValue(Models.Metadata.Slot.SlotOptionKey)!) + { + RemoveFields(subSlotOption, [], itemFieldNames); + } + } + } + + #endregion + } +} diff --git a/SabreTools.DatFiles/DatFile.cs b/SabreTools.DatFiles/DatFile.cs index 99a9977d..4e062916 100644 --- a/SabreTools.DatFiles/DatFile.cs +++ b/SabreTools.DatFiles/DatFile.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using System.Threading.Tasks; using System.Xml.Serialization; using Newtonsoft.Json; using SabreTools.Core; @@ -215,406 +214,6 @@ namespace SabreTools.DatFiles #endregion - #region Removal - - /// - /// Remove fields indicated by the three input lists - /// - public void ApplyRemovals(List headerFieldNames, List machineFieldNames, Dictionary> itemFieldNames) - { - // Remove DatHeader fields - if (headerFieldNames.Count > 0) - RemoveHeaderFields(headerFieldNames); - - // Remove DatItem and Machine fields - if (machineFieldNames.Count > 0 || itemFieldNames.Count > 0) - { - ApplyRemovalsItemDictionary(machineFieldNames, itemFieldNames); - ApplyRemovalsItemDictionaryDB(machineFieldNames, itemFieldNames); - } - } - - /// - /// Apply removals to the item dictionary - /// - private void ApplyRemovalsItemDictionary(List machineFieldNames, Dictionary> itemFieldNames) - { -#if NET452_OR_GREATER || NETCOREAPP - Parallel.ForEach(Items.Keys, Globals.ParallelOptions, key => -#elif NET40_OR_GREATER - Parallel.ForEach(Items.Keys, key => -#else - foreach (var key in Items.Keys) -#endif - { - ConcurrentList? 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], machineFieldNames, itemFieldNames); - } -#if NET40_OR_GREATER || NETCOREAPP - }); -#else - } -#endif - } - - /// - /// Apply removals to the item dictionary - /// - private void ApplyRemovalsItemDictionaryDB(List machineFieldNames, Dictionary> itemFieldNames) - { -#if NET452_OR_GREATER || NETCOREAPP - Parallel.ForEach(ItemsDB.SortedKeys, Globals.ParallelOptions, key => -#elif NET40_OR_GREATER - Parallel.ForEach(ItemsDB.SortedKeys, key => -#else - foreach (var key in ItemsDB.SortedKeys) -#endif - { - var items = ItemsDB.GetDatItemsForBucket(key); - if (items == null) -#if NET40_OR_GREATER || NETCOREAPP - return; -#else - continue; -#endif - - for (int j = 0; j < items.Length; j++) - { - RemoveFields(items[j].Item2, machineFieldNames, itemFieldNames); - } -#if NET40_OR_GREATER || NETCOREAPP - }); -#else - } -#endif - } - - /// - /// Remove fields with given values - /// - private void RemoveHeaderFields(List headerFieldNames) - { - // If we have an invalid input, return - if (Header == null || !headerFieldNames.Any()) - return; - - foreach (var fieldName in headerFieldNames) - { - Header.RemoveField(fieldName); - } - } - - /// - /// Remove fields with given values - /// - private void RemoveFields(Machine? machine, List machineFieldNames) - { - // If we have an invalid input, return - if (machine == null || !machineFieldNames.Any()) - return; - - foreach (var fieldName in machineFieldNames) - { - machine.RemoveField(fieldName); - } - } - - /// - /// Remove fields with given values - /// - /// DatItem to remove fields from - private void RemoveFields(DatItem? datItem, List machineFieldNames, Dictionary> itemFieldNames) - { - if (datItem == null) - return; - - #region Common - - // Handle Machine fields - if (machineFieldNames.Any() && datItem.GetFieldValue(DatItem.MachineKey) != null) - RemoveFields(datItem.GetFieldValue(DatItem.MachineKey), machineFieldNames); - - // If there are no field names, return - if (itemFieldNames == null || !itemFieldNames.Any()) - 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 fieldNames = new List(); - if (itemFieldNames.ContainsKey(itemType)) - fieldNames.AddRange(itemFieldNames[itemType]); - if (itemFieldNames.ContainsKey("item")) - fieldNames.AddRange(itemFieldNames["item"]); - fieldNames = fieldNames.Distinct().ToList(); - - // If the field specifically contains Name, set it separately - if (fieldNames.Contains(Models.Metadata.Rom.NameKey)) - datItem.SetName(null); - - #endregion - - #region Item-Specific - - // Handle unnested removals first - foreach (var datItemField in fieldNames) - { - datItem.RemoveField(datItemField); - } - - // Handle nested removals - switch (datItem) - { - case Adjuster adjuster: RemoveFields(adjuster, itemFieldNames); break; - case Configuration configuration: RemoveFields(configuration, itemFieldNames); break; - case ConfSetting confSetting: RemoveFields(confSetting, itemFieldNames); break; - case Device device: RemoveFields(device, itemFieldNames); break; - case DipSwitch dipSwitch: RemoveFields(dipSwitch, itemFieldNames); break; - case DipValue dipValue: RemoveFields(dipValue, itemFieldNames); break; - case Disk disk: RemoveFields(disk, itemFieldNames); break; - case Input input: RemoveFields(input, itemFieldNames); break; - case Part part: RemoveFields(part, itemFieldNames); break; - case Port port: RemoveFields(port, itemFieldNames); break; - case Rom rom: RemoveFields(rom, itemFieldNames); break; - case Slot slot: RemoveFields(slot, itemFieldNames); break; - } - - #endregion - } - - /// - /// Remove fields with given values - /// - /// Adjuster to remove fields from - private void RemoveFields(Adjuster adjuster, Dictionary> itemFieldNames) - { - if (!adjuster.ConditionsSpecified) - return; - - foreach (Condition subCondition in adjuster.GetFieldValue(Models.Metadata.Adjuster.ConditionKey)!) - { - RemoveFields(subCondition, [], itemFieldNames); - } - } - - /// - /// Remove fields with given values - /// - /// Configuration to remove fields from - private void RemoveFields(Configuration configuration, Dictionary> itemFieldNames) - { - if (configuration.ConditionsSpecified) - { - foreach (Condition subCondition in configuration.GetFieldValue(Models.Metadata.Configuration.ConditionKey)!) - { - RemoveFields(subCondition, [], itemFieldNames); - } - } - - if (configuration.LocationsSpecified) - { - foreach (ConfLocation subLocation in configuration.GetFieldValue(Models.Metadata.Configuration.ConfLocationKey)!) - { - RemoveFields(subLocation, [], itemFieldNames); - } - } - - if (configuration.SettingsSpecified) - { - foreach (ConfSetting subSetting in configuration.GetFieldValue(Models.Metadata.Configuration.ConfSettingKey)!) - { - RemoveFields(subSetting as DatItem, [], itemFieldNames); - } - } - } - - /// - /// Remove fields with given values - /// - /// ConfSetting to remove fields from - private void RemoveFields(ConfSetting confsetting, Dictionary> itemFieldNames) - { - if (confsetting.ConditionsSpecified) - { - foreach (Condition subCondition in confsetting.GetFieldValue(Models.Metadata.ConfSetting.ConditionKey)!) - { - RemoveFields(subCondition, [], itemFieldNames); - } - } - } - - /// - /// Remove fields with given values - /// - /// Device to remove fields from - private void RemoveFields(Device device, Dictionary> itemFieldNames) - { - if (device.ExtensionsSpecified) - { - foreach (Extension subExtension in device.GetFieldValue(Models.Metadata.Device.ExtensionKey)!) - { - RemoveFields(subExtension, [], itemFieldNames); - } - } - - if (device.InstancesSpecified) - { - foreach (Instance subInstance in device.GetFieldValue(Models.Metadata.Device.InstanceKey)!) - { - RemoveFields(subInstance, [], itemFieldNames); - } - } - } - - /// - /// Remove fields with given values - /// - /// DipSwitch to remove fields from - private void RemoveFields(DipSwitch dipSwitch, Dictionary> itemFieldNames) - { - if (dipSwitch.ConditionsSpecified) - { - foreach (Condition subCondition in dipSwitch.GetFieldValue(Models.Metadata.DipSwitch.ConditionKey)!) - { - RemoveFields(subCondition, [], itemFieldNames); - } - } - - if (dipSwitch.LocationsSpecified) - { - foreach (DipLocation subLocation in dipSwitch.GetFieldValue(Models.Metadata.DipSwitch.DipLocationKey)!) - { - RemoveFields(subLocation, [], itemFieldNames); - } - } - - if (dipSwitch.ValuesSpecified) - { - foreach (DipValue subValue in dipSwitch.GetFieldValue(Models.Metadata.DipSwitch.DipValueKey)!) - { - RemoveFields(subValue as DatItem, [], itemFieldNames); - } - } - - if (dipSwitch.PartSpecified) - RemoveFields(dipSwitch.GetFieldValue(DipSwitch.PartKey)! as DatItem, [], itemFieldNames); - } - - /// - /// Remove fields with given values - /// - /// DipValue to remove fields from - private void RemoveFields(DipValue dipValue, Dictionary> itemFieldNames) - { - if (dipValue.ConditionsSpecified) - { - foreach (Condition subCondition in dipValue.GetFieldValue(Models.Metadata.DipValue.ConditionKey)!) - { - RemoveFields(subCondition, [], itemFieldNames); - } - } - } - - /// - /// Remove fields with given values - /// - /// Disk to remove fields from - private void RemoveFields(Disk disk, Dictionary> itemFieldNames) - { - if (disk.DiskAreaSpecified) - RemoveFields(disk.GetFieldValue(Disk.DiskAreaKey)! as DatItem, [], itemFieldNames); - - if (disk.PartSpecified) - RemoveFields(disk.GetFieldValue(Disk.PartKey)! as DatItem, [], itemFieldNames); - } - - /// - /// Remove fields with given values - /// - /// Input to remove fields from - private void RemoveFields(Input input, Dictionary> itemFieldNames) - { - if (input.ControlsSpecified) - { - foreach (Control subControl in input.GetFieldValue(Models.Metadata.Input.ControlKey)!) - { - RemoveFields(subControl, [], itemFieldNames); - } - } - } - - /// - /// Remove fields with given values - /// - /// Part to remove fields from - private void RemoveFields(Part part, Dictionary> itemFieldNames) - { - if (part.FeaturesSpecified) - { - foreach (PartFeature subPartFeature in part.GetFieldValue(Models.Metadata.Part.FeatureKey)!) - { - RemoveFields(subPartFeature, [], itemFieldNames); - } - } - } - - /// - /// Remove fields with given values - /// - /// Port to remove fields from - private void RemoveFields(Port port, Dictionary> itemFieldNames) - { - if (port.AnalogsSpecified) - { - foreach (Analog subAnalog in port.GetFieldValue(Models.Metadata.Port.AnalogKey)!) - { - RemoveFields(subAnalog, [], itemFieldNames); - } - } - } - - /// - /// Remove fields with given values - /// - /// Rom to remove fields from - private void RemoveFields(Rom rom, Dictionary> itemFieldNames) - { - if (rom.DataAreaSpecified) - RemoveFields(rom.GetFieldValue(Rom.DataAreaKey)!, [], itemFieldNames); - - if (rom.PartSpecified) - RemoveFields(rom.GetFieldValue(Rom.PartKey)! as DatItem, [], itemFieldNames); - } - - /// - /// Remove fields with given values - /// - /// Slot to remove fields from - private void RemoveFields(Slot slot, Dictionary> itemFieldNames) - { - if (slot.SlotOptionsSpecified) - { - foreach (SlotOption subSlotOption in slot.GetFieldValue(Models.Metadata.Slot.SlotOptionKey)!) - { - RemoveFields(subSlotOption, [], itemFieldNames); - } - } - } - - #endregion - #region Writing ///