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.

668 lines
18 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "BaseSaveGameDialog.h"
  7. #include "filesystem.h"
  8. #include "savegame_version.h"
  9. #include "vgui_controls/PanelListPanel.h"
  10. #include "vgui_controls/Label.h"
  11. #include "vgui_controls/ImagePanel.h"
  12. #include "vgui_controls/Button.h"
  13. #include "tier1/utlbuffer.h"
  14. #include <stdio.h>
  15. #include <stdlib.h>
  16. #include "filesystem.h"
  17. #include "MouseMessageForwardingPanel.h"
  18. #include "TGAImagePanel.h"
  19. // memdbgon must be the last include file in a .cpp file!!!
  20. #include "tier0/memdbgon.h"
  21. using namespace vgui;
  22. #define TGA_IMAGE_PANEL_WIDTH 180
  23. #define TGA_IMAGE_PANEL_HEIGHT 100
  24. #define MAX_LISTED_SAVE_GAMES 128
  25. //-----------------------------------------------------------------------------
  26. // Purpose: Describes the layout of a same game pic
  27. //-----------------------------------------------------------------------------
  28. class CSaveGamePanel : public vgui::EditablePanel
  29. {
  30. DECLARE_CLASS_SIMPLE( CSaveGamePanel, vgui::EditablePanel );
  31. public:
  32. CSaveGamePanel( PanelListPanel *parent, const char *name, int saveGameListItemID ) : BaseClass( parent, name )
  33. {
  34. m_iSaveGameListItemID = saveGameListItemID;
  35. m_pParent = parent;
  36. m_pSaveGameImage = new CTGAImagePanel( this, "SaveGameImage" );
  37. m_pAutoSaveImage = new ImagePanel( this, "AutoSaveImage" );
  38. m_pSaveGameScreenshotBackground = new ImagePanel( this, "SaveGameScreenshotBackground" );
  39. m_pChapterLabel = new Label( this, "ChapterLabel", "" );
  40. m_pTypeLabel = new Label( this, "TypeLabel", "" );
  41. m_pElapsedTimeLabel = new Label( this, "ElapsedTimeLabel", "" );
  42. m_pFileTimeLabel = new Label( this, "FileTimeLabel", "" );
  43. CMouseMessageForwardingPanel *panel = new CMouseMessageForwardingPanel(this, NULL);
  44. panel->SetZPos(2);
  45. SetSize( 200, 140 );
  46. LoadControlSettings( "resource/SaveGamePanel.res" );
  47. m_FillColor = m_pSaveGameScreenshotBackground->GetFillColor();
  48. }
  49. void SetSaveGameInfo( SaveGameDescription_t &save )
  50. {
  51. // set the bitmap to display
  52. char tga[_MAX_PATH];
  53. Q_strncpy( tga, save.szFileName, sizeof(tga) );
  54. char *ext = strstr( tga, ".sav" );
  55. if ( ext )
  56. {
  57. strcpy( ext, ".tga" );
  58. }
  59. // If a TGA file exists then it is a user created savegame
  60. if ( g_pFullFileSystem->FileExists( tga ) )
  61. {
  62. m_pSaveGameImage->SetTGA( tga );
  63. }
  64. // If there is no TGA then it is either an autosave or the user TGA file has been deleted
  65. else
  66. {
  67. m_pSaveGameImage->SetVisible( false );
  68. m_pAutoSaveImage->SetVisible( true );
  69. m_pAutoSaveImage->SetImage( "resource\\autosave" );
  70. }
  71. // set the title text
  72. m_pChapterLabel->SetText( save.szComment );
  73. // type
  74. SetControlString( "TypeLabel", save.szType );
  75. SetControlString( "ElapsedTimeLabel", save.szElapsedTime );
  76. SetControlString( "FileTimeLabel", save.szFileTime );
  77. }
  78. MESSAGE_FUNC_INT( OnPanelSelected, "PanelSelected", state )
  79. {
  80. if ( state )
  81. {
  82. // set the text color to be orange, and the pic border to be orange
  83. m_pSaveGameScreenshotBackground->SetFillColor( m_SelectedColor );
  84. m_pChapterLabel->SetFgColor( m_SelectedColor );
  85. m_pTypeLabel->SetFgColor( m_SelectedColor );
  86. m_pElapsedTimeLabel->SetFgColor( m_SelectedColor );
  87. m_pFileTimeLabel->SetFgColor( m_SelectedColor );
  88. }
  89. else
  90. {
  91. m_pSaveGameScreenshotBackground->SetFillColor( m_FillColor );
  92. m_pChapterLabel->SetFgColor( m_TextColor );
  93. m_pTypeLabel->SetFgColor( m_TextColor );
  94. m_pElapsedTimeLabel->SetFgColor( m_TextColor );
  95. m_pFileTimeLabel->SetFgColor( m_TextColor );
  96. }
  97. PostMessage( m_pParent->GetVParent(), new KeyValues("PanelSelected") );
  98. }
  99. virtual void OnMousePressed( vgui::MouseCode code )
  100. {
  101. m_pParent->SetSelectedPanel( this );
  102. }
  103. virtual void ApplySchemeSettings( IScheme *pScheme )
  104. {
  105. m_TextColor = pScheme->GetColor( "NewGame.TextColor", Color(255, 255, 255, 255) );
  106. m_SelectedColor = pScheme->GetColor( "NewGame.SelectionColor", Color(255, 255, 255, 255) );
  107. BaseClass::ApplySchemeSettings( pScheme );
  108. }
  109. virtual void OnMouseDoublePressed( vgui::MouseCode code )
  110. {
  111. // call the panel
  112. OnMousePressed( code );
  113. PostMessage( m_pParent->GetParent(), new KeyValues("Command", "command", "loadsave") );
  114. }
  115. int GetSaveGameListItemID()
  116. {
  117. return m_iSaveGameListItemID;
  118. }
  119. private:
  120. vgui::PanelListPanel *m_pParent;
  121. vgui::Label *m_pChapterLabel;
  122. CTGAImagePanel *m_pSaveGameImage;
  123. ImagePanel *m_pAutoSaveImage;
  124. // things to change color when the selection changes
  125. ImagePanel *m_pSaveGameScreenshotBackground;
  126. Label *m_pTypeLabel;
  127. Label *m_pElapsedTimeLabel;
  128. Label *m_pFileTimeLabel;
  129. Color m_TextColor, m_FillColor, m_SelectedColor;
  130. int m_iSaveGameListItemID;
  131. };
  132. //-----------------------------------------------------------------------------
  133. // Purpose: Constructor
  134. //-----------------------------------------------------------------------------
  135. CBaseSaveGameDialog::CBaseSaveGameDialog( vgui::Panel *parent, const char *name ) : BaseClass( parent, name )
  136. {
  137. CreateSavedGamesList();
  138. ScanSavedGames();
  139. m_pLoadButton = new vgui::Button( this, "loadsave", "" );
  140. SetControlEnabled( "loadsave", false );
  141. }
  142. //-----------------------------------------------------------------------------
  143. // Purpose: Creates the load game display list
  144. //-----------------------------------------------------------------------------
  145. void CBaseSaveGameDialog::CreateSavedGamesList()
  146. {
  147. m_pGameList = new vgui::PanelListPanel( this, "listpanel_loadgame" );
  148. m_pGameList->SetFirstColumnWidth( 0 );
  149. }
  150. //-----------------------------------------------------------------------------
  151. // Purpose: returns the save file name of the selected item
  152. //-----------------------------------------------------------------------------
  153. int CBaseSaveGameDialog::GetSelectedItemSaveIndex()
  154. {
  155. CSaveGamePanel *panel = dynamic_cast<CSaveGamePanel *>(m_pGameList->GetSelectedPanel());
  156. if ( panel )
  157. {
  158. // find the panel in the list
  159. for ( int i = 0; i < m_SaveGames.Count(); i++ )
  160. {
  161. if ( i == panel->GetSaveGameListItemID() )
  162. {
  163. return i;
  164. }
  165. }
  166. }
  167. return m_SaveGames.InvalidIndex();
  168. }
  169. //-----------------------------------------------------------------------------
  170. // Purpose: builds save game list from directory
  171. //-----------------------------------------------------------------------------
  172. void CBaseSaveGameDialog::ScanSavedGames()
  173. {
  174. // populate list box with all saved games on record:
  175. char szDirectory[_MAX_PATH];
  176. Q_snprintf( szDirectory, sizeof( szDirectory ), "save/*.sav" );
  177. // clear the current list
  178. m_pGameList->DeleteAllItems();
  179. m_SaveGames.RemoveAll();
  180. // iterate the saved files
  181. FileFindHandle_t handle;
  182. const char *pFileName = g_pFullFileSystem->FindFirst( szDirectory, &handle );
  183. while (pFileName)
  184. {
  185. if ( !Q_strnicmp(pFileName, "HLSave", strlen( "HLSave" ) ) )
  186. {
  187. pFileName = g_pFullFileSystem->FindNext( handle );
  188. continue;
  189. }
  190. char szFileName[_MAX_PATH];
  191. Q_snprintf(szFileName, sizeof( szFileName ), "save/%s", pFileName);
  192. // Only load save games from the current mod's save dir
  193. if( !g_pFullFileSystem->FileExists( szFileName, "MOD" ) )
  194. {
  195. pFileName = g_pFullFileSystem->FindNext( handle );
  196. continue;
  197. }
  198. SaveGameDescription_t save;
  199. if ( ParseSaveData( szFileName, pFileName, save ) )
  200. {
  201. m_SaveGames.AddToTail( save );
  202. }
  203. pFileName = g_pFullFileSystem->FindNext( handle );
  204. }
  205. g_pFullFileSystem->FindClose( handle );
  206. // notify derived classes that save games are being scanned (so they can insert their own)
  207. OnScanningSaveGames();
  208. // sort the save list
  209. qsort( m_SaveGames.Base(), m_SaveGames.Count(), sizeof(SaveGameDescription_t), &SaveGameSortFunc );
  210. // add to the list
  211. for ( int saveIndex = 0; saveIndex < m_SaveGames.Count() && saveIndex < MAX_LISTED_SAVE_GAMES; saveIndex++ )
  212. {
  213. // add the item to the panel
  214. AddSaveGameItemToList( saveIndex );
  215. }
  216. // display a message if there are no save games
  217. if ( !m_SaveGames.Count() )
  218. {
  219. vgui::Label *pNoSavesLabel = SETUP_PANEL(new Label(m_pGameList, "NoSavesLabel", "#GameUI_NoSaveGamesToDisplay"));
  220. pNoSavesLabel->SetTextColorState(vgui::Label::CS_DULL);
  221. m_pGameList->AddItem( NULL, pNoSavesLabel );
  222. }
  223. SetControlEnabled( "loadsave", false );
  224. SetControlEnabled( "delete", false );
  225. }
  226. //-----------------------------------------------------------------------------
  227. // Purpose: Adds an item to the list
  228. //-----------------------------------------------------------------------------
  229. void CBaseSaveGameDialog::AddSaveGameItemToList( int saveIndex )
  230. {
  231. // create the new panel and add to the list
  232. CSaveGamePanel *saveGamePanel = new CSaveGamePanel( m_pGameList, "SaveGamePanel", saveIndex );
  233. saveGamePanel->SetSaveGameInfo( m_SaveGames[saveIndex] );
  234. m_pGameList->AddItem( NULL, saveGamePanel );
  235. }
  236. //-----------------------------------------------------------------------------
  237. // Purpose: Parses the save game info out of the .sav file header
  238. //-----------------------------------------------------------------------------
  239. bool CBaseSaveGameDialog::ParseSaveData( char const *pszFileName, char const *pszShortName, SaveGameDescription_t &save )
  240. {
  241. char szMapName[SAVEGAME_MAPNAME_LEN];
  242. char szComment[SAVEGAME_COMMENT_LEN];
  243. char szElapsedTime[SAVEGAME_ELAPSED_LEN];
  244. if ( !pszFileName || !pszShortName )
  245. return false;
  246. Q_strncpy( save.szShortName, pszShortName, sizeof(save.szShortName) );
  247. Q_strncpy( save.szFileName, pszFileName, sizeof(save.szFileName) );
  248. FileHandle_t fh = g_pFullFileSystem->Open( pszFileName, "rb", "MOD" );
  249. if (fh == FILESYSTEM_INVALID_HANDLE)
  250. return false;
  251. int readok = SaveReadNameAndComment( fh, szMapName, ARRAYSIZE(szMapName), szComment, ARRAYSIZE(szComment) );
  252. g_pFullFileSystem->Close(fh);
  253. if ( !readok )
  254. {
  255. return false;
  256. }
  257. Q_strncpy( save.szMapName, szMapName, sizeof(save.szMapName) );
  258. // Elapsed time is the last 6 characters in comment. (mmm:ss)
  259. int i;
  260. i = strlen( szComment );
  261. Q_strncpy( szElapsedTime, "??", sizeof( szElapsedTime ) );
  262. if (i >= 6)
  263. {
  264. Q_strncpy( szElapsedTime, (char *)&szComment[i - 6], 7 );
  265. szElapsedTime[6] = '\0';
  266. // parse out
  267. int minutes = atoi( szElapsedTime );
  268. int seconds = atoi( szElapsedTime + 4);
  269. // reformat
  270. if ( minutes )
  271. {
  272. Q_snprintf( szElapsedTime, sizeof(szElapsedTime), "%d %s %d seconds", minutes, minutes > 1 ? "minutes" : "minute", seconds );
  273. }
  274. else
  275. {
  276. Q_snprintf( szElapsedTime, sizeof(szElapsedTime), "%d seconds", seconds );
  277. }
  278. // Chop elapsed out of comment.
  279. int n;
  280. n = i - 6;
  281. szComment[n] = '\0';
  282. n--;
  283. // Strip back the spaces at the end.
  284. while ((n >= 1) &&
  285. szComment[n] &&
  286. szComment[n] == ' ')
  287. {
  288. szComment[n--] = '\0';
  289. }
  290. }
  291. // calculate the file name to print
  292. const char *pszType = "";
  293. if (strstr(pszFileName, "quick"))
  294. {
  295. pszType = "#GameUI_QuickSave";
  296. }
  297. else if (strstr(pszFileName, "autosave"))
  298. {
  299. pszType = "#GameUI_AutoSave";
  300. }
  301. Q_strncpy( save.szType, pszType, sizeof(save.szType) );
  302. Q_strncpy( save.szComment, szComment, sizeof(save.szComment) );
  303. Q_strncpy( save.szElapsedTime, szElapsedTime, sizeof(save.szElapsedTime) );
  304. // Now get file time stamp.
  305. long fileTime = g_pFullFileSystem->GetFileTime(pszFileName);
  306. char szFileTime[32];
  307. g_pFullFileSystem->FileTimeToString(szFileTime, sizeof(szFileTime), fileTime);
  308. char *newline = strstr(szFileTime, "\n");
  309. if (newline)
  310. {
  311. *newline = 0;
  312. }
  313. Q_strncpy( save.szFileTime, szFileTime, sizeof(save.szFileTime) );
  314. save.iTimestamp = fileTime;
  315. return true;
  316. }
  317. void CBaseSaveGameDialog::OnKeyCodeTyped( vgui::KeyCode code )
  318. {
  319. if ( code == KEY_ESCAPE )
  320. {
  321. OnCommand( "Close" );
  322. return;
  323. }
  324. BaseClass::OnKeyCodeTyped( code );
  325. }
  326. void CBaseSaveGameDialog::OnKeyCodePressed( vgui::KeyCode code )
  327. {
  328. if ( code == KEY_XBUTTON_B )
  329. {
  330. OnCommand( "Close" );
  331. return;
  332. }
  333. else if ( code == KEY_XSTICK1_DOWN ||
  334. code == KEY_XSTICK2_DOWN ||
  335. code == KEY_XBUTTON_DOWN ||
  336. code == KEY_DOWN )
  337. {
  338. if ( m_pGameList->GetItemCount() )
  339. {
  340. Panel *pSelectedPanel = m_pGameList->GetSelectedPanel();
  341. if ( !pSelectedPanel )
  342. {
  343. m_pGameList->SetSelectedPanel( m_pGameList->GetItemPanel( m_pGameList->FirstItem() ) );
  344. m_pGameList->ScrollToItem( m_pGameList->FirstItem() );
  345. return;
  346. }
  347. else
  348. {
  349. int nNextPanelID = m_pGameList->FirstItem();
  350. while ( nNextPanelID != m_pGameList->InvalidItemID() )
  351. {
  352. if ( m_pGameList->GetItemPanel( nNextPanelID ) == pSelectedPanel )
  353. {
  354. nNextPanelID = m_pGameList->NextItem( nNextPanelID );
  355. if ( nNextPanelID != m_pGameList->InvalidItemID() )
  356. {
  357. m_pGameList->SetSelectedPanel( m_pGameList->GetItemPanel( nNextPanelID ) );
  358. m_pGameList->ScrollToItem( nNextPanelID );
  359. return;
  360. }
  361. break;
  362. }
  363. nNextPanelID = m_pGameList->NextItem( nNextPanelID );
  364. }
  365. }
  366. }
  367. }
  368. else if ( code == KEY_XSTICK1_UP ||
  369. code == KEY_XSTICK2_UP ||
  370. code == KEY_XBUTTON_UP ||
  371. code == KEY_UP )
  372. {
  373. if ( m_pGameList->GetItemCount() )
  374. {
  375. Panel *pSelectedPanel = m_pGameList->GetSelectedPanel();
  376. if ( !pSelectedPanel )
  377. {
  378. m_pGameList->SetSelectedPanel( m_pGameList->GetItemPanel( m_pGameList->FirstItem() ) );
  379. m_pGameList->ScrollToItem( m_pGameList->FirstItem() );
  380. return;
  381. }
  382. else
  383. {
  384. int nNextPanelID = m_pGameList->FirstItem();
  385. if ( m_pGameList->GetItemPanel( nNextPanelID ) != pSelectedPanel )
  386. {
  387. while ( nNextPanelID != m_pGameList->InvalidItemID() )
  388. {
  389. int nOldPanelID = nNextPanelID;
  390. nNextPanelID = m_pGameList->NextItem( nNextPanelID );
  391. if ( nNextPanelID != m_pGameList->InvalidItemID() )
  392. {
  393. if ( m_pGameList->GetItemPanel( nNextPanelID ) == pSelectedPanel )
  394. {
  395. m_pGameList->SetSelectedPanel( m_pGameList->GetItemPanel( nOldPanelID ) );
  396. m_pGameList->ScrollToItem( nOldPanelID );
  397. return;
  398. }
  399. }
  400. }
  401. }
  402. }
  403. }
  404. }
  405. else if ( code == KEY_ENTER || code == KEY_XBUTTON_A || code == STEAMCONTROLLER_A )
  406. {
  407. Panel *pSelectedPanel = m_pGameList->GetSelectedPanel();
  408. if ( pSelectedPanel )
  409. {
  410. if ( code == KEY_XBUTTON_A || code == STEAMCONTROLLER_A )
  411. {
  412. ConVarRef var( "joystick" );
  413. if ( var.IsValid() && !var.GetBool() )
  414. {
  415. var.SetValue( true );
  416. }
  417. ConVarRef var2( "hud_fastswitch" );
  418. if ( var2.IsValid() && var2.GetInt() != 2 )
  419. {
  420. var2.SetValue( 2 );
  421. }
  422. }
  423. m_pLoadButton->DoClick();
  424. return;
  425. }
  426. }
  427. BaseClass::OnKeyCodePressed( code );
  428. }
  429. //-----------------------------------------------------------------------------
  430. // Purpose: timestamp sort function for savegames
  431. //-----------------------------------------------------------------------------
  432. int CBaseSaveGameDialog::SaveGameSortFunc( const void *lhs, const void *rhs )
  433. {
  434. const SaveGameDescription_t *s1 = (const SaveGameDescription_t *)lhs;
  435. const SaveGameDescription_t *s2 = (const SaveGameDescription_t *)rhs;
  436. if (s1->iTimestamp < s2->iTimestamp)
  437. return 1;
  438. else if (s1->iTimestamp > s2->iTimestamp)
  439. return -1;
  440. // timestamps are equal, so just sort by filename
  441. return strcmp(s1->szFileName, s2->szFileName);
  442. }
  443. #define MAKEID(d,c,b,a) ( ((int)(a) << 24) | ((int)(b) << 16) | ((int)(c) << 8) | ((int)(d)) )
  444. int SaveReadNameAndComment( FileHandle_t f, OUT_Z_CAP(nameSize) char *name, int nameSize, OUT_Z_CAP(commentSize) char *comment, int commentSize )
  445. {
  446. int i, tag, size, tokenSize, tokenCount;
  447. char *pSaveData, *pFieldName, **pTokenList;
  448. name[0] = '\0';
  449. comment[0] = '\0';
  450. g_pFullFileSystem->Read( &tag, sizeof(int), f );
  451. if ( tag != MAKEID('J','S','A','V') )
  452. {
  453. return 0;
  454. }
  455. g_pFullFileSystem->Read( &tag, sizeof(int), f );
  456. if ( tag != SAVEGAME_VERSION ) // Enforce version for now
  457. {
  458. return 0;
  459. }
  460. g_pFullFileSystem->Read( &size, sizeof(int), f );
  461. g_pFullFileSystem->Read( &tokenCount, sizeof(int), f ); // These two ints are the token list
  462. g_pFullFileSystem->Read( &tokenSize, sizeof(int), f );
  463. size += tokenSize;
  464. // Sanity Check.
  465. if ( tokenCount < 0 || tokenCount > 1024*1024*32 )
  466. {
  467. return 0;
  468. }
  469. if ( tokenSize < 0 || tokenSize > 1024*1024*32 )
  470. {
  471. return 0;
  472. }
  473. pSaveData = (char *)new char[size];
  474. g_pFullFileSystem->Read(pSaveData, size, f);
  475. int nNumberOfFields;
  476. char *pData;
  477. int nFieldSize;
  478. pData = pSaveData;
  479. // Allocate a table for the strings, and parse the table
  480. if ( tokenSize > 0 )
  481. {
  482. pTokenList = new char *[tokenCount];
  483. // Make sure the token strings pointed to by the pToken hashtable.
  484. for( i=0; i<tokenCount; i++ )
  485. {
  486. pTokenList[i] = *pData ? pData : NULL; // Point to each string in the pToken table
  487. while( *pData++ ); // Find next token (after next null)
  488. }
  489. }
  490. else
  491. pTokenList = NULL;
  492. // short, short (size, index of field name)
  493. nFieldSize = *(short *)pData;
  494. pData += sizeof(short);
  495. pFieldName = pTokenList[ *(short *)pData ];
  496. if (stricmp(pFieldName, "GameHeader"))
  497. {
  498. delete[] pSaveData;
  499. return 0;
  500. };
  501. // int (fieldcount)
  502. pData += sizeof(short);
  503. nNumberOfFields = *(int*)pData;
  504. pData += nFieldSize;
  505. // Each field is a short (size), short (index of name), binary string of "size" bytes (data)
  506. for (i = 0; i < nNumberOfFields; i++)
  507. {
  508. // Data order is:
  509. // Size
  510. // szName
  511. // Actual Data
  512. nFieldSize = *(short *)pData;
  513. pData += sizeof(short);
  514. pFieldName = pTokenList[ *(short *)pData ];
  515. pData += sizeof(short);
  516. if (!stricmp(pFieldName, "comment"))
  517. {
  518. int copySize = MAX(commentSize, nFieldSize);
  519. Q_strncpy(comment, pData, copySize);
  520. }
  521. else if (!stricmp(pFieldName, "mapName"))
  522. {
  523. int copySize = MAX(nameSize, nFieldSize);
  524. Q_strncpy(name, pData, copySize);
  525. };
  526. // Move to Start of next field.
  527. pData += nFieldSize;
  528. };
  529. // Delete the string table we allocated
  530. delete[] pTokenList;
  531. delete[] pSaveData;
  532. if (strlen(name) > 0 && strlen(comment) > 0)
  533. return 1;
  534. return 0;
  535. }
  536. //-----------------------------------------------------------------------------
  537. // Purpose: deletes an existing save game
  538. //-----------------------------------------------------------------------------
  539. void CBaseSaveGameDialog::DeleteSaveGame( const char *fileName )
  540. {
  541. if ( !fileName || !fileName[0] )
  542. return;
  543. // delete the save game file
  544. g_pFullFileSystem->RemoveFile( fileName, "MOD" );
  545. // delete the associated tga
  546. char tga[_MAX_PATH];
  547. Q_strncpy( tga, fileName, sizeof(tga) );
  548. char *ext = strstr( tga, ".sav" );
  549. if ( ext )
  550. {
  551. strcpy( ext, ".tga" );
  552. }
  553. g_pFullFileSystem->RemoveFile( tga, "MOD" );
  554. }
  555. //-----------------------------------------------------------------------------
  556. // Purpose: One item has been selected
  557. //-----------------------------------------------------------------------------
  558. void CBaseSaveGameDialog::OnPanelSelected()
  559. {
  560. SetControlEnabled( "loadsave", true );
  561. SetControlEnabled( "delete", true );
  562. }