Get rid of Software metadata item

Apparently this was a mistake that has proliferated over a very long time. The Software item that was being modeled was actually the game/machine equivilent from SoftwareList. But because there's both an item called SoftwareList and a DAT type called SoftwareList, some wire got crossed and they were mentally combined. Undoing this allows for a more proper internal model, including a couple of extraneous keys that were included originally.
This commit is contained in:
Matt Nadareski
2026-04-03 17:25:09 -04:00
parent 154be08a53
commit e01537c2f4
13 changed files with 49 additions and 356 deletions

View File

@@ -63,7 +63,6 @@ namespace SabreTools.Data.Extensions
SharedFeat sharedFeat => sharedFeat.Name,
Slot slot => slot.Name,
SlotOption slotOption => slotOption.Name,
Software software => software.Name,
SoftwareList softwareList => softwareList.Name,
Sound => null,
SourceDetails => null,
@@ -124,7 +123,6 @@ namespace SabreTools.Data.Extensions
case SharedFeat sharedFeat: sharedFeat.Name = name; break;
case Slot slot: slot.Name = name; break;
case SlotOption slotOption: slotOption.Name = name; break;
case Software software: software.Name = name; break;
case SoftwareList softwareList: softwareList.Name = name; break;
case Sound: break;
case SourceDetails: break;
@@ -186,6 +184,8 @@ namespace SabreTools.Data.Extensions
return sharedFeat.Clone() as SharedFeat;
else if (self is SlotOption slotOption)
return slotOption.Clone() as SlotOption;
else if (self is SoftwareList softwareList)
return softwareList.Clone() as SoftwareList;
else if (self is Sound sound)
return sound.Clone() as Sound;
else if (self is Video video)
@@ -329,19 +329,6 @@ namespace SabreTools.Data.Extensions
cloneRom.Status = selfRom.Status;
cloneRom.Value = selfRom.Value;
}
else if (self is Software selfSoftware && clone is Software cloneSoftware)
{
cloneSoftware.Description = selfSoftware.Description;
cloneSoftware.Supported = selfSoftware.Supported;
}
else if (self is SoftwareList selfSoftwareList && clone is SoftwareList cloneSoftwareList)
{
cloneSoftwareList.Description = selfSoftwareList.Description;
cloneSoftwareList.Filter = selfSoftwareList.Filter;
cloneSoftwareList.Notes = selfSoftwareList.Notes;
cloneSoftwareList.Status = selfSoftwareList.Status;
cloneSoftwareList.Tag = selfSoftwareList.Tag;
}
// Handle known properties
clone.SetName(self.GetName());

View File

@@ -309,7 +309,6 @@ namespace SabreTools.Data.Models.Metadata
SharedFeat,
Slot,
SlotOption,
Software,
SoftwareList,
Sound,
SourceDetails,

View File

@@ -1,50 +0,0 @@
using System.Xml.Serialization;
using Newtonsoft.Json;
namespace SabreTools.Data.Models.Metadata
{
[JsonObject("software"), XmlRoot("software")]
public class Software : DatItem
{
#region Properties
public string? Description { get; set; }
public string? Name { get; set; }
/// <remarks>(yes|partial|no) "yes"</remarks>
public Supported? Supported { get; set; }
#endregion
#region Keys
/// <remarks>string</remarks>
public const string CloneOfKey = "cloneof";
/// <remarks>Info[]</remarks>
[NoFilter]
public const string InfoKey = "info";
/// <remarks>string</remarks>
public const string NotesKey = "notes";
/// <remarks>Part[]</remarks>
[NoFilter]
public const string PartKey = "part";
/// <remarks>string</remarks>
public const string PublisherKey = "publisher";
/// <remarks>SharedFeat[]</remarks>
[NoFilter]
public const string SharedFeatKey = "sharedfeat";
/// <remarks>string</remarks>
public const string YearKey = "year";
#endregion
public Software() => ItemType = ItemType.Software;
}
}

View File

@@ -1,21 +1,18 @@
using System;
using System.Xml.Serialization;
using Newtonsoft.Json;
namespace SabreTools.Data.Models.Metadata
{
[JsonObject("softwarelist"), XmlRoot("softwarelist")]
public class SoftwareList : DatItem
public class SoftwareList : DatItem, ICloneable, IEquatable<SoftwareList>
{
#region Properties
public string? Description { get; set; }
public string? Filter { get; set; }
public string? Name { get; set; }
public string? Notes { get; set; }
/// <remarks>(original|compatible)</remarks>
public SoftwareListStatus? Status { get; set; }
@@ -23,14 +20,48 @@ namespace SabreTools.Data.Models.Metadata
#endregion
#region Keys
/// <remarks>Software[]</remarks>
[NoFilter]
public const string SoftwareKey = "software";
#endregion
public SoftwareList() => ItemType = ItemType.SoftwareList;
/// <inheritdoc/>
public object Clone()
{
var obj = new SoftwareList();
obj.Filter = Filter;
obj.Name = Name;
obj.Status = Status;
obj.Tag = Tag;
return obj;
}
/// <inheritdoc/>
public bool Equals(SoftwareList? other)
{
// Null never matches
if (other is null)
return false;
// Properties
if ((Filter is null) ^ (other.Filter is null))
return false;
else if (Filter is not null && !Filter.Equals(other.Filter, StringComparison.OrdinalIgnoreCase))
return false;
if ((Name is null) ^ (other.Name is null))
return false;
else if (Name is not null && !Name.Equals(other.Name, StringComparison.OrdinalIgnoreCase))
return false;
if (Status != other.Status)
return false;
if ((Tag is null) ^ (other.Tag is null))
return false;
else if (Tag is not null && !Tag.Equals(other.Tag, StringComparison.OrdinalIgnoreCase))
return false;
return true;
}
}
}

View File

@@ -928,32 +928,12 @@ namespace SabreTools.Metadata.DatFiles.Test
};
}
private static Data.Models.Metadata.Software CreateMetadataSoftware()
{
return new Data.Models.Metadata.Software
{
[Data.Models.Metadata.Software.CloneOfKey] = "cloneof",
Description = "description",
[Data.Models.Metadata.Software.InfoKey] = new Data.Models.Metadata.Info[] { CreateMetadataInfo() },
Name = "name",
[Data.Models.Metadata.Software.NotesKey] = "notes",
[Data.Models.Metadata.Software.PartKey] = new Data.Models.Metadata.Part[] { CreateMetadataPart() },
[Data.Models.Metadata.Software.PublisherKey] = "publisher",
[Data.Models.Metadata.Software.SharedFeatKey] = new Data.Models.Metadata.SharedFeat[] { CreateMetadataSharedFeat() },
Supported = Data.Models.Metadata.Supported.Yes,
[Data.Models.Metadata.Software.YearKey] = "year",
};
}
private static Data.Models.Metadata.SoftwareList CreateMetadataSoftwareList()
{
return new Data.Models.Metadata.SoftwareList
{
Description = "description",
Filter = "filter",
Name = "name",
Notes = "notes",
[Data.Models.Metadata.SoftwareList.SoftwareKey] = new Data.Models.Metadata.Software[] { CreateMetadataSoftware() },
Status = Data.Models.Metadata.SoftwareListStatus.Original,
Tag = "tag",
};
@@ -1627,44 +1607,10 @@ namespace SabreTools.Metadata.DatFiles.Test
private static void ValidateSoftwareList(SoftwareList? softwareList)
{
Assert.NotNull(softwareList);
Assert.Equal("description", softwareList.Description);
Assert.Equal("filter", softwareList.Filter);
Assert.Equal("name", softwareList.Name);
Assert.Equal("notes", softwareList.Notes);
Assert.Equal(Data.Models.Metadata.SoftwareListStatus.Original, softwareList.Status);
Assert.Equal("tag", softwareList.Tag);
Software[]? softwares = softwareList.Read<Software[]>(Data.Models.Metadata.SoftwareList.SoftwareKey);
Assert.NotNull(softwares);
Software? software = Assert.Single(softwares);
ValidateSoftware(software);
}
private static void ValidateSoftware(Software? software)
{
Assert.NotNull(software);
Assert.Equal("cloneof", software.ReadString(Data.Models.Metadata.Software.CloneOfKey));
Assert.Equal("description", software.Description);
Assert.Equal("name", software.Name);
Assert.Equal("notes", software.ReadString(Data.Models.Metadata.Software.NotesKey));
Assert.Equal("publisher", software.ReadString(Data.Models.Metadata.Software.PublisherKey));
Assert.Equal(Data.Models.Metadata.Supported.Yes, software.Supported);
Assert.Equal("year", software.ReadString(Data.Models.Metadata.Software.YearKey));
Info[]? infos = software.Read<Info[]>(Data.Models.Metadata.Software.InfoKey);
Assert.NotNull(infos);
Info? info = Assert.Single(infos);
ValidateInfo(info);
Part[]? parts = software.Read<Part[]>(Data.Models.Metadata.Software.PartKey);
Assert.NotNull(parts);
Part? part = Assert.Single(parts);
ValidatePart(part);
SharedFeat[]? sharedFeats = software.Read<SharedFeat[]>(Data.Models.Metadata.Software.SharedFeatKey);
Assert.NotNull(sharedFeats);
SharedFeat? sharedFeat = Assert.Single(sharedFeats);
ValidateSharedFeat(sharedFeat);
}
private static void ValidateSound(Sound? sound)

View File

@@ -1189,46 +1189,10 @@ namespace SabreTools.Metadata.DatFiles.Test
private static void ValidateMetadataSoftwareList(Data.Models.Metadata.SoftwareList? softwareList)
{
Assert.NotNull(softwareList);
Assert.Equal("description", softwareList.Description);
Assert.Equal("filter", softwareList.Filter);
Assert.Equal("name", softwareList.Name);
Assert.Equal("notes", softwareList.Notes);
Assert.Equal(Data.Models.Metadata.SoftwareListStatus.Original, softwareList.Status);
Assert.Equal("tag", softwareList.Tag);
Data.Models.Metadata.Software[]? softwares = softwareList.ReadArray<Data.Models.Metadata.Software>(Data.Models.Metadata.SoftwareList.SoftwareKey);
Assert.NotNull(softwares);
Data.Models.Metadata.Software? software = Assert.Single(softwares);
// TODO: Reenable when the mess that is the Software tree can be fixed
// ValidateMetadataSoftware(software);
}
private static void ValidateMetadataSoftware(Data.Models.Metadata.Software? software)
{
Assert.NotNull(software);
Assert.Equal("cloneof", software.ReadString(Data.Models.Metadata.Software.CloneOfKey));
Assert.Equal("description", software.Description);
Assert.Equal("name", software.Name);
Assert.Equal("notes", software.ReadString(Data.Models.Metadata.Software.NotesKey));
Assert.Equal("publisher", software.ReadString(Data.Models.Metadata.Software.PublisherKey));
Assert.Equal(Data.Models.Metadata.Supported.Yes, software.Supported);
Assert.Equal("year", software.ReadString(Data.Models.Metadata.Software.YearKey));
Data.Models.Metadata.Info[]? infos = software.Read<Data.Models.Metadata.Info[]>(Data.Models.Metadata.Software.InfoKey);
Assert.NotNull(infos);
Data.Models.Metadata.Info? info = Assert.Single(infos);
ValidateMetadataInfo(info);
Data.Models.Metadata.Part[]? parts = software.Read<Data.Models.Metadata.Part[]>(Data.Models.Metadata.Software.PartKey);
Assert.NotNull(parts);
Data.Models.Metadata.Part? part = Assert.Single(parts);
ValidateMetadataPart(part);
Data.Models.Metadata.SharedFeat[]? sharedFeats = software.Read<Data.Models.Metadata.SharedFeat[]>(Data.Models.Metadata.Software.SharedFeatKey);
Assert.NotNull(sharedFeats);
Data.Models.Metadata.SharedFeat? sharedFeat = Assert.Single(sharedFeats);
ValidateMetadataSharedFeat(sharedFeat);
}
private static void ValidateMetadataSound(Data.Models.Metadata.Sound? sound)

View File

@@ -389,7 +389,6 @@ namespace SabreTools.Metadata.DatFiles.Formats
// TODO: Implement these?
case Data.Models.Metadata.ItemType.Dump:
case Data.Models.Metadata.ItemType.Software:
case Data.Models.Metadata.ItemType.Video:
case Data.Models.Metadata.ItemType.NULL:
default:

View File

@@ -51,7 +51,6 @@ namespace SabreTools.Metadata.DatItems
[XmlInclude(typeof(SharedFeat))]
[XmlInclude(typeof(Slot))]
[XmlInclude(typeof(SlotOption))]
[XmlInclude(typeof(Software))]
[XmlInclude(typeof(SoftwareList))]
[XmlInclude(typeof(Sound))]
[XmlInclude(typeof(SourceDetails))]

View File

@@ -57,7 +57,6 @@ namespace SabreTools.Metadata.DatItems
"sharedfeat" or "shared_feat" or "sharedfeature" or "shared_feature" => Data.Models.Metadata.ItemType.SharedFeat,
"slot" => Data.Models.Metadata.ItemType.Slot,
"slotoption" or "slot_option" => Data.Models.Metadata.ItemType.SlotOption,
"software" => Data.Models.Metadata.ItemType.Software,
"softwarelist" or "software_list" => Data.Models.Metadata.ItemType.SoftwareList,
"sound" => Data.Models.Metadata.ItemType.Sound,
"source_details" or "sourcedetails" => Data.Models.Metadata.ItemType.SourceDetails,
@@ -141,7 +140,6 @@ namespace SabreTools.Metadata.DatItems
Data.Models.Metadata.ItemType.SharedFeat => "sharedfeat",
Data.Models.Metadata.ItemType.Slot => "slot",
Data.Models.Metadata.ItemType.SlotOption => "slotoption",
Data.Models.Metadata.ItemType.Software => "software",
Data.Models.Metadata.ItemType.SoftwareList => "softwarelist",
Data.Models.Metadata.ItemType.Sound => "sound",
Data.Models.Metadata.ItemType.SourceDetails => "source_details",

View File

@@ -1,115 +0,0 @@
using System;
using System.Xml.Serialization;
using Newtonsoft.Json;
using SabreTools.Data.Extensions;
namespace SabreTools.Metadata.DatItems.Formats
{
/// <summary>
/// Represents one ListXML software
/// </summary>
[JsonObject("software"), XmlRoot("software")]
public sealed class Software : DatItem<Data.Models.Metadata.Software>
{
#region Properties
public string? Description
{
get => (_internal as Data.Models.Metadata.Software)?.Description;
set => (_internal as Data.Models.Metadata.Software)?.Description = value;
}
/// <inheritdoc>/>
public override Data.Models.Metadata.ItemType ItemType
=> Data.Models.Metadata.ItemType.Software;
public string? Name
{
get => (_internal as Data.Models.Metadata.Software)?.Name;
set => (_internal as Data.Models.Metadata.Software)?.Name = value;
}
public Data.Models.Metadata.Supported? Supported
{
get => (_internal as Data.Models.Metadata.Software)?.Supported;
set => (_internal as Data.Models.Metadata.Software)?.Supported = value;
}
#endregion
#region Constructors
public Software() : base() { }
public Software(Data.Models.Metadata.Software item) : base(item)
{
// Handle subitems
var infos = item.ReadArray<Data.Models.Metadata.Info>(Data.Models.Metadata.Software.InfoKey);
if (infos is not null)
{
Info[] infoItems = Array.ConvertAll(infos, info => new Info(info));
Write<Info[]?>(Data.Models.Metadata.Software.InfoKey, infoItems);
}
var parts = item.ReadArray<Data.Models.Metadata.Part>(Data.Models.Metadata.Software.PartKey);
if (parts is not null)
{
Part[] partItems = Array.ConvertAll(parts, part => new Part(part));
Write<Part[]?>(Data.Models.Metadata.Software.PartKey, partItems);
}
var sharedFeats = item.ReadArray<Data.Models.Metadata.SharedFeat>(Data.Models.Metadata.Software.SharedFeatKey);
if (sharedFeats is not null)
{
SharedFeat[] sharedFeatItems = Array.ConvertAll(sharedFeats, sharedFeat => new SharedFeat(sharedFeat));
Write<SharedFeat[]?>(Data.Models.Metadata.Software.SharedFeatKey, sharedFeatItems);
}
}
public Software(Data.Models.Metadata.Software item, Machine machine, Source source) : this(item)
{
Source = source;
CopyMachineInformation(machine);
}
#endregion
#region Cloning Methods
/// <inheritdoc/>
/// <inheritdoc/>
public override object Clone() => new Software(_internal.DeepClone() as Data.Models.Metadata.Software ?? []);
/// <inheritdoc/>
public override Data.Models.Metadata.Software GetInternalClone()
{
var softwareItem = base.GetInternalClone();
var infos = Read<Info[]?>(Data.Models.Metadata.Software.InfoKey);
if (infos is not null)
{
Data.Models.Metadata.Info[] infoItems = Array.ConvertAll(infos, info => info.GetInternalClone());
softwareItem[Data.Models.Metadata.Software.InfoKey] = infoItems;
}
var parts = Read<Part[]?>(Data.Models.Metadata.Software.PartKey);
if (parts is not null)
{
Data.Models.Metadata.Part[] partItems = Array.ConvertAll(parts, part => part.GetInternalClone());
softwareItem[Data.Models.Metadata.Software.PartKey] = partItems;
}
var sharedFeats = Read<SharedFeat[]?>(Data.Models.Metadata.Software.SharedFeatKey);
if (sharedFeats is not null)
{
Data.Models.Metadata.SharedFeat[] sharedFeatItems = Array.ConvertAll(sharedFeats, sharedFeat => sharedFeat.GetInternalClone());
softwareItem[Data.Models.Metadata.Software.SharedFeatKey] = sharedFeatItems;
}
return softwareItem;
}
#endregion
}
}

View File

@@ -1,5 +1,4 @@
using System;
using System.Xml.Serialization;
using System.Xml.Serialization;
using Newtonsoft.Json;
using SabreTools.Data.Extensions;
@@ -13,12 +12,6 @@ namespace SabreTools.Metadata.DatItems.Formats
{
#region Fields
public string? Description
{
get => (_internal as Data.Models.Metadata.SoftwareList)?.Description;
set => (_internal as Data.Models.Metadata.SoftwareList)?.Description = value;
}
public string? Filter
{
get => (_internal as Data.Models.Metadata.SoftwareList)?.Filter;
@@ -35,12 +28,6 @@ namespace SabreTools.Metadata.DatItems.Formats
set => (_internal as Data.Models.Metadata.SoftwareList)?.Name = value;
}
public string? Notes
{
get => (_internal as Data.Models.Metadata.SoftwareList)?.Notes;
set => (_internal as Data.Models.Metadata.SoftwareList)?.Notes = value;
}
public Data.Models.Metadata.SoftwareListStatus? Status
{
get => (_internal as Data.Models.Metadata.SoftwareList)?.Status;
@@ -59,16 +46,7 @@ namespace SabreTools.Metadata.DatItems.Formats
public SoftwareList() : base() { }
public SoftwareList(Data.Models.Metadata.SoftwareList item) : base(item)
{
// Handle subitems
var softwares = item.ReadArray<Data.Models.Metadata.Software>(Data.Models.Metadata.SoftwareList.SoftwareKey);
if (softwares is not null)
{
Software[] softwareItems = Array.ConvertAll(softwares, software => new Software(software));
Write<Software[]?>(Data.Models.Metadata.SoftwareList.SoftwareKey, softwareItems);
}
}
public SoftwareList(Data.Models.Metadata.SoftwareList item) : base(item) { }
public SoftwareList(Data.Models.Metadata.SoftwareList item, Machine machine, Source source) : this(item)
{
@@ -83,21 +61,6 @@ namespace SabreTools.Metadata.DatItems.Formats
/// <inheritdoc/>
public override object Clone() => new SoftwareList(_internal.DeepClone() as Data.Models.Metadata.SoftwareList ?? []);
/// <inheritdoc/>
public override Data.Models.Metadata.SoftwareList GetInternalClone()
{
var softwareListItem = base.GetInternalClone();
var softwares = Read<Software[]?>(Data.Models.Metadata.SoftwareList.SoftwareKey);
if (softwares is not null)
{
Data.Models.Metadata.Software[] softwareItems = Array.ConvertAll(softwares, software => software.GetInternalClone());
softwareListItem[Data.Models.Metadata.SoftwareList.SoftwareKey] = softwareItems;
}
return softwareListItem;
}
#endregion
}
}

View File

@@ -829,22 +829,9 @@ namespace SabreTools.Metadata.Filter
checkValue = item.DevName;
return true;
case Software item when fieldName == "description":
checkValue = item.Description;
return true;
case Software item when fieldName == "supported":
checkValue = item.Supported?.AsStringValue();
return true;
case SoftwareList item when fieldName == "description":
checkValue = item.Description;
return true;
case SoftwareList item when fieldName == "filter":
checkValue = item.Filter;
return true;
case SoftwareList item when fieldName == "notes":
checkValue = item.Notes;
return true;
case SoftwareList item when fieldName == "status":
checkValue = item.Status?.AsStringValue();
return true;

View File

@@ -328,24 +328,9 @@ namespace SabreTools.Metadata
if (!selfSlotOption.Equals(otherSlotOption))
return false;
}
else if (self is Software selfSoftware && other is Software otherSoftware)
{
if (selfSoftware.Description != otherSoftware.Description)
return false;
if (selfSoftware.Supported != otherSoftware.Supported)
return false;
}
else if (self is SoftwareList selfSoftwareList && other is SoftwareList otherSoftwareList)
{
if (selfSoftwareList.Description != otherSoftwareList.Description)
return false;
if (selfSoftwareList.Filter != otherSoftwareList.Filter)
return false;
if (selfSoftwareList.Notes != otherSoftwareList.Notes)
return false;
if (selfSoftwareList.Status != otherSoftwareList.Status)
return false;
if (selfSoftwareList.Tag != otherSoftwareList.Tag)
if (!selfSoftwareList.Equals(otherSoftwareList))
return false;
}
else if (self is Sound selfSound && other is Sound otherSound)