//-----------------------------------------------------------------------------
// File: Lightgun.cpp
//
// Desc: Code to handle various aspects of a lightgun
//
// Hist: 10.22.02 - Created
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#include <XBApp.h>
#include <XBFont.h>
#include <XBMesh.h>
#include <XBUtil.h>
#include <XBResource.h>
#include <xgraphics.h>
#include "Lightgun.h"
#include <stdio.h>

extern int HDMode;
extern int lightGun;
int drawCrossHairs = 0;
extern void setTriggerBOn();
extern void setTriggerBOff();
extern int inGame;
extern float lastTimeLightGun;
extern int LightGunRumbleOver();
int LightgunRumble = 0;
extern int lgRumble;
extern int leftRumblelg;
extern int rightRumblelg ;
extern float timeRumblelg ;
//-----------------------------------------------------------------------------
// Calibration Process
//-----------------------------------------------------------------------------
// These are the steps taken to calibrate a lightgun:
// 
//  1. Draw a light background (white or yellow) for the calibration screen.
//
//  2. Wait for the XINPUT_LIGHTGUN_ONSCREEN bit to be set. It takes approximately 20
//     frames for the hardware to determine whether the TV is using line or frame
//     doubling, after which the XINPUT_LIGHTGUN_ONSCREEN bit will be turned on.
//
//  3. Draw a first target at the center calibration position and prompt the user
//     to aim at the target and pull the trigger. Note: the center calibration
//     location depends on the resolution of the screen.
//
//  4. Wait for a trigger pull from the gun (with the ONSCREEN bit set) and then read
//     the X,Y values returned by the gun when it is detected. Compute offsets that
//     get from that X,Y point to the actual calibration point.
//
//  5. If the game decides that the calibration values are too far off to be acceptable,
//     prompt user to try again.
//
//  6. Send the computed calibration offsets for the center point down to the gun using
//     the XInputSetLightgunCalibration() call.
//
//  7. Repeat steps 3 through 6 for the upper left calibration point.
//
//  8. The game can now place a crosshair on the screen that follows where the gun is
//     pointed on screen to show the user where they are pointing.
//
//  9. Allow the user to recalibrate either calibration point until they are satisfied
//     with the results. As each new calibration setting is measured, the total
//     cumulative offset for that point should be sent to the gun.
//
// 10. Outside of the calibration screen, the game must monitor the
//     XINPUT_LIGHTGUN_FRAME_DOUBLER flag to ensure that its state does not change.
//     There are TV sets that allow this mode to be changed dynamically. If the flag
//     changes state, the game should lead the user to calibrate the gun or apply
//     appropriate calibration settings measured and stored previously.
//
// 11. The game should save calibration data for this lightgun, resolution, and
//     frame/line doubler state




// Default calibration data for lightguns with no saved data
static LIGHTGUN_CALIBRATION_POINTS g_LightGunDefaultCalibrationPoints[] = 
{
    // Screen res   Center pt   Upperleft pt   User calibrated?
    {  640,  480,   { 0, 0,       0,  0   },   FALSE },
    {  720,  480,   { 0, 0,       0,  0   },   FALSE },
    { 1280,  720,   { 0, 0,       0,  0   },   FALSE },
    { 1920, 1080,   { 0, 0,       0,  0   },   FALSE },
    {  640,  576,   { 0, 0,       0,  0   },   FALSE },
    {  720,  576,   { 0, 0,       0,  0   },   FALSE },
};




//-----------------------------------------------------------------------------
// Name: CLightgun()
// Desc: Constructor
//-----------------------------------------------------------------------------
CLightgun::CLightgun()
{
    m_CalibrationState     = LIGHTGUNCALIBRATIONSTATE_CALIBRATION_UNKNOWN;
    m_bTrackDisplayChanges = FALSE;
    m_bFrameDoubler        = FALSE;
    m_bLineDoubler         = FALSE;
}




//-----------------------------------------------------------------------------
// Name: CopyInput()
// Desc: Updates the lightgun with recently polled gamepad input data.
//-----------------------------------------------------------------------------
VOID CLightgun::CopyInput( XBGAMEPAD* pLightgun )
{
    memcpy( this, pLightgun, sizeof(XBGAMEPAD) );
}




//-----------------------------------------------------------------------------
// Name: InitCalibrationData()
// Desc: Initialize the calibration data structure for a lightgun
//-----------------------------------------------------------------------------
HRESULT CLightgun::InitCalibrationData()
{
    // Make sure the device is a lightgun
    XINPUT_CAPABILITIES caps;
    XInputGetCapabilities( hDevice, &caps );
    if( caps.SubType != XINPUT_DEVSUBTYPE_GC_LIGHTGUN )
        return E_INVALIDARG;

    // Get the current state of the lightgun
    XINPUT_STATE state;
    XInputGetState( hDevice, &state );
    
    // Make sure the lightgun is onscreen...a necessary condition for the
    // frame and line doubler flags to be valid
    if( 0 == ( state.Gamepad.wButtons & XINPUT_LIGHTGUN_ONSCREEN ) )
        return E_FAIL;

    // Record whether frame or line doubling is in effect
    m_CalibrationData.bFrameDoubler = state.Gamepad.wButtons & XINPUT_LIGHTGUN_FRAME_DOUBLER;
    m_CalibrationData.bLineDoubler  = state.Gamepad.wButtons & XINPUT_LIGHTGUN_LINE_DOUBLER;

    // Get the desription of the lightgun
    XInputGetDeviceDescription( hDevice, &m_CalibrationData.DeviceDesc );

    // Initialize the calibration points for the lightgun
    memcpy( m_CalibrationData.CalibrationPts, &g_LightGunDefaultCalibrationPoints,
            sizeof(g_LightGunDefaultCalibrationPoints) );

    // Set the display mode index
    D3DDISPLAYMODE mode;
    D3DDevice::GetDisplayMode( &mode );

    for( WORD i=0; i<NUM_SUPPORTED_LIGHTGUN_DISPLAY_MODES; i++ )
    {
        if( m_CalibrationData.CalibrationPts[i].wScreenWidth  == mode.Width &&
            m_CalibrationData.CalibrationPts[i].wScreenHeight == mode.Height )
        {
            m_CalibrationData.wCurrentDisplayModeIndex = i;
            break;
        }
    }

    // Double check that we are in a supported display mode
    if( i == NUM_SUPPORTED_LIGHTGUN_DISPLAY_MODES )
        return E_FAIL;

    // Put the newly initialized calibration offsets into effect
    XInputSetLightgunCalibration( hDevice, 
                                  &m_CalibrationData.CalibrationPts[m_CalibrationData.wCurrentDisplayModeIndex].offsets );

    // Calibration state is initially unknown
    m_CalibrationState = LIGHTGUNCALIBRATIONSTATE_CALIBRATION_UNKNOWN;

    // Load the calibration data for the lightgun
    if( FAILED( LoadCalibrationData() ) ) 
        return E_FAIL;

    // Update calibration status
    if( m_CalibrationData.CalibrationPts[m_CalibrationData.wCurrentDisplayModeIndex].bUserCalibrated )
        m_CalibrationState = LIGHTGUNCALIBRATIONSTATE_USER_CALIBRATED;
    else
        m_CalibrationState = LIGHTGUNCALIBRATIONSTATE_NOT_CALIBRATED;

    return S_OK;
}




//-----------------------------------------------------------------------------
// Name: GetCalibrationDataFilename()
// Desc: Returns a unique filename for this lightgun (based on vendor
//       information and display settings)
//-----------------------------------------------------------------------------
CHAR* CLightgun::GetCalibrationDataFilename( LIGHTGUN_CALIBRATION_DATA* pCalibrationData )
{
    // Generate a filename
    static CHAR strFilename[100];
    sprintf( strFilename, "t:\\Lightgun_%04lx%04lx%04lx%s%s.dat",
                          pCalibrationData->DeviceDesc.wProductID,
                          pCalibrationData->DeviceDesc.wVendorID,
                          pCalibrationData->DeviceDesc.wVersion,
                          pCalibrationData->bFrameDoubler ? "_fd" : "",
                          pCalibrationData->bLineDoubler  ? "_ld" : "" );

    return strFilename;
}




//-----------------------------------------------------------------------------
// Name: LoadCalibrationData()
// Desc: Loads calibration data for a lightgun
//-----------------------------------------------------------------------------
HRESULT CLightgun::LoadCalibrationData()
{
    // Get the unique filename for this lightgun
    CHAR* strFilename = GetCalibrationDataFilename( &m_CalibrationData );
    
    // Open the file, if it exists
    FILE* file = fopen( strFilename, "rb" );
    if( file )
    {
        // Read the calibration data from the file
        LIGHTGUN_CALIBRATION_POINTS CalibrationPtsFromFile[NUM_SUPPORTED_LIGHTGUN_DISPLAY_MODES];
        int count = fread( &CalibrationPtsFromFile, sizeof(LIGHTGUN_CALIBRATION_POINTS), 
                           NUM_SUPPORTED_LIGHTGUN_DISPLAY_MODES, file );

        fclose( file );

        if( NUM_SUPPORTED_LIGHTGUN_DISPLAY_MODES == count )
        {
            // If the read was successful, use that data
            memcpy( m_CalibrationData.CalibrationPts, &CalibrationPtsFromFile,
                    NUM_SUPPORTED_LIGHTGUN_DISPLAY_MODES * sizeof(LIGHTGUN_CALIBRATION_POINTS) );

            // Put the newly loaded calibration offsets into effect
            XInputSetLightgunCalibration( hDevice, &m_CalibrationData.CalibrationPts[m_CalibrationData.wCurrentDisplayModeIndex].offsets );

            return S_OK;
        }
    }

    return S_FALSE;
}




//-----------------------------------------------------------------------------
// Name: ResetCalibrationOffsets()
// Desc: Sets and saves calibration offsets for a lightgun
//-----------------------------------------------------------------------------
HRESULT CLightgun::ResetCalibrationOffsets()
{
    XINPUT_LIGHTGUN_CALIBRATION_OFFSETS offsets;
    offsets.wCenterX    = 0;
    offsets.wCenterY    = 0;
    offsets.wUpperLeftX = 0;
    offsets.wUpperLeftY = 0;
    XInputSetLightgunCalibration( hDevice, &offsets );

    // Copy the new calibration offsets to the data structure
    memcpy( &m_CalibrationData.CalibrationPts[m_CalibrationData.wCurrentDisplayModeIndex].offsets,
            &offsets, sizeof(XINPUT_LIGHTGUN_CALIBRATION_OFFSETS) );

    m_CalibrationData.CalibrationPts[m_CalibrationData.wCurrentDisplayModeIndex].bUserCalibrated = FALSE;
    m_CalibrationState = LIGHTGUNCALIBRATIONSTATE_NOT_CALIBRATED;

    // Save the new offsets for future retrieval
    return SaveCalibrationOffsets( &offsets );
}




//-----------------------------------------------------------------------------
// Name: SetCalibrationOffsets()
// Desc: Sets and saves calibration offsets for a lightgun
//-----------------------------------------------------------------------------
HRESULT CLightgun::SetCalibrationOffsets( WORD wCenterX, WORD wCenterY,
                                          WORD wUpperLeftX, WORD wUpperLeftY )
{
    XINPUT_LIGHTGUN_CALIBRATION_OFFSETS offsets;
    offsets.wCenterX    = XINPUT_LIGHTGUN_CALIBRATION_CENTER_X    - wCenterX;
    offsets.wCenterY    = XINPUT_LIGHTGUN_CALIBRATION_CENTER_Y    - wCenterY;
    offsets.wUpperLeftX = XINPUT_LIGHTGUN_CALIBRATION_UPPERLEFT_X - wUpperLeftX;
    offsets.wUpperLeftY = XINPUT_LIGHTGUN_CALIBRATION_UPPERLEFT_Y - wUpperLeftY;
    XInputSetLightgunCalibration( hDevice, &offsets );

    // Copy the new calibration offsets to the data structure
    memcpy( &m_CalibrationData.CalibrationPts[m_CalibrationData.wCurrentDisplayModeIndex].offsets,
            &offsets, sizeof(XINPUT_LIGHTGUN_CALIBRATION_OFFSETS) );

    m_CalibrationData.CalibrationPts[m_CalibrationData.wCurrentDisplayModeIndex].bUserCalibrated = TRUE;
    m_CalibrationState = LIGHTGUNCALIBRATIONSTATE_USER_CALIBRATED;

    // Save the new offsets for future retrieval
    return SaveCalibrationOffsets( &offsets );
}




//-----------------------------------------------------------------------------
// Name: SaveCalibrationOffsets()
// Desc: Saves calibration offsets for a lightgun
//-----------------------------------------------------------------------------
HRESULT CLightgun::SaveCalibrationOffsets( XINPUT_LIGHTGUN_CALIBRATION_OFFSETS* pCalibrationOffsets )
{
    if( NULL == pCalibrationOffsets )
        return E_INVALIDARG;

    // Get the unique filename for this lightgun
    CHAR* strFilename = GetCalibrationDataFilename( &m_CalibrationData );
    
    // Create the file
    FILE* file = fopen( strFilename, "wb" );
    if( NULL == file )
        return E_FAIL;

    // Write the calibration data to the file
    int count = fwrite( m_CalibrationData.CalibrationPts, sizeof(LIGHTGUN_CALIBRATION_POINTS),
                        NUM_SUPPORTED_LIGHTGUN_DISPLAY_MODES, file );

    fclose( file );

    // Check whether the write was successful
    if( NUM_SUPPORTED_LIGHTGUN_DISPLAY_MODES != count )
        return E_FAIL;

    return S_OK;
}




//-----------------------------------------------------------------------------
// Name: IsUserCalibrated()
// Desc: Returns whether the lightgun has been calibrated by the user for this
//       display mode
//-----------------------------------------------------------------------------
BOOL CLightgun::IsUserCalibrated()
{
    return ( LIGHTGUNCALIBRATIONSTATE_USER_CALIBRATED == m_CalibrationState );
}




//-----------------------------------------------------------------------------
// Name: GetCalibrationTargetCoords()
// Desc: Returns screen coords for the targets that an app draws during
//       calibration of the lightgun. The coords depend on the display mode, as
//       in the following table:
//
//           Display mode:    Center pt:     Upperleft pt:
//          -------------   ------------    ------------
//            640 x  480:   ( 320, 240 )    (  76,  57 )
//            720 x  480:   ( 360, 240 )    (  85,  57 )
//           1280 x  720:   ( 640, 480 )    ( 152,  85 )
//           1920 x 1080:   ( 960, 540 )    ( 227, 128 )
//            640 x  576:   ( 320, 288 )    (  76,  68 )
//            720 x  576:   ( 360, 288 )    (  85,  68 )
//-----------------------------------------------------------------------------
VOID CLightgun::GetCalibrationTargetCoords( FLOAT* pfCenterTargetX,
                                            FLOAT* pfCenterTargetY,
                                            FLOAT* pfUpperLeftTargetX,
                                            FLOAT* pfUpperLeftTargetY )
{
    // Get the display mode
    D3DDISPLAYMODE mode;
    D3DDevice::GetDisplayMode( &mode );

    // Return the coords for the targets
    if( pfCenterTargetX )    (*pfCenterTargetX )    = floorf( (320.0f/640.0f) * mode.Width  );
    if( pfCenterTargetY )    (*pfCenterTargetY )    = floorf( (240.0f/480.0f) * mode.Height );
    if( pfUpperLeftTargetX ) (*pfUpperLeftTargetX ) = floorf( ( 76.0f/640.0f) * mode.Width  );
    if( pfUpperLeftTargetY ) (*pfUpperLeftTargetY ) = floorf( ( 57.0f/480.0f) * mode.Height );
}




//-----------------------------------------------------------------------------
// Name: IsOnCorrectField()
// Desc: Returns TRUE if display is on correct field for reading lightgun data.
//-----------------------------------------------------------------------------
BOOL CLightgun::IsOnCorrectField()
{
    // Get the current display mode
    D3DDISPLAYMODE DisplayMode;
    D3DDevice::GetDisplayMode( &DisplayMode );
    
    // Get the current display field status
    D3DFIELD_STATUS DisplayFieldStatus;
    D3DDevice::GetDisplayFieldStatus( &DisplayFieldStatus );

    // On interlaced NTSC and PAL-60 systems, lightguns can only be read
    // during odd fields. On PAL-50 systems, lightguns can only be read
    // during even fields. So, here we check the current display field
    // and return the results.
    if( DisplayMode.RefreshRate == 50 )
    {
        if( DisplayFieldStatus.Field == D3DFIELD_EVEN )
            return TRUE;
    }
    else // DisplayMode.RefreshRate == 60
    {   
        if( DisplayFieldStatus.Field == D3DFIELD_ODD )
            return TRUE;
    }

    return FALSE;
}




//-----------------------------------------------------------------------------
// Name: DisplayWhiteField()
// Desc: Displays a white screen for one field.
//-----------------------------------------------------------------------------
VOID CLightgun::DisplayWhiteField()
{
    static D3DGAMMARAMP WhiteGammaRamp = {0};

    // On first entry, initialize the white gamma ramp
    if( WhiteGammaRamp.red[0] == 0 )
    {
        for( DWORD i=0; i<256; i++ )
        {
            WhiteGammaRamp.red[i]   = 255;
            WhiteGammaRamp.green[i] = 255;
            WhiteGammaRamp.blue[i]  = 255;
        }
    }

    // Save the current gamma ramp
    D3DGAMMARAMP SavedGammaRamp;
    D3DDevice::GetGammaRamp( &SavedGammaRamp );
    
    // Set the white gamma ramp and wait for the field to draw
    D3DDevice::SetGammaRamp( D3DSGR_IMMEDIATE, &WhiteGammaRamp );
    D3DDevice::BlockUntilVerticalBlank();
    
    // Restore the gamma ramp
    D3DDevice::SetGammaRamp( D3DSGR_IMMEDIATE, &SavedGammaRamp );
}




//-----------------------------------------------------------------------------
// Name: Update()
// Desc: Get lightgun shot information. Since determining this information
//       involves many different states over many different frames, this
//       function needs to be called every frame.
//-----------------------------------------------------------------------------
VOID CLightgun::Update( BOOL* pbShotFired, BOOL* pbShotHitScreen,
                        BOOL* pbShotMissedScreen )
{
    // Variables for whether shots hit or missed. These are the return values
    // for the function
    BOOL bShotFired        = FALSE;
    BOOL bShotHitScreen    = FALSE;
    BOOL bShotMissedScreen = FALSE;

    // Static state for reading the lightgun data
    enum LIGHTGUNSTATE
    {
        LIGHTGUNSTATE_READY,
        LIGHTGUNSTATE_WAITUNTILCORRECTFIELD,
        LIGHTGUNSTATE_WAITONEFRAME,
        LIGHTGUNSTATE_READINPUT,
    };
    static LIGHTGUNSTATE LightgunState = LIGHTGUNSTATE_READY;

    // To read the lightgun, a number of steps over a few frames needs to be
    // taken. This is due to the fact that the lightgun hardware only works
    // when a white frame is rendered in the odd-display field and then needs
    // time to process data.
    //
    // So, we implement a mini-state engine to track the following:
    // (1) Acknowledge that the lightgun trigger was pulled. Then draw a white
    //     screen in the appropriate display field
    // (2) Wait for the next vblank, so the lightgun hardware gets a signal
    // (3) Poll and read the lightgun's gamepad structure
    switch( LightgunState )
    {
        // If the lightgun state is ready, check for trigger pulls
        case LIGHTGUNSTATE_READY:
            // Don't do anything until the trigger is pulled
            if( FALSE == bPressedAnalogButtons[XINPUT_GAMEPAD_A] )
                break;

            // After the trigger is pulled, fall through to the next state
            bShotFired    = TRUE;
            LightgunState = LIGHTGUNSTATE_WAITUNTILCORRECTFIELD;

        // Wait until the correct field and then draw one field worth of
        // a white screen
        case LIGHTGUNSTATE_WAITUNTILCORRECTFIELD:
            // If we're on the wrong field, wait until the correct one
            if( FALSE == IsOnCorrectField() )
                break;

            // Draw one field worth of a white screen. The lightgun needs this
            // in order to reliably read valid data.
            DisplayWhiteField();

            // Adavance the state for the next time around
            LightgunState = LIGHTGUNSTATE_WAITONEFRAME;
            break;

        // If the trigger was pulled in a previous frame, we would have drawn a
        // white flash, and must still wait an additional frame before reading
        // input so that the lightgun hardware has time to process data.
        case LIGHTGUNSTATE_WAITONEFRAME:
            // On the frame following this one, we will be ready to read the
            // lightgun data
            LightgunState = LIGHTGUNSTATE_READINPUT;
            break;

        // After flashing the screen white and giving the hardware time to
        // process data, the lightgun data will now be ready to be read.
        case LIGHTGUNSTATE_READINPUT:
            // Record whether the screen was hit
            if( wButtons & XINPUT_LIGHTGUN_ONSCREEN )
                bShotHitScreen = TRUE;
            else
                bShotMissedScreen = TRUE;

            // Now we're ready to record another trigger pull again
            LightgunState = LIGHTGUNSTATE_READY;
            break;
    }

    // Return whether a shot was received this frame
    if( pbShotFired )        (*pbShotFired)        = bShotFired;
    if( pbShotHitScreen )    (*pbShotHitScreen)    = bShotHitScreen;
    if( pbShotMissedScreen ) (*pbShotMissedScreen) = bShotMissedScreen;
}




//-----------------------------------------------------------------------------
// Name: VerifyCalibrationState()
// Desc: Verifies whether the lightgun is calibrated for the current display
//       mode.
//-----------------------------------------------------------------------------
VOID CLightgun::VerifyCalibrationState( BOOL bModeChange )
{
    // If the device was just inserted or the display mode was changed, re-init
    // (or reload) the calibration data
    if( bModeChange || bInserted == TRUE )
    {
        // Set a flag so we know to reload the lightgun calibration data
        m_CalibrationState = LIGHTGUNCALIBRATIONSTATE_CALIBRATION_UNKNOWN;
        m_bTrackDisplayChanges = FALSE;
    }

    // Check if we need to load the calibration data.
    if( LIGHTGUNCALIBRATIONSTATE_CALIBRATION_UNKNOWN == m_CalibrationState )
    {
        // The lightgun must be onscreen before we can know if frame
        // doubling or line doubling is in effect. So wait for that bit
        // so we know to load the matching calibration data.
        if( wButtons & XINPUT_LIGHTGUN_ONSCREEN )
        {
            // Initialize the calibration data structure for this mode
            if( SUCCEEDED( InitCalibrationData() ) )
            {
                // Starting now, let's track the state of the display. If the user
                // turns on/off frame or line doubling, we will need to
                // recalibrate the lightgun
                m_bTrackDisplayChanges = TRUE;
                m_bFrameDoubler = wButtons & XINPUT_LIGHTGUN_FRAME_DOUBLER;
                m_bLineDoubler  = wButtons & XINPUT_LIGHTGUN_LINE_DOUBLER;
            }
        }
    }
    
    // If we are tracking display changes (for when the user changes modes on
    // his TV) then do that here. Note: in the middle of a game, we might not
    // want to interrupt the user.
    if( m_bTrackDisplayChanges  )
    {
        if( m_bFrameDoubler != ( wButtons & XINPUT_LIGHTGUN_FRAME_DOUBLER ) ||
            m_bLineDoubler  != ( wButtons & XINPUT_LIGHTGUN_LINE_DOUBLER ) )
        {
            // Settings have changed, so set a flag to get updated calibration info
            m_CalibrationState = LIGHTGUNCALIBRATIONSTATE_CALIBRATION_UNKNOWN;
        }
    }
}



extern int inGame;
//-----------------------------------------------------------------------------
// Name: SetVibrationMotors()
// Desc: Set the vibration motors, but only if they changed
//-----------------------------------------------------------------------------
VOID CLightgun::SetVibrationMotors( WORD wLeftMotorSpeed, WORD wRightMotorSpeed )
{

	if(inGame == 0) return;

	float m_fLeftMotorSpeed;
	float m_fRightMotorSpeed;

	if(wRightMotorSpeed > 0) 
			m_fRightMotorSpeed = (((float)wRightMotorSpeed)/255.0f);
	else
			m_fRightMotorSpeed = 0;

	if(wLeftMotorSpeed > 0) 
			m_fLeftMotorSpeed = (((float)wLeftMotorSpeed)/255.0f);
	else
			m_fLeftMotorSpeed = 0;


	WORD l = WORD( m_fLeftMotorSpeed  * 65535.0f );
	WORD r = WORD( m_fRightMotorSpeed  * 65535.0f );
	

	if(LightgunRumble == 0) {
		while(Feedback.Header.dwStatus == ERROR_IO_PENDING) {}
		Feedback.Rumble.wLeftMotorSpeed  = 0;
        Feedback.Rumble.wRightMotorSpeed =0; 
        XInputSetState( hDevice, &Feedback );
		return;
	}

    if( Feedback.Rumble.wLeftMotorSpeed  !=  l ||
      Feedback.Rumble.wRightMotorSpeed !=   r)
    {
        // Check to see if we are done transferring any previous motor values
        if( Feedback.Header.dwStatus != ERROR_IO_PENDING )
        {
            // Set the new vibration motor values
            Feedback.Rumble.wLeftMotorSpeed  = l;
            Feedback.Rumble.wRightMotorSpeed =r; 
            XInputSetState( hDevice, &Feedback );
        }
    }
	
	
	
}














  // Valid app states
    enum APPSTATE 
    { 
        APPSTATE_CONTROLTEST=0, 
        APPSTATE_BULLETHOLESTEST,
        APPSTATE_CROSSHAIRSTEST,
        APPSTATE_CALIBRATION,
        APPSTATE_MAX,
    };

    // General application members
    APPSTATE           m_AppState;           // State of the app
    APPSTATE           m_OldAppState;        // Previous app state
    
    CXBPackedResource  m_xprResource;        // Packed resources for the app
    CXBFont            m_Font16;             // 16-point font class
    CXBFont            m_Font12;             // 12-point font class
    
    D3DDISPLAYMODE     m_DisplayMode;        // Current display mode
    WCHAR              m_strDisplayMode[80]; // Desc of display mode

    // Geometry
    //CLightgunMesh      m_LightGunMesh;       // Geometry for the lightgun

    // Active gamepad/lightgun
    DWORD              m_dwNumInsertedLightguns;
    CLightgun          m_Lightgun;
    
    // Vibration motor values
    WORD               m_wLeftMotorSpeed;
    WORD               m_wRightMotorSpeed;

    // Calibration data
    BOOL               m_bCalibratingCenter;
    SHORT              m_CenterCalibrationPointX;
    SHORT              m_CenterCalibrationPointY;
    SHORT              m_UpperLeftCalibrationPointX;
    SHORT              m_UpperLeftCalibrationPointY;

    // Calibration target and gun testing
    D3DTexture*        m_pTargetTexture;
    D3DTexture*        m_pBulletHolesTexture;
    D3DXVECTOR2        m_vBulletHoleList[100];
    DWORD              m_dwNumBulletHoles;

    BOOL               m_bVideoModeChanged;

      // Variables for timing
    FLOAT      m_fTime;             // Current absolute time in seconds
    FLOAT      m_fElapsedTime;      // Elapsed absolute time since last frame
    FLOAT      m_fAppTime;          // Current app time in seconds
    FLOAT      m_fElapsedAppTime;   // Elapsed app time since last frame
    BOOL       m_bPaused;           // Whether app time is paused by user
    FLOAT      m_fFPS;              // instantaneous frame rate
    WCHAR      m_strFrameRate[20];  // Frame rate written to a string
    HANDLE     m_hFrameCounter;     // Handle to frame rate perf counter


	// Get the frequency of the timer
    LARGE_INTEGER qwTicksPerSec;
    float fSecsPerTick;

    // Save the start time
    LARGE_INTEGER qwTime, qwLastTime, qwElapsedTime;
    

    LARGE_INTEGER qwAppTime, qwElapsedAppTime;
   

	extern D3DPRESENT_PARAMETERS d3dpp; 
	extern IDirect3DDevice8 *Device;
	extern XBGAMEPAD g_Gamepads[4];

	HRESULT InitControlTestPage()
	{
		//m_AppState = APPSTATE_CONTROLTEST; 
		return S_OK;
	}




	void setLightGunMotors(unsigned short left, unsigned short right) {
			m_wLeftMotorSpeed = left;
			m_wRightMotorSpeed = right;
	}

	HRESULT SelectNextVideoMode()
	{
		//DWORD dwNumModes = Direct3D::GetAdapterModeCount( 0 );

		//static DWORD dwCurrentMode = dwNumModes-1;

		//for( DWORD i=0; i<dwNumModes; i++ )
		//{
			// Check the next available mode
		//	DWORD dwMode = (dwCurrentMode + i + 1 ) % dwNumModes;
		//	Direct3D::EnumAdapterModes( 0, dwMode, &m_DisplayMode );

			// Skip modes we don't care about
		//	if( m_DisplayMode.Format != D3DFMT_LIN_A8R8G8B8 )
		//		continue;
		//	if( m_DisplayMode.Flags & D3DPRESENTFLAG_FIELD )
		//		continue;
		//	if( m_DisplayMode.Flags & D3DPRESENTFLAG_10X11PIXELASPECTRATIO )
		//		continue;
		//	if( m_DisplayMode.Flags & D3DPRESENTFLAG_EMULATE_REFRESH_RATE )
		//		continue;

			// If we get here, we found an acceptable mode
			//dwCurrentMode = dwMode;
		//	break;
	////	}

		// Build a display mode string
		//WCHAR* strMode;
	//	if( m_DisplayMode.Height == 1080 )
	//		strMode = L"1080i";
	//	else if( m_DisplayMode.Flags & D3DPRESENTFLAG_PROGRESSIVE )
	//	{
		//	if( m_DisplayMode.Height == 480 )
		//		strMode = L"480p";
		//	else
		//		strMode = L"720p";
		//}
		//else if( XGetVideoStandard() == XC_VIDEO_STANDARD_PAL_I )
		//	strMode = L"PAL";
		//else
		//	strMode = L"NTSC";

	//	swprintf( m_strDisplayMode, L"Mode = %d x %d %s @%dHz%s%s", 
		//	m_DisplayMode.Width, m_DisplayMode.Height, 
		//	strMode, m_DisplayMode.RefreshRate,
		//	m_DisplayMode.Flags & D3DPRESENTFLAG_WIDESCREEN ? L" Widescreen" : L"" );

		// Set the new presentation parameters
	//	d3dpp.BackBufferWidth            = m_DisplayMode.Width;
	//	d3dpp.BackBufferHeight           = m_DisplayMode.Height;
	//	d3dpp.Flags                      = m_DisplayMode.Flags;
	//	d3dpp.FullScreen_RefreshRateInHz = m_DisplayMode.RefreshRate;

		// If a device already exists, apply the new presentation parameters
	//	if( Device )
	//	{
			// Reset the device with the new parameters
	//		Device->Reset( &d3dpp );

			// Clear both buffers to avoid flicker artifacts
		//	Device->Clear( 0, NULL, D3DCLEAR_TARGET, 0x00000000, 1.0f, 0L );
		//	Device->Present( NULL, NULL, NULL, NULL );
		//	Device->Clear( 0, NULL, D3DCLEAR_TARGET, 0x00000000, 1.0f, 0L );
		//	Device->Present( NULL, NULL, NULL, NULL );
		//}

		// Scale fonts to look right in the various display modes
		

		// Set a flag that the video mode changed
		

		return S_OK;
	}



	HRESULT LightGunInitialize()
{
    // Create the fonts
	if( FAILED( m_Font16.Create( "Font16.xpr" ) ) )
        return XBAPPERR_MEDIANOTFOUND;
    if( FAILED( m_Font12.Create( "Font12.xpr" ) ) )
        return XBAPPERR_MEDIANOTFOUND;

    // Create the resources
    if( FAILED( m_xprResource.Create( "Resource3.xpr" ) ) )
        return XBAPPERR_MEDIANOTFOUND;

    m_pTargetTexture      = m_xprResource.GetTexture( "Target" );
    m_pBulletHolesTexture = m_xprResource.GetTexture( "BulletHole" );

    // Start off in the control test page
    m_AppState =APPSTATE_CROSSHAIRSTEST;
   // InitControlTestPage();

	if( HDMode != 0 )
	{
			m_Font16.SetScaleFactors( 0.75f, 1.0f );
			m_Font12.SetScaleFactors( 0.75f, 1.0f );
	}
	else
		{
			m_Font16.SetScaleFactors( 1.0f, 1.0f );
			m_Font12.SetScaleFactors( 1.0f, 1.0f );
	}

	m_bVideoModeChanged = TRUE;
    return S_OK;
}

int firstLight = 1;
void StartLightGun() {
   

    QueryPerformanceFrequency( &qwTicksPerSec );
    fSecsPerTick = 1.0f / (FLOAT)qwTicksPerSec.QuadPart;


	QueryPerformanceCounter( &qwTime );
    qwLastTime.QuadPart = qwTime.QuadPart;

	qwAppTime.QuadPart        = 0;
    qwElapsedTime.QuadPart    = 0;
    qwElapsedAppTime.QuadPart = 0;

	m_AppState               = APPSTATE_CROSSHAIRSTEST;
    m_dwNumInsertedLightguns = 0L;
    m_wLeftMotorSpeed        = 0;
    m_wRightMotorSpeed       = 0;

    // Make sure lightguns use the SET REPORT method for USB communication. 
    g_PollingParameters.fInterruptOut = FALSE;

    // Select an initial video mode
    
	
	
	//if(firstLight == 1) {
	//	firstLight = 0;
	//	SelectNextVideoMode();
	//}
	
	LightGunInitialize();
}



HRESULT InitCrosshairsTestPage()
{
    m_AppState = APPSTATE_CROSSHAIRSTEST; 

    return S_OK;
}

HRESULT InitBulletHolesTestPage()
{
    m_AppState = APPSTATE_CROSSHAIRSTEST; 

    m_dwNumBulletHoles = 0L;

    return S_OK;
}


HRESULT FrameMoveControlTestPage()
{
    return S_OK;
}


HRESULT InitCalibrationPage()
{
    m_OldAppState = m_AppState;
    m_AppState    = APPSTATE_CALIBRATION;

    // Reset values
    m_CenterCalibrationPointX    = 0;
    m_CenterCalibrationPointY    = 0;
    m_UpperLeftCalibrationPointX = 0;
    m_UpperLeftCalibrationPointY = 0;

    // Reset the calibration offsets
    m_Lightgun.ResetCalibrationOffsets();

    // Calibrate the center point first
    m_bCalibratingCenter = TRUE;

    return S_OK;
}

HRESULT FrameMoveCrosshairsTestPage()
{
    // Handle input options: calibrate the lightgun
    if( m_Lightgun.bAnalogButtons[XINPUT_GAMEPAD_A] && m_Lightgun.bPressedAnalogButtons[XINPUT_GAMEPAD_B] ||
        m_Lightgun.bAnalogButtons[XINPUT_GAMEPAD_B] && m_Lightgun.bPressedAnalogButtons[XINPUT_GAMEPAD_A] )
    {
        // Start the calibration process
        InitCalibrationPage();
    }

    return S_OK;
}


HRESULT FrameMoveCalibrationPage()
{
    // This code serves to calibrate the lightgun. The lightgun needs to be
    // calibrated because people aim differently. Calibration simply requires
    // the user to aim and click at two pre-defined points. The points are
    // passed down to the hardware so that, from then on, the hardware only
    // reports adjusted points back to the app.

    // After receiving calibration data, the app should save the information
    // so the user does not need to go through calibration every time they
    // play the game.

    // See if we received a shot
    BOOL bShotFired;
    BOOL bShotHitScreen;
    BOOL bShotMissedScreen;
    m_Lightgun.Update( &bShotFired, &bShotHitScreen, &bShotMissedScreen );

    // Check if the lightgun is onscreen and the trigger was pulled
    if( bShotHitScreen )
    {
        // Calibrate the center point
        if( m_bCalibratingCenter )
        {
            m_CenterCalibrationPointX = m_Lightgun.sThumbLX;
            m_CenterCalibrationPointY = m_Lightgun.sThumbLY;

            // Set the new calibration offsets
            m_Lightgun.SetCalibrationOffsets( m_CenterCalibrationPointX, 
                                              m_CenterCalibrationPointY,
                                              XINPUT_LIGHTGUN_CALIBRATION_UPPERLEFT_X,
                                              XINPUT_LIGHTGUN_CALIBRATION_UPPERLEFT_Y );

            m_bCalibratingCenter = FALSE;
        }
        else // Calibrate the upperleft point
        {
            m_UpperLeftCalibrationPointX = m_Lightgun.sThumbLX;
            m_UpperLeftCalibrationPointY = m_Lightgun.sThumbLY;

            // Set the new calibration offsets and save them for future retrieval
            m_Lightgun.SetCalibrationOffsets( m_CenterCalibrationPointX, 
                                              m_CenterCalibrationPointY,
                                              m_UpperLeftCalibrationPointX,
                                              m_UpperLeftCalibrationPointY );

            // Go back to whereever we came from
            m_AppState = m_OldAppState;
        }
    }

    return S_OK;
}

/*
	oldmY =mouseyloc;
		oldmX = mousexloc;
		mousexloc=  (int) getLightGunX();
		if(mousexloc > 255) mousexloc = 255;
		if(mousexloc < 0) mousexloc = 0;

		mouseyloc =   (int)getLightGunY();
		if(mouseyloc > 223) mouseyloc = 223;
		if(mouseyloc < 0) mouseyloc = 0;

		//if(mX > oldmX) keys[pl1rightk] = 1; else keys[pl1rightk] = 0;
		//if(mX < oldmX) keys[pl1leftk] = 1; else keys[pl1leftk] = 0;
		//if(mY > oldmY) keys[pl1upk] = 1; else keys[pl1upk] = 0;
		//if(mY < oldmY) keys[pl1downk] = 1; else keys[pl1downk] = 0;
		if(triggerPulled() == 1) {
			mousebuttons = 1; keys[pl1Yk] = 1;   
		}
		else { 
			mousebuttons &= 0x02; keys[pl1Yk] = 0; 
		}


*/

//extern "C" {
	extern void setTriggerButton();
	extern void setTriggerNone();
//}

int triggerPulled() {
   return 0;
	//BOOL bShotFired;
    //BOOL bShotHitScreen;
   // BOOL bShotMissedScreen;
   // m_Lightgun.Update( &bShotFired, &bShotHitScreen, &bShotMissedScreen );

    // If the screen was hit, add a bullet mark to the list

	//if( bShotHitScreen || bShotFired )
    //{
	//	setTriggerButton();
	//	return 1;
   // }
	//else {
	//	setTriggerNone();
	//	return 1;
	//}
	
}

HRESULT FrameMoveBulletHolesTestPage()
{
    // This code serves to calibrate the lightgun. The lightgun needs to be
    // calibrated because people aim differently. Calibration simply requires
    // the user to aim and click at two pre-defined points. The points are
    // passed down to the hardware so that, from then on, the hardware only
    // reports adjusted points back to the app.

    // After receiving calibration data, the app should save the information
    // so the user does not need to go through calibration every time they
    // play the game.

    // See if we received a shot
    BOOL bShotFired;
    BOOL bShotHitScreen;
    BOOL bShotMissedScreen;
    m_Lightgun.Update( &bShotFired, &bShotHitScreen, &bShotMissedScreen );

    // If the screen was hit, add a bullet mark to the list
    if( bShotHitScreen )
    {
        FLOAT fWidth  = (FLOAT)d3dpp.BackBufferWidth;
        FLOAT fHeight = (FLOAT)d3dpp.BackBufferHeight;
        
        DWORD dwBullet = (m_dwNumBulletHoles++) % 100;
        m_vBulletHoleList[dwBullet].x = (fWidth/2) + (fWidth/2)*(m_Lightgun.sThumbLX+0.5f)/32767.5f;
        m_vBulletHoleList[dwBullet].y = (fHeight/2) - (fHeight/2)*(m_Lightgun.sThumbLY+0.5f)/32767.5f;
    }

    // Handle input options: reset bullet holes
    if( m_Lightgun.bAnalogButtons[XINPUT_GAMEPAD_A] && ( m_Lightgun.wPressedButtons & XINPUT_GAMEPAD_START ) )
        m_dwNumBulletHoles = 0;

    // Handle input options: calibrate the lightgun
    if( m_Lightgun.bAnalogButtons[XINPUT_GAMEPAD_A] && m_Lightgun.bPressedAnalogButtons[XINPUT_GAMEPAD_B] ||
        m_Lightgun.bAnalogButtons[XINPUT_GAMEPAD_B] && m_Lightgun.bPressedAnalogButtons[XINPUT_GAMEPAD_A] )
    {
        // Start the calibration process
        InitCalibrationPage();
    }

    return S_OK;
}

int correctAppState() {
	if(m_AppState == APPSTATE_CROSSHAIRSTEST) return 1; else return 0;
}

//Called every frame
HRESULT ProcessLightGun() {
     // Get the current time (keep in LARGE_INTEGER format for precision)
        QueryPerformanceCounter( &qwTime );
        qwElapsedTime.QuadPart = qwTime.QuadPart - qwLastTime.QuadPart;
        qwLastTime.QuadPart    = qwTime.QuadPart;
        if( m_bPaused )
            qwElapsedAppTime.QuadPart = 0;
        else
            qwElapsedAppTime.QuadPart = qwElapsedTime.QuadPart;
        qwAppTime.QuadPart    += qwElapsedAppTime.QuadPart;

        // Store the current time values as floating point
        m_fTime           = fSecsPerTick * ((FLOAT)(qwTime.QuadPart));
        m_fElapsedTime    = fSecsPerTick * ((FLOAT)(qwElapsedTime.QuadPart));
        m_fAppTime        = fSecsPerTick * ((FLOAT)(qwAppTime.QuadPart));
        m_fElapsedAppTime = fSecsPerTick * ((FLOAT)(qwElapsedAppTime.QuadPart));

        // Compute the FPS (frames per second) once per second
        static FLOAT fLastTime = 0.0f;
        static DWORD dwFrames  = 0L;
        dwFrames++;

        if( m_fTime - fLastTime > 1.0f )
        {
            m_fFPS    = dwFrames / ( m_fTime - fLastTime );
            fLastTime = m_fTime;
            dwFrames  = 0L;
            //swprintf( m_strFrameRate, L"%0.02f fps", m_fFPS );
        }
	
	
	
	// Copy data for the active lightgun. This is done in a way that works even
    // if the lightgun is removed and inserted into a different port.
    m_dwNumInsertedLightguns = 0;

    // Check all ports for a lightgun
    for( DWORD i=1; i<4; i++ )
    {
        if( g_Gamepads[i].hDevice && g_Gamepads[i].caps.SubType == XINPUT_DEVSUBTYPE_GC_LIGHTGUN )
        {
            // Copy the gamepad input to the lightgun structure.
            // Note: This is just for convenience so we can refer to a 
            // "lightgun" instead of a "gamepad".
            m_Lightgun.CopyInput( &g_Gamepads[i] );

            // Record the number of lightguns that are inserted
            m_dwNumInsertedLightguns++;
        }
    }

    // Ensure that one and only one lightgun is inserted.
    if( m_dwNumInsertedLightguns != 1 )
        return S_FALSE;

    // Make sure the gun is properly calibrated. Note that this is called every
    // frame in case the state of the display changes
    m_Lightgun.VerifyCalibrationState( m_bVideoModeChanged );

    m_bVideoModeChanged = FALSE;

    // Set the vibration motors
    //m_Lightgun.SetVibrationMotors( m_wLeftMotorSpeed, m_wRightMotorSpeed );

    // b + start = exit
    if(m_Lightgun.bPressedAnalogButtons[XINPUT_GAMEPAD_Y] && m_AppState == APPSTATE_CROSSHAIRSTEST )
    {
        lightGun = 0;
		Device->SetTexture( 3, NULL );
		Device->SetTextureStageState( 3, D3DTSS_COLOROP, D3DTOP_DISABLE );
		Device->SetRenderState( D3DRS_POINTSPRITEENABLE, FALSE );
		Device->SetRenderState( D3DRS_POINTSCALEENABLE,  FALSE );
		Device->SetRenderState( D3DRS_ALPHABLENDENABLE,  FALSE );
		return  S_OK;
    }

	// if(m_Lightgun.bPressedAnalogButtons[XINPUT_GAMEPAD_X] )
   // {
		
	//	if(m_AppState == APPSTATE_CROSSHAIRSTEST)
	//		m_AppState = APPSTATE_BULLETHOLESTEST;
	//	else
	//		m_AppState = APPSTATE_CROSSHAIRSTEST;
		
	//	return  S_OK;
   // }

    // Move to next app state when user presses BACK and START together
 //   if( ( ( m_Lightgun.wButtons & XINPUT_GAMEPAD_START ) && ( m_Lightgun.bPressedAnalogButtons[XINPUT_GAMEPAD_B] ) ) ||
  //      ( ( m_Lightgun.bAnalogButtons[XINPUT_GAMEPAD_B] ) && ( m_Lightgun.wPressedButtons & XINPUT_GAMEPAD_START ) ) )
  //  {

        // Advance the state
       // switch( m_AppState )
       // {
       //     case APPSTATE_CONTROLTEST:     InitCrosshairsTestPage(); 
         //                                  break;
         //   case APPSTATE_CROSSHAIRSTEST:  InitBulletHolesTestPage(); 
         //                                  break;
         //   case APPSTATE_BULLETHOLESTEST: InitControlTestPage(); 
         //                                  break;
       // }
 //   }

    // Go to app-state specific control handling
    switch( m_AppState )
    {
        case APPSTATE_CONTROLTEST:     FrameMoveControlTestPage();
                                       break;
        case APPSTATE_CROSSHAIRSTEST:  FrameMoveCrosshairsTestPage();
                                       break;
        case APPSTATE_BULLETHOLESTEST: FrameMoveBulletHolesTestPage();
                                       break;
        case APPSTATE_CALIBRATION:     FrameMoveCalibrationPage();
                                       break;
    }

    return S_OK;
}


HRESULT ReadLightGun() {	
	// Copy data for the active lightgun. This is done in a way that works even
    // if the lightgun is removed and inserted into a different port.
    m_dwNumInsertedLightguns = 0;

    // Check all ports for a lightgun
    for( DWORD i=1; i<4; i++ )
    {
        if( g_Gamepads[i].hDevice && g_Gamepads[i].caps.SubType == XINPUT_DEVSUBTYPE_GC_LIGHTGUN )
        {
            // Copy the gamepad input to the lightgun structure.
            // Note: This is just for convenience so we can refer to a 
            // "lightgun" instead of a "gamepad".
            m_Lightgun.CopyInput( &g_Gamepads[i] );

            // Record the number of lightguns that are inserted
            m_dwNumInsertedLightguns++;
        }
    }

    // Ensure that one and only one lightgun is inserted.
    if( m_dwNumInsertedLightguns != 1 )
        return S_FALSE;

    // Make sure the gun is properly calibrated. Note that this is called every
    // frame in case the state of the display changes
    m_Lightgun.VerifyCalibrationState( m_bVideoModeChanged );

    m_bVideoModeChanged = FALSE;

   

	if(inGame == 1) {
		//triggerPulled();

		if(m_Lightgun.bAnalogButtons[XINPUT_GAMEPAD_B])
			setTriggerBOn();
		else
			setTriggerBOff();
		


		if( m_Lightgun.bAnalogButtons[XINPUT_GAMEPAD_A])
		{
			setTriggerButton();
			
		//	if(lgRumble == 0) { m_Lightgun.SetVibrationMotors( 0, 0 ); }
			if(lgRumble == 1) { 
				if(LightgunRumble == 0) {
					setLightGunMotors(leftRumblelg, rightRumblelg);

					lastTimeLightGun = 0;
					LightgunRumble = 1;
					m_Lightgun.SetVibrationMotors( leftRumblelg, rightRumblelg );
				}
			}
		}
		else {
			setTriggerNone();
			if(lgRumble == 1) { 
				if(LightgunRumble == 1 && LightGunRumbleOver() == 1) {
					LightgunRumble = 0;
					setLightGunMotors(0, 0);
					m_Lightgun.SetVibrationMotors( 0, 0 );
				}
			}
		}
	}
	else {
		setLightGunMotors(0, 0);
		lastTimeLightGun = 0;
	}

    return S_OK;
}




HRESULT RenderGradientBackground( DWORD dwTopColor, 
                                                  DWORD dwBottomColor )
{
    // Normally there is no need to clear the color buffer - if you are
    // rendering to the entire screen then you just need to clear the
    // z-buffer and stencil buffer. However, when doing a PIX push-buffer
    // capture - when D3DPERF_QueryRepeatFrame() returns TRUE - it can be
    // useful to clear the color buffer, to avoid having the previous
    // frame of data show up in the PIX images window.
    // The samples don't do this because they clear the entire screen immediately
    // anyway.
    Device->Clear( 0, NULL, D3DCLEAR_ZBUFFER|D3DCLEAR_STENCIL, 0x00000000, 1.0f, 0L );

    // Set states
    Device->SetTexture( 0, NULL );
   Device->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_DISABLE );
   Device->SetRenderState( D3DRS_ZENABLE,          FALSE ); 
    Device->SetRenderState( D3DRS_FILLMODE,         D3DFILL_SOLID ); 
    Device->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE ); 
    Device->SetRenderState( D3DRS_ALPHATESTENABLE,  FALSE ); 
    Device->SetVertexShader( D3DFVF_XYZRHW|D3DFVF_DIFFUSE );

    // Draw a background-filling quad
    D3DDISPLAYMODE mode;
    D3DDevice::GetDisplayMode( &mode );
    FLOAT fX1 = -0.5f;
    FLOAT fY1 = -0.5f;
    FLOAT fX2 = (FLOAT)mode.Width - 0.5f;
    FLOAT fY2 = (FLOAT)mode.Height - 0.5f;

    Device->Begin( D3DPT_QUADLIST );
    Device->SetVertexDataColor( D3DVSDE_DIFFUSE, dwTopColor );
   Device->SetVertexData4f( D3DVSDE_VERTEX, fX1, fY1, 1.0f, 1.0f );
    Device->SetVertexData4f( D3DVSDE_VERTEX, fX2, fY1, 1.0f, 1.0f );
    Device->SetVertexDataColor( D3DVSDE_DIFFUSE, dwBottomColor );
    Device->SetVertexData4f( D3DVSDE_VERTEX, fX2, fY2, 1.0f, 1.0f );
   Device->SetVertexData4f( D3DVSDE_VERTEX, fX1, fY2, 1.0f, 1.0f );
    Device->End();

    return S_OK;
}


HRESULT RenderTarget( FLOAT fTargetX, FLOAT fTargetY )
{
    // Adjust coordinates for screen space rendering
    fTargetX = floorf( fTargetX ) - 0.5f;
    fTargetY = floorf( fTargetY ) - 0.5f;

    // Set render states
    Device->SetTexture( 0, m_pTargetTexture );
    Device->SetTextureStageState( 0, D3DTSS_COLOROP,   D3DTOP_SELECTARG1 );
   Device->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
    Device->SetTextureStageState( 0, D3DTSS_ALPHAOP,   D3DTOP_SELECTARG1 );
    Device->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE );
   Device->SetRenderState( D3DRS_TEXTUREFACTOR, 0xffffff00 ); 
    Device->SetRenderState( D3DRS_ZENABLE,          FALSE ); 
   Device->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE ); 
    Device->SetRenderState( D3DRS_SRCBLEND,  D3DBLEND_SRCALPHA ); 
    Device->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA ); 
    Device->SetRenderState( D3DRS_ALPHATESTENABLE,  FALSE ); 
    Device->SetVertexShader( D3DFVF_XYZRHW|D3DFVF_TEX1|D3DFVF_TEXCOORDSIZE4(0) );
    
    // Set the width and height for the display mode
    FLOAT fWidth  = 32.0f;
    FLOAT fHeight = 32.0f;
    if( m_DisplayMode.Flags & D3DPRESENTFLAG_WIDESCREEN )
        fWidth *= 0.75f;

    // Draw the target
    Device->Begin( D3DPT_QUADLIST );
    Device->SetVertexData2f( D3DVSDE_TEXCOORD0, 0.0f, 0.0f );
    Device->SetVertexData4f( D3DVSDE_VERTEX, fTargetX-fWidth, fTargetY-fHeight, 1.0f, 1.0f );
    Device->SetVertexData2f( D3DVSDE_TEXCOORD0, 1.0f, 0.0f );
    Device->SetVertexData4f( D3DVSDE_VERTEX, fTargetX+fWidth, fTargetY-fHeight, 1.0f, 1.0f );
    Device->SetVertexData2f( D3DVSDE_TEXCOORD0, 1.0f, 1.0f );
   Device->SetVertexData4f( D3DVSDE_VERTEX, fTargetX+fWidth, fTargetY+fHeight, 1.0f, 1.0f );
   Device->SetVertexData2f( D3DVSDE_TEXCOORD0, 0.0f, 1.0f );
   Device->SetVertexData4f( D3DVSDE_VERTEX, fTargetX-fWidth, fTargetY+fHeight, 1.0f, 1.0f );
    Device->End();

    return S_OK;
}

int lightgunOnScreen() {
	 if( m_Lightgun.wButtons & XINPUT_LIGHTGUN_ONSCREEN ) return 1; else return 0;
}


HRESULT RenderInsertRemoveControllerPage()
{
    FLOAT fWidth  = (FLOAT)d3dpp.BackBufferWidth;
    FLOAT fHeight = (FLOAT)d3dpp.BackBufferHeight;

    // Draw a gradient filled background
    RenderGradientBackground( 0xff000000, 0xff0000ff );

    // Set some default state
    Device->SetTextureStageState( 0, D3DTSS_COLOROP,   D3DTOP_MODULATE );
    Device->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
    Device->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE );
    Device->SetTextureStageState( 0, D3DTSS_MAGFILTER, D3DTEXF_LINEAR );
    Device->SetTextureStageState( 0, D3DTSS_MINFILTER, D3DTEXF_LINEAR );
    Device->SetRenderState( D3DRS_ZENABLE,          TRUE );
    Device->SetRenderState( D3DRS_AMBIENT,          0x00ffffff );
    Device->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE );
    Device->SetRenderState( D3DRS_LIGHTING,         TRUE );

    // Draw header
    XBUtil_DrawRect( 48, fHeight-80, fWidth-48, fHeight-36, 0x40000000, 0xff000000 );
    m_Font16.DrawText( 53, fHeight-80, 0xffffffff, L"Xbox Lightgun Tool", XBFONT_LEFT );

    // Display a message requesting the user to insert a gamepad controller. Since
    // the Xbox input API take a second or so to detect a controller, let's delay
    // this message a tiny bit
    if( m_fAppTime > 2.0f )
    {
        XBUtil_DrawRect( 48, 36, fWidth-48, fHeight-88, 0x40000000, 0xff000000 );

        if( m_dwNumInsertedLightguns == 0 )
            m_Font12.DrawText( fWidth/2, (fHeight-52)/2, 0xffffffff, L"Please insert a lightgun controller or press Y to exit.", XBFONT_CENTER_X|XBFONT_CENTER_Y );
        else
            m_Font12.DrawText( fWidth/2, (fHeight-52)/2, 0xffffffff, L"Please remove all but one lightgun controller. Press Y to exit.", XBFONT_CENTER_X|XBFONT_CENTER_Y );
    }

    return S_OK;
}

HRESULT RenderControlTestPage()
{
    // Draw a gradient filled background
    RenderGradientBackground( 0xff0000ff, 0xff000000 );

    // Set some default state
    Device->SetTextureStageState( 0, D3DTSS_COLOROP,   D3DTOP_MODULATE );
    Device->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
    Device->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE );
    Device->SetTextureStageState( 0, D3DTSS_MAGFILTER, D3DTEXF_LINEAR );
    Device->SetTextureStageState( 0, D3DTSS_MINFILTER, D3DTEXF_LINEAR );
    Device->SetRenderState( D3DRS_ZENABLE,          TRUE );
    Device->SetRenderState( D3DRS_AMBIENT,          0x00ffffff );
    Device->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE );
    Device->SetRenderState( D3DRS_LIGHTING,         TRUE );
    Device->SetRenderState( D3DRS_CULLMODE,         D3DCULL_NONE );
    Device->SetRenderState( D3DRS_NORMALIZENORMALS, TRUE );

    // Generate spheremap texture coords from the camera space normal. This has
    // two steps. First, tell D3D to use the vertex normal (in camera space) as
    // texture coordinates. Then, we setup a texture matrix to transform these
    // texcoords from (-1,+1) view space to (0,1) texture space. This way,
    // the normal can be used to look up a texel in the spheremap.
    D3DXMATRIX mat;
    mat._11 = 0.5f; mat._12 = 0.0f;
    mat._21 = 0.0f; mat._22 =-0.5f;
    mat._31 = 0.0f; mat._32 = 0.0f;
    mat._41 = 0.5f; mat._42 = 0.5f;
    Device->SetTransform( D3DTS_TEXTURE0, &mat );
    Device->SetTextureStageState( 0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT2 );
    Device->SetTextureStageState( 0, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_CAMERASPACENORMAL );

    // Finally, draw the object
    
    // Restore render states
    Device->SetTextureStageState( 0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE );
    Device->SetTextureStageState( 0, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_PASSTHRU );
    Device->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE );
    Device->SetRenderState( D3DRS_ZWRITEENABLE,     TRUE );

    FLOAT fWidth  = (FLOAT)d3dpp.BackBufferWidth;
    FLOAT fHeight = (FLOAT)d3dpp.BackBufferHeight;

    // Draw header
    XBUtil_DrawRect( 48, 36, fWidth-48, 81, 0x40000000, 0xff000000 );
    m_Font16.DrawText(  53, 36, 0xffffffff, L"Xbox Lightgun Tool" );
    m_Font12.DrawText(  53, 61, 0xffa0a0a0, L"Press B+BACK to change video mode" );
    m_Font16.DrawText( fWidth-53, 36, 0xffffffff, L"Control Test Page", XBFONT_RIGHT );
    m_Font12.DrawText( fWidth-53, 61, 0xffa0a0a0, L"Press B+START for next page", XBFONT_RIGHT );

    // Draw the labels for the buttons
    m_Font16.Begin();
    m_Font16.DrawText( fWidth-250,  85, m_Lightgun.wButtons & XINPUT_GAMEPAD_BACK  ? 0xffffff00: 0x80ffffff, GLYPH_BACK1_BUTTON GLYPH_BACK2_BUTTON, XBFONT_LEFT );
    m_Font16.DrawText( fWidth-150,  85, m_Lightgun.wButtons & XINPUT_GAMEPAD_START ? 0xffffff00: 0x80ffffff, GLYPH_START1_BUTTON GLYPH_START2_BUTTON, XBFONT_LEFT );
    m_Font16.DrawText( fWidth-250, 112, m_Lightgun.bAnalogButtons[XINPUT_GAMEPAD_Y]     ? 0xffffffff: 0x80ffffff, GLYPH_Y_BUTTON );
    m_Font16.DrawText( fWidth-250, 137, m_Lightgun.bAnalogButtons[XINPUT_GAMEPAD_X]     ? 0xffffffff: 0x80ffffff, GLYPH_X_BUTTON );
    m_Font16.DrawText( fWidth-250, 162, m_Lightgun.bAnalogButtons[XINPUT_GAMEPAD_WHITE] ? 0xffffffff: 0x80ffffff, GLYPH_WHITE_BUTTON );
    m_Font16.DrawText( fWidth-150, 112, m_Lightgun.bAnalogButtons[XINPUT_GAMEPAD_B]     ? 0xffffffff: 0x80ffffff, GLYPH_B_BUTTON );
    m_Font16.DrawText( fWidth-150, 137, m_Lightgun.bAnalogButtons[XINPUT_GAMEPAD_A]     ? 0xffffffff: 0x80ffffff, GLYPH_A_BUTTON );
    m_Font16.DrawText( fWidth-150, 162, m_Lightgun.bAnalogButtons[XINPUT_GAMEPAD_BLACK] ? 0xffffffff: 0x80ffffff, GLYPH_BLACK_BUTTON );
    m_Font16.End();
    
    // Draw status of the lightgun controls
    WCHAR strBuffer[100];
    m_Font12.Begin();

    // Show the current display mode
    m_Font12.DrawText( 48, fHeight-108, 0x80ffffff, m_strDisplayMode );

    // Show the calibration status
    if( m_Lightgun.IsUserCalibrated() )
        m_Font12.DrawText( 48, fHeight-88, 0xff80ff80, L"Lightgun is calibrated\nfor this mode" );
    else
        m_Font12.DrawText( 48, fHeight-88, 0xffff8080, L"Lightgun is not calibrated\n for this mode" );

    swprintf( strBuffer, L"LeftStick.x = %d", m_Lightgun.sThumbLX );
    m_Font12.DrawText(  48, 82, m_Lightgun.fX1!=0.0f ? 0xffffff00: 0x80ffffff, strBuffer );
    swprintf( strBuffer, L"LeftStick.y = %d", m_Lightgun.sThumbLY );
    m_Font12.DrawText(  48, 100, m_Lightgun.fY1!=0.0f ? 0xffffff00: 0x80ffffff, strBuffer );

    m_Font12.DrawText( 114, 128, m_Lightgun.wButtons & XINPUT_GAMEPAD_DPAD_UP    ? 0xffffff00: 0x80ffffff, L"Up",    XBFONT_CENTER_X );
    m_Font12.DrawText(  89, 146, m_Lightgun.wButtons & XINPUT_GAMEPAD_DPAD_LEFT  ? 0xffffff00: 0x80ffffff, L"Left",  XBFONT_CENTER_X );
    m_Font12.DrawText( 139, 146, m_Lightgun.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT ? 0xffffff00: 0x80ffffff, L"Right", XBFONT_CENTER_X );
    m_Font12.DrawText( 114, 164, m_Lightgun.wButtons & XINPUT_GAMEPAD_DPAD_DOWN  ? 0xffffff00: 0x80ffffff, L"Down",  XBFONT_CENTER_X );

    swprintf( strBuffer, L" = %d", m_Lightgun.bAnalogButtons[XINPUT_GAMEPAD_Y] );
    m_Font12.DrawText( fWidth-250+28, 116, m_Lightgun.bAnalogButtons[XINPUT_GAMEPAD_Y] ? 0xffffff00: 0x80ffffff, strBuffer );
    swprintf( strBuffer, L" = %d", m_Lightgun.bAnalogButtons[XINPUT_GAMEPAD_X] );
    m_Font12.DrawText( fWidth-250+28, 141, m_Lightgun.bAnalogButtons[XINPUT_GAMEPAD_X] ? 0xffffff00: 0x80ffffff, strBuffer );
    swprintf( strBuffer, L" = %d", m_Lightgun.bAnalogButtons[XINPUT_GAMEPAD_WHITE] );
    m_Font12.DrawText( fWidth-250+28, 166, m_Lightgun.bAnalogButtons[XINPUT_GAMEPAD_WHITE] ? 0xffffff00: 0x80ffffff, strBuffer );

    swprintf( strBuffer, L" = %d", m_Lightgun.bAnalogButtons[XINPUT_GAMEPAD_B] );
    m_Font12.DrawText( fWidth-150+28, 116, m_Lightgun.bAnalogButtons[XINPUT_GAMEPAD_B] ? 0xffffff00: 0x80ffffff, strBuffer );
    swprintf( strBuffer, L" = %d", m_Lightgun.bAnalogButtons[XINPUT_GAMEPAD_A] );
    m_Font12.DrawText( fWidth-150+28, 141, m_Lightgun.bAnalogButtons[XINPUT_GAMEPAD_A] ? 0xffffff00: 0x80ffffff, strBuffer );
    swprintf( strBuffer, L" = %d", m_Lightgun.bAnalogButtons[XINPUT_GAMEPAD_BLACK] );
    m_Font12.DrawText( fWidth-150+28, 166, m_Lightgun.bAnalogButtons[XINPUT_GAMEPAD_BLACK] ? 0xffffff00: 0x80ffffff, strBuffer );

    m_Font12.DrawText( fWidth-180, fHeight-108, m_Lightgun.wButtons & XINPUT_LIGHTGUN_ONSCREEN      ? 0xffffff00: 0x80ffffff, L"On Screen" );
    m_Font12.DrawText( fWidth-180, fHeight-88,  m_Lightgun.wButtons & XINPUT_LIGHTGUN_FRAME_DOUBLER ? 0xffffff00: 0x80ffffff, L"Frame Doubler" );
    m_Font12.DrawText( fWidth-180, fHeight-68,  m_Lightgun.wButtons & XINPUT_LIGHTGUN_LINE_DOUBLER  ? 0xffffff00: 0x80ffffff, L"Line Doubler" );

    m_Font12.End();

    return S_OK;
}




float getLightGunX() {
	float multx = 1.0f;
	float multy = 1.0f;
	if(HDMode == 0) {
		multx = 1.0f;
		multy = 1.0f;
	}
	if(HDMode == 1) {
		multx = 2.0f;
		multy = 1.5f;
	}
	if(HDMode == 2) {
		multx = 3.0f;
		multy = 2.25f;
	}

	FLOAT fWidth  = 640.0f * multx;
//	FLOAT fHeight = 480.0f * multy;
   
    FLOAT x = (fWidth/2) + (fWidth/2)*(m_Lightgun.sThumbLX+0.5f)/32767.5f;
	float xTarget = (float)x;

//		m_zapperDat[0] = ((fThumbLX - m_nScreenX)*256)/m_nScreenMaxX ;
//		m_zapperDat[1] = ((fThumbLY - m_nScreenY)*240)/m_nScreenMaxY ;
	
	xTarget = (xTarget * 256) / fWidth;
	return xTarget;
}

float getLightGunY() {
	float multx = 1.0f;
	float multy = 1.0f;
	if(HDMode == 0) {
		multx = 1.0f;
		multy = 1.0f;
	}
	if(HDMode == 1) {
		multx = 2.0f;
		multy = 1.5f;
	}
	if(HDMode == 2) {
		multx = 3.0f;
		multy = 2.25f;
	}

//	FLOAT fWidth  = 640.0f * multx;
	FLOAT fHeight = 480.0f * multy;
   
    FLOAT y = (fHeight/2) - (fHeight/2)*(m_Lightgun.sThumbLY+0.5f)/32767.5f;
	float yTarget = (float)y;
	yTarget = (yTarget * 224) / fHeight;
	return yTarget;
}

void drawCursor() {
	  FLOAT fWidth  = (FLOAT)d3dpp.BackBufferWidth;
    FLOAT fHeight = (FLOAT)d3dpp.BackBufferHeight;
	 // Draw crosshairs in red
    if( m_Lightgun.wButtons & XINPUT_LIGHTGUN_ONSCREEN )
    {
        FLOAT fThumbLX = (fWidth/2) + (fWidth/2)*(m_Lightgun.sThumbLX+0.5f)/32767.5f;
        FLOAT fThumbLY = (fHeight/2) - (fHeight/2)*(m_Lightgun.sThumbLY+0.5f)/32767.5f;
        XBUtil_DrawRect( fThumbLX-1, fThumbLY-8, fThumbLX+1, fThumbLY+8, 0xffff0000, 0xffff0000 );
        XBUtil_DrawRect( fThumbLX-8, fThumbLY-1, fThumbLX+8, fThumbLY+1, 0xffff0000, 0xffff0000 );
    }
		Device->SetTexture( 3, NULL );
		Device->SetTextureStageState( 3, D3DTSS_COLOROP, D3DTOP_DISABLE );
		Device->SetRenderState( D3DRS_POINTSPRITEENABLE, FALSE );
		Device->SetRenderState( D3DRS_POINTSCALEENABLE,  FALSE );
		Device->SetRenderState( D3DRS_ALPHABLENDENABLE,  FALSE );
}
HRESULT RenderCrosshairsTestPage()
{
    FLOAT fWidth  = (FLOAT)d3dpp.BackBufferWidth;
    FLOAT fHeight = (FLOAT)d3dpp.BackBufferHeight;

    RenderGradientBackground( 0xff00ffff, 0xff00ffff );
	 //RenderGradientBackground( 0xff000000, 0xff000000 );

    // Draw header
    XBUtil_DrawRect( 48, 36, fWidth-48, 81, 0x40000000, 0xff000000 );
    m_Font16.DrawText(  53, 36, 0xffffffff, L"Crosshairs Test Page" );
   // m_Font12.DrawText(  53, 61, 0xffa0a0a0, L"Press B+BACK to change video mode" );
    
	
	//m_Font16.DrawText( fWidth-83, 36, 0xffffffff, L"Crosshairs Test Page", XBFONT_RIGHT );
    //m_Font12.DrawText( fWidth-53, 61, 0xffa0a0a0, L"Press B+START for next page", XBFONT_RIGHT );
	 m_Font12.DrawText( fWidth-83, 61, 0xffffffff, L"Press Y to exit.", XBFONT_RIGHT );



    // Show the current display mode
    FLOAT x = (fWidth/2) + (fWidth/2)*(m_Lightgun.sThumbLX+0.5f)/32767.5f;
    FLOAT y = (fHeight/2) - (fHeight/2)*(m_Lightgun.sThumbLY+0.5f)/32767.5f;
	float xTarget = (float)x;
	float yTarget = (float)y;

	WCHAR strBuffer[100];
	swprintf( strBuffer, L"X = %f", xTarget );
    m_Font12.DrawText(  58, fHeight-148, 0xffffff00, strBuffer );
    swprintf( strBuffer, L"Y = %f", yTarget);
    m_Font12.DrawText(  58, fHeight-128, 0xffffff00, strBuffer );
    // Show the current display mode
	if(HDMode == 0) {
		WCHAR res[280]={0};
		wsprintfW(res,L"Current Resolution: 640x480 (480i/p)");
		m_Font12.DrawText( 58, fHeight-108, 0xffffffff,  res);
	}
	if(HDMode == 1) {
		WCHAR res[280]={0};
		wsprintfW(res,L"Current Resolution: 1280x720 (720p)");
		m_Font12.DrawText( 58, fHeight-108, 0xffffffff,  res);
	}
	if(HDMode == 2) {
		WCHAR res[280]={0};
		wsprintfW(res,L"Current Resolution: 1920x1080 (1080i)");
		m_Font12.DrawText( 58, fHeight-108, 0xffffffff,  res);
	}

    // Show the calibration status
    if( m_Lightgun.IsUserCalibrated() )
        m_Font12.DrawText( 48, fHeight-88, 0xff80ff80, L"Lightgun is calibrated\nfor this mode" );
    else
        m_Font12.DrawText( 48, fHeight-88, 0xffff8080, L"Lightgun is not calibrated\n for this mode" );

    // Draw instructions
    XBUtil_DrawRect( fWidth-286, 90, fWidth-48, 188, 0x40000000, 0xff000000 );
    m_Font12.DrawText( fWidth-281, 90, 0xffffffff, L"Press A and B together to\n"
                                                   L"calibrate the lightgun.\n"
                                                   L"\n" );

    // Draw crosshairs in red
    if( m_Lightgun.wButtons & XINPUT_LIGHTGUN_ONSCREEN )
    {
        FLOAT fThumbLX = (fWidth/2) + (fWidth/2)*(m_Lightgun.sThumbLX+0.5f)/32767.5f;
        FLOAT fThumbLY = (fHeight/2) - (fHeight/2)*(m_Lightgun.sThumbLY+0.5f)/32767.5f;
        XBUtil_DrawRect( fThumbLX-1, fThumbLY-8, fThumbLX+1, fThumbLY+8, 0xffff0000, 0xffff0000 );
        XBUtil_DrawRect( fThumbLX-8, fThumbLY-1, fThumbLX+8, fThumbLY+1, 0xffff0000, 0xffff0000 );
    }

    return S_OK;
}


void drawCrossHair() {
	float multx = 1.0f;
	float multy = 1.0f;
	if(HDMode == 0) {
		multx = 1.0f;
		multy = 1.0f;
	}
	if(HDMode == 1) {
		multx = 2.0f;
		multy = 1.5f;
	}
	if(HDMode == 2) {
		multx = 3.0f;
		multy = 2.25f;
	}

	FLOAT fWidth  = 640.0f * multx;
	FLOAT fHeight = 480.0f * multy;
	  if( m_Lightgun.wButtons & XINPUT_LIGHTGUN_ONSCREEN )
    {
        FLOAT fThumbLX = (fWidth/2) + (fWidth/2)*(m_Lightgun.sThumbLX+0.5f)/32767.5f;
        FLOAT fThumbLY = (fHeight/2) - (fHeight/2)*(m_Lightgun.sThumbLY+0.5f)/32767.5f;
        XBUtil_DrawRect( fThumbLX-1, fThumbLY-8, fThumbLX+1, fThumbLY+8, 0xffff0000, 0xffff0000 );
        XBUtil_DrawRect( fThumbLX-8, fThumbLY-1, fThumbLX+8, fThumbLY+1, 0xffff0000, 0xffff0000 );
    }
}

void drawCoords(int xTarget, int yTarget) {
	float multx = 1.0f;
	float multy = 1.0f;
	if(HDMode == 0) {
		multx = 1.0f;
		multy = 1.0f;
	}
	if(HDMode == 1) {
		multx = 2.0f;
		multy = 1.5f;
	}
	if(HDMode == 2) {
		multx = 3.0f;
		multy = 2.25f;
	}

//	FLOAT fWidth = 640.0f * multx;
	FLOAT fHeight = 480.0f * multy;
	WCHAR strBuffer[100];
	swprintf( strBuffer, L"X = %d", xTarget );
    m_Font12.DrawText(  58, fHeight-148, 0xffffff00, strBuffer );
    swprintf( strBuffer, L"Y = %d", yTarget);
    m_Font12.DrawText(  58, fHeight-128, 0xffffff00, strBuffer );
}


HRESULT RenderBulletHolesTestPage()
{
   

    RenderGradientBackground( 0xff00ff00, 0xff000000 );

	float multx = 1.0f;
	float multy = 1.0f;
	if(HDMode == 0) {
		multx = 1.0f;
		multy = 1.0f;
	}
	if(HDMode == 1) {
		multx = 2.0f;
		multy = 1.5f;
	}
	if(HDMode == 2) {
		multx = 3.0f;
		multy = 2.25f;
	}

	FLOAT fWidth = 640.0f * multx;
	FLOAT fHeight = 480.0f * multy;
    // Draw header
    XBUtil_DrawRect( 48, 36, fWidth-48, 81, 0x40000000, 0xff000000 );
    m_Font16.DrawText(  63, 46, 0xffffffff, L"Lightgun Configuration" );
   // m_Font12.DrawText(  53, 61, 0xffa0a0a0, L"Press B+BACK to change video mode" );
    m_Font12.DrawText( fWidth-83, 36, 0xffffffff, L"Press X for Crosshair mode", XBFONT_RIGHT );
    m_Font12.DrawText( fWidth-83, 61, 0xffffff00, L"Press Y to exit", XBFONT_RIGHT );

	//GetCalibrationTargetCoords( FLOAT* pfCenterTargetX,
      //                                      FLOAT* pfCenterTargetY,
        //                                    FLOAT* pfUpperLeftTargetX,
          //                                  FLOAT* pfUpperLeftTargetY )

	//FLOAT fThumbLX = (fWidth/2) + (fWidth/2)*(m_Lightgun.sThumbLX+0.5f)/32767.5f;

	
	/*
	FLOAT fWidth = 640.0f *VIDEOMODES[m_xboxVidmode].multx;
	FLOAT fHeight = 480.0f *VIDEOMODES[m_xboxVidmode].multy;
    FLOAT fThumbLX = (fWidth/2) + (fWidth/2)*(m_lightgun.sThumbLX+0.5f)/32767.5f;
    FLOAT fThumbLY = (fHeight/2) - (fHeight/2)*(m_lightgun.sThumbLY+0.5f)/32767.5f;


//game screen texture details:
//x=m_nScreenX 
//y=m_nScreenY
//w=m_nScreenMaxX
//h=m_nScreenMaxY
//native screen size is 256x240 - note the 256, 240 values below
//zapperdat[0] is game screen 0-255, zapperdat[1] is game screen 0-239

	if ( ( fThumbLX > m_nScreenX ) && ( fThumbLX < m_nScreenX + m_nScreenMaxX ) &&
		 ( fThumbLY > m_nScreenY ) && ( fThumbLY < m_nScreenY + m_nScreenMaxY ) )
	{
		m_zapperDat[0] = ((fThumbLX - m_nScreenX)*256)/m_nScreenMaxX ;
		m_zapperDat[1] = ((fThumbLY - m_nScreenY)*240)/m_nScreenMaxY ;
	}
	else //off screen
	{
		m_zapperDat[0] = 0 ;
		m_zapperDat[1] = 0 ;
	}


	 if(HDMode == 1) {//1280x720;
	 x = ((float)dx * 2.0f);
	 y = ((float)dy * 1.5f); 
  }
  if(HDMode == 2) { //1920x1080
	 x = ((float)dx * 3.0f);
	 y = ((float)dy * 2.25f); 
	*/
	
	//FLOAT fThumbLX =  (fWidth/2) + (fWidth/2)*(m_Lightgun.sThumbLX+0.5f)/32767.5f;
	//FLOAT fThumbLY = (fHeight/2) - (fHeight/2)*(m_Lightgun.sThumbLY+0.5f)/32767.5f;
//		m_zapperDat[0] = ((fThumbLX - m_nScreenX)*256)/m_nScreenMaxX ;
//		m_zapperDat[1] = ((fThumbLY - m_nScreenY)*240)/m_nScreenMaxY ;
	//float xTarget = ((fThumbLX - 0)*256)/640 ;
	//float yTarget = ((fThumbLY - 0)*224)/480 ;

	     
    FLOAT x = (fWidth/2) + (fWidth/2)*(m_Lightgun.sThumbLX+0.5f)/32767.5f;
    FLOAT y = (fHeight/2) - (fHeight/2)*(m_Lightgun.sThumbLY+0.5f)/32767.5f;
	float xTarget = (float)x;
	float yTarget = (float)y;
	
	WCHAR strBuffer[100];
	swprintf( strBuffer, L"X = %f", xTarget );
    m_Font12.DrawText(  58, fHeight-148, 0xffffff00, strBuffer );
    swprintf( strBuffer, L"Y = %f", yTarget);
    m_Font12.DrawText(  58, fHeight-128, 0xffffff00, strBuffer );
    // Show the current display mode
	if(HDMode == 0) {
		WCHAR res[280]={0};
		wsprintfW(res,L"Current Resolution: 640x480 (480i/p)");
		m_Font12.DrawText( 58, fHeight-108, 0x80ffffff,  res);
	}
	if(HDMode == 1) {
		WCHAR res[280]={0};
		wsprintfW(res,L"Current Resolution: 1280x720 (720p)");
		m_Font12.DrawText( 58, fHeight-108, 0x80ffffff,  res);
	}
	if(HDMode == 2) {
		WCHAR res[280]={0};
		wsprintfW(res,L"Current Resolution: 1920x1080 (1080i)");
		m_Font12.DrawText( 58, fHeight-108, 0x80ffffff,  res);
	}
	
    // Show the calibration status
    if( m_Lightgun.IsUserCalibrated() )
        m_Font12.DrawText( 58, fHeight-88, 0xff80ff80, L"Lightgun is calibrated\nfor this mode" );
    else
        m_Font12.DrawText( 58, fHeight-88, 0xffff8080, L"Lightgun is not calibrated\nfor this mode" );

    XBUtil_DrawRect( fWidth-286, 90, fWidth-48, 242, 0x40000000, 0xff000000 );
    m_Font12.DrawText( fWidth-281, 90, 0xffffffff, L"Point the lightgun away\n"
                                                   L"from the screen.\n"
                                                   L"\n"
                                                   L"Then press A and B together\n"
                                                   L"to calibrate the lightgun.\n"
                                                   L"\n"
                                                   L"Hold A and press START\n"
                                                   L"to clear the screen.\n" );

    // Draw some targets
    RenderTarget( fWidth*0.250f, fHeight*0.250f );
    RenderTarget( fWidth*0.375f, fHeight*0.375f );
    RenderTarget( fWidth*0.500f, fHeight*0.500f );
    RenderTarget( fWidth*0.625f, fHeight*0.625f );
    RenderTarget( fWidth*0.750f, fHeight*0.750f );

    // Draw bullet holes
    Device->SetTexture( 3, m_pBulletHolesTexture );
    Device->SetTextureStageState( 3, D3DTSS_COLOROP,   D3DTOP_SELECTARG1 );
    Device->SetTextureStageState( 3, D3DTSS_COLORARG1, D3DTA_TEXTURE );
    Device->SetTextureStageState( 3, D3DTSS_ALPHAOP,   D3DTOP_SELECTARG1 );
    Device->SetTextureStageState( 3, D3DTSS_ALPHAARG1, D3DTA_TEXTURE );
    Device->SetTextureStageState( 3, D3DTSS_MINFILTER, D3DTEXF_LINEAR );
    Device->SetTextureStageState( 3, D3DTSS_MAGFILTER, D3DTEXF_LINEAR );
    Device->SetRenderState( D3DRS_POINTSPRITEENABLE, TRUE );
    Device->SetRenderState( D3DRS_POINTSCALEENABLE,  FALSE );
    Device->SetRenderState( D3DRS_POINTSIZE,         FtoDW(16.0f) );

    // Turn on alphablending
    Device->SetRenderState( D3DRS_ZENABLE, FALSE );
    Device->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE ); 
    Device->SetRenderState( D3DRS_SRCBLEND,  D3DBLEND_SRCALPHA ); 
    Device->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA ); 
    Device->SetRenderState( D3DRS_ALPHATESTENABLE,  FALSE );
    Device->SetVertexShader( D3DFVF_XYZRHW );

    // Render particles
    D3DDevice::Begin( D3DPT_POINTLIST );
    for( DWORD i=0; i<min(100,m_dwNumBulletHoles); i++ )
    {
        D3DDevice::SetVertexData4f( D3DVSDE_VERTEX, m_vBulletHoleList[i].x, 
                                                    m_vBulletHoleList[i].y,
                                                    1.0f, 1.0f );
//		FLOAT x = m_vBulletHoleList[i].x;
//		FLOAT y = m_vBulletHoleList[i].y;

		//dprintf("X: %f, Y: %f\n", (float)x, (float)y);
    }
    D3DDevice::End();

    // Reset render states
    Device->SetTexture( 3, NULL );
    Device->SetTextureStageState( 3, D3DTSS_COLOROP, D3DTOP_DISABLE );
    Device->SetRenderState( D3DRS_POINTSPRITEENABLE, FALSE );
    Device->SetRenderState( D3DRS_POINTSCALEENABLE,  FALSE );
    Device->SetRenderState( D3DRS_ALPHABLENDENABLE,  FALSE );

	//if(drawCrossHairs == 1) {
	//	// Draw crosshairs in red
	//	if( m_Lightgun.wButtons & XINPUT_LIGHTGUN_ONSCREEN )
	//	{
	//		FLOAT fThumbLX = (fWidth/2) + (fWidth/2)*(m_Lightgun.sThumbLX+0.5f)/32767.5f;
	//		FLOAT fThumbLY = (fHeight/2) - (fHeight/2)*(m_Lightgun.sThumbLY+0.5f)/32767.5f;
	//		XBUtil_DrawRect( fThumbLX-1, fThumbLY-8, fThumbLX+1, fThumbLY+8, 0xffff0000, 0xffff0000 );
		//	XBUtil_DrawRect( fThumbLX-8, fThumbLY-1, fThumbLX+8, fThumbLY+1, 0xffff0000, 0xffff0000 );
	//	}
	//}

    return S_OK;
}


HRESULT RenderCalibrationPage()
{
    FLOAT fWidth  = (FLOAT)d3dpp.BackBufferWidth;
    FLOAT fHeight = (FLOAT)d3dpp.BackBufferHeight;

    // Draw a gradient filled background
    RenderGradientBackground( 0xff0000ff, 0xff0000ff );

    // Draw footer
    XBUtil_DrawRect( 48, fHeight-80, fWidth-48, fHeight-36, 0x40000000, 0xff000000 );
    m_Font16.DrawText( 53, fHeight-80, 0xffffffff, L"Lightgun Calibration" );

    // Draw instructions
    XBUtil_DrawRect( fWidth-286, fHeight-194, fWidth-48, fHeight-88, 0x40000000, 0xff000000 );
    if( m_bCalibratingCenter )
    {
        m_Font12.DrawText( fWidth-281, fHeight-194, 0xffffffff, 
                           L"Step 1: Point the lightgun\n"
                           L"at the target in the center\n"
                           L"of the screen and pull the\n"
                           L"trigger." );
    }
    else
    {
        m_Font12.DrawText( fWidth-281, fHeight-194, 0xffffffff, 
                           L"Step 2: Point the lightgun\n"
                           L"at the target in the upper\n"
                           L"left corner of the screen and\n"
                           L"pull the trigger." );
    }

    // Get the coordinates for the target
    FLOAT fTargetX, fTargetY;
    if( m_bCalibratingCenter )
        m_Lightgun.GetCalibrationTargetCoords( &fTargetX, &fTargetY, NULL, NULL );
    else
        m_Lightgun.GetCalibrationTargetCoords( NULL, NULL, &fTargetX, &fTargetY );

    // Draw the target
    RenderTarget( fTargetX, fTargetY );

    return S_OK;
}


//called every frame
HRESULT LightGunRender()
{
    if( m_dwNumInsertedLightguns != 1 )
    {
        RenderInsertRemoveControllerPage();
    }
    else
    {
        switch( m_AppState )
        {
            case APPSTATE_CONTROLTEST:     RenderControlTestPage();
                                           break;
            case APPSTATE_CROSSHAIRSTEST:  RenderCrosshairsTestPage();
                                           break;
            case APPSTATE_BULLETHOLESTEST: RenderBulletHolesTestPage();
                                           break;
            case APPSTATE_CALIBRATION:     RenderCalibrationPage();
                                           break;
        }
    }

    // Present the scene
    Device->Present( NULL, NULL, NULL, NULL );

    return S_OK;
}
