#include #include #include "dllmain.h" #include "dd.h" #include "hook.h" #include "ddsurface.h" #include "mouse.h" #include "scale_pattern.h" #include "IDirectDrawSurface.h" #include "winapi_hooks.h" #include "debug.h" #include "utils.h" HRESULT dds_AddAttachedSurface(IDirectDrawSurfaceImpl *This, LPDIRECTDRAWSURFACE lpDDSurface) { if (lpDDSurface) { IDirectDrawSurface_AddRef(lpDDSurface); if (g_ddraw->backbuffer && !This->backbuffer) { IDirectDrawSurfaceImpl* surface = (IDirectDrawSurfaceImpl*)lpDDSurface; surface->caps |= DDSCAPS_BACKBUFFER; This->backbuffer = surface; } } return DD_OK; } HRESULT dds_Blt(IDirectDrawSurfaceImpl *This, LPRECT lpDestRect, LPDIRECTDRAWSURFACE lpDDSrcSurface, LPRECT lpSrcRect, DWORD dwFlags, LPDDBLTFX lpDDBltFx) { IDirectDrawSurfaceImpl *src_surface = (IDirectDrawSurfaceImpl *)lpDDSrcSurface; dbg_dump_dds_blt_flags(dwFlags); if (g_ddraw->iskkndx && (dwFlags & DDBLT_COLORFILL) && lpDestRect && lpDestRect->right == 640 && lpDestRect->bottom == 480) lpDestRect = NULL; RECT src_rect = { 0, 0, src_surface ? src_surface->width : 0, src_surface ? src_surface->height : 0 }; RECT dst_rect = { 0, 0, This->width, This->height }; if (lpSrcRect && src_surface) memcpy(&src_rect, lpSrcRect, sizeof(src_rect)); if (lpDestRect) memcpy(&dst_rect, lpDestRect, sizeof(dst_rect)); // stretch or clip? BOOL is_stretch_blt = ((src_rect.right - src_rect.left) != (dst_rect.right - dst_rect.left)) || ((src_rect.bottom - src_rect.top) != (dst_rect.bottom - dst_rect.top)); if (src_surface) { if (src_rect.left < 0) src_rect.left = 0; if (src_rect.top < 0) src_rect.top = 0; if (src_rect.right > src_surface->width) src_rect.right = src_surface->width; if (src_rect.left > src_rect.right) src_rect.left = src_rect.right; if (src_rect.bottom > src_surface->height) src_rect.bottom = src_surface->height; if (src_rect.top > src_rect.bottom) src_rect.top = src_rect.bottom; } if (dst_rect.left < 0) dst_rect.left = 0; if (dst_rect.top < 0) dst_rect.top = 0; if (dst_rect.right > This->width) dst_rect.right = This->width; if (dst_rect.left > dst_rect.right) dst_rect.left = dst_rect.right; if (dst_rect.bottom > This->height) dst_rect.bottom = This->height; if (dst_rect.top > dst_rect.bottom) dst_rect.top = dst_rect.bottom; int src_w = src_rect.right - src_rect.left; int src_h = src_rect.bottom - src_rect.top; int src_x = src_rect.left; int src_y = src_rect.top; int dst_w = dst_rect.right - dst_rect.left; int dst_h = dst_rect.bottom - dst_rect.top; int dst_x = dst_rect.left; int dst_y = dst_rect.top; void* dst_buf = dds_GetBuffer(This); void* src_buf = dds_GetBuffer(src_surface); if (dst_buf && (dwFlags & DDBLT_COLORFILL) && dst_w > 0 && dst_h > 0) { unsigned char *dst = (unsigned char *)dst_buf + (dst_x * This->lx_pitch) + (This->l_pitch * dst_y); unsigned char *first_row = dst; unsigned int dst_pitch = dst_w * This->lx_pitch; int x, i; if (This->bpp == 8) { unsigned char color = (unsigned char)lpDDBltFx->dwFillColor; for (i = 0; i < dst_h; i++) { memset(dst, color, dst_pitch); dst += This->l_pitch; } } else if (This->bpp == 16) { unsigned short *row1 = (unsigned short *)dst; unsigned short color = (unsigned short)lpDDBltFx->dwFillColor; if ((color & 0xFF) == (color >> 8)) { unsigned char c8 = (unsigned char)(color & 0xFF); for (i = 0; i < dst_h; i++) { memset(dst, c8, dst_pitch); dst += This->l_pitch; } } else { for (x = 0; x < dst_w; x++) row1[x] = color; for (i = 1; i < dst_h; i++) { dst += This->l_pitch; memcpy(dst, first_row, dst_pitch); } } } else if (This->bpp == 32) { unsigned int* row1 = (unsigned int*)dst; unsigned int color = lpDDBltFx->dwFillColor; if ((color & 0xFF) == ((color >> 8) & 0xFF) && (color & 0xFF) == ((color >> 16) & 0xFF) && (color & 0xFF) == ((color >> 24) & 0xFF)) { unsigned char c8 = (unsigned char)(color & 0xFF); for (i = 0; i < dst_h; i++) { memset(dst, c8, dst_pitch); dst += This->l_pitch; } } else { for (x = 0; x < dst_w; x++) row1[x] = color; for (i = 1; i < dst_h; i++) { dst += This->l_pitch; memcpy(dst, first_row, dst_pitch); } } } } if (src_surface && src_w > 0 && src_h > 0 && dst_w > 0 && dst_h > 0) { if ((dwFlags & DDBLT_KEYSRC) || (dwFlags & DDBLT_KEYSRCOVERRIDE)) { DDCOLORKEY color_key; color_key.dwColorSpaceLowValue = (dwFlags & DDBLT_KEYSRCOVERRIDE) ? lpDDBltFx->ddckSrcColorkey.dwColorSpaceLowValue : src_surface->color_key.dwColorSpaceLowValue; color_key.dwColorSpaceHighValue = (dwFlags & DDBLT_KEYSRCOVERRIDE) ? lpDDBltFx->ddckSrcColorkey.dwColorSpaceHighValue : src_surface->color_key.dwColorSpaceHighValue; BOOL got_fx = (dwFlags & DDBLT_DDFX) && lpDDBltFx; BOOL mirror_left_right = got_fx && (lpDDBltFx->dwDDFX & DDBLTFX_MIRRORLEFTRIGHT); BOOL mirror_up_down = got_fx && (lpDDBltFx->dwDDFX & DDBLTFX_MIRRORUPDOWN); float scale_w = (float)src_w / dst_w; float scale_h = (float)src_h / dst_h; if (This->bpp == 8) { for (int y = 0; y < dst_h; y++) { int scaled_y = (int)(y * scale_h); if (mirror_up_down) scaled_y = src_h - 1 - scaled_y; int src_row = src_surface->width * (scaled_y + src_y); int dst_row = This->width * (y + dst_y); for (int x = 0; x < dst_w; x++) { int scaled_x = (int)(x * scale_w); if (mirror_left_right) scaled_x = src_w - 1 - scaled_x; unsigned char c = ((unsigned char*)src_buf)[scaled_x + src_x + src_row]; if (c < color_key.dwColorSpaceLowValue || c > color_key.dwColorSpaceHighValue) { ((unsigned char*)dst_buf)[x + dst_x + dst_row] = c; } } } } else if (This->bpp == 16) { for (int y = 0; y < dst_h; y++) { int scaled_y = (int)(y * scale_h); if (mirror_up_down) scaled_y = src_h - 1 - scaled_y; int src_row = src_surface->width * (scaled_y + src_y); int dst_row = This->width * (y + dst_y); for (int x = 0; x < dst_w; x++) { int scaled_x = (int)(x * scale_w); if (mirror_left_right) scaled_x = src_w - 1 - scaled_x; unsigned short c = ((unsigned short*)src_buf)[scaled_x + src_x + src_row]; if (c < color_key.dwColorSpaceLowValue || c > color_key.dwColorSpaceHighValue) { ((unsigned short*)dst_buf)[x + dst_x + dst_row] = c; } } } } else if (This->bpp == 32) { for (int y = 0; y < dst_h; y++) { int scaled_y = (int)(y * scale_h); if (mirror_up_down) scaled_y = src_h - 1 - scaled_y; int src_row = src_surface->width * (scaled_y + src_y); int dst_row = This->width * (y + dst_y); for (int x = 0; x < dst_w; x++) { int scaled_x = (int)(x * scale_w); if (mirror_left_right) scaled_x = src_w - 1 - scaled_x; unsigned int c = ((unsigned int*)src_buf)[scaled_x + src_x + src_row]; if (c < color_key.dwColorSpaceLowValue || c > color_key.dwColorSpaceHighValue) { ((unsigned int*)dst_buf)[x + dst_x + dst_row] = c; } } } } } else { if (!is_stretch_blt) { int width = dst_w > src_w ? src_w : dst_w; int height = dst_h > src_h ? src_h : dst_h; unsigned char *src = (unsigned char *)src_buf + (src_x * src_surface->lx_pitch) + (src_surface->l_pitch * src_y); unsigned char *dst = (unsigned char *)dst_buf + (dst_x * This->lx_pitch) + (This->l_pitch * dst_y); unsigned int dst_pitch = width * This->lx_pitch; if (This == src_surface) { if (dst_y > src_y) { src += src_surface->l_pitch * height; dst += This->l_pitch * height; for (int i = height; i-- > 0;) { src -= src_surface->l_pitch; dst -= This->l_pitch; memmove(dst, src, dst_pitch); } } else { for (int i = 0; i < height; i++) { memmove(dst, src, dst_pitch); src += src_surface->l_pitch; dst += This->l_pitch; } } } else { for (int i = 0; i < height; i++) { memcpy(dst, src, dst_pitch); src += src_surface->l_pitch; dst += This->l_pitch; } } } else { /* Linear scaling using integer math * Since the scaling pattern for x will aways be the same, the pattern itself gets pre-calculated * and stored in an array. * Y scaling pattern gets calculated during the blit loop */ unsigned int x_ratio = (unsigned int)((src_w << 16) / dst_w) + 1; unsigned int y_ratio = (unsigned int)((src_h << 16) / dst_h) + 1; unsigned int s_src_x, s_src_y; unsigned int dest_base, source_base; scale_pattern *pattern = malloc((dst_w + 1) * (sizeof(scale_pattern))); int pattern_idx = 0; unsigned int last_src_x = 0; if (pattern != NULL) { pattern[pattern_idx] = (scale_pattern) { ONCE, 0, 0, 1 }; /* Build the pattern! */ int x; for (x = 1; x < dst_w; x++) { s_src_x = (x * x_ratio) >> 16; if (s_src_x == last_src_x) { if (pattern[pattern_idx].type == REPEAT || pattern[pattern_idx].type == ONCE) { pattern[pattern_idx].type = REPEAT; pattern[pattern_idx].count++; } else if (pattern[pattern_idx].type == SEQUENCE) { pattern_idx++; pattern[pattern_idx] = (scale_pattern) { REPEAT, x, s_src_x, 1 }; } } else if (s_src_x == last_src_x + 1) { if (pattern[pattern_idx].type == SEQUENCE || pattern[pattern_idx].type == ONCE) { pattern[pattern_idx].type = SEQUENCE; pattern[pattern_idx].count++; } else if (pattern[pattern_idx].type == REPEAT) { pattern_idx++; pattern[pattern_idx] = (scale_pattern) { ONCE, x, s_src_x, 1 }; } } else { pattern_idx++; pattern[pattern_idx] = (scale_pattern) { ONCE, x, s_src_x, 1 }; } last_src_x = s_src_x; } pattern[pattern_idx + 1] = (scale_pattern) { END, 0, 0, 0 }; /* Do the actual blitting */ int count = 0; int y; for (y = 0; y < dst_h; y++) { dest_base = dst_x + This->width * (y + dst_y); s_src_y = (y * y_ratio) >> 16; source_base = src_x + src_surface->width * (s_src_y + src_y); pattern_idx = 0; scale_pattern *current = &pattern[pattern_idx]; if (This->bpp == 8) { unsigned char *d, *s, v; unsigned char *src = (unsigned char *)src_buf; unsigned char *dst = (unsigned char *)dst_buf; do { switch (current->type) { case ONCE: dst[dest_base + current->dst_index] = src[source_base + current->src_index]; break; case REPEAT: d = (dst + dest_base + current->dst_index); v = src[source_base + current->src_index]; count = current->count; while (count-- > 0) *d++ = v; break; case SEQUENCE: d = dst + dest_base + current->dst_index; s = src + source_base + current->src_index; memcpy((void *)d, (void *)s, current->count * This->lx_pitch); break; case END: default: break; } current = &pattern[++pattern_idx]; } while (current->type != END); } else if (This->bpp == 16) { unsigned short *d, *s, v; unsigned short *src = (unsigned short *)src_buf; unsigned short *dst = (unsigned short *)dst_buf; do { switch (current->type) { case ONCE: dst[dest_base + current->dst_index] = src[source_base + current->src_index]; break; case REPEAT: d = (dst + dest_base + current->dst_index); v = src[source_base + current->src_index]; count = current->count; while (count-- > 0) *d++ = v; break; case SEQUENCE: d = dst + dest_base + current->dst_index; s = src + source_base + current->src_index; memcpy((void *)d, (void *)s, current->count * This->lx_pitch); break; case END: default: break; } current = &pattern[++pattern_idx]; } while (current->type != END); } else if (This->bpp == 32) { unsigned int* d, * s, v; unsigned int* src = (unsigned int*)src_buf; unsigned int* dst = (unsigned int*)dst_buf; do { switch (current->type) { case ONCE: dst[dest_base + current->dst_index] = src[source_base + current->src_index]; break; case REPEAT: d = (dst + dest_base + current->dst_index); v = src[source_base + current->src_index]; count = current->count; while (count-- > 0) *d++ = v; break; case SEQUENCE: d = dst + dest_base + current->dst_index; s = src + source_base + current->src_index; memcpy((void*)d, (void*)s, current->count * This->lx_pitch); break; case END: default: break; } current = &pattern[++pattern_idx]; } while (current->type != END); } } free(pattern); } } } } if((This->caps & DDSCAPS_PRIMARYSURFACE) && g_ddraw->render.run) { InterlockedExchange(&g_ddraw->render.surface_updated, TRUE); if (!(This->flags & DDSD_BACKBUFFERCOUNT) || This->last_flip_tick + FLIP_REDRAW_TIMEOUT < timeGetTime()) { This->last_blt_tick = timeGetTime(); ReleaseSemaphore(g_ddraw->render.sem, 1, NULL); SwitchToThread(); if (g_ddraw->ticks_limiter.tick_length > 0) { g_ddraw->ticks_limiter.use_blt_or_flip = TRUE; util_limit_game_ticks(); } } } return DD_OK; } HRESULT dds_BltFast(IDirectDrawSurfaceImpl *This, DWORD dst_x, DWORD dst_y, LPDIRECTDRAWSURFACE lpDDSrcSurface, LPRECT lpSrcRect, DWORD flags) { IDirectDrawSurfaceImpl *src_surface = (IDirectDrawSurfaceImpl *)lpDDSrcSurface; dbg_dump_dds_blt_fast_flags(flags); RECT src_rect = { 0, 0, src_surface ? src_surface->width : 0, src_surface ? src_surface->height : 0 }; if (lpSrcRect && src_surface) { memcpy(&src_rect, lpSrcRect, sizeof(src_rect)); if (src_rect.left < 0) src_rect.left = 0; if (src_rect.top < 0) src_rect.top = 0; if (src_rect.right > src_surface->width) src_rect.right = src_surface->width; if (src_rect.left > src_rect.right) src_rect.left = src_rect.right; if (src_rect.bottom > src_surface->height) src_rect.bottom = src_surface->height; if (src_rect.top > src_rect.bottom) src_rect.top = src_rect.bottom; } int src_x = src_rect.left; int src_y = src_rect.top; RECT dst_rect = { dst_x, dst_y, (src_rect.right - src_rect.left) + dst_x, (src_rect.bottom - src_rect.top) + dst_y }; if (dst_rect.left < 0) dst_rect.left = 0; if (dst_rect.top < 0) dst_rect.top = 0; if (dst_rect.right > This->width) dst_rect.right = This->width; if (dst_rect.left > dst_rect.right) dst_rect.left = dst_rect.right; if (dst_rect.bottom > This->height) dst_rect.bottom = This->height; if (dst_rect.top > dst_rect.bottom) dst_rect.top = dst_rect.bottom; dst_x = dst_rect.left; dst_y = dst_rect.top; int dst_w = dst_rect.right - dst_rect.left; int dst_h = dst_rect.bottom - dst_rect.top; void* dst_buf = dds_GetBuffer(This); void* src_buf = dds_GetBuffer(src_surface); if (src_surface && dst_w > 0 && dst_h > 0) { if (flags & DDBLTFAST_SRCCOLORKEY) { if (This->bpp == 8) { for (int y = 0; y < dst_h; y++) { int dst_row = This->width * (y + dst_y); int src_row = src_surface->width * (y + src_y); for (int x = 0; x < dst_w; x++) { unsigned char c = ((unsigned char *)src_buf)[x + src_x + src_row]; if (c < src_surface->color_key.dwColorSpaceLowValue || c > src_surface->color_key.dwColorSpaceHighValue) { ((unsigned char *)dst_buf)[x + dst_x + dst_row] = c; } } } } else if (This->bpp == 16) { for (int y = 0; y < dst_h; y++) { int dst_row = This->width * (y + dst_y); int src_row = src_surface->width * (y + src_y); for (int x = 0; x < dst_w; x++) { unsigned short c = ((unsigned short *)src_buf)[x + src_x + src_row]; if (c < src_surface->color_key.dwColorSpaceLowValue || c > src_surface->color_key.dwColorSpaceHighValue) { ((unsigned short *)dst_buf)[x + dst_x + dst_row] = c; } } } } else if (This->bpp == 32) { for (int y = 0; y < dst_h; y++) { int dst_row = This->width * (y + dst_y); int src_row = src_surface->width * (y + src_y); for (int x = 0; x < dst_w; x++) { unsigned int c = ((unsigned int*)src_buf)[x + src_x + src_row]; if (c < src_surface->color_key.dwColorSpaceLowValue || c > src_surface->color_key.dwColorSpaceHighValue) { ((unsigned int*)dst_buf)[x + dst_x + dst_row] = c; } } } } } else { unsigned char *src = (unsigned char *)src_buf + (src_x * src_surface->lx_pitch) + (src_surface->l_pitch * src_y); unsigned char *dst = (unsigned char *)dst_buf + (dst_x * This->lx_pitch) + (This->l_pitch * dst_y); unsigned int dst_pitch = dst_w * This->lx_pitch; if (This == src_surface) { if (dst_y > src_y) { src += src_surface->l_pitch * dst_h; dst += This->l_pitch * dst_h; for (int i = dst_h; i-- > 0;) { src -= src_surface->l_pitch; dst -= This->l_pitch; memmove(dst, src, dst_pitch); } } else { for (int i = 0; i < dst_h; i++) { memmove(dst, src, dst_pitch); src += src_surface->l_pitch; dst += This->l_pitch; } } } else { for (int i = 0; i < dst_h; i++) { memcpy(dst, src, dst_pitch); src += src_surface->l_pitch; dst += This->l_pitch; } } } } if ((This->caps & DDSCAPS_PRIMARYSURFACE) && g_ddraw->render.run) { InterlockedExchange(&g_ddraw->render.surface_updated, TRUE); DWORD time = timeGetTime(); if (!(This->flags & DDSD_BACKBUFFERCOUNT) || (This->last_flip_tick + FLIP_REDRAW_TIMEOUT < time && This->last_blt_tick + FLIP_REDRAW_TIMEOUT < time)) { ReleaseSemaphore(g_ddraw->render.sem, 1, NULL); } } return DD_OK; } HRESULT dds_DeleteAttachedSurface(IDirectDrawSurfaceImpl *This, DWORD dwFlags, LPDIRECTDRAWSURFACE lpDDSurface) { if (lpDDSurface) { IDirectDrawSurface_Release(lpDDSurface); This->backbuffer = NULL; } return DD_OK; } HRESULT dds_GetSurfaceDesc(IDirectDrawSurfaceImpl *This, LPDDSURFACEDESC lpDDSurfaceDesc) { if (lpDDSurfaceDesc) { memset(lpDDSurfaceDesc, 0, sizeof(DDSURFACEDESC)); lpDDSurfaceDesc->dwSize = sizeof(DDSURFACEDESC); lpDDSurfaceDesc->dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_PITCH | DDSD_PIXELFORMAT | DDSD_LPSURFACE; lpDDSurfaceDesc->dwWidth = This->width; lpDDSurfaceDesc->dwHeight = This->height; lpDDSurfaceDesc->lPitch = This->l_pitch; lpDDSurfaceDesc->lpSurface = dds_GetBuffer(This); lpDDSurfaceDesc->ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT); lpDDSurfaceDesc->ddpfPixelFormat.dwFlags = DDPF_RGB; lpDDSurfaceDesc->ddpfPixelFormat.dwRGBBitCount = This->bpp; lpDDSurfaceDesc->ddsCaps.dwCaps = This->caps; if ((This->caps & DDSCAPS_PRIMARYSURFACE) || (This->caps & DDSCAPS_BACKBUFFER)) { lpDDSurfaceDesc->ddsCaps.dwCaps |= DDSCAPS_VIDEOMEMORY; } if (This->bpp == 8) { lpDDSurfaceDesc->ddpfPixelFormat.dwFlags |= DDPF_PALETTEINDEXED8; } else if (This->bpp == 16) { lpDDSurfaceDesc->ddpfPixelFormat.dwRBitMask = 0xF800; lpDDSurfaceDesc->ddpfPixelFormat.dwGBitMask = 0x07E0; lpDDSurfaceDesc->ddpfPixelFormat.dwBBitMask = 0x001F; } else if (This->bpp == 32) { lpDDSurfaceDesc->ddpfPixelFormat.dwRBitMask = 0xFF0000; lpDDSurfaceDesc->ddpfPixelFormat.dwGBitMask = 0x00FF00; lpDDSurfaceDesc->ddpfPixelFormat.dwBBitMask = 0x0000FF; } } return DD_OK; } HRESULT dds_EnumAttachedSurfaces(IDirectDrawSurfaceImpl *This, LPVOID lpContext, LPDDENUMSURFACESCALLBACK lpEnumSurfacesCallback) { static DDSURFACEDESC dd_surface_desc; memset(&dd_surface_desc, 0, sizeof(DDSURFACEDESC)); if (This->backbuffer) { dds_GetSurfaceDesc(This->backbuffer, &dd_surface_desc); IDirectDrawSurface_AddRef(This->backbuffer); lpEnumSurfacesCallback((LPDIRECTDRAWSURFACE)This->backbuffer, &dd_surface_desc, lpContext); } else if (!g_ddraw->backbuffer) { dds_GetSurfaceDesc(This, &dd_surface_desc); This->caps |= DDSCAPS_BACKBUFFER; // Nox hack lpEnumSurfacesCallback((LPDIRECTDRAWSURFACE)This, &dd_surface_desc, lpContext); if ((This->caps & DDSCAPS_PRIMARYSURFACE) && (This->caps & DDSCAPS_FLIP) && !(This->caps & DDSCAPS_BACKBUFFER)) IDirectDrawSurface_AddRef(This); This->caps &= ~DDSCAPS_BACKBUFFER; } return DD_OK; } HRESULT dds_Flip(IDirectDrawSurfaceImpl *This, LPDIRECTDRAWSURFACE surface, DWORD flags) { if (This->backbuffer) { EnterCriticalSection(&g_ddraw->cs); IDirectDrawSurfaceImpl* backbuffer = surface ? (IDirectDrawSurfaceImpl*)surface : This->backbuffer; void* buf = InterlockedExchangePointer(&This->surface, backbuffer->surface); HBITMAP bitmap = (HBITMAP)InterlockedExchangePointer(&This->bitmap, backbuffer->bitmap); HDC dc = (HDC)InterlockedExchangePointer(&This->hdc, backbuffer->hdc); InterlockedExchangePointer(&backbuffer->surface, buf); InterlockedExchangePointer(&backbuffer->bitmap, bitmap); InterlockedExchangePointer(&backbuffer->hdc, dc); LeaveCriticalSection(&g_ddraw->cs); if (!surface && This->backbuffer->backbuffer) { dds_Flip(This->backbuffer, NULL, 0); } } if (This->caps & DDSCAPS_PRIMARYSURFACE && g_ddraw->render.run) { This->last_flip_tick = timeGetTime(); InterlockedExchange(&g_ddraw->render.surface_updated, TRUE); ReleaseSemaphore(g_ddraw->render.sem, 1, NULL); SwitchToThread(); if ((flags & DDFLIP_WAIT) || g_ddraw->maxgameticks == -2) { dd_WaitForVerticalBlank(DDWAITVB_BLOCKEND, NULL); } if (g_ddraw->ticks_limiter.tick_length > 0) { g_ddraw->ticks_limiter.use_blt_or_flip = TRUE; util_limit_game_ticks(); } } return DD_OK; } HRESULT dds_GetAttachedSurface(IDirectDrawSurfaceImpl *This, LPDDSCAPS lpDdsCaps, LPDIRECTDRAWSURFACE FAR *surface) { if ((This->caps & DDSCAPS_PRIMARYSURFACE) && (This->caps & DDSCAPS_FLIP) && (lpDdsCaps->dwCaps & DDSCAPS_BACKBUFFER)) { if (This->backbuffer) { IDirectDrawSurface_AddRef(This->backbuffer); *surface = (LPDIRECTDRAWSURFACE)This->backbuffer; } else { IDirectDrawSurface_AddRef(This); *surface = (LPDIRECTDRAWSURFACE)This; } } return DD_OK; } HRESULT dds_GetCaps(IDirectDrawSurfaceImpl *This, LPDDSCAPS lpDDSCaps) { lpDDSCaps->dwCaps = This->caps; return DD_OK; } HRESULT dds_GetClipper(IDirectDrawSurfaceImpl* This, LPDIRECTDRAWCLIPPER FAR* lpClipper) { if (!lpClipper) return DDERR_INVALIDPARAMS; *lpClipper = (LPDIRECTDRAWCLIPPER)This->clipper; if (This->clipper) { IDirectDrawClipper_AddRef(This->clipper); return DD_OK; } else { return DDERR_NOCLIPPERATTACHED; } } HRESULT dds_GetColorKey(IDirectDrawSurfaceImpl *This, DWORD flags, LPDDCOLORKEY colorKey) { if (colorKey) { colorKey->dwColorSpaceHighValue = This->color_key.dwColorSpaceHighValue; colorKey->dwColorSpaceLowValue = This->color_key.dwColorSpaceLowValue; } return DD_OK; } HRESULT dds_GetDC(IDirectDrawSurfaceImpl *This, HDC FAR *lpHDC) { if (!This) { if (lpHDC) *lpHDC = NULL; return DDERR_INVALIDPARAMS; } if ((This->l_pitch % 4)) { dprintf("NOT_IMPLEMENTED GetDC: width=%d height=%d\n", This->width, This->height); } RGBQUAD *data = This->palette ? This->palette->data_rgb : g_ddraw->primary && g_ddraw->primary->palette ? g_ddraw->primary->palette->data_rgb : NULL; HDC dc = This->hdc; if (This->backbuffer || (This->caps & DDSCAPS_BACKBUFFER)) dc = (HDC)InterlockedExchangeAdd((LONG*)&This->hdc, 0); if (data) SetDIBColorTable(dc, 0, 256, data); if (lpHDC) *lpHDC = dc; return DD_OK; } HRESULT dds_ReleaseDC(IDirectDrawSurfaceImpl* This, HDC hDC) { if ((This->caps & DDSCAPS_PRIMARYSURFACE) && g_ddraw->render.run) { InterlockedExchange(&g_ddraw->render.surface_updated, TRUE); DWORD time = timeGetTime(); if (!(This->flags & DDSD_BACKBUFFERCOUNT) || (This->last_flip_tick + FLIP_REDRAW_TIMEOUT < time && This->last_blt_tick + FLIP_REDRAW_TIMEOUT < time)) { ReleaseSemaphore(g_ddraw->render.sem, 1, NULL); } } return DD_OK; } HRESULT dds_GetPalette(IDirectDrawSurfaceImpl *This, LPDIRECTDRAWPALETTE FAR *lplpDDPalette) { if (!lplpDDPalette) return DDERR_INVALIDPARAMS; *lplpDDPalette = (LPDIRECTDRAWPALETTE)This->palette; if (This->palette) { IDirectDrawPalette_AddRef(This->palette); return DD_OK; } else { return DDERR_NOPALETTEATTACHED; } } HRESULT dds_GetPixelFormat(IDirectDrawSurfaceImpl *This, LPDDPIXELFORMAT ddpfPixelFormat) { if (ddpfPixelFormat) { memset(ddpfPixelFormat, 0, sizeof(DDPIXELFORMAT)); ddpfPixelFormat->dwSize = sizeof(DDPIXELFORMAT); ddpfPixelFormat->dwFlags = DDPF_RGB; ddpfPixelFormat->dwRGBBitCount = This->bpp; if (This->bpp == 8) { ddpfPixelFormat->dwFlags |= DDPF_PALETTEINDEXED8; } else if (This->bpp == 16) { ddpfPixelFormat->dwRBitMask = 0xF800; ddpfPixelFormat->dwGBitMask = 0x07E0; ddpfPixelFormat->dwBBitMask = 0x001F; } else if (This->bpp == 32) { ddpfPixelFormat->dwRBitMask = 0xFF0000; ddpfPixelFormat->dwGBitMask = 0x00FF00; ddpfPixelFormat->dwBBitMask = 0x0000FF; } return DD_OK; } return DDERR_INVALIDPARAMS; } HRESULT dds_Lock(IDirectDrawSurfaceImpl *This, LPRECT lpDestRect, LPDDSURFACEDESC lpDDSurfaceDesc, DWORD dwFlags, HANDLE hEvent) { dbg_dump_dds_lock_flags(dwFlags); HRESULT ret = dds_GetSurfaceDesc(This, lpDDSurfaceDesc); if (lpDestRect && lpDDSurfaceDesc) { if (lpDestRect->left < 0 || lpDestRect->top < 0 || lpDestRect->left > lpDestRect->right || lpDestRect->top > lpDestRect->bottom || lpDestRect->right > This->width || lpDestRect->bottom > This->height) { return DDERR_INVALIDPARAMS; } lpDDSurfaceDesc->lpSurface = (char*)dds_GetBuffer(This) + (lpDestRect->left * This->lx_pitch) + (lpDestRect->top * This->l_pitch); } return ret; } HRESULT dds_SetColorKey(IDirectDrawSurfaceImpl *This, DWORD flags, LPDDCOLORKEY colorKey) { if (colorKey) { dprintfex(" dwColorSpaceHighValue=%d\n", colorKey->dwColorSpaceHighValue); dprintfex(" dwColorSpaceLowValue=%d\n", colorKey->dwColorSpaceLowValue); This->color_key.dwColorSpaceHighValue = colorKey->dwColorSpaceHighValue; This->color_key.dwColorSpaceLowValue = colorKey->dwColorSpaceLowValue; } return DD_OK; } HRESULT dds_SetClipper(IDirectDrawSurfaceImpl* This, LPDIRECTDRAWCLIPPER lpClipper) { if (lpClipper) IDirectDrawClipper_AddRef(lpClipper); if (This->clipper) IDirectDrawClipper_Release(This->clipper); This->clipper = (IDirectDrawClipperImpl*)lpClipper; return DD_OK; } HRESULT dds_SetPalette(IDirectDrawSurfaceImpl *This, LPDIRECTDRAWPALETTE lpDDPalette) { if (lpDDPalette) IDirectDrawPalette_AddRef(lpDDPalette); if (This->palette) IDirectDrawPalette_Release(This->palette); if (This->caps & DDSCAPS_PRIMARYSURFACE) { EnterCriticalSection(&g_ddraw->cs); This->palette = (IDirectDrawPaletteImpl*)lpDDPalette; LeaveCriticalSection(&g_ddraw->cs); if (g_ddraw->render.run) { InterlockedExchange(&g_ddraw->render.palette_updated, TRUE); ReleaseSemaphore(g_ddraw->render.sem, 1, NULL); } } else { This->palette = (IDirectDrawPaletteImpl*)lpDDPalette; } return DD_OK; } HRESULT dds_Unlock(IDirectDrawSurfaceImpl *This, LPVOID lpRect) { HWND hwnd = g_ddraw->bnet_active ? FindWindowEx(HWND_DESKTOP, NULL, "SDlgDialog", NULL) : NULL; if (hwnd && (This->caps & DDSCAPS_PRIMARYSURFACE)) { HDC primary_dc; dds_GetDC(g_ddraw->primary, &primary_dc); //GdiTransparentBlt idea taken from Aqrit's war2 ddraw RGBQUAD quad; GetDIBColorTable(primary_dc, 0xFE, 1, &quad); COLORREF color = RGB(quad.rgbRed, quad.rgbGreen, quad.rgbBlue); BOOL erase = FALSE; do { RECT rc; if (fake_GetWindowRect(hwnd, &rc)) { if (rc.bottom - rc.top == 479) erase = TRUE; HDC hdc = GetDCEx(hwnd, NULL, DCX_PARENTCLIP | DCX_CACHE); GdiTransparentBlt( hdc, 0, 0, rc.right - rc.left, rc.bottom - rc.top, primary_dc, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, color ); ReleaseDC(hwnd, hdc); } } while ((hwnd = FindWindowEx(HWND_DESKTOP, hwnd, "SDlgDialog", NULL))); if (erase) { BOOL x = g_ddraw->ticks_limiter.use_blt_or_flip; DDBLTFX fx = { .dwFillColor = 0xFE }; IDirectDrawSurface_Blt(This, NULL, NULL, NULL, DDBLT_COLORFILL, &fx); g_ddraw->ticks_limiter.use_blt_or_flip = x; } } if ((This->caps & DDSCAPS_PRIMARYSURFACE) && g_ddraw->render.run) { InterlockedExchange(&g_ddraw->render.surface_updated, TRUE); DWORD time = timeGetTime(); if (!(This->flags & DDSD_BACKBUFFERCOUNT) || (This->last_flip_tick + FLIP_REDRAW_TIMEOUT < time && This->last_blt_tick + FLIP_REDRAW_TIMEOUT < time)) { ReleaseSemaphore(g_ddraw->render.sem, 1, NULL); if (g_ddraw->ticks_limiter.tick_length > 0 && !g_ddraw->ticks_limiter.use_blt_or_flip) util_limit_game_ticks(); } } return DD_OK; } HRESULT dds_GetDDInterface(IDirectDrawSurfaceImpl* This, LPVOID* lplpDD) { if (!lplpDD) return DDERR_INVALIDPARAMS; *lplpDD = This->ddraw; IDirectDraw_AddRef(This->ddraw); return DD_OK; } void* dds_GetBuffer(IDirectDrawSurfaceImpl* This) { if (!This) return NULL; if (This->backbuffer || (This->caps & DDSCAPS_BACKBUFFER)) return (void*)InterlockedExchangeAdd((LONG*)&This->surface, 0); return This->surface; } HRESULT dd_CreateSurface(IDirectDrawImpl* This, LPDDSURFACEDESC lpDDSurfaceDesc, LPDIRECTDRAWSURFACE FAR *lpDDSurface, IUnknown FAR * unkOuter) { dbg_dump_dds_flags(lpDDSurfaceDesc->dwFlags); dbg_dump_dds_caps(lpDDSurfaceDesc->ddsCaps.dwCaps); if (!g_ddraw->passthrough && (lpDDSurfaceDesc->ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE) && g_ddraw->primary && g_ddraw->primary->width == g_ddraw->width && g_ddraw->primary->height == g_ddraw->height && g_ddraw->primary->bpp == g_ddraw->bpp) { *lpDDSurface = (LPDIRECTDRAWSURFACE)g_ddraw->primary; IDirectDrawSurface_AddRef(g_ddraw->primary); return DD_OK; } IDirectDrawSurfaceImpl *dst_surface = (IDirectDrawSurfaceImpl *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IDirectDrawSurfaceImpl)); dst_surface->lpVtbl = &g_dds_vtbl; lpDDSurfaceDesc->dwFlags |= DDSD_CAPS; dst_surface->bpp = g_ddraw->bpp == 0 ? 16 : g_ddraw->bpp; dst_surface->flags = lpDDSurfaceDesc->dwFlags; dst_surface->caps = lpDDSurfaceDesc->ddsCaps.dwCaps; dst_surface->ddraw = This; if (dst_surface->caps & DDSCAPS_PRIMARYSURFACE) { dst_surface->width = g_ddraw->width; dst_surface->height = g_ddraw->height; } else { dst_surface->width = lpDDSurfaceDesc->dwWidth; dst_surface->height = lpDDSurfaceDesc->dwHeight; } if (dst_surface->width && dst_surface->height) { if (dst_surface->width == 71 && dst_surface->height == 24) dst_surface->width = 72; //Commandos dst_surface->lx_pitch = dst_surface->bpp / 8; dst_surface->l_pitch = dst_surface->width * dst_surface->lx_pitch; if (g_ddraw->fixpitch && !(dst_surface->caps & (DDSCAPS_PRIMARYSURFACE | DDSCAPS_BACKBUFFER))) { while (dst_surface->l_pitch % 4) { dst_surface->l_pitch = ++dst_surface->width * dst_surface->lx_pitch; } } dst_surface->bmi = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * 256); dst_surface->bmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); dst_surface->bmi->bmiHeader.biWidth = dst_surface->width; dst_surface->bmi->bmiHeader.biHeight = -((int)dst_surface->height + 200); dst_surface->bmi->bmiHeader.biPlanes = 1; dst_surface->bmi->bmiHeader.biBitCount = dst_surface->bpp; dst_surface->bmi->bmiHeader.biCompression = dst_surface->bpp == 8 ? BI_RGB : BI_BITFIELDS; WORD clr_bits = (WORD)(dst_surface->bmi->bmiHeader.biPlanes * dst_surface->bmi->bmiHeader.biBitCount); if (clr_bits < 24) { dst_surface->bmi->bmiHeader.biClrUsed = (1 << clr_bits); } dst_surface->bmi->bmiHeader.biSizeImage = ((dst_surface->width * clr_bits + 31) & ~31) / 8 * dst_surface->height; if (dst_surface->bpp == 8) { for (int i = 0; i < 256; i++) { dst_surface->bmi->bmiColors[i].rgbRed = i; dst_surface->bmi->bmiColors[i].rgbGreen = i; dst_surface->bmi->bmiColors[i].rgbBlue = i; dst_surface->bmi->bmiColors[i].rgbReserved = 0; } } else if (dst_surface->bpp == 16) { ((DWORD*)dst_surface->bmi->bmiColors)[0] = 0xF800; ((DWORD*)dst_surface->bmi->bmiColors)[1] = 0x07E0; ((DWORD*)dst_surface->bmi->bmiColors)[2] = 0x001F; } else if (dst_surface->bpp == 32) { ((DWORD*)dst_surface->bmi->bmiColors)[0] = 0xFF0000; ((DWORD*)dst_surface->bmi->bmiColors)[1] = 0x00FF00; ((DWORD*)dst_surface->bmi->bmiColors)[2] = 0x0000FF; } dst_surface->hdc = CreateCompatibleDC(g_ddraw->render.hdc); dst_surface->bitmap = CreateDIBSection(dst_surface->hdc, dst_surface->bmi, DIB_RGB_COLORS, (void **)&dst_surface->surface, NULL, 0); dst_surface->bmi->bmiHeader.biHeight = -((int)dst_surface->height); if (!dst_surface->bitmap) { dst_surface->surface = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, dst_surface->l_pitch * (dst_surface->height + 200) * dst_surface->lx_pitch); } if (dst_surface->caps & DDSCAPS_PRIMARYSURFACE) { g_ddraw->primary = dst_surface; FakePrimarySurface = dst_surface->surface; } SelectObject(dst_surface->hdc, dst_surface->bitmap); } if (dst_surface->flags & DDSD_BACKBUFFERCOUNT) { dprintf(" dwBackBufferCount=%d\n", lpDDSurfaceDesc->dwBackBufferCount); if (g_ddraw->backbuffer) { DDSURFACEDESC desc; memset(&desc, 0, sizeof(DDSURFACEDESC)); if (lpDDSurfaceDesc->dwBackBufferCount > 1) { desc.dwBackBufferCount = lpDDSurfaceDesc->dwBackBufferCount - 1; desc.dwFlags |= DDSD_BACKBUFFERCOUNT; } desc.ddsCaps.dwCaps |= DDSCAPS_BACKBUFFER; desc.dwWidth = dst_surface->width; desc.dwHeight = dst_surface->height; dd_CreateSurface(This, &desc, (LPDIRECTDRAWSURFACE*)&dst_surface->backbuffer, unkOuter); } } dprintf(" surface = %p (%dx%d@%d)\n", dst_surface, (int)dst_surface->width, (int)dst_surface->height, (int)dst_surface->bpp); *lpDDSurface = (LPDIRECTDRAWSURFACE)dst_surface; dst_surface->ref = 0; IDirectDrawSurface_AddRef(dst_surface); return DD_OK; }