using System; using System.Collections.Generic; using System.Linq; using SabreTools.Core; using SabreTools.Core.Tools; using SabreTools.DatItems; using SabreTools.DatItems.Formats; namespace SabreTools.DatFiles.Formats { /// /// Represents parsing a SoftwareList /// internal partial class SoftwareList : DatFile { /// public override void ParseFile(string filename, int indexId, bool keep, bool statsOnly = false, bool throwOnError = false) { try { // Deserialize the input file var softwarelist = new Serialization.Files.SoftwareList().Deserialize(filename); // Convert the header to the internal format ConvertHeader(softwarelist, keep); // Convert the software data to the internal format ConvertSoftware(softwarelist?.Software, filename, indexId, statsOnly); } catch (Exception ex) when (!throwOnError) { string message = $"'{filename}' - An error occurred during parsing"; logger.Error(ex, message); } } #region Converters /// /// Convert header information /// /// Deserialized model to convert /// True if full pathnames are to be kept, false otherwise (default) private void ConvertHeader(Models.SoftwareList.SoftwareList? softwarelist, bool keep) { // If the datafile is missing, we can't do anything if (softwarelist == null) return; if (Header.GetFieldValue(Models.Metadata.Header.NameKey) == null) Header.SetFieldValue(Models.Metadata.Header.NameKey, softwarelist.Name); if (Header.GetFieldValue(Models.Metadata.Header.DescriptionKey) == null) Header.SetFieldValue(Models.Metadata.Header.DescriptionKey, softwarelist.Description); if (Header.GetFieldValue(Models.Metadata.Header.CommentKey) == null) Header.SetFieldValue(Models.Metadata.Header.CommentKey, softwarelist.Notes); // Handle implied SuperDAT if (Header.GetFieldValue(Models.Metadata.Header.NameKey)?.Contains(" - SuperDAT") == true && keep) { if (Header.GetFieldValue(Models.Metadata.Header.TypeKey) == null) Header.SetFieldValue(Models.Metadata.Header.TypeKey, "SuperDAT"); } } /// /// Convert software information /// /// Array of deserialized models to convert /// Name of the file to be parsed /// Index ID for the DAT /// True to only add item statistics while parsing, false otherwise private void ConvertSoftware(Models.SoftwareList.Software[]? software, string filename, int indexId, bool statsOnly) { // If the game array is missing, we can't do anything if (software == null || !software.Any()) return; // Loop through the software and add foreach (var sw in software) { ConvertSoftware(sw, filename, indexId, statsOnly); } } /// /// Convert software information /// /// Deserialized model to convert /// Name of the file to be parsed /// Index ID for the DAT /// True to only add item statistics while parsing, false otherwise private void ConvertSoftware(Models.SoftwareList.Software software, string filename, int indexId, bool statsOnly) { // If the game is missing, we can't do anything if (software == null) return; // Create the machine for copying information var machine = new Machine(); machine.SetFieldValue(Models.Metadata.Machine.CloneOfKey, software.CloneOf); machine.SetFieldValue(Models.Metadata.Machine.CommentKey, software.Notes); machine.SetFieldValue(Models.Metadata.Machine.DescriptionKey, software.Description); machine.SetFieldValue(Models.Metadata.Machine.NameKey, software.Name); machine.SetFieldValue(Models.Metadata.Machine.PublisherKey, software.Publisher); machine.SetFieldValue(Models.Metadata.Machine.SupportedKey, software.Supported.AsEnumValue()); machine.SetFieldValue(Models.Metadata.Machine.YearKey, software.Year); // Add all Info objects foreach (var info in software.Info ?? []) { var infoItem = new Info(); infoItem.SetName(info.Name); infoItem.SetFieldValue(DatItem.SourceKey, new Source { Index = indexId, Name = filename }); infoItem.SetFieldValue(Models.Metadata.Info.ValueKey, info.Value); infoItem.CopyMachineInformation(machine); ParseAddHelper(infoItem, statsOnly); } // Add all SharedFeat objects foreach (var sharedfeat in software.SharedFeat ?? []) { var sharedfeatItem = new SharedFeat(); sharedfeatItem.SetName(sharedfeat.Name); sharedfeatItem.SetFieldValue(DatItem.SourceKey, new Source { Index = indexId, Name = filename }); sharedfeatItem.SetFieldValue(Models.Metadata.SharedFeat.ValueKey, sharedfeat.Value); sharedfeatItem.CopyMachineInformation(machine); ParseAddHelper(sharedfeatItem, statsOnly); } // Check if there are any items bool containsItems = false; // Loop through each type of item ConvertPart(software.Part, machine, filename, indexId, statsOnly, ref containsItems); // If we had no items, create a Blank placeholder if (!containsItems) { var blank = new Blank(); blank.SetFieldValue(DatItem.SourceKey, new Source { Index = indexId, Name = filename }); blank.CopyMachineInformation(machine); ParseAddHelper(blank, statsOnly); } } /// /// Convert Part information /// /// Array of deserialized models to convert /// Prefilled machine to use /// Name of the file to be parsed /// Index ID for the DAT /// True to only add item statistics while parsing, false otherwise /// True if there were any items in the array, false otherwise private void ConvertPart(Models.SoftwareList.Part[]? parts, Machine machine, string filename, int indexId, bool statsOnly, ref bool containsItems) { // If the parts array is missing, we can't do anything if (parts == null || !parts.Any()) return; foreach (var part in parts) { var item = new Part(); item.SetName(part.Name); item.SetFieldValue(Models.Metadata.Part.InterfaceKey, part.Interface); item.SetFieldValue(Models.Metadata.Part.FeatureKey, CreateFeatures(part.Feature, machine, filename, indexId, statsOnly)); item.SetFieldValue(DatItem.SourceKey, new Source { Index = indexId, Name = filename }); item.CopyMachineInformation(machine); ConvertDataArea(part.DataArea, item, machine, filename, indexId, statsOnly, ref containsItems); ConvertDiskArea(part.DiskArea, item, machine, filename, indexId, statsOnly, ref containsItems); ConvertDipSwitch(part.DipSwitch, item, machine, filename, indexId, statsOnly, ref containsItems); } } /// /// Convert Feature information /// /// Array of deserialized models to convert /// Prefilled machine to use /// Name of the file to be parsed /// Index ID for the DAT /// True to only add item statistics while parsing, false otherwise private static PartFeature[]? CreateFeatures(Models.SoftwareList.Feature[]? features, Machine machine, string filename, int indexId, bool statsOnly) { // If the feature array is missing, we can't do anything if (features == null || !features.Any()) return null; var partFeatures = new List(); foreach (var feature in features) { var item = new PartFeature(); item.SetName(feature.Name); item.SetFieldValue(DatItem.SourceKey, new Source { Index = indexId, Name = filename }); item.SetFieldValue(Models.Metadata.Feature.ValueKey, feature.Value); item.CopyMachineInformation(machine); partFeatures.Add(item); } return [.. partFeatures]; } /// /// Convert DataArea information /// /// Array of deserialized models to convert /// Parent Part to use /// Prefilled machine to use /// Name of the file to be parsed /// Index ID for the DAT /// True to only add item statistics while parsing, false otherwise /// True if there were any items in the array, false otherwise private void ConvertDataArea(Models.SoftwareList.DataArea[]? dataareas, Part part, Machine machine, string filename, int indexId, bool statsOnly, ref bool containsItems) { // If the dataarea array is missing, we can't do anything if (dataareas == null || !dataareas.Any()) return; foreach (var dataarea in dataareas) { var item = new DataArea(); item.SetName(dataarea.Name); item.SetFieldValue(Models.Metadata.DataArea.EndiannessKey, dataarea.Endianness.AsEnumValue()); item.SetFieldValue(Models.Metadata.DataArea.SizeKey, NumberHelper.ConvertToInt64(dataarea.Size)); item.SetFieldValue(DatItem.SourceKey, new Source { Index = indexId, Name = filename }); item.SetFieldValue(Models.Metadata.DataArea.WidthKey, NumberHelper.ConvertToInt64(dataarea.Width)); item.CopyMachineInformation(machine); ConvertRoms(dataarea.Rom, part, item, machine, filename, indexId, statsOnly, ref containsItems); } } /// /// Convert Rom information /// /// Array of deserialized models to convert /// Parent Part to use /// Parent DataArea to use /// Prefilled machine to use /// Name of the file to be parsed /// Index ID for the DAT /// True to only add item statistics while parsing, false otherwise /// True if there were any items in the array, false otherwise private void ConvertRoms(Models.SoftwareList.Rom[]? roms, Part part, DataArea dataarea, Machine machine, string filename, int indexId, bool statsOnly, ref bool containsItems) { // If the rom array is missing, we can't do anything if (roms == null || !roms.Any()) return; containsItems = true; foreach (var rom in roms) { var item = new Rom(); item.SetName(rom.Name); item.SetFieldValue(Models.Metadata.Rom.CRCKey, rom.CRC); item.SetFieldValue(Rom.DataAreaKey, dataarea); item.SetFieldValue(Models.Metadata.Rom.LoadFlagKey, rom.LoadFlag.AsEnumValue()); item.SetFieldValue(Models.Metadata.Rom.OffsetKey, rom.Offset); item.SetFieldValue(Rom.PartKey, part); item.SetFieldValue(Models.Metadata.Rom.SHA1Key, rom.SHA1); item.SetFieldValue(Models.Metadata.Rom.SizeKey, NumberHelper.ConvertToInt64(rom.Size ?? rom.Length)); item.SetFieldValue(DatItem.SourceKey, new Source { Index = indexId, Name = filename }); item.SetFieldValue(Models.Metadata.Rom.StatusKey, rom.Status.AsEnumValue()); item.SetFieldValue(Models.Metadata.Rom.ValueKey, rom.Value); item.CopyMachineInformation(machine); ParseAddHelper(item, statsOnly); } } /// /// Convert DiskArea information /// /// Array of deserialized models to convert /// Parent Part to use /// Prefilled machine to use /// Name of the file to be parsed /// Index ID for the DAT /// True to only add item statistics while parsing, false otherwise /// True if there were any items in the array, false otherwise private void ConvertDiskArea(Models.SoftwareList.DiskArea[]? diskareas, Part part, Machine machine, string filename, int indexId, bool statsOnly, ref bool containsItems) { // If the diskarea array is missing, we can't do anything if (diskareas == null || !diskareas.Any()) return; foreach (var diskarea in diskareas) { var item = new DiskArea(); item.SetName(diskarea.Name); item.SetFieldValue(DatItem.SourceKey, new Source { Index = indexId, Name = filename }); item.CopyMachineInformation(machine); ConvertDisks(diskarea.Disk, part, item, machine, filename, indexId, statsOnly, ref containsItems); } } /// /// Convert Disk information /// /// Array of deserialized models to convert /// Parent Part to use /// Parent DiskArea to use /// Prefilled machine to use /// Name of the file to be parsed /// Index ID for the DAT /// True to only add item statistics while parsing, false otherwise /// True if there were any items in the array, false otherwise private void ConvertDisks(Models.SoftwareList.Disk[]? disks, Part part, DiskArea diskarea, Machine machine, string filename, int indexId, bool statsOnly, ref bool containsItems) { // If the rom array is missing, we can't do anything if (disks == null || !disks.Any()) return; containsItems = true; foreach (var disk in disks) { var item = new Disk(); item.SetName(disk.Name); item.SetFieldValue(Disk.DiskAreaKey, diskarea); item.SetFieldValue(Models.Metadata.Disk.StatusKey, disk.Status?.AsEnumValue() ?? ItemStatus.NULL); item.SetFieldValue(Models.Metadata.Disk.MD5Key, disk.MD5); item.SetFieldValue(Disk.PartKey, part); item.SetFieldValue(Models.Metadata.Disk.SHA1Key, disk.SHA1); item.SetFieldValue(DatItem.SourceKey, new Source { Index = indexId, Name = filename }); item.SetFieldValue(Models.Metadata.Disk.WritableKey, disk.Writeable.AsYesNo()); item.CopyMachineInformation(machine); ParseAddHelper(item, statsOnly); } } /// /// Convert DipSwitch information /// /// Array of deserialized models to convert /// Parent Part to use /// Prefilled machine to use /// Name of the file to be parsed /// Index ID for the DAT /// True to only add item statistics while parsing, false otherwise /// True if there were any items in the array, false otherwise private void ConvertDipSwitch(Models.SoftwareList.DipSwitch[]? dipswitches, Part part, Machine machine, string filename, int indexId, bool statsOnly, ref bool containsItems) { // If the dipswitch array is missing, we can't do anything if (dipswitches == null || !dipswitches.Any()) return; foreach (var dipswitch in dipswitches) { var item = new DipSwitch(); item.SetName(dipswitch.Name); item.SetFieldValue(Models.Metadata.DipSwitch.DipValueKey, CreateDipValues(dipswitch.DipValue, machine, filename, indexId)?.ToArray()); item.SetFieldValue(DipSwitch.PartKey, part); item.SetFieldValue(Models.Metadata.DipSwitch.MaskKey, dipswitch.Mask); item.SetFieldValue(DatItem.SourceKey, new Source { Index = indexId, Name = filename }); item.SetFieldValue(Models.Metadata.DipSwitch.TagKey, dipswitch.Tag); item.CopyMachineInformation(machine); ParseAddHelper(item, statsOnly); } } /// /// Convert DipValue information /// /// Array of deserialized models to convert /// Prefilled machine to use /// Name of the file to be parsed /// Index ID for the DAT private static List? CreateDipValues(Models.SoftwareList.DipValue[]? dipvalues, Machine machine, string filename, int indexId) { // If the feature array is missing, we can't do anything if (dipvalues == null || !dipvalues.Any()) return null; var settings = new List(); foreach (var dipvalue in dipvalues) { var item = new DipValue(); item.SetName(dipvalue.Name); item.SetFieldValue(Models.Metadata.DipValue.DefaultKey, dipvalue.Default.AsYesNo()); item.SetFieldValue(DatItem.SourceKey, new Source { Index = indexId, Name = filename }); item.SetFieldValue(Models.Metadata.DipValue.ValueKey, dipvalue.Value); item.CopyMachineInformation(machine); settings.Add(item); } return settings; } #endregion } }