You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2557 lines
75 KiB
2557 lines
75 KiB
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
|
//
|
|
// Purpose: Interface to Xbox 360 system functions. Helps deal with the async system and Live
|
|
// functions by either providing a handle for the caller to check results or handling
|
|
// automatic cleanup of the async data when the caller doesn't care about the results.
|
|
//
|
|
//=====================================================================================//
|
|
|
|
#include "host.h"
|
|
#include "tier3/tier3.h"
|
|
#include "vgui/ILocalize.h"
|
|
#include "ixboxsystem.h"
|
|
|
|
#ifdef IS_WINDOWS_PC
|
|
#include "winerror.h"
|
|
#endif
|
|
|
|
#ifdef _X360
|
|
#include <xparty.h>
|
|
#endif
|
|
|
|
#include "vstdlib/random.h"
|
|
|
|
// memdbgon must be the last include file in a .cpp file!!!
|
|
#include "tier0/memdbgon.h"
|
|
|
|
|
|
static wchar_t g_szModSaveContainerDisplayName[XCONTENT_MAX_DISPLAYNAME_LENGTH] = L"";
|
|
static char g_szModSaveContainerName[XCONTENT_MAX_FILENAME_LENGTH] = "";
|
|
|
|
#define XBX_USER_SETTINGS_CONTAINER_ENABLED 1
|
|
|
|
#if !defined( CSTRIKE_TRIAL_MODE )
|
|
#if defined( _CERT ) && defined( _X360 )
|
|
# define CSTRIKE_TRIAL_MODE 1
|
|
#else
|
|
# define CSTRIKE_TRIAL_MODE 0
|
|
#endif
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Implementation of IXboxSystem interface
|
|
//-----------------------------------------------------------------------------
|
|
class CXboxSystem : public IXboxSystem
|
|
{
|
|
public:
|
|
CXboxSystem( void );
|
|
|
|
virtual ~CXboxSystem( void );
|
|
|
|
virtual AsyncHandle_t CreateAsyncHandle( void );
|
|
virtual void ReleaseAsyncHandle( AsyncHandle_t handle );
|
|
virtual int GetOverlappedResult( AsyncHandle_t handle, uint *pResultCode, bool bWait );
|
|
virtual void CancelOverlappedOperation( AsyncHandle_t handle );
|
|
|
|
// Save/Load
|
|
virtual bool GameHasSavegames( void );
|
|
virtual void GetModSaveContainerNames( const char *pchModName, const wchar_t **ppchDisplayName, const char **ppchName );
|
|
virtual uint GetContainerRemainingSpace( DWORD nDeviceID );
|
|
virtual bool DeviceCapacityAdequate( int iController, DWORD nDeviceID, const char *pModName );
|
|
virtual DWORD DiscoverUserData( DWORD nUserID, const char *pModName );
|
|
|
|
// XUI
|
|
virtual bool ShowDeviceSelector( int iController, bool bForce, uint *pStorageID, AsyncHandle_t *pHandle );
|
|
virtual void ShowSigninUI( uint nPanes, uint nFlags );
|
|
|
|
// Rich Presence and Matchmaking
|
|
virtual int UserSetContext( uint nUserIdx, XUSER_CONTEXT const &xc, bool bAsync, AsyncHandle_t *pHandle);
|
|
virtual int UserSetProperty( uint nUserIndex, XUSER_PROPERTY const &xp, bool bAsync, AsyncHandle_t *pHandle );
|
|
virtual int UserGetContext( uint nUserIdx, uint nContextID, uint &nContextValue);
|
|
virtual int UserGetPropertyInt( uint nUserIndex, uint nPropertyId, uint &nPropertyValue);
|
|
|
|
// Matchmaking
|
|
virtual int CreateSession( uint nFlags, uint nUserIdx, uint nMaxPublicSlots, uint nMaxPrivateSlots, uint64 *pNonce, void *pSessionInfo, XboxHandle_t *pSessionHandle, bool bAsync, AsyncHandle_t *pAsyncHandle );
|
|
virtual uint DeleteSession( XboxHandle_t hSession, bool bAsync, AsyncHandle_t *pAsyncHandle = NULL );
|
|
virtual uint SessionSearch( uint nProcedureIndex, uint nUserIndex, uint nNumResults, uint nNumUsers, uint nNumProperties, uint nNumContexts, XUSER_PROPERTY *pSearchProperties, XUSER_CONTEXT *pSearchContexts, uint *pcbResultsBuffer, XSESSION_SEARCHRESULT_HEADER *pSearchResults, bool bAsync, AsyncHandle_t *pAsyncHandle );
|
|
virtual uint SessionStart( XboxHandle_t hSession, uint nFlags, bool bAsync, AsyncHandle_t *pAsyncHandle );
|
|
virtual uint SessionEnd( XboxHandle_t hSession, bool bAsync, AsyncHandle_t *pAsyncHandle );
|
|
virtual int SessionJoinLocal( XboxHandle_t hSession, uint nUserCount, const uint *pUserIndexes, const bool *pPrivateSlots, bool bAsync, AsyncHandle_t *pAsyncHandle );
|
|
virtual int SessionJoinRemote( XboxHandle_t hSession, uint nUserCount, const XUID *pXuids, const bool *pPrivateSlot, bool bAsync, AsyncHandle_t *pAsyncHandle );
|
|
virtual int SessionLeaveLocal( XboxHandle_t hSession, uint nUserCount, const uint *pUserIndexes, bool bAsync, AsyncHandle_t *pAsyncHandle );
|
|
virtual int SessionLeaveRemote( XboxHandle_t hSession, uint nUserCount, const XUID *pXuids, bool bAsync, AsyncHandle_t *pAsyncHandle );
|
|
virtual int SessionMigrate( XboxHandle_t hSession, uint nUserIndex, void *pSessionInfo, bool bAsync, AsyncHandle_t *pAsyncHandle );
|
|
virtual int SessionArbitrationRegister( XboxHandle_t hSession, uint nFlags, uint64 nonce, uint *pBytes, void *pBuffer, bool bAsync, AsyncHandle_t *pAsyncHandle );
|
|
|
|
// Friends
|
|
virtual int EnumerateFriends( uint userIndex, void **pBuffer, bool bAsync, AsyncHandle_t *pAsyncHandle = NULL );
|
|
|
|
// Stats
|
|
virtual int WriteStats( XboxHandle_t hSession, XUID xuid, uint nViews, void* pViews, bool bAsync, AsyncHandle_t *pAsyncHandle );
|
|
virtual int FlushStats( XboxHandle_t hSession, bool bAsync, AsyncHandle_t *pAsyncHandle );
|
|
virtual int EnumerateStatsByRank( uint nStartingRank, uint nNumRows, uint nNumSpecs, void *pSpecs, void **ppResults, bool bAsync, AsyncHandle_t *pAsyncHandle );
|
|
virtual int EnumerateStatsByXuid( XUID nUserId, uint nNumRows, uint nNumSpecs, void *pSpecs, void **ppResults, bool bAsync, AsyncHandle_t *pAsyncHandle );
|
|
|
|
// Achievements
|
|
virtual int EnumerateAchievements( uint nUserIdx, uint64 xuid, uint nStartingIdx, uint nCount, void *pBuffer, uint nBufferBytes, bool bAsync, AsyncHandle_t *pAsyncHandle );
|
|
virtual int AwardAchievement( uint nUserIdx, uint nAchievementId, AsyncHandle_t *ppOverlappedResult );
|
|
virtual int AwardAvatarAsset( uint nUserIdx, uint nAwardId, AsyncHandle_t *ppOverlappedResult );
|
|
|
|
// Arcade titles
|
|
virtual void ShowUnlockFullGameUI( void );
|
|
virtual bool UpdateArcadeTitleUnlockStatus( void );
|
|
virtual bool IsArcadeTitleUnlocked( void );
|
|
virtual float GetArcadeRemainingTrialTime( int nSlot = 0 );
|
|
|
|
virtual void FinishContainerWrites( int iController );
|
|
virtual uint GetContainerOpenResult( int iController );
|
|
virtual uint OpenContainers( int iController );
|
|
virtual void CloseContainers( int iController );
|
|
|
|
virtual void FinishAllContainerWrites( void );
|
|
virtual void CloseAllContainers( void );
|
|
|
|
//
|
|
// Overlapped
|
|
//
|
|
virtual int Io_HasOverlappedIoCompleted( XOVERLAPPED *pOverlapped );
|
|
|
|
//
|
|
// XNet
|
|
//
|
|
virtual int NetRandom( byte *pb, unsigned numBytes );
|
|
virtual DWORD NetGetTitleXnAddr( XNADDR *pxna );
|
|
virtual int NetXnAddrToMachineId( const XNADDR *pxnaddr, uint64 *pqwMachineId );
|
|
virtual int NetInAddrToXnAddr( const IN_ADDR ina, XNADDR *pxna, XNKID *pxnkid );
|
|
virtual int NetXnAddrToInAddr( const XNADDR *pxna, const XNKID *pxnkid, IN_ADDR *pina );
|
|
|
|
//
|
|
// User
|
|
//
|
|
virtual XUSER_SIGNIN_STATE UserGetSigninState( int iCtrlr );
|
|
|
|
private:
|
|
virtual uint CreateSavegameContainer( int iController, uint nCreationFlags );
|
|
virtual uint CreateUserSettingsContainer( int iController, uint nCreationFlags );
|
|
|
|
uint m_OpenContainerResult[ 4 ];
|
|
};
|
|
|
|
static CXboxSystem s_XboxSystem;
|
|
IXboxSystem *g_pXboxSystem = &s_XboxSystem;
|
|
|
|
EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CXboxSystem, IXboxSystem, XBOXSYSTEM_INTERFACE_VERSION, s_XboxSystem );
|
|
|
|
#define ASYNC_RESULT(ph) ((AsyncResult_t*)*ph);
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Holds the overlapped object and any persistent data for async system calls
|
|
//-----------------------------------------------------------------------------
|
|
typedef struct AsyncResult_s
|
|
{
|
|
XOVERLAPPED overlapped;
|
|
bool bAutoRelease;
|
|
void *pInputData;
|
|
AsyncResult_s *pNext;
|
|
} AsyncResult_t;
|
|
|
|
static AsyncResult_t * g_pAsyncResultHead = NULL;
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Remove an AsyncResult_t from the list
|
|
//-----------------------------------------------------------------------------
|
|
static void ReleaseAsyncResult( AsyncResult_t *pAsyncResult )
|
|
{
|
|
if ( pAsyncResult == g_pAsyncResultHead )
|
|
{
|
|
g_pAsyncResultHead = pAsyncResult->pNext;
|
|
free( pAsyncResult->pInputData );
|
|
delete pAsyncResult;
|
|
return;
|
|
}
|
|
|
|
AsyncResult_t *pNode = g_pAsyncResultHead;
|
|
while ( pNode->pNext )
|
|
{
|
|
if ( pNode->pNext == pAsyncResult )
|
|
{
|
|
pNode->pNext = pAsyncResult->pNext;
|
|
free( pAsyncResult->pInputData );
|
|
delete pAsyncResult;
|
|
return;
|
|
}
|
|
pNode = pNode->pNext;
|
|
}
|
|
Warning( "AsyncResult_t not found in ReleaseAsyncResult.\n" );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Remove an AsyncResult_t from the list
|
|
//-----------------------------------------------------------------------------
|
|
static void ReleaseAsyncResult( XOVERLAPPED *pOverlapped )
|
|
{
|
|
AsyncResult_t *pResult = g_pAsyncResultHead;
|
|
while ( pResult )
|
|
{
|
|
if ( &pResult->overlapped == pOverlapped )
|
|
{
|
|
ReleaseAsyncResult( pResult );
|
|
return;
|
|
}
|
|
}
|
|
Warning( "XOVERLAPPED couldn't be found in ReleaseAsyncResult.\n" );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Release async results that were marked for auto-release.
|
|
//-----------------------------------------------------------------------------
|
|
static void CleanupFinishedAsyncResults()
|
|
{
|
|
AsyncResult_t *pResult = g_pAsyncResultHead;
|
|
AsyncResult_t *pNext;
|
|
while( pResult )
|
|
{
|
|
pNext = pResult->pNext;
|
|
if ( pResult->bAutoRelease )
|
|
{
|
|
#ifdef _X360
|
|
bool bCompleted = XHasOverlappedIoCompleted( &pResult->overlapped );
|
|
#else
|
|
bool bCompleted = true;
|
|
#endif
|
|
if ( bCompleted )
|
|
{
|
|
ReleaseAsyncResult( pResult );
|
|
}
|
|
}
|
|
pResult = pNext;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Add a new AsyncResult_t object to the list
|
|
//-----------------------------------------------------------------------------
|
|
static AsyncResult_t *CreateAsyncResult( bool bAutoRelease )
|
|
{
|
|
// Take this opportunity to clean up finished operations
|
|
CleanupFinishedAsyncResults();
|
|
|
|
AsyncResult_t *pAsyncResult = new AsyncResult_t;
|
|
memset( pAsyncResult, 0, sizeof( AsyncResult_t ) );
|
|
|
|
pAsyncResult->pNext = g_pAsyncResultHead;
|
|
g_pAsyncResultHead = pAsyncResult;
|
|
|
|
if ( bAutoRelease )
|
|
{
|
|
pAsyncResult->bAutoRelease = true;
|
|
}
|
|
|
|
return pAsyncResult;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Return an AsyncResult_t object to the pool
|
|
//-----------------------------------------------------------------------------
|
|
static void InitializeAsyncHandle( AsyncHandle_t *pHandle )
|
|
{
|
|
XOVERLAPPED *pOverlapped = &((AsyncResult_t *)*pHandle)->overlapped;
|
|
memset( pOverlapped, 0, sizeof( XOVERLAPPED ) );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Initialize or create and async handle
|
|
//-----------------------------------------------------------------------------
|
|
static AsyncResult_t *InitializeAsyncResult( AsyncHandle_t **ppAsyncHandle )
|
|
{
|
|
AsyncResult_t *pResult = NULL;
|
|
if ( *ppAsyncHandle )
|
|
{
|
|
InitializeAsyncHandle( *ppAsyncHandle );
|
|
pResult = ASYNC_RESULT( *ppAsyncHandle );
|
|
}
|
|
else
|
|
{
|
|
// No handle provided, create one
|
|
pResult = CreateAsyncResult( true );
|
|
}
|
|
return pResult;
|
|
}
|
|
|
|
CXboxSystem::CXboxSystem( void )
|
|
{
|
|
memset( m_OpenContainerResult, 0, sizeof( m_OpenContainerResult ) );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Force overlapped operations to finish and clean up
|
|
//-----------------------------------------------------------------------------
|
|
CXboxSystem::~CXboxSystem()
|
|
{
|
|
// Force async operations to finish.
|
|
AsyncResult_t *pResult = g_pAsyncResultHead;
|
|
while ( pResult )
|
|
{
|
|
AsyncResult_t *pNext = pResult->pNext;
|
|
GetOverlappedResult( (AsyncHandle_t)pResult, NULL, true );
|
|
pResult = pNext;
|
|
}
|
|
|
|
// Release any remaining handles - should have been released by the client that created them.
|
|
int ct = 0;
|
|
while ( g_pAsyncResultHead )
|
|
{
|
|
ReleaseAsyncResult( g_pAsyncResultHead );
|
|
++ct;
|
|
}
|
|
|
|
if ( ct )
|
|
{
|
|
Warning( "Released %d async handles\n", ct );
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Check on the result of an overlapped operation
|
|
//-----------------------------------------------------------------------------
|
|
int CXboxSystem::GetOverlappedResult( AsyncHandle_t handle, uint *pResultCode, bool bWait )
|
|
{
|
|
#ifdef _X360
|
|
if ( !handle )
|
|
return ERROR_INVALID_HANDLE;
|
|
|
|
return XGetOverlappedResult( &((AsyncResult_t*)handle)->overlapped, (DWORD*)pResultCode, bWait );
|
|
#else
|
|
if ( pResultCode )
|
|
*pResultCode = ERROR_SUCCESS;
|
|
return ERROR_SUCCESS;
|
|
#endif
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Cancel an overlapped operation
|
|
//-----------------------------------------------------------------------------
|
|
void CXboxSystem::CancelOverlappedOperation( AsyncHandle_t handle )
|
|
{
|
|
#ifdef _X360
|
|
XCancelOverlapped( &((AsyncResult_t*)handle)->overlapped );
|
|
#else
|
|
(void) 0;
|
|
#endif
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Create a new AsyncHandle_t
|
|
//-----------------------------------------------------------------------------
|
|
AsyncHandle_t CXboxSystem::CreateAsyncHandle( void )
|
|
{
|
|
#ifdef _X360
|
|
return (AsyncHandle_t)CreateAsyncResult( false );
|
|
#else
|
|
return NULL;
|
|
#endif
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Delete an AsyncHandle_t
|
|
//-----------------------------------------------------------------------------
|
|
void CXboxSystem::ReleaseAsyncHandle( AsyncHandle_t handle )
|
|
{
|
|
#ifdef _X360
|
|
ReleaseAsyncResult( (AsyncResult_t*)handle );
|
|
#else
|
|
(void) 0;
|
|
#endif
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Close the open containers
|
|
//-----------------------------------------------------------------------------
|
|
void CXboxSystem::CloseContainers( int iController )
|
|
{
|
|
#ifdef _X360
|
|
char szRootName[XCONTENT_MAX_FILENAME_LENGTH];
|
|
DWORD ret = 0;
|
|
|
|
XBX_MakeStorageContainerRoot( iController, XBX_USER_SAVES_CONTAINER_DRIVE, szRootName, XCONTENT_MAX_FILENAME_LENGTH );
|
|
ret = XContentClose( szRootName, NULL );
|
|
DevMsg( "XContentClose( %s ) = 0x%08X\n", szRootName, ret );
|
|
|
|
#if XBX_USER_SETTINGS_CONTAINER_ENABLED
|
|
XBX_MakeStorageContainerRoot( iController, XBX_USER_SETTINGS_CONTAINER_DRIVE, szRootName, XCONTENT_MAX_FILENAME_LENGTH );
|
|
ret = XContentClose( szRootName, NULL );
|
|
DevMsg( "XContentClose( %s ) = 0x%08X\n", szRootName, ret );
|
|
#endif
|
|
#else
|
|
#endif
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Close all open containers
|
|
//-----------------------------------------------------------------------------
|
|
void CXboxSystem::CloseAllContainers( void )
|
|
{
|
|
for ( DWORD k = 0; k < XUSER_MAX_COUNT; ++ k )
|
|
CloseContainers( k );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
uint CXboxSystem::OpenContainers( int iController )
|
|
{
|
|
#ifdef _X360
|
|
if ( iController < 0 || iController >= XUSER_MAX_COUNT )
|
|
return ERROR_NO_SUCH_USER;
|
|
|
|
if ( UserGetSigninState( iController ) == eXUserSigninState_NotSignedIn )
|
|
return ERROR_NO_SUCH_PRIVILEGE;
|
|
|
|
// Close the containers (force dismount)
|
|
CloseContainers( iController );
|
|
|
|
m_OpenContainerResult[ iController ] = ERROR_SUCCESS;
|
|
|
|
// Open the save games
|
|
if ( ( m_OpenContainerResult[ iController ] =
|
|
CreateUserSettingsContainer( iController, XCONTENTFLAG_OPENALWAYS ) )
|
|
!= ERROR_SUCCESS )
|
|
return m_OpenContainerResult[ iController ];
|
|
|
|
// If we don't care about save game space
|
|
if ( !GameHasSavegames() )
|
|
return m_OpenContainerResult[ iController ];
|
|
|
|
// Open the user settings
|
|
if ( ( m_OpenContainerResult[ iController ] =
|
|
CreateSavegameContainer( iController, XCONTENTFLAG_OPENALWAYS ) )
|
|
!= ERROR_SUCCESS )
|
|
{
|
|
CloseContainers( iController );
|
|
return m_OpenContainerResult[ iController ];
|
|
}
|
|
#else
|
|
m_OpenContainerResult[ iController ] = ERROR_SUCCESS;
|
|
#endif
|
|
|
|
return m_OpenContainerResult[ iController ];
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Returns the results from the last container opening
|
|
//-----------------------------------------------------------------------------
|
|
uint CXboxSystem::GetContainerOpenResult( int iController )
|
|
{
|
|
#ifdef _X360
|
|
if ( iController < 0 || iController >= XUSER_MAX_COUNT )
|
|
return ERROR_NO_SUCH_USER;
|
|
|
|
if ( UserGetSigninState( iController ) == eXUserSigninState_NotSignedIn )
|
|
return ERROR_NO_SUCH_PRIVILEGE;
|
|
#else
|
|
m_OpenContainerResult[ iController ] = ERROR_SUCCESS;
|
|
#endif
|
|
|
|
return m_OpenContainerResult[ iController ];
|
|
}
|
|
|
|
#ifdef _X360
|
|
uint XHelper_CreateContainer( int iController, uint nCreationFlags, XCONTENT_DATA &contentData, uint64 uiBytesNeeded, char const *szContainerRoot )
|
|
{
|
|
if ( iController < 0 || iController >= XUSER_MAX_COUNT )
|
|
return ERROR_NO_SUCH_USER;
|
|
|
|
DWORD dwStorageDevice = XBX_GetStorageDeviceId( iController );
|
|
if ( !XBX_DescribeStorageDevice( dwStorageDevice ) )
|
|
return ERROR_INVALID_HANDLE;
|
|
|
|
// Don't allow any of our saves or user data to be transferred to another user
|
|
nCreationFlags |= XCONTENTFLAG_NOPROFILE_TRANSFER;
|
|
|
|
contentData.DeviceID = dwStorageDevice;
|
|
contentData.dwContentType = XCONTENTTYPE_SAVEDGAME;
|
|
|
|
SIZE_T dwFileCacheSize = 0; // Use the smallest size (default)
|
|
ULARGE_INTEGER ulSize;
|
|
ulSize.QuadPart = uiBytesNeeded;
|
|
|
|
char szRootName[XCONTENT_MAX_FILENAME_LENGTH];
|
|
XBX_MakeStorageContainerRoot( iController, szContainerRoot, szRootName, XCONTENT_MAX_FILENAME_LENGTH );
|
|
|
|
int nRet = ERROR_SUCCESS;
|
|
bool bFound = false;
|
|
if ( ( nCreationFlags & XCONTENTFLAG_OPENALWAYS ) == XCONTENTFLAG_OPENALWAYS )
|
|
{
|
|
uint nTestingFlag = ( nCreationFlags & ~XCONTENTFLAG_OPENALWAYS ) | XCONTENTFLAG_OPENEXISTING;
|
|
nRet = XContentCreateEx( iController, szRootName, &contentData, nTestingFlag, NULL, NULL, dwFileCacheSize, ulSize, NULL );
|
|
if ( nRet == ERROR_SUCCESS )
|
|
{
|
|
bFound = true;
|
|
}
|
|
}
|
|
|
|
if ( !bFound && nRet != ERROR_FILE_CORRUPT )
|
|
{
|
|
nRet = XContentCreateEx( iController, szRootName, &contentData, nCreationFlags, NULL, NULL, dwFileCacheSize, ulSize, NULL );
|
|
}
|
|
if ( nRet == ERROR_SUCCESS )
|
|
{
|
|
BOOL bUserIsCreator = false;
|
|
XContentGetCreator( iController, &contentData, &bUserIsCreator, NULL, NULL );
|
|
if( bUserIsCreator == false )
|
|
{
|
|
XContentClose( szRootName, NULL );
|
|
return ERROR_ACCESS_DENIED;
|
|
}
|
|
|
|
DevMsg( "XContentCreateEx( %s ): %s for %d\n", szRootName, contentData.szFileName, iController );
|
|
}
|
|
|
|
return nRet;
|
|
}
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Open the save game container for the current mod
|
|
//-----------------------------------------------------------------------------
|
|
uint CXboxSystem::CreateSavegameContainer( int iController, uint nCreationFlags )
|
|
{
|
|
#ifdef _X360
|
|
const wchar_t *pchContainerDisplayName;
|
|
const char *pchContainerName;
|
|
g_pXboxSystem->GetModSaveContainerNames( GetCurrentMod(), &pchContainerDisplayName, &pchContainerName );
|
|
|
|
XCONTENT_DATA contentData;
|
|
memset( &contentData, 0, sizeof( contentData ) );
|
|
Q_wcsncpy( contentData.szDisplayName, pchContainerDisplayName, sizeof ( contentData.szDisplayName ) );
|
|
Q_snprintf( contentData.szFileName, sizeof( contentData.szFileName ), pchContainerName );
|
|
|
|
return XHelper_CreateContainer( iController, nCreationFlags, contentData, XBX_PERSISTENT_BYTES_NEEDED, XBX_USER_SAVES_CONTAINER_DRIVE );
|
|
#else
|
|
return ERROR_SUCCESS;
|
|
#endif
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Open the user settings container for the current mod
|
|
//-----------------------------------------------------------------------------
|
|
uint CXboxSystem::CreateUserSettingsContainer( int iController, uint nCreationFlags )
|
|
{
|
|
#ifdef _X360
|
|
#if XBX_USER_SETTINGS_CONTAINER_ENABLED
|
|
XCONTENT_DATA contentData;
|
|
memset( &contentData, 0, sizeof( contentData ) );
|
|
Q_wcsncpy( contentData.szDisplayName, g_pVGuiLocalize->FindSafe( "#GameUI_Console_UserSettings" ), sizeof( contentData.szDisplayName ) );
|
|
Q_snprintf( contentData.szFileName, sizeof( contentData.szFileName ), "UserSettings" );
|
|
|
|
return XHelper_CreateContainer( iController, nCreationFlags, contentData, XBX_USER_SETTINGS_BYTES, XBX_USER_SETTINGS_CONTAINER_DRIVE );
|
|
#else
|
|
return ERROR_SUCCESS;
|
|
#endif
|
|
#else
|
|
return ERROR_SUCCESS;
|
|
#endif
|
|
}
|
|
|
|
#ifdef _GAMECONSOLE
|
|
ConVar host_write_last_time( "host_write_last_time", "0", FCVAR_DEVELOPMENTONLY );
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CXboxSystem::FinishContainerWrites( int iController )
|
|
{
|
|
#ifdef _X360
|
|
char szRootName[XCONTENT_MAX_FILENAME_LENGTH];
|
|
|
|
XBX_MakeStorageContainerRoot( iController, XBX_USER_SAVES_CONTAINER_DRIVE, szRootName, XCONTENT_MAX_FILENAME_LENGTH );
|
|
XContentFlush( szRootName, NULL );
|
|
|
|
#ifdef XBX_USER_SETTINGS_CONTAINER_ENABLED
|
|
XBX_MakeStorageContainerRoot( iController, XBX_USER_SETTINGS_CONTAINER_DRIVE, szRootName, XCONTENT_MAX_FILENAME_LENGTH );
|
|
XContentFlush( szRootName, NULL );
|
|
#endif
|
|
|
|
static ConVarRef host_write_last_time( "host_write_last_time" );
|
|
float flTimeSinceLastWrite = Plat_FloatTime() - host_write_last_time.GetFloat();
|
|
if ( flTimeSinceLastWrite < 3.0f )
|
|
{
|
|
DevWarning( "CXboxSystem::FinishContainerWrites when only %.2f sec elapsed after last write!\n", flTimeSinceLastWrite );
|
|
}
|
|
host_write_last_time.SetValue( ( float ) Plat_FloatTime() );
|
|
#else
|
|
#endif
|
|
}
|
|
|
|
void CXboxSystem::FinishAllContainerWrites( void )
|
|
{
|
|
for ( DWORD k = 0; k < XUSER_MAX_COUNT; ++ k )
|
|
FinishContainerWrites( k );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Determine if game has savegame containers
|
|
//-----------------------------------------------------------------------------
|
|
bool CXboxSystem::GameHasSavegames( void )
|
|
{
|
|
static bool s_bInitialized = false;
|
|
static bool s_bHasSavegames = true;
|
|
|
|
#ifdef _GAMECONSOLE
|
|
if ( XBX_GetNumGameUsers() != 1 )
|
|
return false;
|
|
if ( XBX_GetPrimaryUserIsGuest() )
|
|
return false;
|
|
#endif
|
|
|
|
if ( !s_bInitialized )
|
|
{
|
|
const char *pszMod = GetCurrentMod();
|
|
|
|
if ( !Q_stricmp( pszMod, "left4dead2" ) )
|
|
s_bHasSavegames = false;
|
|
else if ( !Q_stricmp( pszMod, "tf" ) )
|
|
s_bHasSavegames = false;
|
|
|
|
s_bInitialized = true;
|
|
}
|
|
|
|
return s_bHasSavegames;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Retrieve the names used for our save game container
|
|
// Input : *pchModName - Name of the mod we're running (tf, hl2, etc)
|
|
// **ppchDisplayName - Display name that will be presented to users by the console
|
|
// **ppchName - Filename of the container
|
|
//-----------------------------------------------------------------------------
|
|
void CXboxSystem::GetModSaveContainerNames( const char *pchModName, const wchar_t **ppchDisplayName, const char **ppchName )
|
|
{
|
|
// If the strings haven't been setup
|
|
if ( g_szModSaveContainerDisplayName[ 0 ] == '\0' )
|
|
{
|
|
char chFmtString[256] = {0};
|
|
Q_snprintf( chFmtString, sizeof( chFmtString ), "#GameUI_Console_%s_Saves", pchModName );
|
|
wchar_t const *wszLocStr = g_pVGuiLocalize->Find( chFmtString );
|
|
if ( !wszLocStr || !*wszLocStr )
|
|
wszLocStr = g_pVGuiLocalize->Find( "#GameUI_Console_SaveGames" );
|
|
if ( !wszLocStr || !*wszLocStr )
|
|
wszLocStr = L"SAVES";
|
|
|
|
Q_wcsncpy( g_szModSaveContainerDisplayName, wszLocStr, sizeof( g_szModSaveContainerDisplayName ) );
|
|
|
|
// Create a filename with the format "mod_saves"
|
|
Q_snprintf( g_szModSaveContainerName, sizeof( g_szModSaveContainerName ), "%s_saves", pchModName );
|
|
}
|
|
|
|
// Return pointers to these internally kept strings
|
|
*ppchDisplayName = g_szModSaveContainerDisplayName;
|
|
*ppchName = g_szModSaveContainerName;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Search the device and find out if we have adequate space to start a game
|
|
// Input : nStorageID - Device to check
|
|
// *pModName - Name of the mod we want to check for
|
|
//-----------------------------------------------------------------------------
|
|
bool CXboxSystem::DeviceCapacityAdequate( int iController, DWORD nStorageID, const char *pModName )
|
|
{
|
|
#ifdef _X360
|
|
// If we don't have a valid user id, we can't poll the device
|
|
if ( iController == XBX_INVALID_USER_ID )
|
|
return false;
|
|
|
|
if ( XUserGetSigninState( iController ) == eXUserSigninState_NotSignedIn )
|
|
return XBX_INVALID_STORAGE_ID;
|
|
|
|
// Must be a valid storage device to poll
|
|
if ( !XBX_DescribeStorageDevice( nStorageID ) )
|
|
return false;
|
|
|
|
// Get the actual amount on the drive
|
|
XDEVICE_DATA deviceData;
|
|
if ( XContentGetDeviceData( nStorageID, &deviceData ) != ERROR_SUCCESS )
|
|
return false;
|
|
|
|
const ULONGLONG nSaveGameSize = XContentCalculateSize( XBX_PERSISTENT_BYTES_NEEDED, 1 );
|
|
const ULONGLONG nUserSettingsSize = XContentCalculateSize( XBX_USER_SETTINGS_BYTES, 1 );
|
|
|
|
bool bHasSaves = GameHasSavegames();
|
|
ULONGLONG nTotalSpaceNeeded = ( !bHasSaves ) ? nUserSettingsSize : ( nSaveGameSize + nUserSettingsSize );
|
|
ULONGLONG nAvailableSpace = deviceData.ulDeviceFreeBytes; // Take the first device's free space to compare this against
|
|
|
|
// If they've already got enough space, early out
|
|
if ( nAvailableSpace >= nTotalSpaceNeeded )
|
|
return true;
|
|
|
|
const int nNumItemsToRetrieve = 1;
|
|
const int fContentFlags = XCONTENTFLAG_ENUM_EXCLUDECOMMON;
|
|
|
|
// Save for queries against the storage devices
|
|
const wchar_t *pchContainerDisplayName;
|
|
const char *pchContainerName;
|
|
GetModSaveContainerNames( pModName, &pchContainerDisplayName, &pchContainerName );
|
|
|
|
// Look for a user settings block for all products
|
|
DWORD nBufferSize;
|
|
HANDLE hEnumerator;
|
|
if ( XContentCreateEnumerator( iController,
|
|
nStorageID,
|
|
XCONTENTTYPE_SAVEDGAME,
|
|
fContentFlags,
|
|
nNumItemsToRetrieve,
|
|
&nBufferSize,
|
|
&hEnumerator ) == ERROR_SUCCESS )
|
|
{
|
|
// Allocate a buffer of the correct size
|
|
BYTE *pBuffer = new BYTE[nBufferSize];
|
|
if ( pBuffer == NULL )
|
|
return XBX_INVALID_STORAGE_ID;
|
|
|
|
char szFilename[XCONTENT_MAX_FILENAME_LENGTH+1];
|
|
szFilename[XCONTENT_MAX_FILENAME_LENGTH] = 0;
|
|
XCONTENT_DATA *pData = NULL;
|
|
|
|
// Step through all items, looking for ones we care about
|
|
DWORD nNumItems;
|
|
while ( XEnumerate( hEnumerator, pBuffer, nBufferSize, &nNumItems, NULL ) == ERROR_SUCCESS )
|
|
{
|
|
// Grab the item in question
|
|
pData = (XCONTENT_DATA *) pBuffer;
|
|
|
|
// Safely store this away (null-termination is not guaranteed by the API!)
|
|
memcpy( szFilename, pData->szFileName, XCONTENT_MAX_FILENAME_LENGTH );
|
|
|
|
// See if this is our user settings file
|
|
if ( !Q_stricmp( szFilename, "UserSettings" ) )
|
|
{
|
|
nTotalSpaceNeeded -= nUserSettingsSize;
|
|
}
|
|
else if ( bHasSaves && !Q_stricmp( szFilename, pchContainerName ) )
|
|
{
|
|
nTotalSpaceNeeded -= nSaveGameSize;
|
|
}
|
|
}
|
|
|
|
// Clean up
|
|
delete[] pBuffer;
|
|
CloseHandle( hEnumerator );
|
|
}
|
|
|
|
// Finally, check its complete size
|
|
if ( nTotalSpaceNeeded <= nAvailableSpace )
|
|
return true;
|
|
|
|
return false;
|
|
#else
|
|
return true;
|
|
#endif
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Enumerate all devices and search for game data already present. If only one device has it, we return it
|
|
// Input : nUserID - User whose data we're searching for
|
|
// *pModName - Name of the mod we're searching for
|
|
// Output : Device ID which contains our data (-1 if no data was found, or data resided on multiple devices)
|
|
//-----------------------------------------------------------------------------
|
|
DWORD CXboxSystem::DiscoverUserData( DWORD nUserID, const char *pModName )
|
|
{
|
|
#ifdef _X360
|
|
// If we're entering this function without a storage device, then we must pop the UI anyway to choose it!
|
|
Assert( nUserID != XBX_INVALID_USER_ID );
|
|
if ( nUserID == XBX_INVALID_USER_ID )
|
|
return XBX_INVALID_STORAGE_ID;
|
|
|
|
if ( XUserGetSigninState( nUserID ) == eXUserSigninState_NotSignedIn )
|
|
return XBX_INVALID_STORAGE_ID;
|
|
|
|
const int nNumItemsToRetrieve = 1;
|
|
const int fContentFlags = XCONTENTFLAG_ENUM_EXCLUDECOMMON;
|
|
DWORD nFoundDevice = XBX_INVALID_STORAGE_ID;
|
|
|
|
// Save for queries against the storage devices
|
|
const wchar_t *pchContainerDisplayName;
|
|
const char *pchContainerName;
|
|
GetModSaveContainerNames( pModName, &pchContainerDisplayName, &pchContainerName );
|
|
|
|
const ULONGLONG nSaveGameSize = XContentCalculateSize( XBX_PERSISTENT_BYTES_NEEDED, 1 );
|
|
const ULONGLONG nUserSettingsSize = XContentCalculateSize( XBX_USER_SETTINGS_BYTES, 1 );
|
|
bool bHasSaves = GameHasSavegames();
|
|
ULONGLONG nTotalSpaceNeeded = ( !bHasSaves ) ? nUserSettingsSize : ( nSaveGameSize + nUserSettingsSize );
|
|
ULONGLONG nAvailableSpace = 0; // Take the first device's free space to compare this against
|
|
|
|
// Look for a user settings block for all products
|
|
DWORD nBufferSize;
|
|
HANDLE hEnumerator;
|
|
if ( XContentCreateEnumerator( nUserID,
|
|
XCONTENTDEVICE_ANY, // All devices we know about
|
|
XCONTENTTYPE_SAVEDGAME,
|
|
fContentFlags,
|
|
nNumItemsToRetrieve,
|
|
&nBufferSize,
|
|
&hEnumerator ) == ERROR_SUCCESS )
|
|
{
|
|
// Allocate a buffer of the correct size
|
|
BYTE *pBuffer = new BYTE[nBufferSize];
|
|
if ( pBuffer == NULL )
|
|
return XBX_INVALID_STORAGE_ID;
|
|
|
|
char szFilename[XCONTENT_MAX_FILENAME_LENGTH+1];
|
|
szFilename[XCONTENT_MAX_FILENAME_LENGTH] = 0;
|
|
XCONTENT_DATA *pData = NULL;
|
|
|
|
// Step through all items, looking for ones we care about
|
|
DWORD nNumItems;
|
|
while ( XEnumerate( hEnumerator, pBuffer, nBufferSize, &nNumItems, NULL ) == ERROR_SUCCESS )
|
|
{
|
|
// Grab the item in question
|
|
pData = (XCONTENT_DATA *) pBuffer;
|
|
|
|
// If they have multiple devices installed, then we must ask
|
|
if ( nFoundDevice != XBX_INVALID_STORAGE_ID && nFoundDevice != pData->DeviceID )
|
|
{
|
|
// Clean up
|
|
delete[] pBuffer;
|
|
CloseHandle( hEnumerator );
|
|
|
|
return XBX_INVALID_STORAGE_ID;
|
|
}
|
|
|
|
// Hold on to this device ID
|
|
if ( nFoundDevice != pData->DeviceID )
|
|
{
|
|
nFoundDevice = pData->DeviceID;
|
|
|
|
XDEVICE_DATA deviceData;
|
|
if ( XContentGetDeviceData( nFoundDevice, &deviceData ) != ERROR_SUCCESS )
|
|
continue;
|
|
|
|
nAvailableSpace = deviceData.ulDeviceFreeBytes;
|
|
}
|
|
|
|
// Safely store this away (null-termination is not guaranteed by the API!)
|
|
memcpy( szFilename, pData->szFileName, XCONTENT_MAX_FILENAME_LENGTH );
|
|
|
|
// See if this is our user settings file
|
|
if ( !Q_stricmp( szFilename, "UserSettings" ) )
|
|
{
|
|
nTotalSpaceNeeded -= nUserSettingsSize;
|
|
}
|
|
else if ( bHasSaves && !Q_stricmp( szFilename, pchContainerName ) )
|
|
{
|
|
nTotalSpaceNeeded -= nSaveGameSize;
|
|
}
|
|
}
|
|
|
|
// Clean up
|
|
delete[] pBuffer;
|
|
CloseHandle( hEnumerator );
|
|
}
|
|
|
|
// If we found nothing, then give up
|
|
if ( nFoundDevice == XBX_INVALID_STORAGE_ID )
|
|
return nFoundDevice;
|
|
|
|
// Finally, check its complete size
|
|
if ( nTotalSpaceNeeded <= nAvailableSpace )
|
|
return nFoundDevice;
|
|
|
|
return XBX_INVALID_STORAGE_ID;
|
|
#else
|
|
return XBX_INVALID_STORAGE_ID;
|
|
#endif
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Space free on the current device
|
|
//-----------------------------------------------------------------------------
|
|
uint CXboxSystem::GetContainerRemainingSpace( DWORD nStorageID )
|
|
{
|
|
#ifdef _X360
|
|
XDEVICE_DATA deviceData;
|
|
if ( XContentGetDeviceData( nStorageID, &deviceData ) != ERROR_SUCCESS )
|
|
return 0;
|
|
|
|
return deviceData.ulDeviceFreeBytes;
|
|
#else
|
|
return 1024*1024*1024; // 1 Gb
|
|
#endif
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Show the storage device selector
|
|
//-----------------------------------------------------------------------------
|
|
bool CXboxSystem::ShowDeviceSelector( int iController, bool bForce, uint *pStorageID, AsyncHandle_t *pAsyncHandle )
|
|
{
|
|
#ifdef _X360
|
|
AsyncResult_t *pResult = InitializeAsyncResult( &pAsyncHandle );
|
|
|
|
// We validate the size outside of this because we want to look inside our packages to see what's really free
|
|
ULARGE_INTEGER bytes;
|
|
bytes.QuadPart = XContentCalculateSize( XBX_PERSISTENT_BYTES_NEEDED + XBX_USER_SETTINGS_BYTES, 1 );
|
|
|
|
DWORD showFlags = bForce ? XCONTENTFLAG_FORCE_SHOW_UI : 0;
|
|
showFlags |= XCONTENTFLAG_MANAGESTORAGE;
|
|
|
|
DWORD ret = XShowDeviceSelectorUI( iController,
|
|
XCONTENTTYPE_SAVEDGAME,
|
|
showFlags,
|
|
bytes,
|
|
(DWORD*) pStorageID,
|
|
&pResult->overlapped
|
|
);
|
|
|
|
if ( ret != ERROR_IO_PENDING )
|
|
{
|
|
Msg( "Error showing device Selector UI\n" );
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Show the user sign in screen
|
|
//-----------------------------------------------------------------------------
|
|
void CXboxSystem::ShowSigninUI( uint nPanes, uint nFlags )
|
|
{
|
|
#ifdef _X360
|
|
XShowSigninUI( nPanes, nFlags );
|
|
#else
|
|
#endif
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Set a user context
|
|
//-----------------------------------------------------------------------------
|
|
int CXboxSystem::UserSetContext( uint nUserIdx, XUSER_CONTEXT const &xc, bool bAsync, AsyncHandle_t *pAsyncHandle )
|
|
{
|
|
#ifdef _X360
|
|
XOVERLAPPED *pOverlapped = NULL;
|
|
if ( bAsync )
|
|
{
|
|
AsyncResult_t *pResult = InitializeAsyncResult( &pAsyncHandle );
|
|
pOverlapped = &pResult->overlapped;
|
|
}
|
|
|
|
return XUserSetContextEx( nUserIdx, xc.dwContextId, xc.dwValue, pOverlapped );
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Set a user property
|
|
//-----------------------------------------------------------------------------
|
|
int CXboxSystem::UserSetProperty( uint nUserIndex, XUSER_PROPERTY const &xp, bool bAsync, AsyncHandle_t *pAsyncHandle )
|
|
{
|
|
#ifdef _X360
|
|
XOVERLAPPED *pOverlapped = NULL;
|
|
|
|
int nBytes = 0;
|
|
void const *pvData = NULL;
|
|
|
|
switch ( xp.value.type )
|
|
{
|
|
case XUSER_DATA_TYPE_INT32:
|
|
nBytes = sizeof( xp.value.nData );
|
|
pvData = &xp.value.nData;
|
|
break;
|
|
case XUSER_DATA_TYPE_INT64:
|
|
nBytes = sizeof( xp.value.i64Data );
|
|
pvData = &xp.value.i64Data;
|
|
break;
|
|
case XUSER_DATA_TYPE_DOUBLE:
|
|
nBytes = sizeof( xp.value.dblData );
|
|
pvData = &xp.value.dblData;
|
|
break;
|
|
case XUSER_DATA_TYPE_UNICODE:
|
|
nBytes = xp.value.string.cbData;
|
|
pvData = xp.value.string.pwszData;
|
|
break;
|
|
case XUSER_DATA_TYPE_FLOAT:
|
|
nBytes = sizeof( xp.value.fData );
|
|
pvData = &xp.value.fData;
|
|
break;
|
|
case XUSER_DATA_TYPE_BINARY:
|
|
nBytes = xp.value.binary.cbData;
|
|
pvData = xp.value.binary.pbData;
|
|
break;
|
|
default:
|
|
Warning( "UserSetProperty for unsupported property type %d!\n", xp.value.type );
|
|
break;
|
|
}
|
|
|
|
if ( bAsync )
|
|
{
|
|
AsyncResult_t *pResult = InitializeAsyncResult( &pAsyncHandle );
|
|
|
|
if ( nBytes && pvData )
|
|
{
|
|
pResult->pInputData = malloc( nBytes );
|
|
memcpy( pResult->pInputData, pvData, nBytes );
|
|
}
|
|
else
|
|
{
|
|
nBytes = 0;
|
|
}
|
|
|
|
pOverlapped = &pResult->overlapped;
|
|
pvData = pResult->pInputData;
|
|
}
|
|
|
|
return XUserSetPropertyEx( nUserIndex, xp.dwPropertyId, nBytes, pvData, pOverlapped );
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Get a user context
|
|
//-----------------------------------------------------------------------------
|
|
int CXboxSystem::UserGetContext( uint nUserIdx, uint nContextID, uint &nContextValue)
|
|
{
|
|
#ifdef _X360
|
|
XUSER_CONTEXT context;
|
|
context.dwContextId = nContextID;
|
|
context.dwValue = 0;
|
|
|
|
int retValue = XUserGetContext(nUserIdx,&context,NULL);
|
|
|
|
nContextValue = context.dwValue;
|
|
|
|
return retValue;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
};
|
|
|
|
int CXboxSystem::UserGetPropertyInt( uint nUserIndex, uint nPropertyId, uint &nPropertyValue)
|
|
{
|
|
#ifdef _X360
|
|
XUSER_PROPERTY prop;
|
|
prop.dwPropertyId = nPropertyId;
|
|
prop.value.nData = 0;
|
|
|
|
DWORD size = sizeof(XUSER_PROPERTY);
|
|
|
|
int retVal = XUserGetProperty(nUserIndex,&size,&prop,NULL);
|
|
|
|
nPropertyValue = prop.value.nData;
|
|
return retVal;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Create a matchmaking session
|
|
//-----------------------------------------------------------------------------
|
|
int CXboxSystem::CreateSession( uint nFlags,
|
|
uint nUserIdx,
|
|
uint nMaxPublicSlots,
|
|
uint nMaxPrivateSlots,
|
|
uint64 *pNonce,
|
|
void *pSessionInfo,
|
|
XboxHandle_t *pSessionHandle,
|
|
bool bAsync,
|
|
AsyncHandle_t *pAsyncHandle
|
|
)
|
|
{
|
|
#ifdef _X360
|
|
XOVERLAPPED *pOverlapped = NULL;
|
|
|
|
if ( bAsync )
|
|
{
|
|
AsyncResult_t *pResult = InitializeAsyncResult( &pAsyncHandle );
|
|
pOverlapped = &pResult->overlapped;
|
|
}
|
|
|
|
// Create the session
|
|
return XSessionCreate( nFlags, nUserIdx, nMaxPublicSlots, nMaxPrivateSlots, pNonce, (XSESSION_INFO*)pSessionInfo, pOverlapped, pSessionHandle );
|
|
#else
|
|
return ERROR_ACCESS_DISABLED_BY_POLICY;
|
|
#endif
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Destroy a matchmaking session
|
|
//-----------------------------------------------------------------------------
|
|
uint CXboxSystem::DeleteSession( XboxHandle_t hSession, bool bAsync, AsyncHandle_t *pAsyncHandle )
|
|
{
|
|
#ifdef _X360
|
|
XOVERLAPPED *pOverlapped = NULL;
|
|
|
|
if ( bAsync )
|
|
{
|
|
AsyncResult_t *pResult = InitializeAsyncResult( &pAsyncHandle );
|
|
pOverlapped = &pResult->overlapped;
|
|
}
|
|
|
|
// Delete the session
|
|
uint ret = XSessionDelete( hSession, pOverlapped );
|
|
CloseHandle( hSession );
|
|
|
|
return ret;
|
|
#else
|
|
return ERROR_ACCESS_DISABLED_BY_POLICY;
|
|
#endif
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Create a matchmaking session
|
|
//-----------------------------------------------------------------------------
|
|
uint CXboxSystem::SessionSearch( uint nProcedureIndex,
|
|
uint nUserIndex,
|
|
uint nNumResults,
|
|
uint nNumUsers,
|
|
uint nNumProperties,
|
|
uint nNumContexts,
|
|
XUSER_PROPERTY *pSearchProperties,
|
|
XUSER_CONTEXT *pSearchContexts,
|
|
uint *pcbResultsBuffer,
|
|
XSESSION_SEARCHRESULT_HEADER *pSearchResults,
|
|
bool bAsync,
|
|
AsyncHandle_t *pAsyncHandle
|
|
)
|
|
{
|
|
#ifdef _X360
|
|
XOVERLAPPED *pOverlapped = NULL;
|
|
|
|
if ( bAsync )
|
|
{
|
|
AsyncResult_t *pResult = InitializeAsyncResult( &pAsyncHandle );
|
|
pOverlapped = &pResult->overlapped;
|
|
}
|
|
|
|
// Search for the session
|
|
return XSessionSearchEx( nProcedureIndex, nUserIndex, nNumResults, nNumUsers, nNumProperties, nNumContexts, pSearchProperties, pSearchContexts, (DWORD*)pcbResultsBuffer, pSearchResults, pOverlapped );
|
|
#else
|
|
return ERROR_ACCESS_DISABLED_BY_POLICY;
|
|
#endif
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Starting a multiplayer game
|
|
//-----------------------------------------------------------------------------
|
|
uint CXboxSystem::SessionStart( XboxHandle_t hSession, uint nFlags, bool bAsync, AsyncHandle_t *pAsyncHandle )
|
|
{
|
|
#ifdef _X360
|
|
XOVERLAPPED *pOverlapped = NULL;
|
|
|
|
if ( bAsync )
|
|
{
|
|
AsyncResult_t *pResult = InitializeAsyncResult( &pAsyncHandle );
|
|
pOverlapped = &pResult->overlapped;
|
|
}
|
|
|
|
return XSessionStart( hSession, nFlags, pOverlapped );
|
|
#else
|
|
return ERROR_ACCESS_DISABLED_BY_POLICY;
|
|
#endif
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Finished a multiplayer game
|
|
//-----------------------------------------------------------------------------
|
|
uint CXboxSystem::SessionEnd( XboxHandle_t hSession, bool bAsync, AsyncHandle_t *pAsyncHandle )
|
|
{
|
|
#ifdef _X360
|
|
XOVERLAPPED *pOverlapped = NULL;
|
|
|
|
if ( bAsync )
|
|
{
|
|
AsyncResult_t *pResult = InitializeAsyncResult( &pAsyncHandle );
|
|
pOverlapped = &pResult->overlapped;
|
|
}
|
|
|
|
return XSessionEnd( hSession, pOverlapped );
|
|
#else
|
|
return ERROR_ACCESS_DISABLED_BY_POLICY;
|
|
#endif
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Join local users to a session
|
|
//-----------------------------------------------------------------------------
|
|
int CXboxSystem::SessionJoinLocal( XboxHandle_t hSession, uint nUserCount, const uint *pUserIndexes, const bool *pPrivateSlots, bool bAsync, AsyncHandle_t *pAsyncHandle )
|
|
{
|
|
#ifdef _X360
|
|
XOVERLAPPED *pOverlapped = NULL;
|
|
|
|
if ( bAsync )
|
|
{
|
|
AsyncResult_t *pResult = InitializeAsyncResult( &pAsyncHandle );
|
|
pOverlapped = &pResult->overlapped;
|
|
}
|
|
|
|
return XSessionJoinLocal( hSession, nUserCount, (DWORD*)pUserIndexes, (BOOL*)pPrivateSlots, pOverlapped );
|
|
#else
|
|
return ERROR_SUCCESS;
|
|
#endif
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Join remote users to a session
|
|
//-----------------------------------------------------------------------------
|
|
int CXboxSystem::SessionJoinRemote( XboxHandle_t hSession, uint nUserCount, const XUID *pXuids, const bool *pPrivateSlots, bool bAsync, AsyncHandle_t *pAsyncHandle )
|
|
{
|
|
#ifdef _X360
|
|
XOVERLAPPED *pOverlapped = NULL;
|
|
|
|
if ( bAsync )
|
|
{
|
|
AsyncResult_t *pResult = InitializeAsyncResult( &pAsyncHandle );
|
|
pOverlapped = &pResult->overlapped;
|
|
}
|
|
|
|
return XSessionJoinRemote( hSession, nUserCount, pXuids, (BOOL*)pPrivateSlots, pOverlapped );
|
|
#else
|
|
return ERROR_SUCCESS;
|
|
#endif
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Remove local users from a session
|
|
//-----------------------------------------------------------------------------
|
|
int CXboxSystem::SessionLeaveLocal( XboxHandle_t hSession, uint nUserCount, const uint *pUserIndexes, bool bAsync, AsyncHandle_t *pAsyncHandle )
|
|
{
|
|
#ifdef _X360
|
|
XOVERLAPPED *pOverlapped = NULL;
|
|
|
|
if ( bAsync )
|
|
{
|
|
AsyncResult_t *pResult = InitializeAsyncResult( &pAsyncHandle );
|
|
pOverlapped = &pResult->overlapped;
|
|
}
|
|
|
|
return XSessionLeaveLocal( hSession, nUserCount, (DWORD*)pUserIndexes, pOverlapped );
|
|
#else
|
|
return ERROR_SUCCESS;
|
|
#endif
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Remove remote users from a session
|
|
//-----------------------------------------------------------------------------
|
|
int CXboxSystem::SessionLeaveRemote( XboxHandle_t hSession, uint nUserCount, const XUID *pXuids, bool bAsync, AsyncHandle_t *pAsyncHandle )
|
|
{
|
|
#ifdef _X360
|
|
XOVERLAPPED *pOverlapped = NULL;
|
|
|
|
if ( bAsync )
|
|
{
|
|
AsyncResult_t *pResult = InitializeAsyncResult( &pAsyncHandle );
|
|
pOverlapped = &pResult->overlapped;
|
|
}
|
|
|
|
return XSessionLeaveRemote( hSession, nUserCount, pXuids, pOverlapped );
|
|
#else
|
|
return ERROR_SUCCESS;
|
|
#endif
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Migrate a session to a new host
|
|
//-----------------------------------------------------------------------------
|
|
int CXboxSystem::SessionMigrate( XboxHandle_t hSession, uint nUserIndex, void *pSessionInfo, bool bAsync, AsyncHandle_t *pAsyncHandle )
|
|
{
|
|
#ifdef _X360
|
|
XOVERLAPPED *pOverlapped = NULL;
|
|
|
|
if ( bAsync )
|
|
{
|
|
AsyncResult_t *pResult = InitializeAsyncResult( &pAsyncHandle );
|
|
pOverlapped = &pResult->overlapped;
|
|
}
|
|
|
|
return XSessionMigrateHost( hSession, nUserIndex, (XSESSION_INFO*)pSessionInfo, pOverlapped );
|
|
#else
|
|
return ERROR_SUCCESS; // On PC migration is not necessary because sessions are server-side
|
|
#endif
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Register for arbitration
|
|
//-----------------------------------------------------------------------------
|
|
int CXboxSystem::SessionArbitrationRegister( XboxHandle_t hSession, uint nFlags, uint64 nonce, uint *pBytes, void *pBuffer, bool bAsync, AsyncHandle_t *pAsyncHandle )
|
|
{
|
|
#ifdef _X360
|
|
XOVERLAPPED *pOverlapped = NULL;
|
|
|
|
if ( bAsync )
|
|
{
|
|
AsyncResult_t *pResult = InitializeAsyncResult( &pAsyncHandle );
|
|
pOverlapped = &pResult->overlapped;
|
|
}
|
|
|
|
return XSessionArbitrationRegister( hSession, nFlags, nonce, (DWORD*)pBytes, (XSESSION_REGISTRATION_RESULTS*)pBuffer, pOverlapped );
|
|
#else
|
|
return ERROR_ACCESS_DISABLED_BY_POLICY;
|
|
#endif
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Get a list of the players friends
|
|
//-----------------------------------------------------------------------------
|
|
int CXboxSystem::EnumerateFriends( uint userIndex, void **ppBuffer, bool bAsync, AsyncHandle_t *pAsyncHandle )
|
|
{
|
|
#ifdef _X360
|
|
|
|
HANDLE *phEnumerator = new HANDLE;
|
|
*phEnumerator = INVALID_HANDLE_VALUE;
|
|
|
|
DWORD nBufferBytes;
|
|
DWORD ret = XFriendsCreateEnumerator( userIndex, 0, MAX_FRIENDS, &nBufferBytes, phEnumerator );
|
|
|
|
// Just looking for the buffer size needed to hold the results
|
|
if ( ret != ERROR_SUCCESS )
|
|
{
|
|
Warning( "EnumerateFriends: XFriendsCreateEnumerator returned the error code: %d\n", ret );
|
|
CloseHandle( *phEnumerator );
|
|
delete phEnumerator;
|
|
return ret;
|
|
}
|
|
|
|
*ppBuffer = malloc( nBufferBytes );
|
|
if ( !*ppBuffer )
|
|
{
|
|
Warning( "EnumerateFriends: malloc failed for ppBuffer\n" );
|
|
CloseHandle( *phEnumerator );
|
|
delete phEnumerator;
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
|
|
XOVERLAPPED *pOverlapped = NULL;
|
|
int items = 0;
|
|
int *pItems = NULL;
|
|
|
|
if ( bAsync )
|
|
{
|
|
AsyncResult_t *pAsyncResult = InitializeAsyncResult( &pAsyncHandle );
|
|
pOverlapped = &pAsyncResult->overlapped;
|
|
|
|
// Free any existing input data
|
|
if ( pAsyncResult->pInputData )
|
|
delete pAsyncResult->pInputData;
|
|
|
|
pAsyncResult->pInputData = phEnumerator;
|
|
}
|
|
else
|
|
{
|
|
pItems = &items;
|
|
}
|
|
|
|
ret = XEnumerate( *phEnumerator, *ppBuffer, nBufferBytes, (DWORD*)pItems, pOverlapped );
|
|
|
|
if ( ( ret != ERROR_SUCCESS && !bAsync ) || ( ret != ERROR_IO_PENDING && bAsync ) )
|
|
{
|
|
Warning( "XEnumerate failed in EnumerateFriends.\n" );
|
|
CloseHandle( *phEnumerator );
|
|
delete phEnumerator;
|
|
return -1;
|
|
}
|
|
|
|
if ( !bAsync )
|
|
{
|
|
CloseHandle( *phEnumerator );
|
|
delete phEnumerator;
|
|
}
|
|
|
|
return items;
|
|
#else
|
|
return ERROR_ACCESS_DISABLED_BY_POLICY;
|
|
#endif
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Upload player stats to Xbox Live
|
|
//-----------------------------------------------------------------------------
|
|
int CXboxSystem::WriteStats( XboxHandle_t hSession, XUID xuid, uint nViews, void* pViews, bool bAsync, AsyncHandle_t *pAsyncHandle )
|
|
{
|
|
#ifdef _X360
|
|
XOVERLAPPED *pOverlapped = NULL;
|
|
|
|
if ( bAsync )
|
|
{
|
|
AsyncResult_t *pResult = InitializeAsyncResult( &pAsyncHandle );
|
|
pOverlapped = &pResult->overlapped;
|
|
}
|
|
|
|
return XSessionWriteStats( hSession, xuid, nViews, (XSESSION_VIEW_PROPERTIES*)pViews, pOverlapped );
|
|
#else
|
|
return ERROR_ACCESS_DISABLED_BY_POLICY;
|
|
#endif
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Upload player stats to Xbox Live
|
|
//-----------------------------------------------------------------------------
|
|
int CXboxSystem::FlushStats( XboxHandle_t hSession, bool bAsync, AsyncHandle_t *pAsyncHandle )
|
|
{
|
|
#ifdef _X360
|
|
XOVERLAPPED *pOverlapped = NULL;
|
|
|
|
if ( bAsync )
|
|
{
|
|
AsyncResult_t *pResult = InitializeAsyncResult( &pAsyncHandle );
|
|
pOverlapped = &pResult->overlapped;
|
|
}
|
|
|
|
return XSessionFlushStats( hSession, pOverlapped );
|
|
#else
|
|
return ERROR_ACCESS_DISABLED_BY_POLICY;
|
|
#endif
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Enumerate player stats for a specific range starting at a particular rank
|
|
//-----------------------------------------------------------------------------
|
|
int CXboxSystem::EnumerateStatsByRank( uint nStartingRank,
|
|
uint nNumRows,
|
|
uint nNumSpecs,
|
|
void *pSpecs,
|
|
void **ppResults,
|
|
bool bAsync,
|
|
AsyncHandle_t *pAsyncHandle
|
|
)
|
|
{
|
|
#ifdef _X360
|
|
|
|
HANDLE *phEnumerator = new HANDLE;
|
|
*phEnumerator = INVALID_HANDLE_VALUE;
|
|
|
|
DWORD nBufferBytes;
|
|
DWORD ret = XUserCreateStatsEnumeratorByRank( 0, nStartingRank, nNumRows, nNumSpecs, (XUSER_STATS_SPEC*)pSpecs, &nBufferBytes, phEnumerator );
|
|
|
|
if ( ret != ERROR_SUCCESS )
|
|
{
|
|
Warning( "EnumerateStatsByRank: XUserCreateStatsEnumeratorByRank failed (ret %d)\n", ret );
|
|
CloseHandle( *phEnumerator );
|
|
delete phEnumerator;
|
|
return ret;
|
|
}
|
|
|
|
*ppResults = malloc( nBufferBytes );
|
|
if ( !*ppResults )
|
|
{
|
|
Warning( "EnumerateStatsByRank: malloc failed for ppResults\n" );
|
|
CloseHandle( *phEnumerator );
|
|
delete phEnumerator;
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
XOVERLAPPED *pOverlapped = NULL;
|
|
DWORD items = 0;
|
|
DWORD *pItems = NULL;
|
|
|
|
if ( bAsync )
|
|
{
|
|
AsyncResult_t *pAsyncResult = InitializeAsyncResult( &pAsyncHandle );
|
|
pOverlapped = &pAsyncResult->overlapped;
|
|
|
|
// Free any existing input data
|
|
if ( pAsyncResult->pInputData )
|
|
delete pAsyncResult->pInputData;
|
|
|
|
pAsyncResult->pInputData = phEnumerator;
|
|
}
|
|
else
|
|
{
|
|
pItems = &items;
|
|
}
|
|
|
|
ret = XEnumerate( *phEnumerator, *ppResults, nBufferBytes, pItems, pOverlapped );
|
|
|
|
if ( ( ret != ERROR_SUCCESS && !bAsync ) || ( ret != ERROR_IO_PENDING && bAsync ) )
|
|
{
|
|
Warning( "EnumerateStatsByRank: XEnumerate failed (ret %d)\n", ret );
|
|
items = (DWORD)-1;
|
|
}
|
|
|
|
if ( !bAsync )
|
|
{
|
|
CloseHandle( *phEnumerator );
|
|
delete phEnumerator;
|
|
}
|
|
|
|
return ret;
|
|
#else
|
|
return ERROR_ACCESS_DISABLED_BY_POLICY;
|
|
#endif
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Enumerate player stats for a specific range starting at a particular rank
|
|
//-----------------------------------------------------------------------------
|
|
int CXboxSystem::EnumerateStatsByXuid( XUID nUserId,
|
|
uint nNumRows,
|
|
uint nNumSpecs,
|
|
void *pSpecs,
|
|
void **ppResults,
|
|
bool bAsync,
|
|
AsyncHandle_t *pAsyncHandle
|
|
)
|
|
{
|
|
#ifdef _X360
|
|
if (nUserId == 0)
|
|
{
|
|
Warning( "EnumerateStatsByXuid: XUserCreateStatsEnumeratorByXuid failed (nUserID is 0)\n" );
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
DWORD nBufferBytes;
|
|
HANDLE *phEnumerator = new HANDLE;
|
|
*phEnumerator = INVALID_HANDLE_VALUE;
|
|
|
|
DWORD ret = XUserCreateStatsEnumeratorByXuid( 0, nUserId, nNumRows, nNumSpecs, (XUSER_STATS_SPEC*)pSpecs, &nBufferBytes, phEnumerator );
|
|
if ( ret != ERROR_SUCCESS )
|
|
{
|
|
Warning( "EnumerateStatsByXuid: XUserCreateStatsEnumeratorByXuid failed (ret %d)\n", ret );
|
|
CloseHandle( *phEnumerator );
|
|
delete phEnumerator;
|
|
return ret;
|
|
}
|
|
|
|
*ppResults = malloc( nBufferBytes );
|
|
if ( !*ppResults )
|
|
{
|
|
Warning( "EnumerateStatsByXuid: malloc failed for ppResults\n" );
|
|
CloseHandle( *phEnumerator );
|
|
delete phEnumerator;
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
XOVERLAPPED *pOverlapped = NULL;
|
|
|
|
DWORD items = 0;
|
|
DWORD *pItems = NULL;
|
|
|
|
if ( bAsync )
|
|
{
|
|
AsyncResult_t *pAsyncResult = InitializeAsyncResult( &pAsyncHandle );
|
|
pOverlapped = &pAsyncResult->overlapped;
|
|
|
|
// Free any existing input data
|
|
if ( pAsyncResult->pInputData )
|
|
delete pAsyncResult->pInputData;
|
|
|
|
pAsyncResult->pInputData = phEnumerator;
|
|
}
|
|
else
|
|
{
|
|
pItems = &items;
|
|
}
|
|
|
|
ret = XEnumerate( *phEnumerator, *ppResults, nBufferBytes, pItems, pOverlapped );
|
|
|
|
if ( ( ret != ERROR_SUCCESS && !bAsync ) || ( ret != ERROR_IO_PENDING && bAsync ) )
|
|
{
|
|
Warning( "EnumerateStatsByXuid: XEnumerate failed (ret %d)\n", ret );
|
|
items = (DWORD)-1;
|
|
}
|
|
|
|
if ( !bAsync )
|
|
{
|
|
CloseHandle( *phEnumerator );
|
|
delete phEnumerator;
|
|
}
|
|
|
|
return ret;
|
|
|
|
#else
|
|
return ERROR_ACCESS_DISABLED_BY_POLICY;
|
|
#endif
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Enumerate a player's achievements
|
|
//-----------------------------------------------------------------------------
|
|
int CXboxSystem::EnumerateAchievements( uint nUserIdx,
|
|
uint64 xuid,
|
|
uint nStartingIdx,
|
|
uint nCount,
|
|
void *pBuffer,
|
|
uint nBufferBytes,
|
|
bool bAsync,
|
|
AsyncHandle_t *pAsyncHandle
|
|
)
|
|
{
|
|
Error( "This function is obsolete and should not be used!\nReturn code cannot be an error code and number of results at the same time!\n" );
|
|
return ERROR_NO_SUCH_PRIVILEGE;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Award an achievement to the current user
|
|
//-----------------------------------------------------------------------------
|
|
int CXboxSystem::AwardAchievement( uint nUserIdx, uint nAchievementId, AsyncHandle_t *ppOverlappedResult )
|
|
{
|
|
#ifdef _X360
|
|
// Can't award achievements if this is a trial package!
|
|
if ( IsArcadeTitleUnlocked() == false )
|
|
return ERROR_SUCCESS;
|
|
|
|
// Create a new result
|
|
AsyncResult_t *pResult = CreateAsyncResult( false );
|
|
|
|
XUSER_ACHIEVEMENT ach;
|
|
ach.dwUserIndex = nUserIdx;
|
|
ach.dwAchievementId = nAchievementId;
|
|
|
|
pResult->pInputData = malloc( sizeof( ach ) );
|
|
Q_memcpy( pResult->pInputData, &ach, sizeof( ach ) );
|
|
|
|
DWORD ret = XUserWriteAchievements( 1, (XUSER_ACHIEVEMENT*)pResult->pInputData, &pResult->overlapped );
|
|
if ( ret != ERROR_IO_PENDING )
|
|
{
|
|
Warning( "XUserWriteAchievments failed.\n" );
|
|
}
|
|
|
|
// Return it to the user if they've supplied a pointer
|
|
if ( ppOverlappedResult != NULL )
|
|
{
|
|
*ppOverlappedResult = pResult;
|
|
}
|
|
|
|
return ret;
|
|
#else
|
|
return ERROR_ACCESS_DISABLED_BY_POLICY;
|
|
#endif
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Grant an avatar asset award to the current user
|
|
//-----------------------------------------------------------------------------
|
|
int CXboxSystem::AwardAvatarAsset( uint nUserIdx, uint nAwardId, AsyncHandle_t *ppOverlappedResult )
|
|
{
|
|
#ifdef _X360
|
|
// Create a new result
|
|
AsyncResult_t *pResult = CreateAsyncResult( false );
|
|
|
|
XUSER_AVATARASSET award;
|
|
award.dwUserIndex = nUserIdx;
|
|
award.dwAwardId = nAwardId;
|
|
|
|
pResult->pInputData = malloc( sizeof( award ) );
|
|
Q_memcpy( pResult->pInputData, &award, sizeof( award ) );
|
|
|
|
DWORD ret = XUserAwardAvatarAssets( 1, (XUSER_AVATARASSET*)pResult->pInputData, &pResult->overlapped );
|
|
if ( ret != ERROR_IO_PENDING )
|
|
{
|
|
Warning( "XUserAwardAvatarAssets failed.\n" );
|
|
}
|
|
|
|
if ( ppOverlappedResult != NULL )
|
|
{
|
|
*ppOverlappedResult = pResult;
|
|
}
|
|
|
|
return ret;
|
|
#else
|
|
return ERROR_ACCESS_DISABLED_BY_POLICY;
|
|
#endif
|
|
}
|
|
|
|
|
|
// trial mode
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Show the "unlock trial game" blade
|
|
//-----------------------------------------------------------------------------
|
|
void CXboxSystem::ShowUnlockFullGameUI( void )
|
|
{
|
|
#ifdef _X360
|
|
XShowMarketplaceUI( XBX_GetPrimaryUserId(),
|
|
XSHOWMARKETPLACEUI_ENTRYPOINT_CONTENTITEM,
|
|
0x5841096000000001,
|
|
(DWORD) -1 );
|
|
#endif
|
|
}
|
|
|
|
//=============================================================================
|
|
static ConVar xbox_arcade_title_unlocked( "xbox_arcade_title_unlocked", CSTRIKE_TRIAL_MODE ? "0": "1", FCVAR_DEVELOPMENTONLY, "debug unlocking arcade title" );
|
|
static bool g_bTitleUnlocked = false;
|
|
static ConVar xbox_arcade_remaining_trial_time( "xbox_arcade_remaining_trial_time", "2700.0", FCVAR_ARCHIVE_GAMECONSOLE | FCVAR_SS | FCVAR_DEVELOPMENTONLY, "time remaining in trial mode" );
|
|
//=============================================================================
|
|
|
|
float CXboxSystem::GetArcadeRemainingTrialTime( int nSlot )
|
|
{
|
|
SplitScreenConVarRef trialTime( "xbox_arcade_remaining_trial_time" );
|
|
return trialTime.GetFloat( nSlot );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Determine whether this arcade game is unlocked
|
|
//-----------------------------------------------------------------------------
|
|
bool CXboxSystem::UpdateArcadeTitleUnlockStatus( void )
|
|
{
|
|
#ifdef _X360
|
|
// This means it was unlocked at some point during this session, which allows it to stay unlocked for the duration of the session
|
|
if ( g_bTitleUnlocked )
|
|
return false;
|
|
|
|
// Synchronously retrieve our own license bits
|
|
DWORD dwLicenseMask;
|
|
if ( XContentGetLicenseMask( &dwLicenseMask, NULL ) == ERROR_SUCCESS )
|
|
{
|
|
if ( ( dwLicenseMask & 0x01 ) == 0x01 )
|
|
{
|
|
g_bTitleUnlocked = true;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
#else
|
|
return true;
|
|
#endif
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Determine whether this arcade game is unlocked
|
|
//-----------------------------------------------------------------------------
|
|
bool CXboxSystem::IsArcadeTitleUnlocked( void )
|
|
{
|
|
#if defined ( _X360 )
|
|
// This allows us to quickly test licenses being on or off
|
|
if ( xbox_arcade_title_unlocked.GetBool() )
|
|
return true;
|
|
|
|
// This means it was unlocked at some point during this session, which allows it to stay unlocked for the duration of the session
|
|
return g_bTitleUnlocked;
|
|
#else
|
|
return true;
|
|
#endif
|
|
}
|
|
|
|
|
|
int CXboxSystem::Io_HasOverlappedIoCompleted( XOVERLAPPED *pOverlapped )
|
|
{
|
|
#ifdef _X360
|
|
return XHasOverlappedIoCompleted( pOverlapped );
|
|
#else
|
|
return 1;
|
|
#endif
|
|
}
|
|
|
|
int CXboxSystem::NetRandom( byte *pb, unsigned numBytes )
|
|
{
|
|
#ifdef _X360
|
|
return XNetRandom( pb, numBytes );
|
|
#else
|
|
if ( pb )
|
|
{
|
|
for ( byte * const pbEnd = pb + numBytes; pb < pbEnd; ++ pb )
|
|
*pb = ( byte ) ( unsigned ) RandomInt( 0, 255 );
|
|
}
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
DWORD CXboxSystem::NetGetTitleXnAddr( XNADDR *pxna )
|
|
{
|
|
#ifdef _X360
|
|
return XNetGetTitleXnAddr( pxna );
|
|
#else
|
|
if ( pxna )
|
|
memset( pxna, 0, sizeof( *pxna ) );
|
|
return XNET_GET_XNADDR_NONE;
|
|
#endif
|
|
}
|
|
|
|
int CXboxSystem::NetXnAddrToMachineId( const XNADDR *pxnaddr, uint64 *pqwMachineId )
|
|
{
|
|
#ifdef _X360
|
|
return XNetXnAddrToMachineId( pxnaddr, pqwMachineId );
|
|
#else
|
|
if ( pqwMachineId )
|
|
*pqwMachineId = 0ull;
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
int CXboxSystem::NetInAddrToXnAddr( const IN_ADDR ina, XNADDR *pxna, XNKID *pxnkid )
|
|
{
|
|
#ifdef _X360
|
|
return XNetInAddrToXnAddr( ina, pxna, pxnkid );
|
|
#else
|
|
if ( pxnkid )
|
|
{
|
|
memset( pxnkid, 0, sizeof( *pxnkid ) );
|
|
}
|
|
|
|
if ( pxna )
|
|
{
|
|
memset( pxna, 0, sizeof( *pxna ) );
|
|
pxna->ina = ina;
|
|
}
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
int CXboxSystem::NetXnAddrToInAddr( const XNADDR *pxna, const XNKID *pxnkid, IN_ADDR *pina )
|
|
{
|
|
#ifdef _X360
|
|
return XNetXnAddrToInAddr( pxna, pxnkid, pina );
|
|
#else
|
|
if ( pina )
|
|
{
|
|
if ( pxna )
|
|
{
|
|
*pina = pxna->ina;
|
|
}
|
|
else
|
|
{
|
|
memset( pina, 0, sizeof( *pina ) );
|
|
}
|
|
}
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
XUSER_SIGNIN_STATE CXboxSystem::UserGetSigninState( int iCtrlr )
|
|
{
|
|
#ifdef _X360
|
|
return XUserGetSigninState( iCtrlr );
|
|
#else
|
|
return eXUserSigninState_SignedInToLive;
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
#ifdef _X360
|
|
|
|
//
|
|
// Title server address refcounting service
|
|
//
|
|
|
|
struct XTitleServerAddr_t
|
|
{
|
|
IN_ADDR m_ina;
|
|
DWORD m_dwServiceID;
|
|
IN_ADDR m_result;
|
|
DWORD m_dwRefCount;
|
|
};
|
|
|
|
class XTitleServerAddrRefs
|
|
{
|
|
public:
|
|
INT XNetServerToInAddr(
|
|
const IN_ADDR ina,
|
|
DWORD dwServiceId,
|
|
IN_ADDR *pina
|
|
);
|
|
INT XNetUnregisterInAddr(
|
|
const IN_ADDR ina
|
|
);
|
|
protected:
|
|
CUtlVector< XTitleServerAddr_t > m_arrServerAddrs;
|
|
}
|
|
g_XTitleServerAddrRefs;
|
|
|
|
INT XTitleServerAddrRefs::XNetServerToInAddr(
|
|
const IN_ADDR ina,
|
|
DWORD dwServiceId,
|
|
IN_ADDR *pina
|
|
)
|
|
{
|
|
for ( int k = 0; k < m_arrServerAddrs.Count(); ++ k )
|
|
{
|
|
XTitleServerAddr_t &x = m_arrServerAddrs[k];
|
|
if ( x.m_ina.s_addr == ina.s_addr && x.m_dwServiceID == dwServiceId )
|
|
{
|
|
*pina = x.m_result;
|
|
++ x.m_dwRefCount;
|
|
DevMsg( "XTitleServerAddrRefs::XNetServerToInAddr( %08X -> %08X ), cached connection [state = %d], refcount %d\n",
|
|
x.m_ina.s_addr, x.m_result.s_addr, ::XNetGetConnectStatus( x.m_result ), x.m_dwRefCount );
|
|
|
|
INT iConnectCode = ::XNetConnect( x.m_result );
|
|
DevMsg( " XNetConnect code = %d\n", iConnectCode );
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
INT nResult = ::XNetServerToInAddr( ina, dwServiceId, pina );
|
|
if ( nResult )
|
|
return nResult;
|
|
|
|
XTitleServerAddr_t x = { ina, dwServiceId, *pina, 1 };
|
|
m_arrServerAddrs.AddToTail( x );
|
|
DevMsg( "XTitleServerAddrRefs::XNetServerToInAddr( %08X -> %08X ), new connection, refcount %d\n", x.m_ina.s_addr, x.m_result.s_addr, x.m_dwRefCount );
|
|
return 0;
|
|
}
|
|
|
|
INT XTitleServerAddrRefs::XNetUnregisterInAddr(
|
|
const IN_ADDR ina
|
|
)
|
|
{
|
|
for ( int k = 0; k < m_arrServerAddrs.Count(); ++ k )
|
|
{
|
|
XTitleServerAddr_t &x = m_arrServerAddrs[k];
|
|
if ( x.m_result.s_addr == ina.s_addr )
|
|
{
|
|
-- x.m_dwRefCount;
|
|
DevMsg( "XTitleServerAddrRefs::XNetUnregisterInAddr( %08X -> %08X ), cached connection, refcount %d\n", x.m_ina.s_addr, x.m_result.s_addr, x.m_dwRefCount );
|
|
if ( x.m_dwRefCount )
|
|
return 0; // not unregistering netadr since there are more references to it
|
|
|
|
m_arrServerAddrs.FastRemove( k );
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ::XNetUnregisterInAddr( ina );
|
|
}
|
|
|
|
//
|
|
// XSession calls serializer service
|
|
//
|
|
|
|
static ConVar sys_xsessioncallstack_delay( "sys_xsessioncallstack_delay", "0" );
|
|
|
|
class XSessionCallStack
|
|
{
|
|
public:
|
|
struct OverlappedSessionCall
|
|
{
|
|
HANDLE m_hSession;
|
|
XOVERLAPPED *m_pxOverlapped;
|
|
XOVERLAPPED m_xOverlapped;
|
|
|
|
virtual DWORD Run() = NULL;
|
|
virtual char const * Name() = NULL;
|
|
|
|
virtual ~OverlappedSessionCall() {}
|
|
OverlappedSessionCall( HANDLE hSession, XOVERLAPPED *pxOverlapped ) :
|
|
m_hSession( hSession ), m_pxOverlapped( pxOverlapped )
|
|
{
|
|
Plat_FastMemset( &m_xOverlapped, 0, sizeof( m_xOverlapped ) );
|
|
|
|
if ( m_pxOverlapped )
|
|
{
|
|
Assert( !memcmp( &m_xOverlapped, m_pxOverlapped, sizeof( m_xOverlapped ) ) );
|
|
m_pxOverlapped->InternalLow = ERROR_IO_PENDING;
|
|
}
|
|
}
|
|
};
|
|
|
|
struct DebugDelayCall : public OverlappedSessionCall
|
|
{
|
|
DebugDelayCall( HANDLE hSession ) : OverlappedSessionCall( hSession, NULL ), m_flTimeStarted( 0 ) {}
|
|
|
|
virtual DWORD Run() { m_flTimeStarted = Plat_FloatTime(); return ERROR_IO_PENDING; }
|
|
virtual char const * Name() { return "DebugDelayCall"; }
|
|
|
|
float m_flTimeStarted;
|
|
};
|
|
|
|
typedef CUtlVector< OverlappedSessionCall * > CallsArray;
|
|
typedef CUtlVector< CallsArray * > SessionCalls;
|
|
|
|
public:
|
|
DWORD ScheduleOverlappedSessionCall( OverlappedSessionCall *pCall );
|
|
bool CancelOverlapped( XOVERLAPPED *pxOverlapped );
|
|
|
|
void RunFrame();
|
|
|
|
protected:
|
|
void OnDeleteHeadCallAndRunNext( CallsArray &arrCalls, int &idx );
|
|
void OnSessionCallFinished( CallsArray &arrCalls );
|
|
void RunNextCall( CallsArray &arrCalls );
|
|
|
|
protected:
|
|
SessionCalls m_SessionCalls;
|
|
}
|
|
g_XSessionCallStack;
|
|
|
|
// Macros for DECLARE_OVERLAPPED_SESSION_CALL_N
|
|
#include "xboxsystem.xsessioncallstack.inl"
|
|
|
|
void XSessionCallStack::RunFrame()
|
|
{
|
|
// Walk over all currently pending calls and see
|
|
// if they are finished:
|
|
for ( int k = 0; k < m_SessionCalls.Count(); ++ k )
|
|
{
|
|
CallsArray &arrCalls = *m_SessionCalls[k];
|
|
Assert( arrCalls.Count() );
|
|
|
|
OverlappedSessionCall &osc = *arrCalls.Head();
|
|
|
|
if ( !osc.m_pxOverlapped )
|
|
{
|
|
if ( DebugDelayCall *pDebugDelayCall = dynamic_cast< DebugDelayCall * >( &osc ) )
|
|
{
|
|
if ( Plat_FloatTime() < pDebugDelayCall->m_flTimeStarted + sys_xsessioncallstack_delay.GetFloat() )
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if ( !XHasOverlappedIoCompleted( &osc.m_xOverlapped ) )
|
|
continue;
|
|
|
|
DevMsg( 2, "XSessionCallStack: finished overlapped call %s for session %p [%d]\n", osc.Name(), osc.m_hSession, arrCalls.Count() );
|
|
|
|
OnDeleteHeadCallAndRunNext( arrCalls, k );
|
|
}
|
|
}
|
|
|
|
bool XSessionCallStack::CancelOverlapped( XOVERLAPPED *pxOverlapped )
|
|
{
|
|
if ( !pxOverlapped )
|
|
return false;
|
|
|
|
// Attempt to find the overlapped operation in the session calls
|
|
for ( int k = 0; k < m_SessionCalls.Count(); ++ k )
|
|
{
|
|
CallsArray &arrCalls = *m_SessionCalls[k];
|
|
Assert( arrCalls.Count() );
|
|
|
|
OverlappedSessionCall &osc = *arrCalls.Head();
|
|
|
|
if ( osc.m_pxOverlapped == pxOverlapped )
|
|
{
|
|
// Actually cancel the faked overlapped call
|
|
::XCancelOverlapped( &osc.m_xOverlapped );
|
|
DevMsg( 2, "XSessionCallStack: cancelled overlapped call %s for session %p [%d]\n", osc.Name(), osc.m_hSession, arrCalls.Count() );
|
|
|
|
OnDeleteHeadCallAndRunNext( arrCalls, k );
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
DWORD XSessionCallStack::ScheduleOverlappedSessionCall( OverlappedSessionCall *pCall )
|
|
{
|
|
// If the call has a NULL overlapped, then execute it right away
|
|
if ( !pCall->m_pxOverlapped )
|
|
{
|
|
DevWarning( "Executing a non-overlapped call in XSessionCallStack!\n" );
|
|
DWORD ret = pCall->Run();
|
|
delete pCall;
|
|
return ret;
|
|
}
|
|
|
|
// Attempt to find the session for this call
|
|
for ( int k = 0; k < m_SessionCalls.Count(); ++ k )
|
|
{
|
|
CallsArray &arrCalls = *m_SessionCalls[k];
|
|
Assert( arrCalls.Count() );
|
|
|
|
OverlappedSessionCall &osc = *arrCalls.Head();
|
|
|
|
if ( osc.m_hSession == pCall->m_hSession )
|
|
{
|
|
if ( sys_xsessioncallstack_delay.GetFloat() > 0.0f )
|
|
{
|
|
DebugDelayCall *pDbgCall = new DebugDelayCall( pCall->m_hSession );
|
|
arrCalls.AddToTail( pDbgCall );
|
|
DevMsg( 2, "XSessionCallStack: injecting debug delay call %s for session %p [%d]\n", pDbgCall->Name(), pDbgCall->m_hSession, arrCalls.Count() );
|
|
}
|
|
|
|
// We have a call stack for this session
|
|
arrCalls.AddToTail( pCall );
|
|
DevMsg( 2, "XSessionCallStack: queued call %s for session %p [%d]\n", pCall->Name(), pCall->m_hSession, arrCalls.Count() );
|
|
return ERROR_IO_PENDING;
|
|
}
|
|
}
|
|
|
|
// In case special call stack debugging is enabled, we insert a delay between
|
|
// every scheduled call
|
|
|
|
if ( sys_xsessioncallstack_delay.GetFloat() > 0.0f )
|
|
{
|
|
DebugDelayCall *pDbgCall = new DebugDelayCall( pCall->m_hSession );
|
|
DevMsg( 2, "XSessionCallStack: injecting debug delay call %s for session %p [new call stack]\n", pDbgCall->Name(), pDbgCall->m_hSession );
|
|
|
|
CallsArray *pCallStack = new CallsArray;
|
|
pCallStack->AddToTail( pDbgCall );
|
|
pCallStack->AddToTail( pCall );
|
|
DevMsg( 2, "XSessionCallStack: queued call %s for session %p [%d]\n", pCall->Name(), pCall->m_hSession, pCallStack->Count() );
|
|
|
|
m_SessionCalls.AddToTail( pCallStack );
|
|
|
|
return pDbgCall->Run();
|
|
}
|
|
|
|
// Otherwise it is a new session, we should queue the call
|
|
// and if it goes PENDING create a new call stack
|
|
|
|
DevMsg( 2, "XSessionCallStack: running call %s for session %p [new call stack]\n", pCall->Name(), pCall->m_hSession );
|
|
|
|
DWORD ret = pCall->Run();
|
|
if ( ret != ERROR_IO_PENDING )
|
|
{
|
|
DevWarning( 2, "XSessionCallStack: ret=%d for call %s for session %p [call stack not created]\n", ret, pCall->Name(), pCall->m_hSession );
|
|
delete pCall;
|
|
return ret;
|
|
}
|
|
|
|
// Call is pending
|
|
CallsArray *pCallStack = new CallsArray;
|
|
pCallStack->AddToTail( pCall );
|
|
|
|
m_SessionCalls.AddToTail( pCallStack );
|
|
|
|
return ERROR_IO_PENDING;
|
|
}
|
|
|
|
void XSessionCallStack::OnDeleteHeadCallAndRunNext( CallsArray &arrCalls, int &idx )
|
|
{
|
|
// Mark call as finished and delete it
|
|
OnSessionCallFinished( arrCalls );
|
|
|
|
// Check if the stack has another call to schedule
|
|
RunNextCall( arrCalls );
|
|
|
|
// Check if the stack for the current session is now empty
|
|
if ( !arrCalls.Count() )
|
|
{
|
|
delete &arrCalls;
|
|
m_SessionCalls.Remove( idx -- );
|
|
}
|
|
}
|
|
|
|
void XSessionCallStack::OnSessionCallFinished( CallsArray &arrCalls )
|
|
{
|
|
OverlappedSessionCall &osc = *arrCalls.Head();
|
|
|
|
// Copy data into the caller's overlapped structure
|
|
if ( osc.m_pxOverlapped )
|
|
{
|
|
Plat_FastMemcpy( osc.m_pxOverlapped, &osc.m_xOverlapped, sizeof( osc.m_xOverlapped ) );
|
|
}
|
|
|
|
// Delete the call and pop it off the stack
|
|
delete &osc;
|
|
arrCalls.RemoveMultipleFromHead( 1 );
|
|
}
|
|
|
|
void XSessionCallStack::RunNextCall( CallsArray &arrCalls )
|
|
{
|
|
while ( arrCalls.Count() )
|
|
{
|
|
OverlappedSessionCall &osc = *arrCalls.Head();
|
|
|
|
DevMsg( 2, "XSessionCallStack: running next call %s for session %p [%d]\n", osc.Name(), osc.m_hSession, arrCalls.Count() );
|
|
DWORD ret = osc.Run();
|
|
|
|
if ( ret != ERROR_IO_PENDING )
|
|
{
|
|
DevWarning( 2, "XSessionCallStack: ret=%d for call %s for session %p [%d]\n", ret, osc.Name(), osc.m_hSession, arrCalls.Count() );
|
|
OnSessionCallFinished( arrCalls );
|
|
}
|
|
else
|
|
{
|
|
break; // call is pending
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// CXOnline implementation
|
|
//
|
|
|
|
static class CXOnline_Impl : public IXOnline
|
|
{
|
|
|
|
public:
|
|
virtual void RunFrame()
|
|
{
|
|
g_XSessionCallStack.RunFrame();
|
|
}
|
|
|
|
public:
|
|
virtual DWORD XCancelOverlapped( PXOVERLAPPED pOverlapped )
|
|
{
|
|
if ( g_XSessionCallStack.CancelOverlapped( pOverlapped ) )
|
|
return ERROR_SUCCESS;
|
|
|
|
return ::XCancelOverlapped( pOverlapped );
|
|
}
|
|
|
|
public:
|
|
virtual DWORD XFriendsCreateEnumerator(
|
|
DWORD dwUserIndex,
|
|
DWORD dwStartingIndex,
|
|
DWORD dwFriendsToReturn,
|
|
DWORD *pcbBuffer,
|
|
HANDLE *ph
|
|
)
|
|
{
|
|
return ::XFriendsCreateEnumerator( dwUserIndex, dwStartingIndex, dwFriendsToReturn, pcbBuffer, ph );
|
|
}
|
|
|
|
virtual INT XNetQosRelease(
|
|
XNQOS * pxnqos
|
|
)
|
|
{
|
|
return ::XNetQosRelease( pxnqos );
|
|
}
|
|
|
|
virtual INT XNetQosServiceLookup(
|
|
DWORD dwFlags,
|
|
WSAEVENT hEvent,
|
|
XNQOS * * ppxnqos
|
|
)
|
|
{
|
|
return ::XNetQosServiceLookup( dwFlags, hEvent, ppxnqos );
|
|
}
|
|
|
|
virtual DWORD XInviteGetAcceptedInfo(
|
|
DWORD dwUserIndex,
|
|
XINVITE_INFO *pInfo
|
|
)
|
|
{
|
|
return ::XInviteGetAcceptedInfo( dwUserIndex, pInfo );
|
|
}
|
|
|
|
virtual DWORD XInviteSend(
|
|
DWORD dwUserIndex,
|
|
DWORD cInvitees,
|
|
const XUID *pXuidInvitees,
|
|
const WCHAR *pszText,
|
|
XOVERLAPPED *pXOverlapped
|
|
)
|
|
{
|
|
return ::XInviteSend( dwUserIndex, cInvitees, pXuidInvitees, pszText, pXOverlapped );
|
|
}
|
|
|
|
virtual DWORD XTitleServerCreateEnumerator(
|
|
LPCSTR pszServerInfo,
|
|
DWORD cItem,
|
|
PDWORD pcbBuffer,
|
|
PHANDLE phEnum
|
|
)
|
|
{
|
|
return ::XTitleServerCreateEnumerator( pszServerInfo, cItem, pcbBuffer, phEnum );
|
|
}
|
|
|
|
virtual INT XNetQosLookup(
|
|
UINT cxna,
|
|
const XNADDR * apxna[],
|
|
const XNKID * apxnkid[],
|
|
const XNKEY * apxnkey[],
|
|
UINT cina,
|
|
const IN_ADDR aina[],
|
|
const DWORD adwServiceId[],
|
|
UINT cProbes,
|
|
DWORD dwBitsPerSec,
|
|
DWORD dwFlags,
|
|
WSAEVENT hEvent,
|
|
XNQOS ** ppxnqos
|
|
)
|
|
{
|
|
return ::XNetQosLookup( cxna, apxna, apxnkid, apxnkey, cina, aina, adwServiceId, cProbes, dwBitsPerSec, dwFlags, hEvent, ppxnqos );
|
|
}
|
|
|
|
virtual INT XNetUnregisterInAddr(
|
|
const IN_ADDR ina
|
|
)
|
|
{
|
|
return g_XTitleServerAddrRefs.XNetUnregisterInAddr( ina );
|
|
}
|
|
|
|
virtual INT XNetServerToInAddr(
|
|
const IN_ADDR ina,
|
|
DWORD dwServiceId,
|
|
IN_ADDR *pina
|
|
)
|
|
{
|
|
return g_XTitleServerAddrRefs.XNetServerToInAddr( ina, dwServiceId, pina );
|
|
}
|
|
|
|
virtual DWORD XSessionSearchEx(
|
|
DWORD dwProcedureIndex,
|
|
DWORD dwUserIndex,
|
|
DWORD dwNumResults,
|
|
DWORD dwNumUsers,
|
|
WORD wNumProperties,
|
|
WORD wNumContexts,
|
|
PXUSER_PROPERTY pSearchProperties,
|
|
PXUSER_CONTEXT pSearchContexts,
|
|
DWORD *pcbResultsBuffer,
|
|
PXSESSION_SEARCHRESULT_HEADER pSearchResults,
|
|
PXOVERLAPPED pXOverlapped
|
|
)
|
|
{
|
|
return ::XSessionSearchEx( dwProcedureIndex, dwUserIndex, dwNumResults, dwNumUsers, wNumProperties, wNumContexts, pSearchProperties, pSearchContexts, pcbResultsBuffer, pSearchResults, pXOverlapped );
|
|
}
|
|
|
|
IMPLEMENT_OVERLAPPED_SESSION_CALL_2( XSessionGetDetails, DWORD *, pcbResultsBuffer, XSESSION_LOCAL_DETAILS *, pSessionDetails );
|
|
|
|
virtual INT XNetQosListen(
|
|
const XNKID * pxnkid,
|
|
const BYTE * pb,
|
|
UINT cb,
|
|
DWORD dwBitsPerSec,
|
|
DWORD dwFlags
|
|
)
|
|
{
|
|
return ::XNetQosListen( pxnkid, pb, cb, dwBitsPerSec, dwFlags );
|
|
}
|
|
|
|
IMPLEMENT_OVERLAPPED_SESSION_CALL_3( XSessionModify, DWORD, dwFlags, DWORD, dwMaxPublicSlots, DWORD, dwMaxPrivateSlots );
|
|
|
|
virtual DWORD XNetGetTitleXnAddr(
|
|
XNADDR *pxna
|
|
)
|
|
{
|
|
return ::XNetGetTitleXnAddr( pxna );
|
|
}
|
|
|
|
virtual INT XNetRegisterKey(
|
|
const XNKID *pxnkid,
|
|
const XNKEY *pxnkey
|
|
)
|
|
{
|
|
return ::XNetRegisterKey( pxnkid, pxnkey );
|
|
}
|
|
|
|
virtual INT XNetUnregisterKey(
|
|
const XNKID *pxnkid
|
|
)
|
|
{
|
|
return ::XNetUnregisterKey( pxnkid );
|
|
}
|
|
|
|
virtual INT XNetCreateKey(
|
|
XNKID *pxnkid,
|
|
XNKEY *pxnkey
|
|
)
|
|
{
|
|
return ::XNetCreateKey( pxnkid, pxnkey );
|
|
}
|
|
|
|
virtual INT XNetReplaceKey(
|
|
const XNKID *pxnkidUnregister,
|
|
const XNKID * pxnkidReplace
|
|
)
|
|
{
|
|
return ::XNetReplaceKey( pxnkidUnregister, pxnkidReplace );
|
|
}
|
|
|
|
IMPLEMENT_OVERLAPPED_SESSION_CALL_0( XSessionDelete );
|
|
|
|
virtual DWORD XSessionCreate(
|
|
DWORD dwFlags,
|
|
DWORD dwUserIndex,
|
|
DWORD dwMaxPublicSlots,
|
|
DWORD dwMaxPrivateSlots,
|
|
ULONGLONG *pqwSessionNonce,
|
|
PXSESSION_INFO pSessionInfo,
|
|
PXOVERLAPPED pXOverlapped,
|
|
HANDLE *ph
|
|
)
|
|
{
|
|
return ::XSessionCreate( dwFlags, dwUserIndex, dwMaxPublicSlots, dwMaxPrivateSlots, pqwSessionNonce, pSessionInfo, pXOverlapped, ph );
|
|
}
|
|
|
|
virtual DWORD XSessionSearchByID(
|
|
XNKID sessionID,
|
|
DWORD dwUserIndex,
|
|
DWORD *pcbResultsBuffer,
|
|
PXSESSION_SEARCHRESULT_HEADER pSearchResults,
|
|
PXOVERLAPPED pXOverlapped
|
|
)
|
|
{
|
|
return ::XSessionSearchByID( sessionID, dwUserIndex, pcbResultsBuffer, pSearchResults, pXOverlapped );
|
|
}
|
|
|
|
IMPLEMENT_OVERLAPPED_SESSION_CALL_2( XSessionMigrateHost, DWORD, dwUserIndex, XSESSION_INFO *, pSessionInfo );
|
|
|
|
IMPLEMENT_OVERLAPPED_SESSION_CALL_3( XSessionJoinRemote, DWORD, dwXuidCount, const XUID *, pXuids, const BOOL *, pfPrivateSlots );
|
|
|
|
IMPLEMENT_OVERLAPPED_SESSION_CALL_3( XSessionJoinLocal, DWORD, dwUserCount, const DWORD *, pdwUserIndexes, const BOOL *, pfPrivateSlots );
|
|
|
|
IMPLEMENT_OVERLAPPED_SESSION_CALL_2( XSessionLeaveRemote, DWORD, dwXuidCount, const XUID *, pXuids );
|
|
|
|
IMPLEMENT_OVERLAPPED_SESSION_CALL_2( XSessionLeaveLocal, DWORD, dwUserCount, const DWORD *, pdwUserIndexes );
|
|
|
|
IMPLEMENT_OVERLAPPED_SESSION_CALL_0( XSessionEnd );
|
|
|
|
IMPLEMENT_OVERLAPPED_SESSION_CALL_1( XSessionStart, DWORD, dwFlags );
|
|
|
|
virtual DWORD XNetGetConnectStatus(
|
|
const IN_ADDR ina
|
|
)
|
|
{
|
|
return ::XNetGetConnectStatus( ina );
|
|
}
|
|
|
|
virtual INT XNetInAddrToXnAddr(
|
|
const IN_ADDR ina,
|
|
XNADDR *pxna,
|
|
XNKID *pxnkid
|
|
)
|
|
{
|
|
return ::XNetInAddrToXnAddr( ina, pxna, pxnkid );
|
|
}
|
|
|
|
virtual INT XNetXnAddrToInAddr(
|
|
const XNADDR *pxna,
|
|
const XNKID *pxnkid,
|
|
IN_ADDR *pina
|
|
)
|
|
{
|
|
return ::XNetXnAddrToInAddr( pxna, pxnkid, pina );
|
|
}
|
|
|
|
virtual INT XNetConnect(
|
|
const IN_ADDR ina
|
|
)
|
|
{
|
|
return ::XNetConnect( ina );
|
|
}
|
|
|
|
virtual DWORD XUserReadProfileSettingsByXuid(
|
|
DWORD dwTitleId,
|
|
DWORD dwUserIndexRequester,
|
|
DWORD dwNumFor,
|
|
const XUID *pxuidFor,
|
|
DWORD dwNumSettingIds,
|
|
const DWORD *pdwSettingIds,
|
|
DWORD *pcbResults,
|
|
PXUSER_READ_PROFILE_SETTING_RESULT pResults,
|
|
PXOVERLAPPED pXOverlapped
|
|
)
|
|
{
|
|
return ::XUserReadProfileSettingsByXuid( dwTitleId, dwUserIndexRequester, dwNumFor, pxuidFor, dwNumSettingIds, pdwSettingIds, pcbResults, pResults, pXOverlapped );
|
|
}
|
|
|
|
virtual DWORD XUserReadProfileSettings(
|
|
DWORD dwTitleId,
|
|
DWORD dwUserIndex,
|
|
DWORD dwNumSettingIds,
|
|
const DWORD *pdwSettingIds,
|
|
DWORD *pcbResults,
|
|
PXUSER_READ_PROFILE_SETTING_RESULT pResults,
|
|
PXOVERLAPPED pXOverlapped
|
|
)
|
|
{
|
|
return ::XUserReadProfileSettings( dwTitleId, dwUserIndex, dwNumSettingIds, pdwSettingIds, pcbResults, pResults, pXOverlapped );
|
|
}
|
|
|
|
virtual DWORD XUserWriteProfileSettings(
|
|
DWORD dwUserIndex,
|
|
DWORD dwNumSettings,
|
|
const PXUSER_PROFILE_SETTING pSettings,
|
|
PXOVERLAPPED pXOverlapped
|
|
)
|
|
{
|
|
return ::XUserWriteProfileSettings( dwUserIndex, dwNumSettings, pSettings, pXOverlapped );
|
|
}
|
|
|
|
virtual DWORD XUserMuteListQuery(
|
|
DWORD dwUserIndex,
|
|
XUID XuidRemoteTalker,
|
|
BOOL *pfOnMuteList
|
|
)
|
|
{
|
|
return ::XUserMuteListQuery( dwUserIndex, XuidRemoteTalker, pfOnMuteList );
|
|
}
|
|
|
|
IMPLEMENT_OVERLAPPED_SESSION_CALL_3( XSessionWriteStats, XUID, xuid, DWORD, dwNumViews, const XSESSION_VIEW_PROPERTIES *, pViews );
|
|
|
|
IMPLEMENT_OVERLAPPED_SESSION_CALL_0( XSessionFlushStats );
|
|
|
|
virtual DWORD XShowMarketplaceDownloadItemsUI(
|
|
DWORD dwUserIndex,
|
|
DWORD dwEntryPoint,
|
|
CONST ULONGLONG *pOfferIDs,
|
|
DWORD dwOfferIdCount,
|
|
HRESULT *phrResult,
|
|
PXOVERLAPPED pOverlapped
|
|
)
|
|
{
|
|
return ::XShowMarketplaceDownloadItemsUI( dwUserIndex, dwEntryPoint, pOfferIDs, dwOfferIdCount, phrResult, pOverlapped );
|
|
}
|
|
|
|
virtual DWORD XMarketplaceGetDownloadStatus(
|
|
DWORD dwUserIndex,
|
|
ULONGLONG qwOfferID,
|
|
LPDWORD pdwResult
|
|
)
|
|
{
|
|
return ::XMarketplaceGetDownloadStatus( dwUserIndex, qwOfferID, pdwResult );
|
|
}
|
|
|
|
virtual DWORD XShowMarketplaceUI(
|
|
DWORD dwUserIndex,
|
|
DWORD dwEntryPoint,
|
|
ULONGLONG qwOfferID,
|
|
DWORD dwContentCategories
|
|
)
|
|
{
|
|
return ::XShowMarketplaceUI( dwUserIndex, dwEntryPoint, qwOfferID, dwContentCategories );
|
|
}
|
|
|
|
virtual DWORD XShowGameInviteUI(
|
|
DWORD dwUserIndex,
|
|
CONST XUID *pXuidRecipients,
|
|
DWORD cRecipients,
|
|
LPCWSTR wszUnused
|
|
)
|
|
{
|
|
return ::XShowGameInviteUI( dwUserIndex, pXuidRecipients, cRecipients, wszUnused );
|
|
}
|
|
|
|
virtual HRESULT XShowPartyUI(
|
|
DWORD dwUserIndex
|
|
)
|
|
{
|
|
return ::XShowPartyUI( dwUserIndex );
|
|
}
|
|
|
|
virtual DWORD XPartySendGameInvites(
|
|
DWORD dwUserIndex,
|
|
XOVERLAPPED *pOverlapped
|
|
)
|
|
{
|
|
return ::XPartySendGameInvites( dwUserIndex, pOverlapped );
|
|
}
|
|
|
|
virtual HRESULT XShowCommunitySessionsUI(
|
|
DWORD dwUserIndex,
|
|
DWORD dwSocialSessionsFlags
|
|
)
|
|
{
|
|
return ::XShowCommunitySessionsUI( dwUserIndex, dwSocialSessionsFlags );
|
|
}
|
|
|
|
virtual INT XNetXnAddrToMachineId(
|
|
const XNADDR *pxnaddr,
|
|
ULONGLONG *pqwMachineId
|
|
)
|
|
{
|
|
return ::XNetXnAddrToMachineId( pxnaddr, pqwMachineId );
|
|
}
|
|
|
|
virtual DWORD XNetGetEthernetLinkStatus()
|
|
{
|
|
return ::XNetGetEthernetLinkStatus();
|
|
}
|
|
|
|
virtual XONLINE_NAT_TYPE XOnlineGetNatType()
|
|
{
|
|
return ::XOnlineGetNatType();
|
|
}
|
|
|
|
}
|
|
g_XOnline_Impl;
|
|
EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CXOnline_Impl, IXOnline, XONLINE_INTERFACE_VERSION, g_XOnline_Impl );
|
|
|
|
#endif
|
|
|
|
|