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 writing a SoftwareList /// internal partial class SoftwareList : DatFile { /// protected override ItemType[] GetSupportedTypes() { return new ItemType[] { ItemType.DipSwitch, ItemType.Disk, ItemType.Info, ItemType.Rom, ItemType.SharedFeature, }; } /// protected override List GetMissingRequiredFields(DatItem datItem) { List missingFields = new(); switch (datItem.ItemType) { case ItemType.DipSwitch: DipSwitch dipSwitch = datItem as DipSwitch; if (!dipSwitch.PartSpecified) { missingFields.Add(DatItemField.Part_Name); missingFields.Add(DatItemField.Part_Interface); } else { if (string.IsNullOrWhiteSpace(dipSwitch.Part.Name)) missingFields.Add(DatItemField.Part_Name); if (string.IsNullOrWhiteSpace(dipSwitch.Part.Interface)) missingFields.Add(DatItemField.Part_Interface); } if (string.IsNullOrWhiteSpace(dipSwitch.Name)) missingFields.Add(DatItemField.Name); if (string.IsNullOrWhiteSpace(dipSwitch.Tag)) missingFields.Add(DatItemField.Tag); if (string.IsNullOrWhiteSpace(dipSwitch.Mask)) missingFields.Add(DatItemField.Mask); if (dipSwitch.ValuesSpecified) { if (dipSwitch.Values.Any(dv => string.IsNullOrWhiteSpace(dv.Name))) missingFields.Add(DatItemField.Part_Feature_Name); if (dipSwitch.Values.Any(dv => string.IsNullOrWhiteSpace(dv.Value))) missingFields.Add(DatItemField.Part_Feature_Value); } break; case ItemType.Disk: Disk disk = datItem as Disk; if (!disk.PartSpecified) { missingFields.Add(DatItemField.Part_Name); missingFields.Add(DatItemField.Part_Interface); } else { if (string.IsNullOrWhiteSpace(disk.Part.Name)) missingFields.Add(DatItemField.Part_Name); if (string.IsNullOrWhiteSpace(disk.Part.Interface)) missingFields.Add(DatItemField.Part_Interface); } if (!disk.DiskAreaSpecified) { missingFields.Add(DatItemField.AreaName); } else { if (string.IsNullOrWhiteSpace(disk.DiskArea.Name)) missingFields.Add(DatItemField.AreaName); } if (string.IsNullOrWhiteSpace(disk.Name)) missingFields.Add(DatItemField.Name); break; case ItemType.Info: Info info = datItem as Info; if (string.IsNullOrWhiteSpace(info.Name)) missingFields.Add(DatItemField.Name); break; case ItemType.Rom: Rom rom = datItem as Rom; if (!rom.PartSpecified) { missingFields.Add(DatItemField.Part_Name); missingFields.Add(DatItemField.Part_Interface); } else { if (string.IsNullOrWhiteSpace(rom.Part.Name)) missingFields.Add(DatItemField.Part_Name); if (string.IsNullOrWhiteSpace(rom.Part.Interface)) missingFields.Add(DatItemField.Part_Interface); } if (!rom.DataAreaSpecified) { missingFields.Add(DatItemField.AreaName); missingFields.Add(DatItemField.AreaSize); } else { if (string.IsNullOrWhiteSpace(rom.DataArea.Name)) missingFields.Add(DatItemField.AreaName); if (!rom.DataArea.SizeSpecified) missingFields.Add(DatItemField.AreaSize); } break; case ItemType.SharedFeature: SharedFeature sharedFeature = datItem as SharedFeature; if (string.IsNullOrWhiteSpace(sharedFeature.Name)) missingFields.Add(DatItemField.Name); break; default: // Unsupported ItemTypes should be caught already return null; } return missingFields; } /// public override bool WriteToFile(string outfile, bool ignoreblanks = false, bool throwOnError = false) { try { logger.User($"Writing to '{outfile}'..."); var softwarelist = CreateSoftwareList(ignoreblanks); if (!Serialization.SoftawreList.SerializeToFileWithDocType(softwarelist, outfile)) { logger.Warning($"File '{outfile}' could not be written! See the log for more details."); return false; } } catch (Exception ex) when (!throwOnError) { logger.Error(ex); return false; } logger.User($"'{outfile}' written!{Environment.NewLine}"); return true; } #region Converters /// /// Create a SoftwareList from the current internal information /// /// True if blank roms should be skipped on output, false otherwise private Models.SoftwareList.SoftwareList CreateSoftwareList(bool ignoreblanks) { var softwarelist = new Models.SoftwareList.SoftwareList { Name = Header.Name, Description = Header.Description, Notes = Header.Comment, Software = CreateSoftware(ignoreblanks), }; return softwarelist; } /// /// Create an array of Software from the current internal information /// /// True if blank roms should be skipped on output, false otherwise private Models.SoftwareList.Software[]? CreateSoftware(bool ignoreblanks) { // If we don't have items, we can't do anything if (this.Items == null || !this.Items.Any()) return null; // Create a list of hold the games var software = new List(); // Loop through the sorted items and create games for them foreach (string key in Items.SortedKeys) { var items = Items.FilteredItems(key); if (items == null || !items.Any()) continue; // Get the first item for game information var machine = items[0].Machine; var sw = CreateSoftware(machine); // Create holders for all item types var infos = new List(); var sharedfeats = new List(); var parts = new List(); // Loop through and convert the items to respective lists for (int index = 0; index < items.Count; index++) { // Get the item var item = items[index]; // Check for a "null" item item = ProcessNullifiedItem(item); // Skip if we're ignoring the item if (ShouldIgnore(item, ignoreblanks)) continue; switch (item) { case Info info: infos.Add(CreateInfo(info)); break; case SharedFeature sharedFeature: sharedfeats.Add(CreateSharedFeat(sharedFeature)); break; case Rom rom: parts.Add(CreatePart(rom)); break; case Disk disk: parts.Add(CreatePart(disk)); break; case DipSwitch dipswitch: parts.Add(CreatePart(dipswitch)); break; } } // Process the parts to ensure we don't have duplicates if (parts.Count > 0) { var grouped = parts.GroupBy(p => p.Name); var tempParts = new List(); foreach (var grouping in grouped) { var tempPart = new Models.SoftwareList.Part(); var tempFeatures = new List(); var tempDataAreas = new List(); var tempDiskAreas = new List(); var tempDipSwitches = new List(); foreach (var part in grouping) { tempPart.Name ??= part.Name; tempPart.Interface ??= part.Interface; if (part.Feature != null) tempFeatures.AddRange(part.Feature); if (part.DataArea != null) tempDataAreas.AddRange(part.DataArea); if (part.DiskArea != null) tempDiskAreas.AddRange(part.DiskArea); if (part.DipSwitch != null) tempDipSwitches.AddRange(part.DipSwitch); } if (tempFeatures.Count > 0) tempPart.Feature = tempFeatures.ToArray(); if (tempDataAreas.Count > 0) tempPart.DataArea = tempDataAreas.ToArray(); if (tempDiskAreas.Count > 0) tempPart.DiskArea = tempDiskAreas.ToArray(); if (tempDipSwitches.Count > 0) tempPart.DipSwitch = tempDipSwitches.ToArray(); tempParts.Add(tempPart); } parts = tempParts; } // Assign the values to the game sw.Info = infos.ToArray(); sw.SharedFeat = sharedfeats.ToArray(); sw.Part = parts.ToArray(); // Add the game to the list software.Add(sw); } return software.ToArray(); } /// /// Create a Software from the current internal information /// private Models.SoftwareList.Software? CreateSoftware(Machine machine) { var software = new Models.SoftwareList.Software { Name = machine.Name, CloneOf = machine.CloneOf, Supported = machine.Supported.FromSupported(verbose: true), Description = machine.Description, Year = machine.Year, Publisher = machine.Publisher, Notes = machine.Comment, }; return software; } /// /// Create a Info from the current Info DatItem /// private static Models.SoftwareList.Info CreateInfo(Info item) { var info = new Models.SoftwareList.Info { Name = item.Name, Value = item.Value, }; return info; } /// /// Create a SharedFeat from the current SharedFeature DatItem /// private static Models.SoftwareList.SharedFeat CreateSharedFeat(SharedFeature item) { var sharedfeat = new Models.SoftwareList.SharedFeat { Name = item.Name, Value = item.Value, }; return sharedfeat; } /// /// Create a Part from the current Rom DatItem /// private static Models.SoftwareList.Part CreatePart(Rom item) { var part = new Models.SoftwareList.Part { Name = item.Part.Name, Interface = item.Part.Interface, Feature = CreateFeatures(item.Part.Features), DataArea = CreateDataAreas(item), DiskArea = null, DipSwitch = null, }; return part; } /// /// Create a Part from the current Disk DatItem /// private static Models.SoftwareList.Part CreatePart(Disk item) { var part = new Models.SoftwareList.Part { Name = item.Part.Name, Interface = item.Part.Interface, Feature = CreateFeatures(item.Part.Features), DataArea = null, DiskArea = CreateDiskAreas(item), DipSwitch = null, }; return part; } /// /// Create a Part from the current DipSwitch DatItem /// private static Models.SoftwareList.Part CreatePart(DipSwitch item) { var part = new Models.SoftwareList.Part { Name = item.Part.Name, Interface = item.Part.Interface, Feature = CreateFeatures(item.Part.Features), DataArea = null, DiskArea = null, DipSwitch = CreateDipSwitches(item), }; return part; } /// /// Create a Feature array from the current list of PartFeature DatItems /// private static Models.SoftwareList.Feature[]? CreateFeatures(List items) { // If we don't have features, we can't do anything if (items == null || !items.Any()) return null; var features = new List(); foreach (var item in items) { var feature = new Models.SoftwareList.Feature { Name = item.Name, Value = item.Value, }; features.Add(feature); } return features.ToArray(); } /// /// Create a DataArea array from the current Rom DatItem /// private static Models.SoftwareList.DataArea[]? CreateDataAreas(Rom item) { var dataArea = new Models.SoftwareList.DataArea { Name = item.DataArea.Name, Size = item.DataArea.Size?.ToString(), Width = item.DataArea.Width?.ToString(), Endianness = item.DataArea.Endianness.FromEndianness(), Rom = CreateRom(item), }; return new Models.SoftwareList.DataArea[] { dataArea }; } /// /// Create a Rom array from the current Rom DatItem /// private static Models.SoftwareList.Rom[]? CreateRom(Rom item) { var rom = new Models.SoftwareList.Rom { Name = item.Name, Size = item.Size?.ToString(), Length = null, CRC = item.CRC, SHA1 = item.SHA1, Offset = item.Offset, Value = item.Value, Status = item.ItemStatus.FromItemStatus(yesno: false), LoadFlag = item.LoadFlag.FromLoadFlag(), }; return new Models.SoftwareList.Rom[] { rom }; } /// /// Create a DiskArea array from the current Disk DatItem /// private static Models.SoftwareList.DiskArea[]? CreateDiskAreas(Disk item) { var diskArea = new Models.SoftwareList.DiskArea { Disk = CreateDisk(item), }; return new Models.SoftwareList.DiskArea[] { diskArea }; } /// /// Create a Disk array from the current Disk DatItem /// private static Models.SoftwareList.Disk[]? CreateDisk(Disk item) { var disk = new Models.SoftwareList.Disk { Name = item.Name, MD5 = item.MD5, SHA1 = item.SHA1, Status = item.ItemStatus.FromItemStatus(yesno: false), Writeable = item.Writable?.ToString(), }; return new Models.SoftwareList.Disk[] { disk }; } /// /// Create a DipSwitch array from the current DipSwitch DatItem /// private static Models.SoftwareList.DipSwitch[]? CreateDipSwitches(DipSwitch item) { var dipValues = new List(); foreach (var setting in item.Values ?? new List()) { var dipValue = new Models.SoftwareList.DipValue { Name = setting.Name, Value = setting.Value, Default = setting.Default?.ToString(), }; dipValues.Add(dipValue); } var dipSwitch = new Models.SoftwareList.DipSwitch { DipValue = dipValues.ToArray() }; return new Models.SoftwareList.DipSwitch[] { dipSwitch }; } #endregion } }