summaryrefslogtreecommitdiffstats
path: root/winpr/libwinpr/crt
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--winpr/libwinpr/crt/CMakeLists.txt46
-rw-r--r--winpr/libwinpr/crt/ModuleOptions.cmake9
-rw-r--r--winpr/libwinpr/crt/alignment.c268
-rw-r--r--winpr/libwinpr/crt/buffer.c50
-rw-r--r--winpr/libwinpr/crt/casing.c726
-rw-r--r--winpr/libwinpr/crt/conversion.c46
-rw-r--r--winpr/libwinpr/crt/memory.c42
-rw-r--r--winpr/libwinpr/crt/string.c832
-rw-r--r--winpr/libwinpr/crt/test/CMakeLists.txt30
-rw-r--r--winpr/libwinpr/crt/test/TestAlignment.c87
-rw-r--r--winpr/libwinpr/crt/test/TestFormatSpecifiers.c164
-rw-r--r--winpr/libwinpr/crt/test/TestString.c188
-rw-r--r--winpr/libwinpr/crt/test/TestTypes.c115
-rw-r--r--winpr/libwinpr/crt/test/TestUnicodeConversion.c1289
-rw-r--r--winpr/libwinpr/crt/unicode.c657
-rw-r--r--winpr/libwinpr/crt/unicode.h32
-rw-r--r--winpr/libwinpr/crt/unicode_android.c183
-rw-r--r--winpr/libwinpr/crt/unicode_apple.m146
-rw-r--r--winpr/libwinpr/crt/unicode_builtin.c695
-rw-r--r--winpr/libwinpr/crt/unicode_icu.c237
20 files changed, 5842 insertions, 0 deletions
diff --git a/winpr/libwinpr/crt/CMakeLists.txt b/winpr/libwinpr/crt/CMakeLists.txt
new file mode 100644
index 0000000..d82e64f
--- /dev/null
+++ b/winpr/libwinpr/crt/CMakeLists.txt
@@ -0,0 +1,46 @@
+# WinPR: Windows Portable Runtime
+# libwinpr-crt cmake build script
+#
+# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set (CRT_FILES alignment.c
+ conversion.c
+ buffer.c
+ memory.c
+ unicode.c
+ string.c)
+
+if(WITH_UNICODE_BUILTIN)
+ list(APPEND CRT_FILES unicode_builtin.c)
+else()
+ IF (ANDROID)
+ list(APPEND CRT_FILES unicode_android.c)
+ elseif (NOT APPLE AND NOT WIN32)
+ find_package(ICU REQUIRED i18n uc io data)
+ list(APPEND CRT_FILES unicode_icu.c)
+ winpr_include_directory_add(${ICU_INCLUDE_DIRS})
+ winpr_library_add_private(${ICU_LIBRARIES})
+ elseif(APPLE)
+ list(APPEND CRT_FILES unicode_apple.m)
+ find_library(FOUNDATION_FRAMEWORK Foundation REQUIRED)
+ winpr_library_add_private(${FOUNDATION_FRAMEWORK})
+ endif()
+endif()
+
+winpr_module_add(${CRT_FILES})
+
+if(BUILD_TESTING)
+ add_subdirectory(test)
+endif()
diff --git a/winpr/libwinpr/crt/ModuleOptions.cmake b/winpr/libwinpr/crt/ModuleOptions.cmake
new file mode 100644
index 0000000..4530a74
--- /dev/null
+++ b/winpr/libwinpr/crt/ModuleOptions.cmake
@@ -0,0 +1,9 @@
+
+set(MINWIN_LAYER "0")
+set(MINWIN_GROUP "none")
+set(MINWIN_MAJOR_VERSION "0")
+set(MINWIN_MINOR_VERSION "0")
+set(MINWIN_SHORT_NAME "crt")
+set(MINWIN_LONG_NAME "Microsoft C Run-Time")
+set(MODULE_LIBRARY_NAME "${MINWIN_SHORT_NAME}")
+
diff --git a/winpr/libwinpr/crt/alignment.c b/winpr/libwinpr/crt/alignment.c
new file mode 100644
index 0000000..e313c2d
--- /dev/null
+++ b/winpr/libwinpr/crt/alignment.c
@@ -0,0 +1,268 @@
+/**
+ * WinPR: Windows Portable Runtime
+ * Data Alignment
+ *
+ * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <winpr/config.h>
+
+#include <winpr/crt.h>
+
+/* Data Alignment: http://msdn.microsoft.com/en-us/library/fs9stz4e/ */
+
+#if !defined(_WIN32) || (defined(__MINGW32__) && !defined(_UCRT))
+
+#include <stdint.h>
+#include <limits.h>
+
+#define WINPR_ALIGNED_MEM_SIGNATURE 0x0BA0BAB
+
+#define WINPR_ALIGNED_MEM_STRUCT_FROM_PTR(_memptr) \
+ (WINPR_ALIGNED_MEM*)(((size_t)(((BYTE*)_memptr) - sizeof(WINPR_ALIGNED_MEM))))
+
+#include <stdlib.h>
+
+#if defined(__APPLE__)
+#include <malloc/malloc.h>
+#elif defined(__FreeBSD__) || defined(__OpenBSD__)
+#include <stdlib.h>
+#else
+#include <malloc.h>
+#endif
+
+#include "../log.h"
+#define TAG WINPR_TAG("crt")
+
+struct winpr_aligned_mem
+{
+ UINT32 sig;
+ size_t size;
+ void* base_addr;
+};
+typedef struct winpr_aligned_mem WINPR_ALIGNED_MEM;
+
+void* winpr_aligned_malloc(size_t size, size_t alignment)
+{
+ return winpr_aligned_offset_malloc(size, alignment, 0);
+}
+
+void* winpr_aligned_calloc(size_t count, size_t size, size_t alignment)
+{
+ return winpr_aligned_recalloc(NULL, count, size, alignment);
+}
+
+void* winpr_aligned_realloc(void* memblock, size_t size, size_t alignment)
+{
+ return winpr_aligned_offset_realloc(memblock, size, alignment, 0);
+}
+
+void* winpr_aligned_recalloc(void* memblock, size_t num, size_t size, size_t alignment)
+{
+ return winpr_aligned_offset_recalloc(memblock, num, size, alignment, 0);
+}
+
+void* winpr_aligned_offset_malloc(size_t size, size_t alignment, size_t offset)
+{
+ size_t header = 0;
+ size_t alignsize = 0;
+ uintptr_t basesize = 0;
+ void* base = NULL;
+ void* memblock = NULL;
+ WINPR_ALIGNED_MEM* pMem = NULL;
+
+ /* alignment must be a power of 2 */
+ if (alignment % 2 == 1)
+ return NULL;
+
+ /* offset must be less than size */
+ if (offset >= size)
+ return NULL;
+
+ /* minimum alignment is pointer size */
+ if (alignment < sizeof(void*))
+ alignment = sizeof(void*);
+
+ if (alignment > SIZE_MAX - sizeof(WINPR_ALIGNED_MEM))
+ return NULL;
+
+ header = sizeof(WINPR_ALIGNED_MEM) + alignment;
+
+ if (size > SIZE_MAX - header)
+ return NULL;
+
+ alignsize = size + header;
+ /* malloc size + alignment to make sure we can align afterwards */
+#if defined(_ISOC11_SOURCE)
+ base = aligned_alloc(alignment, alignsize);
+#elif _POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600
+ if (posix_memalign(&base, alignment, alignsize) != 0)
+ return NULL;
+#else
+ base = malloc(alignsize);
+#endif
+ if (!base)
+ return NULL;
+
+ basesize = (uintptr_t)base;
+
+ if ((offset > UINTPTR_MAX) || (header > UINTPTR_MAX - offset) ||
+ (basesize > UINTPTR_MAX - header - offset))
+ {
+ free(base);
+ return NULL;
+ }
+
+ memblock = (void*)(((basesize + header + offset) & ~(alignment - 1)) - offset);
+ pMem = WINPR_ALIGNED_MEM_STRUCT_FROM_PTR(memblock);
+ pMem->sig = WINPR_ALIGNED_MEM_SIGNATURE;
+ pMem->base_addr = base;
+ pMem->size = size;
+ return memblock;
+}
+
+void* winpr_aligned_offset_realloc(void* memblock, size_t size, size_t alignment, size_t offset)
+{
+ size_t copySize = 0;
+ void* newMemblock = NULL;
+ WINPR_ALIGNED_MEM* pMem = NULL;
+ WINPR_ALIGNED_MEM* pNewMem = NULL;
+
+ if (!memblock)
+ return winpr_aligned_offset_malloc(size, alignment, offset);
+
+ pMem = WINPR_ALIGNED_MEM_STRUCT_FROM_PTR(memblock);
+
+ if (pMem->sig != WINPR_ALIGNED_MEM_SIGNATURE)
+ {
+ WLog_ERR(TAG,
+ "_aligned_offset_realloc: memory block was not allocated by _aligned_malloc!");
+ return NULL;
+ }
+
+ if (size == 0)
+ {
+ winpr_aligned_free(memblock);
+ return NULL;
+ }
+
+ newMemblock = winpr_aligned_offset_malloc(size, alignment, offset);
+
+ if (!newMemblock)
+ return NULL;
+
+ pNewMem = WINPR_ALIGNED_MEM_STRUCT_FROM_PTR(newMemblock);
+ copySize = (pNewMem->size < pMem->size) ? pNewMem->size : pMem->size;
+ CopyMemory(newMemblock, memblock, copySize);
+ winpr_aligned_free(memblock);
+ return newMemblock;
+}
+
+static INLINE size_t cMIN(size_t a, size_t b)
+{
+ if (a > b)
+ return b;
+ return a;
+}
+
+void* winpr_aligned_offset_recalloc(void* memblock, size_t num, size_t size, size_t alignment,
+ size_t offset)
+{
+ char* newMemblock = NULL;
+ WINPR_ALIGNED_MEM* pMem = NULL;
+ WINPR_ALIGNED_MEM* pNewMem = NULL;
+
+ if (!memblock)
+ {
+ newMemblock = winpr_aligned_offset_malloc(size * num, alignment, offset);
+
+ if (newMemblock)
+ {
+ pNewMem = WINPR_ALIGNED_MEM_STRUCT_FROM_PTR(newMemblock);
+ ZeroMemory(newMemblock, pNewMem->size);
+ }
+
+ return newMemblock;
+ }
+
+ pMem = WINPR_ALIGNED_MEM_STRUCT_FROM_PTR(memblock);
+
+ if (pMem->sig != WINPR_ALIGNED_MEM_SIGNATURE)
+ {
+ WLog_ERR(TAG,
+ "_aligned_offset_recalloc: memory block was not allocated by _aligned_malloc!");
+ goto fail;
+ }
+
+ if ((num == 0) || (size == 0))
+ goto fail;
+
+ if (pMem->size > (1ull * num * size) + alignment)
+ return memblock;
+
+ newMemblock = winpr_aligned_offset_malloc(size * num, alignment, offset);
+
+ if (!newMemblock)
+ goto fail;
+
+ pNewMem = WINPR_ALIGNED_MEM_STRUCT_FROM_PTR(newMemblock);
+ {
+ const size_t csize = cMIN(pMem->size, pNewMem->size);
+ memcpy(newMemblock, memblock, csize);
+ ZeroMemory(newMemblock + csize, pNewMem->size - csize);
+ }
+fail:
+ winpr_aligned_free(memblock);
+ return newMemblock;
+}
+
+size_t winpr_aligned_msize(void* memblock, size_t alignment, size_t offset)
+{
+ WINPR_ALIGNED_MEM* pMem = NULL;
+
+ if (!memblock)
+ return 0;
+
+ pMem = WINPR_ALIGNED_MEM_STRUCT_FROM_PTR(memblock);
+
+ if (pMem->sig != WINPR_ALIGNED_MEM_SIGNATURE)
+ {
+ WLog_ERR(TAG, "_aligned_msize: memory block was not allocated by _aligned_malloc!");
+ return 0;
+ }
+
+ return pMem->size;
+}
+
+void winpr_aligned_free(void* memblock)
+{
+ WINPR_ALIGNED_MEM* pMem = NULL;
+
+ if (!memblock)
+ return;
+
+ pMem = WINPR_ALIGNED_MEM_STRUCT_FROM_PTR(memblock);
+
+ if (pMem->sig != WINPR_ALIGNED_MEM_SIGNATURE)
+ {
+ WLog_ERR(TAG, "_aligned_free: memory block was not allocated by _aligned_malloc!");
+ return;
+ }
+
+ free(pMem->base_addr);
+}
+
+#endif /* _WIN32 */
diff --git a/winpr/libwinpr/crt/buffer.c b/winpr/libwinpr/crt/buffer.c
new file mode 100644
index 0000000..fc914c9
--- /dev/null
+++ b/winpr/libwinpr/crt/buffer.c
@@ -0,0 +1,50 @@
+/**
+ * WinPR: Windows Portable Runtime
+ * Buffer Manipulation
+ *
+ * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <winpr/config.h>
+
+#include <winpr/crt.h>
+
+/* Buffer Manipulation: http://msdn.microsoft.com/en-us/library/b3893xdy/ */
+
+#ifndef _WIN32
+
+#include <string.h>
+
+errno_t memmove_s(void* dest, size_t numberOfElements, const void* src, size_t count)
+{
+ if (count > numberOfElements)
+ return -1;
+
+ memmove(dest, src, count);
+
+ return 0;
+}
+
+errno_t wmemmove_s(WCHAR* dest, size_t numberOfElements, const WCHAR* src, size_t count)
+{
+ if (count * 2 > numberOfElements)
+ return -1;
+
+ memmove(dest, src, count * 2);
+
+ return 0;
+}
+
+#endif
diff --git a/winpr/libwinpr/crt/casing.c b/winpr/libwinpr/crt/casing.c
new file mode 100644
index 0000000..36485ab
--- /dev/null
+++ b/winpr/libwinpr/crt/casing.c
@@ -0,0 +1,726 @@
+/**
+ * Unicode case mappings
+ *
+ * This code is generated by wine's make_unicode script
+ * which downloads data from unicode.org and produces
+ * readily usable conversion tables.
+ *
+ * After asking permission from Alexandre Julliard in May 2011,
+ * it was clarified that no copyright was claimed by the wine
+ * project on the script's generated output.
+ */
+
+#define WINPR_TOLOWERW(_wch) \
+ (_wch + winpr_casemap_lower[winpr_casemap_lower[_wch >> 8] + (_wch & 0xFF)])
+
+#define WINPR_TOUPPERW(_wch) \
+ (_wch + winpr_casemap_upper[winpr_casemap_upper[_wch >> 8] + (_wch & 0xFF)])
+
+static const WCHAR winpr_casemap_lower[3807] = {
+ /* index */
+ 0x01bf, 0x02bf, 0x03bf, 0x044f, 0x054f, 0x064f, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+ 0x0100, 0x0100, 0x0100, 0x0100, 0x06af, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+ 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x07af, 0x08ae, 0x0100, 0x09ab, 0x0100, 0x0100,
+ 0x0a2f, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0b2f, 0x0100, 0x0100, 0x0100,
+ 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+ 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+ 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+ 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+ 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+ 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+ 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+ 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+ 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+ 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0c22, 0x0d00,
+ 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+ 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+ 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+ 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+ 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+ 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+ 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+ 0x0100, 0x0100, 0x0100, 0x0ddf,
+ /* defaults */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 0x0041 .. 0x00ff */
+ 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020,
+ 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020,
+ 0x0020, 0x0020, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020,
+ 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020,
+ 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0000, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020,
+ 0x0020, 0x0020, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 0x0100 .. 0x01ff */
+ 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+ 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+ 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+ 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+ 0xff39, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001,
+ 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001,
+ 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+ 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+ 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+ 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+ 0xff87, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x00d2, 0x0001, 0x0000,
+ 0x0001, 0x0000, 0x00ce, 0x0001, 0x0000, 0x00cd, 0x00cd, 0x0001, 0x0000, 0x0000, 0x004f, 0x00ca,
+ 0x00cb, 0x0001, 0x0000, 0x00cd, 0x00cf, 0x0000, 0x00d3, 0x00d1, 0x0001, 0x0000, 0x0000, 0x0000,
+ 0x00d3, 0x00d5, 0x0000, 0x00d6, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x00da, 0x0001,
+ 0x0000, 0x00da, 0x0000, 0x0000, 0x0001, 0x0000, 0x00da, 0x0001, 0x0000, 0x00d9, 0x00d9, 0x0001,
+ 0x0000, 0x0001, 0x0000, 0x00db, 0x0001, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0001,
+ 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001,
+ 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+ 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+ 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0xff9f, 0xffc8, 0x0001, 0x0000, 0x0001, 0x0000,
+ 0x0001, 0x0000, 0x0001, 0x0000,
+ /* 0x0200 .. 0x02ff */
+ 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+ 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+ 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0xff7e, 0x0000, 0x0001, 0x0000,
+ 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+ 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2a2b, 0x0001,
+ 0x0000, 0xff5d, 0x2a28, 0x0000, 0x0000, 0x0001, 0x0000, 0xff3d, 0x0045, 0x0047, 0x0001, 0x0000,
+ 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 0x0370 .. 0x03ff */
+ 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0026, 0x0000,
+ 0x0025, 0x0025, 0x0025, 0x0000, 0x0040, 0x0000, 0x003f, 0x003f, 0x0000, 0x0020, 0x0020, 0x0020,
+ 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020,
+ 0x0020, 0x0020, 0x0000, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0008,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+ 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+ 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0xffc4, 0x0000, 0x0000, 0x0001, 0x0000, 0xfff9, 0x0001, 0x0000, 0x0000, 0xff7e, 0xff7e, 0xff7e,
+ /* 0x0400 .. 0x04ff */
+ 0x0050, 0x0050, 0x0050, 0x0050, 0x0050, 0x0050, 0x0050, 0x0050, 0x0050, 0x0050, 0x0050, 0x0050,
+ 0x0050, 0x0050, 0x0050, 0x0050, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020,
+ 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020,
+ 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+ 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+ 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+ 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+ 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+ 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+ 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+ 0x000f, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001,
+ 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+ 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+ 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+ 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+ 0x0001, 0x0000, 0x0001, 0x0000,
+ /* 0x0500 .. 0x05ff */
+ 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+ 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+ 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+ 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030,
+ 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030,
+ 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030,
+ 0x0030, 0x0030, 0x0030, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 0x10a0 .. 0x10ff */
+ 0x1c60, 0x1c60, 0x1c60, 0x1c60, 0x1c60, 0x1c60, 0x1c60, 0x1c60, 0x1c60, 0x1c60, 0x1c60, 0x1c60,
+ 0x1c60, 0x1c60, 0x1c60, 0x1c60, 0x1c60, 0x1c60, 0x1c60, 0x1c60, 0x1c60, 0x1c60, 0x1c60, 0x1c60,
+ 0x1c60, 0x1c60, 0x1c60, 0x1c60, 0x1c60, 0x1c60, 0x1c60, 0x1c60, 0x1c60, 0x1c60, 0x1c60, 0x1c60,
+ 0x1c60, 0x1c60, 0x0000, 0x1c60, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1c60, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 0x1e00 .. 0x1eff */
+ 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+ 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+ 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+ 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+ 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+ 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+ 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+ 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+ 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+ 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+ 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+ 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+ 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0xe241, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+ 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+ 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+ 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+ 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+ 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+ 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+ 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+ 0x0001, 0x0000, 0x0001, 0x0000,
+ /* 0x1f01 .. 0x1fff */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0xfff8,
+ 0xfff8, 0xfff8, 0xfff8, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xfff8,
+ 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0xfff8,
+ 0xfff8, 0xfff8, 0xfff8, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xfff8,
+ 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0xfff8, 0x0000, 0xfff8, 0x0000, 0xfff8, 0x0000, 0xfff8, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0xfff8,
+ 0xfff8, 0xfff8, 0xfff8, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0xfff8,
+ 0xfff8, 0xfff8, 0xfff8, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xfff8,
+ 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0xfff8, 0xfff8, 0xffb6, 0xffb6, 0xfff7, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffaa, 0xffaa, 0xffaa, 0xffaa, 0xfff7,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xfff8,
+ 0xfff8, 0xff9c, 0xff9c, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0xfff8, 0xfff8, 0xff90, 0xff90, 0xfff9, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xff80, 0xff80, 0xff82, 0xff82, 0xfff7,
+ 0x0000, 0x0000, 0x0000,
+ /* 0x2103 .. 0x21ff */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xe2a3,
+ 0x0000, 0x0000, 0x0000, 0xdf41, 0xdfba, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x001c,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0010, 0x0010, 0x0010,
+ 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010,
+ 0x0010, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000,
+ /* 0x247c .. 0x24ff */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x001a, 0x001a,
+ 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a,
+ 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 0x2c00 .. 0x2cff */
+ 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030,
+ 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030,
+ 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030,
+ 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0001, 0x0000, 0xd609, 0xf11a, 0xd619, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001,
+ 0x0000, 0xd5e4, 0xd603, 0xd5e1, 0xd5e2, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xd5c1, 0xd5c1, 0x0001, 0x0000, 0x0001, 0x0000,
+ 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+ 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+ 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+ 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+ 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+ 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+ 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+ 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 0xa60d .. 0xa6ff */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001,
+ 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001,
+ 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001,
+ 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001,
+ 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001,
+ 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000,
+ /* 0xa722 .. 0xa7ff */
+ 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+ 0x0001, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+ 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+ 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+ 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+ 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+ 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x75fc, 0x0001, 0x0000, 0x0001, 0x0000,
+ 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x5ad8,
+ 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+ 0x0001, 0x0000, 0x0001, 0x0000, 0x5abc, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 0xff21 .. 0xffff */
+ 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020,
+ 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020,
+ 0x0020, 0x0020, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
+};
+static const WCHAR winpr_casemap_upper[3994] = {
+ /* index */
+ 0x019f, 0x029f, 0x039f, 0x045a, 0x0556, 0x0656, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+ 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+ 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x06dd, 0x07dc, 0x08dc, 0x0100, 0x09d0, 0x0100, 0x0100,
+ 0x0a55, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0b3f, 0x0c3f, 0x0100, 0x0100,
+ 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+ 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+ 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+ 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+ 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+ 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+ 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+ 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+ 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+ 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0cfe, 0x0ddb,
+ 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+ 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+ 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+ 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+ 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+ 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+ 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+ 0x0100, 0x0100, 0x0100, 0x0e9a,
+ /* defaults */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 0x0061 .. 0x00ff */
+ 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0,
+ 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0,
+ 0xffe0, 0xffe0, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x02e7, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0,
+ 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0,
+ 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0x0000, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0,
+ 0xffe0, 0xffe0, 0x0079,
+ /* 0x0100 .. 0x01ff */
+ 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+ 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+ 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+ 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+ 0x0000, 0xff18, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, 0xffff, 0x0000,
+ 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000,
+ 0xffff, 0x0000, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+ 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+ 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+ 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+ 0x0000, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0xfed4, 0x00c3, 0x0000, 0x0000, 0xffff,
+ 0x0000, 0xffff, 0x0000, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0xffff, 0x0000, 0x0000, 0x0061, 0x0000, 0x0000, 0x0000, 0xffff, 0x00a3, 0x0000,
+ 0x0000, 0x0000, 0x0082, 0x0000, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000,
+ 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000,
+ 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 0x0038,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff, 0xfffe, 0x0000, 0xffff, 0xfffe, 0x0000, 0xffff,
+ 0xfffe, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000,
+ 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0xffb1, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+ 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+ 0x0000, 0x0000, 0xffff, 0xfffe, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 0xffff,
+ 0x0000, 0xffff, 0x0000, 0xffff,
+ /* 0x0200 .. 0x02ff */
+ 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+ 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+ 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0xffff,
+ 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+ 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0xffff, 0x0000, 0x0000, 0x2a3f, 0x2a3f, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff,
+ 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x2a1f, 0x2a1c, 0x2a1e, 0xff2e,
+ 0xff32, 0x0000, 0xff33, 0xff33, 0x0000, 0xff36, 0x0000, 0xff35, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0xff33, 0x0000, 0x0000, 0xff31, 0x0000, 0xa528, 0xa544, 0x0000, 0xff2f, 0xff2d, 0x0000, 0x29f7,
+ 0x0000, 0x0000, 0x0000, 0xff2d, 0x0000, 0x29fd, 0xff2b, 0x0000, 0x0000, 0xff2a, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x29e7, 0x0000, 0x0000, 0xff26, 0x0000, 0x0000, 0xff26,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0xff26, 0xffbb, 0xff27, 0xff27, 0xffb9, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0xff25, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 0x0345 .. 0x03ff */
+ 0x0054, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000,
+ 0x0000, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0x0082, 0x0082, 0x0082, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffda, 0xffdb, 0xffdb, 0xffdb, 0x0000,
+ 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0,
+ 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe1, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0,
+ 0xffe0, 0xffe0, 0xffe0, 0xffc0, 0xffc1, 0xffc1, 0x0000, 0xffc2, 0xffc7, 0x0000, 0x0000, 0x0000,
+ 0xffd1, 0xffca, 0xfff8, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000,
+ 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000,
+ 0xffff, 0x0000, 0xffff, 0xffaa, 0xffb0, 0x0007, 0x0000, 0x0000, 0xffa0, 0x0000, 0x0000, 0xffff,
+ 0x0000, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 0x0404 .. 0x04ff */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffe0, 0xffe0, 0xffe0, 0xffe0,
+ 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0,
+ 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0,
+ 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffb0, 0xffb0, 0xffb0, 0xffb0, 0xffb0, 0xffb0, 0xffb0, 0xffb0,
+ 0xffb0, 0xffb0, 0xffb0, 0xffb0, 0xffb0, 0xffb0, 0xffb0, 0xffb0, 0x0000, 0xffff, 0x0000, 0xffff,
+ 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+ 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+ 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+ 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+ 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+ 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+ 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, 0xffff, 0x0000,
+ 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0xfff1,
+ 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+ 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+ 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+ 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+ /* 0x0500 .. 0x05ff */
+ 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+ 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+ 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+ 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0,
+ 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0,
+ 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0,
+ 0xffd0, 0xffd0, 0xffd0, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 0x1d79 .. 0x1dff */
+ 0x8a04, 0x0000, 0x0000, 0x0000, 0x0ee6, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000,
+ /* 0x1e01 .. 0x1eff */
+ 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000,
+ 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000,
+ 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000,
+ 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000,
+ 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000,
+ 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000,
+ 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000,
+ 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000,
+ 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000,
+ 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000,
+ 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000,
+ 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000,
+ 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffc5, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000,
+ 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000,
+ 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000,
+ 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000,
+ 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000,
+ 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000,
+ 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000,
+ 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000,
+ 0xffff, 0x0000, 0xffff,
+ /* 0x1f00 .. 0x1fff */
+ 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0008, 0x0008, 0x0008, 0x0008,
+ 0x0008, 0x0008, 0x0008, 0x0008, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0008, 0x0000, 0x0008,
+ 0x0000, 0x0008, 0x0000, 0x0008, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x004a, 0x004a, 0x0056, 0x0056, 0x0056, 0x0056, 0x0064, 0x0064,
+ 0x0080, 0x0080, 0x0070, 0x0070, 0x007e, 0x007e, 0x0000, 0x0000, 0x0008, 0x0008, 0x0008, 0x0008,
+ 0x0008, 0x0008, 0x0008, 0x0008, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0008, 0x0008, 0x0000, 0x0009,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xe3db, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0009, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0008, 0x0008, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0008, 0x0008, 0x0000, 0x0000,
+ 0x0000, 0x0007, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0009, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 0x210c .. 0x21ff */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffe4, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0xfff0, 0xfff0, 0xfff0, 0xfff0, 0xfff0, 0xfff0, 0xfff0, 0xfff0,
+ 0xfff0, 0xfff0, 0xfff0, 0xfff0, 0xfff0, 0xfff0, 0xfff0, 0xfff0, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 0x247b .. 0x24ff */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6,
+ 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6,
+ 0xffe6, 0xffe6, 0xffe6, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000,
+ /* 0x2c16 .. 0x2cff */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0,
+ 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0,
+ 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0,
+ 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0,
+ 0xffd0, 0x0000, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0xd5d5, 0xd5d8, 0x0000, 0xffff, 0x0000,
+ 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 0x0000,
+ 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff,
+ 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+ 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+ 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+ 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+ 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+ 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+ 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+ 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+ 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff, 0x0000,
+ 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 0x2d00 .. 0x2dff */
+ 0xe3a0, 0xe3a0, 0xe3a0, 0xe3a0, 0xe3a0, 0xe3a0, 0xe3a0, 0xe3a0, 0xe3a0, 0xe3a0, 0xe3a0, 0xe3a0,
+ 0xe3a0, 0xe3a0, 0xe3a0, 0xe3a0, 0xe3a0, 0xe3a0, 0xe3a0, 0xe3a0, 0xe3a0, 0xe3a0, 0xe3a0, 0xe3a0,
+ 0xe3a0, 0xe3a0, 0xe3a0, 0xe3a0, 0xe3a0, 0xe3a0, 0xe3a0, 0xe3a0, 0xe3a0, 0xe3a0, 0xe3a0, 0xe3a0,
+ 0xe3a0, 0xe3a0, 0x0000, 0xe3a0, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xe3a0, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 0xa641 .. 0xa6ff */
+ 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000,
+ 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000,
+ 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000,
+ 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000,
+ 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000,
+ 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 0xa723 .. 0xa7ff */
+ 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000,
+ 0xffff, 0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000,
+ 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000,
+ 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000,
+ 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000,
+ 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000,
+ 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000,
+ 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000,
+ 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 0xff41 .. 0xffff */
+ 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0,
+ 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0,
+ 0xffe0, 0xffe0, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
+};
diff --git a/winpr/libwinpr/crt/conversion.c b/winpr/libwinpr/crt/conversion.c
new file mode 100644
index 0000000..0a3d1e1
--- /dev/null
+++ b/winpr/libwinpr/crt/conversion.c
@@ -0,0 +1,46 @@
+/**
+ * WinPR: Windows Portable Runtime
+ * Data Conversion
+ *
+ * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <winpr/config.h>
+
+#include <winpr/crt.h>
+#include <winpr/string.h>
+
+/* Data Conversion: http://msdn.microsoft.com/en-us/library/0heszx3w/ */
+
+#ifndef _WIN32
+
+errno_t _itoa_s(int value, char* buffer, size_t sizeInCharacters, int radix)
+{
+ int length = 0;
+
+ length = sprintf_s(NULL, 0, "%d", value);
+
+ if (length < 0)
+ return -1;
+
+ if (sizeInCharacters < (size_t)length)
+ return -1;
+
+ sprintf_s(buffer, length + 1, "%d", value);
+
+ return 0;
+}
+
+#endif
diff --git a/winpr/libwinpr/crt/memory.c b/winpr/libwinpr/crt/memory.c
new file mode 100644
index 0000000..40c6c85
--- /dev/null
+++ b/winpr/libwinpr/crt/memory.c
@@ -0,0 +1,42 @@
+/**
+ * WinPR: Windows Portable Runtime
+ * Memory Allocation
+ *
+ * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <winpr/config.h>
+
+#include <winpr/crt.h>
+
+/* Memory Allocation: http://msdn.microsoft.com/en-us/library/hk1k7x6x.aspx */
+/* Memory Management Functions: http://msdn.microsoft.com/en-us/library/windows/desktop/aa366781/ */
+
+#ifndef _WIN32
+
+PVOID SecureZeroMemory(PVOID ptr, SIZE_T cnt)
+{
+ volatile BYTE* p = ptr;
+
+ while (cnt--)
+ {
+ *p = 0;
+ p++;
+ }
+
+ return ptr;
+}
+
+#endif
diff --git a/winpr/libwinpr/crt/string.c b/winpr/libwinpr/crt/string.c
new file mode 100644
index 0000000..93721a6
--- /dev/null
+++ b/winpr/libwinpr/crt/string.c
@@ -0,0 +1,832 @@
+/**
+ * WinPR: Windows Portable Runtime
+ * String Manipulation (CRT)
+ *
+ * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <winpr/config.h>
+#include <winpr/assert.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <wctype.h>
+#include <wchar.h>
+
+#include <winpr/crt.h>
+#include <winpr/endian.h>
+
+#if defined(WITH_URIPARSER)
+#include <uriparser/Uri.h>
+#endif
+
+/* String Manipulation (CRT): http://msdn.microsoft.com/en-us/library/f0151s4x.aspx */
+
+#include "../log.h"
+#define TAG WINPR_TAG("crt")
+
+#ifndef MIN
+#define MIN(x, y) (((x) < (y)) ? (x) : (y))
+#endif
+
+#if defined(WITH_URIPARSER)
+char* winpr_str_url_decode(const char* str, size_t len)
+{
+ char* dst = strndup(str, len);
+ if (!dst)
+ return NULL;
+
+ if (!uriUnescapeInPlaceExA(dst, URI_FALSE, URI_FALSE))
+ {
+ free(dst);
+ return NULL;
+ }
+
+ return dst;
+}
+
+char* winpr_str_url_encode(const char* str, size_t len)
+{
+ char* dst = calloc(len + 1, sizeof(char) * 3);
+ if (!dst)
+ return NULL;
+
+ if (!uriEscapeA(str, dst, URI_FALSE, URI_FALSE))
+ {
+ free(dst);
+ return NULL;
+ }
+ return dst;
+}
+
+#else
+static const char rfc3986[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x2e, 0x00,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x5f,
+ 0x00, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x00, 0x00, 0x00, 0x7e, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static char hex2bin(char what)
+{
+ if (what >= 'a')
+ what -= 'a' - 'A';
+ if (what >= 'A')
+ what -= ('A' - 10);
+ else
+ what -= '0';
+ return what;
+}
+
+static char unescape(const char* what, size_t* px)
+{
+ if ((*what == '%') && (isxdigit(what[1]) && isxdigit(what[2])))
+ {
+ *px += 2;
+ return 16 * hex2bin(what[1]) + hex2bin(what[2]);
+ }
+
+ return *what;
+}
+
+char* winpr_str_url_decode(const char* str, size_t len)
+{
+ char* dst = calloc(len + 1, sizeof(char));
+ if (!dst)
+ return NULL;
+
+ size_t pos = 0;
+ for (size_t x = 0; x < strnlen(str, len); x++)
+ {
+ const char* cur = &str[x];
+ dst[pos++] = unescape(cur, &x);
+ }
+ return dst;
+}
+
+static char* escape(char* dst, char what)
+{
+ if (rfc3986[what & 0xff])
+ {
+ *dst = what;
+ return dst + 1;
+ }
+
+ sprintf(dst, "%%%02" PRIX8, (BYTE)(what & 0xff));
+ return dst + 3;
+}
+
+char* winpr_str_url_encode(const char* str, size_t len)
+{
+ char* dst = calloc(len + 1, sizeof(char) * 3);
+ if (!dst)
+ return NULL;
+
+ char* ptr = dst;
+ for (size_t x = 0; x < strnlen(str, len); x++)
+ {
+ const char cur = str[x];
+ ptr = escape(ptr, cur);
+ }
+ return dst;
+}
+#endif
+
+BOOL winpr_str_append(const char* what, char* buffer, size_t size, const char* separator)
+{
+ const size_t used = strnlen(buffer, size);
+ const size_t add = strnlen(what, size);
+ const size_t sep_len = separator ? strnlen(separator, size) : 0;
+ const size_t sep = (used > 0) ? sep_len : 0;
+
+ if (used + add + sep >= size)
+ return FALSE;
+
+ if ((used > 0) && (sep_len > 0))
+ strncat(buffer, separator, sep_len);
+
+ strncat(buffer, what, add);
+ return TRUE;
+}
+
+WINPR_ATTR_FORMAT_ARG(3, 4)
+int winpr_asprintf(char** s, size_t* slen, WINPR_FORMAT_ARG const char* templ, ...)
+{
+ va_list ap;
+
+ va_start(ap, templ);
+ int rc = winpr_vasprintf(s, slen, templ, ap);
+ va_end(ap);
+ return rc;
+}
+
+WINPR_ATTR_FORMAT_ARG(3, 0)
+int winpr_vasprintf(char** s, size_t* slen, WINPR_FORMAT_ARG const char* templ, va_list oap)
+{
+ va_list ap;
+
+ *s = NULL;
+ *slen = 0;
+
+ va_copy(ap, oap);
+ const int length = vsnprintf(NULL, 0, templ, ap);
+ va_end(ap);
+ if (length < 0)
+ return length;
+
+ char* str = calloc((size_t)length + 1ul, sizeof(char));
+ if (!str)
+ return -1;
+
+ va_copy(ap, oap);
+ const int plen = vsprintf(str, templ, ap);
+ va_end(ap);
+
+ WINPR_ASSERT(length == plen);
+ *s = str;
+ *slen = (size_t)length;
+ return length;
+}
+
+#ifndef _WIN32
+
+char* _strdup(const char* strSource)
+{
+ if (strSource == NULL)
+ return NULL;
+
+ char* strDestination = strdup(strSource);
+
+ if (strDestination == NULL)
+ WLog_ERR(TAG, "strdup");
+
+ return strDestination;
+}
+
+WCHAR* _wcsdup(const WCHAR* strSource)
+{
+ if (!strSource)
+ return NULL;
+
+ size_t len = _wcslen(strSource);
+ WCHAR* strDestination = calloc(len + 1, sizeof(WCHAR));
+
+ if (strDestination != NULL)
+ memcpy(strDestination, strSource, len * sizeof(WCHAR));
+
+ if (strDestination == NULL)
+ WLog_ERR(TAG, "wcsdup");
+
+ return strDestination;
+}
+
+WCHAR* _wcsncat(WCHAR* dst, const WCHAR* src, size_t sz)
+{
+ WINPR_ASSERT(dst);
+ WINPR_ASSERT(src || (sz == 0));
+
+ const size_t dlen = _wcslen(dst);
+ const size_t slen = _wcsnlen(src, sz);
+ for (size_t x = 0; x < slen; x++)
+ dst[dlen + x] = src[x];
+ dst[dlen + slen] = '\0';
+ return dst;
+}
+
+int _stricmp(const char* string1, const char* string2)
+{
+ return strcasecmp(string1, string2);
+}
+
+int _strnicmp(const char* string1, const char* string2, size_t count)
+{
+ return strncasecmp(string1, string2, count);
+}
+
+/* _wcscmp -> wcscmp */
+
+int _wcscmp(const WCHAR* string1, const WCHAR* string2)
+{
+ WINPR_ASSERT(string1);
+ WINPR_ASSERT(string2);
+
+ while (TRUE)
+ {
+ const WCHAR w1 = *string1++;
+ const WCHAR w2 = *string2++;
+
+ if (w1 != w2)
+ return (int)w1 - w2;
+ else if ((w1 == '\0') || (w2 == '\0'))
+ return (int)w1 - w2;
+ }
+}
+
+int _wcsncmp(const WCHAR* string1, const WCHAR* string2, size_t count)
+{
+ WINPR_ASSERT(string1);
+ WINPR_ASSERT(string2);
+
+ for (size_t x = 0; x < count; x++)
+ {
+ const WCHAR a = string1[x];
+ const WCHAR b = string2[x];
+
+ if (a != b)
+ return (int)a - b;
+ else if ((a == '\0') || (b == '\0'))
+ return (int)a - b;
+ }
+ return 0;
+}
+
+/* _wcslen -> wcslen */
+
+size_t _wcslen(const WCHAR* str)
+{
+ const WCHAR* p = (const WCHAR*)str;
+
+ WINPR_ASSERT(p);
+
+ while (*p)
+ p++;
+
+ return (size_t)(p - str);
+}
+
+/* _wcsnlen -> wcsnlen */
+
+size_t _wcsnlen(const WCHAR* str, size_t max)
+{
+ WINPR_ASSERT(str);
+
+ size_t x = 0;
+ for (; x < max; x++)
+ {
+ if (str[x] == 0)
+ return x;
+ }
+
+ return x;
+}
+
+/* _wcsstr -> wcsstr */
+
+WCHAR* _wcsstr(const WCHAR* str, const WCHAR* strSearch)
+{
+ WINPR_ASSERT(str);
+ WINPR_ASSERT(strSearch);
+
+ if (strSearch[0] == '\0')
+ return (WCHAR*)str;
+
+ const size_t searchLen = _wcslen(strSearch);
+ while (*str)
+ {
+ if (_wcsncmp(str, strSearch, searchLen) == 0)
+ return (WCHAR*)str;
+ str++;
+ }
+ return NULL;
+}
+
+/* _wcschr -> wcschr */
+
+WCHAR* _wcschr(const WCHAR* str, WCHAR value)
+{
+ union
+ {
+ const WCHAR* cc;
+ WCHAR* c;
+ } cnv;
+ const WCHAR* p = (const WCHAR*)str;
+
+ while (*p && (*p != value))
+ p++;
+
+ cnv.cc = (*p == value) ? p : NULL;
+ return cnv.c;
+}
+
+/* _wcsrchr -> wcsrchr */
+
+WCHAR* _wcsrchr(const WCHAR* str, WCHAR c)
+{
+ union
+ {
+ const WCHAR* cc;
+ WCHAR* c;
+ } cnv;
+ const WCHAR* p = NULL;
+
+ if (!str)
+ return NULL;
+
+ for (; *str != '\0'; str++)
+ {
+ const WCHAR ch = *str;
+ if (ch == c)
+ p = str;
+ }
+
+ cnv.cc = p;
+ return cnv.c;
+}
+
+char* strtok_s(char* strToken, const char* strDelimit, char** context)
+{
+ return strtok_r(strToken, strDelimit, context);
+}
+
+WCHAR* wcstok_s(WCHAR* strToken, const WCHAR* strDelimit, WCHAR** context)
+{
+ WCHAR* nextToken = NULL;
+ WCHAR value = 0;
+
+ if (!strToken)
+ strToken = *context;
+
+ value = *strToken;
+
+ while (*strToken && _wcschr(strDelimit, value))
+ {
+ strToken++;
+ value = *strToken;
+ }
+
+ if (!*strToken)
+ return NULL;
+
+ nextToken = strToken++;
+ value = *strToken;
+
+ while (*strToken && !(_wcschr(strDelimit, value)))
+ {
+ strToken++;
+ value = *strToken;
+ }
+
+ if (*strToken)
+ *strToken++ = 0;
+
+ *context = strToken;
+ return nextToken;
+}
+
+#endif
+
+#if !defined(_WIN32) || defined(_UWP)
+
+/* Windows API Sets - api-ms-win-core-string-l2-1-0.dll
+ * http://msdn.microsoft.com/en-us/library/hh802935/
+ */
+
+#include "casing.c"
+
+LPSTR CharUpperA(LPSTR lpsz)
+{
+ size_t length = 0;
+
+ if (!lpsz)
+ return NULL;
+
+ length = strlen(lpsz);
+
+ if (length < 1)
+ return (LPSTR)NULL;
+
+ if (length == 1)
+ {
+ char c = *lpsz;
+
+ if ((c >= 'a') && (c <= 'z'))
+ c = c - 'a' + 'A';
+
+ *lpsz = c;
+ return lpsz;
+ }
+
+ for (size_t i = 0; i < length; i++)
+ {
+ if ((lpsz[i] >= 'a') && (lpsz[i] <= 'z'))
+ lpsz[i] = lpsz[i] - 'a' + 'A';
+ }
+
+ return lpsz;
+}
+
+LPWSTR CharUpperW(LPWSTR lpsz)
+{
+ size_t length = 0;
+
+ if (!lpsz)
+ return NULL;
+
+ length = _wcslen(lpsz);
+
+ if (length < 1)
+ return (LPWSTR)NULL;
+
+ if (length == 1)
+ {
+ WCHAR c = *lpsz;
+
+ if ((c >= L'a') && (c <= L'z'))
+ c = c - L'a' + L'A';
+
+ *lpsz = c;
+ return lpsz;
+ }
+
+ for (size_t i = 0; i < length; i++)
+ {
+ if ((lpsz[i] >= L'a') && (lpsz[i] <= L'z'))
+ lpsz[i] = lpsz[i] - L'a' + L'A';
+ }
+
+ return lpsz;
+}
+
+DWORD CharUpperBuffA(LPSTR lpsz, DWORD cchLength)
+{
+ if (cchLength < 1)
+ return 0;
+
+ for (DWORD i = 0; i < cchLength; i++)
+ {
+ if ((lpsz[i] >= 'a') && (lpsz[i] <= 'z'))
+ lpsz[i] = lpsz[i] - 'a' + 'A';
+ }
+
+ return cchLength;
+}
+
+DWORD CharUpperBuffW(LPWSTR lpsz, DWORD cchLength)
+{
+ WCHAR value = 0;
+
+ for (DWORD i = 0; i < cchLength; i++)
+ {
+ Data_Read_UINT16(&lpsz[i], value);
+ value = WINPR_TOUPPERW(value);
+ Data_Write_UINT16(&lpsz[i], value);
+ }
+
+ return cchLength;
+}
+
+LPSTR CharLowerA(LPSTR lpsz)
+{
+ size_t length = 0;
+
+ if (!lpsz)
+ return (LPSTR)NULL;
+
+ length = strlen(lpsz);
+
+ if (length < 1)
+ return (LPSTR)NULL;
+
+ if (length == 1)
+ {
+ char c = *lpsz;
+
+ if ((c >= 'A') && (c <= 'Z'))
+ c = c - 'A' + 'a';
+
+ *lpsz = c;
+ return lpsz;
+ }
+
+ for (size_t i = 0; i < length; i++)
+ {
+ if ((lpsz[i] >= 'A') && (lpsz[i] <= 'Z'))
+ lpsz[i] = lpsz[i] - 'A' + 'a';
+ }
+
+ return lpsz;
+}
+
+LPWSTR CharLowerW(LPWSTR lpsz)
+{
+ CharLowerBuffW(lpsz, _wcslen(lpsz));
+ return lpsz;
+}
+
+DWORD CharLowerBuffA(LPSTR lpsz, DWORD cchLength)
+{
+ if (cchLength < 1)
+ return 0;
+
+ for (DWORD i = 0; i < cchLength; i++)
+ {
+ if ((lpsz[i] >= 'A') && (lpsz[i] <= 'Z'))
+ lpsz[i] = lpsz[i] - 'A' + 'a';
+ }
+
+ return cchLength;
+}
+
+DWORD CharLowerBuffW(LPWSTR lpsz, DWORD cchLength)
+{
+ WCHAR value = 0;
+
+ for (DWORD i = 0; i < cchLength; i++)
+ {
+ Data_Read_UINT16(&lpsz[i], value);
+ value = WINPR_TOLOWERW(value);
+ Data_Write_UINT16(&lpsz[i], value);
+ }
+
+ return cchLength;
+}
+
+BOOL IsCharAlphaA(CHAR ch)
+{
+ if (((ch >= 'a') && (ch <= 'z')) || ((ch >= 'A') && (ch <= 'Z')))
+ return 1;
+ else
+ return 0;
+}
+
+BOOL IsCharAlphaW(WCHAR ch)
+{
+ if (((ch >= L'a') && (ch <= L'z')) || ((ch >= L'A') && (ch <= L'Z')))
+ return 1;
+ else
+ return 0;
+}
+
+BOOL IsCharAlphaNumericA(CHAR ch)
+{
+ if (((ch >= 'a') && (ch <= 'z')) || ((ch >= 'A') && (ch <= 'Z')) ||
+ ((ch >= '0') && (ch <= '9')))
+ return 1;
+ else
+ return 0;
+}
+
+BOOL IsCharAlphaNumericW(WCHAR ch)
+{
+ if (((ch >= L'a') && (ch <= L'z')) || ((ch >= L'A') && (ch <= L'Z')) ||
+ ((ch >= L'0') && (ch <= L'9')))
+ return 1;
+ else
+ return 0;
+}
+
+BOOL IsCharUpperA(CHAR ch)
+{
+ if ((ch >= 'A') && (ch <= 'Z'))
+ return 1;
+ else
+ return 0;
+}
+
+BOOL IsCharUpperW(WCHAR ch)
+{
+ if ((ch >= L'A') && (ch <= L'Z'))
+ return 1;
+ else
+ return 0;
+}
+
+BOOL IsCharLowerA(CHAR ch)
+{
+ if ((ch >= 'a') && (ch <= 'z'))
+ return 1;
+ else
+ return 0;
+}
+
+BOOL IsCharLowerW(WCHAR ch)
+{
+ if ((ch >= L'a') && (ch <= L'z'))
+ return 1;
+ else
+ return 0;
+}
+
+#endif
+
+size_t ConvertLineEndingToLF(char* str, size_t size)
+{
+ size_t skip = 0;
+
+ WINPR_ASSERT(str || (size == 0));
+ for (size_t x = 0; x < size; x++)
+ {
+ char c = str[x];
+ switch (c)
+ {
+ case '\r':
+ str[x - skip] = '\n';
+ if ((x + 1 < size) && (str[x + 1] == '\n'))
+ skip++;
+ break;
+ default:
+ str[x - skip] = c;
+ break;
+ }
+ }
+ return size - skip;
+}
+
+char* ConvertLineEndingToCRLF(const char* str, size_t* size)
+{
+ WINPR_ASSERT(size);
+ const size_t s = *size;
+ WINPR_ASSERT(str || (s == 0));
+
+ *size = 0;
+ if (s == 0)
+ return NULL;
+
+ size_t linebreaks = 0;
+ for (size_t x = 0; x < s - 1; x++)
+ {
+ char c = str[x];
+ switch (c)
+ {
+ case '\r':
+ case '\n':
+ linebreaks++;
+ break;
+ default:
+ break;
+ }
+ }
+ char* cnv = calloc(s + linebreaks * 2ull + 1ull, sizeof(char));
+ if (!cnv)
+ return NULL;
+
+ size_t pos = 0;
+ for (size_t x = 0; x < s; x++)
+ {
+ const char c = str[x];
+ switch (c)
+ {
+ case '\r':
+ cnv[pos++] = '\r';
+ cnv[pos++] = '\n';
+ break;
+ case '\n':
+ /* Do not duplicate existing \r\n sequences */
+ if ((x > 0) && (str[x - 1] != '\r'))
+ {
+ cnv[pos++] = '\r';
+ cnv[pos++] = '\n';
+ }
+ break;
+ default:
+ cnv[pos++] = c;
+ break;
+ }
+ }
+ *size = pos;
+ return cnv;
+}
+
+char* StrSep(char** stringp, const char* delim)
+{
+ char* start = *stringp;
+ char* p = NULL;
+ p = (start != NULL) ? strpbrk(start, delim) : NULL;
+
+ if (!p)
+ *stringp = NULL;
+ else
+ {
+ *p = '\0';
+ *stringp = p + 1;
+ }
+
+ return start;
+}
+
+INT64 GetLine(char** lineptr, size_t* size, FILE* stream)
+{
+#if defined(_WIN32)
+ char c;
+ char* n;
+ size_t step = 32;
+ size_t used = 0;
+
+ if (!lineptr || !size)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+
+ do
+ {
+ if (used + 2 >= *size)
+ {
+ *size += step;
+ n = realloc(*lineptr, *size);
+
+ if (!n)
+ {
+ return -1;
+ }
+
+ *lineptr = n;
+ }
+
+ c = fgetc(stream);
+
+ if (c != EOF)
+ (*lineptr)[used++] = c;
+ } while ((c != '\n') && (c != '\r') && (c != EOF));
+
+ (*lineptr)[used] = '\0';
+ return used;
+#elif !defined(ANDROID) && !defined(IOS)
+ return getline(lineptr, size, stream);
+#else
+ return -1;
+#endif
+}
+
+#if !defined(WINPR_HAVE_STRNDUP)
+char* strndup(const char* src, size_t n)
+{
+ char* dst = calloc(n + 1, sizeof(char));
+ if (dst)
+ strncpy(dst, src, n);
+ return dst;
+}
+#endif
+
+const WCHAR* InitializeConstWCharFromUtf8(const char* str, WCHAR* buffer, size_t len)
+{
+ WINPR_ASSERT(str);
+ WINPR_ASSERT(buffer || (len == 0));
+ ConvertUtf8ToWChar(str, buffer, len);
+ return buffer;
+}
diff --git a/winpr/libwinpr/crt/test/CMakeLists.txt b/winpr/libwinpr/crt/test/CMakeLists.txt
new file mode 100644
index 0000000..5f5b013
--- /dev/null
+++ b/winpr/libwinpr/crt/test/CMakeLists.txt
@@ -0,0 +1,30 @@
+
+set(MODULE_NAME "TestCrt")
+set(MODULE_PREFIX "TEST_CRT")
+
+set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c)
+
+set(${MODULE_PREFIX}_TESTS
+ TestTypes.c
+ TestFormatSpecifiers.c
+ TestAlignment.c
+ TestString.c
+ TestUnicodeConversion.c)
+
+create_test_sourcelist(${MODULE_PREFIX}_SRCS
+ ${${MODULE_PREFIX}_DRIVER}
+ ${${MODULE_PREFIX}_TESTS})
+
+add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS})
+
+target_link_libraries(${MODULE_NAME} winpr)
+
+set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}")
+
+foreach(test ${${MODULE_PREFIX}_TESTS})
+ get_filename_component(TestName ${test} NAME_WE)
+ add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName})
+endforeach()
+
+set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/Test")
+
diff --git a/winpr/libwinpr/crt/test/TestAlignment.c b/winpr/libwinpr/crt/test/TestAlignment.c
new file mode 100644
index 0000000..07bac7f
--- /dev/null
+++ b/winpr/libwinpr/crt/test/TestAlignment.c
@@ -0,0 +1,87 @@
+
+#include <stdio.h>
+#include <winpr/crt.h>
+#include <winpr/windows.h>
+
+int TestAlignment(int argc, char* argv[])
+{
+ void* ptr = NULL;
+ size_t alignment = 0;
+ size_t offset = 0;
+
+ WINPR_UNUSED(argc);
+ WINPR_UNUSED(argv);
+
+ /* Alignment should be 2^N where N is a positive integer */
+
+ alignment = 16;
+ offset = 8;
+
+ /* _aligned_malloc */
+
+ ptr = winpr_aligned_malloc(100, alignment);
+
+ if (ptr == NULL)
+ {
+ printf("Error allocating aligned memory.\n");
+ return -1;
+ }
+
+ if (((size_t)ptr % alignment) != 0)
+ {
+ printf("This pointer, %p, is not aligned on %" PRIuz "\n", ptr, alignment);
+ return -1;
+ }
+
+ /* _aligned_realloc */
+
+ ptr = winpr_aligned_realloc(ptr, 200, alignment);
+
+ if (((size_t)ptr % alignment) != 0)
+ {
+ printf("This pointer, %p, is not aligned on %" PRIuz "\n", ptr, alignment);
+ return -1;
+ }
+
+ winpr_aligned_free(ptr);
+
+ /* _aligned_offset_malloc */
+
+ ptr = winpr_aligned_offset_malloc(200, alignment, offset);
+
+ if (ptr == NULL)
+ {
+ printf("Error reallocating aligned offset memory.");
+ return -1;
+ }
+
+ if (((((size_t)ptr) + offset) % alignment) != 0)
+ {
+ printf("This pointer, %p, does not satisfy offset %" PRIuz " and alignment %" PRIuz "\n",
+ ptr, offset, alignment);
+ return -1;
+ }
+
+ /* _aligned_offset_realloc */
+
+ ptr = winpr_aligned_offset_realloc(ptr, 200, alignment, offset);
+
+ if (ptr == NULL)
+ {
+ printf("Error reallocating aligned offset memory.");
+ return -1;
+ }
+
+ if (((((size_t)ptr) + offset) % alignment) != 0)
+ {
+ printf("This pointer, %p, does not satisfy offset %" PRIuz " and alignment %" PRIuz "\n",
+ ptr, offset, alignment);
+ return -1;
+ }
+
+ /* _aligned_free works for both _aligned_malloc and _aligned_offset_malloc. free() should not be
+ * used. */
+ winpr_aligned_free(ptr);
+
+ return 0;
+}
diff --git a/winpr/libwinpr/crt/test/TestFormatSpecifiers.c b/winpr/libwinpr/crt/test/TestFormatSpecifiers.c
new file mode 100644
index 0000000..5ae6a40
--- /dev/null
+++ b/winpr/libwinpr/crt/test/TestFormatSpecifiers.c
@@ -0,0 +1,164 @@
+#include <stdio.h>
+#include <winpr/wtypes.h>
+#include <winpr/string.h>
+
+int TestFormatSpecifiers(int argc, char* argv[])
+{
+ unsigned errors = 0;
+
+ char fmt[4096];
+
+ WINPR_UNUSED(argc);
+ WINPR_UNUSED(argv);
+
+ /* size_t */
+ {
+ size_t arg = 0xabcd;
+ const char* chk = "uz:43981 oz:125715 xz:abcd Xz:ABCD";
+
+ sprintf_s(fmt, sizeof(fmt), "uz:%" PRIuz " oz:%" PRIoz " xz:%" PRIxz " Xz:%" PRIXz "", arg,
+ arg, arg, arg);
+
+ if (strcmp(fmt, chk))
+ {
+ fprintf(stderr, "%s failed size_t test: got [%s] instead of [%s]\n", __func__, fmt,
+ chk);
+ errors++;
+ }
+ }
+
+ /* INT8 */
+ {
+ INT8 arg = -16;
+ const char* chk = "d8:-16 x8:f0 X8:F0";
+
+ sprintf_s(fmt, sizeof(fmt), "d8:%" PRId8 " x8:%" PRIx8 " X8:%" PRIX8 "", arg, (UINT8)arg,
+ (UINT8)arg);
+
+ if (strcmp(fmt, chk))
+ {
+ fprintf(stderr, "%s failed INT8 test: got [%s] instead of [%s]\n", __func__, fmt, chk);
+ errors++;
+ }
+ }
+
+ /* UINT8 */
+ {
+ UINT8 arg = 0xFE;
+ const char* chk = "u8:254 o8:376 x8:fe X8:FE";
+
+ sprintf_s(fmt, sizeof(fmt), "u8:%" PRIu8 " o8:%" PRIo8 " x8:%" PRIx8 " X8:%" PRIX8 "", arg,
+ arg, arg, arg);
+
+ if (strcmp(fmt, chk))
+ {
+ fprintf(stderr, "%s failed UINT8 test: got [%s] instead of [%s]\n", __func__, fmt, chk);
+ errors++;
+ }
+ }
+
+ /* INT16 */
+ {
+ INT16 arg = -16;
+ const char* chk = "d16:-16 x16:fff0 X16:FFF0";
+
+ sprintf_s(fmt, sizeof(fmt), "d16:%" PRId16 " x16:%" PRIx16 " X16:%" PRIX16 "", arg,
+ (UINT16)arg, (UINT16)arg);
+
+ if (strcmp(fmt, chk))
+ {
+ fprintf(stderr, "%s failed INT16 test: got [%s] instead of [%s]\n", __func__, fmt, chk);
+ errors++;
+ }
+ }
+
+ /* UINT16 */
+ {
+ UINT16 arg = 0xFFFE;
+ const char* chk = "u16:65534 o16:177776 x16:fffe X16:FFFE";
+
+ sprintf_s(fmt, sizeof(fmt),
+ "u16:%" PRIu16 " o16:%" PRIo16 " x16:%" PRIx16 " X16:%" PRIX16 "", arg, arg, arg,
+ arg);
+
+ if (strcmp(fmt, chk))
+ {
+ fprintf(stderr, "%s failed UINT16 test: got [%s] instead of [%s]\n", __func__, fmt,
+ chk);
+ errors++;
+ }
+ }
+
+ /* INT32 */
+ {
+ INT32 arg = -16;
+ const char* chk = "d32:-16 x32:fffffff0 X32:FFFFFFF0";
+
+ sprintf_s(fmt, sizeof(fmt), "d32:%" PRId32 " x32:%" PRIx32 " X32:%" PRIX32 "", arg,
+ (UINT32)arg, (UINT32)arg);
+
+ if (strcmp(fmt, chk))
+ {
+ fprintf(stderr, "%s failed INT32 test: got [%s] instead of [%s]\n", __func__, fmt, chk);
+ errors++;
+ }
+ }
+
+ /* UINT32 */
+ {
+ UINT32 arg = 0xFFFFFFFE;
+ const char* chk = "u32:4294967294 o32:37777777776 x32:fffffffe X32:FFFFFFFE";
+
+ sprintf_s(fmt, sizeof(fmt),
+ "u32:%" PRIu32 " o32:%" PRIo32 " x32:%" PRIx32 " X32:%" PRIX32 "", arg, arg, arg,
+ arg);
+
+ if (strcmp(fmt, chk))
+ {
+ fprintf(stderr, "%s failed UINT16 test: got [%s] instead of [%s]\n", __func__, fmt,
+ chk);
+ errors++;
+ }
+ }
+
+ /* INT64 */
+ {
+ INT64 arg = -16;
+ const char* chk = "d64:-16 x64:fffffffffffffff0 X64:FFFFFFFFFFFFFFF0";
+
+ sprintf_s(fmt, sizeof(fmt), "d64:%" PRId64 " x64:%" PRIx64 " X64:%" PRIX64 "", arg,
+ (UINT64)arg, (UINT64)arg);
+
+ if (strcmp(fmt, chk))
+ {
+ fprintf(stderr, "%s failed INT64 test: got [%s] instead of [%s]\n", __func__, fmt, chk);
+ errors++;
+ }
+ }
+
+ /* UINT64 */
+ {
+ UINT64 arg = 0xFFFFFFFFFFFFFFFE;
+ const char* chk = "u64:18446744073709551614 o64:1777777777777777777776 "
+ "x64:fffffffffffffffe X64:FFFFFFFFFFFFFFFE";
+
+ sprintf_s(fmt, sizeof(fmt),
+ "u64:%" PRIu64 " o64:%" PRIo64 " x64:%016" PRIx64 " X64:%016" PRIX64 "", arg, arg,
+ arg, arg);
+
+ if (strcmp(fmt, chk))
+ {
+ fprintf(stderr, "%s failed UINT64 test: got [%s] instead of [%s]\n", __func__, fmt,
+ chk);
+ errors++;
+ }
+ }
+
+ if (errors)
+ {
+ fprintf(stderr, "%s produced %u errors\n", __func__, errors);
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/winpr/libwinpr/crt/test/TestString.c b/winpr/libwinpr/crt/test/TestString.c
new file mode 100644
index 0000000..cb7d0fb
--- /dev/null
+++ b/winpr/libwinpr/crt/test/TestString.c
@@ -0,0 +1,188 @@
+
+#include <stdio.h>
+#include <winpr/crt.h>
+#include <winpr/windows.h>
+
+static const CHAR testStringA[] = { 'T', 'h', 'e', ' ', 'q', 'u', 'i', 'c', 'k', ' ', 'b',
+ 'r', 'o', 'w', 'n', ' ', 'f', 'o', 'x', ' ', 'j', 'u',
+ 'm', 'p', 's', ' ', 'o', 'v', 'e', 'r', ' ', 't', 'h',
+ 'e', ' ', 'l', 'a', 'z', 'y', ' ', 'd', 'o', 'g', '\0' };
+
+#define testStringA_Length ((sizeof(testStringA) / sizeof(CHAR)) - 1)
+
+static const CHAR testToken1A[] = { 'q', 'u', 'i', 'c', 'k', '\0' };
+static const CHAR testToken2A[] = { 'b', 'r', 'o', 'w', 'n', '\0' };
+static const CHAR testToken3A[] = { 'f', 'o', 'x', '\0' };
+
+#define testToken1A_Length ((sizeof(testToken1A) / sizeof(CHAR)) - 1)
+#define testToken2A_Length ((sizeof(testToken2A) / sizeof(CHAR)) - 1)
+#define testToken3A_Length ((sizeof(testToken3A) / sizeof(CHAR)) - 1)
+
+static const CHAR testTokensA[] = { 'q', 'u', 'i', 'c', 'k', '\r', '\n', 'b', 'r', 'o',
+ 'w', 'n', '\r', '\n', 'f', 'o', 'x', '\r', '\n', '\0' };
+
+#define testTokensA_Length ((sizeof(testTokensA) / sizeof(CHAR)) - 1)
+
+static const CHAR testDelimiterA[] = { '\r', '\n', '\0' };
+
+#define testDelimiterA_Length ((sizeof(testDelimiter) / sizeof(CHAR)) - 1)
+
+struct url_test_pair
+{
+ const char* what;
+ const char* escaped;
+};
+
+static const struct url_test_pair url_tests[] = {
+ { "xxx%bar ga<ka>ee#%%#%{h}g{f{e%d|c\\b^a~p[q]r`s;t/u?v:w@x=y&z$xxx",
+ "xxx%25bar%20ga%3Cka%3Eee%23%25%25%23%25%7Bh%7Dg%7Bf%7Be%25d%7Cc%5Cb%5Ea~p%5Bq%5Dr%60s%3Bt%"
+ "2Fu%3Fv%3Aw%40x%3Dy%26z%24xxx" },
+ { "äöúëü", "%C3%A4%C3%B6%C3%BA%C3%AB%C3%BC" },
+ { "🎅🏄🤘😈", "%F0%9F%8E%85%F0%9F%8F%84%F0%9F%A4%98%F0%9F%98%88" },
+ { "foo$.%.^.&.\\.txt+", "foo%24.%25.%5E.%26.%5C.txt%2B" }
+};
+
+static BOOL test_url_escape(void)
+{
+ for (size_t x = 0; x < ARRAYSIZE(url_tests); x++)
+ {
+ const struct url_test_pair* cur = &url_tests[x];
+
+ char* escaped = winpr_str_url_encode(cur->what, strlen(cur->what) + 1);
+ char* what = winpr_str_url_decode(cur->escaped, strlen(cur->escaped) + 1);
+
+ const size_t elen = strlen(escaped);
+ const size_t wlen = strlen(what);
+ const size_t pelen = strlen(cur->escaped);
+ const size_t pwlen = strlen(cur->what);
+ BOOL rc = TRUE;
+ if (!escaped || (elen != pelen) || (strcmp(escaped, cur->escaped) != 0))
+ {
+ printf("expected: [%" PRIuz "] %s\n", pelen, cur->escaped);
+ printf("got : [%" PRIuz "] %s\n", elen, escaped);
+ rc = FALSE;
+ }
+ else if (!what || (wlen != pwlen) || (strcmp(what, cur->what) != 0))
+ {
+ printf("expected: [%" PRIuz "] %s\n", pwlen, cur->what);
+ printf("got : [%" PRIuz "] %s\n", wlen, what);
+ rc = FALSE;
+ }
+
+ free(escaped);
+ free(what);
+ if (!rc)
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+int TestString(int argc, char* argv[])
+{
+ const WCHAR* p = NULL;
+ size_t pos = 0;
+ size_t length = 0;
+ WCHAR* context = NULL;
+
+ WINPR_UNUSED(argc);
+ WINPR_UNUSED(argv);
+
+ if (!test_url_escape())
+ return -1;
+
+ /* _wcslen */
+ WCHAR testStringW[ARRAYSIZE(testStringA)] = { 0 };
+ ConvertUtf8NToWChar(testStringA, ARRAYSIZE(testStringA), testStringW, ARRAYSIZE(testStringW));
+ const size_t testStringW_Length = testStringA_Length;
+ length = _wcslen(testStringW);
+
+ if (length != testStringW_Length)
+ {
+ printf("_wcslen error: length mismatch: Actual: %" PRIuz ", Expected: %" PRIuz "\n", length,
+ testStringW_Length);
+ return -1;
+ }
+
+ /* _wcschr */
+ union
+ {
+ char c[2];
+ WCHAR w;
+ } search;
+ search.c[0] = 'r';
+ search.c[1] = '\0';
+
+ p = _wcschr(testStringW, search.w);
+ pos = (p - testStringW);
+
+ if (pos != 11)
+ {
+ printf("_wcschr error: position mismatch: Actual: %" PRIuz ", Expected: 11\n", pos);
+ return -1;
+ }
+
+ p = _wcschr(&testStringW[pos + 1], search.w);
+ pos = (p - testStringW);
+
+ if (pos != 29)
+ {
+ printf("_wcschr error: position mismatch: Actual: %" PRIuz ", Expected: 29\n", pos);
+ return -1;
+ }
+
+ p = _wcschr(&testStringW[pos + 1], search.w);
+
+ if (p != NULL)
+ {
+ printf("_wcschr error: return value mismatch: Actual: %p, Expected: NULL\n",
+ (const void*)p);
+ return -1;
+ }
+
+ /* wcstok_s */
+ WCHAR testDelimiterW[ARRAYSIZE(testDelimiterA)] = { 0 };
+ WCHAR testTokensW[ARRAYSIZE(testTokensA)] = { 0 };
+ ConvertUtf8NToWChar(testTokensA, ARRAYSIZE(testTokensA), testTokensW, ARRAYSIZE(testTokensW));
+ ConvertUtf8NToWChar(testDelimiterA, ARRAYSIZE(testDelimiterA), testDelimiterW,
+ ARRAYSIZE(testDelimiterW));
+ p = wcstok_s(testTokensW, testDelimiterW, &context);
+
+ WCHAR testToken1W[ARRAYSIZE(testToken1A)] = { 0 };
+ ConvertUtf8NToWChar(testToken1A, ARRAYSIZE(testToken1A), testToken1W, ARRAYSIZE(testToken1W));
+ if (memcmp(p, testToken1W, sizeof(testToken1W)) != 0)
+ {
+ printf("wcstok_s error: token #1 mismatch\n");
+ return -1;
+ }
+
+ p = wcstok_s(NULL, testDelimiterW, &context);
+
+ WCHAR testToken2W[ARRAYSIZE(testToken2A)] = { 0 };
+ ConvertUtf8NToWChar(testToken2A, ARRAYSIZE(testToken2A), testToken2W, ARRAYSIZE(testToken2W));
+ if (memcmp(p, testToken2W, sizeof(testToken2W)) != 0)
+ {
+ printf("wcstok_s error: token #2 mismatch\n");
+ return -1;
+ }
+
+ p = wcstok_s(NULL, testDelimiterW, &context);
+
+ WCHAR testToken3W[ARRAYSIZE(testToken3A)] = { 0 };
+ ConvertUtf8NToWChar(testToken3A, ARRAYSIZE(testToken3A), testToken3W, ARRAYSIZE(testToken3W));
+ if (memcmp(p, testToken3W, sizeof(testToken3W)) != 0)
+ {
+ printf("wcstok_s error: token #3 mismatch\n");
+ return -1;
+ }
+
+ p = wcstok_s(NULL, testDelimiterW, &context);
+
+ if (p != NULL)
+ {
+ printf("wcstok_s error: return value is not NULL\n");
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/winpr/libwinpr/crt/test/TestTypes.c b/winpr/libwinpr/crt/test/TestTypes.c
new file mode 100644
index 0000000..734da06
--- /dev/null
+++ b/winpr/libwinpr/crt/test/TestTypes.c
@@ -0,0 +1,115 @@
+
+#include <stdio.h>
+#include <winpr/crt.h>
+#include <winpr/windows.h>
+
+#define EXPECTED_SIZEOF_BYTE 1
+#define EXPECTED_SIZEOF_BOOLEAN 1
+#define EXPECTED_SIZEOF_CHAR 1
+#define EXPECTED_SIZEOF_UCHAR 1
+#define EXPECTED_SIZEOF_INT8 1
+#define EXPECTED_SIZEOF_UINT8 1
+#define EXPECTED_SIZEOF_INT16 2
+#define EXPECTED_SIZEOF_UINT16 2
+#define EXPECTED_SIZEOF_WORD 2
+#define EXPECTED_SIZEOF_WCHAR 2
+#define EXPECTED_SIZEOF_SHORT 2
+#define EXPECTED_SIZEOF_USHORT 2
+#define EXPECTED_SIZEOF_BOOL 4
+#define EXPECTED_SIZEOF_INT 4
+#define EXPECTED_SIZEOF_UINT 4
+#define EXPECTED_SIZEOF_INT32 4
+#define EXPECTED_SIZEOF_UINT32 4
+#define EXPECTED_SIZEOF_DWORD 4
+#define EXPECTED_SIZEOF_DWORD32 4
+#define EXPECTED_SIZEOF_LONG 4
+#define EXPECTED_SIZEOF_LONG32 4
+#define EXPECTED_SIZEOF_INT64 8
+#define EXPECTED_SIZEOF_UINT64 8
+#define EXPECTED_SIZEOF_DWORD64 8
+#define EXPECTED_SIZEOF_DWORDLONG 8
+#define EXPECTED_SIZEOF_LONG64 8
+#define EXPECTED_SIZEOF_ULONGLONG 8
+#define EXPECTED_SIZEOF_LUID 8
+#define EXPECTED_SIZEOF_FILETIME 8
+#define EXPECTED_SIZEOF_LARGE_INTEGER 8
+#define EXPECTED_SIZEOF_ULARGE_INTEGER 8
+#define EXPECTED_SIZEOF_GUID 16
+#define EXPECTED_SIZEOF_SYSTEMTIME 16
+#define EXPECTED_SIZEOF_SIZE_T sizeof(void*)
+#define EXPECTED_SIZEOF_INT_PTR sizeof(void*)
+#define EXPECTED_SIZEOF_UINT_PTR sizeof(void*)
+#define EXPECTED_SIZEOF_DWORD_PTR sizeof(void*)
+#define EXPECTED_SIZEOF_LONG_PTR sizeof(void*)
+#define EXPECTED_SIZEOF_ULONG_PTR sizeof(void*)
+
+#define TEST_SIZEOF_TYPE(_name) \
+ if (sizeof(_name) != EXPECTED_SIZEOF_##_name) \
+ { \
+ fprintf(stderr, "sizeof(%s) mismatch: Actual: %" PRIuz ", Expected: %" PRIuz "\n", #_name, \
+ sizeof(_name), (size_t)EXPECTED_SIZEOF_##_name); \
+ status = -1; \
+ }
+
+int TestTypes(int argc, char* argv[])
+{
+ int status = 0;
+
+ WINPR_UNUSED(argc);
+ WINPR_UNUSED(argv);
+
+ TEST_SIZEOF_TYPE(INT8)
+ TEST_SIZEOF_TYPE(UINT8)
+
+ TEST_SIZEOF_TYPE(BYTE)
+ TEST_SIZEOF_TYPE(BOOLEAN)
+ TEST_SIZEOF_TYPE(CHAR)
+ TEST_SIZEOF_TYPE(UCHAR)
+
+ TEST_SIZEOF_TYPE(INT16)
+ TEST_SIZEOF_TYPE(UINT16)
+
+ TEST_SIZEOF_TYPE(WORD)
+ TEST_SIZEOF_TYPE(WCHAR)
+ TEST_SIZEOF_TYPE(SHORT)
+ TEST_SIZEOF_TYPE(USHORT)
+
+ /* fails on OS X */
+ // TEST_SIZEOF_TYPE(BOOL)
+
+ TEST_SIZEOF_TYPE(INT)
+ TEST_SIZEOF_TYPE(UINT)
+ TEST_SIZEOF_TYPE(DWORD)
+ TEST_SIZEOF_TYPE(DWORD32)
+ TEST_SIZEOF_TYPE(LONG)
+ TEST_SIZEOF_TYPE(LONG32)
+
+ TEST_SIZEOF_TYPE(INT32)
+ TEST_SIZEOF_TYPE(UINT32)
+
+ TEST_SIZEOF_TYPE(INT64)
+ TEST_SIZEOF_TYPE(UINT64)
+
+ TEST_SIZEOF_TYPE(DWORD64)
+ TEST_SIZEOF_TYPE(DWORDLONG)
+
+ TEST_SIZEOF_TYPE(LONG64)
+ TEST_SIZEOF_TYPE(ULONGLONG)
+
+ TEST_SIZEOF_TYPE(LUID)
+ TEST_SIZEOF_TYPE(FILETIME)
+ TEST_SIZEOF_TYPE(LARGE_INTEGER)
+ TEST_SIZEOF_TYPE(ULARGE_INTEGER)
+
+ TEST_SIZEOF_TYPE(GUID)
+ TEST_SIZEOF_TYPE(SYSTEMTIME)
+
+ TEST_SIZEOF_TYPE(SIZE_T)
+ TEST_SIZEOF_TYPE(INT_PTR)
+ TEST_SIZEOF_TYPE(UINT_PTR)
+ TEST_SIZEOF_TYPE(DWORD_PTR)
+ TEST_SIZEOF_TYPE(LONG_PTR)
+ TEST_SIZEOF_TYPE(ULONG_PTR)
+
+ return status;
+}
diff --git a/winpr/libwinpr/crt/test/TestUnicodeConversion.c b/winpr/libwinpr/crt/test/TestUnicodeConversion.c
new file mode 100644
index 0000000..a5c4c75
--- /dev/null
+++ b/winpr/libwinpr/crt/test/TestUnicodeConversion.c
@@ -0,0 +1,1289 @@
+
+#include <stdio.h>
+#include <winpr/wtypes.h>
+#include <winpr/crt.h>
+#include <winpr/assert.h>
+#include <winpr/error.h>
+#include <winpr/print.h>
+#include <winpr/windows.h>
+
+#define TESTCASE_BUFFER_SIZE 8192
+
+#ifndef MIN
+#define MIN(x, y) (((x) < (y)) ? (x) : (y))
+#endif
+
+typedef struct
+{
+ char* utf8;
+ size_t utf8len;
+ WCHAR* utf16;
+ size_t utf16len;
+} testcase_t;
+
+// TODO: The unit tests do not check for valid code points, so always end the test
+// strings with a simple ASCII symbol for now.
+static const testcase_t unit_testcases[] = {
+ { "foo", 3, "f\x00o\x00o\x00\x00\x00", 3 },
+ { "foo", 4, "f\x00o\x00o\x00\x00\x00", 4 },
+ { "✊🎅ęʥ꣸𑗊a", 19,
+ "\x0a\x27\x3c\xd8\x85\xdf\x19\x01\xa5\x02\xf8\xa8\x05\xd8\xca\xdd\x61\x00\x00\x00", 9 }
+};
+
+static void create_prefix(char* prefix, size_t prefixlen, size_t buffersize, SSIZE_T rc,
+ SSIZE_T inputlen, const testcase_t* test, const char* fkt, size_t line)
+{
+ _snprintf(prefix, prefixlen,
+ "[%s:%" PRIuz "] '%s' [utf8: %" PRIuz ", utf16: %" PRIuz "] buffersize: %" PRIuz
+ ", rc: %" PRIdz ", inputlen: %" PRIdz ":: ",
+ fkt, line, test->utf8, test->utf8len, test->utf16len, buffersize, rc, inputlen);
+}
+
+static BOOL check_short_buffer(const char* prefix, int rc, size_t buffersize,
+ const testcase_t* test, BOOL utf8)
+{
+ if ((rc > 0) && ((size_t)rc <= buffersize))
+ return TRUE;
+
+ size_t len = test->utf8len;
+ if (!utf8)
+ len = test->utf16len;
+
+ if (buffersize > len)
+ {
+ fprintf(stderr,
+ "%s length does not match buffersize: %" PRId32 " != %" PRIuz
+ ",but is large enough to hold result\n",
+ prefix, rc, buffersize);
+ return FALSE;
+ }
+ const DWORD err = GetLastError();
+ if (err != ERROR_INSUFFICIENT_BUFFER)
+ {
+
+ fprintf(stderr,
+ "%s length does not match buffersize: %" PRId32 " != %" PRIuz
+ ", unexpected GetLastError() 0x08%" PRIx32 "\n",
+ prefix, rc, buffersize, err);
+ return FALSE;
+ }
+ else
+ return TRUE;
+}
+
+#define compare_utf16(what, buffersize, rc, inputlen, test) \
+ compare_utf16_int((what), (buffersize), (rc), (inputlen), (test), __func__, __LINE__)
+static BOOL compare_utf16_int(const WCHAR* what, size_t buffersize, SSIZE_T rc, SSIZE_T inputlen,
+ const testcase_t* test, const char* fkt, size_t line)
+{
+ char prefix[8192] = { 0 };
+ create_prefix(prefix, ARRAYSIZE(prefix), buffersize, rc, inputlen, test, fkt, line);
+
+ WINPR_ASSERT(what || (buffersize == 0));
+ WINPR_ASSERT(test);
+
+ const size_t welen = _wcsnlen(test->utf16, test->utf16len);
+ if (buffersize > welen)
+ {
+ if ((rc < 0) || ((size_t)rc != welen))
+ {
+ fprintf(stderr, "%s length does not match expectation: %" PRIdz " != %" PRIuz "\n",
+ prefix, rc, welen);
+ return FALSE;
+ }
+ }
+ else
+ {
+ if (!check_short_buffer(prefix, rc, buffersize, test, FALSE))
+ return FALSE;
+ }
+
+ if ((rc > 0) && (buffersize > (size_t)rc))
+ {
+ const size_t wlen = _wcsnlen(what, buffersize);
+ if ((rc < 0) || (wlen > (size_t)rc))
+ {
+ fprintf(stderr, "%s length does not match wcslen: %" PRIdz " < %" PRIuz "\n", prefix,
+ rc, wlen);
+ return FALSE;
+ }
+ }
+
+ if (rc >= 0)
+ {
+ if (memcmp(test->utf16, what, rc * sizeof(WCHAR)) != 0)
+ {
+ fprintf(stderr, "%s contents does not match expectations: TODO '%s' != '%s'\n", prefix,
+ test->utf8, test->utf8);
+ return FALSE;
+ }
+ }
+
+ printf("%s success\n", prefix);
+
+ return TRUE;
+}
+
+#define compare_utf8(what, buffersize, rc, inputlen, test) \
+ compare_utf8_int((what), (buffersize), (rc), (inputlen), (test), __func__, __LINE__)
+static BOOL compare_utf8_int(const char* what, size_t buffersize, SSIZE_T rc, SSIZE_T inputlen,
+ const testcase_t* test, const char* fkt, size_t line)
+{
+ char prefix[8192] = { 0 };
+ create_prefix(prefix, ARRAYSIZE(prefix), buffersize, rc, inputlen, test, fkt, line);
+
+ WINPR_ASSERT(what || (buffersize == 0));
+ WINPR_ASSERT(test);
+
+ const size_t slen = strnlen(test->utf8, test->utf8len);
+ if (buffersize > slen)
+ {
+ if ((rc < 0) || ((size_t)rc != slen))
+ {
+ fprintf(stderr, "%s length does not match expectation: %" PRIdz " != %" PRIuz "\n",
+ prefix, rc, slen);
+ return FALSE;
+ }
+ }
+ else
+ {
+ if (!check_short_buffer(prefix, rc, buffersize, test, TRUE))
+ return FALSE;
+ }
+
+ if ((rc > 0) && (buffersize > (size_t)rc))
+ {
+ const size_t wlen = strnlen(what, buffersize);
+ if (wlen != (size_t)rc)
+ {
+ fprintf(stderr, "%s length does not match strnlen: %" PRIdz " != %" PRIuz "\n", prefix,
+ rc, wlen);
+ return FALSE;
+ }
+ }
+
+ if (rc >= 0)
+ {
+ if (memcmp(test->utf8, what, rc) != 0)
+ {
+ fprintf(stderr, "%s contents does not match expectations: '%s' != '%s'\n", prefix, what,
+ test->utf8);
+ return FALSE;
+ }
+ }
+
+ printf("%s success\n", prefix);
+
+ return TRUE;
+}
+
+static BOOL test_convert_to_utf16(const testcase_t* test)
+{
+ const size_t len[] = { TESTCASE_BUFFER_SIZE, test->utf16len, test->utf16len + 1,
+ test->utf16len - 1 };
+ const size_t max = test->utf16len > 0 ? ARRAYSIZE(len) : ARRAYSIZE(len) - 1;
+
+ const SSIZE_T rc2 = ConvertUtf8ToWChar(test->utf8, NULL, 0);
+ const size_t wlen = _wcsnlen(test->utf16, test->utf16len);
+ if ((rc2 < 0) || ((size_t)rc2 != wlen))
+ {
+ char prefix[8192] = { 0 };
+ create_prefix(prefix, ARRAYSIZE(prefix), 0, rc2, -1, test, __func__, __LINE__);
+ fprintf(stderr, "%s ConvertUtf8ToWChar(%s, NULL, 0) expected %" PRIuz ", got %" PRIdz "\n",
+ prefix, test->utf8, wlen, rc2);
+ return FALSE;
+ }
+ for (size_t x = 0; x < max; x++)
+ {
+ WCHAR buffer[TESTCASE_BUFFER_SIZE] = { 0 };
+ const SSIZE_T rc = ConvertUtf8ToWChar(test->utf8, buffer, len[x]);
+ if (!compare_utf16(buffer, len[x], rc, -1, test))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static BOOL test_convert_to_utf16_n(const testcase_t* test)
+{
+ const size_t len[] = { TESTCASE_BUFFER_SIZE, test->utf16len, test->utf16len + 1,
+ test->utf16len - 1 };
+ const size_t max = test->utf16len > 0 ? ARRAYSIZE(len) : ARRAYSIZE(len) - 1;
+
+ const SSIZE_T rc2 = ConvertUtf8NToWChar(test->utf8, test->utf8len, NULL, 0);
+ const size_t wlen = _wcsnlen(test->utf16, test->utf16len);
+ if ((rc2 < 0) || ((size_t)rc2 != wlen))
+ {
+ char prefix[8192] = { 0 };
+ create_prefix(prefix, ARRAYSIZE(prefix), 0, rc2, test->utf8len, test, __func__, __LINE__);
+ fprintf(stderr,
+ "%s ConvertUtf8NToWChar(%s, %" PRIuz ", NULL, 0) expected %" PRIuz ", got %" PRIdz
+ "\n",
+ prefix, test->utf8, test->utf8len, wlen, rc2);
+ return FALSE;
+ }
+
+ for (size_t x = 0; x < max; x++)
+ {
+ const size_t ilen[] = { TESTCASE_BUFFER_SIZE, test->utf8len, test->utf8len + 1,
+ test->utf8len - 1 };
+ const size_t imax = test->utf8len > 0 ? ARRAYSIZE(ilen) : ARRAYSIZE(ilen) - 1;
+
+ for (size_t y = 0; y < imax; y++)
+ {
+ WCHAR buffer[TESTCASE_BUFFER_SIZE] = { 0 };
+ SSIZE_T rc = ConvertUtf8NToWChar(test->utf8, ilen[x], buffer, len[x]);
+ if (!compare_utf16(buffer, len[x], rc, ilen[x], test))
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+static BOOL test_convert_to_utf8(const testcase_t* test)
+{
+ const size_t len[] = { TESTCASE_BUFFER_SIZE, test->utf8len, test->utf8len + 1,
+ test->utf8len - 1 };
+ const size_t max = test->utf8len > 0 ? ARRAYSIZE(len) : ARRAYSIZE(len) - 1;
+
+ const SSIZE_T rc2 = ConvertWCharToUtf8(test->utf16, NULL, 0);
+ const size_t wlen = strnlen(test->utf8, test->utf8len);
+ if ((rc2 < 0) || ((size_t)rc2 != wlen))
+ {
+ char prefix[8192] = { 0 };
+ create_prefix(prefix, ARRAYSIZE(prefix), 0, rc2, -1, test, __func__, __LINE__);
+ fprintf(stderr, "%s ConvertWCharToUtf8(%s, NULL, 0) expected %" PRIuz ", got %" PRIdz "\n",
+ prefix, test->utf8, wlen, rc2);
+ return FALSE;
+ }
+
+ for (size_t x = 0; x < max; x++)
+ {
+ char buffer[TESTCASE_BUFFER_SIZE] = { 0 };
+ SSIZE_T rc = ConvertWCharToUtf8(test->utf16, buffer, len[x]);
+ if (!compare_utf8(buffer, len[x], rc, -1, test))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static BOOL test_convert_to_utf8_n(const testcase_t* test)
+{
+ const size_t len[] = { TESTCASE_BUFFER_SIZE, test->utf8len, test->utf8len + 1,
+ test->utf8len - 1 };
+ const size_t max = test->utf8len > 0 ? ARRAYSIZE(len) : ARRAYSIZE(len) - 1;
+
+ const SSIZE_T rc2 = ConvertWCharNToUtf8(test->utf16, test->utf16len, NULL, 0);
+ const size_t wlen = strnlen(test->utf8, test->utf8len);
+ if ((rc2 < 0) || ((size_t)rc2 != wlen))
+ {
+ char prefix[8192] = { 0 };
+ create_prefix(prefix, ARRAYSIZE(prefix), 0, rc2, test->utf16len, test, __func__, __LINE__);
+ fprintf(stderr,
+ "%s ConvertWCharNToUtf8(%s, %" PRIuz ", NULL, 0) expected %" PRIuz ", got %" PRIdz
+ "\n",
+ prefix, test->utf8, test->utf16len, wlen, rc2);
+ return FALSE;
+ }
+
+ for (size_t x = 0; x < max; x++)
+ {
+ const size_t ilen[] = { TESTCASE_BUFFER_SIZE, test->utf16len, test->utf16len + 1,
+ test->utf16len - 1 };
+ const size_t imax = test->utf16len > 0 ? ARRAYSIZE(ilen) : ARRAYSIZE(ilen) - 1;
+
+ for (size_t y = 0; y < imax; y++)
+ {
+ char buffer[TESTCASE_BUFFER_SIZE] = { 0 };
+ SSIZE_T rc = ConvertWCharNToUtf8(test->utf16, ilen[x], buffer, len[x]);
+ if (!compare_utf8(buffer, len[x], rc, ilen[x], test))
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+static BOOL test_conversion(const testcase_t* testcases, size_t count)
+{
+ WINPR_ASSERT(testcases || (count == 0));
+ for (size_t x = 0; x < count; x++)
+ {
+ const testcase_t* test = &testcases[x];
+
+ printf("Running test case %" PRIuz " [%s]\n", x, test->utf8);
+ if (!test_convert_to_utf16(test))
+ return FALSE;
+ if (!test_convert_to_utf16_n(test))
+ return FALSE;
+ if (!test_convert_to_utf8(test))
+ return FALSE;
+ if (!test_convert_to_utf8_n(test))
+ return FALSE;
+ }
+ return TRUE;
+}
+
+#if defined(WITH_WINPR_DEPRECATED)
+
+#define compare_win_utf16(what, buffersize, rc, inputlen, test) \
+ compare_win_utf16_int((what), (buffersize), (rc), (inputlen), (test), __func__, __LINE__)
+static BOOL compare_win_utf16_int(const WCHAR* what, size_t buffersize, int rc, int inputlen,
+ const testcase_t* test, const char* fkt, size_t line)
+{
+ char prefix[8192] = { 0 };
+ create_prefix(prefix, ARRAYSIZE(prefix), buffersize, rc, inputlen, test, fkt, line);
+
+ WINPR_ASSERT(what || (buffersize == 0));
+ WINPR_ASSERT(test);
+
+ BOOL isNullTerminated = TRUE;
+ if (inputlen > 0)
+ isNullTerminated = strnlen(test->utf8, inputlen) < inputlen;
+ size_t welen = _wcsnlen(test->utf16, buffersize);
+ if (isNullTerminated)
+ welen++;
+
+ if (buffersize >= welen)
+ {
+ if ((inputlen >= 0) && (rc > buffersize))
+ {
+ fprintf(stderr, "%s length does not match expectation: %d > %" PRIuz "\n", prefix, rc,
+ buffersize);
+ return FALSE;
+ }
+ else if ((inputlen < 0) && (rc != welen))
+ {
+ fprintf(stderr, "%s length does not match expectation: %d != %" PRIuz "\n", prefix, rc,
+ welen);
+ return FALSE;
+ }
+ }
+ else
+ {
+ if (!check_short_buffer(prefix, rc, buffersize, test, FALSE))
+ return FALSE;
+ }
+
+ if ((rc > 0) && (buffersize > rc))
+ {
+ size_t wlen = _wcsnlen(what, buffersize);
+ if (isNullTerminated)
+ wlen++;
+ if ((inputlen >= 0) && (buffersize < rc))
+ {
+ fprintf(stderr, "%s length does not match wcslen: %d > %" PRIuz "\n", prefix, rc,
+ buffersize);
+ return FALSE;
+ }
+ else if ((inputlen < 0) && (welen > rc))
+ {
+ fprintf(stderr, "%s length does not match wcslen: %d < %" PRIuz "\n", prefix, rc, wlen);
+ return FALSE;
+ }
+ }
+
+ const size_t cmp_size = MIN(rc, test->utf16len) * sizeof(WCHAR);
+ if (memcmp(test->utf16, what, cmp_size) != 0)
+ {
+ fprintf(stderr, "%s contents does not match expectations: TODO '%s' != '%s'\n", prefix,
+ test->utf8, test->utf8);
+ return FALSE;
+ }
+
+ printf("%s success\n", prefix);
+
+ return TRUE;
+}
+
+#define compare_win_utf8(what, buffersize, rc, inputlen, test) \
+ compare_win_utf8_int((what), (buffersize), (rc), (inputlen), (test), __func__, __LINE__)
+static BOOL compare_win_utf8_int(const char* what, size_t buffersize, SSIZE_T rc, SSIZE_T inputlen,
+ const testcase_t* test, const char* fkt, size_t line)
+{
+ char prefix[8192] = { 0 };
+ create_prefix(prefix, ARRAYSIZE(prefix), buffersize, rc, inputlen, test, fkt, line);
+
+ WINPR_ASSERT(what || (buffersize == 0));
+ WINPR_ASSERT(test);
+
+ BOOL isNullTerminated = TRUE;
+ if (inputlen > 0)
+ isNullTerminated = _wcsnlen(test->utf16, inputlen) < inputlen;
+
+ size_t slen = strnlen(test->utf8, test->utf8len);
+ if (isNullTerminated)
+ slen++;
+
+ if (buffersize > slen)
+ {
+ if ((inputlen >= 0) && (rc > buffersize))
+ {
+ fprintf(stderr, "%s length does not match expectation: %" PRIdz " > %" PRIuz "\n",
+ prefix, rc, buffersize);
+ return FALSE;
+ }
+ else if ((inputlen < 0) && (rc != slen))
+ {
+ fprintf(stderr, "%s length does not match expectation: %" PRIdz " != %" PRIuz "\n",
+ prefix, rc, slen);
+ return FALSE;
+ }
+ }
+ else
+ {
+ if (!check_short_buffer(prefix, rc, buffersize, test, TRUE))
+ return FALSE;
+ }
+
+ if ((rc > 0) && (buffersize > rc))
+ {
+ size_t wlen = strnlen(what, buffersize);
+ if (isNullTerminated)
+ wlen++;
+
+ if (wlen > rc)
+ {
+ fprintf(stderr, "%s length does not match wcslen: %" PRIdz " < %" PRIuz "\n", prefix,
+ rc, wlen);
+ return FALSE;
+ }
+ }
+
+ const size_t cmp_size = MIN(test->utf8len, rc);
+ if (memcmp(test->utf8, what, cmp_size) != 0)
+ {
+ fprintf(stderr, "%s contents does not match expectations: '%s' != '%s'\n", prefix, what,
+ test->utf8);
+ return FALSE;
+ }
+ printf("%s success\n", prefix);
+
+ return TRUE;
+}
+#endif
+
+#if defined(WITH_WINPR_DEPRECATED)
+static BOOL test_win_convert_to_utf16(const testcase_t* test)
+{
+ const size_t len[] = { TESTCASE_BUFFER_SIZE, test->utf16len, test->utf16len + 1,
+ test->utf16len - 1 };
+ const size_t max = test->utf16len > 0 ? ARRAYSIZE(len) : ARRAYSIZE(len) - 1;
+
+ const int rc2 = MultiByteToWideChar(CP_UTF8, 0, test->utf8, -1, NULL, 0);
+ const size_t wlen = _wcsnlen(test->utf16, test->utf16len);
+ if (rc2 != wlen + 1)
+ {
+ char prefix[8192] = { 0 };
+ create_prefix(prefix, ARRAYSIZE(prefix), 0, rc2, -1, test, __func__, __LINE__);
+ fprintf(stderr,
+ "%s MultiByteToWideChar(CP_UTF8, 0, %s, [-1], NULL, 0) expected %" PRIuz
+ ", got %d\n",
+ prefix, test->utf8, wlen + 1, rc2);
+ return FALSE;
+ }
+ for (size_t x = 0; x < max; x++)
+ {
+ WCHAR buffer[TESTCASE_BUFFER_SIZE] = { 0 };
+ const int rc = MultiByteToWideChar(CP_UTF8, 0, test->utf8, -1, buffer, len[x]);
+ if (!compare_win_utf16(buffer, len[x], rc, -1, test))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static BOOL test_win_convert_to_utf16_n(const testcase_t* test)
+{
+ const size_t len[] = { TESTCASE_BUFFER_SIZE, test->utf16len, test->utf16len + 1,
+ test->utf16len - 1 };
+ const size_t max = test->utf16len > 0 ? ARRAYSIZE(len) : ARRAYSIZE(len) - 1;
+
+ BOOL isNullTerminated = strnlen(test->utf8, test->utf8len) < test->utf8len;
+ const int rc2 = MultiByteToWideChar(CP_UTF8, 0, test->utf8, test->utf8len, NULL, 0);
+ size_t wlen = _wcsnlen(test->utf16, test->utf16len);
+ if (isNullTerminated)
+ wlen++;
+
+ if (rc2 != wlen)
+ {
+ char prefix[8192] = { 0 };
+ create_prefix(prefix, ARRAYSIZE(prefix), 0, rc2, test->utf8len, test, __func__, __LINE__);
+ fprintf(stderr,
+ "%s MultiByteToWideChar(CP_UTF8, 0, %s, %" PRIuz ", NULL, 0) expected %" PRIuz
+ ", got %d\n",
+ prefix, test->utf8, test->utf8len, wlen, rc2);
+ return FALSE;
+ }
+
+ for (size_t x = 0; x < max; x++)
+ {
+ const size_t ilen[] = { TESTCASE_BUFFER_SIZE, test->utf8len, test->utf8len + 1,
+ test->utf8len - 1 };
+ const size_t imax = test->utf8len > 0 ? ARRAYSIZE(ilen) : ARRAYSIZE(ilen) - 1;
+
+ for (size_t y = 0; y < imax; y++)
+ {
+ char mbuffer[TESTCASE_BUFFER_SIZE] = { 0 };
+ WCHAR buffer[TESTCASE_BUFFER_SIZE] = { 0 };
+ strncpy(mbuffer, test->utf8, test->utf8len);
+ const int rc = MultiByteToWideChar(CP_UTF8, 0, mbuffer, ilen[x], buffer, len[x]);
+ if (!compare_win_utf16(buffer, len[x], rc, ilen[x], test))
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+#endif
+
+#if defined(WITH_WINPR_DEPRECATED)
+static BOOL test_win_convert_to_utf8(const testcase_t* test)
+{
+ const size_t len[] = { TESTCASE_BUFFER_SIZE, test->utf8len, test->utf8len + 1,
+ test->utf8len - 1 };
+ const size_t max = test->utf8len > 0 ? ARRAYSIZE(len) : ARRAYSIZE(len) - 1;
+
+ const int rc2 = WideCharToMultiByte(CP_UTF8, 0, test->utf16, -1, NULL, 0, NULL, NULL);
+ const size_t wlen = strnlen(test->utf8, test->utf8len) + 1;
+ if (rc2 != wlen)
+ {
+ char prefix[8192] = { 0 };
+ create_prefix(prefix, ARRAYSIZE(prefix), 0, rc2, -1, test, __func__, __LINE__);
+ fprintf(stderr,
+ "%s WideCharToMultiByte(CP_UTF8, 0, %s, -1, NULL, 0, NULL, NULL) expected %" PRIuz
+ ", got %d\n",
+ prefix, test->utf8, wlen, rc2);
+ return FALSE;
+ }
+
+ for (size_t x = 0; x < max; x++)
+ {
+ char buffer[TESTCASE_BUFFER_SIZE] = { 0 };
+ int rc = WideCharToMultiByte(CP_UTF8, 0, test->utf16, -1, buffer, len[x], NULL, NULL);
+ if (!compare_win_utf8(buffer, len[x], rc, -1, test))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static BOOL test_win_convert_to_utf8_n(const testcase_t* test)
+{
+ const size_t len[] = { TESTCASE_BUFFER_SIZE, test->utf8len, test->utf8len + 1,
+ test->utf8len - 1 };
+ const size_t max = test->utf8len > 0 ? ARRAYSIZE(len) : ARRAYSIZE(len) - 1;
+
+ const BOOL isNullTerminated = _wcsnlen(test->utf16, test->utf16len) < test->utf16len;
+ const int rc2 =
+ WideCharToMultiByte(CP_UTF8, 0, test->utf16, test->utf16len, NULL, 0, NULL, NULL);
+ size_t wlen = strnlen(test->utf8, test->utf8len);
+ if (isNullTerminated)
+ wlen++;
+
+ if (rc2 != wlen)
+ {
+ char prefix[8192] = { 0 };
+ create_prefix(prefix, ARRAYSIZE(prefix), 0, rc2, test->utf16len, test, __func__, __LINE__);
+ fprintf(stderr,
+ "%s WideCharToMultiByte(CP_UTF8, 0, %s, %" PRIuz
+ ", NULL, 0, NULL, NULL) expected %" PRIuz ", got %d\n",
+ prefix, test->utf8, test->utf16len, wlen, rc2);
+ return FALSE;
+ }
+
+ for (size_t x = 0; x < max; x++)
+ {
+ const size_t ilen[] = { TESTCASE_BUFFER_SIZE, test->utf16len, test->utf16len + 1,
+ test->utf16len - 1 };
+ const size_t imax = test->utf16len > 0 ? ARRAYSIZE(ilen) : ARRAYSIZE(ilen) - 1;
+
+ for (size_t y = 0; y < imax; y++)
+ {
+ WCHAR wbuffer[TESTCASE_BUFFER_SIZE] = { 0 };
+ char buffer[TESTCASE_BUFFER_SIZE] = { 0 };
+ memcpy(wbuffer, test->utf16, test->utf16len * sizeof(WCHAR));
+ const int rc =
+ WideCharToMultiByte(CP_UTF8, 0, wbuffer, ilen[x], buffer, len[x], NULL, NULL);
+ if (!compare_win_utf8(buffer, len[x], rc, ilen[x], test))
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+static BOOL test_win_conversion(const testcase_t* testcases, size_t count)
+{
+ WINPR_ASSERT(testcases || (count == 0));
+ for (size_t x = 0; x < count; x++)
+ {
+ const testcase_t* test = &testcases[x];
+
+ printf("Running test case %" PRIuz " [%s]\n", x, test->utf8);
+ if (!test_win_convert_to_utf16(test))
+ return FALSE;
+ if (!test_win_convert_to_utf16_n(test))
+ return FALSE;
+ if (!test_win_convert_to_utf8(test))
+ return FALSE;
+ if (!test_win_convert_to_utf8_n(test))
+ return FALSE;
+ }
+ return TRUE;
+}
+#endif
+
+#if defined(WITH_WINPR_DEPRECATED)
+/* Letters */
+
+static BYTE c_cedilla_UTF8[] = "\xC3\xA7\x00";
+static BYTE c_cedilla_UTF16[] = "\xE7\x00\x00\x00";
+static int c_cedilla_cchWideChar = 2;
+static int c_cedilla_cbMultiByte = 3;
+
+/* English */
+
+static BYTE en_Hello_UTF8[] = "Hello\0";
+static BYTE en_Hello_UTF16[] = "\x48\x00\x65\x00\x6C\x00\x6C\x00\x6F\x00\x00\x00";
+static int en_Hello_cchWideChar = 6;
+static int en_Hello_cbMultiByte = 6;
+
+static BYTE en_HowAreYou_UTF8[] = "How are you?\0";
+static BYTE en_HowAreYou_UTF16[] =
+ "\x48\x00\x6F\x00\x77\x00\x20\x00\x61\x00\x72\x00\x65\x00\x20\x00"
+ "\x79\x00\x6F\x00\x75\x00\x3F\x00\x00\x00";
+static int en_HowAreYou_cchWideChar = 13;
+static int en_HowAreYou_cbMultiByte = 13;
+
+/* French */
+
+static BYTE fr_Hello_UTF8[] = "Allo\0";
+static BYTE fr_Hello_UTF16[] = "\x41\x00\x6C\x00\x6C\x00\x6F\x00\x00\x00";
+static int fr_Hello_cchWideChar = 5;
+static int fr_Hello_cbMultiByte = 5;
+
+static BYTE fr_HowAreYou_UTF8[] =
+ "\x43\x6F\x6D\x6D\x65\x6E\x74\x20\xC3\xA7\x61\x20\x76\x61\x3F\x00";
+static BYTE fr_HowAreYou_UTF16[] =
+ "\x43\x00\x6F\x00\x6D\x00\x6D\x00\x65\x00\x6E\x00\x74\x00\x20\x00"
+ "\xE7\x00\x61\x00\x20\x00\x76\x00\x61\x00\x3F\x00\x00\x00";
+static int fr_HowAreYou_cchWideChar = 15;
+static int fr_HowAreYou_cbMultiByte = 16;
+
+/* Russian */
+
+static BYTE ru_Hello_UTF8[] = "\xD0\x97\xD0\xB4\xD0\xBE\xD1\x80\xD0\xBE\xD0\xB2\xD0\xBE\x00";
+static BYTE ru_Hello_UTF16[] = "\x17\x04\x34\x04\x3E\x04\x40\x04\x3E\x04\x32\x04\x3E\x04\x00\x00";
+static int ru_Hello_cchWideChar = 8;
+static int ru_Hello_cbMultiByte = 15;
+
+static BYTE ru_HowAreYou_UTF8[] =
+ "\xD0\x9A\xD0\xB0\xD0\xBA\x20\xD0\xB4\xD0\xB5\xD0\xBB\xD0\xB0\x3F\x00";
+static BYTE ru_HowAreYou_UTF16[] =
+ "\x1A\x04\x30\x04\x3A\x04\x20\x00\x34\x04\x35\x04\x3B\x04\x30\x04"
+ "\x3F\x00\x00\x00";
+static int ru_HowAreYou_cchWideChar = 10;
+static int ru_HowAreYou_cbMultiByte = 17;
+
+/* Arabic */
+
+static BYTE ar_Hello_UTF8[] = "\xD8\xA7\xD9\x84\xD8\xB3\xD9\x84\xD8\xA7\xD9\x85\x20\xD8\xB9\xD9"
+ "\x84\xD9\x8A\xD9\x83\xD9\x85\x00";
+static BYTE ar_Hello_UTF16[] = "\x27\x06\x44\x06\x33\x06\x44\x06\x27\x06\x45\x06\x20\x00\x39\x06"
+ "\x44\x06\x4A\x06\x43\x06\x45\x06\x00\x00";
+static int ar_Hello_cchWideChar = 13;
+static int ar_Hello_cbMultiByte = 24;
+
+static BYTE ar_HowAreYou_UTF8[] = "\xD9\x83\xD9\x8A\xD9\x81\x20\xD8\xAD\xD8\xA7\xD9\x84\xD9\x83\xD8"
+ "\x9F\x00";
+static BYTE ar_HowAreYou_UTF16[] =
+ "\x43\x06\x4A\x06\x41\x06\x20\x00\x2D\x06\x27\x06\x44\x06\x43\x06"
+ "\x1F\x06\x00\x00";
+static int ar_HowAreYou_cchWideChar = 10;
+static int ar_HowAreYou_cbMultiByte = 18;
+
+/* Chinese */
+
+static BYTE ch_Hello_UTF8[] = "\xE4\xBD\xA0\xE5\xA5\xBD\x00";
+static BYTE ch_Hello_UTF16[] = "\x60\x4F\x7D\x59\x00\x00";
+static int ch_Hello_cchWideChar = 3;
+static int ch_Hello_cbMultiByte = 7;
+
+static BYTE ch_HowAreYou_UTF8[] = "\xE4\xBD\xA0\xE5\xA5\xBD\xE5\x90\x97\x00";
+static BYTE ch_HowAreYou_UTF16[] = "\x60\x4F\x7D\x59\x17\x54\x00\x00";
+static int ch_HowAreYou_cchWideChar = 4;
+static int ch_HowAreYou_cbMultiByte = 10;
+
+/* Uppercasing */
+
+static BYTE ru_Administrator_lower[] = "\xd0\x90\xd0\xb4\xd0\xbc\xd0\xb8\xd0\xbd\xd0\xb8\xd1\x81"
+ "\xd1\x82\xd1\x80\xd0\xb0\xd1\x82\xd0\xbe\xd1\x80\x00";
+
+static BYTE ru_Administrator_upper[] = "\xd0\x90\xd0\x94\xd0\x9c\xd0\x98\xd0\x9d\xd0\x98\xd0\xa1"
+ "\xd0\xa2\xd0\xa0\xd0\x90\xd0\xa2\xd0\x9e\xd0\xa0\x00";
+
+static void string_hexdump(const BYTE* data, size_t length)
+{
+ size_t offset = 0;
+
+ char* str = winpr_BinToHexString(data, length, TRUE);
+ if (!str)
+ return;
+
+ while (offset < length)
+ {
+ const size_t diff = (length - offset) * 3;
+ WINPR_ASSERT(diff <= INT_MAX);
+ printf("%04" PRIxz " %.*s\n", offset, (int)diff, &str[offset]);
+ offset += 16;
+ }
+
+ free(str);
+}
+
+static int convert_utf8_to_utf16(BYTE* lpMultiByteStr, BYTE* expected_lpWideCharStr,
+ int expected_cchWideChar)
+{
+ int rc = -1;
+ int length = 0;
+ size_t cbMultiByte = 0;
+ int cchWideChar = 0;
+ LPWSTR lpWideCharStr = NULL;
+
+ cbMultiByte = strlen((char*)lpMultiByteStr);
+ cchWideChar = MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)lpMultiByteStr, -1, NULL, 0);
+
+ printf("MultiByteToWideChar Input UTF8 String:\n");
+ string_hexdump(lpMultiByteStr, cbMultiByte + 1);
+
+ printf("MultiByteToWideChar required cchWideChar: %d\n", cchWideChar);
+
+ if (cchWideChar != expected_cchWideChar)
+ {
+ printf("MultiByteToWideChar unexpected cchWideChar: actual: %d expected: %d\n", cchWideChar,
+ expected_cchWideChar);
+ goto fail;
+ }
+
+ lpWideCharStr = (LPWSTR)calloc((size_t)cchWideChar, sizeof(WCHAR));
+ if (!lpWideCharStr)
+ {
+ printf("MultiByteToWideChar: unable to allocate memory for test\n");
+ goto fail;
+ }
+ lpWideCharStr[cchWideChar - 1] =
+ 0xFFFF; /* should be overwritten if null terminator is inserted properly */
+ length = MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)lpMultiByteStr, cbMultiByte + 1, lpWideCharStr,
+ cchWideChar);
+
+ printf("MultiByteToWideChar converted length (WCHAR): %d\n", length);
+
+ if (!length)
+ {
+ DWORD error = GetLastError();
+ printf("MultiByteToWideChar error: 0x%08" PRIX32 "\n", error);
+ goto fail;
+ }
+
+ if (length != expected_cchWideChar)
+ {
+ printf("MultiByteToWideChar unexpected converted length (WCHAR): actual: %d expected: %d\n",
+ length, expected_cchWideChar);
+ goto fail;
+ }
+
+ if (_wcscmp(lpWideCharStr, (WCHAR*)expected_lpWideCharStr) != 0)
+ {
+ printf("MultiByteToWideChar unexpected string:\n");
+
+ printf("UTF8 String:\n");
+ string_hexdump(lpMultiByteStr, cbMultiByte + 1);
+
+ printf("UTF16 String (actual):\n");
+ string_hexdump((BYTE*)lpWideCharStr, length * sizeof(WCHAR));
+
+ printf("UTF16 String (expected):\n");
+ string_hexdump((BYTE*)expected_lpWideCharStr, expected_cchWideChar * sizeof(WCHAR));
+
+ goto fail;
+ }
+
+ printf("MultiByteToWideChar Output UTF16 String:\n");
+ string_hexdump((BYTE*)lpWideCharStr, length * sizeof(WCHAR));
+ printf("\n");
+
+ rc = length;
+fail:
+ free(lpWideCharStr);
+
+ return rc;
+}
+#endif
+
+#if defined(WITH_WINPR_DEPRECATED)
+static int convert_utf16_to_utf8(BYTE* lpWideCharStr, BYTE* expected_lpMultiByteStr,
+ int expected_cbMultiByte)
+{
+ int rc = -1;
+ int length = 0;
+ int cchWideChar = 0;
+ int cbMultiByte = 0;
+ LPSTR lpMultiByteStr = NULL;
+
+ cchWideChar = _wcslen((WCHAR*)lpWideCharStr);
+ cbMultiByte = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)lpWideCharStr, -1, NULL, 0, NULL, NULL);
+
+ printf("WideCharToMultiByte Input UTF16 String:\n");
+ string_hexdump(lpWideCharStr, (cchWideChar + 1) * sizeof(WCHAR));
+
+ printf("WideCharToMultiByte required cbMultiByte: %d\n", cbMultiByte);
+
+ if (cbMultiByte != expected_cbMultiByte)
+ {
+ printf("WideCharToMultiByte unexpected cbMultiByte: actual: %d expected: %d\n", cbMultiByte,
+ expected_cbMultiByte);
+ goto fail;
+ }
+
+ lpMultiByteStr = (LPSTR)malloc(cbMultiByte);
+ if (!lpMultiByteStr)
+ {
+ printf("WideCharToMultiByte: unable to allocate memory for test\n");
+ goto fail;
+ }
+ lpMultiByteStr[cbMultiByte - 1] =
+ (CHAR)0xFF; /* should be overwritten if null terminator is inserted properly */
+ length = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)lpWideCharStr, cchWideChar + 1,
+ lpMultiByteStr, cbMultiByte, NULL, NULL);
+
+ printf("WideCharToMultiByte converted length (BYTE): %d\n", length);
+
+ if (!length)
+ {
+ DWORD error = GetLastError();
+ printf("WideCharToMultiByte error: 0x%08" PRIX32 "\n", error);
+ goto fail;
+ }
+
+ if (length != expected_cbMultiByte)
+ {
+ printf("WideCharToMultiByte unexpected converted length (BYTE): actual: %d expected: %d\n",
+ length, expected_cbMultiByte);
+ goto fail;
+ }
+
+ if (strcmp(lpMultiByteStr, (char*)expected_lpMultiByteStr) != 0)
+ {
+ printf("WideCharToMultiByte unexpected string:\n");
+
+ printf("UTF16 String:\n");
+ string_hexdump((BYTE*)lpWideCharStr, (cchWideChar + 1) * sizeof(WCHAR));
+
+ printf("UTF8 String (actual):\n");
+ string_hexdump((BYTE*)lpMultiByteStr, cbMultiByte);
+
+ printf("UTF8 String (expected):\n");
+ string_hexdump((BYTE*)expected_lpMultiByteStr, expected_cbMultiByte);
+
+ goto fail;
+ }
+
+ printf("WideCharToMultiByte Output UTF8 String:\n");
+ string_hexdump((BYTE*)lpMultiByteStr, cbMultiByte);
+ printf("\n");
+
+ rc = length;
+fail:
+ free(lpMultiByteStr);
+
+ return rc;
+}
+#endif
+
+#if defined(WITH_WINPR_DEPRECATED)
+static BOOL test_unicode_uppercasing(BYTE* lower, BYTE* upper)
+{
+ WCHAR* lowerW = NULL;
+ int lowerLength = 0;
+ WCHAR* upperW = NULL;
+ int upperLength = 0;
+
+ lowerLength = ConvertToUnicode(CP_UTF8, 0, (LPSTR)lower, -1, &lowerW, 0);
+ upperLength = ConvertToUnicode(CP_UTF8, 0, (LPSTR)upper, -1, &upperW, 0);
+
+ CharUpperBuffW(lowerW, lowerLength);
+
+ if (_wcscmp(lowerW, upperW) != 0)
+ {
+ printf("Lowercase String:\n");
+ string_hexdump((BYTE*)lowerW, lowerLength * 2);
+
+ printf("Uppercase String:\n");
+ string_hexdump((BYTE*)upperW, upperLength * 2);
+
+ return FALSE;
+ }
+
+ free(lowerW);
+ free(upperW);
+
+ printf("success\n\n");
+ return TRUE;
+}
+#endif
+
+#if defined(WITH_WINPR_DEPRECATED)
+static BOOL test_ConvertFromUnicode_wrapper(void)
+{
+ const BYTE src1[] =
+ "\x52\x00\x49\x00\x43\x00\x48\x00\x20\x00\x54\x00\x45\x00\x58\x00\x54\x00\x20\x00\x46\x00"
+ "\x4f\x00\x52\x00\x4d\x00\x41\x00\x54\x00\x40\x00\x40\x00\x40\x00";
+ const BYTE src2[] = "\x52\x00\x49\x00\x43\x00\x48\x00\x20\x00\x54\x00\x45\x00\x58\x00\x54\x00"
+ "\x20\x00\x46\x00\x4f\x00\x52\x00\x4d\x00\x41\x00\x54\x00\x00\x00";
+ /* 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 */
+ const CHAR cmp0[] = { 'R', 'I', 'C', 'H', ' ', 'T', 'E', 'X', 'T',
+ ' ', 'F', 'O', 'R', 'M', 'A', 'T', 0 };
+ CHAR* dst = NULL;
+ int i = 0;
+
+ /* Test unterminated unicode string:
+ * ConvertFromUnicode must always null-terminate, even if the src string isn't
+ */
+
+ printf("Input UTF16 String:\n");
+ string_hexdump((const BYTE*)src1, 19 * sizeof(WCHAR));
+
+ i = ConvertFromUnicode(CP_UTF8, 0, (const WCHAR*)src1, 16, &dst, 0, NULL, NULL);
+ if (i != 16)
+ {
+ fprintf(stderr, "ConvertFromUnicode failure A1: unexpectedly returned %d instead of 16\n",
+ i);
+ goto fail;
+ }
+ if (dst == NULL)
+ {
+ fprintf(stderr, "ConvertFromUnicode failure A2: destination ist NULL\n");
+ goto fail;
+ }
+ if ((i = strlen(dst)) != 16)
+ {
+ fprintf(stderr, "ConvertFromUnicode failure A3: dst length is %d instead of 16\n", i);
+ goto fail;
+ }
+ if (strcmp(dst, cmp0))
+ {
+ fprintf(stderr, "ConvertFromUnicode failure A4: data mismatch\n");
+ goto fail;
+ }
+ printf("Output UTF8 String:\n");
+ string_hexdump((BYTE*)dst, i + 1);
+
+ free(dst);
+ dst = NULL;
+
+ /* Test null-terminated string */
+
+ printf("Input UTF16 String:\n");
+ string_hexdump((const BYTE*)src2, (_wcslen((const WCHAR*)src2) + 1) * sizeof(WCHAR));
+
+ i = ConvertFromUnicode(CP_UTF8, 0, (const WCHAR*)src2, -1, &dst, 0, NULL, NULL);
+ if (i != 17)
+ {
+ fprintf(stderr, "ConvertFromUnicode failure B1: unexpectedly returned %d instead of 17\n",
+ i);
+ goto fail;
+ }
+ if (dst == NULL)
+ {
+ fprintf(stderr, "ConvertFromUnicode failure B2: destination ist NULL\n");
+ goto fail;
+ }
+ if ((i = strlen(dst)) != 16)
+ {
+ fprintf(stderr, "ConvertFromUnicode failure B3: dst length is %d instead of 16\n", i);
+ goto fail;
+ }
+ if (strcmp(dst, cmp0))
+ {
+ fprintf(stderr, "ConvertFromUnicode failure B: data mismatch\n");
+ goto fail;
+ }
+ printf("Output UTF8 String:\n");
+ string_hexdump((BYTE*)dst, i + 1);
+
+ free(dst);
+ dst = NULL;
+
+ printf("success\n\n");
+
+ return TRUE;
+
+fail:
+ free(dst);
+ return FALSE;
+}
+
+static BOOL test_ConvertToUnicode_wrapper(void)
+{
+ /* 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 */
+ const CHAR src1[] = { 'R', 'I', 'C', 'H', ' ', 'T', 'E', 'X', 'T', ' ',
+ 'F', 'O', 'R', 'M', 'A', 'T', '@', '@', '@' };
+ const CHAR src2[] = { 'R', 'I', 'C', 'H', ' ', 'T', 'E', 'X', 'T',
+ ' ', 'F', 'O', 'R', 'M', 'A', 'T', 0 };
+ const BYTE cmp0[] = "\x52\x00\x49\x00\x43\x00\x48\x00\x20\x00\x54\x00\x45\x00\x58\x00\x54\x00"
+ "\x20\x00\x46\x00\x4f\x00\x52\x00\x4d\x00\x41\x00\x54\x00\x00\x00";
+ WCHAR* dst = NULL;
+ int ii = 0;
+ size_t i = 0;
+
+ /* Test static string buffers of differing sizes */
+ {
+ char name[] = "someteststring";
+ const BYTE cmp[] = { 's', 0, 'o', 0, 'm', 0, 'e', 0, 't', 0, 'e', 0, 's', 0, 't', 0,
+ 's', 0, 't', 0, 'r', 0, 'i', 0, 'n', 0, 'g', 0, 0, 0 };
+ WCHAR xname[128] = { 0 };
+ LPWSTR aname = NULL;
+ LPWSTR wname = &xname[0];
+ const size_t len = strnlen(name, ARRAYSIZE(name) - 1);
+ ii = ConvertToUnicode(CP_UTF8, 0, name, len, &wname, ARRAYSIZE(xname));
+ if (ii != (SSIZE_T)len)
+ goto fail;
+
+ if (memcmp(wname, cmp, sizeof(cmp)) != 0)
+ goto fail;
+
+ ii = ConvertToUnicode(CP_UTF8, 0, name, len, &aname, 0);
+ if (ii != (SSIZE_T)len)
+ goto fail;
+ ii = memcmp(aname, cmp, sizeof(cmp));
+ free(aname);
+ if (ii != 0)
+ goto fail;
+ }
+
+ /* Test unterminated unicode string:
+ * ConvertToUnicode must always null-terminate, even if the src string isn't
+ */
+
+ printf("Input UTF8 String:\n");
+ string_hexdump((const BYTE*)src1, 19);
+
+ ii = ConvertToUnicode(CP_UTF8, 0, src1, 16, &dst, 0);
+ if (ii != 16)
+ {
+ fprintf(stderr, "ConvertToUnicode failure A1: unexpectedly returned %d instead of 16\n",
+ ii);
+ goto fail;
+ }
+ i = (size_t)ii;
+ if (dst == NULL)
+ {
+ fprintf(stderr, "ConvertToUnicode failure A2: destination ist NULL\n");
+ goto fail;
+ }
+ if ((i = _wcslen(dst)) != 16)
+ {
+ fprintf(stderr, "ConvertToUnicode failure A3: dst length is %" PRIuz " instead of 16\n", i);
+ goto fail;
+ }
+ if (_wcscmp(dst, (const WCHAR*)cmp0))
+ {
+ fprintf(stderr, "ConvertToUnicode failure A4: data mismatch\n");
+ goto fail;
+ }
+ printf("Output UTF16 String:\n");
+ string_hexdump((const BYTE*)dst, (i + 1) * sizeof(WCHAR));
+
+ free(dst);
+ dst = NULL;
+
+ /* Test null-terminated string */
+
+ printf("Input UTF8 String:\n");
+ string_hexdump((const BYTE*)src2, strlen(src2) + 1);
+
+ i = ConvertToUnicode(CP_UTF8, 0, src2, -1, &dst, 0);
+ if (i != 17)
+ {
+ fprintf(stderr,
+ "ConvertToUnicode failure B1: unexpectedly returned %" PRIuz " instead of 17\n", i);
+ goto fail;
+ }
+ if (dst == NULL)
+ {
+ fprintf(stderr, "ConvertToUnicode failure B2: destination ist NULL\n");
+ goto fail;
+ }
+ if ((i = _wcslen(dst)) != 16)
+ {
+ fprintf(stderr, "ConvertToUnicode failure B3: dst length is %" PRIuz " instead of 16\n", i);
+ goto fail;
+ }
+ if (_wcscmp(dst, (const WCHAR*)cmp0))
+ {
+ fprintf(stderr, "ConvertToUnicode failure B: data mismatch\n");
+ goto fail;
+ }
+ printf("Output UTF16 String:\n");
+ string_hexdump((BYTE*)dst, (i + 1) * 2);
+
+ free(dst);
+ dst = NULL;
+
+ printf("success\n\n");
+
+ return TRUE;
+
+fail:
+ free(dst);
+ return FALSE;
+}
+#endif
+
+int TestUnicodeConversion(int argc, char* argv[])
+{
+ WINPR_UNUSED(argc);
+ WINPR_UNUSED(argv);
+
+ if (!test_conversion(unit_testcases, ARRAYSIZE(unit_testcases)))
+ return -1;
+
+#if defined(WITH_WINPR_DEPRECATED)
+ if (!test_win_conversion(unit_testcases, ARRAYSIZE(unit_testcases)))
+ return -1;
+
+ /* Letters */
+
+ printf("Letters\n");
+
+ if (convert_utf8_to_utf16(c_cedilla_UTF8, c_cedilla_UTF16, c_cedilla_cchWideChar) < 1)
+ return -1;
+
+ if (convert_utf16_to_utf8(c_cedilla_UTF16, c_cedilla_UTF8, c_cedilla_cbMultiByte) < 1)
+ return -1;
+
+ /* English */
+
+ printf("English\n");
+
+ if (convert_utf8_to_utf16(en_Hello_UTF8, en_Hello_UTF16, en_Hello_cchWideChar) < 1)
+ return -1;
+ if (convert_utf8_to_utf16(en_HowAreYou_UTF8, en_HowAreYou_UTF16, en_HowAreYou_cchWideChar) < 1)
+ return -1;
+
+ if (convert_utf16_to_utf8(en_Hello_UTF16, en_Hello_UTF8, en_Hello_cbMultiByte) < 1)
+ return -1;
+ if (convert_utf16_to_utf8(en_HowAreYou_UTF16, en_HowAreYou_UTF8, en_HowAreYou_cbMultiByte) < 1)
+ return -1;
+
+ /* French */
+
+ printf("French\n");
+
+ if (convert_utf8_to_utf16(fr_Hello_UTF8, fr_Hello_UTF16, fr_Hello_cchWideChar) < 1)
+ return -1;
+ if (convert_utf8_to_utf16(fr_HowAreYou_UTF8, fr_HowAreYou_UTF16, fr_HowAreYou_cchWideChar) < 1)
+ return -1;
+
+ if (convert_utf16_to_utf8(fr_Hello_UTF16, fr_Hello_UTF8, fr_Hello_cbMultiByte) < 1)
+ return -1;
+ if (convert_utf16_to_utf8(fr_HowAreYou_UTF16, fr_HowAreYou_UTF8, fr_HowAreYou_cbMultiByte) < 1)
+ return -1;
+
+ /* Russian */
+
+ printf("Russian\n");
+
+ if (convert_utf8_to_utf16(ru_Hello_UTF8, ru_Hello_UTF16, ru_Hello_cchWideChar) < 1)
+ return -1;
+ if (convert_utf8_to_utf16(ru_HowAreYou_UTF8, ru_HowAreYou_UTF16, ru_HowAreYou_cchWideChar) < 1)
+ return -1;
+
+ if (convert_utf16_to_utf8(ru_Hello_UTF16, ru_Hello_UTF8, ru_Hello_cbMultiByte) < 1)
+ return -1;
+ if (convert_utf16_to_utf8(ru_HowAreYou_UTF16, ru_HowAreYou_UTF8, ru_HowAreYou_cbMultiByte) < 1)
+ return -1;
+
+ /* Arabic */
+
+ printf("Arabic\n");
+
+ if (convert_utf8_to_utf16(ar_Hello_UTF8, ar_Hello_UTF16, ar_Hello_cchWideChar) < 1)
+ return -1;
+ if (convert_utf8_to_utf16(ar_HowAreYou_UTF8, ar_HowAreYou_UTF16, ar_HowAreYou_cchWideChar) < 1)
+ return -1;
+
+ if (convert_utf16_to_utf8(ar_Hello_UTF16, ar_Hello_UTF8, ar_Hello_cbMultiByte) < 1)
+ return -1;
+ if (convert_utf16_to_utf8(ar_HowAreYou_UTF16, ar_HowAreYou_UTF8, ar_HowAreYou_cbMultiByte) < 1)
+ return -1;
+
+ /* Chinese */
+
+ printf("Chinese\n");
+
+ if (convert_utf8_to_utf16(ch_Hello_UTF8, ch_Hello_UTF16, ch_Hello_cchWideChar) < 1)
+ return -1;
+ if (convert_utf8_to_utf16(ch_HowAreYou_UTF8, ch_HowAreYou_UTF16, ch_HowAreYou_cchWideChar) < 1)
+ return -1;
+
+ if (convert_utf16_to_utf8(ch_Hello_UTF16, ch_Hello_UTF8, ch_Hello_cbMultiByte) < 1)
+ return -1;
+ if (convert_utf16_to_utf8(ch_HowAreYou_UTF16, ch_HowAreYou_UTF8, ch_HowAreYou_cbMultiByte) < 1)
+ return -1;
+
+#endif
+
+ /* Uppercasing */
+#if defined(WITH_WINPR_DEPRECATED)
+ printf("Uppercasing\n");
+
+ if (!test_unicode_uppercasing(ru_Administrator_lower, ru_Administrator_upper))
+ return -1;
+#endif
+
+ /* ConvertFromUnicode */
+#if defined(WITH_WINPR_DEPRECATED)
+ printf("ConvertFromUnicode\n");
+
+ if (!test_ConvertFromUnicode_wrapper())
+ return -1;
+
+ /* ConvertToUnicode */
+
+ printf("ConvertToUnicode\n");
+
+ if (!test_ConvertToUnicode_wrapper())
+ return -1;
+#endif
+ /*
+
+ printf("----------------------------------------------------------\n\n");
+
+ if (0)
+ {
+ BYTE src[] = { 'R',0,'I',0,'C',0,'H',0,' ',0, 'T',0,'E',0,'X',0,'T',0,'
+ ',0,'F',0,'O',0,'R',0,'M',0,'A',0,'T',0,'@',0,'@',0 };
+ //BYTE src[] = { 'R',0,'I',0,'C',0,'H',0,' ',0, 0,0, 'T',0,'E',0,'X',0,'T',0,'
+ ',0,'F',0,'O',0,'R',0,'M',0,'A',0,'T',0,'@',0,'@',0 };
+ //BYTE src[] = { 0,0,'R',0,'I',0,'C',0,'H',0,' ',0, 'T',0,'E',0,'X',0,'T',0,'
+ ',0,'F',0,'O',0,'R',0,'M',0,'A',0,'T',0,'@',0,'@',0 }; char* dst = NULL; int num; num =
+ ConvertFromUnicode(CP_UTF8, 0, (WCHAR*) src, 16, &dst, 0, NULL, NULL);
+ printf("ConvertFromUnicode returned %d dst=[%s]\n", num, dst);
+ string_hexdump((BYTE*)dst, num+1);
+ }
+ if (1)
+ {
+ char src[] = "RICH TEXT FORMAT@@@@@@";
+ WCHAR *dst = NULL;
+ int num;
+ num = ConvertToUnicode(CP_UTF8, 0, src, 16, &dst, 0);
+ printf("ConvertToUnicode returned %d dst=%p\n", num, (void*) dst);
+ string_hexdump((BYTE*)dst, num * 2 + 2);
+
+ }
+ */
+
+ return 0;
+}
diff --git a/winpr/libwinpr/crt/unicode.c b/winpr/libwinpr/crt/unicode.c
new file mode 100644
index 0000000..123a488
--- /dev/null
+++ b/winpr/libwinpr/crt/unicode.c
@@ -0,0 +1,657 @@
+/**
+ * WinPR: Windows Portable Runtime
+ * Unicode Conversion (CRT)
+ *
+ * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ * Copyright 2022 Armin Novak <anovak@thincast.com>
+ * Copyright 2022 Thincast Technologies GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <winpr/config.h>
+#include <winpr/assert.h>
+
+#include <errno.h>
+#include <wctype.h>
+
+#include <winpr/crt.h>
+#include <winpr/error.h>
+#include <winpr/print.h>
+
+#ifndef MIN
+#define MIN(a, b) (a) < (b) ? (a) : (b)
+#endif
+
+#ifndef _WIN32
+
+#include "unicode.h"
+
+#include "../log.h"
+#define TAG WINPR_TAG("unicode")
+
+/**
+ * Notes on cross-platform Unicode portability:
+ *
+ * Unicode has many possible Unicode Transformation Format (UTF) encodings,
+ * where some of the most commonly used are UTF-8, UTF-16 and sometimes UTF-32.
+ *
+ * The number in the UTF encoding name (8, 16, 32) refers to the number of bits
+ * per code unit. A code unit is the minimal bit combination that can represent
+ * a unit of encoded text in the given encoding. For instance, UTF-8 encodes
+ * the English alphabet using 8 bits (or one byte) each, just like in ASCII.
+ *
+ * However, the total number of code points (values in the Unicode codespace)
+ * only fits completely within 32 bits. This means that for UTF-8 and UTF-16,
+ * more than one code unit may be required to fully encode a specific value.
+ * UTF-8 and UTF-16 are variable-width encodings, while UTF-32 is fixed-width.
+ *
+ * UTF-8 has the advantage of being backwards compatible with ASCII, and is
+ * one of the most commonly used Unicode encoding.
+ *
+ * UTF-16 is used everywhere in the Windows API. The strategy employed by
+ * Microsoft to provide backwards compatibility in their API was to create
+ * an ANSI and a Unicode version of the same function, ending with A (ANSI)
+ * and W (Wide character, or UTF-16 Unicode). In headers, the original
+ * function name is replaced by a macro that defines to either the ANSI
+ * or Unicode version based on the definition of the _UNICODE macro.
+ *
+ * UTF-32 has the advantage of being fixed width, but wastes a lot of space
+ * for English text (4x more than UTF-8, 2x more than UTF-16).
+ *
+ * In C, wide character strings are often defined with the wchar_t type.
+ * Many functions are provided to deal with those wide character strings,
+ * such as wcslen (strlen equivalent) or wprintf (printf equivalent).
+ *
+ * This may lead to some confusion, since many of these functions exist
+ * on both Windows and Linux, but they are *not* the same!
+ *
+ * This sample hello world is a good example:
+ *
+ * #include <wchar.h>
+ *
+ * wchar_t hello[] = L"Hello, World!\n";
+ *
+ * int main(int argc, char** argv)
+ * {
+ * wprintf(hello);
+ * wprintf(L"sizeof(wchar_t): %d\n", sizeof(wchar_t));
+ * return 0;
+ * }
+ *
+ * There is a reason why the sample prints the size of the wchar_t type:
+ * On Windows, wchar_t is two bytes (UTF-16), while on most other systems
+ * it is 4 bytes (UTF-32). This means that if you write code on Windows,
+ * use L"" to define a string which is meant to be UTF-16 and not UTF-32,
+ * you will have a little surprise when trying to port your code to Linux.
+ *
+ * Since the Windows API uses UTF-16, not UTF-32, WinPR defines the WCHAR
+ * type to always be 2-bytes long and uses it instead of wchar_t. Do not
+ * ever use wchar_t with WinPR unless you know what you are doing.
+ *
+ * As for L"", it is unfortunately unusable in a portable way, unless a
+ * special option is passed to GCC to define wchar_t as being two bytes.
+ * For string constants that must be UTF-16, it is a pain, but they can
+ * be defined in a portable way like this:
+ *
+ * WCHAR hello[] = { 'H','e','l','l','o','\0' };
+ *
+ * Such strings cannot be passed to native functions like wcslen(), which
+ * may expect a different wchar_t size. For this reason, WinPR provides
+ * _wcslen, which expects UTF-16 WCHAR strings on all platforms.
+ *
+ */
+
+/** \deprecated We no longer export this function, see ConvertUtf8ToWChar family of functions for a
+ * replacement
+ *
+ * Conversion to Unicode (UTF-16)
+ * MultiByteToWideChar: http://msdn.microsoft.com/en-us/library/windows/desktop/dd319072/
+ *
+ * cbMultiByte is an input size in bytes (BYTE)
+ * cchWideChar is an output size in wide characters (WCHAR)
+ *
+ * Null-terminated UTF-8 strings:
+ *
+ * cchWideChar *cannot* be assumed to be cbMultiByte since UTF-8 is variable-width!
+ *
+ * Instead, obtain the required cchWideChar output size like this:
+ * cchWideChar = MultiByteToWideChar(CP_UTF8, 0, (LPCSTR) lpMultiByteStr, -1, NULL, 0);
+ *
+ * A value of -1 for cbMultiByte indicates that the input string is null-terminated,
+ * and the null terminator *will* be processed. The size returned by MultiByteToWideChar
+ * will therefore include the null terminator. Equivalent behavior can be obtained by
+ * computing the length in bytes of the input buffer, including the null terminator:
+ *
+ * cbMultiByte = strlen((char*) lpMultiByteStr) + 1;
+ *
+ * An output buffer of the proper size can then be allocated:
+ *
+ * lpWideCharStr = (LPWSTR) malloc(cchWideChar * sizeof(WCHAR));
+ *
+ * Since cchWideChar is an output size in wide characters, the actual buffer size is:
+ * (cchWideChar * sizeof(WCHAR)) or (cchWideChar * 2)
+ *
+ * Finally, perform the conversion:
+ *
+ * cchWideChar = MultiByteToWideChar(CP_UTF8, 0, (LPCSTR) lpMultiByteStr, -1, lpWideCharStr,
+ * cchWideChar);
+ *
+ * The value returned by MultiByteToWideChar corresponds to the number of wide characters written
+ * to the output buffer, and should match the value obtained on the first call to
+ * MultiByteToWideChar.
+ *
+ */
+
+#if !defined(WITH_WINPR_DEPRECATED)
+static
+#endif
+ int
+ MultiByteToWideChar(UINT CodePage, DWORD dwFlags, LPCSTR lpMultiByteStr, int cbMultiByte,
+ LPWSTR lpWideCharStr, int cchWideChar)
+{
+ return int_MultiByteToWideChar(CodePage, dwFlags, lpMultiByteStr, cbMultiByte, lpWideCharStr,
+ cchWideChar);
+}
+
+/** \deprecated We no longer export this function, see ConvertWCharToUtf8 family of functions for a
+ * replacement
+ *
+ * Conversion from Unicode (UTF-16)
+ * WideCharToMultiByte: http://msdn.microsoft.com/en-us/library/windows/desktop/dd374130/
+ *
+ * cchWideChar is an input size in wide characters (WCHAR)
+ * cbMultiByte is an output size in bytes (BYTE)
+ *
+ * Null-terminated UTF-16 strings:
+ *
+ * cbMultiByte *cannot* be assumed to be cchWideChar since UTF-8 is variable-width!
+ *
+ * Instead, obtain the required cbMultiByte output size like this:
+ * cbMultiByte = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR) lpWideCharStr, -1, NULL, 0, NULL, NULL);
+ *
+ * A value of -1 for cbMultiByte indicates that the input string is null-terminated,
+ * and the null terminator *will* be processed. The size returned by WideCharToMultiByte
+ * will therefore include the null terminator. Equivalent behavior can be obtained by
+ * computing the length in bytes of the input buffer, including the null terminator:
+ *
+ * cchWideChar = _wcslen((WCHAR*) lpWideCharStr) + 1;
+ *
+ * An output buffer of the proper size can then be allocated:
+ * lpMultiByteStr = (LPSTR) malloc(cbMultiByte);
+ *
+ * Since cbMultiByte is an output size in bytes, it is the same as the buffer size
+ *
+ * Finally, perform the conversion:
+ *
+ * cbMultiByte = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR) lpWideCharStr, -1, lpMultiByteStr,
+ * cbMultiByte, NULL, NULL);
+ *
+ * The value returned by WideCharToMultiByte corresponds to the number of bytes written
+ * to the output buffer, and should match the value obtained on the first call to
+ * WideCharToMultiByte.
+ *
+ */
+
+#if !defined(WITH_WINPR_DEPRECATED)
+static
+#endif
+ int
+ WideCharToMultiByte(UINT CodePage, DWORD dwFlags, LPCWSTR lpWideCharStr, int cchWideChar,
+ LPSTR lpMultiByteStr, int cbMultiByte, LPCSTR lpDefaultChar,
+ LPBOOL lpUsedDefaultChar)
+{
+ return int_WideCharToMultiByte(CodePage, dwFlags, lpWideCharStr, cchWideChar, lpMultiByteStr,
+ cbMultiByte, lpDefaultChar, lpUsedDefaultChar);
+}
+
+#endif
+
+/**
+ * ConvertToUnicode is a convenience wrapper for MultiByteToWideChar:
+ *
+ * If the lpWideCharStr parameter for the converted string points to NULL
+ * or if the cchWideChar parameter is set to 0 this function will automatically
+ * allocate the required memory which is guaranteed to be null-terminated
+ * after the conversion, even if the source c string isn't.
+ *
+ * If the cbMultiByte parameter is set to -1 the passed lpMultiByteStr must
+ * be null-terminated and the required length for the converted string will be
+ * calculated accordingly.
+ */
+#if defined(WITH_WINPR_DEPRECATED)
+int ConvertToUnicode(UINT CodePage, DWORD dwFlags, LPCSTR lpMultiByteStr, int cbMultiByte,
+ LPWSTR* lpWideCharStr, int cchWideChar)
+{
+ int status = 0;
+ BOOL allocate = FALSE;
+
+ if (!lpMultiByteStr)
+ return 0;
+
+ if (!lpWideCharStr)
+ return 0;
+
+ if (cbMultiByte == -1)
+ {
+ size_t len = strnlen(lpMultiByteStr, INT_MAX);
+ if (len >= INT_MAX)
+ return 0;
+ cbMultiByte = (int)(len + 1);
+ }
+
+ if (cchWideChar == 0)
+ {
+ cchWideChar = MultiByteToWideChar(CodePage, dwFlags, lpMultiByteStr, cbMultiByte, NULL, 0);
+ allocate = TRUE;
+ }
+ else if (!(*lpWideCharStr))
+ allocate = TRUE;
+
+ if (cchWideChar < 1)
+ return 0;
+
+ if (allocate)
+ {
+ *lpWideCharStr = (LPWSTR)calloc(cchWideChar + 1, sizeof(WCHAR));
+
+ if (!(*lpWideCharStr))
+ {
+ // SetLastError(ERROR_INSUFFICIENT_BUFFER);
+ return 0;
+ }
+ }
+
+ status = MultiByteToWideChar(CodePage, dwFlags, lpMultiByteStr, cbMultiByte, *lpWideCharStr,
+ cchWideChar);
+
+ if (status != cchWideChar)
+ {
+ if (allocate)
+ {
+ free(*lpWideCharStr);
+ *lpWideCharStr = NULL;
+ status = 0;
+ }
+ }
+
+ return status;
+}
+#endif
+
+/**
+ * ConvertFromUnicode is a convenience wrapper for WideCharToMultiByte:
+ *
+ * If the lpMultiByteStr parameter for the converted string points to NULL
+ * or if the cbMultiByte parameter is set to 0 this function will automatically
+ * allocate the required memory which is guaranteed to be null-terminated
+ * after the conversion, even if the source unicode string isn't.
+ *
+ * If the cchWideChar parameter is set to -1 the passed lpWideCharStr must
+ * be null-terminated and the required length for the converted string will be
+ * calculated accordingly.
+ */
+#if defined(WITH_WINPR_DEPRECATED)
+int ConvertFromUnicode(UINT CodePage, DWORD dwFlags, LPCWSTR lpWideCharStr, int cchWideChar,
+ LPSTR* lpMultiByteStr, int cbMultiByte, LPCSTR lpDefaultChar,
+ LPBOOL lpUsedDefaultChar)
+{
+ int status = 0;
+ BOOL allocate = FALSE;
+
+ if (!lpWideCharStr)
+ return 0;
+
+ if (!lpMultiByteStr)
+ return 0;
+
+ if (cchWideChar == -1)
+ cchWideChar = (int)(_wcslen(lpWideCharStr) + 1);
+
+ if (cbMultiByte == 0)
+ {
+ cbMultiByte =
+ WideCharToMultiByte(CodePage, dwFlags, lpWideCharStr, cchWideChar, NULL, 0, NULL, NULL);
+ allocate = TRUE;
+ }
+ else if (!(*lpMultiByteStr))
+ allocate = TRUE;
+
+ if (cbMultiByte < 1)
+ return 0;
+
+ if (allocate)
+ {
+ *lpMultiByteStr = (LPSTR)calloc(1, cbMultiByte + 1);
+
+ if (!(*lpMultiByteStr))
+ {
+ // SetLastError(ERROR_INSUFFICIENT_BUFFER);
+ return 0;
+ }
+ }
+
+ status = WideCharToMultiByte(CodePage, dwFlags, lpWideCharStr, cchWideChar, *lpMultiByteStr,
+ cbMultiByte, lpDefaultChar, lpUsedDefaultChar);
+
+ if ((status != cbMultiByte) && allocate)
+ {
+ status = 0;
+ }
+
+ if ((status <= 0) && allocate)
+ {
+ free(*lpMultiByteStr);
+ *lpMultiByteStr = NULL;
+ }
+
+ return status;
+}
+#endif
+
+/**
+ * Swap Unicode byte order (UTF16LE <-> UTF16BE)
+ */
+
+const WCHAR* ByteSwapUnicode(WCHAR* wstr, size_t length)
+{
+ WINPR_ASSERT(wstr || (length == 0));
+
+ for (size_t x = 0; x < length; x++)
+ wstr[x] = _byteswap_ushort(wstr[x]);
+ return wstr;
+}
+
+SSIZE_T ConvertWCharToUtf8(const WCHAR* wstr, char* str, size_t len)
+{
+ if (!wstr)
+ {
+ if (str && len)
+ str[0] = 0;
+ return 0;
+ }
+
+ const size_t wlen = _wcslen(wstr);
+ return ConvertWCharNToUtf8(wstr, wlen + 1, str, len);
+}
+
+SSIZE_T ConvertWCharNToUtf8(const WCHAR* wstr, size_t wlen, char* str, size_t len)
+{
+ BOOL isNullTerminated = FALSE;
+ if (wlen == 0)
+ return 0;
+
+ WINPR_ASSERT(wstr);
+ size_t iwlen = _wcsnlen(wstr, wlen);
+
+ if (wlen > INT32_MAX)
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return -1;
+ }
+
+ if (iwlen < wlen)
+ {
+ isNullTerminated = TRUE;
+ iwlen++;
+ }
+ const int rc = WideCharToMultiByte(CP_UTF8, 0, wstr, (int)iwlen, str, (int)MIN(INT32_MAX, len),
+ NULL, NULL);
+ if ((rc <= 0) || ((len > 0) && ((size_t)rc > len)))
+ return -1;
+ else if (!isNullTerminated)
+ {
+ if (str && ((size_t)rc < len))
+ str[rc] = '\0';
+ return rc;
+ }
+ else if ((size_t)rc == len)
+ {
+ if (str && (str[rc - 1] != '\0'))
+ return rc;
+ }
+ return rc - 1;
+}
+
+SSIZE_T ConvertMszWCharNToUtf8(const WCHAR* wstr, size_t wlen, char* str, size_t len)
+{
+ if (wlen == 0)
+ return 0;
+
+ WINPR_ASSERT(wstr);
+
+ if (wlen > INT32_MAX)
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return -1;
+ }
+
+ const int iwlen = MIN(INT32_MAX, len);
+ const int rc = WideCharToMultiByte(CP_UTF8, 0, wstr, (int)wlen, str, (int)iwlen, NULL, NULL);
+ if ((rc <= 0) || ((len > 0) && (rc > iwlen)))
+ return -1;
+
+ return rc;
+}
+
+SSIZE_T ConvertUtf8ToWChar(const char* str, WCHAR* wstr, size_t wlen)
+{
+ if (!str)
+ {
+ if (wstr && wlen)
+ wstr[0] = 0;
+ return 0;
+ }
+
+ const size_t len = strlen(str);
+ return ConvertUtf8NToWChar(str, len + 1, wstr, wlen);
+}
+
+SSIZE_T ConvertUtf8NToWChar(const char* str, size_t len, WCHAR* wstr, size_t wlen)
+{
+ size_t ilen = strnlen(str, len);
+ BOOL isNullTerminated = FALSE;
+ if (len == 0)
+ return 0;
+
+ WINPR_ASSERT(str);
+
+ if (len > INT32_MAX)
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return -1;
+ }
+ if (ilen < len)
+ {
+ isNullTerminated = TRUE;
+ ilen++;
+ }
+
+ const int iwlen = MIN(INT32_MAX, wlen);
+ const int rc = MultiByteToWideChar(CP_UTF8, 0, str, (int)ilen, wstr, (int)iwlen);
+ if ((rc <= 0) || ((wlen > 0) && (rc > iwlen)))
+ return -1;
+ if (!isNullTerminated)
+ {
+ if (wstr && (rc < iwlen))
+ wstr[rc] = '\0';
+ return rc;
+ }
+ else if (rc == iwlen)
+ {
+ if (wstr && (wstr[rc - 1] != '\0'))
+ return rc;
+ }
+ return rc - 1;
+}
+
+SSIZE_T ConvertMszUtf8NToWChar(const char* str, size_t len, WCHAR* wstr, size_t wlen)
+{
+ if (len == 0)
+ return 0;
+
+ WINPR_ASSERT(str);
+
+ if (len > INT32_MAX)
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return -1;
+ }
+
+ const int iwlen = MIN(INT32_MAX, wlen);
+ const int rc = MultiByteToWideChar(CP_UTF8, 0, str, (int)len, wstr, (int)iwlen);
+ if ((rc <= 0) || ((wlen > 0) && (rc > iwlen)))
+ return -1;
+
+ return rc;
+}
+
+char* ConvertWCharToUtf8Alloc(const WCHAR* wstr, size_t* pUtfCharLength)
+{
+ char* tmp = NULL;
+ const SSIZE_T rc = ConvertWCharToUtf8(wstr, NULL, 0);
+ if (pUtfCharLength)
+ *pUtfCharLength = 0;
+ if (rc < 0)
+ return NULL;
+ tmp = calloc((size_t)rc + 1ull, sizeof(char));
+ if (!tmp)
+ return NULL;
+ const SSIZE_T rc2 = ConvertWCharToUtf8(wstr, tmp, (size_t)rc + 1ull);
+ if (rc2 < 0)
+ {
+ free(tmp);
+ return NULL;
+ }
+ WINPR_ASSERT(rc == rc2);
+ if (pUtfCharLength)
+ *pUtfCharLength = (size_t)rc2;
+ return tmp;
+}
+
+char* ConvertWCharNToUtf8Alloc(const WCHAR* wstr, size_t wlen, size_t* pUtfCharLength)
+{
+ char* tmp = NULL;
+ const SSIZE_T rc = ConvertWCharNToUtf8(wstr, wlen, NULL, 0);
+
+ if (pUtfCharLength)
+ *pUtfCharLength = 0;
+ if (rc < 0)
+ return NULL;
+ tmp = calloc((size_t)rc + 1ull, sizeof(char));
+ if (!tmp)
+ return NULL;
+ const SSIZE_T rc2 = ConvertWCharNToUtf8(wstr, wlen, tmp, (size_t)rc + 1ull);
+ if (rc2 < 0)
+ {
+ free(tmp);
+ return NULL;
+ }
+ WINPR_ASSERT(rc == rc2);
+ if (pUtfCharLength)
+ *pUtfCharLength = (size_t)rc2;
+ return tmp;
+}
+
+char* ConvertMszWCharNToUtf8Alloc(const WCHAR* wstr, size_t wlen, size_t* pUtfCharLength)
+{
+ char* tmp = NULL;
+ const SSIZE_T rc = ConvertMszWCharNToUtf8(wstr, wlen, NULL, 0);
+
+ if (pUtfCharLength)
+ *pUtfCharLength = 0;
+ if (rc < 0)
+ return NULL;
+ tmp = calloc((size_t)rc + 1ull, sizeof(char));
+ if (!tmp)
+ return NULL;
+ const SSIZE_T rc2 = ConvertMszWCharNToUtf8(wstr, wlen, tmp, (size_t)rc + 1ull);
+ if (rc2 < 0)
+ {
+ free(tmp);
+ return NULL;
+ }
+ WINPR_ASSERT(rc == rc2);
+ if (pUtfCharLength)
+ *pUtfCharLength = (size_t)rc2;
+ return tmp;
+}
+
+WCHAR* ConvertUtf8ToWCharAlloc(const char* str, size_t* pSize)
+{
+ WCHAR* tmp = NULL;
+ const SSIZE_T rc = ConvertUtf8ToWChar(str, NULL, 0);
+ if (pSize)
+ *pSize = 0;
+ if (rc < 0)
+ return NULL;
+ tmp = calloc((size_t)rc + 1ull, sizeof(WCHAR));
+ if (!tmp)
+ return NULL;
+ const SSIZE_T rc2 = ConvertUtf8ToWChar(str, tmp, (size_t)rc + 1ull);
+ if (rc2 < 0)
+ {
+ free(tmp);
+ return NULL;
+ }
+ WINPR_ASSERT(rc == rc2);
+ if (pSize)
+ *pSize = (size_t)rc2;
+ return tmp;
+}
+
+WCHAR* ConvertUtf8NToWCharAlloc(const char* str, size_t len, size_t* pSize)
+{
+ WCHAR* tmp = NULL;
+ const SSIZE_T rc = ConvertUtf8NToWChar(str, len, NULL, 0);
+ if (pSize)
+ *pSize = 0;
+ if (rc < 0)
+ return NULL;
+ tmp = calloc((size_t)rc + 1ull, sizeof(WCHAR));
+ if (!tmp)
+ return NULL;
+ const SSIZE_T rc2 = ConvertUtf8NToWChar(str, len, tmp, (size_t)rc + 1ull);
+ if (rc2 < 0)
+ {
+ free(tmp);
+ return NULL;
+ }
+ WINPR_ASSERT(rc == rc2);
+ if (pSize)
+ *pSize = (size_t)rc2;
+ return tmp;
+}
+
+WCHAR* ConvertMszUtf8NToWCharAlloc(const char* str, size_t len, size_t* pSize)
+{
+ WCHAR* tmp = NULL;
+ const SSIZE_T rc = ConvertMszUtf8NToWChar(str, len, NULL, 0);
+ if (pSize)
+ *pSize = 0;
+ if (rc < 0)
+ return NULL;
+ tmp = calloc((size_t)rc + 1ull, sizeof(WCHAR));
+ if (!tmp)
+ return NULL;
+ const SSIZE_T rc2 = ConvertMszUtf8NToWChar(str, len, tmp, (size_t)rc + 1ull);
+ if (rc2 < 0)
+ {
+ free(tmp);
+ return NULL;
+ }
+ WINPR_ASSERT(rc == rc2);
+ if (pSize)
+ *pSize = (size_t)rc2;
+ return tmp;
+}
diff --git a/winpr/libwinpr/crt/unicode.h b/winpr/libwinpr/crt/unicode.h
new file mode 100644
index 0000000..9305f2c
--- /dev/null
+++ b/winpr/libwinpr/crt/unicode.h
@@ -0,0 +1,32 @@
+/**
+ * WinPR: Windows Portable Runtime
+ * Unicode Conversion (CRT)
+ *
+ * Copyright 2022 Armin Novak <anovak@thincast.com>
+ * Copyright 2022 Thincast Technologies GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef WINPR_CRT_UNICODE_INTERNAL
+#define WINPR_CRT_UNICODE_INTERNAL
+
+#include <winpr/wtypes.h>
+
+int int_MultiByteToWideChar(UINT CodePage, DWORD dwFlags, LPCSTR lpMultiByteStr, int cbMultiByte,
+ LPWSTR lpWideCharStr, int cchWideChar);
+
+int int_WideCharToMultiByte(UINT CodePage, DWORD dwFlags, LPCWSTR lpWideCharStr, int cchWideChar,
+ LPSTR lpMultiByteStr, int cbMultiByte, LPCSTR lpDefaultChar,
+ LPBOOL lpUsedDefaultChar);
+#endif
diff --git a/winpr/libwinpr/crt/unicode_android.c b/winpr/libwinpr/crt/unicode_android.c
new file mode 100644
index 0000000..2e9bac5
--- /dev/null
+++ b/winpr/libwinpr/crt/unicode_android.c
@@ -0,0 +1,183 @@
+/**
+ * WinPR: Windows Portable Runtime
+ * Unicode Conversion (CRT)
+ *
+ * Copyright 2022 Armin Novak <anovak@thincast.com>
+ * Copyright 2022 Thincast Technologies GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <winpr/config.h>
+#include <winpr/assert.h>
+#include <winpr/string.h>
+
+#include "../utils/android.h"
+
+#ifndef MIN
+#define MIN(a, b) (a) < (b) ? (a) : (b)
+#endif
+
+#include "../log.h"
+#define TAG WINPR_TAG("unicode")
+
+static int convert_int(JNIEnv* env, const void* data, size_t size, void* buffer, size_t buffersize,
+ BOOL toUTF16)
+{
+ WINPR_ASSERT(env);
+ WINPR_ASSERT(data || (size == 0));
+ WINPR_ASSERT(buffer || (buffersize == 0));
+
+ jstring utf8 = (*env)->NewStringUTF(env, "UTF-8");
+ jstring utf16 = (*env)->NewStringUTF(env, "UTF-16LE");
+ jclass stringClass = (*env)->FindClass(env, "java/lang/String");
+
+ if (!utf8 || !utf16 || !stringClass)
+ {
+ WLog_ERR(TAG, "utf8-%p, utf16=%p, stringClass=%p", utf8, utf16, stringClass);
+ return -1;
+ }
+
+ jmethodID constructorID =
+ (*env)->GetMethodID(env, stringClass, "<init>", "([BLjava/lang/String;)V");
+ jmethodID getBytesID =
+ (*env)->GetMethodID(env, stringClass, "getBytes", "(Ljava/lang/String;)[B");
+ if (!constructorID || !getBytesID)
+ {
+ WLog_ERR(TAG, "constructorID=%p, getBytesID=%p", constructorID, getBytesID);
+ return -2;
+ }
+
+ jbyteArray ret = (*env)->NewByteArray(env, size);
+ if (!ret)
+ {
+ WLog_ERR(TAG, "NewByteArray(%" PRIuz ") failed", size);
+ return -3;
+ }
+
+ (*env)->SetByteArrayRegion(env, ret, 0, size, data);
+
+ jobject obj = (*env)->NewObject(env, stringClass, constructorID, ret, toUTF16 ? utf8 : utf16);
+ if (!obj)
+ {
+ WLog_ERR(TAG, "NewObject(String, byteArray, UTF-%d) failed", toUTF16 ? 16 : 8);
+ return -4;
+ }
+
+ jbyteArray res = (*env)->CallObjectMethod(env, obj, getBytesID, toUTF16 ? utf16 : utf8);
+ if (!res)
+ {
+ WLog_ERR(TAG, "CallObjectMethod(String, getBytes, UTF-%d) failed", toUTF16 ? 16 : 8);
+ return -4;
+ }
+
+ jsize rlen = (*env)->GetArrayLength(env, res);
+ if (buffersize > 0)
+ {
+ if (rlen > buffersize)
+ {
+ SetLastError(ERROR_INSUFFICIENT_BUFFER);
+ return 0;
+ }
+ rlen = MIN(rlen, buffersize);
+ (*env)->GetByteArrayRegion(env, res, 0, rlen, buffer);
+ }
+
+ if (toUTF16)
+ rlen /= sizeof(WCHAR);
+
+ return rlen;
+}
+
+static int convert(const void* data, size_t size, void* buffer, size_t buffersize, BOOL toUTF16)
+{
+ int rc;
+ JNIEnv* env = NULL;
+ jboolean attached = winpr_jni_attach_thread(&env);
+ rc = convert_int(env, data, size, buffer, buffersize, toUTF16);
+ if (attached)
+ winpr_jni_detach_thread();
+ return rc;
+}
+
+int int_MultiByteToWideChar(UINT CodePage, DWORD dwFlags, LPCSTR lpMultiByteStr, int cbMultiByte,
+ LPWSTR lpWideCharStr, int cchWideChar)
+{
+ size_t cbCharLen = (size_t)cbMultiByte;
+
+ WINPR_UNUSED(dwFlags);
+
+ /* If cbMultiByte is 0, the function fails */
+ if ((cbMultiByte == 0) || (cbMultiByte < -1))
+ return 0;
+
+ if (cchWideChar < 0)
+ return -1;
+
+ if (cbMultiByte < 0)
+ {
+ const size_t len = strlen(lpMultiByteStr);
+ if (len >= INT32_MAX)
+ return 0;
+ cbCharLen = (int)len + 1;
+ }
+ else
+ cbCharLen = cbMultiByte;
+
+ WINPR_ASSERT(lpMultiByteStr);
+ switch (CodePage)
+ {
+ case CP_ACP:
+ case CP_UTF8:
+ break;
+
+ default:
+ WLog_ERR(TAG, "Unsupported encoding %u", CodePage);
+ return 0;
+ }
+
+ return convert(lpMultiByteStr, cbCharLen, lpWideCharStr, cchWideChar * sizeof(WCHAR), TRUE);
+}
+
+int int_WideCharToMultiByte(UINT CodePage, DWORD dwFlags, LPCWSTR lpWideCharStr, int cchWideChar,
+ LPSTR lpMultiByteStr, int cbMultiByte, LPCSTR lpDefaultChar,
+ LPBOOL lpUsedDefaultChar)
+{
+ size_t cbCharLen = (size_t)cchWideChar;
+
+ WINPR_UNUSED(dwFlags);
+ /* If cchWideChar is 0, the function fails */
+ if ((cchWideChar == 0) || (cchWideChar < -1))
+ return 0;
+
+ if (cbMultiByte < 0)
+ return -1;
+
+ WINPR_ASSERT(lpWideCharStr);
+ /* If cchWideChar is -1, the string is null-terminated */
+ if (cchWideChar == -1)
+ {
+ const size_t len = _wcslen(lpWideCharStr);
+ if (len >= INT32_MAX)
+ return 0;
+ cbCharLen = (int)len + 1;
+ }
+ else
+ cbCharLen = cchWideChar;
+
+ /*
+ * if cbMultiByte is 0, the function returns the required buffer size
+ * in bytes for lpMultiByteStr and makes no use of the output parameter itself.
+ */
+ return convert(lpWideCharStr, cbCharLen * sizeof(WCHAR), lpMultiByteStr, cbMultiByte, FALSE);
+}
diff --git a/winpr/libwinpr/crt/unicode_apple.m b/winpr/libwinpr/crt/unicode_apple.m
new file mode 100644
index 0000000..159252b
--- /dev/null
+++ b/winpr/libwinpr/crt/unicode_apple.m
@@ -0,0 +1,146 @@
+/**
+ * WinPR: Windows Portable Runtime
+ * Unicode Conversion (CRT)
+ *
+ * Copyright 2022 Armin Novak <anovak@thincast.com>
+ * Copyright 2022 Thincast Technologies GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#import <Foundation/Foundation.h>
+
+#include <winpr/config.h>
+#include <winpr/assert.h>
+
+#include <errno.h>
+#include <wctype.h>
+
+#include <winpr/crt.h>
+#include <winpr/error.h>
+#include <winpr/print.h>
+
+#ifndef MIN
+#define MIN(a, b) (a) < (b) ? (a) : (b)
+#endif
+
+#include "../log.h"
+#define TAG WINPR_TAG("unicode")
+
+int int_MultiByteToWideChar(UINT CodePage, DWORD dwFlags, LPCSTR lpMultiByteStr, int cbMultiByte,
+ LPWSTR lpWideCharStr, int cchWideChar)
+{
+ const BOOL isNullTerminated = cbMultiByte < 0;
+
+ /* If cbMultiByte is 0, the function fails */
+ if ((cbMultiByte == 0) || (cbMultiByte < -1))
+ return 0;
+
+ /* If cbMultiByte is -1, the string is null-terminated */
+ if (isNullTerminated)
+ {
+ size_t len = strnlen(lpMultiByteStr, INT32_MAX);
+ if (len >= INT32_MAX)
+ return 0;
+ cbMultiByte = (int)len + 1;
+ }
+
+ NSString *utf = [[NSString alloc] initWithBytes:lpMultiByteStr
+ length:cbMultiByte
+ encoding:NSUTF8StringEncoding];
+ if (!utf)
+ {
+ WLog_WARN(TAG, "[NSString alloc] NSUTF8StringEncoding failed [%d] '%s'", cbMultiByte,
+ lpMultiByteStr);
+ return -1;
+ }
+
+ const WCHAR *utf16 =
+ (const WCHAR *)[utf cStringUsingEncoding:NSUTF16LittleEndianStringEncoding];
+ const size_t utf16ByteLen = [utf lengthOfBytesUsingEncoding:NSUTF16LittleEndianStringEncoding];
+ const size_t utf16CharLen = utf16ByteLen / sizeof(WCHAR);
+ if (!utf16)
+ {
+ WLog_WARN(TAG, "[utf cStringUsingEncoding:NSUTF16LittleEndianStringEncoding] failed");
+ return -1;
+ }
+
+ if (cchWideChar == 0)
+ return utf16CharLen;
+ else if (cchWideChar < utf16CharLen)
+ {
+ SetLastError(ERROR_INSUFFICIENT_BUFFER);
+ return 0;
+ }
+ else
+ {
+ const size_t mlen = MIN((size_t)utf16CharLen, cchWideChar);
+ const size_t len = _wcsnlen(utf16, mlen);
+ memcpy(lpWideCharStr, utf16, len * sizeof(WCHAR));
+ if ((len < (size_t)cchWideChar) && (len > 0) && (lpWideCharStr[len - 1] != '\0'))
+ lpWideCharStr[len] = '\0';
+ return utf16CharLen;
+ }
+}
+
+int int_WideCharToMultiByte(UINT CodePage, DWORD dwFlags, LPCWSTR lpWideCharStr, int cchWideChar,
+ LPSTR lpMultiByteStr, int cbMultiByte, LPCSTR lpDefaultChar,
+ LPBOOL lpUsedDefaultChar)
+{
+ const BOOL isNullTerminated = cchWideChar < 0;
+
+ /* If cchWideChar is 0, the function fails */
+ if ((cchWideChar == 0) || (cchWideChar < -1))
+ return 0;
+
+ /* If cchWideChar is -1, the string is null-terminated */
+ if (isNullTerminated)
+ {
+ size_t len = _wcslen(lpWideCharStr);
+ if (len >= INT32_MAX)
+ return 0;
+ cchWideChar = (int)len + 1;
+ }
+
+ NSString *utf = [[NSString alloc] initWithCharacters:lpWideCharStr length:cchWideChar];
+ if (!utf)
+ {
+ WLog_WARN(TAG, "[NSString alloc] initWithCharacters failed [%d] 'XXX'", cchWideChar);
+ return -1;
+ }
+
+ const char *utf8 = [utf cStringUsingEncoding:NSUTF8StringEncoding];
+ const size_t utf8Len = [utf lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
+ if (!utf8)
+ {
+ WLog_WARN(TAG, "[utf cStringUsingEncoding:NSUTF8StringEncoding] failed");
+ return -1;
+ }
+
+ if (cbMultiByte == 0)
+ return utf8Len;
+ else if (cbMultiByte < utf8Len)
+ {
+ SetLastError(ERROR_INSUFFICIENT_BUFFER);
+ return 0;
+ }
+ else
+ {
+ const size_t mlen = MIN((size_t)cbMultiByte, utf8Len);
+ const size_t len = strnlen(utf8, mlen);
+ memcpy(lpMultiByteStr, utf8, len * sizeof(char));
+ if ((len < (size_t)cbMultiByte) && (len > 0) && (lpMultiByteStr[len - 1] != '\0'))
+ lpMultiByteStr[len] = '\0';
+ return utf8Len;
+ }
+}
diff --git a/winpr/libwinpr/crt/unicode_builtin.c b/winpr/libwinpr/crt/unicode_builtin.c
new file mode 100644
index 0000000..e56ddd7
--- /dev/null
+++ b/winpr/libwinpr/crt/unicode_builtin.c
@@ -0,0 +1,695 @@
+/*
+ * Copyright 2001-2004 Unicode, Inc.
+ *
+ * Disclaimer
+ *
+ * This source code is provided as is by Unicode, Inc. No claims are
+ * made as to fitness for any particular purpose. No warranties of any
+ * kind are expressed or implied. The recipient agrees to determine
+ * applicability of information provided. If this file has been
+ * purchased on magnetic or optical media from Unicode, Inc., the
+ * sole remedy for any claim will be exchange of defective media
+ * within 90 days of receipt.
+ *
+ * Limitations on Rights to Redistribute This Code
+ *
+ * Unicode, Inc. hereby grants the right to freely use the information
+ * supplied in this file in the creation of products supporting the
+ * Unicode Standard, and to make copies of this file in any form
+ * for internal or external distribution as long as this notice
+ * remains attached.
+ */
+
+/* ---------------------------------------------------------------------
+
+Conversions between UTF32, UTF-16, and UTF-8. Source code file.
+Author: Mark E. Davis, 1994.
+Rev History: Rick McGowan, fixes & updates May 2001.
+Sept 2001: fixed const & error conditions per
+mods suggested by S. Parent & A. Lillich.
+June 2002: Tim Dodd added detection and handling of incomplete
+source sequences, enhanced error detection, added casts
+to eliminate compiler warnings.
+July 2003: slight mods to back out aggressive FFFE detection.
+Jan 2004: updated switches in from-UTF8 conversions.
+Oct 2004: updated to use UNI_MAX_LEGAL_UTF32 in UTF-32 conversions.
+
+See the header file "utf.h" for complete documentation.
+
+------------------------------------------------------------------------ */
+
+#include <winpr/wtypes.h>
+#include <winpr/string.h>
+#include <winpr/assert.h>
+
+#include "unicode.h"
+
+#include "../log.h"
+#define TAG WINPR_TAG("unicode")
+
+/*
+ * Character Types:
+ *
+ * UTF8: uint8_t 8 bits
+ * UTF16: uint16_t 16 bits
+ * UTF32: uint32_t 32 bits
+ */
+
+/* Some fundamental constants */
+#define UNI_REPLACEMENT_CHAR (uint32_t)0x0000FFFD
+#define UNI_MAX_BMP (uint32_t)0x0000FFFF
+#define UNI_MAX_UTF16 (uint32_t)0x0010FFFF
+#define UNI_MAX_UTF32 (uint32_t)0x7FFFFFFF
+#define UNI_MAX_LEGAL_UTF32 (uint32_t)0x0010FFFF
+
+typedef enum
+{
+ conversionOK, /* conversion successful */
+ sourceExhausted, /* partial character in source, but hit end */
+ targetExhausted, /* insuff. room in target for conversion */
+ sourceIllegal /* source sequence is illegal/malformed */
+} ConversionResult;
+
+typedef enum
+{
+ strictConversion = 0,
+ lenientConversion
+} ConversionFlags;
+
+static const int halfShift = 10; /* used for shifting by 10 bits */
+
+static const uint32_t halfBase = 0x0010000UL;
+static const uint32_t halfMask = 0x3FFUL;
+
+#define UNI_SUR_HIGH_START (uint32_t)0xD800
+#define UNI_SUR_HIGH_END (uint32_t)0xDBFF
+#define UNI_SUR_LOW_START (uint32_t)0xDC00
+#define UNI_SUR_LOW_END (uint32_t)0xDFFF
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Index into the table below with the first byte of a UTF-8 sequence to
+ * get the number of trailing bytes that are supposed to follow it.
+ * Note that *legal* UTF-8 values can't have 4 or 5-bytes. The table is
+ * left as-is for anyone who may want to do such conversion, which was
+ * allowed in earlier algorithms.
+ */
+static const char trailingBytesForUTF8[256] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5
+};
+
+/*
+ * Magic values subtracted from a buffer value during UTF8 conversion.
+ * This table contains as many values as there might be trailing bytes
+ * in a UTF-8 sequence.
+ */
+static const uint32_t offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL,
+ 0x03C82080UL, 0xFA082080UL, 0x82082080UL };
+
+/*
+ * Once the bits are split out into bytes of UTF-8, this is a mask OR-ed
+ * into the first byte, depending on how many bytes follow. There are
+ * as many entries in this table as there are UTF-8 sequence types.
+ * (I.e., one byte sequence, two byte... etc.). Remember that sequencs
+ * for *legal* UTF-8 will be 4 or fewer bytes total.
+ */
+static const uint8_t firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
+
+/* --------------------------------------------------------------------- */
+
+/* The interface converts a whole buffer to avoid function-call overhead.
+ * Constants have been gathered. Loops & conditionals have been removed as
+ * much as possible for efficiency, in favor of drop-through switches.
+ * (See "Note A" at the bottom of the file for equivalent code.)
+ * If your compiler supports it, the "isLegalUTF8" call can be turned
+ * into an inline function.
+ */
+
+/* --------------------------------------------------------------------- */
+
+static ConversionResult winpr_ConvertUTF16toUTF8_Internal(const uint16_t** sourceStart,
+ const uint16_t* sourceEnd,
+ uint8_t** targetStart, uint8_t* targetEnd,
+ ConversionFlags flags)
+{
+ bool computeLength = (!targetEnd) ? true : false;
+ const uint16_t* source = *sourceStart;
+ uint8_t* target = *targetStart;
+ ConversionResult result = conversionOK;
+
+ while (source < sourceEnd)
+ {
+ uint32_t ch = 0;
+ unsigned short bytesToWrite = 0;
+ const uint32_t byteMask = 0xBF;
+ const uint32_t byteMark = 0x80;
+ const uint16_t* oldSource =
+ source; /* In case we have to back up because of target overflow. */
+
+ ch = *source++;
+
+ /* If we have a surrogate pair, convert to UTF32 first. */
+ if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END)
+ {
+ /* If the 16 bits following the high surrogate are in the source buffer... */
+ if (source < sourceEnd)
+ {
+ uint32_t ch2 = *source;
+
+ /* If it's a low surrogate, convert to UTF32. */
+ if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END)
+ {
+ ch = ((ch - UNI_SUR_HIGH_START) << halfShift) + (ch2 - UNI_SUR_LOW_START) +
+ halfBase;
+ ++source;
+ }
+ else if (flags == strictConversion)
+ {
+ /* it's an unpaired high surrogate */
+ --source; /* return to the illegal value itself */
+ result = sourceIllegal;
+ break;
+ }
+ }
+ else
+ {
+ /* We don't have the 16 bits following the high surrogate. */
+ --source; /* return to the high surrogate */
+ result = sourceExhausted;
+ break;
+ }
+ }
+ else if (flags == strictConversion)
+ {
+ /* UTF-16 surrogate values are illegal in UTF-32 */
+ if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END)
+ {
+ --source; /* return to the illegal value itself */
+ result = sourceIllegal;
+ break;
+ }
+ }
+
+ /* Figure out how many bytes the result will require */
+ if (ch < (uint32_t)0x80)
+ {
+ bytesToWrite = 1;
+ }
+ else if (ch < (uint32_t)0x800)
+ {
+ bytesToWrite = 2;
+ }
+ else if (ch < (uint32_t)0x10000)
+ {
+ bytesToWrite = 3;
+ }
+ else if (ch < (uint32_t)0x110000)
+ {
+ bytesToWrite = 4;
+ }
+ else
+ {
+ bytesToWrite = 3;
+ ch = UNI_REPLACEMENT_CHAR;
+ }
+
+ target += bytesToWrite;
+
+ if ((target > targetEnd) && (!computeLength))
+ {
+ source = oldSource; /* Back up source pointer! */
+ target -= bytesToWrite;
+ result = targetExhausted;
+ break;
+ }
+
+ if (!computeLength)
+ {
+ switch (bytesToWrite)
+ {
+ /* note: everything falls through. */
+ case 4:
+ *--target = (uint8_t)((ch | byteMark) & byteMask);
+ ch >>= 6;
+ /* fallthrough */
+ WINPR_FALLTHROUGH
+ case 3:
+ *--target = (uint8_t)((ch | byteMark) & byteMask);
+ ch >>= 6;
+ /* fallthrough */
+ WINPR_FALLTHROUGH
+
+ case 2:
+ *--target = (uint8_t)((ch | byteMark) & byteMask);
+ ch >>= 6;
+ /* fallthrough */
+ WINPR_FALLTHROUGH
+
+ case 1:
+ *--target = (uint8_t)(ch | firstByteMark[bytesToWrite]);
+ }
+ }
+ else
+ {
+ switch (bytesToWrite)
+ {
+ /* note: everything falls through. */
+ case 4:
+ --target;
+ /* fallthrough */
+ WINPR_FALLTHROUGH
+
+ case 3:
+ --target;
+ /* fallthrough */
+ WINPR_FALLTHROUGH
+
+ case 2:
+ --target;
+ /* fallthrough */
+ WINPR_FALLTHROUGH
+
+ case 1:
+ --target;
+ }
+ }
+
+ target += bytesToWrite;
+ }
+
+ *sourceStart = source;
+ *targetStart = target;
+ return result;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Utility routine to tell whether a sequence of bytes is legal UTF-8.
+ * This must be called with the length pre-determined by the first byte.
+ * If not calling this from ConvertUTF8to*, then the length can be set by:
+ * length = trailingBytesForUTF8[*source]+1;
+ * and the sequence is illegal right away if there aren't that many bytes
+ * available.
+ * If presented with a length > 4, this returns false. The Unicode
+ * definition of UTF-8 goes up to 4-byte sequences.
+ */
+
+static bool isLegalUTF8(const uint8_t* source, int length)
+{
+ uint8_t a = 0;
+ const uint8_t* srcptr = source + length;
+
+ switch (length)
+ {
+ default:
+ return false;
+
+ /* Everything else falls through when "true"... */
+ case 4:
+ if ((a = (*--srcptr)) < 0x80 || a > 0xBF)
+ return false;
+ /* fallthrough */
+ WINPR_FALLTHROUGH
+
+ case 3:
+ if ((a = (*--srcptr)) < 0x80 || a > 0xBF)
+ return false;
+ /* fallthrough */
+ WINPR_FALLTHROUGH
+
+ case 2:
+ if ((a = (*--srcptr)) > 0xBF)
+ return false;
+
+ switch (*source)
+ {
+ /* no fall-through in this inner switch */
+ case 0xE0:
+ if (a < 0xA0)
+ return false;
+
+ break;
+
+ case 0xED:
+ if (a > 0x9F)
+ return false;
+
+ break;
+
+ case 0xF0:
+ if (a < 0x90)
+ return false;
+
+ break;
+
+ case 0xF4:
+ if (a > 0x8F)
+ return false;
+
+ break;
+
+ default:
+ if (a < 0x80)
+ return false;
+ break;
+ }
+ /* fallthrough */
+ WINPR_FALLTHROUGH
+
+ case 1:
+ if (*source >= 0x80 && *source < 0xC2)
+ return false;
+ }
+
+ if (*source > 0xF4)
+ return false;
+
+ return true;
+}
+
+/* --------------------------------------------------------------------- */
+
+static ConversionResult winpr_ConvertUTF8toUTF16_Internal(const uint8_t** sourceStart,
+ const uint8_t* sourceEnd,
+ uint16_t** targetStart,
+ uint16_t* targetEnd,
+ ConversionFlags flags)
+{
+ bool computeLength = (!targetEnd) ? true : false;
+ ConversionResult result = conversionOK;
+ const uint8_t* source = *sourceStart;
+ uint16_t* target = *targetStart;
+
+ while (source < sourceEnd)
+ {
+ uint32_t ch = 0;
+ unsigned short extraBytesToRead = trailingBytesForUTF8[*source];
+
+ if ((source + extraBytesToRead) >= sourceEnd)
+ {
+ result = sourceExhausted;
+ break;
+ }
+
+ /* Do this check whether lenient or strict */
+ if (!isLegalUTF8(source, extraBytesToRead + 1))
+ {
+ result = sourceIllegal;
+ break;
+ }
+
+ /*
+ * The cases all fall through. See "Note A" below.
+ */
+ switch (extraBytesToRead)
+ {
+ case 5:
+ ch += *source++;
+ ch <<= 6; /* remember, illegal UTF-8 */
+ /* fallthrough */
+ WINPR_FALLTHROUGH
+
+ case 4:
+ ch += *source++;
+ ch <<= 6; /* remember, illegal UTF-8 */
+ /* fallthrough */
+ WINPR_FALLTHROUGH
+
+ case 3:
+ ch += *source++;
+ ch <<= 6;
+ /* fallthrough */
+ WINPR_FALLTHROUGH
+
+ case 2:
+ ch += *source++;
+ ch <<= 6;
+ /* fallthrough */
+ WINPR_FALLTHROUGH
+
+ case 1:
+ ch += *source++;
+ ch <<= 6;
+ /* fallthrough */
+ WINPR_FALLTHROUGH
+
+ case 0:
+ ch += *source++;
+ }
+
+ ch -= offsetsFromUTF8[extraBytesToRead];
+
+ if ((target >= targetEnd) && (!computeLength))
+ {
+ source -= (extraBytesToRead + 1); /* Back up source pointer! */
+ result = targetExhausted;
+ break;
+ }
+
+ if (ch <= UNI_MAX_BMP)
+ {
+ /* Target is a character <= 0xFFFF */
+ /* UTF-16 surrogate values are illegal in UTF-32 */
+ if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END)
+ {
+ if (flags == strictConversion)
+ {
+ source -= (extraBytesToRead + 1); /* return to the illegal value itself */
+ result = sourceIllegal;
+ break;
+ }
+ else
+ {
+ if (!computeLength)
+ *target++ = UNI_REPLACEMENT_CHAR;
+ else
+ target++;
+ }
+ }
+ else
+ {
+ if (!computeLength)
+ *target++ = (uint16_t)ch; /* normal case */
+ else
+ target++;
+ }
+ }
+ else if (ch > UNI_MAX_UTF16)
+ {
+ if (flags == strictConversion)
+ {
+ result = sourceIllegal;
+ source -= (extraBytesToRead + 1); /* return to the start */
+ break; /* Bail out; shouldn't continue */
+ }
+ else
+ {
+ if (!computeLength)
+ *target++ = UNI_REPLACEMENT_CHAR;
+ else
+ target++;
+ }
+ }
+ else
+ {
+ /* target is a character in range 0xFFFF - 0x10FFFF. */
+ if ((target + 1 >= targetEnd) && (!computeLength))
+ {
+ source -= (extraBytesToRead + 1); /* Back up source pointer! */
+ result = targetExhausted;
+ break;
+ }
+
+ ch -= halfBase;
+
+ if (!computeLength)
+ {
+ *target++ = (uint16_t)((ch >> halfShift) + UNI_SUR_HIGH_START);
+ *target++ = (uint16_t)((ch & halfMask) + UNI_SUR_LOW_START);
+ }
+ else
+ {
+ target++;
+ target++;
+ }
+ }
+ }
+
+ *sourceStart = source;
+ *targetStart = target;
+ return result;
+}
+
+/**
+ * WinPR built-in Unicode API
+ */
+
+static int winpr_ConvertUTF8toUTF16(const uint8_t* src, int cchSrc, uint16_t* dst, int cchDst)
+{
+ size_t length = 0;
+ uint16_t* dstBeg = NULL;
+ uint16_t* dstEnd = NULL;
+ const uint8_t* srcBeg = NULL;
+ const uint8_t* srcEnd = NULL;
+ ConversionResult result = sourceIllegal;
+
+ if (cchSrc == -1)
+ cchSrc = strlen((char*)src) + 1;
+
+ srcBeg = src;
+ srcEnd = &src[cchSrc];
+
+ if (cchDst == 0)
+ {
+ result =
+ winpr_ConvertUTF8toUTF16_Internal(&srcBeg, srcEnd, &dstBeg, dstEnd, strictConversion);
+
+ length = dstBeg - (uint16_t*)NULL;
+ }
+ else
+ {
+ dstBeg = dst;
+ dstEnd = &dst[cchDst];
+
+ result =
+ winpr_ConvertUTF8toUTF16_Internal(&srcBeg, srcEnd, &dstBeg, dstEnd, strictConversion);
+
+ length = dstBeg - dst;
+ }
+
+ if (result == targetExhausted)
+ {
+ SetLastError(ERROR_INSUFFICIENT_BUFFER);
+ return 0;
+ }
+
+ return (result == conversionOK) ? length : 0;
+}
+
+static int winpr_ConvertUTF16toUTF8(const uint16_t* src, int cchSrc, uint8_t* dst, int cchDst)
+{
+ size_t length = 0;
+ uint8_t* dstBeg = NULL;
+ uint8_t* dstEnd = NULL;
+ const uint16_t* srcBeg = NULL;
+ const uint16_t* srcEnd = NULL;
+ ConversionResult result = sourceIllegal;
+
+ if (cchSrc == -1)
+ cchSrc = _wcslen((uint16_t*)src) + 1;
+
+ srcBeg = src;
+ srcEnd = &src[cchSrc];
+
+ if (cchDst == 0)
+ {
+ result =
+ winpr_ConvertUTF16toUTF8_Internal(&srcBeg, srcEnd, &dstBeg, dstEnd, strictConversion);
+
+ length = dstBeg - ((uint8_t*)NULL);
+ }
+ else
+ {
+ dstBeg = dst;
+ dstEnd = &dst[cchDst];
+
+ result =
+ winpr_ConvertUTF16toUTF8_Internal(&srcBeg, srcEnd, &dstBeg, dstEnd, strictConversion);
+
+ length = dstBeg - dst;
+ }
+
+ if (result == targetExhausted)
+ {
+ SetLastError(ERROR_INSUFFICIENT_BUFFER);
+ return 0;
+ }
+
+ return (result == conversionOK) ? length : 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+int int_MultiByteToWideChar(UINT CodePage, DWORD dwFlags, LPCSTR lpMultiByteStr, int cbMultiByte,
+ LPWSTR lpWideCharStr, int cchWideChar)
+{
+ size_t cbCharLen = (size_t)cbMultiByte;
+
+ WINPR_UNUSED(dwFlags);
+
+ /* If cbMultiByte is 0, the function fails */
+ if ((cbMultiByte == 0) || (cbMultiByte < -1))
+ return 0;
+
+ if (cchWideChar < 0)
+ return -1;
+
+ if (cbMultiByte < 0)
+ {
+ const size_t len = strlen(lpMultiByteStr);
+ if (len >= INT32_MAX)
+ return 0;
+ cbCharLen = (int)len + 1;
+ }
+ else
+ cbCharLen = cbMultiByte;
+
+ WINPR_ASSERT(lpMultiByteStr);
+ switch (CodePage)
+ {
+ case CP_ACP:
+ case CP_UTF8:
+ break;
+
+ default:
+ WLog_ERR(TAG, "Unsupported encoding %u", CodePage);
+ return 0;
+ }
+
+ return winpr_ConvertUTF8toUTF16((const uint8_t*)lpMultiByteStr, cbCharLen,
+ (uint16_t*)lpWideCharStr, cchWideChar);
+}
+
+int int_WideCharToMultiByte(UINT CodePage, DWORD dwFlags, LPCWSTR lpWideCharStr, int cchWideChar,
+ LPSTR lpMultiByteStr, int cbMultiByte, LPCSTR lpDefaultChar,
+ LPBOOL lpUsedDefaultChar)
+{
+ size_t cbCharLen = (size_t)cchWideChar;
+
+ WINPR_UNUSED(dwFlags);
+ /* If cchWideChar is 0, the function fails */
+ if ((cchWideChar == 0) || (cchWideChar < -1))
+ return 0;
+
+ if (cbMultiByte < 0)
+ return -1;
+
+ WINPR_ASSERT(lpWideCharStr);
+ /* If cchWideChar is -1, the string is null-terminated */
+ if (cchWideChar == -1)
+ {
+ const size_t len = _wcslen(lpWideCharStr);
+ if (len >= INT32_MAX)
+ return 0;
+ cbCharLen = (int)len + 1;
+ }
+ else
+ cbCharLen = cchWideChar;
+
+ /*
+ * if cbMultiByte is 0, the function returns the required buffer size
+ * in bytes for lpMultiByteStr and makes no use of the output parameter itself.
+ */
+
+ return winpr_ConvertUTF16toUTF8((const uint16_t*)lpWideCharStr, cbCharLen,
+ (uint8_t*)lpMultiByteStr, cbMultiByte);
+}
diff --git a/winpr/libwinpr/crt/unicode_icu.c b/winpr/libwinpr/crt/unicode_icu.c
new file mode 100644
index 0000000..1ebc558
--- /dev/null
+++ b/winpr/libwinpr/crt/unicode_icu.c
@@ -0,0 +1,237 @@
+/**
+ * WinPR: Windows Portable Runtime
+ * Unicode Conversion (CRT)
+ *
+ * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ * Copyright 2022 Armin Novak <anovak@thincast.com>
+ * Copyright 2022 Thincast Technologies GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <winpr/config.h>
+#include <winpr/assert.h>
+
+#include <errno.h>
+#include <wctype.h>
+
+#include <winpr/crt.h>
+#include <winpr/error.h>
+#include <winpr/print.h>
+
+#ifndef MIN
+#define MIN(a, b) (a) < (b) ? (a) : (b)
+#endif
+
+#include <unicode/ucnv.h>
+#include <unicode/ustring.h>
+
+#include "unicode.h"
+
+#include "../log.h"
+#define TAG WINPR_TAG("unicode")
+
+#define UCNV_CONVERT 1
+
+int int_MultiByteToWideChar(UINT CodePage, DWORD dwFlags, LPCSTR lpMultiByteStr, int cbMultiByte,
+ LPWSTR lpWideCharStr, int cchWideChar)
+{
+ const BOOL isNullTerminated = cbMultiByte < 0;
+
+ WINPR_UNUSED(dwFlags);
+
+ /* If cbMultiByte is 0, the function fails */
+
+ if ((cbMultiByte == 0) || (cbMultiByte < -1))
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return 0;
+ }
+
+ size_t len = 0;
+ if (isNullTerminated)
+ len = strlen(lpMultiByteStr) + 1;
+ else
+ len = cbMultiByte;
+
+ if (len >= INT_MAX)
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return 0;
+ }
+ cbMultiByte = (int)len;
+
+ /*
+ * if cchWideChar is 0, the function returns the required buffer size
+ * in characters for lpWideCharStr and makes no use of the output parameter itself.
+ */
+ {
+ UErrorCode error = U_ZERO_ERROR;
+ int32_t targetLength = -1;
+
+ switch (CodePage)
+ {
+ case CP_ACP:
+ case CP_UTF8:
+ break;
+
+ default:
+ WLog_ERR(TAG, "Unsupported encoding %u", CodePage);
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return 0;
+ }
+
+ const int32_t targetCapacity = cchWideChar;
+#if defined(UCNV_CONVERT)
+ char* targetStart = (char*)lpWideCharStr;
+ targetLength =
+ ucnv_convert("UTF-16LE", "UTF-8", targetStart, targetCapacity * (int32_t)sizeof(WCHAR),
+ lpMultiByteStr, cbMultiByte, &error);
+ if (targetLength > 0)
+ targetLength /= sizeof(WCHAR);
+#else
+ WCHAR* targetStart = lpWideCharStr;
+ u_strFromUTF8(targetStart, targetCapacity, &targetLength, lpMultiByteStr, cbMultiByte,
+ &error);
+#endif
+
+ switch (error)
+ {
+ case U_BUFFER_OVERFLOW_ERROR:
+ if (targetCapacity > 0)
+ {
+ cchWideChar = 0;
+ WLog_ERR(TAG, "insufficient buffer supplied, got %d, required %d",
+ targetCapacity, targetLength);
+ SetLastError(ERROR_INSUFFICIENT_BUFFER);
+ }
+ else
+ cchWideChar = targetLength;
+ break;
+ case U_STRING_NOT_TERMINATED_WARNING:
+ cchWideChar = targetLength;
+ break;
+ case U_ZERO_ERROR:
+ cchWideChar = targetLength;
+ break;
+ default:
+ WLog_WARN(TAG, "unexpected ICU error code %s [0x%08" PRIx32 "]", u_errorName(error),
+ error);
+ if (U_FAILURE(error))
+ {
+ WLog_ERR(TAG, "unexpected ICU error code %s [0x%08" PRIx32 "] is fatal",
+ u_errorName(error), error);
+ cchWideChar = 0;
+ SetLastError(ERROR_NO_UNICODE_TRANSLATION);
+ }
+ else
+ cchWideChar = targetLength;
+ break;
+ }
+ }
+
+ return cchWideChar;
+}
+
+int int_WideCharToMultiByte(UINT CodePage, DWORD dwFlags, LPCWSTR lpWideCharStr, int cchWideChar,
+ LPSTR lpMultiByteStr, int cbMultiByte, LPCSTR lpDefaultChar,
+ LPBOOL lpUsedDefaultChar)
+{
+ /* If cchWideChar is 0, the function fails */
+
+ if ((cchWideChar == 0) || (cchWideChar < -1))
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return 0;
+ }
+
+ /* If cchWideChar is -1, the string is null-terminated */
+
+ size_t len = 0;
+ if (cchWideChar == -1)
+ len = _wcslen(lpWideCharStr) + 1;
+ else
+ len = cchWideChar;
+
+ if (len >= INT32_MAX)
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return 0;
+ }
+ cchWideChar = (int)len;
+
+ /*
+ * if cbMultiByte is 0, the function returns the required buffer size
+ * in bytes for lpMultiByteStr and makes no use of the output parameter itself.
+ */
+ {
+ UErrorCode error = U_ZERO_ERROR;
+ int32_t targetLength = -1;
+
+ switch (CodePage)
+ {
+ case CP_ACP:
+ case CP_UTF8:
+ break;
+
+ default:
+ WLog_ERR(TAG, "Unsupported encoding %u", CodePage);
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return 0;
+ }
+
+ char* targetStart = lpMultiByteStr;
+ const int32_t targetCapacity = cbMultiByte;
+#if defined(UCNV_CONVERT)
+ const char* str = (const char*)lpWideCharStr;
+ targetLength = ucnv_convert("UTF-8", "UTF-16LE", targetStart, targetCapacity, str,
+ cchWideChar * (int32_t)sizeof(WCHAR), &error);
+#else
+ u_strToUTF8(targetStart, targetCapacity, &targetLength, lpWideCharStr, cchWideChar, &error);
+#endif
+ switch (error)
+ {
+ case U_BUFFER_OVERFLOW_ERROR:
+ if (targetCapacity > 0)
+ {
+ WLog_ERR(TAG, "insufficient buffer supplied, got %d, required %d",
+ targetCapacity, targetLength);
+ cbMultiByte = 0;
+ SetLastError(ERROR_INSUFFICIENT_BUFFER);
+ }
+ else
+ cbMultiByte = targetLength;
+ break;
+ case U_STRING_NOT_TERMINATED_WARNING:
+ cbMultiByte = targetLength;
+ break;
+ case U_ZERO_ERROR:
+ cbMultiByte = targetLength;
+ break;
+ default:
+ WLog_WARN(TAG, "unexpected ICU error code %s [0x%08" PRIx32 "]", u_errorName(error),
+ error);
+ if (U_FAILURE(error))
+ {
+ WLog_ERR(TAG, "unexpected ICU error code %s [0x%08" PRIx32 "] is fatal",
+ u_errorName(error), error);
+ cbMultiByte = 0;
+ SetLastError(ERROR_NO_UNICODE_TRANSLATION);
+ }
+ else
+ cbMultiByte = targetLength;
+ break;
+ }
+ }
+ return cbMultiByte;
+}