2024-04-20 15:57:49 +00:00
/*
* Descent 3
* Copyright ( C ) 2024 Parallax Software
*
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
2024-05-06 15:12:44 +00:00
- - - HISTORICAL COMMENTS FOLLOW - - -
2024-04-16 18:56:40 +00:00
* $ Logfile : / DescentIII / Main / ddio_win / winforcefeedback . cpp $
* $ Revision : 19 $
* $ Date : 4 / 09 / 99 12 : 02 p $
* $ Author : Samir $
*
* Low - level force feedback
*
* $ Log : / DescentIII / Main / ddio_win / winforcefeedback . cpp $
*
2024-04-16 03:43:29 +00:00
* 19 4 / 09 / 99 12 : 02 p Samir
* joystick changes ( Win32 DirectInput support )
2024-04-16 18:56:40 +00:00
*
2024-04-16 03:43:29 +00:00
* 18 3 / 28 / 99 5 : 53 p Jeff
* fixed iforce crashes
2024-04-16 18:56:40 +00:00
*
2024-04-16 03:43:29 +00:00
* 17 3 / 11 / 99 5 : 52 p Jeff
* always compile in immersion support
2024-04-16 18:56:40 +00:00
*
2024-04-16 03:43:29 +00:00
* 16 1 / 31 / 99 11 : 11 p Jeff
* made immersion support as a dynamically loadable dll , and compiled in
* by default
2024-04-16 18:56:40 +00:00
*
2024-04-16 03:43:29 +00:00
* 15 1 / 30 / 99 11 : 27 p Jeff
* added immersion support
2024-04-16 18:56:40 +00:00
*
2024-04-16 03:43:29 +00:00
* 14 1 / 28 / 99 12 : 09 p Jeff
* added force feedback to player shake . . . fixed spelling error in define
* for forcefeedback
2024-04-16 18:56:40 +00:00
*
2024-04-16 03:43:29 +00:00
* 13 11 / 18 / 98 5 : 50 p Jeff
* added some cheap recoil effects for ForceFeedback . . . not fully
* implemented
2024-04-16 18:56:40 +00:00
*
2024-04-16 03:43:29 +00:00
* 12 11 / 10 / 98 5 : 16 p Jeff
* updated forcefeedback system . . . pretty complete now
2024-04-16 18:56:40 +00:00
*
2024-04-16 03:43:29 +00:00
* 11 11 / 06 / 98 7 : 00 p Jeff
* first round of new force feedback installed
2024-04-16 18:56:40 +00:00
*
2024-04-16 03:43:29 +00:00
* 10 11 / 03 / 98 6 : 43 p Jeff
* new low - level & high level Force Feedback system implemented , handles
* window losing focus , etc .
2024-04-16 18:56:40 +00:00
*
2024-04-16 03:43:29 +00:00
* 9 11 / 01 / 98 1 : 58 a Jeff
* converted the vsprintf calls to use the Pvsprintf , which is a safe
* vsprintf , no buffer overflows allowed
2024-04-16 18:56:40 +00:00
*
2024-04-16 03:43:29 +00:00
* 8 10 / 16 / 98 1 : 54 p Kevin
* Changes for Demo Beta 4
2024-04-16 18:56:40 +00:00
*
2024-04-16 03:43:29 +00:00
* 7 10 / 12 / 98 3 : 49 p Jeff
* struct changes
2024-04-16 18:56:40 +00:00
*
2024-04-16 03:43:29 +00:00
* 6 10 / 02 / 98 12 : 04 p Jeff
* fixed forcefeedback bug if it couldn ' t disable autocenter
2024-04-16 18:56:40 +00:00
*
2024-04-16 03:43:29 +00:00
* 5 9 / 22 / 98 3 : 55 p Samir
* ifdef out stuff for non - debug version .
2024-04-16 18:56:40 +00:00
*
2024-04-16 03:43:29 +00:00
* 4 9 / 21 / 98 11 : 10 a Jeff
* general update , new low level , small high level implementation
2024-04-16 18:56:40 +00:00
*
2024-04-16 03:43:29 +00:00
* 3 9 / 18 / 98 7 : 38 p Jeff
* creation of low - level forcefeedback and beginning of high - level
* forcefeedback
2024-04-16 18:56:40 +00:00
*
2024-04-16 03:43:29 +00:00
* 2 9 / 15 / 98 12 : 05 p Jeff
* initial creation of low - level forcefeedback
2024-04-16 18:56:40 +00:00
*
* $ NoKeywords : $
*/
2024-04-16 03:43:29 +00:00
#if 0
2024-04-27 19:30:57 +00:00
# include <cstdarg>
# include <cstdio>
2024-04-16 03:43:29 +00:00
# include <stdlib.h>
# include <math.h>
# include "pserror.h"
# include "mono.h"
# include "ddio.h"
# include "ddio_win.h"
# include "Application.h"
# include "forcefeedback.h"
# include "mem.h"
# include "dinput.h"
//#include "iforce2.h"
void ddio_LoadImmersionDLL ( void ) ;
void ddio_FreeImmersionDLL ( void ) ;
2024-04-28 04:39:29 +00:00
void PrintDirectInputErrorString ( HRESULT hr , const char * format , . . . ) ;
2024-04-16 03:43:29 +00:00
# define DDIO_JOY_COOP_FLAGS (DISCL_EXCLUSIVE | DISCL_BACKGROUND)
bool ddForce_found = false ; //a Force Feedback device was found
bool ddForce_enabled = false ; //Force Feedback is ready and can be used
// Force Feedback Effect Data
// --------------------------
const GUID * effectGUID [ kMaxEffectSubTypes ] = {
& GUID_ConstantForce ,
& GUID_RampForce ,
& GUID_CustomForce ,
// period
& GUID_Square ,
& GUID_Sine ,
& GUID_Triangle ,
& GUID_SawtoothUp ,
& GUID_SawtoothDown ,
// condition
& GUID_Spring ,
& GUID_Damper ,
& GUID_Inertia ,
& GUID_Friction
} ;
2024-05-30 07:35:17 +00:00
union tEffectClasses {
2024-04-16 03:43:29 +00:00
DICONSTANTFORCE constant ;
DIRAMPFORCE ramp ;
DIPERIODIC period ;
DICONDITION condition ;
DICUSTOMFORCE custom ;
2024-05-30 07:35:17 +00:00
} ;
2024-04-16 03:43:29 +00:00
2024-05-30 07:35:17 +00:00
struct tEffect {
2024-04-16 03:43:29 +00:00
DIEFFECT general ;
tEffectClasses specific ;
DIENVELOPE envelope ;
LONG direction [ 2 ] ;
} tddEffect ;
static tddEffect ddEffect [ DDIO_FF_MAXEFFECTS ] ;
// DInput Data
// -----------
static LPDIRECTINPUT DI = NULL ;
static LPDIRECTINPUTDEVICE DID1_Rat = NULL ;
static LPDIRECTINPUTDEVICE DID1_Key = NULL ;
static LPDIRECTINPUTDEVICE2 DID2_Joy [ kMaxJoy ] ;
static LPDIRECTINPUTEFFECT DIE_hEffect [ DDIO_FF_MAXEFFECTS ] ;
// Joystick Data
// -------------
static int maskFFB = 0 ;
static int numJoy = 0 ;
static int numEffects = 0 ;
static char isWheel [ kMaxJoy ] ;
2024-05-24 23:53:40 +00:00
static char joyName [ kMaxJoy ] [ _MAX_PATH ] ;
2024-04-16 03:43:29 +00:00
static DWORD dwAxes [ 2 ] = { DIJOFS_X , DIJOFS_Y } ;
// Private Functions
// -----------------
BOOL CALLBACK FFEnumCallback ( LPCDIDEVICEINSTANCE pdinst , LPVOID pvRef ) ;
static int ddio_ffjoy_AcquireErr ( HRESULT res , int dev_num ) ;
static int ddio_ffjoy_UnacquireErr ( HRESULT res , int dev_num ) ;
static int ddio_ff_SetCoopLevel ( tDevice dev , int coop_level ) ;
int ddio_ffb_Init ( void ) ;
void * getwindowhandle ( void )
{
return DInputData . hwnd ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
= =
= Device & Low - Level Driver Functions =
= =
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
LPDIRECTINPUTDEVICE2 ddio_ff_get_joystick_obj ( tDevice dev )
{
return DID2_Joy [ ( int ) dev ] ;
}
// -------------------------------------------------------------------
// ddio_ff_AttachForce
// Purpose:
// Attaches variables initialized in the general ddio system to
// the force feedback system.
// -------------------------------------------------------------------
void ddio_ff_AttachForce ( void )
{
ddio_LoadImmersionDLL ( ) ;
for ( int i = 0 ; i < kMaxJoy ; i + + )
DID2_Joy [ i ] = NULL ;
maskFFB = 0 ;
numJoy = 0 ;
DI = DInputData . lpdi ;
ddForce_enabled = ddForce_found = false ;
}
// -------------------------------------------------------------------
// ddio_ff_DetachForce
// Purpose:
// Detaches variables used by the force-feedback system from the
// ddio system
// -------------------------------------------------------------------
void ddio_ff_DetachForce ( void )
{
int i ;
if ( DI ) {
// Release All Joystick Devices
// ----------------------------
for ( i = 0 ; i < kMaxJoy ; i + + ) {
if ( DID2_Joy [ i ] ) {
IDirectInputDevice2_Unacquire ( DID2_Joy [ i ] ) ;
IDirectInputDevice2_Release ( DID2_Joy [ i ] ) ;
DID2_Joy [ i ] = NULL ;
}
}
}
DI = NULL ;
DID1_Rat = NULL ;
DID1_Key = NULL ;
numEffects = 0 ;
maskFFB = 0 ;
numJoy = 0 ;
ddForce_enabled = ddForce_found = false ;
ddio_FreeImmersionDLL ( ) ;
}
// -------------------------------------------------------------------
// ddio_ff_Init
// Purpose:
// Initialize force feedback if available.
// -------------------------------------------------------------------
int ddio_ff_Init ( void )
{
if ( ! numJoy )
ddio_ffjoy_Init ( ) ;
if ( maskFFB ) {
int i ;
for ( i = kJoy1 ; i < kMaxJoy ; i + + ) {
if ( DID2_Joy [ i ] & & ( maskFFB & ( 1 < < i ) ) )
IDirectInputDevice2_SendForceFeedbackCommand ( DID2_Joy [ i ] , DISFFC_RESET ) ;
}
ddForce_found = true ;
ddForce_enabled = true ;
}
return maskFFB ;
}
// -------------------------------------------------------------------
// ddio_ffjoy_Init
// Purpose:
// Creates and acquires all joysticks
//
// Input:
// None
//
// Return:
// # of sticks acquired
//
// Description:
//
// -------------------------------------------------------------------
static BOOL ( __stdcall * FF_CB_PTR ) ( LPCDIDEVICEINSTANCE , LPVOID ) ;
int ddio_ffjoy_Init ( void )
{
// Make sure Main DInput OBJ has been created
// ------------------------------------------
if ( ! DI ) {
ddio_ff_Init ( ) ;
if ( ! DI ) {
2024-05-24 15:46:07 +00:00
mprintf ( 0 , " ddio_ffjoy_Init: Dinput not initialized yet \n " ) ;
2024-04-16 03:43:29 +00:00
return FALSE ;
}
}
// Don't do this if it's already successfully been done
// ----------------------------------------------------
if ( ! numJoy ) {
int i ;
FF_CB_PTR = FFEnumCallback ;
DI - > EnumDevices ( DIDEVTYPE_JOYSTICK , FF_CB_PTR , DI , DIEDFL_ATTACHEDONLY ) ;
for ( i = 0 ; i < numJoy ; i + + ) {
ddio_ffjoy_Query ( i , NULL , NULL ) ;
}
}
// Only attempt to acquire if a joy device is present
// --------------------------------------------------
if ( numJoy )
ddio_ff_Acquire ( kMaxJoy ) ;
return ( numJoy ) ;
}
// -------------------------------------------------------------------
// ddio_ff_Acquire
// Purpose:
// Acquires a direct input device for use.
//
// Input:
// The device to acquire (use kDI_MaxJoy to acquire all available
// joysticks).
//
// Return:
// # of devices acquired.
//
// Description:
// Call this to gain access to a device after the device has been
// created & after regaining the focus after losing it.
//
// -------------------------------------------------------------------
int ddio_ff_Acquire ( tDevice dev )
{
int i , cnt = 0 ;
if ( DI ) {
if ( dev = = kAllDevices ) {
cnt + = ddio_ff_Acquire ( kMaxJoy ) ;
return cnt ;
}
switch ( dev ) {
case kMaxJoy :
{
for ( i = 0 ; i < kMaxJoy ; i + + ) {
if ( DID2_Joy [ i ] ) {
if ( ddio_ffjoy_AcquireErr ( IDirectInputDevice2_Acquire ( DID2_Joy [ i ] ) , i ) )
cnt + + ;
}
}
return cnt ;
} break ;
default :
if ( dev > kAllDevices ) {
2024-05-24 15:46:07 +00:00
mprintf ( 0 , " ddio_ff_Acquire: Invalid device ID, out of range \n " ) ;
2024-04-16 03:43:29 +00:00
return 0 ;
} else {
if ( DID2_Joy [ dev ] ) {
int ret = ddio_ffjoy_AcquireErr ( IDirectInputDevice2_Acquire ( DID2_Joy [ dev ] ) , dev ) ;
return ret ;
}
} break ;
}
} else
2024-05-24 15:46:07 +00:00
mprintf ( 0 , " ddio_ff_Acquire: Direct Input object not initialized... \n " ) ;
2024-04-16 03:43:29 +00:00
return cnt ;
}
// -------------------------------------------------------------------
// ddio_ff_Unacquire
// Purpose:
// Unacquires a direct input device
//
// Input:
// The device to unacquire (use kDI_MaxJoy to unacquire all available
// joysticks).
//
// Return:
// # of devices unacquired.
//
// Description:
// Call this to lose access to a device after the device has been
// aquired
//
// -------------------------------------------------------------------
int ddio_ff_Unacquire ( tDevice dev )
{
int i , cnt = 0 ;
if ( DI ) {
if ( dev = = kAllDevices ) {
cnt + = ddio_ff_Unacquire ( kMaxJoy ) ;
return cnt ;
}
switch ( dev ) {
case kMaxJoy :
{
for ( i = 0 ; i < kMaxJoy ; i + + ) {
if ( DID2_Joy [ i ] ) {
if ( ddio_ffjoy_UnacquireErr ( IDirectInputDevice2_Unacquire ( DID2_Joy [ i ] ) , i ) )
cnt + + ;
}
}
return cnt ;
} break ;
default :
if ( dev > kAllDevices ) {
2024-05-24 15:46:07 +00:00
mprintf ( 0 , " ddio_ff_Unacquire: Invalid device ID, out of range \n " ) ;
2024-04-16 03:43:29 +00:00
return 0 ;
} else {
if ( DID2_Joy [ dev ] ) {
int ret = ddio_ffjoy_UnacquireErr ( IDirectInputDevice2_Unacquire ( DID2_Joy [ dev ] ) , dev ) ;
return ret ;
}
} break ;
}
} else
2024-05-24 15:46:07 +00:00
mprintf ( 0 , " ddio_ff_Unacquire: Direct Input object not initialized... \n " ) ;
2024-04-16 03:43:29 +00:00
return cnt ;
}
// -------------------------------------------------------------------
// ddio_ff_SetCoopLevel
// -------------------------------------------------------------------
static int ddio_ff_SetCoopLevel ( tDevice dev , int coop_level )
{
HWND hwin ;
if ( ! ( hwin = ( HWND ) getwindowhandle ( ) ) )
{
2024-05-24 15:46:07 +00:00
mprintf ( 0 , " ddio_ff_SetCoopLevel: couldn't get window handle \n " ) ;
2024-04-16 03:43:29 +00:00
}
// Set a single joystick
// ---------------------
if ( dev < kMaxJoy ) {
if ( DID2_Joy [ dev ] ) {
// Set the cooperative level to share the device
// ---------------------------------------------
if ( IDirectInputDevice2_SetCooperativeLevel ( DID2_Joy [ dev ] , ( HWND ) getwindowhandle ( ) , coop_level ) ! = DI_OK ) {
2024-05-24 15:46:07 +00:00
mprintf ( 0 , " ddio_ff_SetCoopLevel: Could not set dinput device coop level \n " ) ;
2024-04-16 03:43:29 +00:00
return 0 ;
}
}
} else // Set all single joysticks
if ( dev = = kMaxJoy ) {
int i ;
for ( i = kJoy1 ; i < kMaxJoy ; i + + ) {
ddio_ff_SetCoopLevel ( ( tDevice ) i , coop_level ) ;
}
}
return 1 ;
}
// -------------------------------------------------------------------
// DIEnumJoysticks_Callback
// Purpose:
// Initialize all connected joysticks.
//
// Input:
// pdinst info about the current joystick being enumed.
// pvRef the direct input object that was passed in before
// starting the enum process.
// Return:
// DIENUM_CONTINUE continue calling us with any more devices
// DIENUM_STOP all done, don't call us back anymore, go away.
// -------------------------------------------------------------------
BOOL CALLBACK FFEnumCallback ( LPCDIDEVICEINSTANCE pdinst , LPVOID pvRef )
{
LPDIRECTINPUT pdi = ( LPDIRECTINPUT ) pvRef ;
LPDIRECTINPUTDEVICE pdev ;
if ( DIDEVTYPEJOYSTICK_WHEEL = = GET_DIDEVICE_SUBTYPE ( pdinst - > dwDevType ) )
isWheel [ numJoy ] = TRUE ;
else
isWheel [ numJoy ] = FALSE ;
// Create an instance of the device
// --------------------------------
if ( IDirectInput_CreateDevice ( pdi , pdinst - > guidInstance , & pdev , NULL ) ! = DI_OK ) {
2024-05-24 15:46:07 +00:00
mprintf ( 0 , " DIEnumJoysticks_Callback: Could not create dinput device obj \n " ) ;
2024-04-16 03:43:29 +00:00
return DIENUM_CONTINUE ;
}
// Set the data format to the default
// ----------------------------------
if ( IDirectInputDevice_SetDataFormat ( pdev , & c_dfDIJoystick ) ! = DI_OK ) {
2024-05-24 15:46:07 +00:00
mprintf ( 0 , " DIEnumJoysticks_Callback: Could not set dinput device data format \n " ) ;
2024-04-16 03:43:29 +00:00
IDirectInputDevice_Unacquire ( pdev ) ;
IDirectInputDevice_Release ( pdev ) ;
return DIENUM_CONTINUE ;
}
// Get the DID2 from DID1
// ----------------------
if ( IDirectInputDevice_QueryInterface ( pdev , IID_IDirectInputDevice2 , ( void * * ) & DID2_Joy [ numJoy ] ) ! = DI_OK ) {
2024-05-24 15:46:07 +00:00
mprintf ( 0 , " DIEnumJoysticks_Callback: QueryInterface did not return DI_OK \n " ) ;
2024-04-16 03:43:29 +00:00
IDirectInputDevice_Unacquire ( pdev ) ;
IDirectInputDevice_Release ( pdev ) ;
return DIENUM_CONTINUE ;
}
// Set the cooperative level
// -------------------------
if ( ! ddio_ff_SetCoopLevel ( ( tDevice ) numJoy , DDIO_JOY_COOP_FLAGS ) ) {
2024-05-24 15:46:07 +00:00
mprintf ( 0 , " DIEnumJoysticks_Callback: Could not set dinput coop level \n " ) ;
2024-04-16 03:43:29 +00:00
return FALSE ;
}
// Done with Device1
// -----------------
IDirectInputDevice_Unacquire ( pdev ) ;
IDirectInputDevice_Release ( pdev ) ;
// Device was added successfully
// -----------------------------
numJoy + + ;
return DIENUM_CONTINUE ;
}
// -------------------------------------------------------------------
// ddio_ffjoy_Query
// Purpose:
// Besides checking what buttons/axis are available, this function
// also checks for force feedback support.
// -------------------------------------------------------------------
int ddio_ffjoy_Query ( int dev , int * but_flags , int * axis_flags )
{
2024-05-24 02:51:16 +00:00
uint32_t i , bit ;
2024-04-16 03:43:29 +00:00
DIDEVCAPS DICaps ;
DIDEVICEOBJECTINSTANCE DIObjInst ;
DWORD DIAxisOFS [ 6 ] = { DIJOFS_X ,
DIJOFS_Y ,
DIJOFS_Z ,
DIJOFS_RX ,
DIJOFS_SLIDER ( 0 ) ,
DIJOFS_SLIDER ( 1 ) } ;
// Make sure Main DInput OBJ has been created
// ------------------------------------------
if ( ! DI ) {
ddio_ff_Init ( ) ;
if ( ! DI ) {
2024-05-24 15:46:07 +00:00
mprintf ( 0 , " ddio_ffjoy_Query: Dinput not initialized yet \n " ) ;
2024-04-16 03:43:29 +00:00
return FALSE ;
}
}
if ( ! DID2_Joy [ dev ] ) {
2024-05-24 15:46:07 +00:00
mprintf ( 0 , " ddio_ffjoy_Query: device not found #%d \n " , dev ) ;
2024-04-16 03:43:29 +00:00
return 0 ;
}
if ( ! numJoy ) {
if ( but_flags ) * but_flags = 0 ;
if ( axis_flags ) * axis_flags = 0 ;
return 0 ;
}
ddio_ff_Acquire ( ( tDevice ) dev ) ;
DICaps . dwSize = sizeof ( DIDEVCAPS ) ;
if ( IDirectInputDevice2_GetCapabilities ( DID2_Joy [ dev ] , & DICaps ) ! = DI_OK ) {
2024-05-24 15:46:07 +00:00
mprintf ( 0 , " ddio_ffjoy_Query: Failed getting device caps \n " ) ;
2024-04-16 03:43:29 +00:00
return 0 ;
}
if ( DICaps . dwFlags & DIDC_FORCEFEEDBACK ) {
2024-05-24 15:46:07 +00:00
mprintf ( 0 , " ddio_ff_joy_Query: ffb support \n " ) ;
2024-04-16 03:43:29 +00:00
maskFFB | = 1 < < dev ;
}
DIObjInst . dwSize = sizeof ( DIDEVICEOBJECTINSTANCE ) ;
// Get the Axis flags
// ------------------
if ( but_flags ) {
* but_flags = 0 ;
for ( i = 0 , bit = 0 ; i < DICaps . dwButtons & & bit < 32 ; bit + + ) {
if ( IDirectInputDevice2_GetObjectInfo ( DID2_Joy [ dev ] , & DIObjInst , DIJOFS_BUTTON ( bit ) , DIPH_BYOFFSET ) = = DI_OK ) {
* but_flags | = 1 < < bit ;
+ + i ;
}
}
}
// Get the Axis flags
// ------------------
if ( axis_flags ) {
* axis_flags = 0 ;
for ( i = 0 , bit = 0 ; i < DICaps . dwAxes & & bit < 8 ; + + bit ) {
if ( IDirectInputDevice2_GetObjectInfo ( DID2_Joy [ dev ] , & DIObjInst , DIAxisOFS [ bit ] , DIPH_BYOFFSET ) = = DI_OK ) {
* axis_flags | = 1 < < bit ;
+ + i ;
}
}
}
return 1 ;
}
// -------------------------------------------------------------------
// ddio_ffjoy_EnableAutoCenter
// Purpose:
// Disables/Enables the autocentering of the joystick
// -------------------------------------------------------------------
void ddio_ffjoy_EnableAutoCenter ( tDevice dev , bool enable )
{
DIPROPDWORD DIPropAutoCenter ;
DIPropAutoCenter . diph . dwSize = sizeof ( DIPropAutoCenter ) ;
DIPropAutoCenter . diph . dwHeaderSize = sizeof ( DIPROPHEADER ) ;
DIPropAutoCenter . diph . dwObj = 0 ;
DIPropAutoCenter . diph . dwHow = DIPH_DEVICE ;
DIPropAutoCenter . dwData = ( enable ) ? DIPROPAUTOCENTER_ON : DIPROPAUTOCENTER_OFF ;
if ( dev > = kMaxJoy ) {
return ;
}
if ( DID2_Joy [ dev ] & & ( maskFFB & 1 < < dev ) ) {
ddio_ff_Unacquire ( dev ) ;
HRESULT hr = DID2_Joy [ dev ] - > SetProperty ( DIPROP_AUTOCENTER , & DIPropAutoCenter . diph ) ;
ddio_ff_Acquire ( dev ) ;
if ( FAILED ( hr ) ) {
PrintDirectInputErrorString ( hr , " Force: Failed to change autocenter property. " ) ;
}
}
}
// -------------------------------------------------------------------
// ddio_ffjoy_SetGain
// Purpose:
// Sets the gain for joystick, pass a value of 0-1
// -------------------------------------------------------------------
void ddio_ffjoy_SetGain ( tDevice dev , float value )
{
DIPROPDWORD DIPropAutoCenter ;
DIPropAutoCenter . diph . dwSize = sizeof ( DIPropAutoCenter ) ;
DIPropAutoCenter . diph . dwHeaderSize = sizeof ( DIPROPHEADER ) ;
DIPropAutoCenter . diph . dwObj = 0 ;
DIPropAutoCenter . diph . dwHow = DIPH_DEVICE ;
DIPropAutoCenter . dwData = ( DWORD ) ( value * 10000.0f ) ;
if ( dev > = kMaxJoy ) {
return ;
}
if ( DID2_Joy [ dev ] & & ( maskFFB & 1 < < dev ) ) {
HRESULT hr = DID2_Joy [ dev ] - > SetProperty ( DIPROP_FFGAIN , & DIPropAutoCenter . diph ) ;
if ( FAILED ( hr ) ) {
PrintDirectInputErrorString ( hr , " Force: Failed to set gain. " ) ;
}
}
}
// -------------------------------------------------------------------
// ddio_ffjoy_IsAutoCentered
// Purpose:
// Returns true if the joystick is set for autocentering
// -------------------------------------------------------------------
bool ddio_ffjoy_IsAutoCentered ( tDevice dev )
{
DIPROPDWORD DIPropAutoCenter ;
DIPropAutoCenter . diph . dwSize = sizeof ( DIPropAutoCenter ) ;
DIPropAutoCenter . diph . dwHeaderSize = sizeof ( DIPROPHEADER ) ;
DIPropAutoCenter . diph . dwObj = 0 ;
DIPropAutoCenter . diph . dwHow = DIPH_DEVICE ;
if ( dev > = kMaxJoy ) {
return false ;
}
if ( DID2_Joy [ dev ] & & ( maskFFB & 1 < < dev ) ) {
ddio_ff_Unacquire ( dev ) ;
HRESULT hr = DID2_Joy [ dev ] - > GetProperty ( DIPROP_AUTOCENTER , & DIPropAutoCenter . diph ) ;
ddio_ff_Acquire ( dev ) ;
if ( FAILED ( hr ) ) {
PrintDirectInputErrorString ( hr , " Force: Failed to get autocenter property " ) ;
return false ;
}
return ( DIPropAutoCenter . dwData ) ? true : false ;
}
return false ;
}
// -------------------------------------------------------------------
// ddio_ffjoy_SupportAutoCenter
// Purpose:
// Returns true if the FF joystick supports auto centering
// -------------------------------------------------------------------
bool ddio_ffjoy_SupportAutoCenter ( tDevice dev )
{
if ( dev > = kMaxJoy ) {
return false ;
}
if ( DID2_Joy [ dev ] & & ( maskFFB & 1 < < dev ) ) {
//check this device
bool set = ddio_ffjoy_IsAutoCentered ( dev ) ;
DIPROPDWORD DIPropAutoCenter ;
DIPropAutoCenter . diph . dwSize = sizeof ( DIPropAutoCenter ) ;
DIPropAutoCenter . diph . dwHeaderSize = sizeof ( DIPROPHEADER ) ;
DIPropAutoCenter . diph . dwObj = 0 ;
DIPropAutoCenter . diph . dwHow = DIPH_DEVICE ;
DIPropAutoCenter . dwData = ( set ) ? DIPROPAUTOCENTER_ON : DIPROPAUTOCENTER_OFF ;
ddio_ff_Unacquire ( dev ) ;
HRESULT hr = DID2_Joy [ dev ] - > SetProperty ( DIPROP_AUTOCENTER , & DIPropAutoCenter . diph ) ;
ddio_ff_Acquire ( dev ) ;
switch ( hr ) {
case DI_OK :
case S_FALSE :
return true ;
default :
PrintDirectInputErrorString ( hr , " " ) ;
return false ;
}
}
return false ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
= =
= Force Feedback Effect Functions =
= =
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
// -------------------------------------------------------------------
// ddio_ff_GetInfo
// Purpose:
// Returns information about the current state of the low-level
// Force Feedback system.
// -------------------------------------------------------------------
void ddio_ff_GetInfo ( bool * ff_found , bool * ff_enabled )
{
if ( ddForce_found ) {
if ( ff_found )
* ff_found = true ;
if ( ff_enabled )
* ff_enabled = ddForce_enabled ;
} else {
if ( ff_found )
* ff_found = false ;
if ( ff_enabled )
* ff_enabled = false ;
}
}
// -------------------------------------------------------------------
// ddio_ffb_Pause
// Purpose:
// Pause the FFB output on the given device. Use ddio_ffb_Continue to
// continue where you left off.
// -------------------------------------------------------------------
void ddio_ffb_Pause ( tDevice dev )
{
if ( dev = = kMaxJoy ) {
int i ;
for ( i = 0 ; i < kMaxJoy ; i + + ) {
ddio_ffb_Pause ( ( tDevice ) i ) ;
}
return ;
}
if ( DID2_Joy [ dev ] & & ( maskFFB & 1 < < dev ) )
IDirectInputDevice2_SendForceFeedbackCommand ( DID2_Joy [ dev ] , DISFFC_PAUSE ) ;
}
// -------------------------------------------------------------------
// ddio_ffb_Continue
// Purpose:
// Unpause the FFB output on the given device. Complimentary to
// ddio_ffb_Pause.
// -------------------------------------------------------------------
void ddio_ffb_Continue ( tDevice dev )
{
if ( dev = = kMaxJoy ) {
int i ;
for ( i = 0 ; i < kMaxJoy ; i + + ) {
ddio_ffb_Continue ( ( tDevice ) i ) ;
}
return ;
}
if ( DID2_Joy [ dev ] & & ( maskFFB & 1 < < dev ) )
IDirectInputDevice2_SendForceFeedbackCommand ( DID2_Joy [ dev ] , DISFFC_CONTINUE ) ;
}
// -------------------------------------------------------------------
// ddio_ffb_Enable
// Purpose:
// Must be called after initialization in order to activate the
// device.
// Use ddio_ffb_Pause & ddio_ffb_Continue if you want disable forces
// temporarily and resume later.
// -------------------------------------------------------------------
void ddio_ffb_Enable ( tDevice dev )
{
if ( DID2_Joy [ dev ] & & ( maskFFB & 1 < < dev ) ) {
IDirectInputDevice2_SendForceFeedbackCommand ( DID2_Joy [ dev ] , DISFFC_SETACTUATORSON ) ;
ddForce_enabled = true ;
}
}
// -------------------------------------------------------------------
// ddio_ffb_Disable
// Purpose:
// Turns off FFB, but effects still play on processor.
// -------------------------------------------------------------------
void ddio_ffb_Disable ( tDevice dev )
{
if ( DID2_Joy [ dev ] & & ( maskFFB & 1 < < dev ) ) {
ddForce_enabled = false ;
IDirectInputDevice2_SendForceFeedbackCommand ( DID2_Joy [ dev ] , DISFFC_SETACTUATORSOFF ) ;
}
}
// -------------------------------------------------------------------
// ddio_ffb_DestroyAll
// Purpose:
// Destroys all created effects
// -------------------------------------------------------------------
void ddio_ffb_DestroyAll ( void )
{
numEffects = 0 ;
}
// -------------------------------------------------------------------
// ddio_ffb_effectCreate
// Purpose:
// Create a single effect for future playback.
// Effect is given a logical ID
// -------------------------------------------------------------------
int ddio_ffb_effectCreate ( tDevice dev , tFFB_Effect * eff )
{
HRESULT hr ;
if ( numEffects > = DDIO_FF_MAXEFFECTS ) {
2024-05-24 15:46:07 +00:00
mprintf ( 0 , " ddio_ffb_effectCreate: Reached hardcoded limit for # of effects. \n " ) ;
2024-04-16 03:43:29 +00:00
return - 1 ;
}
if ( ! ( DID2_Joy [ dev ] & & ( maskFFB & 1 < < dev ) ) )
return ( - 1 ) ;
// Important stuff
ddEffect [ numEffects ] . general . dwDuration = eff - > Duration ;
ddEffect [ numEffects ] . general . dwSamplePeriod = eff - > Period ;
ddEffect [ numEffects ] . general . dwGain = eff - > Gain ;
ddEffect [ numEffects ] . general . dwTriggerButton = ( eff - > Trigger = = kNoButton ? DIEB_NOTRIGGER : DIJOFS_BUTTON ( eff - > Trigger ) ) ;
ddEffect [ numEffects ] . general . dwTriggerRepeatInterval = eff - > TriggerRepeatTime ;
ddEffect [ numEffects ] . direction [ 0 ] = eff - > Direction ;
memcpy ( & ddEffect [ numEffects ] . specific , & eff - > TypeInfo , sizeof ( tEffectClasses ) ) ;
// COM related
ddEffect [ numEffects ] . general . dwSize = sizeof ( DIEFFECT ) ;
ddEffect [ numEffects ] . general . cAxes = isWheel [ dev ] ? 1 : 2 ;
ddEffect [ numEffects ] . general . rgdwAxes = & dwAxes [ 0 ] ;
ddEffect [ numEffects ] . general . dwFlags = DIEFF_POLAR | DIEFF_OBJECTOFFSETS ;
ddEffect [ numEffects ] . general . lpvTypeSpecificParams = & ddEffect [ numEffects ] . specific ;
ddEffect [ numEffects ] . general . rglDirection = ( LONG * ) ddEffect [ numEffects ] . direction ;
ddEffect [ numEffects ] . direction [ 1 ] = 0L ;
ddEffect [ numEffects ] . general . lpEnvelope = NULL ;
if ( eff - > Flags & FF_USEENVELOPE ) {
ddEffect [ numEffects ] . envelope . dwSize = sizeof ( DIENVELOPE ) ;
ddEffect [ numEffects ] . envelope . dwAttackLevel = eff - > Envelope . AttackLevel ;
ddEffect [ numEffects ] . envelope . dwAttackTime = eff - > Envelope . AttackTime ;
ddEffect [ numEffects ] . envelope . dwFadeLevel = eff - > Envelope . FadeLevel ;
ddEffect [ numEffects ] . envelope . dwFadeTime = eff - > Envelope . FadeTime ;
ddEffect [ numEffects ] . general . lpEnvelope = & ddEffect [ numEffects ] . envelope ;
}
switch ( eff - > Type ) {
case kConstant :
ddEffect [ numEffects ] . general . cbTypeSpecificParams = sizeof ( DICONSTANTFORCE ) ;
break ;
case kRamp :
ddEffect [ numEffects ] . general . cbTypeSpecificParams = sizeof ( DIRAMPFORCE ) ;
break ;
case kCustom :
ddEffect [ numEffects ] . general . cbTypeSpecificParams = sizeof ( DICUSTOMFORCE ) ;
break ;
case kWave_Square :
case kWave_Sine :
case kWave_Triangle :
case kWave_SawUp :
case kWave_SawDown :
ddEffect [ numEffects ] . general . cbTypeSpecificParams = sizeof ( DIPERIODIC ) ;
break ;
case kCondition_Spring :
case kCondition_Damper :
case kCondition_Inertia :
case kCondition_Friction :
ddEffect [ numEffects ] . general . cbTypeSpecificParams = sizeof ( DICONDITION ) ;
break ;
default :
2024-05-24 15:46:07 +00:00
mprintf ( 0 , " ddio_ffb_effectCreate: bad effect subType \n " ) ;
2024-04-16 03:43:29 +00:00
return - 1 ;
}
hr = IDirectInputDevice2_CreateEffect ( DID2_Joy [ dev ] ,
* effectGUID [ eff - > Type ] ,
( LPCDIEFFECT ) & ddEffect [ numEffects ] ,
( LPDIRECTINPUTEFFECT * ) & DIE_hEffect [ numEffects ] , 0 ) ;
switch ( hr ) {
case DI_OK :
ddio_ffb_effectUnload ( numEffects ) ;
numEffects + + ;
return ( numEffects - 1 ) ;
case DIERR_DEVICENOTREG :
2024-05-24 15:46:07 +00:00
mprintf ( 0 , " ddio_ffb_effectCreate: effect not created, DIERR_DEVICENOTREG \n " ) ;
2024-04-16 03:43:29 +00:00
return - 1 ;
case DIERR_DEVICEFULL :
2024-05-24 15:46:07 +00:00
mprintf ( 0 , " ddio_ffb_effectCreate: effect not created, DIERR_DEVICEFULL \n " ) ;
2024-04-16 03:43:29 +00:00
return - 1 ;
default :
PrintDirectInputErrorString ( hr , " Force Effect Create " ) ;
break ;
}
return - 1 ;
}
// -------------------------------------------------------------------
// ddio_ffb_effectPlay
// Purpose:
// Play an effect that was previously created.
// -------------------------------------------------------------------
2024-05-24 03:27:12 +00:00
void ddio_ffb_effectPlay ( int16_t eID )
2024-04-16 03:43:29 +00:00
{
if ( eID < 0 | | eID > = DDIO_FF_MAXEFFECTS ) {
Int3 ( ) ; //invalid eID
return ;
}
bool try_again = true ;
ff_try :
HRESULT hr = IDirectInputEffect_Start ( DIE_hEffect [ eID ] , 1 , 0 ) ;
if ( FAILED ( hr ) ) {
PrintDirectInputErrorString ( hr , " Force Play " ) ;
if ( try_again ) {
ddio_ff_Acquire ( kMaxJoy ) ;
try_again = false ;
goto ff_try ;
}
}
}
// -------------------------------------------------------------------
// ddio_ffb_effectStop
// Purpose:
// Stop a single effect.
// -------------------------------------------------------------------
2024-05-24 03:27:12 +00:00
void ddio_ffb_effectStop ( int16_t eID )
2024-04-16 03:43:29 +00:00
{
if ( eID < 0 | | eID > = DDIO_FF_MAXEFFECTS ) {
Int3 ( ) ; //invalid eID
return ;
}
IDirectInputEffect_Stop ( DIE_hEffect [ eID ] ) ;
}
// -------------------------------------------------------------------
// ddio_ffb_effectStopAll
// Purpose:
// Stops all forces on the given device.
// -------------------------------------------------------------------
void ddio_ffb_effectStopAll ( tDevice dev )
{
if ( DID2_Joy [ dev ] & & ( maskFFB & 1 < < dev ) )
IDirectInputDevice2_SendForceFeedbackCommand ( DID2_Joy [ dev ] , DISFFC_STOPALL ) ;
}
// -------------------------------------------------------------------
// ddio_ffb_effectUnload
// Purpose:
// Unload a single effect... Necessary to make room for other
// effects.
// -------------------------------------------------------------------
2024-05-24 03:27:12 +00:00
void ddio_ffb_effectUnload ( int16_t eID )
2024-04-16 03:43:29 +00:00
{
if ( eID < 0 | | eID > = DDIO_FF_MAXEFFECTS ) {
Int3 ( ) ; //invalid eID
return ;
}
IDirectInputEffect_Unload ( DIE_hEffect [ eID ] ) ;
}
// -------------------------------------------------------------------
// ddio_ffb_effectModify
// Purpose:
// Modifies a single effect, only if the given parameters are
// different from what's currently loaded.
// -------------------------------------------------------------------
2024-05-24 03:27:12 +00:00
void ddio_ffb_effectModify ( int16_t eID , int * Direction , uint32_t * Duration , uint32_t * Gain , uint32_t * Period , tEffInfo * TypeInfo , tEffEnvelope * Envelope )
2024-04-16 03:43:29 +00:00
{
2024-05-24 02:51:16 +00:00
uint32_t flags = 0 ;
2024-04-16 03:43:29 +00:00
//return;
if ( Direction ) {
if ( ddEffect [ eID ] . direction [ 0 ] ! = * Direction ) {
ddEffect [ eID ] . direction [ 0 ] = * Direction ;
flags | = DIEP_DIRECTION ;
}
}
if ( Duration ) {
if ( ddEffect [ eID ] . general . dwDuration ! = * Duration ) {
ddEffect [ eID ] . general . dwDuration = * Duration ;
flags | = DIEP_DURATION ;
}
}
if ( Gain ) {
if ( ddEffect [ eID ] . general . dwGain ! = * Gain ) {
ddEffect [ eID ] . general . dwGain = * Gain ;
flags | = DIEP_GAIN ;
}
}
if ( Period ) {
if ( ddEffect [ eID ] . general . dwSamplePeriod ! = * Period ) {
ddEffect [ eID ] . general . dwSamplePeriod = * Period ;
flags | = DIEP_SAMPLEPERIOD ;
}
}
if ( TypeInfo ) {
if ( ! memcmp ( & ddEffect [ eID ] . specific , TypeInfo , ddEffect [ eID ] . general . cbTypeSpecificParams ) ) {
memcpy ( & ddEffect [ eID ] . specific , TypeInfo , ddEffect [ eID ] . general . cbTypeSpecificParams ) ;
flags | = DIEP_TYPESPECIFICPARAMS ;
}
}
if ( Envelope ) {
if ( ddEffect [ eID ] . envelope . dwAttackLevel ! = Envelope - > AttackLevel | |
ddEffect [ eID ] . envelope . dwAttackTime ! = Envelope - > AttackTime | |
ddEffect [ eID ] . envelope . dwFadeLevel ! = Envelope - > FadeLevel | |
ddEffect [ eID ] . envelope . dwFadeTime ! = Envelope - > FadeTime ) {
ddEffect [ eID ] . envelope . dwAttackLevel = Envelope - > AttackLevel ;
ddEffect [ eID ] . envelope . dwAttackTime = Envelope - > AttackTime ;
ddEffect [ eID ] . envelope . dwFadeLevel = Envelope - > FadeLevel ;
ddEffect [ eID ] . envelope . dwFadeTime = Envelope - > FadeTime ;
flags | = DIEP_ENVELOPE ;
}
}
if ( ! flags )
return ;
IDirectInputEffect_SetParameters ( DIE_hEffect [ eID ] , ( LPCDIEFFECT ) & ddEffect [ eID ] , flags ) ;
}
// -------------------------------------------------------------------
// ddio_ffb_GetEffectData
// Purpose:
// Retrieves affect data for the given parameters, pass NULL for those you don't want
// -------------------------------------------------------------------
2024-05-24 03:27:12 +00:00
void ddio_ffb_GetEffectData ( int16_t eID , int * Direction , uint32_t * Duration , uint32_t * Gain , uint32_t * Period , tEffInfo * TypeInfo , tEffEnvelope * Envelope )
2024-04-16 03:43:29 +00:00
{
if ( Direction ) {
* Direction = ddEffect [ eID ] . direction [ 0 ] ;
}
if ( Duration ) {
* Duration = ddEffect [ eID ] . general . dwDuration ;
}
if ( Gain ) {
* Gain = ddEffect [ eID ] . general . dwGain ;
}
if ( Period ) {
* Period = ddEffect [ eID ] . general . dwSamplePeriod ;
}
if ( TypeInfo ) {
memcpy ( TypeInfo , & ddEffect [ eID ] . specific , ddEffect [ eID ] . general . cbTypeSpecificParams ) ;
}
if ( Envelope ) {
Envelope - > AttackLevel = ddEffect [ eID ] . envelope . dwAttackLevel ;
Envelope - > AttackTime = ddEffect [ eID ] . envelope . dwAttackTime ;
Envelope - > FadeLevel = ddEffect [ eID ] . envelope . dwFadeLevel ;
Envelope - > FadeTime = ddEffect [ eID ] . envelope . dwFadeTime ;
}
}
//====================================================================
// Private Fuctions
//====================================================================
// -------------------------------------------------------------------
// ddio_ffjoy_AcquireErr
// Purpose:
// Handle success/err reporting
// -------------------------------------------------------------------
static int ddio_ffjoy_AcquireErr ( HRESULT res , int dev_num )
{
if ( res = = DI_OK ) {
2024-05-24 15:46:07 +00:00
mprintf ( 0 , " device #%d acquired \n " , dev_num ) ;
2024-04-16 03:43:29 +00:00
return TRUE ;
}
else
if ( res = = S_FALSE ) {
2024-05-24 15:46:07 +00:00
mprintf ( 0 , " device #%d already acquired \n " , dev_num ) ;
2024-04-16 03:43:29 +00:00
return TRUE ;
}
else
if ( res = = DIERR_INVALIDPARAM ) {
2024-05-24 15:46:07 +00:00
mprintf ( 0 , " device %d DIERR_INVALIDPARAM \n " , dev_num ) ;
2024-04-16 03:43:29 +00:00
return FALSE ;
}
else
if ( res = = DIERR_OTHERAPPHASPRIO ) {
2024-05-24 15:46:07 +00:00
mprintf ( 0 , " device %d DIERR_OTHERAPPHASPRIO \n " , dev_num ) ;
2024-04-16 03:43:29 +00:00
return TRUE ;
}
else
2024-05-24 15:46:07 +00:00
mprintf ( 0 , " Unknown Error acquiring device %d \n " , dev_num ) ;
2024-04-16 03:43:29 +00:00
return FALSE ;
}
// -------------------------------------------------------------------
// ddio_ffjoy_UnacquireErr
// Purpose:
// Handle success/err reporting
// -------------------------------------------------------------------
static int ddio_ffjoy_UnacquireErr ( HRESULT res , int dev_num )
{
if ( res = = DI_OK ) {
2024-05-24 15:46:07 +00:00
mprintf ( 0 , " device #%d unacquired \n " , dev_num ) ;
2024-04-16 03:43:29 +00:00
return TRUE ;
}
else
if ( res = = S_FALSE ) {
2024-05-24 15:46:07 +00:00
mprintf ( 0 , " device #%d already unacquired \n " , dev_num ) ;
2024-04-16 03:43:29 +00:00
return TRUE ;
}
else
if ( res = = DIERR_INVALIDPARAM ) {
2024-05-24 15:46:07 +00:00
mprintf ( 0 , " device %d DIERR_INVALIDPARAM \n " , dev_num ) ;
2024-04-16 03:43:29 +00:00
return FALSE ;
}
else
if ( res = = DIERR_OTHERAPPHASPRIO ) {
2024-05-24 15:46:07 +00:00
mprintf ( 0 , " device %d DIERR_OTHERAPPHASPRIO \n " , dev_num ) ;
2024-04-16 03:43:29 +00:00
return TRUE ;
}
else
2024-05-24 15:46:07 +00:00
mprintf ( 0 , " Unknown Error unacquiring device %d \n " , dev_num ) ;
2024-04-16 03:43:29 +00:00
return FALSE ;
}
/*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Immersion
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*/
#if 0
# pragma message("Compiling Immersion Support.")
HMODULE ImmersionHandle = NULL ;
typedef HIFORCEPROJECT ( __stdcall * IFLoadProjectFile_fp ) ( LPCSTR pProjectFileName , LPDIRECTINPUTDEVICE2A pDevice ) ;
IFLoadProjectFile_fp d_IFLoadProjectFile = NULL ;
typedef BOOL ( __stdcall * IFReleaseProject_fp ) ( HIFORCEPROJECT hProject ) ;
IFReleaseProject_fp d_IFReleaseProject = NULL ;
typedef LPDIRECTINPUTEFFECT * ( __stdcall * IFCreateEffects_fp ) ( HIFORCEPROJECT hProject , LPCSTR pObjectName , int * pNumEffects ) ;
IFCreateEffects_fp d_IFCreateEffects = NULL ;
void ddio_LoadImmersionDLL ( void )
{
if ( ImmersionHandle ! = NULL ) {
ddio_FreeImmersionDLL ( ) ;
}
ImmersionHandle = LoadLibrary ( " IForce2.dll " ) ;
if ( ImmersionHandle = = NULL ) {
//not found
2024-05-24 15:46:07 +00:00
mprintf ( 0 , " Force: Unable to find Immersion DLL \n " ) ;
2024-04-16 03:43:29 +00:00
return ;
}
//get functions
d_IFLoadProjectFile = ( IFLoadProjectFile_fp ) GetProcAddress ( ImmersionHandle , " _IFLoadProjectFile@8 " ) ;
d_IFReleaseProject = ( IFReleaseProject_fp ) GetProcAddress ( ImmersionHandle , " _IFReleaseProject@4 " ) ;
d_IFCreateEffects = ( IFCreateEffects_fp ) GetProcAddress ( ImmersionHandle , " _IFCreateEffects@12 " ) ;
if ( ! d_IFLoadProjectFile | |
! d_IFReleaseProject | |
! d_IFCreateEffects ) {
2024-05-24 15:46:07 +00:00
mprintf ( 0 , " Force: Unable to bind Immersion functions \n " ) ;
2024-04-16 03:43:29 +00:00
ddio_FreeImmersionDLL ( ) ;
return ;
}
2024-05-24 15:46:07 +00:00
mprintf ( 0 , " Immersion IFORCE2 DLL loaded successfully \n " ) ;
2024-04-16 03:43:29 +00:00
}
void ddio_FreeImmersionDLL ( void )
{
if ( ImmersionHandle ) {
FreeLibrary ( ImmersionHandle ) ;
ImmersionHandle = NULL ;
d_IFLoadProjectFile = NULL ;
d_IFReleaseProject = NULL ;
d_IFCreateEffects = NULL ;
}
}
// Given a filename resource, this loads the file and creates a resource
// for it. It returns a handle to that resource.
// If it returns NULL, then it couldn't load the project.
// Make sure device is aquired before calling.
FORCEPROJECT ddio_ForceLoadProject ( char * filename , tDevice dev )
{
if ( ! d_IFLoadProjectFile ) {
return NULL ;
}
if ( dev < 0 | | dev > kMaxJoy ) {
2024-05-24 15:46:07 +00:00
mprintf ( 0 , " illegal device id passed to ddio_ForceLoadProject() - %d \n " , ( int ) dev ) ;
2024-04-16 03:43:29 +00:00
return NULL ;
}
HIFORCEPROJECT prj = d_IFLoadProjectFile ( filename , DID2_Joy [ dev ] ) ;
return ( FORCEPROJECT ) prj ;
}
// Unloads a FORCEPROJECT file
void ddio_ForceUnloadProject ( FORCEPROJECT prj )
{
if ( ! d_IFReleaseProject )
return ;
if ( ! prj )
return ;
d_IFReleaseProject ( ( HIFORCEPROJECT ) prj ) ;
}
// Given a handle to a resource, and the name of the effect to load
// it will load that effect. Returns the effect ID, or -1 if it couldn't
// be created
2024-04-28 04:39:29 +00:00
int ddio_CreateForceFromProject ( FORCEPROJECT project , const char * forcename )
2024-04-16 03:43:29 +00:00
{
if ( ! d_IFCreateEffects ) {
return - 1 ;
}
LPDIRECTINPUTEFFECT * gppdie ;
HIFORCEPROJECT prj ;
if ( ! project )
return - 1 ;
prj = ( HIFORCEPROJECT ) project ;
gppdie = d_IFCreateEffects ( prj , forcename , NULL ) ;
if ( ! gppdie ) {
return - 1 ;
}
DIE_hEffect [ numEffects ] = * gppdie ;
numEffects + + ;
return ( numEffects - 1 ) ;
}
# else
# pragma message("Not Compiling For Immersion Support.")
// Given a filename resource, this loads the file and creates a resource
// for it. It returns a handle to that resource.
// If it returns NULL, then it couldn't load the project.
// Make sure device is aquired before calling.
FORCEPROJECT ddio_ForceLoadProject ( char * filename , tDevice dev )
{
return NULL ;
}
// Unloads a FORCEPROJECT file
void ddio_ForceUnloadProject ( FORCEPROJECT prj )
{
return ;
}
// Given a handle to a resource, and the name of the effect to load
// it will load that effect. Returns the effect ID, or -1 if it couldn't
// be created
2024-04-28 04:39:29 +00:00
int ddio_CreateForceFromProject ( FORCEPROJECT project , const char * forcename )
2024-04-16 03:43:29 +00:00
{
return - 1 ;
}
void ddio_LoadImmersionDLL ( void )
{
}
void ddio_FreeImmersionDLL ( void )
{
}
# endif
//==========================================================
// Function: PrintDirectInputErrorString
//
// Description:
// Prints a debug message(s) of errors based on error
// code passed in.
//
//==========================================================
2024-04-28 04:39:29 +00:00
void PrintDirectInputErrorString ( HRESULT hr , const char * format , . . . )
2024-04-16 03:43:29 +00:00
{
# ifdef RELEASE
return ;
# endif
char buffer [ 2048 ] ;
2024-04-27 19:30:57 +00:00
std : : va_list marker ;
2024-04-16 03:43:29 +00:00
va_start ( marker , format ) ;
2024-04-27 19:30:57 +00:00
std : : vsnprintf ( buffer , 2048 , format , marker ) ;
2024-04-16 03:43:29 +00:00
va_end ( marker ) ;
strcat ( buffer , " : \n " ) ;
if ( hr = = S_FALSE ) {
strcat ( buffer , " *S_FALSE \n " ) ;
2024-05-24 15:46:07 +00:00
mprintf ( 0 , buffer ) ;
2024-04-16 03:43:29 +00:00
return ;
}
if ( hr = = DI_BUFFEROVERFLOW )
strcat ( buffer , " *The device buffer overflowed and some input was lost. This value is equal to the S_FALSE standard COM return value. \n " ) ;
if ( hr = = DI_DOWNLOADSKIPPED )
strcat ( buffer , " *The parameters of the effect were successfully updated, but the effect could not be downloaded because the associated device was not acquired in exclusive mode. \n " ) ;
if ( hr = = DI_EFFECTRESTARTED )
strcat ( buffer , " *The effect was stopped, the parameters were updated, and the effect was restarted. \n " ) ;
if ( hr = = DI_NOEFFECT )
strcat ( buffer , " *The operation had no effect. This value is equal to the S_FALSE standard COM return value. \n " ) ;
if ( hr = = DI_NOTATTACHED )
strcat ( buffer , " *The device exists but is not currently attached. This value is equal to the S_FALSE standard COM return value. \n " ) ;
if ( hr = = DI_OK )
strcat ( buffer , " *The operation completed successfully. This value is equal to the S_OK standard COM return value. \n " ) ;
if ( hr = = DI_POLLEDDEVICE )
strcat ( buffer , " *The device is a polled device. As a result, device buffering will not collect any data and event notifications will not be signaled until the IDirectInputDevice2::Poll method is called. \n " ) ;
if ( hr = = DI_PROPNOEFFECT )
strcat ( buffer , " *The change in device properties had no effect. This value is equal to the S_FALSE standard COM return value. \n " ) ;
if ( hr = = DI_TRUNCATED )
strcat ( buffer , " *The parameters of the effect were successfully updated, but some of them were beyond the capabilities of the device and were truncated to the nearest supported value. \n " ) ;
if ( hr = = DI_TRUNCATEDANDRESTARTED )
strcat ( buffer , " *Equal to DI_EFFECTRESTARTED | DI_TRUNCATED. \n " ) ;
if ( hr = = DIERR_ACQUIRED )
strcat ( buffer , " *The operation cannot be performed while the device is acquired. \n " ) ;
if ( hr = = DIERR_ALREADYINITIALIZED )
strcat ( buffer , " *This object is already initialized \n " ) ;
if ( hr = = DIERR_BADDRIVERVER )
strcat ( buffer , " *The object could not be created due to an incompatible driver version or mismatched or incomplete driver components. \n " ) ;
if ( hr = = DIERR_BETADIRECTINPUTVERSION )
strcat ( buffer , " *The application was written for an unsupported prerelease version of DirectInput. \n " ) ;
if ( hr = = DIERR_DEVICEFULL )
strcat ( buffer , " *The device is full. \n " ) ;
if ( hr = = DIERR_DEVICENOTREG )
strcat ( buffer , " *The device or device instance is not registered with DirectInput. This value is equal to the REGDB_E_CLASSNOTREG standard COM return value. \n " ) ;
if ( hr = = DIERR_EFFECTPLAYING )
strcat ( buffer , " *The parameters were updated in memory but were not downloaded to the device because the device does not support updating an effect while it is still playing. \n " ) ;
if ( hr = = DIERR_HASEFFECTS )
strcat ( buffer , " *The device cannot be reinitialized because there are still effects attached to it. \n " ) ;
if ( hr = = DIERR_GENERIC )
strcat ( buffer , " *An undetermined error occurred inside the DirectInput subsystem. This value is equal to the E_FAIL standard COM return value. \n " ) ;
if ( hr = = DIERR_HANDLEEXISTS )
strcat ( buffer , " *The device already has an event notification associated with it. This value is equal to the E_ACCESSDENIED standard COM return value. \n " ) ;
if ( hr = = DIERR_INCOMPLETEEFFECT )
strcat ( buffer , " *The effect could not be downloaded because essential information is missing. For example, no axes have been associated with the effect, or no type-specific information has been supplied. \n " ) ;
if ( hr = = DIERR_INPUTLOST )
strcat ( buffer , " *Access to the input device has been lost. It must be reacquired. \n " ) ;
if ( hr = = DIERR_INVALIDPARAM )
strcat ( buffer , " *An invalid parameter was passed to the returning function, or the object was not in a state that permitted the function to be called. This value is equal to the E_INVALIDARG standard COM return value. \n " ) ;
if ( hr = = DIERR_MOREDATA )
strcat ( buffer , " *Not all the requested information fitted into the buffer. \n " ) ;
if ( hr = = DIERR_NOAGGREGATION )
strcat ( buffer , " *This object does not support aggregation. \n " ) ;
if ( hr = = DIERR_NOINTERFACE )
strcat ( buffer , " *The specified interface is not supported by the object. This value is equal to the E_NOINTERFACE standard COM return value. \n " ) ;
if ( hr = = DIERR_NOTACQUIRED )
strcat ( buffer , " *The operation cannot be performed unless the device is acquired. \n " ) ;
if ( hr = = DIERR_NOTBUFFERED )
strcat ( buffer , " *The device is not buffered. Set the DIPROP_BUFFERSIZE property to enable buffering. \n " ) ;
if ( hr = = DIERR_NOTDOWNLOADED )
strcat ( buffer , " *The effect is not downloaded. \n " ) ;
if ( hr = = DIERR_NOTEXCLUSIVEACQUIRED )
strcat ( buffer , " *The operation cannot be performed unless the device is acquired in DISCL_EXCLUSIVE mode. \n " ) ;
if ( hr = = DIERR_NOTFOUND )
strcat ( buffer , " *The requested object does not exist. \n " ) ;
if ( hr = = DIERR_NOTINITIALIZED )
strcat ( buffer , " *This object has not been initialized. \n " ) ;
if ( hr = = DIERR_OBJECTNOTFOUND )
strcat ( buffer , " *The requested object does not exist. \n " ) ;
if ( hr = = DIERR_OLDDIRECTINPUTVERSION )
strcat ( buffer , " *The application requires a newer version of DirectInput. \n " ) ;
if ( hr = = DIERR_OTHERAPPHASPRIO )
strcat ( buffer , " *Another application has a higher priority level, preventing this call from succeeding. This value is equal to the E_ACCESSDENIED standard COM return value. This error can be returned when an application has only foreground access to a device but is attempting to acquire the device while in the background. \n " ) ;
if ( hr = = DIERR_OUTOFMEMORY )
strcat ( buffer , " *The DirectInput subsystem couldn't allocate sufficient memory to complete the call. This value is equal to the E_OUTOFMEMORY standard COM return value. \n " ) ;
if ( hr = = DIERR_READONLY )
strcat ( buffer , " *The specified property cannot be changed. This value is equal to the E_ACCESSDENIED standard COM return value. \n " ) ;
//@@if(hr==DIERR_REPORTFULL)
//@@ strcat(buffer,"*More information was requested to be sent than can be sent to the device.\n");
//@@if(hr==DIERR_UNPLUGGED)
//@@ strcat(buffer,"*The operation could not be completed because the device is not plugged in.\n");
if ( hr = = DIERR_UNSUPPORTED )
strcat ( buffer , " *The function called is not supported at this time. This value is equal to the E_NOTIMPL standard COM return value. \n " ) ;
if ( hr = = E_PENDING )
strcat ( buffer , " *Data is not yet available. \n " ) ;
2024-05-24 15:46:07 +00:00
mprintf ( 0 , buffer ) ;
2024-04-16 03:43:29 +00:00
}
# else
# include "forcefeedback.h"
2024-04-16 18:56:40 +00:00
void ddio_ff_GetInfo ( bool * ff_found , bool * ff_enabled ) {
if ( ff_found ) {
* ff_found = false ;
}
2024-04-16 03:43:29 +00:00
2024-04-16 18:56:40 +00:00
if ( ff_enabled ) {
* ff_enabled = false ;
}
2024-04-16 03:43:29 +00:00
}
2024-04-16 18:56:40 +00:00
void ddio_ffb_Pause ( tDevice dev ) { }
2024-04-16 03:43:29 +00:00
2024-04-16 18:56:40 +00:00
void ddio_ffb_Continue ( tDevice dev ) { }
2024-04-16 03:43:29 +00:00
2024-04-16 18:56:40 +00:00
int ddio_ff_Acquire ( tDevice dev ) { return 0 ; }
2024-04-16 03:43:29 +00:00
2024-04-16 18:56:40 +00:00
void ddio_ffjoy_EnableAutoCenter ( tDevice dev , bool enable ) { }
2024-04-16 03:43:29 +00:00
2024-04-16 18:56:40 +00:00
void ddio_ffjoy_SetGain ( tDevice dev , float value ) { }
2024-04-16 03:43:29 +00:00
2024-04-16 18:56:40 +00:00
bool ddio_ffjoy_SupportAutoCenter ( tDevice dev ) { return false ; }
2024-04-16 03:43:29 +00:00
2024-04-16 18:56:40 +00:00
void ddio_ffb_DestroyAll ( void ) { }
2024-04-16 03:43:29 +00:00
2024-05-24 03:27:12 +00:00
void ddio_ffb_effectPlay ( int16_t eID ) { }
2024-04-16 03:43:29 +00:00
2024-05-24 03:27:12 +00:00
void ddio_ffb_effectModify ( int16_t eID , int * Direction , uint32_t * Duration , uint32_t * Gain , uint32_t * Period ,
2024-04-16 18:56:40 +00:00
tEffInfo * TypeInfo , tEffEnvelope * Envelope ) { }
2024-04-16 03:43:29 +00:00
2024-04-16 18:56:40 +00:00
int ddio_ffb_effectCreate ( tDevice dev , tFFB_Effect * eff ) { return - 1 ; }
2024-04-16 03:43:29 +00:00
2024-04-28 04:39:29 +00:00
int ddio_CreateForceFromProject ( FORCEPROJECT project , const char * forcename ) { return - 1 ; }
2024-04-16 03:43:29 +00:00
2024-04-16 18:56:40 +00:00
void ddio_ForceUnloadProject ( FORCEPROJECT prj ) { }
2024-04-16 03:43:29 +00:00
2024-04-16 18:56:40 +00:00
FORCEPROJECT ddio_ForceLoadProject ( char * filename , tDevice dev ) { return NULL ; }
2024-04-16 03:43:29 +00:00
2024-04-16 18:56:40 +00:00
int ddio_ffjoy_Init ( void ) { return 0 ; }
2024-04-16 03:43:29 +00:00
2024-04-16 18:56:40 +00:00
int ddio_ff_Init ( void ) { return 0 ; }
2024-04-16 03:43:29 +00:00
2024-04-16 18:56:40 +00:00
void ddio_ff_AttachForce ( void ) { }
2024-04-16 03:43:29 +00:00
2024-04-16 18:56:40 +00:00
void ddio_ff_DetachForce ( void ) { }
LPDIRECTINPUTDEVICE2 ddio_ff_get_joystick_obj ( tDevice dev ) { return NULL ; }
2024-04-16 03:43:29 +00:00
# endif