diff --git a/SabreTools.Library/DatFiles/DatFile.cs b/SabreTools.Library/DatFiles/DatFile.cs index 2b1d88af..cc21f4ab 100644 --- a/SabreTools.Library/DatFiles/DatFile.cs +++ b/SabreTools.Library/DatFiles/DatFile.cs @@ -326,6 +326,14 @@ namespace SabreTools.Library.DatFiles DiffAgainst(inputFileNames, outDir, inplace); } + // If we are in game diffing mode + else if (updateMode.HasFlag(UpdateMode.DiffGame)) + { + // Populate the combined data + PopulateUserData(baseFileNames, filter); + DiffGame(inputFileNames, outDir, inplace, filter); + } + // If we have one of the base replacement modes else if (updateMode.HasFlag(UpdateMode.BaseReplace) || updateMode.HasFlag(UpdateMode.ReverseBaseReplace)) @@ -406,6 +414,19 @@ namespace SabreTools.Library.DatFiles DiffNoCascade(paths, outDir, diff); } + /// + /// Output games that contain different items (by name) + /// + /// Names of the input files + /// Optional param for output directory + /// True if the output files should overwrite their inputs, false otherwise + /// Filter object to be passed to the DatItem level + public void DiffGame(List inputs, string outDir, bool inplace, Filter filter) + { + List paths = inputs.Select(i => new ParentablePath(i)).ToList(); + DiffGame(paths, outDir, inplace, filter); + } + /// /// Output user defined merge /// @@ -1226,6 +1247,70 @@ namespace SabreTools.Library.DatFiles watch.Stop(); } + /// + /// Replace item values from the base set represented by the current DAT + /// + /// Names of the input files + /// Optional param for output directory + /// True if the output files should overwrite their inputs, false otherwise + /// Filter object to be passed to the DatItem level + /// TODO: Can this be wrapped into DiffAgainst? + internal void DiffGame(List inputs, string outDir, bool inplace, Filter filter) + { + // Order the current DAT by game first + Items.BucketBy(BucketedBy.Game, DedupeType.None); + + // We want to try to replace each item in each input DAT from the base + foreach (ParentablePath path in inputs) + { + Globals.Logger.User($"Comparing items in '{path.CurrentPath}' to the base DAT"); + + // First we parse in the DAT internally + DatFile intDat = Create(Header.CloneFiltering()); + intDat.Parse(path, 1, keep: true); + filter.FilterDatFile(intDat, false /* useTags */); + intDat.Items.BucketBy(BucketedBy.Game, DedupeType.None); + + // TODO: Do we need to include items in base NOT in compare? + List games = intDat.Items.Keys.ToList(); + foreach (string game in games) + { + // If the base DAT doesn't contain the key, keep it + if (!Items.ContainsKey(game)) + continue; + + // If the number of items is different, then keep it + if (Items[game].Count != intDat.Items[game].Count) + continue; + + // Otherwise, compare by name and hash the remaining files + bool exactMatch = true; + foreach (DatItem item in intDat.Items[game]) + { + // TODO: Make this granular to name as well + if (!Items[game].Contains(item)) + { + exactMatch = false; + break; + } + } + + // If we have an exact match, remove the game + if (exactMatch) + intDat.Items.Remove(game); + } + + // Determine the output path for the DAT + string interOutDir = path.GetOutputPath(outDir, inplace); + + // Once we're done, try writing out + intDat.Write(interOutDir, overwrite: inplace); + + // Due to possible memory requirements, we force a garbage collection + GC.Collect(); + } + } + /// /// Output user defined merge /// diff --git a/SabreTools.Library/Data/Flags.cs b/SabreTools.Library/Data/Flags.cs index 3684ad5b..4942b59d 100644 --- a/SabreTools.Library/Data/Flags.cs +++ b/SabreTools.Library/Data/Flags.cs @@ -257,11 +257,12 @@ namespace SabreTools.Library.Data // Base diffs DiffAgainst = 1 << 5, + DiffGame = 1 << 6, // Special update modes - Merge = 1 << 6, - BaseReplace = 1 << 7, - ReverseBaseReplace = 1 << 8, + Merge = 1 << 7, + BaseReplace = 1 << 8, + ReverseBaseReplace = 1 << 9, // Combinations AllDiffs = DiffDupesOnly | DiffNoDupesOnly | DiffIndividualsOnly, diff --git a/SabreTools.Library/README.1ST b/SabreTools.Library/README.1ST index 118d2bd6..de42a662 100644 --- a/SabreTools.Library/README.1ST +++ b/SabreTools.Library/README.1ST @@ -1205,6 +1205,16 @@ Options: Add a DAT or folder of DATs to the base set to be used for all operations. Multiple instances of this flag are allowed. + -dga, --diff-game Diff all inputs by game against a set of base DATs + This flag will enable a special type of diffing in which a set of base + DATs are used as a comparison point for each of the input DATs by game. + This allows users to get a slightly different output to cascaded + diffing, which may be more useful in some cases. + + -bd=, --base-dat= Add a base DAT for processing + Add a DAT or folder of DATs to the base set to be used for all + operations. Multiple instances of this flag are allowed. + -br, --base-replace Replace from base DATs in order By default, no item names are changed except when there is a merge occurring. This flag enables users to define a DAT or set of base diff --git a/SabreTools/Features/BaseFeature.cs b/SabreTools/Features/BaseFeature.cs index 31fb5906..d1808f07 100644 --- a/SabreTools/Features/BaseFeature.cs +++ b/SabreTools/Features/BaseFeature.cs @@ -336,6 +336,20 @@ namespace SabreTools.Features } } + internal const string DiffGameValue = "diff-game"; + internal static Feature DiffGameFlag + { + get + { + return new Feature( + DiffAgainstValue, + new List() { "-dga", "--diff-game" }, + "Diff all inputs by game against a set of base DATs", + FeatureType.Flag, + "This flag will enable a special type of diffing in which a set of base DATs are used as a comparison point for each of the input DATs by game. This allows users to get a slightly different output to cascaded diffing, which may be more useful in some cases."); + } + } + internal const string DiffIndividualsValue = "diff-individuals"; internal static Feature DiffIndividualsFlag { @@ -2489,6 +2503,9 @@ Some special strings that can be used: if (GetBoolean(features, DiffDuplicatesValue)) updateMode |= UpdateMode.DiffDupesOnly; + if (GetBoolean(features, DiffGameValue)) + updateMode |= UpdateMode.DiffGame; + if (GetBoolean(features, DiffIndividualsValue)) updateMode |= UpdateMode.DiffIndividualsOnly; diff --git a/SabreTools/Features/Update.cs b/SabreTools/Features/Update.cs index bc286ef1..e63234f1 100644 --- a/SabreTools/Features/Update.cs +++ b/SabreTools/Features/Update.cs @@ -56,6 +56,8 @@ namespace SabreTools.Features this[DiffNoDuplicatesFlag].AddFeature(NoAutomaticDateFlag); AddFeature(DiffAgainstFlag); this[DiffAgainstFlag].AddFeature(BaseDatListInput); + AddFeature(DiffGameFlag); + this[DiffGameFlag].AddFeature(BaseDatListInput); AddFeature(BaseReplaceFlag); this[BaseReplaceFlag].AddFeature(BaseDatListInput); this[BaseReplaceFlag].AddFeature(UpdateFieldListInput);