@@ -16,130 +16,49 @@ bool FileExists(LPCWSTR path)
16
16
// #define SKIP_VERSION_CHECK 1
17
17
18
18
char * exe_base = nullptr ;
19
- const size_t addr_UEngine = 0x42AA3A8 ; // GEngine address (UEngine global pointer)
20
19
21
- const size_t offs_UEngine_ConsoleClass = 0x140 ; // offset to ConsoleClass field inside UEngine class
22
- const size_t offs_UGameViewportClient_ViewportConsole = 0x48 ; // offset to ViewportConsole field inside UGameViewportClient class
20
+ const size_t AddrFPakPlatformFile__FindFileInPakFiles = 0x1FC7A30 ; // address of FPakPlatformFile::FindFileInPakFiles func (hooked)
21
+ const size_t AddrFPakPlatformFile__IsNonPakFilenameAllowed = 0x1FCA930 ; // address of FPakPlatformFile::IsNonPakFilenameAllowed func (hooked)
22
+ const size_t AddrWindowTitle = 0x3FC1BC8 ;
23
23
24
- const size_t addr_StaticConstructObject_Internal_Address = 0x14A5AF0 ; // address of StaticConstructObject_Internal func
25
-
26
- const size_t addr_FOutputDeviceRedirector__Get = 0x132C930 ; // address of FOutputDeviceRedirector::Get func
27
- const size_t addr_FOutputDeviceRedirector__AddOutputDevice = 0x131F740 ; // address of FOutputDeviceRedirector::AddOutputDevice func
28
-
29
- const size_t addr_UGameViewportClient__SetupInitialLocalPlayer = 0x207A280 ; // address of UGameViewportClient::SetupInitialLocalPlayer func (hooked)
30
-
31
- const size_t addr_Pakfile__Find = 0x291FB50 ; // address of PakFile::Find func (hooked)
32
- const size_t addr_FPakPlatformFile__IsNonPakFilenameAllowed = 0x29223A0 ; // address of FPakPlatformFile::IsNonPakFilenameAllowed func (hooked)
33
-
34
- const size_t addr_UInputSettings__PostInitProperties_check = 0x2388D8A ; // address of the ConsoleKeys.Num() == 1 checks JNZ inside UInputSettings::PostInitProperties
35
- const size_t addr_UInputSettings__PostInitProperties_check2 = 0x2388DA4 ; // address of the ConsoleKeys[0] == Tilde checks JNZ inside UInputSettings::PostInitProperties
36
-
37
- typedef void *(*StaticConstructObject_Internal_ptr)(void * Class, void * InOuter, void * Name, void * SetFlags, void * InternalSetFlags, void * Template, bool bCopyTransientsFromClassDefaults, struct FObjectInstancingGraph * InstanceGraph);
38
- typedef void *(*FOutputDeviceRedirector__Get__ptr)(void );
39
- typedef void (*FOutputDeviceRedirector__AddOutputDevice__ptr)(void * thisptr, void * OutputDevice);
40
- typedef void *(*SetupInitialLocalPlayer_ptr)(void * thisptr, void * OutError);
41
- typedef void *(*PakFile__Find_ptr)(void * thisptr, void * Filename);
42
-
43
- SetupInitialLocalPlayer_ptr SetupInitialLocalPlayer_orig;
44
-
45
- // UGameViewportClient::SetupInitialLocalPlayer hook: in UE4 builds with ALLOW_CONSOLE=1, this function inits the UGameViewportClient::ViewportConsole field
46
- // so we just recreate the code that does that, and luckily that's all we need to re-enable the console!
47
- void * __fastcall SetupInitialLocalPlayer_hook (char * thisptr, void * OutError)
48
- {
49
- // create UConsole class
50
- StaticConstructObject_Internal_ptr StaticConstructObject_Internal = (StaticConstructObject_Internal_ptr)(exe_base + addr_StaticConstructObject_Internal_Address);
51
-
52
- char * engine = *(char **)(exe_base + addr_UEngine);
53
- void * consoleClass = *(void **)(engine + offs_UEngine_ConsoleClass);
54
-
55
- void * console = StaticConstructObject_Internal (consoleClass, thisptr, 0 , 0 , 0 , 0 , 0 , 0 );
56
-
57
- // set ViewportConsole field in this UGameViewportClient instance
58
- *(void **)(thisptr + offs_UGameViewportClient_ViewportConsole) = console;
59
-
60
- // UE4 calls this, but doesn't seem to actually redirect any logs, probably all stripped in shipping builds
61
- if (addr_FOutputDeviceRedirector__Get)
62
- {
63
- FOutputDeviceRedirector__Get__ptr FOutputDeviceRedirector__Get = (FOutputDeviceRedirector__Get__ptr)(exe_base + addr_FOutputDeviceRedirector__Get);
64
- FOutputDeviceRedirector__AddOutputDevice__ptr FOutputDeviceRedirector__AddOutputDevice = (FOutputDeviceRedirector__AddOutputDevice__ptr)(exe_base + addr_FOutputDeviceRedirector__AddOutputDevice);
65
- void * redirector = FOutputDeviceRedirector__Get ();
66
- FOutputDeviceRedirector__AddOutputDevice (redirector, console);
67
- }
68
-
69
- // call rest of SetupInitialLocalPlayer
70
- return SetupInitialLocalPlayer_orig (thisptr, OutError);
71
- }
72
-
73
- void HookConsole ()
74
- {
75
- char * UGameViewportClient__SetupInitialLocalPlayer = exe_base + addr_UGameViewportClient__SetupInitialLocalPlayer;
76
- MH_CreateHook ((void *)UGameViewportClient__SetupInitialLocalPlayer, SetupInitialLocalPlayer_hook, (LPVOID*)&SetupInitialLocalPlayer_orig);
77
- MH_EnableHook ((void *)UGameViewportClient__SetupInitialLocalPlayer);
78
-
79
- if (addr_UInputSettings__PostInitProperties_check)
80
- {
81
- // change UInputSettings::PostInitProperties's ConsoleKeys.Num() == 1 check to be ConsoleKeys.Num() >= 1
82
- // this way the non-english bindings will be added if ConsoleKeys has at least 1 binding
83
- // so emptying ConsoleKeys will stop non-english bindings being added (for people who want to disable console I guess)
84
- // but having 1 or more binding (in DQXIs case, 2) will still let the game add the non-english ones
85
- // (previously it would never add them, since DQXI BaseInput.ini sets ConsoleKeys = {Tilde, Atmark}, which is > 1)
86
- DWORD new_prot = PAGE_READWRITE;
87
- DWORD old_prot;
88
- VirtualProtect (exe_base + addr_UInputSettings__PostInitProperties_check, 2 , new_prot, &old_prot);
89
- *(uint16_t *)(exe_base + addr_UInputSettings__PostInitProperties_check) = 0x8C0F ; // JNZ -> JL
90
- VirtualProtect (exe_base + addr_UInputSettings__PostInitProperties_check, 2 , old_prot, &new_prot);
91
-
92
- /* VirtualProtect(exe_base + addr_UInputSettings__PostInitProperties_check, 6, new_prot, &old_prot);
93
- *(uint32_t*)(exe_base + addr_UInputSettings__PostInitProperties_check) = 0x90909090;
94
- *(uint16_t*)(exe_base + addr_UInputSettings__PostInitProperties_check + 4) = 0x9090;
95
- VirtualProtect(exe_base + addr_UInputSettings__PostInitProperties_check, 6, old_prot, &new_prot);*/
96
- }
97
-
98
- /* if (addr_UInputSettings__PostInitProperties_check2)
99
- {
100
- // removes ConsoleKeys[0] == Tilde check for the non-english bindings to be added
101
- DWORD new_prot = PAGE_READWRITE;
102
- DWORD old_prot;
103
- VirtualProtect(exe_base + addr_UInputSettings__PostInitProperties_check2, 6, new_prot, &old_prot);
104
- *(uint32_t*)(exe_base + addr_UInputSettings__PostInitProperties_check2) = 0x90909090;
105
- *(uint16_t*)(exe_base + addr_UInputSettings__PostInitProperties_check2 + 4) = 0x9090;
106
- VirtualProtect(exe_base + addr_UInputSettings__PostInitProperties_check2, 6, old_prot, &new_prot);
107
- }*/
108
- }
24
+ typedef void * (*TFPakPlatformFile__FindFileInPakFiles)(void * Paks, void * Filename, void ** OutPakFile);
25
+ typedef void * (*TFPakPlatformFile__IsNonPakFilenameAllowed)(void * thisptr, void * Filename);
109
26
110
27
const wchar_t * gameDataStart = L" ../../../" ; // seems to be at the start of every game path
111
- PakFile__Find_ptr PakFile__Find_orig;
112
28
113
- // PakFile::Find hook: this will check for any loose file with the same filename, and if a loose file is found will return false (ie: saying that the .pak doesn't contain it)
29
+ // FPakPlatformFile::FindFileInPakFiles hook: this will check for any loose file with the same filename
30
+ // If a loose file is found will return null (ie: saying that the .pak doesn't contain it)
114
31
// 90% of UE4 games will then try loading loose files, luckily DQXI is part of that 90% :D
115
- void * __fastcall PakFile__Find_hook (void * thisptr, void * Filename)
32
+ TFPakPlatformFile__FindFileInPakFiles RealFPakPlatformFile__FindFileInPakFiles;
33
+ void * __fastcall HookFPakPlatformFile__FindFileInPakFiles (void * Paks, TCHAR* Filename, void ** OutPakFile)
116
34
{
117
- const TCHAR* fname = *(TCHAR**)Filename;
35
+ if (OutPakFile)
36
+ *OutPakFile = nullptr ;
118
37
119
- if (wcsstr (fname , gameDataStart) && FileExists (fname ))
120
- return 0 ; // file exists loosely, return false so the game thinks that it doesn't exist in the .pak
38
+ if (Filename && wcsstr (Filename , gameDataStart) && FileExists (Filename ))
39
+ return 0 ; // file exists loosely, return false so the game thinks that it doesn't exist in the .pak
121
40
122
- return PakFile__Find_orig (thisptr , Filename);
41
+ return RealFPakPlatformFile__FindFileInPakFiles (Paks , Filename, OutPakFile );
123
42
}
124
43
125
- PakFile__Find_ptr IsNonPakFilenameAllowed_orig;
126
44
127
45
// FPakPlatformFile::IsNonPakFilenameAllowed hook: seems there are policies devs can set to stop certain file types being loaded from outside a .pak
128
46
// This just skips checking against those policies and always allows files to be loaded from wherever
129
- __int64 __fastcall IsNonPakFilenameAllowed_hook (void * thisptr, void * Filename)
47
+ TFPakPlatformFile__IsNonPakFilenameAllowed RealFPakPlatformFile__IsNonPakFilenameAllowed;
48
+ __int64 __fastcall HookFPakPlatformFile__IsNonPakFilenameAllowed (void * thisptr, void * Filename)
130
49
{
131
50
return 1 ;
132
51
}
133
52
134
53
void HookPakFile ()
135
54
{
136
- char * PakFile__Find = exe_base + addr_Pakfile__Find;
137
- MH_CreateHook ((void *)PakFile__Find, PakFile__Find_hook, (LPVOID*)&PakFile__Find_orig);
138
- MH_EnableHook ((void *)PakFile__Find);
55
+ char * FPakPlatformFile__FindFileInPakFiles = exe_base + AddrFPakPlatformFile__FindFileInPakFiles;
56
+ MH_CreateHook ((void *)FPakPlatformFile__FindFileInPakFiles, HookFPakPlatformFile__FindFileInPakFiles, (LPVOID*)&RealFPakPlatformFile__FindFileInPakFiles);
57
+
58
+ char * FPakPlatformFile__IsNonPakFilenameAllowed = exe_base + AddrFPakPlatformFile__IsNonPakFilenameAllowed;
59
+ MH_CreateHook ((void *)FPakPlatformFile__IsNonPakFilenameAllowed, HookFPakPlatformFile__IsNonPakFilenameAllowed, (LPVOID*)&RealFPakPlatformFile__IsNonPakFilenameAllowed);
139
60
140
- char * FPakPlatformFile__IsNonPakFilenameAllowed = exe_base + addr_FPakPlatformFile__IsNonPakFilenameAllowed;
141
- MH_CreateHook ((void *)FPakPlatformFile__IsNonPakFilenameAllowed, IsNonPakFilenameAllowed_hook, (LPVOID*)&IsNonPakFilenameAllowed_orig);
142
- MH_EnableHook ((void *)FPakPlatformFile__IsNonPakFilenameAllowed);
61
+ MH_EnableHook (MH_ALL_HOOKS);
143
62
}
144
63
145
64
bool CheckGameAddress (size_t address, uint32_t expectedValue)
@@ -154,42 +73,38 @@ bool IsSupportedGameVersion()
154
73
{
155
74
#ifndef SKIP_VERSION_CHECK
156
75
// check bytes of the various functions we call/hook, make sure they're what we expect
157
- if (!CheckGameAddress (addr_StaticConstructObject_Internal_Address, 0xEE462BE9 ))
158
- return false ;
159
- if (!CheckGameAddress (addr_FOutputDeviceRedirector__Get, 0x6439EBE9 ))
160
- return false ;
161
- if (!CheckGameAddress (addr_FOutputDeviceRedirector__AddOutputDevice, 0x245C8948 ))
76
+ if (!CheckGameAddress (AddrFPakPlatformFile__FindFileInPakFiles, 0x4CC48B48 ))
162
77
return false ;
163
- if (!CheckGameAddress (addr_UGameViewportClient__SetupInitialLocalPlayer, 0x245C8948 ))
164
- return false ;
165
- if (!CheckGameAddress (addr_Pakfile__Find, 0x245C8948 ))
166
- return false ;
167
- if (!CheckGameAddress (addr_FPakPlatformFile__IsNonPakFilenameAllowed, 0x245C8948 ))
168
- return false ;
169
- if (!CheckGameAddress (addr_UInputSettings__PostInitProperties_check, 0x0237850F ))
170
- return false ;
171
- if (!CheckGameAddress (addr_UInputSettings__PostInitProperties_check2, 0x021D850F ))
78
+ if (!CheckGameAddress (AddrFPakPlatformFile__IsNonPakFilenameAllowed, 0x245C8948 ))
172
79
return false ;
173
80
#endif
174
81
return true ;
175
82
}
176
83
177
84
void InitHooks ()
178
85
{
179
- exe_base = (char *)GetModuleHandleA (" DRAGON QUEST XI.exe" );
86
+ exe_base = (char *)GetModuleHandleA (" DRAGON QUEST XI S .exe" );
180
87
if (!exe_base)
181
- return ;
88
+ {
89
+ exe_base = (char *)GetModuleHandleA (" DRAGON QUEST XI S Demo.exe" );
90
+ }
182
91
183
- if (!IsSupportedGameVersion ())
92
+ if (!exe_base || ! IsSupportedGameVersion ())
184
93
{
185
- MessageBoxA (0 , " DQXIHook : unsupported DQXI version, DQXIHook features disabled.\r\n Please check for a new DQXIHook build!" , " DQXIHook " , 0 );
94
+ MessageBoxA (0 , " DQXISHook : unsupported game version, DQXISHook features disabled.\r\n Please check for a new DQXISHook build!" , " DQXISHook by emoose " , 0 );
186
95
return ;
187
96
}
188
97
189
98
MH_Initialize ();
190
99
191
100
HookPakFile ();
192
- HookConsole ();
101
+
102
+ // Change window title as indication that DQXISHook is active
103
+ wchar_t * windowTitle = (wchar_t *)(exe_base + AddrWindowTitle);
104
+ DWORD oldProtect = 0 ;
105
+ VirtualProtect (windowTitle, 0x30 , PAGE_EXECUTE_WRITECOPY, &oldProtect);
106
+ wcscpy_s (windowTitle, 0x18 , L" DQXIS (hooked)" );
107
+ VirtualProtect (windowTitle, 0x30 , oldProtect, nullptr );
193
108
}
194
109
195
110
HMODULE ourModule = 0 ;
0 commit comments