mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
Try to fix unlock fps
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Snap.Hutao.Core.Logging;
|
||||
using Snap.Hutao.Service;
|
||||
using System.Globalization;
|
||||
using Windows.Globalization;
|
||||
@@ -22,7 +23,7 @@ internal static class DependencyInjection
|
||||
ServiceProvider serviceProvider = new ServiceCollection()
|
||||
|
||||
// Microsoft extension
|
||||
.AddLogging(builder => builder.AddDebug())
|
||||
.AddLogging(builder => builder.AddUnconditionalDebug())
|
||||
.AddMemoryCache()
|
||||
|
||||
// Hutao extensions
|
||||
|
||||
69
src/Snap.Hutao/Snap.Hutao/Core/Logging/DebugLogger.cs
Normal file
69
src/Snap.Hutao/Snap.Hutao/Core/Logging/DebugLogger.cs
Normal file
@@ -0,0 +1,69 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Snap.Hutao.Core.Logging;
|
||||
|
||||
/// <summary>
|
||||
/// A logger that writes messages in the debug output window only when a debugger is attached.
|
||||
/// </summary>
|
||||
internal sealed class DebugLogger : ILogger
|
||||
{
|
||||
private readonly string name;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DebugLogger"/> class.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the logger.</param>
|
||||
public DebugLogger(string name)
|
||||
{
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IDisposable BeginScope<TState>(TState state)
|
||||
where TState : notnull
|
||||
{
|
||||
return NullScope.Instance;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsEnabled(LogLevel logLevel)
|
||||
{
|
||||
// If the filter is null, everything is enabled
|
||||
return logLevel != LogLevel.None;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
|
||||
{
|
||||
if (!IsEnabled(logLevel))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ArgumentNullException.ThrowIfNull(formatter);
|
||||
|
||||
string message = formatter(state, exception);
|
||||
|
||||
if (string.IsNullOrEmpty(message))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
message = $"{logLevel}: {message}";
|
||||
|
||||
if (exception != null)
|
||||
{
|
||||
message += Environment.NewLine + Environment.NewLine + exception;
|
||||
}
|
||||
|
||||
DebugWriteLine(message, name);
|
||||
}
|
||||
|
||||
private static void DebugWriteLine(string message, string name)
|
||||
{
|
||||
Debug.WriteLine(message, category: name);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
|
||||
namespace Snap.Hutao.Core.Logging;
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods for the <see cref="ILoggerFactory"/> class.
|
||||
/// </summary>
|
||||
internal static class DebugLoggerFactoryExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds a debug logger named 'Debug' to the factory.
|
||||
/// </summary>
|
||||
/// <param name="builder">The extension method argument.</param>
|
||||
/// <returns>builder</returns>
|
||||
public static ILoggingBuilder AddUnconditionalDebug(this ILoggingBuilder builder)
|
||||
{
|
||||
builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<ILoggerProvider, DebugLoggerProvider>());
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Core.Logging;
|
||||
|
||||
/// <summary>
|
||||
/// The provider for the <see cref="DebugLogger"/>.
|
||||
/// </summary>
|
||||
[ProviderAlias("Debug")]
|
||||
internal sealed class DebugLoggerProvider : ILoggerProvider
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public ILogger CreateLogger(string name)
|
||||
{
|
||||
return new DebugLogger(name);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
}
|
||||
24
src/Snap.Hutao/Snap.Hutao/Core/Logging/NullScope.cs
Normal file
24
src/Snap.Hutao/Snap.Hutao/Core/Logging/NullScope.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Core.Logging;
|
||||
|
||||
/// <summary>
|
||||
/// An empty scope without any logic
|
||||
/// </summary>
|
||||
internal sealed class NullScope : IDisposable
|
||||
{
|
||||
private NullScope()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 实例
|
||||
/// </summary>
|
||||
public static NullScope Instance { get; } = new NullScope();
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
}
|
||||
55
src/Snap.Hutao/Snap.Hutao/Core/Setting/Feature.cs
Normal file
55
src/Snap.Hutao/Snap.Hutao/Core/Setting/Feature.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
||||
namespace Snap.Hutao.Core.Setting;
|
||||
|
||||
/// <summary>
|
||||
/// 功能
|
||||
/// </summary>
|
||||
internal sealed class Feature : ObservableObject
|
||||
{
|
||||
private readonly string displayName;
|
||||
private readonly string description;
|
||||
private readonly string settingKey;
|
||||
private readonly bool defaultValue;
|
||||
|
||||
/// <summary>
|
||||
/// 构造一个新的功能
|
||||
/// </summary>
|
||||
/// <param name="displayName">显示名称</param>
|
||||
/// <param name="description">描述</param>
|
||||
/// <param name="settingKey">键</param>
|
||||
/// <param name="defaultValue">默认值</param>
|
||||
public Feature(string displayName, string description, string settingKey, bool defaultValue)
|
||||
{
|
||||
this.displayName = displayName;
|
||||
this.description = description;
|
||||
this.settingKey = settingKey;
|
||||
this.defaultValue = defaultValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 显示名称
|
||||
/// </summary>
|
||||
public string DisplayName { get => displayName; }
|
||||
|
||||
/// <summary>
|
||||
/// 描述
|
||||
/// </summary>
|
||||
public string Description { get => description; }
|
||||
|
||||
/// <summary>
|
||||
/// 值
|
||||
/// </summary>
|
||||
public bool Value
|
||||
{
|
||||
get => LocalSetting.Get(settingKey, defaultValue);
|
||||
set
|
||||
{
|
||||
LocalSetting.Set(settingKey, value);
|
||||
OnPropertyChanged(nameof(Value));
|
||||
}
|
||||
}
|
||||
}
|
||||
33
src/Snap.Hutao/Snap.Hutao/Core/Setting/FeatureOptions.cs
Normal file
33
src/Snap.Hutao/Snap.Hutao/Core/Setting/FeatureOptions.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using System.Collections;
|
||||
|
||||
namespace Snap.Hutao.Core.Setting;
|
||||
|
||||
/// <summary>
|
||||
/// 功能选项
|
||||
/// </summary>
|
||||
internal sealed class FeatureOptions : IReadOnlyCollection<Feature>
|
||||
{
|
||||
/// <summary>
|
||||
/// 启用实时便笺无感验证
|
||||
/// </summary>
|
||||
public Feature IsDailyNoteSlientVerificationEnabled { get; } = new("IsDailyNoteSlientVerificationEnabled", "启用实时便笺无感验证", "IsDailyNoteSlientVerificationEnabled", true);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int Count { get => 1; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IEnumerator<Feature> GetEnumerator()
|
||||
{
|
||||
// TODO: Use source generator
|
||||
yield return IsDailyNoteSlientVerificationEnabled;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,9 @@ Module32First
|
||||
Module32Next
|
||||
ReadProcessMemory
|
||||
SetEvent
|
||||
VirtualAlloc
|
||||
VirtualAllocEx
|
||||
VirtualFree
|
||||
VirtualFreeEx
|
||||
WaitForSingleObject
|
||||
WriteProcessMemory
|
||||
|
||||
@@ -5,6 +5,7 @@ using Snap.Hutao.Core.Diagnostics;
|
||||
using Snap.Hutao.Win32;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using Windows.Win32.Foundation;
|
||||
using Windows.Win32.System.Diagnostics.ToolHelp;
|
||||
using static Windows.Win32.PInvoke;
|
||||
@@ -14,12 +15,15 @@ namespace Snap.Hutao.Service.Game.Unlocker;
|
||||
/// <summary>
|
||||
/// 游戏帧率解锁器
|
||||
/// Credit to https://github.com/34736384/genshin-fps-unlock
|
||||
///
|
||||
/// TODO: Save memory alloc on GameModuleEntryInfo
|
||||
/// </summary>
|
||||
[HighQuality]
|
||||
internal sealed class GameFpsUnlocker : IGameFpsUnlocker
|
||||
{
|
||||
private readonly Process gameProcess;
|
||||
private readonly LaunchOptions launchOptions;
|
||||
private readonly ILogger<GameFpsUnlocker> logger;
|
||||
|
||||
private nuint fpsAddress;
|
||||
private bool isValid = true;
|
||||
@@ -37,31 +41,43 @@ internal sealed class GameFpsUnlocker : IGameFpsUnlocker
|
||||
public GameFpsUnlocker(IServiceProvider serviceProvider, Process gameProcess)
|
||||
{
|
||||
launchOptions = serviceProvider.GetRequiredService<LaunchOptions>();
|
||||
logger = serviceProvider.GetRequiredService<ILogger<GameFpsUnlocker>>();
|
||||
this.gameProcess = gameProcess;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task UnlockAsync(TimeSpan findModuleDelay, TimeSpan findModuleLimit, TimeSpan adjustFpsDelay)
|
||||
{
|
||||
logger.LogInformation("UnlockAsync called");
|
||||
Verify.Operation(isValid, "This Unlocker is invalid");
|
||||
|
||||
MODULEENTRY32 unityPlayer = await FindModuleAsync(findModuleDelay, findModuleLimit).ConfigureAwait(false);
|
||||
GameModuleEntryInfo moduleEntryInfo = await FindModuleAsync(findModuleDelay, findModuleLimit).ConfigureAwait(false);
|
||||
Must.Argument(moduleEntryInfo.HasValue, "读取游戏内存失败");
|
||||
|
||||
// Read UnityPlayer.dll
|
||||
UnsafeTryReadModuleMemoryFindFpsAddress(unityPlayer);
|
||||
UnsafeTryReadModuleMemoryFindFpsAddress(moduleEntryInfo);
|
||||
|
||||
// When player switch between scenes, we have to re adjust the fps
|
||||
// So we keep a loop here
|
||||
await LoopAdjustFpsAsync(adjustFpsDelay).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private static unsafe bool UnsafeReadModuleMemory(Process process, MODULEENTRY32 entry, out Span<byte> memory)
|
||||
private static unsafe bool UnsafeReadModulesMemory(Process process, GameModuleEntryInfo moduleEntryInfo, out VirtualMemory memory)
|
||||
{
|
||||
memory = new byte[entry.modBaseSize];
|
||||
MODULEENTRY32 unityPlayer = moduleEntryInfo.UnityPlayer;
|
||||
MODULEENTRY32 userAssembly = moduleEntryInfo.UserAssembly;
|
||||
|
||||
fixed (byte* lpBuffer = memory)
|
||||
memory = new VirtualMemory(unityPlayer.modBaseSize + userAssembly.modBaseSize);
|
||||
byte* lpBuffer = (byte*)memory.Pointer;
|
||||
return ReadProcessMemory((HANDLE)process.Handle, unityPlayer.modBaseAddr, lpBuffer, unityPlayer.modBaseSize, default)
|
||||
&& ReadProcessMemory((HANDLE)process.Handle, userAssembly.modBaseAddr, lpBuffer + unityPlayer.modBaseSize, userAssembly.modBaseSize, default);
|
||||
}
|
||||
|
||||
private static unsafe bool UnsafeReadProcessMemory(Process process, nuint offset, out nuint value)
|
||||
{
|
||||
fixed (nuint* pValue = &value)
|
||||
{
|
||||
return ReadProcessMemory((HANDLE)process.Handle, entry.modBaseAddr, lpBuffer, entry.modBaseSize, null);
|
||||
return ReadProcessMemory((HANDLE)process.Handle, (void*)offset, &pValue, unchecked((uint)sizeof(nuint)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,24 +109,62 @@ internal sealed class GameFpsUnlocker : IGameFpsUnlocker
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<MODULEENTRY32> FindModuleAsync(TimeSpan findModuleDelay, TimeSpan findModuleLimit)
|
||||
private static int FindPatternOffsetImplmentation(ReadOnlySpan<byte> memory)
|
||||
{
|
||||
ValueStopwatch watch = ValueStopwatch.StartNew();
|
||||
// E8 ?? ?? ?? ?? 85 C0 7E 07 E8 ?? ?? ?? ?? EB 05
|
||||
int second = 0;
|
||||
ReadOnlySpan<byte> secondPart = new byte[] { 0x85, 0xC0, 0x7E, 0x07, 0xE8, };
|
||||
ReadOnlySpan<byte> thirdPart = new byte[] { 0xEB, 0x05, };
|
||||
|
||||
while (true)
|
||||
while (second >= 0 && second < memory.Length)
|
||||
{
|
||||
MODULEENTRY32 module = UnsafeFindModule(gameProcess.Id, "UnityPlayer.dll"u8);
|
||||
if (!StructMarshal.IsDefault(module))
|
||||
second += memory[second..].IndexOf(secondPart);
|
||||
if (memory[second - 5].Equals(0xE8) && memory.Slice(second + 9, 2).SequenceEqual(thirdPart))
|
||||
{
|
||||
return module;
|
||||
return second - 5;
|
||||
}
|
||||
|
||||
if (watch.GetElapsedTime() > findModuleLimit)
|
||||
{
|
||||
break;
|
||||
}
|
||||
second += 5;
|
||||
}
|
||||
|
||||
await Task.Delay(findModuleDelay).ConfigureAwait(false);
|
||||
return -1;
|
||||
}
|
||||
|
||||
private unsafe GameModuleEntryInfo UnsafeGetGameModuleEntryInfo(int processId)
|
||||
{
|
||||
logger.LogInformation("UnsafeGetGameModuleEntryInfo called");
|
||||
|
||||
MODULEENTRY32 unityPlayer = UnsafeFindModule(processId, "UnityPlayer.dll"u8);
|
||||
MODULEENTRY32 userAssembly = UnsafeFindModule(processId, "UserAssembly.dll"u8);
|
||||
|
||||
if (unityPlayer.modBaseSize != 0 && userAssembly.modBaseSize != 0)
|
||||
{
|
||||
return new(unityPlayer, userAssembly);
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
private async Task<GameModuleEntryInfo> FindModuleAsync(TimeSpan findModuleDelay, TimeSpan findModuleLimit)
|
||||
{
|
||||
logger.LogInformation("FindModuleAsync called");
|
||||
|
||||
ValueStopwatch watch = ValueStopwatch.StartNew();
|
||||
using (PeriodicTimer timer = new(findModuleDelay))
|
||||
{
|
||||
while (await timer.WaitForNextTickAsync().ConfigureAwait(false))
|
||||
{
|
||||
GameModuleEntryInfo moduleInfo = UnsafeGetGameModuleEntryInfo(gameProcess.Id);
|
||||
if (moduleInfo.HasValue)
|
||||
{
|
||||
return moduleInfo;
|
||||
}
|
||||
|
||||
if (watch.GetElapsedTime() > findModuleLimit)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return default;
|
||||
@@ -118,6 +172,8 @@ internal sealed class GameFpsUnlocker : IGameFpsUnlocker
|
||||
|
||||
private async Task LoopAdjustFpsAsync(TimeSpan adjustFpsDelay)
|
||||
{
|
||||
logger.LogInformation("LoopAdjustFpsAsync called");
|
||||
|
||||
using (PeriodicTimer timer = new(adjustFpsDelay))
|
||||
{
|
||||
while (await timer.WaitForNextTickAsync().ConfigureAwait(false))
|
||||
@@ -136,24 +192,80 @@ internal sealed class GameFpsUnlocker : IGameFpsUnlocker
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe void UnsafeTryReadModuleMemoryFindFpsAddress(MODULEENTRY32 unityPlayer)
|
||||
private unsafe void UnsafeTryReadModuleMemoryFindFpsAddress(GameModuleEntryInfo moduleEntryInfo)
|
||||
{
|
||||
bool readOk = UnsafeReadModuleMemory(gameProcess, unityPlayer, out Span<byte> memory);
|
||||
logger.LogInformation("UnsafeTryReadModuleMemoryFindFpsAddress called");
|
||||
|
||||
bool readOk = UnsafeReadModulesMemory(gameProcess, moduleEntryInfo, out VirtualMemory localMemory);
|
||||
Verify.Operation(readOk, "读取内存失败");
|
||||
|
||||
// Find FPS offset
|
||||
// 7F 0F jg 0x11
|
||||
// 8B 05 ? ? ? ? mov eax, dword ptr[rip+?]
|
||||
int adr = memory.IndexOf(new byte[] { 0x7F, 0x0F, 0x8B, 0x05 });
|
||||
|
||||
Must.Range(adr >= 0, $"未匹配到FPS字节");
|
||||
|
||||
fixed (byte* pSpan = memory)
|
||||
using (localMemory)
|
||||
{
|
||||
int rip = adr + 2;
|
||||
int rel = *(int*)(pSpan + rip + 2); // Unsafe.ReadUnaligned<int>(ref image[rip + 2]);
|
||||
int ofs = rip + rel + 6;
|
||||
fpsAddress = (nuint)(unityPlayer.modBaseAddr + ofs);
|
||||
int offset = FindPatternOffsetImplmentation(localMemory.GetBuffer().Slice(unchecked((int)moduleEntryInfo.UnityPlayer.modBaseSize)));
|
||||
Must.Range(offset > 0, "未匹配到FPS字节");
|
||||
|
||||
byte* pLocalMemory = (byte*)localMemory.Pointer;
|
||||
MODULEENTRY32 unityPlayer = moduleEntryInfo.UnityPlayer;
|
||||
MODULEENTRY32 userAssembly = moduleEntryInfo.UserAssembly;
|
||||
|
||||
logger.LogInformation("Pattern: {bytes}", BitConverter.ToString(localMemory.GetBuffer().Slice((int)(offset + unityPlayer.modBaseSize), 16).ToArray())); //
|
||||
|
||||
nuint localMemoryUnityPlayerAddress = (nuint)pLocalMemory;
|
||||
nuint localMemoryUserAssemblyAddress = localMemoryUnityPlayerAddress + unityPlayer.modBaseSize;
|
||||
{
|
||||
logger.LogInformation("localMemoryUnityPlayerAddress {addr:X8}", localMemoryUnityPlayerAddress);
|
||||
logger.LogInformation("localMemoryUserAssemblyAddress {addr:X8}", localMemoryUserAssemblyAddress);
|
||||
logger.LogInformation("memory end at {addr:X8}", localMemoryUserAssemblyAddress + userAssembly.modBaseSize);
|
||||
}
|
||||
|
||||
nuint rip = localMemoryUserAssemblyAddress + (uint)offset;
|
||||
rip += *(uint*)(rip + 1) + 5;
|
||||
rip += *(uint*)(rip + 3) + 7;
|
||||
|
||||
nuint ptr = 0;
|
||||
nuint address = rip - localMemoryUserAssemblyAddress + (nuint)userAssembly.modBaseAddr;
|
||||
logger.LogInformation("UnsafeReadModuleMemory at {addr:x8}|{uaAddr:x8}", address, (nuint)userAssembly.modBaseAddr);
|
||||
while (ptr == 0)
|
||||
{
|
||||
// Critial: The pointer here is always returning 0
|
||||
// Make this a dead loop.
|
||||
if (UnsafeReadProcessMemory(gameProcess, address, out ptr))
|
||||
{
|
||||
logger.LogInformation("UnsafeReadProcessMemory succeed {addr:x8}", ptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.LogInformation("UnsafeReadProcessMemory failed");
|
||||
}
|
||||
|
||||
Thread.Sleep(100);
|
||||
}
|
||||
|
||||
logger.LogInformation("ptr {addr}", ptr);
|
||||
rip = ptr - (nuint)unityPlayer.modBaseAddr + localMemoryUnityPlayerAddress;
|
||||
|
||||
while (*(byte*)rip == 0xE8 || *(byte*)rip == 0xE9)
|
||||
{
|
||||
rip += *(uint*)(rip + 1) + 5;
|
||||
}
|
||||
|
||||
nuint localMemoryActualAddress = rip + *(uint*)(rip + 2) + 6;
|
||||
nuint actualOffset = localMemoryActualAddress - localMemoryUnityPlayerAddress;
|
||||
fpsAddress = (nuint)(unityPlayer.modBaseAddr + actualOffset);
|
||||
}
|
||||
}
|
||||
|
||||
private readonly struct GameModuleEntryInfo
|
||||
{
|
||||
public readonly bool HasValue = false;
|
||||
public readonly MODULEENTRY32 UnityPlayer;
|
||||
public readonly MODULEENTRY32 UserAssembly;
|
||||
|
||||
public GameModuleEntryInfo(MODULEENTRY32 unityPlayer, MODULEENTRY32 userAssembly)
|
||||
{
|
||||
HasValue = true;
|
||||
UnityPlayer = unityPlayer;
|
||||
UserAssembly = userAssembly;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Windows.Win32.System.Memory;
|
||||
using static Windows.Win32.PInvoke;
|
||||
|
||||
namespace Snap.Hutao.Service.Game.Unlocker;
|
||||
|
||||
/// <summary>
|
||||
/// NativeMemory.AllocZeroed wrapper
|
||||
/// </summary>
|
||||
internal readonly unsafe struct VirtualMemory : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// 缓冲区地址
|
||||
/// </summary>
|
||||
public readonly void* Pointer;
|
||||
|
||||
/// <summary>
|
||||
/// 长度
|
||||
/// </summary>
|
||||
public readonly uint Length;
|
||||
|
||||
/// <summary>
|
||||
/// 构造一个新的本地内存
|
||||
/// </summary>
|
||||
/// <param name="dwSize">长度</param>
|
||||
public unsafe VirtualMemory(uint dwSize)
|
||||
{
|
||||
Length = dwSize;
|
||||
VIRTUAL_ALLOCATION_TYPE commitAndReserve = VIRTUAL_ALLOCATION_TYPE.MEM_COMMIT | VIRTUAL_ALLOCATION_TYPE.MEM_RESERVE;
|
||||
Pointer = VirtualAlloc(default, dwSize, commitAndReserve, PAGE_PROTECTION_FLAGS.PAGE_READWRITE);
|
||||
}
|
||||
|
||||
public static unsafe implicit operator Span<byte>(VirtualMemory memory)
|
||||
{
|
||||
return memory.GetBuffer();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取缓冲区
|
||||
/// </summary>
|
||||
/// <returns>内存</returns>
|
||||
public unsafe Span<byte> GetBuffer()
|
||||
{
|
||||
return new Span<byte>(Pointer, (int)Length);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
VirtualFree(Pointer, 0, VIRTUAL_FREE_TYPE.MEM_RELEASE);
|
||||
}
|
||||
}
|
||||
@@ -253,7 +253,6 @@
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="7.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="7.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="7.0.0" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.5.22">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
|
||||
Reference in New Issue
Block a user