/***************************************************************************** ** ** ** Neversoft Entertainment ** ** ** ** Copyright (C) 1999 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: SYS Library ** ** ** ** Module: SYS (SYS_) ** ** ** ** File name: siodev.cpp ** ** ** ** Created: 05/26/2000 - spg ** ** ** ** Description: Generic input device ** ** ** *****************************************************************************/ /***************************************************************************** ** Includes ** *****************************************************************************/ #include #include #include #include #include //#include #include #include "sys/ngc/p_hwpad.h" // PJR - Doh! extern PADStatus padData[PAD_MAX_CONTROLLERS]; // game pad state /***************************************************************************** ** Externals ** *****************************************************************************/ /***************************************************************************** ** DBG Defines ** *****************************************************************************/ namespace SIO { // Maps Xbox thumbstick values in the range [-32767, 32767] to PS2 thumstick values in the range [0, 255]. #define XboxThumbToPS2Thumb( v ) (( v + 32767 ) / 256 ) /***************************************************************************** ** Private Types ** *****************************************************************************/ /***************************************************************************** ** Private Data ** *****************************************************************************/ /***************************************************************************** ** Public Data ** *****************************************************************************/ /***************************************************************************** ** Private Prototypes ** *****************************************************************************/ /***************************************************************************** ** Private Functions ** *****************************************************************************/ /******************************************************************/ /* */ /* */ /******************************************************************/ void Device::process( void ) { switch( m_state ) { case vIDLE: break; case vBUSY: wait(); break; case vACQUIRING: acquisition_pending(); break; case vACQUIRED: read_data(); break; default: break; } } /******************************************************************/ /* */ /* */ /******************************************************************/ void Device::wait( void ) { Dbg_MsgAssert(( m_next_state >= 0 ) && ( m_next_state <= vNUM_STATES ),( "No next state set for wait state" )); if ( get_status() != vNOTREADY ) { m_state = m_next_state; m_next_state = vIDLE; } } //int gButton_Option = 0; /******************************************************************/ /* */ /* */ /******************************************************************/ void Device::read_data( void ) { PADStatus * p; // Need to check here for the controller becoming detached. if( padData[m_data.m_port].err == PAD_ERR_NO_CONTROLLER ) { m_plugged_in = false; m_data.m_valid = false; Unacquire(); Acquire(); return; } m_data.m_valid = true; m_plugged_in = true; p = &padData[m_data.m_port]; // Convert this data back into PS2-style 'raw' data for a DUALSHOCK2. m_data.m_control_data[0] = 0; // 'Valid' info flag. m_data.m_control_data[1] = ( 0x07 << 4 ) | 16; // DUALSHOCK2 id + data length. m_data.m_control_data[2] = 0xFF; // Turn off all buttons by default. m_data.m_control_data[3] = 0xFF; // Turn off all buttons by default. m_data.m_control_data[2] ^= ( ( p->button & PAD_BUTTON_START ) && !( p->button & PAD_TRIGGER_Z ) ) ? ( 1 << 3 ) : 0; // Gamecube 'Start' = PS2 'Start' (only if Z is not also pressed). #if 0 // Dan: none of the Z-shifting crap // Gamecube 'Z' = PS2 'Select'. // Directly mapping to select is disabled. // Gamecube 'Z' plus 'L' = PS2 'Right Stick Button'. // Gamecube 'Z' plus 'R' = PS2 'shift' (this allows the camera mode shifting). if( p->button & PAD_TRIGGER_Z ) { // with Z pressed, triggers are L2/R2 // GameCube 'Z' plus 'L' = PS2 'Right Stick Button'. if( p->button & PAD_TRIGGER_L ) { m_data.m_control_data[2] ^= ( 1 << 1 ); } else // GameCube 'Z' plus 'R' = PS2 'Right Stick Button'. if( p->button & PAD_TRIGGER_R ) { m_data.m_control_data[2] ^= ( 1 << 2 ); } else // // Z button + START is PS2 'Select'. // if( p->button & PAD_BUTTON_START ) // { // m_data.m_control_data[2] ^= ( 1 << 0 ); // } // Z button without L & R is PS2 L1+R1. { m_data.m_control_data[3] ^= ( 1 << 2 ) | ( 1 << 3 ); m_data.m_control_data[2] ^= ( 1 << 0 ); } } #endif // Temp hack to get fly-around working. if( p->button & PAD_TRIGGER_Z ) { m_data.m_control_data[2] ^= ( 1 << 0 ); } m_data.m_control_data[3] ^= ( p->button & PAD_BUTTON_Y ) ? ( 1 << 4 ) : 0; // Gamecube 'Y' = PS2 'Triangle'. m_data.m_control_data[3] ^= ( p->button & PAD_BUTTON_X ) ? ( 1 << 5 ) : 0; // Gamecibe 'X' = PS2 'Circle'. m_data.m_control_data[3] ^= ( p->button & PAD_BUTTON_A ) ? ( 1 << 6 ) : 0; // Gamecube 'A' = PS2 'X'. m_data.m_control_data[3] ^= ( p->button & PAD_BUTTON_B ) ? ( 1 << 7 ) : 0; // Gamecube 'B' = PS2 'Square'. // Dan: none of this analog crap // m_data.m_control_data[3] ^= ( p->triggerLeft > 128 ) ? ( 1 << 2 ) : 0; // XBox 'Left Trigger pressed down > halfway' = PS2 'L1'. // m_data.m_control_data[3] ^= ( p->triggerRight > 128 ) ? ( 1 << 3 ) : 0; // XBox 'Right Trigger pressed down > halfway' = PS2 'R1'. // m_data.m_control_data[3] ^= ( p->triggerLeft > 16 ) ? ( 1 << 0 ) : 0; // XBox 'Left Trigger' = PS2 'L2'. // m_data.m_control_data[3] ^= ( p->triggerRight > 16 ) ? ( 1 << 1 ) : 0; // XBox 'Right Trigger' = PS2 'R2'. m_data.m_control_data[3] ^= ( p->triggerLeft > 128 ) ? ( 1 << 2 ) : 0; // Gamecube 'Left Trigger = PS2 'L1'. m_data.m_control_data[3] ^= ( p->triggerRight > 128 ) ? ( 1 << 3 ) : 0; // Gamecube 'Right Trigger = PS2 'R1'. // Dan: Store the state of Z in m_control_data[20] above Xbox's black and white buttons. m_data.m_control_data[20]=0; if (p->button & PAD_TRIGGER_Z) { m_data.m_control_data[20] |= (1<<2); } // Make analog buttons full depression if pressed. // Ngc analog buttons return analog value in range [0, 255]. m_data.m_control_data[12] = ( p->button & PAD_BUTTON_Y ) ? 255 : 0; // Gamecube 'Y' = PS2 Analog 'Triangle'. m_data.m_control_data[13] = ( p->button & PAD_BUTTON_X ) ? 255 : 0; // Gamecube 'X' = PS2 Analog 'Circle'. m_data.m_control_data[14] = p->analogA; // Gamecube 'A' = PS2 Analog 'X'. m_data.m_control_data[15] = p->analogB; // Gamecube 'B' = PS2 'Square'. m_data.m_control_data[16] = ( p->triggerLeft > 128 ) ? 255 : 0; // Gamecube 'Left Trigger' = PS2 Analog 'L1'. m_data.m_control_data[17] = ( p->triggerRight > 128 ) ? 255 : 0; // Gamecube 'Right Trigger' = PS2 Analog 'R1'. m_data.m_control_data[18] = 0; // Gamecube no 'L2' m_data.m_control_data[19] = 0; // Gamecube no 'R2' // Zero out the d-pad pressure values. m_data.m_control_data[8] = 0x00; m_data.m_control_data[9] = 0x00; m_data.m_control_data[10] = 0x00; m_data.m_control_data[11] = 0x00; // Handle 8 position d-pad. m_data.m_control_data[2] ^= ( p->button & PAD_BUTTON_UP ) ? ( 1 << 4 ) : 0; // Gamecube 'DPad Up' = PS2 'DPad Up'. m_data.m_control_data[10] = ( p->button & PAD_BUTTON_UP ) ? 0xFF : 0; // Gamecube 'DPad Up' = PS2 'DPad Up'. m_data.m_control_data[2] ^= ( p->button & PAD_BUTTON_RIGHT ) ? ( 1 << 5 ) : 0; // Gamecube 'DPad Right' = PS2 'DPad Right'. m_data.m_control_data[8] = ( p->button & PAD_BUTTON_RIGHT ) ? 0xFF : 0; // Gamecube 'DPad Right' = PS2 'DPad Right'. m_data.m_control_data[2] ^= ( p->button & PAD_BUTTON_DOWN ) ? ( 1 << 6 ) : 0; // Gamecube 'DPad Down' = PS2 'DPad Down'. m_data.m_control_data[11] = ( p->button & PAD_BUTTON_DOWN ) ? 0xFF : 0; // Gamecube 'DPad Down' = PS2 'DPad Down'. m_data.m_control_data[2] ^= ( p->button & PAD_BUTTON_LEFT ) ? ( 1 << 7 ) : 0; // Gamecube 'DPad Left' = PS2 'DPad Left'. m_data.m_control_data[9] = ( p->button & PAD_BUTTON_LEFT ) ? 0xFF : 0; // Gamecube 'DPad Left' = PS2 'DPad Left'. // Gamecube thumbsticks return analog value in range [-128, 127]. int stx = (int)(((float)p->stickX) * 2.0f ); int sty = (int)(((float)p->stickY) * 2.0f ); if ( stx < -128 ) stx = -128; if ( stx > 127 ) stx = 127; if ( sty < -128 ) sty = -128; if ( sty > 127 ) sty = 127; int subx = (int)(((float)p->substickX) * 2.0f ); int suby = (int)(((float)p->substickY) * 2.0f ); if ( subx < -128 ) subx = -128; if ( subx > 127 ) subx = 127; if ( suby < -128 ) suby = -128; if ( suby > 127 ) suby = 127; m_data.m_control_data[4] = (unsigned char)(subx+128); // Analog stick right (X direction). m_data.m_control_data[5] = 255 - (unsigned char)(suby+128); // Analog stick right (Y direction). m_data.m_control_data[6] = (unsigned char)(stx+128); // Analog stick left (X direction). m_data.m_control_data[7] = 255 - (unsigned char)(sty+128); // Analog stick left (Y direction). } /******************************************************************/ /* */ /* */ /******************************************************************/ bool Device::IsPluggedIn( void ) { return m_plugged_in; } /******************************************************************/ /* */ /* */ /******************************************************************/ void Device::acquisition_pending( void ) { int status; status = get_status(); if(( status == vCONNECTED ) || ( status == vREADY )) { // sucessful. Now query the controller for capabilities query_capabilities(); } // failed to acquire controller // stay in this state and continue to try to acquire it or prompt the user if it is mandatory // that they have a controller in } /******************************************************************/ /* */ /* */ /******************************************************************/ void Device::query_capabilities( void ) { // Currently assumes standard Gamecube controller. int id = vANALOG_CTRL; m_data.m_type = id; switch( id ) { case vANALOG_CTRL: { m_data.m_caps.SetMask( mANALOG_BUTTONS ); // GameCube actually only has 1, but we need to track 2 since PS2 and Xbox have 2, // and incoming requests could be for either. m_data.m_num_actuators = 2; m_data.m_caps.SetMask( mACTUATORS ); m_data.m_actuator_max[0] = 1; m_data.m_actuator_max[1] = 1; m_state = vBUSY; m_next_state = vACQUIRED; return; } default: { Dbg_Message( "Detected Controller of unknown type in %d:%d", m_data.m_port, m_data.m_slot ); break; } } m_state = vACQUIRED; } /******************************************************************/ /* */ /* */ /******************************************************************/ int Device::get_status( void ) { if( m_data.m_port < 4 ) { if( padData[m_data.m_port].err == PAD_ERR_NONE ) { return vREADY; } else { // Could do more checking here of the return value. return vDISCONNECTED; } } return vDISCONNECTED; } /***************************************************************************** ** Public Functions ** *****************************************************************************/ Device::Device ( int index, int port, int slot ) { m_node = new Lst::Node< Device > ( this ); Dbg_AssertType ( m_node, Lst::Node< Device > ); Dbg_Assert( port < vMAX_PORT ); Dbg_Assert( slot < vMAX_SLOT ); m_state = vIDLE; m_next_state = vIDLE; m_index = index; // Initialize device m_data.m_port = port; m_data.m_slot = slot; m_data.m_caps.ClearAll(); m_data.m_num_actuators = 0; m_data.m_valid = false; memset( m_data.m_actuator_direct, 0, ACTUATOR_BUFFER_LENGTH ); memset( m_data.m_actuator_align, 0xFF, ACTUATOR_BUFFER_LENGTH ); m_data.m_actuator_align[0] = 0; m_data.m_actuator_align[1] = 1; memset( m_data.m_control_data, 0, CTRL_BUFFER_LENGTH ); // m_data.m_prealbuffer = Mem::Malloc( scePadDmaBufferMax * sizeof( uint128) + 63 ); m_data.m_dma_buff = (unsigned char*)((((uint32)m_data.m_prealbuffer)+63)&~63); // memset( m_data.m_dma_buff, 0, sizeof( uint128 ) * scePadDmaBufferMax ); } /******************************************************************/ /* */ /* */ /******************************************************************/ Device::~Device ( void ) { Dbg_AssertType ( m_node, Lst::Node< Device > ); // Mem::Free( m_data.m_prealbuffer ); // XInputClose( m_data.m_handle ); delete m_node; } /******************************************************************/ /* */ /* */ /******************************************************************/ void Device::Acquire( void ) { Dbg_Message( "Acquiring controller port %d slot %d\n", m_data.m_port, m_data.m_slot ); if( m_state == vIDLE ) { if( m_data.m_port < 4 ) { // Acquire device handle. // m_data.m_handle = XInputOpen( XDEVICE_TYPE_GAMEPAD, m_data.m_port, XDEVICE_NO_SLOT, NULL ); // // if( m_data.m_handle ) { // Store capabilites of the device // XINPUT_CAPABILITIES caps; // XInputGetCapabilities( m_data.m_handle, &caps ); // memset( m_data.m_control_data, 0, CTRL_BUFFER_LENGTH ); m_data.m_valid = false; m_state = vACQUIRING; return; } } } Dbg_Warning( "failed to open controller port %d slot %d\n", m_data.m_port, m_data.m_slot ); } /******************************************************************/ /* */ /* */ /******************************************************************/ void Device::Unacquire ( void ) { Dbg_Message( "Unacquiring controller port %d slot %d\n", m_data.m_port, m_data.m_slot ); if( m_state == vACQUIRED ) { // if( scePadPortClose( m_data.m_port, m_data.m_slot ) != 1 ) // { // Dbg_Warning( "failed to close controller port %d slot %d\n", m_data.m_port, m_data.m_slot ); // } } m_state = vIDLE; } /******************************************************************/ /* */ /* */ /******************************************************************/ void Device::ActivateActuator( int act_num, int percent ) { // Do nothing if the actuators are disabled. if( m_data.m_actuators_disabled ) { return; } float act_strength; // First, make sure we're in a ready state and our controller has actuators if(( m_state == vACQUIRED ) && ( m_data.m_caps.TestMask( mACTUATORS ))) { // Rumble regardless. The problem here is that incoming rumble requests can be for motors // other than 0 - PS2 and Xbox have 2 motors. So, if the request is not for motor0, if it if(( act_num >= 0 ) && ( act_num < m_data.m_num_actuators )) { // act_strength = ((float) percent * m_data.m_actuator_max[act_num] ) / 100.0f; act_strength = percent; // for lack of a rounding function, perform this check here // if( m_data.m_actuator_max[act_num] == 1 ) // { // if( act_strength > 0.0f ) // { // m_data.m_actuator_direct[act_num] = 1; // } // else // { // m_data.m_actuator_direct[act_num] = 0; // } // } // else // { // m_data.m_actuator_direct[act_num] = (unsigned char) act_strength; // } m_data.m_actuator_direct[act_num] = ( act_strength > 0.0f ) ? 1 : 0; // If either tracked motor is on, rumble the single motor, otherwise turn it off. PADControlMotor( m_data.m_port, (( m_data.m_actuator_direct[0] > 0 ) || ( m_data.m_actuator_direct[1] > 0 )) ? PAD_MOTOR_RUMBLE : PAD_MOTOR_STOP ); } } } /******************************************************************/ /* These disable or enable pad vibration. /******************************************************************/ void Device::DisableActuators() { // If disabled already do nothing. if (m_data.m_actuators_disabled) { return; } // Run through all the actuators and make sure they're off. if(( m_state == vACQUIRED ) && ( m_data.m_caps.TestMask( mACTUATORS ))) { for (int i=0; i 0 ) || ( m_data.m_actuator_direct[1] > 0 )) ? PAD_MOTOR_RUMBLE : PAD_MOTOR_STOP ); // scePadSetActDirect( m_data.m_port, m_data.m_slot, m_data.m_actuator_direct ); } } } } // Switches off vibration, and also zeros the saved // vibration levels too so that unpausing will not // switch them on again for goodness sake. void Device::StopAllVibrationIncludingSaved() { // First, make sure we're in a ready state and our controller has actuators if(( m_state == vACQUIRED ) && ( m_data.m_caps.TestMask( mACTUATORS ))) { for (int i=0; i 0 ) || ( m_data.m_actuator_direct[1] > 0 )) ? PAD_MOTOR_RUMBLE : PAD_MOTOR_STOP ); // scePadSetActDirect( m_data.m_port, m_data.m_slot, m_data.m_actuator_direct ); } } /******************************************************************/ /* */ /* */ /******************************************************************/ void Device::ActivatePressureSensitiveMode( void ) { if( m_data.m_caps.TestMask( mANALOG_BUTTONS )) { // if( scePadEnterPressMode( m_data.m_port, m_data.m_slot ) == 1 ) { m_state = vBUSY; m_next_state = vACQUIRED; } } } /******************************************************************/ /* */ /* */ /******************************************************************/ void Device::DeactivatePressureSensitiveMode( void ) { if( m_data.m_caps.TestMask( mANALOG_BUTTONS )) { // if( scePadExitPressMode( m_data.m_port, m_data.m_slot ) == 1 ) { m_state = vBUSY; m_next_state = vACQUIRED; } } } /******************************************************************/ /* */ /* */ /******************************************************************/ } // namespace SIO