qboolean FFConfigParser::ParseSetDevices( const char **pos, TDevice &device ) { qboolean result = qboolean( pos != NULL ); if ( pos ) { char *token = COM_ParseExt( pos, qtrue ); if ( token[ 0 ] == '{' ) { for ( token = COM_ParseExt( pos, qtrue ) ; token[ 0 ] && token[ 0 ] != '}' && result // fail if any problem ; token = COM_ParseExt( pos, qtrue ) ){ device.insert( token ); } result = qboolean( token[ 0 ] != 0 ); } else { // expected '{' result = qfalse; } } return result; }
////----------------------- /// MultiEffect::ChangeGain //--------------------------- // Analogous to CImmEffect::ChangeGain. Changes gain of all component effects. // Returns false if any effect returns false. Attempts to change gain of all effects // regardless of individual return values. // qboolean MultiEffect::ChangeGain( DWORD Gain ) { DWORD CurrentGain; qboolean result = GetGain( CurrentGain ); if ( result ) { DWORD RelativeGain = Gain - CurrentGain; for ( int i = 0, max = GetNumberOfContainedEffects() ; i < max ; i++ ) { CImmEffect* pIE = GetContainedEffect( i ); result &= qboolean ( pIE && pIE->GetGain( CurrentGain ) && pIE->ChangeGain( CurrentGain + RelativeGain ) ); } result &= qboolean( max > 0 ); } return result; }
qboolean MultiEffect::ChangeStartDelay( DWORD StartDelay ) { DWORD CurrentStartDelay; qboolean result = GetStartDelay( CurrentStartDelay ); if ( result ) { DWORD RelativeStartDelay = StartDelay - CurrentStartDelay; for ( int i = 0, max = GetNumberOfContainedEffects() ; i < max ; i++ ) { CImmEffect* pIE = GetContainedEffect( i ); result &= qboolean ( pIE && pIE->GetStartDelay( CurrentStartDelay ) && pIE->ChangeStartDelay( CurrentStartDelay + RelativeStartDelay ) ); } result &= qboolean( max > 0 ); } return result; }
////--------------------------- /// MultiEffect::ChangeDuration //------------------------------- // Analogous to CImmEffect::ChangeDuration. Changes duration of all component effects. // Returns false if any effect returns false. Attempts to change duration of all effects // regardless of individual return values. // qboolean MultiEffect::ChangeDuration( DWORD Duration ) { DWORD CurrentDuration; qboolean result = GetDuration( CurrentDuration ); if ( result ) { DWORD RelativeDuration = Duration - CurrentDuration; for ( int i = 0, max = GetNumberOfContainedEffects() ; i < max ; i++ ) { IMM_ENVELOPE Envelope = {0}; CImmEffect* pIE = GetContainedEffect( i ); result &= qboolean ( pIE && pIE->GetDuration( CurrentDuration ) && pIE->ChangeDuration( CurrentDuration + RelativeDuration ) && ( !pIE->GetEnvelope( &Envelope ) || ( Envelope.dwAttackTime = ( CurrentDuration ? (DWORD)((float)Envelope.dwAttackTime * (float)Duration / (float)CurrentDuration) : 0 ) , Envelope.dwFadeTime = ( CurrentDuration ? (DWORD)((float)Envelope.dwFadeTime * (float)Duration / (float)CurrentDuration) : 0 ) , pIE->ChangeEnvelope( &Envelope ) ) ) ); } result &= qboolean( max > 0 ); } return result; }
qboolean FFConfigParser::ParseSetIncludes( const char **pos, TInclude &include ) { qboolean result = qboolean( pos != NULL ); if ( pos ) { char *token = COM_ParseExt( pos, qtrue ); if ( token[ 0 ] == '{' ) { for ( token = COM_ParseExt( pos, qtrue ) ; token[ 0 ] && token[ 0 ] != '}' && result // fail if any problem ; token = COM_ParseExt( pos, qtrue ) ){ include.push_back( token ); } result = qboolean( token[ 0 ] != 0 ); } else { // expected '{' result = qfalse; } } return result; }
////--------------------------------- /// FFConfigParser::ParseDefaultBlock //------------------------------------- // // // Parameters: // // Returns: // qboolean FFConfigParser::ParseDefault( const char **pos, TDeviceType &defaultSet ) { qboolean result = qboolean( pos != NULL ); if ( pos ) { char *token = COM_ParseExt( pos, qtrue ); if ( token[ 0 ] == '{' ) { for ( token = COM_ParseExt( pos, qtrue ) ; token[ 0 ] && token[ 0 ] != '}' && result // fail if any problem ; token = COM_ParseExt( pos, qtrue ) ){ int device = 0; if ( sscanf( token, "%d", &device ) ) { string &str = defaultSet[ device ]; if ( !str.size() ) { str = COM_ParseExt( pos, qfalse ); result &= qboolean( str.size() > 0 ); } else { result = qfalse; #ifdef FF_PRINT ConsoleParseError ( "Redefinition of DeviceType index" , token ); #endif } } else { result = qfalse; #ifdef FF_PRINT ConsoleParseError ( "DeviceType field should begin with an integer" , token ); #endif } } } } return result; }
////--------------------- /// FFConfigParser::Parse //------------------------- // // // Parameters: // // Returns: // qboolean FFConfigParser::Parse( void *file ) { qboolean result = qboolean( file != NULL ); if ( file ) { const char *token = 0, *pos = (const char*)file; for ( token = COM_ParseExt( &pos, qtrue ) ; token[ 0 ] && result // fail if any problem ; token = COM_ParseExt( &pos, qtrue ) ){ if ( !stricmp( token, "ffdefaults" ) ) { result &= ParseDefaults( &pos ); } else if ( !stricmp( token, "ffsets" ) ) { result &= ParseSets( &pos ); } else { // unexpected field result = qfalse; } } FS_FreeFile( file ); } return result; }
////------------------------- /// FFConfigParser::ParseSets //----------------------------- // // // Parameters: // // Returns: // qboolean FFConfigParser::ParseSets( const char **pos ) { qboolean result = qboolean( pos != NULL ); string groupName; if ( pos ) { char *token = COM_ParseExt( pos, qtrue ); if ( token[ 0 ] == '{' ) { for ( token = COM_ParseExt( pos, qtrue ) ; token[ 0 ] && token[ 0 ] != '}' && result // fail if any problem ; token = COM_ParseExt( pos, qtrue ) ){ TData &data = mMap[ token ]; result &= ParseSet( pos, data ); } } else { // expected '{' result = qfalse; } } return result; }
qboolean IsLocalClient( int clientNum ) { return qboolean ( clientNum == 0 //clientNum == cl.snap.ps.clientNum || clientNum == FF_CLIENT_LOCAL // assumed local ); }
qboolean Update( void ) const { return qboolean ( mRefs != 0 && (ChannelCompound*)mHandle ); }
////-------------------------- /// MultiEffect::GetStartDelay //------------------------------ // Determines the shortest start delay. // qboolean MultiEffect::GetStartDelay( DWORD &StartDelay ) { StartDelay = MAXDWORD; qboolean result = qtrue; for ( int i = 0, max = GetNumberOfContainedEffects() ; i < max ; i++ ) { DWORD CurrentStartDelay; CImmEffect* pIE = GetContainedEffect( i ); if ( pIE && pIE->GetStartDelay( CurrentStartDelay ) ) { StartDelay = Min( StartDelay, CurrentStartDelay ); } else { result = qfalse; } } return qboolean ( result && max > 0 ); }
////------------------------ /// MultiEffect::GetDelayEnd //---------------------------- // Computes end of earliest start delay. Compare this value with ::GetTickCount() // to determine if any component waveform started playing on the device. // qboolean MultiEffect::GetDelayEnd( DWORD &DelayEnd ) { DelayEnd = MAXDWORD; qboolean result = qtrue; for ( int i = 0, max = GetNumberOfContainedEffects() ; i < max ; i++ ) { DWORD StartDelay; CImmEffect* pIE = GetContainedEffect( i ); if ( pIE && pIE->GetStartDelay( StartDelay ) ) { DelayEnd = Min( DelayEnd, StartDelay + pIE->m_dwLastStarted ); } else { result = qfalse; } } return qboolean ( result && max > 0 ); }
qboolean MultiEffect::GetGain( DWORD &Gain ) { Gain = 0; qboolean result = qtrue; for ( int i = 0, max = GetNumberOfContainedEffects() ; i < max ; i++ ) { DWORD CurrentGain; CImmEffect* pIE = GetContainedEffect( i ); if ( pIE && pIE->GetGain( CurrentGain ) ) { Gain = Max( Gain, CurrentGain ); } else { result = qfalse; } } return qboolean ( result && max > 0 ); }
////---------------------- /// MultiEffect::GetStatus //-------------------------- // Analogous to CImmEffect::GetStatus. ORs all status flags from all component effects. // Returns false if any effect returns false. Attempts to get status of all effects // regardless of individual return values. // qboolean MultiEffect::GetStatus( DWORD &Status ) { Status = 0; qboolean result = qtrue; for ( int i = 0, max = GetNumberOfContainedEffects() ; i < max ; i++ ) { DWORD CurrentStatus; CImmEffect* pIE = GetContainedEffect( i ); if ( pIE && pIE->GetStatus( &CurrentStatus ) ) { Status |= CurrentStatus; } else { result = qfalse; } } return qboolean ( result && max > 0 ); }
////------------------- /// MultiCompound::Stop //----------------------- // Analogous to CImmCompoundEffect::Stop. Stops all contained compound effects. // Returns false if any effect returns false. // qboolean MultiCompound::Stop() { qboolean result = qtrue; for ( Set::iterator itSet = mSet.begin() ; itSet != mSet.end() ; itSet++ ){ result &= qboolean( (*itSet)->Stop() ); } return qboolean ( result && mSet.size() != 0 ); }
////---------------------------- /// FFConfigParser::ParseDefault //-------------------------------- // // // Parameters: // // Returns: // qboolean FFConfigParser::ParseDefaults( const char **pos ) { qboolean result = qboolean( pos != NULL ); if ( pos ) { char *token = COM_ParseExt( pos, qtrue ); if ( token[ 0 ] == '{' ) { for ( token = COM_ParseExt( pos, qtrue ) ; token[ 0 ] && token[ 0 ] != '}' && result // fail if any problem ; token = COM_ParseExt( pos, qtrue ) ){ int techType = 0; if ( sscanf( token, "%d", &techType ) ) { TDeviceType &deviceType = mDefaultSet[ techType ]; if ( !deviceType.size() ) { result &= ParseDefault( pos, deviceType ); mDefaultPriority.push_back( techType ); } else { result = qfalse; #ifdef FF_PRINT ConsoleParseError ( "Redefinition of TechType index" , token ); #endif } } else { result = qfalse; #ifdef FF_PRINT ConsoleParseError ( "TechType fields should begin with integers" , token ); #endif } } } else { // expected '{' result = qfalse; } } return result; }
////-------- /// FF_Shake //------------ // Shake the mouse (play the special "shake" effect) at a given strength // for a given duration. The shake effect can be a compound containing // multiple component effects, but each component effect's magnitude and // duration will be set to the parameters passed in this function. // // Parameters: // * intensity [0..10000] - Magnitude of effect // * duration [0..MAXINT] - Length of shake in milliseconds // // Returns: // - qtrue: shake started // - qfalse: shake did not start // qboolean FF_Shake(int intensity, int duration) { char message[64]; message[0] = 0; sprintf( message, "intensity=%d, duration=%d", intensity, duration ); FF_PROLOGUE_NOQUOTE( FF_Shake, message ); FF_RESULT( gFFSystem.Shake( intensity, duration, qboolean( ensureShake->integer != qfalse ) ) ); FF_EPILOGUE; }
qboolean CG_ConfigForce( int index, const char *&name, int &channel ) { qboolean result = qfalse; const char *configstring = CG_ConfigString( CS_FORCES + index ); result = qboolean ( configstring && sscanf( configstring, "%d", &channel ) == 1 ); if ( result ) { name = strchr( configstring, ',' ) + 1; result = qboolean( name != (char *)1 ); } return result; }
////---------------------- /// FFChannelSet::Register //-------------------------- // // Assumptions: // * 'compound' is empty of effects and contains the desired channel prior to entry. // // Parameters: // * compound: its channel parameter is an input. its effect set is filled with registered // - effects. 'compound' should not contain any effects prior to this function call. // * name: effect name to register in each FFSet on the channel // * create: qtrue if FFSet should create the effect, qfalse if it should just look it up. // qboolean FFChannelSet::Register( ChannelCompound &compound, const char *name, qboolean create ) { for ( ChannelIterator itChannel( mChannel, compound.GetChannel() ) ; itChannel != mChannel.end() ; ++itChannel ){ MultiEffect *Effect; Effect = mSet[ (**itChannel).second ]->Register( name, create ); if ( Effect ) compound.GetSet().insert( Effect ); } return qboolean( compound.GetSet().size() != 0 ); }
qboolean FFConfigParser::ParseSet( const char **pos, TData &data ) { qboolean result = qboolean( pos != NULL ); if ( pos ) { const char *oldpos = *pos; // allows set declarations with no attributes to have no "{}" char *token = COM_ParseExt( pos, qtrue ); if ( token[ 0 ] == '{' ) { for ( token = COM_ParseExt( pos, qtrue ) ; token[ 0 ] && token[ 0 ] != '}' && result // fail if any problem ; token = COM_ParseExt( pos, qtrue ) ){ if ( !stricmp( token, "includes" ) ) { result &= ParseSetIncludes( pos, data.include ); } else if ( !stricmp( token, "devices" ) ) { result &= ParseSetDevices( pos, data.device ); } else { result = qfalse; #ifdef FF_PRINT ConsoleParseError ( "Invalid set parameter. Should be 'includes' or 'devices'" , token ); #endif } } } else { // expected '{' (no longer expected!) //result = qfalse; (no longer an error!) *pos = oldpos; } } return result; }
////------------------------- /// MultiCompound::ChangeGain //----------------------------- // Changes gain of all compounds. // Returns false if any effect returns false. // qboolean MultiCompound::ChangeGain( DWORD Gain ) { qboolean result = qtrue; for ( Set::iterator itSet = mSet.begin() ; itSet != mSet.end() ; itSet++ ){ result &= (*itSet)->ChangeGain( Gain ); } return qboolean ( result && mSet.size() != 0 ); }
////---------------------------- /// MultiCompound::EnsurePlaying //-------------------------------- // Starts any contained compound effects if they are not currently playing. // Returns false if any effect returns false or any are playing. // qboolean MultiCompound::EnsurePlaying() { qboolean result = qtrue; if ( !IsPlaying() ) { for ( Set::iterator itSet = mSet.begin() ; itSet != mSet.end() ; itSet++ ){ result &= (*itSet)->Start(); } } return qboolean ( result && mSet.size() != 0 ); }
////-------------------------- /// MultiCompound::operator == //------------------------------ // Returns qtrue if the sets are EXACTLY equal, including order. This is not good // in general. (Fix me) // qboolean MultiCompound::operator == ( MultiCompound &compound ) { Set &other = compound.mSet; qboolean result = qfalse; if ( mSet.size() == other.size() ) { for ( Set::iterator itSet = mSet.begin(), itOther = other.begin() ; itSet != mSet.end() //&& itOther != other.end() // assumed since mSet.size() == other.size() && (*itSet) == (*itOther) ; itSet++, itOther++ ); result = qboolean( itSet == mSet.end() ); } return result; }
////---------------- /// FFMultiSet::Init //-------------------- // Initializes all attached force feedback devices. An empty FFSet is created // for each device. Each device will have its own copy of whatever .ifr file // set 'config' specifies. // // Always pair with clear() // qboolean FFMultiSet::Init( FFSystem::Config &config ) { mConfig = &config; #ifdef FF_PRINT Com_Printf( "Feedback devices:\n" ); #endif HINSTANCE hInstance = (HINSTANCE)g_wv.hInstance; HWND hWnd = (HWND)g_wv.hWnd; mDevices = new CImmDevices; if ( mDevices && mDevices->CreateDevices( hInstance, hWnd ) ) { for ( int i = 0 ; i < mDevices->GetNumDevices() ; i++ ){ FFSet *ffSet = NULL; ffSet = new FFSet( config, mDevices->GetDevice( i ) ); if ( ffSet ) { #ifdef FF_PRINT char ProductName[ FF_MAX_PATH ]; *ProductName = 0; mDevices->GetDevice( i )->GetProductName( ProductName, FF_MAX_PATH - 1 ); Com_Printf( "%d) %s\n", i, ProductName ); #endif mSet.push_back( ffSet ); } } } return qboolean( mSet.size() ); }
////-------------------- /// FFConfigParser::Init //------------------------ // Reads the force feedback configuration file. Call this once after the device // is initialized. // // Parameters: // * filename // // Returns: // * qtrue - the effects set directory has been set according to the initialized // device. (See base/fffx/fffx.cfg) // * qfalse - no effects set could be determined for this device. // qboolean FFConfigParser::Init( const char *filename ) { Clear(); // Always cleanup return qboolean( filename && Parse( LoadFile( filename ) ) ); }