using System; using System.Collections.Generic; using System.Linq; using SabreTools.DatItems; using SabreTools.DatItems.Formats; namespace SabreTools.DatFiles { public partial class DatFile { #region Splitting /// /// Use cdevice_ref tags to get full non-merged sets and remove parenting tags /// /// This is a destructive process and items will be removed public void ApplyDeviceNonMerged() { _logger.User("Creating device non-merged sets from the DAT"); // For sake of ease, the first thing we want to do is bucket by game BucketBy(ItemKey.Machine, norename: true); // Now we want to loop through all of the games and set the correct information while (AddItemsFromDevices(false, false)) ; while (AddItemsFromDevices(true, false)) ; // Then, remove the romof and cloneof tags so it's not picked up by the manager RemoveMachineRelationshipTags(); } /// /// Use cloneof tags to create merged sets and remove the tags plus deduplicating if tags don't catch everything /// /// This is a destructive process and items will be removed public void ApplyFullyMerged() { _logger.User("Creating fully merged sets from the DAT"); // For sake of ease, the first thing we want to do is bucket by game BucketBy(ItemKey.Machine, norename: true); // Now we want to loop through all of the games and set the correct information AddItemsFromChildren(true, false); // Now that we have looped through the cloneof tags, we loop through the romof tags RemoveItemsFromRomOfChild(); // Finally, remove the romof and cloneof tags so it's not picked up by the manager RemoveMachineRelationshipTags(); } /// /// Use cloneof tags to create non-merged sets and remove the tags plus using the device_ref tags to get full sets /// /// This is a destructive process and items will be removed public void ApplyFullyNonMerged() { _logger.User("Creating fully non-merged sets from the DAT"); // For sake of ease, the first thing we want to do is bucket by game BucketBy(ItemKey.Machine, norename: true); // Now we want to loop through all of the games and set the correct information while (AddItemsFromDevices(true, true)) ; AddItemsFromDevices(false, true); AddItemsFromCloneOfParent(); // Now that we have looped through the cloneof tags, we loop through the romof tags AddItemsFromRomOfParent(); // Then, remove the romof and cloneof tags so it's not picked up by the manager RemoveMachineRelationshipTags(); } /// /// Use cloneof tags to create merged sets and remove the tags /// /// This is a destructive process and items will be removed public void ApplyMerged() { _logger.User("Creating merged sets from the DAT"); // For sake of ease, the first thing we want to do is bucket by game BucketBy(ItemKey.Machine, norename: true); // Now we want to loop through all of the games and set the correct information AddItemsFromChildren(true, true); // Now that we have looped through the cloneof tags, we loop through the romof tags RemoveItemsFromRomOfChild(); // Finally, remove the romof and cloneof tags so it's not picked up by the manager RemoveMachineRelationshipTags(); } /// /// Use cloneof tags to create non-merged sets and remove the tags /// /// This is a destructive process and items will be removed public void ApplyNonMerged() { _logger.User("Creating non-merged sets from the DAT"); // For sake of ease, the first thing we want to do is bucket by game BucketBy(ItemKey.Machine, norename: true); // Now we want to loop through all of the games and set the correct information AddItemsFromCloneOfParent(); // Now that we have looped through the cloneof tags, we loop through the romof tags RemoveItemsFromRomOfChild(); // Finally, remove the romof and cloneof tags so it's not picked up by the manager RemoveMachineRelationshipTags(); } /// /// Use cloneof and romof tags to create split sets and remove the tags /// /// This is a destructive process and items will be removed public void ApplySplit() { _logger.User("Creating split sets from the DAT"); // For sake of ease, the first thing we want to do is bucket by game BucketBy(ItemKey.Machine, norename: true); // Now we want to loop through all of the games and set the correct information RemoveItemsFromCloneOfChild(); // Now that we have looped through the cloneof tags, we loop through the romof tags RemoveItemsFromRomOfChild(); // Finally, remove the romof and cloneof tags so it's not picked up by the manager RemoveMachineRelationshipTags(); } #endregion #region Splitting Steps /// /// Use cloneof tags to add items to the parents, removing the child sets in the process /// /// True to add DatItems to subfolder of parent (not including Disk), false otherwise /// True to skip checking for duplicate ROMs in parent, false otherwise /// Assumes items are bucketed by internal void AddItemsFromChildren(bool subfolder, bool skipDedup) { AddItemsFromChildrenImpl(subfolder, skipDedup); AddItemsFromChildrenImplDB(subfolder, skipDedup); } /// /// Use cloneof tags to add items to the children, setting the new romof tag in the process /// /// Assumes items are bucketed by internal void AddItemsFromCloneOfParent() { AddItemsFromCloneOfParentImpl(); AddItemsFromCloneOfParentImplDB(); } /// /// Use device_ref and optionally slotoption tags to add items to the children /// /// True if only child device sets are touched, false for non-device sets /// True if slotoptions tags are used as well, false otherwise /// Assumes items are bucketed by internal bool AddItemsFromDevices(bool deviceOnly, bool useSlotOptions) { bool foundnew = AddItemsFromDevicesImpl(deviceOnly, useSlotOptions); foundnew |= AddItemsFromDevicesImplDB(deviceOnly, useSlotOptions); return foundnew; } /// /// Use romof tags to add items to the children /// /// Assumes items are bucketed by internal void AddItemsFromRomOfParent() { AddItemsFromRomOfParentImpl(); AddItemsFromRomOfParentImplDB(); } /// /// Remove all BIOS and device sets /// /// Assumes items are bucketed by internal void RemoveBiosAndDeviceSets() { RemoveBiosAndDeviceSetsImpl(); RemoveBiosAndDeviceSetsImplDB(); } /// /// Use cloneof tags to remove items from the children /// /// Assumes items are bucketed by internal void RemoveItemsFromCloneOfChild() { RemoveItemsFromCloneOfChildImpl(); RemoveItemsFromCloneOfChildImplDB(); } /// /// Use romof tags to remove bios items from children /// /// Assumes items are bucketed by internal void RemoveItemsFromRomOfChild() { RemoveItemsFromRomOfChildImpl(); RemoveItemsFromRomOfChildImplDB(); } /// /// Remove all romof and cloneof tags from all machines /// /// Assumes items are bucketed by internal void RemoveMachineRelationshipTags() { RemoveMachineRelationshipTagsImpl(); RemoveMachineRelationshipTagsImplDB(); } #endregion #region Splitting Implementations /// /// Use cloneof tags to add items to the parents, removing the child sets in the process /// /// True to add DatItems to subfolder of parent (not including Disk), false otherwise /// True to skip checking for duplicate ROMs in parent, false otherwise /// /// Applies to . /// Assumes items are bucketed by . /// private void AddItemsFromChildrenImpl(bool subfolder, bool skipDedup) { foreach (string bucket in Items.SortedKeys) { // If the bucket has no items in it List items = GetItemsForBucket(bucket); if (items.Count == 0) continue; // Get the machine var machine = items[0].GetMachine(); if (machine == null) continue; // Get the cloneof parent items string? cloneOf = machine.GetStringFieldValue(Models.Metadata.Machine.CloneOfKey); List parentItems = GetItemsForBucket(cloneOf); if (cloneOf == null) continue; // Otherwise, move the items from the current game to a subfolder of the parent game DatItem copyFrom; if (parentItems.Count == 0) { copyFrom = new Rom(); copyFrom.GetMachine()!.SetName(cloneOf); copyFrom.GetMachine()!.SetFieldValue(Models.Metadata.Machine.DescriptionKey, cloneOf); } else { copyFrom = parentItems[0]; } items = GetItemsForBucket(bucket); foreach (DatItem item in items) { // Special disk handling if (item is Disk disk) { string? mergeTag = disk.GetStringFieldValue(Models.Metadata.Disk.MergeKey); // If the merge tag exists and the parent already contains it, skip if (mergeTag != null && GetItemsForBucket(cloneOf) .FindAll(i => i is Disk) .ConvertAll(i => (i as Disk)!.GetName()) .Contains(mergeTag)) { continue; } // If the merge tag exists but the parent doesn't contain it, add to parent else if (mergeTag != null && !GetItemsForBucket(cloneOf) .FindAll(i => i is Disk) .ConvertAll(i => (i as Disk)!.GetName()) .Contains(mergeTag)) { disk.CopyMachineInformation(copyFrom); AddItem(disk, statsOnly: false); } // If there is no merge tag, add to parent else if (mergeTag == null) { disk.CopyMachineInformation(copyFrom); AddItem(disk, statsOnly: false); } } // Special rom handling else if (item is Rom rom) { // If the merge tag exists and the parent already contains it, skip if (rom.GetStringFieldValue(Models.Metadata.Rom.MergeKey) != null && GetItemsForBucket(cloneOf) .FindAll(i => i is Rom) .ConvertAll(i => (i as Rom)!.GetName()) .Contains(rom.GetStringFieldValue(Models.Metadata.Rom.MergeKey))) { continue; } // If the merge tag exists but the parent doesn't contain it, add to subfolder of parent else if (rom.GetStringFieldValue(Models.Metadata.Rom.MergeKey) != null && !GetItemsForBucket(cloneOf) .FindAll(i => i is Rom) .ConvertAll(i => (i as Rom)!.GetName()) .Contains(rom.GetStringFieldValue(Models.Metadata.Rom.MergeKey))) { if (subfolder) rom.SetName($"{rom.GetMachine()!.GetName()}\\{rom.GetName()}"); rom.CopyMachineInformation(copyFrom); AddItem(rom, statsOnly: false); } // If the parent doesn't already contain this item, add to subfolder of parent else if (!GetItemsForBucket(cloneOf).Contains(item) || skipDedup) { if (subfolder) rom.SetName($"{item.GetMachine()!.GetName()}\\{rom.GetName()}"); rom.CopyMachineInformation(copyFrom); AddItem(rom, statsOnly: false); } } // All other that would be missing to subfolder of parent else if (!GetItemsForBucket(cloneOf).Contains(item)) { if (subfolder) item.SetName($"{item.GetMachine()!.GetName()}\\{item.GetName()}"); item.CopyMachineInformation(copyFrom); AddItem(item, statsOnly: false); } } // Then, remove the old game so it's not picked up by the writer RemoveBucket(bucket); } } /// /// Use cloneof tags to add items to the parents, removing the child sets in the process /// /// True to add DatItems to subfolder of parent (not including Disk), false otherwise /// True to skip checking for duplicate ROMs in parent, false otherwise /// /// Applies to . /// Assumes items are bucketed by . /// private void AddItemsFromChildrenImplDB(bool subfolder, bool skipDedup) { List buckets = [.. ItemsDB.SortedKeys]; foreach (string bucket in buckets) { // If the bucket has no items in it Dictionary items = GetItemsForBucketDB(bucket); if (items.Count == 0) continue; // Get the machine for the first item var machine = GetMachineForItemDB(items.First().Key); if (machine.Value == null) continue; // Get the clone parent string? cloneOf = machine.Value.GetStringFieldValue(Models.Metadata.Machine.CloneOfKey); if (string.IsNullOrEmpty(cloneOf)) continue; // Get the clone parent machine var cloneOfMachine = ItemsDB.GetMachine(cloneOf); if (cloneOfMachine.Value == null) continue; items = GetItemsForBucketDB(bucket); foreach (var item in items) { // Get the source for the current item var source = GetSourceForItemDB(item.Key); // Get the parent items and current machine name Dictionary parentItems = GetItemsForBucketDB(cloneOf); if (parentItems.Count == 0) continue; string? machineName = GetMachineForItemDB(item.Key).Value? .GetName(); // Special disk handling if (item.Value is Disk disk) { string? mergeTag = disk.GetStringFieldValue(Models.Metadata.Disk.MergeKey); // If the merge tag exists and the parent already contains it, skip if (mergeTag != null && GetItemsForBucketDB(cloneOf).Values .Where(i => i is Disk) .Select(i => (i as Disk)!.GetName()) .Contains(mergeTag)) { continue; } // If the merge tag exists but the parent doesn't contain it, add to parent else if (mergeTag != null && !GetItemsForBucketDB(cloneOf).Values .Where(i => i is Disk) .Select(i => (i as Disk)!.GetName()) .Contains(mergeTag)) { ItemsDB.RemapDatItemToMachine(item.Key, cloneOfMachine.Key); ItemsDB.AddItem(item.Value, cloneOfMachine.Key, source.Key); } // If there is no merge tag, add to parent else if (mergeTag == null) { ItemsDB.RemapDatItemToMachine(item.Key, cloneOfMachine.Key); ItemsDB.AddItem(item.Value, cloneOfMachine.Key, source.Key); } } // Special rom handling else if (item.Value is Rom rom) { // If the merge tag exists and the parent already contains it, skip if (rom.GetStringFieldValue(Models.Metadata.Rom.MergeKey) != null && GetItemsForBucketDB(cloneOf).Values .Where(i => i is Rom) .Select(i => (i as Rom)!.GetName()) .Contains(rom.GetStringFieldValue(Models.Metadata.Rom.MergeKey))) { continue; } // If the merge tag exists but the parent doesn't contain it, add to subfolder of parent else if (rom.GetStringFieldValue(Models.Metadata.Rom.MergeKey) != null && !GetItemsForBucketDB(cloneOf).Values .Where(i => i is Rom) .Select(i => (i as Rom)!.GetName()) .Contains(rom.GetStringFieldValue(Models.Metadata.Rom.MergeKey))) { if (subfolder) rom.SetName($"{machineName}\\{rom.GetName()}"); ItemsDB.RemapDatItemToMachine(item.Key, machineIndex: cloneOfMachine.Key); ItemsDB.AddItem(item.Value, cloneOfMachine.Key, source.Key); } // If the parent doesn't already contain this item, add to subfolder of parent else if (!GetItemsForBucketDB(cloneOf).Values.Contains(item.Value) || skipDedup) { if (subfolder) rom.SetName($"{machineName}\\{rom.GetName()}"); ItemsDB.RemapDatItemToMachine(item.Key, cloneOfMachine.Key); ItemsDB.AddItem(item.Value, cloneOfMachine.Key, source.Key); } } // All other that would be missing to subfolder of parent else if (!GetItemsForBucketDB(cloneOf).Values.Contains(item.Value)) { if (subfolder) item.Value.SetName($"{machineName}\\{item.Value.GetName()}"); ItemsDB.RemapDatItemToMachine(item.Key, cloneOfMachine.Key); ItemsDB.AddItem(item.Value, cloneOfMachine.Key, source.Key); } // Remove the current item RemoveItemDB(item.Key); } } } /// /// Use cloneof tags to add items to the children, setting the new romof tag in the process /// /// /// Applies to . /// Assumes items are bucketed by . /// private void AddItemsFromCloneOfParentImpl() { foreach (string bucket in Items.SortedKeys) { // If the bucket has no items in it List items = GetItemsForBucket(bucket); if (items.Count == 0) continue; // Get the machine var machine = items[0].GetMachine(); if (machine == null) continue; // Get the cloneof parent items string? cloneOf = machine.GetStringFieldValue(Models.Metadata.Machine.CloneOfKey); List parentItems = GetItemsForBucket(cloneOf); if (parentItems.Count == 0) continue; // If the parent exists and has items, we copy the items from the parent to the current game DatItem copyFrom = items[0]; foreach (DatItem item in parentItems) { DatItem datItem = (DatItem)item.Clone(); datItem.CopyMachineInformation(copyFrom); if (!items.Exists(i => string.Equals(i.GetName(), datItem.GetName(), StringComparison.OrdinalIgnoreCase)) && !items.Contains(datItem)) { AddItem(datItem, statsOnly: false); } } // Now we want to get the parent romof tag and put it in each of the items items = GetItemsForBucket(bucket); string? romof = GetItemsForBucket(cloneOf)[0].GetMachine()!.GetStringFieldValue(Models.Metadata.Machine.RomOfKey); foreach (DatItem item in items) { item.GetMachine()!.SetFieldValue(Models.Metadata.Machine.RomOfKey, romof); } } } /// /// Use cloneof tags to add items to the children, setting the new romof tag in the process /// /// /// Applies to . /// Assumes items are bucketed by . /// private void AddItemsFromCloneOfParentImplDB() { List buckets = [.. ItemsDB.SortedKeys]; foreach (string bucket in buckets) { // If the bucket has no items in it Dictionary items = GetItemsForBucketDB(bucket); if (items.Count == 0) continue; // Get the source for the first item var source = GetSourceForItemDB(items.First().Key); // Get the machine for the first item in the list var machine = GetMachineForItemDB(items.First().Key); if (machine.Value == null) continue; // Get the clone parent string? cloneOf = machine.Value.GetStringFieldValue(Models.Metadata.Machine.CloneOfKey); if (string.IsNullOrEmpty(cloneOf)) continue; // If the parent doesn't have any items, we want to continue Dictionary parentItems = GetItemsForBucketDB(cloneOf); if (parentItems.Count == 0) continue; // If the parent exists and has items, we copy the items from the parent to the current game foreach (var item in parentItems) { DatItem datItem = (DatItem)item.Value.Clone(); if (items.Values.Any(i => i.GetName()?.ToLowerInvariant() == datItem.GetName()?.ToLowerInvariant()) && items.Values.Any(i => i == datItem)) { ItemsDB.AddItem(datItem, machine.Key, source.Key); } } // Get the parent machine var parentMachine = GetMachineForItemDB(GetItemsForBucketDB(cloneOf).First().Key); if (parentMachine.Value == null) continue; // Now we want to get the parent romof tag and put it in each of the items items = GetItemsForBucketDB(bucket); string? romof = parentMachine.Value.GetStringFieldValue(Models.Metadata.Machine.RomOfKey); foreach (var key in items.Keys) { var itemMachine = GetMachineForItemDB(key); if (itemMachine.Value == null) continue; itemMachine.Value.SetFieldValue(Models.Metadata.Machine.RomOfKey, romof); } } } /// /// Use device_ref and optionally slotoption tags to add items to the children /// /// True if only child device sets are touched, false for non-device sets /// True if slotoptions tags are used as well, false otherwise /// True if any items were processed, false otherwise /// /// Applies to . /// Assumes items are bucketed by . /// private bool AddItemsFromDevicesImpl(bool deviceOnly, bool useSlotOptions) { bool foundnew = false; foreach (string bucket in Items.SortedKeys) { // If the bucket doesn't have items List datItems = GetItemsForBucket(bucket); if (datItems.Count == 0) continue; // If the machine (is/is not) a device, we want to continue if (deviceOnly ^ (datItems[0].GetMachine()!.GetBoolFieldValue(Models.Metadata.Machine.IsDeviceKey) == true)) continue; // Get all device reference names from the current machine List deviceReferences = datItems .FindAll(i => i is DeviceRef) .ConvertAll(i => i as DeviceRef) .ConvertAll(dr => dr!.GetName()) .Distinct() .ToList(); // Get all slot option names from the current machine List slotOptions = datItems .FindAll(i => i is Slot) .ConvertAll(i => i as Slot) .FindAll(s => s!.SlotOptionsSpecified) .SelectMany(s => s!.GetFieldValue(Models.Metadata.Slot.SlotOptionKey)!) .Select(so => so.GetStringFieldValue(Models.Metadata.SlotOption.DevNameKey)) .Distinct() .ToList(); // If we're checking device references if (deviceReferences.Count > 0) { // Loop through all names and check the corresponding machines var newDeviceReferences = new HashSet(); foreach (string? deviceReference in deviceReferences) { // Add to the list of new device reference names List devItems = GetItemsForBucket(deviceReference); if (devItems.Count == 0) continue; newDeviceReferences.UnionWith(devItems .FindAll(i => i is DeviceRef) .ConvertAll(i => (i as DeviceRef)!.GetName()!)); // Set new machine information and add to the current machine DatItem copyFrom = datItems[0]; foreach (DatItem item in devItems) { // If the parent machine doesn't already contain this item, add it if (!datItems.Exists(i => i.GetStringFieldValue(Models.Metadata.DatItem.TypeKey) == item.GetStringFieldValue(Models.Metadata.DatItem.TypeKey) && i.GetName() == item.GetName())) { // Set that we found new items foundnew = true; // Clone the item and then add it DatItem datItem = (DatItem)item.Clone(); datItem.CopyMachineInformation(copyFrom); AddItem(datItem, statsOnly: false); } } } // Now that every device reference is accounted for, add the new list of device references, if they don't already exist foreach (string deviceReference in newDeviceReferences) { if (!deviceReferences.Contains(deviceReference)) { var deviceRef = new DeviceRef(); deviceRef.SetName(deviceReference); datItems.Add(deviceRef); } } } // If we're checking slotoptions if (useSlotOptions && slotOptions.Count > 0) { // Loop through all names and check the corresponding machines var newSlotOptions = new HashSet(); foreach (string? slotOption in slotOptions) { // Add to the list of new slot option names List slotItems = GetItemsForBucket(slotOption); if (slotItems.Count == 0) continue; newSlotOptions.UnionWith(slotItems .FindAll(i => i is Slot) .FindAll(s => (s as Slot)!.SlotOptionsSpecified) .SelectMany(s => (s as Slot)!.GetFieldValue(Models.Metadata.Slot.SlotOptionKey)!) .Select(o => o.GetStringFieldValue(Models.Metadata.SlotOption.DevNameKey)!)); // Set new machine information and add to the current machine DatItem copyFrom = datItems[0]; foreach (DatItem item in slotItems) { // If the parent machine doesn't already contain this item, add it if (!datItems.Exists(i => i.GetStringFieldValue(Models.Metadata.DatItem.TypeKey) == item.GetStringFieldValue(Models.Metadata.DatItem.TypeKey) && i.GetName() == item.GetName())) { // Set that we found new items foundnew = true; // Clone the item and then add it DatItem datItem = (DatItem)item.Clone(); datItem.CopyMachineInformation(copyFrom); AddItem(datItem, statsOnly: false); } } } // Now that every device is accounted for, add the new list of slot options, if they don't already exist foreach (string slotOption in newSlotOptions) { if (!slotOptions.Contains(slotOption)) { var slotOptionItem = new SlotOption(); slotOptionItem.SetFieldValue(Models.Metadata.SlotOption.DevNameKey, slotOption); var slotItem = new Slot(); slotItem.SetFieldValue(Models.Metadata.Slot.SlotOptionKey, [slotOptionItem]); datItems.Add(slotItem); } } } } return foundnew; } /// /// Use device_ref and optionally slotoption tags to add items to the children /// /// True if only child device sets are touched, false for non-device sets /// True if slotoptions tags are used as well, false otherwise /// True if any items were processed, false otherwise /// /// Applies to . /// Assumes items are bucketed by . /// private bool AddItemsFromDevicesImplDB(bool deviceOnly, bool useSlotOptions) { bool foundnew = false; List buckets = [.. ItemsDB.SortedKeys]; foreach (string bucket in buckets) { // If the bucket has no items in it Dictionary items = GetItemsForBucketDB(bucket); if (items.Count == 0) continue; // Get the source for the first item var source = GetSourceForItemDB(items.First().Key); // Get the machine for the first item var machine = GetMachineForItemDB(items.First().Key); if (machine.Value == null) continue; // If the machine (is/is not) a device, we want to continue if (deviceOnly ^ (machine.Value.GetBoolFieldValue(Models.Metadata.Machine.IsDeviceKey) == true)) continue; // Get all device reference names from the current machine List deviceReferences = items.Values .Where(i => i is DeviceRef) .Select(i => i as DeviceRef) .Select(dr => dr!.GetName()) .Distinct() .ToList(); // Get all slot option names from the current machine List slotOptions = items.Values .Where(i => i is Slot) .Select(i => i as Slot) .Where(s => s!.SlotOptionsSpecified) .SelectMany(s => s!.GetFieldValue(Models.Metadata.Slot.SlotOptionKey)!) .Select(so => so.GetStringFieldValue(Models.Metadata.SlotOption.DevNameKey)) .Distinct() .ToList(); // If we're checking device references if (deviceReferences.Count > 0) { // Loop through all names and check the corresponding machines var newDeviceReferences = new HashSet(); foreach (string? deviceReference in deviceReferences) { // If the device reference is invalid if (deviceReference == null) continue; // If the machine doesn't exist then we continue Dictionary devItems = GetItemsForBucketDB(deviceReference); if (devItems.Count == 0) continue; // Add to the list of new device reference names newDeviceReferences.UnionWith(devItems.Values .Where(i => i is DeviceRef) .Select(i => (i as DeviceRef)!.GetName()!)); // Set new machine information and add to the current machine var copyFrom = GetMachineForItemDB(items.First().Key); if (copyFrom.Value == null) continue; foreach (var item in devItems.Values) { // If the parent machine doesn't already contain this item, add it if (!items.Values.Any(i => i.GetStringFieldValue(Models.Metadata.DatItem.TypeKey) == item.GetStringFieldValue(Models.Metadata.DatItem.TypeKey) && i.GetName() == item.GetName())) { // Set that we found new items foundnew = true; // Clone the item and then add it DatItem datItem = (DatItem)item.Clone(); ItemsDB.AddItem(datItem, machine.Key, source.Key); } } } // Now that every device reference is accounted for, add the new list of device references, if they don't already exist foreach (string deviceReference in newDeviceReferences) { if (!deviceReferences.Contains(deviceReference)) { var deviceRef = new DeviceRef(); deviceRef.SetName(deviceReference); ItemsDB.AddItem(deviceRef, machine.Key, source.Key); } } } // If we're checking slotoptions if (useSlotOptions && slotOptions.Count > 0) { // Loop through all names and check the corresponding machines var newSlotOptions = new HashSet(); foreach (string? slotOption in slotOptions) { // If the slot option is invalid if (slotOption == null) continue; // If the machine doesn't exist then we continue Dictionary slotItems = GetItemsForBucketDB(slotOption); if (slotItems.Count == 0) continue; // Add to the list of new slot option names newSlotOptions.UnionWith(slotItems.Values .Where(i => i is Slot) .Where(s => (s as Slot)!.SlotOptionsSpecified) .SelectMany(s => (s as Slot)!.GetFieldValue(Models.Metadata.Slot.SlotOptionKey)!) .Select(o => o.GetStringFieldValue(Models.Metadata.SlotOption.DevNameKey)!)); // Set new machine information and add to the current machine var copyFrom = GetMachineForItemDB(GetItemsForBucketDB(bucket).First().Key); if (copyFrom.Value == null) continue; foreach (var item in slotItems.Values) { // If the parent machine doesn't already contain this item, add it if (!items.Values.Any(i => i.GetStringFieldValue(Models.Metadata.DatItem.TypeKey) == item.GetStringFieldValue(Models.Metadata.DatItem.TypeKey) && i.GetName() == item.GetName())) { // Set that we found new items foundnew = true; // Clone the item and then add it DatItem datItem = (DatItem)item.Clone(); ItemsDB.AddItem(datItem, machine.Key, source.Key); } } } // Now that every device is accounted for, add the new list of slot options, if they don't already exist foreach (string slotOption in newSlotOptions) { if (!slotOptions.Contains(slotOption)) { var slotOptionItem = new SlotOption(); slotOptionItem.SetFieldValue(Models.Metadata.SlotOption.DevNameKey, slotOption); var slotItem = new Slot(); slotItem.SetFieldValue(Models.Metadata.Slot.SlotOptionKey, [slotOptionItem]); ItemsDB.AddItem(slotItem, machine.Key, source.Key); } } } } return foundnew; } /// /// Use romof tags to add items to the children /// /// /// Applies to . /// Assumes items are bucketed by . /// private void AddItemsFromRomOfParentImpl() { foreach (string bucket in Items.SortedKeys) { // If the bucket has no items in it List items = GetItemsForBucket(bucket); if (items.Count == 0) continue; // Get the machine var machine = items[0].GetMachine(); if (machine == null) continue; // Get the romof parent items string? romOf = machine.GetStringFieldValue(Models.Metadata.Machine.RomOfKey); List parentItems = GetItemsForBucket(romOf); if (parentItems.Count == 0) continue; // If the parent exists and has items, we copy the items from the parent to the current game DatItem copyFrom = items[0]; foreach (DatItem item in parentItems) { DatItem datItem = (DatItem)item.Clone(); datItem.CopyMachineInformation(copyFrom); if (!items.Exists(i => i.GetName() == datItem.GetName()) && !items.Contains(datItem)) AddItem(datItem, statsOnly: false); } } } /// /// Use romof tags to add items to the children /// /// /// Applies to . /// Assumes items are bucketed by . /// private void AddItemsFromRomOfParentImplDB() { List buckets = [.. ItemsDB.SortedKeys]; foreach (string bucket in buckets) { // If the bucket has no items in it Dictionary items = GetItemsForBucketDB(bucket); if (items.Count == 0) continue; // Get the source for the first item var source = GetSourceForItemDB(items.First().Key); // Get the machine for the first item var machine = GetMachineForItemDB(items.First().Key); if (machine.Value == null) continue; // Get the romof parent items string? romOf = machine.Value.GetStringFieldValue(Models.Metadata.Machine.RomOfKey); Dictionary parentItems = GetItemsForBucketDB(romOf); if (parentItems.Count == 0) continue; // If the parent exists and has items, we copy the items from the parent to the current game foreach (var item in parentItems) { DatItem datItem = (DatItem)item.Value.Clone(); if (items.Any(i => i.Value.GetName() == datItem.GetName()) && items.Any(i => i.Value == datItem)) { ItemsDB.AddItem(datItem, machine.Key, source.Key); } } } } /// /// Remove all BIOS and device sets /// /// /// Applies to . /// Assumes items are bucketed by . /// private void RemoveBiosAndDeviceSetsImpl() { foreach (string bucket in Items.SortedKeys) { // If the bucket has no items in it List items = GetItemsForBucket(bucket); if (items.Count == 0) continue; // Get the machine var machine = items[0].GetMachine(); if (machine == null) continue; // Remove flagged items if ((machine.GetBoolFieldValue(Models.Metadata.Machine.IsBiosKey) == true) || (machine.GetBoolFieldValue(Models.Metadata.Machine.IsDeviceKey) == true)) { RemoveBucket(bucket); } } } /// /// Remove all BIOS and device sets /// /// /// Applies to . /// Assumes items are bucketed by . /// private void RemoveBiosAndDeviceSetsImplDB() { List buckets = [.. ItemsDB.SortedKeys]; foreach (string bucket in buckets) { // If the bucket has no items in it Dictionary items = GetItemsForBucketDB(bucket); if (items.Count == 0) continue; // Get the machine var machine = GetMachineForItemDB(items.First().Key); if (machine.Value == null) continue; // Remove flagged items if ((machine.Value.GetBoolFieldValue(Models.Metadata.Machine.IsBiosKey) == true) || (machine.Value.GetBoolFieldValue(Models.Metadata.Machine.IsDeviceKey) == true)) { foreach (var key in items.Keys) { RemoveItemDB(key); } } // Remove the machine RemoveMachineDB(machine.Key); } } /// /// Use cloneof tags to remove items from the children /// /// /// Applies to . /// Assumes items are bucketed by . /// private void RemoveItemsFromCloneOfChildImpl() { foreach (string bucket in Items.SortedKeys) { // If the bucket has no items in it List items = GetItemsForBucket(bucket); if (items.Count == 0) continue; // Get the machine var machine = items[0].GetMachine(); if (machine == null) continue; // Get the cloneof parent items string? cloneOf = machine.GetStringFieldValue(Models.Metadata.Machine.CloneOfKey); List parentItems = GetItemsForBucket(cloneOf); if (parentItems.Count == 0) continue; // If the parent exists and has items, we remove the parent items from the current game foreach (DatItem item in parentItems) { var matchedItems = items.FindAll(i => i.Equals(item)); foreach (var match in matchedItems) { RemoveItem(bucket, match); } } // Now we want to get the parent romof tag and put it in each of the remaining items items = GetItemsForBucket(bucket); string? romof = GetItemsForBucket(cloneOf)[0].GetMachine()!.GetStringFieldValue(Models.Metadata.Machine.RomOfKey); foreach (DatItem item in items) { item.GetMachine()!.SetFieldValue(Models.Metadata.Machine.RomOfKey, romof); } } } /// /// Use cloneof tags to remove items from the children /// /// /// Applies to . /// Assumes items are bucketed by . /// private void RemoveItemsFromCloneOfChildImplDB() { List buckets = [.. ItemsDB.SortedKeys]; foreach (string bucket in buckets) { // If the bucket has no items in it Dictionary items = GetItemsForBucketDB(bucket); if (items.Count == 0) continue; // Get the machine for the first item var machine = GetMachineForItemDB(items.First().Key); if (machine.Value == null) continue; // Get the cloneof parent items string? cloneOf = machine.Value.GetStringFieldValue(Models.Metadata.Machine.CloneOfKey); Dictionary parentItems = GetItemsForBucketDB(cloneOf); if (parentItems == null || parentItems.Count == 0) continue; // If the parent exists and has items, we remove the parent items from the current game foreach (var item in parentItems) { var matchedItems = items.Where(i => i.Value.Equals(item.Value)); foreach (var match in matchedItems) { RemoveItemDB(match.Key); } } // Now we want to get the parent romof tag and put it in each of the remaining items items = GetItemsForBucketDB(bucket); machine = GetMachineForItemDB(GetItemsForBucketDB(cloneOf).First().Key); if (machine.Value == null) continue; string? romof = machine.Value.GetStringFieldValue(Models.Metadata.Machine.RomOfKey); foreach (var item in items) { machine = GetMachineForItemDB(item.Key); if (machine.Value == null) continue; machine.Value.SetFieldValue(Models.Metadata.Machine.RomOfKey, romof); } } } /// /// Use romof tags to remove items from children /// /// /// Applies to . /// Assumes items are bucketed by . /// private void RemoveItemsFromRomOfChildImpl() { // Loop through the romof tags foreach (string bucket in Items.SortedKeys) { // If the bucket has no items in it List items = GetItemsForBucket(bucket); if (items.Count == 0) continue; // Get the machine var machine = items[0].GetMachine(); if (machine == null) continue; // Get the romof parent items string? romOf = machine.GetStringFieldValue(Models.Metadata.Machine.RomOfKey); List parentItems = GetItemsForBucket(romOf); if (parentItems.Count == 0) continue; // If the parent exists and has items, we remove the items that are in the parent from the current game foreach (DatItem item in parentItems) { var matchedItems = items.FindAll(i => i.Equals(item)); foreach (var match in matchedItems) { RemoveItem(bucket, match); } } } } /// /// Use romof tags to remove bios items from children /// /// True if only child Bios sets are touched, false for non-bios sets /// /// Applies to . /// Assumes items are bucketed by . /// private void RemoveItemsFromRomOfChildImplDB() { // Loop through the romof tags List buckets = [.. ItemsDB.SortedKeys]; foreach (string bucket in buckets) { // If the bucket has no items in it Dictionary items = GetItemsForBucketDB(bucket); if (items.Count == 0) continue; // Get the machine for the item var machine = GetMachineForItemDB(items.First().Key); if (machine.Value == null) continue; // Get the romof parent items string? romOf = machine.Value.GetStringFieldValue(Models.Metadata.Machine.RomOfKey); Dictionary parentItems = GetItemsForBucketDB(romOf); if (parentItems.Count == 0) continue; // If the parent exists and has items, we remove the items that are in the parent from the current game foreach (var item in parentItems) { var matchedItems = items.Where(i => i.Value.Equals(item.Value)); foreach (var match in matchedItems) { RemoveItemDB(match.Key); } } } } /// /// Remove all romof and cloneof tags from all machines /// /// Applies to private void RemoveMachineRelationshipTagsImpl() { foreach (string bucket in Items.SortedKeys) { // If the bucket has no items in it var items = GetItemsForBucket(bucket); if (items == null || items.Count == 0) continue; foreach (DatItem item in items) { // Get the machine var machine = item.GetMachine(); if (machine == null) continue; machine.SetFieldValue(Models.Metadata.Machine.CloneOfKey, null); machine.SetFieldValue(Models.Metadata.Machine.RomOfKey, null); machine.SetFieldValue(Models.Metadata.Machine.SampleOfKey, null); } } } /// /// Remove all romof and cloneof tags from all machines /// /// Applies to private void RemoveMachineRelationshipTagsImplDB() { var machines = GetMachinesDB(); foreach (var machine in machines) { // Get the machine if (machine.Value == null) continue; machine.Value.SetFieldValue(Models.Metadata.Machine.CloneOfKey, null); machine.Value.SetFieldValue(Models.Metadata.Machine.RomOfKey, null); machine.Value.SetFieldValue(Models.Metadata.Machine.SampleOfKey, null); } } #endregion } }