bool fcGraphicsDeviceD3D9::readTexture(void *o_buf, size_t bufsize, void *tex_, int width, int height, fcTextureFormat format) { HRESULT hr; IDirect3DTexture9 *tex = (IDirect3DTexture9*)tex_; // D3D11 と同様 render target の内容は CPU からはアクセス不可能になっている。 // staging texture を用意してそれに内容を移し、CPU はそれ経由でデータを読む。 IDirect3DSurface9 *surf_dst = findOrCreateStagingTexture(width, height, format); if (surf_dst == nullptr) { return false; } IDirect3DSurface9* surf_src = nullptr; hr = tex->GetSurfaceLevel(0, &surf_src); if (FAILED(hr)){ return false; } bool ret = false; hr = m_device->GetRenderTargetData(surf_src, surf_dst); if (SUCCEEDED(hr)) { D3DLOCKED_RECT locked; hr = surf_dst->LockRect(&locked, nullptr, D3DLOCK_READONLY); if (SUCCEEDED(hr)) { char *wpixels = (char*)o_buf; int wpitch = width * fcGetPixelSize(format); const char *rpixels = (const char*)locked.pBits; int rpitch = locked.Pitch; // D3D11 と同様表向き解像度と内部解像度が違うケースを考慮 // (しかし、少なくとも手元の環境では常に wpitch == rpitch っぽい) if (wpitch == rpitch) { memcpy(wpixels, rpixels, bufsize); } else { for (int i = 0; i < height; ++i) { memcpy(wpixels, rpixels, wpitch); wpixels += wpitch; rpixels += rpitch; } } surf_dst->UnlockRect(); // D3D9 の ARGB32 のピクセルの並びは BGRA になっているので並べ替える if (format == fcTextureFormat_ARGB32) { BGRA_RGBA_conversion((RGBA<uint8_t>*)o_buf, bufsize / 4); } ret = true; } } surf_src->Release(); return ret; }
bool Texture2D::GetData(unsigned level, void* dest) const { if (!object_.ptr_) { ATOMIC_LOGERROR("No texture created, can not get data"); return false; } if (!dest) { ATOMIC_LOGERROR("Null destination for getting data"); return false; } if (level >= levels_) { ATOMIC_LOGERROR("Illegal mip level for getting data"); return false; } if (graphics_->IsDeviceLost()) { ATOMIC_LOGWARNING("Getting texture data while device is lost"); return false; } int levelWidth = GetLevelWidth(level); int levelHeight = GetLevelHeight(level); D3DLOCKED_RECT d3dLockedRect; RECT d3dRect; d3dRect.left = 0; d3dRect.top = 0; d3dRect.right = levelWidth; d3dRect.bottom = levelHeight; IDirect3DSurface9* offscreenSurface = 0; // Need to use a offscreen surface & GetRenderTargetData() for rendertargets if (renderSurface_) { if (level != 0) { ATOMIC_LOGERROR("Can only get mip level 0 data from a rendertarget"); return false; } IDirect3DDevice9* device = graphics_->GetImpl()->GetDevice(); HRESULT hr = device->CreateOffscreenPlainSurface((UINT)width_, (UINT)height_, (D3DFORMAT)format_, D3DPOOL_SYSTEMMEM, &offscreenSurface, 0); if (FAILED(hr)) { ATOMIC_SAFE_RELEASE(offscreenSurface); ATOMIC_LOGD3DERROR("Could not create surface for getting rendertarget data", hr); return false; } hr = device->GetRenderTargetData((IDirect3DSurface9*)renderSurface_->GetSurface(), offscreenSurface); if (FAILED(hr)) { ATOMIC_LOGD3DERROR("Could not get rendertarget data", hr); offscreenSurface->Release(); return false; } hr = offscreenSurface->LockRect(&d3dLockedRect, &d3dRect, D3DLOCK_READONLY); if (FAILED(hr)) { ATOMIC_LOGD3DERROR("Could not lock surface for getting rendertarget data", hr); offscreenSurface->Release(); return false; } } else { HRESULT hr = ((IDirect3DTexture9*)object_.ptr_)->LockRect(level, &d3dLockedRect, &d3dRect, D3DLOCK_READONLY); if (FAILED(hr)) { ATOMIC_LOGD3DERROR("Could not lock texture", hr); return false; } } int height = levelHeight; if (IsCompressed()) height = (height + 3) >> 2; unsigned char* destPtr = (unsigned char*)dest; unsigned rowSize = GetRowDataSize(levelWidth); // GetRowDataSize() returns CPU-side (destination) data size, so need to convert for X8R8G8B8 if (format_ == D3DFMT_X8R8G8B8) rowSize = rowSize / 3 * 4; // Perform conversion to RGB / RGBA as necessary switch (format_) { default: for (int i = 0; i < height; ++i) { unsigned char* src = (unsigned char*)d3dLockedRect.pBits + i * d3dLockedRect.Pitch; memcpy(destPtr, src, rowSize); destPtr += rowSize; } break; case D3DFMT_X8R8G8B8: for (int i = 0; i < height; ++i) { unsigned char* src = (unsigned char*)d3dLockedRect.pBits + i * d3dLockedRect.Pitch; for (int j = 0; j < levelWidth; ++j) { destPtr[2] = *src++; destPtr[1] = *src++; destPtr[0] = *src++; ++src; destPtr += 3; } } break; case D3DFMT_A8R8G8B8: for (int i = 0; i < height; ++i) { unsigned char* src = (unsigned char*)d3dLockedRect.pBits + i * d3dLockedRect.Pitch; for (int j = 0; j < levelWidth; ++j) { destPtr[2] = *src++; destPtr[1] = *src++; destPtr[0] = *src++; destPtr[3] = *src++; destPtr += 4; } } break; } if (offscreenSurface) { offscreenSurface->UnlockRect(); offscreenSurface->Release(); } else ((IDirect3DTexture9*)object_.ptr_)->UnlockRect(level); return true; }
gl::Error Framebuffer9::readPixelsImpl(const gl::Context *context, const gl::Rectangle &area, GLenum format, GLenum type, size_t outputPitch, const gl::PixelPackState &pack, uint8_t *pixels) { const gl::FramebufferAttachment *colorbuffer = mState.getColorAttachment(0); ASSERT(colorbuffer); RenderTarget9 *renderTarget = nullptr; ANGLE_TRY(colorbuffer->getRenderTarget(context, &renderTarget)); ASSERT(renderTarget); IDirect3DSurface9 *surface = renderTarget->getSurface(); ASSERT(surface); D3DSURFACE_DESC desc; surface->GetDesc(&desc); if (desc.MultiSampleType != D3DMULTISAMPLE_NONE) { UNIMPLEMENTED(); // FIXME: Requires resolve using StretchRect into non-multisampled render target SafeRelease(surface); return gl::OutOfMemory() << "ReadPixels is unimplemented for multisampled framebuffer attachments."; } IDirect3DDevice9 *device = mRenderer->getDevice(); ASSERT(device); HRESULT result; IDirect3DSurface9 *systemSurface = nullptr; bool directToPixels = !pack.reverseRowOrder && pack.alignment <= 4 && mRenderer->getShareHandleSupport() && area.x == 0 && area.y == 0 && static_cast<UINT>(area.width) == desc.Width && static_cast<UINT>(area.height) == desc.Height && desc.Format == D3DFMT_A8R8G8B8 && format == GL_BGRA_EXT && type == GL_UNSIGNED_BYTE; if (directToPixels) { // Use the pixels ptr as a shared handle to write directly into client's memory result = device->CreateOffscreenPlainSurface(desc.Width, desc.Height, desc.Format, D3DPOOL_SYSTEMMEM, &systemSurface, reinterpret_cast<void**>(&pixels)); if (FAILED(result)) { // Try again without the shared handle directToPixels = false; } } if (!directToPixels) { result = device->CreateOffscreenPlainSurface(desc.Width, desc.Height, desc.Format, D3DPOOL_SYSTEMMEM, &systemSurface, nullptr); if (FAILED(result)) { ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY); SafeRelease(surface); return gl::OutOfMemory() << "Failed to allocate internal texture for ReadPixels."; } } result = device->GetRenderTargetData(surface, systemSurface); SafeRelease(surface); if (FAILED(result)) { SafeRelease(systemSurface); // It turns out that D3D will sometimes produce more error // codes than those documented. if (d3d9::isDeviceLostError(result)) { mRenderer->notifyDeviceLost(); } else { UNREACHABLE(); } return gl::OutOfMemory() << "Failed to read internal render target data."; } if (directToPixels) { SafeRelease(systemSurface); return gl::NoError(); } RECT rect; rect.left = gl::clamp(area.x, 0L, static_cast<LONG>(desc.Width)); rect.top = gl::clamp(area.y, 0L, static_cast<LONG>(desc.Height)); rect.right = gl::clamp(area.x + area.width, 0L, static_cast<LONG>(desc.Width)); rect.bottom = gl::clamp(area.y + area.height, 0L, static_cast<LONG>(desc.Height)); D3DLOCKED_RECT lock; result = systemSurface->LockRect(&lock, &rect, D3DLOCK_READONLY); if (FAILED(result)) { UNREACHABLE(); SafeRelease(systemSurface); return gl::OutOfMemory() << "Failed to lock internal render target."; } uint8_t *source = reinterpret_cast<uint8_t *>(lock.pBits); int inputPitch = lock.Pitch; const d3d9::D3DFormat &d3dFormatInfo = d3d9::GetD3DFormatInfo(desc.Format); gl::FormatType formatType(format, type); PackPixelsParams packParams; packParams.area.x = rect.left; packParams.area.y = rect.top; packParams.area.width = rect.right - rect.left; packParams.area.height = rect.bottom - rect.top; packParams.format = format; packParams.type = type; packParams.outputPitch = static_cast<GLuint>(outputPitch); packParams.pack = pack; PackPixels(packParams, d3dFormatInfo.info(), inputPitch, source, pixels); systemSurface->UnlockRect(); SafeRelease(systemSurface); return gl::NoError(); }
gl::Error Framebuffer9::readPixelsImpl(const gl::Rectangle &area, GLenum format, GLenum type, size_t outputPitch, const gl::PixelPackState &pack, uint8_t *pixels) const { ASSERT(pack.pixelBuffer.get() == nullptr); const gl::FramebufferAttachment *colorbuffer = mState.getColorAttachment(0); ASSERT(colorbuffer); RenderTarget9 *renderTarget = nullptr; gl::Error error = colorbuffer->getRenderTarget(&renderTarget); if (error.isError()) { return error; } ASSERT(renderTarget); IDirect3DSurface9 *surface = renderTarget->getSurface(); ASSERT(surface); D3DSURFACE_DESC desc; surface->GetDesc(&desc); if (desc.MultiSampleType != D3DMULTISAMPLE_NONE) { UNIMPLEMENTED(); // FIXME: Requires resolve using StretchRect into non-multisampled render target SafeRelease(surface); return gl::Error(GL_OUT_OF_MEMORY, "ReadPixels is unimplemented for multisampled framebuffer attachments."); } IDirect3DDevice9 *device = mRenderer->getDevice(); ASSERT(device); HRESULT result; IDirect3DSurface9 *systemSurface = nullptr; bool directToPixels = !pack.reverseRowOrder && pack.alignment <= 4 && mRenderer->getShareHandleSupport() && area.x == 0 && area.y == 0 && static_cast<UINT>(area.width) == desc.Width && static_cast<UINT>(area.height) == desc.Height && desc.Format == D3DFMT_A8R8G8B8 && format == GL_BGRA_EXT && type == GL_UNSIGNED_BYTE; if (directToPixels) { // Use the pixels ptr as a shared handle to write directly into client's memory result = device->CreateOffscreenPlainSurface(desc.Width, desc.Height, desc.Format, D3DPOOL_SYSTEMMEM, &systemSurface, reinterpret_cast<void**>(&pixels)); if (FAILED(result)) { // Try again without the shared handle directToPixels = false; } } if (!directToPixels) { result = device->CreateOffscreenPlainSurface(desc.Width, desc.Height, desc.Format, D3DPOOL_SYSTEMMEM, &systemSurface, nullptr); if (FAILED(result)) { ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY); SafeRelease(surface); return gl::Error(GL_OUT_OF_MEMORY, "Failed to allocate internal texture for ReadPixels."); } } result = device->GetRenderTargetData(surface, systemSurface); SafeRelease(surface); if (FAILED(result)) { SafeRelease(systemSurface); // It turns out that D3D will sometimes produce more error // codes than those documented. if (d3d9::isDeviceLostError(result)) { mRenderer->notifyDeviceLost(); } else { UNREACHABLE(); } return gl::Error(GL_OUT_OF_MEMORY, "Failed to read internal render target data."); } if (directToPixels) { SafeRelease(systemSurface); return gl::Error(GL_NO_ERROR); } RECT rect; rect.left = gl::clamp(area.x, 0L, static_cast<LONG>(desc.Width)); rect.top = gl::clamp(area.y, 0L, static_cast<LONG>(desc.Height)); rect.right = gl::clamp(area.x + area.width, 0L, static_cast<LONG>(desc.Width)); rect.bottom = gl::clamp(area.y + area.height, 0L, static_cast<LONG>(desc.Height)); D3DLOCKED_RECT lock; result = systemSurface->LockRect(&lock, &rect, D3DLOCK_READONLY); if (FAILED(result)) { UNREACHABLE(); SafeRelease(systemSurface); return gl::Error(GL_OUT_OF_MEMORY, "Failed to lock internal render target."); } uint8_t *source; int inputPitch; if (pack.reverseRowOrder) { source = reinterpret_cast<uint8_t*>(lock.pBits) + lock.Pitch * (rect.bottom - rect.top - 1); inputPitch = -lock.Pitch; } else { source = reinterpret_cast<uint8_t*>(lock.pBits); inputPitch = lock.Pitch; } const d3d9::D3DFormat &d3dFormatInfo = d3d9::GetD3DFormatInfo(desc.Format); const gl::InternalFormat &sourceFormatInfo = gl::GetInternalFormatInfo(d3dFormatInfo.internalFormat); if (sourceFormatInfo.format == format && sourceFormatInfo.type == type) { // Direct copy possible for (int y = 0; y < rect.bottom - rect.top; y++) { memcpy(pixels + y * outputPitch, source + y * inputPitch, (rect.right - rect.left) * sourceFormatInfo.pixelBytes); } } else { const d3d9::D3DFormat &sourceD3DFormatInfo = d3d9::GetD3DFormatInfo(desc.Format); ColorCopyFunction fastCopyFunc = sourceD3DFormatInfo.getFastCopyFunction(format, type); GLenum sizedDestInternalFormat = gl::GetSizedInternalFormat(format, type); const gl::InternalFormat &destFormatInfo = gl::GetInternalFormatInfo(sizedDestInternalFormat); if (fastCopyFunc) { // Fast copy is possible through some special function for (int y = 0; y < rect.bottom - rect.top; y++) { for (int x = 0; x < rect.right - rect.left; x++) { uint8_t *dest = pixels + y * outputPitch + x * destFormatInfo.pixelBytes; const uint8_t *src = source + y * inputPitch + x * sourceFormatInfo.pixelBytes; fastCopyFunc(src, dest); } } } else { ColorReadFunction colorReadFunction = sourceD3DFormatInfo.colorReadFunction; ColorWriteFunction colorWriteFunction = GetColorWriteFunction(format, type); uint8_t temp[sizeof(gl::ColorF)]; for (int y = 0; y < rect.bottom - rect.top; y++) { for (int x = 0; x < rect.right - rect.left; x++) { uint8_t *dest = pixels + y * outputPitch + x * destFormatInfo.pixelBytes; const uint8_t *src = source + y * inputPitch + x * sourceFormatInfo.pixelBytes; // readFunc and writeFunc will be using the same type of color, CopyTexImage // will not allow the copy otherwise. colorReadFunction(src, temp); colorWriteFunction(temp, dest); } } } } systemSurface->UnlockRect(); SafeRelease(systemSurface); return gl::Error(GL_NO_ERROR); }
//---------------------------------------------------------------------------- void PdrRenderTarget::ReadColor (int i, Renderer* renderer, Texture2D*& texture) { if (i < 0 || i >= mNumTargets) { assertion(false, "Invalid target index.\n"); return; } IDirect3DDevice9* device = renderer->mData->mDevice; HRESULT hr; WM5_UNUSED(hr); // Enable the input render target surface. if (i == 0) { hr = device->GetRenderTarget(0, &mSaveColorSurface); assertion(hr == D3D_OK, "Failed to get old rt 0 color surface: %s\n", DXGetErrorString(hr)); } hr = device->SetRenderTarget((DWORD)i, mColorSurfaces[i]); assertion(hr == D3D_OK, "Failed to set new rt %d color surface: %s\n", i, DXGetErrorString(hr)); // Make a duplicate in system memory. IDirect3DTexture9* copyTexture = 0; hr = D3DXCreateTexture ( device, (UINT)mWidth, (UINT)mHeight, 0, 0, gDX9TextureFormat[mFormat], D3DPOOL_SYSTEMMEM, ©Texture ); assertion(hr == D3D_OK, "Failed to create copy texture: %s\n", DXGetErrorString(hr)); // Get the surface associated with the copy. IDirect3DSurface9* copySurface = 0; hr = copyTexture->GetSurfaceLevel(0, ©Surface); assertion(hr == D3D_OK, "Failed to get surface level for copy texture: %s\n", DXGetErrorString(hr)); // Copy the render target surface. hr = device->GetRenderTargetData(mColorSurfaces[i], copySurface); assertion(hr == D3D_OK, "Failed to copy the rt %d surface: %s\n", i, DXGetErrorString(hr)); // Get the data to write to disk. D3DLOCKED_RECT rect; hr = copySurface->LockRect(&rect, 0, 0); assertion(hr == D3D_OK, "Failed to lock copy surface: %s\n", DXGetErrorString(hr)); if (texture) { if (texture->GetFormat() != mFormat || texture->GetWidth() != (int)mWidth || texture->GetHeight() != (int)mHeight) { assertion(false, "Incompatible texture.\n"); delete0(texture); texture = new0 Texture2D(mFormat, mWidth, mHeight, 1); } } else { texture = new0 Texture2D(mFormat, mWidth, mHeight, 1); } memcpy(texture->GetData(0), rect.pBits, texture->GetNumLevelBytes(0)); hr = copySurface->UnlockRect(); assertion(hr == D3D_OK, "Failed to unlock copy surface: %s\n", DXGetErrorString(hr)); copySurface->Release(); copyTexture->Release(); // Restore the previous render target surface. if (i == 0) { hr = device->SetRenderTarget(0, mSaveColorSurface); assertion(hr == D3D_OK, "Failed to set old rt 0 color surface: %s\n", DXGetErrorString(hr)); mSaveColorSurface->Release(); } }
bool TextureCube::GetData(CubeMapFace face, unsigned level, void* dest) const { if (!object_.ptr_) { URHO3D_LOGERROR("No texture created, can not get data"); return false; } if (!dest) { URHO3D_LOGERROR("Null destination for getting data"); return false; } if (level >= levels_) { URHO3D_LOGERROR("Illegal mip level for getting data"); return false; } if (graphics_->IsDeviceLost()) { URHO3D_LOGWARNING("Getting texture data while device is lost"); return false; } if (resolveDirty_) graphics_->ResolveToTexture(const_cast<TextureCube*>(this)); int levelWidth = GetLevelWidth(level); int levelHeight = GetLevelHeight(level); D3DLOCKED_RECT d3dLockedRect; RECT d3dRect; d3dRect.left = 0; d3dRect.top = 0; d3dRect.right = levelWidth; d3dRect.bottom = levelHeight; IDirect3DSurface9* offscreenSurface = nullptr; // Need to use a offscreen surface & GetRenderTargetData() for rendertargets if (renderSurfaces_[face]) { if (level != 0) { URHO3D_LOGERROR("Can only get mip level 0 data from a rendertarget"); return false; } // If multisampled, must copy the surface of the resolve texture instead of the multisampled surface IDirect3DSurface9* resolveSurface = nullptr; if (multiSample_ > 1) { HRESULT hr = ((IDirect3DCubeTexture9*)object_.ptr_)->GetCubeMapSurface((D3DCUBEMAP_FACES)face, 0, (IDirect3DSurface9**)&resolveSurface); if (FAILED(hr)) { URHO3D_LOGD3DERROR("Could not get surface of the resolve texture", hr); URHO3D_SAFE_RELEASE(resolveSurface); return false; } } IDirect3DDevice9* device = graphics_->GetImpl()->GetDevice(); HRESULT hr = device->CreateOffscreenPlainSurface((UINT)width_, (UINT)height_, (D3DFORMAT)format_, D3DPOOL_SYSTEMMEM, &offscreenSurface, nullptr); if (FAILED(hr)) { URHO3D_LOGD3DERROR("Could not create surface for getting rendertarget data", hr); URHO3D_SAFE_RELEASE(offscreenSurface); URHO3D_SAFE_RELEASE(resolveSurface); return false; } if (resolveSurface) hr = device->GetRenderTargetData(resolveSurface, offscreenSurface); else hr = device->GetRenderTargetData((IDirect3DSurface9*)renderSurfaces_[face]->GetSurface(), offscreenSurface); URHO3D_SAFE_RELEASE(resolveSurface); if (FAILED(hr)) { URHO3D_LOGD3DERROR("Could not get rendertarget data", hr); URHO3D_SAFE_RELEASE(offscreenSurface); return false; } if (FAILED(offscreenSurface->LockRect(&d3dLockedRect, &d3dRect, D3DLOCK_READONLY))) { URHO3D_LOGD3DERROR("Could not lock surface for getting rendertarget data", hr); URHO3D_SAFE_RELEASE(offscreenSurface); return false; } } else { HRESULT hr = ((IDirect3DCubeTexture9*)object_.ptr_)->LockRect((D3DCUBEMAP_FACES)face, level, &d3dLockedRect, &d3dRect, D3DLOCK_READONLY); if (FAILED(hr)) { URHO3D_LOGD3DERROR("Could not lock texture", hr); return false; } } int height = levelHeight; if (IsCompressed()) height = (height + 3) >> 2; unsigned char* destPtr = (unsigned char*)dest; unsigned rowSize = GetRowDataSize(levelWidth); // GetRowDataSize() returns CPU-side (destination) data size, so need to convert for X8R8G8B8 if (format_ == D3DFMT_X8R8G8B8) rowSize = rowSize / 3 * 4; // Perform conversion to RGB / RGBA as necessary switch (format_) { default: for (int i = 0; i < height; ++i) { unsigned char* src = (unsigned char*)d3dLockedRect.pBits + i * d3dLockedRect.Pitch; memcpy(destPtr, src, rowSize); destPtr += rowSize; } break; case D3DFMT_X8R8G8B8: for (int i = 0; i < height; ++i) { unsigned char* src = (unsigned char*)d3dLockedRect.pBits + i * d3dLockedRect.Pitch; for (int j = 0; j < levelWidth; ++j) { destPtr[2] = *src++; destPtr[1] = *src++; destPtr[0] = *src++; ++src; destPtr += 3; } } break; case D3DFMT_A8R8G8B8: for (int i = 0; i < height; ++i) { unsigned char* src = (unsigned char*)d3dLockedRect.pBits + i * d3dLockedRect.Pitch; for (int j = 0; j < levelWidth; ++j) { destPtr[2] = *src++; destPtr[1] = *src++; destPtr[0] = *src++; destPtr[3] = *src++; destPtr += 4; } } break; } if (offscreenSurface) offscreenSurface->UnlockRect(); else ((IDirect3DCubeTexture9*)object_.ptr_)->UnlockRect((D3DCUBEMAP_FACES)face, level); URHO3D_SAFE_RELEASE(offscreenSurface); return true; }
bool UpdateD3DAntiCheat_ScreenHelpers2( bool lastAttempt ) { if( !g_AntiCheat_PrePresentPixels.Count() ) return false; bool result = false; HDC windowDC = GetWindowDC( r3dRenderer->HLibWin ); WINDOWINFO winfo; memset( &winfo, 0, sizeof winfo ); winfo.cbSize = sizeof winfo; GetWindowInfo( r3dRenderer->HLibWin, &winfo ); // put window at 0 0 for convenience winfo.rcClient.left -= winfo.rcWindow.left; winfo.rcClient.right -= winfo.rcWindow.left; winfo.rcClient.top -= winfo.rcWindow.top; winfo.rcClient.bottom -= winfo.rcWindow.top; winfo.rcWindow.bottom -= winfo.rcWindow.top; winfo.rcWindow.top = 0; winfo.rcWindow.right -= winfo.rcWindow.left; winfo.rcWindow.left = 0; int nTotalWid = winfo.rcWindow.right - winfo.rcWindow.left, nTotalHeight = winfo.rcWindow.bottom - winfo.rcWindow.top; int nClientWid = winfo.rcClient.right - winfo.rcClient.left, nClientHt = winfo.rcClient.bottom - winfo.rcClient.top; if( r_fullscreen->GetInt() ) { nTotalWid = g_AntiCheat_PrePresentPixels_Width; nTotalHeight = g_AntiCheat_PrePresentPixels_Height; nClientWid = g_AntiCheat_PrePresentPixels_Width; nClientHt = g_AntiCheat_PrePresentPixels_Height; } /*if( g_AntiCheat_PrePresentPixels_Width != nClientWid || g_AntiCheat_PrePresentPixels_Height != nClientHt ) { return false; }*/ HDC hBitmapdc; HBITMAP hBitmap, hOriginal; if( windowDC || r_fullscreen->GetInt() ) { DWORD* pixel = NULL; if( !r_fullscreen->GetInt() ) { hBitmap = CreateCompatibleBitmap(windowDC,nTotalWid,nTotalHeight); hBitmapdc = CreateCompatibleDC(windowDC); hOriginal = (HBITMAP)SelectBitmap(hBitmapdc, hBitmap); BitBlt( hBitmapdc, 0, 0, nTotalWid, nTotalHeight, windowDC, 0, 0, SRCCOPY ); BITMAPINFO bmpInfo; memset( &bmpInfo, 0, sizeof bmpInfo ); bmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bmpInfo.bmiHeader.biWidth = nTotalWid; bmpInfo.bmiHeader.biHeight = nTotalHeight; bmpInfo.bmiHeader.biPlanes = 1; bmpInfo.bmiHeader.biBitCount = 32; bmpInfo.bmiHeader.biCompression = BI_RGB; bmpInfo.bmiHeader.biSizeImage = 0; GetDIBits( hBitmapdc, hBitmap, 0, nTotalHeight, NULL, &bmpInfo, DIB_RGB_COLORS ); r3d_assert( nTotalWid * nTotalHeight * sizeof( DWORD ) <= bmpInfo.bmiHeader.biSizeImage ); pixel = (DWORD*)malloc( bmpInfo.bmiHeader.biSizeImage ); GetDIBits( hBitmapdc, hBitmap, 0, nTotalHeight, pixel, &bmpInfo, DIB_RGB_COLORS ); //#if 0 IDirect3DSurface9 *sysmemBB = r3dRenderer->GetTempSurfaceForScreenShots(); D3DLOCKED_RECT lrect; D3D_V( sysmemBB->LockRect( &lrect, NULL, 0 ) ); D3DSURFACE_DESC smbbDesc; sysmemBB->GetDesc( &smbbDesc ); r3d_assert( lrect.Pitch * smbbDesc.Height == nClientWid * nClientHt * sizeof( DWORD ) ); for( int i = 0, e = nClientHt; i < e ; i ++ ) { memcpy( (char*)lrect.pBits + lrect.Pitch * i, pixel + ( nTotalHeight - 1 - ( i + winfo.rcClient.top ) ) * nTotalWid + winfo.rcClient.left, lrect.Pitch ); } D3D_V( sysmemBB->UnlockRect() ); D3DXSaveSurfaceToFile( "test1.bmp", D3DXIFF_BMP, sysmemBB, NULL, NULL ); //#endif DeleteDC( hBitmapdc ); DeleteObject( hBitmap ); } else { int pixelDataSize = g_AntiCheat_PrePresentPixels_Width * g_AntiCheat_PrePresentPixels_Height * sizeof( DWORD ); pixel = (DWORD*)malloc( pixelDataSize ); struct Locals { Locals() { BBuf = 0; } ~Locals() { if( BBuf ) BBuf->Release(); } IDirect3DSurface9 *BBuf; } locals; (void)locals; r3dRenderer->GetRT(0, &locals.BBuf); IDirect3DDevice9 *d = r3dRenderer->pd3ddev; IDirect3DSurface9 *sysmemBB = r3dRenderer->GetTempSurfaceForScreenShots(); if (!SUCCEEDED(d->GetRenderTargetData(locals.BBuf, sysmemBB))) { r3dOutToLog("SUCCEEDED(d->GetRenderTargetData(locals.BBuf, sysmemBB))\n"); InvalidateScreenHelperAnticheat(); return false; } D3DSURFACE_DESC sdesc; sysmemBB->GetDesc( &sdesc ); D3DLOCKED_RECT lrect; sysmemBB->LockRect( &lrect, NULL, D3DLOCK_READONLY ); r3d_assert( lrect.Pitch * sdesc.Height == pixelDataSize ); memcpy( pixel, lrect.pBits, lrect.Pitch * sdesc.Height ); sysmemBB->UnlockRect(); #if 0 D3DXSaveSurfaceToFile( "test1.jpg", D3DXIFF_JPG, sysmemBB, NULL, NULL ); #endif } int subAreaHeight = int( nClientHt * 0.8f ); int subAreaWidth = int( nClientWid * 0.8f ); int yStart = ( nClientHt - subAreaHeight ) / 2; int yEnd = nClientHt - yStart; int xStart = ( nClientWid - subAreaWidth ) / 2; int xEnd = nClientWid - yStart; xStart += winfo.rcClient.left; xEnd += winfo.rcClient.left; yStart += nTotalHeight - winfo.rcClient.bottom; yEnd += nTotalHeight - winfo.rcClient.bottom; int totalSubArea = ( xEnd - xStart ) * ( yEnd - yStart ); int unequalCount = 0; //------------------------------------------------------------------------ #if 0 FILE * fout = fopen( "test0.raw", "wb" ); for( int y = 0, e = nTotalHeight; y < e; y ++ ) { for( int x = 0, e = nTotalWid; x < e; x ++ ) { fputc( pixel[ x + y * nTotalWid ], fout ); } } fclose( fout ); FILE * fout1 = fopen( "test1.raw", "wb" ); for( int y = 0, e = nClientHt; y < e; y ++ ) { for( int x = 0, e = nClientWid; x < e; x ++ ) { fputc( g_AntiCheat_PrePresentPixels[ x + y * nClientWid ], fout1 ); } } fclose( fout1 ); #endif //------------------------------------------------------------------------ for( int y = yStart, e = yEnd; y < e; y ++ ) { for( int x = xStart, e = xEnd; x < e; x ++ ) { int localX = x - winfo.rcClient.left; int localY = r_fullscreen->GetInt() ? y - winfo.rcClient.top : ( nTotalHeight - y - 1 ) - winfo.rcClient.top; DWORD pixel0 = pixel[ x + y * nTotalWid ] & 0xffffff; DWORD pixel1 = g_AntiCheat_PrePresentPixels[ localX + localY * g_AntiCheat_PrePresentPixels_Width ] & 0xffffff; if( pixel0 != pixel1 ) { unequalCount ++; } } } //#if 0 r3dOutToLog( "unequalCount %d\n", unequalCount ); //#endif if( !unequalCount ) { result = true; } else if( lastAttempt ) { result = true; // caught some significant extra stuff on screen D3DCheaters[ ANTICHEAT_SCREEN_HELPERS2 ] = true; r3dOutToLog("ScreenHelper\n"); int texWidth = 386; int texHeight = 256; if( !g_TempSaveDataS ) { D3D_V( r3dRenderer->pd3ddev->CreateTexture( texWidth, texHeight, 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, &g_TempSaveDataS, NULL ) ); } if( g_TempSaveDataS ) { D3DLOCKED_RECT lrect; g_TempSaveDataS->LockRect( 0, &lrect, NULL, 0 ); r3d_assert( lrect.Pitch == texWidth * sizeof( DWORD ) ); memset( lrect.pBits, 0, lrect.Pitch * texHeight ); for( int y=0, yy=0; y < texHeight; y++, yy = y * nClientHt / texHeight ) { for( int x=0, xx=0; x < texWidth; x++, xx = x * nClientWid / texWidth ) { int src_idx = ( nTotalHeight - winfo.rcClient.top - yy - 1 ) * nTotalWid + xx + winfo.rcClient.left; if( src_idx < nTotalWid * nTotalHeight ) { if(r_fullscreen->GetInt()) // in fullscreen image in inverted, so write it upside down to correct for that *((DWORD*)lrect.pBits + (texHeight-y-1) * texWidth + x) = pixel[ src_idx ]; else *((DWORD*)lrect.pBits + y * texWidth + x) = pixel[ src_idx ]; } else { #ifndef FINAL_BUILD r3d_assert(false); #endif } } } g_TempSaveDataS->UnlockRect( 0 ); //#if 0 D3DXSaveTextureToFile( "test_final.jpg", D3DXIFF_JPG, g_TempSaveDataS, NULL ); //#endif } } free( pixel ); ReleaseDC( r3dRenderer->HLibWin, windowDC ); } return result; }
//------------------------------------------------------------------------ void UpdateD3DAntiCheatPrePresent() { if( !r3dRenderer->DeviceAvailable ) { InvalidateScreenHelperAnticheat(); return; } if( EnableD3DAntiCheatCodepath[ ANTICHEAT_SCREEN_HELPERS2 ] ) { if( !g_AntiCheat_PrePresentPixels.Count() ) { IDirect3DDevice9 *d = r3dRenderer->pd3ddev; struct Locals { Locals() { BBuf = 0; } ~Locals() { if( BBuf ) BBuf->Release(); } IDirect3DSurface9 *BBuf; } locals; (void)locals; r3dRenderer->GetRT(0, &locals.BBuf); IDirect3DSurface9 *sysmemBB = r3dRenderer->GetTempSurfaceForScreenShots(); if (!SUCCEEDED(d->GetRenderTargetData(locals.BBuf, sysmemBB))) { InvalidateScreenHelperAnticheat(); return; } //#if 0 D3DXSaveSurfaceToFile( "test0.bmp", D3DXIFF_BMP, sysmemBB, NULL, NULL ); //#endif D3DSURFACE_DESC sdesc; sysmemBB->GetDesc( &sdesc ); g_AntiCheat_PrePresentPixels_Width = sdesc.Width; g_AntiCheat_PrePresentPixels_Height = sdesc.Height; g_AntiCheat_PrePresentPixels.Resize( g_AntiCheat_PrePresentPixels_Width * g_AntiCheat_PrePresentPixels_Height ); D3DLOCKED_RECT lrect; sysmemBB->LockRect( &lrect, NULL, D3DLOCK_READONLY ); r3d_assert( lrect.Pitch = sdesc.Width * sizeof( DWORD ) ); memcpy( &g_AntiCheat_PrePresentPixels[ 0 ], lrect.pBits, lrect.Pitch * sdesc.Height ); sysmemBB->UnlockRect(); } } }
HRESULT WINAPI D3D9SCPresent(IDirect3DSwapChain9 *pSc, CONST RECT* pSourceRect,CONST RECT* pDestRect,HWND hDestWindowOverride,CONST RGNDATA* pDirtyRegion, DWORD dwFlags) { D3D9FrameGrabber *d3d9FrameGrabber = D3D9FrameGrabber::getInstance(); Logger *logger = d3d9FrameGrabber->m_logger; DWORD errorcode; if (WAIT_OBJECT_0 == (errorcode = WaitForSingleObject(d3d9FrameGrabber->m_syncRunMutex, 0))) { IPCContext *ipcContext = d3d9FrameGrabber->m_ipcContext; logger->reportLogDebug(L"D3D9SCPresent"); IDirect3DSurface9 *pBackBuffer = NULL; D3DPRESENT_PARAMETERS params; RECT newRect = RECT(); IDirect3DSurface9 *pDemultisampledSurf = NULL; IDirect3DSurface9 *pOffscreenSurf = NULL; IDirect3DDevice9 *pDev = NULL; HRESULT hRes = pSc->GetPresentParameters(¶ms); if (FAILED(hRes) || params.Windowed) { goto end; } if (FAILED(hRes = pSc->GetBackBuffer( 0, D3DBACKBUFFER_TYPE_MONO, &pBackBuffer))) { logger->reportLogError(L"d3d9sc couldn't get backbuffer. errorcode = 0x%x", hRes); goto end; } D3DSURFACE_DESC surfDesc; pBackBuffer->GetDesc(&surfDesc); hRes = pSc->GetDevice(&pDev); if (FAILED(hRes)) { logger->reportLogError(L"GetFramePrep: FAILED to get pDev. 0x%x, width=%u, height=%u, format=%x", hRes, surfDesc.Width, surfDesc.Height, surfDesc.Format ); goto end; } hRes = pDev->CreateRenderTarget( surfDesc.Width, surfDesc.Height, surfDesc.Format, D3DMULTISAMPLE_NONE, 0, false, &pDemultisampledSurf, NULL ); if (FAILED(hRes)) { logger->reportLogError(L"GetFramePrep: FAILED to create demultisampled render target. 0x%x, width=%u, height=%u, format=%x", hRes, surfDesc.Width, surfDesc.Height, surfDesc.Format ); goto end; } hRes = pDev->StretchRect(pBackBuffer, NULL, pDemultisampledSurf, NULL, D3DTEXF_LINEAR ); if (FAILED(hRes)) { logger->reportLogError(L"GetFramePrep: StretchRect FAILED for image surfacee. 0x%x, width=%u, height=%u, format=%x", hRes, surfDesc.Width, surfDesc.Height, surfDesc.Format ); goto end; } hRes = pDev->CreateOffscreenPlainSurface( surfDesc.Width, surfDesc.Height, surfDesc.Format, D3DPOOL_SYSTEMMEM, &pOffscreenSurf, NULL ); if (FAILED(hRes)) { logger->reportLogError(L"GetFramePrep: FAILED to create image surface. 0x%x, width=%u, height=%u, format=%x", hRes, surfDesc.Width, surfDesc.Height, surfDesc.Format ); goto end; } hRes = pDev->GetRenderTargetData(pDemultisampledSurf, pOffscreenSurf ); if (FAILED(hRes)) { logger->reportLogError(L"GetFramePrep: GetRenderTargetData() FAILED for image surfacee. 0x%x, width=%u, height=%u, format=%x", hRes, surfDesc.Width, surfDesc.Height, surfDesc.Format ); goto end; } D3DLOCKED_RECT lockedSrcRect; newRect.right = surfDesc.Width; newRect.bottom = surfDesc.Height; hRes = pOffscreenSurf->LockRect( &lockedSrcRect, &newRect, 0); if (FAILED(hRes)) { logger->reportLogError(L"GetFramePrep: FAILED to lock source rect. (0x%x)", hRes ); goto end; } ipcContext->m_memDesc.width = surfDesc.Width; ipcContext->m_memDesc.height = surfDesc.Height; ipcContext->m_memDesc.rowPitch = lockedSrcRect.Pitch; ipcContext->m_memDesc.frameId++; ipcContext->m_memDesc.format = getCompatibleBufferFormat(surfDesc.Format); if (WAIT_OBJECT_0 == (errorcode = WaitForSingleObject(ipcContext->m_hMutex, 0))) { // __asm__("int $3"); // reportLog(EVENTLOG_INFORMATION_TYPE, L"d3d9sc writing description to mem mapped file"); memcpy(ipcContext->m_pMemMap, &ipcContext->m_memDesc, sizeof (ipcContext->m_memDesc)); // reportLog(EVENTLOG_INFORMATION_TYPE, L"d3d9sc writing data to mem mapped file"); PVOID pMemDataMap = incPtr(ipcContext->m_pMemMap, sizeof (ipcContext->m_memDesc)); if (static_cast<UINT>(lockedSrcRect.Pitch) == surfDesc.Width * 4) { memcpy(pMemDataMap, lockedSrcRect.pBits, surfDesc.Width * surfDesc.Height * 4); } else { UINT i = 0, cleanOffset = 0, pitchOffset = 0; while (i < surfDesc.Height) { memcpy(incPtr(pMemDataMap, cleanOffset), incPtr(lockedSrcRect.pBits, pitchOffset), surfDesc.Width * 4); cleanOffset += surfDesc.Width * 4; pitchOffset += lockedSrcRect.Pitch; i++; } } ReleaseMutex(ipcContext->m_hMutex); SetEvent(ipcContext->m_hFrameGrabbedEvent); } else { logger->reportLogError(L"d3d9sc couldn't wait mutex. errocode = 0x%x", errorcode); } end: if(pOffscreenSurf) pOffscreenSurf->Release(); if(pDemultisampledSurf) pDemultisampledSurf->Release(); if(pBackBuffer) pBackBuffer->Release(); if(pDev) pDev->Release(); ProxyFuncJmp *d3d9SCPresentProxyFuncJmp = d3d9FrameGrabber->m_d3d9SCPresentProxyFuncJmp; if(d3d9SCPresentProxyFuncJmp->removeHook()) { int i = GetLastError(); logger->reportLogError(L"d3d9sc error occured while trying to removeHook before original call0x%x", i); } HRESULT result = pSc->Present(pSourceRect, pDestRect, hDestWindowOverride, pDirtyRegion, dwFlags); if(d3d9SCPresentProxyFuncJmp->installHook()) { int i = GetLastError(); logger->reportLogError(L"d3d9sc error occured while trying to installHook after original call0x%x", i); } ReleaseMutex(d3d9FrameGrabber->m_syncRunMutex); return result; } else { logger->reportLogError(L"d3d9sc present is skipped because mutex is busy"); return S_FALSE; } }