diff --git a/src/Desktop/Fischless.Fetch/Launch/GILauncher.cs b/src/Desktop/Fischless.Fetch/Launch/GILauncher.cs index 453aa70..8f4b7ef 100644 --- a/src/Desktop/Fischless.Fetch/Launch/GILauncher.cs +++ b/src/Desktop/Fischless.Fetch/Launch/GILauncher.cs @@ -183,8 +183,8 @@ public static async Task LaunchAsync(int? delayMs = null, GIRelaunchMethod relau try { await new GameFpsUnlocker(gameProcess) - .SetTargetFps(launchParameter.Fps.Value) - .UnlockAsync(new UnlockTimingOptions(100, 20000, 3000)); + .SetTargetFps((int)launchParameter.Fps.Value) + .UnlockAsync(GameFpsUnlockerOption.Default.Value); } catch { diff --git a/src/Desktop/Fischless.Fetch/Unlocker/FindModuleResult.cs b/src/Desktop/Fischless.Fetch/Unlocker/FindModuleResult.cs deleted file mode 100644 index 3093b5c..0000000 --- a/src/Desktop/Fischless.Fetch/Unlocker/FindModuleResult.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Fischless.Fetch.Unlocker; - -internal enum FindModuleResult -{ - Ok, - TimeLimitExeeded, - ModuleNotLoaded, - NoModuleFound, -} diff --git a/src/Desktop/Fischless.Fetch/Unlocker/GameFpsUnlocker.cs b/src/Desktop/Fischless.Fetch/Unlocker/GameFpsUnlocker.cs index 4b3db53..0095937 100644 --- a/src/Desktop/Fischless.Fetch/Unlocker/GameFpsUnlocker.cs +++ b/src/Desktop/Fischless.Fetch/Unlocker/GameFpsUnlocker.cs @@ -1,393 +1,21 @@ -using Microsoft; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using Vanara; -using Vanara.PInvoke; +using System.Diagnostics; namespace Fischless.Fetch.Unlocker; -/// -/// Credit to https://github.com/34736384/genshin-fps-unlock -/// -internal sealed class GameFpsUnlocker(Process gameProcess) : IGameFpsUnlocker +internal sealed class GameFpsUnlocker(Process gameProcess) { private readonly Process gameProcess = gameProcess; - private uint targetFps; - private readonly UnlockerStatus status = new(); + private int targetFps; - public GameFpsUnlocker SetTargetFps(uint targetFps) + public GameFpsUnlocker SetTargetFps(int targetFps) { this.targetFps = targetFps; return this; } - /// - public async ValueTask UnlockAsync(UnlockTimingOptions options, IProgress? progress = null, CancellationToken token = default) + public async Task UnlockAsync(GameFpsUnlockerOption options, CancellationTokenSource cts = null!) { - Verify.Operation(status.IsUnlockerValid, "This Unlocker is invalid"); - - (FindModuleResult result, GameModule moduleEntryInfo) = await FindModuleAsync(options.FindModuleDelay, options.FindModuleLimit).ConfigureAwait(false); - Verify.Operation(result != FindModuleResult.TimeLimitExeeded, "Error finding required modules: timeout; please retry"); - Verify.Operation(result != FindModuleResult.NoModuleFound, "Error finding required modules: could not read any module, the protection driver may have been loaded; please retry"); - - // Read UnityPlayer.dll - UnsafeFindFpsAddress(moduleEntryInfo); - progress?.Report(status); - - // When player switch between scenes, we have to re adjust the fps - // So we keep a loop here - await LoopAdjustFpsAsync(options.AdjustFpsDelay, progress, token).ConfigureAwait(false); - } - - private static unsafe bool UnsafeReadModulesMemory(System.Diagnostics.Process process, in GameModule moduleEntryInfo, out VirtualMemory memory) - { - ref readonly Module unityPlayer = ref moduleEntryInfo.UnityPlayer; - ref readonly Module userAssembly = ref moduleEntryInfo.UserAssembly; - - memory = new VirtualMemory(unityPlayer.Size + userAssembly.Size); - return Kernel32X.ReadProcessMemory(process.Handle, unityPlayer.Address, memory.AsSpan()[..(int)unityPlayer.Size], out _) - && Kernel32X.ReadProcessMemory(process.Handle, userAssembly.Address, memory.AsSpan()[(int)unityPlayer.Size..], out _); - } - - private static unsafe bool UnsafeReadProcessMemory(Process process, nuint baseAddress, out nuint value) - { - value = 0; - bool result = Kernel32X.ReadProcessMemory(process.Handle, baseAddress, ref value, out _); - Verify.Operation(result, "Error reading process modules' memory: could not read valid value in given address"); - return result; - } - - private static unsafe bool UnsafeWriteProcessMemory(Process process, nuint baseAddress, int value) - { - return Kernel32X.WriteProcessMemory(process.Handle, baseAddress, ref value, out _); - } - - private static unsafe FindModuleResult UnsafeTryFindModule(in nint hProcess, in ReadOnlySpan moduleName, out Module module) - { - HMODULE[] buffer = new HMODULE[128]; - if (!Kernel32X.K32EnumProcessModules(hProcess, buffer, out uint actualSize)) - { - Marshal.ThrowExceptionForHR(Marshal.GetLastPInvokeError()); - } - - if (actualSize == 0) - { - module = default!; - return FindModuleResult.NoModuleFound; - } - - foreach (ref readonly HMODULE hModule in buffer.AsSpan()[..(int)(actualSize / sizeof(HMODULE))]) - { - char[] baseName = new char[256]; - - if (Kernel32X.K32GetModuleBaseNameW(hProcess, hModule, baseName) == 0) - { - continue; - } - - fixed (char* lpBaseName = baseName) - { - ReadOnlySpan szModuleName = MemoryMarshal.CreateReadOnlySpanFromNullTerminated(lpBaseName); - if (!szModuleName.SequenceEqual(moduleName)) - { - continue; - } - } - - if (!Kernel32X.K32GetModuleInformation(hProcess, hModule, out Kernel32.MODULEINFO moduleInfo)) - { - continue; - } - - module = new((nuint)moduleInfo.lpBaseOfDll, moduleInfo.SizeOfImage); - return FindModuleResult.Ok; - } - - module = default; - return FindModuleResult.ModuleNotLoaded; - } - - private static int IndexOfPattern(in ReadOnlySpan memory) - { - // B9 3C 00 00 00 FF 15 - ReadOnlySpan part = [0xB9, 0x3C, 0x00, 0x00, 0x00, 0xFF, 0x15]; - return memory.IndexOf(part); - } - - private static FindModuleResult UnsafeGetGameModuleInfo(in nint hProcess, out GameModule info) - { - FindModuleResult unityPlayerResult = UnsafeTryFindModule(hProcess, "UnityPlayer.dll", out Module unityPlayer); - FindModuleResult userAssemblyResult = UnsafeTryFindModule(hProcess, "UserAssembly.dll", out Module userAssembly); - - if (unityPlayerResult == FindModuleResult.Ok && userAssemblyResult == FindModuleResult.Ok) - { - info = new(unityPlayer, userAssembly); - return FindModuleResult.Ok; - } - - if (unityPlayerResult == FindModuleResult.NoModuleFound && userAssemblyResult == FindModuleResult.NoModuleFound) - { - info = default; - return FindModuleResult.NoModuleFound; - } - - info = default; - return FindModuleResult.ModuleNotLoaded; - } - - private async ValueTask> FindModuleAsync(TimeSpan findModuleDelay, TimeSpan findModuleLimit) - { - ValueStopwatch watch = ValueStopwatch.StartNew(); - using (PeriodicTimer timer = new(findModuleDelay)) - { - while (await timer.WaitForNextTickAsync().ConfigureAwait(false)) - { - FindModuleResult result = UnsafeGetGameModuleInfo(gameProcess.Handle, out GameModule gameModule); - if (result == FindModuleResult.Ok) - { - return new(FindModuleResult.Ok, gameModule); - } - - if (result == FindModuleResult.NoModuleFound) - { - return new(FindModuleResult.NoModuleFound, default); - } - - if (watch.GetElapsedTime() > findModuleLimit) - { - break; - } - } - } - - return new(FindModuleResult.TimeLimitExeeded, default); - } - - private async ValueTask LoopAdjustFpsAsync(TimeSpan adjustFpsDelay, IProgress progress, CancellationToken token) - { - using (PeriodicTimer timer = new(adjustFpsDelay)) - { - while (await timer.WaitForNextTickAsync(token).ConfigureAwait(false)) - { - if (!gameProcess.HasExited && status.FpsAddress != 0U) - { - UnsafeWriteProcessMemory(gameProcess, status.FpsAddress, (int)targetFps); - progress?.Report(status); - } - else - { - status.IsUnlockerValid = false; - status.FpsAddress = 0; - progress?.Report(status); - return; - } - } - } - } - - private unsafe void UnsafeFindFpsAddress(in GameModule moduleEntryInfo) - { - bool readOk = UnsafeReadModulesMemory(gameProcess, moduleEntryInfo, out VirtualMemory localMemory); - Verify.Operation(readOk, "Error reading required modules' memory: could not copy module memory to destination"); - - using (localMemory) - { - int offset = IndexOfPattern(localMemory.AsSpan()[(int)moduleEntryInfo.UnityPlayer.Size..]); - Must.Range(offset >= 0, "Error matching memory pattern: no expected content"); - - byte* pLocalMemory = (byte*)localMemory.Pointer; - ref readonly Module unityPlayer = ref moduleEntryInfo.UnityPlayer; - ref readonly Module userAssembly = ref moduleEntryInfo.UserAssembly; - - nuint localMemoryUnityPlayerAddress = (nuint)pLocalMemory; - nuint localMemoryUserAssemblyAddress = localMemoryUnityPlayerAddress + unityPlayer.Size; - - nuint rip = localMemoryUserAssemblyAddress + (uint)offset; - rip += 5U; - rip += (nuint)(*(int*)(rip + 2U) + 6); - - nuint address = userAssembly.Address + (rip - localMemoryUserAssemblyAddress); - - nuint ptr = 0; - SpinWait.SpinUntil(() => UnsafeReadProcessMemory(gameProcess, address, out ptr) && ptr != 0); - - rip = ptr - unityPlayer.Address + localMemoryUnityPlayerAddress; - - // CALL or JMP - while (*(byte*)rip == 0xE8 || *(byte*)rip == 0xE9) - { - rip += (nuint)(*(int*)(rip + 1) + 5); - } - - nuint localMemoryActualAddress = rip + *(uint*)(rip + 2) + 6; - nuint actualOffset = localMemoryActualAddress - localMemoryUnityPlayerAddress; - status.FpsAddress = unityPlayer.Address + actualOffset; - } - } - - private readonly struct GameModule - { - public readonly bool HasValue = false; - public readonly Module UnityPlayer; - public readonly Module UserAssembly; - - public GameModule(in Module unityPlayer, in Module userAssembly) - { - HasValue = true; - UnityPlayer = unityPlayer; - UserAssembly = userAssembly; - } - } - - private readonly struct Module(nuint address, uint size) - { - public readonly bool HasValue = true; - public readonly nuint Address = address; - public readonly uint Size = size; - } -} - -file static class UnmanagedMemoryExtension -{ - public static unsafe Span AsSpan(this VirtualMemory unmanagedMemory) - { - return new(unmanagedMemory.Pointer, (int)unmanagedMemory.Size); - } -} - -file static class Must -{ - [MethodImpl(MethodImplOptions.NoInlining)] - public static void Range([DoesNotReturnIf(false)] bool condition, string? message, [CallerArgumentExpression(nameof(condition))] string? parameterName = null) - { - if (!condition) - { - throw new ArgumentOutOfRangeException(parameterName, message); - } - } -} - -file static class Kernel32X -{ - [DebuggerStepThrough] - public static unsafe BOOL ReadProcessMemory(nint hProcess, nuint lpBaseAddress, Span buffer, [MaybeNull] out SizeT numberOfBytesRead) - { - fixed (byte* lpBuffer = buffer) - { - return Kernel32.ReadProcessMemory(hProcess, (nint)lpBaseAddress, (nint)lpBuffer, buffer.Length, out numberOfBytesRead); - } - } - - [DebuggerStepThrough] - public static unsafe BOOL ReadProcessMemory(nint hProcess, nuint lpBaseAddress, ref T buffer, [MaybeNull] out SizeT numberOfBytesRead) - where T : unmanaged - { - fixed (T* lpBuffer = &buffer) - { - return Kernel32.ReadProcessMemory(hProcess, (nint)lpBaseAddress, (nint)lpBuffer, sizeof(T), out numberOfBytesRead); - } - } - - [DebuggerStepThrough] - public static unsafe BOOL WriteProcessMemory(nint hProcess, nuint lpBaseAddress, ref readonly T buffer, out SizeT numberOfBytesWritten) - where T : unmanaged - { - fixed (T* lpBuffer = &buffer) - { - return Kernel32.WriteProcessMemory(hProcess, (nint)lpBaseAddress, (nint)lpBuffer, (uint)sizeof(T), out numberOfBytesWritten); - } - } - - [DllImport("KERNEL32.dll", CallingConvention = CallingConvention.Winapi, ExactSpelling = true)] - public static unsafe extern BOOL K32EnumProcessModules(HANDLE hProcess, HMODULE* lphModule, uint cb, uint* lpcbNeeded); - - [DebuggerStepThrough] - public static unsafe BOOL K32EnumProcessModules(nint hProcess, Span hModules, out uint cbNeeded) - { - fixed (HMODULE* lphModule = hModules) - { - fixed (uint* lpcbNeeded = &cbNeeded) - { - return K32EnumProcessModules(hProcess, lphModule, (uint)(hModules.Length * sizeof(HMODULE)), lpcbNeeded); - } - } - } - - internal readonly struct PWSTR - { - public readonly unsafe char* Value; - - public static unsafe implicit operator PWSTR(char* value) => *(PWSTR*)&value; - - public static unsafe implicit operator char*(PWSTR value) => *(char**)&value; - } - - [DllImport("KERNEL32.dll", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Unicode, ExactSpelling = true)] - public static extern uint K32GetModuleBaseNameW(HANDLE hProcess, [AllowNull] HMODULE hModule, PWSTR lpBaseName, uint nSize); - - [DebuggerStepThrough] - public static unsafe uint K32GetModuleBaseNameW(HANDLE hProcess, [AllowNull] HMODULE hModule, Span baseName) - { - fixed (char* lpBaseName = baseName) - { - return K32GetModuleBaseNameW(hProcess, hModule, lpBaseName, (uint)baseName.Length); - } - } - - [DllImport("KERNEL32.dll", CallingConvention = CallingConvention.Winapi, ExactSpelling = true)] - public static unsafe extern BOOL K32GetModuleInformation(HANDLE hProcess, HMODULE hModule, Kernel32.MODULEINFO* lpmodinfo, uint cb); - - [DebuggerStepThrough] - public static unsafe BOOL K32GetModuleInformation(HANDLE hProcess, HMODULE hModule, out Kernel32.MODULEINFO modinfo) - { - fixed (Kernel32.MODULEINFO* lpmodinfo = &modinfo) - { - return K32GetModuleInformation(hProcess, hModule, lpmodinfo, (uint)sizeof(Kernel32.MODULEINFO)); - } - } -} - -internal readonly struct HMODULE -{ - public readonly nint Value; -} - -internal readonly struct ValueResult(TResult isOk, TValue value) -{ - public readonly TResult IsOk = isOk; - - public readonly TValue Value = value; - - public void Deconstruct(out TResult isOk, out TValue value) - { - isOk = IsOk; - value = Value; - } -} - -internal readonly struct ValueStopwatch -{ - private readonly long startTimestamp; - - private ValueStopwatch(long startTimestamp) - { - this.startTimestamp = startTimestamp; - } - - public bool IsActive - { - get => startTimestamp != 0; - } - - public static ValueStopwatch StartNew() - { - return new(Stopwatch.GetTimestamp()); - } - - public TimeSpan GetElapsedTime() - { - return Stopwatch.GetElapsedTime(startTimestamp); + options.TargetFps = targetFps; + await Task.Run(() => GameFpsUnlockerImpl.Start(options, pid: (uint)gameProcess.Id, cts: cts)); } } diff --git a/src/Desktop/Fischless.Fetch/Unlocker/GameFpsUnlockerImpl.cs b/src/Desktop/Fischless.Fetch/Unlocker/GameFpsUnlockerImpl.cs new file mode 100644 index 0000000..88fed22 --- /dev/null +++ b/src/Desktop/Fischless.Fetch/Unlocker/GameFpsUnlockerImpl.cs @@ -0,0 +1,238 @@ +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Text; +using Vanara.PInvoke; + +namespace Fischless.Fetch.Unlocker; + +internal class GameFpsUnlockerImpl +{ + private const int STILL_ACTIVE = 0x00000103; + + private class DeferManager(List deferredActions) : IDisposable + { + private readonly List deferredActions = deferredActions; + + public DeferManager() : this([]) + { + } + + public void Defer(Action action) + { + deferredActions.Add(action); + } + + public void Dispose() + { + deferredActions.ForEach(action => action?.Invoke()); + deferredActions.Clear(); + } + } + + private static class Interop + { + public static string GetLastErrorAsString(Win32Error errorCode) + { + StringBuilder messageBuffer = new(256); + int formatResult = Kernel32.FormatMessage( + Kernel32.FormatMessageFlags.FORMAT_MESSAGE_FROM_SYSTEM | Kernel32.FormatMessageFlags.FORMAT_MESSAGE_IGNORE_INSERTS, + IntPtr.Zero, + (uint)errorCode, + 0, + messageBuffer, + (uint)messageBuffer.Capacity, + IntPtr.Zero + ); + + if (formatResult == NTStatus.STATUS_SUCCESS) + { + return $"Unknown error (Code {errorCode})"; + } + + return messageBuffer.ToString().Trim(); + } + } + + private static bool GetModule2(Kernel32.SafeHPROCESS hProcess, string moduleName, out Kernel32.MODULEENTRY32 pEntry) + { + pEntry = new Kernel32.MODULEENTRY32 { dwSize = (uint)Marshal.SizeOf() }; + HINSTANCE[] modules = new HINSTANCE[1024]; + + if (!Kernel32.EnumProcessModules(hProcess, modules, (uint)(modules.Length * Marshal.SizeOf()), out uint cbNeeded)) + { + return false; + } + + Array.Resize(ref modules, (int)(cbNeeded / IntPtr.Size)); + + foreach (HINSTANCE module in modules) + { + StringBuilder szModuleName = new(Kernel32.MAX_PATH); + + if (Kernel32.GetModuleBaseName(hProcess, module, szModuleName, (uint)szModuleName.Capacity) == 0) + { + continue; + } + + if (moduleName != szModuleName.ToString()) + { + continue; + } + + if (Kernel32.GetModuleInformation(hProcess, module, out Kernel32.MODULEINFO modInfo, (uint)Marshal.SizeOf())) + { + pEntry.modBaseAddr = modInfo.lpBaseOfDll; + pEntry.modBaseSize = modInfo.SizeOfImage; + return true; + } + } + return false; + } + + private static unsafe nint PatternScan(nint module, uint dataLength, int[] pattern) + { + byte* scanBytes = (byte*)module; + ulong s = (ulong)pattern.Length; + int[] d = pattern.Select(p => p == '?' ? -1 : p).ToArray(); + + for (ulong i = 0ul; i < dataLength - s; ++i) + { + bool found = true; + for (ulong j = 0ul; j < s; ++j) + { + if (scanBytes[i + j] != d[j] && d[j] != -1) + { + found = false; + break; + } + } + if (found) + { + return IntPtr.Add(module, (int)i); + } + } + return IntPtr.Zero; + } + + public static unsafe void Start(GameFpsUnlockerOption option, string? gamePath = null, uint? pid = null, CancellationTokenSource? cts = null) + { + if (!option.TargetFps.HasValue) + { + return; + } + + if (string.IsNullOrWhiteSpace(gamePath) && pid == null) + { + return; + } + + int targetFps = option.TargetFps.Value; + using DeferManager deferManager = new(); + + Kernel32.SafeHPROCESS hProcess = null!; + + if (pid == null) + { + hProcess = Kernel32.CreateProcess(gamePath!); + } + else + { + hProcess = Kernel32.OpenProcess(new ACCESS_MASK(Kernel32.ProcessAccess.PROCESS_ALL_ACCESS), false, pid.Value); + } + + if (hProcess.IsInvalid) + { + Debug.WriteLine($"[Unlocker] CreateProcess failed with {gamePath}"); + return; + } + else + { + deferManager.Defer(() => { using (hProcess) { } }); + } + + int foundLimit = 0; + Kernel32.MODULEENTRY32 hUnityPlayer; + Thread.Sleep(option.FindModuleDelay); + while (!GetModule2(hProcess, "UnityPlayer.dll", out hUnityPlayer)) + { + if (cts?.Token.IsCancellationRequested ?? false) + { + return; + } + + foundLimit += option.FindModuleDelay; + if (foundLimit > option.FindModuleLimit) + { + Debug.WriteLine($"[Unlocker] GetModule2 failed in {option.FindModuleLimit} ms"); + break; + } + + Thread.Sleep(option.FindModuleDelay); + } + + Debug.WriteLine($"[Unlocker] UnityPlayer: {hUnityPlayer.modBaseAddr.ToInt64()}"); + + nint up = Kernel32.VirtualAlloc(IntPtr.Zero, hUnityPlayer.modBaseSize, Kernel32.MEM_ALLOCATION_TYPE.MEM_COMMIT | Kernel32.MEM_ALLOCATION_TYPE.MEM_RESERVE, Kernel32.MEM_PROTECTION.PAGE_READWRITE); + + if (up == IntPtr.Zero) + { + Win32Error code = Kernel32.GetLastError(); + Debug.WriteLine($"[Unlocker] VirtualAlloc UP failed ({code}): {Interop.GetLastErrorAsString(code)}"); + return; + } + else + { + deferManager.Defer(() => Kernel32.VirtualFree(up, hUnityPlayer.modBaseSize, Kernel32.MEM_ALLOCATION_TYPE.MEM_COMMIT | Kernel32.MEM_ALLOCATION_TYPE.MEM_RESERVE)); + } + + if (!Kernel32.ReadProcessMemory(hProcess, hUnityPlayer.modBaseAddr, up, hUnityPlayer.modBaseSize, out _)) + { + Win32Error code = Kernel32.GetLastError(); + Debug.WriteLine($"[Unlocker] ReadProcessMemory unity failed ({code}): {Interop.GetLastErrorAsString(code)}"); + return; + } + + Debug.WriteLine("[Unlocker] Searching for pattern..."); + + nint address = PatternScan(up, hUnityPlayer.modBaseSize, [0x7F, 0x0E, 0xE8, '?', '?', '?', '?', 0x66, 0x0F, 0x6E, 0xC8]); + + if (address == IntPtr.Zero) + { + Debug.WriteLine("[Unlocker] outdated pattern"); + return; + } + + nint pfps = 0; + { + nint rip = address; + rip += 3; + rip += *(int*)(rip) + 6; + rip += *(int*)(rip) + 4; + pfps = rip - up + hUnityPlayer.modBaseAddr; + Debug.WriteLine($"[Unlocker] FPS Offset: {pfps}"); + } + + uint exitCode = STILL_ACTIVE; + while (exitCode == STILL_ACTIVE) + { + if (cts?.Token.IsCancellationRequested ?? false) + { + return; + } + + Kernel32.GetExitCodeProcess(hProcess, out exitCode); + Thread.Sleep(option.FpsDelay); + + int fps = 0; + Kernel32.ReadProcessMemory(hProcess, pfps, new IntPtr(&fps), sizeof(int), out _); + if (fps == -1) + { + continue; + } + if (fps != targetFps) + { + Kernel32.WriteProcessMemory(hProcess, pfps, new IntPtr(&targetFps), sizeof(int), out _); + } + } + } +} diff --git a/src/Desktop/Fischless.Fetch/Unlocker/IGameFpsUnlocker.cs b/src/Desktop/Fischless.Fetch/Unlocker/IGameFpsUnlocker.cs deleted file mode 100644 index e9350ec..0000000 --- a/src/Desktop/Fischless.Fetch/Unlocker/IGameFpsUnlocker.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Fischless.Fetch.Unlocker; - -internal interface IGameFpsUnlocker -{ - ValueTask UnlockAsync(UnlockTimingOptions options, IProgress progress, CancellationToken token = default); -} diff --git a/src/Desktop/Fischless.Fetch/Unlocker/LICENSE b/src/Desktop/Fischless.Fetch/Unlocker/LICENSE deleted file mode 100644 index a83b962..0000000 --- a/src/Desktop/Fischless.Fetch/Unlocker/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2022 DGP Studio - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/src/Desktop/Fischless.Fetch/Unlocker/UnlockTimingOptions.cs b/src/Desktop/Fischless.Fetch/Unlocker/UnlockTimingOptions.cs deleted file mode 100644 index 9e6584f..0000000 --- a/src/Desktop/Fischless.Fetch/Unlocker/UnlockTimingOptions.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Fischless.Fetch.Unlocker; - -internal readonly struct UnlockTimingOptions(int findModuleDelayMilliseconds, int findModuleLimitMilliseconds, int adjustFpsDelayMilliseconds) -{ - public readonly TimeSpan FindModuleDelay = TimeSpan.FromMilliseconds(findModuleDelayMilliseconds); - - public readonly TimeSpan FindModuleLimit = TimeSpan.FromMilliseconds(findModuleLimitMilliseconds); - - public readonly TimeSpan AdjustFpsDelay = TimeSpan.FromMilliseconds(adjustFpsDelayMilliseconds); -} diff --git a/src/Desktop/Fischless.Fetch/Unlocker/UnlockerOption.cs b/src/Desktop/Fischless.Fetch/Unlocker/UnlockerOption.cs new file mode 100644 index 0000000..01de9f3 --- /dev/null +++ b/src/Desktop/Fischless.Fetch/Unlocker/UnlockerOption.cs @@ -0,0 +1,14 @@ +namespace Fischless.Fetch.Unlocker; + +public sealed class GameFpsUnlockerOption +{ + public static Lazy Default { get; } = new(); + + public int? TargetFps { get; set; } = 120; + + public int FindModuleDelay { get; set; } = 100; + + public int FindModuleLimit { get; set; } = 2000; + + public int FpsDelay { get; set; } = 2000; +} diff --git a/src/Desktop/Fischless.Fetch/Unlocker/UnlockerStatus.cs b/src/Desktop/Fischless.Fetch/Unlocker/UnlockerStatus.cs deleted file mode 100644 index 949ff3e..0000000 --- a/src/Desktop/Fischless.Fetch/Unlocker/UnlockerStatus.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Fischless.Fetch.Unlocker; - -internal sealed class UnlockerStatus -{ - public string Description { get; set; } = default!; - - public FindModuleResult FindModuleState { get; set; } - - public bool IsUnlockerValid { get; set; } = true; - - public nuint FpsAddress { get; set; } -} diff --git a/src/Desktop/Fischless.Fetch/Unlocker/VirtualMemory.cs b/src/Desktop/Fischless.Fetch/Unlocker/VirtualMemory.cs deleted file mode 100644 index 9f459d2..0000000 --- a/src/Desktop/Fischless.Fetch/Unlocker/VirtualMemory.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Runtime.InteropServices; - -namespace Fischless.Fetch.Unlocker; - -internal readonly unsafe struct VirtualMemory(uint dwSize) : IDisposable -{ - private readonly uint size = dwSize; - private readonly void* pointer = NativeMemory.Alloc(dwSize); - - public uint Size { get => size; } - public void* Pointer { get => pointer; } - - public void Dispose() - { - NativeMemory.Free(pointer); - } -}