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

Added support for Steam overlay in fullscreen mode

This commit is contained in:
narzoul 2024-04-13 16:44:33 +02:00
parent f1f775aaa4
commit 79d2eea9e3
29 changed files with 659 additions and 98 deletions

View File

@ -105,28 +105,6 @@ namespace
return ntHeaders;
}
unsigned getInstructionSize(void* instruction)
{
const unsigned MAX_INSTRUCTION_SIZE = 15;
HRESULT result = g_debugDataSpaces->WriteVirtual(g_debugBase, instruction, MAX_INSTRUCTION_SIZE, nullptr);
if (FAILED(result))
{
LOG_ONCE("ERROR: DbgEng: WriteVirtual failed: " << Compat::hex(result));
return 0;
}
ULONG64 endOffset = 0;
result = g_debugControl->Disassemble(g_debugBase, 0, nullptr, 0, nullptr, &endOffset);
if (FAILED(result))
{
LOG_ONCE("ERROR: DbgEng: Disassemble failed: " << Compat::hex(result) << " "
<< Compat::hexDump(instruction, MAX_INSTRUCTION_SIZE));
return 0;
}
return static_cast<unsigned>(endOffset - g_debugBase);
}
void hookFunction(void*& origFuncPtr, void* newFuncPtr, const char* funcName)
{
BYTE* targetFunc = static_cast<BYTE*>(origFuncPtr);
@ -193,7 +171,7 @@ namespace
BYTE* dst = trampoline;
while (src - targetFunc < 5)
{
unsigned instructionSize = getInstructionSize(src);
unsigned instructionSize = Compat::getInstructionSize(src);
if (0 == instructionSize)
{
return;
@ -223,13 +201,12 @@ namespace
memset(targetFunc + 5, 0xCC, src - targetFunc - 5);
VirtualProtect(targetFunc, src - targetFunc, PAGE_EXECUTE_READ, &oldProtect);
FlushInstructionCache(GetCurrentProcess(), nullptr, 0);
origFuncPtr = trampoline;
CALL_ORIG_FUNC(FlushInstructionCache)(GetCurrentProcess(), nullptr, 0);
HMODULE module = nullptr;
GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_PIN,
reinterpret_cast<char*>(targetFunc), &module);
origFuncPtr = trampoline;
}
bool initDbgEng()
@ -339,7 +316,7 @@ namespace Compat
g_isDbgEngInitialized = false;
}
std::string funcPtrToStr(void* funcPtr)
std::string funcPtrToStr(const void* funcPtr)
{
std::ostringstream oss;
HMODULE module = Compat::getModuleHandleFromAddress(funcPtr);
@ -355,11 +332,63 @@ namespace Compat
return oss.str();
}
HMODULE getModuleHandleFromAddress(void* address)
unsigned getInstructionSize(void* instruction)
{
const unsigned MAX_INSTRUCTION_SIZE = 15;
HRESULT result = g_debugDataSpaces->WriteVirtual(g_debugBase, instruction, MAX_INSTRUCTION_SIZE, nullptr);
if (FAILED(result))
{
LOG_ONCE("ERROR: DbgEng: WriteVirtual failed: " << Compat::hex(result));
return 0;
}
ULONG64 endOffset = 0;
result = g_debugControl->Disassemble(g_debugBase, 0, nullptr, 0, nullptr, &endOffset);
if (FAILED(result))
{
LOG_ONCE("ERROR: DbgEng: Disassemble failed: " << Compat::hex(result) << " "
<< Compat::hexDump(instruction, MAX_INSTRUCTION_SIZE));
return 0;
}
return static_cast<unsigned>(endOffset - g_debugBase);
}
DWORD getModuleFileOffset(const void* address)
{
LOG_FUNC("getModuleFileOffset", address);
HMODULE mod = getModuleHandleFromAddress(address);
if (!mod)
{
return LOG_RESULT(0);
}
PIMAGE_NT_HEADERS ntHeaders = getImageNtHeaders(mod);
if (!ntHeaders)
{
return LOG_RESULT(0);
}
DWORD offset = static_cast<const BYTE*>(address) - reinterpret_cast<const BYTE*>(mod);
auto sectionHeader = reinterpret_cast<IMAGE_SECTION_HEADER*>(
&ntHeaders->OptionalHeader.DataDirectory[ntHeaders->OptionalHeader.NumberOfRvaAndSizes]);
for (unsigned i = 0; i < ntHeaders->FileHeader.NumberOfSections; ++i)
{
if (offset >= sectionHeader->VirtualAddress &&
offset < sectionHeader->VirtualAddress + sectionHeader->SizeOfRawData)
{
return LOG_RESULT(sectionHeader->PointerToRawData + offset - sectionHeader->VirtualAddress);
}
sectionHeader++;
}
return LOG_RESULT(0);
}
HMODULE getModuleHandleFromAddress(const void* address)
{
HMODULE module = nullptr;
GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
static_cast<char*>(address), &module);
static_cast<const char*>(address), &module);
return module;
}

View File

@ -14,8 +14,10 @@
namespace Compat
{
void closeDbgEng();
std::string funcPtrToStr(void* funcPtr);
HMODULE getModuleHandleFromAddress(void* address);
std::string funcPtrToStr(const void* funcPtr);
unsigned getInstructionSize(void* instruction);
DWORD getModuleFileOffset(const void* address);
HMODULE getModuleHandleFromAddress(const void* address);
template <auto origFunc>
decltype(origFunc) g_origFuncPtr = origFunc;

View File

@ -58,6 +58,8 @@ namespace D3dDdi
, m_origVtable(CompatVtable<D3DDDI_ADAPTERFUNCS>::s_origVtable)
, m_runtimeVersion(data.Version)
, m_driverVersion(data.DriverVersion)
, m_guid(nullptr)
, m_guidBuf{}
, m_luid(KernelModeThunks::getLastOpenAdapterInfo().luid)
, m_deviceName(KernelModeThunks::getLastOpenAdapterInfo().deviceName)
, m_repository{}
@ -496,19 +498,20 @@ namespace D3dDdi
return result;
}
void Adapter::setRepository(LUID luid, CompatWeakPtr<IDirectDraw7> repository, bool isPrimary)
void Adapter::setRepository(LUID luid, GUID* guid, CompatWeakPtr<IDirectDraw7> repository)
{
for (auto& adapter : s_adapters)
{
if (adapter.second.m_luid == luid)
{
if (guid)
{
adapter.second.m_guidBuf = *guid;
adapter.second.m_guid = &adapter.second.m_guidBuf;
}
adapter.second.m_repository = repository;
auto& surfaceRepo = SurfaceRepository::get(adapter.second);
surfaceRepo.setRepository(repository);
if (isPrimary)
{
surfaceRepo.setAsPrimaryRepo();
}
}
}
}

View File

@ -36,6 +36,7 @@ namespace D3dDdi
operator HANDLE() const { return m_adapter; }
SIZE getAspectRatio() const;
GUID* getGuid() const { return m_guid; }
const AdapterInfo& getInfo() const { return m_info; }
LUID getLuid() const { return m_luid; }
const auto& getMonitorInfo() const { return Win32::DisplayMode::getMonitorInfo(m_deviceName); }
@ -53,7 +54,7 @@ namespace D3dDdi
static void add(const D3DDDIARG_OPENADAPTER& data) { s_adapters.emplace(data.hAdapter, data); }
static Adapter& get(HANDLE adapter) { return s_adapters.find(adapter)->second; }
static void setRepository(LUID luid, CompatWeakPtr<IDirectDraw7> repository, bool isPrimary);
static void setRepository(LUID luid, GUID* guid, CompatWeakPtr<IDirectDraw7> repository);
private:
const AdapterInfo& findInfo() const;
@ -74,6 +75,8 @@ namespace D3dDdi
D3DDDI_ADAPTERFUNCS m_origVtable;
UINT m_runtimeVersion;
UINT m_driverVersion;
GUID* m_guid;
GUID m_guidBuf;
LUID m_luid;
std::wstring m_deviceName;
CompatWeakPtr<IDirectDraw7> m_repository;

View File

@ -13,6 +13,7 @@
#include <D3dDdi/Log/DeviceFuncsLog.h>
#include <D3dDdi/Resource.h>
#include <D3dDdi/ShaderAssembler.h>
#include <Overlay/Steam.h>
#include <Shaders/VertexFixup.h>
#define LOG_DS LOG_DEBUG << "DeviceState::" << __func__ << ": "
@ -1117,7 +1118,19 @@ namespace D3dDdi
auto depthStencil = m_app.depthStencil;
bool isTransformed = getVertexDecl().isTransformed;
Resource* resource = m_device.getResource(renderTarget.hRenderTarget);
Resource* resource = nullptr;
auto steamResources = Overlay::Steam::getResources();
if (steamResources.bbResource && *steamResources.rtResource == renderTarget.hRenderTarget)
{
resource = steamResources.bbResource;
renderTarget.hRenderTarget = *steamResources.bbResource;
renderTarget.SubResourceIndex = steamResources.bbSubResourceIndex;
}
else
{
resource = m_device.getResource(renderTarget.hRenderTarget);
}
if (resource)
{
auto customResource = resource->getCustomResource();
@ -1129,11 +1142,11 @@ namespace D3dDdi
if (isTransformed)
{
scissorRect = makeRect(scaledVp);
auto& si = resource->getOrigDesc().pSurfList[renderTarget.SubResourceIndex];
auto& si = resource->getFixedDesc().pSurfList[renderTarget.SubResourceIndex];
vp = { 0, 0, si.Width, si.Height };
if (customResource)
{
auto& scaledSi = customResource->getOrigDesc().pSurfList[renderTarget.SubResourceIndex];
auto& scaledSi = customResource->getFixedDesc().pSurfList[renderTarget.SubResourceIndex];
scaledVp = { 0, 0, scaledSi.Width, scaledSi.Height };
}
else

View File

@ -273,6 +273,27 @@ std::ostream& operator<<(std::ostream& os, const D3DDDIARG_SETVERTEXSHADERCONST&
<< val.Count;
}
std::ostream& operator<<(std::ostream& os, const D3DDDIARG_TEXBLT& val)
{
return Compat::LogStruct(os)
<< val.hDstResource
<< val.hSrcResource
<< val.CubeMapFace
<< val.DstPoint
<< val.SrcRect;
}
std::ostream& operator<<(std::ostream& os, const D3DDDIARG_TEXBLT1& val)
{
return Compat::LogStruct(os)
<< val.hDstResource
<< val.hSrcResource
<< val.CubeMapFace
<< val.DstPoint
<< val.SrcRect
<< Compat::hex(val.CopyFlags);
}
std::ostream& operator<<(std::ostream& os, const D3DDDIARG_TEXTURESTAGESTATE& val)
{
return Compat::LogStruct(os)

View File

@ -34,6 +34,8 @@ std::ostream& operator<<(std::ostream& os, const D3DDDIARG_SETRENDERTARGET& val)
std::ostream& operator<<(std::ostream& os, const D3DDDIARG_SETSTREAMSOURCE& val);
std::ostream& operator<<(std::ostream& os, const D3DDDIARG_SETSTREAMSOURCEUM& val);
std::ostream& operator<<(std::ostream& os, const D3DDDIARG_SETVERTEXSHADERCONST& val);
std::ostream& operator<<(std::ostream& os, const D3DDDIARG_TEXBLT& val);
std::ostream& operator<<(std::ostream& os, const D3DDDIARG_TEXBLT1& val);
std::ostream& operator<<(std::ostream& os, const D3DDDIARG_TEXTURESTAGESTATE& val);
std::ostream& operator<<(std::ostream& os, const D3DDDIARG_UNLOCK& val);
std::ostream& operator<<(std::ostream& os, const D3DDDIARG_UPDATEPALETTE& val);

View File

@ -25,6 +25,7 @@
#include <Gdi/Palette.h>
#include <Gdi/VirtualScreen.h>
#include <Gdi/Window.h>
#include <Overlay/Steam.h>
namespace
{
@ -244,7 +245,14 @@ namespace D3dDdi
return S_OK;
}
if (shouldBltViaCpu(data, *srcResource))
auto steamResources = Overlay::Steam::getResources();
if (steamResources.rtResource == srcResource && steamResources.bbResource)
{
srcResource = steamResources.bbResource;
data.hSrcResource = *steamResources.bbResource;
data.SrcSubResourceIndex = steamResources.bbSubResourceIndex;
}
else if (shouldBltViaCpu(data, *srcResource))
{
return bltViaCpu(data, *srcResource);
}
@ -1014,7 +1022,7 @@ namespace D3dDdi
HRESULT Resource::lock(D3DDDIARG_LOCK& data)
{
if (D3DDDIMULTISAMPLE_NONE != m_fixedData.MultisampleType)
if (D3DDDIMULTISAMPLE_NONE != m_fixedData.MultisampleType || FOURCC_NULL == m_fixedData.Format)
{
return E_FAIL;
}
@ -1325,6 +1333,8 @@ namespace D3dDdi
presentLayeredWindows(*this, data.DstSubResourceIndex, getRect(data.DstSubResourceIndex),
Gdi::Window::getVisibleOverlayWindows(), m_device.getAdapter().getMonitorInfo().rcMonitor);
Overlay::Steam::render(*this, data.DstSubResourceIndex);
return LOG_RESULT(S_OK);
}

View File

@ -431,9 +431,13 @@ namespace D3dDdi
}
}
void SurfaceRepository::setAsPrimaryRepo()
void SurfaceRepository::setRepository(CompatWeakPtr<IDirectDraw7> dd)
{
g_primaryRepository = this;
m_dd = dd;
if (!g_primaryRepository)
{
g_primaryRepository = this;
}
}
bool SurfaceRepository::s_inCreateSurface = false;

View File

@ -57,8 +57,7 @@ namespace D3dDdi
CompatWeakPtr<IDirectDrawSurface7> getWindowedPrimary();
CompatPtr<IDirectDrawSurface7> getWindowedSrc(RECT rect);
void release(Surface& surface);
void setAsPrimaryRepo();
void setRepository(CompatWeakPtr<IDirectDraw7> dd) { m_dd = dd; }
void setRepository(CompatWeakPtr<IDirectDraw7> dd);
static SurfaceRepository& get(const Adapter& adapter);
static SurfaceRepository& getPrimaryRepo();

View File

@ -343,7 +343,7 @@ namespace DDraw
}
repo.get()->lpVtbl->SetCooperativeLevel(repo, nullptr, DDSCL_NORMAL);
it = repositories.insert({ adapterInfo.luid, repo }).first;
D3dDdi::Adapter::setRepository(adapterInfo.luid, it->second, !guid);
D3dDdi::Adapter::setRepository(adapterInfo.luid, guid, it->second);
repo.detach();
}
}

View File

@ -1,6 +1,7 @@
#pragma once
#include <functional>
#include <type_traits>
#include <ddraw.h>
#include <ddrawi.h>
@ -12,18 +13,26 @@ namespace DDraw
{
namespace DirectDraw
{
template <typename TSurface>
struct IsDirectDraw : std::false_type {};
template <> struct IsDirectDraw<IDirectDraw> : std::true_type {};
template <> struct IsDirectDraw<IDirectDraw2> : std::true_type {};
template <> struct IsDirectDraw<IDirectDraw4> : std::true_type {};
template <> struct IsDirectDraw<IDirectDraw7> : std::true_type {};
DDPIXELFORMAT getRgbPixelFormat(DWORD bpp);
void hookDDrawWindowProc(WNDPROC ddrawWndProc);
void onCreate(GUID* guid, CompatRef<IDirectDraw7> dd);
void suppressEmulatedDirectDraw(GUID*& guid);
template <typename TDirectDraw>
template <typename TDirectDraw, typename = std::enable_if_t<IsDirectDraw<TDirectDraw>::value>>
DDRAWI_DIRECTDRAW_INT& getInt(TDirectDraw& dd)
{
return reinterpret_cast<DDRAWI_DIRECTDRAW_INT&>(dd);
}
template <typename TDirectDraw>
template <typename TDirectDraw, typename = std::enable_if_t<IsDirectDraw<TDirectDraw>::value>>
HWND* getDeviceWindowPtr(TDirectDraw& dd)
{
return &reinterpret_cast<HWND>(getInt(dd).lpLcl->hWnd);

View File

@ -6,6 +6,8 @@
#include <DDraw/Surfaces/Surface.h>
#include <DDraw/Surfaces/SurfaceImpl.h>
#include <DDraw/Visitors/DirectDrawSurfaceVtblVisitor.h>
#include <D3dDdi/Device.h>
#include <D3dDdi/SurfaceRepository.h>
#define SET_COMPAT_METHOD(method) \
vtable.method = &callImpl<decltype(&DDraw::SurfaceImpl<TSurface>::method), &DDraw::SurfaceImpl<TSurface>::method, \
@ -92,6 +94,12 @@ namespace DDraw
return context.surfaces;
}
D3dDdi::SurfaceRepository* getSurfaceRepository(HANDLE resource)
{
auto device = D3dDdi::Device::findDeviceByResource(resource);
return device ? &device->getRepo() : nullptr;
}
template <typename Vtable>
void hookVtable(const Vtable& vtable)
{

View File

@ -1,5 +1,6 @@
#pragma once
#include <type_traits>
#include <vector>
#include <Windows.h>
@ -8,36 +9,58 @@
#include <Common/CompatPtr.h>
#include <Common/CompatRef.h>
namespace D3dDdi
{
class SurfaceRepository;
}
namespace DDraw
{
namespace DirectDrawSurface
{
template <typename TSurface>
struct IsSurface : std::false_type {};
template <> struct IsSurface<IDirectDrawSurface> : std::true_type {};
template <> struct IsSurface<IDirectDrawSurface2> : std::true_type {};
template <> struct IsSurface<IDirectDrawSurface3> : std::true_type {};
template <> struct IsSurface<IDirectDrawSurface4> : std::true_type {};
template <> struct IsSurface<IDirectDrawSurface7> : std::true_type {};
std::vector<CompatPtr<IDirectDrawSurface7>> getAllAttachedSurfaces(CompatRef<IDirectDrawSurface7> surface);
template <typename TSurface>
template <typename TSurface, typename = std::enable_if_t<IsSurface<TSurface>::value>>
DDRAWI_DDRAWSURFACE_INT& getInt(TSurface& surface)
{
return reinterpret_cast<DDRAWI_DDRAWSURFACE_INT&>(surface);
}
template <typename TSurface>
template <typename TSurface, typename = std::enable_if_t<IsSurface<TSurface>::value>>
HANDLE getRuntimeResourceHandle(TSurface& surface)
{
return reinterpret_cast<HANDLE>(getInt(surface).lpLcl->hDDSurface);
}
template <typename TSurface>
HANDLE getDriverResourceHandle(TSurface& surface)
template <typename TSurface, typename = std::enable_if_t<IsSurface<TSurface>::value>>
HANDLE& getDriverResourceHandle(TSurface& surface)
{
return reinterpret_cast<HANDLE*>(getRuntimeResourceHandle(surface))[0];
}
template <typename TSurface>
UINT getSubResourceIndex(TSurface& surface)
template <typename TSurface, typename = std::enable_if_t<IsSurface<TSurface>::value>>
UINT& getSubResourceIndex(TSurface& surface)
{
return reinterpret_cast<UINT*>(getRuntimeResourceHandle(surface))[1];
}
D3dDdi::SurfaceRepository* getSurfaceRepository(HANDLE resource);
template <typename TSurface, typename = std::enable_if_t<IsSurface<TSurface>::value>>
D3dDdi::SurfaceRepository* getSurfaceRepository(TSurface& surface)
{
return getSurfaceRepository(getDriverResourceHandle(surface));
}
template <typename Vtable>
void hookVtable(const Vtable& vtable);
}

View File

@ -97,9 +97,6 @@ namespace DDraw
{
void installHooks(CompatPtr<IDirectDraw7> dd7)
{
DDraw::DirectDraw::onCreate(nullptr, *dd7);
RealPrimarySurface::init();
g_origInitialize = dd7.get()->lpVtbl->Initialize;
Compat::hookFunction(reinterpret_cast<void*&>(g_origInitialize), initialize, "IDirectDrawVtbl::Initialize");
@ -107,5 +104,8 @@ namespace DDraw
hookDirectDrawClipper(*dd7);
hookDirectDrawPalette(*dd7);
hookDirectDrawSurface(*dd7);
DDraw::DirectDraw::onCreate(nullptr, *dd7);
DDraw::RealPrimarySurface::init();
}
}

View File

@ -38,6 +38,7 @@
#include <Gdi/WinProc.h>
#include <Overlay/ConfigWindow.h>
#include <Overlay/StatsWindow.h>
#include <Overlay/Steam.h>
#include <Win32/DisplayMode.h>
#include <Win32/DpiAwareness.h>
#include <Win32/Thread.h>
@ -276,18 +277,7 @@ namespace
D3dDdi::SurfaceRepository* repo = nullptr;
if (src)
{
auto resource = DDraw::DirectDrawSurface::getDriverResourceHandle(*frontBuffer);
if (!resource)
{
return;
}
auto device = D3dDdi::Device::findDeviceByResource(resource);
if (!device)
{
return;
}
repo = &device->getRepo();
repo = DDraw::DirectDrawSurface::getSurfaceRepository(*frontBuffer);
}
else
{
@ -364,7 +354,7 @@ namespace
}
Gdi::Cursor::setMonitorClipRect(clipRect);
Gdi::Cursor::setEmulated(true);
Gdi::Cursor::setEmulated(!Overlay::Steam::isOverlayOpen());
}
}
@ -568,6 +558,7 @@ namespace DDraw
static UINT lastOverlayCheckVsyncCount = 0;
if (vsyncCount != lastOverlayCheckVsyncCount)
{
updatePresentationParams();
setPresentationWindowTopmost();
Gdi::Cursor::update();
Gdi::Caret::blink();
@ -577,11 +568,10 @@ namespace DDraw
statsWindow->updateStats();
g_qpcLastUpdate = Time::queryPerformanceCounter();
}
Overlay::Steam::flush();
lastOverlayCheckVsyncCount = vsyncCount;
}
updatePresentationParams();
bool isOverlayOnly = false;
{

View File

@ -325,6 +325,7 @@
<ClInclude Include="Overlay\StatsEventRate.h" />
<ClInclude Include="Overlay\StatsTimer.h" />
<ClInclude Include="Overlay\StatsWindow.h" />
<ClInclude Include="Overlay\Steam.h" />
<ClInclude Include="Overlay\Window.h" />
<ClInclude Include="Win32\DisplayMode.h" />
<ClInclude Include="Win32\DpiAwareness.h" />
@ -459,6 +460,7 @@
<ClCompile Include="Overlay\StatsEventRate.cpp" />
<ClCompile Include="Overlay\StatsTimer.cpp" />
<ClCompile Include="Overlay\StatsWindow.cpp" />
<ClCompile Include="Overlay\Steam.cpp" />
<ClCompile Include="Overlay\Window.cpp" />
<ClCompile Include="Win32\DisplayMode.cpp" />
<ClCompile Include="Win32\DpiAwareness.cpp" />

View File

@ -708,6 +708,9 @@
<ClInclude Include="Win32\DpiAwareness.h">
<Filter>Header Files\Win32</Filter>
</ClInclude>
<ClInclude Include="Overlay\Steam.h">
<Filter>Header Files\Overlay</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Gdi\Gdi.cpp">
@ -1106,6 +1109,9 @@
<ClCompile Include="Win32\DpiAwareness.cpp">
<Filter>Source Files\Win32</Filter>
</ClCompile>
<ClCompile Include="Overlay\Steam.cpp">
<Filter>Source Files\Overlay</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="DDrawCompat.rc">

View File

@ -10,6 +10,7 @@ namespace Dll
HMODULE g_origDciman32Module = nullptr;
Procs g_origProcs = {};
Procs g_jmpTargetProcs = {};
Procs g_newProcs = {};
bool g_isHooked = false;
HANDLE createThread(unsigned(__stdcall* threadProc)(void*), unsigned int* threadId, int priority, unsigned initFlags)

View File

@ -80,6 +80,7 @@ namespace Dll
extern HMODULE g_origDciman32Module;
extern Procs g_origProcs;
extern Procs g_jmpTargetProcs;
extern Procs g_newProcs;
extern bool g_isHooked;
}

View File

@ -27,6 +27,7 @@
#include <Gdi/PresentationWindow.h>
#include <Gdi/VirtualScreen.h>
#include <Input/Input.h>
#include <Overlay/Steam.h>
#include <Win32/DisplayMode.h>
#include <Win32/DpiAwareness.h>
#include <Win32/MemoryManagement.h>
@ -82,7 +83,9 @@ namespace
{
if (!Dll::g_isHooked)
{
Dll::g_isHooked = true;
DDraw::SuppressResourceFormatLogs suppressResourceFormatLogs;
Overlay::Steam::installHooks();
LOG_INFO << "Installing display mode hooks";
Win32::DisplayMode::installHooks();
LOG_INFO << "Installing registry hooks";
@ -129,7 +132,6 @@ namespace
Gdi::installHooks();
Compat::closeDbgEng();
LOG_INFO << "Finished installing hooks";
Dll::g_isHooked = true;
}
}
@ -244,6 +246,8 @@ namespace
Dll::g_origProcs.proc = Compat::getProcAddress(origModule, #proc);
#define HOOK_DDRAW_PROC(proc) \
Dll::g_newProcs.proc = reinterpret_cast<FARPROC>( \
static_cast<decltype(&proc)>(&directDrawFunc<&Dll::Procs::proc, decltype(&proc)>)); \
Compat::hookFunction( \
reinterpret_cast<void*&>(Dll::g_origProcs.proc), \
static_cast<decltype(&proc)>(&directDrawFunc<&Dll::Procs::proc, decltype(&proc)>), \
@ -302,7 +306,8 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
return FALSE;
}
Dll::g_origDDrawModule = LoadLibraryW((systemPath / "ddraw.dll").c_str());
auto origDDrawModulePath = systemPath / "ddraw.dll";
Dll::g_origDDrawModule = LoadLibraryW(origDDrawModulePath.c_str());
if (!Dll::g_origDDrawModule)
{
LOG_INFO << "ERROR: Failed to load system ddraw.dll from " << systemPath.u8string();
@ -323,6 +328,7 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
}
Dll::g_jmpTargetProcs = Dll::g_origProcs;
Overlay::Steam::init(origDDrawModulePath.c_str());
VISIT_PUBLIC_DDRAW_PROCS(HOOK_DDRAW_PROC);
Compat::hookFunction(reinterpret_cast<void*&>(Dll::g_origProcs.SetAppCompatData),

View File

@ -13,6 +13,7 @@
#include <Gdi/WinProc.h>
#include <Overlay/ConfigWindow.h>
#include <Overlay/StatsWindow.h>
#include <Overlay/Steam.h>
#include <Win32/DisplayMode.h>
#include <Win32/DpiAwareness.h>
@ -178,16 +179,13 @@ namespace
LRESULT CALLBACK messageWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
LOG_FUNC("messageWindowProc", Compat::WindowMessageStruct(hwnd, uMsg, wParam, lParam));
if (WM_USER_EXECUTE == uMsg)
{
auto& func = *reinterpret_cast<const std::function<void()>*>(lParam);
func();
return LOG_RESULT(0);
return 0;
}
return LOG_RESULT(CALL_ORIG_FUNC(DefWindowProc)(hwnd, uMsg, wParam, lParam));
return CALL_ORIG_FUNC(DefWindowProc)(hwnd, uMsg, wParam, lParam);
}
unsigned WINAPI messageWindowThreadProc(LPVOID /*lpParameter*/)
@ -230,7 +228,6 @@ namespace
MSG msg = {};
while (CALL_ORIG_FUNC(GetMessageA)(&msg, nullptr, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
@ -276,6 +273,7 @@ namespace Gdi
void destroyWindow(HWND hwnd)
{
Overlay::Steam::onDestroyWindow(hwnd);
PostMessage(hwnd, WM_CLOSE, 0, 0);
}

View File

@ -12,7 +12,7 @@ namespace
LRESULT CALLBACK presentationWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
LOG_FUNC("presentationWindowProc", Compat::WindowMessageStruct(hwnd, uMsg, wParam, lParam));
return CALL_ORIG_FUNC(DefWindowProcA)(hwnd, uMsg, wParam, lParam);
return LOG_RESULT(CALL_ORIG_FUNC(DefWindowProcA)(hwnd, uMsg, wParam, lParam));
}
}
@ -27,7 +27,7 @@ namespace Gdi
GuiThread::execute([&]()
{
presentationWindow = GuiThread::createWindow(
WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_NOPARENTNOTIFY | (owner ? 0 : WS_EX_TOOLWINDOW),
WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_NOPARENTNOTIFY | WS_EX_NOACTIVATE | (owner ? 0 : WS_EX_TOOLWINDOW),
reinterpret_cast<const wchar_t*>(g_classAtom),
nullptr,
WS_DISABLED | WS_POPUP,
@ -56,6 +56,7 @@ namespace Gdi
WNDCLASS wc = {};
wc.lpfnWndProc = &presentationWindowProc;
wc.hInstance = Dll::g_currentModule;
wc.hCursor = CALL_ORIG_FUNC(LoadCursorA)(nullptr, IDC_ARROW);
wc.lpszClassName = "DDrawCompatPresentationWindow";
g_classAtom = CALL_ORIG_FUNC(RegisterClassA)(&wc);
}

View File

@ -206,22 +206,22 @@ namespace
{
case HTLEFT:
case HTRIGHT:
SetCursor(LoadCursor(nullptr, IDC_SIZEWE));
SetCursor(CALL_ORIG_FUNC(LoadCursorA)(nullptr, IDC_SIZEWE));
return TRUE;
case HTTOP:
case HTBOTTOM:
SetCursor(LoadCursor(nullptr, IDC_SIZENS));
SetCursor(CALL_ORIG_FUNC(LoadCursorA)(nullptr, IDC_SIZENS));
return TRUE;
case HTTOPLEFT:
case HTBOTTOMRIGHT:
SetCursor(LoadCursor(nullptr, IDC_SIZENWSE));
SetCursor(CALL_ORIG_FUNC(LoadCursorA)(nullptr, IDC_SIZENWSE));
return TRUE;
case HTBOTTOMLEFT:
case HTTOPRIGHT:
SetCursor(LoadCursor(nullptr, IDC_SIZENESW));
SetCursor(CALL_ORIG_FUNC(LoadCursorA)(nullptr, IDC_SIZENESW));
return TRUE;
}
@ -247,7 +247,7 @@ namespace
}
else
{
SetCursor(LoadCursor(nullptr, IDC_ARROW));
SetCursor(CALL_ORIG_FUNC(LoadCursorA)(nullptr, IDC_ARROW));
}
return FALSE;
}
@ -530,7 +530,7 @@ namespace
case WM_SETCURSOR:
if (CALL_ORIG_FUNC(GetWindowLongA)(hwnd, GWL_STYLE) & (SBS_SIZEBOX | SBS_SIZEGRIP))
{
SetCursor(LoadCursor(nullptr, IDC_SIZENWSE));
SetCursor(CALL_ORIG_FUNC(LoadCursorA)(nullptr, IDC_SIZENWSE));
}
return TRUE;
}

View File

@ -153,19 +153,13 @@ namespace Gdi
return nullptr;
}
auto resource = DDraw::DirectDrawSurface::getDriverResourceHandle(*primary);
if (!resource)
auto repo = DDraw::DirectDrawSurface::getSurfaceRepository(*DDraw::PrimarySurface::getPrimary());
if (!repo)
{
return nullptr;
}
auto device = D3dDdi::Device::findDeviceByResource(resource);
if (!device)
{
return nullptr;
}
auto dd(device->getRepo().getDirectDraw());
auto dd(repo->getDirectDraw());
if (!dd)
{
return nullptr;

View File

@ -17,6 +17,7 @@
#include <Gdi/PresentationWindow.h>
#include <Input/Input.h>
#include <Overlay/ConfigWindow.h>
#include <Overlay/Steam.h>
#include <Overlay/Window.h>
#include <Win32/DisplayMode.h>
@ -157,6 +158,12 @@ namespace
return 1;
}
}
auto steamWindow = Overlay::Steam::getWindow();
if (steamWindow)
{
PostMessage(steamWindow, wParam, llHook->vkCode, 0);
}
}
}
return CallNextHookEx(nullptr, nCode, wParam, lParam);

View File

@ -32,6 +32,7 @@
#include <Overlay/ConfigWindow.h>
#include <Overlay/SettingControl.h>
#include <Overlay/StatsWindow.h>
#include <Overlay/Steam.h>
namespace
{
@ -275,6 +276,11 @@ namespace Overlay
{
if (isVisible != Window::isVisible())
{
if (isVisible && Overlay::Steam::isOverlayOpen())
{
return;
}
Window::setVisible(isVisible);
Input::setCapture(isVisible ? this : nullptr);
setFocus(nullptr);

View File

@ -0,0 +1,395 @@
#include <Common/Hook.h>
#include <Common/Log.h>
#include <Common/Time.h>
#include <D3dDdi/Adapter.h>
#include <D3dDdi/Device.h>
#include <D3dDdi/Resource.h>
#include <Dll/Dll.h>
#include <DDraw/DirectDrawSurface.h>
#include <DDraw/RealPrimarySurface.h>
#include <Gdi/Cursor.h>
#include <Gdi/GuiThread.h>
#include <Overlay/ConfigWindow.h>
#include <Overlay/Steam.h>
#include <Win32/DpiAwareness.h>
namespace
{
decltype(&DirectDrawCreateEx) g_steamDirectDrawCreateEx = nullptr;
D3dDdi::Adapter* g_adapter = nullptr;
IDirectDraw7* g_dd = nullptr;
IDirect3D7* g_d3d = nullptr;
IDirectDrawSurface7* g_rt = nullptr;
D3dDdi::Resource* g_rtResource = nullptr;
D3dDdi::Resource* g_bbResource = nullptr;
UINT g_bbSubResourceIndex = 0;
IDirect3DDevice7* g_dev = nullptr;
UINT g_width = 0;
UINT g_height = 0;
bool g_isOverlayOpen = false;
long long g_qpcLastRender = 0;
HWND g_window = nullptr;
void releaseDevice();
void* getTargetFunc(void* hookedFunc);
BOOL WINAPI flushInstructionCache(HANDLE hProcess, LPCVOID lpBaseAddress, SIZE_T dwSize)
{
LOG_FUNC("FlushInstructionCache", hProcess, lpBaseAddress, dwSize);
auto hookedFunc = const_cast<BYTE*>(static_cast<const BYTE*>(lpBaseAddress));
if ((Dll::g_jmpTargetProcs.DirectDrawCreate == lpBaseAddress ||
Dll::g_jmpTargetProcs.DirectDrawCreateEx == lpBaseAddress) &&
0xE9 == hookedFunc[0] && 5 == dwSize)
{
int& jmpOffset = *reinterpret_cast<int*>(hookedFunc + 1);
decltype(&DirectDrawCreateEx) directDrawCreateEx = nullptr;
if (!g_steamDirectDrawCreateEx && Dll::g_jmpTargetProcs.DirectDrawCreateEx == lpBaseAddress)
{
directDrawCreateEx = reinterpret_cast<decltype(&DirectDrawCreateEx)>(hookedFunc + 5 + jmpOffset);
}
auto targetFunc = getTargetFunc(hookedFunc);
auto gameOverlayRenderer = GetModuleHandle("GameOverlayRenderer.dll");
if (gameOverlayRenderer && Compat::getModuleHandleFromAddress(targetFunc) == gameOverlayRenderer)
{
if (directDrawCreateEx)
{
g_steamDirectDrawCreateEx = directDrawCreateEx;
LOG_INFO << "Steam overlay support activated";
}
LOG_DEBUG << "Restoring hook: " << Compat::funcPtrToStr(lpBaseAddress);
DWORD oldProtect = 0;
VirtualProtect(hookedFunc, 5, PAGE_EXECUTE_READWRITE, &oldProtect);
auto newFunc = Dll::g_jmpTargetProcs.DirectDrawCreate == lpBaseAddress
? Dll::g_newProcs.DirectDrawCreate : Dll::g_newProcs.DirectDrawCreateEx;
jmpOffset = reinterpret_cast<BYTE*>(newFunc) - (hookedFunc + 5);
VirtualProtect(hookedFunc, 5, PAGE_EXECUTE_READ, &oldProtect);
}
}
return LOG_RESULT(CALL_ORIG_FUNC(FlushInstructionCache)(hProcess, lpBaseAddress, dwSize));
}
HWND WINAPI getActiveWindow()
{
LOG_FUNC("steamGetActiveWindow");
return LOG_RESULT(nullptr);
}
BOOL WINAPI getClientRect(HWND hWnd, LPRECT lpRect)
{
LOG_FUNC("steamGetClientRect", hWnd, lpRect);
Win32::ScopedDpiAwareness dpiAwareness;
return LOG_RESULT(GetClientRect(hWnd, lpRect));
}
HWND WINAPI getForegroundWindow()
{
LOG_FUNC("steamGetForegroundWindow");
HWND result = GetForegroundWindow();
if (result)
{
HWND presentationWindow = DDraw::RealPrimarySurface::getPresentationWindow();
if (presentationWindow && GetParent(presentationWindow) == result)
{
g_window = presentationWindow;
return LOG_RESULT(presentationWindow);
}
}
return LOG_RESULT(nullptr);
}
void* getTargetFunc(void* hookedFunc)
{
auto targetFunc = reinterpret_cast<BYTE*>(hookedFunc);
while (0xE9 == targetFunc[0])
{
targetFunc += 5 + *reinterpret_cast<int*>(targetFunc + 1);
}
return targetFunc;
}
template <typename Intf>
void release(Intf*& intf)
{
if (intf)
{
intf->lpVtbl->Release(intf);
intf = nullptr;
}
}
void releaseAll()
{
releaseDevice();
release(g_d3d);
release(g_dd);
g_adapter = nullptr;
}
void releaseDevice()
{
release(g_dev);
release(g_rt);
g_rtResource = nullptr;
g_width = 0;
g_height = 0;
}
void* removeHook(HMODULE gameOverlayRenderer, const wchar_t* origDDrawModulePath, void* hookedFunc)
{
LOG_FUNC("Steam::removeHook", gameOverlayRenderer, origDDrawModulePath, hookedFunc);
auto targetFunc = getTargetFunc(hookedFunc);
if (targetFunc != hookedFunc && Compat::getModuleHandleFromAddress(targetFunc) == gameOverlayRenderer)
{
auto offset = Compat::getModuleFileOffset(hookedFunc);
if (0 == offset)
{
return LOG_RESULT(nullptr);
}
std::ifstream f(origDDrawModulePath, std::ios::in | std::ios::binary);
f.seekg(offset);
char instructions[20] = {};
f.read(instructions, sizeof(instructions));
f.close();
unsigned totalInstructionSize = 0;
while (totalInstructionSize < 5)
{
unsigned instructionSize = Compat::getInstructionSize(instructions + totalInstructionSize);
if (0 == instructionSize)
{
return LOG_RESULT(nullptr);
}
totalInstructionSize += instructionSize;
}
DWORD oldProtect = 0;
VirtualProtect(hookedFunc, totalInstructionSize, PAGE_EXECUTE_READWRITE, &oldProtect);
memcpy(hookedFunc, instructions, totalInstructionSize);
VirtualProtect(hookedFunc, totalInstructionSize, PAGE_EXECUTE_READ, &oldProtect);
CALL_ORIG_FUNC(FlushInstructionCache)(GetCurrentProcess(), hookedFunc, totalInstructionSize);
return LOG_RESULT(targetFunc);
}
return LOG_RESULT(nullptr);
}
LONG WINAPI setClassLongW(HWND hWnd, int nIndex, LONG dwNewLong)
{
LOG_FUNC("steamSetClassLongW", hWnd, nIndex, dwNewLong);
if (GCLP_HCURSOR == nIndex)
{
HWND presentationWindow = DDraw::RealPrimarySurface::getPresentationWindow();
if (presentationWindow && presentationWindow == hWnd)
{
g_isOverlayOpen = 0 == dwNewLong;
LOG_DEBUG << "Steam overlay is " << (g_isOverlayOpen ? "opening" : "closing");
auto exStyle = CALL_ORIG_FUNC(GetWindowLongA)(hWnd, GWL_EXSTYLE);
CALL_ORIG_FUNC(SetWindowLongA)(hWnd, GWL_EXSTYLE,
g_isOverlayOpen ? (exStyle & ~WS_EX_TRANSPARENT) : (exStyle | WS_EX_TRANSPARENT));
if (g_isOverlayOpen)
{
auto configWindow = Gdi::GuiThread::getConfigWindow();
if (configWindow)
{
configWindow->setVisible(false);
}
Gdi::Cursor::setEmulated(false);
}
}
}
return LOG_RESULT(CALL_ORIG_FUNC(SetWindowLongW)(hWnd, nIndex, dwNewLong));
}
bool updateDevice(D3dDdi::Resource& resource)
{
auto adapter = &resource.getDevice().getAdapter();
if (adapter != g_adapter)
{
releaseAll();
g_adapter = adapter;
}
if (!g_dd)
{
auto result = g_steamDirectDrawCreateEx(
adapter->getGuid(), reinterpret_cast<void**>(&g_dd), IID_IDirectDraw7, nullptr);
if (FAILED(result))
{
LOG_ONCE("Failed to create DirectDraw object for Steam overlay: " << Compat::hex(result));
return false;
}
result = g_dd->lpVtbl->SetCooperativeLevel(g_dd, nullptr, DDSCL_NORMAL);
if (FAILED(result))
{
LOG_ONCE("Failed to set cooperative level for Steam overlay: " << Compat::hex(result));
release(g_dd);
return false;
}
}
if (!g_d3d)
{
auto result = g_dd->lpVtbl->QueryInterface(g_dd, IID_IDirect3D7, reinterpret_cast<void**>(&g_d3d));
if (FAILED(result))
{
LOG_ONCE("Failed to create Direct3D object for Steam overlay: " << Compat::hex(result));
return false;
}
}
auto& si = resource.getFixedDesc().pSurfList[0];
if (!g_rt || si.Width != g_width || si.Height != g_height || FAILED(g_rt->lpVtbl->IsLost(g_rt)))
{
releaseDevice();
DDSURFACEDESC2 desc = {};
desc.dwSize = sizeof(desc);
desc.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT | DDSD_CAPS;
desc.dwWidth = si.Width;
desc.dwHeight = si.Height;
desc.ddpfPixelFormat = D3dDdi::getPixelFormat(D3DDDIFMT_X8R8G8B8);
desc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_3DDEVICE | DDSCAPS_VIDEOMEMORY;
auto& formats = adapter->getInfo().formatOps;
if (formats.find(D3dDdi::FOURCC_NULL) != formats.end())
{
D3dDdi::Resource::setFormatOverride(D3dDdi::FOURCC_NULL);
}
D3dDdi::Resource::enableConfig(false);
auto result = g_dd->lpVtbl->CreateSurface(g_dd, &desc, &g_rt, nullptr);
D3dDdi::Resource::enableConfig(true);
D3dDdi::Resource::setFormatOverride(D3DDDIFMT_UNKNOWN);
if (FAILED(result))
{
LOG_ONCE("Failed to create DirectDrawSurface object for Steam overlay: " << Compat::hex(result));
return false;
}
g_width = si.Width;
g_height = si.Height;
g_rtResource = D3dDdi::Device::findResource(DDraw::DirectDrawSurface::getDriverResourceHandle(*g_rt));
}
if (!g_dev)
{
auto result = g_d3d->lpVtbl->CreateDevice(g_d3d, IID_IDirect3DHALDevice, g_rt, &g_dev);
if (FAILED(result))
{
LOG_ONCE("Failed to create Direct3DDevice for Steam overlay: " << Compat::hex(result));
return false;
}
g_dev->lpVtbl->BeginScene(g_dev);
g_dev->lpVtbl->Clear(g_dev, 0, nullptr, D3DCLEAR_TARGET, 0, 0, 0);
g_dev->lpVtbl->EndScene(g_dev);
}
return true;
}
}
namespace Overlay
{
namespace Steam
{
void flush()
{
if (!g_steamDirectDrawCreateEx)
{
return;
}
auto qpcNow = Time::queryPerformanceCounter();
if (g_isOverlayOpen || qpcNow - g_qpcLastRender > Time::g_qpcFrequency / 20)
{
DDraw::RealPrimarySurface::scheduleOverlayUpdate();
}
}
Resources getResources()
{
Resources resources = {};
resources.rtResource = g_rtResource;
resources.bbResource = g_bbResource;
resources.bbSubResourceIndex = g_bbSubResourceIndex;
return resources;
}
HWND getWindow()
{
return g_window;
}
void init(const wchar_t* origDDrawModulePath)
{
HOOK_FUNCTION(kernel32, FlushInstructionCache, flushInstructionCache);
auto gameOverlayRenderer = GetModuleHandle("GameOverlayRenderer.dll");
if (!gameOverlayRenderer)
{
return;
}
removeHook(gameOverlayRenderer, origDDrawModulePath, Dll::g_jmpTargetProcs.DirectDrawCreate);
g_steamDirectDrawCreateEx = static_cast<decltype(&DirectDrawCreateEx)>(
removeHook(gameOverlayRenderer, origDDrawModulePath, Dll::g_jmpTargetProcs.DirectDrawCreateEx));
if (g_steamDirectDrawCreateEx)
{
LOG_INFO << "Steam overlay support activated";
}
}
void installHooks()
{
auto gameOverlayRenderer = GetModuleHandle("GameOverlayRenderer.dll");
if (!gameOverlayRenderer)
{
return;
}
LOG_INFO << "Installing Steam overlay hooks";
Compat::hookIatFunction(gameOverlayRenderer, "GetActiveWindow", getActiveWindow);
Compat::hookIatFunction(gameOverlayRenderer, "GetClientRect", getClientRect);
Compat::hookIatFunction(gameOverlayRenderer, "GetForegroundWindow", getForegroundWindow);
Compat::hookIatFunction(gameOverlayRenderer, "SetClassLongW", setClassLongW);
}
bool isOverlayOpen()
{
return g_isOverlayOpen;
}
void onDestroyWindow(HWND hwnd)
{
if (hwnd == g_window)
{
g_window = nullptr;
}
}
void render(D3dDdi::Resource& resource, unsigned subResourceIndex)
{
LOG_FUNC("Steam::render", resource, subResourceIndex);
if (!g_steamDirectDrawCreateEx || !updateDevice(resource))
{
return;
}
g_bbResource = &resource;
g_bbSubResourceIndex = subResourceIndex;
g_dev->lpVtbl->BeginScene(g_dev);
g_dev->lpVtbl->EndScene(g_dev);
g_bbResource = nullptr;
g_bbSubResourceIndex = 0;
g_qpcLastRender = Time::queryPerformanceCounter();
}
}
}

View File

@ -0,0 +1,28 @@
#pragma once
namespace D3dDdi
{
class Resource;
}
namespace Overlay
{
namespace Steam
{
struct Resources
{
D3dDdi::Resource* rtResource;
D3dDdi::Resource* bbResource;
UINT bbSubResourceIndex;
};
void flush();
Resources getResources();
HWND getWindow();
void init(const wchar_t* origDDrawModulePath);
void installHooks();
bool isOverlayOpen();
void onDestroyWindow(HWND hwnd);
void render(D3dDdi::Resource& resource, unsigned subResourceIndex);
}
}