Full Unicode patch with RTL Support & BiDi logic.

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
This commit is contained in:
rtw1x1
2025-12-26 12:32:43 +00:00
parent d37607baa1
commit a955c50744
86 changed files with 4076 additions and 3839 deletions

View File

@@ -1,6 +1,8 @@
#include "StdAfx.h"
#include "CRC32.h"
#include <utf8.h>
static unsigned long CRCTable[256] =
{
0x00000000,0x77073096,0xEE0E612C,0x990951BA,0x076DC419,0x706AF48F,
@@ -147,65 +149,55 @@ DWORD GetHFILECRC32(HANDLE hFile)
dataOffset + mapSize, // low
NULL); // name
if (hFM)
{
LPVOID lpMapData = MapViewOfFile(hFM,
FILE_MAP_READ,
0,
dwFileMapStart,
dwMapViewSize);
dwRetCRC32=GetCRC32((const char*)lpMapData, dwFileSize);
{
LPVOID lpMapData = MapViewOfFile(hFM, FILE_MAP_READ, 0, dwFileMapStart, dwMapViewSize);
if (lpMapData)
{
//tw1x1: If MapViewOfFile returns null, crash risk on mapping failure
dwRetCRC32 = GetCRC32((const char*)lpMapData, dwFileSize);
UnmapViewOfFile(lpMapData);
}
CloseHandle(hFM);
}
return dwRetCRC32;
}
DWORD GetFileCRC32(const char* c_szFileName)
DWORD GetFileCRC32(const wchar_t* c_szFileName)
{
HANDLE hFile = CreateFile(c_szFileName, // name of the file
GENERIC_READ, // desired access
FILE_SHARE_READ, // share mode
NULL, // security attributes
OPEN_EXISTING, // creation disposition
FILE_ATTRIBUTE_NORMAL, // flags and attr
NULL); // template file
if (INVALID_HANDLE_VALUE == hFile)
if (!c_szFileName || !*c_szFileName)
return 0;
DWORD dwRetCRC32=GetHFILECRC32(hFile);
CloseHandle(hFile);
HANDLE hFile = CreateFileW(c_szFileName, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
if (hFile == INVALID_HANDLE_VALUE)
return 0;
DWORD dwRetCRC32 = GetHFILECRC32(hFile);
CloseHandle(hFile);
return dwRetCRC32;
}
DWORD GetFileCRC32(const char* fileUtf8)
{
if (!fileUtf8 || !*fileUtf8)
return 0;
std::wstring wFile = Utf8ToWide(fileUtf8);
return GetFileCRC32(wFile.c_str());
}
DWORD GetFileSize(const char* c_szFileName)
{
HANDLE hFile = CreateFile(c_szFileName, // name of the file
GENERIC_READ, // desired access
FILE_SHARE_READ, // share mode
NULL, // security attributes
OPEN_EXISTING, // creation disposition
FILE_ATTRIBUTE_NORMAL, // flags and attr
NULL); // template file
if (INVALID_HANDLE_VALUE == hFile)
if (!c_szFileName || !*c_szFileName)
return 0;
DWORD dwSize = GetFileSize(hFile, NULL);
std::wstring wFile = Utf8ToWide(std::string(c_szFileName));
HANDLE hFile = CreateFileW(wFile.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
if (hFile == INVALID_HANDLE_VALUE)
return 0;
DWORD dwSize = ::GetFileSize(hFile, nullptr);
CloseHandle(hFile);
return dwSize;
}

View File

@@ -6,7 +6,8 @@
DWORD GetCRC32(const char* buffer, size_t count);
DWORD GetCaseCRC32(const char * buf, size_t len);
DWORD GetHFILECRC32(HANDLE hFile);
DWORD GetFileCRC32(const char* c_szFileName);
DWORD GetFileCRC32(const wchar_t* c_szFileName);
DWORD GetFileCRC32(const char* fileUtf8);
DWORD GetFileSize(const char* c_szFileName);
#endif

View File

@@ -6,6 +6,7 @@
#include "Singleton.h"
#include "Timer.h"
#include <filesystem>
#include <utf8.h>
const DWORD DEBUG_STRING_MAX_LEN = 1024;
@@ -281,13 +282,18 @@ void LogBoxf(const char* c_szFormat, ...)
LogBox(szBuf);
}
void LogBox(const char* c_szMsg, const char * c_szCaption, HWND hWnd)
void LogBox(const char* c_szMsg, const char* c_szCaption, HWND hWnd)
{
if (!hWnd)
hWnd = g_PopupHwnd;
MessageBox(hWnd, c_szMsg, c_szCaption ? c_szCaption : "LOG", MB_OK);
Tracen(c_szMsg);
std::wstring wMsg = Utf8ToWide(c_szMsg ? c_szMsg : "");
std::wstring wCaption = Utf8ToWide(c_szCaption ? c_szCaption : "LOG");
MessageBoxW(hWnd, wMsg.c_str(), wCaption.c_str(), MB_OK);
// Logging stays UTF-8
Tracen(c_szMsg ? c_szMsg : "");
}
void LogFile(const char * c_szMsg)
@@ -312,7 +318,7 @@ void OpenLogFile(bool bUseLogFIle)
}
#ifndef _DISTRIBUTE
freopen("log/syserr.txt", "w", stderr);
_wfreopen(L"log/syserr.txt", L"w", stderr);
if (bUseLogFIle)
{
@@ -326,6 +332,6 @@ void OpenConsoleWindow()
{
AllocConsole();
freopen("CONOUT$", "a", stdout);
freopen("CONIN$", "r", stdin);
_wfreopen(L"CONOUT$", L"a", stdout);
_wfreopen(L"CONIN$", L"r", stdin);
}

View File

@@ -1,6 +1,8 @@
#include "StdAfx.h"
#include "FileBase.h"
#include <utf8.h>
CFileBase::CFileBase() : m_hFile(NULL), m_dwSize(0)
{
}
@@ -34,9 +36,12 @@ BOOL CFileBase::Create(const char* filename, EFileMode mode)
{
Destroy();
// Keep filename internally as UTF-8 (engine side)
strncpy(m_filename, filename, MAX_PATH);
m_filename[MAX_PATH - 1] = '\0';
DWORD dwMode, dwShareMode = FILE_SHARE_READ;
DWORD dwMode;
DWORD dwShareMode = FILE_SHARE_READ;
if (mode == FILEMODE_WRITE)
{
@@ -44,19 +49,26 @@ BOOL CFileBase::Create(const char* filename, EFileMode mode)
dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
}
else
{
dwMode = GENERIC_READ;
}
m_hFile = CreateFile(filename, // name of the file
dwMode, // desired access
dwShareMode, // share mode
NULL, // security attributes
mode == FILEMODE_READ ? OPEN_EXISTING : OPEN_ALWAYS, // creation disposition
FILE_ATTRIBUTE_NORMAL, // flags and attr
NULL); // template file
// UTF-8 -> UTF-16 conversion for WinAPI
std::wstring wFilename = Utf8ToWide(filename);
m_hFile = CreateFileW(
wFilename.c_str(), // UTF-16 path
dwMode,
dwShareMode,
nullptr,
(mode == FILEMODE_READ) ? OPEN_EXISTING : OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
nullptr
);
if (m_hFile != INVALID_HANDLE_VALUE)
{
m_dwSize = GetFileSize(m_hFile, NULL);
m_dwSize = GetFileSize(m_hFile, nullptr);
m_mode = mode;
return true;
}
@@ -64,7 +76,7 @@ BOOL CFileBase::Create(const char* filename, EFileMode mode)
/* char buf[256];
GetCurrentDirectory(256, buf);
DWORD dwErr = GetLastError();*/
m_hFile = NULL;
m_hFile = nullptr;
return false;
}
@@ -95,7 +107,7 @@ BOOL CFileBase::Write(const void* src, int bytes)
{
DWORD dwUseless;
BOOL ret = WriteFile(m_hFile, src, bytes, &dwUseless, NULL);
if (!ret)
return false;

View File

@@ -1,6 +1,7 @@
#include "StdAfx.h"
#include "FileDir.h"
#include <string>
#include <utf8.h>
CDir::CDir()
{
@@ -20,43 +21,46 @@ void CDir::Destroy()
Initialize();
}
bool CDir::Create(const char * c_szFilter, const char* c_szPath, BOOL bCheckedExtension)
bool CDir::Create(const char* c_szFilter, const char* c_szPath, BOOL bCheckedExtension)
{
Destroy();
std::string stPath = c_szPath;
if (stPath.length())
std::string stPath = c_szPath ? c_szPath : "";
if (!stPath.empty())
{
char end = stPath[stPath.length() - 1];
char end = stPath.back();
if (end != '\\')
stPath+='\\';
stPath += '\\';
}
std::string stQuery;
stQuery += stPath;
stQuery += "*.*";
m_wfd.dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
m_hFind = FindFirstFile(stQuery.c_str(), &m_wfd);
// Query: UTF-8 -> UTF-16 for WinAPI
std::string stQueryUtf8 = stPath + "*.*";
std::wstring stQueryW = Utf8ToWide(stQueryUtf8);
m_wfd.dwFileAttributes = 0;
m_wfd.dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
m_hFind = FindFirstFileW(stQueryW.c_str(), &m_wfd);
if (m_hFind == INVALID_HANDLE_VALUE)
return true;
do
{
if (*m_wfd.cFileName == '.')
// Convert filename to UTF-8 for existing logic/callbacks
std::string fileNameUtf8 = WideToUtf8(m_wfd.cFileName);
if (!fileNameUtf8.empty() && fileNameUtf8[0] == '.')
continue;
if (IsFolder())
if (IsFolder())
{
if (!OnFolder(c_szFilter, stPath.c_str(), m_wfd.cFileName))
if (!OnFolder(c_szFilter, stPath.c_str(), fileNameUtf8.c_str()))
return false;
}
else
{
const char * c_szExtension = strchr(m_wfd.cFileName, '.');
const char* c_szExtension = strchr(fileNameUtf8.c_str(), '.');
if (!c_szExtension)
continue;
@@ -65,27 +69,29 @@ bool CDir::Create(const char * c_szFilter, const char* c_szPath, BOOL bCheckedEx
// 그전에 전 프로젝트의 CDir을 사용하는 곳에서 Extension을 "wav", "gr2" 이런식으로 넣게끔 한다. - [levites]
if (bCheckedExtension)
{
std::string strFilter = c_szFilter;
int iPos = strFilter.find_first_of(';', 0);
std::string strFilter = c_szFilter ? c_szFilter : "";
int iPos = (int)strFilter.find_first_of(';', 0);
if (iPos > 0)
{
std::string strFirstFilter = std::string(c_szFilter).substr(0, iPos);
std::string strSecondFilter = std::string(c_szFilter).substr(iPos+1, strlen(c_szFilter));
if (0 != strFirstFilter.compare(c_szExtension+1) && 0 != strSecondFilter.compare(c_szExtension+1))
std::string first = strFilter.substr(0, iPos);
std::string second = strFilter.substr(iPos + 1);
if (0 != first.compare(c_szExtension + 1) &&
0 != second.compare(c_szExtension + 1))
continue;
}
else
{
if (0 != stricmp(c_szExtension+1, c_szFilter))
if (0 != _stricmp(c_szExtension + 1, c_szFilter))
continue;
}
}
if (!OnFile(stPath.c_str(), m_wfd.cFileName))
if (!OnFile(stPath.c_str(), fileNameUtf8.c_str()))
return false;
}
}
while (FindNextFile(m_hFind, &m_wfd));
} while (FindNextFileW(m_hFind, &m_wfd));
return true;
}
@@ -94,12 +100,12 @@ bool CDir::IsFolder()
{
if (m_wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
return true;
return false;
}
}
void CDir::Initialize()
{
memset(&m_wfd, 0, sizeof(m_wfd));
m_hFind = NULL;
m_hFind = NULL;
}

View File

@@ -1,6 +1,7 @@
#include "StdAfx.h"
#include "FileLoader.h"
#include <assert.h>
#include <utf8.h>
CMemoryTextFileLoader::CMemoryTextFileLoader()
{
@@ -252,7 +253,9 @@ bool CDiskFileLoader::Open(const char* c_szFileName)
if (!c_szFileName[0])
return false;
m_fp = fopen(c_szFileName, "rb");
// UTF-8 → UTF-16 conversion for Unicode path support
std::wstring wFileName = Utf8ToWide(c_szFileName);
m_fp = _wfopen(wFileName.c_str(), L"rb");
if (!m_fp)
return false;

View File

@@ -2,11 +2,17 @@
#include "TempFile.h"
#include "Utils.h"
#include "Debug.h"
#include <utf8.h>
CTempFile::~CTempFile()
{
Destroy();
DeleteFile(m_szFileName);
if (m_szFileName[0])
{
std::wstring wPath = Utf8ToWide(m_szFileName);
DeleteFileW(wPath.c_str());
}
}
CTempFile::CTempFile(const char * c_pszPrefix)

View File

@@ -5,24 +5,43 @@
#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)
const char* CreateTempFileName(const char* c_pszPrefix)
{
char szTempPath[MAX_PATH + 1];
static char szTempName[MAX_PATH + 1];
static std::string s_utf8TempName; // safe static storage
GetTempPath(MAX_PATH, szTempPath);
wchar_t wTempPath[MAX_PATH + 1]{};
wchar_t wTempName[MAX_PATH + 1]{};
GetTempFileName(szTempPath, // directory for temp files
c_pszPrefix ? c_pszPrefix : "etb", // temp file name prefix
c_pszPrefix ? true : false, // create unique name
szTempName); // buffer for name
// Get temp directory
if (!GetTempPathW(MAX_PATH, wTempPath))
return "";
return (szTempName);
// 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)
@@ -41,10 +60,10 @@ void GetFilePathNameExtension(const char * c_szFile, int len, std::string * pstP
if (ext == len && c == '.')
{
ext = pos;
ext = pos;
break;
}
if (c == '/' || c == '\\')
break;
}
@@ -83,7 +102,7 @@ void GetFileExtension(const char* c_szFile, int len, std::string* pstExt)
char c=c_szFile[pos];
if (ext==len && c=='.')
{
ext=pos;
ext=pos;
break;
}
@@ -91,7 +110,7 @@ void GetFileExtension(const char* c_szFile, int len, std::string* pstExt)
else if (c=='\\') break;
}
++ext;
++ext;
if (len>ext)
pstExt->append(c_szFile+ext, len-ext);
}
@@ -110,7 +129,7 @@ void GetFileNameParts(const char* c_szFile, int len, char* pszPath, char* pszNam
char c=c_szFile[pos];
if (ext==len && c=='.')
{
ext=pos;
ext=pos;
break;
}
@@ -127,7 +146,7 @@ void GetFileNameParts(const char* c_szFile, int len, char* pszPath, char* pszNam
else if (c=='\\') break;
}
if (pos)
if (pos)
{
++pos;
for (int i = 0; i < pos; ++i)
@@ -147,7 +166,7 @@ void GetFileNameParts(const char* c_szFile, int len, char* pszPath, char* pszNam
pszName[count] = '\0';
}
++ext;
++ext;
if (len > ext)
{
int count = 0;
@@ -163,10 +182,10 @@ 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);
}
@@ -423,38 +442,40 @@ bool IsGlobalFileName(const char * c_szFileName)
return strchr(c_szFileName, ':') != NULL;
}
void MyCreateDirectory(const char* path)
void MyCreateDirectory(const char* pathUtf8)
{
if (!path || !*path)
if (!pathUtf8 || !*pathUtf8)
return;
char * dir;
const char * p;
// Skip drive letter (C:\)
const char* path = pathUtf8;
if (strlen(path) >= 3 && path[1] == ':')
path += 3;
if (strlen(path) >= 3)
{
if (*(path + 1) == ':') // C:, D: 같은 경우를 체크
path += 3;
}
size_t len = strlen(pathUtf8) + 1;
char* dirUtf8 = new char[len];
p = path;
int len = strlen(path) + 1;
dir = new char[len];
const char* p = path;
while (*p)
{
if (*p == '/' || *p == '\\')
{
memset(dir, 0, len);
strncpy(dir, path, p - path);
CreateDirectory(dir, NULL);
}
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;
}
delete [] dir;
// Create final directory too
std::wstring wFinal = Utf8ToWide(pathUtf8);
CreateDirectoryW(wFinal.c_str(), nullptr);
delete[] dirUtf8;
}
class CDirRemover : public CDir
@@ -484,22 +505,31 @@ class CDirRemover : public CDir
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;
_chmod(strFullPathName.c_str(), _S_IWRITE);
DeleteFile(strFullPathName.c_str());
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)
for (std::deque<std::string>::iterator itor = ms_strDirectoryDeque.begin();
itor != ms_strDirectoryDeque.end(); ++itor)
{
const std::string & c_rstrDirectory = *itor;
RemoveDirectory(c_rstrDirectory.c_str());
const std::string& dirUtf8 = *itor;
std::wstring wDir = Utf8ToWide(dirUtf8);
RemoveDirectoryW(wDir.c_str());
}
ms_strDirectoryDeque.clear();
@@ -511,14 +541,19 @@ class CDirRemover : public CDir
std::deque<std::string> CDirRemover::ms_strDirectoryDeque;
void RemoveAllDirectory(const char * c_szDirectoryName)
void RemoveAllDirectory(const char* c_szDirectoryName)
{
{
CDirRemover remover;
remover.Create("*.*", c_szDirectoryName);
CDirRemover::RemoveAllDirectory();
}
RemoveDirectory(c_szDirectoryName);
if (c_szDirectoryName && *c_szDirectoryName)
{
std::wstring wDir = Utf8ToWide(c_szDirectoryName);
RemoveDirectoryW(wDir.c_str());
}
}
void StringExceptCharacter(std::string * pstrString, const char * c_szCharacter)
@@ -573,14 +608,15 @@ bool SplitLine(const char * c_szLine, const char * c_szDelimeter, std::vector<st
return true;
}
void GetExcutedFileName(std::string & r_str)
void GetExcutedFileName(std::string& r_str)
{
char szPath[MAX_PATH+1];
wchar_t wPath[MAX_PATH + 1]{};
GetModuleFileName(NULL, szPath, MAX_PATH);
szPath[MAX_PATH] = '\0';
GetModuleFileNameW(nullptr, wPath, MAX_PATH);
wPath[MAX_PATH] = L'\0';
r_str = szPath;
// Convert UTF-16 → UTF-8 for engine use
r_str = WideToUtf8(wPath);
}
const char * _getf(const char* c_szFormat, ...)

View File

@@ -4,6 +4,7 @@
#include <time.h>
#include <winsock.h>
#include <imagehlp.h>
#include <utf8.h>
FILE* fException;
@@ -47,15 +48,18 @@ LONG __stdcall EterExceptionFilter(_EXCEPTION_POINTERS* pExceptionInfo)
fException = fopen("log/ErrorLog.txt", "wt");
if (fException)
{
char module_name[256];
wchar_t wModuleName[MAX_PATH]{};
time_t module_time;
HMODULE hModule = GetModuleHandle(NULL);
HMODULE hModule = GetModuleHandleW(nullptr);
GetModuleFileName(hModule, module_name, sizeof(module_name));
GetModuleFileNameW(hModule, wModuleName, MAX_PATH);
module_time = (time_t)GetTimestampForLoadedLibrary(hModule);
fprintf(fException, "Module Name: %s\n", module_name);
// Convert once for logging
std::string moduleNameUtf8 = WideToUtf8(wModuleName);
fprintf(fException, "Module Name: %s\n", moduleNameUtf8.c_str());
fprintf(fException, "Time Stamp: 0x%08x - %s\n", (unsigned int)module_time, ctime(&module_time));
fprintf(fException, "\n");
fprintf(fException, "Exception Type: 0x%08x\n", pExceptionInfo->ExceptionRecord->ExceptionCode);