diff --git a/SabreTools.Helper/Objects/DATFromDirParallel.cs b/SabreTools.Helper/Objects/DATFromDirParallel.cs
index 7e93ea78..51bcd7f2 100644
--- a/SabreTools.Helper/Objects/DATFromDirParallel.cs
+++ b/SabreTools.Helper/Objects/DATFromDirParallel.cs
@@ -4,7 +4,6 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
-using System.Threading;
using System.Threading.Tasks;
namespace SabreTools
diff --git a/SabreTools.Helper/Tools/DatTools.cs b/SabreTools.Helper/Tools/DatTools.cs
index 4e4ee267..04928be8 100644
--- a/SabreTools.Helper/Tools/DatTools.cs
+++ b/SabreTools.Helper/Tools/DatTools.cs
@@ -4,6 +4,7 @@ using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
+using System.Threading.Tasks;
using System.Web;
using System.Xml;
@@ -2393,6 +2394,178 @@ namespace SabreTools.Helper
#endregion
+ #region Converting and Updating (Parallel)
+
+ ///
+ /// Convert, update, and filter a DAT file (Parallel)
+ ///
+ /// Names of the input files and/or folders
+ /// User specified inputs contained in a DatData object
+ /// Optional param for output directory
+ /// True if input files should be merged into a single file, false otherwise
+ /// Non-zero flag for diffing mode, zero otherwise
+ /// True if the diffed files should be cascade diffed, false if diffed files should be reverse cascaded, null otherwise
+ /// True if the cascade-diffed files should overwrite their inputs, false otherwise
+ /// True if the first cascaded diff file should be skipped on output, false otherwise
+ /// True if the date should not be appended to the default name, false otherwise [OBSOLETE]
+ /// True to clean the game names to WoD standard, false otherwise (default)
+ /// True to allow SL DATs to have game names used instead of descriptions, false otherwise (default)
+ /// Name of the game to match (can use asterisk-partials)
+ /// Name of the rom to match (can use asterisk-partials)
+ /// Type of the rom to match
+ /// Find roms greater than or equal to this size
+ /// Find roms less than or equal to this size
+ /// Find roms equal to this size
+ /// CRC of the rom to match (can use asterisk-partials)
+ /// MD5 of the rom to match (can use asterisk-partials)
+ /// SHA-1 of the rom to match (can use asterisk-partials)
+ /// Select roms with nodump status as follows: null (match all), true (match Nodump only), false (exclude Nodump)
+ /// True if we are supposed to trim names to NTFS length, false otherwise
+ /// True if all games should be replaced by '!', false otherwise
+ /// String representing root directory to compare against for length calculation
+ /// Logging object for console and file output
+ public static void UpdateParallel(List inputFileNames, Dat datdata, string outputDirectory, bool merge, DiffMode diff, bool? cascade, bool inplace,
+ bool skip, bool bare, bool clean, bool softlist, string gamename, string romname, string romtype, long sgt, long slt, long seq, string crc,
+ string md5, string sha1, bool? nodump, bool trim, bool single, string root, Logger logger)
+ {
+ // If we're in merging or diffing mode, use the full list of inputs
+ if (merge || diff != 0)
+ {
+ // Make sure there are no folders in inputs
+ List newInputFileNames = new List();
+ foreach (string input in inputFileNames)
+ {
+ if (Directory.Exists(input))
+ {
+ foreach (string file in Directory.EnumerateFiles(input, "*", SearchOption.AllDirectories))
+ {
+ try
+ {
+ newInputFileNames.Add(Path.GetFullPath(file) + "¬" + Path.GetFullPath(input));
+ }
+ catch (PathTooLongException)
+ {
+ logger.Warning("The path for " + file + " was too long");
+ }
+ catch (Exception ex)
+ {
+ logger.Error(ex.ToString());
+ }
+ }
+ }
+ else if (File.Exists(input))
+ {
+ try
+ {
+ newInputFileNames.Add(Path.GetFullPath(input) + "¬" + Path.GetDirectoryName(Path.GetFullPath(input)));
+ }
+ catch (PathTooLongException)
+ {
+ logger.Warning("The path for " + input + " was too long");
+ }
+ catch (Exception ex)
+ {
+ logger.Error(ex.ToString());
+ }
+ }
+ }
+
+ // If we're in inverse cascade, reverse the list
+ if (cascade == false)
+ {
+ newInputFileNames.Reverse();
+ }
+
+ // Create a dictionary of all ROMs from the input DATs
+ Dat userData;
+ List datHeaders = PopulateUserData(newInputFileNames, inplace, clean, softlist,
+ outputDirectory, datdata, out userData, gamename, romname, romtype, sgt, slt, seq,
+ crc, md5, sha1, nodump, trim, single, root, logger);
+
+ // Modify the Dictionary if necessary and output the results
+ if (diff != 0 && cascade == null)
+ {
+ DiffNoCascade(diff, outputDirectory, userData, newInputFileNames, logger);
+ }
+ // If we're in cascade and diff, output only cascaded diffs
+ else if (diff != 0 && cascade != null)
+ {
+ DiffCascade(outputDirectory, inplace, userData, newInputFileNames, datHeaders, skip, logger);
+ }
+ // Output all entries with user-defined merge
+ else
+ {
+ MergeNoDiff(outputDirectory, userData, newInputFileNames, datHeaders, logger);
+ }
+ }
+ // Otherwise, loop through all of the inputs individually
+ else
+ {
+ Parallel.ForEach(inputFileNames, inputFileName =>
+ {
+ // Clean the input string
+ if (inputFileName != "")
+ {
+ inputFileName = Path.GetFullPath(inputFileName);
+ }
+
+ if (File.Exists(inputFileName))
+ {
+ logger.User("Processing \"" + Path.GetFileName(inputFileName) + "\"");
+ datdata = Parse(inputFileName, 0, 0, datdata, gamename, romname,
+ romtype, sgt, slt, seq, crc, md5, sha1, nodump, trim, single,
+ root, logger, true, clean, softlist, keepext: (datdata.XSV != null));
+
+ // If the extension matches, append ".new" to the filename
+ string extension = (datdata.OutputFormat == OutputFormat.Xml || datdata.OutputFormat == OutputFormat.SabreDat ? ".xml" : ".dat");
+ if (outputDirectory == "" && Path.GetExtension(inputFileName) == extension)
+ {
+ datdata.FileName += ".new";
+ }
+
+ // If we have roms, output them
+ if (datdata.Files.Count != 0)
+ {
+ DatTools.WriteDatfile(datdata, (outputDirectory == "" ? Path.GetDirectoryName(inputFileName) : outputDirectory), logger);
+ }
+ }
+ else if (Directory.Exists(inputFileName))
+ {
+ inputFileName = Path.GetFullPath(inputFileName) + Path.DirectorySeparatorChar;
+
+ Parallel.ForEach(Directory.EnumerateFiles(inputFileName, "*", SearchOption.AllDirectories), file =>
+ {
+ logger.User("Processing \"" + Path.GetFullPath(file).Remove(0, inputFileName.Length) + "\"");
+ Dat innerDatdata = (Dat)datdata.Clone();
+ innerDatdata.Files = null;
+ innerDatdata = Parse(file, 0, 0, innerDatdata, gamename, romname, romtype, sgt,
+ slt, seq, crc, md5, sha1, nodump, trim, single, root, logger, true, clean, keepext: (datdata.XSV != null));
+
+ // If the extension matches, append ".new" to the filename
+ string extension = (innerDatdata.OutputFormat == OutputFormat.Xml || innerDatdata.OutputFormat == OutputFormat.SabreDat ? ".xml" : ".dat");
+ if (outputDirectory == "" && Path.GetExtension(file) == extension)
+ {
+ innerDatdata.FileName += ".new";
+ }
+
+ // If we have roms, output them
+ if (innerDatdata.Files != null && innerDatdata.Files.Count != 0)
+ {
+ DatTools.WriteDatfile(innerDatdata, (outputDirectory == "" ? Path.GetDirectoryName(file) : outputDirectory + Path.GetDirectoryName(file).Remove(0, inputFileName.Length - 1)), logger);
+ }
+ });
+ }
+ else
+ {
+ logger.Error("I'm sorry but " + inputFileName + " doesn't exist!");
+ }
+ });
+ }
+ return;
+ }
+
+ #endregion
+
#region DAT Writing
///