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 { /// /// Represents the removal operations that need to be performed on a set of items, usually a DAT /// public class Remover { #region Fields /// /// List of header fields to exclude from writing /// public readonly List HeaderFieldNames = []; /// /// List of machine fields to exclude from writing /// public readonly List MachineFieldNames = []; /// /// List of fields to exclude from writing /// public readonly Dictionary> ItemFieldNames = []; #endregion #region Logging /// /// Logging object /// protected Logger _logger; #endregion #region Constructors /// /// Constructor /// public Remover() { _logger = new Logger(this); } #endregion #region Population /// /// Populate the exclusion objects using a field name /// /// Field name public void PopulateExclusions(string field) => PopulateExclusionsFromList([field]); /// /// Populate the exclusion objects using a set of field names /// /// List of field names public void PopulateExclusionsFromList(List? fields) { // If the list is null or empty, just return if (fields == null || fields.Count == 0) return; var watch = new InternalStopwatch("Populating removals from list"); foreach (string field in fields) { bool removerSet = SetRemover(field); if (!removerSet) _logger.Warning($"The value {field} did not match any known field names. Please check the wiki for more details on supported field names."); } watch.Stop(); } /// /// Set remover from a value /// /// Key for the remover to be set private bool SetRemover(string field) { // If the key is null or empty, return false if (string.IsNullOrEmpty(field)) return false; // Get the parser pair out of it, if possible try { var key = new FilterKey(field); switch (key.ItemName) { case Models.Metadata.MetadataFile.HeaderKey: HeaderFieldNames.Add(key.FieldName); return true; case Models.Metadata.MetadataFile.MachineKey: MachineFieldNames.Add(key.FieldName); return true; default: if (!ItemFieldNames.ContainsKey(key.ItemName)) ItemFieldNames[key.ItemName] = []; ItemFieldNames[key.ItemName].Add(key.FieldName); return true; } } catch { return false; } } #endregion #region Running /// /// Remove fields from a DatFile /// /// Current DatFile object to run operations on public void ApplyRemovals(DatFile datFile) { InternalStopwatch watch = new("Applying removals to DAT"); 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 /// public void RemoveItemFields(ItemDictionary? itemDictionary) { // If we have an invalid input, return if (itemDictionary == null || (MachineFieldNames.Count == 0 && ItemFieldNames.Count == 0)) return; foreach (var key in itemDictionary.SortedKeys) { List? items = itemDictionary.GetItemsForBucket(key); if (items == null) continue; for (int j = 0; j < items.Count; j++) { // Handle machine removals var machine = items[j].GetMachine(); RemoveFields(machine); // Handle item removals RemoveFields(items[j]); } } } /// /// Apply removals to the item dictionary /// 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 foreach (var kvp in itemDictionary.GetMachines()) { RemoveFields(kvp.Value); } // Handle item removals foreach (var key in itemDictionary.SortedKeys) { var items = itemDictionary.GetItemsForBucket(key); if (items == null) continue; foreach (var item in items.Values) { RemoveFields(item); } } } /// /// Remove machine fields with given values /// internal 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 internal void RemoveFields(DatItem? datItem) { if (datItem == null) return; #region Common // 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 } }