mirror of
https://github.com/openmoh/openmohaa.git
synced 2025-04-28 21:57:57 +03:00
389 lines
11 KiB
C
389 lines
11 KiB
C
![]() |
///////////////////////////////////////////////////////////////////////////////
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
#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
|