using System; using SabreTools.Core.Tools; using SabreTools.DatFiles; using SabreTools.DatItems; using SabreTools.IO.Logging; namespace SabreTools.DatTools { public class MergeSplit { #region Fields /// /// Splitting mode to apply /// public MergingFlag SplitType { get; set; } #endregion #region Logging /// /// Logging object /// private static readonly Logger _staticLogger = new(); #endregion // TODO: Should any of these create a new DatFile in the process? // The reason this comes up is that doing any of the splits or merges // is an inherently destructive process. Making it output a new DatFile // might make it easier to deal with multiple internal steps. On the other // hand, this will increase memory usage significantly and would force the // existing paths to behave entirely differently #region Running /// /// Apply splitting on the DatFile /// /// Current DatFile object to run operations on /// True if DatFile tags override splitting, false otherwise /// True if the error that is thrown should be thrown back to the caller, false otherwise /// True if the DatFile was split, false on error public bool ApplySplitting(DatFile datFile, bool useTags, bool throwOnError = false) { InternalStopwatch watch = new("Applying splitting to DAT"); try { // If we are using tags from the DAT, set the proper input for split type unless overridden if (useTags && SplitType == MergingFlag.None) SplitType = datFile.Header.GetStringFieldValue(Models.Metadata.Header.ForceMergingKey).AsEnumValue(); // Run internal splitting switch (SplitType) { // Standard case MergingFlag.None: // No-op break; case MergingFlag.Split: CreateSplitSets(datFile); break; case MergingFlag.Merged: CreateMergedSets(datFile); break; case MergingFlag.NonMerged: CreateNonMergedSets(datFile); break; // Nonstandard case MergingFlag.FullMerged: CreateFullyMergedSets(datFile); break; case MergingFlag.DeviceNonMerged: CreateDeviceNonMergedSets(datFile); break; case MergingFlag.FullNonMerged: CreateFullyNonMergedSets(datFile); break; } } catch (Exception ex) when (!throwOnError) { _staticLogger.Error(ex); return false; } finally { watch.Stop(); } return true; } /// /// Use cdevice_ref tags to get full non-merged sets and remove parenting tags /// /// Current DatFile object to run operations on internal static void CreateDeviceNonMergedSets(DatFile datFile) { _staticLogger.User("Creating device non-merged sets from the DAT"); // For sake of ease, the first thing we want to do is bucket by game datFile.BucketBy(ItemKey.Machine, DedupeType.None, norename: true); // Now we want to loop through all of the games and set the correct information while (datFile.AddItemsFromDevices(false, false)) ; while (datFile.AddItemsFromDevices(true, false)) ; // Then, remove the romof and cloneof tags so it's not picked up by the manager datFile.RemoveMachineRelationshipTags(); } /// /// Use cloneof tags to create merged sets and remove the tags plus deduplicating if tags don't catch everything /// /// Current DatFile object to run operations on internal static void CreateFullyMergedSets(DatFile datFile) { _staticLogger.User("Creating fully merged sets from the DAT"); // For sake of ease, the first thing we want to do is bucket by game datFile.BucketBy(ItemKey.Machine, DedupeType.None, norename: true); // Now we want to loop through all of the games and set the correct information datFile.AddItemsFromChildren(true, false); // Now that we have looped through the cloneof tags, we loop through the romof tags datFile.RemoveBiosItemsFromChild(false); datFile.RemoveBiosItemsFromChild(true); // Finally, remove the romof and cloneof tags so it's not picked up by the manager datFile.RemoveMachineRelationshipTags(); } /// /// Use cloneof tags to create non-merged sets and remove the tags plus using the device_ref tags to get full sets /// /// Current DatFile object to run operations on internal static void CreateFullyNonMergedSets(DatFile datFile) { _staticLogger.User("Creating fully non-merged sets from the DAT"); // For sake of ease, the first thing we want to do is bucket by game datFile.BucketBy(ItemKey.Machine, DedupeType.None, norename: true); // Now we want to loop through all of the games and set the correct information while (datFile.AddItemsFromDevices(true, true)) ; datFile.AddItemsFromDevices(false, true); datFile.AddItemsFromParent(); // Now that we have looped through the cloneof tags, we loop through the romof tags datFile.AddItemsFromBios(); // Then, remove the romof and cloneof tags so it's not picked up by the manager datFile.RemoveMachineRelationshipTags(); } /// /// Use cloneof tags to create merged sets and remove the tags /// /// Current DatFile object to run operations on internal static void CreateMergedSets(DatFile datFile) { _staticLogger.User("Creating merged sets from the DAT"); // For sake of ease, the first thing we want to do is bucket by game datFile.BucketBy(ItemKey.Machine, DedupeType.None, norename: true); // Now we want to loop through all of the games and set the correct information datFile.AddItemsFromChildren(true, true); // Now that we have looped through the cloneof tags, we loop through the romof tags datFile.RemoveBiosItemsFromChild(false); datFile.RemoveBiosItemsFromChild(true); // Finally, remove the romof and cloneof tags so it's not picked up by the manager datFile.RemoveMachineRelationshipTags(); } /// /// Use cloneof tags to create non-merged sets and remove the tags /// /// Current DatFile object to run operations on internal static void CreateNonMergedSets(DatFile datFile) { _staticLogger.User("Creating non-merged sets from the DAT"); // For sake of ease, the first thing we want to do is bucket by game datFile.BucketBy(ItemKey.Machine, DedupeType.None, norename: true); // Now we want to loop through all of the games and set the correct information datFile.AddItemsFromParent(); // Now that we have looped through the cloneof tags, we loop through the romof tags datFile.RemoveBiosItemsFromChild(false); datFile.RemoveBiosItemsFromChild(true); // Finally, remove the romof and cloneof tags so it's not picked up by the manager datFile.RemoveMachineRelationshipTags(); } /// /// Use cloneof and romof tags to create split sets and remove the tags /// /// Current DatFile object to run operations on internal static void CreateSplitSets(DatFile datFile) { _staticLogger.User("Creating split sets from the DAT"); // For sake of ease, the first thing we want to do is bucket by game datFile.BucketBy(ItemKey.Machine, DedupeType.None, norename: true); // Now we want to loop through all of the games and set the correct information datFile.RemoveItemsFromChild(); // Now that we have looped through the cloneof tags, we loop through the romof tags datFile.RemoveBiosItemsFromChild(false); datFile.RemoveBiosItemsFromChild(true); // Finally, remove the romof and cloneof tags so it's not picked up by the manager datFile.RemoveMachineRelationshipTags(); } #endregion } }