Skip to content

Commit 40706c1

Browse files
committed
DQXISHook 0.6, supports DQXIS demo
1 parent ea6d607 commit 40706c1

File tree

2 files changed

+42
-127
lines changed

2 files changed

+42
-127
lines changed

DQXIHook/DQXIHook.vcxproj

+5-5
Original file line numberDiff line numberDiff line change
@@ -23,32 +23,32 @@
2323
<ProjectGuid>{4DA96578-C639-4C8D-89EA-383DECAE3A96}</ProjectGuid>
2424
<Keyword>Win32Proj</Keyword>
2525
<RootNamespace>DQXIHook</RootNamespace>
26-
<WindowsTargetPlatformVersion>10.0.17763.0</WindowsTargetPlatformVersion>
26+
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
2727
</PropertyGroup>
2828
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
2929
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
3030
<ConfigurationType>DynamicLibrary</ConfigurationType>
3131
<UseDebugLibraries>true</UseDebugLibraries>
32-
<PlatformToolset>v141</PlatformToolset>
32+
<PlatformToolset>v142</PlatformToolset>
3333
<CharacterSet>Unicode</CharacterSet>
3434
</PropertyGroup>
3535
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
3636
<ConfigurationType>DynamicLibrary</ConfigurationType>
3737
<UseDebugLibraries>false</UseDebugLibraries>
38-
<PlatformToolset>v141</PlatformToolset>
38+
<PlatformToolset>v142</PlatformToolset>
3939
<WholeProgramOptimization>true</WholeProgramOptimization>
4040
<CharacterSet>Unicode</CharacterSet>
4141
</PropertyGroup>
4242
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
4343
<ConfigurationType>DynamicLibrary</ConfigurationType>
4444
<UseDebugLibraries>true</UseDebugLibraries>
45-
<PlatformToolset>v141</PlatformToolset>
45+
<PlatformToolset>v142</PlatformToolset>
4646
<CharacterSet>Unicode</CharacterSet>
4747
</PropertyGroup>
4848
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
4949
<ConfigurationType>DynamicLibrary</ConfigurationType>
5050
<UseDebugLibraries>false</UseDebugLibraries>
51-
<PlatformToolset>v141</PlatformToolset>
51+
<PlatformToolset>v142</PlatformToolset>
5252
<WholeProgramOptimization>true</WholeProgramOptimization>
5353
<CharacterSet>Unicode</CharacterSet>
5454
</PropertyGroup>

DQXIHook/dllmain.cpp

+37-122
Original file line numberDiff line numberDiff line change
@@ -16,130 +16,49 @@ bool FileExists(LPCWSTR path)
1616
//#define SKIP_VERSION_CHECK 1
1717

1818
char* exe_base = nullptr;
19-
const size_t addr_UEngine = 0x42AA3A8; // GEngine address (UEngine global pointer)
2019

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;
2323

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);
10926

11027
const wchar_t* gameDataStart = L"../../../"; // seems to be at the start of every game path
111-
PakFile__Find_ptr PakFile__Find_orig;
11228

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)
11431
// 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)
11634
{
117-
const TCHAR* fname = *(TCHAR**)Filename;
35+
if (OutPakFile)
36+
*OutPakFile = nullptr;
11837

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
12140

122-
return PakFile__Find_orig(thisptr, Filename);
41+
return RealFPakPlatformFile__FindFileInPakFiles(Paks, Filename, OutPakFile);
12342
}
12443

125-
PakFile__Find_ptr IsNonPakFilenameAllowed_orig;
12644

12745
// FPakPlatformFile::IsNonPakFilenameAllowed hook: seems there are policies devs can set to stop certain file types being loaded from outside a .pak
12846
// 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)
13049
{
13150
return 1;
13251
}
13352

13453
void HookPakFile()
13554
{
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);
13960

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);
14362
}
14463

14564
bool CheckGameAddress(size_t address, uint32_t expectedValue)
@@ -154,42 +73,38 @@ bool IsSupportedGameVersion()
15473
{
15574
#ifndef SKIP_VERSION_CHECK
15675
// 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))
16277
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))
17279
return false;
17380
#endif
17481
return true;
17582
}
17683

17784
void InitHooks()
17885
{
179-
exe_base = (char*)GetModuleHandleA("DRAGON QUEST XI.exe");
86+
exe_base = (char*)GetModuleHandleA("DRAGON QUEST XI S.exe");
18087
if (!exe_base)
181-
return;
88+
{
89+
exe_base = (char*)GetModuleHandleA("DRAGON QUEST XI S Demo.exe");
90+
}
18291

183-
if (!IsSupportedGameVersion())
92+
if (!exe_base || !IsSupportedGameVersion())
18493
{
185-
MessageBoxA(0, "DQXIHook: unsupported DQXI version, DQXIHook features disabled.\r\nPlease check for a new DQXIHook build!", "DQXIHook", 0);
94+
MessageBoxA(0, "DQXISHook: unsupported game version, DQXISHook features disabled.\r\nPlease check for a new DQXISHook build!", "DQXISHook by emoose", 0);
18695
return;
18796
}
18897

18998
MH_Initialize();
19099

191100
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);
193108
}
194109

195110
HMODULE ourModule = 0;

0 commit comments

Comments
 (0)