diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 01:24:41 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 01:24:41 +0000 |
commit | a9bcc81f821d7c66f623779fa5147e728eb3c388 (patch) | |
tree | 98676963bcdd537ae5908a067a8eb110b93486a6 /winpr/libwinpr/crt | |
parent | Initial commit. (diff) | |
download | freerdp3-a9bcc81f821d7c66f623779fa5147e728eb3c388.tar.xz freerdp3-a9bcc81f821d7c66f623779fa5147e728eb3c388.zip |
Adding upstream version 3.3.0+dfsg1.upstream/3.3.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'winpr/libwinpr/crt')
-rw-r--r-- | winpr/libwinpr/crt/CMakeLists.txt | 46 | ||||
-rw-r--r-- | winpr/libwinpr/crt/ModuleOptions.cmake | 9 | ||||
-rw-r--r-- | winpr/libwinpr/crt/alignment.c | 268 | ||||
-rw-r--r-- | winpr/libwinpr/crt/buffer.c | 50 | ||||
-rw-r--r-- | winpr/libwinpr/crt/casing.c | 726 | ||||
-rw-r--r-- | winpr/libwinpr/crt/conversion.c | 46 | ||||
-rw-r--r-- | winpr/libwinpr/crt/memory.c | 42 | ||||
-rw-r--r-- | winpr/libwinpr/crt/string.c | 832 | ||||
-rw-r--r-- | winpr/libwinpr/crt/test/CMakeLists.txt | 30 | ||||
-rw-r--r-- | winpr/libwinpr/crt/test/TestAlignment.c | 87 | ||||
-rw-r--r-- | winpr/libwinpr/crt/test/TestFormatSpecifiers.c | 164 | ||||
-rw-r--r-- | winpr/libwinpr/crt/test/TestString.c | 188 | ||||
-rw-r--r-- | winpr/libwinpr/crt/test/TestTypes.c | 115 | ||||
-rw-r--r-- | winpr/libwinpr/crt/test/TestUnicodeConversion.c | 1289 | ||||
-rw-r--r-- | winpr/libwinpr/crt/unicode.c | 657 | ||||
-rw-r--r-- | winpr/libwinpr/crt/unicode.h | 32 | ||||
-rw-r--r-- | winpr/libwinpr/crt/unicode_android.c | 183 | ||||
-rw-r--r-- | winpr/libwinpr/crt/unicode_apple.m | 146 | ||||
-rw-r--r-- | winpr/libwinpr/crt/unicode_builtin.c | 695 | ||||
-rw-r--r-- | winpr/libwinpr/crt/unicode_icu.c | 237 |
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; +} |