From a955c5074460e4df8ffa92b1fc278b05dad12f86 Mon Sep 17 00:00:00 2001 From: rtw1x1 Date: Fri, 26 Dec 2025 12:32:43 +0000 Subject: [PATCH 01/12] 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 --- CMakeLists.txt | 4 + extern/include/utf8.h | 629 +++++++++ src/AudioLib/Type.cpp | 5 +- src/Discord/discord_register_win.cpp | 21 +- src/EterBase/CRC32.cpp | 66 +- src/EterBase/CRC32.h | 3 +- src/EterBase/Debug.cpp | 18 +- src/EterBase/FileBase.cpp | 34 +- src/EterBase/FileDir.cpp | 64 +- src/EterBase/FileLoader.cpp | 5 +- src/EterBase/TempFile.cpp | 8 +- src/EterBase/Utils.cpp | 134 +- src/EterBase/error.cpp | 12 +- src/EterLib/GrpDIB.cpp | 14 +- src/EterLib/GrpDevice.cpp | 37 +- src/EterLib/GrpFontTexture.cpp | 113 +- src/EterLib/GrpFontTexture.h | 98 +- src/EterLib/GrpPixelShader.cpp | 51 +- src/EterLib/GrpScreen.cpp | 8 +- src/EterLib/GrpTextInstance.cpp | 1194 +++++++++++------ src/EterLib/GrpTextInstance.h | 32 +- src/EterLib/GrpVertexShader.cpp | 49 +- src/EterLib/IME.cpp | 1185 +++++++++------- src/EterLib/IME.h | 334 ++--- src/EterLib/MSWindow.cpp | 96 +- src/EterLib/MSWindow.h | 4 +- src/EterLib/TextBar.cpp | 62 +- src/EterLib/TextTag.cpp | 28 +- src/EterLib/Util.cpp | 249 ---- src/EterLib/Util.h | 13 - src/EterLib/parser.cpp | 309 ++--- src/EterLib/parser.h | 98 +- src/EterLocale/Arabic.cpp | 6 +- src/EterLocale/CodePageId.h | 45 +- src/EterPythonLib/PythonGraphic.cpp | 8 +- src/EterPythonLib/PythonGraphicModule.cpp | 57 +- src/EterPythonLib/PythonWindow.cpp | 71 +- src/EterPythonLib/PythonWindow.h | 5 +- src/EterPythonLib/PythonWindowManager.cpp | 9 - src/EterPythonLib/PythonWindowManager.h | 1 - .../PythonWindowManagerModule.cpp | 18 + src/GameLib/ItemData.cpp | 56 +- src/GameLib/ItemData.h | 2 +- src/GameLib/ItemManager.cpp | 23 - src/GameLib/Property.cpp | 98 +- src/ScriptLib/PythonLauncher.cpp | 5 +- src/SpeedTreeLib/VertexShaders.h | 6 +- src/UserInterface/AbstractApplication.h | 1 - src/UserInterface/AccountConnector.cpp | 14 +- src/UserInterface/GuildMarkDownloader.cpp | 5 +- src/UserInterface/GuildMarkUploader.cpp | 3 +- src/UserInterface/InsultChecker.cpp | 16 +- src/UserInterface/Locale.cpp | 439 ++---- src/UserInterface/Locale.h | 35 +- src/UserInterface/Locale_inc.h | 1 - src/UserInterface/MovieMan.cpp | 12 +- src/UserInterface/ProcessCRC.cpp | 72 +- src/UserInterface/ProcessScanner.cpp | 96 +- src/UserInterface/PythonApplication.cpp | 179 +-- src/UserInterface/PythonApplication.h | 1 - src/UserInterface/PythonApplicationCamera.cpp | 12 +- src/UserInterface/PythonApplicationEvent.cpp | 5 - src/UserInterface/PythonApplicationLogo.cpp | 6 +- src/UserInterface/PythonApplicationModule.cpp | 191 +-- src/UserInterface/PythonBackground.cpp | 2 +- src/UserInterface/PythonChat.cpp | 88 +- src/UserInterface/PythonChat.h | 6 + src/UserInterface/PythonChatModule.cpp | 29 + src/UserInterface/PythonEventManager.cpp | 38 +- src/UserInterface/PythonGuild.cpp | 2 +- src/UserInterface/PythonIME.cpp | 122 +- src/UserInterface/PythonIME.h | 8 +- src/UserInterface/PythonIMEModule.cpp | 38 +- src/UserInterface/PythonMiniMap.cpp | 49 +- .../PythonNetworkStreamModule.cpp | 8 - .../PythonNetworkStreamPhaseGame.cpp | 68 +- .../PythonNetworkStreamPhaseLoading.cpp | 6 +- .../PythonNetworkStreamPhaseLogin.cpp | 19 +- src/UserInterface/PythonPlayer.cpp | 54 +- src/UserInterface/PythonPlayerModule.cpp | 51 +- src/UserInterface/PythonSkill.cpp | 112 +- src/UserInterface/PythonTextTail.cpp | 141 +- src/UserInterface/StdAfx.h | 19 +- src/UserInterface/Test.h | 3 - src/UserInterface/UserInterface.cpp | 424 +----- src/UserInterface/UserInterface.rc | 153 +-- 86 files changed, 4076 insertions(+), 3839 deletions(-) create mode 100644 extern/include/utf8.h delete mode 100644 src/UserInterface/Test.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 8002fae..4b4c958 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,6 +26,10 @@ set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /DEBUG") add_compile_options("$<$:/utf-8>") add_compile_options(/MP) +if(MSVC) + add_compile_definitions(UNICODE _UNICODE) +endif() + add_compile_options( $<$:/wd4828> $<$:/wd4996> diff --git a/extern/include/utf8.h b/extern/include/utf8.h new file mode 100644 index 0000000..1ed5eef --- /dev/null +++ b/extern/include/utf8.h @@ -0,0 +1,629 @@ +#pragma once +#include +#include +#include +#include +#include + +#include + +// ============================================================================ +// CONFIGURATION CONSTANTS +// ============================================================================ + +// Maximum text length for security/performance (prevent DoS attacks) +constexpr size_t MAX_TEXT_LENGTH = 65536; // 64KB of text +constexpr size_t MAX_CHAT_TEXT_LENGTH = 4096; // 4KB for chat messages + +// Arabic shaping buffer size calculations +constexpr size_t ARABIC_SHAPING_EXPANSION_FACTOR = 2; +constexpr size_t ARABIC_SHAPING_SAFETY_MARGIN = 16; +constexpr size_t ARABIC_SHAPING_EXPANSION_FACTOR_RETRY = 4; +constexpr size_t ARABIC_SHAPING_SAFETY_MARGIN_RETRY = 64; + +// ============================================================================ +// DEBUG LOGGING (Uncomment to enable BiDi debugging) +// ============================================================================ +// #define DEBUG_BIDI + +#ifdef DEBUG_BIDI + #include + #define BIDI_LOG(fmt, ...) printf("[BiDi] " fmt "\n", __VA_ARGS__) + #define BIDI_LOG_SIMPLE(msg) printf("[BiDi] %s\n", msg) +#else + #define BIDI_LOG(fmt, ...) ((void)0) + #define BIDI_LOG_SIMPLE(msg) ((void)0) +#endif + +// ============================================================================ +// UNICODE VALIDATION HELPERS +// ============================================================================ + +// Check if codepoint is a valid Unicode scalar value (not surrogate, not non-character) +static inline bool IsValidUnicodeScalar(wchar_t ch) +{ + // Reject surrogate pairs (UTF-16 encoding artifacts, invalid in UTF-8) + if (ch >= 0xD800 && ch <= 0xDFFF) + return false; + + // Reject non-characters (reserved by Unicode standard) + if ((ch >= 0xFDD0 && ch <= 0xFDEF) || // Arabic Presentation Forms non-chars + (ch & 0xFFFE) == 0xFFFE) // U+FFFE, U+FFFF, etc. + return false; + + // Accept everything else in BMP (0x0000-0xFFFF) + return true; +} + +// Sanitize a wide string by removing invalid Unicode codepoints +static inline void SanitizeWideString(std::wstring& ws) +{ + ws.erase(std::remove_if(ws.begin(), ws.end(), + [](wchar_t ch) { return !IsValidUnicodeScalar(ch); }), + ws.end()); +} + +// UTF-8 -> UTF-16 (Windows wide) +inline std::wstring Utf8ToWide(const std::string& s) +{ + if (s.empty()) + return L""; + + // Validate size limits (prevent DoS and INT_MAX overflow) + if (s.size() > MAX_TEXT_LENGTH || s.size() > INT_MAX) + { + BIDI_LOG("Utf8ToWide: String too large (%zu bytes)", s.size()); + return L""; // String too large + } + + int wlen = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), (int)s.size(), nullptr, 0); + if (wlen <= 0) + { + BIDI_LOG("Utf8ToWide: Invalid UTF-8 sequence (error %d)", GetLastError()); + return L""; + } + + std::wstring out(wlen, L'\0'); + int written = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), (int)s.size(), out.data(), wlen); + if (written <= 0 || written != wlen) + { + BIDI_LOG("Utf8ToWide: Second conversion failed (written=%d, expected=%d, error=%d)", written, wlen, GetLastError()); + return L""; // Conversion failed unexpectedly + } + + // Optional: Sanitize to remove invalid Unicode codepoints (surrogates, non-characters) + // Uncomment if you want strict validation + // SanitizeWideString(out); + + return out; +} + +// Convenience overload for char* +inline std::wstring Utf8ToWide(const char* s) +{ + if (!s || !*s) + return L""; + + int wlen = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, s, -1, nullptr, 0); + if (wlen <= 0) + return L""; + + // wlen includes terminating NUL + std::wstring out(wlen, L'\0'); + + int written = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, s, -1, out.data(), wlen); + if (written <= 0 || written != wlen) + { + BIDI_LOG("Utf8ToWide(char*): Conversion failed (written=%d, expected=%d, error=%d)", written, wlen, GetLastError()); + return L""; + } + + // Drop the terminating NUL from std::wstring length + if (!out.empty() && out.back() == L'\0') + out.pop_back(); + + // Optional: Sanitize to remove invalid Unicode codepoints + // SanitizeWideString(out); + + return out; +} + +// UTF-16 (Windows wide) -> UTF-8 +inline std::string WideToUtf8(const std::wstring& ws) +{ + if (ws.empty()) + return ""; + + // Validate size limits (prevent DoS and INT_MAX overflow) + if (ws.size() > MAX_TEXT_LENGTH || ws.size() > INT_MAX) + return ""; // String too large + + int len = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, ws.data(), (int)ws.size(), nullptr, 0, nullptr, nullptr); + if (len <= 0) + return ""; + + std::string out(len, '\0'); + int written = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, ws.data(), (int)ws.size(), out.data(), len, nullptr, nullptr); + if (written <= 0 || written != len) + { + BIDI_LOG("WideToUtf8: Conversion failed (written=%d, expected=%d, error=%d)", written, len, GetLastError()); + return ""; // Conversion failed + } + return out; +} + +// Convenience overload for wchar_t* +inline std::string WideToUtf8(const wchar_t* ws) +{ + if (!ws) + return ""; + return WideToUtf8(std::wstring(ws)); +} + +// ============================================================================ +// RTL & BiDi formatting for RTL UI +// ============================================================================ + +enum class EBidiDir { LTR, RTL }; +enum class ECharDir : unsigned char { Neutral, LTR, RTL }; + +struct TBidiRun +{ + EBidiDir dir; + std::vector text; // logical order +}; + +static inline bool IsRTLCodepoint(wchar_t ch) +{ + // Directional marks / isolates / embeddings that affect bidi + if (ch == 0x200F || ch == 0x061C) return true; // RLM, ALM + if (ch >= 0x202B && ch <= 0x202E) return true; // RLE/RLO/PDF/LRE/LRO + if (ch >= 0x2066 && ch <= 0x2069) return true; // isolates + + // Hebrew + Arabic blocks (BMP) + if (ch >= 0x0590 && ch <= 0x08FF) return true; + + // Presentation forms + if (ch >= 0xFB1D && ch <= 0xFDFF) return true; + if (ch >= 0xFE70 && ch <= 0xFEFF) return true; + + return false; +} + +static inline bool IsStrongAlpha(wchar_t ch) +{ + // Use thread-local cache for BMP (Thread safety) + thread_local static unsigned char cache[65536] = {}; // 0=unknown, 1=true, 2=false + unsigned char& v = cache[(unsigned short)ch]; + if (v == 1) return true; + if (v == 2) return false; + + WORD type = 0; + bool ok = GetStringTypeW(CT_CTYPE1, &ch, 1, &type) && (type & C1_ALPHA); + v = ok ? 1 : 2; + return ok; +} + +static inline bool IsDigit(wchar_t ch) +{ + // Fast path for ASCII digits (90%+ of digit checks) + if (ch >= L'0' && ch <= L'9') + return true; + + // For non-ASCII, use cache (Arabic-Indic digits, etc.) + thread_local static unsigned char cache[65536] = {}; // 0=unknown, 1=true, 2=false + unsigned char& v = cache[(unsigned short)ch]; + if (v == 1) return true; + if (v == 2) return false; + + WORD type = 0; + bool ok = GetStringTypeW(CT_CTYPE1, &ch, 1, &type) && (type & C1_DIGIT); + v = ok ? 1 : 2; + return ok; +} + +static inline bool IsNameTokenPunct(wchar_t ch) +{ + switch (ch) + { + case L'#': + case L'@': + case L'$': + case L'%': + case L'&': + case L'*': + case L'+': + case L'-': + case L'_': + case L'=': + case L'.': + case L',': + case L'/': + case L'\\': + case L'(': + case L')': + case L'[': + case L']': + case L'{': + case L'}': + case L'<': + case L'>': + return true; + default: + return false; + } +} + +// Check RTL first to avoid classifying Arabic as LTR +static inline bool IsStrongLTR(wchar_t ch) +{ + if (IsRTLCodepoint(ch)) + return false; + return IsStrongAlpha(ch) || IsDigit(ch); +} + +static inline bool HasStrongLTRNeighbor(const wchar_t* s, int n, int i) +{ + // Remove null/size check (caller guarantees validity) + // Early exit after first strong neighbor found + + // Check previous character + if (i > 0 && IsStrongLTR(s[i - 1])) + return true; + + // Check next character + if (i + 1 < n && IsStrongLTR(s[i + 1])) + return true; + + return false; +} + +static inline ECharDir GetCharDir(wchar_t ch) +{ + if (IsRTLCodepoint(ch)) + return ECharDir::RTL; + + // Use IsStrongLTR which now correctly excludes RTL + if (IsStrongLTR(ch)) + return ECharDir::LTR; + + return ECharDir::Neutral; +} + +static inline ECharDir GetCharDirSmart(const wchar_t* s, int n, int i) +{ + wchar_t ch = s[i]; + + // True RTL letters/marks + if (IsRTLCodepoint(ch)) + return ECharDir::RTL; + + // True LTR letters/digits (now correctly excludes RTL) + if (IsStrongLTR(ch)) + return ECharDir::LTR; + + // Name-token punctuation: if adjacent to LTR, treat as LTR to keep token intact + if (IsNameTokenPunct(ch) && HasStrongLTRNeighbor(s, n, i)) + return ECharDir::LTR; + + return ECharDir::Neutral; +} + +// Pre-computed strong character lookup for O(1) neutral resolution +struct TStrongDirCache +{ + std::vector nextStrong; // nextStrong[i] = direction of next strong char after position i + EBidiDir baseDir; + + TStrongDirCache(const wchar_t* s, int n, EBidiDir base) : nextStrong(n), baseDir(base) + { + // Build reverse lookup: scan from end to beginning + EBidiDir lastSeen = baseDir; + for (int i = n - 1; i >= 0; --i) + { + ECharDir cd = GetCharDir(s[i]); + if (cd == ECharDir::LTR) + lastSeen = EBidiDir::LTR; + else if (cd == ECharDir::RTL) + lastSeen = EBidiDir::RTL; + + nextStrong[i] = lastSeen; + } + } + + EBidiDir GetNextStrong(int i) const + { + if (i + 1 < (int)nextStrong.size()) + return nextStrong[i + 1]; + return baseDir; + } +}; + +static inline EBidiDir ResolveNeutralDir(const wchar_t* s, int n, int i, EBidiDir baseDir, EBidiDir lastStrong, const TStrongDirCache* cache = nullptr) +{ + // Use pre-computed cache if available (O(1) instead of O(n)) + EBidiDir nextStrong = baseDir; + if (cache) + { + nextStrong = cache->GetNextStrong(i); + } + else + { + // Linear scan (slower, but works without cache) + for (int j = i + 1; j < n; ++j) + { + ECharDir cd = GetCharDirSmart(s, n, j); + if (cd == ECharDir::LTR) { nextStrong = EBidiDir::LTR; break; } + if (cd == ECharDir::RTL) { nextStrong = EBidiDir::RTL; break; } + } + } + + // If both sides agree, neutral adopts that direction + if (lastStrong == nextStrong) + return lastStrong; + + // Handle edge cases for leading/trailing punctuation + if (nextStrong == baseDir && lastStrong != baseDir) + return lastStrong; + + if (lastStrong == baseDir && nextStrong != baseDir) + return nextStrong; + + // Otherwise fall back to base direction + return baseDir; +} + +static EBidiDir DetectBaseDir_FirstStrong(const wchar_t* s, int n) +{ + if (!s || n <= 0) + return EBidiDir::LTR; + + for (int i = 0; i < n; ++i) + { + const wchar_t ch = s[i]; + // Check RTL first, then alpha + if (IsRTLCodepoint(ch)) + return EBidiDir::RTL; + + if (IsStrongAlpha(ch)) + return EBidiDir::LTR; + } + + return EBidiDir::LTR; +} + +static std::vector BuildVisualBidiText_Tagless(const wchar_t* s, int n, bool forceRTL) +{ + if (!s || n <= 0) + return {}; + + // Detect chat format "name : msg" and extract components + int chatSepPos = -1; + for (int i = 0; i < n - 2; ++i) + { + if (s[i] == L' ' && s[i + 1] == L':' && s[i + 2] == L' ') + { + chatSepPos = i; + break; + } + } + + // If chat format detected, process name and message separately + if (chatSepPos > 0 && forceRTL) + { + // Use pointers instead of copying (zero-copy optimization) + const wchar_t* name = s; + const int nameLen = chatSepPos; + + const int msgStart = chatSepPos + 3; + const wchar_t* msg = s + msgStart; + const int msgLen = n - msgStart; + + // Check if message contains RTL + bool msgHasRTL = false; + for (int i = 0; i < msgLen; ++i) + { + if (IsRTLCodepoint(msg[i])) + { + msgHasRTL = true; + break; + } + } + + // Build result based on message direction (pre-reserve exact size) + std::vector visual; + visual.reserve((size_t)n); + + if (msgHasRTL) + { + // Arabic message: apply BiDi to message, then add " : name" + std::vector msgVisual = BuildVisualBidiText_Tagless(msg, msgLen, false); + visual.insert(visual.end(), msgVisual.begin(), msgVisual.end()); + visual.push_back(L' '); + visual.push_back(L':'); + visual.push_back(L' '); + visual.insert(visual.end(), name, name + nameLen); // Direct pointer insert + } + else + { + // English message: "msg : name" + visual.insert(visual.end(), msg, msg + msgLen); // Direct pointer insert + visual.push_back(L' '); + visual.push_back(L':'); + visual.push_back(L' '); + visual.insert(visual.end(), name, name + nameLen); // Direct pointer insert + } + + return visual; + } + + // 1) base direction + EBidiDir base = forceRTL ? EBidiDir::RTL : DetectBaseDir_FirstStrong(s, n); + + // Pre-compute strong character positions for O(1) neutral resolution + TStrongDirCache strongCache(s, n, base); + + // 2) split into runs + // Estimate runs based on text length (~1 per 50 chars, min 4) + std::vector runs; + const size_t estimatedRuns = (size_t)std::max(4, n / 50); + runs.reserve(estimatedRuns); + + auto push_run = [&](EBidiDir d) + { + if (runs.empty() || runs.back().dir != d) + runs.push_back(TBidiRun{ d, {} }); + }; + + // start with base so leading neutrals attach predictably + push_run(base); + + EBidiDir lastStrong = base; + + for (int i = 0; i < n; ++i) + { + wchar_t ch = s[i]; + + EBidiDir d; + ECharDir cd = GetCharDirSmart(s, n, i); + + if (cd == ECharDir::RTL) + { + d = EBidiDir::RTL; + lastStrong = EBidiDir::RTL; + } + else if (cd == ECharDir::LTR) + { + d = EBidiDir::LTR; + lastStrong = EBidiDir::LTR; + } + else + { + // Pass cache for O(1) lookup instead of O(n) scan + d = ResolveNeutralDir(s, n, i, base, lastStrong, &strongCache); + } + + push_run(d); + runs.back().text.push_back(ch); + } + + // 3) shape RTL runs in logical order (Arabic shaping) + for (auto& r : runs) + { + if (r.dir != EBidiDir::RTL) + continue; + + if (r.text.empty()) + continue; + + // Check for potential integer overflow before allocation + if (r.text.size() > SIZE_MAX / ARABIC_SHAPING_EXPANSION_FACTOR_RETRY - ARABIC_SHAPING_SAFETY_MARGIN_RETRY) + { + BIDI_LOG("BuildVisualBidiText: RTL run too large for shaping (%zu chars)", r.text.size()); + continue; // Text too large to process safely + } + + std::vector shaped(r.text.size() * ARABIC_SHAPING_EXPANSION_FACTOR + ARABIC_SHAPING_SAFETY_MARGIN, 0); + + int outLen = Arabic_MakeShape(r.text.data(), (int)r.text.size(), shaped.data(), (int)shaped.size()); + if (outLen <= 0) + { + BIDI_LOG("Arabic_MakeShape failed for run of %zu chars", r.text.size()); + continue; + } + + // Retry once if buffer too small + if (outLen >= (int)shaped.size()) + { + shaped.assign(r.text.size() * ARABIC_SHAPING_EXPANSION_FACTOR_RETRY + ARABIC_SHAPING_SAFETY_MARGIN_RETRY, 0); + outLen = Arabic_MakeShape(r.text.data(), (int)r.text.size(), shaped.data(), (int)shaped.size()); + if (outLen <= 0) + continue; + // Add error check instead of silent truncation + if (outLen > (int)shaped.size()) + { + BIDI_LOG("Arabic_MakeShape: Buffer still too small after retry (%d > %zu)", outLen, shaped.size()); + // Shaping failed critically, use unshaped text + continue; + } + } + + r.text.assign(shaped.begin(), shaped.begin() + outLen); + } + + // 4) produce visual order: + // - reverse RTL runs internally + // - reverse run sequence if base RTL + std::vector visual; + visual.reserve((size_t)n); + + auto emit_run = [&](const TBidiRun& r) + { + if (r.dir == EBidiDir::RTL) + { + for (int k = (int)r.text.size() - 1; k >= 0; --k) + visual.push_back(r.text[(size_t)k]); + } + else + { + visual.insert(visual.end(), r.text.begin(), r.text.end()); + } + }; + + if (base == EBidiDir::LTR) + { + for (const auto& r : runs) + emit_run(r); + } + else + { + for (int i = (int)runs.size() - 1; i >= 0; --i) + emit_run(runs[(size_t)i]); + } + + return visual; +} + +// ============================================================================ +// TextTail formatting for RTL UI +// ============================================================================ + +enum class EPlaceDir +{ + Left, // place block to the LEFT of the cursor (cursor is a right edge) + Right // place block to the RIGHT of the cursor (cursor is a left edge) +}; + +template +inline float TextTailBiDi(TText* t, float cursorX, float y, float z, float fxAdd, EPlaceDir dir) +{ + if (!t) + return cursorX; + + int w = 0, h = 0; + t->GetTextSize(&w, &h); + const float fw = static_cast(w); + + float x; + if (dir == EPlaceDir::Left) + { + x = t->IsRTL() ? cursorX : (cursorX - fw); + // advance cursor left + cursorX = cursorX - fw - fxAdd; + } + else + { + x = t->IsRTL() ? (cursorX + fw) : cursorX; + // advance cursor right + cursorX = cursorX + fw + fxAdd; + } + + // SNAP to pixel grid to avoid "broken pixels" + x = floorf(x + 0.5f); + y = floorf(y + 0.5f); + + t->SetPosition(x, y, z); + t->Update(); + + return cursorX; +} diff --git a/src/AudioLib/Type.cpp b/src/AudioLib/Type.cpp index 8db5a49..ca2b0ca 100644 --- a/src/AudioLib/Type.cpp +++ b/src/AudioLib/Type.cpp @@ -1,6 +1,7 @@ #include "StdAfx.h" #include "Type.h" #include "EterLib/TextFileLoader.h" +#include std::string NSound::strResult; @@ -91,7 +92,9 @@ bool NSound::SaveSoundInformationPiece(const char* c_szFileName, NSound::TSoundD std::string strResult; strResult = c_szFileName; - FILE* File = fopen(c_szFileName, "wt"); + // UTF-8 → UTF-16 conversion for Unicode path support + std::wstring wFileName = Utf8ToWide(c_szFileName); + FILE* File = _wfopen(wFileName.c_str(), L"wt"); if (!File) { diff --git a/src/Discord/discord_register_win.cpp b/src/Discord/discord_register_win.cpp index 928a3b7..933365c 100644 --- a/src/Discord/discord_register_win.cpp +++ b/src/Discord/discord_register_win.cpp @@ -133,27 +133,36 @@ static void Discord_RegisterW(const wchar_t* applicationId, const wchar_t* comma extern "C" DISCORD_EXPORT void Discord_Register(const char* applicationId, const char* command) { wchar_t appId[32]; - MultiByteToWideChar(CP_UTF8, 0, applicationId, -1, appId, 32); + int app = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, applicationId, -1, appId, 32); + if (app <= 0) + return; wchar_t openCommand[1024]; const wchar_t* wcommand = nullptr; if (command && command[0]) { const auto commandBufferLen = sizeof(openCommand) / sizeof(*openCommand); - MultiByteToWideChar(CP_UTF8, 0, command, -1, openCommand, commandBufferLen); + + int ok = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, command, -1, openCommand, commandBufferLen); + if (ok <= 0) + return; + wcommand = openCommand; } Discord_RegisterW(appId, wcommand); } -extern "C" DISCORD_EXPORT void Discord_RegisterSteamGame(const char* applicationId, - const char* steamId) +extern "C" DISCORD_EXPORT void Discord_RegisterSteamGame(const char* applicationId, const char* steamId) { wchar_t appId[32]; - MultiByteToWideChar(CP_UTF8, 0, applicationId, -1, appId, 32); + int app = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, applicationId, -1, appId, 32); + if (app <= 0) + return; wchar_t wSteamId[32]; - MultiByteToWideChar(CP_UTF8, 0, steamId, -1, wSteamId, 32); + int steam = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, steamId, -1, wSteamId, 32); + if (steam <= 0) + return; HKEY key; auto status = RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\Valve\\Steam", 0, KEY_READ, &key); diff --git a/src/EterBase/CRC32.cpp b/src/EterBase/CRC32.cpp index 86ab919..2eb5fa2 100644 --- a/src/EterBase/CRC32.cpp +++ b/src/EterBase/CRC32.cpp @@ -1,6 +1,8 @@ #include "StdAfx.h" #include "CRC32.h" +#include + 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; } diff --git a/src/EterBase/CRC32.h b/src/EterBase/CRC32.h index ee515cf..251a1fc 100644 --- a/src/EterBase/CRC32.h +++ b/src/EterBase/CRC32.h @@ -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 diff --git a/src/EterBase/Debug.cpp b/src/EterBase/Debug.cpp index 1427eb9..82c258a 100644 --- a/src/EterBase/Debug.cpp +++ b/src/EterBase/Debug.cpp @@ -6,6 +6,7 @@ #include "Singleton.h" #include "Timer.h" #include +#include 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); } diff --git a/src/EterBase/FileBase.cpp b/src/EterBase/FileBase.cpp index b9174bd..2d9b6ce 100644 --- a/src/EterBase/FileBase.cpp +++ b/src/EterBase/FileBase.cpp @@ -1,6 +1,8 @@ #include "StdAfx.h" #include "FileBase.h" +#include + 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; diff --git a/src/EterBase/FileDir.cpp b/src/EterBase/FileDir.cpp index 9c1412b..b7e788f 100644 --- a/src/EterBase/FileDir.cpp +++ b/src/EterBase/FileDir.cpp @@ -1,6 +1,7 @@ #include "StdAfx.h" #include "FileDir.h" #include +#include 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; } diff --git a/src/EterBase/FileLoader.cpp b/src/EterBase/FileLoader.cpp index 8a2c279..1d92c60 100644 --- a/src/EterBase/FileLoader.cpp +++ b/src/EterBase/FileLoader.cpp @@ -1,6 +1,7 @@ #include "StdAfx.h" #include "FileLoader.h" #include +#include 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; diff --git a/src/EterBase/TempFile.cpp b/src/EterBase/TempFile.cpp index f55e8bd..3f7b5f7 100644 --- a/src/EterBase/TempFile.cpp +++ b/src/EterBase/TempFile.cpp @@ -2,11 +2,17 @@ #include "TempFile.h" #include "Utils.h" #include "Debug.h" +#include 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) diff --git a/src/EterBase/Utils.cpp b/src/EterBase/Utils.cpp index e05abef..bd185f5 100644 --- a/src/EterBase/Utils.cpp +++ b/src/EterBase/Utils.cpp @@ -5,24 +5,43 @@ #include #include #include +#include + #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::iterator itor = ms_strDirectoryDeque.begin(); itor != ms_strDirectoryDeque.end(); ++itor) + for (std::deque::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 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 #include #include +#include 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); diff --git a/src/EterLib/GrpDIB.cpp b/src/EterLib/GrpDIB.cpp index c1b57b3..61ecd09 100644 --- a/src/EterLib/GrpDIB.cpp +++ b/src/EterLib/GrpDIB.cpp @@ -1,5 +1,6 @@ #include "StdAfx.h" #include "GrpDIB.h" +#include CGraphicDib::CGraphicDib() { @@ -74,10 +75,17 @@ void CGraphicDib::SetBkMode(int iBkMode) ::SetBkMode(m_hDC, iBkMode); } -void CGraphicDib::TextOut(int ix, int iy, const char * c_szText) +void CGraphicDib::TextOut(int ix, int iy, const char* c_szText) { - ::SetBkColor(m_hDC, 0); - ::TextOut(m_hDC, ix, iy, c_szText, strlen(c_szText)); + ::SetBkColor(m_hDC, 0); + + if (!c_szText || !*c_szText) + return; + + std::wstring wText = Utf8ToWide(c_szText); + + if (!wText.empty()) + ::TextOutW(m_hDC, ix, iy, wText.c_str(), (int)wText.length()); } void CGraphicDib::Put(HDC hDC, int x, int y) diff --git a/src/EterLib/GrpDevice.cpp b/src/EterLib/GrpDevice.cpp index e4f3409..52c90ec 100644 --- a/src/EterLib/GrpDevice.cpp +++ b/src/EterLib/GrpDevice.cpp @@ -2,7 +2,9 @@ #include "GrpDevice.h" #include "EterBase/Stl.h" #include "EterBase/Debug.h" + #include +#include bool CPU_HAS_SSE2 = false; bool GRAPHICS_CAPS_CAN_NOT_DRAW_LINE = false; @@ -48,9 +50,16 @@ void CGraphicDevice::RegisterWarningString(UINT uiMsg, const char * c_szString) void CGraphicDevice::__WarningMessage(HWND hWnd, UINT uiMsg) { - if (m_kMap_strWarningMessage.end() == m_kMap_strWarningMessage.find(uiMsg)) + auto it = m_kMap_strWarningMessage.find(uiMsg); + if (it == m_kMap_strWarningMessage.end()) return; - MessageBox(hWnd, m_kMap_strWarningMessage[uiMsg].c_str(), "Warning", MB_OK|MB_TOPMOST); + + const std::string& msgUtf8 = it->second; + + std::wstring wMsg = Utf8ToWide(msgUtf8); + std::wstring wCaption = L"Warning"; // static wide literal is fine + + MessageBoxW(hWnd, wMsg.c_str(), wCaption.c_str(), MB_OK | MB_TOPMOST); } void CGraphicDevice::MoveWebBrowserRect(const RECT& c_rcWebPage) @@ -148,9 +157,9 @@ LPDIRECT3DVERTEXDECLARATION9 CGraphicDevice::CreatePNTStreamVertexShader() if (ms_lpd3dDevice->CreateVertexDeclaration(pShaderDecl, &dwShader) != D3D_OK) { - char szError[1024]; - sprintf(szError, "Failed to create CreatePNTStreamVertexShader"); - MessageBox(NULL, szError, "Vertex Shader Error", MB_ICONSTOP); + wchar_t szError[1024]; + swprintf(szError, L"Failed to create CreatePNTStreamVertexShader"); + MessageBoxW(NULL, szError, L"Vertex Shader Error", MB_ICONSTOP); } return dwShader; @@ -172,9 +181,9 @@ LPDIRECT3DVERTEXDECLARATION9 CGraphicDevice::CreatePNT2StreamVertexShader() if (ms_lpd3dDevice->CreateVertexDeclaration(pShaderDecl, &dwShader) != D3D_OK) { - char szError[1024]; - sprintf(szError, "Failed to create CreatePNT2StreamVertexShader"); - MessageBox(NULL, szError, "Vertex Shader Error", MB_ICONSTOP); + wchar_t szError[1024]; + swprintf(szError, L"Failed to create CreatePNT2StreamVertexShader"); + MessageBoxW(NULL, szError, L"Vertex Shader Error", MB_ICONSTOP); } return dwShader; @@ -194,9 +203,9 @@ LPDIRECT3DVERTEXDECLARATION9 CGraphicDevice::CreatePTStreamVertexShader() if (ms_lpd3dDevice->CreateVertexDeclaration(pShaderDecl, &dwShader) != D3D_OK) { - char szError[1024]; - sprintf(szError, "Failed to create CreatePTStreamVertexShader"); - MessageBox(NULL, szError, "Vertex Shader Error", MB_ICONSTOP); + wchar_t szError[1024]; + swprintf(szError, L"Failed to create CreatePTStreamVertexShader"); + MessageBoxW(NULL, szError, L"Vertex Shader Error", MB_ICONSTOP); } return dwShader; @@ -221,9 +230,9 @@ LPDIRECT3DVERTEXDECLARATION9 CGraphicDevice::CreateDoublePNTStreamVertexShader() if (ms_lpd3dDevice->CreateVertexDeclaration(pShaderDecl, &dwShader) != D3D_OK) { - char szError[1024]; - sprintf(szError, "Failed to create CreateDoublePNTStreamVertexShader"); - MessageBox(NULL, szError, "Vertex Shader Error", MB_ICONSTOP); + wchar_t szError[1024]; + swprintf(szError, L"Failed to create CreateDoublePNTStreamVertexShader"); + MessageBoxW(NULL, szError, L"Vertex Shader Error", MB_ICONSTOP); } return dwShader; diff --git a/src/EterLib/GrpFontTexture.cpp b/src/EterLib/GrpFontTexture.cpp index 3e313ba..3aa2cf4 100644 --- a/src/EterLib/GrpFontTexture.cpp +++ b/src/EterLib/GrpFontTexture.cpp @@ -3,6 +3,7 @@ #include "EterBase/Stl.h" #include "Util.h" +#include CGraphicFontTexture::CGraphicFontTexture() { @@ -69,75 +70,74 @@ void CGraphicFontTexture::DestroyDeviceObjects() bool CGraphicFontTexture::Create(const char* c_szFontName, int fontSize, bool bItalic) { Destroy(); - - strncpy(m_fontName, c_szFontName, sizeof(m_fontName)-1); - m_fontSize = fontSize; - m_bItalic = bItalic; + + // UTF-8 -> UTF-16 for font name + std::wstring wFontName = Utf8ToWide(c_szFontName ? c_szFontName : ""); + wcsncpy_s(m_fontName, wFontName.c_str(), _TRUNCATE); + + m_fontSize = fontSize; + m_bItalic = bItalic; m_x = 0; m_y = 0; m_step = 0; - DWORD width = 256,height = 256; + DWORD width = 256, height = 256; if (GetMaxTextureWidth() > 512) width = 512; if (GetMaxTextureHeight() > 512) height = 512; - + if (!m_dib.Create(ms_hDC, width, height)) return false; HDC hDC = m_dib.GetDCHandle(); - m_hFont = GetFont(GetDefaultCodePage()); + m_hFont = GetFont(); - m_hFontOld=(HFONT)SelectObject(hDC, m_hFont); + m_hFontOld = (HFONT)SelectObject(hDC, m_hFont); SetTextColor(hDC, RGB(255, 255, 255)); - SetBkColor(hDC, 0); + SetBkColor(hDC, 0); if (!AppendTexture()) return false; - + return true; } - - -HFONT CGraphicFontTexture::GetFont(WORD codePage) +HFONT CGraphicFontTexture::GetFont() { - HFONT hFont = NULL; - TFontMap::iterator i = m_fontMap.find(codePage); + HFONT hFont = nullptr; - if(i != m_fontMap.end()) - { - hFont = i->second; - } - else - { - LOGFONT logFont; + // For Unicode, codePage should NOT affect font selection anymore + static const WORD kUnicodeFontKey = 0; - memset(&logFont, 0, sizeof(LOGFONT)); + TFontMap::iterator it = m_fontMap.find(kUnicodeFontKey); + if (it != m_fontMap.end()) + return it->second; - logFont.lfHeight = m_fontSize; - logFont.lfEscapement = 0; - logFont.lfOrientation = 0; - logFont.lfWeight = FW_NORMAL; - logFont.lfItalic = (BYTE) m_bItalic; - logFont.lfUnderline = FALSE; - logFont.lfStrikeOut = FALSE; - logFont.lfCharSet = GetCharsetFromCodePage(codePage); - logFont.lfOutPrecision = OUT_DEFAULT_PRECIS; - logFont.lfClipPrecision = CLIP_DEFAULT_PRECIS; - logFont.lfQuality = ANTIALIASED_QUALITY; - logFont.lfPitchAndFamily = DEFAULT_PITCH; - //Tracenf("font: %s", GetFontFaceFromCodePage(codePage)); - strcpy(logFont.lfFaceName, m_fontName); //GetFontFaceFromCodePage(codePage)); - //strcpy(logFont.lfFaceName, GetFontFaceFromCodePage(codePage)); + LOGFONTW logFont{}; - hFont = CreateFontIndirect(&logFont); + logFont.lfHeight = m_fontSize; + logFont.lfEscapement = 0; + logFont.lfOrientation = 0; + logFont.lfWeight = FW_NORMAL; + logFont.lfItalic = (BYTE)m_bItalic; + logFont.lfUnderline = FALSE; + logFont.lfStrikeOut = FALSE; + logFont.lfCharSet = DEFAULT_CHARSET; + logFont.lfOutPrecision = OUT_DEFAULT_PRECIS; + logFont.lfClipPrecision = CLIP_DEFAULT_PRECIS; + logFont.lfQuality = ANTIALIASED_QUALITY; + logFont.lfPitchAndFamily = DEFAULT_PITCH; - m_fontMap.insert(TFontMap::value_type(codePage, hFont)); - } + // Copy Unicode font face name safely + wcsncpy_s(logFont.lfFaceName, m_fontName, _TRUNCATE); + + hFont = CreateFontIndirectW(&logFont); + + if (hFont) + m_fontMap.insert(TFontMap::value_type(kUnicodeFontKey, hFont)); return hFont; } @@ -189,9 +189,9 @@ bool CGraphicFontTexture::UpdateTexture() return true; } -CGraphicFontTexture::TCharacterInfomation* CGraphicFontTexture::GetCharacterInfomation(WORD codePage, wchar_t keyValue) +CGraphicFontTexture::TCharacterInfomation* CGraphicFontTexture::GetCharacterInfomation(wchar_t keyValue) { - TCharacterKey code(codePage, keyValue); + TCharacterKey code = keyValue; TCharacterInfomationMap::iterator f = m_charInfoMap.find(code); @@ -201,22 +201,20 @@ CGraphicFontTexture::TCharacterInfomation* CGraphicFontTexture::GetCharacterInfo } else { - return &f->second; + return &f->second; } } -CGraphicFontTexture::TCharacterInfomation* CGraphicFontTexture::UpdateCharacterInfomation(TCharacterKey code) +CGraphicFontTexture::TCharacterInfomation* CGraphicFontTexture::UpdateCharacterInfomation(TCharacterKey keyValue) { HDC hDC = m_dib.GetDCHandle(); - SelectObject(hDC, GetFont(code.first)); - - wchar_t keyValue = code.second; + SelectObject(hDC, GetFont()); if (keyValue == 0x08) - keyValue = L' '; // 탭은 공백으로 바꾼다 (아랍 출력시 탭 사용: NAME:\tTEXT -> TEXT\t:NAME 로 전환됨 ) + keyValue = L' '; // 탭은 공백으로 바꾼다 (아랍 출력시 탭 사용: NAME:\tTEXT -> TEXT\t:NAME 로 전환됨 ) - ABCFLOAT stABC; - SIZE size; + ABCFLOAT stABC; + SIZE size; if (!GetTextExtentPoint32W(hDC, &keyValue, 1, &size) || !GetCharABCWidthsFloatW(hDC, keyValue, keyValue, &stABC)) return NULL; @@ -254,21 +252,20 @@ CGraphicFontTexture::TCharacterInfomation* CGraphicFontTexture::UpdateCharacterI } TextOutW(hDC, m_x, m_y, &keyValue, 1); - + int nChrX; int nChrY; int nChrWidth = size.cx; int nChrHeight = size.cy; int nDIBWidth = m_dib.GetWidth(); - - DWORD*pdwDIBData=(DWORD*)m_dib.GetPointer(); + DWORD*pdwDIBData=(DWORD*)m_dib.GetPointer(); DWORD*pdwDIBBase=pdwDIBData+nDIBWidth*m_y+m_x; DWORD*pdwDIBRow; - + pdwDIBRow=pdwDIBBase; for (nChrY=0; nChrY(m_pFontTextureVector.size() - 1); rNewCharInfo.width = size.cx; @@ -292,7 +289,7 @@ CGraphicFontTexture::TCharacterInfomation* CGraphicFontTexture::UpdateCharacterI m_x += size.cx; if (m_step < size.cy) - m_step = size.cy; + m_step = size.cy; m_isDirty = true; diff --git a/src/EterLib/GrpFontTexture.h b/src/EterLib/GrpFontTexture.h index 56ae068..743c756 100644 --- a/src/EterLib/GrpFontTexture.h +++ b/src/EterLib/GrpFontTexture.h @@ -9,73 +9,73 @@ class CGraphicFontTexture : public CGraphicTexture { -public: - typedef std::pair TCharacterKey; + public: + typedef wchar_t TCharacterKey; - typedef struct SCharacterInfomation - { - short index; - short width; - short height; - float left; - float top; - float right; - float bottom; - float advance; - } TCharacterInfomation; + typedef struct SCharacterInfomation + { + short index; + short width; + short height; + float left; + float top; + float right; + float bottom; + float advance; + } TCharacterInfomation; - typedef std::vector TPCharacterInfomationVector; + typedef std::vector TPCharacterInfomationVector; -public: - CGraphicFontTexture(); - virtual ~CGraphicFontTexture(); + public: + CGraphicFontTexture(); + virtual ~CGraphicFontTexture(); - void Destroy(); - bool Create(const char* c_szFontName, int fontSize, bool bItalic); + void Destroy(); + bool Create(const char* c_szFontName, int fontSize, bool bItalic); - bool CreateDeviceObjects(); - void DestroyDeviceObjects(); + bool CreateDeviceObjects(); + void DestroyDeviceObjects(); - bool CheckTextureIndex(DWORD dwTexture); - void SelectTexture(DWORD dwTexture); + bool CheckTextureIndex(DWORD dwTexture); + void SelectTexture(DWORD dwTexture); - bool UpdateTexture(); + bool UpdateTexture(); - TCharacterInfomation* GetCharacterInfomation(WORD codePage, wchar_t keyValue); - TCharacterInfomation* UpdateCharacterInfomation(TCharacterKey code); + TCharacterInfomation* GetCharacterInfomation(wchar_t keyValue); + TCharacterInfomation* UpdateCharacterInfomation(TCharacterKey keyValue); - bool IsEmpty() const; + bool IsEmpty() const; -protected: - void Initialize(); + protected: + void Initialize(); - bool AppendTexture(); + bool AppendTexture(); - HFONT GetFont(WORD codePage); + HFONT GetFont(); -protected: - typedef std::vector TGraphicImageTexturePointerVector; - typedef std::map TCharacterInfomationMap; - typedef std::map TFontMap; + protected: + typedef std::vector TGraphicImageTexturePointerVector; + typedef std::map TCharacterInfomationMap; + typedef std::map TFontMap; -protected: - CGraphicDib m_dib; + protected: + CGraphicDib m_dib; - HFONT m_hFontOld; - HFONT m_hFont; + HFONT m_hFontOld; + HFONT m_hFont; - TGraphicImageTexturePointerVector m_pFontTextureVector; + TGraphicImageTexturePointerVector m_pFontTextureVector; - TCharacterInfomationMap m_charInfoMap; + TCharacterInfomationMap m_charInfoMap; - TFontMap m_fontMap; + TFontMap m_fontMap; - int m_x; - int m_y; - int m_step; - bool m_isDirty; + int m_x; + int m_y; + int m_step; + bool m_isDirty; - TCHAR m_fontName[LF_FACESIZE]; - LONG m_fontSize; - bool m_bItalic; + TCHAR m_fontName[LF_FACESIZE]; + LONG m_fontSize; + bool m_bItalic; }; diff --git a/src/EterLib/GrpPixelShader.cpp b/src/EterLib/GrpPixelShader.cpp index ec6227e..8e31efd 100644 --- a/src/EterLib/GrpPixelShader.cpp +++ b/src/EterLib/GrpPixelShader.cpp @@ -3,6 +3,8 @@ #include "GrpD3DXBuffer.h" #include "StateManager.h" +#include + CPixelShader::CPixelShader() { Initialize(); @@ -29,23 +31,46 @@ void CPixelShader::Destroy() bool CPixelShader::CreateFromDiskFile(const char* c_szFileName) { - Destroy(); + Destroy(); - LPD3DXBUFFER lpd3dxShaderBuffer; - LPD3DXBUFFER lpd3dxErrorBuffer; - - if (FAILED( - D3DXAssembleShaderFromFile(c_szFileName, 0, NULL, 0, &lpd3dxShaderBuffer, &lpd3dxErrorBuffer) - )) - return false; + if (!c_szFileName || !*c_szFileName) + return false; - CDirect3DXBuffer shaderBuffer(lpd3dxShaderBuffer); - CDirect3DXBuffer errorBuffer(lpd3dxErrorBuffer); + // UTF-8 -> UTF-16 for D3DX + std::wstring wFileName = Utf8ToWide(c_szFileName); - if (FAILED(ms_lpd3dDevice->CreatePixelShader((DWORD*)shaderBuffer.GetPointer(), &m_handle))) - return false; + LPD3DXBUFFER lpd3dxShaderBuffer = nullptr; + LPD3DXBUFFER lpd3dxErrorBuffer = nullptr; - return true; + HRESULT hr = D3DXAssembleShaderFromFileW( + wFileName.c_str(), + nullptr, + nullptr, + 0, + &lpd3dxShaderBuffer, + &lpd3dxErrorBuffer + ); + + if (FAILED(hr)) + { + // Log compiler error text (it is ANSI/ASCII) + if (lpd3dxErrorBuffer) + { + const char* err = (const char*)lpd3dxErrorBuffer->GetBufferPointer(); + TraceError("Shader compile error: %s", err); + } + return false; + } + + CDirect3DXBuffer shaderBuffer(lpd3dxShaderBuffer); + CDirect3DXBuffer errorBuffer(lpd3dxErrorBuffer); + + if (FAILED(ms_lpd3dDevice->CreatePixelShader( + (DWORD*)shaderBuffer.GetPointer(), + &m_handle))) + return false; + + return true; } void CPixelShader::Set() diff --git a/src/EterLib/GrpScreen.cpp b/src/EterLib/GrpScreen.cpp index 6ce2124..0b0e2c9 100644 --- a/src/EterLib/GrpScreen.cpp +++ b/src/EterLib/GrpScreen.cpp @@ -4,6 +4,7 @@ #include "StateManager.h" #include +#include DWORD CScreen::ms_diffuseColor = 0xffffffff; DWORD CScreen::ms_clearColor = 0L; @@ -669,8 +670,11 @@ BOOL CScreen::RestoreDevice() if (FAILED(hrReset)) { _com_error err(hrReset); - LPCTSTR errMsg = err.ErrorMessage(); - TraceError(errMsg); + LPCWSTR errMsgW = err.ErrorMessage(); // wide string + + std::string errUtf8 = WideToUtf8(errMsgW); + TraceError("%s", errUtf8.c_str()); + return FALSE; } diff --git a/src/EterLib/GrpTextInstance.cpp b/src/EterLib/GrpTextInstance.cpp index 420fbbd..1d37345 100644 --- a/src/EterLib/GrpTextInstance.cpp +++ b/src/EterLib/GrpTextInstance.cpp @@ -3,17 +3,18 @@ #include "StateManager.h" #include "IME.h" #include "TextTag.h" -#include "EterLocale/StringCodec.h" #include "EterBase/Utils.h" #include "EterLocale/Arabic.h" #include +#include -extern DWORD GetDefaultCodePage(); +// Forward declaration to avoid header conflicts +extern bool IsRTL(); const float c_fFontFeather = 0.5f; -CDynamicPool CGraphicTextInstance::ms_kPool; +CDynamicPool CGraphicTextInstance::ms_kPool; static int gs_mx = 0; static int gs_my = 0; @@ -32,14 +33,14 @@ int CGraphicTextInstance::Hyperlink_GetText(char* buf, int len) if (gs_hyperlinkText.empty()) return 0; - int codePage = GetDefaultCodePage(); + int written = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, gs_hyperlinkText.c_str(), (int)gs_hyperlinkText.length(), buf, len, nullptr, nullptr); - return Ymir_WideCharToMultiByte(codePage, 0, gs_hyperlinkText.c_str(), gs_hyperlinkText.length(), buf, len, NULL, NULL); + return (written > 0) ? written : 0; } -int CGraphicTextInstance::__DrawCharacter(CGraphicFontTexture * pFontTexture, WORD codePage, wchar_t text, DWORD dwColor) +int CGraphicTextInstance::__DrawCharacter(CGraphicFontTexture * pFontTexture, wchar_t text, DWORD dwColor) { - CGraphicFontTexture::TCharacterInfomation* pInsCharInfo = pFontTexture->GetCharacterInfomation(codePage, text); + CGraphicFontTexture::TCharacterInfomation* pInsCharInfo = pFontTexture->GetCharacterInfomation(text); if (pInsCharInfo) { @@ -50,7 +51,7 @@ int CGraphicTextInstance::__DrawCharacter(CGraphicFontTexture * pFontTexture, WO m_textHeight = std::max((WORD)pInsCharInfo->height, m_textHeight); return pInsCharInfo->advance; } - + return 0; } @@ -78,75 +79,49 @@ void CGraphicTextInstance::__GetTextPos(DWORD index, float* x, float* y) *y = sy; } -bool isNumberic(const char chr) -{ - if (chr >= '0' && chr <= '9') - return true; - return false; -} - -bool IsValidToken(const char* iter) -{ - return iter[0]=='@' && - isNumberic(iter[1]) && - isNumberic(iter[2]) && - isNumberic(iter[3]) && - isNumberic(iter[4]); -} - -const char* FindToken(const char* begin, const char* end) -{ - while(begin < end) - { - begin = std::find(begin, end, '@'); - - if(end-begin>5 && IsValidToken(begin)) - { - return begin; - } - else - { - ++begin; - } - } - - return end; -} - -int ReadToken(const char* token) -{ - int nRet = (token[1]-'0')*1000 + (token[2]-'0')*100 + (token[3]-'0')*10 + (token[4]-'0'); - if (nRet == 9999) - return CP_UTF8; - return nRet; -} - void CGraphicTextInstance::Update() { - if (m_isUpdate) // 문자열이 바뀌었을 때만 업데이트 한다. + if (m_isUpdate) return; - if (m_roText.IsNull()) + // Get space height first for empty text cursor rendering + int spaceHeight = 12; // default fallback + if (!m_roText.IsNull() && !m_roText->IsEmpty()) { - Tracef("CGraphicTextInstance::Update - 폰트가 설정되지 않았습니다\n"); - return; + CGraphicFontTexture* pFontTexture = m_roText->GetFontTexturePointer(); + if (pFontTexture) + { + CGraphicFontTexture::TCharacterInfomation* pSpaceInfo = pFontTexture->GetCharacterInfomation(L' '); + spaceHeight = pSpaceInfo ? pSpaceInfo->height : 12; + } } - if (m_roText->IsEmpty()) + auto ResetState = [&, spaceHeight]() + { + m_pCharInfoVector.clear(); + m_dwColorInfoVector.clear(); + m_hyperlinkVector.clear(); + m_logicalToVisualPos.clear(); + m_visualToLogicalPos.clear(); + m_textWidth = 0; + m_textHeight = spaceHeight; // Use space height instead of 0 for cursor rendering + m_computedRTL = (m_direction == ETextDirection::RTL); + m_isUpdate = true; + }; + + if (m_roText.IsNull() || m_roText->IsEmpty()) + { + ResetState(); return; + } CGraphicFontTexture* pFontTexture = m_roText->GetFontTexturePointer(); if (!pFontTexture) + { + ResetState(); return; + } - UINT defCodePage = GetDefaultCodePage(); - - UINT dataCodePage = defCodePage; // 아랍 및 베트남 내부 데이터를 UTF8 을 사용하려 했으나 실패 - - CGraphicFontTexture::TCharacterInfomation* pSpaceInfo = pFontTexture->GetCharacterInfomation(dataCodePage, ' '); - - int spaceHeight = pSpaceInfo ? pSpaceInfo->height : 12; - m_pCharInfoVector.clear(); m_dwColorInfoVector.clear(); m_hyperlinkVector.clear(); @@ -154,332 +129,594 @@ void CGraphicTextInstance::Update() m_textWidth = 0; m_textHeight = spaceHeight; - /* wstring begin */ + const char* utf8 = m_stText.c_str(); + const int utf8Len = (int)m_stText.size(); + const DWORD defaultColor = m_dwTextColor; - const char* begin = m_stText.c_str(); - const char* end = begin + m_stText.length(); - - int wTextMax = (end - begin) * 2; - wchar_t* wText = (wchar_t*)_alloca(sizeof(wchar_t)*wTextMax); - - DWORD dwColor = m_dwTextColor; - - /* wstring end */ - while (begin < end) + // UTF-8 -> UTF-16 conversion - reserve enough space to avoid reallocation + std::vector wTextBuf((size_t)utf8Len + 1u, 0); + const int wTextLen = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, utf8, utf8Len, wTextBuf.data(), (int)wTextBuf.size()); + if (wTextLen <= 0) { - const char * token = FindToken(begin, end); + ResetState(); + return; + } - int wTextLen = Ymir_MultiByteToWideChar(dataCodePage, 0, begin, token - begin, wText, wTextMax); - if (m_isSecret) + // Detect user-typed text direction (skip hyperlink and color tags) + // Used to determine segment order + bool userTextIsRTL = false; + bool foundUserText = false; + { + int hyperlinkStep = 0; // 0 = normal, 1 = in metadata (hidden), 2 = in visible text + const int wTextLenMinusOne = wTextLen - 1; + + for (int i = 0; i < wTextLen; ++i) { - for(int i=0; i> - int x = 0; - - int len; - int hyperlinkStep = 0; - SHyperlink kHyperlink; - std::wstring hyperlinkBuffer; - int no_hyperlink = 0; - - // 심볼로 끝나면 아랍어 모드로 시작해야한다 - if (Arabic_IsInSymbol(wArabicText[wArabicTextLen - 1])) + if (wTextBuf[i + 1] == L'H') { - isEnglish = false; + hyperlinkStep = 1; // Start metadata + ++i; + continue; } - - int i = 0; - for (i = wArabicTextLen - 1 ; i >= 0; --i) + else if (wTextBuf[i + 1] == L'h') { - wchar_t wArabicChar = wArabicText[i]; - - if (isEnglish) - { - - // <<심볼의 경우 (ex. 기호, 공백)>> -> 영어 모드 유지. - // <<(심볼이 아닌 것들 : 숫자, 영어, 아랍어)>> - // (1) 맨 앞의 심볼 or - // (2) - // 1) 앞 글자가 아랍어 아님 && - // 2) 뒷 글자가 아랍어 아님 && - // 3) 뒷 글자가 심볼'|'이 아님 && - // or - // (3) 현재 심볼이 '|' - // <<아랍어 모드로 넘어가는 경우 : 심볼에서.>> - // 1) 앞글자 아랍어 - // 2) 뒷글자 아랍어 - // - // - if (Arabic_IsInSymbol(wArabicChar) && ( - (i == 0) || - (i > 0 && - !(Arabic_HasPresentation(wArabicText, i - 1) || Arabic_IsInPresentation(wArabicText[i + 1])) && //앞글자, 뒷글자가 아랍어 아님. - wArabicText[i+1] != '|' - ) || - wArabicText[i] == '|' - ))//if end. - { - // pass - int temptest = 1; - } - // (1)아랍어이거나 (2)아랍어 다음의 심볼이라면 아랍어 모드 전환 - else if (Arabic_IsInPresentation(wArabicChar) || Arabic_IsInSymbol(wArabicChar)) - { - //그 전까지의 영어를 그린다. - for (int e = i + 1; e <= nEnglishBase;) { - int ret = GetTextTag(&wArabicText[e], wArabicTextLen - e, len, hyperlinkBuffer); - - if (ret == TEXT_TAG_PLAIN || ret == TEXT_TAG_TAG) - { - if (hyperlinkStep == 1) - hyperlinkBuffer.append(1, wArabicText[e]); - else - { - int charWidth = __DrawCharacter(pFontTexture, dataCodePage, wArabicText[e], dwColor); - kHyperlink.ex += charWidth; - //x += charWidth; - - //기존 추가한 하이퍼링크의 좌표 수정. - for (int j = 1; j <= no_hyperlink; j++) - { - if(m_hyperlinkVector.size() < j) - break; - - SHyperlink & tempLink = m_hyperlinkVector[m_hyperlinkVector.size() - j]; - tempLink.ex += charWidth; - tempLink.sx += charWidth; - } - } - } - else - { - if (ret == TEXT_TAG_COLOR) - dwColor = htoi(hyperlinkBuffer.c_str(), 8); - else if (ret == TEXT_TAG_RESTORE_COLOR) - dwColor = m_dwTextColor; - else if (ret == TEXT_TAG_HYPERLINK_START) - { - hyperlinkStep = 1; - hyperlinkBuffer = L""; - } - else if (ret == TEXT_TAG_HYPERLINK_END) - { - if (hyperlinkStep == 1) - { - ++hyperlinkStep; - kHyperlink.ex = kHyperlink.sx = 0; // 실제 텍스트가 시작되는 위치 - } - else - { - kHyperlink.text = hyperlinkBuffer; - m_hyperlinkVector.push_back(kHyperlink); - no_hyperlink++; - - - hyperlinkStep = 0; - hyperlinkBuffer = L""; - } - } - } - e += len; - } - - int charWidth = __DrawCharacter(pFontTexture, dataCodePage, Arabic_ConvSymbol(wArabicText[i]), dwColor); - kHyperlink.ex += charWidth; - - //기존 추가한 하이퍼링크의 좌표 수정. - for (int j = 1; j <= no_hyperlink; j++) - { - if(m_hyperlinkVector.size() < j) - break; - - SHyperlink & tempLink = m_hyperlinkVector[m_hyperlinkVector.size() - j]; - tempLink.ex += charWidth; - tempLink.sx += charWidth; - } - - isEnglish = false; - } - } - else //[[[아랍어 모드]]] - { - // 아랍어이거나 아랍어 출력중 나오는 심볼이라면 - if (Arabic_IsInPresentation(wArabicChar) || Arabic_IsInSymbol(wArabicChar)) - { - int charWidth = __DrawCharacter(pFontTexture, dataCodePage, Arabic_ConvSymbol(wArabicText[i]), dwColor); - kHyperlink.ex += charWidth; - x += charWidth; - - //기존 추가한 하이퍼링크의 좌표 수정. - for (int j = 1; j <= no_hyperlink; j++) - { - if(m_hyperlinkVector.size() < j) - break; - - SHyperlink & tempLink = m_hyperlinkVector[m_hyperlinkVector.size() - j]; - tempLink.ex += charWidth; - tempLink.sx += charWidth; - } - } - else //영어이거나, 영어 다음에 나오는 심볼이라면, - { - nEnglishBase = i; - isEnglish = true; - } - } + if (hyperlinkStep == 1) + hyperlinkStep = 2; // End metadata, start visible + else if (hyperlinkStep == 2) + hyperlinkStep = 0; // End visible + ++i; + continue; } - - if (isEnglish) + else if (wTextBuf[i + 1] == L'c' && i + 10 <= wTextLen) { - for (int e = i + 1; e <= nEnglishBase;) { - int ret = GetTextTag(&wArabicText[e], wArabicTextLen - e, len, hyperlinkBuffer); - - if (ret == TEXT_TAG_PLAIN || ret == TEXT_TAG_TAG) - { - if (hyperlinkStep == 1) - hyperlinkBuffer.append(1, wArabicText[e]); - else - { - int charWidth = __DrawCharacter(pFontTexture, dataCodePage, wArabicText[e], dwColor); - kHyperlink.ex += charWidth; - - //기존 추가한 하이퍼링크의 좌표 수정. - for (int j = 1; j <= no_hyperlink; j++) - { - if(m_hyperlinkVector.size() < j) - break; - - SHyperlink & tempLink = m_hyperlinkVector[m_hyperlinkVector.size() - j]; - tempLink.ex += charWidth; - tempLink.sx += charWidth; - } - } - } - else - { - if (ret == TEXT_TAG_COLOR) - dwColor = htoi(hyperlinkBuffer.c_str(), 8); - else if (ret == TEXT_TAG_RESTORE_COLOR) - dwColor = m_dwTextColor; - else if (ret == TEXT_TAG_HYPERLINK_START) - { - hyperlinkStep = 1; - hyperlinkBuffer = L""; - } - else if (ret == TEXT_TAG_HYPERLINK_END) - { - if (hyperlinkStep == 1) - { - ++hyperlinkStep; - kHyperlink.ex = kHyperlink.sx = 0; // 실제 텍스트가 시작되는 위치 - } - else - { - kHyperlink.text = hyperlinkBuffer; - m_hyperlinkVector.push_back(kHyperlink); - no_hyperlink++; - - hyperlinkStep = 0; - hyperlinkBuffer = L""; - } - } - } - e += len; - } - + // Color tag |cFFFFFFFF - skip 10 characters + i += 9; // +1 from loop increment = 10 total + continue; + } + else if (wTextBuf[i + 1] == L'r') + { + // Color end tag |r - skip + ++i; + continue; } } - else // 아랍외 다른 지역. + + // Only check user-typed text (step 0 = normal text) + // SKIP hyperlink visible text (step 2) to prevent hyperlink language from affecting direction + if (hyperlinkStep == 0) { - int x = 0; - int len; - int hyperlinkStep = 0; - SHyperlink kHyperlink; - std::wstring hyperlinkBuffer; - - for (int i = 0; i < wTextLen; ) + if (IsRTLCodepoint(wTextBuf[i])) { - int ret = GetTextTag(&wText[i], wTextLen - i, len, hyperlinkBuffer); - - if (ret == TEXT_TAG_PLAIN || ret == TEXT_TAG_TAG) - { - if (hyperlinkStep == 1) - hyperlinkBuffer.append(1, wText[i]); - else - { - int charWidth = __DrawCharacter(pFontTexture, dataCodePage, wText[i], dwColor); - kHyperlink.ex += charWidth; - x += charWidth; - } - } - else - { - if (ret == TEXT_TAG_COLOR) - dwColor = htoi(hyperlinkBuffer.c_str(), 8); - else if (ret == TEXT_TAG_RESTORE_COLOR) - dwColor = m_dwTextColor; - else if (ret == TEXT_TAG_HYPERLINK_START) - { - hyperlinkStep = 1; - hyperlinkBuffer = L""; - } - else if (ret == TEXT_TAG_HYPERLINK_END) - { - if (hyperlinkStep == 1) - { - ++hyperlinkStep; - kHyperlink.ex = kHyperlink.sx = x; // 실제 텍스트가 시작되는 위치 - } - else - { - kHyperlink.text = hyperlinkBuffer; - m_hyperlinkVector.push_back(kHyperlink); - - hyperlinkStep = 0; - hyperlinkBuffer = L""; - } - } - } - i += len; + userTextIsRTL = true; + foundUserText = true; + break; + } + if (IsStrongAlpha(wTextBuf[i])) + { + userTextIsRTL = false; + foundUserText = true; + break; } } } + } - if (token < end) - { - int newCodePage = ReadToken(token); - dataCodePage = newCodePage; // 아랍 및 베트남 내부 데이터를 UTF8 을 사용하려 했으나 실패 - begin = token + 5; + // Base direction for BiDi algorithm (for non-hyperlink text reordering) + const bool baseRTL = + (m_direction == ETextDirection::RTL) ? true : + (m_direction == ETextDirection::LTR) ? false : + userTextIsRTL; + + // Computed direction for rendering and alignment + // Always use baseRTL to respect the UI direction setting + // In RTL UI, all text (input and display) should use RTL alignment + m_computedRTL = baseRTL; + + // Secret: draw '*' but keep direction + if (m_isSecret) + { + for (int i = 0; i < wTextLen; ++i) + __DrawCharacter(pFontTexture, L'*', defaultColor); + + pFontTexture->UpdateTexture(); + m_isUpdate = true; + return; + } + + const bool hasTags = (std::find(wTextBuf.begin(), wTextBuf.begin() + wTextLen, L'|') != (wTextBuf.begin() + wTextLen)); + + // ======================================================================== + // Case 1: No tags - Simple BiDi reordering + // ======================================================================== + if (!hasTags) + { + // Build identity mapping (logical == visual for tagless text) + const size_t mappingSize = (size_t)wTextLen + 1; + m_logicalToVisualPos.resize(mappingSize); + m_visualToLogicalPos.resize(mappingSize); + for (int i = 0; i <= wTextLen; ++i) + { + m_logicalToVisualPos[i] = i; + m_visualToLogicalPos[i] = i; + } + + // Check for RTL characters and chat message format in single pass + bool hasRTL = false; + bool isChatMessage = false; + const wchar_t* wTextPtr = wTextBuf.data(); + + for (int i = 0; i < wTextLen; ++i) + { + if (!hasRTL && IsRTLCodepoint(wTextPtr[i])) + hasRTL = true; + + if (!isChatMessage && i < wTextLen - 2 && + wTextPtr[i] == L' ' && wTextPtr[i + 1] == L':' && wTextPtr[i + 2] == L' ') + isChatMessage = true; + + // Early exit if both found + if (hasRTL && isChatMessage) + break; + } + + // Apply BiDi if text contains RTL OR is a chat message in RTL UI + // Skip BiDi for regular input text like :)) in RTL UI + if (hasRTL || (baseRTL && isChatMessage)) + { + std::vector visual = BuildVisualBidiText_Tagless(wTextBuf.data(), wTextLen, baseRTL); + for (size_t i = 0; i < visual.size(); ++i) + __DrawCharacter(pFontTexture, visual[i], defaultColor); } else { - begin = token; + // Pure LTR text or non-chat input - no BiDi processing + for (int i = 0; i < wTextLen; ++i) + __DrawCharacter(pFontTexture, wTextBuf[i], defaultColor); + } + } + // ======================================================================== + // Case 2: Has tags - Parse tags and apply BiDi to segments + // ======================================================================== + else + { + // Check if text contains RTL characters (cache pointer for performance) + bool hasRTL = false; + const wchar_t* wTextPtr = wTextBuf.data(); + for (int i = 0; i < wTextLen; ++i) + { + if (IsRTLCodepoint(wTextPtr[i])) + { + hasRTL = true; + break; + } + } + struct TVisChar + { + wchar_t ch; + DWORD color; + int linkIndex; // -1 = none, otherwise index into linkTargets + }; + + auto ReorderTaggedWithBidi = [&](std::vector& vis, bool forceRTL) + { + if (vis.empty()) + return; + + // Extract only characters + std::vector buf; + buf.reserve(vis.size()); + for (const auto& vc : vis) + buf.push_back(vc.ch); + + // Use the exact same BiDi engine as tagless text + std::vector visual = BuildVisualBidiText_Tagless(buf.data(), (int)buf.size(), forceRTL); + + // If size differs (rare, but can happen with Arabic shaping expansion), + // do a safe best-effort resize while preserving style. + if ((int)visual.size() != (int)vis.size()) + { + // Keep style from nearest original character + std::vector resized; + resized.reserve(visual.size()); + + if (vis.empty()) + return; + + for (size_t i = 0; i < visual.size(); ++i) + { + size_t src = (i < vis.size()) ? i : (vis.size() - 1); + TVisChar tmp = vis[src]; + tmp.ch = visual[i]; + resized.push_back(tmp); + } + vis.swap(resized); + return; + } + + // Same size: just write back characters, keep color + linkIndex intact + for (size_t i = 0; i < vis.size(); ++i) + vis[i].ch = visual[i]; + }; + + DWORD curColor = defaultColor; + + // hyperlinkStep: 0=none, 1=collecting target after |H, 2=visible section between |h and |h + int hyperlinkStep = 0; + std::wstring hyperlinkTarget; + hyperlinkTarget.reserve(64); // Reserve typical hyperlink target size + int activeLinkIndex = -1; + + std::vector linkTargets; // linkTargets[i] is target text for link i + linkTargets.reserve(4); // Reserve space for typical number of links + + std::vector logicalVis; + logicalVis.reserve((size_t)wTextLen); // Reserve max possible size + + // Build logical->visual position mapping (reserve to avoid reallocation) + const size_t mappingSize = (size_t)wTextLen + 1; + m_logicalToVisualPos.resize(mappingSize, 0); + + // ==================================================================== + // PHASE 1: Parse tags and collect visible characters + // ==================================================================== + int tagLen = 1; + std::wstring tagExtra; + + for (int i = 0; i < wTextLen; ) + { + m_logicalToVisualPos[i] = (int)logicalVis.size(); + + tagExtra.clear(); + int ret = GetTextTag(&wTextBuf[i], wTextLen - i, tagLen, tagExtra); + if (tagLen <= 0) tagLen = 1; + + if (ret == TEXT_TAG_PLAIN) + { + wchar_t ch = wTextBuf[i]; + + if (hyperlinkStep == 1) + { + // Collect hyperlink target text between |H and first |h + hyperlinkTarget.push_back(ch); + } + else + { + // Regular visible character + logicalVis.push_back(TVisChar{ ch, curColor, activeLinkIndex }); + } + + i += 1; + continue; + } + + // Tag handling + if (ret == TEXT_TAG_COLOR) + { + curColor = htoi(tagExtra.c_str(), 8); + } + else if (ret == TEXT_TAG_RESTORE_COLOR) + { + curColor = defaultColor; + } + else if (ret == TEXT_TAG_HYPERLINK_START) + { + hyperlinkStep = 1; + hyperlinkTarget.clear(); + activeLinkIndex = -1; + } + else if (ret == TEXT_TAG_HYPERLINK_END) + { + if (hyperlinkStep == 1) + { + // End metadata => start visible section + hyperlinkStep = 2; + + linkTargets.push_back(hyperlinkTarget); + activeLinkIndex = (int)linkTargets.size() - 1; + } + else if (hyperlinkStep == 2) + { + // End visible section + hyperlinkStep = 0; + activeLinkIndex = -1; + hyperlinkTarget.clear(); + } + } + + i += tagLen; + } + + // Complete position mappings + const int logicalVisSize = (int)logicalVis.size(); + m_logicalToVisualPos[wTextLen] = logicalVisSize; + + m_visualToLogicalPos.resize((size_t)logicalVisSize + 1); + const size_t logicalToVisualSize = m_logicalToVisualPos.size(); + const int visualToLogicalSize = (int)m_visualToLogicalPos.size(); + + for (size_t i = 0; i < logicalToVisualSize; ++i) + { + const int visualPos = m_logicalToVisualPos[i]; + if (visualPos >= 0 && visualPos < visualToLogicalSize) + m_visualToLogicalPos[visualPos] = (int)i; + } + + // ==================================================================== + // PHASE 2: Apply BiDi to hyperlinks (if RTL text or RTL UI) + // ==================================================================== + if (hasRTL || baseRTL) + { + // Collect all hyperlink ranges (reserve typical count) + struct LinkRange { int start; int end; int linkIdx; }; + std::vector linkRanges; + linkRanges.reserve(linkTargets.size()); + + int currentLink = -1; + int linkStart = -1; + const int logicalVisCount = (int)logicalVis.size(); + + for (int i = 0; i <= logicalVisCount; ++i) + { + const int linkIdx = (i < logicalVisCount) ? logicalVis[i].linkIndex : -1; + + if (linkIdx != currentLink) + { + if (currentLink >= 0 && linkStart >= 0) + { + linkRanges.push_back({linkStart, i, currentLink}); + } + + currentLink = linkIdx; + linkStart = (currentLink >= 0) ? i : -1; + } + } + + // Process hyperlinks in reverse order to avoid index shifting + const int numRanges = (int)linkRanges.size(); + for (int rangeIdx = numRanges - 1; rangeIdx >= 0; --rangeIdx) + { + const LinkRange& range = linkRanges[rangeIdx]; + const int linkStart = range.start; + const int linkEnd = range.end; + const int linkLength = linkEnd - linkStart; + + // Extract hyperlink text (pre-reserve exact size) + std::vector linkBuf; + linkBuf.reserve(linkLength); + for (int j = linkStart; j < linkEnd; ++j) + linkBuf.push_back(logicalVis[j].ch); + + // Apply BiDi with LTR base direction (hyperlinks use LTR structure like [+9 item]) + std::vector linkVisual = BuildVisualBidiText_Tagless(linkBuf.data(), (int)linkBuf.size(), false); + + // Normalize brackets and enhancement markers + const int linkVisualSize = (int)linkVisual.size(); + if (linkVisualSize > 0) + { + // Find first '[' and first ']' (cache size) + int openBracket = -1, closeBracket = -1; + for (int j = 0; j < linkVisualSize; ++j) + { + if (linkVisual[j] == L'[' && openBracket < 0) openBracket = j; + if (linkVisual[j] == L']' && closeBracket < 0) closeBracket = j; + } + + // Case 1: Brackets are reversed "]text[" => "[text]" + if (closeBracket >= 0 && openBracket > closeBracket) + { + std::vector normalized; + normalized.reserve(linkVisual.size()); + + // Rebuild: [ + (before ]) + (between ] and [) + (after [) + ] + normalized.push_back(L'['); + + for (int j = 0; j < closeBracket; ++j) + normalized.push_back(linkVisual[j]); + + for (int j = closeBracket + 1; j < openBracket; ++j) + normalized.push_back(linkVisual[j]); + + for (int j = openBracket + 1; j < (int)linkVisual.size(); ++j) + normalized.push_back(linkVisual[j]); + + normalized.push_back(L']'); + + linkVisual = normalized; + openBracket = 0; + closeBracket = (int)linkVisual.size() - 1; + } + + // Case 2: Normal brackets "[...]" - check for normalization + if (openBracket >= 0 && closeBracket > openBracket) + { + int pos = openBracket + 1; + + // Skip leading spaces inside brackets + while (pos < closeBracket && linkVisual[pos] == L' ') + { + linkVisual.erase(linkVisual.begin() + pos); + closeBracket--; + } + + // Check for "+" pattern and reverse to "+" + if (pos < closeBracket && linkVisual[pos] == L'+') + { + int digitStart = pos + 1; + int digitEnd = digitStart; + + while (digitEnd < closeBracket && (linkVisual[digitEnd] >= L'0' && linkVisual[digitEnd] <= L'9')) + digitEnd++; + + if (digitEnd > digitStart) + { + wchar_t plus = L'+'; + for (int k = pos; k < digitEnd - 1; ++k) + linkVisual[k] = linkVisual[k + 1]; + linkVisual[digitEnd - 1] = plus; + } + } + } + } + + // Write back - handle size changes by erasing/inserting + const int originalSize = linkLength; + const int newSize = linkVisualSize; + const int sizeDiff = newSize - originalSize; + + // Replace existing characters (cache min for performance) + const int copyCount = (std::min)(originalSize, newSize); + for (int j = 0; j < copyCount; ++j) + logicalVis[linkStart + j].ch = linkVisual[j]; + + if (sizeDiff < 0) + { + // Shrunk - remove extra characters + logicalVis.erase(logicalVis.begin() + linkStart + newSize, + logicalVis.begin() + linkStart + originalSize); + } + else if (sizeDiff > 0) + { + // Grew - insert new characters + TVisChar templateChar = logicalVis[linkStart]; + for (int j = originalSize; j < newSize; ++j) + { + templateChar.ch = linkVisual[j]; + logicalVis.insert(logicalVis.begin() + linkStart + j, templateChar); + } + } + } + } + + // Apply BiDi to non-hyperlink segments and reorder segments for RTL UI + if (hasRTL || baseRTL) + { + // Split text into hyperlink and non-hyperlink segments (reserve typical count) + const size_t estimatedSegments = linkTargets.size() * 2 + 1; + std::vector> segments; + segments.reserve(estimatedSegments); // Estimate: links + text between + + std::vector isHyperlink; // true if segment is a hyperlink + isHyperlink.reserve(estimatedSegments); + + int segStart = 0; + int currentLinkIdx = (logicalVis.empty() ? -1 : logicalVis[0].linkIndex); + const int logicalVisSize2 = (int)logicalVis.size(); + + for (int i = 1; i <= logicalVisSize2; ++i) + { + const int linkIdx = (i < logicalVisSize2) ? logicalVis[i].linkIndex : -1; + + if (linkIdx != currentLinkIdx) + { + // Segment boundary + std::vector seg(logicalVis.begin() + segStart, logicalVis.begin() + i); + segments.push_back(seg); + isHyperlink.push_back(currentLinkIdx >= 0); + + segStart = i; + currentLinkIdx = linkIdx; + } + } + + // Apply BiDi to non-hyperlink segments only (cache segment count) + const size_t numSegments = segments.size(); + for (size_t s = 0; s < numSegments; ++s) + { + if (!isHyperlink[s]) + ReorderTaggedWithBidi(segments[s], baseRTL); + } + + // Rebuild text from segments (reverse order for RTL UI) + logicalVis.clear(); + logicalVis.reserve(logicalVisSize2); // Reserve original size + + if (baseRTL) + { + // RTL UI - reverse segments for right-to-left reading + for (int s = (int)numSegments - 1; s >= 0; --s) + { + logicalVis.insert(logicalVis.end(), segments[s].begin(), segments[s].end()); + } + } + else + { + // LTR UI - keep original segment order + for (size_t s = 0; s < numSegments; ++s) + { + logicalVis.insert(logicalVis.end(), segments[s].begin(), segments[s].end()); + } + } + } + + // ==================================================================== + // PHASE 3: Render and build hyperlink ranges + // ==================================================================== + m_hyperlinkVector.clear(); + m_hyperlinkVector.reserve(linkTargets.size()); // Reserve for known hyperlinks + + int x = 0; + int currentLink = -1; + SHyperlink curLinkRange{}; + curLinkRange.sx = 0; + curLinkRange.ex = 0; + + // Cache size for loop (avoid repeated size() calls) + const size_t logicalVisRenderSize = logicalVis.size(); + for (size_t idx = 0; idx < logicalVisRenderSize; ++idx) + { + const TVisChar& vc = logicalVis[idx]; + const int charWidth = __DrawCharacter(pFontTexture, vc.ch, vc.color); + + // Hyperlink range tracking + const int linkIdx = vc.linkIndex; + + if (linkIdx != currentLink) + { + // Close previous hyperlink + if (currentLink >= 0) + { + curLinkRange.text = linkTargets[(size_t)currentLink]; + m_hyperlinkVector.push_back(curLinkRange); + } + + // Open new hyperlink + currentLink = linkIdx; + if (currentLink >= 0) + { + curLinkRange = SHyperlink{}; + curLinkRange.sx = (short)x; + curLinkRange.ex = (short)x; + } + } + + if (currentLink >= 0) + { + curLinkRange.ex = (short)(curLinkRange.ex + charWidth); + } + + x += charWidth; + } + + // Close last hyperlink + if (currentLink >= 0) + { + curLinkRange.text = linkTargets[(size_t)currentLink]; + m_hyperlinkVector.push_back(curLinkRange); } } pFontTexture->UpdateTexture(); - m_isUpdate = true; } void CGraphicTextInstance::Render(RECT * pClipRect) { if (!m_isUpdate) - return; + return; CGraphicText* pkText=m_roText.GetPointer(); if (!pkText) @@ -492,9 +729,8 @@ void CGraphicTextInstance::Render(RECT * pClipRect) float fStanX = m_v3Position.x; float fStanY = m_v3Position.y + 1.0f; - UINT defCodePage = GetDefaultCodePage(); - - if (defCodePage == CP_ARABIC) + // Use the computed direction for this text instance, not the global UI direction + if (m_computedRTL) { switch (m_hAlign) { @@ -504,11 +740,11 @@ void CGraphicTextInstance::Render(RECT * pClipRect) case HORIZONTAL_ALIGN_CENTER: fStanX -= float(m_textWidth / 2); - break; + break; } } else - { + { switch (m_hAlign) { case HORIZONTAL_ALIGN_RIGHT: @@ -517,7 +753,7 @@ void CGraphicTextInstance::Render(RECT * pClipRect) case HORIZONTAL_ALIGN_CENTER: fStanX -= float(m_textWidth / 2); - break; + break; } } @@ -573,9 +809,8 @@ void CGraphicTextInstance::Render(RECT * pClipRect) akVertex[2].z=m_v3Position.z; akVertex[3].z=m_v3Position.z; - CGraphicFontTexture::TCharacterInfomation* pCurCharInfo; + CGraphicFontTexture::TCharacterInfomation* pCurCharInfo; - // 테두리 if (m_isOutline) { fCurX=fStanX; @@ -591,7 +826,6 @@ void CGraphicTextInstance::Render(RECT * pClipRect) fFontHeight=float(pCurCharInfo->height); fFontAdvance=float(pCurCharInfo->advance); - // NOTE : 폰트 출력에 Width 제한을 둡니다. - [levites] if ((fCurX+fFontWidth)-m_v3Position.x > m_fLimitWidth) { if (m_isMultiLine) @@ -633,9 +867,8 @@ void CGraphicTextInstance::Render(RECT * pClipRect) akVertex[3].color = akVertex[2].color = akVertex[1].color = akVertex[0].color = m_dwOutLineColor; - float feather = 0.0f; // m_fFontFeather - + akVertex[0].y=fFontSy-feather; akVertex[1].y=fFontEy+feather; akVertex[2].y=fFontSy-feather; @@ -648,7 +881,6 @@ void CGraphicTextInstance::Render(RECT * pClipRect) akVertex[3].x=fFontEx-fFontHalfWeight+feather; vtxBatch.insert(vtxBatch.end(), std::begin(akVertex), std::end(akVertex)); - // 오른 akVertex[0].x=fFontSx+fFontHalfWeight-feather; @@ -657,12 +889,12 @@ void CGraphicTextInstance::Render(RECT * pClipRect) akVertex[3].x=fFontEx+fFontHalfWeight+feather; vtxBatch.insert(vtxBatch.end(), std::begin(akVertex), std::end(akVertex)); - + akVertex[0].x=fFontSx-feather; akVertex[1].x=fFontSx-feather; akVertex[2].x=fFontEx+feather; akVertex[3].x=fFontEx+feather; - + // 위 akVertex[0].y=fFontSy-fFontHalfWeight-feather; akVertex[1].y=fFontEy-fFontHalfWeight+feather; @@ -670,7 +902,7 @@ void CGraphicTextInstance::Render(RECT * pClipRect) akVertex[3].y=fFontEy-fFontHalfWeight+feather; vtxBatch.insert(vtxBatch.end(), std::begin(akVertex), std::end(akVertex)); - + // 아래 akVertex[0].y=fFontSy+fFontHalfWeight-feather; akVertex[1].y=fFontEy+fFontHalfWeight+feather; @@ -678,12 +910,11 @@ void CGraphicTextInstance::Render(RECT * pClipRect) akVertex[3].y=fFontEy+fFontHalfWeight+feather; vtxBatch.insert(vtxBatch.end(), std::begin(akVertex), std::end(akVertex)); - + fCurX += fFontAdvance; } } - // 메인 폰트 fCurX=fStanX; fCurY=fStanY; fFontMaxHeight=0.0f; @@ -694,10 +925,9 @@ void CGraphicTextInstance::Render(RECT * pClipRect) fFontWidth=float(pCurCharInfo->width); fFontHeight=float(pCurCharInfo->height); - fFontMaxHeight=std::max(fFontHeight, (float)pCurCharInfo->height); + fFontMaxHeight=(std::max)(fFontHeight, (float)pCurCharInfo->height); fFontAdvance=float(pCurCharInfo->advance); - // NOTE : 폰트 출력에 Width 제한을 둡니다. - [levites] if ((fCurX+fFontWidth)-m_v3Position.x > m_fLimitWidth) { if (m_isMultiLine) @@ -756,12 +986,116 @@ void CGraphicTextInstance::Render(RECT * pClipRect) } } + // --- Selection background (Ctrl+A / shift-select) --- + if (m_isCursor && CIME::ms_bCaptureInput) + { + int selBegin = CIME::GetSelBegin(); + int selEnd = CIME::GetSelEnd(); + + if (selBegin > selEnd) std::swap(selBegin, selEnd); + + if (selBegin != selEnd) + { + float sx, sy, ex, ey; + + // Convert logical selection positions to visual positions (handles tags) + int visualSelBegin = selBegin; + int visualSelEnd = selEnd; + if (!m_logicalToVisualPos.empty()) + { + if (selBegin >= 0 && selBegin < (int)m_logicalToVisualPos.size()) + visualSelBegin = m_logicalToVisualPos[selBegin]; + if (selEnd >= 0 && selEnd < (int)m_logicalToVisualPos.size()) + visualSelEnd = m_logicalToVisualPos[selEnd]; + } + + __GetTextPos(visualSelBegin, &sx, &sy); + __GetTextPos(visualSelEnd, &ex, &sy); + + // Handle RTL - use the computed direction for this text instance + if (m_computedRTL) + { + sx += m_v3Position.x - m_textWidth; + ex += m_v3Position.x - m_textWidth; + } + else + { + sx += m_v3Position.x; + ex += m_v3Position.x; + } + + // Apply vertical alignment + float top = m_v3Position.y; + float bot = m_v3Position.y + m_textHeight; + + switch (m_vAlign) + { + case VERTICAL_ALIGN_BOTTOM: + top -= m_textHeight; + bot -= m_textHeight; + break; + + case VERTICAL_ALIGN_CENTER: + top -= float(m_textHeight) / 2.0f; + bot -= float(m_textHeight) / 2.0f; + break; + } + + TPDTVertex vertices[4]; + vertices[0].diffuse = 0x80339CFF; + vertices[1].diffuse = 0x80339CFF; + vertices[2].diffuse = 0x80339CFF; + vertices[3].diffuse = 0x80339CFF; + + vertices[0].position = TPosition(sx, top, 0.0f); + vertices[1].position = TPosition(ex, top, 0.0f); + vertices[2].position = TPosition(sx, bot, 0.0f); + vertices[3].position = TPosition(ex, bot, 0.0f); + + STATEMANAGER.SetTexture(0, NULL); + CGraphicBase::SetDefaultIndexBuffer(CGraphicBase::DEFAULT_IB_FILL_RECT); + if (CGraphicBase::SetPDTStream(vertices, 4)) + STATEMANAGER.DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 4, 0, 2); + } + } + for (const auto& [pTexture, vtxBatch] : s_vtxBatches) { if (vtxBatch.empty()) continue; STATEMANAGER.SetTexture(0, pTexture); - STATEMANAGER.DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, vtxBatch.size() - 2, vtxBatch.data(), sizeof(SVertex)); + + // Each character is 4 vertices forming a quad (0=TL, 1=BL, 2=TR, 3=BR) + // We need to convert quads to triangle list format + // Triangle list needs 6 vertices per quad: v0,v1,v2, v2,v1,v3 + + size_t numQuads = vtxBatch.size() / 4; + std::vector triangleVerts; + triangleVerts.reserve(numQuads * 6); + + for (size_t i = 0; i < numQuads; ++i) + { + size_t baseIdx = i * 4; + const SVertex& v0 = vtxBatch[baseIdx + 0]; // TL + const SVertex& v1 = vtxBatch[baseIdx + 1]; // BL + const SVertex& v2 = vtxBatch[baseIdx + 2]; // TR + const SVertex& v3 = vtxBatch[baseIdx + 3]; // BR + + // First triangle: TL, BL, TR + triangleVerts.push_back(v0); + triangleVerts.push_back(v1); + triangleVerts.push_back(v2); + + // Second triangle: TR, BL, BR + triangleVerts.push_back(v2); + triangleVerts.push_back(v1); + triangleVerts.push_back(v3); + } + + if (!triangleVerts.empty()) + { + STATEMANAGER.DrawPrimitiveUP(D3DPT_TRIANGLELIST, triangleVerts.size() / 3, triangleVerts.data(), sizeof(SVertex)); + } } if (m_isCursor) @@ -773,13 +1107,24 @@ void CGraphicTextInstance::Render(RECT * pClipRect) int curpos = CIME::GetCurPos(); int compend = curpos + CIME::GetCompLen(); - __GetTextPos(curpos, &sx, &sy); + // Convert logical cursor position to visual position (handles tags) + int visualCurpos = curpos; + int visualCompend = compend; + if (!m_logicalToVisualPos.empty()) + { + if (curpos >= 0 && curpos < (int)m_logicalToVisualPos.size()) + visualCurpos = m_logicalToVisualPos[curpos]; + if (compend >= 0 && compend < (int)m_logicalToVisualPos.size()) + visualCompend = m_logicalToVisualPos[compend]; + } + + __GetTextPos(visualCurpos, &sx, &sy); // If Composition - if(curpos DynamicVertexBuffer CGraphicBase::SetDefaultIndexBuffer(CGraphicBase::DEFAULT_IB_FILL_RECT); if (CGraphicBase::SetPDTStream(vertices, 4)) STATEMANAGER.DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 4, 0, 2); @@ -838,7 +1181,7 @@ void CGraphicTextInstance::Render(RECT * pClipRect) int ulbegin = CIME::GetULBegin(); int ulend = CIME::GetULEnd(); - if(ulbegin < ulend) + if (ulbegin < ulend) { __GetTextPos(curpos+ulbegin, &sx, &sy); __GetTextPos(curpos+ulend, &ex, &sy); @@ -858,7 +1201,7 @@ void CGraphicTextInstance::Render(RECT * pClipRect) vertices[3].position = TPosition(ex, ey, 0.0f); STATEMANAGER.DrawIndexedPrimitiveUP(D3DPT_TRIANGLELIST, 0, 4, 2, c_FillRectIndices, D3DFMT_INDEX16, vertices, sizeof(TPDTVertex)); - } + } } STATEMANAGER.RestoreRenderState(D3DRS_SRCBLEND); @@ -867,17 +1210,14 @@ void CGraphicTextInstance::Render(RECT * pClipRect) STATEMANAGER.SetRenderState(D3DRS_FOGENABLE, dwFogEnable); STATEMANAGER.SetRenderState(D3DRS_LIGHTING, dwLighting); - //금강경 링크 띄워주는 부분. if (m_hyperlinkVector.size() != 0) { - int lx = gs_mx - m_v3Position.x; - int ly = gs_my - m_v3Position.y; + // FOR_ARABIC_ALIGN: RTL text is drawn with offset (m_v3Position.x - m_textWidth) + // Use the computed direction for this text instance, not the global UI direction + int textOffsetX = m_computedRTL ? (int)(m_v3Position.x - m_textWidth) : (int)m_v3Position.x; - //아랍은 좌표 부호를 바꿔준다. - if (GetDefaultCodePage() == CP_ARABIC) { - lx = -lx; - ly = -ly + m_textHeight; - } + int lx = gs_mx - textOffsetX; + int ly = gs_my - (int)m_v3Position.y; if (lx >= 0 && ly >= 0 && lx < m_textWidth && ly < m_textHeight) { @@ -889,10 +1229,6 @@ void CGraphicTextInstance::Render(RECT * pClipRect) if (lx >= link.sx && lx < link.ex) { gs_hyperlinkText = link.text; - /* - OutputDebugStringW(link.text.c_str()); - OutputDebugStringW(L"\n"); - */ break; } } @@ -1045,6 +1381,15 @@ void CGraphicTextInstance::SetTextPointer(CGraphicText* pText) m_roText = pText; } +void CGraphicTextInstance::SetTextDirection(ETextDirection dir) +{ + if (m_direction == dir) + return; + + m_direction = dir; + m_isUpdate = false; +} + const std::string & CGraphicTextInstance::GetValueStringReference() { return m_stText; @@ -1086,17 +1431,42 @@ void CGraphicTextInstance::GetTextSize(int* pRetWidth, int* pRetHeight) int CGraphicTextInstance::PixelPositionToCharacterPosition(int iPixelPosition) { + // iPixelPosition is relative to the window + // Character positions in m_pCharInfoVector are relative to text start (0,0) + // We need to find which character the pixel falls on + + // Clamp to valid range [0, textWidth] + int adjustedPixelPos = iPixelPosition; + if (adjustedPixelPos < 0) + adjustedPixelPos = 0; + if (adjustedPixelPos > m_textWidth) + adjustedPixelPos = m_textWidth; + + // Find the character at the pixel position int icurPosition = 0; + int visualPos = -1; + for (int i = 0; i < (int)m_pCharInfoVector.size(); ++i) { CGraphicFontTexture::TCharacterInfomation* pCurCharInfo = m_pCharInfoVector[i]; icurPosition += pCurCharInfo->width; - if (iPixelPosition < icurPosition) - return i; + if (adjustedPixelPos < icurPosition) + { + visualPos = i; + break; + } } - return -1; + // If not found, use end position + if (visualPos < 0) + visualPos = (int)m_pCharInfoVector.size(); + + // Convert visual position to logical position (accounting for tags) + if (!m_visualToLogicalPos.empty() && visualPos >= 0 && visualPos < (int)m_visualToLogicalPos.size()) + return m_visualToLogicalPos[visualPos]; + + return visualPos; } int CGraphicTextInstance::GetHorizontalAlign() @@ -1112,7 +1482,7 @@ void CGraphicTextInstance::__Initialize() m_vAlign = VERTICAL_ALIGN_TOP; m_iMax = 0; - m_fLimitWidth = 1600.0f; // NOTE : 해상도의 최대치. 이보다 길게 쓸 일이 있을까? - [levites] + m_fLimitWidth = 1600.0f; m_isCursor = false; m_isSecret = false; @@ -1122,6 +1492,10 @@ void CGraphicTextInstance::__Initialize() m_fFontFeather = c_fFontFeather; m_isUpdate = false; + // Use Auto direction for input fields - they should auto-detect based on content + // Only chat messages should be explicitly set to RTL + m_direction = ETextDirection::Auto; + m_computedRTL = false; m_textWidth = 0; m_textHeight = 0; @@ -1137,6 +1511,8 @@ void CGraphicTextInstance::Destroy() m_pCharInfoVector.clear(); m_dwColorInfoVector.clear(); m_hyperlinkVector.clear(); + m_logicalToVisualPos.clear(); + m_visualToLogicalPos.clear(); __Initialize(); } diff --git a/src/EterLib/GrpTextInstance.h b/src/EterLib/GrpTextInstance.h index a87fa33..72462c1 100644 --- a/src/EterLib/GrpTextInstance.h +++ b/src/EterLib/GrpTextInstance.h @@ -16,6 +16,7 @@ class CGraphicTextInstance HORIZONTAL_ALIGN_CENTER = 0x02, HORIZONTAL_ALIGN_RIGHT = 0x03, }; + enum EVerticalAlign { VERTICAL_ALIGN_TOP = 0x10, @@ -23,6 +24,13 @@ class CGraphicTextInstance VERTICAL_ALIGN_BOTTOM = 0x30 }; + enum class ETextDirection : unsigned char + { + Auto = 0, + LTR = 1, + RTL = 2, + }; + public: static void Hyperlink_UpdateMousePos(int x, int y); static int Hyperlink_GetText(char* buf, int len); @@ -65,14 +73,24 @@ class CGraphicTextInstance const std::string& GetValueStringReference(); WORD GetTextLineCount(); + void SetTextDirection(ETextDirection dir); + ETextDirection GetTextDirection() const { return m_direction; } + bool IsRTL() const + { + // For AUTO mode, use computed result from BiDi analysis + if (m_direction == ETextDirection::Auto) + return m_computedRTL; + // For explicit direction, derive from m_direction + return (m_direction == ETextDirection::RTL); + } + int PixelPositionToCharacterPosition(int iPixelPosition); int GetHorizontalAlign(); protected: void __Initialize(); - int __DrawCharacter(CGraphicFontTexture * pFontTexture, WORD codePage, wchar_t text, DWORD dwColor); + int __DrawCharacter(CGraphicFontTexture * pFontTexture, wchar_t text, DWORD dwColor); void __GetTextPos(DWORD index, float* x, float* y); - int __GetTextTag(const wchar_t * src, int maxLen, int & tagLen, std::wstring & extraInfo); protected: struct SHyperlink @@ -112,11 +130,15 @@ class CGraphicTextInstance private: bool m_isUpdate; bool m_isUpdateFontTexture; - + bool m_computedRTL; // Result of BiDi analysis (used when m_direction == Auto) + CGraphicText::TRef m_roText; CGraphicFontTexture::TPCharacterInfomationVector m_pCharInfoVector; std::vector m_dwColorInfoVector; std::vector m_hyperlinkVector; + std::vector m_logicalToVisualPos; // Maps logical cursor pos (UTF-16 with tags) to visual pos (rendered chars) + std::vector m_visualToLogicalPos; // Reverse mapping: visual pos -> logical pos + ETextDirection m_direction = ETextDirection::Auto; // Will be overwritten by __Initialize() public: static void CreateSystem(UINT uCapacity); @@ -125,10 +147,8 @@ class CGraphicTextInstance static CGraphicTextInstance* New(); static void Delete(CGraphicTextInstance* pkInst); - static CDynamicPool ms_kPool; + static CDynamicPool ms_kPool; }; -extern const char* FindToken(const char* begin, const char* end); -extern int ReadToken(const char* token); #endif \ No newline at end of file diff --git a/src/EterLib/GrpVertexShader.cpp b/src/EterLib/GrpVertexShader.cpp index a34c0af..08aee8c 100644 --- a/src/EterLib/GrpVertexShader.cpp +++ b/src/EterLib/GrpVertexShader.cpp @@ -3,6 +3,8 @@ #include "GrpD3DXBuffer.h" #include "StateManager.h" +#include + CVertexShader::CVertexShader() { Initialize(); @@ -29,21 +31,44 @@ void CVertexShader::Destroy() bool CVertexShader::CreateFromDiskFile(const char* c_szFileName, const DWORD* c_pdwVertexDecl) { - Destroy(); + Destroy(); - LPD3DXBUFFER lpd3dxShaderBuffer; - LPD3DXBUFFER lpd3dxErrorBuffer; - - if (FAILED( - D3DXAssembleShaderFromFile(c_szFileName, 0, NULL, 0, &lpd3dxShaderBuffer, &lpd3dxErrorBuffer) - )) return false; + if (!c_szFileName || !*c_szFileName) + return false; - if (FAILED( - ms_lpd3dDevice->CreateVertexShader((const DWORD*)lpd3dxShaderBuffer->GetBufferPointer(), &m_handle) - )) - return false; + // UTF-8 → UTF-16 for D3DX + std::wstring wFileName = Utf8ToWide(c_szFileName); - return true; + LPD3DXBUFFER lpd3dxShaderBuffer = nullptr; + LPD3DXBUFFER lpd3dxErrorBuffer = nullptr; + + HRESULT hr = D3DXAssembleShaderFromFileW( + wFileName.c_str(), + nullptr, + nullptr, + 0, + &lpd3dxShaderBuffer, + &lpd3dxErrorBuffer + ); + + if (FAILED(hr)) + { + if (lpd3dxErrorBuffer) + { + const char* err = (const char*)lpd3dxErrorBuffer->GetBufferPointer(); + TraceError("Vertex shader compile error: %s", err); + } + return false; + } + + if (FAILED( + ms_lpd3dDevice->CreateVertexShader( + (const DWORD*)lpd3dxShaderBuffer->GetBufferPointer(), + &m_handle + ))) + return false; + + return true; } void CVertexShader::Set() diff --git a/src/EterLib/IME.cpp b/src/EterLib/IME.cpp index 57e0f8c..aaebce4 100644 --- a/src/EterLib/IME.cpp +++ b/src/EterLib/IME.cpp @@ -4,6 +4,8 @@ #include "EterBase/Utils.h" #include "msctf.h" #include +#include +#include #define COUNTOF(a) ( sizeof( a ) / sizeof( ( a )[0] ) ) @@ -30,9 +32,9 @@ wchar_t CIME::m_wText[IMESTR_MAXLEN]; #define _CHT_HKL_NEW_QUICK ((HKL)0xE00A0404) // New Quick #define _CHT_HKL_HK_CANTONESE ((HKL)0xE00B0404) // Hong Kong Cantonese -#define CHT_IMEFILENAME1 "TINTLGNT.IME" // New Phonetic -#define CHT_IMEFILENAME2 "CINTLGNT.IME" // New Chang Jie -#define CHT_IMEFILENAME3 "MSTCIPHA.IME" // Phonetic 5.1 +#define CHT_IMEFILENAME1 L"TINTLGNT.IME" // New Phonetic +#define CHT_IMEFILENAME2 L"CINTLGNT.IME" // New Chang Jie +#define CHT_IMEFILENAME3 L"MSTCIPHA.IME" // Phonetic 5.1 #define IMEID_CHT_VER42 (LANG_CHT | MAKEIMEVERSION(4, 2)) // New(Phonetic/ChanJie)IME98 : 4.2.x.x // Win98 #define IMEID_CHT_VER43 (LANG_CHT | MAKEIMEVERSION(4, 3)) // New(Phonetic/ChanJie)IME98a : 4.3.x.x // Win2k @@ -49,8 +51,8 @@ wchar_t CIME::m_wText[IMESTR_MAXLEN]; #define _CHS_HKL_SOGOU ((HKL)0xE0220804) // Sougou PinYin #define _CHS_HKL_GOOGLEPINYIN ((HKL)0xE0230804) // Google PinYin -#define CHS_IMEFILENAME1 "PINTLGNT.IME" // MSPY1.5/2/3 -#define CHS_IMEFILENAME2 "MSSCIPYA.IME" // MSPY3 for OfficeXP +#define CHS_IMEFILENAME1 L"PINTLGNT.IME" // MSPY1.5/2/3 +#define CHS_IMEFILENAME2 L"MSSCIPYA.IME" // MSPY3 for OfficeXP #define CHS_IMEFILENAME_QQPINYIN "QQPINYIN.IME" // QQ PinYin #define CHS_IMEFILENAME_SOGOUPY "SOGOUPY.IME" // Sougou PinYin #define CHS_IMEFILENAME_GOOGLEPINYIN2 "GOOGLEPINYIN2.IME" // Google PinYin 2 @@ -89,10 +91,17 @@ bool CIME::ms_bCaptureInput = false; bool CIME::ms_bChineseIME = false; bool CIME::ms_bUseIMMCandidate = false; +int CIME::ms_selbegin = 0; +int CIME::ms_selend = 0; +int CIME::ms_compCaret = 0; + +std::vector CIME::ms_undo; +std::vector CIME::ms_redo; + HWND CIME::ms_hWnd; HKL CIME::ms_hklCurrent; -char CIME::ms_szKeyboardLayout[KL_NAMELENGTH+1]; -OSVERSIONINFOA CIME::ms_stOSVI; +wchar_t CIME::ms_szKeyboardLayout[KL_NAMELENGTH+1]; +OSVERSIONINFOW CIME::ms_stOSVI; HINSTANCE CIME::ms_hImm32Dll; HINSTANCE CIME::ms_hCurrentImeDll; @@ -109,7 +118,7 @@ bool CIME::ms_bCandidateList; DWORD CIME::ms_dwCandidateCount; bool CIME::ms_bVerticalCandidate; int CIME::ms_iCandListIndexBase; -WCHAR CIME::ms_wszCandidate[CIME::MAX_CANDLIST][MAX_CANDIDATE_LENGTH]; +WCHAR CIME::ms_wszCandidate[CIME::MAX_CANDLIST][CIME::MAX_CANDIDATE_LENGTH]; DWORD CIME::ms_dwCandidateSelection; DWORD CIME::ms_dwCandidatePageSize; @@ -117,7 +126,7 @@ DWORD CIME::ms_dwCandidatePageSize; bool CIME::ms_bReadingInformation; int CIME::ms_iReadingError = 0; bool CIME::ms_bHorizontalReading; -std::vector CIME::ms_wstrReading; +std::vector CIME::ms_wstrReading; // Indicator wchar_t* CIME::ms_wszCurrentIndicator; @@ -127,14 +136,6 @@ IIMEEventSink* CIME::ms_pEvent; int CIME::ms_ulbegin; int CIME::ms_ulend; -UINT CIME::ms_uOutputCodePage = 0; -UINT CIME::ms_uInputCodePage = 0; - -extern DWORD gs_codePage=0; -extern DWORD GetDefaultCodePage(); -extern int ReadToken(const char* token); -extern const char* FindToken(const char* begin, const char* end); - /////////////////////////////////////////////////////////////////////////////// // // CTsfUiLessMode @@ -287,7 +288,7 @@ CIME::CIME() m_bOnlyNumberMode = FALSE; m_hOrgIMC = NULL; - m_bEnablePaste = false; + m_bEnablePaste = true; m_bUseDefaultIME = false; } @@ -296,6 +297,7 @@ CIME::~CIME() SAFE_FREE_LIBRARY(ms_hCurrentImeDll); SAFE_FREE_LIBRARY(ms_hImm32Dll); } + #pragma warning(disable : 4996) bool CIME::Initialize(HWND hWnd) { @@ -305,8 +307,8 @@ bool CIME::Initialize(HWND hWnd) g_disableCicero.Initialize(); - ms_stOSVI.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA); - GetVersionExA(&ms_stOSVI); + ms_stOSVI.dwOSVersionInfoSize = sizeof(OSVERSIONINFOW); + GetVersionExW(&ms_stOSVI); bool bUnicodeImm = false; // IMM in NT or Win98 supports Unicode @@ -317,12 +319,13 @@ bool CIME::Initialize(HWND hWnd) } // Load ImmLock/ImmUnlock Function Proc - CHAR szPath[MAX_PATH+1]; + wchar_t szPath[MAX_PATH + 1]; ms_bDisableIMECompletely = false; - - if(GetSystemDirectoryA(szPath, MAX_PATH+1)) { - strcat(szPath, "\\imm32.dll"); - ms_hImm32Dll = LoadLibraryA(szPath); + + if (GetSystemDirectoryW(szPath, MAX_PATH+1)) + { + wcscat_s(szPath, L"\\imm32.dll"); + ms_hImm32Dll = LoadLibraryW(szPath); if(ms_hImm32Dll) { _ImmLockIMC = (INPUTCONTEXT*(WINAPI *)(HIMC)) GetProcAddress(ms_hImm32Dll, "ImmLockIMC"); @@ -348,7 +351,7 @@ bool CIME::Initialize(HWND hWnd) ms_bUILessMode = CTsfUiLessMode::SetupSinks() != FALSE; CheckToggleState(); - if ( ms_bUILessMode ) + if (ms_bUILessMode) { ms_bChineseIME = ( GETPRIMLANG() == LANG_CHINESE ) && CTsfUiLessMode::CurrentInputLocaleIsIme(); CTsfUiLessMode::UpdateImeState(); @@ -423,17 +426,22 @@ void CIME::Clear() ms_compLen = 0; ms_ulbegin = 0; ms_ulend = 0; + + ms_selbegin = 0; + ms_selend = 0; } int CIME::GetReading(std::string & rstrText) { char reading[IMEREADING_MAXLEN]; - + if(ms_wstrReading.size() == 0) return 0; - int readingLen = WideCharToMultiByte(ms_uOutputCodePage, 0, &ms_wstrReading[0], ms_wstrReading.size(), reading, sizeof(reading), NULL, NULL); - rstrText.append(GetCodePageText()); + int readingLen = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, &ms_wstrReading[0], ms_wstrReading.size(), reading, sizeof(reading), NULL, NULL); + if (readingLen <= 0) + readingLen = 0; + rstrText.append(reading, reading + readingLen); return rstrText.size(); @@ -460,84 +468,74 @@ void CIME::SetText(const char* szText, int len) ms_ulbegin = 0; ms_ulend = 0; - const char* begin = szText; - const char* end = begin + len; - const char* iter = FindToken(begin, end); + if (!szText) + len = 0; - int m_wTextLen = sizeof(m_wText)/sizeof(wchar_t); + if (len < 0 && szText) + len = (int)strlen(szText); - ms_lastpos = MultiByteToWideChar(ms_uInputCodePage, 0, begin, iter-begin, m_wText, m_wTextLen); + const int cap = (int)(sizeof(m_wText) / sizeof(m_wText[0])); // includes space for null + if (cap <= 0) + return; - if (iter < end) - ms_lastpos += MultiByteToWideChar(ReadToken(iter), 0, (iter+5), end-(iter+5), m_wText+ms_lastpos, m_wTextLen-ms_lastpos); + // Empty + if (!szText || len == 0) + { + m_wText[0] = L'\0'; + ms_lastpos = 0; + ms_curpos = 0; + ms_selbegin = ms_selend = 0; + return; + } - ms_curpos = std::min(ms_curpos, ms_lastpos); + // Required wchar count (not including null) + int required = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, szText, len, nullptr, 0); + if (required <= 0) + { + // invalid UTF-8 -> clear + m_wText[0] = L'\0'; + ms_lastpos = 0; + ms_curpos = 0; + ms_selbegin = ms_selend = 0; + return; + } + + // Clamp to buffer capacity - 1 (reserve null) + int outChars = std::min(required, cap - 1); + + // Convert + int written = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, szText, len, m_wText, outChars); + if (written <= 0) + written = 0; + + m_wText[written] = L'\0'; + + ms_lastpos = written; + ms_curpos = ms_lastpos; + ms_selbegin = ms_selend = ms_curpos; } -int CIME::GetText(std::string & rstrText, bool addCodePage) +int CIME::GetText(std::string& rstrText) { - int outCodePage = ms_uOutputCodePage; - int dataCodePage; - switch (outCodePage) - { - //case 1256: // ARABIC - case 1268: // VIETNAM - dataCodePage = CP_UTF8; - break; - default: - dataCodePage = outCodePage; - } + std::wstring w; + w.reserve((size_t)ms_lastpos + (size_t)ms_compLen + 8); - int len = 0; - char text[IMESTR_MAXLEN]; + // before cursor + if (ms_curpos > 0) + w.append(m_wText, m_wText + ms_curpos); - len += WideCharToMultiByte(dataCodePage, 0, m_wText, ms_curpos, text, sizeof(text)-len, NULL, NULL); - len += WideCharToMultiByte(dataCodePage, 0, m_wszComposition, ms_compLen, text+len, sizeof(text)-len, NULL, NULL); - len += WideCharToMultiByte(dataCodePage, 0, m_wText+ms_curpos, ms_lastpos-ms_curpos, text+len, sizeof(text)-len, NULL, NULL); + // composition (uncommitted) + if (ms_compLen > 0) + w.append(m_wszComposition, m_wszComposition + ms_compLen); - int i; - for(i=0; i 0x7F) break; + // after cursor + if (ms_lastpos > ms_curpos) + w.append(m_wText + ms_curpos, m_wText + ms_lastpos); - if(i == len) - { - rstrText.append(text, text+len); - } - else - { - rstrText.append(text, text+i); + std::string utf8 = WideToUtf8(w); + rstrText.append(utf8); - //if (addCodePage) - // rstrText.append(GetCodePageText()); - - rstrText.append(text+i, text+len); - } - - return rstrText.size(); -} - -const char* CIME::GetCodePageText() -{ - static char szCodePage[16]; - - const int defCodePage = GetDefaultCodePage(); - const int outCodePage = ms_uOutputCodePage; - - if (outCodePage != defCodePage) - { - sprintf(szCodePage, "@%04d", outCodePage); - } - else - { - szCodePage[0] = 0; - } - - return szCodePage; -} - -int CIME::GetCodePage() -{ - return ms_uOutputCodePage; + return (int)utf8.size(); } int CIME::GetCandidatePageCount() @@ -550,7 +548,7 @@ int CIME::GetCandidateCount() return ms_dwCandidateCount; } -int CIME::GetCandidate(DWORD index, std::string & rstrText) +int CIME::GetCandidate(DWORD index, std::string & rstrText) { if(index >= MAX_CANDLIST) return 0; @@ -564,10 +562,7 @@ int CIME::GetCandidate(DWORD index, std::string & rstrText) return 0; char text[IMESTR_MAXLEN]; - int len = ::WideCharToMultiByte(CP_UTF8, 0, wszText, wTextLen, text, sizeof(text), 0, 0); - - rstrText.append("@9999"); - rstrText.append(text, text+len); + rstrText.append(WideToUtf8(std::wstring(wszText, wszText + wTextLen))); return wTextLen; } @@ -632,31 +627,227 @@ void CIME::EnablePaste(bool bFlag) m_bEnablePaste = bFlag; } +namespace +{ + inline bool HasSelection() + { + return CIME::ms_selbegin != CIME::ms_selend; + } + + inline void NormalizeSelection() + { + if (CIME::ms_selbegin > CIME::ms_selend) + std::swap(CIME::ms_selbegin, CIME::ms_selend); + } +} + +bool CIME::CanUndo() +{ + return !ms_undo.empty(); +} + +bool CIME::CanRedo() +{ + return !ms_redo.empty(); +} + +void CIME::ClearUndoRedo() +{ + ms_undo.clear(); + ms_redo.clear(); +} + +void CIME::PushUndoState() +{ + SUndoState st; + st.text = m_wText; + st.curpos = ms_curpos; + st.lastpos = ms_lastpos; + st.selbegin = ms_selbegin; + st.selend = ms_selend; + + ms_undo.push_back(std::move(st)); + if (ms_undo.size() > MAX_UNDO) + ms_undo.erase(ms_undo.begin()); + ms_redo.clear(); +} + +void CIME::RestoreState(const SUndoState& st) +{ + const std::string utf8 = WideToUtf8(st.text); + SetText(utf8.c_str(), (int)utf8.size()); + + ms_curpos = std::max(0, std::min(st.curpos, ms_lastpos)); + ms_selbegin = std::max(0, std::min(st.selbegin, ms_lastpos)); + ms_selend = std::max(0, std::min(st.selend, ms_lastpos)); + + if (ms_pEvent) + ms_pEvent->OnUpdate(); +} + +void CIME::Undo() +{ + if (ms_undo.empty()) + return; + + // push current into redo + SUndoState cur; + cur.text.assign(m_wText, m_wText + ms_lastpos); + cur.curpos = ms_curpos; + cur.lastpos = ms_lastpos; + cur.selbegin = ms_selbegin; + cur.selend = ms_selend; + ms_redo.push_back(std::move(cur)); + if (ms_redo.size() > MAX_UNDO) + ms_redo.erase(ms_redo.begin()); + + // restore from undo + SUndoState st = std::move(ms_undo.back()); + ms_undo.pop_back(); + + RestoreState(st); + + if (ms_pEvent) + ms_pEvent->OnUpdate(); +} + +void CIME::Redo() +{ + if (ms_redo.empty()) + return; + + // push current into undo (WITHOUT clearing redo) + SUndoState cur; + cur.text.assign(m_wText, m_wText + ms_lastpos); + cur.curpos = ms_curpos; + cur.lastpos = ms_lastpos; + cur.selbegin = ms_selbegin; + cur.selend = ms_selend; + + ms_undo.push_back(std::move(cur)); + if (ms_undo.size() > MAX_UNDO) + ms_undo.erase(ms_undo.begin()); + + // restore from redo + SUndoState st = std::move(ms_redo.back()); + ms_redo.pop_back(); + + RestoreState(st); + + if (ms_pEvent) + ms_pEvent->OnUpdate(); +} + +void CIME::SelectAll() +{ + ms_selbegin = 0; + ms_selend = ms_lastpos; + ms_curpos = ms_lastpos; + + if (ms_pEvent) + ms_pEvent->OnUpdate(); +} + +void CIME::DeleteSelection() +{ + NormalizeSelection(); + if (!HasSelection()) + return; + + PushUndoState(); + + const int a = ms_selbegin; + const int b = ms_selend; + const int selLen = b - a; + if (selLen <= 0) + return; + + const int tailCount = (ms_lastpos - b) + 1; + memmove(m_wText + a, m_wText + b, (size_t)tailCount * sizeof(wchar_t)); + + ms_lastpos -= selLen; + ms_curpos = a; + + ms_selbegin = ms_selend = a; + + if (ms_pEvent) + ms_pEvent->OnUpdate(); +} + +void CIME::CopySelectionToClipboard(HWND hWnd) +{ + NormalizeSelection(); + if (!HasSelection()) + return; + + const int a = ms_selbegin; + const int b = ms_selend; + const int selLen = b - a; + if (selLen <= 0) + return; + + if (!OpenClipboard(hWnd)) + return; + + EmptyClipboard(); + + const SIZE_T bytes = (SIZE_T)(selLen + 1) * sizeof(wchar_t); + HGLOBAL hMem = GlobalAlloc(GMEM_MOVEABLE, bytes); + if (!hMem) + { + CloseClipboard(); + return; + } + + wchar_t* dst = (wchar_t*)GlobalLock(hMem); + if (!dst) + { + GlobalFree(hMem); + CloseClipboard(); + return; + } + + memcpy(dst, m_wText + a, (size_t)selLen * sizeof(wchar_t)); + dst[selLen] = L'\0'; + + GlobalUnlock(hMem); + + if (!SetClipboardData(CF_UNICODETEXT, hMem)) + GlobalFree(hMem); // only free if SetClipboardData failed + + CloseClipboard(); +} + +void CIME::CutSelectionToClipboard(HWND hWnd) +{ + CopySelectionToClipboard(hWnd); + DeleteSelection(); +} + void CIME::PasteTextFromClipBoard() { if (!m_bEnablePaste) return; - if (!OpenClipboard(NULL)) + if (!OpenClipboard(ms_hWnd)) return; - HANDLE handle = GetClipboardData(CF_TEXT); - char * buffer = (char*)GlobalLock(handle); - std::string strClipboard = buffer; - GlobalUnlock(handle); + // 1) Prefer Unicode clipboard + if (HANDLE hUni = GetClipboardData(CF_UNICODETEXT)) + { + if (wchar_t* wbuf = (wchar_t*)GlobalLock(hUni)) + { + InsertString(wbuf, (int)wcslen(wbuf)); + GlobalUnlock(hUni); + } + + CloseClipboard(); + if (ms_pEvent) ms_pEvent->OnUpdate(); + return; + } + CloseClipboard(); - - if (strClipboard.empty()) - return; - - const char* begin = strClipboard.c_str(); - const char* end = begin + strClipboard.length(); - wchar_t m_wText[IMESTR_MAXLEN]; - int wstrLen = MultiByteToWideChar(ms_uInputCodePage, 0, begin, end-begin, m_wText, IMESTR_MAXLEN); - - InsertString(m_wText, wstrLen); - if(ms_pEvent) - ms_pEvent->OnUpdate(); + if (ms_pEvent) ms_pEvent->OnUpdate(); } void CIME::FinalizeString(bool bSend) @@ -713,6 +904,21 @@ int CIME::GetULEnd() return ms_ulend; } +int CIME::GetSelBegin() +{ + return ms_selbegin; +} + +int CIME::GetSelEnd() +{ + return ms_selend; +} + +void CIME::ClearSelection() +{ + ms_selbegin = ms_selend = ms_curpos; +} + void CIME::CloseCandidateList() { ms_bCandidateList = false; @@ -736,21 +942,8 @@ void CIME::ChangeInputLanguage() ChangeInputLanguageWorker(); if (uLanguage != GETLANG()) { - // Korean IME always uses level 3 support. - // Other languages use the level that is specified by ImeUi_SetSupportLevel() SetSupportLevel( ( GETPRIMLANG() == LANG_KOREAN ) ? 3 : ms_dwIMELevelSaved ); } - - if(ms_pEvent) - ms_pEvent->OnChangeCodePage(); - - //HWND hwndImeDef = ImmGetDefaultIMEWnd(ms_hWnd); - //if ( hwndImeDef ) - //{ - // // Fix for Zooty #3995: prevent CHT IME toobar from showing up - // SendMessageA(hwndImeDef, WM_IME_CONTROL, IMC_OPENSTATUSWINDOW, 0); - // SendMessageA(hwndImeDef, WM_IME_CONTROL, IMC_CLOSESTATUSWINDOW, 0); - //} } void CIME::ChangeInputLanguageWorker() @@ -779,13 +972,21 @@ void CIME::IncCurPos() { if (ms_curpos < ms_lastpos) { - int pos = FindColorTagEndPosition(m_wText + ms_curpos, ms_lastpos - ms_curpos); + // Skip over tags (color tags, hyperlink tags, etc.) + int tagLen = 0; + std::wstring tagExtra; + int tag = GetTextTag(&m_wText[ms_curpos], ms_lastpos - ms_curpos, tagLen, tagExtra); - if (pos > 0) - ms_curpos = std::min(ms_lastpos, std::max(0, ms_curpos + (pos + 1))); + if (tag != TEXT_TAG_PLAIN && tagLen > 0) + { + // We're at the start of a tag - skip the entire tag + ms_curpos += tagLen; + } else + { + // Normal character - move forward by 1 ++ms_curpos; - //++ms_curpos; + } } } @@ -793,13 +994,40 @@ void CIME::DecCurPos() { if (ms_curpos > 0) { - int pos = FindColorTagStartPosition(m_wText + ms_curpos - 1, ms_curpos); - - if (pos > 0) - ms_curpos = std::min(ms_lastpos, std::max(0, ms_curpos - (pos + 1))); - else - --ms_curpos; - //--ms_curpos; + // Move back one position + --ms_curpos; + + // If we landed in the middle of a tag, skip backward to the tag start + // Keep checking backward for tag starts until we find the beginning + while (ms_curpos > 0) + { + int tagLen = 0; + std::wstring tagExtra; + + // Check if current position is a tag start + int tag = GetTextTag(&m_wText[ms_curpos], ms_lastpos - ms_curpos, tagLen, tagExtra); + + if (tag != TEXT_TAG_PLAIN && tagLen > 0) + { + // We're at a tag start - this is good, stop here + break; + } + + // Check if the character BEFORE us starts a tag that extends over our position + if (ms_curpos > 0) + { + int prevTag = GetTextTag(&m_wText[ms_curpos - 1], ms_lastpos - (ms_curpos - 1), tagLen, tagExtra); + if (prevTag != TEXT_TAG_PLAIN && tagLen > 1) + { + // Previous position starts a multi-char tag - move to it + --ms_curpos; + continue; + } + } + + // Not in a tag, we're done + break; + } } } @@ -819,14 +1047,19 @@ void CIME::SetCurPos(int offset) } else { - // offset은 보여지는 텍스트의 위치로 온다. 따라서 새로 계산해야함. - //ms_curpos = min(ms_lastpos, offset); ms_curpos = std::min(ms_lastpos, GetTextTagInternalPosFromRenderPos(m_wText, ms_lastpos, offset)); } } void CIME::DelCurPos() { + // If there is a selection, delete it first + if (ms_selbegin != ms_selend) + { + DeleteSelection(); + return; + } + if (ms_curpos < ms_lastpos) { int eraseCount = FindColorTagEndPosition(m_wText + ms_curpos, ms_lastpos - ms_curpos) + 1; @@ -842,7 +1075,25 @@ void CIME::PasteString(const char * str) const char * begin = str; const char * end = str + strlen(str); wchar_t m_wText[IMESTR_MAXLEN]; - int wstrLen = MultiByteToWideChar(ms_uInputCodePage, 0, begin, end - begin, m_wText, IMESTR_MAXLEN); + int wstrLen = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, begin, end - begin, m_wText, IMESTR_MAXLEN); + + // Check if pasting a hyperlink (contains |H tag) + bool isHyperlink = false; + for (int i = 0; i < wstrLen - 1; ++i) + { + if (m_wText[i] == L'|' && m_wText[i + 1] == L'H') + { + isHyperlink = true; + break; + } + } + + // If pasting hyperlink, always append to end (avoid cursor position issues) + if (isHyperlink) + { + ms_curpos = ms_lastpos; // Move cursor to end first + } + InsertString(m_wText, wstrLen); if(ms_pEvent) ms_pEvent->OnUpdate(); @@ -851,16 +1102,42 @@ void CIME::PasteString(const char * str) /*---------------------------------------------------------------------------*/ /* Private */ void CIME::InsertString(wchar_t* wString, int iSize) { + PushUndoState(); + + if (!wString || iSize <= 0) + return; + + // Replace selection first + if (ms_selbegin != ms_selend) + { + DeleteSelection(); // sets ms_curpos to ms_selbegin and clears selection + } + + if (ms_lastpos < 0) ms_lastpos = 0; + if (ms_curpos < 0) ms_curpos = 0; + if (ms_curpos > ms_lastpos) ms_curpos = ms_lastpos; + + // Need room for NUL terminator + if (ms_lastpos + iSize >= IMESTR_MAXLEN) + return; + if (IsMax(wString, iSize)) return; + // Move tail including the existing NUL if (ms_curpos < ms_lastpos) - memmove(m_wText+ms_curpos+iSize, m_wText+ms_curpos, sizeof(wchar_t)*(ms_lastpos-ms_curpos)); + { + size_t tailChars = (size_t)(ms_lastpos - ms_curpos) + 1; // +1 for NUL + wmemmove(m_wText + ms_curpos + iSize, m_wText + ms_curpos, tailChars); + } - memcpy(m_wText+ms_curpos, wString, sizeof(wchar_t)*iSize); + wmemcpy(m_wText + ms_curpos, wString, (size_t)iSize); ms_curpos += iSize; ms_lastpos += iSize; + + // Ensure termination + m_wText[ms_lastpos] = L'\0'; } void CIME::OnChar(wchar_t c) @@ -869,112 +1146,37 @@ void CIME::OnChar(wchar_t c) if (!iswdigit(c)) return; - if (!__IsWritable(c)) + if (c == 0x16) return; InsertString(&c, 1); } -UINT CIME::GetCodePageFromLang(LANGID langid) -{ - unsigned pri_langid = PRIMARYLANGID(langid); - switch (pri_langid) - { - case LANG_JAPANESE: - //setlocale(LC_ALL, ".932"); - return 932; - case LANG_KOREAN: - //setlocale(LC_ALL, ".949"); - return 949; - case LANG_CHINESE: - { - switch (SUBLANGID(langid)) - { - case SUBLANG_CHINESE_SIMPLIFIED: - case SUBLANG_CHINESE_SINGAPORE: - //setlocale(LC_ALL, ".936"); - return 936; - case SUBLANG_CHINESE_TRADITIONAL: - case SUBLANG_CHINESE_MACAU: - case SUBLANG_CHINESE_HONGKONG: - //setlocale(LC_ALL, ".950"); - return 950; - } - } - //setlocale(LC_ALL, ".936"); - return 936; - case LANG_ARABIC: - return 1256; - case LANG_GREEK: - //setlocale(LC_ALL, ".1253"); - return 1253; - case LANG_TURKISH: - //setlocale(LC_ALL, ".1254"); - return 1254; - case LANG_HEBREW: - //setlocale(LC_ALL, ".1255"); - return 1255; - case LANG_ESTONIAN: - case LANG_LATVIAN: - case LANG_LITHUANIAN: - //setlocale(LC_ALL, ".1257"); - return 1257; - case LANG_VIETNAMESE: - return 1258; - case LANG_THAI: - //setlocale(LC_ALL, ".874"); - return 874; - case LANG_CZECH: - case LANG_HUNGARIAN: - case LANG_POLISH: - case LANG_CROATIAN: - case LANG_MACEDONIAN: - case LANG_ROMANIAN: - case LANG_SLOVAK: - case LANG_SLOVENIAN: - //setlocale(LC_ALL, ".1250"); - return 1250; - case LANG_RUSSIAN: - case LANG_BELARUSIAN: - case LANG_BULGARIAN: - case LANG_UKRAINIAN: - return 1251; - case LANG_GERMAN: - //_wsetlocale(LC_ALL, ".1252"); - return 1252; - default: - //TraceError("UNKNOWN IME[%d]\n", langid); - //setlocale(LC_ALL, ".949"); - return 1252; - } -} - void CIME::CompositionProcess(HIMC hImc) { - ms_compLen = ImmGetCompositionStringW(hImc, GCS_COMPSTR, m_wszComposition, sizeof(m_wszComposition))/sizeof(wchar_t); + LONG bytes = ImmGetCompositionStringW(hImc, GCS_COMPSTR, m_wszComposition, sizeof(m_wszComposition)); + if (bytes <= 0) + { + ms_compLen = 0; + m_wszComposition[0] = L'\0'; + return; + } - //OutputDebugStringW( L"Composition: " ); - //OutputDebugStringW( m_wszComposition ); - //for( int i=0; i < (int) ms_compLen * 2; i++ ) { - // LPBYTE pbyData = (LPBYTE) m_wszComposition; - // pbyData += i; - // WCHAR tszName[32]; - - // swprintf_s( tszName, L"%02X ", (unsigned int) *pbyData ); - // OutputDebugStringW( tszName ); - //} - //OutputDebugStringW( L"\n" ); + ms_compLen = (int)(bytes / (LONG)sizeof(wchar_t)); if (IsMax(m_wszComposition, ms_compLen)) { ImmNotifyIME(hImc, NI_COMPOSITIONSTR, CPS_CANCEL, 0); ms_compLen = 0; + m_wszComposition[0] = L'\0'; } } void CIME::CompositionProcessBuilding(HIMC hImc) { - int textLen = WideCharToMultiByte(ms_uOutputCodePage, 0, m_wText, ms_lastpos, 0, 0, NULL, NULL); + int textLen = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, m_wText, ms_lastpos, nullptr, 0, nullptr, nullptr); + if (textLen <= 0) + textLen = 0; if (textLen >= m_max) { @@ -982,28 +1184,19 @@ void CIME::CompositionProcessBuilding(HIMC hImc) ms_compLen = 0; return; } - + ms_compLen = ImmGetCompositionStringW(hImc, GCS_COMPSTR, m_wszComposition, sizeof(m_wszComposition))/sizeof(wchar_t); - - //OutputDebugStringW( L"Composition: " ); - //OutputDebugStringW( m_wszComposition ); - //for( int i=0; i < (int) ms_compLen * 2; i++ ) { - // LPBYTE pbyData = (LPBYTE) m_wszComposition; - // pbyData += i; - // WCHAR tszName[32]; - - // swprintf_s( tszName, L"%02X ", (unsigned int) *pbyData ); - // OutputDebugStringW( tszName ); - //} - //OutputDebugStringW( L"\n" ); } void CIME::ResultProcess(HIMC hImc) { wchar_t temp[IMESTR_MAXLEN]; - int len = ImmGetCompositionStringW(hImc, GCS_RESULTSTR, temp, sizeof(temp))/sizeof(wchar_t); + LONG bytes = ImmGetCompositionStringW(hImc, GCS_RESULTSTR, temp, sizeof(temp)); + if (bytes <= 0) + return; + int len = (int)(bytes / (LONG)sizeof(wchar_t)); if (len <= 0) return; @@ -1023,62 +1216,70 @@ void CIME::AttributeProcess(HIMC hImc) ms_ulend = end; } +static const wchar_t* CandidateStringAt(const CANDIDATELIST* cl, UINT idx) +{ + if (!cl || idx >= cl->dwCount) + return L""; + const BYTE* base = reinterpret_cast(cl); + return reinterpret_cast(base + cl->dwOffset[idx]); +} + void CIME::CandidateProcess(HIMC hImc) { - std::vector abyCandidate; - DWORD dwCandidateLen = ImmGetCandidateListW(hImc, 0, NULL, 0); - abyCandidate.resize(dwCandidateLen); - if(dwCandidateLen > 0) { - ms_bCandidateList = true; + DWORD bytes = ImmGetCandidateListW(hImc, 0, nullptr, 0); + if (bytes == 0) + return; - CANDIDATELIST* lpCandidateList = (CANDIDATELIST*)(&abyCandidate[0]); - dwCandidateLen = ImmGetCandidateListW(hImc, 0, lpCandidateList, dwCandidateLen); + std::vector buf(bytes); - ms_dwCandidateSelection = lpCandidateList->dwSelection; - ms_dwCandidateCount = lpCandidateList->dwCount; + auto* cl = reinterpret_cast(buf.data()); + DWORD got = ImmGetCandidateListW(hImc, 0, cl, bytes); + if (got == 0 || cl->dwCount == 0) + return; - int iStartOfPage = 0; + ms_bCandidateList = true; + ms_dwCandidateCount = cl->dwCount; + ms_dwCandidateSelection = cl->dwSelection; - if( GETLANG() == LANG_CHS ) { - // MSPY (CHS IME) has variable number of candidates in candidate window find where current page starts, and the size of current page - const int maxCandChar = 18 * (3 - sizeof(TCHAR)); - UINT cChars = 0; - UINT i; - for (i = 0; i < ms_dwCandidateCount; i++) - { - UINT uLen = lstrlenW((LPWSTR)((DWORD)lpCandidateList + lpCandidateList->dwOffset[i])) + (3 - sizeof(WCHAR)); - if (uLen + cChars > maxCandChar) - { - if (i > ms_dwCandidateSelection) - { - break; - } - iStartOfPage = i; - cChars = uLen; - } - else - { - cChars += uLen; - } - } - ms_dwCandidatePageSize = i - iStartOfPage; - } else { - ms_dwCandidatePageSize = MIN( lpCandidateList->dwPageSize, MAX_CANDLIST ); - iStartOfPage = ms_bUILessMode ? lpCandidateList->dwPageStart : (ms_dwCandidateSelection / (MAX_CANDLIST - 1)) * (MAX_CANDLIST - 1); - } + UINT pageStart = 0; - ms_dwCandidateSelection = ( GETLANG() == LANG_CHS && !GetImeId() ) ? (DWORD)-1 : ms_dwCandidateSelection - iStartOfPage; - - //printf( "SEL: %d, START: %d, PAGED: %d\n", ms_dwCandidateSelection, iStartOfPage, ms_dwCandidatePageSize ); - memset(&ms_wszCandidate, 0, sizeof(ms_wszCandidate)); - for(UINT i = iStartOfPage, j = 0; (DWORD)i < lpCandidateList->dwCount && j < ms_dwCandidatePageSize; i++, j++) { - wcscpy( ms_wszCandidate[j], (LPWSTR)( (DWORD)lpCandidateList + lpCandidateList->dwOffset[i] ) ); - } - - // don't display selection in candidate list in case of Korean and old Chinese IME. - if ( GETPRIMLANG() == LANG_KOREAN || GETLANG() == LANG_CHT && !GetImeId() ) - ms_dwCandidateSelection = (DWORD) -1; + if (ms_bUILessMode && cl->dwPageStart < cl->dwCount) + { + pageStart = cl->dwPageStart; } + else + { + const UINT uiPage = (MAX_CANDLIST > 1) ? (MAX_CANDLIST - 1) : 1; + if (ms_dwCandidateSelection < cl->dwCount) + pageStart = (ms_dwCandidateSelection / uiPage) * uiPage; + } + + UINT pageSize = 0; + if (cl->dwPageSize > 0) + pageSize = (UINT)std::min(cl->dwPageSize, (DWORD)MAX_CANDLIST); + else + pageSize = (UINT)std::min(cl->dwCount - pageStart, (DWORD)MAX_CANDLIST); + + ms_dwCandidatePageSize = pageSize; + + // selection relative to page (or none) + if (ms_dwCandidateSelection < cl->dwCount && ms_dwCandidateSelection >= pageStart) + ms_dwCandidateSelection = ms_dwCandidateSelection - pageStart; + else + ms_dwCandidateSelection = (DWORD)-1; + + memset(ms_wszCandidate, 0, sizeof(ms_wszCandidate)); + + for (UINT j = 0; j < pageSize; ++j) + { + UINT srcIdx = pageStart + j; + const wchar_t* src = CandidateStringAt(cl, srcIdx); + wcsncpy_s(ms_wszCandidate[j], CIME::MAX_CANDIDATE_LENGTH, src, _TRUNCATE); + } + + // don't display selection in candidate list in case of Korean and old Chinese IME. + if (GETPRIMLANG() == LANG_KOREAN || (GETLANG() == LANG_CHT && !GetImeId())) + ms_dwCandidateSelection = (DWORD)-1; } void CIME::ReadingProcess(HIMC hImc) @@ -1178,9 +1379,9 @@ void CIME::ReadingProcess(HIMC hImc) case IMEID_CHS_VER42: // 4.2.x.x // SCIME98 or MSPY2 (w/Office2k, Win2k, WinME, etc) { - OSVERSIONINFOA osi; - osi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA); - GetVersionExA(&osi); + OSVERSIONINFOW osi; + osi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOW); + GetVersionExW(&osi); int nTcharSize = (osi.dwPlatformId == VER_PLATFORM_WIN32_NT) ? sizeof(wchar_t) : sizeof(char); p = *(LPBYTE *)((LPBYTE)_ImmLockIMCC(lpIC->hPrivate) + 1*4 + 1*4 + 6*4); @@ -1205,9 +1406,9 @@ void CIME::ReadingProcess(HIMC hImc) if(bUnicodeIme) { ms_wstrReading.assign(temp, temp+tempLen); } else { - int wstrLen = MultiByteToWideChar(ms_uInputCodePage, 0, (char*)temp, tempLen, NULL, 0); + int wstrLen = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, (char*)temp, tempLen, NULL, 0); wchar_t* wstr = (wchar_t*)alloca(sizeof(wchar_t)*wstrLen); - MultiByteToWideChar(ms_uInputCodePage, 0, (char*)temp, tempLen, wstr, wstrLen); + MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, (char*)temp, tempLen, wstr, wstrLen); ms_wstrReading.assign(wstr, wstr+wstrLen); } } @@ -1216,7 +1417,8 @@ void CIME::ReadingProcess(HIMC hImc) _ImmUnlockIMC(hImc); ms_bHorizontalReading = GetReadingWindowOrientation(); - } + } + if (ms_wstrReading.size()) { ms_bReadingInformation = true; if(ms_pEvent) @@ -1228,12 +1430,17 @@ void CIME::ReadingProcess(HIMC hImc) bool CIME::IsMax(const wchar_t* wInput, int len) { - if (ms_lastpos + len > IMESTR_MAXLEN) + if (ms_lastpos + len >= IMESTR_MAXLEN) // keep room for NUL return true; - int textLen = WideCharToMultiByte(ms_uOutputCodePage, 0, m_wText, ms_lastpos, 0, 0, NULL, NULL); - int inputLen = WideCharToMultiByte(ms_uOutputCodePage, 0, wInput, len, 0, 0, NULL, NULL); - //return textLen + inputLen > m_max; + int textLen = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, m_wText, ms_lastpos, nullptr, 0, nullptr, nullptr); + int inputLen = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, wInput, len, nullptr, 0, nullptr, nullptr); + + if (textLen <= 0) + textLen = 0; + + if (inputLen <= 0) + inputLen = 0; if (textLen + inputLen > m_max) return true; @@ -1241,17 +1448,25 @@ bool CIME::IsMax(const wchar_t* wInput, int len) { std::wstring str = GetTextTagOutputString(m_wText, ms_lastpos); std::wstring input = GetTextTagOutputString(wInput, len); - int textLen = WideCharToMultiByte(ms_uOutputCodePage, 0, str.c_str(), str.length(), 0, 0, NULL, NULL); - int inputLen = WideCharToMultiByte(ms_uOutputCodePage, 0, input.c_str(), input.length(), 0, 0, NULL, NULL); + + int textLen = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, str.c_str(), str.length(), 0, 0, NULL, NULL); + if (textLen <= 0) + textLen = 0; + + int inputLen = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, input.c_str(), input.length(), 0, 0, NULL, NULL); + if (inputLen <= 0) + inputLen = 0; + return textLen + inputLen > m_userMax; } + return false; } DWORD CIME::GetImeId( UINT uIndex ) { static HKL hklPrev = 0; - char szTmp[1024]; + wchar_t szTmp[1024]; if (uIndex >= COUNTOF(ms_adwId)) return 0; @@ -1271,122 +1486,130 @@ DWORD CIME::GetImeId( UINT uIndex ) if (!((ms_hklCurrent == _CHT_HKL_NEW_PHONETIC) || (ms_hklCurrent == _CHT_HKL_NEW_CHANG_JIE) || (ms_hklCurrent == _CHT_HKL_NEW_QUICK) || (ms_hklCurrent == _CHT_HKL_HK_CANTONESE) || (ms_hklCurrent == _CHS_HKL))) { ms_adwId[0] = ms_adwId[1] = 0; - return 0; + return 0; } - if (!ImmGetIMEFileNameA(ms_hklCurrent, szTmp, (sizeof(szTmp) / sizeof(szTmp[0])) - 1)) { + // Buffer size parameter + if (!ImmGetIMEFileNameW(ms_hklCurrent, szTmp, _countof(szTmp))) { ms_adwId[0] = ms_adwId[1] = 0; - return 0; + return 0; } - if (!_GetReadingString) + if (!_GetReadingString) { - if ((CompareStringA(LCID_INVARIANT, NORM_IGNORECASE, szTmp, -1, CHT_IMEFILENAME1, -1) != CSTR_EQUAL) && - (CompareStringA(LCID_INVARIANT, NORM_IGNORECASE, szTmp, -1, CHT_IMEFILENAME2, -1) != CSTR_EQUAL) && - (CompareStringA(LCID_INVARIANT, NORM_IGNORECASE, szTmp, -1, CHT_IMEFILENAME3, -1) != CSTR_EQUAL) && - (CompareStringA(LCID_INVARIANT, NORM_IGNORECASE, szTmp, -1, CHS_IMEFILENAME1, -1) != CSTR_EQUAL) && - (CompareStringA(LCID_INVARIANT, NORM_IGNORECASE, szTmp, -1, CHS_IMEFILENAME2, -1) != CSTR_EQUAL)) + if ((CompareStringW(LCID_INVARIANT, NORM_IGNORECASE, szTmp, -1, CHT_IMEFILENAME1, -1) != CSTR_EQUAL) && + (CompareStringW(LCID_INVARIANT, NORM_IGNORECASE, szTmp, -1, CHT_IMEFILENAME2, -1) != CSTR_EQUAL) && + (CompareStringW(LCID_INVARIANT, NORM_IGNORECASE, szTmp, -1, CHT_IMEFILENAME3, -1) != CSTR_EQUAL) && + (CompareStringW(LCID_INVARIANT, NORM_IGNORECASE, szTmp, -1, CHS_IMEFILENAME1, -1) != CSTR_EQUAL) && + (CompareStringW(LCID_INVARIANT, NORM_IGNORECASE, szTmp, -1, CHS_IMEFILENAME2, -1) != CSTR_EQUAL)) { ms_adwId[0] = ms_adwId[1] = 0; - return 0; - } - } + return 0; + } + } - DWORD dwVerHandle; - DWORD dwVerSize = GetFileVersionInfoSize(szTmp, &dwVerHandle); - LANGID langId = LOWORD(ms_hklCurrent); + DWORD dwVerHandle; + DWORD dwVerSize = GetFileVersionInfoSizeW(szTmp, &dwVerHandle); + LANGID langId = LOWORD(ms_hklCurrent); - if (dwVerSize) + if (dwVerSize) { - LPVOID lpVerBuffer = alloca(dwVerSize); + LPVOID lpVerBuffer = alloca(dwVerSize); - if (GetFileVersionInfo(szTmp, dwVerHandle, dwVerSize, lpVerBuffer)) + if (GetFileVersionInfoW(szTmp, dwVerHandle, dwVerSize, lpVerBuffer)) { - LPVOID lpVerData; - UINT cbVerData; + LPVOID lpVerData; + UINT cbVerData; - if(VerQueryValue(lpVerBuffer, "\\", &lpVerData, &cbVerData)) + if (VerQueryValueW(lpVerBuffer, L"\\", &lpVerData, &cbVerData)) { - DWORD dwVer = ((VS_FIXEDFILEINFO*) lpVerData)->dwFileVersionMS; - dwVer = (dwVer & 0x00ff0000) << 8 | (dwVer & 0x000000ff) << 16; + DWORD dwVer = ((VS_FIXEDFILEINFO*) lpVerData)->dwFileVersionMS; + dwVer = (dwVer & 0x00ff0000) << 8 | (dwVer & 0x000000ff) << 16; - if (_GetReadingString - || - (langId == LANG_CHT && - (dwVer == MAKEIMEVERSION(4, 2) || - dwVer == MAKEIMEVERSION(4, 3) || - dwVer == MAKEIMEVERSION(4, 4) || - dwVer == MAKEIMEVERSION(5, 0) || - dwVer == MAKEIMEVERSION(5, 1) || - dwVer == MAKEIMEVERSION(5, 2) || - dwVer == MAKEIMEVERSION(6, 0))) - || - (langId == LANG_CHS && - (dwVer == MAKEIMEVERSION(4, 1) || - dwVer == MAKEIMEVERSION(4, 2) || - dwVer == MAKEIMEVERSION(5, 3)))) + if (_GetReadingString + || + (langId == LANG_CHT && + (dwVer == MAKEIMEVERSION(4, 2) || + dwVer == MAKEIMEVERSION(4, 3) || + dwVer == MAKEIMEVERSION(4, 4) || + dwVer == MAKEIMEVERSION(5, 0) || + dwVer == MAKEIMEVERSION(5, 1) || + dwVer == MAKEIMEVERSION(5, 2) || + dwVer == MAKEIMEVERSION(6, 0))) + || + (langId == LANG_CHS && + (dwVer == MAKEIMEVERSION(4, 1) || + dwVer == MAKEIMEVERSION(4, 2) || + dwVer == MAKEIMEVERSION(5, 3)))) { - ms_adwId[0] = dwVer | langId; - ms_adwId[1] = ((VS_FIXEDFILEINFO*)lpVerData)->dwFileVersionLS; + ms_adwId[0] = dwVer | langId; + ms_adwId[1] = ((VS_FIXEDFILEINFO*)lpVerData)->dwFileVersionLS; return ms_adwId[uIndex]; - } - } - } - } + } + } + } + } ms_adwId[0] = ms_adwId[1] = 0; return ms_adwId[0]; } bool CIME::GetReadingWindowOrientation() { - bool bHorizontalReading = (ms_hklCurrent == _CHS_HKL) || (ms_hklCurrent == _CHT_HKL_NEW_CHANG_JIE) || (ms_adwId[0] == 0); - if(!bHorizontalReading && (GETLANG() == LANG_CHT)) - { - char szRegPath[MAX_PATH]; - HKEY hKey; - DWORD dwVer = ms_adwId[0] & 0xFFFF0000; - strcpy(szRegPath, "software\\microsoft\\windows\\currentversion\\"); - strcat(szRegPath, (dwVer >= MAKEIMEVERSION(5, 1)) ? "MSTCIPH" : "TINTLGNT"); - LONG lRc = RegOpenKeyExA(HKEY_CURRENT_USER, szRegPath, 0, KEY_READ, &hKey); - if (lRc == ERROR_SUCCESS) - { - DWORD dwSize = sizeof(DWORD), dwMapping, dwType; - lRc = RegQueryValueExA(hKey, "Keyboard Mapping", NULL, &dwType, (PBYTE)&dwMapping, &dwSize); - if (lRc == ERROR_SUCCESS) - { - if ((dwVer <= MAKEIMEVERSION(5, 0) && - ((BYTE)dwMapping == 0x22 || (BYTE)dwMapping == 0x23)) - || - ((dwVer == MAKEIMEVERSION(5, 1) || dwVer == MAKEIMEVERSION(5, 2)) && - (BYTE)dwMapping >= 0x22 && (BYTE)dwMapping <= 0x24) - ) - { - bHorizontalReading = true; - } - } - RegCloseKey(hKey); - } - } + bool bHorizontalReading = (ms_hklCurrent == _CHS_HKL) || (ms_hklCurrent == _CHT_HKL_NEW_CHANG_JIE) || (ms_adwId[0] == 0); + if(!bHorizontalReading && (GETLANG() == LANG_CHT)) + { + wchar_t wszRegPath[MAX_PATH]; + HKEY hKey; + DWORD dwVer = ms_adwId[0] & 0xFFFF0000; + wcscpy_s(wszRegPath, L"software\\microsoft\\windows\\currentversion\\"); + wcscpy_s(wszRegPath, (dwVer >= MAKEIMEVERSION(5, 1)) ? L"MSTCIPH" : L"TINTLGNT"); + LONG lRc = RegOpenKeyExW(HKEY_CURRENT_USER, wszRegPath, 0, KEY_READ, &hKey); + if (lRc == ERROR_SUCCESS) + { + DWORD dwSize = sizeof(DWORD), dwMapping, dwType; + lRc = RegQueryValueExW(hKey, L"Keyboard Mapping", NULL, &dwType, (PBYTE)&dwMapping, &dwSize); + if (lRc == ERROR_SUCCESS) + { + if ((dwVer <= MAKEIMEVERSION(5, 0) && + ((BYTE)dwMapping == 0x22 || (BYTE)dwMapping == 0x23)) + || + ((dwVer == MAKEIMEVERSION(5, 1) || dwVer == MAKEIMEVERSION(5, 2)) && + (BYTE)dwMapping >= 0x22 && (BYTE)dwMapping <= 0x24) + ) + { + bHorizontalReading = true; + } + } + RegCloseKey(hKey); + } + } return bHorizontalReading; } void CIME::SetupImeApi() { - char szImeFile[MAX_PATH + 1]; + wchar_t szImeFile[MAX_PATH + 1]; _GetReadingString = NULL; - _ShowReadingWindow = NULL; + _ShowReadingWindow = NULL; ms_bUseIMMCandidate = false; - if(ImmGetIMEFileNameA(ms_hklCurrent, szImeFile, COUNTOF(szImeFile) - 1) == 0) + if(ImmGetIMEFileNameW(ms_hklCurrent, szImeFile, COUNTOF(szImeFile) - 1) == 0) return; - if(stricmp(szImeFile, CHS_IMEFILENAME_QQPINYIN) == 0 || stricmp(szImeFile, CHS_IMEFILENAME_SOGOUPY) == 0 || stricmp(szImeFile, CHS_IMEFILENAME_GOOGLEPINYIN2) == 0) + + std::string imeUtf8 = WideToUtf8(szImeFile); + + if (_stricmp(imeUtf8.c_str(), CHS_IMEFILENAME_QQPINYIN) == 0 || _stricmp(imeUtf8.c_str(), CHS_IMEFILENAME_SOGOUPY) == 0 || _stricmp(imeUtf8.c_str(), CHS_IMEFILENAME_GOOGLEPINYIN2) == 0) + { ms_bUseIMMCandidate = true; + } + if (ms_bUILessMode) return; + SAFE_FREE_LIBRARY(ms_hCurrentImeDll); - ms_hCurrentImeDll = LoadLibraryA(szImeFile); + ms_hCurrentImeDll = LoadLibraryW(szImeFile); if (ms_hCurrentImeDll) { _GetReadingString = (UINT (WINAPI*)(HIMC, UINT, LPWSTR, PINT, BOOL*, PUINT)) (GetProcAddress(ms_hCurrentImeDll, "GetReadingString")); @@ -1438,24 +1661,16 @@ static unsigned long _strtoul( LPCSTR psz, LPTSTR*, int ) void CIME::CheckInputLocale() { - static UINT s_uPrevCodePage = 0xFFFF; - static HKL s_hklPrev = NULL; + static HKL s_hklPrev = NULL; - ms_hklCurrent = GetKeyboardLayout( 0 ); - if ( s_hklPrev == ms_hklCurrent ) + ms_hklCurrent = GetKeyboardLayout(0); + if (s_hklPrev == ms_hklCurrent) return; s_hklPrev = ms_hklCurrent; - char szCodePage[8]; - int iRc = GetLocaleInfoA( MAKELCID( GETLANG(), SORT_DEFAULT ), LOCALE_IDEFAULTANSICODEPAGE, szCodePage, COUNTOF( szCodePage ) ); iRc; - ms_uInputCodePage = _strtoul( szCodePage, NULL, 0 ); - if ( s_uPrevCodePage == ms_uInputCodePage ) - return; - s_uPrevCodePage = ms_uInputCodePage; + GetKeyboardLayoutNameW(ms_szKeyboardLayout); - GetKeyboardLayoutName(ms_szKeyboardLayout); - - switch (GETPRIMLANG()) + switch (GETPRIMLANG()) { case LANG_KOREAN: ms_bVerticalCandidate = false; @@ -1469,52 +1684,42 @@ void CIME::CheckInputLocale() case LANG_CHINESE: ms_bVerticalCandidate = true; - - switch(GETSUBLANG()) + switch (GETSUBLANG()) { - case SUBLANG_CHINESE_SIMPLIFIED: - case SUBLANG_CHINESE_SINGAPORE: - //ms_bVerticalCandidate = (GetImeId() == 0); - ms_bVerticalCandidate = false; - ms_wszCurrentIndicator = s_aszIndicator[INDICATOR_CHS]; - break; + case SUBLANG_CHINESE_SIMPLIFIED: + case SUBLANG_CHINESE_SINGAPORE: + ms_bVerticalCandidate = false; + ms_wszCurrentIndicator = s_aszIndicator[INDICATOR_CHS]; + break; - case SUBLANG_CHINESE_TRADITIONAL: - case SUBLANG_CHINESE_HONGKONG: - case SUBLANG_CHINESE_MACAU: - ms_wszCurrentIndicator = s_aszIndicator[INDICATOR_CHT]; - break; + case SUBLANG_CHINESE_TRADITIONAL: + case SUBLANG_CHINESE_HONGKONG: + case SUBLANG_CHINESE_MACAU: + ms_wszCurrentIndicator = s_aszIndicator[INDICATOR_CHT]; + break; - default: // unsupported sub-language - ms_wszCurrentIndicator = s_aszIndicator[INDICATOR_NON_IME]; - break; + default: + ms_wszCurrentIndicator = s_aszIndicator[INDICATOR_NON_IME]; + break; } break; default: ms_wszCurrentIndicator = s_aszIndicator[INDICATOR_NON_IME]; break; - } - - if (ms_wszCurrentIndicator == s_aszIndicator[INDICATOR_NON_IME]) - { - char szLang[10]; - GetLocaleInfoA(MAKELCID(GETLANG(), SORT_DEFAULT), LOCALE_SABBREVLANGNAME, szLang, sizeof(szLang)); - ms_wszCurrentIndicator[0] = szLang[0]; - ms_wszCurrentIndicator[1] = towlower(szLang[1]); - } - - // 아랍어에서 영어로 변경시 코드 페이지를 바꾸지 않는다 - // 내용도 지우지 않는다. - if(ms_uOutputCodePage != 1256) { - ms_uOutputCodePage = ms_uInputCodePage; - Clear(); } - //for ( int i = 0; i < 256; i++ ) - //{ - // LeadByteTable[i] = (BYTE)IsDBCSLeadByteEx( g_uCodePage, (BYTE)i ); - //} + if (ms_wszCurrentIndicator == s_aszIndicator[INDICATOR_NON_IME]) + { + wchar_t szLang[10]{}; + GetLocaleInfoW(MAKELCID(GETLANG(), SORT_DEFAULT), LOCALE_SABBREVLANGNAME, szLang, _countof(szLang)); + + ms_wszCurrentIndicator[0] = szLang[0]; + ms_wszCurrentIndicator[1] = towlower(szLang[1]); + } + + if (ms_compLen > 0) + FinalizeString(false); } void CIME::CheckToggleState() @@ -1883,10 +2088,10 @@ void CTsfUiLessMode::MakeReadingInformationString(ITfReadingInformationUIElement if ( *pszSource ) { LPWSTR pszNextSrc = CharNextW(pszSource); - SIZE_T size = (LPSTR)pszNextSrc - (LPSTR)pszSource; - CopyMemory( pszDest, pszSource, size ); + SIZE_T cch = (SIZE_T)(pszNextSrc - pszSource); // WCHAR count (1 or 2) + CopyMemory(pszDest, pszSource, cch * sizeof(WCHAR)); pszSource = pszNextSrc; - pszDest += size; + pszDest += cch; } *pszDest = 0; } @@ -1942,7 +2147,7 @@ void CTsfUiLessMode::MakeCandidateStrings(ITfCandidateListUIElement* pcandidate) { if(bstr) { - wcscpy( CIME::ms_wszCandidate[j], bstr ); + wcsncpy_s(CIME::ms_wszCandidate[j], CIME::MAX_CANDIDATE_LENGTH, bstr, _TRUNCATE); SysFreeString(bstr); } } @@ -2132,7 +2337,7 @@ LRESULT CIME::WMStartComposition(HWND /*hWnd*/, UINT /*uiMsg*/, WPARAM /*wParam* LRESULT CIME::WMComposition(HWND hWnd, UINT /*uiMsg*/, WPARAM /*wParam*/, LPARAM lParam) { - LRESULT result = 0; + LRESULT result = 0; if(ms_bCaptureInput == false) return 0; @@ -2142,23 +2347,21 @@ LRESULT CIME::WMComposition(HWND hWnd, UINT /*uiMsg*/, WPARAM /*wParam*/, LPARAM if(hImc == NULL) return 0; - if(lParam&GCS_RESULTSTR) + if (lParam&GCS_RESULTSTR) ResultProcess(hImc); - if(lParam&GCS_COMPATTR) + + if (lParam&GCS_COMPATTR) AttributeProcess(hImc); - if(lParam&GCS_COMPSTR) + + if (lParam&GCS_COMPSTR) { - if (ms_uOutputCodePage == 950) // 대만 주음 입력 처리 - { - if (lParam&GCS_COMPATTR) - CompositionProcessBuilding(hImc); - else - CompositionProcess(hImc); - } - else - { - CompositionProcess(hImc); - } + CompositionProcess(hImc); + } + + if (lParam & GCS_CURSORPOS) + { + LONG pos = ImmGetCompositionStringW(hImc, GCS_CURSORPOS, nullptr, 0); + ms_compCaret = (pos < 0) ? 0 : (int)pos; } ImmReleaseContext(hWnd, hImc); @@ -2263,41 +2466,57 @@ LRESULT CIME::WMNotify(HWND hWnd, UINT uiMsg, WPARAM wParam, LPARAM lParam) LRESULT CIME::WMChar(HWND /*hWnd*/, UINT /*uiMsg*/, WPARAM wParam, LPARAM lParam) { - unsigned char c = unsigned char(wParam & 0xff); + wchar_t wc = (wchar_t)wParam; - switch (c) + switch ((unsigned int)wc) { - case 8: - if(ms_bCaptureInput == false) - return 0; - if (ms_curpos > 0) - { - DecCurPos(); - DelCurPos(); - } - if(ms_pEvent) - ms_pEvent->OnUpdate(); - return 0; - break; + case 8: + if (ms_bCaptureInput == false) + return 0; - default: - if(ms_pEvent) { - if (ms_pEvent->OnWM_CHAR(wParam, lParam)) - break; - } - if(ms_bCaptureInput == false) - return 0; - wchar_t w[10]; - MultiByteToWideChar(ms_uInputCodePage, 0, (char*)&c, 1, w, 1); + // If something is selected, backspace deletes the whole selection + if (ms_selbegin != ms_selend) + { + PushUndoState(); + DeleteSelection(); - OnChar(w[0]); - if (w[0] == L'|') - OnChar(w[0]); - if(ms_pEvent) - ms_pEvent->OnUpdate(); - break; + if (ms_pEvent) + ms_pEvent->OnUpdate(); + + return 0; + } + + // Otherwise delete one char to the left + if (ms_curpos > 0) + { + PushUndoState(); + DecCurPos(); + DelCurPos(); + } + + if (ms_pEvent) + ms_pEvent->OnUpdate(); + return 0; + + default: + if(ms_pEvent) + { + if (ms_pEvent->OnWM_CHAR(wParam, lParam)) + break; + } + + if(ms_bCaptureInput == false) + return 0; + + OnChar(wc); + if (wc == L'|') + OnChar(wc); + + if(ms_pEvent) + ms_pEvent->OnUpdate(); + + break; } return 0; } - diff --git a/src/EterLib/IME.h b/src/EterLib/IME.h index 58e90a1..f90a9e0 100644 --- a/src/EterLib/IME.h +++ b/src/EterLib/IME.h @@ -6,203 +6,237 @@ #include "DIMM.h" +#include +#include + class IIMEEventSink { -public: - virtual bool OnWM_CHAR( WPARAM wParam, LPARAM lParam ) = 0; - virtual void OnUpdate() = 0; + public: + virtual bool OnWM_CHAR( WPARAM wParam, LPARAM lParam ) = 0; + virtual void OnUpdate() = 0; - virtual void OnChangeCodePage() = 0; + virtual void OnOpenCandidateList() = 0; + virtual void OnCloseCandidateList() = 0; - virtual void OnOpenCandidateList() = 0; - virtual void OnCloseCandidateList() = 0; - - virtual void OnOpenReadingWnd() = 0; - virtual void OnCloseReadingWnd() = 0; + virtual void OnOpenReadingWnd() = 0; + virtual void OnCloseReadingWnd() = 0; }; class CIME { -public: - enum - { - IMEREADING_MAXLEN = 128, - IMESTR_MAXLEN = 1024, - IMECANDIDATE_MAXLEN = 32768, - MAX_CANDLIST = 10, - MAX_CANDIDATE_LENGTH = 256 - }; + public: + enum + { + IMEREADING_MAXLEN = 128, + IMESTR_MAXLEN = 1024, + IMECANDIDATE_MAXLEN = 32768, + MAX_CANDLIST = 10, + MAX_CANDIDATE_LENGTH = 256 + }; -public: - CIME(); - virtual ~CIME(); + public: + CIME(); + virtual ~CIME(); - bool Initialize(HWND hWnd); - void Uninitialize(void); + bool Initialize(HWND hWnd); + void Uninitialize(void); - static void Clear(); + static void Clear(); - void SetMax(int iMax); - void SetUserMax(int iMax); - void SetText(const char* c_szText, int len); - int GetText(std::string & rstrText, bool addCodePage=false); - const char* GetCodePageText(); - int GetCodePage(); + void SetMax(int iMax); + void SetUserMax(int iMax); + static void SetText(const char* c_szText, int len); + int GetText(std::string & rstrText); - // Candidate List - int GetCandidateCount(); - int GetCandidatePageCount(); - int GetCandidate(DWORD index, std::string & rstrText); - int GetCandidateSelection(); + // Candidate List + int GetCandidateCount(); + int GetCandidatePageCount(); + int GetCandidate(DWORD index, std::string & rstrText); + int GetCandidateSelection(); - // Reading Information - int GetReading(std::string & rstrText); - int GetReadingError(); + // Reading Information + int GetReading(std::string & rstrText); + int GetReadingError(); - void SetInputMode(DWORD dwMode); - DWORD GetInputMode(); + void SetInputMode(DWORD dwMode); + DWORD GetInputMode(); - bool IsIMEEnabled(); - void EnableIME(bool bEnable=true); - void DisableIME(); + bool IsIMEEnabled(); + void EnableIME(bool bEnable=true); + void DisableIME(); - void EnableCaptureInput(); - void DisableCaptureInput(); - bool IsCaptureEnabled(); + void EnableCaptureInput(); + void DisableCaptureInput(); + bool IsCaptureEnabled(); - void SetNumberMode(); - void SetStringMode(); - bool __IsWritable(wchar_t key); - void AddExceptKey(wchar_t key); - void ClearExceptKey(); + void SetNumberMode(); + void SetStringMode(); + bool __IsWritable(wchar_t key); + void AddExceptKey(wchar_t key); + void ClearExceptKey(); - void PasteTextFromClipBoard(); - void EnablePaste(bool bFlag); - void PasteString(const char * str); - static void FinalizeString(bool bSend = false); + void PasteTextFromClipBoard(); + void EnablePaste(bool bFlag); + void PasteString(const char * str); + static void FinalizeString(bool bSend = false); - void UseDefaultIME(); + void SelectAll(); + void DeleteSelection(); + void CopySelectionToClipboard(HWND hWnd); + void CutSelectionToClipboard(HWND hWnd); - static int GetCurPos(); - static int GetCompLen(); - static int GetULBegin(); - static int GetULEnd(); + void UseDefaultIME(); - static void CloseCandidateList(); - static void CloseReadingInformation(); - static void ChangeInputLanguage(); - static void ChangeInputLanguageWorker(); + static int GetCurPos(); + static int GetCompLen(); + static int GetULBegin(); + static int GetULEnd(); - LRESULT WMInputLanguage(HWND hWnd, UINT uiMsg, WPARAM wParam, LPARAM lParam); - LRESULT WMStartComposition(HWND hWnd, UINT uiMsg, WPARAM wParam, LPARAM lParam); - LRESULT WMComposition(HWND hWnd, UINT uiMsg, WPARAM wParam, LPARAM lParam); - LRESULT WMEndComposition(HWND hWnd, UINT uiMsg, WPARAM wParam, LPARAM lParam); - LRESULT WMNotify(HWND hWnd, UINT uiMsg, WPARAM wParam, LPARAM lParam); - LRESULT WMChar(HWND hWnd, UINT uiMsg, WPARAM wParam, LPARAM lParam); + static int GetSelBegin(); + static int GetSelEnd(); + static void ClearSelection(); -protected: - void IncCurPos(); - void DecCurPos(); - void SetCurPos(int offset); - void DelCurPos(); + static void CloseCandidateList(); + static void CloseReadingInformation(); + static void ChangeInputLanguage(); + static void ChangeInputLanguageWorker(); -protected: - static void CheckInputLocale(); - static void CheckToggleState(); - static void SetSupportLevel( DWORD dwImeLevel ); + LRESULT WMInputLanguage(HWND hWnd, UINT uiMsg, WPARAM wParam, LPARAM lParam); + LRESULT WMStartComposition(HWND hWnd, UINT uiMsg, WPARAM wParam, LPARAM lParam); + LRESULT WMComposition(HWND hWnd, UINT uiMsg, WPARAM wParam, LPARAM lParam); + LRESULT WMEndComposition(HWND hWnd, UINT uiMsg, WPARAM wParam, LPARAM lParam); + LRESULT WMNotify(HWND hWnd, UINT uiMsg, WPARAM wParam, LPARAM lParam); + LRESULT WMChar(HWND hWnd, UINT uiMsg, WPARAM wParam, LPARAM lParam); - void InsertString(wchar_t* szString, int iSize); + public: + struct SUndoState + { + std::wstring text; + int curpos = 0; + int lastpos = 0; + int selbegin = 0; + int selend = 0; + }; - void OnChar(wchar_t c); + static void PushUndoState(); + static void Undo(); + static void Redo(); + static void ClearUndoRedo(); - UINT GetCodePageFromLang( LANGID langid ); - void ResultProcess(HIMC hImc); - void CompositionProcessBuilding(HIMC hImc); - void CompositionProcess(HIMC hImc); - void AttributeProcess(HIMC hImc); - void CandidateProcess(HIMC hImc); - void ReadingProcess(HIMC hImc); + static bool CanUndo(); + static bool CanRedo(); - bool IsMax(const wchar_t* wInput, int len); + private: + static void RestoreState(const SUndoState& st); - DWORD GetImeId(UINT uIndex = 0); - bool GetReadingWindowOrientation(); - static void SetupImeApi(); + private: + static std::vector ms_undo; + static std::vector ms_redo; + static const size_t MAX_UNDO = 64; - static INPUTCONTEXT* (WINAPI * _ImmLockIMC)( HIMC ); - static BOOL (WINAPI * _ImmUnlockIMC)( HIMC ); - static LPVOID (WINAPI * _ImmLockIMCC)( HIMCC ); - static BOOL (WINAPI * _ImmUnlockIMCC)( HIMCC ); + protected: + void IncCurPos(); + void DecCurPos(); + void SetCurPos(int offset); + void DelCurPos(); - static UINT (WINAPI * _GetReadingString)( HIMC, UINT, LPWSTR, PINT, BOOL*, PUINT ); - static BOOL (WINAPI * _ShowReadingWindow)( HIMC, BOOL ); + protected: + static void CheckInputLocale(); + static void CheckToggleState(); + static void SetSupportLevel( DWORD dwImeLevel ); -protected: - HIMC m_hOrgIMC; - int m_max; - int m_userMax; + void InsertString(wchar_t* szString, int iSize); - BOOL m_bOnlyNumberMode; + void OnChar(wchar_t c); - std::vector m_exceptKey; + void ResultProcess(HIMC hImc); + void CompositionProcessBuilding(HIMC hImc); + void CompositionProcess(HIMC hImc); + void AttributeProcess(HIMC hImc); + void CandidateProcess(HIMC hImc); + void ReadingProcess(HIMC hImc); - bool m_bEnablePaste; - bool m_bUseDefaultIME; + bool IsMax(const wchar_t* wInput, int len); + + DWORD GetImeId(UINT uIndex = 0); + bool GetReadingWindowOrientation(); + static void SetupImeApi(); + + static INPUTCONTEXT* (WINAPI * _ImmLockIMC)( HIMC ); + static BOOL (WINAPI * _ImmUnlockIMC)( HIMC ); + static LPVOID (WINAPI * _ImmLockIMCC)( HIMCC ); + static BOOL (WINAPI * _ImmUnlockIMCC)( HIMCC ); + + static UINT (WINAPI * _GetReadingString)( HIMC, UINT, LPWSTR, PINT, BOOL*, PUINT ); + static BOOL (WINAPI * _ShowReadingWindow)( HIMC, BOOL ); + + protected: + HIMC m_hOrgIMC; + int m_max; + int m_userMax; + + BOOL m_bOnlyNumberMode; + + std::vector m_exceptKey; + + bool m_bEnablePaste; + bool m_bUseDefaultIME; -public: - static bool ms_bInitialized; - static bool ms_bDisableIMECompletely; - static bool ms_bUILessMode; - static bool ms_bImeEnabled; - static bool ms_bCaptureInput; - static bool ms_bChineseIME; - static bool ms_bUseIMMCandidate; + public: + static bool ms_bInitialized; + static bool ms_bDisableIMECompletely; + static bool ms_bUILessMode; + static bool ms_bImeEnabled; + static bool ms_bCaptureInput; + static bool ms_bChineseIME; + static bool ms_bUseIMMCandidate; - static HWND ms_hWnd; - static HKL ms_hklCurrent; - static char ms_szKeyboardLayout[KL_NAMELENGTH+1]; - static OSVERSIONINFOA ms_stOSVI; + static HWND ms_hWnd; + static HKL ms_hklCurrent; + static wchar_t ms_szKeyboardLayout[KL_NAMELENGTH+1]; + static OSVERSIONINFOW ms_stOSVI; - static HINSTANCE ms_hImm32Dll; - static HINSTANCE ms_hCurrentImeDll; - static DWORD ms_dwImeState; + static HINSTANCE ms_hImm32Dll; + static HINSTANCE ms_hCurrentImeDll; + static DWORD ms_dwImeState; - static DWORD ms_adwId[2]; + static DWORD ms_adwId[2]; - // IME Level - static DWORD ms_dwIMELevel; - static DWORD ms_dwIMELevelSaved; + // IME Level + static DWORD ms_dwIMELevel; + static DWORD ms_dwIMELevelSaved; - // Candidate List - static bool ms_bCandidateList; - static DWORD ms_dwCandidateCount; - static bool ms_bVerticalCandidate; - static int ms_iCandListIndexBase; - static WCHAR ms_wszCandidate[CIME::MAX_CANDLIST][MAX_CANDIDATE_LENGTH]; - static DWORD ms_dwCandidateSelection; - static DWORD ms_dwCandidatePageSize; + // Candidate List + static bool ms_bCandidateList; + static DWORD ms_dwCandidateCount; + static bool ms_bVerticalCandidate; + static int ms_iCandListIndexBase; + static WCHAR ms_wszCandidate[CIME::MAX_CANDLIST][MAX_CANDIDATE_LENGTH]; + static DWORD ms_dwCandidateSelection; + static DWORD ms_dwCandidatePageSize; - // Reading Information - static bool ms_bReadingInformation; - static int ms_iReadingError; - static bool ms_bHorizontalReading; - static std::vector ms_wstrReading; + // Reading Information + static bool ms_bReadingInformation; + static int ms_iReadingError; + static bool ms_bHorizontalReading; + static std::vector ms_wstrReading; - // Indicator - static wchar_t* ms_wszCurrentIndicator; + // Indicator + static wchar_t* ms_wszCurrentIndicator; - static IIMEEventSink* ms_pEvent; + static IIMEEventSink* ms_pEvent; - wchar_t m_wszComposition[IMESTR_MAXLEN]; - static wchar_t m_wText[IMESTR_MAXLEN]; + wchar_t m_wszComposition[IMESTR_MAXLEN]; + static wchar_t m_wText[IMESTR_MAXLEN]; - static int ms_compLen; - static int ms_curpos; - static int ms_lastpos; - static int ms_ulbegin; - static int ms_ulend; + static int ms_compLen; + static int ms_curpos; + static int ms_lastpos; + static int ms_ulbegin; + static int ms_ulend; - static UINT ms_uOutputCodePage; - static UINT ms_uInputCodePage; + static int ms_selbegin; + static int ms_selend; + static int ms_compCaret; }; diff --git a/src/EterLib/MSWindow.cpp b/src/EterLib/MSWindow.cpp index f446d4e..7dc153f 100644 --- a/src/EterLib/MSWindow.cpp +++ b/src/EterLib/MSWindow.cpp @@ -2,6 +2,7 @@ #include "MsWindow.h" #include +#include CMSWindow::TWindowClassSet CMSWindow::ms_stWCSet; HINSTANCE CMSWindow::ms_hInstance = NULL; @@ -61,27 +62,32 @@ void CMSWindow::Destroy() bool CMSWindow::Create(const char* c_szName, int brush, DWORD cs, DWORD ws, HICON hIcon, int iCursorResource) { - //assert(ms_hInstance != NULL); Destroy(); - - const char* c_szClassName = RegisterWindowClass(cs, brush, MSWindowProcedure, hIcon, iCursorResource); - m_hWnd = CreateWindow( - c_szClassName, - c_szName, - ws, - 0, 0, 0, 0, - NULL, - NULL, - ms_hInstance, - NULL); + const wchar_t* wClassName = + RegisterWindowClass(cs, brush, MSWindowProcedure, hIcon, iCursorResource); + + if (!wClassName) + return false; + + // Window title is UTF-8 → convert + std::wstring wWindowName = Utf8ToWide(c_szName ? c_szName : ""); + + m_hWnd = CreateWindowW( + wClassName, // already wide + wWindowName.c_str(), // wide + ws, + 0, 0, 0, 0, + nullptr, + nullptr, + ms_hInstance, + nullptr + ); if (!m_hWnd) return false; - SetWindowLongPtr(m_hWnd, GWLP_USERDATA, (LONG_PTR)this); - //DestroyWindow(ImmGetDefaultIMEWnd(m_hWnd)); - + SetWindowLongPtrW(m_hWnd, GWLP_USERDATA, (LONG_PTR)this); return true; } @@ -197,7 +203,11 @@ void CMSWindow::AdjustSize(int width, int height) void CMSWindow::SetText(const char* c_szText) { - SetWindowText(m_hWnd, c_szText); + if (!m_hWnd) + return; + + std::wstring wText = Utf8ToWide(c_szText ? c_szText : ""); + SetWindowTextW(m_hWnd, wText.c_str()); } void CMSWindow::SetSize(int width, int height) @@ -205,37 +215,41 @@ void CMSWindow::SetSize(int width, int height) SetWindowPos(m_hWnd, NULL, 0, 0, width, height, SWP_NOZORDER|SWP_NOMOVE); } -const char * CMSWindow::RegisterWindowClass(DWORD style, int brush, WNDPROC pfnWndProc, HICON hIcon, int iCursorResource) +const wchar_t* CMSWindow::RegisterWindowClass(DWORD style, int brush, WNDPROC pfnWndProc, HICON hIcon, int iCursorResource) { - char szClassName[1024]; - sprintf(szClassName, "eter - s%x:b%x:p:%x", style, brush, (DWORD) pfnWndProc); + wchar_t szClassName[1024]; + swprintf_s( + szClassName, + L"eter - s%x:b%x:p:%p", + style, + brush, + pfnWndProc + ); - TWindowClassSet::iterator f = ms_stWCSet.find((char*) szClassName); + // Use a set of std::wstring (NOT char*) + TWindowClassSet::iterator it = ms_stWCSet.find(szClassName); + if (it != ms_stWCSet.end()) + return it->c_str(); - if (f != ms_stWCSet.end()) - return *f; + // Persist the string + std::wstring staticClassName = szClassName; + ms_stWCSet.insert(staticClassName); - const char* c_szStaticClassName = stl_static_string(szClassName).c_str(); + WNDCLASSW wc{}; + wc.style = style; + wc.lpfnWndProc = pfnWndProc; + wc.hCursor = LoadCursor(ms_hInstance, MAKEINTRESOURCE(iCursorResource)); + wc.hIcon = hIcon ? hIcon : LoadIcon(ms_hInstance, IDI_APPLICATION); + wc.hbrBackground = (HBRUSH)GetStockObject(brush); + wc.hInstance = ms_hInstance; + wc.lpszClassName = staticClassName.c_str(); + wc.lpszMenuName = nullptr; - ms_stWCSet.insert((char * const) c_szStaticClassName); - - WNDCLASS wc; + if (!RegisterClassW(&wc)) + return nullptr; - wc.style = 0; - wc.cbClsExtra = 0; - wc.cbWndExtra = 0; - wc.lpfnWndProc = pfnWndProc; - wc.hCursor = LoadCursor(ms_hInstance, MAKEINTRESOURCE(iCursorResource)); - wc.hIcon = hIcon ? hIcon : LoadIcon(ms_hInstance, IDI_APPLICATION); - wc.hbrBackground = (HBRUSH) GetStockObject(brush); - wc.hInstance = ms_hInstance; - wc.lpszClassName = c_szStaticClassName; - wc.lpszMenuName = ""; - - if (!RegisterClass(&wc)) - return ""; - - return c_szStaticClassName; + // Return pointer stable inside the set + return ms_stWCSet.find(staticClassName)->c_str(); } CMSWindow::CMSWindow() diff --git a/src/EterLib/MSWindow.h b/src/EterLib/MSWindow.h index 4c362bc..74cface 100644 --- a/src/EterLib/MSWindow.h +++ b/src/EterLib/MSWindow.h @@ -42,10 +42,10 @@ class CMSWindow virtual void OnSize(WPARAM wParam, LPARAM lParam); protected: - const char* RegisterWindowClass(DWORD style, int brush, WNDPROC pfnWndProc, HICON hIcon=NULL, int iCursorResource=32512); + const wchar_t* RegisterWindowClass(DWORD style, int brush, WNDPROC pfnWndProc, HICON hIcon=NULL, int iCursorResource=32512); protected: - typedef std::set TWindowClassSet; + typedef std::set TWindowClassSet; protected: HWND m_hWnd; diff --git a/src/EterLib/TextBar.cpp b/src/EterLib/TextBar.cpp index 1d8d2dc..78d9c81 100644 --- a/src/EterLib/TextBar.cpp +++ b/src/EterLib/TextBar.cpp @@ -2,38 +2,30 @@ #include "TextBar.h" #include "EterLib/Util.h" +#include + void CTextBar::__SetFont(int fontSize, bool isBold) { - int iCodePage = GetDefaultCodePage(); + LOGFONTW logFont{}; - LOGFONT logFont; + logFont.lfHeight = fontSize; + logFont.lfEscapement = 0; + logFont.lfOrientation = 0; + logFont.lfWeight = isBold ? FW_BOLD : FW_NORMAL; + logFont.lfItalic = FALSE; + logFont.lfUnderline = FALSE; + logFont.lfStrikeOut = FALSE; + logFont.lfCharSet = DEFAULT_CHARSET; + logFont.lfOutPrecision = OUT_DEFAULT_PRECIS; + logFont.lfClipPrecision = CLIP_DEFAULT_PRECIS; + logFont.lfQuality = ANTIALIASED_QUALITY; + logFont.lfPitchAndFamily = DEFAULT_PITCH; + wcscpy_s(logFont.lfFaceName, LF_FACESIZE, L"Tahoma"); - memset(&logFont, 0, sizeof(LOGFONT)); - - logFont.lfHeight = fontSize; - logFont.lfEscapement = 0; - logFont.lfOrientation = 0; - - if (isBold) - logFont.lfWeight = FW_BOLD; - else - logFont.lfWeight = FW_NORMAL; - - logFont.lfItalic = FALSE; - logFont.lfUnderline = FALSE; - logFont.lfStrikeOut = FALSE; - logFont.lfCharSet = GetCharsetFromCodePage(iCodePage); - logFont.lfOutPrecision = OUT_DEFAULT_PRECIS; - logFont.lfClipPrecision = CLIP_DEFAULT_PRECIS; - logFont.lfQuality = ANTIALIASED_QUALITY; - logFont.lfPitchAndFamily = DEFAULT_PITCH; - strcpy(logFont.lfFaceName, GetFontFaceFromCodePage(iCodePage)); m_hFont = CreateFontIndirect(&logFont); - HDC hdc = m_dib.GetDCHandle(); m_hOldFont = (HFONT)SelectObject(hdc, m_hFont); - } void CTextBar::SetTextColor(int r, int g, int b) @@ -41,11 +33,24 @@ void CTextBar::SetTextColor(int r, int g, int b) HDC hDC = m_dib.GetDCHandle(); ::SetTextColor(hDC, RGB(r, g, b)); } - -void CTextBar::GetTextExtent(const char * c_szText, SIZE* p_size) + +void CTextBar::GetTextExtent(const char* c_szText, SIZE* p_size) { + if (!c_szText || !p_size) + { + if (p_size) + { + p_size->cx = 0; + p_size->cy = 0; + } + return; + } + HDC hDC = m_dib.GetDCHandle(); - GetTextExtentPoint32(hDC, c_szText, strlen(c_szText), p_size); + + // UTF-8 → UTF-16 + std::wstring wText = Utf8ToWide(c_szText); + GetTextExtentPoint32W(hDC, wText.c_str(), static_cast(wText.length()), p_size); } void CTextBar::TextOut(int ix, int iy, const char * c_szText) @@ -63,10 +68,9 @@ void CTextBar::OnCreate() CTextBar::CTextBar(int fontSize, bool isBold) { - m_hOldFont = NULL; + m_hOldFont = NULL; m_fontSize = fontSize; m_isBold = isBold; - } CTextBar::~CTextBar() diff --git a/src/EterLib/TextTag.cpp b/src/EterLib/TextTag.cpp index de17172..e2492b2 100644 --- a/src/EterLib/TextTag.cpp +++ b/src/EterLib/TextTag.cpp @@ -57,16 +57,24 @@ std::wstring GetTextTagOutputString(const wchar_t * src, int src_len) if (tag == TEXT_TAG_PLAIN || tag == TEXT_TAG_TAG) { - if (hyperlinkStep == 0) + // Show normal text (step 0) AND hyperlink visible text (step 2) + if (hyperlinkStep == 0 || hyperlinkStep == 2) { ++output_len; dst += src[i]; } } else if (tag == TEXT_TAG_HYPERLINK_START) - hyperlinkStep = 1; + hyperlinkStep = 1; // Start metadata (hidden) else if (tag == TEXT_TAG_HYPERLINK_END) - hyperlinkStep = 0; + { + // First |h: end metadata, start visible (1 -> 2) + // Second |h: end visible (2 -> 0) + if (hyperlinkStep == 1) + hyperlinkStep = 2; + else if (hyperlinkStep == 2) + hyperlinkStep = 0; + } i += len; } @@ -98,7 +106,8 @@ int GetTextTagInternalPosFromRenderPos(const wchar_t * src, int src_len, int off } else if (tag == TEXT_TAG_PLAIN || tag == TEXT_TAG_TAG) { - if (hyperlinkStep == 0) + // Show normal text (step 0) AND hyperlink visible text (step 2) + if (hyperlinkStep == 0 || hyperlinkStep == 2) { if (!color_tag) internal_offset = i; @@ -111,9 +120,16 @@ int GetTextTagInternalPosFromRenderPos(const wchar_t * src, int src_len, int off } } else if (tag == TEXT_TAG_HYPERLINK_START) - hyperlinkStep = 1; + hyperlinkStep = 1; // Start metadata (hidden) else if (tag == TEXT_TAG_HYPERLINK_END) - hyperlinkStep = 0; + { + // First |h: end metadata, start visible (1 -> 2) + // Second |h: end visible (2 -> 0) + if (hyperlinkStep == 1) + hyperlinkStep = 2; + else if (hyperlinkStep == 2) + hyperlinkStep = 0; + } i += len; } diff --git a/src/EterLib/Util.cpp b/src/EterLib/Util.cpp index 409c425..c0ddae4 100644 --- a/src/EterLib/Util.cpp +++ b/src/EterLib/Util.cpp @@ -133,252 +133,3 @@ D3DXCOLOR TokenToColor(CTokenVector & rVector) atof(rVector[2].c_str()), atof(rVector[3].c_str())); } - -/////////////////////////////////////////////////////////////////////////////////////////////////// - -static std::string gs_fontFace=""; -static DWORD gs_codePage=0; - -int CALLBACK EnumFontFamExProc(CONST LOGFONT* plogFont, CONST TEXTMETRIC* /*textMetric*/, DWORD /*dwWord*/, LPARAM lParam) -{ - return stricmp((const char*)lParam, plogFont->lfFaceName); -} - -int GetCharsetFromCodePage(WORD codePage) -{ - switch( codePage ) - { - case CP_932: - return SHIFTJIS_CHARSET; - case CP_949: - return HANGUL_CHARSET; - case CP_936: - return GB2312_CHARSET; - case CP_950: - return CHINESEBIG5_CHARSET; - case CP_1253: - return GREEK_CHARSET; - case CP_1254: - return TURKISH_CHARSET; - case CP_1255: - return HEBREW_CHARSET; - case CP_1256: - return ARABIC_CHARSET; - case CP_1257: - return BALTIC_CHARSET; - case CP_1258: - return VIETNAMESE_CHARSET; - case CP_874: - return THAI_CHARSET; - case CP_1250: - return EASTEUROPE_CHARSET; - case CP_1251: - return RUSSIAN_CHARSET; - default: - return DEFAULT_CHARSET; - } -} - - -const char* GetFontFaceFromCodePageNT(WORD codePage) -{ - switch( codePage ) - { - case CP_932: - return "MS PGothic"; - case CP_949: - return "GulimChe"; - case CP_936: - return "SimSun"; - case CP_950: - return "MingLiU"; - case CP_874: - return "Tahoma"; - case CP_1252: - return "Arial"; - case CP_1256: - return "Tahoma"; - case CP_1258: - return "Tahoma"; - case CP_65001: - return "Arial"; - default: - return "Arial"; - } -} -const char* GetFontFaceFromCodePage9x(WORD codePage) -{ - switch( codePage ) - { - case CP_932: - return "굃굍 굊긕긘긞긏"; - case CP_949: - return "굴림체"; - case CP_936: - return "芥竟"; - case CP_950: - return "꾄ⁿ톱"; - case CP_874: - return "Tahoma"; - case CP_1252: - return "Arial"; - case CP_1256: - return "Tahoma"; - case CP_1258: - return "Tahoma"; - case CP_65001: - return "Arial"; - default: - return "Arial"; - } -} - -DWORD GetDefaultCodePage() -{ - return gs_codePage; -} - -const char * GetDefaultFontFace() -{ - return gs_fontFace.c_str(); -} - -const char* GetFontFaceFromCodePage(WORD codePage) -{ - LOGFONTA logFont = {}; - logFont.lfCharSet = GetCharsetFromCodePage(codePage); - - const char* fontFace = GetFontFaceFromCodePage9x(codePage); - - HDC hDC = GetDC(NULL); - - if(EnumFontFamiliesEx(hDC, &logFont, EnumFontFamExProc, (LPARAM)fontFace, 0) == 0) - { - ReleaseDC(NULL, hDC); - return fontFace; - } - - fontFace = GetFontFaceFromCodePageNT(codePage); - - if(EnumFontFamiliesEx(hDC, &logFont, EnumFontFamExProc, (LPARAM)fontFace, 0) == 0) - { - ReleaseDC(NULL, hDC); - return fontFace; - } - - ReleaseDC(NULL, hDC); - - return GetDefaultFontFace(); -} - -void SetDefaultFontFace(const char* fontFace) -{ - gs_fontFace=fontFace; -} - -bool SetDefaultCodePage(DWORD codePage) -{ - gs_codePage=codePage; - - std::string fontFace=GetFontFaceFromCodePage(codePage); - if (fontFace.empty()) - return false; - - SetDefaultFontFace(fontFace.c_str()); - - return true; -} - - -int __base64_get( const int c ) -{ - if( 'A' <= c && c <= 'Z' ) - return c-'A'; - if( 'a' <= c && c <= 'z' ) - return c - 'a' + 26; - if( '0' <= c && c <= '9' ) - return c - '0' + 52; - if( c == '+' ) - return 62; - if( c == '/' ) - return 63; - if( c == '=' ) // end of line - return -1; - return -2; // non value; -} - -void __strcat1(char * str,int i) -{ - char result[2]; - result[0] = i; - result[1] = NULL; - strcat(str,result); -} - -void base64_decode(const char * str,char * resultStr) -{ - int nCount=0, i=0, r, result; - int length = strlen(str); - char szDest[5]=""; - - strcpy(resultStr,""); - while(nCount < length) - { - i=0; - strcpy(szDest, ""); - while(nCount= length ) // 데이터의 끝에 도달했다. - { - if( szDest[1] == '@' ) - { - __strcat1(resultStr,(szDest[0]<<2)); - break; - }// exit while loop - else - __strcat1(resultStr,(szDest[0]<<2 | szDest[1]>>4)); // 1 Byte - if( szDest[2] == '@' ) - { - __strcat1(resultStr,(szDest[1]<<4)); - break; - } - else - __strcat1(resultStr,(szDest[1]<<4 | szDest[2]>>2)); // 2 Byte - if( szDest[3] == '@' ) - { - __strcat1(resultStr,(szDest[2]<<6)); - break; - } - else - __strcat1(resultStr,(szDest[2]<<6 | szDest[3])); // 3 Byte - } - else - { - __strcat1(resultStr,(szDest[0]<<2 | szDest[1]>>4)); // 1 Byte - __strcat1(resultStr,(szDest[1]<<4 | szDest[2]>>2)); // 2 Byte - __strcat1(resultStr,(szDest[2]<<6 | szDest[3])); // 3 Byte - } - } - - }// end of while - - for (i = 0; i < strlen(resultStr); i++) - { - char c = resultStr[i]; - int xx = i + 5; - resultStr[i] = char(c ^ xx); - } - // E -} diff --git a/src/EterLib/Util.h b/src/EterLib/Util.h index f1ec497..1c8c7ac 100644 --- a/src/EterLib/Util.h +++ b/src/EterLib/Util.h @@ -96,18 +96,5 @@ extern D3DXCOLOR TokenToColor(CTokenVector & rVector); #define GOTO_CHILD_NODE(TextFileLoader, Index) CTextFileLoader::CGotoChild Child(TextFileLoader, Index); -/////////////////////////////////////////////////////////////////////////////////////////////////// - -extern int CALLBACK EnumFontFamExProc(CONST LOGFONT* plogFont, CONST TEXTMETRIC* textMetric, DWORD dwWord, LPARAM lParam); -extern int GetCharsetFromCodePage(WORD codePage); -extern const char* GetFontFaceFromCodePageNT(WORD codePage); -extern const char* GetFontFaceFromCodePage9x(WORD codePage); -extern DWORD GetDefaultCodePage(); -extern const char * GetDefaultFontFace(); -extern const char* GetFontFaceFromCodePage(WORD codePage); -extern void SetDefaultFontFace(const char* fontFace); -extern bool SetDefaultCodePage(DWORD codePage); -extern void base64_decode(const char * str,char * resultStr); - extern DWORD GetMaxTextureWidth(); extern DWORD GetMaxTextureHeight(); \ No newline at end of file diff --git a/src/EterLib/parser.cpp b/src/EterLib/parser.cpp index 110806b..86dc9ae 100644 --- a/src/EterLib/parser.cpp +++ b/src/EterLib/parser.cpp @@ -3,34 +3,48 @@ using namespace script; -#define ishan(ch) (((ch) & 0xE0) > 0x90) -#define isnhspace(ch) (!ishan(ch) && isspace(ch)) +static const char* Utf8Next(const char* p, const char* end) +{ + if (!p || p >= end) return end; + unsigned char c = (unsigned char)*p; + if (c < 0x80) return p + 1; + if ((c >> 5) == 0x6) return (p + 2 <= end) ? p + 2 : end; + if ((c >> 4) == 0xE) return (p + 3 <= end) ? p + 3 : end; + if ((c >> 3) == 0x1E) return (p + 4 <= end) ? p + 4 : end; + // invalid lead byte -> move 1 to avoid infinite loops + return p + 1; +} - -extern DWORD GetDefaultCodePage(); +static const char* Utf8Prev(const char* base, const char* p) +{ + if (!base || !p || p <= base) return base; + const char* q = p - 1; + // move back over continuation bytes 10xxxxxx + while (q > base && (((unsigned char)*q & 0xC0) == 0x80)) + --q; + return q; +} const char* LocaleString_FindChar(const char* base, int len, char test) { if (!base) - return NULL; + return nullptr; - DWORD codePage = GetDefaultCodePage(); - int pos = 0; while (pos < len) { const char* cur = base + pos; - const char* next = CharNextExA(codePage, cur, 0); - int cur_len = next - cur; + const char* next = Utf8Next(cur, base + len); + int cur_len = int(next - cur); + if (cur_len > 1) { pos += cur_len; } - else if (1 == cur_len) + else if (cur_len == 1) { if (*cur == test) return cur; - ++pos; } else @@ -38,36 +52,31 @@ const char* LocaleString_FindChar(const char* base, int len, char test) break; } } - return NULL; + + return nullptr; } int LocaleString_RightTrim(char* base, int len) { - DWORD codePage = GetDefaultCodePage(); - int pos = len; - + while (pos > 0) { char* cur = base + pos; - char* prev = CharPrevExA(codePage, base, cur , 0); - - int prev_len = cur - prev; + char* prev = (char*)Utf8Prev(base, cur); + + int prev_len = int(cur - prev); if (prev_len != 1) break; - - if (!isspace((unsigned char) *prev) && *prev != '\n' && *prev != '\r') - break; - + + if (!isspace((unsigned char)*prev) && *prev != '\n' && *prev != '\r') + break; + *prev = '\0'; - pos -= prev_len; } - if (pos > 0) - return pos; - - return 0; + return (pos > 0) ? pos : 0; } void LocaleString_RightTrim(char* base) @@ -75,52 +84,10 @@ void LocaleString_RightTrim(char* base) LocaleString_RightTrim(base, strlen(base)); } -void OLD_rtrim(char* base) -{ - if (!base) - return; - - DWORD codePage = GetDefaultCodePage(); - - if (949 == codePage || 936 == codePage) - { - char* end = base + strlen(base) - 1; - - while (end != base) - { - if (!isnhspace((unsigned char) *end) && *end != '\n' && *end != '\r' || (end!=base && *((unsigned char*)end-1)>0xa0)) - break; - - *end = '\0'; - - end = CharPrevExA(codePage, base, end, 0); - } - } - else - { - char* end = base + strlen(base); - - while (end != base) - { - char* prev = CharPrevExA(codePage, base, end, 0); - - int prev_len = end - prev; - if (prev_len != 1) - break; - - if (!isspace((unsigned char) *prev) && *prev != '\n' && *prev != '\r') - break; - - *prev = '\0'; - - end = prev; - } - } -} - -const char* LocaleString_Skip(DWORD codePage, const char* cur) +const char* LocaleString_Skip(const char* cur) { int loopCount = 0; + const char* end = cur + strlen(cur); while (*cur) { @@ -130,44 +97,42 @@ const char* LocaleString_Skip(DWORD codePage, const char* cur) break; } - const char* next = CharNextExA(codePage, cur, 0); - int cur_len = next - cur; + const char* next = Utf8Next(cur, end); + int cur_len = int(next - cur); + if (cur_len > 1) - { cur = next; - } - else if (1 == cur_len) + else if (cur_len == 1) { - if (!isspace((unsigned char) *cur) && *cur != '\n' && *cur != '\r') + if (!isspace((unsigned char)*cur) && *cur != '\n' && *cur != '\r') return cur; + ++cur; } else - { break; - } } return cur; } bool Group::GetArg(const char *c_arg_base, int arg_len, TArgList & argList) { - char szName[32 + 1]; - char szValue[64 + 1]; + char szName[32 + 1]; + char szValue[64 + 1]; - int iNameLen = 0; - int iValueLen = 0; + int iNameLen = 0; + int iValueLen = 0; int iCharLen = 0; int pos = 0; - bool isValue = false; + bool isValue = false; - DWORD codePage = GetDefaultCodePage(); - - while (pos < arg_len) - { + while (pos < arg_len) + { + const char* end = c_arg_base + arg_len; const char* cur = c_arg_base + pos; - const char* next = CharNextExA(codePage, cur, 0); + const char* next = Utf8Next(cur, end); + iCharLen = next - cur; if (iCharLen > 1) @@ -180,7 +145,7 @@ bool Group::GetArg(const char *c_arg_base, int arg_len, TArgList & argList) return false; } - memcpy(szValue+iValueLen, cur, iCharLen); + memcpy(szValue+iValueLen, cur, iCharLen); iValueLen += iCharLen; szValue[iValueLen] = '\0'; } @@ -191,7 +156,7 @@ bool Group::GetArg(const char *c_arg_base, int arg_len, TArgList & argList) TraceError("argument name overflow: must be shorter than 32 letters"); return false; } - memcpy(szName+iNameLen, cur, iCharLen); + memcpy(szName+iNameLen, cur, iCharLen); iNameLen += iCharLen; szName[iNameLen] = '\0'; } @@ -220,11 +185,9 @@ bool Group::GetArg(const char *c_arg_base, int arg_len, TArgList & argList) { isValue = true; } - // 값이 아니고, 이름이 시작되지 않았을 경우 빈칸은 건너 뛴다. else if (!isValue && iNameLen == 0 && isspace((unsigned char) c)) { } - // 엔터는 건너 뛴다 else if (c == '\r' || c == '\n') { } @@ -238,9 +201,9 @@ bool Group::GetArg(const char *c_arg_base, int arg_len, TArgList & argList) return false; } - memcpy(szValue+iValueLen, cur, iCharLen); + memcpy(szValue+iValueLen, cur, iCharLen); iValueLen += iCharLen; - szValue[iValueLen] = '\0'; + szValue[iValueLen] = '\0'; } else { @@ -249,10 +212,10 @@ bool Group::GetArg(const char *c_arg_base, int arg_len, TArgList & argList) TraceError("argument name overflow: must be shorter than 32 letters"); return false; } - memcpy(szName+iNameLen, cur, iCharLen); + memcpy(szName+iNameLen, cur, iCharLen); iNameLen += iCharLen; - szName[iNameLen] = '\0'; - } + szName[iNameLen] = '\0'; + } } } else @@ -261,122 +224,128 @@ bool Group::GetArg(const char *c_arg_base, int arg_len, TArgList & argList) } pos += iCharLen; - } + } - if (iNameLen != 0 && iValueLen != 0) - { + if (iNameLen != 0 && iValueLen != 0) + { iNameLen = LocaleString_RightTrim(szName, iNameLen); iValueLen = LocaleString_RightTrim(szValue, iValueLen); - argList.push_back(TArg(szName, szValue)); - } + argList.push_back(TArg(szName, szValue)); + } - return true; + return true; } - -bool Group::Create(const std::string & stSource) +bool Group::Create(const std::string& stSource) { m_cmdList.clear(); if (stSource.empty()) return false; - const char *str_base = stSource.c_str(); - if (!str_base || !*str_base) - { - TraceError("Source file has no content"); - return false; - } - int str_len = stSource.length(); - int str_pos = 0; - - DWORD codePage = GetDefaultCodePage(); + const char* str_base = stSource.c_str(); + if (!str_base || !*str_base) + { + TraceError("Source file has no content"); + return false; + } - char box_data[1024 + 1]; + const int str_len = (int)stSource.size(); + int str_pos = 0; + + char box_data[1024 + 1]; static std::string stLetter; - - while (str_pos < str_len) - { - TCmd cmd; + + while (str_pos < str_len) + { + TCmd cmd; const char* word = str_base + str_pos; - const char* word_next = CharNextExA(codePage, word, 0); - - int word_len = word_next - word; - + const char* end = str_base + str_len; + + const char* word_next = Utf8Next(word, end); + if (!word_next || word_next <= word) + { + // Invalid UTF-8 sequence or broken helper -> advance 1 byte to avoid infinite loop + word_next = word + 1; + } + + const int word_len = (int)(word_next - word); + if (word_len > 1) { str_pos += word_len; - { - stLetter.assign(word, word_next); - - cmd.name.assign("LETTER"); - cmd.argList.push_back(TArg("value", stLetter)); - - m_cmdList.push_back(cmd); - } - + stLetter.assign(word, word_next); + cmd.name.assign("LETTER"); + cmd.argList.push_back(TArg("value", stLetter)); + m_cmdList.push_back(cmd); } else if (word_len == 1) { const char cur = *word; - if ('[' == cur) + if (cur == '[') { ++str_pos; const char* box_begin = str_base + str_pos; - const char* box_end = LocaleString_FindChar(box_begin, str_len - str_pos, ']'); + const char* box_end = LocaleString_FindChar(box_begin, str_len - str_pos, ']'); if (!box_end) { TraceError(" !! PARSING ERROR - Syntax Error : %s\n", box_begin); return false; } - str_pos += box_end - box_begin + 1; - + + str_pos += (int)(box_end - box_begin) + 1; int data_len = 0; { - const char* data_begin = LocaleString_Skip(codePage, box_begin); + const char* data_begin = LocaleString_Skip(box_begin); const char* data_end = box_end; - data_len = data_end - data_begin; + + data_len = (int)(data_end - data_begin); if (data_len >= 1024) { TraceError(" !! PARSING ERROR - Buffer Overflow : %d, %s\n", data_len, str_base); return false; } - memcpy(box_data, data_begin, data_len); + + memcpy(box_data, data_begin, (size_t)data_len); box_data[data_len] = '\0'; - data_len = LocaleString_RightTrim(box_data, data_len); // 오른쪽 빈칸 자르기 + data_len = LocaleString_RightTrim(box_data, data_len); } { const char* space = LocaleString_FindChar(box_data, data_len, ' '); - if (space) // 인자가 있음 + if (space) { - int name_len = space - box_data; + const int name_len = (int)(space - box_data); cmd.name.assign(box_data, name_len); - - const char* space_next = CharNextExA(codePage, space, 0); - const char* arg = LocaleString_Skip(codePage, space_next); - int arg_len = data_len - (arg - box_data); - + const char* data_end = box_data + data_len; + + const char* space_next = Utf8Next(space, data_end); + if (!space_next || space_next <= space) + space_next = space + 1; + + const char* arg = LocaleString_Skip(space_next); + + const int arg_len = (int)(data_len - (arg - box_data)); if (!GetArg(arg, arg_len, cmd.argList)) { TraceError(" !! PARSING ERROR - Unknown Arguments : %d, %s\n", arg_len, arg); return false; } } - else // 인자가 없으므로 모든 스트링이 명령어다. + else { cmd.name.assign(box_data); cmd.argList.clear(); } - + m_cmdList.push_back(cmd); } } @@ -387,51 +356,49 @@ bool Group::Create(const std::string & stSource) else { ++str_pos; - - { - stLetter.assign(1, cur); - cmd.name.assign("LETTER"); - cmd.argList.push_back(TArg("value", stLetter)); - m_cmdList.push_back(cmd); - } + + stLetter.assign(1, cur); + cmd.name.assign("LETTER"); + cmd.argList.push_back(TArg("value", stLetter)); + m_cmdList.push_back(cmd); } } else { break; } - } + } - return true; + return true; } bool Group::GetCmd(TCmd & cmd) { - if (m_cmdList.empty()) - return false; + if (m_cmdList.empty()) + return false; - cmd = m_cmdList.front(); - m_cmdList.pop_front(); - return true; + cmd = m_cmdList.front(); + m_cmdList.pop_front(); + return true; } bool Group::ReadCmd(TCmd & cmd) { - if (m_cmdList.empty()) - return false; + if (m_cmdList.empty()) + return false; - cmd = m_cmdList.front(); - return true; + cmd = m_cmdList.front(); + return true; } std::string & Group::GetError() { - return m_stError; + return m_stError; } void Group::SetError(const char * c_pszError) { - m_stError.assign(c_pszError); + m_stError.assign(c_pszError); } Group::Group() diff --git a/src/EterLib/parser.h b/src/EterLib/parser.h index 1578a2f..59e262c 100644 --- a/src/EterLib/parser.h +++ b/src/EterLib/parser.h @@ -6,89 +6,69 @@ namespace script { - typedef struct SArgumet - { - SArgumet(const std::string& c_stName, const std::string& c_stValue) - { - strName = c_stName; - strValue = c_stValue; - } + typedef struct SArgumet + { + SArgumet(const std::string& c_stName, const std::string& c_stValue) + { + strName = c_stName; + strValue = c_stValue; + } + SArgumet(const SArgumet& c_arg) { strName = c_arg.strName; strValue = c_arg.strValue; - } + } + void operator=(const SArgumet& c_arg) { strName = c_arg.strName; strValue = c_arg.strValue; - } - std::string strName; - std::string strValue; - } TArg; - - typedef std::list TArgList; - - typedef struct SCmd - { - std::string name; - TArgList argList; + } - SCmd() - {} + std::string strName; + std::string strValue; + } TArg; + + typedef std::list TArgList; + + typedef struct SCmd + { + std::string name; + TArgList argList; + + SCmd() {} SCmd(const SCmd& c_cmd) { name = c_cmd.name; argList = c_cmd.argList; } + void operator=(const SCmd& c_cmd) { name = c_cmd.name; argList = c_cmd.argList; } - } TCmd; - - class Group - { + } TCmd; + + class Group + { public: Group(); ~Group(); - + public: - /** 스트링으로 부터 스크립트 그룹을 만든다. - * - * 실패하면 GetError 메소드로 확인할 수 있다. - * - * @param stSource 이 스트링으로 부터 그룹이 만들어 진다. - * @return 성공시 true, 실패하면 false - */ - bool Create(const std::string & stSource); - - /** 명령어를 받는 메소드 - * - * @param cmd 성공시에 이 구조체로 명령어가 복사 된다. - * @return 명령어가 남아 있다면 true, 없다면 false - */ - bool GetCmd(TCmd & cmd); + bool Create(const std::string & stSource); + bool GetCmd(TCmd & cmd); + bool ReadCmd(TCmd & cmd); + std::string & GetError(); - /* - 명령어를 가져오되 꺼내지는 않는다. - */ - bool ReadCmd(TCmd & cmd); - - /** 에러를 출력 받는 메소드 - * - * @return stError 이 곳으로 에러가 출력 된다. - */ - std::string & GetError(); - private: - void SetError(const char *str); - bool GetArg(const char * c_atr_base, int arg_len, TArgList & argList); - - std::string m_stError; - std::list m_cmdList; - }; -} + void SetError(const char *str); + bool GetArg(const char * c_atr_base, int arg_len, TArgList & argList); + std::string m_stError; + std::list m_cmdList; + }; +} #endif diff --git a/src/EterLocale/Arabic.cpp b/src/EterLocale/Arabic.cpp index e735e7e..4536e56 100644 --- a/src/EterLocale/Arabic.cpp +++ b/src/EterLocale/Arabic.cpp @@ -244,8 +244,10 @@ bool Arabic_IsComb2(wchar_t code) } size_t Arabic_MakeShape(wchar_t* src, size_t srcLen, wchar_t* dst, size_t dstLen) -{ - assert(dstLen >= srcLen); +{ + // Runtime validation instead of assert (which is disabled in release builds) + if (!src || !dst || srcLen == 0 || dstLen < srcLen) + return 0; const size_t srcLastIndex = srcLen - 1; diff --git a/src/EterLocale/CodePageId.h b/src/EterLocale/CodePageId.h index fec6697..6e601ba 100644 --- a/src/EterLocale/CodePageId.h +++ b/src/EterLocale/CodePageId.h @@ -1,46 +1,3 @@ #pragma once -#define CP_874 874 // Thai -#define CP_THAI CP_874 - -#define CP_932 932 // Japanese -#define CP_JAPANESE CP_932 - -#define CP_936 936 // Chinese(Simplefied) -#define CP_CHINESE_SIMPLE CP_936 - -#define CP_949 949 // Hangeul -#define CP_HANGUL CP_949 - -#define CP_950 950 // Chinese(Traditional) -#define CP_CHINESE_TRAD CP_950 - -#define CP_1250 1250 // Middle and East Europe -#define CP_EASTEUROPE CP_1250 - -#define CP_1251 1251 // Cyrillic -#define CP_CYRILLIC CP_1251 - -#define CP_1252 1252 // Latin -#define CP_LATIN CP_1252 - -#define CP_1253 1253 // Greek -#define CP_GREEK CP_1253 - -#define CP_1254 1254 // Turkish -#define CP_TURKISH CP_1254 - -#define CP_1255 1255 // Hebrew -#define CP_HEBREW CP_1255 - -#define CP_1256 1256 // Aralic -#define CP_ARABIC CP_1256 - -#define CP_1257 1257 // Baltic -#define CP_BALTIC CP_1257 - -#define CP_1258 1258 // Vietnamese -#define CP_VIETNAMESE CP_1258 - -#define CP_65001 65001 // UTF-8 -//#define CP_UTF8 65001 // UTF-8 translation +#define CP_UTF8 65001 // UTF-8 translation diff --git a/src/EterPythonLib/PythonGraphic.cpp b/src/EterPythonLib/PythonGraphic.cpp index 312aacb..9749fcb 100644 --- a/src/EterPythonLib/PythonGraphic.cpp +++ b/src/EterPythonLib/PythonGraphic.cpp @@ -2,6 +2,7 @@ #include "EterLib/StateManager.h" #include "EterLib/JpegFile.h" #include "PythonGraphic.h" +#include bool g_isScreenShotKey = false; @@ -307,7 +308,9 @@ bool CPythonGraphic::SaveScreenShot(const char * c_pszFileName) if (g_isScreenShotKey) { - FILE* srcFilePtr = fopen(c_pszFileName, "rb"); + // UTF-8 → UTF-16 conversion for Unicode path support + std::wstring wFileName = Utf8ToWide(c_pszFileName); + FILE* srcFilePtr = _wfopen(wFileName.c_str(), L"rb"); if (srcFilePtr) { fseek(srcFilePtr, 0, SEEK_END); @@ -367,8 +370,7 @@ bool CPythonGraphic::SaveScreenShot(const char * c_pszFileName) exifHeader[2] = sizeof(exifHeader) + imgDescLen; - FILE* dstFilePtr = fopen(c_pszFileName, "wb"); - //FILE* dstFilePtr = fopen("temp.jpg", "wb"); + FILE* dstFilePtr = _wfopen(wFileName.c_str(), L"wb"); if (dstFilePtr) { fwrite(head, sizeof(head), 1, dstFilePtr); diff --git a/src/EterPythonLib/PythonGraphicModule.cpp b/src/EterPythonLib/PythonGraphicModule.cpp index 3b69108..63167df 100644 --- a/src/EterPythonLib/PythonGraphicModule.cpp +++ b/src/EterPythonLib/PythonGraphicModule.cpp @@ -2,6 +2,8 @@ #include "EterLib/Camera.h" #include "EterLib/TextBar.h" +#include +#include #include PyObject* grpCreateTextBar(PyObject* poSelf, PyObject* poArgs) @@ -830,35 +832,50 @@ PyObject * grpSaveScreenShotToPath(PyObject * poSelf, PyObject * poArgs) } // END_OF_SCREENSHOT_CWDSAVE -PyObject * grpSaveScreenShot(PyObject * poSelf, PyObject * poArgs) +PyObject* grpSaveScreenShot(PyObject* poSelf, PyObject* poArgs) { - struct tm * tmNow; - time_t ct; + time_t ct = time(nullptr); + tm tmNow{}; + localtime_s(&tmNow, &ct); - ct = time(0); - tmNow = localtime(&ct); + wchar_t wPath[MAX_PATH + 256]{}; - char szPath[MAX_PATH + 256]; - SHGetSpecialFolderPath(NULL, szPath, CSIDL_PERSONAL, TRUE); - //GetTempPath(); - strcat(szPath, "\\METIN2\\"); + if (!SHGetSpecialFolderPathW(nullptr, wPath, CSIDL_PERSONAL, TRUE)) + { + TraceError("SHGetSpecialFolderPathW failed"); + return Py_BuildValue("(is)", FALSE, ""); + } - if (-1 == _access(szPath, 0)) - if (!CreateDirectory(szPath, NULL)) + wcscat_s(wPath, L"\\METIN2\\"); + + if (_waccess(wPath, 0) == -1) + { + if (!CreateDirectoryW(wPath, nullptr) && GetLastError() != ERROR_ALREADY_EXISTS) { - TraceError("Failed to create directory [%s]\n", szPath); + TraceError("Failed to create directory [%s]", WideToUtf8(wPath).c_str()); return Py_BuildValue("(is)", FALSE, ""); } + } - sprintf(szPath + strlen(szPath), "%02d%02d_%02d%02d%02d.jpg", - tmNow->tm_mon + 1, - tmNow->tm_mday, - tmNow->tm_hour, - tmNow->tm_min, - tmNow->tm_sec); + wchar_t wFile[64]{}; + swprintf_s( + wFile, + L"%02d%02d_%02d%02d%02d.jpg", + tmNow.tm_mon + 1, + tmNow.tm_mday, + tmNow.tm_hour, + tmNow.tm_min, + tmNow.tm_sec + ); - BOOL bResult = CPythonGraphic::Instance().SaveScreenShot(szPath); - return Py_BuildValue("(is)", bResult, szPath); + wcscat_s(wPath, wFile); + + // If SaveScreenShot accepts wide paths: + BOOL bResult = CPythonGraphic::Instance().SaveScreenShot(WideToUtf8(wPath).c_str()); + + // Python expects bytes -> return UTF-8 + std::string pathUtf8 = WideToUtf8(wPath); + return Py_BuildValue("(is)", bResult, pathUtf8.c_str()); } PyObject * grpSetGamma(PyObject * poSelf, PyObject * poArgs) diff --git a/src/EterPythonLib/PythonWindow.cpp b/src/EterPythonLib/PythonWindow.cpp index 1c5fe75..f98e875 100644 --- a/src/EterPythonLib/PythonWindow.cpp +++ b/src/EterPythonLib/PythonWindow.cpp @@ -5,6 +5,7 @@ #include "PythonWindowManager.h" #include "EterLib/StateManager.h" +#include "UserInterface/Locale.h" BOOL g_bOutlineBoxEnable = FALSE; @@ -781,16 +782,6 @@ namespace UI return FALSE; } - BOOL CWindow::OnIMEChangeCodePage() - { - long lValue; - if (PyCallClassMemberFunc(m_poHandler, "OnIMEChangeCodePage", BuildEmptyTuple(), &lValue)) - if (0 != lValue) - return TRUE; - - return FALSE; - } - BOOL CWindow::OnIMEOpenCandidateListEvent() { long lValue; @@ -1053,6 +1044,9 @@ namespace UI m_TextInstance.SetColor(0.78f, 0.78f, 0.78f); m_TextInstance.SetHorizonalAlign(CGraphicTextInstance::HORIZONTAL_ALIGN_LEFT); m_TextInstance.SetVerticalAlign(CGraphicTextInstance::VERTICAL_ALIGN_TOP); + + m_baseDirection = ::IsRTL() ? CGraphicTextInstance::ETextDirection::RTL : CGraphicTextInstance::ETextDirection::LTR; + m_TextInstance.SetTextDirection(m_baseDirection); } CTextLine::~CTextLine() { @@ -1132,10 +1126,41 @@ namespace UI return m_TextInstance.PixelPositionToCharacterPosition(lx); } + void CTextLine::SetBaseDirection(int iDir) + { + if (iDir == 2) + m_baseDirection = CGraphicTextInstance::ETextDirection::RTL; + else if (iDir == 1) + m_baseDirection = CGraphicTextInstance::ETextDirection::LTR; + else + m_baseDirection = CGraphicTextInstance::ETextDirection::Auto; + + // Apply paragraph direction to the text instance + m_TextInstance.SetTextDirection(m_baseDirection); + + // Re-anchor immediately + OnChangePosition(); + } + + int CTextLine::GetBaseDirection() const + { + if (m_baseDirection == CGraphicTextInstance::ETextDirection::RTL) + return 2; + if (m_baseDirection == CGraphicTextInstance::ETextDirection::LTR) + return 1; + return 0; + } + void CTextLine::OnSetText(const char * c_szText) { m_TextInstance.SetValue(c_szText); + + // Use the control's base direction (AUTO/LTR/RTL) + m_TextInstance.SetTextDirection(m_baseDirection); m_TextInstance.Update(); + + // RTL anchor + OnChangePosition(); } void CTextLine::OnUpdate() @@ -1151,16 +1176,16 @@ namespace UI void CTextLine::OnChangePosition() { - // FOR_ARABIC_ALIGN - //if (m_TextInstance.GetHorizontalAlign() == CGraphicTextInstance::HORIZONTAL_ALIGN_ARABIC) - if( GetDefaultCodePage() == CP_ARABIC ) - { - m_TextInstance.SetPosition(m_rect.right, m_rect.top); - } + bool bAnchorRTL = false; + + if (m_baseDirection == CGraphicTextInstance::ETextDirection::RTL) + bAnchorRTL = true; + else if (m_baseDirection == CGraphicTextInstance::ETextDirection::LTR) + bAnchorRTL = false; else - { - m_TextInstance.SetPosition(m_rect.left, m_rect.top); - } + bAnchorRTL = m_TextInstance.IsRTL(); // AUTO fallback + + m_TextInstance.SetPosition(bAnchorRTL ? m_rect.right : m_rect.left, m_rect.top); } /////////////////////////////////////////////////////////////////////////////////////////////// @@ -2096,10 +2121,10 @@ namespace UI void CDragButton::OnChangePosition() { - m_x = std::max(m_x, m_restrictArea.left); - m_y = std::max(m_y, m_restrictArea.top); - m_x = std::min(m_x, std::max(0l, m_restrictArea.right - m_lWidth)); - m_y = std::min(m_y, std::max(0l, m_restrictArea.bottom - m_lHeight)); + m_x = (std::max)(m_x, m_restrictArea.left); + m_y = (std::max)(m_y, m_restrictArea.top); + m_x = (std::min)(m_x, (std::max)(0l, m_restrictArea.right - m_lWidth)); + m_y = (std::min)(m_y, (std::max)(0l, m_restrictArea.bottom - m_lHeight)); m_rect.left = m_x; m_rect.top = m_y; diff --git a/src/EterPythonLib/PythonWindow.h b/src/EterPythonLib/PythonWindow.h index 311cae0..93cb66d 100644 --- a/src/EterPythonLib/PythonWindow.h +++ b/src/EterPythonLib/PythonWindow.h @@ -139,7 +139,6 @@ namespace UI virtual BOOL OnIMEReturnEvent(); virtual BOOL OnIMEKeyDownEvent(int ikey); - virtual BOOL OnIMEChangeCodePage(); virtual BOOL OnIMEOpenCandidateListEvent(); virtual BOOL OnIMECloseCandidateListEvent(); virtual BOOL OnIMEOpenReadingWndEvent(); @@ -298,6 +297,9 @@ namespace UI void GetTextSize(int* pnWidth, int* pnHeight); + void SetBaseDirection(int iDir); + int GetBaseDirection() const; + protected: void OnUpdate(); void OnRender(); @@ -307,6 +309,7 @@ namespace UI protected: CGraphicTextInstance m_TextInstance; + CGraphicTextInstance::ETextDirection m_baseDirection; }; class CNumberLine : public CWindow diff --git a/src/EterPythonLib/PythonWindowManager.cpp b/src/EterPythonLib/PythonWindowManager.cpp index 8a79315..3c22f6a 100644 --- a/src/EterPythonLib/PythonWindowManager.cpp +++ b/src/EterPythonLib/PythonWindowManager.cpp @@ -1133,15 +1133,6 @@ namespace UI // NOTE : 전체로 돌리지 않고 Activate되어있는 EditLine에만 보내는 이벤트 } - void CWindowManager::RunChangeCodePage() - { - if (m_pActiveWindow) - if (m_pActiveWindow->IsRendering()) - { - if (m_pActiveWindow->OnIMEChangeCodePage()) - return; - } - } void CWindowManager::RunOpenCandidate() { if (m_pLockWindow) diff --git a/src/EterPythonLib/PythonWindowManager.h b/src/EterPythonLib/PythonWindowManager.h index 1ccb22a..40abd42 100644 --- a/src/EterPythonLib/PythonWindowManager.h +++ b/src/EterPythonLib/PythonWindowManager.h @@ -126,7 +126,6 @@ namespace UI void RunIMETabEvent(); void RunIMEReturnEvent(); void RunIMEKeyDown(int vkey); - void RunChangeCodePage(); void RunOpenCandidate(); void RunCloseCandidate(); void RunOpenReading(); diff --git a/src/EterPythonLib/PythonWindowManagerModule.cpp b/src/EterPythonLib/PythonWindowManagerModule.cpp index e74e540..dfdcdc9 100644 --- a/src/EterPythonLib/PythonWindowManagerModule.cpp +++ b/src/EterPythonLib/PythonWindowManagerModule.cpp @@ -1822,6 +1822,19 @@ PyObject * wndTextSetLimitWidth(PyObject * poSelf, PyObject * poArgs) ((UI::CTextLine*)pWindow)->SetLimitWidth(fWidth); return Py_BuildNone(); } +PyObject * wndTextSetBaseDirection(PyObject * poSelf, PyObject * poArgs) +{ + UI::CWindow * pWindow; + if (!PyTuple_GetWindow(poArgs, 0, &pWindow)) + return Py_BuildException(); + + int iDir; + if (!PyTuple_GetInteger(poArgs, 1, &iDir)) + return Py_BuildException(); + + ((UI::CTextLine*)pWindow)->SetBaseDirection(iDir); + return Py_BuildNone(); +} PyObject * wndTextSetText(PyObject * poSelf, PyObject * poArgs) { UI::CWindow * pWindow; @@ -2549,6 +2562,7 @@ void initwndMgr() { "SetFontName", wndTextSetFontName, METH_VARARGS }, { "SetFontColor", wndTextSetFontColor, METH_VARARGS }, { "SetLimitWidth", wndTextSetLimitWidth, METH_VARARGS }, + { "SetBaseDirection", wndTextSetBaseDirection, METH_VARARGS }, { "GetText", wndTextGetText, METH_VARARGS }, { "GetTextSize", wndTextGetTextSize, METH_VARARGS }, { "ShowCursor", wndTextShowCursor, METH_VARARGS }, @@ -2631,6 +2645,10 @@ void initwndMgr() PyModule_AddIntConstant(poModule, "TEXT_VERTICAL_ALIGN_BOTTOM", CGraphicTextInstance::VERTICAL_ALIGN_BOTTOM); PyModule_AddIntConstant(poModule, "TEXT_VERTICAL_ALIGN_CENTER", CGraphicTextInstance::VERTICAL_ALIGN_CENTER); + PyModule_AddIntConstant(poModule, "TEXT_BASEDIR_AUTO", 0); + PyModule_AddIntConstant(poModule, "TEXT_BASEDIR_LTR", 1); + PyModule_AddIntConstant(poModule, "TEXT_BASEDIR_RTL", 2); + PyModule_AddIntConstant(poModule, "HORIZONTAL_ALIGN_LEFT", UI::CWindow::HORIZONTAL_ALIGN_LEFT); PyModule_AddIntConstant(poModule, "HORIZONTAL_ALIGN_CENTER", UI::CWindow::HORIZONTAL_ALIGN_CENTER); PyModule_AddIntConstant(poModule, "HORIZONTAL_ALIGN_RIGHT", UI::CWindow::HORIZONTAL_ALIGN_RIGHT); diff --git a/src/GameLib/ItemData.cpp b/src/GameLib/ItemData.cpp index d74c5fd..3dc77c3 100644 --- a/src/GameLib/ItemData.cpp +++ b/src/GameLib/ItemData.cpp @@ -3,9 +3,7 @@ #include "ItemData.h" -CDynamicPool CItemData::ms_kPool; - -extern DWORD GetDefaultCodePage(); +CDynamicPool CItemData::ms_kPool; CItemData* CItemData::New() { @@ -98,59 +96,7 @@ void CItemData::SetDescription(const std::string& c_rstDesc) { m_strDescription=c_rstDesc; } -/* -BOOL CItemData::LoadItemData(const char * c_szFileName) -{ - CTextFileLoader TextFileLoader; - if (!TextFileLoader.Load(c_szFileName)) - { - //Lognf(1, "CItemData::LoadItemData(c_szFileName=%s) - FAILED", c_szFileName); - return FALSE; - } - - TextFileLoader.SetTop(); - - TextFileLoader.GetTokenString("modelfilename", &m_strModelFileName); - TextFileLoader.GetTokenString("submodelfilename", &m_strSubModelFileName); - TextFileLoader.GetTokenString("dropmodelfilename", &m_strDropModelFileName); - TextFileLoader.GetTokenString("iconimagefilename", &m_strIconFileName); - - char szDescriptionKey[32+1]; - _snprintf(szDescriptionKey, 32, "%ddescription", GetDefaultCodePage()); - if (!TextFileLoader.GetTokenString(szDescriptionKey, &m_strDescription)) - { - TextFileLoader.GetTokenString("description", &m_strDescription); - } - - // LOD Model File Name List - CTokenVector * pLODModelList; - if (TextFileLoader.GetTokenVector("lodmodellist", &pLODModelList)) - { - m_strLODModelFileNameVector.clear(); - m_strLODModelFileNameVector.resize(pLODModelList->size()); - - for (DWORD i = 0; i < pLODModelList->size(); ++i) - { - m_strLODModelFileNameVector[i] = pLODModelList->at(0); - } - } - - // Attaching Data - // Item 에 Attaching Data 일단 없음. -// if (TextFileLoader.SetChildNode("attachingdata")) -// { -// if (!NRaceData::LoadAttachingData(TextFileLoader, &m_AttachingDataVector)) -// return FALSE; -// -// TextFileLoader.SetParentNode(); -// } - - __LoadFiles(); - - return TRUE; -} -*/ void CItemData::SetDefaultItemData(const char * c_szIconFileName, const char * c_szModelFileName) { if(c_szModelFileName) diff --git a/src/GameLib/ItemData.h b/src/GameLib/ItemData.h index 6a4acf9..bc8f2b4 100644 --- a/src/GameLib/ItemData.h +++ b/src/GameLib/ItemData.h @@ -12,7 +12,7 @@ class CItemData public: enum { - ITEM_NAME_MAX_LEN = 24, + ITEM_NAME_MAX_LEN = 64, ITEM_LIMIT_MAX_NUM = 2, ITEM_VALUES_MAX_NUM = 6, ITEM_SMALL_DESCR_MAX_LEN = 256, diff --git a/src/GameLib/ItemManager.cpp b/src/GameLib/ItemManager.cpp index 43f3d35..9c4df74 100644 --- a/src/GameLib/ItemManager.cpp +++ b/src/GameLib/ItemManager.cpp @@ -366,29 +366,6 @@ bool CItemManager::LoadItemTable(const char* c_szFileName) } } -//!@# -// CItemData::TItemTable * table = (CItemData::TItemTable *) zObj.GetBuffer(); -// for (DWORD i = 0; i < dwElements; ++i, ++table) -// { -// CItemData * pItemData; -// DWORD dwVnum = table->dwVnum; -// -// TItemMap::iterator f = m_ItemMap.find(dwVnum); -// -// if (m_ItemMap.end() == f) -// { -// pItemData = CItemData::New(); -// -// pItemData->LoadItemData(_getf("d:/ymir work/item/%05d.msm", dwVnum)); -// m_ItemMap.insert(TItemMap::value_type(dwVnum, pItemData)); -// } -// else -// { -// pItemData = f->second; -// } -// pItemData->SetItemTableData(table); -// } - delete [] pbData; return true; } diff --git a/src/GameLib/Property.cpp b/src/GameLib/Property.cpp index 83229fc..fa6f693 100644 --- a/src/GameLib/Property.cpp +++ b/src/GameLib/Property.cpp @@ -124,94 +124,72 @@ void CProperty::PutVector(const char * c_pszKey, const CTokenVector & c_rTokenVe void GetTimeString(char * str, time_t ct) { - struct tm tm; - tm = *localtime(&ct); + struct tm tm; + tm = *localtime(&ct); - _snprintf(str, 15, "%04d%02d%02d%02d%02d%02d", - tm.tm_year + 1900, - tm.tm_mon + 1, - tm.tm_mday, - tm.tm_hour, - tm.tm_min, - tm.tm_sec); + _snprintf(str, 15, "%04d%02d%02d%02d%02d%02d", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); } bool CProperty::ReadFromMemory(const void * c_pvData, int iLen, const char * c_pszFileName) { - const char * pcData = (const char *) c_pvData; + const char* pStart = (const char*)c_pvData; + const char* pcData = pStart; if (*(DWORD *) pcData != MAKEFOURCC('Y', 'P', 'R', 'T')) return false; pcData += sizeof(DWORD); - if (*pcData != '\r' || *(pcData + 1) != '\n') + + while (pcData < pStart + iLen && (*pcData == '\r' || *pcData == '\n' || *pcData == ' ' || *pcData == '\t')) + ++pcData; + + + int textLen = iLen - int(pcData - pStart); + if (textLen <= 0) { - TraceError("CProperty::ReadFromMemory: File format error after FourCC: %s\n", c_pszFileName); + TraceError("CProperty::ReadFromMemory: textLen <= 0 in %s\n", c_pszFileName); return false; } - pcData += 2; - CTokenVector stTokenVector; -/* - char szTimeStamp[64]; - memcpy(szTimeStamp, pcData, 14); - szTimeStamp[14] = '\0'; - pcData += 14; - - if (*pcData != '\r' || *(pcData + 1) != '\n') - { - TraceError("CProperty::ReadFromMemory: File format error after TimeStamp: %s\n", c_pszFileName); - return false; - } - - std::string m_stTimeStamp; - - m_stTimeStamp = szTimeStamp; - - int iTimeStampLen = 14 + _snprintf(szTimeStamp + 14, 64 - 14, "%s", mc_pFileName); - m_dwCRC = GetCRC32(szTimeStamp, iTimeStampLen); - - char tmp[64]; - sprintf(tmp, "%u", m_dwCRC); - m_stCRC.assign(tmp); CMemoryTextFileLoader textFileLoader; - textFileLoader.Bind(iLen - (sizeof(DWORD) + 2 + 14 + 2), pcData); + textFileLoader.Bind(textLen, pcData); - for (DWORD i = 0; i < textFileLoader.GetLineCount(); ++i) + m_stCRC.clear(); + m_dwCRC = 0; + + if (textFileLoader.GetLineCount() > 0) { - if (!textFileLoader.SplitLine(i, &stTokenVector)) - continue; + m_stCRC = textFileLoader.GetLineString(0); - stl_lowers(stTokenVector[0]); - std::string stKey = stTokenVector[0]; + bool bAllDigits = !m_stCRC.empty() && std::all_of(m_stCRC.begin(), m_stCRC.end(), [](char c) + { + return isdigit((unsigned char)c); + }); - stTokenVector.erase(stTokenVector.begin()); - PutVector(stKey.c_str(), stTokenVector); - } - return true; - */ - CMemoryTextFileLoader textFileLoader; - textFileLoader.Bind(iLen - (sizeof(DWORD) + 2), pcData); + DWORD startLine = 0; - m_stCRC = textFileLoader.GetLineString(0); - m_dwCRC = atoi(m_stCRC.c_str()); + if (bAllDigits) + { + m_dwCRC = atoi(m_stCRC.c_str()); + startLine = 1; + } - for (DWORD i = 1; i < textFileLoader.GetLineCount(); ++i) - { - if (!textFileLoader.SplitLine(i, &stTokenVector)) - continue; + for (DWORD i = startLine; i < textFileLoader.GetLineCount(); ++i) + { + if (!textFileLoader.SplitLine(i, &stTokenVector)) + continue; - stl_lowers(stTokenVector[0]); - std::string stKey = stTokenVector[0]; + stl_lowers(stTokenVector[0]); + std::string stKey = stTokenVector[0]; - stTokenVector.erase(stTokenVector.begin()); - PutVector(stKey.c_str(), stTokenVector); + stTokenVector.erase(stTokenVector.begin()); + PutVector(stKey.c_str(), stTokenVector); + } } - //Tracef("Property: %s\n", c_pszFileName); return true; } diff --git a/src/ScriptLib/PythonLauncher.cpp b/src/ScriptLib/PythonLauncher.cpp index e88e237..f7f2831 100644 --- a/src/ScriptLib/PythonLauncher.cpp +++ b/src/ScriptLib/PythonLauncher.cpp @@ -7,6 +7,7 @@ #include "PackLib/PackManager.h" #include "PythonLauncher.h" +#include CPythonLauncher::CPythonLauncher() { @@ -150,7 +151,9 @@ bool CPythonLauncher::Create(const char* c_szProgramName) bool CPythonLauncher::RunCompiledFile(const char* c_szFileName) { NANOBEGIN - FILE * fp = fopen(c_szFileName, "rb"); + // UTF-8 → UTF-16 conversion for Unicode path support + std::wstring wFileName = Utf8ToWide(c_szFileName); + FILE * fp = _wfopen(wFileName.c_str(), L"rb"); if (!fp) return false; diff --git a/src/SpeedTreeLib/VertexShaders.h b/src/SpeedTreeLib/VertexShaders.h index 515cb38..0f47ef5 100644 --- a/src/SpeedTreeLib/VertexShaders.h +++ b/src/SpeedTreeLib/VertexShaders.h @@ -143,9 +143,9 @@ static LPDIRECT3DVERTEXDECLARATION9 LoadBranchShader(LPDIRECT3DDEVICE9 pDx) if (pDx->CreateVertexDeclaration(pBranchShaderDecl, &dwShader) != D3D_OK) { - char szError[1024]; - sprintf_s(szError, "Failed to create branch vertex shader."); - MessageBox(NULL, szError, "Vertex Shader Error", MB_ICONSTOP); + wchar_t szError[1024]; + swprintf_s(szError, L"Failed to create branch vertex shader."); + MessageBoxW(NULL, szError, L"Vertex Shader Error", MB_ICONSTOP); } return dwShader; diff --git a/src/UserInterface/AbstractApplication.h b/src/UserInterface/AbstractApplication.h index 004d59c..5b01fdb 100644 --- a/src/UserInterface/AbstractApplication.h +++ b/src/UserInterface/AbstractApplication.h @@ -48,7 +48,6 @@ class IAbstractApplication : public TAbstractSingleton virtual void RunIMETabEvent() = 0; virtual void RunIMEReturnEvent() = 0; - virtual void RunIMEChangeCodePage() = 0; virtual void RunIMEOpenCandidateListEvent() = 0; virtual void RunIMECloseCandidateListEvent() = 0; virtual void RunIMEOpenReadingWndEvent() = 0; diff --git a/src/UserInterface/AccountConnector.cpp b/src/UserInterface/AccountConnector.cpp index 5d4a0b7..f8e89eb 100644 --- a/src/UserInterface/AccountConnector.cpp +++ b/src/UserInterface/AccountConnector.cpp @@ -35,18 +35,8 @@ bool CAccountConnector::Connect(const char * c_szAddr, int iPort, const char * c m_strAddr = c_szAddr; m_iPort = iPort; - __OfflineState_Set(); - - // CHINA_CRYPT_KEY - if (LocaleService_IsYMIR()) - { - } - else - { - __BuildClientKey_20050304Myevan(); - } - // END_OF_CHINA_CRYPT_KEY + __BuildClientKey_20050304Myevan(); return CNetworkStream::Connect(c_szAccountAddr, iAccountPort); } @@ -173,7 +163,7 @@ bool CAccountConnector::__AuthState_RecvPhase() else if (kPacketPhase.phase == PHASE_AUTH) { #ifndef _IMPROVED_PACKET_ENCRYPTION_ - const char* key = LocaleService_GetSecurityKey(); + const char* key = GetSecurityKey(); SetSecurityMode(true, key); #endif diff --git a/src/UserInterface/GuildMarkDownloader.cpp b/src/UserInterface/GuildMarkDownloader.cpp index 9876359..268226c 100644 --- a/src/UserInterface/GuildMarkDownloader.cpp +++ b/src/UserInterface/GuildMarkDownloader.cpp @@ -2,7 +2,6 @@ #include "GuildMarkDownloader.h" #include "PythonCharacterManager.h" #include "Packet.h" -#include "Test.h" // MARK_BUG_FIX struct SMarkIndex @@ -258,7 +257,7 @@ bool CGuildMarkDownloader::__LoginState_RecvPhase() if (kPacketPhase.phase == PHASE_LOGIN) { #ifndef _IMPROVED_PACKET_ENCRYPTION_ - const char* key = LocaleService_GetSecurityKey(); + const char* key = GetSecurityKey(); SetSecurityMode(true, key); #endif @@ -287,7 +286,7 @@ bool CGuildMarkDownloader::__LoginState_RecvPhase() } return true; -} +} // MARK_BUG_FIX bool CGuildMarkDownloader::__SendMarkIDXList() diff --git a/src/UserInterface/GuildMarkUploader.cpp b/src/UserInterface/GuildMarkUploader.cpp index 3a94c45..46e6ffb 100644 --- a/src/UserInterface/GuildMarkUploader.cpp +++ b/src/UserInterface/GuildMarkUploader.cpp @@ -1,7 +1,6 @@ #include "StdAfx.h" #include "GuildMarkUploader.h" #include "Packet.h" -#include "Test.h" #include "stb_image.h" #include "stb_image_write.h" @@ -332,7 +331,7 @@ bool CGuildMarkUploader::__LoginState_RecvPhase() if (kPacketPhase.phase==PHASE_LOGIN) { #ifndef _IMPROVED_PACKET_ENCRYPTION_ - const char* key = LocaleService_GetSecurityKey(); + const char* key = GetSecurityKey(); SetSecurityMode(true, key); #endif diff --git a/src/UserInterface/InsultChecker.cpp b/src/UserInterface/InsultChecker.cpp index 06bcb51..a2c1455 100644 --- a/src/UserInterface/InsultChecker.cpp +++ b/src/UserInterface/InsultChecker.cpp @@ -28,15 +28,14 @@ void CInsultChecker::AppendInsult(const std::string& c_rstInsult) bool CInsultChecker::__GetInsultLength(const char* c_szWord, UINT* puInsultLen) { - std::list::iterator i; - for (i=m_kList_stInsult.begin(); i!=m_kList_stInsult.end(); ++i) + for (auto i = m_kList_stInsult.begin(); i != m_kList_stInsult.end(); ++i) { - std::string& rstInsult=*i; + auto rstInsult = *i; + int ret = StringCompareCI(c_szWord, rstInsult.c_str(), rstInsult.length()); - int ret=LocaleService_StringCompareCI(c_szWord, rstInsult.c_str(), rstInsult.length()); - if (0==ret) + if (0 == ret) { - *puInsultLen=rstInsult.length(); + *puInsultLen = rstInsult.length(); return true; } } @@ -64,10 +63,7 @@ void CInsultChecker::FilterInsult(char* szLine, UINT uLineLen) } else { - if ( LocaleService_IsLeadByte( bChr ) ) - uPos += 2; - else - uPos++; + uPos++; } } } diff --git a/src/UserInterface/Locale.cpp b/src/UserInterface/Locale.cpp index 5e9ef9e..503d125 100644 --- a/src/UserInterface/Locale.cpp +++ b/src/UserInterface/Locale.cpp @@ -3,418 +3,129 @@ #include "PythonApplication.h" #include "resource.h" #include "EterBase/CRC32.h" -#include "EterLocale/Japanese.h" + #include -const char* LSS_YMIR = "YMIR"; -const char* LSS_JAPAN = "JAPAN"; -const char* LSS_ENGLISH = "ENGLISH"; -const char* LSS_HONGKONG = "HONGKONG"; -const char* LSS_TAIWAN = "TAIWAN"; -const char* LSS_NEWCIBN = "NEWCIBN"; -const char* LSS_EUROPE = "EUROPE"; -const char* LSS_GLOBAL = "GLOBAL"; - -static bool IS_CHEONMA = false; - #ifndef LSS_SECURITY_KEY -#define LSS_SECURITY_KEY "testtesttesttest" +#define LSS_SECURITY_KEY "testtesttesttest" #endif std::string __SECURITY_KEY_STRING__ = LSS_SECURITY_KEY; -char MULTI_LOCALE_SERVICE[256] = "YMIR"; -char MULTI_LOCALE_PATH[256] = "locale/ymir"; -char MULTI_LOCALE_NAME[256] = "ymir"; -int MULTI_LOCALE_CODE = 949; -int MULTI_LOCALE_REPORT_PORT = 10000; +char MULTI_LOCALE_PATH_COMMON[256] = "locale/common"; +char MULTI_LOCALE_PATH[256] = "locale/en"; +char MULTI_LOCALE_NAME[256] = "en"; -void LocaleService_LoadConfig(const char* fileName) +void LoadConfig(const char* fileName) { - NANOBEGIN - FILE* fp = fopen(fileName, "rt"); + strncpy_s(MULTI_LOCALE_NAME, "en", _TRUNCATE); + _snprintf_s(MULTI_LOCALE_PATH, _countof(MULTI_LOCALE_PATH), _TRUNCATE, "locale/%s", MULTI_LOCALE_NAME); - if (fp) - { - char line[256]; - char name[256]; - int code; - int id; - if (fgets(line, sizeof(line)-1, fp)) + auto fp = fopen(fileName, "rt"); + if (!fp) + return; + + char line[256] = {}; + if (fgets(line, sizeof(line) - 1, fp)) + { + char name[256] = {}; + if (sscanf(line, "%255s", name) == 1) { - line[sizeof(line)-1] = '\0'; - sscanf(line, "%d %d %s", &id, &code, name); - - MULTI_LOCALE_REPORT_PORT = id; - MULTI_LOCALE_CODE = code; - strcpy(MULTI_LOCALE_NAME, name); - sprintf(MULTI_LOCALE_PATH, "locale/%s", MULTI_LOCALE_NAME); - } - fclose(fp); + strncpy_s(MULTI_LOCALE_NAME, name, _TRUNCATE); + _snprintf_s(MULTI_LOCALE_PATH, _countof(MULTI_LOCALE_PATH), _TRUNCATE, "locale/%s", MULTI_LOCALE_NAME); + } } - NANOEND + fclose(fp); } -unsigned LocaleService_GetLastExp(int level) +unsigned GetGuildLastExp(int level) { static const int GUILD_LEVEL_MAX = 20; - - if (LocaleService_IsCHEONMA()) + static DWORD GUILD_EXP_LIST[GUILD_LEVEL_MAX + 1] = { - static DWORD CHEONMA_GUILDEXP_LIST[GUILD_LEVEL_MAX+1] = - { - 0, // 0 - 15000ul, // 1 - 45000ul, // 2 - 90000ul, // 3 - 160000ul, // 4 - 235000ul, // 5 - 325000ul, // 6 - 430000ul, // 7 - 550000ul, // 8 - 685000ul, // 9 - 835000ul, // 10 - 1000000ul, // 11 - 1500000ul, // 12 - 2100000ul, // 13 - 2800000ul, // 14 - 3600000ul, // 15 - 4500000ul, // 16 - 6500000ul, // 17 - 8000000ul, // 18 - 10000000ul, // 19 - 42000000UL // 20 - }; - if (level < 0 && level >= GUILD_LEVEL_MAX) - return 0; - - return CHEONMA_GUILDEXP_LIST[level]; - } - - static DWORD INTERNATIONAL_GUILDEXP_LIST[GUILD_LEVEL_MAX+1] = - { - 0, // 0 - 6000UL, // 1 - 18000UL, // 2 - 36000UL, // 3 - 64000UL, // 4 - 94000UL, // 5 - 130000UL, // 6 - 172000UL, // 7 - 220000UL, // 8 - 274000UL, // 9 - 334000UL, // 10 - 400000UL, // 11 - 600000UL, // 12 - 840000UL, // 13 - 1120000UL, // 14 - 1440000UL, // 15 - 1800000UL, // 16 - 2600000UL, // 17 - 3200000UL, // 18 - 4000000UL, // 19 - 16800000UL // 20 + 0, + 6000UL, + 18000UL, + 36000UL, + 64000UL, + 94000UL, + 130000UL, + 172000UL, + 220000UL, + 274000UL, + 334000UL, + 400000UL, + 600000UL, + 840000UL, + 1120000UL, + 1440000UL, + 1800000UL, + 2600000UL, + 3200000UL, + 4000000UL, + 16800000UL, }; - if (level < 0 && level >= GUILD_LEVEL_MAX) + // Fix: Correct the condition to ensure 'level' is within valid bounds + if (level < 0 || level > GUILD_LEVEL_MAX) + { return 0; - - return INTERNATIONAL_GUILDEXP_LIST[level]; + } + + return GUILD_EXP_LIST[level]; } -int LocaleService_GetSkillPower(unsigned level) +int GetSkillPower(unsigned level) { static const unsigned SKILL_POWER_NUM = 50; - if (level >= SKILL_POWER_NUM) + { return 0; - - if (LocaleService_IsCHEONMA()) - { - static unsigned CHEONMA_SKILL_POWERS[SKILL_POWER_NUM]= - { - 0, - 5, 7, 9, 11, 13, - 15, 17, 19, 20, 22, - 24, 26, 28, 30, 32, - 34, 36, 38, 40, 50, // master - 52, 55, 58, 61, 63, - 66, 69, 72, 75, 80, // grand_master - 82, 84, 87, 90, 95, - 100,110,120,130,150,// perfect_master - 150, - }; - return CHEONMA_SKILL_POWERS[level]; } - - // 0 5 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40 50 52 54 56 58 60 63 66 69 72 82 85 88 91 94 98 102 106 110 115 125 125 125 125 125 - static unsigned INTERNATIONAL_SKILL_POWERS[SKILL_POWER_NUM]= + + static unsigned SKILL_POWERS[SKILL_POWER_NUM] = { - 0, - 5, 6, 8, 10, 12, - 14, 16, 18, 20, 22, - 24, 26, 28, 30, 32, - 34, 36, 38, 40, 50, // master - 52, 54, 56, 58, 60, - 63, 66, 69, 72, 82, // grand_master - 85, 88, 91, 94, 98, - 102,106,110,115,125,// perfect_master - 125, + 0, + 5, 6, 8, 10, 12, + 14, 16, 18, 20, 22, + 24, 26, 28, 30, 32, + 34, 36, 38, 40, 50, + 52, 54, 56, 58, 60, + 63, 66, 69, 72, 82, + 85, 88, 91, 94, 98, + 102, 106, 110, 115, 125, + 125, }; - return INTERNATIONAL_SKILL_POWERS[level]; + + return SKILL_POWERS[level]; } -const char* LocaleService_GetSecurityKey() +const char* GetSecurityKey() { return __SECURITY_KEY_STRING__.c_str(); } -// CHEONMA -void LocaleService_SetCHEONMA(bool isEnable) -{ - IS_CHEONMA = isEnable; -} - -bool LocaleService_IsCHEONMA() -{ - return LocaleService_IsYMIR(); -} - -// END_OF_CHEONMA - -#if defined(LOCALE_SERVICE_EUROPE) || defined(LOCALE_SERVICE_BRAZIL) || defined(LOCALE_SERVICE_CANADA) || defined(LOCALE_SERVICE_SINGAPORE) || defined(LOCALE_SERVICE_VIETNAM) || defined(LOCALE_SERVICE_TAIWAN) || defined(LOCALE_SERVICE_NEWCIBN) -#define _LSS_USE_LOCALE_CFG 1 -#define _LSS_SERVICE_NAME LSS_EUROPE -#elif defined(LOCALE_SERVICE_ITALY) -#define _LSS_SERVICE_NAME LSS_ITALY -#define _LSS_SERVICE_CODEPAGE CP_LATIN -#define _LSS_SERVICE_LOCALE_NAME "it" -#define _LSS_SERVICE_LOCALE_PATH "locale/it" -#elif defined(LOCALE_SERVICE_ENGLISH) -#define _LSS_SERVICE_NAME LSS_ENGLISH -#define _LSS_SERVICE_CODEPAGE CP_LATIN -#define _LSS_SERVICE_LOCALE_NAME "english" -#define _LSS_SERVICE_LOCALE_PATH "locale/english" -#elif defined(LOCALE_SERVICE_JAPAN) -#define _LSS_SERVICE_NAME LSS_JAPAN -#define _LSS_SERVICE_CODEPAGE CP_JAPANESE -#define _LSS_SERVICE_LOCALE_NAME "japan" -#define _LSS_SERVICE_LOCALE_PATH "locale/japan" -#elif defined(LOCALE_SERVICE_YMIR) -#define _LSS_SERVICE_NAME LSS_YMIR -#define _LSS_SERVICE_CODEPAGE CP_HANGUL -#define _LSS_SERVICE_LOCALE_NAME "ymir" -#define _LSS_SERVICE_LOCALE_PATH "locale/ymir" -#elif defined(LOCALE_SERVICE_HONGKONG) -#define _LSS_SERVICE_NAME LSS_HONGKONG -#define _LSS_SERVICE_CODEPAGE CP_CHINESE_TRAD -#define _LSS_SERVICE_LOCALE_NAME "hongkong" -#define _LSS_SERVICE_LOCALE_PATH "locale/hongkong" -#elif defined(LOCALE_SERVICE_TAIWAN) -#define _LSS_SERVICE_NAME LSS_TAIWAN -#define _LSS_SERVICE_CODEPAGE CP_CHINESE_TRAD -#define _LSS_SERVICE_LOCALE_NAME "taiwan" -#define _LSS_SERVICE_LOCALE_PATH "locale/taiwan" -#elif defined(LOCALE_SERVICE_NEWCIBN) -#define _LSS_SERVICE_NAME LSS_NEWCIBN -#define _LSS_SERVICE_CODEPAGE CP_CHINESE_SIMPLE -#define _LSS_SERVICE_LOCALE_NAME "newcibn" -#define _LSS_SERVICE_LOCALE_PATH "locale/newcibn" -#endif - -#if defined(_LSS_USE_LOCALE_CFG) -#if defined(_LSS_SERVICE_NAME) -const char* LocaleService_GetName() { return _LSS_SERVICE_NAME;} -#else -const char* LocaleService_GetName() { return MULTI_LOCALE_SERVICE; } -#endif -unsigned int LocaleService_GetCodePage() { return MULTI_LOCALE_CODE; } -const char* LocaleService_GetLocaleName() { return MULTI_LOCALE_NAME; } -const char* LocaleService_GetLocalePath() { return MULTI_LOCALE_PATH; } -#elif defined(_LSS_SERVICE_NAME) -const char* LocaleService_GetName() { return _LSS_SERVICE_NAME;} -unsigned int LocaleService_GetCodePage() { return _LSS_SERVICE_CODEPAGE; } -const char* LocaleService_GetLocaleName() { return _LSS_SERVICE_LOCALE_NAME; } -const char* LocaleService_GetLocalePath() { return _LSS_SERVICE_LOCALE_PATH; } -#endif - -void LocaleService_ForceSetLocale(const char* name, const char* localePath) -{ - strcpy(MULTI_LOCALE_NAME, name); - strcpy(MULTI_LOCALE_PATH, localePath); - - // 기존 천마 서버로 접속시에는 security key 변경 (WE 버전 클라로 천마서버 접속하기 위함) - if (0 == stricmp(name, "ymir")) - __SECURITY_KEY_STRING__ = "testtesttesttest"; - if (0 == stricmp(name, "we_korea")) - __SECURITY_KEY_STRING__ = "1234abcd5678efgh"; -} - -#if defined(LOCALE_SERVICE_GLOBAL) -struct SLOCALEDATA -{ - const char* szServiceName; - const char* szLocaleName; - WORD wCodePage; - const char* szSecurityKey; -} gs_stLocaleData[] = { - { LSS_YMIR, "ymir", 949, "testtesttesttest" }, // Korea - { LSS_EUROPE, "en", 1252, "1234abcd5678efgh" }, // GameForge (United Kingdom) - { LSS_EUROPE, "de", 1252, "1234abcd5678efgh" }, // GameForge (Germany) - { LSS_EUROPE, "us", 1252, "1234abcd5678efgh" }, // GameForge (USA) - { LSS_EUROPE, "es", 1252, "1234abcd5678efgh" }, // GameForge (Spain) - { LSS_EUROPE, "it", 1252, "1234abcd5678efgh" }, // GameForge (Italy) - { LSS_EUROPE, "fr", 1252, "1234abcd5678efgh" }, // GameForge (France) - { LSS_EUROPE, "pt", 1252, "1234abcd5678efgh" }, // GameForge (Portugal) - { LSS_EUROPE, "gr", 1253, "1234abcd5678efgh" }, // GameForge (Greece) - { LSS_EUROPE, "pl", 1250, "1234abcd5678efgh" }, // GameForge (Poland) - { LSS_EUROPE, "tr", 1254, "1234abcd5678efgh" }, // GameForge (Turkey) - { LSS_EUROPE, "dk", 1252, "1234abcd5678efgh" }, // GameForge (Demmark) - { LSS_EUROPE, "ae", 1256, "1234abcd5678efgh" }, // GameForge (United Arab Emirate) - { LSS_EUROPE, "mx", 1252, "1234abcd5678efgh" }, // GameForge (Mexico) - { LSS_EUROPE, "nl", 1252, "1234abcd5678efgh" }, // GameForge (Netherlands) - { LSS_EUROPE, "cz", 1252, "1234abcd5678efgh" }, // GameForge (Czech Republic) - { LSS_EUROPE, "ru", 1251, "1234abcd5678efgh" }, // GameForge (Russian Federation) - { LSS_EUROPE, "hu", 1250, "1234abcd5678efgh" }, // GameForge (Hungary) - { LSS_EUROPE, "ro", 1250, "1234abcd5678efgh" }, // GameForge (Romania) - { LSS_EUROPE, "ca", 1252, "testtesttesttest" }, // Z8Games (Canada) - { LSS_EUROPE, "sg", 1252, "testtesttesttest" }, // TEC (Singapore) - { LSS_JAPAN, "japan", 932, "testtesttesttest" }, // Japan - { LSS_EUROPE, "br", 1252, "testtesttesttest" }, // OnGame (Brazil) - { LSS_HONGKONG, "hongkong", 950, "testtesttesttest" }, // HongKong & Taiwan - { LSS_NEWCIBN, "newcibn", 936, "testtesttesttest" }, // CIBN (Free world) - { LSS_ENGLISH, "english", 949, "testtesttesttest" }, // English (Obsoleted) - { LSS_YMIR, "kr", 949, "testtesttesttest" }, // Korea (Obsoleted) - { NULL, NULL, 0, "testtesttesttest" } -}; - -const char* LocaleService_GetName() -{ - return MULTI_LOCALE_SERVICE; -} - -unsigned int LocaleService_GetCodePage() -{ - return MULTI_LOCALE_CODE; -} - -const char* LocaleService_GetLocaleName() +const char* GetLocaleName() { return MULTI_LOCALE_NAME; } -const char* LocaleService_GetLocalePath() +const char* GetLocalePath() { return MULTI_LOCALE_PATH; } -static int gs_iLocale = -1; - -LRESULT CALLBACK SelectDlgProc( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam ) +const char* GetLocalePathCommon() { - switch( uMsg ) { - case WM_INITDIALOG : { - char szLocalePath[256], szDisplayName[256]; - for(int i=0; gs_stLocaleData[i].szServiceName; i++ ) { - sprintf(szLocalePath, "locale/%s/item_proto", gs_stLocaleData[i].szLocaleName); - if(CPackManager::Instance().IsExist(szLocalePath)) { - sprintf(szDisplayName, "%s (%s, %d)", gs_stLocaleData[i].szLocaleName, gs_stLocaleData[i].szServiceName, gs_stLocaleData[i].wCodePage); - int iIndex = ListBox_AddString(GetDlgItem(hDlg, IDC_LOCALE_LIST), szDisplayName); - ListBox_SetItemData(GetDlgItem(hDlg, IDC_LOCALE_LIST), iIndex, i); - } - } - return TRUE; - } - case WM_COMMAND : - switch( LOWORD( wParam ) ) { - case IDC_LOCALE_LIST: { - int iSelected = ListBox_GetCurSel(GetDlgItem(hDlg, IDC_LOCALE_LIST)); - switch(HIWORD(wParam)) { - case LBN_SELCHANGE : - gs_iLocale = ListBox_GetItemData(GetDlgItem(hDlg, IDC_LOCALE_LIST), iSelected); - break; - case LBN_DBLCLK : - gs_iLocale = ListBox_GetItemData(GetDlgItem(hDlg, IDC_LOCALE_LIST), iSelected); - ::EndDialog(hDlg, 0); - break; - } - break; - } - case IDC_START: { - ::EndDialog(hDlg, 0); - break; - } - case IDC_EXIT: { - gs_iLocale = -1; - ::EndDialog(hDlg, 0); - break; - } - } - return FALSE; - } - return FALSE; + return MULTI_LOCALE_PATH_COMMON; } -bool LocaleService_LoadGlobal(HINSTANCE hInstance) +bool IsRTL() { - int nFoundLocales = 0; - char szLocalePath[256]; - - for(int i=0; gs_stLocaleData[i].szServiceName; i++ ) { - sprintf(szLocalePath, "locale/%s/item_proto", gs_stLocaleData[i].szLocaleName); - if(CPackManager::Instance().IsExist(szLocalePath)) { - nFoundLocales++; - if(gs_iLocale == -1) - gs_iLocale = i; - } - } - if (gs_iLocale < 0) - return false; - if(nFoundLocales > 1) - ::DialogBox(hInstance, MAKEINTRESOURCE(IDD_SELECT_LOCALE), NULL, (DLGPROC) SelectDlgProc); - if (gs_iLocale < 0) - return false; - strcpy(MULTI_LOCALE_SERVICE, gs_stLocaleData[gs_iLocale].szServiceName); - strcpy(MULTI_LOCALE_NAME, gs_stLocaleData[gs_iLocale].szLocaleName); - sprintf(MULTI_LOCALE_PATH, "locale/%s", gs_stLocaleData[gs_iLocale].szLocaleName); - MULTI_LOCALE_CODE = gs_stLocaleData[gs_iLocale].wCodePage; - if(gs_stLocaleData[gs_iLocale].szSecurityKey) - __SECURITY_KEY_STRING__ = gs_stLocaleData[gs_iLocale].szSecurityKey; - return true; -} -#else -bool LocaleService_LoadGlobal(HINSTANCE hInstance) -{ - return false; -} -#endif - -bool LocaleService_IsYMIR() { return (stricmp( LocaleService_GetName(), LSS_YMIR ) == 0) || (stricmp( LocaleService_GetLocaleName(), "ymir" ) == 0); } -bool LocaleService_IsJAPAN() { return (stricmp( LocaleService_GetName(), LSS_JAPAN ) == 0) || (stricmp( LocaleService_GetLocaleName(), "japan" ) == 0); } -bool LocaleService_IsENGLISH() { return (stricmp( LocaleService_GetName(), LSS_ENGLISH ) == 0); } -bool LocaleService_IsEUROPE() { return (stricmp( LocaleService_GetName(), LSS_EUROPE ) == 0); } -bool LocaleService_IsHONGKONG() { return (stricmp( LocaleService_GetName(), LSS_HONGKONG ) == 0); } -bool LocaleService_IsTAIWAN() { return (stricmp( LocaleService_GetName(), LSS_TAIWAN ) == 0); } -bool LocaleService_IsNEWCIBN() { return (stricmp( LocaleService_GetName(), LSS_NEWCIBN ) == 0); } - -#if defined(LOCALE_SERVICE_WE_JAPAN) -BOOL LocaleService_IsLeadByte( const char chByte ) -{ - return ShiftJIS_IsLeadByte( chByte ); + return (0 == _stricmp(MULTI_LOCALE_NAME, "ae")); } -int LocaleService_StringCompareCI( LPCSTR szStringLeft, LPCSTR szStringRight, size_t sizeLength ) +int StringCompareCI(LPCSTR szStringLeft, LPCSTR szStringRight, size_t sizeLength) { - return ShiftJIS_StringCompareCI( szStringLeft, szStringRight, sizeLength ); + return strnicmp (szStringLeft, szStringRight, sizeLength); } -#else -BOOL LocaleService_IsLeadByte( const char chByte ) -{ - return (((unsigned char) chByte) & 0x80) != 0; -} - -int LocaleService_StringCompareCI( LPCSTR szStringLeft, LPCSTR szStringRight, size_t sizeLength ) -{ - return strnicmp( szStringLeft, szStringRight, sizeLength ); -} -#endif diff --git a/src/UserInterface/Locale.h b/src/UserInterface/Locale.h index a68bdf1..1c653f1 100644 --- a/src/UserInterface/Locale.h +++ b/src/UserInterface/Locale.h @@ -2,30 +2,13 @@ #include "Locale_inc.h" -bool LocaleService_IsYMIR(); -bool LocaleService_IsJAPAN(); -bool LocaleService_IsENGLISH(); -bool LocaleService_IsHONGKONG(); -bool LocaleService_IsTAIWAN(); -bool LocaleService_IsNEWCIBN(); -bool LocaleService_IsEUROPE(); -bool LocaleService_IsWorldEdition(); +const char* GetLocaleName(); +const char* GetLocalePath(); +const char* GetLocalePathCommon(); +const char* GetSecurityKey(); -unsigned LocaleService_GetCodePage(); -const char* LocaleService_GetName(); -const char* LocaleService_GetLocaleName(); -const char* LocaleService_GetLocalePath(); -const char* LocaleService_GetSecurityKey(); -BOOL LocaleService_IsLeadByte( const char chByte ); -int LocaleService_StringCompareCI( LPCSTR szStringLeft, LPCSTR szStringRight, size_t sizeLength ); - -void LocaleService_ForceSetLocale(const char* name, const char* localePath); -void LocaleService_LoadConfig(const char* fileName); -bool LocaleService_LoadGlobal(HINSTANCE hInstance); -unsigned LocaleService_GetLastExp(int level); -int LocaleService_GetSkillPower(unsigned level); - -// CHEONMA -void LocaleService_SetCHEONMA(bool isEnable); -bool LocaleService_IsCHEONMA(); -// END_OF_CHEONMA +bool IsRTL(); +int StringCompareCI( LPCSTR szStringLeft, LPCSTR szStringRight, size_t sizeLength ); +void LoadConfig(const char* fileName); +unsigned GetGuildLastExp(int level); +int GetSkillPower(unsigned level); diff --git a/src/UserInterface/Locale_inc.h b/src/UserInterface/Locale_inc.h index 66f7e71..a97210a 100644 --- a/src/UserInterface/Locale_inc.h +++ b/src/UserInterface/Locale_inc.h @@ -1,6 +1,5 @@ #define _IMPROVED_PACKET_ENCRYPTION_ -#define LOCALE_SERVICE_GLOBAL #define ENABLE_COSTUME_SYSTEM #define ENABLE_ENERGY_SYSTEM #define ENABLE_DRAGON_SOUL_SYSTEM diff --git a/src/UserInterface/MovieMan.cpp b/src/UserInterface/MovieMan.cpp index bea4eab..10d0f1a 100644 --- a/src/UserInterface/MovieMan.cpp +++ b/src/UserInterface/MovieMan.cpp @@ -372,15 +372,19 @@ HRESULT CMovieMan::RenderFileToMMStream(const char *cpFilename, IMultiMediaStrea } WCHAR wPath[MAX_PATH + 1]; - MultiByteToWideChar(CP_ACP, 0, cpFilename, -1, wPath, MAX_PATH + 1); + + int ok = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, cpFilename, -1, wPath, MAX_PATH + 1); + if (ok <= 0) + return E_FAIL; + // WCHAR wsDir[MAX_PATH + 1]; ::memset(wsDir, 0, sizeof(wsDir)); ::GetCurrentDirectoryW( MAX_PATH, wsDir ); - ::wcsncat( wsDir, L"\\", sizeof(WCHAR)*1 ); - ::wcsncat( wsDir, wPath, sizeof(WCHAR)*::wcsnlen(wPath, MAX_PATH) ); + wcscat_s(wsDir, MAX_PATH + 1, L"\\"); + wcscat_s(wsDir, MAX_PATH + 1, wPath); ::memset(wPath, 0, sizeof(wPath)); - ::wcsncpy( wPath, wsDir, sizeof(WCHAR)*::wcsnlen(wsDir, MAX_PATH) ); + wcsncpy_s(wPath, MAX_PATH + 1, wsDir, _TRUNCATE); // pAMStream->Initialize(STREAMTYPE_READ, AMMSF_NOGRAPHTHREAD, NULL); diff --git a/src/UserInterface/ProcessCRC.cpp b/src/UserInterface/ProcessCRC.cpp index f35ce14..aa4dfe7 100644 --- a/src/UserInterface/ProcessCRC.cpp +++ b/src/UserInterface/ProcessCRC.cpp @@ -1,57 +1,60 @@ #include "StdAfx.h" #include +#include static BYTE abCRCMagicCube[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; static BYTE abCRCXorTable[8] = { 102, 30, 188, 44, 39, 201, 43, 5 }; static BYTE bMagicCubeIdx = 0; -const char * stristr(const char * big, const char * little) +const wchar_t* wcsistr(const wchar_t* haystack, const wchar_t* needle) { - const char * t = big; - size_t len = strlen(little) - 1; + if (!haystack || !needle || !*needle) + return haystack; - for (t = big; *t; ++t) - if (!_strnicmp(t, little, len)) - return t; + size_t needleLen = wcslen(needle); - return NULL; + for (const wchar_t* p = haystack; *p; ++p) + { + if (_wcsnicmp(p, needle, needleLen) == 0) + return p; + } + return nullptr; } -bool GetProcessInformation(std::string & exeFileName, LPCVOID * ppvAddress) +bool GetProcessInformation(std::string& exeFileName, LPCVOID* ppvAddress) { - HANDLE hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetCurrentProcessId()); - if (hModuleSnap != INVALID_HANDLE_VALUE) - { - std::string filename; + HANDLE hModuleSnap = CreateToolhelp32Snapshot( + TH32CS_SNAPMODULE, + GetCurrentProcessId()); - GetExcutedFileName(filename); + if (hModuleSnap == INVALID_HANDLE_VALUE) + return false; - MODULEENTRY32 me32; - memset(&me32, 0, sizeof(me32)); - me32.dwSize = sizeof(MODULEENTRY32); + std::string exeUtf8; + GetExcutedFileName(exeUtf8); - BOOL bRet = Module32First(hModuleSnap, &me32); + std::wstring exeWide = Utf8ToWide(exeUtf8); - while (bRet) + MODULEENTRY32W me32{}; + me32.dwSize = sizeof(me32); + + BOOL bRet = Module32FirstW(hModuleSnap, &me32); + while (bRet) + { + if (wcsistr(me32.szExePath, exeWide.c_str())) { - if (stristr(me32.szExePath, filename.c_str())) - { - exeFileName = me32.szExePath; - *ppvAddress = me32.modBaseAddr; - CloseHandle(hModuleSnap); - return true; - } - - ZeroMemory(&me32, sizeof(MODULEENTRY32)); - me32.dwSize = sizeof(MODULEENTRY32); - - bRet = Module32Next(hModuleSnap, &me32); + exeFileName = WideToUtf8(me32.szExePath); + *ppvAddress = me32.modBaseAddr; + CloseHandle(hModuleSnap); + return true; } - CloseHandle(hModuleSnap); + me32.dwSize = sizeof(me32); + bRet = Module32NextW(hModuleSnap, &me32); } + CloseHandle(hModuleSnap); return false; } @@ -95,13 +98,6 @@ bool __GetExeCRC(DWORD & r_dwProcCRC, DWORD & r_dwFileCRC) void BuildProcessCRC() { - if (LocaleService_IsHONGKONG() || LocaleService_IsTAIWAN()) - { - memset(abCRCMagicCube, 0, sizeof(abCRCMagicCube)); - bMagicCubeIdx = 0; - return; - } - DWORD dwProcCRC, dwFileCRC; if (__GetExeCRC(dwProcCRC, dwFileCRC)) diff --git a/src/UserInterface/ProcessScanner.cpp b/src/UserInterface/ProcessScanner.cpp index 642e74b..76c60cf 100644 --- a/src/UserInterface/ProcessScanner.cpp +++ b/src/UserInterface/ProcessScanner.cpp @@ -2,6 +2,7 @@ #include "ProcessScanner.h" #include +#include static std::vector gs_kVct_crcPair; static CRITICAL_SECTION gs_csData; @@ -11,61 +12,64 @@ static HANDLE gs_hThread=NULL; void ScanProcessList(std::map& rkMap_crcProc, std::vector* pkVct_crcPair) { - SYSTEM_INFO si; - memset(&si, 0, sizeof(si)); - GetSystemInfo(&si); + SYSTEM_INFO si{}; + GetSystemInfo(&si); - PROCESSENTRY32 pro; - pro.dwSize = sizeof(PROCESSENTRY32); + PROCESSENTRY32W pro{}; + pro.dwSize = sizeof(PROCESSENTRY32W); - LPPROCESSENTRY32 Entry; - Entry = &pro; + HANDLE processSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (processSnap == INVALID_HANDLE_VALUE) + return; - HANDLE process = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0); + BOOL bOK = Process32FirstW(processSnap, &pro); - BOOL bOK = Process32First(process, Entry); - - while(bOK) + while (bOK) { - HANDLE hProc = OpenProcess(PROCESS_VM_READ, FALSE, Entry->th32ProcessID); - if (hProc) - { - HANDLE hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, Entry->th32ProcessID); - if (hModuleSnap != INVALID_HANDLE_VALUE) - { - MODULEENTRY32 me32; - memset(&me32, 0, sizeof(me32)); - me32.dwSize = sizeof(MODULEENTRY32); + HANDLE hProc = OpenProcess(PROCESS_VM_READ, FALSE, pro.th32ProcessID); + if (hProc) + { + HANDLE hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pro.th32ProcessID); + if (hModuleSnap != INVALID_HANDLE_VALUE) + { + MODULEENTRY32W me32{}; + me32.dwSize = sizeof(MODULEENTRY32W); - BOOL bRet = Module32First(hModuleSnap, &me32); - while (bRet) - { - DWORD crcExtPath=GetCRC32((const char*)me32.szExePath, strlen(me32.szExePath)); + BOOL bRet = Module32FirstW(hModuleSnap, &me32); + while (bRet) + { + // Wide exe path -> UTF-8 bytes (for CRC / engine use) + std::string exePathUtf8 = WideToUtf8(me32.szExePath); - std::map::iterator f=rkMap_crcProc.find(crcExtPath); - if (rkMap_crcProc.end()==f) - { - DWORD crcProc=GetFileCRC32(me32.szExePath); - rkMap_crcProc.insert(std::make_pair(crcExtPath, crcProc)); - pkVct_crcPair->push_back(std::make_pair(crcProc, (const char*)me32.szExePath)); - } - Sleep(1); - - ZeroMemory(&me32, sizeof(MODULEENTRY32)); - me32.dwSize = sizeof(MODULEENTRY32); - bRet = Module32Next(hModuleSnap, &me32); - } + DWORD crcExtPath = GetCRC32(exePathUtf8.c_str(), (int)exePathUtf8.size()); - CloseHandle(hModuleSnap); - } + auto f = rkMap_crcProc.find(crcExtPath); + if (f == rkMap_crcProc.end()) + { + // Make sure GetFileCRC32 is Unicode-safe: + DWORD crcProc = GetFileCRC32(me32.szExePath); // best + rkMap_crcProc.insert(std::make_pair(crcExtPath, crcProc)); - CloseHandle(hProc); - } + pkVct_crcPair->push_back(std::make_pair(crcProc, exePathUtf8)); + } - - bOK = Process32Next(process, Entry); + Sleep(1); + + me32.dwSize = sizeof(MODULEENTRY32W); + bRet = Module32NextW(hModuleSnap, &me32); + } + + CloseHandle(hModuleSnap); + } + + CloseHandle(hProc); + } + + bOK = Process32NextW(processSnap, &pro); + pro.dwSize = sizeof(PROCESSENTRY32W); } - CloseHandle(process); + + CloseHandle(processSnap); } void ProcessScanner_ReleaseQuitEvent() @@ -122,8 +126,8 @@ void ProcessScanner_Thread(void* pv) bool ProcessScanner_Create() { InitializeCriticalSection(&gs_csData); - gs_evReqExit=CreateEvent(NULL, FALSE, FALSE, "ProcessScanner_ReqExit"); - gs_evResExit=CreateEvent(NULL, FALSE, FALSE, "ProcessScanner_ResExit"); + gs_evReqExit=CreateEvent(NULL, FALSE, FALSE, L"ProcessScanner_ReqExit"); + gs_evResExit=CreateEvent(NULL, FALSE, FALSE, L"ProcessScanner_ResExit"); gs_hThread=(HANDLE)_beginthread(ProcessScanner_Thread, 64*1024, NULL); if (INVALID_HANDLE_VALUE==gs_hThread) diff --git a/src/UserInterface/PythonApplication.cpp b/src/UserInterface/PythonApplication.cpp index dd694f4..625768f 100644 --- a/src/UserInterface/PythonApplication.cpp +++ b/src/UserInterface/PythonApplication.cpp @@ -11,6 +11,8 @@ #include "ProcessScanner.h" +#include + extern void GrannyCreateSharedDeformBuffer(); extern void GrannyDestroySharedDeformBuffer(); @@ -742,82 +744,65 @@ int CPythonApplication::CheckDeviceState() return DEVICE_STATE_OK; } -bool CPythonApplication::CreateDevice(int width, int height, int Windowed, int bit /* = 32*/, int frequency /* = 0*/) +bool CPythonApplication::CreateDevice(int width, int height, int Windowed, int bit, int frequency) { int iRet; m_grpDevice.InitBackBufferCount(2); - m_grpDevice.RegisterWarningString(CGraphicDevice::CREATE_BAD_DRIVER, ApplicationStringTable_GetStringz(IDS_WARN_BAD_DRIVER, "WARN_BAD_DRIVER")); - m_grpDevice.RegisterWarningString(CGraphicDevice::CREATE_NO_TNL, ApplicationStringTable_GetStringz(IDS_WARN_NO_TNL, "WARN_NO_TNL")); - - iRet = m_grpDevice.Create(GetWindowHandle(), width, height, Windowed ? true : false, bit,frequency); + iRet = m_grpDevice.Create(GetWindowHandle(), width, height, Windowed ? true : false, bit, frequency); switch (iRet) { - case CGraphicDevice::CREATE_OK: - return true; - - case CGraphicDevice::CREATE_REFRESHRATE: - return true; - - case CGraphicDevice::CREATE_ENUM: - case CGraphicDevice::CREATE_DETECT: - SET_EXCEPTION(CREATE_NO_APPROPRIATE_DEVICE); - TraceError("CreateDevice: Enum & Detect failed"); - return false; - - case CGraphicDevice::CREATE_NO_DIRECTX: - //PyErr_SetString(PyExc_RuntimeError, "DirectX 8.1 or greater required to run game"); - SET_EXCEPTION(CREATE_NO_DIRECTX); - TraceError("CreateDevice: DirectX 8.1 or greater required to run game"); - return false; - - case CGraphicDevice::CREATE_DEVICE: - //PyErr_SetString(PyExc_RuntimeError, "GraphicDevice create failed"); - SET_EXCEPTION(CREATE_DEVICE); - TraceError("CreateDevice: GraphicDevice create failed"); - return false; - - case CGraphicDevice::CREATE_FORMAT: - SET_EXCEPTION(CREATE_FORMAT); - TraceError("CreateDevice: Change the screen format"); - return false; - - /*case CGraphicDevice::CREATE_GET_ADAPTER_DISPLAY_MODE: - //PyErr_SetString(PyExc_RuntimeError, "GetAdapterDisplayMode failed"); - SET_EXCEPTION(CREATE_GET_ADAPTER_DISPLAY_MODE); - TraceError("CreateDevice: GetAdapterDisplayMode failed"); - return false;*/ - - case CGraphicDevice::CREATE_GET_DEVICE_CAPS: - PyErr_SetString(PyExc_RuntimeError, "GetDevCaps failed"); - TraceError("CreateDevice: GetDevCaps failed"); - return false; - - case CGraphicDevice::CREATE_GET_DEVICE_CAPS2: - PyErr_SetString(PyExc_RuntimeError, "GetDevCaps2 failed"); - TraceError("CreateDevice: GetDevCaps2 failed"); - return false; - - default: - if (iRet & CGraphicDevice::CREATE_OK) - { - //if (iRet & CGraphicDevice::CREATE_BAD_DRIVER) - //{ - // LogBox(ApplicationStringTable_GetStringz(IDS_WARN_BAD_DRIVER), NULL, GetWindowHandle()); - //} - if (iRet & CGraphicDevice::CREATE_NO_TNL) - { - CGrannyLODController::SetMinLODMode(true); - //LogBox(ApplicationStringTable_GetStringz(IDS_WARN_NO_TNL), NULL, GetWindowHandle()); - } + case CGraphicDevice::CREATE_OK: return true; - } - //PyErr_SetString(PyExc_RuntimeError, "Unknown Error!"); - SET_EXCEPTION(UNKNOWN_ERROR); - TraceError("CreateDevice: Unknown Error!"); - return false; + case CGraphicDevice::CREATE_REFRESHRATE: + return true; + + case CGraphicDevice::CREATE_ENUM: + case CGraphicDevice::CREATE_DETECT: + SET_EXCEPTION(CREATE_NO_APPROPRIATE_DEVICE); + TraceError("CreateDevice: Enum & Detect failed"); + return false; + + case CGraphicDevice::CREATE_NO_DIRECTX: + SET_EXCEPTION(CREATE_NO_DIRECTX); + TraceError("CreateDevice: DirectX 8.1 or greater required to run game"); + return false; + + case CGraphicDevice::CREATE_DEVICE: + SET_EXCEPTION(CREATE_DEVICE); + TraceError("CreateDevice: GraphicDevice create failed"); + return false; + + case CGraphicDevice::CREATE_FORMAT: + SET_EXCEPTION(CREATE_FORMAT); + TraceError("CreateDevice: Change the screen format"); + return false; + + case CGraphicDevice::CREATE_GET_DEVICE_CAPS: + PyErr_SetString(PyExc_RuntimeError, "GetDevCaps failed"); + TraceError("CreateDevice: GetDevCaps failed"); + return false; + + case CGraphicDevice::CREATE_GET_DEVICE_CAPS2: + PyErr_SetString(PyExc_RuntimeError, "GetDevCaps2 failed"); + TraceError("CreateDevice: GetDevCaps2 failed"); + return false; + + default: + if (iRet & CGraphicDevice::CREATE_OK) + { + if (iRet & CGraphicDevice::CREATE_NO_TNL) + { + CGrannyLODController::SetMinLODMode(true); + } + return true; + } + + SET_EXCEPTION(UNKNOWN_ERROR); + TraceError("CreateDevice: Unknown Error!"); + return false; } } @@ -840,38 +825,37 @@ void CPythonApplication::Loop() } } -// SUPPORT_NEW_KOREA_SERVER bool LoadLocaleData(const char* localePath) { - NANOBEGIN - CPythonNonPlayer& rkNPCMgr = CPythonNonPlayer::Instance(); + CPythonNonPlayer& rkNPCMgr = CPythonNonPlayer::Instance(); CItemManager& rkItemMgr = CItemManager::Instance(); CPythonSkill& rkSkillMgr = CPythonSkill::Instance(); CPythonNetworkStream& rkNetStream = CPythonNetworkStream::Instance(); char szItemList[256]; char szItemProto[256]; - char szItemDesc[256]; + char szItemDesc[256]; char szMobProto[256]; char szSkillDescFileName[256]; char szSkillTableFileName[256]; char szInsultList[256]; - snprintf(szItemList, sizeof(szItemList) , "%s/item_list.txt", localePath); - snprintf(szItemProto, sizeof(szItemProto), "%s/item_proto", localePath); - snprintf(szItemDesc, sizeof(szItemDesc), "%s/itemdesc.txt", localePath); - snprintf(szMobProto, sizeof(szMobProto), "%s/mob_proto", localePath); - snprintf(szSkillDescFileName, sizeof(szSkillDescFileName), "%s/SkillDesc.txt", localePath); - snprintf(szSkillTableFileName, sizeof(szSkillTableFileName), "%s/SkillTable.txt", localePath); - snprintf(szInsultList, sizeof(szInsultList), "%s/insult.txt", localePath); + + snprintf (szItemList, sizeof (szItemList), "%s/item_list.txt", GetLocalePathCommon()); + snprintf (szItemProto, sizeof (szItemProto), "%s/item_proto", localePath); + snprintf (szItemDesc, sizeof (szItemDesc), "%s/itemdesc.txt", localePath); + snprintf (szMobProto, sizeof (szMobProto), "%s/mob_proto", localePath); + snprintf (szSkillDescFileName, sizeof (szSkillDescFileName), "%s/SkillDesc.txt", localePath); + snprintf (szSkillTableFileName, sizeof (szSkillTableFileName), "%s/SkillTable.txt", GetLocalePathCommon()); + snprintf (szInsultList, sizeof (szInsultList), "%s/insult.txt", localePath); rkNPCMgr.Destroy(); - rkItemMgr.Destroy(); + rkItemMgr.Destroy(); rkSkillMgr.Destroy(); if (!rkItemMgr.LoadItemList(szItemList)) { TraceError("LoadLocaleData - LoadItemList(%s) Error", szItemList); - } + } if (!rkItemMgr.LoadItemTable(szItemProto)) { @@ -881,7 +865,7 @@ bool LoadLocaleData(const char* localePath) if (!rkItemMgr.LoadItemDesc(szItemDesc)) { - Tracenf("LoadLocaleData - LoadItemDesc(%s) Error", szItemDesc); + Tracenf("LoadLocaleData - LoadItemDesc(%s) Error", szItemDesc); } if (!rkNPCMgr.LoadNonPlayerData(szMobProto)) @@ -904,26 +888,11 @@ bool LoadLocaleData(const char* localePath) if (!rkNetStream.LoadInsultList(szInsultList)) { - Tracenf("CPythonApplication - CPythonNetworkStream::LoadInsultList(%s)", szInsultList); + Tracenf("CPythonApplication - CPythonNetworkStream::LoadInsultList(%s)", szInsultList); } - if (LocaleService_IsYMIR()) - { - char szEmpireTextConvFile[256]; - for (DWORD dwEmpireID=1; dwEmpireID<=3; ++dwEmpireID) - { - sprintf(szEmpireTextConvFile, "%s/lang%d.cvt", localePath, dwEmpireID); - if (!rkNetStream.LoadConvertTable(dwEmpireID, szEmpireTextConvFile)) - { - TraceError("LoadLocaleData - CPythonNetworkStream::LoadConvertTable(%d, %s) FAILURE", dwEmpireID, szEmpireTextConvFile); - } - } - } - - NANOEND - return true; + return true; } -// END_OF_SUPPORT_NEW_KOREA_SERVER unsigned __GetWindowMode(bool windowed) { @@ -940,7 +909,9 @@ bool CPythonApplication::Create(PyObject * poSelf, const char * c_szName, int wi bool bAnotherWindow = false; - if (FindWindow(NULL, c_szName)) + std::wstring wWindowName = Utf8ToWide(c_szName ? c_szName : ""); + + if (FindWindowW(nullptr, wWindowName.c_str())) bAnotherWindow = true; m_dwWidth = width; @@ -951,7 +922,6 @@ bool CPythonApplication::Create(PyObject * poSelf, const char * c_szName, int wi if (!CMSWindow::Create(c_szName, 4, 0, WindowMode, ::LoadIcon( GetInstance(), MAKEINTRESOURCE( IDI_METIN2 ) ), IDC_CURSOR_NORMAL)) { - //PyErr_SetString(PyExc_RuntimeError, "CMSWindow::Create failed"); TraceError("CMSWindow::Create failed"); SET_EXCEPTION(CREATE_WINDOW); return false; @@ -966,10 +936,7 @@ bool CPythonApplication::Create(PyObject * poSelf, const char * c_szName, int wi m_pyNetworkStream.Discord_Start(); #endif - // Ç®½ºÅ©¸° ¸ðµåÀ̰í - // µðÆúÆ® IME ¸¦ »ç¿ëÇϰųª À¯·´ ¹öÀüÀ̸é - // À©µµ¿ì Ç®½ºÅ©¸° ¸ðµå¸¦ »ç¿ëÇÑ´Ù - if (!m_pySystem.IsWindowed() && (m_pySystem.IsUseDefaultIME() || LocaleService_IsEUROPE())) + if (!m_pySystem.IsWindowed()) { m_isWindowed = false; m_isWindowFullScreenEnable = TRUE; @@ -1012,7 +979,6 @@ bool CPythonApplication::Create(PyObject * poSelf, const char * c_szName, int wi // Cursor if (!CreateCursors()) { - //PyErr_SetString(PyExc_RuntimeError, "CMSWindow::Cursors Create Error"); TraceError("CMSWindow::Cursors Create Error"); SET_EXCEPTION("CREATE_CURSOR"); return false; @@ -1057,7 +1023,7 @@ bool CPythonApplication::Create(PyObject * poSelf, const char * c_szName, int wi SetVisibleMode(true); - if (m_isWindowFullScreenEnable) //m_pySystem.IsUseDefaultIME() && !m_pySystem.IsWindowed()) + if (m_isWindowFullScreenEnable) { SetWindowPos(GetWindowHandle(), HWND_TOP, 0, 0, width, height, SWP_SHOWWINDOW); } @@ -1076,7 +1042,6 @@ bool CPythonApplication::Create(PyObject * poSelf, const char * c_szName, int wi // Network if (!m_netDevice.Create()) { - //PyErr_SetString(PyExc_RuntimeError, "NetDevice::Create failed"); TraceError("NetDevice::Create failed"); SET_EXCEPTION("CREATE_NETWORK"); return false; diff --git a/src/UserInterface/PythonApplication.h b/src/UserInterface/PythonApplication.h index c60bc55..fc0ef5c 100644 --- a/src/UserInterface/PythonApplication.h +++ b/src/UserInterface/PythonApplication.h @@ -226,7 +226,6 @@ class CPythonApplication : public CMSApplication, public CInputKeyboard, public void RunIMEReturnEvent(); void RunPressExitKey(); - void RunIMEChangeCodePage(); void RunIMEOpenCandidateListEvent(); void RunIMECloseCandidateListEvent(); void RunIMEOpenReadingWndEvent(); diff --git a/src/UserInterface/PythonApplicationCamera.cpp b/src/UserInterface/PythonApplicationCamera.cpp index ba96b3e..94f9cc6 100644 --- a/src/UserInterface/PythonApplicationCamera.cpp +++ b/src/UserInterface/PythonApplicationCamera.cpp @@ -4,6 +4,8 @@ #include "EterBase/timer.h" #include "EterLib/Camera.h" +#include + float BlendValueByLinear(float fElapsedTime, float fDuration, float fBeginValue, float fEndValue) { if (fElapsedTime >= fDuration) @@ -359,8 +361,14 @@ void CPythonApplication::SaveCameraSetting(const char * c_szFileName) SCameraSetting CameraSetting; GetCameraSetting(&CameraSetting); - FILE * File = fopen(c_szFileName, "w"); - SetFileAttributes(c_szFileName, FILE_ATTRIBUTE_NORMAL); + // UTF-8 → UTF-16 + std::wstring wFileName = Utf8ToWide(c_szFileName); + + FILE* File = _wfopen(wFileName.c_str(), L"w"); + if (!File) + return; + + SetFileAttributesW(wFileName.c_str(), FILE_ATTRIBUTE_NORMAL); PrintfTabs(File, 0, "CenterPos %f %f %f\n", CameraSetting.v3CenterPosition.x, CameraSetting.v3CenterPosition.y, CameraSetting.v3CenterPosition.z); PrintfTabs(File, 0, "CameraSetting %f %f %f\n", CameraSetting.fZoom, CameraSetting.fPitch, CameraSetting.fRotation); diff --git a/src/UserInterface/PythonApplicationEvent.cpp b/src/UserInterface/PythonApplicationEvent.cpp index d5b3fcb..2bbd0e5 100644 --- a/src/UserInterface/PythonApplicationEvent.cpp +++ b/src/UserInterface/PythonApplicationEvent.cpp @@ -166,11 +166,6 @@ void CPythonApplication::OnIMEKeyDown(int iIndex) } ///////////////////////////// -void CPythonApplication::RunIMEChangeCodePage() -{ - UI::CWindowManager& rkWndMgr=UI::CWindowManager::Instance(); - rkWndMgr.RunChangeCodePage(); -} void CPythonApplication::RunIMEOpenCandidateListEvent() { UI::CWindowManager& rkWndMgr=UI::CWindowManager::Instance(); diff --git a/src/UserInterface/PythonApplicationLogo.cpp b/src/UserInterface/PythonApplicationLogo.cpp index b70ebfb..26b9046 100644 --- a/src/UserInterface/PythonApplicationLogo.cpp +++ b/src/UserInterface/PythonApplicationLogo.cpp @@ -41,7 +41,11 @@ int CPythonApplication::OnLogoOpen(char* szName) // Render File WCHAR wFileName[ MAX_PATH ]; - MultiByteToWideChar(CP_ACP, 0, szName, -1, wFileName, MAX_PATH); + + int ok = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, szName, -1, wFileName, MAX_PATH); + if (ok <= 0) + return 0; + if(FAILED(m_pGraphBuilder->RenderFile(wFileName, NULL))) { return 0; } IBaseFilter* pSrc; diff --git a/src/UserInterface/PythonApplicationModule.cpp b/src/UserInterface/PythonApplicationModule.cpp index 54051f3..8454c22 100644 --- a/src/UserInterface/PythonApplicationModule.cpp +++ b/src/UserInterface/PythonApplicationModule.cpp @@ -3,8 +3,10 @@ #include "PythonApplication.h" #include "EterLib/Camera.h" #include "PackLib/PackManager.h" +#include "EterBase/tea.h" #include +#include extern D3DXCOLOR g_fSpecularColor; extern BOOL bVisibleNotice = true; @@ -29,7 +31,7 @@ PyObject* appShowWebPage(PyObject* poSelf, PyObject* poArgs) CPythonApplication::Instance().ShowWebPage( szWebPage, - rcWebPage + rcWebPage ); return Py_BuildNone(); } @@ -215,80 +217,8 @@ PyObject* appSetFrameSkip(PyObject* poSelf, PyObject* poArgs) } // LOCALE - -PyObject* appForceSetLocale(PyObject* poSelf, PyObject* poArgs) -{ - char* szName; - if (!PyTuple_GetString(poArgs, 0, &szName)) - return Py_BuildException(); - - char* szLocalePath; - if (!PyTuple_GetString(poArgs, 1, &szLocalePath)) - return Py_BuildException(); - - LocaleService_ForceSetLocale(szName, szLocalePath); - - return Py_BuildNone(); -} - -PyObject* appGetLocaleServiceName(PyObject* poSelf, PyObject* poArgs) -{ - return Py_BuildValue("s", LocaleService_GetName()); -} - -// bool LoadLocaleData(const char* localePath); -PyObject* appSetCHEONMA(PyObject* poSelf, PyObject* poArgs) -{ - int enable; - if (!PyTuple_GetInteger(poArgs, 0, &enable)) - return Py_BuildException(); - - LocaleService_SetCHEONMA(enable ? true : false); - return Py_BuildNone(); -} - -PyObject* appIsCHEONMA(PyObject* poSelf, PyObject* poArgs) -{ - return Py_BuildValue("i", LocaleService_IsCHEONMA()); -} - -#include "EterBase/tea.h" - -PyObject* appLoadLocaleAddr(PyObject* poSelf, PyObject* poArgs) -{ - char* addrPath; - if (!PyTuple_GetString(poArgs, 0, &addrPath)) - return Py_BuildException(); - - FILE* fp = fopen(addrPath, "rb"); - if (!fp) - return Py_BuildException(); - - fseek(fp, 0, SEEK_END); - - int size = ftell(fp); - char* enc = (char*)_alloca(size); - fseek(fp, 0, SEEK_SET); - fread(enc, size, 1, fp); - fclose(fp); - - static const unsigned char key[16] = { - 0x82, 0x1b, 0x34, 0xae, - 0x12, 0x3b, 0xfb, 0x17, - 0xd7, 0x2c, 0x39, 0xae, - 0x41, 0x98, 0xf1, 0x63 - }; - - char* buf = (char*)_alloca(size); - //int decSize = - tea_decrypt((unsigned long*)buf, (const unsigned long*)enc, (const unsigned long*)key, size); - unsigned int retSize = *(unsigned int*)buf; - char* ret = buf + sizeof(unsigned int); - return Py_BuildValue("s#", ret, retSize); -} - PyObject* appLoadLocaleData(PyObject* poSelf, PyObject* poArgs) { char* localePath; @@ -300,20 +230,20 @@ PyObject* appLoadLocaleData(PyObject* poSelf, PyObject* poArgs) PyObject* appGetLocaleName(PyObject* poSelf, PyObject* poArgs) { - return Py_BuildValue("s", LocaleService_GetLocaleName()); + return Py_BuildValue("s", GetLocaleName()); } PyObject* appGetLocalePath(PyObject* poSelf, PyObject* poArgs) { - return Py_BuildValue("s", LocaleService_GetLocalePath()); + return Py_BuildValue("s", GetLocalePath()); +} + +PyObject* appGetLocalePathCommon(PyObject* poSelf, PyObject* poArgs) +{ + return Py_BuildValue("s", GetLocalePathCommon()); } // END_OF_LOCALE -PyObject* appGetDefaultCodePage(PyObject* poSelf, PyObject* poArgs) -{ - return Py_BuildValue("i", LocaleService_GetCodePage()); -} - #ifdef __VTUNE__ PyObject* appGetImageInfo(PyObject* poSelf, PyObject* poArgs) @@ -353,25 +283,25 @@ PyObject* appIsExistFile(PyObject* poSelf, PyObject* poArgs) PyObject* appGetFileList(PyObject* poSelf, PyObject* poArgs) { - char* szFilter; + char* szFilter = nullptr; if (!PyTuple_GetString(poArgs, 0, &szFilter)) return Py_BuildException(); - PyObject* poList=PyList_New(0); + std::wstring wFilter = Utf8ToWide(szFilter ? szFilter : ""); - WIN32_FIND_DATA wfd; - memset(&wfd, 0, sizeof(wfd)); + PyObject* poList = PyList_New(0); - HANDLE hFind = FindFirstFile(szFilter, &wfd); + WIN32_FIND_DATAW wfd{}; + HANDLE hFind = FindFirstFileW(wFilter.c_str(), &wfd); if (hFind != INVALID_HANDLE_VALUE) - { + { do { - PyObject* poFileName=PyString_FromString(wfd.cFileName) ; + std::string filenameUtf8 = WideToUtf8(wfd.cFileName); + PyObject* poFileName = PyString_FromString(filenameUtf8.c_str()); PyList_Append(poList, poFileName); - } - while (FindNextFile(hFind, &wfd)); - + Py_DECREF(poFileName); + } while (FindNextFileW(hFind, &wfd)); FindClose(hFind); } @@ -379,7 +309,6 @@ PyObject* appGetFileList(PyObject* poSelf, PyObject* poArgs) return poList; } - PyObject* appUpdateGame(PyObject* poSelf, PyObject* poArgs) { CPythonApplication::Instance().UpdateGame(); @@ -1093,56 +1022,29 @@ PyObject * appSetGuildMarkPath(PyObject * poSelf, PyObject * poArgs) if (!PyTuple_GetString(poArgs, 0, &path)) return Py_BuildException(); - char newPath[256]; - char * ext = strstr(path, ".tga"); + char newPath[256]; + char * ext = strstr(path, ".tga"); - if (ext) - { + if (ext) + { int extPos = ext - path; - strncpy(newPath, path, extPos); - newPath[extPos] = '\0'; - } - else - strncpy(newPath, path, sizeof(newPath)-1); - + strncpy(newPath, path, extPos); + newPath[extPos] = '\0'; + } + else + strncpy(newPath, path, sizeof(newPath)-1); + CGuildMarkManager::Instance().SetMarkPathPrefix(newPath); return Py_BuildNone(); } -PyObject* appIsDevStage(PyObject* poSelf, PyObject* poArgs) -{ - int nIsDevelopmentStage = 0; -#if defined(LOCALE_SERVICE_STAGE_DEVELOPMENT) - nIsDevelopmentStage = 1; -#endif - return Py_BuildValue("i", nIsDevelopmentStage); -} - -PyObject* appIsTestStage(PyObject* poSelf, PyObject* poArgs) -{ - int nIsTestStage = 0; -#if defined(LOCALE_SERVICE_STAGE_TEST) - nIsTestStage = 1; -#endif - return Py_BuildValue("i", nIsTestStage); -} - -PyObject* appIsLiveStage(PyObject* poSelf, PyObject* poArgs) -{ - int nIsLiveStage = 0; -#if !defined(LOCALE_SERVICE_STAGE_TEST) && !defined(LOCALE_SERVICE_STAGE_DEVELOPMENT) - nIsLiveStage = 1; -#endif - return Py_BuildValue("i", nIsLiveStage); -} - PyObject* appLogoOpen(PyObject* poSelf, PyObject* poArgs) { char* szName; if (!PyTuple_GetString(poArgs, 0, &szName)) return Py_BuildException(); - int nIsSuccess = 1; //CPythonApplication::Instance().OnLogoOpen(szName); + int nIsSuccess = 1; CMovieMan::Instance().PlayLogo(szName); return Py_BuildValue("i", nIsSuccess); @@ -1150,7 +1052,7 @@ PyObject* appLogoOpen(PyObject* poSelf, PyObject* poArgs) PyObject* appLogoUpdate(PyObject* poSelf, PyObject* poArgs) { - int nIsRun = 0; //CPythonApplication::Instance().OnLogoUpdate(); + int nIsRun = 0; return Py_BuildValue("i", nIsRun); } @@ -1166,21 +1068,22 @@ PyObject* appLogoClose(PyObject* poSelf, PyObject* poArgs) return Py_BuildNone(); } +PyObject* appIsRTL(PyObject* poSelf, PyObject* poArgs) +{ + return Py_BuildValue("i", IsRTL() ? 1 : 0); +} + void initapp() { static PyMethodDef s_methods[] = - { - { "IsDevStage", appIsDevStage, METH_VARARGS }, - { "IsTestStage", appIsTestStage, METH_VARARGS }, - { "IsLiveStage", appIsLiveStage, METH_VARARGS }, - + { // TEXTTAIL_LIVINGTIME_CONTROL { "SetTextTailLivingTime", appSetTextTailLivingTime, METH_VARARGS }, // END_OF_TEXTTAIL_LIVINGTIME_CONTROL - + { "EnablePerformanceTime", appEnablePerformanceTime, METH_VARARGS }, { "SetHairColorEnable", appSetHairColorEnable, METH_VARARGS }, - + { "SetArmorSpecularEnable", appSetArmorSpecularEnable, METH_VARARGS }, { "SetWeaponSpecularEnable", appSetWeaponSpecularEnable, METH_VARARGS }, { "SetSkillEffectUpgradeEnable",appSetSkillEffectUpgradeEnable, METH_VARARGS }, @@ -1273,20 +1176,13 @@ void initapp() { "GetTextFileLine", appGetTextFileLine, METH_VARARGS }, // LOCALE - { "GetLocaleServiceName", appGetLocaleServiceName, METH_VARARGS }, { "GetLocaleName", appGetLocaleName, METH_VARARGS }, { "GetLocalePath", appGetLocalePath, METH_VARARGS }, - { "ForceSetLocale", appForceSetLocale, METH_VARARGS }, + { "GetLocalePathCommon", appGetLocalePathCommon, METH_VARARGS }, + { "LoadLocaleData", appLoadLocaleData, METH_VARARGS }, + { "IsRTL", appIsRTL, METH_VARARGS }, // END_OF_LOCALE - // CHEONMA - { "LoadLocaleAddr", appLoadLocaleAddr, METH_VARARGS }, - { "LoadLocaleData", appLoadLocaleData, METH_VARARGS }, - { "SetCHEONMA", appSetCHEONMA, METH_VARARGS }, - { "IsCHEONMA", appIsCHEONMA, METH_VARARGS }, - // END_OF_CHEONMA - - { "GetDefaultCodePage", appGetDefaultCodePage, METH_VARARGS }, { "SetControlFP", appSetControlFP, METH_VARARGS }, { "SetSpecularSpeed", appSetSpecularSpeed, METH_VARARGS }, @@ -1305,6 +1201,7 @@ void initapp() { "OnLogoRender", appLogoRender, METH_VARARGS }, { "OnLogoOpen", appLogoOpen, METH_VARARGS }, { "OnLogoClose", appLogoClose, METH_VARARGS }, + { NULL, NULL }, }; diff --git a/src/UserInterface/PythonBackground.cpp b/src/UserInterface/PythonBackground.cpp index 4ed6f3d..cf4503c 100644 --- a/src/UserInterface/PythonBackground.cpp +++ b/src/UserInterface/PythonBackground.cpp @@ -233,7 +233,7 @@ CPythonBackground::~CPythonBackground() void CPythonBackground::Initialize() { - std::string stAtlasInfoFileName (LocaleService_GetLocalePath()); + std::string stAtlasInfoFileName(GetLocalePath()); stAtlasInfoFileName += "/AtlasInfo.txt"; SetAtlasInfoFileName(stAtlasInfoFileName.c_str()); CMapManager::Initialize(); diff --git a/src/UserInterface/PythonChat.cpp b/src/UserInterface/PythonChat.cpp index 3195c49..1fb2048 100644 --- a/src/UserInterface/PythonChat.cpp +++ b/src/UserInterface/PythonChat.cpp @@ -5,6 +5,8 @@ #include "PythonCharacterManager.h" #include "EterBase/Timer.h" +#include + int CPythonChat::TChatSet::ms_iChatModeSize = CHAT_TYPE_MAX_NUM; const float c_fStartDisappearingTime = 5.0f; @@ -135,7 +137,13 @@ void CPythonChat::UpdateViewMode(DWORD dwID) --iLineIndex; pChatLine->Instance.SetPosition(pChatSet->m_ix, pChatSet->m_iy + iHeight); - pChatLine->Instance.SetColor(rColor); + pChatLine->Instance.SetColor(pChatLine->GetColorRef(dwID)); + + if (pChatSet->m_iAlign == 1) + pChatLine->Instance.SetTextDirection(CGraphicTextInstance::ETextDirection::RTL); + else + pChatLine->Instance.SetTextDirection(CGraphicTextInstance::ETextDirection::LTR); + pChatLine->Instance.Update(); } } @@ -146,7 +154,7 @@ void CPythonChat::UpdateEditMode(DWORD dwID) if (!pChatSet) return; - const int c_iAlphaLine = std::max(0, GetVisibleLineCount(dwID) - GetEditableLineCount(dwID) + 2); + const int c_iAlphaLine = (std::max)(0, GetVisibleLineCount(dwID) - GetEditableLineCount(dwID) + 2); int iLineIndex = 0; float fAlpha = 0.0f; @@ -175,8 +183,15 @@ void CPythonChat::UpdateEditMode(DWORD dwID) } iHeight += pChatSet->m_iStep; + pChatLine->Instance.SetPosition(pChatSet->m_ix, pChatSet->m_iy + iHeight); - pChatLine->Instance.SetColor(rColor); + pChatLine->Instance.SetColor(pChatLine->GetColorRef(dwID)); + + if (pChatSet->m_iAlign == 1) + pChatLine->Instance.SetTextDirection(CGraphicTextInstance::ETextDirection::RTL); + else + pChatLine->Instance.SetTextDirection(CGraphicTextInstance::ETextDirection::LTR); + pChatLine->Instance.Update(); } } @@ -195,8 +210,15 @@ void CPythonChat::UpdateLogMode(DWORD dwID) TChatLine * pChatLine = (*itor); iHeight -= pChatSet->m_iStep; + pChatLine->Instance.SetPosition(pChatSet->m_ix, pChatSet->m_iy + iHeight); pChatLine->Instance.SetColor(pChatLine->GetColorRef(dwID)); + + if (pChatSet->m_iAlign == 1) + pChatLine->Instance.SetTextDirection(CGraphicTextInstance::ETextDirection::RTL); + else + pChatLine->Instance.SetTextDirection(CGraphicTextInstance::ETextDirection::LTR); + pChatLine->Instance.Update(); } } @@ -251,7 +273,6 @@ void CPythonChat::Render(DWORD dwID) } } - void CPythonChat::SetBoardState(DWORD dwID, int iState) { TChatSet * pChatSet = GetChatSetPtr(dwID); @@ -261,6 +282,7 @@ void CPythonChat::SetBoardState(DWORD dwID, int iState) pChatSet->m_iBoardState = iState; ArrangeShowingChat(dwID); } + void CPythonChat::SetPosition(DWORD dwID, int ix, int iy) { TChatSet * pChatSet = GetChatSetPtr(dwID); @@ -270,6 +292,25 @@ void CPythonChat::SetPosition(DWORD dwID, int ix, int iy) pChatSet->m_ix = ix; pChatSet->m_iy = iy; } + +void CPythonChat::SetAlign(DWORD dwID, int iAlign) +{ + TChatSet* pChatSet = GetChatSetPtr(dwID); + if (!pChatSet) + return; + + pChatSet->m_iAlign = (iAlign != 0) ? 1 : 0; +} + +void CPythonChat::SetWidth(DWORD dwID, int iWidth) +{ + TChatSet* pChatSet = GetChatSetPtr(dwID); + if (!pChatSet) + return; + + pChatSet->m_iWidth = iWidth; +} + void CPythonChat::SetHeight(DWORD dwID, int iHeight) { TChatSet * pChatSet = GetChatSetPtr(dwID); @@ -278,6 +319,7 @@ void CPythonChat::SetHeight(DWORD dwID, int iHeight) pChatSet->m_iHeight = iHeight; } + void CPythonChat::SetStep(DWORD dwID, int iStep) { TChatSet * pChatSet = GetChatSetPtr(dwID); @@ -286,6 +328,7 @@ void CPythonChat::SetStep(DWORD dwID, int iStep) pChatSet->m_iStep = iStep; } + void CPythonChat::ToggleChatMode(DWORD dwID, int iMode) { TChatSet * pChatSet = GetChatSetPtr(dwID); @@ -296,6 +339,7 @@ void CPythonChat::ToggleChatMode(DWORD dwID, int iMode) // Tracef("ToggleChatMode : %d\n", iMode); ArrangeShowingChat(dwID); } + void CPythonChat::EnableChatMode(DWORD dwID, int iMode) { TChatSet * pChatSet = GetChatSetPtr(dwID); @@ -306,6 +350,7 @@ void CPythonChat::EnableChatMode(DWORD dwID, int iMode) // Tracef("EnableChatMode : %d\n", iMode); ArrangeShowingChat(dwID); } + void CPythonChat::DisableChatMode(DWORD dwID, int iMode) { TChatSet * pChatSet = GetChatSetPtr(dwID); @@ -316,6 +361,7 @@ void CPythonChat::DisableChatMode(DWORD dwID, int iMode) // Tracef("DisableChatMode : %d\n", iMode); ArrangeShowingChat(dwID); } + void CPythonChat::SetEndPos(DWORD dwID, float fPos) { TChatSet * pChatSet = GetChatSetPtr(dwID); @@ -452,8 +498,21 @@ void CPythonChat::AppendChat(int iType, const char * c_szChat) IAbstractApplication& rApp=IAbstractApplication::GetSingleton(); SChatLine * pChatLine = SChatLine::New(); pChatLine->iType = iType; + + // Pass chat text as-is to BiDi algorithm + // BuildVisualBidiText_Tagless will detect chat format and handle reordering pChatLine->Instance.SetValue(c_szChat); + if (IsRTL()) + { + // RTL mode: BiDi will handle chat format detection and reordering + pChatLine->Instance.SetTextDirection(CGraphicTextInstance::ETextDirection::RTL); + } + else + { + pChatLine->Instance.SetTextDirection(CGraphicTextInstance::ETextDirection::LTR); + } + // DEFAULT_FONT pChatLine->Instance.SetTextPointer(pkDefaultFont); // END_OF_DEFAULT_FONT @@ -699,25 +758,30 @@ void CWhisper::SetBoxSize(float fWidth, float fHeight) void CWhisper::AppendChat(int iType, const char * c_szChat) { - // DEFAULT_FONT - //static CResource * s_pResource = CResourceManager::Instance().GetResourcePointer(g_strDefaultFontName.c_str()); - -#if defined(LOCALE_SERVICE_YMIR) || defined(LOCALE_SERVICE_JAPAN) || defined(LOCALE_SERVICE_HONGKONG) || defined(LOCALE_SERVICE_TAIWAN) || defined(LOCALE_SERVICE_NEWCIBN) - CGraphicText* pkDefaultFont = static_cast(DefaultFont_GetResource()); -#else CGraphicText* pkDefaultFont = (iType == CPythonChat::WHISPER_TYPE_GM) ? static_cast(DefaultItalicFont_GetResource()) : static_cast(DefaultFont_GetResource()); -#endif if (!pkDefaultFont) { TraceError("CWhisper::AppendChat - CANNOT_FIND_DEFAULT_FONT"); return; } - // END_OF_DEFAULT_FONT SChatLine * pChatLine = SChatLine::New(); + + // Pass chat text as-is to BiDi algorithm + // BuildVisualBidiText_Tagless will detect chat format and handle reordering pChatLine->Instance.SetValue(c_szChat); + if (IsRTL()) + { + // RTL mode: BiDi will handle chat format detection and reordering + pChatLine->Instance.SetTextDirection(CGraphicTextInstance::ETextDirection::RTL); + } + else + { + pChatLine->Instance.SetTextDirection(CGraphicTextInstance::ETextDirection::LTR); + } + // DEFAULT_FONT pChatLine->Instance.SetTextPointer(pkDefaultFont); // END_OF_DEFAULT_FONT diff --git a/src/UserInterface/PythonChat.h b/src/UserInterface/PythonChat.h index f8f24b2..47ef466 100644 --- a/src/UserInterface/PythonChat.h +++ b/src/UserInterface/PythonChat.h @@ -129,9 +129,11 @@ class CPythonChat : public CSingleton, public IAbstractChat { int m_ix; int m_iy; + int m_iWidth; int m_iHeight; int m_iStep; float m_fEndPos; + int m_iAlign; int m_iBoardState; std::vector m_iMode; @@ -153,8 +155,10 @@ class CPythonChat : public CSingleton, public IAbstractChat m_ix = 0; m_iy = 0; m_fEndPos = 1.0f; + m_iWidth = 0; m_iHeight = 0; m_iStep = 15; + m_iAlign = 0; m_iMode.clear(); m_iMode.resize(ms_iChatModeSize, 1); @@ -182,6 +186,8 @@ class CPythonChat : public CSingleton, public IAbstractChat void SetBoardState(DWORD dwID, int iState); void SetPosition(DWORD dwID, int ix, int iy); void SetHeight(DWORD dwID, int iHeight); + void SetWidth(DWORD dwID, int iWidth); + void SetAlign(DWORD dwID, int iAlign); void SetStep(DWORD dwID, int iStep); void ToggleChatMode(DWORD dwID, int iMode); void EnableChatMode(DWORD dwID, int iMode); diff --git a/src/UserInterface/PythonChatModule.cpp b/src/UserInterface/PythonChatModule.cpp index 80f9605..ba18fa6 100644 --- a/src/UserInterface/PythonChatModule.cpp +++ b/src/UserInterface/PythonChatModule.cpp @@ -97,6 +97,33 @@ PyObject * chatSetPosition(PyObject* poSelf, PyObject* poArgs) return Py_BuildNone(); } +PyObject* chatSetAlign(PyObject* poSelf, PyObject* poArgs) +{ + int iID; + if (!PyTuple_GetInteger(poArgs, 0, &iID)) + return Py_BuildException(); + + int iAlign; + if (!PyTuple_GetInteger(poArgs, 1, &iAlign)) + return Py_BuildException(); + + CPythonChat::Instance().SetAlign(iID, iAlign); + return Py_BuildNone(); +} + +PyObject* chatSetWidth(PyObject* poSelf, PyObject* poArgs) +{ + int iID; + int iWidth; + if (!PyTuple_GetInteger(poArgs, 0, &iID)) + return Py_BadArgument(); + if (!PyTuple_GetInteger(poArgs, 1, &iWidth)) + return Py_BadArgument(); + + CPythonChat::Instance().SetWidth((DWORD)iID, iWidth); + return Py_BuildNone(); +} + PyObject * chatSetHeight(PyObject* poSelf, PyObject* poArgs) { int iID; @@ -462,6 +489,8 @@ void initChat() { "SetBoardState", chatSetBoardState, METH_VARARGS }, { "SetPosition", chatSetPosition, METH_VARARGS }, + { "SetAlign", chatSetAlign, METH_VARARGS }, + { "SetWidth", chatSetWidth, METH_VARARGS }, { "SetHeight", chatSetHeight, METH_VARARGS }, { "SetStep", chatSetStep, METH_VARARGS }, { "ToggleChatMode", chatToggleChatMode, METH_VARARGS }, diff --git a/src/UserInterface/PythonEventManager.cpp b/src/UserInterface/PythonEventManager.cpp index d55f8dd..d84764d 100644 --- a/src/UserInterface/PythonEventManager.cpp +++ b/src/UserInterface/PythonEventManager.cpp @@ -985,19 +985,19 @@ void CPythonEventManager::__InsertLine(TEventSet& rEventSet, BOOL isCenter, int int textWidth; int textHeight; rEventSet.pCurrentTextLine->GetTextSize(&textWidth,&textHeight); - if (GetDefaultCodePage() == CP_1256) + + const bool prevRTL = (rEventSet.pCurrentTextLine && rEventSet.pCurrentTextLine->IsRTL()); + + kLine.ixLocal = prevRTL ? rEventSet.iWidth : 0; + + if (iX_pos != 0) { - kLine.ixLocal = rEventSet.iWidth; - if (iX_pos != 0) + if (prevRTL) { - kLine.ixLocal -= iX_pos - 20; + kLine.ixLocal -= (iX_pos - 20); kLine.ixLocal += textWidth / 2; } - } - else - { - kLine.ixLocal = 0; - if (iX_pos != 0) + else { kLine.ixLocal += (iX_pos - 20); kLine.ixLocal -= textWidth / 2; @@ -1008,7 +1008,7 @@ void CPythonEventManager::__InsertLine(TEventSet& rEventSet, BOOL isCenter, int } kLine.pInstance = rEventSet.pCurrentTextLine; rEventSet.ScriptTextLineList.push_back(kLine); - __AddSpace(rEventSet, c_fLine_Temp); + __AddSpace(rEventSet, c_fLine_Temp); } // DEFAULT_FONT @@ -1040,16 +1040,8 @@ void CPythonEventManager::__InsertLine(TEventSet& rEventSet, BOOL isCenter, int } else { - if (GetDefaultCodePage() == CP_1256) - { - rEventSet.pCurrentTextLine->SetHorizonalAlign(CGraphicTextInstance::HORIZONTAL_ALIGN_LEFT); - rEventSet.pCurrentTextLine->SetPosition(rEventSet.ix + rEventSet.iWidth, rEventSet.iy + rEventSet.iyLocal); - } - else - { - rEventSet.pCurrentTextLine->SetHorizonalAlign(CGraphicTextInstance::HORIZONTAL_ALIGN_LEFT); - rEventSet.pCurrentTextLine->SetPosition(rEventSet.ix, rEventSet.iy + rEventSet.iyLocal); - } + rEventSet.pCurrentTextLine->SetHorizonalAlign(CGraphicTextInstance::HORIZONTAL_ALIGN_LEFT); + rEventSet.pCurrentTextLine->SetPosition(rEventSet.ix, rEventSet.iy + rEventSet.iyLocal); } rEventSet.iCurrentLetter = 0; @@ -1073,10 +1065,8 @@ void CPythonEventManager::RefreshLinePosition(TEventSet * pEventSet) } else { - if (GetDefaultCodePage() == CP_1256) - ixTextPos = pEventSet->ix+pEventSet->iWidth; - else - ixTextPos = pEventSet->ix; + const bool isRTL = (pEventSet->pCurrentTextLine && pEventSet->pCurrentTextLine->IsRTL()); + ixTextPos = isRTL ? (pEventSet->ix + pEventSet->iWidth) : pEventSet->ix; } pEventSet->pCurrentTextLine->SetPosition(ixTextPos, pEventSet->iy + pEventSet->iyLocal); } diff --git a/src/UserInterface/PythonGuild.cpp b/src/UserInterface/PythonGuild.cpp index bf12ea0..5a8cd65 100644 --- a/src/UserInterface/PythonGuild.cpp +++ b/src/UserInterface/PythonGuild.cpp @@ -475,7 +475,7 @@ PyObject * guildGetGuildExperience(PyObject * poSelf, PyObject * poArgs) if (rGuildInfo.dwGuildLevel >= GULID_MAX_LEVEL) return Py_BuildValue("ii", 0, 0); - unsigned lastExp = LocaleService_GetLastExp(rGuildInfo.dwGuildLevel); + unsigned lastExp = GetGuildLastExp(rGuildInfo.dwGuildLevel); return Py_BuildValue("ii", rGuildInfo.dwCurrentExperience, lastExp - rGuildInfo.dwCurrentExperience); } diff --git a/src/UserInterface/PythonIME.cpp b/src/UserInterface/PythonIME.cpp index 5ae38d5..9ec2d21 100644 --- a/src/UserInterface/PythonIME.cpp +++ b/src/UserInterface/PythonIME.cpp @@ -68,34 +68,88 @@ void CPythonIME::OnEscape() // IAbstractApplication::GetSingleton().RunIMEEscapeEvent(); } -bool CPythonIME::OnWM_CHAR( WPARAM wParam, LPARAM lParam ) +bool CPythonIME::OnWM_CHAR(WPARAM wParam, LPARAM lParam) { - unsigned char c = unsigned char(wParam & 0xff); + const wchar_t wc = (wchar_t)wParam; - switch (c) + const bool ctrlDown = (GetKeyState(VK_CONTROL) & 0x8000) != 0; + + // Ctrl+ combos only when Ctrl is pressed + if (ctrlDown) { - case VK_RETURN: - OnReturn(); - return true; + switch (wc) + { + case 0x01: // Ctrl+A + if (ms_bCaptureInput) + { + SelectAll(); + if (ms_pEvent) ms_pEvent->OnUpdate(); + return true; + } + return false; - case VK_TAB: - if(ms_bCaptureInput == false) - return 0; - OnTab(); - return true; + case 0x1A: // Ctrl+Z + if (ms_bCaptureInput) + { + CIME::Undo(); + return true; + } + return false; - case VK_ESCAPE: - if(ms_bCaptureInput == false) - return 0; - OnEscape(); - return true; + case 0x03: // Ctrl+C + if (ms_bCaptureInput) + { + CopySelectionToClipboard(); + return true; + } + return false; + + case 0x16: // Ctrl+V + if (ms_bCaptureInput) + { + PasteTextFromClipBoard(); + return true; + } + return false; + + case 0x18: // Ctrl+X + if (ms_bCaptureInput) + { + CutSelection(); + if (ms_pEvent) ms_pEvent->OnUpdate(); + return true; + } + return false; + + case 0x19: // Ctrl+Y + if (ms_bCaptureInput) + { + CIME::Redo(); + return true; + } + return false; + } } - return false; -} -void CPythonIME::OnChangeCodePage() -{ - IAbstractApplication::GetSingleton().RunIMEChangeCodePage(); + // Non-Ctrl special keys (WM_CHAR gives these too) + switch (wc) + { + case VK_RETURN: + OnReturn(); + return true; + + case VK_TAB: + if (!ms_bCaptureInput) return 0; + OnTab(); + return true; + + case VK_ESCAPE: + if (!ms_bCaptureInput) return 0; + OnEscape(); + return true; + } + + return false; } void CPythonIME::OnOpenCandidateList() @@ -117,3 +171,29 @@ void CPythonIME::OnCloseReadingWnd() { IAbstractApplication::GetSingleton().RunIMECloseReadingWndEvent(); } + +void CPythonIME::SelectAll() +{ + CIME::SelectAll(); +} + +void CPythonIME::DeleteSelection() +{ + CIME::DeleteSelection(); +} + +void CPythonIME::CopySelectionToClipboard() +{ + CIME::CopySelectionToClipboard(ms_hWnd); +} + +void CPythonIME::CutSelection() +{ + CIME::CopySelectionToClipboard(ms_hWnd); + CIME::DeleteSelection(); +} + +void CPythonIME::PasteTextFromClipBoard() +{ + CIME::PasteTextFromClipBoard(); +} diff --git a/src/UserInterface/PythonIME.h b/src/UserInterface/PythonIME.h index 51ed835..d5e2e31 100644 --- a/src/UserInterface/PythonIME.h +++ b/src/UserInterface/PythonIME.h @@ -19,6 +19,12 @@ public: void SetCursorPosition(int iPosition); void Delete(); + void SelectAll(); + void DeleteSelection(); + void CopySelectionToClipboard(); + void CutSelection(); + void PasteTextFromClipBoard(); + void Create(HWND hWnd); protected: @@ -28,9 +34,9 @@ protected: virtual bool OnWM_CHAR( WPARAM wParam, LPARAM lParam ); virtual void OnUpdate(); - virtual void OnChangeCodePage(); virtual void OnOpenCandidateList(); virtual void OnCloseCandidateList(); virtual void OnOpenReadingWnd(); virtual void OnCloseReadingWnd(); + }; diff --git a/src/UserInterface/PythonIMEModule.cpp b/src/UserInterface/PythonIMEModule.cpp index cfc6845..65533e4 100644 --- a/src/UserInterface/PythonIMEModule.cpp +++ b/src/UserInterface/PythonIMEModule.cpp @@ -57,19 +57,10 @@ PyObject* imeSetText(PyObject* poSelf, PyObject* poArgs) } PyObject* imeGetText(PyObject* poSelf, PyObject* poArgs) -{ - int bCodePage; - if (!PyTuple_GetInteger(poArgs, 0, &bCodePage)) - bCodePage = 0; - - std::string strText; - CPythonIME::Instance().GetText(strText, bCodePage ? true : false); - return Py_BuildValue("s", strText.c_str()); -} - -PyObject* imeGetCodePage(PyObject* poSelf, PyObject* poArgs) { - return Py_BuildValue("i", CPythonIME::Instance().GetCodePage()); + std::string strText; + CPythonIME::Instance().GetText(strText); + return Py_BuildValue("s", strText.c_str()); } PyObject* imeGetCandidateCount(PyObject* poSelf, PyObject* poArgs) @@ -210,6 +201,25 @@ PyObject* imePasteTextFromClipBoard(PyObject* poSelf, PyObject* poArgs) return Py_BuildNone(); } +PyObject* imeSelectAll(PyObject* poSelf, PyObject* poArgs) +{ + CPythonIME::Instance().SelectAll(); + return Py_BuildNone(); +} + +PyObject* imeCopySelectionToClipboard(PyObject* poSelf, PyObject* poArgs) +{ + CPythonIME::Instance().CopySelectionToClipboard(); + return Py_BuildNone(); +} + +PyObject* imeCutSelection(PyObject* poSelf, PyObject* poArgs) +{ + CPythonIME::Instance().CopySelectionToClipboard(); + CPythonIME::Instance().DeleteSelection(); + return Py_BuildNone(); +} + PyObject* imeEnablePaste(PyObject* poSelf, PyObject* poArgs) { int iFlag; @@ -254,7 +264,6 @@ void initime() { "SetUserMax", imeSetUserMax, METH_VARARGS }, { "SetText", imeSetText, METH_VARARGS }, { "GetText", imeGetText, METH_VARARGS }, - { "GetCodePage", imeGetCodePage, METH_VARARGS }, { "GetCandidateCount", imeGetCandidateCount, METH_VARARGS }, { "GetCandidate", imeGetCandidate, METH_VARARGS }, { "GetCandidateSelection", imeGetCandidateSelection, METH_VARARGS }, @@ -280,6 +289,9 @@ void initime() { "PasteBackspace", imePasteBackspace, METH_VARARGS }, { "PasteReturn", imePasteReturn, METH_VARARGS }, { "PasteTextFromClipBoard", imePasteTextFromClipBoard, METH_VARARGS }, + { "SelectAll", imeSelectAll, METH_VARARGS }, + { "CutSelection", imeCutSelection, METH_VARARGS }, + { "CopySelection", imeCopySelectionToClipboard,METH_VARARGS }, { "EnablePaste", imeEnablePaste, METH_VARARGS }, { NULL, NULL, NULL }, diff --git a/src/UserInterface/PythonMiniMap.cpp b/src/UserInterface/PythonMiniMap.cpp index 2f123a7..7767832 100644 --- a/src/UserInterface/PythonMiniMap.cpp +++ b/src/UserInterface/PythonMiniMap.cpp @@ -7,6 +7,7 @@ #include "PythonMiniMap.h" #include "PythonBackground.h" #include "PythonCharacterManager.h" +#include "PythonNonPlayer.h" #include "PythonGuild.h" #include "AbstractPlayer.h" @@ -812,7 +813,7 @@ void CPythonMiniMap::__LoadAtlasMarkInfo() // LOCALE char szAtlasMarkInfoFileName[64+1]; - _snprintf(szAtlasMarkInfoFileName, sizeof(szAtlasMarkInfoFileName), "%s/map/%s_point.txt", LocaleService_GetLocalePath(), rkMap.GetName().c_str()); + _snprintf(szAtlasMarkInfoFileName, sizeof(szAtlasMarkInfoFileName), "%s/map/%s_point.txt", GetLocalePathCommon(), rkMap.GetName().c_str()); // END_OF_LOCALE CTokenVectorMap stTokenVectorMap; @@ -823,8 +824,6 @@ void CPythonMiniMap::__LoadAtlasMarkInfo() return; } - const std::string strType[TYPE_COUNT] = { "OPC", "OPCPVP", "OPCPVPSELF", "NPC", "MONSTER", "WARP", "WAYPOINT" }; - for (DWORD i = 0; i < stTokenVectorMap.size(); ++i) { char szMarkInfoName[32+1]; @@ -835,33 +834,43 @@ void CPythonMiniMap::__LoadAtlasMarkInfo() const CTokenVector & rVector = stTokenVectorMap[szMarkInfoName]; - const std::string & c_rstrType = rVector[0].c_str(); - const std::string & c_rstrPositionX = rVector[1].c_str(); - const std::string & c_rstrPositionY = rVector[2].c_str(); - const std::string & c_rstrText = rVector[3].c_str(); + const std::string& c_rstrPositionX = rVector[0].c_str(); + const std::string& c_rstrPositionY = rVector[1].c_str(); + const std::string& c_rstrVnum = rVector[2].c_str(); + const DWORD c_dwVnum = atoi(c_rstrVnum.c_str()); - TAtlasMarkInfo aAtlasMarkInfo; - - for ( int i = 0; i < TYPE_COUNT; ++i) + const CPythonNonPlayer::TMobTable* c_pMobTable = CPythonNonPlayer::Instance().GetTable(c_dwVnum); + if (c_pMobTable) { - if (0 == c_rstrType.compare(strType[i])) - aAtlasMarkInfo.m_byType = (BYTE)i; - } - aAtlasMarkInfo.m_fX = atof(c_rstrPositionX.c_str()); - aAtlasMarkInfo.m_fY = atof(c_rstrPositionY.c_str()); - aAtlasMarkInfo.m_strText = c_rstrText; + TAtlasMarkInfo aAtlasMarkInfo; + aAtlasMarkInfo.m_fX = atof(c_rstrPositionX.c_str()); + aAtlasMarkInfo.m_fY = atof(c_rstrPositionY.c_str()); + aAtlasMarkInfo.m_strText = c_pMobTable->szLocaleName; + if (c_pMobTable->bType == CActorInstance::TYPE_NPC) + aAtlasMarkInfo.m_byType = TYPE_NPC; + else if (c_pMobTable->bType == CActorInstance::TYPE_WARP) + { + aAtlasMarkInfo.m_byType = TYPE_WARP; + int iPos = aAtlasMarkInfo.m_strText.find(" "); + if (iPos >= 0) + aAtlasMarkInfo.m_strText[iPos] = 0; - aAtlasMarkInfo.m_fScreenX = aAtlasMarkInfo.m_fX / m_fAtlasMaxX * m_fAtlasImageSizeX - (float)m_WhiteMark.GetWidth() / 2.0f; - aAtlasMarkInfo.m_fScreenY = aAtlasMarkInfo.m_fY / m_fAtlasMaxY * m_fAtlasImageSizeY - (float)m_WhiteMark.GetHeight() / 2.0f; + } + else if (c_pMobTable->bType == CActorInstance::TYPE_STONE && c_dwVnum >= 20702 && c_dwVnum <= 20706) + aAtlasMarkInfo.m_byType = TYPE_NPC; - switch(aAtlasMarkInfo.m_byType) - { + aAtlasMarkInfo.m_fScreenX = aAtlasMarkInfo.m_fX / m_fAtlasMaxX * m_fAtlasImageSizeX - (float)m_WhiteMark.GetWidth() / 2.0f; + aAtlasMarkInfo.m_fScreenY = aAtlasMarkInfo.m_fY / m_fAtlasMaxY * m_fAtlasImageSizeY - (float)m_WhiteMark.GetHeight() / 2.0f; + + switch (aAtlasMarkInfo.m_byType) + { case TYPE_NPC: m_AtlasNPCInfoVector.push_back(aAtlasMarkInfo); break; case TYPE_WARP: m_AtlasWarpInfoVector.push_back(aAtlasMarkInfo); break; + } } } } diff --git a/src/UserInterface/PythonNetworkStreamModule.cpp b/src/UserInterface/PythonNetworkStreamModule.cpp index 1a5f69d..6e7d5a0 100644 --- a/src/UserInterface/PythonNetworkStreamModule.cpp +++ b/src/UserInterface/PythonNetworkStreamModule.cpp @@ -1,9 +1,7 @@ #include "StdAfx.h" #include "PythonNetworkStream.h" -//#include "PythonNetworkDatagram.h" #include "AccountConnector.h" #include "PythonGuild.h" -#include "Test.h" #include "AbstractPlayer.h" @@ -67,11 +65,6 @@ PyObject* netStartGame(PyObject* poSelf, PyObject* poArgs) return Py_BuildNone(); } -PyObject* netIsTest(PyObject* poSelf, PyObject* poArgs) -{ - return Py_BuildValue("i", __IS_TEST_SERVER_MODE__); -} - PyObject* netWarp(PyObject* poSelf, PyObject* poArgs) { int nX; @@ -1695,7 +1688,6 @@ void initnet() { "StartGame", netStartGame, METH_VARARGS }, { "Warp", netWarp, METH_VARARGS }, - { "IsTest", netIsTest, METH_VARARGS }, { "SetMarkServer", netSetMarkServer, METH_VARARGS }, { "IsChatInsultIn", netIsChatInsultIn, METH_VARARGS }, { "IsInsultIn", netIsInsultIn, METH_VARARGS }, diff --git a/src/UserInterface/PythonNetworkStreamPhaseGame.cpp b/src/UserInterface/PythonNetworkStreamPhaseGame.cpp index aa8ef49..f04ca00 100644 --- a/src/UserInterface/PythonNetworkStreamPhaseGame.cpp +++ b/src/UserInterface/PythonNetworkStreamPhaseGame.cpp @@ -1268,7 +1268,7 @@ void CPythonNetworkStream::__ConvertEmpireText(DWORD dwEmpireID, char* szText) bool CPythonNetworkStream::RecvChatPacket() { TPacketGCChat kChat; - char buf[1024 + 1]; + char buf[1024 + 1]; char line[1024 + 1]; if (!Recv(sizeof(kChat), &kChat)) @@ -1280,16 +1280,6 @@ bool CPythonNetworkStream::RecvChatPacket() return false; buf[uChatSize]='\0'; - - // 유럽 아랍 버전 처리 - // "이름: 내용" 입력을 "내용: 이름" 순서로 출력하기 위해 탭(0x08)을 넣음 - // 탭을 아랍어 기호로 처리해 (영어1) : (영어2) 로 입력되어도 (영어2) : (영어1) 로 출력하게 만든다 - if (LocaleService_IsEUROPE() && GetDefaultCodePage() == 1256) - { - char * p = strchr(buf, ':'); - if (p && p[1] == ' ') - p[1] = 0x08; - } if (kChat.type >= CHAT_TYPE_MAX_NUM) return true; @@ -4030,7 +4020,6 @@ bool CPythonNetworkStream::RecvWalkModePacket() bool CPythonNetworkStream::RecvChangeSkillGroupPacket() { TPacketGCChangeSkillGroup ChangeSkillGroup; - if (!Recv(sizeof(ChangeSkillGroup), &ChangeSkillGroup)) return false; @@ -4038,7 +4027,6 @@ bool CPythonNetworkStream::RecvChangeSkillGroupPacket() CPythonPlayer::Instance().NEW_ClearSkillData(); __RefreshCharacterWindow(); - return true; } @@ -4164,30 +4152,6 @@ bool CPythonNetworkStream::RecvNPCList() bool CPythonNetworkStream::__SendCRCReportPacket() { - /* - DWORD dwProcessCRC = 0; - DWORD dwFileCRC = 0; - CFilename exeFileName; - //LPCVOID c_pvBaseAddress = NULL; - - GetExeCRC(dwProcessCRC, dwFileCRC); - - CFilename strRootPackFileName = CEterPackManager::Instance().GetRootPacketFileName(); - strRootPackFileName.ChangeDosPath(); - - TPacketCGCRCReport kReportPacket; - - kReportPacket.header = HEADER_CG_CRC_REPORT; - kReportPacket.byPackMode = CEterPackManager::Instance().GetSearchMode(); - kReportPacket.dwBinaryCRC32 = dwFileCRC; - kReportPacket.dwProcessCRC32 = dwProcessCRC; - kReportPacket.dwRootPackCRC32 = GetFileCRC32(strRootPackFileName.c_str()); - - if (!Send(sizeof(kReportPacket), &kReportPacket)) - Tracef("SendClientReportPacket Error"); - - return SendSequence(); - */ return true; } @@ -4200,29 +4164,17 @@ bool CPythonNetworkStream::SendClientVersionPacket() filename = CFileNameHelper::NoPath(filename); CFileNameHelper::ChangeDosPath(filename); - if (LocaleService_IsEUROPE() && false == LocaleService_IsYMIR()) - { - TPacketCGClientVersion2 kVersionPacket; - kVersionPacket.header = HEADER_CG_CLIENT_VERSION2; - strncpy(kVersionPacket.filename, filename.c_str(), sizeof(kVersionPacket.filename)-1); - strncpy(kVersionPacket.timestamp, "1215955205", sizeof(kVersionPacket.timestamp)-1); // # python time.time 앞자리 - //strncpy(kVersionPacket.timestamp, __TIMESTAMP__, sizeof(kVersionPacket.timestamp)-1); // old_string_ver - //strncpy(kVersionPacket.timestamp, "1218055205", sizeof(kVersionPacket.timestamp)-1); // new_future - //strncpy(kVersionPacket.timestamp, "1214055205", sizeof(kVersionPacket.timestamp)-1); // old_past + TPacketCGClientVersion kVersionPacket{}; + kVersionPacket.header = HEADER_CG_CLIENT_VERSION; - if (!Send(sizeof(kVersionPacket), &kVersionPacket)) - Tracef("SendClientReportPacket Error"); - } - else - { - TPacketCGClientVersion kVersionPacket; - kVersionPacket.header = HEADER_CG_CLIENT_VERSION; - strncpy(kVersionPacket.filename, filename.c_str(), sizeof(kVersionPacket.filename)-1); - strncpy(kVersionPacket.timestamp, __TIMESTAMP__, sizeof(kVersionPacket.timestamp)-1); + strncpy(kVersionPacket.filename, filename.c_str(), sizeof(kVersionPacket.filename) - 1); + kVersionPacket.filename[sizeof(kVersionPacket.filename) - 1] = '\0'; + + snprintf(kVersionPacket.timestamp, sizeof(kVersionPacket.timestamp), "%u", 1215955205u); + + if (!Send(sizeof(kVersionPacket), &kVersionPacket)) + Tracef("SendClientReportPacket Error"); - if (!Send(sizeof(kVersionPacket), &kVersionPacket)) - Tracef("SendClientReportPacket Error"); - } return SendSequence(); } diff --git a/src/UserInterface/PythonNetworkStreamPhaseLoading.cpp b/src/UserInterface/PythonNetworkStreamPhaseLoading.cpp index a502fd1..8b47599 100644 --- a/src/UserInterface/PythonNetworkStreamPhaseLoading.cpp +++ b/src/UserInterface/PythonNetworkStreamPhaseLoading.cpp @@ -171,7 +171,7 @@ void CPythonNetworkStream::SetLoadingPhase() Tracen("## Network - Loading Phase ##"); Tracen(""); - m_strPhase = "Loading"; + m_strPhase = "Loading"; m_dwChangingPhaseTime = ELTimer_GetMSec(); m_phaseProcessFunc.Set(this, &CPythonNetworkStream::LoadingPhase); @@ -292,8 +292,8 @@ bool CPythonNetworkStream::RecvMainCharacter4_BGM_VOL() } -static std::string gs_fieldMusic_fileName; -static float gs_fieldMusic_volume = 1.0f / 5.0f * 0.1f; +static std::string gs_fieldMusic_fileName; +static float gs_fieldMusic_volume = 1.0f / 5.0f * 0.1f; void CPythonNetworkStream::__SetFieldMusicFileName(const char* musicName) { diff --git a/src/UserInterface/PythonNetworkStreamPhaseLogin.cpp b/src/UserInterface/PythonNetworkStreamPhaseLogin.cpp index 101c24e..76b53a8 100644 --- a/src/UserInterface/PythonNetworkStreamPhaseLogin.cpp +++ b/src/UserInterface/PythonNetworkStreamPhaseLogin.cpp @@ -1,7 +1,6 @@ #include "StdAfx.h" #include "PythonNetworkStream.h" #include "Packet.h" -#include "Test.h" #include "AccountConnector.h" // Login --------------------------------------------------------------------------- @@ -69,7 +68,7 @@ void CPythonNetworkStream::LoginPhase() void CPythonNetworkStream::SetLoginPhase() { - const char* key = LocaleService_GetSecurityKey(); + const char* key = GetSecurityKey(); #ifndef _IMPROVED_PACKET_ENCRYPTION_ SetSecurityMode(true, key); #endif @@ -81,7 +80,7 @@ void CPythonNetworkStream::SetLoginPhase() Tracen("## Network - Login Phase ##"); Tracen(""); - m_strPhase = "Login"; + m_strPhase = "Login"; m_phaseProcessFunc.Set(this, &CPythonNetworkStream::LoginPhase); m_phaseLeaveFunc.Set(this, &CPythonNetworkStream::__LeaveLoginPhase); @@ -133,8 +132,8 @@ bool CPythonNetworkStream::__RecvLoginSuccessPacket3() TPacketGCLoginSuccess3 kPacketLoginSuccess; if (!Recv(sizeof(kPacketLoginSuccess), &kPacketLoginSuccess)) - return false; - + return false; + for (int i = 0; i SKILL_MAX_LEVEL) + + + + + if (dwSkillLevel>SKILL_MAX_LEVEL) { m_playerStatus.aSkill[dwSlotIndex].fcurEfficientPercentage = 0.0f; m_playerStatus.aSkill[dwSlotIndex].fnextEfficientPercentage = 0.0f; TraceError("CPythonPlayer::SetSkillLevel(SlotIndex=%d, SkillLevel=%d)", dwSlotIndex, dwSkillLevel); - return; } @@ -1106,9 +1085,10 @@ void CPythonPlayer::SetSkillLevel_(DWORD dwSkillIndex, DWORD dwSkillGrade, DWORD { ResetSkillCoolTimeForSlot(dwSlotIndex); } - - m_playerStatus.aSkill[dwSlotIndex].fcurEfficientPercentage = LocaleService_GetSkillPower(dwSkillLevel) / 100.0f; - m_playerStatus.aSkill[dwSlotIndex].fnextEfficientPercentage = LocaleService_GetSkillPower(dwSkillLevel + 1) / 100.0f; + + m_playerStatus.aSkill[dwSlotIndex].fcurEfficientPercentage = GetSkillPower(dwSkillLevel)/100.0f; + m_playerStatus.aSkill[dwSlotIndex].fnextEfficientPercentage = GetSkillPower(dwSkillLevel+1)/100.0f; + } void CPythonPlayer::SetSkillCoolTime(DWORD dwSkillIndex) diff --git a/src/UserInterface/PythonPlayerModule.cpp b/src/UserInterface/PythonPlayerModule.cpp index fa767a4..6905fdb 100644 --- a/src/UserInterface/PythonPlayerModule.cpp +++ b/src/UserInterface/PythonPlayerModule.cpp @@ -2,6 +2,8 @@ #include "PythonPlayer.h" #include "PythonApplication.h" +#include + extern const DWORD c_iSkillIndex_Tongsol = 121; extern const DWORD c_iSkillIndex_Combo = 122; extern const DWORD c_iSkillIndex_Fishing = 123; @@ -1036,19 +1038,22 @@ PyObject * playerGetItemLink(PyObject * poSelf, PyObject * poArgs) switch (PyTuple_Size(poArgs)) { - case 1: - if (!PyTuple_GetInteger(poArgs, 0, &Cell.cell)) + case 1: + if (!PyTuple_GetInteger(poArgs, 0, &Cell.cell)) + return Py_BuildException(); + break; + + case 2: + if (!PyTuple_GetByte(poArgs, 0, &Cell.window_type)) + return Py_BuildException(); + if (!PyTuple_GetInteger(poArgs, 1, &Cell.cell)) + return Py_BuildException(); + break; + + default: return Py_BuildException(); - break; - case 2: - if (!PyTuple_GetByte(poArgs, 0, &Cell.window_type)) - return Py_BuildException(); - if (!PyTuple_GetInteger(poArgs, 1, &Cell.cell)) - return Py_BuildException(); - break; - default: - return Py_BuildException(); } + const TItemData * pPlayerItem = CPythonPlayer::Instance().GetItemData(Cell); CItemData * pItemData = NULL; char buf[1024]; @@ -1060,31 +1065,21 @@ PyObject * playerGetItemLink(PyObject * poSelf, PyObject * poArgs) bool isAttr = false; len = snprintf(itemlink, sizeof(itemlink), "item:%x:%x:%x:%x:%x", - pPlayerItem->vnum, pPlayerItem->flags, - pPlayerItem->alSockets[0], pPlayerItem->alSockets[1], pPlayerItem->alSockets[2]); + pPlayerItem->vnum, pPlayerItem->flags, pPlayerItem->alSockets[0], pPlayerItem->alSockets[1], pPlayerItem->alSockets[2]); for (int i = 0; i < ITEM_ATTRIBUTE_SLOT_MAX_NUM; ++i) if (pPlayerItem->aAttr[i].bType != 0) { - len += snprintf(itemlink + len, sizeof(itemlink) - len, ":%x:%d", - pPlayerItem->aAttr[i].bType, pPlayerItem->aAttr[i].sValue); + len += snprintf(itemlink + len, sizeof(itemlink) - len, ":%x:%d", pPlayerItem->aAttr[i].bType, pPlayerItem->aAttr[i].sValue); isAttr = true; } + const std::string wc_szName = pItemData->GetName(); - if( GetDefaultCodePage() == CP_ARABIC ) { - if (isAttr) - //"item:번호:플래그:소켓0:소켓1:소켓2" - snprintf(buf, sizeof(buf), " |h|r[%s]|cffffc700|H%s|h", pItemData->GetName(), itemlink); - else - snprintf(buf, sizeof(buf), " |h|r[%s]|cfff1e6c0|H%s|h", pItemData->GetName(), itemlink); - } else { - if (isAttr) - //"item:번호:플래그:소켓0:소켓1:소켓2" - snprintf(buf, sizeof(buf), "|cffffc700|H%s|h[%s]|h|r", itemlink, pItemData->GetName()); - else - snprintf(buf, sizeof(buf), "|cfff1e6c0|H%s|h[%s]|h|r", itemlink, pItemData->GetName()); - } + if (isAttr) + _snprintf_s(buf, sizeof(buf), _TRUNCATE, "|cffffc700|H%s|h[%s]|h|r", itemlink, wc_szName.c_str()); + else + _snprintf_s(buf, sizeof(buf), _TRUNCATE, "|cfff1e6c0|H%s|h[%s]|h|r", itemlink, wc_szName.c_str()); } else buf[0] = '\0'; diff --git a/src/UserInterface/PythonSkill.cpp b/src/UserInterface/PythonSkill.cpp index 53c0e21..81dfc50 100644 --- a/src/UserInterface/PythonSkill.cpp +++ b/src/UserInterface/PythonSkill.cpp @@ -164,7 +164,7 @@ bool CPythonSkill::RegisterSkillTable(const char * c_szFileName) rSkillData.byMaxLevel = maxLevel; } - const std::string & c_strLevelLimit = TokenVector[TABLE_TOKEN_TYPE_LEVEL_LIMIT]; + const std::string & c_strLevelLimit = TokenVector[TABLE_TOKEN_TYPE_LEVEL_LIMIT]; if (!c_strLevelLimit.empty()) { int levelLimit = atoi(c_strLevelLimit.c_str()); @@ -174,7 +174,7 @@ bool CPythonSkill::RegisterSkillTable(const char * c_szFileName) const std::string & c_strPointPoly = TokenVector[TABLE_TOKEN_TYPE_POINT_POLY]; // OVERWRITE_SKILLPROTO_POLY - bool USE_SKILL_PROTO = LocaleService_IsCHEONMA() ? false : true; + bool USE_SKILL_PROTO = true; switch (iVnum) { @@ -192,12 +192,12 @@ bool CPythonSkill::RegisterSkillTable(const char * c_szFileName) USE_SKILL_PROTO = false; break; } - + if (!rSkillData.AffectDataVector.empty() && USE_SKILL_PROTO) - { + { TAffectData& affect = rSkillData.AffectDataVector[0]; - if (strstr(c_strPointPoly.c_str(), "atk") != NULL || + if (strstr(c_strPointPoly.c_str(), "atk") != NULL || strstr(c_strPointPoly.c_str(), "mwep") != NULL || strstr(c_strPointPoly.c_str(), "number") != NULL) { @@ -222,22 +222,21 @@ bool CPythonSkill::RegisterSkillTable(const char * c_szFileName) string_replace_word(src_poly_atk.c_str(), src_poly_atk.length(), "mwep", 4, "maxmwep", 7, affect.strAffectMaxFormula); // END_OF_MAX - + switch (iVnum) { - case 1: // 삼연참 + case 1: affect.strAffectMinFormula += "* 3"; affect.strAffectMaxFormula += "* 3"; - break; + break; } - } else { affect.strAffectMinFormula = c_strPointPoly; affect.strAffectMaxFormula = ""; - } - } + } + } // END_OF_OVERWRITE_SKILLPROTO_POLY } @@ -609,7 +608,7 @@ bool CPythonSkill::RegisterSkill(DWORD dwSkillIndex, const char * c_szFileName) { char szName[256]; - sprintf(szName, "%dname", LocaleService_GetCodePage()); + strncpy_s(szName, "name", _TRUNCATE); if (!TextFileLoader.GetTokenString(szName, &SkillData.strName)) if (!TextFileLoader.GetTokenString("name", &SkillData.strName)) { @@ -620,7 +619,7 @@ bool CPythonSkill::RegisterSkill(DWORD dwSkillIndex, const char * c_szFileName) { char szName[256]; - sprintf(szName, "%ddescription", LocaleService_GetCodePage()); + strncpy_s(szName, "description", _TRUNCATE); if (!TextFileLoader.GetTokenString(szName, &SkillData.strDescription)) TextFileLoader.GetTokenString("description", &SkillData.strDescription); } @@ -635,7 +634,7 @@ bool CPythonSkill::RegisterSkill(DWORD dwSkillIndex, const char * c_szFileName) CTokenVector * pConditionDataVector; char szConditionData[256]; - sprintf(szConditionData, "%dconditiondata", LocaleService_GetCodePage()); + strncpy_s(szConditionData, "conditiondata", _TRUNCATE); bool isConditionData=true; if (!TextFileLoader.GetTokenVector(szConditionData, &pConditionDataVector)) @@ -658,7 +657,7 @@ bool CPythonSkill::RegisterSkill(DWORD dwSkillIndex, const char * c_szFileName) CTokenVector * pAffectDataVector; char szAffectData[256]; - sprintf(szAffectData, "%daffectdata", LocaleService_GetCodePage()); + strncpy_s(szAffectData, "affectdata", _TRUNCATE); bool isAffectData=true; if (!TextFileLoader.GetTokenVector(szAffectData, &pAffectDataVector)) @@ -683,7 +682,7 @@ bool CPythonSkill::RegisterSkill(DWORD dwSkillIndex, const char * c_szFileName) CTokenVector * pGradeDataVector; char szGradeData[256]; - sprintf(szGradeData, "%dgradedata", LocaleService_GetCodePage()); + strncpy_s(szGradeData, "gradedata", _TRUNCATE); if (TextFileLoader.GetTokenVector(szGradeData, &pGradeDataVector)) { @@ -1280,68 +1279,59 @@ float CPythonSkill::SSkillData::ProcessFormula(CPoly * pPoly, float fSkillLevel, return pPoly->Eval(); } -const char * CPythonSkill::SSkillData::GetAffectDescription(DWORD dwIndex, float fSkillLevel) +static void ReplaceFirst(std::string& s, const char* needle, const std::string& repl) +{ + size_t pos = s.find(needle); + if (pos != std::string::npos) + s.replace(pos, strlen(needle), repl); +} + +const char* CPythonSkill::SSkillData::GetAffectDescription(DWORD dwIndex, float fSkillLevel) { if (dwIndex >= AffectDataVector.size()) return NULL; - const std::string & c_rstrAffectDescription = AffectDataVector[dwIndex].strAffectDescription; - const std::string & c_rstrAffectMinFormula = AffectDataVector[dwIndex].strAffectMinFormula; - const std::string & c_rstrAffectMaxFormula = AffectDataVector[dwIndex].strAffectMaxFormula; + const std::string& desc = AffectDataVector[dwIndex].strAffectDescription; + const std::string& minF = AffectDataVector[dwIndex].strAffectMinFormula; + const std::string& maxF = AffectDataVector[dwIndex].strAffectMaxFormula; CPoly minPoly; CPoly maxPoly; - minPoly.SetStr(c_rstrAffectMinFormula.c_str()); - maxPoly.SetStr(c_rstrAffectMaxFormula.c_str()); - - // OVERWRITE_SKILLPROTO_POLY + minPoly.SetStr(minF.c_str()); + maxPoly.SetStr(maxF.c_str()); + float fMinValue = ProcessFormula(&minPoly, fSkillLevel); float fMaxValue = ProcessFormula(&maxPoly, fSkillLevel); - if (fMinValue < 0.0) - fMinValue = - fMinValue; - if (fMaxValue < 0.0) - fMaxValue = - fMaxValue; + if (fMinValue < 0.0f) fMinValue = -fMinValue; + if (fMaxValue < 0.0f) fMaxValue = -fMaxValue; - if (CP_ARABIC == ::GetDefaultCodePage()) + const bool wantsInt = (desc.find("%.0f") != std::string::npos); + if (wantsInt) { - // #0000870: [M2AE] 한국어 모드일때 특정 아랍어 문장에서 크래쉬 발생 - static std::string strDescription; - strDescription = c_rstrAffectDescription; - int first = strDescription.find("%.0f"); - if (first >= 0) - { - fMinValue = floorf(fMinValue); + fMinValue = floorf(fMinValue); + fMaxValue = floorf(fMaxValue); + } - char szMinValue[256]; - _snprintf(szMinValue, sizeof(szMinValue), "%.0f", fMinValue); - strDescription.replace(first, 4, szMinValue); - - int second = strDescription.find("%.0f", first); - if (second >= 0) - { - fMaxValue = floorf(fMaxValue); - - char szMaxValue[256]; - _snprintf(szMaxValue, sizeof(szMaxValue), "%.0f", fMaxValue); - strDescription.replace(second, 4, szMaxValue); - } - } - return strDescription.c_str(); + char szMin[64], szMax[64]; + if (wantsInt) + { + _snprintf(szMin, sizeof(szMin), "%.0f", fMinValue); + _snprintf(szMax, sizeof(szMax), "%.0f", fMaxValue); } else { - if (strstr(c_rstrAffectDescription.c_str(), "%.0f")) - { - fMinValue = floorf(fMinValue); - fMaxValue = floorf(fMaxValue); - } - - static char szDescription[64+1]; - _snprintf(szDescription, sizeof(szDescription), c_rstrAffectDescription.c_str(), fMinValue, fMaxValue); - - return szDescription; + _snprintf(szMin, sizeof(szMin), "%.2f", fMinValue); + _snprintf(szMax, sizeof(szMax), "%.2f", fMaxValue); } + + static std::string out; + out = desc; + + ReplaceFirst(out, "%.0f", szMin); + ReplaceFirst(out, "%.0f", szMax); + + return out.c_str(); } DWORD CPythonSkill::SSkillData::GetSkillCoolTime(float fSkillPoint) diff --git a/src/UserInterface/PythonTextTail.cpp b/src/UserInterface/PythonTextTail.cpp index d86c03a..618cd54 100644 --- a/src/UserInterface/PythonTextTail.cpp +++ b/src/UserInterface/PythonTextTail.cpp @@ -10,6 +10,8 @@ #include "Locale.h" #include "MarkManager.h" +#include + const D3DXCOLOR c_TextTail_Player_Color = D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f); const D3DXCOLOR c_TextTail_Monster_Color = D3DXCOLOR(1.0f, 0.0f, 0.0f, 1.0f); const D3DXCOLOR c_TextTail_Item_Color = D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f); @@ -211,113 +213,46 @@ void CPythonTextTail::ArrangeTextTail() for (itor = m_CharacterTextTailList.begin(); itor != m_CharacterTextTailList.end(); ++itor) { - TTextTail * pTextTail = *itor; + TTextTail* pTextTail = *itor; - float fxAdd = 0.0f; - - // Mark 위치 업데이트 - CGraphicMarkInstance * pMarkInstance = pTextTail->pMarkInstance; - CGraphicTextInstance * pGuildNameInstance = pTextTail->pGuildNameTextInstance; + CGraphicMarkInstance* pMarkInstance = pTextTail->pMarkInstance; + CGraphicTextInstance* pGuildNameInstance = pTextTail->pGuildNameTextInstance; if (pMarkInstance && pGuildNameInstance) { int iWidth, iHeight; - int iImageHalfSize = pMarkInstance->GetWidth()/2 + c_fxMarkPosition; + int iImageHalfSize = pMarkInstance->GetWidth() / 2 + c_fxMarkPosition; pGuildNameInstance->GetTextSize(&iWidth, &iHeight); - pMarkInstance->SetPosition(pTextTail->x - iWidth/2 - iImageHalfSize, pTextTail->y - c_fyMarkPosition); + pMarkInstance->SetPosition(pTextTail->x - iWidth / 2 - iImageHalfSize, pTextTail->y - c_fyMarkPosition); pGuildNameInstance->SetPosition(pTextTail->x + iImageHalfSize, pTextTail->y - c_fyGuildNamePosition, pTextTail->z); pGuildNameInstance->Update(); } - int iNameWidth, iNameHeight; + // Title & Name refactor + int iNameWidth = 0, iNameHeight = 0; pTextTail->pTextInstance->GetTextSize(&iNameWidth, &iNameHeight); - // Title 위치 업데이트 - CGraphicTextInstance * pTitle = pTextTail->pTitleTextInstance; - if (pTitle) - { - int iTitleWidth, iTitleHeight; - pTitle->GetTextSize(&iTitleWidth, &iTitleHeight); + const float fxAdd = 4.0f; - fxAdd = 8.0f; + float nameShift = 0.0f; + if (pTextTail->pTitleTextInstance) + nameShift = 1.0f; + else if (pTextTail->pLevelTextInstance) + nameShift = 5.0f; - if (LocaleService_IsEUROPE()) // 독일어는 명칭이 길어 오른정렬 - { - if( GetDefaultCodePage() == CP_ARABIC ) - { - pTitle->SetPosition(pTextTail->x - (iNameWidth / 2) - iTitleWidth - 4.0f, pTextTail->y, pTextTail->z); - } - else - { - // pTitle->SetPosition(pTextTail->x - (iNameWidth / 2), pTextTail->y, pTextTail->z); - pTitle->SetPosition(pTextTail->x - ((iNameWidth / 2)-4.0f), pTextTail->y, pTextTail->z); // Space between level and alignment - } - } - else - { - pTitle->SetPosition(pTextTail->x - (iNameWidth / 2) - fxAdd, pTextTail->y, pTextTail->z); - } - pTitle->Update(); + float nameX = floorf((pTextTail->x + nameShift) + 0.5f); + float nameY = floorf(pTextTail->y + 0.5f); - // Level 위치 업데이트 - CGraphicTextInstance * pLevel = pTextTail->pLevelTextInstance; - if (pLevel) - { - int iLevelWidth, iLevelHeight; - pLevel->GetTextSize(&iLevelWidth, &iLevelHeight); - - if (LocaleService_IsEUROPE()) // 독일어는 명칭이 길어 오른정렬 - { - if( GetDefaultCodePage() == CP_ARABIC ) - { - pLevel->SetPosition(pTextTail->x - (iNameWidth / 2) - iLevelWidth - iTitleWidth - 8.0f, pTextTail->y, pTextTail->z); - } - else - { - pLevel->SetPosition(pTextTail->x - (iNameWidth / 2) - iTitleWidth, pTextTail->y, pTextTail->z); - } - } - else - { - pLevel->SetPosition(pTextTail->x - (iNameWidth / 2) - fxAdd - iTitleWidth, pTextTail->y, pTextTail->z); - } + const float nameLeftEdge = pTextTail->x - (iNameWidth / 2.0f); + float cursor = nameLeftEdge - fxAdd; - pLevel->Update(); - } - } - else - { - fxAdd = 4.0f; + // [Level] [Title] Name - LTR. + // It can be set for RTL - Name [Title] [Level] by adding the required check. + cursor = TextTailBiDi(pTextTail->pTitleTextInstance, cursor, nameY, pTextTail->z, fxAdd, EPlaceDir::Left); + cursor = TextTailBiDi(pTextTail->pLevelTextInstance, cursor, nameY, pTextTail->z, fxAdd, EPlaceDir::Left); - // Level 위치 업데이트 - CGraphicTextInstance * pLevel = pTextTail->pLevelTextInstance; - if (pLevel) - { - int iLevelWidth, iLevelHeight; - pLevel->GetTextSize(&iLevelWidth, &iLevelHeight); - - if (LocaleService_IsEUROPE()) // 독일어는 명칭이 길어 오른정렬 - { - if( GetDefaultCodePage() == CP_ARABIC ) - { - pLevel->SetPosition(pTextTail->x - (iNameWidth / 2) - iLevelWidth - 4.0f, pTextTail->y, pTextTail->z); - } - else - { - pLevel->SetPosition(pTextTail->x - (iNameWidth / 2), pTextTail->y, pTextTail->z); - } - } - else - { - pLevel->SetPosition(pTextTail->x - (iNameWidth / 2) - fxAdd, pTextTail->y, pTextTail->z); - } - - pLevel->Update(); - } - } - pTextTail->pTextInstance->SetColor(pTextTail->Color.r, pTextTail->Color.g, pTextTail->Color.b); - pTextTail->pTextInstance->SetPosition(pTextTail->x + fxAdd, pTextTail->y, pTextTail->z); + pTextTail->pTextInstance->SetPosition(nameX, nameY, pTextTail->z); pTextTail->pTextInstance->Update(); } @@ -721,10 +656,6 @@ void CPythonTextTail::SetItemTextTailOwner(DWORD dwVID, const char * c_szName) } std::string strName = c_szName; - static const std::string & strOwnership = ApplicationStringTable_GetString(IDS_POSSESSIVE_MORPHENE) == "" ? "'s" : ApplicationStringTable_GetString(IDS_POSSESSIVE_MORPHENE); - strName += strOwnership; - - pTextTail->pOwnerTextInstance->SetTextPointer(ms_pFont); pTextTail->pOwnerTextInstance->SetHorizonalAlign(CGraphicTextInstance::HORIZONTAL_ALIGN_CENTER); pTextTail->pOwnerTextInstance->SetValue(strName.c_str()); @@ -733,10 +664,10 @@ void CPythonTextTail::SetItemTextTailOwner(DWORD dwVID, const char * c_szName) int xOwnerSize, yOwnerSize; pTextTail->pOwnerTextInstance->GetTextSize(&xOwnerSize, &yOwnerSize); - pTextTail->yStart = -2.0f; - pTextTail->yEnd += float(yOwnerSize + 4); - pTextTail->xStart = fMIN(pTextTail->xStart, float(-xOwnerSize / 2 - 1)); - pTextTail->xEnd = fMAX(pTextTail->xEnd, float(xOwnerSize / 2 + 1)); + pTextTail->yStart = -2.0f; + pTextTail->yEnd += float(yOwnerSize + 4); + pTextTail->xStart = fMIN(pTextTail->xStart, float(-xOwnerSize / 2 - 1)); + pTextTail->xEnd = fMAX(pTextTail->xEnd, float(xOwnerSize / 2 + 1)); } else { @@ -748,10 +679,10 @@ void CPythonTextTail::SetItemTextTailOwner(DWORD dwVID, const char * c_szName) int xSize, ySize; pTextTail->pTextInstance->GetTextSize(&xSize, &ySize); - pTextTail->xStart = (float) (-xSize / 2 - 2); - pTextTail->yStart = -2.0f; - pTextTail->xEnd = (float) (xSize / 2 + 2); - pTextTail->yEnd = (float) ySize; + pTextTail->xStart = (float) (-xSize / 2 - 2); + pTextTail->yStart = -2.0f; + pTextTail->xEnd = (float) (xSize / 2 + 2); + pTextTail->yEnd = (float) ySize; } } @@ -906,11 +837,7 @@ void CPythonTextTail::AttachTitle(DWORD dwVID, const char * c_szName, const D3DX prTitle = CGraphicTextInstance::New(); prTitle->SetTextPointer(ms_pFont); prTitle->SetOutline(true); - - if (LocaleService_IsEUROPE()) - prTitle->SetHorizonalAlign(CGraphicTextInstance::HORIZONTAL_ALIGN_RIGHT); - else - prTitle->SetHorizonalAlign(CGraphicTextInstance::HORIZONTAL_ALIGN_CENTER); + prTitle->SetHorizonalAlign(CGraphicTextInstance::HORIZONTAL_ALIGN_LEFT); prTitle->SetVerticalAlign(CGraphicTextInstance::VERTICAL_ALIGN_BOTTOM); } @@ -960,7 +887,7 @@ void CPythonTextTail::AttachLevel(DWORD dwVID, const char * c_szText, const D3DX prLevel->SetTextPointer(ms_pFont); prLevel->SetOutline(true); - prLevel->SetHorizonalAlign(CGraphicTextInstance::HORIZONTAL_ALIGN_RIGHT); + prLevel->SetHorizonalAlign(CGraphicTextInstance::HORIZONTAL_ALIGN_LEFT); prLevel->SetVerticalAlign(CGraphicTextInstance::VERTICAL_ALIGN_BOTTOM); } diff --git a/src/UserInterface/StdAfx.h b/src/UserInterface/StdAfx.h index ca1457b..2f0a3e7 100644 --- a/src/UserInterface/StdAfx.h +++ b/src/UserInterface/StdAfx.h @@ -23,23 +23,16 @@ #endif #include - #include "Locale.h" - #include "GameType.h" -extern DWORD __DEFAULT_CODE_PAGE__; -#define APP_NAME "Metin 2" +#define APP_NAME "Metin 2" enum { - POINT_MAX_NUM = 255, + POINT_MAX_NUM = 255, CHARACTER_NAME_MAX_LEN = 24, -#if defined(LOCALE_SERVICE_JAPAN) - PLAYER_NAME_MAX_LEN = 16, -#else PLAYER_NAME_MAX_LEN = 12, -#endif }; void initudp(); @@ -73,11 +66,3 @@ void initquest(); void initsafebox(); void initguild(); void initMessenger(); - -extern const std::string& ApplicationStringTable_GetString(DWORD dwID); -extern const std::string& ApplicationStringTable_GetString(DWORD dwID, LPCSTR szKey); - -extern const char* ApplicationStringTable_GetStringz(DWORD dwID); -extern const char* ApplicationStringTable_GetStringz(DWORD dwID, LPCSTR szKey); - -extern void ApplicationSetErrorString(const char* szErrorString); diff --git a/src/UserInterface/Test.h b/src/UserInterface/Test.h deleted file mode 100644 index ab6c8c4..0000000 --- a/src/UserInterface/Test.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -extern bool __IS_TEST_SERVER_MODE__; diff --git a/src/UserInterface/UserInterface.cpp b/src/UserInterface/UserInterface.cpp index 3e77961..f7e18f8 100644 --- a/src/UserInterface/UserInterface.cpp +++ b/src/UserInterface/UserInterface.cpp @@ -13,21 +13,24 @@ #include "EterBase/lzo.h" #include "PackLib/PackManager.h" + #include #include -extern "C" { -extern int _fltused; -volatile int _AVOID_FLOATING_POINT_LIBRARY_BUG = _fltused; -}; -extern "C" { FILE __iob_func[3] = { *stdin,*stdout,*stderr }; } +#include +#include + +extern "C" { + extern int _fltused; + FILE __iob_func[3] = { *stdin, *stdout, *stderr }; + volatile int _AVOID_FLOATING_POINT_LIBRARY_BUG = _fltused; + __declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001; + __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; +}; + #pragma comment(linker, "/NODEFAULTLIB:libci.lib") -#include -bool __IS_TEST_SERVER_MODE__=false; - -extern bool SetDefaultCodePage(DWORD codePage); - +int Setup(LPSTR lpCmdLine); static const char * sc_apszPythonLibraryFilenames[] = { "UserDict.pyc", @@ -44,98 +47,30 @@ static const char * sc_apszPythonLibraryFilenames[] = "\n", }; -char gs_szErrorString[512] = ""; - -void ApplicationSetErrorString(const char* szErrorString) -{ - strcpy(gs_szErrorString, szErrorString); -} - bool CheckPythonLibraryFilenames() { for (int i = 0; *sc_apszPythonLibraryFilenames[i] != '\n'; ++i) { - std::string stFilename = "lib\\"; - stFilename += sc_apszPythonLibraryFilenames[i]; + std::string stFilenameUtf8 = "lib\\"; + stFilenameUtf8 += sc_apszPythonLibraryFilenames[i]; - if (_access(stFilename.c_str(), 0) != 0) + std::wstring stFilenameW = Utf8ToWide(stFilenameUtf8); + + // Check existence + if (GetFileAttributesW(stFilenameW.c_str()) == INVALID_FILE_ATTRIBUTES) { return false; } - MoveFile(stFilename.c_str(), stFilename.c_str()); + // Keep original behavior (forces Windows path normalization) + MoveFileW(stFilenameW.c_str(), stFilenameW.c_str()); } return true; } -struct ApplicationStringTable -{ - HINSTANCE m_hInstance; - std::map m_kMap_dwID_stLocale; -} gs_kAppStrTable; - -void ApplicationStringTable_Initialize(HINSTANCE hInstance) -{ - gs_kAppStrTable.m_hInstance=hInstance; -} - -const std::string& ApplicationStringTable_GetString(DWORD dwID, LPCSTR szKey) -{ - char szBuffer[512]; - char szIniFileName[256]; - char szLocale[256]; - - ::GetCurrentDirectory(sizeof(szIniFileName), szIniFileName); - if(szIniFileName[lstrlen(szIniFileName)-1] != '\\') - strcat(szIniFileName, "\\"); - strcat(szIniFileName, "metin2client.dat"); - - strcpy(szLocale, LocaleService_GetLocalePath()); - if(strnicmp(szLocale, "locale/", strlen("locale/")) == 0) - strcpy(szLocale, LocaleService_GetLocalePath() + strlen("locale/")); - ::GetPrivateProfileString(szLocale, szKey, NULL, szBuffer, sizeof(szBuffer)-1, szIniFileName); - if(szBuffer[0] == '\0') - LoadString(gs_kAppStrTable.m_hInstance, dwID, szBuffer, sizeof(szBuffer)-1); - if(szBuffer[0] == '\0') - ::GetPrivateProfileString("en", szKey, NULL, szBuffer, sizeof(szBuffer)-1, szIniFileName); - if(szBuffer[0] == '\0') - strcpy(szBuffer, szKey); - - std::string& rstLocale=gs_kAppStrTable.m_kMap_dwID_stLocale[dwID]; - rstLocale=szBuffer; - - return rstLocale; -} - -const std::string& ApplicationStringTable_GetString(DWORD dwID) -{ - char szBuffer[512]; - - LoadString(gs_kAppStrTable.m_hInstance, dwID, szBuffer, sizeof(szBuffer)-1); - std::string& rstLocale=gs_kAppStrTable.m_kMap_dwID_stLocale[dwID]; - rstLocale=szBuffer; - - return rstLocale; -} - -const char* ApplicationStringTable_GetStringz(DWORD dwID, LPCSTR szKey) -{ - return ApplicationStringTable_GetString(dwID, szKey).c_str(); -} - -const char* ApplicationStringTable_GetStringz(DWORD dwID) -{ - return ApplicationStringTable_GetString(dwID).c_str(); -} - -//////////////////////////////////////////// - -int Setup(LPSTR lpCmdLine); // Internal function forward - bool PackInitialize(const char * c_pszFolder) { - NANOBEGIN if (_access(c_pszFolder, 0) != 0) return false; @@ -225,28 +160,7 @@ bool PackInitialize(const char * c_pszFolder) "sound_m", "sound2", "bgm", - "locale_ca", - "locale_ae", - "locale_de", - "locale_es", - "locale_fr", - "locale_gr", - "locale_it", - "locale_nl", - "locale_pl", - "locale_pt", - "locale_tr", - "locale_uk", - "locale_bg", - "locale_en", - "locale_mx", - "locale_ro", - "locale_ru", - "locale_dk", - "locale_cz", - "locale_hu", - "locale_us", - "locale_pa", + "locale", "uiscript", "ETC", "uiloading", @@ -257,7 +171,6 @@ bool PackInitialize(const char * c_pszFolder) CPackManager::instance().AddPack(std::format("{}/{}.pck", c_pszFolder, packFileName)); } - NANOEND return true; } @@ -270,7 +183,7 @@ bool RunMainScript(CPythonLauncher& pyLauncher, const char* lpCmdLine) initgrpImage(); initgrpText(); initwndMgr(); - ///////////////////////////////////////////// + initudp(); initapp(); initsystem(); @@ -298,113 +211,36 @@ bool RunMainScript(CPythonLauncher& pyLauncher, const char* lpCmdLine) initsafebox(); initguild(); initServerStateChecker(); + std::string stRegisterDebugFlag; - NANOBEGIN + #ifdef _DISTRIBUTE + stRegisterDebugFlag = "__DEBUG__ = 0"; + #else + stRegisterDebugFlag = "__DEBUG__ = 1"; + #endif - // RegisterDebugFlag + if (!pyLauncher.RunLine(stRegisterDebugFlag.c_str())) { - std::string stRegisterDebugFlag; - -#ifdef _DISTRIBUTE - stRegisterDebugFlag ="__DEBUG__ = 0"; -#else - stRegisterDebugFlag ="__DEBUG__ = 1"; -#endif - - if (!pyLauncher.RunLine(stRegisterDebugFlag.c_str())) - { - TraceError("RegisterDebugFlag Error"); - return false; - } + TraceError("RegisterDebugFlag Error"); + return false; } - // RegisterCommandLine + if (!pyLauncher.RunFile("system.py")) { - std::string stRegisterCmdLine; - - const char * loginMark = "-cs"; - const char * loginMark_NonEncode = "-ncs"; - const char * seperator = " "; - - std::string stCmdLine; - const int CmdSize = 3; - std::vector stVec; - SplitLine(lpCmdLine,seperator,&stVec); - if (CmdSize == stVec.size() && stVec[0]==loginMark) - { - char buf[MAX_PATH]; //TODO 아래 함수 string 형태로 수정 - base64_decode(stVec[2].c_str(),buf); - stVec[2] = buf; - string_join(seperator,stVec,&stCmdLine); - } - else if (CmdSize <= stVec.size() && stVec[0]==loginMark_NonEncode) - { - stVec[0] = loginMark; - string_join(" ",stVec,&stCmdLine); - } - else - stCmdLine = lpCmdLine; - - stRegisterCmdLine ="__COMMAND_LINE__ = "; - stRegisterCmdLine+='"'; - stRegisterCmdLine+=stCmdLine; - stRegisterCmdLine+='"'; - - const CHAR* c_szRegisterCmdLine=stRegisterCmdLine.c_str(); - if (!pyLauncher.RunLine(c_szRegisterCmdLine)) - { - TraceError("RegisterCommandLine Error"); - return false; - } - } - { - std::vector stVec; - SplitLine(lpCmdLine," " ,&stVec); - - if (stVec.size() != 0 && "--pause-before-create-window" == stVec[0]) - { - system("pause"); - } - if (!pyLauncher.RunFile("system.py")) - { - TraceError("RunMain Error"); - return false; - } + TraceError("RunMain Error"); + return false; } - NANOEND return true; } -bool Main(HINSTANCE hInstance, LPSTR lpCmdLine) +static bool Main(HINSTANCE hInstance, LPSTR lpCmdLine) { -#ifdef LOCALE_SERVICE_YMIR - extern bool g_isScreenShotKey; - g_isScreenShotKey = true; -#endif - - DWORD dwRandSeed=time(NULL)+DWORD(GetCurrentProcess()); + DWORD dwRandSeed = (DWORD)time(NULL) ^ GetCurrentProcessId() ^ GetTickCount(); srandom(dwRandSeed); srand(random()); - SetLogLevel(1); -#ifdef LOCALE_SERVICE_VIETNAM_MILD - extern BOOL USE_VIETNAM_CONVERT_WEAPON_VNUM; - USE_VIETNAM_CONVERT_WEAPON_VNUM = true; -#endif - - if (_access("perf_game_update.txt", 0)==0) - { - DeleteFile("perf_game_update.txt"); - } - - if (_access("newpatch.exe", 0)==0) - { - system("patchupdater.exe"); - return false; - } - if (!Setup(lpCmdLine)) return false; @@ -415,8 +251,8 @@ bool Main(HINSTANCE hInstance, LPSTR lpCmdLine) OpenLogFile(false); // false == uses syserr.txt only #endif - static CLZO lzo; - CPackManager packMgr; + static CLZO lzo; + CPackManager packMgr; if (!PackInitialize("pack")) { @@ -424,57 +260,22 @@ bool Main(HINSTANCE hInstance, LPSTR lpCmdLine) return false; } - if(LocaleService_LoadGlobal(hInstance)) - SetDefaultCodePage(LocaleService_GetCodePage()); + auto app = new CPythonApplication; + app->Initialize (hInstance); + CPythonLauncher pyLauncher; - CPythonApplication * app = new CPythonApplication; - - app->Initialize(hInstance); - - bool ret=false; + if (pyLauncher.Create()) { - CPythonLauncher pyLauncher; - CPythonExceptionSender pyExceptionSender; - SetExceptionSender(&pyExceptionSender); - - if (pyLauncher.Create()) - { - ret=RunMainScript(pyLauncher, lpCmdLine); //게임 실행중엔 함수가 끝나지 않는다. - } - - //ProcessScanner_ReleaseQuitEvent(); - - //게임 종료시. - app->Clear(); - - timeEndPeriod(1); - pyLauncher.Clear(); + RunMainScript (pyLauncher, lpCmdLine); } + app->Clear(); + timeEndPeriod (1); + pyLauncher.Clear(); + app->Destroy(); delete app; - - return ret; -} - -HANDLE CreateMetin2GameMutex() -{ - SECURITY_ATTRIBUTES sa; - ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES)); - sa.nLength = sizeof(sa); - sa.lpSecurityDescriptor = NULL; - sa.bInheritHandle = FALSE; - - return CreateMutex(&sa, FALSE, "Metin2GameMutex"); -} - -void DestroyMetin2GameMutex(HANDLE hMutex) -{ - if (hMutex) - { - ReleaseMutex(hMutex); - hMutex = NULL; - } + return 0; } void __ErrorPythonLibraryIsNotExist() @@ -482,100 +283,12 @@ void __ErrorPythonLibraryIsNotExist() LogBoxf("FATAL ERROR!! Python Library file not exist!"); } -bool __IsTimeStampOption(LPSTR lpCmdLine) -{ - const char* TIMESTAMP = "/timestamp"; - return (strncmp(lpCmdLine, TIMESTAMP, strlen(TIMESTAMP))==0); -} - -void __PrintTimeStamp() -{ -#ifdef _DEBUG - if (__IS_TEST_SERVER_MODE__) - LogBoxf("METIN2 BINARY TEST DEBUG VERSION %s ( MS C++ %d Compiled )", __TIMESTAMP__, _MSC_VER); - else - LogBoxf("METIN2 BINARY DEBUG VERSION %s ( MS C++ %d Compiled )", __TIMESTAMP__, _MSC_VER); - -#else - if (__IS_TEST_SERVER_MODE__) - LogBoxf("METIN2 BINARY TEST VERSION %s ( MS C++ %d Compiled )", __TIMESTAMP__, _MSC_VER); - else - LogBoxf("METIN2 BINARY DISTRIBUTE VERSION %s ( MS C++ %d Compiled )", __TIMESTAMP__, _MSC_VER); -#endif -} - -bool __IsLocaleOption(LPSTR lpCmdLine) -{ - return (strcmp(lpCmdLine, "--locale") == 0); -} - -bool __IsLocaleVersion(LPSTR lpCmdLine) -{ - return (strcmp(lpCmdLine, "--perforce-revision") == 0); -} - int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { -#ifdef _DEBUG - _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_CHECK_CRT_DF | _CRTDBG_LEAK_CHECK_DF ); - //_CrtSetBreakAlloc( 110247 ); -#endif + LoadConfig("config/locale.cfg"); - ApplicationStringTable_Initialize(hInstance); - - LocaleService_LoadConfig("config/locale.cfg"); - SetDefaultCodePage(LocaleService_GetCodePage()); - - bool bQuit = false; int nArgc = 0; - PCHAR* szArgv = CommandLineToArgv( lpCmdLine, &nArgc ); - - for( int i=0; i < nArgc; i++ ) { - if(szArgv[i] == 0) - continue; - if (__IsLocaleVersion(szArgv[i])) // #0000829: [M2EU] 버전 파일이 항상 생기지 않도록 수정 - { - char szModuleName[MAX_PATH]; - char szVersionPath[MAX_PATH]; - GetModuleFileName(NULL, szModuleName, sizeof(szModuleName)); - sprintf(szVersionPath, "%s.version", szModuleName); - FILE* fp = fopen(szVersionPath, "wt"); - if (fp) - { - extern int METIN2_GET_VERSION(); - fprintf(fp, "r%d\n", METIN2_GET_VERSION()); - fclose(fp); - } - bQuit = true; - } else if (__IsLocaleOption(szArgv[i])) - { - FILE* fp=fopen("locale.txt", "wt"); - fprintf(fp, "service[%s] code_page[%d]", - LocaleService_GetName(), LocaleService_GetCodePage()); - fclose(fp); - bQuit = true; - } else if (__IsTimeStampOption(szArgv[i])) - { - __PrintTimeStamp(); - bQuit = true; - } else if ((strcmp(szArgv[i], "--force-set-locale") == 0)) - { - // locale 설정엔 인자가 두 개 더 필요함 (로케일 명칭, 데이터 경로) - if (nArgc <= i + 2) - { - MessageBox(NULL, "Invalid arguments", ApplicationStringTable_GetStringz(IDS_APP_NAME, "APP_NAME"), MB_ICONSTOP); - goto Clean; - } - - const char* localeName = szArgv[++i]; - const char* localePath = szArgv[++i]; - - LocaleService_ForceSetLocale(localeName, localePath); - } - } - - if(bQuit) - goto Clean; + auto szArgv = CommandLineToArgv (lpCmdLine, &nArgc); if (!CheckPythonLibraryFilenames()) { @@ -583,36 +296,23 @@ int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLi goto Clean; } - Main(hInstance, lpCmdLine); - + Main (hInstance, lpCmdLine); ::CoUninitialize(); - if(gs_szErrorString[0]) - MessageBox(NULL, gs_szErrorString, ApplicationStringTable_GetStringz(IDS_APP_NAME, "APP_NAME"), MB_ICONSTOP); - Clean: - SAFE_FREE_GLOBAL(szArgv); - + SAFE_FREE_GLOBAL (szArgv); return 0; } -static void GrannyError(granny_log_message_type Type, - granny_log_message_origin Origin, - char const* File, - granny_int32x Line, - char const* Message, - void* UserData) +static void GrannyError(granny_log_message_type Type, granny_log_message_origin Origin, char const* File, granny_int32x Line, char const* Message, void* UserData) { - TraceError("GRANNY: %s", Message); + TraceError("GRANNY: %s", Message); } int Setup(LPSTR lpCmdLine) { - /* - * 타이머 정밀도를 올린다. - */ - TIMECAPS tc; - UINT wTimerRes; + TIMECAPS tc; + UINT wTimerRes; if (timeGetDevCaps(&tc, sizeof(TIMECAPS)) != TIMERR_NOERROR) return 0; @@ -620,13 +320,9 @@ int Setup(LPSTR lpCmdLine) wTimerRes = MINMAX(tc.wPeriodMin, 1, tc.wPeriodMax); timeBeginPeriod(wTimerRes); - /* - * 그래니 에러 핸들링 - */ - granny_log_callback Callback; - Callback.Function = nullptr; - Callback.UserData = 0; - GrannySetLogCallback(&Callback); + Callback.Function = nullptr; + Callback.UserData = 0; + GrannySetLogCallback(&Callback); return 1; } diff --git a/src/UserInterface/UserInterface.rc b/src/UserInterface/UserInterface.rc index d9a2438..6570c76 100644 --- a/src/UserInterface/UserInterface.rc +++ b/src/UserInterface/UserInterface.rc @@ -1,7 +1,7 @@ // Microsoft Visual C++ generated resource script. // #include "resource.h" -#include "version.h" +#include "version.h" #define APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// @@ -15,60 +15,12 @@ #undef APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// -// Ϻ resources +// English (United States) resources +// -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_JPN) #ifdef _WIN32 -LANGUAGE LANG_JAPANESE, SUBLANG_DEFAULT -#pragma code_page(932) -#endif //_WIN32 - -///////////////////////////////////////////////////////////////////////////// -// -// String Table -// - -STRINGTABLE -BEGIN - IDS_POSSESSIVE_MORPHENE "'s" -END - -#endif // Ϻ resources -///////////////////////////////////////////////////////////////////////////// - - -///////////////////////////////////////////////////////////////////////////// -// ߱(߱) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS) -#ifdef _WIN32 -LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED -#pragma code_page(936) -#endif //_WIN32 - -///////////////////////////////////////////////////////////////////////////// -// -// String Table -// - -STRINGTABLE -BEGIN - IDS_POSSESSIVE_MORPHENE "'s" - IDS_WARN_BAD_DRIVER "ϣܸԿ" - IDS_WARN_NO_TNL "Կ֧3D TnL Ӳ\nϷ޷С" -END - -#endif // ߱(߱) resources -///////////////////////////////////////////////////////////////////////////// - - -///////////////////////////////////////////////////////////////////////////// -// ѱ resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_KOR) -#ifdef _WIN32 -LANGUAGE LANG_KOREAN, SUBLANG_DEFAULT -#pragma code_page(949) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(65001) #endif //_WIN32 ///////////////////////////////////////////////////////////////////////////// @@ -79,7 +31,7 @@ LANGUAGE LANG_KOREAN, SUBLANG_DEFAULT IDD_SELECT_LOCALE DIALOGEX 0, 0, 182, 142 STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION CAPTION "SELECT LOCALE" -FONT 10, "Arial", 0, 0, 0x0 +FONT 10, "Tahoma", 0, 0, 0x0 BEGIN PUSHBUTTON "START",IDC_START,44,123,40,13 PUSHBUTTON "EXIT",IDC_EXIT,96,123,40,13 @@ -93,18 +45,18 @@ END // TEXTINCLUDE // -1 TEXTINCLUDE +1 TEXTINCLUDE BEGIN "resource.h\0" END -2 TEXTINCLUDE +2 TEXTINCLUDE BEGIN - "#include ""afxres.h""\r\n" + "#include \"afxres.h\"\r\n" "\0" END -3 TEXTINCLUDE +3 TEXTINCLUDE BEGIN "\r\n" "\0" @@ -118,19 +70,19 @@ END // Cursor // -IDC_CURSOR_NORMAL CURSOR "Cursors\\cursor.cur" -IDC_CURSOR_CHAIR CURSOR "Cursors\\cursor_chair.cur" -IDC_CURSOR_DOOR CURSOR "Cursors\\cursor_door.cur" -IDC_CURSOR_NO CURSOR "Cursors\\cursor_no.cur" -IDC_CURSOR_PICK CURSOR "Cursors\\cursor_pick.cur" -IDC_CURSOR_TALK CURSOR "Cursors\\cursor_talk.cur" -IDC_CURSOR_ATTACK CURSOR "Cursors\\cursor_attack.cur" -IDC_CURSOR_BUY CURSOR "cursors\\cursor_buy.cur" -IDC_CURSOR_SELL CURSOR "cursors\\cursor_sell.cur" +IDC_CURSOR_NORMAL CURSOR "Cursors\\cursor.cur" +IDC_CURSOR_CHAIR CURSOR "Cursors\\cursor_chair.cur" +IDC_CURSOR_DOOR CURSOR "Cursors\\cursor_door.cur" +IDC_CURSOR_NO CURSOR "Cursors\\cursor_no.cur" +IDC_CURSOR_PICK CURSOR "Cursors\\cursor_pick.cur" +IDC_CURSOR_TALK CURSOR "Cursors\\cursor_talk.cur" +IDC_CURSOR_ATTACK CURSOR "Cursors\\cursor_attack.cur" +IDC_CURSOR_BUY CURSOR "Cursors\\cursor_buy.cur" +IDC_CURSOR_SELL CURSOR "Cursors\\cursor_sell.cur" IDC_CURSOR_CAMERA_ROTATE CURSOR "Cursors\\cursor_camera_rotate.cur" -IDC_CURSOR_HSIZE CURSOR "Cursors\\cursor_hsize.cur" -IDC_CURSOR_VSIZE CURSOR "Cursors\\cursor_vsize.cur" -IDC_CURSOR_HVSIZE CURSOR "Cursors\\cursor_hvsize.cur" +IDC_CURSOR_HSIZE CURSOR "Cursors\\cursor_hsize.cur" +IDC_CURSOR_VSIZE CURSOR "Cursors\\cursor_vsize.cur" +IDC_CURSOR_HVSIZE CURSOR "Cursors\\cursor_hvsize.cur" ///////////////////////////////////////////////////////////////////////////// // @@ -138,7 +90,7 @@ IDC_CURSOR_HVSIZE CURSOR "Cursors\\cursor_hvsize.cur" // #ifdef APSTUDIO_INVOKED -GUIDELINES DESIGNINFO +GUIDELINES DESIGNINFO BEGIN IDD_SELECT_LOCALE, DIALOG BEGIN @@ -164,7 +116,7 @@ IDI_METIN2 ICON "metin2.ico" // VS_VERSION_INFO VERSIONINFO - FILEVERSION VER_FILE_VERSION + FILEVERSION VER_FILE_VERSION PRODUCTVERSION 1,0,0,1 FILEFLAGSMASK 0x17L #ifdef _DEBUG @@ -178,7 +130,8 @@ VS_VERSION_INFO VERSIONINFO BEGIN BLOCK "StringFileInfo" BEGIN - BLOCK "080003b5" + // 0409 = en-US, 04B0 = Unicode + BLOCK "040904B0" BEGIN VALUE "CompanyName", "Ymir Entertainment" VALUE "FileDescription", "Metin2Client" @@ -192,67 +145,31 @@ BEGIN END BLOCK "VarFileInfo" BEGIN - VALUE "Translation", 0x800, 949 + VALUE "Translation", 0x0409, 1200 END END - ///////////////////////////////////////////////////////////////////////////// // // String Table // -STRINGTABLE +STRINGTABLE BEGIN - IDS_APP_NAME "ƾ2" - IDS_POSSESSIVE_MORPHENE "" - IDS_WARN_BAD_DRIVER "׷ ̹ Ʈ Ͻñ ٶϴ." - IDS_WARN_NO_TNL "ϰ ý ׷ī 3D TnL ϵ ʾ\n ǰų ֽϴ." - IDS_ERR_CANNOT_READ_FILE "%s ϴ." - IDS_ERR_NOT_LATEST_FILE "'%s' ֽŹ ƴմϴ. ó ٽ ּ." - IDS_ERR_MUST_LAUNCH_FROM_PATCHER "Ŭ̾Ʈ ó ؼ Ǿ մϴ." + IDS_APP_NAME "Metin 2" + IDS_POSSESSIVE_MORPHENE "'s" + IDS_WARN_BAD_DRIVER "Please update your graphics driver." + IDS_WARN_NO_TNL "Your graphics card does not support 3D TnL hardware acceleration. The game may not run properly." + IDS_ERR_CANNOT_READ_FILE "Cannot read %s file" + IDS_ERR_NOT_LATEST_FILE "File '%s' is not the latest version. Please launch the patcher." + IDS_ERR_MUST_LAUNCH_FROM_PATCHER "Please run the patcher." END -#endif // ѱ resources -///////////////////////////////////////////////////////////////////////////// - - -///////////////////////////////////////////////////////////////////////////// -// (̱) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) -#ifdef _WIN32 -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US -#pragma code_page(1252) -#endif //_WIN32 - -///////////////////////////////////////////////////////////////////////////// -// -// String Table -// - -STRINGTABLE -BEGIN - IDS_APP_NAME "Metin 2" - IDS_POSSESSIVE_MORPHENE "'s" - IDS_WARN_BAD_DRIVER "IDS_WARN_BAD_DRIVER" - IDS_WARN_NO_TNL "IDS_WARN_NO_TNL" - IDS_ERR_CANNOT_READ_FILE "Cannot read %s file" - IDS_ERR_NOT_LATEST_FILE "File '%s' is not latest version. Please launch patcher." - IDS_ERR_MUST_LAUNCH_FROM_PATCHER "Please run patcher." -END - -#endif // (̱) resources -///////////////////////////////////////////////////////////////////////////// - - - #ifndef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 3 resource. // - ///////////////////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED From 4729dafc12034df849d4db0c18987a3587efe998 Mon Sep 17 00:00:00 2001 From: rtw1x1 Date: Fri, 26 Dec 2025 14:53:52 +0000 Subject: [PATCH 02/12] Full Unicode patch with RTL Support & BiDi logic #3 --- src/EterLocale/StringCodec.cpp | 43 -- src/EterLocale/StringCodec.h | 21 - src/EterLocale/StringCodec_Vietnamese.cpp | 554 ---------------------- src/EterLocale/StringCodec_Vietnamese.h | 4 - 4 files changed, 622 deletions(-) delete mode 100644 src/EterLocale/StringCodec.cpp delete mode 100644 src/EterLocale/StringCodec.h delete mode 100644 src/EterLocale/StringCodec_Vietnamese.cpp delete mode 100644 src/EterLocale/StringCodec_Vietnamese.h diff --git a/src/EterLocale/StringCodec.cpp b/src/EterLocale/StringCodec.cpp deleted file mode 100644 index a16724d..0000000 --- a/src/EterLocale/StringCodec.cpp +++ /dev/null @@ -1,43 +0,0 @@ -#include "stdafx.h" -#include "StringCodec.h" -#include "StringCodec_Vietnamese.h" - -int Ymir_WideCharToMultiByte( - UINT CodePage, - DWORD dwFlags, - LPCWSTR lpWideCharStr, - int cchWideChar, - LPSTR lpMultiByteStr, - int cbMultiByte, - LPCSTR lpDefaultChar, - LPBOOL lpUsedDefaultChar -) -{ - if (CodePage == CP_1258) - { - return EL_String_Encode_Vietnamese(lpWideCharStr, cchWideChar, lpMultiByteStr, cbMultiByte); - } - else - { - return WideCharToMultiByte(CodePage, dwFlags, lpWideCharStr, cchWideChar, lpMultiByteStr, cbMultiByte, lpDefaultChar, lpUsedDefaultChar); - } -} - -int Ymir_MultiByteToWideChar( - UINT CodePage, - DWORD dwFlags, - LPCSTR lpMultiByteStr, - int cbMultiByte, - LPWSTR lpWideCharStr, - int cchWideChar -) -{ - if (CodePage == CP_1258) - { - return EL_String_Decode_Vietnamese(lpMultiByteStr, cbMultiByte, lpWideCharStr, cchWideChar); - } - else - { - return MultiByteToWideChar(CodePage, dwFlags, lpMultiByteStr, cbMultiByte, lpWideCharStr, cchWideChar); - } -} \ No newline at end of file diff --git a/src/EterLocale/StringCodec.h b/src/EterLocale/StringCodec.h deleted file mode 100644 index a937f57..0000000 --- a/src/EterLocale/StringCodec.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -int Ymir_WideCharToMultiByte( - UINT CodePage, - DWORD dwFlags, - LPCWSTR lpWideCharStr, - int cchWideChar, - LPSTR lpMultiByteStr, - int cbMultiByte, - LPCSTR lpDefaultChar, - LPBOOL lpUsedDefaultChar -); - -int Ymir_MultiByteToWideChar( - UINT CodePage, - DWORD dwFlags, - LPCSTR lpMultiByteStr, - int cbMultiByte, - LPWSTR lpWideCharStr, - int cchWideChar -); \ No newline at end of file diff --git a/src/EterLocale/StringCodec_Vietnamese.cpp b/src/EterLocale/StringCodec_Vietnamese.cpp deleted file mode 100644 index eab8978..0000000 --- a/src/EterLocale/StringCodec_Vietnamese.cpp +++ /dev/null @@ -1,554 +0,0 @@ -#include "stdafx.h" -#include "StringCodec_Vietnamese.h" - -#pragma warning(disable: 4310) // char 짤림 경고 무시 - -static wchar_t cp1258_to_unicode[256] = { - 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, - 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, - 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, - 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, - 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, - 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, - 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, - 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, - 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, - 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, - 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, - 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, - 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, - 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, - 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, - 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, - 0x20ac, 0x0081, 0x201a, 0x0192, 0x201e, 0x2026, 0x2020, 0x2021, //0x80 - 0x02c6, 0x2030, 0x008a, 0x2039, 0x0152, 0x008d, 0x008e, 0x008f, // - 0x0090, 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014, //0x90 - 0x02dc, 0x2122, 0x009a, 0x203a, 0x0153, 0x009d, 0x009e, 0x0178, // - 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7, //0xa0 - 0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af, // - 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7, //0xb0 - 0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf, // - 0x00c0, 0x00c1, 0x00c2, 0x0102, 0x00c4, 0x00c5, 0x00c6, 0x00c7, //0xc0 - 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x0300, 0x00cd, 0x00ce, 0x00cf, // - 0x0110, 0x00d1, 0x0309, 0x00d3, 0x00d4, 0x01a0, 0x00d6, 0x00d7, //0xd0 - 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x01af, 0x0303, 0x00df, // - 0x00e0, 0x00e1, 0x00e2, 0x0103, 0x00e4, 0x00e5, 0x00e6, 0x00e7, //0xe0 - 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x0301, 0x00ed, 0x00ee, 0x00ef, // - 0x0111, 0x00f1, 0x0323, 0x00f3, 0x00f4, 0x01a1, 0x00f6, 0x00f7, //0xf0 - 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x01b0, 0x20ab, 0x00ff, // -}; - -static wchar_t cp1258_composed_table[][5] = { - { 0x00c1, 0x00c0, 0x1ea2, 0x00c3, 0x1ea0 }, - { 0x00e1, 0x00e0, 0x1ea3, 0x00e3, 0x1ea1 }, - { 0x1eae, 0x1eb0, 0x1eb2, 0x1eb4, 0x1eb6 }, - { 0x1eaf, 0x1eb1, 0x1eb3, 0x1eb5, 0x1eb7 }, - { 0x1ea4, 0x1ea6, 0x1ea8, 0x1eaa, 0x1eac }, - { 0x1ea5, 0x1ea7, 0x1ea9, 0x1eab, 0x1ead }, - { 0x00c9, 0x00c8, 0x1eba, 0x1ebc, 0x1eb8 }, - { 0x00e9, 0x00e8, 0x1ebb, 0x1ebd, 0x1eb9 }, - { 0x1ebe, 0x1ec0, 0x1ec2, 0x1ec4, 0x1ec6 }, - { 0x1ebf, 0x1ec1, 0x1ec3, 0x1ec5, 0x1ec7 }, - { 0x00cd, 0x00cc, 0x1ec8, 0x0128, 0x1eca }, - { 0x00ed, 0x00ec, 0x1ec9, 0x0129, 0x1ecb }, - { 0x00d3, 0x00d2, 0x1ece, 0x00d5, 0x1ecc }, - { 0x00f3, 0x00f2, 0x1ecf, 0x00f5, 0x1ecd }, - { 0x1ed0, 0x1ed2, 0x1ed4, 0x1ed6, 0x1ed8 }, - { 0x1ed1, 0x1ed3, 0x1ed5, 0x1ed7, 0x1ed9 }, - { 0x1eda, 0x1edc, 0x1ede, 0x1ee0, 0x1ee2 }, - { 0x1edb, 0x1edd, 0x1edf, 0x1ee1, 0x1ee3 }, - { 0x00da, 0x00d9, 0x1ee6, 0x0168, 0x1ee4 }, - { 0x00fa, 0x00f9, 0x1ee7, 0x0169, 0x1ee5 }, - { 0x1ee8, 0x1eea, 0x1eec, 0x1eee, 0x1ef0 }, - { 0x1ee9, 0x1eeb, 0x1eed, 0x1eef, 0x1ef1 }, - { 0x00dd, 0x1ef2, 0x1ef6, 0x1ef8, 0x1ef4 }, - { 0x00fd, 0x1ef3, 0x1ef7, 0x1ef9, 0x1ef5 }, -}; - -static bool IsTone(wchar_t tone) -{ - switch(tone) - { - case 0x0300: - case 0x0301: - case 0x0309: - case 0x0303: - case 0x0323: - return true; - default: - return false; - } -} - -static wchar_t ComposeTone(wchar_t prev, wchar_t tone) -{ - int col, row; - - switch(tone) - { - case 0x0301: col = 0; break; - case 0x0300: col = 1; break; - case 0x0309: col = 2; break; - case 0x0303: col = 3; break; - case 0x0323: col = 4; break; - default: - return prev; - } - - switch(prev) - { - case 0x0041: row = 0; break; - case 0x0061: row = 1; break; - case 0x0102: row = 2; break; - case 0x0103: row = 3; break; - case 0x00C2: row = 4; break; - case 0x00E2: row = 5; break; - case 0x0045: row = 6; break; - case 0x0065: row = 7; break; - case 0x00CA: row = 8; break; - case 0x00EA: row = 9; break; - case 0x0049: row = 10; break; - case 0x0069: row = 11; break; - case 0x004F: row = 12; break; - case 0x006F: row = 13; break; - case 0x00D4: row = 14; break; - case 0x00F4: row = 15; break; - case 0x01A0: row = 16; break; - case 0x01A1: row = 17; break; - case 0x0055: row = 18; break; - case 0x0075: row = 19; break; - case 0x01AF: row = 20; break; - case 0x01B0: row = 21; break; - case 0x0059: row = 22; break; - case 0x0079: row = 23; break; - default: - return prev; - } - - return cp1258_composed_table[row][col]; -} - -int EL_String_Decode_Vietnamese(const char* multi, int multiLen, wchar_t* wide, int wideLen) -{ - if(multiLen < 0) - multiLen = (int)strlen(multi) + 1; - - int src = 0; - int dest = 0; - - if(multiLen > 0) - { - /* 첫글자는 무조건 변경 */ - wchar_t prev = cp1258_to_unicode[(BYTE)multi[src++]]; - - while(src < multiLen) - { - wchar_t unicode = cp1258_to_unicode[(BYTE)multi[src]]; - - /* 다음 문자가 Tone 인가? */ - if(IsTone(unicode)) - { - /* 앞의 문자와 합하자. */ - prev = ComposeTone(prev, unicode); - } - else - { - /* 일반 문자가 왔다. 앞 문자를 변환 */ - if(dest < wideLen) - wide[dest++] = prev; - prev = unicode; - } - ++src; - } - - if(dest < wideLen) - wide[dest++] = prev; - } - - return dest; -} - -static bool DecomposeLetter(wchar_t input, char* letter) -{ - switch(input) - { - case 0x00c1: // L'Á' - case 0x00c0: // L'À' - case 0x1ea2: // L'Ả' - case 0x00c3: // L'Ã' - case 0x1ea0: // L'Ạ' - *letter = 'A'; - return true; - case 0x00e1: // L'á' - case 0x00e0: // L'à' - case 0x1ea3: // L'ả' - case 0x00e3: // L'ã' - case 0x1ea1: // L'ạ' - *letter = 'a'; - return true; - case 0x1eae: // L'Ắ' - case 0x1eb0: // L'Ằ' - case 0x1eb2: // L'Ẳ' - case 0x1eb4: // L'Ẵ' - case 0x1eb6: // L'Ặ' - *letter = (char)0xc3; - return true; - case 0x1eaf: // L'ắ' - case 0x1eb1: // L'ằ' - case 0x1eb3: // L'ẳ' - case 0x1eb5: // L'ẵ' - case 0x1eb7: // L'ặ' - *letter = (char)0xe3; - return true; - case 0x1ea4: // L'Ấ' - case 0x1ea6: // L'Ầ' - case 0x1ea8: // L'Ẩ' - case 0x1eaa: // L'Ẫ' - case 0x1eac: // L'Ậ' - *letter = (char)0xc2; - return true; - case 0x1ea5: // L'ấ' - case 0x1ea7: // L'ầ' - case 0x1ea9: // L'ẩ' - case 0x1eab: // L'ẫ' - case 0x1ead: // L'ậ' - *letter = (char)0xe2; - return true; - case 0x00c9: // L'É' - case 0x00c8: // L'È' - case 0x1eba: // L'Ẻ' - case 0x1ebc: // L'Ẽ' - case 0x1eb8: // L'Ẹ' - *letter = (char)'E'; - return true; - case 0x00e9: // L'é' - case 0x00e8: // L'è' - case 0x1ebb: // L'ẻ' - case 0x1ebd: // L'ẽ' - case 0x1eb9: // L'ẹ' - *letter = (char)'e'; - return true; - case 0x1ebe: // L'Ế' - case 0x1ec0: // L'Ề' - case 0x1ec2: // L'Ể' - case 0x1ec4: // L'Ễ' - case 0x1ec6: // L'Ệ' - *letter = (char)0xca; - return true; - case 0x1ebf: // L'ế' - case 0x1ec1: // L'ề' - case 0x1ec3: // L'ể' - case 0x1ec5: // L'ễ' - case 0x1ec7: // L'ệ' - *letter = (char)0xea; - return true; - case 0x00cd: // L'Í' - case 0x00cc: // L'Ì' - case 0x1ec8: // L'Ỉ' - case 0x0128: // L'Ĩ' - case 0x1eca: // L'Ị' - *letter = (char)'I'; - return true; - case 0x00ed: // L'í' - case 0x00ec: // L'ì' - case 0x1ec9: // L'ỉ' - case 0x0129: // L'ĩ' - case 0x1ecb: // L'ị' - *letter = (char)'i'; - return true; - case 0x00d3: // L'Ó' - case 0x00d2: // L'Ò' - case 0x1ece: // L'Ỏ' - case 0x00d5: // L'Õ' - case 0x1ecc: // L'Ọ' - *letter = (char)'O'; - return true; - case 0x00f3: // L'ó' - case 0x00f2: // L'ò' - case 0x1ecf: // L'ỏ' - case 0x00f5: // L'õ' - case 0x1ecd: // L'ọ' - *letter = (char)'o'; - return true; - case 0x1ed0: // L'Ố' - case 0x1ed2: // L'Ồ' - case 0x1ed4: // L'Ổ' - case 0x1ed6: // L'Ỗ' - case 0x1ed8: // L'Ộ' - *letter = (char)0xd4; - return true; - case 0x1ed1: // L'ố' - case 0x1ed3: // L'ồ' - case 0x1ed5: // L'ổ' - case 0x1ed7: // L'ỗ' - case 0x1ed9: // L'ộ' - *letter = (char)0xf4; - return true; - case 0x1eda: // L'Ớ' - case 0x1edc: // L'Ờ' - case 0x1ede: // L'Ở' - case 0x1ee0: // L'Ỡ' - case 0x1ee2: // L'Ợ' - *letter = (char)0xd5; - return true; - case 0x1edb: // L'ớ' - case 0x1edd: // L'ờ' - case 0x1edf: // L'ở' - case 0x1ee1: // L'ỡ' - case 0x1ee3: // L'ợ' - *letter = (char)0xf5; - return true; - case 0x00da: // L'Ú' - case 0x00d9: // L'Ù' - case 0x1ee6: // L'Ủ' - case 0x0168: // L'Ũ' - case 0x1ee4: // L'Ụ' - *letter = (char)'U'; - return true; - case 0x00fa: // L'ú' - case 0x00f9: // L'ù' - case 0x1ee7: // L'ủ' - case 0x0169: // L'ũ' - case 0x1ee5: // L'ụ' - *letter = (char)'u'; - return true; - case 0x1ee8: // L'Ứ' - case 0x1eea: // L'Ừ' - case 0x1eec: // L'Ử' - case 0x1eee: // L'Ữ' - case 0x1ef0: // L'Ự' - *letter = (char)0xdd; - return true; - case 0x1ee9: // L'ứ' - case 0x1eeb: // L'ừ' - case 0x1eed: // L'ử' - case 0x1eef: // L'ữ' - case 0x1ef1: // L'ự' - *letter = (char)0xfd; - return true; - case 0x1ef2: // L'Ỳ' - case 0x00dd: // L'Ý' - case 0x1ef6: // L'Ỷ' - case 0x1ef8: // L'Ỹ' - case 0x1ef4: // L'Ỵ' - *letter = (char)'Y'; - return true; - case 0x1ef3: // L'ỳ' - case 0x00fd: // L'ý' - case 0x1ef7: // L'ỷ' - case 0x1ef9: // L'ỹ' - case 0x1ef5: // L'ỵ' - *letter = (char)'y'; - return true; - case 0x0102: // L'Ă' - *letter = (char)0xc3; - return true; - case 0x0103: // L'ă' - *letter = (char)0xe3; - return true; - case 0x0110: // L'Đ' - *letter = (char)0xd0; - return true; - case 0x0111: // L'đ' - *letter = (char)0xf0; - return true; - case 0x01a0: // L'Ơ' - *letter = (char)0xd5; - return true; - case 0x01a1: // L'ơ' - *letter = (char)0xf5; - return true; - case 0x01af: // L'Ư' - *letter = (char)0xdd; - return true; - case 0x01b0: // L'ư' - *letter = (char)0xfd; - return true; - case 0x20ab: // L'₫' - *letter = (char)0xfe; - return true; - case 0x201c: // L'“' - *letter = (char)'"'; - return true; - case 0x201d: // L'”' - *letter = (char)'"'; - return true; - } - - if(input < 256) - { - *letter = (char)input; - return true; - } - return false; -} - -static bool DecomposeTone(wchar_t input, char* tone) -{ - switch(input) - { - case 0x00c1: // L'Á' - case 0x00e1: // L'á' - case 0x1eae: // L'Ắ' - case 0x1eaf: // L'ắ' - case 0x1ea4: // L'Ấ' - case 0x1ea5: // L'ấ' - case 0x00c9: // L'É' - case 0x00e9: // L'é' - case 0x1ebe: // L'Ế' - case 0x1ebf: // L'ế' - case 0x00cd: // L'Í' - case 0x00ed: // L'í' - case 0x00d3: // L'Ó' - case 0x00f3: // L'ó' - case 0x1ed0: // L'Ố' - case 0x1ed1: // L'ố' - case 0x1eda: // L'Ớ' - case 0x1edb: // L'ớ' - case 0x00da: // L'Ú' - case 0x00fa: // L'ú' - case 0x1ee8: // L'Ứ' - case 0x1ee9: // L'ứ' - case 0x00dd: // L'Ý' - case 0x00fd: // L'ý' - *tone = (char)0xec; - return true; - case 0x00c0: // L'À' - case 0x00e0: // L'à' - case 0x1eb0: // L'Ằ' - case 0x1eb1: // L'ằ' - case 0x1ea6: // L'Ầ' - case 0x1ea7: // L'ầ' - case 0x00c8: // L'È' - case 0x00e8: // L'è' - case 0x1ec0: // L'Ề' - case 0x1ec1: // L'ề' - case 0x00cc: // L'Ì' - case 0x00ec: // L'ì' - case 0x00d2: // L'Ò' - case 0x00f2: // L'ò' - case 0x1ed2: // L'Ồ' - case 0x1ed3: // L'ồ' - case 0x1edc: // L'Ờ' - case 0x1edd: // L'ờ' - case 0x00d9: // L'Ù' - case 0x00f9: // L'ù' - case 0x1eea: // L'Ừ' - case 0x1eeb: // L'ừ' - case 0x1ef2: // L'Ỳ' - case 0x1ef3: // L'ỳ' - *tone = (char)0xcc; - return true; - case 0x1ea2: // L'Ả' - case 0x1ea3: // L'ả' - case 0x1eb2: // L'Ẳ' - case 0x1eb3: // L'ẳ' - case 0x1ea8: // L'Ẩ' - case 0x1ea9: // L'ẩ' - case 0x1eba: // L'Ẻ' - case 0x1ebb: // L'ẻ' - case 0x1ec2: // L'Ể' - case 0x1ec3: // L'ể' - case 0x1ec8: // L'Ỉ' - case 0x1ec9: // L'ỉ' - case 0x1ece: // L'Ỏ' - case 0x1ecf: // L'ỏ' - case 0x1ed4: // L'Ổ' - case 0x1ed5: // L'ổ' - case 0x1ede: // L'Ở' - case 0x1edf: // L'ở' - case 0x1ee6: // L'Ủ' - case 0x1ee7: // L'ủ' - case 0x1eec: // L'Ử' - case 0x1eed: // L'ử' - case 0x1ef6: // L'Ỷ' - case 0x1ef7: // L'ỷ' - *tone = (char)0xd2; - return true; - case 0x00c3: // L'Ã' - case 0x00e3: // L'ã' - case 0x1eb4: // L'Ẵ' - case 0x1eb5: // L'ẵ' - case 0x1eaa: // L'Ẫ' - case 0x1eab: // L'ẫ' - case 0x1ebc: // L'Ẽ' - case 0x1ebd: // L'ẽ' - case 0x1ec4: // L'Ễ' - case 0x1ec5: // L'ễ' - case 0x0128: // L'Ĩ' - case 0x0129: // L'ĩ' - case 0x00d5: // L'Õ' - case 0x00f5: // L'õ' - case 0x1ed6: // L'Ỗ' - case 0x1ed7: // L'ỗ' - case 0x1ee0: // L'Ỡ' - case 0x1ee1: // L'ỡ' - case 0x0169: // L'ũ' - case 0x0168: // L'Ũ' - case 0x1eee: // L'Ữ' - case 0x1eef: // L'ữ' - case 0x1ef8: // L'Ỹ' - case 0x1ef9: // L'ỹ' - *tone = (char)0xde; - return true; - case 0x1ea0: // L'Ạ' - case 0x1ea1: // L'ạ' - case 0x1eb6: // L'Ặ' - case 0x1eb7: // L'ặ' - case 0x1eac: // L'Ậ' - case 0x1ead: // L'ậ' - case 0x1eb8: // L'Ẹ' - case 0x1eb9: // L'ẹ' - case 0x1ec6: // L'Ệ' - case 0x1ec7: // L'ệ' - case 0x1eca: // L'Ị' - case 0x1ecb: // L'ị' - case 0x1ecc: // L'Ọ' - case 0x1ecd: // L'ọ' - case 0x1ed8: // L'Ộ' - case 0x1ed9: // L'ộ' - case 0x1ee2: // L'Ợ' - case 0x1ee3: // L'ợ' - case 0x1ee4: // L'Ụ' - case 0x1ee5: // L'ụ' - case 0x1ef0: // L'Ự' - case 0x1ef1: // L'ự' - case 0x1ef4: // L'Ỵ' - case 0x1ef5: // L'ỵ' - *tone = (char)0xf2; - return true; - } - - return false; -} - -int EL_String_Encode_Vietnamese(const wchar_t* wide, int wideLen, char* multi, int multiLen) -{ - if(wideLen < 0) - wideLen = (int)wcslen(wide) + 1; - - int src = 0; - int dest = 0; - - if(wideLen > 0) - { - while(src < wideLen && dest < multiLen) - { - char letter; - if(DecomposeLetter(wide[src], &letter)) - { - multi[dest++] = letter; - } - - char tone; - if(DecomposeTone(wide[src], &tone) && dest < multiLen) - { - multi[dest++] = tone; - } - - ++src; - } - } - - return dest; -} \ No newline at end of file diff --git a/src/EterLocale/StringCodec_Vietnamese.h b/src/EterLocale/StringCodec_Vietnamese.h deleted file mode 100644 index d1856e0..0000000 --- a/src/EterLocale/StringCodec_Vietnamese.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once - -int EL_String_Decode_Vietnamese(const char* multi, int multiLen, wchar_t* wide, int wideLen); -int EL_String_Encode_Vietnamese(const wchar_t* wide, int wideLen, char* multi, int multiLen); \ No newline at end of file From 5d73d79eb8df6da32b14aca7e2b42149aee0700c Mon Sep 17 00:00:00 2001 From: rtw1x1 Date: Fri, 26 Dec 2025 16:02:34 +0000 Subject: [PATCH 03/12] Full unicode hotfix Debug mode --- src/EterBase/Debug.cpp | 398 ++++++++++++++++++++------------------ src/EterImageLib/StdAfx.h | 21 +- 2 files changed, 228 insertions(+), 191 deletions(-) diff --git a/src/EterBase/Debug.cpp b/src/EterBase/Debug.cpp index 82c258a..d9f77fd 100644 --- a/src/EterBase/Debug.cpp +++ b/src/EterBase/Debug.cpp @@ -2,6 +2,9 @@ #include #include +#include +#include + #include "Debug.h" #include "Singleton.h" #include "Timer.h" @@ -13,228 +16,252 @@ const DWORD DEBUG_STRING_MAX_LEN = 1024; static int isLogFile = false; HWND g_PopupHwnd = NULL; +// Convert UTF-8 char* -> wide and send to debugger (NO helper function, just a macro) +#ifdef _DEBUG +#define DBG_OUT_W_UTF8(psz) \ + do { \ + const char* __s = (psz) ? (psz) : ""; \ + std::wstring __w = Utf8ToWide(__s); \ + OutputDebugStringW(__w.c_str()); \ + } while (0) +#else +#define DBG_OUT_W_UTF8(psz) do { (void)(psz); } while (0) +#endif + class CLogFile : public CSingleton { - public: - CLogFile() : m_fp(NULL) - { - } + public: + CLogFile() : m_fp(NULL) {} - virtual ~CLogFile() - { - if (m_fp) - fclose(m_fp); + virtual ~CLogFile() + { + if (m_fp) + fclose(m_fp); - m_fp = NULL; - } + m_fp = NULL; + } - void Initialize() - { - m_fp = fopen("log/log.txt", "w"); - } + void Initialize() + { + m_fp = fopen("log/log.txt", "w"); + } - void Write(const char * c_pszMsg) - { - if (!m_fp) - return; + void Write(const char* c_pszMsg) + { + if (!m_fp) + return; - time_t ct = time(0); - struct tm ctm = *localtime(&ct); + time_t ct = time(0); + struct tm ctm = *localtime(&ct); - fprintf(m_fp, "%02d%02d %02d:%02d:%05d :: %s", - ctm.tm_mon + 1, - ctm.tm_mday, - ctm.tm_hour, - ctm.tm_min, - ELTimer_GetMSec() % 60000, - c_pszMsg); + fprintf(m_fp, "%02d%02d %02d:%02d:%05d :: %s", + ctm.tm_mon + 1, + ctm.tm_mday, + ctm.tm_hour, + ctm.tm_min, + ELTimer_GetMSec() % 60000, + c_pszMsg); - fflush(m_fp); - } + fflush(m_fp); + } - protected: - FILE * m_fp; + protected: + FILE* m_fp; }; static CLogFile gs_logfile; -static UINT gs_uLevel=0; +static UINT gs_uLevel = 0; void SetLogLevel(UINT uLevel) { - gs_uLevel=uLevel; + gs_uLevel = uLevel; } void Log(UINT uLevel, const char* c_szMsg) { - if (uLevel>=gs_uLevel) - Trace(c_szMsg); + if (uLevel >= gs_uLevel) + Trace(c_szMsg); } void Logn(UINT uLevel, const char* c_szMsg) { - if (uLevel>=gs_uLevel) - Tracen(c_szMsg); + if (uLevel >= gs_uLevel) + Tracen(c_szMsg); } void Logf(UINT uLevel, const char* c_szFormat, ...) { - if (uLevel 0) - { - szBuf[len] = '\n'; - szBuf[len + 1] = '\0'; - } - va_end(args); #ifdef _DEBUG - OutputDebugString(szBuf); - puts(szBuf); + DBG_OUT_W_UTF8(szBuf); + fputs(szBuf, stdout); #endif - if (isLogFile) - LogFile(szBuf); + if (isLogFile) + LogFile(szBuf); } - -void Trace(const char * c_szMsg) +void Trace(const char* c_szMsg) { #ifdef _DEBUG - OutputDebugString(c_szMsg); - printf("%s", c_szMsg); + DBG_OUT_W_UTF8(c_szMsg); + printf("%s", c_szMsg ? c_szMsg : ""); #endif - if (isLogFile) - LogFile(c_szMsg); + if (isLogFile) + LogFile(c_szMsg ? c_szMsg : ""); } void Tracen(const char* c_szMsg) { #ifdef _DEBUG - char szBuf[DEBUG_STRING_MAX_LEN+1]; - _snprintf(szBuf, sizeof(szBuf), "%s\n", c_szMsg); - OutputDebugString(szBuf); - puts(c_szMsg); + char szBuf[DEBUG_STRING_MAX_LEN + 2]; + _snprintf_s(szBuf, sizeof(szBuf), _TRUNCATE, "%s\n", c_szMsg ? c_szMsg : ""); - if (isLogFile) - LogFile(szBuf); + DBG_OUT_W_UTF8(szBuf); - puts(c_szMsg); - putc('\n', stdout); + fputs(szBuf, stdout); + + if (isLogFile) + LogFile(szBuf); #else - if (isLogFile) - { - LogFile(c_szMsg); - LogFile("\n"); - } + if (isLogFile) + { + LogFile(c_szMsg ? c_szMsg : ""); + LogFile("\n"); + } #endif } void Tracenf(const char* c_szFormat, ...) { - va_list args; - va_start(args, c_szFormat); + char szBuf[DEBUG_STRING_MAX_LEN + 2]; - char szBuf[DEBUG_STRING_MAX_LEN+2]; - int len = _vsnprintf(szBuf, sizeof(szBuf)-1, c_szFormat, args); + va_list args; + va_start(args, c_szFormat); + _vsnprintf_s(szBuf, sizeof(szBuf), _TRUNCATE, c_szFormat, args); + va_end(args); + + size_t cur = strnlen(szBuf, sizeof(szBuf)); + if (cur + 1 < sizeof(szBuf)) { + szBuf[cur] = '\n'; + szBuf[cur + 1] = '\0'; + } + else { + szBuf[sizeof(szBuf) - 2] = '\n'; + szBuf[sizeof(szBuf) - 1] = '\0'; + } - if (len > 0) - { - szBuf[len] = '\n'; - szBuf[len + 1] = '\0'; - } - va_end(args); #ifdef _DEBUG - OutputDebugString(szBuf); - printf("%s", szBuf); + DBG_OUT_W_UTF8(szBuf); + fputs(szBuf, stdout); #endif - if (isLogFile) - LogFile(szBuf); + if (isLogFile) + LogFile(szBuf); } void Tracef(const char* c_szFormat, ...) { - char szBuf[DEBUG_STRING_MAX_LEN+1]; + char szBuf[DEBUG_STRING_MAX_LEN + 1]; - va_list args; - va_start(args, c_szFormat); - _vsnprintf(szBuf, sizeof(szBuf), c_szFormat, args); - va_end(args); + va_list args; + va_start(args, c_szFormat); + _vsnprintf_s(szBuf, sizeof(szBuf), _TRUNCATE, c_szFormat, args); + va_end(args); #ifdef _DEBUG - OutputDebugString(szBuf); - fputs(szBuf, stdout); + DBG_OUT_W_UTF8(szBuf); + fputs(szBuf, stdout); #endif - if (isLogFile) - LogFile(szBuf); + if (isLogFile) + LogFile(szBuf); } void TraceError(const char* c_szFormat, ...) { #ifndef _DISTRIBUTE + char szBuf[DEBUG_STRING_MAX_LEN + 2]; - char szBuf[DEBUG_STRING_MAX_LEN+2]; + strncpy_s(szBuf, sizeof(szBuf), "SYSERR: ", _TRUNCATE); + int prefixLen = (int)strlen(szBuf); - strncpy(szBuf, "SYSERR: ", DEBUG_STRING_MAX_LEN); - int len = strlen(szBuf); + va_list args; + va_start(args, c_szFormat); + _vsnprintf_s(szBuf + prefixLen, sizeof(szBuf) - prefixLen, _TRUNCATE, c_szFormat, args); + va_end(args); - va_list args; - va_start(args, c_szFormat); - len = _vsnprintf(szBuf + len, sizeof(szBuf) - (len + 1), c_szFormat, args) + len; - va_end(args); + size_t cur = strnlen(szBuf, sizeof(szBuf)); + if (cur + 1 < sizeof(szBuf)) { + szBuf[cur] = '\n'; + szBuf[cur + 1] = '\0'; + } + else { + szBuf[sizeof(szBuf) - 2] = '\n'; + szBuf[sizeof(szBuf) - 1] = '\0'; + } - szBuf[len] = '\n'; - szBuf[len + 1] = '\0'; + time_t ct = time(0); + struct tm ctm = *localtime(&ct); - time_t ct = time(0); - struct tm ctm = *localtime(&ct); + fprintf(stderr, "%02d%02d %02d:%02d:%05d :: %s", + ctm.tm_mon + 1, + ctm.tm_mday, + ctm.tm_hour, + ctm.tm_min, + ELTimer_GetMSec() % 60000, + szBuf + 8); + fflush(stderr); - fprintf(stderr, "%02d%02d %02d:%02d:%05d :: %s", - ctm.tm_mon + 1, - ctm.tm_mday, - ctm.tm_hour, - ctm.tm_min, - ELTimer_GetMSec() % 60000, - szBuf + 8); - fflush(stderr); - #ifdef _DEBUG - OutputDebugString(szBuf); - fputs(szBuf, stdout); + DBG_OUT_W_UTF8(szBuf); + fputs(szBuf, stdout); #endif - if (isLogFile) - LogFile(szBuf); - + if (isLogFile) + LogFile(szBuf); #endif } @@ -242,96 +269,101 @@ void TraceErrorWithoutEnter(const char* c_szFormat, ...) { #ifndef _DISTRIBUTE - char szBuf[DEBUG_STRING_MAX_LEN]; + char szBuf[DEBUG_STRING_MAX_LEN]; - va_list args; - va_start(args, c_szFormat); - _vsnprintf(szBuf, sizeof(szBuf), c_szFormat, args); - va_end(args); + va_list args; + va_start(args, c_szFormat); + _vsnprintf_s(szBuf, sizeof(szBuf), _TRUNCATE, c_szFormat, args); + va_end(args); - time_t ct = time(0); - struct tm ctm = *localtime(&ct); + time_t ct = time(0); + struct tm ctm = *localtime(&ct); - fprintf(stderr, "%02d%02d %02d:%02d:%05d :: %s", - ctm.tm_mon + 1, - ctm.tm_mday, - ctm.tm_hour, - ctm.tm_min, - ELTimer_GetMSec() % 60000, - szBuf + 8); - fflush(stderr); + fprintf(stderr, "%02d%02d %02d:%02d:%05d :: %s", + ctm.tm_mon + 1, + ctm.tm_mday, + ctm.tm_hour, + ctm.tm_min, + ELTimer_GetMSec() % 60000, + szBuf + 8); + fflush(stderr); #ifdef _DEBUG - OutputDebugString(szBuf); - fputs(szBuf, stdout); + DBG_OUT_W_UTF8(szBuf); + fputs(szBuf, stdout); #endif - if (isLogFile) - LogFile(szBuf); + if (isLogFile) + LogFile(szBuf); #endif } void LogBoxf(const char* c_szFormat, ...) { - va_list args; - va_start(args, c_szFormat); + va_list args; + va_start(args, c_szFormat); - char szBuf[2048]; - _vsnprintf(szBuf, sizeof(szBuf), c_szFormat, args); + char szBuf[2048]; + _vsnprintf_s(szBuf, sizeof(szBuf), _TRUNCATE, c_szFormat, args); - LogBox(szBuf); + va_end(args); + + LogBox(szBuf); } void LogBox(const char* c_szMsg, const char* c_szCaption, HWND hWnd) { - if (!hWnd) - hWnd = g_PopupHwnd; + if (!hWnd) + hWnd = g_PopupHwnd; - std::wstring wMsg = Utf8ToWide(c_szMsg ? c_szMsg : ""); - std::wstring wCaption = Utf8ToWide(c_szCaption ? c_szCaption : "LOG"); + 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); + MessageBoxW(hWnd, wMsg.c_str(), wCaption.c_str(), MB_OK); - // Logging stays UTF-8 - Tracen(c_szMsg ? c_szMsg : ""); + // Logging stays UTF-8 + Tracen(c_szMsg ? c_szMsg : ""); } -void LogFile(const char * c_szMsg) +void LogFile(const char* c_szMsg) { - CLogFile::Instance().Write(c_szMsg); + CLogFile::Instance().Write(c_szMsg); } -void LogFilef(const char * c_szMessage, ...) +void LogFilef(const char* c_szMessage, ...) { - va_list args; - va_start(args, c_szMessage); - char szBuf[DEBUG_STRING_MAX_LEN+1]; - _vsnprintf(szBuf, sizeof(szBuf), c_szMessage, args); + va_list args; + va_start(args, c_szMessage); - CLogFile::Instance().Write(szBuf); + char szBuf[DEBUG_STRING_MAX_LEN + 1]; + _vsnprintf_s(szBuf, sizeof(szBuf), _TRUNCATE, c_szMessage, args); + + va_end(args); + + CLogFile::Instance().Write(szBuf); } void OpenLogFile(bool bUseLogFIle) { - if (!std::filesystem::exists("log")) { - std::filesystem::create_directory("log"); - } + if (!std::filesystem::exists("log")) { + std::filesystem::create_directory("log"); + } #ifndef _DISTRIBUTE - _wfreopen(L"log/syserr.txt", L"w", stderr); + _wfreopen(L"log/syserr.txt", L"w", stderr); - if (bUseLogFIle) - { - isLogFile = true; - CLogFile::Instance().Initialize(); - } + if (bUseLogFIle) + { + isLogFile = true; + CLogFile::Instance().Initialize(); + } #endif } void OpenConsoleWindow() { - AllocConsole(); + AllocConsole(); - _wfreopen(L"CONOUT$", L"a", stdout); - _wfreopen(L"CONIN$", L"r", stdin); + _wfreopen(L"CONOUT$", L"a", stdout); + _wfreopen(L"CONIN$", L"r", stdin); } diff --git a/src/EterImageLib/StdAfx.h b/src/EterImageLib/StdAfx.h index 128c8ea..b32fad2 100644 --- a/src/EterImageLib/StdAfx.h +++ b/src/EterImageLib/StdAfx.h @@ -24,16 +24,21 @@ inline void _TraceForImage(const char* c_szFormat, ...) { - va_list args; - va_start(args, c_szFormat); - - static char szBuf[1024]; - _vsnprintf(szBuf, sizeof(szBuf), c_szFormat, args); + va_list args; + va_start(args, c_szFormat); + + static char szBuf[1024]; + _vsnprintf_s(szBuf, sizeof(szBuf), _TRUNCATE, c_szFormat, args); + #ifdef _DEBUG - OutputDebugString(szBuf); + wchar_t wBuf[1024]; + MultiByteToWideChar(CP_UTF8, 0, szBuf, -1, wBuf, _countof(wBuf)); + OutputDebugStringW(wBuf); #endif - va_end(args); - printf(szBuf); + + va_end(args); + + fputs(szBuf, stdout); } #pragma warning(default:4018) From d3017b0ab01691129416a5f3af51e9afb7957b65 Mon Sep 17 00:00:00 2001 From: rtw1x1 Date: Fri, 26 Dec 2025 16:09:30 +0000 Subject: [PATCH 04/12] Macro removal --- src/UserInterface/InstanceBaseEffect.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/UserInterface/InstanceBaseEffect.cpp b/src/UserInterface/InstanceBaseEffect.cpp index 85e1d23..4f50084 100644 --- a/src/UserInterface/InstanceBaseEffect.cpp +++ b/src/UserInterface/InstanceBaseEffect.cpp @@ -666,9 +666,7 @@ void CInstanceBase::UpdateTextTailLevel(DWORD level) //static D3DXCOLOR s_kLevelColor = D3DXCOLOR(119.0f/255.0f, 246.0f/255.0f, 168.0f/255.0f, 1.0f); static D3DXCOLOR s_kLevelColor = D3DXCOLOR(152.0f/255.0f, 255.0f/255.0f, 51.0f/255.0f, 1.0f); -#if defined(__BL_LEVEL_FIX__) m_dwLevel = level; -#endif char szText[256]; sprintf(szText, "Lv %d", level); From 308511bd22724992ce4f33e944f90ffe62b22ef3 Mon Sep 17 00:00:00 2001 From: rtw1x1 Date: Sat, 27 Dec 2025 07:09:57 +0000 Subject: [PATCH 05/12] Fix hyperlink tags in Arabic --- src/EterLib/GrpTextInstance.cpp | 79 ++++++++++++++++++++++----------- 1 file changed, 54 insertions(+), 25 deletions(-) diff --git a/src/EterLib/GrpTextInstance.cpp b/src/EterLib/GrpTextInstance.cpp index 1d37345..22ada7b 100644 --- a/src/EterLib/GrpTextInstance.cpp +++ b/src/EterLib/GrpTextInstance.cpp @@ -299,6 +299,7 @@ void CGraphicTextInstance::Update() wchar_t ch; DWORD color; int linkIndex; // -1 = none, otherwise index into linkTargets + int logicalPos; // logical index in original wTextBuf (includes tags) }; auto ReorderTaggedWithBidi = [&](std::vector& vis, bool forceRTL) @@ -386,7 +387,7 @@ void CGraphicTextInstance::Update() else { // Regular visible character - logicalVis.push_back(TVisChar{ ch, curColor, activeLinkIndex }); + logicalVis.push_back(TVisChar{ ch, curColor, activeLinkIndex, i }); } i += 1; @@ -430,21 +431,6 @@ void CGraphicTextInstance::Update() i += tagLen; } - // Complete position mappings - const int logicalVisSize = (int)logicalVis.size(); - m_logicalToVisualPos[wTextLen] = logicalVisSize; - - m_visualToLogicalPos.resize((size_t)logicalVisSize + 1); - const size_t logicalToVisualSize = m_logicalToVisualPos.size(); - const int visualToLogicalSize = (int)m_visualToLogicalPos.size(); - - for (size_t i = 0; i < logicalToVisualSize; ++i) - { - const int visualPos = m_logicalToVisualPos[i]; - if (visualPos >= 0 && visualPos < visualToLogicalSize) - m_visualToLogicalPos[visualPos] = (int)i; - } - // ==================================================================== // PHASE 2: Apply BiDi to hyperlinks (if RTL text or RTL UI) // ==================================================================== @@ -564,7 +550,7 @@ void CGraphicTextInstance::Update() // Write back - handle size changes by erasing/inserting const int originalSize = linkLength; - const int newSize = linkVisualSize; + const int newSize = (int)linkVisual.size(); const int sizeDiff = newSize - originalSize; // Replace existing characters (cache min for performance) @@ -582,6 +568,7 @@ void CGraphicTextInstance::Update() { // Grew - insert new characters TVisChar templateChar = logicalVis[linkStart]; + templateChar.logicalPos = logicalVis[linkStart].logicalPos; for (int j = originalSize; j < newSize; ++j) { templateChar.ch = linkVisual[j]; @@ -652,6 +639,45 @@ void CGraphicTextInstance::Update() } } + // ==================================================================== + // FINAL: Rebuild visual<->logical mapping AFTER all BiDi/tag reordering + // ==================================================================== + + m_visualToLogicalPos.clear(); + m_logicalToVisualPos.clear(); + + // logical positions refer to indices in wTextBuf (tagged string) + m_logicalToVisualPos.resize((size_t)wTextLen + 1, -1); + m_visualToLogicalPos.resize((size_t)logicalVis.size() + 1, wTextLen); + + // Fill visual->logical from stored glyph origin + for (size_t v = 0; v < logicalVis.size(); ++v) + { + int lp = logicalVis[v].logicalPos; + if (lp < 0) lp = 0; + if (lp > wTextLen) lp = wTextLen; + + m_visualToLogicalPos[v] = lp; + + // For logical->visual, keep the first visual position that maps to lp + if (m_logicalToVisualPos[(size_t)lp] < 0) + m_logicalToVisualPos[(size_t)lp] = (int)v; + } + + // End positions + m_visualToLogicalPos[logicalVis.size()] = wTextLen; + m_logicalToVisualPos[(size_t)wTextLen] = (int)logicalVis.size(); + + // Fill gaps in logical->visual so cursor movement doesn't break on tag-only regions + int last = 0; + for (int i = 0; i <= wTextLen; ++i) + { + if (m_logicalToVisualPos[(size_t)i] < 0) + m_logicalToVisualPos[(size_t)i] = last; + else + last = m_logicalToVisualPos[(size_t)i]; + } + // ==================================================================== // PHASE 3: Render and build hyperlink ranges // ==================================================================== @@ -1431,10 +1457,6 @@ void CGraphicTextInstance::GetTextSize(int* pRetWidth, int* pRetHeight) int CGraphicTextInstance::PixelPositionToCharacterPosition(int iPixelPosition) { - // iPixelPosition is relative to the window - // Character positions in m_pCharInfoVector are relative to text start (0,0) - // We need to find which character the pixel falls on - // Clamp to valid range [0, textWidth] int adjustedPixelPos = iPixelPosition; if (adjustedPixelPos < 0) @@ -1442,14 +1464,23 @@ int CGraphicTextInstance::PixelPositionToCharacterPosition(int iPixelPosition) if (adjustedPixelPos > m_textWidth) adjustedPixelPos = m_textWidth; - // Find the character at the pixel position + // RTL: interpret click from right edge of rendered text + if (m_computedRTL) + adjustedPixelPos = m_textWidth - adjustedPixelPos; + int icurPosition = 0; int visualPos = -1; for (int i = 0; i < (int)m_pCharInfoVector.size(); ++i) { CGraphicFontTexture::TCharacterInfomation* pCurCharInfo = m_pCharInfoVector[i]; - icurPosition += pCurCharInfo->width; + + // Use advance instead of width (width is not reliable for cursor hit-testing) + int adv = pCurCharInfo->advance; + if (adv <= 0) + adv = pCurCharInfo->width; + + icurPosition += adv; if (adjustedPixelPos < icurPosition) { @@ -1458,11 +1489,9 @@ int CGraphicTextInstance::PixelPositionToCharacterPosition(int iPixelPosition) } } - // If not found, use end position if (visualPos < 0) visualPos = (int)m_pCharInfoVector.size(); - // Convert visual position to logical position (accounting for tags) if (!m_visualToLogicalPos.empty() && visualPos >= 0 && visualPos < (int)m_visualToLogicalPos.size()) return m_visualToLogicalPos[visualPos]; From 977e273764fe150bc72720082706d81130bdb193 Mon Sep 17 00:00:00 2001 From: savis <106487343+savisxss@users.noreply.github.com> Date: Sat, 27 Dec 2025 08:31:54 +0100 Subject: [PATCH 06/12] perf: optimize terrain garbage collector with early exit --- src/GameLib/MapOutdoorUpdate.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/GameLib/MapOutdoorUpdate.cpp b/src/GameLib/MapOutdoorUpdate.cpp index 5d05c3e..82cec12 100644 --- a/src/GameLib/MapOutdoorUpdate.cpp +++ b/src/GameLib/MapOutdoorUpdate.cpp @@ -831,6 +831,10 @@ void CMapOutdoor::__ClearGarvage() void CMapOutdoor::__UpdateGarvage() { + // Early exit if no garbage to collect - saves CPU cycles + if (m_TerrainDeleteVector.empty() && m_AreaDeleteVector.empty()) + return; + const DWORD dwTerrainEraseInterval = 1000 * 60; static DWORD dwEraseTime = ELTimer_GetMSec(); From 2422af51a8c06c3eb234b76a5c598617b435d2d2 Mon Sep 17 00:00:00 2001 From: Mind Rapist Date: Sat, 27 Dec 2025 10:12:27 +0200 Subject: [PATCH 07/12] Fixed PK mode bugs --- src/UserInterface/InstanceBase.cpp | 37 ++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/src/UserInterface/InstanceBase.cpp b/src/UserInterface/InstanceBase.cpp index 81e41a3..ca9205d 100644 --- a/src/UserInterface/InstanceBase.cpp +++ b/src/UserInterface/InstanceBase.cpp @@ -2113,11 +2113,28 @@ void CInstanceBase::SetPartyMemberFlag(bool bFlag) void CInstanceBase::SetStateFlags(DWORD dwStateFlags) { - if (dwStateFlags & ADD_CHARACTER_STATE_KILLER) - SetKiller(TRUE); - else - SetKiller(FALSE); + // MR-4: Fix PK Mode Bug + // Prevent killer mode for same-guild attacks in GUILD PK mode + bool skipKiller = false; + + if ((dwStateFlags & ADD_CHARACTER_STATE_KILLER) && PK_MODE_GUILD == GetPKMode()) { + CPythonPlayer& rkPlayer = CPythonPlayer::Instance(); + DWORD myGuildID = GetGuildID(); + DWORD mainGuildID = rkPlayer.GetGuildID(); + + if (myGuildID != 0 && myGuildID == mainGuildID) { + skipKiller = true; + } + } + + if (!skipKiller) { + if (dwStateFlags & ADD_CHARACTER_STATE_KILLER) + SetKiller(TRUE); + else + SetKiller(FALSE); + } + // MR-4: -- END OF -- Fix PK Mode Bug if (dwStateFlags & ADD_CHARACTER_STATE_PARTY) SetPartyMemberFlag(TRUE); else @@ -2238,10 +2255,20 @@ bool CInstanceBase::IsAttackableInstance(CInstanceBase& rkInstVictim) if (IsPVPInstance(rkInstVictim)) return true; + // MR-4: Fix PK Mode Bug if (PK_MODE_REVENGE == GetPKMode()) + { if (!IAbstractPlayer::GetSingleton().IsSamePartyMember(GetVirtualID(), rkInstVictim.GetVirtualID())) - if (IsConflictAlignmentInstance(rkInstVictim)) + { + if ( + (GetGuildID() == 0 || GetGuildID() != rkInstVictim.GetGuildID()) && + IsConflictAlignmentInstance(rkInstVictim) && + rkInstVictim.GetAlignment() < 0 + ) return true; + } + } + // MR-4: -- END OF -- Fix PK Mode Bug } else { From a4112cd12863c7520741b76fa952d70b63e800f7 Mon Sep 17 00:00:00 2001 From: rtw1x1 Date: Sat, 27 Dec 2025 18:50:42 +0000 Subject: [PATCH 08/12] fix: Better support UTF8 & Arabic --- src/UserInterface/StdAfx.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/UserInterface/StdAfx.h b/src/UserInterface/StdAfx.h index 2f0a3e7..c11f6bb 100644 --- a/src/UserInterface/StdAfx.h +++ b/src/UserInterface/StdAfx.h @@ -31,7 +31,7 @@ enum { POINT_MAX_NUM = 255, - CHARACTER_NAME_MAX_LEN = 24, + CHARACTER_NAME_MAX_LEN = 64, PLAYER_NAME_MAX_LEN = 12, }; From fff15def3c8785654111f2ffb5c5b840575b6638 Mon Sep 17 00:00:00 2001 From: savis <106487343+savisxss@users.noreply.github.com> Date: Sun, 28 Dec 2025 16:57:24 +0100 Subject: [PATCH 09/12] Fix PONG sequence sent as separate packet causing mismatch --- src/EterLib/NetStream.cpp | 5 +++++ src/EterLib/NetStream.h | 1 + src/UserInterface/AccountConnector.cpp | 5 ++--- src/UserInterface/GuildMarkDownloader.cpp | 6 ++---- src/UserInterface/GuildMarkUploader.cpp | 6 ++---- src/UserInterface/Packet.h | 1 + src/UserInterface/PythonNetworkStream.cpp | 6 ++---- 7 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/EterLib/NetStream.cpp b/src/EterLib/NetStream.cpp index 9705948..af492ff 100644 --- a/src/EterLib/NetStream.cpp +++ b/src/EterLib/NetStream.cpp @@ -857,6 +857,11 @@ bool CNetworkStream::SendSequence() return Send(sizeof(BYTE), &bSeq); } +uint8_t CNetworkStream::GetNextSequence() +{ + return m_SequenceGenerator(UINT8_MAX + 1); +} + bool CNetworkStream::OnProcess() { return true; diff --git a/src/EterLib/NetStream.h b/src/EterLib/NetStream.h index bd337d4..aba0009 100644 --- a/src/EterLib/NetStream.h +++ b/src/EterLib/NetStream.h @@ -53,6 +53,7 @@ class CNetworkStream void SetPacketSequenceMode(bool isOn); bool SendSequence(); + uint8_t GetNextSequence(); protected: virtual void OnConnectSuccess(); diff --git a/src/UserInterface/AccountConnector.cpp b/src/UserInterface/AccountConnector.cpp index f8e89eb..74061ac 100644 --- a/src/UserInterface/AccountConnector.cpp +++ b/src/UserInterface/AccountConnector.cpp @@ -288,12 +288,11 @@ bool CAccountConnector::__AuthState_SendPong() { TPacketCGPong kPacketPong; kPacketPong.bHeader = HEADER_CG_PONG; + kPacketPong.bSequence = GetNextSequence(); + if (!Send(sizeof(kPacketPong), &kPacketPong)) return false; - if (IsSecurityMode()) - return SendSequence(); - return true; } diff --git a/src/UserInterface/GuildMarkDownloader.cpp b/src/UserInterface/GuildMarkDownloader.cpp index 268226c..c3d4138 100644 --- a/src/UserInterface/GuildMarkDownloader.cpp +++ b/src/UserInterface/GuildMarkDownloader.cpp @@ -237,14 +237,12 @@ bool CGuildMarkDownloader::__LoginState_RecvPing() TPacketCGPong kPacketPong; kPacketPong.bHeader = HEADER_CG_PONG; + kPacketPong.bSequence = GetNextSequence(); if (!Send(sizeof(TPacketCGPong), &kPacketPong)) return false; - if (IsSecurityMode()) - return SendSequence(); - else - return true; + return true; } bool CGuildMarkDownloader::__LoginState_RecvPhase() diff --git a/src/UserInterface/GuildMarkUploader.cpp b/src/UserInterface/GuildMarkUploader.cpp index 46e6ffb..eabd2ee 100644 --- a/src/UserInterface/GuildMarkUploader.cpp +++ b/src/UserInterface/GuildMarkUploader.cpp @@ -376,14 +376,12 @@ bool CGuildMarkUploader::__LoginState_RecvPing() TPacketCGPong kPacketPong; kPacketPong.bHeader = HEADER_CG_PONG; + kPacketPong.bSequence = GetNextSequence(); if (!Send(sizeof(TPacketCGPong), &kPacketPong)) return false; - if (IsSecurityMode()) - return SendSequence(); - else - return true; + return true; } #ifdef _IMPROVED_PACKET_ENCRYPTION_ diff --git a/src/UserInterface/Packet.h b/src/UserInterface/Packet.h index 9685abe..f05c83c 100644 --- a/src/UserInterface/Packet.h +++ b/src/UserInterface/Packet.h @@ -1784,6 +1784,7 @@ typedef struct packet_ping typedef struct packet_pong { uint8_t bHeader; + uint8_t bSequence; } TPacketCGPong; typedef struct packet_script diff --git a/src/UserInterface/PythonNetworkStream.cpp b/src/UserInterface/PythonNetworkStream.cpp index fcd2bad..1d3d590 100644 --- a/src/UserInterface/PythonNetworkStream.cpp +++ b/src/UserInterface/PythonNetworkStream.cpp @@ -658,14 +658,12 @@ bool CPythonNetworkStream::RecvPingPacket() TPacketCGPong kPacketPong; kPacketPong.bHeader = HEADER_CG_PONG; + kPacketPong.bSequence = GetNextSequence(); if (!Send(sizeof(TPacketCGPong), &kPacketPong)) return false; - if (IsSecurityMode()) - return SendSequence(); - else - return true; + return true; } bool CPythonNetworkStream::RecvDefaultPacket(int header) From 55b2d704590ae4a011bcc2e740ad6c30026690ea Mon Sep 17 00:00:00 2001 From: Mind Rapist Date: Mon, 29 Dec 2025 00:21:32 +0200 Subject: [PATCH 10/12] MR-5: FlyTarget fixes --- README.md | 7 ++--- src/EffectLib/EffectManager.cpp | 11 ++++++++ src/EffectLib/EffectManager.h | 4 +++ src/GameLib/ActorInstanceRender.cpp | 12 ++++++++ src/GameLib/RaceMotionDataEvent.h | 8 +++--- .../PythonNetworkStreamPhaseGame.cpp | 28 +++++++++++++++++++ 6 files changed, 62 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 1d1ca5f..94c0a82 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,6 @@ This repository contains the source code necessary to compile the game client ex ## 📋 Changelog ### 🐛 Bug Fixes -* **Shaman Mounted Combat:** Fixed a bug that wrongly calculated Shaman characters mounted hits that didn't collide with the target to cause damage when attack speed was had an extremely high value. -* **Invisibility VFX Logic:** Fixed an issue where skill visual effects remained visible to the character while under the `AFFECT_INVISIBLE` state. -* **Buff Effects Visibility Recovery:** Fixed an issue where buff skill visual effects remained invisible if the skill was cast while the character was under the `AFFECT_INVISIBLE` state. -* **Casting Speed Cooldowns:** Fixed an issue where Casting Speed was not properly calculated in skill cooldowns. The system now supports real-time calculation updates whenever the bonus value changes. +* **Debug mode:** Fly effects are now registering when using Debug mode. +* **Fix effect rendering in low opacity models:** Effects now appear normally on semi-transparent meshes. +* **Fly targeting fixed for buff/healing skills:** Fixed an issue where fly target effect would render in the buffer's selected target even if the target was unbuffable (if viewing from another client). diff --git a/src/EffectLib/EffectManager.cpp b/src/EffectLib/EffectManager.cpp index 2037c39..de393cb 100644 --- a/src/EffectLib/EffectManager.cpp +++ b/src/EffectLib/EffectManager.cpp @@ -351,6 +351,17 @@ void CEffectManager::HideEffect() m_pSelectedEffectInstance->Hide(); } +// MR-5: Fix effect rendering when actor is semi-transparent +// Credits to d1str4ught +void CEffectManager::RenderEffect() +{ + if (!m_pSelectedEffectInstance) + return; + + m_pSelectedEffectInstance->Render(); +} +// MR-5: -- END OF -- Fix effect rendering when actor is semi-transparent + void CEffectManager::ApplyAlwaysHidden() { if (!m_pSelectedEffectInstance) diff --git a/src/EffectLib/EffectManager.h b/src/EffectLib/EffectManager.h index fb7e245..0014cef 100644 --- a/src/EffectLib/EffectManager.h +++ b/src/EffectLib/EffectManager.h @@ -56,6 +56,10 @@ class CEffectManager : public CScreen, public CSingleton void ShowEffect(); void HideEffect(); + // MR-5: Fix effect rendering when actor is semi-transparent + // Credits to d1str4ught + void RenderEffect(); + // MR-5: -- END OF -- Fix effect rendering when actor is semi-transparent void ApplyAlwaysHidden(); void ReleaseAlwaysHidden(); diff --git a/src/GameLib/ActorInstanceRender.cpp b/src/GameLib/ActorInstanceRender.cpp index d0f608f..81066f0 100644 --- a/src/GameLib/ActorInstanceRender.cpp +++ b/src/GameLib/ActorInstanceRender.cpp @@ -32,6 +32,18 @@ void CActorInstance::SetMaterialAlpha(DWORD dwAlpha) void CActorInstance::OnRender() { + // MR-5: Fix effect rendering when actor is semi-transparent + // Credits to d1str4ught + if (GetAlphaValue() < 1.0f) + { + for (auto it = m_AttachingEffectList.begin(); it != m_AttachingEffectList.end(); ++it) + { + CEffectManager::Instance().SelectEffectInstance(it->dwEffectIndex); + CEffectManager::Instance().RenderEffect(); + } + } + // MR-5: -- END OF -- Fix effect rendering when actor is semi-transparent + D3DMATERIAL9 kMtrl; STATEMANAGER.GetMaterial(&kMtrl); diff --git a/src/GameLib/RaceMotionDataEvent.h b/src/GameLib/RaceMotionDataEvent.h index 4503c9c..703165b 100644 --- a/src/GameLib/RaceMotionDataEvent.h +++ b/src/GameLib/RaceMotionDataEvent.h @@ -147,9 +147,9 @@ namespace NMotionEvent isFishingEffect = FALSE; } dwEffectIndex = GetCaseCRC32(strEffectFileName.c_str(), strEffectFileName.length()); -#ifndef _DEBUG +// #ifndef _DEBUG CEffectManager::Instance().RegisterEffect(strEffectFileName.c_str()); -#endif +// #endif return true; } @@ -188,10 +188,10 @@ namespace NMotionEvent return false; dwFlyIndex = GetCaseCRC32(strFlyFileName.c_str(), strFlyFileName.length()); -#ifndef _DEBUG +// #ifndef _DEBUG // Register Fly CFlyingManager::Instance().RegisterFlyingData(strFlyFileName.c_str()); -#endif +// #endif return true; } diff --git a/src/UserInterface/PythonNetworkStreamPhaseGame.cpp b/src/UserInterface/PythonNetworkStreamPhaseGame.cpp index f04ca00..7d535b2 100644 --- a/src/UserInterface/PythonNetworkStreamPhaseGame.cpp +++ b/src/UserInterface/PythonNetworkStreamPhaseGame.cpp @@ -1142,10 +1142,38 @@ bool CPythonNetworkStream::SendCharacterStatePacket(const TPixelPosition& c_rkPP // NOTE : SlotIndex는 임시 bool CPythonNetworkStream::SendUseSkillPacket(DWORD dwSkillIndex, DWORD dwTargetVID) { + // tw1x1 fix wrong fly targeting for viewing clients + if (dwTargetVID) + { + CPythonCharacterManager& rpcm = CPythonCharacterManager::Instance(); + CInstanceBase* pTarget = rpcm.GetInstancePtr(dwTargetVID); + + if (pTarget) + { + TPixelPosition kPos; + + pTarget->NEW_GetPixelPosition(&kPos); + SendFlyTargetingPacket(dwTargetVID, kPos); + } + else + { + TPixelPosition kPos; + + kPos.x = 0; + kPos.y = 0; + kPos.z = 0; + + SendFlyTargetingPacket(0, kPos); + } + } + // END OF tw1x1 fix wrong fly targeting for viewing clients + TPacketCGUseSkill UseSkillPacket; + UseSkillPacket.bHeader = HEADER_CG_USE_SKILL; UseSkillPacket.dwVnum = dwSkillIndex; UseSkillPacket.dwTargetVID = dwTargetVID; + if (!Send(sizeof(TPacketCGUseSkill), &UseSkillPacket)) { Tracen("CPythonNetworkStream::SendUseSkillPacket - SEND PACKET ERROR"); From 08228b1ff9cfb67c21437d2827592c9038eab0d8 Mon Sep 17 00:00:00 2001 From: Mind Rapist Date: Mon, 29 Dec 2025 18:37:39 +0200 Subject: [PATCH 11/12] MR-5: FlyTarget fixes --- src/UserInterface/PythonNetworkStreamPhaseGame.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/UserInterface/PythonNetworkStreamPhaseGame.cpp b/src/UserInterface/PythonNetworkStreamPhaseGame.cpp index 7d535b2..960a89b 100644 --- a/src/UserInterface/PythonNetworkStreamPhaseGame.cpp +++ b/src/UserInterface/PythonNetworkStreamPhaseGame.cpp @@ -1142,6 +1142,12 @@ bool CPythonNetworkStream::SendCharacterStatePacket(const TPixelPosition& c_rkPP // NOTE : SlotIndex는 임시 bool CPythonNetworkStream::SendUseSkillPacket(DWORD dwSkillIndex, DWORD dwTargetVID) { + TPacketCGUseSkill UseSkillPacket; + + UseSkillPacket.bHeader = HEADER_CG_USE_SKILL; + UseSkillPacket.dwVnum = dwSkillIndex; + UseSkillPacket.dwTargetVID = dwTargetVID; + // tw1x1 fix wrong fly targeting for viewing clients if (dwTargetVID) { @@ -1168,12 +1174,6 @@ bool CPythonNetworkStream::SendUseSkillPacket(DWORD dwSkillIndex, DWORD dwTarget } // END OF tw1x1 fix wrong fly targeting for viewing clients - TPacketCGUseSkill UseSkillPacket; - - UseSkillPacket.bHeader = HEADER_CG_USE_SKILL; - UseSkillPacket.dwVnum = dwSkillIndex; - UseSkillPacket.dwTargetVID = dwTargetVID; - if (!Send(sizeof(TPacketCGUseSkill), &UseSkillPacket)) { Tracen("CPythonNetworkStream::SendUseSkillPacket - SEND PACKET ERROR"); From 96876420d151f174e82f16d0dc4d13bd20262ff0 Mon Sep 17 00:00:00 2001 From: Ricardo Domingues Date: Tue, 30 Dec 2025 17:37:42 +0000 Subject: [PATCH 12/12] Increased horse rotation speed --- src/UserInterface/InstanceBase.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/UserInterface/InstanceBase.cpp b/src/UserInterface/InstanceBase.cpp index ca9205d..8d53629 100644 --- a/src/UserInterface/InstanceBase.cpp +++ b/src/UserInterface/InstanceBase.cpp @@ -15,7 +15,7 @@ BOOL HAIR_COLOR_ENABLE=FALSE; BOOL USE_ARMOR_SPECULAR=FALSE; BOOL RIDE_HORSE_ENABLE=TRUE; const float c_fDefaultRotationSpeed = 1200.0f; -const float c_fDefaultHorseRotationSpeed = 300.0f; +const float c_fDefaultHorseRotationSpeed = 1800.0f; bool IsWall(unsigned race)