//-----------------------------------------------------------------------------
// File: DPConnect.cpp
//
// Desc: Support file to prompt the user for the DirectPlay connection 
//       and the DirectPlay session they want to join or create. 
//
//       Call DPConnect_StartDirectPlayConnect() to start the series of 
//       dialogs boxes.  At the end the player will be connected to an active
//       DirectPlay session that the user either created or joined.  Or the 
//       function may also return an EXITCODE as defined below in the event
//       of error or user cancel.  
//
// Copyright (c) 1999-2001 Microsoft Corp. All rights reserved.
//-----------------------------------------------------------------------------
#define IDIRECTPLAY2_OR_GREATER
#include <windows.h>
#include <basetsd.h>
#include <dplobby.h>
#include <dplay.h>
#include <stdio.h>
#include "resource.h"




//-----------------------------------------------------------------------------
// Defines, and constants
//-----------------------------------------------------------------------------
#define SAFE_DELETE(p)       { if(p) { delete (p);     (p)=NULL; } }
#define SAFE_DELETE_ARRAY(p) { if(p) { delete[] (p);   (p)=NULL; } }
#define SAFE_RELEASE(p)      { if(p) { (p)->Release(); (p)=NULL; } }

#define MAX_PLAYER_NAME      14
#define MAX_SESSION_NAME     256

struct DPSessionInfo
{
	GUID guidSession;
	TCHAR szSession[MAX_SESSION_NAME];
	DPSessionInfo* pDPSINext;
};




//-----------------------------------------------------------------------------
// Global variables
//-----------------------------------------------------------------------------
#define EXITCODE_FORWARD      1  // Dialog success, so go forward
#define EXITCODE_BACKUP       2  // Dialog canceled, show previous dialog
#define EXITCODE_QUIT         3  // Dialog quit, close app
#define EXITCODE_ERROR        4  // Dialog error, terminate app
#define EXITCODE_LOBBYCONNECT 5  // Dialog connected from lobby, connect success

extern LPDIRECTPLAY4A       g_pDP;
extern LPDPLCONNECTION      g_pDPLConnection;
extern LPDIRECTPLAYLOBBY3   g_pDPLobby;
extern GUID                 g_AppGUID; 
extern HANDLE               g_hDPMessageEvent;
extern TCHAR                g_strPreferredProvider[MAX_SESSION_NAME];
extern TCHAR                g_strSessionName[MAX_SESSION_NAME];
extern TCHAR                g_strLocalPlayerName[MAX_PLAYER_NAME];
extern DPID                 g_LocalPlayerDPID; 
extern BOOL                 g_bHostPlayer;
extern TCHAR                g_strAppName[256];
extern BOOL                 g_bUseProtocol;
extern BOOL                 g_bAsyncSupported; // Asynchronous sends supported

DPSessionInfo               g_DPSIHead;
BOOL                        g_bSearchingForSessions;
BOOL                        g_bWaitForConnectionSettings;




//-----------------------------------------------------------------------------
// Function-prototypes
//-----------------------------------------------------------------------------
int             DPConnect_StartDirectPlayConnect( HINSTANCE hInst, BOOL bBackTrack );

INT_PTR CALLBACK DPConnect_ConnectionsDlgProc( HWND, UINT, WPARAM, LPARAM );
BOOL FAR PASCAL DPConnect_EnumConnectionsCallback( LPCGUID, VOID*, DWORD, LPCDPNAME, DWORD, VOID* );
HRESULT         DPConnect_ConnectionsDlgFillListBox( HWND hDlg );
HRESULT         DPConnect_ConnectionsDlgOnOK( HWND hDlg );
VOID            DPConnect_ConnectionsDlgCleanup( HWND hDlg );

INT_PTR CALLBACK DPConnect_SessionsDlgProc( HWND, UINT, WPARAM, LPARAM );
INT_PTR CALLBACK DPConnect_CreateSessionDlgProc( HWND, UINT, WPARAM, LPARAM );
BOOL FAR PASCAL DPConnect_EnumSessionsCallback( LPCDPSESSIONDESC2, DWORD*, DWORD, VOID* );
VOID            DPConnect_SessionsDlgInitListbox( HWND hDlg );
HRESULT         DPConnect_SessionsDlgShowGames( HWND hDlg );
HRESULT         DPConnect_SessionsDlgJoinGame( HWND hDlg );
HRESULT         DPConnect_SessionsDlgCreateGame( HWND hDlg );
VOID            DPConnect_SessionsDlgCleanup();

INT_PTR CALLBACK DPConnect_LobbyWaitDlgProc( HWND, UINT, WPARAM, LPARAM );
HRESULT         DPConnect_CheckForLobbyLaunch( BOOL* pbLaunchedByLobby );
HRESULT         DPConnect_DoLobbyLaunch();
HRESULT         DPConnect_WaitForLobbyLaunch( HWND hParentDlg );
HRESULT         DPConnect_GetLobbyConnectionSettingsMessage();




//-----------------------------------------------------------------------------
// Name: DPConnect_StartDirectPlayConnect()
// Desc: Prompts the user for the DirectPlay connection and the DirectPlay 
//       session they want to join or create.  This function returns one
//       of the EXITCODEs as #defined above
//-----------------------------------------------------------------------------
int DPConnect_StartDirectPlayConnect( HINSTANCE hInst, BOOL bBackTrack = FALSE )
{
    int nExitCode;
    int nStep;

    // Setup the g_DPSIHead circular linked list
    ZeroMemory( &g_DPSIHead, sizeof( DPSessionInfo ) );
    g_DPSIHead.pDPSINext = &g_DPSIHead;
    
    // If the back track flag is true, then the user has already been through
    // the connect process once, and has back tracked out of the main 'game' 
    // so we need start at the last dialog box
    if( bBackTrack )
        nStep = 1;
    else
        nStep = 0;

    // Show the dialog boxes to connect
    while( TRUE )
    {
        switch( nStep )
        {
            case 0:
                // Display the multiplayer connect dialog box.
                nExitCode = (int)DialogBox( hInst, MAKEINTRESOURCE(IDD_MULTIPLAYER_CONNECT), 
                                            NULL, (DLGPROC)DPConnect_ConnectionsDlgProc );
                break;

            case 1:
                // Display the multiplayer games dialog box.
                nExitCode = (int)DialogBox( hInst, MAKEINTRESOURCE(IDD_MULTIPLAYER_GAMES), 
                                            NULL, (DLGPROC)DPConnect_SessionsDlgProc );
                break;
        }

        if( nExitCode == EXITCODE_ERROR || 
            nExitCode == EXITCODE_QUIT )
            return nExitCode;

        if( nExitCode == EXITCODE_BACKUP )
            nStep--;
        else
            nStep++;

        if( nExitCode == EXITCODE_LOBBYCONNECT )
            return EXITCODE_LOBBYCONNECT;

        if( nStep == 2 )
            break;
    }

    return EXITCODE_FORWARD;
}




//-----------------------------------------------------------------------------
// Name: DPConnect_ConnectionsDlgProc()
// Desc: Handles messages for the multiplayer connect dialog
//-----------------------------------------------------------------------------
INT_PTR CALLBACK DPConnect_ConnectionsDlgProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam )
{
    HRESULT hr;

    switch( msg )
    {
        case WM_INITDIALOG:
            {
                SetDlgItemText( hDlg, IDC_PLAYER_NAME_EDIT, g_strLocalPlayerName );

                // Load and set the icon
#ifdef _WIN64
                HINSTANCE hInst = (HINSTANCE) GetWindowLongPtr( hDlg, GWLP_HINSTANCE );
#else
                HINSTANCE hInst = (HINSTANCE) GetWindowLong( hDlg, GWL_HINSTANCE );
#endif
                HICON hIcon = LoadIcon( hInst, MAKEINTRESOURCE( IDI_MAIN ) );
                SendMessage( hDlg, WM_SETICON, ICON_BIG,   (LPARAM) hIcon );  // Set big icon
                SendMessage( hDlg, WM_SETICON, ICON_SMALL, (LPARAM) hIcon );  // Set small icon

                // Set the window title
                TCHAR strWindowTitle[256];
                sprintf( strWindowTitle, "%s - Multiplayer Connect", g_strAppName );
                SetWindowText( hDlg, strWindowTitle );

                if( FAILED( hr = DPConnect_ConnectionsDlgFillListBox( hDlg ) ) )
                    EndDialog( hDlg, EXITCODE_ERROR );
            }
            break;

        case WM_COMMAND:
            switch( LOWORD(wParam) )
            {
                case IDC_CONNECTION_LIST:
                    if( HIWORD(wParam) != LBN_DBLCLK )
                        break;
                    // Fall through

                case IDOK:
                    if( FAILED( hr = DPConnect_ConnectionsDlgOnOK( hDlg ) ) )
                        EndDialog( hDlg, EXITCODE_ERROR );

                    break;

                case IDCANCEL:
                    EndDialog( hDlg, EXITCODE_QUIT );
                    break;

                default:
                    return FALSE; // Message not handled
            }
            break;
        
        case WM_DESTROY:
            DPConnect_ConnectionsDlgCleanup( hDlg );
            break;

        default:
            return FALSE; // Message not handled
    }

    // Message was handled
    return TRUE; 
}




//-----------------------------------------------------------------------------
// Name: DPConnect_ConnectionsDlgFillListBox()
// Desc: Fills the DirectPlay connection listbox, and also adds
//       a "Wait for Lobby" connection option.
//-----------------------------------------------------------------------------
HRESULT DPConnect_ConnectionsDlgFillListBox( HWND hDlg )
{
    HRESULT       hr;
    HWND          hWndListBox = GetDlgItem( hDlg, IDC_CONNECTION_LIST );
    LPDIRECTPLAY4 pDP = NULL;
    int           nIndex;

    // Create a IDirectPlay object
    if( FAILED( hr = CoCreateInstance( CLSID_DirectPlay, NULL, CLSCTX_ALL, 
                                       IID_IDirectPlay4A, (VOID**)&pDP ) ) )
    {
        if( hr == E_NOINTERFACE )
        {
            MessageBox( NULL, TEXT("This application requires DirectPlay 6 or later. "
                        "The sample will now quit."),
                        TEXT("DirectPlay Sample"), MB_OK | MB_ICONERROR );
        }

        return hr;
    }

    // Enumerate all DirectPlay connections, and store them in the listbox
    if( FAILED( hr = pDP->EnumConnections( &g_AppGUID, DPConnect_EnumConnectionsCallback, 
                                           hWndListBox, 0 ) ) )
    {
        SAFE_RELEASE( pDP );
        return hr;
    }

    SAFE_RELEASE( pDP );
    
    // Add "Wait for Lobby Connection" selection in list box
    SendMessage( hWndListBox, LB_ADDSTRING, 0, 
                 (LPARAM)"Wait for Lobby Connection" );

    SetFocus( hWndListBox );
    nIndex = (int)SendMessage( hWndListBox, LB_FINDSTRINGEXACT, -1, 
                               (LPARAM)g_strPreferredProvider );
    if( nIndex != LB_ERR ) 
        SendMessage( hWndListBox, LB_SETCURSEL, nIndex, 0 );
    else
        SendMessage( hWndListBox, LB_SETCURSEL, 0, 0 );

    return S_OK;
}




//-----------------------------------------------------------------------------
// Name: DPConnect_EnumConnectionsCallback()
// Desc: Enumerates through all DirectPlay connections types, 
//       and stores them in the listbox
//-----------------------------------------------------------------------------
BOOL FAR PASCAL DPConnect_EnumConnectionsCallback( LPCGUID   pguidSP, 
                                                   VOID*     pConnection, 
                                                   DWORD     dwConnectionSize, 
                                                   LPCDPNAME pName, 
                                                   DWORD     dwFlags, 
                                                   VOID*     pvContext )
{
    HRESULT       hr;
    LPDIRECTPLAY4 pDP = NULL;
    VOID*         pConnectionBuffer = NULL;
    HWND          hWndListBox = (HWND)pvContext;
    LRESULT       iIndex;

    // Create a IDirectPlay object
    if( FAILED( hr = CoCreateInstance( CLSID_DirectPlay, NULL, CLSCTX_ALL, 
                                       IID_IDirectPlay4A, (VOID**)&pDP ) ) )
        return FALSE; // Error, stop enumerating
    
    // Test the if the connection is available by attempting to initialize 
    // the connection
    if( FAILED( hr = pDP->InitializeConnection( pConnection, 0 ) ) )
    {
        SAFE_RELEASE( pDP );
        return TRUE; // Unavailable connection, keep enumerating
    }

    // Don't need the IDirectPlay interface anymore, so release it
    SAFE_RELEASE( pDP ); 
    
    // Found a good connection, so put it in the listbox
    iIndex = SendMessage( hWndListBox, LB_ADDSTRING, 0, 
                          (LPARAM)pName->lpszShortNameA );
    if( iIndex == CB_ERR )
        return FALSE; // Error, stop enumerating

    pConnectionBuffer = new BYTE[ dwConnectionSize ];
    if( pConnectionBuffer == NULL )
        return FALSE; // Error, stop enumerating

    // Store pointer to GUID in listbox
    memcpy( pConnectionBuffer, pConnection, dwConnectionSize );
    SendMessage( hWndListBox, LB_SETITEMDATA, iIndex, 
                 (LPARAM)pConnectionBuffer );

    return TRUE; // Keep enumerating
}




//-----------------------------------------------------------------------------
// Name: DPConnect_ConnectionsDlgOnOK()
// Desc: Stores the player name g_strPlayerName, and in creates a IDirectPlay
//       object based on the connection type the user selected. 
//-----------------------------------------------------------------------------
HRESULT DPConnect_ConnectionsDlgOnOK( HWND hDlg )
{
    VOID*   pConnection = NULL;
    LRESULT iIndex;
    HRESULT hr;

    GetDlgItemText( hDlg, IDC_PLAYER_NAME_EDIT, g_strLocalPlayerName, 
                    MAX_PLAYER_NAME );

    if( strlen( g_strLocalPlayerName ) == 0 )
    {
        MessageBox( hDlg, TEXT("You must enter a valid player name."),
                    TEXT("DirectPlay Sample"), MB_OK );
        return S_OK;
    }

    HWND hWndListBox = GetDlgItem( hDlg, IDC_CONNECTION_LIST );

    iIndex = SendMessage( hWndListBox, LB_GETCURSEL, 0, 0 );
    SendMessage( hWndListBox, LB_GETTEXT, iIndex, (LPARAM)g_strPreferredProvider );

    pConnection = (VOID*) SendMessage( hWndListBox, LB_GETITEMDATA, iIndex, 0 );
    if( NULL == pConnection )
    {
        if( FAILED( hr = DPConnect_WaitForLobbyLaunch( hDlg ) ) )
        {
            if( hr == DPERR_USERCANCEL )
                return S_OK;

            return hr;
        }

        EndDialog( hDlg, EXITCODE_LOBBYCONNECT );
        return S_OK;
    }

    // Release previously selected DirectPlay object, if any.
    SAFE_RELEASE( g_pDP );

    // Create the DirectPlay object
    if( FAILED( hr = CoCreateInstance( CLSID_DirectPlay, NULL, CLSCTX_ALL, 
                                       IID_IDirectPlay4A, (VOID**)&g_pDP ) ) )
        return hr;
    
    // Initialize the connection based on the selected connection type
    if( FAILED( hr = g_pDP->InitializeConnection( pConnection, 0 ) ) )
        return hr;

    EndDialog( hDlg, EXITCODE_FORWARD );

    return S_OK;
}




//-----------------------------------------------------------------------------
// Name: DPConnect_ConnectionsDlgCleanup()
// Desc: Deletes the connection buffers from the listbox
//-----------------------------------------------------------------------------
VOID DPConnect_ConnectionsDlgCleanup( HWND hDlg )
{
    VOID*   pConnectionBuffer = NULL;
    DWORD   iIndex;
    DWORD   dwCount;

    HWND hWndListBox = GetDlgItem( hDlg, IDC_CONNECTION_LIST );

    dwCount = (DWORD)SendMessage( hWndListBox, LB_GETCOUNT, 0, 0 );
    for( iIndex = 0; iIndex < dwCount; iIndex++ )
    {
        pConnectionBuffer = (VOID*) SendMessage( hWndListBox, LB_GETITEMDATA, 
                                                 iIndex, 0 );
        SAFE_DELETE_ARRAY( pConnectionBuffer );
    }
}




//-----------------------------------------------------------------------------
// Name: DPConnect_SessionsDlgProc()
// Desc: Handles messages fro the multiplayer games dialog
//-----------------------------------------------------------------------------
INT_PTR CALLBACK DPConnect_SessionsDlgProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam )
{
    HRESULT hr;

    switch( msg )
    {
        case WM_INITDIALOG:
            {
                // Load and set the icon
#ifdef _WIN64
                HINSTANCE hInst = (HINSTANCE) GetWindowLongPtr( hDlg, GWLP_HINSTANCE );
#else
                HINSTANCE hInst = (HINSTANCE) GetWindowLong( hDlg, GWL_HINSTANCE );
#endif
                HICON hIcon = LoadIcon( hInst, MAKEINTRESOURCE( IDI_MAIN ) );
                SendMessage( hDlg, WM_SETICON, ICON_BIG,   (LPARAM) hIcon );  // Set big icon
                SendMessage( hDlg, WM_SETICON, ICON_SMALL, (LPARAM) hIcon );  // Set small icon

                // Set the window title
                TCHAR strWindowTitle[256];
                sprintf( strWindowTitle, "%s - Multiplayer Games", g_strAppName );
                SetWindowText( hDlg, strWindowTitle );

                g_bSearchingForSessions = FALSE;
                SetDlgItemText( hDlg, IDC_SEARCH_CHECK, "Start Search" );
                DPConnect_SessionsDlgInitListbox( hDlg );
            }
            break;

        case WM_TIMER:
            if( FAILED( hr = DPConnect_SessionsDlgShowGames( hDlg ) ) )
            {
                KillTimer( hDlg, 1 );
                MessageBox( hDlg, TEXT("Error enumerating DirectPlay games."),
                            TEXT("DirectPlay Sample"), 
                            MB_OK | MB_ICONERROR );
            }
            break;

        case WM_COMMAND:
            switch( LOWORD(wParam) )
            {
                case IDC_SEARCH_CHECK:
                    g_bSearchingForSessions = !g_bSearchingForSessions;

                    if( g_bSearchingForSessions )
                    {
                        // Start the timer, and start the async enumeratation
                        SetDlgItemText( hDlg, IDC_SEARCH_CHECK, "Searching..." );
                        SetTimer( hDlg, 1, 250, NULL );
                        if( FAILED( hr = DPConnect_SessionsDlgShowGames( hDlg ) ) )
                        {
                            KillTimer( hDlg, 1 );
                            MessageBox( hDlg, TEXT("Error enumerating DirectPlay games."),
                                        TEXT("DirectPlay Sample"), 
                                        MB_OK | MB_ICONERROR );
                        }
                    }
                    else
                    {
                        // Stop the timer, and stop the async enumeration
                        KillTimer( hDlg, 1 );

                        DPSESSIONDESC2 dpsd;
                        ZeroMemory( &dpsd, sizeof(dpsd) );
                        dpsd.dwSize          = sizeof(dpsd);
                        dpsd.guidApplication = g_AppGUID;
                        hr = g_pDP->EnumSessions( &dpsd, 0, DPConnect_EnumSessionsCallback, 
                                                  NULL,  DPENUMSESSIONS_STOPASYNC );
                        if( hr != DPERR_GENERIC && FAILED(hr) ) 
                            EndDialog( hDlg, EXITCODE_ERROR );

                        SetDlgItemText( hDlg, IDC_SEARCH_CHECK, "Start Search" );

                        DPConnect_SessionsDlgInitListbox( hDlg );
                    }
                    break;

                case IDC_GAMES_LIST:
                    if( HIWORD(wParam) != LBN_DBLCLK )
                        break;
                    // Fall through

                case IDC_JOIN:
                    if( FAILED( hr = DPConnect_SessionsDlgJoinGame( hDlg ) ) )
                    {
                        MessageBox( hDlg, TEXT("Unable to join game."),
                                    TEXT("DirectPlay Sample"), 
                                    MB_OK | MB_ICONERROR );
                    }                    
                    break;

                case IDC_CREATE:
                    if( FAILED( hr = DPConnect_SessionsDlgCreateGame( hDlg ) ) )
                    {
                        MessageBox( hDlg, TEXT("Unable to create game."),
                                    TEXT("DirectPlay Sample"), 
                                    MB_OK | MB_ICONERROR );
                    }
                    break;

                case IDCANCEL: // The close button was press
                    EndDialog( hDlg, EXITCODE_QUIT );
                    break;

                case IDC_BACK: // "Cancel" button was pressed
                    EndDialog( hDlg, EXITCODE_BACKUP );
                    break;

                default:
                    return FALSE; // Message not handled
            }
            break;
        
        case WM_DESTROY:
            KillTimer( hDlg, 1 );                    
            DPConnect_SessionsDlgCleanup();
            break;

        default:
            return FALSE; // Message not handled
    }

    // Message was handled
    return TRUE; 
}




//-----------------------------------------------------------------------------
// Name: DPConnect_SessionsDlgInitListbox()
// Desc: Initializes the listbox
//-----------------------------------------------------------------------------
VOID DPConnect_SessionsDlgInitListbox( HWND hDlg )
{
    HWND          hWndListBox = GetDlgItem( hDlg, IDC_GAMES_LIST );
    LPDIRECTPLAY4 pDP = NULL;
    
    // Clear the contents from the list box, and 
    // display "Looking for games" text in listbox
    SendMessage( hWndListBox, LB_RESETCONTENT, 0, 0 );
    if( g_bSearchingForSessions )
    {
        SendMessage( hWndListBox, LB_ADDSTRING, 0, 
                     (LPARAM)"Looking for games..." );
    }
    else
    {
        SendMessage( hWndListBox, LB_ADDSTRING, 0, 
                     (LPARAM)"Click Start Search to see a list of games.  "
                             "Click Create to start a new game." );
    }

    SendMessage( hWndListBox, LB_SETITEMDATA,  0, NULL );
    SendMessage( hWndListBox, LB_SETCURSEL,    0, 0 );

    // Disable the join button until sessions are found
    EnableWindow( GetDlgItem( hDlg, IDC_JOIN ), FALSE );
}




//-----------------------------------------------------------------------------
// Name: DPConnect_SessionsDlgShowGames()
// Desc: Enumerates the DirectPlay sessions, and displays them in the listbox
//-----------------------------------------------------------------------------
HRESULT DPConnect_SessionsDlgShowGames( HWND hDlg )
{
    HRESULT        hr;
    HWND           hWndListBox = GetDlgItem( hDlg, IDC_GAMES_LIST );
    DPSESSIONDESC2 dpsd;
    DPSessionInfo* pDPSISelected = NULL;
    int            nItemSelected;
    GUID           guidSelectedSession;
    BOOL           bFindSelectedGUID;
    BOOL           bFoundSelectedGUID;

    // Try to keep the same session selected unless it goes away or 
    // there is no real session currently selected
    bFindSelectedGUID  = FALSE;
    bFoundSelectedGUID = TRUE;

    nItemSelected = (int)SendMessage( hWndListBox, LB_GETCURSEL, 0, 0 );
    if( nItemSelected != LB_ERR )
    {
        pDPSISelected = (DPSessionInfo*) SendMessage( hWndListBox, LB_GETITEMDATA, 
                                                      nItemSelected, 0 );
        if( pDPSISelected != NULL )
        {
            guidSelectedSession = pDPSISelected->guidSession;
            bFindSelectedGUID = TRUE;
        }
    }
    
    // Remove the previous games from the linked list
    DPConnect_SessionsDlgCleanup();

    // Enum sessions and let DirectPlay decide the timeout
    ZeroMemory( &dpsd, sizeof(dpsd) );
    dpsd.dwSize          = sizeof(dpsd);
    dpsd.guidApplication = g_AppGUID;

    // Enumerate all the active DirectPlay games on the selected connection
    hr = g_pDP->EnumSessions( &dpsd, 0, DPConnect_EnumSessionsCallback, NULL, 
                              DPENUMSESSIONS_ALL | DPENUMSESSIONS_ASYNC );    
    if( FAILED(hr) )
    {
        if( hr == DPERR_USERCANCEL )
        {
            // The user canceled the DirectPlay connection dialog, 
            // so stop the search
            if( g_bSearchingForSessions )
            {
                CheckDlgButton( hDlg, IDC_SEARCH_CHECK, BST_UNCHECKED );
                SendMessage( hDlg, WM_COMMAND, IDC_SEARCH_CHECK, 0 );
            }

            return S_OK;
        }
        else 
        {
            DPConnect_SessionsDlgInitListbox( hDlg );
            if ( hr == DPERR_CONNECTING )
                return S_OK;

            return hr;
        }
    }

    // Tell listbox not to redraw itself since the contents are going to change
    SendMessage( hWndListBox, WM_SETREDRAW, FALSE, 0 );

    // Add the enumerated sessions to the listbox
    if( g_DPSIHead.pDPSINext != &g_DPSIHead )
    {
        // Clear the contents from the list box and enable the join button
        SendMessage( hWndListBox, LB_RESETCONTENT, 0, 0 );
        EnableWindow( GetDlgItem( hDlg, IDC_JOIN ), TRUE );
        
        DPSessionInfo* pDPSI = g_DPSIHead.pDPSINext;
        while ( pDPSI != &g_DPSIHead )
        {
            // Add session to the list box
            int nIndex = (int)SendMessage( hWndListBox, LB_ADDSTRING, 0, 
                                           (LPARAM)pDPSI->szSession );
            SendMessage( hWndListBox, LB_SETITEMDATA, nIndex, (LPARAM)pDPSI );

            if( bFindSelectedGUID )
            {
                // Look for the session the was selected before
                if( pDPSI->guidSession == guidSelectedSession )
                {
                    SendMessage( hWndListBox, LB_SETCURSEL, nIndex, 0 );
                    bFoundSelectedGUID = TRUE;
                }
            }

            pDPSI = pDPSI->pDPSINext;
        }

        if( !bFindSelectedGUID || !bFoundSelectedGUID )
            SendMessage( hWndListBox, LB_SETCURSEL, 0, 0 );
    }
    else
    {
        // There are no active session, so just reset the listbox
        DPConnect_SessionsDlgInitListbox( hDlg );
    }

    // Tell listbox to redraw itself now since the contents have changed
    SendMessage( hWndListBox, WM_SETREDRAW, TRUE, 0 );
    InvalidateRect( hWndListBox, NULL, FALSE );

    return S_OK;
}




//-----------------------------------------------------------------------------
// Name: DPConnect_EnumSessionsCallback()
// Desc: Enumerates the sessions, and stores them in the linked list, g_DPSIHead
//-----------------------------------------------------------------------------
BOOL FAR PASCAL DPConnect_EnumSessionsCallback( LPCDPSESSIONDESC2 pdpsd, 
                                                DWORD* pdwTimeout, 
                                                DWORD dwFlags, 
                                                VOID* pvContext )
{
    DPSessionInfo* pDPSINew = NULL;
    
    if( dwFlags & DPESC_TIMEDOUT )
        return FALSE; // The enumeration has timed out, so stop the enumeration.
    
    // Found a good session, save it
    pDPSINew = new DPSessionInfo; 
    if( NULL == pDPSINew )
        return FALSE;

    ZeroMemory( pDPSINew, sizeof(DPSessionInfo) );

    // Copy the information into pDPSINew
    pDPSINew->guidSession = pdpsd->guidInstance;
    sprintf( pDPSINew->szSession, "%s (%d/%d)", pdpsd->lpszSessionNameA, 
             pdpsd->dwCurrentPlayers, pdpsd->dwMaxPlayers );

    // Add pDPSINew to the circular linked list, g_pDPSIFirst
    pDPSINew->pDPSINext = g_DPSIHead.pDPSINext;
    g_DPSIHead.pDPSINext = pDPSINew;
    
    return TRUE;
}




//-----------------------------------------------------------------------------
// Name: DPConnect_SessionsDlgJoinGame()
// Desc: Joins the selected DirectPlay session
//-----------------------------------------------------------------------------
HRESULT DPConnect_SessionsDlgJoinGame( HWND hDlg )
{
    DPSESSIONDESC2 dpsd;
    DPNAME         dpname;
    HRESULT        hr;
    HWND           hWndListBox = GetDlgItem( hDlg, IDC_GAMES_LIST );
    DPSessionInfo* pDPSISelected = NULL;
    int            nItemSelected;

    nItemSelected = (int)SendMessage( hWndListBox, LB_GETCURSEL, 0, 0 );
    
    // Add status text in list box
    pDPSISelected = (DPSessionInfo*) SendMessage( hWndListBox, LB_GETITEMDATA, 
                                                  nItemSelected, 0 );

    if( NULL == pDPSISelected )
    {
        MessageBox( hDlg, TEXT("There are no games to join."),
                    TEXT("DirectPlay Sample"), MB_OK );
        return S_OK;
    }

    // Setup the DPSESSIONDESC2, and get the session guid from 
    // the selected listbox item
    ZeroMemory( &dpsd, sizeof(dpsd) );
    dpsd.dwSize          = sizeof(dpsd);
    dpsd.guidInstance    = pDPSISelected->guidSession;
    dpsd.guidApplication = g_AppGUID;

    // Join the session
    g_bHostPlayer = FALSE;
    if( FAILED( hr = g_pDP->Open( &dpsd, DPOPEN_JOIN ) ) )
        return hr;

    // Check for Async message support
    // The caps we are checking do not differ for guaranteed msg
    DPCAPS  dpcaps;
    ZeroMemory( &dpcaps, sizeof(DPCAPS) );
    dpcaps.dwSize = sizeof(DPCAPS);
    g_pDP->GetCaps( &dpcaps, 0 );

    // Determine if Aync messages are supported.
    g_bAsyncSupported = (dpcaps.dwFlags & DPCAPS_ASYNCSUPPORTED) != 0;

    // Create player based on g_strLocalPlayerName.  
    // Store the player's DPID in g_LocalPlayerDPID.
    // Also all DirectPlay messages for this player will signal g_hDPMessageEvent
    ZeroMemory( &dpname, sizeof(DPNAME) );
    dpname.dwSize         = sizeof(DPNAME);
    dpname.lpszShortNameA = g_strLocalPlayerName;
    
    if( FAILED( hr = g_pDP->CreatePlayer( &g_LocalPlayerDPID, &dpname, 
                                          g_hDPMessageEvent, NULL, 0, 0 ) ) )
        return hr;

    // DirectPlay connect successful, so end dialog
    EndDialog( hDlg, EXITCODE_FORWARD );

    return S_OK;
}




//-----------------------------------------------------------------------------
// Name: DPConnect_SessionsDlgCreateGame()
// Desc: Asks the user the session name, and creates a new DirectPlay session
//-----------------------------------------------------------------------------
HRESULT DPConnect_SessionsDlgCreateGame( HWND hDlg )
{
    DPSESSIONDESC2 dpsd;
    DPNAME         dpname;
    HRESULT        hr;
    int            nResult; 
    HINSTANCE      hInst;

#ifdef _WIN64
    hInst = (HINSTANCE) GetWindowLongPtr( hDlg, GWLP_HINSTANCE );
#else
    hInst = (HINSTANCE) GetWindowLong( hDlg, GWL_HINSTANCE );
#endif

    // Display a modal multiplayer connect dialog box.
    EnableWindow( hDlg, FALSE ); 
    nResult = (int)DialogBox( hInst, MAKEINTRESOURCE(IDD_MULTIPLAYER_CREATE), 
                         hDlg, (DLGPROC)DPConnect_CreateSessionDlgProc );
    EnableWindow( hDlg, TRUE ); 

    if( nResult == IDCANCEL )
        return S_OK;

    // Setup the DPSESSIONDESC2 based on g_AppGUID, and g_strSessionName.
    // The DPSESSION_KEEPALIVE flag keeps the session alive if players abnormally exit
    ZeroMemory( &dpsd, sizeof(dpsd) );
    dpsd.dwSize           = sizeof(dpsd);
    dpsd.guidApplication  = g_AppGUID;
    dpsd.lpszSessionNameA = g_strSessionName;
    dpsd.dwMaxPlayers     = 10;
    dpsd.dwFlags          = DPSESSION_KEEPALIVE | DPSESSION_MIGRATEHOST;
    if( g_bUseProtocol )
        dpsd.dwFlags |= DPSESSION_DIRECTPLAYPROTOCOL;

    // Create a new session
    g_bHostPlayer = TRUE;
    if( FAILED( hr = g_pDP->Open( &dpsd, DPOPEN_CREATE ) ) )
        return hr;

    // Check for Async message support
    // The caps we are checking do not differ for guaranteed msg
    DPCAPS  dpcaps;
    ZeroMemory( &dpcaps, sizeof(DPCAPS) );
    dpcaps.dwSize = sizeof(DPCAPS);
    g_pDP->GetCaps( &dpcaps, 0 );

    // Determine if Aync messages are supported.
    g_bAsyncSupported = (dpcaps.dwFlags & DPCAPS_ASYNCSUPPORTED) != 0;

    // Create player based on g_strLocalPlayerName.  
    // Store the player's DPID in g_LocalPlayerDPID.
    // Also all DirectPlay messages for this player will signal g_hDPMessageEvent
    ZeroMemory( &dpname, sizeof(DPNAME) );
    dpname.dwSize         = sizeof(DPNAME);
    dpname.lpszShortNameA = g_strLocalPlayerName;

    if( FAILED( hr = g_pDP->CreatePlayer( &g_LocalPlayerDPID, &dpname, 
                                          g_hDPMessageEvent, NULL, 0, 
                                          DPPLAYER_SERVERPLAYER ) ) )
        return hr;

    // DirectPlay connect successful, so end dialog
    EndDialog( hDlg, EXITCODE_FORWARD );

    return S_OK;
}




//-----------------------------------------------------------------------------
// Name: DPConnect_CreateSessionDlgProc()
// Desc: Handles messages fro the multiplayer create game dialog
//-----------------------------------------------------------------------------
INT_PTR CALLBACK DPConnect_CreateSessionDlgProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam )
{
    DWORD dwNameLength;

    switch( msg ) 
    {
        case WM_INITDIALOG:
            SetDlgItemText( hDlg, IDC_EDIT_SESSION_NAME, g_strSessionName );
            return TRUE;

        case WM_COMMAND:
            switch( LOWORD(wParam) )
            {
                case IDOK:
                    dwNameLength = GetDlgItemText( hDlg, IDC_EDIT_SESSION_NAME, 
                                                   g_strSessionName, 
                                                   MAX_SESSION_NAME );
                    if( dwNameLength == 0 )
                        return TRUE; // Don't accept blank session names

                    g_bUseProtocol = TRUE;

                    EndDialog( hDlg, IDOK );
                    return TRUE;

                case IDCANCEL:
                    EndDialog( hDlg, IDCANCEL );
                    return TRUE;
            }
            break;
    }

    return FALSE; // Didn't handle message
}




//-----------------------------------------------------------------------------
// Name: DPConnect_SessionsDlgCleanup()
// Desc: Deletes the linked list, g_DPSIHead
//-----------------------------------------------------------------------------
VOID DPConnect_SessionsDlgCleanup()
{
    DPSessionInfo* pDPSI = g_DPSIHead.pDPSINext;
    DPSessionInfo* pDPSIDelete;

    while ( pDPSI != &g_DPSIHead )
    {
        pDPSIDelete = pDPSI;       
        pDPSI = pDPSI->pDPSINext;

        SAFE_DELETE( pDPSIDelete );
    }

    // Re-link the g_DPSIHead circular linked list
    g_DPSIHead.pDPSINext = &g_DPSIHead;
}




//-----------------------------------------------------------------------------
// Name: DPConnect_WaitForLobbyLaunch()
// Desc: Wait for the a lobby connection and connect when found
//-----------------------------------------------------------------------------
HRESULT DPConnect_WaitForLobbyLaunch( HWND hParentDlg )
{
    HRESULT hr;
    BOOL    bLaunchedByLobby;

    // Create a new lobby object
    SAFE_RELEASE( g_pDPLobby );

    if( FAILED( hr = CoCreateInstance( CLSID_DirectPlayLobby, NULL,
                                       CLSCTX_INPROC_SERVER, IID_IDirectPlayLobby3A,
                                       (VOID**)&g_pDPLobby ) ) )
        return hr;

    // Put the application into wait mode
    if( FAILED( hr = g_pDPLobby->WaitForConnectionSettings( 0 ) ) )
        return hr;

    // Use the IDD_LOBBY_WAIT_STATUS dialog to check for a connection message
    g_bWaitForConnectionSettings = TRUE;

    EnableWindow( hParentDlg, FALSE );

    // Display a dialog box and wait for the connection settings
    DialogBoxParam( NULL, MAKEINTRESOURCE( IDD_LOBBY_WAIT_STATUS ), hParentDlg,
                    (DLGPROC)DPConnect_LobbyWaitDlgProc, (LPARAM)&hr );
   
    EnableWindow( hParentDlg, TRUE );

    if( FAILED(hr) )
    {
        g_pDPLobby->WaitForConnectionSettings( DPLWAIT_CANCEL );
        SAFE_RELEASE( g_pDPLobby );

        return hr;
    }

    // The lobby server has launched the game, so check for a lobby
    // launch to continue on to connect to the session and create the player
    if( FAILED( hr = DPConnect_CheckForLobbyLaunch( &bLaunchedByLobby ) ) )
        return hr; // This may be DPERR_USERCANCEL if the user canceled the dialog

    return S_OK;
}




//-----------------------------------------------------------------------------
// Name: DPConnect_CheckForLobbyLaunch()
// Desc: Determines if we were launched by a lobby. If so, gets the connection
//       settings and creates our player using the information from the lobby
//-----------------------------------------------------------------------------
HRESULT DPConnect_CheckForLobbyLaunch( BOOL* pbLaunchedByLobby )
{
    HRESULT hr;
    DWORD   dwSize;

    if( NULL == pbLaunchedByLobby ) 
        return E_FAIL;

    // Create a new lobby object
    SAFE_RELEASE( g_pDPLobby );

    if( FAILED( hr = CoCreateInstance( CLSID_DirectPlayLobby, NULL,
                                       CLSCTX_INPROC_SERVER, IID_IDirectPlayLobby3A,
                                       (VOID**)&g_pDPLobby ) ) )
    {
        if( hr == E_NOINTERFACE )
        {
            MessageBox( NULL, TEXT("This application requires DirectPlay 6 or later. "
                        "The sample will now quit."),
                        TEXT("DirectPlay Sample"), MB_OK | MB_ICONERROR );
        }

        return hr;
    }

    // Get connection settings from the lobby (into g_pdplConnection)
    hr = g_pDPLobby->GetConnectionSettings( 0, NULL, &dwSize );
    if( FAILED(hr) && (DPERR_BUFFERTOOSMALL != hr) )
    {
        if( DPERR_NOTLOBBIED == hr )
        {
            *pbLaunchedByLobby = FALSE;

            // Cleanup since we were not launched from the lobby
            SAFE_DELETE_ARRAY( g_pDPLConnection );
            SAFE_RELEASE( g_pDPLobby );

            return S_OK;
        }

        return hr; 
    }

    // Allocate memory for the connection
    SAFE_DELETE_ARRAY( g_pDPLConnection );
    g_pDPLConnection = (DPLCONNECTION*)new BYTE[ dwSize ];

    // Get the connection settings
    if( FAILED( hr = g_pDPLobby->GetConnectionSettings( 0, g_pDPLConnection, 
                                                        &dwSize ) ) )
        return hr;

    *pbLaunchedByLobby = TRUE;

    if( FAILED( hr = DPConnect_DoLobbyLaunch() ) )
    {
        // Cleanup 
        SAFE_DELETE_ARRAY( g_pDPLConnection );
        SAFE_RELEASE( g_pDPLobby );

        return hr; // This may be DPERR_USERCANCEL if the user canceled the dialog
    }

    return S_OK;
}




//-----------------------------------------------------------------------------
// Name: DPConnect_DoLobbyLaunch()
// Desc: The app was launched by the lobby server, so connect to the session
//       and create the player
//-----------------------------------------------------------------------------
HRESULT DPConnect_DoLobbyLaunch()
{
    HRESULT hr;

    // Detrimine if this is the host player 
    g_bHostPlayer = ( (g_pDPLConnection->dwFlags & DPLCONNECTION_CREATESESSION) != 0 );

    // Set our session flags
    g_pDPLConnection->lpSessionDesc->dwFlags = DPSESSION_KEEPALIVE | 
                                               DPSESSION_MIGRATEHOST;

    // Let lobby know our connection flags
    if( FAILED( hr = g_pDPLobby->SetConnectionSettings( 0, 0, g_pDPLConnection ) ) )
        return hr;

    // If we're hosting, just call ConnectEx syncronously.  Otherwise, display
    // the connecting dialog and do the connect asynchronously.
    if( g_bHostPlayer )
    {
        // Connect to the lobby syncronously
        if( FAILED( hr = g_pDPLobby->ConnectEx( 0, IID_IDirectPlay4A, 
                                                (VOID**)&g_pDP, NULL ) ) )
            return hr;
    }
    else
    {
        // Use the IDD_LOBBY_WAIT_STATUS dialog to attempt to connect to 
        // the new session
        g_bWaitForConnectionSettings = FALSE;

        // Connect to the lobby asyncronously
        DialogBoxParam( NULL, MAKEINTRESOURCE( IDD_LOBBY_WAIT_STATUS ), NULL,
                        (DLGPROC)DPConnect_LobbyWaitDlgProc, (LPARAM)&hr );
        if( FAILED(hr) )
            return hr; // This may be DPERR_USERCANCEL if the user canceled the dialog
    }

    strcpy( g_strLocalPlayerName, g_pDPLConnection->lpPlayerName->lpszShortNameA );

    // Create our player
    DPNAME dpname;
    ZeroMemory( &dpname, sizeof(DPNAME) );
    dpname.dwSize         = sizeof(DPNAME);
    dpname.lpszShortNameA = g_strLocalPlayerName;

    if( FAILED( hr = g_pDP->CreatePlayer( &g_LocalPlayerDPID, &dpname, 
                                          NULL, NULL, 0, 0 ) ) )
        return hr;

    // Successfully lobby launch, note that we don't release the lobby object
    return S_OK;
}




//-----------------------------------------------------------------------------
// Name: DPConnect_LobbyWaitDlgProc()
// Desc: Handles dialog messages.  This dialog starts a timer that attempts to
//       either receive a lobby connection settings message or 
//       connect to new session via the lobby connection depending on the 
//       g_bWaitForConnectionSettings flag.
//-----------------------------------------------------------------------------
INT_PTR CALLBACK DPConnect_LobbyWaitDlgProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam )
{
    static HRESULT* phr = NULL;

    switch( msg )
    {
        case WM_INITDIALOG:
            // Save the HRESULT pointer
            phr = (HRESULT*)lParam;           

            if( g_bWaitForConnectionSettings )
                SetDlgItemText( hDlg, IDC_WAIT_TEXT, TEXT("Waiting for connection...") );
            else
                SetDlgItemText( hDlg, IDC_WAIT_TEXT, TEXT("Finding Game...") );

            // Create a timer.  The dialog will attempt to connect at every WM_TIMER
            SetTimer( hDlg, 0, 250, NULL );
            break;

        case WM_DESTROY:
            KillTimer( hDlg, 0 );
            break;

        case WM_TIMER:
            // Attempt to connect to the game
            if( g_bWaitForConnectionSettings )
            {
                *phr = DPConnect_GetLobbyConnectionSettingsMessage();
                if( *phr == S_OK )
                    EndDialog( hDlg, 0 );
            }
            else
            {
                *phr = g_pDPLobby->ConnectEx( DPCONNECT_RETURNSTATUS, IID_IDirectPlay4A, 
                                              (VOID**)&g_pDP, NULL );
                if( *phr == DPERR_CONNECTING )
                    break; // Try again on the next WM_TIMER message

                EndDialog( hDlg, 0 );
            }

            break;
        
        case WM_COMMAND:
            switch( LOWORD(wParam) )
            {
                case IDC_LOBBYCONNECTCANCEL:
                case IDCANCEL:
                    // the async Connect will be stopped when the
                    // IDirectPlayLobby3 interface is released.
                    
                    // Set the HRESULT to user cancel and close the dialog
                    *phr = DPERR_USERCANCEL;
                    EndDialog( hDlg, 0 );
                    break;

                default:
                    return FALSE; // Message not handled
            }
            break;

        default:
            return FALSE; // Message not handled
    }

    return TRUE; // Message handled
}




//-----------------------------------------------------------------------------
// Name: DPConnect_GetLobbyConnectionSettingsMessage()
// Desc: Attepts to get a lobby connection settings message.  If it does 
//       it returns S_OK otherwise it returns S_FALSE 
//-----------------------------------------------------------------------------
HRESULT DPConnect_GetLobbyConnectionSettingsMessage()
{
    HRESULT hr = NOERROR;
    LPVOID  pvMsgBuffer = NULL;
    DWORD   dwMsgBufferSize = 0;
    DWORD   dwMsgFlags;

    while( TRUE )
    {
        hr = g_pDPLobby->ReceiveLobbyMessage( 0, 0, &dwMsgFlags,
                                              pvMsgBuffer, &dwMsgBufferSize );
        if( hr == DPERR_BUFFERTOOSMALL ) 
        {
            // The current buffer was too small, 
            // so reallocate it and try again
            SAFE_DELETE_ARRAY( pvMsgBuffer );

            pvMsgBuffer = new BYTE[ dwMsgBufferSize ];
            if( pvMsgBuffer == NULL )
                return E_OUTOFMEMORY;

            continue; // Now that the buffer is bigger, try again
        }

        if( DPERR_NOMESSAGES == hr )
            break;

        if( FAILED(hr) )
            break;

        // Found a messages, check to see if its a CONNECTIONSETTINGS message
        if( dwMsgFlags & DPLMSG_SYSTEM )
        {
            // If it is found then stop looking for it
            if( ( (DPLMSG_GENERIC*)pvMsgBuffer)->dwType == DPLSYS_NEWCONNECTIONSETTINGS )
                break;
        }
    }

    // Cleanup buffer
    SAFE_DELETE_ARRAY( pvMsgBuffer );

    // Return S_FALSE if there we no connection settings messages
    if( DPERR_NOMESSAGES == hr )
        return S_FALSE;

    // If the message was recieved the hr should be S_OK
    return hr; 
}


