diff --git a/SabreTools.Core/Prepare.cs b/SabreTools.Core/Prepare.cs
index 8d41d536..151a2136 100644
--- a/SabreTools.Core/Prepare.cs
+++ b/SabreTools.Core/Prepare.cs
@@ -12,8 +12,8 @@ namespace SabreTools.Core
///
/// The current toolset version to be used by all child applications
///
- public readonly static string Version = $"v1.1.0";
- //public readonly static string Version = $"v1.1.0-{File.GetCreationTime(Assembly.GetExecutingAssembly().Location):yyyy-MM-dd HH:mm:ss}";
+ //public readonly static string Version = $"v1.1.0";
+ public readonly static string Version = $"v1.1.0-{File.GetCreationTime(Assembly.GetExecutingAssembly().Location):yyyy-MM-dd HH:mm:ss}";
///
/// Readies the console and outputs the header
diff --git a/SabreTools/Features/Batch.cs b/SabreTools/Features/Batch.cs
index 18a4d6e7..57a60d25 100644
--- a/SabreTools/Features/Batch.cs
+++ b/SabreTools/Features/Batch.cs
@@ -80,7 +80,7 @@ Reset the internal state: reset();";
string[] lines = File.ReadAllLines(path);
// Each batch file has its own state
- BatchFile batchFile = new BatchFile();
+ BatchState batchState = new BatchState();
// Process each command line
foreach (string line in lines)
@@ -101,90 +101,18 @@ Reset the internal state: reset();";
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())
+ // Validate that the command has the proper number and type of arguments
+ (bool valid, string error) = command.ValidateArguments();
+ if (!valid)
{
- // Set a header field
- case "set":
- SetHeaderField(command, batchFile);
- break;
-
- // Parse in new input file(s)
- case "input":
- ParseInputs(command, batchFile);
- break;
-
- // Run DFD/D2D on path(s)
- case "d2d":
- case "dfd":
- PopulateFromDir(command, batchFile);
- break;
-
- // Apply a filter
- case "filter":
- ApplyFilter(command, batchFile);
- break;
-
- // Apply an extra INI
- case "extra":
- ApplyExtra(command, batchFile);
- break;
-
- // Apply internal split/merge
- case "merge":
- RunMerge(command, batchFile);
- break;
-
- // Apply description-as-name logic
- case "descname":
- DescriptionAsName(command, batchFile);
- break;
-
- // Apply 1G1R
- case "1g1r":
- OneGamePerRegion(command, batchFile);
- break;
-
- // Apply one rom per game (ORPG)
- case "orpg":
- OneRomPerGame(command, batchFile);
- break;
-
- // Remove a field
- case "remove":
- RemoveField(command, batchFile);
- break;
-
- // Apply scene date stripping
- case "sds":
- SceneDateStrip(command, batchFile);
- break;
-
- // Set new output format(s)
- case "format":
- SetOutputFormat(command, batchFile);
- break;
-
- // Set output directory
- case "output":
- SetOutputDirectory(command, batchFile);
- break;
-
- // Write out the current DatFile
- case "write":
- Write(command, batchFile);
- break;
-
- // Reset the internal state
- case "reset":
- Reset(command, batchFile);
- break;
-
- default:
- logger.User($"Could not find a match for '{command.Name}'. Please see the help text for more details.");
- break;
+ logger.User(error);
+ logger.User($"Usage: {command.Usage()}");
+ break;
}
+
+ // Now run the command
+ logger.User($"Attempting to invoke {command.Name} with {(command.Arguments.Count == 0 ? "no arguments" : "the following argument(s): " + string.Join(", ", command.Arguments))}");
+ command.Process(batchState);
}
}
catch (Exception ex)
@@ -193,438 +121,30 @@ Reset the internal state: reset();";
}
}
- #region Batch Commands
- ///
- /// Apply a single extras file to the internal DAT
- ///
- /// BatchCommand representing the line
- /// Current BatchFile state
- private void ApplyExtra(BatchCommand command, BatchFile batchFile)
- {
- 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(batchFile.DatFile);
- }
-
- ///
- /// Apply a single filter to the internal DAT
- ///
- /// BatchCommand representing the line
- /// Current BatchFile state
- private void ApplyFilter(BatchCommand command, BatchFile batchFile)
- {
- 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(batchFile.DatFile, filterPerMachine.Value);
-
- // Cleanup after the filter
- // TODO: We might not want to remove immediately
- batchFile.DatFile.Items.ClearMarked();
- batchFile.DatFile.Items.ClearEmpty();
- }
-
- ///
- /// Apply a description-as-name to the internal DAT
- ///
- /// BatchCommand representing the line
- /// Current BatchFile state
- private void DescriptionAsName(BatchCommand command, BatchFile batchFile)
- {
- 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(batchFile.DatFile);
- }
-
- ///
- /// Apply 1G1R to the internal DAT
- ///
- /// BatchCommand representing the line
- /// Current BatchFile state
- private void OneGamePerRegion(BatchCommand command, BatchFile batchFile)
- {
- 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(batchFile.DatFile);
- }
-
- ///
- /// Apply ORPG to the internal DAT
- ///
- /// BatchCommand representing the line
- /// Current BatchFile state
- private void OneRomPerGame(BatchCommand command, BatchFile batchFile)
- {
- 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(batchFile.DatFile);
- }
-
- ///
- /// Populate the internal DAT from one or more files
- ///
- /// BatchCommand representing the line
- /// Current BatchFile state
- private void ParseInputs(BatchCommand command, BatchFile batchFile)
- {
- 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(batchFile.DatFile, datFilePath, batchFile.Index++);
- }
- }
-
- ///
- /// Populate the internal DAT from one or more paths
- ///
- /// BatchCommand representing the line
- /// Current BatchFile state
- private void PopulateFromDir(BatchCommand command, BatchFile batchFile)
- {
- 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(batchFile.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(batchFile.DatFile);
- }
-
- ///
- /// Remove a field from the internal DAT
- ///
- /// BatchCommand representing the line
- /// Current BatchFile state
- private void RemoveField(BatchCommand command, BatchFile batchFile)
- {
- 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(batchFile.DatFile);
- }
-
- ///
- /// Remove a field from the internal DAT
- ///
- /// BatchCommand representing the line
- /// Current BatchFile state
- private void Reset(BatchCommand command, BatchFile batchFile)
- {
- 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();");
- return;
- }
-
- // Reset all state variables
- batchFile.Reset();
- }
-
- ///
- /// Run internal split/merge on the internal DAT
- ///
- /// BatchCommand representing the line
- /// Current BatchFile state
- private void RunMerge(BatchCommand command, BatchFile batchFile)
- {
- 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(batchFile.DatFile, false);
- }
-
- ///
- /// Apply scene date stripping to the internal DAT
- ///
- /// BatchCommand representing the line
- /// Current BatchFile state
- private void SceneDateStrip(BatchCommand command, BatchFile batchFile)
- {
- 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(batchFile.DatFile);
- }
-
- ///
- /// Set a single header field on the internal DAT
- ///
- /// BatchCommand representing the line
- /// Current BatchFile state
- private void SetHeaderField(BatchCommand command, BatchFile batchFile)
- {
- 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
- batchFile.DatFile.Header.SetFields(new Dictionary { [field] = value });
- }
-
- ///
- /// Set output directory
- ///
- /// BatchCommand representing the line
- /// Current BatchFile state
- private void SetOutputDirectory(BatchCommand command, BatchFile batchFile)
- {
- 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);");
- return;
- }
-
- // Only set the first as the output directory
- batchFile.OutputDirectory = command.Arguments[0];
- }
-
- ///
- /// Set output DatFile format
- ///
- /// BatchCommand representing the line
- /// Current BatchFile state
- private void SetOutputFormat(BatchCommand command, BatchFile batchFile)
- {
- 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
- batchFile.DatFile.Header.DatFormat = 0x00;
- foreach (string format in command.Arguments)
- {
- batchFile.DatFile.Header.DatFormat |= GetDatFormat(format);
- }
-
- // If we had an invalid input, log and continue
- if (batchFile.DatFile.Header.DatFormat == 0x00)
- {
- logger.User($"No valid output format found");
- return;
- }
- }
-
- ///
- /// Write out the internal DAT
- ///
- /// BatchCommand representing the line
- /// Current BatchFile state
- private void Write(BatchCommand command, BatchFile batchFile)
- {
- 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(batchFile.DatFile, batchFile.OutputDirectory, overwrite: overwrite.Value);
- }
-
- #endregion
-
- #region Private Helper Classes
+ #region Commands
///
/// 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
+ private abstract class BatchCommand
{
public string Name { get; private set; }
public List Arguments { get; private set; } = new List();
+ ///
+ /// Default constructor for setting arguments
+ ///
+ public BatchCommand(List arguments)
+ {
+ Arguments = arguments;
+ }
+
///
/// Create a command based on parsing a line
///
+ /// Current line to parse into a command
public static BatchCommand Create(string line)
{
// Empty lines don't count
@@ -649,14 +169,730 @@ Reset the internal state: reset();";
.Where(s => !string.IsNullOrWhiteSpace(s)) // TODO: This may interfere with header value replacement
.ToList();
- return new BatchCommand { Name = commandName, Arguments = arguments };
+ return commandName.ToLowerInvariant() switch
+ {
+ "1g1r" => new OneGamePerRegionCommand(arguments),
+ "d2d" => new DFDCommand(arguments),
+ "dfd" => new DFDCommand(arguments),
+ "descname" => new DescriptionAsNameCommand(arguments),
+ "extra" => new ExtraCommand(arguments),
+ "filter" => new FilterCommand(arguments),
+ "format" => new FormatCommand(arguments),
+ "input" => new InputCommand(arguments),
+ "merge" => new MergeCommand(arguments),
+ "orpg" => new OneRomPerGameCommand(arguments),
+ "output" => new OutputCommand(arguments),
+ "remove" => new RemoveCommand(arguments),
+ "reset" => new ResetCommand(arguments),
+ "sds" => new SceneDateStripCommand(arguments),
+ "set" => new SetCommand(arguments),
+ "write" => new WriteCommand(arguments),
+ _ => null,
+ };
+ }
+
+ ///
+ /// Return usage string for a given command
+ ///
+ public abstract string Usage();
+
+ ///
+ /// Validate that a set of arguments are sufficient for a given command
+ ///
+ public abstract (bool, string) ValidateArguments();
+
+ ///
+ /// Process a batch file state with the current command
+ ///
+ /// Current batch file state to work on
+ public abstract void Process(BatchState batchState);
+ }
+
+ ///
+ /// Apply description-as-name logic
+ ///
+ private class DescriptionAsNameCommand : BatchCommand
+ {
+ ///
+ public DescriptionAsNameCommand(List arguments) : base(arguments) { }
+
+ ///
+ public override string Usage()
+ {
+ return "descname();";
+ }
+
+ ///
+ public override (bool, string) ValidateArguments()
+ {
+ if (Arguments.Count != 0)
+ {
+ string message = $"Invoked {Name} and expected no arguments, but {Arguments.Count} arguments were provided";
+ return (false, message);
+ }
+
+ return (true, null);
+ }
+
+ ///
+ public override void Process(BatchState batchState)
+ {
+ Cleaner descNameCleaner = new Cleaner { DescriptionAsName = true };
+ descNameCleaner.ApplyCleaning(batchState.DatFile);
+ }
+ }
+
+ ///
+ /// Run DFD/D2D on path(s)
+ ///
+ private class DFDCommand : BatchCommand
+ {
+ ///
+ public DFDCommand(List arguments) : base(arguments) { }
+
+ ///
+ public override string Usage()
+ {
+ return "d2d(path, ...);";
+ }
+
+ ///
+ public override (bool, string) ValidateArguments()
+ {
+ if (Arguments.Count == 0)
+ {
+ string message = $"Invoked {Name} but no arguments were provided";
+ return (false, message);
+ }
+
+ return (true, null);
+ }
+
+ ///
+ /// TODO: Should any of the other options be added for D2D?
+ public override void Process(BatchState batchState)
+ {
+ // Assume there could be multiple
+ foreach (string input in Arguments)
+ {
+ DatTools.DatFromDir.PopulateFromDir(batchState.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(batchState.DatFile);
}
}
///
- /// Internal representation of a single batch file
+ /// Apply an extra INI
///
- private class BatchFile
+ private class ExtraCommand : BatchCommand
+ {
+ ///
+ public ExtraCommand(List arguments) : base(arguments) { }
+
+ ///
+ public override string Usage()
+ {
+ return "extra(field, inipath);";
+ }
+
+ ///
+ public override (bool, string) ValidateArguments()
+ {
+ if (Arguments.Count != 2)
+ {
+ string message = $"Invoked {Name} and expected 2 arguments, but {Arguments.Count} arguments were provided";
+ return (false, message);
+ }
+
+ // Read in the individual arguments
+ MachineField extraMachineField = Arguments[0].AsMachineField();
+ DatItemField extraDatItemField = Arguments[0].AsDatItemField();
+ string extraFile = Arguments[1];
+
+ // If we had an invalid input, log and continue
+ if (extraMachineField == MachineField.NULL
+ && extraDatItemField == DatItemField.NULL)
+ {
+ string message = $"{Arguments[0]} was an invalid field name";
+ return (false, message);
+ }
+ if (!File.Exists(extraFile))
+ {
+ string message = $"{Arguments[1]} was an invalid file name";
+ return (false, message);
+ }
+
+ return (true, null);
+ }
+
+ ///
+ public override void Process(BatchState batchState)
+ {
+ // Read in the individual arguments
+ MachineField extraMachineField = Arguments[0].AsMachineField();
+ DatItemField extraDatItemField = Arguments[0].AsDatItemField();
+ string extraFile = Arguments[1];
+
+ // 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(batchState.DatFile);
+ }
+ }
+
+ ///
+ /// Apply a filter
+ ///
+ private class FilterCommand : BatchCommand
+ {
+ ///
+ public FilterCommand(List arguments) : base(arguments) { }
+
+ ///
+ public override string Usage()
+ {
+ return "filter(field, value, [remove = false, [perMachine = false]]);";
+ }
+
+ ///
+ public override (bool, string) ValidateArguments()
+ {
+ if (Arguments.Count < 2 || Arguments.Count > 4)
+ {
+ string message = $"Invoked {Name} and expected between 2-4 arguments, but {Arguments.Count} arguments were provided";
+ return (false, message);
+ }
+
+ // Read in the individual arguments
+ DatHeaderField filterDatHeaderField = Arguments[0].AsDatHeaderField();
+ MachineField filterMachineField = Arguments[0].AsMachineField();
+ DatItemField filterDatItemField = Arguments[0].AsDatItemField();
+ bool? filterRemove = false;
+ if (Arguments.Count >= 3)
+ filterRemove = Arguments[2].AsYesNo();
+ bool? filterPerMachine = false;
+ if (Arguments.Count >= 4)
+ filterPerMachine = Arguments[3].AsYesNo();
+
+ // If we had an invalid input, log and continue
+ if (filterDatHeaderField == DatHeaderField.NULL
+ && filterMachineField == MachineField.NULL
+ && filterDatItemField == DatItemField.NULL)
+ {
+ string message = $"{Arguments[0]} was an invalid field name";
+ return (false, message);
+ }
+ if (filterRemove == null)
+ {
+ string message = $"{Arguments[2]} was an invalid true/false value";
+ return (false, message);
+ }
+ if (filterPerMachine == null)
+ {
+ string message = $"{Arguments[3]} was an invalid true/false value";
+ return (false, message);
+ }
+
+ return (true, null);
+ }
+
+ ///
+ public override void Process(BatchState batchState)
+ {
+ // Read in the individual arguments
+ MachineField filterMachineField = Arguments[0].AsMachineField();
+ DatItemField filterDatItemField = Arguments[0].AsDatItemField();
+ string filterValue = Arguments[1];
+ bool? filterRemove = false;
+ if (Arguments.Count >= 3)
+ filterRemove = Arguments[2].AsYesNo();
+ bool? filterPerMachine = false;
+ if (Arguments.Count >= 4)
+ filterPerMachine = Arguments[3].AsYesNo();
+
+ // 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(batchState.DatFile, filterPerMachine.Value);
+
+ // Cleanup after the filter
+ // TODO: We might not want to remove immediately
+ batchState.DatFile.Items.ClearMarked();
+ batchState.DatFile.Items.ClearEmpty();
+ }
+ }
+
+ ///
+ /// Set new output format(s)
+ ///
+ private class FormatCommand : BatchCommand
+ {
+ ///
+ public FormatCommand(List arguments) : base(arguments) { }
+
+ ///
+ public override string Usage()
+ {
+ return "format(datformat, ...);";
+ }
+
+ ///
+ public override (bool, string) ValidateArguments()
+ {
+ if (Arguments.Count == 0)
+ {
+ string message = $"Invoked {Name} but no arguments were provided";
+ return (false, message);
+ }
+
+ // Check all inputs to be valid formats
+ List unmappedFormats = new List();
+ foreach (string format in Arguments)
+ {
+ if (GetDatFormat(format) == 0x0)
+ unmappedFormats.Add(format);
+ }
+
+ // If we had any unmapped formats, return an issue
+ if (unmappedFormats.Any())
+ {
+ string message = $"The following inputs were invalid formats: {string.Join(", ", unmappedFormats)}";
+ return (false, message);
+ }
+
+ return (true, null);
+ }
+
+ ///
+ public override void Process(BatchState batchState)
+ {
+ // Assume there could be multiple
+ batchState.DatFile.Header.DatFormat = 0x00;
+ foreach (string format in Arguments)
+ {
+ batchState.DatFile.Header.DatFormat |= GetDatFormat(format);
+ }
+ }
+ }
+
+ ///
+ /// Parse in new input file(s)
+ ///
+ private class InputCommand : BatchCommand
+ {
+ ///
+ public InputCommand(List arguments) : base(arguments) { }
+
+ ///
+ public override string Usage()
+ {
+ return "input(datpath, ...);";
+ }
+
+ ///
+ public override (bool, string) ValidateArguments()
+ {
+ if (Arguments.Count == 0)
+ {
+ string message = $"Invoked {Name} but no arguments were provided";
+ return (false, message);
+ }
+
+ return (true, null);
+ }
+
+ ///
+ public override void Process(BatchState batchState)
+ {
+ // Get only files from inputs
+ List datFilePaths = PathTool.GetFilesOnly(Arguments);
+
+ // Assume there could be multiple
+ foreach (ParentablePath datFilePath in datFilePaths)
+ {
+ Parser.ParseInto(batchState.DatFile, datFilePath, batchState.Index++);
+ }
+ }
+ }
+
+ ///
+ /// Apply internal split/merge
+ ///
+ private class MergeCommand : BatchCommand
+ {
+ ///
+ public MergeCommand(List arguments) : base(arguments) { }
+
+ ///
+ public override string Usage()
+ {
+ return "merge(split|merged|nonmerged|full|device);";
+ }
+
+ ///
+ public override (bool, string) ValidateArguments()
+ {
+ if (Arguments.Count != 1)
+ {
+ string message = $"Invoked {Name} and expected 1 argument, but {Arguments.Count} arguments were provided";
+ return (false, message);
+ }
+
+ // Read in the individual arguments
+ MergingFlag mergingFlag = Arguments[0].AsMergingFlag();
+
+ // If we had an invalid input, log and continue
+ if (mergingFlag == MergingFlag.None)
+ {
+ string message = $"{Arguments[0]} was an invalid merging flag";
+ return (false, message);
+ }
+
+ return (true, null);
+ }
+
+ ///
+ public override void Process(BatchState batchState)
+ {
+ // Read in the individual arguments
+ MergingFlag mergingFlag = Arguments[0].AsMergingFlag();
+
+ // Apply the merging flag
+ Filtering.Splitter splitter = new Filtering.Splitter { SplitType = mergingFlag };
+ splitter.ApplySplitting(batchState.DatFile, false);
+ }
+ }
+
+ ///
+ /// Apply 1G1R
+ ///
+ private class OneGamePerRegionCommand : BatchCommand
+ {
+ ///
+ public OneGamePerRegionCommand(List arguments) : base(arguments) { }
+
+ ///
+ public override string Usage()
+ {
+ return "1g1r(region, ...);";
+ }
+
+ ///
+ public override (bool, string) ValidateArguments()
+ {
+ if (Arguments.Count == 0)
+ {
+ string message = $"Invoked {Name} but no arguments were provided";
+ return (false, message);
+ }
+
+ return (true, null);
+ }
+
+ ///
+ public override void Process(BatchState batchState)
+ {
+ Cleaner ogorCleaner = new Cleaner { OneGamePerRegion = true, RegionList = Arguments };
+ ogorCleaner.ApplyCleaning(batchState.DatFile);
+ }
+ }
+
+ ///
+ /// Apply one rom per game (ORPG)
+ ///
+ private class OneRomPerGameCommand : BatchCommand
+ {
+ ///
+ public OneRomPerGameCommand(List arguments) : base(arguments) { }
+
+ ///
+ public override string Usage()
+ {
+ return "orpg();";
+ }
+
+ ///
+ public override (bool, string) ValidateArguments()
+ {
+ if (Arguments.Count == 0)
+ {
+ string message = $"Invoked {Name} and expected no arguments, but {Arguments.Count} arguments were provided";
+ return (false, message);
+ }
+
+ return (true, null);
+ }
+
+ ///
+ public override void Process(BatchState batchState)
+ {
+ Cleaner orpgCleaner = new Cleaner { OneRomPerGame = true };
+ orpgCleaner.ApplyCleaning(batchState.DatFile);
+ }
+ }
+
+ ///
+ /// Set output directory
+ ///
+ private class OutputCommand : BatchCommand
+ {
+ ///
+ public OutputCommand(List arguments) : base(arguments) { }
+
+ ///
+ public override string Usage()
+ {
+ return "output(outdir);";
+ }
+
+ ///
+ public override (bool, string) ValidateArguments()
+ {
+ if (Arguments.Count != 1)
+ {
+ string message = $"Invoked {Name} and expected exactly 1 argument, but {Arguments.Count} arguments were provided";
+ return (false, message);
+ }
+
+ return (true, null);
+ }
+
+ ///
+ public override void Process(BatchState batchState)
+ {
+ batchState.OutputDirectory = Arguments[0];
+ }
+ }
+
+ ///
+ /// Remove field(s)
+ ///
+ private class RemoveCommand : BatchCommand
+ {
+ ///
+ public RemoveCommand(List arguments) : base(arguments) { }
+
+ ///
+ public override string Usage()
+ {
+ return "remove(field, ...);";
+ }
+
+ ///
+ public override (bool, string) ValidateArguments()
+ {
+ if (Arguments.Count == 0)
+ {
+ string message = $"Invoked {Name} but no arguments were provided";
+ return (false, message);
+ }
+
+ return (true, null);
+ }
+
+ ///
+ public override void Process(BatchState batchState)
+ {
+ Remover remover = new Remover();
+ remover.PopulateExclusionsFromList(Arguments);
+ remover.ApplyRemovals(batchState.DatFile);
+ }
+ }
+
+ ///
+ /// Reset the internal state
+ ///
+ private class ResetCommand : BatchCommand
+ {
+ ///
+ public ResetCommand(List arguments) : base(arguments) { }
+
+ ///
+ public override string Usage()
+ {
+ return "reset();";
+ }
+
+ ///
+ public override (bool, string) ValidateArguments()
+ {
+ if (Arguments.Count != 0)
+ {
+ string message = $"Invoked {Name} and expected no arguments, but {Arguments.Count} arguments were provided";
+ return (false, message);
+ }
+
+ return (true, null);
+ }
+
+ ///
+ public override void Process(BatchState batchState)
+ {
+ batchState.Reset();
+ }
+ }
+
+ ///
+ /// Apply scene date stripping
+ ///
+ private class SceneDateStripCommand : BatchCommand
+ {
+ ///
+ public SceneDateStripCommand(List arguments) : base(arguments) { }
+
+ ///
+ public override string Usage()
+ {
+ return "sds();";
+ }
+
+ ///
+ public override (bool, string) ValidateArguments()
+ {
+ if (Arguments.Count != 0)
+ {
+ string message = $"Invoked {Name} and expected no arguments, but {Arguments.Count} arguments were provided";
+ return (false, message);
+ }
+
+ return (true, null);
+ }
+
+ ///
+ public override void Process(BatchState batchState)
+ {
+ Cleaner stripCleaner = new Cleaner { SceneDateStrip = true };
+ stripCleaner.ApplyCleaning(batchState.DatFile);
+ }
+ }
+
+ ///
+ /// Set a header field
+ ///
+ private class SetCommand : BatchCommand
+ {
+ ///
+ public SetCommand(List arguments) : base(arguments) { }
+
+ ///
+ public override string Usage()
+ {
+ return "set(header.field, value);";
+ }
+
+ ///
+ public override (bool, string) ValidateArguments()
+ {
+ if (Arguments.Count != 2)
+ {
+ string message = $"Invoked {Name} but no arguments were provided";
+ return (false, message);
+ }
+
+ DatHeaderField field = Arguments[0].AsDatHeaderField();
+
+ // If we had an invalid input, log and continue
+ if (field == DatHeaderField.NULL)
+ {
+ string message = $"{Arguments[0]} was an invalid field name";
+ return (false, message);
+ }
+
+ return (true, null);
+ }
+
+ ///
+ public override void Process(BatchState batchState)
+ {
+ // Read in the individual arguments
+ DatHeaderField field = Arguments[0].AsDatHeaderField();
+ string value = Arguments[1];
+
+ // Set the header field
+ batchState.DatFile.Header.SetFields(new Dictionary { [field] = value });
+ }
+ }
+
+ ///
+ /// Write out the current DatFile
+ ///
+ private class WriteCommand : BatchCommand
+ {
+ ///
+ public WriteCommand(List arguments) : base(arguments) { }
+
+ ///
+ public override string Usage()
+ {
+ return "write([overwrite = true]);";
+ }
+
+ ///
+ public override (bool, string) ValidateArguments()
+ {
+ if (Arguments.Count > 1)
+ {
+ string message = $"Invoked {Name} and expected 0-1 arguments, but {Arguments.Count} arguments were provided";
+ return (false, message);
+ }
+
+ // Get overwrite value, if possible
+ bool? overwrite = true;
+ if (Arguments.Count == 1)
+ overwrite = Arguments[0].AsYesNo();
+
+ // If we had an invalid input, log and continue
+ if (overwrite == null)
+ {
+ string message = $"{Arguments[0]} was an invalid true/false value";
+ return (false, message);
+ }
+
+ return (true, null);
+ }
+
+ ///
+ public override void Process(BatchState batchState)
+ {
+ // Get overwrite value, if possible
+ bool? overwrite = true;
+ if (Arguments.Count == 1)
+ overwrite = Arguments[0].AsYesNo();
+
+ // Write out the dat with the current state
+ Writer.Write(batchState.DatFile, batchState.OutputDirectory, overwrite: overwrite.Value);
+ }
+ }
+
+ #endregion
+
+ #region Private Helper Classes
+
+ ///
+ /// Internal representation of a single batch file state
+ ///
+ private class BatchState
{
public DatFile DatFile { get; set; } = DatFile.Create();
public int Index { get; set; } = 0;
@@ -672,7 +908,7 @@ Reset the internal state: reset();";
this.OutputDirectory = null;
}
}
-
+
#endregion
}
}