forked from PeterTh/dsfix
/
FPS.cpp
195 lines (158 loc) · 5.36 KB
/
FPS.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
// Dark Souls FPS fix by Clement Barnier (Nwks)
///////////////////////////////////////////////
#include "FPS.h"
#include <windows.h>
#include "Settings.h"
#include "main.h"
#include "Detouring.h"
#include "RenderstateManager.h"
void enableGFWLCompatibility(void);
#define JMP32_SZ 5
#define CALL32_SZ 5
#define NOPOP 0x90
#define JMPOP 0xE9
#define CALLOP 0xE8
// Globals
static DWORD originalBase = NULL;
static DWORD imageBase = NULL;
// Hook Globals
double lastRenderTime;
static LARGE_INTEGER timerFreq;
static LARGE_INTEGER counterAtStart;
// Hook Addresses
//------------------------------------
// Time-step value address
#define ADDR_TS 0x011E4D70 // 1.0.0 was 0x012497F0, 1.0.1 was 0x012498E0
// Presentation interval address
#define ADDR_PRESINT 0x00FFA15E // 1.0.0 was 0x010275AE, 1.0.1 was 0x0102788E
// getDrawThreadMsgCommand address in HGCommandDispatcher loop
#define ADDR_GETCMD 0x00BAC3DD // 1.0.0 was 0x00BD601D, 1.0.1 was 0x00BD60ED
//----------------------------------------------------------------------------------------
// Support Functions
//----------------------------------------------------------------------------------------
// Misc
//------------------------------------
DWORD getAbsoluteAddress(DWORD offset) {
if (imageBase)
return imageBase + offset;
else
return NULL;
}
DWORD convertAddress(DWORD Address) {
return getAbsoluteAddress(Address - originalBase);
}
// Memory
//------------------------------------
void writeToAddress(void* Data, DWORD Address, int Size) {
DWORD oldProtect;
VirtualProtect((LPVOID)Address, Size, PAGE_READWRITE, &oldProtect);
memcpy((void*)Address, Data, Size);
VirtualProtect((LPVOID)Address, Size, oldProtect, &oldProtect);
}
void updateAnimationStepTime(float stepTime, float minFPS, float maxFPS) {
float FPS = 1.0f/(stepTime/1000);
if (FPS < minFPS)
FPS = minFPS;
else if (FPS > maxFPS)
FPS = maxFPS;
float cappedStep = 1/(float)FPS;
if(RSManager::get().isPaused()) cappedStep = 0.000000000000000001f;
DWORD data = *(DWORD*)&cappedStep;
writeToAddress(&data, convertAddress(ADDR_TS), sizeof(data));
}
// Timer
double getElapsedTime(void) {
LARGE_INTEGER c;
QueryPerformanceCounter(&c);
return (double)( (c.QuadPart - counterAtStart.QuadPart) * 1000.0 / (double)timerFreq.QuadPart );
}
// Detour
//------------------------------------
// Make sure to adjust length according to instructions below detoured address!
// Partially overwritten instructions will mess-up disassembly and capacity to debug
void *DetourApply(BYTE *orig, BYTE *hook, int len, BYTE type)
{
BYTE OP, SZ;
if (type == JMPOP) {
OP = JMPOP;
SZ = JMP32_SZ;
}
else if (type == CALLOP) {
OP = CALLOP;
SZ = CALL32_SZ;
}
else return 0;
DWORD dwProt = 0;
BYTE *jmp = (BYTE*)malloc(len+SZ);
VirtualProtect(orig, len, PAGE_READWRITE, &dwProt);
memcpy(jmp, orig, len);
jmp += len; // increment to the end of the copied bytes
jmp[0] = OP;
*(DWORD*)(jmp+1) = (DWORD)(orig+len - jmp) - SZ;
memset(orig, NOPOP, len);
orig[0] = OP;
*(DWORD*)(orig+1) = (DWORD)(hook - orig) - SZ;
VirtualProtect(orig, len, dwProt, 0);
return (jmp-len);
}
void DetourRemove(BYTE *src, BYTE *jmp, int len) {
DWORD dwProt = 0;
VirtualProtect(src, len, PAGE_READWRITE, &dwProt);
memcpy(src, jmp, len);
VirtualProtect(src, len, dwProt, 0);
}
//----------------------------------------------------------------------------------------
// Hook functions
//----------------------------------------------------------------------------------------
void _stdcall updateFramerate(unsigned int cmd) {
// If rendering was performed, update animation step-time
if((cmd == 2) || (cmd == 5)) {
// FPS regulation based on previous render
double maxFPS = (double)Settings::get().getCurrentFPSLimit();
double minFPS = 10.0f;
double currentTime = getElapsedTime();
double deltaTime = currentTime - lastRenderTime;
lastRenderTime = currentTime;
// Update step-time
updateAnimationStepTime((float)deltaTime, (float)minFPS, (float)maxFPS);
}
}
// Hook
__declspec(naked) void getDrawThreadMsgCommand(void) {
__asm {
MOV EAX, [ECX+0Ch] // Put msgCmd in EAX (Return value)
PUSHAD
PUSH EAX
CALL updateFramerate // Call updateFramerate(msgCmd)
POPAD
RETN
}
}
//----------------------------------------------------------------------------------------
// Game Patches
//----------------------------------------------------------------------------------------
void applyFPSPatch() {
enableGFWLCompatibility();
// Get imageBase
HANDLE exeHandle = NULL;
originalBase = 0x0400000;
exeHandle = GetModuleHandle(NULL);
if(exeHandle != NULL)
imageBase = (DWORD)exeHandle;
// Init counter for frame-rate calculations
lastRenderTime = 0.0f;
QueryPerformanceFrequency(&timerFreq);
QueryPerformanceCounter(&counterAtStart);
// Binary patches
//--------------------------------------------------------------
DWORD address;
DWORD data;
// Override D3D Presentation Interval
address = convertAddress(ADDR_PRESINT);
data = 5; //Set to immediate
writeToAddress(&data, address, sizeof(data));
// Detour call to getDrawThreadMsgCommand
address = convertAddress(ADDR_GETCMD);
DetourApply((BYTE*)address, (BYTE*)getDrawThreadMsgCommand, 5, CALLOP);
SDLOG(0, "FPS rate unlocked\n");
}