diff --git a/SabreTools.Library/DatFiles/DatFile.cs b/SabreTools.Library/DatFiles/DatFile.cs index 7cd514bf..095bdb90 100644 --- a/SabreTools.Library/DatFiles/DatFile.cs +++ b/SabreTools.Library/DatFiles/DatFile.cs @@ -318,20 +318,13 @@ namespace SabreTools.Library.DatFiles DiffCascade(inputFileNames, datHeaders, outDir, inplace, skip); } - // If we have diff against mode - else if (updateMode.HasFlag(UpdateMode.DiffAgainst)) + // If we have a diff against mode + else if (updateMode.HasFlag(UpdateMode.DiffAgainst) + || updateMode.HasFlag(UpdateMode.DiffGame)) { // Populate the combined data PopulateUserData(baseFileNames, filter); - DiffAgainst(inputFileNames, outDir, inplace, filter); - } - - // 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); + DiffAgainst(inputFileNames, outDir, inplace, filter, updateMode.HasFlag(UpdateMode.DiffGame)); } // If we have one of the base replacement modes @@ -378,10 +371,11 @@ namespace SabreTools.Library.DatFiles /// 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 DiffAgainst(List inputs, string outDir, bool inplace, Filter filter) + /// True to diff using games, false to use hashes + public void DiffAgainst(List inputs, string outDir, bool inplace, Filter filter, bool useGames) { List paths = inputs.Select(i => new ParentablePath(i)).ToList(); - DiffAgainst(paths, outDir, inplace, filter); + DiffAgainst(paths, outDir, inplace, filter, useGames); } /// @@ -415,19 +409,6 @@ 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 /// @@ -968,10 +949,14 @@ namespace SabreTools.Library.DatFiles /// 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 - internal void DiffAgainst(List inputs, string outDir, bool inplace, Filter filter) + /// True to diff using games, false to use hashes + internal void DiffAgainst(List inputs, string outDir, bool inplace, Filter filter, bool useGames) { - // For comparison's sake, we want to use CRC as the base ordering - Items.BucketBy(BucketedBy.CRC, DedupeType.Full); + // For comparison's sake, we want to use a base ordering + if (useGames) + Items.BucketBy(BucketedBy.Game, DedupeType.None); + else + Items.BucketBy(BucketedBy.CRC, DedupeType.Full); // Now we want to compare each input DAT against the base foreach (ParentablePath path in inputs) @@ -983,23 +968,59 @@ namespace SabreTools.Library.DatFiles intDat.Parse(path, 1, keep: true); filter.FilterDatFile(intDat, false /* useTags */); - // For comparison's sake, we want to use CRC as the base bucketing - intDat.Items.BucketBy(BucketedBy.CRC, DedupeType.Full); + // For comparison's sake, we want to a the base bucketing + if (useGames) + intDat.Items.BucketBy(BucketedBy.Game, DedupeType.None); + else + intDat.Items.BucketBy(BucketedBy.CRC, DedupeType.Full); - // Then we do a hashwise comparison against the base DAT - Parallel.ForEach(intDat.Items.Keys, Globals.ParallelOptions, key => + // Then we compare against the base DAT + List keys = intDat.Items.Keys.ToList(); + Parallel.ForEach(keys, Globals.ParallelOptions, key => { - List datItems = intDat.Items[key]; - List keepDatItems = new List(); - foreach (DatItem datItem in datItems) + // Game Against uses game names + if (useGames) { - if (!Items.HasDuplicates(datItem, true)) - keepDatItems.Add(datItem); + // If the base DAT doesn't contain the key, keep it + if (!Items.ContainsKey(key)) + return; + + // If the number of items is different, then keep it + if (Items[key].Count != intDat.Items[key].Count) + return; + + // Otherwise, compare by name and hash the remaining files + bool exactMatch = true; + foreach (DatItem item in intDat.Items[key]) + { + // TODO: Make this granular to name as well + if (!Items[key].Contains(item)) + { + exactMatch = false; + break; + } + } + + // If we have an exact match, remove the game + if (exactMatch) + intDat.Items.Remove(key); } - // Now add the new list to the key - intDat.Items.Remove(key); - intDat.Items.AddRange(key, keepDatItems); + // Standard Against uses hashes + else + { + List datItems = intDat.Items[key]; + List keepDatItems = new List(); + foreach (DatItem datItem in datItems) + { + if (!Items.HasDuplicates(datItem, true)) + keepDatItems.Add(datItem); + } + + // Now add the new list to the key + intDat.Items.Remove(key); + intDat.Items.AddRange(key, keepDatItems); + } }); // Determine the output path for the DAT @@ -1250,72 +1271,6 @@ 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 */); - - // For comparison's sake, we want to use Game as the base bucketing - 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 ///