diff --git a/YaeAchievement/YaeAchievement.csproj b/YaeAchievement/YaeAchievement.csproj index 3ad0769..b32503f 100644 --- a/YaeAchievement/YaeAchievement.csproj +++ b/YaeAchievement/YaeAchievement.csproj @@ -29,6 +29,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/YaeAchievement/src/AppConfig.cs b/YaeAchievement/src/AppConfig.cs index 4285045..c8f071f 100644 --- a/YaeAchievement/src/AppConfig.cs +++ b/YaeAchievement/src/AppConfig.cs @@ -1,5 +1,6 @@ using System.Text.RegularExpressions; using YaeAchievement.res; +using YaeAchievement.Utilities; namespace YaeAchievement; @@ -7,6 +8,8 @@ public static partial class AppConfig { public static string GamePath { get; private set; } = null!; + private static readonly string[] ProductNames = [ "原神", "Genshin Impact" ]; + internal static void Load(string argumentPath) { if (argumentPath != "auto" && File.Exists(argumentPath)) { GamePath = argumentPath; @@ -21,25 +24,16 @@ public static partial class AppConfig { } } var appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); - var cnLogPath = Path.Combine(appDataPath, @"..\LocalLow\miHoYo\原神\output_log.txt"); - var osLogPath = Path.Combine(appDataPath, @"..\LocalLow\miHoYo\Genshin Impact\output_log.txt"); - if (!File.Exists(cnLogPath) && !File.Exists(osLogPath)) { + var logPath = ProductNames + .Select(name => $"{appDataPath}/../LocalLow/miHoYo/{name}/output_log.txt") + .Where(File.Exists) + .MaxBy(File.GetLastWriteTime); + if (logPath == null) { throw new ApplicationException(App.ConfigNeedStartGenshin); } - string finalLogPath; - if (!File.Exists(osLogPath)) { - finalLogPath = cnLogPath; - } else if (!File.Exists(cnLogPath)) { - finalLogPath = osLogPath; - } else { - var cnLastWriteTime = File.GetLastWriteTime(cnLogPath); - var osLastWriteTime = File.GetLastWriteTime(osLogPath); - finalLogPath = cnLastWriteTime > osLastWriteTime ? cnLogPath : osLogPath; - } - GamePath = GetGamePathFromLogFile(finalLogPath) - ?? GetGamePathFromLogFile($"{finalLogPath}.last") + GamePath = GetGamePathFromLogFile(logPath) + ?? GetGamePathFromLogFile($"{logPath}.last") ?? throw new ApplicationException(App.ConfigNeedStartGenshin); - pathCacheFile.Write(GamePath); } private static string? GetGamePathFromLogFile(string path) { @@ -51,7 +45,7 @@ public static partial class AppConfig { var content = File.ReadAllText(copiedLogFilePath); try { File.Delete(copiedLogFilePath); - } catch (Exception) { /* ignore */} + } catch (Exception) { /* ignore */ } var matchResult = GamePathRegex().Match(content); if (!matchResult.Success) { return null; diff --git a/YaeAchievement/src/GlobalVars.cs b/YaeAchievement/src/GlobalVars.cs index ee88893..334c7a9 100644 --- a/YaeAchievement/src/GlobalVars.cs +++ b/YaeAchievement/src/GlobalVars.cs @@ -1,6 +1,7 @@ using System.Diagnostics.CodeAnalysis; using System.Reflection; using Proto; +using YaeAchievement.Utilities; namespace YaeAchievement; diff --git a/YaeAchievement/src/Program.cs b/YaeAchievement/src/Program.cs index e6bafaf..6ba255e 100644 --- a/YaeAchievement/src/Program.cs +++ b/YaeAchievement/src/Program.cs @@ -1,56 +1,76 @@ -using System.Text; +using System.Runtime.CompilerServices; +using System.Text; using System.Text.Json; -using YaeAchievement; +using Windows.Win32; +using Windows.Win32.System.Console; using YaeAchievement.Parsers; using YaeAchievement.res; using static YaeAchievement.Utils; -Console.InputEncoding = Encoding.UTF8; -Console.OutputEncoding = Encoding.UTF8; +namespace YaeAchievement; -TryDisableQuickEdit(); -InstallExitHook(); -InstallExceptionHook(); +internal static class Program { -CheckSelfIsRunning(); -CheckGenshinIsRunning(); + public static async Task Main(string[] args) { -Console.WriteLine(@"----------------------------------------------------"); -Console.WriteLine(App.AppBanner, GlobalVars.AppVersionName); -Console.WriteLine(@"https://github.com/HolographicHat/YaeAchievement"); -Console.WriteLine(@"----------------------------------------------------"); + if (!new Mutex(true, @"Global\YaeMiku~uwu").WaitOne(0, false)) { + Console.WriteLine(App.AnotherInstance); + Environment.Exit(302); + } -AppConfig.Load(args.GetOrNull(0) ?? "auto"); -Export.ExportTo = ToUIntOrNull(args.GetOrNull(1)) ?? uint.MaxValue; + InstallExitHook(); + InstallExceptionHook(); -await CheckUpdate(ToBooleanOrFalse(args.GetOrNull(2))); + CheckGenshinIsRunning(); -var historyCache = GlobalVars.AchievementDataCache; + Console.WriteLine(@"----------------------------------------------------"); + Console.WriteLine(App.AppBanner, GlobalVars.AppVersionName); + Console.WriteLine(@"https://github.com/HolographicHat/YaeAchievement"); + Console.WriteLine(@"----------------------------------------------------"); -AchievementAllDataNotify? data = null; -try { - data = AchievementAllDataNotify.ParseFrom(historyCache.Read().Content.ToByteArray()); -} catch (Exception) { /* ignored */ } + AppConfig.Load(args.GetOrNull(0) ?? "auto"); + Export.ExportTo = ToUIntOrNull(args.GetOrNull(1)) ?? uint.MaxValue; -if (historyCache.LastWriteTime.AddMinutes(60) > DateTime.UtcNow && data != null) { - Console.WriteLine(App.UsePreviousData); - if (Console.ReadLine()?.ToUpper() is "Y" or "YES") { - Export.Choose(data); - return; - } -} + await CheckUpdate(ToBooleanOrFalse(args.GetOrNull(2))); -StartAndWaitResult(AppConfig.GamePath, new Dictionary> { - { 1, AchievementAllDataNotify.OnReceive }, - { 2, PlayerStoreNotify.OnReceive }, - { 100, PlayerPropNotify.OnReceive }, -}, () => { + var historyCache = GlobalVars.AchievementDataCache; + + AchievementAllDataNotify? data = null; + try { + data = AchievementAllDataNotify.ParseFrom(historyCache.Read().Content.ToByteArray()); + } catch (Exception) { /* ignored */ } + + if (historyCache.LastWriteTime.AddMinutes(60) > DateTime.UtcNow && data != null) { + Console.WriteLine(App.UsePreviousData); + if (Console.ReadLine()?.ToUpper() is "Y" or "YES") { + Export.Choose(data); + return; + } + } + + StartAndWaitResult(AppConfig.GamePath, new Dictionary> { + { 1, AchievementAllDataNotify.OnReceive }, + { 2, PlayerStoreNotify.OnReceive }, + { 100, PlayerPropNotify.OnReceive }, + }, () => { #if DEBUG - PlayerPropNotify.OnFinish(); - File.WriteAllText("store_data.json", JsonSerializer.Serialize(PlayerStoreNotify.Instance, new JsonSerializerOptions { - WriteIndented = true, - DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull - })); + PlayerPropNotify.OnFinish(); + File.WriteAllText("store_data.json", JsonSerializer.Serialize(PlayerStoreNotify.Instance, new JsonSerializerOptions { + WriteIndented = true, + DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull + })); #endif - AchievementAllDataNotify.OnFinish(); -}); + AchievementAllDataNotify.OnFinish(); + }); + } + + [ModuleInitializer] + internal static unsafe void SetupConsole() { + var handle = Native.GetStdHandle(STD_HANDLE.STD_INPUT_HANDLE); + CONSOLE_MODE mode = default; + Native.GetConsoleMode(handle, &mode); + Native.SetConsoleMode(handle, mode & ~CONSOLE_MODE.ENABLE_QUICK_EDIT_MODE); + Console.InputEncoding = Console.OutputEncoding = Encoding.UTF8; + } + +} diff --git a/YaeAchievement/src/CacheFile.cs b/YaeAchievement/src/Utilities/CacheFile.cs similarity index 97% rename from YaeAchievement/src/CacheFile.cs rename to YaeAchievement/src/Utilities/CacheFile.cs index 9096ddb..39f8ca2 100644 --- a/YaeAchievement/src/CacheFile.cs +++ b/YaeAchievement/src/Utilities/CacheFile.cs @@ -2,7 +2,7 @@ using Google.Protobuf; using Proto; -namespace YaeAchievement; +namespace YaeAchievement.Utilities; public class CacheFile(string identifier) { diff --git a/YaeAchievement/src/Utils.cs b/YaeAchievement/src/Utils.cs index 8672ca1..ae8d35f 100644 --- a/YaeAchievement/src/Utils.cs +++ b/YaeAchievement/src/Utils.cs @@ -7,9 +7,9 @@ using System.Net.Sockets; using System.Runtime.InteropServices; using Windows.Win32; using Windows.Win32.Foundation; -using Windows.Win32.System.Console; using Proto; using YaeAchievement.res; +using YaeAchievement.Utilities; namespace YaeAchievement; @@ -68,8 +68,7 @@ public static class Utils { } public static unsafe void CopyToClipboard(string text) { - if (Native.OpenClipboard(HWND.Null)) - { + if (Native.OpenClipboard(HWND.Null)) { Native.EmptyClipboard(); var hGlobal = (HGLOBAL) Marshal.AllocHGlobal((text.Length + 1) * 2); var hPtr = (nint) Native.GlobalLock(hGlobal); @@ -78,9 +77,7 @@ public static class Utils { Native.SetClipboardData(13, new HANDLE(hPtr)); Marshal.FreeHGlobal(hGlobal); Native.CloseClipboard(); - } - else - { + } else { throw new Win32Exception(); } } @@ -116,20 +113,6 @@ public static class Utils { _updateInfo = info; } - public static void CheckSelfIsRunning() { - try { - Process.EnterDebugMode(); - var cur = Process.GetCurrentProcess(); - foreach (var process in Process.GetProcesses().Where(process => process.Id != cur.Id)) { - if (process.ProcessName == cur.ProcessName) { - Console.WriteLine(App.AnotherInstance); - Environment.Exit(302); - } - } - Process.LeaveDebugMode(); - } catch (Win32Exception) {} - } - // ReSharper disable once UnusedMethodReturnValue.Global public static bool ShellOpen(string path, string? args = null) { try { @@ -148,13 +131,6 @@ public static class Utils { } } - // ReSharper disable once UnusedMethodReturnValue.Global - public static unsafe bool TryDisableQuickEdit() { - var handle = Native.GetStdHandle(STD_HANDLE.STD_INPUT_HANDLE); - CONSOLE_MODE mode = default; - return Native.GetConsoleMode(handle, &mode) && Native.SetConsoleMode(handle, mode & ~CONSOLE_MODE.ENABLE_QUICK_EDIT_MODE); - } - public static void CheckGenshinIsRunning() { Process.EnterDebugMode(); foreach (var process in Process.GetProcesses()) {