summaryrefslogtreecommitdiff
path: root/utils/xbox/AppChooser/main.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /utils/xbox/AppChooser/main.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'utils/xbox/AppChooser/main.cpp')
-rw-r--r--utils/xbox/AppChooser/main.cpp2239
1 files changed, 2239 insertions, 0 deletions
diff --git a/utils/xbox/AppChooser/main.cpp b/utils/xbox/AppChooser/main.cpp
new file mode 100644
index 0000000..3310ee5
--- /dev/null
+++ b/utils/xbox/AppChooser/main.cpp
@@ -0,0 +1,2239 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: X360 Application Chooser
+//
+//===========================================================================//
+
+#if !defined( _X360 )
+#include <windows.h>
+#endif
+#include "appframework/iappsystemgroup.h"
+#include "appframework/appframework.h"
+#include "tier0/dbg.h"
+#include "tier1/interface.h"
+#include "tier1/KeyValues.h"
+#include "filesystem.h"
+#include "vstdlib/cvar.h"
+#include "filesystem_init.h"
+#include "tier1/utlbuffer.h"
+#include "icommandline.h"
+#include "datacache/idatacache.h"
+#include "datacache/imdlcache.h"
+#include "studio.h"
+#include "utlbuffer.h"
+#include "tier2/utlstreambuffer.h"
+#include "tier2/tier2.h"
+#include "appframework/tier3app.h"
+#include "mathlib/mathlib.h"
+#include "inputsystem/iinputsystem.h"
+#include "vphysics_interface.h"
+#include "istudiorender.h"
+#include "studio.h"
+#include "vgui/IVGui.h"
+#include "VGuiMatSurface/IMatSystemSurface.h"
+#include "matsys_controls/matsyscontrols.h"
+#include "vgui/ILocalize.h"
+#include "vgui_controls/panel.h"
+#include "vgui_controls/Label.h"
+#include "vgui_controls/imagepanel.h"
+#include "vgui_controls/AnimationController.h"
+#include "materialsystem/imaterialsystem.h"
+#include "materialsystem/imesh.h"
+#include "materialsystem/materialsystem_config.h"
+#include "materialsystem/MaterialSystemUtil.h"
+#include "materialsystem/ishaderapi.h"
+#include "materialsystem/itexture.h"
+#include "filesystem/IQueuedLoader.h"
+#include "xwvfile.h"
+#if defined( _X360 )
+#include "hl2orange.spa.h"
+#include "xbox/xbox_console.h"
+#include "xbox/xbox_win32stubs.h"
+#include "xbox/xbox_launch.h"
+#include <xaudio.h>
+#include <xmedia.h>
+#include <xmp.h>
+#endif
+#if !defined( _X360 )
+#include "xbox/xboxstubs.h"
+#endif
+#include "tier0/memdbgon.h"
+
+#define INACTIVITY_TIMEOUT 120
+
+#define MOVIE_PATH "d:\\movies"
+
+bool g_bActive = true;
+
+// user background music can force this to be zero
+static float g_TargetMovieVolume = 1.0f;
+
+extern SpewOutputFunc_t g_DefaultSpewFunc;
+
+typedef void (*MovieEndCallback_t)();
+
+struct game_t
+{
+ const wchar_t *pName;
+ const char *pGameDir;
+ bool bEnabled;
+};
+
+game_t g_Games[] =
+{
+ { L"HALF-LIFE 2", "hl2", true },
+ { L"HALF-LIFE 2:\nEPISODE ONE", "episodic", true },
+ { L" HALF-LIFE 2:\nEPISODE TWO", "ep2", true },
+ { L"PORTAL", "portal", true },
+ { L" TEAM\nFORTRESS 2", "tf", true },
+};
+
+struct StartupMovie_t
+{
+ const char *pMovieName;
+ bool bUserCanSkip;
+};
+
+// played in order on initial startup
+StartupMovie_t g_StartupMovies[] =
+{
+ { "valve_legalese.wmv", false },
+};
+
+const char *g_DemoMovies[] =
+{
+ "demo.wmv",
+ "teaser_l4d.wmv",
+};
+
+class CImage : public vgui::Panel
+{
+ DECLARE_CLASS_SIMPLE( CImage, vgui::Panel );
+public:
+ CImage( vgui::Panel *pParent, const char *pName, const char *pImageName ) : BaseClass( pParent, pName )
+ {
+ m_imageID = vgui::surface()->CreateNewTextureID();
+ vgui::surface()->DrawSetTextureFile( m_imageID, pImageName, false, false );
+ m_Color = Color( 255, 255, 255, 255 );
+ SetPaintBackgroundEnabled( true );
+ m_AspectRatio = 1.0f;
+ m_bLetterbox = false;
+ }
+
+ CImage( vgui::Panel *pParent, const char *pName, IMaterial *pMaterial ) : BaseClass( pParent, pName )
+ {
+ m_imageID = vgui::surface()->CreateNewTextureID();
+ g_pMatSystemSurface->DrawSetTextureMaterial( m_imageID, pMaterial );
+ m_Color = Color( 255, 255, 255, 255 );
+ SetPaintBackgroundEnabled( true );
+ m_AspectRatio = 1.0f;
+ m_bLetterbox = false;
+ }
+
+ void SetAspectRatio( float aspectRatio, bool bLetterbox )
+ {
+ if ( aspectRatio > 1.0f )
+ {
+ m_AspectRatio = aspectRatio;
+ m_bLetterbox = bLetterbox;
+ }
+ }
+
+ void SetColor( int r, int g, int b )
+ {
+ // set rgb only
+ m_Color.SetColor( r, g, b, m_Color.a() );
+ }
+
+ void SetAlpha( int alpha )
+ {
+ // set alpha only
+ alpha = clamp( alpha, 0, 255 );
+ m_Color.SetColor( m_Color.r(), m_Color.g(), m_Color.b(), alpha );
+ }
+
+ int GetAlpha() const
+ {
+ return m_Color.a();
+ }
+
+ virtual void PaintBackground( void )
+ {
+ if ( m_Color.a() != 0 )
+ {
+ int panelWidth, panelHeight;
+ GetSize( panelWidth, panelHeight );
+
+ float s0 = 0.0f;
+ float s1 = 1.0f;
+ int y = 0;
+ if ( m_AspectRatio > 1.0f )
+ {
+ if ( m_bLetterbox )
+ {
+ // horizontal letterbox
+ float adjustedHeight = (float)panelWidth / m_AspectRatio;
+ float bandHeight = ( (float)panelHeight - adjustedHeight ) / 2;
+
+ vgui::surface()->DrawSetColor( Color( 0, 0, 0, m_Color.a() ) );
+ vgui::surface()->DrawFilledRect( 0, 0, panelWidth, bandHeight );
+ vgui::surface()->DrawFilledRect( 0, panelHeight - bandHeight, panelWidth, panelHeight );
+
+ y = bandHeight;
+ panelHeight = adjustedHeight;
+ }
+ else
+ {
+ // hold the panel's height constant, determine the corresponding aspect corrected image width
+ float imageWidth = (float)panelHeight * m_AspectRatio;
+ // adjust the image width as a percentage of the panel's width
+ // scale and center;
+ s1 = (float)panelWidth / imageWidth;
+ s0 = ( 1 - s1 ) / 2.0f;
+ s1 = s0 + s1;
+ }
+ }
+
+ vgui::surface()->DrawSetColor( m_Color );
+ vgui::surface()->DrawSetTexture( m_imageID );
+ vgui::surface()->DrawTexturedSubRect( 0, y, panelWidth, y+panelHeight, s0, 0.0f, s1, 1.0f );
+ }
+ }
+
+ int m_imageID;
+ Color m_Color;
+ float m_AspectRatio;
+ bool m_bLetterbox;
+};
+
+class CMovieImage : public vgui::Panel
+{
+ DECLARE_CLASS_SIMPLE( CMovieImage, vgui::Panel );
+public:
+
+ CMovieImage( vgui::Panel *pParent, const char *pName, const char *pUniqueName, int nDecodeWidth, int nDecodeHeight, float fadeDelay = 1.0f ) : BaseClass( pParent, pName )
+ {
+ // decoupled from actual panel bounds, can be decoded at any power of two resolution
+ m_nDecodeWidth = nDecodeWidth;
+ m_nDecodeHeight = nDecodeHeight;
+
+ char materialName[MAX_PATH];
+ V_snprintf( materialName, sizeof( materialName ), "MovieMaterial_%s.vmt", pUniqueName );
+ char textureName[MAX_PATH];
+ V_snprintf( textureName, sizeof( textureName ), "MovieFrame_%s", pUniqueName );
+
+ // create a texture for use as movie frame grab
+ m_pTexture = g_pMaterialSystem->CreateProceduralTexture(
+ textureName,
+ TEXTURE_GROUP_OTHER,
+ m_nDecodeWidth,
+ m_nDecodeHeight,
+ g_pMaterialSystem->GetBackBufferFormat(),
+ TEXTUREFLAGS_NOMIP | TEXTUREFLAGS_SINGLECOPY );
+
+ KeyValues *pVMTKeyValues = new KeyValues( "screenspace_general" );
+ pVMTKeyValues->SetInt( "$ignorez", 1 );
+ pVMTKeyValues->SetInt( "$linearread_basetexture", 1 );
+ pVMTKeyValues->SetInt( "$X360APPCHOOSER", 1 );
+ pVMTKeyValues->SetString( "$PIXSHADER", "appchooser360movie_ps20b" );
+ pVMTKeyValues->SetString( "$basetexture", textureName );
+
+ m_pMaterial = g_pMaterialSystem->CreateMaterial( materialName, pVMTKeyValues );
+
+ m_imageID = vgui::surface()->CreateNewTextureID();
+ g_pMatSystemSurface->DrawSetTextureMaterial( m_imageID, m_pMaterial );
+
+ SetPaintBackgroundEnabled( true );
+
+ // initially invisible
+ m_Color = Color( 255, 255, 255, 0 );
+
+ m_FadeDelay = fadeDelay;
+ m_pXMVPlayer = NULL;
+ m_bStopping = false;
+ m_StartTime = 0;
+ m_StopTime = 0;
+ m_pMovieEndCallback = NULL;
+ m_bLooped = false;
+ m_AspectRatio = 1.0f;
+ m_bLetterbox = false;
+ }
+
+ void SetAspectRatio( float aspectRatio, bool bLetterbox )
+ {
+ if ( aspectRatio > 1.0f )
+ {
+ m_AspectRatio = aspectRatio;
+ m_bLetterbox = bLetterbox;
+ }
+ }
+
+ void SetColor( int r, int g, int b )
+ {
+ // set rgb only
+ m_Color.SetColor( r, g, b, m_Color.a() );
+ }
+
+ // -1 means never have any audio, otherwise set to current target
+ void InitUserAudioMix( bool bAudio )
+ {
+ m_CurrentVolume = bAudio ? g_TargetMovieVolume : -1;
+ }
+
+ // fade in/out based on user interaction with dashboard music system
+ void UpdateMovieVolume( bool bForce, float frametime = 1.0f )
+ {
+ // m_CurrentVolume < 0 means this movie never plays audio
+ if ( !m_pXMVPlayer || m_CurrentVolume < 0 )
+ return;
+
+ // forced update or new volume, ramp & set
+ if ( bForce || g_TargetMovieVolume != m_CurrentVolume )
+ {
+ if ( bForce )
+ {
+ frametime = 1.0f;
+ }
+
+ m_CurrentVolume = Approach( g_TargetMovieVolume, m_CurrentVolume, frametime * 0.5f );
+
+ // UNDONE: Under what conditions can this fail? If it fails it could cause audible pops
+ IXAudioSourceVoice *pVoice = NULL;
+ HRESULT hr = m_pXMVPlayer->GetSourceVoice( &pVoice );
+ if ( !FAILED( hr ) && pVoice )
+ {
+ pVoice->SetVolume( m_CurrentVolume );
+ }
+ }
+ }
+
+ bool StartMovieFromMemory( const void *pBuffer, int bufferSize, bool bAudio, bool bLoop, MovieEndCallback_t pMovieEndCallback = NULL )
+ {
+ if ( m_pXMVPlayer || m_bStopping )
+ {
+ // already started or currently stopping
+ return false;
+ }
+
+ if ( !pBuffer || !bufferSize )
+ {
+ return false;
+ }
+
+ XMEDIA_XMV_CREATE_PARAMETERS xmvParameters;
+ V_memset( &xmvParameters, 0, sizeof( xmvParameters ) );
+
+ xmvParameters.dwFlags = XMEDIA_CREATE_CPU_AFFINITY;
+ if ( bLoop )
+ {
+ xmvParameters.dwFlags |= XMEDIA_CREATE_FOR_LOOP;
+ m_bLooped = true;
+ }
+ if ( !bAudio )
+ {
+ xmvParameters.dwAudioStreamId = (DWORD)XMEDIA_STREAM_ID_DONT_USE;
+ }
+ InitUserAudioMix(bAudio);
+
+ xmvParameters.dwVideoDecoderCpu = 2;
+ xmvParameters.dwVideoRendererCpu = 2;
+ xmvParameters.dwAudioDecoderCpu = 4;
+ xmvParameters.dwAudioRendererCpu = 4;
+ xmvParameters.createType = XMEDIA_CREATE_FROM_MEMORY;
+ xmvParameters.createFromMemory.pvBuffer = (PVOID)pBuffer;
+ xmvParameters.createFromMemory.dwBufferSize = bufferSize;
+
+ IDirect3DDevice9 *pD3DDevice = (IDirect3DDevice9 *)g_pMaterialSystem->GetD3DDevice();
+ HRESULT hr = XMediaCreateXmvPlayer( pD3DDevice, &xmvParameters, &m_pXMVPlayer );
+ if ( FAILED( hr ) )
+ {
+ return false;
+ }
+
+ m_pMovieEndCallback = pMovieEndCallback;
+
+ RECT rect;
+ rect.left = 0;
+ rect.top = 0;
+ rect.right = m_nDecodeWidth;
+ rect.bottom = m_nDecodeHeight;
+ m_pXMVPlayer->SetRectangle( &rect );
+ UpdateMovieVolume( true );
+
+ SetAlpha( 0 );
+ m_StartTime = Plat_FloatTime() + m_FadeDelay;
+ m_StopTime = 0;
+
+ return true;
+ }
+
+ bool StartMovieFromFile( const char *pMovieName, bool bAudio, bool bLoop, MovieEndCallback_t pMovieEndCallback = NULL )
+ {
+ if ( m_pXMVPlayer || m_bStopping )
+ {
+ // already started or currently stopping
+ return false;
+ }
+
+ XMEDIA_XMV_CREATE_PARAMETERS xmvParameters;
+ V_memset( &xmvParameters, 0, sizeof( xmvParameters ) );
+
+ xmvParameters.dwFlags = XMEDIA_CREATE_CPU_AFFINITY;
+ if ( bLoop )
+ {
+ xmvParameters.dwFlags |= XMEDIA_CREATE_FOR_LOOP;
+ m_bLooped = true;
+ }
+ if ( !bAudio )
+ {
+ xmvParameters.dwAudioStreamId = (DWORD)XMEDIA_STREAM_ID_DONT_USE;
+ }
+ InitUserAudioMix( bAudio );
+
+ char szFilename[MAX_PATH];
+ V_ComposeFileName( MOVIE_PATH, pMovieName, szFilename, sizeof( szFilename ) );
+
+ xmvParameters.dwVideoDecoderCpu = 2;
+ xmvParameters.dwVideoRendererCpu = 2;
+ xmvParameters.dwAudioDecoderCpu = 4;
+ xmvParameters.dwAudioRendererCpu = 4;
+ xmvParameters.createType = XMEDIA_CREATE_FROM_FILE;
+ xmvParameters.createFromFile.szFileName = szFilename;
+
+ IDirect3DDevice9 *pD3DDevice = (IDirect3DDevice9 *)g_pMaterialSystem->GetD3DDevice();
+ HRESULT hr = XMediaCreateXmvPlayer( pD3DDevice, &xmvParameters, &m_pXMVPlayer );
+ if ( FAILED( hr ) )
+ {
+ return false;
+ }
+
+ m_pMovieEndCallback = pMovieEndCallback;
+
+ RECT rect;
+ rect.left = 0;
+ rect.top = 0;
+ rect.right = m_nDecodeWidth;
+ rect.bottom = m_nDecodeHeight;
+ m_pXMVPlayer->SetRectangle( &rect );
+ UpdateMovieVolume( true );
+
+ SetAlpha( 0 );
+ m_StartTime = Plat_FloatTime() + m_FadeDelay;
+ m_StopTime = 0;
+
+ return true;
+ }
+
+ void StopMovie()
+ {
+ if ( !m_pXMVPlayer || m_bStopping )
+ {
+ // already stopped or currently stopping
+ return;
+ }
+
+ m_bLooped = false;
+ m_bStopping = true;
+ m_pXMVPlayer->Stop( XMEDIA_STOP_IMMEDIATE );
+
+ SetAlpha( 255 );
+ m_StartTime = 0;
+ m_StopTime = Plat_FloatTime() + m_FadeDelay;
+ }
+
+ virtual void PaintBackground( void )
+ {
+ if ( m_StartTime )
+ {
+ // fade up goes from [0..1] and holds on 1
+ float t = ( Plat_FloatTime() - m_StartTime ) * 2.0f;
+ t = clamp( t, 0.0f, 1.0f );
+ SetAlpha( t * 255.0f );
+ }
+
+ if ( m_StopTime )
+ {
+ // fade out goes from [1..0] and holds on 0
+ float t = ( Plat_FloatTime() - m_StopTime ) * 2.0f;
+ t = 1.0f - clamp( t, 0.0f, 1.0f );
+ SetAlpha( t * 255.0f );
+ if ( m_Color.a() == 0 )
+ {
+ if ( m_bStopping && m_pMovieEndCallback )
+ {
+ m_pMovieEndCallback();
+ }
+ m_bStopping = false;
+ }
+ }
+
+ if ( m_Color.a() != 0 )
+ {
+ int panelWidth, panelHeight;
+ GetSize( panelWidth, panelHeight );
+
+ float s0 = 0.0f;
+ float s1 = 1.0f;
+ int y = 0;
+ if ( m_AspectRatio > 1.0f )
+ {
+ if ( m_bLetterbox )
+ {
+ // horizontal letterbox
+ float adjustedHeight = (float)panelWidth / m_AspectRatio;
+ float bandHeight = ( (float)panelHeight - adjustedHeight ) / 2;
+
+ vgui::surface()->DrawSetColor( Color( 0, 0, 0, m_Color.a() ) );
+ vgui::surface()->DrawFilledRect( 0, 0, panelWidth, bandHeight );
+ vgui::surface()->DrawFilledRect( 0, panelHeight - bandHeight, panelWidth, panelHeight );
+
+ y = bandHeight;
+ panelHeight = adjustedHeight;
+ }
+ else
+ {
+ // hold the panel's height constant, determine the corresponding aspect corrected image width
+ float imageWidth = (float)panelHeight * m_AspectRatio;
+ // adjust the image width as a percentage of the panel's width
+ // scale and center;
+ s1 = (float)panelWidth / imageWidth;
+ s0 = ( 1 - s1 ) / 2.0f;
+ s1 = s0 + s1;
+ }
+ }
+
+ vgui::surface()->DrawSetColor( m_Color );
+ vgui::surface()->DrawSetTexture( m_imageID );
+ vgui::surface()->DrawTexturedSubRect( 0, y, panelWidth, y+panelHeight, s0, 0.0f, s1, 1.0f );
+ }
+ }
+
+ bool IsFullyReleased()
+ {
+ // fully stopped and released when object no longer exists
+ return ( m_pXMVPlayer == NULL ) && ( m_bStopping == false );
+ }
+
+ bool RenderVideoFrame()
+ {
+ if ( !m_pXMVPlayer )
+ {
+ return false;
+ }
+
+ // If RenderNextFrame does not return S_OK then the frame was not
+ // rendered (perhaps because it was cancelled) so a regular frame
+ // buffer should be rendered before calling present.
+ bool bRenderedFrame = true;
+ HRESULT hr = m_pXMVPlayer->RenderNextFrame( 0, NULL );
+ if ( FAILED( hr ) || hr == XMEDIA_W_EOF )
+ {
+ bRenderedFrame = false;
+
+ if ( !m_bLooped )
+ {
+ // Release the movie object
+ m_pXMVPlayer->Release();
+ m_pXMVPlayer = NULL;
+
+ if ( !m_bStopping )
+ {
+ m_bStopping = true;
+ SetAlpha( 255 );
+ m_StartTime = 0;
+ m_StopTime = Plat_FloatTime() + m_FadeDelay;
+ }
+ }
+ }
+
+ // UNDONE: Need a frametime here. Assume it's 30fps.
+ // NOTE: This is only used to time audio fades, so if it's wrong by 2X it's not
+ // going to be noticeable. Probably fine to ship this.
+ UpdateMovieVolume( false, 1.0f / 30.0f );
+
+ // Reset our cached view of what pixel and vertex shaders are set, because
+ // it is no longer accurate, since XMV will have set their own shaders.
+ // This avoids problems when the shader cache thinks it knows what shader
+ // is set and it is wrong.
+ IDirect3DDevice9 *pD3DDevice = (IDirect3DDevice9 *)g_pMaterialSystem->GetD3DDevice();
+ pD3DDevice->SetVertexShader( NULL );
+ pD3DDevice->SetPixelShader( NULL );
+ pD3DDevice->SetVertexDeclaration( NULL );
+ pD3DDevice->SetRenderState( D3DRS_VIEWPORTENABLE, TRUE );
+
+ if ( bRenderedFrame )
+ {
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+
+ Rect_t rect;
+ rect.x = 0;
+ rect.y = 0;
+ rect.width = m_nDecodeWidth;
+ rect.height = m_nDecodeHeight;
+ pRenderContext->CopyRenderTargetToTextureEx( m_pTexture, 0, &rect );
+ }
+
+ return bRenderedFrame;
+ }
+
+private:
+ void SetAlpha( int alpha )
+ {
+ // set alpha only
+ alpha = clamp( alpha, 0, 255 );
+ m_Color.SetColor( m_Color.r(), m_Color.g(), m_Color.b(), alpha );
+ }
+
+ int m_imageID;
+ Color m_Color;
+ IXMediaXmvPlayer *m_pXMVPlayer;
+ ITexture *m_pTexture;
+ IMaterial *m_pMaterial;
+ bool m_bStopping;
+ bool m_bLooped;
+ float m_StartTime;
+ float m_StopTime;
+ int m_nDecodeWidth;
+ int m_nDecodeHeight;
+ float m_FadeDelay;
+ float m_AspectRatio;
+ float m_CurrentVolume;
+ bool m_bLetterbox;
+ MovieEndCallback_t m_pMovieEndCallback;
+};
+
+class CShadowLabel : public vgui::Label
+{
+ DECLARE_CLASS_SIMPLE( CShadowLabel, vgui::Label );
+public:
+ CShadowLabel( vgui::Panel *pParent, const char *pName, const wchar_t *pText ) : BaseClass( pParent, pName, pText )
+ {
+ }
+
+ virtual void Paint( void )
+ {
+ BaseClass::Paint();
+ BaseClass::Paint();
+ BaseClass::Paint();
+ BaseClass::Paint();
+ BaseClass::Paint();
+ }
+};
+
+class CGamePanel : public vgui::Panel
+{
+ DECLARE_CLASS_SIMPLE( CGamePanel, vgui::Panel );
+public:
+ CGamePanel( vgui::Panel *pParent, const char *pName, game_t *pGame, bool bIsWidescreen, KeyValues *pKVSettings ) : BaseClass( pParent, pName )
+ {
+ m_bEnabled = pGame->bEnabled;
+
+ vgui::HScheme hScheme = vgui::scheme()->GetScheme( "SourceScheme" );
+ vgui::IScheme *pSourceScheme = vgui::scheme()->GetIScheme( hScheme );
+
+ KeyValues *pKV = pKVSettings->FindKey( "GamePanel" );
+ int panelWidth = pKV->GetInt( "wide" );
+ int panelHeight = pKV->GetInt( "tall" );
+ SetSize( panelWidth, panelHeight );
+
+ // thumbnail static image
+ char szFilename[MAX_PATH];
+ V_snprintf( szFilename, sizeof( szFilename ), "vgui/appchooser/%s", pGame->pGameDir );
+ pKV = pKVSettings->FindKey( "GameImage" );
+ int y = pKV->GetInt( "ypos" );
+ int w = pKV->GetInt( "wide" );
+ int h = pKV->GetInt( "tall" );
+ int x = ( panelWidth - w ) / 2;
+ m_pThumbImage = new CImage( this, "GameImage", szFilename );
+ SETUP_PANEL( m_pThumbImage );
+ m_pThumbImage->SetBounds( x, y, w, h );
+ m_pThumbImage->SetAspectRatio( bIsWidescreen ? 1.0f : 1.778f, false );
+ m_pThumbImage->SetVisible( true );
+
+ // thumbnail movie
+ m_pMovieImage = new CMovieImage( this, "Movie", pGame->pGameDir, 256, 256, 0.2f );
+ SETUP_PANEL( m_pMovieImage );
+ m_pMovieImage->SetBounds( x, y, w, h );
+ m_pMovieImage->SetVisible( true );
+ V_snprintf( szFilename, sizeof( szFilename ), "%s\\thumb_%s.wmv", MOVIE_PATH, pGame->pGameDir );
+ if ( !g_pFullFileSystem->ReadFile( szFilename, NULL, m_MovieBuffer ) )
+ {
+ m_MovieBuffer.Purge();
+ }
+
+ // game title shadow
+ pKV = pKVSettings->FindKey( "GameTitle" );
+ y = pKV->GetInt( "ypos" );
+ m_pTitleShadow = new CShadowLabel( this, "GameTitle", pGame->pName );
+ SETUP_PANEL( m_pTitleShadow );
+ m_pTitleShadow->SetVisible( true );
+ m_pTitleShadow->SetFont( pSourceScheme->GetFont( "AppChooserGameTitleFontBlur" ) );
+ m_pTitleShadow->SizeToContents();
+ m_pTitleShadow->SetPos( 0, y );
+ m_pTitleShadow->SetWide( panelWidth );
+ m_pTitleShadow->SetContentAlignment( vgui::Label::a_center );
+ m_pTitleShadow->SetPaintBackgroundEnabled( false );
+ m_pTitleShadow->SetFgColor( Color( 0, 0, 0, 255 ) );
+
+ // game title
+ m_pTitle = new vgui::Label( this, "GameTitle", pGame->pName );
+ SETUP_PANEL( m_pTitle );
+ m_pTitle->SetVisible( true );
+ m_pTitle->SetFont( pSourceScheme->GetFont( "AppChooserGameTitleFont" ) );
+ m_pTitle->SizeToContents();
+ m_pTitle->SetPos( 0, y );
+ m_pTitle->SetWide( panelWidth );
+ m_pTitle->SetContentAlignment( vgui::Label::a_center );
+ m_pTitle->SetPaintBackgroundEnabled( false );
+ m_pTitle->SetFgColor( Color( 255, 255, 255, 255 ) );
+
+ // button bounds
+ vgui::HFont hFont = pSourceScheme->GetFont( "GameUIButtons" );
+ pKV = pKVSettings->FindKey( "GameButton" );
+ y = panelHeight - vgui::surface()->GetFontTall( hFont ) - pKV->GetInt( "ypos" );
+ m_pButtonText = new vgui::Label( this, "GameButton", g_pVGuiLocalize->Find( "#GameUI_Icons_A_BUTTON" ) );
+ SETUP_PANEL( m_pButtonText );
+ m_pButtonText->SetVisible( false );
+ m_pButtonText->SetFont( hFont );
+ m_pButtonText->SizeToContents();
+ m_pButtonText->SetPos( 0, y );
+ m_pButtonText->SetWide( panelWidth );
+ m_pButtonText->SetContentAlignment( vgui::Label::a_center );
+ m_pButtonText->SetPaintBackgroundEnabled( false );
+ m_pButtonText->SetFgColor( Color( 255, 255, 255, 255 ) );
+ }
+
+ ~CGamePanel( void )
+ {
+ }
+
+ void SetSelected( bool bSelected )
+ {
+ m_pButtonText->SetVisible( m_bEnabled ? bSelected : false );
+ if ( bSelected )
+ {
+ SetBgColor( Color( 190, 115, 0, 128 ) );
+ m_pThumbImage->SetColor( 255, 255, 255 );
+ m_pMovieImage->SetColor( 255, 255, 255 );
+ m_pTitle->SetFgColor( Color( 255, 255, 255, 255 ) );
+ }
+ else
+ {
+ SetBgColor( Color( 160, 160, 160, 50 ) );
+ m_pThumbImage->SetColor( 100, 100, 100 );
+ m_pMovieImage->SetColor( 120, 120, 120 );
+ m_pTitle->SetFgColor( Color( 140, 140, 140, 255 ) );
+ }
+ }
+
+ void StartMovie()
+ {
+ m_pMovieImage->StartMovieFromMemory( m_MovieBuffer.Base(), m_MovieBuffer.TellMaxPut(), true, true );
+ }
+
+ void StopMovie()
+ {
+ m_pMovieImage->StopMovie();
+ }
+
+ CImage *m_pThumbImage;
+ CMovieImage *m_pMovieImage;
+ CShadowLabel *m_pTitleShadow;
+ vgui::Label *m_pTitle;
+ vgui::Label *m_pButtonText;
+ CUtlBuffer m_MovieBuffer;
+ bool m_bEnabled;
+ int m_imageID;
+};
+
+//-----------------------------------------------------------------------------
+// Simple XAudio wrapper to instance a sound. Provides lightweight audio feedback for ui.
+// Instance this per sound.
+//-----------------------------------------------------------------------------
+class CXSound
+{
+public:
+ CXSound()
+ {
+ for ( int i=0; i<ARRAYSIZE( m_pSourceVoices ); i++ )
+ {
+ m_pSourceVoices[i] = NULL;
+ }
+ m_pXMAData = NULL;
+ m_nXMADataSize = 0;
+ m_numVoices = 0;
+ }
+
+ ~CXSound()
+ {
+ Stop();
+ Release();
+ }
+
+ //-----------------------------------------------------------------------------
+ // Setup the wave, caller can clamp the number of simultaneous playing instances
+ // of this sound. Useful for ui clicks to keep up with rapid input.
+ //-----------------------------------------------------------------------------
+ bool SetupWave( const char *pFilename, int numSimultaneous = 1 )
+ {
+ CUtlBuffer buf;
+ if ( !g_pFullFileSystem->ReadFile( pFilename, "GAME", buf ) )
+ {
+ Msg( "SetupWave: File '%s' not found\n", pFilename );
+ return false;
+ }
+
+ // only supporting xwv format
+ xwvHeader_t* pHeader = (xwvHeader_t *)buf.Base();
+ if ( pHeader->id != XWV_ID || pHeader->version != XWV_VERSION || pHeader->format != XWV_FORMAT_XMA )
+ {
+ Msg( "SetupWave: File '%s' has bad format\n", pFilename );
+ return false;
+ }
+
+ XAUDIOSOURCEVOICEINIT SourceVoiceInit = { 0 };
+ SourceVoiceInit.Format.SampleType = XAUDIOSAMPLETYPE_XMA;
+ SourceVoiceInit.Format.NumStreams = 1;
+ SourceVoiceInit.MaxPacketCount = 1;
+ SourceVoiceInit.Format.Stream[0].SampleRate = pHeader->GetSampleRate();
+ SourceVoiceInit.Format.Stream[0].ChannelCount = pHeader->channels;
+
+ // create enough source voices to support simultaneous play of this sound
+ HRESULT hr;
+ numSimultaneous = min( numSimultaneous, ARRAYSIZE( m_pSourceVoices ) );
+ for ( int i=0; i<numSimultaneous; i++ )
+ {
+ // create the voice
+ hr = XAudioCreateSourceVoice( &SourceVoiceInit, &m_pSourceVoices[i] );
+ if ( FAILED( hr ) )
+ {
+ return false;
+ }
+ }
+ m_numVoices = numSimultaneous;
+
+ // get the xma data
+ m_nXMADataSize = pHeader->dataSize;
+ m_pXMAData = (unsigned char *)XPhysicalAlloc( m_nXMADataSize, MAXULONG_PTR, 0, PAGE_READWRITE );
+ V_memcpy( m_pXMAData, (unsigned char *)buf.Base() + pHeader->dataOffset, m_nXMADataSize );
+
+ return true;
+ }
+
+ bool IsPlaying()
+ {
+ XAUDIOSOURCESTATE SourceState;
+ for ( int i=0; i<m_numVoices; i++ )
+ {
+ m_pSourceVoices[i]->GetVoiceState( &SourceState );
+ if ( SourceState & XAUDIOSOURCESTATE_STARTED )
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void Play()
+ {
+ int numPlaying = 0;
+ XAUDIOSOURCESTATE SourceState;
+ for ( int i=0; i<m_numVoices; i++ )
+ {
+ m_pSourceVoices[i]->GetVoiceState( &SourceState );
+ if ( SourceState & XAUDIOSOURCESTATE_STARTED )
+ {
+ numPlaying++;
+ }
+ }
+ if ( numPlaying >= m_numVoices )
+ {
+ return;
+ }
+
+ // find a free voice
+ IXAudioSourceVoice *pVoice = NULL;
+ for ( int i=0; i<m_numVoices; i++ )
+ {
+ m_pSourceVoices[i]->GetVoiceState( &SourceState );
+ if ( !( SourceState & XAUDIOSOURCESTATE_STARTED ) )
+ {
+ // use the free voice
+ pVoice = m_pSourceVoices[i];
+ break;
+ }
+ }
+ if ( !pVoice )
+ {
+ // none free
+ return;
+ }
+
+ // Set up packet
+ XAUDIOPACKET Packet = { 0 };
+ Packet.pBuffer = m_pXMAData;
+ Packet.BufferSize = m_nXMADataSize;
+
+ // Submit packet
+ HRESULT hr = pVoice->SubmitPacket( &Packet, XAUDIOSUBMITPACKET_DISCONTINUITY );
+ if ( !FAILED( hr ) )
+ {
+ pVoice->Start( 0 );
+ }
+ }
+
+ void Stop()
+ {
+ for ( int i=0; i<m_numVoices; i++ )
+ {
+ m_pSourceVoices[i]->Stop( 0 );
+ }
+ }
+
+ void Release()
+ {
+ for ( int i=0; i<ARRAYSIZE( m_pSourceVoices ); i++ )
+ {
+ if ( m_pSourceVoices[i] )
+ {
+ m_pSourceVoices[i]->Release();
+ m_pSourceVoices[i] = NULL;
+ }
+ }
+ if ( m_pXMAData )
+ {
+ XPhysicalFree( m_pXMAData );
+ m_pXMAData = NULL;
+ }
+ m_nXMADataSize = 0;
+ m_numVoices = 0;
+ }
+
+private:
+ IXAudioSourceVoice *m_pSourceVoices[4];
+ unsigned char *m_pXMAData;
+ int m_nXMADataSize;
+ int m_numVoices;
+};
+
+//-----------------------------------------------------------------------------
+// The application object
+//-----------------------------------------------------------------------------
+class CAppChooser : public CVguiSteamApp
+{
+ typedef CVguiSteamApp BaseClass;
+
+public:
+ virtual bool Create();
+ virtual bool PreInit();
+ virtual int Main();
+ virtual void Destroy();
+
+ void OnSelectionPrevious();
+ void OnSelectionNext();
+ void OnActivateGame();
+ void OnInactivityTimeout();
+ void OnStopDemoMovie();
+ void OnDemoMovieEnd();
+ void OnStopStartupMovie();
+ void Reset();
+ void ResetTimeout( float timeout );
+ bool IsInputEnabled() { return m_bInputEnabled; }
+ bool IsDemoMoviePlaying() { return m_bPlayingDemoMovie; }
+ bool IsStartupMoviePlaying() { return m_bPlayingStartupMovies; }
+ void HandleInvite( DWORD nUserId );
+
+private:
+ const char *GetAppName() { return "AppChooser"; }
+
+ bool CreateWindow( int width, int height, bool fullscreen );
+ bool InitMaterialSystem();
+ bool InitVGUI();
+ void ShutdownVGUI();
+ bool InitAudio();
+ void ShutdownAudio();
+ void FrameTick();
+ void RenderScene();
+ void ExitChooser();
+ void EnableInput( bool bEnable );
+ void StartExitingProcess();
+ void SetLoadingIconPosition( bool bIsMultiplayer );
+
+ int m_nScreenWidth;
+ int m_nScreenHeight;
+ HWND m_hWnd;
+ float m_FadeInTime;
+ float m_FadeOutTime;
+ float m_StartTime;
+ float m_Timeout;
+ int m_Selection;
+ int m_LastSelection;
+ bool m_bInputEnabled;
+ int m_ExitingFrameCount;
+ float m_GameMovieStartTime;
+ float m_BackgroundMovieStartTime;
+ int m_LastBackgroundMovie;
+ int m_StartupMovie;
+
+ // various operating states
+ bool m_bPlayingStartupMovies;
+ bool m_bPlayingDemoMovie;
+ bool m_bExiting;
+
+ // Live invite handling
+ bool m_bInviteAccepted;
+ XNKID m_InviteSessionID;
+ DWORD m_nInviteUserID;
+
+ int m_iImageID;
+ vgui::Panel *m_pRootPanel;
+ CImage *m_pPersistedImage;
+ CImage *m_pOverlay;
+ CImage *m_pFullBlack;
+ CMovieImage *m_pStartupMovieImage;
+ CMovieImage *m_pBackgroundMovie;
+ CMovieImage *m_pDemoMovieImage;
+ CGamePanel *m_pGames[ARRAYSIZE( g_Games )];
+ CImage *m_pProductTitle;
+ CShadowLabel *m_pInstructionsShadow;
+ vgui::Label *m_pInstructions;
+ vgui::Label *m_pLoading;
+ CImage *m_pLoadingIcon;
+ CImage *m_pProductBackgrounds[ARRAYSIZE( g_Games )];
+ DWORD m_iStorageDeviceID;
+ DWORD m_iUserIdx;
+ KeyValues *m_pKVSettings;
+ int m_DemoMovie;
+
+ CXSound m_Click;
+ CXSound m_Clack;
+ CXSound m_Deny;
+};
+
+CAppChooser g_AppChooserSystem;
+
+//--------------------------------------------------------------------------------------
+// InitMaterialSystem
+//
+//--------------------------------------------------------------------------------------
+bool CAppChooser::InitMaterialSystem()
+{
+ RECT rect;
+
+ MaterialSystem_Config_t config;
+ config.SetFlag( MATSYS_VIDCFG_FLAGS_WINDOWED, IsPC() ? true : false );
+ config.SetFlag( MATSYS_VIDCFG_FLAGS_NO_WAIT_FOR_VSYNC, 0 );
+
+ config.m_VideoMode.m_Width = 0;
+ config.m_VideoMode.m_Height = 0;
+ config.m_VideoMode.m_Format = IMAGE_FORMAT_BGRX8888;
+ config.m_VideoMode.m_RefreshRate = 0;
+ config.dxSupportLevel = IsX360() ? 98 : 0;
+
+ g_pMaterialSystem->ModInit();
+ bool modeSet = g_pMaterialSystem->SetMode( m_hWnd, config );
+ if ( !modeSet )
+ {
+ Error( "Failed to set mode\n" );
+ return false;
+ }
+
+ g_pMaterialSystem->OverrideConfig( config, false );
+
+ GetClientRect( m_hWnd, &rect );
+ m_nScreenWidth = rect.right;
+ m_nScreenHeight = rect.bottom;
+
+ return true;
+}
+
+void CAppChooser::SetLoadingIconPosition( bool bIsMultiplayer )
+{
+ // matches config from matsys_interface.cpp::InitStartupScreen()
+ float flNormalizedX;
+ float flNormalizedY;
+ float flNormalizedSize;
+ if ( !bIsMultiplayer )
+ {
+ flNormalizedX = 0.5f;
+ flNormalizedY = 0.86f;
+ flNormalizedSize = 0.1f;
+ }
+ else
+ {
+ flNormalizedX = 0.5f;
+ flNormalizedY = 0.9f;
+ flNormalizedSize = 0.1f;
+ }
+
+ // matches calcs from CShaderDeviceDx8::RefreshFrontBufferNonInteractive()
+ float flXPos = flNormalizedX;
+ float flYPos = flNormalizedY;
+ float flHeight = flNormalizedSize;
+ int nSize = m_nScreenHeight * flHeight;
+ int x = m_nScreenWidth * flXPos - nSize * 0.5f;
+ int y = m_nScreenHeight * flYPos - nSize * 0.5f;
+ int w = nSize;
+ int h = nSize;
+ m_pLoadingIcon->SetBounds( x, y, w, h );
+}
+
+//-----------------------------------------------------------------------------
+// Setup all our VGUI info
+//-----------------------------------------------------------------------------
+
+bool CAppChooser::InitVGUI( void )
+{
+ int x, y, w, h, g;
+ KeyValues *pKV;
+
+ vgui::surface()->GetScreenSize( w, h );
+ float aspectRatio = (float)w/(float)h;
+ bool bIsWidescreen = ( aspectRatio >= 1.7f );
+ bool bIsHiDef = h > 480;
+
+ const char *pResolutionKey = "";
+ if ( bIsWidescreen )
+ {
+ // 16:9 aspect
+ if ( bIsHiDef )
+ {
+ pResolutionKey = "_hidef";
+ }
+ else
+ {
+ pResolutionKey = "_lodef_wide";
+ }
+ }
+ else
+ {
+ // 4:3 apsect
+ if ( bIsHiDef )
+ {
+ pResolutionKey = "_hidef_norm";
+ }
+ else
+ {
+ pResolutionKey = "_lodef";
+ }
+ }
+
+ // Start vgui
+ vgui::ivgui()->Start();
+ vgui::ivgui()->SetSleep( false );
+
+ // load the scheme
+ vgui::scheme()->LoadSchemeFromFile( "resource/sourcescheme.res", NULL );
+ vgui::HScheme hScheme = vgui::scheme()->GetScheme( "SourceScheme" );
+ vgui::IScheme *pSourceScheme = vgui::scheme()->GetIScheme( hScheme );
+
+ m_pKVSettings = new KeyValues( "AppChooser.res" );
+ if ( m_pKVSettings->LoadFromFile( g_pFullFileSystem, "resource/UI/AppChooser.res", "GAME" ) )
+ {
+ m_pKVSettings->ProcessResolutionKeys( pResolutionKey );
+ }
+ else
+ {
+ return false;
+ }
+
+ // localization
+ g_pVGuiLocalize->AddFile( "resource/gameui_%language%.txt" ,"GAME", true );
+
+ // Init the root panel
+ m_pRootPanel = new vgui::Panel( NULL, "RootPanel" );
+ m_pRootPanel->SetBounds( 0, 0, m_nScreenWidth, m_nScreenHeight );
+ m_pRootPanel->SetPaintBackgroundEnabled( true );
+ m_pRootPanel->SetVisible( true );
+ vgui::surface()->SetEmbeddedPanel( m_pRootPanel->GetVPanel() );
+
+ // need a full pure opaque black before all panel drawing
+ // this fixes the top of frame xmv video render work
+ m_pFullBlack = new CImage( m_pRootPanel, "FullBlack", "vgui/black" );
+ SETUP_PANEL( m_pFullBlack );
+ m_pFullBlack->SetBounds( 0, 0, m_nScreenWidth, m_nScreenHeight );
+ m_pFullBlack->SetVisible( true );
+
+ // all the background movies were authored for widescreen
+ m_pBackgroundMovie = new CMovieImage( m_pRootPanel, "Movie", "Background", m_nScreenWidth, m_nScreenHeight, 0.0f );
+ SETUP_PANEL( m_pBackgroundMovie );
+ m_pBackgroundMovie->SetBounds( 0, 0, m_nScreenWidth, m_nScreenHeight );
+ m_pBackgroundMovie->SetAspectRatio( bIsWidescreen ? 1.0f : 1.778f, false );
+ m_pBackgroundMovie->SetColor( 255, 255, 255 );
+ m_pBackgroundMovie->SetVisible( true );
+
+ pKV = m_pKVSettings->FindKey( "Logo" );
+ y = pKV->GetInt( "ypos" );
+ w = pKV->GetInt( "wide" );
+ h = pKV->GetInt( "tall" );
+ m_pProductTitle = new CImage( m_pRootPanel, "Logo", "vgui/appchooser/orangeboxlogo" );
+ SETUP_PANEL( m_pProductTitle );
+ m_pProductTitle->SetBounds( ( m_nScreenWidth - w )/2, y, w, h );
+ m_pProductTitle->SetVisible( true );
+
+ wchar_t *pString = g_pVGuiLocalize->Find( "#GameUI_AppChooser_SelectGame" );
+ pKV = m_pKVSettings->FindKey( "SelectGame" );
+ y = pKV->GetInt( "ypos" );
+ m_pInstructionsShadow = new CShadowLabel( m_pRootPanel, "SelectGame", pString );
+ SETUP_PANEL( m_pInstructionsShadow );
+ m_pInstructionsShadow->SetFont( pSourceScheme->GetFont( "ChapterTitleBlur" ) );
+ m_pInstructionsShadow->SizeToContents();
+ m_pInstructionsShadow->SetWide( m_nScreenWidth );
+ m_pInstructionsShadow->SetPos( 0, y );
+ m_pInstructionsShadow->SetContentAlignment( vgui::Label::a_center );
+ m_pInstructionsShadow->SetPaintBackgroundEnabled( false );
+ m_pInstructionsShadow->SetFgColor( Color( 0, 0, 0, 255) );
+
+ m_pInstructions = new vgui::Label( m_pRootPanel, "SelectGame", pString );
+ SETUP_PANEL( m_pInstructions );
+ m_pInstructions->SetFont( pSourceScheme->GetFont( "ChapterTitle" ) );
+ m_pInstructions->SizeToContents();
+ m_pInstructions->SetWide( m_nScreenWidth );
+ m_pInstructions->SetPos( 0, y );
+ m_pInstructions->SetContentAlignment( vgui::Label::a_center );
+ m_pInstructions->SetPaintBackgroundEnabled( false );
+ m_pInstructions->SetFgColor( Color( 255, 255, 255, 255) );
+
+ pKV = m_pKVSettings->FindKey( "GamePanel" );
+ w = pKV->GetInt( "wide" );
+ g = pKV->GetInt( "gap" );
+ x = ( m_nScreenWidth - ( (int)( ARRAYSIZE( g_Games ) ) * ( w + g ) - g ) ) / 2;
+ y = pKV->GetInt( "ypos" );
+ for ( int i=0; i<ARRAYSIZE( g_Games ); i++ )
+ {
+ m_pGames[i] = new CGamePanel( m_pRootPanel, "GamePanel", &g_Games[i], bIsWidescreen, m_pKVSettings );
+ SETUP_PANEL( m_pGames[i] );
+ m_pGames[i]->SetPos( x, y );
+ m_pGames[i]->SetPaintBackgroundType( 2 );
+ m_pGames[i]->SetSelected( false );
+ x += w + g;
+ }
+
+ // the product backgrounds are topmost and used as fade out materials
+ for ( int i=0; i<ARRAYSIZE( g_Games ); i++ )
+ {
+ char filename[MAX_PATH];
+ V_snprintf( filename, sizeof( filename ), "vgui/appchooser/background_%s%s", g_Games[i].pGameDir, bIsWidescreen ? "_widescreen" : "" );
+ m_pProductBackgrounds[i] = new CImage( m_pRootPanel, "Products", filename );
+ SETUP_PANEL( m_pProductBackgrounds[i] );
+ m_pProductBackgrounds[i]->SetBounds( 0, 0, m_nScreenWidth, m_nScreenHeight );
+ m_pProductBackgrounds[i]->SetVisible( true );
+ m_pProductBackgrounds[i]->SetAlpha( 0 );
+ }
+
+ pString = g_pVGuiLocalize->Find( "#GameUI_Loading" );
+ y = m_nScreenHeight / 2;
+ m_pLoading = new vgui::Label( m_pRootPanel, "Text", pString );
+ SETUP_PANEL( m_pLoading );
+ m_pLoading->SetFont( pSourceScheme->GetFont( "ChapterTitle" ) );
+ m_pLoading->SizeToContents();
+ m_pLoading->SetWide( m_nScreenWidth );
+ m_pLoading->SetPos( 0, y );
+ m_pLoading->SetContentAlignment( vgui::Label::a_center );
+ m_pLoading->SetPaintBackgroundEnabled( false );
+ m_pLoading->SetFgColor( Color( 255, 255, 255, 255) );
+ m_pLoading->SetVisible( false );
+
+ m_pLoadingIcon = new CImage( m_pRootPanel, "LoadingIcon", "vgui/appchooser/loading_icon" );
+ SETUP_PANEL( m_pLoadingIcon );
+ SetLoadingIconPosition( false );
+ m_pLoadingIcon->SetVisible( false );
+ m_pLoadingIcon->SetAlpha( 0 );
+
+ // create back buffer cloned texture
+ ITexture *pTexture = NULL;
+ if ( XboxLaunch()->GetLaunchFlags() & LF_INTERNALLAUNCH )
+ {
+ pTexture = g_pMaterialSystem->CreateProceduralTexture(
+ "PersistedTexture",
+ TEXTURE_GROUP_OTHER,
+ m_nScreenWidth,
+ m_nScreenHeight,
+ g_pMaterialSystem->GetBackBufferFormat(),
+ TEXTUREFLAGS_NOMIP | TEXTUREFLAGS_SINGLECOPY );
+
+ // the persisted texture is in the back buffer, get it
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ pRenderContext->CopyRenderTargetToTexture( pTexture );
+ }
+
+ // create a material to bind the persisted texture
+ // the fade-in material is topmost, fully opaque, and fades to transluscent on initial rendering
+ KeyValues *pVMTKeyValues = new KeyValues( "UnlitGeneric" );
+ pVMTKeyValues->SetInt( "$vertexcolor", 1 );
+ pVMTKeyValues->SetInt( "$vertexalpha", 1 );
+ pVMTKeyValues->SetInt( "$linearwrite", 1 ); // These 2 lines are needed so that we don't lose bits of precision in
+ pVMTKeyValues->SetInt( "$gammacolorread", 1 ); // the dark colors due to the 360's lossy sRGB read hardware
+ pVMTKeyValues->SetInt( "$ignorez", 1 );
+ if ( pTexture )
+ {
+ pVMTKeyValues->SetString( "$basetexture", "PersistedTexture" );
+ }
+ else
+ {
+ // there is no persisted texture, fade in from black
+ pVMTKeyValues->SetString( "$basetexture", "vgui/black" );
+ }
+ IMaterial *pFadeInMaterial = g_pMaterialSystem->CreateMaterial( "__FadeInMaterial.vmt", pVMTKeyValues );
+
+ // the persisted image is either the image from the relaunch or black during first boot
+ m_pPersistedImage = new CImage( m_pRootPanel, "FadeInOverlay", pFadeInMaterial );
+ SETUP_PANEL( m_pPersistedImage );
+ m_pPersistedImage->SetBounds( 0, 0, m_nScreenWidth, m_nScreenHeight );
+ m_pPersistedImage->SetVisible( true );
+ m_pPersistedImage->SetAlpha( 255 );
+ m_pOverlay = m_pPersistedImage;
+
+ // full screen demo movie, use letterboxing
+ m_pDemoMovieImage = new CMovieImage( m_pRootPanel, "Movie", "Demo", m_nScreenWidth, m_nScreenHeight, 2.0f );
+ SETUP_PANEL( m_pDemoMovieImage );
+ m_pDemoMovieImage->SetBounds( 0, 0, m_nScreenWidth, m_nScreenHeight );
+ m_pDemoMovieImage->SetAspectRatio( bIsWidescreen ? 1.0f : 1.778f, true );
+ m_pDemoMovieImage->SetVisible( true );
+
+ // full screen startup movies, use letterboxing, instant start
+ m_pStartupMovieImage = new CMovieImage( m_pRootPanel, "Movie", "Startup", m_nScreenWidth, m_nScreenHeight, 0.0f );
+ SETUP_PANEL( m_pStartupMovieImage );
+ m_pStartupMovieImage->SetBounds( 0, 0, m_nScreenWidth, m_nScreenHeight );
+ m_pStartupMovieImage->SetAspectRatio( bIsWidescreen ? 1.0f : 1.778f, true );
+ m_pStartupMovieImage->SetVisible( true );
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Stop VGUI
+//-----------------------------------------------------------------------------
+void CAppChooser::ShutdownVGUI( void )
+{
+ delete m_pRootPanel;
+}
+
+//-----------------------------------------------------------------------------
+// Initialize Audio
+//-----------------------------------------------------------------------------
+bool CAppChooser::InitAudio()
+{
+ // Set up initialize parameters of XAudio Engine
+ // Both threads on core 2
+ XAUDIOENGINEINIT EngineInit = { 0 };
+ EngineInit.pEffectTable = &XAudioDefaultEffectTable;
+ EngineInit.ThreadUsage = XAUDIOTHREADUSAGE_THREAD4 | XAUDIOTHREADUSAGE_THREAD5;
+
+ // Initialize the XAudio Engine
+ HRESULT hr = XAudioInitialize( &EngineInit );
+ if ( FAILED( hr ) )
+ {
+ Error( "Error calling XAudioInitialize\n" );
+ }
+
+ m_Click.SetupWave( "sound/ui/buttonclick.360.wav", 2 );
+ m_Clack.SetupWave( "sound/ui/buttonclickrelease.360.wav", 2 );
+ m_Deny.SetupWave( "sound/player/suit_denydevice.360.wav", 2 );
+
+ return true;
+}
+
+void CAppChooser::ShutdownAudio()
+{
+ // Shut down and free XAudio resources
+ XAudioShutDown();
+}
+
+//--------------------------------------------------------------------------------------
+// Reset timeout
+//--------------------------------------------------------------------------------------
+void CAppChooser::ResetTimeout( float timeout )
+{
+ if ( timeout > 0 )
+ {
+ m_Timeout = Plat_FloatTime() + timeout;
+ }
+ else
+ {
+ m_Timeout = 0;
+ }
+}
+
+//--------------------------------------------------------------------------------------
+// Intialization Reset. Expected to be called once after inits.
+//--------------------------------------------------------------------------------------
+void CAppChooser::Reset()
+{
+ // set selection to previous game or default
+ m_Selection = 0;
+ m_LastSelection = 0;
+ const char *pGameName = CommandLine()->ParmValue( "-game", "ep2" );
+ for ( int i=0; i<ARRAYSIZE( g_Games ); i++ )
+ {
+ if ( V_stristr( pGameName, g_Games[i].pGameDir ) )
+ {
+ m_Selection = i;
+ break;
+ }
+ }
+
+ // get the storage device
+ m_iStorageDeviceID = XboxLaunch()->GetStorageID();
+ Msg( "Storage ID: %d", m_iStorageDeviceID );
+
+ // Set the user index
+ m_iUserIdx = XboxLaunch()->GetUserID();
+ if ( g_pInputSystem )
+ {
+ g_pInputSystem->SetPrimaryUserId( m_iUserIdx );
+ }
+ XBX_SetPrimaryUserId( m_iUserIdx );
+ Msg( "User ID: %d", m_iUserIdx );
+
+ m_StartTime = Plat_FloatTime();
+ m_FadeInTime = 0;
+ m_FadeOutTime = 0;
+
+ m_bExiting = false;
+ m_ExitingFrameCount = 0;
+
+ m_bPlayingDemoMovie = false;
+ m_DemoMovie = 0;
+
+ // background movie and selection startup in sync
+ m_LastBackgroundMovie = m_Selection;
+ m_BackgroundMovieStartTime = 0;
+ m_GameMovieStartTime = 0;
+
+ EnableInput( true );
+
+ if ( !( XboxLaunch()->GetLaunchFlags() & LF_INTERNALLAUNCH ) )
+ {
+ // first time boot
+ // startup movies play first, and never again
+ ResetTimeout( 0 );
+
+ m_StartupMovie = -1;
+ m_bPlayingStartupMovies = true;
+ }
+ else
+ {
+ // normal background startup
+ ResetTimeout( INACTIVITY_TIMEOUT );
+
+ // foreground movie starts a little staggered
+ m_BackgroundMovieStartTime = Plat_FloatTime();
+ m_GameMovieStartTime = m_BackgroundMovieStartTime + 1.0f;
+ }
+
+ // Init our invite data
+ m_bInviteAccepted = false;
+ m_nInviteUserID = XBX_INVALID_USER_ID;
+ memset( (void *)&m_InviteSessionID, 0, sizeof( m_InviteSessionID ) );
+}
+
+//--------------------------------------------------------------------------------------
+// Handle previous selection
+//--------------------------------------------------------------------------------------
+void CAppChooser::OnSelectionPrevious()
+{
+ // backward wraparound
+ m_Selection = ( m_Selection + ARRAYSIZE( g_Games ) - 1 ) % ARRAYSIZE( g_Games );
+ m_Click.Play();
+}
+
+//--------------------------------------------------------------------------------------
+// Handle next selection
+//--------------------------------------------------------------------------------------
+void CAppChooser::OnSelectionNext()
+{
+ // forward wraparound
+ m_Selection = ( m_Selection + ARRAYSIZE( g_Games ) + 1 ) % ARRAYSIZE( g_Games );
+ m_Click.Play();
+}
+
+void CAppChooser::EnableInput( bool bEnable )
+{
+ m_bInputEnabled = bEnable;
+}
+
+//--------------------------------------------------------------------------------------
+// Chooser exits
+//--------------------------------------------------------------------------------------
+void CAppChooser::ExitChooser()
+{
+ // reset stale arguments that encode prior game
+ // launcher will establish correct arguments based on desired game
+ CommandLine()->RemoveParm( "-game" );
+ CommandLine()->AppendParm( "-game", g_Games[m_Selection].pGameDir );
+
+ // Special command line parameter for tf. Command line args persist across
+ // relaunches, so remove it first in case we came from tf and are going to another game.
+ CommandLine()->RemoveParm( "-swapcores" );
+ if ( !Q_stricmp( g_Games[m_Selection].pGameDir, "tf" ) )
+ {
+ CommandLine()->AppendParm( "-swapcores", NULL );
+ }
+
+ int fFlags = LF_EXITFROMCHOOSER;
+
+ // allocate the full payload
+ int nPayloadSize = XboxLaunch()->MaxPayloadSize();
+ byte *pPayload = (byte *)stackalloc( nPayloadSize );
+ V_memset( pPayload, 0, nPayloadSize );
+
+ // payload is at least the command line
+ // any user data needed must be placed AFTER the command line
+ const char *pCmdLine = CommandLine()->GetCmdLine();
+ int nCmdLineLength = (int)strlen( pCmdLine ) + 1;
+ V_memcpy( pPayload, pCmdLine, min( nPayloadSize, nCmdLineLength ) );
+
+ // add any other data here to payload, after the command line
+ // ...
+
+ XboxLaunch()->SetStorageID( m_iStorageDeviceID );
+
+ m_iUserIdx = XBX_GetPrimaryUserId();
+ if ( m_bInviteAccepted )
+ {
+ // A potentially different user was invited, so we need to connect them
+ m_iUserIdx = m_nInviteUserID;
+ }
+ XboxLaunch()->SetUserID( m_iUserIdx );
+
+ if ( m_bInviteAccepted )
+ {
+ // In the case of an invitation acceptance, we need to pack extra data into the payload
+ fFlags |= LF_INVITERESTART;
+ XboxLaunch()->SetInviteSessionID( &m_InviteSessionID );
+ }
+
+ // Save out the data
+ bool bLaunch = XboxLaunch()->SetLaunchData( (void *)pPayload, nPayloadSize, fFlags );
+ if ( bLaunch )
+ {
+ COM_TimestampedLog( "Launching: \"%s\" Flags: 0x%8.8x", pCmdLine, XboxLaunch()->GetLaunchFlags() );
+
+ g_pMaterialSystem->PersistDisplay();
+ ShutdownVGUI();
+ ShutdownAudio();
+ XBX_DisconnectConsoleMonitor();
+
+ XboxLaunch()->Launch();
+ }
+}
+
+//--------------------------------------------------------------------------------------
+// Handle game selection
+//--------------------------------------------------------------------------------------
+void CAppChooser::OnActivateGame()
+{
+ if ( !m_FadeInTime || Plat_FloatTime() <= m_FadeInTime + 1.0f )
+ {
+ // lockout user selection input until screen has stable rendering and completed its fade in
+ // prevents button mashing doing an immediate game selection just as the startup movies end
+ return;
+ }
+
+ if ( !g_Games[m_Selection].bEnabled )
+ {
+ m_Deny.Play();
+ return;
+ }
+
+ m_Clack.Play();
+ while ( m_Clack.IsPlaying() )
+ {
+ // let the audio complete
+ Sleep( 1 );
+ }
+
+ StartExitingProcess();
+}
+
+void CAppChooser::OnDemoMovieEnd()
+{
+ g_AppChooserSystem.ResetTimeout( INACTIVITY_TIMEOUT );
+ m_bPlayingDemoMovie = false;
+}
+
+void DemoMovieCallback()
+{
+ g_AppChooserSystem.OnDemoMovieEnd();
+}
+
+//--------------------------------------------------------------------------------------
+// Handle inactivity event
+//--------------------------------------------------------------------------------------
+void CAppChooser::OnInactivityTimeout()
+{
+ // no further inactivity timeouts
+ ResetTimeout( 0 );
+
+ const char *pDemoMovieName = g_DemoMovies[m_DemoMovie];
+ m_DemoMovie++;
+ if ( m_DemoMovie >= ARRAYSIZE( g_DemoMovies ) )
+ {
+ // reset
+ m_DemoMovie = 0;
+ }
+
+ m_bPlayingDemoMovie = m_pDemoMovieImage->StartMovieFromFile( pDemoMovieName, true, false, DemoMovieCallback );
+ if ( !m_bPlayingDemoMovie )
+ {
+ // try again, later
+ ResetTimeout( INACTIVITY_TIMEOUT );
+ }
+}
+
+//--------------------------------------------------------------------------------------
+// Handle demo stop request
+//--------------------------------------------------------------------------------------
+void CAppChooser::OnStopDemoMovie()
+{
+ m_pDemoMovieImage->StopMovie();
+}
+
+//--------------------------------------------------------------------------------------
+// Handle startup stop request
+//--------------------------------------------------------------------------------------
+void CAppChooser::OnStopStartupMovie()
+{
+ if ( m_StartupMovie >= 0 && g_StartupMovies[m_StartupMovie].bUserCanSkip )
+ {
+ m_pStartupMovieImage->StopMovie();
+ }
+}
+
+//--------------------------------------------------------------------------------------
+// Setup the exiting context. Cannot be aborted.
+//--------------------------------------------------------------------------------------
+void CAppChooser::StartExitingProcess()
+{
+ bool bIsMultiplayer = V_stricmp( g_Games[m_Selection].pGameDir, "tf" ) == 0;
+
+ // stable rendering, start the fade out
+ m_pOverlay = m_pProductBackgrounds[m_Selection];
+
+ m_pLoading->SetVisible( true );
+ m_pLoading->SetAlpha( 0 );
+ SetLoadingIconPosition( bIsMultiplayer );
+ m_pLoadingIcon->SetVisible( true );
+ m_pLoadingIcon->SetAlpha( 0 );
+
+ m_FadeOutTime = Plat_FloatTime();
+
+ m_bExiting = true;
+ m_ExitingFrameCount = 0;
+
+ // ensure that nothing can stop the exiting process
+ // otherwise the player could rapidly select a game during the fade outs
+ ResetTimeout( 0 );
+ EnableInput( false );
+}
+
+//--------------------------------------------------------------------------------------
+// Handle per frame update, prior to render
+//--------------------------------------------------------------------------------------
+void CAppChooser::FrameTick()
+{
+ if ( m_ExitingFrameCount )
+ {
+ return;
+ }
+
+ g_TargetMovieVolume = 1.0f;
+ BOOL bControl;
+ if ( ERROR_SUCCESS == XMPTitleHasPlaybackControl(&bControl) )
+ {
+ g_TargetMovieVolume = bControl ? 1.0f : 0.0f;
+ }
+
+ if ( m_FadeInTime )
+ {
+ // fade in overlay goes from [1..0] and holds on 0
+ float t = ( Plat_FloatTime() - m_FadeInTime ) * 2.0f;
+ t = 1.0f - clamp( t, 0.0f, 1.0f );
+ m_pOverlay->SetAlpha ( t * 255.0f );
+ }
+
+ if ( m_FadeOutTime )
+ {
+ // fade out overlay goes from [0..1] and holds on 1
+ float t = ( Plat_FloatTime() - m_FadeOutTime ) * 2.0f;
+ t = clamp( t, 0.0f, 1.0f );
+ m_pOverlay->SetAlpha ( t * 255.0f );
+ }
+
+ for ( int i=0; i<ARRAYSIZE( g_Games ); i++ )
+ {
+ m_pGames[i]->SetSelected( false );
+ }
+ m_pGames[m_Selection]->SetSelected( true );
+
+ if ( m_bExiting )
+ {
+ m_pLoading->SetAlpha( m_pOverlay->GetAlpha() );
+ m_pLoadingIcon->SetAlpha( m_pOverlay->GetAlpha() );
+ if ( m_pOverlay->GetAlpha() == 255 )
+ {
+ // exiting needs to have fade overlay fully opaque before stopping background movie
+ m_pBackgroundMovie->StopMovie();
+
+ // exiting commences after all movies full release and enough frames have swapped to ensure stability
+ // strict time (frame rate) cannot be trusted, must allow a non trivial amount of presents
+ m_ExitingFrameCount = 30;
+ }
+ }
+
+ if ( m_bPlayingStartupMovies )
+ {
+ if ( m_pStartupMovieImage->IsFullyReleased() )
+ {
+ m_StartupMovie++;
+ if ( m_StartupMovie >= ARRAYSIZE( g_StartupMovies ) )
+ {
+ // end of cycle
+ m_StartupMovie = -1;
+ m_bPlayingStartupMovies = false;
+ }
+ else
+ {
+ m_bPlayingStartupMovies = m_pStartupMovieImage->StartMovieFromFile( g_StartupMovies[m_StartupMovie].pMovieName, true, false );
+ }
+ }
+
+ if ( !m_bPlayingStartupMovies )
+ {
+ ResetTimeout( INACTIVITY_TIMEOUT );
+ m_BackgroundMovieStartTime = Plat_FloatTime();
+ m_GameMovieStartTime = m_BackgroundMovieStartTime + 1.0f;
+ }
+ else
+ {
+ return;
+ }
+ }
+
+ if ( m_bInviteAccepted )
+ {
+ // stop attract mode
+ if ( m_bPlayingDemoMovie )
+ {
+ // hint the demo movie to stop
+ OnStopDemoMovie();
+ }
+
+ // attract movies must finish before allowing invite
+ // background movies must finish before allowing invite
+ // starts the exiting process ONCE
+ if ( !m_bPlayingStartupMovies && !m_bPlayingDemoMovie && !m_bExiting )
+ {
+ for ( int i = 0; i < ARRAYSIZE( g_Games ); i++ )
+ {
+ if ( V_stristr( "tf", g_Games[i].pGameDir ) )
+ {
+ // EVIL! spoof a user slection to invite only TF
+ m_Selection = i;
+ StartExitingProcess();
+
+ // must fixup state, invite could have happened very early, at any time, before the initial fade-in
+ // ensure no movies start
+ m_BackgroundMovieStartTime = 0;
+ m_GameMovieStartTime = 0;
+
+ // hack the fade times to match the expected state
+ if ( !m_FadeInTime || m_FadeInTime >= Plat_FloatTime() )
+ {
+ // can't fade out, invite occured before fade-in
+ // can only snap it because there is no background to fade from
+ m_pPersistedImage->SetAlpha( 0 );
+ m_FadeInTime = 0;
+ m_FadeOutTime = 0;
+ m_pOverlay->SetAlpha( 255 );
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ if ( m_bExiting || m_bPlayingDemoMovie || m_LastSelection != m_Selection )
+ {
+ m_pGames[m_LastSelection]->StopMovie();
+ m_LastSelection = m_Selection;
+
+ // user can change selection very quickly
+ // lag the movie starting until selection settles
+ m_GameMovieStartTime = Plat_FloatTime() + 2.0f;
+ }
+
+ if ( !m_bExiting && !m_bPlayingDemoMovie && ( m_GameMovieStartTime != 0 ) && ( Plat_FloatTime() >= m_GameMovieStartTime ) )
+ {
+ // keep trying the current selection, it will eventually start
+ m_pGames[m_Selection]->StartMovie();
+ if ( m_LastBackgroundMovie != m_Selection )
+ {
+ m_pBackgroundMovie->StopMovie();
+ m_LastBackgroundMovie = m_Selection;
+ m_BackgroundMovieStartTime = Plat_FloatTime() + 2.0f;
+ }
+ }
+
+ // update the background movie, lags far behind any user selection
+ if ( !m_bExiting && !m_bPlayingDemoMovie && ( m_BackgroundMovieStartTime != 0 ) && ( Plat_FloatTime() >= m_BackgroundMovieStartTime ) )
+ {
+ char szFilename[MAX_PATH];
+ V_snprintf( szFilename, sizeof( szFilename ), "background_%s.wmv", g_Games[m_Selection].pGameDir );
+
+ // keep trying the current selection, it will eventually start
+ bool bStarted = m_pBackgroundMovie->StartMovieFromFile(
+ szFilename,
+ false,
+ true );
+ if ( bStarted )
+ {
+ m_LastBackgroundMovie = m_Selection;
+ }
+ }
+}
+
+//--------------------------------------------------------------------------------------
+// Render the scene
+//--------------------------------------------------------------------------------------
+void CAppChooser::RenderScene()
+{
+ FrameTick();
+
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ g_pMaterialSystem->BeginFrame( Plat_FloatTime() );
+
+ pRenderContext->ClearColor3ub( 0, 0, 0 );
+ pRenderContext->ClearBuffers( true, true );
+ pRenderContext->Flush( true );
+
+ // render all movies, XMV needs to hijack D3D
+ bool bPreviousState = g_pMaterialSystem->OwnGPUResources( false );
+ {
+ m_pStartupMovieImage->RenderVideoFrame();
+ m_pBackgroundMovie->RenderVideoFrame();
+ m_pDemoMovieImage->RenderVideoFrame();
+ for ( int i=0; i<ARRAYSIZE( m_pGames ); i++ )
+ {
+ m_pGames[i]->m_pMovieImage->RenderVideoFrame();
+ }
+ }
+ g_pMaterialSystem->OwnGPUResources( bPreviousState );
+
+ pRenderContext->Viewport( 0, 0, m_nScreenWidth, m_nScreenHeight );
+ pRenderContext->MatrixMode( MATERIAL_PROJECTION );
+ pRenderContext->LoadIdentity();
+ pRenderContext->MatrixMode( MATERIAL_VIEW );
+ pRenderContext->LoadIdentity();
+ pRenderContext->MatrixMode( MATERIAL_MODEL );
+ pRenderContext->LoadIdentity();
+ pRenderContext->Flush( true );
+
+ vgui::ivgui()->RunFrame();
+ vgui::surface()->PaintTraverseEx( vgui::surface()->GetEmbeddedPanel() );
+
+ g_pMaterialSystem->EndFrame();
+ g_pMaterialSystem->SwapBuffers();
+
+ if ( !m_FadeInTime && !m_bPlayingStartupMovies && !m_bExiting )
+ {
+ // stable rendering, start the fade in
+ m_FadeInTime = Plat_FloatTime() + 1.0f;
+ }
+
+ // check for timeout
+ if ( m_Timeout && ( Plat_FloatTime() >= m_Timeout ) )
+ {
+ m_Timeout = 0;
+ OnInactivityTimeout();
+ }
+
+ if ( m_ExitingFrameCount > 1 && m_pBackgroundMovie->IsFullyReleased() && m_pGames[m_Selection]->m_pMovieImage->IsFullyReleased() )
+ {
+ // must wait for a few frames to settle to ensure frame buffer is stable
+ m_ExitingFrameCount--;
+ if ( m_ExitingFrameCount == 1 )
+ {
+ ExitChooser();
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CAppChooser::HandleInvite( DWORD nUserId )
+{
+ // invites are asynchronous and could happen at any time
+ if ( m_bInviteAccepted )
+ {
+ // already accepted
+ return;
+ }
+
+ // Collect our session data
+ XINVITE_INFO inviteInfo;
+ DWORD dwError = XInviteGetAcceptedInfo( nUserId, &inviteInfo );
+ if ( dwError != ERROR_SUCCESS )
+ {
+ return;
+ }
+
+ // We only care if we're asked to join an Orange Box session
+ if ( inviteInfo.dwTitleID != TITLEID_THE_ORANGE_BOX )
+ {
+ return;
+ }
+
+ // Store off the session ID and mark the invite as accepted internally
+ m_bInviteAccepted = true;
+ m_InviteSessionID = inviteInfo.hostInfo.sessionID;
+ m_nInviteUserID = nUserId;
+
+ // must wait for the startup movies or attract mode to end
+ // FrameTick() will detect the invite and transition
+ // the user has accpeted and cannot abort the invite
+ EnableInput( false );
+ ResetTimeout( 0 );
+}
+
+//--------------------------------------------------------------------------------------
+// Window Proc
+//--------------------------------------------------------------------------------------
+LRESULT CALLBACK WndProc( HWND hWnd, UINT iMsg, WPARAM wParam, LPARAM lParam )
+{
+ switch ( iMsg )
+ {
+ case WM_CLOSE:
+ g_bActive = false;
+ break;
+
+ case WM_DESTROY:
+ PostQuitMessage( 0 );
+ return 0L;
+
+ case WM_LIVE_INVITE_ACCEPTED:
+ g_AppChooserSystem.HandleInvite( LOWORD( lParam ) );
+ break;
+
+ case WM_XCONTROLLER_KEY:
+ if ( !g_AppChooserSystem.IsInputEnabled() )
+ {
+ break;
+ }
+
+ if ( g_AppChooserSystem.IsStartupMoviePlaying() )
+ {
+ // some startup movies can be aborted
+ switch ( wParam )
+ {
+ case KEY_XBUTTON_A:
+ case KEY_XBUTTON_START:
+ if ( LOWORD( lParam ) )
+ {
+ g_AppChooserSystem.OnStopStartupMovie();
+ }
+ }
+ // no other input is allowed during this state
+ break;
+ }
+
+ if ( g_AppChooserSystem.IsDemoMoviePlaying() )
+ {
+ // demo movies can be aborted
+ switch ( wParam )
+ {
+ case KEY_XBUTTON_A:
+ case KEY_XBUTTON_START:
+ if ( LOWORD( lParam ) )
+ {
+ g_AppChooserSystem.OnStopDemoMovie();
+ }
+ }
+ }
+ else
+ {
+ if ( LOWORD( lParam ) )
+ {
+ g_AppChooserSystem.ResetTimeout( INACTIVITY_TIMEOUT );
+ }
+
+ switch ( wParam )
+ {
+ case KEY_XBUTTON_LEFT:
+ case KEY_XSTICK1_LEFT:
+ if ( LOWORD( lParam ) )
+ {
+ g_AppChooserSystem.OnSelectionPrevious();
+ }
+ break;
+
+ case KEY_XBUTTON_RIGHT:
+ case KEY_XSTICK1_RIGHT:
+ if ( LOWORD( lParam ) )
+ {
+ g_AppChooserSystem.OnSelectionNext();
+ }
+ break;
+
+ case KEY_XBUTTON_A:
+ case KEY_XBUTTON_START:
+ if ( LOWORD( lParam ) )
+ {
+ g_AppChooserSystem.OnActivateGame();
+ }
+ break;
+ }
+ }
+ break;
+ }
+
+ return DefWindowProc( hWnd, iMsg, wParam, lParam );
+}
+
+//--------------------------------------------------------------------------------------
+// CreateWindow
+//
+//--------------------------------------------------------------------------------------
+bool CAppChooser::CreateWindow( int width, int height, bool bFullScreen )
+{
+ HWND hWnd;
+ WNDCLASSEX wndClass;
+ DWORD dwStyle, dwExStyle;
+ int x, y, sx, sy;
+
+ if ( ( hWnd = FindWindow( GetAppName(), GetAppName() ) ) != NULL )
+ {
+ SetForegroundWindow( hWnd );
+ return true;
+ }
+
+ wndClass.cbSize = sizeof( wndClass );
+ wndClass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
+ wndClass.lpfnWndProc = ::WndProc;
+ wndClass.cbClsExtra = 0;
+ wndClass.cbWndExtra = 0;
+ wndClass.hInstance = (HINSTANCE)GetAppInstance();
+ wndClass.hIcon = 0;
+ wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
+ wndClass.hbrBackground = (HBRUSH)COLOR_GRAYTEXT;
+ wndClass.lpszMenuName = NULL;
+ wndClass.lpszClassName = GetAppName();
+ wndClass.hIconSm = 0;
+ if ( !RegisterClassEx( &wndClass ) )
+ {
+ Error( "Window class registration failed\n" );
+ return false;
+ }
+
+ if ( bFullScreen )
+ {
+ dwExStyle = WS_EX_TOPMOST;
+ dwStyle = WS_POPUP | WS_VISIBLE;
+ }
+ else
+ {
+ dwExStyle = 0;
+ dwStyle = WS_CAPTION | WS_SYSMENU;
+ }
+
+ x = y = 0;
+ sx = width;
+ sy = height;
+
+ hWnd = CreateWindowEx(
+ dwExStyle,
+ GetAppName(), // window class name
+ GetAppName(), // window caption
+ dwStyle | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, // window style
+ x, // initial x position
+ y, // initial y position
+ sx, // initial x size
+ sy, // initial y size
+ NULL, // parent window handle
+ NULL, // window menu handle
+ (HINSTANCE)GetAppInstance(),// program instance handle
+ NULL); // creation parameter
+
+ if ( hWnd == NULL )
+ {
+ return false;
+ }
+
+ m_hWnd = hWnd;
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Create
+//-----------------------------------------------------------------------------
+bool CAppChooser::Create()
+{
+ AppSystemInfo_t appSystems[] =
+ {
+ { "filesystem_stdio.dll", QUEUEDLOADER_INTERFACE_VERSION },
+ { "materialsystem.dll", MATERIAL_SYSTEM_INTERFACE_VERSION },
+ { "inputsystem.dll", INPUTSYSTEM_INTERFACE_VERSION },
+ { "vgui2.dll", VGUI_IVGUI_INTERFACE_VERSION },
+ { "vguimatsurface.dll", VGUI_SURFACE_INTERFACE_VERSION },
+ { "", "" }
+ };
+
+ MathLib_Init( 2.2f, 2.2f, 0.0f, 2.0f );
+
+ SpewOutputFunc( g_DefaultSpewFunc );
+
+ // Add in the cvar factory
+ AppModule_t cvarModule = LoadModule( VStdLib_GetICVarFactory() );
+ AddSystem( cvarModule, CVAR_INTERFACE_VERSION );
+
+ // vxconsole - true will block (legacy behavior)
+ XBX_InitConsoleMonitor( false );
+
+ if ( !AddSystems( appSystems ) )
+ return false;
+
+ IMaterialSystem* pMaterialSystem = (IMaterialSystem*)FindSystem( MATERIAL_SYSTEM_INTERFACE_VERSION );
+ if ( !pMaterialSystem )
+ {
+ Error( "Failed to find %s\n", MATERIAL_SYSTEM_INTERFACE_VERSION );
+ return false;
+ }
+
+ if ( IsX360() )
+ {
+ IFileSystem* pFileSystem = (IFileSystem*)FindSystem( FILESYSTEM_INTERFACE_VERSION );
+ if ( !pFileSystem )
+ {
+ Error( "Failed to find %s\n", FILESYSTEM_INTERFACE_VERSION );
+ return false;
+ }
+ pFileSystem->LoadModule( "shaderapidx9.dll" );
+ }
+
+ pMaterialSystem->SetShaderAPI( "shaderapidx9.dll" );
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// PreInit
+//-----------------------------------------------------------------------------
+bool CAppChooser::PreInit()
+{
+ if ( !BaseClass::PreInit() )
+ return false;
+
+ if ( !g_pFullFileSystem || !g_pMaterialSystem )
+ {
+ Warning( "Unable to find required interfaces!\n" );
+ return false;
+ }
+
+ // Add paths...
+ if ( !SetupSearchPaths( NULL, false, true ) )
+ {
+ Error( "Failed to setup search paths\n" );
+ return false;
+ }
+
+ // Create the main program window and our viewport
+ int w = 640;
+ int h = 480;
+ if ( IsX360() )
+ {
+ w = GetSystemMetrics( SM_CXSCREEN );
+ h = GetSystemMetrics( SM_CYSCREEN );
+ }
+
+ if ( !CreateWindow( w, h, false ) )
+ {
+ ChangeDisplaySettings( 0, 0 );
+ Error( "Unable to create main window\n" );
+ return false;
+ }
+
+ ShowWindow( m_hWnd, SW_SHOWNORMAL );
+ UpdateWindow( m_hWnd );
+ SetForegroundWindow( m_hWnd );
+ SetFocus( m_hWnd );
+
+ XOnlineStartup();
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Destroy
+//-----------------------------------------------------------------------------
+void CAppChooser::Destroy()
+{
+ XOnlineCleanup();
+}
+
+//-----------------------------------------------------------------------------
+// Main
+//-----------------------------------------------------------------------------
+int CAppChooser::Main()
+{
+ if ( !InitMaterialSystem() )
+ {
+ return 0;
+ }
+
+ if ( !InitVGUI() )
+ {
+ return 0;
+ }
+
+ if ( !InitAudio() )
+ {
+ return 0;
+ }
+
+ // post initialization reset
+ Reset();
+
+ // Setup a listener for invites
+ XBX_NotifyCreateListener( XNOTIFY_LIVE );
+
+ MSG msg;
+ while ( g_bActive == TRUE )
+ {
+ // Pump the XBox notifications
+ XBX_ProcessEvents();
+
+ while ( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
+ {
+ TranslateMessage( &msg );
+ DispatchMessage( &msg );
+ }
+
+ g_pInputSystem->PollInputState();
+ RenderScene();
+ }
+
+ ShutdownVGUI();
+ ShutdownAudio();
+ Destroy();
+
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+// The entry point for the application
+//-----------------------------------------------------------------------------
+extern "C" __declspec(dllexport) int AppChooserMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
+{
+ CommandLine()->CreateCmdLine( lpCmdLine );
+
+ SetAppInstance( hInstance );
+
+ CSteamApplication steamApplication( &g_AppChooserSystem );
+ steamApplication.Run();
+
+ return 0;
+}