diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 9607b20..8b192e0 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -77,7 +77,10 @@ "Bash(find /c/Projects/CLionProjects/ANSCORE -type d -name *ANSODEngine*)", "Bash(powershell -Command \"\\(Get-Content ''C:\\\\Users\\\\nghia\\\\Downloads\\\\ANSLEGION41.log''\\).Count\")", "Bash(powershell -Command \"\\(Get-Content ''C:\\\\Users\\\\nghia\\\\Downloads\\\\ANSLEGION42.log''\\).Count\")", - "Bash(grep -rn \"ApplyTracking\\\\|_trackerEnabled\" /c/Projects/CLionProjects/ANSCORE/modules/ANSODEngine/*.cpp)" + "Bash(grep -rn \"ApplyTracking\\\\|_trackerEnabled\" /c/Projects/CLionProjects/ANSCORE/modules/ANSODEngine/*.cpp)", + "Bash(cmake --build build --target ANSUtilities)", + "Bash(ls -d /c/Projects/CLionProjects/ANSCORE/cmake-build-* /c/Projects/CLionProjects/ANSCORE/out/*)", + "Bash(cmake --build /c/Projects/CLionProjects/ANSCORE/cmake-build-release --target ANSUtilities)" ] } } diff --git a/modules/ANSUtilities/ANSUtilities.cpp b/modules/ANSUtilities/ANSUtilities.cpp index bdacf07..b5e1df5 100644 --- a/modules/ANSUtilities/ANSUtilities.cpp +++ b/modules/ANSUtilities/ANSUtilities.cpp @@ -1002,5 +1002,55 @@ namespace ANSCENTER return utf8Str; #endif } + + std::string ANSUtilities::ConvertUTF16LEToUnicodeEscapes(const char* utf16leBytes, int byteLen) { + if (!utf16leBytes || byteLen <= 0) return ""; + int offset = 0; + // Strip BOM (FF FE) if present + if (byteLen >= 2 && + static_cast(utf16leBytes[0]) == 0xFF && + static_cast(utf16leBytes[1]) == 0xFE) { + offset = 2; + } + int remaining = byteLen - offset; + if (remaining <= 0 || remaining % 2 != 0) return ""; + + std::string result; + result.reserve(remaining * 3); + for (int i = offset; i + 1 < byteLen; i += 2) { + uint16_t codepoint = static_cast(utf16leBytes[i]) + | (static_cast(utf16leBytes[i + 1]) << 8); + if (codepoint >= 0x20 && codepoint <= 0x7E) { + result += static_cast(codepoint); + } else { + char buf[7]; + snprintf(buf, sizeof(buf), "\\u%04X", codepoint); + result += buf; + } + } + return result; + } + + std::string ANSUtilities::ConvertUnicodeEscapesToUTF8(const std::string& escapedStr) { + if (escapedStr.empty()) return ""; + // First decode \uXXXX to UTF-16LE, then convert to UTF-8 + std::string utf16le; + utf16le.reserve(escapedStr.size() * 2); + size_t i = 0; + while (i < escapedStr.size()) { + if (i + 5 < escapedStr.size() && escapedStr[i] == '\\' && escapedStr[i + 1] == 'u') { + char hex[5] = { escapedStr[i + 2], escapedStr[i + 3], escapedStr[i + 4], escapedStr[i + 5], 0 }; + uint16_t codepoint = (uint16_t)strtoul(hex, nullptr, 16); + utf16le += static_cast(codepoint & 0xFF); + utf16le += static_cast((codepoint >> 8) & 0xFF); + i += 6; + } else { + utf16le += escapedStr[i]; + utf16le += '\0'; + i++; + } + } + return ConvertUTF16LEToUTF8(utf16le.data(), (int)utf16le.size()); + } } diff --git a/modules/ANSUtilities/ANSUtilities.h b/modules/ANSUtilities/ANSUtilities.h index 56608ad..e07cf8b 100644 --- a/modules/ANSUtilities/ANSUtilities.h +++ b/modules/ANSUtilities/ANSUtilities.h @@ -102,6 +102,16 @@ namespace ANSCENTER { // Convert a UTF-16LE byte string to UTF-8. // Useful for receiving Unicode text from LabVIEW and converting to UTF-8 for internal processing. static std::string ConvertUTF16LEToUTF8(const char* utf16leBytes, int byteLen); + + // Convert UTF-16LE byte string (with optional BOM) to Unicode escape sequences (\uXXXX). + // Input: raw UTF-16LE bytes (BOM is auto-stripped if present). + // Output: ASCII string with \uXXXX for non-ASCII codepoints, plain ASCII chars preserved. + static std::string ConvertUTF16LEToUnicodeEscapes(const char* utf16leBytes, int byteLen); + + // Convert Unicode escape sequences (\uXXXX) to UTF-8 string. + // Input: ASCII string with \uXXXX escapes (e.g., "\\u6c5f\\u6771 599") + // Output: UTF-8 encoded Unicode string. + static std::string ConvertUnicodeEscapesToUTF8(const std::string& escapedStr); }; // Connection bundle for pool struct S3Connection { @@ -247,6 +257,8 @@ extern "C" ANSULT_API int RebootSystem(); extern "C" ANSULT_API int ANSConvertUTF8ToUTF16LE(const char* utf8Str, LStrHandle result, int includeBOM = 1); extern "C" ANSULT_API int ANSDecodeJsonUnicodeToUTF16LE(const char* escapedStr, LStrHandle result); extern "C" ANSULT_API int ANSConvertUTF16LEToUTF8(const unsigned char* utf16leBytes, int byteLen, LStrHandle result); +extern "C" ANSULT_API int ANSConvertUTF16LEToUnicodeEscapes(const unsigned char* utf16leBytes, int byteLen, LStrHandle result); +extern "C" ANSULT_API int ANSConvertUnicodeEscapesToUTF8(const char* escapedStr, LStrHandle result); // AWS S3 class diff --git a/modules/ANSUtilities/dllmain.cpp b/modules/ANSUtilities/dllmain.cpp index 1903419..225e6f1 100644 --- a/modules/ANSUtilities/dllmain.cpp +++ b/modules/ANSUtilities/dllmain.cpp @@ -893,9 +893,40 @@ extern "C" ANSULT_API int ANSDecodeJsonUnicodeToUTF16LE(const char* escapedStr, catch (...) { return -1; } } +extern "C" ANSULT_API int ANSConvertUTF16LEToUnicodeEscapes(const unsigned char* utf16leBytes, int byteLen, LStrHandle result) { + try { + if (!utf16leBytes || byteLen <= 0 || !result) return -1; + std::string escaped = ANSCENTER::ANSUtilities::ConvertUTF16LEToUnicodeEscapes( + reinterpret_cast(utf16leBytes), byteLen); + if (escaped.empty()) return 0; + int size = static_cast(escaped.size()); + MgErr error = DSSetHandleSize(result, sizeof(int32) + size * sizeof(uChar)); + if (error != noErr) return -2; + (*result)->cnt = size; + memcpy((*result)->str, escaped.data(), size); + return 1; + } + catch (...) { return -1; } +} +extern "C" ANSULT_API int ANSConvertUnicodeEscapesToUTF8(const char* escapedStr, LStrHandle result) { + try { + if (!escapedStr || !result) return -1; + int len = (int)strlen(escapedStr); + if (len == 0) return 0; + std::string utf8 = ANSCENTER::ANSUtilities::ConvertUnicodeEscapesToUTF8(escapedStr); + if (utf8.empty()) return 0; + int size = static_cast(utf8.size()); + MgErr error = DSSetHandleSize(result, sizeof(int32) + size * sizeof(uChar)); + if (error != noErr) return -2; + (*result)->cnt = size; + memcpy((*result)->str, utf8.data(), size); + return 1; + } + catch (...) { return -1; } +} -// AWSS3 +// AWSS3 extern "C" ANSULT_API int CreateANSAWSHandle(ANSCENTER::ANSAWSS3** Handle, const char* licenseKey) { if (Handle == nullptr || licenseKey == nullptr) return 0; if (*Handle) {