Files
Sun1602/Client/MemoryINI/AKMemoryIni/MemoryIni.cpp
T
2022-10-26 12:25:11 +08:00

1086 lines
20 KiB
C++

/*-----------------------------------------------------------------------------
* MemoryIni.cpp
*-----------------------------------------------------------------------------
* 암호화된 Ini파일을 메모리에서 읽고 쓸수 있게 하기 위하여 만든 클래스
*-----------------------------------------------------------------------------
**---------------------------------------------------------------------------*/
#include "MemoryIni.h"
//
MemoryIni::MemoryIni()
: m_bInit(FALSE),
m_bParsed(FALSE),
m_data(NULL),
m_data_size(0)
{
m_sec.clear();
}
MemoryIni::~MemoryIni()
{
if ( NULL != m_data )
{
free(m_data);
m_data = NULL;
}
m_sec.clear();
m_data_size = 0;
}
/** -----------------------------------------------------------------------
\brief ppos버퍼에서 linsz보다 작은 크기로 한줄 읽어서 line에 채움
\remark sgets2()함수를 리팩토링한 함수임
\param
line - 한 Line이 채워짐
linesz - line 의 크기
ppos - line을 검색할 버퍼(IN/OUT겸용)
\return
성공시 읽혀진 라이의 길이, 0을 리턴하면 Line으로 취급할
문자가 없으므로(예:연속된 캐리지리턴들, 연속된 NULL문자들)
실패시 -1을 리턴함
\code
\endcode
-------------------------------------------------------------------------*/
int MemoryIni::sgets2(LPSTR line, DWORD linesz, LPSTR *ppos)
{
_ASSERTE( NULL != line );
_ASSERTE( NULL != ppos );
_ASSERTE( linesz > 0 );
if ( IsBadWritePtr(line, linesz) ||
NULL == ppos ||
0 == linesz )
{
_ASSERTE(!"Invalid parameter");
return -1;
}
int nread = 0;
LPSTR end = (LPSTR)&m_data[m_data_size];
LPSTR pos = *ppos;
// 현재위치가 끝이거나(이미 검색끝났을 경우) 에러 리턴
//
if ( pos >= end )
{
return -1;
}
// 현재 위치가 끝을 넘지 않으며, 채울수 있는 버퍼가 남아있을때까지 반복
//
while ( pos < end && linesz > 0 )
{
int chsize = (int)_tcsnbcnt( pos, 1 );
UINT ch = _tcsnextc(pos);
if ( ch == _T('\0') )
{
if ( !nread )
{
// pos = CharNext(pos) 가 아님!!
// 왜냐하면 *pos가 NULL이면 같은 포인터를 리턴하기 때문
pos++;
continue;
}
break;
}
else if ( ch == _T('\r') || ch == _T('\n') )
{
pos = CharNext(pos);
if ( !nread )
{
continue;
}
break;
}
// 중요!!
// 남아있는 버퍼사이즈가 chsize보다 작다면 chsize를 조절함
// 여기서 체크를 하지 않으면 linsz는 음수값이 될것이며
// line과 linesz에도 잘못된 결과가 들어갈 것이다
//
if ( linesz < (DWORD)chsize )
{
break;
}
memcpy( (PVOID)&line[nread], pos, chsize );
nread += chsize;
linesz -= chsize;
pos = CharNext(pos);
}
*ppos = pos;
if ( nread > 0 )
{
line[nread] = _T('\0');
}
return nread;
}
/** -----------------------------------------------------------------------
\brief ppos버퍼에서 linsz보다 작은 크기로 한줄 읽어서 line에 채움
\param
line - 한 Line이 채워짐
linesz - line 의 크기
ppos - line을 검색할 버퍼(IN/OUT겸용)
\return
성공시 읽혀진 라이의 길이, 0을 리턴하면 Line으로 취급할
문자가 없으므로(예:연속된 캐리지리턴들, 연속된 NULL문자들)
실패시 -1을 리턴함
\code
\endcode
-------------------------------------------------------------------------*/
int MemoryIni::sgets(LPTSTR line, DWORD linesz, LPTSTR *ppos)
{
_ASSERTE( NULL != line );
_ASSERTE( NULL != ppos );
_ASSERTE( linesz > 0 );
if ( 0 != IsBadWritePtr((LPVOID)line, linesz) ||
NULL == ppos ||
0 == linesz )
{
return -1;
}
LPTSTR end_marker = (LPTSTR) &m_data[m_data_size];
LPTSTR pos = *ppos;
UINT ch = _T('\0');
size_t i = 0;
// pos와 end_marker와 같다면 이미 순회를 마친 경우므로 에러
//
if (pos >= end_marker)
{
return -1;
}
//
// 현재 pos가 end_marker에 도달하기전까지의 NULL문자는 무시함
// pos가 end_marker와 같지 않은데 NULL이라면 포인터 증가
//
while ( pos < end_marker )
{
if ( _T('\0') != _tcsnextc(pos) )
break;
LPSTR oldpos = pos;
pos = CharNext(pos);
if ( pos == oldpos )
pos++;
}
while ( linesz > 0 && _T('\0') != _tcsnextc(pos) )
{
size_t ByteCount = _tcsnbcnt( pos, 1 );
ch = _tcsnextc( pos );
if ( ch == _T('\r') || ch == _T('\n') )
{
if ( 0 == i )
{
pos = CharNext(pos);
if (pos == end_marker)
break;
continue;
}
else
break;
}
memcpy( (PVOID)&line[i], pos, ByteCount );
i = i + ByteCount;
linesz = linesz - ByteCount;
pos = CharNext(pos);
if (pos == end_marker)
break;
}
// 더이상 생성할 라인이 없을 경우
//
if (0 == i && pos == end_marker)
return -1;
line[i] = _T('\0');
*ppos = pos;
return (int)i;
}
/** -----------------------------------------------------------------------
\brief 인자로 받은 s의 처음부터 검색하여 널 만나기 전까지
공백문자를 검색하여 건너뜀
\param
s - 스트링
\return
실패시 NULL리턴, 성공시 공백문자가 아닌 위치를 리턴함
\code
\endcode
-------------------------------------------------------------------------*/
LPTSTR MemoryIni::strskip( LPTSTR s )
{
if ( NULL == s )
return NULL;
while ( _istspace(_tcsnextc(s)) && _T('\0') != _tcsnextc(s) )
s = CharNext(s);
return s;
}
/** -----------------------------------------------------------------------
\brief 좌우 공백문자가 존재하면 삭제함
\param
str - 공백문자를 삭제할 스트링
\return
리턴값 없음
\code
\endcode
-------------------------------------------------------------------------*/
void MemoryIni::strip( LPTSTR str )
{
LPTSTR s = str;
int l;
while ( _istspace(_tcsnextc(s)) )
s = CharNext(s);
l = (int) _tcslen(s);
if (s != str)
memmove(str, s, l+1);
if (0 == l)
return;
s = str + l - 1;
while ( _istspace(_tcsnextc(s)) )
{
*s = _T('\0');
s = CharPrev( str, s );
}
}
/** -----------------------------------------------------------------------
\brief 섹션명을 인자로 받으며 새로운 섹션을 추가함
\param
name - 섹션명
\return
\code
\endcode
-------------------------------------------------------------------------*/
int MemoryIni::addNewSection(LPCTSTR name)
{
INI_SECTION ini_sec = {0};
const size_t kLength = (sizeof(ini_sec.name) / sizeof(ini_sec.name[0]));
::strncpy(ini_sec.name, name, kLength);
ini_sec.keys.clear();
m_sec.push_back( ini_sec );
return 0;
}
/** -----------------------------------------------------------------------
\brief 현재 분석하고 있는 섹션중 가장 최근 섹션에 key, value
쌍을 추가함
\remark Internal 함수임
\param
key - 추가할 키이름
value - 추가할 값데이터
\return
\code
\endcode
-------------------------------------------------------------------------*/
int MemoryIni::addtoLastSection(LPCTSTR key, LPCTSTR value)
{
// 가장 최근에 섹션이 등록된게 없다면 key, value값을 등록할 수 없음
//
if ( !m_sec.empty() )
{
INI_SECTION &ini_sec = m_sec.back();
KEYDATA keydata = {0};
strncpy(keydata.key, key, MAX_LINE);
strncpy(keydata.value, value, MAX_LINE);
ini_sec.keys.push_back( keydata );
}
return 0;
}
/** -----------------------------------------------------------------------
\brief 에디터에서 수정을 위하여 인터페이스 노출함(디버깅시 필요)
\remark MemoryIni에 담긴 m_data를 직접 확인하기 위해 필요
\param
\return
\code
\endcode
-------------------------------------------------------------------------*/
int MemoryIni::SetData(LPTSTR ini_data, DWORD ini_size)
{
_ASSERTE( NULL != ini_data );
_ASSERTE( 0 < ini_size );
if ( NULL == ini_data || 0 == ini_size )
{
return -1;
}
if ( NULL != m_data )
{
free(m_data);
m_data = NULL;
m_data_size = 0;
}
m_data = (PBYTE) calloc(1, ini_size);
if ( NULL == m_data )
{
return -1;
}
RtlCopyMemory(m_data, ini_data, ini_size);
m_data_size = ini_size;
parseData();
return 0;
}
/** -----------------------------------------------------------------------
\brief 에디터에서 수정을 위하여 인터페이스 노출함(디버깅시 필요)
\param
\return
\code
\endcode
-------------------------------------------------------------------------*/
int MemoryIni::GetData(LPTSTR ini_data, DWORD ini_size, PDWORD ini_written)
{
_ASSERTE( NULL != ini_data );
_ASSERTE( 0 < ini_size );
_ASSERTE( NULL != ini_written );
DWORD copy_size = m_data_size;
if ( NULL == ini_data || NULL == ini_written || 0 == ini_size )
{
return -1;
}
if ( NULL == m_data || 0 == m_data_size)
{
return -1;
}
if ( ini_size <= m_data_size )
{
copy_size = ini_size;
}
RtlCopyMemory( ini_data, (LPTSTR)m_data, copy_size );
*ini_written = copy_size;
return 0;
}
/** -----------------------------------------------------------------------
\brief m_data에 담긴 ini데이터를 분석함
\remark Internal 함수임
\param
\return
실패시 -1, 성공시 0
\code
\endcode
-------------------------------------------------------------------------*/
int MemoryIni::parseData()
{
_ASSERTE( NULL != m_data );
_ASSERTE( 0 != m_data_size );
if (NULL == m_data || 0 == m_data_size )
{
return -1;
}
LPTSTR pos = (LPTSTR) m_data;
TCHAR key[MAX_LINE] = {0,};
TCHAR line[MAX_LINE] = {0,};
TCHAR value[MAX_LINE] = {0,};
TCHAR section[MAX_LINE] = {0,};
memset( section, 0, sizeof(section) );
memset( value, 0, sizeof(value) );
memset( key, 0, sizeof(key) );
memset( line, 0, sizeof(line) );
m_sec.clear();
while ( 0 <= sgets2(line, MAX_LINE-1, &pos) )
{
// 공백을 건너뛴다
LPTSTR bc = strskip(line);
if ( _T(';') == _tcsnextc(bc) ||
_T('#') == _tcsnextc(bc) ||
_T('\0') == _tcsnextc(bc) )
{
continue;
}
if ( 1 == _stscanf(line, _T("[%[^]]"), section) )
{
addNewSection(section);
}
else if ( 2 == _stscanf(bc, _T("%[^=] = \"%[^\"]\""), key, value) ||
2 == _stscanf(bc, _T("%[^=] = '%[^\']'"), key, value) ||
2 == _stscanf(bc, _T("%[^=] = %[^;#]"), key, value) )
{
strip(key);
strip(value);
if ( !_tcscmp(value, _T("\"\"")) || !_tcscmp(value, _T("''")) )
{
value[0] = _T('\0');
}
addtoLastSection( key, value );
}
memset( section, 0, sizeof(section) );
memset( value, 0, sizeof(value) );
memset( key, 0, sizeof(key) );
memset( line, 0, sizeof(line) );
}
m_bParsed = TRUE;
return 0;
}
/** -----------------------------------------------------------------------
\brief
\param
\return
\code
\endcode
-------------------------------------------------------------------------*/
int MemoryIni::getValue(LPCTSTR section, LPCTSTR key, LPCTSTR value, DWORD slen)
{
if ( 0 == m_sec.size() )
{
return -1;
}
std::vector<INI_SECTION>::iterator it = m_sec.begin();
for (it; it != m_sec.end(); it++)
{
PINI_SECTION ini_sec = &(*it);
if ( 0 == _tcsicmp(ini_sec->name, section) )
{
std::vector<KEYDATA>::iterator it_key = ini_sec->keys.begin();
for ( ; it_key != ini_sec->keys.end(); it_key++)
{
PKEYDATA ini_key = &(*it_key);
if ( 0 == _tcsicmp(ini_key->key, key) )
{
strncpy((LPTSTR)value, ini_key->value, slen);
return 0;
}
}
}
}
return -1;
}
/** -----------------------------------------------------------------------
\brief
\param
\return
\code
\endcode
-------------------------------------------------------------------------*/
int MemoryIni::putValue(LPCTSTR section, LPCTSTR key, LPCTSTR value)
{
if ( 0 == m_sec.size() )
{
addNewSection( section );
addtoLastSection( key, value);
return 0;
}
// MAX_LINE을 넘치지 않게 하기 위해서 key, value 길이를 조절
//
size_t valuelen;
std::vector<INI_SECTION>::iterator it = m_sec.begin();
for (it; it != m_sec.end(); it++)
{
PINI_SECTION ini_sec = &(*it);
if ( 0 == _tcsicmp(ini_sec->name, section) )
{
std::vector<KEYDATA>::iterator it_key = ini_sec->keys.begin();
for ( ; it_key != ini_sec->keys.end(); it_key++)
{
PKEYDATA ini_key = &(*it_key);
// 기존에 key가 있다면 수정 함
//
if ( 0 == _tcsicmp(ini_key->key, key) )
{
valuelen = MAX_LINE - _tcslen(ini_key->key);
strncpy(ini_key->value, value, valuelen);
return 0;
}
}
// key가 존재하지 않으면, 새로운 키를 추가
valuelen = MAX_LINE - _tcslen(key);
KEYDATA keydata = {0};
strncpy(keydata.key, key, MAX_LINE);
strncpy(keydata.value, value, valuelen);
ini_sec->keys.push_back( keydata );
return 0;
}
}
// section이 존재하지 않으면, 새로운 섹션 추가
addNewSection( section );
addtoLastSection( key, value );
return 0;
}
/** -----------------------------------------------------------------------
\brief 메모리 INI정보에 String정보를 가져 옴
\param
\return
\code
\endcode
-------------------------------------------------------------------------*/
int MemoryIni::getString(LPCTSTR section, LPCTSTR key, LPTSTR pStr, DWORD slen)
{
_ASSERTE( NULL != section);
_ASSERTE( NULL != key);
_ASSERTE( NULL != pStr);
_ASSERTE( 0 < slen);
if ( NULL == section || NULL == key ||
NULL == pStr || 0 == slen )
{
return -1;
}
//if ( !m_bParsed )
//{
// return -1;
//}
if ( 0 > getValue(section, key, pStr, slen) )
{
return -1;
}
return 0;
}
/** -----------------------------------------------------------------------
\brief 메모리 INI정보에 Integer정보를 가져 옴
\param
\return
\code
\endcode
-------------------------------------------------------------------------*/
int MemoryIni::getInteger(LPCTSTR section, LPCTSTR key, LONG *pInt)
{
_ASSERTE( NULL != section);
_ASSERTE( NULL != key);
_ASSERTE( NULL != pInt);
if ( NULL == section || NULL == key || NULL == pInt )
{
return -1;
}
//if ( !m_bParsed )
//{
// return -1;
//}
TCHAR value[MAX_LINE] = {0,};
if ( 0 > getValue(section, key, value, MAX_LINE-1) )
{
return -1;
}
*pInt = _tstoi(value);
return 0;
}
/** -----------------------------------------------------------------------
\brief 메모리 INI정보에 String정보를 추가 함
\param
\return
\code
\endcode
-------------------------------------------------------------------------*/
int MemoryIni::putString(LPCTSTR section, LPCTSTR key, LPCTSTR pStr)
{
_ASSERTE( NULL != section);
_ASSERTE( NULL != key);
_ASSERTE( NULL != pStr);
if ( NULL == section || NULL == key || NULL == pStr )
{
return -1;
}
//if ( !m_bParsed )
//{
// return -1;
//}
if ( 0 > putValue(section, key, pStr) )
{
return -1;
}
return 0;
}
/** -----------------------------------------------------------------------
\brief 메모리 INI정보에 Integer정보를 추가 함
\param
\return
\code
\endcode
-------------------------------------------------------------------------*/
int MemoryIni::putInteger(LPCTSTR section, LPCTSTR key, LONG value)
{
_ASSERTE( NULL != section);
_ASSERTE( NULL != key);
if ( NULL == section || NULL == key )
{
return -1;
}
//if ( !m_bParsed )
//{
// return -1;
//}
TCHAR valstr[MAX_LINE] = {0,};
#if (_MSC_VER >= 1500) // vs2008 이상
sprintf_s(valstr, MAX_LINE, _T("%d"), value);
#else
StringCchPrintf(valstr, MAX_LINE, _T("%d"), value);
#endif
if ( 0 > putValue( section, key, valstr ) )
{
return -1;
}
return 0;
}
int MemoryIni::dumpData()
{
if ( 0 == m_sec.size() )
{
return -1;
}
// 메모리할당에 필요한 길이를 구함
//
ULONG tmp_size = MAX_LINE * 100;
PBYTE tmp_data = (PBYTE) calloc( 1, tmp_size );
ULONG written_size = 0;
if ( NULL == tmp_data )
{
return -1;
}
TCHAR line[MAX_LINE] = {0};
std::vector<INI_SECTION>::iterator it = m_sec.begin();
for (it; it != m_sec.end(); it++)
{
PINI_SECTION ini_sec = &(*it);
#if (_MSC_VER >= 1500) // vs2008 이상
sprintf_s( line, MAX_LINE, _T("[%s]\r\n"), ini_sec->name );
#else
StringCchPrintf( line, MAX_LINE, _T("[%s]\r\n"), ini_sec->name );
#endif
// 메모리 할당이 실패하면 더욱 늘려준다
if ( 0 > sputs( line, (LPTSTR)tmp_data, tmp_size ) )
{
tmp_size = tmp_size * 3;
tmp_data = (PBYTE) realloc(tmp_data, tmp_size);
if ( NULL == tmp_data )
{
free(tmp_data);
tmp_data = NULL;
return -1;
}
sputs( line, (LPTSTR)tmp_data, tmp_size );
}
written_size = written_size + (_tcslen(line) * sizeof(TCHAR));
std::vector<KEYDATA>::iterator it_key = ini_sec->keys.begin();
for ( ; it_key != ini_sec->keys.end(); it_key++)
{
PKEYDATA ini_key = &(*it_key);
#if (_MSC_VER >= 1500) // vs2008 이상
sprintf_s( line, MAX_LINE, _T("%s = %s\r\n"), ini_key->key, ini_key->value );
#else
StringCchPrintf( line, MAX_LINE, _T("%s = %s\r\n"), ini_key->key, ini_key->value );
#endif
// 메모리 할당이 실패하면 더욱 늘려준다
if ( 0 > sputs( line, (LPTSTR)tmp_data, tmp_size ) )
{
tmp_size = tmp_size * 3;
tmp_data = (PBYTE) realloc(tmp_data, tmp_size);
if ( NULL == tmp_data )
{
free(tmp_data);
tmp_data = NULL;
return -1;
}
sputs( line, (LPTSTR)tmp_data, tmp_size );
}
written_size = written_size + (_tcslen(line) * sizeof(TCHAR));
}
}
// m_data 우선 free시켜야 한다
//
if ( NULL != m_data )
{
free( m_data );
}
m_data = tmp_data;
m_data_size = written_size;
return 0;
}
int MemoryIni::sputs(LPCTSTR line, LPCTSTR data, ULONG datasz)
{
size_t linesz = _tcslen(line) * sizeof(TCHAR);
size_t fillsz = _tcslen(data) * sizeof(TCHAR);
size_t leftsz = datasz - fillsz;
if ( leftsz <= linesz )
{
return -1;
}
// dummy code
if (FAILED(strcat((LPTSTR)data, line)))
{
return -1;
}
return 0;
}
/** -----------------------------------------------------------------------
\brief 메모리의 INI정보를 파일로 씀
\param
\return
\code
\endcode
-------------------------------------------------------------------------*/
int MemoryIni::SaveToFile(LPCTSTR inifile, INI_CRYPT encrypt = NULL)
{
_ASSERTE( NULL != inifile);
if ( NULL == inifile)
return -1;
// 파일로 저장하기 전에 INI형태로 데이터를 변환 함
//
if ( 0 > dumpData() )
{
return -1;
}
HANDLE hFile = CreateFile( inifile,
FILE_SHARE_READ|FILE_SHARE_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL );
if ( INVALID_HANDLE_VALUE == hFile )
return -1;
// 암호화를 원한다면 암호화시켜서 저장
//
int retval = 0;
DWORD dwWritten = 0;
if ( encrypt )
{
PBYTE save_data = NULL;
DWORD save_size = 0;
if ( 0 != encrypt(m_data, m_data_size, &save_data, &save_size) )
{
CloseHandle(hFile);
return -1;
}
retval = WriteFile(hFile, save_data, save_size, &dwWritten, NULL);
free(save_data);
}
else
{
retval = WriteFile(hFile, m_data, m_data_size, &dwWritten, NULL);
}
if ( 0 == retval )
{
CloseHandle(hFile);
return -1;
}
CloseHandle(hFile);
return 0;
}
/** -----------------------------------------------------------------------
\brief INI파일을 메모리로 로드함
\param
\return
\code
\endcode
-------------------------------------------------------------------------*/
int MemoryIni::LoadFromFile(LPCTSTR inifile, INI_CRYPT decrypt = NULL)
{
_ASSERTE( NULL != inifile);
if ( NULL == inifile)
return -1;
HANDLE hFile = CreateFile( inifile,
FILE_SHARE_READ,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL );
if ( INVALID_HANDLE_VALUE == hFile )
return -1;
PBYTE load_data = NULL;
DWORD load_size = 0;
DWORD dwSizeLow = 0;
DWORD dwSizeHigh = 0;
dwSizeLow = GetFileSize(hFile, &dwSizeHigh);
load_size = dwSizeHigh << 16 | dwSizeLow;
HANDLE hMap = CreateFileMapping( hFile,
NULL,
PAGE_READONLY,
0,
0,
NULL );
if ( INVALID_HANDLE_VALUE == hFile )
{
CloseHandle(hFile);
return -1;
}
PBYTE pMap = (PBYTE) MapViewOfFile( hMap,
FILE_MAP_READ,
0,
0,
0 );
if ( NULL == pMap )
{
CloseHandle(hMap);
CloseHandle(hFile);
return -1;
}
load_data = (PBYTE) calloc( 1, load_size );
if ( NULL == load_data )
{
CloseHandle(hMap);
CloseHandle(hFile);
return -1;
}
RtlCopyMemory( load_data, pMap, load_size );
UnmapViewOfFile( pMap );
CloseHandle( hMap );
CloseHandle( hFile );
// 이전에 m_data가 있으면 메모리 해제
//
if ( NULL != m_data)
{
free(m_data);
m_data = NULL;
m_data_size = 0 ;
}
// 암호화된 파일에 대한 복호화를 원한다면
//
if ( decrypt )
{
if ( 0 != decrypt(load_data, load_size, &m_data, &m_data_size) )
{
free(load_data);
return -1;
}
free(load_data);
load_data = NULL;
load_size = 0;
}
else
{
m_data = load_data;
m_data_size = load_size;
}
// 로드된 INI데이터를 파싱한다
//
parseData();
return 0;
}