//===========================================================================
// dmtcfg.cpp
//
// File / code creation functionality
//
// Functions:
//  dmtcfgCreatePropertySheet
//  dmtcfgDlgProc
//  dmtcfgOnInitDialog
//  dmtcfgOnClose
//  dmtcfgOnCommand
//  dmtcfgOnNotify
//  dmtcfgCreateGenreList
//  dmtcfgFreeGenreList
//  dmtcfgCreateSubGenreList
//  dmtcfgFreeSubGenreList
//  dmtcfgCreateActionList
//  dmtcfgFreeActionList
//
// History:
//  08/20/1999 - davidkl - created
//===========================================================================

#include "dimaptst.h"
#include "dmtinput.h"
//#include "dmtwrite.h"
#include "dmtcfg.h"

//---------------------------------------------------------------------------


//===========================================================================
// dmtcfgCreatePropertySheet
//
// Create property sheet dialog for device action map configuration
//
// Parameters:
//  HINSTANCE   hinst           - app instance handle
//  HWND        hwndParent      - parent window handle
//  LPSTR       szSelectedGenre 
//  DMTGENRE_NODE*   pGenreList
//  DMTGENRE_NODE*   pDeviceNode
//  BOOL        fStartWithDefaults
//
// Returns: HRESULT
//
// History:
//  08/23/1999 - davidkl - created
//  09/08/1999 - davidkl - changed param list
//===========================================================================
HRESULT dmtcfgCreatePropertySheet(HINSTANCE hinst, 
                                HWND hwndParent,
                                LPSTR szSelectedGenre,
                                DMTGENRE_NODE *pGenreList,
                                DMTDEVICE_NODE *pDeviceNode,
                                BOOL fStartWithDefaults)
{
    HRESULT             hRes        = S_OK;
    UINT                u           = 0;
    UINT                uSel        = 0;
    DMTGENRE_NODE       *pNode      = NULL;
    PROPSHEETPAGEA      *pPages     = NULL;
    PROPSHEETHEADERA    psh;
    char                szCaption[MAX_PATH];
    DMT_APPINFO         *pdmtai     = NULL;
    DMTDEVICE_NODE      dmtd;

    // validate pGenreList
    if(IsBadReadPtr((void*)pGenreList, sizeof(DMTGENRE_NODE)))
    {
        DPF(0, "dmtcfgCreatePropertySheet - invalid pGenreList (%016Xh)",
            pGenreList);
        return E_POINTER;
    }

    // validate pDeviceNode
    if(IsBadReadPtr((void*)pDeviceNode, sizeof(DMTDEVICE_NODE)))
    {
        DPF(0, "dmtcfgCreatePropertySheet - invalid pDeviceNode (%016Xh)",
            pDeviceNode);
        return E_POINTER;
    }

    __try
    {
        // count the genres
        //
        // find the node we care about
        u = 0;
        pNode = pGenreList;
        while(pNode)
        {
            // if we find our genre, start on that page
            if(!lstrcmpiA(szSelectedGenre, pNode->szName))
            {
                uSel = u;    
            }

            // increment the number of genres
            u++;

            pNode = pNode->pNext;
        }

        // allocate the page array (dw pages)
        pPages = (PROPSHEETPAGEA*)LocalAlloc(LMEM_FIXED,
                                        sizeof(PROPSHEETPAGEA) * u);
        if(!pPages)
        {
            DPF(0, "dmtcfgCreatePropertySheet - insufficient mempory to "
                "allocate pPages array");
            hRes = E_OUTOFMEMORY;
            __leave;
        }

        // add device name to caption
        wsprintfA(szCaption, 
                "Configure Device Action Map - %s",
                pDeviceNode->szName);


        // strip the next ptr from the selected device node
        CopyMemory((void*)&dmtd, (void*)pDeviceNode, sizeof(DMTDEVICE_NODE));
        dmtd.pNext = NULL;

        // allocate app info data struct for pages
        pdmtai = (DMT_APPINFO*)LocalAlloc(LMEM_FIXED,
                                        u * sizeof(DMT_APPINFO));
		if(!pdmtai)
		{
			hRes = E_OUTOFMEMORY;
			__leave;
		}

		ZeroMemory((void*)pdmtai, u * sizeof(DMT_APPINFO));

        // prepare property sheet header
	    psh.dwSize              = sizeof(PROPSHEETHEADERA);
	    psh.dwFlags             = PSH_PROPSHEETPAGE     | 
                                PSP_USETITLE | PSH_NOAPPLYNOW;
	    psh.hwndParent          = hwndParent;
	    psh.hInstance           = hinst;
	    psh.pszCaption          = szCaption;
	    psh.nPages              = u;
	    psh.nStartPage			= uSel;
	    psh.ppsp                = pPages;

        // describe sheets  
        pNode = pGenreList;
        for(u = 0; u < (DWORD)(psh.nPages); u++)
        {
            if(!pNode)
            {
                DPF(0, "dmtcfgCreatePropertySheet - we messed up! "
                    "we allocated less than %d pages",
                    psh.nPages);
                DPF(0, "PLEASE find someone to look at this NOW");
                hRes = E_UNEXPECTED;
                DebugBreak();
                __leave;
            }

            // populate the app info for the page
            (pdmtai + u)->pGenreList            = pNode;
            (pdmtai + u)->pDeviceList           = &dmtd;
            (pdmtai + u)->fStartWithDefaults    = fStartWithDefaults;
            (pdmtai + u)->fLaunchCplEditMode    = FALSE;

            // populate the page array entry
            ZeroMemory((void*)(pPages + u), sizeof(PROPSHEETPAGEA));
	        (pPages + u)->dwSize        = sizeof(PROPSHEETPAGEA);
			(pPages + u)->dwFlags       = PSP_USETITLE;
    	    (pPages + u)->hInstance     = hinst;
    	    (pPages + u)->pszTemplate   = MAKEINTRESOURCEA(IDD_CONFIGURE_MAPPING_PAGE);
    	    (pPages + u)->pfnDlgProc    = (DLGPROC)dmtcfgDlgProc;
            (pPages + u)->pszTitle      = pNode->szName;
            (pPages + u)->lParam        = (LPARAM)(pdmtai + u);

            // next node
            pNode = pNode->pNext;
        }

        // create this thing
        if(0 > PropertySheetA(&psh))
        {
            DPF(0, "dmtcfgCreatePropertySheet - dialog creation failed (%08Xh)",
                GetLastError());
            hRes = E_UNEXPECTED;
            __leave;
        }
    }
    __finally
    {
        // free the app info array
        if(pdmtai)
        {
            if(LocalFree((HLOCAL)pdmtai))
            {
                DPF(0, "dmtcfgCreaatePropertySheet - !!!MEMORY LEAK!!! "
                    "LocalFree(pdmtai) failed (%08X)",
                    GetLastError());
                hRes = S_FALSE;
            }
            pdmtai = NULL;
        }

        // free the page array
        if(pPages)
        {
            if(LocalFree((HLOCAL)pPages))
            {
                DPF(0, "dmtcfgCreaatePropertySheet - !!!MEMORY LEAK!!! "
                    "LocalFree(pPages) failed (%08X)",
                    GetLastError());
                hRes = S_FALSE;
            }
            pPages = NULL;
        }

    }

    // done
    return hRes;

} //*** end dmtcfgCreatePropertySheet()



//===========================================================================
// dmtcfgDlgProc
//
// Configure Device Action Map dialog processing function
//
// Parameters: (see SDK help for parameter details)
//  HWND    hwnd
//  UINT    uMsg
//  WPARAM  wparam
//  LPARAM  lparam
//
// Returns: (see SDK help for return value details)
//  BOOL
//
// History:
//  08/20/1999 - davidkl - created  
//===========================================================================
BOOL CALLBACK dmtcfgDlgProc(HWND hwnd,
                            UINT uMsg,
                            WPARAM wparam,
                            LPARAM lparam)
{
    switch(uMsg)
    {
        case WM_INITDIALOG:
            return dmtcfgOnInitDialog(hwnd, 
                                    (HWND)wparam, 
                                    lparam);

        case WM_COMMAND:
            return dmtcfgOnCommand(hwnd,
                                    LOWORD(wparam),
                                    (HWND)lparam,
                                    HIWORD(wparam));

        case WM_NOTIFY:
            return dmtcfgOnNotify(hwnd,
                                (PSHNOTIFY *)lparam);

        case WM_DMT_UPDATE_LISTS:
            return dmtcfgOnUpdateLists(hwnd);

        }

    return FALSE;

} //*** end dmtcfgDlgProc()


//===========================================================================
// dmtcfgOnInitDialog
//
// Handle WM_INITDIALOG processing for the config device box
//
// Parameters:
//  HWND    hwnd        - handle to property page
//  HWND    hwndFocus   - handle of ctrl with focus
//  LPARAM  lparam      - user data (in this case, PROPSHEETPAGE*)
//
// Returns: BOOL
//
// History:
//  08/20/1999 - davidkl - created
//===========================================================================
BOOL dmtcfgOnInitDialog(HWND hwnd, 
                        HWND hwndFocus, 
                        LPARAM lparam)
{
    HRESULT             hRes        = S_OK;
    PROPSHEETPAGEA      *ppsp       = (PROPSHEETPAGEA*)lparam;
    DMTGENRE_NODE       *pGenre     = NULL;
    DMTSUBGENRE_NODE    *pSubNode   = NULL;
    DMTMAPPING_NODE     *pMapNode   = NULL;
    //LONG                lPrev       = 0L;
	//JJ 64Bit Compat
	LONG_PTR			lPrev		= 0;
   // int                 nIdx        = 0;
	LONG_PTR			nIdx		= 0;
    DMTDEVICE_NODE      *pDevice    = NULL;
    DMT_APPINFO         *pdmtai     = NULL;
    UINT                u           = 0;
    WORD                wTypeCtrl   = 0;
    DIACTIONFORMATA     diaf;
    
    DPF(5, "dmtcfgOnInitDialog");

    // validate ppsp (lparam)
    if(IsBadWritePtr((void*)ppsp, sizeof(PROPSHEETPAGEA)))
    {
        DPF(0, "dmtcfgOnInitDialog - invalid lParam (%016Xh)",
            ppsp);
        return FALSE;
    }

    // pdmtai == ppsp->lParam
    pdmtai = (DMT_APPINFO*)(ppsp->lParam);

    // validate pdmtai
    if(IsBadWritePtr((void*)pdmtai, sizeof(DMT_APPINFO)))
    {
        DPF(0, "dmtcfgOnInitDialog - invalid ppsp.ptp (%016Xh)",
            pdmtai);
        return FALSE;
    }

    // pGenre == pdmtai->pGenreList
    pGenre = pdmtai->pGenreList;

    // valdiate pGenre
    if(IsBadWritePtr((void*)pGenre, sizeof(DMTGENRE_NODE)))
    {
        DPF(0, "dmtcfgOnInitDialog - invalid pGenre (%016Xh)",
            pGenre);
        return FALSE;
    }

    // pDevice == pdmtai->pDeviceList
    pDevice = pdmtai->pDeviceList;
    // valdiate pGenre
    if(IsBadWritePtr((void*)pDevice, sizeof(DMTDEVICE_NODE)))
    {
        DPF(0, "dmtcfgOnInitDialog - invalid pDevice (%016Xh)",
            pDevice);
        return FALSE;
    }

    // change the property sheet dialog button text
    // Ok -> Save
    SetWindowTextA(GetDlgItem(GetParent(hwnd), IDOK),
                "&Save");
    // Apply -> Load
    //SetWindowTextA(GetDlgItem(GetParent(hwnd), IDC_PS_APPLY),
    //              "Load");
    // Cancel -> Close
    SetWindowTextA(GetDlgItem(GetParent(hwnd), IDCANCEL),
                "&Close");

    __try
    {
        // store the app info in the property page's user data
        SetLastError(0);
        //lPrev = SetWindowLong(hwnd, 
          //                  GWL_USERDATA, 
            //                (LONG)pdmtai);
		//JJ 64Bit Compat
		lPrev = SetWindowLongPtr(hwnd, 
								 GWLP_USERDATA, 
								(LONG_PTR)pdmtai);
        if(!lPrev && GetLastError())
        {
            // serious app problem.  
            //  we need to stop things right here and now
            DPF(0, "dmtcfgOnInitDialog - This is bad... "
                "We failed to store pdmtai");
            DPF(0, "dmtcfgOnInitDialog  - Please find someone "
                "to look at this right away");
            DebugBreak();
            hRes = E_FAIL;
            __leave;
        }

        // walk the list and populate the subgenre list box
        //
        // store the ptr to the subgenre node in the listbox 
        //  entry user data
        pSubNode = pGenre->pSubGenreList;
        while(pSubNode)
        {
            // add the subgenre name to the list
            nIdx = SendMessageA(GetDlgItem(hwnd, IDC_SUBGENRE),
                                CB_ADDSTRING,
                                0,
                                (LPARAM)(pSubNode->szName));
        
            // store the subgenre node in the list entry
            SendMessageA(GetDlgItem(hwnd, IDC_SUBGENRE),
                        CB_SETITEMDATA,
                        nIdx,
                        (LPARAM)pSubNode);

            // if the user has requested default mappings
            //  get them for the specified device
            if(pdmtai->fStartWithDefaults)
            {
                // walk the mappings list until the selected
                //  device is found
                pMapNode = pSubNode->pMappingList;
                while(pMapNode)
                {
                    // try to match on guidInstance
                    if(IsEqualGUID(pDevice->guidInstance,
                                pMapNode->guidInstance))
                    {
                        // match found
                        break;
                    }

                    // next mapping
                    pMapNode = pMapNode->pNext;
                }
                
                if(pMapNode)
                {
                    ZeroMemory((void*)&diaf, sizeof(DIACTIONFORMATA));
                    diaf.dwSize                 = sizeof(DIACTIONFORMATA);
                    diaf.dwActionSize           = sizeof(DIACTIONA);
                    diaf.dwNumActions           = (DWORD)(pMapNode->uActions);
                    diaf.rgoAction              = pMapNode->pdia;
                    diaf.dwDataSize             = 4 * diaf.dwNumActions;
                    diaf.guidActionMap          = GUID_DIMapTst;
                    diaf.dwGenre                = pSubNode->dwGenreId;
                    diaf.dwBufferSize           = DMTINPUT_BUFFERSIZE;
                    lstrcpyA(diaf.tszActionMap, DMT_APP_CAPTION);

                    // get the default mappings
                    hRes = (pDevice->pdid)->BuildActionMap(&diaf,
                                                        (LPCSTR)NULL,
                                                        DIDBAM_HWDEFAULTS);
                    if(FAILED(hRes))
                    {
                       // ISSUE-2001/03/29-timgill Needs error case handling
                    }
                }
                else
                {
                    // ISSUE-2001/03/29-timgill needs error handling
                }

            }

            // next subgenre
            pSubNode = pSubNode->pNext;
        }

        // set the subgenre list selection
        SendMessageA(GetDlgItem(hwnd, IDC_SUBGENRE),
                    CB_SETCURSEL,
                    0,
                    0);

        // selectively disable axis/button/pov radio buttons
        //
        // this is done if the selected device does not
        //  actually have one of these objects
        //
        // since axes are our "prefered" initial display
        //  option, check them last
        if(dmtinputDeviceHasObject(pDevice->pObjectList,
                                        DMTA_TYPE_POV))
        {
            EnableWindow(GetDlgItem(hwnd, IDC_TYPE_POV), TRUE);
            wTypeCtrl = IDC_TYPE_POV;
        }
        if(dmtinputDeviceHasObject(pDevice->pObjectList,
                                        DMTA_TYPE_BUTTON))
        {
            EnableWindow(GetDlgItem(hwnd, IDC_TYPE_BUTTON), TRUE);
            wTypeCtrl = IDC_TYPE_BUTTON;
        }
        if(dmtinputDeviceHasObject(pDevice->pObjectList,
                                        DMTA_TYPE_AXIS))
        {
            EnableWindow(GetDlgItem(hwnd, IDC_TYPE_AXIS), TRUE);
            wTypeCtrl = IDC_TYPE_AXIS;
        }


        // select the axes radio button
        if(0 == wTypeCtrl)
        {
            // we have a "device" that has no objects...
            //
            // this is very bad
            DebugBreak();
            return TRUE;
        }

        CheckRadioButton(hwnd,
                        IDC_TYPE_POV,
                        IDC_TYPE_AXIS,
                        wTypeCtrl);

        // for the default subgenre, walk the list and populate 
        //  the actions list box
        //
        // store the ptr to the actions node in the listbox 
        //  entry user data
        pSubNode = (DMTSUBGENRE_NODE*)SendMessageA(GetDlgItem(hwnd, IDC_SUBGENRE),
                                                CB_GETITEMDATA,
                                                0,
                                                0L);

        // update the lists
        SendMessageA(hwnd,
                    WM_DMT_UPDATE_LISTS,
                    0,
                    0L);

        // select the first entry in each list
        SendMessageA(GetDlgItem(hwnd, IDC_CONTROLS),
                    LB_SETCURSEL,
                    0,
                    0L);
        SendMessageA(GetDlgItem(hwnd, IDC_ACTIONS),
                    LB_SETCURSEL,
                    0,
                    0L);

        // display the subgenre description
        SetDlgItemTextA(hwnd,
                        IDC_DESCRIPTION,
                        pSubNode->szDescription);

        // make sure the map/unmap buttons are enabled correctly
        SendMessageA(hwnd,
                    WM_COMMAND,
                    IDC_CONTROLS,
                    0L);

    }
    __finally
    {
        // if failure case, clean house
        if(FAILED(hRes))
        {
            // ISSUE-2001/03/29-timgill Needs error case handling
        }
    }
    
    // done
    return TRUE;

} //*** end dmtcfgOnInitDialog()


//===========================================================================
// dmtcfgOnCommand
//
// Handle WM_COMMAND processing for the config device box
//
// Parameters:
//  HWND    hwnd        - handle to property page
//  WORD    wId         - control identifier    (LOWORD(wparam))
//  HWND    hwndCtrl    - handle to control     ((HWND)lparam)
//  WORD    wNotifyCode - notification code     (HIWORD(wparam))
//
// Returns: BOOL
//
// History:
//  08/20/1999 - davidkl - created
//===========================================================================
BOOL dmtcfgOnCommand(HWND hwnd,
                    WORD wId,
                    HWND hwndCtrl,
                    WORD wNotifyCode)
{
    HRESULT             hRes            = S_OK;
   // UINT                uSel            = 0; 
	//JJ 64Bit Compat
	UINT_PTR			uSel			= 0;
    UINT                uActions        = 0;   
    BOOL                fEnable         = FALSE;
    DMT_APPINFO         *pdmtai         = NULL;
    DMTSUBGENRE_NODE    *pSubGenre      = NULL;
    DMTMAPPING_NODE     *pMapping       = NULL;
    DIACTIONA           *pdia           = NULL;

	DPF(5, "dmtcfgOnCommand");

    // get the window data   
    //pdmtai = (DMT_APPINFO*)GetWindowLong(hwnd, GWL_USERDATA);
	//JJ 64Bit Compat
	pdmtai = (DMT_APPINFO*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
    if(!pdmtai)
    {
        // big problem
        //
        // this should NEVER happen
       // ISSUE-2001/03/29-timgill Needs error case handling
    }

    // what is the currently selected subgenre?
    uSel = SendMessageA(GetDlgItem(hwnd, IDC_SUBGENRE),
                    CB_GETCURSEL,
                    0,
                    0L);
    pSubGenre = (DMTSUBGENRE_NODE*)SendMessageA(GetDlgItem(hwnd, 
                                                        IDC_SUBGENRE),
                                            CB_GETITEMDATA,
                                            uSel,
                                            0L);
    if(!pSubGenre)
    {
        // big problem
        //
        // this should NEVER happen
        // ISSUE-2001/03/29-timgill Needs error case handling
    }

    // get the active DIACTION array
    pMapping = pSubGenre->pMappingList;
    while(pMapping)
    {
        // match pdmtai->pDeviceList->guidInstance with
        //  pMapping->guidInstance
        if(IsEqualGUID(pdmtai->pDeviceList->guidInstance,
                    pMapping->guidInstance))
        {
            break;
        }

        // next mapping
        pMapping = pMapping->pNext;
    }

    if(pMapping)
    {
        pdia = pMapping->pdia;
        uActions = pMapping->uActions;
    }

    // update genre description
    SetDlgItemTextA(hwnd,
                    IDC_DESCRIPTION,
                    pSubGenre->szDescription);

    switch(wId)
    {
        case IDC_SUBGENRE:
            // based on the selected subgenre
            //
            // display the objects/actions for the selected type
            //  (see type IDs below)
            if(CBN_SELCHANGE == wNotifyCode)
            {
                // update the lists
                SendMessageA(hwnd,
                            WM_DMT_UPDATE_LISTS,
                            0,
                            0L);                            
            }
            break;

        case IDC_TYPE_AXIS:
        case IDC_TYPE_BUTTON:
        case IDC_TYPE_POV:
            // update the lists
            SendMessageA(hwnd,
                        WM_DMT_UPDATE_LISTS,
                        0,
                        0L);   
            // make sure the unmap button is selected as appropriate
            SendMessageA(hwnd,
                        WM_COMMAND,
                        IDC_CONTROLS,
                        0L);
            break;

        case IDC_CONTROLS:
            // if a mapped action is selected
            //  enable the "Unmap action" button
            fEnable = dmtcfgIsControlMapped(hwnd,
                                            pdia,
                                            uActions);
            EnableWindow(GetDlgItem(hwnd, IDC_UNMAP),
                    fEnable);
            // do NOT enable the map button if there are no
            //  more actions
            if(!SendMessage(GetDlgItem(hwnd, IDC_ACTIONS),
                        LB_GETCOUNT,
                        0, 0L))
            {
                EnableWindow(GetDlgItem(hwnd, IDC_STORE_MAPPING),
                        FALSE);
            }
            else
            {
                EnableWindow(GetDlgItem(hwnd, IDC_STORE_MAPPING),
                        !fEnable);
            }
            // if >any< controls are mapped
            //  enable the "Unmap all" button
            fEnable = dmtcfgAreAnyControlsMapped(hwnd,
                                                pdia,
                                                uActions);
            EnableWindow(GetDlgItem(hwnd, IDC_UNMAP_ALL),
                    fEnable);
            break;

        case IDC_STORE_MAPPING:     // "Map action"
            // map it
            hRes = dmtcfgMapAction(hwnd,
                                pdmtai->pDeviceList->guidInstance,
                                pdia,
                                uActions);
            if(FAILED(hRes))
            {
               // ISSUE-2001/03/29-timgill Needs error case handling
            }            

            // set the changed flag
            pMapping->fChanged = TRUE;
            break;

        case IDC_UNMAP:  // "Unmap action"
            // unmap it
            hRes = dmtcfgUnmapAction(hwnd,
                                    pdia,
                                    uActions);
            if(FAILED(hRes))
            {
                // ISSUE-2001/03/29-timgill Needs error case handling
            }

            // set the changed flag
            pMapping->fChanged = TRUE;
            break;

        case IDC_UNMAP_ALL:       // "Unmap all"
            hRes = dmtcfgUnmapAllActions(hwnd,
                                    pdia,
                                    uActions);
            if(FAILED(hRes))
            {
                // ISSUE-2001/03/29-timgill Needs error case handling
            }

            // set the changed flag
            pMapping->fChanged = TRUE;
            break;

    }

    // done
    return FALSE;

} //*** end dmtcfgOnCommand()


//===========================================================================
// dmtcfgOnNotify
//
// Handle WM_NOTIFY processing for the config device box
//
// Parameters:
//  HWND        hwnd    - handle to property page
//  PSHNOTIFY   *ppsh   - PSHNOTIFY ptr
//
// Returns: BOOL
//
// History:
//  08/20/1999 - davidkl - created
//  10/14/1999 - davidkl - implemented save calls
//===========================================================================
BOOL dmtcfgOnNotify(HWND hwnd,
                    PSHNOTIFY *pNotify)    
{
    //int         n           = 0;
	//JJ 64Bit Compat
	INT_PTR		n			= 0;
    BOOL        fSave       = FALSE;
    DMT_APPINFO *pdmtai     = NULL;

	// 7/19/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
    DPF(5, "dmtcfgOnNotify: hwnd == %Ph", hwnd);

    // get the window data   
    //pdmtai = (DMT_APPINFO*)GetWindowLong(hwnd, GWL_USERDATA);
	//JJ 64Bit Compat
	pdmtai = (DMT_APPINFO*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
    if(!pdmtai)
    {
        // bad news
        // ISSUE-2001/03/29-timgill Needs error case handling
    }

    switch(pNotify->hdr.code)
    {
		case PSN_SETACTIVE:
            DPF(5, "PSN_SETACTIVE");
            // force the apply button to be enabled
            SendMessageA(GetParent(hwnd),
                        PSM_CHANGED,
                        (WPARAM)hwnd,
                        0L);
            break;

        case PSN_KILLACTIVE:
            DPF(5, "PSN_KILLACTIVE");
            // make sure we get a PSN_APPLY message
            //SetWindowLong(hwnd, DWL_MSGRESULT, (LONG)FALSE);
			SetWindowLong(hwnd, DWLP_MSGRESULT, (LONG)FALSE);
            break;

        case PSN_APPLY:
            DPF(5, "PSN_APPLY - %s",
                (pNotify->lParam) ? "Ok" : "Apply");

            // save/load mapping data
            //
            // OK       == Save
            // Apply    == Load

            // which button was clicked?
            if(pNotify->lParam)
            {
                // save mapping data
                SendMessage(hwnd,
                            WM_DMT_FILE_SAVE,
                            0,0L);
            }
            else
            {
                // load mapping data
                // ISSUE-2001/03/29-timgill Load Mapping Data not yet implemented
                MessageBoxA(hwnd, "Load - Not Yet Implemented",
                            pdmtai->pDeviceList->szName, 
                            MB_OK);
            }

            // DO NOT allow the dialog to close
            //SetWindowLong(hwnd, 
              //          DWL_MSGRESULT, 
                //        (LONG)PSNRET_INVALID_NOCHANGEPAGE);

			//JJ 64Bit Compat
			SetWindowLongPtr(hwnd, 
                        DWLP_MSGRESULT, 
                        (LONG_PTR)PSNRET_INVALID_NOCHANGEPAGE);

            break;
            
    }

    // done
    return TRUE;

} //*** end dmtcfgOnNotify()


//===========================================================================
// dmtcfgOnUpdateLists
//
// Handle WM_DMT_UPDATE_LISTS message
//
// Parameters:
//
// Returns: BOOL
//
// History:
//  08/25/1999 - davidkl - created
//  11/12/1999 - dvaidkl - fixed problem with control selection setting
//===========================================================================
BOOL dmtcfgOnUpdateLists(HWND hwnd)
{
    //int                     nIdx            = -1;
    //int                     nSelCtrl        = -1;
	//JJ 64Bit Compat
	INT_PTR					nSelCtrl		= -1;
	INT_PTR					nIdx			= -1;
    int                     n               = 0;

    INT_PTR					nControls		= 0;
	INT_PTR					nActions		= 0;
	//int                     nControls       = 0;
    //int                     nActions        = 0;
    DWORD                   dwType          = DMTA_TYPE_UNKNOWN;
    DWORD                   dwObjType       = DMTA_TYPE_UNKNOWN;
    DMTSUBGENRE_NODE        *pSubGenre      = NULL;
    DMTACTION_NODE          *pAction        = NULL;
    DMTMAPPING_NODE         *pMapping       = NULL;
    DMTDEVICEOBJECT_NODE    *pObjectList    = NULL;
    DMTDEVICEOBJECT_NODE    *pObjectNode    = NULL;
    DMT_APPINFO             *pdmtai         = NULL;
    DIACTION                *pdia           = NULL;
    BOOL                    fFound          = FALSE;
    char                    szBuf[MAX_PATH];

    DPF(5, "dmtcfgOnUpdateLists");

    // get the window data   
    //pdmtai = (DMT_APPINFO*)GetWindowLong(hwnd, GWL_USERDATA);
	//JJ 64Bit Compat
	pdmtai = (DMT_APPINFO*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
    if(!pdmtai)
    {
        // bad news
        // ISSUE-2001/03/29-timgill Needs error case handling
    }

    // device object list
    pObjectList = pdmtai->pDeviceList->pObjectList;

    // get the currently selected control
    nSelCtrl = SendMessageA(GetDlgItem(hwnd, IDC_CONTROLS),
                            LB_GETCURSEL,
                            0,
                            0L);

    // clear the list box contents
    // actions
    SendMessageA(GetDlgItem(hwnd, IDC_ACTIONS),
                LB_RESETCONTENT,
                0,
                0L);
    // controls
    SendMessageA(GetDlgItem(hwnd, IDC_CONTROLS),
                LB_RESETCONTENT,
                0,
                0L);

    // get the current selection
    nIdx = SendMessageA(GetDlgItem(hwnd, IDC_SUBGENRE),
                    CB_GETCURSEL,
                    0,
                    0L);

    // get the item data
    pSubGenre = (DMTSUBGENRE_NODE*)SendMessageA(GetDlgItem(hwnd, 
                                                        IDC_SUBGENRE),
                                            CB_GETITEMDATA,
                                            nIdx,
                                            0L);
    
    // get the DIACTION array specific to the current device
    pMapping = pSubGenre->pMappingList;
    while(pMapping)
    {
        // match pdmtai->pDeviceList->guidInstance
        //  with pMapping->guidInstance
        if(IsEqualGUID(pdmtai->pDeviceList->guidInstance,
                    pMapping->guidInstance))
        {
            break;
        }

        // next mapping
        pMapping = pMapping->pNext;

    }
    if(!pMapping)
    {
        // this is very bad and should NEVER happen
        // ISSUE-2001/03/29-timgill Needs error case handling
        DebugBreak();
    }
    pdia = pMapping->pdia;
    nActions = (int)pMapping->uActions;

    // what control type is selected?
    dwType = IDC_TYPE_AXIS - (dmtGetCheckedRadioButton(hwnd,
                                                    IDC_TYPE_POV,
                                                    IDC_TYPE_AXIS));
    // populate the action list
    nIdx = 0;
    pAction = pSubGenre->pActionList;
    while(pAction)
    {
        // filter to the selected control type
        if(dwType == pAction->dwType)
        {
            // filter actions that are already assigned

            // first, find a matching action in the array
            fFound = FALSE;
            for(n = 0; n < nActions; n++)
            {
                // match based on the semantic / action id
                if((pdia+n)->dwSemantic == pAction->dwActionId)
                {
                    DPF(2, "dmtcfgOnUpdateLists- found matching action "
                        "pAction->dwActionId (%08Xh) == "
                        "(pdia+u)->dwSemantic (%08Xh)",
                        pAction->dwActionId,
                        (pdia+n)->dwSemantic);
                    fFound = TRUE;
                    break;
                }
            }

            // next, read the action array entry, 
            //  if GUID_NULL == guidInstance, add the entry
            if(!fFound || 
                IsEqualGUID(GUID_NULL, (pdia+n)->guidInstance))
            {                            
                // prepend the action priority
                wsprintfA(szBuf, "(Pri%d) %s",
                        pAction->dwPriority,
                        pAction->szName);

                // add the action name
                nIdx = SendMessageA(GetDlgItem(hwnd, IDC_ACTIONS),
                                    LB_ADDSTRING,
                                    0,
                                    (LPARAM)szBuf);

                // add the item data (action node)
                SendMessageA(GetDlgItem(hwnd, IDC_ACTIONS),
                            LB_SETITEMDATA,
                            nIdx,
                            (LPARAM)pAction);

            } //* assigned action filter

        } //* control type filter

        // next action
        pAction = pAction->pNext;
    
    }

    // populate the control list
    nIdx = 0;
    pObjectNode = pObjectList;
    while(pObjectNode)
    {
        // convert dinput's DIDFT to our 
        //  internal control type
        if(FAILED(dmtinputXlatDIDFTtoInternalType(pObjectNode->dwObjectType,
                                            &dwObjType)))
        {
            // ISSUE-2001/03/29-timgill Needs error case handling
        }
        DPF(3, "dmtcfgOnUpdateLists - %s : DIDFT type %08Xh, internal type %d",
            pObjectNode->szName,
            pObjectNode->dwObjectType,
            dwObjType);

        // filter on control type
        //
        // dwType populated above
        if(dwType == dwObjType)
        {

            // check to if mapped
            //
            // we do this by scanning the DIACTION array, looking
            //  for actions that contain our device's guidInstance
            //  and our object's offset
            //  if so, put the mapping info in ()
            wsprintfA(szBuf, "%s",
                    pObjectNode->szName);
            for(n = 0; n < nActions; n++)
            {
                if(IsEqualGUID((pdia+n)->guidInstance,
                            pdmtai->pDeviceList->guidInstance) &&
                            ((pdia+n)->dwObjID == 
                                pObjectNode->dwObjectType))
                {
                    wsprintfA(szBuf, "%s (%s)",
                            pObjectNode->szName,
                            (pdia+n)->lptszActionName);
                    break;
                }
            }

            // add the control name
            nIdx = SendMessageA(GetDlgItem(hwnd, IDC_CONTROLS),
                                LB_ADDSTRING,
                                0,
                                (LPARAM)szBuf);

            // add the item data (object node)
            SendMessageA(GetDlgItem(hwnd, IDC_CONTROLS),
                        LB_SETITEMDATA,
                        nIdx,
                        (LPARAM)pObjectNode);

        } //* control type filter

        // next control
        pObjectNode = pObjectNode->pNext;

    }

    // count the number of entries in each list
    nControls = SendMessage(GetDlgItem(hwnd, IDC_CONTROLS),
                            LB_GETCOUNT,
                            0,
                            0L);
    nActions = SendMessage(GetDlgItem(hwnd, IDC_ACTIONS),
                            LB_GETCOUNT,
                            0,
                            0L);                   

    // set the selected entry in each list
    //
    // only do this if there are entries in the lists
    if(nControls)
    {
        if(nSelCtrl > nControls)
        {
            nSelCtrl = 0;
        }

        SendMessageA(GetDlgItem(hwnd, IDC_CONTROLS),
                    LB_SETCURSEL,
                    nSelCtrl,
                    0L);
    }
    if(nActions)
    {
        SendMessageA(GetDlgItem(hwnd, IDC_ACTIONS),
                    LB_SETCURSEL,
                    0,
                    0L);
    }

    // if there are no controls or no actions
    //
    // disable the map button
    if(!nControls || !nActions)
    {
        EnableWindow(GetDlgItem(hwnd, IDC_STORE_MAPPING), FALSE);
    }

    // done
    return FALSE;

} //*** end dmtcfgOnUpdateLists()


//===========================================================================
// dmtcfgSourceDlgProc
//
// Configure Device Mapping Source Code dialog processing function
//
// Parameters: (see SDK help for parameter details)
//  HWND    hwnd
//  UINT    uMsg
//  WPARAM  wparam
//  LPARAM  lparam
//
// Returns: (see SDK help for return value details)
//  BOOL
//
// History:
//  08/31/1999 - davidkl - created  
//===========================================================================
BOOL CALLBACK dmtcfgSourceDlgProc(HWND hwnd,
                                UINT uMsg,
                                WPARAM wparam,
                                LPARAM lparam)
{
    switch(uMsg)
    {
        case WM_INITDIALOG:
            return dmtcfgSourceOnInitDialog(hwnd, 
                                            (HWND)wparam, 
                                            lparam);
        
        case WM_COMMAND:
            return dmtcfgSourceOnCommand(hwnd,
                                        LOWORD(wparam),
                                        (HWND)lparam,
                                        HIWORD(wparam));

        case WM_DMT_UPDATE_LISTS:
            return dmtcfgSourceOnUpdateLists(hwnd);
    }

    return FALSE;

} //*** end dmtcfgSourceDlgProc()


//===========================================================================
// dmtcfgSourceOnInitDialog
//
// Handle WM_INITDIALOG processing for the config source box
//
// Parameters:
//  HWND    hwnd        - handle to property page
//  HWND    hwndFocus   - handle of ctrl with focus
//  LPARAM  lparam      - user data (in this case, PROPSHEETPAGE*)
//
// Returns: BOOL
//
// History:
//  08/31/1999 - davidkl - created
//  10/07/1999 - davidkl - reworked code to match UI change
//===========================================================================
BOOL dmtcfgSourceOnInitDialog(HWND hwnd, 
                            HWND hwndFocus, 
                            LPARAM lparam)
{
    DMTSUBGENRE_NODE    *pSubGenre  = (DMTSUBGENRE_NODE*)lparam;
    //LONG                lPrev       = 0L;
	//JJ 64Bit Compat
	LONG_PTR			lPrev		= 0;
    int                 nIdx        = 0;
    char                szBuf[MAX_PATH];

    DPF(5, "dmtcfgSourceOnInitDialog");

    // validate pSubGenre (lparam)
    if(IsBadWritePtr((void*)pSubGenre, sizeof(DMTSUBGENRE_NODE)))
    {
        DPF(0, "dmtcfgOnInitDialog - invalid ppsp.ptp (%016Xh)",
            pSubGenre);
        return FALSE;
    }

    // set the window caption to include the subgenre name
    wsprintfA(szBuf, "Configure Device Mapping Source Code - %s",
            pSubGenre->szName);
    SetWindowTextA(hwnd, szBuf);

    // store the subgenre node in the window's user data
    SetLastError(0);
    //lPrev = SetWindowLong(hwnd, 
      //                  GWL_USERDATA, 
        //                (LONG)pSubGenre);

	//JJ 64Bit Compat
	lPrev = SetWindowLongPtr(hwnd, 
							GWLP_USERDATA, 
							(LONG_PTR)pSubGenre);
    if(!lPrev && GetLastError())
    {
        // serious app problem.  
        //  we need to stop things right here and now
        DPF(0, "dmtcfgSourceOnInitDialog - This is bad... "
            "We failed to store pSubGenre");
        DPF(0, "dmtcfgSourceOnInitDialog  - Please find someone "
            "to look at this right away");
        DebugBreak();
        return FALSE;
    }

    // populate the subgenre edit box
    SetWindowTextA(GetDlgItem(hwnd, IDC_SUBGENRE),
                pSubGenre->szName);

    // display the subgenre description
    SetWindowTextA(GetDlgItem(hwnd, IDC_DESCRIPTION),
                pSubGenre->szDescription);


    // select the axes radio button
    CheckRadioButton(hwnd,
                    IDC_TYPE_POV,
                    IDC_TYPE_AXIS,
                    IDC_TYPE_AXIS);

    // populate the actions list box
    //
    // store the ptr to the actions node in the listbox 
    //  entry user data
    SendMessageA(hwnd,
                WM_DMT_UPDATE_LISTS,
                0,
                0L);
    
    // done
    return TRUE;

} //*** end dmtcfgSourceOnInitDialog()


//===========================================================================
// dmtcfgSourceOnCommand
//
// Handle WM_COMMAND processing for the config source box
//
// Parameters:
//  HWND    hwnd        - handle to property page
//  WORD    wId         - control identifier    (LOWORD(wparam))
//  HWND    hwndCtrl    - handle to control     ((HWND)lparam)
//  WORD    wNotifyCode - notification code     (HIWORD(wparam))
//
// Returns: BOOL
//
// History:
//  08/31/1999 - davidkl - created
//===========================================================================
BOOL dmtcfgSourceOnCommand(HWND hwnd,
                    WORD wId,
                    HWND hwndCtrl,
                    WORD wNotifyCode)
{
    DMTGENRE_NODE   *pGenre = NULL;

	DPF(5, "dmtcfgOnCommand");

    // get the genre from the window's user data
    // ISSUE-2001/03/29-timgill config source box fails to handle many UI messages
    // IDC_ADD_ACTION, IDC_REMOVE_ACTION, IDC_RENAME_ACTION, IDC_CUSTOM_ACTION all do nothing
    // IDOK/IDCANCEL merely do default processing

    switch(wId)
    {
        case IDOK:
            EndDialog(hwnd, 0);
            break;

        case IDCANCEL:
            EndDialog(hwnd, -1);
            break;

        case IDC_SUBGENRE:
            // based on the selected subgenre
            //
            // display the objects/actions for the selected type
            //  (see type IDs below)
            if(CBN_SELCHANGE == wNotifyCode)
            {
                // update the lists
                SendMessageA(hwnd,
                            WM_DMT_UPDATE_LISTS,
                            0,
                            0L);   
            }
            break;

        case IDC_TYPE_AXIS:
        case IDC_TYPE_BUTTON:
        case IDC_TYPE_POV:
            // update the lists
            SendMessageA(hwnd,
                        WM_DMT_UPDATE_LISTS,
                        0,
                        0L);   
            break;

        case IDC_ADD_ACTION:
            break;

        case IDC_REMOVE_ACTION:
            break;

        case IDC_RENAME_ACTION:
            break;

        case IDC_CUSTOM_ACTION:
            break;

    }

    // done
    return FALSE;

} //*** end dmtcfgSourceOnCommand()


//===========================================================================
// dmtcfgSourceOnUpdateLists
//
// Handle WM_DMT_UPDATE_LISTS message
//
// Parameters:
//
// Returns: BOOL
//
// History:
//  08/31/1999 - davidkl - created
//  10/07/1999 - davidkl - modified to match UI change
//===========================================================================
BOOL dmtcfgSourceOnUpdateLists(HWND hwnd)
{
    //int                 nIdx        = -1;
	//JJ 64Bit Compat
	INT_PTR				nIdx		= -1;
    DWORD               dwType      = 0x0badbad0;
    DMTSUBGENRE_NODE    *pSubGenre  = NULL;
    DMTACTION_NODE      *pAction    = NULL;
    char                szBuf[MAX_PATH];
    
    // get the subgenre node from the window's user data
    //pSubGenre = (DMTSUBGENRE_NODE*)GetWindowLong(hwnd,
      //                                          GWL_USERDATA);

	//JJ 64Bit Compat
	pSubGenre = (DMTSUBGENRE_NODE*)GetWindowLongPtr(hwnd,
                                                GWLP_USERDATA);

    if(!pSubGenre)
    {
        // this is very bad
        // ISSUE-2001/03/29-timgill Needs error case handling
        DebugBreak();
        return TRUE;
    }

    // clear the list box contents
    SendMessageA(GetDlgItem(hwnd, IDC_ACTIONS),
                LB_RESETCONTENT,
                0,
                0L);
   
    // what control type is selected?
    dwType = IDC_TYPE_AXIS - (dmtGetCheckedRadioButton(hwnd,
                                                    IDC_TYPE_POV,
                                                    IDC_TYPE_AXIS));
    // populate the action list
    pAction = pSubGenre->pActionList;
    while(pAction)
    {
        // filter to the selected control type
        if(dwType == pAction->dwType)
        {

            // filter actions that are already selected
/*
            if(DMT_ACTION_NOTASSIGNED == pAction->dwDevObj)
            {
*/
                // if the priority is NOT 1, append that info to the name string
                //
                // ISSUE-2001/03/29-timgill Should the priority 1 mapping display colour be different (eg. red)?
                // Do game developers CARE about action priorities?
/*
                if(1 < pAction->dwPriority)
                {
                    wsprintfA(szBuf, "(Pri%d) %s",
                            pAction->dwPriority,
                            pAction->szName);
                }
                else
                {
*/
                    lstrcpyA(szBuf, pAction->szName);
//                }

                // add the action name
                nIdx = SendMessageA(GetDlgItem(hwnd, IDC_ACTIONS),
                                    LB_ADDSTRING,
                                    0,
                                    (LPARAM)szBuf);

                // add the extra data (action node)
                SendMessageA(GetDlgItem(hwnd, IDC_ACTIONS),
                            LB_SETITEMDATA,
                            nIdx,
                            (LPARAM)&(pAction));

/*
            } //* assigned action filter
*/

        } // control type filter

        // next action
        pAction = pAction->pNext;
    
    }

    // done
    return FALSE;

} //*** end dmtcfgSourceOnUpdateLists()


//===========================================================================
// dmtcfgCreateGenreList
//
// Reads genres.ini and creates the genre list used to populate the
//  Configure Device Action Map property sheet dialog.  Returns the number of
//  parent genres (NOT subgenres) found
//
// Parameters:
//
// Returns: HRESULT
//
// History:
//  08/23/1999 - davidkl - created
//  09/28/1999 - davidkl - modified to match new ini format
//===========================================================================
HRESULT dmtcfgCreateGenreList(DMTGENRE_NODE **ppdmtgList)
{
    HRESULT         hRes        = S_OK;
    UINT            u           = 0;
    BOOL            fFound      = FALSE;
    DMTGENRE_NODE   *pCurrent   = NULL;
    DMTGENRE_NODE   *pNew       = NULL;
	DMTGENRE_NODE	*pHold		= NULL;
    char            szItem[64];
    char            szBuf[MAX_PATH];
    char            szGroup[MAX_PATH];


    // validate  ppmdtgList
    if(IsBadWritePtr((void*)ppdmtgList, sizeof(DMTGENRE_NODE*)))
    {
        DPF(0, "dmtcfgCreateGenreList - invalid ppdmtgList (%016Xh)",
            ppdmtgList);
        return E_POINTER;
    }
    
    // check to make sure we are not being asked 
    //  to append to an existing list
    //
    // callers MUST pass a NULL list
    if(*ppdmtgList)
    {
        DPF(0, "dmtcfgCreateGenreList - ppdmtgList points to "
            "existing list! (%016Xh)", *ppdmtgList);
        return E_INVALIDARG;
    }

    __try
    {
        // get the genre names from genres.ini
        pCurrent = *ppdmtgList;
        lstrcpyA(szBuf, "");
        u = 0;
        while(lstrcmpA("<<>>", szBuf))
        {
            // get the name of the genre
            wsprintfA(szItem, "%d", u);
            GetPrivateProfileStringA(szItem,
                                    "N",
                                    "<<>>",
                                    szBuf,
                                    MAX_PATH,
                                    GENRES_INI);

            if(!lstrcmpA("<<>>", szBuf))
            {
                DPF(3, "end of genre list");
                continue;
            }
            DPF(3, "Genre name == %s", szBuf);

            // extract the group name
            hRes = dmtcfgGetGenreGroupName(szBuf,
                                        szGroup);
            if(FAILED(hRes))
            {
                // ISSUE-2001/03/29-timgill Needs error case handling
            }
            
            // walk the list
            //
            // make sure we did not get a duplicate name
            fFound = FALSE;
			pHold = pCurrent;
			pCurrent = *ppdmtgList;
            while(pCurrent)
            {
                if(!lstrcmpiA(pCurrent->szName,
                            szGroup))
                {
                    // match found
                    fFound = TRUE;
                    break;
                }

                // next node
                pCurrent = pCurrent->pNext;
            }
            if(!fFound)
            {
                // no match, allocate a new node

                // allocate the genre node
                pNew = (DMTGENRE_NODE*)LocalAlloc(LMEM_FIXED,
                                                    sizeof(DMTGENRE_NODE));
                if(!pNew)
                {
                    DPF(0, "dmtcfgCreateGenreList - insufficient memory to "
                        "allocate genre list node");
                    hRes = E_OUTOFMEMORY;
                    __leave;
                }

                // initialize the new node
                ZeroMemory((void*)pNew, sizeof(DMTGENRE_NODE));

                // set the name field
                lstrcpyA(pNew->szName, szGroup);

                // get the list of subgenres
                hRes = dmtcfgCreateSubGenreList(pNew->szName,
                                                &(pNew->pSubGenreList));
                if(FAILED(hRes))
                {
                    // ISSUE-2001/03/29-timgill Needs error case handling
                }

                // add it to the end of the list
                pCurrent = pHold;
                if(pCurrent)
                {
                    // append the list
                    pCurrent->pNext = pNew;

                    // go to the next node
                    pCurrent = pCurrent->pNext;
                }
                else
                {
                    // new list head
                    pCurrent = pNew;
                    *ppdmtgList = pCurrent;
                }

            }

            // next genre
            u++;

        }
    }
    __finally
    {
        if(FAILED(hRes))
		{
			// cleanup allocations
            DPF(1, "dmtcfgCreateGenreList - Failure occurred, "
                "freeing genre list");
			dmtcfgFreeGenreList(ppdmtgList);
            *ppdmtgList = NULL;
		}
    }

    // done
    return hRes;

} //*** end dmtcfgCreateGenreList()


//===========================================================================
// dmtcfgFreeGenreList
//
// Frees the linked list (and sub-lists) created by dmtcfgCreateGenreList
//
// Parameters:
//
// Returns: HRESULT
//
// History:
//  08/23/1999 - davidkl - created
//===========================================================================
HRESULT dmtcfgFreeGenreList(DMTGENRE_NODE **ppdmtgList)
{
    HRESULT         hRes    = S_OK;
    DMTGENRE_NODE   *pNode  = NULL;

    // validate ppdmtgList
    if(IsBadWritePtr((void*)ppdmtgList, sizeof(PDMTGENRE_NODE)))
    {
        DPF(0, "dmtcfgFreeGenreList - Invalid ppdmtgList (%016Xh)",
            ppdmtgList);
        return E_POINTER;
    }

    // validate *ppdmtgList
    if(IsBadReadPtr((void*)*ppdmtgList, sizeof(DMTGENRE_NODE)))
    {
        if(NULL != *ppdmtgList)
        {
            DPF(0, "dmtcfgFreeGenreList - Invalid *ppdmtgList (%016Xh)",
                *ppdmtgList);        
            return E_POINTER;
        }
        else
        {
            // if NULL, then return "did nothing"
            DPF(3, "dmtcfgFreeGenreList - Nothing to do....");
            return S_FALSE;
        }
    }

    // walk the list and free each object
    while(*ppdmtgList)
    {
        pNode = *ppdmtgList;
        *ppdmtgList = (*ppdmtgList)->pNext;

        // first, free the action list
        DPF(5, "dmtcfgFreeGenreList - "
            "freeing subgenre list (%016Xh)", 
            pNode->pSubGenreList);
        hRes = dmtcfgFreeSubGenreList(&(pNode->pSubGenreList));
        if(FAILED(hRes))
        {
            // ISSUE-2001/03/29-timgill Needs error case handling
        }

        DPF(5, "dmtcfgFreeGenreList - Deleting Node (%016Xh)", pNode);
        if(LocalFree((HLOCAL)pNode))
        {
            DPF(0, "dmtcfgFreeSubGenreList - MEMORY LEAK - "
                "LocalFree() failed (%d)...", 
                GetLastError());
            hRes = DMT_S_MEMORYLEAK;
        }
        DPF(5, "dmtcfgFreeGenreList - Node deleted");
    }

    // make sure that we set *ppdmtgList to NULL
    *ppdmtgList = NULL;

    // done
    return hRes;

} //*** end dmtcfgFreeGenreList()


//===========================================================================
// dmtcfgCreateSubGenreList
//
// Reads genres.ini and creates the subgenre list used to populate the
//  Configure Device Action Map property sheet dialog.
//
// Parameters:
//
// Returns: HRESULT
//
// History:
//  08/24/1999 - davidkl - created
//  09/29/1999 - davidkl - modified to match new ini format
//===========================================================================
HRESULT dmtcfgCreateSubGenreList(LPSTR szGenre,
                                DMTSUBGENRE_NODE **ppdmtsgList)
{
    HRESULT             hRes        = S_OK;
    UINT                u           = 0;
    DMTSUBGENRE_NODE    *pCurrent   = NULL;
    DMTSUBGENRE_NODE    *pNew       = NULL;
    char                szItem[64];
    char                szGroup[MAX_PATH];
    char                szBuf[MAX_PATH];

    // validate  ppmdtsgList
    if(IsBadWritePtr((void*)ppdmtsgList, sizeof(DMTSUBGENRE_NODE*)))
    {
        return E_POINTER;
    }
    
    // check to make sure we are not being asked 
    //  to append to an existing list
    //
    // callers MUST pass a NULL list
    if(*ppdmtsgList)
    {
        return E_INVALIDARG;
    }


    __try
    {
        // get the subgenre names from genres.ini
        pCurrent = *ppdmtsgList;
        lstrcpyA(szBuf, "");
        u = 0;
        while(lstrcmpA("<<>>", szBuf))
        {
            // look for subgenres belonging to szGenre
            wsprintfA(szItem, "%d", u);
            GetPrivateProfileStringA(szItem,
                                    "N",
                                    "<<>>",
                                    szBuf,
                                    MAX_PATH,
                                    GENRES_INI);

            if(!lstrcmpA("<<>>", szBuf))
            {
                DPF(3, "end of subgenre list");
                continue;
            }
            hRes = dmtcfgGetGenreGroupName(szBuf,
                                        szGroup);
            if(FAILED(hRes))
            {
                // ISSUE-2001/03/29-timgill Needs error case handling
            }

            // if we do not belong to the genre group
            //
            // make believe we found nothing
            if(lstrcmpiA(szGenre, szGroup))
            {
                u++;
                DPF(4, "bucket mismatch... skipping");
                continue;
            }

            // we fit in the szGenre bucket
            //
            // allocate the genre node
            pNew = (DMTSUBGENRE_NODE*)LocalAlloc(LMEM_FIXED,
                                                sizeof(DMTSUBGENRE_NODE));

			if(!pNew)
			{
				hRes = E_OUTOFMEMORY;
				__leave;
			}

            // initialize the new node
            ZeroMemory((void*)pNew, sizeof(DMTSUBGENRE_NODE));

            // get the genreid
            pNew->dwGenreId = GetPrivateProfileInt(szItem,
                                                "AI0",
                                                0,
                                                GENRES_INI);
            pNew->dwGenreId &= DMT_GENRE_MASK;
            DPF(4, "SubGenre ID == %08Xh", pNew->dwGenreId);

            // get the "name" (Txt1)
            GetPrivateProfileStringA(szItem,
                                    "T1",
                                    "<<>>",
                                    pNew->szName,
                                    MAX_PATH,
                                    GENRES_INI);
            DPF(3, "SubGenre name == %s", pNew->szName);

            // get the description (Txt2)
            GetPrivateProfileStringA(szItem,
                                    "T2",
                                    "<<>>",
                                    pNew->szDescription,
                                    MAX_PATH,
                                    GENRES_INI);
            DPF(4, "SubGenre description == %s", pNew->szDescription);

            // get the list of actions
            hRes = dmtcfgCreateActionList(szItem,
                                        &(pNew->pActionList));
            if(FAILED(hRes) || DMT_S_MEMORYLEAK == hRes)
            {
                // ISSUE-2001/03/29-timgill Needs error case handling
            }

            // add it to the end of the list
            if(pCurrent)
            {
                // append the list
                pCurrent->pNext = pNew;

                // go to the next node
                pCurrent = pCurrent->pNext;
            }
            else
            {
                // new list head
                pCurrent = pNew;
                *ppdmtsgList = pCurrent;
            }

            // next subgenre
            u++;

        }
    }
    __finally
    {
        // cleanup in failure case
        if(FAILED(hRes))
		{
            DPF(1, "dmtcfgCreateSubGenreList - Failure occurred, "
                "freeing subgenre list");
            dmtcfgFreeSubGenreList(ppdmtsgList);
            *ppdmtsgList = NULL;
		}
    }

	//JJ_FIX
	g_NumSubGenres = u;

    // done
    return S_OK;

} //*** end dmtcfgCreateSubGenreList()


//===========================================================================
// dmtcfgFreeSubGenreList
//
// Frees the linked list created by dmtcfgCreateSubGenreList
//
// Parameters:
//
// Returns: HRESULT
//
// History:
//  08/24/1999 - davidkl - created
//  08/25/1999 - davidkl - implemented
//===========================================================================
HRESULT dmtcfgFreeSubGenreList(DMTSUBGENRE_NODE **ppdmtsgList)
{
    HRESULT             hRes    = S_OK;
    DMTSUBGENRE_NODE    *pNode  = NULL;

    // validate ppdmtaList
    if(IsBadWritePtr((void*)ppdmtsgList, sizeof(PDMTSUBGENRE_NODE)))
    {
        DPF(0, "dmtcfgFreeSubGenreList - Invalid ppdmtsgList (%016Xh)",
            ppdmtsgList);
        return E_POINTER;
    }

    // validate *ppPortList
    if(IsBadReadPtr((void*)*ppdmtsgList, sizeof(DMTSUBGENRE_NODE)))
    {
        if(NULL != *ppdmtsgList)
        {
            DPF(0, "dmtcfgFreeSubGenreList - Invalid *ppdmtsgList (%016Xh)",
                *ppdmtsgList);        
            return E_POINTER;
        }
        else
        {
            // if NULL, then return "did nothing"
            DPF(3, "dmtcfgFreeSubGenreList - Nothing to do....");
            return S_FALSE;
        }
    }

    // walk the list and free each object
    while(*ppdmtsgList)
    {
        pNode = *ppdmtsgList;
        *ppdmtsgList = (*ppdmtsgList)->pNext;

        // first, free the action list
        DPF(5, "dmtcfgFreeSubGenreList - "
            "freeing action list (%016Xh)", 
            pNode->pActionList);
        hRes = dmtcfgFreeActionList(&(pNode->pActionList));
        if(FAILED(hRes))
        {
            hRes = DMT_S_MEMORYLEAK;
        }

        // then free the mapping list array
        if(pNode->pMappingList)
        {
            hRes = dmtcfgFreeMappingList(&(pNode->pMappingList));
            if(FAILED(hRes) || DMT_S_MEMORYLEAK == hRes)
            {
                hRes = DMT_S_MEMORYLEAK;
            }
            pNode->pMappingList = NULL;
        }

        // finally, free the node
        DPF(5, "dmtcfgFreeSubGenreList - Deleting Node (%016Xh)", pNode);
        if(LocalFree((HLOCAL)pNode))
        {
            DPF(0, "dmtcfgFreeSubGenreList - MEMORY LEAK - "
                "LocalFree(Node) failed (%d)...", 
                GetLastError());
            hRes = DMT_S_MEMORYLEAK;
        }
        DPF(5, "dmtcfgFreeSubGenreList - Node deleted");
    }

    // make sure that we set *ppdmtsgList to NULL
    *ppdmtsgList = NULL;

    // done
    return hRes;

} //*** end dmtcfgFreeSubGenreList()


//===========================================================================
// dmtcfgCreateActionList
//
// Reads genres.ini and creates the action list used to populate the
//  Configure Device Action Map property sheet dialog.
//
// Parameters:
//
// Returns: HRESULT
//
// History:
//  08/24/1999 - davidkl - created
//  09/07/1999 - davidkl - added DIACTION**
//  09/28/1999 - davidkl - updated to use info extraction macros
//  02/14/2000 - davidkl - started conversion to GetPrivateProfileSectionA
//===========================================================================
HRESULT dmtcfgCreateActionList(LPSTR szGenreSubgenre,
                            DMTACTION_NODE **ppdmtaList)
{
    HRESULT         hRes        = S_OK;
    UINT            u           = 0;
    BYTE            bTypeMask   = 0x03;
    DMTACTION_NODE  *pCurrent   = NULL;
    DMTACTION_NODE  *pNew       = NULL;
    char            szItem[MAX_PATH];
    char            szBuf[MAX_PATH];

    // validate ppmdtsgList
    if(IsBadWritePtr((void*)ppdmtaList, sizeof(DMTACTION_NODE*)))
    {
        DPF(0, "dmtcfgCreateActionList - invalid ppdmtaList (%016Xh)",
            ppdmtaList);
        return E_POINTER;
    }
    
    // check to make sure we are not being asked 
    //  to append to an existing list
    //
    // callers MUST pass a NULL list
    if(*ppdmtaList)
    {
        DPF(0, "dmtcfgCreateActionList - ppdmtaList points to "
            "existing list! (%016Xh)", *ppdmtaList);
        return E_INVALIDARG;
    }

    __try
    {
        // get the action info from genres.ini
        pCurrent = *ppdmtaList;
#ifdef BNW
    char    *pszSection = NULL;
    char    *pCurrent   = NULL;
    int     nAlloc      = 0;

        // allocate space for the (Win9x) max size of an ini section
        nAlloc = 32727;
        pszSection = (char*)LocalAlloc(LMEM_FIXED,
                                    sizeof(char) * nAlloc);
        if(!pszSection)
        {
            // alloc failed,
            //  try ~1/2 of the max (that should still cover the 
            //  fill size of the section)
            nAlloc = 16386;
            pszSection = (char*)LocalAlloc(LMEM_FIXED,
                                        sizeof(char) * nAlloc);
            if(!pszSection)
            {
                // alloc failed,
                //  try ~1/4 of the max (that should still cover the 
                //  fill size of the section)
                nAlloc = 8192;
                pszSection = (char*)LocalAlloc(LMEM_FIXED,
                                            sizeof(char) * nAlloc);
                if(!pszSection)
                {
                    // alloc failed,
                    //  try ~1/8 of the max (that should still cover the 
                    //  fill size of the section)
                    nAlloc = 4096;
                    pszSection = (char*)LocalAlloc(LMEM_FIXED,
                                                sizeof(char) * nAlloc);
                    if(!pszSection)
                    {
                        // alloc failed,
                        //  try ~1/16 of the max (that should still cover the 
                        //  fill size of the section) - this is our last attempt
                        nAlloc = 2048;
                        pszSection = (char*)LocalAlloc(LMEM_FIXED,
                                                    sizeof(char) * nAlloc);
                        if(!pszSection)
                        {
                            // alloc failed, we give up
                            __leave;
                        }
                    }
                }
            }
        }
        DPF(2, "dmtcfgCreateActionList - section allocation: %d bytes", nAlloc);
        
        // read the section specified by szGenreSubgenre
        GetPrivateProfileSectionA(szGenreSubgenre,
                                pszSection,
                                nAlloc,
                                GENRES_INI);

/* the following code fragment does nothing - u is incremented and then never used again
        // parse the action information from the section
        for(u = 0; ; u++)
        {
            break;
        }
*/
#else
        lstrcpyA(szBuf, "");
        u = 0;

        while(lstrcmpA("<<>>", szBuf))
        {
            // add the name of the action to the node
            wsprintfA(szItem, "AN%d", u);
            GetPrivateProfileStringA(szGenreSubgenre,
                                    szItem,
                                    "<<>>",
                                    szBuf,
                                    MAX_PATH,
                                    GENRES_INI);
            if(!lstrcmpA("<<>>", szBuf))
            {
                DPF(3, "end of action list");
                continue;
            }
            DPF(3, "Action name == %s", szBuf);

            // allocate the genre node
            pNew = (DMTACTION_NODE*)LocalAlloc(LMEM_FIXED,
                                                sizeof(DMTACTION_NODE));

			if(!pNew)
			{
				hRes = E_OUTOFMEMORY;
				__leave;
			}

            // initialize the new node
            ZeroMemory((void*)pNew, sizeof(DMTACTION_NODE));

            lstrcpyA(pNew->szName, szBuf);

    
            // get the action id
            wsprintfA(szItem, "AI%d", u);
            pNew->dwActionId = GetPrivateProfileIntA(szGenreSubgenre,
                                                szItem,
                                                0x0badbad0,
                                                GENRES_INI);
            DPF(4, "Action ID == %08Xh", pNew->dwActionId);

            // get the action priority
            pNew->dwPriority = dmtinputGetActionPri(pNew->dwActionId);
            DPF(4, "Action priority == %d", pNew->dwPriority);

            // get action type
            pNew->dwType = dmtinputGetActionObjectType(pNew->dwActionId);
            DPF(4, "Action type == %d", pNew->dwType);
   
            // get the action type name
            wsprintfA(szItem, "AIN%d", u);
            GetPrivateProfileStringA(szGenreSubgenre,
                                    szItem,
                                    "<<>>",
                                    pNew->szActionId,
                                    MAX_ACTION_ID_STRING,
                                    GENRES_INI);
            DPF(4, "Action ID name == %s", pNew->szActionId);

            // add it to the end of the list
            if(pCurrent)
            {
                // append the list
                pCurrent->pNext = pNew;

                // go to the next node
                pCurrent = pCurrent->pNext;
            }
            else
            {
                // new list head
                pCurrent = pNew;
                *ppdmtaList = pCurrent;
            }

            // net action
            u++;

        }
#endif // BNW
    }
    __finally
    {
#ifdef BNW
        // free the section memory we allocated
        if(LocalFree((HLOCAL)pszSection))
        {
            // memory leak
            DPF(0, "dmtcfgCreateActionList - !! MEMORY LEAK !! - LocalFree(section) failed");
        }
#endif // BNW

        // cleanup in failure case
        if(FAILED(hRes))
        {
            // free action list
            DPF(1, "dmtcfgCreateActionList - Failure occurred, "
                "freeing action list");
            dmtcfgFreeActionList(ppdmtaList);
            *ppdmtaList = NULL;
        }
    }

    // done
    return S_OK;

} //*** end dmtCreateActionList()


//===========================================================================
// dmtcfgFreeActionList
//
// Frees the linked list created by dmtcfgCreateActionList
//
// Parameters:
//
// Returns: HRESULT
//
// History:
//  08/24/1999 - davidkl - created
//  08/25/1999 - davidkl - implemented
//===========================================================================
HRESULT dmtcfgFreeActionList(DMTACTION_NODE **ppdmtaList)
{
    HRESULT         hRes    = S_OK;
    DMTACTION_NODE  *pNode  = NULL;

    // validate ppdmtaList
    if(IsBadWritePtr((void*)ppdmtaList, sizeof(PDMTACTION_NODE)))
    {
        DPF(0, "dmtcfgFreeActionList - Invalid ppdmtaList (%016Xh)",
            ppdmtaList);
        return E_POINTER;
    }

    // validate *ppdmtaList
    if(IsBadReadPtr((void*)*ppdmtaList, sizeof(DMTACTION_NODE)))
    {
        if(NULL != *ppdmtaList)
        {
            DPF(0, "dmtcfgFreeActionList - Invalid *ppdmtaList (%016Xh)",
                *ppdmtaList);        
            return E_POINTER;
        }
        else
        {
            // if NULL, then return "did nothing"
            DPF(3, "dmtcfgFreeActionList - Nothing to do....");
            return S_FALSE;
        }
    }

    // walk the list and free each object
    while(*ppdmtaList)
    {
        pNode = *ppdmtaList;
        *ppdmtaList = (*ppdmtaList)->pNext;

        // free the node
        DPF(5, "dmtcfgFreeActionList - deleting Node (%016Xh)", pNode);
        if(LocalFree((HLOCAL)pNode))
        {
            DPF(0, "dmtcfgFreeActionList - MEMORY LEAK - "
                "LocalFree(Node) failed (%d)...",
                GetLastError());
            hRes = DMT_S_MEMORYLEAK;
        }
        DPF(5, "dmtcfgFreeActionList - Node deleted");
    }

    // make sure that we set *ppObjList to NULL
    *ppdmtaList = NULL;

    // done
    return hRes;

} //*** end dmtcfgFreeActionList()


//===========================================================================
// dmtcfgCreateMappingList
//
// Creates a device mapping list
//
// Parameters:
//
// Returns: HRESULT
//
// History:
//  09/23/1999 - davidkl - created
//===========================================================================
HRESULT dmtcfgCreateMappingList(DMTDEVICE_NODE *pDeviceList,
                                DMTACTION_NODE *pActions,
                                DMTMAPPING_NODE **ppdmtmList)
{
    HRESULT         hRes            = S_OK;
    UINT            uActions        = NULL;
    DMTACTION_NODE  *pActionNode    = NULL;
    DMTMAPPING_NODE *pNew           = NULL;
    DMTMAPPING_NODE *pCurrent       = NULL;
	DMTDEVICE_NODE	*pDeviceNode	= NULL;

    // validate pDeviceList
    if(IsBadReadPtr((void*)pDeviceList, sizeof(DMTDEVICE_NODE)))
    {
        DPF(0, "dmtcfgCreateMappingList - invalid pDeviceList (%016Xh)", 
            pDeviceList);
        return E_POINTER;
    }

    // validate pActions
    if(IsBadReadPtr((void*)pActions, sizeof(DMTACTION_NODE)))
    {
        if(NULL != pActions)
        {
            DPF(0, "dmtcfgCreateMappingList - invalid pActions (%016Xh)", 
                pActions);
            return E_POINTER;
        }
        else
        {
            // no actions for this subgenre
            DPF(3, "dmtcfgCreateMappingList - No actions for this subgenre, "
                "nothing to do...");
            return S_FALSE;
        }

    }
    
    // validate ppdmtmList
    if(IsBadWritePtr((void*)ppdmtmList, sizeof(DMTMAPPING_NODE)))
    {
        DPF(0, "dmtcfgCreateMappingList - invalid ppdmtmList (%016Xh)", 
            ppdmtmList);
        return E_POINTER;
    }


    // check to make sure we are not being asked 
    //  to append to an existing list
    //
    // callers MUST pass a NULL list
    if(*ppdmtmList)
    {
        DPF(0, "dmtcfgCreateMappingList - ppdmtmList points to "
            "existing list! (%016Xh)", *ppdmtmList);
        return E_INVALIDARG;
    }

    __try
    {
        // count the actions
        //
        // this lets us know how much space to allocate for the 
        uActions = 0;
        pActionNode = pActions;
        while(pActionNode)
        {
            uActions++;
            
            // next node
            pActionNode = pActionNode->pNext;
        }

        // for each device
		pDeviceNode = pDeviceList;
		while(pDeviceNode)
		{
			// allocate the mapping node
			pNew = (DMTMAPPING_NODE*)LocalAlloc(LMEM_FIXED,
													sizeof(DMTMAPPING_NODE));
			if(!pNew)
			{
                DPF(3, "dmtcfgCreateMappingList - Insufficient memory to "
                    "allocate mapping list node");
				hRes = E_OUTOFMEMORY;
				__leave;
			}

			// initialize the new node
			ZeroMemory((void*)pNew, sizeof(DMTMAPPING_NODE));

			// allocate the action array
			pNew->pdia = (DIACTIONA*)LocalAlloc(LMEM_FIXED,
												uActions * sizeof(DIACTIONA));
			if(!(pNew->pdia))
			{
				hRes = E_OUTOFMEMORY;
				__leave;
			}

			// initial population of the action array
			hRes = dmtinputPopulateActionArray(pNew->pdia,
											uActions,
											pActions);
			if(FAILED(hRes))
			{
				__leave;
			}

			// add the number of actions
			pNew->uActions = uActions;

			// add the device instance guid
			pNew->guidInstance = pDeviceNode->guidInstance;

			// add the new node to the list
			if(pCurrent)
			{
				// append the list
				pCurrent->pNext = pNew;

				// go to the next node
				pCurrent = pCurrent->pNext;
			}
			else
			{
				// new list head
				pCurrent = pNew;
				*ppdmtmList = pCurrent;
			}

			// next device
			pDeviceNode = pDeviceNode->pNext;

		}

    }
    __finally
    {
        // in case of error...
        if(FAILED(hRes))
        {
            // free list
            dmtcfgFreeMappingList(ppdmtmList);
            *ppdmtmList = NULL;
        }
    }

    // done
    return hRes;

} //*** end dmtcfgCreateMappingList()


//===========================================================================
// dmtcfgFreeMappingList
//
// Completely frees a mapping list
//
// Parameters:
//
// Returns: HRESULT
//
// History:
//  09/23/1999 - davidkl - created
//===========================================================================
HRESULT dmtcfgFreeMappingList(DMTMAPPING_NODE **ppdmtmList)
{    
    HRESULT         hRes    = S_OK;
    DMTMAPPING_NODE *pNode  = NULL;

    // validate ppdmtmList
    if(IsBadWritePtr((void*)ppdmtmList, sizeof(DMTMAPPING_NODE*)))
    {
        return E_POINTER;
    }

    // validate *ppdmtmList
    if(IsBadWritePtr((void*)*ppdmtmList, sizeof(DMTMAPPING_NODE)))
    {
        if(NULL != *ppdmtmList)
        {
            return E_POINTER;
        }
        else
        {
            // nothing to do
            return S_FALSE;
        }
    }

    // walk the list and free each object
    while(*ppdmtmList)
    {
        pNode = *ppdmtmList;
        *ppdmtmList = (*ppdmtmList)->pNext;

        // first free the action array
        if(LocalFree((HLOCAL)(pNode->pdia)))
        {
            DPF(0, "dmtcfgFreeMappingList - MEMORY LEAK - "
                "LocalFree(pdia) failed (%d)...",
                GetLastError());
            hRes = DMT_S_MEMORYLEAK;
        }

        // lastly, free the node
        DPF(5, "dmtcfgFreeMappingList - deleting Node (%016Xh)", pNode);
        if(LocalFree((HLOCAL)pNode))
        {
            DPF(0, "dmtcfgFreeMappingList - MEMORY LEAK - "
                "LocalFree(Node) failed (%d)...",
                GetLastError());
            hRes = DMT_S_MEMORYLEAK;
        }
        DPF(5, "dmtcfgFreeMappingList - Node deleted");
    }

    // make sure that we set *ppObjList to NULL
    *ppdmtmList = NULL;

    // done
    return hRes;

} //*** end dmtcfgFreeMappingList


//===========================================================================
// dmtcfgCreateAllMappingLists
//
// Uses dmtcfgCreateMappingList to create mapping lists for each subgenre
//  referenced by pdmtai->pGenreList for each device refereced by 
//  pdmtai->pDeviceList
//
// Parameters:
//
// Returns: HRESULT
//
// History:
//  09/23/1999 - davidkl - created
//===========================================================================
HRESULT dmtcfgCreateAllMappingLists(DMT_APPINFO *pdmtai)
{
    HRESULT             hRes            = S_OK;
    HRESULT             hr              = S_OK;
    DMTGENRE_NODE       *pGenreNode     = NULL;
    DMTSUBGENRE_NODE    *pSubGenreNode  = NULL;

    // validate pdmtai
    if(IsBadReadPtr((void*)pdmtai, sizeof(DMT_APPINFO)))
    {
        return E_POINTER;
    }

    // validate pdmtai->pGenreList
    if(IsBadReadPtr((void*)(pdmtai->pGenreList), sizeof(DMTGENRE_NODE)))
    {
        return E_POINTER;
    }

    // validate pdmtai->pDeviceList
    if(IsBadReadPtr((void*)(pdmtai->pDeviceList), sizeof(DMTDEVICE_NODE)))
    {
        return E_POINTER;
    }   
    
    // for each genre
    pGenreNode = pdmtai->pGenreList;
    while(pGenreNode)
    {
        // for each subgenre of the genre
        pSubGenreNode = pGenreNode->pSubGenreList;
        while(pSubGenreNode)
        {
            // create the mapping list
            hr = dmtcfgCreateMappingList(pdmtai->pDeviceList,
                                        pSubGenreNode->pActionList,
                                        &(pSubGenreNode->pMappingList));
            if(FAILED(hr))
            {
                hRes = S_FALSE;
            }
            
            // next subgenre
            pSubGenreNode = pSubGenreNode->pNext;
        }
         
        // next genre
        pGenreNode = pGenreNode->pNext;
    }

    // done
    return hRes;

} //*** end dmtcfgCreateAllMappingLists()


//===========================================================================
// dmtcfgFreeAllMappingLists
//
// Walks the provided genre list and frees the mapping list found in each
//  subgenre node
//
// Parameters:
//  DMTGENRE_NODE   *pdmtgList  - list of genres
//
// Returns: HRESULT
//
// History:
//  10/05/1999 - davidkl - created
//===========================================================================
HRESULT dmtcfgFreeAllMappingLists(DMTGENRE_NODE *pdmtgList)
{
    HRESULT             hRes        = S_OK;
    HRESULT             hr          = S_OK;
    DMTGENRE_NODE       *pGenre     = NULL;
    DMTSUBGENRE_NODE    *pSubGenre  = NULL;

    // validate pdmtgList
    if(IsBadReadPtr((void*)pdmtgList, sizeof(DMTGENRE_NODE)))
    {
        return E_POINTER;
    }

    // walk the genre list
    pGenre = pdmtgList;
    while(pGenre)
    {
        // walk each subgenre list
        pSubGenre = pGenre->pSubGenreList;
        while(pSubGenre)
        {
            // free the mapping list
            hr = dmtcfgFreeMappingList(&(pSubGenre->pMappingList));
            if(S_OK != hr)
            {
                hRes = hr;
            }

            // next subgenre
            pSubGenre = pSubGenre->pNext;
        }

        // next genre
        pGenre = pGenre->pNext;
    }    

    // done
    return hRes;

} //*** end dmtcfgFreeAllMappingLists()


//===========================================================================
// dmtcfgMapAction
//
// Connects the dots between an action (in the map config dialog) to a device
//   object
//
// Parameters:
//  HWND        hwnd            - handle to property page window
//  REFGUID     guidInstance    - instance GUID of DirectInputDevice object
//  DIACTIONA   *pdia           - ptr to array of DIACTIONA structuresfs
//  UINT        uActions        - number of actions in pdia
//
// Returns: HRESULT
//
// History:
//  09/14/1999 - davidkl - created
//===========================================================================
HRESULT dmtcfgMapAction(HWND hwnd,
                        REFGUID guidInstance,
                        DIACTIONA *pdia,
                        UINT uActions)
{
    HRESULT                 hRes        = S_OK;
   // UINT                    uObjectSel  = 0;
	//JJ 64Bit Compat
	UINT_PTR				uObjectSel	= 0;
	UINT_PTR				uActionSel	= 0;
   // UINT                    uActionSel  = 0;
    UINT                    u           = 0;
    BOOL                    fFound      = FALSE;
    DMTDEVICEOBJECT_NODE    *pObject    = NULL;
    DMTACTION_NODE          *pAction    = NULL;

    // valudate pdia
    if(IsBadWritePtr((void*)pdia, sizeof(DIACTION)))
    {
        DPF(0, "dmtinputMapAction - invalid pdia (%016Xh)",
            pdia);
        return E_POINTER;
    }

    __try
    {
        // get the object & it's data
        uObjectSel = SendMessageA(GetDlgItem(hwnd, IDC_CONTROLS),
                                LB_GETCURSEL,
                                0,
                                0L);
        pObject = (DMTDEVICEOBJECT_NODE*)SendMessageA(GetDlgItem(hwnd, 
                                                                IDC_CONTROLS),
                                                    LB_GETITEMDATA,
                                                    (WPARAM)uObjectSel,
                                                    0L);
        if(!pObject)
        {
            hRes = E_UNEXPECTED;
            __leave;
        }

        // get the action's data
        uActionSel = SendMessageA(GetDlgItem(hwnd, IDC_ACTIONS),
                                LB_GETCURSEL,
                                0,
                                0L);
        pAction = (DMTACTION_NODE*)SendMessageA(GetDlgItem(hwnd, IDC_ACTIONS),
                                            LB_GETITEMDATA,
                                            (WPARAM)uActionSel,
                                            0L);
        if(!pAction)
        {
            hRes = E_UNEXPECTED;
            __leave;
        }

        // find the appropriate action in the array
        fFound = FALSE;
        for(u = 0; u < uActions; u++)
        {
            // match based on the semantic / action id
            if((pdia + u)->dwSemantic == pAction->dwActionId)
            {
                DPF(2, "dmtcfgMapAction - found matching action "
                    "pAction->dwActionId (%08Xh) == "
                    "(pdia+u)->dwSemantic (%08Xh)",
                    pAction->dwActionId,
                    (pdia + u)->dwSemantic);
                fFound = TRUE;
                break;
            }
        }

        // did we find the action in the array?
        if(!fFound)
        {
            // no.  this is very bad!
            //
            // if this EVER happens, 
            //  we have a serious bug in this app
            hRes = E_FAIL;
            // since this should NEVER happen, 
            //  break into the debugger and alert the tester
            DPF(0, "dmtcfgMapAction - action not found in pdia!");
            DPF(0, "dmtcfgMapAction - we were looking for "
                "%08Xh (%s)",
                pAction->dwActionId,
                pAction->szActionId);
            DPF(0, "dmtcfgMapAction - CRITICAL failure.  "
                "This should have never happened!");
            DPF(0, "dmtcfgMapAction - Please find someone "
                "to look at this right away.  ");
            DebugBreak();
            __leave;
        }

        // update the action array
        (pdia + u)->dwObjID    = pObject->dwObjectType;
        (pdia + u)->guidInstance        = guidInstance;
        // HIWORD((DWORD)uAppData) == object type
        // LOWORD((DWORD)uAppData) == pObject->wCtrlId
        (pdia + u)->uAppData            = (DIDFT_GETTYPE(pObject->dwObjectType) << 16) | 
                                        (pObject->wCtrlId);

        // update the list boxes
        SendMessageA(hwnd,
                    WM_DMT_UPDATE_LISTS,
                    0,
                    0L);

        // enable the unmap & unmap all buttons
        EnableWindow(GetDlgItem(hwnd, IDC_UNMAP_ALL),         TRUE);
        EnableWindow(GetDlgItem(hwnd, IDC_UNMAP),    TRUE);
        // disable the map button
        EnableWindow(GetDlgItem(hwnd, IDC_STORE_MAPPING),       FALSE);

    }
    __finally
    {
        // cleanup

        // nothing to do... yet
    }

    // done
    return hRes;

} //*** end dmtcfgMapAction()


//===========================================================================
// dmtcfgUnmapAction
//
// Disconnects the dots between an action (in the map config dialog) and a 
//  device object
//
// Parameters:
//  HWND        hwnd        - handle to property page window
//  DIACTIONA   *pdia       - ptr to DIACTIONA array
//  UINNT       uActions    - number of elements in pdia
//
// Returns: HRESULT
//
// History:
//  09/15/1999 - davidkl - created
//===========================================================================
HRESULT dmtcfgUnmapAction(HWND hwnd,
                        DIACTIONA *pdia,
                        UINT uActions)
{
    HRESULT                 hRes        = S_OK;
    UINT                    u           = 0;
   // UINT                    uSel        = 0;
	//JJ 64Bit Compat
	UINT_PTR				uSel		= 0;
    BOOL                    fFound      = FALSE;
    DMTSUBGENRE_NODE        *pSubGenre  = NULL;
    DMTDEVICEOBJECT_NODE    *pObject    = NULL;

    // validate pdia
    if(IsBadWritePtr((void*)pdia, uActions * sizeof(DIACTIONA)))
    {
        return E_POINTER;
    }

    __try
    {
        // get the current control selection
        uSel = SendMessageA(GetDlgItem(hwnd, IDC_CONTROLS),
                            LB_GETCURSEL,
                            0,
                            0L);
        pObject = (DMTDEVICEOBJECT_NODE*)SendMessageA(GetDlgItem(hwnd, IDC_CONTROLS),
                                                    LB_GETITEMDATA,
                                                    (WPARAM)uSel,
                                                    0L);
        if(!pObject)
        {
            // this is bad
            hRes = E_UNEXPECTED;
            __leave;
        }

        // spin through pdia 
        //  look for an action with our object's offset
        fFound = FALSE;
        for(u = 0; u < uActions; u++)
        {
            // first check the guid
            if(IsEqualGUID(pObject->guidDeviceInstance, (pdia+u)->guidInstance))
            {
                // then compare the offset
                if((pdia+u)->dwObjID == pObject->dwObjectType)
                {
                    fFound = TRUE;
                    break;
                }
            }
        }

        // if nothing is found, 
        //  the selected object is not mapped 
        //
        // (non-critical internal error condition)
        if(!fFound)
        {
            hRes = S_FALSE;
            __leave;
        }

        // reset the guidInstance and dwSemantic fields
        (pdia + u)->guidInstance        = GUID_NULL;
        (pdia + u)->dwObjID    = 0;
        (pdia + u)->uAppData            = 0;

        // update the lists
        SendMessageA(hwnd,
                    WM_DMT_UPDATE_LISTS,
                    0,
                    0L);

        // enable the map button
        EnableWindow(GetDlgItem(hwnd, IDC_STORE_MAPPING),    TRUE);
        // disable the unmap button
        EnableWindow(GetDlgItem(hwnd, IDC_UNMAP), FALSE);

        // if no other actions are mapped,
        //  disable the unmap all button
        fFound = FALSE;
        for(u = 0; u < uActions; u++)
        {
            if(!IsEqualGUID(GUID_NULL, (pdia+u)->guidInstance))
            {
                fFound = TRUE;
            }
        }
        if(!fFound)
        {
            EnableWindow(GetDlgItem(hwnd, IDC_UNMAP_ALL), FALSE);
        }

    }
    __finally
    {
        // cleanup

        // nothing to do... yet
    }

    // done
    return hRes;

} //*** end dmtcfgUnmapAction()


//===========================================================================
// dmtcfgUnmapAllActions
//
// Disconnects the all connections between an action (in the map config 
//  dialog) and a device object
//
// Parameters:
//  HWND        hwnd        - handle to property page window
//  DIACTIONA   *pdia       - ptr to DIACTIONA array
//  UINNT       uActions    - number of elements in pdia
//
// Returns: HRESULT
//
// History:
//  09/15/1999 - davidkl - created
//===========================================================================
HRESULT dmtcfgUnmapAllActions(HWND hwnd,
                            DIACTIONA *pdia,
                            UINT uActions)
{
    UINT u = 0;

    // validate pdia
    if(IsBadWritePtr((void*)pdia, uActions * sizeof(DIACTIONA)))
    {
        return E_POINTER;
    }

    // spin through pdia 
    //  reset the guidInstance and dwSemantic fields
    for(u = 0; u < uActions; u++)
    {
        (pdia + u)->guidInstance        = GUID_NULL;
        (pdia + u)->dwObjID    = 0;
        (pdia + u)->uAppData            = 0;
    }

    // update the lists
    SendMessageA(hwnd,
                WM_DMT_UPDATE_LISTS,
                0,
                0L);

    // disable the unmap & unmap all buttons
    EnableWindow(GetDlgItem(hwnd, IDC_UNMAP),    FALSE);
    EnableWindow(GetDlgItem(hwnd, IDC_UNMAP_ALL),         FALSE);
    // enable the map button
    EnableWindow(GetDlgItem(hwnd, IDC_STORE_MAPPING),       TRUE);

    // done
    return S_OK;

} //*** end dmtcfgUnmapAllActions()


//===========================================================================
// dmtcfgIsControlMapped
//
// Checks to see if a control is mapped to an action
//
// Parameters:
//
// Returns:
//
// History:
//  09/15/1999 - davidkl - created
//===========================================================================
BOOL dmtcfgIsControlMapped(HWND hwnd,
                        DIACTIONA *pdia,
                        UINT uActions)
{   
    BOOL                    fMapped     = FALSE;
    UINT                    u           = 0;
   // UINT                    uSel        = 0;
	//JJ 64Bit Compat
	UINT_PTR				uSel		= 0;
    DMTDEVICEOBJECT_NODE    *pObject    = NULL;

    // validate pdia
    if(IsBadReadPtr((void*)pdia, uActions * sizeof(pdia)))
    {
        DPF(0, "dmtcfgIsControlMapped - invalid pdia (%016Xh)",
            pdia);
        SetLastError(ERROR_INVALID_DATA);
        return FALSE;
    }

    // get the currently selected control
    uSel = SendMessageA(GetDlgItem(hwnd, IDC_CONTROLS),
                        LB_GETCURSEL,
                        0,
                        0L);
    pObject = (DMTDEVICEOBJECT_NODE*)SendMessageA(GetDlgItem(hwnd, IDC_CONTROLS),
                                                LB_GETITEMDATA,
                                                (WPARAM)uSel,
                                                0L);
    if(!pObject)
    {
        // this is bad
        //
        // (serious internal app error)
        SetLastError(ERROR_GEN_FAILURE);
        DebugBreak();
        return FALSE;
    }

    // check the array, 
    //  see if this control is mapped to anything
    fMapped = FALSE;
    for(u = 0; u < uActions; u++)
    {
        // first check the guid
        if(IsEqualGUID(pObject->guidDeviceInstance, (pdia+u)->guidInstance))
        {
            // then compare the offset
            if((pdia+u)->dwObjID == pObject->dwObjectType)
//            if((pdia+u)->dwObjID == pObject->dwObjectOffset)
            {
                fMapped = TRUE;
                break;
            }
        }
    }

    // done
    SetLastError(ERROR_SUCCESS);
    return fMapped;

} //*** end dmtcfgIsControlMapped()


//===========================================================================
// dmtcfgAreAnyControlsMapped
//
// Checks to see if any controls are mapped to an action.
//
// Parameters:
//
// Returns:
//
// History:
//  11/01/1999 - davidkl - created
//===========================================================================
BOOL dmtcfgAreAnyControlsMapped(HWND hwnd,
                                DIACTIONA *pdia,
                                UINT uActions)
{
    BOOL                    fMapped     = FALSE;
    UINT                    u           = 0;

    // validate pdia
    if(IsBadReadPtr((void*)pdia, uActions * sizeof(pdia)))
    {
        DPF(0, "dmtcfgAreAnyControlsMapped - invalid pdia (%016Xh)",
            pdia);
        SetLastError(ERROR_INVALID_DATA);
        return FALSE;
    }

    // check the array, 
    //  see if this control is mapped to anything
    fMapped = FALSE;
    for(u = 0; u < uActions; u++)
    {
        // check guid
        //
        // if not GUID_NULL, this action is mapped
        if(!IsEqualGUID(GUID_NULL, (pdia+u)->guidInstance))
        {
            fMapped = TRUE;
            break;
        }
    }

    // done
    SetLastError(ERROR_SUCCESS);
    return fMapped;

} //*** end dmtcfgAreAnyControlsMapped()


//===========================================================================
// dmtcfgGetGenreGroupName
//
// Extracts the genre group name from the genres.ini entry
//
// Paramters:
//
// Returns: HRESULT
//
// History:
//  09/28/1999 - davidkl - created
//	09/29/1999 - davidkl - modified "buckets"
//===========================================================================
HRESULT dmtcfgGetGenreGroupName(PSTR szGenreName,
                                PSTR szGenreGroupName)
{
    HRESULT hRes        = S_OK;
    char    *pcFirst    = NULL;
    char    *pcCurrent  = NULL;
    
    // find the first '_'
    pcFirst = strchr(szGenreName, '_');

    // copy the characters between pcFirst and pcLast
	pcCurrent = pcFirst+1;		// skip past the first '_'
    while((*pcCurrent != '_') && (*pcCurrent != '\0'))
    {
        *szGenreGroupName = *pcCurrent;

        // next character
        pcCurrent++;
        szGenreGroupName++;
    }
	*szGenreGroupName = '\0';

    // done
    return hRes;

} //*** end dmtcfgGetGenreGroupName()


//===========================================================================
//===========================================================================