Counter Strike : Global Offensive Source Code
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.

3604 lines
91 KiB

  1. //========= Copyright 1996-2005, 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 )
  11. #if !defined( _X360 )
  12. #include "winlite.h"
  13. #include <winsock2.h> // inaddr_any defn
  14. #include <shlobj.h> // isuseranadmin
  15. #endif
  16. #include <direct.h>
  17. #elif defined(_PS3)
  18. #include <sys/stat.h>
  19. #include <unistd.h>
  20. #define PS3_FS_NORMAL_PERMISSIONS S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH
  21. #define GetLastError() errno
  22. #elif defined(POSIX)
  23. #include <sys/stat.h>
  24. #if !defined( LINUX )
  25. #include <copyfile.h>
  26. #endif
  27. #define GetLastError() errno
  28. #else
  29. #error
  30. #endif
  31. #include <time.h>
  32. #include "client.h"
  33. #include <vgui_controls/Frame.h>
  34. #include <vgui/ISystem.h>
  35. #include <vgui/ISurface.h>
  36. #include <vgui/IInput.h>
  37. #include <vgui/IVGui.h>
  38. #include <keyvalues.h>
  39. #include <vgui_controls/BuildGroup.h>
  40. #include <vgui_controls/Tooltip.h>
  41. #include <vgui_controls/TextImage.h>
  42. #include <vgui_controls/CheckButton.h>
  43. #include <vgui_controls/Label.h>
  44. #include <vgui_controls/PropertySheet.h>
  45. #include <vgui_controls/FileOpenDialog.h>
  46. #include "vgui_controls/DirectorySelectDialog.h"
  47. #include <vgui_controls/ProgressBar.h>
  48. #include <vgui_controls/Slider.h>
  49. #include <vgui_controls/ComboBox.h>
  50. #include <vgui_controls/Controls.h>
  51. #include <vgui_controls/TextEntry.h>
  52. #include "enginebugreporter.h"
  53. #include "vgui_baseui_interface.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 "tier1/fmtstr.h"
  61. #include "vstdlib/jobthread.h"
  62. #include "utlsymbol.h"
  63. #include "utldict.h"
  64. #include "filesystem.h"
  65. #include "filesystem_engine.h"
  66. #include "icliententitylist.h"
  67. #include "bugreporter/bugreporter.h"
  68. #include "icliententity.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 "materialsystem/materialsystem_config.h"
  80. #include "Steam.h"
  81. #include "FindSteamServers.h"
  82. #include "vstdlib/random.h"
  83. #include "blackbox.h"
  84. #ifndef DEDICATED
  85. #include "cl_steamauth.h"
  86. #endif
  87. #include "zip/XZip.h"
  88. #include "vengineserver_impl.h"
  89. #include "vprof.h"
  90. #include "matchmaking/imatchframework.h"
  91. #include "sv_remoteaccess.h" // used for remote bug reporting
  92. #if defined( _X360 )
  93. #include "xbox/xbox_win32stubs.h"
  94. #include "xbox/xbox_console.h"
  95. #elif defined( _PS3 )
  96. #include "ps3/ps3_console.h"
  97. #endif
  98. // memdbgon must be the last include file in a .cpp file!!!
  99. #include "tier0/memdbgon.h"
  100. extern IBaseClientDLL *g_ClientDLL;
  101. #define DENY_SOUND "common/bugreporter_failed"
  102. #define SUCCEED_SOUND "common/bugreporter_succeeded"
  103. // Fixme, move these to buguiddata.res script file?
  104. #define BUG_REPOSITORY_URL "\\\\fileserver\\bugs"
  105. #define REPOSITORY_VALIDATION_FILE "info.txt"
  106. #define BUG_REPORTER_DLLNAME "bugreporter_filequeue"
  107. #define BUG_REPORTER_PUBLIC_DLLNAME "bugreporter_public"
  108. #if defined( _DEBUG )
  109. #define PUBLIC_BUGREPORT_WAIT_TIME 3
  110. #else
  111. #define PUBLIC_BUGREPORT_WAIT_TIME 15
  112. #endif
  113. // 16Mb max zipped size
  114. #define MAX_ZIP_SIZE (1024 * 1024 * 16 )
  115. extern float g_fFramesPerSecond;
  116. // Need a non trivial amount of frames to let pause blur effect "unblur" due to screensnapshot temp hiding the gameui.
  117. #define SNAPSHOT_DELAY_DEFAULT "15"
  118. static ConVar bugreporter_includebsp( "bugreporter_includebsp", "1", 0, "Include .bsp for internal bug submissions." );
  119. static ConVar bugreporter_uploadasync( "bugreporter_uploadasync", "0", FCVAR_ARCHIVE, "Upload attachments asynchronously" );
  120. static ConVar bugreporter_snapshot_delay("bugreporter_snapshot_delay", SNAPSHOT_DELAY_DEFAULT, 0, "Frames to delay before taking snapshot" );
  121. static ConVar bugreporter_username( "bugreporter_username", "", FCVAR_ARCHIVE, "Username to use for bugreporter" );
  122. static ConVar bugreporter_console_bytes( "bugreporter_console_bytes", "15000", 0, "Max # of console bytes to put into bug report body (full text still attached)." );
  123. using namespace vgui;
  124. unsigned long GetRam()
  125. {
  126. #ifdef WIN32
  127. MEMORYSTATUSEX statex;
  128. statex.dwLength = sizeof( MEMORYSTATUSEX );
  129. GlobalMemoryStatusEx( &statex );
  130. return ( unsigned long )( statex.ullTotalPhys / ( 1024 * 1024 ) );
  131. #elif defined( LINUX )
  132. unsigned long Ram = 0;
  133. FILE *fh = fopen( "/proc/meminfo", "r" );
  134. if( fh )
  135. {
  136. char buf[ 256 ];
  137. const char szMemTotal[] = "MemTotal:";
  138. while( fgets( buf, sizeof( buf ), fh ) )
  139. {
  140. if ( !Q_strnicmp( buf, szMemTotal, sizeof( szMemTotal ) - 1 ) )
  141. {
  142. // Should already be in kB
  143. Ram = atoi( buf + sizeof( szMemTotal ) - 1 ) / 1024;
  144. break;
  145. }
  146. }
  147. fclose( fh );
  148. }
  149. return Ram;
  150. #else
  151. Assert( !"Impl me " );
  152. return 0;
  153. #endif
  154. }
  155. const char *GetInternalBugReporterDLL( void )
  156. {
  157. // If remote bugging is set on the commandline, always load the remote bug dll
  158. if ( CommandLine()->CheckParm("-remotebug" ) )
  159. return "bugreporter_remote";
  160. char const *pBugReportedDLL = NULL;
  161. if ( CommandLine()->CheckParm("-bugreporterdll", &pBugReportedDLL ) )
  162. return pBugReportedDLL;
  163. return BUG_REPORTER_DLLNAME;
  164. }
  165. bool Plat_IsUserAnAdmin()
  166. {
  167. #if defined( _WIN32 ) && !defined( _X360 )
  168. return ::IsUserAnAdmin() ? true : false;
  169. #else
  170. return true;
  171. #endif
  172. }
  173. void DisplaySystemVersion( char *osversion, int maxlen )
  174. {
  175. #ifdef WIN32
  176. osversion[ 0 ] = 0;
  177. OSVERSIONINFOEX osvi;
  178. BOOL bOsVersionInfoEx;
  179. // Try calling GetVersionEx using the OSVERSIONINFOEX structure.
  180. //
  181. // If that fails, try using the OSVERSIONINFO structure.
  182. ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
  183. osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
  184. bOsVersionInfoEx = GetVersionEx ((OSVERSIONINFO *) &osvi);
  185. if( !bOsVersionInfoEx )
  186. {
  187. // If OSVERSIONINFOEX doesn't work, try OSVERSIONINFO.
  188. osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
  189. if (! GetVersionEx ( (OSVERSIONINFO *) &osvi) )
  190. {
  191. Q_strncpy( osversion, "Unable to get Version", maxlen );
  192. return;
  193. }
  194. }
  195. switch (osvi.dwPlatformId)
  196. {
  197. case VER_PLATFORM_WIN32_NT:
  198. // Test for the product.
  199. if ( osvi.dwMajorVersion <= 4 )
  200. {
  201. Q_strncat ( osversion, "Windows NT ", maxlen, COPY_ALL_CHARACTERS );
  202. }
  203. if ( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0 )
  204. {
  205. Q_strncat ( osversion, "Windows 2000 ", maxlen, COPY_ALL_CHARACTERS );
  206. }
  207. if ( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1 )
  208. {
  209. Q_strncat ( osversion, "Windows XP ", maxlen, COPY_ALL_CHARACTERS );
  210. }
  211. if ( osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 0 )
  212. {
  213. Q_strncat( osversion, "Windows Vista ", maxlen, COPY_ALL_CHARACTERS );
  214. }
  215. if ( osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 1 )
  216. {
  217. Q_strncat( osversion, "Windows 7 ", maxlen, COPY_ALL_CHARACTERS );
  218. }
  219. if ( osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 2 )
  220. {
  221. Q_strncat( osversion, "Windows 8 ", maxlen, COPY_ALL_CHARACTERS );
  222. }
  223. if ( osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 3 )
  224. {
  225. Q_strncat( osversion, "Windows 8.1 ", maxlen, COPY_ALL_CHARACTERS );
  226. }
  227. // Display version, service pack (if any), and build number.
  228. char build[256];
  229. Q_snprintf (build, sizeof( build ), "%s (Build %d) version %d.%d (LimitedUser: %s)",
  230. osvi.szCSDVersion,
  231. osvi.dwBuildNumber & 0xFFFF,
  232. osvi.dwMajorVersion,
  233. osvi.dwMinorVersion,
  234. Plat_IsUserAnAdmin() ? "no" : "yes" );
  235. Q_strncat ( osversion, build, maxlen, COPY_ALL_CHARACTERS );
  236. break;
  237. case VER_PLATFORM_WIN32_WINDOWS:
  238. if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 0)
  239. {
  240. Q_strncat ( osversion, "95 ", maxlen, COPY_ALL_CHARACTERS );
  241. if ( osvi.szCSDVersion[1] == 'C' || osvi.szCSDVersion[1] == 'B' )
  242. {
  243. Q_strncat ( osversion, "OSR2 ", maxlen, COPY_ALL_CHARACTERS );
  244. }
  245. }
  246. if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 10)
  247. {
  248. Q_strncat ( osversion, "98 ", maxlen, COPY_ALL_CHARACTERS );
  249. if ( osvi.szCSDVersion[1] == 'A' )
  250. {
  251. Q_strncat ( osversion, "SE ", maxlen, COPY_ALL_CHARACTERS );
  252. }
  253. }
  254. if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 90)
  255. {
  256. Q_strncat ( osversion, "Me ", maxlen, COPY_ALL_CHARACTERS );
  257. }
  258. break;
  259. case VER_PLATFORM_WIN32s:
  260. Q_strncat ( osversion, "Win32s ", maxlen, COPY_ALL_CHARACTERS );
  261. break;
  262. }
  263. #elif defined(OSX)
  264. FILE *fpVersionInfo = popen( "/usr/bin/sw_vers", "r" );
  265. const char *pszSearchString = "ProductVersion:\t";
  266. const int cchSearchString = Q_strlen( pszSearchString );
  267. char rgchVersionLine[1024];
  268. if ( !fpVersionInfo )
  269. Q_strncpy ( osversion, "OSXU ", maxlen );
  270. else
  271. {
  272. Q_strncpy ( osversion, "OSX10", maxlen );
  273. while ( fgets( rgchVersionLine, sizeof(rgchVersionLine), fpVersionInfo ) )
  274. {
  275. if ( !Q_strnicmp( rgchVersionLine, pszSearchString, cchSearchString ) )
  276. {
  277. const char *pchVersion = rgchVersionLine + cchSearchString;
  278. int ccVersion = Q_strlen(pchVersion); // trim the \n
  279. Q_strncpy ( osversion, pchVersion, ccVersion );
  280. osversion[ ccVersion ] = 0;
  281. break;
  282. }
  283. }
  284. pclose( fpVersionInfo );
  285. }
  286. #elif defined(LINUX)
  287. FILE *fpKernelVer = fopen( "/proc/version_signature", "r" );
  288. if ( !fpKernelVer )
  289. {
  290. Q_strncat ( osversion, "Linux ", maxlen, COPY_ALL_CHARACTERS );
  291. }
  292. else
  293. {
  294. fgets( osversion, maxlen, fpKernelVer );
  295. osversion[ maxlen - 1 ] = 0;
  296. char *szlf = Q_strrchr( osversion, '\n' );
  297. if( szlf )
  298. *szlf = '\0';
  299. fclose( fpKernelVer );
  300. }
  301. #endif
  302. }
  303. static int GetNumberForMap()
  304. {
  305. if ( !host_state.worldmodel )
  306. return 1;
  307. char mapname[256];
  308. CL_SetupMapName( modelloader->GetName( host_state.worldmodel ), mapname, sizeof( mapname ) );
  309. KeyValues *resfilekeys = new KeyValues( "mapnumber" );
  310. if ( resfilekeys->LoadFromFile( g_pFileSystem, "scripts/bugreport_mapnumber.txt", "GAME" ) )
  311. {
  312. KeyValues *entry = resfilekeys->GetFirstSubKey();
  313. while ( entry )
  314. {
  315. if ( !Q_stricmp( entry->GetName(), mapname ) )
  316. {
  317. return entry->GetInt() + 1;
  318. }
  319. entry = entry->GetNextKey();
  320. }
  321. }
  322. resfilekeys->deleteThis();
  323. char szNameCopy[ 128 ];
  324. const char *pszResult = Q_strrchr( mapname, '_' );
  325. if( !pszResult )
  326. //I don't know where the number of this map is, if there even is one.
  327. return 1;
  328. Q_strncpy( szNameCopy, pszResult + 1, sizeof( szNameCopy ) );
  329. if ( !szNameCopy || !*szNameCopy )
  330. return 1;
  331. // in case we can't use tchar.h, this will do the same thing
  332. char *pcEndOfName = szNameCopy;
  333. while(*pcEndOfName != 0)
  334. {
  335. if(*pcEndOfName < '0' || *pcEndOfName > '9')
  336. *pcEndOfName = 0;
  337. pcEndOfName++;
  338. }
  339. //add 1 because pvcs has 0 as the first map number, not 1 (and it is not 0-based).
  340. return atoi(szNameCopy) + 1;
  341. }
  342. //-----------------------------------------------------------------------------
  343. // Purpose: Generic dialog for displaying animating steam progress logo
  344. // used when we are doing a possibly length steam op that has no progress measure (login/create user/etc)
  345. //-----------------------------------------------------------------------------
  346. class CBugReportUploadProgressDialog : public vgui::Frame
  347. {
  348. public:
  349. CBugReportUploadProgressDialog(vgui::Panel *parent, const char *name, const char *title, const char *message);
  350. ~CBugReportUploadProgressDialog();
  351. virtual void PerformLayout();
  352. void SetProgress( float progress );
  353. private:
  354. typedef vgui::Frame BaseClass;
  355. vgui::ProgressBar *m_pProgress;
  356. };
  357. //-----------------------------------------------------------------------------
  358. // Purpose: Constructor
  359. //-----------------------------------------------------------------------------
  360. CBugReportUploadProgressDialog::CBugReportUploadProgressDialog(Panel *parent, const char *name, const char *title, const char *message) : Frame(parent, name)
  361. {
  362. SetSize(300, 160);
  363. SetSizeable(false);
  364. MoveToFront();
  365. SetTitle(title, true);
  366. m_pProgress = new vgui::ProgressBar( this, "ProgressBar" );
  367. LoadControlSettings("Resource\\BugReporterUploadProgress.res");
  368. SetControlString("InfoLabel", message);
  369. MoveToCenterOfScreen();
  370. }
  371. //-----------------------------------------------------------------------------
  372. // Purpose: Destructor
  373. //-----------------------------------------------------------------------------
  374. CBugReportUploadProgressDialog::~CBugReportUploadProgressDialog()
  375. {
  376. }
  377. //-----------------------------------------------------------------------------
  378. // Purpose:
  379. // Input : percent -
  380. //-----------------------------------------------------------------------------
  381. void CBugReportUploadProgressDialog::SetProgress( float progress )
  382. {
  383. Assert( m_pProgress );
  384. m_pProgress->SetProgress( progress );
  385. }
  386. //-----------------------------------------------------------------------------
  387. // Purpose:
  388. //-----------------------------------------------------------------------------
  389. void CBugReportUploadProgressDialog::PerformLayout()
  390. {
  391. SetMinimizeButtonVisible(false);
  392. SetCloseButtonVisible(false);
  393. BaseClass::PerformLayout();
  394. }
  395. //-----------------------------------------------------------------------------
  396. // Purpose: Generic dialog for displaying animating steam progress logo
  397. // used when we are doing a possibly length steam op that has no progress measure (login/create user/etc)
  398. //-----------------------------------------------------------------------------
  399. class CBugReportFinishedDialog : public vgui::Frame
  400. {
  401. public:
  402. CBugReportFinishedDialog(vgui::Panel *parent, const char *name, const char *title, const char *message);
  403. virtual void PerformLayout();
  404. virtual void OnCommand( const char *command );
  405. private:
  406. typedef vgui::Frame BaseClass;
  407. vgui::Button *m_pOk;
  408. };
  409. //-----------------------------------------------------------------------------
  410. // Purpose: Constructor
  411. //-----------------------------------------------------------------------------
  412. CBugReportFinishedDialog::CBugReportFinishedDialog(Panel *parent, const char *name, const char *title, const char *message) : Frame(parent, name)
  413. {
  414. SetSize(300, 160);
  415. SetSizeable(false);
  416. MoveToFront();
  417. SetTitle(title, true);
  418. m_pOk = new vgui::Button( this, "CloseBtn", "#OK", this, "Close" );
  419. LoadControlSettings("Resource\\BugReporterUploadFinished.res");
  420. SetControlString("InfoLabel", message);
  421. MoveToCenterOfScreen();
  422. }
  423. void CBugReportFinishedDialog::OnCommand( const char *command )
  424. {
  425. if ( !Q_stricmp( command, "Close" ) )
  426. {
  427. MarkForDeletion();
  428. OnClose();
  429. }
  430. else
  431. {
  432. BaseClass::OnCommand( command );
  433. }
  434. }
  435. //-----------------------------------------------------------------------------
  436. // Purpose:
  437. //-----------------------------------------------------------------------------
  438. void CBugReportFinishedDialog::PerformLayout()
  439. {
  440. SetMinimizeButtonVisible(false);
  441. SetCloseButtonVisible(true);
  442. BaseClass::PerformLayout();
  443. }
  444. //-----------------------------------------------------------------------------
  445. // Purpose:
  446. //-----------------------------------------------------------------------------
  447. class CBugUIPanel : public vgui::Frame
  448. {
  449. DECLARE_CLASS_SIMPLE( CBugUIPanel, vgui::Frame );
  450. public:
  451. CBugUIPanel( bool bIsPublic, vgui::Panel *parent );
  452. ~CBugUIPanel();
  453. virtual void OnTick();
  454. // Command issued
  455. virtual void OnCommand(const char *command);
  456. virtual void Close();
  457. virtual void Activate();
  458. virtual void SetVisible( bool state )
  459. {
  460. bool changed = state != IsVisible();
  461. BaseClass::SetVisible( state );
  462. if ( changed && state )
  463. {
  464. m_pTitle->RequestFocus();
  465. }
  466. }
  467. void Init();
  468. void Shutdown();
  469. virtual void OnKeyCodeTyped(KeyCode code);
  470. void ParseDefaultParams( void );
  471. void ParseCommands( const CCommand &args );
  472. inline bool IsTakingSnapshot() const
  473. {
  474. return m_bTakingSnapshot;
  475. }
  476. // Methods to get bug count for internal dev work stat tracking.
  477. // Will get the bug count and clear it every map transition
  478. virtual int GetBugSubmissionCount() const;
  479. virtual void ClearBugSubmissionCount();
  480. // When using the remote bugreporter dll, call this to set up any special options
  481. void InitAsRemoteBug();
  482. protected:
  483. vgui::TextEntry *m_pTitle;
  484. vgui::TextEntry *m_pDescription;
  485. vgui::Button *m_pTakeShot;
  486. vgui::Button *m_pSaveGame;
  487. vgui::Button *m_pSaveBSP;
  488. vgui::Button *m_pSaveVMF;
  489. vgui::Button *m_pChooseVMFFolder;
  490. vgui::Button *m_pIncludeFile;
  491. vgui::Button *m_pClearIncludes;
  492. vgui::Label *m_pScreenShotURL;
  493. vgui::Label *m_pSaveGameURL;
  494. vgui::Label *m_pBSPURL;
  495. vgui::Label *m_pVMFURL;
  496. vgui::Label *m_pPosition;
  497. vgui::Label *m_pOrientation;
  498. vgui::Label *m_pLevelName;
  499. vgui::Label *m_pBuildNumber;
  500. vgui::ComboBox *m_pSubmitter;
  501. vgui::ComboBox *m_pAssignTo;
  502. vgui::ComboBox *m_pSeverity;
  503. vgui::ComboBox *m_pReportType;
  504. vgui::ComboBox *m_pPriority;
  505. vgui::ComboBox *m_pGameArea;
  506. vgui::ComboBox *m_pMapNumber;
  507. vgui::Button *m_pSubmit;
  508. vgui::Button *m_pCancel;
  509. vgui::Button *m_pClearForm;
  510. vgui::TextEntry *m_pIncludedFiles;
  511. vgui::TextEntry *m_pEmail;
  512. vgui::Label *m_pSubmitterLabel;
  513. IBugReporter *m_pBugReporter;
  514. CSysModule *m_hBugReporter;
  515. MESSAGE_FUNC_CHARPTR( OnFileSelected, "FileSelected", fullpath );
  516. MESSAGE_FUNC_CHARPTR( OnDirectorySelected, "DirectorySelected", dir );
  517. MESSAGE_FUNC( OnChooseVMFFolder, "OnChooseVMFFolder" );
  518. MESSAGE_FUNC_PTR( OnChooseArea, "TextChanged", panel);
  519. void DetermineSubmitterName();
  520. void PopulateControls();
  521. void TakeSnapshot();
  522. void RepopulateMaps(int area_index, const char *default_level);
  523. void OnTakeSnapshot();
  524. void OnSaveGame();
  525. void OnSaveBSP();
  526. void OnSaveVMF();
  527. void OnSubmit();
  528. void OnClearForm();
  529. void OnIncludeFile();
  530. void OnClearIncludedFiles();
  531. int GetArea();
  532. bool IsValidSubmission( bool verbose );
  533. bool IsValidEmailAddress( char const *email );
  534. void WipeData();
  535. void GetDataFileBase( char const *suffix, char *buf, int bufsize );
  536. const char *GetRepositoryURL( void );
  537. const char *GetSubmissionURL( int bugid );
  538. bool AddFileToZip( char const *relative );
  539. bool AddBugTextToZip( char const *textfilename, char const *text, int textlen );
  540. void CheckContinueQueryingSteamForCSERList();
  541. struct includedfile
  542. {
  543. char name[ 256 ];
  544. char fixedname[ 256 ];
  545. };
  546. bool UploadBugSubmission(
  547. char const *levelname,
  548. int bugId,
  549. char const *savefile,
  550. char const *screenshot,
  551. char const *bsp,
  552. char const *vmf,
  553. CUtlVector< includedfile >& includedfiles );
  554. bool UploadFile( char const *local, char const *remote, bool bDeleteLocal = false );
  555. void DenySound();
  556. void SuccessSound( int bugid );
  557. bool AutoFillToken( char const *token, bool partial );
  558. bool Compare( char const *token, char const *value, bool partial );
  559. char const *GetSubmitter();
  560. void OnFinishBugReport();
  561. void PauseGame( bool bPause );
  562. void GetConsoleHistory( CUtlBuffer &buf ) const;
  563. bool CopyInfoFromRemoteBug();
  564. bool m_bCanSubmit;
  565. bool m_bLoggedIn;
  566. bool m_bCanSeeRepository;
  567. bool m_bValidated;
  568. bool m_bUseNameForSubmitter;
  569. unsigned char m_fAutoAddScreenshot;
  570. enum AutoAddScreenshot { eAutoAddScreenshot_Detect = 0, eAutoAddScreenshot_Add = 1, eAutoAddScreenshot_DontAdd = 2 };
  571. char m_szScreenShotName[ 256 ];
  572. char m_szSaveGameName[ 256 ];
  573. char m_szBSPName[ 256 ];
  574. char m_szVMFName[ 256 ];
  575. char m_szLevel[ 256 ];
  576. CUtlVector< includedfile > m_IncludedFiles;
  577. bool m_bTakingSnapshot;
  578. bool m_bHidGameUIForSnapshot;
  579. int m_nSnapShotFrame;
  580. bool m_bAutoSubmit;
  581. bool m_bPause;
  582. bool m_bIsSubmittingRemoteBug; // If we're submitting a bug that has some of its fields sent from a remote machine
  583. CUtlString m_strRemoteBugInfoPath;
  584. char m_szVMFContentDirFullpath[ MAX_PATH ];
  585. vgui::DHANDLE< vgui::FileOpenDialog > m_hFileOpenDialog;
  586. vgui::DHANDLE< vgui::DirectorySelectDialog > m_hDirectorySelectDialog;
  587. // If true, then once directory for vmfs is selected, we act like the Include .vmf button got pressed, too
  588. bool m_bAddVMF;
  589. HZIP m_hZip;
  590. CBugReportUploadProgressDialog *m_pProgressDialog;
  591. vgui::DHANDLE< CBugReportFinishedDialog > m_hFinishedDialog;
  592. bool m_bWaitForFinish;
  593. float m_flPauseTime;
  594. TSteamGlobalUserID m_SteamID;
  595. netadr_t m_cserIP;
  596. bool m_bQueryingSteamForCSER;
  597. bool m_bIsPublic;
  598. CUtlString m_sDllName;
  599. int m_BugSub; // The number of bugs submitted. This is tracked and reset per map for internal dev stat tracking
  600. };
  601. //-----------------------------------------------------------------------------
  602. // Purpose: Basic help dialog
  603. //-----------------------------------------------------------------------------
  604. CBugUIPanel::CBugUIPanel( bool bIsPublic, vgui::Panel *parent ) :
  605. BaseClass( parent, "BugUIPanel"),
  606. m_bIsPublic( bIsPublic ),
  607. m_bAddVMF( false )
  608. {
  609. m_BugSub = 0;
  610. m_sDllName = m_bIsPublic ?
  611. BUG_REPORTER_PUBLIC_DLLNAME :
  612. GetInternalBugReporterDLL();
  613. m_hZip = (HZIP)0;
  614. m_hDirectorySelectDialog = NULL;
  615. m_hFileOpenDialog = NULL;
  616. m_pBugReporter = NULL;
  617. m_hBugReporter = 0;
  618. m_bQueryingSteamForCSER = false;
  619. memset( &m_SteamID, 0x00, sizeof(m_SteamID) );
  620. // Default server address (hardcoded in case not running on steam)
  621. char const *cserIP = "67.132.200.140:27013";
  622. // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  623. // NOTE: If you need to override the CSER Ip, make sure you tweak the code in
  624. // CheckContinueQueryingSteamForCSERList!!!!!!!!!!
  625. // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  626. NET_StringToAdr( cserIP, &m_cserIP );
  627. m_bValidated = false;
  628. m_szScreenShotName[ 0 ] = 0;
  629. m_szSaveGameName[ 0 ] = 0;
  630. m_szBSPName[ 0 ] = 0;
  631. m_szVMFName[ 0 ] = 0;
  632. m_szLevel[ 0 ] = 0;
  633. m_szVMFContentDirFullpath[ 0 ] = 0;
  634. m_IncludedFiles.Purge();
  635. m_nSnapShotFrame = -1;
  636. m_bTakingSnapshot = false;
  637. m_bHidGameUIForSnapshot = false;
  638. m_bAutoSubmit = false;
  639. m_bPause = false;
  640. m_bIsSubmittingRemoteBug = false;
  641. m_bCanSubmit = false;
  642. m_bLoggedIn = false;
  643. m_bCanSeeRepository = false;
  644. m_pProgressDialog = NULL;
  645. m_flPauseTime = 0.0f;
  646. m_bWaitForFinish = false;
  647. m_bUseNameForSubmitter = false;
  648. m_fAutoAddScreenshot = eAutoAddScreenshot_Detect;
  649. SetTitle("Bug Reporter", true);
  650. m_pTitle = new vgui::TextEntry( this, "BugTitle" );
  651. m_pTitle->SetMaximumCharCount( 60 ); // Titles can be 80 chars, but we put the mapname in front, so limit to 60
  652. m_pDescription = new vgui::TextEntry( this, "BugDescription" );
  653. m_pDescription->SetMultiline( true );
  654. m_pDescription->SetCatchEnterKey( true );
  655. m_pDescription->SetVerticalScrollbar( true );
  656. m_pEmail = new vgui::TextEntry( this, "BugEmail" );;
  657. m_pEmail->SetMaximumCharCount( 80 );
  658. m_pSubmitterLabel = new vgui::Label( this, "BugSubmitterLabel", "Submitter:" );
  659. m_pScreenShotURL = new vgui::Label( this, "BugScreenShotURL", "" );
  660. m_pSaveGameURL = new vgui::Label( this, "BugSaveGameURL", "" );
  661. m_pBSPURL = new vgui::Label( this, "BugBSPURL", "" );
  662. m_pVMFURL = new vgui::Label( this, "BugVMFURL", "" );
  663. m_pIncludedFiles = new vgui::TextEntry( this, "BugIncludedFiles" );
  664. m_pIncludedFiles->SetMultiline( true );
  665. m_pIncludedFiles->SetVerticalScrollbar( true );
  666. m_pIncludedFiles->SetEditable( false );
  667. m_pTakeShot = new vgui::Button( this, "BugTakeShot", "Take shot" );
  668. m_pSaveGame = new vgui::Button( this, "BugSaveGame", "Save game" );
  669. m_pSaveBSP = new vgui::Button( this, "BugSaveBSP", "Include .bsp" );
  670. m_pSaveVMF = new vgui::Button( this, "BugSaveVMF", "Include .vmf" );
  671. m_pChooseVMFFolder = new vgui::Button( this, "BugChooseVMFFolder", "Choose folder" );
  672. m_pIncludeFile = new vgui::Button( this, "BugIncludeFile", "Include file..." );
  673. m_pClearIncludes = new vgui::Button( this, "BugClearIncludedFiles", "Clear files" );
  674. m_pPosition = new vgui::Label( this, "BugPosition", "" );
  675. m_pOrientation = new vgui::Label( this, "BugOrientation", "" );
  676. m_pLevelName = new vgui::Label( this, "BugLevel", "" );
  677. m_pBuildNumber = new vgui::Label( this, "BugBuild", "" );
  678. m_pSubmitter = new ComboBox(this, "BugSubmitter", 5, false );
  679. m_pAssignTo = new ComboBox(this, "BugOwner", 10, false);
  680. m_pSeverity = new ComboBox(this, "BugSeverity", 10, false);
  681. m_pReportType = new ComboBox(this, "BugReportType", 10, false);
  682. m_pPriority = new ComboBox(this, "BugPriority", 10, false);
  683. m_pGameArea = new ComboBox(this, "BugArea", 10, false);
  684. m_pMapNumber = new ComboBox(this, "BugMapNumber", 10, false);
  685. m_pSubmit = new vgui::Button( this, "BugSubmit", "Submit" );
  686. m_pCancel = new vgui::Button( this, "BugCancel", "Cancel" );
  687. m_pClearForm = new vgui::Button( this, "BugClearForm", "Clear Form" );
  688. vgui::ivgui()->AddTickSignal( GetVPanel(), 0 );
  689. if ( m_bIsPublic )
  690. {
  691. LoadControlSettings("Resource\\BugUIPanel_Public.res");
  692. }
  693. else
  694. {
  695. LoadControlSettings("Resource\\BugUIPanel_Filequeue.res");
  696. }
  697. m_pChooseVMFFolder->SetCommand( new KeyValues( "OnChooseVMFFolder" ) );
  698. m_pChooseVMFFolder->AddActionSignalTarget( this );
  699. int w = GetWide();
  700. int h = GetTall();
  701. int x = ( videomode->GetModeWidth() - w ) / 2;
  702. int y = ( videomode->GetModeHeight() - h ) / 2;
  703. // Hidden by default
  704. SetVisible( false );
  705. SetSizeable( false );
  706. SetMoveable( true );
  707. SetPos( x, y );
  708. }
  709. void CBugUIPanel::Init()
  710. {
  711. Color clr( 50, 100, 255, 255 );
  712. Assert( !m_pBugReporter );
  713. m_hBugReporter = g_pFileSystem->LoadModule( m_sDllName);
  714. if( m_bIsPublic )
  715. {
  716. // Hack due to constructor called before phonehome->Init...
  717. m_hBugReporter = g_pFileSystem->LoadModule( m_sDllName );
  718. LoadControlSettings("Resource\\BugUIPanel_Public.res");
  719. int w = GetWide();
  720. int h = GetTall();
  721. int x = ( videomode->GetModeWidth() - w ) / 2;
  722. int y = ( videomode->GetModeHeight() - h ) / 2;
  723. SetPos( x, y );
  724. }
  725. if ( m_hBugReporter )
  726. {
  727. CreateInterfaceFn factory = Sys_GetFactory( m_hBugReporter );
  728. if ( factory )
  729. {
  730. m_pBugReporter = (IBugReporter *)factory( INTERFACEVERSION_BUGREPORTER, NULL );
  731. if( m_pBugReporter )
  732. {
  733. extern CreateInterfaceFn g_AppSystemFactory;
  734. if ( m_pBugReporter->Init( g_AppSystemFactory ) )
  735. {
  736. m_bCanSubmit = true;
  737. m_bLoggedIn = true;
  738. }
  739. else
  740. {
  741. m_pBugReporter = NULL;
  742. ConColorMsg( clr, "m_pBugReporter->Init() failed\n" );
  743. }
  744. }
  745. else
  746. {
  747. ConColorMsg( clr, "Couldn't get interface '%s' from '%s'\n", INTERFACEVERSION_BUGREPORTER, m_sDllName.String() );
  748. }
  749. }
  750. else
  751. {
  752. ConColorMsg( clr, "Couldn't get factory '%s'\n", m_sDllName.String() );
  753. }
  754. }
  755. else
  756. {
  757. ConColorMsg( clr, "Couldn't load '%s'\n", m_sDllName.String() );
  758. }
  759. if ( m_bCanSubmit )
  760. {
  761. PopulateControls();
  762. }
  763. if ( m_pBugReporter && m_pBugReporter->IsPublicUI() )
  764. {
  765. m_pSaveBSP->SetVisible( false );
  766. m_pBSPURL->SetVisible( false );
  767. m_pChooseVMFFolder->SetVisible( false );
  768. m_pSaveVMF->SetVisible( false );
  769. m_pVMFURL->SetVisible( false );
  770. m_pIncludeFile->SetVisible( false );
  771. m_pClearIncludes->SetVisible( false );
  772. m_pAssignTo->SetVisible( false );
  773. m_pSeverity->SetVisible( false );
  774. m_pPriority->SetVisible( false );
  775. m_pGameArea->SetVisible( false );
  776. m_pMapNumber->SetVisible( false );
  777. m_pIncludedFiles->SetVisible( false );
  778. m_pSubmitter->SetVisible( true );
  779. m_pSubmitterLabel->SetVisible( false );
  780. m_bQueryingSteamForCSER = true;
  781. }
  782. else
  783. {
  784. m_pEmail->SetVisible( false );
  785. m_pSubmitterLabel->SetVisible( true );
  786. m_pSubmitter->SetVisible( true );
  787. }
  788. Q_snprintf( m_szVMFContentDirFullpath, sizeof( m_szVMFContentDirFullpath ), "%s/maps", com_gamedir );
  789. Q_strlower( m_szVMFContentDirFullpath );
  790. Q_FixSlashes( m_szVMFContentDirFullpath );
  791. m_pBuildNumber->SetText( va( "%d", build_number() ) );
  792. }
  793. void CBugUIPanel::Shutdown()
  794. {
  795. if ( m_pBugReporter )
  796. {
  797. m_pBugReporter->Shutdown();
  798. }
  799. m_pBugReporter = NULL;
  800. if ( m_hBugReporter )
  801. {
  802. Sys_UnloadModule( m_hBugReporter );
  803. m_hBugReporter = 0;
  804. }
  805. }
  806. //-----------------------------------------------------------------------------
  807. // Purpose:
  808. //-----------------------------------------------------------------------------
  809. CBugUIPanel::~CBugUIPanel()
  810. {
  811. }
  812. void CBugUIPanel::OnTick()
  813. {
  814. BaseClass::OnTick();
  815. CheckContinueQueryingSteamForCSERList();
  816. if ( !IsVisible() )
  817. {
  818. if ( !m_bTakingSnapshot )
  819. {
  820. if ( m_nSnapShotFrame > 0 && host_framecount >= m_nSnapShotFrame + bugreporter_snapshot_delay.GetInt() )
  821. {
  822. // Wait for a few frames for nextbot debug entities to appear on screen
  823. TakeSnapshot();
  824. }
  825. return;
  826. }
  827. // wait two frames for the snapshot to occur
  828. if ( host_framecount < m_nSnapShotFrame + 2 )
  829. return;;
  830. m_bTakingSnapshot = false;
  831. m_nSnapShotFrame = -1;
  832. m_pScreenShotURL->SetText( m_szScreenShotName );
  833. if ( m_bHidGameUIForSnapshot || !m_bAutoSubmit )
  834. {
  835. EngineVGui()->ActivateGameUI();
  836. }
  837. m_bHidGameUIForSnapshot = false;
  838. SetVisible( true );
  839. MoveToFront();
  840. }
  841. if ( !m_bCanSubmit )
  842. {
  843. if ( m_pBugReporter && !m_pBugReporter->IsPublicUI() )
  844. {
  845. if ( !m_bCanSeeRepository )
  846. {
  847. Warning( "Bug UI disabled: Couldn't see repository\n" );
  848. }
  849. else if ( !m_bLoggedIn )
  850. {
  851. Warning( "Bug UI disabled: Couldn't log in to PVCS Tracker\n" );
  852. }
  853. else
  854. {
  855. Assert( 0 );
  856. }
  857. const char textError[] = "If you are accessing from VPN at home, try this:\n"
  858. "Set the ConVar 'bugreporter_username' to your Valve user name.\n"
  859. "Then call the command '_bugreporter_restart autoselect'.\n";
  860. Warning( "%s", textError );
  861. }
  862. SetVisible( false );
  863. return;
  864. }
  865. m_pSubmit->SetEnabled( IsValidSubmission( false ) );
  866. // Have to do it on the tick to ensure the UI is properly populated?
  867. if ( m_bAutoSubmit )
  868. {
  869. OnSubmit();
  870. SetVisible(false);
  871. m_bAutoSubmit = false;
  872. }
  873. if ( m_flPauseTime > 0.0f )
  874. {
  875. if ( m_flPauseTime <= system()->GetFrameTime())
  876. {
  877. m_flPauseTime = 0.0f;
  878. if ( m_pProgressDialog )
  879. {
  880. m_pProgressDialog->Close();
  881. }
  882. m_pProgressDialog = NULL;
  883. OnFinishBugReport();
  884. m_bWaitForFinish = true;
  885. if ( !m_hFinishedDialog.Get() )
  886. {
  887. m_hFinishedDialog = new CBugReportFinishedDialog(NULL, "FinishDialog", "#Steam_FinishedBug_WorkingTitle", "#Steam_FinishedBug_Text" );
  888. m_hFinishedDialog->Activate();
  889. vgui::input()->SetAppModalSurface(m_hFinishedDialog->GetVPanel());
  890. }
  891. }
  892. else
  893. {
  894. if ( m_pProgressDialog )
  895. {
  896. float percent = clamp( 1.0f - ( m_flPauseTime - system()->GetFrameTime() ) / (float)PUBLIC_BUGREPORT_WAIT_TIME, 0.0f, 1.0f );
  897. m_pProgressDialog->SetProgress( percent );
  898. }
  899. }
  900. }
  901. if ( m_bWaitForFinish && !m_hFinishedDialog.Get() )
  902. {
  903. m_bWaitForFinish = false;
  904. Close();
  905. }
  906. }
  907. //-----------------------------------------------------------------------------
  908. // Purpose:
  909. // Input : *suffix -
  910. // *buf -
  911. // bufsize -
  912. //-----------------------------------------------------------------------------
  913. void CBugUIPanel::GetDataFileBase( char const *suffix, char *buf, int bufsize )
  914. {
  915. struct tm t;
  916. Plat_GetLocalTime( &t );
  917. char who[ 128 ];
  918. Q_strncpy( who, suffix, sizeof( who ) );
  919. Q_strlower( who );
  920. if ( m_pBugReporter && m_pBugReporter->IsPublicUI() )
  921. {
  922. Q_snprintf( buf, bufsize, "%i_%02i_%02i", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday );
  923. }
  924. else
  925. {
  926. Q_snprintf( buf, bufsize, "%i_%02i_%02i_%s", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, who );
  927. }
  928. }
  929. //-----------------------------------------------------------------------------
  930. // Purpose:
  931. // Output : const char
  932. //-----------------------------------------------------------------------------
  933. const char *CBugUIPanel::GetRepositoryURL( void )
  934. {
  935. const char *pURL = m_pBugReporter->GetRepositoryURL();
  936. if ( pURL )
  937. return pURL;
  938. return BUG_REPOSITORY_URL;
  939. }
  940. const char *CBugUIPanel::GetSubmissionURL( int bugid )
  941. {
  942. const char *pURL = m_pBugReporter->GetSubmissionURL();
  943. if ( pURL )
  944. return pURL;
  945. static char url[512];
  946. Q_snprintf(url, sizeof(url), "%s/%i", GetRepositoryURL(), bugid);
  947. return url;
  948. }
  949. //-----------------------------------------------------------------------------
  950. // Purpose:
  951. //-----------------------------------------------------------------------------
  952. void CBugUIPanel::OnTakeSnapshot()
  953. {
  954. m_nSnapShotFrame = host_framecount;
  955. m_bTakingSnapshot = false;
  956. if ( EngineVGui()->IsGameUIVisible() )
  957. {
  958. m_bHidGameUIForSnapshot = true;
  959. EngineVGui()->HideGameUI();
  960. }
  961. else
  962. {
  963. m_bHidGameUIForSnapshot = false;
  964. }
  965. SetVisible( false );
  966. // unpause because we need entities to update for nextbot debug
  967. PauseGame( false );
  968. }
  969. void CBugUIPanel::TakeSnapshot()
  970. {
  971. m_nSnapShotFrame = host_framecount;
  972. m_bTakingSnapshot = true;
  973. GetDataFileBase( GetSubmitter(), m_szScreenShotName, sizeof( m_szScreenShotName ) );
  974. // Internal reports at 100% quality .jpg
  975. int quality = 100;
  976. if ( m_pBugReporter && m_pBugReporter->IsPublicUI() )
  977. {
  978. quality = 40;
  979. }
  980. Cbuf_AddText( Cbuf_GetCurrentPlayer(), va( "jpeg \"%s\" %i\n", m_szScreenShotName, quality ) );
  981. PauseGame( true );
  982. }
  983. //-----------------------------------------------------------------------------
  984. // Purpose:
  985. //-----------------------------------------------------------------------------
  986. void CBugUIPanel::OnSaveGame()
  987. {
  988. GetDataFileBase( GetSubmitter(), m_szSaveGameName, sizeof( m_szSaveGameName ) );
  989. if ( m_pBugReporter && m_pBugReporter->IsPublicUI() )
  990. {
  991. // External users send us a "minisave" file which doesn't contain data from other previously encoudntered/connected levels
  992. Cbuf_AddText( Cbuf_GetCurrentPlayer(), va( "minisave %s\n", m_szSaveGameName ) );
  993. }
  994. else
  995. {
  996. Cbuf_AddText( Cbuf_GetCurrentPlayer(), va( "save %s.sav notmostrecent copymap\n", m_szSaveGameName ) );
  997. }
  998. m_pSaveGameURL->SetText( m_szSaveGameName );
  999. }
  1000. void CBugUIPanel::OnSaveBSP()
  1001. {
  1002. GetDataFileBase( GetSubmitter(), m_szBSPName, sizeof( m_szBSPName ) );
  1003. m_pBSPURL->SetText( m_szBSPName );
  1004. }
  1005. void CBugUIPanel::OnSaveVMF()
  1006. {
  1007. if ( m_pBugReporter && m_pBugReporter->IsPublicUI() )
  1008. return;
  1009. char level[ 256 ];
  1010. m_pLevelName->GetText( level, sizeof( level ) );
  1011. // See if .vmf exists in assumed location
  1012. char localfile[ 512 ];
  1013. Q_snprintf( localfile, sizeof( localfile ), "%s/%s.vmf", m_szVMFContentDirFullpath, level );
  1014. if ( !g_pFileSystem->FileExists( localfile, NULL ) )
  1015. {
  1016. if ( !m_hDirectorySelectDialog.Get() )
  1017. {
  1018. m_hDirectorySelectDialog = new DirectorySelectDialog( this, "Choose .vmf folder" );
  1019. }
  1020. m_bAddVMF = true;
  1021. m_hDirectorySelectDialog->SetStartDirectory( m_szVMFContentDirFullpath );
  1022. m_hDirectorySelectDialog->DoModal();
  1023. return;
  1024. }
  1025. GetDataFileBase( GetSubmitter(), m_szVMFName, sizeof( m_szVMFName ) );
  1026. m_pVMFURL->SetText( m_szVMFName );
  1027. }
  1028. void CBugUIPanel::OnChooseVMFFolder()
  1029. {
  1030. if ( !m_hDirectorySelectDialog.Get() )
  1031. {
  1032. m_hDirectorySelectDialog = new DirectorySelectDialog( this, "Choose .vmf folder" );
  1033. }
  1034. m_bAddVMF = false;
  1035. m_hDirectorySelectDialog->SetStartDirectory( m_szVMFContentDirFullpath );
  1036. m_hDirectorySelectDialog->DoModal();
  1037. }
  1038. void CBugUIPanel::RepopulateMaps(int area_index, const char *default_level)
  1039. {
  1040. int c = m_pBugReporter->GetLevelCount(area_index);
  1041. int item = -1;
  1042. m_pMapNumber->DeleteAllItems();
  1043. for ( int i = 0; i < c; i++ )
  1044. {
  1045. const char *level = m_pBugReporter->GetLevel(area_index, i );
  1046. int id = m_pMapNumber->AddItem( level, NULL );
  1047. if (!Q_stricmp(default_level, level))
  1048. {
  1049. item = id;
  1050. }
  1051. }
  1052. if (item >= 0)
  1053. {
  1054. m_pMapNumber->ActivateItem( item );
  1055. }
  1056. else
  1057. {
  1058. m_pMapNumber->ActivateItemByRow(0);
  1059. }
  1060. }
  1061. void CBugUIPanel::OnChooseArea(vgui::Panel *panel)
  1062. {
  1063. if (panel == m_pGameArea)
  1064. {
  1065. if (!Q_strcmp(BUG_REPORTER_DLLNAME, GetInternalBugReporterDLL()))
  1066. {
  1067. int area_index = m_pGameArea->GetActiveItem();
  1068. const char *currentLevel = GetBaseLocalClient().IsActive() ? GetBaseLocalClient().m_szLevelNameShort : "console";
  1069. RepopulateMaps(area_index, currentLevel);
  1070. }
  1071. }
  1072. else if (panel == m_pMapNumber)
  1073. {
  1074. if (!Q_strcmp(BUG_REPORTER_DLLNAME, GetInternalBugReporterDLL()))
  1075. {
  1076. int area_index = m_pGameArea->GetActiveItem();
  1077. int map_index = m_pMapNumber->GetActiveItem();
  1078. const char *defaultOwner = m_pBugReporter->GetLevelOwner(area_index, map_index);
  1079. int c = m_pBugReporter->GetDisplayNameCount();
  1080. for ( int i = 0; i < c; i++ )
  1081. {
  1082. const char *userName = m_pBugReporter->GetUserName( i );
  1083. if (!Q_stricmp( userName, defaultOwner ))
  1084. {
  1085. m_pAssignTo->ActivateItem( i );
  1086. break;
  1087. }
  1088. }
  1089. }
  1090. }
  1091. }
  1092. void CBugUIPanel::OnDirectorySelected( char const *dir )
  1093. {
  1094. Q_strncpy( m_szVMFContentDirFullpath, dir, sizeof( m_szVMFContentDirFullpath ) );
  1095. Q_strlower( m_szVMFContentDirFullpath );
  1096. Q_FixSlashes( m_szVMFContentDirFullpath );
  1097. Q_StripTrailingSlash( m_szVMFContentDirFullpath );
  1098. if ( m_hDirectorySelectDialog != NULL )
  1099. {
  1100. m_hDirectorySelectDialog->MarkForDeletion();
  1101. }
  1102. // See if .vmf exists in assumed location
  1103. if ( m_bAddVMF )
  1104. {
  1105. GetDataFileBase( GetSubmitter(), m_szVMFName, sizeof( m_szVMFName ) );
  1106. m_pVMFURL->SetText( m_szVMFName );
  1107. }
  1108. m_bAddVMF = false;
  1109. }
  1110. void CBugUIPanel::OnFileSelected( char const *fullpath )
  1111. {
  1112. bool baseDirFile = false;
  1113. if ( m_pBugReporter && m_pBugReporter->IsPublicUI() )
  1114. return;
  1115. if ( !fullpath || !fullpath[ 0 ] )
  1116. return;
  1117. char relativepath[ 512 ];
  1118. if ( !g_pFileSystem->FullPathToRelativePath( fullpath, relativepath, sizeof( relativepath ) ) )
  1119. {
  1120. if ( Q_stristr( fullpath, com_basedir ) )
  1121. {
  1122. Q_snprintf( relativepath, sizeof( relativepath ), "..%s", fullpath + strlen(com_basedir) );
  1123. baseDirFile = true;
  1124. }
  1125. else
  1126. {
  1127. Msg("Only files beneath the base game directory can be included\n" );
  1128. return;
  1129. }
  1130. }
  1131. char ext[ 10 ];
  1132. Q_ExtractFileExtension( relativepath, ext, sizeof( ext ) );
  1133. if ( m_hFileOpenDialog != NULL )
  1134. {
  1135. m_hFileOpenDialog->MarkForDeletion();
  1136. }
  1137. includedfile inc;
  1138. Q_strncpy( inc.name, relativepath, sizeof( inc.name ) );
  1139. if ( baseDirFile )
  1140. {
  1141. Q_snprintf( inc.fixedname, sizeof( inc.fixedname ), "%s", inc.name+3 ); // strip the "..\"
  1142. }
  1143. else
  1144. {
  1145. Q_snprintf( inc.fixedname, sizeof( inc.fixedname ), "%s", inc.name );
  1146. }
  1147. Q_FixSlashes( inc.fixedname );
  1148. m_IncludedFiles.AddToTail( inc );
  1149. char concat[ 8192 ];
  1150. concat[ 0 ] = 0;
  1151. for ( int i = 0 ; i < m_IncludedFiles.Count(); ++i )
  1152. {
  1153. Q_strncat( concat, m_IncludedFiles[ i ].name, sizeof( concat), COPY_ALL_CHARACTERS );
  1154. Q_strncat( concat, "\n", sizeof( concat), COPY_ALL_CHARACTERS );
  1155. }
  1156. m_pIncludedFiles->SetText( concat );
  1157. }
  1158. void CBugUIPanel::OnIncludeFile()
  1159. {
  1160. if ( m_pBugReporter && m_pBugReporter->IsPublicUI() )
  1161. return;
  1162. if ( !m_hFileOpenDialog.Get() )
  1163. {
  1164. m_hFileOpenDialog = new vgui::FileOpenDialog( this, "Choose file to include", true );
  1165. if ( m_hFileOpenDialog != NULL )
  1166. {
  1167. m_hFileOpenDialog->SetDeleteSelfOnClose( false );
  1168. m_hFileOpenDialog->AddFilter("*.*", "All Files (*.*)", true);
  1169. }
  1170. }
  1171. if ( m_hFileOpenDialog )
  1172. {
  1173. char startPath[ MAX_PATH ];
  1174. Q_strncpy( startPath, com_gamedir, sizeof( startPath ) );
  1175. Q_FixSlashes( startPath );
  1176. m_hFileOpenDialog->SetStartDirectory( startPath );
  1177. m_hFileOpenDialog->DoModal( false );
  1178. }
  1179. //GetDataFileBase( GetSubmitter(), m_szVMFName, sizeof( m_szVMFName ) );
  1180. }
  1181. void CBugUIPanel::OnClearIncludedFiles()
  1182. {
  1183. m_IncludedFiles.Purge();
  1184. m_pIncludedFiles->SetText( "" );
  1185. }
  1186. //-----------------------------------------------------------------------------
  1187. // Purpose: Shows the panel
  1188. //-----------------------------------------------------------------------------
  1189. void CBugUIPanel::Activate()
  1190. {
  1191. ACTIVE_SPLITSCREEN_PLAYER_GUARD( 0 );
  1192. if ( !m_bValidated )
  1193. {
  1194. m_bValidated = true;
  1195. Init();
  1196. DetermineSubmitterName();
  1197. }
  1198. if ( m_pGameArea->GetItemCount() != 0 )
  1199. {
  1200. int iArea = GetArea();
  1201. if ( iArea != 0 )
  1202. {
  1203. if ( m_pGameArea->GetActiveItem() != iArea )
  1204. m_pGameArea->ActivateItem( iArea );
  1205. else
  1206. OnChooseArea( m_pGameArea );
  1207. }
  1208. }
  1209. if ( GetBaseLocalClient().IsActive() )
  1210. {
  1211. Vector org = MainViewOrigin();
  1212. QAngle ang;
  1213. VectorAngles( MainViewForward(), ang );
  1214. IClientEntity *localPlayer = entitylist->GetClientEntity( GetBaseLocalClient().m_nPlayerSlot + 1 );
  1215. if ( localPlayer )
  1216. {
  1217. org = localPlayer->GetAbsOrigin();
  1218. }
  1219. m_pPosition->SetText( va( "%f %f %f", org.x, org.y, org.z ) );
  1220. m_pOrientation->SetText( va( "%f %f %f", ang.x, ang.y, ang.z ) );
  1221. m_pLevelName->SetText( GetBaseLocalClient().m_szLevelNameShort );
  1222. m_pSaveGame->SetEnabled( ( GetBaseLocalClient().m_nMaxClients == 1 ) ? true : false );
  1223. m_pSaveBSP->SetEnabled( true );
  1224. m_pSaveVMF->SetEnabled( true );
  1225. m_pChooseVMFFolder->SetEnabled( true );
  1226. }
  1227. else
  1228. {
  1229. m_pPosition->SetText( "console" );
  1230. m_pOrientation->SetText( "console" );
  1231. m_pLevelName->SetText( "console" );
  1232. m_pSaveGame->SetEnabled( false );
  1233. m_pSaveBSP->SetEnabled( false );
  1234. m_pSaveVMF->SetEnabled( false );
  1235. m_pChooseVMFFolder->SetEnabled( false );
  1236. }
  1237. BaseClass::Activate();
  1238. m_pTitle->RequestFocus();
  1239. m_pTitle->SelectAllText( true );
  1240. // Automatically make a screenshot if requested.
  1241. if (!m_szScreenShotName[0] )
  1242. {
  1243. bool bAutoAddScreenshot = false;
  1244. // For public bugreports should be explicitly requested
  1245. if ( m_bIsPublic )
  1246. {
  1247. if ( m_fAutoAddScreenshot == CBugUIPanel::eAutoAddScreenshot_Add )
  1248. {
  1249. bAutoAddScreenshot = true;
  1250. }
  1251. }
  1252. // For internal bugreports shouldn't be explicitly denied
  1253. else
  1254. {
  1255. if ( m_fAutoAddScreenshot != CBugUIPanel::eAutoAddScreenshot_DontAdd )
  1256. {
  1257. bAutoAddScreenshot = true;
  1258. }
  1259. }
  1260. if ( bAutoAddScreenshot )
  1261. {
  1262. OnTakeSnapshot();
  1263. }
  1264. }
  1265. // HACK: This is only relevant for portal2.
  1266. Msg( "BUG REPORT PORTAL POSITIONS:\n" );
  1267. Cbuf_AddText( Cbuf_GetCurrentPlayer(), "portal_report\n" );
  1268. }
  1269. void CBugUIPanel::WipeData()
  1270. {
  1271. m_fAutoAddScreenshot = eAutoAddScreenshot_Detect;
  1272. m_szScreenShotName[ 0 ] = 0;
  1273. m_pScreenShotURL->SetText( "Screenshot file" );
  1274. m_szSaveGameName[ 0 ] = 0;
  1275. m_pSaveGameURL->SetText( "Save game file" );
  1276. m_szBSPName[ 0 ] = 0;
  1277. m_pBSPURL->SetText( ".bsp file" );
  1278. m_szVMFName[ 0 ] = 0;
  1279. m_pVMFURL->SetText( ".vmf file" );
  1280. m_IncludedFiles.Purge();
  1281. m_pIncludedFiles->SetText( "" );
  1282. // m_pTitle->SetText( "" );
  1283. m_pDescription->SetText( "" );
  1284. m_pEmail->SetText( "" );
  1285. m_bIsSubmittingRemoteBug = false;
  1286. m_strRemoteBugInfoPath.Clear();
  1287. }
  1288. //-----------------------------------------------------------------------------
  1289. // Purpose: method to make sure the email address is acceptable to be used by steam
  1290. //-----------------------------------------------------------------------------
  1291. bool CBugUIPanel::IsValidEmailAddress( char const *email )
  1292. {
  1293. // basic size check
  1294. if (!email || strlen(email) < 5)
  1295. return false;
  1296. // make sure all the characters in the string are valid
  1297. {for (const char *sz = email; *sz; sz++)
  1298. {
  1299. if (!V_isalnum(*sz) && *sz != '.' && *sz != '-' && *sz != '@' && *sz != '_')
  1300. return false;
  1301. }}
  1302. // make sure it has letters, then the '@', then letters, then '.', then letters
  1303. const char *sz = email;
  1304. if (!V_isalnum(*sz))
  1305. return false;
  1306. sz = strstr(sz, "@");
  1307. if (!sz)
  1308. return false;
  1309. sz++;
  1310. if (!V_isalnum(*sz))
  1311. return false;
  1312. sz = strstr(sz, ".");
  1313. if (!sz)
  1314. return false;
  1315. sz++;
  1316. if (!V_isalnum(*sz))
  1317. return false;
  1318. return true;
  1319. }
  1320. bool CBugUIPanel::IsValidSubmission( bool verbose )
  1321. {
  1322. if ( !m_pBugReporter )
  1323. return false;
  1324. // If set, this machine sends it's bugs to fileserver
  1325. // and another mahchine will submit. Validation checking
  1326. // can be trusted to the other machine.
  1327. if ( CommandLine()->CheckParm( "-remotebug" ) )
  1328. {
  1329. return true;
  1330. }
  1331. bool isPublic = m_pBugReporter->IsPublicUI();
  1332. char title[ 256 ];
  1333. char desc[ 4096 ];
  1334. title[ 0 ] = 0;
  1335. desc[ 0 ] = 0;
  1336. m_pTitle->GetText( title, sizeof( title ) );
  1337. if ( !title[ 0 ] )
  1338. {
  1339. if ( verbose )
  1340. {
  1341. Warning( "Bug must have a title\n" );
  1342. }
  1343. return false;
  1344. }
  1345. // Only require description in public UI
  1346. if ( isPublic )
  1347. {
  1348. m_pDescription->GetText( desc, sizeof( desc ) );
  1349. if ( !desc[ 0 ] )
  1350. {
  1351. if ( verbose )
  1352. {
  1353. Warning( "Bug must have a description\n" );
  1354. }
  1355. return false;
  1356. }
  1357. }
  1358. if ( !isPublic && m_pSeverity->GetActiveItem() < 0 )
  1359. {
  1360. if ( verbose )
  1361. {
  1362. Warning( "Severity not set!\n" );
  1363. }
  1364. return false;
  1365. }
  1366. if ( !isPublic && m_pAssignTo->GetActiveItem() < 0 )
  1367. {
  1368. if ( verbose )
  1369. {
  1370. Warning( "Owner not set!\n" );
  1371. }
  1372. return false;
  1373. }
  1374. char owner[ 256 ];
  1375. Q_strncpy( owner, m_pBugReporter->GetDisplayName( m_pAssignTo->GetActiveItem() ), sizeof( owner ) );
  1376. if ( !isPublic && !Q_stricmp( owner, "<<Unassigned>>" ) )
  1377. {
  1378. if ( verbose )
  1379. {
  1380. Warning( "Owner not set!\n" );
  1381. }
  1382. return false;
  1383. }
  1384. if ( !isPublic && m_pPriority->GetActiveItem() < 0 )
  1385. {
  1386. if ( verbose )
  1387. {
  1388. Warning( "Priority not set!\n" );
  1389. }
  1390. return false;
  1391. }
  1392. if ( !isPublic && m_pReportType->GetActiveItem() < 0 )
  1393. {
  1394. if ( verbose )
  1395. {
  1396. Warning( "ReportType not set!\n" );
  1397. }
  1398. return false;
  1399. }
  1400. if ( !isPublic && m_pGameArea->GetActiveItem() < 0 )
  1401. {
  1402. if ( verbose )
  1403. {
  1404. Warning( "Area not set!\n" );
  1405. }
  1406. return false;
  1407. }
  1408. // Public must have a selection and it can't be the "<<Choose One>>"
  1409. if ( isPublic && m_pReportType->GetActiveItem() <= 0 )
  1410. {
  1411. if ( verbose )
  1412. {
  1413. Warning( "ReportType not set!\n" );
  1414. }
  1415. return false;
  1416. }
  1417. if ( isPublic )
  1418. {
  1419. char email[ 80 ];
  1420. m_pEmail->GetText( email, sizeof( email ) );
  1421. if ( email[ 0 ] != 0 &&
  1422. !IsValidEmailAddress( email ) )
  1423. {
  1424. return false;
  1425. }
  1426. }
  1427. return true;
  1428. }
  1429. bool CBugUIPanel::AddBugTextToZip( char const *textfilename, char const *text, int textlen )
  1430. {
  1431. if ( !m_hZip )
  1432. {
  1433. // Create using OS pagefile memory...
  1434. m_hZip = CreateZipZ( 0, MAX_ZIP_SIZE, ZIP_MEMORY );
  1435. Assert( m_hZip );
  1436. if ( !m_hZip )
  1437. {
  1438. return false;
  1439. }
  1440. }
  1441. ZipAdd( m_hZip, textfilename, (void *)text, textlen, ZIP_MEMORY );
  1442. return true;
  1443. }
  1444. bool CBugUIPanel::AddFileToZip( char const *relative )
  1445. {
  1446. if ( !m_hZip )
  1447. {
  1448. // Create using OS pagefile memory...
  1449. m_hZip = CreateZipZ( 0, MAX_ZIP_SIZE, ZIP_MEMORY );
  1450. Assert( m_hZip );
  1451. if ( !m_hZip )
  1452. {
  1453. return false;
  1454. }
  1455. }
  1456. char fullpath[ 512 ];
  1457. if ( g_pFileSystem->RelativePathToFullPath( relative, "GAME", fullpath, sizeof( fullpath ) ) )
  1458. {
  1459. char extension[ 32 ];
  1460. Q_ExtractFileExtension( relative, extension, sizeof( extension ) );
  1461. char basename[ 256 ];
  1462. Q_FileBase( relative, basename, sizeof( basename ) );
  1463. char outname[ 512 ];
  1464. Q_snprintf( outname, sizeof( outname ), "%s.%s", basename, extension );
  1465. ZipAdd( m_hZip, outname, fullpath, 0, ZIP_FILENAME );
  1466. return true;
  1467. }
  1468. return false;
  1469. }
  1470. class CKeyValuesDumpForBugreport : public IKeyValuesDumpContextAsText
  1471. {
  1472. public:
  1473. CKeyValuesDumpForBugreport( CUtlBuffer &buffer ) : m_buffer( buffer ) {}
  1474. public:
  1475. virtual bool KvWriteText( char const *szText ) { m_buffer.Printf( "%s", szText ); return true; }
  1476. protected:
  1477. CUtlBuffer &m_buffer;
  1478. };
  1479. void CBugUIPanel::OnSubmit()
  1480. {
  1481. if ( !m_bCanSubmit )
  1482. {
  1483. return;
  1484. }
  1485. if ( !IsValidSubmission( true ) )
  1486. {
  1487. // Play deny sound
  1488. DenySound();
  1489. return;
  1490. }
  1491. bool isPublic = m_pBugReporter->IsPublicUI();
  1492. char title[ 256 ];
  1493. char desc[ 8192 ];
  1494. char severity[ 256 ];
  1495. char area[ 256 ];
  1496. char mapnumber[ 256 ];
  1497. char priority[ 256 ];
  1498. char assignedto[ 256 ];
  1499. char level[ 256 ];
  1500. char position[ 256 ];
  1501. char orientation[ 256 ];
  1502. char build[ 256 ];
  1503. char report_type[ 256 ];
  1504. char email[ 256 ];
  1505. title[ 0 ] = 0;
  1506. desc[ 0 ] = 0;
  1507. severity[ 0 ] = 0;
  1508. area[ 0 ] = 0;
  1509. mapnumber[ 0] = 0;
  1510. priority[ 0 ] = 0;
  1511. assignedto[ 0 ] = 0;
  1512. level[ 0 ] = 0;
  1513. orientation[ 0 ] = 0;
  1514. position[ 0 ] = 0;
  1515. build[ 0 ] = 0;
  1516. report_type [ 0 ] = 0;
  1517. email[ 0 ] = 0;
  1518. Assert( m_pBugReporter );
  1519. // Stuff bug data files up to server
  1520. m_pBugReporter->StartNewBugReport();
  1521. // If we are submitting a remote bug, replace fields that are specific to the remote machine
  1522. // (position, screenshot, etc) with the ones saved in the fileserver KV file
  1523. if ( m_bIsSubmittingRemoteBug )
  1524. {
  1525. CopyInfoFromRemoteBug();
  1526. }
  1527. char temp[ 256 ];
  1528. m_pTitle->GetText( temp, sizeof( temp ) );
  1529. if ( !m_bIsSubmittingRemoteBug )
  1530. {
  1531. if ( host_state.worldmodel )
  1532. {
  1533. char mapname[256];
  1534. CL_SetupMapName( modelloader->GetName( host_state.worldmodel ), mapname, sizeof( mapname ) );
  1535. Q_snprintf( title, sizeof( title ), "%s: %s", mapname, temp );
  1536. }
  1537. else
  1538. {
  1539. Q_snprintf( title, sizeof( title ), "%s", temp );
  1540. }
  1541. }
  1542. else
  1543. {
  1544. // remote bugs add the map used by the remote machine
  1545. char mapname[256];
  1546. if ( m_szLevel[0] == 0 )
  1547. {
  1548. V_strncpy( m_szLevel, "console", sizeof( m_szLevel ) );
  1549. }
  1550. V_strncpy( mapname, m_szLevel, sizeof( mapname ) );
  1551. Q_snprintf( title, sizeof( title ), "%s: %s", mapname, temp );
  1552. }
  1553. Msg( "title: %s\n", title );
  1554. m_pDescription->GetText( desc, sizeof( desc ) );
  1555. Msg( "description: %s\n", desc );
  1556. m_pLevelName->GetText( level, sizeof( level ) );
  1557. m_pPosition->GetText( position, sizeof( position ) );
  1558. m_pOrientation->GetText( orientation, sizeof( orientation ) );
  1559. m_pBuildNumber->GetText( build, sizeof( build ) );
  1560. if ( g_pFileSystem->IsSteam() )
  1561. {
  1562. Q_strncat( build, " (Steam)", sizeof(build), COPY_ALL_CHARACTERS );
  1563. }
  1564. else
  1565. {
  1566. Q_strncat( build, " (Perforce)", sizeof(build), COPY_ALL_CHARACTERS );
  1567. }
  1568. MaterialAdapterInfo_t info;
  1569. materials->GetDisplayAdapterInfo( materials->GetCurrentAdapter(), info );
  1570. char driverinfo[ 2048 ];
  1571. const MaterialSystem_Config_t& matSysCfg = materials->GetCurrentConfigForVideoCard();
  1572. char const *dxlevel = "Unk";
  1573. if ( g_pMaterialSystemHardwareConfig )
  1574. {
  1575. dxlevel = COM_DXLevelToString( g_pMaterialSystemHardwareConfig->GetDXSupportLevel() ) ;
  1576. }
  1577. const char *pMaterialThreadMode = "???";
  1578. if ( g_pMaterialSystem )
  1579. {
  1580. switch ( g_pMaterialSystem->GetThreadMode() )
  1581. {
  1582. case MATERIAL_SINGLE_THREADED:
  1583. pMaterialThreadMode = "MATERIAL_SINGLE_THREADED";
  1584. break;
  1585. case MATERIAL_QUEUED_SINGLE_THREADED:
  1586. pMaterialThreadMode = "MATERIAL_QUEUED_SINGLE_THREADED";
  1587. break;
  1588. case MATERIAL_QUEUED_THREADED:
  1589. pMaterialThreadMode = "MATERIAL_QUEUED_THREADED";
  1590. break;
  1591. default:
  1592. pMaterialThreadMode = "unknown";
  1593. break;
  1594. }
  1595. }
  1596. #ifdef VPROF_ENABLED
  1597. int nLostDeviceCount = *g_VProfCurrentProfile.FindOrCreateCounter( "reacquire_resources", COUNTER_GROUP_NO_RESET );
  1598. #else
  1599. int nLostDeviceCount = -1;
  1600. #endif
  1601. Q_snprintf( driverinfo, sizeof( driverinfo ),
  1602. "Driver Name: %s\n"
  1603. "VendorId / DeviceId: 0x%x / 0x%x\n"
  1604. "SubSystem / Rev: 0x%x / 0x%x\n"
  1605. "DXLevel: %s\nVid: %i x %i\n"
  1606. "Framerate: %.3f\n"
  1607. "Window mode: %s\n"
  1608. "Number of ReaquireResource events (lost device): %d\n"
  1609. "Material system thread mode: %s",
  1610. info.m_pDriverName,
  1611. info.m_VendorID,
  1612. info.m_DeviceID,
  1613. info.m_SubSysID,
  1614. info.m_Revision,
  1615. dxlevel ? dxlevel : "Unk",
  1616. videomode->GetModeWidth(), videomode->GetModeHeight(),
  1617. g_fFramesPerSecond,
  1618. matSysCfg.Windowed() ? ( matSysCfg.NoWindowBorder() ? "Windowed no border" : "Windowed" ) : "Fullscreen",
  1619. nLostDeviceCount,
  1620. pMaterialThreadMode );
  1621. Msg( "%s\n", driverinfo );
  1622. int latency = 0;
  1623. if ( GetBaseLocalClient().m_NetChannel )
  1624. {
  1625. latency = (int)( 1000.0f * GetBaseLocalClient().m_NetChannel->GetAvgLatency( FLOW_OUTGOING ) );
  1626. }
  1627. static ConVarRef host_thread_mode( "host_thread_mode" );
  1628. static ConVarRef sv_alternateticks( "sv_alternateticks" );
  1629. static ConVarRef mat_queue_mode( "mat_queue_mode" );
  1630. CUtlBuffer bufDescription( 0, 0, CUtlBuffer::TEXT_BUFFER );
  1631. static ConVarRef skill("skill");
  1632. bufDescription.Printf( "Convars:\n"
  1633. "\tnet: rate %i update %i cmd %i latency %i msec\n"
  1634. "\thost_thread_mode: %i\n"
  1635. "\tsv_alternateticks: %i\n"
  1636. "\tmat_queue_mode: %i\n",
  1637. cl_rate->GetInt(),
  1638. (int)cl_updaterate->GetFloat(),
  1639. (int)cl_cmdrate->GetFloat(),
  1640. latency,
  1641. host_thread_mode.GetInt(),
  1642. sv_alternateticks.GetInt(),
  1643. mat_queue_mode.GetInt()
  1644. );
  1645. KeyValues *pModConvars = new KeyValues( "bugreport_convars" );
  1646. if ( pModConvars->LoadFromFile( g_pFileSystem, "scripts/bugreport_convars.txt", "GAME" ) )
  1647. {
  1648. KeyValues *entry = pModConvars->GetFirstSubKey();
  1649. while ( entry )
  1650. {
  1651. ConVarRef modConvar( entry->GetString() );
  1652. if ( modConvar.IsValid() )
  1653. {
  1654. bufDescription.Printf( "%s: %s\n", entry->GetName(), modConvar.GetString() );
  1655. }
  1656. entry = entry->GetNextKey();
  1657. }
  1658. }
  1659. pModConvars->deleteThis();
  1660. if ( GetBaseLocalClient().IsActive() && g_ServerGlobalVariables.mapversion != 0 && host_state.worldmodel )
  1661. {
  1662. // Note, this won't work in multiplayer, oh well...
  1663. extern CGlobalVars g_ServerGlobalVariables;
  1664. long mapfiletime = g_pFileSystem->GetFileTime( modelloader->GetName( host_state.worldmodel ), "GAME" );
  1665. if ( !isPublic && mapfiletime != 0L )
  1666. {
  1667. char filetimebuf[ 64 ];
  1668. g_pFileSystem->FileTimeToString( filetimebuf, sizeof( filetimebuf ), mapfiletime );
  1669. bufDescription.Printf( "Map version: %i\nFile timestamp: %s", g_ServerGlobalVariables.mapversion, filetimebuf );
  1670. }
  1671. else
  1672. {
  1673. bufDescription.Printf( "Map version: %i\n", g_ServerGlobalVariables.mapversion );
  1674. }
  1675. }
  1676. // We only want to get extra bug info if we are connected to SteamBeta
  1677. // Also, serverGameClients->GetBugReportInfo has a fairly high percentage crash rate
  1678. // when used in SteamPublic.
  1679. if ( k_EUniverseBeta == GetSteamUniverse() )
  1680. {
  1681. if ( sv.IsActive() && serverGameClients )
  1682. {
  1683. char gamedlldata[ 2048 ];
  1684. Q_memset( gamedlldata, 0, sizeof( gamedlldata ) );
  1685. serverGameClients->GetBugReportInfo( gamedlldata, sizeof( gamedlldata ) );
  1686. bufDescription.Printf( "%s", gamedlldata );
  1687. }
  1688. // Call into matchmaking.dll for matchmaking bug info
  1689. bufDescription.Printf( "matchmaking.dll info\n" );
  1690. if ( g_pMatchFramework )
  1691. {
  1692. if ( IMatchSession *pMatchSession = g_pMatchFramework->GetMatchSession() )
  1693. {
  1694. bufDescription.Printf( "match session %p\n", pMatchSession );
  1695. CKeyValuesDumpForBugreport kvDumpContext( bufDescription );
  1696. bufDescription.Printf( "session system data:\n" );
  1697. pMatchSession->GetSessionSystemData()->Dump( &kvDumpContext );
  1698. bufDescription.Printf( "session settings:\n" );
  1699. pMatchSession->GetSessionSettings()->Dump( &kvDumpContext );
  1700. }
  1701. else
  1702. {
  1703. bufDescription.Printf( "[ no match session ]\n" );
  1704. }
  1705. }
  1706. }
  1707. {
  1708. bufDescription.Printf( "\n" );
  1709. }
  1710. {
  1711. bufDescription.Printf( "gamedir: %s\n", com_gamedir );
  1712. }
  1713. {
  1714. bufDescription.Printf( "\n" );
  1715. }
  1716. char blackbox[8192];
  1717. double now = Plat_FloatTime();
  1718. int hh = int(now/(60*60));
  1719. int mm = int(now/60) % 60;
  1720. double ss = now - (mm + hh*60)*60;
  1721. V_snprintf(blackbox, sizeof(blackbox), "Blackbox dumped at %02d:%02d:%02.3f\n", hh, mm, ss);
  1722. for ( int type = 0; type < gBlackBox->GetTypeCount(); type++ )
  1723. {
  1724. for (int i = 0; i < gBlackBox->Count( type ); i++)
  1725. {
  1726. CFmtStrN<1024+16> entry;
  1727. entry.sprintf("%s: %s\n", gBlackBox->GetTypeName(type), gBlackBox->Get( type, i ));
  1728. Q_strncat(blackbox, entry, sizeof(blackbox));
  1729. }
  1730. }
  1731. bufDescription.Printf( "%s", blackbox );
  1732. Msg( "%s", (char *)bufDescription.Base() );
  1733. CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
  1734. buf.Printf( "Console:\n\n" );
  1735. GetConsoleHistory( buf );
  1736. if ( !m_bIsSubmittingRemoteBug )
  1737. {
  1738. // Add it as an attached file
  1739. char consolePath[ MAX_PATH ];
  1740. g_pFullFileSystem->RelativePathToFullPath( "bugconsole.txt", "MOD", consolePath, sizeof( consolePath ) );
  1741. g_pFullFileSystem->WriteFile( consolePath, "MOD", buf );
  1742. OnFileSelected( consolePath );
  1743. }
  1744. // int nBytesToPut = MIN( buf.TellPut(), MAX( bugreporter_console_bytes.GetInt(), 1 ) );
  1745. // char *pStart = (char *)buf.Base() + buf.TellPut() - nBytesToPut;
  1746. //
  1747. // bufDescription.Printf( "\n\n-----------------------------------------------------------\nConsole: (last %d bytes)\n\n", nBytesToPut );
  1748. // bufDescription.Put( (char *)pStart, nBytesToPut );
  1749. if ( !isPublic )
  1750. {
  1751. m_pSeverity->GetText( severity, sizeof( severity ) );
  1752. Msg( "severity %s\n", severity );
  1753. m_pGameArea->GetText( area, sizeof( area ) );
  1754. Msg( "area %s\n", area );
  1755. m_pMapNumber->GetText( mapnumber, sizeof( mapnumber) );
  1756. Msg( "map number %s\n", mapnumber);
  1757. m_pPriority->GetText( priority, sizeof( priority ) );
  1758. Msg( "priority %s\n", priority );
  1759. m_pAssignTo->GetText( assignedto, sizeof( assignedto ) );
  1760. Msg( "owner %s\n", assignedto );
  1761. }
  1762. if ( isPublic )
  1763. {
  1764. m_pEmail->GetText( email, sizeof( email ) );
  1765. if ( Q_strlen( email ) > 0 )
  1766. {
  1767. Msg( "email %s\n", email );
  1768. }
  1769. else
  1770. {
  1771. Msg( "Not sending email address\n" );
  1772. }
  1773. m_pBugReporter->SetOwner( email );
  1774. }
  1775. else
  1776. {
  1777. m_pBugReporter->SetOwner( m_pBugReporter->GetUserNameForDisplayName( assignedto ) );
  1778. }
  1779. char submitter[ 256 ];
  1780. m_pSubmitter->GetText( submitter, sizeof( submitter ) );
  1781. m_pBugReporter->SetSubmitter( m_pBugReporter->GetUserNameForDisplayName( submitter ) );
  1782. Msg( "submitter %s\n", submitter );
  1783. m_pReportType->GetText( report_type, sizeof( report_type ) );
  1784. Msg( "report_type %s\n", report_type );
  1785. Msg( "level %s\n", level );
  1786. Msg( "position %s\n", position );
  1787. Msg( "orientation %s\n", orientation );
  1788. Msg( "build %s\n", build );
  1789. if ( m_szSaveGameName[ 0 ] )
  1790. {
  1791. Msg( "save file save/%s.sav\n", m_szSaveGameName );
  1792. }
  1793. else
  1794. {
  1795. Msg( "no save game\n" );
  1796. }
  1797. if ( m_szScreenShotName[ 0 ] )
  1798. {
  1799. Msg( "screenshot screenshots/%s.jpg\n", m_szScreenShotName );
  1800. }
  1801. else
  1802. {
  1803. Msg( "no screenshot\n" );
  1804. }
  1805. if ( !isPublic )
  1806. {
  1807. if ( m_szBSPName[ 0 ] )
  1808. {
  1809. Msg( "bsp file maps/%s.bsp\n", m_szBSPName );
  1810. }
  1811. if ( m_szVMFName[ 0 ] )
  1812. {
  1813. Msg( "vmf file maps/%s.vmf\n", m_szVMFName );
  1814. }
  1815. if ( m_IncludedFiles.Count() > 0 )
  1816. {
  1817. for ( int i = 0; i < m_IncludedFiles.Count(); ++i )
  1818. {
  1819. Msg( "Include: %s\n", m_IncludedFiles[ i ].name );
  1820. }
  1821. }
  1822. }
  1823. m_pBugReporter->SetTitle( title );
  1824. m_pBugReporter->SetDescription( desc );
  1825. if ( !m_bIsSubmittingRemoteBug )
  1826. {
  1827. m_pBugReporter->SetLevel( level );
  1828. m_pBugReporter->SetPosition( position );
  1829. m_pBugReporter->SetOrientation( orientation );
  1830. m_pBugReporter->SetBuildNumber( build );
  1831. }
  1832. m_pBugReporter->SetSeverity( severity );
  1833. m_pBugReporter->SetPriority( priority );
  1834. m_pBugReporter->SetArea( area );
  1835. m_pBugReporter->SetMapNumber( mapnumber );
  1836. m_pBugReporter->SetReportType( report_type );
  1837. if ( !m_bIsSubmittingRemoteBug )
  1838. {
  1839. m_pBugReporter->SetDriverInfo( driverinfo );
  1840. m_pBugReporter->SetMiscInfo( (char const *)bufDescription.Base() );
  1841. m_pBugReporter->SetConsoleHistory( (char const *)buf.Base() );
  1842. }
  1843. m_pBugReporter->SetCSERAddress( m_cserIP );
  1844. m_pBugReporter->SetExeName( "hl2.exe" );
  1845. m_pBugReporter->SetGameDirectory( com_gamedir );
  1846. const CPUInformation& pi = GetCPUInformation();
  1847. m_pBugReporter->SetRAM( GetRam() );
  1848. double fFrequency = pi.m_Speed / 1000000.0;
  1849. m_pBugReporter->SetCPU( (int)fFrequency );
  1850. m_pBugReporter->SetProcessor( pi.m_szProcessorID );
  1851. int nDxLevel = g_pMaterialSystemHardwareConfig->GetDXSupportLevel();
  1852. int vHigh = nDxLevel / 10;
  1853. int vLow = nDxLevel - vHigh * 10;
  1854. m_pBugReporter->SetDXVersion( vHigh, vLow, info.m_VendorID, info.m_DeviceID );
  1855. char osversion[ 128 ];
  1856. DisplaySystemVersion( osversion, sizeof( osversion ) );
  1857. m_pBugReporter->SetOSVersion( osversion );
  1858. m_pBugReporter->ResetIncludedFiles();
  1859. m_pBugReporter->SetZipAttachmentName( "" );
  1860. char fn[ 512 ];
  1861. if ( m_pBugReporter->IsPublicUI() )
  1862. {
  1863. bool attachedSave = false;
  1864. bool attachedScreenshot = false;
  1865. CUtlBuffer buginfo( 0, 0, CUtlBuffer::TEXT_BUFFER );
  1866. buginfo.Printf( "Title: %s\n", title );
  1867. buginfo.Printf( "Description: %s\n\n", desc );
  1868. buginfo.Printf( "Level: %s\n", level );
  1869. buginfo.Printf( "Position: %s\n", position );
  1870. buginfo.Printf( "Orientation: %s\n", orientation );
  1871. buginfo.Printf( "BuildNumber: %s\n", build );
  1872. buginfo.Printf( "DriverInfo: %s\n", driverinfo );
  1873. buginfo.Printf( "Misc: %s\n", (char const *)bufDescription.Base() );
  1874. buginfo.Printf( "Exe: %s\n", "hl2.exe" );
  1875. char gd[ 256 ];
  1876. Q_FileBase( com_gamedir, gd, sizeof( gd ) );
  1877. buginfo.Printf( "GameDirectory: %s\n", gd );
  1878. buginfo.Printf( "Ram: %lu\n", GetRam() );
  1879. buginfo.Printf( "CPU: %i\n", (int)fFrequency );
  1880. buginfo.Printf( "Processor: %s\n", pi.m_szProcessorID );
  1881. buginfo.Printf( "DXLevel: %d\n", nDxLevel );
  1882. buginfo.Printf( "OSVersion: %s\n", osversion );
  1883. // Terminate it
  1884. buginfo.PutChar( 0 );
  1885. int maxlen = buginfo.TellPut() * 2 + 1;
  1886. char *fixed = new char [ maxlen ];
  1887. Assert( fixed );
  1888. if ( fixed )
  1889. {
  1890. Q_memset( fixed, 0, maxlen );
  1891. char *i = (char *)buginfo.Base();
  1892. char *o = fixed;
  1893. while ( *i && ( o - fixed ) < maxlen - 1 )
  1894. {
  1895. if ( *i == '\n' )
  1896. {
  1897. *o++ = '\r';
  1898. }
  1899. *o++ = *i++;
  1900. }
  1901. *o = '\0';
  1902. AddBugTextToZip( "info.txt", fixed, Q_strlen( fixed ) + 1 );
  1903. delete[] fixed;
  1904. }
  1905. else
  1906. {
  1907. Sys_Error( "Unable to allocate %i bytes for bug description\n", maxlen );
  1908. }
  1909. // Only attach .sav files in single player
  1910. if ( ( GetBaseLocalClient().m_nMaxClients == 1 ) && m_szSaveGameName[ 0 ] )
  1911. {
  1912. Q_snprintf( fn, sizeof( fn ), "save/%s.sav", m_szSaveGameName );
  1913. Q_FixSlashes( fn );
  1914. attachedSave = AddFileToZip( fn );
  1915. }
  1916. if ( m_szScreenShotName[ 0 ] )
  1917. {
  1918. Q_snprintf( fn, sizeof( fn ), "screenshots/%s.jpg", m_szScreenShotName );
  1919. Q_FixSlashes( fn );
  1920. attachedScreenshot = AddFileToZip( fn );
  1921. }
  1922. // End users can only send save games and screenshots to valve
  1923. // Don't bother uploading any attachment if it's just the info.txt file, either...
  1924. if ( m_hZip && ( attachedSave || attachedScreenshot ) )
  1925. {
  1926. Assert( m_hZip );
  1927. void *mem = NULL;
  1928. unsigned long len;
  1929. ZipGetMemory( m_hZip, &mem, &len );
  1930. if ( mem != NULL
  1931. && len > 0 )
  1932. {
  1933. // Store .zip file
  1934. FileHandle_t fh = g_pFileSystem->Open( "bug.zip", "wb" );
  1935. if ( FILESYSTEM_INVALID_HANDLE != fh )
  1936. {
  1937. g_pFileSystem->Write( mem, len, fh );
  1938. g_pFileSystem->Close( fh );
  1939. m_pBugReporter->SetZipAttachmentName( "bug.zip" );
  1940. }
  1941. }
  1942. }
  1943. if ( m_hZip )
  1944. {
  1945. CloseZip( m_hZip );
  1946. m_hZip = (HZIP)0;
  1947. }
  1948. m_pBugReporter->SetSteamUserID( &m_SteamID, sizeof( m_SteamID ) );
  1949. }
  1950. else
  1951. {
  1952. // Notify other players that we've submitted a bug
  1953. if ( GetBaseLocalClient().IsActive() && GetBaseLocalClient().m_nMaxClients > 1 )
  1954. {
  1955. char buf[256];
  1956. Q_snprintf( buf, sizeof(buf), "say \"Bug Submitted [%s]: %s\"\n", assignedto, title );
  1957. Cbuf_AddText( Cbuf_GetCurrentPlayer(), buf, kCommandSrcCode, TIME_TO_TICKS(1.5) );
  1958. }
  1959. if ( m_szSaveGameName[ 0 ] )
  1960. {
  1961. Q_snprintf( fn, sizeof( fn ), "%s/BugId/%s.sav", GetRepositoryURL(), m_szSaveGameName );
  1962. Q_FixSlashes( fn );
  1963. m_pBugReporter->SetSaveGame( fn );
  1964. }
  1965. if ( m_szScreenShotName[ 0 ] )
  1966. {
  1967. Q_snprintf( fn, sizeof( fn ), "%s/BugId/%s.jpg", GetRepositoryURL(), m_szScreenShotName );
  1968. Q_FixSlashes( fn );
  1969. m_pBugReporter->SetScreenShot( fn );
  1970. }
  1971. if ( m_szBSPName[ 0 ] )
  1972. {
  1973. Q_snprintf( fn, sizeof( fn ), "%s/BugId/%s.bsp", GetRepositoryURL(), m_szBSPName );
  1974. Q_FixSlashes( fn );
  1975. m_pBugReporter->SetBSPName( fn );
  1976. }
  1977. if ( m_szVMFName[ 0 ] )
  1978. {
  1979. Q_snprintf( fn, sizeof( fn ), "%s/BugId/%s.vmf", GetRepositoryURL(), m_szVMFName );
  1980. Q_FixSlashes( fn );
  1981. m_pBugReporter->SetVMFName( fn );
  1982. }
  1983. if ( m_IncludedFiles.Count() > 0 )
  1984. {
  1985. for ( int i = 0; i < m_IncludedFiles.Count(); ++i )
  1986. {
  1987. Q_snprintf( fn, sizeof( fn ), "%s/BugId/%s", GetRepositoryURL(), m_IncludedFiles[ i ].fixedname );
  1988. Q_FixSlashes( fn );
  1989. m_pBugReporter->AddIncludedFile( fn );
  1990. }
  1991. }
  1992. }
  1993. if ( !m_bIsSubmittingRemoteBug )
  1994. {
  1995. Q_strncpy( m_szLevel, level, sizeof( m_szLevel ) );
  1996. }
  1997. if ( m_pBugReporter->IsPublicUI() )
  1998. {
  1999. m_pProgressDialog = new CBugReportUploadProgressDialog(NULL, "ProgressDialog", "#Steam_SubmittingBug_WorkingTitle", "#Steam_SubmittingBug_WorkingText" );
  2000. m_pProgressDialog->Activate();
  2001. vgui::input()->SetAppModalSurface(m_pProgressDialog->GetVPanel());
  2002. m_flPauseTime = (float)system()->GetFrameTime() + PUBLIC_BUGREPORT_WAIT_TIME;
  2003. }
  2004. else
  2005. {
  2006. OnFinishBugReport();
  2007. }
  2008. }
  2009. void CBugUIPanel::OnFinishBugReport()
  2010. {
  2011. int bugId = -1;
  2012. bool success = m_pBugReporter->CommitBugReport( bugId );
  2013. if ( success )
  2014. {
  2015. // The public UI handles uploading on it's own...
  2016. if ( !m_pBugReporter->IsPublicUI() )
  2017. {
  2018. if ( !UploadBugSubmission( m_szLevel, bugId, m_szSaveGameName, m_szScreenShotName, m_szBSPName, m_szVMFName, m_IncludedFiles ) )
  2019. {
  2020. Warning( "Unable to upload saved game and screenshot to bug repository!\n" );
  2021. success = false;
  2022. }
  2023. }
  2024. if ( CommandLine()->CheckParm( "-remotebug" ) )
  2025. {
  2026. // If we're using the remote bug reporter dll, respond to the
  2027. // bug requesting machine with the fileserver location of the bug info and files
  2028. g_ServerRemoteAccess.RemoteBug( m_pBugReporter->GetSubmissionURL() );
  2029. }
  2030. }
  2031. else
  2032. {
  2033. Warning( "Unable to post bug report to database\n" );
  2034. }
  2035. if ( !success )
  2036. {
  2037. // Play deny sound
  2038. DenySound();
  2039. m_bWaitForFinish = false;
  2040. }
  2041. else
  2042. {
  2043. // Close
  2044. WipeData();
  2045. SuccessSound( bugId );
  2046. if ( !m_pBugReporter->IsPublicUI() )
  2047. {
  2048. Close();
  2049. // This is for our internal bug stat tracking for clients.
  2050. // We only want to track client side bug submissions this way, wonder
  2051. ++m_BugSub;
  2052. }
  2053. }
  2054. }
  2055. void CBugUIPanel::PauseGame( bool bPause )
  2056. {
  2057. ACTIVE_SPLITSCREEN_PLAYER_GUARD( 0 );
  2058. // Don't pause the game or display 'paused by' text when submitting from a remote machine
  2059. if ( CommandLine()->CheckParm( "-remotebug" ) == NULL )
  2060. {
  2061. Cbuf_AddText( sv.IsActive() ? CBUF_SERVER : CBUF_FIRST_PLAYER, bPause ? "cmd bugpause\n" : "cmd bugunpause\n" );
  2062. }
  2063. }
  2064. void NonFileSystem_CreatePath (const char *path)
  2065. {
  2066. char temppath[512];
  2067. Q_strncpy( temppath, path, sizeof(temppath) );
  2068. for (char *ofs = temppath+1 ; *ofs ; ofs++)
  2069. {
  2070. if (*ofs == '/' || *ofs == '\\')
  2071. { // create the directory
  2072. char old = *ofs;
  2073. *ofs = 0;
  2074. #ifdef _PS3
  2075. mkdir( temppath, PS3_FS_NORMAL_PERMISSIONS );
  2076. #else
  2077. _mkdir (temppath);
  2078. #endif
  2079. *ofs = old;
  2080. }
  2081. }
  2082. }
  2083. bool CBugUIPanel::UploadFile( char const *local, char const *remote, bool bDeleteLocal )
  2084. {
  2085. Msg( "Uploading %s to %s\n", local, remote );
  2086. FileHandle_t hLocal = g_pFileSystem->Open( local, "rb" );
  2087. if ( FILESYSTEM_INVALID_HANDLE == hLocal )
  2088. {
  2089. Warning( "CBugUIPanel::UploadFile: Unable to open local path '%s'\n", local );
  2090. return false;
  2091. }
  2092. int nLocalFileSize = g_pFileSystem->Size( hLocal );
  2093. if ( nLocalFileSize <= 0 )
  2094. {
  2095. Warning( "CBugUIPanel::UploadFile: Local file has 0 size '%s'\n", local );
  2096. g_pFileSystem->Close( hLocal );
  2097. return false;
  2098. }
  2099. NonFileSystem_CreatePath( remote );
  2100. bool bResult;
  2101. if ( !g_pFileSystem->IsSteam() )
  2102. {
  2103. g_pFileSystem->Close( hLocal );
  2104. #ifdef WIN32
  2105. bResult = CopyFile( local, remote, false ) ? true : false;
  2106. #elif defined( _PS3 )
  2107. Warning( "PS3 is missing UploadFile implementation!\n" );
  2108. bResult = false;
  2109. #elif defined( LINUX )
  2110. Warning( "LINUX is missing UploadFile implementation!\n" );
  2111. bResult = false;
  2112. #elif POSIX
  2113. bResult = (0 == copyfile( local, remote, NULL, COPYFILE_ALL ));
  2114. #else
  2115. #error
  2116. #endif
  2117. }
  2118. else
  2119. {
  2120. FILE *r = fopen( va( "%s", remote ), "wb" );
  2121. if ( !r )
  2122. {
  2123. Warning( "CBugUIPanel::UploadFile: Unable to open remote path '%s'\n", remote );
  2124. g_pFileSystem->Close( hLocal );
  2125. return false;
  2126. }
  2127. int nCopyBufferSize = 2 * 1024 * 1024;
  2128. byte *pCopyBuf = new byte[ nCopyBufferSize ];
  2129. Assert( pCopyBuf );
  2130. if ( !pCopyBuf )
  2131. {
  2132. Warning( "CBugUIPanel::UploadFile: Unable to allocate copy buffer of %d bytes\n", nCopyBufferSize );
  2133. fclose( r );
  2134. g_pFileSystem->Close( hLocal );
  2135. return false;
  2136. }
  2137. int nRemainingBytes = nLocalFileSize;
  2138. while ( nRemainingBytes > 0 )
  2139. {
  2140. int nBytesToCopy = MIN( nRemainingBytes, nCopyBufferSize );
  2141. g_pFileSystem->Read( pCopyBuf, nBytesToCopy, hLocal );
  2142. fwrite( pCopyBuf, nBytesToCopy, 1, r );
  2143. nRemainingBytes -= nBytesToCopy;
  2144. }
  2145. fclose( r );
  2146. g_pFileSystem->Close( hLocal );
  2147. delete[] pCopyBuf;
  2148. bResult = true;
  2149. }
  2150. if ( !bResult )
  2151. {
  2152. Warning( "Failed to upload %s, error %d\n", local, GetLastError() );
  2153. }
  2154. else if ( bDeleteLocal )
  2155. {
  2156. _unlink( local );
  2157. }
  2158. return bResult;
  2159. }
  2160. CCallQueue g_UploadQueue;
  2161. bool CBugUIPanel::UploadBugSubmission( char const *levelname, int bugId, char const *savefile, char const *screenshot, char const *bsp, char const *vmf,
  2162. CUtlVector< includedfile >& files )
  2163. {
  2164. bool bret = true;
  2165. bool bAsync = bugreporter_uploadasync.GetBool();
  2166. char localfile[ 512 ];
  2167. char remotefile[ 512 ];
  2168. if ( savefile && savefile[ 0 ] )
  2169. {
  2170. Q_snprintf( localfile, sizeof( localfile ), "%s/save/%s.sav", com_gamedir, savefile );
  2171. Q_snprintf( remotefile, sizeof( remotefile ), "%s/%s.sav", GetSubmissionURL(bugId), savefile );
  2172. Q_FixSlashes( localfile );
  2173. Q_FixSlashes( remotefile );
  2174. g_UploadQueue.QueueCall( this, &CBugUIPanel::UploadFile, CUtlEnvelope<const char *>(localfile), CUtlEnvelope<const char *>(remotefile), false );
  2175. }
  2176. if ( screenshot && screenshot[ 0 ] )
  2177. {
  2178. Q_snprintf( localfile, sizeof( localfile ), "%s/screenshots/%s.jpg", com_gamedir, screenshot );
  2179. Q_snprintf( remotefile, sizeof( remotefile ), "%s/%s.jpg", GetSubmissionURL(bugId), screenshot );
  2180. Q_FixSlashes( localfile );
  2181. Q_FixSlashes( remotefile );
  2182. g_UploadQueue.QueueCall( this, &CBugUIPanel::UploadFile, CUtlEnvelope<const char *>(localfile), CUtlEnvelope<const char *>(remotefile), false );
  2183. }
  2184. if ( bsp && bsp[ 0 ] )
  2185. {
  2186. Q_snprintf( localfile, sizeof( localfile ), "maps/%s.bsp", levelname );
  2187. char *pszMapPath;
  2188. FileHandle_t hBsp = g_pFileSystem->OpenEx( localfile, "rb", 0, 0, &pszMapPath );
  2189. if ( !hBsp )
  2190. {
  2191. Q_snprintf( localfile, sizeof( localfile ), "%s/maps/%s.bsp", com_gamedir, levelname );
  2192. }
  2193. else
  2194. {
  2195. V_strncpy( localfile, pszMapPath, sizeof( localfile ) );
  2196. delete pszMapPath;
  2197. g_pFileSystem->Close( hBsp );
  2198. }
  2199. Q_snprintf( remotefile, sizeof( remotefile ), "%s/%s.bsp", GetSubmissionURL(bugId), bsp );
  2200. Q_FixSlashes( localfile );
  2201. Q_FixSlashes( remotefile );
  2202. g_UploadQueue.QueueCall( this, &CBugUIPanel::UploadFile, CUtlEnvelope<const char *>(localfile), CUtlEnvelope<const char *>(remotefile), false );
  2203. }
  2204. if ( vmf && vmf[ 0 ] )
  2205. {
  2206. Q_snprintf( localfile, sizeof( localfile ), "%s/%s.vmf", m_szVMFContentDirFullpath, levelname );
  2207. if ( g_pFileSystem->FileExists( localfile, NULL ) )
  2208. {
  2209. Q_snprintf( remotefile, sizeof( remotefile ), "%s/%s.vmf", GetSubmissionURL(bugId), vmf );
  2210. Q_FixSlashes( localfile );
  2211. Q_FixSlashes( remotefile );
  2212. g_UploadQueue.QueueCall( this, &CBugUIPanel::UploadFile, CUtlEnvelope<const char *>(localfile), CUtlEnvelope<const char *>(remotefile), false );
  2213. }
  2214. else
  2215. {
  2216. Msg( "Unable to locate .vmf file %s\n", localfile );
  2217. }
  2218. }
  2219. if ( files.Count() > 0 )
  2220. {
  2221. bAsync = false;
  2222. int c = files.Count();
  2223. for ( int i = 0 ; i < c; ++i )
  2224. {
  2225. Q_snprintf( localfile, sizeof( localfile ), "%s/%s", com_gamedir, files[ i ].name );
  2226. Q_snprintf( remotefile, sizeof( remotefile ), "%s/%s", GetSubmissionURL(bugId), files[ i ].fixedname );
  2227. Q_FixSlashes( localfile );
  2228. Q_FixSlashes( remotefile );
  2229. g_UploadQueue.QueueCall( this, &CBugUIPanel::UploadFile, CUtlEnvelope<const char *>(localfile), CUtlEnvelope<const char *>(remotefile), false );
  2230. }
  2231. }
  2232. if ( !bAsync )
  2233. {
  2234. g_UploadQueue.CallQueued();
  2235. }
  2236. else
  2237. {
  2238. ThreadExecuteSolo( "BugUpload", &g_UploadQueue, &CCallQueue::CallQueued );
  2239. }
  2240. return bret;
  2241. }
  2242. void CBugUIPanel::Close()
  2243. {
  2244. WipeData();
  2245. BaseClass::Close();
  2246. PauseGame( false );
  2247. EngineVGui()->HideGameUI();
  2248. }
  2249. void CBugUIPanel::OnCommand( char const *command )
  2250. {
  2251. if ( !Q_strcasecmp( command, "submit" ) )
  2252. {
  2253. OnSubmit();
  2254. }
  2255. else if ( !Q_strcasecmp( command, "cancel" ) )
  2256. {
  2257. Close();
  2258. WipeData();
  2259. }
  2260. else if ( !Q_strcasecmp( command, "snapshot" ) )
  2261. {
  2262. OnTakeSnapshot();
  2263. }
  2264. else if ( !Q_strcasecmp( command, "savegame" ) )
  2265. {
  2266. OnSaveGame();
  2267. //Adrian: We always want the BSP you used when saving the game.
  2268. //But only if you're not the public bug reporter!
  2269. if ( bugreporter_includebsp.GetBool() &&
  2270. m_pBugReporter->IsPublicUI() == false )
  2271. {
  2272. OnSaveBSP();
  2273. }
  2274. }
  2275. else if ( !Q_strcasecmp( command, "savebsp" ) )
  2276. {
  2277. OnSaveBSP();
  2278. }
  2279. else if ( !Q_strcasecmp( command, "savevmf" ) )
  2280. {
  2281. OnSaveVMF();
  2282. }
  2283. else if ( !Q_strcasecmp( command, "clearform" ) )
  2284. {
  2285. OnClearForm();
  2286. }
  2287. else if ( !Q_strcasecmp( command, "addfile" ) )
  2288. {
  2289. OnIncludeFile();
  2290. }
  2291. else if ( !Q_strcasecmp( command, "clearfiles" ) )
  2292. {
  2293. OnClearIncludedFiles();
  2294. }
  2295. else
  2296. {
  2297. BaseClass::OnCommand( command );
  2298. }
  2299. }
  2300. void CBugUIPanel::OnClearForm()
  2301. {
  2302. WipeData();
  2303. m_pTitle->SetText( "" );
  2304. m_pDescription->SetText( "" );
  2305. m_pAssignTo->ActivateItem( 0 );
  2306. m_pSeverity->ActivateItem( 0 );
  2307. m_pReportType->ActivateItem( 0 );
  2308. m_pPriority->ActivateItem( 2 );
  2309. m_pGameArea->ActivateItem( 0 );
  2310. m_pMapNumber->ActivateItem( 0 );
  2311. m_pSubmitter->ActivateItem( 0 );
  2312. }
  2313. void CBugUIPanel::DetermineSubmitterName()
  2314. {
  2315. if ( !m_pBugReporter )
  2316. return;
  2317. if ( m_pBugReporter->IsPublicUI() )
  2318. {
  2319. m_pSubmitter->SetText( "PublicUser" );
  2320. m_bCanSeeRepository = true;
  2321. m_bCanSubmit = true;
  2322. }
  2323. else
  2324. {
  2325. Color clr( 100, 200, 255, 255 );
  2326. //const char *pUserName = GetSubmitter();
  2327. char display[ 256 ] = { 0 };
  2328. m_pSubmitter->GetText( display, sizeof( display ) );
  2329. const char *pUserDisplayName = display;
  2330. char const *pUserName = m_pBugReporter->GetUserNameForDisplayName( display );
  2331. if ( pUserName && pUserName[0] && pUserDisplayName && pUserDisplayName[0] )
  2332. {
  2333. ConColorMsg( clr, "Username '%s' -- '%s'\n", pUserName, pUserDisplayName );
  2334. }
  2335. else
  2336. {
  2337. ConColorMsg( clr, "Failed to determine bug submission name.\n" );
  2338. m_bCanSubmit = false;
  2339. return;
  2340. }
  2341. // See if we can see the bug repository right now
  2342. char fn[ 512 ];
  2343. Q_snprintf( fn, sizeof( fn ), "%s/%s", GetRepositoryURL(), REPOSITORY_VALIDATION_FILE );
  2344. Q_FixSlashes( fn );
  2345. FILE *fp = fopen( fn, "rb" );
  2346. if ( fp )
  2347. {
  2348. ConColorMsg( clr, "Bug Repository '%s'\n", GetRepositoryURL() );
  2349. fclose( fp );
  2350. m_bCanSeeRepository = true;
  2351. }
  2352. else
  2353. {
  2354. Warning( "Unable to see '%s', check permissions and network connectivity\n", fn );
  2355. m_bCanSubmit = false;
  2356. }
  2357. // m_pSubmitter->SetText( pUserDisplayName );
  2358. }
  2359. }
  2360. void CBugUIPanel::PopulateControls()
  2361. {
  2362. if ( !m_pBugReporter )
  2363. return;
  2364. int i;
  2365. int defitem = -1;
  2366. char const *submitter = m_pBugReporter->GetUserName();
  2367. m_pSubmitter->DeleteAllItems();
  2368. int c = m_pBugReporter->GetDisplayNameCount();
  2369. for ( i = 0; i < c; i++ )
  2370. {
  2371. char const *name = m_pBugReporter->GetDisplayName( i );
  2372. char const *userName = m_pBugReporter->GetUserNameForDisplayName( name );
  2373. if (!V_strcasecmp( userName, submitter ))
  2374. defitem = i;
  2375. m_pSubmitter->AddItem( name, NULL );
  2376. }
  2377. m_pSubmitter->ActivateItem( defitem );
  2378. m_pSubmitter->SetText( m_pBugReporter->GetDisplayName( defitem ) );
  2379. c = m_pBugReporter->GetDisplayNameCount();
  2380. m_pAssignTo->DeleteAllItems();
  2381. defitem = -1;
  2382. for ( i = 0; i < c; i++ )
  2383. {
  2384. char const *name = m_pBugReporter->GetDisplayName( i );
  2385. char const *userName = m_pBugReporter->GetUserNameForDisplayName( name );
  2386. if (!V_strcasecmp( userName, submitter ))
  2387. defitem = i;
  2388. m_pAssignTo->AddItem(name , NULL );
  2389. }
  2390. m_pAssignTo->ActivateItem( defitem );
  2391. defitem = 0;
  2392. m_pSeverity->DeleteAllItems();
  2393. c = m_pBugReporter->GetSeverityCount();
  2394. for ( i = 0; i < c; i++ )
  2395. {
  2396. char const *severity = m_pBugReporter->GetSeverity( i );
  2397. if (!V_strcasecmp(severity, "Zero"))
  2398. defitem = i;
  2399. m_pSeverity->AddItem( severity, NULL );
  2400. }
  2401. m_pSeverity->ActivateItem( defitem );
  2402. m_pReportType->DeleteAllItems();
  2403. c = m_pBugReporter->GetReportTypeCount();
  2404. for ( i = 0; i < c; i++ )
  2405. {
  2406. m_pReportType->AddItem( m_pBugReporter->GetReportType( i ), NULL );
  2407. }
  2408. m_pReportType->ActivateItem( 0 );
  2409. m_pPriority->DeleteAllItems();
  2410. c = m_pBugReporter->GetPriorityCount();
  2411. for ( i = 0; i < c; i++ )
  2412. {
  2413. char const *priority = m_pBugReporter->GetPriority( i );
  2414. if (!V_strcasecmp(priority, "None"))
  2415. defitem = i;
  2416. m_pPriority->AddItem( priority, NULL );
  2417. }
  2418. m_pPriority->ActivateItem( defitem );
  2419. m_pGameArea->DeleteAllItems();
  2420. c = m_pBugReporter->GetAreaCount();
  2421. for ( i = 0; i < c; i++ )
  2422. {
  2423. m_pGameArea->AddItem( m_pBugReporter->GetArea( i ), NULL );
  2424. }
  2425. int area_index = GetArea();
  2426. m_pGameArea->ActivateItem( area_index );
  2427. }
  2428. // Evil hack shim to expose convar to bugreporter_filequeue
  2429. class CBugReporterDefaultUsername : public IBugReporterDefaultUsername
  2430. {
  2431. public:
  2432. virtual char const *GetDefaultUsername() const
  2433. {
  2434. return bugreporter_username.GetString();
  2435. }
  2436. };
  2437. static CBugReporterDefaultUsername g_ExposeBugreporterUsername;
  2438. EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CBugReporterDefaultUsername,IBugReporterDefaultUsername, INTERFACEVERSION_BUGREPORTER_DEFAULT_USER_NAME, g_ExposeBugreporterUsername );
  2439. char const *CBugUIPanel::GetSubmitter()
  2440. {
  2441. if ( !m_bCanSubmit )
  2442. return "";
  2443. const char *pUsername = bugreporter_username.GetString();
  2444. if ( 0 == pUsername[0] )
  2445. {
  2446. char submitter[ 256 ];
  2447. m_pSubmitter->GetText( submitter, sizeof( submitter ) );
  2448. pUsername = m_pBugReporter->GetUserNameForDisplayName( submitter );
  2449. if ( 0 == pUsername[0] )
  2450. {
  2451. if ( m_bIsPublic )
  2452. {
  2453. if ( Steam3Client().SteamUser() )
  2454. {
  2455. pUsername = Steam3Client().SteamUser()->GetSteamID().Render();
  2456. }
  2457. else
  2458. {
  2459. pUsername = "PublicUser";
  2460. }
  2461. }
  2462. if ( 0 == pUsername[ 0 ] )
  2463. {
  2464. Color clr( 255, 50, 50, 255 );
  2465. ConColorMsg( clr, "Can't determine username. Please set email address with bugreporter_username ConVar and run _bugreporter_restart autoselect\n" );
  2466. }
  2467. }
  2468. }
  2469. return pUsername;
  2470. }
  2471. //-----------------------------------------------------------------------------
  2472. // Purpose:
  2473. //-----------------------------------------------------------------------------
  2474. void CBugUIPanel::DenySound()
  2475. {
  2476. Cbuf_AddText( Cbuf_GetCurrentPlayer(), va( "play %s\n", DENY_SOUND ) );
  2477. }
  2478. //-----------------------------------------------------------------------------
  2479. // Purpose:
  2480. //-----------------------------------------------------------------------------
  2481. void CBugUIPanel::SuccessSound( int bugId )
  2482. {
  2483. Color clr( 50, 255, 100, 255 );
  2484. if ( m_pBugReporter && m_pBugReporter->IsPublicUI() )
  2485. {
  2486. ConColorMsg( clr, "Bug submission succeeded\n" );
  2487. }
  2488. else
  2489. {
  2490. ConColorMsg( clr, "Bug submission succeeded for bug (%i)\n", bugId );
  2491. }
  2492. Cbuf_AddText( Cbuf_GetCurrentPlayer(), va( "play %s\n", SUCCEED_SOUND ) );
  2493. }
  2494. void CBugUIPanel::OnKeyCodeTyped(KeyCode code)
  2495. {
  2496. if ( code == KEY_ESCAPE )
  2497. {
  2498. Close();
  2499. WipeData();
  2500. }
  2501. else
  2502. {
  2503. BaseClass::OnKeyCodeTyped( code );
  2504. }
  2505. }
  2506. // Load game-specific bug reporter defaults as params
  2507. void CBugUIPanel::ParseDefaultParams( void )
  2508. {
  2509. CUtlBuffer buffer( 0, 0, CUtlBuffer::TEXT_BUFFER );
  2510. if ( !g_pFileSystem->ReadFile( "scripts/bugreporter_defaults.txt", NULL, buffer ) )
  2511. {
  2512. return;
  2513. }
  2514. char token[256];
  2515. const char *pfile = COM_ParseFile( (const char *)buffer.Base(), token, sizeof( token ) );
  2516. while ( pfile )
  2517. {
  2518. bool success = AutoFillToken( token, false );
  2519. if ( !success )
  2520. {
  2521. // Try partials
  2522. success = AutoFillToken( token, true );
  2523. if ( !success )
  2524. {
  2525. Msg( "Unable to determine where to set default bug parameter '%s', ignoring...\n", token );
  2526. }
  2527. }
  2528. pfile = COM_ParseFile( pfile, token, sizeof( token ) );
  2529. }
  2530. }
  2531. void CBugUIPanel::ParseCommands( const CCommand &args )
  2532. {
  2533. if ( !m_bCanSubmit )
  2534. return;
  2535. for (int i = 1; i < args.ArgC(); i++)
  2536. {
  2537. const char *token = args[i];
  2538. if (!V_stricmp("-title", token))
  2539. {
  2540. if (i+1 < args.ArgC())
  2541. {
  2542. m_pTitle->SetText(args[i+1]);
  2543. m_pDescription->SetText(args[i+1]);
  2544. i++;
  2545. }
  2546. }
  2547. else if (!V_stricmp("-auto", token))
  2548. {
  2549. m_bAutoSubmit = true;
  2550. }
  2551. else if ( !V_stricmp( "-remotebugpath", token ) )
  2552. {
  2553. if (i+1 < args.ArgC())
  2554. {
  2555. // This param should only be specified
  2556. // when we receive a remote bug response from
  2557. // the rcon server, and the path should lead to a
  2558. // fileserver location with bug.txt
  2559. m_strRemoteBugInfoPath = args[i+1];
  2560. m_bIsSubmittingRemoteBug = true;
  2561. i++;
  2562. }
  2563. }
  2564. else
  2565. {
  2566. bool success = AutoFillToken( token, false );
  2567. if ( !success )
  2568. {
  2569. // Try partials
  2570. success = AutoFillToken( token, true );
  2571. if ( !success )
  2572. {
  2573. Msg( "Unable to determine where to set default bug parameter '%s', ignoring...\n", token );
  2574. }
  2575. }
  2576. }
  2577. }
  2578. }
  2579. bool CBugUIPanel::Compare( char const *value, char const *token, bool partial )
  2580. {
  2581. if ( !partial )
  2582. {
  2583. if ( !Q_stricmp( value, token ) )
  2584. return true;
  2585. }
  2586. else
  2587. {
  2588. if ( Q_stristr( value, token ) )
  2589. {
  2590. return true;
  2591. }
  2592. }
  2593. return false;
  2594. }
  2595. bool CBugUIPanel::AutoFillToken( char const *token, bool partial )
  2596. {
  2597. // Search for token in all dropdown lists and fill it in if we find it
  2598. if ( !m_pBugReporter )
  2599. return true;
  2600. int i;
  2601. int c;
  2602. Msg("AUTOFILL: %s (%s)\n", token, partial ? "PARTIAL" : "FULL");
  2603. c = m_pBugReporter->GetDisplayNameCount();
  2604. for ( i = 0; i < c; i++ )
  2605. {
  2606. const char *dispName = m_pBugReporter->GetDisplayName( i );
  2607. const char *userName = m_pBugReporter->GetUserNameForDisplayName( dispName );
  2608. if (Compare( userName, token, partial ) || Compare( dispName, token, partial ) )
  2609. {
  2610. Msg(" ASSIGNED TO: %s\n", userName);
  2611. m_pAssignTo->ActivateItem( i );
  2612. return true;
  2613. }
  2614. }
  2615. c = m_pBugReporter->GetSeverityCount();
  2616. for ( i = 0; i < c; i++ )
  2617. {
  2618. if ( Compare( m_pBugReporter->GetSeverity( i ), token, partial ) )
  2619. {
  2620. Msg(" SEVERITY: %s\n", m_pBugReporter->GetSeverity( i ));
  2621. m_pSeverity->ActivateItem( i );
  2622. return true;
  2623. }
  2624. }
  2625. c = m_pBugReporter->GetReportTypeCount();
  2626. for ( i = 0; i < c; i++ )
  2627. {
  2628. if ( Compare( m_pBugReporter->GetReportType( i ), token, partial ) )
  2629. {
  2630. Msg(" REPORT TYPE: %s\n", m_pBugReporter->GetReportType( i ));
  2631. m_pReportType->ActivateItem( i );
  2632. return true;
  2633. }
  2634. }
  2635. c = m_pBugReporter->GetPriorityCount();
  2636. for ( i = 0; i < c; i++ )
  2637. {
  2638. if ( Compare( m_pBugReporter->GetPriority( i ), token, partial ) )
  2639. {
  2640. Msg(" PRIORITY: %s\n", m_pBugReporter->GetPriority( i ));
  2641. m_pPriority->ActivateItem( i );
  2642. return true;
  2643. }
  2644. }
  2645. c = m_pBugReporter->GetAreaCount();
  2646. for ( i = 0; i < c; i++ )
  2647. {
  2648. if ( Compare( m_pBugReporter->GetArea( i ), token, partial ) )
  2649. {
  2650. Msg(" AREA: %s\n", m_pBugReporter->GetArea( i ));
  2651. m_pGameArea->ActivateItem( i );
  2652. return true;
  2653. }
  2654. }
  2655. if ( !Q_stricmp( token, "screenshot" ) )
  2656. {
  2657. m_fAutoAddScreenshot = eAutoAddScreenshot_Add;
  2658. return true;
  2659. }
  2660. if ( !Q_stricmp( token, "noscreenshot" ) )
  2661. {
  2662. m_fAutoAddScreenshot = eAutoAddScreenshot_DontAdd;
  2663. return true;
  2664. }
  2665. return false;
  2666. }
  2667. void CBugUIPanel::CheckContinueQueryingSteamForCSERList()
  2668. {
  2669. if ( !m_bQueryingSteamForCSER || !Steam3Client().SteamUtils() )
  2670. {
  2671. return;
  2672. }
  2673. uint32 unIP;
  2674. uint16 usPort;
  2675. Steam3Client().SteamUtils()->GetCSERIPPort( &unIP, &usPort );
  2676. if ( unIP )
  2677. {
  2678. m_cserIP.SetIPAndPort( unIP, usPort );
  2679. m_bQueryingSteamForCSER = false;
  2680. }
  2681. }
  2682. // Get the bug submission count. Called every map transition
  2683. int CBugUIPanel::GetBugSubmissionCount() const
  2684. {
  2685. return m_BugSub;
  2686. }
  2687. // Clear the bug submission count. Called every map transition
  2688. void CBugUIPanel::ClearBugSubmissionCount()
  2689. {
  2690. m_BugSub = 0;
  2691. }
  2692. //-----------------------------------------------------------------------------
  2693. // Read a partial bug report kv file from fileserver, populate the appropriate fields in this bug report
  2694. // This is intended for internal use only during viper playtests.
  2695. //-----------------------------------------------------------------------------
  2696. bool CBugUIPanel::CopyInfoFromRemoteBug()
  2697. {
  2698. Assert( m_pBugReporter );
  2699. if ( !m_pBugReporter )
  2700. return false;
  2701. Assert ( m_bIsSubmittingRemoteBug );
  2702. if ( !m_bIsSubmittingRemoteBug )
  2703. return false;
  2704. KeyValues *pKV = new KeyValues ( "Bug" );
  2705. if ( !pKV->LoadFromFile( g_pFileSystem, m_strRemoteBugInfoPath + "\\bug.txt") )
  2706. {
  2707. Warning( "Failed to parse remote bug KV file at path: '%s'", m_strRemoteBugInfoPath.Get() );
  2708. pKV->deleteThis();
  2709. Assert( 0 );
  2710. return false;
  2711. }
  2712. // Stomp fields that we need to keep matching the remote machine's values
  2713. const char* pLevelName = pKV->GetString( "level" );
  2714. m_pBugReporter->SetLevel( pLevelName );
  2715. V_strncpy( m_szLevel, pLevelName, sizeof ( m_szLevel ) );
  2716. m_pBugReporter->SetBuildNumber( pKV->GetString( "Build" ) );
  2717. m_pBugReporter->SetPosition( pKV->GetString( "Position" ) );
  2718. m_pBugReporter->SetOrientation( pKV->GetString( "Orientation" ) );
  2719. m_pBugReporter->SetMiscInfo( pKV->GetString( "Misc" ) );
  2720. m_pBugReporter->SetConsoleHistory( pKV->GetString( "Console" ) );
  2721. m_pBugReporter->SetDriverInfo( pKV->GetString( "DriverInfo" ) );
  2722. #if 0
  2723. // BUG: Savegames crash on load after this copy step. Not including them for now, if we use this feature
  2724. // a lot then we can revisit this and fix it.
  2725. CUtlString strSaveName = pKV->GetString( "Savegame" );
  2726. if ( strSaveName.IsEmpty() == false )
  2727. {
  2728. char buffer[MAX_PATH];
  2729. V_StripExtension( strSaveName.UnqualifiedFilename(), m_szSaveGameName, sizeof ( m_szSaveGameName ) );
  2730. V_snprintf( buffer, sizeof( buffer ), "%s/save/%s.sav", com_gamedir, m_szSaveGameName );
  2731. CUtlString strSavePath = m_strRemoteBugInfoPath + "\\" + m_szSaveGameName + ".sav";
  2732. UploadFile( strSavePath, buffer, true );
  2733. V_snprintf( buffer, sizeof( buffer ), "%s/BugId/%s.sav", GetRepositoryURL(), m_szSaveGameName );
  2734. V_FixSlashes( buffer );
  2735. m_pBugReporter->SetSaveGame( buffer );
  2736. }
  2737. CUtlString strBSPName = pKV->GetString( "Bspname" );
  2738. if ( strBSPName.IsEmpty() == false )
  2739. {
  2740. char buffer[MAX_PATH];
  2741. V_StripExtension( strBSPName.UnqualifiedFilename(), m_szBSPName, sizeof ( m_szBSPName ) );
  2742. V_snprintf( buffer, sizeof( buffer ), "%s/maps/%s.bsp", com_gamedir, m_szBSPName );
  2743. CUtlString strBSPPath = m_strRemoteBugInfoPath + "\\" + m_szBSPName + ".bsp";
  2744. UploadFile( strBSPPath, buffer, true );
  2745. V_snprintf( buffer, sizeof( buffer ), "%s/BugId/%s.bsp", GetRepositoryURL(), m_szBSPName );
  2746. V_FixSlashes( buffer );
  2747. m_pBugReporter->SetBSPName( buffer );
  2748. }
  2749. #endif
  2750. CUtlString strSSName = pKV->GetString( "Screenshot" );
  2751. if ( strSSName.IsEmpty() == false )
  2752. {
  2753. char buffer[MAX_PATH];
  2754. V_StripExtension( strSSName.UnqualifiedFilename(), m_szScreenShotName, sizeof ( m_szScreenShotName ) );
  2755. V_snprintf( buffer, sizeof( buffer ), "%s/screenshots/%s.jpg", com_gamedir, m_szScreenShotName );
  2756. CUtlString strSSPath = m_strRemoteBugInfoPath + "\\" + m_szScreenShotName + ".jpg";
  2757. UploadFile( strSSPath, buffer, true );
  2758. V_snprintf( buffer, sizeof( buffer ), "%s/BugId/%s.jpg", GetRepositoryURL(), m_szScreenShotName );
  2759. V_FixSlashes( buffer );
  2760. m_pBugReporter->SetScreenShot( buffer );
  2761. }
  2762. char localBugConsole[MAX_PATH];
  2763. Q_snprintf( localBugConsole, sizeof( localBugConsole ), "%s/bugconsole.txt", com_gamedir );
  2764. CUtlString strBugConsolePath = m_strRemoteBugInfoPath + "\\bugconsole.txt";
  2765. UploadFile( strBugConsolePath, localBugConsole, true );
  2766. OnFileSelected( localBugConsole );
  2767. pKV->deleteThis();
  2768. // Remove the bug.txt file and directory
  2769. _unlink( m_strRemoteBugInfoPath + "\\bug.txt" );
  2770. #if defined(_PS3) || defined(POSIX)
  2771. rmdir( m_strRemoteBugInfoPath );
  2772. #else
  2773. _rmdir( m_strRemoteBugInfoPath );
  2774. #endif
  2775. return true;
  2776. }
  2777. static CBugUIPanel *g_pBugUI = NULL;
  2778. class CEngineBugReporter : public IEngineBugReporter
  2779. {
  2780. public:
  2781. virtual void Init( void );
  2782. virtual void Shutdown( void );
  2783. virtual void InstallBugReportingUI( vgui::Panel *parent, IEngineBugReporter::BR_TYPE type );
  2784. virtual bool ShouldPause() const;
  2785. virtual bool IsVisible() const; //< true iff the bug panel is active and on screen right now
  2786. // Methods to get bug count for internal dev work stat tracking.
  2787. // Will get the bug count and clear it every map transition
  2788. virtual int GetBugSubmissionCount() const;
  2789. virtual void ClearBugSubmissionCount();
  2790. void Restart( IEngineBugReporter::BR_TYPE type );
  2791. private:
  2792. PHandle m_ParentPanel;
  2793. };
  2794. static CEngineBugReporter g_BugReporter;
  2795. IEngineBugReporter *bugreporter = &g_BugReporter;
  2796. CON_COMMAND( _bugreporter_restart, "Restarts bug reporter .dll" )
  2797. {
  2798. if ( args.ArgC() <= 1 )
  2799. {
  2800. Msg( "__bugreporter_restart <internal | external | autoselect>\n" );
  2801. return;
  2802. }
  2803. IEngineBugReporter::BR_TYPE type = IEngineBugReporter::BR_PUBLIC;
  2804. if ( !Q_stricmp( args.Arg( 1 ), "internal" ) )
  2805. {
  2806. type = IEngineBugReporter::BR_INTERNAL;
  2807. }
  2808. else if ( !Q_stricmp( args.Arg( 1 ), "autoselect" ) )
  2809. {
  2810. type = IEngineBugReporter::BR_AUTOSELECT;
  2811. }
  2812. g_BugReporter.Restart( type );
  2813. }
  2814. void CEngineBugReporter::Init( void )
  2815. {
  2816. m_ParentPanel = 0;
  2817. }
  2818. void CEngineBugReporter::Shutdown( void )
  2819. {
  2820. if ( g_pBugUI )
  2821. {
  2822. g_pBugUI->Shutdown();
  2823. g_pBugUI = NULL;
  2824. }
  2825. }
  2826. void CEngineBugReporter::InstallBugReportingUI( vgui::Panel *parent, IEngineBugReporter::BR_TYPE type )
  2827. {
  2828. if ( g_pBugUI )
  2829. return;
  2830. char fn[ 512 ];
  2831. #ifdef OSX
  2832. Q_snprintf( fn, sizeof( fn ), "%s.dylib", GetInternalBugReporterDLL() );
  2833. #else
  2834. Q_snprintf( fn, sizeof( fn ), "%s.dll", GetInternalBugReporterDLL() );
  2835. #endif
  2836. const bool bCouldUseInternal = g_pFileSystem->FileExists( fn, "EXECUTABLE_PATH" );
  2837. bool bUsePublic = true;
  2838. if ( bCouldUseInternal )
  2839. {
  2840. switch ( type )
  2841. {
  2842. case IEngineBugReporter::BR_PUBLIC:
  2843. // Do nothing
  2844. break;
  2845. default:
  2846. case IEngineBugReporter::BR_AUTOSELECT:
  2847. {
  2848. #if !defined( _X360 )
  2849. bUsePublic = ( k_EUniversePublic == GetSteamUniverse() );
  2850. #else
  2851. bUsePublic = false;
  2852. #endif
  2853. }
  2854. break;
  2855. case IEngineBugReporter::BR_INTERNAL:
  2856. {
  2857. bUsePublic = false;
  2858. }
  2859. break;
  2860. }
  2861. }
  2862. if (! bUsePublic )
  2863. {
  2864. // Default internal bug reporter to async upload
  2865. bugreporter_uploadasync.SetValue(1);
  2866. }
  2867. g_pBugUI = new CBugUIPanel( bUsePublic, parent );
  2868. Assert( g_pBugUI );
  2869. m_ParentPanel = parent;
  2870. }
  2871. void CEngineBugReporter::Restart( IEngineBugReporter::BR_TYPE type )
  2872. {
  2873. Shutdown();
  2874. Msg( "Changing to bugreporter(%s)\n",
  2875. ( type == IEngineBugReporter::BR_AUTOSELECT ) ?
  2876. ( "autoselect" ) :
  2877. ( ( type == IEngineBugReporter::BR_PUBLIC ) ?
  2878. "public" :
  2879. "valve" ) );
  2880. InstallBugReportingUI( m_ParentPanel, type );
  2881. }
  2882. bool CEngineBugReporter::ShouldPause() const
  2883. {
  2884. return g_pBugUI && ( g_pBugUI->IsVisible() || g_pBugUI->IsTakingSnapshot() ) && (GetBaseLocalClient().m_nMaxClients == 1);
  2885. }
  2886. bool CEngineBugReporter::IsVisible() const
  2887. {
  2888. return g_pBugUI && g_pBugUI->IsVisible();
  2889. }
  2890. // return the number of bugs submitted so far. This is reset every map transition
  2891. // and is used to track the number of bugs submitted during development for the
  2892. // game stat system
  2893. int CEngineBugReporter::GetBugSubmissionCount() const
  2894. {
  2895. unsigned int bugCnt = 0;
  2896. if ( g_pBugUI )
  2897. {
  2898. bugCnt = g_pBugUI->GetBugSubmissionCount();
  2899. }
  2900. return bugCnt;
  2901. }
  2902. // clears the number of bugs submitted. This is called every map transition since
  2903. // we are interested in the number of bugs submitted per map for internal stat tracking
  2904. void CEngineBugReporter::ClearBugSubmissionCount()
  2905. {
  2906. if ( g_pBugUI )
  2907. {
  2908. g_pBugUI->ClearBugSubmissionCount();
  2909. }
  2910. }
  2911. CON_COMMAND_F( bug, "Show the bug reporting UI.", FCVAR_DONTRECORD )
  2912. {
  2913. #if defined( _X360 )
  2914. XBX_rBugReporter();
  2915. return;
  2916. #elif defined( _PS3 )
  2917. if ( g_pValvePS3Console )
  2918. {
  2919. g_pValvePS3Console->BugReporter();
  2920. }
  2921. return;
  2922. #endif
  2923. if ( !g_pBugUI )
  2924. return;
  2925. // Make sure bug ui is closed otherwise the snapshot code will not work
  2926. bool bWasVisible = g_pBugUI->IsVisible();
  2927. if ( bWasVisible )
  2928. {
  2929. // already open, close for reset
  2930. g_pBugUI->Close();
  2931. }
  2932. g_pBugUI->Activate();
  2933. g_pBugUI->ParseDefaultParams();
  2934. g_pBugUI->ParseCommands( args );
  2935. // Force certain behavior when using the remote bugreporter dll
  2936. if ( CommandLine()->CheckParm( "-remotebug" ) )
  2937. {
  2938. g_pBugUI->InitAsRemoteBug();
  2939. }
  2940. }
  2941. int CBugUIPanel::GetArea()
  2942. {
  2943. char mapname[256] = "";
  2944. int iNewTitleLength = 80;
  2945. if ( host_state.worldmodel )
  2946. {
  2947. CL_SetupMapName( modelloader->GetName( host_state.worldmodel ), mapname, sizeof( mapname ) );
  2948. iNewTitleLength = (80 - (strlen( mapname )+2));
  2949. }
  2950. m_pTitle->SetMaximumCharCount( iNewTitleLength );
  2951. char *gamedir = com_gamedir;
  2952. gamedir = Q_strrchr( gamedir, CORRECT_PATH_SEPARATOR ) + 1;
  2953. for ( int i = 0; i < m_pBugReporter->GetAreaMapCount(); i++ )
  2954. {
  2955. char szAreaMap[MAX_PATH];
  2956. V_strcpy_safe( szAreaMap, m_pBugReporter->GetAreaMap( i ) );
  2957. char *pszAreaDir = Q_strrchr( szAreaMap, '@' );
  2958. char *pszAreaPrefix = Q_strrchr( szAreaMap, '%' );
  2959. int iDirLength = 0;
  2960. if ( pszAreaDir && pszAreaPrefix )
  2961. {
  2962. iDirLength = pszAreaPrefix - pszAreaDir - 1;
  2963. pszAreaDir++;
  2964. pszAreaPrefix++;
  2965. }
  2966. else if ( pszAreaDir && !pszAreaPrefix )
  2967. {
  2968. pszAreaDir++;
  2969. iDirLength = Q_strlen( szAreaMap ) - (pszAreaDir - szAreaMap);
  2970. }
  2971. else
  2972. {
  2973. return 0;
  2974. }
  2975. char szDirectory[MAX_PATH];
  2976. Q_memmove( szDirectory, pszAreaDir, iDirLength );
  2977. szDirectory[iDirLength] = 0;
  2978. if ( pszAreaDir && pszAreaPrefix )
  2979. {
  2980. if ( !Q_strcmp( szDirectory, gamedir)
  2981. && mapname && Q_strstr( mapname, pszAreaPrefix ) )
  2982. {
  2983. return i;
  2984. }
  2985. }
  2986. else if ( pszAreaDir && !pszAreaPrefix )
  2987. {
  2988. if ( !Q_strcmp( szDirectory, gamedir ) )
  2989. {
  2990. return i;
  2991. }
  2992. }
  2993. }
  2994. return 0;
  2995. }
  2996. void CBugUIPanel::GetConsoleHistory( CUtlBuffer &buf ) const
  2997. {
  2998. int nCount = g_pCVar->GetConsoleDisplayFuncCount();
  2999. if ( nCount <= 0 )
  3000. {
  3001. buf.PutChar( 0 );
  3002. return;
  3003. }
  3004. // 1 Mb max
  3005. int nMaxConsoleHistory = 1024 * 1024;
  3006. int nCur = buf.TellPut();
  3007. int nNeeded = nCur + nMaxConsoleHistory;
  3008. buf.EnsureCapacity( nNeeded );
  3009. g_pCVar->GetConsoleText( 0, (char *)buf.Base() + nCur, nMaxConsoleHistory );
  3010. int nActual = Q_strlen( (char *)buf.Base() + nCur ) + 1;
  3011. buf.SeekPut( CUtlBuffer::SEEK_HEAD, nCur + nActual );
  3012. }
  3013. void CBugUIPanel::InitAsRemoteBug()
  3014. {
  3015. Assert( CommandLine()->CheckParm( "-remotebug" ) );
  3016. // always autosubmit when bugging from a remote machine
  3017. m_bAutoSubmit = true;
  3018. #if 0 // BUG: The save games come across broken. Leaving alone
  3019. // for now but will fix if we make use of this feature
  3020. // always take a save game if singleplayer
  3021. if ( GetBaseLocalClient().m_nMaxClients == 1 )
  3022. {
  3023. OnSaveGame();
  3024. OnSaveBSP();
  3025. }
  3026. #endif
  3027. }