openmohaa/code/gamespy/common/gsDebug.c

389 lines
11 KiB
C
Raw Normal View History

2023-02-04 21:00:01 +01:00
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
#include "gsCommon.h"
#include "gsDebug.h"
//#include <stdio.h>
//#include <stdarg.h>
// THIS FILE ONLY INCLUDED WHEN USING GAMESPY DEBUG FUNCTIONS
// (don't put this above the header includes or VC will whine
#ifdef GSI_COMMON_DEBUG
#if defined(_NITRO)
#include "../../common/nitro/screen.h"
#define printf Printf
#define vprintf VPrintf
#endif
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Static debug data
static struct GSIDebugInstance gGSIDebugInstance; // simple singleton "class"
// Line prefixes, e.g. "[ cat][type][ lev] text"
char* gGSIDebugCatStrings[GSIDebugCat_Count] =
{
" APP", " GP ", "PEER", " QR2", " SB", " V2", " AD", " NN", "HTTP", "CDKY", " CMN"
};
char* gGSIDebugTypeStrings[GSIDebugType_Count] =
{
" NET", "FILE", " MEM", "STAT", "MISC"
};
char* gGSIDebugLevelStrings[GSIDebugLevel_Count] =
{
"*ERR", "****", "----", " ", " ", " ", " ->"
};
char* gGSIDebugLevelDescriptionStrings[8] =
{
"None", "<None+1>", "<Normal>", "<Debug>", "<Verbose>", "<Verbose+1>", "<Verbose+2>", "<Hardcore>"
};
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// utility to convert bit flag back to base (e.g. 1<<2 returns 2)
static gsi_u32 gsiDebugLog2(gsi_u32 theInt)
{
gsi_u32 total = 0;
while (theInt > 1)
{
theInt = theInt >> 1;
total++;
}
return total;
}
// default supplied debug function, will receive debug text
// this is platform specific
static void gsiDebugCallback(GSIDebugCategory category, GSIDebugType type,
GSIDebugLevel level, const char * format, va_list params)
{
#if defined(_PSP)
// Output line prefix
vprintf(format, params);
//gsDebugTTyPrint(string);
#elif defined(_PS2)
// Output line prefix
vprintf(format, params);
#elif defined(_PS3)
// Output line prefix
vprintf(format, params);
#elif defined(_WIN32)
static char string[256];
vsprintf(string, format, params);
OutputDebugStringA(string);
#elif defined(_LINUX) || defined(_MACOSX)
//static char string[256];
//vsprintf(string, format, params);
vprintf(format, params);
#elif defined(_NITRO)
VPrintf(format, params);
#elif defined(_REVOLUTION)
static char string[256];
vsprintf(string, format, params);
OSReport(string);
#else
va_list argptr;
static char string[256];
va_start(argptr, format);
vsprintf(string, format, argptr);
va_end(argptr);
gsDebugTTyPrint(string);
#endif
GSI_UNUSED(category);
GSI_UNUSED(type);
GSI_UNUSED(level);
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// process debug output
void gsDebugVaList(GSIDebugCategory theCat, GSIDebugType theType,
GSIDebugLevel theLevel, const char* theTokenStr,
va_list theParamList)
{
// Retrieve the current debug level
GSIDebugLevel aCurLevel;
// Verify Parameters
assert(theCat <= GSIDebugCat_Count);
assert(theType <= GSIDebugType_Count);
assert(theLevel <= (1<<GSIDebugLevel_Count));
assert(theTokenStr);
// Make thread safe
if (gGSIDebugInstance.mInitialized == 0)
{
// Warning: Slight race condition risk here the first time
// gsDebug functions are used.
// The risk is minimal since you usually set
// debug levels and targets at program startup
gGSIDebugInstance.mInitialized = 1;
gsiInitializeCriticalSection(&gGSIDebugInstance.mDebugCrit);
}
gsiEnterCriticalSection(&gGSIDebugInstance.mDebugCrit);
// Are we currently logging this type and level?
aCurLevel = gGSIDebugInstance.mGSIDebugLevel[theCat][theType];
if (aCurLevel & theLevel) // check the flag
{
#if !defined(_NITRO)
// Output line prefix
if (gGSIDebugInstance.mGSIDebugFile)
{
fprintf(gGSIDebugInstance.mGSIDebugFile, "[%s][%s][%s] ",
gGSIDebugCatStrings[theCat],
gGSIDebugTypeStrings[theType],
gGSIDebugLevelStrings[gsiDebugLog2(theLevel)]);
// Output to file
vfprintf(gGSIDebugInstance.mGSIDebugFile, theTokenStr,
theParamList);
}
#endif
// Output to developer function if provided
if (gGSIDebugInstance.mDebugCallback != NULL)
{
(*gGSIDebugInstance.mDebugCallback)(theCat, theType, theLevel,
theTokenStr, theParamList);
}
else
{
gsiDebugCallback(theCat, theType, theLevel,
theTokenStr, theParamList);
}
}
gsiLeaveCriticalSection(&gGSIDebugInstance.mDebugCrit);
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// process debug output
void gsDebugFormat(GSIDebugCategory theCat, GSIDebugType theType,
GSIDebugLevel theLevel, const char* theTokenStr,
...)
{
va_list aParameterList;
// Verify Parameters
assert(theCat <= GSIDebugCat_Count);
assert(theType <= GSIDebugType_Count);
assert(theLevel <= (1<<GSIDebugLevel_Count));
assert(theTokenStr);
// Find start of var arg list
va_start(aParameterList, theTokenStr);
// Pass to VA version
gsDebugVaList(theCat, theType, theLevel, theTokenStr, aParameterList);
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Converts binary buffer to memory view form:
// 0000 0000 0000 0000 0000 0000 0000 0000 ................
static void HexEncode16(const char* theInStream, char* theOutStream,
unsigned int theInLen)
{
const int aRowWidth = 64; // width of the output
const char aReplaceChar = '.'; // Replace non print characters
const int aTextOffSet = 41; // text comes after hex bytes
char* aTextOutStream = (theOutStream+aTextOffSet); // set the write ptr
const unsigned int aWriteBit = theInLen & 1; // write on odd or even bytes?
assert(theInLen <= 16);
// Set buffer to ' '
memset(theOutStream, ' ', aRowWidth);
// Convert characters one at a time
while(theInLen--)
{
// Read the next byte
unsigned char aChar = (unsigned char)(*theInStream++);
// Write one byte in hex form
sprintf(theOutStream, "%02X", aChar);
// Write the printable character
if (isgraph(aChar))
*(aTextOutStream++) = (char)aChar;
else
*(aTextOutStream++) = aReplaceChar;
// Move to next hex byte
theOutStream += 2;
// Insert a space every other byte
if ((theInLen & 1) == aWriteBit)
*theOutStream++ = ' ';
}
// Remove NULL terminator from last sprintf
*theOutStream = ' ';
// NULL terminate the full string
*(aTextOutStream) = '\0';
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Write binary data as B64 bytes (40 bytes per line)
void gsDebugBinary(GSIDebugCategory theCat, GSIDebugType theType,
GSIDebugLevel theLevel, const char* theBuffer, gsi_i32 theLength)
{
int aBytesLeft = theLength;
const char* aReadPos = theBuffer;
char aHexStr[80];
// convert and display in 40 byte segments
while(aBytesLeft > 0)
{
gsi_i32 aBytesToRead = min(aBytesLeft, 16);
HexEncode16(aReadPos, aHexStr, (unsigned int)aBytesToRead);
gsDebugFormat(theCat, theType, theLevel, " %s\r\n", aHexStr);
aReadPos += aBytesToRead;
aBytesLeft -= aBytesToRead;
};
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
void gsSetDebugLevel(GSIDebugCategory theCat, GSIDebugType theType,
GSIDebugLevel theLevel)
{
// Verify Parameters
assert(theCat <= GSIDebugCat_Count);
assert(theType <= GSIDebugType_Count);
// Set for all categories?
if (theCat == GSIDebugCat_Count)
{
int i=0;
for (; i<GSIDebugCat_Count; i++)
gsSetDebugLevel((GSIDebugCategory)i, theType, theLevel);
gsDebugFormat(GSIDebugCat_Common, GSIDebugType_Misc,
GSIDebugLevel_Debug,
"Debug level set to %s for all categories (SDKs)\r\n",
gGSIDebugLevelDescriptionStrings[gsiDebugLog2(theLevel)]);
return;
}
// Set for all types?
if (theType == GSIDebugType_Count)
{
int i=0;
for (; i<GSIDebugType_Count; i++)
gsSetDebugLevel(theCat, (GSIDebugType)i, theLevel);
gsDebugFormat(GSIDebugCat_Common, GSIDebugType_Misc,
GSIDebugLevel_Debug,
"Debug level set to %s for all types\r\n",
gGSIDebugLevelDescriptionStrings[gsiDebugLog2(theLevel)]);
return;
}
// Is the new level different from the old?
if (gGSIDebugInstance.mGSIDebugLevel[theCat][theType] != theLevel)
{
// Notify of the change
gsDebugFormat(GSIDebugCat_Common, GSIDebugType_Misc,
GSIDebugLevel_Comment,
"Changing debug level: [%s][%s][%02X]\r\n",
gGSIDebugCatStrings[theCat],
gGSIDebugTypeStrings[theType],
theLevel );
gGSIDebugInstance.mGSIDebugLevel[theCat][theType] = theLevel;
}
}
#if !defined(_NITRO)
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Set the debug output file to an already open file
// Set to "stdout" for console output
void gsSetDebugFile(FILE* theFile)
{
if (theFile != gGSIDebugInstance.mGSIDebugFile)
{
// If the old file is valid, notify it of the closing
if (gGSIDebugInstance.mGSIDebugFile != NULL)
{
gsDebugFormat(GSIDebugCat_Common, GSIDebugType_Misc,
GSIDebugLevel_Comment, "Debug disabled in this file\r\n");
}
// If the new file is valid, notify it of the opening
{
gsDebugFormat(GSIDebugCat_Common, GSIDebugType_Misc,
GSIDebugLevel_Comment, "Debug enabled in this file\r\n");
}
gGSIDebugInstance.mGSIDebugFile = theFile;
}
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Opens and sets the debug output file
FILE* gsOpenDebugFile(const char* theFileName)
{
// The new file
FILE* aFile = NULL;
// Verify parameters
assert(theFileName != NULL);
// Open the new file (clear contents)
aFile = fopen(theFileName, "w+");
if (aFile != NULL)
gsSetDebugFile(aFile);
return aFile;
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Retrieve the current debug file (if any)
FILE* gsGetDebugFile()
{
return gGSIDebugInstance.mGSIDebugFile;
}
#endif
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Set the developer callback
void gsSetDebugCallback(GSIDebugCallback theCallback)
{
gGSIDebugInstance.mDebugCallback = theCallback;
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
#endif // GSI_COMMON_DEBUG