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 };
// TODO: Machine.SharedFeatures - List>
#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 };
///
/// Include or exclude area byte widths
///
public FilterItem AreaWidth { get; private set; } = new FilterItem();
///
/// Include or exclude area endianness
///
public FilterItem AreaEndianness { get; private set; } = new FilterItem();
#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;
case Field.AreaWidth:
if (negate)
AreaWidth.NegativeSet.Add(value);
else
AreaWidth.PositiveSet.Add(value);
break;
case Field.AreaEndianness:
if (negate)
AreaEndianness.NegativeSet.Add(value);
else
AreaEndianness.PositiveSet.Add(value);
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
}
}