improve fps unlocker

This commit is contained in:
DismissedLight
2023-03-19 12:27:17 +08:00
parent 84b9a9de23
commit 9861a3df37
10 changed files with 64 additions and 29 deletions

View File

@@ -37,7 +37,7 @@ internal static class Must
{
if (!condition)
{
throw new ArgumentOutOfRangeException(message, parameterName);
throw new ArgumentOutOfRangeException(parameterName, message);
}
}

View File

@@ -13,6 +13,7 @@ namespace Snap.Hutao.Service.Game.Unlocker;
/// <summary>
/// 游戏帧率解锁器
/// Credit to https://github.com/34736384/genshin-fps-unlock
/// </summary>
[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<byte> image)
private static unsafe bool UnsafeReadModuleMemory(Process process, MODULEENTRY32 entry, out Span<byte> 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<byte> image);
bool readOk = UnsafeReadModuleMemory(gameProcess, unityPlayer, out Span<byte> 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<int>(ref image[rip + 2]);
int ofs = rip + rel + 6;
fpsAddress = (nuint)((long)unityPlayer.modBaseAddr + ofs);
fpsAddress = (nuint)(unityPlayer.modBaseAddr + ofs);
}
}
}

View File

@@ -9,7 +9,7 @@
IsExpanded="True"
mc:Ignorable="d">
<Grid Margin="16,16,16,16">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>

View File

@@ -40,7 +40,7 @@
<Grid>
<Grid Visibility="{Binding Summary, Converter={StaticResource EmptyObjectToVisibilityConverter}}">
<Rectangle
Height="48"
Height="{StaticResource AppBarThemeCompactHeight}"
VerticalAlignment="Top"
Fill="{ThemeResource CardBackgroundFillColorDefaultBrush}"
IsHitTestVisible="False"/>

View File

@@ -52,7 +52,7 @@
<Grid Visibility="{Binding IsInitialized, Converter={StaticResource BoolToVisibilityConverter}}">
<Grid Visibility="{Binding Projects.Count, Converter={StaticResource Int32ToVisibilityConverter}}">
<Rectangle
Height="48"
Height="{StaticResource AppBarThemeCompactHeight}"
VerticalAlignment="Top"
Fill="{StaticResource CardBackgroundFillColorDefaultBrush}"
IsHitTestVisible="False"/>

View File

@@ -24,7 +24,7 @@
<Grid Visibility="{Binding IsInitialized, Converter={StaticResource BoolToVisibilityConverter}}">
<Grid Visibility="{Binding Statistics, Converter={StaticResource EmptyObjectToVisibilityConverter}}">
<Rectangle
Height="48"
Height="{StaticResource AppBarThemeCompactHeight}"
VerticalAlignment="Top"
Fill="{ThemeResource CardBackgroundFillColorDefaultBrush}"
IsHitTestVisible="False"/>

View File

@@ -27,7 +27,7 @@
</Page.Resources>
<Grid>
<Rectangle
Height="48"
Height="{StaticResource AppBarThemeCompactHeight}"
VerticalAlignment="Top"
Fill="{ThemeResource CardBackgroundFillColorDefaultBrush}"
IsHitTestVisible="False"/>

View File

@@ -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";

View File

@@ -62,8 +62,8 @@ internal sealed class AuthClient
/// <returns>包含token的字典</returns>
public async Task<Response<ListWrapper<NameToken>>> 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<ListWrapper<NameToken>>? resp = await httpClient
.TryCatchGetFromJsonAsync<Response<ListWrapper<NameToken>>>(ApiEndpoints.AuthMultiToken(loginTicket, loginUid), options, logger, token)

View File

@@ -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
/// <returns>新的实例</returns>
public static unsafe MODULEENTRY32 MODULEENTRY32()
{
return new() { dwSize = (uint)sizeof(MODULEENTRY32) };
return new() { dwSize = unchecked((uint)sizeof(MODULEENTRY32)) };
}
/// <summary>
@@ -31,7 +34,7 @@ internal static class StructMarshal
/// <returns>新的实例</returns>
public static unsafe WINDOWPLACEMENT WINDOWPLACEMENT()
{
return new() { length = (uint)sizeof(WINDOWPLACEMENT) };
return new() { length = unchecked((uint)sizeof(WINDOWPLACEMENT)) };
}
/// <summary>
@@ -78,6 +81,27 @@ internal static class StructMarshal
return new(point.X, point.Y, size.Width, size.Height);
}
/// <summary>
/// 枚举快照的模块
/// </summary>
/// <param name="snapshot">快照</param>
/// <returns>模块枚举</returns>
public static IEnumerable<MODULEENTRY32> EnumerateModuleEntry32(HANDLE snapshot)
{
MODULEENTRY32 entry = MODULEENTRY32();
if (!UnsafeModule32First(snapshot, ref entry))
{
yield break;
}
do
{
yield return entry;
}
while (UnsafeModule32Next(snapshot, ref entry));
}
/// <summary>
/// 判断结构实例是否为默认结构
/// </summary>
@@ -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);
}
}
}