Address a handful of TODOs

This commit is contained in:
Matt Nadareski
2024-03-06 00:33:45 -05:00
parent b9f6a6862d
commit f0fa7bb6bf
10 changed files with 132 additions and 333 deletions

View File

@@ -738,6 +738,7 @@ namespace SabreTools.Core
#region Fields
// TODO: This should move to Models like the rest
/// <summary>
/// List of valid field types within a DatHeader
/// </summary>

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Serialization;
using Newtonsoft.Json;
using SabreTools.Core;
@@ -405,136 +406,6 @@ namespace SabreTools.DatFiles
#region Instance Methods
#region Accessors
/// <summary>
/// Set fields with given values
/// </summary>
/// <param name="mappings">Mappings dictionary</param>
public void SetFields(Dictionary<DatHeaderField, string> mappings)
{
#region Common
if (mappings.ContainsKey(DatHeaderField.FileName))
FileName = mappings[DatHeaderField.FileName];
if (mappings.ContainsKey(DatHeaderField.Name))
Name = mappings[DatHeaderField.Name];
if (mappings.ContainsKey(DatHeaderField.Description))
Description = mappings[DatHeaderField.Description];
if (mappings.ContainsKey(DatHeaderField.RootDir))
RootDir = mappings[DatHeaderField.RootDir];
if (mappings.ContainsKey(DatHeaderField.Category))
Category = mappings[DatHeaderField.Category];
if (mappings.ContainsKey(DatHeaderField.Version))
Version = mappings[DatHeaderField.Version];
if (mappings.ContainsKey(DatHeaderField.Date))
Date = mappings[DatHeaderField.Date];
if (mappings.ContainsKey(DatHeaderField.Author))
Author = mappings[DatHeaderField.Author];
if (mappings.ContainsKey(DatHeaderField.Email))
Email = mappings[DatHeaderField.Email];
if (mappings.ContainsKey(DatHeaderField.Homepage))
Homepage = mappings[DatHeaderField.Homepage];
if (mappings.ContainsKey(DatHeaderField.Url))
Url = mappings[DatHeaderField.Url];
if (mappings.ContainsKey(DatHeaderField.Comment))
Comment = mappings[DatHeaderField.Comment];
if (mappings.ContainsKey(DatHeaderField.HeaderSkipper))
HeaderSkipper = mappings[DatHeaderField.HeaderSkipper];
if (mappings.ContainsKey(DatHeaderField.Type))
Type = mappings[DatHeaderField.Type];
if (mappings.ContainsKey(DatHeaderField.ForceMerging))
ForceMerging = mappings[DatHeaderField.ForceMerging].AsEnumValue<MergingFlag>();
if (mappings.ContainsKey(DatHeaderField.ForceNodump))
ForceNodump = mappings[DatHeaderField.ForceNodump].AsEnumValue<NodumpFlag>();
if (mappings.ContainsKey(DatHeaderField.ForcePacking))
ForcePacking = mappings[DatHeaderField.ForcePacking].AsEnumValue<PackingFlag>();
#endregion
#region ListXML
if (mappings.ContainsKey(DatHeaderField.Debug))
Debug = mappings[DatHeaderField.Debug].AsYesNo();
if (mappings.ContainsKey(DatHeaderField.MameConfig))
MameConfig = mappings[DatHeaderField.MameConfig];
#endregion
#region Logiqx
if (mappings.ContainsKey(DatHeaderField.ID))
NoIntroID = mappings[DatHeaderField.ID];
if (mappings.ContainsKey(DatHeaderField.Build))
Build = mappings[DatHeaderField.Build];
if (mappings.ContainsKey(DatHeaderField.RomMode))
RomMode = mappings[DatHeaderField.RomMode].AsEnumValue<MergingFlag>();
if (mappings.ContainsKey(DatHeaderField.BiosMode))
BiosMode = mappings[DatHeaderField.BiosMode].AsEnumValue<MergingFlag>();
if (mappings.ContainsKey(DatHeaderField.SampleMode))
SampleMode = mappings[DatHeaderField.SampleMode].AsEnumValue<MergingFlag>();
if (mappings.ContainsKey(DatHeaderField.LockRomMode))
LockRomMode = mappings[DatHeaderField.LockRomMode].AsYesNo();
if (mappings.ContainsKey(DatHeaderField.LockBiosMode))
LockBiosMode = mappings[DatHeaderField.LockBiosMode].AsYesNo();
if (mappings.ContainsKey(DatHeaderField.LockSampleMode))
LockSampleMode = mappings[DatHeaderField.LockSampleMode].AsYesNo();
#endregion
#region OfflineList
if (mappings.ContainsKey(DatHeaderField.System))
System = mappings[DatHeaderField.System];
if (mappings.ContainsKey(DatHeaderField.ScreenshotsWidth))
ScreenshotsWidth = mappings[DatHeaderField.ScreenshotsWidth];
if (mappings.ContainsKey(DatHeaderField.ScreenshotsHeight))
ScreenshotsHeight = mappings[DatHeaderField.ScreenshotsHeight];
// TODO: Add DatHeader_Info*
// TDOO: Add DatHeader_CanOpen*
if (mappings.ContainsKey(DatHeaderField.RomTitle))
RomTitle = mappings[DatHeaderField.RomTitle];
#endregion
#region RomCenter
if (mappings.ContainsKey(DatHeaderField.RomCenterVersion))
RomCenterVersion = mappings[DatHeaderField.RomCenterVersion];
#endregion
}
#endregion
#region Cloning Methods
/// <summary>
@@ -732,6 +603,118 @@ namespace SabreTools.DatFiles
#endregion
#region Manipulation
//// <summary>
/// Remove a field from the header
/// </summary>
/// <param name="fieldName">Field to remove</param>
/// <returns>True if the removal was successful, false otherwise</returns>
public bool RemoveField(string fieldName)
{
DatHeaderField datHeaderField = fieldName.AsDatHeaderField();
switch (datHeaderField)
{
case DatHeaderField.Author: Author = null; break;
case DatHeaderField.BiosMode: BiosMode = MergingFlag.None; break;
case DatHeaderField.Build: Build = null; break;
case DatHeaderField.CanOpen: CanOpen = null; break;
case DatHeaderField.Category: Category = null; break;
case DatHeaderField.Comment: Comment = null; break;
case DatHeaderField.Date: Date = null; break;
case DatHeaderField.Debug: Debug = null; break;
case DatHeaderField.Description: Description = null; break;
case DatHeaderField.Email: Email = null; break;
case DatHeaderField.FileName: FileName = null; break;
case DatHeaderField.ForceMerging: ForceMerging = MergingFlag.None; break;
case DatHeaderField.ForceNodump: ForceNodump = NodumpFlag.None; break;
case DatHeaderField.ForcePacking: ForcePacking = PackingFlag.None; break;
case DatHeaderField.HeaderSkipper: HeaderSkipper = null; break;
case DatHeaderField.Homepage: Homepage = null; break;
case DatHeaderField.ID: NoIntroID = null; break;
// case DatHeaderField.Info_Default: Info_Default = null; break;
// case DatHeaderField.Info_IsNamingOption: Info_IsNamingOption = null; break;
// case DatHeaderField.Info_Name: Info_Name = null; break;
// case DatHeaderField.Info_Visible: Info_Visible = null; break;
case DatHeaderField.LockBiosMode: LockBiosMode = null; break;
case DatHeaderField.LockRomMode: LockRomMode = null; break;
case DatHeaderField.LockSampleMode: LockSampleMode = null; break;
case DatHeaderField.MameConfig: MameConfig = null; break;
case DatHeaderField.Name: Name = null; break;
case DatHeaderField.RomCenterVersion: RomCenterVersion = null; break;
case DatHeaderField.RomMode: RomMode = MergingFlag.None; break;
case DatHeaderField.RomTitle: RomTitle = null; break;
case DatHeaderField.RootDir: RootDir = null; break;
case DatHeaderField.SampleMode: SampleMode = MergingFlag.None; break;
case DatHeaderField.ScreenshotsHeight: ScreenshotsHeight = null; break;
case DatHeaderField.ScreenshotsWidth: ScreenshotsWidth = null; break;
case DatHeaderField.System: System = null; break;
case DatHeaderField.Type: Type = null; break;
case DatHeaderField.Url: Url = null; break;
case DatHeaderField.Version: Version = null; break;
default: return false;
}
return true;
}
/// <summary>
/// Set a field in the header from a mapping string
/// </summary>
/// <param name="fieldName">Field to set</param>
/// <param name="value">String representing the value to set</param>
/// <returns>True if the setting was successful, false otherwise</returns>
/// <remarks>This only performs minimal validation before setting</remarks>
public bool SetField(string? fieldName, string value)
{
DatHeaderField datHeaderField = fieldName.AsDatHeaderField();
switch (datHeaderField)
{
case DatHeaderField.Author: Author = value; break;
case DatHeaderField.BiosMode: BiosMode = value.AsEnumValue<MergingFlag>(); break;
case DatHeaderField.Build: Build = value; break;
case DatHeaderField.CanOpen: CanOpen = [.. value.Split(',')]; break;
case DatHeaderField.Category: Category = value; break;
case DatHeaderField.Comment: Comment = value; break;
case DatHeaderField.Date: Date = value; break;
case DatHeaderField.Debug: Debug = value.AsYesNo(); break;
case DatHeaderField.Description: Description = value; break;
case DatHeaderField.Email: Email = value; break;
case DatHeaderField.FileName: FileName = value; break;
case DatHeaderField.ForceMerging: ForceMerging = value.AsEnumValue<MergingFlag>(); break;
case DatHeaderField.ForceNodump: ForceNodump = value.AsEnumValue<NodumpFlag>(); break;
case DatHeaderField.ForcePacking: ForcePacking = value.AsEnumValue<PackingFlag>(); break;
case DatHeaderField.HeaderSkipper: HeaderSkipper = value; break;
case DatHeaderField.Homepage: Homepage = value; break;
case DatHeaderField.ID: NoIntroID = value; break;
// case DatHeaderField.Info_Default: Info_Default = value; break;
// case DatHeaderField.Info_IsNamingOption: Info_IsNamingOption = value; break;
// case DatHeaderField.Info_Name: Info_Name = value; break;
// case DatHeaderField.Info_Visible: Info_Visible = value; break;
case DatHeaderField.LockBiosMode: LockBiosMode = value.AsYesNo(); break;
case DatHeaderField.LockRomMode: LockRomMode = value.AsYesNo(); break;
case DatHeaderField.LockSampleMode: LockSampleMode = value.AsYesNo(); break;
case DatHeaderField.MameConfig: MameConfig = value; break;
case DatHeaderField.Name: Name = value; break;
case DatHeaderField.RomCenterVersion: RomCenterVersion = value; break;
case DatHeaderField.RomMode: RomMode = value.AsEnumValue<MergingFlag>(); break;
case DatHeaderField.RomTitle: RomTitle = value; break;
case DatHeaderField.RootDir: RootDir = value; break;
case DatHeaderField.SampleMode: SampleMode = value.AsEnumValue<MergingFlag>(); break;
case DatHeaderField.ScreenshotsHeight: ScreenshotsHeight = value; break;
case DatHeaderField.ScreenshotsWidth: ScreenshotsWidth = value; break;
case DatHeaderField.System: System = value; break;
case DatHeaderField.Type: Type = value; break;
case DatHeaderField.Url: Url = value; break;
case DatHeaderField.Version: Version = value; break;
default: return false;
}
return true;
}
#endregion
#region Writing
/// <summary>

View File

@@ -147,10 +147,9 @@ namespace SabreTools.DatFiles
if (datHeader == null || !HeaderFieldMappings.Any())
return;
foreach (var fieldName in HeaderFieldMappings.Keys)
foreach (var kvp in HeaderFieldMappings)
{
// TODO: Impelement in DatHeader
//datHeader.SetField(fieldName);
datHeader.SetField(kvp.Key, kvp.Value);
}
}

View File

@@ -372,8 +372,6 @@ namespace SabreTools.DatItems
#endregion
// TODO: These should not take a field enum
// TODO: These should be item-specific for better filtering
#region Manipulation
/// <summary>

View File

@@ -1,10 +1,8 @@
using System;
using System.Linq;
using System.Reflection;
#if NET452_OR_GREATER || NETCOREAPP
using System.Xml.Serialization;
using SabreTools.Models;
#endif
using SabreTools.Models.Metadata;
namespace SabreTools.Filter
@@ -24,9 +22,12 @@ namespace SabreTools.Filter
return null;
#if NET20 || NET35 || NET40
// TODO: Figure out how to do this, try using the following:
// https://learn.microsoft.com/en-us/dotnet/api/system.reflection.customattributedata?view=net-8.0
return null;
return fields
.Where(f => f.IsLiteral && !f.IsInitOnly)
.Where(f => Attribute.GetCustomAttributes(f, typeof(NoFilterAttribute)).Length == 0)
.Select(f => f.GetRawConstantValue() as string)
.Where(v => v != null)
.ToArray()!;
#else
return fields
.Where(f => f.IsLiteral && !f.IsInitOnly)
@@ -72,9 +73,7 @@ namespace SabreTools.Filter
return null;
#if NET20 || NET35 || NET40
// TODO: Figure out how to do this, try using the following:
// https://learn.microsoft.com/en-us/dotnet/api/system.reflection.customattributedata?view=net-8.0
return null;
return (Attribute.GetCustomAttribute(type, typeof(XmlRootAttribute)) as XmlRootAttribute)!.ElementName;
#else
return type.GetCustomAttribute<XmlRootAttribute>()?.ElementName;
#endif

View File

@@ -1,181 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
namespace SabreTools.Filtering
{
/// <summary>
/// Represents a single filter within the overall filter
/// </summary>
/// <typeparam name="T">Generic type representing the filtered object</typeparam>
public class FilterItem<T>
{
/// <summary>
/// Single positive value for this filter
/// </summary>
public T? Positive { get; set; }
/// <summary>
/// List of positive values for this filter
/// </summary>
public List<T?> PositiveSet { get; set; } = new List<T?>();
/// <summary>
/// Single negative value for this filter
/// </summary>
public T? Negative { get; set; }
/// <summary>
/// List of negative values for this filter
/// </summary>
public List<T?> NegativeSet { get; set; } = new List<T?>();
/// <summary>
/// Single neutral value for this filter
/// </summary>
public T? Neutral { get; set; }
/// <summary>
/// List of neutral values for this filter
/// </summary>
public List<T?> NeutralSet { get; set; } = new List<T?>();
/// <summary>
/// Check if a value matches the positive filter
/// </summary>
/// <param name="def">Default value to check filter value</param>
/// <param name="value">Value to check</param>
/// <returns>True if the value was found in the positive filter, null on default value, false otherwise</returns>
public bool? MatchesPositive(T def, T value)
{
return Matches(this.Positive, def, value);
}
/// <summary>
/// Check if a value matches the negative filter
/// </summary>
/// <param name="def">Default value to check filter value</param>
/// <param name="value">Value to check</param>
/// <returns>True if the value was found in the negative filter, null on default value, false otherwise</returns>
public bool? MatchesNegative(T def, T value)
{
return Matches(this.Negative, def, value);
}
/// <summary>
/// Check if a value matches the neutral filter
/// </summary>
/// <param name="def">Default value to check filter value</param>
/// <param name="value">Value to check</param>
/// <returns>True if the value was found in the neutral filter, null on default value, false otherwise</returns>
public bool? MatchesNeutral(T def, T value)
{
return Matches(this.Neutral, def, value);
}
/// <summary>
/// Check if the given value matches any of the positive filters
/// </summary>
/// <param name="value">Value to check</param>
/// <returns>True if the value was found in a positive filter, null on an empty set, false otherwise</returns>
public bool? MatchesPositiveSet(T? value)
{
return MatchesSet(this.PositiveSet, value);
}
/// <summary>
/// Check if the given value matches any of the negative filters
/// </summary>
/// <param name="value">Value to check</param>
/// <returns>True if the value was found in a negative filter, null on an empty set, false otherwise</returns>
public bool? MatchesNegativeSet(T? value)
{
return MatchesSet(this.NegativeSet, value);
}
/// <summary>
/// Check if the given value matches any of the neutral filters
/// </summary>
/// <param name="value">Value to check</param>
/// <returns>True if the value was found in a neutral filter, null on an empty set, false otherwise</returns>
public bool? MatchesNeutralSet(T? value)
{
return MatchesSet(this.NeutralSet, value);
}
/// <summary>
/// Check if a value matches the supplied filter
/// </summary>
/// <param name="single">Value to check against</param>
/// <param name="def">Default value to check filter value</param>
/// <param name="value">Value to check</param>
/// <returns>True if the value was found in the supplied filter, null on default value, false otherwise</returns>
private static bool? Matches(T? single, T def, T value)
{
// If the filter is default, we ignore
if (single == null || single.Equals(def))
return null;
#if NETFRAMEWORK
// TODO: Fix flag matching
#else
// If we have a flag
if (typeof(T).IsEnum && (single as Enum)!.HasFlag((value as Enum)!))
return true;
#endif
return single.Equals(value);
}
/// <summary>
/// Check if a value matches the supplied filter
/// </summary>
/// <param name="set">Set to check against</param>
/// <param name="value">Value to check</param>
/// <returns>True if the value was found in the supplied filter, null on an empty set, false otherwise</returns>
private static bool? MatchesSet(List<T?> set, T? value)
{
if (set.Count == 0)
return null;
if (FindValueInList(set, value))
return true;
return false;
}
/// <summary>
/// Generic code to check if a specific value is in the list given
/// </summary>
/// <param name="haystack">List to search for the value in</param>
/// <param name="needle">Value to search the list for</param>
/// <returns>True if the value could be found, false otherwise</returns>
private static bool FindValueInList(List<T?> haystack, T? needle)
{
bool found = false;
foreach (T? straw in haystack)
{
if (straw is string strawString)
{
if (!string.IsNullOrEmpty(strawString) && needle is string needleString)
{
string regexStraw = strawString;
// If the straw has no special characters at all (excluding whitespace), treat it as an exact match
if (regexStraw == Regex.Escape(regexStraw).Replace("\\ ", " "))
regexStraw = $"^{regexStraw}$";
// Check if a match is found with the regex
found |= Regex.IsMatch(needleString, regexStraw, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
}
}
else
{
found |= (needle?.Equals(straw) ?? false);
}
}
return found;
}
}
}

View File

@@ -187,8 +187,7 @@ namespace SabreTools.Filtering
foreach (var fieldName in HeaderFieldNames)
{
// TODO: Impelement in DatHeader
//datHeader.RemoveField(fieldName);
datHeader.RemoveField(fieldName);
}
}

View File

@@ -79,13 +79,14 @@ namespace SabreTools.Filtering
#region Item-Specific
// Handle unnested sets first
// Handle normal sets first
foreach (var fieldName in fieldNames)
{
datItem.ReplaceField(repDatItem, fieldName);
}
// Handle nested sets
// TODO: Filter out hashes before here so these checks actually work
// Handle special cases
switch (datItem, repDatItem)
{
case (Disk disk, Disk repDisk): ReplaceFields(disk, repDisk, fieldNames); break;

View File

@@ -429,7 +429,6 @@ namespace SabreTools.Test.Core
}
// TODO: Write new test for all supported DatHeaderField values
// TODO: Write new test for all supported DatItemField values
[Theory]
[InlineData(DeviceType.NULL, null)]
@@ -607,8 +606,6 @@ namespace SabreTools.Test.Core
Assert.Equal(expected, actual);
}
// TODO: Write new test for all supported MachineField values
[Theory]
[InlineData(MachineType.None, true, "none")]
[InlineData(MachineType.None, false, "none")]

View File

@@ -819,11 +819,14 @@ Reset the internal state: reset();";
public override void Process(BatchState batchState)
{
// Read in the individual arguments
DatHeaderField field = Arguments[0].AsDatHeaderField();
string field = Arguments[0];
string value = Arguments[1];
var setter = new Setter();
setter.PopulateSetters(field, value);
// Set the header field
batchState.DatFile.Header.SetFields(new Dictionary<DatHeaderField, string> { [field] = value });
setter.SetFields(batchState.DatFile.Header);
}
}