From f0fa7bb6bfddf75044c32f7e90ef2fcb8bcc8b77 Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Wed, 6 Mar 2024 00:33:45 -0500 Subject: [PATCH] Address a handful of TODOs --- SabreTools.Core/Enums.cs | 1 + SabreTools.DatFiles/DatHeader.cs | 243 +++++++++++------------- SabreTools.DatFiles/Setter.cs | 5 +- SabreTools.DatItems/DatItem.cs | 2 - SabreTools.Filter/TypeHelper.cs | 15 +- SabreTools.Filtering/FilterItem.cs | 181 ------------------ SabreTools.Filtering/Remover.cs | 3 +- SabreTools.Filtering/Replacer.cs | 5 +- SabreTools.Test/Core/ConvertersTests.cs | 3 - SabreTools/Features/Batch.cs | 7 +- 10 files changed, 132 insertions(+), 333 deletions(-) delete mode 100644 SabreTools.Filtering/FilterItem.cs diff --git a/SabreTools.Core/Enums.cs b/SabreTools.Core/Enums.cs index ec7ebb08..2b7f1bb8 100644 --- a/SabreTools.Core/Enums.cs +++ b/SabreTools.Core/Enums.cs @@ -738,6 +738,7 @@ namespace SabreTools.Core #region Fields + // TODO: This should move to Models like the rest /// /// List of valid field types within a DatHeader /// diff --git a/SabreTools.DatFiles/DatHeader.cs b/SabreTools.DatFiles/DatHeader.cs index 31ce09be..0bbfc65c 100644 --- a/SabreTools.DatFiles/DatHeader.cs +++ b/SabreTools.DatFiles/DatHeader.cs @@ -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 - - /// - /// Set fields with given values - /// - /// Mappings dictionary - public void SetFields(Dictionary 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(); - - if (mappings.ContainsKey(DatHeaderField.ForceNodump)) - ForceNodump = mappings[DatHeaderField.ForceNodump].AsEnumValue(); - - if (mappings.ContainsKey(DatHeaderField.ForcePacking)) - ForcePacking = mappings[DatHeaderField.ForcePacking].AsEnumValue(); - - #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(); - - if (mappings.ContainsKey(DatHeaderField.BiosMode)) - BiosMode = mappings[DatHeaderField.BiosMode].AsEnumValue(); - - if (mappings.ContainsKey(DatHeaderField.SampleMode)) - SampleMode = mappings[DatHeaderField.SampleMode].AsEnumValue(); - - 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 /// @@ -732,6 +603,118 @@ namespace SabreTools.DatFiles #endregion + #region Manipulation + + //// + /// Remove a field from the header + /// + /// Field to remove + /// True if the removal was successful, false otherwise + 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; + } + + /// + /// Set a field in the header from a mapping string + /// + /// Field to set + /// String representing the value to set + /// True if the setting was successful, false otherwise + /// This only performs minimal validation before setting + 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(); 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(); break; + case DatHeaderField.ForceNodump: ForceNodump = value.AsEnumValue(); break; + case DatHeaderField.ForcePacking: ForcePacking = value.AsEnumValue(); 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(); break; + case DatHeaderField.RomTitle: RomTitle = value; break; + case DatHeaderField.RootDir: RootDir = value; break; + case DatHeaderField.SampleMode: SampleMode = value.AsEnumValue(); 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 /// diff --git a/SabreTools.DatFiles/Setter.cs b/SabreTools.DatFiles/Setter.cs index 90e34ba2..2d351c35 100644 --- a/SabreTools.DatFiles/Setter.cs +++ b/SabreTools.DatFiles/Setter.cs @@ -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); } } diff --git a/SabreTools.DatItems/DatItem.cs b/SabreTools.DatItems/DatItem.cs index 72d9e3a8..515ca25f 100644 --- a/SabreTools.DatItems/DatItem.cs +++ b/SabreTools.DatItems/DatItem.cs @@ -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 /// diff --git a/SabreTools.Filter/TypeHelper.cs b/SabreTools.Filter/TypeHelper.cs index 68514799..e060b801 100644 --- a/SabreTools.Filter/TypeHelper.cs +++ b/SabreTools.Filter/TypeHelper.cs @@ -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()?.ElementName; #endif diff --git a/SabreTools.Filtering/FilterItem.cs b/SabreTools.Filtering/FilterItem.cs deleted file mode 100644 index b6331359..00000000 --- a/SabreTools.Filtering/FilterItem.cs +++ /dev/null @@ -1,181 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text.RegularExpressions; - -namespace SabreTools.Filtering -{ - /// - /// Represents a single filter within the overall filter - /// - /// Generic type representing the filtered object - public class FilterItem - { - /// - /// Single positive value for this filter - /// - public T? Positive { get; set; } - - /// - /// List of positive values for this filter - /// - public List PositiveSet { get; set; } = new List(); - - /// - /// Single negative value for this filter - /// - public T? Negative { get; set; } - - /// - /// List of negative values for this filter - /// - public List NegativeSet { get; set; } = new List(); - - /// - /// Single neutral value for this filter - /// - public T? Neutral { get; set; } - - /// - /// List of neutral values for this filter - /// - public List NeutralSet { get; set; } = new List(); - - /// - /// Check if a value matches the positive filter - /// - /// Default value to check filter value - /// Value to check - /// True if the value was found in the positive filter, null on default value, false otherwise - public bool? MatchesPositive(T def, T value) - { - return Matches(this.Positive, def, value); - } - - /// - /// Check if a value matches the negative filter - /// - /// Default value to check filter value - /// Value to check - /// True if the value was found in the negative filter, null on default value, false otherwise - public bool? MatchesNegative(T def, T value) - { - return Matches(this.Negative, def, value); - } - - /// - /// Check if a value matches the neutral filter - /// - /// Default value to check filter value - /// Value to check - /// True if the value was found in the neutral filter, null on default value, false otherwise - public bool? MatchesNeutral(T def, T value) - { - return Matches(this.Neutral, def, value); - } - - /// - /// Check if the given value matches any of the positive filters - /// - /// Value to check - /// True if the value was found in a positive filter, null on an empty set, false otherwise - public bool? MatchesPositiveSet(T? value) - { - return MatchesSet(this.PositiveSet, value); - } - - /// - /// Check if the given value matches any of the negative filters - /// - /// Value to check - /// True if the value was found in a negative filter, null on an empty set, false otherwise - public bool? MatchesNegativeSet(T? value) - { - return MatchesSet(this.NegativeSet, value); - } - - /// - /// Check if the given value matches any of the neutral filters - /// - /// Value to check - /// True if the value was found in a neutral filter, null on an empty set, false otherwise - public bool? MatchesNeutralSet(T? value) - { - return MatchesSet(this.NeutralSet, value); - } - - /// - /// Check if a value matches the supplied filter - /// - /// Value to check against - /// Default value to check filter value - /// Value to check - /// True if the value was found in the supplied filter, null on default value, false otherwise - 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); - } - - /// - /// Check if a value matches the supplied filter - /// - /// Set to check against - /// Value to check - /// True if the value was found in the supplied filter, null on an empty set, false otherwise - private static bool? MatchesSet(List set, T? value) - { - if (set.Count == 0) - return null; - - if (FindValueInList(set, value)) - return true; - - return false; - } - - /// - /// Generic code to check if a specific value is in the list given - /// - /// List to search for the value in - /// Value to search the list for - /// True if the value could be found, false otherwise - private static bool FindValueInList(List 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; - } - } -} diff --git a/SabreTools.Filtering/Remover.cs b/SabreTools.Filtering/Remover.cs index 2d432c67..08808a2f 100644 --- a/SabreTools.Filtering/Remover.cs +++ b/SabreTools.Filtering/Remover.cs @@ -187,8 +187,7 @@ namespace SabreTools.Filtering foreach (var fieldName in HeaderFieldNames) { - // TODO: Impelement in DatHeader - //datHeader.RemoveField(fieldName); + datHeader.RemoveField(fieldName); } } diff --git a/SabreTools.Filtering/Replacer.cs b/SabreTools.Filtering/Replacer.cs index 2b76ca1b..6a4cab53 100644 --- a/SabreTools.Filtering/Replacer.cs +++ b/SabreTools.Filtering/Replacer.cs @@ -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; diff --git a/SabreTools.Test/Core/ConvertersTests.cs b/SabreTools.Test/Core/ConvertersTests.cs index 5ddea244..b6746e58 100644 --- a/SabreTools.Test/Core/ConvertersTests.cs +++ b/SabreTools.Test/Core/ConvertersTests.cs @@ -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")] diff --git a/SabreTools/Features/Batch.cs b/SabreTools/Features/Batch.cs index 7579072b..9f6d0f07 100644 --- a/SabreTools/Features/Batch.cs +++ b/SabreTools/Features/Batch.cs @@ -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 { [field] = value }); + setter.SetFields(batchState.DatFile.Header); } }