mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
improve fps unlocker
This commit is contained in:
@@ -37,7 +37,7 @@ internal static class Must
|
||||
{
|
||||
if (!condition)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(message, parameterName);
|
||||
throw new ArgumentOutOfRangeException(parameterName, message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@
|
||||
IsExpanded="True"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid Margin="16,16,16,16">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="auto"/>
|
||||
<RowDefinition Height="auto"/>
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
</Page.Resources>
|
||||
<Grid>
|
||||
<Rectangle
|
||||
Height="48"
|
||||
Height="{StaticResource AppBarThemeCompactHeight}"
|
||||
VerticalAlignment="Top"
|
||||
Fill="{ThemeResource CardBackgroundFillColorDefaultBrush}"
|
||||
IsHitTestVisible="False"/>
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user