summaryrefslogtreecommitdiff
path: root/engine/bugreporter.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /engine/bugreporter.cpp
downloadarchived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.tar.xz
archived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.zip
Diffstat (limited to 'engine/bugreporter.cpp')
-rw-r--r--engine/bugreporter.cpp3081
1 files changed, 3081 insertions, 0 deletions
diff --git a/engine/bugreporter.cpp b/engine/bugreporter.cpp
new file mode 100644
index 0000000..093041e
--- /dev/null
+++ b/engine/bugreporter.cpp
@@ -0,0 +1,3081 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+
+#undef PROTECT_FILEIO_FUNCTIONS
+#ifndef _LINUX
+#undef fopen
+#endif
+#if defined( WIN32 ) && !defined( _X360 )
+#include "winlite.h"
+#include <winsock2.h> // INADDR_ANY defn
+#include <direct.h>
+#elif defined(POSIX)
+#include <sys/stat.h>
+
+#ifdef OSX
+#include <copyfile.h>
+#import <mach/mach_host.h>
+#import <sys/sysctl.h>
+#elif defined(LINUX)
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#endif
+#define GetLastError() errno
+#elif defined( _X360 )
+#else
+#error
+#endif
+
+#include <time.h>
+
+#include "client.h"
+#include <vgui_controls/Frame.h>
+#include <vgui/ISystem.h>
+#include <vgui/ISurface.h>
+#include <vgui/IInput.h>
+#include <vgui/IVGui.h>
+#include <KeyValues.h>
+#include <vgui_controls/BuildGroup.h>
+#include <vgui_controls/Tooltip.h>
+#include <vgui_controls/TextImage.h>
+#include <vgui_controls/CheckButton.h>
+#include <vgui_controls/Label.h>
+#include <vgui_controls/PropertySheet.h>
+#include <vgui_controls/FileOpenDialog.h>
+#include "vgui_controls/DirectorySelectDialog.h"
+#include <vgui_controls/ProgressBar.h>
+#include <vgui_controls/Slider.h>
+#include <vgui_controls/ComboBox.h>
+#include <vgui_controls/Controls.h>
+#include <vgui_controls/TextEntry.h>
+#include "enginebugreporter.h"
+#include "vgui_baseui_interface.h"
+#include <vgui_controls/FileOpenDialog.h>
+#include "ivideomode.h"
+#include "cl_main.h"
+#include "gl_model_private.h"
+#include "tier2/tier2.h"
+#include "tier1/utlstring.h"
+#include "tier1/callqueue.h"
+#include "vstdlib/jobthread.h"
+
+#include "utlsymbol.h"
+#include "utldict.h"
+#include "filesystem.h"
+#include "filesystem_engine.h"
+#include "icliententitylist.h"
+#include "bugreporter/bugreporter.h"
+#include "icliententity.h"
+#include "tier0/vcrmode.h"
+#include "tier0/platform.h"
+#include "net.h"
+#include "host_phonehome.h"
+#include "tier0/icommandline.h"
+#include "stdstring.h"
+#include "sv_main.h"
+#include "server.h"
+#include "eiface.h"
+#include "gl_matsysiface.h"
+#include "materialsystem/imaterialsystemhardwareconfig.h"
+#include "FindSteamServers.h"
+#include "vstdlib/random.h"
+#ifndef SWDS
+#include "cl_steamauth.h"
+#endif
+
+#include "zip/XZip.h"
+
+#if defined( _X360 )
+#include "xbox/xbox_win32stubs.h"
+#endif
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+#define DENY_SOUND "common/bugreporter_failed"
+#define SUCCEED_SOUND "common/bugreporter_succeeded"
+
+// Fixme, move these to buguiddata.res script file?
+#ifdef WIN32
+#define BUG_REPOSITORY_URL "\\\\fileserver\\bugs"
+#elif defined(OSX)
+#define BUG_REPOSITORY_URL "/Volumes/bugs"
+#elif defined(LINUX)
+#define BUG_REPOSITORY_URL "\\\\fileserver\\bugs"
+#else
+//#error
+#endif
+#define REPOSITORY_VALIDATION_FILE "info.txt"
+
+#define BUG_REPORTER_DLLNAME "bugreporter_filequeue"
+#define BUG_REPORTER_PUBLIC_DLLNAME "bugreporter_public"
+
+#if defined( _DEBUG )
+#define PUBLIC_BUGREPORT_WAIT_TIME 3
+#else
+#define PUBLIC_BUGREPORT_WAIT_TIME 15
+#endif
+
+// 16Mb max zipped size
+#define MAX_ZIP_SIZE (1024 * 1024 * 16 )
+
+extern ConVar skill;
+extern float g_fFramesPerSecond;
+
+static ConVar bugreporter_includebsp( "bugreporter_includebsp", "1", 0, "Include .bsp for internal bug submissions." );
+static ConVar bugreporter_uploadasync( "bugreporter_uploadasync", "0", FCVAR_ARCHIVE, "Upload attachments asynchronously" );
+
+
+using namespace vgui;
+
+unsigned long GetRam()
+{
+#ifdef WIN32
+ MEMORYSTATUS stat;
+ GlobalMemoryStatus( &stat );
+ return (stat.dwTotalPhys / (1024 * 1024));
+#elif defined(OSX)
+ int mib[2] = { CTL_HW, HW_MEMSIZE };
+ u_int namelen = sizeof(mib) / sizeof(mib[0]);
+ uint64_t memsize;
+ size_t len = sizeof(memsize);
+
+ if (sysctl(mib, namelen, &memsize, &len, NULL, 0) >= 0)
+ {
+ return memsize / (1024*1024);
+ }
+ else
+ return 0;
+#elif defined( LINUX )
+ unsigned long Ram = 0;
+ FILE *fh = fopen( "/proc/meminfo", "r" );
+ if( fh )
+ {
+ char buf[ 256 ];
+ const char szMemTotal[] = "MemTotal:";
+
+ while( fgets( buf, sizeof( buf ), fh ) )
+ {
+ if ( !Q_strnicmp( buf, szMemTotal, sizeof( szMemTotal ) - 1 ) )
+ {
+ // Should already be in kB
+ Ram = atoi( buf + sizeof( szMemTotal ) - 1 ) / 1024;
+ break;
+ }
+ }
+
+ fclose( fh );
+ }
+ return Ram;
+#else
+ Assert( !"Impl GetRam" );
+ return 0;
+#endif
+}
+
+const char *GetInternalBugReporterDLL( void )
+{
+ char const *pBugReportedDLL = NULL;
+ if ( CommandLine()->CheckParm("-bugreporterdll", &pBugReportedDLL ) )
+ return pBugReportedDLL;
+
+ return BUG_REPORTER_DLLNAME;
+}
+
+void DisplaySystemVersion( char *osversion, int maxlen )
+{
+#ifdef WIN32
+ osversion[ 0 ] = 0;
+ OSVERSIONINFOEX osvi;
+ BOOL bOsVersionInfoEx;
+
+ // Try calling GetVersionEx using the OSVERSIONINFOEX structure.
+ //
+ // If that fails, try using the OSVERSIONINFO structure.
+
+ ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
+ osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+
+ bOsVersionInfoEx = GetVersionEx ((OSVERSIONINFO *) &osvi);
+
+ if( !bOsVersionInfoEx )
+ {
+ // If OSVERSIONINFOEX doesn't work, try OSVERSIONINFO.
+
+ osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
+ if (! GetVersionEx ( (OSVERSIONINFO *) &osvi) )
+ {
+ Q_strncpy( osversion, "Unable to get Version", maxlen );
+ return;
+ }
+ }
+
+ switch (osvi.dwPlatformId)
+ {
+ case VER_PLATFORM_WIN32_NT:
+
+ // Test for the product.
+
+ if ( osvi.dwMajorVersion <= 4 )
+ {
+ Q_strncat ( osversion, "NT ", maxlen, COPY_ALL_CHARACTERS );
+ }
+
+ if ( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0 )
+ {
+ Q_strncat ( osversion, "2000 ", maxlen, COPY_ALL_CHARACTERS );
+ }
+
+ if ( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1 )
+ {
+ Q_strncat ( osversion, "XP ", maxlen, COPY_ALL_CHARACTERS );
+ }
+
+ // Display version, service pack (if any), and build number.
+
+ char build[256];
+ Q_snprintf (build, sizeof( build ), "%s (Build %d) version %d.%d",
+ osvi.szCSDVersion,
+ osvi.dwBuildNumber & 0xFFFF,
+ osvi.dwMajorVersion,
+ osvi.dwMinorVersion );
+ Q_strncat ( osversion, build, maxlen, COPY_ALL_CHARACTERS );
+ break;
+
+ case VER_PLATFORM_WIN32_WINDOWS:
+
+ if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 0)
+ {
+ Q_strncat ( osversion, "95 ", maxlen, COPY_ALL_CHARACTERS );
+ if ( osvi.szCSDVersion[1] == 'C' || osvi.szCSDVersion[1] == 'B' )
+ {
+ Q_strncat ( osversion, "OSR2 ", maxlen, COPY_ALL_CHARACTERS );
+ }
+ }
+
+ if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 10)
+ {
+ Q_strncat ( osversion, "98 ", maxlen, COPY_ALL_CHARACTERS );
+ if ( osvi.szCSDVersion[1] == 'A' )
+ {
+ Q_strncat ( osversion, "SE ", maxlen, COPY_ALL_CHARACTERS );
+ }
+ }
+
+ if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 90)
+ {
+ Q_strncat ( osversion, "Me ", maxlen, COPY_ALL_CHARACTERS );
+ }
+ break;
+
+ case VER_PLATFORM_WIN32s:
+
+ Q_strncat ( osversion, "Win32s ", maxlen, COPY_ALL_CHARACTERS );
+ break;
+ }
+#elif defined(OSX)
+ FILE *fpVersionInfo = popen( "/usr/bin/sw_vers", "r" );
+ const char *pszSearchString = "ProductVersion:\t";
+ const int cchSearchString = Q_strlen( pszSearchString );
+ char rgchVersionLine[1024];
+
+ if ( !fpVersionInfo )
+ Q_strncat ( osversion, "OSXU ", maxlen, COPY_ALL_CHARACTERS );
+ else
+ {
+ while ( fgets( rgchVersionLine, sizeof(rgchVersionLine), fpVersionInfo ) )
+ {
+ if ( !Q_strnicmp( rgchVersionLine, pszSearchString, cchSearchString ) )
+ {
+ const char *pchVersion = rgchVersionLine + cchSearchString;
+ if ( !Q_strnicmp( pchVersion, "10.", Q_strlen( "10." ) ) )
+ {
+ pchVersion += 3; // move past "10."
+ if( *pchVersion == '4' && *(pchVersion+1) == '.' )
+ {
+ Q_strncat ( osversion, "OSX104 ", maxlen, COPY_ALL_CHARACTERS );
+ break;
+ }
+ else if ( *pchVersion == '5' && *(pchVersion+1) == '.' )
+ {
+ Q_strncat ( osversion, "OSX105 ", maxlen, COPY_ALL_CHARACTERS );
+ break;
+ }
+ else if ( *pchVersion == '6' && *(pchVersion+1) == '.' )
+ {
+ Q_strncat ( osversion, "OSX106 ", maxlen, COPY_ALL_CHARACTERS );
+ break;
+ }
+ else if ( *pchVersion == '7' && *(pchVersion+1) == '.' )
+ {
+ Q_strncat ( osversion, "OSX107 ", maxlen, COPY_ALL_CHARACTERS );
+ break;
+ }
+ }
+ break;
+ }
+ }
+ pclose( fpVersionInfo );
+ }
+#elif defined(LINUX)
+ FILE *fpKernelVer = fopen( "/proc/version_signature", "r" );
+
+ if ( !fpKernelVer )
+ {
+ Q_strncat ( osversion, "Linux ", maxlen, COPY_ALL_CHARACTERS );
+ }
+ else
+ {
+ fgets( osversion, maxlen, fpKernelVer );
+ osversion[ maxlen - 1 ] = 0;
+
+ char *szlf = Q_strrchr( osversion, '\n' );
+ if( szlf )
+ *szlf = '\0';
+
+ fclose( fpKernelVer );
+ }
+#endif
+}
+
+static int GetNumberForMap()
+{
+ if ( !host_state.worldmodel )
+ return 1;
+
+ char mapname[256];
+ CL_SetupMapName( modelloader->GetName( host_state.worldmodel ), mapname, sizeof( mapname ) );
+
+ KeyValues *resfilekeys = new KeyValues( "mapnumber" );
+ if ( resfilekeys->LoadFromFile( g_pFileSystem, "scripts/bugreport_mapnumber.txt", "GAME" ) )
+ {
+ KeyValues *entry = resfilekeys->GetFirstSubKey();
+
+ while ( entry )
+ {
+ if ( !Q_stricmp( entry->GetName(), mapname ) )
+ {
+ return entry->GetInt() + 1;
+ }
+
+ entry = entry->GetNextKey();
+ }
+ }
+ resfilekeys->deleteThis();
+
+ char szNameCopy[ 128 ];
+
+ const char *pszResult = Q_strrchr( mapname, '_' );
+ if( !pszResult )
+ //I don't know where the number of this map is, if there even is one.
+ return 1;
+
+ Q_strncpy( szNameCopy, pszResult + 1, sizeof( szNameCopy ) );
+ if ( !szNameCopy[0] )
+ return 1;
+
+// in case we can't use tchar.h, this will do the same thing
+ char *pcEndOfName = szNameCopy;
+ while(*pcEndOfName != 0)
+ {
+ if(*pcEndOfName < '0' || *pcEndOfName > '9')
+ *pcEndOfName = 0;
+ pcEndOfName++;
+ }
+
+ //add 1 because pvcs has 0 as the first map number, not 1 (and it is not 0-based).
+ return atoi(szNameCopy) + 1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Generic dialog for displaying animating steam progress logo
+// used when we are doing a possibly length steam op that has no progress measure (login/create user/etc)
+//-----------------------------------------------------------------------------
+class CBugReportUploadProgressDialog : public vgui::Frame
+{
+public:
+ CBugReportUploadProgressDialog(vgui::Panel *parent, const char *name, const char *title, const char *message);
+ ~CBugReportUploadProgressDialog();
+
+ virtual void PerformLayout();
+
+ void SetProgress( float progress );
+
+private:
+ typedef vgui::Frame BaseClass;
+
+ vgui::ProgressBar *m_pProgress;
+};
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+CBugReportUploadProgressDialog::CBugReportUploadProgressDialog(Panel *parent, const char *name, const char *title, const char *message) : Frame(parent, name)
+{
+ SetSize(300, 160);
+ SetSizeable(false);
+ MoveToFront();
+ SetTitle(title, true);
+
+ m_pProgress = new vgui::ProgressBar( this, "ProgressBar" );
+
+ LoadControlSettings("Resource\\BugReporterUploadProgress.res");
+ SetControlString("InfoLabel", message);
+
+ MoveToCenterOfScreen();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+CBugReportUploadProgressDialog::~CBugReportUploadProgressDialog()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : percent -
+//-----------------------------------------------------------------------------
+void CBugReportUploadProgressDialog::SetProgress( float progress )
+{
+ Assert( m_pProgress );
+ m_pProgress->SetProgress( progress );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBugReportUploadProgressDialog::PerformLayout()
+{
+ SetMinimizeButtonVisible(false);
+ SetCloseButtonVisible(false);
+ BaseClass::PerformLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Generic dialog for displaying animating steam progress logo
+// used when we are doing a possibly length steam op that has no progress measure (login/create user/etc)
+//-----------------------------------------------------------------------------
+class CBugReportFinishedDialog : public vgui::Frame
+{
+public:
+ CBugReportFinishedDialog(vgui::Panel *parent, const char *name, const char *title, const char *message);
+
+ virtual void PerformLayout();
+
+ virtual void OnCommand( const char *command );
+
+private:
+ typedef vgui::Frame BaseClass;
+
+ vgui::Button *m_pOk;
+};
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+CBugReportFinishedDialog::CBugReportFinishedDialog(Panel *parent, const char *name, const char *title, const char *message) : Frame(parent, name)
+{
+ SetSize(300, 160);
+ SetSizeable(false);
+ MoveToFront();
+ SetTitle(title, true);
+
+ m_pOk = new vgui::Button( this, "CloseBtn", "#OK", this, "Close" );
+
+ LoadControlSettings("Resource\\BugReporterUploadFinished.res");
+ SetControlString("InfoLabel", message);
+
+ MoveToCenterOfScreen();
+}
+
+void CBugReportFinishedDialog::OnCommand( const char *command )
+{
+ if ( !Q_stricmp( command, "Close" ) )
+ {
+ MarkForDeletion();
+ OnClose();
+ }
+ else
+ {
+ BaseClass::OnCommand( command );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBugReportFinishedDialog::PerformLayout()
+{
+ SetMinimizeButtonVisible(false);
+ SetCloseButtonVisible(true);
+ BaseClass::PerformLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CBugUIPanel : public vgui::Frame
+{
+ DECLARE_CLASS_SIMPLE( CBugUIPanel, vgui::Frame );
+
+public:
+ CBugUIPanel( bool bIsPublic, vgui::Panel *parent );
+ ~CBugUIPanel();
+
+ virtual void OnTick();
+
+ // Command issued
+ virtual void OnCommand(const char *command);
+ virtual void Close();
+
+ virtual void Activate();
+
+ virtual void SetVisible( bool state )
+ {
+ bool changed = state != IsVisible();
+ BaseClass::SetVisible( state );
+
+ if ( changed && state )
+ {
+ m_pTitle->RequestFocus();
+ }
+ }
+
+ bool Init();
+ void Shutdown();
+
+ virtual void OnKeyCodePressed( KeyCode code );
+
+ void ParseDefaultParams( void );
+ void ParseCommands( const CCommand &args );
+
+ inline bool IsTakingSnapshot() const
+ {
+ return m_bTakingSnapshot;
+ }
+
+
+protected:
+ vgui::TextEntry *m_pTitle;
+ vgui::TextEntry *m_pDescription;
+
+ vgui::Button *m_pTakeShot;
+ vgui::Button *m_pSaveGame;
+ vgui::Button *m_pSaveBSP;
+ vgui::Button *m_pSaveVMF;
+ vgui::Button *m_pChooseVMFFolder;
+ vgui::Button *m_pIncludeFile;
+ vgui::Button *m_pClearIncludes;
+
+ vgui::Label *m_pScreenShotURL;
+ vgui::Label *m_pSaveGameURL;
+ vgui::Label *m_pBSPURL;
+ vgui::Label *m_pVMFURL;
+ vgui::Label *m_pPosition;
+ vgui::Label *m_pOrientation;
+ vgui::Label *m_pLevelName;
+ vgui::Label *m_pBuildNumber;
+
+ vgui::Label *m_pSubmitter;
+
+ vgui::ComboBox *m_pAssignTo;
+ vgui::ComboBox *m_pSeverity;
+ vgui::ComboBox *m_pReportType;
+ vgui::ComboBox *m_pPriority;
+ vgui::ComboBox *m_pGameArea;
+ vgui::ComboBox *m_pMapNumber;
+
+ vgui::Button *m_pSubmit;
+ vgui::Button *m_pCancel;
+ vgui::Button *m_pClearForm;
+
+ vgui::TextEntry *m_pIncludedFiles;
+
+ vgui::TextEntry *m_pEmail;
+ vgui::Label *m_pSubmitterLabel;
+
+ IBugReporter *m_pBugReporter;
+ CSysModule *m_hBugReporter;
+
+ MESSAGE_FUNC_CHARPTR( OnFileSelected, "FileSelected", fullpath );
+ MESSAGE_FUNC_CHARPTR( OnDirectorySelected, "DirectorySelected", dir );
+ MESSAGE_FUNC( OnChooseVMFFolder, "OnChooseVMFFolder" );
+ MESSAGE_FUNC_PTR( OnChooseArea, "TextChanged", panel);
+protected:
+
+ void DetermineSubmitterName();
+
+ void PopulateControls();
+
+ void OnTakeSnapshot();
+ void OnSaveGame();
+ void OnSaveBSP();
+ void OnSaveVMF();
+ void OnSubmit();
+ void OnClearForm();
+ void OnIncludeFile();
+ void OnClearIncludedFiles();
+
+ int GetArea();
+
+ bool IsValidSubmission( bool verbose );
+ bool IsValidEmailAddress( char const *email );
+
+ void WipeData();
+
+ void GetDataFileBase( char const *suffix, char *buf, int bufsize );
+ const char *GetRepositoryURL( void );
+ const char *GetSubmissionURL( int bugid );
+
+ bool AddFileToZip( char const *relative );
+ bool AddBugTextToZip( char const *textfilename, char const *text, int textlen );
+
+ void CheckContinueQueryingSteamForCSERList();
+
+ struct includedfile
+ {
+ char name[ 256 ];
+ char fixedname[ 256 ];
+ };
+
+ bool UploadBugSubmission(
+ char const *levelname,
+ int bugId,
+
+ char const *savefile,
+ char const *screenshot,
+ char const *bsp,
+ char const *vmf,
+ CUtlVector< includedfile >& includedfiles );
+ bool UploadFile( char const *local, char const *remote, bool bDeleteLocal = false );
+
+ void DenySound();
+ void SuccessSound( int bugid );
+
+ bool AutoFillToken( char const *token, bool partial );
+ bool Compare( char const *token, char const *value, bool partial );
+
+ char const *GetSubmitter();
+
+ void OnFinishBugReport();
+
+ bool m_bCanSubmit;
+ bool m_bLoggedIn;
+ bool m_bCanSeeRepository;
+ bool m_bValidated;
+ bool m_bUseNameForSubmitter;
+
+ unsigned char m_fAutoAddScreenshot;
+ enum AutoAddScreenshot { eAutoAddScreenshot_Detect = 0, eAutoAddScreenshot_Add = 1, eAutoAddScreenshot_DontAdd = 2 };
+
+ char m_szScreenShotName[ 256 ];
+ char m_szSaveGameName[ 256 ];
+ char m_szBSPName[ 256 ];
+ char m_szVMFName[ 256 ];
+ char m_szLevel[ 256 ];
+ CUtlVector< includedfile > m_IncludedFiles;
+
+ bool m_bTakingSnapshot;
+ bool m_bHidGameUIForSnapshot;
+ int m_nSnapShotFrame;
+
+ char m_szVMFContentDirFullpath[ MAX_PATH ];
+
+ vgui::DHANDLE< vgui::FileOpenDialog > m_hFileOpenDialog;
+ vgui::DHANDLE< vgui::DirectorySelectDialog > m_hDirectorySelectDialog;
+ // If true, then once directory for vmfs is selected, we act like the Include .vmf button got pressed, too
+ bool m_bAddVMF;
+
+ HZIP m_hZip;
+
+ CBugReportUploadProgressDialog *m_pProgressDialog;
+ vgui::DHANDLE< CBugReportFinishedDialog > m_hFinishedDialog;
+ bool m_bWaitForFinish;
+ float m_flPauseTime;
+ CSteamID m_SteamID;
+ netadr_t m_cserIP;
+ bool m_bQueryingSteamForCSER;
+ bool m_bIsPublic;
+ CUtlString m_sDllName;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: Basic help dialog
+//-----------------------------------------------------------------------------
+CBugUIPanel::CBugUIPanel( bool bIsPublic, vgui::Panel *parent ) :
+ BaseClass( parent, "BugUIPanel"),
+ m_bIsPublic( bIsPublic ),
+ m_bAddVMF( false )
+{
+ m_sDllName = m_bIsPublic ?
+ BUG_REPORTER_PUBLIC_DLLNAME :
+ GetInternalBugReporterDLL();
+
+ m_hZip = (HZIP)0;
+
+ m_hDirectorySelectDialog = NULL;
+ m_hFileOpenDialog = NULL;
+ m_pBugReporter = NULL;
+ m_hBugReporter = 0;
+ m_bQueryingSteamForCSER = false;
+
+ // Default server address (hardcoded in case not running on steam)
+ char const *cserIP = "207.173.177.12:27013";
+
+ // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ // NOTE: If you need to override the CSER Ip, make sure you tweak the code in
+ // CheckContinueQueryingSteamForCSERList!!!!!!!!!!
+ // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ NET_StringToAdr( cserIP, &m_cserIP );
+
+ m_bValidated = false;
+ m_szScreenShotName[ 0 ] = 0;
+ m_szSaveGameName[ 0 ] = 0;
+ m_szBSPName[ 0 ] = 0;
+ m_szVMFName[ 0 ] = 0;
+ m_szLevel[ 0 ] = 0;
+ m_szVMFContentDirFullpath[ 0 ] = 0;
+ m_IncludedFiles.Purge();
+
+ m_nSnapShotFrame = -1;
+ m_bTakingSnapshot = false;
+ m_bHidGameUIForSnapshot = false;
+
+ m_bCanSubmit = false;
+ m_bLoggedIn = false;
+ m_bCanSeeRepository = false;
+
+ m_pProgressDialog = NULL;
+ m_flPauseTime = 0.0f;
+ m_bWaitForFinish = false;
+ m_bUseNameForSubmitter = false;
+
+ m_fAutoAddScreenshot = eAutoAddScreenshot_Detect;
+
+ SetTitle("Bug Reporter", true);
+
+ m_pTitle = new vgui::TextEntry( this, "BugTitle" );
+ m_pTitle->SetMaximumCharCount( 60 ); // Titles can be 80 chars, but we put the mapname in front, so limit to 60
+ m_pDescription = new vgui::TextEntry( this, "BugDescription" );
+ m_pDescription->SetMultiline( true );
+ m_pDescription->SetCatchEnterKey( true );
+ m_pDescription->SetVerticalScrollbar( true );
+
+ m_pEmail = new vgui::TextEntry( this, "BugEmail" );;
+ m_pEmail->SetMaximumCharCount( 80 );
+
+ m_pSubmitterLabel = new vgui::Label( this, "BugSubitterLabelPublic", "" );
+
+ m_pScreenShotURL = new vgui::Label( this, "BugScreenShotURL", "" );
+ m_pSaveGameURL = new vgui::Label( this, "BugSaveGameURL", "" );
+ m_pBSPURL = new vgui::Label( this, "BugBSPURL", "" );
+ m_pVMFURL = new vgui::Label( this, "BugVMFURL", "" );
+ m_pIncludedFiles = new vgui::TextEntry( this, "BugIncludedFiles" );
+ m_pIncludedFiles->SetMultiline( true );
+ m_pIncludedFiles->SetVerticalScrollbar( true );
+ m_pIncludedFiles->SetEditable( false );
+
+ m_pTakeShot = new vgui::Button( this, "BugTakeShot", "Take shot" );
+ m_pSaveGame = new vgui::Button( this, "BugSaveGame", "Save game" );
+ m_pSaveBSP = new vgui::Button( this, "BugSaveBSP", "Include .bsp" );
+ m_pSaveVMF = new vgui::Button( this, "BugSaveVMF", "Include .vmf" );
+ m_pChooseVMFFolder = new vgui::Button( this, "BugChooseVMFFolder", "Choose folder" );
+ m_pIncludeFile = new vgui::Button( this, "BugIncludeFile", "Include file..." );
+ m_pClearIncludes = new vgui::Button( this, "BugClearIncludedFiles", "Clear files" );
+
+ m_pPosition = new vgui::Label( this, "BugPosition", "" );
+ m_pOrientation = new vgui::Label( this, "BugOrientation", "" );
+ m_pLevelName = new vgui::Label( this, "BugLevel", "" );
+ m_pBuildNumber = new vgui::Label( this, "BugBuild", "" );
+
+ m_pSubmitter = new Label(this, "BugSubmitter", "" );
+ m_pAssignTo = new ComboBox(this, "BugOwner", 10, false);
+ m_pSeverity = new ComboBox(this, "BugSeverity", 10, false);
+ m_pReportType = new ComboBox(this, "BugReportType", 10, false);
+ m_pPriority = new ComboBox(this, "BugPriority", 10, false);
+ m_pGameArea = new ComboBox(this, "BugArea", 10, false);
+ m_pMapNumber = new ComboBox(this, "BugMapNumber", 10, false);
+
+ m_pSubmit = new vgui::Button( this, "BugSubmit", "Submit" );
+ m_pCancel = new vgui::Button( this, "BugCancel", "Cancel" );
+ m_pClearForm = new vgui::Button( this, "BugClearForm", "Clear Form" );
+
+ vgui::ivgui()->AddTickSignal( GetVPanel(), 0 );
+
+ if ( m_bIsPublic )
+ {
+ LoadControlSettings("Resource\\BugUIPanel_Public.res");
+ }
+ else
+ {
+ LoadControlSettings("Resource\\BugUIPanel_Filequeue.res");
+ }
+
+ m_pChooseVMFFolder->SetCommand( new KeyValues( "OnChooseVMFFolder" ) );
+ m_pChooseVMFFolder->AddActionSignalTarget( this );
+
+ int w = GetWide();
+ int h = GetTall();
+
+ int x = ( videomode->GetModeStereoWidth() - w ) / 2;
+ int y = ( videomode->GetModeStereoHeight() - h ) / 2;
+
+ // Hidden by default
+ SetVisible( false );
+
+ SetSizeable( false );
+ SetMoveable( true );
+
+ SetPos( x, y );
+}
+
+bool CBugUIPanel::Init()
+{
+ Color clr( 50, 100, 255, 255 );
+
+ Assert( !m_pBugReporter );
+
+ m_hBugReporter = g_pFileSystem->LoadModule( m_sDllName);
+
+ if( m_bIsPublic )
+ {
+ // Hack due to constructor called before phonehome->Init...
+ m_hBugReporter = g_pFileSystem->LoadModule( m_sDllName );
+
+ LoadControlSettings("Resource\\BugUIPanel_Public.res");
+
+ int w = GetWide();
+ int h = GetTall();
+
+ int x = ( videomode->GetModeStereoWidth() - w ) / 2;
+ int y = ( videomode->GetModeStereoHeight() - h ) / 2;
+
+
+ SetPos( x, y );
+ }
+
+ if ( m_hBugReporter )
+ {
+ CreateInterfaceFn factory = Sys_GetFactory( m_hBugReporter );
+ if ( factory )
+ {
+ m_pBugReporter = (IBugReporter *)factory( INTERFACEVERSION_BUGREPORTER, NULL );
+ if( m_pBugReporter )
+ {
+ extern CreateInterfaceFn g_AppSystemFactory;
+ if ( m_pBugReporter->Init( g_AppSystemFactory ) )
+ {
+ m_bCanSubmit = true;
+ m_bLoggedIn = true;
+ }
+ else
+ {
+ m_pBugReporter = NULL;
+ ConColorMsg( clr, "m_pBugReporter->Init() failed\n" );
+ return false;
+ }
+ }
+ else
+ {
+ ConColorMsg( clr, "Couldn't get interface '%s' from '%s'\n", INTERFACEVERSION_BUGREPORTER, m_sDllName.String() );
+ return false;
+ }
+ }
+ else
+ {
+ ConColorMsg( clr, "Couldn't get factory '%s'\n", m_sDllName.String() );
+ return false;
+ }
+ }
+ else
+ {
+ ConColorMsg( clr, "Couldn't load '%s'\n", m_sDllName.String() );
+ return false;
+ }
+
+ if ( m_bCanSubmit )
+ {
+ PopulateControls();
+ }
+
+
+ if ( m_pBugReporter && m_pBugReporter->IsPublicUI() )
+ {
+ m_pSaveBSP->SetVisible( false );
+ m_pBSPURL->SetVisible( false );
+
+ m_pChooseVMFFolder->SetVisible( false );
+ m_pSaveVMF->SetVisible( false );
+ m_pVMFURL->SetVisible( false );
+
+ m_pIncludeFile->SetVisible( false );
+ m_pClearIncludes->SetVisible( false );
+
+ m_pAssignTo->SetVisible( false );
+ m_pSeverity->SetVisible( false );
+ m_pPriority->SetVisible( false );
+ m_pGameArea->SetVisible( false );
+ m_pMapNumber->SetVisible( false );
+
+ m_pIncludedFiles->SetVisible( false );
+ m_pSubmitter->SetVisible( false );
+
+ if ( Steam3Client().SteamUser() )
+ {
+ m_pSubmitterLabel->SetText( Steam3Client().SteamUser()->GetSteamID().Render() );
+ }
+ else
+ {
+ m_pSubmitterLabel->SetText( "PublicUser" );
+ }
+
+ m_pSubmitterLabel->SetVisible( true );
+
+ m_bQueryingSteamForCSER = true;
+ }
+ else
+ {
+ m_pEmail->SetVisible( false );
+ m_pSubmitterLabel->SetVisible( true );
+
+ }
+
+ Q_snprintf( m_szVMFContentDirFullpath, sizeof( m_szVMFContentDirFullpath ), "%s/maps", com_gamedir );
+ Q_strlower( m_szVMFContentDirFullpath );
+ Q_FixSlashes( m_szVMFContentDirFullpath );
+
+ m_pBuildNumber->SetText( va( "%d", build_number() ) );
+
+ return false;
+}
+
+void CBugUIPanel::Shutdown()
+{
+ if ( m_pBugReporter )
+ {
+ m_pBugReporter->Shutdown();
+ }
+
+ m_pBugReporter = NULL;
+ if ( m_hBugReporter )
+ {
+ Sys_UnloadModule( m_hBugReporter );
+ m_hBugReporter = 0;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CBugUIPanel::~CBugUIPanel()
+{
+
+}
+
+void CBugUIPanel::OnTick()
+{
+ BaseClass::OnTick();
+
+ CheckContinueQueryingSteamForCSERList();
+
+ if ( !IsVisible() )
+ {
+ if ( !m_bTakingSnapshot )
+ {
+ return;
+ }
+
+ if ( host_framecount < m_nSnapShotFrame + 2 )
+ return;;
+
+ m_bTakingSnapshot = false;
+ m_nSnapShotFrame = -1;
+
+ m_pScreenShotURL->SetText( m_szScreenShotName );
+
+ if (m_bHidGameUIForSnapshot)
+ {
+ EngineVGui()->ActivateGameUI();
+ }
+ m_bHidGameUIForSnapshot = false;
+
+ SetVisible( true );
+ MoveToFront();
+ }
+
+ if ( !m_bCanSubmit )
+ {
+ if ( m_pBugReporter && !m_pBugReporter->IsPublicUI() )
+ {
+ if ( !m_bCanSeeRepository )
+ {
+ Warning( "Bug UI disabled: Couldn't see repository\n" );
+ }
+ else if ( !m_bLoggedIn )
+ {
+ Warning( "Bug UI disabled: Couldn't log in to PVCS Tracker\n" );
+ }
+ else
+ {
+ Assert( 0 );
+ }
+ }
+
+ SetVisible( false );
+ return;
+ }
+
+ m_pSubmit->SetEnabled( IsValidSubmission( false ) );
+
+ if (m_flPauseTime > 0.0f )
+ {
+ if ( m_flPauseTime <= system()->GetFrameTime())
+ {
+ m_flPauseTime = 0.0f;
+
+ if ( m_pProgressDialog )
+ {
+ m_pProgressDialog->Close();
+ }
+ m_pProgressDialog = NULL;
+
+ OnFinishBugReport();
+
+ m_bWaitForFinish = true;
+
+ if ( !m_hFinishedDialog.Get() )
+ {
+ m_hFinishedDialog = new CBugReportFinishedDialog(NULL, "FinishDialog", "#Steam_FinishedBug_WorkingTitle", "#Steam_FinishedBug_Text" );
+ m_hFinishedDialog->Activate();
+ vgui::input()->SetAppModalSurface(m_hFinishedDialog->GetVPanel());
+ }
+ }
+ else
+ {
+ if ( m_pProgressDialog )
+ {
+ float percent = clamp( 1.0f - (float)( m_flPauseTime - system()->GetFrameTime() ) / (float)PUBLIC_BUGREPORT_WAIT_TIME, 0.0f, 1.0f );
+ m_pProgressDialog->SetProgress( percent );
+ }
+ }
+ }
+
+ if ( m_bWaitForFinish && !m_hFinishedDialog.Get() )
+ {
+ m_bWaitForFinish = false;
+ Close();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *suffix -
+// *buf -
+// bufsize -
+//-----------------------------------------------------------------------------
+void CBugUIPanel::GetDataFileBase( char const *suffix, char *buf, int bufsize )
+{
+ struct tm t;
+ VCRHook_LocalTime( &t );
+
+ char who[ 128 ];
+ Q_strncpy( who, suffix, sizeof( who ) );
+ Q_strlower( who );
+
+ if ( m_pBugReporter && m_pBugReporter->IsPublicUI() )
+ {
+ Q_snprintf( buf, bufsize, "%i_%02i_%02i", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday );
+ }
+ else
+ {
+ Q_snprintf( buf, bufsize, "%i_%02i_%02i_%s", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, who );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : const char
+//-----------------------------------------------------------------------------
+const char *CBugUIPanel::GetRepositoryURL( void )
+{
+ const char *pURL = m_pBugReporter->GetRepositoryURL();
+ if ( pURL )
+ return pURL;
+
+ return BUG_REPOSITORY_URL;
+}
+
+const char *CBugUIPanel::GetSubmissionURL( int bugid )
+{
+ const char *pURL = m_pBugReporter->GetSubmissionURL();
+ if ( pURL )
+ return pURL;
+
+ static char url[512];
+
+ Q_snprintf(url, sizeof(url), "%s/%i", GetRepositoryURL(), bugid);
+
+ return url;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBugUIPanel::OnTakeSnapshot()
+{
+ GetDataFileBase( GetSubmitter(), m_szScreenShotName, sizeof( m_szScreenShotName ) );
+
+ m_nSnapShotFrame = host_framecount;
+ m_bTakingSnapshot = true;
+
+ if ( EngineVGui()->IsGameUIVisible() )
+ {
+ m_bHidGameUIForSnapshot = true;
+ EngineVGui()->HideGameUI();
+ }
+ else
+ {
+ m_bHidGameUIForSnapshot = false;
+ }
+
+ SetVisible( false );
+
+ // Internal reports at 100% quality .jpg
+ int quality = 100;
+
+ if ( m_pBugReporter && m_pBugReporter->IsPublicUI() )
+ {
+ quality = 40;
+ }
+
+ Cbuf_AddText( va( "jpeg \"%s\" %i\n", m_szScreenShotName, quality ) );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBugUIPanel::OnSaveGame()
+{
+ GetDataFileBase( GetSubmitter(), m_szSaveGameName, sizeof( m_szSaveGameName ) );
+
+ if ( m_pBugReporter && m_pBugReporter->IsPublicUI() )
+ {
+ // External users send us a "minisave" file which doesn't contain data from other previously encoudntered/connected levels
+ Cbuf_AddText( va( "minisave %s\n", m_szSaveGameName ) );
+ }
+ else
+ {
+ Cbuf_AddText( va( "save %s notmostrecent copymap\n", m_szSaveGameName ) );
+ }
+
+ m_pSaveGameURL->SetText( m_szSaveGameName );
+}
+
+void CBugUIPanel::OnSaveBSP()
+{
+ GetDataFileBase( GetSubmitter(), m_szBSPName, sizeof( m_szBSPName ) );
+ m_pBSPURL->SetText( m_szBSPName );
+}
+
+void CBugUIPanel::OnSaveVMF()
+{
+ if ( m_pBugReporter && m_pBugReporter->IsPublicUI() )
+ return;
+
+ char level[ 256 ];
+ m_pLevelName->GetText( level, sizeof( level ) );
+
+ // See if .vmf exists in assumed location
+ char localfile[ 512 ];
+ Q_snprintf( localfile, sizeof( localfile ), "%s/%s.vmf", m_szVMFContentDirFullpath, level );
+ if ( !g_pFileSystem->FileExists( localfile, NULL ) )
+ {
+ if ( !m_hDirectorySelectDialog.Get() )
+ {
+ m_hDirectorySelectDialog = new DirectorySelectDialog( this, "Choose .vmf folder" );
+ }
+
+ m_bAddVMF = true;
+ m_hDirectorySelectDialog->SetStartDirectory( m_szVMFContentDirFullpath );
+ m_hDirectorySelectDialog->DoModal();
+ return;
+ }
+
+ GetDataFileBase( GetSubmitter(), m_szVMFName, sizeof( m_szVMFName ) );
+ m_pVMFURL->SetText( m_szVMFName );
+}
+
+void CBugUIPanel::OnChooseVMFFolder()
+{
+ if ( !m_hDirectorySelectDialog.Get() )
+ {
+ m_hDirectorySelectDialog = new DirectorySelectDialog( this, "Choose .vmf folder" );
+ }
+
+ m_bAddVMF = false;
+ m_hDirectorySelectDialog->SetStartDirectory( m_szVMFContentDirFullpath );
+ m_hDirectorySelectDialog->DoModal();
+}
+
+void CBugUIPanel::OnChooseArea(vgui::Panel *panel)
+{
+ if (panel != m_pGameArea)
+ return;
+
+ if (!Q_strcmp(BUG_REPORTER_DLLNAME, GetInternalBugReporterDLL()))
+ {
+ int area_index = m_pGameArea->GetActiveItem();
+ int c = m_pBugReporter->GetLevelCount(area_index);
+ int item = -1;
+ const char *currentLevel = cl.IsActive() ? cl.m_szLevelBaseName : "console";
+
+ m_pMapNumber->DeleteAllItems();
+
+ for ( int i = 0; i < c; i++ )
+ {
+ const char *level = m_pBugReporter->GetLevel(area_index, i );
+ int id = m_pMapNumber->AddItem( level, NULL );
+ if (!Q_strcmp(currentLevel, level))
+ {
+ item = id;
+ }
+ }
+
+ if (item >= 0)
+ {
+ m_pMapNumber->ActivateItem( item );
+ }
+ else
+ {
+ m_pMapNumber->ActivateItemByRow(0);
+ }
+ }
+}
+
+void CBugUIPanel::OnDirectorySelected( char const *dir )
+{
+ Q_strncpy( m_szVMFContentDirFullpath, dir, sizeof( m_szVMFContentDirFullpath ) );
+ Q_strlower( m_szVMFContentDirFullpath );
+ Q_FixSlashes( m_szVMFContentDirFullpath );
+ Q_StripTrailingSlash( m_szVMFContentDirFullpath );
+
+ if ( m_hDirectorySelectDialog != 0 )
+ {
+ m_hDirectorySelectDialog->MarkForDeletion();
+ }
+
+ // See if .vmf exists in assumed location
+ if ( m_bAddVMF )
+ {
+ GetDataFileBase( GetSubmitter(), m_szVMFName, sizeof( m_szVMFName ) );
+ m_pVMFURL->SetText( m_szVMFName );
+ }
+ m_bAddVMF = false;
+}
+
+void CBugUIPanel::OnFileSelected( char const *fullpath )
+{
+ bool baseDirFile = false;
+
+ if ( m_pBugReporter && m_pBugReporter->IsPublicUI() )
+ return;
+
+ if ( !fullpath || !fullpath[ 0 ] )
+ return;
+
+ char relativepath[ 512 ];
+ if ( !g_pFileSystem->FullPathToRelativePath( fullpath, relativepath, sizeof( relativepath ) ) )
+ {
+ if ( Q_stristr( fullpath, com_basedir ) )
+ {
+ Q_snprintf( relativepath, sizeof( relativepath ), "..%s", fullpath + strlen(com_basedir) );
+ baseDirFile = true;
+ }
+ else
+ {
+ Msg("Only files beneath the base game directory can be included\n" );
+ return;
+ }
+ }
+
+ char ext[ 10 ];
+ Q_ExtractFileExtension( relativepath, ext, sizeof( ext ) );
+
+ if ( m_hFileOpenDialog != 0 )
+ {
+ m_hFileOpenDialog->MarkForDeletion();
+ }
+
+ includedfile inc;
+ Q_strncpy( inc.name, relativepath, sizeof( inc.name ) );
+
+ if ( baseDirFile )
+ {
+ Q_snprintf( inc.fixedname, sizeof( inc.fixedname ), "%s", inc.name+3 ); // strip the "..\"
+ }
+ else
+ {
+ Q_snprintf( inc.fixedname, sizeof( inc.fixedname ), "%s", inc.name );
+ }
+ Q_FixSlashes( inc.fixedname );
+
+ m_IncludedFiles.AddToTail( inc );
+
+ char concat[ 8192 ];
+ concat[ 0 ] = 0;
+ for ( int i = 0 ; i < m_IncludedFiles.Count(); ++i )
+ {
+ Q_strncat( concat, m_IncludedFiles[ i ].name, sizeof( concat), COPY_ALL_CHARACTERS );
+ Q_strncat( concat, "\n", sizeof( concat), COPY_ALL_CHARACTERS );
+ }
+ m_pIncludedFiles->SetText( concat );
+}
+
+void CBugUIPanel::OnIncludeFile()
+{
+ if ( m_pBugReporter && m_pBugReporter->IsPublicUI() )
+ return;
+
+ if ( !m_hFileOpenDialog.Get() )
+ {
+ m_hFileOpenDialog = new FileOpenDialog( this, "Choose file to include", true );
+ if ( m_hFileOpenDialog != 0 )
+ {
+ m_hFileOpenDialog->AddFilter("*.*", "All Files (*.*)", true);
+ }
+ }
+ if ( m_hFileOpenDialog )
+ {
+ char startPath[ MAX_PATH ];
+ Q_strncpy( startPath, com_gamedir, sizeof( startPath ) );
+ Q_FixSlashes( startPath );
+ m_hFileOpenDialog->SetStartDirectory( startPath );
+ m_hFileOpenDialog->DoModal( false );
+ }
+
+ //GetDataFileBase( GetSubmitter(), m_szVMFName, sizeof( m_szVMFName ) );
+}
+
+void CBugUIPanel::OnClearIncludedFiles()
+{
+ m_IncludedFiles.Purge();
+ m_pIncludedFiles->SetText( "" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Shows the panel
+//-----------------------------------------------------------------------------
+void CBugUIPanel::Activate()
+{
+ if ( !m_bValidated )
+ {
+ if ( Init() )
+ {
+ m_bValidated = true;
+ DetermineSubmitterName();
+ }
+ }
+
+ if ( m_pGameArea->GetItemCount() != 0 )
+ {
+ int iArea = GetArea();
+ if ( iArea != 0 )
+ m_pGameArea->ActivateItem( iArea );
+ }
+
+ {
+ int c = m_pMapNumber->GetItemCount();
+ const char *currentLevel = cl.IsActive() ? cl.m_szLevelBaseName : "console";
+ int item = -1;
+
+ for ( int i = 0; i < c; i++ )
+ {
+ int id = m_pMapNumber->GetItemIDFromRow(i);
+ char level[256];
+ m_pMapNumber->GetItemText(id, level, sizeof(level));
+ if (!Q_strcmp(currentLevel, level))
+ {
+ item = id;
+ }
+ }
+
+ if (item >= 0)
+ {
+ m_pMapNumber->ActivateItem( item );
+ }
+ else
+ {
+ m_pMapNumber->ActivateItemByRow(0);
+ }
+ }
+
+ if ( cl.IsActive() )
+ {
+ Vector org = MainViewOrigin();
+ QAngle ang;
+ VectorAngles( MainViewForward(), ang );
+
+ IClientEntity *localPlayer = entitylist->GetClientEntity( cl.m_nPlayerSlot + 1 );
+ if ( localPlayer )
+ {
+ org = localPlayer->GetAbsOrigin();
+ }
+
+ m_pPosition->SetText( va( "%f %f %f", org.x, org.y, org.z ) );
+ m_pOrientation->SetText( va( "%f %f %f", ang.x, ang.y, ang.z ) );
+
+ m_pLevelName->SetText( cl.m_szLevelBaseName );
+ m_pSaveGame->SetEnabled( ( cl.m_nMaxClients == 1 ) ? true : false );
+ m_pSaveBSP->SetEnabled( true );
+ m_pSaveVMF->SetEnabled( true );
+ m_pChooseVMFFolder->SetEnabled( true );
+ }
+ else
+ {
+ m_pPosition->SetText( "console" );
+ m_pOrientation->SetText( "console" );
+ m_pLevelName->SetText( "console" );
+ m_pSaveGame->SetEnabled( false );
+ m_pSaveBSP->SetEnabled( false );
+ m_pSaveVMF->SetEnabled( false );
+ m_pChooseVMFFolder->SetEnabled( false );
+ }
+
+ BaseClass::Activate();
+
+ m_pTitle->RequestFocus();
+ m_pTitle->SelectAllText( true );
+
+ // Automatically make a screenshot if requested.
+ if (!m_szScreenShotName[0] )
+ {
+ bool bAutoAddScreenshot = false;
+
+ // For public bugreports should be explicitly requested
+ if ( m_bIsPublic )
+ {
+ if ( m_fAutoAddScreenshot == CBugUIPanel::eAutoAddScreenshot_Add )
+ {
+ bAutoAddScreenshot = true;
+ }
+ }
+ // For internal bugreports shouldn't be explicitly denied
+ else
+ {
+ if ( m_fAutoAddScreenshot != CBugUIPanel::eAutoAddScreenshot_DontAdd )
+ {
+ bAutoAddScreenshot = true;
+ }
+ }
+
+ if ( bAutoAddScreenshot )
+ {
+ OnTakeSnapshot();
+ }
+ }
+}
+
+void CBugUIPanel::WipeData()
+{
+ m_fAutoAddScreenshot = eAutoAddScreenshot_Detect;
+
+ m_szScreenShotName[ 0 ] = 0;
+ m_pScreenShotURL->SetText( "Screenshot file" );
+ m_szSaveGameName[ 0 ] = 0;
+ m_pSaveGameURL->SetText( "Save game file" );
+ m_szBSPName[ 0 ] = 0;
+ m_pBSPURL->SetText( ".bsp file" );
+ m_szVMFName[ 0 ] = 0;
+ m_pVMFURL->SetText( ".vmf file" );
+ m_IncludedFiles.Purge();
+ m_pIncludedFiles->SetText( "" );
+
+// m_pTitle->SetText( "" );
+ m_pDescription->SetText( "" );
+ m_pEmail->SetText( "" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: method to make sure the email address is acceptable to be used by steam
+//-----------------------------------------------------------------------------
+bool CBugUIPanel::IsValidEmailAddress( char const *email )
+{
+ // basic size check
+ if (!email || strlen(email) < 5)
+ return false;
+
+ // make sure all the characters in the string are valid
+ {for (const char *sz = email; *sz; sz++)
+ {
+ if (!V_isalnum(*sz) && *sz != '.' && *sz != '-' && *sz != '@' && *sz != '_')
+ return false;
+ }}
+
+ // make sure it has letters, then the '@', then letters, then '.', then letters
+ const char *sz = email;
+ if (!V_isalnum(*sz))
+ return false;
+ sz = strstr(sz, "@");
+ if (!sz)
+ return false;
+ sz++;
+ if (!V_isalnum(*sz))
+ return false;
+ sz = strstr(sz, ".");
+ if (!sz)
+ return false;
+ sz++;
+ if (!V_isalnum(*sz))
+ return false;
+
+ return true;
+}
+
+bool CBugUIPanel::IsValidSubmission( bool verbose )
+{
+ if ( !m_pBugReporter )
+ return false;
+
+ bool isPublic = m_pBugReporter->IsPublicUI();
+
+ char title[ 256 ];
+ char desc[ 4096 ];
+
+ title[ 0 ] = 0;
+ desc[ 0 ] = 0;
+
+ m_pTitle->GetText( title, sizeof( title ) );
+ if ( !title[ 0 ] )
+ {
+ if ( verbose )
+ {
+ Warning( "Bug must have a title\n" );
+ }
+
+ return false;
+ }
+
+ // Only require description in public UI
+ if ( isPublic )
+ {
+ m_pDescription->GetText( desc, sizeof( desc ) );
+ if ( !desc[ 0 ] )
+ {
+ if ( verbose )
+ {
+ Warning( "Bug must have a description\n" );
+ }
+ return false;
+ }
+ }
+
+
+ if ( !isPublic && m_pSeverity->GetActiveItem() < 0 )
+ {
+ if ( verbose )
+ {
+ Warning( "Severity not set!\n" );
+ }
+ return false;
+ }
+
+ if ( !isPublic && m_pAssignTo->GetActiveItem() < 0 )
+ {
+ if ( verbose )
+ {
+ Warning( "Owner not set!\n" );
+ }
+ return false;
+ }
+
+ char owner[ 256 ];
+ Q_strncpy( owner, m_pBugReporter->GetDisplayName( m_pAssignTo->GetActiveItem() ), sizeof( owner ) );
+ if ( !isPublic && !Q_stricmp( owner, "<<Unassigned>>" ) )
+ {
+ if ( verbose )
+ {
+ Warning( "Owner not set!\n" );
+ }
+ return false;
+ }
+
+ if ( !isPublic && m_pPriority->GetActiveItem() < 0 )
+ {
+ if ( verbose )
+ {
+ Warning( "Priority not set!\n" );
+ }
+ return false;
+ }
+
+ if ( !isPublic && m_pReportType->GetActiveItem() < 0 )
+ {
+ if ( verbose )
+ {
+ Warning( "Report Type not set!\n" );
+ }
+ return false;
+ }
+
+ if ( !isPublic && m_pGameArea->GetActiveItem() < 0 )
+ {
+ if ( verbose )
+ {
+ Warning( "Area not set!\n" );
+ }
+ return false;
+ }
+
+ // Public must have a selection and it can't be the "<<Choose One>>"
+ if ( isPublic && m_pReportType->GetActiveItem() <= 0 )
+ {
+ if ( verbose )
+ {
+ Warning( "Report type not set!\n" );
+ }
+ return false;
+ }
+
+ if ( isPublic )
+ {
+ char email[ 80 ];
+ m_pEmail->GetText( email, sizeof( email ) );
+ if ( email[ 0 ] != 0 &&
+ !IsValidEmailAddress( email ) )
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool CBugUIPanel::AddBugTextToZip( char const *textfilename, char const *text, int textlen )
+{
+ if ( !m_hZip )
+ {
+ // Create using OS pagefile memory...
+ m_hZip = CreateZipZ( 0, MAX_ZIP_SIZE, ZIP_MEMORY );
+ Assert( m_hZip );
+ if ( !m_hZip )
+ {
+ return false;
+ }
+ }
+
+ ZipAdd( m_hZip, textfilename, (void *)text, textlen, ZIP_MEMORY );
+
+ return true;
+}
+
+
+bool CBugUIPanel::AddFileToZip( char const *relative )
+{
+ if ( !m_hZip )
+ {
+ // Create using OS pagefile memory...
+ m_hZip = CreateZipZ( 0, MAX_ZIP_SIZE, ZIP_MEMORY );
+ Assert( m_hZip );
+ if ( !m_hZip )
+ {
+ return false;
+ }
+ }
+
+ char fullpath[ 512 ];
+ if ( g_pFileSystem->RelativePathToFullPath( relative, "GAME", fullpath, sizeof( fullpath ) ) )
+ {
+ char extension[ 32 ];
+ Q_ExtractFileExtension( relative, extension, sizeof( extension ) );
+ char basename[ 256 ];
+ Q_FileBase( relative, basename, sizeof( basename ) );
+ char outname[ 512 ];
+ Q_snprintf( outname, sizeof( outname ), "%s.%s", basename, extension );
+
+ ZipAdd( m_hZip, outname, fullpath, 0, ZIP_FILENAME );
+
+ return true;
+ }
+
+ return false;
+}
+
+void CBugUIPanel::OnSubmit()
+{
+ if ( !m_bCanSubmit )
+ {
+ return;
+ }
+
+ if ( !IsValidSubmission( true ) )
+ {
+ // Play deny sound
+ DenySound();
+ return;
+ }
+
+ bool isPublic = m_pBugReporter->IsPublicUI();
+
+ char title[ 80 ];
+ char desc[ 4096 ];
+ char severity[ 128 ];
+ char area[ 128 ];
+ char mapnumber[ 128 ];
+ char priority[ 128 ];
+ char assignedto[ 128 ];
+ char level[ 128 ];
+ char position[ 128 ];
+ char orientation[ 128 ];
+ char build[ 128 ];
+ char reporttype[ 128 ];
+ char email[ 80 ];
+
+ title[ 0 ] = 0;
+ desc[ 0 ] = 0;
+ severity[ 0 ] = 0;
+ area[ 0 ] = 0;
+ mapnumber[ 0] = 0;
+ priority[ 0 ] = 0;
+ assignedto[ 0 ] = 0;
+ level[ 0 ] = 0;
+ orientation[ 0 ] = 0;
+ position[ 0 ] = 0;
+ build[ 0 ] = 0;
+ reporttype [ 0 ] = 0;
+ email[ 0 ] = 0;
+
+ Assert( m_pBugReporter );
+
+ // Stuff bug data files up to server
+ m_pBugReporter->StartNewBugReport();
+
+
+ char temp[ 80 ];
+ m_pTitle->GetText( temp, sizeof( temp ) );
+
+ if ( host_state.worldmodel )
+ {
+ char mapname[256];
+ CL_SetupMapName( modelloader->GetName( host_state.worldmodel ), mapname, sizeof( mapname ) );
+
+ Q_snprintf( title, sizeof( title ), "%s: %s", mapname, temp );
+ }
+ else
+ {
+ Q_snprintf( title, sizeof( title ), "%s", temp );
+ }
+
+ Msg( "title: %s\n", title );
+
+ m_pDescription->GetText( desc, sizeof( desc ) );
+
+ Msg( "description: %s\n", desc );
+
+ m_pLevelName->GetText( level, sizeof( level ) );
+ m_pPosition->GetText( position, sizeof( position ) );
+ m_pOrientation->GetText( orientation, sizeof( orientation ) );
+ m_pBuildNumber->GetText( build, sizeof( build ) );
+
+ Q_strncat( build, " (Steam)", sizeof(build), COPY_ALL_CHARACTERS );
+
+ MaterialAdapterInfo_t info;
+ materials->GetDisplayAdapterInfo( materials->GetCurrentAdapter(), info );
+ char driverinfo[ 2048 ];
+
+ char const *dxlevel = "Unk";
+ if ( g_pMaterialSystemHardwareConfig )
+ {
+ dxlevel = COM_DXLevelToString( g_pMaterialSystemHardwareConfig->GetDXSupportLevel() ) ;
+ }
+
+ char osversion[ 256 ];
+ DisplaySystemVersion( osversion, sizeof( osversion ) );
+
+ Q_snprintf( driverinfo, sizeof( driverinfo ),
+ "OS Version: %s\n"
+ "Driver Name: %s\n"
+ "VendorId / DeviceId: 0x%x / 0x%x\n"
+ "SubSystem / Rev: 0x%x / 0x%x\n"
+ "DXLevel: %s\nVid: %i x %i\n"
+ "Framerate: %.3f\n",
+ osversion,
+ info.m_pDriverName,
+ info.m_VendorID,
+ info.m_DeviceID,
+ info.m_SubSysID,
+ info.m_Revision,
+ dxlevel ? dxlevel : "Unk",
+ videomode->GetModeWidth(), videomode->GetModeHeight(),
+ g_fFramesPerSecond );
+
+ Msg( "%s", driverinfo );
+
+ int latency = 0;
+ if ( cl.m_NetChannel )
+ {
+ latency = (int)( 1000.0f * cl.m_NetChannel->GetAvgLatency( FLOW_OUTGOING ) );
+ }
+
+ ConVarRef host_thread_mode( "host_thread_mode" );
+ ConVarRef sv_alternateticks( "sv_alternateticks" );
+ ConVarRef ai_strong_optimizations( "ai_strong_optimizations" );
+
+ char misc[ 1024 ];
+ Q_snprintf( misc, sizeof( misc ), "Convars:\n\tskill: %i\n\tnet: rate %i update %i cmd %i latency %i msec\n\thost_thread_mode: %i\n\tsv_alternateticks: %i\n\tai_strong_optimizations: %i\n",
+ skill.GetInt(),
+ cl_rate->GetInt(),
+ (int)cl_updaterate->GetFloat(),
+ (int)cl_cmdrate->GetFloat(),
+ latency,
+ host_thread_mode.GetInt(),
+ sv_alternateticks.GetInt(),
+ ai_strong_optimizations.GetInt()
+ );
+
+ if ( cl.IsActive() && g_ServerGlobalVariables.mapversion != 0 && host_state.worldmodel )
+ {
+ // Note, this won't work in multiplayer, oh well...
+ extern CGlobalVars g_ServerGlobalVariables;
+ char misc2[ 256 ];
+
+ long mapfiletime = g_pFileSystem->GetFileTime( modelloader->GetName( host_state.worldmodel ), "GAME" );
+ if ( !isPublic && mapfiletime != 0L )
+ {
+ char filetimebuf[ 64 ];
+ g_pFileSystem->FileTimeToString( filetimebuf, sizeof( filetimebuf ), mapfiletime );
+
+ Q_snprintf( misc2, sizeof( misc2 ), "Map version: %i\nFile timestamp: %s", g_ServerGlobalVariables.mapversion, filetimebuf );
+ Q_strncat( misc, misc2, sizeof( misc ), COPY_ALL_CHARACTERS );
+ }
+ else
+ {
+ Q_snprintf( misc2, sizeof( misc2 ), "Map version: %i\n", g_ServerGlobalVariables.mapversion );
+ Q_strncat( misc, misc2, sizeof( misc ), COPY_ALL_CHARACTERS );
+ }
+ }
+
+ if ( sv.IsActive() && serverGameClients )
+ {
+ char gamedlldata[ 2048 ];
+ Q_memset( gamedlldata, 0, sizeof( gamedlldata ) );
+ serverGameClients->GetBugReportInfo( gamedlldata, sizeof( gamedlldata ) );
+ Q_strncat( misc, gamedlldata, sizeof( misc ), COPY_ALL_CHARACTERS );
+ }
+
+ {
+ char misc2[ 128 ];
+ Q_snprintf( misc2, sizeof( misc2 ), "gamedir: %s\n", com_gamedir );
+ Q_strncat( misc, misc2, sizeof( misc ), COPY_ALL_CHARACTERS );
+ //Q_snprintf( misc2, sizeof( misc2 ), "game: %s\n", COM_GetModDirectory() );
+ //Q_strncat( misc, misc2, sizeof( misc ), COPY_ALL_CHARACTERS );
+ }
+
+ Msg( "%s", misc );
+
+ if ( !isPublic )
+ {
+ m_pSeverity->GetText( severity, sizeof( severity ) );
+ Msg( "severity %s\n", severity );
+
+ m_pGameArea->GetText( area, sizeof( area ) );
+ Msg( "area %s\n", area );
+
+ m_pMapNumber->GetText( mapnumber, sizeof( mapnumber) );
+ Msg( "map number %s\n", mapnumber);
+
+ m_pPriority->GetText( priority, sizeof( priority ) );
+ Msg( "priority %s\n", priority );
+
+ m_pAssignTo->GetText( assignedto, sizeof( assignedto ) );
+ Msg( "owner %s\n", assignedto );
+ }
+
+ if ( isPublic )
+ {
+ m_pEmail->GetText( email, sizeof( email ) );
+ if ( Q_strlen( email ) > 0 )
+ {
+ Msg( "email %s\n", email );
+ }
+ else
+ {
+ Msg( "Not sending email address\n" );
+ }
+
+ m_pBugReporter->SetOwner( email );
+ char submitter[ 80 ];
+ m_pSubmitterLabel->GetText( submitter, sizeof( submitter ) );
+ m_pBugReporter->SetSubmitter( submitter );
+ }
+ else
+ {
+ m_pBugReporter->SetOwner( m_pBugReporter->GetUserNameForDisplayName( assignedto ) );
+
+ if ( m_bUseNameForSubmitter )
+ {
+ char submitter[ 80 ];
+ m_pSubmitter->GetText( submitter, sizeof( submitter ) );
+ m_pBugReporter->SetSubmitter( submitter );
+ }
+ else
+ {
+ m_pBugReporter->SetSubmitter( NULL );
+ }
+ }
+
+ m_pReportType->GetText( reporttype, sizeof( reporttype ) );
+ Msg( "report type %s\n", reporttype );
+
+ Msg( "submitter %s\n", m_pBugReporter->GetUserName() );
+
+ Msg( "level %s\n", level );
+ Msg( "position %s\n", position );
+ Msg( "orientation %s\n", orientation );
+ Msg( "build %s\n", build );
+
+ if ( m_szSaveGameName[ 0 ] )
+ {
+ Msg( "save file save/%s.sav\n", m_szSaveGameName );
+ }
+ else
+ {
+ Msg( "no save game\n" );
+ }
+
+ if ( m_szScreenShotName[ 0 ] )
+ {
+ Msg( "screenshot screenshots/%s.jpg\n", m_szScreenShotName );
+ }
+ else
+ {
+ Msg( "no screenshot\n" );
+ }
+
+ if ( !isPublic )
+ {
+ if ( m_szBSPName[ 0 ] )
+ {
+ Msg( "bsp file maps/%s.bsp\n", m_szBSPName );
+ }
+
+ if ( m_szVMFName[ 0 ] )
+ {
+ Msg( "vmf file maps/%s.vmf\n", m_szVMFName );
+ }
+
+ if ( m_IncludedFiles.Count() > 0 )
+ {
+ for ( int i = 0; i < m_IncludedFiles.Count(); ++i )
+ {
+ Msg( "Include: %s\n", m_IncludedFiles[ i ].name );
+ }
+ }
+ }
+
+
+ m_pBugReporter->SetTitle( title );
+ m_pBugReporter->SetDescription( desc );
+ m_pBugReporter->SetLevel( level );
+ m_pBugReporter->SetPosition( position );
+ m_pBugReporter->SetOrientation( orientation );
+ m_pBugReporter->SetBuildNumber( build );
+
+ m_pBugReporter->SetSeverity( severity );
+ m_pBugReporter->SetPriority( priority );
+ m_pBugReporter->SetArea( area );
+ m_pBugReporter->SetMapNumber( mapnumber );
+ m_pBugReporter->SetReportType( reporttype );
+ m_pBugReporter->SetDriverInfo( driverinfo );
+ m_pBugReporter->SetMiscInfo( misc );
+
+ m_pBugReporter->SetCSERAddress( m_cserIP );
+ m_pBugReporter->SetExeName( "hl2.exe" );
+ m_pBugReporter->SetGameDirectory( com_gamedir );
+
+ const CPUInformation& pi = *GetCPUInformation();
+
+ m_pBugReporter->SetRAM( GetRam() );
+
+ double fFrequency = pi.m_Speed / 1000000.0;
+
+ m_pBugReporter->SetCPU( (int)fFrequency );
+
+ m_pBugReporter->SetProcessor( pi.m_szProcessorID );
+
+ int nDxLevel = g_pMaterialSystemHardwareConfig->GetDXSupportLevel();
+ int vHigh = nDxLevel / 10;
+ int vLow = nDxLevel - vHigh * 10;
+
+ m_pBugReporter->SetDXVersion( vHigh, vLow, info.m_VendorID, info.m_DeviceID );
+
+ m_pBugReporter->SetOSVersion( osversion );
+
+ m_pBugReporter->ResetIncludedFiles();
+ m_pBugReporter->SetZipAttachmentName( "" );
+
+ char fn[ 512 ];
+ if ( m_pBugReporter->IsPublicUI() )
+ {
+ bool attachedSave = false;
+ bool attachedScreenshot = false;
+
+ CUtlBuffer buginfo( 0, 0, CUtlBuffer::TEXT_BUFFER );
+
+ buginfo.Printf( "Title: %s\n", title );
+ buginfo.Printf( "Description: %s\n\n", desc );
+ buginfo.Printf( "Level: %s\n", level );
+ buginfo.Printf( "Position: %s\n", position );
+ buginfo.Printf( "Orientation: %s\n", orientation );
+ buginfo.Printf( "BuildNumber: %s\n", build );
+ buginfo.Printf( "DriverInfo: %s\n", driverinfo );
+ buginfo.Printf( "Misc: %s\n", misc );
+ buginfo.Printf( "Exe: %s\n", "hl2.exe" );
+ char gd[ 256 ];
+ Q_FileBase( com_gamedir, gd, sizeof( gd ) );
+ buginfo.Printf( "GameDirectory: %s\n", gd );
+ buginfo.Printf( "Ram: %lu\n", GetRam() );
+ buginfo.Printf( "CPU: %i\n", (int)fFrequency );
+ buginfo.Printf( "Processor: %s\n", pi.m_szProcessorID );
+ buginfo.Printf( "DXLevel: %d\n", nDxLevel );
+ buginfo.Printf( "OSVersion: %s\n", osversion );
+ // Terminate it
+ buginfo.PutChar( 0 );
+
+
+ int maxlen = buginfo.TellPut() * 2 + 1;
+ char *fixed = new char [ maxlen ];
+ Assert( fixed );
+ if ( fixed )
+ {
+ Q_memset( fixed, 0, maxlen );
+
+ char *i = (char *)buginfo.Base();
+ char *o = fixed;
+ while ( *i && ( o - fixed ) < maxlen - 1 )
+ {
+ if ( *i == '\n' )
+ {
+ *o++ = '\r';
+ }
+ *o++ = *i++;
+ }
+ *o = '\0';
+
+ AddBugTextToZip( "info.txt", fixed, Q_strlen( fixed ) + 1 );
+
+ delete[] fixed;
+ }
+ else
+ {
+ Sys_Error( "Unable to allocate %i bytes for bug description\n", maxlen );
+ }
+
+ // Only attach .sav files in single player
+ if ( ( cl.m_nMaxClients == 1 ) && m_szSaveGameName[ 0 ] )
+ {
+ Q_snprintf( fn, sizeof( fn ), "save/%s.sav", m_szSaveGameName );
+ Q_FixSlashes( fn );
+ attachedSave = AddFileToZip( fn );
+ }
+ if ( m_szScreenShotName[ 0 ] )
+ {
+ Q_snprintf( fn, sizeof( fn ), "screenshots/%s.jpg", m_szScreenShotName );
+ Q_FixSlashes( fn );
+ attachedScreenshot = AddFileToZip( fn );
+ }
+
+ // End users can only send save games and screenshots to valve
+ // Don't bother uploading any attachment if it's just the info.txt file, either...
+ if ( m_hZip && ( attachedSave || attachedScreenshot ) )
+ {
+ Assert( m_hZip );
+ void *mem;
+ unsigned long len;
+
+ ZipGetMemory( m_hZip, &mem, &len );
+ if ( mem != NULL
+ && len > 0 )
+ {
+ // Store .zip file
+ FileHandle_t fh = g_pFileSystem->Open( "bug.zip", "wb" );
+ if ( FILESYSTEM_INVALID_HANDLE != fh )
+ {
+ g_pFileSystem->Write( mem, len, fh );
+ g_pFileSystem->Close( fh );
+
+ m_pBugReporter->SetZipAttachmentName( "bug.zip" );
+ }
+ }
+ }
+
+ if ( m_hZip )
+ {
+ CloseZip( m_hZip );
+ m_hZip = (HZIP)0;
+ }
+
+ uint64 un64SteamID = m_SteamID.ConvertToUint64();
+ m_pBugReporter->SetSteamUserID( &un64SteamID, sizeof( un64SteamID ) );
+ }
+ else
+ {
+ // Notify other players that we've submitted a bug
+ if ( cl.IsActive() && cl.m_nMaxClients > 1 )
+ {
+ char buf[256];
+ Q_snprintf( buf, sizeof(buf), "say \"Bug Submitted: \'%s\'\"", title );
+ Cbuf_AddText( buf );
+ }
+
+ if ( m_szSaveGameName[ 0 ] )
+ {
+ Q_snprintf( fn, sizeof( fn ), "%s/BugId/%s.sav", GetRepositoryURL(), m_szSaveGameName );
+ Q_FixSlashes( fn );
+ m_pBugReporter->SetSaveGame( fn );
+ }
+ if ( m_szScreenShotName[ 0 ] )
+ {
+ Q_snprintf( fn, sizeof( fn ), "%s/BugId/%s.jpg", GetRepositoryURL(), m_szScreenShotName );
+ Q_FixSlashes( fn );
+ m_pBugReporter->SetScreenShot( fn );
+ }
+ if ( m_szBSPName[ 0 ] )
+ {
+ Q_snprintf( fn, sizeof( fn ), "%s/BugId/%s.bsp", GetRepositoryURL(), m_szBSPName );
+ Q_FixSlashes( fn );
+ m_pBugReporter->SetBSPName( fn );
+ }
+ if ( m_szVMFName[ 0 ] )
+ {
+ Q_snprintf( fn, sizeof( fn ), "%s/BugId/%s.vmf", GetRepositoryURL(), m_szVMFName );
+ Q_FixSlashes( fn );
+ m_pBugReporter->SetVMFName( fn );
+ }
+
+
+ if ( m_IncludedFiles.Count() > 0 )
+ {
+ for ( int i = 0; i < m_IncludedFiles.Count(); ++i )
+ {
+ Q_snprintf( fn, sizeof( fn ), "%s/BugId/%s", GetRepositoryURL(), m_IncludedFiles[ i ].fixedname );
+ Q_FixSlashes( fn );
+ m_pBugReporter->AddIncludedFile( fn );
+ }
+ }
+ }
+
+ Q_strncpy( m_szLevel, level, sizeof( m_szLevel ) );
+
+ if ( m_pBugReporter->IsPublicUI() )
+ {
+ m_pProgressDialog = new CBugReportUploadProgressDialog(NULL, "ProgressDialog", "#Steam_SubmittingBug_WorkingTitle", "#Steam_SubmittingBug_WorkingText" );
+ m_pProgressDialog->Activate();
+ vgui::input()->SetAppModalSurface(m_pProgressDialog->GetVPanel());
+ m_flPauseTime = (float)system()->GetFrameTime() + PUBLIC_BUGREPORT_WAIT_TIME;
+ }
+ else
+ {
+ OnFinishBugReport();
+ }
+}
+
+void CBugUIPanel::OnFinishBugReport()
+{
+ int bugId = -1;
+
+ bool success = m_pBugReporter->CommitBugReport( bugId );
+ if ( success )
+ {
+ // The public UI handles uploading on it's own...
+ if ( !m_pBugReporter->IsPublicUI() )
+ {
+ if ( !UploadBugSubmission( m_szLevel, bugId, m_szSaveGameName, m_szScreenShotName, m_szBSPName, m_szVMFName, m_IncludedFiles ) )
+ {
+ Warning( "Unable to upload saved game and screenshot to bug repository!\n" );
+ success = false;
+ }
+ }
+ }
+ else
+ {
+ Warning( "Unable to post bug report to database\n" );
+ }
+
+ if ( !success )
+ {
+ // Play deny sound
+ DenySound();
+ m_bWaitForFinish = false;
+ }
+ else
+ {
+ // Close
+ WipeData();
+ SuccessSound( bugId );
+
+ if ( !m_pBugReporter->IsPublicUI() )
+ {
+ Close();
+ }
+ }
+}
+
+void NonFileSystem_CreatePath (const char *path)
+{
+ char temppath[512];
+ Q_strncpy( temppath, path, sizeof(temppath) );
+
+ for (char *ofs = temppath+1 ; *ofs ; ofs++)
+ {
+ if (*ofs == '/' || *ofs == '\\')
+ { // create the directory
+ char old = *ofs;
+ *ofs = 0;
+ _mkdir (temppath);
+ *ofs = old;
+ }
+ }
+}
+
+#ifdef LINUX
+#define COPYFILE_ALL 0
+#define BSIZE 65535
+int copyfile( const char *local, const char *remote, void *ignored, int ignoredFlags )
+{
+ ssize_t bytes;
+ int fps, fpd;
+ char buffer[BSIZE];
+
+ if ( (fps = open( local , O_RDONLY ) ) == -1 )
+ return -1;
+
+ if ( (fpd = open( remote, O_WRONLY | O_CREAT | O_TRUNC, 0644 ) ) == -1 )
+ return -1;
+
+ while((bytes = read(fps, buffer, BSIZE)) > 0)
+ write(fpd, buffer, bytes);
+
+ close(fpd);
+ close(fps);
+ return 0;
+}
+#endif
+
+bool CBugUIPanel::UploadFile( char const *local, char const *remote, bool bDeleteLocal )
+{
+ Msg( "Uploading %s to %s\n", local, remote );
+ FileHandle_t hLocal = g_pFileSystem->Open( local, "rb" );
+ if ( FILESYSTEM_INVALID_HANDLE == hLocal )
+ {
+ Warning( "CBugUIPanel::UploadFile: Unable to open local path '%s'\n", local );
+ return false;
+ }
+
+ int nLocalFileSize = g_pFileSystem->Size( hLocal );
+ if ( nLocalFileSize <= 0 )
+ {
+ Warning( "CBugUIPanel::UploadFile: Local file has 0 size '%s'\n", local );
+ g_pFileSystem->Close( hLocal );
+ return false;
+ }
+ NonFileSystem_CreatePath( remote );
+
+ bool bResult;
+ if ( !g_pFileSystem->IsSteam() )
+ {
+ g_pFileSystem->Close( hLocal );
+#ifdef WIN32
+ bResult = CopyFile( local, remote, false ) ? true : false;
+#elif POSIX
+ bResult = (0 == copyfile( local, remote, NULL, COPYFILE_ALL ));
+#else
+#error
+#endif
+ }
+ else
+ {
+
+ FILE *r = fopen( va( "%s", remote ), "wb" );
+ if ( !r )
+ {
+ Warning( "CBugUIPanel::UploadFile: Unable to open remote path '%s'\n", remote );
+ g_pFileSystem->Close( hLocal );
+ return false;
+ }
+
+ int nCopyBufferSize = 2 * 1024 * 1024;
+
+ byte *pCopyBuf = new byte[ nCopyBufferSize ];
+ Assert( pCopyBuf );
+ if ( !pCopyBuf )
+ {
+ Warning( "CBugUIPanel::UploadFile: Unable to allocate copy buffer of %d bytes\n", nCopyBufferSize );
+ fclose( r );
+ g_pFileSystem->Close( hLocal );
+ return false;
+ }
+
+ int nRemainingBytes = nLocalFileSize;
+ while ( nRemainingBytes > 0 )
+ {
+ int nBytesToCopy = MIN( nRemainingBytes, nCopyBufferSize );
+ g_pFileSystem->Read( pCopyBuf, nBytesToCopy, hLocal );
+ fwrite( pCopyBuf, nBytesToCopy, 1, r );
+ nRemainingBytes -= nBytesToCopy;
+ }
+
+ fclose( r );
+ g_pFileSystem->Close( hLocal );
+
+ delete[] pCopyBuf;
+
+ bResult = true;
+ }
+
+ if ( !bResult )
+ {
+ Warning( "Failed to upload %s, error %d\n", local, GetLastError() );
+ }
+ else if ( bDeleteLocal )
+ {
+ unlink( local );
+ }
+ return bResult;
+}
+
+CCallQueue g_UploadQueue;
+
+bool CBugUIPanel::UploadBugSubmission( char const *levelname, int bugId, char const *savefile, char const *screenshot, char const *bsp, char const *vmf,
+ CUtlVector< includedfile >& files )
+{
+ bool bret = true;
+ bool bAsync = bugreporter_uploadasync.GetBool();
+
+ char localfile[ 512 ];
+ char remotefile[ 512 ];
+
+ if ( savefile && savefile[ 0 ] )
+ {
+ Q_snprintf( localfile, sizeof( localfile ), "%s/save/%s.sav", com_gamedir, savefile );
+ Q_snprintf( remotefile, sizeof( remotefile ), "%s/%s.sav", GetSubmissionURL(bugId), savefile );
+ Q_FixSlashes( localfile );
+ Q_FixSlashes( remotefile );
+
+ g_UploadQueue.QueueCall( this, &CBugUIPanel::UploadFile, CUtlEnvelope<const char *>(localfile), CUtlEnvelope<const char *>(remotefile), false );
+ }
+
+ if ( screenshot && screenshot[ 0 ] )
+ {
+ Q_snprintf( localfile, sizeof( localfile ), "%s/screenshots/%s.jpg", com_gamedir, screenshot );
+ Q_snprintf( remotefile, sizeof( remotefile ), "%s/%s.jpg", GetSubmissionURL(bugId), screenshot );
+ Q_FixSlashes( localfile );
+ Q_FixSlashes( remotefile );
+
+ g_UploadQueue.QueueCall( this, &CBugUIPanel::UploadFile, CUtlEnvelope<const char *>(localfile), CUtlEnvelope<const char *>(remotefile), false );
+ }
+
+ if ( bsp && bsp[ 0 ] )
+ {
+ Q_snprintf( localfile, sizeof( localfile ), "maps/%s.bsp", levelname );
+ char *pszMapPath;
+ FileHandle_t hBsp = g_pFileSystem->OpenEx( localfile, "rb", 0, 0, &pszMapPath );
+ if ( !hBsp )
+ {
+ Q_snprintf( localfile, sizeof( localfile ), "%s/maps/%s.bsp", com_gamedir, levelname );
+ }
+ else
+ {
+ V_strncpy( localfile, pszMapPath, sizeof( localfile ) );
+ delete pszMapPath;
+ g_pFileSystem->Close( hBsp );
+ }
+ Q_snprintf( remotefile, sizeof( remotefile ), "%s/%s.bsp", GetSubmissionURL(bugId), bsp );
+ Q_FixSlashes( localfile );
+ Q_FixSlashes( remotefile );
+
+ g_UploadQueue.QueueCall( this, &CBugUIPanel::UploadFile, CUtlEnvelope<const char *>(localfile), CUtlEnvelope<const char *>(remotefile), false );
+ }
+
+ if ( vmf && vmf[ 0 ] )
+ {
+ Q_snprintf( localfile, sizeof( localfile ), "%s/%s.vmf", m_szVMFContentDirFullpath, levelname );
+ if ( g_pFileSystem->FileExists( localfile, NULL ) )
+ {
+ Q_snprintf( remotefile, sizeof( remotefile ), "%s/%s.vmf", GetSubmissionURL(bugId), vmf );
+ Q_FixSlashes( localfile );
+ Q_FixSlashes( remotefile );
+
+ g_UploadQueue.QueueCall( this, &CBugUIPanel::UploadFile, CUtlEnvelope<const char *>(localfile), CUtlEnvelope<const char *>(remotefile), false );
+ }
+ else
+ {
+ Msg( "Unable to locate .vmf file %s\n", localfile );
+ }
+ }
+
+ if ( files.Count() > 0 )
+ {
+ bAsync = false;
+ int c = files.Count();
+ for ( int i = 0 ; i < c; ++i )
+ {
+ Q_snprintf( localfile, sizeof( localfile ), "%s/%s", com_gamedir, files[ i ].name );
+ Q_snprintf( remotefile, sizeof( remotefile ), "%s/%s", GetSubmissionURL(bugId), files[ i ].fixedname );
+ Q_FixSlashes( localfile );
+ Q_FixSlashes( remotefile );
+
+ g_UploadQueue.QueueCall( this, &CBugUIPanel::UploadFile, CUtlEnvelope<const char *>(localfile), CUtlEnvelope<const char *>(remotefile), false );
+ }
+ }
+
+ if ( !bAsync )
+ {
+ g_UploadQueue.CallQueued();
+ }
+ else
+ {
+ ThreadExecuteSolo( "BugUpload", &g_UploadQueue, &CCallQueue::CallQueued );
+ }
+
+ return bret;
+}
+
+void CBugUIPanel::Close()
+{
+ WipeData();
+ BaseClass::Close();
+}
+
+void CBugUIPanel::OnCommand( char const *command )
+{
+ if ( !Q_strcasecmp( command, "submit" ) )
+ {
+ OnSubmit();
+ }
+ else if ( !Q_strcasecmp( command, "cancel" ) )
+ {
+ Close();
+ WipeData();
+ }
+ else if ( !Q_strcasecmp( command, "snapshot" ) )
+ {
+ OnTakeSnapshot();
+ }
+ else if ( !Q_strcasecmp( command, "savegame" ) )
+ {
+ OnSaveGame();
+
+ //Adrian: We always want the BSP you used when saving the game.
+ //But only if you're not the public bug reporter!
+ if ( bugreporter_includebsp.GetBool() &&
+ m_pBugReporter->IsPublicUI() == false )
+ {
+ OnSaveBSP();
+ }
+ }
+ else if ( !Q_strcasecmp( command, "savebsp" ) )
+ {
+ OnSaveBSP();
+ }
+ else if ( !Q_strcasecmp( command, "savevmf" ) )
+ {
+ OnSaveVMF();
+ }
+ else if ( !Q_strcasecmp( command, "clearform" ) )
+ {
+ OnClearForm();
+ }
+ else if ( !Q_strcasecmp( command, "addfile" ) )
+ {
+ OnIncludeFile();
+ }
+ else if ( !Q_strcasecmp( command, "clearfiles" ) )
+ {
+ OnClearIncludedFiles();
+ }
+ else
+ {
+ BaseClass::OnCommand( command );
+ }
+}
+
+void CBugUIPanel::OnClearForm()
+{
+ WipeData();
+
+ m_pTitle->SetText( "" );
+ m_pDescription->SetText( "" );
+
+ m_pAssignTo->ActivateItem( 0 );
+ m_pSeverity->ActivateItem( 0 );
+ m_pReportType->ActivateItem( 0 );
+ m_pPriority->ActivateItem( 2 );
+ m_pGameArea->ActivateItem( 0 );
+ m_pMapNumber->ActivateItem( 0 );
+}
+
+void CBugUIPanel::DetermineSubmitterName()
+{
+ if ( !m_pBugReporter )
+ return;
+
+ if ( m_pBugReporter->IsPublicUI() )
+ {
+ m_pSubmitter->SetText( "PublicUser" );
+ m_bCanSeeRepository = true;
+ m_bCanSubmit = true;
+ }
+ else
+ {
+ Color clr( 100, 200, 255, 255 );
+
+ const char *pUserName = m_pBugReporter->GetUserName();
+ const char *pUserDisplayName = m_pBugReporter->GetUserName_Display();
+
+ if ( pUserName && pUserName[0] && pUserDisplayName && pUserDisplayName[0] )
+ {
+ ConColorMsg( clr, "Username '%s' -- '%s'\n", pUserName, pUserDisplayName );
+ }
+ else if ( cl.IsActive() )
+ {
+ m_bUseNameForSubmitter = true;
+ pUserDisplayName = cl_name.GetString();
+ ConColorMsg( clr, "Using '%s' as bug submission name.\n", pUserDisplayName );
+ }
+ else
+ {
+ ConColorMsg( clr, "Failed to determine bug submission name.\n" );
+ }
+
+ // See if we can see the bug repository right now
+ char fn[ 512 ];
+ Q_snprintf( fn, sizeof( fn ), "%s/%s", GetRepositoryURL(), REPOSITORY_VALIDATION_FILE );
+ Q_FixSlashes( fn );
+
+ FILE *fp = fopen( fn, "rb" );
+ if ( fp )
+ {
+ ConColorMsg( clr, "Bug Repository '%s'\n", GetRepositoryURL() );
+ fclose( fp );
+
+ m_bCanSeeRepository = true;
+ }
+ else
+ {
+ Warning( "Unable to see '%s', check permissions and network connectivity\n", fn );
+ m_bCanSubmit = false;
+ }
+
+ m_pSubmitter->SetText( pUserDisplayName );
+ }
+}
+
+void CBugUIPanel::PopulateControls()
+{
+ if ( !m_pBugReporter )
+ return;
+
+ m_pAssignTo->DeleteAllItems();
+ int i;
+ int c = m_pBugReporter->GetDisplayNameCount();
+ int defitem = 0;
+ for ( i = 0; i < c; i++ )
+ {
+ char const *name = m_pBugReporter->GetDisplayName( i );
+ if (!V_strcasecmp(name, "Triage"))
+ defitem = i;
+ m_pAssignTo->AddItem(name , NULL );
+ }
+ m_pAssignTo->ActivateItem( defitem );
+
+ defitem = 0;
+ m_pSeverity->DeleteAllItems();
+ c = m_pBugReporter->GetSeverityCount();
+ for ( i = 0; i < c; i++ )
+ {
+ char const *severity = m_pBugReporter->GetSeverity( i );
+ if (!V_strcasecmp(severity, "Zero"))
+ defitem = i;
+ m_pSeverity->AddItem( severity, NULL );
+ }
+ m_pSeverity->ActivateItem( defitem );
+
+ m_pReportType->DeleteAllItems();
+ c = m_pBugReporter->GetReportTypeCount();
+ for ( i = 0; i < c; i++ )
+ {
+ m_pReportType->AddItem( m_pBugReporter->GetReportType( i ), NULL );
+ }
+ m_pReportType->ActivateItem( 0 );
+
+ m_pPriority->DeleteAllItems();
+ c = m_pBugReporter->GetPriorityCount();
+ for ( i = 0; i < c; i++ )
+ {
+ m_pPriority->AddItem( m_pBugReporter->GetPriority( i ), NULL );
+ }
+ m_pPriority->ActivateItem( 2 );
+
+ m_pGameArea->DeleteAllItems();
+ c = m_pBugReporter->GetAreaCount();
+ for ( i = 0; i < c; i++ )
+ {
+ m_pGameArea->AddItem( m_pBugReporter->GetArea( i ), NULL );
+ }
+
+ int area_index = GetArea();
+ m_pGameArea->ActivateItem( area_index );
+}
+
+char const *CBugUIPanel::GetSubmitter()
+{
+ if ( !m_bCanSubmit )
+ return "";
+ return m_pBugReporter->GetUserName();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBugUIPanel::DenySound()
+{
+ Cbuf_AddText( va( "play %s\n", DENY_SOUND ) );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBugUIPanel::SuccessSound( int bugId )
+{
+ Color clr( 50, 255, 100, 255 );
+
+ if ( m_pBugReporter && m_pBugReporter->IsPublicUI() )
+ {
+ ConColorMsg( clr, "Bug submission succeeded\n" );
+ }
+ else
+ {
+ ConColorMsg( clr, "Bug submission succeeded for bug (%i)\n", bugId );
+ }
+ Cbuf_AddText( va( "play %s\n", SUCCEED_SOUND ) );
+}
+
+void CBugUIPanel::OnKeyCodePressed(KeyCode code)
+{
+ if ( code == KEY_ESCAPE ||
+ GetBaseButtonCode( code ) == KEY_XBUTTON_B )
+ {
+ Close();
+ WipeData();
+ }
+ else
+ {
+ BaseClass::OnKeyCodePressed( code );
+ }
+}
+
+
+// Load game-specific bug reporter defaults as params
+void CBugUIPanel::ParseDefaultParams( void )
+{
+ const char *szDefaults = "scripts/bugreporter_defaults.txt";
+
+ FileHandle_t hLocal = g_pFileSystem->Open( szDefaults, "rb" );
+ if ( FILESYSTEM_INVALID_HANDLE == hLocal )
+ {
+ return;
+ }
+
+ // load file into a null-terminated buffer
+ int fileSize = g_pFileSystem->Size(hLocal);
+ char *buffer = (char*)MemAllocScratch(fileSize + 1);
+
+ Assert(buffer);
+
+ g_pFileSystem->Read(buffer, fileSize, hLocal); // read into local buffer
+ buffer[fileSize] = 0; // null terminate file as EOF
+ g_pFileSystem->Close( hLocal ); // close file after reading
+
+ char token[64];
+ const char *pfile = COM_ParseFile(buffer, token, sizeof( token ) );
+
+ while ( pfile )
+ {
+ bool success = AutoFillToken( token, false );
+ if ( !success )
+ {
+ // Try partials
+ success = AutoFillToken( token, true );
+ if ( !success )
+ {
+ Msg( "Unable to determine where to set default bug parameter '%s', ignoring...\n", token );
+ }
+ }
+
+ pfile = COM_ParseFile(pfile, token, sizeof( token ) );
+ }
+}
+
+void CBugUIPanel::ParseCommands( const CCommand &args )
+{
+ if ( !m_bCanSubmit )
+ return;
+
+ for ( int i = 1; i < args.ArgC(); i++ )
+ {
+ bool success = AutoFillToken( args[ i ], false );
+ if ( !success )
+ {
+ // Try partials
+ success = AutoFillToken( args[ i ], true );
+ if ( !success )
+ {
+ Msg( "Unable to determine where to set bug parameter '%s', ignoring...\n", args[ i ] );
+ }
+ }
+ }
+}
+
+bool CBugUIPanel::Compare( char const *value, char const *token, bool partial )
+{
+ if ( !partial )
+ {
+ if ( !Q_stricmp( value, token ) )
+ return true;
+ }
+ else
+ {
+ if ( Q_stristr( value, token ) )
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+bool CBugUIPanel::AutoFillToken( char const *token, bool partial )
+{
+ // Search for token in all dropdown lists and fill it in if we find it
+ if ( !m_pBugReporter )
+ return true;
+
+ int i;
+ int c;
+
+ c = m_pBugReporter->GetDisplayNameCount();
+ for ( i = 0; i < c; i++ )
+ {
+ if ( Compare( m_pBugReporter->GetDisplayName( i ), token, partial ) )
+ {
+ m_pAssignTo->ActivateItem( i );
+ return true;
+ }
+ }
+
+ c = m_pBugReporter->GetSeverityCount();
+ for ( i = 0; i < c; i++ )
+ {
+ if ( Compare( m_pBugReporter->GetSeverity( i ), token, partial ) )
+ {
+ m_pSeverity->ActivateItem( i );
+ return true;
+ }
+ }
+
+ c = m_pBugReporter->GetReportTypeCount();
+ for ( i = 0; i < c; i++ )
+ {
+ if ( Compare( m_pBugReporter->GetReportType( i ), token, partial ) )
+ {
+ m_pReportType->ActivateItem( i );
+ return true;
+ }
+ }
+
+ c = m_pBugReporter->GetPriorityCount();
+ for ( i = 0; i < c; i++ )
+ {
+ if ( Compare( m_pBugReporter->GetPriority( i ), token, partial ) )
+ {
+ m_pPriority->ActivateItem( i );
+ return true;
+ }
+ }
+
+ c = m_pBugReporter->GetAreaCount();
+ for ( i = 0; i < c; i++ )
+ {
+ if ( Compare( m_pBugReporter->GetArea( i ), token, partial ) )
+ {
+ m_pGameArea->ActivateItem( i );
+ return true;
+ }
+ }
+
+ if ( !Q_stricmp( token, "screenshot" ) )
+ {
+ m_fAutoAddScreenshot = eAutoAddScreenshot_Add;
+ return true;
+ }
+
+ if ( !Q_stricmp( token, "noscreenshot" ) )
+ {
+ m_fAutoAddScreenshot = eAutoAddScreenshot_DontAdd;
+ return true;
+ }
+
+ return false;
+}
+
+void CBugUIPanel::CheckContinueQueryingSteamForCSERList()
+{
+ if ( !m_bQueryingSteamForCSER || !Steam3Client().SteamUtils() )
+ {
+ return;
+ }
+
+ uint32 unIP;
+ uint16 usPort;
+ Steam3Client().SteamUtils()->GetCSERIPPort( &unIP, &usPort );
+ if ( unIP )
+ {
+ m_cserIP.SetIPAndPort( unIP, usPort );
+ m_bQueryingSteamForCSER = false;
+ }
+}
+
+static CBugUIPanel *g_pBugUI = NULL;
+
+class CEngineBugReporter : public IEngineBugReporter
+{
+public:
+ virtual void Init( void );
+ virtual void Shutdown( void );
+
+ virtual void InstallBugReportingUI( vgui::Panel *parent, IEngineBugReporter::BR_TYPE type );
+ virtual bool ShouldPause() const;
+
+ virtual bool IsVisible() const; //< true iff the bug panel is active and on screen right now
+
+ void Restart( IEngineBugReporter::BR_TYPE type );
+private:
+ PHandle m_ParentPanel;
+};
+
+static CEngineBugReporter g_BugReporter;
+IEngineBugReporter *bugreporter = &g_BugReporter;
+
+CON_COMMAND( _bugreporter_restart, "Restarts bug reporter .dll" )
+{
+ if ( args.ArgC() <= 1 )
+ {
+ Msg( "__bugreporter_restart <internal | external | autoselect>\n" );
+ return;
+ }
+
+ IEngineBugReporter::BR_TYPE type = IEngineBugReporter::BR_PUBLIC;
+
+ if ( !Q_stricmp( args.Arg( 1 ), "internal" ) )
+ {
+ type = IEngineBugReporter::BR_INTERNAL;
+ }
+ else if ( !Q_stricmp( args.Arg( 1 ), "autoselect" ) )
+ {
+ type = IEngineBugReporter::BR_AUTOSELECT;
+ }
+
+ g_BugReporter.Restart( type );
+}
+
+void CEngineBugReporter::Init( void )
+{
+ m_ParentPanel = 0;
+}
+
+void CEngineBugReporter::Shutdown( void )
+{
+ if ( g_pBugUI )
+ {
+ g_pBugUI->Shutdown();
+ g_pBugUI = NULL;
+ }
+}
+
+void CEngineBugReporter::InstallBugReportingUI( vgui::Panel *parent, IEngineBugReporter::BR_TYPE type )
+{
+ if ( g_pBugUI )
+ return;
+
+ bool bIsPublic = true;
+
+ char fn[ 512 ];
+
+ Q_snprintf( fn, sizeof( fn ), "%s%s", GetInternalBugReporterDLL(), DLL_EXT_STRING );
+
+ bool bCanUseInternal = g_pFileSystem->FileExists( fn, "EXECUTABLE_PATH" );
+
+ switch ( type )
+ {
+ case IEngineBugReporter::BR_PUBLIC:
+ // Nothing
+ break;
+ default:
+ case IEngineBugReporter::BR_AUTOSELECT:
+ {
+ // check
+ bIsPublic = phonehome->IsExternalBuild() ? true : false;
+ if ( bCanUseInternal )
+ {
+ // if command line param specifies internal, use that
+ if ( CommandLine()->FindParm( "-internalbuild" ) )
+ {
+ bIsPublic = false;
+ }
+#if !defined( _X360 )
+ // otherwise, if Steam is running and connected to beta, autoselect the internal bug db
+ else if ( k_EUniverseBeta == GetSteamUniverse() )
+ {
+ bIsPublic = false;
+ }
+#endif
+ }
+ }
+ break;
+
+ case IEngineBugReporter::BR_INTERNAL:
+ {
+ if ( bCanUseInternal )
+ {
+ bIsPublic = false;
+ }
+ }
+ break;
+ }
+
+ g_pBugUI = new CBugUIPanel( bIsPublic, parent );
+ Assert( g_pBugUI );
+
+ m_ParentPanel = parent;
+}
+
+void CEngineBugReporter::Restart( IEngineBugReporter::BR_TYPE type )
+{
+ Shutdown();
+ Msg( "Changing to bugreporter(%s)\n",
+ ( type == IEngineBugReporter::BR_AUTOSELECT ) ?
+ ( "autoselect" ) :
+ ( ( type == IEngineBugReporter::BR_PUBLIC ) ?
+ "public" :
+ "valve" ) );
+ InstallBugReportingUI( m_ParentPanel, type );
+}
+
+bool CEngineBugReporter::ShouldPause() const
+{
+ return g_pBugUI && ( g_pBugUI->IsVisible() || g_pBugUI->IsTakingSnapshot() ) && (cl.m_nMaxClients == 1);
+}
+
+
+bool CEngineBugReporter::IsVisible() const
+{
+ return g_pBugUI && g_pBugUI->IsVisible();
+}
+
+CON_COMMAND_F( bug, "Show/hide the bug reporting UI.", FCVAR_DONTRECORD )
+{
+ if ( !g_pBugUI )
+ return;
+
+ bool bWasVisible = g_pBugUI->IsVisible();
+
+ if ( bWasVisible )
+ {
+ // hide
+ g_pBugUI->Close();
+ }
+
+ // make sure the gameUI is open so the bugreporter is visible
+ EngineVGui()->ActivateGameUI();
+
+ g_pBugUI->Activate();
+
+
+ g_pBugUI->ParseDefaultParams();
+ if (args.ArgC() > 1 )
+ {
+ g_pBugUI->ParseCommands( args );
+ }
+}
+
+
+int CBugUIPanel::GetArea()
+{
+ char mapname[256] = "";
+ int iNewTitleLength = 80;
+
+ if ( host_state.worldmodel )
+ {
+ CL_SetupMapName( modelloader->GetName( host_state.worldmodel ), mapname, sizeof( mapname ) );
+ iNewTitleLength = (80 - (strlen( mapname )+2));
+ }
+ m_pTitle->SetMaximumCharCount( iNewTitleLength );
+
+ char *gamedir = com_gamedir;
+ gamedir = Q_strrchr( gamedir, CORRECT_PATH_SEPARATOR ) + 1;
+
+ for ( int i = 0; i < m_pBugReporter->GetAreaMapCount(); i++ )
+ {
+ char szAreaMap[MAX_PATH];
+ V_strcpy_safe( szAreaMap, m_pBugReporter->GetAreaMap( i ) );
+ char *pszAreaDir = Q_strrchr( szAreaMap, '@' );
+ char *pszAreaPrefix = Q_strrchr( szAreaMap, '%' );
+ int iDirLength = 0;
+ if ( pszAreaDir && pszAreaPrefix )
+ {
+ iDirLength = pszAreaPrefix - pszAreaDir - 1;
+ pszAreaDir++;
+ pszAreaPrefix++;
+ }
+ else if ( pszAreaDir && !pszAreaPrefix )
+ {
+ pszAreaDir++;
+ iDirLength = Q_strlen( szAreaMap ) - (pszAreaDir - szAreaMap);
+ }
+ else
+ {
+ return 0;
+ }
+
+ char szDirectory[MAX_PATH];
+ Q_memmove( szDirectory, pszAreaDir, iDirLength );
+ szDirectory[iDirLength] = 0;
+
+ if ( pszAreaDir && pszAreaPrefix )
+ {
+ if ( !Q_strcmp( szDirectory, gamedir)
+ && Q_strstr( mapname, pszAreaPrefix ) )
+ {
+ return i+1;
+ }
+ }
+ else if ( pszAreaDir && !pszAreaPrefix )
+ {
+ if ( !Q_strcmp( szDirectory, gamedir ) )
+ {
+ return i+1;
+ }
+ }
+ }
+ return 0;
+}