Initial commit: Complete open-source Decal rebuild

All 5 phases of the open-source Decal rebuild:

Phase 1: 14 decompiled .NET projects (Interop.*, Adapter, FileService, DecalUtil)
Phase 2: 10 native DLLs rewritten as C# COM servers with matching GUIDs
  - DecalDat, DHS, SpellFilter, DecalInput, DecalNet, DecalFilters
  - Decal.Core, DecalControls, DecalRender, D3DService
Phase 3: C++ shims for Inject.DLL (D3D9 hooking) and LauncherHook.DLL
Phase 4: DenAgent WinForms tray application
Phase 5: WiX installer and build script

25 C# projects building with 0 errors.
Native C++ projects require VS 2022 + Windows SDK (x86).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
erik 2026-02-08 18:27:56 +01:00
commit d1442e3747
1382 changed files with 170725 additions and 0 deletions

748
Native/Inject/Canvas.cpp Normal file
View file

@ -0,0 +1,748 @@
// Canvas.cpp : Implementation of cCanvas
#include "stdafx.h"
#include "Inject.h"
#include "Canvas.h"
#include "Manager.h"
/////////////////////////////////////////////////////////////////////////////
// cCanvas
cCanvas::~cCanvas()
{
_ASSERTE( m_hdc == NULL );
if( m_rgn != NULL )
::DeleteObject( m_rgn );
}
void cCanvas::testSurface()
{
if( m_pSurface.p == NULL )
cManager::_p->createSurface( &m_sz, &m_pSurface );
else if( m_pSurface->IsLost() != DD_OK )
m_pSurface->Restore();
}
STDMETHODIMP cCanvas::PushClipRect(LPRECT prc, VARIANT_BOOL *pbVisible)
{
_ASSERTE( prc != NULL );
_ASSERTE( prc->right >= prc->left && prc->bottom >= prc->top );
_ASSERTE( pbVisible != NULL );
ClipParams &current = m_clipping.top();
if( !current.visible )
{
// Not visible means just push and hope for a pop
m_clipping.push( current );
return S_OK;
}
int nChildLeft = prc->left + current.window.left - current.org.x,
nChildTop = prc->top + current.window.top - current.org.y,
nChildWidth = prc->right - prc->left,
nChildHeight = prc->bottom - prc->top;
ClipParams p = {
{ nChildLeft, nChildTop, nChildLeft + nChildWidth, nChildTop + nChildHeight },
{ 0, 0 },
VARIANT_TRUE };
// Clip the four sides of the rectangle
if( p.window.left < current.window.left )
{
// Clip left edge
p.org.x += current.window.left - p.window.left;
p.window.left = current.window.left;
}
if( p.window.top < current.window.top )
{
// Clip top edge
p.org.y += current.window.top - p.window.top;
p.window.top = current.window.top;
}
if( p.window.right > current.window.right )
// Clip right edge
p.window.right = current.window.right;
if( p.window.bottom > current.window.bottom )
// Clip bottom edge
p.window.bottom = current.window.bottom;
// Check for clipping out of existance
if( p.window.left >= p.window.right || p.window.top >= p.window.bottom )
// This rectangle is no longer visible
p.visible = VARIANT_FALSE;
// Return the remainder of the rectangle
*pbVisible = p.visible;
m_clipping.push( p );
return S_OK;
}
STDMETHODIMP cCanvas::PopClipRect()
{
_ASSERTE( m_clipping.size() > 1 );
m_clipping.pop();
return S_OK;
}
STDMETHODIMP cCanvas::GetDC(HDC *pdc)
{
_ASSERTE( pdc != NULL );
testSurface();
if( m_hdc != NULL )
// Already checked out
return E_FAIL;
m_pSurface->GetDC( &m_hdc );
// Create a clipping region
ClipParams &param = m_clipping.top();
if( m_rgn == NULL )
m_rgn = ::CreateRectRgnIndirect( &param.window );
else
::SetRectRgn( m_rgn, param.window.left, param.window.top, param.window.right, param.window.bottom );
::SelectObject( m_hdc, m_rgn );
// Set the origin
::SetWindowOrgEx( m_hdc, -param.window.left + param.org.x, -param.window.top + param.org.y, NULL );
*pdc = m_hdc;
return S_OK;
}
STDMETHODIMP cCanvas::ReleaseDC()
{
if( m_hdc == NULL )
return E_FAIL;
m_pSurface->ReleaseDC( m_hdc );
m_hdc = NULL;
return S_OK;
}
STDMETHODIMP cCanvas::GetSurface(REFIID iid, void **ppvItf)
{
_ASSERTE( iid == IID_IDirectDrawSurface4 );
_ASSERTE( ppvItf != NULL );
testSurface();
return m_pSurface->QueryInterface( iid, ppvItf );
}
STDMETHODIMP cCanvas::GetClipParams(ClipParams *pParams)
{
_ASSERTE( pParams != NULL );
*pParams = m_clipping.top();
return S_OK;
}
STDMETHODIMP cCanvas::Fill(LPRECT prc, long nRGB)
{
_ASSERTE( prc != NULL );
_ASSERTE( prc->right > prc->left && prc->bottom > prc->top );
testSurface();
VARIANT_BOOL bVisible;
PushClipRect( prc, &bVisible );
if( bVisible )
{
DDBLTFX fx;
::memset( &fx, 0, sizeof( DDBLTFX ) );
fx.dwSize = sizeof( DDBLTFX );
DownMixRGB((BYTE)nRGB, (BYTE)(nRGB >> 8), (BYTE)(nRGB >> 16), (WORD *)&fx.dwFillColor);
m_pSurface->Blt( &m_clipping.top().window, NULL, NULL, DDBLT_COLORFILL, &fx );
}
PopClipRect();
return S_OK;
}
STDMETHODIMP cCanvas::Frame(LPRECT prc, long nRGB)
{
_ASSERTE( prc != NULL );
_ASSERTE( prc->right > prc->left && prc->bottom > prc->top );
testSurface();
// TODO: Add your implementation code here
return S_OK;
}
STDMETHODIMP cCanvas::get_WasLost(VARIANT_BOOL *pVal)
{
_ASSERTE( pVal != NULL );
*pVal = VARIANT_FALSE;
if( m_pSurface.p != NULL )
{
if( m_pSurface->IsLost() != DD_OK )
*pVal = VARIANT_TRUE;
}
else
*pVal = VARIANT_TRUE;
return S_OK;
}
STDMETHODIMP cCanvas::get_Size(LPSIZE pVal)
{
_ASSERTE( pVal != NULL );
*pVal = m_sz;
return S_OK;
}
STDMETHODIMP cCanvas::put_Size(LPSIZE newVal)
{
_ASSERTE( newVal->cx > 0 && newVal->cy > 0 );
_ASSERTE( m_clipping.size() == 1 );
if( m_sz.cx != newVal->cx || m_sz.cy != newVal->cy )
{
m_sz = *newVal;
if( m_pSurface.p )
// Next frame this'll be marked as lost
m_pSurface.Release();
// Reset the bounds in the root clip rectangle
ClipParams &clip = m_clipping.top();
clip.window.right = clip.window.left + newVal->cx;
clip.window.bottom = clip.window.top + newVal->cy;
}
return S_OK;
}
STDMETHODIMP cCanvas::Blt(LPRECT prcSrc, ICanvas *pSrc, LPPOINT pptDest)
{
_ASSERTE( pSrc != NULL );
_ASSERTE( prcSrc != NULL );
_ASSERTE( prcSrc->right > prcSrc->left && prcSrc->bottom > prcSrc->top );
_ASSERTE( pptDest != NULL );
testSurface();
// Ok, first, make up the clip rect
RECT rc = { pptDest->x, pptDest->y, pptDest->x + ( prcSrc->right - prcSrc->left ),
pptDest->y + ( prcSrc->bottom - prcSrc->top ) };
VARIANT_BOOL bVisible;
PushClipRect( &rc, &bVisible );
if( bVisible )
{
CComPtr< IDirectDrawSurface4 > pSurf;
pSrc->GetSurface( IID_IDirectDrawSurface4, reinterpret_cast< void ** >( &pSurf ) );
_ASSERTE( pSurf.p != NULL );
_ASSERTE( pSurf->IsLost() == DD_OK );
// Calculate the metrics
#ifdef _DEBUG
SIZE sz;
pSrc->get_Size( &sz );
_ASSERTE( prcSrc->left >= 0 && prcSrc->top >= 0 && prcSrc->right <= sz.cx && prcSrc->bottom <= sz.cy );
#endif
ClipParams &current = m_clipping.top();
RECT rcSrcClipped = { prcSrc->left + current.org.x, prcSrc->top + current.org.y,
prcSrc->left + current.org.x + ( current.window.right - current.window.left ),
prcSrc->top + current.org.y + ( current.window.bottom - current.window.top ) };
HRESULT hRes = S_OK;
if ( sourceAlpha < 255) {
if( cManager::_p->m_eAlphaBlendMode == eAlphaBlendSoftware )
{
// Software alpha blending
// It's actually plausible to implement this entirely using D3D and thus get hardware acceleration
// Perhaps we should detect device caps and use a suitable method?
// This should work on every Decal configuration though (except bank switched video cards, but who has them? :)
//Lock and calculate metrics for destination
DDSURFACEDESC2 ddsd;
memset(&ddsd, 0, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
int left = current.window.left;
int top = current.window.top;
int width = rcSrcClipped.right - rcSrcClipped.left;
int height = rcSrcClipped.bottom - rcSrcClipped.top;
if (FAILED(m_pSurface->Lock(NULL, &ddsd, DDLOCK_WAIT | DDLOCK_NOSYSLOCK, NULL)))
_ASSERTE("Failed to destination lock surface!");
WORD *dest = (WORD *)ddsd.lpSurface;
int iPitch = ddsd.lPitch / 2;
int initialJump = (top * iPitch) + left;
int jump = (iPitch - (width + left)) + left + width;
//Lock and calculate metrics for source
DDSURFACEDESC2 srcddsd;
memset(&srcddsd, 0, sizeof(srcddsd));
srcddsd.dwSize = sizeof(srcddsd);
if (FAILED(pSurf->Lock(NULL, &srcddsd, DDLOCK_WAIT | DDLOCK_NOSYSLOCK, NULL)))
_ASSERTE("Failed to lock source surface!");
WORD *src = (WORD *)srcddsd.lpSurface;
//Do it
int iSrcPitch = srcddsd.lPitch / 2;
int alpha = sourceAlpha;
int ialpha = 255 - alpha;
WORD *srcLine = new WORD[width];
WORD *destLine = new WORD[width];
WORD sB, dB, sG, dG, sR, dR;
dest += initialJump;
for (int y = 0; y < height; y++) {
// We'll read 1 line at a time - 80% of the speed decrease here is reading/writing against the surface
memcpy((void *)srcLine, src, width * 2);
memcpy((void *)destLine, dest, width * 2);
for (int x = 0; x < width; x++) {
// This method is very fast, but assumes a constant 128 alpha
// destLine[x] = ( (srcLine[x] & 0xF7DE) >> 1) + ( (destLine[x] & 0xF7DE) >> 1);
// Basically ((alpha * (src - dest) / MAXALPHA) + dest) for each component
// There's some significant room for MMX parrallelism here (~20% more FPS, methinks), but do we want to
// be writing ASM?
sB = srcLine[x] & ddsd.ddpfPixelFormat.dwBBitMask;
dB = destLine[x] & ddsd.ddpfPixelFormat.dwBBitMask;
sG = srcLine[x] & ddsd.ddpfPixelFormat.dwGBitMask;
dG = destLine[x] & ddsd.ddpfPixelFormat.dwGBitMask;
sR = srcLine[x] & ddsd.ddpfPixelFormat.dwRBitMask;
dR = destLine[x] & ddsd.ddpfPixelFormat.dwRBitMask;
destLine[x] = ddsd.ddpfPixelFormat.dwBBitMask & ((alpha * (sB - dB) >> 8) + dB) |
ddsd.ddpfPixelFormat.dwGBitMask & (((alpha * (sG - dG) >> 8) + dG)) |
ddsd.ddpfPixelFormat.dwRBitMask & (((alpha * (sR - dR) >> 8) + dR));
// The below is eversoslightly faster by assuming RGB 565 surface exposure. Some stupid old cards
// blow up however. Fiddle if you like, YMMV. I'm sticking with something that works :)
//sB = srcLine[x] & 0x1f;
//dB = destLine[x] & 0x1f;
//sG = (srcLine[x] >> 5) & 0x3f;
//dG = (destLine[x] >> 5) & 0x3f;
//sR = (srcLine[x] >> 11) & 0x1f;
//dR = (destLine[x] >> 11) & 0x1f;
//destLine[x] = ((alpha * (sB - dB) >> 8) + dB) |
// (((alpha * (sG - dG) >> 8) + dG) << 5) |
// (((alpha * (sR - dR) >> 8) + dR) << 11);*/
}
memcpy(dest, destLine, width * 2);
src += iSrcPitch;
dest += jump;
}
//Release
delete srcLine;
delete destLine;
if (FAILED(pSurf->Unlock(NULL)))
_ASSERTE("Failed to unlock source surface!");
if (FAILED(m_pSurface->Unlock(NULL)))
_ASSERTE("Failed to unlock destination surface!");
}
else if ((cManager::_p->m_eAlphaBlendMode == eAlphaBlendGDIPlus) && (AlphaBlendF!=NULL))
{
HDC hdcd;
HDC hdcs;
GetDC(&hdcd);
pSrc->GetDC(&hdcs);
hRes = AlphaBlendF(hdcd, current.org.x, current.org.y, rcSrcClipped.right-rcSrcClipped.left, rcSrcClipped.bottom-rcSrcClipped.top, hdcs, current.org.x, current.org.y, rcSrcClipped.right-rcSrcClipped.left, rcSrcClipped.bottom-rcSrcClipped.top, m_bf);
pSrc->ReleaseDC();
ReleaseDC();
}
}
else
{
hRes = m_pSurface->BltFast( current.window.left, current.window.top, pSurf, &rcSrcClipped, DDBLTFAST_SRCCOLORKEY | DDBLTFAST_WAIT );
}
/* if(!SUCCEEDED(hRes))
{
POINT pt = { 0, 0 };
ClientToScreen(cManager::_p->m_hMain, &pt);
/*if((pt.x==0) || (pt.y==0))
MessageBox(0,"asd","asd",0);*/
//RECT rcd = {pt.x, pt.y, rcSrcClipped.right-rcSrcClipped.left, rcSrcClipped.bottom-rcSrcClipped.top};
//m_pSurface->Blt(&rcd,pSurf,&rcSrcClipped,DDBLT_WAIT,NULL);
// RECT rcd = {0, 0, 640, 480};
/* rcSrcClipped.left+=current.window.left+pt.x;
rcSrcClipped.right+=current.window.left+pt.x;
rcSrcClipped.top+=current.window.top+pt.y+28;
rcSrcClipped.bottom+=current.window.top+pt.y+28;
/*
DDBLTFX ddbltfx;
ddbltfx.dwSize=sizeof(ddbltfx);
ddbltfx.dwFillColor=5;
m_pSurface->Blt(&rcSrcClipped,NULL,NULL,DDBLT_COLORFILL,&ddbltfx);*/
/* CComPtr< ICanvas > pCan;
cManager::_p->GetPrimarySurface(&pCan);
CComPtr< IDirectDrawSurface4 > pDDS4;
pCan->GetSurface( IID_IDirectDrawSurface4, reinterpret_cast< void ** >( &pDDS4 ) );
// if(m_pSurface.p==pDDS4.p)
if(cManager::_p->m_lpSurface!=NULL)
{
MessageBeep(0);
DDSURFACEDESC2 ddesc;
ddesc.dwSize=sizeof(DDSURFACEDESC2);
pSurf->Lock(&rcSrcClipped, &ddesc, DDLOCK_SURFACEMEMORYPTR , NULL);
//m_pSurface->Blt(&rcSrcClipped,pSurf,NULL,DDBLT_KEYSRC | DDBLT_WAIT ,NULL);
memcpy(cManager::_p->m_lpSurface, ddesc.lpSurface, 1000);
pSurf->Unlock(&rcSrcClipped);
SetEvent(cManager::_p->m_hDrawSync);
}
//m_pSurface->Blt(&rcSrcClipped,pSurf,NULL,DDBLT_KEYSRC | DDBLT_WAIT ,NULL);
/* RECT rc;
::GetWindowRect( cManager::_p->m_hMain, &rc );*/
/*
HDC hdcd;
HDC hdcs;
m_pSurface->GetDC(&hdcd);
pSurf->GetDC(&hdcs);
BitBlt(hdcd, pt.x+current.window.left, pt.y+28+current.window.top, 332, current.window.bottom-current.window.top, hdcs, rcSrcClipped.left, rcSrcClipped.top, SRCCOPY);
pSurf->ReleaseDC(hdcs);
m_pSurface->ReleaseDC(hdcd);*/
/* hRes=0;
}
/*if(!SUCCEEDED(hRes))
SetEvent(cManager::_p->m_hDrawSync);*/
_ASSERTE( SUCCEEDED( hRes ) );
}
PopClipRect();
_ASSERTMEM( _CrtCheckMemory() );
return S_OK;
}
STDMETHODIMP cCanvas::HitTest(LPPOINT ppt, VARIANT_BOOL *pbHit)
{
_ASSERTE( ppt != NULL );
_ASSERTE( pbHit != NULL );
// Assuming we're in screen coords
ClipParams &clip = m_clipping.top();
if( ppt->x >= clip.window.left && ppt->y >= clip.window.top &&
ppt->x <= clip.window.right && ppt->y <= clip.window.bottom )
{
*pbHit = VARIANT_TRUE;
}
else
*pbHit = VARIANT_FALSE;
return S_OK;
}
STDMETHODIMP cCanvas::ToClient(LPPOINT pt)
{
_ASSERTE( pt != NULL );
ClipParams &clip = m_clipping.top();
pt->x -= clip.window.left - clip.org.x;
pt->y -= clip.window.top - clip.org.y;
return S_OK;
}
STDMETHODIMP cCanvas::ToScreen(LPPOINT ppt)
{
_ASSERTE( ppt != NULL );
ClipParams &clip = m_clipping.top();
ppt->x += clip.window.left - clip.org.x;
ppt->y += clip.window.top - clip.org.y;
return S_OK;
}
STDMETHODIMP cCanvas::OffsetOrg(LPPOINT ppt, VARIANT_BOOL *pbVisible)
{
_ASSERTE( ppt != NULL );
_ASSERTE( pbVisible != NULL );
_ASSERTE( m_clipping.size() != 1 );
ClipParams &clip = m_clipping.top();
clip.org.x += ppt->x;
clip.org.y += ppt->y;
*pbVisible = clip.visible;
return S_OK;
}
STDMETHODIMP cCanvas::SetClipRect(LPRECT prc, VARIANT_BOOL *pbVisible)
{
_ASSERTE( prc != NULL );
_ASSERTE( prc->right >= prc->left && prc->bottom >= prc->top );
_ASSERTE( pbVisible != NULL );
_ASSERTE( m_clipping.size() != 1 );
ClipParams &current = m_clipping.top();
int nChildLeft = prc->left + current.window.left - current.org.x,
nChildTop = prc->top + current.window.top - current.org.y,
nChildWidth = prc->right - prc->left,
nChildHeight = prc->bottom - prc->top;
ClipParams p = {
{ nChildLeft, nChildTop, nChildLeft + nChildWidth, nChildTop + nChildHeight },
{ 0, 0 },
VARIANT_TRUE };
// Clip the four sides of the rectangle
if( p.window.left < current.window.left )
{
// Clip left edge
p.org.x += current.window.left - p.window.left;
p.window.left = current.window.left;
}
if( p.window.top < current.window.top )
{
// Clip top edge
p.org.y += current.window.top - p.window.top;
p.window.top = current.window.top;
}
if( p.window.right > current.window.right )
// Clip right edge
p.window.right = current.window.right;
if( p.window.bottom > current.window.bottom )
// Clip bottom edge
p.window.bottom = current.window.bottom;
// Check for clipping out of existance
if( p.window.left >= p.window.right || p.window.top >= p.window.bottom )
// This rectangle is no longer visible
p.visible = VARIANT_FALSE;
// Return the remainder of the rectangle
*pbVisible = p.visible;
m_clipping.top() = p;
return S_OK;
}
STDMETHODIMP cCanvas::put_Alpha(long Alpha)
{
m_bf.SourceConstantAlpha = sourceAlpha = Alpha;
return S_OK;
}
// processMask - Support function for DownMixRGB
void cCanvas::processMask(WORD bitMask, WORD *loBit, WORD *bitCount) {
_ASSERTE(loBit);
_ASSERTE(bitCount);
WORD mask = 1;
for (*loBit = 0; *loBit < 16; (*loBit)++) {
if (bitMask & mask)
break;
mask <<= 1;
}
for (*bitCount = 1; *bitCount < 32; (*bitCount)++) {
mask <<= 1;
if (!(bitMask & mask))
break;
}
}
// DownMixRGB - Returns a 16 bit RGB value suitable for this canvas from a standard R, G, B set
STDMETHODIMP cCanvas::DownMixRGB(WORD wRed, WORD wGreen, WORD wBlue, WORD *wDMRGB) {
_ASSERTE(wDMRGB);
if (wRBitCount == 0) {
// We've never obtained surface information before for this canvas
DDSURFACEDESC2 desc;
desc.dwSize = sizeof( DDSURFACEDESC2 );
if (FAILED(m_pSurface->GetSurfaceDesc(&desc)))
return S_FALSE;
processMask(desc.ddpfPixelFormat.dwRBitMask, &wRLoBit, &wRBitCount);
processMask(desc.ddpfPixelFormat.dwGBitMask, &wGLoBit, &wGBitCount);
processMask(desc.ddpfPixelFormat.dwBBitMask, &wBLoBit, &wBBitCount);
}
// Transparent color handling
if (wRed == 0 && wGreen == 255 && wBlue == 255) {
// We are a transparent colour - The downmix would've destroyed it
*wDMRGB = 0x07FF;
} else {
// Safe for a normal mix
*wDMRGB = ((wRed * (1 << wRBitCount)) / 256) << wRLoBit |
((wGreen * (1 << wGBitCount)) / 256) << wGLoBit |
((wBlue * (1 << wBBitCount)) / 256) << wBLoBit;
}
return S_OK;
}
STDMETHODIMP cCanvas::GetDCLong(long *DC)
{
HDC pdc;
GetDC(&pdc);
*DC = reinterpret_cast< long >( pdc );
return S_OK;
}
STDMETHODIMP cCanvas::SetTransparentColor(long TransColor)
{
unsigned short wDownmix;
DownMixRGB((BYTE)TransColor, (BYTE)(TransColor >> 8), (BYTE)(TransColor >> 16), &wDownmix );
_DDCOLORKEY colorkey;
colorkey.dwColorSpaceHighValue = wDownmix;
colorkey.dwColorSpaceLowValue = wDownmix;
m_pSurface->SetColorKey( DDCKEY_SRCBLT, &colorkey );
return S_OK;
}
STDMETHODIMP cCanvas::StretchBlt(LPRECT prcSrc, ICanvas *pSrc, LPRECT prcDest)
{
_ASSERTE( pSrc != NULL );
_ASSERTE( prcSrc != NULL );
_ASSERTE( prcSrc->right > prcSrc->left && prcSrc->bottom > prcSrc->top );
//_ASSERTE( pptDest != NULL );
testSurface();
//RECT rc = { pptDest->x, pptDest->y, pptDest->x + ( prcSrc->right - prcSrc->left ), pptDest->y + ( prcSrc->bottom - prcSrc->top ) };
// Ok, first, make up the clip rect
RECT rc = { prcDest->left, prcDest->top, prcDest->right, prcDest->bottom };
VARIANT_BOOL bVisible = TRUE;
PushClipRect( &rc, &bVisible );
//if( bVisible )
{
CComPtr< IDirectDrawSurface4 > pSurf;
pSrc->GetSurface( IID_IDirectDrawSurface4, reinterpret_cast< void ** >( &pSurf ) );
_ASSERTE( pSurf.p != NULL );
_ASSERTE( pSurf->IsLost() == DD_OK );
// Calculate the metrics
#ifdef _DEBUG
SIZE sz;
pSrc->get_Size( &sz );
_ASSERTE( prcSrc->left >= 0 && prcSrc->top >= 0 && prcSrc->right <= sz.cx && prcSrc->bottom <= sz.cy );
#endif
ClipParams &current = m_clipping.top();
RECT rcSrcClipped = {
prcSrc->left,
prcSrc->top,
prcSrc->right,// + ( current.window.right - current.window.left ),
prcSrc->bottom// + ( current.window.bottom - current.window.top )
};
RECT rcDestClipped = {
prcDest->left + current.org.x + current.window.left,
prcDest->top + current.org.y + current.window.top,
prcDest->left + prcDest->right + current.org.x,
prcDest->top + prcDest->bottom + current.org.y
};
HRESULT hRes = m_pSurface->Blt( &rcDestClipped, pSurf, &rcSrcClipped, DDBLT_KEYSRC | DDBLT_WAIT, NULL );
_ASSERTE( SUCCEEDED( hRes ) );
}
PopClipRect();
_ASSERTMEM( _CrtCheckMemory() );
return S_OK;
}