diff options
Diffstat (limited to 'utils/xbox/AppChooser')
| -rw-r--r-- | utils/xbox/AppChooser/AppChooser.vpc | 47 | ||||
| -rw-r--r-- | utils/xbox/AppChooser/main.cpp | 2239 | ||||
| -rw-r--r-- | utils/xbox/AppChooser/xbox/xbox.def | 3 |
3 files changed, 2289 insertions, 0 deletions
diff --git a/utils/xbox/AppChooser/AppChooser.vpc b/utils/xbox/AppChooser/AppChooser.vpc new file mode 100644 index 0000000..e960987 --- /dev/null +++ b/utils/xbox/AppChooser/AppChooser.vpc @@ -0,0 +1,47 @@ +//----------------------------------------------------------------------------- +// AppChooser.VPC +// +// Project Script +//----------------------------------------------------------------------------- + +$Macro SRCDIR "..\..\.." +$Macro OUTBINDIR "$SRCDIR\..\game\bin" + +$Include "$SRCDIR\vpc_scripts\source_dll_base.vpc" + +$Configuration "Debug" +{ + $Linker [$X360] + { + $AdditionalDependencies "$BASE xaudiod.lib xmediad.lib xnet.lib xonline.lib xmp.lib" + } +} + +$Configuration "Release" +{ + $Linker [$X360] + { + $AdditionalDependencies "$BASE xaudio.lib xmedia.lib xnet.lib xonline.lib xmp.lib" + } +} + +$Project "AppChooser" +{ + $Folder "Source Files" + { + $file "main.cpp" + $file "$SRCDIR\public\vgui_controls\vgui_controls.cpp" + } + + $Folder "Link Libraries" + { + $Lib appframework + $Lib mathlib + $Lib tier2 + $Lib tier3 + $Lib vtf + $Lib bitmap + $Lib vgui_controls + $Lib vgui_surfacelib + } +} 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; +} diff --git a/utils/xbox/AppChooser/xbox/xbox.def b/utils/xbox/AppChooser/xbox/xbox.def new file mode 100644 index 0000000..66245f5 --- /dev/null +++ b/utils/xbox/AppChooser/xbox/xbox.def @@ -0,0 +1,3 @@ +LIBRARY AppChooser_360.dll +EXPORTS + AppChooserMain @1
\ No newline at end of file |