//===========================================================================
// DICPUTIL.CPP
//
// DirectInput CPL helper functions.
//
// Functions:
//  DIUtilGetJoystickTypeName()
//  DIUtilPollJoystick()
//
//===========================================================================

//===========================================================================
// (C) Copyright 1997 Microsoft Corp.  All rights reserved.
//
// You have a royalty-free right to use, modify, reproduce and
// distribute the Sample Files (and/or any modified version) in
// any way you find useful, provided that you agree that
// Microsoft has no warranty obligations or liability for any
// Sample Application Files which are modified.
//===========================================================================

#include "cplsvr1.h"
#include "dicputil.h"
#include <shlwapi.h>  // for Str... functions!
#include <malloc.h>	 // for _alloca

extern HWND ghDlg;
extern CDIGameCntrlPropSheet_X *pdiCpl;
extern HINSTANCE  ghInst;
extern CRITICAL_SECTION gcritsect;


//===========================================================================
// DIUtilPollJoystick
//
// Polls the joystick device and returns the device state.
//
// Parameters:
//  LPDIRECTINPUTDEVICE2    pdiDevice2  - ptr to device object
//  DIJOYSTATE              *pdijs      - ptr to store joystick state
//
// Returns: HRESULT
//
//===========================================================================
HRESULT DIUtilPollJoystick(LPDIRECTINPUTDEVICE2 pdiDevice2, LPDIJOYSTATE pdijs)
{
    // clear the pdijs memory
    // this way, if we fail, we return no data
    pdijs->lX = pdijs->lY = pdijs->lZ = pdijs->lRx = pdijs->lRy = pdijs->lRz = pdijs->rglSlider[0] = pdijs->rglSlider[1] = 0;

    // poll the joystick
    HRESULT hRes; 

    if( SUCCEEDED(hRes = pdiDevice2->Poll()) )
    {
        static BOOL bFirstPoll = TRUE;

        // This is to disreguard the first poll!
        // DINPUT sends garbage the first poll.
        if( bFirstPoll )
        {
            pdiDevice2->GetDeviceState(sizeof(DIJOYSTATE), pdijs);
            bFirstPoll = FALSE;
        }

        // query the device state
        if( FAILED(hRes = pdiDevice2->GetDeviceState(sizeof(DIJOYSTATE), pdijs)) )
        {
            if( hRes == DIERR_INPUTLOST )
            {
                if( SUCCEEDED(hRes = pdiDevice2->Acquire()) )
                    hRes = pdiDevice2->GetDeviceState(sizeof(DIJOYSTATE), pdijs);
            }
        }
    }

    // done
    return(hRes);
} // *** end DIUtilPollJoystick()

//===========================================================================
// InitDInput
//
// Initializes DirectInput objects
//
// Parameters:
//  HWND                    hWnd    - handle of caller's window
//  CDIGameCntrlPropSheet_X *pdiCpl - pointer to Game Controllers property
//                                      sheet object
//
// Returns: HRESULT
//
//===========================================================================
HRESULT InitDInput(HWND hWnd, CDIGameCntrlPropSheet_X *pdiCpl)
{
    HRESULT                 hRes = S_OK;
    LPDIRECTINPUTDEVICE2    pdiDevice2;
    LPDIRECTINPUTJOYCONFIG  pdiJoyCfg;
    LPDIRECTINPUT           pdi = 0;

    // protect ourselves from multithreading problems
    EnterCriticalSection(&gcritsect);

    // validate pdiCpl
    if( (IsBadReadPtr((void*)pdiCpl, sizeof(CDIGameCntrlPropSheet_X))) ||
        (IsBadWritePtr((void*)pdiCpl, sizeof(CDIGameCntrlPropSheet_X))) )
    {
#ifdef _DEBUG
        OutputDebugString(TEXT("GCDEF.DLL: InitDInput() - bogus pointer\n"));
#endif
        hRes = E_POINTER;
        goto exitinit;
    }

    // retrieve the current device object
    pdiCpl->GetDevice(&pdiDevice2);   

    // retrieve the current joyconfig object
    pdiCpl->GetJoyConfig(&pdiJoyCfg);   

    // have we already initialized DirectInput?
    if( (NULL == pdiDevice2) || (NULL == pdiJoyCfg) )
    {
        // no, create a base DirectInput object
        if( FAILED(hRes = DirectInputCreate(ghInst, DIRECTINPUT_VERSION, &pdi, NULL)) )
        {
#ifdef _DEBUG
            OutputDebugString(TEXT("GCDEF.DLL: DirectInputCreate() failed\n"));
#endif
            goto exitinit;
        }

        // have we already created a joyconfig object?
        if( NULL == pdiJoyCfg )
        {
            // no, create a joyconfig object
            if( SUCCEEDED(pdi->QueryInterface(IID_IDirectInputJoyConfig, (LPVOID*)&pdiJoyCfg)) )
            {
                if( SUCCEEDED(pdiJoyCfg->SetCooperativeLevel(hWnd, DISCL_EXCLUSIVE | DISCL_BACKGROUND)) )
                    pdiCpl->SetJoyConfig(pdiJoyCfg);
            } else
            {
#ifdef _DEBUG
                OutputDebugString(TEXT("GCDEF.DLL: Unable to create joyconfig\n"));
#endif
                goto exitinit;
            }
        }

        // have we already created a device object?
        if( NULL == pdiDevice2 )
        {
            // no, create a device object
            if( NULL != pdiJoyCfg )
            {
                LPDIRECTINPUTDEVICE  pdiDevTemp;
                LPDIJOYCONFIG_DX5    lpDIJoyConfig = (LPDIJOYCONFIG_DX5)_alloca(sizeof(DIJOYCONFIG_DX5));
                ASSERT (lpDIJoyConfig);

                // get the type name
                ZeroMemory(lpDIJoyConfig, sizeof(DIJOYCONFIG_DX5));

                // GetConfig will provide this information
                lpDIJoyConfig->dwSize = sizeof(DIJOYCONFIG_DX5);

                // Get the instance necessarey for CreateDevice
                if( SUCCEEDED(hRes = pdiJoyCfg->GetConfig(pdiCpl->GetID(), (LPDIJOYCONFIG)lpDIJoyConfig, DIJC_GUIDINSTANCE)) )
                {
                    // Create the device
                    if( SUCCEEDED(hRes = pdi->CreateDevice(lpDIJoyConfig->guidInstance, &pdiDevTemp, NULL)) )
                    {
                        // Query the device for the Device2 interface!
                        if( SUCCEEDED(hRes = pdiDevTemp->QueryInterface(IID_IDirectInputDevice2, (LPVOID*)&pdiDevice2)) )
                        {
                            // release the temporary object
                            pdiDevTemp->Release();

                            // Set the DataFormat and CooperativeLevel!
                            if( SUCCEEDED(hRes = pdiDevice2->SetDataFormat(&c_dfDIJoystick)) )
                                hRes = pdiDevice2->SetCooperativeLevel(hWnd, DISCL_EXCLUSIVE | DISCL_BACKGROUND);
                        }
                    }
                }

                if( SUCCEEDED(hRes) )
                {
                    // store the device object
                    pdiCpl->SetDevice(pdiDevice2);
                } else
                {
                    goto exitinit;
                }
            } else goto exitinit;
        }
    } else {
    	goto exitinit;
    }

    // if everything is Zero, either you've never enumerated or the enumeration is suspectable
    if( (pdiCpl->GetStateFlags()->nButtons == 0) &&
        (pdiCpl->GetStateFlags()->nAxis    == 0) &&
        (pdiCpl->GetStateFlags()->nPOVs    == 0) )
    {
        EnumDeviceObjects(pdiDevice2, pdiCpl->GetStateFlags());

        /*
        if (FAILED(pdiDevice2->EnumObjects((LPDIENUMDEVICEOBJECTSCALLBACK)DIEnumDeviceObjectsProc, (LPVOID *)pdiCpl->GetStateFlags(), DIDFT_ALL)))
        {
#ifdef _DEBUG
            OutputDebugString(TEXT("GCDEF.DLL: TEST.CPP: WM_INIT: EnumObjects FAILED!\n"));
#endif
        }
        */
    }

exitinit:
    // release the base DirectInput object
    if( pdi ) {
        pdi->Release();
    }

    // we're done
    LeaveCriticalSection(&gcritsect);
    return(hRes);

} //*** end InitDInput()



void OnHelp(LPARAM lParam)
{                  
    assert ( lParam );

    short nSize = STR_LEN_32;

    // point to help file
    LPTSTR pszHelpFileName = (LPTSTR) _alloca(sizeof(TCHAR[STR_LEN_32]));
    assert (pszHelpFileName);

    // returns help file name and size of string
    GetHelpFileName(pszHelpFileName, &nSize);

    if( ((LPHELPINFO)lParam)->iContextType == HELPINFO_WINDOW )
        WinHelp((HWND)((LPHELPINFO)lParam)->hItemHandle, pszHelpFileName, (UINT)HELP_WM_HELP, (ULONG_PTR)gaHelpIDs);
}

BOOL GetHelpFileName(TCHAR *lpszHelpFileName, short* nSize)
{
    if( LoadString(ghInst, IDS_HELPFILENAME, lpszHelpFileName, *nSize) )
        return(S_OK);
    else
        return(E_FAIL);
}

////////////////////////////////////////////////////////////////////////////////////////
//	OnContextMenu(WPARAM wParam)
////////////////////////////////////////////////////////////////////////////////////////
void OnContextMenu(WPARAM wParam)
{
    short nSize = STR_LEN_32;

    // point to help file
    LPTSTR pszHelpFileName = (LPTSTR) _alloca(sizeof(TCHAR[STR_LEN_32]));
    assert (pszHelpFileName);                      

    // returns help file name and size of string
    GetHelpFileName(pszHelpFileName, &nSize);

    WinHelp((HWND)wParam, pszHelpFileName, HELP_CONTEXTMENU, (ULONG_PTR)gaHelpIDs);
}

// Instead of enumerating via EnumObjects
void EnumDeviceObjects(LPDIRECTINPUTDEVICE2 pdiDevice2, STATEFLAGS *pStateFlags)
{
    LPDIDEVICEOBJECTINSTANCE_DX3 pDevObjInst = (LPDIDEVICEOBJECTINSTANCE_DX3) _alloca(sizeof(DIDEVICEOBJECTINSTANCE_DX3));
    assert (pDevObjInst);

    pDevObjInst->dwSize = sizeof(DIDEVICEOBJECTINSTANCE_DX3);

    const DWORD dwOffsetArray[] = {DIJOFS_X, DIJOFS_Y, DIJOFS_Z, DIJOFS_RX, DIJOFS_RY, DIJOFS_RZ, DIJOFS_SLIDER(0), DIJOFS_SLIDER(1)};

    // -1 is for 0 based dwOffsetArray!
    BYTE n = MAX_AXIS;

    do
    {
        if( SUCCEEDED(pdiDevice2->GetObjectInfo((LPDIDEVICEOBJECTINSTANCE)pDevObjInst, dwOffsetArray[--n], DIPH_BYOFFSET)) )
            pStateFlags->nAxis |= (HAS_X<<n);
    } while( n );


    n = MAX_BUTTONS;

    do
    {
        if( SUCCEEDED(pdiDevice2->GetObjectInfo((LPDIDEVICEOBJECTINSTANCE)pDevObjInst, DIJOFS_BUTTON(--n), DIPH_BYOFFSET)) )
            pStateFlags->nButtons |= (HAS_BUTTON1<<n);
    } while( n );


    n = MAX_POVS;

    do
    {
        if( SUCCEEDED(pdiDevice2->GetObjectInfo((LPDIDEVICEOBJECTINSTANCE)pDevObjInst, DIJOFS_POV(--n), DIPH_BYOFFSET)) )
            pStateFlags->nPOVs |= (HAS_POV1<<n);
    } while( n );
}

#define GETRANGE( n ) \
		pDIPropCal->lMin	  = lpMyRanges->jpMin.dw##n##;		\
		pDIPropCal->lCenter = lpMyRanges->jpCenter.dw##n##;	\
		pDIPropCal->lMax	  = lpMyRanges->jpMax.dw##n##;		\


void SetMyRanges(LPDIRECTINPUTDEVICE2 lpdiDevice2, LPMYJOYRANGE lpMyRanges, BYTE nAxis)
{
    LPDIPROPCAL pDIPropCal = (LPDIPROPCAL)_alloca(sizeof(DIPROPCAL));
    assert (pDIPropCal);

    pDIPropCal->diph.dwSize         = sizeof(DIPROPCAL);
    pDIPropCal->diph.dwHeaderSize = sizeof(DIPROPHEADER);
    pDIPropCal->diph.dwHow          = DIPH_BYOFFSET;

    const DWORD dwOffsetArray[] = {DIJOFS_X, DIJOFS_Y, DIJOFS_Z, DIJOFS_RX, DIJOFS_RY, DIJOFS_RZ, DIJOFS_SLIDER(0), DIJOFS_SLIDER(1)};
    BYTE n = 0;

    // You have to start with a "while" here because Reset to Default may not have Any Axis!!!
    while( nAxis )
    {
        if( nAxis & HAS_X )
        {
            GETRANGE(X);
        } else if( nAxis & HAS_Y )
        {
            GETRANGE(Y);
            n = 1;
        } else if( nAxis & HAS_Z )
        {
            GETRANGE(Z)
            n = 2;
        } else if( nAxis & HAS_RX )
        {
            GETRANGE(Rx);
            n = 3;
        } else if( nAxis & HAS_RY )
        {
            GETRANGE(Ry);
            n = 4;
        } else if( nAxis & HAS_RZ )
        {
            GETRANGE(Rz);
            n = 5;
        } else if( nAxis & HAS_SLIDER0 )
        {
            GETRANGE(S0);
            n = 6;
        } else if( nAxis & HAS_SLIDER1 )
        {
            GETRANGE(S1); 
            n = 7;
        }

        pDIPropCal->diph.dwObj = dwOffsetArray[n];

        VERIFY(SUCCEEDED(lpdiDevice2->SetProperty(DIPROP_CALIBRATION, &pDIPropCal->diph)));

        nAxis &= ~HAS_X<<n;
    }
}

// Removed 'till we calibrate POVs again!
void SetMyPOVRanges(LPDIRECTINPUTDEVICE2 pdiDevice2)
{
    DIPROPCALPOV *pDIPropCal = new (DIPROPCALPOV);
    assert (pDIPropCal);

    ZeroMemory(pDIPropCal, sizeof(*pDIPropCal));

    pDIPropCal->diph.dwSize = sizeof(*pDIPropCal);
    pDIPropCal->diph.dwHeaderSize = sizeof(DIPROPHEADER);
    pDIPropCal->diph.dwHow = DIPH_BYID; 
    pDIPropCal->diph.dwObj = DIDFT_POV; 

    memcpy( pDIPropCal->lMin, myPOV[POV_MIN], sizeof(pDIPropCal->lMin) );
    memcpy( pDIPropCal->lMax, myPOV[POV_MAX], sizeof(pDIPropCal->lMax) );
    
    if( FAILED(pdiDevice2->SetProperty(DIPROP_CALIBRATION, &pDIPropCal->diph)) )
    {
#if (defined(_DEBUG) || defined(DEBUG))
        OutputDebugString(TEXT("GCDEF.DLL: SetMyRanges: SetProperty failed to set POV!\n"));
#endif
    }

    if( pDIPropCal ) {
        delete (pDIPropCal);
    }
}


void SetTitle( HWND hDlg )
{
    // Set the title bar!
    LPDIRECTINPUTDEVICE2 pdiDevice2;
    pdiCpl->GetDevice(&pdiDevice2);

    DIPROPSTRING *pDIPropStr = new (DIPROPSTRING);
    ASSERT (pDIPropStr);

    ZeroMemory(pDIPropStr, sizeof(DIPROPSTRING));

    pDIPropStr->diph.dwSize       = sizeof(DIPROPSTRING);
    pDIPropStr->diph.dwHeaderSize = sizeof(DIPROPHEADER);
    pDIPropStr->diph.dwHow        = DIPH_DEVICE;

    if( SUCCEEDED(pdiDevice2->GetProperty(DIPROP_INSTANCENAME, &pDIPropStr->diph)) )
    {
        TCHAR  tszFormat[STR_LEN_64];
#ifndef _UNICODE
        CHAR   szOut[STR_LEN_128];
#endif

        LPWSTR lpwszTitle = new (WCHAR[STR_LEN_128]);
        ASSERT (lpwszTitle);

        // Shorten length, provide elipse, 
        if( wcslen(pDIPropStr->wsz) > 32 )
        {
            pDIPropStr->wsz[30] = pDIPropStr->wsz[31] = pDIPropStr->wsz[32] = L'.';
            pDIPropStr->wsz[33] = L'\0';
        }

        LoadString(ghInst, IDS_SHEETCAPTION, tszFormat, sizeof(tszFormat)/sizeof(tszFormat[0]));

#ifdef _UNICODE
        wsprintfW(lpwszTitle, tszFormat, pDIPropStr->wsz);
#else
        USES_CONVERSION;

        wsprintfA(szOut, tszFormat, W2A(pDIPropStr->wsz));
        StrCpyW(lpwszTitle, A2W(szOut));
#endif

        //SetWindowText(GetParent(hDlg), 
        ::SendMessage(GetParent(hDlg), WM_SETTEXT, 0, (LPARAM)(LPCTSTR)
#ifdef _UNICODE
                      lpwszTitle);
#else
                      W2A(lpwszTitle));
#endif 

        if( lpwszTitle )
            delete[] (lpwszTitle);
    }
#ifdef _DEBUG
    else OutputDebugString(TEXT("GCDEF.DLL: DICPUTIL.CPP: SetTitle: GetProperty Failed!\n"));
#endif

    if( pDIPropStr )
        delete (pDIPropStr);
}

BOOL Error(HWND hWnd, short nTitleID, short nMsgID)
{
    LPTSTR lptTitle = new TCHAR[STR_LEN_64];
    ASSERT (lptTitle);

    BOOL bRet = FALSE;

    if( LoadString(ghInst, nTitleID, lptTitle, STR_LEN_64) )
    {
        LPTSTR lptMsg = (LPTSTR)_alloca(sizeof(TCHAR[STR_LEN_128]));
        ASSERT (lptMsg);

        if( LoadString(ghInst, nMsgID, lptMsg, STR_LEN_128) )
        {
            MessageBox(hWnd, lptMsg, lptTitle, MB_ICONHAND | MB_OK);
            bRet = TRUE;
        }
    }

    if( lptTitle )
        delete[] (lptTitle);

    return(bRet);
}

void CenterDialog(HWND hWnd)
{
    RECT rc;
    HWND hParentWnd = GetParent(hWnd);

    GetWindowRect(hParentWnd, &rc);

    // Centre the Dialog!
    SetWindowPos(hParentWnd, NULL, 
                 (GetSystemMetrics(SM_CXSCREEN) - (rc.right-rc.left))>>1, 
                 (GetSystemMetrics(SM_CYSCREEN) - (rc.bottom-rc.top))>>1, 
                 NULL, NULL, SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW);
}


#define SETRANGE( n ) \
      lpMyRanges->jpMin.dw##n##    = pDIPropRange->lMin;		\
      lpMyRanges->jpCenter.dw##n## = pDIPropRange->lCenter;	\
      lpMyRanges->jpMax.dw##n##    = pDIPropRange->lMax;		\

//===========================================================================
// BOOL GetMyRanges( LPMYJOYRANGE lpMyRanges, LPDIRECTINPUTDEVICE2 pdiDevice2, BYTE nAxis)
//
// Parameters:
//    LPMYJOYRANGE         lpMyRanges - Structure to fill with ranges
//    LPDIRECTINPUTDEVICE2 pdiDevice2 - Device in which axis ranges are requested
//    BYTE                 nAxis      - Bit mask of axis ranges to retrieve
//
// Returns: FALSE if failed
//
//===========================================================================
void GetMyRanges(LPDIRECTINPUTDEVICE2 lpdiDevice2, LPMYJOYRANGE lpMyRanges, BYTE nAxis)
{
    // Use DIPROPCAL to retrieve Range Information
    // Don't use DIPROPRANGE, as it doesn't have Center!
    LPDIPROPCAL pDIPropRange = (LPDIPROPCAL)_alloca(sizeof(DIPROPCAL));
    assert(pDIPropRange);

    pDIPropRange->diph.dwSize       = sizeof(DIPROPCAL);
    pDIPropRange->diph.dwHeaderSize = sizeof(DIPROPHEADER);
    pDIPropRange->diph.dwHow        = DIPH_BYOFFSET;

    const DWORD dwOffsetArray[] = {DIJOFS_X, DIJOFS_Y, DIJOFS_Z, DIJOFS_RX, DIJOFS_RY, DIJOFS_RZ, DIJOFS_SLIDER(0), DIJOFS_SLIDER(1)};
    BYTE nIndex = 0;

    // Zero out the buffer members and the index!
    pDIPropRange->lMin = pDIPropRange->lCenter = pDIPropRange->lMax = 0;

    // You don't have to start with "while" here because Reset to Default does not call this function!!1
    do
    {
        if( nAxis & HAS_X )
        {
            pDIPropRange->diph.dwObj = dwOffsetArray[nIndex = 0];

            if( SUCCEEDED(lpdiDevice2->GetProperty(DIPROP_CALIBRATION, &pDIPropRange->diph)) )
            {
                SETRANGE(X);
            }
        } else if( nAxis & HAS_Y )
        {
            pDIPropRange->diph.dwObj = dwOffsetArray[nIndex = 1];

            if( SUCCEEDED(lpdiDevice2->GetProperty(DIPROP_CALIBRATION, &pDIPropRange->diph)) )
            {
                SETRANGE(Y);
            }
        } else if( nAxis & HAS_Z )
        {
            pDIPropRange->diph.dwObj = dwOffsetArray[nIndex = 2];

            if( SUCCEEDED(lpdiDevice2->GetProperty(DIPROP_CALIBRATION, &pDIPropRange->diph)) )
            {
                SETRANGE(Z);
            }
        } else if( nAxis & HAS_RX )
        {
            pDIPropRange->diph.dwObj = dwOffsetArray[nIndex = 3];

            if( SUCCEEDED(lpdiDevice2->GetProperty(DIPROP_CALIBRATION, &pDIPropRange->diph)) )
            {
                SETRANGE(Rx);
            }
        } else if( nAxis & HAS_RY )
        {
            pDIPropRange->diph.dwObj = dwOffsetArray[nIndex = 4];

            if( SUCCEEDED(lpdiDevice2->GetProperty(DIPROP_CALIBRATION, &pDIPropRange->diph)) )
            {
                SETRANGE(Ry);
            }
        } else if( nAxis & HAS_RZ )
        {
            pDIPropRange->diph.dwObj = dwOffsetArray[nIndex = 5];

            if( SUCCEEDED(lpdiDevice2->GetProperty(DIPROP_CALIBRATION, &pDIPropRange->diph)) )
            {
                SETRANGE(Rz);
            }
        } else if( nAxis & HAS_SLIDER0 )
        {
            pDIPropRange->diph.dwObj = dwOffsetArray[nIndex = 6];

            if( SUCCEEDED(lpdiDevice2->GetProperty(DIPROP_CALIBRATION, &pDIPropRange->diph)) )
            {
                SETRANGE(S0);
            }
        } else if( nAxis & HAS_SLIDER1 )
        {
            pDIPropRange->diph.dwObj = dwOffsetArray[nIndex = 7];

            if( SUCCEEDED(lpdiDevice2->GetProperty(DIPROP_CALIBRATION, &pDIPropRange->diph)) )
            {
                SETRANGE(S1); 
            }
        } else {
            break;
        }
    } while( nAxis &= ~HAS_X<<nIndex );
}

void PostDlgItemEnableWindow(HWND hDlg, USHORT nItem, BOOL bEnabled)
{
    HWND hCtrl = GetDlgItem(hDlg, nItem);

    if( hCtrl )
        PostEnableWindow(hCtrl, bEnabled);
}

void PostEnableWindow(HWND hCtrl, BOOL bEnabled)
{
    DWORD dwStyle = GetWindowLong(hCtrl, GWL_STYLE);

    // No point Redrawing the Window if there's no change!
    if( bEnabled )
    {
        if( dwStyle & WS_DISABLED )
            dwStyle &= ~WS_DISABLED;
        else return;
    } else
    {
        if( !(dwStyle & WS_DISABLED) )
            dwStyle |=  WS_DISABLED;
        else return;
    }

    SetWindowLongPtr(hCtrl, GWL_STYLE, (LONG_PTR)dwStyle);

    RedrawWindow(hCtrl, NULL, NULL, RDW_INTERNALPAINT | RDW_INVALIDATE); 
}

void CopyRange( LPJOYRANGE lpjr, LPMYJOYRANGE lpmyjr )
{
    memcpy( &lpjr->jpMin,    &lpmyjr->jpMin, sizeof(JOYPOS) );
    memcpy( &lpjr->jpCenter, &lpmyjr->jpCenter, sizeof(JOYPOS) );
    memcpy( &lpjr->jpMax,    &lpmyjr->jpMax, sizeof(JOYPOS) );

}