using System.Collections.Generic; using System.Linq; using SabreTools.Core.Tools; using SabreTools.DatItems; namespace SabreTools.DatFiles { public partial class DatFile { #region To Metadata /// /// Convert metadata information /// public Models.Metadata.MetadataFile? ConvertMetadata(bool ignoreblanks = false) { // If we don't have items, we can't do anything if (Items == null || Items.Count == 0) return null; // Create an object to hold the data var metadataFile = new Models.Metadata.MetadataFile(); // Convert and assign the header var header = ConvertHeader(); if (header != null) metadataFile[Models.Metadata.MetadataFile.HeaderKey] = header; // Convert and assign the machines var machines = ConvertMachines(ignoreblanks); if (machines != null) metadataFile[Models.Metadata.MetadataFile.MachineKey] = machines; return metadataFile; } /// /// Convert metadata information /// public Models.Metadata.MetadataFile? ConvertMetadataDB(bool ignoreblanks = false) { // If we don't have items, we can't do anything if (ItemsDB == null) return null; // Create an object to hold the data var metadataFile = new Models.Metadata.MetadataFile(); // Convert and assign the header var header = ConvertHeader(); if (header != null) metadataFile[Models.Metadata.MetadataFile.HeaderKey] = header; // Convert and assign the machines var machines = ConvertMachinesDB(ignoreblanks); if (machines != null) metadataFile[Models.Metadata.MetadataFile.MachineKey] = machines; return metadataFile; } /// /// Convert header information /// private Models.Metadata.Header? ConvertHeader() { // If the header is invalid, we can't do anything if (Header == null) return null; // Create an internal header var header = Header.GetInternalClone(); // Remove fields with default values if (header.Read(Models.Metadata.Header.ForceMergingKey) == MergingFlag.None) header.Remove(Models.Metadata.Header.ForceMergingKey); if (header.Read(Models.Metadata.Header.ForceNodumpKey) == NodumpFlag.None) header.Remove(Models.Metadata.Header.ForceNodumpKey); if (header.Read(Models.Metadata.Header.ForcePackingKey) == PackingFlag.None) header.Remove(Models.Metadata.Header.ForcePackingKey); // Convert subheader values if (Header.CanOpenSpecified) header[Models.Metadata.Header.CanOpenKey] = new Models.OfflineList.CanOpen { Extension = Header.GetStringArrayFieldValue(Models.Metadata.Header.CanOpenKey) }; if (Header.ImagesSpecified) header[Models.Metadata.Header.ImagesKey] = Header.GetFieldValue(Models.Metadata.Header.ImagesKey); if (Header.InfosSpecified) header[Models.Metadata.Header.InfosKey] = Header.GetFieldValue(Models.Metadata.Header.InfosKey); if (Header.NewDatSpecified) header[Models.Metadata.Header.NewDatKey] = Header.GetFieldValue(Models.Metadata.Header.NewDatKey); if (Header.SearchSpecified) header[Models.Metadata.Header.SearchKey] = Header.GetFieldValue(Models.Metadata.Header.SearchKey); return header; } /// /// Convert machines information /// private Models.Metadata.Machine[]? ConvertMachines(bool ignoreblanks = false) { // Create a machine list to hold all outputs List machines = []; // 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.Count == 0) continue; // Create a machine to hold everything var machine = items[0].GetFieldValue(DatItems.DatItem.MachineKey)!.GetInternalClone(); // Handle Trurip object, if it exists if (machine.ContainsKey(Models.Metadata.Machine.TruripKey)) { var trurip = machine.Read(Models.Metadata.Machine.TruripKey); if (trurip != null) { var truripItem = trurip.ConvertToLogiqx(); truripItem.Publisher = machine.ReadString(Models.Metadata.Machine.PublisherKey); truripItem.Year = machine.ReadString(Models.Metadata.Machine.YearKey); truripItem.Players = machine.ReadString(Models.Metadata.Machine.PlayersKey); truripItem.Source = machine.ReadString(Models.Metadata.Machine.SourceFileKey); truripItem.CloneOf = machine.ReadString(Models.Metadata.Machine.CloneOfKey); machine[Models.Metadata.Machine.TruripKey] = truripItem; } } // Create mapping dictionaries for the Parts, DataAreas, and DiskAreas associated with this machine Dictionary partMappings = []; Dictionary dataAreaMappings = []; Dictionary diskAreaMappings = []; // 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 DatItems.Formats.Adjuster adjuster: var adjusterItem = ProcessItem(adjuster); EnsureMachineKey(machine, Models.Metadata.Machine.AdjusterKey); AppendToMachineKey(machine, Models.Metadata.Machine.AdjusterKey, adjusterItem); break; case DatItems.Formats.Archive archive: var archiveItem = archive.GetInternalClone(); EnsureMachineKey(machine, Models.Metadata.Machine.ArchiveKey); AppendToMachineKey(machine, Models.Metadata.Machine.ArchiveKey, archiveItem); break; case DatItems.Formats.BiosSet biosSet: var biosSetItem = biosSet.GetInternalClone(); EnsureMachineKey(machine, Models.Metadata.Machine.BiosSetKey); AppendToMachineKey(machine, Models.Metadata.Machine.BiosSetKey, biosSetItem); break; case DatItems.Formats.Chip chip: var chipItem = chip.GetInternalClone(); EnsureMachineKey(machine, Models.Metadata.Machine.ChipKey); AppendToMachineKey(machine, Models.Metadata.Machine.ChipKey, chipItem); break; case DatItems.Formats.Configuration configuration: var configurationItem = ProcessItem(configuration); EnsureMachineKey(machine, Models.Metadata.Machine.ConfigurationKey); AppendToMachineKey(machine, Models.Metadata.Machine.ConfigurationKey, configurationItem); break; case DatItems.Formats.Device device: var deviceItem = ProcessItem(device); EnsureMachineKey(machine, Models.Metadata.Machine.DeviceKey); AppendToMachineKey(machine, Models.Metadata.Machine.DeviceKey, deviceItem); break; case DatItems.Formats.DeviceRef deviceRef: var deviceRefItem = deviceRef.GetInternalClone(); EnsureMachineKey(machine, Models.Metadata.Machine.DeviceRefKey); AppendToMachineKey(machine, Models.Metadata.Machine.DeviceRefKey, deviceRefItem); break; case DatItems.Formats.DipSwitch dipSwitch: var dipSwitchItem = ProcessItem(dipSwitch); EnsureMachineKey(machine, Models.Metadata.Machine.DipSwitchKey); AppendToMachineKey(machine, Models.Metadata.Machine.DipSwitchKey, dipSwitchItem); // Add Part mapping bool dipSwitchContainsPart = dipSwitchItem.ContainsKey(DatItems.Formats.DipSwitch.PartKey); if (dipSwitchContainsPart) { var partItem = dipSwitchItem.Read(DatItems.Formats.DipSwitch.PartKey); if (partItem != null) partMappings[partItem.GetInternalClone()] = dipSwitchItem; } break; case DatItems.Formats.Disk disk: var diskItem = disk.GetInternalClone(); EnsureMachineKey(machine, Models.Metadata.Machine.DiskKey); AppendToMachineKey(machine, Models.Metadata.Machine.DiskKey, diskItem); // Add Part and DiskArea mappings bool diskContainsPart = diskItem.ContainsKey(DatItems.Formats.Disk.PartKey); bool diskContainsDiskArea = diskItem.ContainsKey(DatItems.Formats.Disk.DiskAreaKey); if (diskContainsPart && diskContainsDiskArea) { var partItem = diskItem.Read(DatItems.Formats.Disk.PartKey); if (partItem != null) { var partItemInternal = partItem.GetInternalClone(); partMappings[partItemInternal] = diskItem; var diskAreaItem = diskItem.Read(DatItems.Formats.Disk.DiskAreaKey); if (diskAreaItem != null) diskAreaMappings[partItemInternal] = (diskAreaItem.GetInternalClone(), diskItem); } } break; case DatItems.Formats.Display display: var displayItem = ProcessItem(display, machine); EnsureMachineKey(machine, Models.Metadata.Machine.DisplayKey); AppendToMachineKey(machine, Models.Metadata.Machine.DisplayKey, displayItem); break; case DatItems.Formats.Driver driver: var driverItem = driver.GetInternalClone(); EnsureMachineKey(machine, Models.Metadata.Machine.DriverKey); AppendToMachineKey(machine, Models.Metadata.Machine.DriverKey, driverItem); break; case DatItems.Formats.Feature feature: var featureItem = feature.GetInternalClone(); EnsureMachineKey(machine, Models.Metadata.Machine.FeatureKey); AppendToMachineKey(machine, Models.Metadata.Machine.FeatureKey, featureItem); break; case DatItems.Formats.Info info: var infoItem = info.GetInternalClone(); EnsureMachineKey(machine, Models.Metadata.Machine.InfoKey); AppendToMachineKey(machine, Models.Metadata.Machine.InfoKey, infoItem); break; case DatItems.Formats.Input input: var inputItem = ProcessItem(input); EnsureMachineKey(machine, Models.Metadata.Machine.InputKey); AppendToMachineKey(machine, Models.Metadata.Machine.InputKey, inputItem); break; case DatItems.Formats.Media media: var mediaItem = media.GetInternalClone(); EnsureMachineKey(machine, Models.Metadata.Machine.MediaKey); AppendToMachineKey(machine, Models.Metadata.Machine.MediaKey, mediaItem); break; case DatItems.Formats.PartFeature partFeature: var partFeatureItem = partFeature.GetInternalClone(); EnsureMachineKey(machine, Models.Metadata.Machine.FeatureKey); AppendToMachineKey(machine, Models.Metadata.Machine.FeatureKey, partFeatureItem); // Add Part mapping bool partFeatureContainsPart = partFeatureItem.ContainsKey(DatItems.Formats.PartFeature.PartKey); if (partFeatureContainsPart) { var partItem = partFeatureItem.Read(DatItems.Formats.PartFeature.PartKey); if (partItem != null) partMappings[partItem.GetInternalClone()] = partFeatureItem; } break; case DatItems.Formats.Port port: var portItem = ProcessItem(port); EnsureMachineKey(machine, Models.Metadata.Machine.PortKey); AppendToMachineKey(machine, Models.Metadata.Machine.PortKey, portItem); break; case DatItems.Formats.RamOption ramOption: var ramOptionItem = ramOption.GetInternalClone(); EnsureMachineKey(machine, Models.Metadata.Machine.RamOptionKey); AppendToMachineKey(machine, Models.Metadata.Machine.RamOptionKey, ramOptionItem); break; case DatItems.Formats.Release release: var releaseItem = release.GetInternalClone(); EnsureMachineKey(machine, Models.Metadata.Machine.ReleaseKey); AppendToMachineKey(machine, Models.Metadata.Machine.ReleaseKey, releaseItem); break; case DatItems.Formats.Rom rom: var romItem = ProcessItem(rom, machine); EnsureMachineKey(machine, Models.Metadata.Machine.RomKey); AppendToMachineKey(machine, Models.Metadata.Machine.RomKey, romItem); // Add Part and DataArea mappings bool romContainsPart = romItem.ContainsKey(DatItems.Formats.Rom.PartKey); bool romContainsDataArea = romItem.ContainsKey(DatItems.Formats.Rom.DataAreaKey); if (romContainsPart && romContainsDataArea) { var partItem = romItem.Read(DatItems.Formats.Rom.PartKey); if (partItem != null) { var partItemInternal = partItem.GetInternalClone(); partMappings[partItemInternal] = romItem; var dataAreaItem = romItem.Read(DatItems.Formats.Rom.DataAreaKey); if (dataAreaItem != null) dataAreaMappings[partItemInternal] = (dataAreaItem.GetInternalClone(), romItem); } } break; case DatItems.Formats.Sample sample: var sampleItem = sample.GetInternalClone(); EnsureMachineKey(machine, Models.Metadata.Machine.SampleKey); AppendToMachineKey(machine, Models.Metadata.Machine.SampleKey, sampleItem); break; case DatItems.Formats.SharedFeat sharedFeat: var sharedFeatItem = sharedFeat.GetInternalClone(); EnsureMachineKey(machine, Models.Metadata.Machine.SharedFeatKey); AppendToMachineKey(machine, Models.Metadata.Machine.SharedFeatKey, sharedFeatItem); break; case DatItems.Formats.Slot slot: var slotItem = ProcessItem(slot); EnsureMachineKey(machine, Models.Metadata.Machine.SlotKey); AppendToMachineKey(machine, Models.Metadata.Machine.SlotKey, slotItem); break; case DatItems.Formats.SoftwareList softwareList: var softwareListItem = softwareList.GetInternalClone(); EnsureMachineKey(machine, Models.Metadata.Machine.SoftwareListKey); AppendToMachineKey(machine, Models.Metadata.Machine.SoftwareListKey, softwareListItem); break; case DatItems.Formats.Sound sound: var soundItem = sound.GetInternalClone(); EnsureMachineKey(machine, Models.Metadata.Machine.SoundKey); AppendToMachineKey(machine, Models.Metadata.Machine.SoundKey, soundItem); break; } } // Handle Part, DiskItem, and DatItem mappings, if they exist if (partMappings.Count != 0) { // Create a collection to hold the inverted Parts Dictionary partItems = []; // Loop through the Part mappings foreach (var partMapping in partMappings) { // Get the mapping pieces var partItem = partMapping.Key; var datItem = partMapping.Value; // Get the part name and skip if there's none string? partName = partItem.ReadString(Models.Metadata.Part.NameKey); if (partName == null) continue; // Create the part in the dictionary, if needed if (!partItems.ContainsKey(partName)) partItems[partName] = []; // Copy over string values partItems[partName][Models.Metadata.Part.NameKey] = partName; if (!partItems[partName].ContainsKey(Models.Metadata.Part.InterfaceKey)) partItems[partName][Models.Metadata.Part.InterfaceKey] = partItem.ReadString(Models.Metadata.Part.InterfaceKey); // Clear any empty fields ClearEmptyKeys(partItems[partName]); // If the item has a DataArea mapping if (dataAreaMappings.ContainsKey(partItem)) { // Get the mapped items var (dataArea, romItem) = dataAreaMappings[partItem]; // Clear any empty fields ClearEmptyKeys(romItem); // Get the data area name and skip if there's none string? dataAreaName = dataArea.ReadString(Models.Metadata.DataArea.NameKey); if (dataAreaName != null) { // Get existing data areas as a list var dataAreasArr = partItems[partName].Read(Models.Metadata.Part.DataAreaKey) ?? []; List dataAreas = [.. dataAreasArr]; // Find the existing disk area to append to, otherwise create a new disk area int dataAreaIndex = dataAreas.FindIndex(da => da.ReadString(Models.Metadata.DataArea.NameKey) == dataAreaName); Models.Metadata.DataArea aggregateDataArea; if (dataAreaIndex > -1) { aggregateDataArea = dataAreas[dataAreaIndex]; } else { aggregateDataArea = []; aggregateDataArea[Models.Metadata.DataArea.EndiannessKey] = dataArea.ReadString(Models.Metadata.DataArea.EndiannessKey); aggregateDataArea[Models.Metadata.DataArea.NameKey] = dataArea.ReadString(Models.Metadata.DataArea.NameKey); aggregateDataArea[Models.Metadata.DataArea.SizeKey] = dataArea.ReadString(Models.Metadata.DataArea.SizeKey); aggregateDataArea[Models.Metadata.DataArea.WidthKey] = dataArea.ReadString(Models.Metadata.DataArea.WidthKey); } // Clear any empty fields ClearEmptyKeys(aggregateDataArea); // Get existing roms as a list var romsArr = aggregateDataArea.Read(Models.Metadata.DataArea.RomKey) ?? []; List roms = [.. romsArr]; // Add the rom to the data area roms.Add(romItem); // Assign back the roms aggregateDataArea[Models.Metadata.DataArea.RomKey] = roms.ToArray(); // Assign back the data area if (dataAreaIndex > -1) dataAreas[dataAreaIndex] = aggregateDataArea; else dataAreas.Add(aggregateDataArea); // Assign back the data areas array partItems[partName][Models.Metadata.Part.DataAreaKey] = dataAreas.ToArray(); } } // If the item has a DiskArea mapping if (diskAreaMappings.ContainsKey(partItem)) { // Get the mapped items var (diskArea, diskItem) = diskAreaMappings[partItem]; // Clear any empty fields ClearEmptyKeys(diskItem); // Get the disk area name and skip if there's none string? diskAreaName = diskArea.ReadString(Models.Metadata.DiskArea.NameKey); if (diskAreaName != null) { // Get existing disk areas as a list var diskAreasArr = partItems[partName].Read(Models.Metadata.Part.DiskAreaKey) ?? []; List diskAreas = [.. diskAreasArr]; // Find the existing disk area to append to, otherwise create a new disk area int diskAreaIndex = diskAreas.FindIndex(da => da.ReadString(Models.Metadata.DiskArea.NameKey) == diskAreaName); Models.Metadata.DiskArea aggregateDiskArea; if (diskAreaIndex > -1) { aggregateDiskArea = diskAreas[diskAreaIndex]; } else { aggregateDiskArea = []; aggregateDiskArea[Models.Metadata.DiskArea.NameKey] = diskArea.ReadString(Models.Metadata.DiskArea.NameKey); } // Clear any empty fields ClearEmptyKeys(aggregateDiskArea); // Get existing disks as a list var disksArr = aggregateDiskArea.Read(Models.Metadata.DiskArea.DiskKey) ?? []; List disks = [.. disksArr]; // Add the disk to the data area disks.Add(diskItem); // Assign back the disks aggregateDiskArea[Models.Metadata.DiskArea.DiskKey] = disks.ToArray(); // Assign back the disk area if (diskAreaIndex > -1) diskAreas[diskAreaIndex] = aggregateDiskArea; else diskAreas.Add(aggregateDiskArea); // Assign back the disk areas array partItems[partName][Models.Metadata.Part.DiskAreaKey] = diskAreas.ToArray(); } } // If the item is a DipSwitch if (datItem is Models.Metadata.DipSwitch dipSwitchItem) { // Get existing dipswitches as a list var dipSwitchesArr = partItems[partName].Read(Models.Metadata.Part.DipSwitchKey) ?? []; List dipSwitches = [.. dipSwitchesArr]; // Clear any empty fields ClearEmptyKeys(dipSwitchItem); // Add the dipswitch dipSwitches.Add(dipSwitchItem); // Assign back the dipswitches partItems[partName][Models.Metadata.Part.DipSwitchKey] = dipSwitches.ToArray(); } // If the item is a Feature else if (datItem is Models.Metadata.Feature featureItem) { // Get existing features as a list var featuresArr = partItems[partName].Read(Models.Metadata.Part.FeatureKey) ?? []; List features = [.. featuresArr]; // Clear any empty fields ClearEmptyKeys(featureItem); // Add the feature features.Add(featureItem); // Assign back the features partItems[partName][Models.Metadata.Part.FeatureKey] = features.ToArray(); } } // Assign the part array to the machine machine[Models.Metadata.Machine.PartKey] = partItems.Values.ToArray(); } // Add the machine to the list machines.Add(machine); } // Return the list of machines return [.. machines]; } /// /// Convert machines information /// private Models.Metadata.Machine[]? ConvertMachinesDB(bool ignoreblanks = false) { // Create a machine list to hold all outputs List machines = []; // Loop through the sorted items and create games for them foreach (string key in ItemsDB.SortedKeys) { var items = ItemsDB.GetItemsForBucket(key, filter: true); if (items == null || items.Count == 0) continue; // Create a machine to hold everything var machine = ItemsDB.GetMachineForItem(items.First().Key).Value!.GetInternalClone(); // Handle Trurip object, if it exists if (machine.ContainsKey(Models.Metadata.Machine.TruripKey)) { var trurip = machine.Read(Models.Metadata.Machine.TruripKey); if (trurip != null) { var truripItem = trurip.ConvertToLogiqx(); truripItem.Publisher = machine.ReadString(Models.Metadata.Machine.PublisherKey); truripItem.Year = machine.ReadString(Models.Metadata.Machine.YearKey); truripItem.Players = machine.ReadString(Models.Metadata.Machine.PlayersKey); truripItem.Source = machine.ReadString(Models.Metadata.Machine.SourceFileKey); truripItem.CloneOf = machine.ReadString(Models.Metadata.Machine.CloneOfKey); machine[Models.Metadata.Machine.TruripKey] = truripItem; } } // Create mapping dictionaries for the Parts, DataAreas, and DiskAreas associated with this machine Dictionary partMappings = []; Dictionary dataAreaMappings = []; Dictionary diskAreaMappings = []; // Loop through and convert the items to respective lists foreach (var kvp in items) { // Check for a "null" item var item = ProcessNullifiedItem(kvp); // Skip if we're ignoring the item if (ShouldIgnore(item.Value, ignoreblanks)) continue; switch (item.Value) { case DatItems.Formats.Adjuster adjuster: var adjusterItem = ProcessItem(adjuster); EnsureMachineKey(machine, Models.Metadata.Machine.AdjusterKey); AppendToMachineKey(machine, Models.Metadata.Machine.AdjusterKey, adjusterItem); break; case DatItems.Formats.Archive archive: var archiveItem = archive.GetInternalClone(); EnsureMachineKey(machine, Models.Metadata.Machine.ArchiveKey); AppendToMachineKey(machine, Models.Metadata.Machine.ArchiveKey, archiveItem); break; case DatItems.Formats.BiosSet biosSet: var biosSetItem = biosSet.GetInternalClone(); EnsureMachineKey(machine, Models.Metadata.Machine.BiosSetKey); AppendToMachineKey(machine, Models.Metadata.Machine.BiosSetKey, biosSetItem); break; case DatItems.Formats.Chip chip: var chipItem = chip.GetInternalClone(); EnsureMachineKey(machine, Models.Metadata.Machine.ChipKey); AppendToMachineKey(machine, Models.Metadata.Machine.ChipKey, chipItem); break; case DatItems.Formats.Configuration configuration: var configurationItem = ProcessItem(configuration); EnsureMachineKey(machine, Models.Metadata.Machine.ConfigurationKey); AppendToMachineKey(machine, Models.Metadata.Machine.ConfigurationKey, configurationItem); break; case DatItems.Formats.Device device: var deviceItem = ProcessItem(device); EnsureMachineKey(machine, Models.Metadata.Machine.DeviceKey); AppendToMachineKey(machine, Models.Metadata.Machine.DeviceKey, deviceItem); break; case DatItems.Formats.DeviceRef deviceRef: var deviceRefItem = deviceRef.GetInternalClone(); EnsureMachineKey(machine, Models.Metadata.Machine.DeviceRefKey); AppendToMachineKey(machine, Models.Metadata.Machine.DeviceRefKey, deviceRefItem); break; case DatItems.Formats.DipSwitch dipSwitch: var dipSwitchItem = ProcessItem(dipSwitch); EnsureMachineKey(machine, Models.Metadata.Machine.DipSwitchKey); AppendToMachineKey(machine, Models.Metadata.Machine.DipSwitchKey, dipSwitchItem); // Add Part mapping bool dipSwitchContainsPart = dipSwitchItem.ContainsKey(DatItems.Formats.DipSwitch.PartKey); if (dipSwitchContainsPart) { var partItem = dipSwitchItem.Read(DatItems.Formats.DipSwitch.PartKey); if (partItem != null) partMappings[partItem.GetInternalClone()] = dipSwitchItem; } break; case DatItems.Formats.Disk disk: var diskItem = disk.GetInternalClone(); EnsureMachineKey(machine, Models.Metadata.Machine.DiskKey); AppendToMachineKey(machine, Models.Metadata.Machine.DiskKey, diskItem); // Add Part and DiskArea mappings bool diskContainsPart = diskItem.ContainsKey(DatItems.Formats.Disk.PartKey); bool diskContainsDiskArea = diskItem.ContainsKey(DatItems.Formats.Disk.DiskAreaKey); if (diskContainsPart && diskContainsDiskArea) { var partItem = diskItem.Read(DatItems.Formats.Disk.PartKey); if (partItem != null) { var partItemInternal = partItem.GetInternalClone(); partMappings[partItemInternal] = diskItem; var diskAreaItem = diskItem.Read(DatItems.Formats.Disk.DiskAreaKey); if (diskAreaItem != null) diskAreaMappings[partItemInternal] = (diskAreaItem.GetInternalClone(), diskItem); } } break; case DatItems.Formats.Display display: var displayItem = ProcessItem(display, machine); EnsureMachineKey(machine, Models.Metadata.Machine.DisplayKey); AppendToMachineKey(machine, Models.Metadata.Machine.DisplayKey, displayItem); break; case DatItems.Formats.Driver driver: var driverItem = driver.GetInternalClone(); EnsureMachineKey(machine, Models.Metadata.Machine.DriverKey); AppendToMachineKey(machine, Models.Metadata.Machine.DriverKey, driverItem); break; case DatItems.Formats.Feature feature: var featureItem = feature.GetInternalClone(); EnsureMachineKey(machine, Models.Metadata.Machine.FeatureKey); AppendToMachineKey(machine, Models.Metadata.Machine.FeatureKey, featureItem); break; case DatItems.Formats.Info info: var infoItem = info.GetInternalClone(); EnsureMachineKey(machine, Models.Metadata.Machine.InfoKey); AppendToMachineKey(machine, Models.Metadata.Machine.InfoKey, infoItem); break; case DatItems.Formats.Input input: var inputItem = ProcessItem(input); EnsureMachineKey(machine, Models.Metadata.Machine.InputKey); AppendToMachineKey(machine, Models.Metadata.Machine.InputKey, inputItem); break; case DatItems.Formats.Media media: var mediaItem = media.GetInternalClone(); EnsureMachineKey(machine, Models.Metadata.Machine.MediaKey); AppendToMachineKey(machine, Models.Metadata.Machine.MediaKey, mediaItem); break; case DatItems.Formats.PartFeature partFeature: var partFeatureItem = partFeature.GetInternalClone(); EnsureMachineKey(machine, Models.Metadata.Machine.FeatureKey); AppendToMachineKey(machine, Models.Metadata.Machine.FeatureKey, partFeatureItem); // Add Part mapping bool partFeatureContainsPart = partFeatureItem.ContainsKey(DatItems.Formats.PartFeature.PartKey); if (partFeatureContainsPart) { var partItem = partFeatureItem.Read(DatItems.Formats.PartFeature.PartKey); if (partItem != null) partMappings[partItem.GetInternalClone()] = partFeatureItem; } break; case DatItems.Formats.Port port: var portItem = ProcessItem(port); EnsureMachineKey(machine, Models.Metadata.Machine.PortKey); AppendToMachineKey(machine, Models.Metadata.Machine.PortKey, portItem); break; case DatItems.Formats.RamOption ramOption: var ramOptionItem = ramOption.GetInternalClone(); EnsureMachineKey(machine, Models.Metadata.Machine.RamOptionKey); AppendToMachineKey(machine, Models.Metadata.Machine.RamOptionKey, ramOptionItem); break; case DatItems.Formats.Release release: var releaseItem = release.GetInternalClone(); EnsureMachineKey(machine, Models.Metadata.Machine.ReleaseKey); AppendToMachineKey(machine, Models.Metadata.Machine.ReleaseKey, releaseItem); break; case DatItems.Formats.Rom rom: var romItem = ProcessItem(rom, machine); EnsureMachineKey(machine, Models.Metadata.Machine.RomKey); AppendToMachineKey(machine, Models.Metadata.Machine.RomKey, romItem); // Add Part and DataArea mappings bool romContainsPart = romItem.ContainsKey(DatItems.Formats.Rom.PartKey); bool romContainsDataArea = romItem.ContainsKey(DatItems.Formats.Rom.DataAreaKey); if (romContainsPart && romContainsDataArea) { var partItem = romItem.Read(DatItems.Formats.Rom.PartKey); if (partItem != null) { var partItemInternal = partItem.GetInternalClone(); partMappings[partItemInternal] = romItem; var dataAreaItem = romItem.Read(DatItems.Formats.Rom.DataAreaKey); if (dataAreaItem != null) dataAreaMappings[partItemInternal] = (dataAreaItem.GetInternalClone(), romItem); } } break; case DatItems.Formats.Sample sample: var sampleItem = sample.GetInternalClone(); EnsureMachineKey(machine, Models.Metadata.Machine.SampleKey); AppendToMachineKey(machine, Models.Metadata.Machine.SampleKey, sampleItem); break; case DatItems.Formats.SharedFeat sharedFeat: var sharedFeatItem = sharedFeat.GetInternalClone(); EnsureMachineKey(machine, Models.Metadata.Machine.SharedFeatKey); AppendToMachineKey(machine, Models.Metadata.Machine.SharedFeatKey, sharedFeatItem); break; case DatItems.Formats.Slot slot: var slotItem = ProcessItem(slot); EnsureMachineKey(machine, Models.Metadata.Machine.SlotKey); AppendToMachineKey(machine, Models.Metadata.Machine.SlotKey, slotItem); break; case DatItems.Formats.SoftwareList softwareList: var softwareListItem = softwareList.GetInternalClone(); EnsureMachineKey(machine, Models.Metadata.Machine.SoftwareListKey); AppendToMachineKey(machine, Models.Metadata.Machine.SoftwareListKey, softwareListItem); break; case DatItems.Formats.Sound sound: var soundItem = sound.GetInternalClone(); EnsureMachineKey(machine, Models.Metadata.Machine.SoundKey); AppendToMachineKey(machine, Models.Metadata.Machine.SoundKey, soundItem); break; } } // Handle Part, DiskItem, and DatItem mappings, if they exist if (partMappings.Count != 0) { // Create a collection to hold the inverted Parts Dictionary partItems = []; // Loop through the Part mappings foreach (var partMapping in partMappings) { // Get the mapping pieces var partItem = partMapping.Key; var datItem = partMapping.Value; // Get the part name and skip if there's none string? partName = partItem.ReadString(Models.Metadata.Part.NameKey); if (partName == null) continue; // Create the part in the dictionary, if needed if (!partItems.ContainsKey(partName)) partItems[partName] = []; // Copy over string values partItems[partName][Models.Metadata.Part.NameKey] = partName; if (!partItems[partName].ContainsKey(Models.Metadata.Part.InterfaceKey)) partItems[partName][Models.Metadata.Part.InterfaceKey] = partItem.ReadString(Models.Metadata.Part.InterfaceKey); // Clear any empty fields ClearEmptyKeys(partItems[partName]); // If the item has a DataArea mapping if (dataAreaMappings.ContainsKey(partItem)) { // Get the mapped items var (dataArea, romItem) = dataAreaMappings[partItem]; // Clear any empty fields ClearEmptyKeys(romItem); // Get the data area name and skip if there's none string? dataAreaName = dataArea.ReadString(Models.Metadata.DataArea.NameKey); if (dataAreaName != null) { // Get existing data areas as a list var dataAreasArr = partItems[partName].Read(Models.Metadata.Part.DataAreaKey) ?? []; List dataAreas = [.. dataAreasArr]; // Find the existing disk area to append to, otherwise create a new disk area int dataAreaIndex = dataAreas.FindIndex(da => da.ReadString(Models.Metadata.DataArea.NameKey) == dataAreaName); Models.Metadata.DataArea aggregateDataArea; if (dataAreaIndex > -1) { aggregateDataArea = dataAreas[dataAreaIndex]; } else { aggregateDataArea = []; aggregateDataArea[Models.Metadata.DataArea.EndiannessKey] = dataArea.ReadString(Models.Metadata.DataArea.EndiannessKey); aggregateDataArea[Models.Metadata.DataArea.NameKey] = dataArea.ReadString(Models.Metadata.DataArea.NameKey); aggregateDataArea[Models.Metadata.DataArea.SizeKey] = dataArea.ReadString(Models.Metadata.DataArea.SizeKey); aggregateDataArea[Models.Metadata.DataArea.WidthKey] = dataArea.ReadString(Models.Metadata.DataArea.WidthKey); } // Clear any empty fields ClearEmptyKeys(aggregateDataArea); // Get existing roms as a list var romsArr = aggregateDataArea.Read(Models.Metadata.DataArea.RomKey) ?? []; List roms = [.. romsArr]; // Add the rom to the data area roms.Add(romItem); // Assign back the roms aggregateDataArea[Models.Metadata.DataArea.RomKey] = roms.ToArray(); // Assign back the data area if (dataAreaIndex > -1) dataAreas[dataAreaIndex] = aggregateDataArea; else dataAreas.Add(aggregateDataArea); // Assign back the data areas array partItems[partName][Models.Metadata.Part.DataAreaKey] = dataAreas.ToArray(); } } // If the item has a DiskArea mapping if (diskAreaMappings.ContainsKey(partItem)) { // Get the mapped items var (diskArea, diskItem) = diskAreaMappings[partItem]; // Clear any empty fields ClearEmptyKeys(diskItem); // Get the disk area name and skip if there's none string? diskAreaName = diskArea.ReadString(Models.Metadata.DiskArea.NameKey); if (diskAreaName != null) { // Get existing disk areas as a list var diskAreasArr = partItems[partName].Read(Models.Metadata.Part.DiskAreaKey) ?? []; List diskAreas = [.. diskAreasArr]; // Find the existing disk area to append to, otherwise create a new disk area int diskAreaIndex = diskAreas.FindIndex(da => da.ReadString(Models.Metadata.DiskArea.NameKey) == diskAreaName); Models.Metadata.DiskArea aggregateDiskArea; if (diskAreaIndex > -1) { aggregateDiskArea = diskAreas[diskAreaIndex]; } else { aggregateDiskArea = []; aggregateDiskArea[Models.Metadata.DiskArea.NameKey] = diskArea.ReadString(Models.Metadata.DiskArea.NameKey); } // Clear any empty fields ClearEmptyKeys(aggregateDiskArea); // Get existing disks as a list var disksArr = aggregateDiskArea.Read(Models.Metadata.DiskArea.DiskKey) ?? []; List disks = [.. disksArr]; // Add the disk to the data area disks.Add(diskItem); // Assign back the disks aggregateDiskArea[Models.Metadata.DiskArea.DiskKey] = disks.ToArray(); // Assign back the disk area if (diskAreaIndex > -1) diskAreas[diskAreaIndex] = aggregateDiskArea; else diskAreas.Add(aggregateDiskArea); // Assign back the disk areas array partItems[partName][Models.Metadata.Part.DiskAreaKey] = diskAreas.ToArray(); } } // If the item is a DipSwitch if (datItem is Models.Metadata.DipSwitch dipSwitchItem) { // Get existing dipswitches as a list var dipSwitchesArr = partItems[partName].Read(Models.Metadata.Part.DipSwitchKey) ?? []; List dipSwitches = [.. dipSwitchesArr]; // Clear any empty fields ClearEmptyKeys(dipSwitchItem); // Add the dipswitch dipSwitches.Add(dipSwitchItem); // Assign back the dipswitches partItems[partName][Models.Metadata.Part.DipSwitchKey] = dipSwitches.ToArray(); } // If the item is a Feature else if (datItem is Models.Metadata.Feature featureItem) { // Get existing features as a list var featuresArr = partItems[partName].Read(Models.Metadata.Part.FeatureKey) ?? []; List features = [.. featuresArr]; // Clear any empty fields ClearEmptyKeys(featureItem); // Add the feature features.Add(featureItem); // Assign back the features partItems[partName][Models.Metadata.Part.FeatureKey] = features.ToArray(); } } // Assign the part array to the machine machine[Models.Metadata.Machine.PartKey] = partItems.Values.ToArray(); } // Add the machine to the list machines.Add(machine); } // Return the list of machines return [.. machines]; } /// /// Convert Adjuster information /// /// Item to convert private static Models.Metadata.Adjuster ProcessItem(DatItems.Formats.Adjuster item) { var adjusterItem = item.GetInternalClone(); var condition = item.GetFieldValue(Models.Metadata.Adjuster.ConditionKey); if (condition != null) adjusterItem[Models.Metadata.Adjuster.ConditionKey] = condition.GetInternalClone(); return adjusterItem; } /// /// Convert Configuration information /// /// Item to convert private static Models.Metadata.Configuration ProcessItem(DatItems.Formats.Configuration item) { var configurationItem = item.GetInternalClone(); var condition = item.GetFieldValue(Models.Metadata.Configuration.ConditionKey); if (condition != null) configurationItem[Models.Metadata.Configuration.ConditionKey] = condition.GetInternalClone(); var confLocations = item.GetFieldValue(Models.Metadata.Configuration.ConfLocationKey); if (confLocations != null) { List confLocationItems = []; foreach (var confLocation in confLocations) { var confLocationItem = confLocation.GetInternalClone(); confLocationItems.Add(confLocationItem); } configurationItem[Models.Metadata.Configuration.ConfLocationKey] = confLocationItems.ToArray(); } var confSettings = item.GetFieldValue(Models.Metadata.Configuration.ConfSettingKey); if (confSettings != null) { List confSettingItems = []; foreach (var confSetting in confSettings) { var confSettingItem = confSetting.GetInternalClone(); confSettingItems.Add(confSettingItem); } configurationItem[Models.Metadata.Configuration.ConfSettingKey] = confSettingItems.ToArray(); } return configurationItem; } /// /// Convert Device information /// /// Item to convert private static Models.Metadata.Device ProcessItem(DatItems.Formats.Device item) { var deviceItem = item.GetInternalClone(); var instance = item.GetFieldValue(Models.Metadata.Device.InstanceKey); if (instance != null) deviceItem[Models.Metadata.Device.InstanceKey] = instance.GetInternalClone(); var extensions = item.GetFieldValue(Models.Metadata.Device.ExtensionKey); if (extensions != null) { List extensionItems = []; foreach (var extension in extensions) { var extensionItem = extension.GetInternalClone(); extensionItems.Add(extensionItem); } deviceItem[Models.Metadata.Device.ExtensionKey] = extensionItems.ToArray(); } return deviceItem; } /// /// Convert DipSwitch information /// /// Item to convert /// Machine to use for Part private static Models.Metadata.DipSwitch ProcessItem(DatItems.Formats.DipSwitch item) { var dipSwitchItem = item.GetInternalClone(); var condition = item.GetFieldValue(Models.Metadata.DipSwitch.ConditionKey); if (condition != null) dipSwitchItem[Models.Metadata.DipSwitch.ConditionKey] = condition.GetInternalClone(); var dipLocations = item.GetFieldValue(Models.Metadata.DipSwitch.DipLocationKey); if (dipLocations != null) { List dipLocationItems = []; foreach (var dipLocation in dipLocations) { var extensionItem = dipLocation.GetInternalClone(); dipLocationItems.Add(extensionItem); } dipSwitchItem[Models.Metadata.DipSwitch.DipLocationKey] = dipLocationItems.ToArray(); } var dipValues = item.GetFieldValue(Models.Metadata.DipSwitch.DipValueKey); if (dipValues != null) { List dipValueItems = []; foreach (var dipValue in dipValues) { var extensionItem = dipValue.GetInternalClone(); dipValueItems.Add(extensionItem); } dipSwitchItem[Models.Metadata.DipSwitch.DipValueKey] = dipValueItems.ToArray(); } return dipSwitchItem; } /// /// Convert Display information /// /// Item to convert /// Machine to use for Video private static Models.Metadata.Display ProcessItem(DatItems.Formats.Display item, Models.Metadata.Machine machine) { var displayItem = item.GetInternalClone(); // Create a Video for any item that has specific fields if (displayItem.ContainsKey(Models.Metadata.Video.AspectXKey)) { var videoItem = new Models.Metadata.Video(); videoItem[Models.Metadata.Video.AspectXKey] = displayItem.ReadLong(Models.Metadata.Video.AspectXKey).ToString(); videoItem[Models.Metadata.Video.AspectYKey] = displayItem.ReadLong(Models.Metadata.Video.AspectYKey).ToString(); videoItem[Models.Metadata.Video.HeightKey] = displayItem.ReadLong(Models.Metadata.Display.HeightKey).ToString(); videoItem[Models.Metadata.Video.RefreshKey] = displayItem.ReadDouble(Models.Metadata.Display.RefreshKey).ToString(); videoItem[Models.Metadata.Video.ScreenKey] = displayItem.ReadString(Models.Metadata.Display.DisplayTypeKey).AsEnumValue().AsStringValue(); videoItem[Models.Metadata.Video.WidthKey] = displayItem.ReadLong(Models.Metadata.Display.WidthKey).ToString(); EnsureMachineKey(machine, Models.Metadata.Machine.VideoKey); AppendToMachineKey(machine, Models.Metadata.Machine.VideoKey, videoItem); } return displayItem; } /// /// Convert Input information /// /// Item to convert private static Models.Metadata.Input ProcessItem(DatItems.Formats.Input item) { var inputItem = item.GetInternalClone(); var controls = item.GetFieldValue(Models.Metadata.Input.ControlKey); if (controls != null) { List controlItems = []; foreach (var control in controls) { var controlItem = control.GetInternalClone(); controlItems.Add(controlItem); } inputItem[Models.Metadata.Input.ControlKey] = controlItems.ToArray(); } return inputItem; } /// /// Convert Port information /// /// Item to convert private static Models.Metadata.Port ProcessItem(DatItems.Formats.Port item) { var slotItem = item.GetInternalClone(); var analogs = item.GetFieldValue(Models.Metadata.Port.AnalogKey); if (analogs != null) { List analogItems = []; foreach (var analog in analogs) { var extensionItem = analog.GetInternalClone(); analogItems.Add(extensionItem); } slotItem[Models.Metadata.Port.AnalogKey] = analogItems.ToArray(); } return slotItem; } /// /// Convert Rom information /// /// Item to convert /// Machine to use for Part and DataArea private static Models.Metadata.Rom ProcessItem(DatItems.Formats.Rom item, Models.Metadata.Machine machine) { var romItem = item.GetInternalClone(); // Create a Dump for every Rom that has a subtype switch (romItem.ReadString(Models.Metadata.Rom.OpenMSXMediaType).AsEnumValue()) { case OpenMSXSubType.Rom: var dumpRom = new Models.Metadata.Dump(); var rom = new Models.Metadata.Rom(); rom[Models.Metadata.Rom.NameKey] = romItem.ReadString(Models.Metadata.Rom.NameKey); rom[Models.Metadata.Rom.OffsetKey] = romItem.ReadString(Models.Metadata.Rom.StartKey) ?? romItem.ReadString(Models.Metadata.Rom.OffsetKey); rom[Models.Metadata.Rom.OpenMSXType] = romItem.ReadString(Models.Metadata.Rom.OpenMSXType); rom[Models.Metadata.Rom.RemarkKey] = romItem.ReadString(Models.Metadata.Rom.RemarkKey); rom[Models.Metadata.Rom.SHA1Key] = romItem.ReadString(Models.Metadata.Rom.SHA1Key); rom[Models.Metadata.Rom.StartKey] = romItem.ReadString(Models.Metadata.Rom.StartKey) ?? romItem.ReadString(Models.Metadata.Rom.OffsetKey); dumpRom[Models.Metadata.Dump.RomKey] = rom; var romOriginal = romItem.Read("ORIGINAL"); if (romOriginal != null) { var newOriginal = new Models.Metadata.Original { [Models.Metadata.Original.ValueKey] = romOriginal.Value.FromYesNo(), [Models.Metadata.Original.ContentKey] = romOriginal.Content, }; dumpRom[Models.Metadata.Dump.OriginalKey] = newOriginal; } EnsureMachineKey(machine, Models.Metadata.Machine.DumpKey); AppendToMachineKey(machine, Models.Metadata.Machine.DumpKey, dumpRom); break; case OpenMSXSubType.MegaRom: var dumpMegaRom = new Models.Metadata.Dump(); var megaRom = new Models.Metadata.Rom(); megaRom[Models.Metadata.Rom.NameKey] = romItem.ReadString(Models.Metadata.Rom.NameKey); megaRom[Models.Metadata.Rom.OffsetKey] = romItem.ReadString(Models.Metadata.Rom.StartKey) ?? romItem.ReadString(Models.Metadata.Rom.OffsetKey); megaRom[Models.Metadata.Rom.OpenMSXType] = romItem.ReadString(Models.Metadata.Rom.OpenMSXType); megaRom[Models.Metadata.Rom.RemarkKey] = romItem.ReadString(Models.Metadata.Rom.RemarkKey); megaRom[Models.Metadata.Rom.SHA1Key] = romItem.ReadString(Models.Metadata.Rom.SHA1Key); megaRom[Models.Metadata.Rom.StartKey] = romItem.ReadString(Models.Metadata.Rom.StartKey) ?? romItem.ReadString(Models.Metadata.Rom.OffsetKey); dumpMegaRom[Models.Metadata.Dump.MegaRomKey] = megaRom; var megaRomOriginal = romItem.Read("ORIGINAL"); if (megaRomOriginal != null) { var newOriginal = new Models.Metadata.Original { [Models.Metadata.Original.ValueKey] = megaRomOriginal.Value.FromYesNo(), [Models.Metadata.Original.ContentKey] = megaRomOriginal.Content, }; dumpMegaRom[Models.Metadata.Dump.OriginalKey] = newOriginal; } EnsureMachineKey(machine, Models.Metadata.Machine.DumpKey); AppendToMachineKey(machine, Models.Metadata.Machine.DumpKey, dumpMegaRom); break; case OpenMSXSubType.SCCPlusCart: var dumpSccPlusCart = new Models.Metadata.Dump(); var sccPlusCart = new Models.Metadata.Rom(); sccPlusCart[Models.Metadata.Rom.NameKey] = romItem.ReadString(Models.Metadata.Rom.NameKey); sccPlusCart[Models.Metadata.Rom.OffsetKey] = romItem.ReadString(Models.Metadata.Rom.StartKey) ?? romItem.ReadString(Models.Metadata.Rom.OffsetKey); sccPlusCart[Models.Metadata.Rom.OpenMSXType] = romItem.ReadString(Models.Metadata.Rom.OpenMSXType); sccPlusCart[Models.Metadata.Rom.RemarkKey] = romItem.ReadString(Models.Metadata.Rom.RemarkKey); sccPlusCart[Models.Metadata.Rom.SHA1Key] = romItem.ReadString(Models.Metadata.Rom.SHA1Key); sccPlusCart[Models.Metadata.Rom.StartKey] = romItem.ReadString(Models.Metadata.Rom.StartKey) ?? romItem.ReadString(Models.Metadata.Rom.OffsetKey); dumpSccPlusCart[Models.Metadata.Dump.RomKey] = sccPlusCart; var sccPlusCartOriginal = romItem.Read("ORIGINAL"); if (sccPlusCartOriginal != null) { var newOriginal = new Models.Metadata.Original { [Models.Metadata.Original.ValueKey] = sccPlusCartOriginal.Value.FromYesNo(), [Models.Metadata.Original.ContentKey] = sccPlusCartOriginal.Content, }; dumpSccPlusCart[Models.Metadata.Dump.OriginalKey] = newOriginal; } EnsureMachineKey(machine, Models.Metadata.Machine.DumpKey); AppendToMachineKey(machine, Models.Metadata.Machine.DumpKey, dumpSccPlusCart); break; } return romItem; } /// /// Convert Slot information /// /// Item to convert private static Models.Metadata.Slot ProcessItem(DatItems.Formats.Slot item) { var slotItem = item.GetInternalClone(); var slotOptions = item.GetFieldValue(Models.Metadata.Slot.SlotOptionKey); if (slotOptions != null) { List slotOptionItems = []; foreach (var slotOption in slotOptions) { var extensionItem = slotOption.GetInternalClone(); slotOptionItems.Add(extensionItem); } slotItem[Models.Metadata.Slot.SlotOptionKey] = slotOptionItems.ToArray(); } return slotItem; } /// /// Ensure a key in a machine /// private static void EnsureMachineKey(Models.Metadata.Machine machine, string key) { if (machine.Read(key) == null) #if NET20 || NET35 || NET40 || NET452 machine[key] = new T[0]; #else machine[key] = System.Array.Empty(); #endif } /// /// Append to a machine key as if its an array /// private static void AppendToMachineKey(Models.Metadata.Machine machine, string key, T value) where T : Models.Metadata.DatItem { // Get the existing array var arr = machine.Read(key); if (arr == null) return; // Trim all null fields ClearEmptyKeys(value); // Add to the array List list = [.. arr]; list.Add(value); machine[key] = list.ToArray(); } /// /// Clear empty keys from a DictionaryBase object /// private static void ClearEmptyKeys(Models.Metadata.DictionaryBase obj) { string[] fieldNames = [.. obj.Keys]; foreach (string fieldName in fieldNames) { if (obj[fieldName] == null) obj.Remove(fieldName); } } #endregion } }