diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Validation/Must.cs b/src/Snap.Hutao/Snap.Hutao/Core/Validation/Must.cs index 4dde9d4f..acc8021a 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/Validation/Must.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/Validation/Must.cs @@ -37,7 +37,7 @@ internal static class Must { if (!condition) { - throw new ArgumentOutOfRangeException(message, parameterName); + throw new ArgumentOutOfRangeException(parameterName, message); } } diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/Unlocker/GameFpsUnlocker.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/Unlocker/GameFpsUnlocker.cs index d60ecd8f..6172a5cf 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Game/Unlocker/GameFpsUnlocker.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/Unlocker/GameFpsUnlocker.cs @@ -13,6 +13,7 @@ namespace Snap.Hutao.Service.Game.Unlocker; /// /// 游戏帧率解锁器 +/// Credit to https://github.com/34736384/genshin-fps-unlock /// [HighQuality] internal sealed class GameFpsUnlocker : IGameFpsUnlocker @@ -58,10 +59,10 @@ internal sealed class GameFpsUnlocker : IGameFpsUnlocker await LoopAdjustFpsAsync(adjustFpsDelay).ConfigureAwait(false); } - private static unsafe bool UnsafeReadModuleMemory(Process process, MODULEENTRY32 entry, out Span image) + private static unsafe bool UnsafeReadModuleMemory(Process process, MODULEENTRY32 entry, out Span memory) { - image = new byte[entry.modBaseSize]; - fixed (byte* lpBuffer = image) + memory = new byte[entry.modBaseSize]; + fixed (byte* lpBuffer = memory) { return ReadProcessMemory(process.SafeHandle, entry.modBaseAddr, lpBuffer, entry.modBaseSize, null); } @@ -81,22 +82,15 @@ internal sealed class GameFpsUnlocker : IGameFpsUnlocker { Marshal.ThrowExceptionForHR(Marshal.GetLastPInvokeError()); - MODULEENTRY32 entry = StructMarshal.MODULEENTRY32(); - bool found = false; - - bool loop = Module32First(snapshot, &entry); - while (loop) + foreach (MODULEENTRY32 entry in StructMarshal.EnumerateModuleEntry32(snapshot)) { - if (entry.th32ProcessID == processId && entry.szModule.AsNullTerminatedReadOnlySpan() == moduleName) + if (entry.th32ProcessID == processId && entry.szModule.AsNullTerminatedReadOnlySpan().SequenceEqual(moduleName)) { - found = true; - break; + return entry; } - - loop = Module32Next(snapshot, &entry); } - return found ? entry : default; + return default; } finally { @@ -148,22 +142,22 @@ internal sealed class GameFpsUnlocker : IGameFpsUnlocker private unsafe void UnsafeTryReadModuleMemoryFindFpsAddress(MODULEENTRY32 unityPlayer) { - bool readOk = UnsafeReadModuleMemory(gameProcess, unityPlayer, out Span image); + bool readOk = UnsafeReadModuleMemory(gameProcess, unityPlayer, out Span memory); Verify.Operation(readOk, "读取内存失败"); // Find FPS offset // 7F 0F jg 0x11 // 8B 05 ? ? ? ? mov eax, dword ptr[rip+?] - int adr = image.IndexOf(new byte[] { 0x7F, 0x0F, 0x8B, 0x05 }); + int adr = memory.IndexOf(new byte[] { 0x7F, 0x0F, 0x8B, 0x05 }); - Must.Range(adr >= 0, "未匹配到FPS字节"); + Must.Range(adr >= 0, $"未匹配到FPS字节"); - fixed (byte* pSpan = image) + fixed (byte* pSpan = memory) { int rip = adr + 2; int rel = *(int*)(pSpan + rip + 2); // Unsafe.ReadUnaligned(ref image[rip + 2]); int ofs = rip + rel + 6; - fpsAddress = (nuint)((long)unityPlayer.modBaseAddr + ofs); + fpsAddress = (nuint)(unityPlayer.modBaseAddr + ofs); } } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/View/Control/LaunchGameResourceExpander.xaml b/src/Snap.Hutao/Snap.Hutao/View/Control/LaunchGameResourceExpander.xaml index e9b55b27..4947b339 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Control/LaunchGameResourceExpander.xaml +++ b/src/Snap.Hutao/Snap.Hutao/View/Control/LaunchGameResourceExpander.xaml @@ -9,7 +9,7 @@ IsExpanded="True" mc:Ignorable="d"> - + diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/AvatarPropertyPage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/AvatarPropertyPage.xaml index 99575c1f..8d2cc224 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Page/AvatarPropertyPage.xaml +++ b/src/Snap.Hutao/Snap.Hutao/View/Page/AvatarPropertyPage.xaml @@ -40,7 +40,7 @@ diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/CultivationPage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/CultivationPage.xaml index b4932573..fb5ce6fc 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Page/CultivationPage.xaml +++ b/src/Snap.Hutao/Snap.Hutao/View/Page/CultivationPage.xaml @@ -52,7 +52,7 @@ diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/GachaLogPage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/GachaLogPage.xaml index b96c7420..3d2416fe 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Page/GachaLogPage.xaml +++ b/src/Snap.Hutao/Snap.Hutao/View/Page/GachaLogPage.xaml @@ -24,7 +24,7 @@ diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/LaunchGamePage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/LaunchGamePage.xaml index c7da4086..cefdbc66 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Page/LaunchGamePage.xaml +++ b/src/Snap.Hutao/Snap.Hutao/View/Page/LaunchGamePage.xaml @@ -27,7 +27,7 @@ diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Cookie.Constant.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Cookie.Constant.cs index 920257f3..4b6f4cfa 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Cookie.Constant.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Cookie.Constant.cs @@ -11,6 +11,7 @@ namespace Snap.Hutao.Web.Hoyolab; internal sealed partial class Cookie { public const string LOGIN_TICKET = "login_ticket"; + public const string LOGIN_UID = "login_uid"; public const string ACCOUNT_ID = "account_id"; public const string COOKIE_TOKEN = "cookie_token"; diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Auth/AuthClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Auth/AuthClient.cs index ba3aaa1e..18774c57 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Auth/AuthClient.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Auth/AuthClient.cs @@ -62,8 +62,8 @@ internal sealed class AuthClient /// 包含token的字典 public async Task>> GetMultiTokenByLoginTicketAsync(Cookie cookie, CancellationToken token) { - string loginTicket = cookie["login_ticket"]; - string loginUid = cookie["login_uid"]; + string loginTicket = cookie[Cookie.LOGIN_TICKET]; + string loginUid = cookie[Cookie.LOGIN_UID]; Response>? resp = await httpClient .TryCatchGetFromJsonAsync>>(ApiEndpoints.AuthMultiToken(loginTicket, loginUid), options, logger, token) diff --git a/src/Snap.Hutao/Snap.Hutao/Win32/StructMarshal.cs b/src/Snap.Hutao/Snap.Hutao/Win32/StructMarshal.cs index fa1c2c1f..b6668902 100644 --- a/src/Snap.Hutao/Snap.Hutao/Win32/StructMarshal.cs +++ b/src/Snap.Hutao/Snap.Hutao/Win32/StructMarshal.cs @@ -4,9 +4,12 @@ using System.Buffers.Binary; using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using Windows.Graphics; +using Windows.Win32.Foundation; using Windows.Win32.System.Diagnostics.ToolHelp; using Windows.Win32.UI.WindowsAndMessaging; +using static Windows.Win32.PInvoke; namespace Snap.Hutao.Win32; @@ -22,7 +25,7 @@ internal static class StructMarshal /// 新的实例 public static unsafe MODULEENTRY32 MODULEENTRY32() { - return new() { dwSize = (uint)sizeof(MODULEENTRY32) }; + return new() { dwSize = unchecked((uint)sizeof(MODULEENTRY32)) }; } /// @@ -31,7 +34,7 @@ internal static class StructMarshal /// 新的实例 public static unsafe WINDOWPLACEMENT WINDOWPLACEMENT() { - return new() { length = (uint)sizeof(WINDOWPLACEMENT) }; + return new() { length = unchecked((uint)sizeof(WINDOWPLACEMENT)) }; } /// @@ -78,6 +81,27 @@ internal static class StructMarshal return new(point.X, point.Y, size.Width, size.Height); } + /// + /// 枚举快照的模块 + /// + /// 快照 + /// 模块枚举 + public static IEnumerable EnumerateModuleEntry32(HANDLE snapshot) + { + MODULEENTRY32 entry = MODULEENTRY32(); + + if (!UnsafeModule32First(snapshot, ref entry)) + { + yield break; + } + + do + { + yield return entry; + } + while (UnsafeModule32Next(snapshot, ref entry)); + } + /// /// 判断结构实例是否为默认结构 /// @@ -87,4 +111,20 @@ internal static class StructMarshal { return moduleEntry32.dwSize == 0; } + + private static unsafe BOOL UnsafeModule32First(HANDLE snapshot, ref MODULEENTRY32 lpme) + { + fixed (MODULEENTRY32* lpmeLocal = &lpme) + { + return Module32First(snapshot, lpmeLocal); + } + } + + private static unsafe BOOL UnsafeModule32Next(HANDLE snapshot, ref MODULEENTRY32 lpme) + { + fixed (MODULEENTRY32* lpmeLocal = &lpme) + { + return Module32Next(snapshot, lpmeLocal); + } + } } \ No newline at end of file