mirror of
https://github.com/HolographicHat/Yae.git
synced 2025-12-12 01:18:15 +08:00
Improve game path getter
This commit is contained in:
18
res/App.Designer.cs
generated
18
res/App.Designer.cs
generated
@@ -86,15 +86,6 @@ namespace YaeAchievement.res {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Detected game location: {0}.
|
||||
/// </summary>
|
||||
internal static string ConfigInitGotPath {
|
||||
get {
|
||||
return ResourceManager.GetString("ConfigInitGotPath", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to If correct, input Y; otherwise input N.
|
||||
/// </summary>
|
||||
@@ -104,6 +95,15 @@ namespace YaeAchievement.res {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to You need start genshin once before exporting..
|
||||
/// </summary>
|
||||
internal static string ConfigNeedStartGenshin {
|
||||
get {
|
||||
return ResourceManager.GetString("ConfigNeedStartGenshin", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Download: {0}.
|
||||
/// </summary>
|
||||
|
||||
@@ -67,8 +67,8 @@ Input a number (0-5): </value>
|
||||
<data name="StatusFinished" xml:space="preserve">
|
||||
<value>Reward not taken</value>
|
||||
</data>
|
||||
<data name="ConfigInitGotPath" xml:space="preserve">
|
||||
<value>Detected game location: {0}</value>
|
||||
<data name="ConfigNeedStartGenshin" xml:space="preserve">
|
||||
<value>You need start genshin once before exporting.</value>
|
||||
</data>
|
||||
<data name="ConfigInitPathConfirm" xml:space="preserve">
|
||||
<value>If correct, input Y; otherwise input N</value>
|
||||
|
||||
@@ -61,8 +61,8 @@
|
||||
<data name="StatusRewardTaken" xml:space="preserve">
|
||||
<value>已完成</value>
|
||||
</data>
|
||||
<data name="ConfigInitGotPath" xml:space="preserve">
|
||||
<value>自动读取到游戏路径: {0}</value>
|
||||
<data name="ConfigNeedStartGenshin" xml:space="preserve">
|
||||
<value>在导出前你需要先启动一次原神.</value>
|
||||
</data>
|
||||
<data name="ConfigInitPathConfirm" xml:space="preserve">
|
||||
<value>如果确认路径无误,请按 Y ;若有误或需要自行选择,请按 N </value>
|
||||
|
||||
@@ -5,15 +5,13 @@ using YaeAchievement.AppCenterSDK.Models.Serialization;
|
||||
|
||||
namespace YaeAchievement.AppCenterSDK;
|
||||
|
||||
#pragma warning disable CA1416, CA2211
|
||||
public static class AppCenter {
|
||||
|
||||
private const string LogCache = "./cache/bf18159fb833715i.miko";
|
||||
private const string AppSecret = "648b83bf-d439-49bd-97f4-e1e506bdfe39";
|
||||
private const string ApiUrl = "https://in.appcenter.ms/logs?api-version=1.0.0";
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
public static Guid? SessionID;
|
||||
public static Guid? SessionID { get; private set; }
|
||||
public static readonly string DeviceID;
|
||||
public static readonly Device DeviceInfo;
|
||||
private static List<Log> Queue;
|
||||
@@ -40,10 +38,6 @@ public static class AppCenter {
|
||||
});
|
||||
AppDomain.CurrentDomain.ProcessExit += (_, _) => {
|
||||
running = false;
|
||||
if (Queue.Count > 0) {
|
||||
Directory.CreateDirectory("cache");
|
||||
File.WriteAllText(LogCache, Queue.ToJson());
|
||||
}
|
||||
};
|
||||
LogSerializer.AddLogType(PageLog.JsonIdentifier, typeof(PageLog));
|
||||
LogSerializer.AddLogType(EventLog.JsonIdentifier, typeof(EventLog));
|
||||
@@ -51,13 +45,6 @@ public static class AppCenter {
|
||||
LogSerializer.AddLogType(ManagedErrorLog.JsonIdentifier, typeof(ManagedErrorLog));
|
||||
LogSerializer.AddLogType(StartServiceLog.JsonIdentifier, typeof(StartServiceLog));
|
||||
LogSerializer.AddLogType(StartSessionLog.JsonIdentifier, typeof(StartSessionLog));
|
||||
if (Directory.Exists("./cache") && File.Exists(LogCache)) {
|
||||
var list = File.ReadAllText(LogCache).FromJson()?.Logs;
|
||||
if (list != null) {
|
||||
Queue.AddRange(list);
|
||||
}
|
||||
File.Delete(LogCache);
|
||||
}
|
||||
}
|
||||
|
||||
// ReSharper restore InconsistentNaming
|
||||
@@ -98,9 +85,4 @@ public static class AppCenter {
|
||||
private static string ToJson(this IEnumerable<Log> queue) {
|
||||
return LogSerializer.Serialize(new LogContainer(queue));
|
||||
}
|
||||
|
||||
private static LogContainer? FromJson(this string text) {
|
||||
return LogSerializer.DeserializeLogs(text);
|
||||
}
|
||||
}
|
||||
#pragma warning restore CA1416, CA2211
|
||||
|
||||
@@ -1,42 +1,31 @@
|
||||
using Newtonsoft.Json;
|
||||
using System.Text.RegularExpressions;
|
||||
using YaeAchievement.res;
|
||||
|
||||
namespace YaeAchievement;
|
||||
|
||||
public class AppConfig {
|
||||
public static class AppConfig {
|
||||
|
||||
[JsonProperty(PropertyName = "location")]
|
||||
public string? Location { get; set; }
|
||||
|
||||
private static AppConfig? _instance;
|
||||
|
||||
private static readonly string FileName = Path.Combine(GlobalVars.AppPath, "conf.json");
|
||||
|
||||
public static string GamePath => _instance!.Location!;
|
||||
public static string GamePath { get; private set; } = null!;
|
||||
|
||||
internal static void Load() {
|
||||
if (File.Exists(FileName)) {
|
||||
var text = File.ReadAllText(FileName);
|
||||
_instance = JsonConvert.DeserializeObject<AppConfig>(text)!;
|
||||
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)) {
|
||||
throw new ApplicationException(App.ConfigNeedStartGenshin);
|
||||
}
|
||||
if (_instance?.Location == null || !Utils.CheckGamePathValid(_instance.Location)) {
|
||||
var gameInstallPath = Utils.FindGamePathFromRegistry();
|
||||
if (!string.IsNullOrEmpty(gameInstallPath)) {
|
||||
Console.WriteLine(App.ConfigInitGotPath, gameInstallPath);
|
||||
Console.WriteLine(App.ConfigInitPathConfirm);
|
||||
var key = Console.ReadKey().Key;
|
||||
gameInstallPath = key == ConsoleKey.Y ? gameInstallPath : Utils.SelectGameExecutable();
|
||||
} else {
|
||||
gameInstallPath = Utils.SelectGameExecutable();
|
||||
}
|
||||
_instance = new AppConfig {
|
||||
Location = gameInstallPath
|
||||
};
|
||||
Save();
|
||||
string latestLogPath;
|
||||
if (!File.Exists(osLogPath)) {
|
||||
latestLogPath = cnLogPath;
|
||||
} else if (!File.Exists(cnLogPath)) {
|
||||
latestLogPath = osLogPath;
|
||||
} else {
|
||||
var cnLastWriteTime = File.GetLastWriteTime(cnLogPath);
|
||||
var osLastWriteTime = File.GetLastWriteTime(cnLogPath);
|
||||
latestLogPath = cnLastWriteTime > osLastWriteTime ? cnLogPath : osLogPath;
|
||||
}
|
||||
}
|
||||
|
||||
public static void Save() {
|
||||
File.WriteAllText(FileName, JsonConvert.SerializeObject(_instance!, Formatting.Indented));
|
||||
var content = File.ReadAllText(latestLogPath);
|
||||
var matchResult = Regex.Match(content, @"(?m).:/.+(GenshinImpact_Data|YuanShen_Data)");
|
||||
GamePath = matchResult.Value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,11 +5,11 @@ using YaeAchievement.res;
|
||||
using static YaeAchievement.Utils;
|
||||
|
||||
InstallExitHook();
|
||||
InstallExceptionHook();
|
||||
|
||||
await CheckVcRuntime();
|
||||
CheckSelfIsRunning();
|
||||
TryDisableQuickEdit();
|
||||
InstallExceptionHook();
|
||||
CheckGenshinIsRunning();
|
||||
|
||||
Console.WriteLine(@"----------------------------------------------------");
|
||||
|
||||
84
src/Utils.cs
84
src/Utils.cs
@@ -8,7 +8,6 @@ using Microsoft.Win32;
|
||||
using YaeAchievement.AppCenterSDK;
|
||||
using YaeAchievement.res;
|
||||
using YaeAchievement.Win32;
|
||||
using static YaeAchievement.Win32.OpenFileFlags;
|
||||
|
||||
namespace YaeAchievement;
|
||||
|
||||
@@ -119,50 +118,6 @@ public static class Utils {
|
||||
}
|
||||
}
|
||||
|
||||
public static bool CheckGamePathValid(string? path) {
|
||||
if (path == null) return false;
|
||||
var dir = Path.GetDirectoryName(path)!;
|
||||
return !GlobalVars.CheckGamePath || File.Exists(Path.Combine(dir, "UnityPlayer.dll"));
|
||||
}
|
||||
|
||||
public static string SelectGameExecutable() {
|
||||
var fnPtr = Marshal.AllocHGlobal(32768);
|
||||
Native.RtlZeroMemory(fnPtr, 32768);
|
||||
var ofn = new OpenFileName {
|
||||
file = fnPtr,
|
||||
size = Marshal.SizeOf<OpenFileName>(),
|
||||
owner = Native.GetConsoleWindow(),
|
||||
flags = Explorer | NoNetworkButton | FileMustExist | NoChangeDir,
|
||||
title = App.SelectTitle,
|
||||
filter = $"{App.SelectFilterName} (YuanShen/GenshinImpact.exe)\0YuanShen.exe;GenshinImpact.exe\0",
|
||||
maxFile = 32768
|
||||
};
|
||||
new Thread(() => {
|
||||
var handle = Native.FindWindow("#32770", App.SelectTitle);
|
||||
while (handle == IntPtr.Zero) {
|
||||
handle = Native.FindWindow("#32770", App.SelectTitle);
|
||||
Thread.Sleep(1);
|
||||
}
|
||||
var currentThreadId = Native.GetCurrentThreadId();
|
||||
var foregroundThreadId = Native.GetWindowThreadProcessId(Native.GetForegroundWindow(), out _);
|
||||
Native.AttachThreadInput(currentThreadId, foregroundThreadId, true);
|
||||
Native.SetWindowPos(handle, new IntPtr(-1), 0, 0, 0, 0, 1 | 2);
|
||||
Native.SetForegroundWindow(handle);
|
||||
Native.AttachThreadInput(currentThreadId, foregroundThreadId, false);
|
||||
}).Start();
|
||||
if(!Native.GetOpenFileName(ofn)) {
|
||||
var err = Native.CommDlgExtendedError();
|
||||
if (err != 0) {
|
||||
throw new SystemException($"Dialog error: {err}");
|
||||
}
|
||||
Console.WriteLine(App.SelectCanceled);
|
||||
Environment.Exit(0);
|
||||
}
|
||||
var path = Marshal.PtrToStringAuto(fnPtr)!;
|
||||
Marshal.FreeHGlobal(fnPtr);
|
||||
return path;
|
||||
}
|
||||
|
||||
// ReSharper disable once UnusedMethodReturnValue.Global
|
||||
public static bool TryDisableQuickEdit() {
|
||||
var handle = Native.GetStdHandle();
|
||||
@@ -193,11 +148,16 @@ public static class Utils {
|
||||
|
||||
public static void InstallExceptionHook() {
|
||||
AppDomain.CurrentDomain.UnhandledException += (_, e) => {
|
||||
Console.WriteLine(e.ExceptionObject.ToString());
|
||||
Console.WriteLine(App.UploadError);
|
||||
AppCenter.TrackCrash((Exception) e.ExceptionObject);
|
||||
AppCenter.Upload();
|
||||
Environment.Exit(-1);
|
||||
var ex = e.ExceptionObject;
|
||||
if (ex is ApplicationException exception) {
|
||||
Console.WriteLine(exception.Message);
|
||||
} else {
|
||||
Console.WriteLine(ex.ToString());
|
||||
Console.WriteLine(App.UploadError);
|
||||
AppCenter.TrackCrash((Exception) e.ExceptionObject);
|
||||
AppCenter.Upload();
|
||||
Environment.Exit(-1);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -260,30 +220,6 @@ public static class Utils {
|
||||
}
|
||||
|
||||
#pragma warning disable CA1416
|
||||
/// <summary>
|
||||
/// 从注册表中寻找安装路径 暂时只支持国服
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static string? FindGamePathFromRegistry() {
|
||||
try {
|
||||
using var root = Registry.LocalMachine;
|
||||
using var sub = root.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\原神");
|
||||
if (sub == null) {
|
||||
return null;
|
||||
}
|
||||
var installLocation = sub.GetValue("InstallPath")?.ToString();
|
||||
if (!string.IsNullOrEmpty(installLocation)) {
|
||||
var folder = Path.Combine(installLocation, "Genshin Impact Game\\");
|
||||
var exePath = Path.Combine(folder, "YuanShen.exe");
|
||||
if (File.Exists(Path.Combine(folder, "UnityPlayer.dll")) && File.Exists(exePath)) {
|
||||
return exePath;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Console.WriteLine(e.Message);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static async Task CheckVcRuntime() {
|
||||
using var root = Registry.LocalMachine;
|
||||
|
||||
@@ -71,9 +71,6 @@ public static class Native {
|
||||
out IntPtr lpThreadId
|
||||
);
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
public static extern IntPtr GetConsoleWindow();
|
||||
|
||||
// ReSharper disable once InconsistentNaming
|
||||
private const int STD_INPUT_HANDLE = -10;
|
||||
|
||||
@@ -86,12 +83,6 @@ public static class Native {
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
public static extern bool SetConsoleMode(IntPtr handle, int ioMode);
|
||||
|
||||
[DllImport("comdlg32.dll", SetLastError = true)]
|
||||
public static extern int CommDlgExtendedError();
|
||||
|
||||
[DllImport("comdlg32.dll", SetLastError = true, CharSet = CharSet.Auto)]
|
||||
public static extern bool GetOpenFileName([In, Out] OpenFileName ofn);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
public static extern IntPtr GlobalLock(IntPtr mem);
|
||||
|
||||
@@ -113,30 +104,6 @@ public static class Native {
|
||||
[DllImport("user32.dll")]
|
||||
public static extern bool EmptyClipboard();
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
public static extern void RtlZeroMemory(IntPtr dst, ulong length);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
public static extern bool SetForegroundWindow(IntPtr hWnd);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, uint uFlags);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint pid);
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
public static extern uint GetCurrentThreadId();
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
public static extern IntPtr GetForegroundWindow();
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
public static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach);
|
||||
|
||||
[DllImport("gdi32.dll")]
|
||||
public static extern int GetDeviceCaps(IntPtr hdc, int nIndex);
|
||||
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
namespace YaeAchievement.Win32;
|
||||
|
||||
[Flags]
|
||||
public enum OpenFileFlags : uint {
|
||||
Explorer = 0x00080000,
|
||||
NoChangeDir = 0x00000008,
|
||||
FileMustExist = 0x00001000,
|
||||
NoNetworkButton = 0x00020000,
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// ReSharper disable MemberCanBePrivate.Global
|
||||
// ReSharper disable FieldCanBeMadeReadOnly.Global
|
||||
|
||||
namespace YaeAchievement.Win32;
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
|
||||
public struct OpenFileName {
|
||||
public int size;
|
||||
public IntPtr owner;
|
||||
public IntPtr instance;
|
||||
public string filter;
|
||||
public IntPtr customFilter;
|
||||
public int maxCustFilter;
|
||||
public int filterIndex;
|
||||
public IntPtr file;
|
||||
public int maxFile;
|
||||
public IntPtr fileTitle;
|
||||
public int maxFileTitle;
|
||||
public string initialDir;
|
||||
public string title;
|
||||
public OpenFileFlags flags;
|
||||
public short fileOffset;
|
||||
public short fileExtension;
|
||||
public string defExt;
|
||||
public IntPtr custData;
|
||||
public IntPtr hook;
|
||||
public string templateName;
|
||||
public IntPtr reservedPtr;
|
||||
public int reservedInt;
|
||||
public int flagsEx;
|
||||
}
|
||||
Reference in New Issue
Block a user