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.

3081 lines
74 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #undef PROTECT_FILEIO_FUNCTIONS
  7. #ifndef _LINUX
  8. #undef fopen
  9. #endif
  10. #if defined( WIN32 ) && !defined( _X360 )
  11. #include "winlite.h"
  12. #include <winsock2.h> // INADDR_ANY defn
  13. #include <direct.h>
  14. #elif defined(POSIX)
  15. #include <sys/stat.h>
  16. #ifdef OSX
  17. #include <copyfile.h>
  18. #import <mach/mach_host.h>
  19. #import <sys/sysctl.h>
  20. #elif defined(LINUX)
  21. #include <sys/types.h>
  22. #include <sys/stat.h>
  23. #include <fcntl.h>
  24. #endif
  25. #define GetLastError() errno
  26. #elif defined( _X360 )
  27. #else
  28. #error
  29. #endif
  30. #include <time.h>
  31. #include "client.h"
  32. #include <vgui_controls/Frame.h>
  33. #include <vgui/ISystem.h>
  34. #include <vgui/ISurface.h>
  35. #include <vgui/IInput.h>
  36. #include <vgui/IVGui.h>
  37. #include <KeyValues.h>
  38. #include <vgui_controls/BuildGroup.h>
  39. #include <vgui_controls/Tooltip.h>
  40. #include <vgui_controls/TextImage.h>
  41. #include <vgui_controls/CheckButton.h>
  42. #include <vgui_controls/Label.h>
  43. #include <vgui_controls/PropertySheet.h>
  44. #include <vgui_controls/FileOpenDialog.h>
  45. #include "vgui_controls/DirectorySelectDialog.h"
  46. #include <vgui_controls/ProgressBar.h>
  47. #include <vgui_controls/Slider.h>
  48. #include <vgui_controls/ComboBox.h>
  49. #include <vgui_controls/Controls.h>
  50. #include <vgui_controls/TextEntry.h>
  51. #include "enginebugreporter.h"
  52. #include "vgui_baseui_interface.h"
  53. #include <vgui_controls/FileOpenDialog.h>
  54. #include "ivideomode.h"
  55. #include "cl_main.h"
  56. #include "gl_model_private.h"
  57. #include "tier2/tier2.h"
  58. #include "tier1/utlstring.h"
  59. #include "tier1/callqueue.h"
  60. #include "vstdlib/jobthread.h"
  61. #include "utlsymbol.h"
  62. #include "utldict.h"
  63. #include "filesystem.h"
  64. #include "filesystem_engine.h"
  65. #include "icliententitylist.h"
  66. #include "bugreporter/bugreporter.h"
  67. #include "icliententity.h"
  68. #include "tier0/vcrmode.h"
  69. #include "tier0/platform.h"
  70. #include "net.h"
  71. #include "host_phonehome.h"
  72. #include "tier0/icommandline.h"
  73. #include "stdstring.h"
  74. #include "sv_main.h"
  75. #include "server.h"
  76. #include "eiface.h"
  77. #include "gl_matsysiface.h"
  78. #include "materialsystem/imaterialsystemhardwareconfig.h"
  79. #include "FindSteamServers.h"
  80. #include "vstdlib/random.h"
  81. #ifndef SWDS
  82. #include "cl_steamauth.h"
  83. #endif
  84. #include "zip/XZip.h"
  85. #if defined( _X360 )
  86. #include "xbox/xbox_win32stubs.h"
  87. #endif
  88. // memdbgon must be the last include file in a .cpp file!!!
  89. #include "tier0/memdbgon.h"
  90. #define DENY_SOUND "common/bugreporter_failed"
  91. #define SUCCEED_SOUND "common/bugreporter_succeeded"
  92. // Fixme, move these to buguiddata.res script file?
  93. #ifdef WIN32
  94. #define BUG_REPOSITORY_URL "\\\\fileserver\\bugs"
  95. #elif defined(OSX)
  96. #define BUG_REPOSITORY_URL "/Volumes/bugs"
  97. #elif defined(LINUX)
  98. #define BUG_REPOSITORY_URL "\\\\fileserver\\bugs"
  99. #else
  100. //#error
  101. #endif
  102. #define REPOSITORY_VALIDATION_FILE "info.txt"
  103. #define BUG_REPORTER_DLLNAME "bugreporter_filequeue"
  104. #define BUG_REPORTER_PUBLIC_DLLNAME "bugreporter_public"
  105. #if defined( _DEBUG )
  106. #define PUBLIC_BUGREPORT_WAIT_TIME 3
  107. #else
  108. #define PUBLIC_BUGREPORT_WAIT_TIME 15
  109. #endif
  110. // 16Mb max zipped size
  111. #define MAX_ZIP_SIZE (1024 * 1024 * 16 )
  112. extern ConVar skill;
  113. extern float g_fFramesPerSecond;
  114. static ConVar bugreporter_includebsp( "bugreporter_includebsp", "1", 0, "Include .bsp for internal bug submissions." );
  115. static ConVar bugreporter_uploadasync( "bugreporter_uploadasync", "0", FCVAR_ARCHIVE, "Upload attachments asynchronously" );
  116. using namespace vgui;
  117. unsigned long GetRam()
  118. {
  119. #ifdef WIN32
  120. MEMORYSTATUS stat;
  121. GlobalMemoryStatus( &stat );
  122. return (stat.dwTotalPhys / (1024 * 1024));
  123. #elif defined(OSX)
  124. int mib[2] = { CTL_HW, HW_MEMSIZE };
  125. u_int namelen = sizeof(mib) / sizeof(mib[0]);
  126. uint64_t memsize;
  127. size_t len = sizeof(memsize);
  128. if (sysctl(mib, namelen, &memsize, &len, NULL, 0) >= 0)
  129. {
  130. return memsize / (1024*1024);
  131. }
  132. else
  133. return 0;
  134. #elif defined( LINUX )
  135. unsigned long Ram = 0;
  136. FILE *fh = fopen( "/proc/meminfo", "r" );
  137. if( fh )
  138. {
  139. char buf[ 256 ];
  140. const char szMemTotal[] = "MemTotal:";
  141. while( fgets( buf, sizeof( buf ), fh ) )
  142. {
  143. if ( !Q_strnicmp( buf, szMemTotal, sizeof( szMemTotal ) - 1 ) )
  144. {
  145. // Should already be in kB
  146. Ram = atoi( buf + sizeof( szMemTotal ) - 1 ) / 1024;
  147. break;
  148. }
  149. }
  150. fclose( fh );
  151. }
  152. return Ram;
  153. #else
  154. Assert( !"Impl GetRam" );
  155. return 0;
  156. #endif
  157. }
  158. const char *GetInternalBugReporterDLL( void )
  159. {
  160. char const *pBugReportedDLL = NULL;
  161. if ( CommandLine()->CheckParm("-bugreporterdll", &pBugReportedDLL ) )
  162. return pBugReportedDLL;
  163. return BUG_REPORTER_DLLNAME;
  164. }
  165. void DisplaySystemVersion( char *osversion, int maxlen )
  166. {
  167. #ifdef WIN32
  168. osversion[ 0 ] = 0;
  169. OSVERSIONINFOEX osvi;
  170. BOOL bOsVersionInfoEx;
  171. // Try calling GetVersionEx using the OSVERSIONINFOEX structure.
  172. //
  173. // If that fails, try using the OSVERSIONINFO structure.
  174. ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
  175. osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
  176. bOsVersionInfoEx = GetVersionEx ((OSVERSIONINFO *) &osvi);
  177. if( !bOsVersionInfoEx )
  178. {
  179. // If OSVERSIONINFOEX doesn't work, try OSVERSIONINFO.
  180. osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
  181. if (! GetVersionEx ( (OSVERSIONINFO *) &osvi) )
  182. {
  183. Q_strncpy( osversion, "Unable to get Version", maxlen );
  184. return;
  185. }
  186. }
  187. switch (osvi.dwPlatformId)
  188. {
  189. case VER_PLATFORM_WIN32_NT:
  190. // Test for the product.
  191. if ( osvi.dwMajorVersion <= 4 )
  192. {
  193. Q_strncat ( osversion, "NT ", maxlen, COPY_ALL_CHARACTERS );
  194. }
  195. if ( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0 )
  196. {
  197. Q_strncat ( osversion, "2000 ", maxlen, COPY_ALL_CHARACTERS );
  198. }
  199. if ( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1 )
  200. {
  201. Q_strncat ( osversion, "XP ", maxlen, COPY_ALL_CHARACTERS );
  202. }
  203. // Display version, service pack (if any), and build number.
  204. char build[256];
  205. Q_snprintf (build, sizeof( build ), "%s (Build %d) version %d.%d",
  206. osvi.szCSDVersion,
  207. osvi.dwBuildNumber & 0xFFFF,
  208. osvi.dwMajorVersion,
  209. osvi.dwMinorVersion );
  210. Q_strncat ( osversion, build, maxlen, COPY_ALL_CHARACTERS );
  211. break;
  212. case VER_PLATFORM_WIN32_WINDOWS:
  213. if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 0)
  214. {
  215. Q_strncat ( osversion, "95 ", maxlen, COPY_ALL_CHARACTERS );
  216. if ( osvi.szCSDVersion[1] == 'C' || osvi.szCSDVersion[1] == 'B' )
  217. {
  218. Q_strncat ( osversion, "OSR2 ", maxlen, COPY_ALL_CHARACTERS );
  219. }
  220. }
  221. if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 10)
  222. {
  223. Q_strncat ( osversion, "98 ", maxlen, COPY_ALL_CHARACTERS );
  224. if ( osvi.szCSDVersion[1] == 'A' )
  225. {
  226. Q_strncat ( osversion, "SE ", maxlen, COPY_ALL_CHARACTERS );
  227. }
  228. }
  229. if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 90)
  230. {
  231. Q_strncat ( osversion, "Me ", maxlen, COPY_ALL_CHARACTERS );
  232. }
  233. break;
  234. case VER_PLATFORM_WIN32s:
  235. Q_strncat ( osversion, "Win32s ", maxlen, COPY_ALL_CHARACTERS );
  236. break;
  237. }
  238. #elif defined(OSX)
  239. FILE *fpVersionInfo = popen( "/usr/bin/sw_vers", "r" );
  240. const char *pszSearchString = "ProductVersion:\t";
  241. const int cchSearchString = Q_strlen( pszSearchString );
  242. char rgchVersionLine[1024];
  243. if ( !fpVersionInfo )
  244. Q_strncat ( osversion, "OSXU ", maxlen, COPY_ALL_CHARACTERS );
  245. else
  246. {
  247. while ( fgets( rgchVersionLine, sizeof(rgchVersionLine), fpVersionInfo ) )
  248. {
  249. if ( !Q_strnicmp( rgchVersionLine, pszSearchString, cchSearchString ) )
  250. {
  251. const char *pchVersion = rgchVersionLine + cchSearchString;
  252. if ( !Q_strnicmp( pchVersion, "10.", Q_strlen( "10." ) ) )
  253. {
  254. pchVersion += 3; // move past "10."
  255. if( *pchVersion == '4' && *(pchVersion+1) == '.' )
  256. {
  257. Q_strncat ( osversion, "OSX104 ", maxlen, COPY_ALL_CHARACTERS );
  258. break;
  259. }
  260. else if ( *pchVersion == '5' && *(pchVersion+1) == '.' )
  261. {
  262. Q_strncat ( osversion, "OSX105 ", maxlen, COPY_ALL_CHARACTERS );
  263. break;
  264. }
  265. else if ( *pchVersion == '6' && *(pchVersion+1) == '.' )
  266. {
  267. Q_strncat ( osversion, "OSX106 ", maxlen, COPY_ALL_CHARACTERS );
  268. break;
  269. }
  270. else if ( *pchVersion == '7' && *(pchVersion+1) == '.' )
  271. {
  272. Q_strncat ( osversion, "OSX107 ", maxlen, COPY_ALL_CHARACTERS );
  273. break;
  274. }
  275. }
  276. break;
  277. }
  278. }
  279. pclose( fpVersionInfo );
  280. }
  281. #elif defined(LINUX)
  282. FILE *fpKernelVer = fopen( "/proc/version_signature", "r" );
  283. if ( !fpKernelVer )
  284. {
  285. Q_strncat ( osversion, "Linux ", maxlen, COPY_ALL_CHARACTERS );
  286. }
  287. else
  288. {
  289. fgets( osversion, maxlen, fpKernelVer );
  290. osversion[ maxlen - 1 ] = 0;
  291. char *szlf = Q_strrchr( osversion, '\n' );
  292. if( szlf )
  293. *szlf = '\0';
  294. fclose( fpKernelVer );
  295. }
  296. #endif
  297. }
  298. static int GetNumberForMap()
  299. {
  300. if ( !host_state.worldmodel )
  301. return 1;
  302. char mapname[256];
  303. CL_SetupMapName( modelloader->GetName( host_state.worldmodel ), mapname, sizeof( mapname ) );
  304. KeyValues *resfilekeys = new KeyValues( "mapnumber" );
  305. if ( resfilekeys->LoadFromFile( g_pFileSystem, "scripts/bugreport_mapnumber.txt", "GAME" ) )
  306. {
  307. KeyValues *entry = resfilekeys->GetFirstSubKey();
  308. while ( entry )
  309. {
  310. if ( !Q_stricmp( entry->GetName(), mapname ) )
  311. {
  312. return entry->GetInt() + 1;
  313. }
  314. entry = entry->GetNextKey();
  315. }
  316. }
  317. resfilekeys->deleteThis();
  318. char szNameCopy[ 128 ];
  319. const char *pszResult = Q_strrchr( mapname, '_' );
  320. if( !pszResult )
  321. //I don't know where the number of this map is, if there even is one.
  322. return 1;
  323. Q_strncpy( szNameCopy, pszResult + 1, sizeof( szNameCopy ) );
  324. if ( !szNameCopy[0] )
  325. return 1;
  326. // in case we can't use tchar.h, this will do the same thing
  327. char *pcEndOfName = szNameCopy;
  328. while(*pcEndOfName != 0)
  329. {
  330. if(*pcEndOfName < '0' || *pcEndOfName > '9')
  331. *pcEndOfName = 0;
  332. pcEndOfName++;
  333. }
  334. //add 1 because pvcs has 0 as the first map number, not 1 (and it is not 0-based).
  335. return atoi(szNameCopy) + 1;
  336. }
  337. //-----------------------------------------------------------------------------
  338. // Purpose: Generic dialog for displaying animating steam progress logo
  339. // used when we are doing a possibly length steam op that has no progress measure (login/create user/etc)
  340. //-----------------------------------------------------------------------------
  341. class CBugReportUploadProgressDialog : public vgui::Frame
  342. {
  343. public:
  344. CBugReportUploadProgressDialog(vgui::Panel *parent, const char *name, const char *title, const char *message);
  345. ~CBugReportUploadProgressDialog();
  346. virtual void PerformLayout();
  347. void SetProgress( float progress );
  348. private:
  349. typedef vgui::Frame BaseClass;
  350. vgui::ProgressBar *m_pProgress;
  351. };
  352. //-----------------------------------------------------------------------------
  353. // Purpose: Constructor
  354. //-----------------------------------------------------------------------------
  355. CBugReportUploadProgressDialog::CBugReportUploadProgressDialog(Panel *parent, const char *name, const char *title, const char *message) : Frame(parent, name)
  356. {
  357. SetSize(300, 160);
  358. SetSizeable(false);
  359. MoveToFront();
  360. SetTitle(title, true);
  361. m_pProgress = new vgui::ProgressBar( this, "ProgressBar" );
  362. LoadControlSettings("Resource\\BugReporterUploadProgress.res");
  363. SetControlString("InfoLabel", message);
  364. MoveToCenterOfScreen();
  365. }
  366. //-----------------------------------------------------------------------------
  367. // Purpose: Destructor
  368. //-----------------------------------------------------------------------------
  369. CBugReportUploadProgressDialog::~CBugReportUploadProgressDialog()
  370. {
  371. }
  372. //-----------------------------------------------------------------------------
  373. // Purpose:
  374. // Input : percent -
  375. //-----------------------------------------------------------------------------
  376. void CBugReportUploadProgressDialog::SetProgress( float progress )
  377. {
  378. Assert( m_pProgress );
  379. m_pProgress->SetProgress( progress );
  380. }
  381. //-----------------------------------------------------------------------------
  382. // Purpose:
  383. //-----------------------------------------------------------------------------
  384. void CBugReportUploadProgressDialog::PerformLayout()
  385. {
  386. SetMinimizeButtonVisible(false);
  387. SetCloseButtonVisible(false);
  388. BaseClass::PerformLayout();
  389. }
  390. //-----------------------------------------------------------------------------
  391. // Purpose: Generic dialog for displaying animating steam progress logo
  392. // used when we are doing a possibly length steam op that has no progress measure (login/create user/etc)
  393. //-----------------------------------------------------------------------------
  394. class CBugReportFinishedDialog : public vgui::Frame
  395. {
  396. public:
  397. CBugReportFinishedDialog(vgui::Panel *parent, const char *name, const char *title, const char *message);
  398. virtual void PerformLayout();
  399. virtual void OnCommand( const char *command );
  400. private:
  401. typedef vgui::Frame BaseClass;
  402. vgui::Button *m_pOk;
  403. };
  404. //-----------------------------------------------------------------------------
  405. // Purpose: Constructor
  406. //-----------------------------------------------------------------------------
  407. CBugReportFinishedDialog::CBugReportFinishedDialog(Panel *parent, const char *name, const char *title, const char *message) : Frame(parent, name)
  408. {
  409. SetSize(300, 160);
  410. SetSizeable(false);
  411. MoveToFront();
  412. SetTitle(title, true);
  413. m_pOk = new vgui::Button( this, "CloseBtn", "#OK", this, "Close" );
  414. LoadControlSettings("Resource\\BugReporterUploadFinished.res");
  415. SetControlString("InfoLabel", message);
  416. MoveToCenterOfScreen();
  417. }
  418. void CBugReportFinishedDialog::OnCommand( const char *command )
  419. {
  420. if ( !Q_stricmp( command, "Close" ) )
  421. {
  422. MarkForDeletion();
  423. OnClose();
  424. }
  425. else
  426. {
  427. BaseClass::OnCommand( command );
  428. }
  429. }
  430. //-----------------------------------------------------------------------------
  431. // Purpose:
  432. //-----------------------------------------------------------------------------
  433. void CBugReportFinishedDialog::PerformLayout()
  434. {
  435. SetMinimizeButtonVisible(false);
  436. SetCloseButtonVisible(true);
  437. BaseClass::PerformLayout();
  438. }
  439. //-----------------------------------------------------------------------------
  440. // Purpose:
  441. //-----------------------------------------------------------------------------
  442. class CBugUIPanel : public vgui::Frame
  443. {
  444. DECLARE_CLASS_SIMPLE( CBugUIPanel, vgui::Frame );
  445. public:
  446. CBugUIPanel( bool bIsPublic, vgui::Panel *parent );
  447. ~CBugUIPanel();
  448. virtual void OnTick();
  449. // Command issued
  450. virtual void OnCommand(const char *command);
  451. virtual void Close();
  452. virtual void Activate();
  453. virtual void SetVisible( bool state )
  454. {
  455. bool changed = state != IsVisible();
  456. BaseClass::SetVisible( state );
  457. if ( changed && state )
  458. {
  459. m_pTitle->RequestFocus();
  460. }
  461. }
  462. bool Init();
  463. void Shutdown();
  464. virtual void OnKeyCodePressed( KeyCode code );
  465. void ParseDefaultParams( void );
  466. void ParseCommands( const CCommand &args );
  467. inline bool IsTakingSnapshot() const
  468. {
  469. return m_bTakingSnapshot;
  470. }
  471. protected:
  472. vgui::TextEntry *m_pTitle;
  473. vgui::TextEntry *m_pDescription;
  474. vgui::Button *m_pTakeShot;
  475. vgui::Button *m_pSaveGame;
  476. vgui::Button *m_pSaveBSP;
  477. vgui::Button *m_pSaveVMF;
  478. vgui::Button *m_pChooseVMFFolder;
  479. vgui::Button *m_pIncludeFile;
  480. vgui::Button *m_pClearIncludes;
  481. vgui::Label *m_pScreenShotURL;
  482. vgui::Label *m_pSaveGameURL;
  483. vgui::Label *m_pBSPURL;
  484. vgui::Label *m_pVMFURL;
  485. vgui::Label *m_pPosition;
  486. vgui::Label *m_pOrientation;
  487. vgui::Label *m_pLevelName;
  488. vgui::Label *m_pBuildNumber;
  489. vgui::Label *m_pSubmitter;
  490. vgui::ComboBox *m_pAssignTo;
  491. vgui::ComboBox *m_pSeverity;
  492. vgui::ComboBox *m_pReportType;
  493. vgui::ComboBox *m_pPriority;
  494. vgui::ComboBox *m_pGameArea;
  495. vgui::ComboBox *m_pMapNumber;
  496. vgui::Button *m_pSubmit;
  497. vgui::Button *m_pCancel;
  498. vgui::Button *m_pClearForm;
  499. vgui::TextEntry *m_pIncludedFiles;
  500. vgui::TextEntry *m_pEmail;
  501. vgui::Label *m_pSubmitterLabel;
  502. IBugReporter *m_pBugReporter;
  503. CSysModule *m_hBugReporter;
  504. MESSAGE_FUNC_CHARPTR( OnFileSelected, "FileSelected", fullpath );
  505. MESSAGE_FUNC_CHARPTR( OnDirectorySelected, "DirectorySelected", dir );
  506. MESSAGE_FUNC( OnChooseVMFFolder, "OnChooseVMFFolder" );
  507. MESSAGE_FUNC_PTR( OnChooseArea, "TextChanged", panel);
  508. protected:
  509. void DetermineSubmitterName();
  510. void PopulateControls();
  511. void OnTakeSnapshot();
  512. void OnSaveGame();
  513. void OnSaveBSP();
  514. void OnSaveVMF();
  515. void OnSubmit();
  516. void OnClearForm();
  517. void OnIncludeFile();
  518. void OnClearIncludedFiles();
  519. int GetArea();
  520. bool IsValidSubmission( bool verbose );
  521. bool IsValidEmailAddress( char const *email );
  522. void WipeData();
  523. void GetDataFileBase( char const *suffix, char *buf, int bufsize );
  524. const char *GetRepositoryURL( void );
  525. const char *GetSubmissionURL( int bugid );
  526. bool AddFileToZip( char const *relative );
  527. bool AddBugTextToZip( char const *textfilename, char const *text, int textlen );
  528. void CheckContinueQueryingSteamForCSERList();
  529. struct includedfile
  530. {
  531. char name[ 256 ];
  532. char fixedname[ 256 ];
  533. };
  534. bool UploadBugSubmission(
  535. char const *levelname,
  536. int bugId,
  537. char const *savefile,
  538. char const *screenshot,
  539. char const *bsp,
  540. char const *vmf,
  541. CUtlVector< includedfile >& includedfiles );
  542. bool UploadFile( char const *local, char const *remote, bool bDeleteLocal = false );
  543. void DenySound();
  544. void SuccessSound( int bugid );
  545. bool AutoFillToken( char const *token, bool partial );
  546. bool Compare( char const *token, char const *value, bool partial );
  547. char const *GetSubmitter();
  548. void OnFinishBugReport();
  549. bool m_bCanSubmit;
  550. bool m_bLoggedIn;
  551. bool m_bCanSeeRepository;
  552. bool m_bValidated;
  553. bool m_bUseNameForSubmitter;
  554. unsigned char m_fAutoAddScreenshot;
  555. enum AutoAddScreenshot { eAutoAddScreenshot_Detect = 0, eAutoAddScreenshot_Add = 1, eAutoAddScreenshot_DontAdd = 2 };
  556. char m_szScreenShotName[ 256 ];
  557. char m_szSaveGameName[ 256 ];
  558. char m_szBSPName[ 256 ];
  559. char m_szVMFName[ 256 ];
  560. char m_szLevel[ 256 ];
  561. CUtlVector< includedfile > m_IncludedFiles;
  562. bool m_bTakingSnapshot;
  563. bool m_bHidGameUIForSnapshot;
  564. int m_nSnapShotFrame;
  565. char m_szVMFContentDirFullpath[ MAX_PATH ];
  566. vgui::DHANDLE< vgui::FileOpenDialog > m_hFileOpenDialog;
  567. vgui::DHANDLE< vgui::DirectorySelectDialog > m_hDirectorySelectDialog;
  568. // If true, then once directory for vmfs is selected, we act like the Include .vmf button got pressed, too
  569. bool m_bAddVMF;
  570. HZIP m_hZip;
  571. CBugReportUploadProgressDialog *m_pProgressDialog;
  572. vgui::DHANDLE< CBugReportFinishedDialog > m_hFinishedDialog;
  573. bool m_bWaitForFinish;
  574. float m_flPauseTime;
  575. CSteamID m_SteamID;
  576. netadr_t m_cserIP;
  577. bool m_bQueryingSteamForCSER;
  578. bool m_bIsPublic;
  579. CUtlString m_sDllName;
  580. };
  581. //-----------------------------------------------------------------------------
  582. // Purpose: Basic help dialog
  583. //-----------------------------------------------------------------------------
  584. CBugUIPanel::CBugUIPanel( bool bIsPublic, vgui::Panel *parent ) :
  585. BaseClass( parent, "BugUIPanel"),
  586. m_bIsPublic( bIsPublic ),
  587. m_bAddVMF( false )
  588. {
  589. m_sDllName = m_bIsPublic ?
  590. BUG_REPORTER_PUBLIC_DLLNAME :
  591. GetInternalBugReporterDLL();
  592. m_hZip = (HZIP)0;
  593. m_hDirectorySelectDialog = NULL;
  594. m_hFileOpenDialog = NULL;
  595. m_pBugReporter = NULL;
  596. m_hBugReporter = 0;
  597. m_bQueryingSteamForCSER = false;
  598. // Default server address (hardcoded in case not running on steam)
  599. char const *cserIP = "207.173.177.12:27013";
  600. // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  601. // NOTE: If you need to override the CSER Ip, make sure you tweak the code in
  602. // CheckContinueQueryingSteamForCSERList!!!!!!!!!!
  603. // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  604. NET_StringToAdr( cserIP, &m_cserIP );
  605. m_bValidated = false;
  606. m_szScreenShotName[ 0 ] = 0;
  607. m_szSaveGameName[ 0 ] = 0;
  608. m_szBSPName[ 0 ] = 0;
  609. m_szVMFName[ 0 ] = 0;
  610. m_szLevel[ 0 ] = 0;
  611. m_szVMFContentDirFullpath[ 0 ] = 0;
  612. m_IncludedFiles.Purge();
  613. m_nSnapShotFrame = -1;
  614. m_bTakingSnapshot = false;
  615. m_bHidGameUIForSnapshot = false;
  616. m_bCanSubmit = false;
  617. m_bLoggedIn = false;
  618. m_bCanSeeRepository = false;
  619. m_pProgressDialog = NULL;
  620. m_flPauseTime = 0.0f;
  621. m_bWaitForFinish = false;
  622. m_bUseNameForSubmitter = false;
  623. m_fAutoAddScreenshot = eAutoAddScreenshot_Detect;
  624. SetTitle("Bug Reporter", true);
  625. m_pTitle = new vgui::TextEntry( this, "BugTitle" );
  626. m_pTitle->SetMaximumCharCount( 60 ); // Titles can be 80 chars, but we put the mapname in front, so limit to 60
  627. m_pDescription = new vgui::TextEntry( this, "BugDescription" );
  628. m_pDescription->SetMultiline( true );
  629. m_pDescription->SetCatchEnterKey( true );
  630. m_pDescription->SetVerticalScrollbar( true );
  631. m_pEmail = new vgui::TextEntry( this, "BugEmail" );;
  632. m_pEmail->SetMaximumCharCount( 80 );
  633. m_pSubmitterLabel = new vgui::Label( this, "BugSubitterLabelPublic", "" );
  634. m_pScreenShotURL = new vgui::Label( this, "BugScreenShotURL", "" );
  635. m_pSaveGameURL = new vgui::Label( this, "BugSaveGameURL", "" );
  636. m_pBSPURL = new vgui::Label( this, "BugBSPURL", "" );
  637. m_pVMFURL = new vgui::Label( this, "BugVMFURL", "" );
  638. m_pIncludedFiles = new vgui::TextEntry( this, "BugIncludedFiles" );
  639. m_pIncludedFiles->SetMultiline( true );
  640. m_pIncludedFiles->SetVerticalScrollbar( true );
  641. m_pIncludedFiles->SetEditable( false );
  642. m_pTakeShot = new vgui::Button( this, "BugTakeShot", "Take shot" );
  643. m_pSaveGame = new vgui::Button( this, "BugSaveGame", "Save game" );
  644. m_pSaveBSP = new vgui::Button( this, "BugSaveBSP", "Include .bsp" );
  645. m_pSaveVMF = new vgui::Button( this, "BugSaveVMF", "Include .vmf" );
  646. m_pChooseVMFFolder = new vgui::Button( this, "BugChooseVMFFolder", "Choose folder" );
  647. m_pIncludeFile = new vgui::Button( this, "BugIncludeFile", "Include file..." );
  648. m_pClearIncludes = new vgui::Button( this, "BugClearIncludedFiles", "Clear files" );
  649. m_pPosition = new vgui::Label( this, "BugPosition", "" );
  650. m_pOrientation = new vgui::Label( this, "BugOrientation", "" );
  651. m_pLevelName = new vgui::Label( this, "BugLevel", "" );
  652. m_pBuildNumber = new vgui::Label( this, "BugBuild", "" );
  653. m_pSubmitter = new Label(this, "BugSubmitter", "" );
  654. m_pAssignTo = new ComboBox(this, "BugOwner", 10, false);
  655. m_pSeverity = new ComboBox(this, "BugSeverity", 10, false);
  656. m_pReportType = new ComboBox(this, "BugReportType", 10, false);
  657. m_pPriority = new ComboBox(this, "BugPriority", 10, false);
  658. m_pGameArea = new ComboBox(this, "BugArea", 10, false);
  659. m_pMapNumber = new ComboBox(this, "BugMapNumber", 10, false);
  660. m_pSubmit = new vgui::Button( this, "BugSubmit", "Submit" );
  661. m_pCancel = new vgui::Button( this, "BugCancel", "Cancel" );
  662. m_pClearForm = new vgui::Button( this, "BugClearForm", "Clear Form" );
  663. vgui::ivgui()->AddTickSignal( GetVPanel(), 0 );
  664. if ( m_bIsPublic )
  665. {
  666. LoadControlSettings("Resource\\BugUIPanel_Public.res");
  667. }
  668. else
  669. {
  670. LoadControlSettings("Resource\\BugUIPanel_Filequeue.res");
  671. }
  672. m_pChooseVMFFolder->SetCommand( new KeyValues( "OnChooseVMFFolder" ) );
  673. m_pChooseVMFFolder->AddActionSignalTarget( this );
  674. int w = GetWide();
  675. int h = GetTall();
  676. int x = ( videomode->GetModeStereoWidth() - w ) / 2;
  677. int y = ( videomode->GetModeStereoHeight() - h ) / 2;
  678. // Hidden by default
  679. SetVisible( false );
  680. SetSizeable( false );
  681. SetMoveable( true );
  682. SetPos( x, y );
  683. }
  684. bool CBugUIPanel::Init()
  685. {
  686. Color clr( 50, 100, 255, 255 );
  687. Assert( !m_pBugReporter );
  688. m_hBugReporter = g_pFileSystem->LoadModule( m_sDllName);
  689. if( m_bIsPublic )
  690. {
  691. // Hack due to constructor called before phonehome->Init...
  692. m_hBugReporter = g_pFileSystem->LoadModule( m_sDllName );
  693. LoadControlSettings("Resource\\BugUIPanel_Public.res");
  694. int w = GetWide();
  695. int h = GetTall();
  696. int x = ( videomode->GetModeStereoWidth() - w ) / 2;
  697. int y = ( videomode->GetModeStereoHeight() - h ) / 2;
  698. SetPos( x, y );
  699. }
  700. if ( m_hBugReporter )
  701. {
  702. CreateInterfaceFn factory = Sys_GetFactory( m_hBugReporter );
  703. if ( factory )
  704. {
  705. m_pBugReporter = (IBugReporter *)factory( INTERFACEVERSION_BUGREPORTER, NULL );
  706. if( m_pBugReporter )
  707. {
  708. extern CreateInterfaceFn g_AppSystemFactory;
  709. if ( m_pBugReporter->Init( g_AppSystemFactory ) )
  710. {
  711. m_bCanSubmit = true;
  712. m_bLoggedIn = true;
  713. }
  714. else
  715. {
  716. m_pBugReporter = NULL;
  717. ConColorMsg( clr, "m_pBugReporter->Init() failed\n" );
  718. return false;
  719. }
  720. }
  721. else
  722. {
  723. ConColorMsg( clr, "Couldn't get interface '%s' from '%s'\n", INTERFACEVERSION_BUGREPORTER, m_sDllName.String() );
  724. return false;
  725. }
  726. }
  727. else
  728. {
  729. ConColorMsg( clr, "Couldn't get factory '%s'\n", m_sDllName.String() );
  730. return false;
  731. }
  732. }
  733. else
  734. {
  735. ConColorMsg( clr, "Couldn't load '%s'\n", m_sDllName.String() );
  736. return false;
  737. }
  738. if ( m_bCanSubmit )
  739. {
  740. PopulateControls();
  741. }
  742. if ( m_pBugReporter && m_pBugReporter->IsPublicUI() )
  743. {
  744. m_pSaveBSP->SetVisible( false );
  745. m_pBSPURL->SetVisible( false );
  746. m_pChooseVMFFolder->SetVisible( false );
  747. m_pSaveVMF->SetVisible( false );
  748. m_pVMFURL->SetVisible( false );
  749. m_pIncludeFile->SetVisible( false );
  750. m_pClearIncludes->SetVisible( false );
  751. m_pAssignTo->SetVisible( false );
  752. m_pSeverity->SetVisible( false );
  753. m_pPriority->SetVisible( false );
  754. m_pGameArea->SetVisible( false );
  755. m_pMapNumber->SetVisible( false );
  756. m_pIncludedFiles->SetVisible( false );
  757. m_pSubmitter->SetVisible( false );
  758. if ( Steam3Client().SteamUser() )
  759. {
  760. m_pSubmitterLabel->SetText( Steam3Client().SteamUser()->GetSteamID().Render() );
  761. }
  762. else
  763. {
  764. m_pSubmitterLabel->SetText( "PublicUser" );
  765. }
  766. m_pSubmitterLabel->SetVisible( true );
  767. m_bQueryingSteamForCSER = true;
  768. }
  769. else
  770. {
  771. m_pEmail->SetVisible( false );
  772. m_pSubmitterLabel->SetVisible( true );
  773. }
  774. Q_snprintf( m_szVMFContentDirFullpath, sizeof( m_szVMFContentDirFullpath ), "%s/maps", com_gamedir );
  775. Q_strlower( m_szVMFContentDirFullpath );
  776. Q_FixSlashes( m_szVMFContentDirFullpath );
  777. m_pBuildNumber->SetText( va( "%d", build_number() ) );
  778. return false;
  779. }
  780. void CBugUIPanel::Shutdown()
  781. {
  782. if ( m_pBugReporter )
  783. {
  784. m_pBugReporter->Shutdown();
  785. }
  786. m_pBugReporter = NULL;
  787. if ( m_hBugReporter )
  788. {
  789. Sys_UnloadModule( m_hBugReporter );
  790. m_hBugReporter = 0;
  791. }
  792. }
  793. //-----------------------------------------------------------------------------
  794. // Purpose:
  795. //-----------------------------------------------------------------------------
  796. CBugUIPanel::~CBugUIPanel()
  797. {
  798. }
  799. void CBugUIPanel::OnTick()
  800. {
  801. BaseClass::OnTick();
  802. CheckContinueQueryingSteamForCSERList();
  803. if ( !IsVisible() )
  804. {
  805. if ( !m_bTakingSnapshot )
  806. {
  807. return;
  808. }
  809. if ( host_framecount < m_nSnapShotFrame + 2 )
  810. return;;
  811. m_bTakingSnapshot = false;
  812. m_nSnapShotFrame = -1;
  813. m_pScreenShotURL->SetText( m_szScreenShotName );
  814. if (m_bHidGameUIForSnapshot)
  815. {
  816. EngineVGui()->ActivateGameUI();
  817. }
  818. m_bHidGameUIForSnapshot = false;
  819. SetVisible( true );
  820. MoveToFront();
  821. }
  822. if ( !m_bCanSubmit )
  823. {
  824. if ( m_pBugReporter && !m_pBugReporter->IsPublicUI() )
  825. {
  826. if ( !m_bCanSeeRepository )
  827. {
  828. Warning( "Bug UI disabled: Couldn't see repository\n" );
  829. }
  830. else if ( !m_bLoggedIn )
  831. {
  832. Warning( "Bug UI disabled: Couldn't log in to PVCS Tracker\n" );
  833. }
  834. else
  835. {
  836. Assert( 0 );
  837. }
  838. }
  839. SetVisible( false );
  840. return;
  841. }
  842. m_pSubmit->SetEnabled( IsValidSubmission( false ) );
  843. if (m_flPauseTime > 0.0f )
  844. {
  845. if ( m_flPauseTime <= system()->GetFrameTime())
  846. {
  847. m_flPauseTime = 0.0f;
  848. if ( m_pProgressDialog )
  849. {
  850. m_pProgressDialog->Close();
  851. }
  852. m_pProgressDialog = NULL;
  853. OnFinishBugReport();
  854. m_bWaitForFinish = true;
  855. if ( !m_hFinishedDialog.Get() )
  856. {
  857. m_hFinishedDialog = new CBugReportFinishedDialog(NULL, "FinishDialog", "#Steam_FinishedBug_WorkingTitle", "#Steam_FinishedBug_Text" );
  858. m_hFinishedDialog->Activate();
  859. vgui::input()->SetAppModalSurface(m_hFinishedDialog->GetVPanel());
  860. }
  861. }
  862. else
  863. {
  864. if ( m_pProgressDialog )
  865. {
  866. float percent = clamp( 1.0f - (float)( m_flPauseTime - system()->GetFrameTime() ) / (float)PUBLIC_BUGREPORT_WAIT_TIME, 0.0f, 1.0f );
  867. m_pProgressDialog->SetProgress( percent );
  868. }
  869. }
  870. }
  871. if ( m_bWaitForFinish && !m_hFinishedDialog.Get() )
  872. {
  873. m_bWaitForFinish = false;
  874. Close();
  875. }
  876. }
  877. //-----------------------------------------------------------------------------
  878. // Purpose:
  879. // Input : *suffix -
  880. // *buf -
  881. // bufsize -
  882. //-----------------------------------------------------------------------------
  883. void CBugUIPanel::GetDataFileBase( char const *suffix, char *buf, int bufsize )
  884. {
  885. struct tm t;
  886. VCRHook_LocalTime( &t );
  887. char who[ 128 ];
  888. Q_strncpy( who, suffix, sizeof( who ) );
  889. Q_strlower( who );
  890. if ( m_pBugReporter && m_pBugReporter->IsPublicUI() )
  891. {
  892. Q_snprintf( buf, bufsize, "%i_%02i_%02i", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday );
  893. }
  894. else
  895. {
  896. Q_snprintf( buf, bufsize, "%i_%02i_%02i_%s", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, who );
  897. }
  898. }
  899. //-----------------------------------------------------------------------------
  900. // Purpose:
  901. // Output : const char
  902. //-----------------------------------------------------------------------------
  903. const char *CBugUIPanel::GetRepositoryURL( void )
  904. {
  905. const char *pURL = m_pBugReporter->GetRepositoryURL();
  906. if ( pURL )
  907. return pURL;
  908. return BUG_REPOSITORY_URL;
  909. }
  910. const char *CBugUIPanel::GetSubmissionURL( int bugid )
  911. {
  912. const char *pURL = m_pBugReporter->GetSubmissionURL();
  913. if ( pURL )
  914. return pURL;
  915. static char url[512];
  916. Q_snprintf(url, sizeof(url), "%s/%i", GetRepositoryURL(), bugid);
  917. return url;
  918. }
  919. //-----------------------------------------------------------------------------
  920. // Purpose:
  921. //-----------------------------------------------------------------------------
  922. void CBugUIPanel::OnTakeSnapshot()
  923. {
  924. GetDataFileBase( GetSubmitter(), m_szScreenShotName, sizeof( m_szScreenShotName ) );
  925. m_nSnapShotFrame = host_framecount;
  926. m_bTakingSnapshot = true;
  927. if ( EngineVGui()->IsGameUIVisible() )
  928. {
  929. m_bHidGameUIForSnapshot = true;
  930. EngineVGui()->HideGameUI();
  931. }
  932. else
  933. {
  934. m_bHidGameUIForSnapshot = false;
  935. }
  936. SetVisible( false );
  937. // Internal reports at 100% quality .jpg
  938. int quality = 100;
  939. if ( m_pBugReporter && m_pBugReporter->IsPublicUI() )
  940. {
  941. quality = 40;
  942. }
  943. Cbuf_AddText( va( "jpeg \"%s\" %i\n", m_szScreenShotName, quality ) );
  944. }
  945. //-----------------------------------------------------------------------------
  946. // Purpose:
  947. //-----------------------------------------------------------------------------
  948. void CBugUIPanel::OnSaveGame()
  949. {
  950. GetDataFileBase( GetSubmitter(), m_szSaveGameName, sizeof( m_szSaveGameName ) );
  951. if ( m_pBugReporter && m_pBugReporter->IsPublicUI() )
  952. {
  953. // External users send us a "minisave" file which doesn't contain data from other previously encoudntered/connected levels
  954. Cbuf_AddText( va( "minisave %s\n", m_szSaveGameName ) );
  955. }
  956. else
  957. {
  958. Cbuf_AddText( va( "save %s notmostrecent copymap\n", m_szSaveGameName ) );
  959. }
  960. m_pSaveGameURL->SetText( m_szSaveGameName );
  961. }
  962. void CBugUIPanel::OnSaveBSP()
  963. {
  964. GetDataFileBase( GetSubmitter(), m_szBSPName, sizeof( m_szBSPName ) );
  965. m_pBSPURL->SetText( m_szBSPName );
  966. }
  967. void CBugUIPanel::OnSaveVMF()
  968. {
  969. if ( m_pBugReporter && m_pBugReporter->IsPublicUI() )
  970. return;
  971. char level[ 256 ];
  972. m_pLevelName->GetText( level, sizeof( level ) );
  973. // See if .vmf exists in assumed location
  974. char localfile[ 512 ];
  975. Q_snprintf( localfile, sizeof( localfile ), "%s/%s.vmf", m_szVMFContentDirFullpath, level );
  976. if ( !g_pFileSystem->FileExists( localfile, NULL ) )
  977. {
  978. if ( !m_hDirectorySelectDialog.Get() )
  979. {
  980. m_hDirectorySelectDialog = new DirectorySelectDialog( this, "Choose .vmf folder" );
  981. }
  982. m_bAddVMF = true;
  983. m_hDirectorySelectDialog->SetStartDirectory( m_szVMFContentDirFullpath );
  984. m_hDirectorySelectDialog->DoModal();
  985. return;
  986. }
  987. GetDataFileBase( GetSubmitter(), m_szVMFName, sizeof( m_szVMFName ) );
  988. m_pVMFURL->SetText( m_szVMFName );
  989. }
  990. void CBugUIPanel::OnChooseVMFFolder()
  991. {
  992. if ( !m_hDirectorySelectDialog.Get() )
  993. {
  994. m_hDirectorySelectDialog = new DirectorySelectDialog( this, "Choose .vmf folder" );
  995. }
  996. m_bAddVMF = false;
  997. m_hDirectorySelectDialog->SetStartDirectory( m_szVMFContentDirFullpath );
  998. m_hDirectorySelectDialog->DoModal();
  999. }
  1000. void CBugUIPanel::OnChooseArea(vgui::Panel *panel)
  1001. {
  1002. if (panel != m_pGameArea)
  1003. return;
  1004. if (!Q_strcmp(BUG_REPORTER_DLLNAME, GetInternalBugReporterDLL()))
  1005. {
  1006. int area_index = m_pGameArea->GetActiveItem();
  1007. int c = m_pBugReporter->GetLevelCount(area_index);
  1008. int item = -1;
  1009. const char *currentLevel = cl.IsActive() ? cl.m_szLevelBaseName : "console";
  1010. m_pMapNumber->DeleteAllItems();
  1011. for ( int i = 0; i < c; i++ )
  1012. {
  1013. const char *level = m_pBugReporter->GetLevel(area_index, i );
  1014. int id = m_pMapNumber->AddItem( level, NULL );
  1015. if (!Q_strcmp(currentLevel, level))
  1016. {
  1017. item = id;
  1018. }
  1019. }
  1020. if (item >= 0)
  1021. {
  1022. m_pMapNumber->ActivateItem( item );
  1023. }
  1024. else
  1025. {
  1026. m_pMapNumber->ActivateItemByRow(0);
  1027. }
  1028. }
  1029. }
  1030. void CBugUIPanel::OnDirectorySelected( char const *dir )
  1031. {
  1032. Q_strncpy( m_szVMFContentDirFullpath, dir, sizeof( m_szVMFContentDirFullpath ) );
  1033. Q_strlower( m_szVMFContentDirFullpath );
  1034. Q_FixSlashes( m_szVMFContentDirFullpath );
  1035. Q_StripTrailingSlash( m_szVMFContentDirFullpath );
  1036. if ( m_hDirectorySelectDialog != 0 )
  1037. {
  1038. m_hDirectorySelectDialog->MarkForDeletion();
  1039. }
  1040. // See if .vmf exists in assumed location
  1041. if ( m_bAddVMF )
  1042. {
  1043. GetDataFileBase( GetSubmitter(), m_szVMFName, sizeof( m_szVMFName ) );
  1044. m_pVMFURL->SetText( m_szVMFName );
  1045. }
  1046. m_bAddVMF = false;
  1047. }
  1048. void CBugUIPanel::OnFileSelected( char const *fullpath )
  1049. {
  1050. bool baseDirFile = false;
  1051. if ( m_pBugReporter && m_pBugReporter->IsPublicUI() )
  1052. return;
  1053. if ( !fullpath || !fullpath[ 0 ] )
  1054. return;
  1055. char relativepath[ 512 ];
  1056. if ( !g_pFileSystem->FullPathToRelativePath( fullpath, relativepath, sizeof( relativepath ) ) )
  1057. {
  1058. if ( Q_stristr( fullpath, com_basedir ) )
  1059. {
  1060. Q_snprintf( relativepath, sizeof( relativepath ), "..%s", fullpath + strlen(com_basedir) );
  1061. baseDirFile = true;
  1062. }
  1063. else
  1064. {
  1065. Msg("Only files beneath the base game directory can be included\n" );
  1066. return;
  1067. }
  1068. }
  1069. char ext[ 10 ];
  1070. Q_ExtractFileExtension( relativepath, ext, sizeof( ext ) );
  1071. if ( m_hFileOpenDialog != 0 )
  1072. {
  1073. m_hFileOpenDialog->MarkForDeletion();
  1074. }
  1075. includedfile inc;
  1076. Q_strncpy( inc.name, relativepath, sizeof( inc.name ) );
  1077. if ( baseDirFile )
  1078. {
  1079. Q_snprintf( inc.fixedname, sizeof( inc.fixedname ), "%s", inc.name+3 ); // strip the "..\"
  1080. }
  1081. else
  1082. {
  1083. Q_snprintf( inc.fixedname, sizeof( inc.fixedname ), "%s", inc.name );
  1084. }
  1085. Q_FixSlashes( inc.fixedname );
  1086. m_IncludedFiles.AddToTail( inc );
  1087. char concat[ 8192 ];
  1088. concat[ 0 ] = 0;
  1089. for ( int i = 0 ; i < m_IncludedFiles.Count(); ++i )
  1090. {
  1091. Q_strncat( concat, m_IncludedFiles[ i ].name, sizeof( concat), COPY_ALL_CHARACTERS );
  1092. Q_strncat( concat, "\n", sizeof( concat), COPY_ALL_CHARACTERS );
  1093. }
  1094. m_pIncludedFiles->SetText( concat );
  1095. }
  1096. void CBugUIPanel::OnIncludeFile()
  1097. {
  1098. if ( m_pBugReporter && m_pBugReporter->IsPublicUI() )
  1099. return;
  1100. if ( !m_hFileOpenDialog.Get() )
  1101. {
  1102. m_hFileOpenDialog = new FileOpenDialog( this, "Choose file to include", true );
  1103. if ( m_hFileOpenDialog != 0 )
  1104. {
  1105. m_hFileOpenDialog->AddFilter("*.*", "All Files (*.*)", true);
  1106. }
  1107. }
  1108. if ( m_hFileOpenDialog )
  1109. {
  1110. char startPath[ MAX_PATH ];
  1111. Q_strncpy( startPath, com_gamedir, sizeof( startPath ) );
  1112. Q_FixSlashes( startPath );
  1113. m_hFileOpenDialog->SetStartDirectory( startPath );
  1114. m_hFileOpenDialog->DoModal( false );
  1115. }
  1116. //GetDataFileBase( GetSubmitter(), m_szVMFName, sizeof( m_szVMFName ) );
  1117. }
  1118. void CBugUIPanel::OnClearIncludedFiles()
  1119. {
  1120. m_IncludedFiles.Purge();
  1121. m_pIncludedFiles->SetText( "" );
  1122. }
  1123. //-----------------------------------------------------------------------------
  1124. // Purpose: Shows the panel
  1125. //-----------------------------------------------------------------------------
  1126. void CBugUIPanel::Activate()
  1127. {
  1128. if ( !m_bValidated )
  1129. {
  1130. if ( Init() )
  1131. {
  1132. m_bValidated = true;
  1133. DetermineSubmitterName();
  1134. }
  1135. }
  1136. if ( m_pGameArea->GetItemCount() != 0 )
  1137. {
  1138. int iArea = GetArea();
  1139. if ( iArea != 0 )
  1140. m_pGameArea->ActivateItem( iArea );
  1141. }
  1142. {
  1143. int c = m_pMapNumber->GetItemCount();
  1144. const char *currentLevel = cl.IsActive() ? cl.m_szLevelBaseName : "console";
  1145. int item = -1;
  1146. for ( int i = 0; i < c; i++ )
  1147. {
  1148. int id = m_pMapNumber->GetItemIDFromRow(i);
  1149. char level[256];
  1150. m_pMapNumber->GetItemText(id, level, sizeof(level));
  1151. if (!Q_strcmp(currentLevel, level))
  1152. {
  1153. item = id;
  1154. }
  1155. }
  1156. if (item >= 0)
  1157. {
  1158. m_pMapNumber->ActivateItem( item );
  1159. }
  1160. else
  1161. {
  1162. m_pMapNumber->ActivateItemByRow(0);
  1163. }
  1164. }
  1165. if ( cl.IsActive() )
  1166. {
  1167. Vector org = MainViewOrigin();
  1168. QAngle ang;
  1169. VectorAngles( MainViewForward(), ang );
  1170. IClientEntity *localPlayer = entitylist->GetClientEntity( cl.m_nPlayerSlot + 1 );
  1171. if ( localPlayer )
  1172. {
  1173. org = localPlayer->GetAbsOrigin();
  1174. }
  1175. m_pPosition->SetText( va( "%f %f %f", org.x, org.y, org.z ) );
  1176. m_pOrientation->SetText( va( "%f %f %f", ang.x, ang.y, ang.z ) );
  1177. m_pLevelName->SetText( cl.m_szLevelBaseName );
  1178. m_pSaveGame->SetEnabled( ( cl.m_nMaxClients == 1 ) ? true : false );
  1179. m_pSaveBSP->SetEnabled( true );
  1180. m_pSaveVMF->SetEnabled( true );
  1181. m_pChooseVMFFolder->SetEnabled( true );
  1182. }
  1183. else
  1184. {
  1185. m_pPosition->SetText( "console" );
  1186. m_pOrientation->SetText( "console" );
  1187. m_pLevelName->SetText( "console" );
  1188. m_pSaveGame->SetEnabled( false );
  1189. m_pSaveBSP->SetEnabled( false );
  1190. m_pSaveVMF->SetEnabled( false );
  1191. m_pChooseVMFFolder->SetEnabled( false );
  1192. }
  1193. BaseClass::Activate();
  1194. m_pTitle->RequestFocus();
  1195. m_pTitle->SelectAllText( true );
  1196. // Automatically make a screenshot if requested.
  1197. if (!m_szScreenShotName[0] )
  1198. {
  1199. bool bAutoAddScreenshot = false;
  1200. // For public bugreports should be explicitly requested
  1201. if ( m_bIsPublic )
  1202. {
  1203. if ( m_fAutoAddScreenshot == CBugUIPanel::eAutoAddScreenshot_Add )
  1204. {
  1205. bAutoAddScreenshot = true;
  1206. }
  1207. }
  1208. // For internal bugreports shouldn't be explicitly denied
  1209. else
  1210. {
  1211. if ( m_fAutoAddScreenshot != CBugUIPanel::eAutoAddScreenshot_DontAdd )
  1212. {
  1213. bAutoAddScreenshot = true;
  1214. }
  1215. }
  1216. if ( bAutoAddScreenshot )
  1217. {
  1218. OnTakeSnapshot();
  1219. }
  1220. }
  1221. }
  1222. void CBugUIPanel::WipeData()
  1223. {
  1224. m_fAutoAddScreenshot = eAutoAddScreenshot_Detect;
  1225. m_szScreenShotName[ 0 ] = 0;
  1226. m_pScreenShotURL->SetText( "Screenshot file" );
  1227. m_szSaveGameName[ 0 ] = 0;
  1228. m_pSaveGameURL->SetText( "Save game file" );
  1229. m_szBSPName[ 0 ] = 0;
  1230. m_pBSPURL->SetText( ".bsp file" );
  1231. m_szVMFName[ 0 ] = 0;
  1232. m_pVMFURL->SetText( ".vmf file" );
  1233. m_IncludedFiles.Purge();
  1234. m_pIncludedFiles->SetText( "" );
  1235. // m_pTitle->SetText( "" );
  1236. m_pDescription->SetText( "" );
  1237. m_pEmail->SetText( "" );
  1238. }
  1239. //-----------------------------------------------------------------------------
  1240. // Purpose: method to make sure the email address is acceptable to be used by steam
  1241. //-----------------------------------------------------------------------------
  1242. bool CBugUIPanel::IsValidEmailAddress( char const *email )
  1243. {
  1244. // basic size check
  1245. if (!email || strlen(email) < 5)
  1246. return false;
  1247. // make sure all the characters in the string are valid
  1248. {for (const char *sz = email; *sz; sz++)
  1249. {
  1250. if (!V_isalnum(*sz) && *sz != '.' && *sz != '-' && *sz != '@' && *sz != '_')
  1251. return false;
  1252. }}
  1253. // make sure it has letters, then the '@', then letters, then '.', then letters
  1254. const char *sz = email;
  1255. if (!V_isalnum(*sz))
  1256. return false;
  1257. sz = strstr(sz, "@");
  1258. if (!sz)
  1259. return false;
  1260. sz++;
  1261. if (!V_isalnum(*sz))
  1262. return false;
  1263. sz = strstr(sz, ".");
  1264. if (!sz)
  1265. return false;
  1266. sz++;
  1267. if (!V_isalnum(*sz))
  1268. return false;
  1269. return true;
  1270. }
  1271. bool CBugUIPanel::IsValidSubmission( bool verbose )
  1272. {
  1273. if ( !m_pBugReporter )
  1274. return false;
  1275. bool isPublic = m_pBugReporter->IsPublicUI();
  1276. char title[ 256 ];
  1277. char desc[ 4096 ];
  1278. title[ 0 ] = 0;
  1279. desc[ 0 ] = 0;
  1280. m_pTitle->GetText( title, sizeof( title ) );
  1281. if ( !title[ 0 ] )
  1282. {
  1283. if ( verbose )
  1284. {
  1285. Warning( "Bug must have a title\n" );
  1286. }
  1287. return false;
  1288. }
  1289. // Only require description in public UI
  1290. if ( isPublic )
  1291. {
  1292. m_pDescription->GetText( desc, sizeof( desc ) );
  1293. if ( !desc[ 0 ] )
  1294. {
  1295. if ( verbose )
  1296. {
  1297. Warning( "Bug must have a description\n" );
  1298. }
  1299. return false;
  1300. }
  1301. }
  1302. if ( !isPublic && m_pSeverity->GetActiveItem() < 0 )
  1303. {
  1304. if ( verbose )
  1305. {
  1306. Warning( "Severity not set!\n" );
  1307. }
  1308. return false;
  1309. }
  1310. if ( !isPublic && m_pAssignTo->GetActiveItem() < 0 )
  1311. {
  1312. if ( verbose )
  1313. {
  1314. Warning( "Owner not set!\n" );
  1315. }
  1316. return false;
  1317. }
  1318. char owner[ 256 ];
  1319. Q_strncpy( owner, m_pBugReporter->GetDisplayName( m_pAssignTo->GetActiveItem() ), sizeof( owner ) );
  1320. if ( !isPublic && !Q_stricmp( owner, "<<Unassigned>>" ) )
  1321. {
  1322. if ( verbose )
  1323. {
  1324. Warning( "Owner not set!\n" );
  1325. }
  1326. return false;
  1327. }
  1328. if ( !isPublic && m_pPriority->GetActiveItem() < 0 )
  1329. {
  1330. if ( verbose )
  1331. {
  1332. Warning( "Priority not set!\n" );
  1333. }
  1334. return false;
  1335. }
  1336. if ( !isPublic && m_pReportType->GetActiveItem() < 0 )
  1337. {
  1338. if ( verbose )
  1339. {
  1340. Warning( "Report Type not set!\n" );
  1341. }
  1342. return false;
  1343. }
  1344. if ( !isPublic && m_pGameArea->GetActiveItem() < 0 )
  1345. {
  1346. if ( verbose )
  1347. {
  1348. Warning( "Area not set!\n" );
  1349. }
  1350. return false;
  1351. }
  1352. // Public must have a selection and it can't be the "<<Choose One>>"
  1353. if ( isPublic && m_pReportType->GetActiveItem() <= 0 )
  1354. {
  1355. if ( verbose )
  1356. {
  1357. Warning( "Report type not set!\n" );
  1358. }
  1359. return false;
  1360. }
  1361. if ( isPublic )
  1362. {
  1363. char email[ 80 ];
  1364. m_pEmail->GetText( email, sizeof( email ) );
  1365. if ( email[ 0 ] != 0 &&
  1366. !IsValidEmailAddress( email ) )
  1367. {
  1368. return false;
  1369. }
  1370. }
  1371. return true;
  1372. }
  1373. bool CBugUIPanel::AddBugTextToZip( char const *textfilename, char const *text, int textlen )
  1374. {
  1375. if ( !m_hZip )
  1376. {
  1377. // Create using OS pagefile memory...
  1378. m_hZip = CreateZipZ( 0, MAX_ZIP_SIZE, ZIP_MEMORY );
  1379. Assert( m_hZip );
  1380. if ( !m_hZip )
  1381. {
  1382. return false;
  1383. }
  1384. }
  1385. ZipAdd( m_hZip, textfilename, (void *)text, textlen, ZIP_MEMORY );
  1386. return true;
  1387. }
  1388. bool CBugUIPanel::AddFileToZip( char const *relative )
  1389. {
  1390. if ( !m_hZip )
  1391. {
  1392. // Create using OS pagefile memory...
  1393. m_hZip = CreateZipZ( 0, MAX_ZIP_SIZE, ZIP_MEMORY );
  1394. Assert( m_hZip );
  1395. if ( !m_hZip )
  1396. {
  1397. return false;
  1398. }
  1399. }
  1400. char fullpath[ 512 ];
  1401. if ( g_pFileSystem->RelativePathToFullPath( relative, "GAME", fullpath, sizeof( fullpath ) ) )
  1402. {
  1403. char extension[ 32 ];
  1404. Q_ExtractFileExtension( relative, extension, sizeof( extension ) );
  1405. char basename[ 256 ];
  1406. Q_FileBase( relative, basename, sizeof( basename ) );
  1407. char outname[ 512 ];
  1408. Q_snprintf( outname, sizeof( outname ), "%s.%s", basename, extension );
  1409. ZipAdd( m_hZip, outname, fullpath, 0, ZIP_FILENAME );
  1410. return true;
  1411. }
  1412. return false;
  1413. }
  1414. void CBugUIPanel::OnSubmit()
  1415. {
  1416. if ( !m_bCanSubmit )
  1417. {
  1418. return;
  1419. }
  1420. if ( !IsValidSubmission( true ) )
  1421. {
  1422. // Play deny sound
  1423. DenySound();
  1424. return;
  1425. }
  1426. bool isPublic = m_pBugReporter->IsPublicUI();
  1427. char title[ 80 ];
  1428. char desc[ 4096 ];
  1429. char severity[ 128 ];
  1430. char area[ 128 ];
  1431. char mapnumber[ 128 ];
  1432. char priority[ 128 ];
  1433. char assignedto[ 128 ];
  1434. char level[ 128 ];
  1435. char position[ 128 ];
  1436. char orientation[ 128 ];
  1437. char build[ 128 ];
  1438. char reporttype[ 128 ];
  1439. char email[ 80 ];
  1440. title[ 0 ] = 0;
  1441. desc[ 0 ] = 0;
  1442. severity[ 0 ] = 0;
  1443. area[ 0 ] = 0;
  1444. mapnumber[ 0] = 0;
  1445. priority[ 0 ] = 0;
  1446. assignedto[ 0 ] = 0;
  1447. level[ 0 ] = 0;
  1448. orientation[ 0 ] = 0;
  1449. position[ 0 ] = 0;
  1450. build[ 0 ] = 0;
  1451. reporttype [ 0 ] = 0;
  1452. email[ 0 ] = 0;
  1453. Assert( m_pBugReporter );
  1454. // Stuff bug data files up to server
  1455. m_pBugReporter->StartNewBugReport();
  1456. char temp[ 80 ];
  1457. m_pTitle->GetText( temp, sizeof( temp ) );
  1458. if ( host_state.worldmodel )
  1459. {
  1460. char mapname[256];
  1461. CL_SetupMapName( modelloader->GetName( host_state.worldmodel ), mapname, sizeof( mapname ) );
  1462. Q_snprintf( title, sizeof( title ), "%s: %s", mapname, temp );
  1463. }
  1464. else
  1465. {
  1466. Q_snprintf( title, sizeof( title ), "%s", temp );
  1467. }
  1468. Msg( "title: %s\n", title );
  1469. m_pDescription->GetText( desc, sizeof( desc ) );
  1470. Msg( "description: %s\n", desc );
  1471. m_pLevelName->GetText( level, sizeof( level ) );
  1472. m_pPosition->GetText( position, sizeof( position ) );
  1473. m_pOrientation->GetText( orientation, sizeof( orientation ) );
  1474. m_pBuildNumber->GetText( build, sizeof( build ) );
  1475. Q_strncat( build, " (Steam)", sizeof(build), COPY_ALL_CHARACTERS );
  1476. MaterialAdapterInfo_t info;
  1477. materials->GetDisplayAdapterInfo( materials->GetCurrentAdapter(), info );
  1478. char driverinfo[ 2048 ];
  1479. char const *dxlevel = "Unk";
  1480. if ( g_pMaterialSystemHardwareConfig )
  1481. {
  1482. dxlevel = COM_DXLevelToString( g_pMaterialSystemHardwareConfig->GetDXSupportLevel() ) ;
  1483. }
  1484. char osversion[ 256 ];
  1485. DisplaySystemVersion( osversion, sizeof( osversion ) );
  1486. Q_snprintf( driverinfo, sizeof( driverinfo ),
  1487. "OS Version: %s\n"
  1488. "Driver Name: %s\n"
  1489. "VendorId / DeviceId: 0x%x / 0x%x\n"
  1490. "SubSystem / Rev: 0x%x / 0x%x\n"
  1491. "DXLevel: %s\nVid: %i x %i\n"
  1492. "Framerate: %.3f\n",
  1493. osversion,
  1494. info.m_pDriverName,
  1495. info.m_VendorID,
  1496. info.m_DeviceID,
  1497. info.m_SubSysID,
  1498. info.m_Revision,
  1499. dxlevel ? dxlevel : "Unk",
  1500. videomode->GetModeWidth(), videomode->GetModeHeight(),
  1501. g_fFramesPerSecond );
  1502. Msg( "%s", driverinfo );
  1503. int latency = 0;
  1504. if ( cl.m_NetChannel )
  1505. {
  1506. latency = (int)( 1000.0f * cl.m_NetChannel->GetAvgLatency( FLOW_OUTGOING ) );
  1507. }
  1508. ConVarRef host_thread_mode( "host_thread_mode" );
  1509. ConVarRef sv_alternateticks( "sv_alternateticks" );
  1510. ConVarRef ai_strong_optimizations( "ai_strong_optimizations" );
  1511. char misc[ 1024 ];
  1512. Q_snprintf( misc, sizeof( misc ), "Convars:\n\tskill: %i\n\tnet: rate %i update %i cmd %i latency %i msec\n\thost_thread_mode: %i\n\tsv_alternateticks: %i\n\tai_strong_optimizations: %i\n",
  1513. skill.GetInt(),
  1514. cl_rate->GetInt(),
  1515. (int)cl_updaterate->GetFloat(),
  1516. (int)cl_cmdrate->GetFloat(),
  1517. latency,
  1518. host_thread_mode.GetInt(),
  1519. sv_alternateticks.GetInt(),
  1520. ai_strong_optimizations.GetInt()
  1521. );
  1522. if ( cl.IsActive() && g_ServerGlobalVariables.mapversion != 0 && host_state.worldmodel )
  1523. {
  1524. // Note, this won't work in multiplayer, oh well...
  1525. extern CGlobalVars g_ServerGlobalVariables;
  1526. char misc2[ 256 ];
  1527. long mapfiletime = g_pFileSystem->GetFileTime( modelloader->GetName( host_state.worldmodel ), "GAME" );
  1528. if ( !isPublic && mapfiletime != 0L )
  1529. {
  1530. char filetimebuf[ 64 ];
  1531. g_pFileSystem->FileTimeToString( filetimebuf, sizeof( filetimebuf ), mapfiletime );
  1532. Q_snprintf( misc2, sizeof( misc2 ), "Map version: %i\nFile timestamp: %s", g_ServerGlobalVariables.mapversion, filetimebuf );
  1533. Q_strncat( misc, misc2, sizeof( misc ), COPY_ALL_CHARACTERS );
  1534. }
  1535. else
  1536. {
  1537. Q_snprintf( misc2, sizeof( misc2 ), "Map version: %i\n", g_ServerGlobalVariables.mapversion );
  1538. Q_strncat( misc, misc2, sizeof( misc ), COPY_ALL_CHARACTERS );
  1539. }
  1540. }
  1541. if ( sv.IsActive() && serverGameClients )
  1542. {
  1543. char gamedlldata[ 2048 ];
  1544. Q_memset( gamedlldata, 0, sizeof( gamedlldata ) );
  1545. serverGameClients->GetBugReportInfo( gamedlldata, sizeof( gamedlldata ) );
  1546. Q_strncat( misc, gamedlldata, sizeof( misc ), COPY_ALL_CHARACTERS );
  1547. }
  1548. {
  1549. char misc2[ 128 ];
  1550. Q_snprintf( misc2, sizeof( misc2 ), "gamedir: %s\n", com_gamedir );
  1551. Q_strncat( misc, misc2, sizeof( misc ), COPY_ALL_CHARACTERS );
  1552. //Q_snprintf( misc2, sizeof( misc2 ), "game: %s\n", COM_GetModDirectory() );
  1553. //Q_strncat( misc, misc2, sizeof( misc ), COPY_ALL_CHARACTERS );
  1554. }
  1555. Msg( "%s", misc );
  1556. if ( !isPublic )
  1557. {
  1558. m_pSeverity->GetText( severity, sizeof( severity ) );
  1559. Msg( "severity %s\n", severity );
  1560. m_pGameArea->GetText( area, sizeof( area ) );
  1561. Msg( "area %s\n", area );
  1562. m_pMapNumber->GetText( mapnumber, sizeof( mapnumber) );
  1563. Msg( "map number %s\n", mapnumber);
  1564. m_pPriority->GetText( priority, sizeof( priority ) );
  1565. Msg( "priority %s\n", priority );
  1566. m_pAssignTo->GetText( assignedto, sizeof( assignedto ) );
  1567. Msg( "owner %s\n", assignedto );
  1568. }
  1569. if ( isPublic )
  1570. {
  1571. m_pEmail->GetText( email, sizeof( email ) );
  1572. if ( Q_strlen( email ) > 0 )
  1573. {
  1574. Msg( "email %s\n", email );
  1575. }
  1576. else
  1577. {
  1578. Msg( "Not sending email address\n" );
  1579. }
  1580. m_pBugReporter->SetOwner( email );
  1581. char submitter[ 80 ];
  1582. m_pSubmitterLabel->GetText( submitter, sizeof( submitter ) );
  1583. m_pBugReporter->SetSubmitter( submitter );
  1584. }
  1585. else
  1586. {
  1587. m_pBugReporter->SetOwner( m_pBugReporter->GetUserNameForDisplayName( assignedto ) );
  1588. if ( m_bUseNameForSubmitter )
  1589. {
  1590. char submitter[ 80 ];
  1591. m_pSubmitter->GetText( submitter, sizeof( submitter ) );
  1592. m_pBugReporter->SetSubmitter( submitter );
  1593. }
  1594. else
  1595. {
  1596. m_pBugReporter->SetSubmitter( NULL );
  1597. }
  1598. }
  1599. m_pReportType->GetText( reporttype, sizeof( reporttype ) );
  1600. Msg( "report type %s\n", reporttype );
  1601. Msg( "submitter %s\n", m_pBugReporter->GetUserName() );
  1602. Msg( "level %s\n", level );
  1603. Msg( "position %s\n", position );
  1604. Msg( "orientation %s\n", orientation );
  1605. Msg( "build %s\n", build );
  1606. if ( m_szSaveGameName[ 0 ] )
  1607. {
  1608. Msg( "save file save/%s.sav\n", m_szSaveGameName );
  1609. }
  1610. else
  1611. {
  1612. Msg( "no save game\n" );
  1613. }
  1614. if ( m_szScreenShotName[ 0 ] )
  1615. {
  1616. Msg( "screenshot screenshots/%s.jpg\n", m_szScreenShotName );
  1617. }
  1618. else
  1619. {
  1620. Msg( "no screenshot\n" );
  1621. }
  1622. if ( !isPublic )
  1623. {
  1624. if ( m_szBSPName[ 0 ] )
  1625. {
  1626. Msg( "bsp file maps/%s.bsp\n", m_szBSPName );
  1627. }
  1628. if ( m_szVMFName[ 0 ] )
  1629. {
  1630. Msg( "vmf file maps/%s.vmf\n", m_szVMFName );
  1631. }
  1632. if ( m_IncludedFiles.Count() > 0 )
  1633. {
  1634. for ( int i = 0; i < m_IncludedFiles.Count(); ++i )
  1635. {
  1636. Msg( "Include: %s\n", m_IncludedFiles[ i ].name );
  1637. }
  1638. }
  1639. }
  1640. m_pBugReporter->SetTitle( title );
  1641. m_pBugReporter->SetDescription( desc );
  1642. m_pBugReporter->SetLevel( level );
  1643. m_pBugReporter->SetPosition( position );
  1644. m_pBugReporter->SetOrientation( orientation );
  1645. m_pBugReporter->SetBuildNumber( build );
  1646. m_pBugReporter->SetSeverity( severity );
  1647. m_pBugReporter->SetPriority( priority );
  1648. m_pBugReporter->SetArea( area );
  1649. m_pBugReporter->SetMapNumber( mapnumber );
  1650. m_pBugReporter->SetReportType( reporttype );
  1651. m_pBugReporter->SetDriverInfo( driverinfo );
  1652. m_pBugReporter->SetMiscInfo( misc );
  1653. m_pBugReporter->SetCSERAddress( m_cserIP );
  1654. m_pBugReporter->SetExeName( "hl2.exe" );
  1655. m_pBugReporter->SetGameDirectory( com_gamedir );
  1656. const CPUInformation& pi = *GetCPUInformation();
  1657. m_pBugReporter->SetRAM( GetRam() );
  1658. double fFrequency = pi.m_Speed / 1000000.0;
  1659. m_pBugReporter->SetCPU( (int)fFrequency );
  1660. m_pBugReporter->SetProcessor( pi.m_szProcessorID );
  1661. int nDxLevel = g_pMaterialSystemHardwareConfig->GetDXSupportLevel();
  1662. int vHigh = nDxLevel / 10;
  1663. int vLow = nDxLevel - vHigh * 10;
  1664. m_pBugReporter->SetDXVersion( vHigh, vLow, info.m_VendorID, info.m_DeviceID );
  1665. m_pBugReporter->SetOSVersion( osversion );
  1666. m_pBugReporter->ResetIncludedFiles();
  1667. m_pBugReporter->SetZipAttachmentName( "" );
  1668. char fn[ 512 ];
  1669. if ( m_pBugReporter->IsPublicUI() )
  1670. {
  1671. bool attachedSave = false;
  1672. bool attachedScreenshot = false;
  1673. CUtlBuffer buginfo( 0, 0, CUtlBuffer::TEXT_BUFFER );
  1674. buginfo.Printf( "Title: %s\n", title );
  1675. buginfo.Printf( "Description: %s\n\n", desc );
  1676. buginfo.Printf( "Level: %s\n", level );
  1677. buginfo.Printf( "Position: %s\n", position );
  1678. buginfo.Printf( "Orientation: %s\n", orientation );
  1679. buginfo.Printf( "BuildNumber: %s\n", build );
  1680. buginfo.Printf( "DriverInfo: %s\n", driverinfo );
  1681. buginfo.Printf( "Misc: %s\n", misc );
  1682. buginfo.Printf( "Exe: %s\n", "hl2.exe" );
  1683. char gd[ 256 ];
  1684. Q_FileBase( com_gamedir, gd, sizeof( gd ) );
  1685. buginfo.Printf( "GameDirectory: %s\n", gd );
  1686. buginfo.Printf( "Ram: %lu\n", GetRam() );
  1687. buginfo.Printf( "CPU: %i\n", (int)fFrequency );
  1688. buginfo.Printf( "Processor: %s\n", pi.m_szProcessorID );
  1689. buginfo.Printf( "DXLevel: %d\n", nDxLevel );
  1690. buginfo.Printf( "OSVersion: %s\n", osversion );
  1691. // Terminate it
  1692. buginfo.PutChar( 0 );
  1693. int maxlen = buginfo.TellPut() * 2 + 1;
  1694. char *fixed = new char [ maxlen ];
  1695. Assert( fixed );
  1696. if ( fixed )
  1697. {
  1698. Q_memset( fixed, 0, maxlen );
  1699. char *i = (char *)buginfo.Base();
  1700. char *o = fixed;
  1701. while ( *i && ( o - fixed ) < maxlen - 1 )
  1702. {
  1703. if ( *i == '\n' )
  1704. {
  1705. *o++ = '\r';
  1706. }
  1707. *o++ = *i++;
  1708. }
  1709. *o = '\0';
  1710. AddBugTextToZip( "info.txt", fixed, Q_strlen( fixed ) + 1 );
  1711. delete[] fixed;
  1712. }
  1713. else
  1714. {
  1715. Sys_Error( "Unable to allocate %i bytes for bug description\n", maxlen );
  1716. }
  1717. // Only attach .sav files in single player
  1718. if ( ( cl.m_nMaxClients == 1 ) && m_szSaveGameName[ 0 ] )
  1719. {
  1720. Q_snprintf( fn, sizeof( fn ), "save/%s.sav", m_szSaveGameName );
  1721. Q_FixSlashes( fn );
  1722. attachedSave = AddFileToZip( fn );
  1723. }
  1724. if ( m_szScreenShotName[ 0 ] )
  1725. {
  1726. Q_snprintf( fn, sizeof( fn ), "screenshots/%s.jpg", m_szScreenShotName );
  1727. Q_FixSlashes( fn );
  1728. attachedScreenshot = AddFileToZip( fn );
  1729. }
  1730. // End users can only send save games and screenshots to valve
  1731. // Don't bother uploading any attachment if it's just the info.txt file, either...
  1732. if ( m_hZip && ( attachedSave || attachedScreenshot ) )
  1733. {
  1734. Assert( m_hZip );
  1735. void *mem;
  1736. unsigned long len;
  1737. ZipGetMemory( m_hZip, &mem, &len );
  1738. if ( mem != NULL
  1739. && len > 0 )
  1740. {
  1741. // Store .zip file
  1742. FileHandle_t fh = g_pFileSystem->Open( "bug.zip", "wb" );
  1743. if ( FILESYSTEM_INVALID_HANDLE != fh )
  1744. {
  1745. g_pFileSystem->Write( mem, len, fh );
  1746. g_pFileSystem->Close( fh );
  1747. m_pBugReporter->SetZipAttachmentName( "bug.zip" );
  1748. }
  1749. }
  1750. }
  1751. if ( m_hZip )
  1752. {
  1753. CloseZip( m_hZip );
  1754. m_hZip = (HZIP)0;
  1755. }
  1756. uint64 un64SteamID = m_SteamID.ConvertToUint64();
  1757. m_pBugReporter->SetSteamUserID( &un64SteamID, sizeof( un64SteamID ) );
  1758. }
  1759. else
  1760. {
  1761. // Notify other players that we've submitted a bug
  1762. if ( cl.IsActive() && cl.m_nMaxClients > 1 )
  1763. {
  1764. char buf[256];
  1765. Q_snprintf( buf, sizeof(buf), "say \"Bug Submitted: \'%s\'\"", title );
  1766. Cbuf_AddText( buf );
  1767. }
  1768. if ( m_szSaveGameName[ 0 ] )
  1769. {
  1770. Q_snprintf( fn, sizeof( fn ), "%s/BugId/%s.sav", GetRepositoryURL(), m_szSaveGameName );
  1771. Q_FixSlashes( fn );
  1772. m_pBugReporter->SetSaveGame( fn );
  1773. }
  1774. if ( m_szScreenShotName[ 0 ] )
  1775. {
  1776. Q_snprintf( fn, sizeof( fn ), "%s/BugId/%s.jpg", GetRepositoryURL(), m_szScreenShotName );
  1777. Q_FixSlashes( fn );
  1778. m_pBugReporter->SetScreenShot( fn );
  1779. }
  1780. if ( m_szBSPName[ 0 ] )
  1781. {
  1782. Q_snprintf( fn, sizeof( fn ), "%s/BugId/%s.bsp", GetRepositoryURL(), m_szBSPName );
  1783. Q_FixSlashes( fn );
  1784. m_pBugReporter->SetBSPName( fn );
  1785. }
  1786. if ( m_szVMFName[ 0 ] )
  1787. {
  1788. Q_snprintf( fn, sizeof( fn ), "%s/BugId/%s.vmf", GetRepositoryURL(), m_szVMFName );
  1789. Q_FixSlashes( fn );
  1790. m_pBugReporter->SetVMFName( fn );
  1791. }
  1792. if ( m_IncludedFiles.Count() > 0 )
  1793. {
  1794. for ( int i = 0; i < m_IncludedFiles.Count(); ++i )
  1795. {
  1796. Q_snprintf( fn, sizeof( fn ), "%s/BugId/%s", GetRepositoryURL(), m_IncludedFiles[ i ].fixedname );
  1797. Q_FixSlashes( fn );
  1798. m_pBugReporter->AddIncludedFile( fn );
  1799. }
  1800. }
  1801. }
  1802. Q_strncpy( m_szLevel, level, sizeof( m_szLevel ) );
  1803. if ( m_pBugReporter->IsPublicUI() )
  1804. {
  1805. m_pProgressDialog = new CBugReportUploadProgressDialog(NULL, "ProgressDialog", "#Steam_SubmittingBug_WorkingTitle", "#Steam_SubmittingBug_WorkingText" );
  1806. m_pProgressDialog->Activate();
  1807. vgui::input()->SetAppModalSurface(m_pProgressDialog->GetVPanel());
  1808. m_flPauseTime = (float)system()->GetFrameTime() + PUBLIC_BUGREPORT_WAIT_TIME;
  1809. }
  1810. else
  1811. {
  1812. OnFinishBugReport();
  1813. }
  1814. }
  1815. void CBugUIPanel::OnFinishBugReport()
  1816. {
  1817. int bugId = -1;
  1818. bool success = m_pBugReporter->CommitBugReport( bugId );
  1819. if ( success )
  1820. {
  1821. // The public UI handles uploading on it's own...
  1822. if ( !m_pBugReporter->IsPublicUI() )
  1823. {
  1824. if ( !UploadBugSubmission( m_szLevel, bugId, m_szSaveGameName, m_szScreenShotName, m_szBSPName, m_szVMFName, m_IncludedFiles ) )
  1825. {
  1826. Warning( "Unable to upload saved game and screenshot to bug repository!\n" );
  1827. success = false;
  1828. }
  1829. }
  1830. }
  1831. else
  1832. {
  1833. Warning( "Unable to post bug report to database\n" );
  1834. }
  1835. if ( !success )
  1836. {
  1837. // Play deny sound
  1838. DenySound();
  1839. m_bWaitForFinish = false;
  1840. }
  1841. else
  1842. {
  1843. // Close
  1844. WipeData();
  1845. SuccessSound( bugId );
  1846. if ( !m_pBugReporter->IsPublicUI() )
  1847. {
  1848. Close();
  1849. }
  1850. }
  1851. }
  1852. void NonFileSystem_CreatePath (const char *path)
  1853. {
  1854. char temppath[512];
  1855. Q_strncpy( temppath, path, sizeof(temppath) );
  1856. for (char *ofs = temppath+1 ; *ofs ; ofs++)
  1857. {
  1858. if (*ofs == '/' || *ofs == '\\')
  1859. { // create the directory
  1860. char old = *ofs;
  1861. *ofs = 0;
  1862. _mkdir (temppath);
  1863. *ofs = old;
  1864. }
  1865. }
  1866. }
  1867. #ifdef LINUX
  1868. #define COPYFILE_ALL 0
  1869. #define BSIZE 65535
  1870. int copyfile( const char *local, const char *remote, void *ignored, int ignoredFlags )
  1871. {
  1872. ssize_t bytes;
  1873. int fps, fpd;
  1874. char buffer[BSIZE];
  1875. if ( (fps = open( local , O_RDONLY ) ) == -1 )
  1876. return -1;
  1877. if ( (fpd = open( remote, O_WRONLY | O_CREAT | O_TRUNC, 0644 ) ) == -1 )
  1878. return -1;
  1879. while((bytes = read(fps, buffer, BSIZE)) > 0)
  1880. write(fpd, buffer, bytes);
  1881. close(fpd);
  1882. close(fps);
  1883. return 0;
  1884. }
  1885. #endif
  1886. bool CBugUIPanel::UploadFile( char const *local, char const *remote, bool bDeleteLocal )
  1887. {
  1888. Msg( "Uploading %s to %s\n", local, remote );
  1889. FileHandle_t hLocal = g_pFileSystem->Open( local, "rb" );
  1890. if ( FILESYSTEM_INVALID_HANDLE == hLocal )
  1891. {
  1892. Warning( "CBugUIPanel::UploadFile: Unable to open local path '%s'\n", local );
  1893. return false;
  1894. }
  1895. int nLocalFileSize = g_pFileSystem->Size( hLocal );
  1896. if ( nLocalFileSize <= 0 )
  1897. {
  1898. Warning( "CBugUIPanel::UploadFile: Local file has 0 size '%s'\n", local );
  1899. g_pFileSystem->Close( hLocal );
  1900. return false;
  1901. }
  1902. NonFileSystem_CreatePath( remote );
  1903. bool bResult;
  1904. if ( !g_pFileSystem->IsSteam() )
  1905. {
  1906. g_pFileSystem->Close( hLocal );
  1907. #ifdef WIN32
  1908. bResult = CopyFile( local, remote, false ) ? true : false;
  1909. #elif POSIX
  1910. bResult = (0 == copyfile( local, remote, NULL, COPYFILE_ALL ));
  1911. #else
  1912. #error
  1913. #endif
  1914. }
  1915. else
  1916. {
  1917. FILE *r = fopen( va( "%s", remote ), "wb" );
  1918. if ( !r )
  1919. {
  1920. Warning( "CBugUIPanel::UploadFile: Unable to open remote path '%s'\n", remote );
  1921. g_pFileSystem->Close( hLocal );
  1922. return false;
  1923. }
  1924. int nCopyBufferSize = 2 * 1024 * 1024;
  1925. byte *pCopyBuf = new byte[ nCopyBufferSize ];
  1926. Assert( pCopyBuf );
  1927. if ( !pCopyBuf )
  1928. {
  1929. Warning( "CBugUIPanel::UploadFile: Unable to allocate copy buffer of %d bytes\n", nCopyBufferSize );
  1930. fclose( r );
  1931. g_pFileSystem->Close( hLocal );
  1932. return false;
  1933. }
  1934. int nRemainingBytes = nLocalFileSize;
  1935. while ( nRemainingBytes > 0 )
  1936. {
  1937. int nBytesToCopy = MIN( nRemainingBytes, nCopyBufferSize );
  1938. g_pFileSystem->Read( pCopyBuf, nBytesToCopy, hLocal );
  1939. fwrite( pCopyBuf, nBytesToCopy, 1, r );
  1940. nRemainingBytes -= nBytesToCopy;
  1941. }
  1942. fclose( r );
  1943. g_pFileSystem->Close( hLocal );
  1944. delete[] pCopyBuf;
  1945. bResult = true;
  1946. }
  1947. if ( !bResult )
  1948. {
  1949. Warning( "Failed to upload %s, error %d\n", local, GetLastError() );
  1950. }
  1951. else if ( bDeleteLocal )
  1952. {
  1953. unlink( local );
  1954. }
  1955. return bResult;
  1956. }
  1957. CCallQueue g_UploadQueue;
  1958. bool CBugUIPanel::UploadBugSubmission( char const *levelname, int bugId, char const *savefile, char const *screenshot, char const *bsp, char const *vmf,
  1959. CUtlVector< includedfile >& files )
  1960. {
  1961. bool bret = true;
  1962. bool bAsync = bugreporter_uploadasync.GetBool();
  1963. char localfile[ 512 ];
  1964. char remotefile[ 512 ];
  1965. if ( savefile && savefile[ 0 ] )
  1966. {
  1967. Q_snprintf( localfile, sizeof( localfile ), "%s/save/%s.sav", com_gamedir, savefile );
  1968. Q_snprintf( remotefile, sizeof( remotefile ), "%s/%s.sav", GetSubmissionURL(bugId), savefile );
  1969. Q_FixSlashes( localfile );
  1970. Q_FixSlashes( remotefile );
  1971. g_UploadQueue.QueueCall( this, &CBugUIPanel::UploadFile, CUtlEnvelope<const char *>(localfile), CUtlEnvelope<const char *>(remotefile), false );
  1972. }
  1973. if ( screenshot && screenshot[ 0 ] )
  1974. {
  1975. Q_snprintf( localfile, sizeof( localfile ), "%s/screenshots/%s.jpg", com_gamedir, screenshot );
  1976. Q_snprintf( remotefile, sizeof( remotefile ), "%s/%s.jpg", GetSubmissionURL(bugId), screenshot );
  1977. Q_FixSlashes( localfile );
  1978. Q_FixSlashes( remotefile );
  1979. g_UploadQueue.QueueCall( this, &CBugUIPanel::UploadFile, CUtlEnvelope<const char *>(localfile), CUtlEnvelope<const char *>(remotefile), false );
  1980. }
  1981. if ( bsp && bsp[ 0 ] )
  1982. {
  1983. Q_snprintf( localfile, sizeof( localfile ), "maps/%s.bsp", levelname );
  1984. char *pszMapPath;
  1985. FileHandle_t hBsp = g_pFileSystem->OpenEx( localfile, "rb", 0, 0, &pszMapPath );
  1986. if ( !hBsp )
  1987. {
  1988. Q_snprintf( localfile, sizeof( localfile ), "%s/maps/%s.bsp", com_gamedir, levelname );
  1989. }
  1990. else
  1991. {
  1992. V_strncpy( localfile, pszMapPath, sizeof( localfile ) );
  1993. delete pszMapPath;
  1994. g_pFileSystem->Close( hBsp );
  1995. }
  1996. Q_snprintf( remotefile, sizeof( remotefile ), "%s/%s.bsp", GetSubmissionURL(bugId), bsp );
  1997. Q_FixSlashes( localfile );
  1998. Q_FixSlashes( remotefile );
  1999. g_UploadQueue.QueueCall( this, &CBugUIPanel::UploadFile, CUtlEnvelope<const char *>(localfile), CUtlEnvelope<const char *>(remotefile), false );
  2000. }
  2001. if ( vmf && vmf[ 0 ] )
  2002. {
  2003. Q_snprintf( localfile, sizeof( localfile ), "%s/%s.vmf", m_szVMFContentDirFullpath, levelname );
  2004. if ( g_pFileSystem->FileExists( localfile, NULL ) )
  2005. {
  2006. Q_snprintf( remotefile, sizeof( remotefile ), "%s/%s.vmf", GetSubmissionURL(bugId), vmf );
  2007. Q_FixSlashes( localfile );
  2008. Q_FixSlashes( remotefile );
  2009. g_UploadQueue.QueueCall( this, &CBugUIPanel::UploadFile, CUtlEnvelope<const char *>(localfile), CUtlEnvelope<const char *>(remotefile), false );
  2010. }
  2011. else
  2012. {
  2013. Msg( "Unable to locate .vmf file %s\n", localfile );
  2014. }
  2015. }
  2016. if ( files.Count() > 0 )
  2017. {
  2018. bAsync = false;
  2019. int c = files.Count();
  2020. for ( int i = 0 ; i < c; ++i )
  2021. {
  2022. Q_snprintf( localfile, sizeof( localfile ), "%s/%s", com_gamedir, files[ i ].name );
  2023. Q_snprintf( remotefile, sizeof( remotefile ), "%s/%s", GetSubmissionURL(bugId), files[ i ].fixedname );
  2024. Q_FixSlashes( localfile );
  2025. Q_FixSlashes( remotefile );
  2026. g_UploadQueue.QueueCall( this, &CBugUIPanel::UploadFile, CUtlEnvelope<const char *>(localfile), CUtlEnvelope<const char *>(remotefile), false );
  2027. }
  2028. }
  2029. if ( !bAsync )
  2030. {
  2031. g_UploadQueue.CallQueued();
  2032. }
  2033. else
  2034. {
  2035. ThreadExecuteSolo( "BugUpload", &g_UploadQueue, &CCallQueue::CallQueued );
  2036. }
  2037. return bret;
  2038. }
  2039. void CBugUIPanel::Close()
  2040. {
  2041. WipeData();
  2042. BaseClass::Close();
  2043. }
  2044. void CBugUIPanel::OnCommand( char const *command )
  2045. {
  2046. if ( !Q_strcasecmp( command, "submit" ) )
  2047. {
  2048. OnSubmit();
  2049. }
  2050. else if ( !Q_strcasecmp( command, "cancel" ) )
  2051. {
  2052. Close();
  2053. WipeData();
  2054. }
  2055. else if ( !Q_strcasecmp( command, "snapshot" ) )
  2056. {
  2057. OnTakeSnapshot();
  2058. }
  2059. else if ( !Q_strcasecmp( command, "savegame" ) )
  2060. {
  2061. OnSaveGame();
  2062. //Adrian: We always want the BSP you used when saving the game.
  2063. //But only if you're not the public bug reporter!
  2064. if ( bugreporter_includebsp.GetBool() &&
  2065. m_pBugReporter->IsPublicUI() == false )
  2066. {
  2067. OnSaveBSP();
  2068. }
  2069. }
  2070. else if ( !Q_strcasecmp( command, "savebsp" ) )
  2071. {
  2072. OnSaveBSP();
  2073. }
  2074. else if ( !Q_strcasecmp( command, "savevmf" ) )
  2075. {
  2076. OnSaveVMF();
  2077. }
  2078. else if ( !Q_strcasecmp( command, "clearform" ) )
  2079. {
  2080. OnClearForm();
  2081. }
  2082. else if ( !Q_strcasecmp( command, "addfile" ) )
  2083. {
  2084. OnIncludeFile();
  2085. }
  2086. else if ( !Q_strcasecmp( command, "clearfiles" ) )
  2087. {
  2088. OnClearIncludedFiles();
  2089. }
  2090. else
  2091. {
  2092. BaseClass::OnCommand( command );
  2093. }
  2094. }
  2095. void CBugUIPanel::OnClearForm()
  2096. {
  2097. WipeData();
  2098. m_pTitle->SetText( "" );
  2099. m_pDescription->SetText( "" );
  2100. m_pAssignTo->ActivateItem( 0 );
  2101. m_pSeverity->ActivateItem( 0 );
  2102. m_pReportType->ActivateItem( 0 );
  2103. m_pPriority->ActivateItem( 2 );
  2104. m_pGameArea->ActivateItem( 0 );
  2105. m_pMapNumber->ActivateItem( 0 );
  2106. }
  2107. void CBugUIPanel::DetermineSubmitterName()
  2108. {
  2109. if ( !m_pBugReporter )
  2110. return;
  2111. if ( m_pBugReporter->IsPublicUI() )
  2112. {
  2113. m_pSubmitter->SetText( "PublicUser" );
  2114. m_bCanSeeRepository = true;
  2115. m_bCanSubmit = true;
  2116. }
  2117. else
  2118. {
  2119. Color clr( 100, 200, 255, 255 );
  2120. const char *pUserName = m_pBugReporter->GetUserName();
  2121. const char *pUserDisplayName = m_pBugReporter->GetUserName_Display();
  2122. if ( pUserName && pUserName[0] && pUserDisplayName && pUserDisplayName[0] )
  2123. {
  2124. ConColorMsg( clr, "Username '%s' -- '%s'\n", pUserName, pUserDisplayName );
  2125. }
  2126. else if ( cl.IsActive() )
  2127. {
  2128. m_bUseNameForSubmitter = true;
  2129. pUserDisplayName = cl_name.GetString();
  2130. ConColorMsg( clr, "Using '%s' as bug submission name.\n", pUserDisplayName );
  2131. }
  2132. else
  2133. {
  2134. ConColorMsg( clr, "Failed to determine bug submission name.\n" );
  2135. }
  2136. // See if we can see the bug repository right now
  2137. char fn[ 512 ];
  2138. Q_snprintf( fn, sizeof( fn ), "%s/%s", GetRepositoryURL(), REPOSITORY_VALIDATION_FILE );
  2139. Q_FixSlashes( fn );
  2140. FILE *fp = fopen( fn, "rb" );
  2141. if ( fp )
  2142. {
  2143. ConColorMsg( clr, "Bug Repository '%s'\n", GetRepositoryURL() );
  2144. fclose( fp );
  2145. m_bCanSeeRepository = true;
  2146. }
  2147. else
  2148. {
  2149. Warning( "Unable to see '%s', check permissions and network connectivity\n", fn );
  2150. m_bCanSubmit = false;
  2151. }
  2152. m_pSubmitter->SetText( pUserDisplayName );
  2153. }
  2154. }
  2155. void CBugUIPanel::PopulateControls()
  2156. {
  2157. if ( !m_pBugReporter )
  2158. return;
  2159. m_pAssignTo->DeleteAllItems();
  2160. int i;
  2161. int c = m_pBugReporter->GetDisplayNameCount();
  2162. int defitem = 0;
  2163. for ( i = 0; i < c; i++ )
  2164. {
  2165. char const *name = m_pBugReporter->GetDisplayName( i );
  2166. if (!V_strcasecmp(name, "Triage"))
  2167. defitem = i;
  2168. m_pAssignTo->AddItem(name , NULL );
  2169. }
  2170. m_pAssignTo->ActivateItem( defitem );
  2171. defitem = 0;
  2172. m_pSeverity->DeleteAllItems();
  2173. c = m_pBugReporter->GetSeverityCount();
  2174. for ( i = 0; i < c; i++ )
  2175. {
  2176. char const *severity = m_pBugReporter->GetSeverity( i );
  2177. if (!V_strcasecmp(severity, "Zero"))
  2178. defitem = i;
  2179. m_pSeverity->AddItem( severity, NULL );
  2180. }
  2181. m_pSeverity->ActivateItem( defitem );
  2182. m_pReportType->DeleteAllItems();
  2183. c = m_pBugReporter->GetReportTypeCount();
  2184. for ( i = 0; i < c; i++ )
  2185. {
  2186. m_pReportType->AddItem( m_pBugReporter->GetReportType( i ), NULL );
  2187. }
  2188. m_pReportType->ActivateItem( 0 );
  2189. m_pPriority->DeleteAllItems();
  2190. c = m_pBugReporter->GetPriorityCount();
  2191. for ( i = 0; i < c; i++ )
  2192. {
  2193. m_pPriority->AddItem( m_pBugReporter->GetPriority( i ), NULL );
  2194. }
  2195. m_pPriority->ActivateItem( 2 );
  2196. m_pGameArea->DeleteAllItems();
  2197. c = m_pBugReporter->GetAreaCount();
  2198. for ( i = 0; i < c; i++ )
  2199. {
  2200. m_pGameArea->AddItem( m_pBugReporter->GetArea( i ), NULL );
  2201. }
  2202. int area_index = GetArea();
  2203. m_pGameArea->ActivateItem( area_index );
  2204. }
  2205. char const *CBugUIPanel::GetSubmitter()
  2206. {
  2207. if ( !m_bCanSubmit )
  2208. return "";
  2209. return m_pBugReporter->GetUserName();
  2210. }
  2211. //-----------------------------------------------------------------------------
  2212. // Purpose:
  2213. //-----------------------------------------------------------------------------
  2214. void CBugUIPanel::DenySound()
  2215. {
  2216. Cbuf_AddText( va( "play %s\n", DENY_SOUND ) );
  2217. }
  2218. //-----------------------------------------------------------------------------
  2219. // Purpose:
  2220. //-----------------------------------------------------------------------------
  2221. void CBugUIPanel::SuccessSound( int bugId )
  2222. {
  2223. Color clr( 50, 255, 100, 255 );
  2224. if ( m_pBugReporter && m_pBugReporter->IsPublicUI() )
  2225. {
  2226. ConColorMsg( clr, "Bug submission succeeded\n" );
  2227. }
  2228. else
  2229. {
  2230. ConColorMsg( clr, "Bug submission succeeded for bug (%i)\n", bugId );
  2231. }
  2232. Cbuf_AddText( va( "play %s\n", SUCCEED_SOUND ) );
  2233. }
  2234. void CBugUIPanel::OnKeyCodePressed(KeyCode code)
  2235. {
  2236. if ( code == KEY_ESCAPE ||
  2237. GetBaseButtonCode( code ) == KEY_XBUTTON_B )
  2238. {
  2239. Close();
  2240. WipeData();
  2241. }
  2242. else
  2243. {
  2244. BaseClass::OnKeyCodePressed( code );
  2245. }
  2246. }
  2247. // Load game-specific bug reporter defaults as params
  2248. void CBugUIPanel::ParseDefaultParams( void )
  2249. {
  2250. const char *szDefaults = "scripts/bugreporter_defaults.txt";
  2251. FileHandle_t hLocal = g_pFileSystem->Open( szDefaults, "rb" );
  2252. if ( FILESYSTEM_INVALID_HANDLE == hLocal )
  2253. {
  2254. return;
  2255. }
  2256. // load file into a null-terminated buffer
  2257. int fileSize = g_pFileSystem->Size(hLocal);
  2258. char *buffer = (char*)MemAllocScratch(fileSize + 1);
  2259. Assert(buffer);
  2260. g_pFileSystem->Read(buffer, fileSize, hLocal); // read into local buffer
  2261. buffer[fileSize] = 0; // null terminate file as EOF
  2262. g_pFileSystem->Close( hLocal ); // close file after reading
  2263. char token[64];
  2264. const char *pfile = COM_ParseFile(buffer, token, sizeof( token ) );
  2265. while ( pfile )
  2266. {
  2267. bool success = AutoFillToken( token, false );
  2268. if ( !success )
  2269. {
  2270. // Try partials
  2271. success = AutoFillToken( token, true );
  2272. if ( !success )
  2273. {
  2274. Msg( "Unable to determine where to set default bug parameter '%s', ignoring...\n", token );
  2275. }
  2276. }
  2277. pfile = COM_ParseFile(pfile, token, sizeof( token ) );
  2278. }
  2279. }
  2280. void CBugUIPanel::ParseCommands( const CCommand &args )
  2281. {
  2282. if ( !m_bCanSubmit )
  2283. return;
  2284. for ( int i = 1; i < args.ArgC(); i++ )
  2285. {
  2286. bool success = AutoFillToken( args[ i ], false );
  2287. if ( !success )
  2288. {
  2289. // Try partials
  2290. success = AutoFillToken( args[ i ], true );
  2291. if ( !success )
  2292. {
  2293. Msg( "Unable to determine where to set bug parameter '%s', ignoring...\n", args[ i ] );
  2294. }
  2295. }
  2296. }
  2297. }
  2298. bool CBugUIPanel::Compare( char const *value, char const *token, bool partial )
  2299. {
  2300. if ( !partial )
  2301. {
  2302. if ( !Q_stricmp( value, token ) )
  2303. return true;
  2304. }
  2305. else
  2306. {
  2307. if ( Q_stristr( value, token ) )
  2308. {
  2309. return true;
  2310. }
  2311. }
  2312. return false;
  2313. }
  2314. bool CBugUIPanel::AutoFillToken( char const *token, bool partial )
  2315. {
  2316. // Search for token in all dropdown lists and fill it in if we find it
  2317. if ( !m_pBugReporter )
  2318. return true;
  2319. int i;
  2320. int c;
  2321. c = m_pBugReporter->GetDisplayNameCount();
  2322. for ( i = 0; i < c; i++ )
  2323. {
  2324. if ( Compare( m_pBugReporter->GetDisplayName( i ), token, partial ) )
  2325. {
  2326. m_pAssignTo->ActivateItem( i );
  2327. return true;
  2328. }
  2329. }
  2330. c = m_pBugReporter->GetSeverityCount();
  2331. for ( i = 0; i < c; i++ )
  2332. {
  2333. if ( Compare( m_pBugReporter->GetSeverity( i ), token, partial ) )
  2334. {
  2335. m_pSeverity->ActivateItem( i );
  2336. return true;
  2337. }
  2338. }
  2339. c = m_pBugReporter->GetReportTypeCount();
  2340. for ( i = 0; i < c; i++ )
  2341. {
  2342. if ( Compare( m_pBugReporter->GetReportType( i ), token, partial ) )
  2343. {
  2344. m_pReportType->ActivateItem( i );
  2345. return true;
  2346. }
  2347. }
  2348. c = m_pBugReporter->GetPriorityCount();
  2349. for ( i = 0; i < c; i++ )
  2350. {
  2351. if ( Compare( m_pBugReporter->GetPriority( i ), token, partial ) )
  2352. {
  2353. m_pPriority->ActivateItem( i );
  2354. return true;
  2355. }
  2356. }
  2357. c = m_pBugReporter->GetAreaCount();
  2358. for ( i = 0; i < c; i++ )
  2359. {
  2360. if ( Compare( m_pBugReporter->GetArea( i ), token, partial ) )
  2361. {
  2362. m_pGameArea->ActivateItem( i );
  2363. return true;
  2364. }
  2365. }
  2366. if ( !Q_stricmp( token, "screenshot" ) )
  2367. {
  2368. m_fAutoAddScreenshot = eAutoAddScreenshot_Add;
  2369. return true;
  2370. }
  2371. if ( !Q_stricmp( token, "noscreenshot" ) )
  2372. {
  2373. m_fAutoAddScreenshot = eAutoAddScreenshot_DontAdd;
  2374. return true;
  2375. }
  2376. return false;
  2377. }
  2378. void CBugUIPanel::CheckContinueQueryingSteamForCSERList()
  2379. {
  2380. if ( !m_bQueryingSteamForCSER || !Steam3Client().SteamUtils() )
  2381. {
  2382. return;
  2383. }
  2384. uint32 unIP;
  2385. uint16 usPort;
  2386. Steam3Client().SteamUtils()->GetCSERIPPort( &unIP, &usPort );
  2387. if ( unIP )
  2388. {
  2389. m_cserIP.SetIPAndPort( unIP, usPort );
  2390. m_bQueryingSteamForCSER = false;
  2391. }
  2392. }
  2393. static CBugUIPanel *g_pBugUI = NULL;
  2394. class CEngineBugReporter : public IEngineBugReporter
  2395. {
  2396. public:
  2397. virtual void Init( void );
  2398. virtual void Shutdown( void );
  2399. virtual void InstallBugReportingUI( vgui::Panel *parent, IEngineBugReporter::BR_TYPE type );
  2400. virtual bool ShouldPause() const;
  2401. virtual bool IsVisible() const; //< true iff the bug panel is active and on screen right now
  2402. void Restart( IEngineBugReporter::BR_TYPE type );
  2403. private:
  2404. PHandle m_ParentPanel;
  2405. };
  2406. static CEngineBugReporter g_BugReporter;
  2407. IEngineBugReporter *bugreporter = &g_BugReporter;
  2408. CON_COMMAND( _bugreporter_restart, "Restarts bug reporter .dll" )
  2409. {
  2410. if ( args.ArgC() <= 1 )
  2411. {
  2412. Msg( "__bugreporter_restart <internal | external | autoselect>\n" );
  2413. return;
  2414. }
  2415. IEngineBugReporter::BR_TYPE type = IEngineBugReporter::BR_PUBLIC;
  2416. if ( !Q_stricmp( args.Arg( 1 ), "internal" ) )
  2417. {
  2418. type = IEngineBugReporter::BR_INTERNAL;
  2419. }
  2420. else if ( !Q_stricmp( args.Arg( 1 ), "autoselect" ) )
  2421. {
  2422. type = IEngineBugReporter::BR_AUTOSELECT;
  2423. }
  2424. g_BugReporter.Restart( type );
  2425. }
  2426. void CEngineBugReporter::Init( void )
  2427. {
  2428. m_ParentPanel = 0;
  2429. }
  2430. void CEngineBugReporter::Shutdown( void )
  2431. {
  2432. if ( g_pBugUI )
  2433. {
  2434. g_pBugUI->Shutdown();
  2435. g_pBugUI = NULL;
  2436. }
  2437. }
  2438. void CEngineBugReporter::InstallBugReportingUI( vgui::Panel *parent, IEngineBugReporter::BR_TYPE type )
  2439. {
  2440. if ( g_pBugUI )
  2441. return;
  2442. bool bIsPublic = true;
  2443. char fn[ 512 ];
  2444. Q_snprintf( fn, sizeof( fn ), "%s%s", GetInternalBugReporterDLL(), DLL_EXT_STRING );
  2445. bool bCanUseInternal = g_pFileSystem->FileExists( fn, "EXECUTABLE_PATH" );
  2446. switch ( type )
  2447. {
  2448. case IEngineBugReporter::BR_PUBLIC:
  2449. // Nothing
  2450. break;
  2451. default:
  2452. case IEngineBugReporter::BR_AUTOSELECT:
  2453. {
  2454. // check
  2455. bIsPublic = phonehome->IsExternalBuild() ? true : false;
  2456. if ( bCanUseInternal )
  2457. {
  2458. // if command line param specifies internal, use that
  2459. if ( CommandLine()->FindParm( "-internalbuild" ) )
  2460. {
  2461. bIsPublic = false;
  2462. }
  2463. #if !defined( _X360 )
  2464. // otherwise, if Steam is running and connected to beta, autoselect the internal bug db
  2465. else if ( k_EUniverseBeta == GetSteamUniverse() )
  2466. {
  2467. bIsPublic = false;
  2468. }
  2469. #endif
  2470. }
  2471. }
  2472. break;
  2473. case IEngineBugReporter::BR_INTERNAL:
  2474. {
  2475. if ( bCanUseInternal )
  2476. {
  2477. bIsPublic = false;
  2478. }
  2479. }
  2480. break;
  2481. }
  2482. g_pBugUI = new CBugUIPanel( bIsPublic, parent );
  2483. Assert( g_pBugUI );
  2484. m_ParentPanel = parent;
  2485. }
  2486. void CEngineBugReporter::Restart( IEngineBugReporter::BR_TYPE type )
  2487. {
  2488. Shutdown();
  2489. Msg( "Changing to bugreporter(%s)\n",
  2490. ( type == IEngineBugReporter::BR_AUTOSELECT ) ?
  2491. ( "autoselect" ) :
  2492. ( ( type == IEngineBugReporter::BR_PUBLIC ) ?
  2493. "public" :
  2494. "valve" ) );
  2495. InstallBugReportingUI( m_ParentPanel, type );
  2496. }
  2497. bool CEngineBugReporter::ShouldPause() const
  2498. {
  2499. return g_pBugUI && ( g_pBugUI->IsVisible() || g_pBugUI->IsTakingSnapshot() ) && (cl.m_nMaxClients == 1);
  2500. }
  2501. bool CEngineBugReporter::IsVisible() const
  2502. {
  2503. return g_pBugUI && g_pBugUI->IsVisible();
  2504. }
  2505. CON_COMMAND_F( bug, "Show/hide the bug reporting UI.", FCVAR_DONTRECORD )
  2506. {
  2507. if ( !g_pBugUI )
  2508. return;
  2509. bool bWasVisible = g_pBugUI->IsVisible();
  2510. if ( bWasVisible )
  2511. {
  2512. // hide
  2513. g_pBugUI->Close();
  2514. }
  2515. // make sure the gameUI is open so the bugreporter is visible
  2516. EngineVGui()->ActivateGameUI();
  2517. g_pBugUI->Activate();
  2518. g_pBugUI->ParseDefaultParams();
  2519. if (args.ArgC() > 1 )
  2520. {
  2521. g_pBugUI->ParseCommands( args );
  2522. }
  2523. }
  2524. int CBugUIPanel::GetArea()
  2525. {
  2526. char mapname[256] = "";
  2527. int iNewTitleLength = 80;
  2528. if ( host_state.worldmodel )
  2529. {
  2530. CL_SetupMapName( modelloader->GetName( host_state.worldmodel ), mapname, sizeof( mapname ) );
  2531. iNewTitleLength = (80 - (strlen( mapname )+2));
  2532. }
  2533. m_pTitle->SetMaximumCharCount( iNewTitleLength );
  2534. char *gamedir = com_gamedir;
  2535. gamedir = Q_strrchr( gamedir, CORRECT_PATH_SEPARATOR ) + 1;
  2536. for ( int i = 0; i < m_pBugReporter->GetAreaMapCount(); i++ )
  2537. {
  2538. char szAreaMap[MAX_PATH];
  2539. V_strcpy_safe( szAreaMap, m_pBugReporter->GetAreaMap( i ) );
  2540. char *pszAreaDir = Q_strrchr( szAreaMap, '@' );
  2541. char *pszAreaPrefix = Q_strrchr( szAreaMap, '%' );
  2542. int iDirLength = 0;
  2543. if ( pszAreaDir && pszAreaPrefix )
  2544. {
  2545. iDirLength = pszAreaPrefix - pszAreaDir - 1;
  2546. pszAreaDir++;
  2547. pszAreaPrefix++;
  2548. }
  2549. else if ( pszAreaDir && !pszAreaPrefix )
  2550. {
  2551. pszAreaDir++;
  2552. iDirLength = Q_strlen( szAreaMap ) - (pszAreaDir - szAreaMap);
  2553. }
  2554. else
  2555. {
  2556. return 0;
  2557. }
  2558. char szDirectory[MAX_PATH];
  2559. Q_memmove( szDirectory, pszAreaDir, iDirLength );
  2560. szDirectory[iDirLength] = 0;
  2561. if ( pszAreaDir && pszAreaPrefix )
  2562. {
  2563. if ( !Q_strcmp( szDirectory, gamedir)
  2564. && Q_strstr( mapname, pszAreaPrefix ) )
  2565. {
  2566. return i+1;
  2567. }
  2568. }
  2569. else if ( pszAreaDir && !pszAreaPrefix )
  2570. {
  2571. if ( !Q_strcmp( szDirectory, gamedir ) )
  2572. {
  2573. return i+1;
  2574. }
  2575. }
  2576. }
  2577. return 0;
  2578. }