#include "debug.h" #include #include #include #ifdef _WINDOWS #include #define snprintf _snprintf #define strncasecmp _strnicmp #define strcasecmp _stricmp #else #include #include #include #endif #include "../common/StringUtil.h" #include "../common/MiscFunctions.h" #include "../common/platform.h" #ifndef va_copy #define va_copy(d,s) ((d) = (s)) #endif static volatile bool logFileValid = false; static EQEMuLog realLogFile; EQEMuLog *LogFile = &realLogFile; static const char* FileNames[EQEMuLog::MaxLogID] = { "logs/eqemu", "logs/eqemu", "logs/eqemu_error", "logs/eqemu_debug", "logs/eqemu_quest", "logs/eqemu_commands", "logs/crash" }; static const char* LogNames[EQEMuLog::MaxLogID] = { "Status", "Normal", "Error", "Debug", "Quest", "Command", "Crash" }; EQEMuLog::EQEMuLog() { // MOpen = new Mutex; // MLog = new Mutex*[MaxLogID]; // fp = new FILE*[MaxLogID]; // pLogStatus = new uint8[MaxLogID]; for (int i=0; i= 2 pLogStatus[i] = 1 | 2; #else pLogStatus[i] = 0; #endif logCallbackFmt[i] = nullptr; logCallbackBuf[i] = nullptr; logCallbackPva[i] = nullptr; } // TODO: Make this read from an ini or something, everyone has different opinions on what it should be #if EQDEBUG < 2 pLogStatus[Status] = 2; pLogStatus[Error] = 2; pLogStatus[Quest] = 2; pLogStatus[Commands] = 1; #endif logFileValid = true; } EQEMuLog::~EQEMuLog() { logFileValid = false; for (int i=0; i= MaxLogID) { return false; } LockMutex lock(&MOpen); if (pLogStatus[id] & 4) { return false; } if (fp[id]) { //cerr<<"Warning: LogFile already open"<= MaxLogID) { return false; } bool dofile = false; if (pLogStatus[id] & 1) { dofile = open(id); } if (!(dofile || pLogStatus[id] & 2)) return false; LockMutex lock(&MLog[id]); if (!logFileValid) return false; //check again for threading race reasons (to avoid two mutexes) time_t aclock; struct tm *newtime; time( &aclock ); /* Get time in seconds */ newtime = localtime( &aclock ); /* Convert time to struct */ if (dofile) #ifndef NO_PIDLOG fprintf(fp[id], "[%02d.%02d. - %02d:%02d:%02d] ", newtime->tm_mon+1, newtime->tm_mday, newtime->tm_hour, newtime->tm_min, newtime->tm_sec); #else fprintf(fp[id], "%04i [%02d.%02d. - %02d:%02d:%02d] ", getpid(), newtime->tm_mon+1, newtime->tm_mday, newtime->tm_hour, newtime->tm_min, newtime->tm_sec); #endif va_list argptr, tmpargptr; va_start(argptr, fmt); if (dofile) { va_copy(tmpargptr, argptr); vfprintf( fp[id], fmt, tmpargptr ); } if(logCallbackFmt[id]) { msgCallbackFmt p = logCallbackFmt[id]; va_copy(tmpargptr, argptr); p(id, fmt, tmpargptr ); } std::string outputMessage; StringFormat(outputMessage, fmt, argptr); if (pLogStatus[id] & 2) { if (pLogStatus[id] & 8) { std::cerr << "[" << LogNames[id] << "] "; std::cerr << outputMessage; } else { std::cout << "[" << LogNames[id] << "] "; std::cout << outputMessage; } } va_end(argptr); if (dofile) fprintf(fp[id], "\n"); if (pLogStatus[id] & 2) { if (pLogStatus[id] & 8) { std::cerr << std::endl; } else { std::cout << std::endl; } } if(dofile) fflush(fp[id]); return true; } //write with Prefix and a VA_list bool EQEMuLog::writePVA(LogIDs id, const char *prefix, const char *fmt, va_list argptr) { if (!logFileValid) { return false; } if (id >= MaxLogID) { return false; } bool dofile = false; if (pLogStatus[id] & 1) { dofile = open(id); } if (!(dofile || pLogStatus[id] & 2)) { return false; } LockMutex lock(&MLog[id]); if (!logFileValid) return false; //check again for threading race reasons (to avoid two mutexes) time_t aclock; struct tm *newtime; time( &aclock ); /* Get time in seconds */ newtime = localtime( &aclock ); /* Convert time to struct */ va_list tmpargptr; if (dofile) { #ifndef NO_PIDLOG fprintf(fp[id], "[%02d.%02d. - %02d:%02d:%02d] %s", newtime->tm_mon+1, newtime->tm_mday, newtime->tm_hour, newtime->tm_min, newtime->tm_sec, prefix); #else fprintf(fp[id], "%04i [%02d.%02d. - %02d:%02d:%02d] %s", getpid(), newtime->tm_mon+1, newtime->tm_mday, newtime->tm_hour, newtime->tm_min, newtime->tm_sec, prefix); #endif va_copy(tmpargptr, argptr); vfprintf( fp[id], fmt, tmpargptr ); } if(logCallbackPva[id]) { msgCallbackPva p = logCallbackPva[id]; va_copy(tmpargptr, argptr); p(id, prefix, fmt, tmpargptr ); } if (pLogStatus[id] & 2) { if (pLogStatus[id] & 8) { fprintf(stderr, "[%s] %s", LogNames[id], prefix); vfprintf( stderr, fmt, argptr ); } else { fprintf(stdout, "[%s] %s", LogNames[id], prefix); vfprintf( stdout, fmt, argptr ); } } va_end(argptr); if (dofile) fprintf(fp[id], "\n"); if (pLogStatus[id] & 2) { if (pLogStatus[id] & 8) fprintf(stderr, "\n"); else fprintf(stdout, "\n"); } if(dofile) fflush(fp[id]); return true; } bool EQEMuLog::writebuf(LogIDs id, const char *buf, uint8 size, uint32 count) { if (!logFileValid) { return false; } if (id >= MaxLogID) { return false; } bool dofile = false; if (pLogStatus[id] & 1) { dofile = open(id); } if (!(dofile || pLogStatus[id] & 2)) return false; LockMutex lock(&MLog[id]); if (!logFileValid) return false; //check again for threading race reasons (to avoid two mutexes) time_t aclock; struct tm *newtime; time( &aclock ); /* Get time in seconds */ newtime = localtime( &aclock ); /* Convert time to struct */ if (dofile) #ifndef NO_PIDLOG fprintf(fp[id], "[%02d.%02d. - %02d:%02d:%02d] ", newtime->tm_mon+1, newtime->tm_mday, newtime->tm_hour, newtime->tm_min, newtime->tm_sec); #else fprintf(fp[id], "%04i [%02d.%02d. - %02d:%02d:%02d] ", getpid(), newtime->tm_mon+1, newtime->tm_mday, newtime->tm_hour, newtime->tm_min, newtime->tm_sec); #endif if (dofile) { fwrite(buf, size, count, fp[id]); fprintf(fp[id], "\n"); } if(logCallbackBuf[id]) { msgCallbackBuf p = logCallbackBuf[id]; p(id, buf, size, count); } if (pLogStatus[id] & 2) { if (pLogStatus[id] & 8) { fprintf(stderr, "[%s] ", LogNames[id]); fwrite(buf, size, count, stderr); fprintf(stderr, "\n"); } else { fprintf(stdout, "[%s] ", LogNames[id]); fwrite(buf, size, count, stdout); fprintf(stdout, "\n"); } } if(dofile) fflush(fp[id]); return true; } bool EQEMuLog::writeNTS(LogIDs id, bool dofile, const char *fmt, ...) { va_list argptr, tmpargptr; va_start(argptr, fmt); if (dofile) { va_copy(tmpargptr, argptr); vfprintf( fp[id], fmt, tmpargptr ); } if (pLogStatus[id] & 2) { if (pLogStatus[id] & 8) vfprintf( stderr, fmt, argptr ); else vfprintf( stdout, fmt, argptr ); } va_end(argptr); return true; }; bool EQEMuLog::Dump(LogIDs id, uint8* data, uint32 size, uint32 cols, uint32 skip) { if (!logFileValid) { #if EQDEBUG >= 10 cerr << "Error: Dump() from null pointer"<= MaxLogID) return false; bool dofile = false; if (pLogStatus[id] & 1) { dofile = open(id); } if (!(dofile || pLogStatus[id] & 2)) return false; LockMutex lock(&MLog[id]); if (!logFileValid) return false; //check again for threading race reasons (to avoid two mutexes) write(id, "Dumping Packet: %i", size); // Output as HEX int beginningOfLineOffset = 0; uint32 indexInData; std::string asciiOutput; for(indexInData=skip; indexInData= 32 && data[indexInData] < 127) { // According to http://msdn.microsoft.com/en-us/library/vstudio/ee404875(v=vs.100).aspx // Visual Studio 2010 doesn't have std::to_string(int) but it does have the long long // version. asciiOutput.append(std::to_string((long long)data[indexInData])); } else { asciiOutput.append("."); } } uint32 k = ((indexInData-skip)-1)%cols; if (k < 8) writeNTS(id, dofile, " "); for (uint32 h = k+1; h < cols; h++) { writeNTS(id, dofile, " "); } writeNTS(id, dofile, " | %s\n", asciiOutput.c_str()); if (dofile) fflush(fp[id]); return true; } void EQEMuLog::SetCallback(LogIDs id, msgCallbackFmt proc) { if (!logFileValid) return; if (id >= MaxLogID) { return; } logCallbackFmt[id] = proc; } void EQEMuLog::SetCallback(LogIDs id, msgCallbackBuf proc) { if (!logFileValid) return; if (id >= MaxLogID) { return; } logCallbackBuf[id] = proc; } void EQEMuLog::SetCallback(LogIDs id, msgCallbackPva proc) { if (!logFileValid) return; if (id >= MaxLogID) { return; } logCallbackPva[id] = proc; } void EQEMuLog::SetAllCallbacks(msgCallbackFmt proc) { if (!logFileValid) return; int r; for(r = Status; r < MaxLogID; r++) { SetCallback((LogIDs)r, proc); } } void EQEMuLog::SetAllCallbacks(msgCallbackBuf proc) { if (!logFileValid) return; int r; for(r = Status; r < MaxLogID; r++) { SetCallback((LogIDs)r, proc); } } void EQEMuLog::SetAllCallbacks(msgCallbackPva proc) { if (!logFileValid) return; int r; for(r = Status; r < MaxLogID; r++) { SetCallback((LogIDs)r, proc); } }