diff --git a/SabreTools.Library/DatFiles/ItemDictionary.cs b/SabreTools.Library/DatFiles/ItemDictionary.cs
index 7fa54108..31d211db 100644
--- a/SabreTools.Library/DatFiles/ItemDictionary.cs
+++ b/SabreTools.Library/DatFiles/ItemDictionary.cs
@@ -179,6 +179,12 @@ namespace SabreTools.Library.DatFiles
[JsonIgnore]
public long MediaCount { get; private set; } = 0;
+ ///
+ /// Number of PartFeature items
+ ///
+ [JsonIgnore]
+ public long PartFeatureCount { get; private set; } = 0;
+
///
/// Number of Port items
///
@@ -623,6 +629,9 @@ namespace SabreTools.Library.DatFiles
SHA1Count += (string.IsNullOrWhiteSpace((item as Media).SHA1) ? 0 : 1);
SHA256Count += (string.IsNullOrWhiteSpace((item as Media).SHA256) ? 0 : 1);
break;
+ case ItemType.PartFeature:
+ PartFeatureCount++;
+ break;
case ItemType.Port:
PortCount++;
break;
@@ -806,6 +815,9 @@ namespace SabreTools.Library.DatFiles
SHA1Count -= (string.IsNullOrWhiteSpace((item as Media).SHA1) ? 0 : 1);
SHA256Count -= (string.IsNullOrWhiteSpace((item as Media).SHA256) ? 0 : 1);
break;
+ case ItemType.PartFeature:
+ PartFeatureCount--;
+ break;
case ItemType.Port:
PortCount--;
break;
diff --git a/SabreTools.Library/DatFiles/Json.cs b/SabreTools.Library/DatFiles/Json.cs
index e00c074f..2244e670 100644
--- a/SabreTools.Library/DatFiles/Json.cs
+++ b/SabreTools.Library/DatFiles/Json.cs
@@ -274,6 +274,9 @@ namespace SabreTools.Library.DatFiles
case ItemType.Media:
datItem = datItemObj.ToObject();
break;
+ case ItemType.PartFeature:
+ datItem = datItemObj.ToObject();
+ break;
case ItemType.Port:
datItem = datItemObj.ToObject();
break;
diff --git a/SabreTools.Library/DatItems/Auxiliary.cs b/SabreTools.Library/DatItems/Auxiliary.cs
index 487f2e34..d2f0e8a1 100644
--- a/SabreTools.Library/DatItems/Auxiliary.cs
+++ b/SabreTools.Library/DatItems/Auxiliary.cs
@@ -4,9 +4,9 @@ using System.Linq;
using SabreTools.Library.DatItems;
using SabreTools.Library.Filtering;
+using SabreTools.Library.Tools;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
-using SabreTools.Library.Tools;
///
/// This holds all of the auxiliary types needed for proper parsing
@@ -103,20 +103,6 @@ namespace SabreTools.Library.DatItems
public List Features { get; set; }
}
- ///
- /// Represents one SoftwareList feature object
- ///
- /// TODO: Promote this to DatItem
- [JsonObject("part_feature")]
- public class PartFeature
- {
- [JsonProperty("name")]
- public string Name { get; set; }
-
- [JsonProperty("value")]
- public string Value { get; set; }
- }
-
#endregion
#endregion //DatItem
diff --git a/SabreTools.Library/DatItems/DatItem.cs b/SabreTools.Library/DatItems/DatItem.cs
index 4d1558e8..7e581abb 100644
--- a/SabreTools.Library/DatItems/DatItem.cs
+++ b/SabreTools.Library/DatItems/DatItem.cs
@@ -355,6 +355,9 @@ namespace SabreTools.Library.DatItems
case ItemType.Media:
return new Media();
+ case ItemType.PartFeature:
+ return new PartFeature();
+
case ItemType.Port:
return new Port();
@@ -414,6 +417,7 @@ namespace SabreTools.Library.DatItems
ItemType.Instance => new Instance(),
ItemType.Location => new Location(),
ItemType.Media => new Media(),
+ ItemType.PartFeature => new PartFeature(),
ItemType.Port => new Port(),
ItemType.RamOption => new RamOption(),
ItemType.Release => new Release(),
diff --git a/SabreTools.Library/DatItems/Display.cs b/SabreTools.Library/DatItems/Display.cs
index 7a1ab5d5..d184a3cb 100644
--- a/SabreTools.Library/DatItems/Display.cs
+++ b/SabreTools.Library/DatItems/Display.cs
@@ -1,8 +1,9 @@
using System.Collections.Generic;
using System.Linq;
+
using SabreTools.Library.Filtering;
-using Newtonsoft.Json;
using SabreTools.Library.Tools;
+using Newtonsoft.Json;
namespace SabreTools.Library.DatItems
{
diff --git a/SabreTools.Library/DatItems/Enums.cs b/SabreTools.Library/DatItems/Enums.cs
index e34ccaca..a3630729 100644
--- a/SabreTools.Library/DatItems/Enums.cs
+++ b/SabreTools.Library/DatItems/Enums.cs
@@ -483,6 +483,7 @@ namespace SabreTools.Library.DatItems
Input,
Instance,
Location,
+ PartFeature,
Port,
RamOption,
Release,
diff --git a/SabreTools.Library/DatItems/Info.cs b/SabreTools.Library/DatItems/Info.cs
index e742a3f1..ffd21dee 100644
--- a/SabreTools.Library/DatItems/Info.cs
+++ b/SabreTools.Library/DatItems/Info.cs
@@ -1,9 +1,10 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
+
using SabreTools.Library.Filtering;
-using Newtonsoft.Json;
using SabreTools.Library.Tools;
+using Newtonsoft.Json;
namespace SabreTools.Library.DatItems
{
diff --git a/SabreTools.Library/DatItems/Input.cs b/SabreTools.Library/DatItems/Input.cs
index 52b3d71a..545db0bf 100644
--- a/SabreTools.Library/DatItems/Input.cs
+++ b/SabreTools.Library/DatItems/Input.cs
@@ -1,8 +1,9 @@
using System.Collections.Generic;
using System.Linq;
+
using SabreTools.Library.Filtering;
-using Newtonsoft.Json;
using SabreTools.Library.Tools;
+using Newtonsoft.Json;
namespace SabreTools.Library.DatItems
{
diff --git a/SabreTools.Library/DatItems/Location.cs b/SabreTools.Library/DatItems/Location.cs
index 81401b41..69f467e1 100644
--- a/SabreTools.Library/DatItems/Location.cs
+++ b/SabreTools.Library/DatItems/Location.cs
@@ -1,9 +1,10 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
+
using SabreTools.Library.Filtering;
-using Newtonsoft.Json;
using SabreTools.Library.Tools;
+using Newtonsoft.Json;
namespace SabreTools.Library.DatItems
{
diff --git a/SabreTools.Library/DatItems/PartFeature.cs b/SabreTools.Library/DatItems/PartFeature.cs
new file mode 100644
index 00000000..4c327273
--- /dev/null
+++ b/SabreTools.Library/DatItems/PartFeature.cs
@@ -0,0 +1,226 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+
+using SabreTools.Library.Filtering;
+using SabreTools.Library.Tools;
+using Newtonsoft.Json;
+
+namespace SabreTools.Library.DatItems
+{
+ ///
+ /// Represents one part feature object
+ ///
+ [JsonObject("part_feature")]
+ public class PartFeature : DatItem
+ {
+ #region Fields
+
+ ///
+ /// Name of the item
+ ///
+ [JsonProperty("name")]
+ public string Name { get; set; }
+
+ ///
+ /// PartFeature value
+ ///
+ [JsonProperty("value")]
+ public string Value { get; set; }
+
+ #endregion
+
+ #region Accessors
+
+ ///
+ /// Gets the name to use for a DatItem
+ ///
+ /// Name if available, null otherwise
+ public override string GetName()
+ {
+ return Name;
+ }
+
+ ///
+ /// Set fields with given values
+ ///
+ /// Mappings dictionary
+ public override void SetFields(Dictionary mappings)
+ {
+ // Set base fields
+ base.SetFields(mappings);
+
+ // Handle PartFeature-specific fields
+ if (mappings.Keys.Contains(Field.DatItem_Name))
+ Name = mappings[Field.DatItem_Name];
+
+ if (mappings.Keys.Contains(Field.DatItem_Value))
+ Value = mappings[Field.DatItem_Value];
+ }
+
+ #endregion
+
+ #region Constructors
+
+ ///
+ /// Create a default, empty PartFeature object
+ ///
+ public PartFeature()
+ {
+ Name = string.Empty;
+ ItemType = ItemType.PartFeature;
+ }
+
+ #endregion
+
+ #region Cloning Methods
+
+ public override object Clone()
+ {
+ return new PartFeature()
+ {
+ ItemType = this.ItemType,
+ DupeType = this.DupeType,
+
+ Machine = this.Machine.Clone() as Machine,
+ Source = this.Source.Clone() as Source,
+ Remove = this.Remove,
+
+ Name = this.Name,
+ Value = this.Value,
+ };
+ }
+
+ #endregion
+
+ #region Comparision Methods
+
+ public override bool Equals(DatItem other)
+ {
+ // If we don't have a sample, return false
+ if (ItemType != other.ItemType)
+ return false;
+
+ // Otherwise, treat it as a PartFeature
+ PartFeature newOther = other as PartFeature;
+
+ // If the archive information matches
+ return (Name == newOther.Name && Value == newOther.Value);
+ }
+
+ #endregion
+
+ #region Filtering
+
+ ///
+ /// Clean a DatItem according to the cleaner
+ ///
+ /// Cleaner to implement
+ public override void Clean(Cleaner cleaner)
+ {
+ // Clean common items first
+ base.Clean(cleaner);
+
+ // If we're stripping unicode characters, strip item name
+ if (cleaner?.RemoveUnicode == true)
+ Name = Sanitizer.RemoveUnicodeCharacters(Name);
+
+ // If we are in NTFS trim mode, trim the game name
+ if (cleaner?.Trim == true)
+ {
+ // Windows max name length is 260
+ int usableLength = 260 - Machine.Name.Length - (cleaner.Root?.Length ?? 0);
+ if (Name.Length > usableLength)
+ {
+ string ext = Path.GetExtension(Name);
+ Name = Name.Substring(0, usableLength - ext.Length);
+ Name += ext;
+ }
+ }
+ }
+
+ ///
+ /// Check to see if a DatItem passes the filter
+ ///
+ /// Filter to check against
+ /// True if the item passed the filter, false otherwise
+ public override bool PassesFilter(Filter filter)
+ {
+ // Check common fields first
+ if (!base.PassesFilter(filter))
+ return false;
+
+ // Filter on item name
+ if (filter.DatItem_Name.MatchesPositiveSet(Name) == false)
+ return false;
+ if (filter.DatItem_Name.MatchesNegativeSet(Name) == true)
+ return false;
+
+ // Filter on info value
+ if (filter.DatItem_Value.MatchesPositiveSet(Value) == false)
+ return false;
+ if (filter.DatItem_Value.MatchesNegativeSet(Value) == true)
+ return false;
+
+ return true;
+ }
+
+ ///
+ /// Remove fields from the DatItem
+ ///
+ /// List of Fields to remove
+ public override void RemoveFields(List fields)
+ {
+ // Remove common fields first
+ base.RemoveFields(fields);
+
+ // Remove the fields
+ if (fields.Contains(Field.DatItem_Name))
+ Name = null;
+
+ if (fields.Contains(Field.DatItem_Value))
+ Value = null;
+ }
+
+ ///
+ /// Set internal names to match One Rom Per Game (ORPG) logic
+ ///
+ public override void SetOneRomPerGame()
+ {
+ string[] splitname = Name.Split('.');
+ Machine.Name += $"/{string.Join(".", splitname.Take(splitname.Length > 1 ? splitname.Length - 1 : 1))}";
+ Name = Path.GetFileName(Name);
+ }
+
+ #endregion
+
+ #region Sorting and Merging
+
+ ///
+ /// Replace fields from another item
+ ///
+ /// DatItem to pull new information from
+ /// List of Fields representing what should be updated
+ public override void ReplaceFields(DatItem item, List fields)
+ {
+ // Replace common fields first
+ base.ReplaceFields(item, fields);
+
+ // If we don't have a PartFeature to replace from, ignore specific fields
+ if (item.ItemType != ItemType.PartFeature)
+ return;
+
+ // Cast for easier access
+ PartFeature newItem = item as PartFeature;
+
+ // Replace the fields
+ if (fields.Contains(Field.DatItem_Name))
+ Name = newItem.Name;
+
+ if (fields.Contains(Field.DatItem_Value))
+ Value = newItem.Value;
+ }
+
+ #endregion
+ }
+}
diff --git a/SabreTools.Library/DatItems/Port.cs b/SabreTools.Library/DatItems/Port.cs
index bb9b3692..29dbdfe4 100644
--- a/SabreTools.Library/DatItems/Port.cs
+++ b/SabreTools.Library/DatItems/Port.cs
@@ -1,9 +1,7 @@
using System.Collections.Generic;
-using System.IO;
using System.Linq;
using SabreTools.Library.Filtering;
-using SabreTools.Library.Tools;
using Newtonsoft.Json;
namespace SabreTools.Library.DatItems
diff --git a/SabreTools.Library/DatItems/SharedFeature.cs b/SabreTools.Library/DatItems/SharedFeature.cs
index 9fabfca9..ee6b1b21 100644
--- a/SabreTools.Library/DatItems/SharedFeature.cs
+++ b/SabreTools.Library/DatItems/SharedFeature.cs
@@ -1,9 +1,10 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
+
using SabreTools.Library.Filtering;
-using Newtonsoft.Json;
using SabreTools.Library.Tools;
+using Newtonsoft.Json;
namespace SabreTools.Library.DatItems
{
diff --git a/SabreTools.Library/DatItems/SlotOption.cs b/SabreTools.Library/DatItems/SlotOption.cs
index bb110008..515c4be5 100644
--- a/SabreTools.Library/DatItems/SlotOption.cs
+++ b/SabreTools.Library/DatItems/SlotOption.cs
@@ -1,9 +1,10 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
+
using SabreTools.Library.Filtering;
-using Newtonsoft.Json;
using SabreTools.Library.Tools;
+using Newtonsoft.Json;
namespace SabreTools.Library.DatItems
{
diff --git a/SabreTools.Library/Filtering/Filter.cs b/SabreTools.Library/Filtering/Filter.cs
index 252e0c47..ab34a114 100644
--- a/SabreTools.Library/Filtering/Filter.cs
+++ b/SabreTools.Library/Filtering/Filter.cs
@@ -11,9 +11,9 @@ 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?
- /// TODO: Field name for filter population needs to be overhauled
public class Filter
{
+ // TODO: Reorder once all reorganization is done
#region Fields
#region Machine Filters
diff --git a/SabreTools.Library/Tools/Converters.cs b/SabreTools.Library/Tools/Converters.cs
index 7d528a6e..23aff73d 100644
--- a/SabreTools.Library/Tools/Converters.cs
+++ b/SabreTools.Library/Tools/Converters.cs
@@ -1650,6 +1650,9 @@ namespace SabreTools.Library.Tools
return ItemType.Location;
case "media":
return ItemType.Media;
+ case "partfeature":
+ case "part_feature":
+ return ItemType.PartFeature;
case "port":
return ItemType.Port;
case "ramoption":
@@ -1700,6 +1703,8 @@ namespace SabreTools.Library.Tools
"instance" => ItemType.Instance,
"location" => ItemType.Location,
"media" => ItemType.Media,
+ "partfeature" => ItemType.PartFeature,
+ "part_feature" => ItemType.PartFeature,
"port" => ItemType.Port,
"ramoption" => ItemType.RamOption,
"release" => ItemType.Release,
@@ -2297,6 +2302,8 @@ namespace SabreTools.Library.Tools
return "location";
case ItemType.Media:
return "media";
+ case ItemType.PartFeature:
+ return "part_feature";
case ItemType.Port:
return "port";
case ItemType.RamOption:
@@ -2347,6 +2354,7 @@ namespace SabreTools.Library.Tools
ItemType.Instance => "instance",
ItemType.Location => "location",
ItemType.Media => "media",
+ ItemType.PartFeature => "part_feature",
ItemType.Port => "port",
ItemType.RamOption => "ramoption",
ItemType.Release => "release",