diff --git a/src/engine/configEngine.cpp b/src/engine/configEngine.cpp index 5589e064b..a265f0bee 100644 --- a/src/engine/configEngine.cpp +++ b/src/engine/configEngine.cpp @@ -23,6 +23,7 @@ #ifdef _WIN32 #include "winStuff.h" #define CONFIG_FILE "\\furnace.cfg" +#define LOG_FILE "\\furnace.log" #else #ifdef __HAIKU__ #include diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 281e7c5d5..6ee8513f5 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -4221,7 +4221,7 @@ bool DivEngine::deinitAudioBackend(bool dueToSwitchMaster) { return true; } -bool DivEngine::init() { +void DivEngine::preInit() { // register systems if (!systemsRegistered) registerSystems(); @@ -4229,8 +4229,13 @@ bool DivEngine::init() { initConfDir(); logD("config path: %s",configPath.c_str()); + String logPath=configPath+DIR_SEPARATOR_STR+"furnace.log"; + startLogFile(logPath.c_str()); + loadConf(); +} +bool DivEngine::init() { loadSampleROMs(); // set default system preset diff --git a/src/engine/engine.h b/src/engine/engine.h index 0e10ff66a..f57bd8d0c 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -1012,6 +1012,9 @@ class DivEngine { // quit dispatch void quitDispatch(); + // pre-initialize the engine. + void preInit(); + // initialize the engine. bool init(); diff --git a/src/log.cpp b/src/log.cpp index ef2750a2e..320237911 100644 --- a/src/log.cpp +++ b/src/log.cpp @@ -18,6 +18,8 @@ */ #include "ta-log.h" +#include +#include #ifdef IS_MOBILE int logLevel=LOGLEVEL_TRACE; @@ -25,11 +27,54 @@ int logLevel=LOGLEVEL_TRACE; int logLevel=LOGLEVEL_INFO; #endif +FILE* logFile; +char* logFileBuf; +unsigned int logFilePosI=0; +unsigned int logFilePosO=0; +std::thread* logFileThread; +std::mutex logFileLock; +std::mutex logFileLockI; +std::condition_variable logFileNotify; +bool logFileAvail=false; + std::atomic logPosition; LogEntry logEntries[TA_LOG_SIZE]; static constexpr unsigned int TA_LOG_MASK=TA_LOG_SIZE-1; +static constexpr unsigned int TA_LOGFILE_BUF_MASK=TA_LOGFILE_BUF_SIZE-1; + +const char* logTypes[5]={ + "ERROR", + "warning", + "info", + "debug", + "trace" +}; + +void appendLogBuf(const char* msg, size_t len) { + logFileLockI.lock(); + + int remaining=logFilePosO-logFilePosI; + if (remaining<=0) remaining+=TA_LOGFILE_BUF_SIZE; + + if (len>=(unsigned int)remaining) { + printf("line too long to fit in log buffer!\n"); + logFileLockI.unlock(); + return; + } + + if ((logFilePosI+len)>=TA_LOGFILE_BUF_SIZE) { + size_t firstWrite=TA_LOGFILE_BUF_SIZE-logFilePosI; + memcpy(logFileBuf+logFilePosI,msg,firstWrite); + memcpy(logFileBuf,msg+firstWrite,len-firstWrite); + } else { + memcpy(logFileBuf+logFilePosI,msg,len); + } + + logFilePosI=(logFilePosI+len)&TA_LOGFILE_BUF_MASK; + logFileLockI.unlock(); +} int writeLog(int level, const char* msg, fmt::printf_args args) { time_t thisMakesNoSense=time(NULL); @@ -54,6 +99,20 @@ int writeLog(int level, const char* msg, fmt::printf_args args) { logEntries[pos].loglevel=level; logEntries[pos].ready=true; + // write to log file + if (logFileAvail) { + std::string toWrite=fmt::sprintf( + "%02d:%02d:%02d [%s] %s\n", + logEntries[pos].time.tm_hour, + logEntries[pos].time.tm_min, + logEntries[pos].time.tm_sec, + logTypes[logEntries[pos].loglevel], + logEntries[pos].text + ); + appendLogBuf(toWrite.c_str(),toWrite.size()); + logFileNotify.notify_one(); + } + if (logLevel lock(logFileLock); + while (true) { + unsigned int logFilePosICopy=logFilePosI; + if (logFilePosICopy!=logFilePosO) { + // write + if (logFilePosO>logFilePosICopy) { + fwrite(logFileBuf+logFilePosO,1,TA_LOGFILE_BUF_SIZE-logFilePosO,logFile); + logFilePosO=0; + } else { + fwrite(logFileBuf+logFilePosO,1,logFilePosICopy-logFilePosO,logFile); + logFilePosO=logFilePosICopy; + } + } else { + // wait + if (!logFileAvail) break; + fflush(logFile); + logFileNotify.wait(lock); + } + } +} + +bool startLogFile(const char* path) { + if (logFileAvail) return true; + + // rotate log file if possible + + // open log file + if ((logFile=fopen(path,"w+"))==NULL) { + logFileAvail=false; + logW("could not open log file! (%s)",strerror(errno)); + return false; + } + + logFileBuf=new char[TA_LOGFILE_BUF_SIZE]; + logFilePosI=0; + logFilePosO=0; + logFileAvail=true; + + logFileThread=new std::thread(_logFileThread); + return true; +} + +bool finishLogFile() { + if (!logFileAvail) return false; + + logFileAvail=false; + + // flush + logFileLockI.lock(); + logFileNotify.notify_one(); + logFileThread->join(); + logFileLockI.unlock(); + + fclose(logFile); + return true; +} \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index bea535a46..019ba75d2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -416,7 +416,11 @@ int main(int argc, char** argv) { logI("usage: %s file",argv[0]); return 1; } + logI("Furnace version " DIV_VERSION "."); + + e.preInit(); + if (!fileName.empty()) { logI("loading module..."); FILE* f=ps_fopen(fileName.c_str(),"rb"); @@ -584,6 +588,8 @@ int main(int argc, char** argv) { logI("stopping engine."); e.quit(); + finishLogFile(); + #ifdef _WIN32 if (coResult==S_OK || coResult==S_FALSE) { CoUninitialize(); diff --git a/src/ta-log.h b/src/ta-log.h index f7921c710..160201e03 100644 --- a/src/ta-log.h +++ b/src/ta-log.h @@ -35,6 +35,9 @@ // this has to be a power of 2 #define TA_LOG_SIZE 2048 +// this as well +#define TA_LOGFILE_BUF_SIZE 65536 + extern int logLevel; extern std::atomic logPosition; @@ -76,4 +79,6 @@ template int logE(const char* msg, const T&... args) { } void initLog(); +bool startLogFile(const char* path); +bool finishLogFile(); #endif