diff --git a/SabreTools.Library/DatFiles/ItemDictionary.cs b/SabreTools.Library/DatFiles/ItemDictionary.cs
index d12309dd..00f03938 100644
--- a/SabreTools.Library/DatFiles/ItemDictionary.cs
+++ b/SabreTools.Library/DatFiles/ItemDictionary.cs
@@ -125,6 +125,12 @@ namespace SabreTools.Library.DatFiles
[JsonIgnore]
public long DiskCount { get; private set; } = 0;
+ ///
+ /// Number of Feature items
+ ///
+ [JsonIgnore]
+ public long FeatureCount { get; private set; } = 0;
+
///
/// Number of Media items
///
@@ -533,6 +539,9 @@ namespace SabreTools.Library.DatFiles
NodumpCount += ((item as Disk).ItemStatus == ItemStatus.Nodump ? 1 : 0);
VerifiedCount += ((item as Disk).ItemStatus == ItemStatus.Verified ? 1 : 0);
break;
+ case ItemType.Feature:
+ FeatureCount++;
+ break;
case ItemType.Media:
MediaCount++;
MD5Count += (string.IsNullOrWhiteSpace((item as Media).MD5) ? 0 : 1);
@@ -686,6 +695,9 @@ namespace SabreTools.Library.DatFiles
NodumpCount -= ((item as Disk).ItemStatus == ItemStatus.Nodump ? 1 : 0);
VerifiedCount -= ((item as Disk).ItemStatus == ItemStatus.Verified ? 1 : 0);
break;
+ case ItemType.Feature:
+ FeatureCount--;
+ break;
case ItemType.Media:
MediaCount--;
MD5Count -= (string.IsNullOrWhiteSpace((item as Media).MD5) ? 0 : 1);
diff --git a/SabreTools.Library/DatFiles/Json.cs b/SabreTools.Library/DatFiles/Json.cs
index 7aaa8be0..a413d138 100644
--- a/SabreTools.Library/DatFiles/Json.cs
+++ b/SabreTools.Library/DatFiles/Json.cs
@@ -235,6 +235,9 @@ namespace SabreTools.Library.DatFiles
case ItemType.Disk:
datItem = datItemObj.ToObject();
break;
+ case ItemType.Feature:
+ datItem = datItemObj.ToObject();
+ break;
case ItemType.Media:
datItem = datItemObj.ToObject();
break;
@@ -257,7 +260,7 @@ namespace SabreTools.Library.DatFiles
datItem = datItemObj.ToObject();
break;
case ItemType.Sound:
- datItem = datItemObj.ToObject();
+ datItem = datItemObj.ToObject();
break;
}
}
diff --git a/SabreTools.Library/DatFiles/Listxml.cs b/SabreTools.Library/DatFiles/Listxml.cs
index d2e8d5c4..b27e5e75 100644
--- a/SabreTools.Library/DatFiles/Listxml.cs
+++ b/SabreTools.Library/DatFiles/Listxml.cs
@@ -289,6 +289,17 @@ namespace SabreTools.Library.DatFiles
reader.Read();
break;
+ case "feature":
+ datItems.Add(new Feature
+ {
+ Type = reader.GetAttribute("type"),
+ Status = reader.GetAttribute("status"),
+ Overall = reader.GetAttribute("overall"),
+ });
+
+ reader.Read();
+ break;
+
case "rom":
datItems.Add(new Rom
{
@@ -481,21 +492,6 @@ namespace SabreTools.Library.DatFiles
reader.Read();
break;
- case "feature":
- var feature = new Feature();
- feature.Type = reader.GetAttribute("type");
- feature.Status = reader.GetAttribute("status");
- feature.Overall = reader.GetAttribute("overall");
-
- // Ensure the list exists
- if (machine.Features == null)
- machine.Features = new List();
-
- machine.Features.Add(feature);
-
- reader.Read();
- break;
-
case "device":
var device = new Device();
device.Type = reader.GetAttribute("type");
@@ -1318,20 +1314,6 @@ namespace SabreTools.Library.DatFiles
xtw.WriteEndElement();
}
}
- if (datItem.Machine.Features != null)
- {
- foreach (var feature in datItem.Machine.Features)
- {
- xtw.WriteStartElement("feature");
-
- xtw.WriteOptionalAttributeString("type", feature.Type);
- xtw.WriteOptionalAttributeString("status", feature.Status);
- xtw.WriteOptionalAttributeString("overall", feature.Overall);
-
- // End feature
- xtw.WriteEndElement();
- }
- }
if (datItem.Machine.Devices != null)
{
foreach (var device in datItem.Machine.Devices)
@@ -1584,6 +1566,15 @@ namespace SabreTools.Library.DatFiles
xtw.WriteEndElement();
break;
+ case ItemType.Feature:
+ var feature = datItem as Feature;
+ xtw.WriteStartElement("feature");
+ xtw.WriteOptionalAttributeString("type", feature.Type);
+ xtw.WriteOptionalAttributeString("status", feature.Status);
+ xtw.WriteOptionalAttributeString("overall", feature.Overall);
+ xtw.WriteEndElement();
+ break;
+
case ItemType.RamOption:
var ramOption = datItem as RamOption;
xtw.WriteStartElement("ramoption");
diff --git a/SabreTools.Library/DatFiles/SabreDat.cs b/SabreTools.Library/DatFiles/SabreDat.cs
index 8deb4728..9adda339 100644
--- a/SabreTools.Library/DatFiles/SabreDat.cs
+++ b/SabreTools.Library/DatFiles/SabreDat.cs
@@ -1389,6 +1389,16 @@ namespace SabreTools.Library.DatFiles
xtw.WriteEndElement();
break;
+ case ItemType.Feature:
+ var feature = datItem as Feature;
+ xtw.WriteStartElement("file");
+ xtw.WriteAttributeString("type", "feature");
+ xtw.WriteOptionalAttributeString("type", feature.Type);
+ xtw.WriteOptionalAttributeString("status", feature.Status);
+ xtw.WriteOptionalAttributeString("overall", feature.Overall);
+ xtw.WriteEndElement();
+ break;
+
case ItemType.Media:
var media = datItem as Media;
xtw.WriteStartElement("file");
diff --git a/SabreTools.Library/DatItems/Auxiliary.cs b/SabreTools.Library/DatItems/Auxiliary.cs
index f42cedb2..0d0b6720 100644
--- a/SabreTools.Library/DatItems/Auxiliary.cs
+++ b/SabreTools.Library/DatItems/Auxiliary.cs
@@ -193,23 +193,6 @@ namespace SabreTools.Library.DatItems
public string Name { get; set; }
}
- ///
- /// Represents one ListXML feature
- ///
- /// TODO: Promote to DatItem level
- [JsonObject("feature")]
- public class Feature
- {
- [JsonProperty("type")]
- public string Type { get; set; } // TODO: (protection|palette|graphics|sound|controls|keyboard|mouse|microphone|camera|disk|printer|lan|wan|timing)
-
- [JsonProperty("status")]
- public string Status { get; set; } // TODO: (unemulated|imperfect)
-
- [JsonProperty("overall")]
- public string Overall { get; set; } // TODO: (unemulated|imperfect)
- }
-
///
/// Represents one ListXML input
///
diff --git a/SabreTools.Library/DatItems/DatItem.cs b/SabreTools.Library/DatItems/DatItem.cs
index 0b76e491..16de3d26 100644
--- a/SabreTools.Library/DatItems/DatItem.cs
+++ b/SabreTools.Library/DatItems/DatItem.cs
@@ -482,6 +482,9 @@ namespace SabreTools.Library.DatItems
case ItemType.Disk:
return new Disk();
+ case ItemType.Feature:
+ return new Feature();
+
case ItemType.Media:
return new Media();
@@ -521,6 +524,7 @@ namespace SabreTools.Library.DatItems
ItemType.DeviceReference => new DeviceReference(),
ItemType.DipSwitch => new DipSwitch(),
ItemType.Disk => new Disk(),
+ ItemType.Feature => new Feature(),
ItemType.Media => new Media(),
ItemType.RamOption => new RamOption(),
ItemType.Release => new Release(),
diff --git a/SabreTools.Library/DatItems/Enums.cs b/SabreTools.Library/DatItems/Enums.cs
index 5b8bb133..1e9762ed 100644
--- a/SabreTools.Library/DatItems/Enums.cs
+++ b/SabreTools.Library/DatItems/Enums.cs
@@ -190,12 +190,6 @@ namespace SabreTools.Library.DatItems
Machine_Driver_Cocktail,
Machine_Driver_SaveState,
- // Features
- Machine_Features,
- Machine_Feature_Type,
- Machine_Feature_Status,
- Machine_Feature_Overall,
-
// Devices
Machine_Devices,
Machine_Device_Type,
@@ -378,6 +372,11 @@ namespace SabreTools.Library.DatItems
DatItem_Value_Value,
DatItem_Value_Default,
+ // Feature
+ DatItem_FeatureType,
+ DatItem_FeatureStatus,
+ DatItem_FeatureOverall,
+
// Ram Option
DatItem_Content,
@@ -440,6 +439,7 @@ namespace SabreTools.Library.DatItems
Configuration,
DeviceReference,
DipSwitch,
+ Feature,
RamOption,
Release,
Sample,
diff --git a/SabreTools.Library/DatItems/Feature.cs b/SabreTools.Library/DatItems/Feature.cs
new file mode 100644
index 00000000..a1d9951b
--- /dev/null
+++ b/SabreTools.Library/DatItems/Feature.cs
@@ -0,0 +1,217 @@
+using System.Collections.Generic;
+using System.Linq;
+
+using SabreTools.Library.Filtering;
+using Newtonsoft.Json;
+
+namespace SabreTools.Library.DatItems
+{
+ ///
+ /// Represents the a feature of the machine
+ ///
+ [JsonObject("feature")]
+ public class Feature : DatItem
+ {
+ #region Fields
+
+ ///
+ /// Type of feature
+ ///
+ [JsonProperty("type")]
+ public string Type { get; set; } // TODO: (protection|palette|graphics|sound|controls|keyboard|mouse|microphone|camera|disk|printer|lan|wan|timing)
+
+ ///
+ /// Emulation status
+ ///
+ [JsonProperty("status")]
+ public string Status { get; set; } // TODO: (unemulated|imperfect)
+
+ ///
+ /// Overall status
+ ///
+ [JsonProperty("overall")]
+ public string Overall { get; set; } // TODO: (unemulated|imperfect)
+
+ #endregion
+
+ #region Accessors
+
+ ///
+ /// Set fields with given values
+ ///
+ /// Mappings dictionary
+ public override void SetFields(Dictionary mappings)
+ {
+ // Set base fields
+ base.SetFields(mappings);
+
+ // Handle Feature-specific fields
+ if (mappings.Keys.Contains(Field.DatItem_FeatureType))
+ Type = mappings[Field.DatItem_FeatureType];
+
+ if (mappings.Keys.Contains(Field.DatItem_FeatureStatus))
+ Status = mappings[Field.DatItem_FeatureStatus];
+
+ if (mappings.Keys.Contains(Field.DatItem_FeatureOverall))
+ Overall = mappings[Field.DatItem_FeatureOverall];
+ }
+
+ #endregion
+
+ #region Constructors
+
+ ///
+ /// Create a default, empty Feature object
+ ///
+ public Feature()
+ {
+ ItemType = ItemType.Feature;
+ }
+
+ #endregion
+
+ #region Cloning Methods
+
+ public override object Clone()
+ {
+ return new Feature()
+ {
+ ItemType = this.ItemType,
+ DupeType = this.DupeType,
+
+ AltName = this.AltName,
+ AltTitle = this.AltTitle,
+
+ Original = this.Original,
+ OpenMSXSubType = this.OpenMSXSubType,
+ OpenMSXType = this.OpenMSXType,
+ Remark = this.Remark,
+ Boot = this.Boot,
+
+ Part = this.Part,
+ Features = this.Features,
+ AreaName = this.AreaName,
+ AreaSize = this.AreaSize,
+ AreaWidth = this.AreaWidth,
+ AreaEndianness = this.AreaEndianness,
+ Value = this.Value,
+ LoadFlag = this.LoadFlag,
+
+ Machine = this.Machine.Clone() as Machine,
+ Source = this.Source.Clone() as Source,
+ Remove = this.Remove,
+
+ Type = this.Type,
+ Status = this.Status,
+ Overall = this.Overall,
+ };
+ }
+
+ #endregion
+
+ #region Comparision Methods
+
+ public override bool Equals(DatItem other)
+ {
+ // If we don't have a Feature, return false
+ if (ItemType != other.ItemType)
+ return false;
+
+ // Otherwise, treat it as a Feature
+ Feature newOther = other as Feature;
+
+ // If the Feature information matches
+ return (Type == newOther.Type && Status == newOther.Status && Overall == newOther.Overall);
+ }
+
+ #endregion
+
+ #region Filtering
+
+ ///
+ /// 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 type
+ if (filter.DatItem_FeatureType.MatchesPositiveSet(Type) == false)
+ return false;
+ if (filter.DatItem_FeatureType.MatchesNegativeSet(Type) == true)
+ return false;
+
+ // Filter on status
+ if (filter.DatItem_FeatureStatus.MatchesPositiveSet(Status) == false)
+ return false;
+ if (filter.DatItem_FeatureStatus.MatchesNegativeSet(Status) == true)
+ return false;
+
+ // Filter on overall
+ if (filter.DatItem_FeatureOverall.MatchesPositiveSet(Overall) == false)
+ return false;
+ if (filter.DatItem_FeatureOverall.MatchesNegativeSet(Overall) == 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_FeatureType))
+ Type = null;
+
+ if (fields.Contains(Field.DatItem_FeatureStatus))
+ Status = null;
+
+ if (fields.Contains(Field.DatItem_FeatureOverall))
+ Overall = null;
+ }
+
+ #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 Feature to replace from, ignore specific fields
+ if (item.ItemType != ItemType.Feature)
+ return;
+
+ // Cast for easier access
+ Feature newItem = item as Feature;
+
+ // Replace the fields
+ if (fields.Contains(Field.DatItem_FeatureType))
+ Type = newItem.Type;
+
+ if (fields.Contains(Field.DatItem_FeatureStatus))
+ Status = newItem.Status;
+
+ if (fields.Contains(Field.DatItem_FeatureOverall))
+ Overall = newItem.Overall;
+ }
+
+ #endregion
+ }
+}
diff --git a/SabreTools.Library/DatItems/Machine.cs b/SabreTools.Library/DatItems/Machine.cs
index 58a9b3df..e41a8fa9 100644
--- a/SabreTools.Library/DatItems/Machine.cs
+++ b/SabreTools.Library/DatItems/Machine.cs
@@ -182,12 +182,6 @@ namespace SabreTools.Library.DatItems
[JsonProperty("drivers", DefaultValueHandling = DefaultValueHandling.Ignore)]
public List Drivers { get; set; } = null;
- ///
- /// List of associated features
- ///
- [JsonProperty("features", DefaultValueHandling = DefaultValueHandling.Ignore)]
- public List Features { get; set; } = null;
-
///
/// List of associated devices
///
@@ -551,7 +545,6 @@ namespace SabreTools.Library.DatItems
Inputs = this.Inputs,
Ports = this.Ports,
Drivers = this.Drivers,
- Features = this.Features,
Devices = this.Devices,
#endregion
diff --git a/SabreTools.Library/Filtering/Filter.cs b/SabreTools.Library/Filtering/Filter.cs
index 2eac69ee..8850f014 100644
--- a/SabreTools.Library/Filtering/Filter.cs
+++ b/SabreTools.Library/Filtering/Filter.cs
@@ -112,12 +112,6 @@ namespace SabreTools.Library.Filtering
public FilterItem Machine_Driver_Cocktail { get; private set; } = new FilterItem();
public FilterItem Machine_Driver_SaveState { get; private set; } = new FilterItem();
- // Features
- public FilterItem Machine_Features { get; private set; } = new FilterItem() { Neutral = null };
- public FilterItem Machine_Feature_Type { get; private set; } = new FilterItem();
- public FilterItem Machine_Feature_Status { get; private set; } = new FilterItem();
- public FilterItem Machine_Feature_Overall { get; private set; } = new FilterItem();
-
// Devices
public FilterItem Machine_Devices { get; private set; } = new FilterItem() { Neutral = null };
public FilterItem Machine_Device_Type { get; private set; } = new FilterItem();
@@ -300,6 +294,12 @@ namespace SabreTools.Library.Filtering
public FilterItem DatItem_Value_Value { get; private set; } = new FilterItem();
public FilterItem DatItem_Value_Default { get; private set; } = new FilterItem() { Neutral = null };
+ // Feature
+ public FilterItem DatItem_FeatureType { get; private set; } = new FilterItem();
+ public FilterItem DatItem_FeatureStatus { get; private set; } = new FilterItem();
+ public FilterItem DatItem_FeatureOverall { get; private set; } = new FilterItem();
+
+
// Ram Option
public FilterItem DatItem_Content { get; private set; } = new FilterItem();
@@ -882,35 +882,6 @@ namespace SabreTools.Library.Filtering
Machine_Driver_SaveState.PositiveSet.Add(value);
break;
- // Features
- case Field.Machine_Features:
- if (negate || value.Equals("false", StringComparison.OrdinalIgnoreCase))
- Machine_Features.Neutral = false;
- else
- Machine_Features.Neutral = true;
- break;
-
- case Field.Machine_Feature_Type:
- if (negate)
- Machine_Feature_Type.NegativeSet.Add(value);
- else
- Machine_Feature_Type.PositiveSet.Add(value);
- break;
-
- case Field.Machine_Feature_Status:
- if (negate)
- Machine_Feature_Status.NegativeSet.Add(value);
- else
- Machine_Feature_Status.PositiveSet.Add(value);
- break;
-
- case Field.Machine_Feature_Overall:
- if (negate)
- Machine_Feature_Overall.NegativeSet.Add(value);
- else
- Machine_Feature_Overall.PositiveSet.Add(value);
- break;
-
// Devices
case Field.Machine_Devices:
if (negate || value.Equals("false", StringComparison.OrdinalIgnoreCase))
@@ -1726,6 +1697,28 @@ namespace SabreTools.Library.Filtering
DatItem_Value_Default.Neutral = true;
break;
+ // Feature
+ case Field.DatItem_FeatureType:
+ if (negate)
+ DatItem_FeatureType.NegativeSet.Add(value);
+ else
+ DatItem_FeatureType.PositiveSet.Add(value);
+ break;
+
+ case Field.DatItem_FeatureStatus:
+ if (negate)
+ DatItem_FeatureStatus.NegativeSet.Add(value);
+ else
+ DatItem_FeatureStatus.PositiveSet.Add(value);
+ break;
+
+ case Field.DatItem_FeatureOverall:
+ if (negate)
+ DatItem_FeatureOverall.NegativeSet.Add(value);
+ else
+ DatItem_FeatureOverall.PositiveSet.Add(value);
+ break;
+
// Ram Option
case Field.DatItem_Content:
if (negate)
diff --git a/SabreTools.Library/Tools/Converters.cs b/SabreTools.Library/Tools/Converters.cs
index ab95deb6..9b03d855 100644
--- a/SabreTools.Library/Tools/Converters.cs
+++ b/SabreTools.Library/Tools/Converters.cs
@@ -596,18 +596,6 @@ namespace SabreTools.Library.Tools
case "driver_savestate":
return Field.Machine_Driver_SaveState;
- case "features":
- return Field.Machine_Features;
-
- case "feature_type":
- return Field.Machine_Feature_Type;
-
- case "feature_status":
- return Field.Machine_Feature_Status;
-
- case "feature_overall":
- return Field.Machine_Feature_Overall;
-
case "devices":
return Field.Machine_Devices;
@@ -1014,6 +1002,16 @@ namespace SabreTools.Library.Tools
case "value_default":
return Field.DatItem_Value_Default;
+ // Feature
+ case "featuretype":
+ return Field.DatItem_FeatureType;
+
+ case "featurestatus":
+ return Field.DatItem_FeatureStatus;
+
+ case "featureoverall":
+ return Field.DatItem_FeatureOverall;
+
// Ram Option
case "content":
return Field.DatItem_Content;
@@ -1568,6 +1566,8 @@ namespace SabreTools.Library.Tools
return ItemType.DipSwitch;
case "disk":
return ItemType.Disk;
+ case "feature":
+ return ItemType.Feature;
case "media":
return ItemType.Media;
case "ramoption":
@@ -1599,6 +1599,7 @@ namespace SabreTools.Library.Tools
"device_ref" => ItemType.DeviceReference,
"dipswitch" => ItemType.DipSwitch,
"disk" => ItemType.Disk,
+ "feature" => ItemType.Feature,
"media" => ItemType.Media,
"ramoption" => ItemType.RamOption,
"release" => ItemType.Release,
@@ -2016,6 +2017,8 @@ namespace SabreTools.Library.Tools
return "dipswitch";
case ItemType.Disk:
return "disk";
+ case ItemType.Feature:
+ return "feature";
case ItemType.Media:
return "media";
case ItemType.RamOption:
@@ -2047,6 +2050,7 @@ namespace SabreTools.Library.Tools
ItemType.DeviceReference => "device_ref",
ItemType.DipSwitch => "dipswitch",
ItemType.Disk => "disk",
+ ItemType.Feature => "feature",
ItemType.Media => "media",
ItemType.RamOption => "ramoption",
ItemType.Release => "release",