Team Fortress 2 Source Code as on 22/4/2020
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

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: X360 Application Chooser
  4. //
  5. //===========================================================================//
  6. #if !defined( _X360 )
  7. #include <windows.h>
  8. #endif
  9. #include "appframework/iappsystemgroup.h"
  10. #include "appframework/appframework.h"
  11. #include "tier0/dbg.h"
  12. #include "tier1/interface.h"
  13. #include "tier1/KeyValues.h"
  14. #include "filesystem.h"
  15. #include "vstdlib/cvar.h"
  16. #include "filesystem_init.h"
  17. #include "tier1/utlbuffer.h"
  18. #include "icommandline.h"
  19. #include "datacache/idatacache.h"
  20. #include "datacache/imdlcache.h"
  21. #include "studio.h"
  22. #include "utlbuffer.h"
  23. #include "tier2/utlstreambuffer.h"
  24. #include "tier2/tier2.h"
  25. #include "appframework/tier3app.h"
  26. #include "mathlib/mathlib.h"
  27. #include "inputsystem/iinputsystem.h"
  28. #include "vphysics_interface.h"
  29. #include "istudiorender.h"
  30. #include "studio.h"
  31. #include "vgui/IVGui.h"
  32. #include "VGuiMatSurface/IMatSystemSurface.h"
  33. #include "matsys_controls/matsyscontrols.h"
  34. #include "vgui/ILocalize.h"
  35. #include "vgui_controls/panel.h"
  36. #include "vgui_controls/Label.h"
  37. #include "vgui_controls/imagepanel.h"
  38. #include "vgui_controls/AnimationController.h"
  39. #include "materialsystem/imaterialsystem.h"
  40. #include "materialsystem/imesh.h"
  41. #include "materialsystem/materialsystem_config.h"
  42. #include "materialsystem/MaterialSystemUtil.h"
  43. #include "materialsystem/ishaderapi.h"
  44. #include "materialsystem/itexture.h"
  45. #include "filesystem/IQueuedLoader.h"
  46. #include "xwvfile.h"
  47. #if defined( _X360 )
  48. #include "hl2orange.spa.h"
  49. #include "xbox/xbox_console.h"
  50. #include "xbox/xbox_win32stubs.h"
  51. #include "xbox/xbox_launch.h"
  52. #include <xaudio.h>
  53. #include <xmedia.h>
  54. #include <xmp.h>
  55. #endif
  56. #if !defined( _X360 )
  57. #include "xbox/xboxstubs.h"
  58. #endif
  59. #include "tier0/memdbgon.h"
  60. #define INACTIVITY_TIMEOUT 120
  61. #define MOVIE_PATH "d:\\movies"
  62. bool g_bActive = true;
  63. // user background music can force this to be zero
  64. static float g_TargetMovieVolume = 1.0f;
  65. extern SpewOutputFunc_t g_DefaultSpewFunc;
  66. typedef void (*MovieEndCallback_t)();
  67. struct game_t
  68. {
  69. const wchar_t *pName;
  70. const char *pGameDir;
  71. bool bEnabled;
  72. };
  73. game_t g_Games[] =
  74. {
  75. { L"HALF-LIFE 2", "hl2", true },
  76. { L"HALF-LIFE 2:\nEPISODE ONE", "episodic", true },
  77. { L" HALF-LIFE 2:\nEPISODE TWO", "ep2", true },
  78. { L"PORTAL", "portal", true },
  79. { L" TEAM\nFORTRESS 2", "tf", true },
  80. };
  81. struct StartupMovie_t
  82. {
  83. const char *pMovieName;
  84. bool bUserCanSkip;
  85. };
  86. // played in order on initial startup
  87. StartupMovie_t g_StartupMovies[] =
  88. {
  89. { "valve_legalese.wmv", false },
  90. };
  91. const char *g_DemoMovies[] =
  92. {
  93. "demo.wmv",
  94. "teaser_l4d.wmv",
  95. };
  96. class CImage : public vgui::Panel
  97. {
  98. DECLARE_CLASS_SIMPLE( CImage, vgui::Panel );
  99. public:
  100. CImage( vgui::Panel *pParent, const char *pName, const char *pImageName ) : BaseClass( pParent, pName )
  101. {
  102. m_imageID = vgui::surface()->CreateNewTextureID();
  103. vgui::surface()->DrawSetTextureFile( m_imageID, pImageName, false, false );
  104. m_Color = Color( 255, 255, 255, 255 );
  105. SetPaintBackgroundEnabled( true );
  106. m_AspectRatio = 1.0f;
  107. m_bLetterbox = false;
  108. }
  109. CImage( vgui::Panel *pParent, const char *pName, IMaterial *pMaterial ) : BaseClass( pParent, pName )
  110. {
  111. m_imageID = vgui::surface()->CreateNewTextureID();
  112. g_pMatSystemSurface->DrawSetTextureMaterial( m_imageID, pMaterial );
  113. m_Color = Color( 255, 255, 255, 255 );
  114. SetPaintBackgroundEnabled( true );
  115. m_AspectRatio = 1.0f;
  116. m_bLetterbox = false;
  117. }
  118. void SetAspectRatio( float aspectRatio, bool bLetterbox )
  119. {
  120. if ( aspectRatio > 1.0f )
  121. {
  122. m_AspectRatio = aspectRatio;
  123. m_bLetterbox = bLetterbox;
  124. }
  125. }
  126. void SetColor( int r, int g, int b )
  127. {
  128. // set rgb only
  129. m_Color.SetColor( r, g, b, m_Color.a() );
  130. }
  131. void SetAlpha( int alpha )
  132. {
  133. // set alpha only
  134. alpha = clamp( alpha, 0, 255 );
  135. m_Color.SetColor( m_Color.r(), m_Color.g(), m_Color.b(), alpha );
  136. }
  137. int GetAlpha() const
  138. {
  139. return m_Color.a();
  140. }
  141. virtual void PaintBackground( void )
  142. {
  143. if ( m_Color.a() != 0 )
  144. {
  145. int panelWidth, panelHeight;
  146. GetSize( panelWidth, panelHeight );
  147. float s0 = 0.0f;
  148. float s1 = 1.0f;
  149. int y = 0;
  150. if ( m_AspectRatio > 1.0f )
  151. {
  152. if ( m_bLetterbox )
  153. {
  154. // horizontal letterbox
  155. float adjustedHeight = (float)panelWidth / m_AspectRatio;
  156. float bandHeight = ( (float)panelHeight - adjustedHeight ) / 2;
  157. vgui::surface()->DrawSetColor( Color( 0, 0, 0, m_Color.a() ) );
  158. vgui::surface()->DrawFilledRect( 0, 0, panelWidth, bandHeight );
  159. vgui::surface()->DrawFilledRect( 0, panelHeight - bandHeight, panelWidth, panelHeight );
  160. y = bandHeight;
  161. panelHeight = adjustedHeight;
  162. }
  163. else
  164. {
  165. // hold the panel's height constant, determine the corresponding aspect corrected image width
  166. float imageWidth = (float)panelHeight * m_AspectRatio;
  167. // adjust the image width as a percentage of the panel's width
  168. // scale and center;
  169. s1 = (float)panelWidth / imageWidth;
  170. s0 = ( 1 - s1 ) / 2.0f;
  171. s1 = s0 + s1;
  172. }
  173. }
  174. vgui::surface()->DrawSetColor( m_Color );
  175. vgui::surface()->DrawSetTexture( m_imageID );
  176. vgui::surface()->DrawTexturedSubRect( 0, y, panelWidth, y+panelHeight, s0, 0.0f, s1, 1.0f );
  177. }
  178. }
  179. int m_imageID;
  180. Color m_Color;
  181. float m_AspectRatio;
  182. bool m_bLetterbox;
  183. };
  184. class CMovieImage : public vgui::Panel
  185. {
  186. DECLARE_CLASS_SIMPLE( CMovieImage, vgui::Panel );
  187. public:
  188. CMovieImage( vgui::Panel *pParent, const char *pName, const char *pUniqueName, int nDecodeWidth, int nDecodeHeight, float fadeDelay = 1.0f ) : BaseClass( pParent, pName )
  189. {
  190. // decoupled from actual panel bounds, can be decoded at any power of two resolution
  191. m_nDecodeWidth = nDecodeWidth;
  192. m_nDecodeHeight = nDecodeHeight;
  193. char materialName[MAX_PATH];
  194. V_snprintf( materialName, sizeof( materialName ), "MovieMaterial_%s.vmt", pUniqueName );
  195. char textureName[MAX_PATH];
  196. V_snprintf( textureName, sizeof( textureName ), "MovieFrame_%s", pUniqueName );
  197. // create a texture for use as movie frame grab
  198. m_pTexture = g_pMaterialSystem->CreateProceduralTexture(
  199. textureName,
  200. TEXTURE_GROUP_OTHER,
  201. m_nDecodeWidth,
  202. m_nDecodeHeight,
  203. g_pMaterialSystem->GetBackBufferFormat(),
  204. TEXTUREFLAGS_NOMIP | TEXTUREFLAGS_SINGLECOPY );
  205. KeyValues *pVMTKeyValues = new KeyValues( "screenspace_general" );
  206. pVMTKeyValues->SetInt( "$ignorez", 1 );
  207. pVMTKeyValues->SetInt( "$linearread_basetexture", 1 );
  208. pVMTKeyValues->SetInt( "$X360APPCHOOSER", 1 );
  209. pVMTKeyValues->SetString( "$PIXSHADER", "appchooser360movie_ps20b" );
  210. pVMTKeyValues->SetString( "$basetexture", textureName );
  211. m_pMaterial = g_pMaterialSystem->CreateMaterial( materialName, pVMTKeyValues );
  212. m_imageID = vgui::surface()->CreateNewTextureID();
  213. g_pMatSystemSurface->DrawSetTextureMaterial( m_imageID, m_pMaterial );
  214. SetPaintBackgroundEnabled( true );
  215. // initially invisible
  216. m_Color = Color( 255, 255, 255, 0 );
  217. m_FadeDelay = fadeDelay;
  218. m_pXMVPlayer = NULL;
  219. m_bStopping = false;
  220. m_StartTime = 0;
  221. m_StopTime = 0;
  222. m_pMovieEndCallback = NULL;
  223. m_bLooped = false;
  224. m_AspectRatio = 1.0f;
  225. m_bLetterbox = false;
  226. }
  227. void SetAspectRatio( float aspectRatio, bool bLetterbox )
  228. {
  229. if ( aspectRatio > 1.0f )
  230. {
  231. m_AspectRatio = aspectRatio;
  232. m_bLetterbox = bLetterbox;
  233. }
  234. }
  235. void SetColor( int r, int g, int b )
  236. {
  237. // set rgb only
  238. m_Color.SetColor( r, g, b, m_Color.a() );
  239. }
  240. // -1 means never have any audio, otherwise set to current target
  241. void InitUserAudioMix( bool bAudio )
  242. {
  243. m_CurrentVolume = bAudio ? g_TargetMovieVolume : -1;
  244. }
  245. // fade in/out based on user interaction with dashboard music system
  246. void UpdateMovieVolume( bool bForce, float frametime = 1.0f )
  247. {
  248. // m_CurrentVolume < 0 means this movie never plays audio
  249. if ( !m_pXMVPlayer || m_CurrentVolume < 0 )
  250. return;
  251. // forced update or new volume, ramp & set
  252. if ( bForce || g_TargetMovieVolume != m_CurrentVolume )
  253. {
  254. if ( bForce )
  255. {
  256. frametime = 1.0f;
  257. }
  258. m_CurrentVolume = Approach( g_TargetMovieVolume, m_CurrentVolume, frametime * 0.5f );
  259. // UNDONE: Under what conditions can this fail? If it fails it could cause audible pops
  260. IXAudioSourceVoice *pVoice = NULL;
  261. HRESULT hr = m_pXMVPlayer->GetSourceVoice( &pVoice );
  262. if ( !FAILED( hr ) && pVoice )
  263. {
  264. pVoice->SetVolume( m_CurrentVolume );
  265. }
  266. }
  267. }
  268. bool StartMovieFromMemory( const void *pBuffer, int bufferSize, bool bAudio, bool bLoop, MovieEndCallback_t pMovieEndCallback = NULL )
  269. {
  270. if ( m_pXMVPlayer || m_bStopping )
  271. {
  272. // already started or currently stopping
  273. return false;
  274. }
  275. if ( !pBuffer || !bufferSize )
  276. {
  277. return false;
  278. }
  279. XMEDIA_XMV_CREATE_PARAMETERS xmvParameters;
  280. V_memset( &xmvParameters, 0, sizeof( xmvParameters ) );
  281. xmvParameters.dwFlags = XMEDIA_CREATE_CPU_AFFINITY;
  282. if ( bLoop )
  283. {
  284. xmvParameters.dwFlags |= XMEDIA_CREATE_FOR_LOOP;
  285. m_bLooped = true;
  286. }
  287. if ( !bAudio )
  288. {
  289. xmvParameters.dwAudioStreamId = (DWORD)XMEDIA_STREAM_ID_DONT_USE;
  290. }
  291. InitUserAudioMix(bAudio);
  292. xmvParameters.dwVideoDecoderCpu = 2;
  293. xmvParameters.dwVideoRendererCpu = 2;
  294. xmvParameters.dwAudioDecoderCpu = 4;
  295. xmvParameters.dwAudioRendererCpu = 4;
  296. xmvParameters.createType = XMEDIA_CREATE_FROM_MEMORY;
  297. xmvParameters.createFromMemory.pvBuffer = (PVOID)pBuffer;
  298. xmvParameters.createFromMemory.dwBufferSize = bufferSize;
  299. IDirect3DDevice9 *pD3DDevice = (IDirect3DDevice9 *)g_pMaterialSystem->GetD3DDevice();
  300. HRESULT hr = XMediaCreateXmvPlayer( pD3DDevice, &xmvParameters, &m_pXMVPlayer );
  301. if ( FAILED( hr ) )
  302. {
  303. return false;
  304. }
  305. m_pMovieEndCallback = pMovieEndCallback;
  306. RECT rect;
  307. rect.left = 0;
  308. rect.top = 0;
  309. rect.right = m_nDecodeWidth;
  310. rect.bottom = m_nDecodeHeight;
  311. m_pXMVPlayer->SetRectangle( &rect );
  312. UpdateMovieVolume( true );
  313. SetAlpha( 0 );
  314. m_StartTime = Plat_FloatTime() + m_FadeDelay;
  315. m_StopTime = 0;
  316. return true;
  317. }
  318. bool StartMovieFromFile( const char *pMovieName, bool bAudio, bool bLoop, MovieEndCallback_t pMovieEndCallback = NULL )
  319. {
  320. if ( m_pXMVPlayer || m_bStopping )
  321. {
  322. // already started or currently stopping
  323. return false;
  324. }
  325. XMEDIA_XMV_CREATE_PARAMETERS xmvParameters;
  326. V_memset( &xmvParameters, 0, sizeof( xmvParameters ) );
  327. xmvParameters.dwFlags = XMEDIA_CREATE_CPU_AFFINITY;
  328. if ( bLoop )
  329. {
  330. xmvParameters.dwFlags |= XMEDIA_CREATE_FOR_LOOP;
  331. m_bLooped = true;
  332. }
  333. if ( !bAudio )
  334. {
  335. xmvParameters.dwAudioStreamId = (DWORD)XMEDIA_STREAM_ID_DONT_USE;
  336. }
  337. InitUserAudioMix( bAudio );
  338. char szFilename[MAX_PATH];
  339. V_ComposeFileName( MOVIE_PATH, pMovieName, szFilename, sizeof( szFilename ) );
  340. xmvParameters.dwVideoDecoderCpu = 2;
  341. xmvParameters.dwVideoRendererCpu = 2;
  342. xmvParameters.dwAudioDecoderCpu = 4;
  343. xmvParameters.dwAudioRendererCpu = 4;
  344. xmvParameters.createType = XMEDIA_CREATE_FROM_FILE;
  345. xmvParameters.createFromFile.szFileName = szFilename;
  346. IDirect3DDevice9 *pD3DDevice = (IDirect3DDevice9 *)g_pMaterialSystem->GetD3DDevice();
  347. HRESULT hr = XMediaCreateXmvPlayer( pD3DDevice, &xmvParameters, &m_pXMVPlayer );
  348. if ( FAILED( hr ) )
  349. {
  350. return false;
  351. }
  352. m_pMovieEndCallback = pMovieEndCallback;
  353. RECT rect;
  354. rect.left = 0;
  355. rect.top = 0;
  356. rect.right = m_nDecodeWidth;
  357. rect.bottom = m_nDecodeHeight;
  358. m_pXMVPlayer->SetRectangle( &rect );
  359. UpdateMovieVolume( true );
  360. SetAlpha( 0 );
  361. m_StartTime = Plat_FloatTime() + m_FadeDelay;
  362. m_StopTime = 0;
  363. return true;
  364. }
  365. void StopMovie()
  366. {
  367. if ( !m_pXMVPlayer || m_bStopping )
  368. {
  369. // already stopped or currently stopping
  370. return;
  371. }
  372. m_bLooped = false;
  373. m_bStopping = true;
  374. m_pXMVPlayer->Stop( XMEDIA_STOP_IMMEDIATE );
  375. SetAlpha( 255 );
  376. m_StartTime = 0;
  377. m_StopTime = Plat_FloatTime() + m_FadeDelay;
  378. }
  379. virtual void PaintBackground( void )
  380. {
  381. if ( m_StartTime )
  382. {
  383. // fade up goes from [0..1] and holds on 1
  384. float t = ( Plat_FloatTime() - m_StartTime ) * 2.0f;
  385. t = clamp( t, 0.0f, 1.0f );
  386. SetAlpha( t * 255.0f );
  387. }
  388. if ( m_StopTime )
  389. {
  390. // fade out goes from [1..0] and holds on 0
  391. float t = ( Plat_FloatTime() - m_StopTime ) * 2.0f;
  392. t = 1.0f - clamp( t, 0.0f, 1.0f );
  393. SetAlpha( t * 255.0f );
  394. if ( m_Color.a() == 0 )
  395. {
  396. if ( m_bStopping && m_pMovieEndCallback )
  397. {
  398. m_pMovieEndCallback();
  399. }
  400. m_bStopping = false;
  401. }
  402. }
  403. if ( m_Color.a() != 0 )
  404. {
  405. int panelWidth, panelHeight;
  406. GetSize( panelWidth, panelHeight );
  407. float s0 = 0.0f;
  408. float s1 = 1.0f;
  409. int y = 0;
  410. if ( m_AspectRatio > 1.0f )
  411. {
  412. if ( m_bLetterbox )
  413. {
  414. // horizontal letterbox
  415. float adjustedHeight = (float)panelWidth / m_AspectRatio;
  416. float bandHeight = ( (float)panelHeight - adjustedHeight ) / 2;
  417. vgui::surface()->DrawSetColor( Color( 0, 0, 0, m_Color.a() ) );
  418. vgui::surface()->DrawFilledRect( 0, 0, panelWidth, bandHeight );
  419. vgui::surface()->DrawFilledRect( 0, panelHeight - bandHeight, panelWidth, panelHeight );
  420. y = bandHeight;
  421. panelHeight = adjustedHeight;
  422. }
  423. else
  424. {
  425. // hold the panel's height constant, determine the corresponding aspect corrected image width
  426. float imageWidth = (float)panelHeight * m_AspectRatio;
  427. // adjust the image width as a percentage of the panel's width
  428. // scale and center;
  429. s1 = (float)panelWidth / imageWidth;
  430. s0 = ( 1 - s1 ) / 2.0f;
  431. s1 = s0 + s1;
  432. }
  433. }
  434. vgui::surface()->DrawSetColor( m_Color );
  435. vgui::surface()->DrawSetTexture( m_imageID );
  436. vgui::surface()->DrawTexturedSubRect( 0, y, panelWidth, y+panelHeight, s0, 0.0f, s1, 1.0f );
  437. }
  438. }
  439. bool IsFullyReleased()
  440. {
  441. // fully stopped and released when object no longer exists
  442. return ( m_pXMVPlayer == NULL ) && ( m_bStopping == false );
  443. }
  444. bool RenderVideoFrame()
  445. {
  446. if ( !m_pXMVPlayer )
  447. {
  448. return false;
  449. }
  450. // If RenderNextFrame does not return S_OK then the frame was not
  451. // rendered (perhaps because it was cancelled) so a regular frame
  452. // buffer should be rendered before calling present.
  453. bool bRenderedFrame = true;
  454. HRESULT hr = m_pXMVPlayer->RenderNextFrame( 0, NULL );
  455. if ( FAILED( hr ) || hr == XMEDIA_W_EOF )
  456. {
  457. bRenderedFrame = false;
  458. if ( !m_bLooped )
  459. {
  460. // Release the movie object
  461. m_pXMVPlayer->Release();
  462. m_pXMVPlayer = NULL;
  463. if ( !m_bStopping )
  464. {
  465. m_bStopping = true;
  466. SetAlpha( 255 );
  467. m_StartTime = 0;
  468. m_StopTime = Plat_FloatTime() + m_FadeDelay;
  469. }
  470. }
  471. }
  472. // UNDONE: Need a frametime here. Assume it's 30fps.
  473. // NOTE: This is only used to time audio fades, so if it's wrong by 2X it's not
  474. // going to be noticeable. Probably fine to ship this.
  475. UpdateMovieVolume( false, 1.0f / 30.0f );
  476. // Reset our cached view of what pixel and vertex shaders are set, because
  477. // it is no longer accurate, since XMV will have set their own shaders.
  478. // This avoids problems when the shader cache thinks it knows what shader
  479. // is set and it is wrong.
  480. IDirect3DDevice9 *pD3DDevice = (IDirect3DDevice9 *)g_pMaterialSystem->GetD3DDevice();
  481. pD3DDevice->SetVertexShader( NULL );
  482. pD3DDevice->SetPixelShader( NULL );
  483. pD3DDevice->SetVertexDeclaration( NULL );
  484. pD3DDevice->SetRenderState( D3DRS_VIEWPORTENABLE, TRUE );
  485. if ( bRenderedFrame )
  486. {
  487. CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
  488. Rect_t rect;
  489. rect.x = 0;
  490. rect.y = 0;
  491. rect.width = m_nDecodeWidth;
  492. rect.height = m_nDecodeHeight;
  493. pRenderContext->CopyRenderTargetToTextureEx( m_pTexture, 0, &rect );
  494. }
  495. return bRenderedFrame;
  496. }
  497. private:
  498. void SetAlpha( int alpha )
  499. {
  500. // set alpha only
  501. alpha = clamp( alpha, 0, 255 );
  502. m_Color.SetColor( m_Color.r(), m_Color.g(), m_Color.b(), alpha );
  503. }
  504. int m_imageID;
  505. Color m_Color;
  506. IXMediaXmvPlayer *m_pXMVPlayer;
  507. ITexture *m_pTexture;
  508. IMaterial *m_pMaterial;
  509. bool m_bStopping;
  510. bool m_bLooped;
  511. float m_StartTime;
  512. float m_StopTime;
  513. int m_nDecodeWidth;
  514. int m_nDecodeHeight;
  515. float m_FadeDelay;
  516. float m_AspectRatio;
  517. float m_CurrentVolume;
  518. bool m_bLetterbox;
  519. MovieEndCallback_t m_pMovieEndCallback;
  520. };
  521. class CShadowLabel : public vgui::Label
  522. {
  523. DECLARE_CLASS_SIMPLE( CShadowLabel, vgui::Label );
  524. public:
  525. CShadowLabel( vgui::Panel *pParent, const char *pName, const wchar_t *pText ) : BaseClass( pParent, pName, pText )
  526. {
  527. }
  528. virtual void Paint( void )
  529. {
  530. BaseClass::Paint();
  531. BaseClass::Paint();
  532. BaseClass::Paint();
  533. BaseClass::Paint();
  534. BaseClass::Paint();
  535. }
  536. };
  537. class CGamePanel : public vgui::Panel
  538. {
  539. DECLARE_CLASS_SIMPLE( CGamePanel, vgui::Panel );
  540. public:
  541. CGamePanel( vgui::Panel *pParent, const char *pName, game_t *pGame, bool bIsWidescreen, KeyValues *pKVSettings ) : BaseClass( pParent, pName )
  542. {
  543. m_bEnabled = pGame->bEnabled;
  544. vgui::HScheme hScheme = vgui::scheme()->GetScheme( "SourceScheme" );
  545. vgui::IScheme *pSourceScheme = vgui::scheme()->GetIScheme( hScheme );
  546. KeyValues *pKV = pKVSettings->FindKey( "GamePanel" );
  547. int panelWidth = pKV->GetInt( "wide" );
  548. int panelHeight = pKV->GetInt( "tall" );
  549. SetSize( panelWidth, panelHeight );
  550. // thumbnail static image
  551. char szFilename[MAX_PATH];
  552. V_snprintf( szFilename, sizeof( szFilename ), "vgui/appchooser/%s", pGame->pGameDir );
  553. pKV = pKVSettings->FindKey( "GameImage" );
  554. int y = pKV->GetInt( "ypos" );
  555. int w = pKV->GetInt( "wide" );
  556. int h = pKV->GetInt( "tall" );
  557. int x = ( panelWidth - w ) / 2;
  558. m_pThumbImage = new CImage( this, "GameImage", szFilename );
  559. SETUP_PANEL( m_pThumbImage );
  560. m_pThumbImage->SetBounds( x, y, w, h );
  561. m_pThumbImage->SetAspectRatio( bIsWidescreen ? 1.0f : 1.778f, false );
  562. m_pThumbImage->SetVisible( true );
  563. // thumbnail movie
  564. m_pMovieImage = new CMovieImage( this, "Movie", pGame->pGameDir, 256, 256, 0.2f );
  565. SETUP_PANEL( m_pMovieImage );
  566. m_pMovieImage->SetBounds( x, y, w, h );
  567. m_pMovieImage->SetVisible( true );
  568. V_snprintf( szFilename, sizeof( szFilename ), "%s\\thumb_%s.wmv", MOVIE_PATH, pGame->pGameDir );
  569. if ( !g_pFullFileSystem->ReadFile( szFilename, NULL, m_MovieBuffer ) )
  570. {
  571. m_MovieBuffer.Purge();
  572. }
  573. // game title shadow
  574. pKV = pKVSettings->FindKey( "GameTitle" );
  575. y = pKV->GetInt( "ypos" );
  576. m_pTitleShadow = new CShadowLabel( this, "GameTitle", pGame->pName );
  577. SETUP_PANEL( m_pTitleShadow );
  578. m_pTitleShadow->SetVisible( true );
  579. m_pTitleShadow->SetFont( pSourceScheme->GetFont( "AppChooserGameTitleFontBlur" ) );
  580. m_pTitleShadow->SizeToContents();
  581. m_pTitleShadow->SetPos( 0, y );
  582. m_pTitleShadow->SetWide( panelWidth );
  583. m_pTitleShadow->SetContentAlignment( vgui::Label::a_center );
  584. m_pTitleShadow->SetPaintBackgroundEnabled( false );
  585. m_pTitleShadow->SetFgColor( Color( 0, 0, 0, 255 ) );
  586. // game title
  587. m_pTitle = new vgui::Label( this, "GameTitle", pGame->pName );
  588. SETUP_PANEL( m_pTitle );
  589. m_pTitle->SetVisible( true );
  590. m_pTitle->SetFont( pSourceScheme->GetFont( "AppChooserGameTitleFont" ) );
  591. m_pTitle->SizeToContents();
  592. m_pTitle->SetPos( 0, y );
  593. m_pTitle->SetWide( panelWidth );
  594. m_pTitle->SetContentAlignment( vgui::Label::a_center );
  595. m_pTitle->SetPaintBackgroundEnabled( false );
  596. m_pTitle->SetFgColor( Color( 255, 255, 255, 255 ) );
  597. // button bounds
  598. vgui::HFont hFont = pSourceScheme->GetFont( "GameUIButtons" );
  599. pKV = pKVSettings->FindKey( "GameButton" );
  600. y = panelHeight - vgui::surface()->GetFontTall( hFont ) - pKV->GetInt( "ypos" );
  601. m_pButtonText = new vgui::Label( this, "GameButton", g_pVGuiLocalize->Find( "#GameUI_Icons_A_BUTTON" ) );
  602. SETUP_PANEL( m_pButtonText );
  603. m_pButtonText->SetVisible( false );
  604. m_pButtonText->SetFont( hFont );
  605. m_pButtonText->SizeToContents();
  606. m_pButtonText->SetPos( 0, y );
  607. m_pButtonText->SetWide( panelWidth );
  608. m_pButtonText->SetContentAlignment( vgui::Label::a_center );
  609. m_pButtonText->SetPaintBackgroundEnabled( false );
  610. m_pButtonText->SetFgColor( Color( 255, 255, 255, 255 ) );
  611. }
  612. ~CGamePanel( void )
  613. {
  614. }
  615. void SetSelected( bool bSelected )
  616. {
  617. m_pButtonText->SetVisible( m_bEnabled ? bSelected : false );
  618. if ( bSelected )
  619. {
  620. SetBgColor( Color( 190, 115, 0, 128 ) );
  621. m_pThumbImage->SetColor( 255, 255, 255 );
  622. m_pMovieImage->SetColor( 255, 255, 255 );
  623. m_pTitle->SetFgColor( Color( 255, 255, 255, 255 ) );
  624. }
  625. else
  626. {
  627. SetBgColor( Color( 160, 160, 160, 50 ) );
  628. m_pThumbImage->SetColor( 100, 100, 100 );
  629. m_pMovieImage->SetColor( 120, 120, 120 );
  630. m_pTitle->SetFgColor( Color( 140, 140, 140, 255 ) );
  631. }
  632. }
  633. void StartMovie()
  634. {
  635. m_pMovieImage->StartMovieFromMemory( m_MovieBuffer.Base(), m_MovieBuffer.TellMaxPut(), true, true );
  636. }
  637. void StopMovie()
  638. {
  639. m_pMovieImage->StopMovie();
  640. }
  641. CImage *m_pThumbImage;
  642. CMovieImage *m_pMovieImage;
  643. CShadowLabel *m_pTitleShadow;
  644. vgui::Label *m_pTitle;
  645. vgui::Label *m_pButtonText;
  646. CUtlBuffer m_MovieBuffer;
  647. bool m_bEnabled;
  648. int m_imageID;
  649. };
  650. //-----------------------------------------------------------------------------
  651. // Simple XAudio wrapper to instance a sound. Provides lightweight audio feedback for ui.
  652. // Instance this per sound.
  653. //-----------------------------------------------------------------------------
  654. class CXSound
  655. {
  656. public:
  657. CXSound()
  658. {
  659. for ( int i=0; i<ARRAYSIZE( m_pSourceVoices ); i++ )
  660. {
  661. m_pSourceVoices[i] = NULL;
  662. }
  663. m_pXMAData = NULL;
  664. m_nXMADataSize = 0;
  665. m_numVoices = 0;
  666. }
  667. ~CXSound()
  668. {
  669. Stop();
  670. Release();
  671. }
  672. //-----------------------------------------------------------------------------
  673. // Setup the wave, caller can clamp the number of simultaneous playing instances
  674. // of this sound. Useful for ui clicks to keep up with rapid input.
  675. //-----------------------------------------------------------------------------
  676. bool SetupWave( const char *pFilename, int numSimultaneous = 1 )
  677. {
  678. CUtlBuffer buf;
  679. if ( !g_pFullFileSystem->ReadFile( pFilename, "GAME", buf ) )
  680. {
  681. Msg( "SetupWave: File '%s' not found\n", pFilename );
  682. return false;
  683. }
  684. // only supporting xwv format
  685. xwvHeader_t* pHeader = (xwvHeader_t *)buf.Base();
  686. if ( pHeader->id != XWV_ID || pHeader->version != XWV_VERSION || pHeader->format != XWV_FORMAT_XMA )
  687. {
  688. Msg( "SetupWave: File '%s' has bad format\n", pFilename );
  689. return false;
  690. }
  691. XAUDIOSOURCEVOICEINIT SourceVoiceInit = { 0 };
  692. SourceVoiceInit.Format.SampleType = XAUDIOSAMPLETYPE_XMA;
  693. SourceVoiceInit.Format.NumStreams = 1;
  694. SourceVoiceInit.MaxPacketCount = 1;
  695. SourceVoiceInit.Format.Stream[0].SampleRate = pHeader->GetSampleRate();
  696. SourceVoiceInit.Format.Stream[0].ChannelCount = pHeader->channels;
  697. // create enough source voices to support simultaneous play of this sound
  698. HRESULT hr;
  699. numSimultaneous = min( numSimultaneous, ARRAYSIZE( m_pSourceVoices ) );
  700. for ( int i=0; i<numSimultaneous; i++ )
  701. {
  702. // create the voice
  703. hr = XAudioCreateSourceVoice( &SourceVoiceInit, &m_pSourceVoices[i] );
  704. if ( FAILED( hr ) )
  705. {
  706. return false;
  707. }
  708. }
  709. m_numVoices = numSimultaneous;
  710. // get the xma data
  711. m_nXMADataSize = pHeader->dataSize;
  712. m_pXMAData = (unsigned char *)XPhysicalAlloc( m_nXMADataSize, MAXULONG_PTR, 0, PAGE_READWRITE );
  713. V_memcpy( m_pXMAData, (unsigned char *)buf.Base() + pHeader->dataOffset, m_nXMADataSize );
  714. return true;
  715. }
  716. bool IsPlaying()
  717. {
  718. XAUDIOSOURCESTATE SourceState;
  719. for ( int i=0; i<m_numVoices; i++ )
  720. {
  721. m_pSourceVoices[i]->GetVoiceState( &SourceState );
  722. if ( SourceState & XAUDIOSOURCESTATE_STARTED )
  723. {
  724. return true;
  725. }
  726. }
  727. return false;
  728. }
  729. void Play()
  730. {
  731. int numPlaying = 0;
  732. XAUDIOSOURCESTATE SourceState;
  733. for ( int i=0; i<m_numVoices; i++ )
  734. {
  735. m_pSourceVoices[i]->GetVoiceState( &SourceState );
  736. if ( SourceState & XAUDIOSOURCESTATE_STARTED )
  737. {
  738. numPlaying++;
  739. }
  740. }
  741. if ( numPlaying >= m_numVoices )
  742. {
  743. return;
  744. }
  745. // find a free voice
  746. IXAudioSourceVoice *pVoice = NULL;
  747. for ( int i=0; i<m_numVoices; i++ )
  748. {
  749. m_pSourceVoices[i]->GetVoiceState( &SourceState );
  750. if ( !( SourceState & XAUDIOSOURCESTATE_STARTED ) )
  751. {
  752. // use the free voice
  753. pVoice = m_pSourceVoices[i];
  754. break;
  755. }
  756. }
  757. if ( !pVoice )
  758. {
  759. // none free
  760. return;
  761. }
  762. // Set up packet
  763. XAUDIOPACKET Packet = { 0 };
  764. Packet.pBuffer = m_pXMAData;
  765. Packet.BufferSize = m_nXMADataSize;
  766. // Submit packet
  767. HRESULT hr = pVoice->SubmitPacket( &Packet, XAUDIOSUBMITPACKET_DISCONTINUITY );
  768. if ( !FAILED( hr ) )
  769. {
  770. pVoice->Start( 0 );
  771. }
  772. }
  773. void Stop()
  774. {
  775. for ( int i=0; i<m_numVoices; i++ )
  776. {
  777. m_pSourceVoices[i]->Stop( 0 );
  778. }
  779. }
  780. void Release()
  781. {
  782. for ( int i=0; i<ARRAYSIZE( m_pSourceVoices ); i++ )
  783. {
  784. if ( m_pSourceVoices[i] )
  785. {
  786. m_pSourceVoices[i]->Release();
  787. m_pSourceVoices[i] = NULL;
  788. }
  789. }
  790. if ( m_pXMAData )
  791. {
  792. XPhysicalFree( m_pXMAData );
  793. m_pXMAData = NULL;
  794. }
  795. m_nXMADataSize = 0;
  796. m_numVoices = 0;
  797. }
  798. private:
  799. IXAudioSourceVoice *m_pSourceVoices[4];
  800. unsigned char *m_pXMAData;
  801. int m_nXMADataSize;
  802. int m_numVoices;
  803. };
  804. //-----------------------------------------------------------------------------
  805. // The application object
  806. //-----------------------------------------------------------------------------
  807. class CAppChooser : public CVguiSteamApp
  808. {
  809. typedef CVguiSteamApp BaseClass;
  810. public:
  811. virtual bool Create();
  812. virtual bool PreInit();
  813. virtual int Main();
  814. virtual void Destroy();
  815. void OnSelectionPrevious();
  816. void OnSelectionNext();
  817. void OnActivateGame();
  818. void OnInactivityTimeout();
  819. void OnStopDemoMovie();
  820. void OnDemoMovieEnd();
  821. void OnStopStartupMovie();
  822. void Reset();
  823. void ResetTimeout( float timeout );
  824. bool IsInputEnabled() { return m_bInputEnabled; }
  825. bool IsDemoMoviePlaying() { return m_bPlayingDemoMovie; }
  826. bool IsStartupMoviePlaying() { return m_bPlayingStartupMovies; }
  827. void HandleInvite( DWORD nUserId );
  828. private:
  829. const char *GetAppName() { return "AppChooser"; }
  830. bool CreateWindow( int width, int height, bool fullscreen );
  831. bool InitMaterialSystem();
  832. bool InitVGUI();
  833. void ShutdownVGUI();
  834. bool InitAudio();
  835. void ShutdownAudio();
  836. void FrameTick();
  837. void RenderScene();
  838. void ExitChooser();
  839. void EnableInput( bool bEnable );
  840. void StartExitingProcess();
  841. void SetLoadingIconPosition( bool bIsMultiplayer );
  842. int m_nScreenWidth;
  843. int m_nScreenHeight;
  844. HWND m_hWnd;
  845. float m_FadeInTime;
  846. float m_FadeOutTime;
  847. float m_StartTime;
  848. float m_Timeout;
  849. int m_Selection;
  850. int m_LastSelection;
  851. bool m_bInputEnabled;
  852. int m_ExitingFrameCount;
  853. float m_GameMovieStartTime;
  854. float m_BackgroundMovieStartTime;
  855. int m_LastBackgroundMovie;
  856. int m_StartupMovie;
  857. // various operating states
  858. bool m_bPlayingStartupMovies;
  859. bool m_bPlayingDemoMovie;
  860. bool m_bExiting;
  861. // Live invite handling
  862. bool m_bInviteAccepted;
  863. XNKID m_InviteSessionID;
  864. DWORD m_nInviteUserID;
  865. int m_iImageID;
  866. vgui::Panel *m_pRootPanel;
  867. CImage *m_pPersistedImage;
  868. CImage *m_pOverlay;
  869. CImage *m_pFullBlack;
  870. CMovieImage *m_pStartupMovieImage;
  871. CMovieImage *m_pBackgroundMovie;
  872. CMovieImage *m_pDemoMovieImage;
  873. CGamePanel *m_pGames[ARRAYSIZE( g_Games )];
  874. CImage *m_pProductTitle;
  875. CShadowLabel *m_pInstructionsShadow;
  876. vgui::Label *m_pInstructions;
  877. vgui::Label *m_pLoading;
  878. CImage *m_pLoadingIcon;
  879. CImage *m_pProductBackgrounds[ARRAYSIZE( g_Games )];
  880. DWORD m_iStorageDeviceID;
  881. DWORD m_iUserIdx;
  882. KeyValues *m_pKVSettings;
  883. int m_DemoMovie;
  884. CXSound m_Click;
  885. CXSound m_Clack;
  886. CXSound m_Deny;
  887. };
  888. CAppChooser g_AppChooserSystem;
  889. //--------------------------------------------------------------------------------------
  890. // InitMaterialSystem
  891. //
  892. //--------------------------------------------------------------------------------------
  893. bool CAppChooser::InitMaterialSystem()
  894. {
  895. RECT rect;
  896. MaterialSystem_Config_t config;
  897. config.SetFlag( MATSYS_VIDCFG_FLAGS_WINDOWED, IsPC() ? true : false );
  898. config.SetFlag( MATSYS_VIDCFG_FLAGS_NO_WAIT_FOR_VSYNC, 0 );
  899. config.m_VideoMode.m_Width = 0;
  900. config.m_VideoMode.m_Height = 0;
  901. config.m_VideoMode.m_Format = IMAGE_FORMAT_BGRX8888;
  902. config.m_VideoMode.m_RefreshRate = 0;
  903. config.dxSupportLevel = IsX360() ? 98 : 0;
  904. g_pMaterialSystem->ModInit();
  905. bool modeSet = g_pMaterialSystem->SetMode( m_hWnd, config );
  906. if ( !modeSet )
  907. {
  908. Error( "Failed to set mode\n" );
  909. return false;
  910. }
  911. g_pMaterialSystem->OverrideConfig( config, false );
  912. GetClientRect( m_hWnd, &rect );
  913. m_nScreenWidth = rect.right;
  914. m_nScreenHeight = rect.bottom;
  915. return true;
  916. }
  917. void CAppChooser::SetLoadingIconPosition( bool bIsMultiplayer )
  918. {
  919. // matches config from matsys_interface.cpp::InitStartupScreen()
  920. float flNormalizedX;
  921. float flNormalizedY;
  922. float flNormalizedSize;
  923. if ( !bIsMultiplayer )
  924. {
  925. flNormalizedX = 0.5f;
  926. flNormalizedY = 0.86f;
  927. flNormalizedSize = 0.1f;
  928. }
  929. else
  930. {
  931. flNormalizedX = 0.5f;
  932. flNormalizedY = 0.9f;
  933. flNormalizedSize = 0.1f;
  934. }
  935. // matches calcs from CShaderDeviceDx8::RefreshFrontBufferNonInteractive()
  936. float flXPos = flNormalizedX;
  937. float flYPos = flNormalizedY;
  938. float flHeight = flNormalizedSize;
  939. int nSize = m_nScreenHeight * flHeight;
  940. int x = m_nScreenWidth * flXPos - nSize * 0.5f;
  941. int y = m_nScreenHeight * flYPos - nSize * 0.5f;
  942. int w = nSize;
  943. int h = nSize;
  944. m_pLoadingIcon->SetBounds( x, y, w, h );
  945. }
  946. //-----------------------------------------------------------------------------
  947. // Setup all our VGUI info
  948. //-----------------------------------------------------------------------------
  949. bool CAppChooser::InitVGUI( void )
  950. {
  951. int x, y, w, h, g;
  952. KeyValues *pKV;
  953. vgui::surface()->GetScreenSize( w, h );
  954. float aspectRatio = (float)w/(float)h;
  955. bool bIsWidescreen = ( aspectRatio >= 1.7f );
  956. bool bIsHiDef = h > 480;
  957. const char *pResolutionKey = "";
  958. if ( bIsWidescreen )
  959. {
  960. // 16:9 aspect
  961. if ( bIsHiDef )
  962. {
  963. pResolutionKey = "_hidef";
  964. }
  965. else
  966. {
  967. pResolutionKey = "_lodef_wide";
  968. }
  969. }
  970. else
  971. {
  972. // 4:3 apsect
  973. if ( bIsHiDef )
  974. {
  975. pResolutionKey = "_hidef_norm";
  976. }
  977. else
  978. {
  979. pResolutionKey = "_lodef";
  980. }
  981. }
  982. // Start vgui
  983. vgui::ivgui()->Start();
  984. vgui::ivgui()->SetSleep( false );
  985. // load the scheme
  986. vgui::scheme()->LoadSchemeFromFile( "resource/sourcescheme.res", NULL );
  987. vgui::HScheme hScheme = vgui::scheme()->GetScheme( "SourceScheme" );
  988. vgui::IScheme *pSourceScheme = vgui::scheme()->GetIScheme( hScheme );
  989. m_pKVSettings = new KeyValues( "AppChooser.res" );
  990. if ( m_pKVSettings->LoadFromFile( g_pFullFileSystem, "resource/UI/AppChooser.res", "GAME" ) )
  991. {
  992. m_pKVSettings->ProcessResolutionKeys( pResolutionKey );
  993. }
  994. else
  995. {
  996. return false;
  997. }
  998. // localization
  999. g_pVGuiLocalize->AddFile( "resource/gameui_%language%.txt" ,"GAME", true );
  1000. // Init the root panel
  1001. m_pRootPanel = new vgui::Panel( NULL, "RootPanel" );
  1002. m_pRootPanel->SetBounds( 0, 0, m_nScreenWidth, m_nScreenHeight );
  1003. m_pRootPanel->SetPaintBackgroundEnabled( true );
  1004. m_pRootPanel->SetVisible( true );
  1005. vgui::surface()->SetEmbeddedPanel( m_pRootPanel->GetVPanel() );
  1006. // need a full pure opaque black before all panel drawing
  1007. // this fixes the top of frame xmv video render work
  1008. m_pFullBlack = new CImage( m_pRootPanel, "FullBlack", "vgui/black" );
  1009. SETUP_PANEL( m_pFullBlack );
  1010. m_pFullBlack->SetBounds( 0, 0, m_nScreenWidth, m_nScreenHeight );
  1011. m_pFullBlack->SetVisible( true );
  1012. // all the background movies were authored for widescreen
  1013. m_pBackgroundMovie = new CMovieImage( m_pRootPanel, "Movie", "Background", m_nScreenWidth, m_nScreenHeight, 0.0f );
  1014. SETUP_PANEL( m_pBackgroundMovie );
  1015. m_pBackgroundMovie->SetBounds( 0, 0, m_nScreenWidth, m_nScreenHeight );
  1016. m_pBackgroundMovie->SetAspectRatio( bIsWidescreen ? 1.0f : 1.778f, false );
  1017. m_pBackgroundMovie->SetColor( 255, 255, 255 );
  1018. m_pBackgroundMovie->SetVisible( true );
  1019. pKV = m_pKVSettings->FindKey( "Logo" );
  1020. y = pKV->GetInt( "ypos" );
  1021. w = pKV->GetInt( "wide" );
  1022. h = pKV->GetInt( "tall" );
  1023. m_pProductTitle = new CImage( m_pRootPanel, "Logo", "vgui/appchooser/orangeboxlogo" );
  1024. SETUP_PANEL( m_pProductTitle );
  1025. m_pProductTitle->SetBounds( ( m_nScreenWidth - w )/2, y, w, h );
  1026. m_pProductTitle->SetVisible( true );
  1027. wchar_t *pString = g_pVGuiLocalize->Find( "#GameUI_AppChooser_SelectGame" );
  1028. pKV = m_pKVSettings->FindKey( "SelectGame" );
  1029. y = pKV->GetInt( "ypos" );
  1030. m_pInstructionsShadow = new CShadowLabel( m_pRootPanel, "SelectGame", pString );
  1031. SETUP_PANEL( m_pInstructionsShadow );
  1032. m_pInstructionsShadow->SetFont( pSourceScheme->GetFont( "ChapterTitleBlur" ) );
  1033. m_pInstructionsShadow->SizeToContents();
  1034. m_pInstructionsShadow->SetWide( m_nScreenWidth );
  1035. m_pInstructionsShadow->SetPos( 0, y );
  1036. m_pInstructionsShadow->SetContentAlignment( vgui::Label::a_center );
  1037. m_pInstructionsShadow->SetPaintBackgroundEnabled( false );
  1038. m_pInstructionsShadow->SetFgColor( Color( 0, 0, 0, 255) );
  1039. m_pInstructions = new vgui::Label( m_pRootPanel, "SelectGame", pString );
  1040. SETUP_PANEL( m_pInstructions );
  1041. m_pInstructions->SetFont( pSourceScheme->GetFont( "ChapterTitle" ) );
  1042. m_pInstructions->SizeToContents();
  1043. m_pInstructions->SetWide( m_nScreenWidth );
  1044. m_pInstructions->SetPos( 0, y );
  1045. m_pInstructions->SetContentAlignment( vgui::Label::a_center );
  1046. m_pInstructions->SetPaintBackgroundEnabled( false );
  1047. m_pInstructions->SetFgColor( Color( 255, 255, 255, 255) );
  1048. pKV = m_pKVSettings->FindKey( "GamePanel" );
  1049. w = pKV->GetInt( "wide" );
  1050. g = pKV->GetInt( "gap" );
  1051. x = ( m_nScreenWidth - ( (int)( ARRAYSIZE( g_Games ) ) * ( w + g ) - g ) ) / 2;
  1052. y = pKV->GetInt( "ypos" );
  1053. for ( int i=0; i<ARRAYSIZE( g_Games ); i++ )
  1054. {
  1055. m_pGames[i] = new CGamePanel( m_pRootPanel, "GamePanel", &g_Games[i], bIsWidescreen, m_pKVSettings );
  1056. SETUP_PANEL( m_pGames[i] );
  1057. m_pGames[i]->SetPos( x, y );
  1058. m_pGames[i]->SetPaintBackgroundType( 2 );
  1059. m_pGames[i]->SetSelected( false );
  1060. x += w + g;
  1061. }
  1062. // the product backgrounds are topmost and used as fade out materials
  1063. for ( int i=0; i<ARRAYSIZE( g_Games ); i++ )
  1064. {
  1065. char filename[MAX_PATH];
  1066. V_snprintf( filename, sizeof( filename ), "vgui/appchooser/background_%s%s", g_Games[i].pGameDir, bIsWidescreen ? "_widescreen" : "" );
  1067. m_pProductBackgrounds[i] = new CImage( m_pRootPanel, "Products", filename );
  1068. SETUP_PANEL( m_pProductBackgrounds[i] );
  1069. m_pProductBackgrounds[i]->SetBounds( 0, 0, m_nScreenWidth, m_nScreenHeight );
  1070. m_pProductBackgrounds[i]->SetVisible( true );
  1071. m_pProductBackgrounds[i]->SetAlpha( 0 );
  1072. }
  1073. pString = g_pVGuiLocalize->Find( "#GameUI_Loading" );
  1074. y = m_nScreenHeight / 2;
  1075. m_pLoading = new vgui::Label( m_pRootPanel, "Text", pString );
  1076. SETUP_PANEL( m_pLoading );
  1077. m_pLoading->SetFont( pSourceScheme->GetFont( "ChapterTitle" ) );
  1078. m_pLoading->SizeToContents();
  1079. m_pLoading->SetWide( m_nScreenWidth );
  1080. m_pLoading->SetPos( 0, y );
  1081. m_pLoading->SetContentAlignment( vgui::Label::a_center );
  1082. m_pLoading->SetPaintBackgroundEnabled( false );
  1083. m_pLoading->SetFgColor( Color( 255, 255, 255, 255) );
  1084. m_pLoading->SetVisible( false );
  1085. m_pLoadingIcon = new CImage( m_pRootPanel, "LoadingIcon", "vgui/appchooser/loading_icon" );
  1086. SETUP_PANEL( m_pLoadingIcon );
  1087. SetLoadingIconPosition( false );
  1088. m_pLoadingIcon->SetVisible( false );
  1089. m_pLoadingIcon->SetAlpha( 0 );
  1090. // create back buffer cloned texture
  1091. ITexture *pTexture = NULL;
  1092. if ( XboxLaunch()->GetLaunchFlags() & LF_INTERNALLAUNCH )
  1093. {
  1094. pTexture = g_pMaterialSystem->CreateProceduralTexture(
  1095. "PersistedTexture",
  1096. TEXTURE_GROUP_OTHER,
  1097. m_nScreenWidth,
  1098. m_nScreenHeight,
  1099. g_pMaterialSystem->GetBackBufferFormat(),
  1100. TEXTUREFLAGS_NOMIP | TEXTUREFLAGS_SINGLECOPY );
  1101. // the persisted texture is in the back buffer, get it
  1102. CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
  1103. pRenderContext->CopyRenderTargetToTexture( pTexture );
  1104. }
  1105. // create a material to bind the persisted texture
  1106. // the fade-in material is topmost, fully opaque, and fades to transluscent on initial rendering
  1107. KeyValues *pVMTKeyValues = new KeyValues( "UnlitGeneric" );
  1108. pVMTKeyValues->SetInt( "$vertexcolor", 1 );
  1109. pVMTKeyValues->SetInt( "$vertexalpha", 1 );
  1110. pVMTKeyValues->SetInt( "$linearwrite", 1 ); // These 2 lines are needed so that we don't lose bits of precision in
  1111. pVMTKeyValues->SetInt( "$gammacolorread", 1 ); // the dark colors due to the 360's lossy sRGB read hardware
  1112. pVMTKeyValues->SetInt( "$ignorez", 1 );
  1113. if ( pTexture )
  1114. {
  1115. pVMTKeyValues->SetString( "$basetexture", "PersistedTexture" );
  1116. }
  1117. else
  1118. {
  1119. // there is no persisted texture, fade in from black
  1120. pVMTKeyValues->SetString( "$basetexture", "vgui/black" );
  1121. }
  1122. IMaterial *pFadeInMaterial = g_pMaterialSystem->CreateMaterial( "__FadeInMaterial.vmt", pVMTKeyValues );
  1123. // the persisted image is either the image from the relaunch or black during first boot
  1124. m_pPersistedImage = new CImage( m_pRootPanel, "FadeInOverlay", pFadeInMaterial );
  1125. SETUP_PANEL( m_pPersistedImage );
  1126. m_pPersistedImage->SetBounds( 0, 0, m_nScreenWidth, m_nScreenHeight );
  1127. m_pPersistedImage->SetVisible( true );
  1128. m_pPersistedImage->SetAlpha( 255 );
  1129. m_pOverlay = m_pPersistedImage;
  1130. // full screen demo movie, use letterboxing
  1131. m_pDemoMovieImage = new CMovieImage( m_pRootPanel, "Movie", "Demo", m_nScreenWidth, m_nScreenHeight, 2.0f );
  1132. SETUP_PANEL( m_pDemoMovieImage );
  1133. m_pDemoMovieImage->SetBounds( 0, 0, m_nScreenWidth, m_nScreenHeight );
  1134. m_pDemoMovieImage->SetAspectRatio( bIsWidescreen ? 1.0f : 1.778f, true );
  1135. m_pDemoMovieImage->SetVisible( true );
  1136. // full screen startup movies, use letterboxing, instant start
  1137. m_pStartupMovieImage = new CMovieImage( m_pRootPanel, "Movie", "Startup", m_nScreenWidth, m_nScreenHeight, 0.0f );
  1138. SETUP_PANEL( m_pStartupMovieImage );
  1139. m_pStartupMovieImage->SetBounds( 0, 0, m_nScreenWidth, m_nScreenHeight );
  1140. m_pStartupMovieImage->SetAspectRatio( bIsWidescreen ? 1.0f : 1.778f, true );
  1141. m_pStartupMovieImage->SetVisible( true );
  1142. return true;
  1143. }
  1144. //-----------------------------------------------------------------------------
  1145. // Stop VGUI
  1146. //-----------------------------------------------------------------------------
  1147. void CAppChooser::ShutdownVGUI( void )
  1148. {
  1149. delete m_pRootPanel;
  1150. }
  1151. //-----------------------------------------------------------------------------
  1152. // Initialize Audio
  1153. //-----------------------------------------------------------------------------
  1154. bool CAppChooser::InitAudio()
  1155. {
  1156. // Set up initialize parameters of XAudio Engine
  1157. // Both threads on core 2
  1158. XAUDIOENGINEINIT EngineInit = { 0 };
  1159. EngineInit.pEffectTable = &XAudioDefaultEffectTable;
  1160. EngineInit.ThreadUsage = XAUDIOTHREADUSAGE_THREAD4 | XAUDIOTHREADUSAGE_THREAD5;
  1161. // Initialize the XAudio Engine
  1162. HRESULT hr = XAudioInitialize( &EngineInit );
  1163. if ( FAILED( hr ) )
  1164. {
  1165. Error( "Error calling XAudioInitialize\n" );
  1166. }
  1167. m_Click.SetupWave( "sound/ui/buttonclick.360.wav", 2 );
  1168. m_Clack.SetupWave( "sound/ui/buttonclickrelease.360.wav", 2 );
  1169. m_Deny.SetupWave( "sound/player/suit_denydevice.360.wav", 2 );
  1170. return true;
  1171. }
  1172. void CAppChooser::ShutdownAudio()
  1173. {
  1174. // Shut down and free XAudio resources
  1175. XAudioShutDown();
  1176. }
  1177. //--------------------------------------------------------------------------------------
  1178. // Reset timeout
  1179. //--------------------------------------------------------------------------------------
  1180. void CAppChooser::ResetTimeout( float timeout )
  1181. {
  1182. if ( timeout > 0 )
  1183. {
  1184. m_Timeout = Plat_FloatTime() + timeout;
  1185. }
  1186. else
  1187. {
  1188. m_Timeout = 0;
  1189. }
  1190. }
  1191. //--------------------------------------------------------------------------------------
  1192. // Intialization Reset. Expected to be called once after inits.
  1193. //--------------------------------------------------------------------------------------
  1194. void CAppChooser::Reset()
  1195. {
  1196. // set selection to previous game or default
  1197. m_Selection = 0;
  1198. m_LastSelection = 0;
  1199. const char *pGameName = CommandLine()->ParmValue( "-game", "ep2" );
  1200. for ( int i=0; i<ARRAYSIZE( g_Games ); i++ )
  1201. {
  1202. if ( V_stristr( pGameName, g_Games[i].pGameDir ) )
  1203. {
  1204. m_Selection = i;
  1205. break;
  1206. }
  1207. }
  1208. // get the storage device
  1209. m_iStorageDeviceID = XboxLaunch()->GetStorageID();
  1210. Msg( "Storage ID: %d", m_iStorageDeviceID );
  1211. // Set the user index
  1212. m_iUserIdx = XboxLaunch()->GetUserID();
  1213. if ( g_pInputSystem )
  1214. {
  1215. g_pInputSystem->SetPrimaryUserId( m_iUserIdx );
  1216. }
  1217. XBX_SetPrimaryUserId( m_iUserIdx );
  1218. Msg( "User ID: %d", m_iUserIdx );
  1219. m_StartTime = Plat_FloatTime();
  1220. m_FadeInTime = 0;
  1221. m_FadeOutTime = 0;
  1222. m_bExiting = false;
  1223. m_ExitingFrameCount = 0;
  1224. m_bPlayingDemoMovie = false;
  1225. m_DemoMovie = 0;
  1226. // background movie and selection startup in sync
  1227. m_LastBackgroundMovie = m_Selection;
  1228. m_BackgroundMovieStartTime = 0;
  1229. m_GameMovieStartTime = 0;
  1230. EnableInput( true );
  1231. if ( !( XboxLaunch()->GetLaunchFlags() & LF_INTERNALLAUNCH ) )
  1232. {
  1233. // first time boot
  1234. // startup movies play first, and never again
  1235. ResetTimeout( 0 );
  1236. m_StartupMovie = -1;
  1237. m_bPlayingStartupMovies = true;
  1238. }
  1239. else
  1240. {
  1241. // normal background startup
  1242. ResetTimeout( INACTIVITY_TIMEOUT );
  1243. // foreground movie starts a little staggered
  1244. m_BackgroundMovieStartTime = Plat_FloatTime();
  1245. m_GameMovieStartTime = m_BackgroundMovieStartTime + 1.0f;
  1246. }
  1247. // Init our invite data
  1248. m_bInviteAccepted = false;
  1249. m_nInviteUserID = XBX_INVALID_USER_ID;
  1250. memset( (void *)&m_InviteSessionID, 0, sizeof( m_InviteSessionID ) );
  1251. }
  1252. //--------------------------------------------------------------------------------------
  1253. // Handle previous selection
  1254. //--------------------------------------------------------------------------------------
  1255. void CAppChooser::OnSelectionPrevious()
  1256. {
  1257. // backward wraparound
  1258. m_Selection = ( m_Selection + ARRAYSIZE( g_Games ) - 1 ) % ARRAYSIZE( g_Games );
  1259. m_Click.Play();
  1260. }
  1261. //--------------------------------------------------------------------------------------
  1262. // Handle next selection
  1263. //--------------------------------------------------------------------------------------
  1264. void CAppChooser::OnSelectionNext()
  1265. {
  1266. // forward wraparound
  1267. m_Selection = ( m_Selection + ARRAYSIZE( g_Games ) + 1 ) % ARRAYSIZE( g_Games );
  1268. m_Click.Play();
  1269. }
  1270. void CAppChooser::EnableInput( bool bEnable )
  1271. {
  1272. m_bInputEnabled = bEnable;
  1273. }
  1274. //--------------------------------------------------------------------------------------
  1275. // Chooser exits
  1276. //--------------------------------------------------------------------------------------
  1277. void CAppChooser::ExitChooser()
  1278. {
  1279. // reset stale arguments that encode prior game
  1280. // launcher will establish correct arguments based on desired game
  1281. CommandLine()->RemoveParm( "-game" );
  1282. CommandLine()->AppendParm( "-game", g_Games[m_Selection].pGameDir );
  1283. // Special command line parameter for tf. Command line args persist across
  1284. // relaunches, so remove it first in case we came from tf and are going to another game.
  1285. CommandLine()->RemoveParm( "-swapcores" );
  1286. if ( !Q_stricmp( g_Games[m_Selection].pGameDir, "tf" ) )
  1287. {
  1288. CommandLine()->AppendParm( "-swapcores", NULL );
  1289. }
  1290. int fFlags = LF_EXITFROMCHOOSER;
  1291. // allocate the full payload
  1292. int nPayloadSize = XboxLaunch()->MaxPayloadSize();
  1293. byte *pPayload = (byte *)stackalloc( nPayloadSize );
  1294. V_memset( pPayload, 0, nPayloadSize );
  1295. // payload is at least the command line
  1296. // any user data needed must be placed AFTER the command line
  1297. const char *pCmdLine = CommandLine()->GetCmdLine();
  1298. int nCmdLineLength = (int)strlen( pCmdLine ) + 1;
  1299. V_memcpy( pPayload, pCmdLine, min( nPayloadSize, nCmdLineLength ) );
  1300. // add any other data here to payload, after the command line
  1301. // ...
  1302. XboxLaunch()->SetStorageID( m_iStorageDeviceID );
  1303. m_iUserIdx = XBX_GetPrimaryUserId();
  1304. if ( m_bInviteAccepted )
  1305. {
  1306. // A potentially different user was invited, so we need to connect them
  1307. m_iUserIdx = m_nInviteUserID;
  1308. }
  1309. XboxLaunch()->SetUserID( m_iUserIdx );
  1310. if ( m_bInviteAccepted )
  1311. {
  1312. // In the case of an invitation acceptance, we need to pack extra data into the payload
  1313. fFlags |= LF_INVITERESTART;
  1314. XboxLaunch()->SetInviteSessionID( &m_InviteSessionID );
  1315. }
  1316. // Save out the data
  1317. bool bLaunch = XboxLaunch()->SetLaunchData( (void *)pPayload, nPayloadSize, fFlags );
  1318. if ( bLaunch )
  1319. {
  1320. COM_TimestampedLog( "Launching: \"%s\" Flags: 0x%8.8x", pCmdLine, XboxLaunch()->GetLaunchFlags() );
  1321. g_pMaterialSystem->PersistDisplay();
  1322. ShutdownVGUI();
  1323. ShutdownAudio();
  1324. XBX_DisconnectConsoleMonitor();
  1325. XboxLaunch()->Launch();
  1326. }
  1327. }
  1328. //--------------------------------------------------------------------------------------
  1329. // Handle game selection
  1330. //--------------------------------------------------------------------------------------
  1331. void CAppChooser::OnActivateGame()
  1332. {
  1333. if ( !m_FadeInTime || Plat_FloatTime() <= m_FadeInTime + 1.0f )
  1334. {
  1335. // lockout user selection input until screen has stable rendering and completed its fade in
  1336. // prevents button mashing doing an immediate game selection just as the startup movies end
  1337. return;
  1338. }
  1339. if ( !g_Games[m_Selection].bEnabled )
  1340. {
  1341. m_Deny.Play();
  1342. return;
  1343. }
  1344. m_Clack.Play();
  1345. while ( m_Clack.IsPlaying() )
  1346. {
  1347. // let the audio complete
  1348. Sleep( 1 );
  1349. }
  1350. StartExitingProcess();
  1351. }
  1352. void CAppChooser::OnDemoMovieEnd()
  1353. {
  1354. g_AppChooserSystem.ResetTimeout( INACTIVITY_TIMEOUT );
  1355. m_bPlayingDemoMovie = false;
  1356. }
  1357. void DemoMovieCallback()
  1358. {
  1359. g_AppChooserSystem.OnDemoMovieEnd();
  1360. }
  1361. //--------------------------------------------------------------------------------------
  1362. // Handle inactivity event
  1363. //--------------------------------------------------------------------------------------
  1364. void CAppChooser::OnInactivityTimeout()
  1365. {
  1366. // no further inactivity timeouts
  1367. ResetTimeout( 0 );
  1368. const char *pDemoMovieName = g_DemoMovies[m_DemoMovie];
  1369. m_DemoMovie++;
  1370. if ( m_DemoMovie >= ARRAYSIZE( g_DemoMovies ) )
  1371. {
  1372. // reset
  1373. m_DemoMovie = 0;
  1374. }
  1375. m_bPlayingDemoMovie = m_pDemoMovieImage->StartMovieFromFile( pDemoMovieName, true, false, DemoMovieCallback );
  1376. if ( !m_bPlayingDemoMovie )
  1377. {
  1378. // try again, later
  1379. ResetTimeout( INACTIVITY_TIMEOUT );
  1380. }
  1381. }
  1382. //--------------------------------------------------------------------------------------
  1383. // Handle demo stop request
  1384. //--------------------------------------------------------------------------------------
  1385. void CAppChooser::OnStopDemoMovie()
  1386. {
  1387. m_pDemoMovieImage->StopMovie();
  1388. }
  1389. //--------------------------------------------------------------------------------------
  1390. // Handle startup stop request
  1391. //--------------------------------------------------------------------------------------
  1392. void CAppChooser::OnStopStartupMovie()
  1393. {
  1394. if ( m_StartupMovie >= 0 && g_StartupMovies[m_StartupMovie].bUserCanSkip )
  1395. {
  1396. m_pStartupMovieImage->StopMovie();
  1397. }
  1398. }
  1399. //--------------------------------------------------------------------------------------
  1400. // Setup the exiting context. Cannot be aborted.
  1401. //--------------------------------------------------------------------------------------
  1402. void CAppChooser::StartExitingProcess()
  1403. {
  1404. bool bIsMultiplayer = V_stricmp( g_Games[m_Selection].pGameDir, "tf" ) == 0;
  1405. // stable rendering, start the fade out
  1406. m_pOverlay = m_pProductBackgrounds[m_Selection];
  1407. m_pLoading->SetVisible( true );
  1408. m_pLoading->SetAlpha( 0 );
  1409. SetLoadingIconPosition( bIsMultiplayer );
  1410. m_pLoadingIcon->SetVisible( true );
  1411. m_pLoadingIcon->SetAlpha( 0 );
  1412. m_FadeOutTime = Plat_FloatTime();
  1413. m_bExiting = true;
  1414. m_ExitingFrameCount = 0;
  1415. // ensure that nothing can stop the exiting process
  1416. // otherwise the player could rapidly select a game during the fade outs
  1417. ResetTimeout( 0 );
  1418. EnableInput( false );
  1419. }
  1420. //--------------------------------------------------------------------------------------
  1421. // Handle per frame update, prior to render
  1422. //--------------------------------------------------------------------------------------
  1423. void CAppChooser::FrameTick()
  1424. {
  1425. if ( m_ExitingFrameCount )
  1426. {
  1427. return;
  1428. }
  1429. g_TargetMovieVolume = 1.0f;
  1430. BOOL bControl;
  1431. if ( ERROR_SUCCESS == XMPTitleHasPlaybackControl(&bControl) )
  1432. {
  1433. g_TargetMovieVolume = bControl ? 1.0f : 0.0f;
  1434. }
  1435. if ( m_FadeInTime )
  1436. {
  1437. // fade in overlay goes from [1..0] and holds on 0
  1438. float t = ( Plat_FloatTime() - m_FadeInTime ) * 2.0f;
  1439. t = 1.0f - clamp( t, 0.0f, 1.0f );
  1440. m_pOverlay->SetAlpha ( t * 255.0f );
  1441. }
  1442. if ( m_FadeOutTime )
  1443. {
  1444. // fade out overlay goes from [0..1] and holds on 1
  1445. float t = ( Plat_FloatTime() - m_FadeOutTime ) * 2.0f;
  1446. t = clamp( t, 0.0f, 1.0f );
  1447. m_pOverlay->SetAlpha ( t * 255.0f );
  1448. }
  1449. for ( int i=0; i<ARRAYSIZE( g_Games ); i++ )
  1450. {
  1451. m_pGames[i]->SetSelected( false );
  1452. }
  1453. m_pGames[m_Selection]->SetSelected( true );
  1454. if ( m_bExiting )
  1455. {
  1456. m_pLoading->SetAlpha( m_pOverlay->GetAlpha() );
  1457. m_pLoadingIcon->SetAlpha( m_pOverlay->GetAlpha() );
  1458. if ( m_pOverlay->GetAlpha() == 255 )
  1459. {
  1460. // exiting needs to have fade overlay fully opaque before stopping background movie
  1461. m_pBackgroundMovie->StopMovie();
  1462. // exiting commences after all movies full release and enough frames have swapped to ensure stability
  1463. // strict time (frame rate) cannot be trusted, must allow a non trivial amount of presents
  1464. m_ExitingFrameCount = 30;
  1465. }
  1466. }
  1467. if ( m_bPlayingStartupMovies )
  1468. {
  1469. if ( m_pStartupMovieImage->IsFullyReleased() )
  1470. {
  1471. m_StartupMovie++;
  1472. if ( m_StartupMovie >= ARRAYSIZE( g_StartupMovies ) )
  1473. {
  1474. // end of cycle
  1475. m_StartupMovie = -1;
  1476. m_bPlayingStartupMovies = false;
  1477. }
  1478. else
  1479. {
  1480. m_bPlayingStartupMovies = m_pStartupMovieImage->StartMovieFromFile( g_StartupMovies[m_StartupMovie].pMovieName, true, false );
  1481. }
  1482. }
  1483. if ( !m_bPlayingStartupMovies )
  1484. {
  1485. ResetTimeout( INACTIVITY_TIMEOUT );
  1486. m_BackgroundMovieStartTime = Plat_FloatTime();
  1487. m_GameMovieStartTime = m_BackgroundMovieStartTime + 1.0f;
  1488. }
  1489. else
  1490. {
  1491. return;
  1492. }
  1493. }
  1494. if ( m_bInviteAccepted )
  1495. {
  1496. // stop attract mode
  1497. if ( m_bPlayingDemoMovie )
  1498. {
  1499. // hint the demo movie to stop
  1500. OnStopDemoMovie();
  1501. }
  1502. // attract movies must finish before allowing invite
  1503. // background movies must finish before allowing invite
  1504. // starts the exiting process ONCE
  1505. if ( !m_bPlayingStartupMovies && !m_bPlayingDemoMovie && !m_bExiting )
  1506. {
  1507. for ( int i = 0; i < ARRAYSIZE( g_Games ); i++ )
  1508. {
  1509. if ( V_stristr( "tf", g_Games[i].pGameDir ) )
  1510. {
  1511. // EVIL! spoof a user slection to invite only TF
  1512. m_Selection = i;
  1513. StartExitingProcess();
  1514. // must fixup state, invite could have happened very early, at any time, before the initial fade-in
  1515. // ensure no movies start
  1516. m_BackgroundMovieStartTime = 0;
  1517. m_GameMovieStartTime = 0;
  1518. // hack the fade times to match the expected state
  1519. if ( !m_FadeInTime || m_FadeInTime >= Plat_FloatTime() )
  1520. {
  1521. // can't fade out, invite occured before fade-in
  1522. // can only snap it because there is no background to fade from
  1523. m_pPersistedImage->SetAlpha( 0 );
  1524. m_FadeInTime = 0;
  1525. m_FadeOutTime = 0;
  1526. m_pOverlay->SetAlpha( 255 );
  1527. }
  1528. break;
  1529. }
  1530. }
  1531. }
  1532. }
  1533. if ( m_bExiting || m_bPlayingDemoMovie || m_LastSelection != m_Selection )
  1534. {
  1535. m_pGames[m_LastSelection]->StopMovie();
  1536. m_LastSelection = m_Selection;
  1537. // user can change selection very quickly
  1538. // lag the movie starting until selection settles
  1539. m_GameMovieStartTime = Plat_FloatTime() + 2.0f;
  1540. }
  1541. if ( !m_bExiting && !m_bPlayingDemoMovie && ( m_GameMovieStartTime != 0 ) && ( Plat_FloatTime() >= m_GameMovieStartTime ) )
  1542. {
  1543. // keep trying the current selection, it will eventually start
  1544. m_pGames[m_Selection]->StartMovie();
  1545. if ( m_LastBackgroundMovie != m_Selection )
  1546. {
  1547. m_pBackgroundMovie->StopMovie();
  1548. m_LastBackgroundMovie = m_Selection;
  1549. m_BackgroundMovieStartTime = Plat_FloatTime() + 2.0f;
  1550. }
  1551. }
  1552. // update the background movie, lags far behind any user selection
  1553. if ( !m_bExiting && !m_bPlayingDemoMovie && ( m_BackgroundMovieStartTime != 0 ) && ( Plat_FloatTime() >= m_BackgroundMovieStartTime ) )
  1554. {
  1555. char szFilename[MAX_PATH];
  1556. V_snprintf( szFilename, sizeof( szFilename ), "background_%s.wmv", g_Games[m_Selection].pGameDir );
  1557. // keep trying the current selection, it will eventually start
  1558. bool bStarted = m_pBackgroundMovie->StartMovieFromFile(
  1559. szFilename,
  1560. false,
  1561. true );
  1562. if ( bStarted )
  1563. {
  1564. m_LastBackgroundMovie = m_Selection;
  1565. }
  1566. }
  1567. }
  1568. //--------------------------------------------------------------------------------------
  1569. // Render the scene
  1570. //--------------------------------------------------------------------------------------
  1571. void CAppChooser::RenderScene()
  1572. {
  1573. FrameTick();
  1574. CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
  1575. g_pMaterialSystem->BeginFrame( Plat_FloatTime() );
  1576. pRenderContext->ClearColor3ub( 0, 0, 0 );
  1577. pRenderContext->ClearBuffers( true, true );
  1578. pRenderContext->Flush( true );
  1579. // render all movies, XMV needs to hijack D3D
  1580. bool bPreviousState = g_pMaterialSystem->OwnGPUResources( false );
  1581. {
  1582. m_pStartupMovieImage->RenderVideoFrame();
  1583. m_pBackgroundMovie->RenderVideoFrame();
  1584. m_pDemoMovieImage->RenderVideoFrame();
  1585. for ( int i=0; i<ARRAYSIZE( m_pGames ); i++ )
  1586. {
  1587. m_pGames[i]->m_pMovieImage->RenderVideoFrame();
  1588. }
  1589. }
  1590. g_pMaterialSystem->OwnGPUResources( bPreviousState );
  1591. pRenderContext->Viewport( 0, 0, m_nScreenWidth, m_nScreenHeight );
  1592. pRenderContext->MatrixMode( MATERIAL_PROJECTION );
  1593. pRenderContext->LoadIdentity();
  1594. pRenderContext->MatrixMode( MATERIAL_VIEW );
  1595. pRenderContext->LoadIdentity();
  1596. pRenderContext->MatrixMode( MATERIAL_MODEL );
  1597. pRenderContext->LoadIdentity();
  1598. pRenderContext->Flush( true );
  1599. vgui::ivgui()->RunFrame();
  1600. vgui::surface()->PaintTraverseEx( vgui::surface()->GetEmbeddedPanel() );
  1601. g_pMaterialSystem->EndFrame();
  1602. g_pMaterialSystem->SwapBuffers();
  1603. if ( !m_FadeInTime && !m_bPlayingStartupMovies && !m_bExiting )
  1604. {
  1605. // stable rendering, start the fade in
  1606. m_FadeInTime = Plat_FloatTime() + 1.0f;
  1607. }
  1608. // check for timeout
  1609. if ( m_Timeout && ( Plat_FloatTime() >= m_Timeout ) )
  1610. {
  1611. m_Timeout = 0;
  1612. OnInactivityTimeout();
  1613. }
  1614. if ( m_ExitingFrameCount > 1 && m_pBackgroundMovie->IsFullyReleased() && m_pGames[m_Selection]->m_pMovieImage->IsFullyReleased() )
  1615. {
  1616. // must wait for a few frames to settle to ensure frame buffer is stable
  1617. m_ExitingFrameCount--;
  1618. if ( m_ExitingFrameCount == 1 )
  1619. {
  1620. ExitChooser();
  1621. }
  1622. }
  1623. }
  1624. //-----------------------------------------------------------------------------
  1625. // Purpose:
  1626. //-----------------------------------------------------------------------------
  1627. void CAppChooser::HandleInvite( DWORD nUserId )
  1628. {
  1629. // invites are asynchronous and could happen at any time
  1630. if ( m_bInviteAccepted )
  1631. {
  1632. // already accepted
  1633. return;
  1634. }
  1635. // Collect our session data
  1636. XINVITE_INFO inviteInfo;
  1637. DWORD dwError = XInviteGetAcceptedInfo( nUserId, &inviteInfo );
  1638. if ( dwError != ERROR_SUCCESS )
  1639. {
  1640. return;
  1641. }
  1642. // We only care if we're asked to join an Orange Box session
  1643. if ( inviteInfo.dwTitleID != TITLEID_THE_ORANGE_BOX )
  1644. {
  1645. return;
  1646. }
  1647. // Store off the session ID and mark the invite as accepted internally
  1648. m_bInviteAccepted = true;
  1649. m_InviteSessionID = inviteInfo.hostInfo.sessionID;
  1650. m_nInviteUserID = nUserId;
  1651. // must wait for the startup movies or attract mode to end
  1652. // FrameTick() will detect the invite and transition
  1653. // the user has accpeted and cannot abort the invite
  1654. EnableInput( false );
  1655. ResetTimeout( 0 );
  1656. }
  1657. //--------------------------------------------------------------------------------------
  1658. // Window Proc
  1659. //--------------------------------------------------------------------------------------
  1660. LRESULT CALLBACK WndProc( HWND hWnd, UINT iMsg, WPARAM wParam, LPARAM lParam )
  1661. {
  1662. switch ( iMsg )
  1663. {
  1664. case WM_CLOSE:
  1665. g_bActive = false;
  1666. break;
  1667. case WM_DESTROY:
  1668. PostQuitMessage( 0 );
  1669. return 0L;
  1670. case WM_LIVE_INVITE_ACCEPTED:
  1671. g_AppChooserSystem.HandleInvite( LOWORD( lParam ) );
  1672. break;
  1673. case WM_XCONTROLLER_KEY:
  1674. if ( !g_AppChooserSystem.IsInputEnabled() )
  1675. {
  1676. break;
  1677. }
  1678. if ( g_AppChooserSystem.IsStartupMoviePlaying() )
  1679. {
  1680. // some startup movies can be aborted
  1681. switch ( wParam )
  1682. {
  1683. case KEY_XBUTTON_A:
  1684. case KEY_XBUTTON_START:
  1685. if ( LOWORD( lParam ) )
  1686. {
  1687. g_AppChooserSystem.OnStopStartupMovie();
  1688. }
  1689. }
  1690. // no other input is allowed during this state
  1691. break;
  1692. }
  1693. if ( g_AppChooserSystem.IsDemoMoviePlaying() )
  1694. {
  1695. // demo movies can be aborted
  1696. switch ( wParam )
  1697. {
  1698. case KEY_XBUTTON_A:
  1699. case KEY_XBUTTON_START:
  1700. if ( LOWORD( lParam ) )
  1701. {
  1702. g_AppChooserSystem.OnStopDemoMovie();
  1703. }
  1704. }
  1705. }
  1706. else
  1707. {
  1708. if ( LOWORD( lParam ) )
  1709. {
  1710. g_AppChooserSystem.ResetTimeout( INACTIVITY_TIMEOUT );
  1711. }
  1712. switch ( wParam )
  1713. {
  1714. case KEY_XBUTTON_LEFT:
  1715. case KEY_XSTICK1_LEFT:
  1716. if ( LOWORD( lParam ) )
  1717. {
  1718. g_AppChooserSystem.OnSelectionPrevious();
  1719. }
  1720. break;
  1721. case KEY_XBUTTON_RIGHT:
  1722. case KEY_XSTICK1_RIGHT:
  1723. if ( LOWORD( lParam ) )
  1724. {
  1725. g_AppChooserSystem.OnSelectionNext();
  1726. }
  1727. break;
  1728. case KEY_XBUTTON_A:
  1729. case KEY_XBUTTON_START:
  1730. if ( LOWORD( lParam ) )
  1731. {
  1732. g_AppChooserSystem.OnActivateGame();
  1733. }
  1734. break;
  1735. }
  1736. }
  1737. break;
  1738. }
  1739. return DefWindowProc( hWnd, iMsg, wParam, lParam );
  1740. }
  1741. //--------------------------------------------------------------------------------------
  1742. // CreateWindow
  1743. //
  1744. //--------------------------------------------------------------------------------------
  1745. bool CAppChooser::CreateWindow( int width, int height, bool bFullScreen )
  1746. {
  1747. HWND hWnd;
  1748. WNDCLASSEX wndClass;
  1749. DWORD dwStyle, dwExStyle;
  1750. int x, y, sx, sy;
  1751. if ( ( hWnd = FindWindow( GetAppName(), GetAppName() ) ) != NULL )
  1752. {
  1753. SetForegroundWindow( hWnd );
  1754. return true;
  1755. }
  1756. wndClass.cbSize = sizeof( wndClass );
  1757. wndClass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
  1758. wndClass.lpfnWndProc = ::WndProc;
  1759. wndClass.cbClsExtra = 0;
  1760. wndClass.cbWndExtra = 0;
  1761. wndClass.hInstance = (HINSTANCE)GetAppInstance();
  1762. wndClass.hIcon = 0;
  1763. wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
  1764. wndClass.hbrBackground = (HBRUSH)COLOR_GRAYTEXT;
  1765. wndClass.lpszMenuName = NULL;
  1766. wndClass.lpszClassName = GetAppName();
  1767. wndClass.hIconSm = 0;
  1768. if ( !RegisterClassEx( &wndClass ) )
  1769. {
  1770. Error( "Window class registration failed\n" );
  1771. return false;
  1772. }
  1773. if ( bFullScreen )
  1774. {
  1775. dwExStyle = WS_EX_TOPMOST;
  1776. dwStyle = WS_POPUP | WS_VISIBLE;
  1777. }
  1778. else
  1779. {
  1780. dwExStyle = 0;
  1781. dwStyle = WS_CAPTION | WS_SYSMENU;
  1782. }
  1783. x = y = 0;
  1784. sx = width;
  1785. sy = height;
  1786. hWnd = CreateWindowEx(
  1787. dwExStyle,
  1788. GetAppName(), // window class name
  1789. GetAppName(), // window caption
  1790. dwStyle | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, // window style
  1791. x, // initial x position
  1792. y, // initial y position
  1793. sx, // initial x size
  1794. sy, // initial y size
  1795. NULL, // parent window handle
  1796. NULL, // window menu handle
  1797. (HINSTANCE)GetAppInstance(),// program instance handle
  1798. NULL); // creation parameter
  1799. if ( hWnd == NULL )
  1800. {
  1801. return false;
  1802. }
  1803. m_hWnd = hWnd;
  1804. return true;
  1805. }
  1806. //-----------------------------------------------------------------------------
  1807. // Create
  1808. //-----------------------------------------------------------------------------
  1809. bool CAppChooser::Create()
  1810. {
  1811. AppSystemInfo_t appSystems[] =
  1812. {
  1813. { "filesystem_stdio.dll", QUEUEDLOADER_INTERFACE_VERSION },
  1814. { "materialsystem.dll", MATERIAL_SYSTEM_INTERFACE_VERSION },
  1815. { "inputsystem.dll", INPUTSYSTEM_INTERFACE_VERSION },
  1816. { "vgui2.dll", VGUI_IVGUI_INTERFACE_VERSION },
  1817. { "vguimatsurface.dll", VGUI_SURFACE_INTERFACE_VERSION },
  1818. { "", "" }
  1819. };
  1820. MathLib_Init( 2.2f, 2.2f, 0.0f, 2.0f );
  1821. SpewOutputFunc( g_DefaultSpewFunc );
  1822. // Add in the cvar factory
  1823. AppModule_t cvarModule = LoadModule( VStdLib_GetICVarFactory() );
  1824. AddSystem( cvarModule, CVAR_INTERFACE_VERSION );
  1825. // vxconsole - true will block (legacy behavior)
  1826. XBX_InitConsoleMonitor( false );
  1827. if ( !AddSystems( appSystems ) )
  1828. return false;
  1829. IMaterialSystem* pMaterialSystem = (IMaterialSystem*)FindSystem( MATERIAL_SYSTEM_INTERFACE_VERSION );
  1830. if ( !pMaterialSystem )
  1831. {
  1832. Error( "Failed to find %s\n", MATERIAL_SYSTEM_INTERFACE_VERSION );
  1833. return false;
  1834. }
  1835. if ( IsX360() )
  1836. {
  1837. IFileSystem* pFileSystem = (IFileSystem*)FindSystem( FILESYSTEM_INTERFACE_VERSION );
  1838. if ( !pFileSystem )
  1839. {
  1840. Error( "Failed to find %s\n", FILESYSTEM_INTERFACE_VERSION );
  1841. return false;
  1842. }
  1843. pFileSystem->LoadModule( "shaderapidx9.dll" );
  1844. }
  1845. pMaterialSystem->SetShaderAPI( "shaderapidx9.dll" );
  1846. return true;
  1847. }
  1848. //-----------------------------------------------------------------------------
  1849. // PreInit
  1850. //-----------------------------------------------------------------------------
  1851. bool CAppChooser::PreInit()
  1852. {
  1853. if ( !BaseClass::PreInit() )
  1854. return false;
  1855. if ( !g_pFullFileSystem || !g_pMaterialSystem )
  1856. {
  1857. Warning( "Unable to find required interfaces!\n" );
  1858. return false;
  1859. }
  1860. // Add paths...
  1861. if ( !SetupSearchPaths( NULL, false, true ) )
  1862. {
  1863. Error( "Failed to setup search paths\n" );
  1864. return false;
  1865. }
  1866. // Create the main program window and our viewport
  1867. int w = 640;
  1868. int h = 480;
  1869. if ( IsX360() )
  1870. {
  1871. w = GetSystemMetrics( SM_CXSCREEN );
  1872. h = GetSystemMetrics( SM_CYSCREEN );
  1873. }
  1874. if ( !CreateWindow( w, h, false ) )
  1875. {
  1876. ChangeDisplaySettings( 0, 0 );
  1877. Error( "Unable to create main window\n" );
  1878. return false;
  1879. }
  1880. ShowWindow( m_hWnd, SW_SHOWNORMAL );
  1881. UpdateWindow( m_hWnd );
  1882. SetForegroundWindow( m_hWnd );
  1883. SetFocus( m_hWnd );
  1884. XOnlineStartup();
  1885. return true;
  1886. }
  1887. //-----------------------------------------------------------------------------
  1888. // Destroy
  1889. //-----------------------------------------------------------------------------
  1890. void CAppChooser::Destroy()
  1891. {
  1892. XOnlineCleanup();
  1893. }
  1894. //-----------------------------------------------------------------------------
  1895. // Main
  1896. //-----------------------------------------------------------------------------
  1897. int CAppChooser::Main()
  1898. {
  1899. if ( !InitMaterialSystem() )
  1900. {
  1901. return 0;
  1902. }
  1903. if ( !InitVGUI() )
  1904. {
  1905. return 0;
  1906. }
  1907. if ( !InitAudio() )
  1908. {
  1909. return 0;
  1910. }
  1911. // post initialization reset
  1912. Reset();
  1913. // Setup a listener for invites
  1914. XBX_NotifyCreateListener( XNOTIFY_LIVE );
  1915. MSG msg;
  1916. while ( g_bActive == TRUE )
  1917. {
  1918. // Pump the XBox notifications
  1919. XBX_ProcessEvents();
  1920. while ( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
  1921. {
  1922. TranslateMessage( &msg );
  1923. DispatchMessage( &msg );
  1924. }
  1925. g_pInputSystem->PollInputState();
  1926. RenderScene();
  1927. }
  1928. ShutdownVGUI();
  1929. ShutdownAudio();
  1930. Destroy();
  1931. return 0;
  1932. }
  1933. //-----------------------------------------------------------------------------
  1934. // The entry point for the application
  1935. //-----------------------------------------------------------------------------
  1936. extern "C" __declspec(dllexport) int AppChooserMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
  1937. {
  1938. CommandLine()->CreateCmdLine( lpCmdLine );
  1939. SetAppInstance( hInstance );
  1940. CSteamApplication steamApplication( &g_AppChooserSystem );
  1941. steamApplication.Run();
  1942. return 0;
  1943. }