diff --git a/src/main/iidxhook-util/d3d9.c b/src/main/iidxhook-util/d3d9.c index efea60c..028537e 100644 --- a/src/main/iidxhook-util/d3d9.c +++ b/src/main/iidxhook-util/d3d9.c @@ -21,7 +21,7 @@ /* ------------------------------------------------------------------------- */ -struct d3d8_vertex { +struct d3d9_vertex { float x; float y; float z; @@ -33,94 +33,6 @@ struct d3d8_vertex { /* ------------------------------------------------------------------------- */ -/* Device IDs recognised by IIDX <20. Hex chars must be capital letters. - - 7146: RV515 [Radeon X1300] - 95C5: RV620 LE [Radeon HD 3450] - - VEN is always 1002 (ATi) */ - -static HWND STDCALL my_CreateWindowExA( - DWORD dwExStyle, LPCSTR lpClassName, LPCSTR lpWindowName, DWORD dwStyle, - int X, int Y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, - HINSTANCE hInstance, LPVOID lpParam); - -static BOOL STDCALL my_GetClientRect(HWND hWnd, LPRECT lpRect); - -static HRESULT STDCALL my_CreateDevice( - IDirect3D9 *self, UINT adapter, D3DDEVTYPE type, HWND hwnd, - DWORD flags, D3DPRESENT_PARAMETERS *pp, IDirect3DDevice9 **pdev); - -static IDirect3D9 *STDCALL my_Direct3DCreate9(UINT sdk_ver); - -static BOOL STDCALL my_EnumDisplayDevicesA( - const char *dev_name, DWORD dev_num, DISPLAY_DEVICEA *info, - DWORD flags); - -static HRESULT STDCALL my_CreateTexture(IDirect3DDevice9* self, UINT Width, - UINT Height, UINT Levels, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool, - IDirect3DTexture9** ppTexture, HANDLE* pSharedHandle); - -static HRESULT STDCALL my_SetRenderState(IDirect3DDevice9* self, - D3DRENDERSTATETYPE State, DWORD Value); - -static HRESULT STDCALL my_DrawPrimitiveUP( - IDirect3DDevice9* self, D3DPRIMITIVETYPE primitive_type, - UINT primitive_count, const void *data, UINT stride); - -static HRESULT STDCALL my_Present(IDirect3DDevice9* self, - CONST RECT *pSourceRect, CONST RECT *pDestRect, - HWND hDestWindowOverride, CONST RGNDATA *pDirtyRegion); - -static HRESULT STDCALL my_BeginScene(IDirect3DDevice9* self); - -static HRESULT STDCALL my_EndScene(IDirect3DDevice9* self); - -static void calc_win_size_with_framed(HWND hwnd, DWORD x, DWORD y, - DWORD width, DWORD height, LPWINDOWPOS wp); - -static HWND (STDCALL *real_CreateWindowExA)( - DWORD dwExStyle, LPCSTR lpClassName, LPCSTR lpWindowName, DWORD dwStyle, - int X, int Y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, - HINSTANCE hInstance, LPVOID lpParam); - -static BOOL (STDCALL* real_GetClientRect)(HWND hWnd, LPRECT lpRect); - -static IDirect3D9 * (STDCALL *real_Direct3DCreate9)( - UINT sdk_ver); - -static BOOL (STDCALL *real_EnumDisplayDevicesA)( - const char *dev_name, DWORD dev_num, DISPLAY_DEVICEA *info, - DWORD flags); - -static HRESULT (STDCALL *real_CreateTexture)(IDirect3DDevice9* self, UINT Width, - UINT Height, UINT Levels, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool, - IDirect3DTexture9** ppTexture, HANDLE* pSharedHandle); - -static HRESULT (STDCALL* real_SetRenderState)(IDirect3DDevice9* self, - D3DRENDERSTATETYPE State, DWORD Value); - -static HRESULT (STDCALL *real_DrawPrimitiveUP)( - IDirect3DDevice9* self, D3DPRIMITIVETYPE primitive_type, - UINT primitive_count, const void *data, UINT stride); - -static HRESULT (STDCALL* real_Present)(IDirect3DDevice9* self, - CONST RECT *pSourceRect, CONST RECT *pDestRect, - HWND hDestWindowOverride, CONST RGNDATA *pDirtyRegion); - -static HRESULT (STDCALL* real_BeginScene)(IDirect3DDevice9* self); - -static HRESULT (STDCALL* real_EndScene)(IDirect3DDevice9* self); - -static HRESULT (STDCALL* real_GetRenderTarget)(IDirect3DDevice9* self, DWORD RenderTargetIndex, - IDirect3DSurface9 **ppRenderTarget); - -static HRESULT (STDCALL* real_SetRenderTarget)(IDirect3DDevice9* self, DWORD RenderTargetIndex, - IDirect3DSurface9 *pRenderTarget); - -static HRESULT (STDCALL* real_StretchRect)(IDirect3DDevice9* self, IDirect3DSurface9 *pSourceSurface, - const RECT *pSourceRect, IDirect3DSurface9 *pDestSurface, const RECT *pDestRect, D3DTEXTUREFILTERTYPE Filter); - typedef HRESULT WINAPI (*func_D3DXCreateFontA)(struct IDirect3DDevice9 *device, INT height, UINT width, UINT weight, UINT miplevels, BOOL italic, DWORD charset, DWORD precision, DWORD quality, DWORD pitchandfamily, @@ -128,745 +40,30 @@ typedef HRESULT WINAPI (*func_D3DXCreateFontA)(struct IDirect3DDevice9 *device, /* ------------------------------------------------------------------------- */ -static char d3d9_pci_id[32]; -static bool d3d9_windowed; -static int32_t d3d9_window_width = -1; -static int32_t d3d9_window_height = -1; -static bool d3d9_window_framed; -static d3d9_monitor_check_result_callback_t d3d9_monitor_check_cb; -static bool d3d9_fix_iidx_12_fog = false; -static bool d3d9_fix_iidx_13_lighting = false; -static bool d3d9_nvidia_fix; -static bool d3d9_bg_video_fix; -static float d3d9_frame_rate_limit = 0.0f; -static uint16_t d3d9_scale_back_buffer_width; -static uint16_t d3d9_scale_back_buffer_height; +static struct iidxhook_util_d3d9_config iidxhook_util_d3d9_config; -static D3DTEXTUREFILTERTYPE d3d9_scale_back_buffer_filter; -static IDirect3DSurface9* d3d9_original_back_buffer; -static IDirect3DSurface9* d3d9_scale_intermediate_render_target; -static uint16_t d3d9_original_back_buffer_width; -static uint16_t d3d9_original_back_buffer_height; +static uint64_t iidxhook_util_d3d9_present_current_time = 0; -static void execute_monitor_check(IDirect3DDevice9* api, - IDirect3DDevice9Vtbl* api_vtbl, IDirect3DDevice9* pdev); +static struct { + uint16_t original_back_buffer_width; + uint16_t original_back_buffer_height; -/* ------------------------------------------------------------------------- */ + IDirect3DTexture9* rt_texture; + IDirect3DSurface9* rt_surface; + IDirect3DSurface9* rt_orig_surface; -static const struct hook_symbol d3d9_hook_syms[] = { - { - .name = "Direct3DCreate9", - .patch = my_Direct3DCreate9, - .link = (void **) &real_Direct3DCreate9 - }, -}; + D3DTEXTUREFILTERTYPE filter; +} iidxhook_util_d3d9_back_buffer_scaling; -static const struct hook_symbol d3d9_hook_user32_syms[] = { - { - .name = "EnumDisplayDevicesA", - .patch = my_EnumDisplayDevicesA, - .link = (void **) &real_EnumDisplayDevicesA - }, - { - .name = "CreateWindowExA", - .patch = my_CreateWindowExA, - .link = (void **) &real_CreateWindowExA - }, - { - .name = "GetClientRect", - .patch = my_GetClientRect, - .link = (void **) &real_GetClientRect - }, -}; +/* ------------------------------------------------------------------------------------------------------------------ */ -/* ------------------------------------------------------------------------- */ - -static bool float_equal(float a, float b, float eps) +static bool iidxhook_util_d3d9_float_equal(float a, float b, float eps) { return fabs(a - b) < eps; } -static HWND STDCALL my_CreateWindowExA( - DWORD dwExStyle, LPCSTR lpClassName, LPCSTR lpWindowName, DWORD dwStyle, - int X, int Y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, - HINSTANCE hInstance, LPVOID lpParam) -{ - if (d3d9_windowed && d3d9_window_framed) { - /* use a different style */ - dwStyle |= WS_OVERLAPPEDWINDOW; - /* also show mouse cursor */ - ShowCursor(TRUE); - } - - if (d3d9_window_width != -1 && d3d9_window_height != -1) { - log_misc("Overriding window size from %dx%d with %dx%d", nWidth, - nHeight, d3d9_window_width, d3d9_window_height); - - nWidth = d3d9_window_width; - nHeight = d3d9_window_height; - } - - HWND hwnd = real_CreateWindowExA(dwExStyle, lpClassName, lpWindowName, - dwStyle, X, Y, nWidth, nHeight, hWndParent, hMenu, hInstance, - lpParam); - - if (hwnd == INVALID_HANDLE_VALUE) { - return hwnd; - } - - if (d3d9_windowed && d3d9_window_framed) { - /* we have to adjust the window size, because the window needs to be a - slightly bigger than the rendering resolution (window caption and - stuff is included in the window size) */ - WINDOWPOS wp; - calc_win_size_with_framed(hwnd, X, Y, nWidth, nHeight, &wp); - SetWindowPos(hwnd, 0, wp.x, wp.y, wp.cx, wp.cy, 0); - X = wp.x; - Y = wp.y; - nWidth = wp.cx; - nHeight = wp.cy; - } - - return hwnd; -} - -static BOOL STDCALL my_GetClientRect(HWND hWnd, LPRECT lpRect) -{ - /* IIDX 9-13 (at least) use this call to get the size of rectangle for setting the viewport size... */ - - /* Always use the original requested back buffer size. If scaling is active, using the scaled - values leads to (3D) scenes getting render to a viewport with incorrect sized. */ - lpRect->left = 0; - lpRect->top = 0; - lpRect->right = d3d9_original_back_buffer_width; - lpRect->bottom = d3d9_original_back_buffer_height; - - return TRUE; -} - -static HRESULT STDCALL my_CreateDevice( - IDirect3D9 *self, UINT adapter, D3DDEVTYPE type, HWND hwnd, DWORD flags, - D3DPRESENT_PARAMETERS *pp, IDirect3DDevice9 **pdev) -{ - IDirect3D9 *real = COM_PROXY_UNWRAP(self); - IDirect3DDevice9* api; - IDirect3DDevice9Vtbl *api_vtbl; - struct com_proxy *api_proxy; - HRESULT hr; - char* error; - - log_misc("CreateDevice parameters: adapter %d, type %d, hwnd %p, flags %lX, pdev %p", - adapter, type, hwnd, flags, pdev); - - log_misc("D3D9 presenter parameters: BackBufferWidth %d, BackBufferHeight %d, BackBufferFormat %d, " - "BackBufferCount %d, MultiSampleType %d, SwapEffect %d, hDeviceWindow %p, Windowed %d, EnableAutoDepthStencil " - "%d, AutoDepthStencilFormat %d, Flags %lX, FullScreen_RefreshRateInHz %d", - pp->BackBufferWidth, pp->BackBufferHeight, pp->BackBufferFormat, pp->BackBufferCount, pp->MultiSampleType, - pp->SwapEffect, pp->hDeviceWindow, pp->Windowed, pp->EnableAutoDepthStencil, pp->AutoDepthStencilFormat, - pp->Flags, pp->FullScreen_RefreshRateInHz); - - /* Fix a long-standing bug in IIDX */ - if (flags & D3DCREATE_SOFTWARE_VERTEXPROCESSING) { - flags &= ~D3DCREATE_PUREDEVICE; - } - - if (d3d9_windowed) { - log_misc("Window mode"); - - pp->Windowed = TRUE; - pp->FullScreen_RefreshRateInHz = 0; - } - - /* Store these so we can apply scaling further down */ - d3d9_original_back_buffer_width = pp->BackBufferWidth; - d3d9_original_back_buffer_height = pp->BackBufferHeight; - - if (d3d9_scale_back_buffer_width > 0 && d3d9_scale_back_buffer_height > 0) { - log_misc("Scale back buffer from %dx%d -> %dx%d", d3d9_original_back_buffer_width, - d3d9_original_back_buffer_height, d3d9_scale_back_buffer_width, d3d9_scale_back_buffer_height); - - pp->BackBufferWidth = d3d9_scale_back_buffer_width; - pp->BackBufferHeight = d3d9_scale_back_buffer_height; - } - - /* Same fix as on D3D8, orz... - If we don't do this, some games one certain platforms (e.g. iidx 14/15 on Windows 10). - CreateDevice fails with an "invalid call" on either fullscreen or windowed or even both. - Also, further reports about textures with green glowing borders are gone as well when applying this */ - D3DDISPLAYMODE mode; - IDirect3D9_GetAdapterDisplayMode(real, adapter, &mode); - pp->BackBufferFormat = mode.Format; - - hr = IDirect3D9_CreateDevice(real, adapter, type, hwnd, flags, pp, pdev); - - if (hr != S_OK) { - switch (hr) { - case D3DERR_DEVICELOST: - error = "device lost"; - break; - - case D3DERR_INVALIDCALL: - error = "invalid call"; - break; - - case D3DERR_NOTAVAILABLE: - error = "not available"; - break; - - case D3DERR_OUTOFVIDEOMEMORY: - error = "out of video memory"; - break; - - default: - error = "unknown"; - break; - } - - log_warning("Creating D3D9 device failed: %lX, %s", hr, error); - - return hr; - } - - api = *pdev; - api_proxy = com_proxy_wrap(api, sizeof(*api->lpVtbl)); - api_vtbl = api_proxy->vptr; - - real_CreateTexture = api_vtbl->CreateTexture; - api_vtbl->CreateTexture = my_CreateTexture; - - real_DrawPrimitiveUP = api_vtbl->DrawPrimitiveUP; - api_vtbl->DrawPrimitiveUP = my_DrawPrimitiveUP; - - real_SetRenderState = api_vtbl->SetRenderState; - api_vtbl->SetRenderState = my_SetRenderState; - - real_Present = api_vtbl->Present; - api_vtbl->Present = my_Present; - - real_BeginScene = api_vtbl->BeginScene; - api_vtbl->BeginScene = my_BeginScene; - - real_EndScene = api_vtbl->EndScene; - api_vtbl->EndScene = my_EndScene; - - real_SetRenderTarget = api_vtbl->SetRenderTarget; - real_GetRenderTarget = api_vtbl->GetRenderTarget; - real_StretchRect = api_vtbl->StretchRect; - - *pdev = (IDirect3DDevice9*) api_proxy; - - if (d3d9_scale_back_buffer_width > 0 && d3d9_scale_back_buffer_height > 0) { - hr = api_vtbl->CreateRenderTarget(*pdev, d3d9_scale_back_buffer_width, d3d9_scale_back_buffer_height, - pp->BackBufferFormat, pp->MultiSampleType, 0, false, &d3d9_scale_intermediate_render_target, NULL); - - if (hr != D3D_OK) { - log_warning("Creating intermediate render target for scaling back buffer failed: %ld", hr); - return hr; - } - } - - if (d3d9_monitor_check_cb) { - execute_monitor_check(api, api_vtbl, *pdev); - } - - /* Avoid returning our scaled values because the application might use them, e.g. calculate - sprite positions. */ - pp->BackBufferWidth = d3d9_original_back_buffer_width; - pp->BackBufferHeight = d3d9_original_back_buffer_height; - - return hr; -} - -static IDirect3D9 *STDCALL my_Direct3DCreate9(UINT sdk_ver) -{ - IDirect3D9 *api; - IDirect3D9Vtbl *api_vtbl; - struct com_proxy *api_proxy; - - log_info("Direct3DCreate9 hook hit"); - - api = real_Direct3DCreate9(sdk_ver); - api_proxy = com_proxy_wrap(api, sizeof(*api->lpVtbl)); - api_vtbl = api_proxy->vptr; - - api_vtbl->CreateDevice = my_CreateDevice; - - return (IDirect3D9 *) api_proxy; -} - -static BOOL STDCALL my_EnumDisplayDevicesA( - const char *dev_name, DWORD dev_num, DISPLAY_DEVICEA *info, - DWORD flags) -{ - BOOL ok; - - ok = real_EnumDisplayDevicesA(dev_name, dev_num, info, flags); - - if (ok && d3d9_pci_id[0] != '\0') { - /* Apparently Konami didn't read the "Not Used" message in the MSDN - docs for DISPLAY_DEVICE */ - log_misc("Replacing device ID %s with %s", - info->DeviceID, d3d9_pci_id); - - str_cpy(info->DeviceID, sizeof(info->DeviceID), d3d9_pci_id); - } - - return ok; -} - -static HRESULT STDCALL my_CreateTexture(IDirect3DDevice9* self, UINT Width, - UINT Height, UINT Levels, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool, - IDirect3DTexture9** ppTexture, HANDLE* pSharedHandle) -{ - /* Fix non ATI cards not working on Gold (and DJT?). There is a check that - creates textures when starting the game. This check fails which results - in a message box being shown mentioning something about an aep-lib error. - Seems like a combination of parameters is not supported by - non ATI cards and throws an error - (Fix taken from old Gold cracks by ahnada and tau) - */ - if (d3d9_nvidia_fix && Width == 256 && Height == 256 && Levels == 1 && - Usage == 1 && Format == 25 && Pool == 0 && pSharedHandle == 0) { - /* log_misc("CreateTexture: Patching texture format..."); */ - Format = 21; - } - - return real_CreateTexture(self, Width, Height, Levels, Usage, Format, Pool, - ppTexture, pSharedHandle); -} - -static HRESULT STDCALL my_SetRenderState(IDirect3DDevice9* self, - D3DRENDERSTATETYPE State, DWORD Value) -{ - if (d3d9_fix_iidx_12_fog) { - if (State == D3DRS_FOGENABLE) { - Value = FALSE; - } - } - - if (d3d9_fix_iidx_13_lighting) { - if (State == D3DRS_LIGHTING) { - Value = FALSE; - } - } - - return real_SetRenderState(self, State, Value); -} - -static HRESULT STDCALL my_DrawPrimitiveUP( - IDirect3DDevice9* self, D3DPRIMITIVETYPE primitive_type, - UINT primitive_count, const void *data, UINT stride) -{ - /* same code taken from the d3d8 module. but, this just fixes the quad - seam issue as there are no reports of streched bg videos */ - if ( d3d9_bg_video_fix && primitive_type == 6 && primitive_count == 2 && - stride == 28) { - - struct d3d8_vertex* vertices = (struct d3d8_vertex*) data; - - /* - log_info("Video Tex: %f/%f %f/%f %f/%f %f/%f", - vertices[0].x, vertices[0].y, - vertices[1].x, vertices[1].y, - vertices[2].x, vertices[2].y, - vertices[3].x, vertices[3].y); - */ - - /* Fix full screen background videos (e.g. DistorteD intro sequence) */ - if ( vertices[0].x >= 0.0f && vertices[0].x < 1.0f && - vertices[0].y >= 0.0f && vertices[0].y < 1.0f && - vertices[1].x > 639.0f && vertices[1].x < 641.0f && - vertices[1].y >= 0.0f && vertices[1].y < 1.0f && - vertices[2].x > 639.0f && vertices[2].x < 641.0f && - vertices[2].y > 479.0f && vertices[2].y < 481.0f && - vertices[3].x >= 0.0f && vertices[3].x < 1.0f && - vertices[3].y > 479.0f && vertices[3].y < 481.0f) { - /* fix UVs - 1.0f / 640 or 480 fixes the diagonal seam connecting the two - triangles which is visible on some GPUs (why? idk) - */ - vertices[0].tu = 0.0f + 1.0f / 640; - vertices[0].tv = 1.0f; - vertices[1].tu = 1.0f; - vertices[1].tv = 1.0f; - vertices[2].tu = 1.0f; - vertices[2].tv = 0.0f + 1.0f / 480; - vertices[3].tu = 0.0f + 1.0f / 640; - vertices[3].tv = 0.0f + 1.0f / 480; - } else - - /* another identifier, because there are other textures with 512x512 size - make sure we got the bg video only to not mess up anything else */ - /* different versions have different themes and position the bg video - on slightly different positions (good job...) */ - if ( /* single */ - ((vertices[0].x >= 164.0f && vertices[0].x <= 168.0f && - float_equal(vertices[0].y, 0.0f, 0.1f)) && - (vertices[1].x >= 472.0f && vertices[1].x <= 476.0f && - float_equal(vertices[1].y, 0.0f, 0.1f)) && - (vertices[2].x >= 472.0f && vertices[2].x <= 476.0f && - float_equal(vertices[2].y, 416.0f, 0.1f)) && - (vertices[3].x >= 164.0f && vertices[3].x <= 168.0f && - float_equal(vertices[3].y, 416.0f, 0.1f))) || - /* double top left */ - ((float_equal(vertices[0].x, 6.0f, 0.1f) && - vertices[0].y >= 24.0f && vertices[0].y <= 28.0f) && - (float_equal(vertices[1].x, 147.0f, 0.1f) && - vertices[1].y >= 24.0f && vertices[1].y <= 28.0f) && - (float_equal(vertices[2].x, 147.0f, 0.1f) && - vertices[2].y >= 212.0f && vertices[2].y <= 216.0f) && - (float_equal(vertices[3].x, 6.0f, 0.1f) && - vertices[3].y >= 212.0f && vertices[3].y <= 216.0f)) || - /* double bottom left */ - ((float_equal(vertices[0].x, 6.0f, 0.1f) && - vertices[0].y >= 216.0f && vertices[0].y <= 220.0f) && - (float_equal(vertices[1].x, 147.0f, 0.1f) && - vertices[1].y >= 216.0f && vertices[1].y <= 220.0f) && - (float_equal(vertices[2].x, 147.0f, 0.1) && - vertices[2].y >= 404.0f && vertices[2].y <= 408.0f) && - (float_equal(vertices[3].x, 6.0f, 0.1f) && - vertices[3].y >= 404.0f && vertices[3].y <= 408.0f)) || - /* double top right */ - ((vertices[0].x >= 493.0f && vertices[0].x <= 494.0f && - vertices[0].y >= 24.0f && vertices[0].y <= 28.0f) && - (vertices[1].x >= 634.0f && vertices[1].x <= 635.0f && - vertices[1].y >= 24.0f && vertices[1].y <= 28.0f) && - (vertices[2].x >= 634.0f && vertices[2].x <= 635.0f && - vertices[2].y >= 212.0f && vertices[2].y <= 216.0f) && - (vertices[3].x >= 493.0f && vertices[3].x <= 494.0f && - vertices[3].y >= 212.0f && vertices[3].y <= 216.0f)) || - /* double bottom right */ - ((vertices[0].x >= 493.0f && vertices[0].x <= 494.0f && - vertices[0].y >= 216.0f && vertices[0].y <= 220.0f) && - (vertices[1].x >= 634.0f && vertices[1].x <= 635.0f && - vertices[1].y >= 216.0f && vertices[1].y <= 220.0f) && - (vertices[2].x >= 634.0f && vertices[2].x <= 635.0f && - vertices[2].y >= 404.0f && vertices[2].y <= 408.0f) && - (vertices[3].x >= 493.0f && vertices[3].x <= 494.0f && - vertices[3].y >= 404.0f && vertices[3].y <= 408.0f))) - { - /* fix UVs - 1.0f / 512 fixes the diagonal seam connecting the two triangles - which is visible on some GPUs (why? idk) - */ - vertices[0].tu = 0.0f + 1.0f / 512; - vertices[0].tv = 1.0f; - vertices[1].tu = 1.0f; - vertices[1].tv = 1.0f; - vertices[2].tu = 1.0f; - vertices[2].tv = 0.0f + 1.0f / 512; - vertices[3].tu = 0.0f + 1.0f / 512; - vertices[3].tv = 0.0f + 1.0f / 512; - } - - } - - return real_DrawPrimitiveUP(self, primitive_type, primitive_count, - data, stride); -} - -static HRESULT STDCALL my_Present(IDirect3DDevice9* self, - CONST RECT *pSourceRect, CONST RECT *pDestRect, - HWND hDestWindowOverride, CONST RGNDATA *pDirtyRegion) -{ - static uint64_t current_time = 0; - - HRESULT hr = real_Present(self, pSourceRect, pDestRect, hDestWindowOverride, - pDirtyRegion); - - if (d3d9_frame_rate_limit > 0.0f) { - if (current_time == 0) { - current_time = time_get_counter(); - } else { - uint64_t frame_time = 1000000 / d3d9_frame_rate_limit; - - uint64_t dt = time_get_elapsed_us( - time_get_counter() - current_time); - - while (dt < frame_time) { - /* waste some cpu time by polling - because we can't sleep for X us */ - dt = time_get_elapsed_us(time_get_counter() - current_time); - } - - current_time = time_get_counter(); - } - } - - return hr; -} - -static HRESULT STDCALL my_BeginScene(IDirect3DDevice9* self) -{ - HRESULT res; - - if (d3d9_scale_back_buffer_width > 0 && d3d9_scale_back_buffer_height > 0) { - res = real_GetRenderTarget(self, 0, &d3d9_original_back_buffer); - - if (res != D3D_OK) { - log_warning("Getting back buffer render target failed: %ld", res); - return res; - } - - res = real_SetRenderTarget(self, 0, d3d9_scale_intermediate_render_target); - - if (res != D3D_OK) { - log_warning("Setting intermediate render target failed: %ld", res); - return res; - } - } - - return real_BeginScene(self); -} - -static HRESULT STDCALL my_EndScene(IDirect3DDevice9* self) -{ - HRESULT res = real_EndScene(self); - - if (res == D3D_OK) { - if (d3d9_scale_back_buffer_width > 0 && d3d9_scale_back_buffer_height > 0) { - res = real_SetRenderTarget(self, 0, d3d9_original_back_buffer); - - if (res != D3D_OK) { - log_warning("Setting back back buffer render target failed: %ld", res); - return res; - } - - RECT rect; - rect.left = 0; - rect.top = 0; - rect.right = d3d9_original_back_buffer_width; - rect.bottom = d3d9_original_back_buffer_height; - - /* Must be called outside of begin-end scene block */ - res = real_StretchRect(self, d3d9_scale_intermediate_render_target, &rect, d3d9_original_back_buffer, NULL, - d3d9_scale_back_buffer_filter); - - if (res != D3D_OK) { - log_warning("Stretching immediate render target to back buffer failed: %ld", res); - return res; - } - } - } - - return res; -} - -void d3d9_hook_init(void) -{ - hook_table_apply( - NULL, - "d3d9.dll", - d3d9_hook_syms, - lengthof(d3d9_hook_syms)); - - hook_table_apply( - NULL, - "user32.dll", - d3d9_hook_user32_syms, - lengthof(d3d9_hook_user32_syms)); - - log_info("Inserted graphics hooks"); -} - -void d3d9_set_windowed(bool framed, int32_t width, int32_t height) -{ - d3d9_windowed = true; - d3d9_window_framed = framed; - d3d9_window_width = width; - d3d9_window_height = height; -} - -void d3d9_set_pci_id(uint16_t vid, uint16_t pid) -{ - str_format(d3d9_pci_id, sizeof(d3d9_pci_id), "PCI\\VEN_%04X&DEV_%04X", - vid, pid); -} - -void d3d9_set_frame_rate_limit(float limit) -{ - if (limit < 0.0f) { - limit = 0.0f; - } - - log_info("Limiting rendering frame rate to %f frames", limit); - d3d9_frame_rate_limit = limit; -} - -void d3d9_enable_monitor_check(d3d9_monitor_check_result_callback_t cb) -{ - log_assert(cb); - - d3d9_monitor_check_cb = cb; - log_info("Enabled monitor check"); -} - -void d3d9_iidx_fix_stretched_bg_videos(void) -{ - /* Same as the seam fix */ - d3d9_bg_video_fix = true; - log_info("Fixing UVs of stretched BG videos"); -} - -void d3d9_iidx_fix_12_song_select_bg(void) -{ - d3d9_fix_iidx_12_fog = true; - log_info("Fixing Happy Sky bugged music select 3D background"); -} - -void d3d9_iidx_fix_13_song_select_bg(void) -{ - d3d9_fix_iidx_13_lighting = true; - log_info("Fixing DistorteD music select 3D background"); -} - -void d3d9_enable_nvidia_fix(void) -{ - d3d9_nvidia_fix = true; - log_info("Enabled NVIDIA fix"); -} - -void d3d9_bg_video_seams_fix(void) -{ - d3d9_bg_video_fix = true; - log_info("Enabled BG video seam fix"); -} - -void d3d9_scale_back_buffer(uint16_t width, uint16_t height, enum d3d9_back_buffer_scale_filter filter) -{ - d3d9_scale_back_buffer_width = width; - d3d9_scale_back_buffer_height = height; - - switch (filter) { - case D3D9_BACK_BUFFER_SCALE_FILTER_NONE: - d3d9_scale_back_buffer_filter = D3DTEXF_NONE; - break; - - case D3D9_BACK_BUFFER_SCALE_FILTER_LINEAR: - d3d9_scale_back_buffer_filter = D3DTEXF_LINEAR; - break; - - case D3D9_BACK_BUFFER_SCALE_FILTER_POINT: - d3d9_scale_back_buffer_filter = D3DTEXF_POINT; - break; - - default: - log_fatal("Illegal state"); - break; - } - - if (d3d9_scale_back_buffer_width > 0 && d3d9_scale_back_buffer_height > 0) { - log_info("Enable scaling of back buffer, target size %dx%d, filter %d", width, height, filter); - } else { - d3d9_scale_back_buffer_width = 0; - d3d9_scale_back_buffer_height = 0; - } -} - -/* ------------------------------------------------------------------------- */ - -static void execute_monitor_check(IDirect3DDevice9* api, - IDirect3DDevice9Vtbl* api_vtbl, IDirect3DDevice9* pdev) -{ - log_info("Running monitor check..."); - - /* All games include the _24 version anyway, so leave that hardcoded here */ - HMODULE d3d9 = GetModuleHandleA("d3dx9_24.dll"); - - if (d3d9 == NULL) { - log_fatal("monitor check: failed to load d3dx9_24.dll"); - } - - func_D3DXCreateFontA d3dxCreateFontA = - (func_D3DXCreateFontA) GetProcAddress(d3d9, "D3DXCreateFontA"); - - if (d3dxCreateFontA == NULL) { - log_fatal("monitor check: failed to find function D3DXCreateFontA"); - } - - RECT fRectangle; - ID3DXFont* font = NULL; - struct com_proxy *font_api_proxy; - ID3DXFontVtbl *font_api_vtbl; - - HRESULT hr = d3dxCreateFontA(api, 22, 0, FW_NORMAL, 1, false, - DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, ANTIALIASED_QUALITY, FF_DONTCARE, - "Arial", &font); - - font_api_proxy = com_proxy_wrap(font, sizeof(*font->lpVtbl)); - font_api_vtbl = font_api_proxy->vptr; - - font = (ID3DXFont*) font_api_proxy; - SetRect(&fRectangle, 20, 20, 640, 480); - - if (hr != S_OK) { - log_fatal("monitor check: Creating font failed"); - } - - if (font == NULL) { - log_fatal("monitor check: Loading font failed"); - } - - const uint32_t max_iterations = 60 * 30; - const uint32_t skip_frames = 60 * 1; - - uint64_t accu_us = 0; - - double result = 0; - uint32_t iterations = 0; - - char text_buffer[256]; - - while (iterations < max_iterations) { - sprintf(text_buffer, - "Monitor check...\n" - "Elapsed iterations: %d/%d\n" - "Refresh rate: %f", - iterations + 1, max_iterations, result); - - iterations++; - uint64_t start = time_get_counter(); - - api_vtbl->Clear(pdev, 0, NULL, D3DCLEAR_TARGET, - D3DCOLOR_XRGB(0, 0, 0), 0.0f, 0); - api_vtbl->BeginScene(pdev); - - font_api_vtbl->DrawTextA(font, NULL, text_buffer, -1, &fRectangle, - DT_LEFT, D3DCOLOR_XRGB(0xFF, 0xFF, 0xFF)); - - api_vtbl->EndScene(pdev); - api_vtbl->Present(pdev, NULL, NULL, NULL, NULL); - - /* Skip the first inaccurate values */ - if (iterations > skip_frames) { - accu_us += time_get_elapsed_us(time_get_counter() - start); - - result = ((double) (iterations - skip_frames)) / - (accu_us / 1000.0 / 1000.0); - } - } - - log_info("Monitor check done (total iterations %d), refesh rate: %f hz", - iterations, result); - - /* Sanity check to ensure people notice that their current refresh rate - is way off. */ - if (result < 55 || result > 65) { - log_warning("Monitor check result (%f hz) is not even near the " - "intended refresh rate of 60 hz. Fix your setup to ensure a " - "constant and as close to as possible 60 hz refresh rate.", result); - } - - /* Leave results of monitor check on screen for a moment */ - Sleep(2000); - - log_assert(d3d9_monitor_check_cb); - - d3d9_monitor_check_cb(result); -} - -static void calc_win_size_with_framed(HWND hwnd, DWORD x, DWORD y, DWORD width, - DWORD height, LPWINDOWPOS wp) +static void iidxhook_util_d3d9_calc_win_size_with_framed(HWND hwnd, DWORD x, DWORD y, DWORD width, DWORD height, + LPWINDOWPOS wp) { /* taken from dxwnd */ RECT rect; @@ -905,3 +102,863 @@ static void calc_win_size_with_framed(HWND hwnd, DWORD x, DWORD y, DWORD width, wp->cy = rect.bottom - rect.top; } +/* ------------------------------------------------------------------------------------------------------------------ */ + +static void iidxhook_util_d3d9_patch_gpu_vid_pid(struct hook_d3d9_irp* irp) +{ + char pci_id[32]; + + log_assert(irp); + log_assert(irp->op == HOOK_D3D9_IRP_OP_ENUM_DISPLAY_DEVICES); + + if (iidxhook_util_d3d9_config.pci_vid > 0 && iidxhook_util_d3d9_config.pci_pid > 0) { + str_format(pci_id, sizeof(pci_id), "PCI\\VEN_%04X&DEV_%04X", iidxhook_util_d3d9_config.pci_vid, + iidxhook_util_d3d9_config.pci_pid); + + /* Apparently Konami didn't read the "Not Used" message in the MSDN + docs for DISPLAY_DEVICE */ + log_misc("Replacing device ID %s with %s", irp->args.enum_display_devices.info->DeviceID, pci_id); + + str_cpy(irp->args.enum_display_devices.info->DeviceID, + sizeof(irp->args.enum_display_devices.info->DeviceID), + pci_id); + } +} + +static void iidxhook_util_d3d9_patch_window(struct hook_d3d9_irp* irp) +{ + log_assert(irp); + log_assert(irp->op == HOOK_D3D9_IRP_OP_CREATE_WINDOW_EX); + + if (iidxhook_util_d3d9_config.windowed && iidxhook_util_d3d9_config.framed) { + /* use a different style */ + irp->args.create_window_ex.style |= WS_OVERLAPPEDWINDOW; + + /* also show mouse cursor */ + ShowCursor(TRUE); + } + + if (iidxhook_util_d3d9_config.override_window_width > 0 && iidxhook_util_d3d9_config.override_window_height) { + log_misc("Overriding window size from %dx%d with %dx%d", + irp->args.create_window_ex.width, + irp->args.create_window_ex.height, + iidxhook_util_d3d9_config.override_window_width, + iidxhook_util_d3d9_config.override_window_height); + + irp->args.create_window_ex.width = iidxhook_util_d3d9_config.override_window_width; + irp->args.create_window_ex.height = iidxhook_util_d3d9_config.override_window_height; + } +} + +static void iidxhook_util_d3d9_fix_window_size_and_pos(struct hook_d3d9_irp* irp) +{ + log_assert(irp); + log_assert(irp->op == HOOK_D3D9_IRP_OP_CREATE_WINDOW_EX); + + if (iidxhook_util_d3d9_config.windowed && iidxhook_util_d3d9_config.framed) { + /* we have to adjust the window size, because the window needs to be a + slightly bigger than the rendering resolution (window caption and + stuff is included in the window size) */ + WINDOWPOS wp; + + iidxhook_util_d3d9_calc_win_size_with_framed( + irp->args.create_window_ex.result, + irp->args.create_window_ex.x, + irp->args.create_window_ex.y, + irp->args.create_window_ex.width, + irp->args.create_window_ex.height, + &wp); + + SetWindowPos(irp->args.create_window_ex.result, 0, wp.x, wp.y, wp.cx, wp.cy, 0); + + irp->args.create_window_ex.x = wp.x; + irp->args.create_window_ex.y = wp.y; + irp->args.create_window_ex.width = wp.cx; + irp->args.create_window_ex.height = wp.cy; + } +} + +static void iidxhook_util_d3d9_log_create_device_params(struct hook_d3d9_irp* irp) +{ + log_assert(irp); + log_assert(irp->op == HOOK_D3D9_IRP_OP_CTX_CREATE_DEVICE); + + log_misc("CreateDevice parameters: adapter %d, type %d, hwnd %p, flags %lX, pdev %p", + irp->args.ctx_create_device.adapter, + irp->args.ctx_create_device.type, + irp->args.ctx_create_device.hwnd, + irp->args.ctx_create_device.flags, + irp->args.ctx_create_device.pdev); + + log_misc("D3D9 presenter parameters: BackBufferWidth %d, BackBufferHeight %d, BackBufferFormat %d, " + "BackBufferCount %d, MultiSampleType %d, SwapEffect %d, hDeviceWindow %p, Windowed %d, EnableAutoDepthStencil " + "%d, AutoDepthStencilFormat %d, Flags %lX, FullScreen_RefreshRateInHz %d", + irp->args.ctx_create_device.pp->BackBufferWidth, + irp->args.ctx_create_device.pp->BackBufferHeight, + irp->args.ctx_create_device.pp->BackBufferFormat, + irp->args.ctx_create_device.pp->BackBufferCount, + irp->args.ctx_create_device.pp->MultiSampleType, + irp->args.ctx_create_device.pp->SwapEffect, + irp->args.ctx_create_device.pp->hDeviceWindow, + irp->args.ctx_create_device.pp->Windowed, + irp->args.ctx_create_device.pp->EnableAutoDepthStencil, + irp->args.ctx_create_device.pp->AutoDepthStencilFormat, + irp->args.ctx_create_device.pp->Flags, + irp->args.ctx_create_device.pp->FullScreen_RefreshRateInHz); +} + +static void iidxhook_util_d3d9_fix_iidx_bug_software_processing(struct hook_d3d9_irp* irp) +{ + log_assert(irp); + log_assert(irp->op == HOOK_D3D9_IRP_OP_CTX_CREATE_DEVICE); + + /* Fix a long-standing bug in IIDX */ + if (irp->args.ctx_create_device.flags & D3DCREATE_SOFTWARE_VERTEXPROCESSING) { + irp->args.ctx_create_device.flags &= ~D3DCREATE_PUREDEVICE; + } +} + +static void iidxhook_util_d3d9_fix_create_device_apply_window_mode(struct hook_d3d9_irp* irp) +{ + log_assert(irp); + log_assert(irp->op == HOOK_D3D9_IRP_OP_CTX_CREATE_DEVICE); + + if (iidxhook_util_d3d9_config.windowed) { + irp->args.ctx_create_device.pp->Windowed = TRUE; + irp->args.ctx_create_device.pp->FullScreen_RefreshRateInHz = 0; + } +} + +static void iidxhook_util_d3d9_fix_back_buffer_format(struct hook_d3d9_irp* irp) +{ + log_assert(irp); + log_assert(irp->op == HOOK_D3D9_IRP_OP_CTX_CREATE_DEVICE); + + /* Same fix as on D3D8, orz... + If we don't do this, some games one certain platforms (e.g. iidx 14/15 on Windows 10). + CreateDevice fails with an "invalid call" on either fullscreen or windowed or even both. + Also, further reports about textures with green glowing borders are gone as well when applying this */ + D3DDISPLAYMODE mode; + IDirect3D9_GetAdapterDisplayMode( + irp->args.ctx_create_device.self, + irp->args.ctx_create_device.adapter, + &mode); + + irp->args.ctx_create_device.pp->BackBufferFormat = mode.Format; +} + +static ID3DXFont* iidxhook_util_d3d9_load_and_create_font(IDirect3DDevice9* dev) +{ + HMODULE d3d9_24; + HRESULT hr; + ID3DXFont* font; + + d3d9_24 = GetModuleHandleA("d3dx9_24.dll"); + + if (d3d9_24 == NULL) { + log_warning("Failed to load d3dx9_24.dll to create a font for displaying framerate on monitor check."); + return NULL; + } + + func_D3DXCreateFontA d3dxCreateFontA =(func_D3DXCreateFontA) GetProcAddress(d3d9_24, "D3DXCreateFontA"); + + if (d3dxCreateFontA == NULL) { + log_warning("Failed to find function D3DXCreateFontA"); + return NULL; + } + + hr = d3dxCreateFontA(dev, 22, 0, FW_NORMAL, 1, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, ANTIALIASED_QUALITY, + FF_DONTCARE, "Arial", &font); + + if (hr != S_OK) { + log_warning("Failed to load font for monitor check: %lX", hr); + return NULL; + } + + return font; +} + +static void iidxhook_util_d3d9_execute_monitor_check(struct hook_d3d9_irp* irp) +{ + const uint32_t max_iterations = 60 * 30; + const uint32_t skip_frames = 60 * 1; + + uint64_t accu_us; + double result; + uint32_t iterations; + uint64_t start; + + char text_buffer[256]; + ID3DXFont* font; + RECT font_rect; + + log_assert(irp); + log_assert(irp->op == HOOK_D3D9_IRP_OP_CTX_CREATE_DEVICE); + + if (iidxhook_util_d3d9_config.iidx09_to_19_monitor_check_cb) { + log_info("Running monitor check...2"); + + accu_us = 0; + + result = 0; + iterations = 0; + + font = iidxhook_util_d3d9_load_and_create_font(*irp->args.ctx_create_device.pdev); + + if (font == NULL) { + log_info("Monitor check without on-screen text showing current refresh rate."); + } else { + SetRect(&font_rect, 20, 20, 640, 480); + } + + while (iterations < max_iterations) { + if (font) { + sprintf( + text_buffer, + "Monitor check...\n" + "Elapsed iterations: %d/%d\n" + "Refresh rate: %f", + iterations + 1, + max_iterations, + result); + } + + iterations++; + start = time_get_counter(); + + IDirect3DDevice9_BeginScene(*irp->args.ctx_create_device.pdev); + + IDirect3DDevice9_Clear(*irp->args.ctx_create_device.pdev, 0, NULL, D3DCLEAR_TARGET, + D3DCOLOR_XRGB(0xFF, 0xFF, 0xFF), 0.0f, 0); + + if (font) { + ID3DXFont_DrawTextA(font, NULL, text_buffer, -1, &font_rect, DT_LEFT, D3DCOLOR_XRGB(0xFF, 0xFF, 0xFF)); + } + + IDirect3DDevice9_EndScene(*irp->args.ctx_create_device.pdev); + + IDirect3DDevice9_Present(*irp->args.ctx_create_device.pdev, NULL, NULL, NULL, NULL); + + /* Skip the first inaccurate values */ + if (iterations > skip_frames) { + accu_us += time_get_elapsed_us(time_get_counter() - start); + + result = ((double) (iterations - skip_frames)) / + (accu_us / 1000.0 / 1000.0); + } + } + + log_info("Monitor check done (total iterations %d), refesh rate: %f hz", + iterations, result); + + /* Sanity check to ensure people notice that their current refresh rate + is way off. */ + if (result < 55 || result > 65) { + log_warning("Monitor check result (%f hz) is not even near the " + "intended refresh rate of 60 hz. Fix your setup to ensure a " + "constant and as close to as possible 60 hz refresh rate.", result); + } + + /* Leave results of monitor check on screen for a moment */ + Sleep(2000); + + log_assert(iidxhook_util_d3d9_config.iidx09_to_19_monitor_check_cb); + + iidxhook_util_d3d9_config.iidx09_to_19_monitor_check_cb(result); + } +} + +static void iidxhook_util_d3d9_log_result_create_device(HRESULT hr) +{ + char* error; + + if (hr != S_OK) { + switch (hr) { + case D3DERR_DEVICELOST: + error = "device lost"; + break; + + case D3DERR_INVALIDCALL: + error = "invalid call"; + break; + + case D3DERR_NOTAVAILABLE: + error = "not available"; + break; + + case D3DERR_OUTOFVIDEOMEMORY: + error = "out of video memory"; + break; + + default: + error = "unknown"; + break; + } + + log_warning("Creating D3D9 device failed: %lX, %s", hr, error); + } +} + +static void iidxhook_util_d3d9_nvidia_fix_iidx14_to_19(struct hook_d3d9_irp* irp) +{ + log_assert(irp); + log_assert(irp->op == HOOK_D3D9_IRP_OP_DEV_CREATE_TEXTURE); + + /* Fix non ATI cards not working on Gold (and DJT?). There is a check that + creates textures when starting the game. This check fails which results + in a message box being shown mentioning something about an aep-lib error. + Seems like a combination of parameters is not supported by + non ATI cards and throws an error + (Fix taken from old Gold cracks by ahnada and tau) + */ + + if (iidxhook_util_d3d9_config.iidx14_to_19_nvidia_fix && + irp->args.dev_create_texture.width == 256 && + irp->args.dev_create_texture.height == 256 && + irp->args.dev_create_texture.levels == 1 && + irp->args.dev_create_texture.usage == D3DUSAGE_RENDERTARGET && + irp->args.dev_create_texture.format == D3DFMT_A1R5G5B5 && + irp->args.dev_create_texture.pool == D3DPOOL_DEFAULT && + irp->args.dev_create_texture.shared_handle == NULL) { + irp->args.dev_create_texture.format = D3DFMT_A8R8G8B8; + } +} + +static void iidxhook_util_d3d9_framerate_limiter(struct hook_d3d9_irp* irp) +{ + log_assert(irp); + log_assert(irp->op == HOOK_D3D9_IRP_OP_DEV_PRESENT); + + if (iidxhook_util_d3d9_config.framerate_limit > 0.0f) { + if (iidxhook_util_d3d9_present_current_time == 0) { + iidxhook_util_d3d9_present_current_time = time_get_counter(); + } else { + uint64_t frame_time = 1000000 / iidxhook_util_d3d9_config.framerate_limit; + + uint64_t dt = time_get_elapsed_us(time_get_counter() - iidxhook_util_d3d9_present_current_time); + + while (dt < frame_time) { + /* waste some cpu time by polling + because we can't sleep for X us */ + dt = time_get_elapsed_us(time_get_counter() - iidxhook_util_d3d9_present_current_time); + } + + iidxhook_util_d3d9_present_current_time = time_get_counter(); + } + } +} + +static void iidxhook_util_d3d9_iidx12_fix_song_select_bg(struct hook_d3d9_irp* irp) +{ + log_assert(irp); + log_assert(irp->op == HOOK_D3D9_IRP_OP_DEV_SET_RENDER_STATE); + + if (iidxhook_util_d3d9_config.iidx12_fix_song_select_bg) { + if (irp->args.dev_set_render_state.state == D3DRS_FOGENABLE) { + irp->args.dev_set_render_state.value = FALSE; + } + } +} + +static void iidxhook_util_d3d9_iidx13_fix_song_select_bg(struct hook_d3d9_irp* irp) +{ + log_assert(irp); + log_assert(irp->op == HOOK_D3D9_IRP_OP_DEV_SET_RENDER_STATE); + + if (iidxhook_util_d3d9_config.iidx13_fix_song_select_bg) { + if (irp->args.dev_set_render_state.state == D3DRS_LIGHTING) { + irp->args.dev_set_render_state.value = FALSE; + } + } +} + +static void iidxhook_util_d3d9_iidx11_to_17_fix_uvs_bg_videos(struct hook_d3d9_irp* irp) +{ + log_assert(irp); + log_assert(irp->op == HOOK_D3D9_IRP_OP_DEV_DRAW_PRIMITIVE_UP); + + /* same code taken from the d3d8 module. but, this just fixes the quad + seam issue as there are no reports of streched bg videos */ + if (iidxhook_util_d3d9_config.iidx11_to_17_fix_uvs_bg_videos && + irp->args.dev_draw_primitive_up.primitive_type == D3DPT_TRIANGLEFAN && + irp->args.dev_draw_primitive_up.primitive_count == 2 && + irp->args.dev_draw_primitive_up.stride == 28) { + struct d3d9_vertex* vertices = (struct d3d9_vertex*) irp->args.dev_draw_primitive_up.data; + + /* + log_info("Video Tex: %f/%f %f/%f %f/%f %f/%f", + vertices[0].x, vertices[0].y, + vertices[1].x, vertices[1].y, + vertices[2].x, vertices[2].y, + vertices[3].x, vertices[3].y); + */ + + /* Fix full screen background videos (e.g. DistorteD intro sequence) */ + if ( vertices[0].x >= 0.0f && vertices[0].x < 1.0f && + vertices[0].y >= 0.0f && vertices[0].y < 1.0f && + vertices[1].x > 639.0f && vertices[1].x < 641.0f && + vertices[1].y >= 0.0f && vertices[1].y < 1.0f && + vertices[2].x > 639.0f && vertices[2].x < 641.0f && + vertices[2].y > 479.0f && vertices[2].y < 481.0f && + vertices[3].x >= 0.0f && vertices[3].x < 1.0f && + vertices[3].y > 479.0f && vertices[3].y < 481.0f) { + /* fix UVs + 1.0f / 640 or 480 fixes the diagonal seam connecting the two + triangles which is visible on some GPUs (why? idk) + */ + vertices[0].tu = 0.0f + 1.0f / 640; + vertices[0].tv = 1.0f; + vertices[1].tu = 1.0f; + vertices[1].tv = 1.0f; + vertices[2].tu = 1.0f; + vertices[2].tv = 0.0f + 1.0f / 480; + vertices[3].tu = 0.0f + 1.0f / 640; + vertices[3].tv = 0.0f + 1.0f / 480; + } else + + /* another identifier, because there are other textures with 512x512 size + make sure we got the bg video only to not mess up anything else */ + /* different versions have different themes and position the bg video + on slightly different positions (good job...) */ + if ( /* single */ + ((vertices[0].x >= 164.0f && vertices[0].x <= 168.0f && + iidxhook_util_d3d9_float_equal(vertices[0].y, 0.0f, 0.1f)) && + (vertices[1].x >= 472.0f && vertices[1].x <= 476.0f && + iidxhook_util_d3d9_float_equal(vertices[1].y, 0.0f, 0.1f)) && + (vertices[2].x >= 472.0f && vertices[2].x <= 476.0f && + iidxhook_util_d3d9_float_equal(vertices[2].y, 416.0f, 0.1f)) && + (vertices[3].x >= 164.0f && vertices[3].x <= 168.0f && + iidxhook_util_d3d9_float_equal(vertices[3].y, 416.0f, 0.1f))) || + /* double top left */ + ((iidxhook_util_d3d9_float_equal(vertices[0].x, 6.0f, 0.1f) && + vertices[0].y >= 24.0f && vertices[0].y <= 28.0f) && + (iidxhook_util_d3d9_float_equal(vertices[1].x, 147.0f, 0.1f) && + vertices[1].y >= 24.0f && vertices[1].y <= 28.0f) && + (iidxhook_util_d3d9_float_equal(vertices[2].x, 147.0f, 0.1f) && + vertices[2].y >= 212.0f && vertices[2].y <= 216.0f) && + (iidxhook_util_d3d9_float_equal(vertices[3].x, 6.0f, 0.1f) && + vertices[3].y >= 212.0f && vertices[3].y <= 216.0f)) || + /* double bottom left */ + ((iidxhook_util_d3d9_float_equal(vertices[0].x, 6.0f, 0.1f) && + vertices[0].y >= 216.0f && vertices[0].y <= 220.0f) && + (iidxhook_util_d3d9_float_equal(vertices[1].x, 147.0f, 0.1f) && + vertices[1].y >= 216.0f && vertices[1].y <= 220.0f) && + (iidxhook_util_d3d9_float_equal(vertices[2].x, 147.0f, 0.1) && + vertices[2].y >= 404.0f && vertices[2].y <= 408.0f) && + (iidxhook_util_d3d9_float_equal(vertices[3].x, 6.0f, 0.1f) && + vertices[3].y >= 404.0f && vertices[3].y <= 408.0f)) || + /* double top right */ + ((vertices[0].x >= 493.0f && vertices[0].x <= 494.0f && + vertices[0].y >= 24.0f && vertices[0].y <= 28.0f) && + (vertices[1].x >= 634.0f && vertices[1].x <= 635.0f && + vertices[1].y >= 24.0f && vertices[1].y <= 28.0f) && + (vertices[2].x >= 634.0f && vertices[2].x <= 635.0f && + vertices[2].y >= 212.0f && vertices[2].y <= 216.0f) && + (vertices[3].x >= 493.0f && vertices[3].x <= 494.0f && + vertices[3].y >= 212.0f && vertices[3].y <= 216.0f)) || + /* double bottom right */ + ((vertices[0].x >= 493.0f && vertices[0].x <= 494.0f && + vertices[0].y >= 216.0f && vertices[0].y <= 220.0f) && + (vertices[1].x >= 634.0f && vertices[1].x <= 635.0f && + vertices[1].y >= 216.0f && vertices[1].y <= 220.0f) && + (vertices[2].x >= 634.0f && vertices[2].x <= 635.0f && + vertices[2].y >= 404.0f && vertices[2].y <= 408.0f) && + (vertices[3].x >= 493.0f && vertices[3].x <= 494.0f && + vertices[3].y >= 404.0f && vertices[3].y <= 408.0f))) + { + /* fix UVs + 1.0f / 512 fixes the diagonal seam connecting the two triangles + which is visible on some GPUs (why? idk) + */ + vertices[0].tu = 0.0f + 1.0f / 512; + vertices[0].tv = 1.0f; + vertices[1].tu = 1.0f; + vertices[1].tv = 1.0f; + vertices[2].tu = 1.0f; + vertices[2].tv = 0.0f + 1.0f / 512; + vertices[3].tu = 0.0f + 1.0f / 512; + vertices[3].tv = 0.0f + 1.0f / 512; + } + } +} + +static void iidxhook_uti_d3d9_fix_iidx9_to_13_viewport_size(struct hook_d3d9_irp* irp) +{ + log_assert(irp); + log_assert(irp->op == HOOK_D3D9_IRP_OP_GET_CLIENT_RECT); + + /* IIDX 9-13 (at least) use this call to get the size of rectangle for setting the viewport size... */ + + /* Always use the original requested back buffer size. If scaling is active, using the scaled + values leads to (3D) scenes getting render to a viewport with incorrect sized. */ + irp->args.get_client_rect.rect->left = 0; + irp->args.get_client_rect.rect->top = 0; + irp->args.get_client_rect.rect->right = iidxhook_util_d3d9_back_buffer_scaling.original_back_buffer_width; + irp->args.get_client_rect.rect->bottom = iidxhook_util_d3d9_back_buffer_scaling.original_back_buffer_height; +} + +static void iidxhook_util_d3d9_setup_back_buffer_scaling_pre(struct hook_d3d9_irp* irp) +{ + log_assert(irp); + log_assert(irp->op == HOOK_D3D9_IRP_OP_CTX_CREATE_DEVICE); + + /* Store these so we can apply scaling further down */ + iidxhook_util_d3d9_back_buffer_scaling.original_back_buffer_width = + irp->args.ctx_create_device.pp->BackBufferWidth; + iidxhook_util_d3d9_back_buffer_scaling.original_back_buffer_height = + irp->args.ctx_create_device.pp->BackBufferHeight; + + switch (iidxhook_util_d3d9_config.scale_back_buffer_filter) { + case IIDXHOOK_UTIL_D3D9_BACK_BUFFER_SCALE_FILTER_NONE: + iidxhook_util_d3d9_back_buffer_scaling.filter = D3DTEXF_NONE; + break; + + case IIDXHOOK_UTIL_D3D9_BACK_BUFFER_SCALE_FILTER_LINEAR: + iidxhook_util_d3d9_back_buffer_scaling.filter = D3DTEXF_LINEAR; + break; + + case IIDXHOOK_UTIL_D3D9_BACK_BUFFER_SCALE_FILTER_POINT: + iidxhook_util_d3d9_back_buffer_scaling.filter = D3DTEXF_POINT; + break; + + default: + log_fatal("Unhandled case, illegal state: %d", iidxhook_util_d3d9_config.scale_back_buffer_filter); + break; + } + + if (iidxhook_util_d3d9_config.scale_back_buffer_width > 0 && + iidxhook_util_d3d9_config.scale_back_buffer_height > 0) { + log_misc("Scale back buffer from %dx%d -> %dx%d", + iidxhook_util_d3d9_back_buffer_scaling.original_back_buffer_width, + iidxhook_util_d3d9_back_buffer_scaling.original_back_buffer_height, + iidxhook_util_d3d9_config.scale_back_buffer_width, + iidxhook_util_d3d9_config.scale_back_buffer_height); + + irp->args.ctx_create_device.pp->BackBufferWidth = + iidxhook_util_d3d9_config.scale_back_buffer_width; + irp->args.ctx_create_device.pp->BackBufferHeight = + iidxhook_util_d3d9_config.scale_back_buffer_height; + } +} + +static void iidxhook_util_d3d9_setup_back_buffer_scaling_post(struct hook_d3d9_irp* irp) +{ + HRESULT hr; + + log_assert(irp); + log_assert(irp->op == HOOK_D3D9_IRP_OP_CTX_CREATE_DEVICE); + + if (iidxhook_util_d3d9_config.scale_back_buffer_width > 0 && + iidxhook_util_d3d9_config.scale_back_buffer_height > 0) { + + log_misc("Creating intermediate render target texture: %dx%d", + iidxhook_util_d3d9_back_buffer_scaling.original_back_buffer_width, + iidxhook_util_d3d9_back_buffer_scaling.original_back_buffer_height); + + /* This is our new render target. Let the game render to this in its native res instead of the back buffer */ + hr = IDirect3DDevice9_CreateTexture( + *irp->args.ctx_create_device.pdev, + iidxhook_util_d3d9_back_buffer_scaling.original_back_buffer_width, + iidxhook_util_d3d9_back_buffer_scaling.original_back_buffer_height, + 0, + D3DUSAGE_RENDERTARGET, + irp->args.ctx_create_device.pp->BackBufferFormat, + D3DPOOL_DEFAULT, + &iidxhook_util_d3d9_back_buffer_scaling.rt_texture, + NULL); + + if (hr != S_OK) { + log_fatal("Creating render target texture failed: %lX", hr); + } + + hr = IDirect3DTexture9_GetSurfaceLevel( + iidxhook_util_d3d9_back_buffer_scaling.rt_texture, + 0, + &iidxhook_util_d3d9_back_buffer_scaling.rt_surface); + + if (hr != S_OK) { + log_fatal("Getting surface of render target texture failed: %lX", hr); + } + + /* Save surface of original render target */ + hr = IDirect3DDevice9_GetRenderTarget( + *irp->args.ctx_create_device.pdev, + 0, + &iidxhook_util_d3d9_back_buffer_scaling.rt_orig_surface); + + if (hr != S_OK) { + log_fatal("Getting original render target failed: %lX", hr); + } + + /* Avoid returning our scaled values because the application might use them, e.g. calculate + sprite positions. */ + irp->args.ctx_create_device.pp->BackBufferWidth = + iidxhook_util_d3d9_back_buffer_scaling.original_back_buffer_width; + irp->args.ctx_create_device.pp->BackBufferHeight = + iidxhook_util_d3d9_back_buffer_scaling.original_back_buffer_height; + } +} + +static void iidxhook_util_d3d9_set_intermediate_rt(struct hook_d3d9_irp* irp) +{ + HRESULT hr; + + log_assert(irp); + log_assert(irp->op == HOOK_D3D9_IRP_OP_DEV_BEGIN_SCENE); + + if (iidxhook_util_d3d9_config.scale_back_buffer_width > 0 && + iidxhook_util_d3d9_config.scale_back_buffer_height > 0) { + /* Set our intermediate render texture as render target */ + hr = IDirect3DDevice9_SetRenderTarget( + irp->args.dev_begin_scene.self, + 0, + iidxhook_util_d3d9_back_buffer_scaling.rt_surface); + + if (hr != S_OK) { + log_fatal("Setting intermediate render target failed: %lX", hr); + } + } +} + +static void iidxhook_util_d3d9_set_back_buffer_rt(struct hook_d3d9_irp* irp) +{ + HRESULT hr; + + log_assert(irp); + log_assert(irp->op == HOOK_D3D9_IRP_OP_DEV_PRESENT); + + if (iidxhook_util_d3d9_config.scale_back_buffer_width > 0 && + iidxhook_util_d3d9_config.scale_back_buffer_height > 0) { + /* Set our original surface/back buffer as rt */ + hr = IDirect3DDevice9_SetRenderTarget( + irp->args.dev_present.self, + 0, + iidxhook_util_d3d9_back_buffer_scaling.rt_orig_surface); + + if (hr != S_OK) { + log_fatal("Setting original render target failed: %lX", hr); + } + } +} + +static void iidxhook_util_d3d9_scale_render_target_to_back_buffer(struct hook_d3d9_irp* irp) +{ + HRESULT hr; + RECT src_rect; + + log_assert(irp); + log_assert(irp->op == HOOK_D3D9_IRP_OP_DEV_PRESENT); + + if (iidxhook_util_d3d9_config.scale_back_buffer_width > 0 && + iidxhook_util_d3d9_config.scale_back_buffer_height > 0) { + src_rect.left = 0; + src_rect.top = 0; + src_rect.right = iidxhook_util_d3d9_back_buffer_scaling.original_back_buffer_width; + src_rect.bottom = iidxhook_util_d3d9_back_buffer_scaling.original_back_buffer_height; + + hr = IDirect3DDevice9_StretchRect( + irp->args.dev_present.self, + iidxhook_util_d3d9_back_buffer_scaling.rt_surface, + &src_rect, + iidxhook_util_d3d9_back_buffer_scaling.rt_orig_surface, + NULL, + iidxhook_util_d3d9_back_buffer_scaling.filter); + + if (hr != S_OK) { + log_fatal("Scaling intermediate render target to back buffer failed: %lX", hr); + } + } +} + +/* ------------------------------------------------------------------------------------------------------------------ */ + +static void iidxhook_util_d3d9_log_config(const struct iidxhook_util_d3d9_config* config) +{ + log_misc( + "iidxhook_util_d3d9_config\n" + "windowed: %d\n" + "framed: %d\n" + "override_window_width: %d\n" + "override_window_height: %d\n" + "framerate_limit: %f\n" + "pci_vid: %X\n" + "pci_pid: %X\n" + "iidx09_to_19_monitor_check_cb: %p\n" + "iidx11_to_17_fix_uvs_bg_videos: %d\n" + "iidx12_fix_song_select_bg: %d\n" + "iidx13_fix_song_select_bg: %d\n" + "iidx14_to_19_nvidia_fix: %d\n" + "scale_back_buffer_width: %d\n" + "scale_back_buffer_height: %d\n" + "scale_back_buffer_filter: %d", + config->windowed, + config->framed, + config->override_window_width, + config->override_window_height, + config->framerate_limit, + config->pci_vid, + config->pci_pid, + config->iidx09_to_19_monitor_check_cb, + config->iidx11_to_17_fix_uvs_bg_videos, + config->iidx12_fix_song_select_bg, + config->iidx13_fix_song_select_bg, + config->iidx14_to_19_nvidia_fix, + config->scale_back_buffer_width, + config->scale_back_buffer_height, + config->scale_back_buffer_filter); +} + +static void iidxhook_util_d3d9_validate_config(const struct iidxhook_util_d3d9_config* config) +{ + if (!config->windowed && config->framed) { + log_warning("Option framed does not have an effect without windowed"); + } + + if (config->override_window_width == -1) { + log_fatal("Invalid value for override_window_width: %d", config->override_window_width); + } + + if (config->override_window_height == -1) { + log_fatal("Invalid value for override_window_height: %d", config->override_window_height); + } + + if (!config->windowed && + (config->override_window_width > 0 || config->override_window_height > 0)) { + log_warning("Overriding window size does not have an effect without windowed"); + } + + if (config->framerate_limit < 0.0f) { + log_fatal("Invalid value for framerate_limit: %f", config->framerate_limit); + } +} + +/* ------------------------------------------------------------------------------------------------------------------ */ + +void iidxhook_util_d3d9_hook_init(void) +{ + iidxhook_util_d3d9_init_config(&iidxhook_util_d3d9_config); + + log_info("Initialized"); +} + +void iidxhook_util_d3d9_init_config(struct iidxhook_util_d3d9_config* config) +{ + config->windowed = false; + config->framed = false; + config->override_window_width = 0; + config->override_window_height = 0; + config->framerate_limit = 0.0f; + config->pci_vid = 0; + config->pci_pid = 0; + config->iidx09_to_19_monitor_check_cb = NULL; + config->iidx11_to_17_fix_uvs_bg_videos = false; + config->iidx12_fix_song_select_bg = false; + config->iidx13_fix_song_select_bg = false; + config->iidx14_to_19_nvidia_fix = false; + config->scale_back_buffer_width = 0; + config->scale_back_buffer_height = 0; + config->scale_back_buffer_filter = IIDXHOOK_UTIL_D3D9_BACK_BUFFER_SCALE_FILTER_NONE; +} + +void iidxhook_util_d3d9_configure(const struct iidxhook_util_d3d9_config* config) +{ + log_assert(config); + + iidxhook_util_d3d9_log_config(config); + iidxhook_util_d3d9_validate_config(config); + + memcpy(&iidxhook_util_d3d9_config, config, sizeof(struct iidxhook_util_d3d9_config)); +} + +HRESULT iidxhook_util_d3d9_irp_handler(struct hook_d3d9_irp* irp) +{ + HRESULT hr; + + log_assert(irp); + + switch (irp->op) { + case HOOK_D3D9_IRP_OP_ENUM_DISPLAY_DEVICES: + hr = hook_d3d9_irp_invoke_next(irp); + + if (hr == S_OK) { + iidxhook_util_d3d9_patch_gpu_vid_pid(irp); + } + + return hr; + + case HOOK_D3D9_IRP_OP_CREATE_WINDOW_EX: + iidxhook_util_d3d9_patch_window(irp); + + hr = hook_d3d9_irp_invoke_next(irp); + + if (hr == S_OK) { + iidxhook_util_d3d9_fix_window_size_and_pos(irp); + } + + return hr; + + case HOOK_D3D9_IRP_OP_GET_CLIENT_RECT: + hr = hook_d3d9_irp_invoke_next(irp); + + if (hr == S_OK) { + iidxhook_uti_d3d9_fix_iidx9_to_13_viewport_size(irp); + } + + return hr; + + case HOOK_D3D9_IRP_OP_CTX_CREATE_DEVICE: + iidxhook_util_d3d9_log_create_device_params(irp); + iidxhook_util_d3d9_fix_iidx_bug_software_processing(irp); + iidxhook_util_d3d9_fix_create_device_apply_window_mode(irp); + iidxhook_util_d3d9_fix_back_buffer_format(irp); + iidxhook_util_d3d9_setup_back_buffer_scaling_pre(irp); + + hr = hook_d3d9_irp_invoke_next(irp); + + if (hr == S_OK) { + iidxhook_util_d3d9_log_result_create_device(hr); + iidxhook_util_d3d9_setup_back_buffer_scaling_post(irp); + iidxhook_util_d3d9_execute_monitor_check(irp); + } + + return hr; + + case HOOK_D3D9_IRP_OP_DEV_CREATE_TEXTURE: + iidxhook_util_d3d9_nvidia_fix_iidx14_to_19(irp); + + return hook_d3d9_irp_invoke_next(irp); + + case HOOK_D3D9_IRP_OP_DEV_BEGIN_SCENE: + iidxhook_util_d3d9_set_intermediate_rt(irp); + + return hook_d3d9_irp_invoke_next(irp); + + case HOOK_D3D9_IRP_OP_DEV_PRESENT: + iidxhook_util_d3d9_scale_render_target_to_back_buffer(irp); + iidxhook_util_d3d9_set_back_buffer_rt(irp); + + hr = hook_d3d9_irp_invoke_next(irp); + + if (hr == S_OK) { + iidxhook_util_d3d9_framerate_limiter(irp); + } + + return hr; + + case HOOK_D3D9_IRP_OP_DEV_SET_RENDER_STATE: + iidxhook_util_d3d9_iidx12_fix_song_select_bg(irp); + iidxhook_util_d3d9_iidx13_fix_song_select_bg(irp); + + return hook_d3d9_irp_invoke_next(irp); + + case HOOK_D3D9_IRP_OP_DEV_DRAW_PRIMITIVE_UP: + iidxhook_util_d3d9_iidx11_to_17_fix_uvs_bg_videos(irp); + + return hook_d3d9_irp_invoke_next(irp); + + default: + return hook_d3d9_irp_invoke_next(irp); + } + + log_fatal("Illegal state"); +} \ No newline at end of file diff --git a/src/main/iidxhook-util/d3d9.h b/src/main/iidxhook-util/d3d9.h index 1d95424..0ef3240 100644 --- a/src/main/iidxhook-util/d3d9.h +++ b/src/main/iidxhook-util/d3d9.h @@ -1,123 +1,180 @@ #ifndef IIDXHOOK_D3D9_H #define IIDXHOOK_D3D9_H +/** + * This module hooks into various D3D9 calls implementing a large set of features for IIDX games such as: + * window mode, frame buffer up-/downscaling, software rate limiting, software monitor check,... + * + * @author icex2 + */ + +#include #include +#include "hook/d3d9.h" + /** * Filter types for scaling back buffer, e.g. upscaling for higher resolution monitors. */ -enum d3d9_back_buffer_scale_filter { - D3D9_BACK_BUFFER_SCALE_FILTER_NONE = 0, - D3D9_BACK_BUFFER_SCALE_FILTER_LINEAR = 1, - D3D9_BACK_BUFFER_SCALE_FILTER_POINT = 2, +enum iidxhook_util_d3d9_back_buffer_scale_filter { + IIDXHOOK_UTIL_D3D9_BACK_BUFFER_SCALE_FILTER_NONE = 0, + IIDXHOOK_UTIL_D3D9_BACK_BUFFER_SCALE_FILTER_LINEAR = 1, + IIDXHOOK_UTIL_D3D9_BACK_BUFFER_SCALE_FILTER_POINT = 2, }; /** * Callback function to receive resulting refresh rate of monitor check. */ -typedef void (*d3d9_monitor_check_result_callback_t)(double refresh_rate); +typedef void (*iidxhook_util_d3d9_monitor_check_result_callback_t)(double refresh_rate); /** - * Hook some d3d9 functions to patch gfx related stuff - * like enabling window mode. - * - * Games using d3d9: - * - IIDX: Gold to Copula + * Config structure for this module. Enable/disable supported features. */ -void d3d9_hook_init(void); +struct iidxhook_util_d3d9_config { + /** + * Enable/disable window mode. Consider combining with framed. + */ + bool windowed; + + /** + * Enable a window frame and make the window movable, resizable, minizable. + */ + bool framed; + + /** + * Override the default window height which is determined by the frame buffer size. + * Set this to 0 to disable the override. + */ + uint16_t override_window_width; + + /** + * Override the default window width which is determined by the frame buffer size. + * Set this to 0 to disable the override. + */ + uint16_t override_window_height; + + /** + * Set a software driven framerate limit for the rendering loop and lock to the specified refresh rate, e.g. 59.94. + * Use this if the game won't sync up properly with vsync enabled. + */ + float framerate_limit; + + /** + * Override the GPU vendor ID identifier on display device enumeration. + * Use this to disable locking to specific GPU cards. + * Combine with the pci_pid parameter. + * + * Vendor ATI: 1002 + */ + uint16_t pci_vid; + + /** + * Override the GPU product ID identifier on display device enumeration. + * Use this to disable locking to specific GPU cards. + * Combine with the pci_pid parameter. + * + * PIDs: + * 7146: RV515 [Radeon X1300] + * 95C5: RV620 LE [Radeon HD 3450] + */ + uint16_t pci_pid; + + /** + * Enable a software "monitor check" which determines the (avg.) refresh rate + * of the game. Ensure that your GPU is outputting a stable refresh rate to + * your monitor. Otherwise, the resulting values fluctuate a lot. + * + * Callback to a function that receives the result of the monitor check once it completed. + */ + iidxhook_util_d3d9_monitor_check_result_callback_t iidx09_to_19_monitor_check_cb; + + /** + * Ported from d3d8 module to enable this when using d3d8to9. + * + * Fixes UV coordinates for background videos on IIDX Red to DD and a quad seam issue for 14 to 17. + * Use this if you experience the game stretching the background videos or seeing a quad seam + * (see implementation for further details). + */ + bool iidx11_to_17_fix_uvs_bg_videos; + + /** + * Ported from d3d8 module to enable this when using d3d8to9. + * + * Fixes the 3D background displayed during song select by disabling fog. + * On some machines/GPUs the background is rendered completely white which + * is caused by buggy fog rendering. Fog is used to hide the rings appearing + * but that's quite 'far away' so disabling fog doesn't look bad. + */ + bool iidx12_fix_song_select_bg; + + /** + * Ported from d3d8 module to enable this when using d3d8to9. + * + * Fixes the 3D background displayed during song select by disabling lighting. + * On some machines/GPUs the background is rendered completely black which + * is caused by buggy lighting. Disabling (dynamic) lighting sets the whole + * scene to ambient lighting which displays everything properly. + */ + bool iidx13_fix_song_select_bg; + + /** + * Fixes a parameter on CreateTexture calls causing the game to crash with + * NVIDIA cards (on GOLD and possible newer) + */ + bool iidx14_to_19_nvidia_fix; + + /** + * Scale the back buffer after a frame got rendered. This allows you to up-/downscale the final frame to display + * arbitrary resolutions, e.g. 640x480 -> 1920x1080 to allow displaying the game in the monitor's native resolution. + * This avoids over-/underscan (yes, that's still a thing with todays TVs) or leaving the upscaling to monitors/TVs + * that are doing a terrible job regarding picture quality or latency wise. + * + * @param width Target width to scale the frame to. 0 disables scaling entirely. + * @param height Target height to scale the frame to. 0 disables scaling entirely. + * @param filter Filtering method to use for scaling. Depending on how you scale, this can have an impact on the + * final image quality. + */ + uint16_t scale_back_buffer_width; + + /** + * Back buffer scale height, see parameter scale_back_buffer_width for full doc. + */ + uint16_t scale_back_buffer_height; + + /** + * Back buffer scale filter to apply, see parameter scale_back_buffer_width for full doc. + */ + enum iidxhook_util_d3d9_back_buffer_scale_filter scale_back_buffer_filter; +}; /** - * Set the game to window mode. - * - * @param framed True to add a window frame and make the window - * movable, resizable, minizable. False for no frame. - * @param width Width of the window. -1 to keep original width. - * @param height Height of the window. -1 to keep original height. - */ -void d3d9_set_windowed(bool framed, int32_t width, int32_t height); - -/** - * Patch the GPU device ID detection to allow running the game with - * other GPUs than the ones it is locked to. - * - * @param vid Vendor ID to patch. - * @param pid Product ID to patch. - */ -void d3d9_set_pci_id(uint16_t vid, uint16_t pid); - -/** - * Set a framerate limit for the rendering loop. - * - * Use this if the game won't sync up properly with vsync enabled. - * - * @limit Number of frames to limit the rendering loop to - */ -void d3d9_set_frame_rate_limit(float limit); - -/** - * Enable a software a "monitor check" which determines the (avg.) refresh rate - * of the game. Ensure that your GPU is outputting a stable refresh rate to - * your monitor. Otherwise, the resulting values fluctuate a lot + * Initialize this hook module. Must be called before any other function of this module. * - * @param cb Callback to a function that receives the result of the monitor - * check once it completed. + * Games using d3d9: IIDX 14 (Gold) to IIDX 27 (Rootage) + * For d3d8 based games (IIDX 9 to 13): use d3d8to9 wrapper to allow usage of this module. */ -void d3d9_enable_monitor_check(d3d9_monitor_check_result_callback_t cb); +void iidxhook_util_d3d9_hook_init(void); /** - * Ported from d3d8 module to enable this when using d3d8to9. + * Initialize the provided config struct with default values. * - * Fixes UV coordinates for background videos on - * IIDX Red to DD. Use this if you experience the game - * stretching the background videos - * (see implementation for further details) + * @param config Pointer to an initialized struct to write default values to. */ -void d3d9_iidx_fix_stretched_bg_videos(void); +void iidxhook_util_d3d9_init_config(struct iidxhook_util_d3d9_config* config); /** - * Ported from d3d8 module to enable this when using d3d8to9. + * Configure this module by applying the provided config. * - * Fixes the 3D background displayed during song select by disabling fog. - * On some machines/GPUs the background is rendered completely white which - * is caused by buggy fog rendering. Fog is used to hide the rings appearing - * but that's quite 'far away' so disabling fog doesn't look bad. + * @param config Config to apply. */ -void d3d9_iidx_fix_12_song_select_bg(void); +void iidxhook_util_d3d9_configure(const struct iidxhook_util_d3d9_config* config); /** - * Ported from d3d8 module to enable this when using d3d8to9. + * Main IRP handler call for this module. Must be set up with the hook/d3d9 module to receive calls from d3d9. * - * Fixes the 3D background displayed during song select by disabling lighting. - * On some machines/GPUs the background is rendered completely black which - * is caused by buggy lighting. Disabling (dynamic) lighting sets the whole - * scene to ambient lighting which displays everything properly. + * @param irp Pointer to a valid IRP. + * @return S_OK on success, error otherwise (depending on the irp op). */ -void d3d9_iidx_fix_13_song_select_bg(void); - -/** - * Fixes a parameter on CreateTexture calls causing the game to crash with - * NVIDIA cards (on GOLD and possible newer) - */ -void d3d9_enable_nvidia_fix(void); - -/** - * Fixes UV coordinates for background videos on - * (similar to the d3d8 games). But, this fixes the quad seam issue on 14 to - * 17, only. - */ -void d3d9_bg_video_seams_fix(void); - -/** - * Scale the back buffer after a frame got rendered. This allows you to up-/downscale the final frame to display - * arbitrary resolutions, e.g. 640x480 -> 1920x1080 to allow displaying the game in the monitor's native resolution. - * This avoids over-/underscan (yes, that's still a thing with todays TVs) or leaving the upscaling to monitors/TVs - * that are doing a terrible job regarding picture quality or latency wise. - * - * @param width Target width to scale the frame to. 0 disables scaling entirely. - * @param height Target height to scale the frame to. 0 disables scaling entirely. - * @param filter Filtering method to use for scaling. Depending on how you scale, this can have an impact on the final - * image quality. - */ -void d3d9_scale_back_buffer(uint16_t width, uint16_t height, enum d3d9_back_buffer_scale_filter filter); +HRESULT iidxhook_util_d3d9_irp_handler(struct hook_d3d9_irp* irp); #endif