From 640f746633d012d3aade940724b4e462c8b0bbf5 Mon Sep 17 00:00:00 2001 From: narzoul Date: Mon, 8 May 2017 22:57:30 +0200 Subject: [PATCH] Implement 8/16 bit display mode emulation 8/16 bit display mode emulation is now implemented internally instead of relying on the builtin Windows shims, as those are sometimes unreliable (not triggering for some games on some systems when they should). External DirectDraw hooks (such as the DWM8And16BitMitigation shim) are disabled to avoid interference. Fixes issues reported in #8, #9, #15. --- DDrawCompat/Common/Hook.cpp | 216 +++++++-- DDrawCompat/Common/Hook.h | 6 + DDrawCompat/DDraw/ActivateAppHandler.cpp | 1 + DDrawCompat/DDraw/DirectDraw.cpp | 114 +++-- DDrawCompat/DDraw/DirectDraw.h | 12 +- DDrawCompat/DDraw/DisplayMode.cpp | 251 ----------- DDrawCompat/DDraw/DisplayMode.h | 25 -- DDrawCompat/DDraw/RealPrimarySurface.cpp | 6 +- DDrawCompat/DDraw/Surfaces/PrimarySurface.cpp | 5 +- DDrawCompat/DDraw/Surfaces/Surface.cpp | 3 +- DDrawCompat/DDrawCompat.vcxproj | 12 +- DDrawCompat/DDrawCompat.vcxproj.filters | 12 +- DDrawCompat/Dll/DllMain.cpp | 14 +- DDrawCompat/Gdi/DcFunctions.cpp | 8 +- DDrawCompat/Win32/DisplayMode.cpp | 413 ++++++++++++++++++ DDrawCompat/Win32/DisplayMode.h | 21 + 16 files changed, 758 insertions(+), 361 deletions(-) delete mode 100644 DDrawCompat/DDraw/DisplayMode.cpp delete mode 100644 DDrawCompat/DDraw/DisplayMode.h create mode 100644 DDrawCompat/Win32/DisplayMode.cpp create mode 100644 DDrawCompat/Win32/DisplayMode.h diff --git a/DDrawCompat/Common/Hook.cpp b/DDrawCompat/Common/Hook.cpp index 77ed6b1..65ff706 100644 --- a/DDrawCompat/Common/Hook.cpp +++ b/DDrawCompat/Common/Hook.cpp @@ -2,10 +2,14 @@ #include #include +#include +#include +#include #include #include #include +#include #include "Common/Hook.h" #include "Common/Log.h" @@ -27,42 +31,79 @@ namespace [=](const auto& i) { return origFunc == i.first || origFunc == i.second.trampoline; }); } - FARPROC getProcAddress(HMODULE module, const char* procName) + std::vector getProcessModules(HANDLE process) { - if (!module || !procName) + std::vector modules(10000); + DWORD bytesNeeded = 0; + if (EnumProcessModules(process, modules.data(), modules.size(), &bytesNeeded)) { - return nullptr; + modules.resize(bytesNeeded / sizeof(modules[0])); + } + return modules; + } + + std::set getIatHookFunctions(const char* moduleName, const char* funcName) + { + std::set hookFunctions; + if (!moduleName || !funcName) + { + return hookFunctions; } - PIMAGE_DOS_HEADER dosHeader = reinterpret_cast(module); - if (IMAGE_DOS_SIGNATURE != dosHeader->e_magic) { - return nullptr; - } - char* moduleBase = reinterpret_cast(module); + auto modules = getProcessModules(GetCurrentProcess()); + const HMODULE targetModule = GetModuleHandle(moduleName); - PIMAGE_NT_HEADERS ntHeader = reinterpret_cast( - reinterpret_cast(dosHeader) + dosHeader->e_lfanew); - if (IMAGE_NT_SIGNATURE != ntHeader->Signature) + for (auto module : modules) { - return nullptr; - } - - PIMAGE_EXPORT_DIRECTORY exportDir = reinterpret_cast( - moduleBase + ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); - - DWORD* rvaOfNames = reinterpret_cast(moduleBase + exportDir->AddressOfNames); - - for (DWORD i = 0; i < exportDir->NumberOfNames; ++i) - { - if (0 == strcmp(procName, moduleBase + rvaOfNames[i])) + FARPROC func = Compat::getProcAddressFromIat(module, moduleName, funcName); + if (!func) { - WORD* nameOrds = reinterpret_cast(moduleBase + exportDir->AddressOfNameOrdinals); - DWORD* rvaOfFunctions = reinterpret_cast(moduleBase + exportDir->AddressOfFunctions); - return reinterpret_cast(moduleBase + rvaOfFunctions[nameOrds[i]]); + typedef decltype(GetProcAddress)* GetProcAddressFunc; + static const auto origGetProcAddressFunc = reinterpret_cast( + Compat::getProcAddress(GetModuleHandle("kernel32"), "GetProcAddress")); + + auto getProcAddressFunc = reinterpret_cast( + Compat::getProcAddressFromIat(module, "kernel32", "GetProcAddress")); + if (getProcAddressFunc && *getProcAddressFunc != origGetProcAddressFunc) + { + func = getProcAddressFunc(targetModule, funcName); + } + } + + if (func) + { + hookFunctions.insert(func); } } - return nullptr; + return hookFunctions; + } + + PIMAGE_NT_HEADERS getImageNtHeaders(HMODULE module) + { + PIMAGE_DOS_HEADER dosHeader = reinterpret_cast(module); + if (IMAGE_DOS_SIGNATURE != dosHeader->e_magic) + { + return nullptr; + } + + PIMAGE_NT_HEADERS ntHeaders = reinterpret_cast( + reinterpret_cast(dosHeader) + dosHeader->e_lfanew); + if (IMAGE_NT_SIGNATURE != ntHeaders->Signature) + { + return nullptr; + } + + return ntHeaders; + } + + std::string getModuleBaseName(HMODULE module) + { + char path[MAX_PATH] = {}; + GetModuleFileName(module, path, sizeof(path)); + const char* lastBackSlash = strrchr(path, '\\'); + const char* baseName = lastBackSlash ? lastBackSlash + 1 : path; + return baseName; } void hookFunction(const char* funcName, void*& origFuncPtr, void* newFuncPtr) @@ -114,6 +155,115 @@ namespace namespace Compat { + void redirectIatHooks(const char* moduleName, const char* funcName, void* newFunc) + { + auto hookFunctions(getIatHookFunctions(moduleName, funcName)); + + for (auto hookFunc : hookFunctions) + { + HMODULE module = nullptr; + if (!GetModuleHandleEx( + GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + static_cast(hookFunc), &module)) + { + continue; + } + + std::string moduleBaseName(getModuleBaseName(module)); + if (0 != _stricmp(moduleBaseName.c_str(), moduleName)) + { + Compat::Log() << "Disabling external hook to " << funcName << " in " << moduleBaseName; + hookFunction(hookFunc, newFunc); + } + } + } + + FARPROC* findProcAddressInIat(HMODULE module, const char* importedModuleName, const char* procName) + { + if (!module || !importedModuleName || !procName) + { + return nullptr; + } + + PIMAGE_NT_HEADERS ntHeaders = getImageNtHeaders(module); + if (!ntHeaders) + { + return nullptr; + } + + char* moduleBase = reinterpret_cast(module); + PIMAGE_IMPORT_DESCRIPTOR importDesc = reinterpret_cast(moduleBase + + ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress); + + for (PIMAGE_IMPORT_DESCRIPTOR desc = importDesc; + 0 != desc->Characteristics && 0xFFFF != desc->Name; + ++desc) + { + if (0 != _stricmp(moduleBase + desc->Name, importedModuleName)) + { + continue; + } + + auto thunk = reinterpret_cast(moduleBase + desc->FirstThunk); + auto origThunk = reinterpret_cast(moduleBase + desc->OriginalFirstThunk); + while (0 != thunk->u1.AddressOfData && 0 != origThunk->u1.AddressOfData) + { + auto origImport = reinterpret_cast( + moduleBase + origThunk->u1.AddressOfData); + + if (0 == strcmp(origImport->Name, procName)) + { + return reinterpret_cast(&thunk->u1.Function); + } + + ++thunk; + ++origThunk; + } + + break; + } + + return nullptr; + } + + FARPROC getProcAddress(HMODULE module, const char* procName) + { + if (!module || !procName) + { + return nullptr; + } + + PIMAGE_NT_HEADERS ntHeaders = getImageNtHeaders(module); + if (!ntHeaders) + { + return nullptr; + } + + char* moduleBase = reinterpret_cast(module); + PIMAGE_EXPORT_DIRECTORY exportDir = reinterpret_cast( + moduleBase + ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); + + DWORD* rvaOfNames = reinterpret_cast(moduleBase + exportDir->AddressOfNames); + + for (DWORD i = 0; i < exportDir->NumberOfNames; ++i) + { + if (0 == strcmp(procName, moduleBase + rvaOfNames[i])) + { + WORD* nameOrds = reinterpret_cast(moduleBase + exportDir->AddressOfNameOrdinals); + DWORD* rvaOfFunctions = reinterpret_cast(moduleBase + exportDir->AddressOfFunctions); + return reinterpret_cast(moduleBase + rvaOfFunctions[nameOrds[i]]); + } + } + + return nullptr; + } + + FARPROC getProcAddressFromIat(HMODULE module, const char* importedModuleName, const char* procName) + { + FARPROC* proc = findProcAddressInIat(module, importedModuleName, procName); + return proc ? *proc : nullptr; + } + void hookFunction(void*& origFuncPtr, void* newFuncPtr) { ::hookFunction(nullptr, origFuncPtr, newFuncPtr); @@ -143,6 +293,20 @@ namespace Compat FreeLibrary(module); } + void hookIatFunction(HMODULE module, const char* importedModuleName, const char* funcName, void* newFuncPtr) + { + FARPROC* func = findProcAddressInIat(module, importedModuleName, funcName); + if (func) + { + Compat::LogDebug() << "Hooking function via IAT: " << funcName; + DWORD oldProtect = 0; + VirtualProtect(func, sizeof(func), PAGE_READWRITE, &oldProtect); + *func = static_cast(newFuncPtr); + DWORD dummy = 0; + VirtualProtect(func, sizeof(func), oldProtect, &dummy); + } + } + void unhookAllFunctions() { while (!g_hookedFunctions.empty()) diff --git a/DDrawCompat/Common/Hook.h b/DDrawCompat/Common/Hook.h index 4e8dd0d..ff8e3b7 100644 --- a/DDrawCompat/Common/Hook.h +++ b/DDrawCompat/Common/Hook.h @@ -15,6 +15,8 @@ namespace Compat { + void redirectIatHooks(const char* moduleName, const char* funcName, void* newFunc); + template OrigFuncPtr& getOrigFuncPtr() { @@ -22,9 +24,13 @@ namespace Compat return origFuncPtr; } + FARPROC* findProcAddressInIat(HMODULE module, const char* importedModuleName, const char* procName); + FARPROC getProcAddress(HMODULE module, const char* procName); + FARPROC getProcAddressFromIat(HMODULE module, const char* importedModuleName, const char* procName); void hookFunction(void*& origFuncPtr, void* newFuncPtr); void hookFunction(HMODULE module, const char* funcName, void*& origFuncPtr, void* newFuncPtr); void hookFunction(const char* moduleName, const char* funcName, void*& origFuncPtr, void* newFuncPtr); + void hookIatFunction(HMODULE module, const char* importedModuleName, const char* funcName, void* newFuncPtr); template void hookFunction(const char* moduleName, const char* funcName, OrigFuncPtr newFuncPtr) diff --git a/DDrawCompat/DDraw/ActivateAppHandler.cpp b/DDrawCompat/DDraw/ActivateAppHandler.cpp index 6e611af..b990a49 100644 --- a/DDrawCompat/DDraw/ActivateAppHandler.cpp +++ b/DDrawCompat/DDraw/ActivateAppHandler.cpp @@ -5,6 +5,7 @@ #include "Common/Hook.h" #include "DDraw/ActivateAppHandler.h" #include "Gdi/Gdi.h" +#include "Win32/DisplayMode.h" #include "Win32/FontSmoothing.h" namespace diff --git a/DDrawCompat/DDraw/DirectDraw.cpp b/DDrawCompat/DDraw/DirectDraw.cpp index f6c62e2..3675112 100644 --- a/DDrawCompat/DDraw/DirectDraw.cpp +++ b/DDrawCompat/DDraw/DirectDraw.cpp @@ -1,12 +1,86 @@ #include "Common/CompatPtr.h" #include "DDraw/ActivateAppHandler.h" #include "DDraw/DirectDraw.h" -#include "DDraw/DisplayMode.h" +#include "DDraw/Repository.h" #include "DDraw/Surfaces/TagSurface.h" #include "DDraw/Surfaces/PrimarySurface.h" +#include "Win32/DisplayMode.h" + +namespace +{ + DDPIXELFORMAT getRgbPixelFormat(DWORD bpp) + { + DDPIXELFORMAT pf = {}; + pf.dwSize = sizeof(pf); + pf.dwFlags = DDPF_RGB; + pf.dwRGBBitCount = bpp; + + switch (bpp) + { + case 1: + pf.dwFlags |= DDPF_PALETTEINDEXED1; + break; + case 2: + pf.dwFlags |= DDPF_PALETTEINDEXED2; + break; + case 4: + pf.dwFlags |= DDPF_PALETTEINDEXED4; + break; + case 8: + pf.dwFlags |= DDPF_PALETTEINDEXED8; + break; + case 16: + pf.dwRBitMask = 0xF800; + pf.dwGBitMask = 0x07E0; + pf.dwBBitMask = 0x001F; + break; + case 24: + case 32: + pf.dwRBitMask = 0xFF0000; + pf.dwGBitMask = 0x00FF00; + pf.dwBBitMask = 0x0000FF; + break; + } + + return pf; + } + + template + HRESULT setDisplayMode(TDirectDraw* This, DWORD width, DWORD height, DWORD bpp) + { + return DDraw::DirectDraw::s_origVtable.SetDisplayMode(This, width, height, bpp); + } + + template + HRESULT setDisplayMode(TDirectDraw* This, DWORD width, DWORD height, DWORD bpp, + DWORD refreshRate, DWORD flags) + { + Win32::DisplayMode::setDDrawBpp(bpp); + HRESULT result = DDraw::DirectDraw::s_origVtable.SetDisplayMode( + This, width, height, bpp, refreshRate, flags); + Win32::DisplayMode::setDDrawBpp(0); + return result; + } +} namespace DDraw { + CompatPtr createCompatibleSurface(DWORD bpp) + { + DDSURFACEDESC2 desc = {}; + desc.dwSize = sizeof(desc); + desc.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_CAPS | DDSD_PIXELFORMAT; + desc.dwWidth = 1; + desc.dwHeight = 1; + desc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY; + desc.ddpfPixelFormat = getRgbPixelFormat(bpp); + + CompatPtr surface; + auto dd = DDraw::Repository::getDirectDraw(); + dd->CreateSurface(dd, &desc, &surface.getRef(), nullptr); + return surface; + } + template void* getDdObject(TDirectDraw& dd) { @@ -18,14 +92,20 @@ namespace DDraw template void* getDdObject(IDirectDraw4&); template void* getDdObject(IDirectDraw7&); + DDSURFACEDESC2 getDisplayMode(CompatRef dd) + { + DDSURFACEDESC2 dm = {}; + dm.dwSize = sizeof(dm); + dd->GetDisplayMode(&dd, &dm); + return dm; + } + template void DirectDraw::setCompatVtable(Vtable& vtable) { vtable.CreateSurface = &CreateSurface; vtable.FlipToGDISurface = &FlipToGDISurface; - vtable.GetDisplayMode = &GetDisplayMode; vtable.GetGDISurface = &GetGDISurface; - vtable.RestoreDisplayMode = &RestoreDisplayMode; vtable.SetCooperativeLevel = &SetCooperativeLevel; vtable.SetDisplayMode = &SetDisplayMode; } @@ -64,24 +144,6 @@ namespace DDraw return PrimarySurface::flipToGdiSurface(); } - template - HRESULT STDMETHODCALLTYPE DirectDraw::GetDisplayMode( - TDirectDraw* This, TSurfaceDesc* lpDDSurfaceDesc) - { - const DWORD size = lpDDSurfaceDesc ? lpDDSurfaceDesc->dwSize : 0; - if (sizeof(DDSURFACEDESC) != size && sizeof(DDSURFACEDESC2) != size) - { - return DDERR_INVALIDPARAMS; - } - - CompatPtr dd(Compat::queryInterface(This)); - const DDSURFACEDESC2 dm = DisplayMode::getDisplayMode(*dd); - CopyMemory(lpDDSurfaceDesc, &dm, size); - lpDDSurfaceDesc->dwSize = size; - - return DD_OK; - } - template HRESULT STDMETHODCALLTYPE DirectDraw::GetGDISurface( TDirectDraw* /*This*/, TSurface** lplpGDIDDSSurface) @@ -101,13 +163,6 @@ namespace DDraw return DD_OK; } - template - HRESULT STDMETHODCALLTYPE DirectDraw::RestoreDisplayMode(TDirectDraw* This) - { - CompatPtr dd(Compat::queryInterface(This)); - return DisplayMode::restoreDisplayMode(*dd); - } - template HRESULT STDMETHODCALLTYPE DirectDraw::SetCooperativeLevel( TDirectDraw* This, HWND hWnd, DWORD dwFlags) @@ -138,8 +193,7 @@ namespace DDraw DWORD dwBPP, Params... params) { - CompatPtr dd(Compat::queryInterface(This)); - return DisplayMode::setDisplayMode(*dd, dwWidth, dwHeight, dwBPP, params...); + return setDisplayMode(This, dwWidth, dwHeight, dwBPP, params...); } template DirectDraw; diff --git a/DDrawCompat/DDraw/DirectDraw.h b/DDrawCompat/DDraw/DirectDraw.h index 89186fb..041b588 100644 --- a/DDrawCompat/DDraw/DirectDraw.h +++ b/DDrawCompat/DDraw/DirectDraw.h @@ -1,14 +1,24 @@ #pragma once +#define CINTERFACE + +#include + +#include "Common/CompatPtr.h" +#include "Common/CompatRef.h" #include "Common/CompatVtable.h" #include "DDraw/Visitors/DirectDrawVtblVisitor.h" #include "DDraw/Types.h" namespace DDraw { + CompatPtr createCompatibleSurface(DWORD bpp); + template void* getDdObject(TDirectDraw& dd); + DDSURFACEDESC2 getDisplayMode(CompatRef dd); + template class DirectDraw: public CompatVtable> { @@ -25,9 +35,7 @@ namespace DDraw IUnknown* pUnkOuter); static HRESULT STDMETHODCALLTYPE FlipToGDISurface(TDirectDraw* This); - static HRESULT STDMETHODCALLTYPE GetDisplayMode(TDirectDraw* This, TSurfaceDesc* lpDDSurfaceDesc); static HRESULT STDMETHODCALLTYPE GetGDISurface(TDirectDraw* This, TSurface** lplpGDIDDSSurface); - static HRESULT STDMETHODCALLTYPE RestoreDisplayMode(TDirectDraw* This); static HRESULT STDMETHODCALLTYPE SetCooperativeLevel(TDirectDraw* This, HWND hWnd, DWORD dwFlags); template diff --git a/DDrawCompat/DDraw/DisplayMode.cpp b/DDrawCompat/DDraw/DisplayMode.cpp deleted file mode 100644 index 8fad834..0000000 --- a/DDrawCompat/DDraw/DisplayMode.cpp +++ /dev/null @@ -1,251 +0,0 @@ -#include "Common/CompatPtr.h" -#include "Common/Hook.h" -#include "DDraw/DisplayMode.h" -#include "DDraw/Repository.h" -#include "Dll/Procs.h" - -namespace -{ - CompatWeakPtr g_compatibleSurface = {}; - HDC g_compatibleDc = nullptr; - DDSURFACEDESC2 g_emulatedDisplayMode = {}; - - template - LONG changeDisplaySettingsEx( - ChangeDisplaySettingsExPtr origChangeDisplaySettings, - EnumDisplaySettingsPtr origEnumDisplaySettings, - CStr lpszDeviceName, DevMode* lpDevMode, HWND hwnd, DWORD dwflags, LPVOID lParam) - { - DevMode targetDevMode = {}; - if (lpDevMode) - { - targetDevMode = *lpDevMode; - } - else - { - targetDevMode.dmSize = sizeof(targetDevMode); - origEnumDisplaySettings(lpszDeviceName, ENUM_REGISTRY_SETTINGS, &targetDevMode); - } - - if (targetDevMode.dmPelsWidth) - { - DevMode currentDevMode = {}; - currentDevMode.dmSize = sizeof(currentDevMode); - origEnumDisplaySettings(lpszDeviceName, ENUM_CURRENT_SETTINGS, ¤tDevMode); - - if (targetDevMode.dmPelsWidth == currentDevMode.dmPelsWidth && - targetDevMode.dmPelsHeight == currentDevMode.dmPelsHeight && - targetDevMode.dmBitsPerPel == currentDevMode.dmBitsPerPel && - targetDevMode.dmDisplayFrequency == currentDevMode.dmDisplayFrequency && - targetDevMode.dmDisplayFlags == currentDevMode.dmDisplayFlags) - { - LONG result = origChangeDisplaySettings( - lpszDeviceName, lpDevMode, hwnd, dwflags, lParam); - if (SUCCEEDED(result)) - { - HANDLE dwmDxFullScreenTransitionEvent = OpenEventW( - EVENT_MODIFY_STATE, FALSE, L"DWM_DX_FULLSCREEN_TRANSITION_EVENT"); - SetEvent(dwmDxFullScreenTransitionEvent); - CloseHandle(dwmDxFullScreenTransitionEvent); - } - return result; - } - } - - return origChangeDisplaySettings(lpszDeviceName, lpDevMode, hwnd, dwflags, lParam); - } - - LONG WINAPI changeDisplaySettingsExA( - LPCSTR lpszDeviceName, DEVMODEA* lpDevMode, HWND hwnd, DWORD dwflags, LPVOID lParam) - { - return changeDisplaySettingsEx(CALL_ORIG_FUNC(ChangeDisplaySettingsExA), - CALL_ORIG_FUNC(EnumDisplaySettingsA), lpszDeviceName, lpDevMode, hwnd, dwflags, lParam); - } - - LONG WINAPI changeDisplaySettingsExW( - LPCWSTR lpszDeviceName, DEVMODEW* lpDevMode, HWND hwnd, DWORD dwflags, LPVOID lParam) - { - return changeDisplaySettingsEx(CALL_ORIG_FUNC(ChangeDisplaySettingsExW), - CALL_ORIG_FUNC(EnumDisplaySettingsW), lpszDeviceName, lpDevMode, hwnd, dwflags, lParam); - } - - CompatPtr createCompatibleSurface() - { - DDSURFACEDESC2 desc = {}; - desc.dwSize = sizeof(desc); - desc.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT | DDSD_CAPS; - desc.dwWidth = 1; - desc.dwHeight = 1; - desc.ddpfPixelFormat = g_emulatedDisplayMode.ddpfPixelFormat; - desc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY; - - auto dd = DDraw::Repository::getDirectDraw(); - CompatPtr surface; - dd->CreateSurface(dd, &desc, &surface.getRef(), nullptr); - return surface; - } - - template - HRESULT PASCAL enumDisplayModesCallback( - TSurfaceDesc* lpDDSurfaceDesc, - LPVOID lpContext) - { - if (lpDDSurfaceDesc) - { - *static_cast(lpContext) = lpDDSurfaceDesc->ddpfPixelFormat; - } - return DDENUMRET_CANCEL; - } - - DDPIXELFORMAT getDisplayModePixelFormat( - CompatRef dd, DWORD width, DWORD height, DWORD bpp) - { - DDSURFACEDESC2 desc = {}; - desc.ddpfPixelFormat.dwSize = sizeof(desc.ddpfPixelFormat); - desc.ddpfPixelFormat.dwFlags = DDPF_RGB; - desc.ddpfPixelFormat.dwRGBBitCount = bpp; - - if (bpp <= 8) - { - switch (bpp) - { - case 1: desc.ddpfPixelFormat.dwFlags |= DDPF_PALETTEINDEXED1; break; - case 2: desc.ddpfPixelFormat.dwFlags |= DDPF_PALETTEINDEXED2; break; - case 4: desc.ddpfPixelFormat.dwFlags |= DDPF_PALETTEINDEXED4; break; - case 8: desc.ddpfPixelFormat.dwFlags |= DDPF_PALETTEINDEXED8; break; - default: return DDPIXELFORMAT(); - } - return desc.ddpfPixelFormat; - } - - desc.dwSize = sizeof(desc); - desc.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT; - desc.dwWidth = width; - desc.dwHeight = height; - - DDPIXELFORMAT pf = {}; - if (FAILED(dd->EnumDisplayModes(&dd, 0, &desc, &pf, &enumDisplayModesCallback)) || - 0 == pf.dwSize) - { - Compat::Log() << "Failed to find the requested display mode: " << - width << "x" << height << "x" << bpp; - } - - return pf; - } - - DDSURFACEDESC2 getRealDisplayMode(CompatRef dd) - { - DDSURFACEDESC2 desc = {}; - desc.dwSize = sizeof(desc); - dd->GetDisplayMode(&dd, &desc); - return desc; - } - - void releaseCompatibleDc() - { - if (g_compatibleDc) - { - Dll::g_origProcs.AcquireDDThreadLock(); - g_compatibleSurface->ReleaseDC(g_compatibleSurface, g_compatibleDc); - g_compatibleDc = nullptr; - g_compatibleSurface.release(); - } - } - - void replaceDc(HDC& hdc) - { - if (g_compatibleDc && hdc && OBJ_DC == GetObjectType(hdc) && - DT_RASDISPLAY == GetDeviceCaps(hdc, TECHNOLOGY)) - { - hdc = g_compatibleDc; - } - } - - void updateCompatibleDc() - { - releaseCompatibleDc(); - g_compatibleSurface = createCompatibleSurface().detach(); - if (g_compatibleSurface && - SUCCEEDED(g_compatibleSurface->GetDC(g_compatibleSurface, &g_compatibleDc))) - { - Dll::g_origProcs.ReleaseDDThreadLock(); - } - } -} - -namespace DDraw -{ - namespace DisplayMode - { - HBITMAP WINAPI createCompatibleBitmap(HDC hdc, int cx, int cy) - { - replaceDc(hdc); - return CALL_ORIG_FUNC(CreateCompatibleBitmap)(hdc, cx, cy); - } - - HBITMAP WINAPI createDIBitmap(HDC hdc, const BITMAPINFOHEADER* lpbmih, DWORD fdwInit, - const void* lpbInit, const BITMAPINFO* lpbmi, UINT fuUsage) - { - replaceDc(hdc); - return CALL_ORIG_FUNC(CreateDIBitmap)(hdc, lpbmih, fdwInit, lpbInit, lpbmi, fuUsage); - } - - HBITMAP WINAPI createDiscardableBitmap(HDC hdc, int nWidth, int nHeight) - { - replaceDc(hdc); - return CALL_ORIG_FUNC(createDiscardableBitmap)(hdc, nWidth, nHeight); - } - - DDSURFACEDESC2 getDisplayMode(CompatRef dd) - { - if (0 == g_emulatedDisplayMode.dwSize) - { - g_emulatedDisplayMode = getRealDisplayMode(dd); - } - return g_emulatedDisplayMode; - } - - void installHooks() - { - HOOK_FUNCTION(user32, ChangeDisplaySettingsExA, changeDisplaySettingsExA); - HOOK_FUNCTION(user32, ChangeDisplaySettingsExW, changeDisplaySettingsExW); - } - - HRESULT restoreDisplayMode(CompatRef dd) - { - const HRESULT result = dd->RestoreDisplayMode(&dd); - if (SUCCEEDED(result)) - { - ZeroMemory(&g_emulatedDisplayMode, sizeof(g_emulatedDisplayMode)); - releaseCompatibleDc(); - } - return result; - } - - HRESULT setDisplayMode(CompatRef dd, - DWORD width, DWORD height, DWORD bpp, DWORD refreshRate, DWORD flags) - { - DDPIXELFORMAT pf = getDisplayModePixelFormat(dd, width, height, bpp); - if (0 == pf.dwSize) - { - return DDERR_INVALIDMODE; - } - - const HRESULT result = dd->SetDisplayMode(&dd, width, height, 32, refreshRate, flags); - if (FAILED(result)) - { - Compat::Log() << "Failed to set the display mode to " << width << "x" << height << - "x" << bpp << " (" << std::hex << result << std::dec << ')'; - return result; - } - - g_emulatedDisplayMode = getRealDisplayMode(dd); - g_emulatedDisplayMode.ddpfPixelFormat = pf; - updateCompatibleDc(); - - return DD_OK; - } - } -} diff --git a/DDrawCompat/DDraw/DisplayMode.h b/DDrawCompat/DDraw/DisplayMode.h deleted file mode 100644 index 67b09e1..0000000 --- a/DDrawCompat/DDraw/DisplayMode.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#define CINTERFACE - -#include - -#include "Common/CompatRef.h" - -namespace DDraw -{ - namespace DisplayMode - { - void installHooks(); - - HBITMAP WINAPI createCompatibleBitmap(HDC hdc, int cx, int cy); - HBITMAP WINAPI createDIBitmap(HDC hdc, const BITMAPINFOHEADER* lpbmih, DWORD fdwInit, - const void* lpbInit, const BITMAPINFO* lpbmi, UINT fuUsage); - HBITMAP WINAPI createDiscardableBitmap(HDC hdc, int nWidth, int nHeight); - - DDSURFACEDESC2 getDisplayMode(CompatRef dd); - HRESULT restoreDisplayMode(CompatRef dd); - HRESULT setDisplayMode(CompatRef dd, - DWORD width, DWORD height, DWORD bpp, DWORD refreshRate = 0, DWORD flags = 0); - }; -} diff --git a/DDrawCompat/DDraw/RealPrimarySurface.cpp b/DDrawCompat/DDraw/RealPrimarySurface.cpp index cbb5966..9eb6e7d 100644 --- a/DDrawCompat/DDraw/RealPrimarySurface.cpp +++ b/DDrawCompat/DDraw/RealPrimarySurface.cpp @@ -4,14 +4,15 @@ #include "Common/Hook.h" #include "Common/Time.h" #include "Config/Config.h" +#include "DDraw/DirectDraw.h" #include "DDraw/DirectDrawSurface.h" -#include "DDraw/DisplayMode.h" #include "DDraw/IReleaseNotifier.h" #include "DDraw/RealPrimarySurface.h" #include "DDraw/ScopedThreadLock.h" #include "DDraw/Surfaces/PrimarySurface.h" #include "DDraw/Types.h" #include "Gdi/Gdi.h" +#include "Win32/DisplayMode.h" namespace { @@ -90,8 +91,7 @@ namespace template HRESULT createPaletteConverter(CompatRef dd) { - auto dd7(CompatPtr::from(&dd)); - auto dm = DDraw::DisplayMode::getDisplayMode(*dd7); + auto dm = DDraw::getDisplayMode(*CompatPtr::from(&dd)); if (dm.ddpfPixelFormat.dwRGBBitCount > 8) { return DD_OK; diff --git a/DDrawCompat/DDraw/Surfaces/PrimarySurface.cpp b/DDrawCompat/DDraw/Surfaces/PrimarySurface.cpp index 7742e54..1ad1db6 100644 --- a/DDrawCompat/DDraw/Surfaces/PrimarySurface.cpp +++ b/DDrawCompat/DDraw/Surfaces/PrimarySurface.cpp @@ -1,5 +1,5 @@ #include "Common/CompatPtr.h" -#include "DDraw/DisplayMode.h" +#include "DDraw/DirectDraw.h" #include "DDraw/RealPrimarySurface.h" #include "DDraw/Surfaces/PrimarySurface.h" #include "DDraw/Surfaces/PrimarySurfaceImpl.h" @@ -42,8 +42,7 @@ namespace DDraw return result; } - CompatPtr dd7(Compat::queryInterface(&dd)); - const auto& dm = DisplayMode::getDisplayMode(*dd7); + const auto& dm = DDraw::getDisplayMode(*CompatPtr::from(&dd)); desc.dwFlags |= DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT; desc.dwWidth = dm.dwWidth; desc.dwHeight = dm.dwHeight; diff --git a/DDrawCompat/DDraw/Surfaces/Surface.cpp b/DDrawCompat/DDraw/Surfaces/Surface.cpp index 5048e2a..7ef3d1a 100644 --- a/DDrawCompat/DDraw/Surfaces/Surface.cpp +++ b/DDrawCompat/DDraw/Surfaces/Surface.cpp @@ -3,7 +3,6 @@ #include "Common/CompatPtr.h" #include "DDraw/DirectDraw.h" #include "DDraw/DirectDrawSurface.h" -#include "DDraw/DisplayMode.h" #include "DDraw/Surfaces/Surface.h" #include "DDraw/Surfaces/SurfaceImpl.h" @@ -44,7 +43,7 @@ namespace { if (!(flags & DDSD_PIXELFORMAT)) { - auto dm = DDraw::DisplayMode::getDisplayMode(dd); + auto dm = DDraw::getDisplayMode(dd); flags |= DDSD_PIXELFORMAT; pf = dm.ddpfPixelFormat; } diff --git a/DDrawCompat/DDrawCompat.vcxproj b/DDrawCompat/DDrawCompat.vcxproj index ba16c3c..aa04093 100644 --- a/DDrawCompat/DDrawCompat.vcxproj +++ b/DDrawCompat/DDrawCompat.vcxproj @@ -90,7 +90,7 @@ Level4 - WIN32;_DEBUG;_WINDOWS;_USRDLL;DDRAWCOMPAT_EXPORTS;%(PreprocessorDefinitions) + PSAPI_VERSION=1;WIN32;_DEBUG;_WINDOWS;_USRDLL;DDRAWCOMPAT_EXPORTS;%(PreprocessorDefinitions) MultiThreadedDebug true false @@ -99,7 +99,7 @@ Dll/DDrawCompat.def - dxguid.lib;detours.lib;msimg32.lib;oleacc.lib;uxtheme.lib;dwmapi.lib;winmm.lib;%(AdditionalDependencies) + dxguid.lib;detours.lib;msimg32.lib;oleacc.lib;psapi.lib;uxtheme.lib;dwmapi.lib;winmm.lib;%(AdditionalDependencies) UseLinkTimeCodeGeneration @@ -122,14 +122,14 @@ Level4 - WIN32;NDEBUG;_WINDOWS;_USRDLL;DDRAWCOMPAT_EXPORTS;%(PreprocessorDefinitions) + PSAPI_VERSION=1;WIN32;NDEBUG;_WINDOWS;_USRDLL;DDRAWCOMPAT_EXPORTS;%(PreprocessorDefinitions) MultiThreaded true $(IntDir)%(RelativeDir) Dll/DDrawCompat.def - dxguid.lib;detours.lib;msimg32.lib;oleacc.lib;uxtheme.lib;dwmapi.lib;winmm.lib;%(AdditionalDependencies) + dxguid.lib;detours.lib;msimg32.lib;oleacc.lib;psapi.lib;uxtheme.lib;dwmapi.lib;winmm.lib;%(AdditionalDependencies) No UseLinkTimeCodeGeneration @@ -185,7 +185,6 @@ - @@ -225,6 +224,7 @@ + @@ -250,7 +250,6 @@ - @@ -280,6 +279,7 @@ + diff --git a/DDrawCompat/DDrawCompat.vcxproj.filters b/DDrawCompat/DDrawCompat.vcxproj.filters index 0923468..2f2d03b 100644 --- a/DDrawCompat/DDrawCompat.vcxproj.filters +++ b/DDrawCompat/DDrawCompat.vcxproj.filters @@ -120,9 +120,6 @@ Header Files\DDraw - - Header Files\DDraw - Header Files\DDraw @@ -306,6 +303,9 @@ Header Files\DDraw + + Header Files\Win32 + @@ -347,9 +347,6 @@ Source Files\DDraw - - Source Files\DDraw - Source Files\DDraw @@ -467,6 +464,9 @@ Source Files\DDraw + + Source Files\Win32 + diff --git a/DDrawCompat/Dll/DllMain.cpp b/DDrawCompat/Dll/DllMain.cpp index 00376df..d56783d 100644 --- a/DDrawCompat/Dll/DllMain.cpp +++ b/DDrawCompat/Dll/DllMain.cpp @@ -5,13 +5,15 @@ #include #include +#include "Common/Hook.h" +#include "Common/Log.h" #include "Common/Time.h" #include "D3dDdi/Hooks.h" -#include "DDraw/DisplayMode.h" #include "DDraw/Hooks.h" #include "Direct3d/Hooks.h" #include "Dll/Procs.h" #include "Gdi/Gdi.h" +#include "Win32/DisplayMode.h" #include "Win32/FontSmoothing.h" #include "Win32/MsgHooks.h" #include "Win32/Registry.h" @@ -30,6 +32,7 @@ namespace static bool isAlreadyInstalled = false; if (!isAlreadyInstalled) { + Win32::DisplayMode::disableDwm8And16BitMitigation(); Compat::Log() << "Installing registry hooks"; Win32::Registry::installHooks(); Compat::Log() << "Installing Direct3D driver hooks"; @@ -40,6 +43,8 @@ namespace Direct3d::installHooks(); Compat::Log() << "Installing GDI hooks"; Gdi::installHooks(); + Compat::Log() << "Installing display mode hooks"; + Win32::DisplayMode::installHooks(g_origDDrawModule); Compat::Log() << "Finished installing hooks"; isAlreadyInstalled = true; } @@ -82,7 +87,7 @@ namespace } #define LOAD_ORIGINAL_PROC(procName) \ - Dll::g_origProcs.procName = GetProcAddress(g_origDDrawModule, #procName); + Dll::g_origProcs.procName = Compat::getProcAddress(g_origDDrawModule, #procName); BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID /*lpvReserved*/) { @@ -122,7 +127,10 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID /*lpvReserved*/) SetProcessAffinityMask(GetCurrentProcess(), 1); SetThemeAppProperties(0); - DDraw::DisplayMode::installHooks(); + Compat::redirectIatHooks("ddraw.dll", "DirectDrawCreate", + Compat::getProcAddress(hinstDLL, "DirectDrawCreate")); + Compat::redirectIatHooks("ddraw.dll", "DirectDrawCreateEx", + Compat::getProcAddress(hinstDLL, "DirectDrawCreateEx")); Win32::FontSmoothing::g_origSystemSettings = Win32::FontSmoothing::getSystemSettings(); Win32::MsgHooks::installHooks(); Time::init(); diff --git a/DDrawCompat/Gdi/DcFunctions.cpp b/DDrawCompat/Gdi/DcFunctions.cpp index c381bb7..e4cb20a 100644 --- a/DDrawCompat/Gdi/DcFunctions.cpp +++ b/DDrawCompat/Gdi/DcFunctions.cpp @@ -2,10 +2,10 @@ #include "Common/Hook.h" #include "Common/Log.h" -#include "DDraw/DisplayMode.h" #include "Gdi/Dc.h" #include "Gdi/DcFunctions.h" #include "Gdi/Gdi.h" +#include "Win32/DisplayMode.h" namespace { @@ -307,9 +307,9 @@ namespace Gdi // Bitmap functions HOOK_GDI_DC_FUNCTION(msimg32, AlphaBlend); HOOK_GDI_DC_FUNCTION(gdi32, BitBlt); - HOOK_FUNCTION(gdi32, CreateCompatibleBitmap, DDraw::DisplayMode::createCompatibleBitmap); - HOOK_FUNCTION(gdi32, CreateDIBitmap, DDraw::DisplayMode::createDIBitmap); - HOOK_FUNCTION(gdi32, CreateDiscardableBitmap, DDraw::DisplayMode::createDiscardableBitmap); + HOOK_FUNCTION(gdi32, CreateCompatibleBitmap, Win32::DisplayMode::createCompatibleBitmap); + HOOK_FUNCTION(gdi32, CreateDIBitmap, Win32::DisplayMode::createDIBitmap); + HOOK_FUNCTION(gdi32, CreateDiscardableBitmap, Win32::DisplayMode::createDiscardableBitmap); HOOK_GDI_DC_FUNCTION(gdi32, ExtFloodFill); HOOK_GDI_DC_FUNCTION(gdi32, GdiAlphaBlend); HOOK_GDI_DC_FUNCTION(gdi32, GdiGradientFill); diff --git a/DDrawCompat/Win32/DisplayMode.cpp b/DDrawCompat/Win32/DisplayMode.cpp new file mode 100644 index 0000000..ef3770b --- /dev/null +++ b/DDrawCompat/Win32/DisplayMode.cpp @@ -0,0 +1,413 @@ +#include +#include + +#include "Common/CompatPtr.h" +#include "Common/Hook.h" +#include "DDraw/DirectDraw.h" +#include "Win32/DisplayMode.h" + +BOOL WINAPI DWM8And16Bit_IsShimApplied_CallOut() { return FALSE; }; + +namespace +{ + template + struct EnumParams + { + std::basic_string deviceName; + DWORD flags; + + bool operator==(const EnumParams& other) const + { + return deviceName == other.deviceName && flags == other.flags; + } + + bool operator!=(const EnumParams& other) const + { + return !(*this == other); + } + }; + + CompatWeakPtr g_compatibleSurface = {}; + HDC g_compatibleDc = nullptr; + DWORD g_origBpp = 0; + DWORD g_currentBpp = 0; + DWORD g_lastBpp = 0; + DWORD g_ddrawBpp = 0; + + BOOL WINAPI enumDisplaySettingsExA( + LPCSTR lpszDeviceName, DWORD iModeNum, DEVMODEA* lpDevMode, DWORD dwFlags); + BOOL WINAPI enumDisplaySettingsExW( + LPCWSTR lpszDeviceName, DWORD iModeNum, DEVMODEW* lpDevMode, DWORD dwFlags); + void updateCompatibleDc(); + + template + LONG changeDisplaySettingsEx( + ChangeDisplaySettingsExFunc origChangeDisplaySettingsEx, + EnumDisplaySettingsExFunc origEnumDisplaySettingsEx, + CStr lpszDeviceName, DevMode* lpDevMode, HWND hwnd, DWORD dwflags, LPVOID lParam) + { + DevMode prevDevMode = {}; + if (!(dwflags & CDS_TEST)) + { + prevDevMode.dmSize = sizeof(prevDevMode); + origEnumDisplaySettingsEx(lpszDeviceName, ENUM_CURRENT_SETTINGS, &prevDevMode, 0); + } + + BOOL result = FALSE; + if (lpDevMode) + { + DWORD origBpp = lpDevMode->dmBitsPerPel; + lpDevMode->dmBitsPerPel = 32; + result = origChangeDisplaySettingsEx(lpszDeviceName, lpDevMode, hwnd, dwflags, lParam); + lpDevMode->dmBitsPerPel = origBpp; + } + else + { + result = origChangeDisplaySettingsEx(lpszDeviceName, lpDevMode, hwnd, dwflags, lParam); + } + + if (SUCCEEDED(result) && !(dwflags & CDS_TEST)) + { + if (lpDevMode) + { + g_currentBpp = lpDevMode->dmBitsPerPel; + g_lastBpp = lpDevMode->dmBitsPerPel; + } + else + { + g_currentBpp = g_origBpp; + } + updateCompatibleDc(); + + DevMode currDevMode = {}; + currDevMode.dmSize = sizeof(currDevMode); + origEnumDisplaySettingsEx(lpszDeviceName, ENUM_CURRENT_SETTINGS, &currDevMode, 0); + + if (currDevMode.dmPelsWidth == prevDevMode.dmPelsWidth && + currDevMode.dmPelsHeight == prevDevMode.dmPelsHeight && + currDevMode.dmBitsPerPel == prevDevMode.dmBitsPerPel && + currDevMode.dmDisplayFrequency == prevDevMode.dmDisplayFrequency && + currDevMode.dmDisplayFlags == prevDevMode.dmDisplayFlags) + { + HANDLE dwmDxFullScreenTransitionEvent = OpenEventW( + EVENT_MODIFY_STATE, FALSE, L"DWM_DX_FULLSCREEN_TRANSITION_EVENT"); + SetEvent(dwmDxFullScreenTransitionEvent); + CloseHandle(dwmDxFullScreenTransitionEvent); + } + } + + return result; + } + + LONG WINAPI changeDisplaySettingsExA( + LPCSTR lpszDeviceName, DEVMODEA* lpDevMode, HWND hwnd, DWORD dwflags, LPVOID lParam) + { + Compat::LogEnter("ChangeDisplaySettingsExA", lpszDeviceName, lpDevMode, hwnd, dwflags, lParam); + LONG result = changeDisplaySettingsEx( + CALL_ORIG_FUNC(ChangeDisplaySettingsExA), + CALL_ORIG_FUNC(EnumDisplaySettingsExA), + lpszDeviceName, lpDevMode, hwnd, dwflags, lParam); + Compat::LogLeave("ChangeDisplaySettingsExA", lpszDeviceName, lpDevMode, hwnd, dwflags, lParam) << result; + return result; + } + + LONG WINAPI changeDisplaySettingsExW( + LPCWSTR lpszDeviceName, DEVMODEW* lpDevMode, HWND hwnd, DWORD dwflags, LPVOID lParam) + { + Compat::LogEnter("ChangeDisplaySettingsExW", lpszDeviceName, lpDevMode, hwnd, dwflags, lParam); + LONG result = changeDisplaySettingsEx( + CALL_ORIG_FUNC(ChangeDisplaySettingsExW), + CALL_ORIG_FUNC(EnumDisplaySettingsExW), + lpszDeviceName, lpDevMode, hwnd, dwflags, lParam); + Compat::LogLeave("ChangeDisplaySettingsExW", lpszDeviceName, lpDevMode, hwnd, dwflags, lParam) << result; + return result; + } + + template + LONG ddrawChangeDisplaySettingsEx( + ChangeDisplaySettingsExFunc changeDisplaySettingsEx, + CStr lpszDeviceName, DevMode* lpDevMode, HWND hwnd, DWORD dwflags, LPVOID lParam) + { + if (lpDevMode && 0 != lpDevMode->dmBitsPerPel) + { + lpDevMode->dmBitsPerPel = (0 != g_ddrawBpp) ? g_ddrawBpp : g_lastBpp; + } + return changeDisplaySettingsEx(lpszDeviceName, lpDevMode, hwnd, dwflags, lParam); + } + + LONG WINAPI ddrawChangeDisplaySettingsA( + DEVMODEA* lpDevMode, DWORD dwflags) + { + return ddrawChangeDisplaySettingsEx(&changeDisplaySettingsExA, + nullptr, lpDevMode, nullptr, dwflags, nullptr); + } + + LONG WINAPI ddrawChangeDisplaySettingsW( + DEVMODEW* lpDevMode, DWORD dwflags) + { + return ddrawChangeDisplaySettingsEx(&changeDisplaySettingsExW, + nullptr, lpDevMode, nullptr, dwflags, nullptr); + } + + LONG WINAPI ddrawChangeDisplaySettingsExA( + LPCSTR lpszDeviceName, DEVMODEA* lpDevMode, HWND hwnd, DWORD dwflags, LPVOID lParam) + { + return ddrawChangeDisplaySettingsEx(&changeDisplaySettingsExA, + lpszDeviceName, lpDevMode, hwnd, dwflags, lParam); + } + + LONG WINAPI ddrawChangeDisplaySettingsExW( + LPCWSTR lpszDeviceName, DEVMODEW* lpDevMode, HWND hwnd, DWORD dwflags, LPVOID lParam) + { + return ddrawChangeDisplaySettingsEx(&changeDisplaySettingsExW, + lpszDeviceName, lpDevMode, hwnd, dwflags, lParam); + } + + template + BOOL WINAPI ddrawEnumDisplaySettingsEx( + EnumDisplaySettingsExFunc origEnumDisplaySettingsEx, + EnumDisplaySettingsExFunc enumDisplaySettingsEx, + CStr lpszDeviceName, DWORD iModeNum, DevMode* lpDevMode, DWORD dwFlags) + { + if (ENUM_CURRENT_SETTINGS == iModeNum) + { + return origEnumDisplaySettingsEx(lpszDeviceName, iModeNum, lpDevMode, dwFlags); + } + return enumDisplaySettingsEx(lpszDeviceName, iModeNum, lpDevMode, dwFlags); + } + + BOOL WINAPI ddrawEnumDisplaySettingsA(LPCSTR lpszDeviceName, DWORD iModeNum, DEVMODEA* lpDevMode) + { + return ddrawEnumDisplaySettingsEx(CALL_ORIG_FUNC(EnumDisplaySettingsExA), &enumDisplaySettingsExA, + lpszDeviceName, iModeNum, lpDevMode, 0); + } + + BOOL WINAPI ddrawEnumDisplaySettingsW(LPCWSTR lpszDeviceName, DWORD iModeNum, DEVMODEW* lpDevMode) + { + return ddrawEnumDisplaySettingsEx(CALL_ORIG_FUNC(EnumDisplaySettingsExW), &enumDisplaySettingsExW, + lpszDeviceName, iModeNum, lpDevMode, 0); + } + + BOOL WINAPI ddrawEnumDisplaySettingsExA( + LPCSTR lpszDeviceName, DWORD iModeNum, DEVMODEA* lpDevMode, DWORD dwFlags) + { + return ddrawEnumDisplaySettingsEx(CALL_ORIG_FUNC(EnumDisplaySettingsExA), &enumDisplaySettingsExA, + lpszDeviceName, iModeNum, lpDevMode, dwFlags); + } + + BOOL WINAPI ddrawEnumDisplaySettingsExW( + LPCWSTR lpszDeviceName, DWORD iModeNum, DEVMODEW* lpDevMode, DWORD dwFlags) + { + return ddrawEnumDisplaySettingsEx(CALL_ORIG_FUNC(EnumDisplaySettingsExW), &enumDisplaySettingsExW, + lpszDeviceName, iModeNum, lpDevMode, dwFlags); + } + + BOOL WINAPI dwm8And16BitIsShimAppliedCallOut() + { + return FALSE; + } + + template + BOOL enumDisplaySettingsEx(EnumDisplaySettingsExFunc origEnumDisplaySettingsEx, + const Char* lpszDeviceName, DWORD iModeNum, DevMode* lpDevMode, DWORD dwFlags) + { + if (ENUM_REGISTRY_SETTINGS == iModeNum || !lpDevMode) + { + BOOL result = origEnumDisplaySettingsEx(lpszDeviceName, iModeNum, lpDevMode, dwFlags); + return result; + } + + if (ENUM_CURRENT_SETTINGS == iModeNum) + { + BOOL result = origEnumDisplaySettingsEx(lpszDeviceName, iModeNum, lpDevMode, dwFlags); + if (result) + { + lpDevMode->dmBitsPerPel = g_currentBpp; + } + return result; + } + + thread_local std::vector devModes; + thread_local EnumParams lastEnumParams = {}; + + EnumParams currentEnumParams = { + lpszDeviceName ? lpszDeviceName : std::basic_string(), dwFlags }; + + if (0 == iModeNum || devModes.empty() || currentEnumParams != lastEnumParams) + { + devModes.clear(); + lastEnumParams = currentEnumParams; + + DWORD modeNum = 0; + DevMode dm = {}; + dm.dmSize = sizeof(dm); + while (origEnumDisplaySettingsEx(lpszDeviceName, modeNum, &dm, dwFlags)) + { + if (32 == dm.dmBitsPerPel) + { + dm.dmBitsPerPel = 8; + devModes.push_back(dm); + dm.dmBitsPerPel = 16; + devModes.push_back(dm); + dm.dmBitsPerPel = 32; + devModes.push_back(dm); + } + ++modeNum; + } + } + + if (iModeNum >= devModes.size()) + { + return FALSE; + } + + *lpDevMode = devModes[iModeNum]; + return TRUE; + } + + BOOL WINAPI enumDisplaySettingsExA( + LPCSTR lpszDeviceName, DWORD iModeNum, DEVMODEA* lpDevMode, DWORD dwFlags) + { + Compat::LogEnter("EnumDisplaySettingsExA", lpszDeviceName, iModeNum, lpDevMode, dwFlags); + BOOL result = enumDisplaySettingsEx(CALL_ORIG_FUNC(EnumDisplaySettingsExA), + lpszDeviceName, iModeNum, lpDevMode, dwFlags); + Compat::LogLeave("EnumDisplaySettingsExA", lpszDeviceName, iModeNum, lpDevMode, dwFlags) << result; + return result; + } + + BOOL WINAPI enumDisplaySettingsExW( + LPCWSTR lpszDeviceName, DWORD iModeNum, DEVMODEW* lpDevMode, DWORD dwFlags) + { + Compat::LogEnter("EnumDisplaySettingsExW", lpszDeviceName, iModeNum, lpDevMode, dwFlags); + BOOL result = enumDisplaySettingsEx(CALL_ORIG_FUNC(EnumDisplaySettingsExW), + lpszDeviceName, iModeNum, lpDevMode, dwFlags); + Compat::LogLeave("EnumDisplaySettingsExW", lpszDeviceName, iModeNum, lpDevMode, dwFlags) << result; + return result; + } + + int WINAPI getDeviceCaps(HDC hdc, int nIndex) + { + Compat::LogEnter("GetDeviceCaps", hdc, nIndex); + if (hdc && BITSPIXEL == nIndex && + DT_RASDISPLAY == GetDeviceCaps(hdc, TECHNOLOGY) && OBJ_DC == GetObjectType(hdc)) + { + Compat::LogLeave("GetDeviceCaps", hdc, nIndex) << g_currentBpp; + return g_currentBpp; + } + int result = CALL_ORIG_FUNC(GetDeviceCaps)(hdc, nIndex); + Compat::LogLeave("GetDeviceCaps", hdc, nIndex) << result; + return result; + } + + void releaseCompatibleDc() + { + if (g_compatibleDc) + { + Dll::g_origProcs.AcquireDDThreadLock(); + g_compatibleSurface->ReleaseDC(g_compatibleSurface, g_compatibleDc); + g_compatibleDc = nullptr; + g_compatibleSurface.release(); + } + } + + void replaceDc(HDC& hdc) + { + if (g_compatibleDc && hdc && + OBJ_DC == GetObjectType(hdc) && DT_RASDISPLAY == GetDeviceCaps(hdc, TECHNOLOGY)) + { + hdc = g_compatibleDc; + } + } + + void updateCompatibleDc() + { + releaseCompatibleDc(); + g_compatibleSurface = DDraw::createCompatibleSurface(g_currentBpp).detach(); + if (g_compatibleSurface && + SUCCEEDED(g_compatibleSurface->GetDC(g_compatibleSurface, &g_compatibleDc))) + { + Dll::g_origProcs.ReleaseDDThreadLock(); + } + } +} + +namespace Win32 +{ + namespace DisplayMode + { + HBITMAP WINAPI createCompatibleBitmap(HDC hdc, int cx, int cy) + { + Compat::LogEnter("CreateCompatibleBitmap", hdc, cx, cy); + replaceDc(hdc); + HBITMAP result = CALL_ORIG_FUNC(CreateCompatibleBitmap)(hdc, cx, cy); + Compat::LogLeave("CreateCompatibleBitmap", hdc, cx, cy) << result; + return result; + } + + HBITMAP WINAPI createDIBitmap(HDC hdc, const BITMAPINFOHEADER* lpbmih, DWORD fdwInit, + const void* lpbInit, const BITMAPINFO* lpbmi, UINT fuUsage) + { + Compat::LogEnter("CreateDIBitmap", hdc, lpbmih, fdwInit, lpbInit, lpbmi, fuUsage); + replaceDc(hdc); + HBITMAP result = CALL_ORIG_FUNC(CreateDIBitmap)(hdc, lpbmih, fdwInit, lpbInit, lpbmi, fuUsage); + Compat::LogLeave("CreateDIBitmap", hdc, lpbmih, fdwInit, lpbInit, lpbmi, fuUsage) + << result; + return result; + } + + HBITMAP WINAPI createDiscardableBitmap(HDC hdc, int nWidth, int nHeight) + { + Compat::LogEnter("CreateDiscardableBitmap", hdc, nWidth, nHeight); + replaceDc(hdc); + HBITMAP result = CALL_ORIG_FUNC(createDiscardableBitmap)(hdc, nWidth, nHeight); + Compat::LogLeave("CreateDiscardableBitmap", hdc, nWidth, nHeight) << result; + return result; + } + + void setDDrawBpp(DWORD bpp) + { + g_ddrawBpp = bpp; + } + + void disableDwm8And16BitMitigation() + { + HOOK_FUNCTION(apphelp, DWM8And16Bit_IsShimApplied_CallOut, dwm8And16BitIsShimAppliedCallOut); + } + + void installHooks(HMODULE origDDrawModule) + { + DEVMODEA devMode = {}; + devMode.dmSize = sizeof(devMode); + EnumDisplaySettingsEx(nullptr, ENUM_CURRENT_SETTINGS, &devMode, 0); + g_origBpp = devMode.dmBitsPerPel; + g_currentBpp = g_origBpp; + g_lastBpp = g_origBpp; + + HOOK_FUNCTION(user32, ChangeDisplaySettingsExA, changeDisplaySettingsExA); + HOOK_FUNCTION(user32, ChangeDisplaySettingsExW, changeDisplaySettingsExW); + HOOK_FUNCTION(user32, EnumDisplaySettingsExA, enumDisplaySettingsExA); + HOOK_FUNCTION(user32, EnumDisplaySettingsExW, enumDisplaySettingsExW); + HOOK_FUNCTION(gdi32, GetDeviceCaps, getDeviceCaps); + + Compat::hookIatFunction(origDDrawModule, "user32.dll", "ChangeDisplaySettingsA", + &ddrawChangeDisplaySettingsA); + Compat::hookIatFunction(origDDrawModule, "user32.dll", "ChangeDisplaySettingsW", + &ddrawChangeDisplaySettingsW); + Compat::hookIatFunction(origDDrawModule, "user32.dll", "ChangeDisplaySettingsExA", + &ddrawChangeDisplaySettingsExA); + Compat::hookIatFunction(origDDrawModule, "user32.dll", "ChangeDisplaySettingsExW", + &ddrawChangeDisplaySettingsExW); + Compat::hookIatFunction(origDDrawModule, "user32.dll", "EnumDisplaySettingsA", + &ddrawEnumDisplaySettingsA); + Compat::hookIatFunction(origDDrawModule, "user32.dll", "EnumDisplaySettingsW", + &ddrawEnumDisplaySettingsW); + Compat::hookIatFunction(origDDrawModule, "user32.dll", "EnumDisplaySettingsExA", + &ddrawEnumDisplaySettingsExA); + Compat::hookIatFunction(origDDrawModule, "user32.dll", "EnumDisplaySettingsExW", + &ddrawEnumDisplaySettingsExW); + + updateCompatibleDc(); + } + } +} diff --git a/DDrawCompat/Win32/DisplayMode.h b/DDrawCompat/Win32/DisplayMode.h new file mode 100644 index 0000000..5ea21a3 --- /dev/null +++ b/DDrawCompat/Win32/DisplayMode.h @@ -0,0 +1,21 @@ +#pragma once + +#define WIN32_LEAN_AND_MEAN + +#include + +namespace Win32 +{ + namespace DisplayMode + { + HBITMAP WINAPI createCompatibleBitmap(HDC hdc, int cx, int cy); + HBITMAP WINAPI createDIBitmap(HDC hdc, const BITMAPINFOHEADER* lpbmih, DWORD fdwInit, + const void* lpbInit, const BITMAPINFO* lpbmi, UINT fuUsage); + HBITMAP WINAPI createDiscardableBitmap(HDC hdc, int nWidth, int nHeight); + + void setDDrawBpp(DWORD bpp); + + void disableDwm8And16BitMitigation(); + void installHooks(HMODULE origDDrawModule); + } +}