mirror of
https://github.com/s1lentq/ReGameDLL_CS.git
synced 2024-12-28 15:45:41 +03:00
303 lines
6.8 KiB
C++
303 lines
6.8 KiB
C++
|
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||
|
//
|
||
|
// Purpose:
|
||
|
//
|
||
|
// $NoKeywords: $
|
||
|
//
|
||
|
//===========================================================================//
|
||
|
|
||
|
#include "precompiled.h"
|
||
|
#include "resource.h"
|
||
|
|
||
|
class CDialogInitInfo
|
||
|
{
|
||
|
public:
|
||
|
const char *m_pFilename;
|
||
|
int m_iLine;
|
||
|
const char *m_pExpression;
|
||
|
};
|
||
|
|
||
|
class CAssertDisable
|
||
|
{
|
||
|
public:
|
||
|
char m_Filename[MAX_OSPATH];
|
||
|
|
||
|
// If these are not -1, then this CAssertDisable only disables asserts on lines between
|
||
|
// these values (inclusive).
|
||
|
int m_LineMin;
|
||
|
int m_LineMax;
|
||
|
|
||
|
// Decremented each time we hit this assert and ignore it, until it's 0.
|
||
|
// Then the CAssertDisable is removed.
|
||
|
// If this is -1, then we always ignore this assert.
|
||
|
int m_nIgnoreTimes;
|
||
|
|
||
|
CAssertDisable *m_pNext;
|
||
|
};
|
||
|
|
||
|
static CDialogInitInfo g_Info{};
|
||
|
static bool g_bAssertsEnabled = true;
|
||
|
static CAssertDisable *g_pAssertDisables = nullptr;
|
||
|
|
||
|
// Set to true if they want to break in the debugger
|
||
|
static bool g_bBreak = false;
|
||
|
|
||
|
// Internal functions
|
||
|
static HINSTANCE g_hTier0Instance = nullptr;
|
||
|
BOOL WINAPI DllMain(
|
||
|
HINSTANCE hinstDLL, // handle to the DLL module
|
||
|
DWORD fdwReason, // reason for calling function
|
||
|
LPVOID lpvReserved // reserved
|
||
|
)
|
||
|
{
|
||
|
g_hTier0Instance = hinstDLL;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
static bool IsDebugBreakEnabled()
|
||
|
{
|
||
|
static bool bResult = (Q_strstr(Plat_GetCommandLine(), "-debugbreak") != nullptr);
|
||
|
return bResult;
|
||
|
}
|
||
|
|
||
|
static bool AreAssertsDisabled()
|
||
|
{
|
||
|
static bool bResult = (Q_strstr(Plat_GetCommandLine(), "-noassert") != nullptr);
|
||
|
return bResult;
|
||
|
}
|
||
|
|
||
|
static bool AreAssertsEnabledInFileLine(const char *pFilename, int iLine)
|
||
|
{
|
||
|
CAssertDisable **pPrev = &g_pAssertDisables;
|
||
|
CAssertDisable *pNext = nullptr;
|
||
|
|
||
|
for (CAssertDisable *pCur = g_pAssertDisables; pCur; pCur = pNext)
|
||
|
{
|
||
|
pNext = pCur->m_pNext;
|
||
|
|
||
|
if (Q_stricmp(pFilename, pCur->m_Filename) == 0)
|
||
|
{
|
||
|
// Are asserts disabled in the whole file?
|
||
|
bool bAssertsEnabled = true;
|
||
|
if (pCur->m_LineMin == -1 && pCur->m_LineMax == -1)
|
||
|
bAssertsEnabled = false;
|
||
|
|
||
|
// Are asserts disabled on the specified line?
|
||
|
if (iLine >= pCur->m_LineMin && iLine <= pCur->m_LineMax)
|
||
|
bAssertsEnabled = false;
|
||
|
|
||
|
if (!bAssertsEnabled)
|
||
|
{
|
||
|
// If this assert is only disabled for the next N times, then countdown..
|
||
|
if (pCur->m_nIgnoreTimes > 0)
|
||
|
{
|
||
|
pCur->m_nIgnoreTimes--;
|
||
|
|
||
|
if (pCur->m_nIgnoreTimes == 0)
|
||
|
{
|
||
|
// Remove this one from the list.
|
||
|
*pPrev = pNext;
|
||
|
delete pCur;
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pPrev = &pCur->m_pNext;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
CAssertDisable *CreateNewAssertDisable(const char *pFilename)
|
||
|
{
|
||
|
CAssertDisable *pDisable = new CAssertDisable;
|
||
|
pDisable->m_pNext = g_pAssertDisables;
|
||
|
g_pAssertDisables = pDisable;
|
||
|
|
||
|
pDisable->m_LineMin = pDisable->m_LineMax = -1;
|
||
|
pDisable->m_nIgnoreTimes = -1;
|
||
|
|
||
|
Q_strlcpy(pDisable->m_Filename, g_Info.m_pFilename);
|
||
|
|
||
|
return pDisable;
|
||
|
}
|
||
|
|
||
|
void IgnoreAssertsInCurrentFile()
|
||
|
{
|
||
|
CreateNewAssertDisable(g_Info.m_pFilename);
|
||
|
}
|
||
|
|
||
|
CAssertDisable *IgnoreAssertsNearby(int nRange)
|
||
|
{
|
||
|
CAssertDisable *pDisable = CreateNewAssertDisable(g_Info.m_pFilename);
|
||
|
pDisable->m_LineMin = g_Info.m_iLine - nRange;
|
||
|
pDisable->m_LineMax = g_Info.m_iLine - nRange;
|
||
|
return pDisable;
|
||
|
}
|
||
|
|
||
|
static int g_iLastLineRange = 5;
|
||
|
static int g_nLastIgnoreNumTimes = 1;
|
||
|
|
||
|
int CALLBACK AssertDialogProc(
|
||
|
HWND hDlg, // handle to dialog box
|
||
|
UINT uMsg, // message
|
||
|
WPARAM wParam, // first message parameter
|
||
|
LPARAM lParam // second message parameter
|
||
|
)
|
||
|
{
|
||
|
switch (uMsg)
|
||
|
{
|
||
|
case WM_INITDIALOG:
|
||
|
{
|
||
|
SetDlgItemText(hDlg, IDC_ASSERT_MSG_CTRL, g_Info.m_pExpression);
|
||
|
SetDlgItemText(hDlg, IDC_FILENAME_CONTROL, g_Info.m_pFilename);
|
||
|
|
||
|
SetDlgItemInt(hDlg, IDC_LINE_CONTROL, g_Info.m_iLine, false);
|
||
|
SetDlgItemInt(hDlg, IDC_IGNORE_NUMLINES, g_iLastLineRange, false);
|
||
|
SetDlgItemInt(hDlg, IDC_IGNORE_NUMTIMES, g_nLastIgnoreNumTimes, false);
|
||
|
|
||
|
// Center the dialog.
|
||
|
RECT rcDlg, rcDesktop;
|
||
|
GetWindowRect(hDlg, &rcDlg);
|
||
|
GetWindowRect(GetDesktopWindow(), &rcDesktop);
|
||
|
SetWindowPos(hDlg, HWND_TOP,
|
||
|
((rcDesktop.right - rcDesktop.left) - (rcDlg.right - rcDlg.left)) / 2,
|
||
|
((rcDesktop.bottom - rcDesktop.top) - (rcDlg.bottom - rcDlg.top)) / 2,
|
||
|
0, 0,
|
||
|
SWP_NOSIZE
|
||
|
);
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
case WM_COMMAND:
|
||
|
{
|
||
|
switch (LOWORD(wParam))
|
||
|
{
|
||
|
// Ignore this assert N times
|
||
|
case IDC_IGNORE_THIS:
|
||
|
{
|
||
|
BOOL bTranslated = false;
|
||
|
UINT value = GetDlgItemInt(hDlg, IDC_IGNORE_NUMTIMES, &bTranslated, false);
|
||
|
if (bTranslated && value > 1)
|
||
|
{
|
||
|
CAssertDisable *pDisable = IgnoreAssertsNearby(0);
|
||
|
pDisable->m_nIgnoreTimes = value - 1;
|
||
|
g_nLastIgnoreNumTimes = value;
|
||
|
}
|
||
|
|
||
|
EndDialog(hDlg, 0);
|
||
|
return TRUE;
|
||
|
}
|
||
|
case IDC_IGNORE_NEARBY:
|
||
|
{
|
||
|
BOOL bTranslated = false;
|
||
|
UINT value = GetDlgItemInt(hDlg, IDC_IGNORE_NUMLINES, &bTranslated, false);
|
||
|
if (!bTranslated || value < 1)
|
||
|
return TRUE;
|
||
|
|
||
|
IgnoreAssertsNearby(value);
|
||
|
EndDialog(hDlg, 0);
|
||
|
return TRUE;
|
||
|
}
|
||
|
case IDC_IGNORE_FILE:
|
||
|
IgnoreAssertsInCurrentFile();
|
||
|
EndDialog(hDlg, 0);
|
||
|
return TRUE;
|
||
|
// Always ignore this assert
|
||
|
case IDC_IGNORE_ALWAYS:
|
||
|
IgnoreAssertsNearby(0);
|
||
|
EndDialog(hDlg, 0);
|
||
|
return TRUE;
|
||
|
case IDC_IGNORE_ALL:
|
||
|
g_bAssertsEnabled = false;
|
||
|
EndDialog(hDlg, 0);
|
||
|
return TRUE;
|
||
|
case IDC_BREAK:
|
||
|
g_bBreak = true;
|
||
|
EndDialog(hDlg, 0);
|
||
|
return TRUE;
|
||
|
}
|
||
|
case WM_KEYDOWN:
|
||
|
{
|
||
|
// Escape?
|
||
|
if (wParam == 2)
|
||
|
{
|
||
|
// Ignore this assert
|
||
|
EndDialog(hDlg, 0);
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
static HWND g_hBestParentWindow = nullptr;
|
||
|
|
||
|
static BOOL CALLBACK ParentWindowEnumProc(
|
||
|
HWND hWnd, // handle to parent window
|
||
|
LPARAM lParam // application-defined value
|
||
|
)
|
||
|
{
|
||
|
if (IsWindowVisible(hWnd))
|
||
|
{
|
||
|
DWORD procID;
|
||
|
GetWindowThreadProcessId(hWnd, &procID);
|
||
|
if (procID == (DWORD)lParam)
|
||
|
{
|
||
|
g_hBestParentWindow = hWnd;
|
||
|
return FALSE; // don't iterate any more.
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
static HWND FindLikelyParentWindow()
|
||
|
{
|
||
|
// Enumerate top-level windows and take the first visible one with our processID.
|
||
|
g_hBestParentWindow = nullptr;
|
||
|
EnumWindows(ParentWindowEnumProc, GetCurrentProcessId());
|
||
|
return g_hBestParentWindow;
|
||
|
}
|
||
|
|
||
|
bool DoNewAssertDialog(const char *pFilename, int line, const char *pExpression)
|
||
|
{
|
||
|
if (AreAssertsDisabled())
|
||
|
return false;
|
||
|
|
||
|
// If they have the old mode enabled (always break immediately), then just break right into
|
||
|
// the debugger like we used to do.
|
||
|
if (IsDebugBreakEnabled())
|
||
|
return true;
|
||
|
|
||
|
// Have ALL Asserts been disabled?
|
||
|
if (!g_bAssertsEnabled)
|
||
|
return false;
|
||
|
|
||
|
// Has this specific Assert been disabled?
|
||
|
if (!AreAssertsEnabledInFileLine(pFilename, line))
|
||
|
return false;
|
||
|
|
||
|
// Now create the dialog.
|
||
|
g_Info.m_pFilename = pFilename;
|
||
|
g_Info.m_iLine = line;
|
||
|
g_Info.m_pExpression = pExpression;
|
||
|
|
||
|
g_bBreak = false;
|
||
|
|
||
|
HWND hParentWindow = FindLikelyParentWindow();
|
||
|
DialogBox(g_hTier0Instance, MAKEINTRESOURCE(IDD_ASSERT_DIALOG), hParentWindow, AssertDialogProc);
|
||
|
|
||
|
return g_bBreak;
|
||
|
}
|