diff --git a/README.md b/README.md index de93739d..2fd499c0 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,8 @@ Options: --titlekeys Load title keys from an external file. --accesslog Specify the access log file path. --disablekeywarns Disables warning output when loading external keys. + --version Display version information and exit. + --help Display this help and exit. NCA options: --plaintext Specify file path for saving a decrypted copy of the NCA. --ciphertext Specify file path for saving an encrypted copy of the NCA. diff --git a/src/LibHac/Fs/AccessLog.cs b/src/LibHac/Fs/AccessLog.cs index f3e8ed9f..75c99441 100644 --- a/src/LibHac/Fs/AccessLog.cs +++ b/src/LibHac/Fs/AccessLog.cs @@ -812,11 +812,11 @@ namespace LibHac.Fs.Impl public static ReadOnlySpan FsModuleName => // "$fs" new[] { (byte)'$', (byte)'f', (byte)'s' }; - /// "0.16.1" - public static ReadOnlySpan LogLibHacVersion => // "0.16.1" + /// "0.17.0" + public static ReadOnlySpan LogLibHacVersion => // "0.17.0" new[] { - (byte)'0', (byte)'.', (byte)'1', (byte)'6', (byte)'.', (byte)'1' + (byte)'0', (byte)'.', (byte)'1', (byte)'7', (byte)'.', (byte)'0' }; /// """ diff --git a/src/LibHac/LibHac.csproj b/src/LibHac/LibHac.csproj index 67fdf2fb..e7df0d52 100644 --- a/src/LibHac/LibHac.csproj +++ b/src/LibHac/LibHac.csproj @@ -2,7 +2,7 @@ Library - 0.16.1 + 0.17.0 net6.0 true diff --git a/src/hactoolnet/CliParser.cs b/src/hactoolnet/CliParser.cs index 74eedb42..f8ae51c6 100644 --- a/src/hactoolnet/CliParser.cs +++ b/src/hactoolnet/CliParser.cs @@ -9,8 +9,10 @@ internal static class CliParser { private static CliOption[] GetCliOptions() => new[] { + new CliOption("help", 0, (o, _) => o.PrintHelp = true), + new CliOption("version", 0, (o, _) => o.PrintVersion = true), new CliOption("custom", 0, (o, _) => o.RunCustom = true), - new CliOption("intype", 't', 1, (o, a) => o.InFileType = ParseFileType(a[0])), + new CliOption("intype", 't', 1, (o, a) => o.InFileType = ParseFileType(o, a[0])), new CliOption("raw", 'r', 0, (o, _) => o.Raw = true), new CliOption("verify", 'y', 0, (o, _) => o.Validate = true), new CliOption("dev", 'd', 0, (o, _) => o.UseDevKeys = true), @@ -63,9 +65,9 @@ internal static class CliParser new CliOption("readbench", 0, (o, _) => o.ReadBench = true), new CliOption("hashedfs", 0, (o, _) => o.BuildHfs = true), new CliOption("extractini1", 0, (o, _) => o.ExtractIni1 = true), - new CliOption("title", 1, (o, a) => o.TitleId = ParseTitleId(a[0])), + new CliOption("title", 1, (o, a) => o.TitleId = ParseTitleId(o, a[0])), new CliOption("bench", 1, (o, a) => o.BenchType = a[0]), - new CliOption("cpufreq", 1, (o, a) => o.CpuFrequencyGhz = ParseDouble(a[0])), + new CliOption("cpufreq", 1, (o, a) => o.CpuFrequencyGhz = ParseDouble(o, a[0])), new CliOption("replacefile", 2, (o, a) => { @@ -75,6 +77,38 @@ internal static class CliParser }; public static Options Parse(string[] args) + { + Options options = ParseAllOptions(args); + + if (options.PrintVersion) + { + Console.WriteLine(GetLongVersion()); + + return options; + } + + if (options.PrintHelp) + { + Console.WriteLine(GetShortVersion()); + Console.WriteLine(GetUsage()); + + return options; + } + + if (!options.IsParseSuccessful) + { + Console.WriteLine($"hactoolnet: {options.ParseErrorMessage}"); + Console.WriteLine("Usage: hactoolnet [options...] "); + Console.WriteLine("Use 'hactoolnet --help' for full usage information."); + + return options; + } + + options.ContinueRunning = true; + return options; + } + + public static Options ParseAllOptions(string[] args) { var options = new Options(); bool inputSpecified = false; @@ -89,7 +123,7 @@ internal static class CliParser { arg = args[i][1].ToString().ToLower(); } - else if (args[i].Length > 2 && args[i].Substring(0, 2) == "--") + else if (args[i].Length > 2 && (args[i][0] == '-' && args[i][1] == '-')) { arg = args[i].Substring(2).ToLower(); } @@ -97,8 +131,8 @@ internal static class CliParser { if (inputSpecified) { - PrintWithUsage($"Unable to parse option {args[i]}"); - return null; + options.ParseErrorMessage ??= $"Unable to parse option {args[i]}"; + continue; } options.InFile = args[i]; @@ -109,14 +143,14 @@ internal static class CliParser CliOption option = cliOptions.FirstOrDefault(x => x.Long == arg || x.Short == arg); if (option == null) { - PrintWithUsage($"Unknown option {args[i]}"); - return null; + options.ParseErrorMessage ??= $"Unknown option {args[i]}"; + continue; } if (i + option.ArgsNeeded >= args.Length) { - PrintWithUsage($"Need {option.ArgsNeeded} parameter{(option.ArgsNeeded == 1 ? "" : "s")} after {args[i]}"); - return null; + options.ParseErrorMessage ??= $"Need {option.ArgsNeeded} parameter{(option.ArgsNeeded == 1 ? "" : "s")} after {args[i]}"; + continue; } string[] optionArgs = new string[option.ArgsNeeded]; @@ -128,14 +162,14 @@ internal static class CliParser if (!inputSpecified && options.InFileType != FileType.Keygen && options.InFileType != FileType.Bench && !options.RunCustom) { - PrintWithUsage("Input file must be specified"); - return null; + options.ParseErrorMessage ??= "Input file must be specified"; } + options.IsParseSuccessful = options.ParseErrorMessage is null; return options; } - private static FileType ParseFileType(string input) + private static FileType ParseFileType(Options options, string input) { switch (input.ToLower()) { @@ -158,41 +192,51 @@ internal static class CliParser case "bench": return FileType.Bench; } - PrintWithUsage("Specified type is invalid."); + options.ParseErrorMessage ??= "Specified type is invalid."; return default; } - private static ulong ParseTitleId(string input) + private static ulong ParseTitleId(Options options, string input) { if (input.Length != 16) { - PrintWithUsage("Title ID must be 16 hex characters long"); + options.ParseErrorMessage ??= "Title ID must be 16 hex characters long"; + return default; } if (!ulong.TryParse(input, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong id)) { - PrintWithUsage("Could not parse title ID"); + options.ParseErrorMessage ??= "Could not parse title ID"; } return id; } - private static double ParseDouble(string input) + private static double ParseDouble(Options options, string input) { if (!double.TryParse(input, out double value)) { - PrintWithUsage($"Could not parse value \"{input}\""); + options.ParseErrorMessage ??= $"Could not parse value \"{input}\""; } return value; } - private static void PrintWithUsage(string toPrint) + private static string GetShortVersion() { - Console.WriteLine(toPrint); - Console.WriteLine(GetUsage()); - // PrintUsage(); + return $"hactoolnet {VersionInfo.Version}"; + } + + private static string GetLongVersion() + { + var sb = new StringBuilder(); + + sb.AppendLine($"hactoolnet {VersionInfo.Version}"); + //sb.AppendLine($"Commit time: {VersionInfo.CommitTime}"); + //sb.Append($"Commit hash: {VersionInfo.CommitHash}"); + + return sb.ToString(); } private static string GetUsage() @@ -210,6 +254,8 @@ internal static class CliParser sb.AppendLine(" --titlekeys Load title keys from an external file."); sb.AppendLine(" --accesslog Specify the access log file path."); sb.AppendLine(" --disablekeywarns Disables warning output when loading external keys."); + sb.AppendLine(" --version Display version information and exit."); + sb.AppendLine(" --help Display this help and exit."); sb.AppendLine("NCA options:"); sb.AppendLine(" --plaintext Specify file path for saving a decrypted copy of the NCA."); sb.AppendLine(" --ciphertext Specify file path for saving an encrypted copy of the NCA."); diff --git a/src/hactoolnet/Options.cs b/src/hactoolnet/Options.cs index 6439c816..a0ab9032 100644 --- a/src/hactoolnet/Options.cs +++ b/src/hactoolnet/Options.cs @@ -7,6 +7,8 @@ namespace hactoolnet; internal class Options { + public bool PrintHelp; + public bool PrintVersion; public bool RunCustom; public string InFile; public FileType InFileType = FileType.Nca; @@ -62,6 +64,10 @@ internal class Options public string BenchType; public double CpuFrequencyGhz; + public string ParseErrorMessage; + public bool IsParseSuccessful; + public bool ContinueRunning; + public IntegrityCheckLevel IntegrityLevel { get diff --git a/src/hactoolnet/Program.cs b/src/hactoolnet/Program.cs index 58a83ca1..9a540320 100644 --- a/src/hactoolnet/Program.cs +++ b/src/hactoolnet/Program.cs @@ -60,7 +60,9 @@ public static class Program Console.OutputEncoding = Encoding.UTF8; var ctx = new Context(); ctx.Options = CliParser.Parse(args); - if (ctx.Options == null) return false; + if (!ctx.Options.IsParseSuccessful) return false; + + if (!ctx.Options.ContinueRunning) return true; StreamWriter logWriter = null; ResultLogger resultLogger = null; diff --git a/src/hactoolnet/VersionInfo.cs b/src/hactoolnet/VersionInfo.cs new file mode 100644 index 00000000..f5fdbac5 --- /dev/null +++ b/src/hactoolnet/VersionInfo.cs @@ -0,0 +1,9 @@ +namespace hactoolnet +{ + internal static class VersionInfo + { + public static string Version => "0.17.0"; + public static string CommitTime => ""; + public static string CommitHash => ""; + } +} \ No newline at end of file diff --git a/src/hactoolnet/hactoolnet.csproj b/src/hactoolnet/hactoolnet.csproj index 3cc5eddb..d6b4f3eb 100644 --- a/src/hactoolnet/hactoolnet.csproj +++ b/src/hactoolnet/hactoolnet.csproj @@ -6,7 +6,7 @@ - 0.16.1 + 0.17.0 $(MSBuildProjectDirectory)=C:/hactoolnet/ @@ -15,6 +15,10 @@ + + + +