/// <summary> /// Output the stats for a list of input dats as files in a human-readable format /// </summary> /// <param name="stats">List of pre-calculated statistics objects</param> /// <param name="reportName">Name of the output file</param> /// <param name="baddumpCol">True if baddumps should be included in output, false otherwise</param> /// <param name="nodumpCol">True if nodumps should be included in output, false otherwise</param> /// <param name="statDatFormat"> Set the statistics output format to use</param> /// <param name="throwOnError">True if the error that is thrown should be thrown back to the caller, false otherwise</param> /// <returns>True if the report was written correctly, false otherwise</returns> public static bool Write( List <DatStatistics> stats, string reportName, string outDir, bool baddumpCol, bool nodumpCol, StatReportFormat statDatFormat, bool throwOnError = false) { // If there's no output format, set the default if (statDatFormat == StatReportFormat.None) { logger.Verbose("No report format defined, defaulting to textfile"); statDatFormat = StatReportFormat.Textfile; } // Get the proper output file name if (string.IsNullOrWhiteSpace(reportName)) { reportName = "report"; } // Get the proper output directory name outDir = outDir.Ensure(); InternalStopwatch watch = new InternalStopwatch($"Writing out report data to '{outDir}'"); // Get the dictionary of desired output report names Dictionary <StatReportFormat, string> outfiles = CreateOutStatsNames(outDir, statDatFormat, reportName); try { // Write out all required formats Parallel.ForEach(outfiles.Keys, Globals.ParallelOptions, reportFormat => { string outfile = outfiles[reportFormat]; try { BaseReport.Create(reportFormat, stats)?.WriteToFile(outfile, baddumpCol, nodumpCol, throwOnError); } catch (Exception ex) when(!throwOnError) { logger.Error(ex, $"Report '{outfile}' could not be written out"); } }); } catch (Exception ex) when(!throwOnError) { logger.Error(ex); return(false); } finally { watch.Stop(); } return(true); }
/// <summary> /// Wrap getting statistics on a DAT or folder of DATs /// </summary> /// <param name="inputs">List of inputs to be used</param> /// <param name="filename">Name of the file to output to, blank for default</param> /// <param name="outDir">Output directory for the report files</param> /// <param name="single">True to show individual DAT statistics, false otherwise</param> /// <param name="baddumpCol">True if baddumps should be included in output, false otherwise</param> /// <param name="nodumpCol">True if nodumps should be included in output, false otherwise</param> /// <param name="statDatFormat">Set the statistics output format to use</param> private static void InitStats( List <string> inputs, string filename, string outDir, bool single, bool baddumpCol, bool nodumpCol, StatReportFormat statDatFormat) { DatFile.OutputStats(inputs, filename, outDir, single, baddumpCol, nodumpCol, statDatFormat); }
/// <summary> /// Create a specific type of BaseReport to be used based on a format and user inputs /// </summary> /// <param name="statReportFormat">Format of the Statistics Report to be created</param> /// <param name="statsList">List of statistics objects to set</param> /// <returns>BaseReport of the specific internal type that corresponds to the inputs</returns> public static BaseReport Create(StatReportFormat statReportFormat, List <DatStatistics> statsList) { return(statReportFormat switch { StatReportFormat.None => new Textfile(statsList, true), StatReportFormat.Textfile => new Textfile(statsList, false), StatReportFormat.CSV => new SeparatedValue(statsList, ','), StatReportFormat.HTML => new Html(statsList), StatReportFormat.SSV => new SeparatedValue(statsList, ';'), StatReportFormat.TSV => new SeparatedValue(statsList, '\t'), _ => null, });
/// <summary> /// Create a specific type of BaseReport to be used based on a format and user inputs /// </summary> /// <param name="statReportFormat">Format of the Statistics Report to be created</param> /// <param name="filename">Name of the file to write out to</param> /// <param name="baddumpCol">True if baddumps should be included in output, false otherwise</param> /// <param name="nodumpCol">True if nodumps should be included in output, false otherwise</param> /// <returns>BaseReport of the specific internal type that corresponds to the inputs</returns> public static BaseReport Create(StatReportFormat statReportFormat, string filename, bool baddumpCol, bool nodumpCol) { #if NET_FRAMEWORK switch (statReportFormat) { case StatReportFormat.None: return(new Textfile(Console.OpenStandardOutput(), baddumpCol, nodumpCol)); case StatReportFormat.Textfile: return(new Textfile(filename, baddumpCol, nodumpCol)); case StatReportFormat.CSV: return(new SeparatedValue(filename, ',', baddumpCol, nodumpCol)); case StatReportFormat.HTML: return(new Html(filename, baddumpCol, nodumpCol)); case StatReportFormat.SSV: return(new SeparatedValue(filename, ';', baddumpCol, nodumpCol)); case StatReportFormat.TSV: return(new SeparatedValue(filename, '\t', baddumpCol, nodumpCol)); default: return(null); } #else return(statReportFormat switch { StatReportFormat.None => new Textfile(Console.OpenStandardOutput(), baddumpCol, nodumpCol), StatReportFormat.Textfile => new Textfile(filename, baddumpCol, nodumpCol), StatReportFormat.CSV => new SeparatedValue(filename, ',', baddumpCol, nodumpCol), StatReportFormat.HTML => new Html(filename, baddumpCol, nodumpCol), StatReportFormat.SSV => new SeparatedValue(filename, ';', baddumpCol, nodumpCol), StatReportFormat.TSV => new SeparatedValue(filename, '\t', baddumpCol, nodumpCol), _ => null, });
/// <summary> /// Get the proper extension for the stat output format /// </summary> /// <param name="outDir">Output path to use</param> /// <param name="statDatFormat">StatDatFormat to get the extension for</param> /// <param name="reportName">Name of the input file to use</param> /// <returns>Dictionary of output formats mapped to file names</returns> private static Dictionary <StatReportFormat, string> CreateOutStatsNames(string outDir, StatReportFormat statDatFormat, string reportName, bool overwrite = true) { Dictionary <StatReportFormat, string> output = new Dictionary <StatReportFormat, string>(); // First try to create the output directory if we need to if (!Directory.Exists(outDir)) { Directory.CreateDirectory(outDir); } // Double check the outDir for the end delim if (!outDir.EndsWith(Path.DirectorySeparatorChar.ToString())) { outDir += Path.DirectorySeparatorChar; } // For each output format, get the appropriate stream writer output.Add(StatReportFormat.None, CreateOutStatsNamesHelper(outDir, ".null", reportName, overwrite)); if (statDatFormat.HasFlag(StatReportFormat.Textfile)) { output.Add(StatReportFormat.Textfile, CreateOutStatsNamesHelper(outDir, ".txt", reportName, overwrite)); } if (statDatFormat.HasFlag(StatReportFormat.CSV)) { output.Add(StatReportFormat.CSV, CreateOutStatsNamesHelper(outDir, ".csv", reportName, overwrite)); } if (statDatFormat.HasFlag(StatReportFormat.HTML)) { output.Add(StatReportFormat.HTML, CreateOutStatsNamesHelper(outDir, ".html", reportName, overwrite)); } if (statDatFormat.HasFlag(StatReportFormat.SSV)) { output.Add(StatReportFormat.SSV, CreateOutStatsNamesHelper(outDir, ".ssv", reportName, overwrite)); } if (statDatFormat.HasFlag(StatReportFormat.TSV)) { output.Add(StatReportFormat.TSV, CreateOutStatsNamesHelper(outDir, ".tsv", reportName, overwrite)); } return(output); }
/// <summary> /// Entry class for the SabreTools application /// </summary> /// <param name="args">String array representing command line parameters</param> public static void Main(string[] args) { // Perform initial setup and verification Globals.Logger = new Logger(true, "sabretools.log"); // Create a new Help object for this program _help = SabreTools.RetrieveHelp(); // Get the location of the script tag, if it exists int scriptLocation = (new List <string>(args)).IndexOf("--script"); // If output is being redirected or we are in script mode, don't allow clear screens if (!Console.IsOutputRedirected && scriptLocation == -1) { Console.Clear(); Build.PrepareConsole("SabreTools"); } // Now we remove the script tag because it messes things up if (scriptLocation > -1) { List <string> newargs = new List <string>(args); newargs.RemoveAt(scriptLocation); args = newargs.ToArray(); } // Credits take precidence over all if ((new List <string>(args)).Contains("--credits")) { _help.OutputCredits(); Globals.Logger.Close(); return; } // If there's no arguments, show help if (args.Length == 0) { _help.OutputGenericHelp(); Globals.Logger.Close(); return; } // User flags bool addBlankFiles = false, addFileDates = false, archivesAsFiles = false, basedat = false, chdsAsFiles = false, cleanGameNames = false, copyFiles = false, delete = false, depot = false, descAsName = false, hashOnly = false, individual = false, inplace = false, inverse = false, noAutomaticDate = false, nostore = false, onlySame = false, quickScan = false, removeUnicode = false, showBaddumpColumn = false, showNodumpColumn = false, shortname = false, skipFirstOutput = false, updateDat = false; Hash omitFromScan = Hash.DeepHashes;// TODO: All instances of Hash.DeepHashes should be made into 0x0 eventually OutputFormat outputFormat = OutputFormat.Folder; ReplaceMode replaceMode = ReplaceMode.None; SkipFileType skipFileType = SkipFileType.None; SplittingMode splittingMode = SplittingMode.None; SplitType splitType = SplitType.None; StatReportFormat statDatFormat = StatReportFormat.None; UpdateMode updateMode = UpdateMode.None; // User inputs int gz = 1, rar = 1, sevenzip = 1, zip = 1; long radix = 0; string outDir = null, tempDir = ""; DatHeader datHeader = new DatHeader(); Filter filter = new Filter(); List <string> basePaths = new List <string>(); List <string> datfiles = new List <string>(); List <string> exta = new List <string>(); List <string> extb = new List <string>(); List <string> inputs = new List <string>(); List <Field> updateFields = new List <Field>(); // Get the first argument as a feature flag string feature = args[0]; // Verify that the flag is valid if (!_help.TopLevelFlag(feature)) { Globals.Logger.User("'{0}' is not valid feature flag", feature); _help.OutputIndividualFeature(feature); Globals.Logger.Close(); return; } // Now get the proper name for the feature feature = _help.GetFeatureName(feature); // If we had the help feature first if (feature == "Help") { // If we had something else after help if (args.Length > 1) { _help.OutputIndividualFeature(args[1]); Globals.Logger.Close(); return; } // Otherwise, show generic help else { _help.OutputGenericHelp(); Globals.Logger.Close(); return; } } else if (feature == "Help (Detailed)") { // If we had something else after help if (args.Length > 1) { _help.OutputIndividualFeature(args[1], includeLongDescription: true); Globals.Logger.Close(); return; } // Otherwise, show generic help else { _help.OutputAllHelp(); Globals.Logger.Close(); return; } } // Now verify that all other flags are valid for (int i = 1; i < args.Length; i++) { // Verify that the current flag is proper for the feature if (!_help[feature].ValidateInput(args[i])) { Globals.Logger.Error("Invalid input detected: {0}", args[i]); _help.OutputIndividualFeature(feature); Globals.Logger.Close(); return; } // Special precautions for files and directories if (File.Exists(args[i]) || Directory.Exists(args[i])) { inputs.Add(args[i]); } } // Now loop through all inputs Dictionary <string, Feature> features = _help.GetEnabledFeatures(); foreach (KeyValuePair <string, Feature> feat in features) { // Check all of the flag names and translate to arguments switch (feat.Key) { #region User Flags case "add-blank-files": addBlankFiles = true; break; case "add-date": addFileDates = true; break; case "archives-as-files": archivesAsFiles = true; break; case "baddump-column": showBaddumpColumn = true; break; case "base": basedat = true; break; case "base-replace": updateMode |= UpdateMode.BaseReplace; break; case "chds-as-Files": chdsAsFiles = true; break; case "copy-files": copyFiles = true; break; case "clean": cleanGameNames = true; break; case "dat-device-non-merged": splitType = SplitType.DeviceNonMerged; break; case "dat-full-non-merged": splitType = SplitType.FullNonMerged; break; case "dat-merged": splitType = SplitType.Merged; break; case "dat-non-merged": splitType = SplitType.NonMerged; break; case "dat-split": splitType = SplitType.Split; break; case "dedup": datHeader.DedupeRoms = DedupeType.Full; break; case "delete": delete = true; break; case "depot": depot = true; break; case "depreciated": // Remove the Logiqx standard output if this is included if ((datHeader.DatFormat & DatFormat.Logiqx) != 0) { datHeader.DatFormat &= ~DatFormat.Logiqx; } datHeader.DatFormat |= DatFormat.LogiqxDepreciated; break; case "description-as-name": descAsName = true; break; case "diff-against": updateMode |= UpdateMode.DiffAgainst; break; case "diff-all": updateMode |= UpdateMode.AllDiffs; break; case "diff-cascade": updateMode |= UpdateMode.DiffCascade; break; case "diff-duplicates": updateMode |= UpdateMode.DiffDupesOnly; break; case "diff-individuals": updateMode |= UpdateMode.DiffIndividualsOnly; break; case "diff-no-duplicates": updateMode |= UpdateMode.DiffNoDupesOnly; break; case "diff-reverse-cascade": updateMode |= UpdateMode.DiffReverseCascade; break; case "exclude-of": Globals.Logger.User("This flag '{0}' is depreciated, please use {1} instead", feat.Key, String.Join(", ", _excludeFieldListInput.Flags)); datHeader.ExcludeFields[(int)Field.CloneOf] = true; datHeader.ExcludeFields[(int)Field.MachineType] = true; datHeader.ExcludeFields[(int)Field.RomOf] = true; datHeader.ExcludeFields[(int)Field.Runnable] = true; datHeader.ExcludeFields[(int)Field.SampleOf] = true; break; case "extension": splittingMode |= SplittingMode.Extension; break; case "game-dedup": datHeader.DedupeRoms = DedupeType.Game; break; case "game-prefix": datHeader.GameName = true; break; case "hash": splittingMode |= SplittingMode.Hash; break; case "hash-only": hashOnly = true; break; case "individual": individual = true; break; case "inplace": inplace = true; break; case "inverse": inverse = true; break; case "keep-empty-games": datHeader.KeepEmptyGames = true; break; case "level": splittingMode |= SplittingMode.Level; break; case "match-of-tags": filter.IncludeOfInGame.Neutral = true; break; case "merge": updateMode |= UpdateMode.Merge; break; case "no-automatic-date": noAutomaticDate = true; break; case "nodump-column": showNodumpColumn = true; break; case "not-runnable": filter.Runnable.Neutral = false; break; case "no-store-header": nostore = true; break; case "one-rom-per-game": datHeader.OneRom = true; break; case "only-same": onlySame = true; break; case "quick": quickScan = true; break; case "quotes": datHeader.Quotes = true; break; case "remove-extensions": datHeader.RemoveExtension = true; break; case "remove-md5": Globals.Logger.User("This flag '{0}' is depreciated, please use {1} instead", feat.Key, String.Join(", ", _excludeFieldListInput.Flags)); datHeader.ExcludeFields[(int)Field.MD5] = true; break; case "remove-sha1": Globals.Logger.User("This flag '{0}' is depreciated, please use {1} instead", feat.Key, String.Join(", ", _excludeFieldListInput.Flags)); datHeader.ExcludeFields[(int)Field.SHA1] = true; break; case "remove-sha256": Globals.Logger.User("This flag '{0}' is depreciated, please use {1} instead", feat.Key, String.Join(", ", _excludeFieldListInput.Flags)); datHeader.ExcludeFields[(int)Field.SHA256] = true; break; case "remove-sha384": Globals.Logger.User("This flag '{0}' is depreciated, please use {1} instead", feat.Key, String.Join(", ", _excludeFieldListInput.Flags)); datHeader.ExcludeFields[(int)Field.SHA384] = true; break; case "remove-sha512": Globals.Logger.User("This flag '{0}' is depreciated, please use {1} instead", feat.Key, String.Join(", ", _excludeFieldListInput.Flags)); datHeader.ExcludeFields[(int)Field.SHA512] = true; break; case "remove-unicode": removeUnicode = true; break; case "reverse-base-name": updateMode |= UpdateMode.ReverseBaseReplace; break; case "romba": datHeader.Romba = true; break; case "roms": datHeader.UseRomName = true; break; case "runnable": filter.Runnable.Neutral = true; break; case "scan-all": sevenzip = 0; gz = 0; rar = 0; zip = 0; break; case "scene-date-strip": datHeader.SceneDateStrip = true; break; case "short": shortname = true; break; case "size": splittingMode |= SplittingMode.Size; break; case "skip-archives": skipFileType = SkipFileType.Archive; break; case "skip-files": skipFileType = SkipFileType.File; break; case "skip-first-output": skipFirstOutput = true; break; case "skip-md5": omitFromScan |= Hash.MD5; break; case "skip-sha1": omitFromScan |= Hash.SHA1; break; case "skip-sha256": omitFromScan &= ~Hash.SHA256; // This needs to be inverted later break; case "skip-sha384": omitFromScan &= ~Hash.SHA384; // This needs to be inverted later break; case "skip-sha512": omitFromScan &= ~Hash.SHA512; // This needs to be inverted later break; case "single-set": filter.Single.Neutral = true; break; case "superdat": datHeader.Type = "SuperDAT"; break; case "tar": outputFormat = OutputFormat.TapeArchive; break; case "torrent-7zip": outputFormat = OutputFormat.Torrent7Zip; break; case "torrent-gzip": outputFormat = OutputFormat.TorrentGzip; break; case "torrent-lrzip": outputFormat = OutputFormat.TorrentLRZip; break; case "torrent-lz4": outputFormat = OutputFormat.TorrentLZ4; break; case "torrent-rar": outputFormat = OutputFormat.TorrentRar; break; case "torrent-xz": outputFormat = OutputFormat.TorrentXZ; break; case "torrent-zip": outputFormat = OutputFormat.TorrentZip; break; case "torrent-zpaq": outputFormat = OutputFormat.TorrentZPAQ; break; case "torrent-zstd": outputFormat = OutputFormat.TorrentZstd; break; case "trim": filter.Trim.Neutral = true; break; case "type": splittingMode |= SplittingMode.Type; break; case "update-dat": updateDat = true; break; case "update-description": replaceMode |= ReplaceMode.Description; break; case "update-game-type": replaceMode |= ReplaceMode.MachineType; break; case "update-hashes": replaceMode |= ReplaceMode.Hash; break; case "update-manufacturer": replaceMode |= ReplaceMode.Manufacturer; break; case "update-names": replaceMode |= ReplaceMode.ItemName; break; case "update-parents": replaceMode |= ReplaceMode.Parents; break; case "update-year": replaceMode |= ReplaceMode.Year; break; #endregion #region User Int32 Inputs case "7z": sevenzip = (int)feat.Value.GetValue() == Int32.MinValue ? (int)feat.Value.GetValue() : 1; break; case "gz": gz = (int)feat.Value.GetValue() == Int32.MinValue ? (int)feat.Value.GetValue() : 1; break; case "rar": rar = (int)feat.Value.GetValue() == Int32.MinValue ? (int)feat.Value.GetValue() : 1; break; case "threads": int val = (int)feat.Value.GetValue(); if (val != Int32.MinValue) { Globals.MaxThreads = val; } break; case "zip": zip = (int)feat.Value.GetValue() == Int32.MinValue ? (int)feat.Value.GetValue() : 1; break; #endregion #region User Int64 Inputs case "radix": radix = (long)feat.Value.GetValue() == Int64.MinValue ? (long)feat.Value.GetValue() : 0; break; #endregion #region User List<string> Inputs case "base-dat": basePaths.AddRange((List <string>)feat.Value.GetValue()); break; case "crc": filter.CRC.PositiveSet.AddRange((List <string>)feat.Value.GetValue()); break; case "dat": datfiles.AddRange((List <string>)feat.Value.GetValue()); break; case "exclude-field": // TODO: Use this foreach (string field in (List <string>)feat.Value.GetValue()) { datHeader.ExcludeFields[(int)Utilities.GetField(field)] = true; } break; case "exta": exta.AddRange((List <string>)feat.Value.GetValue()); break; case "extb": extb.AddRange((List <string>)feat.Value.GetValue()); break; case "game-description": filter.MachineDescription.PositiveSet.AddRange((List <string>)feat.Value.GetValue()); break; case "game-name": filter.MachineName.PositiveSet.AddRange((List <string>)feat.Value.GetValue()); break; case "game-type": foreach (string mach in (List <string>)feat.Value.GetValue()) { filter.MachineTypes.Positive |= Utilities.GetMachineType(mach); } break; case "item-name": filter.ItemName.PositiveSet.AddRange((List <string>)feat.Value.GetValue()); break; case "item-type": filter.ItemTypes.PositiveSet.AddRange((List <string>)feat.Value.GetValue()); break; case "md5": filter.MD5.PositiveSet.AddRange((List <string>)feat.Value.GetValue()); break; case "not-crc": filter.CRC.NegativeSet.AddRange((List <string>)feat.Value.GetValue()); break; case "not-game-description": filter.MachineDescription.NegativeSet.AddRange((List <string>)feat.Value.GetValue()); break; case "not-game-name": filter.MachineName.NegativeSet.AddRange((List <string>)feat.Value.GetValue()); break; case "not-game-type": foreach (string nmach in (List <string>)feat.Value.GetValue()) { filter.MachineTypes.Negative |= Utilities.GetMachineType(nmach); } break; case "not-item-name": filter.ItemName.NegativeSet.AddRange((List <string>)feat.Value.GetValue()); break; case "not-item-type": filter.ItemTypes.NegativeSet.AddRange((List <string>)feat.Value.GetValue()); break; case "not-md5": filter.MD5.NegativeSet.AddRange((List <string>)feat.Value.GetValue()); break; case "not-sha1": filter.SHA1.NegativeSet.AddRange((List <string>)feat.Value.GetValue()); break; case "not-sha256": filter.SHA256.NegativeSet.AddRange((List <string>)feat.Value.GetValue()); break; case "not-sha384": filter.SHA384.NegativeSet.AddRange((List <string>)feat.Value.GetValue()); break; case "not-sha512": filter.SHA512.NegativeSet.AddRange((List <string>)feat.Value.GetValue()); break; case "not-status": foreach (string nstat in (List <string>)feat.Value.GetValue()) { filter.ItemStatuses.Negative |= Utilities.GetItemStatus(nstat); } break; case "output-type": foreach (string ot in (List <string>)feat.Value.GetValue()) { DatFormat dftemp = Utilities.GetDatFormat(ot); if (dftemp != DatFormat.Logiqx || (dftemp == DatFormat.Logiqx && (datHeader.DatFormat & DatFormat.LogiqxDepreciated) == 0)) { datHeader.DatFormat |= dftemp; } } break; case "report-type": foreach (string rt in (List <string>)feat.Value.GetValue()) { statDatFormat |= Utilities.GetStatFormat(rt); } break; case "sha1": filter.SHA1.PositiveSet.AddRange((List <string>)feat.Value.GetValue()); break; case "sha256": filter.SHA256.PositiveSet.AddRange((List <string>)feat.Value.GetValue()); break; case "sha384": filter.SHA384.PositiveSet.AddRange((List <string>)feat.Value.GetValue()); break; case "sha512": filter.SHA512.PositiveSet.AddRange((List <string>)feat.Value.GetValue()); break; case "status": foreach (string stat in (List <string>)feat.Value.GetValue()) { filter.ItemStatuses.Positive |= Utilities.GetItemStatus(stat); } break; case "update-field": // TODO: Use this foreach (string field in (List <string>)feat.Value.GetValue()) { updateFields.Add(Utilities.GetField(field)); } break; #endregion #region User String Inputs case "add-extension": datHeader.AddExtension = (string)feat.Value.GetValue(); break; case "author": datHeader.Author = (string)feat.Value.GetValue(); break; case "category": datHeader.Category = (string)feat.Value.GetValue(); break; case "comment": datHeader.Comment = (string)feat.Value.GetValue(); break; case "date": datHeader.Date = (string)feat.Value.GetValue(); break; case "description": datHeader.Description = (string)feat.Value.GetValue(); break; case "email": datHeader.Email = (string)feat.Value.GetValue(); break; case "equal": filter.Size.Neutral = Utilities.GetSizeFromString((string)feat.Value.GetValue()); break; case "filename": datHeader.FileName = (string)feat.Value.GetValue(); break; case "forcemerging": datHeader.ForceMerging = Utilities.GetForceMerging((string)feat.Value.GetValue()); break; case "forcenodump": datHeader.ForceNodump = Utilities.GetForceNodump((string)feat.Value.GetValue()); break; case "forcepacking": datHeader.ForcePacking = Utilities.GetForcePacking((string)feat.Value.GetValue()); break; case "greater": filter.Size.Positive = Utilities.GetSizeFromString((string)feat.Value.GetValue()); break; case "header": datHeader.Header = (string)feat.Value.GetValue(); break; case "homepage": datHeader.Homepage = (string)feat.Value.GetValue(); break; case "less": filter.Size.Negative = Utilities.GetSizeFromString((string)feat.Value.GetValue()); break; case "name": datHeader.Name = (string)feat.Value.GetValue(); break; case "output-dir": outDir = (string)feat.Value.GetValue(); break; case "postfix": datHeader.Postfix = (string)feat.Value.GetValue(); break; case "prefix": datHeader.Prefix = (string)feat.Value.GetValue(); break; case "replace-extension": datHeader.ReplaceExtension = (string)feat.Value.GetValue(); break; case "root": datHeader.RootDir = (string)feat.Value.GetValue(); break; case "root-dir": filter.Root.Neutral = (string)feat.Value.GetValue(); break; case "temp": tempDir = (string)feat.Value.GetValue(); break; case "url": datHeader.Url = (string)feat.Value.GetValue(); break; case "version": datHeader.Version = (string)feat.Value.GetValue(); break; #endregion } } // Now take care of each mode in succesion switch (feature) { case "Help": // No-op as this should be caught break; // Create a DAT from a directory or set of directories case "DATFromDir": VerifyInputs(inputs, feature); InitDatFromDir(inputs, datHeader, omitFromScan, noAutomaticDate, archivesAsFiles, chdsAsFiles, skipFileType, addBlankFiles, addFileDates, tempDir, outDir, copyFiles, filter); break; // If we're in header extract and remove mode case "Extract": VerifyInputs(inputs, feature); InitExtractRemoveHeader(inputs, outDir, nostore); break; // If we're in header restore mode case "Restore": VerifyInputs(inputs, feature); InitReplaceHeader(inputs, outDir); break; case "Script": // No-op as this should be caught break; // If we're using the sorter case "Sort": InitSort(datfiles, inputs, outDir, depot, quickScan, addFileDates, delete, inverse, outputFormat, datHeader.Romba, sevenzip, gz, rar, zip, updateDat, datHeader.Header, splitType, chdsAsFiles, individual); break; // Split a DAT by the split type case "Split": VerifyInputs(inputs, feature); InitSplit(inputs, outDir, inplace, datHeader.DatFormat, splittingMode, exta, extb, shortname, basedat, radix); break; // Get statistics on input files case "Stats": VerifyInputs(inputs, feature); InitStats(inputs, datHeader.FileName, outDir, individual, showBaddumpColumn, showNodumpColumn, statDatFormat); break; // Convert, update, merge, diff, and filter a DAT or folder of DATs case "Update": VerifyInputs(inputs, feature); InitUpdate(inputs, basePaths, datHeader, updateMode, inplace, skipFirstOutput, noAutomaticDate, filter, splitType, outDir, cleanGameNames, removeUnicode, descAsName, replaceMode, onlySame); break; // If we're using the verifier case "Verify": VerifyInputs(inputs, feature); InitVerify(datfiles, inputs, depot, hashOnly, quickScan, datHeader.Header, splitType, chdsAsFiles, individual, filter); break; // If nothing is set, show the help default: _help.OutputGenericHelp(); break; } Globals.Logger.Close(); return; }