mirror of
https://github.com/mumble-voip/mumble.git
synced 2025-03-14 12:43:05 +00:00

Keeping these up-to-date is just super tedious and they don't really fulfill any purpose these days.
1145 lines
34 KiB
C++
1145 lines
34 KiB
C++
// Copyright The Mumble Developers. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license
|
|
// that can be found in the LICENSE file at the root of the
|
|
// Mumble source tree or at <https://www.mumble.info/LICENSE>.
|
|
|
|
#include "lib.h"
|
|
#include <d3d9.h>
|
|
#include <time.h>
|
|
|
|
Direct3D9Data *d3dd = nullptr;
|
|
|
|
typedef IDirect3D9 *(WINAPI *pDirect3DCreate9)(UINT SDKVersion);
|
|
typedef HRESULT(WINAPI *pDirect3DCreate9Ex)(UINT SDKVersion, IDirect3D9Ex **ppD3D);
|
|
|
|
template< class T > class Stash {
|
|
public:
|
|
Stash(T *variable, T newValue) : var(variable), value(*var) { *variable = newValue; }
|
|
~Stash() { *var = value; }
|
|
|
|
private:
|
|
T *var;
|
|
T value;
|
|
};
|
|
|
|
struct D3DTLVERTEX {
|
|
float x, y, z, rhw; // Position
|
|
float tu, tv; // Texture coordinates
|
|
};
|
|
static const DWORD D3DFVF_TLVERTEX = D3DFVF_XYZRHW | D3DFVF_TEX1;
|
|
|
|
class DevState : public Pipe {
|
|
public:
|
|
IDirect3DDevice9 *dev;
|
|
IDirect3DStateBlock9 *pSB;
|
|
|
|
/// Non-Win8: Initial ref count, directly after device creation.
|
|
/// Win8: Unused
|
|
LONG initRefCount;
|
|
LONG refCount;
|
|
/// Refcount of self (library). Used to make the overlay transparent
|
|
/// (invisible) to the outside.
|
|
LONG myRefCount;
|
|
DWORD dwMyThread;
|
|
|
|
D3DTLVERTEX vertices[4];
|
|
LPDIRECT3DTEXTURE9 texTexture;
|
|
|
|
clock_t timeT;
|
|
unsigned int frameCount;
|
|
|
|
DevState();
|
|
|
|
void createCleanState();
|
|
void releaseData();
|
|
void releaseAll();
|
|
void draw();
|
|
|
|
virtual void blit(unsigned int x, unsigned int y, unsigned int w, unsigned int h);
|
|
virtual void setRect();
|
|
virtual void newTexture(unsigned int width, unsigned int height);
|
|
};
|
|
|
|
/// Vtable offset; see d3d9.h of win-/D3D-API.
|
|
/// 3 from IUnknown + 14 in IDirect3D9, 0-based => 16
|
|
static const int VTABLE_OFFSET_ID3D_CREATEDEVICE = 16;
|
|
/// Vtable offset; see d3d9.h of win-/D3D-API.
|
|
/// Offset: 3 from IUnknown + 14 from IDirect3D9 + 4 in IDirect3D9Ex, 0-based => 20
|
|
static const int VTABLE_OFFSET_ID3D_CREATEDEVICE_EX = 20;
|
|
|
|
typedef map< IDirect3DDevice9 *, DevState * > DevMapType;
|
|
static DevMapType devMap;
|
|
static bool bHooked = false;
|
|
|
|
DevState::DevState() {
|
|
dev = nullptr;
|
|
pSB = nullptr;
|
|
dwMyThread = 0;
|
|
initRefCount = 0;
|
|
refCount = 0;
|
|
myRefCount = 0;
|
|
texTexture = nullptr;
|
|
|
|
timeT = clock();
|
|
frameCount = 0;
|
|
|
|
for (int i = 0; i < 4; ++i) {
|
|
vertices[i].x = vertices[i].y = 0.0f;
|
|
vertices[i].tu = vertices[i].tv = 0.0f;
|
|
vertices[i].z = vertices[i].rhw = 1.0f;
|
|
}
|
|
}
|
|
|
|
void DevState::releaseData() {
|
|
ods("D3D9: Release Data");
|
|
|
|
if (texTexture) {
|
|
texTexture->Release();
|
|
texTexture = nullptr;
|
|
}
|
|
}
|
|
|
|
void DevState::blit(unsigned int x, unsigned int y, unsigned int w, unsigned int h) {
|
|
// Blit is called often. Thus, we do not want to always log here.
|
|
#ifdef EXTENDED_OVERLAY_DEBUGOUTPUT
|
|
ods("D3D9: Blit %d %d %d %d", x, y, w, h);
|
|
#endif
|
|
|
|
if (!texTexture || !a_ucTexture || uiLeft == uiRight)
|
|
return;
|
|
|
|
D3DLOCKED_RECT lr;
|
|
|
|
if ((x == 0) && (y == 0) && (w == uiWidth) && (h == uiHeight)) {
|
|
if (texTexture->LockRect(0, &lr, nullptr, D3DLOCK_DISCARD) != D3D_OK)
|
|
return;
|
|
} else {
|
|
RECT r;
|
|
|
|
r.left = x;
|
|
r.top = y;
|
|
r.right = x + w;
|
|
r.bottom = y + h;
|
|
|
|
if (texTexture->LockRect(0, &lr, &r, 0) != D3D_OK)
|
|
return;
|
|
}
|
|
|
|
for (unsigned int r = 0; r < h; ++r) {
|
|
unsigned char *dptr = reinterpret_cast< unsigned char * >(lr.pBits) + r * lr.Pitch;
|
|
unsigned char *sptr = a_ucTexture + 4 * ((y + r) * uiWidth + x);
|
|
memcpy(dptr, sptr, w * 4);
|
|
}
|
|
|
|
texTexture->UnlockRect(0);
|
|
}
|
|
|
|
void DevState::setRect() {
|
|
ods("D3D9: New subrect");
|
|
|
|
float w = static_cast< float >(uiWidth);
|
|
float h = static_cast< float >(uiHeight);
|
|
|
|
float left = static_cast< float >(uiLeft) - 0.5f;
|
|
float top = static_cast< float >(uiTop) - 0.5f;
|
|
float right = static_cast< float >(uiRight) + 0.5f;
|
|
float bottom = static_cast< float >(uiBottom) + 0.5f;
|
|
|
|
float texl = (left) / w;
|
|
float text = (top) / h;
|
|
float texr = (right + 1.0f) / w;
|
|
float texb = (bottom + 1.0f) / h;
|
|
|
|
vertices[0].x = left;
|
|
vertices[0].y = top;
|
|
vertices[0].tu = texl;
|
|
vertices[0].tv = text;
|
|
|
|
vertices[1].x = right;
|
|
vertices[1].y = top;
|
|
vertices[1].tu = texr;
|
|
vertices[1].tv = text;
|
|
|
|
vertices[2].x = right;
|
|
vertices[2].y = bottom;
|
|
vertices[2].tu = texr;
|
|
vertices[2].tv = texb;
|
|
|
|
vertices[3].x = left;
|
|
vertices[3].y = bottom;
|
|
vertices[3].tu = texl;
|
|
vertices[3].tv = texb;
|
|
}
|
|
|
|
void DevState::newTexture(unsigned int width, unsigned int height) {
|
|
ods("D3D9: New texture %d x %d", width, height);
|
|
|
|
if (texTexture) {
|
|
texTexture->Release();
|
|
texTexture = nullptr;
|
|
}
|
|
|
|
dev->CreateTexture(uiWidth, uiHeight, 1, D3DUSAGE_DYNAMIC, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &texTexture, nullptr);
|
|
|
|
for (int i = 0; i < 4; ++i) {
|
|
vertices[i].x = vertices[i].y = vertices[i].z = 0.0f;
|
|
vertices[i].tu = vertices[i].tv = 0.0f;
|
|
vertices[i].rhw = 1.0f;
|
|
}
|
|
}
|
|
|
|
void DevState::releaseAll() {
|
|
ods("D3D9: Release All");
|
|
releaseData();
|
|
if (pSB)
|
|
pSB->Release();
|
|
pSB = nullptr;
|
|
}
|
|
|
|
void DevState::draw() {
|
|
clock_t t = clock();
|
|
float elapsed = static_cast< float >(t - timeT) / CLOCKS_PER_SEC;
|
|
++frameCount;
|
|
if (elapsed > OVERLAY_FPS_INTERVAL) {
|
|
OverlayMsg om;
|
|
om.omh.uiMagic = OVERLAY_MAGIC_NUMBER;
|
|
om.omh.uiType = OVERLAY_MSGTYPE_FPS;
|
|
om.omh.iLength = sizeof(OverlayMsgFps);
|
|
om.omf.fps = frameCount / elapsed;
|
|
|
|
sendMessage(om);
|
|
|
|
frameCount = 0;
|
|
timeT = t;
|
|
}
|
|
|
|
D3DVIEWPORT9 vp;
|
|
dev->GetViewport(&vp);
|
|
|
|
checkMessage(vp.Width, vp.Height);
|
|
|
|
if (!a_ucTexture || (uiLeft == uiRight))
|
|
return;
|
|
|
|
if (!texTexture) {
|
|
unsigned int l, r, t, b;
|
|
l = uiLeft;
|
|
r = uiRight;
|
|
t = uiTop;
|
|
b = uiBottom;
|
|
newTexture(uiWidth, uiHeight);
|
|
blit(0, 0, uiWidth, uiHeight);
|
|
|
|
uiLeft = l;
|
|
uiRight = r;
|
|
uiTop = t;
|
|
uiBottom = b;
|
|
setRect();
|
|
}
|
|
|
|
dev->SetTexture(0, texTexture);
|
|
dev->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, vertices, sizeof(D3DTLVERTEX));
|
|
}
|
|
|
|
void DevState::createCleanState() {
|
|
if (dwMyThread != 0) {
|
|
ods("D3D9: CreateCleanState from other thread.");
|
|
}
|
|
Stash< DWORD > stashThread(&dwMyThread, GetCurrentThreadId());
|
|
|
|
if (pSB)
|
|
pSB->Release();
|
|
pSB = nullptr;
|
|
|
|
IDirect3DStateBlock9 *pStateBlock = nullptr;
|
|
dev->CreateStateBlock(D3DSBT_ALL, &pStateBlock);
|
|
if (!pStateBlock)
|
|
return;
|
|
|
|
pStateBlock->Capture();
|
|
|
|
dev->CreateStateBlock(D3DSBT_ALL, &pSB);
|
|
if (!pSB) {
|
|
pStateBlock->Release();
|
|
return;
|
|
}
|
|
|
|
D3DVIEWPORT9 vp;
|
|
dev->GetViewport(&vp);
|
|
|
|
dev->SetVertexShader(nullptr);
|
|
dev->SetPixelShader(nullptr);
|
|
dev->SetFVF(D3DFVF_TLVERTEX);
|
|
|
|
dev->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);
|
|
dev->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD);
|
|
dev->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); // 0x16
|
|
dev->SetRenderState(D3DRS_WRAP0, FALSE); // 0x80
|
|
|
|
dev->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
|
|
dev->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE);
|
|
dev->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
|
|
|
|
dev->SetRenderState(D3DRS_ALPHATESTENABLE, TRUE);
|
|
dev->SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATER);
|
|
|
|
dev->SetRenderState(D3DRS_ZENABLE, FALSE);
|
|
dev->SetRenderState(D3DRS_ZWRITEENABLE, FALSE);
|
|
dev->SetRenderState(D3DRS_ZFUNC, D3DCMP_ALWAYS);
|
|
dev->SetRenderState(D3DRS_COLORVERTEX, FALSE);
|
|
|
|
dev->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
|
|
dev->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
|
|
dev->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);
|
|
|
|
dev->SetRenderState(D3DRS_LIGHTING, FALSE);
|
|
|
|
pSB->Capture();
|
|
|
|
pStateBlock->Apply();
|
|
pStateBlock->Release();
|
|
}
|
|
|
|
static HardHook hhCreateDevice;
|
|
static HardHook hhCreateDeviceEx;
|
|
static HardHook hhReset;
|
|
static HardHook hhResetEx;
|
|
static HardHook hhAddRef;
|
|
static HardHook hhRelease;
|
|
static HardHook hhPresent;
|
|
static HardHook hhPresentEx;
|
|
static HardHook hhSwapPresent;
|
|
|
|
/// Present the overlay.
|
|
///
|
|
/// If called via IDirect3DDevice9::present() or IDirect3DDevice9Ex::present(),
|
|
/// idd will be non-nullptr and ids ill be nullptr.
|
|
///
|
|
/// If called via IDirect3DSwapChain9::present(), both idd and ids will be
|
|
/// non-nullptr.
|
|
///
|
|
/// The doPresent function expects the following assumptions to be valid:
|
|
///
|
|
/// - Only one swap chain used at the same time.
|
|
/// - Windowed? IDirect3D9SwapChain::present() is used. ("Additional swap chain" is used)
|
|
/// - Full screen? IDirect3D9Device::present() is used. (Implicit swap chain for IDirect3D9Device is used)
|
|
///
|
|
/// It's either/or.
|
|
///
|
|
/// If doPresent is called multiple times per frame (say, for different swap chains),
|
|
/// the overlay will break badly when DevState::draw() is called. FPS counting will be off,
|
|
/// different render targets with different sizes will cause a size-renegotiation with Mumble
|
|
/// every frame, etc.
|
|
static void doPresent(IDirect3DDevice9 *idd, IDirect3DSwapChain9 *ids) {
|
|
DevMapType::iterator it = devMap.find(idd);
|
|
DevState *ds = it != devMap.end() ? it->second : nullptr;
|
|
HRESULT hres;
|
|
|
|
if (ds && ds->pSB) {
|
|
if (ds->dwMyThread != 0) {
|
|
ods("D3D9: doPresent from other thread");
|
|
}
|
|
Stash< DWORD > stashThread(&(ds->dwMyThread), GetCurrentThreadId());
|
|
|
|
// Get the back buffer.
|
|
// If we're called via IDirect3DSwapChain9, acquire it via the swap chain object.
|
|
// Otherwise, acquire it via the device itself.
|
|
IDirect3DSurface9 *pTarget = nullptr;
|
|
if (ids) {
|
|
hres = ids->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &pTarget);
|
|
if (FAILED(hres)) {
|
|
if (hres == D3DERR_INVALIDCALL) {
|
|
ods("D3D9: IDirect3DSwapChain9::GetBackBuffer failed. BackBuffer index equals or exceeds the total "
|
|
"number of back buffers");
|
|
} else {
|
|
ods("D3D9: IDirect3DSwapChain9::GetBackBuffer failed");
|
|
}
|
|
}
|
|
} else {
|
|
hres = idd->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &pTarget);
|
|
if (FAILED(hres)) {
|
|
if (hres == D3DERR_INVALIDCALL) {
|
|
ods("D3D9: IDirect3DDevice9::GetBackBuffer failed. BackBuffer index equals or exceeds the total "
|
|
"number of back buffers");
|
|
} else {
|
|
ods("D3D9: IDirect3DDevice9::GetBackBuffer failed");
|
|
}
|
|
}
|
|
}
|
|
|
|
IDirect3DSurface9 *pRenderTarget = nullptr;
|
|
hres = idd->GetRenderTarget(0, &pRenderTarget);
|
|
if (FAILED(hres)) {
|
|
if (hres == D3DERR_NOTFOUND) {
|
|
ods("D3D9: IDirect3DDevice9::GetRenderTarget failed. There is no render target with the specified "
|
|
"index");
|
|
} else if (hres == D3DERR_INVALIDCALL) {
|
|
ods("D3D9: IDirect3DDevice9::GetRenderTarget failed. One of the passed arguments was invalid");
|
|
} else {
|
|
ods("D3D9: IDirect3DDevice9::GetRenderTarget failed");
|
|
}
|
|
}
|
|
|
|
// Present is called for each frame. Thus, we do not want to always log here.
|
|
#ifdef EXTENDED_OVERLAY_DEBUGOUTPUT
|
|
ods("D3D9: doPresent BackB %p RenderT %p", pTarget, pRenderTarget);
|
|
#endif
|
|
|
|
IDirect3DStateBlock9 *pStateBlock = nullptr;
|
|
idd->CreateStateBlock(D3DSBT_ALL, &pStateBlock);
|
|
pStateBlock->Capture();
|
|
|
|
ds->pSB->Apply();
|
|
|
|
if (pTarget != pRenderTarget) {
|
|
hres = idd->SetRenderTarget(0, pTarget);
|
|
if (FAILED(hres)) {
|
|
ods("D3D9: IDirect3DDevice9::SetRenderTarget failed");
|
|
}
|
|
}
|
|
|
|
// If we're called via IDirect3DSwapChain9::present(), we have to
|
|
// get the size of the back buffer and manually set the viewport size
|
|
// to match it.
|
|
//
|
|
// Although the call to IDirect3DDevice9::SetRenderTarget() above is
|
|
// documented as updating the device's viewport:
|
|
//
|
|
// "Setting a new render target will cause the viewport (see Viewports
|
|
// and Clipping (Direct3D 9)) to be set to the full size of the new
|
|
// render target."
|
|
//
|
|
// (via https://msdn.microsoft.com/en-us/library/windows/desktop/bb174455(v=vs.85).aspx)
|
|
//
|
|
// ...this doesn't happen. At least for some programs such as Final Fantasy XIV
|
|
// and Battle.net Launcher. For these programs, we get a viewport of 1x1.
|
|
//
|
|
// The viewport we set here is used in the call below to DevState::draw()
|
|
// as the full size of the screen/window.
|
|
if (ids) {
|
|
D3DPRESENT_PARAMETERS pp;
|
|
hres = ids->GetPresentParameters(&pp);
|
|
if (FAILED(hres)) {
|
|
ods("D3D9: IDirect3DSwapChain9::GetPresentParameters failed");
|
|
} else {
|
|
if (pp.BackBufferWidth != 0 && pp.BackBufferHeight != 0) {
|
|
D3DVIEWPORT9 vp;
|
|
vp.X = 0;
|
|
vp.Y = 0;
|
|
vp.Width = pp.BackBufferWidth;
|
|
vp.Height = pp.BackBufferHeight;
|
|
vp.MinZ = 0.0f;
|
|
vp.MaxZ = 1.0f;
|
|
|
|
idd->SetViewport(&vp);
|
|
}
|
|
}
|
|
}
|
|
|
|
idd->BeginScene();
|
|
ds->draw();
|
|
idd->EndScene();
|
|
|
|
pStateBlock->Apply();
|
|
pStateBlock->Release();
|
|
|
|
pRenderTarget->Release();
|
|
pTarget->Release();
|
|
|
|
// ods("D3D9: Finished ref is %d %d", ds->myRefCount, ds->refCount);
|
|
}
|
|
}
|
|
|
|
|
|
typedef HRESULT(__stdcall *SwapPresentType)(IDirect3DSwapChain9 *, CONST RECT *, CONST RECT *, HWND, CONST RGNDATA *,
|
|
DWORD);
|
|
static HRESULT __stdcall mySwapPresent(IDirect3DSwapChain9 *ids, CONST RECT *pSourceRect, CONST RECT *pDestRect,
|
|
HWND hDestWindowOverride, CONST RGNDATA *pDirtyRegion, DWORD dwFlags) {
|
|
// Present is called for each frame. Thus, we do not want to always log here.
|
|
#ifdef EXTENDED_OVERLAY_DEBUGOUTPUT
|
|
ods("D3D9: SwapChain Present");
|
|
#endif
|
|
|
|
IDirect3DDevice9 *idd = nullptr;
|
|
ids->GetDevice(&idd);
|
|
if (idd) {
|
|
doPresent(idd, ids);
|
|
idd->Release();
|
|
}
|
|
|
|
// TODO: Move logic to HardHook.
|
|
// Call base without active hook in case of no trampoline.
|
|
SwapPresentType oSwapPresent = (SwapPresentType) hhSwapPresent.call;
|
|
hhSwapPresent.restore();
|
|
HRESULT hr = oSwapPresent(ids, pSourceRect, pDestRect, hDestWindowOverride, pDirtyRegion, dwFlags);
|
|
hhSwapPresent.inject();
|
|
|
|
return hr;
|
|
}
|
|
|
|
typedef HRESULT(__stdcall *PresentType)(IDirect3DDevice9 *, CONST RECT *, CONST RECT *, HWND, CONST RGNDATA *);
|
|
static HRESULT __stdcall myPresent(IDirect3DDevice9 *idd, CONST RECT *pSourceRect, CONST RECT *pDestRect,
|
|
HWND hDestWindowOverride, CONST RGNDATA *pDirtyRegion) {
|
|
// Present is called for each frame. Thus, we do not want to always log here.
|
|
#ifdef EXTENDED_OVERLAY_DEBUGOUTPUT
|
|
ods("D3D9: Device Present");
|
|
#endif
|
|
|
|
doPresent(idd, nullptr);
|
|
|
|
// TODO: Move logic to HardHook.
|
|
// Call base without active hook in case of no trampoline.
|
|
PresentType oPresent = (PresentType) hhPresent.call;
|
|
hhPresent.restore();
|
|
HRESULT hr = oPresent(idd, pSourceRect, pDestRect, hDestWindowOverride, pDirtyRegion);
|
|
hhPresent.inject();
|
|
|
|
return hr;
|
|
}
|
|
|
|
typedef HRESULT(__stdcall *PresentExType)(IDirect3DDevice9Ex *, CONST RECT *, CONST RECT *, HWND, CONST RGNDATA *,
|
|
DWORD);
|
|
static HRESULT __stdcall myPresentEx(IDirect3DDevice9Ex *idd, CONST RECT *pSourceRect, CONST RECT *pDestRect,
|
|
HWND hDestWindowOverride, CONST RGNDATA *pDirtyRegion, DWORD dwFlags) {
|
|
// Present is called for each frame. Thus, we do not want to always log here.
|
|
#ifdef EXTENDED_OVERLAY_DEBUGOUTPUT
|
|
ods("D3D9: Device Present Ex");
|
|
#endif
|
|
|
|
doPresent(idd, nullptr);
|
|
|
|
PresentExType oPresentEx = (PresentExType) hhPresentEx.call;
|
|
hhPresentEx.restore();
|
|
HRESULT hr = oPresentEx(idd, pSourceRect, pDestRect, hDestWindowOverride, pDirtyRegion, dwFlags);
|
|
hhPresentEx.inject();
|
|
|
|
return hr;
|
|
}
|
|
|
|
typedef HRESULT(__stdcall *ResetType)(IDirect3DDevice9 *, D3DPRESENT_PARAMETERS *);
|
|
static HRESULT __stdcall myReset(IDirect3DDevice9 *idd, D3DPRESENT_PARAMETERS *param) {
|
|
ods("D3D9: Chaining Reset");
|
|
|
|
DevMapType::iterator it = devMap.find(idd);
|
|
DevState *ds = it != devMap.end() ? it->second : nullptr;
|
|
if (ds) {
|
|
if (ds->dwMyThread != 0) {
|
|
ods("D3D9: myReset from other thread");
|
|
}
|
|
Stash< DWORD > stashThread(&(ds->dwMyThread), GetCurrentThreadId());
|
|
|
|
ds->releaseAll();
|
|
}
|
|
|
|
// TODO: Move logic to HardHook.
|
|
// Call base without active hook in case of no trampoline.
|
|
ResetType oReset = (ResetType) hhReset.call;
|
|
hhReset.restore();
|
|
HRESULT hr = oReset(idd, param);
|
|
hhReset.inject();
|
|
|
|
if (ds)
|
|
ds->createCleanState();
|
|
|
|
return hr;
|
|
}
|
|
|
|
typedef HRESULT(__stdcall *ResetExType)(IDirect3DDevice9Ex *, D3DPRESENT_PARAMETERS *, D3DDISPLAYMODEEX *);
|
|
static HRESULT __stdcall myResetEx(IDirect3DDevice9Ex *idd, D3DPRESENT_PARAMETERS *param, D3DDISPLAYMODEEX *param2) {
|
|
ods("D3D9: Chaining ResetEx");
|
|
|
|
DevMapType::iterator it = devMap.find(idd);
|
|
DevState *ds = it != devMap.end() ? it->second : nullptr;
|
|
if (ds) {
|
|
if (ds->dwMyThread) {
|
|
ods("D3D9: myResetEx from other thread");
|
|
}
|
|
Stash< DWORD > stashThread(&(ds->dwMyThread), GetCurrentThreadId());
|
|
|
|
ds->releaseAll();
|
|
}
|
|
|
|
// TODO: Move logic to HardHook.
|
|
// Call base without active hook in case of no trampoline.
|
|
ResetExType oResetEx = (ResetExType) hhResetEx.call;
|
|
hhResetEx.restore();
|
|
HRESULT hr = oResetEx(idd, param, param2);
|
|
hhResetEx.inject();
|
|
|
|
if (ds)
|
|
ds->createCleanState();
|
|
|
|
return hr;
|
|
}
|
|
|
|
typedef ULONG(__stdcall *AddRefType)(IDirect3DDevice9 *);
|
|
|
|
static ULONG __stdcall myAddRef(IDirect3DDevice9 *idd) {
|
|
Mutex m;
|
|
|
|
DevMapType::iterator it = devMap.find(idd);
|
|
DevState *ds = it != devMap.end() ? it->second : nullptr;
|
|
if (ds) {
|
|
// AddRef is called very often. Thus, we do not want to always log here.
|
|
#ifdef EXTENDED_OVERLAY_DEBUGOUTPUT
|
|
ods("D3D9: Using own Refcount implementation for call to AddRef.");
|
|
#endif
|
|
|
|
if (ds->dwMyThread == GetCurrentThreadId()) {
|
|
ds->myRefCount++;
|
|
} else {
|
|
ds->refCount++;
|
|
}
|
|
return ds->initRefCount + ds->refCount;
|
|
}
|
|
|
|
// TODO: Move logic to HardHook.
|
|
// Call base without active hook in case of no trampoline.
|
|
AddRefType oAddRef = (AddRefType) hhAddRef.call;
|
|
hhAddRef.restore();
|
|
ULONG res = oAddRef(idd);
|
|
hhAddRef.inject();
|
|
|
|
// AddRef is called very often. Thus, we do not want to always log here.
|
|
#ifdef EXTENDED_OVERLAY_DEBUGOUTPUT
|
|
ods("D3D9: Chained AddRef with result %d", res);
|
|
#endif
|
|
|
|
return res;
|
|
}
|
|
|
|
static ULONG __stdcall myWin8AddRef(IDirect3DDevice9 *idd) {
|
|
Mutex m;
|
|
|
|
DevMapType::iterator it = devMap.find(idd);
|
|
DevState *ds = it != devMap.end() ? it->second : nullptr;
|
|
if (ds && ds->dwMyThread == GetCurrentThreadId()) {
|
|
// AddRef is called very often. Thus, we do not want to always log here.
|
|
#ifdef EXTENDED_OVERLAY_DEBUGOUTPUT
|
|
ods("D3D9: Using own Refcount implementation for call to AddRef (Win8).");
|
|
#endif
|
|
|
|
ds->myRefCount++;
|
|
return ds->refCount;
|
|
}
|
|
|
|
// TODO: Move logic to HardHook.
|
|
// Call base without active hook in case of no trampoline.
|
|
AddRefType oAddRef = (AddRefType) hhAddRef.call;
|
|
hhAddRef.restore();
|
|
ULONG res = oAddRef(idd);
|
|
hhAddRef.inject();
|
|
|
|
if (ds)
|
|
ds->refCount = res;
|
|
|
|
// AddRef is called very often. Thus, we do not want to always log here.
|
|
#ifdef EXTENDED_OVERLAY_DEBUGOUTPUT
|
|
ods("D3D9: Chained AddRef (Win8) with result %d", res);
|
|
#endif
|
|
|
|
return res;
|
|
}
|
|
|
|
typedef ULONG(__stdcall *ReleaseType)(IDirect3DDevice9 *);
|
|
|
|
static ULONG __stdcall myRelease(IDirect3DDevice9 *idd) {
|
|
Mutex m;
|
|
|
|
DevMapType::iterator it = devMap.find(idd);
|
|
DevState *ds = it != devMap.end() ? it->second : nullptr;
|
|
if (ds) {
|
|
// Release is called very often. Thus, we do not want to always log here.
|
|
#ifdef EXTENDED_OVERLAY_DEBUGOUTPUT
|
|
ods("D3D9: Using own Refcount implementation for call to Release.");
|
|
#endif
|
|
|
|
if (ds->dwMyThread == GetCurrentThreadId()) {
|
|
ds->myRefCount--;
|
|
return ds->initRefCount + ds->refCount;
|
|
} else {
|
|
ds->refCount--;
|
|
}
|
|
|
|
if (ds->refCount <= 1) {
|
|
ds->disconnect();
|
|
}
|
|
|
|
if (ds->refCount >= 0) {
|
|
return ds->initRefCount + ds->refCount;
|
|
}
|
|
|
|
ods("D3D9: Final release is following. MyRefs = %d, Tot = %d", ds->myRefCount, ds->refCount);
|
|
|
|
if (ds->dwMyThread != 0) {
|
|
ods("D3D9: finalRelease from other thread");
|
|
}
|
|
|
|
// Codeblock for stashing threadid
|
|
{
|
|
Stash< DWORD > stashThread(&(ds->dwMyThread), GetCurrentThreadId());
|
|
|
|
ds->releaseAll();
|
|
}
|
|
|
|
ods("D3D9: Final release of %p. MyRefs = %d Tot = %d", idd, ds->myRefCount, ds->refCount);
|
|
|
|
devMap.erase(it);
|
|
delete ds;
|
|
ds = nullptr;
|
|
}
|
|
|
|
// TODO: Move logic to HardHook.
|
|
// Call base without active hook in case of no trampoline.
|
|
ReleaseType oRelease = (ReleaseType) hhRelease.call;
|
|
hhRelease.restore();
|
|
ULONG res = oRelease(idd);
|
|
hhRelease.inject();
|
|
|
|
// Release is called very often. Thus, we do not want to always log here.
|
|
#ifdef EXTENDED_OVERLAY_DEBUGOUTPUT
|
|
ods("D3D9: Chained Release with result %d", res);
|
|
#endif
|
|
|
|
return res;
|
|
}
|
|
|
|
static ULONG __stdcall myWin8Release(IDirect3DDevice9 *idd) {
|
|
Mutex m;
|
|
|
|
DevMapType::iterator it = devMap.find(idd);
|
|
DevState *ds = it != devMap.end() ? it->second : nullptr;
|
|
if (ds) {
|
|
// Release is called very often. Thus, we do not want to always log here.
|
|
#ifdef EXTENDED_OVERLAY_DEBUGOUTPUT
|
|
ods("D3D9: Using own Refcount implementation for call to Release.");
|
|
#endif
|
|
|
|
if (ds->dwMyThread == GetCurrentThreadId()) {
|
|
ds->myRefCount--;
|
|
return ds->refCount;
|
|
}
|
|
if (ds->refCount == 1) {
|
|
ds->disconnect();
|
|
|
|
ods("D3D9: Final release. MyRefs = %d, Tot = %d", ds->myRefCount, ds->refCount);
|
|
|
|
if (ds->dwMyThread != 0) {
|
|
ods("D3D9: finalRelease from other thread");
|
|
}
|
|
|
|
// Codeblock for stashing threadid
|
|
{
|
|
Stash< DWORD > stashThread(&(ds->dwMyThread), GetCurrentThreadId());
|
|
|
|
ds->releaseAll();
|
|
}
|
|
|
|
ods("D3D9: Final release of %p. MyRefs = %d Tot = %d", idd, ds->myRefCount, ds->refCount);
|
|
|
|
devMap.erase(it);
|
|
delete ds;
|
|
ds = nullptr;
|
|
}
|
|
}
|
|
|
|
// TODO: Move logic to HardHook.
|
|
// Call base without active hook in case of no trampoline.
|
|
ReleaseType oRelease = (ReleaseType) hhRelease.call;
|
|
hhRelease.restore();
|
|
ULONG res = oRelease(idd);
|
|
hhRelease.inject();
|
|
|
|
if (ds)
|
|
ds->refCount = res;
|
|
|
|
// Release is called very often. Thus, we do not want to always log here.
|
|
#ifdef EXTENDED_OVERLAY_DEBUGOUTPUT
|
|
ods("D3D9: Chained Release (Win8) with result: %d", res);
|
|
#endif
|
|
|
|
return res;
|
|
}
|
|
|
|
static IDirect3DDevice9 *findOriginalDevice(IDirect3DDevice9 *device) {
|
|
IDirect3DSwapChain9 *pSwap = nullptr;
|
|
device->GetSwapChain(0, &pSwap);
|
|
if (pSwap) {
|
|
IDirect3DDevice9 *originalDevice = nullptr;
|
|
if (SUCCEEDED(pSwap->GetDevice(&originalDevice))) {
|
|
if (originalDevice == device) {
|
|
// Found the original device. Release responsibility is passed
|
|
// to the caller.
|
|
} else {
|
|
device = findOriginalDevice(originalDevice);
|
|
originalDevice->Release();
|
|
}
|
|
|
|
} else {
|
|
ods("D3D9: Failed to recurse to find original device. Could not get Device from Swapchain.");
|
|
}
|
|
|
|
pSwap->Release();
|
|
} else {
|
|
ods("D3D9: Failed to recurse to find original device. Could not get Swapchain.");
|
|
}
|
|
|
|
return device;
|
|
}
|
|
|
|
typedef HRESULT(__stdcall *CreateDeviceType)(IDirect3D9 *, UINT, D3DDEVTYPE, HWND, DWORD, D3DPRESENT_PARAMETERS *,
|
|
IDirect3DDevice9 **);
|
|
static HRESULT __stdcall myCreateDevice(IDirect3D9 *id3d, UINT Adapter, D3DDEVTYPE DeviceType, HWND hFocusWindow,
|
|
DWORD BehaviorFlags, D3DPRESENT_PARAMETERS *pPresentationParameters,
|
|
IDirect3DDevice9 **ppReturnedDeviceInterface) {
|
|
Mutex m;
|
|
|
|
ods("D3D9: Chaining CreateDevice");
|
|
|
|
// BehaviorFlags &= ~D3DCREATE_PUREDEVICE;
|
|
|
|
// TODO: Move logic to HardHook.
|
|
// Call base without active hook in case of no trampoline.
|
|
CreateDeviceType oCreateDevice = (CreateDeviceType) hhCreateDevice.call;
|
|
hhCreateDevice.restore();
|
|
HRESULT hr = oCreateDevice(id3d, Adapter, DeviceType, hFocusWindow, BehaviorFlags, pPresentationParameters,
|
|
ppReturnedDeviceInterface);
|
|
hhCreateDevice.inject();
|
|
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
IDirect3DDevice9 *idd = *ppReturnedDeviceInterface;
|
|
|
|
// Get real interface, please.
|
|
IDirect3DDevice9 *originalDevice = findOriginalDevice(idd);
|
|
if (idd != originalDevice) {
|
|
ods("D3D9: Prepatched device, using original. %p => %p", idd, originalDevice);
|
|
idd = originalDevice;
|
|
}
|
|
|
|
DevState *ds = new DevState;
|
|
ds->dev = idd;
|
|
|
|
idd->AddRef();
|
|
ds->initRefCount = idd->Release();
|
|
|
|
DevMapType::iterator it = devMap.find(idd);
|
|
if (it != devMap.end()) {
|
|
ods("Device exists in devMap already - canceling injection into device. Thread prev: %d ; new: %d",
|
|
it->second->dwMyThread, GetCurrentThreadId());
|
|
delete ds;
|
|
|
|
return hr;
|
|
}
|
|
devMap[idd] = ds;
|
|
|
|
// The offsets are dependent on the declaration order of the struct.
|
|
// See IDirect3DDevice9 (2nd, 3rd, 17th, 18th functions)
|
|
const unsigned int offsetAddref = 1;
|
|
const unsigned int offsetRelease = 2;
|
|
const unsigned int offsetReset = 16;
|
|
const unsigned int offsetPresent = 17;
|
|
if (bIsWin8) {
|
|
hhAddRef.setupInterface(idd, offsetAddref, reinterpret_cast< voidFunc >(myWin8AddRef));
|
|
hhRelease.setupInterface(idd, offsetRelease, reinterpret_cast< voidFunc >(myWin8Release));
|
|
} else {
|
|
hhAddRef.setupInterface(idd, offsetAddref, reinterpret_cast< voidFunc >(myAddRef));
|
|
hhRelease.setupInterface(idd, offsetRelease, reinterpret_cast< voidFunc >(myRelease));
|
|
}
|
|
hhReset.setupInterface(idd, offsetReset, reinterpret_cast< voidFunc >(myReset));
|
|
hhPresent.setupInterface(idd, offsetPresent, reinterpret_cast< voidFunc >(myPresent));
|
|
|
|
IDirect3DSwapChain9 *pSwap = nullptr;
|
|
idd->GetSwapChain(0, &pSwap);
|
|
if (pSwap) {
|
|
// The offset is dependent on the declaration order of the struct.
|
|
// See IDirect3DSwapChain9 (Present is the fourth function)
|
|
const unsigned int offsetPresent = 3;
|
|
hhSwapPresent.setupInterface(pSwap, offsetPresent, reinterpret_cast< voidFunc >(mySwapPresent));
|
|
pSwap->Release();
|
|
} else {
|
|
ods("D3D9: Failed to get swapchain");
|
|
}
|
|
|
|
ds->createCleanState();
|
|
|
|
return hr;
|
|
}
|
|
|
|
typedef HRESULT(__stdcall *CreateDeviceExType)(IDirect3D9Ex *, UINT, D3DDEVTYPE, HWND, DWORD, D3DPRESENT_PARAMETERS *,
|
|
D3DDISPLAYMODEEX *, IDirect3DDevice9Ex **);
|
|
static HRESULT __stdcall myCreateDeviceEx(IDirect3D9Ex *id3d, UINT Adapter, D3DDEVTYPE DeviceType, HWND hFocusWindow,
|
|
DWORD BehaviorFlags, D3DPRESENT_PARAMETERS *pPresentationParameters,
|
|
D3DDISPLAYMODEEX *pFullscreenDisplayMode,
|
|
IDirect3DDevice9Ex **ppReturnedDeviceInterface) {
|
|
Mutex m;
|
|
ods("D3D9: Chaining CreateDeviceEx");
|
|
|
|
// BehaviorFlags &= ~D3DCREATE_PUREDEVICE;
|
|
|
|
// TODO: Move logic to HardHook.
|
|
// Call base without active hook in case of no trampoline.
|
|
CreateDeviceExType oCreateDeviceEx = (CreateDeviceExType) hhCreateDeviceEx.call;
|
|
hhCreateDeviceEx.restore();
|
|
HRESULT hr = oCreateDeviceEx(id3d, Adapter, DeviceType, hFocusWindow, BehaviorFlags, pPresentationParameters,
|
|
pFullscreenDisplayMode, ppReturnedDeviceInterface);
|
|
hhCreateDeviceEx.inject();
|
|
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
IDirect3DDevice9Ex *idd = *ppReturnedDeviceInterface;
|
|
|
|
DevState *ds = new DevState;
|
|
ds->dev = idd;
|
|
|
|
idd->AddRef();
|
|
ds->initRefCount = idd->Release();
|
|
|
|
DevMapType::iterator it = devMap.find(idd);
|
|
if (it != devMap.end()) {
|
|
ods("Device exists in devMap already - canceling injection into device. Thread prev: %d ; new: %d",
|
|
it->second->dwMyThread, GetCurrentThreadId());
|
|
delete ds;
|
|
|
|
return hr;
|
|
}
|
|
devMap[idd] = ds;
|
|
|
|
// The offsets are dependent on the declaration order of the struct.
|
|
// See IDirect3DDevice9 (2nd, 3rd, 17th, 18th functions)
|
|
const unsigned int offsetAddref = 1;
|
|
const unsigned int offsetRelease = 2;
|
|
const unsigned int offsetReset = 16;
|
|
const unsigned int offsetPresent = 17;
|
|
// On IDirect3DDevice9Ex
|
|
const unsigned int offsetPresentEx = 121;
|
|
const unsigned int offsetResetEx = 132;
|
|
if (bIsWin8) {
|
|
hhAddRef.setupInterface(idd, offsetAddref, reinterpret_cast< voidFunc >(myWin8AddRef));
|
|
hhRelease.setupInterface(idd, offsetRelease, reinterpret_cast< voidFunc >(myWin8Release));
|
|
} else {
|
|
hhAddRef.setupInterface(idd, offsetAddref, reinterpret_cast< voidFunc >(myAddRef));
|
|
hhRelease.setupInterface(idd, offsetRelease, reinterpret_cast< voidFunc >(myRelease));
|
|
}
|
|
hhReset.setupInterface(idd, offsetReset, reinterpret_cast< voidFunc >(myReset));
|
|
hhResetEx.setupInterface(idd, offsetResetEx, reinterpret_cast< voidFunc >(myResetEx));
|
|
hhPresent.setupInterface(idd, offsetPresent, reinterpret_cast< voidFunc >(myPresent));
|
|
hhPresentEx.setupInterface(idd, offsetPresentEx, reinterpret_cast< voidFunc >(myPresentEx));
|
|
|
|
IDirect3DSwapChain9 *pSwap = nullptr;
|
|
idd->GetSwapChain(0, &pSwap);
|
|
if (pSwap) {
|
|
// The offset is dependent on the declaration order of the struct.
|
|
// See IDirect3DSwapChain9 (Present is the fourth function)
|
|
const unsigned int offsetPresent = 3;
|
|
hhSwapPresent.setupInterface(pSwap, offsetPresent, reinterpret_cast< voidFunc >(mySwapPresent));
|
|
pSwap->Release();
|
|
} else {
|
|
ods("D3D9: Failed to get swapchain for DevEx");
|
|
}
|
|
|
|
ds->createCleanState();
|
|
return hr;
|
|
}
|
|
|
|
static void HookCreateRaw(voidFunc vfCreate) {
|
|
ods("D3D9: Injecting CreateDevice Raw");
|
|
hhCreateDevice.setup(vfCreate, reinterpret_cast< voidFunc >(myCreateDevice));
|
|
}
|
|
|
|
static void HookCreateRawEx(voidFunc vfCreate) {
|
|
ods("D3D9: Injecting CreateDeviceEx Raw");
|
|
hhCreateDeviceEx.setup(vfCreate, reinterpret_cast< voidFunc >(myCreateDeviceEx));
|
|
}
|
|
|
|
static void HookCreate(IDirect3D9 *pD3D) {
|
|
ods("D3D9: Injecting CreateDevice");
|
|
|
|
hhCreateDevice.setupInterface(pD3D, VTABLE_OFFSET_ID3D_CREATEDEVICE, reinterpret_cast< voidFunc >(myCreateDevice));
|
|
}
|
|
|
|
static void HookCreateEx(IDirect3D9Ex *pD3D) {
|
|
ods("D3D9Ex: Injecting CreateDevice / CreateDeviceEx");
|
|
|
|
// Best effort hooking for the non-ex create method. If it was set up
|
|
// previously this setup call is safely ignored by the hook.
|
|
HookCreate(pD3D);
|
|
|
|
hhCreateDeviceEx.setupInterface(pD3D, VTABLE_OFFSET_ID3D_CREATEDEVICE_EX,
|
|
reinterpret_cast< voidFunc >(myCreateDeviceEx));
|
|
}
|
|
|
|
static void hookD3D9(HMODULE hD3D, bool preonly);
|
|
|
|
// @param preonly If rawpatching the createdevice-functions fails, don't try to
|
|
// patch Direct3DCreate9.
|
|
// Should be true on PROC_ATTACH, and false on THREAD_ATTACH. (?)
|
|
void checkD3D9Hook(bool preonly) {
|
|
static bool bCheckHookActive = false;
|
|
if (bCheckHookActive) {
|
|
ods("D3D9: Recursion in checkD3D9Hook");
|
|
return;
|
|
}
|
|
|
|
bCheckHookActive = true;
|
|
|
|
HMODULE hD3D = GetModuleHandle("D3D9.DLL");
|
|
|
|
if (hD3D) {
|
|
if (!bHooked) {
|
|
hookD3D9(hD3D, preonly);
|
|
}
|
|
} else {
|
|
#ifdef EXTENDED_OVERLAY_DEBUGOUTPUT
|
|
ods("D3D9: No D3D9.DLL found as loaded. No hooking at this point.");
|
|
#endif
|
|
}
|
|
|
|
bCheckHookActive = false;
|
|
}
|
|
|
|
static void hookD3D9(HMODULE hD3D, bool preonly) {
|
|
char procname[MODULEFILEPATH_BUFLEN];
|
|
GetModuleFileName(nullptr, procname, ARRAY_NUM_ELEMENTS(procname));
|
|
ods("D3D9: hookD3D9 in App '%s'", procname);
|
|
|
|
// Add a ref to ourselves; we do NOT want to get unloaded directly from this process.
|
|
HMODULE hTempSelf = nullptr;
|
|
GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, reinterpret_cast< LPCTSTR >(&hookD3D9), &hTempSelf);
|
|
|
|
bHooked = true;
|
|
|
|
// Can we use the prepatch data?
|
|
wchar_t modulename[MODULEFILEPATH_BUFLEN];
|
|
GetModuleFileNameW(hD3D, modulename, ARRAY_NUM_ELEMENTS(modulename));
|
|
if (_wcsicmp(d3dd->wcFileName, modulename) == 0) {
|
|
// The module seems to match the one we prepared d3dd for.
|
|
|
|
unsigned char *raw = (unsigned char *) hD3D;
|
|
HookCreateRaw((voidFunc)(raw + d3dd->offsetCreate));
|
|
if (d3dd->offsetCreateEx) {
|
|
HookCreateRawEx((voidFunc)(raw + d3dd->offsetCreateEx));
|
|
}
|
|
|
|
} else if (!preonly) {
|
|
ods("D3D9: Interface changed, can't rawpatch. Current: %ls ; Previously: %ls", modulename, d3dd->wcFileName);
|
|
|
|
pDirect3DCreate9 d3dcreate9 = reinterpret_cast< pDirect3DCreate9 >(GetProcAddress(hD3D, "Direct3DCreate9"));
|
|
if (d3dcreate9) {
|
|
ods("D3D9: Got %p for Direct3DCreate9", d3dcreate9);
|
|
|
|
IDirect3D9 *id3d9 = d3dcreate9(D3D_SDK_VERSION);
|
|
if (id3d9) {
|
|
HookCreate(id3d9);
|
|
id3d9->Release();
|
|
} else {
|
|
ods("D3D9: Failed call to Direct3DCreate9");
|
|
}
|
|
} else {
|
|
ods("D3D9: Library without Direct3DCreate9?");
|
|
}
|
|
|
|
pDirect3DCreate9Ex d3dcreate9ex =
|
|
reinterpret_cast< pDirect3DCreate9Ex >(GetProcAddress(hD3D, "Direct3DCreate9Ex"));
|
|
if (d3dcreate9ex) {
|
|
ods("D3D9: Got %p for Direct3DCreate9Ex", d3dcreate9ex);
|
|
IDirect3D9Ex **id3d9ex = 0;
|
|
HRESULT hr = d3dcreate9ex(D3D_SDK_VERSION, id3d9ex);
|
|
if (SUCCEEDED(hr)) {
|
|
HookCreateEx(*id3d9ex);
|
|
(*id3d9ex)->Release();
|
|
} else {
|
|
switch (hr) {
|
|
case D3DERR_OUTOFVIDEOMEMORY:
|
|
ods("D3D11: Direct3DCreate9Ex returned with out of memory error.");
|
|
break;
|
|
case D3DERR_NOTAVAILABLE:
|
|
ods("D3D11: Direct3DCreate9Ex is not available.");
|
|
break;
|
|
default:
|
|
ods("D3D11: Unexpected return result from Direct3DCreate9Ex");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
bHooked = false;
|
|
}
|
|
}
|
|
|
|
extern "C" __declspec(dllexport) void __cdecl PrepareD3D9() {
|
|
if (!d3dd)
|
|
return;
|
|
|
|
ods("D3D9: Preparing static data for D3D9 Injection");
|
|
|
|
HMODULE hD3D = LoadLibrary("D3D9.DLL");
|
|
|
|
if (hD3D) {
|
|
GetModuleFileNameW(hD3D, d3dd->wcFileName, ARRAY_NUM_ELEMENTS(d3dd->wcFileName));
|
|
|
|
std::string d3d9FnName("Direct3DCreate9");
|
|
pDirect3DCreate9 d3dcreate9 = reinterpret_cast< pDirect3DCreate9 >(GetProcAddress(hD3D, d3d9FnName.c_str()));
|
|
if (!d3dcreate9) {
|
|
ods(("D3D9: Library without " + d3d9FnName).c_str());
|
|
} else {
|
|
if (!IsFnInModule(reinterpret_cast< voidFunc >(d3dcreate9), d3dd->wcFileName, "D3D9", d3d9FnName)) {
|
|
ods(("D3D9: " + d3d9FnName + " is not in D3D9 library").c_str());
|
|
} else {
|
|
IDirect3D9 *id3d9 = d3dcreate9(D3D_SDK_VERSION);
|
|
if (id3d9) {
|
|
void ***vtbl = (void ***) id3d9;
|
|
|
|
void *pCreate = (*vtbl)[VTABLE_OFFSET_ID3D_CREATEDEVICE];
|
|
|
|
if (!IsFnInModule(reinterpret_cast< voidFunc >(pCreate), d3dd->wcFileName, "D3D9",
|
|
"CreateDevice")) {
|
|
ods("D3D9: CreateDevice is not in D3D9 library");
|
|
} else {
|
|
size_t fn = reinterpret_cast< size_t >(pCreate);
|
|
size_t base = reinterpret_cast< size_t >(hD3D);
|
|
d3dd->offsetCreate = fn - base;
|
|
ods("D3D9: Successfully found prepatch offset: %p %p %p: %d", hD3D, d3dcreate9, pCreate,
|
|
d3dd->offsetCreate);
|
|
}
|
|
id3d9->Release();
|
|
}
|
|
}
|
|
}
|
|
|
|
std::string d3d9exFnName("Direct3DCreate9Ex");
|
|
pDirect3DCreate9Ex d3dcreate9ex =
|
|
reinterpret_cast< pDirect3DCreate9Ex >(GetProcAddress(hD3D, d3d9exFnName.c_str()));
|
|
if (!d3dcreate9ex) {
|
|
ods(("D3D9: Library without " + d3d9exFnName).c_str());
|
|
} else {
|
|
if (!IsFnInModule(reinterpret_cast< voidFunc >(d3dcreate9ex), d3dd->wcFileName, "D3D9", d3d9exFnName)) {
|
|
ods(("D3D9: " + d3d9exFnName + " is not in D3D9 library").c_str());
|
|
} else {
|
|
IDirect3D9Ex *id3d9 = nullptr;
|
|
d3dcreate9ex(D3D_SDK_VERSION, &id3d9);
|
|
if (id3d9) {
|
|
void ***vtbl = (void ***) id3d9;
|
|
void *pCreateEx = (*vtbl)[VTABLE_OFFSET_ID3D_CREATEDEVICE_EX];
|
|
|
|
if (!IsFnInModule(reinterpret_cast< voidFunc >(pCreateEx), d3dd->wcFileName, "D3D9",
|
|
"CreateDeviceEx")) {
|
|
ods("D3D9: CreateDeviceEx is not in D3D9 library");
|
|
} else {
|
|
size_t fn = reinterpret_cast< size_t >(pCreateEx);
|
|
size_t base = reinterpret_cast< size_t >(hD3D);
|
|
d3dd->offsetCreateEx = fn - base;
|
|
ods("D3D9: Successfully found prepatch ex offset: %p %p %p: %d", hD3D, d3dcreate9ex, pCreateEx,
|
|
d3dd->offsetCreateEx);
|
|
}
|
|
|
|
id3d9->Release();
|
|
}
|
|
}
|
|
}
|
|
|
|
FreeLibrary(hD3D);
|
|
}
|
|
}
|