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.
3092 lines
74 KiB
3092 lines
74 KiB
//========= 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(PLATFORM_BSD) |
|
# include <sys/sysctl.h> |
|
# include <sys/types.h> |
|
# include <fcntl.h> |
|
# define HW_MEMSIZE HW_PHYSMEM |
|
#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) || defined(PLATFORM_BSD) |
|
#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) || defined(PLATFORM_BSD) |
|
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 ); |
|
} |
|
#elif PLATFORM_BSD |
|
#ifdef __FreeBSD__ |
|
osversion = (char *)"FreeBSD"; |
|
#else |
|
osversion = (char *)"*BSD"; |
|
#endif |
|
#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 ]; |
|
|
|
time_t 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; |
|
} |
|
} |
|
} |
|
|
|
#if defined(LINUX) || defined(PLATFORM_BSD) |
|
#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; |
|
}
|
|
|