legatt 32f4597c4d Base frontend module
- Naming style migrated to camel-case
- P7lib logger sources added
- Vcpkg manifest, CMake presets and build moved to root
- SDL2, Vulkan and RmlUi dependencies added
- Logger LOG macro added
- Logger  configuration
- SDL2 window configuration
- Vulkan configuration
- RmlUi configuration
- SDL event loop
2025-08-08 00:07:32 +03:00

700 lines
22 KiB
C++

////////////////////////////////////////////////////////////////////////////////
// /
// 2012-2020 (c) Baical /
// /
// This library is free software; you can redistribute it and/or /
// modify it under the terms of the GNU Lesser General Public /
// License as published by the Free Software Foundation; either /
// version 3.0 of the License, or (at your option) any later version. /
// /
// This library is distributed in the hope that it will be useful, /
// but WITHOUT ANY WARRANTY; without even the implied warranty of /
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU /
// Lesser General Public License for more details. /
// /
// You should have received a copy of the GNU Lesser General Public /
// License along with this library. /
// /
////////////////////////////////////////////////////////////////////////////////
#pragma once
#define FJOURNAL_FILE_BUFFER_LENGTH (0x20000)
#define FJOURNAL_FILE_BUFFER_MINIMUM_REMAINDER (0x2000)
#define FJOURNAL_THREAD_WAKEUP_INTERVAL (2500)
#define FJOURNAL_FILE_EXTENSION L"log"
#define FJOURNAL_DESCRIPTION_MAX_LENGTH (64)
#define FJOURNAL_MAX_FILES (7)
void __cdecl FJournal_Routine(void *i_pContext);
// 1. Dump to the file
// 2. must have parameters and possibility to export them
// 3. should have own thread to notify about writing, every 1-5 seconds.
class CJournal
: virtual public IJournal
{
private:
//put volatile variables at the top, to obtain 32 bit alignment.
//Project has 8 bytes alignment by default
volatile LONG m_lReference_Counter;
HANDLE m_hLog_File;
wchar_t *m_pJournal_Buffer;
wchar_t *m_pWrite_Buffer;
DWORD m_dwBuffer_Used;
HANDLE m_hThread;
HANDLE m_hEvent_Exit;
HANDLE m_hEvent_Write;
CRITICAL_SECTION m_tCS;
SYSTEMTIME m_tTime;
wchar_t m_pTypes_Description[IJournal::eLEVEL_COUNT][FJOURNAL_DESCRIPTION_MAX_LENGTH];
CWString m_cFile_Name;
UINT64 m_pCount[IJournal::eLEVEL_COUNT];
IJournal::eLevel m_eVerbosity;
IJournal::eTime m_eTime; //ignored for a while
tBOOL m_bFunction;
tBOOL m_bType;
BOOL m_bInitialized;
BOOL m_bError;
DWORD m_dwMax_Files;
public:
////////////////////////////////////////////////////////////////////////////
CJournal()
: m_lReference_Counter(1)
, m_hLog_File(INVALID_HANDLE_VALUE)
, m_pJournal_Buffer(NULL)
, m_pWrite_Buffer(NULL)
, m_dwBuffer_Used(0)
, m_hThread(NULL)
, m_hEvent_Exit(NULL)
, m_hEvent_Write(NULL)
, m_eVerbosity(IJournal::eLEVEL_TRACE)
, m_eTime(IJournal::eTIME_YYYYMMDDHHMMSSMS)
, m_bFunction(TRUE)
, m_bType(TRUE)
, m_bInitialized(FALSE)
, m_bError(FALSE)
, m_dwMax_Files(FJOURNAL_MAX_FILES)
{
memset(&m_tCS, 0, sizeof(CRITICAL_SECTION));
memset(&m_pCount, 0, sizeof(m_pCount));
InitializeCriticalSection(&m_tCS);
wcscpy_s(m_pTypes_Description[IJournal::eLEVEL_TRACE], FJOURNAL_DESCRIPTION_MAX_LENGTH, L"INFO : ");
wcscpy_s(m_pTypes_Description[IJournal::eLEVEL_DEBUG], FJOURNAL_DESCRIPTION_MAX_LENGTH, L"DEBUG: ");
wcscpy_s(m_pTypes_Description[IJournal::eLEVEL_INFO], FJOURNAL_DESCRIPTION_MAX_LENGTH, L"INFO : ");
wcscpy_s(m_pTypes_Description[IJournal::eLEVEL_WARNING], FJOURNAL_DESCRIPTION_MAX_LENGTH, L"WARN.: ");
wcscpy_s(m_pTypes_Description[IJournal::eLEVEL_ERROR], FJOURNAL_DESCRIPTION_MAX_LENGTH, L"ERROR: ");
wcscpy_s(m_pTypes_Description[IJournal::eLEVEL_CRITICAL], FJOURNAL_DESCRIPTION_MAX_LENGTH, L"CRIT.: ");
}
////////////////////////////////////////////////////////////////////////////
void Set_Files_Max_Count(DWORD i_dwVal)
{
m_dwMax_Files = i_dwVal;
}//Set_Files_Max_Count
////////////////////////////////////////////////////////////////////////////
tBOOL Initialize(const tXCHAR *i_pName)
{
if (TRUE == m_bInitialized)
{
return TRUE;
}
if ( (NULL == i_pName)
|| (TRUE == m_bError)
)
{
return FALSE;
}
CWString l_sDirectory;
if (FALSE == m_bError)
{
m_pJournal_Buffer = new wchar_t[FJOURNAL_FILE_BUFFER_LENGTH];
m_pWrite_Buffer = new wchar_t[FJOURNAL_FILE_BUFFER_LENGTH];
if ( (NULL == m_pJournal_Buffer) || (NULL == m_pWrite_Buffer) )
{
m_bError = TRUE;
}
}
if (FALSE == m_bError)
{
CFSYS::Get_Process_Directory(&l_sDirectory);
l_sDirectory.Append(2, L"\\", i_pName);
if (FALSE == CFSYS::Directory_Exists(l_sDirectory.Get()))
{
m_bError = (! CFSYS::Directory_Create(l_sDirectory.Get()));
}
}
if (FALSE == m_bError)
{
Remove_Old_Journals(l_sDirectory.Get(),
m_dwMax_Files,
FJOURNAL_FILE_EXTENSION
);
DWORD l_dwIterator = 0;
SYSTEMTIME l_tTime;
GetLocalTime(&l_tTime);
do
{
swprintf_s(m_pJournal_Buffer,
FJOURNAL_FILE_BUFFER_LENGTH,
L"%s\\%04d_%02d_%02d-%02d_%02d_%02d-%d.%s",
l_sDirectory.Get(),
(int)l_tTime.wYear,
(int)l_tTime.wMonth,
(int)l_tTime.wDay,
(int)l_tTime.wHour,
(int)l_tTime.wMinute,
(int)l_tTime.wSecond,
(int)l_dwIterator++,
FJOURNAL_FILE_EXTENSION
);
} while (CFSYS::File_Exists(m_pJournal_Buffer));
m_cFile_Name.Set((wchar_t*)m_pJournal_Buffer);
m_hLog_File = CreateFileW(m_pJournal_Buffer,
GENERIC_WRITE | GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL
);
if ( (NULL == m_hLog_File)
|| (INVALID_HANDLE_VALUE == m_hLog_File)
)
{
m_hLog_File = INVALID_HANDLE_VALUE;
m_bError = TRUE;
}
}
if (FALSE == m_bError)
{
m_hEvent_Exit = CreateEventW(NULL, FALSE, FALSE, NULL);
m_hEvent_Write = CreateEventW(NULL, TRUE, FALSE, NULL);
if ( (NULL == m_hEvent_Exit) || (NULL == m_hEvent_Write) )
{
m_bError = TRUE;
}
}
if (FALSE == m_bError)
{
m_pJournal_Buffer[0] = 0xFEFF;
m_dwBuffer_Used = 1;
m_hThread = (void*)_beginthread(&Routine_Entry_Point, 0, this);
if (NULL == m_hThread)
{
m_bError = TRUE;
}
}
if (FALSE == m_bError)
{
m_bInitialized = TRUE;
}
return (! m_bError);
}
////////////////////////////////////////////////////////////////////////////
void Set_Verbosity(IJournal::eLevel i_eVerbosity)
{
EnterCriticalSection(&m_tCS);
m_eVerbosity = i_eVerbosity;
LeaveCriticalSection(&m_tCS);
}
////////////////////////////////////////////////////////////////////////////
void Set_Format(IJournal::eTime i_eTime, tBOOL i_bFunction, tBOOL i_bType)
{
EnterCriticalSection(&m_tCS);
m_eTime = i_eTime;
m_bFunction = i_bFunction;
m_bType = i_bType;
LeaveCriticalSection(&m_tCS);
}
////////////////////////////////////////////////////////////////////////////
IJournal::eLevel Get_Verbosity()
{
IJournal::eLevel l_eResult;
EnterCriticalSection(&m_tCS);
l_eResult = m_eVerbosity;
LeaveCriticalSection(&m_tCS);
return l_eResult;
}
////////////////////////////////////////////////////////////////////////////
tBOOL Log(IJournal::eLevel i_eType,
IJournal::hModule i_hModule,
const char *i_pFile,
const char *i_pFunction,
tUINT32 i_dwLine,
const tXCHAR *i_pFormat,
...
)
{
tBOOL l_bResult = TRUE;
int l_iCount = 0;
wchar_t *l_pBuffer = NULL;
if ( (FALSE == m_bInitialized)
|| (NULL == i_pFormat)
|| (i_eType >= IJournal::eLEVEL_COUNT)
)
{
return FALSE;
}
EnterCriticalSection(&m_tCS);
m_pCount[i_eType]++;
if (i_eType < m_eVerbosity)
{
l_bResult = FALSE;
goto l_lExit;
}
if ( (m_dwBuffer_Used >= FJOURNAL_FILE_BUFFER_LENGTH)
|| (FJOURNAL_FILE_BUFFER_MINIMUM_REMAINDER >= (FJOURNAL_FILE_BUFFER_LENGTH - m_dwBuffer_Used) )
)
{
SetEvent(m_hEvent_Write);
while (WAIT_OBJECT_0 == WaitForSingleObject(m_hEvent_Write, 0))
{
Sleep(5);
}
}
if ( (l_bResult)
&& (i_pFunction)
&& (m_bFunction)
)
{
l_pBuffer = m_pJournal_Buffer + m_dwBuffer_Used;
while (*i_pFunction)
{
(*l_pBuffer++) = (*i_pFunction++);
m_dwBuffer_Used ++;
}
l_iCount = swprintf_s(m_pJournal_Buffer + m_dwBuffer_Used,
FJOURNAL_FILE_BUFFER_LENGTH - m_dwBuffer_Used,
L"() Ln %04d : ", (int)i_dwLine
);
if (0 > l_iCount)
{
l_bResult = FALSE;
}
else
{
m_dwBuffer_Used += l_iCount;
}
l_pBuffer = m_pJournal_Buffer + m_dwBuffer_Used;
(*l_pBuffer++) = 13;
(*l_pBuffer++) = 10;
m_dwBuffer_Used += 2;
}
if (l_bResult)
{
GetLocalTime(&m_tTime);
l_iCount = swprintf_s(m_pJournal_Buffer + m_dwBuffer_Used,
FJOURNAL_FILE_BUFFER_LENGTH - m_dwBuffer_Used,
L"[%04d.%02d.%02d %02d:%02d:%02d.%03d] %s ",
(int)m_tTime.wYear, (int)m_tTime.wMonth, (int)m_tTime.wDay,
(int)m_tTime.wHour, (int)m_tTime.wMinute, (int)m_tTime.wSecond, (int)m_tTime.wMilliseconds,
m_bType ? m_pTypes_Description[i_eType] : L""
);
if (0 > l_iCount)
{
l_bResult = FALSE;
}
else
{
m_dwBuffer_Used += l_iCount;
}
}
if (l_bResult)
{
va_list l_pArgList;
va_start(l_pArgList, i_pFormat);
size_t l_stRemaining = 0;
if (S_OK == StringCbVPrintfExW(m_pJournal_Buffer + m_dwBuffer_Used,
(FJOURNAL_FILE_BUFFER_LENGTH - m_dwBuffer_Used) * sizeof(wchar_t),
NULL,
&l_stRemaining,
STRSAFE_IGNORE_NULLS,
i_pFormat,
l_pArgList
)
)
{
m_dwBuffer_Used = FJOURNAL_FILE_BUFFER_LENGTH - (DWORD)(l_stRemaining / sizeof(wchar_t));
}
else
{
l_bResult = FALSE;
}
va_end(l_pArgList);
}
if (l_bResult)
{
l_pBuffer = m_pJournal_Buffer + m_dwBuffer_Used;
(*l_pBuffer++) = 13;
(*l_pBuffer++) = 10;
m_dwBuffer_Used += 2;
}
l_lExit:
LeaveCriticalSection(&m_tCS);
return l_bResult;
}
////////////////////////////////////////////////////////////////////////////
wchar_t *Get_File_Name()
{
if (FALSE == m_bInitialized)
{
return NULL;
}
return m_cFile_Name.Get();
}
////////////////////////////////////////////////////////////////////////////
tUINT64 Get_Count(IJournal::eLevel i_eType)
{
UINT64 l_qwReturn = 0ULL;
if (i_eType >= IJournal::eLEVEL_COUNT)
{
return 0ULL;
}
EnterCriticalSection(&m_tCS);
l_qwReturn = m_pCount[i_eType];
LeaveCriticalSection(&m_tCS);
return l_qwReturn;
}
////////////////////////////////////////////////////////////////////////////
tINT32 Add_Ref()
{
return InterlockedIncrement(&m_lReference_Counter);
}
////////////////////////////////////////////////////////////////////////////
tINT32 Release()
{
volatile tINT32 l_iRCnt = InterlockedDecrement(&m_lReference_Counter);
if ( 0 >= l_iRCnt)
{
delete this;
}
return l_iRCnt;
}
////////////////////////////////////////////////////////////////////////////
BOOL Get_Initialized()
{
return m_bInitialized;
}
////////////////////////////////////////////////////////////////////////////
tBOOL Register_Thread(const tXCHAR *i_pName, tUINT32 i_dwThreadId)
{
return FALSE;
}
////////////////////////////////////////////////////////////////////////////
tBOOL Unregister_Thread(tUINT32 i_dwThreadId)
{
return FALSE;
}
////////////////////////////////////////////////////////////////////////////
tBOOL Register_Module(const tXCHAR *i_pName, IJournal::hModule *o_hModule)
{
if (o_hModule)
{
*o_hModule = NULL;
}
return FALSE;
}
protected:
////////////////////////////////////////////////////////////////////////////
static void __cdecl Routine_Entry_Point(void *i_pContext)
{
CJournal * l_pFJournal = (CJournal *)i_pContext;
if (l_pFJournal)
{
l_pFJournal->Routine();
}
_endthread();
}
////////////////////////////////////////////////////////////////////////////
void Routine()
{
BOOL l_bExit = FALSE;
HANDLE l_pEvents[] = {m_hEvent_Exit, m_hEvent_Write};
DWORD l_dwWFMO_result = 0;
DWORD l_dwSize = 0;
while (FALSE == l_bExit)
{
l_dwWFMO_result = WaitForMultipleObjects(sizeof(l_pEvents)/sizeof(l_pEvents[0]),
l_pEvents,
FALSE,
FJOURNAL_THREAD_WAKEUP_INTERVAL
); //Process events
// Exit signal
if (WAIT_OBJECT_0 == l_dwWFMO_result)
{
l_dwSize = Copy_Bufer(TRUE);
if (l_dwSize)
{
Write((BYTE*)m_pWrite_Buffer, l_dwSize);
}
l_bExit = TRUE;
}
else if ( (WAIT_OBJECT_0 + 1) == l_dwWFMO_result)// we have received signal for write
{
l_dwSize = Copy_Bufer(FALSE);
ResetEvent(m_hEvent_Write);
if ( (l_dwSize) && (FALSE == Write((BYTE*)m_pWrite_Buffer, l_dwSize)) )
{
l_bExit = TRUE;
}
}
else if (WAIT_TIMEOUT == l_dwWFMO_result)
{
l_dwSize = Copy_Bufer(TRUE);
if ( (l_dwSize) && (FALSE == Write((BYTE*)m_pWrite_Buffer, l_dwSize)) )
{
l_bExit = TRUE;
}
}
}
m_hThread = NULL;
}
private:
////////////////////////////////////////////////////////////////////////////
DWORD Copy_Bufer(BOOL i_bLock)
{
DWORD l_dwResult = 0;
if (i_bLock)
{
EnterCriticalSection(&m_tCS);
}
if (m_dwBuffer_Used)
{
l_dwResult = m_dwBuffer_Used * sizeof(wchar_t);
memcpy_s(m_pWrite_Buffer, FJOURNAL_FILE_BUFFER_LENGTH * sizeof(wchar_t), m_pJournal_Buffer, l_dwResult);
m_dwBuffer_Used = 0;
}
if (i_bLock)
{
LeaveCriticalSection(&m_tCS);
}
return l_dwResult;
}
////////////////////////////////////////////////////////////////////////////
BOOL Write(BYTE *i_pBuffer, DWORD i_dwSize)
{
BOOL l_bResult = TRUE;
DWORD l_dwReturned = 0;
DWORD l_dwWriten = 0;
if ( (0 >= i_dwSize) || (NULL == i_pBuffer) )
{
return FALSE;
}
while ( l_dwWriten != i_dwSize )
{
l_dwReturned = 0;
if ( 0 == WriteFile(m_hLog_File,
i_pBuffer + l_dwWriten,
i_dwSize - l_dwWriten,
&l_dwReturned,
NULL)
)
{
i_dwSize = 0;
l_dwWriten = 0;
l_bResult = FALSE;
}
else
{
l_dwWriten += l_dwReturned;
}
} //while
return l_bResult;
}
////////////////////////////////////////////////////////////////////////////
BOOL Remove_Old_Journals(wchar_t *i_pFolder, DWORD i_dwMax_Files, const wchar_t *i_pExtension)
{
wchar_t l_pFileName[ MAX_PATH ] = L"9999";
DWORD l_dwPrev_Error_Mode = 0;
WIN32_FIND_DATAW l_tFind_Info;
HANDLE l_hFind = INVALID_HANDLE_VALUE;
DWORD l_dwCount = 0;
BOOL l_bResult = TRUE;
if ( (NULL == i_pFolder) || (NULL == i_pExtension) )
{
return FALSE;
}
l_dwPrev_Error_Mode = SetErrorMode(SEM_FAILCRITICALERRORS);
l_dwCount = i_dwMax_Files + 1;
while ( (l_dwCount > i_dwMax_Files) && (l_bResult) )
{
l_dwCount = 0;
swprintf_s(m_pJournal_Buffer, FJOURNAL_FILE_BUFFER_LENGTH, L"%s\\*.%s", i_pFolder, i_pExtension);
l_hFind = FindFirstFileW(m_pJournal_Buffer, &l_tFind_Info);
while(INVALID_HANDLE_VALUE != l_hFind)
{
if (0 == (l_tFind_Info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
{
l_dwCount ++;
if (0 <= _wcsicmp(l_pFileName, l_tFind_Info.cFileName))
{
wcscpy_s(l_pFileName, MAX_PATH, l_tFind_Info.cFileName);
}
if ( ! FindNextFileW(l_hFind, &l_tFind_Info))
{
FindClose(l_hFind);
l_hFind = INVALID_HANDLE_VALUE;
}
}
} //while(INVALID_HANDLE_VALUE != hFind)
SetErrorMode(l_dwPrev_Error_Mode);
if (l_dwCount > i_dwMax_Files)
{
swprintf_s(m_pJournal_Buffer, FJOURNAL_FILE_BUFFER_LENGTH, L"%s\\%s", i_pFolder, l_pFileName);
DWORD l_dwFAttr = GetFileAttributesW(m_pJournal_Buffer);
l_dwFAttr = l_dwFAttr & (~FILE_ATTRIBUTE_ARCHIVE);
l_dwFAttr = l_dwFAttr & (~FILE_ATTRIBUTE_READONLY);
l_dwFAttr = l_dwFAttr & (~FILE_ATTRIBUTE_SYSTEM);
SetFileAttributesW(m_pJournal_Buffer, l_dwFAttr);
l_bResult = DeleteFileW(m_pJournal_Buffer);
l_dwCount --;
wcscpy_s(l_pFileName, MAX_PATH, L"9999");
}
}
return l_bResult;
}
////////////////////////////////////////////////////////////////////////////
virtual ~CJournal() //use Release() instead
{
if (m_hThread)
{
if (m_hEvent_Exit)
{
SetEvent(m_hEvent_Exit);
if (WAIT_TIMEOUT == WaitForSingleObject(m_hThread, 150000))
{
OutputDebugStringW(L"~CFJournal::Thread exit failed !\n");
}
CloseHandle(m_hEvent_Exit);
m_hEvent_Exit = NULL;
}
}
if (m_hEvent_Write)
{
CloseHandle(m_hEvent_Write);
m_hEvent_Write = NULL;
}
if (INVALID_HANDLE_VALUE != m_hLog_File)
{
CloseHandle(m_hLog_File);
m_hLog_File = INVALID_HANDLE_VALUE;
}
if (m_pJournal_Buffer)
{
delete [] m_pJournal_Buffer;
m_pJournal_Buffer = NULL;
}
if (m_pWrite_Buffer)
{
delete [] m_pWrite_Buffer;
m_pWrite_Buffer = NULL;
}
DeleteCriticalSection(&m_tCS);
}
};