#include "framework.h" #include "exception.hpp" #include "Patches\patches.h" #include "Render\render.h" #include #include #pragma comment(lib, "Shlwapi.lib") #include #include #include #pragma comment(linker,"\"/manifestdependency:type='win32' \ name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \ processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") HMODULE hm; std::vector iniPaths; unsigned short game_version = 710; bool allowcustomfreeplay = false; 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& 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 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(dwLoadOffset); PIMAGE_DOS_HEADER pDosHeader = reinterpret_cast(dwLoadOffset); PIMAGE_NT_HEADERS pNtHeader = reinterpret_cast(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(pSection->Name); if (s.find(pszSectionName) != s.end()) { DWORD dwPhysSize = (pSection->Misc.VirtualSize + 4095) & ~4095; std::forward(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\nAre all dependencies installed?\nIf the problem persists, please disable this plugin."); 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 if (!_wcsicmp(fd->cFileName, L"Patches.dva") || !_wcsicmp(fd->cFileName, L"Render.dva")) { DeleteFileW(fd->cFileName); continue; } 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, FALSE, 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); } } ApplyPatches(); if (allowcustomfreeplay) ApplyVerText(); if (SetCurrentDirectoryW(szSelfPath.c_str())) ApplyAllCustomPatches(); if (!allowcustomfreeplay) { // we want to prevent both patches and plugins from changing it, not just patches InjectCode((void*)0x1409F61F0, { 0x46, 0x52, 0x45, 0x45, 0x20, 0x50, 0x4C, 0x41, 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x52, 0x45, 0x44, 0x49, 0x54, 0x28, 0x53, 0x29, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }); ApplyVerText(); } ApplyRender(hm); } 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(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( (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(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(<ime, &time); wcsftime(timestamp, _countof(timestamp), L"%Y%m%d%H%M%S", <ime); 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\\config.ini"; iniPaths.emplace_back(modulePath + CONFIG_FILE); // move old configuration files // should probably be handled by the yet-to-be unified config manager MoveFileW(L"plugins\\config.ini", CONFIG_FILE); MoveFileW(L"plugins\\components.ini", L"plugins_config\\components.ini"); MoveFileW(L"plugins\\keyconfig.ini", L"plugins_config\\keyconfig.ini"); MoveFileW(L"plugins\\playerdata.ini", L"plugins_config\\playerdata.ini"); MoveFileW(L"plugins\\CustoMixer.ini", L"plugins_config\\CustoMixer.ini"); MoveFileW(L"plugins\\DivaWig.ini", L"plugins_config\\DivaWig.ini"); MoveFileW(L"plugins\\DSCRemote.ini", L"plugins_config\\DSCRemote.ini"); MoveFileW(L"plugins\\DivaMovie.ini", L"plugins_config\\DivaMovie.ini"); MoveFileW(L"plugins\\EnhancedStageManager.ini", L"plugins_config\\EnhancedStageManager.ini"); MoveFileW(L"plugins\\DivaSound.ini", L"plugins_config\\DivaSound.ini"); MoveFileW(L"plugins\\Novidia.ini", L"plugins_config\\Novidia.ini"); MoveFileW(L"plugins\\ShaderPatchConfig.ini", L"plugins_config\\ShaderPatchConfig.ini"); MoveFileW(L"plugins\\pv_equip\\", L"plugins_config\\pv_equip\\"); DeleteFileW(L"plugins\\config_template.bin"); DeleteFileW(L"plugins\\components_template.bin"); DeleteFileW(L"plugins\\DivaSound_template.bin"); DeleteFileW(L"plugins\\keyconfig_template.bin"); DeleteFileW(L"plugins\\playerdata_template.bin"); DeleteFileW(L"plugins\\ShaderPatchConfig_template.bin"); // initialize configuration files WCHAR config_pcname[MAX_COMPUTERNAME_LENGTH + 1]; GetPrivateProfileStringW(L"global", L"pc", L"", config_pcname, MAX_COMPUTERNAME_LENGTH + 1, CONFIG_FILE); WCHAR this_pcname[MAX_COMPUTERNAME_LENGTH + 1]; DWORD count = MAX_COMPUTERNAME_LENGTH + 1; if (!PathFileExistsW(CONFIG_FILE)) { if (!( CreateDirectoryW(L"plugins_config", NULL) && WritePrivateProfileStringW(L"global", L"pc", this_pcname, CONFIG_FILE) && WritePrivateProfileStringW(L"patches", L"allow_custom_freeplay", L"0", CONFIG_FILE) && WritePrivateProfileStringW(L"plugins", L"Launcher.dva", L"1", CONFIG_FILE) && WritePrivateProfileStringW(L"plugins", L"TLAC.dva", L"1", CONFIG_FILE) && WritePrivateProfileStringW(L"plugins", L"DivaSound.dva", L"1", CONFIG_FILE) && WritePrivateProfileStringW(L"plugins", L"Novidia.dva", L"1", CONFIG_FILE) && WritePrivateProfileStringW(L"plugins", L"ShaderPatch.dva", L"1", CONFIG_FILE) )) { MessageBoxW(0, L"Could not install configuration files. Is the game in a read-only folder?", L"PD Loader", MB_ICONERROR); exit(1); } } else if(GetComputerNameW(this_pcname, &count) && wcscmp(config_pcname, this_pcname)!=0) { if (!( WritePrivateProfileStringW(L"global", L"pc", this_pcname, CONFIG_FILE) && WritePrivateProfileStringW(L"patches", L"allow_custom_freeplay", L"0", CONFIG_FILE) )) { MessageBoxW(0, L"Could not update configuration files. Is the game in a read-only folder?", L"PD Loader", MB_ICONERROR); exit(1); } } else allowcustomfreeplay = GetPrivateProfileIntW(L"patches", L"allow_custom_freeplay", FALSE, CONFIG_FILE) == 1; CreateDirectoryW(L"plugins_config\\pv_equip", NULL); const auto eq_modules = L"plugins_config\\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_config\\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_config\\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"; 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 the file read-only? Or is the game still running?", 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; }