diff --git a/SabreTools.DatFiles/ItemDictionary.cs b/SabreTools.DatFiles/ItemDictionary.cs index d157556f..0710af4f 100644 --- a/SabreTools.DatFiles/ItemDictionary.cs +++ b/SabreTools.DatFiles/ItemDictionary.cs @@ -881,6 +881,89 @@ namespace SabreTools.DatFiles } } + /// + /// Filter a DAT using 1G1R logic given an ordered set of regions + /// + /// List of regions in order of priority + /// + /// In the most technical sense, the way that the region list is being used does not + /// confine its values to be just regions. Since it's essentially acting like a + /// specialized version of the machine name filter, anything that is usually encapsulated + /// in parenthesis would be matched on, including disc numbers, languages, editions, + /// and anything else commonly used. Please note that, unlike other existing 1G1R + /// solutions, this does not have the ability to contain custom mappings of parent + /// to clone sets based on name, nor does it have the ability to match on the + /// Release DatItem type. + /// + public void SetOneGamePerRegion(List regionList) + { + // If we have null region list, make it empty + regionList ??= []; + + // For sake of ease, the first thing we want to do is bucket by game + BucketBy(ItemKey.Machine, DedupeType.None, norename: true); + + // Then we want to get a mapping of all machines to parents + Dictionary> parents = []; + foreach (string key in Keys) + { + DatItem item = this[key]![0]; + + // Match on CloneOf first + if (!string.IsNullOrEmpty(item.GetFieldValue(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.CloneOfKey))) + { + if (!parents.ContainsKey(item.GetFieldValue(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.CloneOfKey)!.ToLowerInvariant())) + parents.Add(item.GetFieldValue(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.CloneOfKey)!.ToLowerInvariant(), []); + + parents[item.GetFieldValue(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.CloneOfKey)!.ToLowerInvariant()].Add(item.GetFieldValue(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.NameKey)!.ToLowerInvariant()); + } + + // Then by RomOf + else if (!string.IsNullOrEmpty(item.GetFieldValue(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.RomOfKey))) + { + if (!parents.ContainsKey(item.GetFieldValue(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.RomOfKey)!.ToLowerInvariant())) + parents.Add(item.GetFieldValue(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.RomOfKey)!.ToLowerInvariant(), []); + + parents[item.GetFieldValue(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.RomOfKey)!.ToLowerInvariant()].Add(item.GetFieldValue(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.NameKey)!.ToLowerInvariant()); + } + + // Otherwise, treat it as a parent + else + { + if (!parents.ContainsKey(item.GetFieldValue(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.NameKey)!.ToLowerInvariant())) + parents.Add(item.GetFieldValue(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.NameKey)!.ToLowerInvariant(), []); + + parents[item.GetFieldValue(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.NameKey)!.ToLowerInvariant()].Add(item.GetFieldValue(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.NameKey)!.ToLowerInvariant()); + } + } + + // Once we have the full list of mappings, filter out games to keep + foreach (string key in parents.Keys) + { + // Find the first machine that matches the regions in order, if possible + string? machine = default; + foreach (string region in regionList) + { + machine = parents[key].FirstOrDefault(m => Regex.IsMatch(m, @"\(.*" + region + @".*\)", RegexOptions.IgnoreCase)); + if (machine != default) + break; + } + + // If we didn't get a match, use the parent + if (machine == default) + machine = key; + + // Remove the key from the list + parents[key].Remove(machine); + + // Remove the rest of the items from this key + parents[key].ForEach(k => Remove(k)); + } + + // Finally, strip out the parent tags + RemoveTagsFromChild(); + } + /// /// Ensure that all roms are in their own game (or at least try to ensure) /// @@ -1088,6 +1171,512 @@ namespace SabreTools.DatFiles #endregion + #region Splitting + + /// + /// Use romof tags to add roms to the children + /// + public void AddRomsFromBios() + { + List games = [.. Keys.OrderBy(g => g)]; + foreach (string game in games) + { + var items = this[game]; + if (items == null) + continue; + + // If the game has no items in it, we want to continue + if (items.Count == 0) + continue; + + // Determine if the game has a parent or not + string? parent = null; + if (!string.IsNullOrEmpty(items[0].GetFieldValue(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.RomOfKey))) + parent = items[0].GetFieldValue(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.RomOfKey); + + // If the parent doesnt exist, we want to continue + if (string.IsNullOrEmpty(parent)) + continue; + + // If the parent doesn't have any items, we want to continue + if (this[parent!]!.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]; + var parentItems = this[parent!]; + if (parentItems == null) + continue; + + foreach (DatItem item in parentItems) + { + DatItem datItem = (DatItem)item.Clone(); + datItem.CopyMachineInformation(copyFrom); + if (!items.Where(i => i.GetName() == datItem.GetName()).Any() && !items.Contains(datItem)) + Add(game, datItem); + } + } + } + + /// + /// Use device_ref and optionally slotoption tags to add roms to the children + /// + /// True if only child device sets are touched, false for non-device sets (default) + /// True if slotoptions tags are used as well, false otherwise + public bool AddRomsFromDevices(bool dev, bool useSlotOptions) + { + bool foundnew = false; + List machines = [.. Keys.OrderBy(g => g)]; + foreach (string machine in machines) + { + // If the machine doesn't have items, we continue + if (this[machine] == null || this[machine]!.Count == 0) + continue; + + // If the machine (is/is not) a device, we want to continue + if (dev ^ (this[machine]![0].GetFieldValue(DatItem.MachineKey)!.GetBoolFieldValue(Models.Metadata.Machine.IsDeviceKey) == true)) + continue; + + // Get all device reference names from the current machine + List deviceReferences = this[machine]! + .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 = this[machine]! + .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.Any()) + { + // Loop through all names and check the corresponding machines + List newDeviceReferences = []; + foreach (string? deviceReference in deviceReferences) + { + // If the machine doesn't exist then we continue + if (deviceReference == null || this[deviceReference] == null || this[deviceReference]!.Count == 0) + continue; + + // Add to the list of new device reference names + var devItems = this[deviceReference]; + if (devItems == null) + continue; + + newDeviceReferences.AddRange(devItems + .Where(i => i is DeviceRef) + .Select(i => (i as DeviceRef)!.GetName()!)); + + // Set new machine information and add to the current machine + DatItem copyFrom = this[machine]![0]; + foreach (DatItem item in devItems) + { + // If the parent machine doesn't already contain this item, add it + if (!this[machine]!.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(); + datItem.CopyMachineInformation(copyFrom); + Add(machine, datItem); + } + } + } + + // 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.Distinct()) + { + if (!deviceReferences.Contains(deviceReference)) + { + var deviceRef = new DeviceRef(); + deviceRef.SetName(deviceReference); + this[machine]!.Add(deviceRef); + } + } + } + + // If we're checking slotoptions + if (useSlotOptions && slotOptions.Any()) + { + // Loop through all names and check the corresponding machines + List newSlotOptions = []; + foreach (string? slotOption in slotOptions) + { + // If the machine doesn't exist then we continue + if (slotOption == null || this[slotOption] == null || this[slotOption]!.Count == 0) + continue; + + // Add to the list of new slot option names + var slotItems = this[slotOption]; + if (slotItems == null) + continue; + + newSlotOptions.AddRange(slotItems + .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 + DatItem copyFrom = this[machine]![0]; + foreach (DatItem item in slotItems) + { + // If the parent machine doesn't already contain this item, add it + if (!this[machine]!.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(); + datItem.CopyMachineInformation(copyFrom); + Add(machine, datItem); + } + } + } + + // 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.Distinct()) + { + 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]); + + this[machine]!.Add(slotItem); + } + } + } + } + + return foundnew; + } + + /// + /// Use cloneof tags to add roms to the children, setting the new romof tag in the process + /// + public void AddRomsFromParent() + { + List games = [.. Keys.OrderBy(g => g)]; + foreach (string game in games) + { + // If the game has no items in it, we want to continue + var items = this[game]; + if (items == null || items.Count == 0) + continue; + + // Determine if the game has a parent or not + string? parent = null; + if (!string.IsNullOrEmpty(items[0].GetFieldValue(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.CloneOfKey))) + parent = items[0].GetFieldValue(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.CloneOfKey); + + // If the parent doesnt exist, we want to continue + if (string.IsNullOrEmpty(parent)) + continue; + + // If the parent doesn't have any items, we want to continue + if (this[parent!]!.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]; + var parentItems = this[parent!]; + foreach (DatItem item in parentItems!) + { + DatItem datItem = (DatItem)item.Clone(); + datItem.CopyMachineInformation(copyFrom); + if (!items.Where(i => i.GetName()?.ToLowerInvariant() == datItem.GetName()?.ToLowerInvariant()).Any() + && !items.Contains(datItem)) + { + Add(game, datItem); + } + } + + // Now we want to get the parent romof tag and put it in each of the items + items = this[game]; + string? romof = this[parent!]![0].GetFieldValue(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.RomOfKey); + foreach (DatItem item in items!) + { + item.GetFieldValue(DatItem.MachineKey)!.SetFieldValue(Models.Metadata.Machine.RomOfKey, romof); + } + } + } + + /// + /// Use cloneof tags to add roms 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 + public void AddRomsFromChildren(bool subfolder, bool skipDedup) + { + List games = [.. Keys.OrderBy(g => g)]; + foreach (string game in games) + { + // If the game has no items in it, we want to continue + var items = this[game]; + if (items == null || items.Count == 0) + continue; + + // Determine if the game has a parent or not + string? parent = null; + if (!string.IsNullOrEmpty(items[0].GetFieldValue(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.CloneOfKey))) + parent = items[0].GetFieldValue(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.CloneOfKey); + + // If there is no parent, then we continue + if (string.IsNullOrEmpty(parent)) + continue; + + // Otherwise, move the items from the current game to a subfolder of the parent game + DatItem copyFrom; + if (this[parent!]!.Count == 0) + { + copyFrom = new Rom(); + copyFrom.GetFieldValue(DatItem.MachineKey)!.SetFieldValue(Models.Metadata.Machine.NameKey, parent); + copyFrom.GetFieldValue(DatItem.MachineKey)!.SetFieldValue(Models.Metadata.Machine.DescriptionKey, parent); + } + else + { + copyFrom = this[parent!]![0]; + } + + items = this[game]; + 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 && this[parent!]! + .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 && !this[parent!]! + .Where(i => i is Disk) + .Select(i => (i as Disk)!.GetName()).Contains(mergeTag)) + { + disk.CopyMachineInformation(copyFrom); + Add(parent!, disk); + } + + // If there is no merge tag, add to parent + else if (mergeTag == null) + { + disk.CopyMachineInformation(copyFrom); + Add(parent!, disk); + } + } + + // 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 && this[parent!]! + .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 && !this[parent!]! + .Where(i => i is Rom).Select(i => (i as Rom)!.GetName()) + .Contains(rom.GetStringFieldValue(Models.Metadata.Rom.MergeKey))) + { + if (subfolder) + rom.SetName($"{rom.GetFieldValue(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.NameKey)}\\{rom.GetName()}"); + + rom.CopyMachineInformation(copyFrom); + Add(parent!, rom); + } + + // If the parent doesn't already contain this item, add to subfolder of parent + else if (!this[parent!]!.Contains(item) || skipDedup) + { + if (subfolder) + rom.SetName($"{item.GetFieldValue(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.NameKey)}\\{rom.GetName()}"); + + rom.CopyMachineInformation(copyFrom); + Add(parent!, rom); + } + } + + // All other that would be missing to subfolder of parent + else if (!this[parent!]!.Contains(item)) + { + if (subfolder) + item.SetName($"{item.GetFieldValue(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.NameKey)}\\{item.GetName()}"); + + item.CopyMachineInformation(copyFrom); + Add(parent!, item); + } + } + + // Then, remove the old game so it's not picked up by the writer + Remove(game); + } + } + + /// + /// Remove all BIOS and device sets + /// + public void RemoveBiosAndDeviceSets() + { + List games = [.. Keys.OrderBy(g => g)]; + foreach (string game in games) + { + var items = this[game]; + if (items == null) + continue; + + if (items.Count > 0 + && ((items[0].GetFieldValue(DatItem.MachineKey)!.GetBoolFieldValue(Models.Metadata.Machine.IsBiosKey) == true) + || (items[0].GetFieldValue(DatItem.MachineKey)!.GetBoolFieldValue(Models.Metadata.Machine.IsDeviceKey) == true))) + { + Remove(game); + } + } + } + + /// + /// Use romof tags to remove bios roms from children + /// + /// True if only child Bios sets are touched, false for non-bios sets + public void RemoveBiosRomsFromChild(bool bios) + { + // Loop through the romof tags + List games = [.. Keys.OrderBy(g => g)]; + foreach (string game in games) + { + // If the game has no items in it, we want to continue + var items = this[game]; + if (items == null || items.Count == 0) + continue; + + // If the game (is/is not) a bios, we want to continue + if (bios ^ (items[0].GetFieldValue(DatItem.MachineKey)!.GetBoolFieldValue(Models.Metadata.Machine.IsBiosKey) == true)) + continue; + + // Determine if the game has a parent or not + string? parent = null; + if (!string.IsNullOrEmpty(items[0].GetFieldValue(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.RomOfKey))) + parent = items[0].GetFieldValue(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.RomOfKey); + + // If the parent doesnt exist, we want to continue + if (string.IsNullOrEmpty(parent)) + continue; + + // If the parent doesn't have any items, we want to continue + if (this[parent!]!.Count == 0) + continue; + + // If the parent exists and has items, we remove the items that are in the parent from the current game + var parentItems = this[parent!]; + if (parentItems == null) + continue; + + foreach (DatItem item in parentItems) + { + DatItem datItem = (DatItem)item.Clone(); + while (items.Contains(datItem)) + { + Remove(game, datItem); + } + } + } + } + + /// + /// Use cloneof tags to remove roms from the children + /// + public void RemoveRomsFromChild() + { + List games = [.. Keys.OrderBy(g => g)]; + foreach (string game in games) + { + var items = this[game]; + if (items == null) + continue; + + // If the game has no items in it, we want to continue + if (items.Count == 0) + continue; + + // Determine if the game has a parent or not + string? parent = null; + if (!string.IsNullOrEmpty(items[0].GetFieldValue(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.CloneOfKey))) + parent = items[0].GetFieldValue(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.CloneOfKey); + + // If the parent doesnt exist, we want to continue + if (string.IsNullOrEmpty(parent)) + continue; + + // If the parent doesn't have any items, we want to continue + if (this[parent!] == null || this[parent!]!.Count == 0) + continue; + + // If the parent exists and has items, we remove the parent items from the current game + var parentItems = this[parent!]; + foreach (DatItem item in parentItems!) + { + DatItem datItem = (DatItem)item.Clone(); + while (items.Contains(datItem)) + { + Remove(game, datItem); + } + } + + // Now we want to get the parent romof tag and put it in each of the remaining items + items = this[game]; + string? romof = this[parent!]![0].GetFieldValue(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.RomOfKey); + foreach (DatItem item in items!) + { + item.GetFieldValue(DatItem.MachineKey)!.SetFieldValue(Models.Metadata.Machine.RomOfKey, romof); + } + } + } + + /// + /// Remove all romof and cloneof tags from all games + /// + public void RemoveTagsFromChild() + { + List games = [.. Keys.OrderBy(g => g)]; + foreach (string game in games) + { + var items = this[game]; + if (items == null) + continue; + + foreach (DatItem item in items) + { + item.GetFieldValue(DatItem.MachineKey)!.SetFieldValue(Models.Metadata.Machine.CloneOfKey, null); + item.GetFieldValue(DatItem.MachineKey)!.SetFieldValue(Models.Metadata.Machine.RomOfKey, null); + item.GetFieldValue(DatItem.MachineKey)!.SetFieldValue(Models.Metadata.Machine.SampleOfKey, null); + } + } + } + + #endregion + #region Statistics /// diff --git a/SabreTools.Filtering/Cleaner.cs b/SabreTools.Filtering/Cleaner.cs index eded4a29..28448322 100644 --- a/SabreTools.Filtering/Cleaner.cs +++ b/SabreTools.Filtering/Cleaner.cs @@ -134,7 +134,7 @@ namespace SabreTools.Filtering // Run the one rom per game logic, if required if (OneGamePerRegion == true && RegionList != null) { - SetOneGamePerRegion(datFile); + datFile.Items.SetOneGamePerRegion(RegionList); datFile.ItemsDB.SetOneGamePerRegion(RegionList); } @@ -233,90 +233,6 @@ namespace SabreTools.Filtering } } - /// - /// Filter a DAT using 1G1R logic given an ordered set of regions - /// - /// Current DatFile object to run operations on - /// - /// In the most technical sense, the way that the region list is being used does not - /// confine its values to be just regions. Since it's essentially acting like a - /// specialized version of the machine name filter, anything that is usually encapsulated - /// in parenthesis would be matched on, including disc numbers, languages, editions, - /// and anything else commonly used. Please note that, unlike other existing 1G1R - /// solutions, this does not have the ability to contain custom mappings of parent - /// to clone sets based on name, nor does it have the ability to match on the - /// Release DatItem type. - /// - internal void SetOneGamePerRegion(DatFile datFile) - { - // If we have null region list, make it empty - RegionList ??= []; - - // For sake of ease, the first thing we want to do is bucket by game - datFile.Items.BucketBy(ItemKey.Machine, DedupeType.None, norename: true); - - // Then we want to get a mapping of all machines to parents - Dictionary> parents = []; - foreach (string key in datFile.Items.Keys) - { - DatItem item = datFile.Items[key]![0]; - - // Match on CloneOf first - if (!string.IsNullOrEmpty(item.GetFieldValue(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.CloneOfKey))) - { - if (!parents.ContainsKey(item.GetFieldValue(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.CloneOfKey)!.ToLowerInvariant())) - parents.Add(item.GetFieldValue(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.CloneOfKey)!.ToLowerInvariant(), []); - - parents[item.GetFieldValue(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.CloneOfKey)!.ToLowerInvariant()].Add(item.GetFieldValue(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.NameKey)!.ToLowerInvariant()); - } - - // Then by RomOf - else if (!string.IsNullOrEmpty(item.GetFieldValue(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.RomOfKey))) - { - if (!parents.ContainsKey(item.GetFieldValue(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.RomOfKey)!.ToLowerInvariant())) - parents.Add(item.GetFieldValue(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.RomOfKey)!.ToLowerInvariant(), []); - - parents[item.GetFieldValue(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.RomOfKey)!.ToLowerInvariant()].Add(item.GetFieldValue(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.NameKey)!.ToLowerInvariant()); - } - - // Otherwise, treat it as a parent - else - { - if (!parents.ContainsKey(item.GetFieldValue(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.NameKey)!.ToLowerInvariant())) - parents.Add(item.GetFieldValue(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.NameKey)!.ToLowerInvariant(), []); - - parents[item.GetFieldValue(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.NameKey)!.ToLowerInvariant()].Add(item.GetFieldValue(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.NameKey)!.ToLowerInvariant()); - } - } - - // Once we have the full list of mappings, filter out games to keep - foreach (string key in parents.Keys) - { - // Find the first machine that matches the regions in order, if possible - string? machine = default; - foreach (string region in RegionList) - { - machine = parents[key].FirstOrDefault(m => Regex.IsMatch(m, @"\(.*" + region + @".*\)", RegexOptions.IgnoreCase)); - if (machine != default) - break; - } - - // If we didn't get a match, use the parent - if (machine == default) - machine = key; - - // Remove the key from the list - parents[key].Remove(machine); - - // Remove the rest of the items from this key - parents[key].ForEach(k => datFile.Items.Remove(k)); - } - - // Finally, strip out the parent tags - Splitter.RemoveTagsFromChild(datFile); - datFile.ItemsDB.RemoveTagsFromChild(); - } - /// /// Ensure that all roms are in their own game (or at least try to ensure) /// diff --git a/SabreTools.Filtering/Splitter.cs b/SabreTools.Filtering/Splitter.cs index ce9bba74..50a8cc6e 100644 --- a/SabreTools.Filtering/Splitter.cs +++ b/SabreTools.Filtering/Splitter.cs @@ -1,10 +1,7 @@ using System; -using System.Collections.Generic; -using System.Linq; using SabreTools.Core.Tools; using SabreTools.DatFiles; using SabreTools.DatItems; -using SabreTools.DatItems.Formats; using SabreTools.Logging; namespace SabreTools.Filtering @@ -108,13 +105,13 @@ namespace SabreTools.Filtering datFile.Items.BucketBy(ItemKey.Machine, DedupeType.None, norename: true); // Now we want to loop through all of the games and set the correct information - while (AddRomsFromDevices(datFile, false, false)) ; + while (datFile.Items.AddRomsFromDevices(false, false)) ; while (datFile.ItemsDB.AddRomsFromDevices(false, false)) ; - while (AddRomsFromDevices(datFile, true, false)) ; + while (datFile.Items.AddRomsFromDevices(true, false)) ; while (datFile.ItemsDB.AddRomsFromDevices(true, false)) ; // Then, remove the romof and cloneof tags so it's not picked up by the manager - RemoveTagsFromChild(datFile); + datFile.Items.RemoveTagsFromChild(); datFile.ItemsDB.RemoveTagsFromChild(); } @@ -130,17 +127,17 @@ namespace SabreTools.Filtering datFile.Items.BucketBy(ItemKey.Machine, DedupeType.None, norename: true); // Now we want to loop through all of the games and set the correct information - AddRomsFromChildren(datFile, skipDedup: false); + datFile.Items.AddRomsFromChildren(true, false); datFile.ItemsDB.AddRomsFromChildren(true, false); // Now that we have looped through the cloneof tags, we loop through the romof tags - RemoveBiosRomsFromChild(datFile, false); + datFile.Items.RemoveBiosRomsFromChild(false); datFile.ItemsDB.RemoveBiosRomsFromChild(false); - RemoveBiosRomsFromChild(datFile, true); + datFile.Items.RemoveBiosRomsFromChild(true); datFile.ItemsDB.RemoveBiosRomsFromChild(true); // Finally, remove the romof and cloneof tags so it's not picked up by the manager - RemoveTagsFromChild(datFile); + datFile.Items.RemoveTagsFromChild(); datFile.ItemsDB.RemoveTagsFromChild(); } @@ -156,19 +153,19 @@ namespace SabreTools.Filtering datFile.Items.BucketBy(ItemKey.Machine, DedupeType.None, norename: true); // Now we want to loop through all of the games and set the correct information - while (AddRomsFromDevices(datFile, true, true)) ; + while (datFile.Items.AddRomsFromDevices(true, true)) ; while (datFile.ItemsDB.AddRomsFromDevices(true, true)) ; - AddRomsFromDevices(datFile, false, true); + datFile.Items.AddRomsFromDevices(false, true); datFile.ItemsDB.AddRomsFromDevices(false, true); - AddRomsFromParent(datFile); + datFile.Items.AddRomsFromParent(); datFile.ItemsDB.AddRomsFromParent(); // Now that we have looped through the cloneof tags, we loop through the romof tags - AddRomsFromBios(datFile); + datFile.Items.AddRomsFromBios(); datFile.ItemsDB.AddRomsFromBios(); // Then, remove the romof and cloneof tags so it's not picked up by the manager - RemoveTagsFromChild(datFile); + datFile.Items.RemoveTagsFromChild(); datFile.ItemsDB.RemoveTagsFromChild(); } @@ -184,17 +181,17 @@ namespace SabreTools.Filtering datFile.Items.BucketBy(ItemKey.Machine, DedupeType.None, norename: true); // Now we want to loop through all of the games and set the correct information - AddRomsFromChildren(datFile, skipDedup: true); + datFile.Items.AddRomsFromChildren(true, true); datFile.ItemsDB.AddRomsFromChildren(true, true); // Now that we have looped through the cloneof tags, we loop through the romof tags - RemoveBiosRomsFromChild(datFile, false); + datFile.Items.RemoveBiosRomsFromChild(false); datFile.ItemsDB.RemoveBiosRomsFromChild(false); - RemoveBiosRomsFromChild(datFile, true); + datFile.Items.RemoveBiosRomsFromChild(true); datFile.ItemsDB.RemoveBiosRomsFromChild(true); // Finally, remove the romof and cloneof tags so it's not picked up by the manager - RemoveTagsFromChild(datFile); + datFile.Items.RemoveTagsFromChild(); datFile.ItemsDB.RemoveTagsFromChild(); } @@ -210,17 +207,17 @@ namespace SabreTools.Filtering datFile.Items.BucketBy(ItemKey.Machine, DedupeType.None, norename: true); // Now we want to loop through all of the games and set the correct information - AddRomsFromParent(datFile); + datFile.Items.AddRomsFromParent(); datFile.ItemsDB.AddRomsFromParent(); // Now that we have looped through the cloneof tags, we loop through the romof tags - RemoveBiosRomsFromChild(datFile, false); + datFile.Items.RemoveBiosRomsFromChild(false); datFile.ItemsDB.RemoveBiosRomsFromChild(false); - RemoveBiosRomsFromChild(datFile, true); + datFile.Items.RemoveBiosRomsFromChild(true); datFile.ItemsDB.RemoveBiosRomsFromChild(true); // Finally, remove the romof and cloneof tags so it's not picked up by the manager - RemoveTagsFromChild(datFile); + datFile.Items.RemoveTagsFromChild(); datFile.ItemsDB.RemoveTagsFromChild(); } @@ -236,530 +233,20 @@ namespace SabreTools.Filtering datFile.Items.BucketBy(ItemKey.Machine, DedupeType.None, norename: true); // Now we want to loop through all of the games and set the correct information - RemoveRomsFromChild(datFile); + datFile.Items.RemoveRomsFromChild(); datFile.ItemsDB.RemoveRomsFromChild(); // Now that we have looped through the cloneof tags, we loop through the romof tags - RemoveBiosRomsFromChild(datFile, false); + datFile.Items.RemoveBiosRomsFromChild(false); datFile.ItemsDB.RemoveBiosRomsFromChild(false); - RemoveBiosRomsFromChild(datFile, true); + datFile.Items.RemoveBiosRomsFromChild(true); datFile.ItemsDB.RemoveBiosRomsFromChild(true); // Finally, remove the romof and cloneof tags so it's not picked up by the manager - RemoveTagsFromChild(datFile); + datFile.Items.RemoveTagsFromChild(); datFile.ItemsDB.RemoveTagsFromChild(); } - /// - /// Use romof tags to add roms to the children - /// - /// Current DatFile object to run operations on - internal static void AddRomsFromBios(DatFile datFile) - { - List games = datFile.Items.Keys.OrderBy(g => g).ToList(); - foreach (string game in games) - { - var items = datFile.Items[game]; - if (items == null) - continue; - - // If the game has no items in it, we want to continue - if (items.Count == 0) - continue; - - // Determine if the game has a parent or not - string? parent = null; - if (!string.IsNullOrEmpty(items[0].GetFieldValue(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.RomOfKey))) - parent = items[0].GetFieldValue(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.RomOfKey); - - // If the parent doesnt exist, we want to continue - if (string.IsNullOrEmpty(parent)) - continue; - - // If the parent doesn't have any items, we want to continue - if (datFile.Items[parent!]!.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]; - var parentItems = datFile.Items[parent!]; - if (parentItems == null) - continue; - - foreach (DatItem item in parentItems) - { - DatItem datItem = (DatItem)item.Clone(); - datItem.CopyMachineInformation(copyFrom); - if (!items.Where(i => i.GetName() == datItem.GetName()).Any() && !items.Contains(datItem)) - datFile.Items.Add(game, datItem); - } - } - } - - /// - /// Use device_ref and optionally slotoption tags to add roms to the children - /// - /// Current DatFile object to run operations on - /// True if only child device sets are touched, false for non-device sets (default) - /// True if slotoptions tags are used as well, false otherwise - internal static bool AddRomsFromDevices(DatFile datFile, bool dev = false, bool useSlotOptions = false) - { - bool foundnew = false; - List machines = datFile.Items.Keys.OrderBy(g => g).ToList(); - foreach (string machine in machines) - { - // If the machine doesn't have items, we continue - if (datFile.Items[machine] == null || datFile.Items[machine]!.Count == 0) - continue; - - // If the machine (is/is not) a device, we want to continue - if (dev ^ (datFile.Items[machine]![0].GetFieldValue(DatItem.MachineKey)!.GetBoolFieldValue(Models.Metadata.Machine.IsDeviceKey) == true)) - continue; - - // Get all device reference names from the current machine - List deviceReferences = datFile.Items[machine]! - .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 = datFile.Items[machine]! - .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.Any()) - { - // Loop through all names and check the corresponding machines - List newDeviceReferences = []; - foreach (string? deviceReference in deviceReferences) - { - // If the machine doesn't exist then we continue - if (deviceReference == null || datFile.Items[deviceReference] == null || datFile.Items[deviceReference]!.Count == 0) - continue; - - // Add to the list of new device reference names - var devItems = datFile.Items[deviceReference]; - if (devItems == null) - continue; - - newDeviceReferences.AddRange(devItems - .Where(i => i is DeviceRef) - .Select(i => (i as DeviceRef)!.GetName()!)); - - // Set new machine information and add to the current machine - DatItem copyFrom = datFile.Items[machine]![0]; - foreach (DatItem item in devItems) - { - // If the parent machine doesn't already contain this item, add it - if (!datFile.Items[machine]!.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(); - datItem.CopyMachineInformation(copyFrom); - datFile.Items.Add(machine, datItem); - } - } - } - - // 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.Distinct()) - { - if (!deviceReferences.Contains(deviceReference)) - { - var deviceRef = new DeviceRef(); - deviceRef.SetName(deviceReference); - datFile.Items[machine]!.Add(deviceRef); - } - } - } - - // If we're checking slotoptions - if (useSlotOptions && slotOptions.Any()) - { - // Loop through all names and check the corresponding machines - List newSlotOptions = new(); - foreach (string? slotOption in slotOptions) - { - // If the machine doesn't exist then we continue - if (slotOption == null || datFile.Items[slotOption] == null || datFile.Items[slotOption]!.Count == 0) - continue; - - // Add to the list of new slot option names - var slotItems = datFile.Items[slotOption]; - if (slotItems == null) - continue; - - newSlotOptions.AddRange(slotItems - .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 - DatItem copyFrom = datFile.Items[machine]![0]; - foreach (DatItem item in slotItems) - { - // If the parent machine doesn't already contain this item, add it - if (!datFile.Items[machine]!.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(); - datItem.CopyMachineInformation(copyFrom); - datFile.Items.Add(machine, datItem); - } - } - } - - // 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.Distinct()) - { - 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]); - - datFile.Items[machine]!.Add(slotItem); - } - } - } - } - - return foundnew; - } - - /// - /// Use cloneof tags to add roms to the children, setting the new romof tag in the process - /// - /// Current DatFile object to run operations on - internal static void AddRomsFromParent(DatFile datFile) - { - List games = datFile.Items.Keys.OrderBy(g => g).ToList(); - foreach (string game in games) - { - // If the game has no items in it, we want to continue - var items = datFile.Items[game]; - if (items == null || items.Count == 0) - continue; - - // Determine if the game has a parent or not - string? parent = null; - if (!string.IsNullOrEmpty(items[0].GetFieldValue(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.CloneOfKey))) - parent = items[0].GetFieldValue(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.CloneOfKey); - - // If the parent doesnt exist, we want to continue - if (string.IsNullOrEmpty(parent)) - continue; - - // If the parent doesn't have any items, we want to continue - if (datFile.Items[parent!]!.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]; - var parentItems = datFile.Items[parent!]; - foreach (DatItem item in parentItems!) - { - DatItem datItem = (DatItem)item.Clone(); - datItem.CopyMachineInformation(copyFrom); - if (!items.Where(i => i.GetName()?.ToLowerInvariant() == datItem.GetName()?.ToLowerInvariant()).Any() - && !items.Contains(datItem)) - { - datFile.Items.Add(game, datItem); - } - } - - // Now we want to get the parent romof tag and put it in each of the items - items = datFile.Items[game]; - string? romof = datFile.Items[parent!]![0].GetFieldValue(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.RomOfKey); - foreach (DatItem item in items!) - { - item.GetFieldValue(DatItem.MachineKey)!.SetFieldValue(Models.Metadata.Machine.RomOfKey, romof); - } - } - } - - /// - /// Use cloneof tags to add roms to the parents, removing the child sets in the process - /// - /// Current DatFile object to run operations on - /// True to add DatItems to subfolder of parent (not including Disk), false otherwise - /// True to skip checking for duplicate ROMs in parent, false otherwise - internal static void AddRomsFromChildren(DatFile datFile, bool subfolder = true, bool skipDedup = false) - { - List games = datFile.Items.Keys.OrderBy(g => g).ToList(); - foreach (string game in games) - { - // If the game has no items in it, we want to continue - var items = datFile.Items[game]; - if (items == null || items.Count == 0) - continue; - - // Determine if the game has a parent or not - string? parent = null; - if (!string.IsNullOrEmpty(items[0].GetFieldValue(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.CloneOfKey))) - parent = items[0].GetFieldValue(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.CloneOfKey); - - // If there is no parent, then we continue - if (string.IsNullOrEmpty(parent)) - continue; - - // Otherwise, move the items from the current game to a subfolder of the parent game - DatItem copyFrom; - if (datFile.Items[parent!]!.Count == 0) - { - copyFrom = new Rom(); - copyFrom.GetFieldValue(DatItem.MachineKey)!.SetFieldValue(Models.Metadata.Machine.NameKey, parent); - copyFrom.GetFieldValue(DatItem.MachineKey)!.SetFieldValue(Models.Metadata.Machine.DescriptionKey, parent); - } - else - { - copyFrom = datFile.Items[parent!]![0]; - } - - items = datFile.Items[game]; - 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 && datFile.Items[parent!]! - .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 && !datFile.Items[parent!]! - .Where(i => i is Disk) - .Select(i => (i as Disk)!.GetName()).Contains(mergeTag)) - { - disk.CopyMachineInformation(copyFrom); - datFile.Items.Add(parent!, disk); - } - - // If there is no merge tag, add to parent - else if (mergeTag == null) - { - disk.CopyMachineInformation(copyFrom); - datFile.Items.Add(parent!, disk); - } - } - - // 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 && datFile.Items[parent!]! - .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 && !datFile.Items[parent!]! - .Where(i => i is Rom).Select(i => (i as Rom)!.GetName()) - .Contains(rom.GetStringFieldValue(Models.Metadata.Rom.MergeKey))) - { - if (subfolder) - rom.SetName($"{rom.GetFieldValue(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.NameKey)}\\{rom.GetName()}"); - - rom.CopyMachineInformation(copyFrom); - datFile.Items.Add(parent!, rom); - } - - // If the parent doesn't already contain this item, add to subfolder of parent - else if (!datFile.Items[parent!]!.Contains(item) || skipDedup) - { - if (subfolder) - rom.SetName($"{item.GetFieldValue(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.NameKey)}\\{rom.GetName()}"); - - rom.CopyMachineInformation(copyFrom); - datFile.Items.Add(parent!, rom); - } - } - - // All other that would be missing to subfolder of parent - else if (!datFile.Items[parent!]!.Contains(item)) - { - if (subfolder) - item.SetName($"{item.GetFieldValue(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.NameKey)}\\{item.GetName()}"); - - item.CopyMachineInformation(copyFrom); - datFile.Items.Add(parent!, item); - } - } - - // Then, remove the old game so it's not picked up by the writer - datFile.Items.Remove(game); - } - } - - /// - /// Remove all BIOS and device sets - /// - /// Current DatFile object to run operations on - internal static void RemoveBiosAndDeviceSets(DatFile datFile) - { - List games = datFile.Items.Keys.OrderBy(g => g).ToList(); - foreach (string game in games) - { - var items = datFile.Items[game]; - if (items == null) - continue; - - if (items.Count > 0 - && ((items[0].GetFieldValue(DatItem.MachineKey)!.GetBoolFieldValue(Models.Metadata.Machine.IsBiosKey) == true) - || (items[0].GetFieldValue(DatItem.MachineKey)!.GetBoolFieldValue(Models.Metadata.Machine.IsDeviceKey) == true))) - { - datFile.Items.Remove(game); - } - } - } - - /// - /// Use romof tags to remove bios roms from children - /// - /// Current DatFile object to run operations on - /// True if only child Bios sets are touched, false for non-bios sets (default) - internal static void RemoveBiosRomsFromChild(DatFile datFile, bool bios = false) - { - // Loop through the romof tags - List games = datFile.Items.Keys.OrderBy(g => g).ToList(); - foreach (string game in games) - { - // If the game has no items in it, we want to continue - var items = datFile.Items[game]; - if (items == null || items.Count == 0) - continue; - - // If the game (is/is not) a bios, we want to continue - if (bios ^ (items[0].GetFieldValue(DatItem.MachineKey)!.GetBoolFieldValue(Models.Metadata.Machine.IsBiosKey) == true)) - continue; - - // Determine if the game has a parent or not - string? parent = null; - if (!string.IsNullOrEmpty(items[0].GetFieldValue(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.RomOfKey))) - parent = items[0].GetFieldValue(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.RomOfKey); - - // If the parent doesnt exist, we want to continue - if (string.IsNullOrEmpty(parent)) - continue; - - // If the parent doesn't have any items, we want to continue - if (datFile.Items[parent!]!.Count == 0) - continue; - - // If the parent exists and has items, we remove the items that are in the parent from the current game - var parentItems = datFile.Items[parent!]; - if (parentItems == null) - continue; - - foreach (DatItem item in parentItems) - { - DatItem datItem = (DatItem)item.Clone(); - while (items.Contains(datItem)) - { - datFile.Items.Remove(game, datItem); - } - } - } - } - - /// - /// Use cloneof tags to remove roms from the children - /// - /// Current DatFile object to run operations on - internal static void RemoveRomsFromChild(DatFile datFile) - { - List games = datFile.Items.Keys.OrderBy(g => g).ToList(); - foreach (string game in games) - { - var items = datFile.Items[game]; - if (items == null) - continue; - - // If the game has no items in it, we want to continue - if (items.Count == 0) - continue; - - // Determine if the game has a parent or not - string? parent = null; - if (!string.IsNullOrEmpty(items[0].GetFieldValue(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.CloneOfKey))) - parent = items[0].GetFieldValue(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.CloneOfKey); - - // If the parent doesnt exist, we want to continue - if (string.IsNullOrEmpty(parent)) - continue; - - // If the parent doesn't have any items, we want to continue - if (datFile.Items[parent!] == null || datFile.Items[parent!]!.Count == 0) - continue; - - // If the parent exists and has items, we remove the parent items from the current game - var parentItems = datFile.Items[parent!]; - foreach (DatItem item in parentItems!) - { - DatItem datItem = (DatItem)item.Clone(); - while (items.Contains(datItem)) - { - datFile.Items.Remove(game, datItem); - } - } - - // Now we want to get the parent romof tag and put it in each of the remaining items - items = datFile.Items[game]; - string? romof = datFile.Items[parent!]![0].GetFieldValue(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.RomOfKey); - foreach (DatItem item in items!) - { - item.GetFieldValue(DatItem.MachineKey)!.SetFieldValue(Models.Metadata.Machine.RomOfKey, romof); - } - } - } - - /// - /// Remove all romof and cloneof tags from all games - /// - /// Current DatFile object to run operations on - internal static void RemoveTagsFromChild(DatFile datFile) - { - List games = [.. datFile.Items.Keys.OrderBy(g => g)]; - foreach (string game in games) - { - var items = datFile.Items[game]; - if (items == null) - continue; - - foreach (DatItem item in items) - { - item.GetFieldValue(DatItem.MachineKey)!.SetFieldValue(Models.Metadata.Machine.CloneOfKey, null); - item.GetFieldValue(DatItem.MachineKey)!.SetFieldValue(Models.Metadata.Machine.RomOfKey, null); - item.GetFieldValue(DatItem.MachineKey)!.SetFieldValue(Models.Metadata.Machine.SampleOfKey, null); - } - } - } - #endregion } }