diff --git a/SabreTools/Features/Batch.cs b/SabreTools/Features/Batch.cs index 265fd08c..eb1d1574 100644 --- a/SabreTools/Features/Batch.cs +++ b/SabreTools/Features/Batch.cs @@ -56,414 +56,551 @@ Reset the internal state: reset();"; // Try to read each input as a batch run file foreach (string path in Inputs) { - // If the file doesn't exist, warn but continue - if (!File.Exists(path)) - { - logger.User($"{path} does not exist. Skipping..."); - continue; - } - - // Try to process the file now - try - { - // Every line is its own command - string[] lines = File.ReadAllLines(path); - - // Each batch file has its own state - int index = 0; - DatFile datFile = DatFile.Create(); - string outputDirectory = null; - - // Process each command line - foreach (string line in lines) - { - // Skip empty lines - if (string.IsNullOrWhiteSpace(line)) - continue; - - // Skip lines that start with REM or # - if (line.StartsWith("REM") || line.StartsWith("#")) - continue; - - // Read the command in, if possible - var command = BatchCommand.Create(line); - if (command == null) - { - logger.User($"Could not process {path} due to the following line: {line}"); - break; - } - - // Now switch on the command - logger.User($"Attempting to invoke {command.Name} with {(command.Arguments.Count == 0 ? "no arguments" : "the following argument(s): " + string.Join(", ", command.Arguments))}"); - switch (command.Name.ToLowerInvariant()) - { - // Set a header field - case "set": - if (command.Arguments.Count != 2) - { - logger.User($"Invoked {command.Name} but no arguments were provided"); - logger.User("Usage: set(header.field, value);"); - continue; - } - - // Read in the individual arguments - DatHeaderField field = command.Arguments[0].AsDatHeaderField(); - string value = command.Arguments[1]; - - // If we had an invalid input, log and continue - if (field == DatHeaderField.NULL) - { - logger.User($"{command.Arguments[0]} was an invalid field name"); - continue; - } - - // Set the header field - datFile.Header.SetFields(new Dictionary { [field] = value }); - - break; - - // Parse in new input file(s) - case "input": - if (command.Arguments.Count == 0) - { - logger.User($"Invoked {command.Name} but no arguments were provided"); - logger.User("Usage: input(datpath, ...);"); - continue; - } - - // Get only files from inputs - List datFilePaths = PathTool.GetFilesOnly(command.Arguments); - - // Assume there could be multiple - foreach (ParentablePath datFilePath in datFilePaths) - { - Parser.ParseInto(datFile, datFilePath, index++); - } - - break; - - // Run DFD/D2D on path(s) - case "d2d": - case "dfd": - if (command.Arguments.Count == 0) - { - logger.User($"Invoked {command.Name} but no arguments were provided"); - logger.User("Usage: d2d(path, ...);"); - continue; - } - - // TODO: Should any of the other options be added for D2D? - - // Assume there could be multiple - foreach (string input in command.Arguments) - { - DatTools.DatFromDir.PopulateFromDir(datFile, input, hashes: Hash.Standard); - } - - // TODO: We might not want to remove dates in the future - Remover dfdRemover = new Remover(); - dfdRemover.PopulateExclusionsFromList(new List { "DatItem.Date" }); - dfdRemover.ApplyRemovals(datFile); - - break; - - // Apply a filter - case "filter": - if (command.Arguments.Count < 2 || command.Arguments.Count > 4) - { - logger.User($"Invoked {command.Name} and expected between 2-4 arguments, but {command.Arguments.Count} arguments were provided"); - logger.User("Usage: filter(field, value, [remove = false, [perMachine = false]]);"); - continue; - } - - // Read in the individual arguments - DatHeaderField filterDatHeaderField = command.Arguments[0].AsDatHeaderField(); - MachineField filterMachineField = command.Arguments[0].AsMachineField(); - DatItemField filterDatItemField = command.Arguments[0].AsDatItemField(); - string filterValue = command.Arguments[1]; - bool? filterRemove = false; - if (command.Arguments.Count >= 3) - filterRemove = command.Arguments[2].AsYesNo(); - bool? filterPerMachine = false; - if (command.Arguments.Count >= 4) - filterPerMachine = command.Arguments[3].AsYesNo(); - - // If we had an invalid input, log and continue - if (filterDatHeaderField == DatHeaderField.NULL - && filterMachineField == MachineField.NULL - && filterDatItemField == DatItemField.NULL) - { - logger.User($"{command.Arguments[0]} was an invalid field name"); - continue; - } - if (filterRemove == null) - { - logger.User($"{command.Arguments[2]} was an invalid true/false value"); - continue; - } - if (filterPerMachine == null) - { - logger.User($"{command.Arguments[3]} was an invalid true/false value"); - continue; - } - - // Create cleaner to run filters from - Filter filter = new Filter - { - MachineFilter = new MachineFilter(), - DatItemFilter = new DatItemFilter(), - }; - - // Set the possible filters - filter.MachineFilter.SetFilter(filterMachineField, filterValue, filterRemove.Value); - filter.DatItemFilter.SetFilter(filterDatItemField, filterValue, filterRemove.Value); - - // Apply the filters blindly - filter.ApplyFilters(datFile, filterPerMachine.Value); - - // Cleanup after the filter - // TODO: We might not want to remove immediately - datFile.Items.ClearMarked(); - datFile.Items.ClearEmpty(); - - break; - - // Apply an extra INI - case "extra": - if (command.Arguments.Count != 2) - { - logger.User($"Invoked {command.Name} and expected 2 arguments, but {command.Arguments.Count} arguments were provided"); - logger.User("Usage: extra(field, inipath);"); - continue; - } - - // Read in the individual arguments - MachineField extraMachineField = command.Arguments[0].AsMachineField(); - DatItemField extraDatItemField = command.Arguments[0].AsDatItemField(); - string extraFile = command.Arguments[1]; - - // If we had an invalid input, log and continue - if (extraMachineField == MachineField.NULL - && extraDatItemField == DatItemField.NULL) - { - logger.User($"{command.Arguments[0]} was an invalid field name"); - continue; - } - if (!File.Exists(command.Arguments[1])) - { - logger.User($"{command.Arguments[1]} was an invalid file name"); - continue; - } - - // Create the extra INI - ExtraIni extraIni = new ExtraIni(); - ExtraIniItem extraIniItem = new ExtraIniItem(); - extraIniItem.PopulateFromFile(extraFile); - extraIniItem.MachineField = extraMachineField; - extraIniItem.DatItemField = extraDatItemField; - extraIni.Items.Add(extraIniItem); - - // Apply the extra INI blindly - extraIni.ApplyExtras(datFile); - - break; - - // Apply internal split/merge - case "merge": - if (command.Arguments.Count != 1) - { - logger.User($"Invoked {command.Name} and expected 1 argument, but {command.Arguments.Count} arguments were provided"); - logger.User("Usage: merge(split|merged|nonmerged|full|device);"); - continue; - } - - // Read in the individual arguments - MergingFlag mergingFlag = command.Arguments[0].AsMergingFlag(); - - // If we had an invalid input, log and continue - if (mergingFlag == MergingFlag.None) - { - logger.User($"{command.Arguments[0]} was an invalid merging flag"); - continue; - } - - // Apply the merging flag - Filtering.Splitter splitter = new Filtering.Splitter { SplitType = mergingFlag }; - splitter.ApplySplitting(datFile, false); - - break; - - // Apply description-as-name logic - case "descname": - if (command.Arguments.Count != 0) - { - logger.User($"Invoked {command.Name} and expected no arguments, but {command.Arguments.Count} arguments were provided"); - logger.User("Usage: descname();"); - continue; - } - - // Apply the logic - Cleaner descNameCleaner = new Cleaner { DescriptionAsName = true }; - descNameCleaner.ApplyCleaning(datFile); - - break; - - // Apply 1G1R - case "1g1r": - if (command.Arguments.Count == 0) - { - logger.User($"Invoked {command.Name} but no arguments were provided"); - logger.User("Usage: 1g1r(region, ...);"); - continue; - } - - // Run the 1G1R functionality - Cleaner ogorCleaner = new Cleaner { OneGamePerRegion = true, RegionList = command.Arguments }; - ogorCleaner.ApplyCleaning(datFile); - - break; - - // Apply one rom per game (ORPG) - case "orpg": - if (command.Arguments.Count != 0) - { - logger.User($"Invoked {command.Name} and expected no arguments, but {command.Arguments.Count} arguments were provided"); - logger.User("Usage: orpg();"); - continue; - } - - // Apply the logic - Cleaner orpgCleaner = new Cleaner { OneRomPerGame = true }; - orpgCleaner.ApplyCleaning(datFile); - - break; - - // Remove a field - case "remove": - if (command.Arguments.Count == 0) - { - logger.User($"Invoked {command.Name} but no arguments were provided"); - logger.User("Usage: remove(field, ...);"); - continue; - } - - // Run the removal functionality - Remover remover = new Remover(); - remover.PopulateExclusionsFromList(command.Arguments); - remover.ApplyRemovals(datFile); - - break; - - // Apply scene date stripping - case "sds": - if (command.Arguments.Count != 0) - { - logger.User($"Invoked {command.Name} and expected no arguments, but {command.Arguments.Count} arguments were provided"); - logger.User("Usage: sds();"); - continue; - } - - // Apply the logic - Cleaner stripCleaner = new Cleaner { SceneDateStrip = true }; - stripCleaner.ApplyCleaning(datFile); - - break; - - // Set new output format(s) - case "format": - if (command.Arguments.Count == 0) - { - logger.User($"Invoked {command.Name} but no arguments were provided"); - logger.User("Usage: format(datformat, ...);"); - continue; - } - - // Assume there could be multiple - datFile.Header.DatFormat = 0x00; - foreach (string format in command.Arguments) - { - datFile.Header.DatFormat |= GetDatFormat(format); - } - - // If we had an invalid input, log and continue - if (datFile.Header.DatFormat == 0x00) - { - logger.User($"No valid output format found"); - continue; - } - - break; - - // Set output directory - case "output": - if (command.Arguments.Count != 1) - { - logger.User($"Invoked {command.Name} and expected exactly 1 argument, but {command.Arguments.Count} arguments were provided"); - logger.User("Usage: output(outdir);"); - continue; - } - - // Only set the first as the output directory - outputDirectory = command.Arguments[0]; - break; - - // Write out the current DatFile - case "write": - if (command.Arguments.Count > 1) - { - logger.User($"Invoked {command.Name} and expected 0-1 arguments, but {command.Arguments.Count} arguments were provided"); - logger.User("Usage: write([overwrite = true]);"); - continue; - } - - // Get overwrite value, if possible - bool? overwrite = true; - if (command.Arguments.Count == 1) - overwrite = command.Arguments[0].AsYesNo(); - - // If we had an invalid input, log and continue - if (overwrite == null) - { - logger.User($"{command.Arguments[0]} was an invalid true/false value"); - continue; - } - - // Write out the dat with the current state - Writer.Write(datFile, outputDirectory, overwrite: overwrite.Value); - break; - - // Reset the internal state - case "reset": - if (command.Arguments.Count != 0) - { - logger.User($"Invoked {command.Name} and expected no arguments, but {command.Arguments.Count} arguments were provided"); - logger.User("Usage: reset();"); - continue; - } - - // Reset all state variables - index = 0; - datFile = DatFile.Create(); - outputDirectory = null; - break; - - default: - logger.User($"Could not find a match for '{command.Name}'. Please see the help text for more details."); - break; - } - } - } - catch (Exception ex) - { - logger.Error(ex, $"There was an exception processing {path}"); - continue; - } + ProcessScript(path); } } + /// + /// Process a single input file as a batch run file + /// + /// Path to the input file + private void ProcessScript(string path) + { + // If the file doesn't exist, warn but continue + if (!File.Exists(path)) + { + logger.User($"{path} does not exist. Skipping..."); + return; + } + + // Try to process the file now + try + { + // Every line is its own command + string[] lines = File.ReadAllLines(path); + + // Each batch file has its own state + int index = 0; + DatFile datFile = DatFile.Create(); + string outputDirectory = null; + + // Process each command line + foreach (string line in lines) + { + // Skip empty lines + if (string.IsNullOrWhiteSpace(line)) + continue; + + // Skip lines that start with REM or # + if (line.StartsWith("REM") || line.StartsWith("#")) + continue; + + // Read the command in, if possible + var command = BatchCommand.Create(line); + if (command == null) + { + logger.User($"Could not process {path} due to the following line: {line}"); + break; + } + + // Now switch on the command + logger.User($"Attempting to invoke {command.Name} with {(command.Arguments.Count == 0 ? "no arguments" : "the following argument(s): " + string.Join(", ", command.Arguments))}"); + switch (command.Name.ToLowerInvariant()) + { + // Set a header field + case "set": + SetHeaderField(command, datFile); + break; + + // Parse in new input file(s) + case "input": + ParseInputs(command, datFile, ref index); + break; + + // Run DFD/D2D on path(s) + case "d2d": + case "dfd": + PopulateFromDir(command, datFile); + break; + + // Apply a filter + case "filter": + ApplyFilter(command, datFile); + break; + + // Apply an extra INI + case "extra": + ApplyExtra(command, datFile); + break; + + // Apply internal split/merge + case "merge": + RunMerge(command, datFile); + break; + + // Apply description-as-name logic + case "descname": + DescriptionAsName(command, datFile); + break; + + // Apply 1G1R + case "1g1r": + OneGamePerRegion(command, datFile); + break; + + // Apply one rom per game (ORPG) + case "orpg": + OneRomPerGame(command, datFile); + break; + + // Remove a field + case "remove": + RemoveField(command, datFile); + break; + + // Apply scene date stripping + case "sds": + SceneDateStrip(command, datFile); + break; + + // Set new output format(s) + case "format": + SetOutputFormat(command, datFile); + break; + + // Set output directory + case "output": + if (command.Arguments.Count != 1) + { + logger.User($"Invoked {command.Name} and expected exactly 1 argument, but {command.Arguments.Count} arguments were provided"); + logger.User("Usage: output(outdir);"); + continue; + } + + // Only set the first as the output directory + outputDirectory = command.Arguments[0]; + break; + + // Write out the current DatFile + case "write": + Write(command, datFile, outputDirectory); + break; + + // Reset the internal state + case "reset": + if (command.Arguments.Count != 0) + { + logger.User($"Invoked {command.Name} and expected no arguments, but {command.Arguments.Count} arguments were provided"); + logger.User("Usage: reset();"); + continue; + } + + // Reset all state variables + index = 0; + datFile = DatFile.Create(); + outputDirectory = null; + break; + + default: + logger.User($"Could not find a match for '{command.Name}'. Please see the help text for more details."); + break; + } + } + } + catch (Exception ex) + { + logger.Error(ex, $"There was an exception processing {path}"); + } + } + + #region Batch Commands + + /// + /// Apply a single extras file to the internal DAT + /// + /// BatchCommand representing the line + /// DatFile representing the internal DAT + private void ApplyExtra(BatchCommand command, DatFile datFile) + { + if (command.Arguments.Count != 2) + { + logger.User($"Invoked {command.Name} and expected 2 arguments, but {command.Arguments.Count} arguments were provided"); + logger.User("Usage: extra(field, inipath);"); + return; + } + + // Read in the individual arguments + MachineField extraMachineField = command.Arguments[0].AsMachineField(); + DatItemField extraDatItemField = command.Arguments[0].AsDatItemField(); + string extraFile = command.Arguments[1]; + + // If we had an invalid input, log and continue + if (extraMachineField == MachineField.NULL + && extraDatItemField == DatItemField.NULL) + { + logger.User($"{command.Arguments[0]} was an invalid field name"); + return; + } + if (!File.Exists(command.Arguments[1])) + { + logger.User($"{command.Arguments[1]} was an invalid file name"); + return; + } + + // Create the extra INI + ExtraIni extraIni = new ExtraIni(); + ExtraIniItem extraIniItem = new ExtraIniItem + { + MachineField = extraMachineField, + DatItemField = extraDatItemField, + }; + extraIniItem.PopulateFromFile(extraFile); + extraIni.Items.Add(extraIniItem); + + // Apply the extra INI blindly + extraIni.ApplyExtras(datFile); + } + + /// + /// Apply a single filter to the internal DAT + /// + /// BatchCommand representing the line + /// DatFile representing the internal DAT + private void ApplyFilter(BatchCommand command, DatFile datFile) + { + if (command.Arguments.Count < 2 || command.Arguments.Count > 4) + { + logger.User($"Invoked {command.Name} and expected between 2-4 arguments, but {command.Arguments.Count} arguments were provided"); + logger.User("Usage: filter(field, value, [remove = false, [perMachine = false]]);"); + return; + } + + // Read in the individual arguments + DatHeaderField filterDatHeaderField = command.Arguments[0].AsDatHeaderField(); + MachineField filterMachineField = command.Arguments[0].AsMachineField(); + DatItemField filterDatItemField = command.Arguments[0].AsDatItemField(); + string filterValue = command.Arguments[1]; + bool? filterRemove = false; + if (command.Arguments.Count >= 3) + filterRemove = command.Arguments[2].AsYesNo(); + bool? filterPerMachine = false; + if (command.Arguments.Count >= 4) + filterPerMachine = command.Arguments[3].AsYesNo(); + + // If we had an invalid input, log and continue + if (filterDatHeaderField == DatHeaderField.NULL + && filterMachineField == MachineField.NULL + && filterDatItemField == DatItemField.NULL) + { + logger.User($"{command.Arguments[0]} was an invalid field name"); + return; + } + if (filterRemove == null) + { + logger.User($"{command.Arguments[2]} was an invalid true/false value"); + return; + } + if (filterPerMachine == null) + { + logger.User($"{command.Arguments[3]} was an invalid true/false value"); + return; + } + + // Create filter to run filters from + Filter filter = new Filter + { + MachineFilter = new MachineFilter { HasFilters = true }, + DatItemFilter = new DatItemFilter { HasFilters = true }, + }; + + // Set the possible filters + filter.MachineFilter.SetFilter(filterMachineField, filterValue, filterRemove.Value); + filter.DatItemFilter.SetFilter(filterDatItemField, filterValue, filterRemove.Value); + + // Apply the filters blindly + filter.ApplyFilters(datFile, filterPerMachine.Value); + + // Cleanup after the filter + // TODO: We might not want to remove immediately + datFile.Items.ClearMarked(); + datFile.Items.ClearEmpty(); + } + + /// + /// Apply a description-as-name to the internal DAT + /// + /// BatchCommand representing the line + /// DatFile representing the internal DAT + private void DescriptionAsName(BatchCommand command, DatFile datFile) + { + if (command.Arguments.Count != 0) + { + logger.User($"Invoked {command.Name} and expected no arguments, but {command.Arguments.Count} arguments were provided"); + logger.User("Usage: descname();"); + return; + } + + // Apply the logic + Cleaner descNameCleaner = new Cleaner { DescriptionAsName = true }; + descNameCleaner.ApplyCleaning(datFile); + } + + /// + /// Apply 1G1R to the internal DAT + /// + /// BatchCommand representing the line + /// DatFile representing the internal DAT + private void OneGamePerRegion(BatchCommand command, DatFile datFile) + { + if (command.Arguments.Count == 0) + { + logger.User($"Invoked {command.Name} but no arguments were provided"); + logger.User("Usage: 1g1r(region, ...);"); + return; + } + + // Run the 1G1R functionality + Cleaner ogorCleaner = new Cleaner { OneGamePerRegion = true, RegionList = command.Arguments }; + ogorCleaner.ApplyCleaning(datFile); + } + + /// + /// Apply ORPG to the internal DAT + /// + /// BatchCommand representing the line + /// DatFile representing the internal DAT + private void OneRomPerGame(BatchCommand command, DatFile datFile) + { + if (command.Arguments.Count != 0) + { + logger.User($"Invoked {command.Name} and expected no arguments, but {command.Arguments.Count} arguments were provided"); + logger.User("Usage: orpg();"); + return; + } + + // Apply the logic + Cleaner orpgCleaner = new Cleaner { OneRomPerGame = true }; + orpgCleaner.ApplyCleaning(datFile); + } + + /// + /// Populate the internal DAT from one or more files + /// + /// BatchCommand representing the line + /// DatFile representing the internal DAT + /// Current input file index + private void ParseInputs(BatchCommand command, DatFile datFile, ref int index) + { + if (command.Arguments.Count == 0) + { + logger.User($"Invoked {command.Name} but no arguments were provided"); + logger.User("Usage: input(datpath, ...);"); + return; + } + + // Get only files from inputs + List datFilePaths = PathTool.GetFilesOnly(command.Arguments); + + // Assume there could be multiple + foreach (ParentablePath datFilePath in datFilePaths) + { + Parser.ParseInto(datFile, datFilePath, index++); + } + } + + /// + /// Populate the internal DAT from one or more paths + /// + /// BatchCommand representing the line + /// DatFile representing the internal DAT + private void PopulateFromDir(BatchCommand command, DatFile datFile) + { + if (command.Arguments.Count == 0) + { + logger.User($"Invoked {command.Name} but no arguments were provided"); + logger.User("Usage: d2d(path, ...);"); + return; + } + + // TODO: Should any of the other options be added for D2D? + + // Assume there could be multiple + foreach (string input in command.Arguments) + { + DatTools.DatFromDir.PopulateFromDir(datFile, input, hashes: Hash.Standard); + } + + // TODO: We might not want to remove dates in the future + Remover dfdRemover = new Remover(); + dfdRemover.PopulateExclusionsFromList(new List { "DatItem.Date" }); + dfdRemover.ApplyRemovals(datFile); + } + + /// + /// Remove a field from the internal DAT + /// + /// BatchCommand representing the line + /// DatFile representing the internal DAT + private void RemoveField(BatchCommand command, DatFile datFile) + { + if (command.Arguments.Count == 0) + { + logger.User($"Invoked {command.Name} but no arguments were provided"); + logger.User("Usage: remove(field, ...);"); + return; + } + + // Run the removal functionality + Remover remover = new Remover(); + remover.PopulateExclusionsFromList(command.Arguments); + remover.ApplyRemovals(datFile); + } + + /// + /// Run internal split/merge on the internal DAT + /// + /// BatchCommand representing the line + /// DatFile representing the internal DAT + private void RunMerge(BatchCommand command, DatFile datFile) + { + if (command.Arguments.Count != 1) + { + logger.User($"Invoked {command.Name} and expected 1 argument, but {command.Arguments.Count} arguments were provided"); + logger.User("Usage: merge(split|merged|nonmerged|full|device);"); + return; + } + + // Read in the individual arguments + MergingFlag mergingFlag = command.Arguments[0].AsMergingFlag(); + + // If we had an invalid input, log and continue + if (mergingFlag == MergingFlag.None) + { + logger.User($"{command.Arguments[0]} was an invalid merging flag"); + return; + } + + // Apply the merging flag + Filtering.Splitter splitter = new Filtering.Splitter { SplitType = mergingFlag }; + splitter.ApplySplitting(datFile, false); + } + + /// + /// Apply scene date stripping to the internal DAT + /// + /// BatchCommand representing the line + /// DatFile representing the internal DAT + private void SceneDateStrip(BatchCommand command, DatFile datFile) + { + if (command.Arguments.Count != 0) + { + logger.User($"Invoked {command.Name} and expected no arguments, but {command.Arguments.Count} arguments were provided"); + logger.User("Usage: sds();"); + return; + } + + // Apply the logic + Cleaner stripCleaner = new Cleaner { SceneDateStrip = true }; + stripCleaner.ApplyCleaning(datFile); + } + + /// + /// Set a single header field on the internal DAT + /// + /// BatchCommand representing the line + /// DatFile representing the internal DAT + private void SetHeaderField(BatchCommand command, DatFile datFile) + { + if (command.Arguments.Count != 2) + { + logger.User($"Invoked {command.Name} but no arguments were provided"); + logger.User("Usage: set(header.field, value);"); + return; + } + + // Read in the individual arguments + DatHeaderField field = command.Arguments[0].AsDatHeaderField(); + string value = command.Arguments[1]; + + // If we had an invalid input, log and continue + if (field == DatHeaderField.NULL) + { + logger.User($"{command.Arguments[0]} was an invalid field name"); + return; + } + + // Set the header field + datFile.Header.SetFields(new Dictionary { [field] = value }); + } + + /// + /// Set output DatFile format + /// + /// BatchCommand representing the line + /// DatFile representing the internal DAT + private void SetOutputFormat(BatchCommand command, DatFile datFile) + { + if (command.Arguments.Count == 0) + { + logger.User($"Invoked {command.Name} but no arguments were provided"); + logger.User("Usage: format(datformat, ...);"); + return; + } + + // Assume there could be multiple + datFile.Header.DatFormat = 0x00; + foreach (string format in command.Arguments) + { + datFile.Header.DatFormat |= GetDatFormat(format); + } + + // If we had an invalid input, log and continue + if (datFile.Header.DatFormat == 0x00) + { + logger.User($"No valid output format found"); + return; + } + } + + /// + /// Write out the internal DAT + /// + /// BatchCommand representing the line + /// DatFile representing the internal DAT + /// Directory to write outputs to + private void Write(BatchCommand command, DatFile datFile, string outputDirectory) + { + if (command.Arguments.Count > 1) + { + logger.User($"Invoked {command.Name} and expected 0-1 arguments, but {command.Arguments.Count} arguments were provided"); + logger.User("Usage: write([overwrite = true]);"); + return; + } + + // Get overwrite value, if possible + bool? overwrite = true; + if (command.Arguments.Count == 1) + overwrite = command.Arguments[0].AsYesNo(); + + // If we had an invalid input, log and continue + if (overwrite == null) + { + logger.User($"{command.Arguments[0]} was an invalid true/false value"); + return; + } + + // Write out the dat with the current state + Writer.Write(datFile, outputDirectory, overwrite: overwrite.Value); + } + + #endregion + /// /// Internal representation of a single batch command /// + /// TODO: Should there be individual commands like there's individual features? + /// TODO: Should BatchCommand take care of the branching command values? + /// TODO: Should BatchCommand be a part of SabreTools.DatTools? private class BatchCommand { public string Name { get; private set; }