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
}
}