1
0
mirror of https://github.com/FunkyFr3sh/cnc-ddraw.git synced 2025-03-24 17:49:52 +01:00
cnc-ddraw/src/directinput.c

476 lines
15 KiB
C
Raw Normal View History

2020-10-13 09:20:52 +02:00
#include <windows.h>
2021-05-23 14:55:36 +02:00
#include <initguid.h>
2021-05-11 21:45:38 +02:00
#include "directinput.h"
2020-10-13 09:20:52 +02:00
#include "debug.h"
#include "hook.h"
#include "dd.h"
#include "mouse.h"
2023-08-03 07:19:27 +02:00
#include "config.h"
2020-10-13 09:20:52 +02:00
2023-08-02 17:09:01 +02:00
#ifdef _MSC_VER
#include "detours.h"
#endif
BOOL g_dinput_hook_active;
2020-10-13 09:20:52 +02:00
2021-05-11 21:45:38 +02:00
DIRECTINPUTCREATEAPROC real_DirectInputCreateA;
2021-05-23 14:55:36 +02:00
DIRECTINPUTCREATEWPROC real_DirectInputCreateW;
DIRECTINPUTCREATEEXPROC real_DirectInputCreateEx;
2021-05-11 21:45:38 +02:00
DIRECTINPUT8CREATEPROC real_DirectInput8Create;
2020-10-13 09:20:52 +02:00
static DICREATEDEVICEPROC real_di_CreateDevice;
2021-05-23 14:55:36 +02:00
static DICREATEDEVICEEXPROC real_di_CreateDeviceEx;
2020-10-13 09:20:52 +02:00
static DIDSETCOOPERATIVELEVELPROC real_did_SetCooperativeLevel;
static DIDGETDEVICEDATAPROC real_did_GetDeviceData;
2021-06-15 02:21:44 +02:00
static DIDGETDEVICESTATEPROC real_did_GetDeviceState;
2024-06-11 02:58:20 +02:00
static PROC* g_di_CreateDevice_vtbl_addr;
static PROC* g_di_CreateDeviceEx_vtbl_addr;
static PROC* g_did_SetCooperativeLevel_vtbl_addr;
static PROC* g_did_GetDeviceData_vtbl_addr;
static PROC* g_did_GetDeviceState_vtbl_addr;
static LPDIRECTINPUTDEVICEA g_mouse_device;
2020-10-13 09:20:52 +02:00
2021-06-11 20:30:43 +02:00
static PROC hook_func(PROC* org_func, PROC new_func)
2020-10-13 09:20:52 +02:00
{
2024-06-11 03:00:50 +02:00
if (!org_func || !new_func)
2024-06-11 02:58:20 +02:00
return 0;
2020-10-13 09:20:52 +02:00
PROC org = *org_func;
DWORD old_protect;
if (VirtualProtect(org_func, sizeof(PROC), PAGE_EXECUTE_READWRITE, &old_protect))
{
*org_func = new_func;
VirtualProtect(org_func, sizeof(PROC), old_protect, &old_protect);
2020-10-13 21:58:04 +02:00
2020-10-13 09:20:52 +02:00
return org;
}
return 0;
}
2021-06-11 20:30:43 +02:00
static HRESULT WINAPI fake_did_SetCooperativeLevel(IDirectInputDeviceA* This, HWND hwnd, DWORD dwFlags)
2020-10-13 09:20:52 +02:00
{
2024-06-16 05:37:11 +02:00
TRACE("DirectInput SetCooperativeLevel(This=%p, hwnd=%p, dwFlags=0x%08X) [%p]\n", This, hwnd, dwFlags, _ReturnAddress());
2021-05-23 14:55:36 +02:00
2024-03-22 22:27:00 +01:00
if (This == g_mouse_device && g_ddraw.ref && (dwFlags & DISCL_EXCLUSIVE))
{
2023-09-22 00:38:42 +02:00
if (g_mouse_locked || g_config.devmode)
{
while (real_ShowCursor(FALSE) >= 0);
}
2024-03-22 22:27:00 +01:00
InterlockedExchange((LONG*)&g_ddraw.show_cursor_count, -1);
}
2020-10-13 09:20:52 +02:00
return real_did_SetCooperativeLevel(This, hwnd, DISCL_BACKGROUND | DISCL_NONEXCLUSIVE);
}
2021-06-11 20:30:43 +02:00
static HRESULT WINAPI fake_did_GetDeviceData(
IDirectInputDeviceA* This,
DWORD cbObjectData,
LPDIDEVICEOBJECTDATA rgdod,
LPDWORD pdwInOut,
DWORD dwFlags)
2020-10-13 09:20:52 +02:00
{
2024-08-07 04:20:21 +02:00
/*
TRACE(
"DirectInput GetDeviceData(This=%p, cbObjectData=%lu, rgdod=%p, pdwInOut=%p, dwFlags=%lu) [%p]\n",
This,
cbObjectData,
rgdod,
pdwInOut,
dwFlags,
_ReturnAddress());
*/
2021-05-23 14:55:36 +02:00
2020-10-13 09:20:52 +02:00
HRESULT result = real_did_GetDeviceData(This, cbObjectData, rgdod, pdwInOut, dwFlags);
if (SUCCEEDED(result) && This == g_mouse_device && !g_mouse_locked && !g_config.devmode)
2020-10-13 09:20:52 +02:00
{
2021-06-15 02:21:44 +02:00
if (pdwInOut)
{
if (rgdod && *pdwInOut > 0 && cbObjectData > 0)
{
memset(rgdod, 0, *pdwInOut * cbObjectData);
}
*pdwInOut = 0;
}
}
return result;
}
static HRESULT WINAPI fake_did_GetDeviceState(IDirectInputDeviceA* This, DWORD cbData, LPVOID lpvData)
{
2024-08-07 04:20:21 +02:00
//TRACE("DirectInput GetDeviceState(This=%p, cbData=%lu, lpvData=%p) [%p]\n", This, cbData, lpvData, _ReturnAddress());
2021-06-15 02:21:44 +02:00
HRESULT result = real_did_GetDeviceState(This, cbData, lpvData);
if (SUCCEEDED(result) && This == g_mouse_device && !g_mouse_locked && !g_config.devmode)
2021-06-15 02:21:44 +02:00
{
if (cbData > 0 && lpvData)
{
memset(lpvData, 0, cbData);
}
2020-10-13 09:20:52 +02:00
}
return result;
}
2021-06-11 20:30:43 +02:00
static HRESULT WINAPI fake_di_CreateDevice(
IDirectInputA* This,
REFGUID rguid,
LPDIRECTINPUTDEVICEA* lplpDIDevice,
LPUNKNOWN pUnkOuter)
2020-10-13 09:20:52 +02:00
{
2024-06-16 05:37:11 +02:00
TRACE("DirectInput CreateDevice [%p]\n", _ReturnAddress());
2021-05-23 14:55:36 +02:00
2020-10-13 09:20:52 +02:00
HRESULT result = real_di_CreateDevice(This, rguid, lplpDIDevice, pUnkOuter);
if (SUCCEEDED(result))
2020-10-13 09:20:52 +02:00
{
2024-09-16 19:30:42 +02:00
if (rguid)
2021-06-16 22:55:42 +02:00
{
2024-09-16 19:30:42 +02:00
TRACE(" GUID = %08X\n", ((GUID*)rguid)->Data1);
if (IsEqualGUID(&GUID_SysMouse, rguid))
{
g_mouse_device = *lplpDIDevice;
}
2021-06-16 22:55:42 +02:00
}
if (!real_did_SetCooperativeLevel)
{
2024-06-11 02:58:20 +02:00
g_did_SetCooperativeLevel_vtbl_addr = (PROC*)&(*lplpDIDevice)->lpVtbl->SetCooperativeLevel;
real_did_SetCooperativeLevel =
2024-06-11 02:58:20 +02:00
(DIDSETCOOPERATIVELEVELPROC)hook_func(g_did_SetCooperativeLevel_vtbl_addr, (PROC)fake_did_SetCooperativeLevel);
2024-06-11 02:58:20 +02:00
g_did_GetDeviceData_vtbl_addr = (PROC*)&(*lplpDIDevice)->lpVtbl->GetDeviceData;
2020-10-13 09:20:52 +02:00
real_did_GetDeviceData =
2024-06-11 02:58:20 +02:00
(DIDGETDEVICEDATAPROC)hook_func(g_did_GetDeviceData_vtbl_addr, (PROC)fake_did_GetDeviceData);
2024-06-11 02:58:20 +02:00
g_did_GetDeviceState_vtbl_addr = (PROC*)&(*lplpDIDevice)->lpVtbl->GetDeviceState;
2021-06-15 02:21:44 +02:00
real_did_GetDeviceState =
2024-06-11 02:58:20 +02:00
(DIDGETDEVICESTATEPROC)hook_func(g_did_GetDeviceState_vtbl_addr, (PROC)fake_did_GetDeviceState);
}
2020-10-13 09:20:52 +02:00
}
return result;
}
2021-06-11 20:30:43 +02:00
static HRESULT WINAPI fake_di_CreateDeviceEx(
IDirectInputA* This,
REFGUID rguid,
REFIID riid,
LPDIRECTINPUTDEVICEA* lplpDIDevice,
LPUNKNOWN pUnkOuter)
2021-05-23 14:55:36 +02:00
{
2024-06-16 05:37:11 +02:00
TRACE("DirectInput CreateDeviceEx [%p]\n", _ReturnAddress());
2021-05-23 14:55:36 +02:00
HRESULT result = real_di_CreateDeviceEx(This, rguid, riid, lplpDIDevice, pUnkOuter);
if (SUCCEEDED(result))
2021-05-23 14:55:36 +02:00
{
2024-09-16 19:30:42 +02:00
if (rguid)
{
2024-09-16 19:30:42 +02:00
TRACE(" GUID = %08X\n", ((GUID*)rguid)->Data1);
if (IsEqualGUID(&GUID_SysMouse, rguid))
{
g_mouse_device = *lplpDIDevice;
}
}
2021-06-15 02:21:44 +02:00
if (!real_did_SetCooperativeLevel)
{
2024-06-11 02:58:20 +02:00
g_did_SetCooperativeLevel_vtbl_addr = (PROC*)&(*lplpDIDevice)->lpVtbl->SetCooperativeLevel;
real_did_SetCooperativeLevel =
2024-06-11 02:58:20 +02:00
(DIDSETCOOPERATIVELEVELPROC)hook_func(g_did_SetCooperativeLevel_vtbl_addr, (PROC)fake_did_SetCooperativeLevel);
2024-06-11 02:58:20 +02:00
g_did_GetDeviceData_vtbl_addr = (PROC*)&(*lplpDIDevice)->lpVtbl->GetDeviceData;
real_did_GetDeviceData =
2024-06-11 02:58:20 +02:00
(DIDGETDEVICEDATAPROC)hook_func(g_did_GetDeviceData_vtbl_addr, (PROC)fake_did_GetDeviceData);
2024-06-11 02:58:20 +02:00
g_did_GetDeviceState_vtbl_addr = (PROC*)&(*lplpDIDevice)->lpVtbl->GetDeviceState;
real_did_GetDeviceState =
2024-06-11 02:58:20 +02:00
(DIDGETDEVICESTATEPROC)hook_func(g_did_GetDeviceState_vtbl_addr, (PROC)fake_did_GetDeviceState);
}
2021-05-23 14:55:36 +02:00
}
return result;
}
2021-06-11 20:30:43 +02:00
HRESULT WINAPI fake_DirectInputCreateA(
HINSTANCE hinst,
DWORD dwVersion,
LPDIRECTINPUTA* lplpDirectInput,
LPUNKNOWN punkOuter)
2020-10-13 09:20:52 +02:00
{
2024-06-16 05:37:11 +02:00
TRACE("DirectInputCreateA [%p]\n", _ReturnAddress());
2020-10-13 09:20:52 +02:00
2021-05-11 21:45:38 +02:00
if (!real_DirectInputCreateA)
{
real_DirectInputCreateA =
(DIRECTINPUTCREATEAPROC)real_GetProcAddress(real_LoadLibraryA("dinput.dll"), "DirectInputCreateA");
2021-08-02 18:56:06 +02:00
2021-08-02 18:58:28 +02:00
if (real_DirectInputCreateA == fake_DirectInputCreateA)
2021-08-02 18:56:06 +02:00
{
real_DirectInputCreateA =
2023-08-06 09:25:34 +02:00
(DIRECTINPUTCREATEAPROC)real_GetProcAddress(
real_LoadLibraryA("system32\\dinput.dll"),
"DirectInputCreateA");
2021-08-02 18:56:06 +02:00
}
2021-05-11 21:45:38 +02:00
}
2020-10-13 09:20:52 +02:00
if (!real_DirectInputCreateA)
return DIERR_GENERIC;
HRESULT result = real_DirectInputCreateA(hinst, dwVersion, lplpDirectInput, punkOuter);
2023-09-22 00:38:42 +02:00
if (SUCCEEDED(result) && !real_di_CreateDevice && !g_config.no_dinput_hook)
2020-10-13 09:20:52 +02:00
{
2024-06-11 02:58:20 +02:00
g_di_CreateDevice_vtbl_addr = (PROC*)&(*lplpDirectInput)->lpVtbl->CreateDevice;
2020-10-13 09:20:52 +02:00
real_di_CreateDevice =
2024-06-11 02:58:20 +02:00
(DICREATEDEVICEPROC)hook_func(g_di_CreateDevice_vtbl_addr, (PROC)fake_di_CreateDevice);
2020-10-13 09:20:52 +02:00
}
return result;
}
2021-06-11 20:30:43 +02:00
HRESULT WINAPI fake_DirectInputCreateW(
HINSTANCE hinst,
DWORD dwVersion,
LPDIRECTINPUTW* lplpDirectInput,
LPUNKNOWN punkOuter)
2021-05-23 14:55:36 +02:00
{
2024-06-16 05:37:11 +02:00
TRACE("DirectInputCreateW [%p]\n", _ReturnAddress());
2021-05-23 14:55:36 +02:00
if (!real_DirectInputCreateW)
{
real_DirectInputCreateW =
(DIRECTINPUTCREATEWPROC)real_GetProcAddress(real_LoadLibraryA("dinput.dll"), "DirectInputCreateW");
2021-08-02 18:56:06 +02:00
if (real_DirectInputCreateW == fake_DirectInputCreateW)
{
real_DirectInputCreateW =
2023-08-06 09:25:34 +02:00
(DIRECTINPUTCREATEWPROC)real_GetProcAddress(
real_LoadLibraryA("system32\\dinput.dll"),
"DirectInputCreateW");
2021-08-02 18:56:06 +02:00
}
2021-05-23 14:55:36 +02:00
}
if (!real_DirectInputCreateW)
return DIERR_GENERIC;
HRESULT result = real_DirectInputCreateW(hinst, dwVersion, lplpDirectInput, punkOuter);
2023-09-22 00:38:42 +02:00
if (SUCCEEDED(result) && !real_di_CreateDevice && !g_config.no_dinput_hook)
2021-05-23 14:55:36 +02:00
{
2024-06-11 02:58:20 +02:00
g_di_CreateDevice_vtbl_addr = (PROC*)&(*lplpDirectInput)->lpVtbl->CreateDevice;
2021-05-23 14:55:36 +02:00
real_di_CreateDevice =
2024-06-11 02:58:20 +02:00
(DICREATEDEVICEPROC)hook_func(g_di_CreateDevice_vtbl_addr, (PROC)fake_di_CreateDevice);
2021-05-23 14:55:36 +02:00
}
return result;
}
2021-06-11 20:30:43 +02:00
HRESULT WINAPI fake_DirectInputCreateEx(
HINSTANCE hinst,
DWORD dwVersion,
REFIID riidltf,
LPDIRECTINPUT7A* ppvOut,
LPUNKNOWN punkOuter)
2021-05-23 14:55:36 +02:00
{
2024-06-16 05:37:11 +02:00
TRACE("DirectInputCreateEx [%p]\n", _ReturnAddress());
2021-05-23 14:55:36 +02:00
if (!real_DirectInputCreateEx)
{
real_DirectInputCreateEx =
(DIRECTINPUTCREATEEXPROC)real_GetProcAddress(real_LoadLibraryA("dinput.dll"), "DirectInputCreateEx");
2021-08-02 18:56:06 +02:00
if (real_DirectInputCreateEx == fake_DirectInputCreateEx)
{
real_DirectInputCreateEx =
2023-08-06 09:25:34 +02:00
(DIRECTINPUTCREATEEXPROC)real_GetProcAddress(
real_LoadLibraryA("system32\\dinput.dll"),
"DirectInputCreateEx");
2021-08-02 18:56:06 +02:00
}
2021-05-23 14:55:36 +02:00
}
if (!real_DirectInputCreateEx)
return DIERR_GENERIC;
HRESULT result = real_DirectInputCreateEx(hinst, dwVersion, riidltf, ppvOut, punkOuter);
2023-09-22 00:38:42 +02:00
if (SUCCEEDED(result) && !real_di_CreateDevice && !g_config.no_dinput_hook)
2021-05-23 14:55:36 +02:00
{
2024-06-11 02:58:20 +02:00
g_di_CreateDevice_vtbl_addr = (PROC*)&(*ppvOut)->lpVtbl->CreateDevice;
2021-05-23 14:55:36 +02:00
real_di_CreateDevice =
2024-06-11 02:58:20 +02:00
(DICREATEDEVICEPROC)hook_func(g_di_CreateDevice_vtbl_addr, (PROC)fake_di_CreateDevice);
2021-05-23 14:55:36 +02:00
}
2021-06-11 20:30:43 +02:00
if (SUCCEEDED(result) &&
!real_di_CreateDeviceEx &&
riidltf &&
2023-08-02 15:15:44 +02:00
(IsEqualGUID(&IID_IDirectInput7A, riidltf) || IsEqualGUID(&IID_IDirectInput7W, riidltf))
2023-09-22 00:38:42 +02:00
&& !g_config.no_dinput_hook)
2021-05-23 14:55:36 +02:00
{
2024-06-11 02:58:20 +02:00
g_di_CreateDeviceEx_vtbl_addr = (PROC*)&(*ppvOut)->lpVtbl->CreateDeviceEx;
2021-05-23 14:55:36 +02:00
real_di_CreateDeviceEx =
2024-06-11 02:58:20 +02:00
(DICREATEDEVICEEXPROC)hook_func(g_di_CreateDeviceEx_vtbl_addr, (PROC)fake_di_CreateDeviceEx);
2021-05-23 14:55:36 +02:00
}
return result;
}
2021-06-11 20:30:43 +02:00
HRESULT WINAPI fake_DirectInput8Create(
HINSTANCE hinst,
DWORD dwVersion,
REFIID riidltf,
LPDIRECTINPUT8* ppvOut,
LPUNKNOWN punkOuter)
{
2024-06-16 05:37:11 +02:00
TRACE("DirectInput8Create [%p]\n", _ReturnAddress());
2021-05-11 21:45:38 +02:00
if (!real_DirectInput8Create)
{
real_DirectInput8Create =
(DIRECTINPUT8CREATEPROC)real_GetProcAddress(real_LoadLibraryA("dinput8.dll"), "DirectInput8Create");
2021-08-02 18:56:06 +02:00
if (real_DirectInput8Create == fake_DirectInput8Create)
{
real_DirectInput8Create =
2023-08-06 09:25:34 +02:00
(DIRECTINPUT8CREATEPROC)real_GetProcAddress(
real_LoadLibraryA("system32\\dinput8.dll"),
"DirectInput8Create");
2021-08-02 18:56:06 +02:00
}
2021-05-11 21:45:38 +02:00
}
if (!real_DirectInput8Create)
return DIERR_GENERIC;
HRESULT result = real_DirectInput8Create(hinst, dwVersion, riidltf, ppvOut, punkOuter);
2023-09-22 00:38:42 +02:00
if (SUCCEEDED(result) && !real_di_CreateDevice && !g_config.no_dinput_hook)
{
2024-06-11 02:58:20 +02:00
g_di_CreateDevice_vtbl_addr = (PROC*)&(*ppvOut)->lpVtbl->CreateDevice;
real_di_CreateDevice =
2024-06-11 02:58:20 +02:00
(DICREATEDEVICEPROC)hook_func(g_di_CreateDevice_vtbl_addr, (PROC)fake_di_CreateDevice);
}
return result;
}
2023-08-02 17:09:01 +02:00
void dinput_hook_init()
{
#ifdef _MSC_VER
if (!g_dinput_hook_active)
{
g_dinput_hook_active = TRUE;
2023-08-06 09:25:34 +02:00
real_DirectInputCreateA = (void*)real_GetProcAddress(real_LoadLibraryA("dinput.dll"), "DirectInputCreateA");
2023-08-02 17:09:01 +02:00
if (real_DirectInputCreateA && real_DirectInputCreateA != fake_DirectInputCreateA)
{
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourAttach((PVOID*)&real_DirectInputCreateA, (PVOID)fake_DirectInputCreateA);
DetourTransactionCommit();
}
2023-08-02 18:57:34 +02:00
/* Being called from winmm for some reason
2023-08-06 09:25:34 +02:00
real_DirectInputCreateW = (void*)real_GetProcAddress(real_LoadLibraryA("dinput.dll"), "DirectInputCreateW");
2023-08-02 17:09:01 +02:00
if (real_DirectInputCreateW && real_DirectInputCreateW != fake_DirectInputCreateW)
{
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourAttach((PVOID*)&real_DirectInputCreateW, (PVOID)fake_DirectInputCreateW);
DetourTransactionCommit();
}
2023-08-02 18:53:36 +02:00
*/
2023-08-06 09:25:34 +02:00
real_DirectInputCreateEx = (void*)real_GetProcAddress(real_LoadLibraryA("dinput.dll"), "DirectInputCreateEx");
2023-08-02 17:09:01 +02:00
if (real_DirectInputCreateEx && real_DirectInputCreateEx != fake_DirectInputCreateEx)
{
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourAttach((PVOID*)&real_DirectInputCreateEx, (PVOID)fake_DirectInputCreateEx);
DetourTransactionCommit();
}
2023-08-06 09:25:34 +02:00
real_DirectInput8Create = (void*)real_GetProcAddress(real_LoadLibraryA("dinput8.dll"), "DirectInput8Create");
2023-08-02 17:09:01 +02:00
if (real_DirectInput8Create && real_DirectInput8Create != fake_DirectInput8Create)
{
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourAttach((PVOID*)&real_DirectInput8Create, (PVOID)fake_DirectInput8Create);
DetourTransactionCommit();
}
}
#endif
}
void dinput_hook_exit()
{
2024-06-11 02:58:20 +02:00
hook_func(g_di_CreateDevice_vtbl_addr, (PROC)real_di_CreateDevice);
hook_func(g_di_CreateDeviceEx_vtbl_addr, (PROC)real_di_CreateDeviceEx);
hook_func(g_did_SetCooperativeLevel_vtbl_addr, (PROC)real_did_SetCooperativeLevel);
hook_func(g_did_GetDeviceData_vtbl_addr, (PROC)real_did_GetDeviceData);
hook_func(g_did_GetDeviceState_vtbl_addr, (PROC)real_did_GetDeviceState);
2023-08-02 17:09:01 +02:00
#ifdef _MSC_VER
if (g_dinput_hook_active)
{
if (real_DirectInputCreateA)
{
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourDetach((PVOID*)&real_DirectInputCreateA, (PVOID)fake_DirectInputCreateA);
DetourTransactionCommit();
}
2023-08-02 18:53:36 +02:00
/* Being called from winmm for some reason
2023-08-02 17:09:01 +02:00
if (real_DirectInputCreateW)
{
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourDetach((PVOID*)&real_DirectInputCreateW, (PVOID)fake_DirectInputCreateW);
DetourTransactionCommit();
}
2023-08-02 18:53:36 +02:00
*/
2023-08-02 17:09:01 +02:00
if (real_DirectInputCreateEx)
{
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourDetach((PVOID*)&real_DirectInputCreateEx, (PVOID)fake_DirectInputCreateEx);
DetourTransactionCommit();
}
if (real_DirectInput8Create)
{
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourDetach((PVOID*)&real_DirectInput8Create, (PVOID)fake_DirectInput8Create);
DetourTransactionCommit();
}
g_dinput_hook_active = FALSE;
}
#endif
}