#include "protoDebug.h"
#include "protoPipe.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#ifdef WIN32
#ifndef _WIN32_WCE
#include <io.h>
#include <fcntl.h>
#endif #include <Windows.h>
#include <tchar.h>
#endif
#ifdef __ANDROID__
#include <android/log.h>
#endif
#ifdef MACOSX
#include <fcntl.h>
#endif
#if defined(PROTO_DEBUG) || defined(PROTO_MSG)
static unsigned int DebugLevel(bool set = false, unsigned int value = PL_ERROR)
{
static unsigned int debug_level = PL_ERROR;
if (set) debug_level = value;
return debug_level;
}
static FILE* DebugLog(bool set = false, FILE* value = stderr)
{
static FILE* debug_log = stderr;
if (set) debug_log = value;
return debug_log;
}
FILE* GetDebugLog()
{
return DebugLog();
}
static ProtoAssertFunction* assert_function = NULL;
static void* assert_data = NULL;
#ifndef SIMULATE
static ProtoPipe debug_pipe(ProtoPipe::MESSAGE);
#endif
void SetDebugLevel(unsigned int level)
{
unsigned int debugLevel = DebugLevel();
#if defined(WIN32) && !defined(SIMULATE)
FILE* debugLog = DebugLog();
if (0 != level)
{
if ((0 == debugLevel) && ((stderr == debugLog) || (stdout == debugLog)))
OpenDebugWindow();
}
else
{
if ((0 != debugLevel) && ((stderr == debugLog) || (debugLog == stdout)))
CloseDebugWindow();
}
#endif DebugLevel(true, level); if (level != debugLevel)
PLOG(PL_INFO,"ProtoDebug>SetDebugLevel: debug level changed from %d to %d\n", debugLevel, level);
}
unsigned int GetDebugLevel()
{
return DebugLevel();
}
bool OpenDebugLog(const char *path)
{
PLOG(PL_INFO,"ProtoDebug>OpenDebugLog: debug log is being set to \"%s\"\n",path);
#ifdef OPNET
if ('\0' == *path) return false;
#endif CloseDebugLog();
FILE* ptr = fopen(path, "w+");
if (ptr)
{
#if defined(WIN32) && !defined(SIMULATE)
FILE* debugLog = DebugLog();
if ((0 != DebugLevel()) && ((debugLog == stdout) || (debugLog == stderr)))
CloseDebugWindow();
#endif DebugLog(true, ptr);
return true;
}
else
{
#if defined(WIN32) && !defined(SIMULATE)
FILE* debugLog = DebugLog();
if ((0 != DebugLevel()) && (debugLog != stdout) && (debugLog != stderr))
OpenDebugWindow();
#endif DebugLog(true, stderr);
PLOG(PL_ERROR, "OpenDebugLog: Error opening debug log file: %s\n", path);
return false;
}
}
void CloseDebugLog()
{
FILE* debugLog = DebugLog();
if (debugLog && (debugLog != stderr) && (debugLog != stdout))
{
fclose(debugLog);
#if defined(WIN32) && !defined(SIMULATE)
if (0 != DebugLevel()) OpenDebugWindow();
#endif }
#ifndef SIMULATE
if (debug_pipe.IsOpen()) debug_pipe.Close();
#endif DebugLog(true, stderr);
}
bool OpenDebugPipe(const char* pipeName)
{
#ifndef SIMULATE
if (!debug_pipe.Connect(pipeName))
{
PLOG(PL_ERROR, "OpenDebugPipe: error opening/connecting debug_pipe!\n");
return false;
}
return true;
#else
return false;
#endif }
void CloseDebugPipe()
{
#ifndef SIMULATE
if (debug_pipe.IsOpen()) debug_pipe.Close();
#endif
}
#if defined(WIN32) && !defined(SIMULATE)
static unsigned int console_reference_count = 0;
class ProtoDebugWindow
{
public:
ProtoDebugWindow();
~ProtoDebugWindow();
bool IsOpen() {return (NULL != hwnd);}
bool Create();
void Destroy();
void Print(const char* text, unsigned int len);
void Popup() {if (hwnd) SetForegroundWindow(hwnd);}
private:
static DWORD WINAPI RunInThread(LPVOID lpParameter);
DWORD Run();
static LRESULT CALLBACK MessageHandler(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
enum {BUFFER_MAX = 8190};
HANDLE thread_handle;
DWORD thread_id;
DWORD parent_thread_id;
HWND hwnd;
char buffer[BUFFER_MAX+2];
unsigned int count;
CRITICAL_SECTION buffer_section;
int content_height;
double scroll_fraction;
};
ProtoDebugWindow::ProtoDebugWindow()
: thread_handle(NULL), thread_id(NULL), parent_thread_id(NULL),
hwnd(NULL), count(0),
content_height(0), scroll_fraction(0.0)
{
}
ProtoDebugWindow::~ProtoDebugWindow()
{
Destroy();
}
bool ProtoDebugWindow::Create()
{
Destroy();
parent_thread_id = GetCurrentThreadId();
if (!(thread_handle = CreateThread(NULL, 0, RunInThread, this, 0, &thread_id)))
{
return true;
}
else
{
PLOG(PL_ERROR, "ProtoDebugWindow::Create() CreateThread() error: %s\n", GetErrorString());
return false;
}
}
void ProtoDebugWindow::Destroy()
{
parent_thread_id = NULL;
if (NULL != thread_handle)
{
if (thread_id != GetCurrentThreadId())
{
if (hwnd) PostMessage(hwnd, WM_CLOSE, 0, 0);
WaitForSingleObject(thread_handle, INFINITE);
CloseHandle(thread_handle);
thread_handle = NULL;
thread_id = NULL;
}
else if (hwnd)
{
DestroyWindow(hwnd);
}
}
}
DWORD WINAPI ProtoDebugWindow::RunInThread(LPVOID lpParameter)
{
DWORD result = ((ProtoDebugWindow*)lpParameter)->Run();
ExitThread(result);
return result;
}
DWORD ProtoDebugWindow::Run()
{
content_height = 0;
scroll_fraction = 1.0;
InitializeCriticalSection(&buffer_section);
EnterCriticalSection(&buffer_section);
memset(buffer, '\0', BUFFER_MAX+2);
LeaveCriticalSection(&buffer_section);
count = 0;
HINSTANCE theInstance = GetModuleHandle(NULL);
WNDCLASS cl;
cl.style = CS_HREDRAW | CS_VREDRAW;
cl.lpfnWndProc = MessageHandler;
cl.cbClsExtra = 0;
cl.cbWndExtra = 0;
cl.hInstance = theInstance;
cl.hIcon = NULL;
cl.hCursor = NULL;
cl.hbrBackground = NULL;
cl.lpszMenuName = NULL;
TCHAR moduleName[256];
DWORD nameLen = GetModuleFileName(GetModuleHandle(NULL), moduleName, 256);
if (0 != nameLen)
_tcsncat(moduleName, _T(" Debug"), 256 - nameLen);
const LPCTSTR myName = (0 != nameLen) ? moduleName : _T("ProtoDebugWindow");
cl.lpszClassName = myName;
if (!RegisterClass(&cl))
{
LPVOID lpMsgBuf;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL );
MessageBox( NULL, (LPCTSTR)lpMsgBuf, (LPCTSTR)"Error", MB_OK | MB_ICONINFORMATION );
LocalFree(lpMsgBuf);
DeleteCriticalSection(&buffer_section);
PLOG(PL_ERROR, "ProtoDebugWindow::Win32Init() Error registering message window class!\n");
return GetLastError();
}
hwnd = CreateWindow(myName,
myName,
WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU |
WS_SIZEBOX | WS_VISIBLE | WS_VSCROLL,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, theInstance, this);
if (NULL == hwnd)
{
Destroy();
LPVOID lpMsgBuf;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, GetLastError(), 0,
(LPTSTR)&lpMsgBuf, 0, NULL );
MessageBox( NULL, (LPCTSTR)lpMsgBuf, (LPCTSTR)_T("Error"), MB_OK | MB_ICONINFORMATION);
LocalFree(lpMsgBuf);
UnregisterClass(cl.lpszClassName, theInstance);
DeleteCriticalSection(&buffer_section);
return GetLastError();
}
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
UnregisterClass(cl.lpszClassName, theInstance);
DeleteCriticalSection(&buffer_section);
return msg.wParam;
}
void ProtoDebugWindow::Print(const char* text, unsigned int len)
{
ASSERT(NULL != hwnd);
EnterCriticalSection(&buffer_section);
if (len > BUFFER_MAX)
{
memcpy(buffer, text + len - BUFFER_MAX, BUFFER_MAX);
count = BUFFER_MAX;
}
else
{
unsigned int space = BUFFER_MAX - count;
unsigned int move = (len > space) ? len - space : 0;
if (move) memmove(buffer, buffer+move, count - move);
memcpy(buffer+count-move, text, len);
count += len - move;
}
#ifdef _UNICODE
*((wchar_t*)(buffer+count)) = 0;
#else
buffer[count] = '\0';
#endif
LeaveCriticalSection(&buffer_section);
RECT rect;
GetClientRect(hwnd, &rect);
InvalidateRect(hwnd, &rect, FALSE);
PostMessage(hwnd, WM_PAINT, 0, 0);
}
LRESULT CALLBACK ProtoDebugWindow::MessageHandler(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
{
CREATESTRUCT *info = (CREATESTRUCT*)lParam;
ProtoDebugWindow* dbg = (ProtoDebugWindow*)info->lpCreateParams;
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)dbg);
RECT rect;
GetClientRect(hwnd, &rect);
SetScrollRange(hwnd, SB_VERT, 0, 0, FALSE);
SetScrollPos(hwnd, SB_VERT, 0, FALSE);
return 0;
}
case WM_SIZE:
{
ProtoDebugWindow* dbg = (ProtoDebugWindow*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
int newHeight = HIWORD(lParam);
if (dbg->content_height > newHeight)
{
SetScrollRange(hwnd, SB_VERT, 0, newHeight, TRUE);
}
else
{
SetScrollRange(hwnd, SB_VERT, 0, 0, TRUE);
dbg->scroll_fraction = 1.0;
}
int pos = (int)((dbg->scroll_fraction * newHeight) + 0.5);
SetScrollPos(hwnd, SB_VERT, pos, TRUE);
return 0;
}
case WM_VSCROLL:
{
ProtoDebugWindow* dbg = (ProtoDebugWindow*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
RECT rect;
GetClientRect(hwnd, &rect);
switch (LOWORD(wParam))
{
case SB_BOTTOM:
SetScrollPos(hwnd, SB_VERT, rect.bottom, TRUE);
dbg->scroll_fraction = 1.0;
break;
case SB_TOP:
SetScrollPos(hwnd, SB_VERT, 0, TRUE);
dbg->scroll_fraction = 0.0;
break;
case SB_THUMBPOSITION:
case SB_THUMBTRACK:
{
int pos = HIWORD(wParam);
SetScrollPos(hwnd, SB_VERT, pos, TRUE);
dbg->scroll_fraction = (double)pos / rect.bottom;
break;
}
default:
break;
}
InvalidateRect(hwnd, &rect, FALSE);
PostMessage(hwnd, WM_PAINT, 0, 0);
return 0;
}
case WM_PAINT:
{
ProtoDebugWindow* dbg = (ProtoDebugWindow*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
char tempBuffer[BUFFER_MAX+2];
EnterCriticalSection(&dbg->buffer_section);
memcpy(tempBuffer, dbg->buffer, BUFFER_MAX+2);
LeaveCriticalSection(&dbg->buffer_section);
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
RECT rect;
GetClientRect(hwnd, &rect);
RECT textRect = rect;
#ifndef _WIN32_WCE
HFONT oldFont = (HFONT)SelectObject(hdc, GetStockObject(ANSI_FIXED_FONT));
#endif HPEN oldPen = (HPEN)SelectObject(hdc, GetStockObject(BLACK_PEN));
int textHeight =
DrawText(hdc, (LPCTSTR)tempBuffer, -1, &textRect, DT_CALCRECT | DT_LEFT | DT_NOPREFIX | DT_WORDBREAK);
FillRect(hdc, &rect, (HBRUSH)GetStockObject(WHITE_BRUSH));
int windowHeight = rect.bottom - rect.top;
if (textHeight > windowHeight)
{
int delta = textHeight - windowHeight;
delta = (int)((delta * dbg->scroll_fraction) + 0.5);
rect.top -= delta;
bool updateScroll = dbg->content_height <= windowHeight;
dbg->content_height = textHeight;
SetScrollRange(hwnd, SB_VERT, 0, rect.bottom, TRUE);
}
DrawText(hdc, (LPCTSTR)tempBuffer, -1, &rect, DT_LEFT | DT_NOPREFIX | DT_WORDBREAK);
#ifndef _WIN32_WCE
SelectObject(hdc, oldFont);
#endif SelectObject(hdc, oldPen);
EndPaint(hwnd, &ps);
return 0;
}
case WM_DESTROY:
{
ProtoDebugWindow* dbg = (ProtoDebugWindow*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
dbg->hwnd = NULL;
console_reference_count = 0;
PostQuitMessage(0);
if (NULL != dbg->parent_thread_id)
PostThreadMessage(dbg->parent_thread_id, WM_QUIT, 0, 0);
return 0;
}
default:
break;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
#ifdef _WIN32_WCE
static ProtoDebugWindow debug_window;
#endif
#endif
void ProtoDMSG(unsigned int level, const char *format, ...)
{
if (level <= DebugLevel())
{
FILE* debugLog = DebugLog();
va_list args;
va_start(args, format);
#ifdef _WIN32_WCE
if (debug_window.IsOpen() && ((stderr == debugLog) || (stdout == debugLog)))
{
char charBuffer[2048];
charBuffer[2047] = '\0';
int count = _vsnprintf(charBuffer, 2047, format, args);
#ifdef _UNICODE
wchar_t wideBuffer[2048];
count = mbstowcs(wideBuffer, charBuffer, count);
count *= sizeof(wchar_t);
const char* theBuffer = (char*)wideBuffer;
#else
const char* theBuffer = charBuffer;
#endif debug_window.Print(theBuffer, count);
}
else
#endif {
vfprintf(debugLog, format, args);
fflush(debugLog);
}
va_end(args);
}
}
void ProtoLog(ProtoDebugLevel level, const char* format, ...)
{
if (((unsigned int)level <= DebugLevel()) || (PL_ALWAYS == level))
{
va_list args;
va_start(args, format);
const char* header = "";
switch(level)
{ case PL_FATAL: header = "Proto Fatal: ";
break;
case PL_ERROR: header = "Proto Error: ";
break;
case PL_WARN: header = "Proto Warn: ";
break;
case PL_INFO: header = "Proto Info: ";
break;
case PL_DEBUG: header = "Proto Debug: ";
break;
case PL_TRACE: header = "Proto Trace: ";
break;
case PL_DETAIL: header = "Proto Detail: ";
break;
case PL_MAX: header = "Proto Max: ";
break;
default:
break;
}
size_t headerLen = strlen(header);
FILE* debugLog = DebugLog();
#ifdef _WIN32_WCE
if (debug_window.IsOpen() && !debug_pipe.IsOpen() && ((stderr == debugLog) || (stdout == debugLog)))
{
char charBuffer[8192];
charBuffer[8191] = '\0';
int count = _vsnprintf(charBuffer, 8191, format, args);
#ifdef _UNICODE
wchar_t wideBuffer[8192];
count = mbstowcs(wideBuffer, charBuffer, count);
count *= sizeof(wchar_t);
const char* theBuffer = (char*)wideBuffer;
#else
const char* theBuffer = charBuffer;
#endif debug_window.Print(theBuffer, count);
}
else
#endif #ifndef SIMULATE
if (debug_pipe.IsOpen())
#else
if (false)#endif {
char buffer[8192];
buffer[8191] = '\0';
#ifdef _WIN32_WCE
unsigned int count = (unsigned int)_vsnprintf(buffer, 8191, format, args) + 1;
#else
#ifdef WIN32
strcpy(buffer, header);
unsigned int count = (unsigned int)_vsnprintf(buffer + headerLen, 8191-headerLen, format, args) + 1;
count += (unsigned int)headerLen;
#else
strcpy(buffer, header);
unsigned int count = (unsigned int)vsnprintf(buffer + headerLen, 8191-headerLen, format, args) + 1;
count += headerLen;
#endif
#endif if (count > 8192) count = 8192;
#ifndef SIMULATE
if (!debug_pipe.Send(buffer, count))
#else
if (true)
#endif {
fprintf(stderr, "PLOG() error: unable to send to debug pipe!!!\n");
vfprintf(stderr, format, args);
fflush(stderr);
}
}
else
{
#ifdef __ANDROID__
android_LogPriority prio;
switch(level)
{
case PL_FATAL:
prio = ANDROID_LOG_FATAL;
break;
case PL_ERROR:
prio = ANDROID_LOG_ERROR;
break;
case PL_WARN:
prio = ANDROID_LOG_WARN;
break;
case PL_INFO:
prio = ANDROID_LOG_INFO;
break;
case PL_DEBUG:
prio = ANDROID_LOG_DEBUG;
break;
case PL_TRACE:
case PL_DETAIL:
case PL_MAX:
case PL_ALWAYS:
prio = ANDROID_LOG_VERBOSE;
break;
default:
prio = ANDROID_LOG_DEFAULT;
break;
}
__android_log_vprint(prio, "protolib", format, args);
#else
fprintf(debugLog, "%s", header);
if (vfprintf(debugLog, format, args) < 0)
{
perror(""); char buffer[8192];
buffer[8191] = '\0';
strcpy(buffer, header);
va_end(args);
va_start(args, format);
int count = vsnprintf(buffer + headerLen, 8191 - headerLen, format, args);
if ('\n' == buffer[headerLen + count - 1])
buffer[headerLen + count - 1] = '\0';
perror(buffer);
clearerr(debugLog);
}
fflush(debugLog);
#endif
}
va_end(args);
}
}
#if defined(WIN32) && !defined(SIMULATE)
void OpenDebugWindow()
{
if (0 == console_reference_count)
{
#ifndef _WIN32_WCE
int fdStd;
HANDLE hStd;
CONSOLE_SCREEN_BUFFER_INFO coninfo;
if (_get_osfhandle(0) < 0)
_close(0);
freopen("//./NUL", "r", stdin);
setvbuf(stdin, NULL, _IONBF, 0);
if (_get_osfhandle(1) < 0)
_close(1);
freopen("//./NUL", "w", stdout);
setvbuf(stdout, NULL, _IONBF, 0);
if (_get_osfhandle(2) < 0)
_close(2);
freopen("//./NUL", "w", stderr);
setvbuf(stderr, NULL, _IONBF, 0);
FreeConsole();
if (!AllocConsole()) {
return;
}
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &coninfo);
coninfo.dwSize.Y = 1024;
SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), coninfo.dwSize);
hStd = GetStdHandle(STD_INPUT_HANDLE);
fdStd = _open_osfhandle((intptr_t)hStd, _O_TEXT);
_dup2(fdStd, _fileno(stdin));
SetStdHandle(STD_INPUT_HANDLE, (HANDLE)_get_osfhandle(_fileno(stdin)));
_close(fdStd);
hStd = GetStdHandle(STD_OUTPUT_HANDLE);
fdStd = _open_osfhandle((intptr_t)hStd, _O_TEXT);
_dup2(fdStd, _fileno(stdout));
SetStdHandle(STD_OUTPUT_HANDLE, (HANDLE)_get_osfhandle(_fileno(stdout)));
_close(fdStd);
hStd = GetStdHandle(STD_ERROR_HANDLE);
fdStd = _open_osfhandle((intptr_t)hStd, _O_TEXT);
_dup2(fdStd, _fileno(stderr));
SetStdHandle(STD_ERROR_HANDLE, (HANDLE)_get_osfhandle(_fileno(stderr)));
_close(fdStd);
SetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE),
ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT);
#endif }
#ifdef _WIN32_WCE
if (!debug_window.IsOpen()) debug_window.Create();
#endif console_reference_count++;
}
#ifndef _WIN32_WCE
static HWND GetDebugWindowHandle(void)
{
#define MY_BUFSIZE 1024
HWND hwndFound; #ifdef _UNICODE
wchar_t pszNewWindowTitle[MY_BUFSIZE]; wchar_t pszOldWindowTitle[MY_BUFSIZE]; #else
char pszNewWindowTitle[MY_BUFSIZE]; char pszOldWindowTitle[MY_BUFSIZE]; #endif
GetConsoleTitle(pszOldWindowTitle, MY_BUFSIZE);
wsprintf(pszNewWindowTitle, _T("%d/%d"),
GetTickCount(),
GetCurrentProcessId());
SetConsoleTitle(pszNewWindowTitle);
Sleep(40);
hwndFound=FindWindow(NULL, pszNewWindowTitle);
SetConsoleTitle(pszOldWindowTitle);
return(hwndFound);
} #endif
void PopupDebugWindow()
{
#ifdef _WIN32_WCE
if (debug_window.IsOpen())
debug_window.Popup();
else
OpenDebugWindow();
#else
HWND debugWindow = GetDebugWindowHandle();
if (debugWindow)
SetForegroundWindow(debugWindow);
else
OpenDebugWindow();
#endif }
void CloseDebugWindow()
{
if (console_reference_count > 0)
{
console_reference_count--;
if (0 == console_reference_count)
{
#ifdef _WIN32_WCE
debug_window.Destroy();
#else
FreeConsole();
#endif }
}
}
#endif
#endif
#ifdef PROTO_DEBUG
void TRACE(const char *format, ...)
{
FILE* debugLog = DebugLog();
va_list args;
va_start(args, format);
#ifdef _WIN32_WCE
if (debug_window.IsOpen() && ((stderr == debugLog) || (stdout == debugLog)))
{
char charBuffer[2048];
charBuffer[2047] = '\0';
int count = _vsnprintf(charBuffer, 2047, format, args);
#ifdef _UNICODE
wchar_t wideBuffer[2048];
count = mbstowcs(wideBuffer, charBuffer, count);
count *= sizeof(wchar_t);
const char* theBuffer = (char*)wideBuffer;
#else
const char* theBuffer = charBuffer;
#endif debug_window.Print(theBuffer, count);
}
else
#endif {
#ifdef __ANDROID__
__android_log_vprint(ANDROID_LOG_ERROR, "protolib", format, args);
#else
if (vfprintf(debugLog, format, args) < 0)
{
perror("");
char buffer[8192];
buffer[8191] = '\0';
va_end(args);
va_start(args, format);
int count = vsnprintf(buffer, 8191, format, args);
if ('\n' == buffer[count - 1])
buffer[count - 1] = '\0';
perror(buffer);
clearerr(debugLog);
}
fflush(debugLog);
#endif }
va_end(args);
}
void PROTO_ABORT(const char *format, ...)
{
#ifndef _WIN32_WCE
FILE* debugLog = DebugLog();
va_list args;
va_start(args, format);
vfprintf(debugLog, format, args);
fflush(debugLog);
va_end(args);
abort();
#endif }
void SetAssertFunction(ProtoAssertFunction* assertFunction, void* userData)
{
assert_function = assertFunction;
assert_data = userData;
}
void ClearAssertFunction()
{
assert_function = NULL;
}
bool HasAssertFunction()
{
return (NULL != assert_function);
}
void ProtoAssertHandler(bool condition, const char* conditionText, const char* fileName, int lineNumber)
{
if (NULL != assert_function) assert_function(condition, conditionText, fileName, lineNumber, assert_data);
}
#endif