ReChecker/src/resource.cpp
2022-09-25 00:01:37 +07:00

483 lines
10 KiB
C++

#include "precompiled.h"
CResourceFile Resource;
std::vector<const char *> StringsCache;
void CResourceFile::CreateResourceList()
{
int nConsistency = g_RehldsServerData->GetConsistencyNum();
m_DecalsNum = g_RehldsServerData->GetDecalNameNum();
for (auto iter = m_resourceList.cbegin(), end = m_resourceList.cend(); iter != end; ++iter)
{
CResourceBuffer *pRes = (*iter);
// prevent duplicate of filenames
// check if filename is been marked so do not add the resource again
if (!pRes->IsDuplicate())
{
// check limit resource
if (g_RehldsServerData->GetResourcesNum() >= MAX_RESOURCE_LIST)
{
UTIL_Printf(__FUNCTION__ ": can't add resource \"%s\" on line %d; exceeded the limit of resources max '%d'\n", pRes->GetFileName(), pRes->GetLine(), MAX_RESOURCE_LIST);
break;
}
#ifdef _DEBUG
UTIL_Printf(__FUNCTION__ " :: (%s)(%s)(%x)\n", pRes->GetFileName(), pRes->GetCmdExec(), pRes->GetFileHash());
#endif // _DEBUG
SV_AddResource(t_decal, pRes->GetFileName(), 0, RES_CHECKFILE, m_DecalsNum++);
nConsistency++;
}
}
m_DecalsNum = g_RehldsServerData->GetDecalNameNum();
g_RehldsServerData->SetConsistencyNum(nConsistency);
}
void CResourceFile::Clear()
{
m_PrevHash = 0;
m_DecalsNum = 0;
// clear resources
m_resourceList.clear();
ClearStringsCache();
}
void CResourceFile::Init()
{
char *pos;
char path[MAX_PATH_LENGTH];
strncpy(path, GET_PLUGIN_PATH(PLID), sizeof(path) - 1);
path[sizeof(path) - 1] = '\0';
pos = strrchr(path, '/');
if (*pos == '\0')
return;
*(pos + 1) = '\0';
// resources.ini
snprintf(m_PathDir, sizeof(m_PathDir), "%s" FILE_INI_RESOURCES, path);
}
inline uint8 hexbyte(uint8 *hex)
{
return ((hex[0] > '9' ? toupper(hex[0]) - 'A' + 10 : hex[0] - '0') << 4)
| (hex[1] > '9' ? toupper(hex[1]) - 'A' + 10 : hex[1] - '0');
}
inline bool invalidchar(const char c)
{
// to check for invalid characters
return (c == '\\' || c == '/' || c == ':'
|| c == '*' || c == '?'
|| c == '"' || c == '<'
|| c == '>' || c == '|') != 0;
}
bool IsValidFilename(char *psrc, char &pchar)
{
char *pch = strrchr(psrc, '/');
if (pch == NULL)
pch = psrc;
while (*pch++)
{
if (invalidchar(*pch))
{
pchar = *pch;
return false;
}
}
return true;
}
bool IsFileHasExtension(char *psrc)
{
// find the extension filename
char *pch = strrchr(psrc, '.');
if (pch == NULL)
return false;
// the size extension
if (strlen(&pch[1]) <= 0)
return false;
return strchr(pch, '/') == NULL;
}
void CResourceFile::LoadResources()
{
char *pos;
char line[4096];
uint8 hash[16];
FILE *fp;
int argc;
int len;
flag_type_resources flag;
char filename[MAX_PATH_LENGTH];
char cmdBufExec[MAX_PATH_LENGTH];
int cline = 0;
fp = fopen(m_PathDir, "r");
if (fp == NULL)
{
m_ConfigFailed = true;
UTIL_Printf(__FUNCTION__ ": can't find path to " FILE_INI_RESOURCES "\n");
return;
}
while (!feof(fp) && fgets(line, sizeof(line), fp))
{
pos = line;
cline++;
if (*pos == '\0' || *pos == ';' || *pos == '\\' || *pos == '/' || *pos == '#')
continue;
const char *pToken = GetNextToken(&pos);
argc = 0;
flag = FLAG_TYPE_NONE;
memset(hash, 0, sizeof(hash));
while (pToken != NULL && argc <= MAX_PARSE_ARGUMENT)
{
len = strlen(pToken);
switch (argc)
{
case ARG_TYPE_FILE_NAME:
{
strncpy(filename, pToken, len);
filename[len] = '\0';
break;
}
case ARG_TYPE_FILE_HASH:
{
uint8 pbuf[33];
strncpy((char *)pbuf, pToken, len);
pbuf[len] = '\0';
if (_stricmp((const char *)pbuf, "UNKNOWN") == 0)
{
flag = FLAG_TYPE_HASH_ANY;
}
else
{
for (int i = 0; i < sizeof(pbuf) / 2; i++)
hash[i] = hexbyte(&pbuf[i * 2]);
flag = (*(uint32 *)&hash[0] != 0x00000000) ? FLAG_TYPE_EXISTS : FLAG_TYPE_MISSING;
}
break;
}
case ARG_TYPE_CMD_EXEC:
{
strncpy(cmdBufExec, pToken, len);
cmdBufExec[len] = '\0';
if (_stricmp(cmdBufExec, "IGNORE") == 0)
{
flag = FLAG_TYPE_IGNORE;
cmdBufExec[0] = '\0';
}
else if (_stricmp(cmdBufExec, "BREAK") == 0)
{
flag = FLAG_TYPE_BREAK;
cmdBufExec[0] = '\0';
}
else
{
// replface \' to "
StringReplace(cmdBufExec, "'", "\"");
}
break;
}
case ARG_TYPE_FLAG:
{
if (_stricmp(pToken, "IGNORE") == 0)
{
flag = FLAG_TYPE_IGNORE;
}
else if (_stricmp(pToken, "BREAK") == 0)
{
flag = FLAG_TYPE_BREAK;
}
break;
}
default:
break;
}
argc++;
pToken = GetNextToken(&pos);
if (pToken == NULL && argc == ARG_TYPE_FLAG)
{
// go to next argument
argc++;
}
}
if (argc >= MAX_PARSE_ARGUMENT)
{
char pchar;
if (strlen(filename) <= 0)
{
UTIL_Printf(__FUNCTION__ ": Failed to load \"" FILE_INI_RESOURCES "\"; path to filename is empty on line %d\n", cline);
continue;
}
else if (!IsFileHasExtension(filename))
{
UTIL_Printf(__FUNCTION__ ": Failed to load \"" FILE_INI_RESOURCES "\"; filename has no extension on line %d\n", cline);
continue;
}
else if (!IsValidFilename(filename, pchar))
{
UTIL_Printf(__FUNCTION__ ": Failed to load \"" FILE_INI_RESOURCES "\"; filename has invalid character '%c' on line %d\n", pchar, cline);
continue;
}
else if (flag == FLAG_TYPE_NONE)
{
UTIL_Printf(__FUNCTION__ ": Failed to load \"" FILE_INI_RESOURCES "\"; parsing hash failed on line %d\n", cline);
continue;
}
// TODO: is there a need to flag FLAG_TYPE_BREAK without cmdexec?
else if (strlen(cmdBufExec) <= 0 && (flag != FLAG_TYPE_IGNORE && flag != FLAG_TYPE_BREAK))
{
UTIL_Printf(__FUNCTION__ ": Failed to load \"" FILE_INI_RESOURCES "\"; parsing command line is empty on line %d\n", cline);
continue;
}
AddElement(filename, cmdBufExec, flag, *(uint32 *)&hash[0], cline);
}
else if (pToken != NULL || argc > ARG_TYPE_FILE_NAME)
{
UTIL_Printf(__FUNCTION__ ": Failed to load \"" FILE_INI_RESOURCES "\"; parsing not enough arguments on line %d (got '%d', expected '%d')\n", cline, argc, MAX_PARSE_ARGUMENT);
}
}
fclose(fp);
m_ConfigFailed = false;
}
const char *CResourceFile::GetNextToken(char **pbuf)
{
char *rpos = *pbuf;
if (*rpos == '\0')
return NULL;
// skip spaces at the beginning
while (*rpos != 0 && isspace(*rpos))
rpos++;
if (*rpos == '\0')
{
*pbuf = rpos;
return NULL;
}
const char *res = rpos;
char *wpos = rpos;
char inQuote = '\0';
while (*rpos != '\0')
{
char cc = *rpos;
if (inQuote)
{
if (inQuote == cc)
{
inQuote = '\0';
rpos++;
}
else
{
if (rpos != wpos)
*wpos = cc;
rpos++;
wpos++;
}
}
else if (isspace(cc))
{
break;
}
else if (cc == '\'' || cc == '"')
{
inQuote = cc;
rpos++;
}
else
{
if (rpos != wpos)
*wpos = cc;
rpos++; wpos++;
}
}
if (*rpos != '\0')
rpos++;
*pbuf = rpos;
*wpos = '\0';
return res;
}
void CResourceFile::AddElement(char *filename, char *cmdExec, flag_type_resources flag, uint32 hash, int line)
{
auto nRes = new CResourceBuffer(filename, cmdExec, flag, hash, line);
// to mark files which are not required to add to the resource again
for (auto iter = m_resourceList.cbegin(), end = m_resourceList.cend(); iter != end; ++iter)
{
CResourceBuffer *pRes = (*iter);
if (_stricmp(pRes->GetFileName(), filename) == 0)
{
// resource name already registered
nRes->SetDuplicate();
break;
}
}
m_resourceList.push_back(nRes);
}
bool CResourceFile::FileConsistencyResponce(IGameClient *pSenderClient, resource_t *resource, uint32 hash)
{
if (resource->type != t_decal
|| resource->nIndex < m_DecalsNum) // if by some miracle the decals will have the flag RES_CHECKFILE
// to be sure not bypass the decals
{
m_PrevHash = hash;
return true;
}
// strange thing
// if this happened when missing all the files from client
if (!m_PrevHash)
{
return true;
}
bool bHandled = false;
flag_type_resources typeFind;
std::vector<CResourceBuffer *> tempResourceList;
for (auto iter = m_resourceList.cbegin(), end = m_resourceList.cend(); iter != end; ++iter)
{
CResourceBuffer *pRes = (*iter);
if (strcmp(resource->szFileName, pRes->GetFileName()) != 0)
continue;
typeFind = pRes->GetFileFlag();
if (m_PrevHash == hash && typeFind != FLAG_TYPE_MISSING)
typeFind = FLAG_TYPE_NONE;
switch (typeFind)
{
case FLAG_TYPE_BREAK:
/* empty */
break;
case FLAG_TYPE_IGNORE:
tempResourceList.push_back(pRes);
break;
case FLAG_TYPE_EXISTS:
if (pRes->GetFileHash() == hash)
{
typeFind = FLAG_TYPE_EXISTS;
}
break;
case FLAG_TYPE_HASH_ANY:
for (auto it = tempResourceList.cbegin(); it != tempResourceList.cend(); ++it)
{
CResourceBuffer *pTemp = (*it);
if (_stricmp(pTemp->GetFileName(), pRes->GetFileName()) != 0)
continue;
if (pTemp->GetFileHash() == hash)
{
typeFind = FLAG_TYPE_NONE;
break;
}
}
break;
case FLAG_TYPE_MISSING:
if (m_PrevHash != hash)
{
typeFind = FLAG_TYPE_NONE;
}
break;
default:
typeFind = FLAG_TYPE_NONE;
break;
}
if (typeFind != FLAG_TYPE_NONE)
{
// push exec cmd
Exec.AddElement(pSenderClient, pRes, hash);
#ifdef _DEBUG
UTIL_Printf(" -> filename: (%s), cmdexec: (%s), hash: (%x), typeFind: (%d)\n", pRes->GetFileName(), pRes->GetCmdExec(), pRes->GetFileHash(), typeFind);
#endif // _DEBUG
}
bHandled = true;
}
m_PrevHash = hash;
return !bHandled;
}
const char *DuplicateString(const char *str)
{
for (auto iter = StringsCache.cbegin(), end = StringsCache.cend(); iter != end; ++iter)
{
if (!strcmp(*iter, str))
return *iter;
}
const char *s = strcpy(new char[strlen(str) + 1], str);
StringsCache.push_back(s);
return s;
}
void ClearStringsCache()
{
for (auto iter = StringsCache.begin(), end = StringsCache.end(); iter != end; ++iter)
delete [] *iter;
StringsCache.clear();
}
CResourceBuffer::CResourceBuffer(char *filename, char *cmdExec, flag_type_resources flag, uint32 hash, int line)
{
m_FileName = DuplicateString(filename);
m_CmdExec = (cmdExec[0] != '\0') ? DuplicateString(cmdExec) : NULL;
m_Duplicate = false;
m_Flag = flag;
m_FileHash = hash;
m_Line = line;
}