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

635 lines
20 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. /
// /
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// This header file provide printing to text file /
////////////////////////////////////////////////////////////////////////////////
#ifndef CLTEXTFILE_H
#define CLTEXTFILE_H
#include "IFile.h"
#include "PFile.h"
#define TXT_FILE_DEF_FILES_COUNT 0
#define TXT_FILE_EXT TM("txt")
#define TXT_FILE_WRITE_PERIOD 1000u
////////////////////////////////////////////////////////////////////////////////
class CClTextFile
: public CClTextSink
{
enum eRolling
{
EROLLING_NONE,
EROLLING_MEGABYTES,
EROLLING_HOURS,
EROLLING_TIME,
EROLLING_MAX
};
CPFile m_cFile;
CWString m_pDir;
eRolling m_eRolling;
tUINT64 m_qwRolling_Value;
tUINT32 m_dwFile_Tick;
tUINT32 m_dwWrite_Tick;
tUINT64 m_qwFile_Size;
tUINT32 m_dwFiles_Max_Count;
tUINT64 m_qwFiles_Max_Size;
CBList<CWString*> m_cFiles;
tXCHAR *m_pBuffer;
size_t m_szBuffer;
size_t m_szBufferOffs;
CUintList m_cSecondsList;
tUINT32 m_uiSecond;
tUINT32 m_dwIndex;
public:
////////////////////////////////////////////////////////////////////////////
//CClTextFile
CClTextFile()
: m_eRolling(EROLLING_NONE)
, m_qwRolling_Value(0ull)
, m_dwFile_Tick(0u)
, m_dwWrite_Tick(0ull)
, m_qwFile_Size(0ull)
, m_dwFiles_Max_Count(0u)
, m_qwFiles_Max_Size(0u)
, m_pBuffer(NULL)
, m_szBuffer(0x10000)
, m_szBufferOffs(0)
, m_dwIndex(0)
{
m_dwWrite_Tick = GetTickCount();
m_uiSecond = GetSecondOfDay();
}
////////////////////////////////////////////////////////////////////////////
//~CClTextFile
virtual ~CClTextFile()
{
m_cFile.Write((tUINT8*)m_pBuffer, m_szBufferOffs * sizeof(tXCHAR), FALSE);
m_cFile.Close(TRUE);
m_cFiles.Clear(TRUE);
if (m_pBuffer)
{
free(m_pBuffer);
m_pBuffer = NULL;
}
m_szBufferOffs = 0;
m_cSecondsList.Clear(TRUE);
}
////////////////////////////////////////////////////////////////////////////
//Initialize
virtual eClient_Status Initialize(tXCHAR **i_pArgs, tINT32 i_iCount)
{
eClient_Status l_eReturn = ECLIENT_STATUS_OK;
tXCHAR *l_pArgV = NULL;
pAList_Cell l_pStart = NULL;
tUINT32 l_dwDirLen = 0;
m_pDir.Realloc(4096);
m_pBuffer = (tXCHAR*)malloc(m_szBuffer * sizeof(tXCHAR));
if (!m_pBuffer)
{
PRINTF(TM("P7:Can't allocate memory\n"));
l_eReturn = ECLIENT_STATUS_INTERNAL_ERROR;
P7_Set_Last_Error(eP7_Error_MemoryAllocation);
goto l_lblExit;
}
//unicode marker
#ifdef UTF8_ENCODING
m_pBuffer[0] = (char)0xEF;
m_pBuffer[1] = (char)0xBB;
m_pBuffer[2] = (char)0xBF;
m_szBufferOffs = 3;
m_qwFile_Size = 3;
#else
m_pBuffer[0] = 0xFEFF;
m_szBufferOffs = 1;
m_qwFile_Size = sizeof(tXCHAR);
#endif
////////////////////////////////////////////////////////////////////////////
//get maximum count of the log files
l_pArgV = Get_Argument_Text_Value(i_pArgs, i_iCount,
(tXCHAR*)CLIENT_COMMAND_LINE_FILES_COUNT_MAX
);
if (l_pArgV)
{
m_dwFiles_Max_Count = (tUINT32)PStrToInt(l_pArgV);
if (4096 < m_dwFiles_Max_Count)
{
m_dwFiles_Max_Count = TXT_FILE_DEF_FILES_COUNT;
}
}
l_pArgV = Get_Argument_Text_Value(i_pArgs, i_iCount,
(tXCHAR*)CLIENT_COMMAND_LINE_FILES_SIZE_MAX
);
if (l_pArgV)
{
m_qwFiles_Max_Size = (tUINT64)PStrToInt(l_pArgV) * 1048576ull;
}
////////////////////////////////////////////////////////////////////////////
//get path to storage
l_pArgV = Get_Argument_Text_Value(i_pArgs, i_iCount,
(tXCHAR*)CLIENT_COMMAND_LINE_DIR
);
if (l_pArgV)
{
m_pDir.Set(l_pArgV);
}
else
{
CProc::Get_Process_Path(m_pDir.Get(), m_pDir.Max_Length());
m_pDir.Append(1, TM("/P7logs/"));
}
if (FALSE == CFSYS::Directory_Exists(m_pDir.Get()))
{
if (FALSE == CFSYS::Directory_Create(m_pDir.Get()))
{
P7_Set_Last_Error(eP7_Error_FolderCreation);
PRINTF(TM("P7:Can't create directory: %s\n"), l_pArgV);
l_eReturn = ECLIENT_STATUS_NOT_ALLOWED;
goto l_lblExit;
}
}
////////////////////////////////////////////////////////////////////////////
//enumerate files & sorting ....
if ( (TXT_FILE_DEF_FILES_COUNT != m_dwFiles_Max_Count)
|| (m_qwFiles_Max_Size)
)
{
CFSYS::Enumerate_Files(&m_cFiles, &m_pDir, TM("*.") TXT_FILE_EXT, 0);
l_pStart = NULL;
l_dwDirLen = m_pDir.Length();
while ((l_pStart = m_cFiles.Get_Next(l_pStart)))
{
pAList_Cell l_pMin = l_pStart;
pAList_Cell l_pIter = l_pStart;
while ((l_pIter = m_cFiles.Get_Next(l_pIter)))
{
CWString *l_pPathM = m_cFiles.Get_Data(l_pMin);
CWString *l_pPathI = m_cFiles.Get_Data(l_pIter);
tXCHAR *l_pNameM = l_pPathM->Get() + l_dwDirLen;
tXCHAR *l_pNameI = l_pPathI->Get() + l_dwDirLen;
if (0 < PStrICmp(l_pNameM, l_pNameI))
{
l_pMin = l_pIter;
}
}
if (l_pMin != l_pStart)
{
m_cFiles.Extract(l_pMin);
m_cFiles.Put_After(m_cFiles.Get_Prev(l_pStart), l_pMin);
l_pStart = l_pMin;
}
} //while (l_pStart = m_cFiles.Get_Next(l_pStart))
}
////////////////////////////////////////////////////////////////////////////
//create file
l_eReturn = Create_File();
if (ECLIENT_STATUS_OK != l_eReturn)
{
P7_Set_Last_Error(eP7_Error_FileCreation);
PRINTF(TM("P7:File creation failed"));
goto l_lblExit;
}
////////////////////////////////////////////////////////////////////////////
//get rolling type
l_pArgV = Get_Argument_Text_Value(i_pArgs, i_iCount,
(tXCHAR*)CLIENT_COMMAND_LINE_FILE_ROLLING
);
if (l_pArgV)
{
CWString l_cRolling(l_pArgV);
tUINT32 l_dwLen = l_cRolling.Length();
tBOOL l_bError = FALSE;
if (3 <= l_dwLen)
{
tXCHAR *l_pSuffix = l_cRolling.Get() + l_dwLen - 2;
if (0 == PStrICmp(l_pSuffix, TM("hr")))
{
m_eRolling = EROLLING_HOURS;
}
else if (0 == PStrICmp(l_pSuffix, TM("mb")))
{
m_eRolling = EROLLING_MEGABYTES;
}
else if (0 == PStrICmp(l_pSuffix, TM("tm")))
{
m_eRolling = EROLLING_TIME;
}
else
{
l_bError = TRUE;
}
//cut off suffix
l_cRolling.Trim(l_dwLen - 2);
}
else
{
l_bError = TRUE;
}
if (FALSE == l_bError)
{
if (m_eRolling != EROLLING_TIME)
{
m_qwRolling_Value = PStrToInt(l_cRolling.Get());
if (0 >= m_qwRolling_Value)
{
m_eRolling = EROLLING_NONE;
l_bError = TRUE;
}
}
else
{
if (!Parse_Rolling_Time(l_cRolling.Get()))
{
m_eRolling = EROLLING_NONE;
l_bError = TRUE;
}
}
}
if (l_bError)
{
PRINTF(TM("P7:Rolling value is not correct = %s\n"), l_cRolling.Get());
}
if (EROLLING_MEGABYTES == m_eRolling)
{
m_qwRolling_Value *= 0x100000ULL; //megabytes
}
else if (EROLLING_HOURS == m_eRolling)
{
if (m_qwRolling_Value > 1000)
{
PRINTF(TM("P7:Rolling value is more than 1000 hours, cutting\n"));
m_qwRolling_Value = 1000;
}
m_qwRolling_Value *= 3600000ULL; //milliseconds in one hour
}
}//if (l_pArgV)
l_lblExit:
return l_eReturn;
}
////////////////////////////////////////////////////////////////////////////////
//Parse_Rolling_Time
tBOOL Parse_Rolling_Time(const tXCHAR *i_pTime)
{
const tXCHAR *l_pIter = i_pTime;
const tXCHAR *l_pBegin = i_pTime;
tBOOL l_bReturn = TRUE;
while (*l_pIter)
{
tUINT32 l_uiSeconds = 0;
tUINT32 l_uiValue = 0;
l_pBegin = l_pIter; //scan hours
while ( (*l_pIter >= TM('0'))
&& (*l_pIter <= TM('9'))
)
{
l_uiValue = l_uiValue * 10 + (*l_pIter - TM('0'));
l_pIter ++;
}
if (l_pBegin == l_pIter) //no digits
{
l_bReturn = FALSE;
break;
}
l_uiSeconds = l_uiValue * 3600;
if (*l_pIter != TM(':')) //error
{
l_bReturn = FALSE;
break;
}
l_pIter++;
l_pBegin = l_pIter; //scan minutes
l_uiValue = 0;
while ( (*l_pIter >= TM('0'))
&& (*l_pIter <= TM('9'))
)
{
l_uiValue = l_uiValue * 10 + (*l_pIter - TM('0'));
l_pIter ++;
}
if (l_pBegin == l_pIter) //no digits
{
l_bReturn = FALSE;
break;
}
l_uiSeconds += l_uiValue * 60;
m_cSecondsList.Add_After(m_cSecondsList.Get_Last(), l_uiSeconds);
if (*l_pIter == TM(','))
{
l_pIter++;
}
}
if (!m_cSecondsList.Count())
{
l_bReturn = FALSE;
}
return l_bReturn;
}//Parse_Rolling_Time
////////////////////////////////////////////////////////////////////////////
//Log
virtual eClient_Status Log(const CClTextSink::sLog &i_rRawLog,
const tXCHAR *i_pFmtLog,
size_t i_szFmtLog
)
{
UNUSED_ARG(i_rRawLog);
if ( ((i_szFmtLog + 16) > (m_szBuffer - m_szBufferOffs))
|| (CTicks::Difference(GetTickCount(), m_dwWrite_Tick) >= TXT_FILE_WRITE_PERIOD)
)
{
if ((i_szFmtLog + 16) > m_szBuffer)
{
if (m_szBufferOffs)
{
m_cFile.Write((tUINT8*)m_pBuffer, m_szBufferOffs * sizeof(tXCHAR), FALSE);
m_szBufferOffs = 0u;
}
m_cFile.Write((tUINT8*)i_pFmtLog, i_szFmtLog * sizeof(tXCHAR), FALSE);
m_qwFile_Size += (i_szFmtLog + 2) * sizeof(tXCHAR);
*(m_pBuffer) = TM('\r');
*(m_pBuffer + 1) = TM('\n');
m_szBufferOffs += 2;
TryRoll();
return ECLIENT_STATUS_OK;
}
else
{
m_cFile.Write((tUINT8*)m_pBuffer, m_szBufferOffs * sizeof(tXCHAR), FALSE);
m_szBufferOffs = 0;
TryRoll();
}
m_dwWrite_Tick = GetTickCount();
}
memcpy(m_pBuffer + m_szBufferOffs, i_pFmtLog, sizeof(tXCHAR) * i_szFmtLog);
m_szBufferOffs += i_szFmtLog;
*(m_pBuffer + m_szBufferOffs) = TM('\r');
*(m_pBuffer + m_szBufferOffs + 1) = TM('\n');
m_szBufferOffs += 2;
m_qwFile_Size += (i_szFmtLog + 2) * sizeof(tXCHAR);
return ECLIENT_STATUS_OK;
}
////////////////////////////////////////////////////////////////////////////
// TryRoll
eClient_Status TryRoll()
{
if ( ( (EROLLING_HOURS == m_eRolling)
&& (CTicks::Difference(GetTickCount(), m_dwFile_Tick) > m_qwRolling_Value)
)
|| ( (EROLLING_MEGABYTES == m_eRolling)
&& (m_qwRolling_Value <= m_qwFile_Size)
)
)
{
Roll();
}
else if (EROLLING_TIME == m_eRolling)
{
tUINT32 l_uiNextSecond = GetSecondOfDay();
pAList_Cell l_pEl = NULL;
while ((l_pEl = m_cSecondsList.Get_Next(l_pEl)))
{
tUINT32 l_uiIem = m_cSecondsList.Get_Data(l_pEl);
if ( (m_uiSecond <= l_uiIem)
&& (l_uiNextSecond > l_uiIem)
)
{
Roll();
break;
}
}
m_uiSecond = l_uiNextSecond;
}
return ECLIENT_STATUS_OK;
}// TryRoll
////////////////////////////////////////////////////////////////////////////
// Roll
void Roll()
{
m_cFile.Write((tUINT8*)m_pBuffer, m_szBufferOffs * sizeof(tXCHAR), TRUE);
m_cFile.Close(TRUE);
m_qwFile_Size = 0ull;
m_szBufferOffs = 0u;
Create_File();
m_dwWrite_Tick = GetTickCount();
}// Roll
////////////////////////////////////////////////////////////////////////////
// Create_File
eClient_Status Create_File()
{
eClient_Status l_eReturn = ECLIENT_STATUS_OK;
tUINT32 l_dwYear = 0;
tUINT32 l_dwMonth = 0;
tUINT32 l_dwDay = 0;
tUINT32 l_dwHr = 0;
tUINT32 l_dwMin = 0;
tUINT32 l_dwSec = 0;
tUINT32 l_dwmSec = 0;
CWString l_cFilePath;
tXCHAR l_pFile_Name[64];
m_cFile.Close(TRUE);
CSys::Get_DateTime(l_dwYear,
l_dwMonth,
l_dwDay,
l_dwHr,
l_dwMin,
l_dwSec,
l_dwmSec
);
l_dwmSec = 0;
do
{
PSPrint(l_pFile_Name,
LENGTH(l_pFile_Name),
TM("/%04d%02d%02d-%02d%02d%02d%03d.") TXT_FILE_EXT,
l_dwYear,
l_dwMonth,
l_dwDay,
l_dwHr,
l_dwMin,
l_dwSec,
m_dwIndex
);
l_cFilePath.Set(m_pDir.Get());
l_cFilePath.Append(1, l_pFile_Name);
m_dwIndex ++;
} while (TRUE == CFSYS::File_Exists(l_cFilePath.Get()));
if (FALSE == m_cFile.Open(l_cFilePath.Get(),
IFile::ECREATE | IFile::EACCESS_WRITE | IFile::ESHARE_READ
)
)
{
PRINTF(TM("P7:Can't create file: %s"), l_cFilePath.Get());
l_eReturn = ECLIENT_STATUS_INTERNAL_ERROR;
}
else
{
m_cFiles.Add_After(m_cFiles.Get_Last(), new CWString(l_cFilePath.Get()));
if (TXT_FILE_DEF_FILES_COUNT != m_dwFiles_Max_Count)
{
while (m_dwFiles_Max_Count < m_cFiles.Count())
{
pAList_Cell l_pEl = m_cFiles.Get_First();
CWString *l_pPath = m_cFiles.Get_Data(l_pEl);
if (l_pPath)
{
if (!CFSYS::Delete_File(l_pPath->Get()))
{
PRINTF(TM("P7:Can't delete file: %s"), l_pPath->Get());
}
}
m_cFiles.Del(l_pEl, TRUE);
}
}
if (0 != m_qwFiles_Max_Size)
{
pAList_Cell l_pEl = NULL;
tUINT64 l_qwTotalSize = 0;
tUINT64 l_qwFileSize = 0;
while ((l_pEl = m_cFiles.Get_Next(l_pEl)))
{
CWString *l_pPath = m_cFiles.Get_Data(l_pEl);
if (l_pPath)
{
l_qwTotalSize += CFSYS::Get_File_Size(l_pPath->Get());
}
}
while (l_qwTotalSize > (m_qwFiles_Max_Size))
{
pAList_Cell l_pEl = m_cFiles.Get_First();
CWString *l_pPath = m_cFiles.Get_Data(l_pEl);
if (l_pPath)
{
l_qwFileSize = CFSYS::Get_File_Size(l_pPath->Get());
if (l_qwFileSize < l_qwTotalSize)
{
l_qwTotalSize -= l_qwFileSize;
}
else
{
l_qwTotalSize = 0ull;
}
if (!CFSYS::Delete_File(l_pPath->Get()))
{
PRINTF(TM("Can't delete file: %s"), l_pPath->Get());
}
}
m_cFiles.Del(l_pEl, TRUE);
}
}
}
m_dwFile_Tick = GetTickCount();
return l_eReturn;
}// Create_File
virtual eClient_Status DumpBuffers()
{
if (m_szBufferOffs)
{
m_cFile.Write((tUINT8*)m_pBuffer, m_szBufferOffs * sizeof(tXCHAR), TRUE);
m_szBufferOffs = 0;
}
return ECLIENT_STATUS_OK;
}
};
#endif //CLTEXTFILE_H