-
Notifications
You must be signed in to change notification settings - Fork 12
Expand file tree
/
Copy pathmain.cpp
More file actions
217 lines (181 loc) · 5.04 KB
/
main.cpp
File metadata and controls
217 lines (181 loc) · 5.04 KB
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
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
#include <Windows.h>
#include <Psapi.h>
namespace Proud
{
class CHostBase {};
class CRemotePeer : CHostBase
{
private:
char pad154[0x150];
public:
unsigned int Ping; // One way latency
};
class CNetClient {};
class CNetClientImpl
{
private:
char pad2B8[0x2B8];
public:
struct
{
int Id;
CHostBase *Pointer;
} *Host;
private:
char pad540[0x540 - 0x2B8 - 8];
public:
CNetClient Base;
};
}
class UInputUnit
{
private:
char pad2180[0x2180];
public:
unsigned int CurrentTimestamp;
unsigned int OpponentTimestamp;
private:
char pad21D8[0x21D8 - 0x2180 - 8];
public:
unsigned int MaxFramesAhead; // Max frames ahead of opponent (will slow down to reach this)
unsigned int IsPaused; // Stop simulating the game
private:
char pad220C[0x220C - 0x21D8 - 8];
public:
unsigned int TimeBase; // Set at round start, used to offset desired timestamp
private:
char pad2214[0x2214 - 0x220C - 4];
public:
unsigned int DesiredTimestamp; // Set based on real time, will speed up to reach this
private:
char pad2220[0x2220 - 0x2214 - 4];
public:
unsigned int FramesToSimulate; // Number of frames to simulate, used to speed up/slow down
};
class UnknownNetBullshit
{
private:
char pad010[0x10];
public:
Proud::CNetClient *Client;
};
class UnknownNetList
{
private:
char pad070[0x70];
public:
UnknownNetBullshit **List;
UnknownNetBullshit **ListEnd;
};
UnknownNetList *NetList;
extern "C" uintptr_t UpdateTimestampsOrig;
uintptr_t UpdateTimestampsOrig;
extern "C" void UpdateTimestampsOrigWrapper(UInputUnit*);
bool GetPing(unsigned int *Ping)
{
// Index 1 is opponent
if (NetList->List == nullptr ||
NetList->List + 1 >= NetList->ListEnd ||
NetList->List[1] == nullptr ||
NetList->List[1]->Client == nullptr)
{
return false;
}
// dynamic_cast to CNetClientImpl
const auto *Client = NetList->List[1]->Client;
const auto *ClientImpl = (Proud::CNetClientImpl*)((char*)Client - offsetof(Proud::CNetClientImpl, Base));
if (ClientImpl->Host == nullptr || ClientImpl->Host->Pointer == nullptr)
return false;
const auto *Peer = (Proud::CRemotePeer*)ClientImpl->Host->Pointer;
*Ping = Peer->Ping;
return true;
}
// Called after UInputUnit::UpdateTimestamps
extern "C" void UpdateTimestampsHook(UInputUnit *Input)
{
const auto OldTimeBase = Input->TimeBase;
UpdateTimestampsOrigWrapper(Input);
static unsigned int LastPingFrames = 0;
// Game hasn't started yet if TimeBase is still updating
if (Input->TimeBase != OldTimeBase)
{
LastPingFrames = 0;
return;
}
unsigned int Ping;
if (!GetPing(&Ping))
{
LastPingFrames = 0;
return;
}
auto PingFrames = (unsigned int)((float)Ping * 60.f / 1000.f + .5f);
// Don't hitch from small ping fluctuations
if (PingFrames == LastPingFrames - 1)
PingFrames = LastPingFrames;
else
LastPingFrames = PingFrames;
// Don't get farther ahead than normal for compatibility, even with high ping
Input->MaxFramesAhead = min(PingFrames + 1, 15);
if (Input->CurrentTimestamp >= Input->OpponentTimestamp + Input->MaxFramesAhead)
{
// Don't speed up after waiting for the opponent if ping increases
Input->TimeBase--;
}
// Never get ahead of where we should be based on real time
const auto TargetTimestamp = min(Input->DesiredTimestamp, Input->OpponentTimestamp + Input->MaxFramesAhead);
if (Input->CurrentTimestamp < TargetTimestamp)
{
// Speed up to correct for hitch
Input->FramesToSimulate = TargetTimestamp - Input->CurrentTimestamp;
}
}
bool GetModuleBounds(const char *Name, uintptr_t *Start, uintptr_t *End)
{
const auto Module = GetModuleHandle(Name);
if(Module == nullptr)
return false;
MODULEINFO Info;
GetModuleInformation(GetCurrentProcess(), Module, &Info, sizeof(Info));
*Start = (uintptr_t)(Info.lpBaseOfDll);
*End = *Start + Info.SizeOfImage;
return true;
}
uintptr_t Sigscan(const uintptr_t Start, const uintptr_t End, const char *Sig, const char *Mask)
{
const auto ScanEnd = End - strlen(Mask) + 1;
for (auto Address = Start; Address < ScanEnd; Address++) {
for (size_t i = 0;; i++) {
if (Mask[i] == '\0')
return Address;
if (Mask[i] != '?' && Sig[i] != *(char*)(Address + i))
break;
}
}
return 0;
}
uintptr_t GetRel32(const uintptr_t Address)
{
return Address + *(int*)(Address) + 4;
}
void JmpHook(const uintptr_t Address, void *Target)
{
constexpr auto PatchSize = 12;
DWORD OldProtect;
VirtualProtect((void*)Address, PatchSize, PAGE_EXECUTE_READWRITE, &OldProtect);
*(WORD*)Address = 0xB848; // mov rax, Target
*(void**)(Address + 2) = Target;
*(WORD*)(Address + 10) = 0xE0FF; // jmp rax
VirtualProtect((void*)Address, PatchSize, OldProtect, &OldProtect);
}
BOOL WINAPI DllMain(HINSTANCE Instance, DWORD Reason, void* Reserved)
{
if (Reason != DLL_PROCESS_ATTACH)
return FALSE;
uintptr_t Start, End;
if (!GetModuleBounds("StreetFighterV.exe", &Start, &End))
return FALSE;
NetList = (UnknownNetList*)GetRel32(Sigscan(Start, End, "\x9B\x00\x00\x00\x83\xC8\x01", "xxxxxxx") + 0xA);
UpdateTimestampsOrig = Sigscan(Start, End, "\x83\xBB\x04\x22\x00\x00\x00", "xxxxxxx") - 0x29;
JmpHook(UpdateTimestampsOrig, UpdateTimestampsHook);
return TRUE;
}