Skip to content

Commit b9e5a8c

Browse files
zofrea10dm
andauthored
Add pattern scan, resolve issue #6 (#8)
* Added pattern scan functionality to find the right offset, added optional logging, added option to disable forcing borderless window, commented out the code that used the manually found offset * Clean up unused files * Update src/dll/dllmain.c Co-authored-by: Aaron <57018940+0dm@users.noreply.github.com> * Update src/dll/dllmain.c Co-authored-by: Aaron <57018940+0dm@users.noreply.github.com> * Update src/dll/dllmain.c Co-authored-by: Aaron <57018940+0dm@users.noreply.github.com> * Update src/dll/dllmain.c Co-authored-by: Aaron <57018940+0dm@users.noreply.github.com> * Update src/dll/dllmain.c Co-authored-by: Aaron <57018940+0dm@users.noreply.github.com> * Update src/dll/build.bat Co-authored-by: Aaron <57018940+0dm@users.noreply.github.com> * Update src/dll/FPSconfig.ini Co-authored-by: Aaron <57018940+0dm@users.noreply.github.com> * Update src/dll/dllmain.c Co-authored-by: Aaron <57018940+0dm@users.noreply.github.com> * Update src/dll/dllmain.c Co-authored-by: Aaron <57018940+0dm@users.noreply.github.com> * Update dllmain.c * Update dllmain.c Get rid of the old hardcoded offset code that was commented out * Add a dummy atexit command otherwise the build will fail if we want to include the -nostartfiles flag during compilation --------- Co-authored-by: Aaron <57018940+0dm@users.noreply.github.com>
1 parent 3902158 commit b9e5a8c

File tree

3 files changed

+120
-28
lines changed

3 files changed

+120
-28
lines changed

src/dll/FPSconfig.ini

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
; Config file for DS3DebugFPS
22

33
[Settings]
4-
fps = 144.0 ; FPS Cap
4+
fps = 75.0 ; FPS Cap, ~82 and below resolves the slow sprinting issues that occur beyond this value
5+
EnableLogging = 1 ; Set to 1 to enable debug logging
6+
EnableBorderless = 1 ; Set to 1 to enable borderless window mode (default), 0 for normal window mode
57

68
UseCustomScreenDimensions = 0 ; Set to 1 to change how the game is stretched, by default it will stretch the game to fit screen (optimal)
79

@@ -13,3 +15,4 @@ EnableCursorClip = 0 ; Set to 1 to enable the cursor clip hotkey, used to
1315
CursorClipHotkey = 0x7A ; default: f11 (0x7A). You can still alt tab out of the window. This value is in hex.
1416
; Hotkey list can be found here: https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
1517

18+

src/dll/build.bat

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
@gcc -o D3DCompiler_43.dll asm.s dllmain.c -O3 -lmsvcrt -lntdll -shared -nostartfiles -Wno-multichar -Wl,--image-base,0x150000000,-e,DllMain
1+
@gcc -o D3DCompiler_43.dll asm.s dllmain.c -O3 -lmsvcrt -lntdll -lpsapi -nostartfiles -shared -Wno-multichar -Wl,--image-base,0x150000000,-e,DllMain
22
@pause

src/dll/dllmain.c

Lines changed: 115 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
#include "dllmain.h"
22
#include "../../external/inih/ini.c"
3+
#include <stdio.h>
4+
#include <stdarg.h>
5+
#include <psapi.h>
36

47
typedef struct {
58
float fps;
@@ -8,6 +11,8 @@ typedef struct {
811
int ScreenHeight;
912
int EnableCursorClip;
1013
int CursorClipHotkey;
14+
int EnableLogging;
15+
int EnableBorderless;
1116
} config;
1217
config configFile;
1318

@@ -25,7 +30,7 @@ static int handler(void *Settings, const char *section, const char *name, const
2530
config *modconfig = (config *)Settings;
2631
if (MATCH("Settings", "fps")) {
2732
modconfig->fps = atof(value);
28-
}
33+
}
2934
if (MATCH("Settings", "UseCustomScreenDimensions")) {
3035
modconfig->UseCustomScreenDimensions = atoi(value);
3136
} else if (MATCH("Settings", "ScreenWidth")) {
@@ -36,57 +41,136 @@ static int handler(void *Settings, const char *section, const char *name, const
3641
modconfig->EnableCursorClip = atoi(value);
3742
} else if (MATCH("Settings", "CursorClipHotkey")) {
3843
modconfig->CursorClipHotkey = strtol(value, NULL, 16);
44+
} else if (MATCH("Settings", "EnableLogging")) {
45+
modconfig->EnableLogging = atoi(value);
46+
} else if (MATCH("Settings", "EnableBorderless")) {
47+
modconfig->EnableBorderless = atoi(value);
3948
} else
4049
return 0;
4150
return 1;
4251
}
4352

4453
float readFile() {
45-
return ini_parse("FPSconfig.ini", handler, &configFile) < 0 ? 1000 : configFile.fps; // will set framerate limit to 1000 if the ini file is missing
54+
// Initialize config with default values
55+
configFile.EnableLogging = 0; // Default logging off
56+
configFile.fps = 1000; // Default FPS limit
57+
configFile.EnableBorderless = 1; // Default borderless enabled
58+
59+
// Parse INI file, which will override defaults if present
60+
if (ini_parse("FPSconfig.ini", handler, &configFile) < 0) {
61+
return configFile.fps; // Return default if file is missing
62+
}
63+
return configFile.fps;
4664
}
4765

48-
void setFps(float rFPS) {
66+
FILE* logFile = NULL;
67+
68+
void log_init() {
69+
if (!configFile.EnableLogging) return;
70+
logFile = fopen("DS3DebugFPS_log.txt", "w");
71+
if (logFile) {
72+
fprintf(logFile, "[INFO] Log started\n");
73+
fflush(logFile);
74+
}
75+
fclose(logFile);
76+
logFile = fopen("DS3DebugFPS_log.txt", "a");
77+
}
78+
void log_close() {
79+
if (!configFile.EnableLogging) return;
80+
if (logFile) fclose(logFile);
81+
}
82+
void log_print(const char* fmt, ...) {
83+
if (!configFile.EnableLogging || !logFile) return;
84+
va_list args;
85+
va_start(args, fmt);
86+
vfprintf(logFile, fmt, args);
87+
fprintf(logFile, "\n");
88+
fflush(logFile);
89+
va_end(args);
90+
}
4991

92+
void setFps(float rFPS) {
93+
log_print("[INFO] setFps called with rFPS = %f", rFPS);
5094
// Find Process
5195
DWORD PID;
5296
HWND hWnd = FindWindowA(NULL, "DARK SOULS III");
97+
log_print("[INFO] FindWindowA returned HWND = %p", hWnd);
5398
GetWindowThreadProcessId(hWnd, &PID);
99+
log_print("[INFO] GetWindowThreadProcessId returned PID = %lu", PID);
54100
HANDLE pHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, PID);
101+
log_print("[INFO] OpenProcess returned HANDLE = %p", pHandle);
55102

56103
// Skip Intros
57104
unsigned char SkipIntro[20] = {0x48, 0x31, 0xC0, 0x48, 0x89, 0x02, 0x49, 0x89, 0x04, 0x24, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90};
58105
NtWriteVirtualMemory(pHandle, (LPVOID)0x140BF66AE, SkipIntro, 20, 0);
59106

60-
// Borderless
61-
if (configFile.UseCustomScreenDimensions != 1) {
62-
final.right = GetSystemMetrics(SM_CXSCREEN);
63-
final.bottom = GetSystemMetrics(SM_CYSCREEN);
64-
} else {
65-
final.right = configFile.ScreenWidth;
66-
final.bottom = configFile.ScreenHeight;
107+
// Borderless window mode (if enabled)
108+
if (configFile.EnableBorderless) {
109+
log_print("[INFO] Applying borderless window mode");
110+
if (configFile.UseCustomScreenDimensions != 1) {
111+
final.right = GetSystemMetrics(SM_CXSCREEN);
112+
final.bottom = GetSystemMetrics(SM_CYSCREEN);
113+
} else {
114+
final.right = configFile.ScreenWidth;
115+
final.bottom = configFile.ScreenHeight;
116+
}
117+
final.left = 0;
118+
final.top = 0;
119+
SetWindowLong(hWnd, GWL_STYLE, WS_POPUP | WS_VISIBLE);
120+
AdjustWindowRect(&final, GetWindowLong(hWnd, GWL_STYLE), FALSE);
121+
SetWindowLong(hWnd, GWL_EXSTYLE, (GetWindowLong(hWnd, GWL_EXSTYLE) | WS_EX_TOPMOST));
122+
MoveWindow(hWnd, final.left, final.top, final.right - final.left, final.bottom - final.top, TRUE);
123+
log_print("[INFO] Borderless window mode applied successfully");
67124
}
68-
final.left = 0;
69-
final.top = 0;
70-
SetWindowLong(hWnd, GWL_STYLE, WS_POPUP | WS_VISIBLE);
71-
AdjustWindowRect(&final, GetWindowLong(hWnd, GWL_STYLE), FALSE);
72-
SetWindowLong(hWnd, GWL_EXSTYLE, (GetWindowLong(hWnd, GWL_EXSTYLE) | WS_EX_TOPMOST));
73-
MoveWindow(hWnd, final.left, final.top, final.right - final.left, final.bottom - final.top, TRUE);
74-
75-
SprjFlipper = (DWORD64)GetModuleHandle("darksoulsiii.exe") + 0x489DD10;
76125

77-
// GRAPHICS -> GFX
78-
NtReadVirtualMemory(pHandle, (LPVOID)SprjFlipper, &SprjFlipper, sizeof(SprjFlipper), NULL);
126+
HMODULE hModule = GetModuleHandleA("darksoulsiii.exe");
127+
log_print("[SCAN] Module base address: 0x%p", hModule);
79128

80-
// Debug FPS | デバッグFPS
81-
NtWriteVirtualMemory(pHandle, (LPVOID)(SprjFlipper + 0x354), &rFPS, sizeof(DWORD), NULL);
129+
BYTE sprjFlipperPattern1[] = { 0x50, 0x60, 0x3E, 0x08, 0xF4, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
130+
BYTE sprjFlipperPattern2[] = { 0x50, 0x60, 0x3E, 0x08, 0xF3, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
131+
const char* sprjFlipperMask = "xx??xxxxxxxxxxxx";
82132

83-
// Use Debug FPS | デバッグFPSを利用するか
84-
NtWriteVirtualMemory(pHandle, (LPVOID)(SprjFlipper + 0x358), &useDebug, sizeof(char), NULL);
133+
HMODULE scanModule = GetModuleHandleA("darksoulsiii.exe");
134+
MODULEINFO moduleInfo;
135+
if (GetModuleInformation(pHandle, scanModule, &moduleInfo, sizeof(moduleInfo))) {
136+
BYTE* moduleBase = (BYTE*)moduleInfo.lpBaseOfDll;
137+
DWORD moduleSize = moduleInfo.SizeOfImage;
138+
size_t patternLen = strlen(sprjFlipperMask);
139+
int foundCount = 0;
140+
for (DWORD i = 0; i <= moduleSize - patternLen; ++i) {
141+
BOOL found1 = TRUE, found2 = TRUE;
142+
for (size_t j = 0; j < patternLen; ++j) {
143+
if (sprjFlipperMask[j] == 'x') {
144+
if (sprjFlipperPattern1[j] != moduleBase[i + j]) found1 = FALSE;
145+
if (sprjFlipperPattern2[j] != moduleBase[i + j]) found2 = FALSE;
146+
}
147+
}
148+
if (found1 || found2) {
149+
foundCount++;
150+
DWORD64 matchAddr = (DWORD64)(moduleBase + i);
151+
log_print("[SCAN] Pattern %s match at: 0x%llx", found1 ? "F4" : "F3", matchAddr);
152+
DWORD64 realSprjFlipper = 0;
153+
NtReadVirtualMemory(pHandle, (LPVOID)matchAddr, &realSprjFlipper, sizeof(realSprjFlipper), NULL);
154+
log_print("[PATCH] Read pointer at match: 0x%llx", realSprjFlipper);
155+
NtWriteVirtualMemory(pHandle, (LPVOID)(realSprjFlipper + 0x354), &rFPS, sizeof(DWORD), NULL);
156+
log_print("[PATCH] Patched rFPS at: 0x%llx", realSprjFlipper + 0x354);
157+
NtWriteVirtualMemory(pHandle, (LPVOID)(realSprjFlipper + 0x358), &useDebug, sizeof(char), NULL);
158+
log_print("[PATCH] Patched useDebug at: 0x%llx", realSprjFlipper + 0x358);
159+
break;
160+
}
161+
}
162+
if (foundCount == 0) {
163+
log_print("[SCAN] No matching patterns found in module.");
164+
} else {
165+
log_print("[SCAN] Successfully patched FPS and useDebug values.");
166+
}
167+
} else {
168+
log_print("[SCAN] Failed to get module information for scan.");
169+
}
85170

86171
if (configFile.EnableCursorClip != 0) {
87-
88-
// createthread for an asynchronous loop
89172
HANDLE thread = CreateThread(NULL, 0, (void *)containCursor, &hWnd, 0, NULL);
173+
log_print("[INFO] Cursor clip thread created.");
90174
}
91175
}
92176

@@ -99,6 +183,8 @@ BOOL WINAPI DllMain(HINSTANCE baseaddr, DWORD reason, BOOL isstatic) {
99183
while (BaseFileName-- > FullFilePath)
100184
if (*BaseFileName == L'\\')
101185
break;
186+
readFile();
187+
log_init();
102188
case DLL_THREAD_ATTACH:
103189
break;
104190
}
@@ -131,3 +217,6 @@ importD3D(FARPROC D3DAssemble_, DebugSetMute_, D3DCompile_, D3DCompressShaders_,
131217
setFps(readFile());
132218
}
133219
}
220+
221+
// Dummy atexit implementation for -nostartfiles builds
222+
int atexit(void (*func)(void)) { return 0; }

0 commit comments

Comments
 (0)