using System; using System.Collections.Generic; using SabreTools.Library.Data; using SabreTools.Library.DatFiles; using SabreTools.Library.DatItems; using SabreTools.Library.Tools; namespace SabreTools.Library.Filtering { /// /// Represents the filtering operations that need to be performed on a set of items, usually a DAT /// /// TODO: Can clever use of Filtering allow for easier external splitting methods? public class Filter { #region Fields #region Machine Filters #region Common /// /// Include or exclude machine names /// public FilterItem MachineName { get; private set; } = new FilterItem(); /// /// Include or exclude machine comments /// public FilterItem Comment { get; private set; } = new FilterItem(); /// /// Include or exclude machine descriptions /// public FilterItem MachineDescription { get; private set; } = new FilterItem(); /// /// Include or exclude machine years /// public FilterItem Year { get; private set; } = new FilterItem(); /// /// Include or exclude machine manufacturers /// public FilterItem Manufacturer { get; private set; } = new FilterItem(); /// /// Include or exclude machine publishers /// public FilterItem Publisher { get; private set; } = new FilterItem(); /// /// Include or exclude machine categories /// public FilterItem Category { get; private set; } = new FilterItem(); /// /// Include or exclude machine romof /// public FilterItem RomOf { get; private set; } = new FilterItem(); /// /// Include or exclude machine cloneof /// public FilterItem CloneOf { get; private set; } = new FilterItem(); /// /// Include or exclude machine sampleof /// public FilterItem SampleOf { get; private set; } = new FilterItem(); #endregion #region AttractMode /// /// Include or exclude machine players /// public FilterItem Players { get; private set; } = new FilterItem(); /// /// Include or exclude machine rotation /// public FilterItem Rotation { get; private set; } = new FilterItem(); /// /// Include or exclude machine control /// public FilterItem Control { get; private set; } = new FilterItem(); /// /// Include or exclude machine support status /// public FilterItem SupportStatus { get; private set; } = new FilterItem(); /// /// Include or exclude machine display count /// public FilterItem DisplayCount { get; private set; } = new FilterItem(); /// /// Include or exclude machine display type /// public FilterItem DisplayType { get; private set; } = new FilterItem(); /// /// Include or exclude machine buttons /// public FilterItem Buttons { get; private set; } = new FilterItem(); #endregion #region ListXML /// /// Include or exclude machine source file /// public FilterItem SourceFile { get; private set; } = new FilterItem(); /// /// Include or exclude items with the "Runnable" tag /// public FilterItem Runnable { get; private set; } = new FilterItem() { Neutral = null }; /// /// Include or exclude machine devices /// public FilterItem Devices { get; private set; } = new FilterItem(); /// /// Include or exclude machine slotoptions /// public FilterItem SlotOptions { get; private set; } = new FilterItem(); // TODO: Machine.Infos - List> /// /// Include or exclude machine types /// public FilterItem MachineTypes { get; private set; } = new FilterItem() { Positive = MachineType.NULL, Negative = MachineType.NULL }; #endregion #region Logiqx /// /// Include or exclude machine board /// public FilterItem Board { get; private set; } = new FilterItem(); /// /// Include or exclude machine rebuildto /// public FilterItem RebuildTo { get; private set; } = new FilterItem(); #endregion #region Logiqx EmuArc /// /// Include or exclude machine title ID /// public FilterItem TitleID { get; private set; } = new FilterItem(); /// /// Include or exclude machine developer /// public FilterItem Developer { get; private set; } = new FilterItem(); /// /// Include or exclude machine genre /// public FilterItem Genre { get; private set; } = new FilterItem(); /// /// Include or exclude machine subgenre /// public FilterItem Subgenre { get; private set; } = new FilterItem(); /// /// Include or exclude machine ratings /// public FilterItem Ratings { get; private set; } = new FilterItem(); /// /// Include or exclude machine score /// public FilterItem Score { get; private set; } = new FilterItem(); /// /// Include or exclude machine enabled /// public FilterItem Enabled { get; private set; } = new FilterItem(); /// /// Include or exclude items with the "crc" tag /// public FilterItem HasCrc { get; private set; } = new FilterItem() { Neutral = null }; /// /// Include or exclude machine related to /// public FilterItem RelatedTo { get; private set; } = new FilterItem(); #endregion #region SoftwareList /// /// Include or exclude items with the "Supported" tag /// public FilterItem Supported { get; private set; } = new FilterItem() { Neutral = null }; #endregion #endregion // Machine Filters #region DatItem Filters #region Common /// /// Include or exclude item names /// public FilterItem ItemName { get; private set; } = new FilterItem(); /// /// Include or exclude item types /// public FilterItem ItemTypes { get; private set; } = new FilterItem(); #endregion #region AttractMode /// /// Include or exclude alt names /// public FilterItem AltName { get; private set; } = new FilterItem(); /// /// Include or exclude alt titles /// public FilterItem AltTitle { get; private set; } = new FilterItem(); #endregion #region SoftwareList /// /// Include or exclude part names /// public FilterItem PartName { get; private set; } = new FilterItem(); /// /// Include or exclude part interfaces /// public FilterItem PartInterface { get; private set; } = new FilterItem(); // TODO: DatItem.Features - List> /// /// Include or exclude area names /// public FilterItem AreaName { get; private set; } = new FilterItem(); /// /// Include or exclude area sizes /// /// Positive means "Greater than or equal", Negative means "Less than or equal", Neutral means "Equal" public FilterItem AreaSize { get; private set; } = new FilterItem() { Positive = null, Negative = null, Neutral = null }; #endregion /// /// Include or exclude items with the "Default" tag /// public FilterItem Default { get; private set; } = new FilterItem() { Neutral = null }; /// /// Include or exclude descriptions /// public FilterItem Description { get; private set; } = new FilterItem(); /// /// Include or exclude item sizes /// /// Positive means "Greater than or equal", Negative means "Less than or equal", Neutral means "Equal" public FilterItem Size { get; private set; } = new FilterItem() { Positive = -1, Negative = -1, Neutral = -1 }; /// /// Include or exclude CRC32 hashes /// public FilterItem CRC { get; private set; } = new FilterItem(); /// /// Include or exclude MD5 hashes /// public FilterItem MD5 { get; private set; } = new FilterItem(); #if NET_FRAMEWORK /// /// Include or exclude RIPEMD160 hashes /// public FilterItem RIPEMD160 { get; private set; } = new FilterItem(); #endif /// /// Include or exclude SHA-1 hashes /// public FilterItem SHA1 { get; private set; } = new FilterItem(); /// /// Include or exclude SHA-256 hashes /// public FilterItem SHA256 { get; private set; } = new FilterItem(); /// /// Include or exclude SHA-384 hashes /// public FilterItem SHA384 { get; private set; } = new FilterItem(); /// /// Include or exclude SHA-512 hashes /// public FilterItem SHA512 { get; private set; } = new FilterItem(); /// /// Include or exclude merge tags /// public FilterItem MergeTag { get; private set; } = new FilterItem(); /// /// Include or exclude regions /// public FilterItem Region { get; private set; } = new FilterItem(); /// /// Include or exclude indexes /// public FilterItem Index { get; private set; } = new FilterItem(); /// /// Include or exclude items with the "Writable" tag /// public FilterItem Writable { get; private set; } = new FilterItem() { Neutral = null }; /// /// Include or exclude items with the "Writable" tag /// public FilterItem Optional { get; private set; } = new FilterItem() { Neutral = null }; /// /// Include or exclude item statuses /// public FilterItem Status { get; private set; } = new FilterItem() { Positive = ItemStatus.NULL, Negative = ItemStatus.NULL }; /// /// Include or exclude languages /// public FilterItem Language { get; private set; } = new FilterItem(); /// /// Include or exclude dates /// public FilterItem Date { get; private set; } = new FilterItem(); /// /// Include or exclude bioses /// public FilterItem Bios { get; private set; } = new FilterItem(); /// /// Include or exclude offsets /// public FilterItem Offset { get; private set; } = new FilterItem(); /// /// Include or exclude offsets /// public FilterItem Inverted { get; private set; } = new FilterItem(); #endregion // DatItem Filters #region Manipulation Flags /// /// Clean all names to WoD standards /// public bool Clean { get; set; } /// /// Set Machine Description from Machine Name /// public bool DescriptionAsName { get; set; } /// /// Include romof and cloneof when filtering machine names /// public bool IncludeOfInGame { get; set; } /// /// Internally split a DatFile /// public SplitType InternalSplit { get; set; } /// /// Remove all unicode characters /// public bool RemoveUnicode { get; set; } /// /// Include root directory when determing trim sizes /// public string Root { get; set; } /// /// Change all machine names to "!" /// public bool Single { get; set; } /// /// Trim total machine and item name to not exceed NTFS limits /// public bool Trim { get; set; } #endregion #endregion // Fields #region Instance methods #region Filter Population /// /// Populate the filters object using a set of key:value filters /// /// List of key:value where ~key/!key is negated public void PopulateFromList(List filters) { foreach (string filterPair in filters) { // If we don't even have a possible filter pair if (!filterPair.Contains(":")) { Globals.Logger.Warning($"'{filterPair}` is not a valid filter string. Valid filter strings are of the form 'key:value'. Please refer to README.1ST or the help feature for more details."); continue; } string filterPairTrimmed = filterPair.Trim('"', ' ', '\t'); bool negate = filterPairTrimmed.StartsWith("!") || filterPairTrimmed.StartsWith("~") || filterPairTrimmed.StartsWith("not-"); filterPairTrimmed = filterPairTrimmed.TrimStart('!', '~'); filterPairTrimmed = filterPairTrimmed.StartsWith("not-") ? filterPairTrimmed.Substring(4) : filterPairTrimmed; string filterFieldString = filterPairTrimmed.Split(':')[0].ToLowerInvariant().Trim('"', ' ', '\t'); string filterValue = filterPairTrimmed.Substring(filterFieldString.Length + 1).Trim('"', ' ', '\t'); Field filterField = filterFieldString.AsField(); SetFilter(filterField, filterValue, negate); } } /// /// Set multiple filters from key /// /// Key for the filter to be set /// List of values for the filter /// True if negative filter, false otherwise public void SetFilter(Field key, List values, bool negate) { foreach (string value in values) { SetFilter(key, value, negate); } } /// /// Set a single filter from key /// /// Key for the filter to be set /// Value of the filter /// True if negative filter, false otherwise public void SetFilter(Field key, string value, bool negate) { switch (key) { #region Machine Filters #region Common case Field.MachineName: if (negate) MachineName.NegativeSet.Add(value); else MachineName.PositiveSet.Add(value); break; case Field.Comment: if (negate) Comment.NegativeSet.Add(value); else Comment.PositiveSet.Add(value); break; case Field.Description: if (negate) MachineDescription.NegativeSet.Add(value); else MachineDescription.PositiveSet.Add(value); break; case Field.Year: if (negate) Year.NegativeSet.Add(value); else Year.PositiveSet.Add(value); break; case Field.Manufacturer: if (negate) Manufacturer.NegativeSet.Add(value); else Manufacturer.PositiveSet.Add(value); break; case Field.Publisher: if (negate) Publisher.NegativeSet.Add(value); else Publisher.PositiveSet.Add(value); break; case Field.Category: if (negate) Category.NegativeSet.Add(value); else Category.PositiveSet.Add(value); break; case Field.RomOf: if (negate) RomOf.NegativeSet.Add(value); else RomOf.PositiveSet.Add(value); break; case Field.CloneOf: if (negate) CloneOf.NegativeSet.Add(value); else CloneOf.PositiveSet.Add(value); break; case Field.SampleOf: if (negate) SampleOf.NegativeSet.Add(value); else SampleOf.PositiveSet.Add(value); break; case Field.MachineType: if (negate) MachineTypes.Negative |= value.AsMachineType(); else MachineTypes.Positive |= value.AsMachineType(); break; #endregion #region AttractMode case Field.Players: if (negate) Players.NegativeSet.Add(value); else Players.PositiveSet.Add(value); break; case Field.Rotation: if (negate) Rotation.NegativeSet.Add(value); else Rotation.PositiveSet.Add(value); break; case Field.Control: if (negate) Control.NegativeSet.Add(value); else Control.PositiveSet.Add(value); break; case Field.SupportStatus: if (negate) SupportStatus.NegativeSet.Add(value); else SupportStatus.PositiveSet.Add(value); break; case Field.DisplayCount: if (negate) DisplayCount.NegativeSet.Add(value); else DisplayCount.PositiveSet.Add(value); break; case Field.DisplayType: if (negate) DisplayType.NegativeSet.Add(value); else DisplayType.PositiveSet.Add(value); break; case Field.Buttons: if (negate) Buttons.NegativeSet.Add(value); else Buttons.PositiveSet.Add(value); break; #endregion #region ListXML case Field.SourceFile: if (negate) SourceFile.NegativeSet.Add(value); else SourceFile.PositiveSet.Add(value); break; case Field.Runnable: if (negate || value.Equals("false", StringComparison.OrdinalIgnoreCase)) Runnable.Neutral = false; else Runnable.Neutral = true; break; case Field.Devices: if (negate) Devices.NegativeSet.Add(value); else Devices.PositiveSet.Add(value); break; case Field.SlotOptions: if (negate) SlotOptions.NegativeSet.Add(value); else SlotOptions.PositiveSet.Add(value); break; #endregion #region Logiqx case Field.Board: if (negate) Board.NegativeSet.Add(value); else Board.PositiveSet.Add(value); break; case Field.RebuildTo: if (negate) RebuildTo.NegativeSet.Add(value); else RebuildTo.PositiveSet.Add(value); break; #endregion #region Logiqx EmuArc case Field.TitleID: if (negate) TitleID.NegativeSet.Add(value); else TitleID.PositiveSet.Add(value); break; case Field.Developer: if (negate) Developer.NegativeSet.Add(value); else Developer.PositiveSet.Add(value); break; case Field.Genre: if (negate) Genre.NegativeSet.Add(value); else Genre.PositiveSet.Add(value); break; case Field.Subgenre: if (negate) Subgenre.NegativeSet.Add(value); else Subgenre.PositiveSet.Add(value); break; case Field.Ratings: if (negate) Ratings.NegativeSet.Add(value); else Ratings.PositiveSet.Add(value); break; case Field.Score: if (negate) Score.NegativeSet.Add(value); else Score.PositiveSet.Add(value); break; case Field.Enabled: if (negate) Enabled.NegativeSet.Add(value); else Enabled.PositiveSet.Add(value); break; case Field.HasCrc: if (negate || value.Equals("false", StringComparison.OrdinalIgnoreCase)) HasCrc.Neutral = false; else HasCrc.Neutral = true; break; case Field.RelatedTo: if (negate) RelatedTo.NegativeSet.Add(value); else RelatedTo.PositiveSet.Add(value); break; #endregion #region SoftwareList case Field.Supported: if (negate || value.Equals("false", StringComparison.OrdinalIgnoreCase)) Supported.Neutral = false; else Supported.Neutral = true; break; #endregion #endregion // Machine Filters #region DatItem Filters #region Common case Field.Name: if (negate) ItemName.NegativeSet.Add(value); else ItemName.PositiveSet.Add(value); break; case Field.ItemType: if (value.AsItemType() == null) return; if (negate) ItemTypes.NegativeSet.Add(value); else ItemTypes.PositiveSet.Add(value); break; #endregion #region AttractMode case Field.AltName: if (negate) AltName.NegativeSet.Add(value); else AltName.PositiveSet.Add(value); break; case Field.AltTitle: if (negate) AltTitle.NegativeSet.Add(value); else AltTitle.PositiveSet.Add(value); break; #endregion #region SoftwareList case Field.PartName: if (negate) PartName.NegativeSet.Add(value); else PartName.PositiveSet.Add(value); break; case Field.PartInterface: if (negate) PartInterface.NegativeSet.Add(value); else PartInterface.PositiveSet.Add(value); break; case Field.AreaName: if (negate) AreaName.NegativeSet.Add(value); else AreaName.PositiveSet.Add(value); break; case Field.AreaSize: bool? asOperation = null; if (value.StartsWith(">")) asOperation = true; else if (value.StartsWith("<")) asOperation = false; else if (value.StartsWith("=")) asOperation = null; string areasizeString = value.TrimStart('>', '<', '='); if (!Int64.TryParse(areasizeString, out long areasize)) return; // Equal if (asOperation == null && !negate) { AreaSize.Neutral = areasize; } // Not Equal else if (asOperation == null && negate) { AreaSize.Negative = areasize - 1; AreaSize.Positive = areasize + 1; } // Greater Than or Equal else if (asOperation == true && !negate) { AreaSize.Positive = areasize; } // Strictly Less Than else if (asOperation == true && negate) { AreaSize.Negative = areasize - 1; } // Less Than or Equal else if (asOperation == false && !negate) { AreaSize.Negative = areasize; } // Strictly Greater Than else if (asOperation == false && negate) { AreaSize.Positive = areasize + 1; } break; #endregion case Field.Default: if (negate || value.Equals("false", StringComparison.OrdinalIgnoreCase)) Default.Neutral = false; else Default.Neutral = true; break; case Field.BiosDescription: if (negate) Description.NegativeSet.Add(value); else Description.PositiveSet.Add(value); break; case Field.Size: bool? sOperation = null; if (value.StartsWith(">")) sOperation = true; else if (value.StartsWith("<")) sOperation = false; else if (value.StartsWith("=")) sOperation = null; string sizeString = value.TrimStart('>', '<', '='); if (!Int64.TryParse(sizeString, out long size)) return; // Equal if (sOperation == null && !negate) { Size.Neutral = size; } // Not Equal else if (sOperation == null && negate) { Size.Negative = size - 1; Size.Positive = size + 1; } // Greater Than or Equal else if (sOperation == true && !negate) { Size.Positive = size; } // Strictly Less Than else if (sOperation == true && negate) { Size.Negative = size - 1; } // Less Than or Equal else if (sOperation == false && !negate) { Size.Negative = size; } // Strictly Greater Than else if (sOperation == false && negate) { Size.Positive = size + 1; } break; case Field.CRC: if (negate) CRC.NegativeSet.Add(value); else CRC.PositiveSet.Add(value); break; case Field.MD5: if (negate) MD5.NegativeSet.Add(value); else MD5.PositiveSet.Add(value); break; #if NET_FRAMEWORK case Field.RIPEMD160: if (negate) RIPEMD160.NegativeSet.Add(value); else RIPEMD160.PositiveSet.Add(value); break; #endif case Field.SHA1: if (negate) SHA1.NegativeSet.Add(value); else SHA1.PositiveSet.Add(value); break; case Field.SHA256: if (negate) SHA256.NegativeSet.Add(value); else SHA256.PositiveSet.Add(value); break; case Field.SHA384: if (negate) SHA384.NegativeSet.Add(value); else SHA384.PositiveSet.Add(value); break; case Field.SHA512: if (negate) SHA512.NegativeSet.Add(value); else SHA512.PositiveSet.Add(value); break; case Field.Merge: if (negate) MergeTag.NegativeSet.Add(value); else MergeTag.PositiveSet.Add(value); break; case Field.Region: if (negate) Region.NegativeSet.Add(value); else Region.PositiveSet.Add(value); break; case Field.Index: if (negate) Index.NegativeSet.Add(value); else Index.PositiveSet.Add(value); break; case Field.Writable: if (negate || value.Equals("false", StringComparison.OrdinalIgnoreCase)) Writable.Neutral = false; else Writable.Neutral = true; break; case Field.Optional: if (negate || value.Equals("false", StringComparison.OrdinalIgnoreCase)) Optional.Neutral = false; else Optional.Neutral = true; break; case Field.Status: if (negate) Status.Negative |= value.AsItemStatus(); else Status.Positive |= value.AsItemStatus(); break; case Field.Language: if (negate) Language.NegativeSet.Add(value); else Language.PositiveSet.Add(value); break; case Field.Date: if (negate) Date.NegativeSet.Add(value); else Date.PositiveSet.Add(value); break; case Field.Bios: if (negate) Bios.NegativeSet.Add(value); else Bios.PositiveSet.Add(value); break; case Field.Offset: if (negate) Offset.NegativeSet.Add(value); else Offset.PositiveSet.Add(value); break; case Field.Inverted: if (negate || value.Equals("false", StringComparison.OrdinalIgnoreCase)) Inverted.Neutral = false; else Inverted.Neutral = true; break; #endregion // DatItem Filters } } #endregion #endregion // Instance Methods } }