├── 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 |
--------------------------------------------------------------------------------