953 lines
32 KiB
C++
953 lines
32 KiB
C++
#include "framework.h"
|
|
#include "exception.hpp"
|
|
#include "Patches\patches.h"
|
|
#include "Render\render.h"
|
|
#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;
|
|
|
|
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<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\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<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(<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;
|
|
}
|