341 lines
9.4 KiB
C++
341 lines
9.4 KiB
C++
//========= Copyright © 1996-2009, Valve Corporation, All rights reserved. ============//
|
|
//
|
|
// Purpose:
|
|
//
|
|
//=====================================================================================//
|
|
|
|
#ifndef _X360
|
|
#include "xbox/xboxstubs.h"
|
|
#endif
|
|
|
|
#include "mm_framework.h"
|
|
#include "filesystem.h"
|
|
|
|
// NOTE: This has to be the last file included!
|
|
#include "tier0/memdbgon.h"
|
|
|
|
|
|
|
|
static CDlcManager g_DlcManager;
|
|
CDlcManager *g_pDlcManager = &g_DlcManager;
|
|
|
|
CON_COMMAND( mm_dlc_debugprint, "Shows information about dlc" )
|
|
{
|
|
KeyValuesDumpAsDevMsg( g_pDlcManager->GetDataInfo(), 1 );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
CDlcManager::CDlcManager() :
|
|
m_pDataInfo( NULL ),
|
|
m_eState( STATE_IDLE ),
|
|
m_flTimestamp( 0.0f ),
|
|
m_bNeedToDiscoverAllDlcs( true ),
|
|
m_bNeedToUpdateFileSystem( false )
|
|
{
|
|
#ifdef _X360
|
|
m_hEnumerator = NULL;
|
|
memset( &m_xOverlapped, 0, sizeof( m_xOverlapped ) );
|
|
#endif
|
|
}
|
|
|
|
CDlcManager::~CDlcManager()
|
|
{
|
|
if ( m_pDataInfo )
|
|
m_pDataInfo->deleteThis();
|
|
m_pDataInfo = NULL;
|
|
}
|
|
|
|
void CDlcManager::Update()
|
|
{
|
|
#ifdef _X360
|
|
// Per TCR-126 we don't want to open DLC for users who haven't unlocked the game yet
|
|
IXboxSystem *pXboxSystem = g_pMatchExtensions->GetIXboxSystem();
|
|
if ( !pXboxSystem || !pXboxSystem->IsArcadeTitleUnlocked() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
DWORD ret = 0;
|
|
switch ( m_eState )
|
|
{
|
|
case STATE_XENUMERATE:
|
|
if ( !XHasOverlappedIoCompleted( &m_xOverlapped ) )
|
|
return;
|
|
|
|
ret = XGetOverlappedResult( &m_xOverlapped, ( DWORD * ) &m_dwNumItems, false );
|
|
if ( ret != ERROR_SUCCESS )
|
|
{
|
|
Warning( "DLCMANAGER: XContentCreateEnumerator/XEnumerate async failed with error %d!\n", ret );
|
|
m_dwNumItems = 0;
|
|
m_bNeedToDiscoverAllDlcs = true;
|
|
}
|
|
|
|
CloseHandle( m_hEnumerator );
|
|
m_hEnumerator = NULL;
|
|
|
|
CreateNextContent();
|
|
return;
|
|
|
|
case STATE_XCONTENT_CREATE:
|
|
if ( !XHasOverlappedIoCompleted( &m_xOverlapped ) )
|
|
return;
|
|
|
|
ProcessNextContent();
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
// Once we are idle check if we need to update the file systems list of DLC
|
|
if ( m_eState == STATE_IDLE )
|
|
{
|
|
if ( m_bNeedToUpdateFileSystem )
|
|
{
|
|
m_bNeedToUpdateFileSystem = false;
|
|
g_pFullFileSystem->DiscoverDLC( XBX_GetPrimaryUserId() );
|
|
}
|
|
}
|
|
}
|
|
|
|
void CDlcManager::RequestDlcUpdate()
|
|
{
|
|
if ( m_eState > STATE_IDLE )
|
|
return;
|
|
|
|
if ( m_eState == STATE_IDLE && !m_bNeedToDiscoverAllDlcs )
|
|
{
|
|
Msg( "DLCMANAGER: RequestDlcUpdate has no new content.\n" );
|
|
return;
|
|
}
|
|
|
|
// If we specified dlc via the command line we can skip the actual enumeration
|
|
const char *pCmdLine = CommandLine()->GetCmdLine();
|
|
if ( V_stristr( pCmdLine, "-dlc" ) )
|
|
{
|
|
m_eState = STATE_IDLE;
|
|
m_bNeedToUpdateFileSystem = true;
|
|
m_bNeedToDiscoverAllDlcs = false;
|
|
return;
|
|
}
|
|
|
|
#if !defined( NO_STEAM ) && !defined( SWDS )
|
|
// Client is requesting a DLC update
|
|
m_CallbackOnDLCInstalled.Register( this, &CDlcManager::Steam_OnDLCInstalled );
|
|
Steam_OnDLCInstalled( NULL );
|
|
#endif
|
|
|
|
#ifdef _X360
|
|
if ( XBX_GetPrimaryUserIsGuest() )
|
|
{
|
|
Msg( "DLCMANAGER: RequestDlcUpdate will not update for guests.\n" );
|
|
return;
|
|
}
|
|
|
|
DWORD nBufferSize = 0;
|
|
DWORD ret = XContentCreateEnumerator( XBX_GetPrimaryUserId(), XCONTENTDEVICE_ANY,
|
|
XCONTENTTYPE_MARKETPLACE, 0, 100, &nBufferSize, &m_hEnumerator );
|
|
if ( ret )
|
|
{
|
|
Warning( "DLCMANAGER: XContentCreateEnumerator failed with error %d!\n", ret );
|
|
return;
|
|
}
|
|
|
|
if ( nBufferSize )
|
|
{
|
|
m_dwNumItems = 0;
|
|
m_arrContentData.EnsureCapacity( nBufferSize/sizeof( XCONTENT_DATA ) + 1 );
|
|
m_arrContentData.RemoveAll();
|
|
ret = XEnumerate( m_hEnumerator, m_arrContentData.Base(), nBufferSize, NULL, &m_xOverlapped );
|
|
if ( ret && ret != ERROR_IO_PENDING )
|
|
{
|
|
Warning( "DLCMANAGER: XContentCreateEnumerator/XEnumerate failed with error %d!\n", ret );
|
|
}
|
|
Msg( "DLCMANAGER: XContentCreateEnumerator/XEnumerate initiated.\n" );
|
|
m_eState = STATE_XENUMERATE;
|
|
m_flTimestamp = Plat_FloatTime();
|
|
m_bNeedToDiscoverAllDlcs = false;
|
|
return;
|
|
}
|
|
|
|
Warning( "DLCMANAGER: XContentCreateEnumerator not starting enumeration!\n" );
|
|
::CloseHandle( m_hEnumerator );
|
|
m_hEnumerator = NULL;
|
|
#endif
|
|
}
|
|
|
|
#ifdef _X360
|
|
void CDlcManager::CreateNextContent()
|
|
{
|
|
Msg( "DLCMANAGER: enumeration checkpoint after %.3f sec\n", Plat_FloatTime() - m_flTimestamp );
|
|
while ( m_dwNumItems -- > 0 )
|
|
{
|
|
XCONTENT_DATA *pContentData = m_arrContentData.Base() + m_dwNumItems;
|
|
char chKey[ XCONTENT_MAX_FILENAME_LENGTH + 1 ];
|
|
memcpy( chKey, pContentData->szFileName, XCONTENT_MAX_FILENAME_LENGTH );
|
|
chKey[ XCONTENT_MAX_FILENAME_LENGTH ] = 0;
|
|
|
|
if ( m_pDataInfo->FindKey( chKey ) )
|
|
continue; // Already processed that DLC
|
|
|
|
// Kick off DLC processing
|
|
m_dwLicenseMask = 0;
|
|
DWORD ret = XContentCreate( XBX_GetPrimaryUserId(), "PKG", pContentData, XCONTENTFLAG_OPENEXISTING, NULL, &m_dwLicenseMask, &m_xOverlapped );
|
|
if ( ret && ( ret != ERROR_IO_PENDING ) )
|
|
{
|
|
Warning( "DLCMANAGER: [%.*s] is corrupt\n", ARRAYSIZE( pContentData->szFileName ), pContentData->szFileName );
|
|
continue; // assume corrupt
|
|
}
|
|
|
|
m_eState = STATE_XCONTENT_CREATE;
|
|
return;
|
|
}
|
|
|
|
// All done
|
|
m_eState = STATE_IDLE;
|
|
float flTime = Plat_FloatTime() - m_flTimestamp;
|
|
Msg( "DLCMANAGER: Full update finished after %.3f sec\n", flTime );
|
|
KeyValuesDumpAsDevMsg( m_pDataInfo, 1 );
|
|
}
|
|
|
|
void CDlcManager::ProcessNextContent()
|
|
{
|
|
DWORD dwResult = 0;
|
|
DWORD ret = XGetOverlappedResult( &m_xOverlapped, &dwResult, false );
|
|
if( ret == ERROR_SUCCESS )
|
|
{
|
|
XCONTENT_DATA *pContentData = m_arrContentData.Base() + m_dwNumItems;
|
|
char chKey[ XCONTENT_MAX_FILENAME_LENGTH + 1 ];
|
|
memcpy( chKey, pContentData->szFileName, XCONTENT_MAX_FILENAME_LENGTH );
|
|
chKey[ XCONTENT_MAX_FILENAME_LENGTH ] = 0;
|
|
|
|
Msg( "DLCMANAGER: [%.*s] has license mask = 0x%08X\n", ARRAYSIZE( pContentData->szFileName ), pContentData->szFileName, m_dwLicenseMask );
|
|
|
|
if ( !m_pDataInfo )
|
|
{
|
|
m_pDataInfo = new KeyValues( "DlcManager" );
|
|
m_pDataInfo->SetUint64( "@info/installed", 0 );
|
|
}
|
|
|
|
if ( KeyValues *pDlc = m_pDataInfo->FindKey( chKey, true ) )
|
|
{
|
|
pDlc->SetInt( "licensemask", m_dwLicenseMask );
|
|
pDlc->SetWString( "displayname", pContentData->szDisplayName );
|
|
pDlc->SetInt( "deviceid", pContentData->DeviceID );
|
|
}
|
|
|
|
m_pDataInfo->SetUint64( "@info/installed", m_pDataInfo->GetUint64( "@info/installed" ) | ( 1ull << DLC_LICENSE_ID( m_dwLicenseMask ) ) );
|
|
m_bNeedToUpdateFileSystem = true;
|
|
}
|
|
else
|
|
{
|
|
XCONTENT_DATA *pContentData = m_arrContentData.Base() + m_dwNumItems;
|
|
Warning( "DLCMANAGER: [%.*s] async open failed with error %d\n", ARRAYSIZE( pContentData->szFileName ), pContentData->szFileName, ret );
|
|
}
|
|
XContentClose( "PKG", NULL );
|
|
CreateNextContent();
|
|
}
|
|
#endif
|
|
|
|
bool CDlcManager::IsDlcUpdateFinished( bool bWaitForFinish )
|
|
{
|
|
if ( m_eState == STATE_IDLE )
|
|
return true;
|
|
if ( !bWaitForFinish )
|
|
return false;
|
|
|
|
float flTimestamp = Plat_FloatTime();
|
|
while ( m_eState != STATE_IDLE )
|
|
{
|
|
Update();
|
|
ThreadSleep( 1 );
|
|
}
|
|
float flEndTimestamp = Plat_FloatTime();
|
|
|
|
Warning( "DLCMANAGER: Forcing wait for update to finish stalled for %.3f sec\n", flEndTimestamp - flTimestamp );
|
|
return true;
|
|
}
|
|
|
|
KeyValues * CDlcManager::GetDataInfo()
|
|
{
|
|
return m_pDataInfo;
|
|
}
|
|
|
|
void CDlcManager::OnEvent( KeyValues *kvEvent )
|
|
{
|
|
#ifdef _X360
|
|
char const *szEvent = kvEvent->GetName();
|
|
|
|
if ( !Q_stricmp( "OnDowloadableContentInstalled", szEvent ) )
|
|
{
|
|
m_bNeedToDiscoverAllDlcs = true;
|
|
}
|
|
else if ( !Q_stricmp( "OnLiveMembershipPurchased", szEvent ) )
|
|
{
|
|
m_bNeedToDiscoverAllDlcs = true;
|
|
}
|
|
else if ( !Q_stricmp( "OnSysSigninChange", szEvent ) )
|
|
{
|
|
m_bNeedToDiscoverAllDlcs = true;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#if !defined( NO_STEAM ) && !defined( SWDS )
|
|
void CDlcManager::Steam_OnDLCInstalled( DlcInstalled_t *pParam )
|
|
{
|
|
m_bNeedToDiscoverAllDlcs = false;
|
|
|
|
TitleDlcDescription_t const *dlcs = g_pMatchFramework->GetMatchTitle()->DescribeTitleDlcs();
|
|
if ( !dlcs )
|
|
return;
|
|
|
|
TitleDataFieldsDescription_t const *fields = g_pMatchFramework->GetMatchTitle()->DescribeTitleDataStorage();
|
|
|
|
uint64 uiOldDlcMask = m_pDataInfo->GetUint64( "@info/installed" );
|
|
if ( !m_pDataInfo )
|
|
{
|
|
m_pDataInfo = new KeyValues( "DlcManager" );
|
|
m_pDataInfo->SetUint64( "@info/installed", 0 );
|
|
}
|
|
IPlayerLocal *pPlayerLocal = g_pMatchFramework->GetMatchSystem()->GetPlayerManager()->GetLocalPlayer( XBX_GetPrimaryUserId() );
|
|
for ( ; dlcs->m_uiLicenseMaskId; ++ dlcs )
|
|
{
|
|
// Check if DLC already detected
|
|
if ( ( uiOldDlcMask & dlcs->m_uiLicenseMaskId ) == dlcs->m_uiLicenseMaskId )
|
|
continue;
|
|
|
|
// Check player profile first
|
|
TitleDataFieldsDescription_t const *pDlcField = dlcs->m_szTitleDataBitfieldStatName ?
|
|
TitleDataFieldsDescriptionFindByString( fields, dlcs->m_szTitleDataBitfieldStatName ) : NULL;
|
|
if ( pDlcField && pPlayerLocal &&
|
|
TitleDataFieldsDescriptionGetBit( pDlcField, pPlayerLocal ) )
|
|
{
|
|
m_pDataInfo->SetUint64( "@info/installed", m_pDataInfo->GetUint64( "@info/installed" ) | dlcs->m_uiLicenseMaskId );
|
|
continue;
|
|
}
|
|
|
|
// Check Steam subscription
|
|
if ( steamapicontext->SteamApps()->BIsSubscribedApp( dlcs->m_idDlcAppId ) )
|
|
{
|
|
m_pDataInfo->SetUint64( "@info/installed", m_pDataInfo->GetUint64( "@info/installed" ) | dlcs->m_uiLicenseMaskId );
|
|
|
|
// Set player profile bit
|
|
if ( pDlcField && pPlayerLocal )
|
|
TitleDataFieldsDescriptionSetBit( pDlcField, pPlayerLocal, true );
|
|
}
|
|
}
|
|
|
|
// Send the event in case detected DLC installed changes
|
|
uint64 uiNewDlcMask = m_pDataInfo->GetUint64( "@info/installed" );
|
|
if ( uiNewDlcMask != uiOldDlcMask )
|
|
{
|
|
KeyValues *kvEvent = new KeyValues( "OnDowloadableContentInstalled" );
|
|
kvEvent->SetUint64( "installed", uiNewDlcMask );
|
|
g_pMatchFramework->GetEventsSubscription()->BroadcastEvent( kvEvent );
|
|
m_bNeedToUpdateFileSystem = true;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|