mirror of
https://github.com/HolographicHat/Yae.git
synced 2025-12-06 14:42:52 +08:00
optimize GetGamePath
This commit is contained in:
@@ -21,3 +21,6 @@ WaitForSingleObject
|
||||
WriteProcessMemory
|
||||
|
||||
GetCurrentConsoleFontEx
|
||||
|
||||
OpenProcess
|
||||
GetModuleFileNameEx
|
||||
|
||||
2
YaeAchievement/res/App.Designer.cs
generated
2
YaeAchievement/res/App.Designer.cs
generated
@@ -105,7 +105,7 @@ namespace YaeAchievement.res {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to You need to login genshin impact before exporting..
|
||||
/// Looks up a localized string similar to Please launch GenshinImpact to continue..
|
||||
/// </summary>
|
||||
internal static string ConfigNeedStartGenshin {
|
||||
get {
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
<value>Reward not taken</value>
|
||||
</data>
|
||||
<data name="ConfigNeedStartGenshin" xml:space="preserve">
|
||||
<value>You need to login genshin impact before exporting.</value>
|
||||
<value>Please launch GenshinImpact to continue.</value>
|
||||
</data>
|
||||
<data name="DownloadLink" xml:space="preserve">
|
||||
<value>Download: {0}</value>
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
<value>已完成</value>
|
||||
</data>
|
||||
<data name="ConfigNeedStartGenshin" xml:space="preserve">
|
||||
<value>在导出前你需要先完成一次登入流程.</value>
|
||||
<value>请打开 原神 后继续操作</value>
|
||||
</data>
|
||||
<data name="DownloadLink" xml:space="preserve">
|
||||
<value>下载地址: {0}</value>
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using Spectre.Console;
|
||||
using YaeAchievement.Utilities;
|
||||
|
||||
@@ -13,31 +16,66 @@ public static partial class AppConfig {
|
||||
internal static void Load(string argumentPath) {
|
||||
if (argumentPath != "auto" && File.Exists(argumentPath)) {
|
||||
GamePath = argumentPath;
|
||||
return;
|
||||
} else if (TryReadGamePathFromCache(out var cachedPath)) {
|
||||
GamePath = cachedPath;
|
||||
} else if (TryReadGamePathFromUnityLog(out var loggedPath)) {
|
||||
GamePath = loggedPath;
|
||||
} else {
|
||||
GamePath = ReadGamePathFromProcess();
|
||||
}
|
||||
if (CacheFile.TryRead("genshin_impact_game_path", out var cache)) {
|
||||
var path = cache.Content.ToStringUtf8();
|
||||
if (path != null && File.Exists(path)) {
|
||||
GamePath = path;
|
||||
return;
|
||||
Span<byte> buffer = stackalloc byte[0x10000];
|
||||
using var stream = File.OpenRead(GamePath);
|
||||
if (stream.Read(buffer) == buffer.Length) {
|
||||
var hash = Convert.ToHexString(MD5.HashData(buffer));
|
||||
CacheFile.Write("genshin_impact_game_path_v2", Encoding.UTF8.GetBytes($"{GamePath}\u1145{hash}"));
|
||||
}
|
||||
SentrySdk.AddBreadcrumb(GamePath.EndsWith("YuanShen.exe") ? "CN" : "OS", "GamePath");
|
||||
return;
|
||||
static bool TryReadGamePathFromCache([NotNullWhen(true)] out string? path) {
|
||||
path = null;
|
||||
try {
|
||||
if (!CacheFile.TryRead("genshin_impact_game_path_v2", out var cacheFile)) {
|
||||
return false;
|
||||
}
|
||||
var cacheData = cacheFile.Content.ToStringUtf8().Split("\u1145");
|
||||
Span<byte> buffer = stackalloc byte[0x10000];
|
||||
using var stream = File.OpenRead(cacheData[0]);
|
||||
if (stream.Read(buffer) != buffer.Length || Convert.ToHexString(MD5.HashData(buffer)) != cacheData[1]) {
|
||||
return false;
|
||||
}
|
||||
path = cacheData[0];
|
||||
return true;
|
||||
} catch (Exception) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
var appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
|
||||
var logPath = ProductNames
|
||||
.Select(name => $"{appDataPath}/../LocalLow/miHoYo/{name}/output_log.txt")
|
||||
.Where(File.Exists)
|
||||
.MaxBy(File.GetLastWriteTime);
|
||||
if (logPath == null) {
|
||||
AnsiConsole.WriteLine(App.ConfigNeedStartGenshin);
|
||||
Environment.Exit(-1);
|
||||
static bool TryReadGamePathFromUnityLog([NotNullWhen(true)] out string? path) {
|
||||
path = null;
|
||||
try {
|
||||
var appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
|
||||
var logPath = ProductNames
|
||||
.Select(name => $"{appDataPath}/../LocalLow/miHoYo/{name}/output_log.txt")
|
||||
.Where(File.Exists)
|
||||
.MaxBy(File.GetLastWriteTime);
|
||||
if (logPath == null) {
|
||||
return false;
|
||||
}
|
||||
return (path = GetGamePathFromLogFile(logPath) ?? GetGamePathFromLogFile($"{logPath}.last")) != null;
|
||||
} catch (Exception) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
var gamePath = GetGamePathFromLogFile(logPath) ?? GetGamePathFromLogFile($"{logPath}.last");
|
||||
if (gamePath == null) {
|
||||
AnsiConsole.WriteLine(App.ConfigNeedStartGenshin);
|
||||
Environment.Exit(-1);
|
||||
static string ReadGamePathFromProcess() {
|
||||
return AnsiConsole.Status().Spinner(Spinner.Known.SimpleDotsScrolling).Start(App.ConfigNeedStartGenshin, _ => {
|
||||
Process? proc;
|
||||
while ((proc = Utils.GetGameProcess()) == null) {
|
||||
Thread.Sleep(250);
|
||||
}
|
||||
var fileName = proc.GetFileName()!;
|
||||
proc.Kill();
|
||||
return fileName;
|
||||
});
|
||||
}
|
||||
GamePath = gamePath;
|
||||
SentrySdk.AddBreadcrumb(GamePath.EndsWith("YuanShen.exe") ? "CN" : "OS", "GamePath");
|
||||
}
|
||||
|
||||
private static string? GetGamePathFromLogFile(string path) {
|
||||
|
||||
@@ -42,13 +42,16 @@ internal static class Program {
|
||||
InstallExitHook();
|
||||
InstallExceptionHook();
|
||||
|
||||
CheckGenshinIsRunning();
|
||||
if (GetGameProcess() != null) {
|
||||
AnsiConsole.WriteLine(App.GenshinIsRunning, 0);
|
||||
Environment.Exit(-1);
|
||||
}
|
||||
|
||||
await CheckUpdate(ToBooleanOrDefault(args.GetOrNull(2)));
|
||||
|
||||
AppConfig.Load(args.GetOrNull(0) ?? "auto");
|
||||
Export.ExportTo = ToIntOrDefault(args.GetOrNull(1), 114514);
|
||||
|
||||
await CheckUpdate(ToBooleanOrDefault(args.GetOrNull(2)));
|
||||
|
||||
AchievementAllDataNotify? data = null;
|
||||
try {
|
||||
if (CacheFile.TryRead("achievement_data", out var cache)) {
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace System.Collections.Generic;
|
||||
|
||||
public static class Collection {
|
||||
|
||||
public static IDictionary<TKey, TValue> RemoveValues<TKey, TValue>(
|
||||
this IDictionary<TKey, TValue> dictionary,
|
||||
params TKey[] keys
|
||||
) {
|
||||
foreach (var key in keys) {
|
||||
dictionary.Remove(key);
|
||||
}
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,14 +1,31 @@
|
||||
// ReSharper disable once CheckNamespace
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace System.Linq;
|
||||
namespace System.Collections.Generic {
|
||||
|
||||
public static class Enumerable {
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
internal static class CollectionExtensions {
|
||||
|
||||
public static IEnumerable<IGrouping<TKey, TKey>> GroupKeys<TKey, TValue>(
|
||||
this IEnumerable<Dictionary<TKey, TValue>> source,
|
||||
Func<TValue, bool> condition
|
||||
) where TKey : notnull {
|
||||
return source.SelectMany(dict => dict.Where(pair => condition(pair.Value)).Select(pair => pair.Key)).GroupBy(x => x);
|
||||
public static IDictionary<TKey, TValue> RemoveValues<TKey, TValue>(
|
||||
this IDictionary<TKey, TValue> dictionary, params TKey[] keys
|
||||
) {
|
||||
foreach (var key in keys) {
|
||||
dictionary.Remove(key);
|
||||
}
|
||||
return dictionary;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace System.Linq {
|
||||
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
internal static class EnumerableExtensions {
|
||||
|
||||
public static IEnumerable<IGrouping<TKey, TKey>> GroupKeys<TKey, TValue>(
|
||||
this IEnumerable<Dictionary<TKey, TValue>> source,
|
||||
Func<TValue, bool> condition
|
||||
) where TKey : notnull => source
|
||||
.SelectMany(dict => dict.Where(pair => condition(pair.Value)).Select(pair => pair.Key))
|
||||
.GroupBy(x => x);
|
||||
}
|
||||
|
||||
}
|
||||
24
YaeAchievement/src/Utilities/Extensions/Process.cs
Normal file
24
YaeAchievement/src/Utilities/Extensions/Process.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using System.ComponentModel;
|
||||
using Windows.Win32;
|
||||
using Windows.Win32.Foundation;
|
||||
using static Windows.Win32.System.Threading.PROCESS_ACCESS_RIGHTS;
|
||||
|
||||
// ReSharper disable CheckNamespace
|
||||
|
||||
namespace System.Diagnostics;
|
||||
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
internal static class ProcessExtensions {
|
||||
|
||||
public static unsafe string? GetFileName(this Process process) {
|
||||
using var hProc = Native.OpenProcess_SafeHandle(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, (uint) process.Id);
|
||||
if (hProc.IsInvalid) {
|
||||
return null;
|
||||
}
|
||||
var sProcPath = stackalloc char[32767];
|
||||
return Native.GetModuleFileNameEx((HANDLE) hProc.DangerousGetHandle(), HMODULE.Null, sProcPath, 32767) == 0
|
||||
? null
|
||||
: new string(sProcPath);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,10 +1,12 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Spectre.Console;
|
||||
|
||||
// ReSharper disable CheckNamespace
|
||||
|
||||
namespace Google.Protobuf;
|
||||
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
internal static class BinaryReaderExtensions {
|
||||
|
||||
public static byte[] ReadBytes(this BinaryReader reader) {
|
||||
@@ -23,6 +25,7 @@ internal static class BinaryReaderExtensions {
|
||||
|
||||
}
|
||||
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
internal static class CodedInputStreamExtensions {
|
||||
|
||||
[UnsafeAccessor(UnsafeAccessorKind.Method)]
|
||||
|
||||
@@ -163,22 +163,9 @@ public static class Utils {
|
||||
}
|
||||
}
|
||||
|
||||
internal static void CheckGenshinIsRunning() {
|
||||
// QueryProcessEvent?
|
||||
var appdata = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
|
||||
var dataPath = $"{appdata}/../LocalLow/miHoYo";
|
||||
if (!Directory.Exists(dataPath)) {
|
||||
return;
|
||||
}
|
||||
foreach (var path in Directory.EnumerateDirectories(dataPath).Where(p => File.Exists($"{p}/info.txt"))) {
|
||||
try {
|
||||
using var handle = File.OpenHandle($"{path}/output_log.txt", share: FileShare.None, mode: FileMode.OpenOrCreate);
|
||||
} catch (IOException) {
|
||||
AnsiConsole.WriteLine(App.GenshinIsRunning, 0);
|
||||
Environment.Exit(301);
|
||||
}
|
||||
}
|
||||
}
|
||||
internal static Process? GetGameProcess() => Process.GetProcessesByName("YuanShen")
|
||||
.Concat(Process.GetProcessesByName("GenshinImpact"))
|
||||
.FirstOrDefault(p => File.Exists($"{p.GetFileName()}/../HoYoKProtect.sys"));
|
||||
|
||||
private static GameProcess? _proc;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user