//------------------------------------------------------------------------------
// ObjectWindows
// Copyright (c) 1998 by Yura Bidus
//
/// \file
/// This source file implements TConfigFile,TIniConfigFile,TRegConfigFile and
/// TConfigFileSection.
//------------------------------------------------------------------------------
#include <owl/pch.h>
//#pragma hdrstop
 
#include <owl/configfl.h>
#include <owl/file.h>
#include <owl/date.h>
#include <owl/time.h>
#include <owl/template.h>
#include <owl/pointer.h>
#include <owl/wsyscls.h>
#include <owl/color.h>
#include <owl/private/strmdefs.h>
#include <algorithm>
 
#if defined WINELIB
//#include <wine/msvcrt/stdlib.h> //for _ltoa, _itoa
#endif
 
using namespace std;
 
namespace owl {
 
static const tchar szNO[]    = _T("no");
static const tchar szOFF[]   = _T("off");
static const tchar szZERO[]  = _T("0");
static const tchar szFALSE[] = _T("false");
static const tchar szYES[]   = _T("yes");
static const tchar szON[]    = _T("on");
static const tchar szONE[]   = _T("1");
static const tchar szTRUE[]  = _T("true");
//static const tchar szDef[]   = _T("!)@(#*$&%^");
static const tchar szSep[]   = _T("\\");
 
int  TConfigFile::DefaultPrecision = -1;
 
namespace {
 
auto SkipWhitespace_(LPCTSTR sz) -> LPCTSTR
{
  PRECONDITION(sz);
  while (*sz == _T(' ') || *sz == _T('\t'))
    ++sz;
  return sz;
}
 
} // anonymous namespace
 
//
///  This static method converts a string to a bool, accepting most common
///  boolean keywords: yes, on, true and 1.
//
bool TConfigFile::StringToBool(LPCTSTR str, bool *pVal)
{
  PRECONDITION(str && pVal);
  if ((_tcsicmp(str,szNO)==0)    ||
      (_tcsicmp(str,szOFF)==0)  ||
      (_tcsicmp(str,szZERO)==0) ||
      (_tcsicmp(str,szFALSE)==0)
    ){
    *pVal = false;
    return true;
  }
  if ((_tcsicmp(str,szYES)==0)  ||
      (_tcsicmp(str,szON)==0)    ||
      (_tcsicmp(str,szONE)==0)  ||
      (_tcsicmp(str,szTRUE)==0)
    ){
    *pVal = true;
    return true;
  }
  return false;
}
 
////////////////////////////////////////////////////////////////////////////
///  This static method returns yes or no given a boolean value.
//
tstring TConfigFile::BoolToString(bool b)
{
  return tstring(b ? szYES : szNO);
}
 
void TConfigFile::LoadValues(const TConfigFile& file)
{
  //
  // Checks if string (up to newline or EOS) is digits.
  //
  const auto isInteger = [](LPCTSTR text) -> bool
  {
    PRECONDITION(text);
 
    const auto isDigit = [](tchar c) -> bool
    {
      return _istdigit(c) || (_T('a') <= c && c <= _T('f')) || (_T('A') <= c && c <= _T('F'));
    };
 
    auto p = SkipWhitespace_(text); // Ignore leading whitespace.
    if (!isDigit(*p))
      return false;
    else
    {
      do ++p; while (isDigit(*p));
      p = SkipWhitespace_(p); // Ignore trailing whitespace.
      return *p == _T('\n') || *p == _T('\0');
    }
  };
 
  const auto nextName = [](LPTSTR p)
  {
    PRECONDITION(p);
    while (*p != _T('\0'))
      ++p;
    return ++p;
  };
 
  tchar  text[MAX_PATH];
  TAPointer<tchar> _ClnObj1,_ClnObj2;
  tchar *sections, *entries;
 
  int sect_size = 4096;
  int size;
  do{
    _ClnObj1 = sections = new tchar[sect_size];
    size = file.ReadSections(sections, sect_size);
    sect_size += 1024;
  }
  while(size >= (sect_size-2));
 
  // while sections not '\0'
  while(*sections){
    sect_size = 4096;
    do{
      _ClnObj2 = entries = new tchar[sect_size];
      size = file.ReadSection(sections, entries, sect_size);
      sect_size += 1024;
    }
    while(size >= (sect_size-2));
 
    //2. Read all values
    while(*entries){
      //?? where difference between int and text entries????
      int size = file.ReadString(sections,entries,text,MAX_PATH,_T(""));
      // if size ==0 or strlen == 0 -> error
      if(!size || !::_tcslen(text) || isInteger(text)){
        size = file.ReadInteger(sections,entries, INT_MAX);
        if(size < INT_MAX)
          WriteInteger(sections,entries, size);
      }
      else
        WriteString(sections,entries, text);
 
      //get next entry name
      entries = nextName(entries);
    }
    //get next section
    sections  = nextName(sections);
  }
}
//
///  This method reads binary data.
//
bool TConfigFile::ReadData(LPCTSTR section, LPCTSTR entry, void* buffer, uint bufSize) const
{
  PRECONDITION(section && entry && buffer && bufSize > 0);
  TAPointer<tchar> __ClnObj(new tchar[bufSize * 3 + 1]);
  LPTSTR text = __ClnObj;
 
  if (!ReadString(section, entry, text, bufSize * 3 + 1, _T("")))
    return false;
 
  if(!::_tcslen(text))
    return false;
 
  uint8* data = (uint8*)buffer;
  for (uint i = 0; i < bufSize; i++) {
    int n; // string in form 'XX '
    if(_stscanf(text, _T("%2x"), &n) == EOF)
      return true;
    *data++ = (uint8)n;
 
    text += 3;
  }
  return true;
}
//
///  This method writes binary data.
//
bool TConfigFile::WriteData(LPCTSTR section, LPCTSTR entry, void* buffer, uint bufSize)
{
  PRECONDITION(section && entry && buffer);
  TAPointer<tchar> __ClnObj(new tchar[bufSize * 3 + 1]);
 
  LPTSTR text = __ClnObj;
  uint8* data = (uint8*)buffer;
 
  for (uint i = 0; i < bufSize; i++) {
    _stprintf(text, _T("%02X "), (int)*data++);
    text += 3;
  }
  return WriteString(section, entry, __ClnObj);
}
//
///  This method reads an integer.
//
int TConfigFile::ReadInteger(LPCTSTR section, LPCTSTR entry, int defval) const
{
  PRECONDITION(section && entry);
 
  tchar text [20];
  LPTSTR end;
 
  if (!ReadString(section, entry, text, COUNTOF(text), _T("")))
    return defval;
 
  int retval = (int)_tcstol(text, &end, 10);
 
  if (*end)
    return retval;
 
  return retval;
}
 
//
///  This method writes an integer.  We format it as a string.
//
bool TConfigFile::WriteInteger(LPCTSTR section, LPCTSTR entry, int value)
{
  PRECONDITION(section && entry);
  tchar  text [20];
 
#if defined __GNUC__
  #if defined UNICODE
  snwprintf(text, 20, _T("%d"), value);
  #else
  snprintf(text, 20, "%d", value);
  #endif
#else
  _ltot (value, text, 10);
#endif
 
 
 
  return WriteString(section, entry, text);
}
 
//
//
//
int TConfigFile::ReadSection(const tstring& section, tstring& buffer) const
{
  TAPointer<tchar> _ClnObj1;
  tchar *sections;
  int sect_size = 4096;
  int size;
 
  do{
    _ClnObj1 = sections = new tchar[sect_size];
    size = ReadSection(section.c_str(),sections, sect_size);
    sect_size += 1024;
  }
  while(size >= (sect_size-2));
  buffer = sections;
  return size;
}
//
//
//
int TConfigFile::ReadSections(tstring& buffer) const
{
  TAPointer<tchar> _ClnObj1;
  tchar *sections;
  int sect_size = 4096;
  int size;
 
  do{
    _ClnObj1 = sections = new tchar[sect_size];
    size = ReadSections(sections, sect_size);
    sect_size += 1024;
  }
  while(size >= (sect_size-2));
  buffer = sections;
  return size;
}
//
/// String-aware overload.
//
uint TConfigFile::ReadString(const tstring& section, const tstring& entry, tstring& buffer, LPCTSTR defstr) const
{
  auto result = tstring(MAX_PATH, _T('\0')); // String length; space for an additional null-terminator is guarantied (since C++11).
  const auto resultSize = static_cast<uint>(result.size() + 1); // Size in character count, including null-terminator.
  const auto p = const_cast<LPTSTR>(result.data()); // Workaround for pre-C++17 compilers.
  const auto n = ReadString(section.c_str(), entry.c_str(), p, resultSize, defstr);
  result.resize(n);
  result.shrink_to_fit();
  swap(buffer, result);
  return n;
}
 
//
//
//
double TConfigFile::ReadDouble(LPCTSTR section, LPCTSTR entry, double defval) const
{
  PRECONDITION(section && entry);
  double retval = defval;
  tchar* end;
  tchar  text[80];
 
  ReadString(section, entry, text, COUNTOF(text), _T(""));
 
  if (::_tcslen(text)){
    retval = _tcstod(text, &end);
 
    if(*end)
      retval = defval;
  }
  return retval;
}
 
//
///  This method writes an integer.
//
bool TConfigFile::WriteDouble(LPCTSTR section, LPCTSTR entry, double val)
{
  PRECONDITION(section && entry);
  tchar text [80];
 
  //
  //  We request a formatted number with a LOT of significant digits:
  //
  if(DefaultPrecision < 0)
    _stprintf(text, _T("%1.20Lg"), (long double)val);
  else{
    if(DefaultPrecision==0)
      _stprintf(text, _T("%Lg"), (long double)val);
    else{
      tstring format = _T("%1.");
#if defined __GNUC__
  #if defined UNICODE
      snwprintf(text, 10, _T("%d"), DefaultPrecision);
  #else
      snprintf(text, 10, "%d", DefaultPrecision);
  #endif
      format += text;
#else
      format += _itot(DefaultPrecision, text, 10);
#endif
      format += _T("Lg");
      _stprintf(text, format.c_str(), (long double)val);
    }
  }
  return WriteString(section, entry, text);
}
//
///  This method reads a boolean value.
//
bool TConfigFile::ReadBool(LPCTSTR section, LPCTSTR entry, bool defval) const
{
  PRECONDITION(section && entry);
  tchar  text[20];
  ReadString(section, entry, text, COUNTOF(text), _T(""));
 
  bool bVal;
 
  if (::_tcslen(text)){
    bool b = StringToBool(text, &bVal);
 
    if (b)
      return bVal;
  }
 
  return defval;
}
//
///  This method writes a bool.
//
bool TConfigFile::WriteBool (LPCTSTR section, LPCTSTR entry, bool val)
{
  PRECONDITION(section && entry);
  return WriteString(section, entry, BoolToString(val).c_str());
}
//
//
//
bool TConfigFile::ReadDate(LPCTSTR section, LPCTSTR entry, TDate& value) const
{
  PRECONDITION(section && entry);
  tstring text;
  ReadString(section, entry, text, _T(""));
  if(text.length() > 0)
  {
    tistringstream strtemp(text);
    value = TDate((const tistream&)strtemp);
    return true;
  }
  return false;
}
//
//
//
bool TConfigFile::WriteDate(LPCTSTR section, LPCTSTR entry, const TDate& val)
{
  PRECONDITION(section && entry);
  tostringstream strtemp;
  TDate::HowToPrint oldOpt = TDate::SetPrintOption(TDate::Numbers);
  strtemp << val.AsString().c_str();
  TDate::SetPrintOption(oldOpt);
  return WriteString(section, entry, strtemp.str());
}
//
//
//
bool TConfigFile::ReadSystemTime(LPCTSTR section, LPCTSTR entry, TSystemTime& val) const
{
  PRECONDITION(section && entry);
  tchar  text[80];
  ReadString(section, entry, text, COUNTOF(text), _T(""));
  if(::_tcslen(text)){
    SYSTEMTIME dt;
    _stscanf(text,_T("%hu,%hu,%hu,%hu,%hu,%hu,%hu,%hu"),&dt.wYear,&dt.wMonth,&dt.wDayOfWeek,
            &dt.wDay,&dt.wHour,&dt.wMinute,&dt.wSecond,&dt.wMilliseconds);
    val = TSystemTime(dt);
    return true;
  }
  return false;
}
//
//
//
bool TConfigFile::WriteSystemTime(LPCTSTR section, LPCTSTR entry, const TSystemTime& dt)
{
  PRECONDITION(section && entry);
  tchar text[80];
  wsprintf(text,_T("%hu,%hu,%hu,%hu,%hu,%hu,%hu,%hu"),dt.wYear,dt.wMonth,dt.wDayOfWeek,
            dt.wDay,dt.wHour,dt.wMinute,dt.wSecond,dt.wMilliseconds);
  return WriteString(section, entry, text);
}
//
//
//
bool TConfigFile::ReadTime(LPCTSTR section, LPCTSTR entry, TTime& val) const
{
  PRECONDITION(section && entry);
  tchar  text[80];
  ReadString(section, entry, text, COUNTOF(text), _T(""));
  if(::_tcslen(text)){
    int h,m,s;
    _stscanf(text,_T("h=%d m=%d s=%d"), &h, &m, &s);
    val = TTime(h,m,s);
    return true;
  }
  return false;
}
//
//
//
bool TConfigFile::WriteTime(LPCTSTR section, LPCTSTR entry, TTime& val)
{
  PRECONDITION(section && entry);
  tchar text[80];
  wsprintf(text,_T("h=%d m=%d s=%d"),val.Hour(),val.Minute(),val.Second());
  return WriteString(section, entry, text);
}
//
//
///  This method will take a profile string of the form:
/// \code
///                   +---------------- lfHeight
///                   |  +------------- lfWidth
///                   |  |             /lfWeight
///                   |  |   +------- / lfItalic
///                   |  |   |        \ lfUnderline
///                   |  |   |         \lfStrikeOut
///                   |  |   |   +----- lfEscapement
///                   |  |   |   |  +-- lfOrientation
///                   |  |   |   |  |
///                   v  v   v   v  v
///                  10, 8, BIU, 0, 0, 0, 0, 0, 0, 0, Times New Roman
///                                    ^  ^  ^  ^  ^  ^
///                                    |  |  |  |  |  |
///                         lfCharSet -+  |  |  |  |  |
///                    lfOutPrecision ----+  |  |  |  |
///                   lfClipPrecision -------+  |  |  |
///                         lfQuality ----------+  |  |
///                  lfPitchAndFamily -------------+  |
///                        lfFaceName ----------------+
/// \endcode
///  and fills in a logfont structure.
//
bool TConfigFile::ReadFont(LPCTSTR section, LPCTSTR entry, LOGFONT& pFont) const
{
  PRECONDITION(section && entry);
  tchar  text[MAX_PATH];
  ReadString(section, entry, text, MAX_PATH, _T(""));
 
  if(!::_tcslen(text))
    return false;
 
  LPCTSTR szSep = _T(", \t");
  bool    b;
  tchar* sz;
  LOGFONT lf;
  int     i, n;
 
  _tcscat(text,_T("\n"));
  sz = _tcstok(text, szSep);
  n = 0;
  while (sz){
    i = _ttoi(sz);   // not needed all the time, but oh wel...
 
    switch (n++){
      // int   lfHeight;
      case 0:
        lf.lfHeight = i;
        break;
 
      // int   lfWidth;
      case 1:
        lf.lfWidth = i;
        break;
 
      // int   lfWeight;
      // BYTE  lfItalic;
      // BYTE  lfUnderline;
      // BYTE  lfStrikeOut;
      case 2:
        lf.lfWeight      = FW_NORMAL;
        lf.lfItalic      = FALSE;
        lf.lfUnderline  = FALSE;
        lf.lfStrikeOut  = FALSE;
 
        if (_tcschr(sz, _T('B')))
          lf.lfWeight = FW_BOLD;
        if (_tcschr(sz, _T('I')))
          lf.lfItalic = TRUE;
        if (_tcschr(sz, _T('U')))
          lf.lfUnderline = TRUE;
        if (_tcschr(sz, _T('S')))
          lf.lfStrikeOut = TRUE;
        break;
 
      // int   lfEscapement;
      case 3:
        lf.lfEscapement = i;
        break;
 
      // int   lfOrientation;
      case 4:
        lf.lfOrientation = i;
        break;
 
      // BYTE  lfCharSet;
      case 5:
        lf.lfCharSet = uint8(i);
        break;
 
      // BYTE  lfOutPrecision;
      case 6:
        lf.lfOutPrecision = uint8(i);
        break;
 
      // BYTE  lfClipPrecision;
      case 7:
        lf.lfClipPrecision = uint8(i);
        break;
 
      // BYTE  lfQuality;
      case 8:
        lf.lfQuality = uint8(i);
        break;
 
      // BYTE  lfPitchAndFamily;
      case 9:
        lf.lfPitchAndFamily = uint8(i);
        break;
 
      // BYTE  lfFaceName[LF_FACESIZE];
      case 10:
        ::_tcscpy(lf.lfFaceName, sz);
        break;
    }
 
    if (n==10)
      sz = _tcstok(0, _T("\n"));
    else
      sz = _tcstok(0, szSep);
  }
  b = (n == 11);
 
  if(b)
    pFont = lf;
 
  return b;
}
 
//
//
///  This method writes a font description in the following format:
/// \code
///                   +---------------- lfHeight
///                   |  +------------- lfWidth
///                   |  |             /lfWeight
///                   |  |   +------- / lfItalic
///                   |  |   |        \ lfUnderline
///                   |  |   |         \lfStrikeOut
///                   |  |   |   +----- lfEscapement
///                   |  |   |   |  +-- lfOrientation
///                   |  |   |   |  |
///                   v  v   v   v  v
///                  10, 8, BIU, 0, 0, 0, 0, 0, 0, 0, Times New Roman
///                                    ^  ^  ^  ^  ^  ^
///                                    |  |  |  |  |  |
///                         lfCharSet -+  |  |  |  |  |
///                    lfOutPrecision ----+  |  |  |  |
///                   lfClipPrecision -------+  |  |  |
///                         lfQuality ----------+  |  |
///                  lfPitchAndFamily -------------+  |
///                        lfFaceName ----------------+
/// \endcode
bool TConfigFile::WriteFont (LPCTSTR section, LPCTSTR entry, const LOGFONT& logFont)
{
  PRECONDITION(section && entry);
  tstring  s;
  tchar      ach [40];
  int          i;
  tchar      comma[] = _T(",");
 
#if defined __GNUC__
  _tprintf(ach, _T("%d"), logFont.lfHeight);
  s = ach;
#else
  s = _itot(logFont.lfHeight, ach, 10);
#endif
  s += comma;
#if defined __GNUC__
  _tprintf(ach, _T("%d"), logFont.lfWidth);
  s += ach;
#else
  s += _itot(logFont.lfWidth, ach, 10);
#endif
  s += comma;
 
  i = 0;
  ach[i++] = (logFont.lfWeight < FW_BOLD) ? _T('N') : _T('B');
  if (logFont.lfItalic)
    ach[i++] = _T('I');
  if (logFont.lfUnderline)
    ach[i++] = _T('U');
  if (logFont.lfStrikeOut)
    ach[i++] = _T('S');
  ach[i] = 0;
  s += ach;
  s += comma;
 
#if defined __GNUC__
  _tprintf(ach, _T("%d"), logFont.lfEscapement);
  s += ach;
#else
  s += _itot(logFont.lfEscapement, ach, 10);
#endif
  s += comma;
#if defined __GNUC__
  _tprintf(ach, _T("%d"), logFont.lfOrientation);
  s += ach;
#else
  s += _itot(logFont.lfOrientation, ach, 10);
#endif
  s += comma;
#if defined __GNUC__
  _tprintf(ach, _T("%d"), logFont.lfCharSet);
  s += ach;
#else
  s += _itot(logFont.lfCharSet, ach, 10);
#endif
  s += comma;
#if defined __GNUC__
  _tprintf(ach, _T("%d"), logFont.lfOutPrecision);
  s += ach;
#else
  s += _itot(logFont.lfOutPrecision, ach, 10);
#endif
  s += comma;
#if defined __GNUC__
  _tprintf(ach, _T("%d"), logFont.lfClipPrecision);
  s += ach;
#else
  s += _itot(logFont.lfClipPrecision, ach, 10);
#endif
  s += comma;
#if defined __GNUC__
  _tprintf(ach, _T("%d"), logFont.lfQuality);
  s += ach;
#else
  s += _itot(logFont.lfQuality, ach, 10);
#endif
  s += comma;
#if defined __GNUC__
  _tprintf(ach, _T("%d"), logFont.lfPitchAndFamily);
  s += ach;
#else
  s += _itot(logFont.lfPitchAndFamily, ach, 10);
#endif
  s += comma;
  s += logFont.lfFaceName;
 
  return WriteString(section, entry, s.c_str());
}
//
///  This method reads an RGB color.
//
bool TConfigFile::ReadColor(LPCTSTR section, LPCTSTR entry, TColor& color) const
{
  PRECONDITION(section && entry);
 
  const auto skipPrefix = [](LPCTSTR sz, tchar ch) -> LPCTSTR
  {
    PRECONDITION(sz);
    sz = SkipWhitespace_(sz);
    if (_totupper(*sz) == ch)
    {
      ++sz;
      sz = SkipWhitespace_(sz);
      if (*sz == _T('='))
        sz = SkipWhitespace_(sz + 1);
    }
    return sz;
  };
 
  tchar  text[40];
  ReadString(section, entry, text, COUNTOF(text), _T(""));
 
  if(!::_tcslen(text))
    return false;
 
  TColor  clr;
  tchar*  sz[3];
  bool    b;
 
  b = false;
 
  sz[0] = text;
  sz[1] = _tcschr(sz[0], _T(','));
  if(sz[1]){
    *(sz[1]++) = 0;
    sz[2] = _tcschr(sz[1], _T(','));
    if (sz[2]){
      *(sz[2]++) = 0;
 
      int R = _ttoi(skipPrefix(sz[0], _T('R')));
      int G = _ttoi(skipPrefix(sz[1], _T('G')));
      int B = _ttoi(skipPrefix(sz[2], _T('B')));
 
      if (R >= 0 && R < 256 &&
          G >= 0 && G < 256 &&
          B >= 0 && B < 256){
        clr = RGB(R,G,B);
        b = true;
      }
    }
  }
  if(b)
    color = clr;
  return b;
}
//
///  This method writes an RGB color
//
bool TConfigFile::WriteColor(LPCTSTR section, LPCTSTR entry, const TColor& clr)
{
  PRECONDITION(section && entry);
  tchar ach[40];
 
  wsprintf (ach, _T("R=%d,G=%d,B=%d"), clr.Red(), clr.Green(), clr.Blue());
 
  return WriteString(section, entry, ach);
}
//
//  This method reads an X,Y point.
//
bool TConfigFile::ReadPoint (LPCTSTR section, LPCTSTR entry, TPoint& point) const
{
  PRECONDITION(section && entry);
  tchar  text[40];
  ReadString(section, entry, text, COUNTOF(text), _T(""));
 
  if(!::_tcslen(text))
    return false;
 
  LPCTSTR  sz;
  bool    b = false;
  int     array[2], i;
 
  sz = text;
 
  for ( i = 0; i < 2; i++){
    sz = _tcschr (sz, _T('='));
    if(! sz)
      break;
    sz++;
    if(!_istdigit(*sz))
      break;
 
    array[i] = _ttoi(sz);
  }
 
  if (i == 2){
    b = true;
    point.x  = array[0];
    point.y = array[1];
  }
  return b;
}
//
///  This method writes a TPoint.
//
bool TConfigFile::WritePoint (LPCTSTR section, LPCTSTR entry, const TPoint& point)
{
  PRECONDITION(section && entry);
  tchar buf[20];
 
  wsprintf(buf, _T("X=%d,Y=%d"), point.x, point.y);
 
  return WriteString(section, entry, buf);
}
//
///  This method reads a rectangle.
//
bool TConfigFile::ReadRect (LPCTSTR section, LPCTSTR entry, TRect& rect) const
{
  PRECONDITION(section && entry);
  tchar  text[40];
  ReadString(section, entry, text, COUNTOF(text), _T(""));
 
  if(!::_tcslen(text))
    return false;
 
  LPCTSTR  sz;
  bool    b = false;
  int     nArray[4], i;
 
  sz = text;
 
  for(i = 0; i < 4; i++){
    sz = _tcschr(sz, _T('='));
    if (!sz)
      break;
 
    sz++;
    if(!_istdigit(*sz))
      break;
 
    nArray[i] = _ttoi(sz);
  }
 
  if(i == 4){
    b = true;
    rect.left    = nArray[0];
    rect.top     = nArray[1];
    rect.right   = nArray[0] + nArray[2];
    rect.bottom  = nArray[1] + nArray[3];
  }
  return b;
}
//
///  This method writes a rectangle.
//
bool TConfigFile::WriteRect (LPCTSTR section, LPCTSTR entry, const TRect& r)
{
  PRECONDITION(section && entry);
  tchar buf [40];
 
  wsprintf (buf, _T("L=%d,T=%d,W=%d,H=%d"), r.left, r.top, r.Width(), r.Height());
 
  return WriteString(section, entry, buf);
}
//
///  This method writes a TSize.
//
bool TConfigFile::WriteSize (LPCTSTR section, LPCTSTR entry, const TSize& size)
{
  PRECONDITION(section && entry);
  tchar buf [80];
 
  wsprintf(buf, _T("W=%d,H=%d"), size.cx, size.cy);
 
  return WriteString(section, entry, buf);
}
//
bool TConfigFile::SectionExists(LPCTSTR section) const
{
  PRECONDITION(section);
  tchar buff[3];
  return ReadSection(section, buff, 3) != 0;
}
///////////////////////////////////////////////////////////////////////////////////////
//
// class TIniConfigFile
// ~~~~~ ~~~~~~~~~~~~~~
//
uint TIniConfigFile::ReadSection(LPCTSTR section, LPTSTR buffer, uint bufSize) const
{
  PRECONDITION(section && buffer && bufSize > 1);
  return GetPrivateProfileString(section, 0, _T(""), buffer, bufSize, FileName.c_str());
}
//
uint TIniConfigFile::ReadSections(LPTSTR buffer, uint bufSize) const
{
  PRECONDITION(buffer && bufSize > 1);
  return GetPrivateProfileString(0, 0, _T(""), buffer, bufSize,FileName.c_str());
}
//
///  This method deletes the specified section.
//
bool TIniConfigFile::EraseSection (LPCTSTR section)
{
  PRECONDITION(section);
  return ::WritePrivateProfileString(section, 0, 0, FileName.c_str()) != 0;
}
//
//
//
///  This method deletes the specified entry.
//
bool TIniConfigFile::EraseEntry(LPCTSTR section, LPCTSTR entry)
{
  PRECONDITION(section && entry);
  return ::WritePrivateProfileString (section, entry, 0, FileName.c_str()) != 0;
}
//
//
//
void TIniConfigFile::UpdateFile()
{
  ::WritePrivateProfileString(0, 0, 0, FileName.c_str());
}
//
/// This method is the base method for reading strings.
///
/// \return The number of characters written to the output buffer (excluding the null-terminator, which is also written).
///
/// \note If the buffer is too small to hold the whole string, the string is truncated to (bufSize - 1) characters and
/// null-terminated.
//
uint TIniConfigFile::ReadString (LPCTSTR section, LPCTSTR entry,
                                LPTSTR buffer, uint bufSize, LPCTSTR def) const
{
  PRECONDITION(section && entry && buffer && bufSize > 0);
  return ::GetPrivateProfileString (section, entry, def?def:_T(""), buffer, bufSize, FileName.c_str());
}
//
///  This method is the low-level method for writing strings.
//
bool TIniConfigFile::WriteString (LPCTSTR section, LPCTSTR entry, LPCTSTR value)
{
  PRECONDITION(section && entry);
  return ::WritePrivateProfileString (section, entry, value, FileName.c_str()) != 0;
}
//
///  This method reads binary data.
//
bool TIniConfigFile::ReadData(LPCTSTR section, LPCTSTR entry, void* buffer, uint bufSize) const
{
  PRECONDITION(section && entry && buffer && bufSize > 0);
  return ::GetPrivateProfileStruct(section, entry, buffer, bufSize, FileName.c_str()) != 0;
}
//
///  This method writes binary data.
//
bool TIniConfigFile::WriteData(LPCTSTR section, LPCTSTR entry, void* buffer, uint bufSize)
{
  PRECONDITION(section && entry && buffer);
  return ::WritePrivateProfileStruct(section, entry, buffer, bufSize, FileName.c_str()) != 0;
}
//////////////////////////////////////////////////////////////////////////////////////////////////
//
/// Container support
//
class TStringMapNode: public TMapNode<tstring,tstring> {
public:
    TStringMapNode(const tstring& str, const tstring& val)
      : TMapNode<tstring,tstring>() {Name = str; Value = val;}
 
    TStringMapNode(const tstring& str)
      : TMapNode<tstring,tstring>() {Name = str;}
};
//
typedef TSortedObjectArray<TStringMapNode>  TStringMap;
//
class TStringNode: public TMapNode<tstring,TStringMap> {
  public:
    TStringNode(const tstring& str)
      : TMapNode<tstring,TStringMap>(){Name = str;}
};
//
class TMemConfigFileSections: public TIPtrArray<TStringNode*>  {
  public:
    TMemConfigFileSections(){}
    ~TMemConfigFileSections(){}
 
    bool  DestroyItem(TStringNode* item);
    bool  Destroy(int loc);
    int   Add(TStringNode* item);
    bool  HasMember(TStringNode* item) const;
    int   Find(TStringNode* item) const;
};
//
bool TMemConfigFileSections::HasMember(TStringNode* item) const
{
  return Find(item) != NPOS;
}
//
bool TMemConfigFileSections::DestroyItem(TStringNode* item)
{
  const auto index = Find(item);
  if (index != NPOS)
  {
    TStringNode* item = Data[index];
    Remove(index);
    delete item;// using global delete because we didn't allocate it
    return true;
  }
  return false;
}
//
bool TMemConfigFileSections::Destroy(int loc)
{
  if(loc < (int)Size()){
    TStringNode* item = Data[loc];
    Remove(loc);
    delete item;// using global delete because we didn't allocate it
    return true;
  }
  return false;
}
//
int TMemConfigFileSections::Add(TStringNode* item)
{
   if(ItemCnt>=Reserved)
     Resize(ItemCnt+1); // on error -> throw xalloc
  unsigned loc = ItemCnt++;
  while( loc > 0 && *item < *Data[loc-1]) {
    Data[loc] = Data[loc-1];
    loc--;
   }
  Data[loc] = item;
  return loc;
}
//
int TMemConfigFileSections::Find(TStringNode* item) const
{
  auto lower = 0;
  auto upper = static_cast<int>(Size()) - 1;
  while (lower <= upper)
  {
    const auto middle = (lower + upper) / 2;
    if (*Data[middle] == *item)
      return middle;
    if (*Data[middle] < *item)
      lower = middle + 1;
    else
      upper = middle - 1;
  }
  return NPOS;
}
///////////////////////////////////////////////////////////////////////
//
// class TMemConfigFile
// ~~~~~ ~~~~~~~~~~~~~~
TMemConfigFile::TMemConfigFile(const tstring& fileName)
:
  TConfigFile(fileName)
{
  Sections = new TMemConfigFileSections();
}
//
TMemConfigFile::~TMemConfigFile()
{
  Flush();
  delete Sections;
}
//
void TMemConfigFile::Flush()
{
  Sections->Flush();
}
//
void TMemConfigFile::LoadValues(const TConfigFile& file)
{
  Flush();
  TConfigFile::LoadValues(file);
}
//
int TMemConfigFile::AddSection(const tstring& section)
{
  return Sections->Add(new TStringNode(section));
}
//
bool TMemConfigFile::EraseEntry(LPCTSTR section, LPCTSTR entry)
{
  PRECONDITION(section && entry);
  TStringNode sn(section);
  const auto index = Sections->Find(&sn);
  if (index != Sections->NPOS)
  {
    return (*Sections)[index]->Value.DestroyItem(TStringMapNode(entry));
  }
  return false;
}
//
bool TMemConfigFile::EraseSection(LPCTSTR section)
{
  PRECONDITION(section);
  TStringNode sn(section);
  return Sections->DestroyItem(&sn);
}
 
//
/// Copies all value names of the given section to the specified buffer.
/// Each string is null-terminated, and the buffer is then terminated with a trailing null.
/// Returns the number of characters written, excluding the trailing null-terminators.
/// If the buffer is too small, the last string copied to the buffer may be truncated.
//
uint TMemConfigFile::ReadSection(LPCTSTR section, LPTSTR buffer, uint bufSize) const
{
  PRECONDITION(section && buffer && bufSize > 1);
  tchar* p = buffer;
  *p = _T('\0'); // Ensure the buffer is terminated, if we exit early.
  TStringNode sn(section);
  const auto k = Sections->Find(&sn);
  if (k == Sections->NPOS) return 0;
  const TStringMap& map = (*Sections)[k]->Value;
  if (map.Empty()) return 0;
  for (uint i = 0; i != map.Size(); ++i)
  {
    const tstring& s = map[i].Name;
    size_t m = static_cast<size_t>(buffer + bufSize - 1 - p); // space left
    if (m == 0) break; // We've run out of space.
    size_t n = min(s.size(), m - 1);
    ::_tcsncpy(p, s.c_str(), n);
    p += n;
    *p++ = _T('\0');
  }
 
  // Add buffer terminator.
  //
  *p++ = _T('\0');
  return static_cast<uint>(p - buffer) - 2; // Exclude trailing nulls in the count.
}
 
//
/// Copies all section names to the specified buffer.
/// Each string is null-terminated, and the buffer is then terminated with a trailing null.
/// Returns the number of characters written, excluding the trailing null-terminators.
/// If the buffer is too small, the last string copied to the buffer may be truncated.
//
uint TMemConfigFile::ReadSections(LPTSTR buffer, uint bufSize) const
{
  PRECONDITION(buffer && bufSize > 1);
  tchar* p = buffer;
  *p = _T('\0'); // Ensure the buffer is terminated, if we exit early.
  if (Sections->Empty()) return 0;
  for (uint i = 0; i != Sections->Size(); ++i)
  {
    const tstring& s = (*Sections)[i]->Name;
    size_t m = static_cast<size_t>(buffer + bufSize - 1 - p); // space left
    if (m == 0) break; // We've run out of space.
    size_t n = min(s.size(), m - 1);
    ::_tcsncpy(p, s.c_str(), n);
    p += n;
    *p++ = _T('\0');
  }
 
  // Add buffer terminator.
  //
  *p++ = _T('\0');
  return static_cast<uint>(p - buffer) - 2; // Exclude trailing nulls in the count.
}
 
//
/// Looks up the value of the given entry of the given section, and if found, copies it to the given buffer.
/// If not found, the given default value `defstr` is copied to the buffer, unless `defstr` is null, in which
/// case an empty string is copied to the buffer.
///
/// \return The number of characters written to the output buffer (excluding the null-terminator, which is also written).
///
/// \note If the buffer is too small to hold the whole string, the string is truncated to (bufSize - 1) characters and
/// null-terminated.
//
uint TMemConfigFile::ReadString(LPCTSTR section, LPCTSTR entry, LPTSTR buffer, uint bufSize, LPCTSTR defstr) const
{
  PRECONDITION(section && entry && buffer && bufSize > 0);
 
  const auto lookup = [](const TMemConfigFile& f, LPCTSTR section, LPCTSTR entry, LPCTSTR def) -> LPCTSTR
  {
    TStringNode sn(section);
    const auto i = f.Sections->Find(&sn);
    if (i == f.Sections->NPOS) return def;
    const TStringMap& map = (*f.Sections)[i]->Value;
    const auto j = map.Find(TStringMapNode{entry});
    if (j == map.NPOS) return def;
    return map[j].Value.c_str();
  };
 
  LPCTSTR s = lookup(*this, section, entry, defstr ? defstr : _T(""));
  size_t n = min(::_tcslen(s), static_cast<size_t>(bufSize - 1));
  ::_tcsncpy(buffer, s, n);
  buffer[n] = _T('\0');
  return static_cast<uint>(n); // Exclude null-terminator in the count.
}
 
//
bool TMemConfigFile::WriteString(LPCTSTR section, LPCTSTR entry, LPCTSTR value)
{
  PRECONDITION(section && entry && value);
  TStringNode sn(section);
  auto index = Sections->Find(&sn);
  if (index == Sections->NPOS)
    index = AddSection(section);
  TStringMap& map = (*Sections)[index]->Value;
  index = map.Find(TStringMapNode(entry));
  if (index != map.NPOS)
    map[index].Value = tstring(value);
  else
    index =  map.Add(TStringMapNode(entry,value));
  return index != map.NPOS;
}
//
void TMemConfigFile::UpdateFile()
{
  TTextFile file(FileName.c_str(),TTextFile::WriteOnly|TTextFile::CreateAlways);
  if(!file.IsOpen())
    return;
 
  TAPointer<tchar> buf(new tchar[MAX_PATH]);
  for(uint section = 0; section < Sections->Size(); section++){
    // Section name
    file << _T("[") << (*Sections)[section]->Name.c_str() << _T("]") << _T("\r\n");
 
    TStringMap& map = (*Sections)[section]->Value;
    for(uint value = 0; value < map.Size(); value++)
      file << map[value].Name.c_str() << _T("=") << map[value].Value.c_str() << _T("\r\n");
  }
}
 
void TMemConfigFile::LoadFromFile()
{
  const auto getName = [](tstring& name, LPCTSTR p)
  {
    PRECONDITION(p);
    while (!_istalpha(*p))
      ++p;
    const auto b = p++;
    while (_istalpha(*p))
      ++p;
    name.assign(b, p);
    return p;
  };
 
  const auto getValue = [](tstring& name, LPCTSTR p)
  {
    PRECONDITION(p);
    while (*p && (*p == _T(' ') || *p == _T('\t')) && *p != _T('='))
      ++p;
    name = ++p;
  };
 
  TTextFile file(FileName.c_str());
  if(!file.IsOpen())
    return;
 
  Sections->Flush();
  TFileLineIterator fitr(file);
  int sec_index = -1;
  tstring tmp_name1,tmp_name2;
 
  while(fitr){
    auto p = SkipWhitespace_(fitr);
    if(::_tcslen(p) && *p != _T(';')){
      // read section
      if(*p == _T('[')){
        getName(tmp_name1,p);
        sec_index = AddSection(tmp_name1.c_str());
      }
      // else read value
      else if(sec_index >= 0){
        p = getName(tmp_name1,p);
        getValue(tmp_name2,p);
        (*Sections)[sec_index]->Value.Add(TStringMapNode(tmp_name1,tmp_name2));
      }
    }
    ++fitr;
  }
}
//
 
//
/// \class TRegConfigFile
// ~~~~~ ~~~~~~~~~~~~~~
///  This source file implements TRegConfigFile.  This class is derived from
///  TConfigFile and overrides the low-level storage methods to vector them
///  to the registry.
//
///  TRegConfigFile is not intended to handle all of an app's registry needs,
///  but rather to be used for a convenient wrapper for app preference
///  settings.  All data is stored in HKEY_CURRENT_USER, using FileName like
///  so:
/// \code
///      HKEY_CURRENT_USER\Software\"FileName"\...
/// \endcode
///  FileName should contain the company name and product name to adhere to
///  standard registry practices.  Eg:
/// \code
///      FileName = "My Company\\Product"
///      HKEY_CURRENT_USER\Software\My Company\Product\...
/// \endcode
///  A version number can also be used to allow multiple versions of the
///  same product to live together:
/// \code
///      mFile = "My Company\\Product\\V1.0"
///      HKEY_CURRENT_USER\Software\My Company\Product\V1.0\...
/// \endcode
/// \note FileName must not end with a backslash!
///
///  All sections are created under the subtree as described above.  Eg,
/// \code
///      Section = "Options"
///      HKEY_CURRENT_USER\Software\My Company\Product\V1.0\Options
/// \endcode
///  Entries are stored under sections as REG_SZ values (for numbers it is REG_DWORD).  Eg,
/// \code
///      entry = "Tile"
///      HKEY_CURRENT_USER\Software\My Company\Product\V1.0\Options\Tile
/// \endcode
//
TRegConfigFile::TRegConfigFile(const tstring& name, HKEY root)
  : TConfigFile{_T("Software\\") + name}, Root{root}
{}
 
//
TRegConfigFile::~TRegConfigFile()
{
}
//
bool TRegConfigFile::SectionExists(LPCTSTR section) const
{
  PRECONDITION(section);
  return Root.HasSubkey(FileName + szSep + section);
}
 
//
/// Copies all value names of the given section to the specified buffer.
/// Each string is null-terminated, and the buffer is then terminated with a trailing null.
/// Returns the number of characters written, excluding the trailing null-terminators.
/// If the buffer is too small, the last string copied to the buffer may be truncated.
//
uint TRegConfigFile::ReadSection(LPCTSTR section, LPTSTR buffer, uint bufSize) const
{
  PRECONDITION(section && buffer && bufSize > 1);
  tchar* p = buffer;
  *p = _T('\0'); // Ensure the buffer is terminated, if we exit early.
  TRegKey root(Root, FileName + szSep + section);
  TRegValueIterator i(root);
  if (!i) return 0;
  for (; i; ++i)
  {
    TRegValue v(i);
    LPCTSTR s = v.GetName();
    size_t m = static_cast<size_t>(buffer + bufSize - 1 - p); // space left
    if (m == 0) break; // We've run out of space.
    size_t n = min(::_tcslen(s), m - 1);
    ::_tcsncpy(p, s, n);
    p += n;
    *p++ = _T('\0');
  }
 
  // Add buffer terminator.
  //
  *p++ = _T('\0');
  return static_cast<uint>(p - buffer) - 2; // Exclude trailing nulls in the count.
}
 
//
/// Copies all section names to the specified buffer.
/// Each string is null-terminated, and the buffer is then terminated with a trailing null.
/// Returns the number of characters written, excluding the trailing null-terminators.
/// If the buffer is too small, the last string copied to the buffer may be truncated.
//
uint TRegConfigFile::ReadSections(LPTSTR buffer, uint bufSize) const
{
  PRECONDITION(buffer && bufSize > 1);
  tchar* p = buffer;
  *p = _T('\0'); // Ensure the buffer is terminated, if we exit early.
  TRegKey root(Root, FileName);
  TRegKeyIterator i(root);
  if (!i) return 0;
  for (; i; ++i)
  {
    TRegKey k(i);
    LPCTSTR s = k.GetName();
    size_t m = static_cast<size_t>(buffer + bufSize - 1 - p); // space left
    if (m == 0) break; // We've run out of space.
    size_t n = min(::_tcslen(s), m - 1);
    ::_tcsncpy(p, s, n);
    p += n;
    *p++ = _T('\0');
  }
 
  // Add buffer terminator.
  //
  *p++ = _T('\0');
  return static_cast<uint>(p - buffer) - 2; // Exclude trailing nulls in the count.
}
 
//
bool TRegConfigFile::EraseSection(LPCTSTR section)
{
  PRECONDITION(section);
  return TRegKey{Root, FileName}.NukeKey(section) == ERROR_SUCCESS;
}
//
bool TRegConfigFile::EraseEntry(LPCTSTR section, LPCTSTR entry)
{
  PRECONDITION(section && entry);
  return TRegKey{Root, FileName + szSep + section}.DeleteValue(entry) == ERROR_SUCCESS;
}
//
void TRegConfigFile::UpdateFile()
{
  // don't need this currently
}
//
int TRegConfigFile::ReadInteger(LPCTSTR section, LPCTSTR entry, int defint) const
{
  PRECONDITION(section && entry);
  const auto k = Root.GetSubkey(FileName + szSep + section);
  return k ? static_cast<int>(k->GetValueOrDefault<uint32>(entry, defint)) : defint;
}
//
bool TRegConfigFile::WriteInteger(LPCTSTR section, LPCTSTR entry, int value)
{
  PRECONDITION(section && entry);
  const auto k = TRegKey{Root, FileName + szSep + section};
  return k.SetValue(entry, static_cast<uint32>(value)) == ERROR_SUCCESS;
}
 
//
/// Looks up the value of the given entry of the given section, and if found, copies it to the given buffer.
/// If not found, the given default value `defstr` is copied to the buffer, unless `defstr` is null, in which case an empty
/// string is copied to the buffer.
///
/// \return The number of characters written to the output buffer (excluding the null-terminator, which is also written).
/// If an error occurs, e.g. registry access is denied, a TXRegistry exception is thrown.
///
/// \note If the buffer is too small to hold the whole string, the string is truncated to (bufSize - 1) characters and
/// null-terminated.
//
uint TRegConfigFile::ReadString(LPCTSTR section, LPCTSTR entry, LPTSTR buffer, uint bufSize, LPCTSTR defstr) const
{
  PRECONDITION(section && entry && buffer && bufSize > 0);
  const auto k = Root.GetSubkey(FileName + szSep + section);
  const auto d = defstr ? defstr : tstring{};
  const auto s = k ? k->GetValueOrDefault(entry, d) : d;
  const auto n = min(static_cast<uint>(s.size()), bufSize - 1);
  const auto e = copy_n(s.begin(), n, buffer);
  *e = _T('\0');
  return n;
}
 
//
bool TRegConfigFile::WriteString(LPCTSTR section, LPCTSTR entry, LPCTSTR value)
{
  PRECONDITION(section && entry && value);
  const auto k = TRegKey{Root, FileName + szSep + section};
  return k.SetValue(entry, value) == ERROR_SUCCESS;
}
 
//
///  This method reads binary data.
//
bool TRegConfigFile::ReadData(LPCTSTR section, LPCTSTR entry, void* buffer, uint bufSize) const
{
  PRECONDITION(section && entry && buffer && bufSize > 0);
  const auto k = Root.GetSubkey(FileName + szSep + section);
  if (!k) return false;
  auto size = uint32{bufSize};
  auto type = uint32{};
  return k->QueryValue(entry, &type, reinterpret_cast<uint8*>(buffer), &size) == ERROR_SUCCESS;
}
//
///  This method writes binary data.
//
bool TRegConfigFile::WriteData(LPCTSTR section, LPCTSTR entry, void* buffer, uint bufSize)
{
  PRECONDITION(section && entry && buffer);
  const auto k = TRegKey{Root, FileName + szSep + section};
  return k.SetValue(entry, REG_BINARY, reinterpret_cast<const uint8*>(buffer), bufSize) == ERROR_SUCCESS;
}
//
} // OWL namespace
//============================================================================================

V1004 The 'sz' pointer was used unsafely after it was verified against nullptr. Check lines: 48, 49.

V1004 The 'text' pointer was used unsafely after it was verified against nullptr. Check lines: 97, 104.

V1004 The 'p' pointer was used unsafely after it was verified against nullptr. Check lines: 117, 118.

V1004 The 'entry' pointer was used unsafely after it was verified against nullptr. Check lines: 394, 396.

V1004 The 'section' pointer was used unsafely after it was verified against nullptr. Check lines: 394, 396.

V1004 The 'entry' pointer was used unsafely after it was verified against nullptr. Check lines: 410, 415.

V1004 The 'section' pointer was used unsafely after it was verified against nullptr. Check lines: 410, 415.

V1004 The 'sz' pointer was used unsafely after it was verified against nullptr. Check lines: 724, 725.

V1004 The 'buffer' pointer was used unsafely after it was verified against nullptr. Check lines: 1198, 1214.

V1004 The 'entry' pointer was used unsafely after it was verified against nullptr. Check lines: 1221, 1227.

V1004 The 'p' pointer was used unsafely after it was verified against nullptr. Check lines: 1256, 1257.

V1004 The 'p' pointer was used unsafely after it was verified against nullptr. Check lines: 1268, 1269.

V1004 The 'section' pointer was used unsafely after it was verified against nullptr. Check lines: 1353, 1354.

V1004 The 'section' pointer was used unsafely after it was verified against nullptr. Check lines: 1365, 1368.

V1004 The 'section' pointer was used unsafely after it was verified against nullptr. Check lines: 1430, 1431.

V1004 The 'section' pointer was used unsafely after it was verified against nullptr. Check lines: 1441, 1442.

V1004 The 'section' pointer was used unsafely after it was verified against nullptr. Check lines: 1448, 1449.

V1004 The 'section' pointer was used unsafely after it was verified against nullptr. Check lines: 1466, 1467.

V1004 The 'section' pointer was used unsafely after it was verified against nullptr. Check lines: 1479, 1480.

V1004 The 'value' pointer was used unsafely after it was verified against nullptr. Check lines: 1479, 1481.

V1004 The 'section' pointer was used unsafely after it was verified against nullptr. Check lines: 1489, 1490.

V1004 The 'section' pointer was used unsafely after it was verified against nullptr. Check lines: 1501, 1502.

V576 Incorrect format. Consider checking the third actual argument of the 'sscanf' function. A pointer to the unsigned int type is expected.

V1004 The 'pVal' pointer was used unsafely after it was verified against nullptr. Check lines: 62, 68.

V1004 The 'entry' pointer was used unsafely after it was verified against nullptr. Check lines: 1107, 1112.

V1004 The 'buffer' pointer was used unsafely after it was verified against nullptr. Check lines: 1132, 1143.

V1004 The 'buffer' pointer was used unsafely after it was verified against nullptr. Check lines: 1165, 1172.

V1004 The 'section' pointer was used unsafely after it was verified against nullptr. Check lines: 1221, 1225.

V1004 The 'value' pointer was used unsafely after it was verified against nullptr. Check lines: 1221, 1231.

V1004 The 'buffer' pointer was used unsafely after it was verified against nullptr. Check lines: 1365, 1375.

V1004 The 'buffer' pointer was used unsafely after it was verified against nullptr. Check lines: 1397, 1407.

V576 Incorrect format. Consider checking the fifth actual argument of the 'wsprintfA' function. The SIGNED integer type argument is expected.

V576 Incorrect format. Consider checking the fourth actual argument of the 'wsprintfA' function. The SIGNED integer type argument is expected.

V576 Incorrect format. Consider checking the third actual argument of the 'wsprintfA' function. The SIGNED integer type argument is expected.

V811 Decreased performance. Excessive type casting: string -> char * -> string. Consider inspecting first argument of the function AddSection.

V832 It's better to use '= default;' syntax instead of empty constructor body.

V832 It's better to use '= default;' syntax instead of empty destructor body.