diff --git a/Source/GrasscutterTools/GrasscutterTools.csproj b/Source/GrasscutterTools/GrasscutterTools.csproj
index 4abf15c..85a670f 100644
--- a/Source/GrasscutterTools/GrasscutterTools.csproj
+++ b/Source/GrasscutterTools/GrasscutterTools.csproj
@@ -282,9 +282,11 @@
+
+
FormActivityEditor.cs
diff --git a/Source/GrasscutterTools/Program.cs b/Source/GrasscutterTools/Program.cs
index 3ad8e2d..25b44ed 100644
--- a/Source/GrasscutterTools/Program.cs
+++ b/Source/GrasscutterTools/Program.cs
@@ -18,11 +18,13 @@
**/
using System;
+using System.Linq;
using System.Reflection;
+using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Windows.Forms;
-
+using GrasscutterTools.OpenCommand;
using GrasscutterTools.Properties;
using GrasscutterTools.Utils;
@@ -63,8 +65,12 @@ namespace GrasscutterTools
/// 应用程序的主入口点。
///
[STAThread]
- private static void Main()
+ private static int Main(string[] args)
{
+ var result = HandleCommandLine(args);
+ if (result != -1)
+ return result;
+
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
@@ -91,8 +97,129 @@ namespace GrasscutterTools
Application.Run(new Forms.FormMain());
Logger.I(TAG, "Program end.");
+ return 0;
}
+
+ #region - 命令行参数 -
+
+ ///
+ /// 处理命令行参数并返回处理结果
+ ///
+ /// 命令行参数
+ /// 返回-1表示继续启动应用程序。返回其它值表示退出应用并将该值作为返回结果。
+ private static int HandleCommandLine(string[] args)
+ {
+ var parser = new ToggleParser(args);
+ if (parser.IsEmpty) return -1;
+ try
+ {
+ GuiRedirect.Redirect();
+
+ // 是否启动日志
+ if (parser.HasToggle("debug") || parser.HasToggle("log"))
+ Logger.IsSaveLogs = true;
+
+ if (parser.HasToggle("v") || parser.HasToggle("version"))
+ {
+ Console.WriteLine("v" + Common.AppVersion.ToString(3));
+ return 0;
+ }
+
+ if (parser.HasToggle("h") || parser.HasToggle("help") || parser.HasToggle("?"))
+ {
+ Console.WriteLine("Usages:");
+ Console.WriteLine(" GcTools.exe -help");
+ Console.WriteLine(" GcTools.exe -version");
+ Console.WriteLine(" GcTools.exe -c \"cmd arg\"");
+ Console.WriteLine(" GcTools.exe -c \"cmd1 arg\" && GcTools -c \"cmd2 arg1 arg2\"");
+ Console.WriteLine(" GcTools.exe -host http://127.0.0.1:443 -token 123456 -c \"cmd1 arg1 arg2 | cmd2 | cmd3 arg\"");
+ return 0;
+ }
+
+ // 服务器地址
+ var host = parser.GetToggleValueOrDefault("host", Settings.Default.Host);
+ // 服务器令牌
+ var token = parser.GetToggleValueOrDefault("token", Settings.Default.TokenCache);
+
+ if (Settings.Default.Host != host || Settings.Default.TokenCache != token)
+ {
+ Settings.Default.Host = host;
+ Settings.Default.TokenCache = token;
+ Settings.Default.Save();
+ }
+
+#if DEBUG
+ Logger.I(TAG, $"Host: {Settings.Default.Host} Token: {Settings.Default.TokenCache}");
+#endif
+ // UID
+ //Settings.Default.RemoteUid = decimal.Parse(parser.GetToggleValueOrDefault("uid", Settings.Default.RemoteUid.ToString()));
+
+
+ if (!string.IsNullOrEmpty(Settings.Default.Host) && !string.IsNullOrEmpty(Settings.Default.TokenCache))
+ {
+ Common.OC = new OpenCommandAPI(Settings.Default.Host, Settings.Default.TokenCache);
+ }
+
+ // 解析并执行命令
+ var cmd = parser.GetToggleValueOrDefault("c", string.Empty);
+ if (string.IsNullOrEmpty(cmd)) cmd = parser.GetToggleValueOrDefault("command", string.Empty);
+ if (!string.IsNullOrEmpty(cmd))
+ {
+ return RunCommand(cmd) ? 0 : 1;
+ }
+ }
+ catch (Exception ex)
+ {
+ Logger.E(TAG, "Parse command failed!", ex);
+ }
+ return -1;
+ }
+
+ ///
+ /// 执行命令
+ ///
+ /// GC命令,由|分割多条命令
+ /// 返回是否执行成功
+ private static bool RunCommand(string commands)
+ {
+ if (Common.OC == null || !Common.OC.CanInvoke)
+ {
+ Console.WriteLine(Resources.RequireOpenCommandTip);
+ Logger.E(TAG, Resources.RequireOpenCommandTip);
+ return false;
+ }
+
+ try
+ {
+ foreach (var cmd in commands.Split('|').Select(FormatCommand))
+ {
+ var msg = Common.OC.Invoke(cmd).Result;
+ Console.WriteLine(string.IsNullOrEmpty(msg) ? "OK" : msg);
+ }
+ return true;
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine(ex);
+ Logger.E(TAG, "RunCommand Error:", ex);
+ return false;
+ }
+ }
+
+ ///
+ /// 格式化命令
+ /// (去除收尾空白,替换换行)
+ ///
+ /// 原始输入
+ /// 格式化后可执行命令
+ private static string FormatCommand(string raw)
+ {
+ return raw.Trim().Replace("\\r", "\r").Replace("\\n", "\n");
+ }
+
+ #endregion
+
#region - 全局异常处理 -
private static void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
diff --git a/Source/GrasscutterTools/Utils/GuiRedirect.cs b/Source/GrasscutterTools/Utils/GuiRedirect.cs
new file mode 100644
index 0000000..62e4443
--- /dev/null
+++ b/Source/GrasscutterTools/Utils/GuiRedirect.cs
@@ -0,0 +1,64 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace GrasscutterTools.Utils
+{
+ ///
+ ///
+ ///
+ public class GuiRedirect
+ {
+ [DllImport("kernel32.dll", SetLastError = true)]
+ private static extern bool AttachConsole(int dwProcessId);
+ [DllImport("kernel32.dll", SetLastError = true)]
+ private static extern IntPtr GetStdHandle(StandardHandle nStdHandle);
+ [DllImport("kernel32.dll", SetLastError = true)]
+ private static extern bool SetStdHandle(StandardHandle nStdHandle, IntPtr handle);
+ [DllImport("kernel32.dll", SetLastError = true)]
+ private static extern FileType GetFileType(IntPtr handle);
+ //[DllImport("kernel32.dll", SetLastError = true)]
+ //[return: MarshalAs(UnmanagedType.Bool)]
+ //static extern bool AllocConsole();
+
+ private enum StandardHandle : uint
+ {
+ Input = unchecked((uint)-10),
+ Output = unchecked((uint)-11),
+ Error = unchecked((uint)-12)
+ }
+
+ private enum FileType : uint
+ {
+ Unknown = 0x0000,
+ Disk = 0x0001,
+ Char = 0x0002,
+ Pipe = 0x0003
+ }
+
+ private static bool IsRedirected(IntPtr handle)
+ {
+ FileType fileType = GetFileType(handle);
+
+ return (fileType == FileType.Disk) || (fileType == FileType.Pipe);
+ }
+
+ public static void Redirect()
+ {
+ if (IsRedirected(GetStdHandle(StandardHandle.Output)))
+ {
+ var initialiseOut = Console.Out;
+ }
+
+ bool errorRedirected = IsRedirected(GetStdHandle(StandardHandle.Error));
+ if (errorRedirected)
+ {
+ var initialiseError = Console.Error;
+ }
+
+ AttachConsole(-1);
+
+ if (!errorRedirected)
+ SetStdHandle(StandardHandle.Error, GetStdHandle(StandardHandle.Output));
+ }
+ }
+}
diff --git a/Source/GrasscutterTools/Utils/Logger.cs b/Source/GrasscutterTools/Utils/Logger.cs
index 9c73e51..a14d6ca 100644
--- a/Source/GrasscutterTools/Utils/Logger.cs
+++ b/Source/GrasscutterTools/Utils/Logger.cs
@@ -25,16 +25,17 @@ namespace GrasscutterTools.Utils
{
public static class Logger
{
+ public static bool IsSaveLogs = false;
+
private static readonly string LogPath = Path.Combine(Path.GetDirectoryName(Application.ExecutablePath), $"GcTools-{DateTime.Now:MMdd}.log");
private static void Write(string message)
{
-#if DEBUG
- Console.WriteLine($"{DateTime.Now:mm:ss.fff} {message}");
-#else
- // Test log
- //File.AppendAllText(LogPath, $"{DateTime.Now:mm:ss.fff} {message}{Environment.NewLine}");
-#endif
+ if (IsSaveLogs)
+ {
+ Console.WriteLine($"{DateTime.Now:mm:ss.fff} {message}");
+ File.AppendAllText(LogPath, $"{DateTime.Now:mm:ss.fff} {message}{Environment.NewLine}");
+ }
}
private static void Write(string level, string tag, string message) => Write($"<{level}:{tag}> {message}");
diff --git a/Source/GrasscutterTools/Utils/ToggleParser.cs b/Source/GrasscutterTools/Utils/ToggleParser.cs
new file mode 100644
index 0000000..e537e22
--- /dev/null
+++ b/Source/GrasscutterTools/Utils/ToggleParser.cs
@@ -0,0 +1,42 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace GrasscutterTools.Utils
+{
+ ///
+ /// Simple command line toggles parser:
+ /// - toggles are identified with (any number of) '-' prefixes
+ /// - toggle can be with or without associated value
+ /// - toggles are case-insensitive
+ ///
+ /// --toggle_without_value -toggle value
+ ///
+ ///
+ internal class ToggleParser
+ {
+ private readonly Dictionary toggles;
+
+ public ToggleParser(string[] args)
+ {
+ toggles =
+ args.Zip(args.Skip(1).Concat(new[] { string.Empty }), (first, second) => new { first, second })
+ .Where(pair => IsToggle(pair.first))
+ .ToDictionary(pair => RemovePrefix(pair.first).ToLowerInvariant(), g => IsToggle(g.second) ? string.Empty : g.second);
+ }
+
+ private static string RemovePrefix(string toggle)
+ => new string(toggle.SkipWhile(c => c == '-').ToArray());
+
+ private static bool IsToggle(string arg)
+ => arg.StartsWith("-", StringComparison.InvariantCulture);
+
+ public bool HasToggle(string toggle)
+ => toggles.ContainsKey(toggle.ToLowerInvariant());
+
+ public string GetToggleValueOrDefault(string toggle, string defaultValue)
+ => toggles.TryGetValue(toggle.ToLowerInvariant(), out var value) ? value : defaultValue;
+
+ public bool IsEmpty => toggles.Count == 0;
+ }
+}