1
0
mirror of https://github.com/narzoul/DDrawCompat synced 2024-12-30 08:55:36 +01:00

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.
This commit is contained in:
narzoul 2017-05-08 22:57:30 +02:00
parent 672b3b640f
commit 640f746633
16 changed files with 758 additions and 361 deletions

View File

@ -2,10 +2,14 @@
#include <algorithm>
#include <map>
#include <set>
#include <string>
#include <vector>
#include <utility>
#include <Windows.h>
#include <detours.h>
#include <Psapi.h>
#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<HMODULE> getProcessModules(HANDLE process)
{
if (!module || !procName)
std::vector<HMODULE> 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<void*> getIatHookFunctions(const char* moduleName, const char* funcName)
{
std::set<void*> hookFunctions;
if (!moduleName || !funcName)
{
return hookFunctions;
}
PIMAGE_DOS_HEADER dosHeader = reinterpret_cast<PIMAGE_DOS_HEADER>(module);
if (IMAGE_DOS_SIGNATURE != dosHeader->e_magic) {
return nullptr;
}
char* moduleBase = reinterpret_cast<char*>(module);
auto modules = getProcessModules(GetCurrentProcess());
const HMODULE targetModule = GetModuleHandle(moduleName);
PIMAGE_NT_HEADERS ntHeader = reinterpret_cast<PIMAGE_NT_HEADERS>(
reinterpret_cast<char*>(dosHeader) + dosHeader->e_lfanew);
if (IMAGE_NT_SIGNATURE != ntHeader->Signature)
for (auto module : modules)
{
return nullptr;
}
PIMAGE_EXPORT_DIRECTORY exportDir = reinterpret_cast<PIMAGE_EXPORT_DIRECTORY>(
moduleBase + ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
DWORD* rvaOfNames = reinterpret_cast<DWORD*>(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<WORD*>(moduleBase + exportDir->AddressOfNameOrdinals);
DWORD* rvaOfFunctions = reinterpret_cast<DWORD*>(moduleBase + exportDir->AddressOfFunctions);
return reinterpret_cast<FARPROC>(moduleBase + rvaOfFunctions[nameOrds[i]]);
typedef decltype(GetProcAddress)* GetProcAddressFunc;
static const auto origGetProcAddressFunc = reinterpret_cast<GetProcAddressFunc>(
Compat::getProcAddress(GetModuleHandle("kernel32"), "GetProcAddress"));
auto getProcAddressFunc = reinterpret_cast<GetProcAddressFunc>(
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<PIMAGE_DOS_HEADER>(module);
if (IMAGE_DOS_SIGNATURE != dosHeader->e_magic)
{
return nullptr;
}
PIMAGE_NT_HEADERS ntHeaders = reinterpret_cast<PIMAGE_NT_HEADERS>(
reinterpret_cast<char*>(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<LPCSTR>(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<char*>(module);
PIMAGE_IMPORT_DESCRIPTOR importDesc = reinterpret_cast<PIMAGE_IMPORT_DESCRIPTOR>(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<PIMAGE_THUNK_DATA>(moduleBase + desc->FirstThunk);
auto origThunk = reinterpret_cast<PIMAGE_THUNK_DATA>(moduleBase + desc->OriginalFirstThunk);
while (0 != thunk->u1.AddressOfData && 0 != origThunk->u1.AddressOfData)
{
auto origImport = reinterpret_cast<PIMAGE_IMPORT_BY_NAME>(
moduleBase + origThunk->u1.AddressOfData);
if (0 == strcmp(origImport->Name, procName))
{
return reinterpret_cast<FARPROC*>(&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<char*>(module);
PIMAGE_EXPORT_DIRECTORY exportDir = reinterpret_cast<PIMAGE_EXPORT_DIRECTORY>(
moduleBase + ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
DWORD* rvaOfNames = reinterpret_cast<DWORD*>(moduleBase + exportDir->AddressOfNames);
for (DWORD i = 0; i < exportDir->NumberOfNames; ++i)
{
if (0 == strcmp(procName, moduleBase + rvaOfNames[i]))
{
WORD* nameOrds = reinterpret_cast<WORD*>(moduleBase + exportDir->AddressOfNameOrdinals);
DWORD* rvaOfFunctions = reinterpret_cast<DWORD*>(moduleBase + exportDir->AddressOfFunctions);
return reinterpret_cast<FARPROC>(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<FARPROC>(newFuncPtr);
DWORD dummy = 0;
VirtualProtect(func, sizeof(func), oldProtect, &dummy);
}
}
void unhookAllFunctions()
{
while (!g_hookedFunctions.empty())

View File

@ -15,6 +15,8 @@
namespace Compat
{
void redirectIatHooks(const char* moduleName, const char* funcName, void* newFunc);
template <typename OrigFuncPtr, OrigFuncPtr origFunc>
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 <typename OrigFuncPtr, OrigFuncPtr origFunc>
void hookFunction(const char* moduleName, const char* funcName, OrigFuncPtr newFuncPtr)

View File

@ -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

View File

@ -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 <typename TDirectDraw>
HRESULT setDisplayMode(TDirectDraw* This, DWORD width, DWORD height, DWORD bpp)
{
return DDraw::DirectDraw<TDirectDraw>::s_origVtable.SetDisplayMode(This, width, height, bpp);
}
template <typename TDirectDraw>
HRESULT setDisplayMode(TDirectDraw* This, DWORD width, DWORD height, DWORD bpp,
DWORD refreshRate, DWORD flags)
{
Win32::DisplayMode::setDDrawBpp(bpp);
HRESULT result = DDraw::DirectDraw<TDirectDraw>::s_origVtable.SetDisplayMode(
This, width, height, bpp, refreshRate, flags);
Win32::DisplayMode::setDDrawBpp(0);
return result;
}
}
namespace DDraw
{
CompatPtr<IDirectDrawSurface7> 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<IDirectDrawSurface7> surface;
auto dd = DDraw::Repository::getDirectDraw();
dd->CreateSurface(dd, &desc, &surface.getRef(), nullptr);
return surface;
}
template <typename TDirectDraw>
void* getDdObject(TDirectDraw& dd)
{
@ -18,14 +92,20 @@ namespace DDraw
template void* getDdObject(IDirectDraw4&);
template void* getDdObject(IDirectDraw7&);
DDSURFACEDESC2 getDisplayMode(CompatRef<IDirectDraw7> dd)
{
DDSURFACEDESC2 dm = {};
dm.dwSize = sizeof(dm);
dd->GetDisplayMode(&dd, &dm);
return dm;
}
template <typename TDirectDraw>
void DirectDraw<TDirectDraw>::setCompatVtable(Vtable<TDirectDraw>& 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 <typename TDirectDraw>
HRESULT STDMETHODCALLTYPE DirectDraw<TDirectDraw>::GetDisplayMode(
TDirectDraw* This, TSurfaceDesc* lpDDSurfaceDesc)
{
const DWORD size = lpDDSurfaceDesc ? lpDDSurfaceDesc->dwSize : 0;
if (sizeof(DDSURFACEDESC) != size && sizeof(DDSURFACEDESC2) != size)
{
return DDERR_INVALIDPARAMS;
}
CompatPtr<IDirectDraw7> dd(Compat::queryInterface<IDirectDraw7>(This));
const DDSURFACEDESC2 dm = DisplayMode::getDisplayMode(*dd);
CopyMemory(lpDDSurfaceDesc, &dm, size);
lpDDSurfaceDesc->dwSize = size;
return DD_OK;
}
template <typename TDirectDraw>
HRESULT STDMETHODCALLTYPE DirectDraw<TDirectDraw>::GetGDISurface(
TDirectDraw* /*This*/, TSurface** lplpGDIDDSSurface)
@ -101,13 +163,6 @@ namespace DDraw
return DD_OK;
}
template <typename TDirectDraw>
HRESULT STDMETHODCALLTYPE DirectDraw<TDirectDraw>::RestoreDisplayMode(TDirectDraw* This)
{
CompatPtr<IDirectDraw7> dd(Compat::queryInterface<IDirectDraw7>(This));
return DisplayMode::restoreDisplayMode(*dd);
}
template <typename TDirectDraw>
HRESULT STDMETHODCALLTYPE DirectDraw<TDirectDraw>::SetCooperativeLevel(
TDirectDraw* This, HWND hWnd, DWORD dwFlags)
@ -138,8 +193,7 @@ namespace DDraw
DWORD dwBPP,
Params... params)
{
CompatPtr<IDirectDraw7> dd(Compat::queryInterface<IDirectDraw7>(This));
return DisplayMode::setDisplayMode(*dd, dwWidth, dwHeight, dwBPP, params...);
return setDisplayMode(This, dwWidth, dwHeight, dwBPP, params...);
}
template DirectDraw<IDirectDraw>;

View File

@ -1,14 +1,24 @@
#pragma once
#define CINTERFACE
#include <ddraw.h>
#include "Common/CompatPtr.h"
#include "Common/CompatRef.h"
#include "Common/CompatVtable.h"
#include "DDraw/Visitors/DirectDrawVtblVisitor.h"
#include "DDraw/Types.h"
namespace DDraw
{
CompatPtr<IDirectDrawSurface7> createCompatibleSurface(DWORD bpp);
template <typename TDirectDraw>
void* getDdObject(TDirectDraw& dd);
DDSURFACEDESC2 getDisplayMode(CompatRef<IDirectDraw7> dd);
template <typename TDirectDraw>
class DirectDraw: public CompatVtable<Vtable<TDirectDraw>>
{
@ -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 <typename... Params>

View File

@ -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<IDirectDrawSurface7> g_compatibleSurface = {};
HDC g_compatibleDc = nullptr;
DDSURFACEDESC2 g_emulatedDisplayMode = {};
template <typename CStr, typename DevMode,
typename ChangeDisplaySettingsExPtr, typename EnumDisplaySettingsPtr>
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, &currentDevMode);
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<IDirectDrawSurface7> 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<IDirectDrawSurface7> surface;
dd->CreateSurface(dd, &desc, &surface.getRef(), nullptr);
return surface;
}
template <typename TSurfaceDesc>
HRESULT PASCAL enumDisplayModesCallback(
TSurfaceDesc* lpDDSurfaceDesc,
LPVOID lpContext)
{
if (lpDDSurfaceDesc)
{
*static_cast<DDPIXELFORMAT*>(lpContext) = lpDDSurfaceDesc->ddpfPixelFormat;
}
return DDENUMRET_CANCEL;
}
DDPIXELFORMAT getDisplayModePixelFormat(
CompatRef<IDirectDraw7> 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<IDirectDraw7> 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<IDirectDraw7> 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<IDirectDraw7> dd)
{
const HRESULT result = dd->RestoreDisplayMode(&dd);
if (SUCCEEDED(result))
{
ZeroMemory(&g_emulatedDisplayMode, sizeof(g_emulatedDisplayMode));
releaseCompatibleDc();
}
return result;
}
HRESULT setDisplayMode(CompatRef<IDirectDraw7> 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;
}
}
}

View File

@ -1,25 +0,0 @@
#pragma once
#define CINTERFACE
#include <ddraw.h>
#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<IDirectDraw7> dd);
HRESULT restoreDisplayMode(CompatRef<IDirectDraw7> dd);
HRESULT setDisplayMode(CompatRef<IDirectDraw7> dd,
DWORD width, DWORD height, DWORD bpp, DWORD refreshRate = 0, DWORD flags = 0);
};
}

View File

@ -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 <typename TDirectDraw>
HRESULT createPaletteConverter(CompatRef<TDirectDraw> dd)
{
auto dd7(CompatPtr<IDirectDraw7>::from(&dd));
auto dm = DDraw::DisplayMode::getDisplayMode(*dd7);
auto dm = DDraw::getDisplayMode(*CompatPtr<IDirectDraw7>::from(&dd));
if (dm.ddpfPixelFormat.dwRGBBitCount > 8)
{
return DD_OK;

View File

@ -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<IDirectDraw7> dd7(Compat::queryInterface<IDirectDraw7>(&dd));
const auto& dm = DisplayMode::getDisplayMode(*dd7);
const auto& dm = DDraw::getDisplayMode(*CompatPtr<IDirectDraw7>::from(&dd));
desc.dwFlags |= DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT;
desc.dwWidth = dm.dwWidth;
desc.dwHeight = dm.dwHeight;

View File

@ -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;
}

View File

@ -90,7 +90,7 @@
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level4</WarningLevel>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;DDRAWCOMPAT_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>PSAPI_VERSION=1;WIN32;_DEBUG;_WINDOWS;_USRDLL;DDRAWCOMPAT_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<MinimalRebuild>false</MinimalRebuild>
@ -99,7 +99,7 @@
</ClCompile>
<Link>
<ModuleDefinitionFile>Dll/DDrawCompat.def</ModuleDefinitionFile>
<AdditionalDependencies>dxguid.lib;detours.lib;msimg32.lib;oleacc.lib;uxtheme.lib;dwmapi.lib;winmm.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>dxguid.lib;detours.lib;msimg32.lib;oleacc.lib;psapi.lib;uxtheme.lib;dwmapi.lib;winmm.lib;%(AdditionalDependencies)</AdditionalDependencies>
<LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
</Link>
</ItemDefinitionGroup>
@ -122,14 +122,14 @@
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level4</WarningLevel>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;DDRAWCOMPAT_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>PSAPI_VERSION=1;WIN32;NDEBUG;_WINDOWS;_USRDLL;DDRAWCOMPAT_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<ObjectFileName>$(IntDir)%(RelativeDir)</ObjectFileName>
</ClCompile>
<Link>
<ModuleDefinitionFile>Dll/DDrawCompat.def</ModuleDefinitionFile>
<AdditionalDependencies>dxguid.lib;detours.lib;msimg32.lib;oleacc.lib;uxtheme.lib;dwmapi.lib;winmm.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>dxguid.lib;detours.lib;msimg32.lib;oleacc.lib;psapi.lib;uxtheme.lib;dwmapi.lib;winmm.lib;%(AdditionalDependencies)</AdditionalDependencies>
<GenerateDebugInformation>No</GenerateDebugInformation>
<LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
</Link>
@ -185,7 +185,6 @@
<ClInclude Include="DDraw\DirectDrawClipper.h" />
<ClInclude Include="DDraw\DirectDrawPalette.h" />
<ClInclude Include="DDraw\DirectDrawSurface.h" />
<ClInclude Include="DDraw\DisplayMode.h" />
<ClInclude Include="DDraw\Hooks.h" />
<ClInclude Include="DDraw\Repository.h" />
<ClInclude Include="DDraw\ScopedThreadLock.h" />
@ -225,6 +224,7 @@
<ClInclude Include="Gdi\ScrollFunctions.h" />
<ClInclude Include="Gdi\TitleBar.h" />
<ClInclude Include="Gdi\WinProc.h" />
<ClInclude Include="Win32\DisplayMode.h" />
<ClInclude Include="Win32\FontSmoothing.h" />
<ClInclude Include="Win32\MsgHooks.h" />
<ClInclude Include="Win32\Registry.h" />
@ -250,7 +250,6 @@
<ClCompile Include="DDraw\DirectDrawClipper.cpp" />
<ClCompile Include="DDraw\DirectDrawPalette.cpp" />
<ClCompile Include="DDraw\DirectDrawSurface.cpp" />
<ClCompile Include="DDraw\DisplayMode.cpp" />
<ClCompile Include="DDraw\Hooks.cpp" />
<ClCompile Include="DDraw\Repository.cpp" />
<ClCompile Include="DDraw\IReleaseNotifier.cpp" />
@ -280,6 +279,7 @@
<ClCompile Include="Gdi\ScrollFunctions.cpp" />
<ClCompile Include="Gdi\TitleBar.cpp" />
<ClCompile Include="Gdi\WinProc.cpp" />
<ClCompile Include="Win32\DisplayMode.cpp" />
<ClCompile Include="Win32\FontSmoothing.cpp" />
<ClCompile Include="Win32\MsgHooks.cpp" />
<ClCompile Include="Win32\Registry.cpp" />

View File

@ -120,9 +120,6 @@
<ClInclude Include="DDraw\ActivateAppHandler.h">
<Filter>Header Files\DDraw</Filter>
</ClInclude>
<ClInclude Include="DDraw\DisplayMode.h">
<Filter>Header Files\DDraw</Filter>
</ClInclude>
<ClInclude Include="DDraw\Hooks.h">
<Filter>Header Files\DDraw</Filter>
</ClInclude>
@ -306,6 +303,9 @@
<ClInclude Include="DDraw\DirectDrawClipper.h">
<Filter>Header Files\DDraw</Filter>
</ClInclude>
<ClInclude Include="Win32\DisplayMode.h">
<Filter>Header Files\Win32</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Gdi\Gdi.cpp">
@ -347,9 +347,6 @@
<ClCompile Include="DDraw\ActivateAppHandler.cpp">
<Filter>Source Files\DDraw</Filter>
</ClCompile>
<ClCompile Include="DDraw\DisplayMode.cpp">
<Filter>Source Files\DDraw</Filter>
</ClCompile>
<ClCompile Include="DDraw\Hooks.cpp">
<Filter>Source Files\DDraw</Filter>
</ClCompile>
@ -467,6 +464,9 @@
<ClCompile Include="DDraw\DirectDrawClipper.cpp">
<Filter>Source Files\DDraw</Filter>
</ClCompile>
<ClCompile Include="Win32\DisplayMode.cpp">
<Filter>Source Files\Win32</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="Dll\DDrawCompat.def">

View File

@ -5,13 +5,15 @@
#include <Windows.h>
#include <Uxtheme.h>
#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();

View File

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

View File

@ -0,0 +1,413 @@
#include <string>
#include <vector>
#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 <typename Char>
struct EnumParams
{
std::basic_string<Char> 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<IDirectDrawSurface7> 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 <typename CStr, typename DevMode, typename ChangeDisplaySettingsExFunc,
typename EnumDisplaySettingsExFunc>
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 <typename CStr, typename DevMode, typename ChangeDisplaySettingsExFunc>
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 <typename CStr, typename DevMode, typename EnumDisplaySettingsExFunc>
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 <typename Char, typename DevMode, typename EnumDisplaySettingsExFunc>
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<DevMode> devModes;
thread_local EnumParams<Char> lastEnumParams = {};
EnumParams<Char> currentEnumParams = {
lpszDeviceName ? lpszDeviceName : std::basic_string<Char>(), 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();
}
}
}

View File

@ -0,0 +1,21 @@
#pragma once
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
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);
}
}