diff --git a/IPA/Arguments.cs b/IPA/Arguments.cs index f00110f9..53bb301f 100644 --- a/IPA/Arguments.cs +++ b/IPA/Arguments.cs @@ -1,22 +1,44 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; +using System.Reflection; using System.Text; using System.Threading.Tasks; -namespace IPA +namespace IPA.ArgParsing { public class Arguments { - public static Arguments Process = new Arguments(Environment.GetCommandLineArgs()); + public static Arguments CmdLine = new Arguments(Environment.GetCommandLineArgs()); private List positional = new List(); private Dictionary longFlags = new Dictionary(); private Dictionary flags = new Dictionary(); + private List flagObjects = new List(); + + private string[] toParse = null; private Arguments(string[] args) { - foreach(var arg in args) + toParse = args; + } + + public Arguments Flags(params ArgumentFlag[] toAdd) + { + foreach (var f in toAdd) AddFlag(f); + return this; + } + + public void AddFlag(ArgumentFlag toAdd) + { + if (toParse == null) throw new InvalidOperationException(); + flagObjects.Add(toAdd); + } + + public void Process() + { + foreach (var arg in toParse) { if (arg.StartsWith("--")) { // parse as a long flag @@ -27,7 +49,7 @@ namespace IPA { var spl = name.Split('='); name = spl[0]; - value = string.Join("=", spl, 1, spl.Length-1); + value = string.Join("=", spl, 1, spl.Length - 1); } longFlags.Add(name, value); @@ -73,6 +95,31 @@ namespace IPA positional.Add(arg); } } + + toParse = null; + + foreach (var flag in flagObjects) + { + foreach (var chflag in flag.shortFlags) + { + if (flag.exists = HasFlag(chflag)) + { + flag.value = GetFlagValue(chflag); + goto FoundValue; // continue to next flagObjects item + } + } + + foreach (var lflag in flag.longFlags) + { + if (flag.exists = HasLongFlag(lflag)) + { + flag.value = GetLongFlagValue(lflag); + goto FoundValue; // continue to next flagObjects item + } + } + + FoundValue:; + } } public bool HasLongFlag(string flag) @@ -95,6 +142,60 @@ namespace IPA return flags[flag]; } + public void PrintHelp() + { + const string indent = " "; + string filename = Path.GetFileName(Assembly.GetExecutingAssembly().Location); + string format = @"help: +{2}{0} [FLAGS] [ARGUMENTS] + +flags: +{1}"; + var flagsBuilder = new StringBuilder(); + foreach (var flag in flagObjects) + { + flagsBuilder.AppendFormat("{2}{0}{1}", + string.Join(", ", flag.shortFlags.Select(s => $"-{s}").Concat( flag.longFlags.Select(s => $"--{s}")) ), + Environment.NewLine, indent); + flagsBuilder.AppendFormat("{2}{2}{0}{1}", flag.DocString, Environment.NewLine, indent); + } + + Console.Write(string.Format(format, filename, flagsBuilder.ToString(), indent)); + } + public IReadOnlyList PositionalArgs => positional; } + + public class ArgumentFlag + { + internal List shortFlags = new List(); + internal List longFlags = new List(); + + internal string value = null; + internal bool exists = false; + + public ArgumentFlag(params string[] flags) + { + foreach (var part in flags) + AddPart(part); + } + + private void AddPart(string flagPart) + { + if (flagPart.StartsWith("--")) + longFlags.Add(flagPart.Substring(2)); + else if (flagPart.StartsWith("-")) + shortFlags.Add(flagPart[1]); + } + + public bool Exists => exists; + public string Value => value; + + public string DocString { get; set; } = ""; + + public static implicit operator bool(ArgumentFlag f) + { + return f.Exists; + } + } } diff --git a/IPA/Program.cs b/IPA/Program.cs index bd9893a8..14c4d5be 100644 --- a/IPA/Program.cs +++ b/IPA/Program.cs @@ -9,6 +9,7 @@ using System.Runtime.InteropServices; using System.Text; using System.Text.RegularExpressions; using System.Windows.Forms; +using IPA.ArgParsing; namespace IPA { public class Program { @@ -20,13 +21,29 @@ namespace IPA { private static Version Version => Assembly.GetEntryAssembly().GetName().Version; + public static ArgumentFlag ArgHelp = new ArgumentFlag("--help", "-h") { DocString = "prints this message" }; + public static ArgumentFlag ArgWaitFor = new ArgumentFlag("--waitfor") { DocString = "waits for the specified PID to exit" }; + public static ArgumentFlag ArgForce = new ArgumentFlag("--force", "-f") { DocString = "forces the operation to go through" }; + public static ArgumentFlag ArgRevert = new ArgumentFlag("--revert") { DocString = "reverts the IPA installation" }; + public static ArgumentFlag ArgNoWait = new ArgumentFlag("--nowait") { DocString = "doesn't wait for user input after the operation" }; + public static ArgumentFlag ArgStart = new ArgumentFlag("--start") { DocString = "uses value as arguments to start the game after the patch/unpatch" }; + public static ArgumentFlag ArgLaunch = new ArgumentFlag("--launch") { DocString = "uses positional parameters as arguments to start the game after patch/unpatch" }; + static void Main(string[] args) { + Arguments.CmdLine.Flags(ArgHelp, ArgWaitFor, ArgForce, ArgRevert, ArgNoWait, ArgStart, ArgLaunch).Process(); + + if (ArgHelp) + { + Arguments.CmdLine.PrintHelp(); + return; + } + try { - if (Arguments.Process.HasLongFlag("waitfor") && Arguments.Process.GetLongFlagValue("waitfor") != null) + if (ArgWaitFor && ArgWaitFor.Value != null) { - int pid = int.Parse(Arguments.Process.GetLongFlagValue("waitfor")); + int pid = int.Parse(ArgWaitFor.Value); try { // wait for beat saber to exit (ensures we can modify the file) @@ -41,7 +58,7 @@ namespace IPA { PatchContext context; - var argExeName = Arguments.Process.PositionalArgs.FirstOrDefault(s => s.EndsWith(".exe")); + var argExeName = Arguments.CmdLine.PositionalArgs.FirstOrDefault(s => s.EndsWith(".exe")); if (argExeName == null) { @@ -55,7 +72,7 @@ namespace IPA { context = PatchContext.Create(argExeName); } - bool isRevert = Arguments.Process.HasLongFlag("revert") || Keyboard.IsKeyDown(Keys.LMenu); + bool isRevert = ArgRevert || Keyboard.IsKeyDown(Keys.LMenu); // Sanitizing Validate(context); @@ -107,8 +124,7 @@ namespace IPA { var nativePluginFolder = Path.Combine(context.DataPathDst, "Plugins"); bool isFlat = Directory.Exists(nativePluginFolder) && Directory.GetFiles(nativePluginFolder).Any(f => f.EndsWith(".dll")); - bool force = !BackupManager.HasBackup(context) || Arguments.Process.HasFlag('f') || - Arguments.Process.HasLongFlag("force"); + bool force = !BackupManager.HasBackup(context) || ArgForce; var architecture = DetectArchitecture(context.Executable); Console.WriteLine("Architecture: {0}", architecture); @@ -202,7 +218,7 @@ namespace IPA { } - if (!Arguments.Process.HasLongFlag("nowait")) + if (!ArgNoWait) { Console.ForegroundColor = ConsoleColor.Green; Console.WriteLine("Finished!"); @@ -232,7 +248,7 @@ namespace IPA { Console.WriteLine(""); Console.WriteLine("--- Done reverting ---"); - if (!Arguments.Process.HasLongFlag("nowait") && !isNewVersion) { + if (!ArgNoWait && !isNewVersion) { Console.WriteLine("\n\n[Press any key to quit]"); Console.ReadKey(); } @@ -241,17 +257,17 @@ namespace IPA { } private static void StartIfNeedBe(PatchContext context) { - if (Arguments.Process.HasLongFlag("start") && Arguments.Process.GetLongFlagValue("start") != null) + if (ArgStart && ArgStart.Value != null) { - Process.Start(context.Executable, Arguments.Process.GetLongFlagValue("start")); + Process.Start(context.Executable, ArgStart.Value); } else { - var argList = Arguments.Process.PositionalArgs.ToList(); + var argList = Arguments.CmdLine.PositionalArgs.ToList(); argList.Remove(context.Executable); - if (Arguments.Process.HasLongFlag("launch")) + if (ArgLaunch) { Process.Start(context.Executable, Args(argList.ToArray())); } @@ -332,7 +348,7 @@ namespace IPA { static void Fail(string message) { Console.Error.Write("ERROR: " + message); - if (!Arguments.Process.HasLongFlag("nowait")) { + if (!ArgNoWait) { Console.WriteLine("\n\n[Press any key to quit]"); Console.ReadKey(); }