├── exe ├── .gitignore ├── App.config ├── FromStutterFix.sln ├── Properties │ └── AssemblyInfo.cs ├── FromStutterFix.csproj └── Program.cs ├── dll ├── exports.def ├── .gitignore ├── targetver.h ├── LICENSE ├── StutterFixAndNoLogo.vcxproj.filters ├── StutterFixAndNoLogo.sln ├── dllmain.cpp └── StutterFixAndNoLogo.vcxproj ├── DINPUT8.DLL └── README.md /exe/.gitignore: -------------------------------------------------------------------------------- 1 | /obj 2 | /.vs 3 | /bin 4 | -------------------------------------------------------------------------------- /dll/exports.def: -------------------------------------------------------------------------------- 1 | LIBRARY 2 | EXPORTS 3 | DirectInput8Create -------------------------------------------------------------------------------- /dll/.gitignore: -------------------------------------------------------------------------------- 1 | /.vs 2 | /StutterFixAndNoLogo.vcxproj.user 3 | /x64 4 | -------------------------------------------------------------------------------- /DINPUT8.DLL: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kh0nsu/FromStutterFix/HEAD/DINPUT8.DLL -------------------------------------------------------------------------------- /exe/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /dll/targetver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Including SDKDDKVer.h defines the highest available Windows platform. 4 | 5 | // If you wish to build your application for a previous Windows platform, include WinSDKVer.h and 6 | // set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. 7 | 8 | #include 9 | -------------------------------------------------------------------------------- /dll/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Bladecoding 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /exe/FromStutterFix.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.32602.291 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FromStutterFix", "FromStutterFix.csproj", "{5DF606E8-7636-4B34-9A7F-95C9300AC273}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {5DF606E8-7636-4B34-9A7F-95C9300AC273}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {5DF606E8-7636-4B34-9A7F-95C9300AC273}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {5DF606E8-7636-4B34-9A7F-95C9300AC273}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {5DF606E8-7636-4B34-9A7F-95C9300AC273}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {98DF9B8C-8F9A-4405-A6EC-172A3F02615F} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /dll/StutterFixAndNoLogo.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Header Files 20 | 21 | 22 | 23 | 24 | Source Files 25 | 26 | 27 | 28 | 29 | Source Files 30 | 31 | 32 | -------------------------------------------------------------------------------- /dll/StutterFixAndNoLogo.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.32602.291 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "StutterFixAndNoLogo", "StutterFixAndNoLogo.vcxproj", "{5824B2BD-0EF4-4AB3-AC2D-49AE9559B39E}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {5824B2BD-0EF4-4AB3-AC2D-49AE9559B39E}.Debug|x64.ActiveCfg = Debug|x64 17 | {5824B2BD-0EF4-4AB3-AC2D-49AE9559B39E}.Debug|x64.Build.0 = Debug|x64 18 | {5824B2BD-0EF4-4AB3-AC2D-49AE9559B39E}.Debug|x86.ActiveCfg = Debug|Win32 19 | {5824B2BD-0EF4-4AB3-AC2D-49AE9559B39E}.Debug|x86.Build.0 = Debug|Win32 20 | {5824B2BD-0EF4-4AB3-AC2D-49AE9559B39E}.Release|x64.ActiveCfg = Release|x64 21 | {5824B2BD-0EF4-4AB3-AC2D-49AE9559B39E}.Release|x64.Build.0 = Release|x64 22 | {5824B2BD-0EF4-4AB3-AC2D-49AE9559B39E}.Release|x86.ActiveCfg = Release|Win32 23 | {5824B2BD-0EF4-4AB3-AC2D-49AE9559B39E}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {487AD9EF-F808-4EC8-897A-EE8610439F42} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /exe/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("FromStutterFix")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("FromStutterFix")] 13 | [assembly: AssemblyCopyright("Copyright © 2022")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("5df606e8-7636-4b34-9a7f-95c9300ac273")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.2.0.0")] 36 | [assembly: AssemblyFileVersion("1.2.0.0")] 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FromStutterFix 2 | 3 | This tool will apply a fix for a certain type of stutter ("device stutter") in FromSoft games. You can trigger this just by plugging in a usb drive. 4 | 5 | Supported games: 6 | * DS3 7 | * Sekiro 8 | * Elden Ring 9 | 10 | All patches should work. 11 | 12 | ## Usage 13 | 14 | Place dinput8.dll in the game folder. Alternatively, mod loaders for DLL mods can be used. 15 | Note: EAC needs to be disabled for Elden Ring. 16 | 17 | Run the game. After a delay, a chime sound will be played if the fix was applied successfully. 18 | 19 | ## What about the EXE version? 20 | 21 | It's obsolete as most people were using the DLL. 22 | 23 | ## What happend to no logo/achievement fix? 24 | 25 | These were dropped in order to simplify the mod and most people just want the stutters gone. Separate mods can still be used. 26 | 27 | ## How does it work? 28 | 29 | The game gets a message from Windows ("WM_DEVICECHANGE") saying that something changed. The game then will re-scan for any controllers ("EnumDevices"), even if the device is not your controller. The game stops rendering frames until this is done, and it can take up to a second on some PCs. Steam hooks into this process too which probably doesn't help. 30 | 31 | This mod will patch the game to not do the scan. As long as the game has seen your controller already, this isn't really a problem, even if you re-plug the controller. 32 | 33 | ## Okay, but how do I know it's working? 34 | 35 | Run the game without the fix, and try plugging/unplugging a USB dongle (any kind). If windowed, make sure the game window is focused (the stutter won't happen otherwise). You should see a noticeable stutter. Now apply the fix, then repeat the test. It should no longer occur. 36 | 37 | ## This fixed my unplayable stutters that happened every 10 seconds! 38 | 39 | Cool, but something is probably still wrong with your PC. Check if anything looks weird in Device Manager, eg. if it's constantly refreshing, or any warning symbols. 40 | 41 | ## I still get stutters at certain points in the game 42 | 43 | Stutters at loading triggers probably can't be fixed, except by From, or a miracle patch to the rendering pipeline. There may be other sources of stutter also. 44 | -------------------------------------------------------------------------------- /exe/FromStutterFix.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {5DF606E8-7636-4B34-9A7F-95C9300AC273} 8 | Exe 9 | FromStutterFix 10 | FromStutterFix 11 | v4.8 12 | 512 13 | true 14 | true 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | false 26 | 27 | 28 | AnyCPU 29 | pdbonly 30 | true 31 | bin\Release\ 32 | TRACE 33 | prompt 34 | 4 35 | false 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /dll/dllmain.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | volatile bool gameIsAlive = false; //this is hacky but it's non-essential 5 | 6 | typedef HRESULT(__stdcall *DIRECTINPUT8CREATE)(HINSTANCE, DWORD, REFIID, LPVOID *, LPUNKNOWN); 7 | DIRECTINPUT8CREATE fpDirectInput8Create; 8 | extern "C" __declspec(dllexport) HRESULT __stdcall DirectInput8Create( 9 | HINSTANCE hinst, 10 | DWORD dwVersion, 11 | REFIID riidltf, 12 | LPVOID * ppvOut, 13 | LPUNKNOWN punkOuter 14 | ) 15 | { 16 | gameIsAlive = true; 17 | return fpDirectInput8Create(hinst, dwVersion, riidltf, ppvOut, punkOuter); 18 | } 19 | 20 | void SetupD8Proxy() { 21 | char syspath[320]; 22 | GetSystemDirectoryA(syspath, 320); 23 | strcat_s(syspath, "\\dinput8.dll"); 24 | auto hMod = LoadLibraryA(syspath); 25 | if (0 == hMod) 26 | { 27 | MessageBoxA(0, "Could not setup DINPUT8 proxy.", "", 0); 28 | return; 29 | } 30 | fpDirectInput8Create = (DIRECTINPUT8CREATE)GetProcAddress(hMod, "DirectInput8Create"); 31 | } 32 | 33 | BYTE aob[] = { 0x48, 0x88, 0x88, 0x88, 0x80, 0xB9, 0x88, 0x88, 0x00, 0x00, 0x00, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x00, 0x00, 0x88, 0x8B, 0x88, 0x88, 0x44, 0x24, 0x20, 0x01 }; 34 | BYTE mask[] = { 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00 }; 35 | int patternlength = 37; 36 | BYTE patch[] = { 0x48, 0x85, 0xC9, 0x90, 0x90, 0x90, 0x90 }; 37 | int patchOffset = 4; 38 | int patchLength = 7; 39 | 40 | DWORD WINAPI doPatching(LPVOID lpParam) 41 | { 42 | //we need to wait for the game to fully load/deobfuscate and scan controllers 43 | //if loaded as dinput8, we'll know this because the create hook was hit 44 | //otherwise we'll just wait up to 15 seconds 45 | //TODO: consider checking if the game window has opened instead. this will happen after loading but before the scan 46 | //alternatively, we could hook EnumDevices itself, but steam also hooks this and it seems more likely to crash the game 47 | for (int i = 0; i < 15; i++) 48 | { 49 | if (gameIsAlive) { break; } 50 | Sleep(1000); 51 | } 52 | Sleep(1500); //wait a bit longer as the scan may not have run immediately, though it's safe to patch if the scan is already underway. 53 | 54 | bool success = false; 55 | 56 | auto hModule = GetModuleHandle(NULL); 57 | if (!hModule) { return 1; } 58 | auto pDosHeader = (PIMAGE_DOS_HEADER)hModule; 59 | if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) { return 1; } 60 | auto pNtHeaders = (PIMAGE_NT_HEADERS)((BYTE*)hModule + pDosHeader->e_lfanew); 61 | if (pNtHeaders->Signature != IMAGE_NT_SIGNATURE) { return 1; } 62 | auto pSectionHeader = IMAGE_FIRST_SECTION(pNtHeaders); 63 | 64 | //int patchCount = 0; 65 | for (auto i = 0; i < pNtHeaders->FileHeader.NumberOfSections; i++, pSectionHeader++) 66 | { 67 | if (strncmp((char*)pSectionHeader->Name, ".text", IMAGE_SIZEOF_SHORT_NAME) == 0) 68 | { 69 | BYTE* sectionStart = (BYTE*)hModule + pSectionHeader->VirtualAddress; 70 | int sectionSize = (int)pSectionHeader->Misc.VirtualSize; 71 | 72 | for (int j = 0; j < sectionSize - patternlength + 1; ++j) 73 | { 74 | bool found = true; 75 | for (int k = 0; k < patternlength; ++k) 76 | { 77 | if (!mask[k] && sectionStart[j + k] != aob[k]) 78 | { 79 | found = false; 80 | break; 81 | } 82 | } 83 | if (found) 84 | { 85 | auto patchAddr = sectionStart + j + patchOffset; 86 | 87 | DWORD oldFlags; 88 | VirtualProtect(patchAddr, patchLength, PAGE_EXECUTE_READWRITE, &oldFlags); 89 | memcpy(patchAddr, patch, patchLength); 90 | VirtualProtect(patchAddr, patchLength, oldFlags, &oldFlags); 91 | 92 | success = true; 93 | 94 | //patchCount++; 95 | //if (patchCount >= 2) { break; } //AC6 can be patched in two places, but this is untested 96 | break; //for now just break on first match 97 | } 98 | } 99 | } 100 | } 101 | 102 | if (!success) 103 | { 104 | MessageBoxA(0, "FromStutterFix failed. You may be running an unsupported version.", "", 0); 105 | return 1; 106 | } 107 | 108 | PlaySound(TEXT("SystemStart"), NULL, SND_SYNC); 109 | return 0; 110 | } 111 | 112 | BOOL APIENTRY DllMain(HMODULE hModule, 113 | DWORD ul_reason_for_call, 114 | LPVOID lpReserved 115 | ) 116 | { 117 | HANDLE res = 0; 118 | switch (ul_reason_for_call) 119 | { 120 | case DLL_PROCESS_ATTACH: 121 | SetupD8Proxy(); 122 | 123 | res = CreateThread(NULL, 0, doPatching, NULL, 0, NULL); 124 | if (res == NULL) 125 | { 126 | MessageBoxA(0, "Could not start patching thread.", "", 0); 127 | } 128 | 129 | break; 130 | case DLL_THREAD_ATTACH: 131 | case DLL_THREAD_DETACH: 132 | case DLL_PROCESS_DETACH: 133 | break; 134 | } 135 | return TRUE; 136 | } 137 | -------------------------------------------------------------------------------- /dll/StutterFixAndNoLogo.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | x64 7 | 8 | 9 | Release 10 | x64 11 | 12 | 13 | 14 | {5824B2BD-0EF4-4AB3-AC2D-49AE9559B39E} 15 | Win32Proj 16 | StutterFixAndNoLogo 17 | 10.0 18 | StutterFixAndNoLogo 19 | 20 | 21 | 22 | DynamicLibrary 23 | true 24 | v143 25 | Unicode 26 | 27 | 28 | DynamicLibrary 29 | false 30 | v143 31 | true 32 | Unicode 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | true 48 | DINPUT8 49 | 50 | 51 | false 52 | DINPUT8 53 | 54 | 55 | 56 | 57 | 58 | Level3 59 | Disabled 60 | _DEBUG;_WINDOWS;_USRDLL;StutterFixAndNoLogo_EXPORTS;%(PreprocessorDefinitions) 61 | MultiThreadedDebug 62 | 63 | 64 | Windows 65 | true 66 | $(OutDir)DINPUT8.DLL 67 | winmm.lib;%(AdditionalDependencies) 68 | 69 | 70 | 71 | 72 | Level3 73 | 74 | 75 | MaxSpeed 76 | true 77 | true 78 | NDEBUG;_WINDOWS;_USRDLL;StutterFixAndNoLogo_EXPORTS;%(PreprocessorDefinitions) 79 | MultiThreaded 80 | 81 | 82 | Windows 83 | true 84 | true 85 | true 86 | $(OutDir)DINPUT8.DLL 87 | exports.def 88 | winmm.lib;%(AdditionalDependencies) 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | false 97 | 98 | 99 | false 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | -------------------------------------------------------------------------------- /exe/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.InteropServices; 4 | using System.Diagnostics; 5 | 6 | namespace FromStutterFix 7 | { 8 | class Program 9 | { 10 | [DllImport("kernel32.dll")] 11 | private static extern IntPtr OpenProcess(uint dwDesiredAcess, bool bInheritHandle, int dwProcessId); 12 | const uint PROCESS_ALL_ACCESS = 2035711; 13 | 14 | [DllImport("kernel32.dll")] 15 | private static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, int iSize, ref int lpNumberOfBytesRead); 16 | 17 | [DllImport("kernel32.dll")] 18 | private static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, int iSize, int lpNumberOfBytesWritten); 19 | 20 | [DllImport("kernel32.dll")] 21 | private static extern bool CloseHandle(IntPtr hObject); 22 | 23 | public IntPtr ReadPtr(IntPtr handle, IntPtr addr) 24 | { 25 | var array = new byte[8]; 26 | var lpNumberOfBytesRead = 1; 27 | ReadProcessMemory(handle, addr, array, 8, ref lpNumberOfBytesRead); 28 | return (IntPtr)BitConverter.ToUInt64(array, 0); 29 | } 30 | 31 | public byte ReadByte(IntPtr handle, IntPtr addr) 32 | { 33 | var array = new byte[1]; 34 | var lpNumberOfBytesRead = 1; 35 | ReadProcessMemory(handle, addr, array, 1, ref lpNumberOfBytesRead); 36 | return array[0]; 37 | } 38 | 39 | public void WriteByte(IntPtr handle, IntPtr addr, byte val) 40 | { 41 | var array = new byte[] { val }; 42 | WriteProcessMemory(handle, addr, array, array.Length, 0); 43 | } 44 | 45 | List games = new List() 46 | { 47 | new Game(){ exename = "darksoulsiii", ptrAddr = 0x49644C8, offset = 0x24b }, //1.15.1 48 | new Game(){ exename = "sekiro", ptrAddr = 0x3F42B28, offset = 0x23b }, //1.06 49 | new Game(){ exename = "eldenring", ptrAddr = 0x45075C8, offset = 0x88b }, //1.06 50 | }; 51 | 52 | public void run() 53 | { 54 | bool foundGame = false; 55 | var processes = Process.GetProcesses(); 56 | foreach (Process process in processes) 57 | { 58 | foreach (var game in games) 59 | { 60 | if (process.ProcessName.ToLower().Equals(game.exename.ToLower()) && !process.HasExited) 61 | { 62 | foundGame = true; 63 | Console.WriteLine("Game: " + game.exename); 64 | var handle = OpenProcess(PROCESS_ALL_ACCESS, false, process.Id); 65 | if (handle == IntPtr.Zero) 66 | { 67 | Console.WriteLine("Cannot open process"); 68 | break; 69 | } 70 | IntPtr baseAddr = IntPtr.Zero; 71 | foreach (var module in process.Modules) 72 | { 73 | var processModule = module as ProcessModule; 74 | var modNameLower = processModule.ModuleName.ToLower(); 75 | if (modNameLower == game.exename + ".exe") 76 | { 77 | baseAddr = processModule.BaseAddress; 78 | } 79 | } 80 | if (baseAddr == IntPtr.Zero) 81 | { 82 | Console.WriteLine("Could not find base address"); 83 | CloseHandle(handle); 84 | break; 85 | } 86 | 87 | var addr = ReadPtr(handle, baseAddr + game.ptrAddr) + game.offset; 88 | var state = ReadByte(handle, addr); 89 | if (state == 1) 90 | { 91 | Console.WriteLine("Stutter fix: Flag already set"); 92 | } 93 | else 94 | { 95 | WriteByte(handle, addr, 1); 96 | Console.WriteLine("Stutter fix: Flag set"); 97 | } 98 | 99 | if (game.exename == "eldenring") 100 | { 101 | const int trophyImpOffset = 0x4453838; //CS::CSTrophyImp, 1.06 102 | var ptr = ReadPtr(handle, baseAddr + trophyImpOffset) + 8; 103 | var ptr2 = ReadPtr(handle, ptr) + 0x4c; 104 | state = ReadByte(handle, ptr2); 105 | if (state == 0) 106 | { 107 | Console.WriteLine("Achievements already disabled"); 108 | } 109 | else 110 | { 111 | WriteByte(handle, ptr2, 0); 112 | Console.WriteLine("Achivements disabled"); 113 | } 114 | } 115 | 116 | CloseHandle(handle); 117 | } 118 | } 119 | } 120 | if (!foundGame) { Console.WriteLine("No game found"); } 121 | Console.ReadLine(); 122 | } 123 | 124 | static void Main(string[] args) 125 | { 126 | new Program().run(); 127 | } 128 | } 129 | 130 | class Game 131 | { 132 | public string exename { get; set; } 133 | //TODO: game version and/or an AOB for checking it 134 | public int ptrAddr { get; set; } 135 | //this gets a pointer to the user input manager eg. DLUserInputManagerImpl. 136 | //the instance is found by RTTI scan. the pointer to it can be found by pointerscan or just scanning for an 8-byte value. 137 | public int offset { get; set; } 138 | //offset is a bit harder to find. 139 | //example from sekiro of the target function (after unpacking with steamless; not needed for other games): 140 | /* 141 | 0000000141A31670 | 40:56 | push rsi | 142 | 0000000141A31672 | 48:83EC 30 | sub rsp,30 | 143 | 0000000141A31676 | 48:895C24 40 | mov qword ptr ss:[rsp+40],rbx | 144 | 0000000141A3167B | 48:8BF1 | mov rsi, rcx | 145 | 0000000141A3167E | 48:8B59 50 | mov rbx, qword ptr ds:[rcx+50] | 146 | 0000000141A31682 | 48:8959 58 | mov qword ptr ds:[rcx+58],rbx | 147 | 0000000141A31686 | 80B9 3B020000 00 | cmp byte ptr ds:[rcx+23B],0 | <--- the flag 148 | 0000000141A3168D | 48:8B5C24 40 | mov rbx, qword ptr ss:[rsp+40] | 149 | 0000000141A31692 | 75 21 | jne sekiro.exe.unpacked.141A316B5 | 150 | 0000000141A31694 | 48:8B49 40 | mov rcx, qword ptr ds:[rcx+40] | 151 | 0000000141A31698 | 4C:8D05 C1060000 | lea r8, qword ptr ds:[] | 152 | 0000000141A3169F | 4C:8BCE | mov r9, rsi | 153 | 0000000141A316A2 | C74424 20 01000000 | mov dword ptr ss:[rsp+20],1 | 154 | 0000000141A316AA | BA 04000000 | mov edx,4 | 155 | 0000000141A316AF | 48:8B01 | mov rax, qword ptr ds:[rcx] | 156 | 0000000141A316B2 | FF50 20 | call qword ptr ds:[rax+20] | 157 | 0000000141A316B5 | 48:83C4 30 | add rsp,30 | 158 | 0000000141A316B9 | 5E | pop rsi | 159 | 0000000141A316BA | C3 | ret | 160 | */ 161 | } 162 | } 163 | --------------------------------------------------------------------------------