Files
PDModdingCommunity_PD-Loader/source-code/source/PD-Loader/dllmain.cpp
T

904 lines
31 KiB
C++

#include "framework.h"
#include "exception.hpp"
#include <VersionHelpers.h>
#include <Shlwapi.h>
#pragma comment(lib, "Shlwapi.lib")
#include <filesystem>
#include <fstream>
#include <iostream>
#pragma comment(linker,"\"/manifestdependency:type='win32' \
name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
HMODULE hm;
std::vector<std::wstring> iniPaths;
bool iequals(std::wstring_view s1, std::wstring_view s2)
{
std::wstring str1(std::move(s1));
std::wstring str2(std::move(s2));
std::transform(str1.begin(), str1.end(), str1.begin(), [](wchar_t c) { return ::towlower(c); });
std::transform(str2.begin(), str2.end(), str2.begin(), [](wchar_t c) { return ::towlower(c); });
return (str1 == str2);
}
std::wstring to_wstring(std::string_view cstr)
{
std::string str(std::move(cstr));
auto charsReturned = MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), NULL, 0);
std::wstring wstrTo(charsReturned, 0);
MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), &wstrTo[0], charsReturned);
return wstrTo;
}
std::wstring SHGetKnownFolderPath(REFKNOWNFOLDERID rfid, DWORD dwFlags, HANDLE hToken)
{
std::wstring r;
WCHAR* szSystemPath = nullptr;
if (SUCCEEDED(SHGetKnownFolderPath(rfid, dwFlags, hToken, &szSystemPath)))
{
r = szSystemPath;
}
CoTaskMemFree(szSystemPath);
return r;
};
HMODULE LoadLibraryW(const std::wstring& lpLibFileName)
{
return LoadLibraryW(lpLibFileName.c_str());
}
std::wstring GetCurrentDirectoryW()
{
static constexpr auto INITIAL_BUFFER_SIZE = MAX_PATH;
static constexpr auto MAX_ITERATIONS = 7;
std::wstring ret;
auto bufferSize = INITIAL_BUFFER_SIZE;
for (size_t iterations = 0; iterations < MAX_ITERATIONS; ++iterations)
{
ret.resize(bufferSize);
auto charsReturned = GetCurrentDirectoryW(bufferSize, &ret[0]);
if (charsReturned < ret.length())
{
ret.resize(charsReturned);
return ret;
}
else
{
bufferSize *= 2;
}
}
return L"";
}
UINT GetPrivateProfileIntW(LPCWSTR lpAppName, LPCWSTR lpKeyName, INT nDefault, const std::vector<std::wstring>& fileNames)
{
for (const auto& file : fileNames)
{
nDefault = GetPrivateProfileIntW(lpAppName, lpKeyName, nDefault, file.c_str());
}
return nDefault;
}
std::wstring GetSelfName()
{
const std::wstring moduleFileName = GetModuleFileNameW(hm);
return moduleFileName.substr(moduleFileName.find_last_of(L"/\\") + 1);
}
template<typename T, typename... Args>
void GetSections(T&& h, Args... args)
{
const std::set< std::string_view, std::less<> > s = { args... };
size_t dwLoadOffset = (size_t)GetModuleHandle(NULL);
BYTE* pImageBase = reinterpret_cast<BYTE*>(dwLoadOffset);
PIMAGE_DOS_HEADER pDosHeader = reinterpret_cast<PIMAGE_DOS_HEADER>(dwLoadOffset);
PIMAGE_NT_HEADERS pNtHeader = reinterpret_cast<PIMAGE_NT_HEADERS>(pImageBase + pDosHeader->e_lfanew);
PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(pNtHeader);
for (int iSection = 0; iSection < pNtHeader->FileHeader.NumberOfSections; ++iSection, ++pSection)
{
auto pszSectionName = reinterpret_cast<const char*>(pSection->Name);
if (s.find(pszSectionName) != s.end())
{
DWORD dwPhysSize = (pSection->Misc.VirtualSize + 4095) & ~4095;
std::forward<T>(h)(pSection, dwLoadOffset, dwPhysSize);
}
}
}
enum Kernel32ExportsNames
{
eGetStartupInfoA,
eGetStartupInfoW,
eGetModuleHandleA,
eGetModuleHandleW,
eGetProcAddress,
eGetShortPathNameA,
eFindNextFileA,
eFindNextFileW,
eLoadLibraryA,
eLoadLibraryW,
eFreeLibrary,
eCreateEventA,
eCreateEventW,
eGetSystemInfo,
eInterlockedCompareExchange,
eSleep,
Kernel32ExportsNamesCount
};
enum Kernel32ExportsData
{
IATPtr,
ProcAddress,
Kernel32ExportsDataCount
};
size_t Kernel32Data[Kernel32ExportsNamesCount][Kernel32ExportsDataCount];
static LONG OriginalLibraryLoaded = 0;
void LoadOriginalLibrary()
{
if (_InterlockedCompareExchange(&OriginalLibraryLoaded, 1, 0) != 0) return;
auto szSelfName = GetSelfName();
auto szSystemPath = SHGetKnownFolderPath(FOLDERID_System, 0, nullptr) + L'\\' + szSelfName;
auto szLocalPath = GetModuleFileNameW(hm); szLocalPath = szLocalPath.substr(0, szLocalPath.find_last_of(L"/\\") + 1);
if (iequals(szSelfName, L"dinput8.dll")) {
dinput8.LoadOriginalLibrary(LoadLibraryW(szSystemPath));
}
else if (iequals(szSelfName, L"dnsapi.dll")) {
dnsapi.LoadOriginalLibrary(LoadLibraryW(szSystemPath));
}
}
void LoadDVA(std::wstring &path, LPCWSTR dir, LPCWSTR fileName)
{
if (GetModuleHandle(path.c_str()) == NULL)
{
auto h = LoadLibraryW(path);
SetCurrentDirectoryW(dir); //in case dva switched it
if (h == NULL)
{
auto e = GetLastError();
if (e != ERROR_DLL_INIT_FAILED) // in case dllmain returns false
{
std::wstring msg = L"Unable to load " + std::wstring(fileName) + L". Error: " + std::to_wstring(e);
LPVOID err_msg;
if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, e, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), (LPWSTR)&err_msg, 1, NULL)
!= 0)
{
msg += L"\n";
msg += (LPWSTR)err_msg;
}
switch (e)
{
case 126:
msg.append(L"\n\nIs the Microsoft Visual C++ Redistributable for Visual Studio 2019 (x64) installed?");
break;
case 998:
msg.append(L"\n\nPossible causes:\n- a dependency is not installed; or\n- GPU detection failed (select the GPU Type manually/force Novidia in its Config).");
break;
default:
msg.append(L"\n\nAre all dependencies installed?");
}
msg.append(L"\nPlease refer to the quick start guide or the wiki.\n\nAlso, please note that 3rd-party plugins may require additional libraries.");
MessageBoxW(0, msg.c_str(), L"PD Loader", MB_ICONERROR);
}
}
else
{
auto procedure = (void(*)())GetProcAddress(h, "InitializeDVA");
if (procedure != NULL)
{
procedure();
}
}
}
}
void FindFiles(WIN32_FIND_DATAW* fd)
{
auto dir = GetCurrentDirectoryW();
HANDLE dvaFile = FindFirstFileW(L"*.dva", fd);
if (dvaFile != INVALID_HANDLE_VALUE)
{
do {
if (!(fd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
{
if (fd->cFileName[0] == '.' && fd->cFileName[1] == '_') continue; // exclude macOS metadata
auto pos = wcslen(fd->cFileName);
if (fd->cFileName[pos - 4] == '.' &&
(fd->cFileName[pos - 3] == 'd' || fd->cFileName[pos - 3] == 'D') &&
(fd->cFileName[pos - 2] == 'v' || fd->cFileName[pos - 2] == 'V') &&
(fd->cFileName[pos - 1] == 'a' || fd->cFileName[pos - 1] == 'A') &&
GetPrivateProfileIntW(L"plugins", fd->cFileName, TRUE, iniPaths))
{
auto path = dir + L'\\' + fd->cFileName;
LoadDVA(path, dir.c_str(), fd->cFileName);
}
}
} while (FindNextFileW(dvaFile, fd));
FindClose(dvaFile);
}
}
void LoadPlugins()
{
auto oldDir = GetCurrentDirectoryW(); // store the current directory
auto szSelfPath = GetModuleFileNameW(hm).substr(0, GetModuleFileNameW(hm).find_last_of(L"/\\") + 1);
SetCurrentDirectoryW(szSelfPath.c_str());
auto nWantsToLoadPlugins = GetPrivateProfileIntW(L"global", L"enable", TRUE, iniPaths);
if (nWantsToLoadPlugins)
{
if (SetCurrentDirectoryW(szSelfPath.c_str()))
{
WIN32_FIND_DATAW dh;
HANDLE divaHook = FindFirstFileW(L"divahook.dll", &dh);
if (divaHook != INVALID_HANDLE_VALUE && !(dh.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
{
printf("[PD Loader] Loading divahook.dll\n");
auto path = szSelfPath + L'\\' + dh.cFileName;
LoadDVA(path, szSelfPath.c_str(), dh.cFileName);
}
if (SetCurrentDirectoryW(L"plugins\\"))
{
WIN32_FIND_DATAW fd;
FindFiles(&fd);
}
}
}
SetCurrentDirectoryW(oldDir.c_str()); // Reset the current directory
}
static LONG LoadedPluginsYet = 0;
void LoadEverything()
{
if (_InterlockedCompareExchange(&LoadedPluginsYet, 1, 0) != 0) return;
LoadOriginalLibrary();
LoadPlugins();
}
static LONG RestoredOnce = 0;
void LoadPluginsAndRestoreIAT(uintptr_t retaddr)
{
bool calledFromBind = false;
//steam drm check
GetSections([&](PIMAGE_SECTION_HEADER pSection, size_t dwLoadOffset, DWORD dwPhysSize) {
auto dwStart = static_cast<uintptr_t>(dwLoadOffset + pSection->VirtualAddress);
auto dwEnd = dwStart + dwPhysSize;
if (retaddr >= dwStart && retaddr <= dwEnd)
calledFromBind = true;
}, ".bind");
if (calledFromBind) return;
if (_InterlockedCompareExchange(&RestoredOnce, 1, 0) != 0) return;
LoadEverything();
for (size_t i = 0; i < Kernel32ExportsNamesCount; i++)
{
if (Kernel32Data[i][IATPtr] && Kernel32Data[i][ProcAddress])
{
auto ptr = (size_t*)Kernel32Data[i][IATPtr];
DWORD dwProtect[2];
VirtualProtect(ptr, sizeof(size_t), PAGE_EXECUTE_READWRITE, &dwProtect[0]);
*ptr = Kernel32Data[i][ProcAddress];
VirtualProtect(ptr, sizeof(size_t), dwProtect[0], &dwProtect[1]);
}
}
}
void WINAPI CustomGetStartupInfoA(LPSTARTUPINFOA lpStartupInfo)
{
LoadPluginsAndRestoreIAT((uintptr_t)_ReturnAddress());
return GetStartupInfoA(lpStartupInfo);
}
void WINAPI CustomGetStartupInfoW(LPSTARTUPINFOW lpStartupInfo)
{
LoadPluginsAndRestoreIAT((uintptr_t)_ReturnAddress());
return GetStartupInfoW(lpStartupInfo);
}
HMODULE WINAPI CustomGetModuleHandleA(LPCSTR lpModuleName)
{
LoadPluginsAndRestoreIAT((uintptr_t)_ReturnAddress());
return GetModuleHandleA(lpModuleName);
}
HMODULE WINAPI CustomGetModuleHandleW(LPCWSTR lpModuleName)
{
LoadPluginsAndRestoreIAT((uintptr_t)_ReturnAddress());
return GetModuleHandleW(lpModuleName);
}
FARPROC WINAPI CustomGetProcAddress(HMODULE hModule, LPCSTR lpProcName)
{
LoadPluginsAndRestoreIAT((uintptr_t)_ReturnAddress());
return GetProcAddress(hModule, lpProcName);
}
DWORD WINAPI CustomGetShortPathNameA(LPCSTR lpszLongPath, LPSTR lpszShortPath, DWORD cchBuffer)
{
LoadPluginsAndRestoreIAT((uintptr_t)_ReturnAddress());
return GetShortPathNameA(lpszLongPath, lpszShortPath, cchBuffer);
}
BOOL WINAPI CustomFindNextFileA(HANDLE hFindFile, LPWIN32_FIND_DATAA lpFindFileData)
{
LoadPluginsAndRestoreIAT((uintptr_t)_ReturnAddress());
return FindNextFileA(hFindFile, lpFindFileData);
}
BOOL WINAPI CustomFindNextFileW(HANDLE hFindFile, LPWIN32_FIND_DATAW lpFindFileData)
{
LoadPluginsAndRestoreIAT((uintptr_t)_ReturnAddress());
return FindNextFileW(hFindFile, lpFindFileData);
}
HMODULE WINAPI CustomLoadLibraryA(LPCSTR lpLibFileName)
{
LoadOriginalLibrary();
return LoadLibraryA(lpLibFileName);
}
HMODULE WINAPI CustomLoadLibraryW(LPCWSTR lpLibFileName)
{
LoadOriginalLibrary();
return LoadLibraryW(lpLibFileName);
}
BOOL WINAPI CustomFreeLibrary(HMODULE hLibModule)
{
if (hLibModule != hm)
return FreeLibrary(hLibModule);
else
return !NULL;
}
HANDLE WINAPI CustomCreateEventA(LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, BOOL bInitialState, LPCSTR lpName)
{
LoadPluginsAndRestoreIAT((uintptr_t)_ReturnAddress());
return CreateEventA(lpEventAttributes, bManualReset, bInitialState, lpName);
}
HANDLE WINAPI CustomCreateEventW(LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, BOOL bInitialState, LPCWSTR lpName)
{
LoadPluginsAndRestoreIAT((uintptr_t)_ReturnAddress());
return CreateEventW(lpEventAttributes, bManualReset, bInitialState, lpName);
}
void WINAPI CustomGetSystemInfo(LPSYSTEM_INFO lpSystemInfo)
{
LoadPluginsAndRestoreIAT((uintptr_t)_ReturnAddress());
return GetSystemInfo(lpSystemInfo);
}
LONG WINAPI CustomInterlockedCompareExchange(LONG volatile* Destination, LONG ExChange, LONG Comperand)
{
LoadPluginsAndRestoreIAT((uintptr_t)_ReturnAddress());
return _InterlockedCompareExchange(Destination, ExChange, Comperand);
}
void WINAPI CustomSleep(DWORD dwMilliseconds)
{
LoadPluginsAndRestoreIAT((uintptr_t)_ReturnAddress());
return Sleep(dwMilliseconds);
}
bool HookKernel32IAT(HMODULE mod, bool exe)
{
auto hExecutableInstance = (size_t)mod;
IMAGE_NT_HEADERS* ntHeader = (IMAGE_NT_HEADERS*)(hExecutableInstance + ((IMAGE_DOS_HEADER*)hExecutableInstance)->e_lfanew);
IMAGE_IMPORT_DESCRIPTOR* pImports = (IMAGE_IMPORT_DESCRIPTOR*)(hExecutableInstance + ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
size_t nNumImports = ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size / sizeof(IMAGE_IMPORT_DESCRIPTOR) - 1;
if (exe)
{
Kernel32Data[eGetStartupInfoA][ProcAddress] = (size_t)GetProcAddress(GetModuleHandle(TEXT("KERNEL32.DLL")), "GetStartupInfoA");
Kernel32Data[eGetStartupInfoW][ProcAddress] = (size_t)GetProcAddress(GetModuleHandle(TEXT("KERNEL32.DLL")), "GetStartupInfoW");
Kernel32Data[eGetModuleHandleA][ProcAddress] = (size_t)GetProcAddress(GetModuleHandle(TEXT("KERNEL32.DLL")), "GetModuleHandleA");
Kernel32Data[eGetModuleHandleW][ProcAddress] = (size_t)GetProcAddress(GetModuleHandle(TEXT("KERNEL32.DLL")), "GetModuleHandleW");
Kernel32Data[eGetProcAddress][ProcAddress] = (size_t)GetProcAddress(GetModuleHandle(TEXT("KERNEL32.DLL")), "GetProcAddress");
Kernel32Data[eGetShortPathNameA][ProcAddress] = (size_t)GetProcAddress(GetModuleHandle(TEXT("KERNEL32.DLL")), "GetShortPathNameA");
Kernel32Data[eFindNextFileA][ProcAddress] = (size_t)GetProcAddress(GetModuleHandle(TEXT("KERNEL32.DLL")), "FindNextFileA");
Kernel32Data[eFindNextFileW][ProcAddress] = (size_t)GetProcAddress(GetModuleHandle(TEXT("KERNEL32.DLL")), "FindNextFileW");
Kernel32Data[eLoadLibraryA][ProcAddress] = (size_t)GetProcAddress(GetModuleHandle(TEXT("KERNEL32.DLL")), "LoadLibraryA");
Kernel32Data[eLoadLibraryW][ProcAddress] = (size_t)GetProcAddress(GetModuleHandle(TEXT("KERNEL32.DLL")), "LoadLibraryW");
Kernel32Data[eFreeLibrary][ProcAddress] = (size_t)GetProcAddress(GetModuleHandle(TEXT("KERNEL32.DLL")), "FreeLibrary");
Kernel32Data[eCreateEventA][ProcAddress] = (size_t)GetProcAddress(GetModuleHandle(TEXT("KERNEL32.DLL")), "CreateEventA");
Kernel32Data[eCreateEventW][ProcAddress] = (size_t)GetProcAddress(GetModuleHandle(TEXT("KERNEL32.DLL")), "CreateEventW");
Kernel32Data[eGetSystemInfo][ProcAddress] = (size_t)GetProcAddress(GetModuleHandle(TEXT("KERNEL32.DLL")), "GetSystemInfo");
Kernel32Data[eInterlockedCompareExchange][ProcAddress] = (size_t)GetProcAddress(GetModuleHandle(TEXT("KERNEL32.DLL")), "InterlockedCompareExchange");
Kernel32Data[eSleep][ProcAddress] = (size_t)GetProcAddress(GetModuleHandle(TEXT("KERNEL32.DLL")), "Sleep");
}
uint32_t matchedImports = 0;
auto PatchIAT = [&](size_t start, size_t end, size_t exe_end)
{
for (size_t i = 0; i < nNumImports; i++)
{
if (hExecutableInstance + (pImports + i)->FirstThunk > start && !(end && hExecutableInstance + (pImports + i)->FirstThunk > end))
end = hExecutableInstance + (pImports + i)->FirstThunk;
}
if (!end) { end = start + 0x100; }
if (end > exe_end) //for very broken exes
{
start = hExecutableInstance;
end = exe_end;
}
for (auto i = start; i < end; i += sizeof(size_t))
{
DWORD dwProtect[2];
VirtualProtect((size_t*)i, sizeof(size_t), PAGE_EXECUTE_READWRITE, &dwProtect[0]);
auto ptr = *(size_t*)i;
if (!ptr)
continue;
if (ptr == Kernel32Data[eGetStartupInfoA][ProcAddress])
{
if (exe) Kernel32Data[eGetStartupInfoA][IATPtr] = i;
*(size_t*)i = (size_t)CustomGetStartupInfoA;
matchedImports++;
}
else if (ptr == Kernel32Data[eGetStartupInfoW][ProcAddress])
{
if (exe) Kernel32Data[eGetStartupInfoW][IATPtr] = i;
*(size_t*)i = (size_t)CustomGetStartupInfoW;
matchedImports++;
}
else if (ptr == Kernel32Data[eGetModuleHandleA][ProcAddress])
{
if (exe) Kernel32Data[eGetModuleHandleA][IATPtr] = i;
*(size_t*)i = (size_t)CustomGetModuleHandleA;
matchedImports++;
}
else if (ptr == Kernel32Data[eGetModuleHandleW][ProcAddress])
{
if (exe) Kernel32Data[eGetModuleHandleW][IATPtr] = i;
*(size_t*)i = (size_t)CustomGetModuleHandleW;
matchedImports++;
}
else if (ptr == Kernel32Data[eGetProcAddress][ProcAddress])
{
if (exe) Kernel32Data[eGetProcAddress][IATPtr] = i;
*(size_t*)i = (size_t)CustomGetProcAddress;
matchedImports++;
}
else if (ptr == Kernel32Data[eGetShortPathNameA][ProcAddress])
{
if (exe) Kernel32Data[eGetShortPathNameA][IATPtr] = i;
*(size_t*)i = (size_t)CustomGetShortPathNameA;
matchedImports++;
}
else if (ptr == Kernel32Data[eFindNextFileA][ProcAddress])
{
if (exe) Kernel32Data[eFindNextFileA][IATPtr] = i;
*(size_t*)i = (size_t)CustomFindNextFileA;
matchedImports++;
}
else if (ptr == Kernel32Data[eFindNextFileW][ProcAddress])
{
if (exe) Kernel32Data[eFindNextFileW][IATPtr] = i;
*(size_t*)i = (size_t)CustomFindNextFileW;
matchedImports++;
}
else if (ptr == Kernel32Data[eLoadLibraryA][ProcAddress])
{
if (exe) Kernel32Data[eLoadLibraryA][IATPtr] = i;
*(size_t*)i = (size_t)CustomLoadLibraryA;
matchedImports++;
}
else if (ptr == Kernel32Data[eLoadLibraryW][ProcAddress])
{
if (exe) Kernel32Data[eLoadLibraryW][IATPtr] = i;
*(size_t*)i = (size_t)CustomLoadLibraryW;
matchedImports++;
}
else if (ptr == Kernel32Data[eFreeLibrary][ProcAddress])
{
if (exe) Kernel32Data[eFreeLibrary][IATPtr] = i;
*(size_t*)i = (size_t)CustomFreeLibrary;
matchedImports++;
}
else if (ptr == Kernel32Data[eCreateEventA][ProcAddress])
{
if (exe) Kernel32Data[eCreateEventA][IATPtr] = i;
*(size_t*)i = (size_t)CustomCreateEventA;
matchedImports++;
}
else if (ptr == Kernel32Data[eCreateEventW][ProcAddress])
{
if (exe) Kernel32Data[eCreateEventW][IATPtr] = i;
*(size_t*)i = (size_t)CustomCreateEventW;
matchedImports++;
}
else if (ptr == Kernel32Data[eGetSystemInfo][ProcAddress])
{
if (exe) Kernel32Data[eGetSystemInfo][IATPtr] = i;
*(size_t*)i = (size_t)CustomGetSystemInfo;
matchedImports++;
}
else if (ptr == Kernel32Data[eInterlockedCompareExchange][ProcAddress])
{
if (exe) Kernel32Data[eInterlockedCompareExchange][IATPtr] = i;
*(size_t*)i = (size_t)CustomInterlockedCompareExchange;
matchedImports++;
}
else if (ptr == Kernel32Data[eSleep][ProcAddress])
{
if (exe) Kernel32Data[eSleep][IATPtr] = i;
*(size_t*)i = (size_t)CustomSleep;
matchedImports++;
}
VirtualProtect((size_t*)i, sizeof(size_t), dwProtect[0], &dwProtect[1]);
}
};
static auto getSection = [](const PIMAGE_NT_HEADERS nt_headers, unsigned section) -> PIMAGE_SECTION_HEADER
{
return reinterpret_cast<PIMAGE_SECTION_HEADER>(
(UCHAR*)nt_headers->OptionalHeader.DataDirectory +
nt_headers->OptionalHeader.NumberOfRvaAndSizes * sizeof(IMAGE_DATA_DIRECTORY) +
section * sizeof(IMAGE_SECTION_HEADER));
};
static auto getSectionEnd = [](IMAGE_NT_HEADERS* ntHeader, size_t inst) -> auto
{
auto sec = getSection(ntHeader, ntHeader->FileHeader.NumberOfSections - 1);
auto secSize = max(sec->SizeOfRawData, sec->Misc.VirtualSize);
auto end = inst + max(sec->PointerToRawData, sec->VirtualAddress) + secSize;
return end;
};
auto hExecutableInstance_end = getSectionEnd(ntHeader, hExecutableInstance);
// Find kernel32.dll
for (size_t i = 0; i < nNumImports; i++)
{
if ((size_t)(hExecutableInstance + (pImports + i)->Name) < hExecutableInstance_end)
{
if (!_stricmp((const char*)(hExecutableInstance + (pImports + i)->Name), "KERNEL32.DLL"))
PatchIAT(hExecutableInstance + (pImports + i)->FirstThunk, 0, hExecutableInstance_end);
}
}
// Fixing ordinals
auto szSelfName = GetSelfName();
static auto PatchOrdinals = [&szSelfName](size_t hInstance)
{
IMAGE_NT_HEADERS* ntHeader = (IMAGE_NT_HEADERS*)(hInstance + ((IMAGE_DOS_HEADER*)hInstance)->e_lfanew);
IMAGE_IMPORT_DESCRIPTOR* pImports = (IMAGE_IMPORT_DESCRIPTOR*)(hInstance + ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
size_t nNumImports = ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size / sizeof(IMAGE_IMPORT_DESCRIPTOR) - 1;
for (size_t i = 0; i < nNumImports; i++)
{
if ((size_t)(hInstance + (pImports + i)->Name) < getSectionEnd(ntHeader, (size_t)hInstance))
{
if (iequals(szSelfName, (to_wstring((const char*)(hInstance + (pImports + i)->Name)))))
{
PIMAGE_THUNK_DATA thunk = (PIMAGE_THUNK_DATA)(hInstance + (pImports + i)->OriginalFirstThunk);
size_t j = 0;
while (thunk->u1.Function)
{
if (thunk->u1.Ordinal & IMAGE_ORDINAL_FLAG)
{
PIMAGE_IMPORT_BY_NAME import = (PIMAGE_IMPORT_BY_NAME)(hInstance + thunk->u1.AddressOfData);
void** p = (void**)(hInstance + (pImports + i)->FirstThunk);
if (iequals(szSelfName, L"dinput8.dll"))
{
DWORD Protect;
VirtualProtect(&p[j], 4, PAGE_EXECUTE_READWRITE, &Protect);
if ((IMAGE_ORDINAL(thunk->u1.Ordinal)) == 1)
p[j] = _DirectInput8Create;
}
}
++thunk;
}
}
}
}
};
ModuleList dlls;
dlls.Enumerate(ModuleList::SearchLocation::LocalOnly);
for (auto& e : dlls.m_moduleList)
{
PatchOrdinals((size_t)std::get<HMODULE>(e));
}
return matchedImports > 0;
}
LONG WINAPI CustomUnhandledExceptionFilter(LPEXCEPTION_POINTERS ExceptionInfo)
{
// step 1: write minidump
wchar_t modulename[MAX_PATH];
wchar_t filename[MAX_PATH];
wchar_t timestamp[128];
__time64_t time;
struct tm ltime;
HANDLE hFile;
HWND hWnd;
wchar_t* modulenameptr = NULL;
if (GetModuleFileNameW(GetModuleHandle(NULL), modulename, _countof(modulename)) != 0)
{
modulenameptr = wcsrchr(modulename, '\\');
*modulenameptr = L'\0';
modulenameptr += 1;
}
else
{
*modulenameptr = L'err.err';
}
_time64(&time);
_localtime64_s(&ltime, &time);
wcsftime(timestamp, _countof(timestamp), L"%Y%m%d%H%M%S", &ltime);
swprintf_s(filename, L"%s\\%s\\%s.%s.dmp", modulename, L"logs", modulenameptr, timestamp);
hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile != INVALID_HANDLE_VALUE)
{
MINIDUMP_EXCEPTION_INFORMATION ex;
memset(&ex, 0, sizeof(ex));
ex.ThreadId = GetCurrentThreadId();
ex.ExceptionPointers = ExceptionInfo;
ex.ClientPointers = TRUE;
if (FAILED(MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, MiniDumpWithDataSegs, &ex, NULL, NULL)))
{
}
CloseHandle(hFile);
}
// step 2: write log
// Logs exception into buffer and writes to file
swprintf_s(filename, L"%s\\%s\\%s.%s.log", modulename, L"logs", modulenameptr, timestamp);
hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile != INVALID_HANDLE_VALUE)
{
auto Log = [ExceptionInfo, hFile](char* buffer, size_t size, bool reg, bool stack, bool trace)
{
if (LogException(buffer, size, (LPEXCEPTION_POINTERS)ExceptionInfo, reg, stack, trace))
{
DWORD NumberOfBytesWritten = 0;
WriteFile(hFile, buffer, strlen(buffer), &NumberOfBytesWritten, NULL);
}
};
// Try to make a very descriptive exception, for that we need to malloc a huge buffer...
if (auto buffer = (char*)malloc(max_logsize_ever))
{
Log(buffer, max_logsize_ever, true, true, true);
free(buffer);
}
else
{
// Use a static buffer, no need for any allocation
static const auto size = max_logsize_basic + max_logsize_regs + max_logsize_stackdump;
static char static_buf[size];
static_assert(size <= max_static_buffer, "Static buffer is too big");
Log(buffer = static_buf, sizeof(static_buf), true, true, false);
}
CloseHandle(hFile);
}
// step 3: exit the application
ShowCursor(TRUE);
hWnd = FindWindowW(0, L"");
SetForegroundWindow(hWnd);
return EXCEPTION_CONTINUE_SEARCH;
}
void Init()
{
SetProcessDPIAware();
auto dir = GetModuleFileNameW(hm).substr(0, GetModuleFileNameW(hm).find_last_of(L"/\\") + 1);
SetCurrentDirectoryW(dir.c_str());
std::wstring modulePath = GetModuleFileNameW(hm);
std::wstring moduleName = modulePath.substr(modulePath.find_last_of(L"/\\") + 1);
moduleName.resize(moduleName.find_last_of(L'.'));
modulePath.resize(modulePath.find_last_of(L"/\\") + 1);
iniPaths.emplace_back(modulePath + moduleName + L".ini");
const auto CONFIG_FILE = L"plugins\\config.ini";
const auto CONFIG_FILE_TEMPLATE = L"plugins\\config_template.bin";
iniPaths.emplace_back(modulePath + CONFIG_FILE);
// initialize configuration files.
CopyFileW(CONFIG_FILE_TEMPLATE, CONFIG_FILE, true);
const auto COMPONENTS_TEMPLATE = L"plugins\\components_template.bin";
const auto COMPONENTS = L"plugins\\components.ini";
CopyFileW(COMPONENTS_TEMPLATE, COMPONENTS, true);
const auto KEYCONFIG_TEMPLATE = L"plugins\\keyconfig_template.bin";
const auto KEYCONFIG = L"plugins\\keyconfig.ini";
CopyFileW(KEYCONFIG_TEMPLATE, KEYCONFIG, true);
const auto PLAYERDATA_TEMPLATE = L"plugins\\playerdata_template.bin";
const auto PLAYERDATA = L"plugins\\playerdata.ini";
CopyFileW(PLAYERDATA_TEMPLATE, PLAYERDATA, true);
const auto DIVASOUND_TEMPLATE = L"plugins\\DivaSound_template.bin";
const auto DIVASOUND = L"plugins\\DivaSound.ini";
CopyFileW(DIVASOUND_TEMPLATE, DIVASOUND, true);
const auto SHADERPATCH_TEMPLATE = L"plugins\\ShaderPatch_template.bin";
const auto SHADERPATCH = L"plugins\\ShaderPatch.ini";
CopyFileW(SHADERPATCH_TEMPLATE, SHADERPATCH, true);
const auto SHADERPATCHCONFIG_TEMPLATE = L"plugins\\ShaderPatchConfig_template.bin";
const auto SHADERPATCHCONFIG = L"plugins\\ShaderPatchConfig.ini";
CopyFileW(SHADERPATCHCONFIG_TEMPLATE, SHADERPATCHCONFIG, true);
if ((PathFileExistsW(CONFIG_FILE_TEMPLATE) && !PathFileExistsW(CONFIG_FILE)) ||
(PathFileExistsW(COMPONENTS_TEMPLATE) && !PathFileExistsW(COMPONENTS)) ||
(PathFileExistsW(KEYCONFIG_TEMPLATE) && !PathFileExistsW(KEYCONFIG)) ||
(PathFileExistsW(PLAYERDATA_TEMPLATE) && !PathFileExistsW(PLAYERDATA)) ||
(PathFileExistsW(DIVASOUND_TEMPLATE) && !PathFileExistsW(DIVASOUND)) ||
(PathFileExistsW(SHADERPATCH_TEMPLATE) && !PathFileExistsW(SHADERPATCH)) ||
(PathFileExistsW(SHADERPATCHCONFIG_TEMPLATE) && !PathFileExistsW(SHADERPATCHCONFIG))
)
MessageBoxW(0, L"Could not install configuration files. Possible causes:\n\n- PD Loader was not installed correctly\n- The game is on a read-only drive\n- No privileges to write in the folder", L"PD Loader", MB_ICONWARNING);
CreateDirectoryW(L"plugins\\pv_equip", NULL);
const auto eq_modules = L"plugins\\pv_equip\\modules.ini";
if (!PathFileExistsW(eq_modules))
{
std::ofstream stream(eq_modules);
stream << "[modules]\n# Manual editing is NOT recommended as the game will save here during gameplay";
stream.close();
}
const auto eq_sfx = L"plugins\\pv_equip\\sfx.ini";
if (!PathFileExistsW(eq_sfx))
{
std::ofstream stream(eq_sfx);
stream << "[SFX]\n# INI Format = ' pv.xxx.btn = sfx id (ex. 5) '\n# pv.001.btn=2 for example\n# pv.xxx.btn, pv.xxx.chain, pv.xxx.slide & pv.xxx.touch can all be assigned using the format";
stream.close();
}
const auto eq_skins = L"plugins\\pv_equip\\skins.ini";
if (!PathFileExistsW(eq_skins))
{
std::ofstream stream(eq_skins);
stream << "[skins]\n# INI Format = ' pv.xxx.skin = skin ID (ex. 123) '\n# pv.001.skin=100 for example";
stream.close();
}
const std::filesystem::path databank_template = L"ram\\databank_template_v2";
const std::filesystem::path databank = L"ram\\databank";
if (!std::filesystem::exists(databank)&&std::filesystem::exists(databank_template))
{
try
{
std::filesystem::create_directories(databank);
std::filesystem::copy(databank_template, databank, std::filesystem::copy_options::recursive);
}
catch (std::exception& e)
{
std::cout << "[PD Loader] databank copy exception: " << e.what() << std::endl;
MessageBoxW(0, L"Could not install databank files. Is the game in a read-only folder?\n\n", L"PD Loader", MB_ICONWARNING);
}
}
std::wstring m = GetModuleFileNameW(NULL);
m = m.substr(0, m.find_last_of(L"/\\") + 1) + L"logs";
auto FolderExists = [](LPCWSTR szPath) -> BOOL
{
DWORD dwAttrib = GetFileAttributes(szPath);
return (dwAttrib != INVALID_FILE_ATTRIBUTES && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
};
if (FolderExists(m.c_str()))
{
SetUnhandledExceptionFilter(CustomUnhandledExceptionFilter);
// Now stub out CustomUnhandledExceptionFilter so NO ONE ELSE can set it!
uint32_t ret = 0x909090C3; //ret
DWORD protect[2];
VirtualProtect(&SetUnhandledExceptionFilter, sizeof(ret), PAGE_EXECUTE_READWRITE, &protect[0]);
memcpy(&SetUnhandledExceptionFilter, &ret, sizeof(ret));
VirtualProtect(&SetUnhandledExceptionFilter, sizeof(ret), protect[0], &protect[1]);
}
DeleteFileW(L"~dinput8.dll");
std::wstring szSelfName = GetSelfName();
if (iequals(szSelfName, L"DINPUT8.dll"))
{
WIN32_FIND_DATAW fd;
HANDLE dnsloader = FindFirstFileW(L"dnsapi.dll", &fd);
if (dnsloader != INVALID_HANDLE_VALUE)
{
MessageBoxW(0, L"PD Loader was loaded from \"dinput8.dll\", but \"dnsapi.dll\" was found. Aborting.\nPlease refer to the quick start guide or the wiki.", L"PD Loader", MB_ICONERROR);
exit(1);
}
}
else if (iequals(szSelfName, L"DNSAPI.dll"))
{
WIN32_FIND_DATAW fd;
HANDLE dnsloader = FindFirstFileW(L"dinput8.dll", &fd);
if (dnsloader != INVALID_HANDLE_VALUE)
{
auto dinput_handle = GetModuleHandleW(L"DINPUT8.dll");
if (dinput_handle&&!FreeLibrary(dinput_handle))
{
MessageBoxW(0, L"PD Loader was loaded from \"dnsapi.dll\", but \"dinput8.dll\" was found and PD Loader could not unload it. Please delete \"dinput8.dll\".", L"PD Loader", MB_ICONERROR);
exit(1);
}
else if (!MoveFileExW(L"dinput8.dll", L"~dinput8.dll", MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH))
{
MessageBoxW(0, L"PD Loader was loaded from \"dnsapi.dll\", but \"dinput8.dll\" was found and PD Loader could not delete it automatically\nIs it read-only?", L"PD Loader", MB_ICONERROR);
exit(1);
}
}
if(IsWindows8OrGreater())
{
if (MoveFileExW(L"dnsapi.dll", L"dinput8.dll", MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH))
{
WCHAR DIVA_EXECUTABLE[MAX_PATH];
GetModuleFileNameW(NULL, DIVA_EXECUTABLE, MAX_PATH);
LPWSTR DIVA_COMMAND_LINE = GetCommandLineW();
STARTUPINFOW si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
CreateProcessW(DIVA_EXECUTABLE, DIVA_COMMAND_LINE, NULL, NULL, false, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);
exit(0);
}
else MessageBoxW(0, L"It is highly recommended to rename \"dnsapi.dll\" \"dinput8.dll\" on Windows 8/8.1/10 to avoid issues, but PD Loader could not rename the file automatically.\nIs it read-only?", L"PD Loader", MB_ICONWARNING);
}
}
else
{
MessageBoxW(0, L"Supported file names:\n\"dnsapi.dll\" (Windows Vista/7)\n\"dinput8.dll\"", L"PD Loader", MB_ICONERROR);
exit(1);
}
LoadEverything();
}
BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID /*lpReserved*/)
{
if (reason == DLL_PROCESS_ATTACH)
{
hm = hModule;
Init();
}
return TRUE;
}