//-----------------------------------------------------------------------------
//
// Desc: Matchmaking classs for 25 To Life (ID 0x45530018)
// Generated by MatchSim
//
//-----------------------------------------------------------------------------
#include "pch.hpp"
#include "XboxMatchSim.h"


#include "OSIXbox.hpp"




#ifdef _DEBUG
//-----------------------------------------------------------------------------
// Name: Print
// Desc: Write formatted debug output
//-----------------------------------------------------------------------------
static VOID __cdecl Print( const WCHAR* strFormat, ... )
{
    const int MAX_OUTPUT_STR = 80;
    WCHAR strBuffer[ MAX_OUTPUT_STR ];
    va_list pArglist;
    va_start( pArglist, strFormat );

    INT iChars= wvsprintfW( strBuffer, strFormat, pArglist );
    assert( iChars < MAX_OUTPUT_STR );
    (VOID) iChars; // Avoid compiler warning

    OutputDebugStringW( L"\n*** Matchmaking: " );
    OutputDebugStringW( strBuffer );
    OutputDebugStringW( L"\n\n" );

#ifdef ENABLE_OSI_XBOX_LOG
	OSIXbox::Log( "*** Matchmaking: %S\n", strBuffer );
#endif

    va_end( pArglist );
}
#endif




//-----------------------------------------------------------------------------
// Name: CMatchSimSession
// Desc: Constructor
//-----------------------------------------------------------------------------
CMatchSimSession::CMatchSimSession()
{
    m_State = STATE_IDLE;
    m_hSessionTask = NULL;
    m_bUpdate = FALSE;
    m_bListening = FALSE;
    m_bKeyRegistered = FALSE;
    SetupAttributes();
}




//-----------------------------------------------------------------------------
// Name: ~CMatchSimSession
// Desc: Destructor
//-----------------------------------------------------------------------------
CMatchSimSession::~CMatchSimSession()
{
    Reset();
}




//-----------------------------------------------------------------------------
// Name: Reset
// Desc: Reset the Session
//-----------------------------------------------------------------------------
VOID CMatchSimSession::Reset()
{
#ifdef ENABLE_OSI_XBOX_LOG
	OSIXbox::Log( "CMatchSimSession::Reset\n" );
#endif

	Close();
    Listen( FALSE, NO_WAIT );
    PurgeSessionQ( TRUE );
    if( m_bKeyRegistered )
    {
#ifdef ENABLE_OSI_XBOX_LOG
		OSIXbox::Log( "CMatchSimSession::Reset - XNetUnregisterKey 0x%I64X\n", *((__int64*)(&SessionID.ab[0])) );
#endif
        XNetUnregisterKey( &SessionID );
        m_bKeyRegistered = FALSE;
    }
}




//-----------------------------------------------------------------------------
// Name: Create
// Desc: Create a new matchmaking session
//-----------------------------------------------------------------------------
HRESULT CMatchSimSession::Create()
{
#ifdef ENABLE_OSI_XBOX_LOG
	OSIXbox::Log( "CMatchSimSession::Create\n" );
#endif

	assert( m_State == STATE_IDLE );

    HRESULT hr = XOnlineMatchSessionCreate( PublicFilled, PublicOpen,
        PrivateFilled, PrivateOpen, NUM_ATTRIBUTES, m_Attributes, NULL,
        &m_hSessionTask );

    if( hr == S_OK )
    {
#ifdef ENABLE_OSI_XBOX_LOG
				OSIXbox::Log( "CMatchSimSession::Create - XOnlineMatchSessionCreate succeeded with:\n" );
				OSIXbox::Log( "                         - PublicFilled = %u PublicOpen = %u PrivateFilled = %u PrivateOpen = %u\n",
					PublicFilled, PublicOpen, PrivateFilled, PrivateOpen );
				for( int i = 0; i < NUM_ATTRIBUTES; i++ )
				{
					XONLINE_ATTRIBUTE& attr = m_Attributes[ i ];
					if( attr.dwAttributeID == XATTRIB_HOST_TITLE )
					{
						OSIXbox::Log( "                         - [%u] 0x%X = %p (%S)\n", i, attr.dwAttributeID, attr.info.string.lpValue, attr.info.string.lpValue );
					}
					else
					{
						OSIXbox::Log( "                         - [%u] 0x%X = 0x%I64X\n", i, attr.dwAttributeID, attr.info.integer.qwValue );
					}
				}
#endif

		m_State = STATE_CREATING;
    }
    else
    {
#ifdef ENABLE_OSI_XBOX_LOG
		OSIXbox::Log( "CMatchSimSession::Create - XOnlineMatchSessionCreate failed with 0x%X\n", hr );
#endif

#ifdef _DEBUG
        Print( L"Session Creation Failed with 0x%X", hr );

#endif
    }

    return hr;

}




//-----------------------------------------------------------------------------
// Name: Update
// Desc: Update an existing session
//-----------------------------------------------------------------------------
HRESULT CMatchSimSession::Update()
{
    switch ( m_State )
    {
    case STATE_IDLE:
    case STATE_DELETING:
        return E_UNEXPECTED;
    case STATE_CREATING:
    case STATE_UPDATING:
        // If the session is still being created or is in the process of
        // updating, set m_bUpdate so that another update will be done
        // afterwards
        m_bUpdate = TRUE;
        return S_OK;
    case STATE_ACTIVE:
        {
            XOnlineTaskClose( m_hSessionTask );
            m_hSessionTask = NULL;

            HRESULT hr = XOnlineMatchSessionUpdate( SessionID,
                PublicFilled, PublicOpen, PrivateFilled,
                PrivateOpen, NUM_ATTRIBUTES, m_Attributes, NULL,
                &m_hSessionTask );

            m_bUpdate = FALSE;  // Clear update flag since we just updated

            if( SUCCEEDED( hr ) )
            {
#ifdef ENABLE_OSI_XBOX_LOG
				OSIXbox::Log( "CMatchSimSession::Update - XOnlineMatchSessionUpdate succeeded with:\n" );
				OSIXbox::Log( "                         - PublicFilled = %u PublicOpen = %u PrivateFilled = %u PrivateOpen = %u\n",
					PublicFilled, PublicOpen, PrivateFilled, PrivateOpen );
				for( int i = 0; i < NUM_ATTRIBUTES; i++ )
				{
					XONLINE_ATTRIBUTE& attr = m_Attributes[ i ];
					if( attr.dwAttributeID == XATTRIB_HOST_TITLE )
					{
						OSIXbox::Log( "                         - [%u] 0x%X = %p (%S)\n", i, attr.dwAttributeID, attr.info.string.lpValue, attr.info.string.lpValue );
					}
					else
					{
						OSIXbox::Log( "                         - [%u] 0x%X = 0x%I64X\n", i, attr.dwAttributeID, attr.info.integer.qwValue );
					}
				}
#endif

                m_State = STATE_UPDATING;
            }
            else
            {
#ifdef ENABLE_OSI_XBOX_LOG
				OSIXbox::Log( "CMatchSimSession::Update - XOnlineMatchSessionUpdate failed with 0x%X\n", hr );
#endif

#ifdef _DEBUG
                Print( L"Session Update Failed with 0x%X", hr );
#endif
                Close();
            }

            return hr;
        }
    default:
        assert(0);
        return E_UNEXPECTED;

    }
}




//-----------------------------------------------------------------------------
// Name: Delete
// Desc: Delete an existing session
//-----------------------------------------------------------------------------
HRESULT CMatchSimSession::Delete()
{
#ifdef ENABLE_OSI_XBOX_LOG
	OSIXbox::Log( "CMatchSimSession::Delete\n" );
#endif

	HRESULT hr = S_OK;


    switch( m_State )
    {
    case STATE_IDLE:
        break;
    case STATE_CREATING:
        Close();  // Close down create task
        break;
    case STATE_UPDATING:
        Close();  // Close down update task
        Listen( FALSE );  // Send go-aways
        break;
    case STATE_DELETING:
        break;
    case STATE_ACTIVE:
        if( m_bListening )
        {
            Listen( FALSE );
        }
        else
        {
#ifdef ENABLE_OSI_XBOX_LOG
			OSIXbox::Log( "CMatchSimSession::Delete - XNetUnregisterKey 0x%I64X\n", *((__int64*)(&SessionID.ab[0])) );
#endif
            INT iResult = XNetUnregisterKey( &SessionID );
            assert( iResult == 0 );
            (VOID) iResult; // Avoid compiler warning
        }
        m_bKeyRegistered = FALSE;
        Close();
        hr = XOnlineMatchSessionDelete( SessionID, NULL, &m_hSessionTask );
        if(SUCCEEDED( hr ) )
        {
#ifdef ENABLE_OSI_XBOX_LOG
			OSIXbox::Log( "CMatchSimSession::Delete - XOnlineMatchSessionDelete Succeeded\n" );
#endif

            m_State = STATE_DELETING;
        }
        else
        {
#ifdef ENABLE_OSI_XBOX_LOG
			OSIXbox::Log( "CMatchSimSession::Delete - XOnlineMatchSessionDelete failed with 0x%X\n", hr );
#endif

#ifdef _DEBUG
            Print( L"Session Delete Failed with 0x%X", hr );
#endif
        }

        break;
    }

    return hr;
}




//-----------------------------------------------------------------------------
// Name: ProcessStateCreateSession
// Desc: Continue servicing the creation task
//-----------------------------------------------------------------------------
HRESULT CMatchSimSession::ProcessStateCreateSession()
{
#ifdef ENABLE_OSI_XBOX_LOG
	OSIXbox::Log( "CMatchSimSession::ProcessStateCreateSession\n" );
#endif

	HRESULT hr  = XOnlineTaskContinue( m_hSessionTask );
    if( hr != XONLINETASK_S_RUNNING )
    {
        if( FAILED( hr ) )
        {
            Close();

#ifdef ENABLE_OSI_XBOX_LOG
			OSIXbox::Log( "CMatchSimSession::ProcessStateCreateSession - XOnlineTaskContinue failed with 0x%X\n", hr );
#endif

#ifdef _DEBUG
            Print( L"Session Creation Failed with 0x%X", hr );
#endif
        }
        else
        {
 
#ifdef ENABLE_OSI_XBOX_LOG
			OSIXbox::Log( "CMatchSimSession::ProcessStateCreateSession - XOnlineTaskContinue Succeeded\n" );
#endif

			// Extract the new session ID and Key-Exchange Key
            HRESULT hrGet = XOnlineMatchSessionGetInfo(
                m_hSessionTask,  &SessionID, &KeyExchangeKey );
            assert( SUCCEEDED( hrGet ) );
            (VOID)hrGet; // Avoid compiler warning
            m_State = STATE_ACTIVE;

#ifdef ENABLE_OSI_XBOX_LOG
			OSIXbox::Log( "CMatchSimSession::ProcessStateCreateSession - SessionID = 0x%I64X\n", *((ULONGLONG*)(SessionID.ab)));
			OSIXbox::Log( "CMatchSimSession::ProcessStateCreateSession - XNetRegisterKey 0x%I64X\n", *((__int64*)(&SessionID.ab[0])) );
#endif

            INT iKeyRegistered = XNetRegisterKey( &SessionID, 
                &KeyExchangeKey );
            if( iKeyRegistered == WSAENOMORE )
            {
#ifdef _DEBUG
                Print( L"Out of keys... Purging SessionQ and trying again" );
#endif
                // Too many keys have been registered, remove
                // the registered key on the session queue and try again
                PurgeSessionQHead();
#ifdef ENABLE_OSI_XBOX_LOG
				OSIXbox::Log( "CMatchSimSession::ProcessStateCreateSession (previous register failed) - XNetRegisterKey 0x%I64X\n", *((__int64*)(&SessionID.ab[0])) );
#endif
                iKeyRegistered = XNetRegisterKey( &SessionID, 
                    &KeyExchangeKey );
            }
            assert( iKeyRegistered == NO_ERROR );
            m_bKeyRegistered = ( iKeyRegistered == NO_ERROR);

            // Start listening for Qos probes for this new session
            Listen( TRUE );

            // If a call to Update() was made while the session
            // was being created, call Update to send the new
            // information.
            if( m_bUpdate )
            {
                hr = Update();
            }
        }
    }

    return hr;
}




//-----------------------------------------------------------------------------
// Name: ProcessStateUpdateSession
// Desc: Continue servicing the session update task
//-----------------------------------------------------------------------------
HRESULT CMatchSimSession::ProcessStateUpdateSession()
{
#ifdef ENABLE_OSI_XBOX_LOG
	OSIXbox::Log( "CMatchSimSession::ProcessStateUpdateSession\n" );
#endif

	HRESULT hr  = XOnlineTaskContinue( m_hSessionTask );
    if( hr != XONLINETASK_S_RUNNING )
    {
        if( FAILED( hr ) )
        {
            Close();
#ifdef ENABLE_OSI_XBOX_LOG
			OSIXbox::Log( "CMatchSimSession::ProcessStateUpdateSession - XOnlineTaskContinue failed with 0x%X\n", hr );
#endif

#ifdef _DEBUG
            Print( L"Session Update Failed with 0x%X", hr );
#endif
        }
        else
        {
#ifdef ENABLE_OSI_XBOX_LOG
			OSIXbox::Log( "CMatchSimSession::ProcessStateUpdateSession - XOnlineTaskContinue Succeeded\n" );
#endif

			m_State = STATE_ACTIVE;

            // If a call to Update() was made while the session
            // was already being updated, call Update to send the new
            // information.
            if( m_bUpdate )
            {
                hr = Update();
            }
        }
    }

    return hr;
}




//-----------------------------------------------------------------------------
// Name: ProcessStateDeleteSession
// Desc: Continue servicing the session deletion task
//-----------------------------------------------------------------------------
HRESULT CMatchSimSession::ProcessStateDeleteSession()
{
#ifdef ENABLE_OSI_XBOX_LOG
	OSIXbox::Log( "CMatchSimSession::ProcessStateDeleteSession\n" );
#endif

	HRESULT hr  = XOnlineTaskContinue( m_hSessionTask );
    if( hr != XONLINETASK_S_RUNNING )
    {
        if( FAILED( hr ) )
        {
#ifdef ENABLE_OSI_XBOX_LOG
		OSIXbox::Log( "CMatchSimSession::ProcessStateDeleteSession - XOnlineTaskContinue failed with 0x%X\n", hr );
#endif

#ifdef _DEBUG
            Print( L"Session Delete Failed with 0x%X", hr );
#endif
        }
#ifdef ENABLE_OSI_XBOX_LOG
		else
		{
			OSIXbox::Log( "CMatchSimSession::ProcessStateDeleteSession - XOnlineTaskContinue succeeded\n" );
		}
#endif

        Close();
    }

    return hr;
}




//-----------------------------------------------------------------------------
// Name: ProcessStateActiveSession
// Desc: Continue servicing the session task
//-----------------------------------------------------------------------------
HRESULT CMatchSimSession::ProcessStateActiveSession()
{
    HRESULT hr  = XOnlineTaskContinue( m_hSessionTask );
    if( FAILED( hr ) )
    {
        Close();

#ifdef ENABLE_OSI_XBOX_LOG
		OSIXbox::Log( "CMatchSimSession::ProcessStateActiveSession - XOnlineTaskContinue failed with 0x%X\n", hr );
#endif

#ifdef _DEBUG
        Print( L"Session Task Failed with 0x%X", hr );
#endif
    }

    return hr;
}




//-----------------------------------------------------------------------------
// Name: Process
// Desc: Perform a unit of work (such as servicing tasks) as necessary
//-----------------------------------------------------------------------------
HRESULT CMatchSimSession::Process()
{
    PurgeSessionQ();
    HRESULT hr;

    switch( m_State )
    {
    case STATE_IDLE:       hr = XONLINETASK_S_SUCCESS;       break;
    case STATE_CREATING:   hr = ProcessStateCreateSession(); break;
    case STATE_UPDATING:   hr = ProcessStateUpdateSession(); break;
    case STATE_DELETING:   hr = ProcessStateDeleteSession(); break;
    case STATE_ACTIVE:     hr = ProcessStateActiveSession(); break;
    default: assert(0);    return E_UNEXPECTED;
    }

    if( FAILED( hr ) )
    {
        Reset();
    }

    return hr;
}




//-----------------------------------------------------------------------------
// Name: Close
// Desc: Close down any session tasks
//-----------------------------------------------------------------------------
VOID CMatchSimSession::Close()
{
#ifdef ENABLE_OSI_XBOX_LOG
	OSIXbox::Log( "CMatchSimSession::Close\n" );
#endif

    if( m_hSessionTask )
    {
        XOnlineTaskClose( m_hSessionTask);
        m_hSessionTask = NULL;
        m_State = STATE_IDLE;
    }

    m_bUpdate = FALSE;
}




//-----------------------------------------------------------------------------
// Name: PurgeSessionQ
// Desc: Cease Qos listening, and unregister, old SessionIDs
//-----------------------------------------------------------------------------
VOID CMatchSimSession::PurgeSessionQ( BOOL fRemoveAll )
{
    // Cleanup any registered SessionIDs which are sending 
    // go-away qos responses
    const DWORD QOS_PROBE_REJECT_TIMELIMIT = 15000; // timeout in ms

    const QosQEntry *pItem;
    // Entries in the queue are in reverse chronological order, 
    // so we can stop once we encounter an entry that has not reached
    // the time limit
    while( ( pItem = m_SessionQosQ.Head() ) != NULL )
    {
        if( fRemoveAll || 
            GetTickCount() - pItem->dwStartTick >= QOS_PROBE_REJECT_TIMELIMIT )
        {
            PurgeSessionQHead();
        }
        else
            break;
    }
}




//-----------------------------------------------------------------------------
// Name: PurgeSessionQHead
// Desc: Cease Qos listening, and unregister, the first session on the queue
//-----------------------------------------------------------------------------
VOID CMatchSimSession::PurgeSessionQHead()
{
#ifdef ENABLE_OSI_XBOX_LOG
	OSIXbox::Log( "CMatchSimSession::PurgeSessionQHead\n" );
#endif

	const QosQEntry *pItem;
    if ( ( pItem = m_SessionQosQ.Head() ) != NULL )
    {
#ifdef _DEBUG
        Print( L"Qos listening rejection period expired for 0x%X", pItem );
#endif
        INT iQos = XNetQosListen( &pItem->SessionID, NULL, 0, 0, XNET_QOS_LISTEN_RELEASE );
        assert( iQos == 0 );
        (VOID) iQos;
        // If this SessionID is not in use, unregister it
        if( !m_bKeyRegistered || memcmp( &pItem->SessionID, &SessionID, sizeof( XNKID ) ) != 0 )
        {
#ifdef ENABLE_OSI_XBOX_LOG
			OSIXbox::Log( "CMatchSimSession::PurgeSessionQHead - XNetUnregisterKey 0x%I64X\n", *((__int64*)(&pItem->SessionID.ab[0])) );
#endif
            INT iResult = XNetUnregisterKey( &pItem->SessionID );
            assert( iResult == 0 );
            (VOID) iResult;
        }
        m_SessionQosQ.Dequeue();
    }
}




//-----------------------------------------------------------------------------
// Name: Listen
// Desc: Control Qos listening
// bEnable     dwBitsPerSec
// -------     ---------
// TRUE        Bandwidth setting (zero for default)
// FALSE       Bandwidth setting (zero is default, NO_WAIT means shut down
//             immediately)
//-----------------------------------------------------------------------------
VOID CMatchSimSession::Listen( BOOL bEnable, DWORD dwBitsPerSec )
{
    if( bEnable )
    {
#ifdef ENABLE_OSI_XBOX_LOG
		OSIXbox::Log( "CMatchSimSession::Listen - QOS listening enabled\n" );
#endif

        m_SessionQosQ.Remove( SessionID );
        INT iQos = XNetQosListen( &SessionID, (BYTE *) m_QosResponse.Data,
            m_QosResponse.Length, dwBitsPerSec, 
            XNET_QOS_LISTEN_ENABLE | XNET_QOS_LISTEN_SET_DATA |
            XNET_QOS_LISTEN_SET_BITSPERSEC );
        assert( iQos == 0 );
        (VOID)iQos; // Avoid compiler warning
        m_bListening = TRUE;
    }
    else
    {
        if( m_bListening )
        {
            INT iQos;
            m_bListening = FALSE;
            if( dwBitsPerSec == NO_WAIT ) // Stop listening, and release Qos resources
            {
#ifdef _DEBUG
                Print( L"Qos listening stopped" );
#endif
                iQos = XNetQosListen( &SessionID, NULL, 0, 0, XNET_QOS_LISTEN_RELEASE );
                assert( iQos == 0 );
            }
            else
            {
                // Start rejecting probes for a period of time by sending a "go away"
                // response.  Then, after a certain period of time, actually stop
                // listening by releasing Qos resources
                m_SessionQosQ.Add( SessionID, GetTickCount() );
                iQos = XNetQosListen( &SessionID, NULL, 0, 
                    dwBitsPerSec, 
                    XNET_QOS_LISTEN_DISABLE | XNET_QOS_LISTEN_SET_BITSPERSEC );
                assert( iQos == 0 );
#ifdef _DEBUG
                Print( L"Qos probe rejection period started for 0x%X", m_SessionQosQ.Tail() );
#endif
            }
            (VOID)iQos; // Avoid compiler warning
        }
    }
}




//-----------------------------------------------------------------------------
// Name: GetQosResponse
// Desc: Return the value of the Qos Response
//-----------------------------------------------------------------------------
CBlob CMatchSimSession::GetQosResponse()
{
    return m_QosResponse;
}




//-----------------------------------------------------------------------------
// Name: SetQosResponse
// Desc: Set data to be sent in response to Qos probes
//-----------------------------------------------------------------------------
VOID  CMatchSimSession::SetQosResponse( CBlob Value )
{
    m_QosResponse = Value;
    if( m_bListening )
    {
        // Call XNetQosListen to set the new value
        INT iQos = XNetQosListen(&SessionID, (BYTE *) m_QosResponse.Data,
            m_QosResponse.Length, 0, XNET_QOS_LISTEN_SET_DATA  );
        assert( iQos == 0 );
        (VOID)iQos; // Avoid compiler warning
    }
}




//-----------------------------------------------------------------------------
// Name: SetupAttributes
// Desc: Initialize the m_Attributes array and related string/blob buffers
//-----------------------------------------------------------------------------
VOID CMatchSimSession::SetupAttributes()
{
    ZeroMemory( &m_Attributes, sizeof( m_Attributes ) );
    m_Attributes[CONSOLE_REGION_INDEX].dwAttributeID = XATTRIB_CONSOLE_REGION;
    m_Attributes[GAME_VERSION_INDEX].dwAttributeID = XATTRIB_GAME_VERSION;
    m_Attributes[ADDITIONAL_CONTENT_INDEX].dwAttributeID = XATTRIB_ADDITIONAL_CONTENT;
    m_Attributes[LEVEL_INDEX_INDEX].dwAttributeID = XATTRIB_LEVEL_INDEX;
    m_Attributes[GAME_TYPE_INDEX].dwAttributeID = XATTRIB_GAME_TYPE;
    m_Attributes[TIME_LIMIT_INDEX].dwAttributeID = XATTRIB_TIME_LIMIT;
    m_Attributes[SCORE_LIMIT_INDEX].dwAttributeID = XATTRIB_SCORE_LIMIT;
    m_Attributes[TAG_LIMIT_INDEX].dwAttributeID = XATTRIB_TAG_LIMIT;
    m_Attributes[BEST_OF_SERIES_INDEX].dwAttributeID = XATTRIB_BEST_OF_SERIES;
    m_Attributes[PLAYERS_RESPAWN_INDEX].dwAttributeID = XATTRIB_PLAYERS_RESPAWN;
    m_Attributes[FRIENDLY_FIRE_INDEX].dwAttributeID = XATTRIB_FRIENDLY_FIRE;
    m_Attributes[APPREHENSION_INDEX].dwAttributeID = XATTRIB_APPREHENSION;
    m_Attributes[SELF_DAMAGE_INDEX].dwAttributeID = XATTRIB_SELF_DAMAGE;
    m_Attributes[ALTERNATING_TEAMS_INDEX].dwAttributeID = XATTRIB_ALTERNATING_TEAMS;
    m_Attributes[MAX_PLAYERS_INDEX].dwAttributeID = XATTRIB_MAX_PLAYERS;
    m_Attributes[HOST_TITLE_INDEX].dwAttributeID = XATTRIB_HOST_TITLE;
    m_Attributes[HOST_TITLE_INDEX].info.string.lpValue = m_strHostTitle;
    m_strHostTitle[0] = L'\0';
    m_Attributes[IS_RANKED_INDEX].dwAttributeID = XATTRIB_IS_RANKED;
    m_Attributes[TOTAL_CONNECTED_PLAYERS_INDEX].dwAttributeID = XATTRIB_TOTAL_CONNECTED_PLAYERS;
    m_Attributes[LOAD_OUT_LIMIT_INDEX].dwAttributeID = XATTRIB_LOAD_OUT_LIMIT;
    m_Attributes[VOICE_COUNT_INDEX].dwAttributeID = XATTRIB_VOICE_COUNT;
}



//-----------------------------------------------------------------------------
// Name: GetConsoleRegion
// Desc: Return the value of the 'ConsoleRegion' attribute
//-----------------------------------------------------------------------------
ULONGLONG CMatchSimSession::GetConsoleRegion()
{
    return m_Attributes[CONSOLE_REGION_INDEX].info.integer.qwValue;
}




//-----------------------------------------------------------------------------
// Name: SetConsoleRegion
// Desc: Set the 'ConsoleRegion' attribute
//-----------------------------------------------------------------------------
VOID  CMatchSimSession::SetConsoleRegion( ULONGLONG Value )
{
    m_Attributes[CONSOLE_REGION_INDEX].info.integer.qwValue = Value;
    m_Attributes[CONSOLE_REGION_INDEX].fChanged = TRUE;
}




//-----------------------------------------------------------------------------
// Name: GetGameVersion
// Desc: Return the value of the 'GameVersion' attribute
//-----------------------------------------------------------------------------
ULONGLONG CMatchSimSession::GetGameVersion()
{
    return m_Attributes[GAME_VERSION_INDEX].info.integer.qwValue;
}




//-----------------------------------------------------------------------------
// Name: SetGameVersion
// Desc: Set the 'GameVersion' attribute
//-----------------------------------------------------------------------------
VOID  CMatchSimSession::SetGameVersion( ULONGLONG Value )
{
    m_Attributes[GAME_VERSION_INDEX].info.integer.qwValue = Value;
    m_Attributes[GAME_VERSION_INDEX].fChanged = TRUE;
}




//-----------------------------------------------------------------------------
// Name: GetAdditionalContent
// Desc: Return the value of the 'AdditionalContent' attribute
//-----------------------------------------------------------------------------
ULONGLONG CMatchSimSession::GetAdditionalContent()
{
    return m_Attributes[ADDITIONAL_CONTENT_INDEX].info.integer.qwValue;
}




//-----------------------------------------------------------------------------
// Name: SetAdditionalContent
// Desc: Set the 'AdditionalContent' attribute
//-----------------------------------------------------------------------------
VOID  CMatchSimSession::SetAdditionalContent( ULONGLONG Value )
{
    m_Attributes[ADDITIONAL_CONTENT_INDEX].info.integer.qwValue = Value;
    m_Attributes[ADDITIONAL_CONTENT_INDEX].fChanged = TRUE;
}




//-----------------------------------------------------------------------------
// Name: GetLevelIndex
// Desc: Return the value of the 'LevelIndex' attribute
//-----------------------------------------------------------------------------
ULONGLONG CMatchSimSession::GetLevelIndex()
{
    return m_Attributes[LEVEL_INDEX_INDEX].info.integer.qwValue;
}




//-----------------------------------------------------------------------------
// Name: SetLevelIndex
// Desc: Set the 'LevelIndex' attribute
//-----------------------------------------------------------------------------
VOID  CMatchSimSession::SetLevelIndex( ULONGLONG Value )
{
    m_Attributes[LEVEL_INDEX_INDEX].info.integer.qwValue = Value;
    m_Attributes[LEVEL_INDEX_INDEX].fChanged = TRUE;
}




//-----------------------------------------------------------------------------
// Name: GetGameType
// Desc: Return the value of the 'GameType' attribute
//-----------------------------------------------------------------------------
ULONGLONG CMatchSimSession::GetGameType()
{
    return m_Attributes[GAME_TYPE_INDEX].info.integer.qwValue;
}




//-----------------------------------------------------------------------------
// Name: SetGameType
// Desc: Set the 'GameType' attribute
//-----------------------------------------------------------------------------
VOID  CMatchSimSession::SetGameType( ULONGLONG Value )
{
    m_Attributes[GAME_TYPE_INDEX].info.integer.qwValue = Value;
    m_Attributes[GAME_TYPE_INDEX].fChanged = TRUE;
}




//-----------------------------------------------------------------------------
// Name: GetTimeLimit
// Desc: Return the value of the 'TimeLimit' attribute
//-----------------------------------------------------------------------------
ULONGLONG CMatchSimSession::GetTimeLimit()
{
    return m_Attributes[TIME_LIMIT_INDEX].info.integer.qwValue;
}




//-----------------------------------------------------------------------------
// Name: SetTimeLimit
// Desc: Set the 'TimeLimit' attribute
//-----------------------------------------------------------------------------
VOID  CMatchSimSession::SetTimeLimit( ULONGLONG Value )
{
    m_Attributes[TIME_LIMIT_INDEX].info.integer.qwValue = Value;
    m_Attributes[TIME_LIMIT_INDEX].fChanged = TRUE;
}




//-----------------------------------------------------------------------------
// Name: GetScoreLimit
// Desc: Return the value of the 'ScoreLimit' attribute
//-----------------------------------------------------------------------------
ULONGLONG CMatchSimSession::GetScoreLimit()
{
    return m_Attributes[SCORE_LIMIT_INDEX].info.integer.qwValue;
}




//-----------------------------------------------------------------------------
// Name: SetScoreLimit
// Desc: Set the 'ScoreLimit' attribute
//-----------------------------------------------------------------------------
VOID  CMatchSimSession::SetScoreLimit( ULONGLONG Value )
{
    m_Attributes[SCORE_LIMIT_INDEX].info.integer.qwValue = Value;
    m_Attributes[SCORE_LIMIT_INDEX].fChanged = TRUE;
}




//-----------------------------------------------------------------------------
// Name: GetTagLimit
// Desc: Return the value of the 'TagLimit' attribute
//-----------------------------------------------------------------------------
ULONGLONG CMatchSimSession::GetTagLimit()
{
    return m_Attributes[TAG_LIMIT_INDEX].info.integer.qwValue;
}




//-----------------------------------------------------------------------------
// Name: SetTagLimit
// Desc: Set the 'TagLimit' attribute
//-----------------------------------------------------------------------------
VOID  CMatchSimSession::SetTagLimit( ULONGLONG Value )
{
    m_Attributes[TAG_LIMIT_INDEX].info.integer.qwValue = Value;
    m_Attributes[TAG_LIMIT_INDEX].fChanged = TRUE;
}




//-----------------------------------------------------------------------------
// Name: GetBestOfSeries
// Desc: Return the value of the 'BestOfSeries' attribute
//-----------------------------------------------------------------------------
ULONGLONG CMatchSimSession::GetBestOfSeries()
{
    return m_Attributes[BEST_OF_SERIES_INDEX].info.integer.qwValue;
}




//-----------------------------------------------------------------------------
// Name: SetBestOfSeries
// Desc: Set the 'BestOfSeries' attribute
//-----------------------------------------------------------------------------
VOID  CMatchSimSession::SetBestOfSeries( ULONGLONG Value )
{
    m_Attributes[BEST_OF_SERIES_INDEX].info.integer.qwValue = Value;
    m_Attributes[BEST_OF_SERIES_INDEX].fChanged = TRUE;
}




//-----------------------------------------------------------------------------
// Name: GetPlayersRespawn
// Desc: Return the value of the 'PlayersRespawn' attribute
//-----------------------------------------------------------------------------
ULONGLONG CMatchSimSession::GetPlayersRespawn()
{
    return m_Attributes[PLAYERS_RESPAWN_INDEX].info.integer.qwValue;
}




//-----------------------------------------------------------------------------
// Name: SetPlayersRespawn
// Desc: Set the 'PlayersRespawn' attribute
//-----------------------------------------------------------------------------
VOID  CMatchSimSession::SetPlayersRespawn( ULONGLONG Value )
{
    m_Attributes[PLAYERS_RESPAWN_INDEX].info.integer.qwValue = Value;
    m_Attributes[PLAYERS_RESPAWN_INDEX].fChanged = TRUE;
}




//-----------------------------------------------------------------------------
// Name: GetFriendlyFire
// Desc: Return the value of the 'FriendlyFire' attribute
//-----------------------------------------------------------------------------
ULONGLONG CMatchSimSession::GetFriendlyFire()
{
    return m_Attributes[FRIENDLY_FIRE_INDEX].info.integer.qwValue;
}




//-----------------------------------------------------------------------------
// Name: SetFriendlyFire
// Desc: Set the 'FriendlyFire' attribute
//-----------------------------------------------------------------------------
VOID  CMatchSimSession::SetFriendlyFire( ULONGLONG Value )
{
    m_Attributes[FRIENDLY_FIRE_INDEX].info.integer.qwValue = Value;
    m_Attributes[FRIENDLY_FIRE_INDEX].fChanged = TRUE;
}




//-----------------------------------------------------------------------------
// Name: GetApprehension
// Desc: Return the value of the 'Apprehension' attribute
//-----------------------------------------------------------------------------
ULONGLONG CMatchSimSession::GetApprehension()
{
    return m_Attributes[APPREHENSION_INDEX].info.integer.qwValue;
}




//-----------------------------------------------------------------------------
// Name: SetApprehension
// Desc: Set the 'Apprehension' attribute
//-----------------------------------------------------------------------------
VOID  CMatchSimSession::SetApprehension( ULONGLONG Value )
{
    m_Attributes[APPREHENSION_INDEX].info.integer.qwValue = Value;
    m_Attributes[APPREHENSION_INDEX].fChanged = TRUE;
}




//-----------------------------------------------------------------------------
// Name: GetSelfDamage
// Desc: Return the value of the 'SelfDamage' attribute
//-----------------------------------------------------------------------------
ULONGLONG CMatchSimSession::GetSelfDamage()
{
    return m_Attributes[SELF_DAMAGE_INDEX].info.integer.qwValue;
}




//-----------------------------------------------------------------------------
// Name: SetSelfDamage
// Desc: Set the 'SelfDamage' attribute
//-----------------------------------------------------------------------------
VOID  CMatchSimSession::SetSelfDamage( ULONGLONG Value )
{
    m_Attributes[SELF_DAMAGE_INDEX].info.integer.qwValue = Value;
    m_Attributes[SELF_DAMAGE_INDEX].fChanged = TRUE;
}




//-----------------------------------------------------------------------------
// Name: GetAlternatingTeams
// Desc: Return the value of the 'AlternatingTeams' attribute
//-----------------------------------------------------------------------------
ULONGLONG CMatchSimSession::GetAlternatingTeams()
{
    return m_Attributes[ALTERNATING_TEAMS_INDEX].info.integer.qwValue;
}




//-----------------------------------------------------------------------------
// Name: SetAlternatingTeams
// Desc: Set the 'AlternatingTeams' attribute
//-----------------------------------------------------------------------------
VOID  CMatchSimSession::SetAlternatingTeams( ULONGLONG Value )
{
    m_Attributes[ALTERNATING_TEAMS_INDEX].info.integer.qwValue = Value;
    m_Attributes[ALTERNATING_TEAMS_INDEX].fChanged = TRUE;
}




//-----------------------------------------------------------------------------
// Name: GetMaxPlayers
// Desc: Return the value of the 'MaxPlayers' attribute
//-----------------------------------------------------------------------------
ULONGLONG CMatchSimSession::GetMaxPlayers()
{
    return m_Attributes[MAX_PLAYERS_INDEX].info.integer.qwValue;
}




//-----------------------------------------------------------------------------
// Name: SetMaxPlayers
// Desc: Set the 'MaxPlayers' attribute
//-----------------------------------------------------------------------------
VOID  CMatchSimSession::SetMaxPlayers( ULONGLONG Value )
{
    m_Attributes[MAX_PLAYERS_INDEX].info.integer.qwValue = Value;
    m_Attributes[MAX_PLAYERS_INDEX].fChanged = TRUE;
}




//-----------------------------------------------------------------------------
// Name: GetHostTitle
// Desc: Return the value of the 'HostTitle' attribute
//-----------------------------------------------------------------------------
const WCHAR * CMatchSimSession::GetHostTitle()
{
    return m_strHostTitle;
}




//-----------------------------------------------------------------------------
// Name: SetHostTitle
// Desc: Set the 'HostTitle' attribute
//-----------------------------------------------------------------------------
VOID  CMatchSimSession::SetHostTitle( const WCHAR * Value )
{
    wcscpy( m_strHostTitle, Value );
    m_Attributes[HOST_TITLE_INDEX].fChanged = TRUE;
}




//-----------------------------------------------------------------------------
// Name: GetIsRanked
// Desc: Return the value of the 'IsRanked' attribute
//-----------------------------------------------------------------------------
ULONGLONG CMatchSimSession::GetIsRanked()
{
    return m_Attributes[IS_RANKED_INDEX].info.integer.qwValue;
}




//-----------------------------------------------------------------------------
// Name: SetIsRanked
// Desc: Set the 'IsRanked' attribute
//-----------------------------------------------------------------------------
VOID  CMatchSimSession::SetIsRanked( ULONGLONG Value )
{
    m_Attributes[IS_RANKED_INDEX].info.integer.qwValue = Value;
    m_Attributes[IS_RANKED_INDEX].fChanged = TRUE;
}




//-----------------------------------------------------------------------------
// Name: GetTotalConnectedPlayers
// Desc: Return the value of the 'TotalConnectedPlayers' attribute
//-----------------------------------------------------------------------------
ULONGLONG CMatchSimSession::GetTotalConnectedPlayers()
{
    return m_Attributes[TOTAL_CONNECTED_PLAYERS_INDEX].info.integer.qwValue;
}




//-----------------------------------------------------------------------------
// Name: SetTotalConnectedPlayers
// Desc: Set the 'TotalConnectedPlayers' attribute
//-----------------------------------------------------------------------------
VOID  CMatchSimSession::SetTotalConnectedPlayers( ULONGLONG Value )
{
    m_Attributes[TOTAL_CONNECTED_PLAYERS_INDEX].info.integer.qwValue = Value;
    m_Attributes[TOTAL_CONNECTED_PLAYERS_INDEX].fChanged = TRUE;
}




//-----------------------------------------------------------------------------
// Name: GetLoadOutLimit
// Desc: Return the value of the 'LoadOutLimit' attribute
//-----------------------------------------------------------------------------
ULONGLONG CMatchSimSession::GetLoadOutLimit()
{
    return m_Attributes[LOAD_OUT_LIMIT_INDEX].info.integer.qwValue;
}




//-----------------------------------------------------------------------------
// Name: SetLoadOutLimit
// Desc: Set the 'LoadOutLimit' attribute
//-----------------------------------------------------------------------------
VOID  CMatchSimSession::SetLoadOutLimit( ULONGLONG Value )
{
    m_Attributes[LOAD_OUT_LIMIT_INDEX].info.integer.qwValue = Value;
    m_Attributes[LOAD_OUT_LIMIT_INDEX].fChanged = TRUE;
}




//-----------------------------------------------------------------------------
// Name: GetVoiceCount
// Desc: Return the value of the 'VoiceCount' attribute
//-----------------------------------------------------------------------------
ULONGLONG CMatchSimSession::GetVoiceCount()
{
    return m_Attributes[VOICE_COUNT_INDEX].info.integer.qwValue;
}




//-----------------------------------------------------------------------------
// Name: SetVoiceCount
// Desc: Set the 'VoiceCount' attribute
//-----------------------------------------------------------------------------
VOID  CMatchSimSession::SetVoiceCount( ULONGLONG Value )
{
    m_Attributes[VOICE_COUNT_INDEX].info.integer.qwValue = Value;
    m_Attributes[VOICE_COUNT_INDEX].fChanged = TRUE;
}




//-----------------------------------------------------------------------------
// Name: CMatchSimSessionQosQ
// Desc: Constructor
//-----------------------------------------------------------------------------
CMatchSimSession::CMatchSimSessionQosQ::CMatchSimSessionQosQ()
{
    m_pHead = m_pTail = NULL;
}




//-----------------------------------------------------------------------------
// Name: Add
// Desc: Add a session id to the qos go-away response queue
//-----------------------------------------------------------------------------

VOID CMatchSimSession::CMatchSimSessionQosQ::Add( XNKID & SessionID, DWORD dwStartTick )
{
    assert( m_pHead && m_pTail || !m_pHead && !m_pTail );
    QosQEntry *pItem = new QosQEntry;
    pItem->SessionID = SessionID;
    pItem->dwStartTick = dwStartTick;
    pItem->pNext = NULL;
    if( m_pTail )
        m_pTail->pNext = pItem;
    m_pTail = pItem;
    if( !m_pHead )
        m_pHead = pItem;
}




//-----------------------------------------------------------------------------
// Name: Remove
// Desc: Remove a session from the qos go-away response queue
//-----------------------------------------------------------------------------
VOID CMatchSimSession::CMatchSimSessionQosQ::Remove( XNKID &SessionID )
{
    assert( m_pHead && m_pTail || !m_pHead && !m_pTail );
    QosQEntry *pPrev = NULL;

    for( QosQEntry *pItem = m_pHead; pItem != NULL; pItem = pItem->pNext )
    {
        if( memcmp(&pItem->SessionID, &SessionID, sizeof( XNKID ) ) == 0 )
        {
            if( pPrev )
            {
                pPrev->pNext = pItem->pNext;
            }
            else
            {
                m_pHead = pItem->pNext;
            }
            if( pItem == m_pTail )
            {
                m_pTail = pPrev;
            }
            delete pItem;
            break;
        }
        pPrev = pItem;
    }
}




//-----------------------------------------------------------------------------
// Name: Dequeue
// Desc: Remove the head of the qos go-away response queue
//-----------------------------------------------------------------------------
VOID CMatchSimSession::CMatchSimSessionQosQ::Dequeue()
{
    assert( m_pHead && m_pTail || !m_pHead && !m_pTail );
    QosQEntry *pItem = m_pHead;
    if( pItem )
    {
        m_pHead = pItem->pNext;
        delete pItem;
        if( !m_pHead )
            m_pTail = NULL;
    }
}



// Attribute layout for CommonMatch return attributes.
// This must match the order of both the query return attributes
// and the fields specified in the CCommonMatchResult class.
static const
XONLINE_ATTRIBUTE_SPEC CommonMatchAttributeSpec[]=
{
    { X_ATTRIBUTE_DATATYPE_INTEGER, sizeof( ULONGLONG ) }, // ConsoleRegion
    { X_ATTRIBUTE_DATATYPE_INTEGER, sizeof( ULONGLONG ) }, // GameVersion
    { X_ATTRIBUTE_DATATYPE_INTEGER, sizeof( ULONGLONG ) }, // AdditionalContent
    { X_ATTRIBUTE_DATATYPE_INTEGER, sizeof( ULONGLONG ) }, // LevelIndex
    { X_ATTRIBUTE_DATATYPE_INTEGER, sizeof( ULONGLONG ) }, // GameType
    { X_ATTRIBUTE_DATATYPE_INTEGER, sizeof( ULONGLONG ) }, // TimeLimit
    { X_ATTRIBUTE_DATATYPE_INTEGER, sizeof( ULONGLONG ) }, // ScoreLimit
    { X_ATTRIBUTE_DATATYPE_INTEGER, sizeof( ULONGLONG ) }, // TagLimit
    { X_ATTRIBUTE_DATATYPE_INTEGER, sizeof( ULONGLONG ) }, // BestOfSeries
    { X_ATTRIBUTE_DATATYPE_INTEGER, sizeof( ULONGLONG ) }, // PlayersRespawn
    { X_ATTRIBUTE_DATATYPE_INTEGER, sizeof( ULONGLONG ) }, // FriendlyFire
    { X_ATTRIBUTE_DATATYPE_INTEGER, sizeof( ULONGLONG ) }, // Apprehension
    { X_ATTRIBUTE_DATATYPE_INTEGER, sizeof( ULONGLONG ) }, // SelfDamage
    { X_ATTRIBUTE_DATATYPE_INTEGER, sizeof( ULONGLONG ) }, // AlternatingTeams
    { X_ATTRIBUTE_DATATYPE_INTEGER, sizeof( ULONGLONG ) }, // MaxPlayers
    { X_ATTRIBUTE_DATATYPE_STRING, ( XATTRIB_HOST_TITLE_MAX_LEN + 1 ) * sizeof( WCHAR ) }, // HostTitle
    { X_ATTRIBUTE_DATATYPE_INTEGER, sizeof( ULONGLONG ) }, // IsRanked
    { X_ATTRIBUTE_DATATYPE_INTEGER, sizeof( ULONGLONG ) }, // TotalConnectedPlayers
    { X_ATTRIBUTE_DATATYPE_INTEGER, sizeof( ULONGLONG ) }, // LoadOutLimit
    { X_ATTRIBUTE_DATATYPE_INTEGER, sizeof( ULONGLONG ) }, // VoiceCount
};




//-----------------------------------------------------------------------------
// Name: CCommonMatchQuery
// Desc: Constructor
//-----------------------------------------------------------------------------
CCommonMatchQuery::CCommonMatchQuery()
{
    m_State = STATE_IDLE;
    m_hrQuery = S_FALSE;
    m_hSearchTask = NULL;
    m_pXnQos = NULL;
}



//-----------------------------------------------------------------------------
// Name: CCommonMatchQuery()
// Desc: Destructor
//-----------------------------------------------------------------------------
CCommonMatchQuery::~CCommonMatchQuery()
{
    Cancel();
}



//-----------------------------------------------------------------------------
// Name: Cancel
// Desc: Cancel a CommonMatch query
//-----------------------------------------------------------------------------
void CCommonMatchQuery::Cancel()
{
#ifdef ENABLE_OSI_XBOX_LOG
	OSIXbox::Log( "CMatchSimSession::Cancel\n" );
#endif

	switch( m_State )
    {
    case STATE_IDLE:
       break;
    case STATE_RUNNING:
    case STATE_PROBING_BANDWIDTH:
    case STATE_PROBING_CONNECTIVITY:
    case STATE_DONE:
		if( m_hSearchTask )
        {
            XOnlineTaskClose( m_hSearchTask );
            m_hSearchTask = NULL;
        }
        Clear();
        m_State = STATE_IDLE;
        m_hrQuery = S_FALSE;
        break;
    default:
        assert(0);
    }
}



//-----------------------------------------------------------------------------
// Name: Clear
// Desc: Release resources
//-----------------------------------------------------------------------------
void CCommonMatchQuery::Clear()
{
#ifdef ENABLE_OSI_XBOX_LOG
	OSIXbox::Log( "CMatchSimSession::Clear\n" );
#endif

	if( m_pXnQos )
    {
        XNetQosRelease( m_pXnQos );
        m_pXnQos = NULL;
    }
    Results.Clear();
}



//-----------------------------------------------------------------------------
// Name: Query
// Desc: Execute the CommonMatch query (id 0x1)
//-----------------------------------------------------------------------------
HRESULT CCommonMatchQuery::Query(
                  ULONGLONG ConsoleRegion,
                  ULONGLONG GameVersion,
                  ULONGLONG AdditionalContent,
                  ULONGLONG LevelIndex, // Optional: X_MATCH_NULL_INTEGER to omit
                  ULONGLONG GameType, // Optional: X_MATCH_NULL_INTEGER to omit
                  ULONGLONG TimeLimit, // Optional: X_MATCH_NULL_INTEGER to omit
                  ULONGLONG ScoreLimit, // Optional: X_MATCH_NULL_INTEGER to omit
                  ULONGLONG TagLimit, // Optional: X_MATCH_NULL_INTEGER to omit
                  ULONGLONG BestOfSeries, // Optional: X_MATCH_NULL_INTEGER to omit
                  ULONGLONG PlayersRespawn, // Optional: X_MATCH_NULL_INTEGER to omit
                  ULONGLONG FriendlyFire, // Optional: X_MATCH_NULL_INTEGER to omit
                  ULONGLONG Apprehension, // Optional: X_MATCH_NULL_INTEGER to omit
                  ULONGLONG SelfDamage, // Optional: X_MATCH_NULL_INTEGER to omit
                  ULONGLONG AlternatingTeams, // Optional: X_MATCH_NULL_INTEGER to omit
                  ULONGLONG MaxPlayers, // Optional: X_MATCH_NULL_INTEGER to omit
                  ULONGLONG IsRanked, // Optional: X_MATCH_NULL_INTEGER to omit
                  ULONGLONG TotalConnectedPlayers, // Optional: X_MATCH_NULL_INTEGER to omit
                  ULONGLONG LoadOutLimit, // Optional: X_MATCH_NULL_INTEGER to omit
                  ULONGLONG VoiceCount // Optional: X_MATCH_NULL_INTEGER to omit
                  )
{
    const DWORD SEARCH_PROC_ID = 0x1;

    if( m_State == STATE_DONE ) // Clear existing results
    {
        Clear();
        m_State = STATE_IDLE;
    }
    assert( m_State == STATE_IDLE );
    if (m_State != STATE_IDLE)
        return E_UNEXPECTED;

    XONLINE_ATTRIBUTE QueryParameters[19] = { 0 };
    QueryParameters[0].dwAttributeID = X_ATTRIBUTE_DATATYPE_INTEGER;
    QueryParameters[0].info.integer.qwValue = ConsoleRegion;
    QueryParameters[1].dwAttributeID = X_ATTRIBUTE_DATATYPE_INTEGER;
    QueryParameters[1].info.integer.qwValue = GameVersion;
    QueryParameters[2].dwAttributeID = X_ATTRIBUTE_DATATYPE_INTEGER;
    QueryParameters[2].info.integer.qwValue = AdditionalContent;
    QueryParameters[3].dwAttributeID = ( LevelIndex == X_MATCH_NULL_INTEGER ) ? X_ATTRIBUTE_DATATYPE_NULL : X_ATTRIBUTE_DATATYPE_INTEGER;
    QueryParameters[3].info.integer.qwValue = LevelIndex;
    QueryParameters[4].dwAttributeID = ( GameType == X_MATCH_NULL_INTEGER ) ? X_ATTRIBUTE_DATATYPE_NULL : X_ATTRIBUTE_DATATYPE_INTEGER;
    QueryParameters[4].info.integer.qwValue = GameType;
    QueryParameters[5].dwAttributeID = ( TimeLimit == X_MATCH_NULL_INTEGER ) ? X_ATTRIBUTE_DATATYPE_NULL : X_ATTRIBUTE_DATATYPE_INTEGER;
    QueryParameters[5].info.integer.qwValue = TimeLimit;
    QueryParameters[6].dwAttributeID = ( ScoreLimit == X_MATCH_NULL_INTEGER ) ? X_ATTRIBUTE_DATATYPE_NULL : X_ATTRIBUTE_DATATYPE_INTEGER;
    QueryParameters[6].info.integer.qwValue = ScoreLimit;
    QueryParameters[7].dwAttributeID = ( TagLimit == X_MATCH_NULL_INTEGER ) ? X_ATTRIBUTE_DATATYPE_NULL : X_ATTRIBUTE_DATATYPE_INTEGER;
    QueryParameters[7].info.integer.qwValue = TagLimit;
    QueryParameters[8].dwAttributeID = ( BestOfSeries == X_MATCH_NULL_INTEGER ) ? X_ATTRIBUTE_DATATYPE_NULL : X_ATTRIBUTE_DATATYPE_INTEGER;
    QueryParameters[8].info.integer.qwValue = BestOfSeries;
    QueryParameters[9].dwAttributeID = ( PlayersRespawn == X_MATCH_NULL_INTEGER ) ? X_ATTRIBUTE_DATATYPE_NULL : X_ATTRIBUTE_DATATYPE_INTEGER;
    QueryParameters[9].info.integer.qwValue = PlayersRespawn;
    QueryParameters[10].dwAttributeID = ( FriendlyFire == X_MATCH_NULL_INTEGER ) ? X_ATTRIBUTE_DATATYPE_NULL : X_ATTRIBUTE_DATATYPE_INTEGER;
    QueryParameters[10].info.integer.qwValue = FriendlyFire;
    QueryParameters[11].dwAttributeID = ( Apprehension == X_MATCH_NULL_INTEGER ) ? X_ATTRIBUTE_DATATYPE_NULL : X_ATTRIBUTE_DATATYPE_INTEGER;
    QueryParameters[11].info.integer.qwValue = Apprehension;
    QueryParameters[12].dwAttributeID = ( SelfDamage == X_MATCH_NULL_INTEGER ) ? X_ATTRIBUTE_DATATYPE_NULL : X_ATTRIBUTE_DATATYPE_INTEGER;
    QueryParameters[12].info.integer.qwValue = SelfDamage;
    QueryParameters[13].dwAttributeID = ( AlternatingTeams == X_MATCH_NULL_INTEGER ) ? X_ATTRIBUTE_DATATYPE_NULL : X_ATTRIBUTE_DATATYPE_INTEGER;
    QueryParameters[13].info.integer.qwValue = AlternatingTeams;
    QueryParameters[14].dwAttributeID = ( MaxPlayers == X_MATCH_NULL_INTEGER ) ? X_ATTRIBUTE_DATATYPE_NULL : X_ATTRIBUTE_DATATYPE_INTEGER;
    QueryParameters[14].info.integer.qwValue = MaxPlayers;
    QueryParameters[15].dwAttributeID = ( IsRanked == X_MATCH_NULL_INTEGER ) ? X_ATTRIBUTE_DATATYPE_NULL : X_ATTRIBUTE_DATATYPE_INTEGER;
    QueryParameters[15].info.integer.qwValue = IsRanked;
    QueryParameters[16].dwAttributeID = ( TotalConnectedPlayers == X_MATCH_NULL_INTEGER ) ? X_ATTRIBUTE_DATATYPE_NULL : X_ATTRIBUTE_DATATYPE_INTEGER;
    QueryParameters[16].info.integer.qwValue = TotalConnectedPlayers;
    QueryParameters[17].dwAttributeID = ( LoadOutLimit == X_MATCH_NULL_INTEGER ) ? X_ATTRIBUTE_DATATYPE_NULL : X_ATTRIBUTE_DATATYPE_INTEGER;
    QueryParameters[17].info.integer.qwValue = LoadOutLimit;
    QueryParameters[18].dwAttributeID = ( VoiceCount == X_MATCH_NULL_INTEGER ) ? X_ATTRIBUTE_DATATYPE_NULL : X_ATTRIBUTE_DATATYPE_INTEGER;
    QueryParameters[18].info.integer.qwValue = VoiceCount;

    // Calculate maximum space required to hold results
    DWORD dwResultsLen = XOnlineMatchSearchResultsLen( MAX_COMMON_MATCH_RESULTS, sizeof( CommonMatchAttributeSpec ) / sizeof( CommonMatchAttributeSpec[0] ), CommonMatchAttributeSpec );

#ifdef ENABLE_OSI_XBOX_LOG
	OSIXbox::Log( "CMatchSimSession::Query - Parameters are:\n" );
	for( int i = 0; i < 19; i++ )
	{
		XONLINE_ATTRIBUTE& attr = QueryParameters[i];
		OSIXbox::Log( "                        - [%u] 0x%X = 0x%I64X\n", i, attr.dwAttributeID, attr.info.integer.qwValue );
	}
#endif

    HRESULT hr = XOnlineMatchSearch( SEARCH_PROC_ID, MAX_COMMON_MATCH_RESULTS,
                                    19, QueryParameters, dwResultsLen, NULL, &m_hSearchTask );
    if( SUCCEEDED( hr ) )
        m_State = STATE_RUNNING;
#ifdef ENABLE_OSI_XBOX_LOG
	else
		OSIXbox::Log( "CMatchSimSession::Query - XOnlineMatchSearch failed with 0x%X\n", hr );
#endif 

    return hr;

}



//-----------------------------------------------------------------------------
// Name: Probe
// Desc: Initiate bandwidth probing
// NOTE: Resulting bandwidth values are only rough estimates, and are not always
// accurate indicators of true bandwidth conditions. They are only useful in
// comparing relative bandwidths of various consoles, and should not be diplayed
// or used to set player maximums or other similar game restrictions.
//-----------------------------------------------------------------------------
HRESULT CCommonMatchQuery::Probe()
{
#ifdef ENABLE_OSI_XBOX_LOG
	OSIXbox::Log( "CMatchSimSession::Probe\n" );
#endif

	assert( m_State == STATE_DONE );
    if (m_State != STATE_DONE)
       return E_UNEXPECTED;

    // Clean up any earlier probes
    if( m_pXnQos )
    {
        XNetQosRelease( m_pXnQos );
        m_pXnQos = NULL;
    }

    DWORD dwNumSessions = Results.Size();
    if( dwNumSessions )
    {
        for( DWORD i = 0; i < dwNumSessions; ++i )
        {
            Results[i].pQosInfo = NULL;
            m_rgpXnAddr[i] = &Results[i].HostAddress;
            m_rgpXnKid[i]  = &Results[i].SessionID;
            m_rgpXnKey[i]  = &Results[i].KeyExchangeKey;
        }
        INT iQos = XNetQosLookup( dwNumSessions, m_rgpXnAddr,
            m_rgpXnKid, m_rgpXnKey, 0, NULL, NULL,
            NUM_QOS_PROBES, QOS_BITS_PER_SEC, 0, NULL,
            &m_pXnQos );
        assert( iQos == 0 );

#ifdef ENABLE_OSI_XBOX_LOG
		if( iQos == 0 )
			OSIXbox::Log( "CMatchSimSession::Probe - XNetQosLookup succeeded\n" );
		else
			OSIXbox::Log( "CMatchSimSession::Probe - XNetQosLookup failed with 0x%X\n", iQos );
#endif

        if( iQos == 0 )
            m_State = STATE_PROBING_BANDWIDTH;
        else
            return E_FAIL;
    }

    return S_OK;

}



//-----------------------------------------------------------------------------
// Name: Process
// Desc: Continue servicing the query task
//-----------------------------------------------------------------------------
HRESULT CCommonMatchQuery::Process()
{

    if( m_State == STATE_IDLE )
        return S_OK;

    if( m_State == STATE_DONE )
        return m_hrQuery;

    if( m_State == STATE_PROBING_BANDWIDTH )
    {
        // Update any completed Qos info for sessions
        // as they finish
        for( DWORD iResult = 0; iResult < Results.Size(); ++iResult )
        {
            if( !Results[iResult].pQosInfo && 
               ( m_pXnQos->axnqosinfo[iResult].bFlags & XNET_XNQOSINFO_COMPLETE ) )
            {
#ifdef ENABLE_OSI_XBOX_LOG
				OSIXbox::Log( "CMatchSimSession::Process - Probe for 0x%I64X completed\n", *((ULONGLONG*)(Results[iResult].SessionID.ab)) );
				OSIXbox::Log( "                          - PublicFilled = %u PublicOpen = %u PrivateFilled = %u PrivateOpen = %u\n",
					Results[iResult].PublicFilled, Results[iResult].PublicOpen, Results[iResult].PrivateFilled, Results[iResult].PrivateOpen );
				OSIXbox::Log( "                          - ConsoleRegion = %I64u\n", Results[iResult].ConsoleRegion );
				OSIXbox::Log( "                          - GameVersion = %I64u\n", Results[iResult].GameVersion );
				OSIXbox::Log( "                          - AdditionalContent = %I64u\n", Results[iResult].AdditionalContent );
				OSIXbox::Log( "                          - LevelIndex = %I64u\n", Results[iResult].LevelIndex );
				OSIXbox::Log( "                          - GameType = %I64u\n", Results[iResult].GameType );
				OSIXbox::Log( "                          - TimeLimit = %I64u\n", Results[iResult].TimeLimit );
				OSIXbox::Log( "                          - ScoreLimit = %I64u\n", Results[iResult].ScoreLimit );
				OSIXbox::Log( "                          - TagLimit = %I64u\n", Results[iResult].TagLimit );
				OSIXbox::Log( "                          - BestOfSeries = %I64u\n", Results[iResult].BestOfSeries );
				OSIXbox::Log( "                          - PlayersRespawn = %I64u\n", Results[iResult].PlayersRespawn );
				OSIXbox::Log( "                          - FriendlyFire = %I64u\n", Results[iResult].FriendlyFire );
				OSIXbox::Log( "                          - Apprehension = %I64u\n", Results[iResult].Apprehension );
				OSIXbox::Log( "                          - SelfDamage = %I64u\n", Results[iResult].SelfDamage );
				OSIXbox::Log( "                          - AlternatingTeams = %I64u\n", Results[iResult].AlternatingTeams );
				OSIXbox::Log( "                          - MaxPlayers = %I64u\n", Results[iResult].MaxPlayers );
				OSIXbox::Log( "                          - HostTitle = %S\n",   Results[iResult].HostTitle );
				OSIXbox::Log( "                          - IsRanked = %I64u\n", Results[iResult].IsRanked );
				OSIXbox::Log( "                          - TotalConnectedPlayers = %I64u\n", Results[iResult].TotalConnectedPlayers );
				OSIXbox::Log( "                          - LoadOutLimit = %I64u\n", Results[iResult].LoadOutLimit );
				OSIXbox::Log( "                          - VoiceCount = %I64u\n", Results[iResult].VoiceCount );
				
				XNQOSINFO* qos = &m_pXnQos->axnqosinfo[iResult];
				if( !qos )
				{
					OSIXbox::Log( "                          - NO QOS DATA\n" );
				}
				else
				{
					OSIXbox::Log( "CMatchSimSession::Process - QOS data follows:\n" );
					OSIXbox::Log( "                          - bFlags = %x\n", (unsigned)qos->bFlags );
					OSIXbox::Log( "                          - bReserved = %x\n", (unsigned)qos->bReserved );
					OSIXbox::Log( "                          - cbData = %u\n", (unsigned)qos->cbData );
					OSIXbox::Log( "                          - cProbesRecv = %u\n", (unsigned)qos->cProbesRecv );
					OSIXbox::Log( "                          - cProbesXmit = %u\n", (unsigned)qos->cProbesXmit );
					OSIXbox::Log( "                          - dwDnBitsPerSec = %u\n", qos->dwDnBitsPerSec );
					OSIXbox::Log( "                          - dwUpBitsPerSec = %u\n", qos->dwUpBitsPerSec );
					OSIXbox::Log( "                          - pbData = %p\n", qos->pbData );
					OSIXbox::Log( "                          - wRttMedInMsecs = %u\n", (unsigned)qos->wRttMedInMsecs );
					OSIXbox::Log( "                          - wRttMinInMsecs = %u\n", (unsigned)qos->wRttMinInMsecs );
				}
#endif

                Results[iResult].pQosInfo = &m_pXnQos->axnqosinfo[iResult];
            }
        }

        // Check if all probing is complete
        if( m_pXnQos->cxnqosPending == 0 )
        {
#ifdef ENABLE_OSI_XBOX_LOG
			OSIXbox::Log( "CMatchSimSession::Process - All probes completed\n" );
#endif

            m_State = STATE_DONE;
            return m_hrQuery;
        }

        return XONLINETASK_S_RUNNING;
    }

    if (m_State == STATE_PROBING_CONNECTIVITY ) 
    {
        // Check if probing is complete
        if( m_pXnQos->cxnqosPending == 0 )
        {
            // Copy QoS info into individual result objects, removing
            // any entries which are not reachable
            DWORD dwNumQosResults = Results.Size();
            DWORD iResult = 0;
            for( DWORD iQos = 0; iQos < dwNumQosResults; ++iQos )
            {
                if( ( m_pXnQos->axnqosinfo[iQos].bFlags & XNET_XNQOSINFO_TARGET_CONTACTED ) &&
                    !( m_pXnQos->axnqosinfo[iQos].bFlags & XNET_XNQOSINFO_TARGET_DISABLED ) )
                {
#ifdef ENABLE_OSI_XBOX_LOG
					OSIXbox::Log( "CMatchSimSession::Process - Connectivity probe succeeded for session 0x%I64X\n", *((ULONGLONG*)(Results[iResult].SessionID.ab)) );
#endif

					Results[iResult].pQosInfo = &m_pXnQos->axnqosinfo[iQos];
                    iResult++;
                }
                else
                {
#ifdef ENABLE_OSI_XBOX_LOG
					OSIXbox::Log( "CMatchSimSession::Process - Connectivity probe FAILED for session 0x%I64X\n", *((ULONGLONG*)(Results[iResult].SessionID.ab)) );
#endif

#ifdef _DEBUG
                    Print( L"Removing Matching Session %lu (host not reachable or disabled)\n", iResult );
#endif
                    // Target not contacted, or is disabled, so remove result
                    Results.Remove( iResult );
                }
            }
            m_State = STATE_DONE;
            return m_hrQuery;
        }

        return XONLINETASK_S_RUNNING;
    }

    HRESULT hr = XOnlineTaskContinue( m_hSearchTask );
    if( hr != XONLINETASK_S_RUNNING )
    {
        m_hrQuery = hr;
        m_State = STATE_DONE;
        if( SUCCEEDED( hr ) )
        {
#ifdef ENABLE_OSI_XBOX_LOG
			OSIXbox::Log( "CMatchSimSession::Process - XOnlineTaskContinue succeeded\n" );
#endif

            // Fetch results
            XONLINE_MATCH_SEARCHRESULT** ppSearchResults;
            DWORD dwNumSessions;
            hr= XOnlineMatchSearchGetResults( m_hSearchTask,
                &ppSearchResults, &dwNumSessions );
            if( SUCCEEDED( hr ) )
            {
#ifdef ENABLE_OSI_XBOX_LOG
				OSIXbox::Log( "CMatchSimSession::Process - XOnlineMatchSearchGetResults returned %u results:\n", dwNumSessions );
#endif

                Results.SetSize( dwNumSessions );
                for( DWORD i=0; i < dwNumSessions; ++i )
                {
                    XONLINE_MATCH_SEARCHRESULT* pxms = ppSearchResults[i];

                    Results.v[i].SessionID = pxms->SessionID;
                    Results.v[i].KeyExchangeKey = pxms->KeyExchangeKey;
                    Results.v[i].HostAddress = pxms->HostAddress;
                    Results.v[i].PublicOpen = pxms->dwPublicOpen;
                    Results.v[i].PrivateOpen = pxms->dwPrivateOpen;
                    Results.v[i].PublicFilled = pxms->dwPublicFilled;
                    Results.v[i].PrivateFilled = pxms->dwPrivateFilled;
                    hr = XOnlineMatchSearchParse( ppSearchResults[i],
                        sizeof( CommonMatchAttributeSpec ) / sizeof( CommonMatchAttributeSpec[0] ),
                        CommonMatchAttributeSpec, &Results.v[i] );
                    assert(SUCCEEDED(hr));
                   // Save data for Qos probing
                    Results.v[i].pQosInfo = NULL;
                    m_rgpXnAddr[i] = &Results.v[i].HostAddress;
                    m_rgpXnKid[i]  = &Results.v[i].SessionID;
                    m_rgpXnKey[i]  = &Results.v[i].KeyExchangeKey;

#ifdef ENABLE_OSI_XBOX_LOG
					OSIXbox::Log( 
						"                          - Session 0x%I64X with PublicFilled = %u, PublicOpen = %u, PrivateFilled = %u, PrivateOpen = %u\n",
						*((ULONGLONG*)(pxms->SessionID.ab)),
						pxms->dwPublicFilled, pxms->dwPublicOpen, pxms->dwPrivateFilled, pxms->dwPrivateOpen );

					OSIXbox::Log( 
						"                          - XNADDR::ina = %i.%i.%i.%i\n",
						pxms->HostAddress.ina.S_un.S_un_b.s_b1, pxms->HostAddress.ina.S_un.S_un_b.s_b2, pxms->HostAddress.ina.S_un.S_un_b.s_b3, pxms->HostAddress.ina.S_un.S_un_b.s_b4 );

					OSIXbox::Log( 
						"                          - XNADDR::inaOnline = %i.%i.%i.%i\n",
						pxms->HostAddress.inaOnline.S_un.S_un_b.s_b1, pxms->HostAddress.inaOnline.S_un.S_un_b.s_b2, pxms->HostAddress.inaOnline.S_un.S_un_b.s_b3, pxms->HostAddress.inaOnline.S_un.S_un_b.s_b4 );

					OSIXbox::Log( "                          - XNADDR::wPortOnline = %u\n", (unsigned)pxms->HostAddress.wPortOnline );

					OSIXbox::Log( "                          - XNADDR::abEnet = 0x" );
					{ for( int i = 0; i < 6; i++ ) {	OSIXbox::LogNoStamp( "%02X", (int)pxms->HostAddress.abEnet[i] ); } }
					OSIXbox::LogNoStamp( "\n" );
					OSIXbox::Log( "                          - XNADDR::abOnline = 0x" );
					{ for( int i = 0; i < 20; i++ ) {	OSIXbox::LogNoStamp( "%02X", (int)pxms->HostAddress.abOnline[i] ); } }
					OSIXbox::LogNoStamp( "\n" );

					IN_ADDR ina;
					XNetXnAddrToInAddr( &pxms->HostAddress, &pxms->SessionID, &ina );
					OSIXbox::Log( 
						"                          - XNetXnAddrToInAddr = %i.%i.%i.%i\n",
						ina.S_un.S_un_b.s_b1, ina.S_un.S_un_b.s_b2, ina.S_un.S_un_b.s_b3, ina.S_un.S_un_b.s_b4 );
#endif
				}
				
                if( dwNumSessions )
                {
                    INT iQos = XNetQosLookup( dwNumSessions, m_rgpXnAddr,
                        m_rgpXnKid, m_rgpXnKey, 0, NULL, NULL,
                        0, QOS_BITS_PER_SEC, 0, NULL,
                        &m_pXnQos );
                    assert( iQos == 0 );
                    if( iQos == 0 )
                        m_State = STATE_PROBING_CONNECTIVITY;

#ifdef ENABLE_OSI_XBOX_LOG
					if( iQos == 0 )
						OSIXbox::Log( "CMatchSimSession::Process - XNetQosLookup succeeded\n" );
					else
						OSIXbox::Log( "CMatchSimSession::Process - XNetQosLookup failed with 0x%X\n", iQos );
#endif
				}
            }
#ifdef ENABLE_OSI_XBOX_LOG
			else
			{
				OSIXbox::Log( "CMatchSimSession::Process - XOnlineMatchSearchGetResults failed with 0x%X\n", hr );
			}
#endif
		}
#ifdef ENABLE_OSI_XBOX_LOG
		else
		{
			OSIXbox::Log( "CMatchSimSession::Process - XOnlineTaskContinue failed with 0x%X\n", hr );
		}
#endif

        XOnlineTaskClose( m_hSearchTask );
        m_hSearchTask = NULL;
    }

    return hr;
}



// Attribute layout for SpecificMatch return attributes.
// This must match the order of both the query return attributes
// and the fields specified in the CSpecificMatchResult class.
static const
XONLINE_ATTRIBUTE_SPEC SpecificMatchAttributeSpec[]=
{
    { X_ATTRIBUTE_DATATYPE_INTEGER, sizeof( ULONGLONG ) }, // ConsoleRegion
    { X_ATTRIBUTE_DATATYPE_INTEGER, sizeof( ULONGLONG ) }, // GameVersion
    { X_ATTRIBUTE_DATATYPE_INTEGER, sizeof( ULONGLONG ) }, // AdditionalContent
    { X_ATTRIBUTE_DATATYPE_INTEGER, sizeof( ULONGLONG ) }, // LevelIndex
    { X_ATTRIBUTE_DATATYPE_INTEGER, sizeof( ULONGLONG ) }, // GameType
    { X_ATTRIBUTE_DATATYPE_INTEGER, sizeof( ULONGLONG ) }, // TimeLimit
    { X_ATTRIBUTE_DATATYPE_INTEGER, sizeof( ULONGLONG ) }, // ScoreLimit
    { X_ATTRIBUTE_DATATYPE_INTEGER, sizeof( ULONGLONG ) }, // TagLimit
    { X_ATTRIBUTE_DATATYPE_INTEGER, sizeof( ULONGLONG ) }, // BestOfSeries
    { X_ATTRIBUTE_DATATYPE_INTEGER, sizeof( ULONGLONG ) }, // PlayersRespawn
    { X_ATTRIBUTE_DATATYPE_INTEGER, sizeof( ULONGLONG ) }, // FriendlyFire
    { X_ATTRIBUTE_DATATYPE_INTEGER, sizeof( ULONGLONG ) }, // Apprehension
    { X_ATTRIBUTE_DATATYPE_INTEGER, sizeof( ULONGLONG ) }, // SelfDamage
    { X_ATTRIBUTE_DATATYPE_INTEGER, sizeof( ULONGLONG ) }, // AlternatingTeams
    { X_ATTRIBUTE_DATATYPE_INTEGER, sizeof( ULONGLONG ) }, // MaxPlayers
    { X_ATTRIBUTE_DATATYPE_STRING, ( XATTRIB_HOST_TITLE_MAX_LEN + 1 ) * sizeof( WCHAR ) }, // HostTitle
    { X_ATTRIBUTE_DATATYPE_INTEGER, sizeof( ULONGLONG ) }, // IsRanked
    { X_ATTRIBUTE_DATATYPE_INTEGER, sizeof( ULONGLONG ) }, // TotalConnectedPlayers
    { X_ATTRIBUTE_DATATYPE_INTEGER, sizeof( ULONGLONG ) }, // LoadOutLimit
    { X_ATTRIBUTE_DATATYPE_INTEGER, sizeof( ULONGLONG ) }, // VoiceCount
};




//-----------------------------------------------------------------------------
// Name: CSpecificMatchQuery
// Desc: Constructor
//-----------------------------------------------------------------------------
CSpecificMatchQuery::CSpecificMatchQuery()
{
    m_State = STATE_IDLE;
    m_hrQuery = S_FALSE;
    m_hSearchTask = NULL;
}



//-----------------------------------------------------------------------------
// Name: CSpecificMatchQuery()
// Desc: Destructor
//-----------------------------------------------------------------------------
CSpecificMatchQuery::~CSpecificMatchQuery()
{
    Cancel();
}



//-----------------------------------------------------------------------------
// Name: Cancel
// Desc: Cancel a SpecificMatch query
//-----------------------------------------------------------------------------
void CSpecificMatchQuery::Cancel()
{
    switch( m_State )
    {
    case STATE_IDLE:
       break;
    case STATE_RUNNING:
    case STATE_DONE:
        if( m_hSearchTask )
        {
            XOnlineTaskClose( m_hSearchTask );
            m_hSearchTask = NULL;
        }
        Clear();
        m_State = STATE_IDLE;
        m_hrQuery = S_FALSE;
        break;
    default:
        assert(0);
    }
}



//-----------------------------------------------------------------------------
// Name: Clear
// Desc: Release resources
//-----------------------------------------------------------------------------
void CSpecificMatchQuery::Clear()
{
    Results.Clear();
}



//-----------------------------------------------------------------------------
// Name: Query
// Desc: Execute the SpecificMatch query (id 0x3)
//-----------------------------------------------------------------------------
HRESULT CSpecificMatchQuery::Query(
                  ULONGLONG SessionID
                  )
{
    const DWORD SEARCH_PROC_ID = 0x3;

    if( m_State == STATE_DONE ) // Clear existing results
    {
        Clear();
        m_State = STATE_IDLE;
    }
    assert( m_State == STATE_IDLE );
    if (m_State != STATE_IDLE)
        return E_UNEXPECTED;

    XONLINE_ATTRIBUTE QueryParameters[1] = { 0 };
    QueryParameters[0].dwAttributeID = X_ATTRIBUTE_DATATYPE_INTEGER;
    QueryParameters[0].info.integer.qwValue = SessionID;

    // Calculate maximum space required to hold results
    DWORD dwResultsLen = XOnlineMatchSearchResultsLen( MAX_SPECIFIC_MATCH_RESULTS, sizeof( SpecificMatchAttributeSpec ) / sizeof( SpecificMatchAttributeSpec[0] ), SpecificMatchAttributeSpec );

    HRESULT hr = XOnlineMatchSearch( SEARCH_PROC_ID, MAX_SPECIFIC_MATCH_RESULTS,
                                    1, QueryParameters, dwResultsLen, NULL, &m_hSearchTask );
    if( SUCCEEDED( hr ) )
        m_State = STATE_RUNNING;

    return hr;

}



//-----------------------------------------------------------------------------
// Name: Process
// Desc: Continue servicing the query task
//-----------------------------------------------------------------------------
HRESULT CSpecificMatchQuery::Process()
{

    if( m_State == STATE_IDLE )
        return S_OK;

    if( m_State == STATE_DONE )
        return m_hrQuery;

    HRESULT hr = XOnlineTaskContinue( m_hSearchTask );
    if( hr != XONLINETASK_S_RUNNING )
    {
        m_hrQuery = hr;
        m_State = STATE_DONE;
        if( SUCCEEDED( hr ) )
        {
            // Fetch results
            XONLINE_MATCH_SEARCHRESULT** ppSearchResults;
            DWORD dwNumSessions;
            hr= XOnlineMatchSearchGetResults( m_hSearchTask,
                &ppSearchResults, &dwNumSessions );
            if( SUCCEEDED( hr ) )
            {
                Results.SetSize( dwNumSessions );
                for( DWORD i=0; i < dwNumSessions; ++i )
                {
                    XONLINE_MATCH_SEARCHRESULT* pxms = ppSearchResults[i];

                    Results.v[i].SessionID = pxms->SessionID;
                    Results.v[i].KeyExchangeKey = pxms->KeyExchangeKey;
                    Results.v[i].HostAddress = pxms->HostAddress;
                    Results.v[i].PublicOpen = pxms->dwPublicOpen;
                    Results.v[i].PrivateOpen = pxms->dwPrivateOpen;
                    Results.v[i].PublicFilled = pxms->dwPublicFilled;
                    Results.v[i].PrivateFilled = pxms->dwPrivateFilled;
                    hr = XOnlineMatchSearchParse( ppSearchResults[i],
                        sizeof( SpecificMatchAttributeSpec ) / sizeof( SpecificMatchAttributeSpec[0] ),
                        SpecificMatchAttributeSpec, &Results.v[i] );
                    assert(SUCCEEDED(hr));
                }
            }
        }

        XOnlineTaskClose( m_hSearchTask );
        m_hSearchTask = NULL;
    }

    return hr;
}



