Files
Sun1602/ProgramCommon/TableData.cpp
T
2022-10-26 12:25:11 +08:00

517 lines
16 KiB
C++

#include "stdafx.h"
#include "./TableData.h"
//==================================================================================================
//==================================================================================================
#define WRAP_CODE_FOR_DEVPARTNER_SKIP(block) block
#define _SDL_IS_WHITESPACE(chr) ((chr) == ' ' || (chr) == '\t')
#define _SDL_IS_COMMENT(pchr) (*(pchr) == '/' || *((pchr)+1) == '/')
#define _SDL_IS_SECTOR(chr) ((chr) == '[')
#define _SDL_IS_EOL(pchr) (*((pchr)) == '\0') //(*((pchr)+1) == '\0')
#define token_check_by_filter(token_val, filter_ptr) \
(filter_ptr[(token_val) >> 3] & (1 << ((token_val) & 7)))
__forceinline char*
TableData::_RawTokenizer(char* pToken, DelimiterBuffer* delimeters)
{ // single thread support
// reference c-runtime library 'strtok'
const BYTE* filter = (BYTE*)delimeters;
static char* next = 0;
BYTE* str = pToken ? (BYTE*)pToken : (BYTE*)next;
while(token_check_by_filter(*str, filter) && *str)
++str;
pToken = (char*)str;
for( ; *str; ++str) {
if(token_check_by_filter(*str, filter)) {
*str++ = '\0';
break;
}
}
next = (char*)str;
return pToken == (char*)str ? 0 : pToken;
}
TableData::TableData()
: m_pBuffer(0)
, m_ColumnCount(0)
, m_pColumnHash(0)
, m_LineCount(0)
{
m_LineRef.pRef = 0;
}
char*
TableData::file_data_buffer_static_ = 0;
BYTE*
TableData::token_stream_static_ = 0;
//DWORD*
//TableData::crc_table_static_ = 0;
TableData::DelimiterBuffer*
TableData::delimiters_static_[2] = { 0, };
TableData::TABLE_NAME_HASH*
TableData::column_hash_static_ = 0;
/*
// changes, f100813.3L, replace an interface to acquire an unique id
//////////////////////////////////////////////////////////////////////////
// reference Unreal3 GameEngine, Epic Games, co.ltd.
// - changes : 처음 사용한 SHA alogrithm hash는 유니크 할 수는 없었다.
// 희박한 확률이라도 Hash중복 가능성이 있었고, 또한 기타 여러
// 가지 면에서도 당근 아래와 같은 코드가 더 효율적이다.
#define _SDL_CRC32_POLYNOMIAL 0x04C11DB7
BOOLEAN
TableData::_MakeHashInit()
{
for(DWORD crc = 0 ; crc < 256 ; ++crc) {
for(DWORD chr = (crc << 24), loop = 8 ; loop ; --loop) {
chr = (chr & 0x80000000)
? (chr << 1) ^ _SDL_CRC32_POLYNOMIAL
: (chr << 1)
;
crc_table_static_[crc] = chr;
}
}
return TRUE;
}
//////////////////////////////////////////////////////////////////////////
DWORD
TableData::MakeHash(const char* const pszString)
{
const char* it = pszString;
DWORD crc = DWORD(-1);
for ( ; *it != '\0'; ++it)
{
char c = *it;
DWORD cL = c & 255;
crc = (crc << 8) ^ crc_table_static_[ (crc >> 24) ^ cL ];
DWORD cH = (c >> 8) & 255;
crc = (crc << 8) ^ crc_table_static_[ (crc >> 24) ^ cH ];
}
return ~crc;
}
*/
BOOLEAN
TableData::CreateEnvironment()
{
// 최대치 산정해 두자.
file_data_buffer_static_ = (char*)malloc(MaxScriptDataSize);
// relation file_size & token_list_buffer_size
// MaxTokenBlockSize == (file_size / 2) * sizeof(token_info)
token_stream_static_ = (BYTE*)malloc(MaxTokenStreamSize);
//crc_table_static_ = (DWORD*)malloc(256 * sizeof(DWORD));
delimiters_static_[0] = (DelimiterBuffer*)malloc(sizeof(DelimiterBuffer));
delimiters_static_[1] = (DelimiterBuffer*)malloc(sizeof(DelimiterBuffer));
ZeroMemory(delimiters_static_[0], sizeof(DelimiterBuffer));
ZeroMemory(delimiters_static_[1], sizeof(DelimiterBuffer));
#undef SetDelimBit
#define SetDelimBit(chr, slot) \
slot[(chr) >> 3] |= 1 << ((chr) & 7)
DelimiterBuffer& rRowDelim = *delimiters_static_[0];
SetDelimBit('\r', rRowDelim);
SetDelimBit('\n', rRowDelim);
DelimiterBuffer& rColDelim = *delimiters_static_[1];
SetDelimBit('\t', rColDelim);
//#undef SetDelimBit
//_MakeHashInit();
column_hash_static_ = new TABLE_NAME_HASH;
column_hash_static_->Initialize(300);
return true;
}
BOOLEAN
TableData::DestroyEnvironment()
{
// (BUGFIX) (091119) (WAVERIX) SolarAuth로부터 스크립트를 제공 받는 경우에는
// 네트워크 연결/재연결 등, 연결 이벤트마다 스크립트 확인 절차를 거치게 되면서
// 리소스 해제 루틴이 호출되게 된다.
if(file_data_buffer_static_ == 0)
return true;
free(file_data_buffer_static_);
file_data_buffer_static_ = 0;
free(token_stream_static_);
token_stream_static_ = 0;
//free(crc_table_static_);
//crc_table_static_ = 0;
free(delimiters_static_[0]);
free(delimiters_static_[1]);
delete column_hash_static_;
return true;
}
BOOLEAN
TableData::_OpenFileAndReadBuffer(const char* fileName,
BOOLEAN use_packfile, const char* packFileName,BOOLEAN is_enc)
{
//MessageBoxA(0,"test","test,",0);
if(fileName == NULL)
return FALSE;
if(use_packfile == 0)
{
HANDLE fileHandle = ::CreateFile(fileName,
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
DWORD fileSize = ::GetFileSize(fileHandle, NULL);
DWORD paddingFileSize = fileSize + 10;
if(!FlowControl::FCAssert(paddingFileSize <= MaxScriptDataSize))
return FALSE;
m_pBuffer = file_data_buffer_static_;
DWORD OUT readedSize = 0;
::ReadFile(fileHandle, m_pBuffer, fileSize, &readedSize, NULL);
if(is_enc){ //xiaomi 警속TXT속쵱
//if (m_pBuffer[0] == 'M' && m_pBuffer[1] == 'I')
//{
for (int i=0;i<readedSize-4;i+=4)
{
*(int *)(&m_pBuffer[i]) = (*(int *)(&m_pBuffer[i])) ^ 0x12365487;
}
//}
}
ZeroMemory(m_pBuffer + readedSize, paddingFileSize - readedSize);
::CloseHandle((HANDLE)fileHandle);
}
else
{
util::SolarFileReader sr;
BOOLEAN result = !!sr.OpenFileFromPack(const_cast<char*>(packFileName),
const_cast<char*>(fileName),
SEPERATOR_WHITESPACE_FOR_COMMENTS);
if(result == 0)
{
SUNLOG(eCRITICAL_LOG, "File[%s] Open Error", fileName);
//char szError[MAX_PATH + 1];
//_snprintf(szError, MAX_PATH, "File[%s] Open Error \n", pszFileName);
//SUNLOG(eCRITICAL_LOG, szError);
ASSERT(!"File Open Error");
return FALSE;
}
const char* buffer = sr.GetBuffer();
DWORD fileSize = sr.GetBufferSize();
DWORD paddingFileSize = fileSize + 10;
if(!FlowControl::FCAssert(paddingFileSize <= MaxScriptDataSize))
return FALSE;
m_pBuffer = file_data_buffer_static_;
CopyMemory(m_pBuffer, buffer, fileSize);
ZeroMemory(m_pBuffer + fileSize, paddingFileSize - fileSize);
sr.CloseFile();
}
return TRUE;
}
BOOLEAN
TableData::_UnLoad()
{
m_ColumnCount = 0;
m_pBuffer = 0;
util::LList::Init(&m_Rows);
if(m_pColumnHash)
m_pColumnHash = NULL;
m_LineRef.count = 0;
if(m_LineRef.pRef)
m_LineRef.pRef = 0;
m_LineCount = 0;
return TRUE;
}
BOOLEAN
TableData::Load(eSCRIPT_CODE script_code, const TCHAR* pszFileName,
BOOLEAN use_packfile, const char* packFileName,BOOLEAN is_enc)
{
if(_OpenFileAndReadBuffer(pszFileName, use_packfile, packFileName,is_enc) == FALSE)
{
//printf("error --
return FALSE;
}
BOOLEAN bRet = _BuildDictionary(script_code);
return bRet;
}
BOOLEAN
TableData::LoadBuffer(eSCRIPT_CODE script_code, const BYTE* pBuffer, const DWORD buffer_size)
{
if(!(pBuffer && buffer_size))
return FALSE;
const DWORD alloced_size = buffer_size + !!(pBuffer[buffer_size-1] != '\0');
if(!FlowControl::FCAssert(alloced_size <= MaxScriptDataSize))
return FALSE;
m_pBuffer = file_data_buffer_static_;
CopyMemory(m_pBuffer, pBuffer, buffer_size);
m_pBuffer[alloced_size - 1] = '\0';
BOOLEAN bRet = _BuildDictionary(script_code);
return bRet;
}
void
TableData::RegisterColumnDelimiters(const char* delimiters)
{
ZeroMemory(delimiters_static_[1], sizeof(DelimiterBuffer));
DelimiterBuffer& rColDelim = *delimiters_static_[1];
for(const char* it = delimiters; *it; ++it) {
char delim = *it;
SetDelimBit(delim, rColDelim);
}
}
BOOLEAN
TableData::_BuildDictionary(eSCRIPT_CODE script_code)
{
BYTE* pTokenOffset = token_stream_static_;
DWORD64* const pTokenTailMark =
(DWORD64*)&token_stream_static_[MaxTokenStreamSize - sizeof(DWORD64)];
*pTokenTailMark = 0xDEADBEEFDEADBEEFull;
{ // 스크립트 특유의 구분자 설정
#ifdef _NA_0_20100312_MODIFY_DELIMITERS_FOR_TEXTSCRIPT
const char* delimiters_text = "\t";
#else
const char* delimiters_text = "\"\t";
#endif
const char* delimiters = " \t"; // { space, tab }
switch(script_code)
{
case SCRIPT_QUEST_TEXT_INFO:
case SCRIPT_HELP_MESSAGE:
#ifdef _DH_DIVIDE_MESSAGE_TEXT
case SCRIPT_MESSAGE_TEXT:
#else
case SCRIPT_MESSAGE:
#endif//_DH_DIVIDE_MESSAGE_TEXT
case SCRIPT_FILTER:
case SCRIPT_FILTERNAME:
#ifdef _DH_DISPLAY_FOR_MISSION_OBJECT
case SCRIPT_MISSION_TEXT_INFO:
#endif//_DH_DISPLAY_FOR_MISSION_OBJECT
#ifdef _NA_002521_20110613_TOTAL_GUIDE_SYSTEM
case SCRIPT_TOTAL_GUIDE_MESSAGE:
#endif //_NA_002521_20110613_TOTAL_GUIDE_SYSTEM
case SCRIPT_HOT_KEY_INFO:
case SCRIPT_MISSIONHELP_INFO:
case SCRIPT_OPTION_CATEGORY_INFO:
delimiters = delimiters_text;
break;
}
RegisterColumnDelimiters(delimiters);
};
//////////////////////////////////////////////////////////////////////////
//WRAP_CODE_FOR_DEVPARTNER_SKIP(
{ // Build Row Tokenizing
util::LList::Init(&m_Rows);
char* pToken = _RawTokenizer(m_pBuffer, delimiters_static_[0]);
while(pToken != 0)
{
LineListNode* const pLine = reinterpret_cast<LineListNode*>(pTokenOffset);
pTokenOffset += sizeof(LineListNode);
util::LList::Init(pLine);
LinePair& rPair = pLine->linePair;
rPair.pLinePtr = pToken;
rPair.pColumns = 0;
util::LList::AddPrev(pLine, &m_Rows);
pToken = _RawTokenizer(0, delimiters_static_[0]);
++m_LineCount;
}
};
//); //WRAP_CODE_FOR_DEVPARTNER_SKIP
//////////////////////////////////////////////////////////////////////////
//WRAP_CODE_FOR_DEVPARTNER_SKIP(
{ // Build Col Tokenizing
// CHANGES: f110415.5L, we need line comparision based on column schemes.
bool check_first_line = true;
LineListNode* it = static_cast<LineListNode*>(m_Rows.next);
while (it != &m_Rows)
{
LinePair& rPair = it->linePair;
ColInfo* const pCols = reinterpret_cast<ColInfo*>(pTokenOffset);
rPair.pColumns = pCols;
pCols->count = 0;
char* pOffset = rPair.pLinePtr;
BOOLEAN bEOL = _SDL_IS_EOL(pOffset);
char* pToken = bEOL ? 0 : _RawTokenizer(pOffset, delimiters_static_[1]);
while(pToken != 0)
{
if(_SDL_IS_COMMENT(pToken))
break;
pCols->pRef[pCols->count++] = pToken;
pToken = _RawTokenizer(0, delimiters_static_[1]);
}
if(bEOL)
break;
if(pCols->count == 0)
{
it = util::LList::Delete(it);
--m_LineCount;
continue;
}
// CHANGES: f110415.5L, we need line comparision based on column schemes.
if (check_first_line) {
m_ColumnCount = pCols->count;
check_first_line = false;
};
while(_SDL_IS_WHITESPACE(*pOffset))
++pOffset;
if(_SDL_IS_COMMENT(pOffset))
{
it = util::LList::Delete(it);
--m_LineCount;
continue;
}
//pTokenOffset += sizeof(pCols->count) + (sizeof(char*) * pCols->count);
pTokenOffset += sizeof(pCols->count) + (sizeof(char*) * m_ColumnCount);
it = it->next;
}
};
//); //WRAP_CODE_FOR_DEVPARTNER_SKIP
//////////////////////////////////////////////////////////////////////////
// Buffer Overrun이 발생하지 않아야 하며, (발생한다면, 실제 데이터가 대단히 커졌을 가능성 있음)
// 데이터가 또한 없으면 안된다.
if(!FlowControl::FCAssert(pTokenOffset < (BYTE*)pTokenTailMark &&
util::LList::IsEmpty(&m_Rows) == FALSE))
return FALSE;
//////////////////////////////////////////////////////////////////////////
{ // parse & make hash column name
m_pColumnHash = column_hash_static_;// new SolarHashTable<INT>;
m_pColumnHash->RemoveAll();
#if SOLAR_HASH_TABLE_VERSION < 0x0200
m_pColumnHash->Initialize(m_ColumnCount);
#endif
LineListNode* columnLine = static_cast<LineListNode*>(m_Rows.next);
LinePair& rPair = columnLine->linePair;
char* pOffset = rPair.pLinePtr; //for debug
ColInfo* pCols = rPair.pColumns;
int counter = 0;
char **it = &pCols->pRef[0], **itend = &pCols->pRef[pCols->count];
for( ; it != itend; ++it)
{
char* pToken = *it;
if(pToken[0] == 'F' && (strncmp("FieldEnd", pToken, 10) == 0))
break;
//WRAP_CODE_FOR_DEVPARTNER_SKIP(
DWORD hash = util::StreamHashKey::GetStringHashKey(pToken);
INT value = m_pColumnHash->GetData(hash);
ASSERT(!value);
m_pColumnHash->Add(++counter, hash); // 0이 아닌 값으로 설정
//); //WRAP_CODE_FOR_DEVPARTNER_SKIP
}
util::LList::Delete(columnLine);
--m_LineCount;
if(m_ColumnCount != counter + 1) {
// critical error
}
else if(m_LineCount == 0) {
--m_ColumnCount;
}
else {
m_ColumnCount = counter;
}
};
//////////////////////////////////////////////////////////////////////////
//WRAP_CODE_FOR_DEVPARTNER_SKIP(
static ColInfo* pCols_debug;
pCols_debug = 0;
{ // make serialized array index table
m_LineRef.count = m_LineCount;
m_LineRef.pRef = reinterpret_cast<ColInfo**>(pTokenOffset);
pTokenOffset += sizeof(ColInfo*) * m_LineRef.count;
LineListNode* it = static_cast<LineListNode*>(m_Rows.next);
for(int lineNo = 0; it != &m_Rows; it = it->next)
{
LinePair& rPair = it->linePair;
ColInfo* pCols = rPair.pColumns;
m_LineRef.pRef[lineNo++] = pCols;
if(m_ColumnCount != pCols->count) {
pCols_debug = pCols;
#ifdef _SERVER
SUNLOG(eCRITICAL_LOG,
"Script=%d|Column0=%s|Column1=%s|斤口=Column mismatched, Check script data|",
script_code, pCols->pRef[0], pCols->pRef[1]);
#endif
// (WARNING) 여기서 문제가 발생하면, 'pCols_debug'에 기재된 레코드에 column에 문제가
// 있는 겁니다. (누락된 column또는 더 많은 입력이 일어난 상태)
// - parity check methodology
__debugbreak();
}
}
}
//); //WRAP_CODE_FOR_DEVPARTNER_SKIP
//////////////////////////////////////////////////////////////////////////
// Buffer overrun이 발생해서는 안된다. 발생한다면, 실제 데이터가 대단히 커졌을 가능성 있음.
return FlowControl::FCAssert(pTokenOffset < (BYTE*)pTokenTailMark &&
*pTokenTailMark == 0xDEADBEEFDEADBEEFull);
}
//==================================================================================================
//==================================================================================================