You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2239 lines
61 KiB
2239 lines
61 KiB
//========= 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; |
|
}
|
|
|