diff --git a/SabreTools.Library/Help/Feature.cs b/SabreTools.Library/Help/Feature.cs index 6b0294fe..6ae64353 100644 --- a/SabreTools.Library/Help/Feature.cs +++ b/SabreTools.Library/Help/Feature.cs @@ -16,7 +16,6 @@ namespace SabreTools.Library.Help private string _longDescription; // TODO: Use this to generate README.1ST? private FeatureType _featureType; private Dictionary _features; - private List _additionalNotes; private bool _foundOnce = false; // Specific value types @@ -63,10 +62,9 @@ namespace SabreTools.Library.Help _longDescription = null; _featureType = FeatureType.Flag; _features = new Dictionary(); - _additionalNotes = new List(); } - public Feature(string name, string flag, string description, FeatureType featureType, string longDescription = null, List additionalNotes = null) + public Feature(string name, string flag, string description, FeatureType featureType, string longDescription = null) { _name = name; List flags = new List(); @@ -76,10 +74,9 @@ namespace SabreTools.Library.Help _longDescription = longDescription; _featureType = featureType; _features = new Dictionary(); - _additionalNotes = additionalNotes; } - public Feature(string name, List flags, string description, FeatureType featureType, string longDescription = null, List additionalNotes = null) + public Feature(string name, List flags, string description, FeatureType featureType, string longDescription = null) { _name = name; _flags = flags; @@ -87,7 +84,6 @@ namespace SabreTools.Library.Help _longDescription = longDescription; _featureType = featureType; _features = new Dictionary(); - _additionalNotes = additionalNotes; } #endregion @@ -170,40 +166,6 @@ namespace SabreTools.Library.Help } } - /// - /// Add a new additional note to this feature - /// - /// Note to add for this feature - public void AddNote(string note) - { - if (_additionalNotes == null) - { - _additionalNotes = new List(); - } - - lock (_additionalNotes) - { - _additionalNotes.Add(note); - } - } - - /// - /// Add a set of new notes for this feature - /// - /// List of notes to add to this feature - public void AddNotes(List notes) - { - if (_additionalNotes == null) - { - _additionalNotes = new List(); - } - - lock (_additionalNotes) - { - _additionalNotes.AddRange(notes); - } - } - /// /// Returns if a flag exists for the current feature /// @@ -262,7 +224,8 @@ namespace SabreTools.Library.Help /// /// Positive number representing number of spaces to put in front of the feature /// Positive number representing the column where the description should start - public List Output(int pre = 0, int midpoint = 0) + /// True if the long description should be formatted and output, false otherwise + public List Output(int pre = 0, int midpoint = 0, bool includeLongDescription = false) { // Create the output list List outputList = new List(); @@ -271,12 +234,7 @@ namespace SabreTools.Library.Help string output = ""; // Add the pre-space first - string prespace = ""; - for (int i = 0; i < pre; i++) - { - prespace += " "; - } - output += prespace; + output += CreatePadding(pre); // Now add all flags output += String.Join(", ", _flags); @@ -284,10 +242,7 @@ namespace SabreTools.Library.Help // If we have a midpoint set, check to see if the string needs padding if (midpoint > 0 && output.Length < midpoint) { - while (output.Length < midpoint) - { - output += " "; - } + output += CreatePadding(midpoint - output.Length); } else { @@ -300,16 +255,88 @@ namespace SabreTools.Library.Help // Now append it to the list outputList.Add(output); + // If we are outputting the long description, format it and then add it as well + if (includeLongDescription) + { + // Get the width of the console for wrapping reference + int width = Console.WindowWidth - 1; + + // Prepare the output string + output = CreatePadding(pre + 4); + + // Now split the input description and start processing + string[] split = _longDescription.Split(' '); + for (int i = 0; i < split.Length; i++) + { + // If we have a newline character, reset the line and continue + if (split[i].Contains("\n")) + { + string[] subsplit = split[i].Replace("\r", "").Split('\n'); + + // Add the next word only if the total length doesn't go above the width of the screen + if (output.Length + subsplit[0].Length < width) + { + output += (output.Length == pre + 4 ? "" : " ") + subsplit[0]; + } + // Otherwise, we want to cache the line to output and create a new blank string + else + { + outputList.Add(output); + output = CreatePadding(pre + 4); + output += (output.Length == pre + 4 ? "" : " ") + subsplit[0]; + } + + outputList.Add(output); + output = CreatePadding(pre + 4); + output += subsplit[1]; + continue; + } + + // Add the next word only if the total length doesn't go above the width of the screen + if (output.Length + split[i].Length < width) + { + output += (output.Length == pre + 4 ? "" : " ") + split[i]; + } + // Otherwise, we want to cache the line to output and create a new blank string + else + { + outputList.Add(output); + output = CreatePadding(pre + 4); + output += (output.Length == pre + 4 ? "" : " ") + split[i]; + } + } + + // Add the last created output and a blank line for clarity + outputList.Add(output); + outputList.Add(""); + } + return outputList; } + /// + /// Create a padding space based on the given length + /// + /// Number of padding spaces to add + /// String with requested number of blank spaces + private string CreatePadding(int spaces) + { + string blank = ""; + for (int i = 0; i < spaces; i++) + { + blank += " "; + } + return blank; + } + /// /// Output this feature and all subfeatures /// /// Level of indentation for this feature /// Positive number representing number of spaces to put in front of the feature /// Positive number representing the column where the description should start - public List OutputRecursive(int tabLevel, int pre = 0, int midpoint = 0) + /// True if the long description should be formatted and output, false otherwise + public List OutputRecursive(int tabLevel, int pre = 0, int midpoint = 0, bool includeLongDescription = false) { // Create the output list List outputList = new List(); @@ -327,12 +354,7 @@ namespace SabreTools.Library.Help } // Add the pre-space first - string prespace = ""; - for (int i = 0; i < preAdjusted; i++) - { - prespace += " "; - } - output += prespace; + output += CreatePadding(preAdjusted); // Now add all flags output += String.Join(", ", _flags); @@ -340,10 +362,7 @@ namespace SabreTools.Library.Help // If we have a midpoint set, check to see if the string needs padding if (midpoint > 0 && output.Length < midpointAdjusted) { - while (output.Length < midpointAdjusted) - { - output += " "; - } + output += CreatePadding(midpointAdjusted - output.Length); } else { @@ -356,19 +375,66 @@ namespace SabreTools.Library.Help // Now append it to the list outputList.Add(output); + // If we are outputting the long description, format it and then add it as well + if (includeLongDescription) + { + // Get the width of the console for wrapping reference + int width = Console.WindowWidth - 1; + + // Prepare the output string + output = CreatePadding(preAdjusted + 4); + + // Now split the input description and start processing + string[] split = _longDescription.Split(' '); + for (int i = 0; i < split.Length; i++) + { + // If we have a newline character, reset the line and continue + if (split[i].Contains("\n")) + { + string[] subsplit = split[i].Replace("\r", "").Split('\n'); + + // Add the next word only if the total length doesn't go above the width of the screen + if (output.Length + subsplit[0].Length < width) + { + output += (output.Length == preAdjusted + 4 ? "" : " ") + subsplit[0]; + } + // Otherwise, we want to cache the line to output and create a new blank string + else + { + outputList.Add(output); + output = CreatePadding(preAdjusted + 4); + output += (output.Length == preAdjusted + 4 ? "" : " ") + subsplit[0]; + } + + outputList.Add(output); + output = CreatePadding(preAdjusted + 4); + output += subsplit[1]; + continue; + } + + // Add the next word only if the total length doesn't go above the width of the screen + if (output.Length + split[i].Length < width) + { + output += (output.Length == preAdjusted + 4 ? "" : " ") + split[i]; + } + // Otherwise, we want to cache the line to output and create a new blank string + else + { + outputList.Add(output); + output = CreatePadding(preAdjusted + 4); + output += (output.Length == preAdjusted + 4 ? "" : " ") + split[i]; + } + } + + // Add the last created output and a blank line for clarity + outputList.Add(output); + outputList.Add(""); + } + // Now let's append all subfeatures foreach (string feature in _features.Keys) { - outputList.AddRange(_features[feature].OutputRecursive(tabLevel + 1, pre, midpoint)); - } - - // Finally, let's append all additional notes - if (_additionalNotes != null && _additionalNotes.Count > 0) - { - foreach (string note in _additionalNotes) - { - outputList.Add(prespace + note); - } + outputList.AddRange(_features[feature].OutputRecursive(tabLevel + 1, pre, midpoint, includeLongDescription)); } return outputList; diff --git a/SabreTools.Library/Help/Help.cs b/SabreTools.Library/Help/Help.cs index 10fb2e05..365fdcda 100644 --- a/SabreTools.Library/Help/Help.cs +++ b/SabreTools.Library/Help/Help.cs @@ -156,7 +156,7 @@ namespace SabreTools.Library.Help output.Add("Available options:"); foreach (string feature in _features.Keys) { - output.AddRange(_features[feature].Output(pre: 2, midpoint: 25)); + output.AddRange(_features[feature].Output(pre: 2, midpoint: 30)); } // And append the generic ending @@ -182,7 +182,7 @@ namespace SabreTools.Library.Help output.Add("Available options:"); foreach (string feature in _features.Keys) { - output.AddRange(_features[feature].OutputRecursive(0, pre: 2, midpoint: 25)); + output.AddRange(_features[feature].OutputRecursive(0, pre: 2, midpoint: 30, includeLongDescription: true)); } // Now write out everything in a staged manner @@ -210,7 +210,9 @@ namespace SabreTools.Library.Help /// /// Output a single feature recursively /// - public void OutputIndividualFeature(string featurename) + /// Name of the feature to output information for, if possible + /// True if the long description should be formatted and output, false otherwise + public void OutputIndividualFeature(string featurename, bool includeLongDescription = false) { // Start building the output list List output = new List(); @@ -245,7 +247,7 @@ namespace SabreTools.Library.Help if (realname != null) { output.Add("Available options for " + realname + ":"); - output.AddRange(_features[realname].OutputRecursive(0, pre: 2, midpoint: 25)); + output.AddRange(_features[realname].OutputRecursive(0, pre: 2, midpoint: 30, includeLongDescription: includeLongDescription)); } // If no name was found but we have possible matches, show them @@ -254,7 +256,7 @@ namespace SabreTools.Library.Help output.Add("\"" + featurename + "\" not found. Did you mean:"); foreach (string possible in startsWith) { - output.AddRange(_features[possible].Output(pre: 2, midpoint: 25)); + output.AddRange(_features[possible].Output(pre: 2, midpoint: 30, includeLongDescription: includeLongDescription)); } } diff --git a/SabreTools/SabreTools.Help.cs b/SabreTools/SabreTools.Help.cs index 8c8493ed..ca88b8d7 100644 --- a/SabreTools/SabreTools.Help.cs +++ b/SabreTools/SabreTools.Help.cs @@ -1391,7 +1391,10 @@ namespace SabreTools new List() { "-gz", "--gz" }, "Set scanning level for GZip archives (default 1)", FeatureType.Int32, - longDescription: "Scan GZip archives in one of the following ways: 0 - Hash both archive and its contents; 1 - Only hash contents of the archive; 2 - Only hash archive itself (treat like a regular file)."); + longDescription: @"Scan GZip archives in one of the following ways: +0 - Hash both archive and its contents +1 - Only hash contents of the archive +2 - Only hash archive itself (treat like a regular file)"); } } private static Feature rarInt32Input @@ -1403,7 +1406,10 @@ namespace SabreTools new List() { "-rar", "--rar" }, "Set scanning level for RAR archives (default 1)", FeatureType.Int32, - longDescription: "Scan RAR archives in one of the following ways: 0 - Hash both archive and its contents; 1 - Only hash contents of the archive; 2 - Only hash archive itself (treat like a regular file)."); + longDescription: @"Scan RAR archives in one of the following ways: +0 - Hash both archive and its contents +1 - Only hash contents of the archive +2 - Only hash archive itself (treat like a regular file)"); } } private static Feature sevenZipInt32Input @@ -1415,7 +1421,10 @@ namespace SabreTools new List() { "-7z", "--7z" }, "Set scanning level for 7zip archives (default 1)", FeatureType.Int32, - longDescription: "Scan 7Zip archives in one of the following ways: 0 - Hash both archive and its contents; 1 - Only hash contents of the archive; 2 - Only hash archive itself (treat like a regular file)."); + longDescription: @"Scan 7Zip archives in one of the following ways: +0 - Hash both archive and its contents +1 - Only hash contents of the archive +2 - Only hash archive itself (treat like a regular file)"); } } private static Feature threadsInt32Input @@ -1439,7 +1448,10 @@ namespace SabreTools new List() { "-zip", "--zip" }, "Set scanning level for Zip archives (default 1)", FeatureType.Int32, - longDescription: "Scan Zip archives in one of the following ways: 0 - Hash both archive and its contents; 1 - Only hash contents of the archive; 2 - Only hash archive itself (treat like a regular file)."); + longDescription: @"Scan Zip archives in one of the following ways: +0 - Hash both archive and its contents +1 - Only hash contents of the archive +2 - Only hash archive itself (treat like a regular file)"); } } @@ -1555,9 +1567,10 @@ namespace SabreTools return new Feature( "game-type", new List() { "-gt", "--game-type" }, - "Include only games with a given type [None, Bios, Device, Mechanical]", + "Include only games with a given type", FeatureType.List, - longDescription: "Include only items with this game type in the output. Additionally, the user can specify an exact match or full C#-style regex for pattern matching. Multiple instances of this flag are allowed."); + longDescription: @"Include only items with this game type in the output. Multiple instances of this flag are allowed. +Possible values are: None, Bios, Device, Mechanical"); } } private static Feature itemNameListInput @@ -1639,9 +1652,10 @@ namespace SabreTools return new Feature( "not-game-type", new List() { "-ngt", "--not-game-type" }, - "Exclude only games with a given type [None, Bios, Device, Mechanical]", + "Exclude only games with a given type", FeatureType.List, - longDescription: "Include only items without this game type in the output. Additionally, the user can specify an exact match or full C#-style regex for pattern matching. Multiple instances of this flag are allowed."); + longDescription: @"Include only items without this game type in the output. Multiple instances of this flag are allowed. +Possible values are: None, Bios, Device, Mechanical"); } } private static Feature notItemNameListInput @@ -1735,9 +1749,10 @@ namespace SabreTools return new Feature( "not-status", new List() { "-nis", "--not-status" }, - "Exclude only items with a given status [None, Good, BadDump, Nodump, Verified]", + "Exclude only items with a given status", FeatureType.List, - longDescription: "Include only items without this item status in the output. Additionally, the user can specify an exact match or full C#-style regex for pattern matching. Multiple instances of this flag are allowed."); + longDescription: @"Include only items without this item status in the output. Multiple instances of this flag are allowed. +Possible values are: None, Good, BadDump, Nodump, Verified"); } } private static Feature outputTypeListInput @@ -1747,9 +1762,31 @@ namespace SabreTools return new Feature( "output-type", new List() { "-ot", "--output-type" }, - "Output DATs to a given format [all, am/attractmode, cmp/clrmamepro, csv, dc/doscenter, lr/listrom, lx/listxml, miss/missfile, md5, ol/offlinelist, rc/romcenter, sd/sabredat, sfv, sha1, sha256, sha384, sha512, sl/softwarelist, ssv, tsv, xml/logiqx", + "Output DATs to a specified format", FeatureType.List, - longDescription: @"Add outputting the created DAT to one of the following formats: all - All available DAT types; am, attractmode - AttractMode XML; cmp, clrmamepro - ClrMamePro; csv - Standardized Comma-Separated Value; dc, doscenter - DOSCenter; lr, listrom - MAME Listrom; lx, listxml - MAME Listxml; miss, missfile - GoodTools Missfile; md5 - MD5; ol, offlinelist - OfflineList XML; rc, romcenter - RomCenter; sd, sabredat - SabreDat XML; sfv - SFV; sha1 - SHA-1; sha256 - SHA-256; sha384 - SHA-384; sha512 - SHA-512; sl, softwarelist - MAME Software List XML; ssv - Standardized Semicolon - Separated Value; tsv - Standardized Tab - Separated Value; xml, logiqx - Logiqx XML. Multiple instances of this flag are allowed."); + longDescription: @"Add outputting the created DAT to known format. Multiple instances of this flag are allowed. +Possible values are: +all - All available DAT types +am, attractmode - AttractMode XML +cmp, clrmamepro - ClrMamePro +csv - Standardized Comma-Separated Value +dc, doscenter - DOSCenter +lr, listrom - MAME Listrom +lx, listxml - MAME Listxml +miss, missfile - GoodTools Missfile +md5 - MD5 +ol, offlinelist - OfflineList XML +rc, romcenter - RomCenter +sd, sabredat - SabreDat XML +sfv - SFV +sha1 - SHA1 +sha256 - SHA256 +sha384 - SHA384 +sha512 - SHA512 +sl, softwarelist - MAME Software List XML +ssv - Standardized Semicolon-Separated Value +tsv - Standardized Tab-Separated Value +xml, logiqx - Logiqx XML"); } } private static Feature sha1ListInput @@ -1807,9 +1844,10 @@ namespace SabreTools return new Feature( "status", new List() { "-is", "--status" }, - "Include only items with a given status [None, Good, BadDump, Nodump, Verified]", + "Include only items with a given status", FeatureType.List, - longDescription: "Include only items with this item status in the output. Additionally, the user can specify an exact match or full C#-style regex for pattern matching. Multiple instances of this flag are allowed."); + longDescription: @"Include only items with this item status in the output. Multiple instances of this flag are allowed. +Possible values are: None, Good, BadDump, Nodump, Verified"); } } @@ -1932,9 +1970,10 @@ namespace SabreTools return new Feature( "forcemerging", new List() { "-fm", "--forcemerging" }, - "Set force merging [None, Split, Merged, Nonmerged, Full]", + "Set force merging", FeatureType.String, - longDescription: "Set the forcemerging tag to the given value."); + longDescription: @"Set the forcemerging tag to the given value. +Possible values are: None, Split, Merged, Nonmerged, Full"); } } private static Feature forcenodumpStringInput @@ -1944,9 +1983,10 @@ namespace SabreTools return new Feature( "forcenodump", new List() { "-fn", "--forcenodump" }, - "Set force nodump [None, Obsolete, Required, Ignore]", + "Set force nodump", FeatureType.String, - longDescription: "Set the forcenodump tag to the given value."); + longDescription: @"Set the forcenodump tag to the given value. +Possible values are: None, Obsolete, Required, Ignore"); } } private static Feature forcepackingStringInput @@ -1956,9 +1996,10 @@ namespace SabreTools return new Feature( "forcepacking", new List() { "-fp", "--forcepacking" }, - "Set force packing [None, Zip, Unzip]", + "Set force packing", FeatureType.String, - longDescription: "Set the forcepacking tag to the given value."); + longDescription: @"Set the forcepacking tag to the given value. +Possible values are: None, Zip, Unzip"); } } private static Feature greaterStringInput @@ -2157,6 +2198,17 @@ namespace SabreTools #endregion + #region Detailed Help + + Feature detailedHelpFeature = new Feature( + "Help (Detailed)", + new List() { "-??", "-hd", "--help-detailed" }, + "Show this detailed help", + FeatureType.Flag, + longDescription: "Display a detailed help text to the screen."); + + #endregion + #region Script Feature script = new Feature( @@ -2244,7 +2296,15 @@ namespace SabreTools new List() { "-ex", "--extract" }, "Extract and remove copier headers", FeatureType.Flag, - longDescription: "This will detect, store, and remove copier headers from a file or folder of files. The headers are backed up and collated by the hash of the unheadered file. Files are then output without the detected copier header alongside the originals with the suffix .new. No input files are altered in the process. The following systems have headers that this program can work with: Atari 7800; Atari Lynx; Commodore PSID Music; NEC PC - Engine / TurboGrafx 16; Nintendo Famicom / Nintendo Entertainment System; Nintendo Famicom Disk System; Nintendo Super Famicom / Super Nintendo Entertainment System; Nintendo Super Famicom / Super Nintendo Entertainment System SPC."); + longDescription: @"This will detect, store, and remove copier headers from a file or folder of files. The headers are backed up and collated by the hash of the unheadered file. Files are then output without the detected copier header alongside the originals with the suffix .new. No input files are altered in the process. The following systems have headers that this program can work with: +- Atari 7800 +- Atari Lynx +- Commodore PSID Music +- NEC PC - Engine / TurboGrafx 16 +- Nintendo Famicom / Nintendo Entertainment System +- Nintendo Famicom Disk System +- Nintendo Super Famicom / Super Nintendo Entertainment System +- Nintendo Super Famicom / Super Nintendo Entertainment System SPC"); extract.AddFeature(outputDirStringInput); extract.AddFeature(noStoreHeaderFlag); @@ -2257,7 +2317,15 @@ namespace SabreTools new List() { "-re", "--restore" }, "Restore header to file based on SHA-1", FeatureType.Flag, - longDescription: "This will make use of stored copier headers and reapply them to files if they match the included hash. More than one header can be applied to a file, so they will be output to new files, suffixed with .newX, where X is a number. No input files are altered in the process. The following systems have headers that this program can work with: Atari 7800; Atari Lynx; Commodore PSID Music; NEC PC - Engine / TurboGrafx 16; Nintendo Famicom / Nintendo Entertainment System; Nintendo Famicom Disk System; Nintendo Super Famicom / Super Nintendo Entertainment System; Nintendo Super Famicom / Super Nintendo Entertainment System SPC."); + longDescription: @"This will make use of stored copier headers and reapply them to files if they match the included hash. More than one header can be applied to a file, so they will be output to new files, suffixed with .newX, where X is a number. No input files are altered in the process. The following systems have headers that this program can work with: +- Atari 7800 +- Atari Lynx +- Commodore PSID Music +- NEC PC - Engine / TurboGrafx 16 +- Nintendo Famicom / Nintendo Entertainment System +- Nintendo Famicom Disk System +- Nintendo Super Famicom / Super Nintendo Entertainment System +- Nintendo Super Famicom / Super Nintendo Entertainment System SPC"); restore.AddFeature(outputDirStringInput); #endregion @@ -2361,7 +2429,18 @@ namespace SabreTools new List() { "-st", "--stats" }, "Get statistics on all input DATs", FeatureType.Flag, - longDescription: "This will output by default the combined statistics for all input DAT files. The stats that are outputted are as follows: Total uncompressed size; Number of games found; Number of roms found; Number of disks found; Roms that include a CRC/MD5/SHA-1/SHA-256/SHA-384/SHA-512; Roms with Nodump status."); + longDescription: @"This will output by default the combined statistics for all input DAT files. The stats that are outputted are as follows: +- Total uncompressed size +- Number of games found +- Number of roms found +- Number of disks found +- Items that include a CRC +- Items that include a MD5 +- Items that include a SHA-1 +- Items that include a SHA-256 +- Items that include a SHA-384 +- Items that include a SHA-512 +- Items with Nodump status"); stats.AddFeature(allStatsFlag); stats.AddFeature(baddumpColumnFlag); stats.AddFeature(csvFlag); @@ -2572,6 +2651,7 @@ namespace SabreTools // Now, add all of the main features to the Help object help.Add(helpFeature); + help.Add(detailedHelpFeature); help.Add(script); help.Add(datFromDir); help.Add(extract); diff --git a/SabreTools/SabreTools.cs b/SabreTools/SabreTools.cs index d6fafb30..03f76bdf 100644 --- a/SabreTools/SabreTools.cs +++ b/SabreTools/SabreTools.cs @@ -154,6 +154,23 @@ namespace SabreTools 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++)