enderalse/SKSE/Plugins/fs_skse_plugin_functions/common/IDebugLog.cpp
2021-10-06 02:45:46 +02:00

325 lines
5.6 KiB
C++

#include "common/IDebugLog.h"
#include <share.h>
#include "common/IFileStream.h"
#include <shlobj.h>
std::FILE * IDebugLog::logFile = NULL;
char IDebugLog::sourceBuf[16] = { 0 };
char IDebugLog::headerText[16] = { 0 };
char IDebugLog::formatBuf[8192] = { 0 };
int IDebugLog::indentLevel = 0;
int IDebugLog::rightMargin = 0;
int IDebugLog::cursorPos = 0;
int IDebugLog::inBlock = 0;
bool IDebugLog::autoFlush = true;
IDebugLog::LogLevel IDebugLog::logLevel = IDebugLog::kLevel_DebugMessage;
IDebugLog::LogLevel IDebugLog::printLevel = IDebugLog::kLevel_Message;
IDebugLog::IDebugLog()
{
//
}
IDebugLog::IDebugLog(const char * name)
{
Open(name);
}
IDebugLog::~IDebugLog()
{
if(logFile)
fclose(logFile);
}
void IDebugLog::Open(const char * path)
{
logFile = _fsopen(path, "w", _SH_DENYWR);
if(!logFile)
{
UInt32 id = 0;
char name[1024];
do
{
sprintf_s(name, sizeof(name), "%s%d", path, id);
id++;
logFile = NULL;
logFile = _fsopen(name, "w", _SH_DENYWR);
}
while(!logFile && (id < 5));
}
}
void IDebugLog::OpenRelative(int folderID, const char * relPath)
{
char path[MAX_PATH];
HRESULT err = SHGetFolderPath(NULL, folderID | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, path);
if(!SUCCEEDED(err))
{
_FATALERROR("SHGetFolderPath %08X failed (result = %08X lasterr = %08X)", folderID, err, GetLastError());
}
ASSERT_CODE(SUCCEEDED(err), err);
strcat_s(path, sizeof(path), relPath);
IFileStream::MakeAllDirs(path);
Open(path);
}
/**
* Output a non-formatted message to the log file
*
* @param message the message
* @param source the source of the message, or NULL to use the previous source
*/
void IDebugLog::Message(const char * message, const char * source, bool newLine)
{
if(source)
SetSource(source);
if(inBlock)
{
SeekCursor(RoundToTab((indentLevel * 4) + strlen(headerText)));
}
else
{
SeekCursor(indentLevel * 4);
PrintText(headerText);
}
PrintText(message);
if(newLine)
NewLine();
}
/**
* Output a formatted message to the log file
*
* @note It is impossible to set the source of a formatted message.
* The previous source will be used.
*/
void IDebugLog::FormattedMessage(const char * fmt, ...)
{
va_list argList;
va_start(argList, fmt);
vsprintf_s(formatBuf, sizeof(formatBuf), fmt, argList);
Message(formatBuf);
va_end(argList);
}
/**
* Output a formatted message to the log file
*
* @note It is impossible to set the source of a formatted message.
* The previous source will be used.
*/
void IDebugLog::FormattedMessage(const char * fmt, va_list args)
{
vsprintf_s(formatBuf, sizeof(formatBuf), fmt, args);
Message(formatBuf);
}
void IDebugLog::Log(LogLevel level, const char * fmt, va_list args)
{
bool log = (level <= logLevel);
bool print = (level <= printLevel);
if(log || print)
vsprintf_s(formatBuf, sizeof(formatBuf), fmt, args);
if(log)
Message(formatBuf);
if(print)
printf("%s\n", formatBuf);
}
void IDebugLog::LogNNL(LogLevel level, const char * fmt, va_list args)
{
bool log = (level <= logLevel);
bool print = (level <= printLevel);
if(log || print)
vsprintf_s(formatBuf, sizeof(formatBuf), fmt, args);
if(log)
Message(formatBuf, NULL, false);
if(print)
printf("%s", formatBuf);
}
/**
* Set the current message source
*/
void IDebugLog::SetSource(const char * source)
{
strcpy_s(sourceBuf, sizeof(sourceBuf), source);
strcpy_s(headerText, sizeof(headerText), "[ ]\t");
char * tgt = headerText + 1;
char * src = sourceBuf;
for(int i = 0; (i < 8) && *src; i++, tgt++, src++)
*tgt = *src;
}
/**
* Clear the current message source
*/
void IDebugLog::ClearSource(void)
{
sourceBuf[0] = 0;
}
/**
* Increase the indentation level
*/
void IDebugLog::Indent(void)
{
indentLevel++;
}
/**
* Decrease the indentation level
*/
void IDebugLog::Outdent(void)
{
if(indentLevel)
indentLevel--;
}
/**
* Enter a logical block
*/
void IDebugLog::OpenBlock(void)
{
SeekCursor(indentLevel * 4);
PrintText(headerText);
inBlock = 1;
}
/**
* Close a logical block
*/
void IDebugLog::CloseBlock(void)
{
inBlock = 0;
}
/**
* Enable/disable autoflush
*
* @param inAutoFlush autoflush state
*/
void IDebugLog::SetAutoFlush(bool inAutoFlush)
{
autoFlush = inAutoFlush;
}
/**
* Print spaces to the log
*
* If possible, tabs are used instead of spaces.
*/
void IDebugLog::PrintSpaces(int numSpaces)
{
int originalNumSpaces = numSpaces;
if(logFile)
{
while(numSpaces > 0)
{
if(numSpaces >= TabSize())
{
numSpaces -= TabSize();
fputc('\t', logFile);
}
else
{
numSpaces--;
fputc(' ', logFile);
}
}
}
cursorPos += originalNumSpaces;
}
/**
* Prints raw text to the log file
*/
void IDebugLog::PrintText(const char * buf)
{
if(logFile)
{
fputs(buf, logFile);
if(autoFlush)
fflush(logFile);
}
const char * traverse = buf;
char data;
while(data = *traverse++)
{
if(data == '\t')
cursorPos += TabSize();
else
cursorPos++;
}
}
/**
* Moves to the next line of the log file
*/
void IDebugLog::NewLine(void)
{
if(logFile)
{
fputc('\n', logFile);
if(autoFlush)
fflush(logFile);
}
cursorPos = 0;
}
/**
* Prints spaces to align the cursor to the requested position
*
* @note The cursor move will not be performed if the request would move the cursor
* backwards.
*/
void IDebugLog::SeekCursor(int position)
{
if(position > cursorPos)
PrintSpaces(position - cursorPos);
}
/**
* Returns the number of spaces a tab would occupy at the current cursor position
*/
int IDebugLog::TabSize(void)
{
return ((~cursorPos) & 3) + 1;
}
/**
* Rounds a number of spaces to the nearest tab
*/
int IDebugLog::RoundToTab(int spaces)
{
return (spaces + 3) & ~3;
}