forked from metin-server/m2dev-client-src
This commit is well documented, so no need to tell you my life story. Full Unicode patch with RTL Support & BiDi logic. Removed the legacy codePage, normalised to UTF8 (65001). It also comes with: CTRL + A : select text (highlighted) CTRL + C : copy CTRL + V : paste CTRL + X : cut CTRL + Y : redo CTRL + Z : undo
713 lines
13 KiB
C++
713 lines
13 KiB
C++
#include "StdAfx.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <direct.h>
|
|
#include <io.h>
|
|
#include <assert.h>
|
|
#include <sys/stat.h>
|
|
#include <utf8.h>
|
|
|
|
#include "Utils.h"
|
|
#include "filedir.h"
|
|
|
|
char korean_tolower(const char c);
|
|
|
|
const char* CreateTempFileName(const char* c_pszPrefix)
|
|
{
|
|
static std::string s_utf8TempName; // safe static storage
|
|
|
|
wchar_t wTempPath[MAX_PATH + 1]{};
|
|
wchar_t wTempName[MAX_PATH + 1]{};
|
|
|
|
// Get temp directory
|
|
if (!GetTempPathW(MAX_PATH, wTempPath))
|
|
return "";
|
|
|
|
// Prefix must be wide
|
|
wchar_t wPrefix[4] = L"etb";
|
|
if (c_pszPrefix && *c_pszPrefix)
|
|
{
|
|
std::wstring wp = Utf8ToWide(c_pszPrefix);
|
|
wcsncpy_s(wPrefix, wp.c_str(), 3);
|
|
}
|
|
|
|
// Create temp file name
|
|
if (!GetTempFileNameW(
|
|
wTempPath,
|
|
wPrefix,
|
|
0, // unique number generated by system
|
|
wTempName))
|
|
return "";
|
|
|
|
// Convert result to UTF-8 for engine
|
|
s_utf8TempName = WideToUtf8(wTempName);
|
|
return s_utf8TempName.c_str();
|
|
}
|
|
|
|
void GetFilePathNameExtension(const char * c_szFile, int len, std::string * pstPath, std::string * pstName, std::string * pstExt)
|
|
{
|
|
assert(pstPath != NULL);
|
|
assert(pstName != NULL);
|
|
assert(pstExt != NULL);
|
|
|
|
int ext = len;
|
|
int pos = len;
|
|
|
|
while (pos > 0)
|
|
{
|
|
--pos;
|
|
char c = c_szFile[pos];
|
|
|
|
if (ext == len && c == '.')
|
|
{
|
|
ext = pos;
|
|
break;
|
|
}
|
|
|
|
if (c == '/' || c == '\\')
|
|
break;
|
|
}
|
|
|
|
while (pos > 0)
|
|
{
|
|
--pos;
|
|
char c = c_szFile[pos];
|
|
|
|
if (c == '/' || c == '\\')
|
|
break;
|
|
}
|
|
|
|
if (pos)
|
|
{
|
|
++pos;
|
|
pstPath->append(c_szFile, pos);
|
|
}
|
|
|
|
if (ext > pos)
|
|
pstName->append(c_szFile + pos, ext - pos);
|
|
|
|
++ext;
|
|
|
|
if (len > ext)
|
|
pstExt->append(c_szFile + ext, len - ext);
|
|
}
|
|
|
|
void GetFileExtension(const char* c_szFile, int len, std::string* pstExt)
|
|
{
|
|
int ext = len;
|
|
int pos = len;
|
|
while (pos > 0)
|
|
{
|
|
--pos;
|
|
char c=c_szFile[pos];
|
|
if (ext==len && c=='.')
|
|
{
|
|
ext=pos;
|
|
break;
|
|
}
|
|
|
|
if (c=='/') break;
|
|
else if (c=='\\') break;
|
|
}
|
|
|
|
++ext;
|
|
if (len>ext)
|
|
pstExt->append(c_szFile+ext, len-ext);
|
|
}
|
|
|
|
void GetFileNameParts(const char* c_szFile, int len, char* pszPath, char* pszName, char* pszExt)
|
|
{
|
|
assert(pszPath!=NULL);
|
|
assert(pszName!=NULL);
|
|
assert(pszExt!=NULL);
|
|
|
|
int ext=len;
|
|
int pos=len;
|
|
while (pos>0)
|
|
{
|
|
--pos;
|
|
char c=c_szFile[pos];
|
|
if (ext==len && c=='.')
|
|
{
|
|
ext=pos;
|
|
break;
|
|
}
|
|
|
|
if (c=='/') break;
|
|
else if (c=='\\') break;
|
|
}
|
|
|
|
while (pos>0)
|
|
{
|
|
--pos;
|
|
char c=c_szFile[pos];
|
|
|
|
if (c=='/') break;
|
|
else if (c=='\\') break;
|
|
}
|
|
|
|
if (pos)
|
|
{
|
|
++pos;
|
|
for (int i = 0; i < pos; ++i)
|
|
{
|
|
pszPath[i] = c_szFile[i];
|
|
}
|
|
pszPath[pos] = '\0';
|
|
}
|
|
|
|
if (ext>pos)
|
|
{
|
|
int count = 0;
|
|
for (int i = pos; i < ext; ++i)
|
|
{
|
|
pszName[count++] = c_szFile[i];
|
|
}
|
|
pszName[count] = '\0';
|
|
}
|
|
|
|
++ext;
|
|
if (len > ext)
|
|
{
|
|
int count = 0;
|
|
for (int i = ext; i < len; ++i)
|
|
{
|
|
pszExt[count++] = c_szFile[i];
|
|
}
|
|
pszExt[count] = '\0';
|
|
}
|
|
}
|
|
|
|
void GetOldIndexingName(char * szName, int Index)
|
|
{
|
|
int dec, sign;
|
|
char Temp[512];
|
|
|
|
strcpy(Temp, _ecvt(Index, 256, &dec, &sign));
|
|
Temp[dec] = '\0';
|
|
|
|
strcat(szName, Temp);
|
|
}
|
|
|
|
void GetIndexingName(char * szName, DWORD Index)
|
|
{
|
|
sprintf(szName + strlen(szName), "%u", Index);
|
|
}
|
|
|
|
void GetOnlyFileName(const char * sz_Name, std::string & strFileName)
|
|
{
|
|
strFileName = "";
|
|
|
|
int i;
|
|
for (i=strlen(sz_Name)-1; i>=0; --i)
|
|
{
|
|
if ('\\' == sz_Name[i] || '/' == sz_Name[i])
|
|
{
|
|
++i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == -1)
|
|
i = 0;
|
|
|
|
for (size_t j = i; j < strlen(sz_Name); ++j)
|
|
{
|
|
strFileName += sz_Name[j];
|
|
}
|
|
|
|
strFileName += "\0";
|
|
}
|
|
|
|
void GetExceptionPathName(const char * sz_Name, std::string & OnlyFileName)
|
|
{
|
|
GetOnlyFileName(sz_Name, OnlyFileName);
|
|
}
|
|
|
|
void GetOnlyPathName(const char * sz_Name, std::string & OnlyPathName)
|
|
{
|
|
int i;
|
|
for (i = strlen(sz_Name) - 1; i >= 0; --i)
|
|
{
|
|
if ('\\' == sz_Name[i] || '/' == sz_Name[i])
|
|
{
|
|
++i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == -1)
|
|
i = 0;
|
|
|
|
OnlyPathName.reserve(strlen(sz_Name));
|
|
OnlyPathName = "";
|
|
|
|
for (int j=0; j<i; ++j)
|
|
{
|
|
OnlyPathName += sz_Name[j];
|
|
}
|
|
OnlyPathName += "\0";
|
|
}
|
|
|
|
const char * GetOnlyPathName(const char * c_szName)
|
|
{
|
|
static std::string strPathName;
|
|
GetOnlyPathName(c_szName, strPathName);
|
|
|
|
return strPathName.c_str();
|
|
}
|
|
|
|
bool GetLocalFileName(const char * c_szGlobalPath, const char * c_szFullPathFileName, std::string * pstrLocalFileName)
|
|
{
|
|
std::string strLocalFileName;
|
|
|
|
std::string strGlobalPath;
|
|
std::string strFullPathFileName;
|
|
StringPath(c_szGlobalPath, strGlobalPath);
|
|
StringPath(c_szFullPathFileName, strFullPathFileName);
|
|
|
|
if (strGlobalPath.length() >= strFullPathFileName.length())
|
|
return false;
|
|
|
|
DWORD length = std::min(strGlobalPath.length(), strFullPathFileName.length());
|
|
for (DWORD dwPos = 0; dwPos < length; ++dwPos)
|
|
{
|
|
if (strGlobalPath[dwPos] != strFullPathFileName[dwPos])
|
|
return false;
|
|
}
|
|
|
|
*pstrLocalFileName = &c_szFullPathFileName[length];
|
|
|
|
return true;
|
|
}
|
|
|
|
void GetWorkingFolder(std::string & strFileName)
|
|
{
|
|
char buf[128+1];
|
|
_getcwd(buf, 128);
|
|
strcat(buf, "/");
|
|
strFileName = buf;
|
|
}
|
|
|
|
void StringLowers(char * String)
|
|
{
|
|
for (DWORD i = 0; i < strlen(String); ++i)
|
|
{
|
|
String[i] = korean_tolower(String[i]);
|
|
}
|
|
}
|
|
|
|
void StringPath(std::string & rString)
|
|
{
|
|
for (DWORD i = 0; i < rString.length(); ++i)
|
|
{
|
|
if (rString[i] == '\\')
|
|
rString[i] = '/';
|
|
else
|
|
rString[i] = korean_tolower(rString[i]);
|
|
}
|
|
}
|
|
|
|
void StringPath(char * pString)
|
|
{
|
|
for (DWORD i = 0; i < strlen(pString); ++i)
|
|
{
|
|
if (pString[i] == '\\')
|
|
pString[i] = '/';
|
|
else
|
|
pString[i] = korean_tolower(pString[i]);
|
|
}
|
|
}
|
|
|
|
void StringPath(const char * c_szSrc, char * szDest)
|
|
{
|
|
for (DWORD i = 0; i < strlen(c_szSrc); ++i)
|
|
{
|
|
if (c_szSrc[i] == '\\')
|
|
szDest[i] = '/';
|
|
else
|
|
szDest[i] = korean_tolower(c_szSrc[i]);
|
|
}
|
|
}
|
|
|
|
void StringPath(const char * c_szSrc, std::string & rString)
|
|
{
|
|
rString = "";
|
|
rString.resize(strlen(c_szSrc));
|
|
|
|
for (DWORD i = 0; i < strlen(c_szSrc); ++i)
|
|
{
|
|
if (c_szSrc[i] == '\\')
|
|
rString[i] = '/';
|
|
else
|
|
rString[i] = korean_tolower(c_szSrc[i]);
|
|
}
|
|
}
|
|
|
|
#define ishprint(x) ((((x) & 0xE0) > 0x90) || isprint(x))
|
|
|
|
void PrintAsciiData(const void* void_data, int bytes)
|
|
{
|
|
int i, j, k;
|
|
const unsigned char* p;
|
|
const unsigned char* data;
|
|
|
|
data = (const unsigned char*) void_data;
|
|
|
|
fprintf(stdout, "------------------------------------------------------------------\n");
|
|
j = bytes;
|
|
while (1)
|
|
{
|
|
k = j >= 16 ? 16 : j;
|
|
|
|
p = data;
|
|
for (i = 0; i < 16; ++i)
|
|
{
|
|
if (i >= k)
|
|
fprintf(stdout, " ");
|
|
else
|
|
fprintf(stdout, "%02x ", *p);
|
|
p++;
|
|
}
|
|
|
|
fprintf(stdout, "| ");
|
|
|
|
p = data;
|
|
for (i = 0; i < k; ++i)
|
|
{
|
|
if (i >= k)
|
|
fprintf(stdout, " ");
|
|
else
|
|
fprintf(stdout, "%c", ishprint(*p) ? *p : '.');
|
|
p++;
|
|
}
|
|
|
|
fprintf(stdout, "\n");
|
|
|
|
j -= 16;
|
|
data += 16;
|
|
|
|
if (j <= 0)
|
|
break;
|
|
}
|
|
|
|
fprintf(stdout, "------------------------------------------------------------------\n");
|
|
}
|
|
|
|
int MIN(int a, int b)
|
|
{
|
|
return a < b ? a : b;
|
|
}
|
|
|
|
int MAX(int a, int b)
|
|
{
|
|
return a > b ? a : b;
|
|
}
|
|
|
|
int MINMAX(int min, int value, int max)
|
|
{
|
|
if (max < min)
|
|
return MAX(min, value);
|
|
|
|
int tv;
|
|
tv = (min > value ? min : value);
|
|
return (max < tv) ? max : tv;
|
|
}
|
|
|
|
float fMIN(float a, float b)
|
|
{
|
|
return a < b ? a : b;
|
|
}
|
|
|
|
float fMAX(float a, float b)
|
|
{
|
|
return a > b ? a : b;
|
|
}
|
|
|
|
float fMINMAX(float min, float value, float max)
|
|
{
|
|
float tv;
|
|
|
|
tv = (min > value ? min : value);
|
|
return (max < tv) ? max : tv;
|
|
}
|
|
|
|
bool IsFile(const char* filename)
|
|
{
|
|
return _access(filename, 0) == 0 ? true : false;
|
|
}
|
|
|
|
bool IsGlobalFileName(const char * c_szFileName)
|
|
{
|
|
return strchr(c_szFileName, ':') != NULL;
|
|
}
|
|
|
|
void MyCreateDirectory(const char* pathUtf8)
|
|
{
|
|
if (!pathUtf8 || !*pathUtf8)
|
|
return;
|
|
|
|
// Skip drive letter (C:\)
|
|
const char* path = pathUtf8;
|
|
if (strlen(path) >= 3 && path[1] == ':')
|
|
path += 3;
|
|
|
|
size_t len = strlen(pathUtf8) + 1;
|
|
char* dirUtf8 = new char[len];
|
|
|
|
const char* p = path;
|
|
|
|
while (*p)
|
|
{
|
|
if (*p == '/' || *p == '\\')
|
|
{
|
|
memset(dirUtf8, 0, len);
|
|
strncpy(dirUtf8, pathUtf8, p - path + (path - pathUtf8));
|
|
|
|
// UTF-8 → UTF-16 for WinAPI
|
|
std::wstring wDir = Utf8ToWide(dirUtf8);
|
|
CreateDirectoryW(wDir.c_str(), nullptr);
|
|
}
|
|
++p;
|
|
}
|
|
|
|
// Create final directory too
|
|
std::wstring wFinal = Utf8ToWide(pathUtf8);
|
|
CreateDirectoryW(wFinal.c_str(), nullptr);
|
|
|
|
delete[] dirUtf8;
|
|
}
|
|
|
|
class CDirRemover : public CDir
|
|
{
|
|
public:
|
|
CDirRemover()
|
|
{
|
|
}
|
|
virtual ~CDirRemover()
|
|
{
|
|
}
|
|
|
|
bool OnFolder(const char* c_szFilter, const char* c_szPathName, const char* c_szFileName)
|
|
{
|
|
std::string strFullPathName;
|
|
strFullPathName = c_szPathName;
|
|
strFullPathName += c_szFileName;
|
|
|
|
CDirRemover remover;
|
|
remover.Create(c_szFilter, strFullPathName.c_str());
|
|
|
|
std::string strWorkingFolder;
|
|
GetWorkingFolder(strWorkingFolder);
|
|
strWorkingFolder += strFullPathName;
|
|
strWorkingFolder += "/";
|
|
StringPath(strWorkingFolder);
|
|
ms_strDirectoryDeque.push_back(strWorkingFolder);
|
|
return true;
|
|
}
|
|
|
|
bool OnFile(const char* c_szPathName, const char* c_szFileName)
|
|
{
|
|
std::string strFullPathName;
|
|
strFullPathName = c_szPathName;
|
|
strFullPathName += c_szFileName;
|
|
|
|
std::wstring wFullPath = Utf8ToWide(strFullPathName);
|
|
|
|
// Make writable (use wide version)
|
|
_wchmod(wFullPath.c_str(), _S_IWRITE);
|
|
|
|
// Delete (use wide WinAPI)
|
|
DeleteFileW(wFullPath.c_str());
|
|
return true;
|
|
}
|
|
|
|
static void RemoveAllDirectory()
|
|
{
|
|
for (std::deque<std::string>::iterator itor = ms_strDirectoryDeque.begin();
|
|
itor != ms_strDirectoryDeque.end(); ++itor)
|
|
{
|
|
const std::string& dirUtf8 = *itor;
|
|
std::wstring wDir = Utf8ToWide(dirUtf8);
|
|
RemoveDirectoryW(wDir.c_str());
|
|
}
|
|
|
|
ms_strDirectoryDeque.clear();
|
|
}
|
|
|
|
protected:
|
|
static std::deque<std::string> ms_strDirectoryDeque;
|
|
};
|
|
|
|
std::deque<std::string> CDirRemover::ms_strDirectoryDeque;
|
|
|
|
void RemoveAllDirectory(const char* c_szDirectoryName)
|
|
{
|
|
{
|
|
CDirRemover remover;
|
|
remover.Create("*.*", c_szDirectoryName);
|
|
CDirRemover::RemoveAllDirectory();
|
|
}
|
|
|
|
if (c_szDirectoryName && *c_szDirectoryName)
|
|
{
|
|
std::wstring wDir = Utf8ToWide(c_szDirectoryName);
|
|
RemoveDirectoryW(wDir.c_str());
|
|
}
|
|
}
|
|
|
|
void StringExceptCharacter(std::string * pstrString, const char * c_szCharacter)
|
|
{
|
|
int icurPos = 0;
|
|
int iNextPos = 0;
|
|
|
|
while((iNextPos = pstrString->find_first_of(c_szCharacter, icurPos)) >= 0)
|
|
{
|
|
std::string strFront = pstrString->substr(icurPos, iNextPos - icurPos);
|
|
std::string strBack = pstrString->substr(iNextPos+1, pstrString->length() - iNextPos - 1);
|
|
*pstrString = strFront + strBack;
|
|
}
|
|
}
|
|
|
|
bool SplitLine(const char * c_szLine, const char * c_szDelimeter, std::vector<std::string> * pkVec_strToken)
|
|
{
|
|
pkVec_strToken->reserve(10);
|
|
pkVec_strToken->clear();
|
|
|
|
std::string strLine = c_szLine;
|
|
|
|
DWORD basePos = 0;
|
|
|
|
do
|
|
{
|
|
int beginPos = strLine.find_first_not_of(c_szDelimeter, basePos);
|
|
if (beginPos < 0)
|
|
return false;
|
|
|
|
int endPos;
|
|
|
|
if (strLine[beginPos] == '"')
|
|
{
|
|
++beginPos;
|
|
endPos = strLine.find_first_of("\"", beginPos);
|
|
|
|
if (endPos < 0)
|
|
return false;
|
|
|
|
basePos = endPos + 1;
|
|
}
|
|
else
|
|
{
|
|
endPos = strLine.find_first_of(c_szDelimeter, beginPos);
|
|
basePos = endPos;
|
|
}
|
|
|
|
pkVec_strToken->push_back(strLine.substr(beginPos, endPos - beginPos));
|
|
} while (basePos < strLine.length());
|
|
|
|
return true;
|
|
}
|
|
|
|
void GetExcutedFileName(std::string& r_str)
|
|
{
|
|
wchar_t wPath[MAX_PATH + 1]{};
|
|
|
|
GetModuleFileNameW(nullptr, wPath, MAX_PATH);
|
|
wPath[MAX_PATH] = L'\0';
|
|
|
|
// Convert UTF-16 → UTF-8 for engine use
|
|
r_str = WideToUtf8(wPath);
|
|
}
|
|
|
|
const char * _getf(const char* c_szFormat, ...)
|
|
{
|
|
static char szBuf[256];
|
|
|
|
va_list args;
|
|
va_start(args, c_szFormat);
|
|
_vsnprintf(szBuf, sizeof(szBuf), c_szFormat, args);
|
|
va_end(args);
|
|
|
|
return szBuf;
|
|
}
|
|
|
|
PCHAR* CommandLineToArgv( PCHAR CmdLine, int* _argc )
|
|
{
|
|
PCHAR* argv;
|
|
PCHAR _argv;
|
|
ULONG len;
|
|
ULONG argc;
|
|
CHAR a;
|
|
ULONG i, j;
|
|
|
|
BOOLEAN in_QM;
|
|
BOOLEAN in_TEXT;
|
|
BOOLEAN in_SPACE;
|
|
|
|
len = strlen(CmdLine);
|
|
i = ((len+2)/2)*sizeof(PVOID) + sizeof(PVOID);
|
|
|
|
argv = (PCHAR*)GlobalAlloc(GMEM_FIXED,
|
|
i + (len+2)*sizeof(CHAR));
|
|
|
|
_argv = (PCHAR)(((PUCHAR)argv)+i);
|
|
|
|
argc = 0;
|
|
argv[argc] = _argv;
|
|
in_QM = FALSE;
|
|
in_TEXT = FALSE;
|
|
in_SPACE = TRUE;
|
|
i = 0;
|
|
j = 0;
|
|
|
|
while( a = CmdLine[i] ) {
|
|
if(in_QM) {
|
|
if(a == '\"') {
|
|
in_QM = FALSE;
|
|
} else {
|
|
_argv[j] = a;
|
|
j++;
|
|
}
|
|
} else {
|
|
switch(a) {
|
|
case '\"':
|
|
in_QM = TRUE;
|
|
in_TEXT = TRUE;
|
|
if(in_SPACE) {
|
|
argv[argc] = _argv+j;
|
|
argc++;
|
|
}
|
|
in_SPACE = FALSE;
|
|
break;
|
|
case ' ':
|
|
case '\t':
|
|
case '\n':
|
|
case '\r':
|
|
if(in_TEXT) {
|
|
_argv[j] = '\0';
|
|
j++;
|
|
}
|
|
in_TEXT = FALSE;
|
|
in_SPACE = TRUE;
|
|
break;
|
|
default:
|
|
in_TEXT = TRUE;
|
|
if(in_SPACE) {
|
|
argv[argc] = _argv+j;
|
|
argc++;
|
|
}
|
|
_argv[j] = a;
|
|
j++;
|
|
in_SPACE = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
i++;
|
|
}
|
|
_argv[j] = '\0';
|
|
argv[argc] = NULL;
|
|
|
|
(*_argc) = argc;
|
|
return argv;
|
|
}
|