diff --git a/SabreTools.DatFiles/DatFile.ToMetadata.cs b/SabreTools.DatFiles/DatFile.ToMetadata.cs
index 037e9d34..01804271 100644
--- a/SabreTools.DatFiles/DatFile.ToMetadata.cs
+++ b/SabreTools.DatFiles/DatFile.ToMetadata.cs
@@ -1,4 +1,3 @@
-using System;
using System.Collections.Generic;
using System.Linq;
using SabreTools.Core.Tools;
@@ -35,6 +34,31 @@ namespace SabreTools.DatFiles
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
///
@@ -618,6 +642,423 @@ namespace SabreTools.DatFiles
return [.. machines];
}
+ ///
+ /// Convert machines information
+ ///
+ private Models.Metadata.Machine[]? ConvertMachinesDB(bool ignoreblanks = false)
+ {
+ // Create a machine list to hold all outputs
+ var machines = new List();
+
+ // 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.Length == 0)
+ continue;
+
+ // Create a machine to hold everything
+ var machine = ItemsDB.GetMachineForItem(items[0].Item1).Item2!.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.Length; 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.Item2)
+ {
+ 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 dataAreas = partItems[partName].Read(Models.Metadata.Part.DataAreaKey)?.ToList() ?? [];
+
+ // 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 roms = aggregateDataArea.Read(Models.Metadata.DataArea.RomKey)?.ToList() ?? [];
+
+ // 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 data areas as a list
+ var diskAreas = partItems[partName].Read(Models.Metadata.Part.DiskAreaKey)?.ToList() ?? [];
+
+ // 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 disks = aggregateDiskArea.Read(Models.Metadata.DiskArea.DiskKey)?.ToList() ?? [];
+
+ // 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 dipSwitches = partItems[partName].Read(Models.Metadata.Part.DipSwitchKey)?.ToList() ?? [];
+
+ // 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 features = partItems[partName].Read(Models.Metadata.Part.FeatureKey)?.ToList() ?? [];
+
+ // 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
///
diff --git a/SabreTools.DatFiles/SerializableDatFile.cs b/SabreTools.DatFiles/SerializableDatFile.cs
index aa4fa3f9..510688e8 100644
--- a/SabreTools.DatFiles/SerializableDatFile.cs
+++ b/SabreTools.DatFiles/SerializableDatFile.cs
@@ -70,7 +70,7 @@ namespace SabreTools.DatFiles
logger.User($"Writing to '{outfile}'...");
// Serialize the input file in two steps
- var internalFormat = ConvertMetadata(ignoreblanks);
+ var internalFormat = ConvertMetadataDB(ignoreblanks);
var specificFormat = Activator.CreateInstance().Deserialize(internalFormat);
if (!Activator.CreateInstance().Serialize(specificFormat, outfile))
{